pax_global_header00006660000000000000000000000064145510553030014513gustar00rootroot0000000000000052 comment=9526bf7ae62243e8b8a7ff68a77d9427d74a25ca relic-7.6.1/000077500000000000000000000000001455105530300126245ustar00rootroot00000000000000relic-7.6.1/.dockerignore000066400000000000000000000003701455105530300153000ustar00rootroot00000000000000build keys trust work vendor _* *.application *.appx *.appxbundle *.asc *.cab *.cat *.deb *.dll *.dmg *.EC *.img *.ipa *.jar *.manifest *.MF *.mof *.msi *.p7m *.p7s *.pgp *.pkg *.ps1 *.ps1xml *.vsix *.rpm *.RSA *.SF *.sig *.SIG *.xap *.zip *.log relic-7.6.1/.gitattributes000066400000000000000000000000631455105530300155160ustar00rootroot00000000000000*.ps1 text *.yaml text *.yml text relic-7.6.1/.github/000077500000000000000000000000001455105530300141645ustar00rootroot00000000000000relic-7.6.1/.github/workflows/000077500000000000000000000000001455105530300162215ustar00rootroot00000000000000relic-7.6.1/.github/workflows/build.yml000066400000000000000000000073351455105530300200530ustar00rootroot00000000000000name: Build on: [push, pull_request] jobs: linux: runs-on: ubuntu-20.04 steps: - name: Checkout uses: actions/checkout@v3 - name: Setup Go uses: actions/setup-go@v3 with: go-version: "1.20" - name: Restore cache uses: actions/cache@v3 with: path: | ~/.cache/go-build ~/go/pkg/mod key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} restore-keys: | ${{ runner.os }}-go- - name: Build clients run: | source scripts/ci-set-ldflags.sh export CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags "$ldflags" -tags clientonly -o build/relic-client-linux-amd64 GOOS=linux GOARCH=arm64 go build -ldflags "$ldflags" -tags clientonly -o build/relic-client-linux-arm64 GOOS=linux GOARCH=ppc64le go build -ldflags "$ldflags" -tags clientonly -o build/relic-client-linux-ppc64le GOOS=freebsd GOARCH=amd64 go build -ldflags "$ldflags" -tags clientonly -o build/relic-client-freebsd-amd64 - name: Build server run: CGO_ENABLED=1 go build -ldflags "$ldflags" -o build/relic-linux-amd64 - name: Unit tests run: go test ./... - name: Functional tests run: | sudo apt-get update sudo apt-get install curl softhsm2 zip sed -i -e 's#provider:.*#provider: /usr/lib/softhsm/libsofthsm2.so#' functest/testconf.yml ./functest/functest.sh - name: Save artifacts uses: actions/upload-artifact@v3 with: name: linux path: build/ darwin: runs-on: macos-11 steps: - name: Checkout uses: actions/checkout@v3 - name: Setup Go uses: actions/setup-go@v3 with: go-version: "1.20" - name: Restore cache uses: actions/cache@v3 with: path: | ~/Library/Caches/go-build ~/go/pkg/mod key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} restore-keys: | ${{ runner.os }}-go- - name: Build client run: | source scripts/ci-set-ldflags.sh export CGO_ENABLED=0 GOARCH=amd64 go build -ldflags "$ldflags" -tags clientonly -o build/relic-client-darwin-amd64 GOARCH=arm64 go build -ldflags "$ldflags" -tags clientonly -o build/relic-client-darwin-arm64 - name: Build server run: | export CGO_ENABLED=1 GOARCH=amd64 go build -ldflags "$ldflags" -o build/relic-darwin-amd64 GOARCH=arm64 go build -ldflags "$ldflags" -o build/relic-darwin-arm64 - name: Save artifacts uses: actions/upload-artifact@v3 with: name: darwin path: build/ windows: runs-on: windows-2019 defaults: run: shell: bash steps: - name: Checkout uses: actions/checkout@v3 - name: Setup Go uses: actions/setup-go@v3 with: go-version: "1.20" - name: Restore cache uses: actions/cache@v3 with: path: | %LocalAppData%\go-build ~/go/pkg/mod key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} restore-keys: | ${{ runner.os }}-go- - name: Build client run: | source scripts/ci-set-ldflags.sh CGO_ENABLED=0 go build -ldflags "$ldflags" -tags clientonly -o build/relic-client-windows-amd64.exe - name: Build server run: CGO_ENABLED=1 go build -ldflags "$ldflags" -o build/relic-windows-amd64.exe - name: Save artifacts uses: actions/upload-artifact@v3 with: name: windows path: build/ relic-7.6.1/.gitignore000066400000000000000000000015551455105530300146220ustar00rootroot00000000000000build/ keys/ trust/ work/ vendorizer _* *.application *.appx *.appxbundle *.asc *.cab *.cat *.deb *.dll *.dmg *.EC *.img *.ipa *.jar *.manifest *.MF *.mof *.msi *.p7m *.p7s *.pgp *.pkg *.ps1 *.ps1xml *.vsix *.rpm *.RSA *.SF *.sig *.SIG *.xap *.zip *.orig *.rej *.swp *.log .dccache .vscode # Created by https://www.gitignore.io/api/go,vim ### Go ### # Compiled Object files, Static and Dynamic libs (Shared Objects) *.o *.a *.so # Folders _obj _test # Architecture specific extensions/prefixes *.[568vq] [568vq].out *.cgo1.go *.cgo2.c _cgo_defun.c _cgo_gotypes.go _cgo_export.* _testmain.go *.exe *.test *.prof # Output of the go coverage tool, specifically when used with LiteIDE *.out # external packages folder vendor/ ### Vim ### # swap [._]*.s[a-w][a-z] [._]s[a-w][a-z] # session Session.vim # temporary .netrwhist *~ # auto-generated tag files tags .vscode relic-7.6.1/LICENSE000066400000000000000000000261361455105530300136410ustar00rootroot00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. relic-7.6.1/README.md000066400000000000000000000074201455105530300141060ustar00rootroot00000000000000relic is a multi-tool and server for package signing and working with hardware security modules (HSMs). # Package types * RPM - RedHat packages * DEB - Debian packages * JAR - Java archives * EXE (PE/COFF) - Windows executable * MSI - Windows installer * appx, appxbundle - Windows universal application * CAB - Windows cabinet file * CAT - Windows security catalog * XAP - Silverlight and legacy Windows Phone applications * PS1, PS1XML, MOF, etc. - Microsoft Powershell scripts and modules * manifest, application - Microsoft ClickOnce manifest * VSIX - Visual Studio extension * Mach-O - macOS/iOS signed executables * DMG, PKG - macOS disk images / installer packages * APK - Android package * PGP - inline, detached or cleartext signature of data # Token types relic can work with several types of token: * pkcs11 - Industry standard PKCS#11 HSM interface using shared object files * Cloud services - AWS, Azure and Google Cloud managed keys * scdaemon - The GnuPG scdaemon service can enable access to OpenPGP cards (such as Yubikey NEO) * file - Private keys stored in a password-protected file # Features Relic is primarily meant to operate as a signing server, allowing clients to authenticate with a TLS certificate and sign packages remotely. It can also be used as a standalone signing tool. Other features include: * Generating and importing keys in the token * Importing certificate chains from a PKCS#12 file * Creating X509 certificate signing requests (CSR) and self-signed certificates * Limited X509 CA support -- signing CSRs and cross-signing certificates * Creating simple PGP public keys * RSA and ECDSA supported for all signature types * Verify signatures, certificate chains and timestamps on all supported package types * Sending audit logs to an AMQP broker, with an optional sealing signature * Save token PINs in the system keyring # Platforms Linux, Windows and MacOS are supported. Other platforms probably work as well. relic is tested using libsofthsm2 and Gemalto SafeNet Network HSM (Luna SA). Every vendor PKCS#11 implementation has quirks, so if relic doesn't work with your hardware please submit a pull request. # Installation Pre-built client binaries are available from the Github releases page. Alternately, relic can be built from source: ```go install github.com/sassoftware/relic/v7@latest``` The following build tags are also available: * clientonly - build a lightweight binary without standalone signing features See [doc/relic.yml](./doc/relic.yml) for an example configuration. # Additional documentation * [Signing Android packages](./doc/android.md) * [Signing MacOS binaries](./doc/macos.md) * [Using Azure Key Vault](./doc/azure.md) * [Using a PGP card, YubiKey etc.](./doc/pgpcard.md) # Related projects * SoftHSMv2 - file-based PKCS#11 implementation for testing https://github.com/opendnssec/SoftHSMv2 * uts-server - timestamping server for testing https://github.com/kakwa/uts-server * osslsigncode - Signs EXEs, MSIs, and CABs using openssl https://sourceforge.net/projects/osslsigncode/ * fb-util-for-appx - Builds signed APPX archives https://github.com/facebook/fb-util-for-appx * OpenVsixSignTool - Sign VSIX extensions using an Azure key vault https://github.com/vcsjones/OpenVsixSignTool # Reference specifications * PE/COFF specification - https://www.microsoft.com/en-us/download/details.aspx?id=19509 * Authenticode PE specification - http://download.microsoft.com/download/9/c/5/9c5b2167-8017-4bae-9fde-d599bac8184a/Authenticode_PE.docx * Microsoft ClickOnce manifest structure - https://msdn.microsoft.com/en-us/library/dd947276(v=office.12).aspx * Microsoft Compound File format (for MSI) - https://msdn.microsoft.com/en-us/library/dd942138.aspx * Alternate reference for compound document format from OpenOffice - https://www.openoffice.org/sc/compdocfileformat.pdf relic-7.6.1/cmdline/000077500000000000000000000000001455105530300142375ustar00rootroot00000000000000relic-7.6.1/cmdline/auditor/000077500000000000000000000000001455105530300157065ustar00rootroot00000000000000relic-7.6.1/cmdline/auditor/auditor.go000066400000000000000000000165431455105530300177150ustar00rootroot00000000000000// // Copyright (c) SAS Institute Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package auditor import ( "database/sql" "encoding/base64" "fmt" "os" "time" "github.com/spf13/cobra" "github.com/streadway/amqp" "github.com/sassoftware/relic/v7/cmdline/shared" "github.com/sassoftware/relic/v7/config" "github.com/sassoftware/relic/v7/internal/activation" "github.com/sassoftware/relic/v7/lib/audit" _ "github.com/lib/pq" ) var AuditCmd = &cobra.Command{ Use: "audit", Short: "Receive audit data from relic servers", RunE: auditCmd, } var argConfigFile string func init() { shared.RootCmd.AddCommand(AuditCmd) AuditCmd.Flags().StringVarP(&argConfigFile, "config", "c", "", "Name of relic-audit configuration file") } func auditCmd(cmd *cobra.Command, args []string) error { if err := readConfig(); err != nil { return err } configs, err := getServerConfs() if err != nil { return err } // test logfile but open it as-needed to make rotation simpler if auditConfig.LogFile != "" { f, err := os.OpenFile(auditConfig.LogFile, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0666) if err != nil { return err } f.Close() } // open and test database connection. the sql module manages a pool for goroutines as needed. db, err := openDb() if err != nil { return err } if err := db.Ping(); err != nil { return err } // start listeners for each broker for _, cfg := range configs { if err := startListener(cfg, db); err != nil { return fmt.Errorf("%s: %w", cfg.Path(), err) } } _ = activation.DaemonReady() // nothing left to do in this goroutine time.Sleep(1<<63 - 1) return nil } func openDb() (*sql.DB, error) { return sql.Open("postgres", auditConfig.DatabaseURI) } func startListener(conf *config.Config, db *sql.DB) error { aconf := conf.Amqp l, err := NewListener(aconf) if err != nil { return err } fmt.Fprintf(os.Stderr, "%s: connected\n", conf.Path()) go func() { l2 := l var start time.Time delay := new(expBackoff) for { if l2 != nil { if err := l2.Loop(db); err != nil { fmt.Fprintf(os.Stderr, "%s: %s\n", conf.Path(), err) } l2.Close() l2 = nil } delay.CancelReset() if time.Since(start) < time.Second { delay.Sleep() } var err error start = time.Now() l2, err = NewListener(aconf) if err != nil { fmt.Fprintf(os.Stderr, "%s: %s\n", conf.Path(), err) } else { fmt.Fprintf(os.Stderr, "%s: connection reestablished\n", conf.Path()) delay.ResetAfter(60 * time.Second) } } }() return nil } type Listener struct { aconf *config.AmqpConfig conn *amqp.Connection ch *amqp.Channel qname string } func NewListener(aconf *config.AmqpConfig) (*Listener, error) { conn, err := audit.Connect(aconf) if err != nil { return nil, err } ch, err := conn.Channel() if err != nil { conn.Close() return nil, err } hostname, _ := os.Hostname() if hostname == "" { hostname = "unknown" } qname := "audit." + hostname l := &Listener{aconf, conn, ch, qname} if err := ch.ExchangeDeclarePassive(aconf.ExchangeName(), amqp.ExchangeFanout, true, false, false, false, nil); err != nil { l.Close() return nil, err } if _, err := ch.QueueDeclare(qname, true, false, false, false, nil); err != nil { l.Close() return nil, err } if err := ch.QueueBind(qname, "", aconf.ExchangeName(), false, nil); err != nil { l.Close() return nil, err } return l, nil } func (l *Listener) Loop(db *sql.DB) error { errch := l.conn.NotifyClose(make(chan *amqp.Error, 1)) delivery, err := l.ch.Consume(l.qname, "", false, true, false, false, nil) if err != nil { return err } for { d, ok := <-delivery if !ok { break } info, err := audit.Parse(d.Body) if err != nil { fmt.Fprintf(os.Stderr, "parse failed: %s\n", err) if err := d.Ack(false); err != nil { return err } continue } if err := logToAll(db, info); err != nil { // reject the message, disconnect, and start a timeout fmt.Fprintf(os.Stderr, "ERROR: %s\n", err) _ = d.Reject(true) return err } if err := d.Ack(false); err != nil { return err } } if err := <-errch; err != nil { return err } return nil } func logToAll(db *sql.DB, info *audit.Info) (err error) { tx, err := db.Begin() if err != nil { return err } defer tx.Rollback() //nolint:Errcheck rowid, err := insertRow(db, info) if err != nil { return err } if err := logGraylog(info, rowid); err != nil { return err } if err := logToFile(info, rowid); err != nil { return err } return tx.Commit() } func insertRow(db *sql.DB, info *audit.Info) (int64, error) { blob, err := info.Marshal() if err != nil { return 0, err } attrs64 := base64.StdEncoding.EncodeToString(blob) var rowid int64 row := db.QueryRow("INSERT INTO signatures (timestamp, client_name, client_ip, client_dn, client_filename, sig_hostname, sig_type, sig_keyname, attributes) VALUES($1, $2, $3, $4, $5, $6, $7, $8, $9) RETURNING signature_id", info.Attributes["sig.timestamp"], info.Attributes["client.name"], info.Attributes["client.ip"], info.Attributes["client.dn"], info.Attributes["client.filename"], info.Attributes["sig.hostname"], info.Attributes["sig.type"], info.Attributes["sig.keyname"], attrs64, ) if err := row.Scan(&rowid); err != nil { return 0, err } return rowid, nil } func logToFile(info *audit.Info, rowid int64) error { formatted := fmtRow(info, rowid) if auditConfig.LogFile != "" { f, err := os.OpenFile(auditConfig.LogFile, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0666) if err != nil { return err } defer f.Close() if _, err := fmt.Fprintln(f, formatted); err != nil { return err } } else { fmt.Println(formatted) } return nil } func fmtRow(info *audit.Info, rowid int64) string { client := info.Attributes["client.name"] if client == nil { client = "" } ip := info.Attributes["client.ip"] if ip == nil { ip = "" } dn := info.Attributes["client.dn"] if dn == nil { dn = "" } return fmt.Sprintf("[%s] client=%s dn=%s ip=%s server=%s sigtype=%s filename=%s key=%s rowid=%d", info.Attributes["sig.timestamp"], client, dn, ip, info.Attributes["sig.hostname"], info.Attributes["sig.type"], info.Attributes["client.filename"], info.Attributes["sig.keyname"], rowid, ) } func (l *Listener) Close() error { if l.ch != nil { l.ch.Close() l.ch = nil } if l.conn != nil { l.conn.Close() l.conn = nil } return nil } const ( backoffMin = 1 backoffMax = 60 backoffE = 2.7182818284590451 ) type expBackoff struct { e float32 t *time.Timer } func (e *expBackoff) Sleep() { if e.e == 0 { e.e = backoffMin } time.Sleep(time.Duration(e.e * float32(time.Second))) e.e *= backoffE if e.e > backoffMax { e.e = backoffMax } } func (e *expBackoff) ResetAfter(d time.Duration) { if e.t != nil { e.t.Stop() } e.t = time.AfterFunc(d, func() { e.e = 0 }) } func (e *expBackoff) CancelReset() { if e.t != nil { e.t.Stop() e.t = nil } } relic-7.6.1/cmdline/auditor/config.go000066400000000000000000000036411455105530300175060ustar00rootroot00000000000000// // Copyright (c) SAS Institute Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package auditor import ( "errors" "fmt" "io/ioutil" "os" "path/filepath" "sort" "strings" "gopkg.in/yaml.v3" "github.com/sassoftware/relic/v7/config" ) type AuditConfig struct { ConfigDir string DatabaseURI string LogFile string GraylogURL string } var auditConfig AuditConfig func readConfig() error { if argConfigFile == "" { return errors.New("--config is required") } blob, err := ioutil.ReadFile(argConfigFile) if err != nil { return err } if err := yaml.Unmarshal(blob, &auditConfig); err != nil { return err } if auditConfig.ConfigDir == "" { return errors.New("ConfigDir must be set in configuration file") } return nil } func getServerConfs() ([]*config.Config, error) { dir, err := os.Open(auditConfig.ConfigDir) if err != nil { return nil, err } defer dir.Close() names, err := dir.Readdirnames(-1) if err != nil { return nil, err } sort.Strings(names) var confs []*config.Config for _, name := range names { if strings.HasSuffix(name, ".yml") { cpath := filepath.Join(auditConfig.ConfigDir, name) cfg, err := config.ReadFile(cpath) if err != nil { return nil, fmt.Errorf("%s: %w", cpath, err) } if cfg.Amqp == nil || cfg.Amqp.URL == "" { return nil, fmt.Errorf("%s has no amqp server", cpath) } confs = append(confs, cfg) } } return confs, nil } relic-7.6.1/cmdline/auditor/graylog.go000066400000000000000000000033721455105530300177060ustar00rootroot00000000000000// // Copyright (c) SAS Institute Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package auditor import ( "bytes" "encoding/json" "fmt" "net/http" "strings" "time" "github.com/sassoftware/relic/v7/lib/audit" ) func logGraylog(info *audit.Info, rowid int64) error { if auditConfig.GraylogURL == "" { return nil } msg := map[string]interface{}{ "version": "1.1", "host": info.Attributes["sig.hostname"], "short_message": fmtRow(info, rowid), "level": 6, // INFO } if timestamp, err := time.Parse(time.RFC3339Nano, info.Attributes["sig.timestamp"].(string)); err == nil { msg["timestamp"] = timestamp.Unix() } for k, v := range info.Attributes { if v == nil { continue } // graylog quietly changes dots to underscores, but only after running // stream filters. that gets confusing real quickly so change it to // underscore now. k = strings.ReplaceAll(k, ".", "_") msg["_"+k] = v } blob, err := json.Marshal(msg) if err != nil { return err } resp, err := http.Post(auditConfig.GraylogURL, "application/json", bytes.NewReader(blob)) if err != nil { return err } else if resp.StatusCode >= 300 { return fmt.Errorf("posting to graylog: %s", resp.Status) } resp.Body.Close() return nil } relic-7.6.1/cmdline/remotecmd/000077500000000000000000000000001455105530300162165ustar00rootroot00000000000000relic-7.6.1/cmdline/remotecmd/azure.go000066400000000000000000000104731455105530300177000ustar00rootroot00000000000000package remotecmd import ( "context" "errors" "fmt" "log" "net/http" "os" "os/exec" "path/filepath" "strings" "time" "github.com/AzureAD/microsoft-authentication-library-for-go/apps/cache" "github.com/AzureAD/microsoft-authentication-library-for-go/apps/public" "github.com/gregjones/httpcache" "github.com/gregjones/httpcache/diskcache" "github.com/peterbourgon/diskv" "github.com/sassoftware/relic/v7/lib/dlog" "golang.org/x/oauth2" ) type azureSource struct { cli public.Client useAT bool scopes []string } func azureTokenSource(authority, clientID string, scopes []string) (oauth2.TokenSource, error) { if authority == "" || clientID == "" { return nil, errors.New("authority and clientID are required") } if len(scopes) == 0 { scopes = []string{"openid"} } s := &azureSource{scopes: scopes} // The returned access token can be used if an app-specific scope was // requested. Otherwise it will be an in opaque format and we must use the // generic ID token. for _, scope := range scopes { if strings.HasPrefix(scope, "app://"+clientID) || strings.HasPrefix(scope, "https://") { s.useAT = true } } // cache both azure metadata and the resulting tokens cacheBase, err := os.UserCacheDir() if err != nil { return nil, err } cacheBase = filepath.Join(cacheBase, "relic") storage := diskv.New(diskv.Options{ BasePath: cacheBase, TempDir: filepath.Join(cacheBase, "tmp"), CacheSizeMax: 10e6, }) // build HTTP transport with a cache tr := loggingTransport{RoundTripper: http.DefaultTransport} cache := httpcache.NewTransport(diskcache.NewWithDiskv(storage)) cache.Transport = tr hc := &http.Client{Transport: cache} // configure MSAL s.cli, err = public.New(clientID, public.WithAuthority(authority), public.WithCache(&dvCache{ dv: storage, key: "msal-" + clientID, }), public.WithHTTPClient(hc), ) if err != nil { return nil, err } return oauth2.ReuseTokenSource(nil, s), nil } func (s *azureSource) Token() (*oauth2.Token, error) { // use cached token or silently refresh if acc := s.cli.Accounts(); len(acc) != 0 { result, err := s.cli.AcquireTokenSilent(context.Background(), s.scopes, public.WithSilentAccount(acc[0])) if err != nil { log.Println("warning: failed to refresh cached token:", err) } else { return s.toToken(result) } } // use browser to interactively authenticate fmt.Fprintln(os.Stderr, "attempting interactive login") result, err := s.cli.AcquireTokenInteractive(context.Background(), s.scopes) if err == nil { return s.toToken(result) } else if !errors.As(err, new(*exec.ExitError)) && !errors.As(err, new(*exec.Error)) { return nil, err } // use device code fmt.Fprintln(os.Stderr, "attempting device code login") dc, err := s.cli.AcquireTokenByDeviceCode(context.Background(), s.scopes) if err != nil { return nil, err } fmt.Fprintln(os.Stderr) fmt.Fprintln(os.Stderr, dc.Result.Message) fmt.Fprintln(os.Stderr) result, err = dc.AuthenticationResult(context.Background()) if err != nil { return nil, err } return s.toToken(result) } func (s *azureSource) toToken(res public.AuthResult) (*oauth2.Token, error) { if s.useAT { return &oauth2.Token{ AccessToken: res.AccessToken, Expiry: res.ExpiresOn, }, nil } return &oauth2.Token{ AccessToken: res.IDToken.RawToken, Expiry: time.Unix(res.IDToken.ExpirationTime, 0), }, nil } // dvCache adapts the diskv key-value store to act as MSAL's persistence layer type dvCache struct { dv *diskv.Diskv key string } func (c *dvCache) Export(cache cache.Marshaler, key string) { data, err := cache.Marshal() if err == nil { err = c.dv.Write(c.key, data) } if err != nil { log.Println("error: persisting access token:", err) } } func (c *dvCache) Replace(cache cache.Unmarshaler, key string) { data, err := c.dv.Read(c.key) if err == nil { err = cache.Unmarshal(data) } if err != nil && !errors.Is(err, os.ErrNotExist) { log.Println("error: reading cached access token:", err) } } type loggingTransport struct { RoundTripper http.RoundTripper } func (t loggingTransport) RoundTrip(req *http.Request) (*http.Response, error) { dlog.Printf(3, "auth > %s %s", req.Method, req.URL) resp, err := t.RoundTripper.RoundTrip(req) if err != nil { dlog.Printf(3, "auth < %+v", err) } else { dlog.Printf(3, "auth < %s", resp.Status) } return resp, err } relic-7.6.1/cmdline/remotecmd/client.go000066400000000000000000000213531455105530300200270ustar00rootroot00000000000000// // Copyright (c) SAS Institute Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package remotecmd import ( "crypto/tls" "encoding/json" "errors" "fmt" "io" "net" "net/http" "net/url" "os" "strings" "time" "golang.org/x/net/http2" "golang.org/x/oauth2" "github.com/sassoftware/relic/v7/cmdline/shared" "github.com/sassoftware/relic/v7/config" "github.com/sassoftware/relic/v7/internal/authmodel" "github.com/sassoftware/relic/v7/internal/httperror" "github.com/sassoftware/relic/v7/lib/compresshttp" "github.com/sassoftware/relic/v7/lib/x509tools" ) type ReaderGetter interface { GetReader() (io.Reader, error) } type client struct { config *config.RemoteConfig cli *http.Client tokenSource oauth2.TokenSource } func newClient() (*client, error) { err := shared.InitClientConfig() if err != nil { return nil, err } cfg := shared.CurrentConfig.Remote if cfg == nil { return nil, errors.New("missing remote section in config file") } else if cfg.DirectoryURL == "" { if cfg.URL == "" { return nil, errors.New("url or directoryUrl must be set in 'remote' section of configuration") } cfg.DirectoryURL = cfg.URL } tconf, err := makeTLSConfig(cfg) if err != nil { return nil, err } dialer := &net.Dialer{ Timeout: time.Duration(cfg.ConnectTimeout) * time.Second, } transport := &http.Transport{TLSClientConfig: tconf, DialContext: dialer.DialContext} if err := http2.ConfigureTransport(transport); err != nil { return nil, err } client := &client{ config: cfg, cli: &http.Client{Transport: transport}, } if cfg.AccessToken != "" { // static access token from environment client.tokenSource = oauth2.StaticTokenSource(&oauth2.Token{AccessToken: cfg.AccessToken}) } if client.tokenSource == nil && tconf.GetClientCertificate == nil && !cfg.Interactive { return nil, errors.New("remote.certfile and remote.keyfile must be set") } // in case of interactive auth, wait until we have metadata return client, nil } // Make a single API request to a named endpoint, handling directory lookup and failover automatically. func CallRemote(endpoint, method string, query *url.Values, body ReaderGetter) (*http.Response, error) { cli, err := newClient() if err != nil { return nil, err } cfg := shared.CurrentConfig.Remote bases := []string{cfg.DirectoryURL} metadata, serverEncodings, err := cli.getDirectory(cfg.DirectoryURL) if err != nil { return nil, err } else if len(metadata.Hosts) > 0 { // list of direct URLs provided bases = metadata.Hosts } if err := cli.interactiveAuth(metadata); err != nil { return nil, fmt.Errorf("configuring interactive authentication: %w", err) } return cli.doRequest(bases, endpoint, method, serverEncodings, query, body) } func (cli *client) interactiveAuth(metadata *authmodel.Metadata) error { if !cli.config.Interactive || cli.tokenSource != nil { // not needed return nil } for _, auth := range metadata.Auth { if auth.Type != authmodel.AuthTypeAzureAD { continue } var err error cli.tokenSource, err = azureTokenSource(auth.Authority, auth.ClientID, auth.Scopes) if err != nil { return err } break } return nil } // Call the configured directory URL to get a list of servers to try. // callRemote() calls this automatically, use that instead. func (cli *client) getDirectory(dirurl string) (*authmodel.Metadata, string, error) { response, err := cli.doRequest([]string{dirurl}, "directory", "GET", "", nil, nil) if err != nil { return nil, "", err } encodings := response.Header.Get("Accept-Encoding") bodybytes, err := io.ReadAll(response.Body) if err != nil { return nil, "", err } response.Body.Close() m := new(authmodel.Metadata) if strings.Contains(response.Header.Get("Content-Type"), "json") { if err := json.Unmarshal(bodybytes, m); err != nil { return nil, "", err } } else { // legacy path text := strings.Trim(string(bodybytes), "\r\n") if len(text) == 0 { return nil, encodings, nil } m.Hosts = strings.Split(text, "\r\n") } return m, encodings, nil } // Build a HTTP request from various bits and pieces func (cli *client) buildRequest(base, endpoint, method, encoding string, query *url.Values, bodyFile ReaderGetter) (*http.Request, error) { request, err := http.NewRequest(method, base, nil) if err != nil { return nil, err } request.URL, err = request.URL.Parse(endpoint) if err != nil { return nil, err } if query != nil { request.URL.RawQuery = query.Encode() } request.Header.Set("User-Agent", config.UserAgent) if encoding != "" { request.Header.Set("Accept-Encoding", encoding) } if cli.tokenSource != nil { tok, err := cli.tokenSource.Token() if err != nil { return nil, err } tok.SetAuthHeader(request) } if bodyFile != nil { stream, err := bodyFile.GetReader() if err != nil { return nil, err } request.Body = io.NopCloser(stream) if err := compresshttp.CompressRequest(request, encoding); err != nil { return nil, err } } if endpoint == "directory" { request.Header.Set("Accept", "application/json, */*") } return request, nil } // Build TLS config based on client configuration func makeTLSConfig(cfg *config.RemoteConfig) (*tls.Config, error) { tconf := new(tls.Config) if err := x509tools.LoadCertPool(cfg.CaCert, tconf); err != nil { return nil, err } x509tools.SetKeyLogFile(tconf) if cfg.CertFile == "" && cfg.KeyFile == "" { return tconf, nil } var err error var certBytes, keyBytes []byte if strings.Contains(cfg.CertFile, "-----BEGIN") { certBytes = []byte(cfg.CertFile) } else { certBytes, err = os.ReadFile(cfg.CertFile) if err != nil { return nil, fmt.Errorf("remote.certfile: %w", err) } } if strings.Contains(cfg.KeyFile, "-----BEGIN") { keyBytes = []byte(cfg.KeyFile) } else { keyBytes, err = os.ReadFile(cfg.KeyFile) if err != nil { return nil, fmt.Errorf("remote.keyfile: %w", err) } } tlscert, err := tls.X509KeyPair(certBytes, keyBytes) if err != nil { return nil, err } // When the server is running behind nginx, nginx must be configured to send // at least one CA-cert to the client to pick from. However, it's not // feasible to list every cert the server would accept as most of them are // self-signed, so only a dummy cert is sent. Go is clever though, and if we // set tconf.Certificates here it will try to match it again what the server // claims to want, and if none match then no cert is sent at all. Work // around this by using GetClientCertificate so that the matching behavior // is bypassed and it always sends the configured client cert. tconf.GetClientCertificate = func(*tls.CertificateRequestInfo) (*tls.Certificate, error) { return &tlscert, nil } return tconf, nil } // Transact one request, trying multiple servers if necessary. Internal use only. func (cli *client) doRequest(bases []string, endpoint, method, encodings string, query *url.Values, bodyFile ReaderGetter) (response *http.Response, err error) { minAttempts := shared.CurrentConfig.Remote.Retries if len(bases) < minAttempts { var repeated []string for len(repeated) < minAttempts { repeated = append(repeated, bases...) } bases = repeated } loop: for i, base := range bases { var request *http.Request request, err = cli.buildRequest(base, endpoint, method, encodings, query, bodyFile) if err != nil { return nil, err } response, err = cli.cli.Do(request) if request.Body != nil { request.Body.Close() } if err == nil { if response.StatusCode < 300 { if i != 0 { fmt.Printf("successfully contacted %s\n", request.URL) } break loop } // HTTP error, probably a 503 err = httperror.FromResponse(response) } if response != nil && response.StatusCode == http.StatusNotAcceptable && encodings != "" { // try again without compression encodings = "" goto loop } else if httperror.Temporary(err) && i+1 < len(bases) { fmt.Printf("%s\nunable to connect to %s; trying next server\n", err, request.URL) } else { return nil, err } } if response != nil { if err := compresshttp.DecompressResponse(response); err != nil { return nil, err } } return } func setDigestQueryParam(query url.Values) error { if shared.ArgDigest == "" { return nil } if _, err := shared.GetDigest(); err != nil { return err } query.Add("digest", shared.ArgDigest) return nil } relic-7.6.1/cmdline/remotecmd/command.go000066400000000000000000000016401455105530300201640ustar00rootroot00000000000000// // Copyright (c) SAS Institute Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package remotecmd import ( "github.com/spf13/cobra" "github.com/sassoftware/relic/v7/cmdline/shared" ) var RemoteCmd = &cobra.Command{ Use: "remote", Short: "Commands accessing a remote server", } var ( argKeyName string argFile string argOutput string ) func init() { shared.RootCmd.AddCommand(RemoteCmd) } relic-7.6.1/cmdline/remotecmd/getkeycmd.go000066400000000000000000000034231455105530300205230ustar00rootroot00000000000000// // Copyright (c) SAS Institute Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package remotecmd import ( "encoding/json" "errors" "io/ioutil" "net/url" "os" "github.com/spf13/cobra" "github.com/sassoftware/relic/v7/cmdline/shared" ) var GetKeyCmd = &cobra.Command{ Use: "get-key", Short: "Get a public key certificate from the remote server", RunE: getKeyCmd, } func init() { RemoteCmd.AddCommand(GetKeyCmd) } type keyInfo struct { X509Certificate string PGPCertificate string } func getKeyInfo(keyName string) (keyInfo, error) { response, err := CallRemote("keys/"+url.PathEscape(keyName), "GET", nil, nil) if err != nil { return keyInfo{}, err } blob, err := ioutil.ReadAll(response.Body) if err != nil { return keyInfo{}, err } response.Body.Close() var info keyInfo if err := json.Unmarshal(blob, &info); err != nil { return keyInfo{}, err } return info, nil } func getKeyCmd(cmd *cobra.Command, args []string) error { if len(args) == 0 { return errors.New("specify one or more key names. See also 'list-keys'") } for _, keyName := range args { info, err := getKeyInfo(keyName) if err != nil { return shared.Fail(err) } os.Stdout.WriteString(info.X509Certificate) os.Stdout.WriteString(info.PGPCertificate) } return nil } relic-7.6.1/cmdline/remotecmd/listkeyscmd.go000066400000000000000000000025551455105530300211070ustar00rootroot00000000000000// // Copyright (c) SAS Institute Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package remotecmd import ( "encoding/json" "fmt" "io/ioutil" "github.com/sassoftware/relic/v7/cmdline/shared" "github.com/spf13/cobra" ) var ListKeysCmd = &cobra.Command{ Use: "list-keys", Short: "List keys available on the remote server", RunE: listKeysCmd, } func init() { RemoteCmd.AddCommand(ListKeysCmd) } func listKeysCmd(cmd *cobra.Command, args []string) error { var keyList []string response, err := CallRemote("list_keys", "GET", nil, nil) if err != nil { return shared.Fail(err) } resbytes, err := ioutil.ReadAll(response.Body) if err != nil { return shared.Fail(err) } response.Body.Close() err = json.Unmarshal(resbytes, &keyList) if err != nil { return shared.Fail(err) } for _, key := range keyList { fmt.Println(key) } return nil } relic-7.6.1/cmdline/remotecmd/login.go000066400000000000000000000037271455105530300176660ustar00rootroot00000000000000// // Copyright (c) SAS Institute Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package remotecmd import ( "errors" "fmt" "net/http" "os" "github.com/sassoftware/relic/v7/cmdline/shared" "github.com/sassoftware/relic/v7/config" "github.com/spf13/cobra" ) var LoginCmd = &cobra.Command{ Use: "login", Short: "Interactively login to the server with single sign-on", RunE: loginCmd, } func init() { RemoteCmd.AddCommand(LoginCmd) LoginCmd.Flags().StringVarP(&argRemoteURL, "url", "u", "", "URL of remote server to register to") LoginCmd.Flags().BoolVarP(&argForce, "force", "f", false, "Overwrite existing configuration file") } func loginCmd(cmd *cobra.Command, args []string) error { if argRemoteURL == "" { return errors.New("--url is required") } if shared.ArgConfig == "" { shared.ArgConfig = config.DefaultConfig() if shared.ArgConfig == "" { return errors.New("unable to determine default config location") } } if fileExists(shared.ArgConfig) && !argForce { fmt.Fprintf(os.Stderr, "Config file %s already exists\n", shared.ArgConfig) return nil } shared.CurrentConfig = &config.Config{Remote: &config.RemoteConfig{ DirectoryURL: argRemoteURL, Interactive: true, }} // prove auth is working if _, err := CallRemote("/list_keys", http.MethodGet, nil, nil); err != nil { return shared.Fail(err) } if err := writeConfigObject(shared.ArgConfig, shared.CurrentConfig, false); err != nil { return shared.Fail(err) } return nil } relic-7.6.1/cmdline/remotecmd/registercmd.go000066400000000000000000000152011455105530300210540ustar00rootroot00000000000000// // Copyright (c) SAS Institute Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package remotecmd import ( "crypto/ecdsa" "crypto/elliptic" "crypto/rand" "crypto/sha256" "crypto/x509" "encoding/hex" "encoding/pem" "errors" "fmt" "io" "io/fs" "io/ioutil" "math/big" "os" "path/filepath" "time" "gopkg.in/yaml.v3" "github.com/sassoftware/relic/v7/cmdline/shared" "github.com/sassoftware/relic/v7/config" "github.com/sassoftware/relic/v7/lib/certloader" "github.com/spf13/cobra" ) var RegisterCmd = &cobra.Command{ Use: "register", Short: "Generate a certificate for communicating with a server", RunE: registerCmd, } var ( argRemoteURL string argCaCert string argDirectory bool argForce bool argEmbed bool ) func init() { RemoteCmd.AddCommand(RegisterCmd) RegisterCmd.Flags().StringVarP(&argRemoteURL, "url", "u", "", "URL of remote server to register to") RegisterCmd.Flags().StringVarP(&argCaCert, "ca-cert", "C", "", "Path to CA certificate file (will be copied)") RegisterCmd.Flags().BoolVarP(&argDirectory, "directory", "D", false, "Remote URL is a cluster directory") RegisterCmd.Flags().BoolVarP(&argForce, "force", "f", false, "Overwrite existing configuration file") RegisterCmd.Flags().BoolVarP(&argEmbed, "embed", "E", false, "Embed certificate in config file") } func registerCmd(cmd *cobra.Command, args []string) error { if argRemoteURL == "" { return errors.New("--url and --ca-cert are required") } if shared.ArgConfig == "" { shared.ArgConfig = config.DefaultConfig() if shared.ArgConfig == "" { return errors.New("Unable to determine default config location") } } if fileExists(shared.ArgConfig) && !argForce { fmt.Fprintf(os.Stderr, "Config file %s already exists\n", shared.ArgConfig) return nil } var cacert []byte var err error if argCaCert != "" { cacert, err = ioutil.ReadFile(argCaCert) if err != nil { return shared.Fail(fmt.Errorf("reading cacert: %w", err)) } } var certPath, keyPath, capath string if argEmbed { // embed generated PEM into config certPEM, keyPEM, fingerprint, err := genKeyPair() if err != nil { return shared.Fail(err) } fmt.Fprintln(os.Stderr, "New key fingerprint:", fingerprint) certPath = string(certPEM) keyPath = string(keyPEM) capath = string(cacert) } else { // write generated PEM to file defaultDir := filepath.Dir(shared.ArgConfig) keyPath = filepath.Join(defaultDir, "client.pem") certPath = keyPath if fileExists(keyPath) { if !argForce { return shared.Fail(fmt.Errorf("Key file %s already exists", keyPath)) } if err := readKeyPair(keyPath); err != nil { return shared.Fail(err) } } else { if err := writeKeyPair(keyPath); err != nil { return shared.Fail(err) } } if len(cacert) != 0 { capath = filepath.Join(defaultDir, "cacert.pem") if err := ioutil.WriteFile(capath, cacert, 0644); err != nil { return shared.Fail(err) } } } if err := writeConfig(shared.ArgConfig, argRemoteURL, certPath, keyPath, capath); err != nil { return shared.Fail(err) } return nil } func fileExists(path string) bool { _, err := os.Lstat(path) return err == nil } func writeConfig(cfgPath, url, certPath, keyPath, caPath string) error { newConfig := &config.Config{Remote: &config.RemoteConfig{}} if argDirectory { newConfig.Remote.DirectoryURL = url } else { newConfig.Remote.URL = url } newConfig.Remote.CertFile = certPath newConfig.Remote.KeyFile = keyPath newConfig.Remote.CaCert = caPath return writeConfigObject(cfgPath, newConfig, true) } func writeConfigObject(cfgPath string, newConfig *config.Config, secure bool) error { cfgblob, err := yaml.Marshal(newConfig) if err != nil { return err } if cfgPath == "-" { os.Stdout.Write(cfgblob) return nil } if err = os.MkdirAll(filepath.Dir(cfgPath), 0755); err != nil { return err } var umask fs.FileMode = 0644 if secure { umask = 0600 } return os.WriteFile(cfgPath, cfgblob, umask) } func genKeyPair() (certPEM, keyPEM []byte, fingerprint string, err error) { key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) if err != nil { return } certPEM, fingerprint, err = selfSign(key) if err != nil { return } keyPEM, err = serializeKey(key) return } func writeKeyPair(keyPath string) error { certPEM, keyPEM, fingerprint, err := genKeyPair() if err != nil { return err } pemdata := append(keyPEM, certPEM...) if err = os.MkdirAll(filepath.Dir(keyPath), 0755); err != nil { return err } if err = ioutil.WriteFile(keyPath, pemdata, 0600); err != nil { return err } fmt.Fprintln(os.Stderr, "New key fingerprint:", fingerprint) return nil } func readKeyPair(keyPath string) error { cert, err := certloader.LoadX509KeyPair(keyPath, keyPath) if err != nil { return err } digest := sha256.Sum256(cert.Leaf.RawSubjectPublicKeyInfo) encoded := hex.EncodeToString(digest[:]) fmt.Println("Existing key fingerprint:", encoded) return nil } func selfSign(key *ecdsa.PrivateKey) ([]byte, string, error) { blob := make([]byte, 12) if _, err := io.ReadFull(rand.Reader, blob); err != nil { return nil, "", errors.New("failed to make serial number") } hostname, _ := os.Hostname() var template x509.Certificate template.SerialNumber = new(big.Int).SetBytes(blob) template.Subject.CommonName = fmt.Sprintf("self-signed cert for %s", hostname) template.Issuer = template.Subject template.SignatureAlgorithm = x509.ECDSAWithSHA256 template.NotBefore = time.Now().Add(time.Hour * -24) template.NotAfter = time.Now().Add(time.Hour * 24 * 36525) certblob, err := x509.CreateCertificate(rand.Reader, &template, &template, key.Public(), key) if err != nil { return nil, "", err } cert, err := x509.ParseCertificate(certblob) if err != nil { return nil, "", err } digest := sha256.Sum256(cert.RawSubjectPublicKeyInfo) encoded := hex.EncodeToString(digest[:]) block := &pem.Block{Type: "CERTIFICATE", Bytes: certblob} return pem.EncodeToMemory(block), encoded, nil } func serializeKey(key *ecdsa.PrivateKey) ([]byte, error) { blob, err := x509.MarshalECPrivateKey(key) if err != nil { return nil, err } block := &pem.Block{Type: "EC PRIVATE KEY", Bytes: blob} return pem.EncodeToMemory(block), nil } relic-7.6.1/cmdline/remotecmd/signcmd.go000066400000000000000000000100061455105530300201660ustar00rootroot00000000000000// // Copyright (c) SAS Institute Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package remotecmd import ( "errors" "fmt" "net/url" "os" "path/filepath" "github.com/spf13/cobra" "github.com/sassoftware/relic/v7/cmdline/shared" "github.com/sassoftware/relic/v7/signers" ) var SignCmd = &cobra.Command{ Use: "sign", Short: "Sign a package using a remote signing server", RunE: signCmd, } var ( argIfUnsigned bool argSigType string ) func init() { RemoteCmd.AddCommand(SignCmd) SignCmd.Flags().StringVarP(&argKeyName, "key", "k", "", "Name of key on remote server to use") SignCmd.Flags().StringVarP(&argFile, "file", "f", "", "Input file to sign") SignCmd.Flags().StringVarP(&argOutput, "output", "o", "", "Output file. Defaults to same as --file.") SignCmd.Flags().StringVarP(&argSigType, "sig-type", "T", "", "Specify signature type (default: auto-detect)") SignCmd.Flags().BoolVar(&argIfUnsigned, "if-unsigned", false, "Skip signing if the file already has a signature") shared.AddDigestFlag(SignCmd) shared.AddLateHook(func() { signers.MergeFlags(SignCmd) }) } func signCmd(cmd *cobra.Command, args []string) (err error) { if argFile == "" || argKeyName == "" { return errors.New("--file and --key are required") } if argOutput == "" { argOutput = argFile } // detect signature type mod, err := signers.ByFile(argFile, argSigType) if err != nil { return shared.Fail(err) } if mod.Sign == nil { return shared.Fail(fmt.Errorf("can't sign files of type: %s", mod.Name)) } // parse signer-specific flags flags, err := mod.FlagsFromCmdline(cmd.Flags()) if err != nil { return shared.Fail(err) } infile, err := shared.OpenForPatching(argFile, argOutput) if err != nil { return shared.Fail(err) } else if infile == os.Stdin { if !mod.AllowStdin { return shared.Fail(errors.New("this signature type does not support reading from stdin")) } } else { defer infile.Close() } if argIfUnsigned { if infile == os.Stdin { return shared.Fail(errors.New("cannot use --if-unsigned with standard input")) } if signed, err := mod.IsSigned(infile); err != nil { return shared.Fail(err) } else if signed { fmt.Fprintf(os.Stderr, "skipping already-signed file: %s\n", argFile) return nil } if _, err := infile.Seek(0, 0); err != nil { return shared.Fail(fmt.Errorf("rewinding input file: %w", err)) } } // transform input if needed hash, err := shared.GetDigest() if err != nil { return err } opts := signers.SignOpts{ Path: argFile, Hash: hash, Flags: flags, } transform, err := mod.GetTransform(infile, opts) if err != nil { return shared.Fail(err) } // build request values := url.Values{} values.Add("key", argKeyName) values.Add("filename", filepath.Base(argFile)) values.Add("sigtype", mod.Name) if err := flags.ToQuery(values); err != nil { return shared.Fail(err) } if err := setDigestQueryParam(values); err != nil { return err } // do request response, err := CallRemote("sign", "POST", &values, transform) if err != nil { return shared.Fail(err) } defer response.Body.Close() // apply the result if err := transform.Apply(argOutput, response.Header.Get("Content-Type"), response.Body); err != nil { return shared.Fail(err) } // if needed, do a final fixup step if mod.Fixup != nil { f, err := os.OpenFile(argOutput, os.O_RDWR, 0) if err != nil { return shared.Fail(err) } defer f.Close() if err := mod.Fixup(f); err != nil { return shared.Fail(err) } } fmt.Fprintf(os.Stderr, "Signed %s\n", argFile) return nil } relic-7.6.1/cmdline/remotecmd/signpgpcmd.go000066400000000000000000000023061455105530300207010ustar00rootroot00000000000000// // Copyright (c) SAS Institute Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package remotecmd import ( "github.com/spf13/cobra" "github.com/sassoftware/relic/v7/signers/pgp" ) var SignPgpCmd = &cobra.Command{ Use: "sign-pgp", Short: "Create PGP signatures", Long: "This command is vaguely compatible with the gpg command-line and accepts (and mostly, ignores) many of gpg's options. It can thus be used as a drop-in replacement for tools that use gpg to make signatures.", RunE: signPgpCmd, } func init() { pgp.AddCompatFlags(SignPgpCmd) RemoteCmd.AddCommand(SignPgpCmd) } func signPgpCmd(cmd *cobra.Command, args []string) (err error) { return pgp.CallCmd(cmd, SignCmd, args) } relic-7.6.1/cmdline/servecmd/000077500000000000000000000000001455105530300160475ustar00rootroot00000000000000relic-7.6.1/cmdline/servecmd/servecmd.go000066400000000000000000000054501455105530300202120ustar00rootroot00000000000000// // Copyright (c) SAS Institute Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package servecmd import ( "errors" "fmt" "net" "net/http" "github.com/rs/zerolog/log" "github.com/spf13/cobra" "github.com/sassoftware/relic/v7/cmdline/shared" "github.com/sassoftware/relic/v7/server/daemon" _ "net/http/pprof" ) var ServeCmd = &cobra.Command{ Use: "serve", Short: "Offer signing services over a HTTPS API", RunE: serveCmd, } var argTest bool func init() { shared.RootCmd.AddCommand(ServeCmd) ServeCmd.Flags().BoolP("force", "f", false, "(ignored)") ServeCmd.Flags().BoolVarP(&argTest, "test", "t", false, "Test configuration and exit") } func MakeServer() (*daemon.Daemon, error) { if err := shared.InitConfig(); err != nil { return nil, err } if shared.CurrentConfig.Server == nil { return nil, errors.New("Missing server section in configuration file") } if shared.CurrentConfig.Clients == nil { return nil, errors.New("Missing clients section in configuration file") } if shared.CurrentConfig.Server.Listen == "" && shared.CurrentConfig.Server.ListenHTTP == "" { shared.CurrentConfig.Server.Listen = ":6300" } if shared.CurrentConfig.Server.Listen != "" { if shared.CurrentConfig.Server.KeyFile == "" { return nil, errors.New("missing keyfile option in server configuration file") } if shared.CurrentConfig.Server.CertFile == "" { return nil, errors.New("missing certfile option in server configuration file") } } return daemon.New(shared.CurrentConfig, argTest) } func listenDebug() error { if !shared.CurrentConfig.Server.ListenDebug { return nil } lis, err := net.Listen("tcp", ":0") if err != nil { return err } log.Info().Msgf("serving debug info on http://%s/debug/pprof/", lis.Addr()) go func() { // pprof installs itself into the default handler on import err := http.Serve(lis, nil) log.Err(err).Msg("debug listener stopped") }() return nil } func serveCmd(cmd *cobra.Command, args []string) error { // let journald add timestamps srv, err := MakeServer() if err != nil { return shared.Fail(err) } else if argTest { fmt.Println("OK") return nil } go watchSignals(srv) if err := listenDebug(); err != nil { return err } if err := srv.Serve(); err != nil && err != http.ErrServerClosed { return shared.Fail(err) } return nil } relic-7.6.1/cmdline/servecmd/setupcmd.go000066400000000000000000000106731455105530300202310ustar00rootroot00000000000000// // Copyright (c) SAS Institute Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package servecmd import ( "crypto" "crypto/ecdsa" "crypto/rand" "crypto/rsa" "crypto/x509" "encoding/pem" "errors" "fmt" "io/ioutil" "os" "github.com/spf13/cobra" "github.com/sassoftware/relic/v7/cmdline/shared" "github.com/sassoftware/relic/v7/lib/certloader" "github.com/sassoftware/relic/v7/lib/x509tools" ) var SetupCmd = &cobra.Command{ Use: "setup", Short: "Generate private key and certificate request for server", RunE: setupCmd, } var ( argRsaBits uint argEcdsaBits uint argSelfSign bool ) func init() { SetupCmd.Flags().UintVar(&argRsaBits, "generate-rsa", 0, "Generate a RSA key of the specified bit size, if needed") SetupCmd.Flags().UintVar(&argEcdsaBits, "generate-ecdsa", 0, "Generate an ECDSA key of the specified curve size, if needed") SetupCmd.Flags().BoolVar(&argSelfSign, "self-sign", false, "Make and store a self-signed certificate instead of a request") x509tools.AddRequestFlags(SetupCmd) ServeCmd.AddCommand(SetupCmd) } func readKey(path string) (crypto.PrivateKey, error) { pemData, err := ioutil.ReadFile(path) if err != nil { return nil, err } return certloader.ParseAnyPrivateKey(pemData, nil) } func selectOrGenerate(path string) (crypto.PrivateKey, error) { key, err := readKey(path) if err == nil { fmt.Fprintf(os.Stderr, "Using existing private key at %s\n", path) return key, nil } else if !os.IsNotExist(err) { return nil, fmt.Errorf("reading private key: %w", err) } var block *pem.Block if argRsaBits != 0 { rsaKey, err := rsa.GenerateKey(rand.Reader, int(argRsaBits)) if err != nil { return nil, err } key = rsaKey keyBytes := x509.MarshalPKCS1PrivateKey(rsaKey) block = &pem.Block{Type: "RSA PRIVATE KEY", Bytes: keyBytes} } else if argEcdsaBits != 0 { curve, err := x509tools.CurveByBits(argEcdsaBits) if err != nil { return nil, err } ecKey, err := ecdsa.GenerateKey(curve.Curve, rand.Reader) if err != nil { return nil, err } key = ecKey keyBytes, err := x509.MarshalECPrivateKey(ecKey) if err != nil { return nil, err } block = &pem.Block{Type: "EC PRIVATE KEY", Bytes: keyBytes} } else { return nil, errors.New("No matching key exists, specify --generate-rsa or --generate-ecdsa to generate one") } pemData := pem.EncodeToMemory(block) if err := ioutil.WriteFile(path, pemData, 0600); err != nil { return nil, fmt.Errorf("writing new private key: %w", err) } fmt.Fprintf(os.Stderr, "Wrote new private key to %s\n", path) return key, nil } func setupCmd(cmd *cobra.Command, args []string) error { if err := shared.InitConfig(); err != nil { return err } if shared.CurrentConfig.Server == nil { return errors.New("Missing server section in configuration file") } if shared.CurrentConfig.Server.KeyFile == "" { return errors.New("Missing keyfile option in server configuration file") } if argSelfSign && shared.CurrentConfig.Server.KeyFile == "" { return errors.New("Missing certfile option in server configuration file") } if x509tools.ArgCommonName == "" { return errors.New("--commonName is required") } if x509tools.ArgDNSNames == "" && x509tools.ArgEmailNames == "" { fmt.Fprintf(os.Stderr, "Subject alternate names is empty; appending %s\n", x509tools.ArgCommonName) x509tools.ArgDNSNames = x509tools.ArgCommonName } key, err := selectOrGenerate(shared.CurrentConfig.Server.KeyFile) if err != nil { return err } signer, ok := key.(crypto.Signer) if !ok { panic("expected a signer") } if argSelfSign { x509tools.ArgExpireDays = 36525 x509tools.ArgCertAuthority = true cert, err := x509tools.MakeCertificate(rand.Reader, signer) if err != nil { return err } err = ioutil.WriteFile(shared.CurrentConfig.Server.CertFile, []byte(cert), 0600) if err != nil { return err } } else { req, err := x509tools.MakeRequest(rand.Reader, signer) if err != nil { return err } os.Stdout.WriteString(req) } return nil } relic-7.6.1/cmdline/servecmd/signals_unix.go000066400000000000000000000026011455105530300211000ustar00rootroot00000000000000//go:build !windows // +build !windows // // Copyright (c) SAS Institute Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package servecmd import ( "os" "os/signal" "syscall" "github.com/rs/zerolog/log" "github.com/sassoftware/relic/v7/server/daemon" ) func watchSignals(srv *daemon.Daemon) { ch := make(chan os.Signal, 4) signal.Notify( ch, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT, syscall.SIGUSR1, syscall.SIGUSR2, ) already := false for { sig := <-ch switch { case sig == syscall.SIGUSR1: // no longer used case !already: log.Info().Stringer("signal", sig).Msg("initiating graceful shutdown") go func() { if err := srv.Close(); err != nil { log.Err(err).Msg("failed to shutdown gracefully") } }() already = true default: log.Warn().Stringer("signal", sig).Msg("shutting down immediately") os.Exit(0) } } } relic-7.6.1/cmdline/servecmd/signals_windows.go000066400000000000000000000016101455105530300216060ustar00rootroot00000000000000// // Copyright (c) SAS Institute Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package servecmd import ( "log" "os" "os/signal" "github.com/sassoftware/relic/v7/server/daemon" ) func watchSignals(srv *daemon.Daemon) { ch := make(chan os.Signal, 1) signal.Notify(ch, os.Interrupt) sig := <-ch log.Printf("Received signal %d; shutting down immediately", sig) os.Exit(0) } relic-7.6.1/cmdline/shared/000077500000000000000000000000001455105530300155055ustar00rootroot00000000000000relic-7.6.1/cmdline/shared/digest.go000066400000000000000000000022741455105530300173200ustar00rootroot00000000000000// // Copyright (c) SAS Institute Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package shared import ( "crypto" "fmt" "github.com/spf13/cobra" "github.com/sassoftware/relic/v7/lib/x509tools" ) var ArgDigest string const DefaultHash = "SHA-256" func AddDigestFlag(cmd *cobra.Command) { cmd.Flags().StringVar(&ArgDigest, "digest", DefaultHash, "Specify a digest algorithm") } func GetDigest() (hash crypto.Hash, err error) { if ArgDigest == "" { // TODO: figure out why this randomly started coming back blank ArgDigest = DefaultHash } hash = x509tools.HashByName(ArgDigest) if hash == 0 { err = fmt.Errorf("unsupported digest \"%s\"", ArgDigest) } return hash, err } relic-7.6.1/cmdline/shared/main.go000066400000000000000000000034111455105530300167570ustar00rootroot00000000000000// // Copyright (c) SAS Institute Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package shared import ( "errors" "fmt" "os" "github.com/spf13/cobra" "github.com/sassoftware/relic/v7/config" ) var ( ArgConfig string ArgDebug uint32 CurrentConfig *config.Config argVersion bool ) var lateHooks []func() var RootCmd = &cobra.Command{ Use: "relic", PersistentPreRun: showVersion, RunE: bailUnlessVersion, } func init() { RootCmd.PersistentFlags().StringVarP(&ArgConfig, "config", "c", "", "Configuration file") RootCmd.PersistentFlags().BoolVar(&argVersion, "version", false, "Show version and exit") RootCmd.PersistentFlags().Uint32VarP(&ArgDebug, "debug", "d", 0, "Log additional diagnostic data. (0-9)") } func showVersion(cmd *cobra.Command, args []string) { if argVersion { fmt.Printf("relic version %s (%s)\n", config.Version, config.Commit) os.Exit(0) } } func bailUnlessVersion(cmd *cobra.Command, args []string) error { if !argVersion { return errors.New("Expected a command") } return nil } func AddLateHook(f func()) { lateHooks = append(lateHooks, f) } func Main() { for _, f := range lateHooks { f() } if err := RootCmd.Execute(); err != nil { fmt.Fprintln(os.Stderr, err) os.Exit(1) } } relic-7.6.1/cmdline/shared/util.go000066400000000000000000000043271455105530300170170ustar00rootroot00000000000000// // Copyright (c) SAS Institute Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package shared import ( "errors" "fmt" "os" "github.com/sassoftware/relic/v7/config" "github.com/sassoftware/relic/v7/lib/dlog" ) func InitConfig() error { return initConfig(false) } func InitClientConfig() error { return initConfig(true) } func initConfig(client bool) error { if CurrentConfig != nil { return nil } dlog.SetLevel(ArgDebug) usedDefault := false if ArgConfig == "" { ArgConfig = config.DefaultConfig() usedDefault = true } if client && usedDefault { cfg, err := config.FromEnvironment() if err != nil { return err } else if cfg != nil { CurrentConfig = cfg return nil } } if ArgConfig == "" { return errors.New("--config not specified") } cfg, err := config.ReadFile(ArgConfig) if err != nil { if os.IsNotExist(err) && usedDefault { if client { // try to use environment cfg, err = config.FromEnvironment() if err != nil { return err } else if cfg != nil { CurrentConfig = cfg return nil } } return fmt.Errorf("--config not specified and default config at %s does not exist", ArgConfig) } return err } CurrentConfig = cfg return nil } func OpenFile(path string) (*os.File, error) { if path == "-" { return os.Stdin, nil } return os.Open(path) } func OpenForPatching(inpath, outpath string) (*os.File, error) { switch { case inpath == "-": return os.Stdin, nil case inpath == outpath: // open for writing so in-place patch works return os.OpenFile(inpath, os.O_RDWR, 0) default: return os.Open(inpath) } } func Fail(err error) error { if err != nil { fmt.Fprintln(os.Stderr, "ERROR:", err) os.Exit(70) } return err } relic-7.6.1/cmdline/token/000077500000000000000000000000001455105530300153575ustar00rootroot00000000000000relic-7.6.1/cmdline/token/common.go000066400000000000000000000111241455105530300171750ustar00rootroot00000000000000// // Copyright (c) SAS Institute Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package token import ( "context" "errors" "fmt" "os" "strings" "time" "github.com/spf13/cobra" "github.com/sassoftware/relic/v7/cmdline/shared" "github.com/sassoftware/relic/v7/config" "github.com/sassoftware/relic/v7/lib/passprompt" "github.com/sassoftware/relic/v7/signers/sigerrors" "github.com/sassoftware/relic/v7/token" "github.com/sassoftware/relic/v7/token/open" ) var ( argFile string argKeyName string argToken string argLabel string argRsaBits uint argEcdsaBits uint ) var tokenMap map[string]token.Token func addKeyFlags(cmd *cobra.Command) { cmd.Flags().StringVarP(&argKeyName, "key", "k", "", "Name of key section in config file to use") } func addSelectOrGenerateFlags(cmd *cobra.Command) { addKeyFlags(cmd) cmd.Flags().StringVarP(&argToken, "token", "t", "", "Name of token to generate key in") cmd.Flags().StringVarP(&argLabel, "label", "l", "", "Label to attach to generated key") cmd.Flags().UintVar(&argRsaBits, "generate-rsa", 0, "Generate a RSA key of the specified bit size, if needed") cmd.Flags().UintVar(&argEcdsaBits, "generate-ecdsa", 0, "Generate an ECDSA key of the specified curve size, if needed") } // Update key config with values from --token and --label func newKeyConfig() (*config.KeyConfig, error) { if err := shared.InitConfig(); err != nil { return nil, err } var keyConf *config.KeyConfig if argKeyName != "" { var err error keyConf, err = shared.CurrentConfig.GetKey(argKeyName) if err != nil { return nil, err } } else { if argToken == "" || argLabel == "" { return nil, errors.New("Either --key, or --token and --label, must be set") } argKeyName = fmt.Sprintf("new-key-%d", time.Now().UnixNano()) keyConf = shared.CurrentConfig.NewKey(argKeyName) } if argToken != "" { tokenConf, err := shared.CurrentConfig.GetToken(argToken) if err != nil { return nil, err } keyConf.SetToken(tokenConf) } if argLabel != "" { keyConf.Label = argLabel keyConf.ID = "" } return keyConf, nil } func selectOrGenerate() (key token.Key, err error) { keyConf, err := newKeyConfig() if err != nil { return nil, err } tok, err := openToken(keyConf.Token) if err != nil { return nil, err } key, err = tok.GetKey(context.Background(), argKeyName) if err == nil { fmt.Fprintln(os.Stderr, "Using existing key in token") return key, nil } else if _, ok := err.(sigerrors.KeyNotFoundError); !ok { return nil, err } fmt.Fprintln(os.Stderr, "Generating a new key in token") if argRsaBits != 0 { return tok.Generate(argKeyName, token.KeyTypeRsa, argRsaBits) } else if argEcdsaBits != 0 { return tok.Generate(argKeyName, token.KeyTypeEcdsa, argEcdsaBits) } else { return nil, errors.New("No matching key exists, specify --generate-rsa or --generate-ecdsa to generate one") } } func openToken(tokenName string) (token.Token, error) { tok, ok := tokenMap[tokenName] if ok { return tok, nil } err := shared.InitConfig() if err != nil { return nil, err } prompt := new(passprompt.PasswordPrompt) tok, err = open.Token(shared.CurrentConfig, tokenName, prompt) if err != nil { return nil, err } if tokenMap == nil { tokenMap = make(map[string]token.Token) } tokenMap[tokenName] = tok return tok, nil } func openTokenByKey(keyName string) (token.Token, error) { if keyName == "" { return nil, errors.New("--key is a required parameter") } err := shared.InitConfig() if err != nil { return nil, err } keyConf, err := shared.CurrentConfig.GetKey(keyName) if err != nil { return nil, err } tok, err := openToken(keyConf.Token) if err != nil { return nil, err } return tok, nil } func openKey(keyName string) (token.Key, error) { tok, err := openTokenByKey(keyName) if err != nil { return nil, err } key, err := tok.GetKey(context.Background(), keyName) if err != nil { tok.Close() return nil, err } return key, err } func formatKeyID(keyID []byte) string { chunks := make([]string, len(keyID)) for i, j := range keyID { chunks[i] = fmt.Sprintf("%02x", j) } return strings.Join(chunks, ":") } relic-7.6.1/cmdline/token/importkeycmd.go000066400000000000000000000073671455105530300204320ustar00rootroot00000000000000// // Copyright (c) SAS Institute Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package token import ( "context" "errors" "fmt" "io/ioutil" "os" "github.com/spf13/cobra" "github.com/sassoftware/relic/v7/cmdline/shared" "github.com/sassoftware/relic/v7/lib/certloader" "github.com/sassoftware/relic/v7/lib/passprompt" "github.com/sassoftware/relic/v7/lib/x509tools" "github.com/sassoftware/relic/v7/signers/sigerrors" ) var ImportKeyCmd = &cobra.Command{ Use: "import-key", Short: "Import a private key to a token", RunE: importKeyCmd, } var argPkcs12 bool func init() { shared.RootCmd.AddCommand(ImportKeyCmd) addKeyFlags(ImportKeyCmd) ImportKeyCmd.Flags().StringVarP(&argToken, "token", "t", "", "Name of token to import key to") ImportKeyCmd.Flags().StringVarP(&argLabel, "label", "l", "", "Label to attach to imported key") ImportKeyCmd.Flags().StringVarP(&argFile, "file", "f", "", "Private key file to import: PEM, DER, or PGP") ImportKeyCmd.Flags().BoolVar(&argPkcs12, "pkcs12", false, "Import a PKCS12 key and certificate chain") } func importKeyCmd(cmd *cobra.Command, args []string) error { if argFile == "" { return errors.New("--file is required") } blob, err := ioutil.ReadFile(argFile) if err != nil { return shared.Fail(err) } prompt := new(passprompt.PasswordPrompt) var cert *certloader.Certificate if argPkcs12 { var err error cert, err = certloader.ParsePKCS12(blob, prompt) if err != nil { return shared.Fail(err) } } else { privKey, err := certloader.ParseAnyPrivateKey(blob, prompt) if err != nil { return shared.Fail(err) } cert = &certloader.Certificate{PrivateKey: privKey} } keyConf, err := newKeyConfig() if err != nil { return err } tok, err := openToken(keyConf.Token) if err != nil { return shared.Fail(err) } var didSomething bool key, err := tok.GetKey(context.Background(), argKeyName) if err == nil { if cert.Leaf == nil { return errors.New("An object with that label already exists in the token") } fmt.Fprintln(os.Stderr, "Private key already exists. Attempting to import certificates.") } else if _, ok := err.(sigerrors.KeyNotFoundError); !ok { return err } else { key, err = tok.Import(argKeyName, cert.PrivateKey) if err != nil { return err } didSomething = true } if cert.Leaf != nil { name := x509tools.FormatSubject(cert.Leaf) err := key.ImportCertificate(cert.Leaf) if err == sigerrors.ErrExist { fmt.Fprintln(os.Stderr, "Certificate already exists:", name) } else if err != nil { return shared.Fail(fmt.Errorf("importing %s: %w", name, err)) } else { fmt.Fprintln(os.Stderr, "Imported", name) didSomething = true } for _, chain := range cert.Chain() { if chain == cert.Leaf { continue } name = x509tools.FormatSubject(chain) err = tok.ImportCertificate(chain, keyConf.Label) if err == sigerrors.ErrExist { fmt.Fprintln(os.Stderr, "Certificate already exists:", name) } else if err != nil { return shared.Fail(fmt.Errorf("importing %s: %w", name, err)) } else { fmt.Fprintln(os.Stderr, "Imported", name) didSomething = true } } } if !didSomething { return shared.Fail(errors.New("nothing imported")) } fmt.Fprintln(os.Stderr, "Token CKA_ID: ", formatKeyID(key.GetID())) return nil } relic-7.6.1/cmdline/token/newpgpkeycmd.go000066400000000000000000000070031455105530300204030ustar00rootroot00000000000000// // Copyright (c) SAS Institute Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package token import ( "crypto" "crypto/ecdsa" "crypto/rsa" "encoding/hex" "errors" "fmt" "os" "strings" "time" "github.com/spf13/cobra" "golang.org/x/crypto/openpgp" "golang.org/x/crypto/openpgp/armor" "golang.org/x/crypto/openpgp/packet" "github.com/sassoftware/relic/v7/cmdline/shared" "github.com/sassoftware/relic/v7/token" ) var NewPgpKeyCmd = &cobra.Command{ Use: "pgp-generate", Short: "Generate a new PGP key from token", RunE: newPgpKeyCmd, } var ( argUserName string argUserComment string argUserEmail string ) func init() { shared.RootCmd.AddCommand(NewPgpKeyCmd) NewPgpKeyCmd.Flags().StringVarP(&argUserName, "name", "n", "", "Name of user identity") NewPgpKeyCmd.Flags().StringVarP(&argUserComment, "comment", "C", "", "Comment of user identity") NewPgpKeyCmd.Flags().StringVarP(&argUserEmail, "email", "E", "", "Email of user identity") addSelectOrGenerateFlags(NewPgpKeyCmd) } func makeKey(key token.Key, uids []*packet.UserId) (*openpgp.Entity, error) { creationTime := time.Now() var pubKey *packet.PublicKey switch pub := key.Public().(type) { case *rsa.PublicKey: pubKey = packet.NewRSAPublicKey(creationTime, pub) case *ecdsa.PublicKey: pubKey = packet.NewECDSAPublicKey(creationTime, pub) default: return nil, errors.New("Unsupported key type") } entity := &openpgp.Entity{ PrimaryKey: pubKey, PrivateKey: &packet.PrivateKey{ PublicKey: *pubKey, Encrypted: false, PrivateKey: key, }, Identities: make(map[string]*openpgp.Identity), } isPrimaryID := true for _, uid := range uids { sig := &packet.Signature{ SigType: packet.SigTypePositiveCert, CreationTime: creationTime, PubKeyAlgo: pubKey.PubKeyAlgo, Hash: crypto.SHA512, IsPrimaryId: &isPrimaryID, FlagsValid: true, FlagSign: true, FlagCertify: true, IssuerKeyId: &pubKey.KeyId, } err := sig.SignUserId(uid.Id, entity.PrimaryKey, entity.PrivateKey, nil) if err != nil { return nil, err } entity.Identities[uid.Id] = &openpgp.Identity{ Name: uid.Name, UserId: uid, SelfSignature: sig, } } return entity, nil } func newPgpKeyCmd(cmd *cobra.Command, args []string) error { if argUserName == "" { return errors.New("--name is required") } uid := packet.NewUserId(argUserName, argUserComment, argUserEmail) if uid == nil { return errors.New("Invalid user ID") } key, err := selectOrGenerate() if err != nil { return err } entity, err := makeKey(key, []*packet.UserId{uid}) if err != nil { return err } fingerprint := hex.EncodeToString(entity.PrimaryKey.Fingerprint[:]) fmt.Fprintln(os.Stderr, "Token CKA_ID: ", formatKeyID(key.GetID())) fmt.Fprintln(os.Stderr, "PGP ID: ", strings.ToUpper(fingerprint)) writer, err := armor.Encode(os.Stdout, openpgp.PublicKeyType, nil) if err != nil { return err } err = entity.Serialize(writer) if err != nil { return err } writer.Close() fmt.Println() return nil } relic-7.6.1/cmdline/token/ping.go000066400000000000000000000023531455105530300166460ustar00rootroot00000000000000// // Copyright (c) SAS Institute Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package token import ( "context" "fmt" "github.com/spf13/cobra" "github.com/sassoftware/relic/v7/cmdline/shared" ) var PingCmd = &cobra.Command{ Use: "ping", Short: "Check whether a token is working", RunE: pingCmd, } func init() { shared.RootCmd.AddCommand(PingCmd) PingCmd.Flags().StringVarP(&argToken, "token", "t", "", "Name of token section in config file to use") } func pingCmd(cmd *cobra.Command, args []string) error { token, err := openToken(argToken) if err != nil { return shared.Fail(err) } if err := token.Ping(context.Background()); err != nil { return shared.Fail(err) } else { fmt.Println("OK") } return nil } relic-7.6.1/cmdline/token/signcmd.go000066400000000000000000000076271455105530300173460ustar00rootroot00000000000000// // Copyright (c) SAS Institute Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package token import ( "bytes" "context" "errors" "fmt" "os" "github.com/spf13/cobra" "github.com/sassoftware/relic/v7/cmdline/shared" "github.com/sassoftware/relic/v7/internal/signinit" "github.com/sassoftware/relic/v7/signers" ) var SignCmd = &cobra.Command{ Use: "sign", Short: "Sign a package using a token", RunE: signCmd, } var ( argIfUnsigned bool argSigType string argOutput string ) func init() { shared.RootCmd.AddCommand(SignCmd) addKeyFlags(SignCmd) SignCmd.Flags().StringVarP(&argFile, "file", "f", "", "Input file to sign") SignCmd.Flags().StringVarP(&argOutput, "output", "o", "", "Output file") SignCmd.Flags().StringVarP(&argSigType, "sig-type", "T", "", "Specify signature type (default: auto-detect)") SignCmd.Flags().BoolVar(&argIfUnsigned, "if-unsigned", false, "Skip signing if the file already has a signature") shared.AddDigestFlag(SignCmd) shared.AddLateHook(func() { signers.MergeFlags(SignCmd) }) } func signCmd(cmd *cobra.Command, args []string) error { if argFile == "" || argKeyName == "" { return errors.New("--file and --key are required") } if argOutput == "" { argOutput = argFile } mod, err := signers.ByFile(argFile, argSigType) if err != nil { return shared.Fail(err) } if mod.Sign == nil { return shared.Fail(fmt.Errorf("can't sign files of type: %s", mod.Name)) } flags, err := mod.FlagsFromCmdline(cmd.Flags()) if err != nil { return shared.Fail(err) } hash, err := shared.GetDigest() if err != nil { return shared.Fail(err) } token, err := openTokenByKey(argKeyName) if err != nil { return shared.Fail(err) } cert, opts, err := signinit.Init(context.Background(), mod, token, argKeyName, hash, flags) if err != nil { return shared.Fail(err) } opts.Path = argFile infile, err := shared.OpenForPatching(argFile, argOutput) if err != nil { return shared.Fail(err) } else if infile == os.Stdin { if !mod.AllowStdin { return shared.Fail(errors.New("this signature type does not support reading from stdin")) } } else { defer infile.Close() } if argIfUnsigned { if infile == os.Stdin { return shared.Fail(errors.New("cannot use --if-unsigned with standard input")) } if signed, err := mod.IsSigned(infile); err != nil { return shared.Fail(err) } else if signed { fmt.Fprintf(os.Stderr, "skipping already-signed file: %s\n", argFile) return nil } if _, err := infile.Seek(0, 0); err != nil { return shared.Fail(fmt.Errorf("rewinding input file: %w", err)) } } // transform the input, sign the stream, and apply the result transform, err := mod.GetTransform(infile, *opts) if err != nil { return shared.Fail(err) } stream, err := transform.GetReader() if err != nil { return shared.Fail(err) } blob, err := mod.Sign(stream, cert, *opts) if err != nil { return shared.Fail(err) } mimeType := opts.Audit.GetMimeType() if err := transform.Apply(argOutput, mimeType, bytes.NewReader(blob)); err != nil { return shared.Fail(err) } // if needed, do a final fixup step if mod.Fixup != nil { f, err := os.OpenFile(argOutput, os.O_RDWR, 0) if err != nil { return shared.Fail(err) } defer f.Close() if err := mod.Fixup(f); err != nil { return shared.Fail(err) } } if err := signinit.PublishAudit(opts.Audit); err != nil { return err } fmt.Fprintln(os.Stderr, "Signed", argFile) return nil } relic-7.6.1/cmdline/token/signpgpcmd.go000066400000000000000000000023711455105530300200440ustar00rootroot00000000000000// // Copyright (c) SAS Institute Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package token import ( "github.com/spf13/cobra" "github.com/sassoftware/relic/v7/cmdline/shared" "github.com/sassoftware/relic/v7/signers/pgp" ) var SignPgpCmd = &cobra.Command{ Use: "sign-pgp", Short: "Create PGP signatures", Long: "This command is vaguely compatible with the gpg command-line and accepts (and mostly, ignores) many of gpg's options. It can thus be used as a drop-in replacement for tools that use gpg to make signatures.", RunE: signPgpCmd, } func init() { pgp.AddCompatFlags(SignPgpCmd) shared.RootCmd.AddCommand(SignPgpCmd) } func signPgpCmd(cmd *cobra.Command, args []string) (err error) { return pgp.CallCmd(cmd, SignCmd, args) } relic-7.6.1/cmdline/token/token.go000066400000000000000000000070501455105530300170300ustar00rootroot00000000000000// // Copyright (c) SAS Institute Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package token import ( "errors" "fmt" "os" "sort" "strings" "github.com/spf13/cobra" "github.com/sassoftware/relic/v7/cmdline/shared" "github.com/sassoftware/relic/v7/config" "github.com/sassoftware/relic/v7/token" "github.com/sassoftware/relic/v7/token/open" ) var TokenCmd = &cobra.Command{ Use: "token", Short: "View and manipulate token objects", } var TokensCmd = &cobra.Command{ Use: "list", Short: "List tokens provided by a driver", RunE: tokensCmd, } var ContentsCmd = &cobra.Command{ Use: "contents", Short: "List keys in a token", RunE: contentsCmd, } var ( argType string argProvider string argId string argValues bool ) func init() { shared.RootCmd.AddCommand(TokenCmd) TokenCmd.PersistentFlags().StringVarP(&argToken, "token", "t", "", "Name of token") TokenCmd.PersistentFlags().StringVar(&argProvider, "provider", "", "Provider module path") TokenCmd.AddCommand(TokensCmd) TokenCmd.AddCommand(ContentsCmd) ContentsCmd.Flags().StringVarP(&argLabel, "label", "l", "", "Display objects with this label only") ContentsCmd.Flags().StringVarP(&argId, "id", "i", "", "Display objects with this ID only") ContentsCmd.Flags().BoolVarP(&argValues, "values", "v", false, "Show contents of objects") shared.AddLateHook(addProviderTypeHelp) // deferred so token providers can init() } func addProviderTypeHelp() { var listable []string for ptype := range token.Listers { listable = append(listable, ptype) } sort.Strings(listable) TokenCmd.PersistentFlags().StringVar(&argType, "type", "", fmt.Sprintf("Provider type (%s)", strings.Join(listable, ", "))) } func tokensCmd(cmd *cobra.Command, args []string) error { if argToken == "" && (argType == "" || argProvider == "") { return errors.New("--token, or --type and --provider, are required") } if err := shared.InitConfig(); err != nil { return err } if argToken != "" { tokenConf, err := shared.CurrentConfig.GetToken(argToken) if err != nil { return err } if argType == "" { argType = tokenConf.Type } if argProvider == "" { argProvider = tokenConf.Provider } } return shared.Fail(open.List(argType, argProvider, os.Stdout)) } func contentsCmd(cmd *cobra.Command, args []string) error { if argToken == "" && (argType == "" || argProvider == "") { return errors.New("--token, or --type and --provider, are required") } if err := shared.InitConfig(); err != nil { return err } var tokenConf *config.TokenConfig if argToken != "" { var err error tokenConf, err = shared.CurrentConfig.GetToken(argToken) if err != nil { return err } } else { argToken = ":new-token:" tokenConf = shared.CurrentConfig.NewToken(argToken) } if argType != "" { tokenConf.Type = argType } if argProvider != "" { tokenConf.Provider = argProvider } tok, err := openToken(argToken) if err != nil { return err } return shared.Fail(tok.ListKeys(token.ListOptions{ Output: os.Stdout, Label: argLabel, ID: argId, Values: argValues, })) } relic-7.6.1/cmdline/token/x509cmd.go000066400000000000000000000064461455105530300171110ustar00rootroot00000000000000// // Copyright (c) SAS Institute Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package token import ( "crypto/rand" "errors" "fmt" "io/ioutil" "os" "github.com/spf13/cobra" "github.com/sassoftware/relic/v7/cmdline/shared" "github.com/sassoftware/relic/v7/lib/certloader" "github.com/sassoftware/relic/v7/lib/x509tools" ) var ( argCopyExtensions bool argCrossSign bool ) var ReqCmd = &cobra.Command{ Use: "x509-request", Short: "Generate PKCS#10 certificate signing request", } var SelfSignCmd = &cobra.Command{ Use: "x509-self-sign", Short: "Generate self-signed X509 certificate", } var SignCsrCmd = &cobra.Command{ Use: "x509-sign", Short: "Create a X509 certificate from a certificate signing request", RunE: signCsrCmd, } func init() { ReqCmd.RunE = x509Cmd shared.RootCmd.AddCommand(ReqCmd) addSelectOrGenerateFlags(ReqCmd) x509tools.AddRequestFlags(ReqCmd) SelfSignCmd.RunE = x509Cmd shared.RootCmd.AddCommand(SelfSignCmd) addSelectOrGenerateFlags(SelfSignCmd) x509tools.AddCertFlags(SelfSignCmd) shared.RootCmd.AddCommand(SignCsrCmd) addKeyFlags(SignCsrCmd) x509tools.AddCertFlags(SignCsrCmd) SignCsrCmd.Flags().BoolVar(&argCopyExtensions, "copy-extensions", false, "Copy extensions verbabim from CSR") SignCsrCmd.Flags().BoolVar(&argCrossSign, "cross-sign", false, "Input is an existing certificate (implies --copy-extensions)") } func x509Cmd(cmd *cobra.Command, args []string) error { if x509tools.ArgCommonName == "" { return errors.New("--commonName is required") } key, err := selectOrGenerate() if err != nil { return err } var result string if cmd == ReqCmd { result, err = x509tools.MakeRequest(rand.Reader, key) } else { result, err = x509tools.MakeCertificate(rand.Reader, key) } if err != nil { return err } os.Stdout.WriteString(result) if ckaID := key.GetID(); len(ckaID) != 0 { fmt.Println("CKA_ID:", formatKeyID(ckaID)) } return nil } func signCsrCmd(cmd *cobra.Command, args []string) error { if len(args) != 1 { return errors.New("expected a CSR file as input") } csr, err := ioutil.ReadFile(args[0]) if err != nil { return err } key, err := openKey(argKeyName) if err != nil { return err } parsedCert := key.Certificate() certPath := key.Config().X509Certificate if certPath == "" && len(parsedCert) == 0 { return errors.New("token key has no x509 certificate") } cert, err := certloader.LoadTokenCertificates(key, certPath, "", parsedCert) if err != nil { return err } if argCrossSign { result, err := x509tools.CrossSign(csr, rand.Reader, key, cert.Leaf) if err != nil { return err } os.Stdout.WriteString(result) } else { result, err := x509tools.SignCSR(csr, rand.Reader, key, cert.Leaf, argCopyExtensions) if err != nil { return err } os.Stdout.WriteString(result) } return nil } relic-7.6.1/cmdline/verify/000077500000000000000000000000001455105530300155435ustar00rootroot00000000000000relic-7.6.1/cmdline/verify/verify.go000066400000000000000000000131271455105530300174020ustar00rootroot00000000000000// // Copyright (c) SAS Institute Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package verify import ( "crypto/x509" "encoding/pem" "errors" "fmt" "os" "github.com/spf13/cobra" "github.com/sassoftware/relic/v7/cmdline/shared" "github.com/sassoftware/relic/v7/lib/certloader" "github.com/sassoftware/relic/v7/lib/magic" "github.com/sassoftware/relic/v7/lib/pgptools" "github.com/sassoftware/relic/v7/lib/x509tools" "github.com/sassoftware/relic/v7/signers" ) var VerifyCmd = &cobra.Command{ Use: "verify", Short: "Verify a signed package or executable", RunE: verifyCmd, } var ( argNoIntegrityCheck bool argNoChain bool argAlsoSystem bool argShowCerts bool argContent string argTrustedCerts []string ) func init() { shared.RootCmd.AddCommand(VerifyCmd) VerifyCmd.Flags().BoolVar(&argNoIntegrityCheck, "no-integrity-check", false, "Bypass the integrity check of the file contents and only inspect the signature itself") VerifyCmd.Flags().BoolVar(&argNoChain, "no-trust-chain", false, "Do not test whether the signing certificate is trusted") VerifyCmd.Flags().BoolVar(&argAlsoSystem, "system-store", false, "When --cert is used, append rather than replace the system trust store") VerifyCmd.Flags().BoolVar(&argShowCerts, "show-certs", false, "Dump certificate chain from signature") VerifyCmd.Flags().StringVar(&argContent, "content", "", "Specify file containing contents for detached signatures") VerifyCmd.Flags().StringArrayVar(&argTrustedCerts, "cert", nil, "Add a trusted root certificate (PEM, DER, PKCS#7, or PGP)") } func verifyCmd(cmd *cobra.Command, args []string) error { if len(args) == 0 { return errors.New("Expected 1 or more files") } opts, err := loadCerts() if err != nil { return err } rc := 0 for _, path := range args { if err := verifyOne(path, opts); err != nil { fmt.Printf("%s ERROR: %s\n", path, err) rc = 1 } } if rc != 0 { fmt.Fprintln(os.Stderr, "ERROR: 1 or more files did not validate") } os.Exit(rc) return nil } func verifyOne(path string, opts signers.VerifyOpts) error { f, err := shared.OpenFile(path) if err != nil { return err } defer f.Close() fileType, compression := magic.DetectCompressed(f) opts.FileName = path opts.Compression = compression if _, err := f.Seek(0, 0); err != nil { return err } mod := signers.ByMagic(fileType) if mod == nil { mod = signers.ByFileName(path) } if mod == nil { return errors.New("unknown filetype") } var sigs []*signers.Signature if mod.VerifyStream != nil { r, err2 := magic.Decompress(f, opts.Compression) if err2 != nil { return err } sigs, err = mod.VerifyStream(r, opts) } else { if opts.Compression != magic.CompressedNone { return errors.New("cannot verify compressed file") } sigs, err = mod.Verify(f, opts) } if err != nil { if _, ok := err.(pgptools.ErrNoKey); ok { return fmt.Errorf("%w; use --cert to specify known keys", err) } return err } sawCerts := make(map[string]bool) for _, sig := range sigs { var si, pkg, ts string if sig.SigInfo != "" { si = " " + sig.SigInfo + ":" } if sig.Package != "" { pkg = sig.Package + " " } if sig.X509Signature != nil && argShowCerts { showCert(sig.X509Signature.Certificate.Raw, sawCerts) for _, cert := range sig.X509Signature.Intermediates { showCert(cert.Raw, sawCerts) } } if sig.X509Signature != nil && !opts.NoChain { if err := sig.X509Signature.VerifyChain(opts.TrustedPool, nil, x509.ExtKeyUsageAny); err != nil { if e := new(x509.UnknownAuthorityError); errors.As(err, e) { fmt.Printf("While validating certificate:\n Subject: %s\n Issuer: %s\n Serial: %X\n", x509tools.FormatSubject(e.Cert), x509tools.FormatIssuer(e.Cert), e.Cert.SerialNumber) } return err } } if sig.X509Signature != nil && sig.X509Signature.CounterSignature != nil { fmt.Printf("%s: OK -%s %s%s\n", path, si, pkg, sig.SignerName()) fmt.Printf("%s(timestamp): OK - `%s` [%s]\n", path, x509tools.FormatSubject(sig.X509Signature.CounterSignature.Certificate), sig.X509Signature.CounterSignature.SigningTime) } else { if !sig.CreationTime.IsZero() { ts = fmt.Sprintf(" [%s]", sig.CreationTime) } fmt.Printf("%s: OK -%s %s%s%s\n", path, si, pkg, sig.SignerName(), ts) } } return nil } func loadCerts() (signers.VerifyOpts, error) { opts := signers.VerifyOpts{ NoChain: argNoChain, NoDigests: argNoIntegrityCheck, Content: argContent, } trusted, err := certloader.LoadAnyCerts(argTrustedCerts) if err != nil { return opts, err } opts.TrustedX509 = trusted.X509Certs opts.TrustedPgp = trusted.PGPCerts if len(opts.TrustedX509) > 0 { if argAlsoSystem { var err error opts.TrustedPool, err = x509.SystemCertPool() if err != nil { return opts, err } } else { opts.TrustedPool = x509.NewCertPool() } for _, cert := range opts.TrustedX509 { opts.TrustedPool.AddCert(cert) } } return opts, nil } func showCert(blob []byte, seen map[string]bool) { if seen[string(blob)] { return } seen[string(blob)] = true _ = pem.Encode(os.Stdout, &pem.Block{ Type: "CERTIFICATE", Bytes: blob, }) } relic-7.6.1/cmdline/workercmd/000077500000000000000000000000001455105530300162345ustar00rootroot00000000000000relic-7.6.1/cmdline/workercmd/handler.go000066400000000000000000000117111455105530300202010ustar00rootroot00000000000000// // Copyright (c) SAS Institute Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package workercmd import ( "context" "crypto" "crypto/hmac" "crypto/rsa" "crypto/x509" "encoding/json" "errors" "fmt" "io" "net/http" "os" "time" "github.com/miekg/pkcs11" "github.com/rs/zerolog/log" "github.com/sassoftware/relic/v7/cmdline/shared" "github.com/sassoftware/relic/v7/internal/workerrpc" "github.com/sassoftware/relic/v7/token" "github.com/sassoftware/relic/v7/token/tokencache" ) // an arbitarily-chosen set of error codes that indicate that the token session // is busted and that the worker should exit and start over var fatalErrors = map[pkcs11Error]bool{ pkcs11.CKR_CRYPTOKI_NOT_INITIALIZED: true, pkcs11.CKR_DEVICE_REMOVED: true, pkcs11.CKR_GENERAL_ERROR: true, pkcs11.CKR_HOST_MEMORY: true, pkcs11.CKR_LIBRARY_LOAD_FAILED: true, pkcs11.CKR_SESSION_CLOSED: true, pkcs11.CKR_SESSION_HANDLE_INVALID: true, pkcs11.CKR_TOKEN_NOT_PRESENT: true, pkcs11.CKR_TOKEN_NOT_RECOGNIZED: true, pkcs11.CKR_USER_NOT_LOGGED_IN: true, } func (h *handler) healthCheck() { interval := time.Duration(shared.CurrentConfig.Server.TokenCheckInterval) * time.Second timeout := time.Duration(shared.CurrentConfig.Server.TokenCheckTimeout) * time.Second ppid := os.Getppid() tick := time.NewTicker(interval) errch := make(chan error) for { // check if parent process went away if os.Getppid() != ppid { log.Error(). Int("old_ppid", ppid).Int("new_ppid", os.Getppid()). Msg("parent process disappeared, worker stopping") h.shutdown() return } // check if token is alive ctx, cancel := context.WithTimeout(context.Background(), timeout) go func() { errch <- h.token.Ping(ctx) }() var err error select { case err = <-errch: // ping completed case <-ctx.Done(): // timed out err = fmt.Errorf("timed out after %s", timeout) } cancel() if err != nil { // stop the worker on error log.Err(err).Msg("token health check failed") h.shutdown() return } // wait for next tick <-tick.C } } type handler struct { token *tokencache.Cache cookie []byte shutdown func() } func (h *handler) ServeHTTP(rw http.ResponseWriter, req *http.Request) { // validate auth cookie cookie := req.Header.Get("Auth-Cookie") if !hmac.Equal([]byte(cookie), []byte(h.cookie)) { rw.WriteHeader(http.StatusForbidden) return } // dispatch resp, err := h.handle(rw, req) if err != nil { resp.Retryable = true resp.Err = err.Error() switch e := err.(type) { case pkcs11Error: if fatalErrors[e] { log.Err(err).Msg("terminating worker due to token error") go h.shutdown() // errors that cause the worker to restart are also retryable resp.Retryable = true } else { // pkcs11 errors not in fatalErrors are probably user error, so don't retry resp.Retryable = false } case token.NotImplementedError: resp.Retryable = false case token.KeyUsageError: resp.Retryable = false resp.Usage = true resp.Key = e.Key resp.Err = e.Err.Error() } } // marshal response blob, err := json.Marshal(resp) if err == nil { _, err = rw.Write(blob) } if err != nil { log.Err(err).Msg("error handling worker request") } } func (h *handler) handle(rw http.ResponseWriter, req *http.Request) (resp workerrpc.Response, err error) { blob, err := io.ReadAll(req.Body) if err != nil { return resp, err } var rr workerrpc.Request if err := json.Unmarshal(blob, &rr); err != nil { return resp, err } ctx := req.Context() if rr.KeyID != nil { // if the caller knows the key ID already, pass that along to ensure the // same key is used in case it got rotated ctx = token.WithKeyID(ctx, rr.KeyID) } switch req.URL.Path { case workerrpc.Ping: return resp, h.token.Ping(ctx) case workerrpc.GetKey: key, err := h.token.GetKey(ctx, rr.KeyName) if err != nil { return resp, err } resp.ID = key.GetID() resp.Cert = key.Certificate() resp.Value, err = x509.MarshalPKIXPublicKey(key.Public()) return resp, err case workerrpc.Sign: hash := crypto.Hash(rr.Hash) opts := crypto.SignerOpts(hash) if rr.SaltLength != nil { opts = &rsa.PSSOptions{SaltLength: *rr.SaltLength, Hash: hash} } key, err := h.token.GetKey(ctx, rr.KeyName) if err != nil { return resp, err } resp.Value, err = key.SignContext(ctx, rr.Digest, opts) return resp, err default: return resp, errors.New("invalid method: " + req.URL.Path) } } relic-7.6.1/cmdline/workercmd/pkcs11error_cgo.go000066400000000000000000000013321455105530300215660ustar00rootroot00000000000000// // Copyright (c) SAS Institute Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //go:build cgo && !pure // +build cgo,!pure package workercmd import "github.com/miekg/pkcs11" type pkcs11Error = pkcs11.Error relic-7.6.1/cmdline/workercmd/pkcs11error_nocgo.go000066400000000000000000000014611455105530300221260ustar00rootroot00000000000000// // Copyright (c) SAS Institute Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //go:build !cgo || pure // +build !cgo pure package workercmd // stub to make catching errors simpler while still compiling with cgo disabled type pkcs11Error uint func (pkcs11Error) Error() string { return "" } relic-7.6.1/cmdline/workercmd/workercmd.go000066400000000000000000000063461455105530300205710ustar00rootroot00000000000000// // Copyright (c) SAS Institute Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package workercmd import ( "context" "fmt" "io" "net/http" "os" "os/signal" "sync" "syscall" "time" "github.com/rs/zerolog" "github.com/rs/zerolog/log" "github.com/spf13/cobra" "github.com/sassoftware/relic/v7/cmdline/shared" "github.com/sassoftware/relic/v7/internal/activation" "github.com/sassoftware/relic/v7/internal/activation/activatecmd" "github.com/sassoftware/relic/v7/internal/zhttp" "github.com/sassoftware/relic/v7/token/open" "github.com/sassoftware/relic/v7/token/tokencache" ) func init() { AddWorkerCommand(shared.RootCmd) } func AddWorkerCommand(parent *cobra.Command) { wc := &cobra.Command{ Use: "worker", Run: worker, Hidden: true, } parent.AddCommand(wc) } func worker(cmd *cobra.Command, args []string) { if len(args) != 2 { log.Fatal().Msg("invalid argument") } shared.ArgConfig = args[0] tokenName := args[1] if err := runWorker(tokenName); err != nil { log.Fatal().Msgf("worker stopping for token %s", tokenName) } } func runWorker(tokenName string) error { if err := shared.InitConfig(); err != nil { return err } cookie, err := io.ReadAll(os.Stdin) if err != nil { return err } lis, err := activation.GetListener(0, "tcp", "") if err != nil { return err } cfg := shared.CurrentConfig tok, err := open.Token(cfg, tokenName, nil) if err != nil { return err } if err := zhttp.SetupLogging(cfg.Server.LogLevel, cfg.Server.LogFile); err != nil { return fmt.Errorf("configuring logging: %w", err) } log.Logger.UpdateContext(func(c zerolog.Context) zerolog.Context { return c.Str("token", tokenName).Int("pid", os.Getpid()) }) tconf := tok.Config() if tconf.RateLimit != 0 { tok = tokencache.NewLimiter(tok, tconf.RateLimit, tconf.RateBurst) } expiry := time.Second * time.Duration(cfg.Server.TokenCacheSeconds) handler := &handler{ token: tokencache.New(tok, expiry), cookie: cookie, } srv := &http.Server{Handler: handler} wg := new(sync.WaitGroup) handler.shutdown = func() { // keep the main goroutine from exiting until all requests are served wg.Add(1) // notify parent that this process is doomed and to start another one _ = activatecmd.DaemonStopping() // stop accepting requests and wait for ongoing ones to finish _ = srv.Shutdown(context.Background()) wg.Done() } go handler.watchSignals() go handler.healthCheck() _ = activation.DaemonReady() err = srv.Serve(lis) if err == http.ErrServerClosed { err = nil } wg.Wait() // wait for shutdown to finish return err } func (h *handler) watchSignals() { ch := make(chan os.Signal, 1) signal.Notify(ch, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT, ) <-ch signal.Stop(ch) h.shutdown() } relic-7.6.1/config/000077500000000000000000000000001455105530300140715ustar00rootroot00000000000000relic-7.6.1/config/client.go000066400000000000000000000023141455105530300156760ustar00rootroot00000000000000// // Copyright (c) SAS Institute Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package config import "crypto/x509" func (cl *ClientConfig) Match(incoming []*x509.Certificate) (bool, error) { if cl.certs == nil || len(incoming) == 0 { return false, nil } leaf := incoming[0] intermediates := incoming[1:] ipool := x509.NewCertPool() for _, cert := range intermediates { ipool.AddCert(cert) } _, err := leaf.Verify(x509.VerifyOptions{ Roots: cl.certs, Intermediates: ipool, KeyUsages: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth}, }) if err == nil { return true, nil } else if _, ok := err.(x509.UnknownAuthorityError); ok { return false, nil } return false, err } relic-7.6.1/config/config.go000066400000000000000000000245121455105530300156710ustar00rootroot00000000000000// // Copyright (c) SAS Institute Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package config import ( "crypto/x509" "errors" "fmt" "io/ioutil" "strings" "gopkg.in/yaml.v3" "github.com/sassoftware/relic/v7/lib/certloader" ) const ( defaultSigXchg = "relic.signatures" sigKey = "relic.signatures" ) var ( // these are set by main Version = "unknown" Commit = "unknown" Author = "SAS Institute Inc." UserAgent = "relic/" + Version ) type TokenConfig struct { Type string // Provider type: file or pkcs11 (default) Provider string // Path to PKCS#11 provider module (required) Label string // Select a token by label Serial string // Select a token by serial number Pin *string // PIN to use, otherwise will be prompted. Can be empty. (optional) Timeout int // (server) Terminate command after N seconds (default 60) Retries int // (server) Retry failed commands N times (default 5) RateLimit float64 // (server) limit token operations per second RateBurst int // (server) allow burst of operations before limit kicks in User *uint // User argument for PKCS#11 login (optional) UseKeyring bool // Read PIN from system keyring name string } type KeyConfig struct { Token string // Token section to use for this key (linux) Alias string // This is an alias for another key Label string // Select a key by label ID string // Select a key by ID (hex notation) PgpCertificate string // Path to PGP certificate associated with this key X509Certificate string // Path to X.509 certificate associated with this key KeyFile string // For "file" tokens, path to the private key IsPkcs12 bool // If true, key file contains PKCS#12 key and certificate chain Roles []string // List of user roles that can use this key Timestamp bool // If true, attach a timestamped countersignature when possible Hide bool // If true, then omit this key from 'remote list-keys' name string token *TokenConfig } type ServerConfig struct { Listen string // Port to listen for TLS connections ListenHTTP string // Port to listen for plaintext connections KeyFile string // Path to TLS key file CertFile string // Path to TLS certificate chain LogFile string // Optional error log LogLevel string // Optional log level PolicyURL string // Optional open-policy-agent endpoint Disabled bool // Always return 503 Service Unavailable ListenDebug bool // Serve debug info on an alternate port ListenMetrics string // Port to listen for plaintext metrics NumWorkers int // Number of worker subprocesses per configured token TokenCheckInterval int TokenCheckFailures int TokenCheckTimeout int TokenCacheSeconds int ReadHeaderTimeout int ReadTimeout int WriteTimeout int // URLs to all servers in the cluster. If a client uses DirectoryURL to // point to this server (or a load balancer), then we will give them these // URLs as a means to distribute load without needing a middle-box. Siblings []string // IP networks of trusted reverse proxies that can front this service TrustedProxies []string AzureAD *ServerAzureConfig } type ServerAzureConfig struct { Authority string ClientID string Scopes []string } type ClientConfig struct { Nickname string // Name that appears in audit log entries Roles []string // List of roles that this client possesses Certificate string // Optional CA certificate(s) that sign client certs instead of using fingerprint-based auth certs *x509.CertPool } type RemoteConfig struct { URL string `yaml:",omitempty"` // URL of remote server DirectoryURL string `yaml:",omitempty"` // URL of directory server KeyFile string `yaml:",omitempty"` // Path to TLS client key file CertFile string `yaml:",omitempty"` // Path to TLS client certificate or embedded certificate CaCert string `yaml:",omitempty"` // Path to CA certificate or embedded certificate ConnectTimeout int `yaml:",omitempty"` // Connection timeout in seconds Retries int `yaml:",omitempty"` // Attempt an operation (at least) N times AccessToken string `yaml:"-"` Interactive bool } type TimestampConfig struct { URLs []string // List of timestamp server URLs MsURLs []string // List of microsoft-style URLs Timeout int // Connect timeout in seconds CaCert string // Path to CA certificate Memcache []string // host:port of memcached to use for caching timestamps RateLimit float64 // limit timestamp requests per second RateBurst int // allow burst of requests before limit kicks in } type AmqpConfig struct { URL string // AMQP URL to report signatures to i.e. amqp://user:password@host CaCert string KeyFile string CertFile string SigsXchg string // Name of exchange to send to (default relic.signatures) } type Config struct { Tokens map[string]*TokenConfig `yaml:",omitempty"` Keys map[string]*KeyConfig `yaml:",omitempty"` Server *ServerConfig `yaml:",omitempty"` Clients map[string]*ClientConfig `yaml:",omitempty"` Remote *RemoteConfig `yaml:",omitempty"` Timestamp *TimestampConfig `yaml:",omitempty"` Amqp *AmqpConfig `yaml:",omitempty"` PinFile string `yaml:",omitempty"` // Optional YAML file with additional token PINs path string } func ReadFile(path string) (*Config, error) { data, err := ioutil.ReadFile(path) if err != nil { return nil, err } config := new(Config) err = yaml.Unmarshal(data, config) if err != nil { return nil, err } return config, config.Normalize(path) } func (config *Config) Normalize(path string) error { config.path = path normalized := make(map[string]*ClientConfig) for fingerprint, client := range config.Clients { if client.Certificate != "" { certs, err := certloader.ParseX509Certificates([]byte(client.Certificate)) if err != nil { return fmt.Errorf("invalid certificate for client %s: %w", fingerprint, err) } client.certs = x509.NewCertPool() for _, cert := range certs { client.certs.AddCert(cert) } } else if len(fingerprint) != 64 { return errors.New("Client keys must be hex-encoded SHA256 digests of the public key") } lower := strings.ToLower(fingerprint) normalized[lower] = client } config.Clients = normalized if config.PinFile != "" { contents, err := ioutil.ReadFile(config.PinFile) if err != nil { return fmt.Errorf("reading PinFile: %w", err) } pinMap := make(map[string]string) if err := yaml.Unmarshal(contents, pinMap); err != nil { return fmt.Errorf("reading PinFile: %w", err) } for token, pin := range pinMap { tokenConf := config.Tokens[token] if tokenConf != nil { ppin := pin tokenConf.Pin = &ppin } } } for tokenName, tokenConf := range config.Tokens { tokenConf.name = tokenName if tokenConf.Type == "" { tokenConf.Type = "pkcs11" } } for keyName, keyConf := range config.Keys { keyConf.name = keyName if keyConf.Token != "" { keyConf.token = config.Tokens[keyConf.Token] } } if s := config.Server; s != nil { if s.TokenCheckInterval == 0 { s.TokenCheckInterval = 60 } if s.TokenCheckTimeout == 0 { s.TokenCheckTimeout = 60 } if s.TokenCheckFailures == 0 { s.TokenCheckFailures = 3 } if s.TokenCacheSeconds == 0 { s.TokenCacheSeconds = 600 } if s.ReadHeaderTimeout == 0 { s.ReadHeaderTimeout = 10 } if s.ReadTimeout == 0 { s.ReadTimeout = 600 } if s.WriteTimeout == 0 { s.WriteTimeout = 600 } } if r := config.Remote; r != nil { if r.ConnectTimeout == 0 { r.ConnectTimeout = 15 } if r.Retries == 0 { r.Retries = 3 } } return nil } func (config *Config) GetToken(tokenName string) (*TokenConfig, error) { if config.Tokens == nil { return nil, errors.New("No tokens defined in configuration") } tokenConf, ok := config.Tokens[tokenName] if !ok { return nil, fmt.Errorf("Token \"%s\" not found in configuration", tokenName) } return tokenConf, nil } func (config *Config) NewToken(name string) *TokenConfig { if config.Tokens == nil { config.Tokens = make(map[string]*TokenConfig) } tok := &TokenConfig{name: name} config.Tokens[name] = tok return tok } func (config *Config) GetKey(keyName string) (*KeyConfig, error) { keyConf, ok := config.Keys[keyName] if !ok { return nil, fmt.Errorf("Key \"%s\" not found in configuration", keyName) } else if keyConf.Alias != "" { keyConf, ok = config.Keys[keyConf.Alias] if !ok { return nil, fmt.Errorf("Alias \"%s\" points to undefined key \"%s\"", keyName, keyConf.Alias) } } if keyConf.Token == "" { return nil, fmt.Errorf("Key \"%s\" does not specify required value 'token'", keyName) } return keyConf, nil } func (config *Config) NewKey(name string) *KeyConfig { if config.Keys == nil { config.Keys = make(map[string]*KeyConfig) } key := &KeyConfig{name: name} config.Keys[name] = key return key } func (config *Config) Path() string { return config.path } func (config *Config) GetTimestampConfig() (*TimestampConfig, error) { tconf := config.Timestamp if tconf == nil { return nil, errors.New("No timestamp section exists in the configuration") } return tconf, nil } // ListServedTokens returns a list of token names that are accessible by at least one role func (config *Config) ListServedTokens() []string { names := make(map[string]bool) for _, key := range config.Keys { if len(key.Roles) != 0 { names[key.Token] = true } } ret := make([]string, 0, len(names)) for name := range names { ret = append(ret, name) } return ret } func (tconf *TokenConfig) Name() string { return tconf.name } func (aconf *AmqpConfig) ExchangeName() string { if aconf.SigsXchg != "" { return aconf.SigsXchg } return defaultSigXchg } func (aconf *AmqpConfig) RoutingKey() string { return sigKey } relic-7.6.1/config/default.go000066400000000000000000000017331455105530300160500ustar00rootroot00000000000000// // Copyright (c) SAS Institute Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package config import ( "os" "path/filepath" ) func DefaultConfig() string { fp := os.Getenv("RELIC_CFG") if fp != "" { return fp } profile := os.Getenv("APPDATA") if profile != "" { // windows return filepath.Join(profile, "relic", "relic.yml") } home := os.Getenv("HOME") if home != "" { return filepath.Join(home, ".config", "relic", "relic.yml") } return "" } relic-7.6.1/config/env.go000066400000000000000000000026741455105530300152210ustar00rootroot00000000000000// // Copyright (c) SAS Institute Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package config import ( "errors" "os" ) // FromEnvironment tries to build a client-only config from environment variables. If none are set then returns nil. func FromEnvironment() (*Config, error) { remoteURL := os.Getenv("RELIC_URL") if remoteURL == "" { return nil, nil } clientCert := os.Getenv("RELIC_CLIENT_CERT") clientKey := os.Getenv("RELIC_CLIENT_KEY") accessToken := os.Getenv("RELIC_ACCESS_TOKEN") if clientCert == "" && accessToken == "" { return nil, errors.New("RELIC_CLIENT_CERT or RELIC_ACCESS_TOKEN must be set when RELIC_URL is set") } if clientKey == "" { clientKey = clientCert } cfg := &Config{ Remote: &RemoteConfig{ DirectoryURL: remoteURL, KeyFile: clientKey, CertFile: clientCert, AccessToken: accessToken, CaCert: os.Getenv("RELIC_CACERT"), }, } return cfg, cfg.Normalize("") } relic-7.6.1/config/key.go000066400000000000000000000020461455105530300152120ustar00rootroot00000000000000// // Copyright (c) SAS Institute Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package config import "time" const defaultTimeout = 60 * time.Second func (keyConf *KeyConfig) Name() string { return keyConf.name } func (keyConf *KeyConfig) GetTimeout() time.Duration { if keyConf.token != nil && keyConf.token.Timeout != 0 { return time.Second * time.Duration(keyConf.token.Timeout) } return defaultTimeout } func (keyConf *KeyConfig) SetToken(tokenConf *TokenConfig) { keyConf.Token = tokenConf.name keyConf.token = tokenConf } relic-7.6.1/distro/000077500000000000000000000000001455105530300141305ustar00rootroot00000000000000relic-7.6.1/distro/linux/000077500000000000000000000000001455105530300152675ustar00rootroot00000000000000relic-7.6.1/distro/linux/audit.yml000066400000000000000000000001601455105530300171150ustar00rootroot00000000000000--- ConfigDir: /etc/relic/audit.d DatabaseUri: host=/var/run/postgresql LogFile: /var/log/relic-audit/audit.log relic-7.6.1/distro/linux/logrotate.conf000066400000000000000000000002521455105530300201350ustar00rootroot00000000000000/var/log/relic/server.log { missingok nocreate sharedscripts postrotate /usr/libexec/relic-einhorn reopenlogs >/dev/null 2>&1 ||: endscript } relic-7.6.1/distro/linux/relic-audit.service000066400000000000000000000004401455105530300210510ustar00rootroot00000000000000[Unit] Description=Relic package signing auditor Wants=postgresql.service After=postgresql.service [Service] User=relic-audit Group=relic-audit Type=notify WorkingDirectory=/ ExecStart=/usr/bin/relic audit -c /etc/relic/audit.yml Restart=on-failure [Install] WantedBy=multi-user.target relic-7.6.1/distro/linux/relic-einhorn000077500000000000000000000026351455105530300177610ustar00rootroot00000000000000#!/bin/bash # # Copyright (c) SAS Institute Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # PATH=/usr/local/bin:$PATH cfg=/etc/relic/relic.yml sock=/run/relic/einhorn.sock check() { /usr/bin/relic -c $cfg serve --test || exit $? } case "$1" in start) check exec einhorn \ -c relic \ -d $sock \ -e /run/relic/einhorn.pid \ -f /run/relic/einhorn.lock \ -m manual \ -q \ -b 0.0.0.0:6300,r \ -b 0.0.0.0:6301,r \ -- \ /usr/bin/relic -c $cfg serve ;; stop) [ -e $sock ] || exit 0 exec einhornsh -d $sock -e die ;; reload) check exec einhornsh -d $sock -e upgrade ;; reopenlogs) [ -e $sock ] || exit 0 exec einhornsh -d $sock -e "signal USR1" ;; *) echo invalid command exit 1 esac relic-7.6.1/distro/linux/relic.service000066400000000000000000000005531455105530300177520ustar00rootroot00000000000000[Unit] Description=Relic package signing service [Service] User=relic Group=relic Type=notify NotifyAccess=all WorkingDirectory=/ RuntimeDirectory=relic ExecStart=/usr/libexec/relic-einhorn start ExecStop=/usr/libexec/relic-einhorn stop ExecReload=/usr/libexec/relic-einhorn reload SyslogIdentifier=relic Restart=on-failure [Install] WantedBy=multi-user.target relic-7.6.1/distro/linux/relic.spec000066400000000000000000000070061455105530300172440ustar00rootroot00000000000000Name: relic Version: 1.0 Release: 1%{?dist} Summary: Client to the relic secure package signing service License: Apache 2.0 URL: http://github.com/sassoftware/relic Source0: relic.tar Group: Utilities/File BuildArch: x86_64 %package server Requires: %{name} = %{version}-%{release} Requires: rubygem-einhorn Summary: Secure package signing service Group: Utilities/File %package audit Requires: %{name} = %{version}-%{release} Summary: Audit client for relic Group: Utilities/File %define confdir %{_sysconfdir}/relic %define systemddir %{_prefix}/lib/systemd/system %description Relic is a service for signing RPMs and other package types using a PKCS#11 Hardware Security Module (HSM) or other token. It also includes functions for creating keys, manipulating tokens, and a client for accessing a remote signing server. %description server This package contains the relic signing service and startup files. %description audit relic-audit subscribes to audit events on one or more AMQP message brokers and saves records to a database or log file. %prep %autosetup %install mkdir -p %{buildroot}%{systemddir} mkdir -p %{buildroot}%{confdir}/{certs,server,audit.d} mkdir -p %{buildroot}%{_localstatedir}/log/relic{,-audit} install -D relic %{buildroot}%{_bindir}/relic install -D relic-einhorn %{buildroot}%{_libexecdir}/relic-einhorn install -D relic.yml %{buildroot}%{confdir}/relic.yml install -D audit.yml %{buildroot}%{confdir}/audit.yml install -D logrotate.conf %{buildroot}%{_sysconfdir}/logrotate.d/relic install relic.service relic-audit.service %{buildroot}%{systemddir}/ %clean rm -rf %{buildroot} %files %attr(0755,root,root) %{_bindir}/relic %attr(0755,root,root) %dir %{confdir} %files server %attr(0755,root,root) %{_libexecdir}/relic-einhorn %attr(0644,root,root) %{systemddir}/relic.service %attr(0755,root,root) %dir %{confdir}/certs %attr(0640,root,relic) %config(noreplace) %{confdir}/relic.yml %attr(0750,root,relic) %dir %{confdir}/server %attr(0750,relic,relic) %dir %{_localstatedir}/log/relic %attr(0644,root,root) %{_sysconfdir}/logrotate.d/relic %files audit %attr(0644,root,root) %{systemddir}/relic-audit.service %attr(0755,root,root) %dir %{confdir}/audit.d %attr(0644,root,root) %config(noreplace) %{confdir}/audit.yml %attr(0750,relic-audit,relic-audit) %dir %{_localstatedir}/log/relic-audit %changelog %pre server getent group relic >/dev/null || groupadd -r relic getent passwd relic >/dev/null || useradd -r -g relic \ -d / relic -s /sbin/nologin -c "relic package signing service" %pre audit getent group relic-audit >/dev/null || groupadd -r relic-audit getent passwd relic-audit >/dev/null || useradd -r -g relic-audit \ -d / relic-audit -s /sbin/nologin -c "relic audit service" %post server /bin/systemctl daemon-reload %post audit /bin/systemctl daemon-reload %preun server if [ $1 -eq 0 ] ; then # removal, not upgrade systemctl --no-reload disable --now relic.service > /dev/null 2>&1 || : fi %preun audit if [ $1 -eq 0 ] ; then systemctl --no-reload disable --now relic-audit.service > /dev/null 2>&1 || : fi %postun server if [ $1 -ge 1 ] ; then # upgrade, not removal # try-reload not available on centos 7 unfortunately if systemctl -q is-active relic.service; then systemctl reload relic.service >/dev/null 2>&1 || : fi fi %postun audit if [ $1 -ge 1 ] ; then # upgrade, not removal systemctl try-restart relic-audit.service >/dev/null 2>&1 || : fi relic-7.6.1/distro/linux/rubygem-einhorn.spec000066400000000000000000000030641455105530300212600ustar00rootroot00000000000000%global gem_name einhorn %if 0%{?rhel} == 6 %global gem_dir %(ruby -rubygems -e 'puts Gem::dir' 2>/dev/null) %global gem_docdir %{gem_dir}/doc/%{gem_name}-%{version} %global gem_cache %{gem_dir}/cache/%{gem_name}-%{version}.gem %global gem_spec %{gem_dir}/specifications/%{gem_name}-%{version}.gemspec %global gem_instdir %{gem_dir}/gems/%{gem_name}-%{version} %endif Summary: Language-independent shared socket manager Name: rubygem-%{gem_name} Version: 0.7.5 Release: 1%{?dist} Group: Development/Languages License: MIT URL: https://github.com/stripe/einhorn Source0: https://github.com/stripe/einhorn/archive/v%{version}.tar.gz %if 0%{?rhel} == 6 Requires: ruby(abi) = 1.8 %else Requires: ruby(release) %endif %if 0%{?fedora} BuildRequires: rubygems-devel %endif BuildRequires: rubygems BuildArch: noarch Provides: rubygem(%{gem_name}) = %{version} %description Einhorn makes it easy to run (and keep alive) multiple copies of a single long-lived process. If that process is a server listening on some socket, Einhorn will open the socket in the master process so that it's shared among the workers. %prep %setup -n %{gem_name}-%{version} tar -tzf %{SOURCE0} |cut -d/ -f2- |grep -v /$ >files sed -i -e 's/git ls-files/cat files/' %{gem_name}.gemspec %build gem build %{gem_name}.gemspec %gem_install %install mkdir -p %{buildroot}%{gem_dir} cp -a ./%{gem_dir}/* %{buildroot}%{gem_dir}/ mkdir -p %{buildroot}%{_bindir} cp -a ./%{_bindir}/* %{buildroot}%{_bindir} %files %{_bindir}/einhorn %{_bindir}/einhornsh %exclude %{gem_cache} %doc %{gem_docdir} %{gem_instdir} %{gem_spec} relic-7.6.1/distro/linux/scdaemon@.service000066400000000000000000000004121455105530300205370ustar00rootroot00000000000000[Unit] Description=GnuPG SmartCard Daemon Before=relic.service [Service] Type=forking Environment=GNUPGHOME=%t/scdaemon/%I ExecStartPre=/bin/mkdir -p $GNUPGHOME ExecStart=/usr/libexec/scdaemon --daemon --quiet --reader-port %I [Install] WantedBy=multi-user.target relic-7.6.1/distro/linux/socket-activation/000077500000000000000000000000001455105530300207165ustar00rootroot00000000000000relic-7.6.1/distro/linux/socket-activation/relic.service000066400000000000000000000004071455105530300233770ustar00rootroot00000000000000[Unit] Description=Relic package signing service Requires=relic.socket [Service] User=relic Group=relic Type=notify WorkingDirectory=/ ExecStart=/usr/bin/relic -c /etc/relic/relic.yml serve KillSignal=QUIT [Install] Also=relic.socket WantedBy=multi-user.target relic-7.6.1/distro/linux/socket-activation/relic.socket000066400000000000000000000002021455105530300232200ustar00rootroot00000000000000[Unit] Description=Relic package signing service [Socket] ListenStream=6300 ListenStream=6301 [Install] WantedBy=sockets.target relic-7.6.1/doc/000077500000000000000000000000001455105530300133715ustar00rootroot00000000000000relic-7.6.1/doc/android.md000066400000000000000000000013311455105530300153310ustar00rootroot00000000000000# Signing Android packages Android presently has two types of signature. Version 1 signatures are simply standard JAR signatures. Version 2 is Android-specific and can be applied to a V1 signed package. In order to prevent a downgrade attack by stripping the V2 signature, an additional header is inserted into the V1 signature which will indicate to V2-capable verifiers that a V2 signature must be present. To create a dual-version APK signature with relic, first create the JAR signature then the APK signature: relic sign -k mykey -f mypackage.apk -T jar --apk-v2-present relic sign -k mykey -f mypackage.apk For more information on Android package signing, see: https://source.android.com/security/apksigning/v2 relic-7.6.1/doc/audit.psql000066400000000000000000000004151455105530300154000ustar00rootroot00000000000000CREATE TABLE signatures ( signature_id bigserial PRIMARY KEY, "timestamp" timestamptz, client_name text, client_ip text, client_dn text, client_filename text, sig_hostname text, sig_type text, sig_keyname text, attributes text); relic-7.6.1/doc/azure.md000066400000000000000000000044161455105530300150460ustar00rootroot00000000000000# Using Relic with Azure Key Vault Relic can use keys and certificates stored in an Azure Key Vault to sign. ## Keys Relic can use either certificates or keys not associated with a certificate. If a key is used, the ID of a single key version must be provided. For a certificate, the ID may be provided with or without a version. In the latter case, the latest version will be selected and cached until relic is reloaded. For example, these are all valid key configurations: ```yaml id: https://example.vault.azure.net/keys/my-azure-key/00112233445566778899aabbccddeeff id: https://example.vault.azure.net/certificates/my-azure-key id: https://example.vault.azure.net/certificates/my-azure-key/00112233445566778899aabbccddeeff ``` If a certificate ID is provided and `x509certificate` is not set, then the certificate will be loaded from Azure Key Vault. Relic does not yet support generating or importing keys or certificates into Azure. ## Authentication Authenticating to Azure can be accomplished in all the usual ways. This includes using secrets, certificates, managed service identity, the Azure CLI, or via a federated identity token. If no explicit configuration is provided to relic then azure will be configured via the process environment. The default set of accepted environment variables can be found [here](https://pkg.go.dev/github.com/Azure/go-autorest/autorest/azure/auth#pkg-constants). For interactive use, configuring relic to use Azure CLI auth is convenient. Setting pin to an empty string will enable this: ```yaml tokens: azure: type: azure pin: "" ``` If modifying the environment is not desireable, or more than one account is to be used, the pin option can be used to point to a JSON file with credentials or other azure-sdk settings: ```yaml tokens: azure: type: azure pin: /etc/relic/mycred.json ``` Finally, in a Kubernetes or GitHub Actions environment, a native service token can be used to authenticate using Workload Identity Federation. To use this, set `AZURE_BEARER_TOKEN_FILE` to a file containing the token, and also set `AZURE_CLIENT_ID` and `AZURE_TENANT_ID` to their respective values. See the [workload identity federation doc](https://docs.microsoft.com/en-us/azure/active-directory/develop/workload-identity-federation) for more details.relic-7.6.1/doc/macos.md000066400000000000000000000017621455105530300150230ustar00rootroot00000000000000# Signing MacOS binaries relic has preliminary support for signing MacOS and iOS binaries. For example, to sign a Mac binary for offline distribution, you will need a DevID certificate. Configure relic to use the cert with Apple's timestamp servers: ```yaml keys: devid: token: file x509certificate: ./devid.crt keyfile: ./devid.key timestamp: true tokens: file: type: file timestamp: urls: - http://timestamp.apple.com/ts01 ``` Binaries should be signed with the `hardened-runtime` flag is set or notarization will fail: relic sign -k devid -f foo-darwin-amd64 --hardened-runtime Note also that relic currently does not support signing multi-arch ("fat") binaries, although it can verify them. Sign each arch separately and then combine them afterwards: go install github.com/randall77/makefat@latest makefat foo foo-darwin-amd64 foo-darwin-arm64 relic verify foo The signed binary can then be placed into a regular zip file and uploaded for notarization.relic-7.6.1/doc/opa.md000066400000000000000000000060551455105530300145000ustar00rootroot00000000000000# Open Policy Agent relic can optionally replace its builtin certificate-based authentication with [Open Policy Agent](https://www.openpolicyagent.org/docs/latest/). OPA is a flexible policy engine that evaluates centrally-distributed, user-defined rules from a sidecar daemon. relic can use this to make certificate-based auth more dynamic and CI/CD-friendly, to add support for Azure AD and other token-based authentication, or both. To enable OPA in relic, set a URL to the policy to evaluate in the server configuration: ```yaml server: policyurl: http://127.0.0.1/v1/data/relic ``` policyurl can point either at a specific package, or at the root of the server to use the default decision. relic will provide the following inputs to the policy: - token - The Bearer token provided in the `Authorization` header, if provided - fingerprint - Digest of the client's leaf certificate, if provided - path - Path from the URL being accessed - query - Query parameters from the URL being accessed The policy may provide the following fields in return: - allow - true if the request should proceed - sub - a string describing the subject or user that authenticated - claims - a map of further key-value pairs describing the authenticated user - errors - an array of strings indicating any errors explaining why the request was denied - roles - an array of strings specifying which key roles the user may access - allowed_keys - an array of strings specifying individual named keys the user may access A successful request must set `allow`, `sub` and either `roles` or `allowed_keys` depending on the desired auth model. A key can be used if the key's name is listed in `allowed_keys`, or there is an intersection between the `roles` response and the `roles` in the key's configuration in the relic server configuration, or both. ## Interactive authentication A server with OPA enabled may provide metadata that allows the client to interactively authenticate to Azure AD and then provide the resulting token to relic. To enable this, first create or update an Azure AD application with the following under the "Authentication" tab: - In the "Mobile and desktop applications" box, add `http://localhost` as a permitted Redirect URI. - In the "Advanced settings" box, ensure "Allow public client flows" is enabled Then in the relic server configuration add: ```yaml server: azuread: authority: https://login.microsoftonline.com/YOUR-TENANT-ID clientid: YOUR-APP-CLIENT-ID ``` You can find the tenant ID and client ID on the "Overview" tab of your app registration. The full authority URL can be found by clicking the "Endpoints" button at the top and removing everything after the tenant ID from any of the displayed OAuth URLs. Finally, to configure a client to use interactive auth run: relic remote login -u https://relic.example.com ## Verifying OpenID Connect tokens To get started with writing a policy that can verify OpenID Connect tokens such as those from interactive command-line login, see [the OPA documentation](https://www.openpolicyagent.org/docs/latest/oauth-oidc/). relic-7.6.1/doc/pgpcard.md000066400000000000000000000042021455105530300153310ustar00rootroot00000000000000# Using OpenPGP cards with Relic relic can use GnuPG scdaemon to interface with OpenPGP cards and other types of smartcard supported by GnuPG. For example, the YubiKey NEO is an inexpensive way to safeguard a RSA key for code signing purposes. To do this, you will first need to generate a private key in the token. This can be done using the `gpg2 --edit-card` interface and `generate` command, or by generating a key in software and moving it to the card. It is recommended to make a backup of the private key in a safe location and/or to generate a revocation certificate at the time the key is generated. Once this has been done, relic can use the private key. Either the PGP certificate created by GnuPG can be used, or one or more additional PGP or X509 certificates can be created by relic using the private key on the card. relic only ever uses the private key, so it does not matter whether the configured certificate is the one originally generated by GnuPG as long as the public key is the same. relic accesses the PGP card by connecting to GnuPG scdaemon over a UNIX socket. A sample systemd unit file `scdaemon@.service` is provided for starting scdaemon. For example, to start a scdaemon to access the first card on the system: systemctl enable --now scdaemon@0.service This will create a UNIX socket at `/run/scdaemon/0/S.scdaemon` which relic can connect to. Place the following in your `relic.yml` in order to access it: tokens: scd0: type: scdaemon provider: /run/scdaemon/0/S.scdaemon pin: 123456 keys: mykey: token: scd0 id: OPENPGP.1 pgpcertificate: certs/mykey.pgp Either place the corresponding PGP certificate in the configured location, or make a new one using the existing private key by running: relic -c relic.yml pgp-generate -k mykey -n "My Key" >certs/mykey.pgp Now you can create signatures: echo hello world | relic -c relic.yml sign-pgp -u mykey --clearsign Information about the IDs of keys in the token, and the serial number of the token itself, can be displayed by running: relic -c relic.yml token contents -t scd0 relic-7.6.1/doc/relic.yml000066400000000000000000000225011455105530300152120ustar00rootroot00000000000000--- # Tokens on which signing keys can be found. Each configured key refers to a token by name. tokens: # Use a PKCS#11 library as a token mytoken: # Full path to provider library provider: /usr/lib64/softhsm/libsofthsm.so # Optional selectors to pick a token from those the provider offers label: alpha serial: 99999 # PIN is optional for command-line use, but required for servers. See also 'pinfile'. pin: 123456 #pin: "" # blank PIN, without prompting # If true, try to save the PIN in the system keyring (command-line only) #usekeyring: false # Optional login user. Useful values: # 0 - CKU_SO # 1 - CKU_USER (default) # 2 - CKU_CONTEXT_SPECIFIC, SafeNet: CKU_AUDIT # 0x80000001 - SafeNet: CKU_LIMITED_USER #user: 1 # Optional parameters for server mode #timeout: 60 # Terminate each attempt after N seconds (default: 60) #retries: 5 # Retry failed commands N times (default: 5) #ratelimit: 10 # Limit token operations per second #rateburst: 10 # Allow burst of requests before limit kicks in # Use GnuPG scdaemon as a token myscd: type: scdaemon # Optionally specify the scdaemon socket path. If not provided then the # default will be used. provider: /run/myscd/S.scdaemon # Optional serial number of the expected card. serial: D99999999999999999999 # PIN is optional for command-line use, but required for servers. See also 'pinfile'. pin: 123456 # Use private key files as a "token". The path to the key is specified in the key section(s) file: type: file # If the private key is protected with a password, specify it here pin: password # Use keys stored in Google Cloud Key Management Service gcloud: type: gcloud # Optionally configure a credential file. If not specified then the default # environment is used. #pin: service-account.json # Use keys stored in Azure Key Vault azurekv: type: azure # Optionally configure a service principal file #pin: service-principal.auth # Or use CLI authentication #pin: "" # Otherwise the environment will be used # Use CMKs stored in AWS Key Management Service aws: type: aws # Presently, configuration must be done via the standard SDK env vars # Keys that can be used for signing keys: my_token_key: # Which token, defined above, to find the key on token: mytoken # Optional selectors to pick a key from those in the token # CKA_LABEL: label: "label" # CKA_ID: id: 00112233 # Path to a PGP certificate, if PGP signing is desired. Can be ascii-armored or binary. pgpcertificate: ./keys/rsa1.pub # Path to a X509 certificate, if X509 signing is desired. Can be PEM, DER, # or PKCS#7 (p7b) format, with optional certificate chain. x509certificate: ./keys/rsa1.cer # true if a RFC 3161 timestamp should be attached, see 'timestamp' below timestamp: false # Clients with any of these roles can utilize this key roles: ['somegroup'] my_scd_key: token: myscd # Specify which key to use. For OpenPGP cards this will be either OPENPGP.1 or OPENPGP.3. id: OPENPGP.1 # Same options as above: pgpcertificate, x509certificate, timestamp, roles my_file_key: token: file # Path to the private key file. The password is specified in the token configuration above. keyfile: ./keys/rsa1.key # true if key file contains PKCS#12 key and certificate chain ispkcs12: false # Same options as above: pgpcertificate, x509certificate, timestamp, roles my_gcloud_key: token: gcloud # Fully-qualified name of a key version resource. Must point to a key version, not a key. id: projects/root-opus-123456/locations/us-east1/keyRings/my-keyring/cryptoKeys/my-gloud-key/cryptoKeyVersions/1 # Same options as above: pgpcertificate, x509certificate, timestamp, roles my_azure_key: token: azurekv # URL of key version resource. Must point to a key version, not a key. id: https://example.vault.azure.net/keys/my-azure-key/00112233445566778899aabbccddeeff # Alternately, point to a certificate or certificate version. In this case # x509certificate may be omitted to load the cert from key vault as well. #id: https://example.vault.azure.net/certificates/my-azure-key # Same options as above: pgpcertificate, x509certificate, timestamp, roles my_aws_key: token: aws # ID or ARN of an asymmetric CMK id: arn:aws:kms:us-east-1:111111111111:key/22222222-3333-4444-5555-666666666666 # Same options as above: pgpcertificate, x509certificate, timestamp, roles aliased_key: # When alias is set, this key name becomes an alias for the other key. # Alises cannot override any parameters of the key, including roles. alias: my_token_key # Server-specific configuration server: # What port to listen on. Defaults to :6300. # Socket activation via systemd is also supported, in which case this is ignored. listen: ":6300" # Listen for non-secure connections. This is useful for health checks and/or # if clients connect via a trusted reverse proxy. Default is none. listenhttp: ":6301" # Private key for server TLS. PEM format, RSA or ECDSA keyfile: /etc/relic/server/server.key # X.509 certificate for server TLS. PEM format. If a cert chain is needed it # should follow the main cert. certfile: /etc/relic/server/server.key # Optional logfile for server errors. If not set, then standard error is used logfile: /var/log/relic/server.log # How many worker subprocesses to spawn per token. Usually only 1 is required. #numworkers: 1 # Set the frequency and tolerance of token health checks #tokencheckinterval: 60 # ping the token every N seconds #tokenchecktimeout: 30 # fail a ping if it is stuck for N seconds #tokencheckfailures: 3 # the server will report "not healthy" after N failed pings #tokencacheseconds: 600 # cache key/cert info from token # Optional list of URLs that are part of a cluster of servers. If set clients # will connect directly to one of these servers at random, otherwise they # will connect to their originally configured URL. #siblings: #- https://relic1:6300 #- https://relic2:6300 # Optionally utilize Open Policy Agent to authenticate and authorize requests # instead of the builtin client certificate verification. # See [opa.md](./opa.md) for details. #policyurl: http://127.0.0.1:8181/v1/data/relic # If policyurl is set, optionally specify an Azure AD tenant which will be # provided to clients that wish to perform interactive token authentication. #azuread: # authority: https://login.microsoftonline.com/00000000-1111-2222-3333-444444444444 # clientid: 55555555-6666-7777-8888-999999999999 # If fronted by a trusted reverse proxy, list the IP(s) and IP network(s) of # the proxy here. The X-Forwarded-{For,Proto,Host} and Ssl-Client-Certificate # headers will be respected when connections come from one of these IPs. #trustedproxies: #- 127.0.0.1 #- 10.0.0.0/30 # Instead of including token PINs in this file, you can specify an alternate # "pin file" which is a YAML file holding key-value pairs where the key is the # name of the token and the value is the PIN. #pinfile: /etc/relic/pin.yaml # Configure trusted timestamping servers, used by keys that have timestamping # enabled when using a signature type that supports it. timestamp: # RFC 3161 timestamp server(s). If more than one is provided then they will # be tried in the order given until one succeeds. urls: - http://mytimestamp.server/rfc3161 # Non-RFC3161 timestamp server(s), used for appmanifest only msurls: - http://mytimestamp.server # Optional timeout for each timestamp request timeout: 60 # Optional alternate CA certificate file for contacting timestamp servers # cacert: /etc/pki/tls/mychain.pem # Optional memcache servers for memoizing timestamp requests #memcache: # - 127.0.0.1:11211 # Optional rate limit for timestamp requests #ratelimit: 1 # requests per second #rateburst: 10 # burst capacity # AMQP broker used to submit audit logs amqp: # Optional audit logging to an AMQP broker #url: amqp://guest:guest@127.0.0.1:5672 # Optional TLS parameters #cacert: #keyfile: #certfile: # "fanout" type exchange to send audit messages to, default relic.signatures #sigsXchg: relic.signatures # Authentication to the server is via client certificate. Certificates are # identified by their fingerprint. Fingerprints can be obtained by using the # "relic remote register" command on the client to generate the key, or by # checking the server error log after attempting to connect. clients: E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855: # Display name for this client nickname: myuser # List of roles this user possesses. Must contain at least one of the roles # on a key for the user to access that key. roles: ['somegroup'] # Alternately, clients can be authenticated using one or more CA # certificates. The CA that the client matches determines the roles they have # access to, and the subject DN of the leaf certificate is logged. This can # be used to delegate access to a short-lived build process. #my_root_ca: # nickname: my_root_ca # certificate: | # -----BEGIN CERTIFICATE----- # asdfasdfasdf # -----END CERTIFICATE----- # roles: ['somegroup'] relic-7.6.1/functest/000077500000000000000000000000001455105530300144575ustar00rootroot00000000000000relic-7.6.1/functest/.gitignore000066400000000000000000000000101455105530300164360ustar00rootroot00000000000000scratch relic-7.6.1/functest/functest.sh000077500000000000000000000137131455105530300166560ustar00rootroot00000000000000#!/bin/bash # # Copyright (c) SAS Institute Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # cd $(dirname $0) set -ex srcdir=$(pwd -P) rm -rf scratch mkdir -p scratch/token export SOFTHSM2_CONF=./token.conf softhsm2-util --slot=0 --init-token --label=functest --pin=123456 --so-pin=12345678 GOOS=$(go env GOOS) GOARCH=$(go env GOARCH) if [ -e ../build/relic-$GOOS-$GOARCH ] then client=../build/relic-$GOOS-$GOARCH else client=relic fi relic="$client -c ./testconf.yml" verify_2048p="$client verify --cert testkeys/rsa2048.pgp" verify_2048x="$client verify --cert testkeys/rsa2048.crt" $relic import-key -k rsa2048 -f testkeys/rsa2048.key $relic serve & spid=$! trap "kill $spid" EXIT INT QUIT TERM signed=scratch/signed mkdir -p $signed echo set +x for x in {1..100} do curl -skf https://localhost:6363/health && break if [ i == 100 ] then echo server failed to start exit 1 fi sleep 0.1 done set -x ### RPM pkg="zlib-1.2.8-10.fc24.i686.rpm" $client verify --cert "testkeys/RPM-GPG-KEY-fedora-25-i386" "packages/$pkg" $relic remote sign -k rsa2048 -f "packages/$pkg" -o "$signed/$pkg" $client verify "$signed/$pkg" 2>/dev/null && { echo expected an error; exit 1; } $verify_2048p "$signed/$pkg" echo ### DEB pkg="zlib1g_1.2.8.dfsg-5_i386.deb" $relic remote sign -k rsa2048 -f "packages/$pkg" -o "$signed/$pkg" $client verify "$signed/$pkg" 2>/dev/null && { echo expected an error; exit 1; } $verify_2048p "$signed/$pkg" echo ### PGP $client verify "packages/InRelease" 2>/dev/null && { echo expected an error; exit 1; } $client verify --cert "testkeys/ubuntu2012.pgp" "packages/InRelease" $client verify "packages/Release.gpg" --content "packages/Release" 2>/dev/null && { echo expected an error; exit 1; } $client verify --cert "testkeys/ubuntu2012.pgp" "packages/Release.gpg" --content "packages/Release" $relic remote sign-pgp -u rsa2048 -ba "packages/Release" -o "$signed/Release.gpg" $verify_2048p "$signed/Release.gpg" --content "packages/Release" $relic remote sign-pgp -u rsa2048 --clearsign "packages/Release" -o "$signed/InRelease" $verify_2048p "$signed/InRelease" $relic remote sign-pgp -u rsa2048 "packages/Release" -o "$signed/Release.inline" $verify_2048p "$signed/Release.inline" echo ### JAR pkg="hello.jar" $relic remote sign -k rsa2048 -f "packages/$pkg" -o "$signed/$pkg" $verify_2048x "$signed/$pkg" echo ### EXE pkg="ClassLibrary1.dll" $relic remote sign -k rsa2048 -f "packages/$pkg" -o "$signed/$pkg" $verify_2048x "$signed/$pkg" echo ### MSI pkg="dummy.msi" $relic remote sign -k rsa2048 -f "packages/$pkg" -o "$signed/$pkg" $verify_2048x "$signed/$pkg" echo ### appx pkg="App1_1.0.3.0_x64.appx" $client verify --cert "testkeys/ralph.crt" "packages/$pkg" $relic remote sign -k rsa2048 -f "packages/$pkg" -o "$signed/$pkg" $verify_2048x "$signed/$pkg" echo ### CAB pkg="dummy.cab" $relic remote sign -k rsa2048 -f "packages/$pkg" -o "$signed/$pkg" $verify_2048x "$signed/$pkg" echo ### CAT pkg="hyperv.cat" $relic remote sign -k rsa2048 -f "packages/$pkg" -o "$signed/$pkg" $verify_2048x "$signed/$pkg" echo ### XAP pkg="dummy.xap" $relic remote sign -k rsa2048 -f "packages/$pkg" -o "$signed/$pkg" $verify_2048x "$signed/$pkg" echo ### Powershell pkg="hello.ps1" $relic remote sign -k rsa2048 -f "packages/$pkg" -o "$signed/$pkg" $verify_2048x "$signed/$pkg" pkg="hello.ps1xml" $relic remote sign -k rsa2048 -f "packages/$pkg" -o "$signed/$pkg" $verify_2048x "$signed/$pkg" pkg="hello.mof" $relic remote sign -k rsa2048 -f "packages/$pkg" -o "$signed/$pkg" $verify_2048x "$signed/$pkg" echo ### ClickOnce pkg="WindowsFormsApplication1.exe.manifest" $relic remote sign -k rsa2048 -f "packages/$pkg" -o "$signed/$pkg" $verify_2048x "$signed/$pkg" echo ### VSIX pkg="VSIXProject1.vsix" $client verify --cert "testkeys/ralph.crt" "packages/$pkg" $relic remote sign -k rsa2048 -f "packages/$pkg" -o "$signed/$pkg" $verify_2048x "$signed/$pkg" echo ### APK pkg="dummy.apk" $relic remote sign -k rsa2048 -f "packages/$pkg" -o "$signed/$pkg" -T jar --apk-v2-present $relic remote sign -k rsa2048 -f "$signed/$pkg" $verify_2048x "$signed/$pkg" echo ### Mach-O pkg="slimfile.app" $relic remote sign -k rsa2048 -f "packages/$pkg/dummyapp" --info-plist "packages/$pkg/Info.plist" --resources "packages/$pkg/_CodeSignature/CodeResources" -o "$signed/slimfile.macho" $verify_2048x "$signed/slimfile.macho" ( cd $signed && mkdir -p Payload && cp -r $srcdir/packages/$pkg Payload/ && cp -f slimfile.macho Payload/$pkg/dummyapp && zip -r slimfile.ipa Payload ) $verify_2048x "$signed/slimfile.ipa" echo ### DMG pkg="dummy.dmg" $relic remote sign -k rsa2048 -f "packages/$pkg" -o "$signed/$pkg" $verify_2048x "$signed/$pkg" echo ### PKG pkg="dummy.pkg" $relic remote sign -k rsa2048 -f "packages/$pkg" -o "$signed/$pkg" $relic remote sign -k rsa2048 -f "$signed/$pkg" $verify_2048x "$signed/$pkg" echo ### X.509 certificate operations $relic x509-self-sign -k root --generate-rsa 2048 --cert-authority -n "functest CA" >"$signed/root.crt" $relic x509-request -k inter --generate-ecdsa 384 --commonName "functest inter" >"$signed/inter.csr" $relic x509-sign -k root --cert-authority "$signed/inter.csr" > "$signed/inter.crt" $relic x509-request -k leaf --generate-ecdsa 256 --commonName "functest leaf" --alternate-dns leaf.localdomain >"$signed/leaf.csr" $relic x509-sign -k inter --copy-extensions "$signed/leaf.csr" > "$signed/leaf.crt" openssl verify -check_ss_sig -CAfile "$signed/root.crt" -untrusted "$signed/inter.crt" "$signed/leaf.crt" trap - EXIT kill -QUIT $spid wait $spid set +x echo echo OK echo relic-7.6.1/functest/packages/000077500000000000000000000000001455105530300162355ustar00rootroot00000000000000relic-7.6.1/functest/packages/.gitignore000066400000000000000000000000031455105530300202160ustar00rootroot00000000000000!* relic-7.6.1/functest/packages/App1_1.0.3.0_x64.appx000077500000000000000000011273521455105530300213240ustar00rootroot00000000000000PK-3kJClrCompression.dllì½w\W×8¾ Xê¬; hÐUƒÁt520+³²¶XbAQlØ`Wl 8 ¬ãZbMbÔÄ5Å.`gA¥Y(6Ô(£‹h46@Åýsg©Ñ¼yÞçyÞÏ÷gçö{îéçÎ1hÄ*‘T$Éà²XD¢‘ðç+úŸÿÌp¹´=â":hî½±öÜ{ƒ'MŽr›9cbäØinac§OŸ¡s7Á-R?Ýmòt7&xÛ´ã'tqvv𰎱wW[ͳÌY-ª®Ê½·[XHzf‹§poy’kQIî‹[¼ ÷E-ÊÈ}K‹çä¾Éz#÷“Ã&á8õa Q‹Dã—ØŠZOÜ;¤ªì±è}7G‰“ƒ(XÛéáGA’‹Ä"kZ"Ùâ¨æ.Rˆ ÒÚCIhƒªNU·¿æ…ä¢"Ñ3¸ëNŠDs DrD~‘Ht ‘* ëƒbRè"‘ÈçТúÚ‡þMuÝ„9:¸ÏŸj×.«ÛÆ †è9~¬n¬Hô-Œ)òƒkfÝv¾ð¯‹ÐL”ˆ¹"‚+Ñþ¿´Kí2ShHÖXdóà[Æ‹ŒŠ Yq¸„×0ev"f„U‘M,Ì{â/íüþÿÿü1|––ϧM% *9‹æÊÅú©$‰43®âOБ¶q& ô#Í¢…jñÀ4-Ó¦bã˜Eó¦t6!_/3ú‰Óa E­NHÕ5£ÚúŠh-È8P,ÍÄ1¸T‹þaÝ¡Sf-²[D—›îSSÓ(Zj2)Mét\ùÞ&8Ð6eÏ®é£é²"Ó}G†¿†Ã†Smã üt\jÈ}‹åÈçЬj>i&üú‰aB!%©NI«S²ê”Muʶ:eW’W§ì«SÕ)Çê”Suʹ:åR¢ ¥áÒ \š…Jl±.¬|r¢BšY B#ÍÔp¥b*¡1´¤Rña¡ˆcjq.ŸÏeM÷eá5ø€õšÿ?Ž–»‹ ¥k]þzüòÏÐT‹ÏÖúC¾”z§¦— Þ¼ïPÖØ{Ç4‘HÍ_Æ/ËÃùÛÑ/Ò¤º¶0wCÞÄzçj,i&³œ¡’Òüx§¥Þ™4—nÑ_§¹WnÔâÀþ¬a¤‡‚毳¦RÚÛdº™Ì *)—$äTR&ÂðñPÏA‰+ã¿R$yó–-…yL_*r0¼ýdjZ§ZRÿÚò{§{Å¿=Bã:Ýa¸ª~€þ¢šÑþ‡Q\ÿm8Z—ºU‘Y=D.¿|%‡¸z/߀x;aÿTïå¤;K„Õ\i±°<ò÷Jù™šÏјJ$T[:{;Àr^3¼ f‘U±M­I¬£[çzë$®D¬OR'\×½· yå…ðí_™¶¨,7©%Ⱥnô`ú3z(Ë=pãiWú$ú}\ê+ÖááK«2XƒÎƒeøÛ¬aGCù•³|¡ÚûE8Ÿç6 ˆnÊò9æãÍ«ŠP7%‚nâyd1·V„5øË`ƒ¾FY=­J¥‰‚®Le ¤Þ ôÏšcÏòy¬tPH©TÃ_f”&µwÎqØðÊ{óÖÆ8<ËæÉOñTMw¥¦»&<Ôõfùn,ß_Áð§ùJsx(¶éëÉŠc¬xŽ›xz«Ó]±¤Ã$OhæÉô˜å©—³<­` 4t”2=fºRñ?ˆªÀÓñþ ÒÙÆ ä•o,€T^ P+”þu(SÇü¯ L˜@x§²<‡åV,„C~Ær§Üè1ôhzdº:áŽîOàHH€sk0+À¨æÏÓÜkÀNSkMŒ2Õ⇫'ðóüŽæNÍÉ qÕ7IA' ;1ó˜æÏ”ÌŽùt•£Š¥SÉ8(­Ío ä(סÀuCªøNímAv QóeZcçè{ ÂáøB‹'DĘ/‰ª˜«7 ª=â9°YÝB{X(*80ZŠÌ)œÌòþ!Œ°–4‰6j$ 21ï?“çqä‚"– e¼Swÿ¼¿VëžÉügúQÉ2¿p?ƒLeB~ÐLÖ= e¨än€íÀIT2Aa¸A6@ðBhmèÏÐÊó„YT™ÑÝù5{¡Ðè÷¤e¶3Í_FðGºŠD¥M¸—R}S®B¬o`-~Þ™hî(þJ©Å<¦gMq\9ܘªY%·Q´&‘d 2vÑ}©Ð6œ‘f@{3$‹-–¡J ’â=C~zK<—ŠJŒJN gTg¢¡j¹gÈnËݳ†™Ôül¿×QþF²dö #òTà›´„*n'´ôk²€MÂVˆ°ƒ°ªjûná’üQ‡#³[ô‰%ëêi%˜7D˜‹1”# œ„.(²¤’Œ0=–•ŽfŒ ìTRcqÚz±Õ%u°\sLTƒˆ; ¿©#ê„|*ž»ñ6‘½R%²ÞY §È/Û?ýŸµ_]m3ðshƒš†hœ9nDÔÞ¢ræÖW9s@åL~-¨œT9¨h@ï„Vk›QôÈ1é 8¡ž®¹Óû¯º&µ±U×ÌuÕ7¬­kÊi>£tñ_MD-ESKßÈQµ¼Ué¼`;ü†Êæ>5þÍqDmŒ•¨ábŒþ¢o®7ü‹¾yHÅWH}"û/ë›^õ9šç 3m$¨ÛJÈ¿ «,T|J$SK3]¨ÒL“ª5­:…šé .4­Qµf¢UgQ3‘âÏ›Ti&Aè¹r›è˜Ûp+¬]ß\ ± ”zã¦Âl[žÛJz€2¸S÷ PK¯‹«hþï!…©²J¿1ÒUŽ LÞû+ †Þ[' Ãns]Ez ‹M×u‘Ón)€; ²â˜QŸ§k1ˆ…¯%A ?ß—58ùrå©„è$Îï$¡ZKcR½ó½³Ìù,C€–;è¯8#·µø{Öäø:99ä´¼¿EA`V ”R¨»‰kZ2ÿrŽû™hL{½1¤¢´)wõ¬$F~"W.‰±9‚û¢Ö>HðÚ@9B.Ã_> ©;¬LÃûz±eWiÞ·W‹üE·eW¡Ä­EÖ¢b‰$%9æä$‡)™$OÝËZ—*ÉÅ1ië ƒp/Ð:ªúÊe)´y[¡­Pˆ%¤‚ÚY[þë ­;ŽSÍà Äè1S¡ó„rW¬§eò¿ôh.ôÀÖ¤W™½the¬5_|H¨Ï¢|…jDáSË•ÈMÅNq&ŒÃ« lǯ!à¿”,hÁ½´±#~IiHƒXþ¸TI©’¦"[ÇLh”'”ËrKIPøÉ­D*+ÆÅ)³óJ¤Ù¦™ÅÄL5µ…%Ó|®é¾¼u™DæªFÒ‚è/4Åêw5‡VZ@Íg’V%òD'É_ZU ÀŸ%Ü|¹šJÈøÙ ñJÅ£Ö¥’}]¬‡¿OÔ,AèªïYÈÖ´*{¶+­Ì¦ù|P2 PMˆÖ˜dL–¿`¾í@èÉðyÐS÷¡@¸@hˆôaù S+é,þ>Ø‚Jf]{úD®¬ž¨ Ò ëì&0¾Z™fÏܨj& 9é£w´Sƒ”š/× CèK%œ¨Y™¿‹XWÏjû£3*W½OõŒÍ˜­1ÌÆ(Ó¬“ýPZ½,G¡Pûé:Àšðu£’ºÉ´Ø@Ö¢*ø RžðB¿¡Ôæ„Ø ™¥¥0sÚì†ÂLdžO…y }Õb>ó9•°¿f^Tü7Â:Vý3øïÞ¯†?Ðþð{ýÛðÇÞ¯‚ß`Ÿ(À>}_ý˜ê®ý¡' Ì>[Ž ¥È §i,Šþ„J&ÎC&N­¥gP¨‚ À;Š€ÂlVébzDyê:àx“pŸ¯G´«^™áï 6-î›hÉŠÕ`@¹ÞV=Zh¶L¯¨¶8®X£CC“¹ M€`hJ‘­@$Ñ€ëaX€[¤Ñ/F´œÖhÓ;rÒTþZZKãT#ûÂÿhÄ 6ß&÷H1ÙZÓV*³ÞÎÛ´ÊÇàÓ5ïñ†úJ©ÞÔyçþ&Y²ˆ¸×¡S^r4Õ¼# sÁ<ÃNX8/Ew£,§|`§„¬…r£º²äø!\XÂ¥ÿ ®³ÕhF×/è_¿ ¸~Sýçú.u ¬Ä¤  TóŸÔmˆ…ɶÈN°äX›²BC`'d ðvÎT90Cd϶äÙ¸µÉXnõ(ÞÔò(ªüôf x$¾Ìq"O’AÓ€{¦^ü\¹1¬Xïh{DLžJÜXj[ZdkÎ2KçgAšëÂðÀ„^8Ççé,?@Á&äëì¾–A(t°€Io) }KYHÝ2‚ULz§Fö‚©Y6„ù x¦¬^aÍú† ­Š—Y>ÞãšH:ÍWTEÍ,†¾°†E>]©„O!ÁóÀ0c•à…ÏG²ôÄGUq¢Ï;K Qy(ktc{zé)Öh³…EŒ‚oú†¨l(üŠúûp/-)¶$Ð7«²æÚ1|EÙ­RŠæ*¨X{š¸æÜ#°33µ†ñÃh£Ú•˩¥;„'dr*áHʹr;p0Q¶*í©¥¸IÍðã=æà~µt®IèK«ÊSì u!tŠk3\ñeŠ)· ]ø˜,¹¯˜€æÉ_` ®è:ÓdŸ‚1ÌéDƒìØ _/´"†9,c`ÚH[ }µÀhE†¾:K¦¡ï‚”Ÿ~üñGCÿ9œIRVœwß°ž`˜è7 –ÿ# Éú†T—éªËB­e0›-d'Ñwé!é1 R/[[°\l¬,Z)ø%XgtJ ÀõwÕM€ÚP‘n4Ü&‰tC‘sò™†Ý¨®Ø<3cø¹ùLß^rÖ(óc K0ä€d?VšÉV˜IÔ‘cMø¦„Óò5± Ò<£/ÑnЄj± ÏÍ|BȔרBOö+MKyi媎u÷sVn⇮âZñƒ‡˜„ ^ p› IHº@2ƒf%Vrzy¿0Ìje˜åÄf¹¡r@0xÿN†(τԹ6eW þ@AéˆaRV JFT®Á_êQoÖeÿoª$²Š¬Æ3Çåkþº3Ù^ñÎ,M5ø³d¡bÿ«Vnˆ'(66qŸ…!‹Jf²%aû4Y–mà’“-3p)Ö¤­û’v[CúñäíŽ!j&À+ƒìàŠ3È. Y;Dé>D}Yå¼&_¿ÂV Ó†3ˆgT/ëóž dÿ!z˜2§ãƒå“8ÓKNš£Ê‹\ÀòÁ@ütÖØ¸¹ùq_”¬XsÀ¤[p7¨Ã–µäáígl쾊Jö{þQ¥GÊ”é¥ïÔpƒ„<çýº…ò¬ÑiXxÍó`*ÔˆQS§½˜K³èïJŸ²<…¢n¿ÄÐü~)‰˜¥ç 1¯‚8O¼T¨Z¹‰D©ÒÈÎÖFüÐ~% Ÿuã7 ÞÆoÀRWA¶ýÀ€Ùqs0½q=pº—€NôejáQYů@ÙV𑞪Y®TÀ,ׄºa¿A£@´{!À7ÍGÔ¸é0<àOV<ÌMݪC´+ÉÁ^fÂ=Ø“›ë)ª½/«û %ߺÿ‰öÈ­j”?,l]cx ƒ&ŒÂïCÖÒ†­ÞJª¾EžSã^’Í Y3Њ³ieE\bÐoÑI ©å'‡‚¿!±¨©¤^Fµ7RjúCÂðD¶ZU¬¦ŠÕüSô²&,›ñéÕ€J@‹éëÑÀ"8°T’ÜÇJØA’¬Ø§Ÿ˜Jø ©þ Ë¥IVQIýĬ%ÕG®ÿ ’!I©ô…¤LH~ I!Ù’¶B²-$í„dcHÊaT¹µÑ¨R#·ÐÊT\–©1.zë†H&¶9cøz#ÁH­:K„ØŒÒKÌj5îçÕTòÈ £º5Û$pg`= ­"ÑIž•A€Jjªo6Ú\_š7Uí‘ÕÓor> £ŠHé†ÏÖJ5!@%* \ÇUœÚG\…¯1í3rß[±¤C^çü„ÆŠ°Q˜¶bl¦­(ScÚŠ³˜¶"­3¦­X{ÓV´5Á´æ©Á[ZäZ™FðÆ•K´¤y²‹¥2ÑÚ¨¾™ÖÁÒÒÉ/±^éÂz#êèxú>­J ËWÐTÀcˆ OjË–ò ‘6§=y¶><‘i†2qæÇ×A97BUÝ ¦PtÓª¨›±ü9AØ‚üPØÚògÄÑNÂð(cÂld"Ö õòYíÈ\Ïãb“€®4÷ÐF‹¢h9Ïk\5–L-ªÌcøènZéü_†Ã0ÆÃà&>xR´\ËOƧiÀ+Á ÷òà:±†Á þRÁm>8Â’mˆ÷ {u^ÚHôS€_YpϾãáj,-‚‡=ݬÏGiîÕ†Jê+¦ùœ‚"–;#‘^¥r*ɶ ’\ºÔ³{ššoOóiÂaˆ‚RFYdÍWÇçëz¾HSè™iWølÚt_ÁŸe˜éžNKßÝSFcIÅÆêDEÉn<iî6¥ëÀ¨®E5D XËDÎgW ÃHsåµ>ø[î™4ßËÀgÔ‚Dÿ ‚þ}:Ýô¨riôˆ{[Ýyhp"”j Dqο`à—–Ó|ª’ñP¨Ê©%jÜé¾ ÌT-'´£–Zˆ“2ØÃUkìgQOwå+MZi&hŠÛ‚ü¤Ì\Òâ £FŒ¸Lq&‹þAÉ|âžQšÕŸQ¥BWZy^è5ú)3ñÁƒ?•Ĉkõ}D&T=‰ò§ùójåå· _ÚÖ:NÛwÌÏgñ™Ze&ͽ”ÌîIûU¯ýUc?1éAsi•t¢/É ’Bî©R(Ã8¬òœþ2y¨i´ðC=£º†…t&ójy`ìÒ>Cñœ2³ÆþCG°ÿR2Ì yKÕTÂLdôU˜J÷Á[gq­™¤Ô‰Lc­×¨žG…©2¢F#¥ÜJö½FýÝËãEØ×µ$òÑÉ3ìqg/Ã$t'ýÜ2ìZbQ7(EֶصVu¨n Õ¥·j7~2•HA7*k•eçÈž“ò*k$À$£¯†QhŒjƒŸ¼àwË9÷_ƒ”åãR10•ÁÏÕúÅ0†}åSýÐet•ÓŒJ…è0ㆰü6î0·à<ÏjBL·gîVŒoR½ç>¼IÝ=wMT-1"GT»sP_“ kn«‡âžú0½œÎPãi$PŠÃ]ÕÞw´àÖ¨ùšW‹øYr-ßUÍÇ€ÑÅh A©Œa”A n)Œa‚WÞŠZz÷ndÎsN[,㈠£z¿Yàò 4Q8¦b¹4“lõ–ó&eZžY úS65µ­*“JàÉTT/ÜW2Ú¸Îö•p.ä’f6¨KɸtáWLí¥UAž³±µCýÖ%øˆa„ÕáúüÆæ÷/âÝGr\DÙ\ÍÝ“€=¤¹ öjCŒX²]Fp;½Þ˜ëdŠÝ¿lÃwãRЋ,l{#2Ê2ðp7YÕ¼úcÒ2Ä`0¢ÎQÀW$6ˆé2µã;“³IBÀëĘ}„*Ý.×P¨u£Ô†{å&¹­Á;€t‹,Bº!Íd§Ëª§û`Àœ4Q½Oˆ¤¡p5™‰êýÐààÀ¥:Hk°béýw @„e7†àÄU&S‰ÜÀ:ý³õ×^€óé iÝôÚò°Qñw ǰUèpýG³']¬-¥ÎÕÕB ÂSÌ\èêÎóTÀ›‹nLUë.ZžUÚñ!®Ê<žm†Ê4[¦Ì.•ðð‘A¶†¿¬‘f™·ûXÇCÈ! š†ËæNVHø e™Vú˜îžbAe}G&°Žm8rè…8‰§a¯Wñ–Œ¾T’‡WP-X>Ä ÷?¨YÏ(9“èÑ€¶¨Y7OV >l"Ð_ánÛ cJKûÀvU€–ÙýíµXÈ¥9Âv´ÒÒ ‘8RÙèÔ) ¾ôÒ=ˆÓ|]›êý/àWàÓøj>Žâû„OêñÇâb”sˆqZRñßU`RÅÉö„“·#Ëj ‹È¶ËxO+;¶2ºšWë‹­o^zA`æn¨S€qû¯Ç¸W†nZ¿ü=€'siŒA0îœY ÐubÛ*@0¾s;LvøÉB5ÒKf‰#†y5–\€µ©9¬ÞlÉåxvHJ•ø 6çÞØFÑq¯pO/b¶`ÍçCœéÊò´†O Ñrp}iÃ(Æ|õÚ¼`ÃqÕòŸðPU/Ô+3T‰^?jX-ñ£y_%0 x42Ä]›ÿ¥Ä¸ŒO¥âOBÛ©„dâ*äâ5¦ûršÐFM%¥ñ¹´4ÄŸØ(X²ÀÖ¨Ý/Ð%Œ4ÓŸ@ì>êŒ wÀ’ÍHMŒ*}ö†J’DüŸZ@Ò:€ÏÕšf9]ZZšàÒЬŽ%SížA˜i*ÂíÇËÀq 5>ú@[Yµ¤êŒ ^|ªø6Cº•½°XvºQ ÷ðîKhûlh=Ú@,‚F,j„R` :Ĥg•êeï”@>Ûª0-™4Bÿˆ«Yå5­1aÏ!¼CaCßMX5±O8™¼Îd¼Ãß7´.qS‘¨GÿãD #DÅý3„o’ôß#lèß_fåS‹ÀVòµþ¬ùZ=D³Š´5´lR—–* ¥ÑÇ;%ÿ1µ@L^íE+qs| -ÙVˆR’/ž nPS‘õø0-!;$ZšOƒ¾ªÌÙí•@su7H9L”™Ê¼R™ÒÄçj¤C| †…—RLiø<"¾LZH Èa”…J* 2¼¶&þñ£’dt¢Xc1áVäcÔ(t –JXá!®çó§øæ(õ³˜¬]Í }hKN•ªn›õ”èy¹HPÕ>é,ñ»}Rµ²€m¥i…;‹a\k€¥«ù4V™ ã)D:;ó7§-–RsRšÊêýc ðqËKß^ˆ6„ÈcŽŒªhƒì‘¼+îɹ¶è?º¾ÿ‹ë­ö}› ±®h­Ä=;™‰ÏNæà!ªalÜ)´Uô¨1£ÒkÀÄ0ÐÇÈÉH'úº¥2†Ÿ@âM–W§ÂÔ ^íc°Nëè$ÂyøQ)8¥Þ ͳkL;ÕW_€‡yZ‘½t qüV029HP?Ê1®â1­xý1e´\íD‹Gµ"8ÒuI¸®ëÈòCÁ^¥#ùôZ¼‘Nø<µ&âäÕ²#¸š²«ä»-³¸b_²J1i@#icf^èe0®-w2½DäPXÕèWÈ#wr \xø^ ET[Ѷž0r/‘(.º·Ht®ÀOE¢kpÍì#¹úŠDYpu¦E¢0¸¦Á5ØO$:—‡¿H´ ®lЯö¬HtO#}ÜO$:(µ×ŠDGáº×á ‘(´¿H4 ®Ôþäõç:ç—ð}DÚ·ºiyÚ'J–ñ±ž#ÒøÒ–Ü:çˆØ ODÉ;ÎÕ=¯Á¿„Yðltü\79*t›pkÅ0*áªH8&ä‚Ç„jÎ%Xkƒ=f §„æz —Îòe ´ §„t¤öA0ãI)l;‘ˆÖÉ…%éý^““uF÷÷L°Ìµ3Ì”—Ý*mRW.³¤üäœÅ+FÊÝ£„ÃBÑmðlÇ<ð³"}ê QÒK$Â#V†(O빊ºÓÇYÞrØ‚¼ž¥LŽ’½aòVþ9ácý ZKÎT$?'|–W'ƒ_7¸ù²ÆÅèž²†Å‹H¿P¸†y§fø5ÆïsÔH¯:ÏþunÖ“Ã=ù',_r’ŒÎÍ÷éßã=ù+·0¼|¯eˆž¹BÄÙñ%[ˆ« «R{¿`zDuÒ;Aó‚{†ù´†(/¥Þ•Ÿß‰å‡(Te‘xÞÚGQ^1?òÀ³0CT·Ò!ÊùÝøÀnª7ü*à +µ6Ê5gC#e™®%öúžÑ@ñ¡/vm!†®þÝTþhŠ9cð‡Œd¢l¤/TB^Â+(>Ö7_ñX+¦}ñÑ­ðàÖáQÌÆ=È$³‰Š›÷)Ö ÖÖ/T{?¤ùò ¬³:ZË¿ÔÔ'þO0[Ôü%Ö0$Ù´’5è÷·{j=_õ/Me¹r7Š+D¿Æ’Î"l2hî%5»˜5lòÀçZÃ÷©šXtmY>›å¯Xã?*gIÂ-Œ:?‚ ÿCÍ»Ñ\–…§Þó(nÇðéáLu†^NK ¹Wâ¨?Õo ð[Âuý` ï˜ñn—ñp‡ ›“Ê PH@ð‚$FìXáà8€Æv¬÷YsgwHÇBœÏŽÆŒ•~„lfÅž/WÍÞ¢CkÒ—O³¦¼Œ~bk²­ÑObMºÐ\º —fýl0ûDiÈÉð ^º´Ô~ñ$¼%]­úíˆð=MMùýV½ß™¦–¦RÉZu£4Q‰±Š% QyWÛP¶l2¾¯ñJL%à[¾€íi%þÜÜÂóDŒsV¸”q. 70Î¥ˆ[‚Ö{jþ4 ¿“0Ÿ, ÕÖ„J&ÝÃÈúc!ÐàmIÅs&Œ*#ò ¾ÊÛŸæÞˆu­´FçýCQW³hMî EŠ-Õ†l(õ…º¦Xw¬‰+‘Hðˆo¿¡˜»OrUmkùËÀD´üy¸-¥7"´ˆÙâJóg!ôÎV2|*]p—ÏåbVŠùÇZ¾ÍŸ1¨9ÚØßbZ£ïØ{/ˆ~¤;ñåJÜ@Ò«ùC­Éí»ŸîÛñL"ãñŸF%;Ñ´ê\ŒÓ`Q8pÙû¥ïÅ6b¥Yèj5€FnøÂ™}©6ÅóUŒÇûÈ[4Ÿ£Ûzôµà.-=ƒÄ¢è 5(núâô-… O5‰ ’ƒåí˜ÄƸ!˜Lâx‰XƒZ–b•&ÖT.;Hpé%Á¸« ÄBÌûÅuløQ’P†,éÊ«a©´Ê¤sÖàÿTó7`T .tãÏã1i à au÷i@À¿’€ÿTÂ,ÆÝtXDâ›TývX!k*’°bPˆ ü n%§À‡á2-%'ànŽl9ŸD¬ˆ–Ï;Šn!ohÜŒ(©‚¢“¸1NÛ/V@ WáÛŸæzŠH†ï‡'¼i‹Ÿ'c«@óèŽËÈHØ"–À-ÆüÓpÚðö/u0ÒRÚ@ iž´U{_§ÉÌ5“ÚÇÿ/&¥ëÏZV{V;ë¬>Û||ñc±S J—êÈ•ÛSÆ×"aÙÃqª:pý«àXJkaà+K“ð‚ß 0*É×E®}ŒšÇ‘Ó5ü@Å[¦§e ›´²Î‹MIyÇ#USûRIÂéêùËÂÕÖù›B)`®®â%ƒæ ‚;‘pÖÔë-'ßzñ3ëÛØ4ÿ'aÖz¬ü<ÓcŽìïÂòbn+ÑÛ˜ñÆ5¾ŽÂ¡ã/É3ë5ìÙe2á¾_Š/.âF Ãgòkdˆ!“—æ`*‘šîKUyÑòRm ±á³2•)º%›ðP×”yQ:ñÍ s`ð’⪽dTü¸©ò¨øŸóùÆV>^1cì+#_Q´Ûsˆ*‰ÜÞòfg%øa˜Žd¨äš9N 8càŒÃ¨5#í ¤Ãs¡øÔÁbâ×°$‘jI#ß“P ñ!ï,Nð­ÏÃÛ@•üW@•¼T­ÑæÞprÄ9tˆ@áâŒûAº`ž´ãÊå±­ÉË õ@±ÂPê@ck­<¡@p áÖ¸YëÕ„€”¸ƒôh`EÖ‡HɈCF•Ï9Ž0’ÑFEr!$G0hˆ&ÔÙu“„ºïHn¦Pg/Ôán/ZË8Ň”Lw¢¬š?КZþš 3‘ce•lú—•!Hm}A”T+ µaYµÀ£ÆPÛ/«­1„Mj¾$·¤Â®<\Ëgá#…O©$²ÃB~QÚ¢€#®ÁµF‘öFX5h+Ò+(º ‹µ±¢nãªþ4Gº”W÷âQ†c rW#º¤Ð¼Ö©šé¯ q€ƒbHv¦UôWBã#¸ÇpÒDÜTbg ÐskßÃbN­±>äD'/PIø‚ÑÑf©þ[(ms5Vý§ür÷ÜÚ8 P®u÷M~Nóÿ-¸î×ÁéªjœÞœ®ªƒSÕN"[‹¼Æ8o<º „—:<ñ6Kdµ,Ðâñ_k7€4 ê×ãQ8†g ÄZÞWQjróÇ×Z}=± ”4 fØW¥7ZᥒrëƒÜ’¶NQ[tr¬Ì@%¯@è ‰N‚‰’Ó£«Œ”Õ>-ðP–Ÿæ¯²üKôÅÚ2üpO«åÖ‚ñ÷O”5Ëm5ÜÄkúvÿÀ;µÆ½ªãAu!ä5(Z‘ØJ⭅δ"Ÿå%6l`é‹üªrRFZ†—å×o ¥ ×uíj½–dÇŠëA„Ÿ>ü¼y‚4S¤×Ö§äÝP© 4ÆZ|Må#+ùòã‚Í-`yV®1¦ ÊÖònÚ°…6ŒuŇî!‚F%§‡?ýI¶ˆhþ°å‹PÓeF‚íå±Ðb+ʪNSÆåd3?”£4㎪\z.ȸ—X:­dì¶DÐç÷­ûAam4ü«þÆ^߈„©˜OoÚ%‹‰VeÅÚÐ|–% ¼'í'Fu}a ~¶È;?œ¶Äû"ÀjÕ¯ œXžñÑ(+¾q ŒÐëÚ(s ödÐâE‚)¿£k¢È| ¡¥iB9Zt wœž‡b*!Ú‚ßË·ZåøVç0<5bBmg`¡âŽM ± þ„&,;œæŽ±8¹Hßp•% ý(!àˆt ÎS¸Z¼‰”@U¼ï2HHÂñ,IÈ_xe‚6l¤« 3Èk}…]¿ ã*DV°±ñWäy²&Ìïuqàk%G•¾ðÖØk ¬d‰wGšr”™T²µ'“'Šy ¿-!_†Æ¯fniPÔ>/OüOj›A­ûÃŒ‡›÷uÖÈxHÀbæh•©Z¾PØÃý”@£,$œ‘:ÉÂÕÒ¬ð¾§ÁKš†{IÅþ® 9ÏIÈŸÈÌKð-ktò€ad 1€6úŠÃ b>½Î~r¦%›+ÓyfG“4ñ0Ïîoñ‹)tÉ–×z¸áÞí,g"Ì^uýM!¾9‚§ÇÔÞ©«`Áà!ûÊÈ&èk\s¤ˆ|‚!©[±Í‘Fï¨'»IZCA¥!îª4CœœÄ^q¨Ó…ç=š°Ô£èáklر8$G\TU&µ2E$¼†«ˆé ÔÕ†m" ´ak㈧6ákÜ\®J‹l¢»›ïˆníï€÷h)„½µzBÛ£¤v`õ «;øÄSx›Ý À ¹f1†Mõ Y¼íg€D]®@5á:&¤¡cu•µn5¥cÒkô'•ÅΚhdÎ R?ËúÔªŽÎ´®·æ¥ÖB5_(„³4_Î?á*cG1Æ/È«nƒÐØ¥ý[» ÷…]†ç€³5Äе¡’ÏA0\™V{·ÁFØm¨ÚèpŒŽÓ[2ÿSûÏ«÷9šÜZþfj'²Ç¢6ú¿þOÍý¬öÜþ¯kÏ-æÆ³+~ ‘¯s¡?Å)°¾¯Í @’˜ì6€î‰ç0®hóÞj ššŒŽ;臒 ‚úÒ|OÁçâÆ¾ü €°Ceþ€ð^€µHîGZß—³â9ã*{M%O‘Ð\Ž…Î`®¯áûjc MÂC½ýaÃAÚrñûè‰AÛ¡7š\O­1ÊßYòÒ牉ÀU.º0ª¼¹ª’Š&CÙG“Q:ÝRXc««=nÆAdT;{ —'@ ÿ½ƒàjþ/¼e»uã£Àús *y ù·8y¹£ðŒ 4Rñg0PR½¢â·€oJy+äáPµ<å¿ :òFÊ»`·d ï¾ ¯‰wþûx]uçí° °p.±cqXµ±_å nfžT‡¾%ĺ/­{p&I‰·¤ ‚ÔñÔÊFbBÑ÷êàÉü€'óßãÉù¿‚§§Õxê(~+ž¤%ŽV”üQ%þPòàïQb;ÚŠ’çÿA””Õ€€øp¬‹;ëö„7„L·@1:Zc?4ý•L¿Lÿa;k~ ÷ÔȽ—èð£†Äèí?ùÄ„kO±?üN¨àrÒ|žš7iù¼p¦‡\OŒÒSaÄrû¥ÜK{*áGôLñ9½Æ¸¸å<üÓìkás‰ØJˆR¬L?orý@¼9é{àM¡wc9“- ë NËU¸-8¡á%ÜK7ò¹J˦cOÔ+‘ý¥Dú—’:ãüuÖKS{rùlÖÒn¾?±H,É„ûWmÓ¥LY¿lP_ZÝ$î¥Xïbî-63›üì`}I³ÞŽ19Æ}®CuÁayÝ|Õx6F@ߣ}w«ó—JH©Ò©áK2Îû ÆÒd¾9æò=63ج#ehøSÙ®6KH6›˜Š\¡@O ‚± «Í¤dœY¤Ì]g—×Äg¬Ê¹ÇpŒlY“I^f²XôÒ4Dc–¢Ä—(d•yÔâïð,W5,ˆÏÔ˜J¤º¦Äcy™°°\±.×?ÒpEvºfÕã/ƒjð”,™‰´EÿDçcpX+>\UýÇr/‘3Ë ¹bJç¢UšžúQ3d-žÒÔB©Fš#Ìj£‰«Àÿ¾‰Z:¥Ž+jû9ÀÖA`¸…S é"øÑ<€ô·Kð` ¯;cÝÜ ¥Û@"„b€òúÇ¥«ß_5œq/ñÅÁÈ8ò2-å*s \_6΄•%Sÿ€+Áø™?Q-øÂ©{b¸4¨/1E¤â1"Ð÷4¡÷yMœ{kâÊñõåãEN"ëò©9:Ds0Vÿ­.lüït€HÁUDjÀòŒC9~ã“HüWú0Ö’E–ÿnúmGëÀ;Eâ4â°e…¸Éã§ÔBßǬ4óoèÃ8<&ë,0 íú þGô«HôÏèø‰è´XT›L}LµéS Ò‰¡AH )à×É+|öÿ>±oª¿Ìâ„R.ìD%åk‹­jOD…bGw4Kc,.×¥ôÏ (!~N,·ÀÊõ/ÒªPSSïÌ’þ(Ov:[$™AQM')¨é¾Tƒ•­pÒ~F§,MY¡Æ’¦5¶F[llÅÓ–ßXCÖ0"ä ¸ èà—'\^pù +k,Ùú«d‡:Ðþ?ñw-U¸g¦Ö-ÿ¹^~J½|H½ü"kþ«zåøWõî¡©ÃÿÛmo \;ëÖá‡ðõ’ƇáÚU·ÿz/»èNc]þ82ÀþËæ§¦Ù­|6eæÇÛÃJ¶­¿ù£Äë.ߧܘۦäRQbaþ¥6Éþø‚}±vSî­n}rÕxErë'n{§°³?½IŸˆ¹x­Eé_§ 8tñYFB÷ÙÆˆF©;§ŽŸW1®òAÖ„eÁ#Å #lÃÂzS/Ÿ;Ë™nÓU£“µwèÐK‘³&~—wGÚ+à¢,°2Á9ê!‡í/wú8bã~¿ËYG¯ûVûù·ˆ«éGÞR_p¾|cx?~ö*lßStsÀ¢W2›ŽÍ=yèÙÁ÷ÜOøìËuG.PÛ§lN‘xÊmÆì/t:ãQâ¸oé‚6ÐNöS뱦ÞÓVÑ7fÛκ.>æ–¿±£âÒ#C¾Ym%Ùn¿sr‹÷ÅówYvsºUˆ¨;í×»nt)>ꨳRv:b¼úYtÌ’#>ËXOË—fjަå-3?z™Søì7§àC?Åî>´¢3·ùB«K4Lë§p/T0óÙœ/èGc‡\ÙÝ¥ñÝCŸ\9´±ÕúxÎføÆ–‹]w‰¾¾þ²óÞ¯rºï5*ún«ˆË­>ôÅÍ.#Î}ç¿î·&[–»õÝ¿clô®?›ÿ¼—ûÔìÁÝLŸ°ÿôµwUº¥/ãyvêŒÉ•3gïžÚµÇ£Ý:ÙÛ9NóiÕði÷¾·öÛ:3kð̇^¦>+{ÚñhRZ×ïô«[ºmµ[õêØ–fGŒñ.soîjçóh/ËܧQ'X󔘖§é^o³ žû˜‰W²´spÚ™Vƒ¯‹—5Yë{ÔÅÈ™ÝH+ô9é|z«öÙÑ÷VîXïp~½±‘½èìH¶ë5?îåôìAÅуG=Ò$bë¶Ë}Œ‰\Ö¯Ùxåfƒ]g3ò_?X“óòùÈ+ã"[ß;õæ¡Á¾[⃇kèÒ´¹{êÚL¯i÷Þ;¦VN¸;âî‚vö9CÇûäöß¶{µøö–e®ïÝáZy¬ãžb_þÈís°3×pKÄ~û%?y°Y÷› ÊíÐ7öÕ¤è ·_Ìíóa2ëÑ]×Ò¾õÇÊžxÀÈœysÂÜ®bCß<½{ïdZ囫s/õÞšsöe÷eÜQ»Õ_Í|l_ ;vnäT»6¾Ÿõò}bÎnEð¨Gc\"žõ=×§pÚr—4ÃO%îô}ÓÌÓ":újÀ•îlïv^qúh{||Ëc-–ѳ>?:bÒFs´aaĈ6Á›Œ‰íî´©sË~m%ú´¡hãáA]¹­«Æíæóú¢éég¿}öèÔÚi.­/|QrÌn÷Nc«ä©7»nêø¨cÂùS³fgÌh9ª¼KǾÅÅÝo†oµ;½0l°yðûžýþt¿äû´]-'®Ž÷j¿uK‡”ݧïTl¹YÙóìÓËó+ÍŽMݵ¨rÄþ/?µß²¡OüO{=Zuú³‰ÝÇõíØ&*º«ÝÁâ™ìóÜ©ºs}çEïÖÿÊËênqþÙCg+ŒñWŠú5\¿ÇÑݘ”9së7úh|ßQîR»Ÿ§ú4éšàââé·1x¢t×àØÔ—s‡ÄåŒÓ¶=í7ú™ûÆov¸¸±¾É-EƒÑ5øõ—ãÆ]¹:hîùfÁÅšÏb_†¬ë|eþÅVgÇ5Zfì<ðèún«ÍGÛænµ}ð4vô/i¦¯f#>Ü:HoîÞ°ç.;{÷)ƒ=äÊ }Ü™»cývöØÊð–«¿ÿ ã²ãEÇrž|kÌ-w³2ÕýÑÝÂãÅÇ¿ÎÝá܇[Ö>vÿê>‡=rg–7É™¬ê{×o^tå燦N¸ñlDìéOìý1Û‡-Û·Û>åñ–†ßzŸí³,²Òc Êÿ'SÒÄÓ/ÆK‡Ÿè+¹±ÓÞ®4-Çvÿ£,¡i°MÊ€F.+UyÔå„ Îņ8¯ha?hÍU¹CçͽF:ÎÜÖ¾U@ÖÖm¢¿kÛ¡uh›Ÿ?lùõ؇-ÒnýäúüÐäæ™O>nhÙXÖè‡öû¬ÖF)èÕÄÝëMãy+Ršݙ׬¨oÏ•N+{¬ø"Ku¸Ü±g¯?˜>ŽñK>Ø3µ×„IïaÍFwŸ³é“îé×>ïrírh×—ÖyÝŸZðѯâÞ«~Ÿê±cå®voº>ú ëóÎíµAãÜç°õý÷¿¼ëÖä©Ç{žçtl{óˆ²oèë³>õ|ÖjVçôÙ:}•ù¼Ë/ß}òaªñ«™e÷†ÍÚ=¼MÔ—Þ7"[µ[­ 0;òÏf:õ×—ôsnbƒ“6•;Ǽ7î‚ïu‹æ®iã7'{«l^eö™ù{»‹XÊÄN»ùÒgFɪ—Ó#–šúùƒè)’]'}ìót²|ážq}šL ÿI9v€©$ôâ•&üv|üø#Ò÷Ó‹&ºå\a›}ÛDÓ¿í À¹úÕý^‡Ý αoÕÿ‹äÚí¿n ÚøÕ©¾{Û¼èï«6y,f˜ÎÑQÃ(ߎw5~­—óþ!“þèè=Þn¼ äRì‰æ·GÜäöù’‡áÃ÷-ý~X·Õ¥£¤¯:Œ¡ž1zÚ‡¿ŒYÔòãâäYeE\Øw»p{Ôo#ôº;µðͽ&¦ünã<ïŽO¿ö(·;|¶ù»cŸ…>¼sûCsAâÃ’eÝ*=>ròýÇ)*3òó^oŽØðf—íK'E‹—-ç]}åwjs…~÷Èr¶Dôt!—ö¤}ïøÇ †öýs-mÿl›2çùËuƲs–à¡£æýäÈUÃëk=~ûôú±ç³nò[ÜÊïù¯¿~rãªÓÔ+Âw]NºúèbÜþΗdßËõ>¿5oÊÌ»£Zxä+ÞŒ>ýÁÚM§b;^;£ñuÍ8ûÙдW½Ö¥·¸À´ÎÜ uçž œ-§W¥²2ÿlà˜¥³cÎúO[r®EAê…·%ç}×ïYýzÚª.~Ê/š{–¬Ùôéë<~Óûï¯;½¨hmyƱ-~‰Ý¼¾‘Ϧoç¾üÆcÆ¡l¢¿ìw´ë×1yO¿òúØ`´¸|t±óŠÉü…•¾Y´ìz™¿X#3r?“˜8þ+î¤Ë°Åw÷¶Y”wíFÜà³kâÃ~ÐÓµÙRçÈKK¶æÞÞµáˆÛÎó²ð+¦ÿÓ‚9¥»vØÓðç¿´;óËÏ®qîø°Ôû‡ÙCôßÓ}’·ŸêP¾õ©Ïo¬\°í› '¾Ë:uè^sÛƒ'vø&Î-Nvº~n¿ju`¥Ù7d¿wÒûWŽŽa›±}1(¥ë¦Õ‡.»yŒ+juüÚè'vÛHþGç ¥ð™ö?þ±çPáÆ­üèñÍOnŒ™Úî°Ï•oy<½,X3¾èø ñRñ•”“—ûýzuÊïè[EŸ)†mÚ<ä¥×vsã-Š—³ÊöËLë—ýå‘úö3Ο›¹ð—uM\t~•ü}ŸÍ¹®K?0_há·2껟ÝÎìÍ ˜'ÿtÏG[?Ï\ázh¢Ê!W´tÿdŸÆ<Øpûñð„¥~ï·tÉ„ƒE=¼òBOïè%Š;÷‹6ÐùêCßI'”[®||j˶£×û#‚‡n¾ýb†fáù󌘾—\S_O럻åce²»ñ´ëú-[vü y@å‘n´ä‹•Ïefvgïa¿Ög§{ÒªÖ] ʾ5L¹0»1µ3ûçb»…'âCÓmógnܬÕÝá)»žõºÕD›p/-¯hQìd—sæOe£Ó¿£œs2&í‹ê!«ÝšRø|ã f•¥àulHø-?¹öYЇ]<}m’Î<Úûý¶D§V-ö^[xebÕsò•ÏÚN_#-k¯ _õ—ØçÃÕ‡­é08”9ØÕ®ÜéÖΠ6G.üùÃ[_¼1¤té;±Ù׃%3?xp”}<ëæ‡Ú,¯EׇÜS‡mÚÚgjEWÐlRwS$µlnÎ'£îì)ékÜ¿ÝFó‹¨D~wÓGŸ4-÷],ûæƒOy´-yž3äË=WŠøì½#½UIÃŽÎû}ÈÞØ€KÍX"ÞS³ï»ôÚ½’2¯X1óâæ‡žû*¬óÙÅ=»ëÉÙ¯Üqwºw[¹i‰ó´zú¾Ø÷¸iÉk›;hšõëÿ|ÏÓÙï´¾ÁæesN(7¶.Üà˜õlN—Ýljª¸2{8=¥Õèões{t Þ°~˨'ºÿnÓ|jPhì•ç+òÆÝ+Mº9fà û´OJÍ+ì+štì½c¹÷»ÞJ\ùY˜×ØŠËïË6OùêøÇí®šÄ'»O?øË {y“¯úG¶¶t8¿½Á’ùɺâ ê\¡â‹'~îØ·As÷ÙÛéÃÏ\Uˆç…¾Ù}!ò묨ì÷ÿü~hÀz×G»züRyvtµíA³Ã ž7¶gn˜éòñæ~XÑ­ÍúÚ¾öƒN^þª´äLÅåߪǹ ÕŠMפͺöi§›3:i¬}'³_w7eß|ó¶6]öµd?Í÷ï<|ú«ô×K~X=q@Û]{ÏÎ;ï¤[;öpøŸ¶67Qþ£í¼Öÿá2¶Ó—öŽØM;Õ|æ”g+K¶¼yüºm.×?š{#åû¢K%m6ä&Ú'i•±û¿—ÒÙnÕÉÖ«['+ÆOÙëöäéáYlü úf‹kc¦ýz§ôÙÅC¸‚„ŒF??ugj帊yË&d=h,fQø’êf”;?WMwËÔNVŒ¾Jßùnâ¬È^Ò;y²‹ùÎ •ÛMýøÐΗ~û7F\?šuùçc¾]øM}k¤þÆeç ?åõÞ¾Ðëç7E=3¯ Xt2wlÓ÷>;ôÙSîެû2åøvÊS’²yŒM¢üŒSáþ}Ž%ÎX°tØ$mÓØÖ?%…wî=û½J|}–íÆ|·c.):ÎÚ—`°M:ÔvËÉ;ÛÍ)½çØe}G‰*º­·ß¹¢Øe£ëÂ)ÎGåÓzw^=>bÃݰ‘â©SÐ?žpT(²œÜ– t¨ÛŒ„‡Ã/ÚŸ{gËè›. ³6îÍÝø~Æ•‡èݬËAç¸äã#U©G"~y˜Â¹}uò@’4é¶“n_þW÷/ôø&?zìzû&Q7šr¿…96\;^ïáºwť˗·™¿Í[ës5íTð­'óŽNüó‡cC'ö9dû+»¿{àUgåµ=-µÝ¥ý^вy¡·B¥ïœxkH×é+õ|bnÎØË…s-ÚÍÈß:pÒíîg¢×u ¾/izxˆMûOQØÜÑSypÄ#L‘¥]±ì8”{¡k§ÂÍüç7w·þz|ÚÞaOÚë§lÛ$™¶Æa“½í®R—î}"í&&؈†6Zw²ßâÛ)óÏß§Üí”ÔÒç‹ü_ÿ~é„ó‚’¯ßzoIAþÅ?/Þö›ôëò¶×¸éq'šž{q¸ýç3öF»&D'ÊGܵPSf†Ûª²ˆG®œ°ÙR>n÷ä±S-M#.¨·:ÜžsÅ9ÿþ2npé´ÛÍ mZ¦™ôéôèë»ó¦ô+¼<â†ï-ÇðO®ªï:¶{ÐÍ£›Oì¿°Ðë¥äøñ'­²¤-t`—ÇÁm?ÉínsîŠíšÁ7‡vp/œxò@ø¼.§ÆöK5©¥¤ã åÚƒŽ'>IwZñí›[-;KQþ›·ìJÔ}ÜbeçAéã#NËb¢Ÿ©?±cI0½>#³p™üÿcï=À¢X¶µá˜!眳ˆ ÐCÆHI‚`‰Cè0d% Š`Q”((bŒ`"‰ˆ(‚EýVàÀÞûxî=çüûÞç¿5ÏË[«º+tU­U««{†û×+Ì¿ôîz5ò´áB‰5[I^ŒåÉå½ÛÓÄšV\ç±µ{*Ë1üÐmÀà€J^Ëjí’>±´’BÂÖªhÚÚcH®Pò×ç¹Z G{Âøû‹=2Ðk¿»Žãäá­r™{Nñ¿Ê.2‘ºšîö±·ólïpó›Ú¶¹guE$gç¼sF&§Þù5*„Oøêýò¤× °22/äÓ h7ѾéÌXÔl¿iÄ„V(Î$^¿¼S,9t?cº†à©Êo[“Êçå¶E.)Ð Žü±±×ìªAè–5mŠK#­ oÝcŒ®[+on/vóÿ.\G…þYÙÞ„j§×uÒëØ¥+FV²¤dïãMJ¹çÔˆ0·Í¿ºïï´«/¯p¶?îÏ¿#IïIr Gj[KšÍ­Æ\î÷_Œ~m8Hvoqò‹ì×·/i[k½õ”ÏN~Ya9àgKe Ž‘&ü²7÷¬ëql`žgÕ¬ã‰KÎ;-´ëT—zv£´bå„ëE¹ÎüKzÍNñ$('2ùß1“;ßlÇß÷-ÆdA)ÜGU/ò“–œÙqfQ žGQÓ®rh•YRq$©-Ø5f€ø¦gøû÷‰ëÕ›“776¤/MØ¥õõh2cEa¥}DN6IwqBš…LZ. sÉ×;\y ;\œ­MFü9žêÝ¥\çØ3¼ŽÇ°Ï@–îšðµÖpƒ¸T­Ó½Ê= YHI]¥èÖ­› v‰ø¬«XoÞ›¶ÎÿéS~ek 6­—ù¢ÊÇ—!b/ËÒžô¢Ýî<÷ýJìî¿©¸10â8p=ûЧ§)ó+ß^KÊ9NlóÛ¦8 XïÙÿ0Øì˜³hP§‰¢ŠW›Vglc:Üþ{X“d芆_‹æ^?ƒnMö^p*]¾».¯t¢íÔ—'Ëšz'¢ãrý*­›(:żìüVØ…bršŒü%MÔ‰á!fÁ—lýšG£L´<œ‹Â¾¶X=èìáüÒXÒØÑ²5)?…gÅå$YÖéÁ··V줗u6Ññg$òëÝPààØæm¶<Æ>—~uäך•î ññ:u’i²#Š9²OœãOyñÚ·&®kqw?r/Ò®Õ¼ÓZÐækŒCt‹òa÷F±GÊI»x5R*VIVô&3¤?mÞ3üÞ”tý¢¿Yr`¨]ºê"­^YfÆ\&9ûÜz$¥”ìÈî}•fYg’E½ªv)Îj¨ìxל”Q3Ñæþ´g@–¯²³Š=»y\~W‚ž^rQLp³\™oÿØò“Åë'£^üJêbÖ|°cÖþl¦VÊœW˜Ásjp—^£úA¹ 2¦ÿ¸ëµé·> ¤3¹º–‘9çCÃõwø¤ÍEkm¼¶¥œ÷ïcOÝö„mõ@'³ÈO¦Öƒv,'•YXL—ŠÉŸï¾c*™.!á*¾@T•õ‚H¿Û1¡óí×…}KFy4‡nó~NûÁ](–+de2ÿ’u||ßQYÒ½Q‚QÝ–:–&ºû¶,¾s`ï"Ö±2=£K–%ne]R³hÕR:’ÖÁ5ÚÇ·i>«ÕÒÊÅ«·¢‡«=ôëSçÆ½”ó{½^î¾ìùÄïòÊëïȺ[®”IŸ-ÕsDFZn˜_1â’‚Ry›ä‚qW…e,›”7‰,,«U½}TU;óbðѤšMkÞ|‘X›G~¡~$üà<±0[k%ŠàGrèãcÆ1;_DÄZ\¶ÞÂÎ$°¹É]:2Žr&b¹ÄÁ(|z}ôÍú ÿJ•‚€£íA:_Û¿îëW’è¿1üýzâ*:ßaM÷üX&~=7¥ó^®o¯Ù’ζ<òô¬zå%C_îÝá›dÖÒ eΟ!ha'iµ"94ÒºÍcÜJŒ¹aåº+,Ó^f™Ü8šfÊ0˜o¬oõɈ*wÍச‘>çšåæ=І»÷ˆÛô±Y­NçíZ¹ÌáÊf»±š=««°ÙÌ×e{5æñº.ÝÞõRvík½úלÙ^èü.YÃiÁ7ú AÆë\.ªtjŠÆu|Þt¥«°é묧=KŠÞ|ê÷ºÔ[­;Š0 ¿BçC·çûÌ“ý®#n½ª]Ýoûw<|w^kWŸ¯SÕoéàøý·¾§úŸü±š!÷«×Âo­Q¢_NÞX>æ”:Œ¼5ºž;¸u©üGGîfƒC£ J§?'þúÉúÇݧή­åÚŽÏÆwr<_öJ·mÓhe{ñ©Ý/G¥¼Ð¶xÝâÇÖú$×kàÑ@ëåÇÊEñÍîgð÷Óï©?ì Þø@NĹnÃw®ÇÍ¿ùL1æ–¾ùuG‡ÆÚÃK¾]{Hͬáî=Ü`™ŸS¿¯îÔí;Ñ7ï°rl4b¤ÜM 0lªy(r®L99?EÀxø¥å*ß*§œ]v<ÕÓþüa™¾¡Cqu§*oŒ¹Øt\‡7åÄ×ÈŒ´’ ¹#áÞcÄŠG‡ïoIÚ©‰î±paÜËÞ¹a_Ónß]q'Þï^þùùN¼9uÇMÙ’„£ž;¨k8ªã$ zâ_<»¿õ`£ý6Û³Û…%>&³çv5§çH•§žóÂß;&ðKÞ»ˆÍù x,.]à¹pñæ¼ìñBgÕß©ž ]–uEÏ }lÁŒE†Ã§7OœË¼šz¢äFHó%á7—õ³¯^¡ÞÝYt÷9[1gáâBsN÷‚ݤÕ-2>åüf.¥vŸÊ’+Ûv]ªëH¨^·áÙÕ4~LÿK+½Iç˜oïæ)Éÿ°Ï=ÕQ»mPmžŸË‹-:eu—¶0Yï ªêðlÁmÇ­xR]:´±õeG{¸ú.aÇS½Yèׯ\§ø–_ÛÔ]sÛûVöÅP2“áÝ{Aò‡/Æ+s ñë¹òm»PóI‘¦Þù™!û–Ü”ºÀez']-™ÐÞÛëY{—mGšUmt|Sß»°m[;صôuêvRâöû/u\jÚg½.\]A¯¯›+qÕšðWúý­ìc¶Õ>ƒJcf©§n¬“A»N:ZÇš}º5Êtð±~ö–€ñ!ÍSÍVI²W”N¥ÕÍWŒM/Ÿ0¦3ÚÌ5²ï@ŽYïçc|k–Êæè­!Šï¿¼3ãóC¾°¦ês8¯Æ2v2ÔºnÝü GLðÊHnéÚ•üíKî_³Í7&®#ºA•£6Ü̯33¤Ðç–þÖ‚§¥é+‚ÒFÇþØÿÁË&Æá™¶(GÙ|ËË}…37ÅØvœŽ^ ¢ëÝ«ô "ù` äÈbùÏô G÷{%«êíXppMÿ%#W{¶1F¢„eNûÙMåß´¿7Q)ÝiLÐûýü`ºMƒfwVª¶=ÿ‡z¿Yí§—~üa­/×´|#wq’»µœ:mò¬ìèêM›—·{Gˆ€ö€žª?V¹lþ é·’rGVGìîhÉWw*¨ï_{yñê×÷7˜ÚÄüx/üØ}±´ÿ³í&Ì·ÕÞ¾ Þ»—ªÐòÑ~Û/…Ôn—Ekˆ‹²÷1'Hªö°'^c*Yt6 J¿ó a<ñš ùÕüQ«j2aûNr§¤)]J}*1rÇ¢Æ,OåÁ®f£WÖç” bÖŠº‘ΧRR_Ë~²ô&Œ¶Ä¸¾q¿¿×¥íò»ëÌ{WE•¼ÕöALž6WJWîh'ö¡ûdž|q;ºñ$ž±êlU5îZëÅKZüLÌvâd«£Y÷ü¸ÈÍy«“r€ëé]Å Wst„¹Mâ²Âd[oz…ºFá¸ÈMyßëCîs<óQf@(Åtâ¢n®1÷†Æ0Ë ÷>îôxa$Ÿä9fÐûE5ÒÖà „†|á³wGŸT¹ù–Õ8cð ‡{ÆJ_Æ%Çl¯n¯õ<åÀM•ï6òöeGNö¶ïqÞÄ£Ñ)ž«¥r{—ùyz½㊄x©WÉaë>ñš¿];ñqõ²4*ƒô£enF7wß;£Ûô$Ö@'°VÞysØÎâõž¬Ô™QÈ2Ì'Ñm‘ÏÛàÛr>œ£¨ìz†.æ¦îñÜïqrüG^Ãí?ë^1ÛÀD/ìeÏ6]_Ô¶ðÐÃ2sf|ü`aiÏáÓìæÑ¢…*†–™} \«üöù§Z[ïä³¹’¶{Q^en‡Ú §ßäGâ°·µðða¸ MËû™÷_2"Ÿ_2Á~p½YlJf‚8v|¦Ÿ_²ösZ¸i“èpþ—L‡™kZ&ª¨©¨céž$/7 Ij2AÊ0(8’ìëíC‘"êêj*«¡¨¦Ô ’[ ²Ph°”©›¯¿¤v]¾anþ¾žRž¾!·@’%(HÊË,åîæá÷gÇ=‚L –ƒÜ ù¡™ÖD@>õB< °äGP?/Ä7A¾Œõç.À G ?âD.…—f S@ Ž7Býœ÷ȃ|ëïD€ Èï!?3Ä7$@>õ{@<° äP¿(Ä* Wsö¬@§¿-€®?6þ›D8~6þni3aV3Å @î¢ÿZ€0Èhã Ðùmü} @.ÆÚ»`òmüWø.&:ÜÔø‡4áx=müaÎ2Íù0Ö_ÛÆ ÷ÑÆß òQÚøo,ùmüÊ —cå%,AþJ,/\-müC0Ì7iãõ)¤3ŸÆÎOÀ83÷ÐÆ=ú™9…6þÑèæ'´ñMe‚ëb¾ Œ•·`ò'Úø;gŒ8@Žß£¿7`>ÈyÀX}Û¦  ¿ Æù$mücz ·ÑÆ?  òU`¬¼}k¿ÿFÿ¯ÍÑÿ d¶þwÎÑÿä9úŽþ"³õÿãoôÿÎýÏEfëÿÛ9údŽþ?£ÿeÈlýÿòý¯›£ÿYÈlý=GÿÏÑÿÇsô¿™­ÿ£¿Ñÿ»sôÿ"2[ÿûçèÿ‰9úÿrŽþW!³õâ7ú_;GÿO#³õÿÕý?8GÿÎÑÿbd¶þÿFÿæèÿyd¶þ¿›£ÿÇæèÿó9ú_ÌÖÿo¿Ñÿ[sô?™­ÿoæèêýo™£ÿWÙúÿù7úß4Gÿó‘Ùú?0GÿOÍÑÿö9ú_ÌÖÿ‡p8^7"JÀqqˆ0‡ç' âœ>"àèøÒˆ'ÅC`FD8&B<¢Mç"ÐAþxÈ/Œåâ&0bùø nX~W.ËOÏG bùQ;–ŸM€pm*?Â=ù+ÌL# ¾ øk¬à“±!ô;B‡p ¸ÉÿAâèD&“<(R>$7OYÊÇ„9@¡~AáàØ“I!!¾AR$ŠOç”3í…ûz…K…øFMù?Óùh¥oæ"B¢LûEÓµ=¤|CÜ(>³Ês÷òð“¢D“f×B "“¦O9]!ØqÌm p Œ¤¥I‘¹l!‘îAþ!³ÊÁµéü3Ûõ³~_ÊtQdR0É2çøl¿ïWsýÅé#¿ú×Óâ6Ý»3Ó§=Èé#SAÊ7ðw~­¥ÙOÊÀÓºQÅödàšóá¦}xh^Ú‡öá§}¸;È l¯æÔT°ùÆ9…i„ˆ nHÎufýÓuN×…Õ!AøÁG>"ð…|Äá£GG'002113³°°²²A`‡À-pÓ-ðÒßœÀ?'Ì ‚s‚Ðo‚ðo‚Èo‚è?b“×' 0B`¢fZ`™Xç¶ßöߎ1pþ‹f76šÓc7wœ~7ÿbÿ‹Šý‹Aü_ Èonèæ€~ðs@˜ƒ¶¾¹åÏ-F˜h`¦…VØh`§áߦÛÈH“a6KÀÌÀB`ųѳÓqà8gÏ­ÜX h,\øÞÄÇN€€%€JÀfÀ8€@,\È2î€.ÔŽ¬<¨ŠdÀ€° °PPœlôDGëÏ‹å€(ÀW `¸ œxz€€à @ pøàìXîTy€@À€à$`  ° pøÀâ€ki@À Ð à$l÷D@!`àãäÇ„ì˜îr¾€·aÀÀZÀS€.   ø ¶°ÿºW˜È¯ü€Ã€Õ€ÇM@ 0 `$,wÊ€‹€@?@pà x X ¨lLxá xqÌÈAàƒÀvÀvÀ««‡‡³³ïÞ llÜܬ¬|ø<°°ð;àwÀ¢À¢ÀÇ€¯^üø9ðbàÅÀÀÀÑÀÑÀ߀¿c«<#ðvàíÀ¦À¦ÀØqº<x>p6p6°7°7ðà7À‚À‚À©À©ÀŽÀŽÀ-À-ÀÚÀÚÀW€¯‡‡þ Ì Ì ¼x°5°5pp°*°*p>p>ppðð°$°$ð)àSÀ.À.ÀíÀíÀzÀzÀÕÀÕÀ±À±À?€à˜'-…>@ à àØP@ð¤ƈ˜>!À€ €äž>€=@@ÐC C@-@pàx…0MZO®ÉYL€² “ž€  ð0¿_dÒ3$€N@o  ›Ð?è®îK؇ÀÐx'ï0W0¯Žc6ÛòãfØÍi›>mË™hv³ÕØ7ѱ/×cß3Ãþû"öÿC¤Øÿ´@:}€ À ˆ¤ja³çÖÏ@«—VŸ­”V¾+­ÜZæ¸ÿNÓëC)m_šÆJ4Fi¬Oc»Ò8‚Æ;æä $‘°› Üh¹‘#‘ ™ä E ¤Ýoyùú“¤HdryJž>>2yw1ã¸o`H¨——¯‡/)7nAP&Ü!n~ž‰Ý‡»Q|Ý¡ü0»ÑÃÒã¦ڵŸÆ«ìŒìÙUuÙEë—2‹¯8ºäm!–îµÈÉÝZìK q2„{£’§/Ü·8™D›“[€§–™Dq2ô'þº—œ#ª{ºOµ{zÿ9ÆpƒBBî‡ tÈ ÜÔj­¿B¤Áñ™2Ž 0©áƒémèPiv<Â>³¿›q•AV€}­O¦Iù<Œ ­œÙ2LÈ‹ÌÍ‹ãܲ( ª„ Ö€UØ9Ø?o›†Ì’q,x„EƒaQcžj’®ó0N«g¦Œcc@ØÔ˜:h׿‚G\n€f>bC¸"ˆ`aùôµ±LÊÜʲ¤|ª¬Ù2Lrȇ@~Ε»nÚ±ÙéÈ,'þ…= ýÈ ×Á©ÆŒCÙ”‚(x2#RöX:+–>ÈÜÁXƒ“dC$íYAO6DÒÕx¹;8›Ùk Ÿ"ébö¬ˆ˜ "¦&4(ÐÁ×ÌSÃ<ÙOž3úIƒ±!"H8@®bª]3eÜ2ð34hí²§µ +Ÿþ”#mÓû8(Ò!Ô,Pƒ›,‡àø™~wîAÎöfÖæ`&WFÐyœ ‚´`iaDx„áëfC8o°C»À’jp"ͺ`!È™ÉöÀœsn¬Á;…s?œƒÌ–iãoÆoŸÍôøÃúC2KÆñ1!|Ð×|Ð×|0|j\S}ÔÍ€4Û"ˆÈ*°A•´úfÈÂràÛá„–A¦†f˜O®t6éa‚ýìº4áº!„0iH!Íp¾µØ«JÚ¼€täéÈ,'‚ˆP˜Ol~0""0"jSóarq ôƒ`Z§=éýâ «œ¾ö¹iÈ,ÇÊ€°ò0Ö‚ñ®“ú ÀȌÀý@7¬jX‘A/(Âæ;M÷fÊÂütÃ*:»fÌ´ã$Ø +"º aÏŒHh€ïª&2(Ô󃞙,_ˆ yÕ ë)VZOz$¬äcÀZ3eÔ¡F‡â8±¹ó›“Ø\d‚UßAsKšp@› öx$Ý Ar#XY ˆØnOX‘o0N¦7Àyž¬g®<3ïd?ShsCð˧ÖÈéµrnøùýðê©ãÓ÷\9Pv#`ðꔽÃl¦ÿ°&LÎÝA멹k¢Š ü_ Ð,¬Nê©ÐÍÚ¥òð'ÏØ\Tñô÷GhÏr§Ù?ZÔ<З¢æ‚x‘I$„¶36ÍØY´(í¬7ÿ $Êß×[c`å4Á¶!…š_×Þq8Xâg¤q\ƒk€4i:fiL3Òœ MÒ«¥%b¿Ò÷'L ÿùïûÛ†’È‘6$²W9Û4 ÅÖcðèLIÃP2<{ð<Í=1ß Rí"C(¤{ß’Aˆ to ŽØûzøM ¥•mäâÂTö•¾îdpQ ¡—Cà¾Æ0ÈÞ-ÄÏ’`0Ùëp§÷3Å.·`_å€åpß@eð”ƒÉA˜3£ìOT&*£“ãþgçxÀ¼ LÖ2óÔ¹g†D†ÀàýÃÒü§ì„m=ÿÃ3aBÎ:þŸ 5…4ÿ°`б_ZœüOÔ´ãú—§Ò›iDZ€ýš(çœrl Ç~Mtzÿbú‹î:K=ðz„ýæÿ…ÿIa¦ßŠñ~°³eSÜ0*›|÷YËn3ø\®À×àÆé=Ä›Á.3UÌöGgú°Gà VDÙ3/@=eSÌóâxÙ+.•ÍöO1Þ¸Q6Å?­eSÌëÀû²ÙþàLÿcSð[í˧ØàS>Å1€ˆò)Þ8T>Å)€ å³}^Œß¨Â5–Oñg€ı{1 ð÷*Ëgû~çЊ)¾0‚x3p˜:´âqÀþ`϶B<˜KÚY1Å °®í©˜í›a¼Ê |£Š)ö„uïQÅ¿ŒUL­…&6Sý6Ó?ø{ü_9Åàƒ‰UN±=-ޱ'­œí£a\°¯œâ{ŸÊÙ¾Æx{èÃÊ)^}X9Å’€—¶[m…¸+p›´âqÄØ…Öfàƒà?쨚íKÌô30Ö"ÁšwuŠW§¸`„ù À=àgÙ_íoa¬êƒ +«¦øÀû·]¾Rˆ¨šíÛaÌì×T5Å’¡ª)F «¦XĪ.)àcàwuWÍöÁ0– @ŸTM±4 »^ðWÂC§®=xs-ÌýeêGFA;! óåêÒষâæ¦ã¦¦üÒ™±ÿ¾1×A¸^ú?–x†[¸;Äþ#« fŽX#V ›Ã_ˆc¡ ?ðý×n=6[¦XV¶-B7§l:zì,;„‚ao(ÍñGHPr â…Á9mtØ9(¢ÐdckÑJDÒ áœ$qƒó#¡5n M=𵄒< Ü $àuHMžM†³±¿nâ 1l“ÂVCÜϺ!“y}áÜ_g/ ÇÉ?ë$Ož2ã¸";£yÖq"¢W ‚¨!êÇþb2 -sGB'sQ&ã‘È"`èkkd\¯!bX€d W…e“íÅúˆ2YG \ÿŒ«ÿ];©ˆ*ä_ éÞ“9±3ƒ¡V슽{Þœ -øçûPeòL(ËøW9!“V?ÖÎ0øë ç"?ûÜšv®/í¦û.ðŸ¾{Dʱ™l¥'ô¢ÔúçA´IÎ1†¼RP#™vvøä5aÎ7NŽãì2çŽæ¿k,&5ÆÚ@þ“ÙkÒ¤æØO¶2ê÷Ÿ5sÁ¶á»ñÔÚÿ ÿ¿ éSûà9é¥é·Ó[Ó{ÓÇÒ™2„22t2°Í)8V“};»9»5»#»7{0{,ÉaÊIÏÉÉAš§öÒ‘t¦t©t…t³ô¿ñ:þ/ü·Âl¥£CPªž³b¢Yâgv#]:UO’4ép8"+ÊÌ@Pâ §! èJ%GÕ¦ÃáÓ—¡KPÁ)œ(3=IÇgÒÅ!¨ôŒ|x¾wYîµ ‚Äê|»ä|–ó~²'ßr·t*?¥âkQ*ýùtz:/ö.ý¦b•]~Ôs’V“-Ü„²ÿlŽMŸlýj</Ýj;"/Ê L¼,Žn!>¾Þ” @"Ê%2ò2®"yz%P1,……—ßÒ׃äE‘2 "‘ݰgDiT;NÏ+ôë8¶¡lGq –²14@%Ù‰š¨ºJÔ†?êë@ÔBµ~Šh|ñ¤eì(+vœ•oim³Š87%JúûÈRFvÆRÆvV‹–ë*«£ÊÚD â¡Û-"Z°i<øòÃÆËû×àôžH $ó ±fýœÅÍ›vs›Ã†ê—Ø´KâúFy|žªD'2‘óE?zÚ,`]£Ê*©\ÿZ´9ïíÖ‚}¬Î!¥ñú²kö¹û^û0i]ðxä]¯’G '¬Kq±R?õÂÑ÷ó ²S9dNܶ °ãÖÇ7ºy–ö^)/õ[Ç–ÑòÝÈÈ<ÁX÷¨ [Çkƒƒw‹º…\<¼G)L&ç|°Î%œÂ¯åvm§F7¯C9¨ôk?mM,ãŸç^Àú éãóc0cïÁŒ=úeÆp, #óZ"£Ï^Ànep,sÍXäÄXÈ¢ÒSJ/2ó¸'IÊÎ×;JaÈ´P ¢¶†š†æ”!Óý)¢ñ ÿ_2Úéôqúo Óñ ­ŠNú2…'&÷3ÝʳL¾y,SùbúàÖûþ[G åmC«ž5r2ðqŸñ>Yãbi¿³ÛÒ¦5én†[V8ïQ±ì~vÊçl‡È^…Ïv £<:F¥^yÿÔtÌéð¼Å•,uøì½›·™…‰¹™œ¾å¾ûÚuÍsßÖÕy°4GãE£Ûc7[_2 p‰Ë¿ü9…wÕ@Ù}KÝ×!mfVKøÎ¥²ë6î¶ítº¿xp¯wêšk¹î„aÕsÙòÎ ®Ç-m3·žë>ž½ôÉé!³œ¯…VYiÕƒ|ÃÈí³'ë¿ë´ìäV&”ÒYË )ó.nÑ Üês^JHGá»X&WaÖ´ar…qú3E¥Ÿa­v~g!öÉßí ¸•œ\vhO¦›¶j‹æÆƒ½8c‚Íu”ˆ‰^Eu¢¦®¦ªjëªk¢ÊD-/7e -TÙ]ËÝKY×SÍèá‰jêêhÌ2€ ܽõ. ¬ÁÝÑVQ([y”Eu˜2€Ö(˜Àt0‰Æÿ%sf2LbTGY¨ «/:i×Í0V(Á&pÙ?gÿ¢lÊŸÙ»³9vÉí‹qßݜֽódkýütëCÄ–ƒëþ™û‚ oö¨ë(µÞ¤ßúNýÀHn—÷Ýó,q+CcgQóWÖƒ1ûwp×S/žùv6×e4ÕõvôêÍÇ|û%©×>ÞÝaá>ÚÂ.ÖbÇó$eÕ€f•ðÞtýƒ,YÊǯ›P˜Þ=iÍ2ײçá^M_-ðÍôû7Ÿñc§.=žHõÌj]›¾0ãþ:–ck ¹Í©'ãOÒ¯_ñJD…pÁÆTuÏ×ÈV ©1·~B|_Éø+¬ÙG=ßs;[›'-QþÖ|™ÙÎEãP‡À²¾Í^ÇÎwµB·Š7¹míºl´óÐéD”J¨{wfÊÞ±¸©Ë‹LzkĹfÎeÒz°0ßyph¡'NX€:ž(Œù3™Ž QUš² r¿ìª  00P¾^¾Øë衟 ²/%rÒ˜¡¨Ž:QM¨«®ÆL&ªaâßiggÁŠÈk„QÏjñ£®RRË„Ùù/}ÔØð±Ïï{ªWGû"J‚ÈÕtµ÷?Ú®/·’}LFžk:²ì¬Ï“2ô¹`i‘”Ui±é˜)㳉yí'Bw4 1Šm‰>\5¤u掓ñ‹ü‹K;|RE²³È!uOh"§? s‘7Nئ#вžPî½*)«ÈWõ™0ë÷”]aªö/ùеc’Ü'mÊæóvë£Mä\ 2·´­–¦«-Ý7C‡a›“•UA‘ vÅ¢ÅÚãÍe÷ÆKß\`B>™dœ¼¿~¼]oÔ¹C&MÚKtN^ wÊ<™ÔÀ½Ïaɵ Ì.ô§-Øè‘u('fxq¸xJ4Ãzý©%ÁŒ•8'30åa`¦Ý…ðãð„É‚Áýý™F‡•2qŸhõP~×áÎ4×Å9Ä ³K*[•QáŸ'ñÑáÙ$X;$qG ƒY¶ŒãÕUßa~êëy¼ãŠ,v‡×vŸAm¦l™9jЧ¦$êýó¶ìça2LmÌMZ1ûVÌ £<ÊéüW9La §Jý£ý¢Ã!ku—ÅÊ›ä¿ Ò/T+ÙøŽC50Çüó;—Ðþ•‹•[ /²~ox«L<-Û¸Ù&-NÚùÂRÕ•å™9Ç_W”^‹,1'^Ög[ßÉ&èÛu\Jù+«Í ‡»Ê¯V<¨ ~“ÞIŸåÐQºËÂqèðòã‡>¼J”ÔXRêptÐNv›âªØÁ®CŒâC]Vc{2ê{y³’­n‹>ØG>¬¸)à˜È˜Ø ÝïF™Nâw3÷TÍ/Šôp0Ê´½ûåíé5/Ñ©ºŒ<Ë{DU ?s˜·ûï›ÜÌ…Wo+qqöy>šù•Gž™¤sèc”䊊û½Í)BNw4\^7ß«|õ¢†‘Ø.~Äù¥æzé{i·˜?lãØcÀÁkµtó³ãäûÃþõ×ÞŸv<à¸åPRº¨ýºÏM§½Y(YZýʪ‚·{ÈÚ<#A…K¼©_V%© $8v½äjó ºgòè¡àÛÈøK¿-l—ÜuòË7Þùú»¿tæÆšT0ºš’\õ­ –¿·ê/‹leÑ`‹#JvqØ¿|ñíµ)×EÏ´6*›« ÒQ]‡ æûÖÜwøNRë1é l•+Âüñ”‹CÑŸäÊv4mÌ1%ª}ñjÓÒ$ÆÝôþ½wJ…¾r“®^šO§¿ñ‡ï±”.®®KÚ6LOj—¢TF°ßÓö[Àgò;"öwØoT| °Øšê¨.f¿‰“¢:Љßíþï¬÷© ÿÂöçf7û©wVu½ª;b+ksñÞK!+9Î÷³ï¯¼HA¥¸ß1>¶?Ìo~Htù¼4'Tþâ×]õ~'#çg|ÚàÎFÉu¹í'†F¼ÅŽG¿Ù!Þ÷ÆêtÆ5Y»ú¤¯ÆMÌÍò› –ã3¿œõ?èÝ¢ðÂÄ® ±ùµ‚‰Êü ‰Ö«W±uÓ/ü¶qÿ~4pûðZôÄט'©Å½Ò©1cx‡™®Ø¬ºd¼ÿ”²ÂÔ‹{þ¯œÔî‡ ñ+2¿lÍæ6åc¦žÚÚ¿:â; Ó6„ 5é¿Ò&kRqCÙþT¾D„1¼ñXû℃nt%âì…ãŸáîÉXØÿøB¨½.Å:m½ÏCdÿ#ëý§7³¬7×Lë )Ÿ6e|ã÷£ñIn~3<θýǧ'•+ò¢@ÆŠô¬‹+CÖŒ0òªþ×XýêÖû-‹Ô]µNôFZ/ß^ºþü^¤­%®P…²i}ïù{W£÷•ª<âÉÜà^êH×`%Åksäe”~—cEþš£bâ¸Ä C»›ß/Æ}躺…p;ɬkÐŽÿ¥õùÝo’6>Ž»ÖshˆAuýÛdE9™àoŸÆ»#ލ°fì ®²:±×…|¸4C÷¸·r-GŸ»“ž@Ún)½.Fµ/ÄaÄ¥JdÖÛ}ÁKlcám¿Îâ¶w°¥TðÕîØ:M¥ §«ßUna]ýÈŽ,ý­¯ˆ 9­Ç ²ðqF½6ßþráÆ¥;3ÂÜ.fðž­>g:È4±KÍ¿è{»íí=²w¼ªNˆoçñ¤[ªœ¿v_i·ôë’‚zKö„G*6dEœ/NO yz`;o¨ŒªZS`úú=óªÓ¶ÖK?y'a}çèóŽÏ8RÐNÖ-·}o÷öe§Þ#.øÁQ·Þ©ÕR4£õ«êI=•Õ~wxOO© ,Qês:uûûüå?ß6ùµ‰œ?¹k´ùËLOd›¹ ø%±9ЙGù1gp:#žFI  ‡àõfÞµôû¶…O²³²ÉFãÏÈÃFŒOBãw¥ÇþÃLj.à.j Ú†=b&A\ÑDÜDbÑ‚3”÷€˜¤jÀQwˆéÀ¹šîg© žØ#Ð ð7iZ?Kãç¬Fx* JÐ8ká"fd2²ÓÞ/¼µ4ûæ¾;Ç–rÕ<æoå¨vvqí<ÛÁ+ÀÏ›-k2^ûñéÖµwË¸æ¾ OøŠI<”mBZ7o½LUÇ*çÕþ7ÌüÍËS†TÇC=ÖwÝVîÎÁÿp©]|ˆÓ$¼(4iÅÆÅ 1!~œW2>+?&Ù©s[øØòW‚ër؈'`]rwûëá¶¼ÐéH3=uxuÒ·²“‡ÚUÚxïl¬=¬1ü]üùë¦Û‡™]FÍÝ*7. Èlë6”gXwÉ"®ú¥¾ñ£Û-ý{wdÛŸ(4ë,ö6cØŸû=¾~±šiê éQÞ]É÷´3¹I·.F‰òfPå“Q*ܯÁe Réø!‰{r:ïýÛ܇?”1c2;£B3ç2믧/8¨üç‘sr·N[MÕ$ê¢pG;w*oYÜËr?GAåYa€K`ïòÞOs =6WÚýj-DV¬ž·ž¿­¨Ëü¬—Óç—Gmgþ6$$R:½ùØŒGa:I²óOËwLΙ£Ê MŠùLnT~jrKþµ­–RC‰Äɽ " Q"‘¨5µ‘Lü)þ/ÙH¾Þö¤©ëJ,7éG±Gî0¹ì£Ü;—3bEFkßÿüœä{ôü~â†mJžÜ£ýþoijíþFªðéWGþ&uºC8¶4iÉÆï·y’^?Ô°´Z–˜²’»{ë±ùi;+?Ú"4Lè¸h_f*Ücrcωç;[å¶_1_ô"¢¬–”¤ÝW¹éì‰ {÷%íHŽÉÑž÷4^.wLj‡I_…Ôœjë^¶§Oû;´¾yúXŽ/WM–Êe:ª°Â4gðûÓ¢¨‰Ö^\e¼ñùE½"Š/uôŸ·vî×]¨uwjÀ'üvDв1©)"\W?žkv;™stkõ²¼üz–µ¯“v¦ùXè‘è©]yخǯäŸó|%n†cóc½«\–ñ(µ;t^³*Úâò_ØTÖÒPSòPSóÐÕU×RvW÷ÐVÖPÓ&*»i¸{*{‘Pu/7m5u/tÖFŒY çkÂCê9Amm™’€œúPºÃýTíO½æ àI¯|æ–/6]°?ʨ¶2ª3é–»ÍpËW£vpe¿ÜrãßVð‡]å?VAeú®‰Eæh÷¤s~’˜S~YS$ikÁ¢ÃÚ]w£ß7Ùu8Sˆ( m-óý`¨ƒ©%ÅïÈv'ÊŽà,“ KÇEÓÖòXj\m~Zì̦þ¤•ðIS4f§Ùµ×;³”iÏ7•øàóÐgIæ…mß~4÷ö>v]ýÆÈϲ©ª-ëEÅã¨Ý_™Ý2‰M ùR{ûý.Öϊʼ<û¾ûå¤m3¿·è¬FŠûó=¯ØžŠzÜú*äö¾ a:ßݸ™'ëòÙ*’+‡¸úZvN5§˜æhÆøK1¥¹ÎÏ~x­<©/Ÿ»ò»ÕN½”ûþu­y ò^Õ¤WŸ+<¾æ,PŸ_o5¿þòÇ•}=Ù,É*µ[·ã Ç–q¸>m¬Ûo½›{džp¦ŠW»ÎÇ1/Sºà}Í´¿(Ç9jIaÔu×c“A§¡l¥nü®äí {Ö+õûA÷¦ìùÝʉbýšMy&'öv"8¿ÿ€ÌO¸mÝs}Qç€XÅç m“׉çSóB£Â>œ´XÌP/™ãt!‰_Wº§ªózø‘ŒÏrŸêH¬dýòáGrÀ¥]+w(x´Úx8½9÷mõî Úôú¸`S¥_|L]2É»™í8ô*’–§óêZ\7HPEÇÖËI–)[E²U¼r>¥½ñ©ÙŰªo¯úk­y|`ü}ûpá5¾\^ÞÑGïè´h¾ñÓA©ŒQ(•à>½OñÿÁäR@?w)ˆßñ1µj(:e[ü3wË¿V"ªê¨¡šºS;:Z“"ÅÄ¿}Õ¢Òýq= ÃÖ:X@çÎ~%s‰©\l AÙW(µú†³8E…eT=^K/ÖšbËÑÎ}±bAü[Œ³Ã^ë+’N×:¯rO¬÷ÐJMl6<6$qt~çÝ¬ÔæÎ¯Ö3ß¾Œ!œá>Ca¨$ßU7·’Á7EñWÛ nq(|h#©¾Oý”á”ÒŠ<É0©Z;œšÅlaÆu$N²YPWx,KÏ8\BóæƒÌÌSQQ2_ÍKžÿf*7zr¬ÚïŠEj׻Б÷}ÚG"…,~<)–õ íÉÿ:¾ûk\Ÿïâüq´¿ro{{h€GòÒû'¬¬«ãe2"¸Õ¤£ X õ¾å4žq¾–±ã˜ã&+3ãšåwŽ…­g‰3ó›ˆ+{“ª”vîÖ—Šme+ļ‰šhJ3åJ–æS¡ÏªgÒ>¶ïØ…÷¸ÏùÏnÙÿp¬q;äUêd˜}zÝ]™¦¡‹»v¯¸¼qá×zù‡™¸;Ž'¯å=·½%œj~½¿µVwÉùÃñçÎÆr]-=622¯ƒãj`ÈÓ½77<õ0;~5w§fúkÕ-C?úÖ8ô®ÒjÍÈ=Ò*³)TøGN ¸ÐpÈv9—üÅ.\å“Çô>WSžfï5÷ÓÎÚR˜·ÄiÕFrߦ¬E™Ý×Uµ¯áX¨ï¾Ù)½=¹ÆÑ‡óÉ']¿l'1;|•×¥òšÿ6zz‘Ê« iš³öFxå!I†Gÿǽ*n)ëô<áÂáa.„¾U€r0ýhEo]ËBû“Í‘á{dêîìé¾Ó°]¿ÑEOÆ¥Ã3ÀCìÇ¢—ã7Ò1¬>ؘUk'Ä jðk+L]EM[k K.Ž7’_v&þòßþ®!j¢¡¬Žêh)›èü“oá¦S9šQ*Ã&”Š_;íê3Ç+Nª8ÝÿXW¥½|£ÕˆêZ𨛄jšS¢&¢›ÿ#-•Gå¦Z*þW»¯S­ûÝÆO®8{e8÷®ERïÇySG¿Æúò£Øs=ÎbA¨Û¾õoö±XèRWûÚízØ•e^Jëùíæã‰’¢"‡Åï1óîÝ솮-D8?ºîØjoÈÖbYªÃT¼üåÁ OBPGyêicöæ¾QÓø»Õr®jk\M†Ö˜&ªm• ¸y'»ÄÛ6\yàÚ³Î÷ÝxnŠ|IŒ«hw7¹6_\Λ‡,"½+ßÂs§{¡óD¸äÒAÇÐ U»–[êÛÞç<ΰÌ;vÔ«†>Å(f Ùb"ðõÑúœ¡(Eâà3+†äË;˜4{#Ä Þ¹»äªQyöÈ Ïª3¸kRÌ#îŽWÖÜ1rÞ pG°–±œvt{ Gºþ¡£ûØc‘Ó.âþaÃèÍ&¢uê>붬þ÷8º?94ìëÍtr÷¹¿ôÐþõ-÷Ž ÝøW_üéˆbN(ªŒj)«©ÿ»^ü‹²ÁÁMŸtp±ËW!þ¿o–.áãÿ²6 ó)×ÐÆNÕ“äåêOù'<\›Y®Ý?ëá2öì°eïºÙ‘¦¿‰~é‰ÓÒAÅïÎ6ޝ6ºr¿u F‹¯×yÝÙØ>úf0¢êVÎÉy¯¾TöûÙ;2)ÙRx¾•ÙíiqåÙÖ¨¯opì…“à§/5Óä,(˹nçWÇÞŒÒÞ²NOlèHäŠäUH¾§à¨jd<éin‰_÷­{Ÿê£ ÔHNfIS?}Vʾµ */Oà;ƒO©­˜?±:_õûå×{Õ+Ü]Ž>wÔûñÞ¯ý{~lt}Y·‚5Y¾K÷½ðŠM'¢¥jŸÎ\ò5vKÊÆýgtÿ">®ÓzhäfKJ♪“"yøå ¥%qÇdt¤Jæ½ö|ÿy¤:Æcí<Î(]ǰÌMçvqî(wËæn8ÚÊaz4®®ùnvóÀ–„ÁÎÅ›˜Û)i2LnÏ l$‚ª>v"íoˆŒg.úL2èË1;ÿ¹ãæžßÍ|A·âý͆Vê%ò]ñ‘Òm8‰¸o¢;íu‹Ö?»Í¾úHÔöÅL=iÊ}Iê*< FkTíçK¾}Áê™Biaîó”¢ìå™wš„ ,;¿(=Íí…>¬ ¨Y´{DÙ¥*zK:mIÞ £%©6›Z2Ô­~åÛæxí~Z3ÑØcù½ÚÙúí aÕʦ5•ötÍgÌóÅÇhJ0•?ÓzþÐe_e‡9,8ÏP*ýõÏV],{îE쥩/®š»úü½&}ÖwpÔ´f}GMëïÿÎkõÿíBÕ5P­ûx;ç®%5ëL7 :¼—häÎX}ÚFÙìÅ¥±UÌG.ÜýnèüZñùCmêý•+݅ݳld?i² ùÎkjþz£®ÈÉamlã'=ëN*ÿÇŽuýwŽP£ïÞŒÊ/MäÛ¥k›ôÎs5q|uùk¶;?¾}—§\\¡öF\Âa—ÎþC龜Â{‘ÑíVÕJª ŠM¬ðEîJõ7 úô~p<¯·y‡ÀcÔ½C›7€gk4?Üѽ®ä€Ö„Œæu¯ñÏ×øØx_«Á8A=Æ`^çîwQ&=ê™ßg&Ý&A‰Ú„wË¿¼ÛQò#,» %ÑfÉS&õZ>f‘xQ誶Æm{”,"ŸEžr|Áþ‘î/¥¿‚¸*ã¬yT.›Ý’áü; ˜|5‡•«Zp‰yÛ}í÷'^j'8®ÅüE¤QìÁc)óZw÷EHØõKŽ—ÞÖ->DÙÛò¡$aY‡¤ñÑïô^·ö±ß)/ºùL²å›•‡Ð#aëêBtë£O^&$*çN$ïm=|iÛ ßþüByNáÔ÷}YU±¶Dº³ ZTúH”Jç6yÿþ*ãñ Bæ<¸†_¯Ðó#Qoo{vz/Q¶T¶ïFx‚?ÚT™¯>_‰Æ—ý=Wieo7y•šÚ&ZʆDcTYSSè„.˜ºJ™ÙW)eJö IYº…P ·!ö»úsÞ] # O×e/Be„8i·jjêêšjjë~&¨ÓÐpp²~ÞkâqDUÐ!Z˜GË‚ýŒ#‰¼PÆÉ¸0yê„õcåØ Z3²Óa[3?·C&o^ÓYP&,½À+׌é8c†ÔŸ}E$ò¹GbwU}… uÿ'æVBÿ~Ɔ•W¶x_c9ý-QûI~à¢Ë W‡œ¹›ðü5ÉÂH¼îÅA—¤ýu÷ƧÜzQ$8œ$@ÿú{'öK®Ó[3aPØá¶ïèÓÍõ÷:Ø]GKJT+ >ä7IÇ´ml?vj™²~º[ôCW2v¢'㓆ðþGÊ嚆š\©ì.=2Ýhò+I%ãí·#›Ÿ®o¹4$¾óp̉¾€$½'Ü«“Ý'ªÆTéªXx¼Ù¤K'[ªˆ~%.‰ô^)¦pÊå†Ôå’áäÍñì}l,x=4nÝ`ŸocõŒâóêÙÙ,l„ürñwqóbî©t£ |'•ïàßë7ü¥g3{ëL ™¹OÆ>kOã[aa¶‰[S3¿/\û4¥/a{œŸ±mÞâyùOÛêYªÚs'öÄéz¼‘8qsëò³Ž•G"\ïÝØBH¿Y€_·2oUõõ—(ž_î9×ÝãŠÝ(·éΓáâS‡2«{L½iN)n™H(mþêK¥ówôhëQªËûlÐÎõLí°°AýXþÒ±S*9?ô&êŸ~¾«é‘":zîAN˜ßkÓR?XųnêqßzCÆÌ—ã~Úù#êñvܧêšâî%ìPÄË~}TCÝ»’ú7Îp¢ýŸ¶ƒ!U4ûõ^äÿÿÿPK1ÒÔ‹”XÿPK-3kJ$Assets/Square44x44Logo.scale-200.png‰PNG  IHDRXXq•04tEXtSoftwareAdobe ImageReadyqÉe<„iTXtXML:com.adobe.xmp ÖÚ¥³IDATxÚìÝÁNÃ0 ÐÖã6!!àvØ'ô0¾ý„vã°âˆJ@£íÒÆŽíØ–|Ø¢mÒ“•4‘§Ô]×UtQ;°;°Ç à¶m]%!š¦ùóœ„6n&Æ|î˜9 ½éÌXÁ—ñòÝ+û§RoC®°¿¿ð5ä>äÉ(î&äsÈ]ì ²rϱë`ã¸ÿl„<GÂ=ô6ÉÀ]?-XEÃ}ŒYbŸ"NF‘§pß°æ`«ÈɸKžƒ­ £à.Ýh”ŽŒ†›²“+7u«\2:.ÆYD)È$¸À% “ábkF&ÅÅÖˆLŽ‹ ¬ 9 .°äl¸TÀ’‘³âRKDÎŽK , ™7°d6Ü\ÀœÈ¬¸99ÙqsçDËœY .0%²(\N` dq¸ÜÀ˜È"q%c ‹Å•œ‚<†»—€+ x ò®˜æDiýÁ±È*p%Ç «Á• <…Üö©W2ðò¶O¸Ò#ÆŽ•‚n{ÿŒqàó‚¶ÛV š[@îîbZ8"]˜{kúTÓAŠpÏ šª"P†›zvaxîM2(ÅA¾wà4ÜkÈOA9®xd(W42‚+ ‰ …áŠC†qE!C¡¸b¡`\ÈP8.;2ÀeE#¸lÈ`—ŒáfGƒ¸Y‘Á(n6d0Œ›Œã’#ƒãÒ"ƒãÒ"ƒãÒ"ƒãÒ"ƒãÒ"ƒãÒ"Ç×Fqck àµaÜkÈk à•qÜ)ä«7̽æá!äKe;êނ䚇»Ê#ëY„GB×Îã,Âö ûØ=˜-¾^®È-."FIEND®B`‚PKzóø ooPK-3kJ#Assets/LockScreenLogo.scale-200.png‰PNG  IHDR00Wù‡tEXtSoftwareAdobe ImageReadyqÉe<„iTXtXML:com.adobe.xmp cî7ШIDATxÚìšÁjÂ@†ÍèÉk¡ÔW)ˆ9>aŸ¢xL ø*JÁkN­q³ZÓÎîÎììB~‚ð}qcæ7V}ßÏJ^0+|-ÜAÓ4E×uý]€V)û©úñ ŒÖæ‚9`¾2žcžiË?ÞÝB£5@¯0˜-¦3†_bÞ0O˜#ç"n1ï˜ ½q™ü†˜ZŽÀ'yk‰[ø-±±¾F;c‰{ðï}ÀJ‚ Ϲ‘¥–ð‚çÞ‰SIxÃûŒÚAð¾³–D0|È0'-:JIDÃnjӱ"ð±} TB ^¢ÐøJˆÂK52®„8¼d¥üKB^ºOI¨ÁO ¼§××ðc‰=»’¤ÒîŠÿYECÀíù5ù–ŽU¦XP‚wì EmEx·çUGqP†Wï^UÁ«I@Bx H /.ð¢`/&†ð"` -ÀGI@&ðÁ|dï--Íþ7‰§Råce ?Õ±á‘æ sƼfÒÏÄ4ç¸ç°»:ñ­@UZ©¯þÿ­b¼® –^溆_‰xIEND®B`‚PK—z²––PK-3kJ!Assets/SplashScreen.scale-200.png‰PNG  IHDRØX°Ö[žtEXtSoftwareAdobe ImageReadyqÉe<„iTXtXML:com.adobe.xmp j#6&IDATxÚìÛ½ŽeiuÇ᫉±, ˆHˆ "·FÉÆ)Ä\1 7@@†d"2G–<Œd9ÇS®€B@jNO±é®®ó±÷^ïçzŸGúÇo•”ôÓ9Ÿ¼}ûö 8çœÎØ @`€ 6Ø @`€ 6Ø @`€ 6Ø @`€ 6Ø @`€ 6Ø @`€ 6Ø @`€ 6Ø @`€ 6Ø @`€ 6Ø @`€ 6Ø @`€ 6Ø @`€ 6Ø @`€ 6Ø @`€ 6Ø @`€ 6Ø @`€ 6Ø @`€ 6Ø @`€ 6Ø @`€ 6Ø @`€ 6Ø @`€ 6Ø @`€ 6Ø @`€ 6Ø @`€ 6Ø àÐÛ_|á„|öÙgŽ@7£xëœð‰ЛŸˆ@€Àl °@€Àl °@€Àl °@€Àl °@€À¼qxá.ûéeq €»¾yÙÏ.û'§`u¼ôée?¾ì‡—ýÙ9®úöe¿|×àK~" ¯ýà²_]ö§xå]\ûü²o8<Øà:‘ à5q ®Øà6‘ à=q nØà>‘ @\€»6xLdV&®Àì#²+×` öÙ€•ˆk°“ÀLjlÀ Ä58@`€ãD6 3q Øà‘ ÈH\€68Od2×à$ bD6 q 6²ú߆o‰lÀÌZƵ?:7 ldõ³Ë~Ýð=‘ ˜Q˸öûË~âäd$°Õ_.ûÑ“ÈpË»¸ö›§vqí{—ýÎÙÈH` ³?=‰l×lqíë ÞÚâÚo€¬6²Ù^× 0 €ˆlÏÄ5¨@``"°:q *ØX‰È¬J\€Š6V#²«× 2 €‰lÀ*Ä5h@``U"¸l¬Ld²× ! €Õ‰l@6â4&°€Èä!®@<ـىkЉÀï‰lÀ¬Ä5èH`€—D6`6ât&°Àk"0 q  °Àu"0:q !°Àm"0*q "°À}"0q #°Àc"0 q $°À>"Л¸ƒØ`?‘ èE\€ lpŒÈ´&®Àà68NdZ×`œ#²µ‰k0 ÎÙ€ZÄ5˜ˆÀ1"Pš¸“Ø NdJ×`B”!²QâLJ`€rD6à,q &&°@Y"p”¸“Ø <‘ ØK\€6¨Cd× êÙ€[Ä5HD`€ºD6àcâ$#°@}"°× ! ÚÙq ’Ø ‘ Ö%®@b´%²ÁzÄ5HN`€öD6X‡¸ Ø ‘ ò×`ô#²A^â,D`€¾D6ÈG\€ÅlПÈyˆk° Æ ²ÁüÄ5X”ÀãÙ`^â,L`€±ˆl0q '°ÀxD6˜‡¸l0(‘ Æ'®_Ø`\"ŒK\þN`€±‰l0q xA`€ñ‰l0q xE`€9ˆlП¸\%°ÀÄ5€;6`6"[[âÀ0#‘­ q ` ˜•ÈV—¸°“ÀÌLd«C\8@`f'²•%®$°ˆleˆk'l@"[Œ¸p’Àd"²#®l@6"Û1â@Àd$²í#® °Y‰l÷‰k…l@f"Ûuâ@AÈö’¸P˜À¬@d{&®T °«X=²‰k•lÀJVlâ@E°šÕ"›¸P™À¬h•È&®4 °«ÊÙÄ5€F6`eY#›¸ÐÀ¬.[dרòD6q   àÙì‘M\èD`xoÖÈ&®t$°¼4[d×:Ø^›%²‰kØ®=²‰kƒØn5²‰kØî-²‰kƒØ%²‰kØöéÙÄ5€A lûõŠlÿù$® K`8¦Gdû÷'q `XÀq="[mâÀIÀ9™"›¸ °œ—!²‰kA@ÌÌ‘M\(@`ˆ›1²‰k…leÌÙÄ5€‚6€rfˆlâ@a@Y#G6q    ¼#›¸P‰ÀPÇH‘M\¨H`¨g„È&®T&°ÔÕ3²‰k lõm‘í¿¿û—ý¿óÔ%°´ñ­Ë¾ÛøÍ½ìW—}ÅùêØêûöe¿¹ìkÞþÁ“ÈP•ÀP××¾ÞñoÙ*Øê!®mD6€J6€:FŠk‘    ¼ãÚFd(L`(k丶Ù ØÊ™!®mD6€B6€2fŠk‘   nƸ¶Ù‚6€˜™ãÚFdØÎË×6"ÀIÀ9™âÚFd8A`8®e\ûýeÿÝð³‰l lÇ´Žkß»ìû—ýºágÙØöë×~{ÙŸ.ûÑ“È0$ `Ÿ^qm#² J`x¬w\ÛˆlØî%®mD6€Ál·×6"À@6€ëFk‘ `Àk£ÇµÈ0 à¥YâÚFdèL`xo¶¸¶Ù:ØžÍ×6"@'Àüqm#²t °«Ë×6"@c°²lqm#²4$°«Ê×6"@#°¢ìqm#²4 °«Y%®mD6€Ê6`%«ÅµÈP‘À¬bÕ¸¶Ù*Ø€¬×6"@¸ö’ÈP˜Àd&®]'²$°Y‰k÷‰l…l@FâÚ>"@¸vŒÈ$°™ˆkçˆl…¸#²œ$°ˆkeˆl'lÀìĵ²D6€ƒ6`fâZ"À0+q­.‘ `' ˜‘¸Ö†È°ƒÀÌF\kKdx@`f"®õ!²Ü!°³×úÙnØ€ˆkcÙ®؀щkcÙ>"°#ׯ$²|@`F%®Mdø ‘¸6‘ àI`Æ#®ÍEd–'°#׿$²KØ€QˆksÙ€e lÀĵD6`IЛ¸–‹È,G`z×rÙ€¥l@/âZn"° èA\[ƒÈ,A`Z×Ö"²é l@KâÚšD6 5 hE\[›È¤%°-ˆk¼#²) l@mâÙ€t6 &qkD6  ¨E\ã‘ HC`jרCdRØ€ÒÄ5ŽÙ€é l@IâgˆlÀÔ6 q‘ ˜–À” ®Q‚ÈLI`¢Ä5JÙ€él@„¸F "0 8K\£&‘ ˜†Àœ!®Ñ‚ÈLA`Ž×hId†'°Gˆkô ²CØ€½Ä5zÙ€a lÀâ#Ù€! lÀ#â#Ù€álÀ=â#Ù€¡lÀ-â#Ù€alÀ5â3Ù€!lÀÇÄ5f"²Ý lÀ‡Ä5f$²] lÀF\cf"ÐÀ¼#®‘Èt!°â™ˆl@s¬M\##‘ hJ`€u‰kd&²Íl°&qˆl@¬G\c%"PÀk×X‘ÈT%°À:Ä5V&²Õl°q D6  ò×à=‘ (N`€ÜÄ5xMdŠØ /q nÙ€b6ÈI\ƒÇD6  ò×`?‘ Ø q ŽÙ€ ò×à<‘ 8M`€Ä5ˆÙ€S6˜Ÿ¸åˆlÀaÌM\ƒòD6à æ%®A="°›Às× >‘ ØE`€ùˆkÐŽÈ<$°À\Ä5hOdîØ`âô#²7 l0q úÙ€«6Ÿ¸ãÙ€W6ÛwžÄ5ȼ °À¸ÞŵϟÄ5‘ÈüÀc×`|"ð% Æ#®Á ¨Pp‹IDATxÚìÝÏMA‡áñÊ Di$%P¢ˆœ.ràr”€¨€†R8k ç $Äkïþæ{^iD.,ã±çÑ8òŸÕv»m’”Ð` $K’€% X’,I–$`I°$ X’€%IÀ’$`I–$K’€% X’,Iú{ë©.ôøøh5%½ÚÙÙ™–$'¬Có!ñ’ö­¦¼˜–¤˜€% X’,IÀ’$`I°$K’€%I‡µ^ðÜÆñeÏî&餇˜Ïã8ÖÛÚ-Ø÷ql % «oKÅ*á)áÕ8nnùÙ]#`éXXíßn3ågÄCKŽÜ:V­Í÷ Ó–`õ&¬ %`) +h XŠÂ Z–¢°‚–€¥(¬ %`) +h XŠÂ Z–¢°‚–€¥(¬ %`) +h XŠÂ Z–û5 +h X°ŠÂ Z¬¢°‚–€«(¬ %`Á* +h X°ŠÂ Z¬¢°‚–€«(¬ %`Á* +h X°ŠÂ Zr‡Á*2hK°‚–€%XAKÀ‚Ui¬ ,Á ZvÀ¬ %`Á VЖ`-ûX‚´,XÁ ZÀ¬ -` Vа`%hK°‚´€+XAKÀ‚• ,Á ZЬ`- V‚°+hA X°‚´,X ZÀ¬ -`Á Vа`%hËZ ZЬ-YDX ZÀ‚¬ -`ÁJЖ`%h V°‚´€+A X‚• ,XÁ ZЬ-` V‚°`%hA X°´€+X ZÀ‚• -`ÁJЬ`%h V‚´€+A X°‚• ,X ZЬ-`ÁJ‚°`%hA«0X°´€+ ZÀ‚• ­R`ÁJЬ$h V´J+A«c´†În ¬­ŽÑ:º°´:Gkèä6ÀJÐ*€ÖÐÁüa%hAkŸ;¬­Bh Á󆕠U ­!tΰ´ ¢5ÎV‚VQ´†°¹ÂJ*ŒÖ4OXIÅÑBæ+ Z‹Ÿ ¬$hE€+ Z`ÁJ‚VX°’ ¬$hE€+ Z`ÁJ‚VX°’ õO­a%•A«°×ö¿·©z‚•ä¤sºÇ¬¤¸“Ö»Š',XI™'­‹¹&Ÿø~°’æG X°’ Õ X°’Š£•¬$hE€+ Z`ÁJ‚ÖÏÖ žÛÃ8¾ŽãƒÇ’t²v{îý8Îõ¶Î—ºh’<%”$`I–$K’€% X’,I–$`IÒ;Æ+ÝW–U’–¤Ò­¶Û­Uä„%IÀ’,I–$K°$ X’,IÀ’$`I°$K’€%IÀ’,I–$KR/ý`£5C½u‹ÚxIEND®B`‚PK]^}y y PK-3kJAssets/StoreLogo.png‰PNG  IHDR22?ˆ±tEXtSoftwareAdobe ImageReadyqÉe<„iTXtXML:com.adobe.xmp Î/v½IDATxÚìÚÁJÃ@àdˆx”zñàÑ'(Ô{ õàÍ÷óQ,47•¾D=*ä¬'¸ 1d5³;³; ø!ôPò%Ùfg·yÓ4Ù ²™Taªªš$ ,ËßSSzÎòÁ;Ò©wÌæó©ìäO0·˜Sç£Õ»+˜KÌ ¦V‚8Ãl‡®ÁþŠyÁ\cÌhA¬\vÒ>VE˜.¢=§'ÊÏo­ÓGlÌ…&½GRc†µï ±ÙFÂcßì]Ì*†Œ LQba¼Ô¹–4Æá3i”Â!|g¿Ü˜`DÈ4ž ÂíGB1lŽÆÊÊàê©vg«;#‚àîÙÿÈ!$\Q„«CäÂØߙϗR)H³3€¶öRˆY­kIAì˜Xš;±7Çb-"ìÀ^›ˆ¶ Œ°cB¼Ÿˆ(ý DBˆc "B‘bH€Á@";"X1Á† (Ac@"Äs„7ˆØû‰$ (Eü…)ƶº…„k   )³Ÿ-àÌæ^I7{À\ac!vûs7•ž½É‹‰+?þ_KY} 0…AêÿÙÖ‰IEND®B`‚PK2ˆåq««PK-3kJ9Assets/Square44x44Logo.targetsize-24_altform-unplated.png‰PNG  IHDRàw=øtEXtSoftwareAdobe ImageReadyqÉe<„iTXtXML:com.adobe.xmp ­pNzùIDATxÚbÜ¿?- ûßâošÉÄâ@Ì b#[p*ÄgÈ4܈W±;ÏC¢ï@Äk  É5<Ùèqp†LKL zBÑ}-’Iµfx¶ Å•Šˆµ¯á„’)!KNL>Àe Q†£çb, Š­â`b’3 ‘©fÉ& fb_bó ©EÈðÿ´(‹L Ár¹)ù„‰ÃaaNR>a"Ñp’3#†“d ™†m †e …†´ÙN2 Çk rNb j3dKJ°YÀÄ3¨Xß« [ÀE‹V@€ú¯K (IEND®B`‚PKô½ ççPK-3kJ$Assets/Wide310x150Logo.scale-200.png‰PNG  IHDRl,~%ïÔtEXtSoftwareAdobe ImageReadyqÉe<„iTXtXML:com.adobe.xmp y¿l–IDATxÚìݱjQ€áYÙXé;ˆï(¢­éu“F°´ñì´°M,­´WX| _ÅÎãØ…v'îsf¾6â‘ÀŸ»fî¬mÛ€¸®X€`@°6‚ @° Ø‚ Á Øl6Á€`l6€`@°6‚ @° Øl‚ Á Øl6Á€`l6€`@° Ø‚ @° Øl‚ Á ØinÐßr¹´ØÐÁÁ%€`ƒAµVÿ4³¸‰6€`@° Ø‚ @°Š› Žež5nM –»e^–Ù³ l@Ó–ùUæX´ă2/Ä ÏG¢ˢ̛ÆÝ‹Äˆµ÷e®Z6@´!ÖÁ¢ Ä6@´!ÖÁ¢Í*k Ø€:>‰6FkgÖ‚ Æêi™w¢ä±ö»Ìc«ƒz¼‡ êjWñÕyØ#Ú:ÞÓÆÐ±Ö½3ð‡õA=NØ`¸hsÒFÖXûl} Ø@´‰6Ä Ø@´!ÖÄ6@´!ÖÁ¢M´!Ö@°¢ ±&Ö@°¢ ±6mˆ5±‚ mˆ5@°hmˆ5l€hC¬‰5l€hC¬‚ D›hkb  Úk€`Db l Úkb  Úk€`Ñ&ÚÄšXÁˆ6Ä ØÑ†Xˆ6Ñ&ÖÄ6@´!ÖÁˆ6±&Ö@°¢M´‰5@°¢ ±6@´‰5±‚ Hm§¢M¬‚ ˆmG¢M¬‚ mˆ5@°¢M¬‰5l€hmb l€hC¬‚ mb l€hmb l€hkb l€hk€`Db l€hk€`D›hk€`D›Xk€`D›X ÚD›X ÚÄ ØD›X ÚÄšX8ÚÞŽ<ÚÄ Ø€ôÑöhÄÑ&ÖÁˆ6±6Ñ&ÖÁˆ¶‘E›X ÚÄ€`D›X ÚFmb l€hk‚ mb l€hY´‰5@°¢-p´‰5@°¢-p´‰5@°¢-p´‰5@°¢-p´‰5@°¢-p´‰5@°¢-p´‰5@°Ž6±6€ÀÑ&ÖÁ8ÚÄ ØG›X@àhk€`mb l£M¬‚ p´‰5@°Ž6±6€ÀÑ&Ö€Q˜[4ÚÖ¶m´5«çï‹5@°ĶeöÅ ØâFÛ­-¿XBóØ€ ÑvºÃ?C¬‚ p´‰5@°Ž6±6€ÀÑ&ÖÁ8ÚÄ Ø*EÛ·žÏ/óÅÁ°[ÝKq÷{>{»ÙüÂxÁÐCŸë¦.ÚäÂxÁ0P¬‰6@°$ˆ5Ñ6€±&ÚÁ ÖD ؈µî=k_E ØâÆZ÷RÜ;M¿—ëŠ6 ¤¹#‹µõ Gç"lÛh[?ßú§"pÂŒ1Öšær×X9iÀŽcM´‚ A¬‰6@°$ˆ5Ñ6€±&ÚÁ ÖD ØÄšh@‚Xm€`Hk¢ l bM´‚ A¬‰6@°$ˆ5Ñ6€±&ÚÁˆµ±&ÚÁˆµ±&ÚÁˆµ±&ÚÁˆµ±&ÚÁˆµ±&ÚÁˆµ±&ÚÁˆµ±&ÚÁˆµDOÑ6@¬‰6Áˆ5Ñ6@¬<ÖD ر&Ú ÖD ر6‘Xm€`Äšhl€Xm€`ÄÚĈ6@°bM´‚ @¬‰6@°bM´‰6@°bM´‚ kbM´‚ k¢M´+s+±VæC™=±V%ÚÖ¶m´5«ç[«„éqÂbM¬Õ6'm€`Äšh Öm€`Äšhm€`±&ÖD ر†h ÖD›hÁˆ5D ر†hˆ5±&ÚÁˆ5D ر&ÚD6@¬!ÚÁˆ5D Ø@¬‰5Ñ&Ú@°b Ñ6@¬!ÚÁb Ñ6@¬!ÚÁbM¬‰6Ñ‚ kˆ6@°b Ñ6kˆ6Ñ‚ kˆ6@°Xkˆ6l€XC´‰6l€XC´‚ Äšõ!Ú@°b Ñ6kb Ñ6kˆ6Ñ‚ kˆ6@°Xkˆ6˜ˆ¹€Xc’Ñ¶Ž°m£­9÷zÀ{­Z”3ªêOÊézÀr{= S…Q-í!—×_+)»e¯Ò–¾”2=`CDfíñì|ÆŽÀÓß@ HjPž‡æ#2‰Z <"øæB>Ç€¤vRµâ7ì0àù/Fd=‰]À¬€ ðÿßr…9Æt ½À‰AðÿuÕW·l𺪞o»€‰~3 CUTµ*£6Æd½ð•øßçŸÿ}þ[>ª±ø{H=àP×Hàøa…HÆW@¼U䪡ª7ò‘P2QïÛ¸¯h @»ñàøDð~T¾¢Ž ¾©9)0Ú\mÇC8Î["(×Q$ @û¼á8”}Y@™çJ½o§û¨Æ-Õ˜©_)ÇaS¡úº"3Ð\_ª3|¢;•ëi°ª¸ÆS]™TdTù{Öª¨Q¥žúZ¾Ã>}ÙM%/ÚÙ9–RÞ­Ñ\= y½QËFQÉM¸4‹JžÕÓ-J>÷ÙÍýTòcLB¨ä1ą̃äCŠžSÉEo¡\,O|ÑU@!OÙÅ‚ÊÏã:Û÷RÕÇ®0¨¹…¼ýÌÔ…Tú’Ƕ_¨üËŠã•|ܾ¨cT~ÂG5¹H¥?ÚgT~P¢à[ÿ1µ0þÆ]ê©ûŸMRU¹7’ç’äAHq¨ª< É}«åIÕý<ÉS¨å.H_äîwÔrw$ÏErÏ#Uý” ¹ùíªú„™Zî’\U_ŒäQB=µ?¤ïˆäŽG«ê»#¹ù#µÜéû"¹Í±ªú2$O!á—#¹Œ„3 É#Dj<¾Hž…ä…$}%’ûÞSË¥H^ŠärCµŸ $7­¯–›Þ¯ªoƒä6¤úKÜEYUî‰ä¹$¹ɃHõ‰@òRƒªx¢‘ÜѨª­lB%³KTò¼Ã-í¨ä+Æ%Ræ%6?MégæƒÁTòúc'RÖÍâèó1Úr8œ1Ö°ÀõWÎ8û ²«×á‡Vubpý@¦{¶ÿw}`õjÒ`Íß=Qü fŒ—SÉlŒ©äÛÛž{F%_•30’J>ðÍÛ*¹Ý÷QK¨ä™Ê<ÊþüukÓTr·ø-qTòœÊ~[¿çäúTò/ŒêRÉ¥…Ôr›+‘ETr—£¨äÝR7¦’;ž¤–§µñzH%Om±·•¼tÏ"ÊóhȬI¨ä,ŒŸRÉïÕ§–ß7£–ßhH-ÿ5qeÝLbÖΤ’×½ù%~«/(¯S~±ƒ³¨äM›>/¤’…¾šL%ß;)Ø—Jþ,%+JÞ<à*åõ.Ùúe^£Ö„¡’?öYq†J~È-þ•¼@òëU*y»m«¬¨ä¦e“{§ ùƒÉâѱ=éX³mX1k·xÃe[4ó =0ou¢… f~‘Ÿ ÖS y‹lµz>Ãgüw"ÙA¦ø¹5¿ïNS¦ø%5Âõ´~LñSÄUãŠ~1öÒ³|âr›=É’ —{Sf\N9wùÄ•¯h7† —¸aMµ—HN\Û…)¾f>+EܽÆðè³Q_Ÿ³Åš3µ—ǩٞË3áÐìOˆ_ŠðHaŒ²Î|ž×ú l­S[¶x"ÚVň=Àjß‹1;^Ï#e>ãz#Ë¢j;*‘ªýZ†ˆ]j;Õo³ÏîÐì׺#î©9ž#úƽѱ/â\ü§¯¯‹ó/™KíŸÏúX7/dªŸÒ¢æÎð—¿1ŽC¦kn¾ýös'¦øQvhþoÇ‚©ž˜ç›ãœÛ.â×­Kc³¯Ç,pý]ç×ßG×eœ§udÆåás]Jn=öY6•|òŠg÷øìÿà bÿ`Ê#å‘Â"Ÿù7ŒêêŽOü á>ϵuåŠ/]R÷Dþ²Ñqa]7ô@æcÁ.F¼˜ëu ¨n=;òvö®ïË—¤33>“9/òøÄ÷ƾù¯lñ)Yà›ôpAŸøÖöY¹€u»vaÆ7úÚ¤jô;5¾øòóä;°zÈ_‘5užKt âöˆK»¢}Kæ|8}Ìg{LY#8ζ=L™ñ­jŸžÏq Ó¼ Æý›x¸­_sˆO\ï›´Ç„KÒ× ÿü|âšúk‘® R?ã2¿ ô7¿2_ýõZÞ|Ëdœÿü÷j‰óŸ½ úþkë%8ÿIÁÕ÷ßHÏ¡ ÎÔÂêûïù¶¶-ο4¤úþo×Ú³;?­¾ÿÖ©‡ÚáüÛ˪ïÿ¢ñ‘ÏØþV}ÿi?޶Âöü·º|+ÛÿUßH¸clÿ_\}ÿÂÓãaû8µ8Ëê©wƒ¢+æë#‘¢¿!.Cü Ò ¸wÑñŠòq\Û_ÿð.£ùôw°Mê6ÝýQ|aùœò¾¡¡ÖÈ©äÃZPË=šQˇ7ÿCÎßz¥6Hh)½ÊuÿúÞ]ßâW ¹ç¢ã\t\‚ŽKÐ1!AÜŠùú]?Î(…¿¼ Àzç…J®yƒž|­ÃÔx²»hðÔFYcäÿéù°µƒ#âîˆûZñ›wåy±o[³ÓlçÅ6½™û‰ë3u|â{²å—lñe±À×gâ‘£|â;ã:Ɔ->ß>Ìø:ÍØ“Èå:ñîü !v³„ï}@íü;§™F±Î¿/sþʹո_Ißúî%;Ùâ#ú±Ø iàÊ'¾ßÜÛÁ_< |‘#Ú*ùÄ×÷Øilñ¹ôgÆg´¬O'>ñmž‘6‹-¾BøB~ˆò‰ïz½Ùâ‹paÆ—íóΈO|Cß`üÞ®|üÀŒ¯ðy—ƒ|â34}Ïzüg/'}¸7ŸøG–°n_ßÌø²òî÷äŸwâÖãß f|™Íç&ñ‰oäÖWŒû^åã |Þ£^_äŸòµ²ëñÏ•ßÅz9ÕØgÖÞ;½É©%ã÷î,pÕÙ”èÅ'®%‹â&1îϹ1㊘ö½!Ÿ¸ZF90Þ'ð33®Aûíá2OÛýKn"v?hiõ÷ æÔ93»´¬úþÓç4{ÝЍ¾ÿáàHv?ˆÿµ,›[c÷ƒ–WßÿÂC7±ûA+ªïß9FÝ/ŽZY}ÿÓEîØuDdõýÏ ÍÛŽíÿ«ªïß§§"ÛÿWWß¿Iüglÿ_Cíÿ{^[ †ÿÊb4ãÄ’›ODhÚqyµ×kÚã_‹sÓÇ¿aÌãß_ÆÕ˜GiãŠl‘QÄøý \Y×G7ãWãï¶üÆxýo¢û÷Laï¦ ÇžQÕï· z•¥`Ï‹µÕ÷o”ô‹)ö¼XW}ÿ-ìïÇŽKÿðlýæ§òùÚ÷5Ç_*}O’þÙ»ŽÓéÛô}W„Òé“ñìŽ4Œ Ñ˜“ô͸;§Y±¿®e'ü`ºS;»zêb÷rÚ̧ºØéÎvÓÅN6ËöŽ.v«S]ì>ôùe£.vûÍ‚fèb?w»“.vOöba\HvÁ±ó°u¡êÇ[·ÜLà¢ßü“§/}·ßþ.ã¢p™íg.ú½–ŸËE?½Ð#†‹þ­ÔÒç\ôÝÓ]å¢ÿÓ¨Uk¹èϰÇE?Ð3¢—q*ó:˜Í¦ß‘ã”]o¹àl·YÌ—ðãD6ç­VãVí›qÁe•7³+\+G÷bs~kÅQ6d—8—-cæë4Þ¬`…|ýÌ$=Ç¥n#î}ÇEÿtÇâO\ògÔ8I—:{OÈØË%Ž÷Å.]êÜ]¿ºØõݧ4Ðéz´0Ü[—zL]æRY;b–ëg×®Ã/¹ÄYsxÏ]ℽ)  ëw$ýÞ÷a¯«T¸žf}¨ ®bŸ['¹œ&Û»Ò]÷´p…lÜæ£K¿Y¹¿uˆ.ù¬ÈŒÉâ’OÝÇ~ç’Ñ‘ø¨K>Å6‹ûêb÷›}þ;]ìrëöj­Ký†…›üÌ¥;zLÕßÇèZ…ºàóJŽŠâ‚ïÑïŸègÜÛÎ7¹ô£DAvܧÒï{åGK.ú«vnÇžßTy‹Õ‹Ó¥]º/if¡‹]Ø~+….umàæÌ%¯ó¿ÎèÁ&Ž;Éná¹hì8D…ëÔ©+†\pylo;A—üÿ0~Ø’Kœûõ‹8ņ>Ý¡ÏLu>I¤ï›³Iqõ*Åõ$ÅMˆ`7ú,ÉÒŒªÏrDÞ4q½Iq7ߨtK\Ô_´Æ÷#NWÄ¢þ‘„xZüu7ÚõFzú{âÊ }Yÿü»ÏÏŠÑßK7èªæ14×Á@ÿ’)Ò³DÜñ ŒŠþOî3|â:!= â®]«žO•û¡W†z<ˆ"ùy}õS±²“"Œñ“V©¾äó±óS“7 »hÄ~Îð⛂ô2ϦÉCã‡ÿÁÏ!ÊBd÷qà¨æ±È¾4]Ê ˆÇÉ’)$3Hæ,!‰!YC²d É’*GHN$ú@rä I T?.,'ŒÀx‡ÁÇMQ|sÄň—ÿ^ =/nß5¿Žä¯×üŽlÂ_éyq€<žúìUÇýŒøA’§«ßÛ%Gç™7Ò‰'ÿš¿Û“Ž5çeÅ›­Ä)ü×s®úú`ó;môª!î~Y7â/ÿݵæwGˆK@\9Ý7‹òt¤7X÷GP1b!eC*ƒdó‚d e½Pß±Á×ý5ÀÈ×ÍÇåëî#ä”oB¥<}öÂ~t^Ý^äñ™©}*ðªÛ'hžæ~à@1!òïÿ—߉É·A¸³Ð±):. Ró$T¯œ/ø~Ñn|•|oë€ÍTrß§K«ýûˆùšz›ѡۃþ[ëÍ×ïõ”=5õ¨ NÞWÿ·Ô£â>=íybÿÅ+záæ{Ñ»E -VH÷>¸’¿úð/‘ÞF]pþR¬ÐüñxÄ£—#…xâ2ă—"7➈»#qGÄÅh<“¢û“Q}LѸÌå¾8ÃÕIƒp÷ÅEOªþ}wÝNþç_:…‡ßÿÿÜÄ ‹ßÚæ¼ÑÄ«˜ïŠAÊÏPÞ?´«ëbkõuUÖ\Í•b57Er€x‰æïˆg#®@< ñhĽ—hôßhÄeˆ»k¸%Q¥pºÿùtz®ž’<<_RV(¶½0þ}Ä ’d‘xA ‚´RÉz!0Ù ¹(¾EͶ§µ@µßWy½:Mqמ¨~ÇÐùT>ï!¯;è¸Öa ÎO Íø5üÖ•HœêC5¤òS«äˆÞ¶þÔ»NqÑocöÏk.ú3×|ÇEÿdƦ«\ô¯çz ⢿ÐêÆB.ú¿›ÏE_hò1Ž‹þ´M«ãôñû ÖÀøFWÚößÀ%n·€€#\ô'ÖᢾŸÈEÿ•Wé.ú~á;~¥Ñ×:/EÄ`;]ÎË;»Îã‚+8á—k\ôËz9­æ¢?\n™ÍEÿùóÑ\ô‰7ƹè7oòÓa.úFÍ28õ‹ Å=¹è§Æ/½ÏEÿ—7“¿ã¢?Ë,â¢_{æ2Ní»fòW.úËÛ8DqÑÿ½×§¦\ô} ±×E*}½''¹èŸÿ2<˜ÓõÄÍè4ý¤#…Ÿ¸è?1Üæ:¬ýü²õT~®=vÎãwÏØN\âÎûT •|âƒx_*ÿC†¤Æsñ6t‚•Ÿü–#GrÉk”ÛÅ\ôûײ:ÇEÙÖ9œÆŸ²Ù;¹èû:¬Hç¢jÀ„‹þÛ5µ°ëLÊz:uú‹þO®î«¸èÇìý¸‰‹¾?8NpÑßäÇé|?³/ø3ýý{ÌhÎ#mýwz?¯à¢¿äêœ\ôÝ×sšK¿äÔ^~÷KéæAZú÷—·ç4>vêkÅEÿ‘rý/¦4梯?$»ýEéŽr.úÂÆ>᢯øÇÓúfÙˆœÆŸ_Öæ¢Ÿ.oÁ©ÿÛ ¸œÈéü"nOå¢ÿÇî¼Áx}íç÷5ÿ­ù)ú{â2Ä™žßÇçït‹¤£˜ŸódNÞ—Ôþ¾+a²T§ßSØ4™ÐžÉŽ¿|`SÛÎáLùVÊS«ÝúúDÝѵݨü•DuÚÀ§¿¶¡Á øô·+ôäÝý©ÏÔÞ†þiã^þüÕ\ôý£ñ¤™êý°lÄ©ì³cûÛáìÝÛ©¹g;c¬ýöû³zã콑/}Ü´â58{)² ¢±ÿiý]œ½ ÙEÐØ;3öÅÙG!;9ý°FS‡á죑]<}ïÒìPlû!»û-÷âìÈ.‹Æ>&þ½gŸìri쟱ÇÖO‰ì iì¿\-<³/AýÖ4ßï®–ÆÚ£¸¥åñµÏÇNFÝsù<¿ßwx¼†Où¯Ïvýÿp]óî¥xÌ4ÎÛ×àûÍ”Y™Lñ½k0þØ­_>1Ū±ø0JÞòÛë,ŸqócÏþ'ânHéý¾:óŠEÓÞ^¬Æy8€ì¯I«#Íÿóç¡,‰¯¨N]®] Åg]gmçs¼SÚmøGWʵ"©0Öð† )ÒH¦™p‚ä Éã,¢¡®äüµ!¨ÿøü\¦öÑÜ&Û†îg@œ@<åcc¾„ u_®>°¤^+?ÆøSÛª>d{=0®UF}œ=ÞN~øÔÜ“lÇßó¯kƒÛ?Ý‘ý§ï+QÖØ}3õ@žETÆ:?¦ûfJ5÷i5á·Ší÷SÍü)öì[$¤zP_ÖÙ u¿hî3Òš÷ž0 »†›÷ævDë§5UçÍ•÷WœM¬®pÙ‘Ž?v™‹þåÒAz\ôÇ6â´_;Â/ú,ý¼_åàô OÀëE*¼^@ †”é $Ó“ðzÉ’GOôÁ¶¶µ7²Å%Ù[—¶=@XZ׃\WÇDø@J‚œÈïz`Já‘0®ëÏx4‰¯z¾{ÒôSqnùï=qþèú鲑â78{ûp°çwœö¬wð+Ûºˆ÷i÷;\û†îH-ÿªÆÞ{1¼^/æÿŸµz3¾¯F_ÉÿUWûcdüQ‹D |¿øûëµ:Éôïìñ¯­¿v¿"L’ÃøÅŸ*˜Ç¿÷~öøo{àMÆ_"¿øïßvcÝÿØãx`G¿b7ãÿº5½X÷øGÆ´O$ãw>¡üâ'¼^þ̺ÿ`ß³qÒ~2~Yˆ†ð‹ÿâ$¯K¬ûÿAöøÝäm~'ãwY(®<+q/HÞ| ¥ì!¾q_H÷àÿ³öV"®úðy]ýgÝ)Ûü{Õ|Jœš_@< ÕC cz‹€{0ß8õÁz‡¯q\qVžÇ”Å´¿?Rµ_ÉVMá#ýßNžfEí~”}}¾,ÜÝŽœÙ]},>ÜÔ^¯ {ÉVøAJ¾•ßúã/­ynü4Ø6_ü¦Í‚³îÿ öø£¬µêŸ;U.Oå£^óÌY÷ÿ3ìñw-ôÊ#ã' ö²)üâOñã{bËëÇÿìïãz’ñ;Bì¶<ãïy×?•uÿOçpýr_«þ¾¾"àåË/þÅÎã6³îÿìñÏYÝB«þòÉ"9™_ü_~®}‚uÿÏd¿]ÑÈ?Éø•Ë `²‚ž¬Wð;¿5kxý&×y„ýY4ÿÏd?¿µj´û­v!ù£›ßžòΊÆÙ‹#à IÉ'‚çõ‘ÝÕ%\ç)îçÐüÿûú¬Ÿ^û89?²?ºú„6š¢õ}…ÆÞ{)œÿCÊ…»”ßú|/˜WÂu$=Úý<ûú´ >AÎì®>Mæåãì£Ã p’ù„ó[Ÿ¦ÿ4ÙÎuž•…Ú=‹}}F¾Ü¬õ½?Ù]}žoûIkFc¯\ÇŸÅpüd½˜ßú$øŒg|y—tµûöõ ‰ú]kÿŠì®>;]o³‡Áñ’’O¿õiÜ·M×yböEÔîÙ×Çs†ù[r~dtõ)n¬˜³÷…ã¤\H±¡üÖ'­çÖ]\ç¡%—P»_b_Ÿ [Û‘ó#û£«ÏN¿¡Xûè…püdÇŸ…üÖçªÐ)™ë<×ô j÷Ëö¯JäZý‡ì®>¯¶ôÒê?{å8þÃñ’u0¿õ¹'Ìumµûöõ Þ´Oëû²?ºúD¿¸¡Õ4öâùpü$‡ä3ŸßúHÄE1\çéî×P»_c_ŸÑ3½#çGöGWŸ×2qööAð‚” )6ˆßúŒÌúÌø;ò:@špe³¯OX}[­þCöGWŸµþƒÚãìKæ m¿û«ËóŸŒe[Çëì×GàT>9ÅlÜ›Í'~=pÓ©Ë &ü%·æ¾‡ rÜÚ÷=LN—‹² PÈ/ÞùÃÇZ3þç{¼û>Î.•³P6‹_¼½?-.eÂë~“=ÞéµGœÔKý ° RÄ x=…$™I€„~ñ›O1‘ üLtEø}¿âßs1úäÜ}-2/ˆR.$ƒ±H‚d;Ž6ÞðO¤„_ P ©ÏD(|a{ArœÇ;?dC*ƒd3ÿûƒº/íÎøýü>ô{§œJ÷éãê°¥»1®ñ#`þ£ømÇ ¿Lb¿ áÏÕ~þ«þ¡µK;ãð§¸Áþ)i,‡ÀÒH HÁ0/Hüæ×ÑaÄ(¦ü¡üîUºÝså‡_ÔëÑL8$wÐ|áþŠ޼™dö~>ñÍ5N„hˆîW y^¨|sÎ^lûóˆwSƒ0Æïo¤¤çñÿ´ð¨SüR|ßü?GRV§3á(4¯Ú¯èÎß©ô[âÚ/Ë…fCD@‰¸9$KHbHÖCøÝ_6~s` ãü ·Ayêü ç‘ðäÊÝ×åÇÈžò¹Ä%£.áê`ßη‹€ äý †”s!©äJHƒù­G­+ï÷²­GÄŸêz¢z¸ÅÍà鋸lêÿ§S[\=¢zÃùÆÏ"¹Ox=†´Ë!©äÑbæ·YOÖõg[ù=íz ÇZõ8áàû‰M=Bý¸ãêÝ®·Ü`¾ˆ{Còä ÉÏß:ü0,ʼnm¢ïkŸ'j¿Úç‰ò}J[6uxó×ÛØñþ'X»Š@6â6l!ÙCêäÊo6<s‹mâÿb?^´–OØÅ¦=Íì°Ï-Îþ®wŠ@!â%Þ@*…T6ß:4m-³­CÒöuÈx­3›:|WoZ#\r»ÃñÁ ޶9ñ;¿šnwv6ãúO©Î·˜q @ƒ]žyí<=>ãŸ7ïH€Ènˆ‡”Ùß<;e\7Fä³ÏóAëÎ.tyv4Øû¼Îpœïçó.wá7Ï¡- ïo‘>ÔÎSíO;Ï}+¶8Ðå¹êÄp!¶=`íóƒ”Ó‰ß<_„𯔧ç#öíùÉ}üZºgŸç…3i‡èòôµ3œŽËSjÛ³lOHð›'Qo¿€)Oßböy^øºBBñ~?ebç·ßÁñ¶6oëÁùB]äÂÿ|OwH–" å@›Âz@’CÊ„$®C?HžÐ¦ØI¤uùÞ÷3‚4œõïºp¿Oz¾ÿ¬ôÞDê}"Ð^ì¦õû#œ_Ê÷úÜø÷B‰`Œ @Ô"€u-~ûÓßdü}rÊKöý)û¼S>]Zh±û¾,#8¿Âù5¤b!¿yZµjÇø;­è¿Ùç™¶ékE8Š<vé‹ËS¢O€{(…dfÈožÈ[3®KØçÝåÎ\º<Ç8MžÏÃà€¦zp¾¢Çož=™öŒëßWìó<“<•.Oïr{ìxÿYL¾ ¤>øÌ íâŸþ}Ía]èdèN—gö×'e¸<#> u™H y•ñ›gRºKãú÷öyZì0iN—ç·’°ûÓï…ÀéƒxB üÀožÇcÜc\ÿ¾aŸghÊpCº<7=¼9»ÿüF<Þ Rä;~ó|÷`¡ãú÷-û<ºøýY!ÑÎS9Vû^ªìWBà÷D@Šý‡ß2®9ä)ëzÛ€.Ï¸î± °ó±—B^"ÑÒJøÍó¬Ðx>ãú÷û<÷Ÿ-ºK—ç5Ÿ£ø÷° Á¶0?H9/øÍ3qTcæõï{öyÎ;‚ËO… ’A!¿yN²±)ãú÷û<çw¸) ËóìÑìû] AA€ÇB`ù˜ß<ã"ö`\ÿ~dŸç¾Œg¹tySî·ÅÞö@Êò…Àü¡tzÈož·ï2Þ‡ãû‰}ž“öÖjA—g·¸}Ø÷tò™×(¹ðS^æ¤÷ŽPá­ÕFäÿoཷÕ_ȄץßÓ²¯s£Lñƒj0þÃp+Æ}˜xíõd«~~Æ#Þ¥{RçcYËj®^Û>è3ÖKQƒíÕÏ|ã}ø¹,Úëíî^¾Øëu\ÿAòÎS?b‚OüõG.3c\Y¨ñ¿©4îùä Ò6HY ™Þ[Hž­EÀ ’wkþŸÛèžÚä>Þ:è> O„;q9â¥(§¯øûñÎF&:c׫·à¼ß†ßûGâ Ÿ§EΫ¬R{lÈ‚$H÷rø¼.êƒ×ó•lqiê ÖT<W_㱉þñiª·à cÿŽ`>?ß½šqë_¹¿öòÆß÷{²ÀÛtÀkÇïÍ[Nõש57þ.Øý…ñy6™ëÝ`ñ‹£^жíŸçÍ¢}»¯~Q鯿&âô~íëÉg9½G¾çÅ}¸è'fYÞ㢟4Öfý¹7sÑo2`ïA.úǧrzñçG×ÿýëZÒnÆ÷éz[2÷¯‡Î _rÉoã@NïÓN[|ó8ýCÿFý.Þ>Àx§Œ·÷{iÇŸäó´+ãþ‰sûmÎxøÿÿÿÌ |SÅöø§ ‹R´"KÀ¢eP0 BPТ€á‰Yƒ,–MƒP^A|/ JTР,Y†E-¥@‘°(1òÄ´@À"¿)÷LéÜ“dnâÿÿòùœÎçÞÎÌùž¹sçÎÌ9W—U)/¢‡Ðaþ ég ÒÏ Ç:î8 Ž“ûe$˜éß¼“oæ“DI—!îÊFôœ^)ÅˬKn†yï&Þ I&éô¯{!ˇQ Ýï`¼ú=©d,•xr¯=þì—‘¸MÀíî\š7Aøk¶žTãÏý6•Ì¢bÞ™JfPɧ²£ •ø¨”PIß•J²¨©Œ¢b¥²˜Š›J• •»ãkÿ8Íáî‘ì·€ýÉ3%ûõaìoPôÔk˜ýÞm©$y{*qQ)¤â§’¼#•h©´ß_»9}:-’]V°Kv™ÃØÕ¯ZƒÓ˜]¥¿t"ÿ­¹Þ¯ÚÿÚù”ŸÎ%”ý7ƒhéßÌE’½)T®×hÁìõê¥Ð¡ËQQ:†ðÍÎÿçCëÍrÃd^oBãoÿ ‹æú%”æc›+å£-=óòºAX>®UÒuÓ­–BÇJ)´¹àºB¨[ç×C¼¯à|¾Z $¤Ð¡B²Q ó t|-…ÚMR˜ß ¸à¼Â<›¥0 B÷VH¿òƒP»Ž! @¸Úëpõž/ÿõAm±ŠòÉgDËûã’OåjêòÑrùÜøWý¸ðôi‹çSúû»î£=eÔ‹ÔÑC^ΞÄ[ö$é_-´s•©üñÒÇ~=R.p>!i+…Ó L‡P a&„‹Êú9ÑÛù¤þõã"íÅõ¡.‰®sÖ¦’³kRI€ÊØš‰ Ê1˜]\9³ãL8vsåž\fmV‚©G2­aÆ+IÍÃÕ£Ò߇ßñæfXžÓ&xNó|NŽÏÉñå£|:b¦ój$”ñ}T´`ÆgØ#µ/<_üÊKGLOµ™oAË«ðÿ=Ï3/ôŸ€–ÂCS&ðõñ¿?ìCíZfœ0¦ò'–Îvh>L{@JË ýÆZÔ®þ»ü°|}`—v/<á>'pl€0ï{ˆ·O õÏa6„FMæAþ–Ÿáyç—Bãax¾ƒó¯g?<¾£pþ<!ŸjÐÞ)•#ÙÛ)µ7Ìs/·s ÄòI»v\’—›Ž-ú 4_+7ݰB3œÏÐÂBåœ×í”ߟpÝ®Cç_n$‘o¿'Œ|j¼Ó™JzQ!ËéøJ.•C‹S‰eE*YùY*É\IÇkTnkš¬¥ó œ+P¹mŧoæ²~„V=>G+7N”ÂüÕÉÂz·˜çϰBùÚ _;„zÈŸ?ï…óÁôФ„Š×—"×—HöÿüÚ¦Ïù9!tA¾üùÓйû)¬ÏÏXªµFJùrJ¦õïx»W_µ“Ùw'Øg-ëOçU(ß.—þ¯hj“Ê츴ܫ$W$iT½œJŒW4¤×•[åï¹ïVù÷˜ñŸêÑréî—ÒÙ‹¤ò÷׆çðÍPQþ–YÇr±ò7¦+—¿Hþíj°ü]w+缤!%TÈe I¦¢¡R…J•—•¯ïL_ô×—•#+¿h®o×¥[ö”¿¾ÊñHñŒ»ÞaÇ¿@û.~òdûw"ñ{Ÿ(xU$~OÓcãEâß5ô_'Dâ»­ãŠÄ»àöJ"ñ_Í´Ý.üòªUEâoü–P|[·+SDâw¯Ù{žHüï~é‰ÿìË]߉£R‘ø­«d›Eâpzî‡"ñß7׊?çÔ‚Ù"ñíê Å?”ºÖÇÇwJ½y\€·{)ú×û k’HݺS_p·Un àü†ùRüýÎÝDj—jElÈÉe­Ç°c–¾`¤Ä‰÷Ë2nöËÊsžŸõþUQΜ¦Ñs^º<ì%žÓ=BâÄçÅ2RôçŒÑŽz¢œiÍ¢ç|úŽçš…”çp‰“Ï–çÔqœëß·e‹ræ pŽ.yp*ÏéÈ‘8a8Íçªék^å47žsýë&žÓù‚xyêï8õ»(§¦Eôœ÷6ü-/)Kï&ÎùĬé#D9]œ4o?ŠçÌ*ÎùéÔû„9Mºè9‹Ï7 )Ï!âíçá>ßå$DÏy_³óœƒ%Î0ýÛ”LŽóx‡Î?‹r:8S;ÍsºŸç<¶u»_”ÓØ2zÎ ;þx1¤<‰×Ï"CaΠg£cB8]f‰“?\žÓÈqê“ßøM”ÓÑ*zÎG§nËá9õ%Μ0œŽóÈՙωrfgEÏùïgöå9Äëçk›Šˆr8H^Îsf÷ŸyT'Êio=ç?&-å9ÍýÄË󕤡(§¡Môœ9o­ŸÏsŠÌ[í+¾R7Ìû·>þ=7*¬‰â=_HºÛ*O@×»„KWòdï¶‘Òá×cqÈ<È£¦]ÆæAÒª‹Î³„æoš3iš åüù÷6ìùÌÖ×–{o“\ºþ#8ŸéK$§«ôý]ÁÞ·ƒ^/·'¥gëBbÑ?ªþ*t–é÷DÐ/R_ÉäÖE˜>[[ùû{#áúÉçÉnÞÝÿл~¾¶Ê×Ï z•îÏÛç=S»ìxq2/‘x¾½÷χæ›D.xr_ÓK—H>¼º!_DÏÆ)ÃÏcñ- Gó¼Ü­'ÄËýÜK3MX¹ÛžLU,w/è·ÆAÿ#÷wŠÎ¿vUÖo½"z¦ûmazüHýb¿Üör;­¤·w²µpÆáh¦l¯ô“‡•9l­Å9NÝy‘(GpX]q[‹›D9Œ ?­ƒ2GðIqŽ–ºSŸ‰rxÃŽpØ»‰sÜsªùAQ3è×vD®KwqŽ •vÙE9üÀáD8 Tpì8Ÿ\_”Ãúu9‡ê§Y/Î1½ý¸ßE9‚¬8_'ØOõˆ8‡ïý«•D9ò@¿¡“2Gî£âo%N½(Ê¡ý‡ë´cÓÅÛõó¿€q¸;+sØ@¿ÿ‰TRLÅB¥ðІx®HÙüqZˆþÅ7çƒ Y¿ˆŽ‡–ýPë±õ“Ç>…ñï#’ÞÚÜú ¥þÂè…Ë·±c>ëð¾ñ3©ÜÒgÊËÓ|4åfº0ý;é=xÙx)L?àö'›õC´Ào~§Y:Ö®”xò9Ž»Ûð=Æa~T™# 8ÜÀáXÏg›zŽKïÏÃ8ü‡Ö‡xüžG1pdÔ_ô:Æaé¬ÌA€#¦ÍðÅnÊà°²ò€ç‹ýõíµ.F×i"y—$+ípßÎUÏñðÂIèuÉí®Ì,–8ò#Ü·1p<ôð8/:oßC™ÃàÐï‡ûvžzŽ]-‹Ñ}kV„Ãÿ¬£ÿxîÏWÏQÛÝwºïí)e3pDǸ:Àýõìó*Ô±¿§ã¢ã›$Òù“Áë0N;ÇÉÖ=™`dn¼i¼CÃsãK"XžIÄRµ®ã4•9­§÷¯èÆCZà ä‹sþðY“oÐñ™Ïqê¢/€3m£8çO÷}Ž>ï=•9ý§õztã)3ãüNœsÁºßP¿9ÙÿPæLÏ”súJ¢oå§n§8gÖÄÑç™Íqê£9€Ó²KœÓýýÐmèýþ´2g.Çiû3ºñš›Ýï{Ä9¿yî§è~×^ÊœNŽÓ-ºñœÝï{Å9·x‡ œ~„ÓËq®E7Þ#C ~zÄ9÷öýµ?£Ì©i$ç´G9þѧq8göëëÑ÷ëúÞÊœzŽ3Œn|dNËzqΊ5v ýa„3‡ãÌŽrüdf×}­8ç]ó:¡ãë³ÊœvŽ“k"¯ò€Óó•8ç)íÊ §Î¤ÌYÀq¡ŸŸ œysá~çÆ_àLwŠs~µá«k§á rœÆËÑÝÀØ!ι°Ý©;ÑùÀç”93Ë9—¢GúØý^ Îy‡NúÈìƒô?9Nr)ºq& ϯÝâœÕ×|…rzN+Çi*ŽnªNO¡8çmÍ[÷Cǃ}‘þ'Çéú#ºqª8Óö‰sVù|=Ê©í‡ô?9NÍÑcÍÀ™¹XœslïgQ?gz9§ùwè×-åÚË{+HõBÒR pì‚P·EC²¨˜Ï¤MTرžJ{*þ3Ñûní‹K"[ïNžƒ¾?é/·k×R)|`‰f°y{÷pv¹Ûá/ ×Çõ›)?ëÉr– ;¶ŸJ!‹©øN‰Û•Qód::^ f—v×~dHÜiælÒQT¢çK$‡FlDßh*óÝÇúݬ¼?ãÚ àÑA¨9‘BvPá²L¯€öcó9.6ÿÎs±ëœ½AηΛÇsó¯pó+u¤üz‚βó÷KvÀ>G3)Þr¶®ίaëbáØaú1 ©MEK%ƒJ&•¦TtT²¨è©´§b Ò™J6•nTŒTzQ1QéGÅLe•*£¨X¨Œ¥’Kå5*yT¦Q±R™AÅFe¶Žó§Un=ãÍõä·®‹†4®õéftÞÃ,¿.W¡¾ ‚ú¼•…ð~|„sVÀu|^JŸYn~öÀ…¬»{·¡ùLÙ1§åÇ®?Ù(¿®ÌO,yU~Þ8V~œ] ÚñÁÜ}—)]Ç<¸žžL˜X%ųq×ßÇ.?†ò/š×§ZþÏËË_3D:ÖCh/œ÷‘—¿ æS˜ý"þ4^Þ·é,æÇÃùñçÃî¿Üñ@Ù¾ff?‘Œzâ‹á||V·Ò…Ö«‡ ,ÁÒéC҇ꭔþý¿Ôè­¢uf`éØõaõÙÏÕgæXŸËµ_\}ί õt˜ü|Ôg7ÔOsK蟬–ây¸úìƒã@êsFÏZè<½v¨¼>{[ó‚^P®Ïyp>ÿy}¶Â¼³_¤>¯nXЫÏ9ÃÄësöÖƒŸò×;\½÷àßøø¬<ÂÕ«ÿ4iT€¥cåNoáÐ5Xúpzµÿ9ó-–Ž]Ÿç |=_+·Ï– òó¶q\õh§†sý¨Ï¤!ô?ËÛç48¿ê³Žu c¯Ï–׿£~É/È본½¦P®ÏF8o!¯Ïùì½êpñú|dÔ¸‘X}֯ϵN­ü¿ÞáêÕÙFsÛóñYy„«Wu>8Kç Iª÷À¤ÇªÑû^Æï•±tìú dþX} ã(äºLè¹´ÌÿyH 5º!þ|ÎHx¾7P¯W—ÚøL¯~¤²^œg~°Ôè}eØ3u1½.D¯i”tÞC9/î¿Ó›9JYo¸úÿfçqO°ã²}”Má¹ü½DÄÖ˲´üúÙòíJù}r)¥ûÛu!‘öÉy€;d/œoÌÞsÃøˆoêÏåÆ7ø8íæ~#WÙþ¦2xÎÉÁèz2(7-„–Ñr>Ö¿Ît%¨ÖïÒµE«?Àé/ë­Q¯ÿ¶Gµý/Êõ³ç—7û—¿³óó¨íçô³úŒ¯£nš\ZÙ}SêgyÂÊg”ùÉcïéØ}?5ù&ØÖþ;ò}èà‡G¾Ÿ_|z[¤|Éåy‡h÷K–®›_Ó}.¾ß®òºyžßœ~æWJDµ_ís1ý™P¯Böý€^òrìúï½¼{$¦_‡è÷€þ1…Ô:Û±:?ñJøùNÖïó7MÙû)x^1{¼{£n/CørZ÷¡ãͱêø\Éùr¿WÏgûŒÜƒ®{UÉ—|9Má=ã>õ|½–Lߊîû'ç; |]9¾)Ÿ晼÷Â{&ŽÏú­†Ì b£2‹ŠÊ\**‹©8©¬¤â¢²þ[±úºþXåè:Áñr{¶‚=8{=͹újköyÔ—÷#CÇöEßóq|˜Ït¼*Gzá¸y¹ú*=óBû±;»»ì8ŠqÜtß¼§ùøŒ#\º¾ïvhŽ¥sgÁ:”ýêËíÚÖ6èûM.7‡co®¼ÜÒ&ÈË­tŸ VnCÞWQ¤ÜæTÿå;>>ã—nÍ#í—`éX}K; ¾Üê»âF×9MPWß °ŽÌ¹CÎeÿR:6}#…Û„yÈÉܼ<ç×™ÍLjúcH¥ÿ©xȆ~·ùc í¥ùcðݼ_÷Üóý+éØöŒÑKöü“›Ÿš?{fÍþ©š¨=®‰Êöx9{ì`OŽ[ ß{¬y\ýŠ£=š{òöŠÚ“7IÙËwÜõɇy°çS°'{ ÇÿZüìÙ3x ºÎ ³Çøš²=šÜõ{r·Já&°GÃõ÷l¯ÇÏžcÞ!jöue{Âø¼9Þ*ï×daAah¿Ú ¾0’;¸vqM©¼æ@h)ù»h·nÈ㛇' q—Žw­ê> ]?Ü"å•]Ü]ÿí ß¾¸¸rråpìß!¹ã°×ÕC®Šr0îC××€›µ+|{¢9Ì=ÊÿîÚ€?8Í$Q~?¨àÏ6<ºí'7kGøöÃÌñ;9þðÜ+~í_ÒÿuÀàø*ø«¶›¿ ½€Ÿµ|{‘Ïñ®>µþ:º€?8óäünüV¾‹~'Ês=4ä/•"*¾ ¢ó¯MSÊÏ÷T w^›Gú.fó=hßÒ 4€^þ<ïà­eR¨ã¾—ùw÷ópçüžm±yc+Äïp;9ýÎÄõwí·}Ÿ¨þ\Л9k¯N‰ëww}ü„°ý ßÅégþG"Ï×…rÔMx©¶¨¿Üi‘ûý=»L›ÉŽùz.Ýà]›ªIwuPßWÔ¤ ±¨I÷åè»êªIWÏ}n²štGÓ§Þ®&Ý‘Oj¨Iw¸Á”;Ô¤+\R¢JßìaïT“î‹ïÎîW“®ÙÓÓú«I÷Û¼³Õ¤#ÏÞ7XMºk_žPÅéHšYAM:»íë‰jÒ]þ)­§št—†ÙT¥ þXͨ&Ý•!ï©J7ÆÝ1IMº;®ä< &]¯‡ZÌÃÒéßçÀçÒ±ú1y:ÏpãÈ6Ð/ ó\Ðyÿå·“C×Á¿-åä86€þ…ú§Ë9ôŠsœ.tEû±Ó#=‡)|ÿÇÙñ[l<ǥ湹g§ïöÜ4´W~Ïá á­"{Ï_j¯gDÃùѾÿSJïh5cb¤ôfø¾%+Ëòòx®ß@ NÊÿßÎw€Õ/¾^âýå¬þò°Æ}Ÿ@×OÏ€þ9”+ãf×-ßBn=´Žm+åõ’¯Ï"ü­úÏ>‚Þ/ïÈù#í¯Ù'oò,@ÇyúwåóŸeþOÖKýx[|¬‡õd¦7SÈ*6*…ÿJ¡õV"aþÆÙ=æ{„7ý—ßW°9ø&ºOÞ œ|;§yS:ß’ÛW`np;¾ªý4™èõ›©Ìgàø à½•øÀgk;_Ã-z£ë&lÊ|¹_&ð™Ï|Ž8ðÕê”eBï_„ÏÅñ±ýð¶¾ø\qàóå_BýÀ¼§Ìççøüð^ ø<ÀçŽßžŒŸ&£~>í¿ä|ÙÀg>ðyâÀ÷ä÷6tÏþ¾2Ÿ‰ãs¾wð€Ï¾ìNUuh;ø2ŸãÓŸ‹­Ç…}38ðuuÏlî{Eø 8¾Xî¾4à#cçëðh¥Ðöo–2ù7×þÁþvði/-|]Ôûmÿf+óé9¾Læ÷ øtÀ§_õ¤hû‡ðY8>+ìgŸøtqàÛzáMtÿ­éCe>'Çç‡õÛî#ÛW¾i†ŸEÛ?„ÏÇñe³}öÀg>cø^üÑ9mÿ>RæK·ríì[×2¾Æ/ãɶèõÕÛ•ùŒóó¥> ðYâÀwäË)Ñöá³r|9°OÝ|yÀ—¾ÍŸû'ÚþÍQæss|°?Ý|6à³Å/G7ퟦ¬Ìäø2Ï |àsÄoøšºÐöáӽŵ°Ý|.àsÅãú¶YÐmÿ>QæËáøü°ÝÖú}ÀçŽßȯê5EÛ?„ÏÁñeŸø<Àç‰ßõ:kn íß\e>/Çç¼ý+àóŸ/|ÿìô³mÿæ)ó¥½ÍµÀç¾ðâÀWiÙÖyhû‡ðes|9x~pýÞJéA;­ nÙù³¦ :^búÝ,|M½3v6  þR¾ÍÀΚº×sëk^³Óâ¸eg•¡Þ‡#éw:”ßÓ‹Øùp­-CPÿF ¥|{€}m t}!×Çëd§iá-;7¿ìy+’~ÛÂ[ó»¥?Ý É¢’3i¾Sî^ÄòM"[l=îFç·¹ÿuöÞÝq?«Ü÷ÅväjH•B**‡¨x©Qñå2žÐï}¯­4‡Å8˜~ö½ïi7¤u§·æ©%»íŸÞ²ûâ7º®=’Ýì}?o·í/ö݃P».^šÒ3V»Vþ¥l—eñ-»r/4yM­]loWþuÜ®ÿL~õO­]‡®ß²«éÄ¢¯!TŒTz×”Ù™»ä–×Û÷Bý:`v²ïóï9eG›rõfý8 ɧ²‰Š›Ê*T ©xÆáõö©.-ÐùŒ#[¡ÞÞŒØæ- ÔB«!ÅT‚Tvä¦Ðû(²-‘÷¶,ìˆú=ö.Qî°u8z¶®8uÀe€Ð¡ŽòeQÑS)z5…_ç<µlä4týÒR1Nã2X6›rÍ·ß¡éÍëzkS"Y”x½{Ÿe„÷'&öòåÏŸ†÷CwÃ{’ꜟ]/¤áX5xZÆ‘/ÈdóðNqŽ…TÅ8 N1 pøUpXfßÖã(äð³yÿåâõï¯pã0.ã0‡î# „yX4¤ä•”›aç±)ijÑ~ôþ_.v_¹À¿V:çÏJd_yÓ̂ؾò<ÈŸ?Ïô…Ñ“"דHj\EÇô8`µ èáÏc×É0ÆAÍ ½ƒÐ¡×’B2Ƥ •¹TòLj_§íÏw¸ mÿ> ?ŸË_'ö¾ú6¶~%´‡6ÚÚJiäßI--¯ƒ¿ïˆúu„ôüï4{¯ÿoWî{JÊû€i{[¶Œ¾l]À}Àé/ÓÊWtq7ê‡ÍϸJÎD÷wÒå›*ÇØ1ó³Ò‘íëX…ó êp{>êŸoUx¾Çg üØíNvÌü™€ù#Rây¾º?mux>Ûj9_>ºÏ‹ÖìëÝ/³c|LyÕò £ìÖ 0?þÁÕ·Æ5ÊéÉôÊý·cés?‡v½ž¼ º¤ÿá¾õÀ¼‹áÚ¾¾~_¤–”÷+Hþ¨”Ò=¯½ryZá¼õ€‡ù`í¹~B™ŸΟZ¾³`n›/൜éös´¼>Ž—ég¼æïxyÿBŽàŸx*xÿé]Ô Z^›KÎËô3^-ð2?¼/ãA)~ð’⼋uOÜ-¯aœ—ég¼¸¿Dæ+¦=~]ÿÏ~lüNt’}9Ü<›ãÛÒü[´˜õ2º?òaßÓÖÔ‚õé\þjüÔܶá¶î͉¬{s‚^à )L{MÜ|[q{ïö7šÎ¿¬“Ûk{ùù<5öÖk^TYÔ^û:ù<…ìÍææÝØ|œ’½¿Yj¢ßÅÖ®—Ûk{ùy=5ö£³MÔ^ëzùü…ìÕsóomÂØûÏm5ÑyÌ´/äöêšHzøù=5öNèíÚ!joîòyŒ4ð;œù¥œ'7Œ½wÖlÝm¾”ÛËúv.;[X޶Áìt!væ€>Ö~°ýÂjô›ë¼åÕïýì~¶Ç ¿hbfaûA»¿Ø~\5úWº«¯°ý ŸÕw{ úÛÏKÂöƒ>æ˜÷+\ð††RñP9DÅK¥ˆŠÊÙÒcàc}EÇ[~ÿZéÞÞ[ãÅdrñöçÙx1øÜúY9pçùñØ7ï‡ãì=)ôïÊüìrý»X¸e]¾ ãvoˆŽ;¸xn:I$5ÿ;o#;YǸQ¹Ÿ‹_“1÷bv:7Fg§ùke;èx&‘ìü:çOÌNæ—“ïÿÆbgwš˜¶¯£³3{“²îÜÎyïFídÏ×&¹Wç‹· Õ:ü5\øù·Iþ\ÐoŽ£’nÜQ²Yþ|pÇcªÏcîÿl–?'²·Äα}øá~gÚùóÂަ›R´Âý_ÐËê)ó—‹_¹ =4¢ÚoäõÔŽóî)<þùF^OsܱsÔÚ=už(‡Î-¯§8pô=:ï¤(G¾[^O™?X8Š©y@”ðU^OÉ6n~ªR¢0Ǧç/ˆrl•×Sk8î80ûª(‡q›¼ž¦mcÕ¹í¿ˆrx·Éë©=}ì÷¬å0o—×SíŽØ9>ÖB/ÊáÿE)´Ka,~q¶;z1’_ÍÃp=àØöó~qr~—ÎãÀõƒ[‡~¯ãÒ|«Ì宼K±s=öx—“¢\„+ç¸ß/ÇÎU¿ê&ÔŸ0Æ¥ÿ)/à²ÅË5OŸ!ÊåA¸Ò¯@½/Qær£\Úd3¹ågICÿ“]g0ú½xÆ•Æq1j9;•ùÀÇü‰ò±õ”¥|ýGU~F”ùGÓ(óeáºªä ”ãûqÏêˆþÎx>æï̉ð¹€ùåcëKù­}õ£ˆñ1ÿeÙ»”ùôWa~S%[wXÊ×UûqDd<óGæGøÜÀÇüÅrlnu¡“Úûú[™/ûštÞ£’¯üýQ£‡}/éþÈÜ£Ìç>æw+–ûc墭?©½? >ÓŸÒù€J¾ò÷Gß亵÷GN¡2Ÿø˜ß¬Xîk›ÍP{hö*ó1ÿ˱øÓÚôZõg1ZNÈ_ÔŸ–öá3ÿ˱ðÕ^1lÆ—ý½:¾œuó¿ ßÏëöeb|þ(ùr|ka¿)ðÙGkÈ\**‹©8GÇîGmb­S÷0ît¨wZ­ —?ÏsOAü¨1?˱ð–58ñ<Ññù ãË3ÏܲO3;ñ‡°?v{j4ìu³'mtö¸öËíñ‡™O>gy ³‡ù%ŽÅžÏ7÷[Ùã‹Òþú¤Ã:EæG4ܺ‘¿wÜÛ.ý¾gØËürŠäÛc`º®ò-ó—)ï9×ãÐ÷þà—’ù±É·êª#]°|­/ó/)’¯»Ö‰÷°|}o4×÷/ó^ç·ÑýDúCÑ_7>_Òðx–¯íPô×Ï×~þ t»ÿPô×Ï·ã+}OaùGÝø|W»÷G÷7B¾ì} ›'L?"Ÿšæ}ûFÏÅò@þì}?{ŸÂæó8=ÃèYfldÄô˜ŽÈõ°÷%lžÏÏé9FO³ŽÝ^Çô¸9=ì}›Ç3•ë) £ç\Ý5Ðu°GåzØû޲y:Nó÷ª¤çñf ÿÞœžpßÕ"/ü£~Ùq~õ·Üè¼O¼ Ò%‘]½ C¾ÛÉÒé"¦O$¦u¾P£wý³Í/aéœÓ'ß{ DÊgå^ýR>¾öXdÎÃà ;béŒÓ'’ #g¨Ñ»å­¦u±tQ¤÷·ÝûŸžÿnA¸ô7Æ_°`é½Q¤Ï:¼wŸžÿÞDØz¹nä:,=û>E¸úñâñ?O”/Š\?¦LºPvÿù]^ò!–ŽùËÑC;9Z ™_9vÙ`}ª}wŽ]l_ ɢ¢§ÒžŠJg*ÙTºQ1RéEÅ4HzB…ñ/ÍíO uê¹…èwh½'äíÛ‡ÀüùÙÛB{ v1ì¾p¿ìò°uΊpVß³Ïwqí7¬ Î;-o·y~:3-W*z*µ©*Å5ÄO¥ˆŠ›J ••Tò©,.ý?Ê­½É<)”ü\ÏKè¾fV¾&ð;ˆµ¿9¸uGiœNÞßb$δ“·8 ôG*_ýrN¾´“s:à:dž’sž„}P4¤Š‡Ê¯}h9÷‘×ßÐïÑ-¾É,«I¤÷}ÚOÞý5Pîpûkò9ÎÉOÊË1žœ]6 ãSÊRnßÊÍjÉñ¬TÁ3É\³ÆãDxØ8œñ\—ßßö~2—ŠƒÊb*Î~±Í×”r6N¨ò æÿT:|½zŒó3ÉîÓXxj<}í ÆãCxœ—í…ýY¼_¶~AÄm¯À»õ°÷ИÿZ è Â÷Ò”sä…é_«oDǯÁ3òç€òÏåò_&ÿ½‰žãè¸çGyþ~._[Û„åú¿ÇæÚèzfì;‡ù ×Ñ,‘Ù²o¤±}R6:Î?+¥÷dâé}KŽÏFý«@zsK<ýWî5§±ôùž}Ÿ\)}ïÙ=ÞD¯3¤Ïû4–¿ƒ€üzˆø¿5vø-¯Ÿ¸çóÛîoØWd€ý èß<Ç­sÎÏÒMTܺR»e Ñ·’ö“F¿®4ãæºR™¿Êc¡ïíÝ?)· `™÷‡«;Œ`aû»¸ýv±ðvéîAßËXÏ)óêNñ†›WÞp¼Ó[ì8Ä×}¢ípè÷¨ï¯×ëõHߣÎ^þ{ÔÄ/gߣöMÿ«×®Ñâ옥·>;ÿô%ž‰ÄŸéWæwsüÚ0üïßûeÙw;Xúô_äõ¨ “x{™¸i` ÑöÒï<^\]ÓWö=hæ,?fùø–_"kþô\ȼK爘>‘Œx¯¤±½×Î܃¦K?¹|²GꈔχÇn|ÏÇ·EÔ“Dêí>Ž¥sGLŸHMú_T£7ùäŠW°túÿD.ŸÍ-jÝÁ§Ç™°³Ûf>¾+¢ž$â¯áŠ¥óELŸHƼÒü#5zß«>,¤Þ²t¦_#—O…»žê.R>úÊçëññ=õ$‘ÔfîÆÒ‘ ‘Ëçã¯÷'`éÃémÙcRÈ÷IYºÜˆzȦ§¯ý R>SìÓññõ$‘u÷çebé´¿E.ŸLÏ¥Jjôjn™Ž¥³GÔ›@Lïhž)Ÿ¥Woàã§ý72烟ÛBÚ–Î1}"YüÜåö+½…Ÿ¯ )–.? ½Óª-­ÂŽCü£2ÿÀЯKk»ÊZ«¨Ys@¹§ Èûq¾yxÿᩇ«³cÖW?žêzþŒHüîÞ1K$~7s£||¶Žšÿ^¯HæÓÏê|€õgHdüzÙziÞŸ¦ˆþGV¼þ¹¨~=èõsú*ôÏ=òºŽµô²õϼÿIý:^Y-l?èõsúÙú3ý¿T ¶ô²õû|ýS³ïep»…1 V ?X¬\Õpœ¹}Û=¢nÐÏÖçóõQ LJsÞ5ˆrdƒ~¶Ÿ¯—j8Z®»ø“(‡8l—•ë§Ž1ßfÍå0~6¯ÈÖß3žá*öfWë¶Wؾp°}QŽ8pÜ5¦éBQŽÐËöE±õô±p,¼³úÝMŒ#l_”+G:¬î-Ê‘ zÙ¾(¶>>Ž­sFxÿïUy=usTp”ì˜ÒY”ÃzU^OÙz÷X8ÖxKVˆr¤]“×SO8>ê½wŒðþßkòzÊÖ¯ÇÂñ[õ1ÂûµÊë©/¯wž°W”Ãù§¼ž²}Lüý¢¦}¥ægbš"§ÿuÁ{céó¢Hß½È5‹Oï?;Þá‘ÓY’ÖKoyžì©SÓU}O~IÿÆ=Ô¤k·ûõJjÒ½1úLm5é:ζJMºo,YŠ¥ &Ëï«M°.Êö7ÿÐVCzQ1Q©ÝZC´­Å×sªÚ¶Ìo ?¯èŽïC&Içùõ\ö9÷5¶ž‹ãá{ÿÄèúe>ÇÇÖáè+ÈùôÝ`E=ßÑŒfè:É ÂWÀñéÙz/Žo<ðÙcà³/¬Œú£¶WPæ#Ér>;¬ÿ²¥ÊùÖï‡ÍÓFCQñR™ÖRC¬-Å륻k/t]Ÿ>U™›ù_ãë¥N#çþ¸y¿j"|}ú`=º/ác~Óøzéáøt°Î÷‡&ÂGÎ?‹ñÙ4Ê|Ìß_/­å|çÏ0ƒ›ÿËÒb*A*3tbÓ‰_÷»7®Æ¸u•¹õÉÊíQf%9wf‰ûÿÿÿÌ} \TÕ÷ž3gfΙtQðò|… K T *QoPhZXê•Sƒ’€ÔÄóYZà+銦afiE}kï³ÌLz«_ßgõç¿ÖÚûìÇ:{¯½Ï>Ø™«ÈTB*wñŒ¯ü&ýˆô£ òÏV•3ü¾S$å㉒ü[y>Æg–éÈÁÈWDޱõf)ÉyÉ>ê‰ëýí7Zúä¼Y;"Q’í²P 888¨œÔΗWäôG }v´?›äþhIæw½_²µR{[¥ü°?­È7‘ï"ç¿…:æoÝ&±ïv‰×¿#±ýàü›ä‡ÿ×?õ~à'@#à2à à@à{ÀÀ/þãúч¨™¿¶m”Ú­ûïs>÷óןõSú©ó¯G?5•H†~‡~ ~@~²8œ}€À @`8 0`ô“ü¤@?ü§ÓOeïf¼öwýT„~‹ÑŽ~ ÁñTòù)ÒW 3Q€9€X@< H<ȬdûJ~âÐO±›;ýD–­¸ûwýÔŠ~šŒ~ Áq”ƒ~ºùù©ÀG …€­€m€€RÀn@%`/ ppÄGòSú©ÐdÞùí˜âøwýˆ~*Âüù8žÐ_!ÿŸê†øh\\|h|¸ø@À?<@ð1O¥&~ºœÿNÚßõSú© ó¡ŸZÑO“ÿ©ø>p8ú<>€A€Àp@0` Àø°ä'ú©~S§Ÿ¶ö/;üwý”„~jÀüGÑOÖ8ï2þ©øä ñ ˜ˆÄ)€tÀó€LÀ @¶·ä';ôS£‰Ÿv(t/ÿ]?e Ÿš0úÉý´ìŸŠO^Ÿ…€­€m€€RÀn@%`/ ppÄKò“€~j3ñS¿¶a…×O9è§›˜¿ ýä‰~Êÿ§âÓˆO€FÀeÀÀ7€6À÷€;€_üÃ/óñtÅÄOGúÿë©¿ë§eè§VÌ߀~rA?åüSñ |àpôx|ƒ€á€`À€q€ùx*ØÐé§5Ô¼òwýd~ÊÀüqè§2ŒãöÿT|êñ ˜ˆÄ)€tÀó€LÀ @vÉOÎè'ƒÉ>óbá“ú¿ë§õè'îŸn¢Ÿq<ýSñ©Ä'@!`+`` °P Ø ¨é'ùÉýTm²/0Þ8`ü»~šŽ~:€ùËÐOwqõLä?z¿Q]Ôçwíå îç§B·¿çàxóÄ8o/ï{q^µx¿üõÚ9C¼dV²¹€<ÀZ@` °°ÍÙ<~ÙuÎËÿŒ5Þ×_ÖOUè'ô“µ<ÑOþ¡yYd1®¶¡^ú'Ç•ÝæÎq~øZÊýütÇý¯«:ôW úËý•„þj°WWšŒ¿ª,üUú‘?é/g½súÚ‚ûùËã¡¿æ¯ôWúËý•þj²ðWå¥ã/‚ûû•ùþ¾îëÏGN°†‚cFÀ@8` 0åÔùJ%;¬N^ìp}¸²°ó¹` >áÎýüéñ×üwU*·ùrÀ5\/ôs¨ú»^mîïÕèï4“ç©ÿå·‚ÄN¿Ùÿ\éq?¿ñük~«ýF*—\G!§#—^7÷Ûƒzͱ˜×y¨üÉy}Åd}mÕtö~þ"ýÿš¿Žâ¼öÅyí‚ó:çużn¼ø`üUgá¯zÔÿ¤¿"Mâàœ¹§“îç/ŸÍ_Mè¯0ôW úkú«ÕÂ_¥Œ¿®ÄKõÝáÍçåiŒ{Çÿ¹y™gÏŽ–Oì{?¿Eyý5¿%¶âþ¹ÙãZøUóyþÓƒñÛd<çð´o>¨ ßßoÒxÛ¶¡s¼åoû¨ÿýüVàý×üæ‚ã-ógàóÖwžãÍîî:ÿíë& 0  ˜ˆÌÄ≀@zóÏcš¼Ož¹÷¾Ÿ‡ú³~*A?Yã¼TÈŸëÁyYöí³má¹X (l¶¶vJ»•¶æï§‚ßèôÓÙu½þ·?·"Ÿ—aþO9诛Ûÿ?Ùá¾"Àb_Žû‰€?¹¯6ÙWD6¿xð~þröûkó°÷[Èv¸ÏˆDÎûÆ<~åÝ~@ó°Ñüs•ÿÆsíÈ÷_'%%ö&þº#ŽO¸ïþuà_Œ÷‹1Þ#ŸÅ¸zæYseñ€Þ«[øëúiÇŸôW¶©¿ú­¾ïç8ýÅ÷'éèäjô!÷³çÌýyæÁø+çe¶h>/¹I’¿®¢ßkè@w€Àà èð ø¬åsJNš·¹óÖóÎ+ÂýüZéÿ×üÚ†û}ç8_‘ ko˜û5àAë¢_=´æ~ F¿z ËþÉ4ñê ÷}ßiò×ücøç'r:r%rÛ·æþ±ûõ½GùÊ|žFâþ5xüÍSi?–ýFç~ìTßó­÷Ýÿü5‘z©]>ȱÈÛëÍýUZ÷`üUiá¯×ÐO/ü¡¿ðœ;©3®}Rynó}÷ýÍ_Î ¸¿GÎF>‚L,ÖÌÚã¯]ßÎ~”ò"©w¦Jý×)¾|œÚ§ãóã̧νOý&ç«™U|†êήRþWÒwæoXAõVÌÿfÓå4øP)¿CÜ¡Fª÷{a1&~8›æ¥²íÐÿô—eúóîc·¶Ñ|ËpŸ,׿¡é¶¦å?÷‚Ûw¦ísÊ.T›öË’^|Ë4Γ9·^;kj—Ù²~ÏÈ÷Êw¿ü ·Šiþ¼RÿåvËþ’÷o­Ù÷¢i¹–å\ˆ þ…íó‚¤qò_«»YT¯.Ÿûße~½ƒzý“—¾¡º°A*wŸœ©_Ò1=ùÑÏÚfZî´_ýª¢+Í€vgÛ:ªG¢þvõ­“¦åo(±0_J'ýo½û¿Ò7n÷þÿJë—$ZÌýÒƒKcBMÓ«“ï¾ý¿ÊÛcìÚDÓ£Þ‘ìÿ•Xtéú»î=óøü°7²‡™Û½%û.›Ëéu>›ñ<ÒG²;?gžß#Ÿç‘ÿc‘¾ã)–[;óoÖHé¨'ö¸G뻲×s?)›E}wžÁü±âý Íóùa¹ö/Û±r0õk^¹äŸB‰íæIùœó9σ¥üéȵ¯·ýv(-§m¤t!?ÇñŸ'§šÚƒÑþλK•fväÚ·Þ¾Kë«F½­0e1ÍwŸãŠz¬_DõXgŒ³Ïš·ƒÌ•®[2y;-ç~ùc¤|O¨^XGóµ¡þül—Ϩ.×'Ï_™'g›óšw×o¾W~9=pJÍÓöÿQy—Ÿ¸s¯ürúúÃÙu´¾ÚžxŸ&m˜DóA¿ /4†·Ñ|²ž{áöÃ4Ÿ|\®1+¿ïûâY›Üî•_Î÷ægéñû©ñ'#eÙºÄa³ïnœ'Ûow{æY¦?î‹ ,­p“Å|³ßç6=ý-ïò]°½‰æS¼óçW¸<ÏÞ}»Åô>T›õ«û¬—Xü Çþ..üy m_$Æëº[ß»RÝçÇ·N{P=óG¢ýqq·'µ£žöaf?Sý…©ŸÒzdýÆÆ¡ÌÛ|%]^7c½¯eÒ|•˜/2Xâ-{Ø}‘íƒ=ÛLó%b;^¸Æü…éÇ /c~Fýê·š¦OßZmeš~øùÿeå]’ô-úï±öö’ôtä‚óXþÙÿ°ñî„û©e_°õÚÇ^Ò=#Ùx–ãsä ,g8²OÑ뉣d_ùíEj¶C½ßlo'ªËå XNõå.q¦õ|bˆ)cýÂûõƪs¬\ê{\Ë¥øù_©vˆFÔ÷Í}ÆùႤg÷‘Ú7¥ø¥—Ù<Âþ.yg¤Šõ÷[Œ#ØÑLúe©ï»ƒMõÇ¢/>Iõê+RyÖ½f[R¹¼Ø¾’=nê"6‡æ[ñŽj8ó¶/`E%kG î#†öX\kªÍ]7‘æ—õÍ=‚Ö˜¦¿Ûê8Í4Ý1ît¹izåð'™Žçebû6ôß÷GÆt/Ií¥×%{ÞËxq½3è Û—Èûœ8·¾ÑdY_:Ëάÿz¼î‹þü.S?Û¡ý½îål̶“ìr½×ŸË:ÂêA{Ʊó;Y? 8ŸÚ%>yÚºMŽKŽÃçN¦y¨¬?x’µ¯÷k‰ÿ’xÞ@§\¶ßB{Úm.]Mg~A{Ñ7 ظ’ýTŠó÷´ý÷+;âàözO¹¾œ½˜,ï§å¸ñˆUÚZÖÏs8þpNùÄy&Í_Šëó$‰ÇÕ„³và|ò.ñIaqJžgµÃ$ΰÒd³}–ŒåôÙã4›ßØ~§ï“WPý~(=÷ñ¹—X“ŸìÐþÎÈ©l¿…öÊiÿÙqÓr"ÑþCâ×Òz‹í ÀyýՌǙ=ÒÂ>èùÕì^Éí–ícw gíŒon¯{{,ÛgVŽ3·{þîë¿…½Ç€íÔžfnö¨k>»ÿFs»ûñÝlXØç\ªgë|Á#ævÿìê4æ·±æöÿÄö¯‘vmó¥7Yû-úÛsÕ3l>Ç¢¾´PÁÖ§ÆI¯…þ–ÆÍøÙãÎÓürzúh¼ï!Rº»òS¶:cyÁ·®Ì`í ÀñŽãiòõ³,þ7b¾ädž±u_Öíž›ÀîÏœ‡È_z™ù#ãø¡l¶þ >8ÏFHÜCH*µG=,é´g~[ª×çUÄoŸŒdë3ÚÃÑþøÙšZΜOÑ ?bu“×;ãåò¥þî¥gqJN?2Qâm#¥ôMïüúŽizÁd¼¾ðÊCOš–ïƒ×;ãõOÌÏú[‹í 7m1-Ïg0Îó <xø;Óòòü%&˜~§ßëÌÏò:=í°®ÂtüG%¾´÷á!T•ǧs¸ÄêúÄtÛ`: Îû‘Oln:/ª±?煚΋(´oÉÈÎäõÿTÈÛgL÷%·†³ý†õ·r÷3Ýõw®•Ma~GÿFáxöÁyñðÑ7¦ååg½Î⋬ÇÏO`ÏÕCð>à<ø¡ â°éu­-e#LïWΛ]¿ífëêrú·iJö\SŠöœu°ýJ_x_u¿º=CË ö3_¸=ŸùËÝÜÓú5Û‡†[Ø­¦îdím}ÌíC‡~Íö•ÄÂ>¸y“3Íéfn_ûHc%k·ÅzõÙ§ýzÓü‰¨ß}&›­w‰Î8ÏpÝù²tL)µ .8ïÑÞ²¯œµO^7Ž _.LX´ÇtÝíÛøô14¦ÏßšÂö'Qx?òp—g'P»wdÿ§yªãùãíž_Ëkó¾Á+ú™ÞGùºÌÖilßzëû³ù€éoÔŸg댮›kßžñ¦i»ƒñ¹|ås#Ùó³¼nF¢}ïO»^6]7' ÝC1šÝOù½Ë+Ÿ•ž`ûÔÏßrÛi:Ïáu뽞ˆ2—hž¿a:¿ÑnXü¥éü6 ýñµ'XÜ”ãöîöl|Èqå>g¿õ »ïòüþíݵ5ßhß¶®ï!Ó¸uí¤Èj¿éºwí¯z9þbºoÙ‹v·¤{™×›R´÷9±g:-N^gbÑÞßwéÌ߸ÎÌ@û§øÑüò:Žö#v³ç yƒöc^{‘#¯“[Ñ>·é§JÓ¸Q€ö_®þÚfºßÉEûË_ífç]ò~'íù»·wgíÇù2@öíئóËícS¬lh~9^D¿1RdíÃxÐïßÛØ¾^Ž\ÀWÒ:ƒó¿ÌY±Ç4Œ[ûkÆW&¾Öêòüÿ),c<ó êì…l]Ãýv ¶k†S"k‡¼ïõþpvnÜØ_ÒŸø×*¶^ˆøüQ:±‹òþ÷ Ž»ˆ9¿êÙ|@ûy´k#쿼×þ¦Óß­T|gš.ïa:ÿ^*{N°\'*1ý¹égØ:g¹?Øé¥2Λ­ç˜^ˆég#?q1½^Þ?¬Åô=jš.ƒ9˜~{ñ‘8Ótyÿ’é íÌâ–¼ŸxÓ _jš.ï'1}Q];O.ðÄuï÷Ã'™Þ§ÂXþ×6N|¥uÔñ‘ùìœ,Ÿs?]ìÏÎÙ žWæáùbÞós#žSV#×Yœ®ô(<·ÙñìëlžßïÏ€íŽvñ–¶Ûc îϰþêç-ΰނXù|Ñ<}žÏ&býŽÙF³ñ‡ñ×ú¢÷Óx,?×^'ï÷4Í÷дォþô“Y¼—óÿ²âõ7MõÌ!Á, ʺ|ÝÒ‘ÿ5{ž® uL7ÍWùÒÃËMÓe¾YúÊSûÓúËLõÜMógÝ«¾·ìÔþÔ.ŸóÈç!ÛfçNò¹Æ–:ë¹T—Ï…¶/ÌÞ§Èú›G™?b–ô¦âÙ}Dà¼Ìøh‹/™?á8Ú}–Í[Ypý¶ý=6^ïb?O»í0Õ<+ß;­ÿ¥wp½{êuv,ëm¸~wÝö [¯”ô—œž‰§×ÉzÛH‰(Œeç?`üyýÑbS½Ë{Ú˜ÈÖé;·ñ¾\­iy²^åù½+K¡ÝïÊžd½ZÞ—NØÎÎå<¾—ô’oWL1Õ«±¼ÇšBXü ¾%é›ZœÙy»¬oÃòÎl ½Ìæëw’¾ùsÂÎ=d=óÙô*<ÇÆÉMŒïùÇ¿3Õ·a½¿¹\ÏæA›¤~W•oªyó]ibçw…ßJú”u'Ù>XÖåóçôG¿cëJå Ix_ù¼G•y{³©.Ÿ;ÁçÙ¯&û¯o¤ôu Y<ȼ&éû­¯²þ˺üœ˜ôå3ì¬à*–[yð²©.?¿½_ÍÖåÒVW‘/Í7Õ#1Î:Ô°quEÒ/;m`Ï9².ŸÛzßþGø^Êïó_Ùó²¬ËÏyüÔïW6®¾–ô…]v;³q…z%®ûc?™ÃÞÃоáOØsˆ¬b¾õ>ƒÙýôhÁõ¼qq©.Ÿ¯¾1ù4›_Á—q~ LºcªËëŠnÊ:/6Žš%=º·‹ƒ©.þ®pZ¥ïð\yÙ†Ÿdã¥IÒ_|è»LõpŒÎOõgñ¿°QÒ_(vÑT—ÏÅÇÔ®fãã+\¯pýâmŠîšÚåõåÍÕsÙ9Ù¶‹¸>cܺøÚÊ›l]G»¼~3ísåx8rL[ÿåóJÏqO|{»ÌÔžô¥M¥é¾F޽gØÜ2µËïåVçì{޶K¶Ï|êE#ÕÓÏb>¼þ›Q=û˜Úå÷ŠSš/²õê ž‹†ÈÆYî³~v¸ÉÎe½LÝò°©^–ú¸©0,î Óë·ØÏbçá§Ì×IÑÓrMí^~›é…Ÿ›¯Kš7 [Mí»›­¢z[y¾eO^ûÀÔþä)´]Fì—'ªTûÙóÊRóxâz+žÝÙl,cñ^~?$ljËsòúÒë ¢¤ý@59ì:õ{ž×úÌô Aì~x`z³oëÏ•§%]÷Æ9ƒiþÒ'ñ=è˳q‘ˆö6´·žîÂÖÑÄbóqŸŽï±}VÎâÞýÒ7ô\vÜ4ýúºlžfcûz¯”ü†zÁ†+ì\Ì”ôu‚4>L”Æžë`Ø´n7ÍŸ‰í_ïÿ û|@0Ú«Ñ^?ùÛ'o6ŸŸ±ØÞƒ6°sÆû¥ŸÙ¼#ßôú[mÙû+g÷ò<þ÷·Áì¹VÞ¿æáß™r ÛgÉÏÁ¸½’XÃâY>ÞnW±÷}òû•Õi¯Iç¨÷,=÷!‹¨³)‘í‹å÷GýFtaû¡(ÔZ½—ÅÛ<Ôoìºö#¿ÏÿÞdcH;÷‘u9}vI_öüsû?hÓâ6Þåý™—ÄÄýk¯× g´ßXó[?1ÿÂŒ>¯R½Ë·CÿMwÀöoµhÄõ›Ÿ¶säõ4·{ý÷k§ 1ÿñ1ÇØsz5öñ£}˜?êQ‡§Æ78³õVŽƒmöæqñÛÅžì¹KÀú ñ9øán¯³8å,Û±¼#ÿ~–=¿ÊãCއ{φ_”}žà÷ÐRù^Ü´ÝÞo Ö[™ò©t~fñ\‘òèèÉÿkœnùÅïÚÿš—gjËÿ×<8ÿìÍÿš÷×_­sú_õÿôí…ýÿ«þþýß2­E¶ÚÑ´¾G>6d~Û~ÄýIœ¡‰=—îÄu÷ASÞy†­Q»$]ÞGíðØÊÞoDîÃvâ~rù§‹»P»ÝGxq?:pJWöž¾`¿¤ËûÞA×—±ýÝI—÷×þñ]˜½àcœÿ¸üm&k§Ý!l>/¬}g ë÷ÛØ܇8 ÎîGý\·pŸzâ 6}ìÐ.ïK÷+.²ó«Æl'î£~ª}žSn—tyÖ8½‚ÅagÌ/ï§bˣغӈ~–÷‹ÙcëØûŸLüü’¼¯êvQdŸo)Ø‹ë!î»Ù±—µ³ý)?´{±õ/²Ûƒþ¼´½Œ ózô§ü\ÓÜ+'ò Þ_|~zùý÷ÙóK%Žyõã¾Øsb®ÃòóÛŒÀMì¾_Áñ$ïÛü¸Kl¿õÎ/ŒGò牸'ØçÈÚ*%»¼¿Àô;/^d~4|ˆqÓ˜î—Ú›íO ‡1îãsK$¦Çmuaϯ>ŸHvùù'Óº°ýxâQÉ.?o¥cúÙ_ìØç1JKvùù.ÓÏ-y“¥·Äø…é˜~9Ë=ÇøÔJvùùu¦»œˆaŸýŒûò#˜Õα÷.‰»Ñ˜^é«CâÙû Ÿ÷°|܇_Áô®ŸD˯­@¿ÈçG˜>¸çklk· Ûeñ¹¹;¹µì}pÎ+ùyÏÓ_m[Ìâ@øô¦{`úšÅ?°ùšw Ûé˜^ð“‘=7Öž@ÿaºÓ×ggûÃgØ.Œ{‘˜ÞRЗ­Ó[ï½­cû©üœ§q‡×§Ÿ}‘WÉŸû½‚ö¯ß;‡ùd§dçÂj¥Ï Ýc~/>å[¶ßs‘ô žoóìzWI-`®4Oñ¼êLòö¹Œ(Ì?ykðy]ºqþ¹XšFåÜ·>,Ë%W‹‚d9†÷œ+ËÓ>¼^¾ö\Ä®î²}þ•iŸÉöîqE^²}dÄ;d{ÑG?œeûõV3å<Ï¿"Û¯O&Û~¡_.ÛçúÈò¸ðÒyržö»_ÔÊö¡o¶=.Û ³­¶Ëöo}#:ʹô_§M²<÷çÉùÿSÜú‹lŸß5éY^úÈ'?Éršªß²Ìù¾ÿ©,ÏVi£ärz­-—íƒö‘íöªßÉöűŸËØÝí‰e²\¶ùÊY^6®§|­uýøVÙž”ýcÙþâ@Ÿk²þ”DZõ6Ûâ󞖟׌ݗÏö‰玖ç”Ç»®cã,Òâ|Ôò<õ³ªeu¦ÞîG/†ÿ™zSÎnø3õ^ÛRÍú]ø‰¹}ÂÛ¾bëï‹sßÄ6/-ìVz_6æ2šÛge±=³ó1‹öíº»„毶°þs>{NŠ:nnÿbI)Û‡“æö5 ¿B û/¯ðÒyÚIsû®ÿ¼5Û4®tü½Þ#Òß d­ú€·ËñÅ2ýjO6>äszëùì\B^{ŒšËÞOÉëaךŸÙܒ׿£¿îaÏòz·ÁNËžÿ;Ö7âÆÎíäõ,nùgäõ+ ÅuÔŸYYÌê“×§±›Ö³ç9ùùyõ©»l_,¯O!Ù³¸,¯Gû»Ž¿Í®ÇõCüökv.*¯7_­}Åiy}™Ÿ·ÅAy=Ù÷B«O^?>üÍéòzñbÊ^ö¹~y}Xq;ˆéòzpv}ovÞ'Çÿsߨ0ÿÈûà Qo¾ ÏÕSg—o•çê¤Çμ)ËMãÞ‹óTÕ{”íöÖwįª]’å<çÜlgÈòWßÛ~%çÉxû`G¬™_þÉ˦qá˃‹YÌ“ÿÎ\.Ñ?²ô+ö1ÿÝ#½ÏK4·ÿ¶HÅbO…ýç¦^l\ÉßÙQ~þFöž_þ?Ù¾HÛÌâ–ü½ˆ²½&‹OülñïV(ŠöS»å¿g±ÿ“l|Xökú³çY̶ìÿ…ÀþîÀ²_¾ìy•Ú-û5ÿÀbö¼bÙ¯œ °ùlÙ¯SC'±{cÙ¯%/°ÏkZökr¯lµìײ7º¥vyß°çм…ì>¡~¯aG˜¯zÐ €~ ¤oÛÊ´Ñî O1ŽŠ[0'&ùé§§ÆÆ%„¦ÇLŒŽ[€eÔb^æëDŽdïâÉ[+ØJÁæœØiËMáHý»<ñ1±íÛ°Ml×ÀFJyibsJ…6‚-ÖÄö4ØœKù{öq-æÛ†|¹1‘3Ëwu}’ÄŽÈ>Èc‘3‘×"ï@Þ‹\‹ÜˆÜ†¬O–xòäXäläCÈÈ¿ ëIìŒ<y ò4ää\äȵÈß ÷I‘8yòäçSÌýTj¡Ÿ@ý¼…]Ÿj®-ôlÔ7XاÆF'ÇÌ™7+9:9Ã{N<{¹EF'ÇD§ÄŒ^˜½иŒNY˜<r‡§$OM‰NN™³à™”X©9-$99:Ã"µÏ¤Ði}HBrНÏÓщ‰XË”ØÄѱ1³çž ûIå€mRÌâ±Ñ‹R:ô)1i1É‹bÂä-œs/Û”˜”Ôär~4†¦ÏŽIL‰[¸à‘Ôèä9˜655119fÑ¢GfOM¡ÜYÇ3q‹Rb’'.œ“uD,XôûÜ`Ÿ›¼pqhzGûB-Š{fÁ”˜¹¡c"MúÀ<ÑY~ ½ªãÖï˜9¿»Ò¦e$ÆŒ†þ?-ù¤°в¹Ñ³±]a âRâ¢ã;¬câ%F§ÌŽí¬kÑÂø´˜Žô‰P÷Â9÷(ïÕÂä¹îŸá®ÇÇ-xæiÖÙÑñÑ‹ÅÇÍ ƒ˜”þhjÊ£s§D/x¦ÓÃ÷i7»ì>i²w ½Ä¤@-1Ñò=š–(uøëñ踔± “§¦.JŒY0ÇÌöÈh)OH|üÂÙÎŽŽ—}¼01#dAÆãq)±'ǥČŠNNŽ‹I¾¿Ÿ-ìa‹Â,J‰^0;æÑ¹rWî—nz‹î—çÏùøQØsãéëtnç}Ÿ˜Ÿ7+#%Æd®Ä-ˆŽ{6z–¡LYˆC0îÙòh|LHjÊ@?Lžš±hlrL Ì虤K2„ƒß?{a‚eá£N‹^4bL»÷iPòÂY©sçÆ$[\%µ† à¹>—29G?#7´Eù7A¥ZÆñ)¶jQ9/T>ÃqsÖéðÜ‚bÝQžK«1øª¹yš¥ú»rµæÙ›šôO¸ùÅ*ŽŸÒ¢mW†Gè2xþ‰íed6ˆçÛª~åbý• Jî [Í]Ž‹—)y•2TU¥Z]c¡æòüµjÎßÿ‚ÃV åóBµP­G ·ÂV˜¨Þ"–À€©ÑÖ(9ãJu9ÇM+W7qÜØ•ô u±ÉPuá]ô¶<Ï9ÆòüàhÞ…#}nó·9nWÃ-Sr1ð_¨’»Î—q *ž?£ôçTY7 í*"üZ®¼ ìTåEˆÄK©°½ ’·V:Úë8+à <·®IRñ4[‹P¦Ê›I³¹Ø¶@¶bG?´ß×?ïÜeµÌÛΓ〪íæûæß;à4ààkÀ·ÅnØ×O©âÉ“€9€@ ±ðñ^¸ppð5à[€b\œuŒ'/Ö6ÞT"Þ|÷)O~ðŸñÄ Ðàø [O}^€Á#bdßÑ¿]*2ÔSEÔžì1мÓ_E† PåIßùŠŠ8äI ?»ÔÄ®·š¼ã¤6ñFÛHs&äkj²°PøP‹ø0LÌÄr™€'nªÉl@< ðàEÀz@àì-5i|¸ ø ù^Mì@/€`À0ð`: °ý'5y°PøЈøðìÏj² XØxP(ÌkW“E€ç+«%€×…í¯jbp¸¼Ca€`À1À)Ày@ ààG€øøð` ` ° P8 ¨Œ#20XX Èd´ ±8ÜÞ€!€0@0ààà< pð#@ä4°XiÈK€5€ €bÀ.@à( 0N õfb ‹9€L€–‡úŽw€7`  888hÜüUP?à%ÀÀ@1` pP §†ú31€€Å€@&@«úŽw€7`  888hÜü¨ð` ` ° P8 ¨Œ¡~À @ ``1  Ðj¡~€#Àà ŽNÎZ7?DÔx °°P بTÆé¡~À @ ``1  ÐZAýG€;À0œœ´n~ˆ] ~ÀK€5€ €bÀ.@à( 0®+Ô˜ˆ,,ä2ZÔp¸¼Ca€`@?k L<ˆD®n~¨ºiH@O€'À°ð>`/ ð)àK@+ pp ð @e×z<΀ˆp ™XÎ^Ó“¹OjÈ!@Ñ“’¾&ZC>TFKúÁ8 é5|'éƒB[O,”ô„W4d) îIß ¼puËŸ«ùÐ>À/Õ«Ð>€3¢'àØ¸?€ó€À Y+áGµ¯ƒŽw€7 1p p pи ~Y»®8ÜÞ€`Ä@øFé}[$rá õ}ÏÇèM³ûÝ`¡ 3ÿÿkÝãÿ²nÙŸ¿Ú¿?ò¯¥þ ýõÿµ®Võ,.Qá2ìùU~ ¯ ü%L…i4/µÏ‚g¹nJ•0…W* Éì¥% t?Igy\±¬)P–^å®Wj¦(9–.éÉ:ÍŸ¬SÝM›ªR¢àºn÷nJ«ïÔ|¢2 óŠ*"Ž’s€®á +™æïªrïªÔ~×Q®­ÖßViøXߦmjÕÕr)Š®é:E×&6jjUÕx--w–E¹~˜±ßj–ئiTÕv¤wÕк’ÍÚf«u‡ú6ß³>?‹út+”6 b™›– ù“-òS‚4¼Îú^×éxwî$­S­"úX®ó¾j•îZ%Ü )Ÿ?äÛÌòY©ý­”BVG[i¿“±Nzÿ’©ox"*l - î¿Ù½“ÊÊb×:èŠiÛìïÕ6ݺûö—ömsgÛ{ö ÆÆ{&cƒÞ¯Í&÷ËZð·Vê/ mwDá`µÒ9V ÷­—ŽíÍèÉ›;®•úùjG?{hC{(­½ˆ¶Mh4¹·tld±±QÝÑÑÝFÙåUóœ´È;Ê"¯4ަ˜#+µ;´eVGRß6›õö?ˤÿÔ?~&þ±ÜášYf×Håd™Ù:ÇvµYYŸ›ÎC‘tSt'æ#’)S¯J÷—úZ$÷½´½[´7Ù¤Ø7u::-cýžåiU°·#~„9óªÅœ‘|ûž™oítîv÷›?÷§sžKãàsyˆ=T¡¦ãŸÆ>Ñ^ão ˜Ý"V1[OóùÆl&±Æ$Ï,³<=X;¦˜ÕkËæÞ¬Ž<=Õ¡¿+»‡ù|e6“˜nRÎæŽkp `²“èc9Ùr,³´{ŒgôG„¥ßÙasï²ÄîlM±èW–EÞ3ëCU„é}éèW–I¿ìU¦ëk5ãÜ¿„sŸWCº’hcy)i8¢Q'±Äd¼Óêg2G/uÆx5k G"Õ†kÜç&qÒ½#NJò(é:6v^5£’ís [èïÆ²´¾ý~½‘æùÇfó\Ê{éwy¥vvø–ÅÿWqþ؈¡p¿>ÿ]³BW¾µñ»øKçø«&sׇ{Îçnšˆß­õ˜¿ç½òÓ1ú*”)Å dfë.úwWv…¹mÚ °b€Àt¤²ûÿ±Yìÿ¸£Ý&ãHÑM ݾ³Ü+ÐõÐFZ¥ãgV[{UåëÕDù;ü ]c~Ṹ+¬×f{Vß¹F±z­¥z™ìN¤Bþµä{ßFß„¾È]Å‘Üå™2uÌÔ‚Âù;\F/¿Q¼[:»Ju€îãf±(&yÑŒäè9qÏD/˜1fáìÔ„˜)‹f<·(5:ÞyjJꜸ…Î~>¾þ3“΋™ i!‰‰¾Ò¯…³æÍH–¯ËgÍòXߺ²’ºÀoÚK½¯gíÔ±‘ ûÐEømÅjíÂjíÊj5à|¢µv3i¯4[8BØg­˜®cíв‘µC`íаv¨Y;T¬Ò(f¾Ǻ[´¬,‘•%°²4¬,5»*6·xæ­?S?Çjå˜9æGŽù‘c~äÑÿjv¥Š]É㈶¹Ç±eã ;«Û†åíÆÊ´f÷ÎÀÚÚ••Ð…ÕaÅJÒ³ulˆ¬'Z6ÖB Þ[k_gœT™ bÒWŽÍnɦÄùDþôà1+Mú'Y¥¸Ü íÝYý6¬þn83h{ ì.te>éÂj´b÷TÏZ¢ccKËî†ÈÆ–ÀÆ–†ùDÍÆ–Š-çÕßw)ÒJkŒ‚ÅùÍo³4¬W*鋱™ŸkÁ1AX,&8ޤ1¡ÂqÔ9&„޵GZ¹Ô8î,½s"¿YS™Äi5kƒ€qZ“*¼^¹v=o²*ÈsžÃRTX®šY5f£@*G^G¥þ+;dEGïTDZ“ˆÉ8úã•IÏÚ§&ë¯|m_Ô LïÁÊrceÙ°²XYv¬¬^¬¬n¬,{6>{³–;1O»²Þ;3öd³Æ‘Ýi[æ·î¬GRÄqcmîÂFrWì‘=¨'ulLk1®ªX¨/혤gwÒŠÍ7Ò8rcþìÉúÛ¤ÞlƸ°«ÿÕqhm]LÆ#IÖg+Ög=볎õYËúÜ•õYdwDm²vñ¬ç*Œ ®\-+W4)W÷åÊãFë¦+Çjê[òXéÌ£êÈ£$¿¯j¼VŠ„›¡ÑTÄ1ÔY¾`2v¥ØÊwøUž»Ò¸7ߪŽõPn·¼7ѱò¬X𖥉¬<=k­†ùDÍ|"tøW…µsèe¹/¢‰u&ãÞÊijÒ.ÆÀƪ5ëg·Ž¾IqAZ¡U&}“}dÙË<]±mŒ†j6Êië­X„“öŸ:¶bjtDiX4¬v?4¸»Vá*Û•ÝqÁ¤|i ïÂÊ·båëYù:V¾–•/²’V¾£€†õWƒ{›—´‘å‘~ÂñßMX!ÿ»CÈwÈÿ'òùßWË“8y+ònä>ùø> ¹YxUb;äAÈ…È¥ÈõÈákðïø ‘w 7";¯•x8ònäÈúu;"BÞŠ\ü½œþšÄcg ¯@Þ†¼¹ù2ò/ȯã÷ û`;‘×"ƒlXõ#O@ND^‹\Š\ü½œÿ ̼ù{ä{ý°r`@Ǧ2ë_d˜s)ð"ÄtB†€.Ø(È× §;(ÈÞdXøs„äƒ=ê >s¥ –5™}ɰ#þ¤BzèyÏâúVºâ \ßz0èáKy xÛBB†ö!ä¤Wƒýy°¾@ˆ½F–BýÀyÀÀïñ¥dX=èC¡üH'é3Ê~8»·‚,ŽÝÂgŽïG†ÝÙÌgvq&ÂAÿÚ[ |Ä êëMH±?è;øÌgéu„ü4Œ »ò !^psÌ=`ë„”‡B{Ÿí¾ \ ¼ìykY\ ÜŸæ[KÈ!šx+päbBö¸ƒÿ?ú ìù²y8ä „ñí>Æ{0´ øU`Ã0_À‰À_×ï€r|†ò)pðŒ‘p=pH"ä ”S ¬× dp"ð$àJàŸ Ÿ0’àºHà¯)òÖÀ¿!„Œ›þxÆ‹7Üï (Ú߸G'ÂýÙó7îó&ˆ’acy8x´{!Ü7à ÀäQ˜—ÀQÀ}á:àX`çph?p&pp#ð $¸~2!éÀ…“éß)Àõô›™’¡ýP¿/õ_!Ì7¨7 ì'€#§½ú¼Ø.‚·ÓÓóîQ—‰ÀÖ½Á/Àó@7§9‘™ÀOû‘Áoò™EûAÿŸ9¦ŒŒÈ^éÀƒW’ÕÀ«@O/ä3¯Œƒtàã¥dD%ðpÈøÓ¥d)â3uÈúorOt$#œ@z5Ì‹fGÙ wLFô„þ?ív4Bû€þÛç›ÝȈFàÃPý·Z h90~¹ d„ð 2¸_”<8øåÈúo•îÌ$#b= Ý‰Àå g+¡]ÙÀNÀÕÀ¡ýÀµ^dDpèyñ=Ôg ó„Ö ¼ ØømàpàràHà8È<’úx7ð6àAÐßJà|Èçñ6Ÿùø!Ða~ͽ8ýßP̳ƒàJgBz÷‡y ö±žz;ØÃ]yìp °³+!‡Á?°Ù#Éà¯RЇoýp$ppx§aøèw€§¾ ظÐMÀáÀÉÀFØ_¾ú^à+ »ƒ|À°ÄÀW }ÏAûj!üóÛ9Ÿ^cá¾WÏnƒ|ƒœÁOëùòOnæ=ÖR ýI¾3ŽŽOB’€ó€_£ì ëÌh(ظ8 8êaB– >0?©òߥóÚ—'ažoN†ùìã ôÈA„äB\hn£ñÁÖY˜ß>o†W!äà€]|fô+*üúú}¾p?Fræúhˆ'0¯í`~O.üÌÇ• ‡B¼ƒy] ¼8r,ì1aß~¸àB^0òp=° Ìçô0B¦ßð&p%ðWÀ‘ãÁï0Ïï.˜þö™HÿÞâ ðvàFX—ì Þßõh<ă#~{ÔTB`¾ß¦kG¤ó+J]¥§úü”‹œ rê;#7wº27ZY§ªKàêR¹º%\cœª1Tå·Œó‹æŽ5ðA-üþŠýUŠæreó>ø_Ñ\¥l.ášË¸æ*®ù×|”k®ãš¸æ&®¹•k¾É5ßå–Z)–ú*—*—†(—ÎT.Q.MP.MU.µWV¯îóI´²=ZÑ¢hwQ´+í‰Ò°†3ðеjx[+ø“¯i ú“¯u1N¾ÖÍн¬¼‡¡{C¹¡»âƒ^~ŽVÇ{û9úw6¸Z}ÚÇàêÿi_ƒëÌO= ®+?ígpÝùéƒwÒ)ƒ÷úS~Cè¥ACÂ¥ÁCþ¥ƒ¡üÒPƒ¡áÒpƒ¡ýÒƒÁ¥9Ø`ke0$51Ö754 †Öæqƒýå zy’A÷›pƒÞåúƒ>ôú4ƒ>îúcýªë‘}ùõ' ú ×gôV7ž2D½ó7Ë0~Ï5ÄÂïy†øŸûñ ‰ÇûóɆD‡|Š´Ù†EÀ+ƒFzyq¹é¹Ïç*¹*E®¨ÈµRäZ+rm¹öŠ\'E®‹"×]‘ë©ÈõRäú*rý¹ŠÜ ÅåE—–NÔÞÏž'‚÷ DëIôÞY¤‹÷ bðþ7éæMº{¿Dzxç;ïÕ¤—wqô~•ôö^Kœ½_#®Þ¤÷¤¯÷ⱩôÞ¼UzΠúoaÇm;мH^#’ÏÉeøO¥Ð)+è—ÙìUì‡ÿ‚¸ nWÿµs¢R¯´…ÿ𔗕וÝù^üüÃG´Ï ì•kÝ+W´Ëµ·Ë­³Ï-±Ïµ¶ËMµÏµµË Ù>×Ê.·Á.w•]n´]®¿͹Ä.÷LÏÜ$ûÜ•ö—CÝæ¾s¦Ëщ†£q®G÷¹t óµö¡>Oå7ÏÉoÞL¿y^~ór|çùúÎ óá¹üá$YWÎì]™Ô»2þº¨kõ*Cuœ¡:Ä0%£Ë'ë~µA~µ~µa~#ìGìì2¢Èyÿ*Ã~ÃþŒ®ûsºîoéº_4ì·¥– À¡?(ÔeƒÕ%þêª!êœ!ê êõÄAjÿ!j+õÝÁꣃÕEƒÔù~êƒÔ©ƒÕ¡ƒÕ¶ƒÕ×ýÔåþê•þ4³»¿úö õÌêªAê3ÕÅÕ ?u»Ÿú¤Ÿz£Ÿ:ÎO8P­òÓnõ UÇ&zÝÚè³t¢ÓRw§¥íŽKÏ8.]å¸4Úq©¿ãRÑq釥IŽKs–F;, tXjí°´µ×ÒrÇ¥3Š7>ü…í€ö*ÝC½U%ÝUíÝUeÝÝ=öVÙî]e»7Ú¶y²}s´}s’}ó2ûæÛ=›Oöl.îÙ¼¤góäžÍž=›=›z4—õhÎéÑÝ£9°G³Uæ&Ûæu¶Íq¶ÍA¶ÍÖ¶Í-Ý›÷uo^×½Ù¶g³{ÏfÿžÍ¡=›#@¶mžÙ³9©góÊžÍë{ª7N÷tH?*¦—9T©´âØ¢‹tâdßú.~+E¿¢.iƒV»;­rZ=Ýiu’ÓêUN«KœV×8­nrZÝî´Ú¾÷jÿÞ«'÷^Ð{uNïÕVÇ8¬^æ°z½Ãêr‡Õ'V·:¬V8^ËÐäë Šôeú‚ú‚vmA¾à¨¶`£¶ U[¦-pѬהè ÂÄ‚“º‚b]A‹¶ IW¦+pÑÜÖÒÌË´Ú/í‘õvG®÷<â®iYg×ÒÚ³eºº½Î.¤¸ï¨‰-kõûx½ïèÏ k— /—ŠP—Šh—Š —Šu.;]*ŽºT4¹T´»TغVøºV„¹VĸV,q­XïZQæZqÒµ¢ÅµBáVaïVáïV1Ñ­"έb™[ÅF·Šr·Š:·ŠV· UŸ §>}*&÷©HèS±²OEQŸŠ:‡Šë*Ç ÇŠ@ÇŠÇŠÇŠÇŠ"ÇŠ}Žg+n:VˆNîNANÓ*’œ*V9U;Upªhpª¸íTaÕ»¢Ü¾â¤}E«}…¢W…S¯ŠëNž½*B{MÝ××s™ÛÙ¯³eneúÀ•úÀéúö±Ý^l¿)´Ú‹„öT¡}¢Ðî.´·kÚ[º//kÛ^žŠÅÆTÅžeÝ÷xñ{Vuß×gË…š}}ææL6̹«MšØ?©¬ÒÉþI©ý“Bú'ÝîŸd5 ©¼ÿâh¯/¸‘ïþÅL÷/¼ÜöKßÚ¦6Š™Ûw¨UËv•ª#[_}owTù!£½1r­fôñ!F‡0:v‚ѧµŒÈçŒNÁȱžÑÙóŒâ/2â8£õþ#r(l ùº‹1 ¹ÒÃX¼Ž¿ª6žYÇÓÛhõ£·1ô5¾­¯1õ5þ»¾Æâ×øï½ë¸£6–ïúI™ôaÚ/PnÒ‡¿*8Æ*NÁ¸þÃ_­8µ±îÃ_­Ä=¿Ú­ÿ8Ížl¬û8͉ áVÓÜA =˜æIÕeÓ¼@­:˜æKÕÛÓüAõ:”HÕ¸CiA J ¡ê…Ci¡\>”Q“6‘+â¡5iœzÜ’š´éPÑΚ´™@-5iÑ@N‡Ób€"§Å­:œ–Ts8- ¨ýpZ*ÿ'i@ Ÿ¤-*ù$m7ÂXtÌq%íKÝ1ÇZÌ ×U\_ãä®ù@9'\×Õœp]OóÜ<ẑkô:éZsÒµ˜ó6®;éZÂM2ÖtÝ Y]>ó.ãz£?ó.‡´üϼ«8ïq>óÞtó3ï@.µÞ5\ßq3k½rëUãVÕz×QÞWë}†òõZïÊNuÞ(O¬ón¢¼¤Î»…òÎ:ïVÊ꼯CiVŸ{ß úÜû6PÂçÞw¹ÇÆøÜ»ë=N<å­P6pãüOy«”}K¾•VJoc°µò)£¢ÞVivE½¨tÞXï¢tWö6ÞöTÎ5NnpQz)ÕÀ¾JÆÊ à“À!ÊÇŒ^g•¡ÊÆ“g aÊ4£ç9ÃD(}ú9Ãdåz•1çœ!‚rÕ9ÃtÊ­ç 3)ÛŸ7DS;oˆ¡œqÞG¹ä¼!rÃyC#^0¤^0dÅ]0,_0,íÌÃJ¨ßó¢!š›tѰ ziÜxÑy.ÖAë¯ ëi›¿2l-ç+ChG¿2ƒ¦h4”€ÒhØ ”Ñh(£×7Ê)×5ª(·7öQöl2 Ñd¨ì«š Gjš 'Ú› uІÿµUžÊÅ+¶Êå ÆŒ+¾Ê `? Ü®œÞªo—´ê[¡þ†VýuÚÅ«ú›´Wõ·AKºª¿ËºqUßNýpòª^Áƒ~÷ª^ŃîyM/R=âšÞŠê+¯é­yoã¾kz[ÚqMoÏ÷5~£wcÒ7z0–}£wç{¯9sž¼nOsû‚ø¬¿Ä‡%>$ñéà—!_(ä>&ñቛ,ñéàS1Üt~&{9 ;$ëµ?Ÿà6õÖ¾ü·¹·ñM¥2ŽOàu<I<ÌìåTJÓ-*dPÓ|HKÀtŽ Ë¨iª¤•`:H…j¦i˜Þ¡B>5¹ ­Ó*¬§&ÒF>D4ΧR1Áã•Jøé¢q˜¤2j{‡Jå|’hÔé@ÚÇ/ÏQ©†Ï·¨t’Jóõ ÕQï¤Òþ߯ÙÝ” ü¨õšh­ãm@jÓ*´RÓÀî ]Ó*ܤ&!üm0½L…»Ô¤ìR;˜QA¡Ó5*©TƒOöA¤¦ST²Óh;¬©i•lÁÔß{jÚB%'0ÙôÁ…š^¦’;˜~¦‚'5-rÉ LÍTð¥¦'AòÓq*RÓh'‚Àô>B¨©oBÁô&Â¨Éæ_ MÓr*L¦¦Ÿ©¦ùÎ L§¦f*ÍÓT¢©é8•bÀ4Ì„8jzŸJ `rs!‰šÞ¤R*˜t}@È ¦åTZ¦[TXFMóÝAZ ¦sTÈ¡¦©}AZ¦ƒTȧ¦a´LïPa=5¹y€´Lk¨PDM:OŠÁôJ¨é•v‚iv?ʨé•ÊÁ4¾?UÔtJûÀ4p¨é*Õ€ÉÁ „£Ô´†J'Á¤ô¡Žšž£Ò0]£B5Í~˜Ž/•·ñ šT0ö|èøR­6Ž¥lU]Wõ6n£¼©º Âñ0å]U;§*j•º·Ñæq¥¨¶á¹'”Öj[ÎTÚ«@¸õ´ÒEíN“┞j/5TK_5­vHþ`:E…@j?¤ 0í¡B5 Œ)L[¨FMÕՓ¡ÐSÉÊõtÖ¤*gª£AXž®ŒQÇà°K™ NR¿fÜB…Tú¿ B†ú®ÞOßËÔ+Õ0)—W)sÔ«¨4ûCe¾zÚ×Ê8u¯r£ºH­6SðÅê¨ù*ì¤5»q •i Ê©éM¯Rï“Ná5-§R ˜nQá(5ÍׂtLç¨PGMSu zR¡„z.€°… M ØXÐÂr*´‚p‹ ×A˜Ý„› œ¢ÂmFwá.ïS¡7 Ú¸† *”Ö ˆ ,¢‚ÍT°aj7lA8H{Ú€àÂ*¸€`Ów–SÁ„[Tða¶-¾ œ¢‚?£{€¨ñ6n¡B½¼'!š¾ÆçùPM˜¦·QéÌOÔLÖ@8¢B„††#*MÓ“. ̤¦STŠÓhWb¨i•âÀÔß „j:×—OÒ¤B¡×êø Íá—iV‚°¦žÏѬáÜ9>_³„þ_ñë5ižK|‘¦·¯ùÍN¦^åË4å ©Ú’¤ú FyQ®Lä.·ðq9êÚ ê¶"Á×V30P5h•zD´jj„rF†ê¥êAeÜ¡ªªVõ5MÑ|˵Mœè¸SÓG¡|Ö](lÞ]¢êqSåÊoðäÞvÒrÑt9#øåâgri å.ue‰¦å¤âV’zP(?¼U=ÙKߤY‘*l…÷¬”§£U—CU?ÁnnŸÆ¯‰Ÿ”ÊeÆñÛ&*J|¹ž*»Ê!«,ÓDûs+sT¹NŠM þ xò9ªv âüCøÙŠ*{õ‰Áuîêë%|»F!¨Q°¬[Á^p\wÁSð|!PB„P!L˜(L"„éÂL!Zˆâ„!IH2„%Â2a¥#¬ò…T1C\".WŠ9â*1_\'®7ŠEb±X"îËÄr±JÜ'kÄ£âI±N<#6ˆÄ&±El¯‹7ÅÛâ]±]ThUZQk¥µÖÚjíµNZ­»ÖSë¥õÕúkµAÚm¨6L;Q;Y¡®©ÖÆhã´ Ú$mª6C»D»L»R›£]¥Í×®Ó®×nÔi‹µ%ÚÚ2m¹¶J»O{@[£=ª=©­ÓžÑ6h/h›´-ÚVíuíMímí]m»V¡SéD•ÎZg«³×9é\tî:O—ÎWç¯ ÔéBt¡º0ÝDÝd]„nºn¦.Z£‹Ó%è’t©º ÝÝ2ÝJ]Žn•._·N·^·QW¤+Ö•èvêÊtåº*Ý>Ý]î¨î¤®NwF× » kÒµèZu×u7u·uwuí:…^¥õVzk½­Þ^ï¤wÑ»ë=õ^z_½¿>P¤чêÃôõ“õúéú™úh}Œ>NŸ OÒ§ê3Ø÷œÓ³Í(vìjpóL›ñìscW -µR1ª‡se¯ˆšų̂ƒWÏÛ1éâ¼%úeú•úý*}¾~~½~£¾H_¬/ÑïÔ—éËõUú}úúýQýI}þŒ¾AAߤoÑã‹þ&«‹~^+˜ž³Û(Hðž4vUàôE é{[çý<Éî¡€˜Æ“{‰Žê¥ ‰À‰Ë €ôÒÁ R úàFàØ÷¤8ò}(ücžwC¹ÀåP°óiƒt Ð@y™ Û}¡€˜Í“Là<à+À¥Àá_*H5¤Wg‚{t`èØã+(¸¸òÝá9âsÚ«âH-­WÍ‘`ÐëÃó4 ƒÝGàHèµÀ‰ÀÆFh/ÍlÎ9RHË×r$“¦RgÐíšÁÀéÀÎ¥OÕÐÏŸäÈqYIÞ ‘rƒtUxA$ŒW¬Q”)~PÜŽ¤j»'‰ƒUÚñabI‚P>]{¨X{~ºpñ¤ Ë¬÷‰¶9Z{+½‹½îá™Â˜1[%l ”ëO.Ïô«´n´}Öi¦•èæéæ•hžµÒ-[&¾ž œ®ÓmºYk'Ÿ#u‡ŠuÇJtŸ ÍUÚa*a\«0å¤vZˆö‰š'Äø•âNOa÷F±¶Uûø:¸(«¼?sf¸©3*­äe!²$¹ˆ‚„ˆŠˆ‚ˆ7BqÔI†;%;Ñ‚¬›DÈR².»Q’Q"‘YaâHHIFHŠFAä¯ë»ïïœ3ó0ôÖçëÿÜ~çöœç<ç™9ÌéžkÓŸkcŸ>i¶‹M@¾årÕ¤H×Iy]V”Z7ùZ}Sa=¥ÐÆîÀ¤—\&*¶x³Îú·`ëÿXXÞ±Á£ä!ËÅ–K[­Ã[,¢:-ödØdÅX¾‘ky©ÛjZ†¥›·Eè°,l®Uô€,ÉÕêív‹÷Zeß&Û TY=–k½¦Öz+}¯°Øh{Çú/ýV¯ÛXœpµ È|X.+´Xæmµ;ÛêC­åy•uG¿ì²Äò»Ëk-RŸÙ–«F-Bý­,2fË2B,ÐW¥úÙR¬§{´V7 ­þëoah9'Êò‘ Ù¼VéËçîH³¤‡*,_u¾•i)8IícdZÌk–ÍêJe/:YV×Jª«%ÕU’êdIu©¤ºDR]lü·PR­•T;Hªí%Õv’ŽNÙm‰túY²¡Îb{«¬¦EZgoј+³«’L·“ºyK†Xl™-ÛZ)ÛQ%U·“ç½-ò³e µäÖ1é)£SîM‘È-ä6r¹ÜNn/wÏ–;ÉçÊ]ä®r¹·ÜWî/”ËCäaòy”|‹ I§'àVh þŽ‚*@†¿ôƒ_æZÁ°g~º9g>ütOÒ«²º©g,ݼSpD@7Ý,³z­ºA§ŒZ„Ŷ-¶èæ"w¸éæ –nj °èf +È'nº‘È6|£{x p7< 6Y 'aÃ-˜÷ÇIB–/Z NnFœµ…2bÝÕ³ÈZ8‚:5ÄX tóLåþ zoÄG0´ù‚ €ðh+Áv«•àî?p÷„î° Íi€#(X€²@øN¤‡@››µP’@°=®¨H ¶VBÚ;q<„0;ä„ÅN†nêõ04 Àe¡gfON‚ã  ¸#îl8²À>ö€X°„ƒU øGÔ•n›0Ï;ð$xì* ž‚:0nùBA˜悇@ G–¼jÀ·€µã-÷`ía§à ÁMpÐM  ”‚³àp ÜV‹±–N`XB@H¯‚¿Fðø\£àßTƒðLà¼A Ø ²@&8 Ž€÷A ¸¾?Û sú2ö€wÁÛàkð¸ ~KðLX T T‚ 0nú¬ƒÇ Ð} \?ƒŸ€Ïš)`+Ø~û—,KxA0k +”ɲVÁº•eÅî—ØÂ¿5ìfXGØXذ{`çÁ&Ñô°a` `}`PûY–{Âa`Ïc].Ðõy:ê'—Cà Ö)CÀG‰{ì™*²¦ÒyëìX¼Olþ£D8|þŽr‘æ$‡¥Aš›I¸ç“1_ÙI„ßàŸõ|,Üe˜sŽÃž¡sl¬´7ac‘> /ŽöÈ<Ý$‚õ ÔG|€¿Â<ö „}uäA>Íît^¢~Ô¯i7§`Þ hëôçM°ùú”ë?ô =¡$Í`‡ Òƒü„3 Íð‚5Ãv(¬±ì`D¡ç_@Þ°WØ~BÀAv@¡°¶çAv8¡0o&ò„?v3HiØLv(=ø‹î…õ,Êî ãxl,Ø7‹vB8¡‡tу¸èa[ô@-Áø€p°d#àälúœ£Ï@ú|¤ÏNú\¥ë Äãýë8(›ÃŸ_I˜£Nb>r}Cç"Ì)Cà æŠ2Ovð=µÅ½rà^Ø mزÀñ'Ù&ôöS§ÖôEk“yA(ÌÃ;Ö¬ö#nò«V¢ÀÁUló·pìYøPăUkP6؆²±>°D8(Àµ¥G¿ùþb’¾Øô)æ¢ïª¾}æ—:R(UÉ<,F-Z,K¬TÖ6’I[&N ž\:ùÎä³Sòå1 Wå=e³m±zªÿ4ûéƒÓkíóˆúÃÜwf´:T<˜>Ó–|vëìŠ9é v´wÒ:ù?dïÜí\ü°v®÷#òG»­˜—îâÿ˜|~çüŠÇÓ]ƒÝûü ÿä“Ûÿl×9iIò‚ôsJT-hYп@â>ÛÝ×=Ê=Ù=ß½Ò½Ù½×ýž»ƒ‡àïÁþøÒöôŸžÚè°°'7yA­‡£QÓåqÇÃΓk\=C<¹Fí)äzš—Ôé)–4êiïåá5¦2h¼Ì5•^¢¦ÕkÀËb¡±vN ÿ…ÐÌ3Õd,œÅ¥ ëv.WÊðB^ÊX{r½çpE•w‹w¿÷­‘,¬É}5S-2h,:¶èì"£¦k‘¨¹³È¼5[j–¹¸dqíâq5k_l®ó1”’ì“ïSé3A)Í>æ5Sû4½î¹¾¾¾\Õí;êkÐØ/1ï_þ¨`5yAò’yÅKª—´.Xbáçäçï·Å/ݯÐOb]ë'Ü“òÿ‘¸OŠQ?>DÆ*ƒçEö|Ü'/h|º—±Y ÝO˜72ÄßÐ-jÿlÿRÿ±Fª[ço®,54qöRߥQK'¸`ÉKÍktÖ¨éZzg©Ý“h\Ÿ4×TÙýäè“t¿}€yÝò šÊ€æ€Þ€ 4÷Æi¢ =˜X8®šÍvË ¥¸. Y¦^6Ak²—™kF—‰7“}GPXи¨ 2¿™:ƒÄ[}8H¾Üeù%/7ï·cËEÕÙå]ËïL¤² 6¯_a°¨ª n îž ï$+ÌËrYaè»àª™+Ƶ©d…y9ƒ+ÄrlVÎ]¸r‚rbVš«ºWÊ]i¿ÊcÕ¸k¶Ê\Q»ÊpÚW ®² ™ 憘k*BŒ#.¤;d4d¢·Ú\¼Zljuæê’Õ¨jW›÷›}¨¨ò Õ„NP¿¡æªöPCýCmÖÌ]3AIkÌë×»ÆÐs÷Ö8„y‡qMD˜6Ìð€É 3W ‡J‘¯uY¼v‚š©ÖškºÖ>hlϵvë\וƒ2BÖ™§¯^'¶¿×½uÝÀ:‹ðq§psUiøœ1U]xgø°‰Šiäã4¢¦.¢3b8bÜ#V¾Þ\S²^ÔÔ®o_?¸~\Ýl"³ks¤u?K[²@è4ÏI»AÌ)wCÅ†Æ Æ~éÞ`Èitƒ¹&&ʤo2¢Š£ª£Æ•ße®òÞ8Ë¨ŠØ¨Ý˜»Ñ´heÅFóÇŽ÷¦†ô›´›r7ÝwÅ*6™çï°Y¬•÷æˆÍÚÍ-;6›«îm6Œ>‡-Þ["¶Œ›´[ƾ-b9ò­.[ƒ·N4þ¶š«ú·Ƭ$zv´oôš¨èqϤhCݺ¢ïDÛ=5®n®O™—’û”x5+žj|ª{¼fô)ó9<9æÿ_åǘ—2ch‹Å6§mþÛ&hË–mæšÆm†Rº·n³ß>®íæ­¯Ún(¥e{ÿvIìsÉìØqO×XãÓ5¶9¶7v‚šÝ‹5/g‹Jì³tU¡ªJ5®n-ªqk’†rÔ;²w”î_·Ç#3RÓÔZ·ðÍ3ª4µ;ˆ(1Iз³ßȨÛáר,~çˆEœàW}N Áq~Ÿ‘LêûŒœ…å&ð<3çIðNøZȘÆÒL}ˆ…¾ $&POPŸž4Ãd\”YìrÚ%,1Ôd}zBšF«vÛ¤IØ™ølªÁ«Of¿Í¡ÚEKÍÝÅJ¥¥2sTìb4îbYvïÝ%þ»Y>Ô óÞJLQ¯ÑìÐ7Òn%[˜¨„‹J¸ˆ”Z»[˜uýõ*ºéƒ´3ͦ¡˜ÕÿocÝ¢BÜ6«´ñúaöÓ>s÷Ð<Âxa<7CkÓµêM\ªÛF5=-U_HßЈ†© ¹ª«¨Am«öîñrjwBbjÕoHLŒOÕ{Ð/jI Ssõ0WÃÃÁ˜º“§îä©aŒ#?(1>£ý›ªw˜iL>Ì“óä0KÆ'‡;!.=%E¦¯›eTÊã™’(a&R®MLX©N cR¿eŽQé•.\ ó˜¨Ô&%& ˜0Œ¢xýèŠ`®æ c£–«ÒTT¦MLÐç:“«xrO®«šéŠLSÅíÝ¢ŠSë]œÊL®ÌäJ˜…Ýa¬ë[6ÊJ¸¬„Ë`Œ³ÇòŒ•V'Î%µ\RË%0K ’•ñ‰;Æn…à}iê„TvÑ\æµí\Ûε0S Úuú.cªAžj§‚™'¦r[¡‰WsŸž~fÏ6Z>5iùÔ¤«tŸÀt:¸µs¹v.×ÂxŽiCRã1…î¤çbjÐWºU\ÈU÷•• U% ùÎ0µ61%ƒ›¨ÒêCÜÚ®áZ˜ Ú5š„dý€‡1]O—ÁÓÁ¸™¤Cÿ²cY—x5Å\SÌ50Æñ¶ný9V>èÙ2c¾‘¸R4inAñ*6UŸ±Aœ¾“øôħï$±¢=ž§IRÅë6Š7WÍå*˜ûÖuëÕ»ÓãU)¦³då&qöæÒ@.…Yd”°=‚4^•”„;&d‹8ss] ×Á<>NGOîÄh«8‰sI—À,Xâ®JQaù¯Ï}JœÌ¹¶˜katÛj1‡‘ê¤kIq’k‚0¢¹–Svmd€ú+á/LnVtõcí®ç\ɽÈuø¯ëçmñí‡ôº0’ltT,EOÿHá÷&þóß»aâ¿rcdË€‰¿``dÔÔï~k$䶉?ëöHµè¸íìøÃˆæø]~àñe?ðb·DpÄ  Óı–>Ãböí`fÕÌœäæüO¼9CºJµË>ÂN#¦¶j]Än—}9¶Ï²n_ï>» |d3I?ë†÷ðXY{†äO<ÿ=:»½¬@УÑ5Æ#Vð<@ˆgfè]u ·Ó±€›Z]I ðåe‰º– â“y­~å%ýªKN£Ñ–QÏx®ê9]oZÌËîçr3ƒŸÇ…}ž%:“æðç?óôÿµÉòÍ¢ÎW« ¡½·Ò]JnIÉXÿ½Z-%BpvIv]ö@¶®òYÚIêýÅû»÷{çÄäèêö±úäçðÌyw$ñÚõ<ÇÌf^ð"ްз+³Ç”„wÒQfþÁ‡Õ?™ (cfÆkܼΓ¼É„½v\8TÉK>ÁK®âæ]ži5´§¸ð=fŽ¿ÏÛ}†gZÏÖ‡ÌÄ~ÄŒOïŒF^™&]r³ÝKÿ•ìÿŠv£/ªB2ÜVA3É¿ VÒYdூ- $/°`—„Ç7¥¨’ð>!žfÌÎ:N¥±•/¸7 2¦‰9(Xó'ç Ñꊵ LMUkwÄgÐD²áƒ†ÎhÉtí–‡B©ªååáé…*,7õŒ¯O(êó^ÜŸÑÄ©é¢ÒzCJºz Ÿ—' Y7œ—û݇“ÿ*Q¿®úóáCÄéeæ*~YògÜšÔ݈ Òûòêßé‡åË—­Yîé»(ÐuùšÀeºc_¿,Èl œ ¸™Ö·@Ò¦*€K[ kok. ð¬þv°üy/rݯڡ+lÏ,„²´Pçp©¹tÁ£;v‰åë}ØÐ1>žžÈcµ©ÇpîáC©:,À%V{²uÖéòpbñt3Ýg(X¶+ )*,’uIØÏ@žIN×l KZ¤ÅÅ4©íÀáŠâÎbù+vEÁ¯¤¿RñŠLò·ÖWè‡ §sT4ùÖÿO·¡GHF‰áèl.וL•ü½ÿï¹ÿ”f”—ªËºË¢Ž ‚Ãa )3…ô¾ÆÊ»Ñwˆôî}ŠˆÃŸ¹Z–^^Zþ0= lGŒ~zRÅb£¶³ÜD;Xδ6Y&Z.*ý_°*?L¶¼Î‡)]y>¾QBWþÏx»¹Ï.=’>^•ÁÎ˦S•¤ˆËr­+ëåà VD]ve…@º˜‡HÞÐ~I‡ƒÌãÖ7á¦ÏñÚ㮕‚¬¥2ý-„´¼5E~"ÿÄo÷¿­©HgBÙDßû\Nï’ÀwÒø®Îé+Ão]7w¦ž îÙ•Ë×{®þ4Æñ¼Œçé+Óv²©#òjÀúäλâ­CnÔ4àŠQa'7J5Eÿ»ÑÑH¯ii)šéij«¸&R|rÿ^žVMMmÆ"h)ý FϪ±^¯ÚÇ\©¢j‰a‘J“!j‡& K1vûØk7–cô9•®›áAt‚+3Ôð¡)†“ÝqU7NO4iféŠ_ö1F.W§Æ¥hØÜ?VŸ±È]ªôø´Àxj¬¶îÆXVÝ„Œñá)‰;ÓãÒĈ…c ãÄzÐÏB ƒUŒ\mŒ IØ•˜¢e½¦Š7Oµl­:= Tñ}d*aw:^DÆš\ó)±¨V,KLŒWã… •Ç‚ÖDÒê£a c•ñ4–g|¹c‚«é¨ ®¦$¸šŽ˨ íÊÂjAÈõ4ûZ@xÀä£-ÃÝT·¯ªš ¬Ã†;×éTiùô±W0…aèœ~;E²!ã(2NÑ[^'ù%ì”@šO ²¨]Ä/ݧ¬‡OeÖØ¼§yO°xovmD­­ñ“jàøSa-+ɲº¶«Vò>-ó×ò+†‘­ Ÿ*/OÔêœþíÿ>Q!Öš¾ÐÚJ»±bìzßt¼ëÿ­:MJNÃUù›ê4æ®3Ôe¨RÆ)Ú6Û0•&~NÍæI²ÀþžÆ÷ uÉrÏÔ)±ÚÏ Ÿ‘Í®ó­C¢ê*êLBHgݽ:ôRîb÷AØÌ5YØ:è'’ûaÌlJOþ äƒölêyÕ¬ëÕõ™õUõ“-ˆàñQðY“j[Ò˜â³<¦ä#Ó˜ÌúÁÔõ–IIvR!÷ãÚI×Ç‚€„w>l%7ä70_eC·Ñ‡ÊÊý‰ºÑàÎn$ÜÝØh÷ ½¶®ŸDQ‹ ß™+õxCC}‚gÉ'íŸÈ›‚›²›êšî4¹~ªùôاÉt*onÎl®jîož}.ê\þ¹æs÷Îy¦ý¬ò3Üûl’ÿùÌógÏß;ïßB›™‰fVÕšYÚbÚ˜;-4Vo7æø™ÖÙüAUxÃõyÁòØ…Ãídr½·žOn1ú`"¤ësõ¤JÏûý!úiúvú­ý$€ÞpR¾Ü¨~¾…‰¦Ï¾q1÷bóÅá‹®­êÖâÖöÖÓäŸ{óùÏ«>çcšf% ;YàcN_¨¾(ü¢ñ‹á/œÚbÚòÛζ ¶ÍnkÏl¯jïn—_ò¿¤½ÔxɦûC–ÜQÒ1«±c¸cîå-—ó/7^¾sÙ¥3¦³°³¹s´ÓõKÕ—ôa¹UÊ ’ÈnŒÄJ«Ÿ·ûŠ߯2¿ªûJrÅÐ)ÆØ˜+†ØÒ+ÃW"ºÌbó» ±­]®_‹±hÂ颽hBÉ×Ô»¢õk—«[®æ^­»:pÕá›o2¾©ü¦ë›nßnMwEwg·Ë·Éß–~Ûõ­Ýwßå~×ò¤'°'£§¢§½ç^KoTo~osï½Þ9­¯_Ó^+¼V{­ëÚ½kN}!}É}%}}}×C®Ïɸ^u½÷º}Hfu¿Ã÷aßg_ûýÀ÷so¨oˆýk¬zÅ CÕoØÜß웆،›­7MƯëõ>´e r kÀæVà­ä[¥·ZoÞr¹uûÀí³·ïÝöý!ù‡ê¦¶¾2úƒÓ`ð f0°v°{Pþ£­ÿÚýØù£ÍOþ?%ÿTõ½*宊@ÔCôg¥Å9?a—fw:_¿‹ê³;š¤¤tˆ4Ño‚šÕlm1Lo!ü#>06¤¨vªµ*,µÂG0moÔ¤jî{ôá©5¼½JàAáhø.É1*Aó ªx#W÷ ª”Ýê´±å»QïðÐ:ŒµÇÏè’àì°Ÿ‰.CeÉ?7ýz؆tŸ Èý9ÿ—êQÿ©þß»wUÿþOò½Ñ{ÿ;ú¿%ÿMHŽ„ ¾’CH‘”ÉH¹©·$V¤ÏšôÙ«“ÈÕÉD*'AJiKâìHêT"LÊý0g z€x|tâB‹)ÿ™¢ÿ™ì@œ$Ò™dæ,â5›­DöÓI½xèb<[ƒŽˆ¾9dþ‰ýÇûb¥Ž4v²5¡ƒZGIn3+Ku"5NpÉFœÈ|gòëúI$Ï™<ÐáLœççGˆó£Äy‰v!q‘£óIùãä„+©qƒ‚&>d]¿€t¸“ÛÄË Ž˜Câ5qìJ³ó'[³y°{è±J‘Ó^ïAfœ±»÷ß6‰hD‘7•úE¦Ê¾j(«êË.•úÜ'ExŸÏ˜væSmyíýÚ‘%æÚ?òkÊd2ÙŸ<çO¼–’‘'I_é$EA$g9q^A¦­$‘«HP)_MŠBÉÈÒFœ×‘iádf‰ ]si ñ‹$²¼HÂÞ2†¯hèEÈyG“s5Š,‹ÜHô¹›dÖÿš"'9›iÚ„ Iቚ„4zSŒl&ìæÚBëõX*XGjþÄVq'âÈ—–êÃ;c*h|ù;"LWDVå52,E혯S¦ðï^|øzR²61¤G—÷ߪw·Ü«Ó k/ºÂ ó2Ï,ã}_°¦Ï mª¶ h+ožõ‰­DMtÑd$ÚÐëÆ$qO%Ñ?E‚bH}Œ!‰a£-˜¿ÍôR ìE¸Z·8ì¼äm7&úõT ‰Œ…äÄê¹Kˆ3}…£çD¦ÛAtqôÚðïô½¦èv’¼ìøµÑÕdt}UÞ"l=mXÏ•ÎTó›©Cm(˜Dî"žâ—õƯïñP~F³Sb•ó¹¬~¼ï{yC[¼§îž°-ó÷óHH‚&ÉÔâ7º»s.ÉdM{ˆTC„Ý—dlQ—€&±O$^ÈÓÐN¨Ñ°›}æÓ4›•ê´e˜Q/CRúAQäÓÄz*ýæ{Y|bÜÞÓ#—e¬íqôËð„4ö-ÈN¬œk¾Â³5¬ð쬄œx"Õ¶|‚/Tkê«×’ îÛ]Þ-#y ¨ªô[Tuwêw2ÙüD’šˆÈ¹HäÙºüº,÷Ãè$ÞÉÔ'‘™ÉD yÉ,d’a­—Bëýäq~–Ìpù[{}d Ùû,IÝGtO]ÉûÉÓ‘òçHS& Ê‚¼(›<"öœØöÝΓ,¸›M‚ö“¼ý¤c?™™Cârȉ2’CüþBrþBô!- ƒHt€4ÿ͵¸øËŸâþJTGèNÿñ™z²\S_ õ/éAz$Wç\²7—Ôä’»¹$èE"L_—°F•ž·G½“}Δ÷"»\ô߇×%¬U=£ÙÍÆÈ ÆK"Ã5jb©n¿È.*KSžahlÖÆ'¤â­OC^Q„É/!jêö$à >-݃lMæy+Áë%rô%žžðÝ~‰ìÍ'¤&Ÿpo>‰;$FÖº/2èer÷e12´à¾È™…D_(FÎ<|_dßaRT$FöÝyâo$¨˜vܺb2ížÃ1úR½ÔÇVý·ôU’󪣕D}åGHtɘ¯„¤þ]ôÕÿä}GIý?DŸ´”䔎åYJtÿä>LÜMÿ$#ðùý‹0oê¿Hß¿ˆet)*#ú2v¼Ú1¢;Fd5Ljþõ÷#§¥2™î5RôN¼FN;ÛÈdÒr2³~¯ròkäJ ™®œQG994Y&‹{è^‡¿èur:U.“9W¿ 5–#é;Eòjh:RSC¢ßc®œ÷ÈmîšVKòjyl-™ü>u=L›À?®]¦JUÓÆ°·a¯÷‰¬¢1¦)yThzŸÌ?M“ÃwšÜen‡G¯3ä胻ã I­3 âu¬Nw©Ÿ&û€}`H¦7qGÖ¹GLܺÇÜóÏŽ¹ËMÜ¡™äùÑX5¼>6º­„¢ ½FïÇ×@,¯6i$´‘ßx9ô”ƒ>á!ºOøsä¢kÂi¢{·‰„~ʺnï§ä6s Óš –ÞÑÍÔ“ÚLn7㢜£¿s$ç!õÌÓqŽD~FHÞgÔSþéƒgÚyꙞyŠ˜ç÷Ìo¡ž æ9Á&Ý>–[Ü%bÍï^"^bž{; yò¼–kXFª” ž×ý×u,»š2I »LŽ^¦Ï:àéšQê—‰ut')ê$úN"ý’–êl2²M†¯³Éðu6¾Î&Ã×Ùdø:› _g“áël2|Mîg“¡ìl2”¥÷ßQR“‘-hdKÇléØÈ–Þ7²ñ.é÷%9A›Mbôðõ}I"¿b î?ú™EŒ»BF®˜Äúu}—+ýš}-Æáêׯ©EWù•Ô_%ÆÙœ¿ú1§_·è¼:æŒü–fÒñ-ÏdòwÔ—ó÷Õ3ßüî‹ë!ú"C'9÷Ò:E÷’½ì¦íè%ô³9̼fpÔÓú Žr£còuã½^#ºœû®¼~2ÒÏ\3¿'ºï™«®¼Hù rô¦ÁÝt“Ôp·qRè0™î˜ÌÓn™Î·Læ [&ó@ô-“y õ–É<Ç<ÿÿÿŒ= `“ÕÕ÷‘„$¶¡)¥/khj­("RÄ–P –—Pì\ i 1iR’d˜±Í¿èÄnsµsC¢"TŒÚ¹ú*¾êì모Z±>€Ì1­ºÿ>¾ûåÑà¦%ß9÷œ{î¹÷ž{﹯ïkî/þH’Ôû°¤‹!Ñz €^†Ø^gÈCƇC• ÛEÝC ŸàÆOåSŠ[?‘OúÀÏ®Ÿï þ µ¾ò> ÿ @Ï[€„ ¿ÜŸ ú ˜?§Ü¶8îCšùd|ÇÛ@Çߥ½$NæMöÏ"gÃjoµtº±æs ªyt†©È¶Ë¤Ò/€¥“êFàæ/@ûŸ(LÄu6ùí çj9£À³`Ô:!t´þYÖS‚{ÎÅ‚[£J0ü§.”šÿ zÿI`pKÁt_Ç—L9Gñ6µÔñ¥R”9ú%¨þ—ÂúèÿW³ù||iHÀ}T¿H“íçÁ¢?S@÷oè@ÞW‚É­@C_ޝy2 ÷kÐ:Ìa©k,:(* ³Ú6C‘²oÒÊŽo˜¹¿¥ˆå[†ô2dˆ#Öï(òð`Íwµ~:zËÇÝþÿCPÒ”ç-$I·ð§$©ò$¸©ËÙ•ëðy׸Ö6ùíA—Ï+ †J=ª¤öQð¹ÛHuºñå· žQ ú]«›‚ÎŒ ß^çl°ûÝU^b<¾F›Ó¿Þåpr×øí Î >¿{1yN8ƒµóDÈ\W ÑcßH (IåRôÐØý°$-tGgéÛpÊ#F©‚Û!Ô!B0Š Ã~ m*Ø­‚ejØ¡†f lÕ@Ýhè ûGÃ2-ìÐB6ë`TÝzØ­‡’Êvì½ÚÒ ¾7 ÚÒao:´`¯ÚÆ@ †Í*I³©koW) ª@÷GT ¦Àϰ7Va—–fÂp&Ì [ÆB˜Yp( ÖŒƒ½ã 5veCK ç@c.lɅù0ò`M>ìɇ֋aäbh)€íÐx ]‡/n0AÛxØ3–ÂH!4›a»êŠ`¨F‹ ûRØ)´ÃîbXvì¸ šK`k Ô]›/‡ÑË¡c쟫-°ÛK'ÂŽ‰0ï Øz„“`ó$84 :®„ÝW¼ɴ¬;&CËØ1Z¦ÂŽ©ÐR ;J¡eì˜-WÁŽ« e:ì˜-WÃŽ«¡eì˜-×ÀŽk e&ì˜ -e°£ ZfÁŽYÐr-ì¸ZfÃŽÙÐrì¸ZÊiih1nâ9½”d ÿi(­‚¶B/­A§2t0½•Ð#œªçÈ|*Ê20*ys9GxnÇü4ߣ¢ùá+<¶ úîeÔM1}uU\›ž*¡ãzÖÈ íÕI6qÓÓÈ%b]˜@§¥ÅãƒÁÐ>4åó"N×t/‚e‹ad14/á!È)kÂøÚåPe) /…y7À–d>!i —qKmBã›Ð˜kW$ÕÕ)NBÞJ.Áx£àìê¸nŠI¸)µ Ý«¸„æ›cu¨«qiX kyK,ðÕ ç–˜!ÀZέ,-·BÆ¥”I·].êÕ°{uB©Éš–94åzÆÕo4®‚ÎÄ¢*[3¢ò*¥y-§«ÖšzØSŸP#Â\,ÔÐì‚C.è¸ öÞ«Ý°Ë K=0ì!DM^lo€y^Øî¥Xé?TÜ7Å·/²Æø–ÑÛ+2Ÿsôúã9ÜG8È9Z›b復˲>ÆáØÀ9B·Çdäm$þðï€íGpZèG°½üo‚ð§0o34oew@kÖü :¶ÂæŸÃÐ/`û/aøNØõ°»öoƒwÁá»!¼æm‡æ{ai+´þÚ~CÒ rÍ«é êø-¤HÍ}0-rìÿ„÷C];4ÿ–=m eõùr鿱ËýG¹I˜wÀöÐø lyp¤¼± îŒë:­ÓÍSÏNjd$}c—1œ`]áx#xcWô¡Ñ©x"@ai}ø¿²¸ÁÂTØÔU4Ï×ä­cƒµÕç%C±#¸Ö¸D ¬» ´6°D-I%+¼®õNÀî©ht%ð žâåu•ëÞà»·Îãôß:•»–G!Zw°åQžr7}’r†»ÉsÃ7©³¬»aËnØ»æíŽ=°cŒîe0Ô{: ñ1(m8·J"Á€H†EΠèi§y¸¼ºÛ¶—IVåíƒá}Ðò¸°˜öÇáðã°f?T8[ös«:}/gA¥(KŒ~àûéÖ'.@g¦ðêîá'dSDàPÖ< {Ÿä! ^LõS\ÌÀS0µÛÓ²œž§¡µF:¡åO)ä„ÿ$7’g’åÈ QNŽ?_€¡»‹3Øž½€&]2•=#ÏÅëÀšö˜Ÿ—ûžîçaÙ 0ò‚Ì£tO„å Ì>‡`Ë¡‘,Q9L: ‡Ã@7è†5/Ždì•ÃTíG ñ%Øò’ÂRlk 4:½u.ïZf„þµ’ªkÍZœ5ü´¾ [^†½/üW ãØñ Œ¾Ë^…y!zJÓ:íëI¼9v‡{­Ÿ¶˜„ÀûY|Ìì§W}M©çUh| Ö¼ïÁ¡×`ilî¡FÝõèîº×IJ•ÔÛuÖ¥0½êÂ4&Ýö:l¼‰Ê–7 #oÀžŸ®7 ‘ß„Rf…#èZÏÚ5ígÞÙ[ýf|?C‰¡7EÿðÎ^óÑ1m°÷AuçÀOÑ m§z°w§º³{ Ãäù#D#îí⇎Åw*2±÷mÈ©º¿¤ öÿ%–¦ñ¯ñi¶îUwšûÔ4}–¶’&5y.Žªû[Ì;xg¯µ/Æ ï´nCLšñÉ$‰±\Tÿ}„ļþx‰¶wc ªÎð=)ËÂúÞ)eÿˆ—x?Æ é´þ F¦Ìa׉åÖs2Vnp ^Ò@)³WRç샒j>ŒIjý(>gÖ?¤ÌYd0U=~WŸÄ3”î$ ÄMч?I0;Bݱ»Ïä út>‡¶/`ÏJëÎX^ïwÚi;–£œÜg=#w=g õ,ì:;Ò_=¹¯ôœ9-QÑEÙК?ÏåqV8Èü2°ÈWçŒÔ…ÃQ¨®ø˜Œvµü“¡tÖ*©a>hþ'”ÒlA¢HC@NáÐãáÊR˾„‘/¡å_r‚,‘Œ•vO““ŽŒÙUó|þ{0h_íqjVú\u»¬Ÿ‘dؼxÝçêì¹òL™2çdWŸ‡-ç©$¢FÇy8|>¦•ùß eZÔÍÐPù’”=ßïª#ƒtÊ ÿ {ÿ‹8ÌQÑòEYDRº.‡ÛK ¢d¡}£¯)hÝèð8+ow8©Jl5†.°|E4bHËWl€Ð’ÉûJÚEÎ:—ýüÎ^àþ‚."é‘É|S£NÚˆ.~ª˜Ô§ï5ׂ–ZyÅ€tQMA_ï¢8ë O¾IɪÐk¾MI7 :¾QK`¸ypà5è [ÁÄùN¯“®³ÎJªYYn`Ù½E¾ƒCßQ_du7æÿ@tè̺s¦J¯ÃGÍm©ß·ÞUçôÓ\Ù>Ñæô»ìט²JIGfhˆ†ý×ÕþtÌ;ڙ뮔èÄ}ǾS³ç”Qš^'°?}úÞñ¿’Í3A¶Ãçõ:4nU>h÷¯u¥N£ Ð ù5xç³Cbíºlê…̵€tµ@â´ †5šAóhÍ`x´FÒÊë ˆ4È‚%ÞÅdÛËy Є&4dB5ãQïxd-D]…ÈbFT{32¡p2_JMÏZ­ì&ÿ¬ 5t2Jõæi†? i:ŠyÆúåç`ïbb¡$A?¨`œç ÏŽË€%”Ô”ei0´LÓ9Ê&S(à°i6œÀHËeÀ “&  \K—kˆóžd—wÊó¥¸^§ïÔTÄé¥Å[MxJžj '  Ê›(ÔC¶ÿ›d-ǯ’VÉémÀ=Ø!€ý8(€£8!€3ètü§Ô §©õâ»:ýRgéc¤­Ùn†¬!j‚¤{7¬iòxØ¢çyx³&Û^WW»Ìhj ͹\²\€í $ø ¾õÎ8 iäÍ„ 0ýͧÑb.2ØK(…rÄm"e]á­gS»:¥çãQ†I”©r”‘<hÔ~,iž„¤ÉrÌ,,â÷šG¬Ž‹8‚¥ˆt§$ÓëšœÚÛÕ;œåR€ÄŠ›FRH¡´bÖ¯ì+;ëX`7 $ý)wâ C„P¸ÄksÚýŽúD’ñJ$M$¤z»ß¹œõë‰ô2BŸÊe.!E»”Œ»N"‹#Æb³¯w¦bi!,3–x­vG=éæ ãŠF2%NæŠ\Éô¿Ñå­óm°·Eú aÂo¬ÀãÁÉHÒÚȨ”(j™L ÆFsúìdmhôyÉ,„RU-.Ð>u1žúKæSèC¡üäÂî §“Nö½ÁEGŠFÚŠaK·é™î̘Ý.ê6Uv„Ú›Ž>©pDµ\/š|±p„¾ì6]ñbVm-Ñ|ÉêÛȸ«nuj.¿·æ“NÑ笑ûш°CûpPGpBg ­Õè'9AŸŸ–ñÏf-²»¼³W×ÖN®Ìë±”[„þªf;¦3ì’Œ£³ÛˆˆBk5§£NI¤¡×k4Ĥ…óAºWFݼ9éobß ±L#R‹ o©ÍÙ¸Ðç]KI¤)LCàì/é¦åɯ’]ÆåÔ7 LXèó§T‘Þçv.¯\2^Å"$S“-@*¶D²@³»Ô¾–PK¯¢ qŠ–KÕ-äü‚èQCɦ™«\R{»ÌÊ +rÞæÖH#(<&”lFáñç£$$¿–ö—4*Ë-E)“…qÜêÑH´2ÐÙ_Rý%T=(“M»yr[.uÓ0Ö­¬ =²g#1<ž‡…{Ž=àTòEŠž†QÏ´âE6:ýÁ¬è‰¿­ÔŠtW“€,Ê[¨ðûí„§”™xÕçñpG±XxA "ÊËLs]ŒÃîß8p¿»òv’v€òyèÒÐ,—pt™3Øägó [°iu\èsØYÁ²,U aúU;Y1›DWEì^‡ÓPQG<ÊEöÆr¹?à´Ï¸šõíì¡êb9eÌ+´U°R̈‘ó—5y©92'Ûé—M‚:¨dÞņ¨Ò«dÛ üµb¤{~µM µCGKÉ jÙßÊO®=ƶ¿eˆUgÑ¡· FÔ]gøB9ûVšRSÑÓÄO¿åhv|=¡»Ž$Uz޲Äj }v´pD%¡‹eÆê-:fJ®"´éXV\ ¡½Ç ¢‚Ð{ÇÒäúA½Q%ÚXÎ8)t(aË™-g š(<彇f ÒkPó5¨û¤›‰l3QûL$‰º˜I{(]«H+}諼ë}nç†së5¤:ç8׺¼<¤\ê)C(Z†x}²8•Þ:A¥5—7‹Š³ÒßÂEMž Ëaç:=N:/a4Ü „w$ “ÆúL*‚qjSÌάU˜5ͳx³³t{f±ltÓæ)©Œ×2É5ô‘K \6lj˜aH $‰÷ðBÞ°E}ƒ&†@ù # TáÙrœ˜0’u¶ˆ“D qÜ„F&›Á$ä={+˜I¨²–00ÉœrY 0§b†%šS> SÌ©€¢²9ñtJ¯£-’Ë–ÅCÛih.Q”÷`£vµÜÈX3Ôº¸úEHR>‹ Õ œ¨x6 QÔ.©uŒ,£ÜZW\N²kݱ d×6(zηnÖHYµN¡tàü ˜6K©ôÙîÚZêHÐY²Ëé©ë4û4kVB%àAÊ“P©#xš€„.ïG«úÑýè™~tº弋ªÞEßE»ßE}ï¢Ñï¡kÞ#½MõýŒy?7ɸXJè†÷Áïç&ÙOàÕ÷Á¨¼åÑŠ`ÍF×`o0*¥Á¨Xƒ¡ý+Òñ^›¹j¾´ÄÀ<bÜ¿³Œrj/ŒC¡p3 É8mX±€IŒa®“ú{N¯c£¨ÆÉòÐ6‚+›²½n‰×³1_Œeͤ(?©D\’ ÚNq!Ô'‚³úfì6™èPØm1öÊn“Ùµµ^n\qúO ØH%³˜Ñr2¤ 6˜Pd“fqÅFXGë MšeKM „€¤aÚîÓÖã÷ÎŒ•8ªø$;¾€ÑŸ”¤*GôÕ'™±‚C+†ˆÑÐtÑÖ!ðEh:èšOÁºO³ªâŽfK>ÄÄF”H®@Ö9¨eêØ{ç ê_‚<+2ß VD·9è Z‡I £Ê[ïô»Äl£t.b´òÔ®§Åœ[áñø6°á¡‘XŒrÔ§ÒCfÞ`ŽZÊ«DíÛØ±*ººÔQ‰Üw¹ 0Ô6uÌC‚8ØZ¢Õ Ѻ01æBÔ¼H^ ‹,BÅ c×bä^"SÂKm©Bi_Š¢KeŠåºSƨ¥žPõ2Ú—!†ö.c¨ÍÆÑbGب[Ž"ËÒ­@ÖT’yl^Pdêexû%¸e%ª^Iq÷J^‰PÿJ4Ìð¼Qg÷'ÙnDI@«Œ÷߈†)žwÁ¿„¨ù&Ô~Á»nBçmó1²¬BÕ«î^…·žÔ x32ßLñ›ÑùÞ§4¨ãfÔCpµ­Sc¬ARÆŠªXÅö× Ç”Bˆü ±boAÝ·(Dãˆí?Œ'êj«§6ž8œHl¾5žØ}kÑl7‰{Q·¹WÇ*su±ÔZ qÀ‘@¬©‹'vÔ%Îø4CÎÄ4× þ5 Ѳ61f=ꮕ+ØïBý·ÅbºˆV‚ ÑÖ@´xQÔ³n_b 5¢îÆXšëˆ=ëâ‰!b}â‰CÄú Æ-M‰ µ)žX³>8¼>žØº!1Ÿ·ÇnOLsc‚Ø%Ög3Š4+DøãĘ?FÑÇJhS1² •ý$fC?I,ÛŸ¢®ŸÆìvsbÌÍ(¼E!F·$æó4ŠõV?KÌçV4´5FüybÌŸ£ö_Ä þ Äð/Qõ±~èΑô)5ÿ‡Œ-ˆ÷/Õ-JWcÞ†,w!Ôz깋Ráݨ~©B¥w#ÛÝ$ ã‘»Q/Å£wó®¤ìW¨æWoþÇ{~…†(®»u| QÍ=¨ù‚·ËøÐ=H·à–ílG­Èxÿv4Lñ¼{‰¼¯T¨õ^¹—à½2^ÖŠjZiz­nEy¿&xÙ¯9ù5ê¥xTÆ¿A¡ß<ü9þoQÍoiüßÊñ‹òî£ñïãxÿ}h˜ây¿#ùýN…l¿CßÑ®“àV FÿCym”¿ u†TjÔ܆Ú)ÞEðn­åÝÊî'xÍý$~ºÁvdn'¸µÄ7ª‘ù÷Èú{‚;~Oâ#ôùJ€ÄÏS£ÞP”âÆ?ø&5êþøÁáI×|Ýhäþ#jù#-‚ÊÑȺ9v¼c:?0g4*}Ù¤åý :™;åíDe; ډΛçF¶0 „©¾at¾µj4jyuP=5?ŒÚF’)æmðõ¶‚e¾¦ 3vv¡(—O ÉGÅFhÝÁ»²ƒâ̱°>‚ZA½ ¼]ȱ —"üìØ…¢»PÙ£(ô(MÍo_;RTϣɢ,»Q`7êÚB¯ÒÃEpjݪÉïÔ¿™;”«ˆ¢ZÒÊÝÁGª!òœ@©l]4þ< ãzŒL׿ÇŠ®zœñYdsýÈi­·{צʯeo²’-{Qï^”·9ö¡Ž}(º•=ŽB£žÇ©keܤ’d‰ì¤È\{ÐÎf‹·“iÅ |~×Íî©ð¸ÖzéX¨f? íGÊöpGÅöðCÙö0ÁŒñ–Ìù W÷‘L܄٠ÀaÂÚ5N%2a×tL/XX½’ «ô Ôüê~é"È¡UHЬ=‚"wR©$Íý~ù´xf\†NQQOލ¨'Qï“(ï)äx u<…¢ØËžF¡§QÏÓÈØ‰¤éL˜t2)!±÷&¥UÓ‰Âh¨“:Õ­¿¥¢k4ÍBÝBºghºÒÌÔÇÄÚžIÎCä4ü ²þ™šVi$ª“æy´ü‘4zÿŒ¤™© %ÈÍëJ–ÛÜ…º»îY*×ö,rß ÚŸ¥mŒÌÀ³¬^LbDìÈÝ 'žC5ÏÅl(ÄQfC61”Rœ'EY6ägíÏjª3U§ã9}•=BÏ£žç‘ñªEÍ ´5Ã/ iñ´ÔN¦!ìå¤}Ýè·76’²&:½€Ìcv^}¢ÂÎ ãv®çh#ÃíATsˆ¯ôöÈ¡ØøJÐÒèå°Bì?œ@$s—ên…?w!h{7ª~Q!¶¿#JŽ#d´( žÿÔ{„öÖ›ˆçÿª~I8ò…¹_B-ïx‰Œ¦E•½Œj^æÝ_3yNçYPöŠFB÷ËɆ`~¹_A‘WhG=ü ²¾J;í–WQï«(ï5äx I“åšõø©Dv¼–,R׃l=¨½õÿÃ*û =%éËI}ô ‚J3¹Èïïx_Ñž_G½¯£¼7¨Ç¨ã :%ÚFß –[ö&’æq¹+]×j—Çõ==UèÍdéo"Ë[(ðêz ‘ÖEzsxUE­GQÿQd>Fæ¾UÞÆ¦ |@ dîc|£zè˜8!@w Co‹„¥«7¶¿ÃŽ4ïÍ÷äãΗÆÞ¿pYwb§ tE›ºb²¬ÇŽð½zgœŒaÎZÿ&dXúPÜ%Ââø{‚ ~r|oœ c¿|8 ?>OwãòÔñÞ‘Ñò.Ãò~,/Ýï'äeøDR^ä3 ŠŒÈI¹ä2ÌÅòÒõŠ/èà)Sãi.ÃöILM]CŸÄ•‡ùÓI·Cóg\Fóç1=Œ_ ùÞ“Q}æ¿Èpœ•õ8“1t.!/æ&ÕKE’Y¾ä2ÊþËKï¿ÊT÷ï±{.‚¡ô«¤ _ äÛ4ôŒ!á€_'pÈôŠ8޼á•Ï!*¥ß$©!óœ¿c"‚ߢ´ÒoQÙwȰQ­{µX’2ʼn)ŸW–Õ‘lz¬Hƒ9ÚxsL¥Å3Á|-/¿D&kŽ×ŠgK)`Â`Lg  ;]fTÕ°3¥u giÎ,ìT‘ÇXª39KéXÁÒ=ÇÝÝ!,ÃYÉ 9»†Æ¦‰ôâ½pg:êþ¥~q,ƒé¡Ä\ƒÕ'§jÙÈ×ÌÑ)¥ sT ìýeƒæäTƒ ;Þl;ÌÅRIM•³%~®ÎÅ­¹¸?›ópé5(/ˆæÎ䯎äáá<,§ˆ:-õèÀH©»vJâg¶ÌÐZ§'ßNßk¾T> w`†–íuòq{>)0ÔÜAÝ´å6vv'm9=ºà Êñ¯Ñò#Ç0j¦V>Ä$€É(ÀRÜ*€FlÀ=3µƒÆkµ§_ÞI‚ؾæ`ëµÚÁÞÙ4,c¶Vš,®l÷î±$„ì¸#hÂì$$ Ð%”™0™pì $è2av~È„éÍŒµŽ9$å’¸Ë!JJ”uç&&,î*ot®^à ¸êˆ³®ç+ç~é]µŒÇÍã±âq…*<®nNdõÎ8Êì®H‘”˜ñ@!]…$[ù‹ìþ@=ñûøÞ0‰-ÄyæXRV3EçŽaJR]eIMIšã¬'†ÏŸ˜fu­E$ÍÜùM®ºDšíbœÕS„᥸ìR¸w\Š.ÅyÅØVŒ[Šqw1.&1 —¬wú=>{RìM~Pz!°C:—8QL _†».‹åf€¡,7ÅV{SÀN­~9©5çBçzVÒy%ØQkµ-ñûÊâ(ñm¾&¿ƒ&æ:œwy,ªõrLQõòÛ Æb*1—9=¬g¥~ÿåx8.²yC›¡¢Bœ¨ÔG­ †89¦ÕF¯£Þïóúš7úünZ³p‡%–D¯…¡"‰æ‰eIðf»}•ÜZO `ÔÍ¢µ  \Kp«°Y÷P€J~ðfYàYŒ¯rp«°Y÷`G,ð@Ðð20åBްY÷ü@[\•¢fWL&áöIx`&“7Ë•´¯%“m“9vàJÜu%–&/´7yãÂÅ–FJWkád,ѧm ©üÙOž3ª–úI½§ŽÌï*Åp®ž†{_†d¾Kæ¨Äå'žsÇ.ºˆÓ: KS«.”:Ð? ›¯Âî«0‰DdD®Âdº>|¶NÇ-Óqït,ML8ß— {ÞÕ\׿XšT•šŸ§˜‰kfâðL<4—–áæ2Ü]†I*ºY8o$ÓirÒaÁ„tl³h>.ÄÁ¯]‹³qÍlžCph6.½ƒ$Áæë0™£H3S9Œ-rT}/™¥Ñ}Ö•c[9n/ÇåØR¸«“Bƒs°#¤ò œ[LÈMõ,Í©úoœ,E‡wXqÔJ³D¦äæGÁÐ Tö4$Ò9×\ÌSLq 2!ÅÐ\‘â÷q²Û+ñ@%¶Ì£öÝGó0™u‘ù˜˜˜4ï§*Q ªþ&î£ÌÇ­óqÿ|Ü2‡NR‰›b^€Ý ¨•Dàᤤ8†©È˜Yõ}T¾@Z…[ªpoŽîy×cÇõ¸ãzj~ÆNDB¢×ÓŠ[ܨ¼Ýéh¢€-HD‘ή¬·VǺÂ.ŽŠ®p˜¡¢·­^Ȉ¢· s”õ¶&Ú³Òã#qN!ñu‹¼@ŠÆye)ˆØKØ*Y€¾¥‹ðñ÷óÈŽE ]×NÐ wÐ_ ݺî„Ïg+ƒ‚7Ò-e =RÄfÒü¢N‚@ãâ˜À²Å e©®ÝþA%ûÍ$ƒ ×9À4¶5][u’2Ä…Ggw Ë] N[£Ý›6¸›d\ÖPzgô i—ºnwz4tÑX½‹±$Ž—PW±Œþf(«ƒ^¿ƒ½]ºD–p\Óq3¨^Š»—â²d¯¼ˆnÊ/%®Fì¦Î y'u±/X±ÞîòÐý…8-F¬ôRJlY’í͹ô9@åƒêe4`vIÉþ°öúE.‡ßð­ NâNt`ÒŠªITIsš\žºIËíw Ã4eÚ¤Éôÿ ºZ6Çãs¸ÛÔRÏ2²a±@ÕkÃñ«WáåXly¬zEŒ(éVb¶7ÔºGVmz =‡ûFÜr#–ït܈աýÚ@ŸÍ7ñg?yJŽ+ÕRgdŠZE³7< X6ËVa Jš¬¸SšåR€JYôŒ¸D‡“Ð4%€¾LDB]ä­ kt¯Â§ïµniqw_nÆ7ce¿ùf¿ÁÖUƒk~ ;~@tÜ‚‡nQˆe?Äñ»oý?ÄŽZ ú^`XGm}ýƒ?½rÒ9OeR§'ý ãSÁM o`®sËËfuzëVÌ)ú¥·LÿÕ»Ž5êÀBpàší™…ËïûET§#x©^'ÑÃ9 Ò}n§Wê4FR'Ää'pù±ê¿Áè ßÎpˆ²ÜI~Ìw“ŸÐ½ºRÛéÏŠî òN‰Ôçi©&Ã!nŠø65¤Ç6ƒ>ÿT…²,ž@²5Ñë½"¹ËxHé®[ÖG,•°uø¥ýÛgô—Ü4ôächT[ó@ûϱô‹4dìØí|Ï»6îp*±©ÄŒ ¸³pܘ®“Ž÷ÒŸ°A'ýÂ1F'å'gïb·­¼LÆé!ƒK>ï®r;7jèùR­‹úòDŽˆà9ŒÏÐÉ9Ü*€FlÀ=Ø!€ýèÎÐé}l]ßà Öûê­F|d°ÛH8¬™:éý|™©Öó«b†Fùæ*»)F³dÇoù‘RþCOòz`”þ§8O›œ6+î4mF ÞçRèx7Ñäx`,)ÉÄCŽüT!ÿ¡G Ù¿ö—%ÕÈ’sBg “Ã8Ii|>`Ê$%tE€ô™ VÈ®‘±{ÍÜW¯+Çâ3›Ï^甯˜µ¶`)Wp²ðv_.¶pÈùº_‘¸0#fósS¹Çqvnä–=8@Šët#y²„žÊ=Ý6ÒwÅ)J“¨[“bÛ¿\*%ÌÒ$êâ¤$w§†p°{$#öùË¥=ÆNA$q;ª]D*Æå-—zi„LA„®aX°Èi4ùt‹Œ*ÎrÉ|ÕwQç3@ èáp’aÁ ¼ÄK§3—;=¤ÞJæû–ûdÏ–Íiè’/»CC¼lû]t¥ûnæÊ†îfÞwçÀ0qßt£!p†u|GÐ~‚–i¡^èØi> $Ô¡…@§ƒz^ù†‚¦(‚Q‡› šºU0+®Ò n Mýæ&˜–¡B“Á¢‘µi(SCS‡^¬“ÒäÚ3˜5ÐԪǻ tRQUòØiêÖKtï=ab6äk"žür{c£°žá_a©XXO5—±Óó-^v¸™]¿¼K&Á+H #=RàŒ'³€lA¦SG¥¸}Y Á×ÈbÌ÷™O#bœ™óŸ'fÎÕ÷ÄÌY&0¦jçÆ¹¾ ò•Ó@“LÈ™V4fÇ+sy{ ðäLJ;‘¦Rò"»×ÕØä‘ß~$áá µ“„fÈ‘R²MH0×é ÚyänyrŠÈŒevrÄ*/mSvqˆ‹""榑Ä<)Y#ˆL·ciZ !2Ó䔑ÊHìéŠM¸Šiô¥>5+!="]‰9QŽ™Ä01!Rct´N¦&s+”¢¸ò¡” ‰Ìrha<ãí®¸þ>!ÐǶˆP늹XX|¢KÉ‘¨_ËHþÈP[—”s<)ŽõÆz§Ó#wÓÙãIlàYæZ[Œoé-ÛcO‘ dñ|‘í±Œ‡›DN¬èþíq]GµP´ê¤¶ïÅRI\CWÈ3iþjÚ8ù+:É£ÁEËŠG¶Èårä ²¦¤\)–oº …µ$A¿’&Ço&ñ'%Ç—9XµŠ;Á´Ã÷ÆL;‰¡$.R’®=qi%sL\âåç›b¯CYê¤=u”D’¦-ñ’<Éï Yé 4Ù‰q¸ÿr_“£ž;G¬vóZ1²¶bw+^[[Ù€ØMZÖGÂ9ÐdÌ…irWjèʆ&K4ˆ^Ú0”M5ã`†ÒoZr¡i8f(ㆡÚM]F† Ci&4…3¡AòÆBSËX¨¡Öb€Df æ'¶$C¨ˆŒ³E0;¾ÚÍd-‚ImÉྔŒ·—*2ä.ÀÐ3šÊ anB‹7D ¡Él†E#;À šlãù¢* ×@“Û‹S4Jƒ­˜ŒÿÅPÏÛ”Á\Bí˜?èÐÔ›ÓäÌÐ;š¬Ù0+®½Ê.#.Àe°$Ußj0B—ÀéßÓ÷¬CSäbXœ¢k7X  ©½Ž~ 5ùÐÔ“'¤Õ ¼‹„3‡{ÄIyêüû˜Ã=‚%CI¾×4ä‚z.Ûà¸ôÒ·ACµ›Œ`nX8"ª¡ÔCF24Tñ¥Ié•bŸ)1pr¦°Õ2a«˜,€r,À­eJw)• 3ž%*m–ˆ8Ká/ýƒÒ×¶Q× þk¿n@£6_³REÔI!aÊla¥³…u`‡ö à Ž àÄl]éˆS¬VÿqÄjõ±yvïÀ­»@dÞC?£/å ¿@wO­·—HJºS¢vÌÕµ}ºwGVŒý#4†“ „qùÛM/]´ÏDð!ºÍ^ýÝCm}w=¤‰ÂñHºÈ‡ð‘iô?”œFÙÃ8ô0îyWÿ ͦ{÷ÆG覺u®y[®$†Í…%%19ÞŸ™Pø‘ä„à.\½‹Ýâv/ ¿ý»°îd~»Å‘GÉd.^dRrŲ§22¥áG“SªÙûñÐn ߤ§3:^$™Ò=¸yެÇÕG‘4Q––”Ȥ8÷'Å1ò=É ™;°»G:ðpÝ£'F@jÆúny »ï¤§µ¥iq“+OåW¥¸]òXrª¥{qó^ܽ—a÷5ˆÔ©.Ý>jÑ7m–楔üâïñÜFjѾ/Y‹á}Øú8nyœjA …X×[ôKÞ.zsA7 KË¿'…$ef§pSÅã#Šb?nÞ»÷ÓÙ¿ tìCÖéXw“Òpˆ»â²C8r[ãðaœ×[»±îEz¿(o“mJP!p„/3¿„Û_ÂÆ—qèe<ü2¼‚‡^ÁŽWqÿ«¸ú5Üý.ëÁ‘ly‡_ÇyoàÖ7°îMz¿‰oá·pÍQÜ{WÃÝÇpÙÛ8ò6¶ôâp/Îû ný Ö½ƒCïàáwpà¯xè¯Øq÷Ƕ¿áž¿ÅmßÅô²öñ`U´»ÿŽþžR{[¿ÌßÅÍïâè»)¹ïñ`­í¸çØú>޼-'R¦–ƒõ5'qïI\} w–PØó>Àűãéqw_Ó5€¥©±—}òYW‡ãÃ8ŸwžÝ剳  ÊkCG6ëGÉ &üú—ÒNʇ›q÷ Ö}Œû÷° »4#¥.‰ÀÇ#:ñðÇØzšŽ4ä e-§±m&½¢ÉÃ=ÏljMÈ@‚ØŠäku½§qÞ'Øñ ¶=‹zïŸà¼×è ÂÒëpôªµ43&Õô%W|\¡!Ü3$Ÿý3~ŠCÏÓÛ€d¨r¼ɸ!iªsgë•HúFµÐ·á;â6ûìÍ2d±W¨²UÜoû›ðÛú„×Õ'Ü54 `³îéÓAÉ¥lé|˜„öéÒ§9ù€îF¸eþÏNfïÆÏ¹ŽzvYæÏp5û \àOR—öëè{ãÕá~]uà“ä@ 0Ë4ú¶}ù  ’ü\>Õ8Î …<âäïî Çå‹Ê¶¼‚¾\VR-Z^¡!“Àzé­ìWr4›‰©þœ‰¡¿šÅ>¯“–¤­XK*Õ@oÝ_W(ÇŒ´fQÃ,jý•€ñ ¶ÌefU){h¬ì¡üÛ9·1,ÍÁ†{d„Ø#³•=²ÂìØ«¯2–ú}A§ƒ4…NÇ-HÒ/%^”Ëa ]¶ËOµHʨjhôù‰šÁ´•Nÿj2½è Û‘”¶ÐµÚo÷o̯\k½â•W½b×ßRo­÷¹N=oÚJ¿ßç×L”b~‹}A×ù_§c ’Jˆ2ké’Y<%C9ß—ŽóU¿=ý´µìÎ2ùÁ){Òïÿô¥kÜs5Øñ£·f§~·ð|Ù!44 |öÚ¡ßɳ˜÷Å,F'ß³˜b#€É'ts«â®t9SÜäP[Oètׂ U”lÛà’ïŽðÝéi<¾;uzŠÉ7º‚õô5Ãò\ü„ÐQ£N ÕNÊóõŠ“bQA‡ÏH§D`ÀäSº•8ùKuYì$mxµÏÏ^Îãq꾇—Þ$%·;ÜöµÎyö—‡¿Ï‹«yJ¨)€CB…3„š0 `ò€î<@L_H4lr‚.Ç\gôò…–Š‘¬ ‘g } ’€é¹p§| ÇÚ"€‚ç„Î@úPÈ€I“?$†ôßN O•UþP¨,€CBÌ!æûŽËbF}$=°å#1‰ÀAýHÎðIÁMALß‘Uð¸M˜Â¸@¾50A IØxYâÛ›D!+Qˆ¤Ÿx‚¾B‘„g,ñ*G HP+ Ê]âMØH%Á]$xâoê pB ô™‰ô[õ„MwÓ·*§Ü„§ï9#äâD²|R€ž¹!ÄI‰Ä¸½VÂ" iK¼l·ŽŸ´9ϲ'oØe.ñŠ­:þ çû0}Ÿµ²[GรôÝМAÙªËä0ݤÓSÐ×ÈÞ7;×¹ÆÞä Ú‚=N’T1=È“Æ_“ç’i¾3(Nå°­Ér©÷<æ/k>OIåIô]ü œ…LÉFnh× “n4ÊŽ·Jƒ[‹LýZt¼õŒN*H²pCh42EG£ã޳:â‰É9§*P©XÞÌûJÙ)8'v 0**F%” `iTGÛ~ä+¬¯¢ga§²ne‚$·§¯b+Ü )?ŽQéÆò¾ÆÊ«Öc´’xV1›á¬_Ǻ„dŽ ‰Ñä銼èþulY|K†¢$ý€)`B™1e C$¨f<2%'gè%ÖBT8Bž¡«™,f”Q%§ñ#LSåg)é?ÄQ[^Óhýš9ëCôaªJ<ˆ;uªõBÇEÙUCëplÐqcвA‡uºS¦kæl :³oj`/4ÚÎ êìÖcuäߺð0¿üÐú –¦ÑÔHýÊwC”£„¤Ó4~­CäIÏX2˜ÍqB á K?ð‰i£eî–z¾˜¥«j¤ TKŠ‚Ð·Ž£8Šã(¡’8$<êü1L~´òÑÈVœ÷CÐõ¨·ü/Oý¿€éxç ollþ!¸ñ'ã{1)…ó0}Ö±cªî«)w¾;tmÏ_¿&œíúÐ|ot/ì)[6KI¨QúsïÕ-Ý׃ç??ú›_<=îüõ³-OmTƒ‡æG¤ÎÞÇAúÞ[öî)ý 0¯{ÿ%u¶F@ú¾•»[¿mªÉÚ4:}EþUO›3Uæ«AÈ0¼Ö'Ñ×¼HôÝ.}¡‹Dßâ"u§¢ôÝ3õ;À´7oyàTQk:—ugÿ-¡áI€º$3Púœ—Çÿà©HÏOÀŒuinRô3Qú_o+7„~†mËÀw¼™1]ôŸtô‘§ÿî³üGLpF¤M–TܺaÞ“íG0-ÖeÿK$9FÒËQ`®²v`¿¤­JÄC"â˜Q2P1JæoÀæQJD·q»à?)€Q@æŸ,€r,À­@ÕªˆZd ‡pVð›  •ˆ]JÄ (óoÀ!ÁF’R$ (Æ#9â:<(ø àD,¢N©ã³‚¿ Xa+UØÔ³¯e¢Røk~»Jf; €“‚ß VøC ÿxµH]ÛÕ2ÿAUëóÏO™–Úékt½ì›Òkììzzë[ UÊ ‘‡TUf* —Û9Q\zŒr”]z¤ï¬Â ¥ÓóKò2ªç¨ûDÑ12jT1Á;N1¬†aê-q¢Õ)º]F{ejG«Õ =)£Í½!£ŽŽ2pT§áqe´š£SÆpôÈv–wë‰5fh…Q¦rô€ŒpÔžÉÑ2-CÇŒåh+GÉh”£S²dÉ:†ž•ÑGãèG·gsÔ¦gèƒ9íçèø\Žæ]Ä CF›9:&_.å4®³Œ:8zìb‚Nˆ™†ì[ˆW=¤©,é1ƒ°¥3Ty}C…A p¢0‹¡Â l–QÙz9* "o ,„•aŠA¸9* ¢C¦Êå¨0[Cƒà¨0häqe´Œ£Â š9*,¢G¦Ê1$SÂz(& ’Ɉ ÜÑÁQa½‘7–¡Â lâ¨0ˆG…A sT„5‹†Œ¶pTiYLgâTfÔ\Æñ„&Éjpô†+•†Æ2(£]œ:j²Üвz@FÝ>EÎ/GÇLM´¼%~Û„ûJ¯%çÄ,¯,GEQay­ e–'a¡k½“¾œ¿ƒ®?G5'ÁœËP!¡†¡LÂ̘„˜Ó>×åçË´¾rU¹1Iº<†*Ý"CE+âDÑ ¬ù e­`bœ¦vo]ƒÝQ¥5?®ß«¢¨H +Ÿ¢"¦Jòkl€±òV&>ÇJ¿T<¡*Å.s/ˆ€éUÜSÎóÐOåÉŽÍLyÙ"€gŠAF'pf¦2^•)F•ÉoÀ–2ážP€ì.É,˜2K¸hœ%3o<ÇpVð˜®Õ§G ,—µOQYÀÎ//ÿ»zÚµzI=—ü°ÈŒŸñÒ3PÛ®ÕÙjpÈïêaßýÓ6зì°=)ÃjyqØÁjàu` X ê ÜÜéÆqη¬õªîŸ€ûO¢SW÷tŽ›ÿâ{k#ó@0½xö«Ã6-n¾”ö™_–^úÍ„®–{h)67¼œ~ƒ+²ÍúÈ»üº¢×‘þÊW¯nmPÕ¬/t¯Y°òöô;ïð[vƒÖZ°(ýìïm)ÝËòÁ¯ª­é/ð{¢f=8ûÃï¢é'žÏì€eê€ äÏÝxFõ¨$­•L ‚/Õ’A|US¢WÆÙšÛƒ³õ½¯+ÑK¹½©KhÃdZR*g?€èUq ù‰êÉOé6–†åа‹iX!ý9O'2Á ]è '>k€:{?G1dà õÀ¿Dé7Üðë÷vÂÒBðÍúùL“ñ×MŒÓé,iMf&ù ÏRgú¥¤_ÌŠ3³]æ\ë å†Ò êp¹¾ÿU~Â7¸§¨{+ô!“Š~¶%¸4èŸ1×¹ºiíZ§ßt6.¯÷ûšÖÖ+ÓÛ쪊ÀF¯ƒ¿±*—Áâõ—jëýSeÇ[çè¥ôßvü8p¶6‚Û6¼’~ùª˜%ÕU?ÿcúëå}õ¡ÿ Àb nN¿ôhÑ”2,·TÔÿÏô7¶íû4êGÑMàjmúêS/-Û©]~ýÄôÅûÌÿÁ5YàoÿN/t<Ð;ðì/éÿ® ý;«µà£ôž[ÇìéùVY :ÒÓ«¿˜qÒ6œ ^ì |3ZÊPVpµü£Ëèšõ6ã=™üA_0¡Í¯¼½‘” ý’†½1àÔ§èB%e‹—RÑ}ªŒÌ\E,%fiæúîl-ûfTn{Ðð|ýrßÚµgA›5¯ñùL…¹”B+Îîò:ýE+]þ`û¢K¨XK†§–°×#°•² dzO̬ðz}|x˜¤¡kr“3ér?Ì?5‹¿P™½ kZ–­‘nœêÎàô‚8„¦2CÏÖÙ33ͦÎ*ïBbºÐ뫳 4éÊ:W°Üdm Ž;ÖƒÎÑÏi }ÞÎ0ÒJb Nºùj°Ö;î9¾Û V_Ãj44zÆÒÇû=.¯[[Õ`_ë4,$æIÑP@C—ÑÓèÏ">Y©Š=L˜µÌ^çòñ3x…‘P½ÍC· Óll"ƒ^ñk¢ì*Riä“¶Üç£Ìì¹ÜÕ¨Yîw: ô‡Rõ<[ÚùÄØµËë›VèEMG"zâ¿KwjÄéžOõIº_ø&ÝÉïR•nËwIߥ»ö»”_¦;ùmò·éÚ¾Mø:ÝÆoâ¿OWñ$éG|¤îo’j›^¯W¾UwJBÿÝ?$i[ÒççÞ•€>ŒøÝ_”}öRñ*P2—óØ6Xϯبlà´|Œ¤¨Yþ)QPܧ*)ÎQ—¨"ÏÑ%}@9¨2ƒ„¨<8³d. )Ùšu˜l«ÌrБ\†ç•ä Âà9R@elÛj¢ü`ÛDŠ1—¨Ú6/碒úâ6m= +ÎÑ–÷iKÚ2×°ÊH[ÁªRudô©ÊѰš’U™$pÛ¼>}I}I=‘ã©DQ 1tV1ôˆºd.Åë±–àD3¬„:—†QmYsyð¼¶lOå9Åù=G Õ®~+¦YÍAÅ«t%¸˜$ 4Ûôõ„FŸ$ ÃYÁ0€ª¥ÙÖ—›ƒŠæµÝSY}¹[-8pŠ–†^ß—«oƒ¤Ê(¦™Ø—;qîD’ìôåÖŸ¢w8úìËmC”Iµ³/·¸/_O¹¨t}=…P}_îaš‹¼¦jC‡s X¤6Ô—»*¶ÅÀ>¢Ü—{J¥R=º³žáz./.$=ÕæQÞg¢|@µjüáB¦(I˜kOUäjƒŒI³m§^ÏHeÓ¸þ—Ä™TÛvöAË,ÚÌ,`^ÛE}c(y¸^f¤á°`¶íðÎÃ}E4†\L¬9´êRV¬<ÌCÓ˜ çæ …œ”ƒææ\ºšÇàQWëë¨Yjr¾®ïR # ŸRBsŠëLŒA ¸ði‰ãS~¾¹Ž?Í öB‚å¾?Ï"OÙ]<üyî<2rÖXW_[»õó• W²F†˜Z\ÿì³9Œ›O¶HxSPÅó7ŸeÇçaëg†’ÌnüìfÖ&zVµ©ç{µµ}Ÿþœ¥?ã«¢(ó?J9=T’ªòžÊgÂå×Ö>8¤e| e1Âjâl×ÖnúÃVTÕÒÁÕ²IEmÒEŸÚÿYû'÷&ˆù¡˜ÿ]ÖªÓw'ÈJþNÌÿ.é–w¤ÈdŠ Fÿ»Èk¹q)µµ—NàÅ-7Qê§?ÊM°¿W?Êd8‹tà£âmªé£É Ú’¶ãpÏ4‘)óTé®o­x|õáÄ›WæÃ\&_üW=oÛ?œ`8âyǙ¢I¾NMH-ZÁÉ âU'¼‡>àÂy³?ðA£s¬íƒlN£ïv"F÷oB2êü`ÏUb¿%ºWQ7SkmÄÏ#SÓZþnÈ@mÕœ¦5kh­ñTjE#; ª5ÿ›tÎ.‹j;5]ù£ïy®ê|Å©B^tqýõ§~¨„ÑO•ÓB&*²¯\“˜ö|üÃÚl'ËG891VÐñ¢œ'½‰¥ŸJ‚G0ì>QŸ3I“*ï_ M.:Á q½æS¨ñáû =Ýï§ìh¶¿ŸÊˆ]ï_À&¯x?©_Êy?E¥þÇ´ üƒ:(óÚF—´Ïø/«K è—kãšñaâÖ—Vzé Šû‚ôÞ =Qùâ5¯z¼8'«d*=ëDÆ•ª5|=Þåâg—þ¬¢3‚‡K¦Ž^·×·Á«œr¤ÕHföþ?O CÙˆñ{|/ZꩯÉù{ñN¾D_ª…m-̦£“ËÛä¤^ÃÓ$…ù[êbzó÷\«Çi÷65.¶Ó„wÞ¢ZD.†%åóéG„y²ó|þeNbîNU)ýðç>êà–”˜llGC¨ºžÀÛÌ_-*)NÒ æŽ‘€Õwˆ w3 `^Ð,ȸhÀ‚¢Ešà|½DZñzv0€­ªŒJuŒòmO‰ôYO&"³P’±z3’4 2=(Ë–îÜÔ#mž,˜0×þêÒ¯‚”€úÍSGP·¢þ1 $EÔ‹‘,è‰8Òâ'öUÁc4Ò¤DÂ’ªkzE^<éØ ˆúÝ)iöÐ*ÔFiÉ”¯ž§JÐиEσ>”¨/™¼ï~^•ƒWa}r> éÚçUmX¯OY „|è9Uðx¨j#‰‹ž£®5+FL\‹xçY™«¨`ðèæé#n }8ñ´gR†¤U‹WŸÕêõT= —>Þ<›²%¯aÜõ,I‰1j#¸lž•(’­jT=«É!Yjƒ¯Çd&éFeîíÒ0y²úH¿9í~ U*´©¸Û^y˜Ø™l“ŸUjMÆj”{¿VRµ©ûÔÅ9 ‘Ù¸¡;T虪mÎÄ#*-ª$³ÁJµ UjÐdhX\•¤Z¥Z¥)nÓhô±dv$ÉTæ€#Ù[s4 OÃ'²ÔGk6kÀtI¯-^5Zƒ´wÒpF÷Ödì•_gàáâUZ ÒÜoPßh2gEÀ[€0VÂÔĨ3ˆ¤!i5?ÏÑ‘9´¦M§éÓ1õ2¨z[ËŠsôš40o•ž6OÒͩɴY³ <‡Ìùú¨%ú#ˆ.h¶ÒI*È$9t`:oy’É¡ffA|©T.T!l$r.¨r -°ž$Ib‘¸€$ÚwKôH>aÃ,@8—0瀤¢_ B[/hë%„ RbÆýHB«TGnÓ5Ò¸Œ+:âM¬‰WJâQ’/.6‘x«äxeDhQB<œOâUx”t 6gþÒô“·IwI *—!lSʵ¸^{¤0¢i`DZ&}í$P[2vjOÍšù’ŠÖ#)yRÄ„éÓH÷–£Õ°îMðšŒ#k@¥‹!´Di\Piäx›ÀDZ¾p^S¤–N*ªxUºfs‘ÚÒEwÛÔ úÒ‰5åDÈ¢NÒàM”‡ÛU Ç¿|Z¡³éò±§:÷ÇèlzØÄ耮hm.¤Áò¬z G…ï§@‰FIõ–§v>Û|à)ÎSt÷“ŸÏ÷{^î¹÷ž{î¹|ÉJ]1ŽÉ*;—é ²Î«ÔLUú4£c+Á9ç-|γ…N¢;€æúa‡3y/éïâ…êÝM¾!T_Æ„¹T˜Nª/öb½÷ € WýBKv&@'Tªl\÷‹¼Yw9HÅ/kÑÜ NTp\„»ºàlð.=¬êäÕ@>$^–§ï$ÕÍé žñÒýéø3/ý™³kü/vv±›ß¢#x7»áU)=ß÷í¿‹­K–ú&bçVÎÙn°õ.äÈÁ_YW㲯⠷v¡i¹·üb—xöN”È™ûžË9ËG黓Vå:riøâa7 9Ÿ:È w¢@.nu §¿š›º#ÕÖÄÅ¿k¹©3ÕåfÜã‚î¦>œUjödð^Î9öN¿Ÿv¨jöëoÓrXÚëG5J-9ó-iƒ%H‰“œÈ×êO~^θo;éûë3«»;R[IÇ_—sÝØžê¶tûµÛØšÛRýÏ{Xæ,í‘SH•ËjpÙÂö¶ûãÈOÈÙ žèÑùýÌ͔ٓ(@úÁ.NÎ<û»ç$J‚­¸`!Ñr’Q$¤Ž†xäå«Ê¹ =ÞãCÜáFÒáV¹Õ‡T4×Ñ,Ê‘[é@~@ÃNYö„βÑ[oM{ÞÔeW3”î«È%[Ð^º©–|v9>³k›ZRÝV}v½ÏµÝy· iŒ×àœs-øž6:°$±„}ç Ž]í]m°x6ìyö¼º ©–ÎÙi9gYõC­È1¯h¾mï›+²¹ù5yøv¸ ¤WüæÈYÀ¶ÐªBG6{’7àJÿ‰¿¯º9 ñavà>Ïëï¾  ?½äçûþu7!•—_‹Ë¿êßÚ½©Öd‰½Àß´s•ÄäJÿ§0œHuÃ#ÝüG)x9™Lu²é³‡öì?d|Zž€ö¬wßñÙ$rä¦&y@»à+åÞO’°×¡ÉæêKìŒÊ&’ìå hZBN}œ$û!&µéûâ Ô_Æ“xïHz¼À QöQ½’z»´ç¼ñÿgªp#.\`ÿåp²S'øE{´§Ç’ì, h?çO"§ÿ>¯Ë/»›[ù^ò|ÃyŸÅréKÉÏ<ûòóŠ÷'?5íÏo|Í/’s®ýªs]û÷^H²¶ýyþû‹/$ç|ûÒsËÞ‹%?5îÏ+ü×óÉ 9÷KŸO.`Ý¿ølr!ïþšç’)ó¾ü¼²;žMÎsï(ïK~jߟ_zú™ä§þ}ùùöüãO'çøç׸÷çÉùþ9v;±æo~:yž…_µ…O=\ÐÃß´€‡ÍÏ“ç›øçŒ÷ZÖ›<ËÅ'>Χ®ç†_&ϵ=ß{)9ç8²–Éã/%Ï6>—ΫAL½7ú“g[ŸÛû?«A>.ÓÙ¬ùY8Ïü<þ‹äÙîçÓ/&çÛŸ0õ>mÁ~Õnx1y®º=–\ÐÍ!˰@·<Ÿ<Ï=ýÜgµÏîç­ç’ç¹ žKâÜU¹¦/I\CsÅBžÔ³ŸU'—¨ì¶:oëBÕO=“œó*çNréÏSýï^°ÿ§qƒ¢³ÌМ^¶EÿÁ…Z\ÓûY‹¹“ºã)ö6™ëY6å‡ny*5Öo/ÔÑÊÿN~yGôh4‰-Ñ%ŸY¢Çœ<ÇÝ|!OtÃO“_Â-œgTfü$ùU\QêÇÉϱ*·ÃØÏöEy‹¾‚Ÿîš%Ï6F—§¡Ö…9wáÃzòó÷F$yakô½Ç“¬7ºTÑ¿{£Ú/iX>þxrž9º‹!._Ê=þpòsÝÑ·ÁS抯`nx4y!´`νæ‘乞eÙ#ɳÒ÷NÎ9FsiÆÃç5;Þ<Û$=Jø…v¶KúÞCçµ½÷tòlŸ4ý¡y‡dSלNžë”¾õ`òS;•]Àn~09ß+­ùsóp(ùUÌÒ•$Ùï½ vøÖýÉ”]ÚŸÌ Þ¢ ÷ötp®· ù¥GïK~‘azǽsëÔܹøž$k^š+6ò~Ò•\hnýëÎ$kå}zGÒÿ+™ò/m ®_w|vhv’<Ý•:4ïЇ~˿ࡗú?;4;o¿#uèÖc 5¸÷ûÉÔ7×<ïô8ió™yZz®yúbgò<÷ôpgòlû4ƒ$Ȳ<—:îOžg ¾uû\µOS[nO¦,ÔÖü…Oÿ=ß‚§¿Ò‡O¿hž‰ú“ödÊE5—.xþ·$¿¼úô©ä—öQ_<™ü²Fêã¾ä—vROÝšüŠVê–[p òÏc,°n¿|"¹€™ZuA3õæÙú­é ³ˆõTñö{¾›Jµ$/h§Ÿk§fÜœ<ÏO-=ßO͹)™2T¿õU Õ›á YGõÀ—sTÂ)²–jùy–ê{ÍÉyžjÑYžê–ædÊÅüÎW7Uï½a®qõ׿ªnÿÞ\Ÿ[/h«žò$çùª øª/_Ÿœg¬.ùÌXÝp}2嬮>×Y½÷»É”µêúú­Õ¿\›Ly‰Ž¯Ù[=ú¹žm_«¹J›ë·ñÿ»z±{îóž9{õÅ«“)µöÂþê[G“ç¬ózš›3ÿ{U2å°®:Ça½JXOqÛ…,ÖãõÉs<ÖšÏõX·I¦LÖM2Y¯ùf2å²Ö|ŽËzêÊä<›µô|›õ ×ÜàçMdvQÙŽ‹4ŸÚf ­‡¯H¦œÖUç8­×Íõ¼ùÂVëO.O¦¼Öõ y­é—'ç™­óFȾ;“)·uõ¹nëË—%Svkñyvk”9æ•Í÷[ïøÆ\™õë4\—⋱ôSßy¿êæƒÉy–+—ûŸz®wH¦L×_dºžv$S®ëú…]×tÇœÉxÝb»¾d®ùÖÏñ]O훫%ÿ?¯§÷&ÿïÎë¿ö|¡óºeÏ…×Çm_ì¼¾¼û+9¯‡«>×y}zçWu^s6}¡ózxÇç8¯omÿç5gûWp^ÿbü2Îë5ÿózé:ܵÿûËðûS‡£º‰æ¼Áù˜“íäÚ¸uÜãÜ»¸Or‡¸ÿà.F«ÐÅðìÝŒC¯¢÷M•P{¨«¨;©õ.õOJJií¥£ôoè¿Ò‹x:žƒ÷]ÞC¼çx#<£eLÌ1¦•9ͼÆüòõüüJþþ·ùÇù·óç¿Äÿ+_ (TrÝëòG«kU–z󱵡zËwBËZ"æ'={þÛµïiÿŽ·•ňîä‡×d›ò³[óBU+äöšÆõ5þ­WY/¾qÒüsMi|`ù¼M Úõ›”úÚµÑÊ-þ5ß hol¬õÚÕ>nÙƒö‹N+WGgL¡½oëþm+ç­×oØ0°aShËÅMuÇ Ú ßx ¯*¬*ú½×ò–ø£Z½Æs°Â¾úÒ’MºK[Íëýý»:ÇÖ»jRçþvRûIÝ]!³­iSƒ×ñøè²çó_‹¬Ù1“ûÄè²]åÝÛ¹A{±Ú¸mh{KÏE·øô·õmy&Qû²½ê5‡ö·æåÁUmsûšûòCÆO†µÏ·F*ÿ,ß²kdßaƒéˆKÿ°pß³‰]Ï÷m|Þ~ðÏuêìvËzç¥GÌ®54[aÕÒÇ,<´ùWÑ%#3ÚÉÉÕÿ2ÿÛV$ޝRÄwTU(÷ –ï]rJkŽ4èz£ê÷ µŸ¸V†–ãê*­Ö˜¿³_kíº]µü—³›_Í5¬ú;w™1°Å<²{ÿÌÅᙵOø¬Q÷º÷{  ZJÕonºÔ[áv\P·È oï_õÇ²ç•¹Ï KÞåÚ§¼f¾¦^³ùj㘱ø;ò²ÆÜq×^®|mQûžýbÓµÎÜæõÝ%)Ùþl¢ò_ê¥+;˾´p¶t“!k[ýÖ÷Þƒ}™WïºzÖpMSŽg¶¬Å±çýðŽ ñ6‹ÎdᙊÚl™§Ô«üîÝÐÚŸ&´¯Ì¬ǾòCÏÞEñz»±‚»¬B£ß<“w±MûLÃn¡c­ÊŸ©ÕÙ—æÕh©—D‚ùñA˦]g<…›±d*7/« ckÊݵVùÒÝõ«öw|ËžulpÓ5õ{ ¹7É÷ú#™wÚ OZ¬ êkÇV{ôÕ7¸sþ«kÏ}±Ý¡1Û3ú¼7‚Ê?6ýY¸’ã]ÇhvJ=+òâÅ—Žm>^Ÿ}K£ò§–a×ÅiÆ5’YT›¯Kä¬è«µ(m]Æ6ßÈ­ŒZmÏšV½éÎý]Ü veDk4βšÞÚý£ÚÃâë†VßX—uרö»»š´þTSúœ{eltÕßÂ*zª(7°q…F_êÞjª_ÿ-ßEß“oíåÕ>?³uÈ·dª/Kà+Ý:«Û—ÈÛ?“q“{Ñ@lÅ7Kª\§î¯­tî±ˬ½¶[3þb^ü×®2º»VÒZ(·._ìY±$VY¦_[£¯¾,nên°ýh²jКývtå‡ce³¼ŠÌêÒìhvž)ÿF¥â÷u[? –O TdÇVåD µ¡¥ËÅ5k…»76åš,+wz6ï²í>2n>2¶úZ¹: É‹„7þ¶Ñ<ѳKÄ[¹F¨*’m]fáUíÐoß7%?Þh¾Éf:5¸úûœN»ºÏ¸èYaö‹ÞM4eª6š,Ë®t˜¯hÜãKïèͽ;¶ëÙꙦâ,sÁzŸö£ìXÀxS°æ ]eÏàŽK*†BÅÆ–~ݶA¯=êQ}ËR~̰­3a¾?”®{Y¾úõÞuoάyÏ•ÿ‘¼Dè^–­[åç®x9Qú;ÓÎŽܸ3U)è¾H/_4ºiñji{îfíª½£+O…·vX–?©)z¥±èÍš7¦¶¾Ó°åk–ŽèÖÄ4扥'ë®k`uD»a¨±jLU8í^Ãé®LÓ”e% 4u†Íö|›ºì#é.ÇÊ+M™?I˜ë3ßp,WýÑUþ×é"³XÑ“®_»j¤|]“´&º¥Î‘ù]qÖ)_^Eƒt½oóÆqÓuã‹»â•/*~Ç+ýc·âO±e—9½ÛÅreV¬(·}ʾý¤Dn1ï2›÷ÅWv­®×->ÚX~wpã/Ôy¯)WÿvpiG»ÚZp»«èçæÓ*óÌš¿ÌÊ}Ë×µo«ÓVx—ýÀ }S®ý_g?ºYâJ –Íf]T½qŸu“C.º~¶øæñšÛ,‹ïh?0-ù™cE̹:>Xôî ðÏ}RqŸ&Û°% ¬$¶ø§øªúÕþ‰¨U£L”¯1–­sm®0¥¯ïKß_'îóW½R·ùýxvšØ)X(+5äì[¾×nÚ6;ªÅGüúïFKÒ—¾=ù¾Øø~°âC_©BS°Ø·eÉØ²Zq½Vÿ½Y‡YØ)¾è‡–Ÿú”o;Š? _Íð˜dê×öÅôq‡úõÄÚ·frJm›+»*ì–œý^cÃä¦Ô(åŒëzyâ¾C\µþϵ:]é7[éÇ"¼ß¨ÿ~R3¥×g'¤KõK¯ ¬|À(yS»rÚžñqpù¿\ô¿KŒ%E+ <ë`åQãÊ6É;3ékh¾šw㔸O¨{Ç®ËîaŽ:¥wÉÿ7Wýæô¢lŸ¸(©¿°<žs¸¯ò†î‚Pon8œ6à6üž+}{pM~=µÝÈÛ;¹ì@õV/·âž¡¿4¿nÍ?^ŒÖg¸DªñÅ+Fù«ÂÅî°üXCÞ #­ÜôSƒ™?³n~Æ]öf/wvpgzQaÿª õªš¾ì]6­î‡ƒeIÍñ.¢yõml™sfå›ÑM¿Ëÿ2®[¤äå4eè囌&sÓÊkfíÁªgûË]è´ì…zäF¨5¢ @jBÈ‹Z1ùP;&?êÂ@AL!Ô)Œ"˜¢¨)Ps/º 5÷AÍ1'ú!œˆC81€jÐÄ >ÀÄ”$Ð%Ð(”%Ðà8š„*ÍS¸Jó4$Thpq© Ä£„€'ÅgäJJ¨¦4€ZJGé)~ U t BFf‘ ³¨eƲ@pÒŠõIÈ3v,Ï8@*©:¬•”5嚪‡@K¹q ¥à€/4x¨&@/Õ è£ÚýT`€ †¨nÀ0ŒR=€½T`ŒêŒS€ƒÔô쥸g/5ŃÀOãÀOMB ¦p ¦!Q38Q³„). A˜âÑ5p,!¢”‚^JŽƒ^J AŒRá F©!ˆSÄ)-ƒ”ƒ”ž®@%´ÆSdÄ”+2‘HRMïCfúÝ‘÷ú:/Oö!ÆI²Úh;½M8裵¨¨Ô8r6»T¨p¹yTàÉF€3%Õ¨¦¼€ZªÐG¿z=-“µƒl@~Àº20F¦™˜jæ2@3£´0ð<˜+Shc®‚Yog®t0‹è¶6.ZD7Ã… Ùɸ0Õ3nL L#&Ó„É L@>¦ÈÏt1à bº ‡™sÌó(ÓÃÀqz™*À>&Æ “ýÏÄ1ª©LZj“Â䧘BÔ¦(5Š)Fa¤Æ1y¨IL/LaœEÓ˜xÔ &šÅ¤¤¸| /Åà„˜Â”,˜â”Ó„ c ­Æ”@¾–Nê0žÑcTS%˜´”“2bòS&L!ªS”2cŠQLƒ”Ó,²aâQvL*äÀ‹0¦f'F/å ê1…)7¦^ªSœjÄ4áá7ñá2zù­˜|üv üü.L~ˆâÃëÓMQ˜¿(‡õ™å_ØÃ¿2½|¼Œòûp]QŒÔ•ô㺒8Ôíç@Ý~þ ®+Âuûù \W2‚ëæŠF¡n®h êŽòÇ¡î(êæŠ¦pÝQþ4ÔÍÍðgaä4WÔÁÃh@BL6Z,€Y,(L*ZcÖi It˜rEzA .3 IL˜rEÕ‚4çp'cÁ˜g´ í@AaÀ‰Ûº‰ê IܘrE ‚F\æ!$jT ‘Wpµ à D>’–´CZ⇴¤ §%œÎ»á¼B€ÝaŠÚ訠G€õbŒÓ}˜òb;û1vÄ1Ð&=(ƒH Î´ÑcüqÆUèä$À™)55 ¨¥f}Ô, Ÿâ «`Öñám£Ä€ƒ”ÐC)_PÌ"5 ÒªPIé›õ^ª0@Ô°—2Æ©jÀ 3@ mL «Ð&¤ã´]èÀ‚§_û’Õ D»„õl¡ H4Ù„ H4 á™jö ×£“­g|jªPKù}T Ÿ †¨ `” ƨnÀA* è¡"€/DfQ êT¡>@%lîðRqÀ5¦{©!À8•œ(¡GaG&܃:Ç…÷¡ÎIP݇:¦@Ð4ä h´žm£¹iw¡N^Ú~ABp°ó"C8"8h@‡äÛh%Ä£‚ƒ6ú*í~XHÕÛ;Ó‘§I»žZmÚ·uiú4˜!%˜: ;Œ È„ÉFW§ƒ‡ÚœÏK§% ^ÔÖ´ï@Æ–fÇ iu˜œ8² ]8² ëq$rã@Ô€µ¤k‰'­γ) æpyÓZÓ !Q;¦\‘ŸD’®´ÛaÄ´ZÔLƒª3²£ËŽ0H˜åXP݃Ý›v-´êKƒ×NœŽaêìÇØÇ³Ìö´¡4<Û ‰FÒ`ºñGI ƒ Ÿ?Žƒ\Ñ$£ü)è{:-NÏîœ%ÔÁMÇd@<Â6Z 3KŒ©SޱC‰Ñ€T˜l´H“§µ„;u„:ô„ ¨„°6¤Óaì&LÕ;Ì È‚ÉF[Óméd8vVt:Xî¨cÙ€œ¬°Ñ.,ð!ëYÑéf¹£ejd…ö¤‘Շċ)WÔšîK‡ËÔNHäO¯B¢.¢%Ð’ Ö¹¢Pz7qSgcG£õ`‚›’Þ‡+Ä0uöcìˆc„Û nGú®ÀÔ9‚±c£a²Ñãé°}§'Ó/œJWN§ã4=“ÛjzŠíB®è2@žH(A±](ÕÊ!%R‚©@IÔ $Ñ>Ô¡ÁÚBëíB½¨D“]h™pTÉ.4‹zð|²`² ­˜D6Œ»ÈëÕa² "'»Ì¸Dõ"vÂ/@¢t SØ(òˆ^Ð„É ðb2 Z1™>,Iã¨]«íl@]0²Á«©™‘ SGvÆ„#;‚~#La·¨º ÃÐ "pŽzA_½ ÎU/è…bƒ Š ‚ý¸Ø ˆC±A0ÅFÁ CPl$p±Q0ÅFÁ(›cPlŒC±I0‰‹M‚)(6 ¦¡Ø%œb—pŠ]B®Š]Bž¸P(®@n¡X| \¬Tâb·PÅn¡Z\'­¯ÔŠubxõ˜Fù%˜úùLF±I\-FASH`ÁÔ-°b l˜"»Ø!¦ƒNæ€ãÄÜ-à¸0‡œzÌÇ-ÞA7ˆãt#áN¡Ž&Bä%l£[}P«p§ŸPG! ¶ÑAqHÌ4ˆáw§Tg8%:")KTJÁ2…•×ïM©Î¾”舥„õ§”ŽÃ óàR…ùƒ ûùC a z”?z”? —P/4Æ‚I@¸=€.á4 [8#†ÙªÌb2¸ £€‡É$b óŘúùrL£|%&—P…É-Œ ðß;–Àïv.‡#à©·:š…¤2Qö }2®n¤Ú,ôË×½.æ~Ÿmt Ñ3m‚–4Ž’iµH8Z¦MÆÑ1m N”i[Ô’É Ú²8!A[6§[Ð–Ë Úò8A[Ç–Ö¶„ãLk+äÔ§µ-ã4¤µqim+ZVqBim«[Š9á´¶Ò–rN4­mmK§7­m}ËFÎ`zÛ¦–*N"½msËVÎHzÛ¶–Îhz[mËÎXzÛΖݽ¨mOË^ŽAÔ¶¯e?Ç,j;Ðrcµj¹Œcµ]ÞrÇ&j»²åÇ%j»ªåjŽ[Ôö­–cœqÛwZ®åÌŠÛ®k¹žÃ•´}¯åOÒÖÜr‚#”´ÝÔÒÂKÚÚZnåÈ%m'[nã(%m-·sT’¶Î–;ÙÿŸøRøÌ!‹òsžâ$8ç(¸ÅÜíÜ+¹wqÎs‡¹-…õ :ŠÚÐCèô>âRZj=õMêVê‡Ô/©1Š¢WÐét3}ÝGÿþ7-ç•òªy—Ã×ëݼŸð^æðÒ˜"f=³¹Ž9Å™2¿`Ì“Ã_Á7ó/ãçßÂçMnËáÖ¸â]þd¢ŠjÓÇrŠB—¶ZNÖËlh-èиƒ{88i{N|É%eö®Ël?\nÞu‘Ã~I¸îrñå×ɯð{ËúíGþ¤vLLÖþS»_X½VdÒi5«×Š¿Qï9Ü2`ÿ~"û±™ªßů\ªÜ¢ŸqT¸–U6Z\¶K¯ë3’ïË5™Ôã*ÃôŠŠ¦5UþÚݺÕßV9†o7æ5êZ4{ºf¶Ÿv.}:žó ýª¿™Š¹vJ\²h¬®z¶øâ¡M—ôä·D¶Æ­¦‹înê–×|ÜXau­¼¦áЦ×SGí—kKtzÝ–^í®é+u©Å:¯0G·\3³·°Î`–ïÙ6¶äÎ~kåé¡ü‡«$6Ž˜K>RÛe¡²ÅBÝE^­G¸ì¦DÝ÷Ç×Þã)xÄ·âG]ëþ®ú³<++–³¼qû·U›oVï¾×´õ7CkÊ#5¬ËŽî¿¶~çw…ÖëÕßç–ÞÛó\ס–—$º÷|â:X[wIýÔΣÁC¿TælÙ­ér®²n*óT^#¯¸'P}:²þ¹úÊaÛÁaOMz¨zsG©¥ª,ºÓØ­ÚÉ|°ÎôÓ®ý“Nët_6]½YÜ Ïœ1¯ \ToÍoîÉéˆ|¸^ù¤eñ‡ÆZ:X»\U±U_pI¨ø˜yý=S%D«~ÜU00[ÐgüÑwp|Èœ) WšöîIltE2õ¿½xrÈ<¥ÝN+/[_—·S_wLŸùA­¯Zâ.~О÷øâEõÞ?tW‰8ÖÖï6%l ‰ SU÷l5”¿í1ÊÛ÷åÎlÔÍfëÆ«¥µ­•÷˜õ÷ù ×ýij±·äàï#e#ê¬1Ãî8u*ÿžœ&…!b=.,;m¯oþm]þ#Þ1éÚóϦEéÜ-½ÆJÛòKâ…Žð®oL–;åÅÞСgì5/ ¾¬W½á=ð§:% j½yOcÈî¬z©nÓË%[‡ûåÿòWæE7­t/-ã^ä0mónôéëîîÚü€¥ô±üëwqº²Š-Ú%…kNZÖÞÖ·h(¶..ùSõúƒ™ò±Rù JQ]›Ûe(Ðm,nݦº¤®ÇT7&v:7Ü){k|Ñ„aKVq@»æ”Nÿ¤Æ~Æ,ûp<ÿ»\e³j冂ǜeÑ~ý˼̑ø%—ÿc¬üã)#GµRØ-Ó”8¶9swFLû¢†}ª,çLñ•‰•÷Ž‹~7ý«Ä‘ÁËY4˜q…;ï»~Å>Utj÷ÓáÌ~hÈUõï¸!­5Oݹ$±dÅrmv{,oOxCý@Ö #²·4—ü%bÛ۷˵ÑÖ¿öcó¢úHMÐ& ú+ê?¯WþIiâtU[{Šöô)›Ç+î(ûÁŒõ uFD[åí–Œk–ê½ÚuEU@tÛTmß̢׆$z_uicéVÏ>ψý–øº;•ËV‹Õ9Cá´wûvÌp DGGjÌ{"¶åßMer®lQ_mNŸ0·_¹}Hw¸µÒ\r¼7ÿÄ`áë”#3i9‰Åá å]­ù£pÞ'â"N$}½Zµ'ºùêÑ‚¦ÆŒý¢ûfÊïw«7<å.~³Kù^pëûñ‹2»UjÍ–‚±‹ áÍÛf³BÞÌGÌKßÒïù¤Kyõf›jŸ›ÓÙ£ ¸ ݸìqólë– aAΤøˆGÚ8e}Ñ»þÃþÂÆÝkª¡œÖ¨¬­nÃjQ¸aÍãÚô'ÍÌKîâ_™r_é2¼Ÿ1I?NT.öm¨ò kz˜šö£[ñÒß5|0´~ªaå?¼™ÈgYª.\é(¼qLö^IÅÿ4ÔÐ=ËݼMÏ †ºÍÙÁeK”ë´zÑÚ>qUcÖæªÆŒ[lµ'Õ¥§¼Õ¡ÂpSî Kô¶OóŽ]üo½MZ´­¡ð–®~yáSÓ«š•äî.õW™ª<ªÝÝbßWD"&ä_ÎTgïÐ×vÌTô¹óÿ'œ{¢¿èû&É­›ïã®}µuùÿ¸6L»–¡èÒ?]Ì[nïÖßîZ}—eËUÚƒ#•ß3~×yQ@¾ù´9û7š·ý²YƒYi¡Ö;sÍÝ¥ñî+æë\i^ôngõîe?s¯î ZŸñç½aßý¶cCŽYUá+m5ä½>T3é‘f5)s‡¤åÝŠÓÑõ=EOé$/u¥ý^Sù®qvù²¦Ê#ÛnPny³ºæ­ÉMcÑœiáÚFLéšìεÕòÚoõä{\ËG¹!óæ¾†Úø@Í ·ìưöñ˜ìçöZµ$kÍÉÖ`ÊÍÓb*àè$Y ”¦—dÝJçrJ0É ˜–1pL’¬ѲjIÖÕLÑ Žó*Ç‚YÆ0VÌŽM’õ /|;áÕIÖþ¿ÿp8u’,qˆvJ²º‘ X–+rI²Æçê%74f¢f·ä† Pƒä†> FÉ #@É ³@M’4‹Q³Wrƒ¨UrƒÈ'9´KNà~ɉHvINL$'4Y¨9(9a IʲÐÉnÉmþ,øKnÏ‚ÏBÉmb„Qɶ:Ù#yȦBy½’‡§U´LÖ'yXœ “<\‚¹_òp=æ¸äávÌ’Ç"ÙôOþÙSC’Ÿ?ólBò(©7"yTž<*yÔ€yLò¨ó¸äù&à&%ÏG0OIžÃ<-y‰ðŒä%a.ð¬äWú\ÔÉ•þº?½Â“þÚ®¦_}M(ýõ¨½.–þº>½.—Þ-ÏGJ˜Z@*éoûòájéo'1k¤ÃÚô{­tØU€ÞÖI‡Õô½t8¢¡ßy·D:<®A2H‡•KПŒÒá! “txr)½äƒjé°¥>3a–·¢3hSˆ>´J‡Ç ÑI›tX³ ´K‡mËÑI‡tØS„NÖI‡ûuè#§t˜·‚ù¸ãutú¤^:ܽ%ÝÒáÄ †‹šy¨A:\² Q£t8¸É‘G:<½’þD…š¤ÃŽU(é•V!5j•®Bäƒúz”l‡þôH‹üp=­CzÔ%¯F¯¤ÃæÕ¨¥ÃM«a+’÷bî†~W# K‡UÅt5z="vÃk” ê‘÷• ê…~Jûà`ŽI‡eÀýpšåÀqéð,æ©Ù èÝA`¿½2\½½>I.æ¸ôÕ"¦¤ÓRž ß„Y)W&– eSëM ”ÉY¡‘áZ*™Z¦'J+ÓÉ 2|JdF¾?6Á+&žTMØF›I‡ï¤Ydøl팙¶‘ªÿ°ÊDüÝ.«#âo<ä"â¯N™[†o~½ÌC ²FYQA¶¯¬Uæ'ª ä ]2»,˜R<J)§,œRݲhJEd½)Õ#‹¥TŸ,žRý²Á”%RjH6šR#²ñ”“M¦”U6M<%›‘áù`ð_Pf‰îfð_Qxr\ΕËåxÌði/W‘ŒR®–ãI«”ëH‰F®•—½ÜD2¹Qn!ªZn–Û‰²Êmr'QyÜEêk©z9>ž›in`ÜD¶2Í>¦ÈF¦ÙÃ4ÙÄ4{i£¦šHÊÅ4×3^"ë˜f'ÓJJO¶“Cøäc².¢üòY¨€¼_ÖMTHÞ#‹–÷ÉzˆŠÊ#²>¢zåݲ~¢bò!ÙQqùˆlPާ Éñ³.D#ä€ ùáQù8K;ƒÿ 5IrB4E.”Í’^¦å3r®¯ B$TàÛÀS4‹x^‚P*ð’"WhH‰J¡VèˆzG«(a…^ad…AQÍ “ ³ÂÆ «ÂÁ »Â©Àƒ¨S¸øà.E½ÂCTƒ¢Qá%•š­²Ö”j”µ“ø2"¿¢K$J/o)ȃåt+䬓fPUàµ)¢èQàË£—÷‘^z1y²eý¬È޳Ü*PG\2¨ ¸dˆ-è–%X‘°¢G6ÊŠ>Ù+úeã¬M²bH6ÅŠÙ4+Æd3¬°ÊfYá”q3ˆÐÊy¬0Ê…¬0ËŬ°É嬨“+YÓ3r5+ì2 +xHËŠF…Žõ }YÑàf±B¯0°Â 0²Â¤0±Â¬¨f…Uaf…]aaEš•íïseŽT '+ê2\d)4Óõdm4Óî ²z⇶aN†˜FVâùèa%Kêšé®Ø`+ÀSâ­™*qQ5Ý)&E A5­Tâ))Wª•x“£Rj”xó£Rj•xS¤Rê”x“¤Rê•x3¤R–.L´Ä@šh#Ii‘I‰§¼‘®& -2“ÚBp;­Dé¨l¤HG9H®l¥ë”xGŸ@N%Þà'K‰? ¨^‰?È­Äß Ô Äß Ԩğ äQâM5)ñ>?¼JüÕ@­¤¿ én¢ô6á'Mt‘¾&¤«‰ éi"D:šè&ýL„I7Q%~ª#J+ݣᡯÀô)ñBÒ«¬ËèWâ ¦Œ+ñ,µ A%Þ¢(ä솔#„“c„F•“„Ç•SJòbŠ0ÓJ9+f•ìqFÉÍÄc%ÌÄó2•™¸Lœ)ÏÔ¥ÊTgê2ñ­×fHFŸY’YM”1Ó”iÉÄC6ƒ²eâãY3„í™.RR—éÌtgâMz}fG‡‡´kÈlÌl%ª)Ó›éËdOºšnÏ$#-aü™rVÈ‘»2C™ÁÌüׯîLü 6&”@‘Lü)6%”@=™ø[l¢—Põeâo²‰¡êÏÄegâ„ÔÔa-5HØG öS Â!j„p”%£ÆRã„ÇÑ$a5Eø…iB³h†0š%¬BÜŘ•p³—PrÂaJI¸—RŽSjÂB%´–péãOÇ3zBjª„°–2öQFÂ~ÊD8DUŽRfÂ1ÊBx²žE6Â<ÊNX…„•Táf'!/å" ê ‡)7á^ªpœj$<ѴسØd„»äe…]ØÊ ‘eIûâGèg~BÏtz"@h.Æ_žõŠáFE7a&\'¶É£„ÍòÂFy/áúñPŒ°]ÖOxF',–ÖÊ ;eC„­²á1ÙáÙ(á!ÙáÙ8á~Ù$á>ÙáÙ4áˆl†p·l–¶Œ›…¹UÆ#œ-ÌÂÒ°q³ÂÉÈYQÏ(YÑÀ¨XáaÔ¬ð2Vø-xƒ¢# ïZô)fJ²ð×¹™6dáÏs3mÌÂßç&BðµF¾Ö²ð÷z‡…ì©ÛhGVƒ¸a=þË-+:,w¸X†=+l´;ËÇVm`Eg#Ë– ¨‰6º5˛Ջ«úXa¶gÝ4³ô:ÓEHMk© a"짺 ‡¨0á(!£¢„©Â/ôšE}„yTŒ0þïßcVRqÂÍ„¼Ô á5D8L%÷R#„ãÔ(á‰1B 4•5žåØ€ô‚iV3¬0 fYapUD„ùDŸ(€ßGOÐ'nýü„x ‡s,ù~@Ù‰0}âÈiúÄõ÷Ñ'ŠÅ\ÎGs8û¡Þ1øeAùîõÐè-Aœª{ †ß èëðí™p ‡èœàp®‡¸èèKÎáÈ þ0ôùÄ@¾à^èËËáÃ8²2 ÿûé¨søvè« Ž#€úO)8œ,à¢Û1çön8.ôw Ú½º—Á¯÷õ«àW }Þ ¼~Çàw+ŒçÑ2ç)àš ®”Ë)Â1åsÈáp~•ÁåTeÃØ¡øßkÐî²l.§Æõèà·ûA>çKð—Sã¿~G ¸œaø½c|ÎíQ|= ï4\Îþïp8wC|l —óèG¡~Vôõ\ÐïÜc¾™Ãù+\ûMÂùârî~Æ ¹Юæ8æb8(/‚qî‡_ÄUÀ·ÂyH£c?DHçpäF[ÈA7|ƒ±Äƒ~rœ°6¢àhB}à~™ÐùKŒ¹iÓ‚ô+h 'é£1ÞVŒë1ôwíáwÃ)œCvC¶!ßù*$ÎÓÚ‡sÃiHZ9@Ë_ƒ¤å¯Ã'”Ÿ<Șvôùú1oÂNÒ~€õ,쎷`'pÿ m@ç9´94ˆù5Òo£ ˆéDw¿@ºÞAÙ»ˆƒ÷ÀîßBχ¨{?Êzwvý¾ïżôA×àCÐóÆÞº×`ÿ1wœ]GÝÎÖüg´ý/´@Ÿ ïS´ÿo´¿ö7Ñô|†öAûA´¿…öŸ£ý_Ѧ’ÆgK71d œ!q£b@D쀔†ä1Û bF3$ ä‚fÐ QˆO êA7£¡d'èƒ j Úƒ\Ð úá~†¤ƒÁ*ÄÖSÒýH‹cѱ›éúqh ’Q§’ŒG;PÄ`+ÈD™r¤Å`ý2m@=)B/–ï /´qQ bÖbÄ2Ä2‘€Œ›ˆþ‘vBö€A¤-ÁWÀ úña†XAw Ö.¤%Ž!m ftM¢>À<@¦ÏE¼M†N`. >€@¦ þDÌ dÌC˜ÃGÑßãèÒ9ëí1äÏ¢k>…Œ±`^P/=>Aº±Öý]Ø7öó™ü]´9Œ=Rÿ=Œßú Óà“€¹–Dè)IHOÆØ€u:l¶°õqØ 3a;ˆš€¸Ù/H~ã6ÚG tÎà˜‹:OÂ>›Š>ç¡`› 0™›Â`} }|:AÆ"ؽz€zÛžFÈôg`ä d”?ÖæôGcè¨ÿ,ÆÄ¥èXôƒže°a9úâshl ¸37+àÐ ô+á €ú3~ mê×XÌéjHƒýÎø<ÒÀø#Œ´!¯Òb¥ñŒ9^ƒþy¸'cø1Æ;^O`b(e-úŽu˜‡1>䑟 àÝ .ýƒn _2òSôû3´mÐ~#ôýþÝk¤þðȵa®²P¶ i¼“³E½} *616ƒLàýÀ¸ãÐÈØ¬ ôC.ì¹  ‚˜<Ì#pn ÏGŸ ƒ‚},×È”­è¤#V-vè„´AÆÀ¤!­Û ¿sÒÐ œE¨S ½ ³å¥(ie(Îr”oG9Èt¢¼å ­åÀY…òj”ƒÌ”ï€À¸cVü Òv¡ˆÙq¨=h H-ê€~Y‡v{1&²m/Ýéõ(Öý(?ÃAÔÉ ¨ç†Œ«G¬@¦Bß`D†¿A3™G‡˜ŠrÃ{su þ= í8æ$7ÂNׄþ@Ô˰~ Í ¹¶AyºA?H? ¹mÁ â'î$ôäS(i¯¢?Ñ;Aæiøâ5ÔU³$Rÿ:ôãÄhFŒYÚa3°¾›ß„ŸA?Íï€.`;‹úo¡þ+è;‡:Àrí@æ¯a÷ÛèXÑÎÚ‰vÀrvý;¨ÿ+Ä dÊ»è.ä+òÝâEèÍÀð´A¾ ²¤¼‡|@º1× Ôƒ`¼„r :y}häœ+¨—áÄ·þ êƒz¤­¿Å\|ˆ>@rÆlWab\ÿ;Ô†ß#ç™ýÀÒ‹yÄõÚÙHúQ@{÷úiô¾òÊ€ tÃa3p€60âþ„:À z€á:ÚèäcÔ™À €ñϨœ èÿ vLÐ @Ìúä@ïý Ó¤`ìýŸÐ5†=ôSŒ dü7$°Þ€^äBÚnb~>C`ø ê ö\Qƒ°ÄÝÂ8@&ò“?‡- í¯°Þ†ÿO¾@{`üm€EB=NX2l ‹ée‰eIp‚n`Pà8@ K†'suCÉ’‘% WÅ’(Ľs2‹ýœ%m ô€@a‰„(R@:°¨€Ä£Hi d‚\àÍ tƒA`˜} d€\àÝ ùQØ:Á Ð?ÝÀR@:ÈnЀނr¬ 8Aè7™Ê 0€(24ƒÐ úù.t#ˆÉ X 8€ ¸Aè€|í@ °€2A.p‚zÐ :AdÚ#ˆÉ d€L œ 4ƒÐHÚ#ˆ¹xæJ´B:£pŠôÀ$Ü“MÀ=4ÒŽ=œÍ 鄬…l Æ=ø}¸†#ÝôÒ´ Ò=T1‘%Àâ@sú€ˆ…~H0 Ï iÁs¤2÷¸olCº›¶IŽϓÐtÒbtáÆH%Hi¸gÌ€lž[! NÈÌéÐ Ù ´>d¤3 ÷Qƒ‰_2ÚCFAZ@t§Q rçõñhCï;‘îA^'•ÉÔ·x®HgâYKœ> ƒ|¤õH§?€{W¤ÝSÐÿxÜs#‚´éôéÔNôêAèâ :Ø Òyc„4¦`Œ6H'¤eÆÙƒóHÎ{h}øp² íô#!£ - $ÓçxÈH+¤ ¸€80Ö¶‡ÒÝÀ‰ñöC’™(GÚ™‹g^W$t"mÁó±r0¾ƒL´BÖ›qoI ]i£à?ª#þƒ´BGÿLºW ¾öÒ<¤Elž_ ¯(H °ÑyL‡½ÄeÍNP Ü`u: »A⩟æýlœ Hé 8@=hÝ`ˆOÀÇ ¤€ `Nà  } ô HÖ:/Ð \À Ú@g èâ¤ñ l€wã¾2¤€ êAè§uæ¢ 2@.¨m †ázб"=RžÄØ@'èƒ@LE=â@ HV œ ´nÐ8í@ °€4lÀêA3è=`ˆóá?’A°p‚zÐ:A?úhâ@2H™ 8´nÐp#zâ@ H™ ¸€t€n0ÈBŒD8ÒA&p€zкA? O¡, d€~ì6H7æÎé¢iÐ:Aâ÷1&’A°‚\@öa/…tƒ6Ð zÀ-[;A HV`ànÐ:A? ‹Ñ°P‰8HÌ çxN³A:°9!ëAfÖMCv@vƒt¤ûi=ÈAHñiìÙ ó ží ã@Õ 9é‡}i=Ö®éL¤{ï€l†o xöwÑöH§FH;†a½Cº×ÙFû‚ì†4ŒÁzcGÚ)¢® ëß‚´‘¦Qž†´ä‚R@P×Ù{(7dMÓ¶é˜/ 2@.p‚zкÁ—@7ˆ) Ø€¸Aèƒ@ÿè¬ ¸@3èý€< ÀÒ ¸@èd)ʤp6ÐÈ2” H¸Ÿ´A:›žƒ0ÈrØŒ`,ˆÓ@ X2ÀZ`vàµÀ ZAè=à:ÜsÐ Â@ˆ0¤¥À 6€\P \ 4ƒ3 \ý`30À œ  ¤¸ð¼g™~¤É Ä:ˆÉ d¨m@DÝnÈAZw%lq ¤ rÔƒfкA?âÑD8 Ò@Èx&ÊÄ={.Ò¥À@38:ÁeÐn² þQÀÒ€äh ÕèÄ€d2Ôƒ6Ð èBŸŒÅãDJꢉÙŽ·À‚‡ò#pãa|–ÏÈÏÁkH¯‰H>ØZD‚Ç@xíÿn tqà!0d¨GÁ+àðøop¢Ÿ`"xÌÏ€ŸËãèü”ƒçg d3hg€ uœ`h­¨Ó>þ3RÀ‹`hçÀ§@ö1 ü°s°ú‡[ ÂÑžkÈ9+œÄGñ”A^$6RLS¤‹ü–ô“ωš ebf3éÌ*æEf3SÊ8™Z¦‘9Ãt2ï3dð4ÆŠl ÆšÙñìƒl<›È¦°il:»Šµ±»ÙsìŸÙÏÙPE¬"U±VQ­hUô(>Wœ‘KæVs«¸¸ œ+æ\\wŽ»Â]ænpþü>–ÿ.ÿ4ÿ<¿‰/æwóüüûü/¡Âx!^˜/¬r…z¡CèˆÒ_9F«ŒW~W™¬|J™¡|A¹IéPº”Êw”ÝÊ~%'ŽgˆO‹/ˆ›ÅJñ°øšøñº(ªBUãUßUÍW­Tý\U¬zYuYõÕm• 6¨£Ô1êÉêõJõu§šó‰ò™é³ÊÇîÓàóšO·Ï N¥™¬™©yZó‚&[Sª©ÖìÕ¸5§4šÍ4š6X®¬¡]©ý©6_ëÐîÐÖjµ¯h_Óvjûµ·µœN¯‹ÔÅèÕ¥éž×eêHL|jzÆ›½º¾±­³§hôa«ª¸™Ù¥çz¯sú°˜i)iéNW½»¹­ƒØ²Ý™ŽÚæ3×ņ3Ý=\TrF¶«¹³Ÿb,«6Ø«ÛÎu]ט㗮Í.mîqÉ«6ä:›ûopav‡‹ ëèˆINÏ,uwôÞL]e¯m…Ÿìh4ÇdÚr®†ÆÖ37nkô ´8›ÏôŒ–’ípuuߎµX7tô†Å»[ψƙiÙ Ýú°™V›»[?`\´¶Ú}NŸîlíèÖ„M·–ž»;-³¶¹'0#»­'0fUvCsÏõ@crêR;7-Ýy#l¦£5,>9ÛÑ•Û|=.ûòí±‹ì¥­É6˜ZÚmÆØ:9óg™Y#Æêêâ’—æº/sQ©Ù­°G 4Ä'§¦mÈ-uÒóÈ0Ž›–²(c­Íî¬u#³çú ܇¼L¸¹ùrÿ x9֒šcÓîfz*YK».÷Ü0˜cbÓ–Z×BIc÷ן£çôU™Ù:wçþa ®†æ3rÇž:ÎÎî~slF¦{@ ŒµÌ„šÜ†îÞ~ZoYjÅ€0´‰l½q¬eZò¢ô¥«¬k37d;ºÇ.mîˆOµvv]î¹AĽý×n Þ&œ¨ÑêQ1qÙÝ=wsÇÆ[ÒÝc­Üm ¼äÀiç â5µ¹»çÆXKZúí˜U™gº®À`ÔKËÈtÈQ·hmi56tëj÷Æí «­¡ã\ç@Zz[ü<-5Û͌«lÎæîA1™NWvnm}ó™ŽÎ®µgºDCFfæXo ³cIž™MCÖ{‚EžaÍ´9\ˆÅÖÈ™‰}Óe/uÞŽŸ63‹ò\ç¿™¢;ǹi3S3<~[4Œµ¤¤¯ÊvÒH¿Íéí¥ÕîÔ„µˆ™oŸ‚gâ-tgjE*6Þ’¾jm¦³¶ÁÝy¹·_ 3b«2mÙžœÛbmÏÀÀ™tAÀ+çnsi¥®wi¶ìž^cìªÌÜ3ÝýQ«¬ú™‹V­ÕxÖ—½­{ f¦]Þÿ:»{¯c4ÇÆZ’S±X±Ða3¶½:0}˜9&nÚÌ4bË-­®Ç>åm‚%].eÑR+4:±¸Ú:º.÷ÓÑàí´¥p|kµrP4c <½Ú£·[!´ÆcotÖ»Û nÀ3T[i­[VâYÕqÓ¨±éÞDcÁfØÐzî†eI·n°×6"ЩÙq׆lWCkï°ø™ir¸wöÓ *n]­]74c§-ÝPÝH÷ñi‹6”6ž»Î™ã±îÎõrañ©kí gzo‡ÅÒÍ‹*pìÌ¥XS×Í‹œ—WÕw ÄZrÛÑO6ܶÊÕÚÕÏÅXàò ¥®æ®ž¨UvsÜLg­¹ŸÓ„YÒVÙ\4zûÅiìX\bTü¢¥™wó¹Þäê®7ÒµfbÉ´uôÀ‰«ÖÖ64r𙩹­½1«Ü;®I]7Üçn‹Q©‹²Û.Ǥ4FÅN[jŒOÎp´õb~‹2Î3çnÄ¥.ZeCä]¿Ÿ~¹7,1Þ¯Á…*5;×…KšflzFW ‹ÎöuÌgr¼B7Ϻ=Xq¥l•½*O >.Làb¹‹á;õ;G»µÜ,b¾‹Ø‡U–ÈÔéa™ö]ˆê eó‰øyލø $šfð?לc`;˜híÏØy\6yŸ}Žl > On¨¶‰¥¢-6JõNÝç¸*âì«æ#¦Vù%»<<›¨Y—âK–{ÃögËu“Ø$ö1Ú§Ž­c? ¼«ÕSz„I%$C¯QÆ «d:HŸ>•¤‘¬"¯’TùÕÁÝò¡çHv@¹Ÿk ù仄%¡Á„¤êMäa¡ZÉ’ÈK¸Kô'ïi }ô’ 1™p|´ÿ ¸çUÍhñk6œ 8øß†òɰü^ .Ÿõ~ï’ùÃf*a~%nЗ‰3…:Õ§¬Ž,T¼K¨|T‘¿ÊÇÉÍ+Ôµ ôìG¾Ñ‚¤}‰3R+¬Õ>¢î =Ìn^Ä”Á‚{9‚ãøQUªÃl¥¿ÒxÕ÷¿´o¨÷2RГ¯ÕÐA¦1¯³ã9e§n¥b¥bó$ãÔáà“JŽè¶›}æ:îuö¿•ÍlIP3û,ó,sY=ÉïIfR]b¨>À¿8ðY¿M¾‹|WýU<£yIkVºÕùçÕ'ÔËø"Õ;š-*vЇÿ ñdN®êpþo¼¾IóݲGÉİG }meý§¶$_ø½­%Dø§5¿½üwÇðôü#Ýž2B>ÔnPDP’毤Ôç×|fš8AÍâyå:Òº“C¾Îowõ d$û4¡¯|ò4¹›z »È#ä 5=r÷¼¶¾ˆ~-ªUþɤɸ—¡kb¡ºW¨ó£e ¾s~vij÷qÓî‘;ÜõÙϘ°~† ‘¾Í>[}qä«õÔ#Y޼­üNÄ;ýRꫯ­ì½ãü{ŸZ}îÖ¥Vò§˜²>Ú² jüÔxB-Òü±Ê%‡šÅ Fñ¡/Q©ÊG‰Þûê ~Éÿ¯LPà2I1L ‘U¥þ!¤ÞSÍcõ†¿2ì$Å™ÀÞOù½/šÚÂm*Qëõ`=ùº×³ì7ùgÙ¯/ðw/š{“*oâø3}ˆÌð}è+­ú»¾"{õŸ1Eúê€+鋃ôäi¾X847X ôõ¡?K 𠮪N0Dâ×*Ûø—5'˜fæ2ßÌü‘§¥ ᜚üo½¦a¾Fó½A TVj!ÅÚ¦á:‘#Ãy#ó;ÆÈ¨'²? ? F†鋞ÿŽ9ÅýmbÑ ¤ó¾,žîÿ%¡q1 ÷Õ¥jÖ  }PhÖs"®v¥Hççpôj±Ž¼2j ÷áð±¦T’Bs’´åÖ ê†rLj€§µH¾ú¤0WŽ40+Mñ\k,œ9à³OX¯®f«ÙUÊøϨ<Ñ~c©ÃZû\ gù°j_¸n8GÖÆyW’¹Ï{géîß<¬CºË ¤r„§ìÞRâÍ©ajµ¨æ‡‡ —°^H3a½µG‘do+šþßI­e†Ro)hj*û’bhMÏ%ôûª3Ìyrž¬æ:™ÓL“PÃï!›ÈŒ®~>ÂÜÛî?cqœ/M%r÷Ú>”¢éïÊsç‘ä+Ò“/|ÃÙ÷8–1¿7?ÂpÐ7Y“Lö0ž]ü¨£#ƒ$×Gɼ>ò2ȧ‡5*‡öx_?B ÁÁv‘ÆÇvæf³É òUþ„îGBÁ({Ø(¿{u¨"/‰¿ç»•7U'•’j(¢èk¼ûví”;©ÿ“rˆÿ©žQ,µu{š¡QñUí:&cÌ$þ #u"g¨Ö×—~}®@óFhv24"Èþö µsu><…µ¯\ƒý‰YXÅ’2…‡Ý§^Cïudœþ=.Ö‡#SUÃHŒÀ1|rsxϪ—ç“ífüÉxn.¼ŸBξ,ÏþXf…j.9†c_ÔìÀ~tED«A«l&«ä\=þ¿Ëwø{Ï8ì#Éä²ðyU›L3þH÷€n¶ß v¨æ°Òa)†ãØsB‹xÃWÐ<#êG8 Ù4Ÿë>Ù8ímåbî<̈>v¯*‡3ÁŒ6à3ÿyªŠûBØ6)p7`üƒéSµ‹Ù ]ÂF®œoäþ¬Yß,À.0NÔ°5ÂÄ‹DvÔp“û”ª?%QMäAæ§â‹Âòkßlõ«¤ÌW„ÿ¤»öÕêëÂ1~£ð'ï¾ü Cwn M%@ßFÕ«Þ=þÇ*z<ö1‘<r; ‹éê};C– ŸiþLñc¾–_‡-öløèö6¬‹V\ðÛDBÓ]Ì$þ‡#â^õ™ÂS§.²„¥5héëªùÚÛÂëÜ—„¾–‚H²qzgاºRÈuÜ|q—ÖcÅ*ò¡Ï/tf?”úü@óSX¸„û,ä ó óîó”0W• ž7£f–ØîÓDÞ1¸Å Ë\ |ÕD^ %q÷é®r‹”UˆŸD"ÇÔRe¶À‘þ†ð¬z(ng³C³LSÒ·ü³È5“¾uý¡¿Ùr‹ï¥ÝÚYÏ¿ö÷3Yÿ&ï1ï_êÍÚåߢEõ:»‘Ú÷5-伣ߠ뗄rŠœ!o!utËõ~‹c¯·Å½ò¿ o¼÷ïÆ=ç·†Ò^©fôL#Iÿ=š õ`œ|‹ãdÆ‚c‚|>“™ËÜÕkaÒ˜g¼çË W3´†$­c~Ƭc²ðòÔ’¤¬{Z}»¿òåæYã?ÓüÕô·µe+ómk~{ùïŽáéùGº¿m¿žºæ«mþ•ößüW½ÕÿÖ¼ÖÿÛÑðÍÿÎÜ´þ v¼ÆœEí·ÁEo«ËW½é?0’S™O™ÁoÐúå|žý¦^4ßXòÏÿþý×Þð5ïCž™f`b§x˧±3¼©Ùÿþ³OÞcéÂÿ±ÕÏ|+ «äZëÿ¥Þ6ßS»àoZ–Ü9«ò¦vþÍ{þ®¼þNÎáoiUË?¨÷*ûuéÿlªýNêWÞÔY~û}ëÞvÿ ‹ïþµÍ(<÷ÿ\~ýý{ïkzúèž¼åôà=9 …$‰@§øÛVòyè=¹‘_©ñÿ¾íߨÿ°çþÏ\·ÿ7þ&ÈžŠWL…œ©˜«HS<ýðÝRo?ü†¾^¸'ÿ'ÿ#{^ú›Öù8+RTÜ“·÷kµŸBnûzç¿…}ÿŸ[«z*À×_kòÓ©ƒBBƒGJj‰•”¢¤‘zi¤ô”dýÝËDŠ—öîŠtŒ·[¤7¤÷7roÅ7q“Ù¹R§4‰m"Ò2zØÈJË©EávÉ}=Á½ÃI_J7¤Nš§ü¸¯H–¦*J_(~Ep-ÝCÌÒ$NÊã¤9ìw¥qŠ.]Ê•æñR/5±ÒU^ZÇÕi±ðå)QrŸ…Ê·8É}“m!äM"e)¥:AÇK#”ÒòLéš"MZÂeJçùd£¤RÖÁ"Åókµ’ûÚýš“ڹŠt^8©Ø3Ùvj2ÛR¤"•´^%M¤ÕJéšRš ”‚TÒMAZ¨\«â¥Šx¨Ý–(}ÂU_—fq;1\Õ‘Di–¸³Ó^ÀI-B‰ÙnÞf†þéÐÿGNJòoŠK%~Jª0ÀKª•‡*‹Ÿ/xÞþ|Þ󛟗ö«Ò2ìÖ•+í+óVn^)©ÔeÜÛgU…i{ªŽ§9Òö¦íN+I«JÛ™Vœ&]tÒ5­tV'µøHE‚T –Vè¤9jØ"%h$“V¡‘B’V+Ýô‘T¬~T¹~4rR¬Tº¥É”VðxJ>©©.–&jáÿÅ~VúXÜ>[:ÊmΔ*tp§®…HëÄ•QR‚rœbÑ,9Fš£k¥eêµÉÒbmy8¦í(ÑJ~l …J.vÒ •´_yr¡ ÷ÙuQ#1ªW f¡º6¾¦'NZçûãÙó¥K¢yºÉOº¥«¹üF:*îÚ»U«‘>aSM>#R»]° ç9ù­d邦®Œ³ï…ß­'1ÆH_hËÊ”eJÊî©­“R‚ïÞ_ O>Ýkô¼â°zÙ1’&]ôíc÷Ód”tIÿisyEy­0~¾TÇï?ÄÕÌÞeØÊJešº:ÖUÇ–ï¤ ßßýRp ª%„gŠQî0.ò“æ¨N·soæŸUHW¸=œÝX`´‡;¥¢”'–utÙ÷kKºÊºò»¤ý~—OÀüá»4A¬ ñ|[‘l/ˆ·÷îæB¥:ßßžøD¬¿Ân½Â^ο,-Ô¼’#8×±›×±8ÉìëØ†—÷½,Õég)Îå°E9¬#‡-9êïàjsØC—”ù—”’Öoÿ›&eA“rÛzaÏMíöû@Ý@ÁÀî_×€}º¶d½°«I(i¤u~ó¿ÉQ.a÷f)Žg)Y gŸÖ‘£üõv×¶d »gŽbçEñ…tÕo*ßÅsyÎÅsåeþο–u~¯Mð­fD#–‰åóDû<±0H”¾Pbs”üt˜/)B‡Õ†5*%N•*´Rë8,BiVâiš¤|p®4²”•¦¨“¥Öâ8©+éš"eÉÔi( q35nQšÊ»Ç ´±ìçù`Áñ>ëC$•&a´Âç0Á"]1ºZ±¸ù·¥>'‰Täó{"ÕˆE‰8ì¤)W¢tH[>Yš$"ÞikX¤Ü+ê]Å8@éE%M±5,v¤=ˆ<6‹ ÊIÐ&^Ô^!r|•üÄÆ8i‰X…;´u"¦ÅžòôN5Ž{[ÔïX˜´‹-®™Œãz‚Õ4}ÂnÚ'J7ùÞÃ=ÐC5\TnŸÍ0Ð\–Zž}‡;Ø×õd+­½¡Š ªãŒ"Äa—¨—³xV˜çs–8‰ìˆéÚ—+•ðP ]4²^áLt$B¤Ç,ÙBÆH$ÇÂh$’m þÒYõN²—à,"„Œ¤Â@B!Ì!$"<„TК„”ÛqcäLnŒœÉ¡ ¸È¢¦Â@µpP¦óš TxtRAËÌœÇ8&õô§†‡œ±HrV¬iÉnû˜“ê¯Q¯àå¡”)“í¢,µ ³[GréÅ¢0qëu«v••®¾Ô‚TèŒ2ÀÎÕšÍÅ=òFŸüëdh;µsrY/¦dp÷ätj\ub­çš”üN2Ϋ+Œ{ŒR޶ ëy")Ý`@‡~ºƒ"WQT=°A Ǹ«î÷ÈÒû5Èß2&D)®’Èæ1!¼šÊ(¯Þ9rddˆ,³Ãý‰Ú.‘ál”Û+# r;a´§\ðÖãG1:¯‚ ’ûcØ÷n2B೸ÝðÑîµ7ÞwÒ rÕ„©û(Û̽ÉIgÙ¹Ûg¯÷ôºÄ¥jé¤r#.<áX‡ÄM;{é¢WÙgC•é.fQ´E78éuÙËv¼p‰Û!D ÛéŠ×Å¥gE8¼8ÎŽ—Tü‡}áÇK±”½uVø¬Õf¬Å¶— œ¹]î4AˆPœè¼ÊÁúQ¢[Âxwe£Y"ù£C”15¢Dòà˜ðÚbê1¯{Y¤.2ð¡X•„60ú ‰È1ÃXFdó(Ô¡E9c Làë(Ê¡ušh"ÂÀhN"áÿÀ/0¨ìåM¨ pF=Z èÂk^ö&xèÑŸI„¿VþV¾×)^®!“ hˆ´Ò”#™^æiÓ‹Â~›Þâ¬SHn­Â¶„;…+¦òÜf;3Yj·—¿Z^ú‹TíaטÇP|`û죆%~¸´1ùmfý¤«Âov«4—4K}𽑕²¾kŠRJÒœt­ÇöCÁîЉzÛÛŽŸ×“Ý܃,RoàÎrnNZ¯vGVÉm׫‡´LÒ(N þ+‡TžâÄ<ÝçœâÓ»?|§{J³Až˜i¸ò žcµÛE¤†FIÊ¢SéÔG©›£Cù‘9Õ½9a{—IyGù£õl#? )ñ{kE‘ŒÑ#|ïUjèž(£ Jí³88ù~'Ìî¦~ åƒ[©>ÁŒT¯œ"z{4BåLj±mDŽ…$uc Ñ5ùØM¶ä&‹Ù’¤–·1¢mPêËâ  \IôJñ©2#,¢|(»ÿ‹@OÄhJÆ|ç;Xˆ÷c!ºD>jÇ@C­¬!ïŽ2•~ºâzÙËÄŠ]²*yKrI³`ºN%çäH8ËnvJ ª©~Òimåd{x%õa‚²ºØQl/.‚ [ÌX³ôÎŽîìîðw¼´ÃÙSKÞ&k¥&µ|²m2Œô‰2¼)î_t‚PƒÅUT·4Õgê9v+ûº qœÂa¶kÙb-ËS硎í­8Ù:m=7:$/ ðÜU’áãèÞÓ$¾‰E£»ßðfT½§Îð14¿Y|K¤ûôì’ûº*Ô(*·5m;¹­e®6u©{RwÊ/˜ßÄî#¸|¸±¥Ð“ÌFbÙ._ò"0%ÞíËÏÁëØWvР—A»÷%€ê_ÂÉ8éªÏN³Ó\)¿g Ûð1Û4E뜂`ÍGˆŽ=´·ho ¿­‘!oŠC ˆl…÷Ä¡¥Mª¢ÖJcüý°w]¦›áNžµÌà¸\õ©V;Õ.uàR§hŽE`lµü>Q;I-ü@ÃYñØ EÉ E+Ÿ¶ˆÇ>J>pÚ¤v˜Ë0¿a•x­†ŸŸªâvrˆ™4nr´5F§Ñeôÿ“ amHÒÕâ^=ĕͦ6Ý"ýي׸w9!o†t˜‚ì×àÊ£E‘ÍÏ×õϱo”)ÞÝV±­vÛ^éÓ3š|ט’1>÷x†7>ø$Ýlç&{Tñg—)^»ÜÇÑåbH ýÕ'ÁÕScžx~"ëíÍ®f;ÏÖ`ŸÈ‹4$¿eVX{!¶%+-Œ×Ì´cQt÷Àn]WƒUR[C/¡|è³TÇg©ªf©<—4Ín^QÈ+ x¸[9;†[XxZÜ"ë»çΠ«„2ì¥Dcx/ò½öê«Á]-°_;Þðž®>B»#B[7.q„åaèòVxxõ\H’îÕš@ûéÀ ÊGcrױ͞›y²çšSEÂGEBùÑ ïÓÍ Q¹o™®z™îˆùböW£U:¶šúMܶ“^]/(Ž}xÒªIšAªóÙ}It^|ÁæááÁ·kÇ{ì½®ø=±åˆ–ä*¸I;Úï#õ:Eû2ÕÉeª–etk4ë• lSSà» šíx.óvô´ÜQÊÏ^\.‘‚qܱYšª%Úª‰:{n‘Λc}ýYž¥Í5ŒpIÁýwj9<µv±Ñ\»º:|_*N [´!WĽIqéc¿ªý¾¥W‡9NѬ5Þ,×Eƒãš¡IŠ 9víš±}݇±É¸“½s^ÖÌáI÷žØõÄ/™iÔjص›Ga#_ºSxôE:5!°«LSu>°*Ag÷3ÀÔ10uO™Æ‘è@Öé`Ï©Ï.²‡h·¯ r„h—bÅS`Ç)Ÿì˜ì/Ý[+Mõ+S&éGÆ<P%üp§0¾êŽÇnËûó«‰Eôf\ŒzXå¯fwȹö ߟ'ó°œi?((}žÚ« c²Ôh]{Zý¥]¬ áÛBxû…a%!|YŸÂÓÉòSŠYŠFrˆ$'דFr„ ù*YöU|ÉŒ²ù3<:õì—²Ê?¿’ZçˆÃ¾tVñáäòɧ'ïœüæd'ö¿Xi¾Qú¿ˆìñ]¢c—xh—XX'û¢¨gë©N]õ1Ï£*)| ä/šÆMÕmU„®ê¤¯ý¦Ž>VåÏú±ò‚þ¼Ý;úèvÈ®VPõšhÅ;‰y‰0 ˆw‰¶ñ_š+ÌÅau Ö<óÏp½AŽ[Aô¶·vº"òîÇ&_Þïð0½ðÌï9y~[é3r‘esÙAÙÜë¯ ß“Љï8Ãר›‡ Ý["~þ6Äâã†üYÃ[dž4¼ûD ›Åª"ôIºyì‘^G<5âBdìÛþìbÖ'Rÿ¦&š=´­x›ý}‰¼ý°/&÷]âÞÃxªþþÏçÓE\íŽNÑTNÑÔ.rÌÞ¾×½õž d'zf=ˆ}#µ(Õ‘:4À›¿‡öžR7/>´SÞLƒD Û)Oò™ú¡ç~¢Š‚“âKE§X%V‹‹¶‹¥bYªÆnÄÆÜ1)¤\¥îV©ªÔ;óL öë“ò¤ˆëŸÅµÒ¸Ø¨x¥öJÀ¶® ËƒüÎËÁ5S(âj¹íx¹¹²É•“Ë&—r?uL²q³< /î/õ|tà5Ò•­¼ýK+D¶r~ôÃZƒƒ¢ :_g«ØÝ3*gxž;t{q»}QÙ¢í³‹ªØífH£\½ö°ð³!ýtõ«ÙOåÕÓyŒeÖìQ =v¬ÝÈý‡ mûùãûùüýüVùMç/ð Û@§O]~à¨hÿ8è{NÍ|Ï ³G9y7rÇÂ+Â_1ÆoÀue÷®È7;šo=•<» { ÏÒ1üD¦Ö°“ä ó<)j5Ëq÷½K¥´§ç¥ÛÓ®—ÝeO/Hç4±ta…üEÿª)xÇ”Ðó†ïˆ±£bdÑŰ¡áuÉÃ;ÓXþäñQ?éÕÞw#æøçCžÎUžÎ{NG…j:ß Ñ¿¼Ñ¿|£¥i侩#¶ï ®¾O÷ç@_;UÞŸÅìët‡öãÊH¯DØè*ÃëŒÕ±%Íű[?fË>f1PÜÒ<´½ó1RÎݤD¢#ú_íé?‚mÇè÷kh`¾±ö £†ÍRP'ÏS¼¢n¯ ùh°¡erI!;¹NÑ2QS1Qóòõ×馄s„¼iˆ {P¸ÿÉljœïÙLÕ¬M.yagEÀ¶)N^“ÏË‹ ñõk9¼^{­(pÿjõæÕê¼ÕjiŠ~wT½ì”¡Ü0ݯŠ]±1æœQ…×»{e§vøDÜs÷³¬¡%áñÓõAa/·Á~ïçó‡l,• Ù\ǰv†ÝÆ°Ž•ù+©Î‰X¼ŸÉk÷{ ûØ9ÓçcãÅÖ[Ã}þ»ÙßàÈ¡€;.*Ë”ÿªü! G(³UpŒ),¸cÌ$1Ÿ¦Ë}ßû±¹4æá'Så½HýÙ«twù~­·Ð³OÝòìSçÙãS‡áì¤9Ÿ>àÙŸ×mxë¦Ö3Ê9 y˜HzƒçÚö¸0ß³c<ÌÎ鎡÷m†/sôy9ú:fXE¢´K}AyvvÞìÂÙµò±zvS‹x‰{ÐÍN¨ciNô Æ»|“PÄžXÔÀNe¦Ï)õ8Ê[|ÔS\À¾ò•bùºÆU?$O·|pìžážQ„Â3†î@<®ÁîŸ#TO*¦ uB)]^E{üõÇFÁ>G°Oì—ÄgwÓrOTd)ä°X¢°‰_ŽÁÝö1…crÇ<L]:ï {UnîßĞΠ8"[ç¼c<]`KXå4ÌÕºÀšæ «Ý¯*د:¸_uç3‰ozyö&‘ âdû®)víªJc¡w½5½e½¸»ûÄòu¯!|ìñÀY¶eŽP1G¨™.TÞq‚€'e}¦úº—÷9Ë«ÆÏ£æ³ÆõŽ£»ÍVFSuMÌ—ßt×ðKÒ½óÖD÷ÃçÎù¤hÕy®ã‚oùßš³êSgÕgÕíBýšaUcžD ä ›ñ’È»£c?Ô³E¬v´þ=\T_© *œ`?P°&`§ðønoH{æ'Á3?t~xEY„Æ!ߢç2lÅʼ•ÒQ]–-Â;¬ ¬"̎㶯ÈYé0þÊØ‡?D,¬SÐ.Õ~ŠvAQ{Tò¿pžÄ:'±;'±ö£jÇ$¶ô¨zÿ$6»u»]ΔHqŒá/QMSƒA¡Ž)ÃªŽ…#×î+¾e,Ž0•Í1•m4•í7•kôb¶öí‘î´âQjt |(P´-ñfÄðƒ‡ôŽCzWEÿÎþü~Ï®9>˜$È»æMþò¥Â/|0¡ s|ácËøòªøéUñ•«¢gJƒÙv³v)~9'ȽL³c™fwQpƒ<ýUw§¦97õå‹°z1.q~Žýâùýâ¶ýbù®à g¿Âå]á6_áèg·xþÊóÑ×½jÔžK®?—–+_äÃŒ£——KÆ)¶S”PY¡¨Opô Õ}¥!Ûstù9::“ßÉEsòDjäÃMÅ¥¦á•ËBÜâvÜâjd“ªëwzLò<ßq&vå]ñl} —WÃm“5\ƒ*¨¼hØÎš=#4žÝKÏ.óì^ Š÷yU¯:Ì«ŽÕ( jöEq™ïÖ…£Ì·pIH^bs‚–úv(÷…'Âôòᬢõã°“Yê¢,µ#K½ë’ÖqI[óD5Ï>ÉxzºÊvMUÕMU˜ªÚcR˜vzÒm5¡']á’ay&Åf“‚n—í“y“8:ƱãØ²q¬]~ÈïrÏé$Eü^빡E4ãQ&Ç`×ê ´úcgC÷\S”\S4^SÔ_Sœ¸¦h¹¦ø¤Î¯¤Î¯n\Xõ5û5úÑ·á/Æã§õ[7•n*ÛäÄ«aSÒCl*›j65oª‰uí=­‡‰~*§ŸªÌOe»(xwÏôé»Òwë/zq ü0íãä~?×>)hÛyMÅzmÑzíöÎÞÂU´p-ܤw”œ×Ôhµ%Z­Dºá\½AáÇõL0Vn¼Ïqiä‘%>EK|K|ªk†ç¯¬>?bg‚O¾üFÀNUíŸëÄË1·bîÖ:± N,?©qà½$ |„ªx„ªl„ªÊè4ÖKEFšª1jÏ=€+ò, µ|X¦x}z s™²<+xÇ2¥c™rû2eñFUÝy ¤´)´|}hɪ¨ux–[,¼>U(™*”áx`ëªö%Þ8pô@ÑÂ¥ÈÉ=à8P3Upi s´Å9ÚÂ"­'fÂ8?þ)9häÃ'\g´O^ËÈâhŸ#Ñ>-êÃêíê=}>g o_ÂW/áw/ᇖÿ:yÇç(\ »¿²°rOåöJÇ!¶°ŽË©ãò*í•y•ŽçíuÛÛu[/êíºâ]Ãòž§Ï O9“|wÈ7ù6úœÚès|£Oùµκ°–“a¯­ ©6é&}éD}ùDûDÿ‰zÜpj}޼¿ëýü÷ ÆiÊÞ¯~¿ò}çûŽqšÂ>6¿}'+ȹ" |¦qæJ¦¬=ÚÇiûØ’>¶ê}ú+s-ülùS ù°Ž¿º~ÄÁ/ôE_èk¾ OÛý…°‰/„½‹5%‹5»"”û"”û#”eJÏ„éÙ5‚lw‚ðۢǼÀüCbÞ!±aI`Mà¨ÔhÊj´%7ì5B]°ý¸ë´6ï4\¯ yO½'/À­*_ÁÖ¬` V°;î=&D«Ê¢U¶¨/W°eŸ:.p;.p».p»å÷¾dÿGr…Eòȇ…B×U¾já}¯òåWù#Wù–óþ‡¯òuëGœ÷¯éÓñ9E¼½ˆ¯)âwñôrÞ‡±Ü->‘®ùp”ï½5Òr_ÕÑu»B+M†n^—Ïëª'ªòå÷ž‚Ð= Êü¡·çj¤a×ðrüÌá÷,Ï[^°XQ@‡–;–&•!+wyárçò’å…‹…«…’%‚c‰°µI³=H‘'¿eŸjã¸Kª{¢ñÊÿåB.x!×½kiòùÍBnûB®YNÛ›|šû†3B#Ø¡šêaÈ yJYÅDå¾µyk+N³k N³‡×î:Í–&(vÓ¼µ…kkk׺N³… ŠÚKBÑ%akf{"O~)ºäY ¿—ŽlµP@‡0œÒÅ ç=#+Æ ¿qpU7=6]T ý'(`S‚âëlÚ{šÝ!›U÷5f½;:õÃ0ÁO™xϬɇ‹Â…ýÆŠ,cáBµ½ý¾Ý ÕoÜ =äçò+,qp¡úH®¤NWÓÎï‘ß í|Y;/ó)yöªºõªºåªºi¢²d¢²îšþì®d ×8…«ŸÂ½>…k™ÂÝš"–Lë.úUçˆ9¢mžâKC]kIkck}ë©Ö–Öç…’óBÝjmu² N^$AOhØõ¼*‹d´Ž+©ãÊë¸êM^ [\ )¬a jØ’6O~ã´¼²ð[pˆ-9ÄæÉoœºžwä©¶å©pÜ÷ü¶ç±yŒóQÝ··ÂðÊ-uë-uË­»F¿eâJL\“‰«7qgL\‹éïŒ^£÷þ#£éÖÊÎSÉ›‚Ÿêp——Å\ ²Ÿ××Öùo?©(<©¨8©ØqR‘RñÞ”‘΂°†Åaµ'Û¯qy׸Íò‰’«A¹yi»¦¤]cÏâj§ëK¦ë½¯Ø'ÆOÛq7ØïÚÅ·Ö)óÖ)·ÍòÏkWâ]sÒՙy™5gòÎg–œ±#Ý ò=’YŒda¦óLù™òÌÚyJÇ<å¶&ÝîCŠ‚ ½#ZÌ‹·RäR”) ú)‡:¿òMŸüæÎwÜÓùOç[ÿÅΟ@çFq¡*~Zí×u>Iõ^–o^–ï¶„°¼Õ¾x×…½r4¤zŸ7‹ß½ZÌ[-¬,Y-Ú?Væ}¬<”âþXY†ÓY|ñ,¾j±_9Þ³øÚ>_GŸï¶/ »'¨ ®„–ŒóËç·u‚:‚ºô–¯c]XÁ-_‰tGIáÂ:Õ.y>½{˜ úø‚ѹиe×}“î+ZÏç­çw^ò©•M—|ÞXÏo]Ï%øå%ø¼¤*¸¤*›nغØP¸ÆwIåšÄMâq,]æïXæï®ð)ªðq˜ŠM¥üÈ’ Ÿm(šÄï¡¶P;¾^ž3 ?žQót.ùh(Ýa äŒe4[FÓaПf ò"iˆD†$©é¯½døÓ/}u[a†~=*n¦màC·Ž£§ãpJ§RO}¥èHiFðÑ ÑÐoõEº.tôú;/ˆ[èšè/ä¨ô§¿qÒÓƒì/9¸ÆÿŒ¯¿z¸ü3ªícóÕ©Ë ‹AU‘¤c‡§ù¹ÑD‘„Äüâ ƒZãqçŸïqí1ošºÜ"§=_ÉýY(Ï~ò‡æjÏɺ¡“ Üaž÷|,x!2ðm;Iþ]œiÖØÕž'=2Þž£±ª×ðòæ¹ãhòT£ë}$;Aþ Bãý|Ùó¥­H{ ãVó÷܈›Ù#zõƒ–„ÈRÓœˆÈÑk,/6-H|!1ò…Q‰á‰s­Öå½ÆÒˆçZoÇ”š“æG¾t´—ô6ûh»e„¡±3K‘8om’y®©Ø|¼§'iþè¹áçHxþ˜h.[h¢µJMóÃÛæçzm‰¦ÄQ×)æ,ÛÎÈnË\ÓÑĈ9æ3zó-+» qôiK»²ysN–­uÓ®‰1óçÎ1uµmʲ*´¼Ä”4Tv‘霾×R»/)2‚í3®O·gÙŠbO /S›‰-–Í›d¬ÄÙT¶<1UÈìÊR´[¾?/¨ì…]=Ž”ðᛎ–˜KͶ£Š,_ø>"@ÕÑsz[3”¼Ðæ.6ÃU}Æ?ù´µ§,Žiìêé#‡Øö”2†Ö.†íÓ8mùÉDl½¬?gì5±ÈËÞd+·¢§>²ïàöåÜÆâÈ-×Iñ¨âp[ëK%/E`äóç®)N2W&†'%šúȣݖÊRMÙ%—IkiBäeýÁ¤ùá}Æ–ËúÞX¿ Úm}Æ=0-ü¥M¶ÆMY ¦Î©ìš²ÄÔQY¶£¶ÒÌ®¶>ý£í–“é}ú Xw\Á¶Ã=tüóLíô<‚–—z3oTìC¿l.^^:ºßR:ªÈ\<º·§×‘h0eïÄ´˜“G/HœpÎX»©ÄÔb½ž”`îs±çô®všº-˜Hw¯¾ÏÈp—c÷ädµ"-'%“Åf¹Ú--6Ìn;—Äð¡imjOùc¨NJ?P×k™;*)²7½("É”Yú 5Õ¼eïmý–5ýÖ¤‹Ÿ›g.‰LZ›}<éÅù¦—Ž'>”eB4Z‹“–%ÎÁÑ4p±52qNÄ‚ùæÍ1'&DfÙ^-MŒLc.KeÛµ`>GL•‰‘mŽˆ3úå}zΞ!a^b²h¹1{Ï‚ùsÌóÑz>Z%¥Î]ÓebÃKæÎ±v¹S"/“Þ˜vò]ÓeýucŸþfÜ9 „c° hº¾^Æ!®‘†ò+XX‰‘íS{ÊBcãæì—²Ûý¦Ó Ë>·€vC=¸¡Ê+ûŒ{x !1qà#"p ¯^Œa/YÙDÓÅ©¬`¥±aÆT\7b¹Ï[‹$/•RXU°5_ž®‚ðv:}²k[é(•×ÖÏ “—[*Ý âªå˜š7Š”]‰)fzLÕNûGÜ…Ä•z¦ly'ZÍ3{Öh„¼ì0y–ÎËT!Úx2ÍžŽt?¸%#Kײ‰FÇ(rfºú^¡ÛÈËV\Ž °6ÂðTJùbhÎå4ŽAÏ1Ó ÉZ¸)KRB$-}«Øb:g쳲Ծ驙d’cÍ,Ç•êFœ2¬€ð<­ÀZ‚–ù²l%ítg1ËÛÁ.ªSv)f·=ýÉy|“\j¢†U{"tVµÔD¯‰µÓåáIO”w=î0aaáÉ[SÒt¹ÀÓ§,èUe~ µæœñ9yy$yÖÊxæª";kËâC8ÏÖá ÜQ´Ýüyf¹¯„XY§§ z•ð(§çò7×äÙÝlÏ3¶ŸV°Ù±^ãÝ×½íR½“–ËW¢×˜jø asbä>è8.ô<êI U†sN³ì–œì¬;Õ·ÄÜI=x§4òB‚pCZ|î&‡ßM>r7ù³¡äËë\¹ógã<àÏu˜#"Ò<ù¿ÿ?û‹”ÿ±öÊ(¯ŒöÊ1^y¿WþÂóOÈlAœ·žWÆóÞú^¹Â+Wz¥Õ+Mž€f ÷ÊQ^iöʯŒòÊq^9Þ+c¼òA¯œè•“¼r˜Ò#‡{å¯4yå(¯´xe’WÎôÊY^9Û+ŸðJ¥Ê#E¯ÔyeˆÖ#{åÓ^ùŒW¦{å¼òY¯\ê•˼r¹W>ç•^¹Â+Wzå½òG^¹Î+_ôÊŸxe¦W®÷ÊŸzåϼrƒWnôÊŸ{å/¼Òæ•Y^¹É+³½2Ç+7{å¯lôüó4[ª¯GÎ÷ʧ¼r‘WFúyd”WrþÉ{åÿÃÞ€GQ$ ïééÎ}M..Ñ ‚ Ün¢„3áH€@.r£DE9AÅ ¬¸âŠ ,¸ €ÂŠ]Ö×ÝõŸd¦Ó“ ®Ïç÷¾¾É/ÕU]]]]]]]ÝFÌ‹æcNÃ",Á§pž«)ü Ïãü¿Ák ×bÏÚÂ^¸ón¢^p9®ÄÓ¸*Fø¤èBg¿†kŨ›½_ÆXŠ›ñU| _Ç7pÒ- œŒS0³0s0§bæcj ŒI˜ŒCp(Ãá8Gâ(LÁïñ6l(¼›`·FÂÑX§±0ûâ|¬{»ðI|?C{aý¦Â~8ßÄvÍ„0ÏàÎÂw±FaM¬…·`ClŒ·cSl†±ØïÀÞxöÅ$ŠÏ8ó¥±)6Ƕ˜‚£q)>ÂUø>/àj\ƒkq=¾Œ±°­p:á,,Æ{±ßiGþí…;P/8ÓpÎÂÙx?.ĈŽÂ›ñ܆'ñÎìD9Qí,ŒÂñ\‹/a×®ÂîØá`LÂ!8Gâ(cq¦a:NÀ‰˜Ù˜ƒ¹8óñÜŒoâÛ¸¿Äkø Jwm(£TÑC±¶ÇÎØ ã±öƼ ±öÇ8—àãø >‹ûp?ÃÜnÂÙxÄŸñ6îÁ½¸A©;õ‡÷â|¢£‡pNÄÉ8Ã¥¸_–=…­± Æb[l‡±ÆaøîÂ÷ð~ßàE¼ŒWñ'üÿ…×00^‚¡†í°=vÀ8ìŽ=0{c€ƒp0&a2Á¡8 ‡ã‰£0Gã‹©8Çc>ˆáø4.îM½á­ ÂFØ›c l‰­°5¶ÁXl‹í°=vÀŽØ mw e´£‚*ú /ú¡?` a0†¡Ã1#1 £±ÖÄZXëà ¸_ĵ¸×ãKø2nÀø –â&ÜŒ;q¾‹ïán܃{ñï¸÷ãûø<€â!<ŒGð#üâ1ü'~‚ÇñSü ?Ç“øžÂÓx¿Ä³x¿Âóx¿ÆKø-~‡ßãeüÄ+xŸñ_x ¥»„6”ÑŽ ªèƒ¾è‡þ€„7âMƒõðf¬·`lˆ·b#lŒ·agì‚]ñì†qØ{`OŒÇ^Øû`NÆ)˜‰Y˜9˜‹S1ó± qáLœ…wc1Þƒ÷âl,Áûð~|¹8çã|â"|ã|ÃWq ¾†Á×q+¾Å7q¾…Ãíx?Ç“øžÂÓx¿Ä³x¿Âóxïì+¼ ûb"öÃþ8â ŒI˜ŒCp8ŽÀ‘8 Sp4ŽÁ±˜Šãp<¦a:vLvÂÎØ»âØ ã°;öÀž½ðÄðC<„‡ñ~„ãQ<†ÿÄOpB?áDœ„8§`&fa6æ`.Nňu&oÄcpŽÇ_& wM¾‹ïánü¯àUü ÛM¶ÇØÆ…¸ÁÁ™ÂdŽ#1à2|#³„µ°Þ„1x ~„ßad¶ðflˆ½ñ.ÿÄK(ç}±öÁt| ÿ‚¶\ÖÃfØŸÂ-X†ç0lª0{âC¸ŸÄWñ¯xãÏ'ŒÃþ8“qæbnÇݸ¿À3(å °?Åk¨ƒ0—ãŒBá|\ŽÛp/^ÀžÓ„Ëði\»ñ^ÆÎÓ…}pã=x?ÎÁñ9TgØ {áfü?Ç/ñ~‡5Š„ °1ÞŽƒp,¦á\Žëq#–âEüÎÞqÏâK¸âyü7̆cÆ`3숽p¾€ëñ%Ü€ñ+ü¯¡ÿÝBÖÅn8 ³q&>ˆsq>.ÀÇq>‡«ñÏø7Ü…ñc<Чñ^Æ1¨XXëá|<ð2þˆá÷oÀ&ØS0Çc.Àe¸ŸÅ×p'žÇÀ{…x#¶ÂŽØ»á8Gá$œŒ9˜‹%ø0.Äçðy|â/¨ýÛÇ~8Ó0ÂÇpîÁƒx¯á/¨–Xc°9Æa æc)¾ƒûð~Чñ~‹—ñ úß'l° Æc"öÇ!˜‚i˜ƒ÷ãC8Åq=nÄ×p7~„ÇðüÂcèý¬M±¶ÁŽ˜„᣸×`)¾ŠŸà <‹Ak`¬Í0§áL|ŸÄå¸÷â>ÜïãAü?ÄCxàGø1ÃO° Oãy ™# ÆØïÆïðþ‚m¶ÇŽØ»b7ìŽ=±ŽÄ,\€ëpîÅÓh{H¨bCl†-°'>ˆsñOXŠoáÛø!~ŒŸažÀ“x ¿Ä³x¿Áoñ{¬1WXc,vÆnØã1ûãB܈¥¸wâ§øÖš'¬‡]°'ŽÀÑø.Ágñy\ëpþßÃàùÂP¼Ûa<&àh“q fb6æb>`!ÎÄb¼ŸÃÕ¸wà~<ˆâü¿À›°?؇âHœŽãf| ·ã!¬ý°ðv쉃pÎÇ·ñ~Šex¿Áæ …Ãq$fc.ÂçqÆð,ÖX$l‡¹x>ˆ p žÆóø=þ€ûb?LÁiøÎÅeø ®Æ5ø"®Ã—p#¾‚¥¸_Ç7ð8–á7ø3Ê‹…>è‡Q8 p>ˆóñÜŠŸáIü / m‰°Æcfá,¼ãR\Ž+ð\‰Ïáj\ƒ/âËø nÂCøžÆoñ*^Ã_P~T€Ax'NÁ,,Æ|Kñ~òcBôà ŒÄh¼ 31‹ñ |×âV| wá»øîÁ¿ãûø<€‡ñc<†×Pz\Šuðflˆðvl±˜ã¸×ãN<Š¿ m©ÐÛcWì†É8SpÿBùOBUŒÃᘃËq#–â><†_àyŒx’râ»xÇ2aÌ«xÃSÂþ8áÌÇ3xÏá„§…Âeø®Âçñ\/âZ|7â+ønÇØg¹p.ÄEø¾‚Á XsûeèóŒ0[a| ŸÁ7ð†<+|WMÞ€-°/NÂÙø8¾Œoâ,Ë”'l‚}qÎÅX†ÿ |a ¶Áž˜„ø0¾Œð<ÞR l}1 ï.p¯§RSxáOL˃ ÝÃ}LṄ—›–W÷Óþge¶þŸ ”ÙTÃnþ±AÂұ²ñ,O:?ká󅯯¶_ãžÎoǬs_Þžð¼ò’pí˰ ÂF›YŽÒŸÝóYù÷ð—[Yï áb”þÊ~ýÍ=½TÇÇ=̧fíQnl„¹ù~†žâç‰øS…1˜›+”peNÕùxZæe½Ø»aivÕë ð1ËË~{‰oá%þ@fÕñ¹^âüėN©:^ò¿rrÕñݼėeT?ÛK|Œ—øí“ªŽë%ÞS»‘X¯ÛDÚí/íÈK¼ä%~{º—và%>ÌK|iÚ¯«‡¬wq¼p1¶Àã8Ï1 KS…R½œ_c½œ_^â[x‰?0ÆK½z‰ó_:ÚK»ð1ÅËþ{‰oá-ÞÃq]9J¬W6ÂËùé%>ÆKüöá^ÎO/ñ’—ø•üôO^âˆzÙ/ñ1^â·ùuÇçb²Xo6^ôëòYÌz-ðÀ@ÎW ÃÒœ¯x±?ç;¶Àý¸¾cn"ýc_Òá»ÈgßùëÊÃzÛ(WƸ²7ý4æöÂø_·½²ž¿òzáe½•=¼´S/ñeݽ´Sâõ[H©üãé÷ßbÙoñ¹è!ßë]þGI÷ßÏ?¿íg{˜/çµû碇åÿýüÿþ p¸÷Ù¦ðJSø¢—°îna oŽÜÂ{MáÏMá\Óú+Má¦ðO¦õÃ#ÝÃR„{úÛLñÝMá¦pž)<ß~Î~ÃþÀ>g ËQîáÚ¦p+SøNSxœ)<Ó~Ô^o ¿m 3…¿5…¢ÝÃ7›ÂÎÏÜB±¼Žö*eHÙRš”#M—ò¥fR²” ý&¥JYR¦ö[œ”«ýËÔR×–hæHÙò«Qžß )IŠ‘úkk¦Ky.ëÅh¿çk?“ §xÈçVí› ¥ž¦¥Ò·œf‘ÓSŠ8-¿"mÉx©‡Ÿe”9ÝKyëi_=u¦‘v¼K)ÇiùŽ×ʧïËD-¬o#_‹Ñ¾9Ú?}IªVž‰Úz•óÕÿ,B¼4CË!]+ÇËmtwÛB_òO2rïk‘·kþúŸ3G-]ša }ÓŒ#æÜšž_‘Çv ç§§Ðë·È"¾ÇCl/F˱Ш‹i‚qDkêõ¦Çé)ó-òi@>ùZš Ú?½„ZžÙFnùZ]¥u&j9Fû=G[VÄoæ´Î?QË;ÓHÓÔ¨ƒ,#ÿüòzÈ4•8[[.¶^h´ô-¦@['Í"ÿí{—ç©Dz.z›k桞£MíBäeÕ:ùx:^QÚwˆ±/…FmU·,ÎõkšÊQ‘WõÊâÌGÿÏ›ši[êeÔZŒV׺z‰ôÖ9Áe¹œ³M´˜iFKÏ0ê|’ö³@+AG©¹öo¢–¦™–^oyÚïùFûÒ÷`¼qF7×BÓ31[;ošKwgþ{‚v´ºHm¥VRk©Ô^ŠõPoÆ~gk{š¥m=&™?Îô¡¦zr®ïaMçzanÛéG ¬ýLµh]ëEzØžÈÁûúúŒu6~W?BúY1Á8ïÒ¤NÔC¡qž¸÷Ô9½w–é¬Ñû}K…F©<µûö«—–[†QÖ4­ êÛʯtn›ÛmO-E{§_«*_ôž³óÞºÝêçá`c{¬7Óe/+Jj}õ¨ÈGÿ³æë§õõ2Ѩ÷tãŠ:H3ßè'óŒíèkõàÌ©ÛÃX¿À¨isà,‡þg\ë²¢ÿLu¹jÅh5•lœ/ÙFi=ïWö¤¥Ñk6ÃØg-¥[¦·kß)F_fŸþg–â´r¥•_Oӌ޶¢–Ò-û©(S»qÍ£g¥*¯¯¿æÜC‹Í.o¯ãÚÔë$O[š½Ê¯gDz™v4ÓsQ,MÕ–ô×Z Þž2µ©F+,0¶‘¡]Źq«ÑSƘ¶£Õs¡q|Å•*ÆÈÑ}ÍŠæsíJw9+ïQª¶f¦ÅÙXQ/3·zé­¥ÊøÃ×ÇÜß­>òækW¾T®dµ‘`ºžf¬—÷¬+g=Ýû»ÕS’V#zYSËÏ÷?^­TÔ˜߭^œWƒþuîÓFéÚx¶§ös‚3•×Óççâ|w3âN0θßÉ0æ Äv=çåš_-ùÅ3#ãh•Þ˥σô5îÅLPí·ñ×QŽ’ÿ¸T÷z©÷ •®š´–⬗û·zª-'õ1úèÍ ãÎ*ý8†ø¿©Ÿ8cE_«b>QÌ(ÿ‘ëGÿsѽ˜I×~ëtúÛµÅn3Çîñúýü,©…ÔÑC¼>NK ½3EUäÓÒc>¾F9ô-é?[U3]ëj¦kSÍt±ÕL×¶šéÚU3]ûj¦ëPåq¨HײÒó”Î|D<¥3OéÌGÄS:óñ”Î|D<¥3×ñW±År}þ7Aê¯]¥zmWÌnºá\ÓëÞÙ<Ÿ8ÆãŒÁåÏ/ôI’óNv‡/R›ÏH×çE•·ãºVÅÓç|XºñŒ¥‡qÇ’j”Â|þ6û1Øe.ÒÛ~4½®íÄ•?…q“äk1Œþ1O;VúZâIFuëÍÓö\K_½zÓŸ'õ×úÜÉ’˜oîi<»È5®šb¤äz5`äì|êQù‰eåüCòç3ûb¶#‹þ{sÖåê`±ßýkŠ^_ƒŒ2M4FcΧ±ÆÜiª1§­—ʵ.3³›h 1"Ôç¤úi߉”šå}”~ibºWq×ÿ µ÷縮uØÃ˜ ¥·*£¾ûäZoîûg]ž¸ÿ³òèÏuG¿åxhg•Û±u¾®¥s¶’þZìPcda=ÞµŠó¤ÀÈO<‡H(nîL%æÞ´-Åi¥H2j£€2TlQ#é9U^_ïm{l]þWÊ嬕ªJ•àV*gyÚZ–ǵp機ÖÇG?Ò=Üf_ô%ýŒ¬—Öw»¦õT/úujœEkó”^¿O¬ºŸroA™å1ÖùÕ³¨ëþŵ>¹fTÑ_èÏ•D­%•{›i¥Èô0¢ÏSˆ«}Œv Ó¯øÅZÙô'S½+]WÝgÖõ%=ÖWÍò|«Ÿ›U>ú<’è/+·1Qïý\úÏAÜ-g#±^ÅýˆUþ!’äÒÊzhµŸh<=Í3ús/çþ>Ç4‹T×rW=Þèk,¨-Ÿt#×ñk}î †i£ÈbI²Lç#IU¤rO—¯m±¾åÝ„õø0ÆâŽÂSºêŒ_c,î(œéZKÕ÷ÕÏL}†/›;o}|"žW¼½”jœSâ ¥×póž¯kVóy½³|œTùùv3c™R:¥óÜŽ®7ß¾ô …•f^ÝómxùêçgŽ–cžÑö&µ6ÈTý|^¿ÞÏÒþÅ?¹óqÏDîpD¿RÌ1ÖÓµ"]+Òµ"]kSºÖ¤kCºÖ¤‹-OWlyòôu”K„[»•GÜIU.O+SyZ¹”G„E>mËÃ"Ÿvö«½i¿:”‡[S¾åKÚ°Ä\—mÈ«eyéÛ”ï]ëò%ÎüÚ”/qæç©.]ëSÿö¶XI=÷“œo"eºÔsO·co•oÖæ²Öp—#3Âå¨ u;"Öålüž§®s.â ž<Ë÷ìê[lÇýÙQåç«îËôk¹ùé†ûýäo“³cÖ××ÚÛO*Þ—qþVç¿Çû:‰F(ú?Oýµ~]ɬ¢¯t¦kiYÏâ}«<®zYòŒÑT[é9FßSë;yçvcÿãí:k§”jŒ¢ôùÔ‰ÆÒ cL‘eŒŒ²Œº«¼ýö¿ÃöÅÓ£iÆ] §öó«ÚËI¼Ùëù¸êÿÛ¯Êã¯AÆúãÅÆï“*Ý—5ŽaQwΙšþZ™ç1ôÿ½S?c;ÓØŠ8îéÜ_1îÒ¬ÞšýOfùÛÐî³zý‰ó!¿<÷ŠqŒ~7Áín×}¼t½÷á¢$Iƾç3c?ɨõ£¤…ƨØ9ƒU ™ß 3׿÷í'ãôFÌxFôûWy ý¶õöÚUk{æ' Õ=*"çœj¼ŸßArÎ&3Ÿ˜g´ÑCW¼¹o¾ŸÕßHÏ1Ò¸ñDÜõ^AŸ‰p½´ž÷j&Iåﴚߺö¶÷{Yg.úÝÉ”òç#æs»-®s»æ=¹Þm;·ÛÊâ|·¾ŸGǵ$Îü³ÝzO5í¾ý˜òãn•³Þ§ë‹s©@¶1z^Þù©ÚõéÜ–˜Qö4âZÞêå[1¥×§œù6¯öq0ϲå3c>3<Ï‘ºn·^µ÷Ç9wÕËè]³<ì‘ëýYåëHÕý–Ø«§óîïWy.o²1?çÞê«.gLµë½ó ýŒö(j·Ÿ±–U9{üÊvâ:úÑÓº–>Á(‘8 æ'ÎÜô3¦—q^äoiyšGó^Îñpõê1Úòx›¯,z‰EééøFXä㼂ñø”çzök:õ]½ý²jÇžÛÇD­î3ȵêyQÅÈ·Ð!ZÅû»Ä;ÿ;•ñéôïmêÑõIbQæ^9Fjä¡|ú·‰‡å®³—‚äòû:‰x×ϲgÅß½2Ö½d½|í’Ûò•eÜ×½à¾|@á8÷åsOˆðëmå÷3®Ÿ¸§Ü·›û¹dYççìj÷ôÃNV~e÷øn_0¯»Öz½•­Ü—7:Åx`sõÒ‡á¶Nÿº)ýÒç¾ê!SùËΈðâ-ò7¥ßý¥—¾f~Xk÷å¥gEøÀ_¬Ó0åÿä9¾øºuú/Még%ÂaoX§?»Êºzú8–»§ŸtÞ:ßšÄòÝ·šÚó7U·Ÿ+¥îñ¯]uz¿Ú"~ƱªÓ9?æò̽è6ç1ówåñêåÿ$åYûiÕé[Ü.âçbégÕËßÜ?ä^¢=ì´^ñ îˇ}K{x×:ýZSúnßq>¾g~»©<¾ç|Üí!½)ÿ°Ëœ{¬ÓlÊÿ éïõÞ”ÙÔïß­Ó_4å¿ûGÎÇ}Ò›ò/½Býï·Nï×Ítþ^¥þÿá!}]ÓùûõÀCû0õÿ“~¶N7 X\§zùŠpTwí2½XzG:'Õ°5±u²M±=b{ÓvÒöo[„œ O–ɯȟËvû-öxûxû2û&ûû?íWíõ•ÁÊte¥òŽ"툎Ü_§þªèæ[¢[¯Šl{9ºoQäá£â¢ò¢êl)©¹ežcç&Ç ¢|·F…,s86EG¥‡Gg…×>q£#¢^qDZ­ðyÂötˆÜ_t)ñB­YãKæDÍ[¾ó ãP¬ãëã5¾+аeEoªÑ_Ü!:9Ñ‘’ñˆêx´¨Öºm57oŠÞ¹.lÿÔ'·Eøì‰h®Ö´",iOhæ*Ç=…5æÔŠÜ°:âÕM‘on‰z'%tïñè‡C‡ŸÚå«ß8¢çå°Á§BïÛòÒÀo,ŒnÚ2<~iä‹Ã^Ž­q%5ÔÇê×%¤Ý–È.WCº&Gwï5!¥Æ¤a ~¨qÈC×k®†8þáÖ°ã ?\ š´,<|NHĪð›ýCÚ]JÜœ»-äžm!Ï̋ڸ:êÓ©¡u‚£êžhÞ4¼ýª¨ŽéÑ}O„ô¾.bÒê°¬©Á‹Á/Ö Û^7ò³UÁ§J‚š¶Œê¾ÚÑ]TRJøÄeAë2‚7&½y*bç’àÝs"ön >”taxpƒä »âƒS®ÅEÍŠ ™¼b›ãÈÕÀ/Ý:<âöý¦F¼<øŸþŽ3±_dž4º¥0°ááF…AÝ— ¿6Ët½ÇF<¾)9`{qÀ‡é¡Ç†}|zYè™DÇWëÂ\ mi ‘•1m[øì„ Eñ9Çn]ø]zp³9þm.µw„u¼ìV><*tÄ®€qu Ï:¸¾pÀé.a>'BZ•ܵ,|xpxA†cºÀLÿ{v9>±_Žþù‚ÄÒк%¡7¦wêß384ùjàˆBǨ„àü¥aÓvøÍ<öH\àó‡C^m´;1àØ²OãCë7îå7~S`QbØÌ³~÷,õ-è»/6ðØÙÀ²,ÿ s|Ûú7hàÛN î~Ð`-¿ÁWƇ¬»ä³58do‚ßÉB?¹±¿_VX­¾ ³üïLñÉݼpGІÕ!\ <²Ðÿx±oâÚuøuZ·Ä'ÛTÒ tA­ %Ký>9ë4ÐÇëÑÔïÆe¾MûtŸªN^t÷’’ºs·>|I]y4ðÍmoïQ÷¯ø0Öç³tŸË;ün:î“80$9>`ÖÑàÙñÁólœPËŽúV L,öMÚáwï® #-}?I÷=[䕨Ե÷ª§ô[å;ö„²èD@ãbµÕ2íàªùÁÏï÷y¡ƒÏº A»Ó•céöÏæ)7RÛG©].ûõ9«ÜÕÔXcŸQÛürJ|‹¶øÞwÕoI¢Yªý\ºï/µì…ö°©>—N ¸mN`SŸŽ :µT¶ùgµô)ªë;Ǧ.8a_eß|Jy}“”âk¿`‹ÚàwK¼ë?p Î~Û›ËüßÉò?§þ”ìóïy¾‘]ü¢×ùÔÞâŸtÁ6¦®íÞƒöÇ6ø<¿Ä¶¦¥íÏ[å+|wœð{{ÐIÕæ{Ö/2Ù72C­ÓTn¼Ç¯ó.ß1ié[}¦ÇÛî©kÛ^W=7O–dÅÇ/ ($,<2ºfínŒ¹ù–†nkÒ¬E«6mÛwìܵ[÷ž½úÜٷ߀AIC†5z츴 “&gfçæL›1óî{fß÷Àƒsç?¼hñ£?ñäSËŸYùÜ kÖ®ycéæW_{ý7ßÚþöÎwwïÝ÷þùøØ'Ÿ–}þÅé/Ïÿúâ·ßÿpå§ýÛ&«²¿,;ä(¹–\W®'7ËMå–r¬ÜAî"ÇÉñZWš(”“åárŠœ*§Ër–%Ÿ•/È—äËòUùšl³«v{°Ýa²×²×µ×³7°7¶7µ·´ÇÚ;Ø»Øã´®;ÁžhhO¶·§ØSíéö {–}ª½Ð^d/¶—ØçØçÙÚ—Ø—jü û*ûjû:û­«ßbßjßfßaߥuúûíí‡íGíÇí'ì§ìgíì—ì—µ Á5»MQ%Xq(QJ-¥®ROi 4Vš*-•X¥ƒÒE‰Sâ•%Q¨$+Õ%UIW2”,eªR¨)ÅJ‰2G™§,T–(K•eÊ e•²ZY§lP6)[”­Ê6e‡²KÙ£ìW*‡•£Êqå„rJ9«\P.)—•«Ê5Ŧªª¿¬:Ô(µ–ZW­§6P«MÕ–j¬ÚAí¢Æ©ñj‚š¨T“ÕájŠšª¦«j–:U-T‹ÔbµD£ÎSªKÔ¥ê2u…ºJ]­®S7¨›Ô-êVu›ºCÝ¥jç¦zP=¬U«'ÔSêYõ‚zI½¬^U¯©6ŸŠëëŒq}-áâkÓî@›h÷ëý¥±Ú]ì|iƒô‘tJRmõµKí@ÛDÛݶÇlkmïØÎØTù¹½ÖB²å§åÈçäÚöŽÚ±*±¿`ßmÿÚî«Dkµ9H«³EÊ­>>VÎ+WµŽÚBí¥ŽRsÕûÔGÕçÕWµ’~®þ †ø4òéå3Ò§Àg¡ÏzŸ}>ßúÔñíã›á»Èwƒïßï|kû5÷ëæ7Âošßã~›ý>ð;ïçç_Û?Öÿÿÿì½\K÷7žDQTPPÀ†‚Št!tB ½ˆH€Ñ@b VDĆÅŠŠ ± XPôZP¯W¬½^ÅŽ;êUÿ»Ù™Mv³!áyÞßûüÞÏÿÉý\‡™s¾sδ3gfgg½;&tœÙqKÇK?t4Ö¡í««=U{‘v™öígÚµé ;YwšÔin§ã.tzÒÉ´shç¬Îµv¬ã®3[g¹Î.Ó:÷tÞëèv ï"ê2¿Ku— ]®wùÖ¥_W뮞]Ù]ÇuØuV׃]¯tÕÒ5ֵХËÔeëNÔ©»F÷îeݧº¿t{w³êÆî&춬Ûþn7º}î¦Û}h÷€îÂî »oë~ªûÝî_ºw×ë­g¦g¥ç ç¢ç§7Y/Wo©Þz½zuz—ôõ^ë©é÷Ö÷ÑÏÔ_¦¿Gÿ²þ#}Zö={{ä÷ØÔãD?{¼í¡Ù³_ÏQ=C{òzæõ\Ú³¼ç¡ž§zÞíù¹gÇ^Ö½B{%õÊîµ¼Wy¯ú^·{½íÅ0èo`gànji°Ñà†¶áC{C_à †©† ë › u{›öÓ;¨w\oqïâÞ{zÿÑ»©÷ÛÞ_zwécÙÇ·¯OFŸ™}æ÷©ès©ÏÛ>Œ¬ŒŒ&å•Õ]2j4zjôÓ¨³ñ@cc_ãdã<ãÍÆuÆŒŸ3úšöÕ×·oXߤ¾¢¾E}·÷=Þ÷bßo}-ûMî7­_E¿Æ~=LFšÄšÌ3©5¹iòÆä‡‰©)Ë4Ê4Ût‡i½émÓǦoM5Ìt͆›9›y›%™Í6+6Ûkö‡ÙK3ÝþÃú;ôè/î¿¡ÿÙþ¯û÷`7 |À¬Ûœðl€îÀQ£fÜ<ðÒÀ—; ²4~ЬAƒ® ú8¨ß`×ÁüÁKü×àæ#Ì£Ìg˜o1¿`Nâ4$sÈÎ!χ ;tÞÐýComJ#üJ»2rèH8‡ŽÅ³ËÔ%ñ«»±þ"ãì7î÷‰w-œw 69ö¤E ÑÆð p‚ì΢ûA› ÔœÅUuyƸ»¨qC¾k¤ÎË [‘[¶gTo—rHó¸fš$ô¹M#àÂ6Ù¯:´¤Åg×§öw{:ø7\Þ"- _T_Œ_«€kùùþiwÍÙ>ô>Ðjþ\[iºgÞÃ5¨qœGÇVÄœxxpKq·Å¦-€4&г´Ãåhq«×°:üòÓóp¢xêÕë—|!ÍàZFËáFv‡òN?öš«û½cò·ÃHoXÊ7’FYŸÃ³ûˆE‘ ó?yÛæ‘iV@”/j*Qn§&#?c‡×Ú´ó ƒ: iæÇM­gíÓŒåe¬(Ö‰7 Šó=‚z=[è¹~•(Pãƒÿü³'Š^¦$Þ„4 ¯J<îJ·þ“=dîU.ôbl{å±¼ºž®¥/¯aÔaÕÑaZUµVe#Ïí„4 ¯à„ýˆ8׋嘬 ÜÄÝñÑζóh¼¿ÀöƒòH¸ŽúÑ Ç^ë@"N¯÷ŠÖ寠C«7Ï98§Áö83"nó…ï¾eA~¹U6'n›ì ´VsmB»‘ÛÏïmçCjþa|œ§×¼ÍÛÒZN¨@Þô)¿Þò©òºb–›¶ïŸÍJäm+xoc;8àèy±½ÖÒî†Ö¤w|Öõ®6T{­ý£¶øFD¨'¤5*Ám\×b”°Æž•óko}seì"Hk8Zê~móÏÐcûV¼v[8¨êíóy« ­^‰¼ò²1îüð9üOõ­7OÙ6V§7¹%~Ø„kæ‡>8û@wnGH«¸ zó,ªzä¾ò¸ëu—…‰] ­J‰¼;6›êvid­›á©;ð}?8Ð*Ž=€Z^ý×;~û/_ò=ùÛDŸ›¬WV p­ p¹Â\ÑÀð÷~Ç;öcßXi%JôœØ/=®ßâïž{ÓMb—žÇí|À•¤–÷ný©9O3òÝs_[zËÏÝ Ò ”ÈÓû™>ù½¥OÀ¢ã{“¯¹=ƒ´€óD-ïü¾´ ÇK‡#ùïâýüiYW`Mêùëg˜×^Ÿu£ «§Z@šŽ?öå„ú2úÐõèO#î‰èêCš -àtÇPÛÝ=ã÷ì<Tî{øJþ,û=åŸñù¨³K5&Ôz®}unTZåW¯ÒiWN»¤;CZ<ÔS¼Êˆ+f/t?\c6sÕ¡Žuø¼ä±M©å¾köepÉXæÎׯ.®=•^iQ°>ÔË‘k±¼¾(ü\ ºí>øüθfí×tBWü³.;Œ<²Síi>°Ÿ)h?îÄØ.Ÿ»³öFOhJ骵Ò˜W£7¾ñç³±eî{z1fhíïiŽP(_… ±|Ï;¥&Ç:çz”u%ÿ=ý¤Y3ºž\KœÑeZ÷À#“¦öØ)xWÏ· Jm©õ<SËhtôÛÜÿ¤fg÷Ÿ÷!ÍÈKñ¤Æý{ùF6ó¹Û–µVÅ×[Xç ÍDÉø›v“ÑÇv•~вýzÎn?\†4%¸ƒŸ²»^ìãZñöã?YÏr"!MàôÏŒ§‡ª~ó;Vѧ4y£Ö‹.¨+'jœÆæ}CÓû^ñÛy<òÈ,ÓUÆø8Z€á˜vÔõòÛyæÁƒŒÖ ·wÔ½+€4-%勚h3¨×cד¿0ó6:†Û%šܹ߂ çLs›2¡˜ý»zOHk ú™‚þ2$Úñ±zÝ¿B»•óm*ÀÇ-(Ÿ–uù´—&Ü YýUûõêâCÜηy´ jy¿3o§´lúƒµÏÕEó•÷ôÕxÿò*È;ñtM逧k=7Ŧ?ü÷Ïš¼xò6 ¹]Ý3 7.W}Û€¿>âíäùxSËàÁÜ{ÐG|ýÑÖ%\‡´& ¯N¼Ë‹*_Å-9TâeµÍAûŸûx)Àä5+×ÿ—×iÆ{‹è̉´¶Ò<6µ¼„RëÞÙ¬õ^ïùZš9âíäøPËÛ0ß6åætæ~‡^#²w=Zi @^ŽyU ºO^rËuryÕ8µK+Þ~@ž9‹ZÞaû[å¡=üËK>ì¸h:Ô Òê¼&òÌ7\_?£Oîôúý· ÎãíäÕ+·cÙ¤î;ùï~;§¹F_liu@3˜Z^«çÜÖ-MýŽt¾kò´ñDÞ~ù˜¼_jy;÷ÏÙtȧ’ÔmCó/=H«ãO¼KñÝv¹á¿ëî”>‰õ/WâíäiùQË[©Ãø³roÙõÒ]±·»Upü…PËëê÷Ö±Uÿ¾ÛÚ¾åÇÏìŠ6W¡@^ÿu{ª]G,aÍ5¤Ç5oÅý¬ ¯FÁ€3Ù‚…ŽØ¾ÁÃòäÙM«,BòuõÏ ›Ôâr`»7aüì&¢¼i—WôçìwܹÊwå?|¼gœy9Æ_äéŽÇäeÆOª~hxн¢kî¸F%ÆBœÖ§‚z érð㈤!^[}_ŒïÏ:ãi)ƒÛžW˜‰_M]gâ}ÌÔ·~Ó_½@Z<´ƒ Úïü‚~/>½¬ñ-ø’¢“k¼›iQ§@Ï¡)jƒ¶mèÏ<¸Š•÷Þü¤±®Q¼©G®\Ï£ýéºÙÔ¼Çâ1¿YBšÄ)·çLË¡QOYÇ–ÝðæÇ<|]Ì„ãÈ•Z'Þ³ó•oî¹­šïìúÆ@š#À ·‚ý¬­DyóZC¯·øŸ¿äÕ´QÍ8Í àš?{ØG ÂÚ}å/ó¿û^}ScÈ™z*zn_Ì®àè—ˆòl"]Ǽ÷©Š*Éèt±»ÛÒL`¿®¤S–/;v*m]”}l¥hø´] !n_ ®EA;„<3Ê<•îtBì~fcÝ |ŸHâ´ÃæËó÷y¿ìWÑmÐÚ«ãF¿4-€Ób*ð[XvÑÎðÜôöò©àÌ|‚qÆÔòæÍȳì›õ…µéí‘toMHk„ážo¥:æËOuÝ?lÃÙäçÚÇÆÖp‡ÀN<$ÊÛXÍ8·D{MÜÁ,‹ÏyƒCZ3À1·cüÍ Ý…O°v™íQÙíÆìqÇ^MZuÁWwÄ5\Ô#ÐÏå%pf;¤Fž¤OýøƒaŒï4Bœ u}~EÖüC·\²%뎫Öp ìü;s‹ãïíòÝ1&”]Ð×Òê®IŸüí:-¥¦è«×«˜ÓÒ[¾BZÝ ¶íÒ’¦ðÆCÝÜj;ÍþûXZîHH«8ÚXâ¾.üÙwæ_?1ºæÃܺâǸ?æ¿{j= NñsÜ6ž5½xhêa[H«ò ÈËþç¯~¯~·Aïׄcê ¸?1øƒ êóF¬>o­a¤×ÆÈç—ê%ãëÆ ÏÇ__¾„ãvÈè»×Ü íW);ã‚!­àt©õ\1ù–ëô«û£ô\2ÇåÕ‘ZÜzf9QãvšÏæf.êÇ®²n¼»":.÷'®Ižá5;ÿÔS Øfü`D‡Ñ|H+z–(гwe^Å–Gš®á—¿Ý‹·Ô3@Ðáû³ÂÓÄp]µÞH+òL\¨å¹4ã&…å.Ѝ8xnB_¼ýæaòt©å­¾üüŠÁùeÌÃïyW'Ú÷Ò A¿‡ZžCÙ…­=¿,¿ü ãÝ”‚2¼€<°Ÿ öÄþ²®aÙ–5¾3ü6,lmè'Àýž ¯HÁúÖi_n×vòYp÷åØK·ÃÛ–Ï‘º|ñÃGÞ<[æ¶¼sï“~ÅÛ/ È3P gØ,ÚÓÖúí\r½eÊ ‹Õ&8+¸êËÞ uSgmñÝ{i)°>èiºdÛÌ¥6•¹·>Ë¢Ûo†´x(Iݦ…FU‚{Ç寲êâò41ïg ^„Ôz¹{éà£Ež‡õÑ}©BH‹‚ãVAùLՇĞð+[cÑ×¶¥«ÒØW•G­gÈû!§ïÝs_Ú;/û椙#ðþ™§Õ¦ý<0.sò–ïêµoz{^½8ÁÒ|àxóf“+QÏD³eÁC¢‚vœÑ Ü9Ÿ7™×¢ ŸÅ8›uá…Ç<½ëù±ÚbÜÔ8swþÙo›¶M¼ãÏÌu Šƒ4-€«Q0l’\:šè¾pÝõqËq;Ú¯^¼®æúqÎÅ­þG^œµ*ûƒ±§A¿Ç•Z^lèAÇyƒÍ‚Wý¼”=ný~|üy9 ^L:ñË_ïs,Ü,b­¸ÿñKtÍ^|ýÐ:Ì+W0XŸ6nÛn÷"Ûëä-Ëç_ׇ´€«¸R’¼Ý¬²û,ƒ83˜;¢;×eBZ3À™gS׋X¸ÿŒëSgÏÜúˆóWН@ZÀÕyYÙD=kÖwêohÛÁ}ç ß%):öƒ!­àtC©åÙ–ì<´lâUÚ/Q¹W5@ZÃÀ¶ûçí)¿þZ»z¸_ùº1ûfßû„û×õW¡@^e²ñï/²™'rR/ U«Ä÷“ë”ÈS?¬æ=ÇÝ}îÒ¿û>×:…û‘5§µ´Ã:b½ì~tê`Jþ†qÛ8Ÿm¬jØÓ!­ àrH8á¬Lzèy½9ò˜Ï—ØMŸ7,ƒ¸ €c>Æø[Hþuü†~Æcn~‰Üç±t†O–W3¤•œAÆ_üz“hLž®ÿüåú=Ã÷ÝN<¹rÈ>ˆ+QR/aÃâo}.øì>¯qÇmým´"€3ÙÉ©rs|1y×'ó? ?6®fhêúGs;âë¿Ø?Ÿ€ýǤyúFëñE³¢Ž4ž¼aÃ…´€kò @(daòfvyqþó&·˜õ~»ñ­·­„¸,Ø?Ÿ‚}'Dy©÷^ÄäÛ†åӹǗ64¿‡4!À•îëa¶y«ýKô]jç²7¾Ê6ï5¦Dq)× äU<%Ê[ëj÷LØZw¬ßü+ª4 -àâwuuÝ1y:¢yŸûDnØi–#¾g<¾ÿëåÆ_ôŒ(Qûì‚þ©½ãŠüfF9¸÷ÏØ°ývƒöòJ€¼»}cßånŠ]²8lì瘞øsfh'šh&Ù‰s3ö¾S»·ðð “çØãåcB=¼jùaòš¯^ª`ý˜±ø¿7u{|<8B;øãN”÷ùpßn£· ŒcRUÝ?7ßßµ‚í·´M<0yAù¿Ëï}òŸ}qžÄçish¯_Ü ¢¼º.¶ þŠ[}eÀÂÍ×Oñ Ͷ_%(+€¼p¿"s‡ûû"ª—亻÷½ZqÐN¼Äø­^åU,;½±:qhôül‹W¿OýiºJÆ­é–úZº•ýNçq£›p?KK ®ÖO{+gîz¯¢;ÏË;gdàö“¦çÊœS9òìA÷“kºêð¥ ÷'Z´‹÷ÐýÑcø¿…K¿ñF»ržCZ‹œë·´/]ÿò<‘¿QoÐQ?üy\3À5Aÿe=±>§,Vé;æϼ”/+‹}>âûDMWâAíG¯¼›áå±ÿÓÔÌ(ïRÜß öç=¨ç£Ä÷3ûù2÷<Ô’˜½¦Ñ ßÏjTR>ÿI&žÚ'ƒ*ŸÛŒ1™i Jp1òsûÍg]² P¯d ¤Õ+Á ßëWi•Üõ=y«+ u°>3©ËÇŸy`kе_çÇ‘Îý¢ìñùÔ‹ž¯ëLô_ö½0É~²y­w-›ÿÖ©Kºî¸€k"á}r=»Ã½§Ûaƒ¦}}ôŸãçtNWß“ß1ûó“§ï½—Ÿ{õûã£=Öâõ™‹áZ³¨q í—ÞýýÕÖ¾QËôÍÂþ¬ÄËpÀ~Ö^R¿ùrõóiVeÈœ°\Á­©NV꓾ÃUœI˜ïwÖ>ÿ|I{¢hÈ¥e#×A\À¥¼Âø ^‘öÁæ¿Ô`Né^³øRå‰È§øü^Û}'u»¿4{÷áŸCÇž±wβò¤•\)h‡ÖP¢¼/÷Ou;mÏpÝÿ–Ñk‡hÁH+¸¢…Ôû»ßûêÇ}?5tÕÖs:ÅQÕx}ÂòYRûåÝÆ/ÙuªÊ4¦¸òÝä[2@ž€«³ Æ=5»½êâvÓØ#_³MÁ÷³r®f5îýìZïâc‘Ë»[tš¿|'¾ÎɸªÔ8‹?_U÷Ys>îèï3.O~ùßm$5®bú¦²ËïÇïòøùcP’~.6à˜öÔ¸?zLK‰ª4Œ[hûnæò#®'¶Ÿ 5îB Ÿ0]Ï.üù|À#|}p-¶Ô¸ÕM=×>{wÜ‘Ï1.«öŒ‰ÆÇ-´ŸvÔ¸m× ¸¢QÞ!‡½§6¯‡<>ç£@Þb^ǧ»&ÝYèRòõ‰_?0®BA}nÜöÙ‡›W¹’¶:Ik6_ÿ9Ûš÷Kø¬oØ…‹ÑÛÃ8kvœ0ÇŸçX\«Ü÷æ­&¯G….]¼é±úû0Ècpº úÙEv™µ–ãBÊ7½10} ä1¸øáÔ¸»¿.Ýy›Ò0áPdÖ0í 9³ Ài)èŸen7½¾„<‹ÚýÏÚTû€R3Ü~\”5p¿~Ýãךf¯qÒÇŸ«iœ•!ðW턱™ÕÅÛó¿37O¾xøŠÖ œ¦d>z2Ð騝^î[>1zßôÈ‚´Öþ ¿y¥ÿÖc!Œuƒ;·z^¾zÅ÷‰çðù¡à⮕¤gÈöì§¿pÝöÕž¹wæq Nkîß¶žž{gÎ\lŸä>ÏâÞ…ÁjÓçAZÀey&?ˆò„!n¡§Nv›·i΃ ÁñçÚWës5gºí9=É÷v`qÐÆ{UÓo«áóÀÕ+Ø×pp™ø°ÏðÎì=)¿½ž¶4Cz> ÌcU¯19Ì×Dy™ j}ãG|?ü¡5>ûiQ'H«ò˜–ÔþÒÂ…½'~ r]_{hÂ… ½ðý]] ¯T.y[å·—ß/±öìŸbàžæŽûYsÀ~8?ß –“±¡°wîã>^5ç—|»úû“"H«ƒõÒøD¹Y'£_„ønÕ±eTö[¯ÿj®‚O]Ÿ÷c~6ÕÅÅÔU_[£•|Ÿ§® È3FòçGü¯qø^Ðö[/,»¾Ÿ§•ô³O_~lºPÓxøšñÓAýêñyö+êú<“ùþjc‰½×IÖËl¶Øç9¾ÿ9§í}Ú=¯¾ ð9áz4Ç8ê\H">nK”èy>1N°óQe`UÓݦýFàó;Ôœ÷ŽLÔw€¹hÑ_­nU‹BÙÍÜ“‹Ïï°ýSûg†§KŒô:1Ýl½Q|×`- Ÿß®A¼3·úÄÌ-¿îïîÏgõüŽ?ß̸VàUTqît¿Z³Ñã–¿žþÛ©!O¤çi¡]2 Ês܇…Ã}zž|Ó|äÂèœÀaIø¸M¸š7_Ê¢¼?»;ÛùA愽ç4yg´q>à„@³‘ˆ{òÅuo÷#®»ì÷½ô ÏïWp%æDœßˆÍ#f ûíÑih¼ìµTzàJ®t)w[3d¯Í™$ï?–n;yüè]|~WÒ_R>áýóY¹sóM—^‘×q pZ`Y‘Æíªšn#.~q«m^¨ë9m¶¤9œù\êq;B÷·åƒ·„n;•ùyé®ï¾´îzÖ0nïå9œº0áðK~Üú£)‚á§«ðýk+ ìuÉ"®ÛÔN®c~Íö^¿öÚÛ;VÃð÷ÌáxE­§–ð½ÅÁáâˆ}—ŸñÏþ‘„Ûk0n[Áú¶nQÞÝ«©›=.ǺŸXI¿5À¨?¾7òtûRãt;ùOXòh¼ß‰Þq n^Ûì Í@Iû5†=,ó*ì»vCé§Ý£¶Bš®2;Q©»¦ºÐÀuݹŽcì4ÇBš–ܪš;J½þô_|âëÖ9÷ãñuM nàžÃ&Fó›ï`±à3¾ïÝjÖ6Îûør¿E‘n[+wYòçü—Ö¢÷Oµ ?oÉ2ÿÝ=ÔËç±ñ÷sš•à™Xr¯,Zã^ëC);ŽÛ‰&€3íÇÖ&ö›+Þ0–xlôZ½F»°ŸðîG6àê7Û½ûEÊs;ïC¥Ïâ mªðóì F=¯„]¹:ýJÿ?ü÷ò¶xÕ‚4|~ý³Fnéö[bÆ–%%^° †èâó{†3§ÆMñÚ°¿ûy÷šåëÞ½Þ¼ŸÇ.KÝZ¾u_°pй ë-œðq”Óöü·£Øôä‹öî«g´þóe¡>O×+i¿üÙö.1›Ôýj ªÃêxÑø¼R§`Q˜t14Æoç§ó]}“!­à¬öƒöóIC$ÖþSuÄ?5õÞ/55š¶xr¾Î©8öŒ¿às"0\àm?ÑÊRã¸uw?Ïý¶Ç—‚5ß=vÝ ÍÊóÿ¶Ò¬”èÉÿ9qßåýý7ÎKž`å?Èà Ž=AköÁÚqÃXæ «6F.q»Éê<q&Wú´û[¢žâc:¿ÛnÅ.ËÞt=ñâÜÏ2¸F /„ñ@^à̰n±ûwD.šX¼þ ¾© p-ÀÏm!ÊûAëš±uçâˆÂ^Ýv:‚ï÷hÁùôO]±6u) =~¿Û"­oйùøy)]`¯ëóAù~å™Íêd]åtß~¢±ù2Ÿ§i@^ $\O¸É«îi-޼{áÎ#>þ½ÕÔ À±q®«wo:kï^é˜ÐýpÌ2ün ÀµœÁP".Çj݉ßö[¸ðªèmõŸx;4œX¯ýIÄÝ÷ Œ’峺|Õ€{‡Gàý¥ àL®Ô”ˆ‘íÔS\ëäz²ÿ¾ŸÓ‹ñþÒq JIzf]º*z{@TÑIpûÎÕ~ÖpÔ~ù|ŽË7}·EnÛüõ/ìNï¯ëMÛ¶#ߨÜ2˜“|õTBs¾®ªS‚3ذÜýB\Œß²õnUõê‡ ­àJ@ùêHýì‚~VpQWB'â*ÀUÀzù ÔË_Äz14Ÿ|mö‚™±y·"þ\¡ï —BÜêu@wâI̵ã÷4Ö{ý‰ûK³Á{ pÓ&9vèØÔiÜ®”ŠÇ ƒ'zãþÀeÁuiå©‚ò•Àú¼ æË»ÄòùF^z•wN¾-{²–Û•êÙVp)ƒ¨õÔxÜõ…õó1¸¾Îœ…ûg°| pgS½úvÍûV35nô&?~î\éßÀŸø›¨ç·Õ— "7°×=˜ò{Íû¡VÛaµ¼ÍW¯ŸêU9!¦zsíïk}iúzÅ=`'îåýñGw}Ãñsöÿ˱§Úï–äU(X§Îu]4jl6mÜœu¾o—ûÄ­ô¥õ Ї,‰ó‡äüôÚ¾Àh„óˆïø9,%ã(xÇü%GÔ¯ù-ßðóמ¦ø>»P îÚ ¯¡NZsÝŽ›S§Ù–¤¥(Á­ûÐg~J⛀ƒ]2R|cŸáçžâ•áN\ÚkeãºÊç±yÚcÜï‰R‚»âX©ýZCâ^ïv‹)>ø9ã"8Hó8sç#WÖ›>d齑cg°˜þÚ—Ö“@W¤ç…ßÝ/Möò=±Z·9v‰ZmìÓ-LÜ?«8æ0nIò¦ £þ^1:|Ïºàžšú»ñç”×äå€ÐÀ“÷km§í´9Ñ+/}‰üà±ß·)8ó㢼ìõß[êØö=öµk:þ<®¶ßIÐ~0ôÂä•Þš±+±97rmŸ ›&ã‡øy·"€Óýêå#QÞñÓqAýü„Ïï3gú¾T¼ `;Ô~–‚ò-¯îp²°]̲cwédqâr®È«øD”÷Û¤ßÖ:”¿ŽX2wëS–Y ~, ¶ß) 'WÊ'ïÕßoZùWÑÞ¿-žg¸¦Ï ?&Ê{°àÉëY nÆ¥{:l):€Ï+)°ý€¼š`òºzÝ}ê+-dIxrVdæTüüg<ÀÕû<_ˆò^2f{JìÎ* a7 |ß& ¶ßiÐn d{còvjs½v„…_01iæ¼ØÐ¶‚õp+QgMQèöèØC&A#G~¨ý i>°ý~ü ¬åco¶,éÎý^v÷Pñf˸žL¨çWà·~%Ê»äªS0à·ÚЗ÷‡dq^Þ€4GØ~g@ûy |áêÂôƒï¢‹.À±ëÁþ À c0ÜÀ¿–Ý-ðj >ZXVVóùˆÓ‚v÷°ƒ lò²½Ÿ².÷ÒŽ™[ Z±3ð/ü}fœo/‚þ Bƒq®º0¿óíRöÉ££¤,vÁûuk?ÐÏ.vaAØÇ|”=+§6jÑ®qÚ>OwáïS¶\Ãï`ÿ„u@Þ›½‘k.œîU×3~dH2Þ?›®å2B«p ×Ñä÷»Æå mâç^ê„ßÖpºW@ûýäÆb8ÿ~pïxvéü-¬ú~êøûÌgÕÚà¼)µ±óõ˜=nSÁ\Ñžë,¼_7œãUPÿ_ÞâtÅ™[×?rÛà±£x»ñ÷7ë.ø$°9“™õþÁ±ëh;ÃòG?Âçé:X/@^éU"îÖåC‡.T²Ì (øñäþm À•^vå÷gïÄÒêu­qGÝŒ­\‹?­8öuÀˆûûªß{£ÇÆmÌOö˜ù+?—Wp´w ÖÅ:ÛôVxYD-³åÏÖÿ°·»¥°à^›3~ìv>äȈg© æð÷ˆJ.^ÎÛÛ2¨¤hVLÕ8û–ª­óñsyEW¤7·ßöÑ›{5‡.XüÚÄ÷?ïVË׬àùídmû9âÖdð³^FÁÏ ä\Üâˆ;¦&;ޝÐ佟ây²Ž öÛZnÛá-i?¦7Ì÷VLý’Ô~j ¬Ã/˜^pLCêó‘+-^¬üýy«î×›¤‹KÇÁt6ÀíØŒñ×ß Ê£«3(å AùÊ®à&IO¸€Ûp)¤za(ÀÅÃöƒzë`÷‰ê éf†ðN¢жÐ.Òšijtº=‘žO¯ ×Ñÿ¢wdX14肵˴*—i9Òñ.]û¾†ÖƒSÚOæi¿¢ÝrSûã>-Íe;ÞÖ²Û§™ØCkW{WkÉL­ƒ¯vÔzùUëSªö— Zîï´â ;&þ­•ÌÑžÔC{ÞsÍUµš«u4›j5DkÑ—i xÞÁÅPËçV(W«d_‡ ,Íóã;<2Õê`ªÕý”¦YYOãŽÞ†š¡ÅÆñ´?4–Ýì°ßT³ÁNóXëñUÍÎO:ô¸Ù¡ÿLÍ€Žø=:Ìûª¹¨¸Ã™çggj¾=¢a[ÝÁi_‡±Ý4<«5ÖlÕØ]«qòÆùÛêŸætè»OÓ~˜fÆ'õ|q‡µW5wÒø{dõj35:w›­î~[#úH‡8º†`¤ºøI‡•§:l.Ó8ÌÕ8g‡ÚŠ¿é·]Õïg4ý`¼Ïøò€þSC½Ãbšým5Oa°=ÞŽÎ˦O¹ÍÈøÁXU¬vÐç–˜þÉU éi´´;wÑí®ßÓ ·Q_³ƒÌ‡Z·²¶µwíäÌtóðòñõd‡„EDÅÄÆÅ'$ML™ÄOŠ22³¦Í˜•“›7¿ pÑ’¢å+W•¬]¿±tsù¶Š»+«öF5㣖qŠA“ýeuÁÆË>ïmaj1Âb”…·E¨Ç‚o1Û"ߢȢÔâ Å ‹ó ·-[¼µønÁ°ìjéhjɵ̵\m¹Û²Îò/ËO–:Ãó–2¬pØÞaç‡=ök˜ñðÃG÷>axÚðùÃ7 ?:üÚð7ÃõF¸ŒH1oÄ®ÇF\ñxÄ÷Ý­†Yù[M´šeUlµÇªÞê••îH‹‘Ì‘¡#¹#§,Y6òìÈ»#?T³îhÝ×z”u u¸u¬u‚µÐzºõ2ëÍÖÕÖW¬Zÿ°îe3ÌÆÓ&Ìf’Í,›6å6•6'mîÚ¼²¡ÙêÚµeÚFÙ*¿ý9Í6Ë6×v¥íVÛjÛ˶wl3 žÛ~³Õ±³´s² ²o—n7Ín©Ý:»=v'ì.Úýe×b§nßÇ~€½=ÓžmŸd?Í~©}©ýûËö7íßÚ«;˜889„8¤:`÷"ªŸV¯K¬ÿ!ðˆl´_¿¨ãéíã÷i'VzûøÛ™};ó¯jgþ­íãonoý´3ÿªvò7µ¯~¬Ú™¿• }üíä7ig¦¶³~ÚÉ_—Ö>~v;ùáýó•ðw¡.¸‡úœ~-–njŸ>5Îíl_õéBxŸµ2~Â{¬•ñwù«Èß„KÛÙ?ÍTËžÚ¬R‘ß„QýUãïÿ ¿1+Täï Bö@ÕøM@ت"¿™Öªñ÷¡ÐD5þA Ô2UN³¥ƒT㇟%вQmüZ€°B?iš§ a”­jù¡'µþ¤Ï(Ь@Øb«¿kTÌÔßK5þ1 lU‘,K¼Uãw¡£jü® lT‘ß„Y,Õø=AhàKݾd~oÖøª–? „Q~ªñûA~¦jý3„p ”DWd?ÿ·ò“ë‡\Þ0Ú¿–?Û•ºþÉùGÿ‹ùÙ©Ö¾) ´²W2[Uì?i ,ñWÍ~Nú¨ÆŸÂzùÅ Ôri§?ÖN~x_y¼ !ŧ‚°ÎAµúÌa¼£jåBxYR<„­*æŸÂF7Õô/a–»jü‹@ï"ëïGŠ/…ùgSó‘âË@X¢ZyW€0>T5þbÂ{t”ñ¯!ïÊìín%üäöÝ£„?œßKâ/U¢?¾žZO]^²>p¿ÂÀCµþp„ñ!íÿÓó…îòvî'¨Øê@ÈngþÍí\¿µ3ÿ†vò··>MŠÚ—¿îŠv®Ç‹ÿgõg·SŸ¢vò—¶Sÿ†væßÒÎüµJÛÙ^+ÛÙ^íä/j'?¼¿CU~xo‡ªüð¾Uùá=ªòÃû9Tå‡÷r¨ÊïãP•Þá*?¼CU~Ÿvæ_ÑÎú÷l¨ÊßÚN~ÝvöxŸ†ªüZíì?ðþ Uùá}Êæ¯ë lU‘ÿáýÊøoƒÞ Œÿ>áýÊøÀü×¶s½0XµüŸ‚ÐÀ\5þg „ïù«ªO‰Šù¿!|O^ÿÂ÷Ç•ñ!|[™?ÿ „ªúÃ_”ð/%Å[•ð“õÿ Bø>2™ 卨ŸñøÚöñ3ÛÙ?‹ÚÉß«<¯„Œ7P±?ôüÂ’ö•·¹üð}6eüð€i©ŠûÃý!?ÈÿŒ~xо¦Œ`4_ÓÎç‰íä/*j_}6´Ó?7YÖÎçwíä¯k'?|ÿAYyá5M†«Öÿ½“Šüð füÕøá ZVªñõwýÒÐNÿY·ëü¼¢Šü𜢪üð|¢ªüðϱÀ(ÍpXàPäPâ°ËáÃ)‡s¿;\s¸ãðÄá‹CÇŽnŽg;–8îwlp|ã¨7ÊlÔ°Qa£fŽ*urÔQïG1F÷í2:ttÚè¼ÑëGW>7úéè/£uÇXŽñ3&eÌŒ1cÖŽ)S5¦vÌÅ1wÇ´Œa8é9™:™;Y91Bœ¢xN9NKJœJö:sºæôÄéÓ'ƒ±VcÇzŽe›1væØcWŒ-{`líØ›cŒý>¶“sçÎcœCœÓœ3óœ—:—9ïr®q>ë|Õùµóg#—!.c]]8."—™.Ë\6»pùÍåšËC—Ï.ÌÌ!L[f 3Ž™ÊœÊœËÜÀÜάaža60›˜?™=]M]]Ù®)®³]‹\÷ºžw½ïúÙµ‹›¥›«[¤ßm®[‘Û·£n nÏÜþqëî>ØÝË}œûd÷îåîµî×ܛݺwô0ðèaíáë‘ä1ÛcÇFƒ<yüã¡åiê9ÚÓË3Âs¼çDO¾§Èsºç<Ï"Ï2Ͻžç=oz>òüî©åÕÓ«¿—¥×h/O¯p¯8/¾×4¯|¯å^›¼¶{Uzõ:íuÝëžW³×g/š·¶·ž·©÷ïQÞ^ÞaÞ£||}b|>y>«}¶úìó9ësÝç™Ï'ŸN,CÖ0–Ë•ÄJdMeå±6±ªXÇYgX ¬§¬¾&¾Î¾1¾™¾K}Ë}Oú>ñ¥ùù9ø…ùeû­ö«ò;ïw×¾¿‚¹ÿCÿïþ=¢KvÔ< öt Œ Î, <x#ðU ZP¿ ‡   ÉAùA[‚ŽÝ údĶbû³ùìEì]ìKì—lµ`ÃàAÁöÁ.ÁþÁ1ÁƒEÁs‚——WŸ¾|;øUð`Ñ!N!¾!!“C愬Ùr6äNÈÓŽ¡¦¡¡á¡SB„n ­½ú&´SXÿ°±a‘a aSÃæ†­ ÛVv2ìZ؃°Oa]† ÷  Ÿ.Ÿ^~&üI¸f„Y„kDbDVÄ’ˆòˆ×#ÞDtŒ9&Ò-2 2-ridYäȳ‘×#ŸD~ŽdDõŠåÅŽšUu!ª9ªS´U4+zbôÒèòhé8léF‡d?ï¿ñÿÅÉûÀÿÛâÊô%¯;þÓõù:NÞ×ÿOÇÉÏ1þÝø¿›ÿÿöúho~Jâÿ®à}-Òw9£À÷ƒá÷‹á¾¯¿³eð{†;‰éºàžvøÝø‚ïÀï(À_ø>üN^.ðÝøøkß5€ß9€¿ðÝø\ð}øø³ß€ß!€?6¸ï~gç÷¥Á{×p~p¼W //¸ï Þ›†—܇ïEÃË î9ƒ÷žáå÷˜Á{Íðò‚{Êà½exyÁ}bð^2\p_¼w ×ÜïÃõ÷}Á{ÃÈ?x¾„ücêS§§ô N/ꉥ‡€øpßüÞiø³1.aŒƒ÷îá÷Gá÷DáwØáwÕáwàM@èB6É÷‹¦€t¸oÖ„V ‚÷Ý|ŒˆøR W Ô¼ÝâÍð;àýðÂï®võïé‡÷cÁû®àýUð¼ÿÕu?<Ïãð¼>ŒÃóø0ÏÛÃ8á|x4úO´tzuzgÕeù.é]о]g¬ü ò^™Ø=;‡<Ç_ÿ¬éE”s{3±Þ~ýÂN¦Á´”L¢=a¼… Þ"‹G¯.S|u3‰t4]S&^:]¯)#_+‹Hï@Ãîòáû–ð‡bµeô„ïoŠí h»åJ¤£÷šè9’ñoO¤£yëÉÐáý ð‡Þ+bˆü¯èZ6òåë+£_Ñlyº‰LùÙXÿ^°k'X?ðgÐKžn*ƒ/1¤ËåÞu;•–¥<}MZ¿ñòtszÔ0yúP:{„<ÝR†^j%O.Co²“§[ÉЙ6òtkz‘­<ÝV†îH‘¿½ ½™"GºÏHyúhz ÝI†^b-Ow–¡WQ´S†N.Ow“¡WPÐ=dè­íã%C×J•§ûÈгöÊÓ}iÒþg^%O÷“¡7RÐýeèûäé2ô’gòô@ý¢šåél:üÁù‚Ë­Ño°ƒûØ!tWº`š`Zé¤I5túÕ´[3þ¦Óï‡Ø…-N¨L¹¢chmëµÚã¼ù}ó÷–FN^n"«Ù¶ =Û±ÜÀí?\o¨bßoEÿŒ6‹aÅÀ²ÝÈ!ñÁ{ÌÈöƒ™Mâ÷‘‘íHC‘Þ[F¶'9$>x_Ù®0I|ðž2²}išJäƒ÷“‘íLS&‘Þ3F¶7¥$>xÙîÄ“øà÷ŽÉö§TLäké‡ÑÉv(DæC,^“ˆÑÉöh‰/ЇÑÉv)—ÄW0£“íÓZŸOolü™¤Qß³ûðåô|‚¶ùjŒ0¾R%ùµ¾Ô¶ùàw†Èv4‡TÏð{Kd{O⛲–èOÒÝ$C‹R“~˜žÚ0?p¿BÃd,Ì Ãp éXÈ#æƒÏÓà½ÁfÒwv•ýàûþ0Tõ üæ`’ÿLõCu„÷s?GêCB?MÑw4¨~¨Ûá_À¡wÐAžöàtÿE\7Ü‚õ? Ûúu—ÁÁœ?õ@<#fN̘U1¥11{cNÅœ¹s/æĖ˜㺎3’ü7|œïcBÄ£?- ÙZ ¼°¿£íÓ脦P`¨ô”ÞÇcBcÒÂh“hÓiÅ´£´«´4º5=ΥϤ/ WÒ¯ÐßÐ;1ÌNŒF&#Ÿ±QǸÏPSë£f«–¨¶Hm‡Úµ{jŸÕz¨Tg«OS'ʃ~Í šžÁM"NËà¥r‡³Ò2¸"0”+Êä%rÓ‡Ó"yiI‚©éýâ´$NO6Ü]ÀçsÑ?©éxZ8kx'•?܃“Á‘OÅ\…B>/Q‚ $qÛ¢¸"b3Ha¥ ÅòÉ®â A*as¹¢ô¶9D‚L^W$Ïä.HË øð@N&/YNÜ$EÑ8¢Éb!!]±@¶ˆ—ÊCŠÍ¥åïšÆKmSÉ¿6”­(NåŠx‰2™{ó >o)KÐSÂRD\N/-™ Ì] â¶Ñn!ÜtX„v("ch†@ÄIæR*æÁã$§ Ò3ºA@h¢Eª,›(ñ2²‡»‹²…‚dG˜’­ŒL­L(—#JL¡ê`’±1\Ù˜8HVÆî.H Ë*kÜ Nåðñ$"-—–AÑ+ƒˆ"µ„šÂ*îG¬TN2%Lf„(î§!’³‰Ê}ÀDѨ ´WvÐ6ç°ÂEdMËóç"³pZ²RÁÉ"Ô|’z>5ËÊ5ÈÜ$…Xjº¼øˆ@K'å¦,–\Ü´Äì „Iˆ‘¢H²F‡9Ë“ÏE­<-ß›M²!1!×|£J¢5eª -O&ã̉$Áx,ŒlÜšœ`CËg xh²ÉgT9Ã8B!I‹|–(Çë!#fœ!K’Ãùørö1ŸRå¸C‘žA)D†"‡ à¤ñ„b>œ4Dò©XÚ̇•†LS<Ž Ù‘8•k'W6 Ž6sñàò38mä!¡·™â ùܶ5ÁyÈ9ÉŒéÜTÀ’ú.¨û/— e…Þâ)yœ 驨ÇÐU!–à…ÈÑÉ!ÓYà&ѰX!œ© Lp– å¡å—CY+.™<ÉF1³<É5-MyŠ«HÊÔveQð©_[(ËIVÝVq9åIvŠ™íˆeÊ•X.RH‰'Œ„£f‘éÌ„yÀXW‚1ÃréÖ´\tuAQñÖNyxŽ–y”8gI†!qHJ$©!ŽP([dT£+nVFwŠ˜›Þ. ù ¡Îʵ#Ù gD×DdY’ŽL‰¼t!Ÿ“ý¯¨¤<*IHÊKOÿ·$ÉçAÄJËLþסhz!ëîTh2ôLòByÓ¸îÈr$™ì:°ÈÐQÉYHm°Gˆ8•x" {òÃø:Ü—#ªŽ*7gZ1 } €z°É&.H@]O>Ö‘>•N+Æ9pEÈHt74Œ“>™-Bº/#[ŽÁš¶Š> €ÄЭ×$˜D5¸W8Ð:¤6ò|*Ø HE[$‡’ ˜iÐ ÖòÙ¤yèv+Uš5m5 <Cû-×5=;-*€¯é`_éDàG !ÏQ q‘¡ÚŠÓåýyBgéTÒ&»3œ¤$ðúˆ ”‹‹0.R´°Å-$DC¹|l¢N–u±©èÖÊpÖrÊ¢)%¥Ó·$J[&Òš`!¥Ï)G%ÌLT¤…äö6 á¼Æ…JàÊÙȹ¡T^‘ƒÒ1TÂbMÁÓ†/¬ÀSÄÔfî6mme§¢po"+J§²ˆ0 3JØÓ ,DˆXƒÜ ç&ñDXG”å²!˜µ4d„á&YŽ ŸbM´U f’Ú´r$kJ†’©kXnâ‰Èä@[Ë’yÏKÄRaÖ±|8é)®üdÔ¢§¤¢y¤+ áû¢m“ióXàédĤFÏKÀGY¥qdÐ ‡ CBŠíŠ`ðù ¡JÎÈ'˃ˆ¼r¶Çm“E1û°¢,ÑÇÖ6¨Cd‡¬Òy_BV€œ)§€¯a‰3TÖC©®mÛüJt%ƒå§7dìq9irSÌaRVùl¨ÚQ•<©q„¹ìáðùCA:i:¶VÄ* ëç„ÎKäqÍDfNzI>kÊ™[†A•2ù×U°˜+ÊÆÑéä1g-Ž2ÚHÿDõ‡‹Iiª-âOHôrçˆÓ9h¹PwAj àócrœ–‡…Äg³R“Š*qOd °Ì–&xJͤ“7í€|[¤PõF)F‹à¥ó°6£ÎšNx|N2öäçäG1ŠÈÔhEû)Êù¨ó÷)•ä'ÏGy§F1]Iù”eDÁH#yH1]‰FÊ2¢`¤Î‘¼Ã¤˜®D#eQ0Rçh§$#;U5R–‘‘ø¤‘b ´É Øïl³ó¶É)禮ÝïÚd•wkÛî2m²Úªž™rVåµOÑ¢ÔM¯øs›ŒÔËné~%Ï4uNÛf#d…íwö—\E"NvK™ÌUÃQêõ/ˆº¶ Vwä ´vúƒí†“½WgÚFâ–ÁÅܰ °£¸ÀaÑ Ä“º´VG”žÂá+gtMFÊIŠªŒy ­~VÚDÎÉIüŠ à(0`A]*%,þ¼…Øö1°_LÃ7ˆ¥IöÞeö‡±¿ÃÓxߦÉïÓÀ0 îøÒH[¼ä¸$K|Ÿ—7vá6ð[št¯–&Ýœ¥Éì»Ò°Y¹[á‚¶Kñ' p-ê/ È#,Yá+<‹˜¥£ØæqDEgÛ:]¨ÒÂöTåØŸòC}ªÚ#s‘jÑ Øœ t,K ÖDd™&Ë6@%öB&9HÄC²Ã†“L²?/“ÊÍ@KH[ÐÆ!9)BºÙé!ÙL#eH:è&+ 1©ÑdÌ8*8{¥à¤”âsJ4Î ÉóPœR|X‡–ç#ñ¦!pø®|^r6 "Ðþ‘HHR~¤”²s8…*žfÁ˜©O—(<îA+$œëäXHynCñùŒBªÓ‹À£sðä\ò¨<}ˆ)BÆ#Ú» ©Ÿ—Kó]•ÊOKGì;/ ›"K±i••îšÌb Ș •"dŽ˜/Ù—æ 8I2ù$ê]{‰¬RIp3FDJA¹Èü䔎´«¹h¥‘ÜAz†/ i©Þbž¬È,ǧ#„ d²”¾†$ÍP2‹¦£Ž· KäáÎE)2QgLˆRi¥ØÆ‹ /ð°PÓ\J"–þ®™ ž&y«ˆ¶<@}M†L$e]ù|ÁÔ¤1xBbº´Þ¤©+¤Ït¥påÅ®$?-&?Û$Æñ'•«%‰O%WÉ=Ó„×öÃ1Ða¥Æò(žQ?ýÂA­ xâ![tésd¼|xJÛõ+–yⱊüŒb-ÅÚ:ù¤4é?¾ŸOÚΗÝÍ_¹R¨ÍD;mÌ–<Ô²„jK‘VBLðçf"ÝOšŠ5›4!„‹Í²à™*9ØÆK“.õa]B ˆãp›ªIÖâJ¥L­49ORfÌÕ–ÛÀz<±·ÓIv>$îÃ"l‹ 럗I¤rQCµHÒ±€_°­„Pn²¤š!Cm\‚„ 9@¿u'Ñ}5LŒË±cÍ#ÑîÔÙxÈÊ’ì „# ¢ù\©Ö¡>/I²—ˆ-ŽÉ” zú\™4LŽLBˆ«,Çç³Ò(“ƒÄòé!¶ËI%뤊À©{©°žIɈG+WÙž|úâ=VݨS‚n5ÈseaûÍ<ãôâ$qåK†¦R õ9"’êX"Yu,•¤:Öœ ›5”rK²Ñ—ËÛõL•+ … å Bù@)*@2S¨8•+ª3XÌIB7Úñ¨H&‚Þ€"!\‰+ —Wßµ É&QdA¡HecÙbÆ…ÔBX"¹…°TR IOרb`•£Ü©-âªög¹ÈPg™Ë1ÂçpW{=>?ÛïóTVk­lŽêêÉ_‚èª|žrŸ³ºz¸Ë]®­î*­(*sù ÜDÕg­¿o¥§tb£•Ã&9}*=“µF%N¿׬P9ÅSã×Öf¨Ðç3¢¢¦j¼¶Pœ—Ô”—;«\ú;üN›«t¢Ó§¿s"%ºÅéö뫯ö¹Ê²ÝØâ]âî ó¾Žjg±ÓQæô9&­²Þä¢JgÒ1¬³U:¦8}Y,Jœ_i…QŸ¹ª²t@²…M§›Ý¢oßïqëúgŠ×é«t¹'Wwz¿qÝP§»FÞ£Ëõñzû:|ƞ¾šÏFö¨Yï÷p‡»Ü)î^¶gÖuz)®UiTQé*Ó_æÄY*Ή®Ö{•8+áä1¿ræÉ246@³„l>ú”ªn¿«tŒÇ£»E#<åå•Î’É.i…qñíëWá,h49Ê\c®9#¦± y%ûܨ®_Új€ÙÅÑØŠ`TŸ6ÏU§Ï;ÙÜ«O¦gÜšÒi}]î2ÌÏbP(Ó†;+Q5ÉYâ©ñ•:S•ʽ‰¥g‚ÇWÕ­¿2çjǫ̱9ü\å5>§2§¯´ÄY.f'eN_ç-.§/uZT‰^©v^íôT9ý¾)ˆB•X#Õ }}5ÕÊœ!.wš­8KET«lžÊ)šK‰¢L\[ã(ó¡q¥ãlå#\.‰\ê®sŽO<72³ÄÒ–¥Ã—™ú%ž þÉŸ³¯Ë_åð&»²oùè‹X,½÷Êë©vùé|¯Íé«ö ãIåuÝGx<•ã¾~5Õ¸6§;½0]£ 3áq_7›ÓbºIú#‘Yî©Ô—Ê+fóIÖyù0¶|úr¤¨/^SidÛ×QYéõ¸Ü~C§To諊]å•8üY2(u%3ž©–ë‡×xYyRÔ8ªœ>‡”‰<Šj±öa<àþŠB.× =Ðí­ñM«·¯¼Z¥-Z‰‚á˜ÇœbŒ$ÎJJnG Hñ¸sè$‹’§%~ŸÓ靯ªcs¸=>GR’ùøûý^ƒâ!žRG¥ñ¥–¢‹„ÏRyšÆ"ŸquMë“- b¤WyÜ×8&¹Êý—Ø.¡º'xD=ÖwMMUv“þ>WeåÀFbáZ\9»EZMn‹Z­+vvÏ’¯Wl²ZÔ¥6šeêV }ê …!(nºÎVÚ%[_UFMj ×XèÔÆF©KD–*u7¨«ÊZ<Éã'éß3¨¦Ê+.!Îú:J'–c w—!q¼— ƒŸSg~©ÄÒ£k¾™ ê[¢Ü3Äãñb¹J®Ýâ2Èσ³RW)[¢)¤iÂMÝòfH“™ÑŒÖâ¬÷ÈhQožaæ…›¨6Ú¾4ë2©»µÂ¨¹Y—a£ÉTN"v±³Ò+6(9»û<8õOQîدÒSí_éTþ1p°sÊ(Ge æ<—ÏÞõ LChü…O]ƒ9¹l¸³:1]•ÕnÉÀÑŽªÊxW1°ù—(7Ë5ÔY5Þé»J¹s¤Ï¥Œ®ªîÕ^æBUUz'Ô¸K˜ե“>*)¶Н%å@™«Úë*K´#ñ´—ʿՂ¡ŽÚ~_‰ë§Òß9ÁQSéçʌҾSüNe¤ÛUê)s¦×õóˆÕ¡Ü™"œ2ÀU‹yE®R\ÎÊ2¥Âá.C÷¦ýñžÕ2ÐBÉþgfûüDý‹3ÿÛ þ$Ï?¿+óü>MýëšzŸ¦~¡¦~¸¦þM}WMýMý ÍyK}'M}{Íù‡wgžž‘y¾[sþ˜Æ~£¦þuÍù¼gœ?¬9Ïi™yþE‹ÌóËÇdž?®©Ÿ÷ßß2Î'Í<Ïù=ó|ðëšx#2ÏûÑ?Ûx8’w$óþÏÊloß:Íýžžy^§9_¨±¯kâü&ÍùÍù Íy/Íy§&Îó›8/Q2ûëÍùìÑ™ç2û›æ~¬8’yÞ¶D3^4õ³5çÛ†gÚŸ}_æy[ÍyûÛ5íÓœÐÜß5'ÉÉ9#5ÞïÌôFs¾P[¯y>÷hÆo¦^{~“v>МÒœ÷ÒÎMœ_¯Éç°v~М÷ÓØŸ¦9¿uTfÿo;¬¹ÿ¿iîÿµšû¯©?`ˬïÔÜÍy§[5óŸæüÙM™ö/jžÏÏšùŽçÉñÐR3¦åfŽŸÍxꦩÏÑÔo;>óü%Sæùñ×Ìñò’æ<_c?ïµÌëŸvœ¦?5þ5ëG·_3× íü°ðŽÌë] ñ¿ešf½×œ¯þ‡f}׌Ï^ûŸ4óÍÿÕû~?g¶ïEÿÙšúmdæÿ¹&^¾æz÷ÝÙx¾ 5ç÷ÎìÿRÍùšù™çc4þÝújÆgŸÌó‰džwÒø¯éŸY?MsÞöÁÌó‡WgöGõšÃއºÛ2¯—£™ß¾8¤¿5óß`Í|ª9契ÿø2Í~çÍüújæù4Íý®Öœ—jü_×̧ƒ5ñjê/ÐÔ_¿Jó¿àýÌó=õ™çÏ~ ñÿ(ó¼ôMýM>oiúweæùš73ÇCŽf<´= Ù¯jÎÇÜ¢Yß5罬¹ÿšúöšóNšõ¬½æ<¿‰óg5í›ÑìŸ5÷ï4ÍýÛ¦éÏ3ÏoÕÜ¿Ë5÷ïˆæþ­ÐÜ¿—ÞÖÔ¯×äó¦f|iÎÛ¾¡ÙÏjÚwÂ ÍøÒŒ‡4í™§iOÉû™ãA»ÈÑ<_hÎ×\ïVM^®yŽh®¿BÓŸ·F5󗦿n]§ÉGÓþ—4ýõ‘f}¬/ïjÖÛ=šëÍÓ´§DÓž¶ïi®§iÏ»šùnš¦}ó4ó×aÍüô˜fþ¯9ߨ±÷iê»jηíÍ|ž×¬È\?ZÞ–¹ŸÔ®ïÓ¼ÿhÎo}V3?iίלÖœ_®9¿@s~ä‡Ìë•hÞçJ‹2ퟙ¬Yß5ç>·æ{Gæý¦‰ómš|>Òœ¯ÑœWkΧe©W4’õgïÑÌϯkö¯c4ûÛišùã Mÿ.ÖŒWÍùÃÏdÆ»^s¾â¹ÌóÙÏfžÖœ¯ÑØWkÖ«—´ó·æù)}NS¿T³þ×jž—)šýÀ4ë{¸ñýQŽvþÒø¿¤™ÏÖ¼¨yÿ»S3_?®Yï5çƒ5÷§D³ÌÓÌ_g¿lÜÉöhçsí÷¦w5þ{4óû<Íþ¦D3?¶Õ®7šùòݰfþÓôç¯Õšx¥Ogö+M{ûiÖ·Óö6žÿpÍõújÎ^œ9þž}2óûÛñÚû¯™ŸÖœWk®ÿ¡Oó>ì˼Ÿ¹ÚçWïlM¼;5þ&ÿ {2ח쪯]’ª=dPkNõsAOz«’µöÎúÚXªÖdÐÏ6w²v´E_IÕ.©Ô׆.LõF;ý¸J®³-rÚ­Ð×&×è9[ j“ë,f¼7 žPu½{S_«®híÞ2ðMÕn1¨UW¥+ jS¾£#úZuei·Ê V½®A­º:,XmP«^wÁŒ”šKÛ­5¨MÍá=?Ð×*©¹TùQ½¿-áœSšÜ)Y jG»’µvƒÚ™’µAƒÚkSû7e§~\Uœ—ôÔμ)ÞõµêŠ|I_«®8á—õµêŠ]fà›šùã¯ø¦j –ôd*+ËkúZuű½®¯UWË:ƒ¾J= ¶õúZõéö¾mà›ª nÐתOwøƒÚ”ot£¾V}ºãïÔ¦| Þ3¸¿)_Ëûµ)_[½¾V}º½QƒÚÔÓm*SGljÏPšW=Ój“#V}º·´h™ªMî”ÔÚp™þYP#·k©ú¦Þ7SµK\ú¬’ófB—>+uVÙÒRŸ•Zk»IŸ•¹]Ž>«´ëNÐg•œ¯Ð“åú¬ÔÙl´AVjm \Ÿ•yA_©µ±ËôYÙ‡§îo/}VÉZ%§gZm2+µÖÛKŸ•9”V›Ì*U;¬ãÖ´Úçeí,¾˜CüÎãw®±üœç÷»aË ¼í7ËØæ%ôŽÓ»Ã`~¢\EïCǵÔy=2¢9Boó4iâµ-ü48lA+½÷Τ÷†ä—Gz?ÊkG“™÷<^ïmõÒ;šü,Iï/émM^{‹÷‚¤÷–ägQz·ÂïÇIï`®Þ;ùL™cɯ®ôMok2s‹IïeæÉ¯ßô~€Þöäµ£ÞÉ'ÇO~§÷Çôö&½‹cúûíý+ïw˜Þ ½íì5%ÕkÞ3“ÞËé]@o¿$½gæܱ9ÌüPò›=½O¦w(ÙkóõÞ¤·²R„轉ÞT»ûŒs>Cæ-üÚoå—ÚÏ“±yíåfé-~lþ÷GeáªsÄrçß‹ÇùêEú°úæÇ$[æ|ú á^ÒyUÒ ëâný,“|*ZäDvëg™d-vE»õ³ŒZÛs~–Q#{÷ègµ6ºWŸUòyÁÞô}VÉZ\÷}Vjmíú¬ÔÈáôY©µÞ]ú¬‚©œC»ôYS½±e—>+µ¶Ýn}VjdÛn}Vj­R­¿ÑíFêntAäÝvßô7µö,n¬ÕÍ„r·ÞW­ÝÙÉÀ7µM Í2ðMÕné¨÷U7@îÒûªµË ®«n­* ®«Ö.1¸®ºi \W­ip]u;h1¸®Z[kpÝ´×Hƒë¦Õ\WÝšià›ª-6¸®º96\W­mgà«n»wÎÐûªµ rV7ôƒœÕÚž£Nýà5¨U?"ì<_]õ5#l³Z»ÜÀW}©5ðUk7’Ý,­ ±FÌÒš~Coö›¾ =ßÌn–Ö„H#fiM(~+»YZ¢˜¥5aôJ½YòÉIkB¬³´&TD²›¥5!ÞˆYZjWe7KkBìÅì³kúy)»YZn‘—³›¥å^–Ý,-7eµÞìßyÓºwf#fiM(X“Ý,­ 1Kk‚ymv³´&x—fâ·ŸŸÖ<κ‘"š|´iûT›Ä«q3ì­iöæÿ6m¿ Í>Ü ûTsaomÐÛOi¤½Ñfا·×®´lÒ>½½ñfا·7Тiûôö.¯R?pÿ’õê6;ºõµê6Ô¦~´™õOŽÉ£wSæ0¨UŠ{È Vý)®×«¯UŠk;W]S“ŸrR_zr¬_¨µÉß²¦~{ȲE­M~wHDþL_›üâªäÄöèk­©o%¡ÉúÚä×;%gK­¾vfªÖ¼Ï òœddë~}m U«¤µ(ùEÝ÷˜?Ó×&[„‡j¾Öª~Á™¬¯M¶¯+µúÚ™©Úø‘ç$#ì××RµÞ´;ø®EK jÕNûÙEê;õõ'hP«~GÛ`P«ö†rœ¾VýÚUûºÁ‹_ê'9öúZõgÅo¼¥~Þ‹êkÕ»þÀ rª¶§AÎê]ðÔªw!Ô× ŸS?÷/Uk/[¢™NslÏék“÷¨eNÜ 6Ù"¼ <¯¯™ª­HíÉ/ƒêó;ó3}­úü¶Û«¯UŸ_ëçúZõùÔªÏïè}‘SOhí~}­úü.IkQòk¦:Ú7|¦¯Mí{õµêˆÝ2Y_«Žœ‚)úZuä,Øg956–ï×ת#'–Ö¢äXµEÊçúZµEÁ½úZµE_µEÊúZµEÑ}‘S9Ç÷ëkS-Rþÿ£?¹Êp„§µT"à8Þ€ŽƒOÊUÖBÛÀÀI²\ð_Ô!0Žc“°ıZ99W¹èdic]8¾„ö‚ÿby|: ¥VÚæ*ÝÚJm}8¶‹ë‚ópü,|Á˰\9%W9û©-à•8”éˆVá0A{AKa®R-¸¤PæËÿ,ûÁ >Ngš«˜a/Ø¢´/ÿöicóÛK]ö£¶çFpÎi²‚àbê0xüé²½`ïÓe¹,¥ö‚/.ó€¹g  Ð`ï3hÚ©Ô‚{Î9ÄÁKÿ*}­ ý¯´Ô‚!jÁµ`ŒZP9SjÁ“©ÍàQ=_1ç*3ÏÊUŠ¡—â¸ZðWj¥îCyWâ¨mÿ|n®R -Øý\Ùç‚£¨—P ~D/?Oö•œqžl{ü‚:æž/s.Ÿ;_^7 ~ÊòxZG¶ ¬é„öÀFp ްó/à}G_ ¯ko¾@¶+ Ê.ÌU^mï½P^+žy‘ÔfpÞE2N4ufŸ€·w–¾psg¶ìÑEj+øŽµ"&x”Z± ¯,RÛÁW¨#`ƒE^K¹8Wyáby¿Âàî‹e{ãàÙ]¥¶€7Q{Á£8¢"~7<ãÝd{-à nÒÆ¾ÐMæ6ÝeþpFw™sü¡»Ì'Nï!s‚{È8Q0çÞ#pµ üê™C ,¾T^Ë®¼”ã,ì)¯ek¨àW=åucà)—ÉüÍà’ËdžapûeìðŒ^àuÔv0ÖKæ,8ÿrÞ;°þræ¶¹‚cœ@íO¼ ×€¯àß®’Ï‚ì}ç+°ú*>‚½s•C(lcÅ331Á VÆ·r>Oì+í{÷•öVpk_9 žÖý¾Ø?W96ðÌ"™ƒ¬)b¿_±ßÀd¹ |…:žqµ´1ƒÿ¤‚?PÇÁ¡ÅRÛÀ¾s+®k_È1^4ˆcœ1ˆcüaã€ÓsÌ€ûKß8X4„¹Ï ‘öap#ޏVÜGÿ:Tνf°õ5r]¼ä>_àn7{pí0ÌKЂŸ“¾1p†MÚÁ·l²<~x-û üïµòºÊp\w¸Ôfðj+xÛpö9øÔp' ¾O›(¸:þ©Dêðªy-+¸ Çta~G›8xáßö!×k+8n„´±ƒÛqÜ-xˆåÊHä0R¶1 þi”|v À~£¤ ´S ®%ûJpó(öØê:æ ÞyìsÁ…×ÉÜBàJÚDÀÅ£å3ÿ;}ÁË©­ iŒÌ­ì1†å Ÿ:^s½´±ã®gžà!hÁ}×ËvÅÁÕ7°½àá¸Þ݈ùÇR” ¾~#ç0~#÷NàIã Åý§ãØ Æ8Á.czÁoìÜ;¯ŽÇœ&Ú nÏû_Êö‚'•I_3X\&¯ekÊØF°…yB vprï–8Ù^ðÚ¶œÀ9 ¼i‚¼‚ÏLà¾l_.ËÇ”Ër;xZ…ìÁ«*d?XÁÒ y-/¨¸¤àÙ.icpɘ‚¯º8‡€?»8ÞnÊUÞ¼I¶7öŸ˜«ì…3‘ë,صRÚXÁ‘nŒWhÁe9·DÀj¯´ €î›1ïß…ÜÀÕ7Ëû¯ñɱ*Xæ“}ëWø¸ï/ªÆÞ ¾°³ÏÐàS~ù¼®ðsÜ‚»ý\GÀòÙç‚SkdŸÀá“dnvðÓI2ŽàžI`ûÉ\ƒÀ“yÁεÒ×VÕrŸ¨•6aðŽ)l;¸b ÷rॷÈ|‡ÝÂý*ØáVÆ·ã6ÆÁÕ·ñY»cüúvYwÈ{-8ãy¯ƒàwpOÞ>ý‹˜ðšŸGpÌ?ð¬¢\pÊ?äÜ.ø0ulq'÷½à3a¯LCâX"b‚L—9›.s°OO—}+øætÞ#pÎ ¬5ð Ïà>̹‹{ðAka#øÂ]|/?§M ,¿[ÚN½›÷|æn®Ëà¥3¥à°™ìsð–™\ëÁÜYÒFðÌYÒÆ šÅý8‡:žZ'í»ÔñyŸŸ«|ŒrÁpì…tžç»[&¸G;è(81(çg/xÞ½ÜÏ€­ïÃzÁ+î“åVpÏ}\OÁ³ï—{!Áªû|ö~™à¶ûù,€Ïõ\ŽÃŠø‚Ç? ×¾ðªøþÞû ÜÛ„À²2Áy äµBইrÊÛ/”ãÍ ~¸c|ìa9Ÿ‡ÁOA,ØÇÀÜG¥.‹äµ¼à³‹˜?¸mó!™³àâ÷'`§ËùPð¦ËùÐ ž÷˜´±€×<Æu܇Ã8qð‡'¤¯`ÛÅÜ׿/æ{Ö“XŸ’6‚Gž’6Êì—–pOyZö`¿§¥¯ ¼ú>§à_ŸÅzÁkŸ•9ØÁQKs•Ђ³–roŽ}NÚ Þûœ´>'ûG0ÿyÎ'`ßçe¹à-,€¿ Ç¡Ì s K{Áiai¿ù¼q°Ã‹ÒFpÌ‹œ?ÁŸ^ä{ÐKÐ8–"7åeôù2ù~g|E®#‚«^•åQðÕåÒ>¾ô5xóë²pà ©m`›7¤.Û¾‰÷jh3xç›Üß‚…oÉ>7ƒoQGÀñ+ù\€-"R€P‡@Û*^ lµš×£Ô‚;Vs‚í×pM‹Öp\ã¨íà–5ÜûÖp_¶_K_°h-}ÁqÔvpËZú‚ûÖÒl¿Ž¾`Ñ:ú‚ã¨íà–uô÷­£/Ø~=}Á¢õôÇQÛÁ-ëé î[O_°ýÛô‹Þ¦/8ŽÚ>ÿ¶Ü®}›ß¦Àý´‰ƒ…¸o‹¨m` u\L7SÇÀVïpœƒ=¨­ “Ú Î§먣àAjec®rîF©-àHj;8ƒ:.£Ž€;¨ã`á»Ì,¢¶5Ôp1uÜL[mbþ`j+è¤ö‚ó©Cà:ê(¸•:Ž~OÞ#A÷{ü..|ßÀuïÑÜJG¿O_Ðý>}Á…ïÓ\÷>}Á­Ô1pt=}Aw=}Á…õô×ÕÓú‚­ösœ€=¨­ “Ú Î§먣àAj%Žqç8GRÛÁÔApu¬§Ž‚Å?²ßÀ±?²ßÀºÙo࢙8ò'¼3Ì„ xé~‡ÇàuÁàÖýŒ=9ìƒà{?ó;9øÐÁ\¥;ÊCà‡å;~ Œü‚œP.øÆ¯xÏ<õ|ß7ƒE‡xÀÕ‡ä»Rüý×ßßÐ'¿±OÀÇ~ã;øåo|[fÿƒgR›Á‡eŸ>XöIì~D~g}D~7°ƒ3ްŸÁEÔ!°Ýï2Ž`çßùspÌïü™8ãwú‚˨#àê8Ø@­Å^õ¨Œ)øÏ£2f|ý(×JpÇQú‚…ÿe»À"jXCS‡Ázê(hjàû5Ø­û°”Ú Î£áÆ?by ÌQLòg`7j+XJí8ù€³< þJ­´0)'àß® ÀBj3xS ÆoeyœAçS‡ÀÅÔapu\G7SÇÀÔq°uK™gØ+ߤø1>­`É ¦Äû¯üGâgvàäM‰wdÁÇO”6að®Ö&ù³Ðÿ'“2qïÁ!Þï¿¤Ž³ÛÐŒ´‘íü¦À”øFŸ$ímàp$~ö^ÚV^× n+Ûb«ÚʶxÁ–ÀMmeœ(x¨-ûÿôù)ÒÞ ®?Õ$ß‹Á9§™ïËAðáÓØÏàbê0¸Œ:®£Ž‚›©càê8x„Z9ݤ´:÷<Ú žKm/§¶‚EÔ6𹿚”Gз‚_ãß?>SjÁf©­`‘™¾àÍfŽIðêø uüŒ:¶é`R–Ï?×F>g›{x+xãÙÒÆÞNÿEŸÍ¾—QGÀuÔQp3u ÜE£VÎ1)yç°¯ÀBj3x.µ|ü96ÂàNê8x€6ʹ&eÔ¹r ÛÁÊ‹ä8÷‚Ï_D_ðë‹ä؈­:ó¹{P[A'µœO×QGÁƒÔJäÙEj 8’ÚΠ‚'YdßšÁ"‹,·«-2·(ø;ŽÄZp1b^̘`oj+¸Gâ=|Gâ=<|1}»Â·+}ÁÞÔVpyWú‚ïw¥/x¸+}»Á·}ÁÞÔVpy7ú‚ïw£/x¸}»›”iÝeƒà²îoàê8X؃Ï#XDmk¨`¸çRðƒKä\G\Êû Ö^J{°þRö!ølOÞk°Z¹ ÷å2ú‚S/£/¿\Î ‚\ŒÁ—q$~×\|¥)ñ. Æ®äø[ô–¹€zËø60L-¸¢7Ÿ ð`oÎKV“ÒÅ*µCm×Y¥o<Ìr¥IéÔ‡öàj;8Ÿ:n¦ŽWô•m·‚5}eyÜŒãK´1öè'µœOR+ýMJG\ì…ÀOp˜fÁPdRN‡tá°@{Á÷qX¡£àyL‰ï´‚Cpž]ø|?q»]-ãXÁGpŒ€¿S+ŸÅRÛÁÇqˆołۊe¿ÅÁ¢ÒÆÖ ”6‚¯”6°p´1ƒ5Ôð}"0Žã~1nÁÖƒ9W€ Ë>\5XöI숣Väž5T¶Q°t(û üÇhÁö×`Š|À‡p„¡7∈øà…ÃdžpuÜA‹ll;¸˜: ¶ºVêÐIí×QGÁs‡3>8‡:ƒï• ¡‹F`.×}#8ÆÀFpüƒ5#å ~?RöI¼c”ìÁÈ(Ù'‚…×ɱgk¨àfêØc4Ç-8Ÿ:¤VþŽqòw©íà2êX8†ñÁ긙::¯7)Jú<å“ÒZp<ñýÜ þë9†Àso”öpÆ2NÜA‹ÆJmS‡ÁVã¤.Ô^pu<×.µœAwPÇÁ"ãƒK¼ïà‰ãyßÁJj/¸‰: ^XÊûΡ‚{©ãà2Ž+p)ulådþ “Ú ¾æ”ÏQü ‡ý£L0)'Èrøu<0Ïx¹I1—ËçNðÊr®AàürÎ `=ñ \ðh9çÉ Ü÷ ÎಠÞw°ÐÅûÖPÀÍÔ1°ÇMWà|êxZ™hRfM”ãVpéD9nÃ`«Jöè¤ö‚먣à¹U¼à ê ¸ƒ:¹yÁÅÔa°•‡÷,ÇaE >à‘óC<@ÅkR†y¥ Û+m¼àn6” žv3çð1e(\ƒ£¶Nü¾Ië“å‚ÿôÉò xA5öÿЂŽjYî×U³½àI~9˜Á~®Yà÷~yï²\©1)ƒjd¹ ½†öàÔ®×à"êøul7IŽÁ 'qޤ¶ƒ¡Ir¯"£<2Öñü‚ãx:^Qk’¿ƒ j¥½àÆZîÀ˧`®†½|G¤NüÞöA·˜” ÐðË[dœxæ­2Ž\ˆCüÞrÜÊòxám&ùû ÷6Y.¦l}»´)‡Ü.Ëm`uÜHóïºDm먃àFê(˜?•öà jXG7RGÁüíÁAÔ6°Ž:n¤Ž‚ùÿ =8ˆÚÖQÁÔQ0ÿNÚƒƒ¨m`uÜHó§ÑDm먃àFê(˜?öà jXG7RGÁü´QÛÀ:ê ¸‘: æßE{pµ ¬£‚©£`þÝ´QÛÀ:ê ¸‘: æÏ¤=8ˆÚÖQÁÔQ0íÁAÔ6°Ž:n¤Ž‚ùu´QÛÀ:ê ¸‘: æÏ¦=8ˆÚÖQÁÔQ0íÁAÔ6°Ž:þ8‡ï\sñüÍå;#˜+çÁïçÊy@ðørÏžòO¾W‚ÝYn§ü“Ï5¸G¾ N¿‡ßaÀÍBãYüùæï5%~>n½—s øø½|/O½OîÃÍàÈû8§‰ßDz\™‡÷Áy²< ^q¿ÔVpuÜKÏÏ÷pÎ|Ú€_RÇÀ ÚÞF§VÄò Ô60L-øíƒ2~\Ø,  xãΫ ‡Ú v|Hîó-`ï‡ØÏ`·Gùm\ü(÷‡à–Eü>î_Ä÷Dð¬Ç¤½|à1~Ÿ_}Œï•à¸'LÊvÜÁ¹OÈ~\EG.–Úî[Ì~.‘ßlà„%̼‹:>I6OK_/8éŽpÛ3Ì<þYŽ7ðÒgùÍl¹T~Ë*Ï[ʵ <é9~o|Žý N¢€o<Çwðs–ÇÀQaŒE´]pZXÚ¾A·…™xÆ‹r]3ƒe/™”ãf·LðÞ—¤½àÛÔQpßKô^&Û_YÆþ_&í•WÐW¯ð½<õU®û`ÇWù ìImßzUŽ“hMÆô½Æ¶ƒ¿¾.ûGYaRþ¼‚ß ÀK¨­à(j;8‰:¶|SÆ,‡½)s³yoÉë€ç½Å{ö|‹¹3Þ’÷.V­4%~JðË•2N ܹ’ß²Àã#¼ïà¿€£#ÒÆÞáûèY%¿'>µJ~Ç ƒ¿®â<°Ú¤\·šãü|5¿€7¬‘Ú~IÛ­•¾fpŽSpƒ,4¯£ X»Žs&8sç:ð~êøuŒã0Ïn™àñëùml»žó*øõ2ç 8Ÿå!Q¶^Ο ®çMo³ßÀ?¿Í8 •å‚èmàûï˜äïò9ïʱQÚÞå÷Oð/›øL#pˆß··ƒóÞ“å!ð£÷8瀿¿Ïï‡õ&eL=ïXWÏu\TÏ='xZÔ”ø=FÁ+£ò‘å8§RÀEÔ!p%uÜJ¨•°?ü€ãìBm£8.Dÿ ¶üÐ$ÿøÝ‡òý:¶ÿˆ{K°è#ö8ŽÚvøý‹8‚W~,ã^Om§~ÌüÁEÔ!p%uÜJ¨•ÍÈ3ó{S[ÁqÔvp*u\DWRGÀ­Ô1°ZùĤ\ö ÷äàê¸èÆ×QGÁµà~ê8¸øShôƒà{8ÄïFFÁ·˜¿w·n¡/Ø@­|†5÷3Þpµ ¬¡€‹¨Cà3ÔapËgüF¶úœãìBm‡QÛÀqÔv0øÞ—g·Lð›/øÝ\ÿ¥Qpÿ—l#Øæ+Æ»P[ÀÞÔVÐûµŒ)¸êkþ|Üÿ5ã€m¶2Ø…Úö¦¶‚Þ〫bŒî1ØæÆ»P[ÀÞÔVÐû-〫¾epÿ·Œ¶ÙÆ8`j Ø›Ú z·3¸j;ã€û·3Øfã€]¨-`oj+èýŽqÀUß1¸ÿ;ÆÛ|Ï8`j Ø›Ú zw2¸j'ã€ûw2Øfã€]¨-`oj+èÝÍ8àªÝŒîßÍ8`›=Œv¡¶€½©­ w/〫ö2¸/ã€m~`° µìMm½û\µqÀýûl³ŸqÀ.Ô°7µôÆ\gpœqÀ6?2Ø…Úö¦¶‚ÞŸ\õã€ûb°ÍÆ»P[ÀÞÔVÐû3〫~fpÿÏŒ¶9È8`j Ø›Ú ú~Á{â®øEî#àÖ_8·€ Ôʯ˜?åü v¡¶€oÂÚŒ8‚­ãÞìòmÀaÔ6ðÁßø}¼õ°IY _Áy‡åº×æ< n¥Ž£ÈŸõºÈ8^pá¾ã€«ÐÜOOþùƒ%Ôvð¹ß¹çR+G±—;ʾ«©àâ£üÖ¶ú/×;ðÆÿÊ|ì`ß9ïÙ@wƒôõ‚³¨ƒà2ê¸:¯äÉ{ö¢¶‚èmà—-ò}.xQË<ù»Öà°–´=Ô^ðU‰ƒ>œ“—èsÁ—p$öä`}Ž´‚J«¼Ä¼¶•Œo§¶’6pu\Ië©£ ’Ë8൹ŒNÍepu\Ië©£ ’Ç8àµyŒNÍcpu\Ië©£ rã€×žÀ8àÔ\DWRGÀzê(¨´fðÚÖŒNmÍ8à"긒:ÖSGA¥ ã€×¶apjÆQ‡À•Ô°ž: *'1xíIŒN=‰qÀEÔ!p%u¬§Ž‚J[ƯmË8àÔ¶Œ.¢+©#`=uT ¼¶qÀ©…Œ.¢+©#`=uTNeðÚSœz*》¨CàJêXO½íò”7Äœ ÞÓN–Á¨Ãà÷8ÄsX®ü%OéÚ>/1O ÞŠCÌ“pQ{^\I·RÇÀýÔq°Ãé2ŽàÄÓe/xÏéÌ|: ÖSGÁýÔq°Z9#O‡C|—¬Á‘xŸ9C¶% ÖŸÁ8àVêØû¯ÒWpä_¥¯¬û«ô ‚/ü•ù€+©#à™gJ_ÁgJ_+xÓ™Ò× Þs&Û.¢ ô,4K_38È,}m Ç̹ œJ·š¥¯àAú*g!Ÿ³¤ìBmï9KÚ .>KÚ‡ÁÏϒ׊ ´W:ä)m:pî=¤¯àŒÒ7¾@›0¸’:žy6ûìq6û¼élö xÏÙìð-–GÀ,ƒmÏ‘åfpû¹yÊŒÁÜóòÿîL°µàÝÔAðŽóóïðÅó™XO‹;æ%ÞÍmàyË“ÿV¬Æ‘xgÿm7SÇÀýÔqðöNy‰÷MÁù8ÿ~ü‡ø7¡10ÿÙ–ð x_Àï/׃yÒlsQžC{;ã¿ë(8†Úöí,Ûh™3èí’§ü,žqð¦9-üu\aÉSÚ î°°-`áÅ?`µ Gm·âÿÞSð׋e{•®È¹+Ç ¸‡ø÷€‚S»Éù*ÞÓ÷¼¦{^â}YpŽÄ›\Ó]ö[|²‡ìŸ0øyŽUðŒKd{ÍàŸ.•ýP–\*Ëí࿨C` gžÒ}NËÅ!þÝh|ê2Ùapåe'`çËå|+8òr™¿àKÔ°ûyòÛ/xá•?àÄ+eþ^ðYê0øÎ•2ÿ(8¶7Æ®+ØÉš—ø=7 xrÙ38¨çð길Ç$¸™:¶êËþ»P[Àÿö•¹)ý°và(Ãuíàë8ÄÏÔ"àòþ`§¢¼Äϼ,àßÈr  ËW`_[©c`µr5æ«9–ÀÞÔVpµœAŸ¤ƒ±«å¼!ø Ë•â<åìb¶ Im'QÀp1ç7p=Ë£àAje`žòçÌ ´ ”ö‚XîçS‡ÀW¨#`œö‚ÇbŸƒ=¨­à(j;Ä9|’åap3u ü…ZŒ1;XÚ Ìg¬¡€P‡À(í¿cy,Â6‚—P[Aïi/x˃à2êø u ,*íÏÊþGRÛÁIÔ0L{Áõ,‚©•kÐÿ×07Ðv ûœÀr/8Ÿ:¾BwPÇÁㇱÿÁÔVpµ cÿƒO²< n¦Ž¿P+6ô¿ý°±ÿÁêøuŒÒ^ð;–ÇÁÂkÙFðj+è½–ýÞÅò ¸Œ:~B †³ÿÁó†³ÿÁ‘Ôvpu Ó^p=Ë£àAj¥ý_ÂÜ@[ ûœÀr/8Ÿ:¾Bã´<~ûìAmGQÛÁ7FðÝ ÜÊòØ@­ŒÄü4RÚØÁ#¹ï}£äÜÅ|Àè(ö9øËã`áulx µtR{Á»¨ƒà2êø u àÓÔa06†s ø Ë•ëó”s¯çPÛÀ;©ƒà:ê(¸Ÿ:.|o /8žÚ ÞC_¤Ž€Ûoà»øá2çØp#ó›§üe,Û §¶ƒó©CàJêxZ‡wŠq¼Gà8j;x'u|y÷‡àcvîÀz;Ûƒ ÔŠk1ŽJ¬_‚Sìp±ƒý uȱ§ŒÏS†Ž—ÚNÏÀ©#àj¥m/eÛÁ"j8Ú Î§먣àAj¥ ÷¥Œ÷ImgPÁeÔðêØÊÉgœŠCüì,¾Àò0¸…:¶™@{pж|”:À‘ø™Kyž2‡øýó ¸®œùƒÿ¥V*ðìWð>‚/ã?lj€‡+Ø·.ì§\27/¸É%ãGÁò›äžÊ >sß×Àí7ñž‚&Êr øñ;~Q°°2Oþì|¤R^7þ^Éü«ð.P%Ëà²*ö!¸‡:vp³ÿÁYn™g\çæ<^â‘ײ‚xdüø;µâEž^æ Žôò>‚S©àó^ÎÏà×Ô1pôÍyò›è¾Y~§ò‚ÏÞL{põÍìpuÌññž‚Çø¼¾àŸ¼qpNµÔApu¬ôKí÷øe¿ÅÁ5ìp4µ¼‡:~_#¯ûO’}eŸ$û' æN–6`ïÉ|7™ÌþO¬¥ xE-Çè¤ö‚P‡À-µ2çhšÂµDm—Na`î-ŒVÞ"˽àö[Ø^pÈ­²Ü®½U–GÁ o“åð‰Ûdy<ñvY^N¿]–Á½,ƒewÈr/¸éްÿTYn—Nežà9Ynçdy<î²¼¬üó·³<¹“ùƒkïdþà…Ó˜?øÄ4æž8ùƒÓ§3p/Ëã`Ù ænšÁüÁþw1pé]Ì<çnæι›ùƒÇÍdþ`åLængy2‹ùƒkg1ðÂ:æ>QÇüÁg3púlæîey|¶xïaþà¦9Ìì?—ùƒKç2ðœ2pÎ?™?xÜ=̬¼‡ùƒî‘c> ö2&¸4Șà9÷2&8ç^Æ»1ÁÊûÜDŸœÇµ¼ë~©ƒà„ùÜK€ÃïvpâƒòÓ X ml ã¡<¥傇ø\Jï]ˆ}3lBà€‡Ñ×Ð6Á‡åz*¸üa®Ëà¼G°ïD¹à3È÷bÁµÔQ°á®ÿÊS~Ä!þ[I‚…J3øÞ£²\ðG–+‹p-âgÁ‚1‰ÿf8'$íÃ!i/¸9Äyœøoy_¼`ÎcyJy€?=&Ë•Çó”ëçþ|ýqÙÞ¸t±´ ƒSŸ”6pï“ò»Gúç:pÑSrn '.‘6à[K¤MlXÂ5ñi¬OKm'SÀ§žæ¼ îZúÆÁþÏp½'?Ãïœà'ÏÈëÆÀ!ÏÊëÚÀÉÏÒ\°4Oþ÷‹Àgž“öa°Óó|ßg„ų!þ»7Øc„9¯‚gý‡y‚c_”¾v°îE¾[Ó_–ñƒàƒ¯æ%~w+ž°\æ_ž¿œëè\Î÷ ð§åì“×û5Þ ðIê0xð5~ç|±_g»À?¯ù˜Á^+¸^€%Ôvp uœGÛ¿Áï®`Á›\ÀÝoòþ‚׬ÌKüÁW²ŸÁ>Þ pB„køú*ŒQ1~Àu«1V ·®¦/xÚ^|‡2ù€…k¥6ƒS‡À½kåØŽƒ×Ér/8}×_pó:ÙÏ1к^æ/øúzyÝøÙzîÁSÞÎKüÎà9oó¾€Óß–q‚à«oË<#àIïp¯^òû¬¥€¦òÛ¯à•¥¯`%µ¼k#sŸ¤ƒ—½Ëïiàüw¹³\Ù”§tÛÄ=xuürÇ*x`÷cïå)mß“Ú v£¶‚/¿ÇvŸ¼Ç>O_Æ1ƒ—¼Ï6‚·±<þü>Ça=Æ^=÷ÿà¼zæ ¾ÂòØëîÁòØà]°À'©Ãàzê(øÏy/À'?¤ ¸ž: ¶þXÎÛàyó>‚›óÿ®JðüÍ|~Á›ùž¶ûD~w5ƒ»qÌ¿{“§˜?•ß-ç|ÊÀ×¶ÈkEÀ{?“ñ—|Æç\ö9÷ÿà%_JÁ¾äs ®ø ×Àµ·~%ǧ`§¯¥¶€3¾æüèk¹Žù[¹×¯Þʹ|o+÷ `î·y‰ßŸ<÷[GðjjÁ¨ߢހn“ÏŽܵÏØ~;ï5øàvyÝØo‡ÌÙºwpûŽ÷\ý´‰‚Ê÷²o˾çÚ>¹3/ñ{Œ‚+wr½»í’¹YÁGvËþ\½›ûvð‚½²?‡í•×\H7îå˜sàœ Îýû%ðý²ß¿ß/ã æÅ¥M- Üçœ ¶ÿ‘}>ø#û ìþ“ì+øÓO27åîÅæ ŽùYö¹ÜD[äÞ¼è Ç98”ÚVQ{Áh/ÿ…ó8ê® `ë_eþày¿2&xǯrn|ˆå‚«¨£àg‡,úïà„ߘøËÃàO¿ñg%‡ñ áþœp„öà]ÔAðIê0¸÷ßÝÀoç~ô•ï°¢?Kïj`°BÉ—c ¼­E¾|7ÿÔ2_¶<G¢í  G¢-àœ|¥»XƒÀ¿—ŸØYÀšãd̸Gâ¿…žÚJúšÁ!­dLØâø|¥q Àv8s2xÛñ2‡˜—+ã€#se|;èÏ•qàßLùò÷äÁgqŒFÌ085OæïÏ“ö!pè ÒÞ^sb¾R{xûŸh¾õ'™CüèOÒ7 VÚä+§´‘Ú >Cì!Ãà™l/Ø­@ÚXÁòÓ žr’Ôfp,µ|š: ¶<™÷,=YöÜz²¼_1°M[¿ìIm=Ô^pE[¶ls c‚¥§0&¸æ™sìR(µ\.ñoñÀƒ…lûŸó•ÎæØÔ^p;ŽÄûXqªÌÓ ®9U^+ öo'mlàãídy<ç/²Ü.ù‹ô ƒ9í¥ÍÿÿóÿöŸJKe§¢:â(À¹l޵–‰ÿÓ–,å3³”‡ ÊóŽS”噈?²”oÁQðÍñJèojM8?Wù=m†[ç*»Óþ?_NV®QŠ”ø»þ¨ŒÂY_e$Ô¥?þ¾F  ƒ]ßWe™yį³—Ùçô·Îo?Âã©4—V8\î^­ó]•¥µNsòO/óÅ»wîÚ½«¥ggËÅæñp÷›ÇOée¶ 6¨¨ßˆk®î?¼kë|w¿t\™Ï5ÉéK¸7ÓÍW1Þå.S/˜î–Ý«º<#Å„—¥é‹µÎàsT9'{|ÑÐ~•¾~ž*¯ÏY]íò¸;—UVš›ó‡×êvq×n-ýµ/Ò:¨«Ôç©öLðwîWRáðy›¾—¹{çKRÁÓÃŒrU×8*û:ª]¥MÇÊ ÓtŽ×¹Üݺv¶ù\U.?î`u£WÐæXU]êñUºÆ7» ´Î/™RíwVuîãõöó¸ýÎZóÂ5¯¥ ÞÏSYé,õãnWC»Kk|>§»‘ i[j¦9IþáVUÕøã+\¦9^ãq_ít;}œf„)ñ:K]ŽJ×-β¬q²†©òzÜè롞2geç>n·Çïh¤›¦¿Ãïh4”ñÓD9Ƈ)[˜¢IxDÇrã+qú&¹J1ìösT¼–¥G¿Ø ŠÏ9/É}jÊ«ð¦ÐÔbÕD]NšhÍ‹Òß9ÉYéñ:}Xp[nÍNº™QÒ6¶Fj^”fî5õ÷.±IèzÙ%}/ê?¤OßìYʽmg¥³\”g©Md9Ðíwú<ÞlÝÞÌ(Ã"šeD6eÙ_#šððl–Q Æ£ˆ÷Îä+!:Å•mkž|€{víjô Ä>^ïèÆ†uóàþ®jL¥6Ÿ§vJÓï­Yš:pˆx%L|%mÎ{[¶(Ù{:k”æ·4½©7šÆsÚïêc™•³Dá&¤©×Üc[ “Ám_b惘XIFTÔ¸µóÿëÆdt£ˆÿó›”öô7º4Þ½iQŠj¥5Ùf“ÿqŽKã]•.¿îéivŽC]xóp—uúe˜3ŠjKÞTÊGI¿ÇÍXÝ›ý™Œ¯Î£‰/wý5ÕÑÖ’¯!uÉÆ³?PâÀf ÷c¼#}®ÿýmk2xÆÐÈz|l©§Ýüf}Ñ?¦ÔÓ‚7kv7Þ7µs”"¼ÛW³¬QI|³ÏúöÖt”&¿jý:=ùÓŠôašõ.ùGsOd“pŒÁ«=5>±ïKª¡‰/<Ùùc ÞÌ®ÿ£àÍ4x±Ã]VÙŒÏ(xS;oÃàMFÉöúÚx”æ¾’g{¦± só›”ePu/r ÞD”ìÏe–¼q˜ÑUÙìÆ“iâ£B–þmæ@Ò9°óh‡QžÇ¼;0ŸØõ«t¸ªþ—TpÜw©Ëëhâ£É1oÞîÊ0¸q”þ5ÞJgíÿÞ·2‚7ã«ìŽw–¥‡ÿãÁ›±Øýñà©1ò¿´Žf¿ÎÙä÷ãa1BìúŠÜ¥žÄöº‚Ù°\6·ýYÂüÑúeF9ÆúeFî,¯©tøšó•lMjâãM3“IE6Éé«tx½ün†.™&†€|„£¯Áâ[Ï„JÏäcü–6ÌÿêÔ¨ ÞÔ¡šÎkBopYïW–?¶ƒKEɾhk”cùáaö(Ã?Õ¸“KÓ“¥{E˜ænvOftOiMU£¿´Õœdp4;Pãa’Íj²ošýuâ÷–ÝE¥”(ã¿Ã9j 2L¹çêoq*Ê›Çíÿ¯ü}Ñä/JöæÙqi5É?ýe£‡âC—R©8Ó­LP<‰ús^#Pë@i5êŠvœñ·N»Aüß"'?¬\(/7ˆÔ3acIý¯»2+JûÄïÉöƒMþ焽W‘:¤Õyן‚Ö:vÉ?]•V°I^¯?Žj¥4‘‡7#Ï>8÷*'´E1¥ùŒÂთj{±Ò6ÉC\#ö¹ [7r¨LË$»3Îj™[±r2|†à¬X+¬¶>¢Z}t»wÚÚŽè¾lý÷†I8>™rÜÙ­¢ÿžxZ×=eÿ™Å_Ñ5?µ)h=¿üHƒè óÎ=Ö„ßmOËþ1Ëþé©éŸÖÝÏ×»D½Þš~bH´:Ú¡!ãzIû+öÇ«öó¤ý¾,öýöŸ<œ²/•ö¥gg‰ß%Ñ~ißgÚ†ÜpxýÐ;áuÅå™^ꟑų÷"„¥xnQ¼¡Ðý°x,‰(v†~oh¸8Rœ¸ñhECá7Ârî`SñìO Ë`½¾_A ÜSÅhÆ—ãÇš?VŽ1%àh׳OA›éOeŒ¢ŽÍE›;ŠúqÏYÐôHYÐP¸øÁDñŒl×Pøàƒ‰eåÀ2 ¬ûïPVñåêm8çDq<˜º {º'oƒk–¸ Ûȸ çøxrµ…fXšûó‡XGÅïC¬Íô°èÓÙG{úÔòË*4—Ø.íd„°8¹}’èe hD1nõu膠Hunqrm(œ&N8‚/í¥6¸Û ¢Á#H5øp·dƒkgŠ&ý4?£ÁÝnfƒO†ËÎ÷.S#íÊ‘NP#…S‘:'"-ÉŒ´ËËHæ«#XöÏÙ«‹g vQçÄSèÃSh8{šŠÖ½¯‰¶b*Y,NfG’ •–ÖðDZþù©´þ’JëÁ»EZ'e¦5<™ÖÅ(ß¹·§É”ˆÔITß5ih"ÒÛ÷gD2%#}ªÏgoÚó¹öþäóÙÑðùL›Ÿ §Vˆ Ü Ãių¯·ü²&1®GáiizC¶cJ4N¼?Õ¬žÉ!o< šˆíä@=npxDû†™Ãpû<´ïj´ïj±ö¼­kbªuų7f rZÖËNîû†ãlÓJ0µEJë½wÜG‚é{§M›’ûXDó-¢íº&[DBiÍLìüìþ¢Ò*$®li2ÛWøÕ 1nöß—jÅÜÂ^y jÊiöÅ”Øgv6Ÿ(é6›4ù[’c‹>Ùºæd9…$ž Á÷%æËõºýHq"–Ü”v%z¤g‹â¹#ÎéØh·Ëþ˜¶¶x¾ýä•˜Û Ú_ |æ¾sô¿jã ê_K¯7xÞ>¨DKg4ø­©‡nؽ‰µÑ~£®j(<û^u}!ñücˆoò×ö×ÿÿÿÄ} |Uòðäd8 Ç@@” QÃ%áPƒA˜!鑈@$`„U¹ rC”ÐŒâ*x€ÇŠ÷±‚Ȫr áP.aã¨l€@Uõ^ŸÓ= ,ûÿö·˜éîzïUÕ«ªWUïÊ2“SÑçL‹‚C’Süâ䤀Ø4 Jõœ®ˆ*ˆ…_ü³{¤¦‚˜™œ"”l6ìÿ'”ü’&”ù““°Žâž6Ðu¼æ‚À‹0mƒÉ÷ƒµ|ßZË÷kùþz-ߟ5ýÞ}«ä:úØyi‡þƒïê¹Ó3ÀR½â ¿xVÏðú†à€'ƒÒˆOðC¢ç¦BIÅ É5*C†MÝi«Æ ïÜvzuBsœBéáÂa‚HÖÓN¯'8Ÿ„kL[”ï¤=‚xÀŒûîl4ÔÚB·H®O ¡ñ²‘N¯¸Ó/nð‰ûqW¨2ÚfCªS=£<#G޽˜M ó~)¶ÙlBp´R\ ¿ýâ)A<(”²„Í^;¼°u/ʼ‰ð@¼) ¼6ø¼óºá_êbAô8á‡]ì’kÌÝç¥ùåŽù­¢l¶â™©6G©~ bƒ³³"±®å£È:oVÎÙ…R©ðzALxcÖyIrýÄ"ÕÀ†[ÎI’̆5Ȇ9ö²a³ÌNcè}5šH#‚D¢¸ë°M^›m“7Êd;‘l¡,× %¡Ùí’ëUà+ÒS QUP]¨ ;Qrm@‚ý¡šì$ɵžN9œÞ”SŽ&)ųm¶‚&‚pJ®ñPCU\¨‘ û"8&z"w높_lÛ`±ÃRXw)0¬c@m?Ş®]Røœf#¢Ïä#ï  â^ü´ÍŽ·X¶(Uiõº´Q² ®‡¯‰ØʺP6$9GuÉÕû^ä—¹&Õ*¤)²zÑùȨГ{%Wá"ì¼ÇrcàPЕ¹[}ôP®ü+/—J8±þÉu”A)&:²Õ ofYðÞ²RâLÙ’ô'HLœIŒe¹@Œ»ƒˆÌN Œ{ ‡îëbcŒ¬‘ý¢ÏvCr=A"]Ð8tüþý=øŽÒEa³¹‰Š¤Ìœ3R£@æ©A1#‰3R°aD¿àÛ µ×A’|Prñb± )©Ž*H,©Ž.hVRmO”ÛîmâIð¡0OŸ'JÅíÀÃ{Ï]¦Þ.’Fà+i¿PZ^x(‹w6òû_ ñÓ½rþ‚újÆ ¦äګܼ¾üÏT|éµWu„†ì€KáÕ‚HÖA(ÝZ*æîf,. R%êC\Úx9JžÆoù[@þffŸ“„üF „˜-ÒwU$ײáØôóØ3ÀV ½?tõz™ ÕnMsU”eƒ¨P†¤8x3€âÆ$4:ôŸD¨0‘xâü3T ŸH†²UPÅ`€ÎvbŘ¢ªz¡×¡’ªF¡—ðO\h¹¬£P‡êT:åÃ)\G ç_A–웉Ö’¾¸®a*êO Ur½;LKjÄá‡8Þ‘oÒ·ýø.)¿ÆK®ûËPâãþ^£ÜÍ’ëÔH’ÇúôðëH=ýøEÚŒ’cÜÔë쬴&’kÁ¨ó’cM9ˆFkä~Z”äz{±±9¢s2ÓšˆGm¾ìÑ$.-kÈ%á•&7 (Kgªº{D®z«ÚŽU/…ªÝŽ5›qÖc¬¯Ï€ðþ‘aµÖŒTku›ÔªŒï¡«÷ãhØ—\Óîç´üûí /Û?ýäþ.{q:ãù£¬µþë ®XÏZ›-/ŽÞ`}ÑA¿’\ÏC¥bFªc](ñ‘ ¨­¿5z¶Z?,’EÞ‹ªŠîVîXs€Q4(ê P´+t?ˆ–cÍÅÚié¹[OÏË9=­uô¨þOé°ŸFS¡æ¼ÐX¹Ð£]%?~õ°\ùC<ÙÜ º­lªbP|§ Õt@»rágÆ ÑX凹‚Õ4êºNNCûã|úš& ²ùøÌ0 |µçÿ$_Í/~ ªnó‰;Àf?…æmüÃÌ6yŠ{%Ó©ŽÎäf¸‚JÛK©ùÓ˜É+ó:Y-ƒÿÄZ®eµ$² üàY”B%Éÿ™d§;=bÀž~²plðΨôm³GùJfÛmÁØú…qkAØl鳣ŊôMŽEãÓú‚Þ¥ås: ù•B0!úÂYhà9jþ@YAr’·ûV¿XÞ}OüBr}r7¾ß[#ˆÍvÉ5!óG9ƒQeýÀNM~ìô¡0- :6é#íÓÒ…’òh!Øh}¹ ¾HýšöÙvèØ®‚ÞŠèM<Þl‡äòæòN‰EH×môßâñüåïóñG€¬yàö‹'‰Ûh€KtÚ°WGȰæ+6 u´|àð0}œ0\ÕÇ÷l\ƒcÒÐåôäzF(ÎŽ*S©Z:TœóTM™ æö…úfÅè” CçÈ0v6y\÷Žã´ý\¢¨ŒÖ^¦lïÝÆG7jáQV]!HnÑkÄÎŒ©ô­àf,Ãý+,³øIò`tqJu¹¶Ô¹\©/«¾™Ç•ªõ¸ì²Çåy?*SDY!:»Kãz}ˆÜ¨4½Cnÿ$—ûr¡â§‡£«EeÒC.Œ?ˆ÷s/g`€AlÈÑ`óÝ< 6²¼É÷¸,3?}¨¦Ñ ¬Ñ4ãø-ËOÉYû´!Bþ6ð |~'o^Ñb‘¸‘ø£ üpáëàÇAª®„ÍðGsø± ݪ†ð£ :>ÑÐ%Z±Täѧ“ÇuÑäã$¼úµÍÅÜœd†ÞÅç HCßKô½u×ï“Ðܤ‹à"f%-Ö ìKù“Ý>&à/pP0r^´„@¯k j&óTËLd˜ô_Ô¿2J­‰eý$ŸI¼¤7ul£"sY®¤úÑ4%é•@_õÅÔ4$¢q$Û,7óÙ™ ¹™ÿª~µâì°Š/þjý¿ÞcUÿP&¬Õ䌊ž$Øí‚cÝ·QSíØƒReUcAÌŠ§ ›‚^X,ëš&¿Ïp¡gnŸ¢ä‚yNüá;¹scš¨mN |â.Ée£ßõübCŸ¸Ðü)jjÃÄßb¥ }…˜Þ ˜–¦w^ ýT36H­Ÿ“êï-ó€ó²‡”6AJâ–J—JìÕô<#Ñ“È "•>q+ozÄ ªö(M×8g3⥛cihÜ/VøÄ=&Õ ¿å?M%xp‚c¶ì41oÍ:/ùÅP¦83Ñ/N¶w?,äïõ¥5Íë‡| ßn÷‹ƒ’|ŽußöLP|Òžª&>q0o—_ÜÄû¤A¤ ™éç דÑ!Ù(¹zÍbC>ïYó ›ïG{µr&³n¤÷C¹»¾q&ƒg£Áz%ÚQ,šÊ_Å^÷göúãÛŒöz47ÓFs3íæfº&Š›éÁQÜL7Йi A¶Ïq'y×ÏÔÛg='“9®xð¼T6ÙÎ2W•G>¨Øë>ã ýÌÿ½­±§·Ž®“=½h{wk”ÎÞíÌ3±w—®ïq}O¸‘$¥Ï U©;Òïz™Z¥öÖQ© úUýö^¤~{.§~{ëªß3ý^÷;ŠèŽ¢‹ÐïL½~gÖA¿G)úæ?eõqh ÷ŸÇpÅüRVÌE²bþ!+æQµøOÿy]SÍq¿ Ý‹¦+ªIÞRÁtEûÖE™zs:2ÆÄ5Ð'U‚$ÛÒ{ž¯Áéâ¸ízÿôÒôñ¤FßνüþÓ<›Z¶eýÿ¾çêõý›aúÎzCö ¾¡ñ â¸>dÅk)U¯Š½ ˆ'9~uµípë°Sr% À^…`MÃϤO`öcëCYëC/8Ö€X$ ¾ûùÊÂóÒ‚{x|Æô»‚«¨Û‡­J ³/ãüÜfi4öÀ§·[¹1âÉì6·){+àä™Î¼[…*±¾yS)Nƒ‰›LÁ­` ösSp;š‚ï˜;ç“vW5ö‰wpwÎ'ÝAFàÂÍZ£*¹î‚zÃò“ÿeä0ÜN¢Öq ônéá‚L%„{RLÛ-gP”Z©Ä™ É5kšìð£Šsñtðˆiú)H…ÿ8wiUs¢2©Ð«°ZrÍC9,• ÀxõGþI.iªl†Äæ¼åï§b˼¸(NÕ”-+”›M6ʳŒ‰Êt›¿#öPv¢eaOî …ê ÂÆ¡UÍ8‰Ò”ó’/8€ç?¦ðîÊN´ê-€u’´`ýe…vœí}ÝöDÓ›8…€Ï•ñf¸kfÈ$W§»¸s™0îd[|N?PØMƒèʦ—¯Má@»àU_zõÔ™ó’+kØy©ê¡ôt!N‚:ž*¯jçßÓhp¡¦(tÝż´£ª;{è­o%©[yUýÆ©e/ŒLc’Œsù@j&Uy¤ñô½(6ìiç×í‘d4E•ÑFyPs9$^ÕoØi¾Î©z¦é€(è8»UC^Ä-¡#Mp®ŠfI°ÿ')ù:«ÒLI«1ÛŒìºÞüóÄç'¡ú¶¸¾õIûæ—Ì¢îÁÉ©0aLÿ† „„ ÎeA‰/§€|Àé S@”}ùG=Òf9Åûê$5Å{¥.Å;˜¾°6 ß«Š}ž¤ÏÚ¶úš§L#§Y“´eþ:¬¤Y“tiV­þ—7Ñèÿ1ÒÿûUýo¤èÿhÒÿûõúï“õÿ~þ×Y^’þ¿ÈËÛ­¼ìšx òrå勉)/ÏM4È ÈIÈ ØŸN5ä¥p¢•¼\3Ñ(/wL4ÈË¡K—ƒò’MÃŒ9M9K÷ÌhZÒ€›ÜEÎKÔ#¸ZÄ.¹&¸™Ù@VAœ›ê>½™Ø0^ÎMÒ>mÁy[^¥(’kèÇLŒ„v…îÀ6©˜US'rQ,(Gß{ðyéÖ†6[1Œ §êsGù=7ž¦\ÿ¥N¹†ËÛ¼_ž!_þ­dœ¸Û/›¸ Z}Ûÿ¼ÏyŽ»Y?kq_NsÕ›½66êmÁ|éˆÉëãëZG/BÔ]ilÝÁ”‰²Ù国4=ås߃ÎK¦Ù+üÁì²Án¿èÊ…6<ónÁ¦qVòNd¨uà=a³ÇÍJè /çõ$B0°BrÝL˜Í./-/º˜|+ðjîôdàu¢NŽ>ˆ""ƒÏ®”;/…¶$ï(Ú’‡Æ)¶äz»lK^h;NgKò¼Ü–`~b‡þ&ù–²ñÝÀýÑ,¬ë?0¬ëŒ?0¬kƒ?0¬kˆ?0¬;ûU´6ßréñÏÖ¯¢•ø'õ¿‰&BÚøg`Xüc‚Ð5{dz|âg×´‘¥ 5Ù)I,¯„NÇðh ÊÙRð_šdÞ^¶×®–öV~íÅÉíU е×ܪ=‹ü{Ðõl.´ùö^Ë6qN:ÔÚ”Óð `KÓðVýtõÄúÖõM_욢dšZM[äƒ7:̺½­# ½#{"·—»éQªÿ,P×|ÐuÖ?»–úÏo×Õ§Yýš1Ä-”=•Œ+V0´±|H*¶—Š^rÐåÄ&P“à·ÐÏûpB¤¤ÜNým&}ò¥Ú/þ͎ㆠ-9-ÞAj¹Oü’òqËL¼-…Dˆá@øü±,l»V(ùù¼ ŽL†Bõp,ø!»,±„ê{«šDÛðr3Œ­ì%n³™iK{Ôª«Ñý¼‡Û£¯öp{ôùn>Üý_åQþwŒ!ÿ;†ås(ÿ{!ÿ{šÿBùß1ÿ›üïw»U{xkÿÿ=,Ý­³‡;o¿¼ùß^Jþw(å{iò¿½.Wþ×(?gn0ÊÏÑJ.?;+¹ü|ZÉåç­J.?Ë+ùx¶°ò2gwUªý7Û÷¿é¿ó»týרgì?¾tOA}ɵ²7÷“0ù›¢æ¿SÀ²¼iÕ`Â…éØMI·óÅqX>§z: Â/kFsEqõ(âÞJÇžJ“Ø óoSùÊè@ª‡Í4*nÛ6P›´Öuj´ì¶MiªñÒžMG«_IŽš¾Å½7!q9¢M^[íl™heö„Þ€ÇÒò͘õÜ-¯ ªëd×øƒpC…tMËš¯’ë…Ñ´¼ ¤Ö“gCÁ~T9®ÙãÔxËæÀ­o1[bhðèüãßóTÿ؃ÂìU@\O^Œ³à¦Ú"º©kòtnê»y&nêyF7uqbc×y¥2¾ÊÝÖ—úc_^—gÑ—_ô¤\íËî0éK\©¶;4Oב{ÓxG>8ʲ#çö´êȉ£”ޏ.iJ¿%b·ÉÑšär¥±NJ¾¡ý3p”&~+ €Æ´4EµvÍ£t]Ó|”I×üg¤±kþiÝ5Ì~pýœ?û¡ÅHY??,àì›y£N?¹½aj)žÄµˆ)¸Î™ŠïÏ•‹¿*ïÅŠòBï\ ÀIü†œ"·eÀU‚lH®V f¸º€\Ÿ?@Ò7Ïm_zzÖ±ƒlÀX¥Ž!r/÷`uÜD¥>…R7§ošež¡2Ý”2÷Êe&ñ2Ìj“éÃ< Ç(àÏÊàî|’ºm®Y|„öð‹²‡ñ…X…Ó«ÚCþýÆBú~?}?‘iüžð׈áœ6¶$%ÜߺfRÑ{ÊéÍÃÄÔìºHnéͲ-[¹¿Ÿò“ÞP§Þv×u´žžO¾?Q£î0|]qÐ÷› ûþ=—}ÿ¾Û4ßµò„%Éu ’ìϦë>ÎÓÌFòecs ’¡ÐGÊI0qŽ”õ0ÜHk³)›$×öáZ>ÖÑeÓô[ÀmTr.ªÀQr.º AU›Sç©×Ÿ“N9šÜ\6Ê>Ö1 *”sô×Ûxª¢74j%?¥ën¹¿¾Èýµ}’^z³Ü_ Éð½:L×_?u5öΟBÝt퉻q½ëɳ’gm<®Ó¯§6œ­=¡£+ºo…6Ì–à˜ÇÖú7ÕØÁ-¡ûˆ>|f Gö d’Á@~8Lk wW¶Ü |Pƒ÷§»©=]ûØe˜jqþ/ŸÆ~ñ$ÈAwföÅ^gÙâJäRY!&^¢#šÍîÒ™Í#w™˜Íò»Œfó3š5(ÄÄK“0ë \ÃM\ŸRŃ‚ºt,W=ľnõúTœE®D“Ñ«7"ÇrH˜¸ü¼MöÂ5 ¢xkïNÊÒówìàTieèš ’d.^øßCæ³v¼¥FnÁ¡Ì#f»î;VÍPXÜú¸·Å³¶ûXÓõ Ú¶ÐjŽ:±kW6††¾Ö] CŸF>×úOüèC½S41Q¿ç§=BèóÄ 5ÅOÐ;>Úo×UA²‘aÃû®Ðç &¥2 ôN|¸3Õècñþøô*œ®G«‘¶hõK»',ô{î¶Ë±ÆnÊȉ‚b/´Æp°’֛ޤ%“L·—ÝJ]Ü0š''¶ubªLxæ{ìõew¢«ƒ¥Ø–Vêž[Ùdâ–/¡§ò‚Î,Ù¥/Ú®Céea"Tuã¯g%¾ùÍçU}ÎKU-à}{ýûÆ}tÝ$ו}¸Xúï ›Mñ‰åÜìöûÙæ`‡.>'Ifï‹,Þ›ÙG9:J%i’œj´N õ…"û¿ÙªýÐ]¸ñh~–øVQ'ËñF¶Îr¼œmb9Î6ZŽùÙµZŽ­À}9õ3êKãZôEá—G\ï÷PþËÃç·…ššÌ¿w‡høÑzþ `¡Yè½³°Ç)’ â&?ã¢;"ï¢ãbî.º‡¹Ø{êBD.–œ•dû©ÿ˳TL¢Òøgçc ´ëö(2t×åVpagfJp§*_yæ0™Ã࿪ÉÞH˜Â‡S]d:® õ…§à˜nSÙ—¨NL¹RY<ß}[w_vRÆ;¿è|qcÞ‰A:ždÂóƒŒ<ß6¨VÉuœ‘LüÝ4&¿MŸ•pD‹ÏV¤¢7â]“¦‰ Œ@=ä/ ä@¯¸AÖÀû£i+;Ûw„ûÄ:0W‰f˜²Ýz»¶p]Gîà@ë·gŠÞ<¨v†f¨CÙXª'ŽFtàƒš<§öŸGº¹ïI5kÄ@ðS—]Möü”½ÛjÆŒ™öG®V†FV³›O—RmL²ú·¢õeíiË4xÑàSµ’«~Gþß8ÆøOƒ7h<íæ¨ ²ñÉx%¹žjOX|‘ºk`Í©ÙË3=’kQ{î`Âï¼öl,¹©*›^ùç Kü„7¿°5ßÑØ<•¡áróÐ-Ûó@¨1ýÀ“ J6ºyøÔ—§åðiÐ>ÄœìÀK};0ÜŸ̲Øþä,zä¾âèÿICoŠ&³Sð[EL03y2šÀ,ãØÑ–pE´Êbðo¢7¼îöðÅVúyc{~Š.^Gýu:êh¢£):zÝÀZutü_:eÞNhI4Òs”D6n ÕRúÖÔ§Ÿÿ& e°½ÛT²?ß²?N2— OµCq=ãÅÐ5ô¹-u¤#Åb7,µ?ï—UDÏh:ÀÅA\¼–Ê?•L3¼bVþf»ë³RåY [ÁÁŠÙ§DÜŸZ$ˆ -èxÁ‰?îÄ✙ v äÿ¿‹ùo’Åü· ˆ÷âÐÍŒüv¬d ÿ ¦ Ã?`p.y†¿+#¾G½š‘Â'™@ >¸ƪ`\î‡ÕÒº“òËWÙË ð¼®[¹˜½ì=ä—3ÙËø²—ür,¾Åjj¬ÄN¯Ÿ´É_³®C&N¢‡þì!ncYôàaEôЋž\%#؃=—ËÏØsH~N¾»tÞF,0ʘ@  ©ðŠëi]2›º ô‰Þßý¾t? "¢þµõ«ú÷12\“%a¢0½Ì»BzWøÊ ¿Ä¸¿ ªž—&kÕïcö“=×jÖÎþFÍÚoÄjɵ¯íy©TšaŸ'ÅGEö@ßù-ýs·«©’×Úr ö@[³ô4°$ [Þ|»’ûÛy‰VUCéÑréoo7·˜Ÿ™u+Vð°¦Ñèëx±×µÅTÿ±â;ôë©þãÊS&£fýHªku/ç FLƒ­œd‹ì컄e¯Õ)̲äBùé<‘ý¡ttØ 5üJc«7Ú^s^"Ï+î0[¶O•7BÀßE[#Ãn¶<Þ$¾úŠ®± o—¯vú†æVôõ¹ºVúÌâ_Ÿxô¤ôpAŒ¯Ì¦ ¾Í'íÆ—mŒx’t¼…¦½,ƈH ;Šçº±0ŒX %רv¼9Æ&+ûÓídK^U8÷†)m CjáU¥M1ÿþñw¯6þºäŒäÚrw6Û¸“Y·Ug?gêœÌo3MœÌ ™Æ¡p}&67»ÜtÞˆâkEšXwT(©:Ì0£WîE¶ } Ó|?¸ž£D8†[g¯ä½åZp3Íëf¨ÑË72Ô[môIH«-i£‰µkåï® •¿^??æJ]¼í¹(_¾8CÇæ&l‘adó°ŒZ}ù‡ÅÛú¶púV}¿÷=¯]9Y¡ø&@héþ±€ŠõW*žÞÃ+ž¡,eîbödc'®‰aB¿R63¸‚¿’«3b .AÔ«,ÏÕ ma@‹e {_`‡Þe¹Pzù‡‡½¬ÕÅkÚWëâ5¢(óѾa.^•ÇÈðã¬g­-¼çѶð†[èÞBYX °kma€®ZØæ káú°®õ05H‰°Ø-¹~rkAÄ.ƒbl+Ž„ÔÇn-RÿpÓüb8RO¸ÃæÿÝŒ±uje°®•µ²ËÖJ§°V:0 Ä:µòkm+?÷ÁVî oeSc+yfÜjÉYDû2©–í…Ò­…±ÝË«&Ô*½u覺}ÂÐm†®«Æåx¬˜Ë.½k™í·\_½˜ð··•}ô&*öQ>Œµ¤˜ÿN’ó g;3{ëlç­½MlgRo#ÙWõ®Õvæÿ@¶Ó˜OÒ¤àä’7R c?%…–= [ßâÁxø8ê´.4*÷S¶…N~U»¦Êc܉òHø@*J@¨—:îë%C½ä2Ž]fÓqê~{·5>W‰i½$î¾¸Ø þžžÄ—XÊ®CuyX‘8Ÿßá´9Úg&»á別Ræ³ åR‚˜eÁdJ¦Âïù<ñ3“5ú–ëۘ㔳!\^WéäUJ·’×%.£¼:œÑŽö^›dÙëøD9ê¦R¤æâäw~ºN~g§›Èo^ºQ~G¦ëä7ÑD~_þŽä÷æóŸýVÂŒÒ b•ë÷ÏpÁy½9¯ ëèû˜Ñxÿí~ÿfæSøÝ›é¦ðÕ|ÿ ]j¾ß!߯Æã1¿{¤›”áÁ¶êL&y;Ss„è¦ÆIÞ4öaJSFß‘ç¿zZå{½Íyʼè\o^OŒÜÕÓDFzõ4Ê­ƒ‰lãæ ÷/aýÇ7Zy9¶Q–—MeyÙµåe^šV^®hb./1M,äåSyùú*ëaÒGzûh\ZûèÝCaöLÝ?'”-¢qßC'CøÅSÞî’¿lV²'²pB‡fxpÊߦâÒül.güWJ®èçobb¯§ùƒ4Äö<%6Âé1î«@7Á÷LÜž"OLiût'oJ ¡¾¹¡?òÌöDžÿïnµÞâËFÊz ô©/nÍʼnnúùÿnfóÿÝÂæÿ»ÕºæÂqÐlþÿg_Úv³²/«^¢}Ù×UÇ›/ºšðæ½®FÞ¼ÓµVÙýc?ó׬ò­ºµ hÖ6ì’\ê[­eÐÚ£¡wŽ(K–×—W)<^ßr)à ЄúaKÔñ™ÛC6×N{IR¬mâ-_M¤ØÖ6×àòú¸ÔÖb­¶2-Me3­e²–QXà§_ ¥I\K×´Òji jiª¬¥Sí(§ð–ëf±‰n¶ÀwÊ’L%ôçï[ú]¬ôÑ[_ÑÇ‹\ÿÔE¿þ©‹Ùú§.a럺ԾþéKEÞ¸,ÒžŒêB÷'t?Lê—å²ûð,ôTv {.ˆ[nÒbWt2Êç {Øb¬cR¡Í"ˆýfqÀÌN‘ÀUÀaZD û=/a‘¹ºä‡LSYC¡¬?Hkãþ‰d8¼n·$!ëSMbùiŽ&¸•—ÔçÈþ’nýˆ~–™6(¼-Nc·Í毳2䃜:KþÃ~ŒôæKKþ–ôRŒÖ’_B¼7£Râ øtŒ6¢óŘGt]c4ç_Žìçá|’ý4H´\Ôã9ÙYaü‹•ÙS¨N)k¸³ÂÜáϾBTéƒÓ`<6ÄÈ“|ú¯’«UjÄÉØ5-ZNœÝG¿ØŠMƒ®¿ç‰Ö0Ø,^€¿É‘ㆻ$‰,@2Y€$;_qÈRÜî +Gn×­N%Ó྅¯[Ý}>lÝ*ã£q74ýKIÒ¯^ÍRx‘dŒ/#=A›5=Sl¦ôúÌŒž_þ—ôTýƒÓÓ-ªÎôìßk¤çS©Æ’ž•ðÍ„ž«MéY´ï¿¤GüœÓs·­ÎôÔ £ç× Öôì¾`JϸOÍèùdoX¾HÑÏM¨ŸÖJ9o»$ÏÇ’r¥†ºòCŒ«8ƒ$¸þÉ’ô’kvQÝøñŸÝF~9oÍõçkÂâ{æÐÊLÀFÄ þ†1Ë/~OcŒaÞò•R¹g—‚nhï½ðo2w“èÖœ²\÷b²Èó¡1<ó<-ÜG+³Sh ‹%é(¤,À j‹Áä%ßAEªÙiO¹€ôj\§Èl>ÞéC.äŸ 2ªÎ“]ÈjjèæGŠ7 \òZÀ´ó%˜^Š(zO‚ì+;+bg¦XÊÖ@.© ó5w„> whÊË©ow}E~ûÝ+sO ÑË0ö à³è@_¥üú1Ú‰Uæ6 ʉyàÆ)0jæ´qúœÜÆ/çX§jJšùúù \gž¿YMMÈóÈ$>¿!XMpˆÁ6y¯ôÅÌ/_­sŸ»ÚÄAœ{µÑAœ}u­[Ð7¼ÿåãͲÿ2÷lÆ™q¶ÆÔ}¶Fõ_ÔóÖ²ð¸Ͷ½0W^rmw^b·|a ˆ´ÄãÓ²øí|xÚßCTv®ƒá,ÃéïÛùªª„ß›Ñ9×ôŒÖ)ý@á `JÐëí8>ø9½jÙNøÛ ‘gUN%ˇ6#£0”NÄ;m”µ…ÙZyÌ’;Ù'žd(f­ž³KÍè0„ÝÀÝ+*$I=‘íVZýµÝ¸DrmLRXß”û ¹kŒ·bõ âÁl=’ØÁlt¡;˜mª_|À®T:È)¹T Æ'‘ öÓ¡“ex;ÄOÈß…g±­÷¥ïœÖYÊ…õ߯ 1›Åu¸Ú+(àR0¨ê-¶¸–ÇöÇVÑ–9vòÑjë)DZ¥Z¨zŠ~Ù"å(¶ƒòjò³dùÙñYÆ£7²Ý‘¦xî( «QhdØDÝÅî¿jÔ‹+þA‹Áv…ž\‚Z0ÏY£3gDûÙ 'Hêñ£sJ#âGçÿ@›Ÿœ©;~+þ¬Qùl†ø€^‰'ÇB óÏgñè΂+}âmÜ$×ðX>Žº’î-’·_i~~©b$Ø1'‡è"8:B¢}}¬à˜¼ŒÌ¸Nqá Êî§àb‹•˜‹Qìtï i 7â?Ì´ ¢sd¾BbæØ´GLL>]Ãpn mãåeàX,ðF…¯ü³a¸ kà3˜'“@ÍŠ´<]c<4òþÇ6ÚüU¹rþ‡Pç@V·ÑYûwÚ˜XûÇÛ­ýcm,×ÏŸwÂgr¤Ë˜zt]ML§íÃMiú¶¸# JÒp¡fDJ®Å™õå`žÔ¾|åOÞ—ý¯8orff!ç߯PBÎ,ÝE¬Råô)ÉpÊK?UGÐD*Ìj‹²®í§?Œ£_Õ“!ɲj¬@h0°Csš£<‚Ê'9D©'9¦§ñ¼ŽÖ´ÿ¸5ภ|µ¬>¾•÷3ôjIçÿ´bc]еñ&®ï{)îÖœÿsçPeEº?ð ;Û(PZío‘Û±•¼YCȶshp ëóãU½ÈRÈAã‚e¶#:„›{啈÷µ¢k&£øi½ÚdL’Â{l2?®Žå›I˜©©`\cE³tMZÞÖ¼ çO,±¥‰2^¹‘óçÜï5FþXžù{¢ný'¸ÜguZ&Å`#'ÊZ–§×²¼S5Ò¥o9'Qg6¦'š˜a‰F³‘“È×éx˘“\AßI •®ÆþbÇ[Î[„›î_n)ûL!}Äõ}ÄU7}RÙ˜Gd‚]Èk¾ÿÆr¼aü4+œÞÒb¬¹ê¤ÆNAÄwc}ÙNYŒ3ž¨áÈviÝGZ»Ôè'û-dY;)êÄï5l¦¸.à À—ÿ6¬èWÏúÅ 8×¢#?Ϫƒî‡QȈû[Xˆ’ë +‘3ÀRÅu²k ¶“¼y†€¿{Œäý_ÜŸgLœ‚?÷{ ³µšü!ݹüÎ8|¢¡wþƹWè2ŒÐy&†uAÓ'a·2ø›2x§˜Î8˜×x§ué¿Õ˜L?Dß.íø¾žõ ă~10™h/E›jæw6×éëææ&úúFs£¾®j^ëòÒò~ç²!É÷úèÀ?܃¯™ú{ø2%FÞfÉÿ mgþñx BLxy?æâb˜®@håg9õ­d²Ù\àO?ÖHU æ®#?b}ãýâ  ~Ú¶É$c[ì>ôWi›h}ñßDÿÝY_¸ÃÏ—”uI ) P„RtF7™iœ‡€¸ÿDgH°ëeZ+Øå˜ï4E­©Õà‡ë×hÍ×Åðµ¸(ÖVÛýpUW,tíÃçEŒçÝsÓ|±Ðê·Õñüýù8žïu(ãyÑÛòx~šFÉÝxþþ¿ùx¾Øa2ž×Ùÿý#²ÿ ¡˜âÿþ%1ÔÛ:ÈÿuDðëâÏVËõíjlâÏž=¡÷g{°AØÜMT?veã~l˜Ïú•©{j²ÿŽÆºçÃ-ãÝ’áªÃ»/á]jp¿S hoÀM´Ÿ–´¨:MŽ’ÿãO¹šrŒw©Ûó$×[ Ô ÿ4nÙÓŸzRA¿ ƒŸ“ªÁxñïÄé›åTµhîä˜~}šÍ?ŸB½ÿäpAó¡,¢ ñ/óPˆ'(Bü7d!nrµöÍF:!þåâíŒ÷7àéq«é˜„ŸÚ£À•ÿ©&4U$ -nއ,®'³‡àÙMñÿd-»Qƒt ‚ÄßFçØ±+oAŠsÂ2=9ÚL?7¹IhIøî@'ºl”=“8ï£s9sTŸÆ|óÌD2],Ý’4ö]ÎÇ/h]×¶ð­›#ûÒò{÷­` ï«Nx’sÒ–’êGÉ/Øù[@r÷ŸÕáK÷ΤjÒ”&g®¦jÈ­övßêí^ί«ýè;JÀ ”àêFo>Þ’¦"‰¢™ügíñG WÇWšö‹$?Fæ=àyý†Æ´Öräƒ(ôg%×+ßÔ—^Åv*ã1§¬¶UNTAU4R”J¸½š¾§êMÁ^y>¿¥Ét«¹H J¶„ g£õ› Þº¸äxÁ,MÁý!·(qiæÌÌ!ªÑé %Ϥõ hXÝáçÛÚC÷<Ì{¸áayw›ì­Õú) œ†\—§¸7B.çìå«CxV31¬º$¬?õâËNEþ–B’½¤ž<S¡]I±^¥¦Å%o¬Éß…ëƒAîz‹³ªÜn¡ ëk”a™]Vçt­2W”!oßåT†7¾Ñ(õv¦ §l²2Ì,R•áúÊ0ö8HðgŠ2¼xØ\ž>\»2Ì IRÕaJÀIH3 ú¢™ ónUk‚qÅÓé>§ÐÚ9T9ïUYÎg•ðλ怩œ–-uÊ!¸®%5ç¤PáÔˆ÷H®ØÃº4P]ó §p¬Ùo‘¯¸ÿ[M¾B£¸ÒêšÈùŠÌoùНió#Žr·.&Þ2_á>zqù çÑZò–ëßãtþs–I=Ë8óÇ äÌÛqç!Geß9ÜÄé<ç‰q&žsVœÑs§xÎáë=yþë9ÿ«u4ùš4M†ý ‘Ê…ÀN¤VÄZzß7š\ &­ŠOk…À$¡ÞçY¶°Ûîqð¿ûe­ 9ÂÑ=Ǯ瑰_ÓŽ09H4ɨ$Y”‰=Rct1ÂåAžçù«,ò·OPô«¸¼Äš_CÖ¼cÁšG4¬‹7æ/™5òÜ‘‘=;%WÒ>-­d¨‚Xº*ƒÕê7-£:~Í…ë¹DolY :Á,IÆ;ÈÁ×J&ÊŒo©Åv6M7ÕI~¶üŠLz(Ú‚Ií¿6äúÜÖ"?Òaù¹z¥–-Ís¶ü3ÊR~ª]¼ül¯Æóß`(cSB¡UÏË¡Ðu4<­ ÅnJ(ôÝ! •Ã{C÷¸.Ï‹yÆj¿ø=b–§ãÛ•Q¸´áÝ7·†ÿ[WÒê†ÂñÞÒÓsîì~Ø/NHÎ ˆížÓŸGá%s¥L göùÄ/ByÄ™”S…xz  Ó7›.<#¤÷³O늋 ÁFëéDÝ6ðQÚàIÿfÖ¿dé’O ¢þ¼€”ÞFQóÇž‹˜ŸØ÷£* žã‚0—}üBMäü„eþéE,!ÿôîq¬ÿ¾ ò´¨!ÿä\^ë¨ß…gÍõûü1µ[ß_Æ»u¥ýÖÕÔDÖïZ”{O5e;”l‘O&C-'¦=ð[(y™Ê¤)Ⱦ5 C~~VfÈ?žÓ1$i?gˆt.Œ!Úe6„æä€ø…y´2e²O÷ñŠé‹ÆâH®îû¸ëÊ>i^%î£É'îÀ‚Ø¢øZUg+èãǺcQS›ÈëJwûèH¨ªLÄ÷‹ƒ/ð¹†m~\<áôáýŽ{ª® ˆYA;.ªøæ <Õ¦ØNa+…ÌìW*çÀqy*'Œþ½:2µ躗»áh±—-Ñ䟴×yÛƒŸ¾û&¹~†ß [€öÆÀ™€èð‰±ÉŒN'Û£…”²qp<ß²ÅöÀƒšÒçÁªnÄxŸØ" Ö ˆxvÔ{(ÛJûÇ«Å*H–êc1õv¦ðW GÛøË±ƒ#øî2™oìIϺ&{8ëä:îý±›sOùªeà—»9•¯Z~D_¿Õ|Õ^9ü,} á×gÈŒïFD«Œ ˆ¹6àû âûíŒMWøqFAÇ™ëÃT©+cý|Çœ"Ÿ“Ïn¬¦–„Ûi †Õ-˪‹•Ï·*-åóñJKù| ÒR>‡WªòÙ¯ÒÀ8EX݌ԦüꯒmŪV>ÁÛhÁÍd!ší™9·æì²äVþ.έ„“!´©ƒé¹šŒXŸ]=Mczêë1gâ¸*Ó~j([/mÐÍNL7½` ©»-T³ ©f˜J𱋣÷Ñ–ôNÛi)9;UÒ3wjHïÉH÷)¤ûµ¤÷““%í…öÀ¥Ð.Ó¯ñ=³èR;–¤LHû;nÙʘ•¥çF1}9A××M€ß¢£xVòd[Ag¿<®$àe]»ñB;À,M–ËL>íDɽÚ/^v¼< àœ ®ÿ.³»#Å7ð÷r§$açHYùÿ·è·Ã±¦£!•áäÈt¬aã)‘®àX£Œ¥DF;ºdxaF…c :C¨å9ÔH rì&t] =¶ÚžÑIãÛUÒvÓoÞ77(}Ó˜dF¦¬§:òk¨º&¬sšÈ´ æ½£â?Ç4<œ9eÜoSøßNÜ þõ²çúí5ÜcÏ-5ÔÄÀï?Îþ±g<(R9)R_vgâÏQS›âÉM©|ƒu3AÚ_•Og9 b?@{èôÝP>ÆÑ|ýXŸcN9 ”Þª)ž%)Þ‹'ã›.ßÿóyø>NÏÚæ *8ŒßÿóŒqç•…ûÆû6Ö¨g«ôWfA——9¥ ¿ûшðßF…_þ ‚¿B…ŸþA‚o­Â{"ÃßNð­4ý¾Á'ªðß#ÂG|KþýÈðÿžð-Tøy‘á÷Ì6Œ?wD†_GðÍUøv‘á_šm°ï'E„_HðMUøõ‘á§Î6ØÏÇ#Ã#x§ ?:2|ßÙûÔ52|‡Ù†û £"Ã7%øü‹á¿'{ÑH#ÿ‘á? ø†ù ÿÁ7ÐÈdøA__#ÿ‘áÃìÝw #Û‡™†û~Þ _Aðñù ÿÆLƒ~YêË%?oN;ÃÆŸŸ˜?-ÔYÌ>¯~Ô8þ,_g5þÌ[g:þüûAÓñgµÕøóƃû5ÏJ^/‰þî ýÿ4£?‘Ó?Ó £ÿcKú?6ÍéÿЊþ³3þNú­ä÷’è?6‘û Ö™Ñß‘Aýàô/ £­%ýkMé¿e†ý‚–þGý‘õ%šà;jìóG‘õ±áoÐŒVõ_š[ÀùûÆZ3þvfPÎßlÑÈßô¬øÛî#Sþ6-2½ÿs‘ž¿äQI./Å« ×UÒú#,}tÝvŽOÊ¡ÞÝ.¹>9¢ŸBÖ/WÀtiæ‚EÉtQ^Ùüdºv½CåQ»±ò†køDÂqjìÜjyžå =W­æSQ g¾ÀçëùÜ^ÂqzÞ¾šÏæ%¤ç®®¡[Ž&ÛÍôæuþ¦Þ¬¦7OÓ›ù|r0a½|ˆ¿d&ˆôr"ɘMx€^æð—ì„üEÉìØ[VÓL’jsÌÇ[@ÅÆ^ÇGñðÏáu¬iž ÿÉø‚äÉŽ57ùáo‘—~|BxÀï>~ñj~''ûEv̌כáÃ3ìËDËôÈ\0!9gAfrVæ‚{’…̳’Ý™ ’ÓÜðŸÔÌ#’S2 IN’ç_Ž7¦ñƒã±\”ËÁrYXNÀrn,—†åR±\ÊXv€ã£-™Ú¹šã¸+EŒ÷8>jßêB²lFY¦cÍUD`Ý[& ~߬!­‡B<ÌÖ’6†“ÖÁ 37>órÒ¿\éÿ7"Ì·}Þ‹Ó?+ŒþU–ô¯2¥Ø8Sú_¶¤ÿ>C¾ëñ/'ý×(ô¿!ßõy:§fý¯YÒÿš)ýÞgJÿKVô»Ï0Þ?>ã²Î7Žé_a¼ûNÿƒaôÿÝ’þ¿›Òÿƽ¦ô¯´¤ÿ^C>øñ¢Ëêï—éÍŒþœþžœþaô¿jIÿ«æþÞßLé_aEÿ:??¾]CÿôËJÿîïµþ»ý>5;Ó_Fÿ+–ô¿bJÿÙ±fôÏšaI?úgièà²ê¿Bÿ«fôäôßÌéŸFÿË–ô¿lž3§¿È’þ1H¿ ¡¿0ò|K.Â÷Sá»®Œ<¿Dõ÷ÖÀG®ÿ!ªÿV~÷ŠˆðgïAø[4ð‘ç[¨þž*ü”Èõ/£ú{ià#×_1áÓUø&‘ë¿…êï«\ÿÝT¿G…ÿňð‡òÞ­Ÿ9ŸMõ÷Qáïˆ\ÿƒT¦>rý/ Gø þä á[Sý·ià§FžŸ¤ú½yŽ\ÿº»~ÞªþKÒÿ9ßÓz¥™þßÁõ¿;×ÿiaúÿ¢¥þ¿hªÿî6ÕÿKýôß©¡Êe÷úW˜ÑŸÍéïÆéŸFÿ –ô¿`JßѦôO³¤?颡òeï+Óÿ¢ýƒ9ý]9ýSÂèÞ’þçÍýßazÿÍýVò±l¸a¾zÊøÈãû`„ï¬ñ7ž‰ìÿPýiÿ'rýƒ¨þ›5ùi«õÁÜÿ†ð7iðY>!ü*|»Èõ?Hõ÷Ðø?‘ë‚êï®Â¯·ZßËýª¿›Æÿ¹/òz:ª¿« ?:rýëîBx¿Fÿ­ê¿´ù®‘²ý{ÆL¾ûsû×Û¿qaöo©¥ý[j*ßH‰ý.ßZzäý÷zkNâY¿xrÞvǬöK:͉N6=À/°ü ƒ\åK·îšÙ÷—ÔHîù§ c»•WE¿¥\)˜²€ß:ëfij`Goo5Ýgy<Ï4ðê" *òè%ï ›|ÿõ'š©Ýý–c–ò‰¼o6P”éXs±—ÎÎüD—ö/øÄ$í?ôcÚ?û“šÚ.º\v·6ã¯æóO Å|>N®„>£Íçóþß3а>®®!ÃY™H7³©óÙne>X6^)wüíÅ©›÷wœl^þ$M6Ó|÷#Oò½w8éÝW÷ÜÙùN»–‚84^¿·®© ½À7Ý'¤R…ÑB7@UÞsgç;í øÀxýÞºúrñÜGü*ÏàÅS„2wI¹õçš|ùÃú@\Zg~±µ8ú%Á„ÏæàœÖ‚'Ô%ðÛá¬çpt"SnQ–4§7G{T†à¶}mEÄwY´]…îfæ+zkŠå!ÈdT¤<,v£=[{ãh¿ˆ¯¬é/å«b×fhtø!Ð"ã¼ÝIÑžµIL){^W-…šs“! D˜“\?VõOŸÓO(q‡3§ààïh>àTÎN©ÛüÛ©µ:E¬Zk¢ˆ;×qÇZeþ­‰ÅÔ[“Q ˆxžê¼_Êm´êæ$[kE‰-_LV­à^eƒfsìG7tŠÛÑ8ZÕÙ/fÄD›ÒÐ'ï­&Äs¶ ²@‚fÍB šóQ Ýê™ähâ¶£fUå+°^ÇÏyX*µª³¼’$È6Ôʬàõ¦âÃ{üÄ&¡%5ÒŠ7’*72ÙÑDH1Aj•ýqìd|° eCäKKahˆÊð ó 'üKÄyëþg¥Ì ÐwXáu¥¡Xï%ÅûHåËIõ÷’†ÇW–÷ dòñØlý ?ßÖÐcÑö’wËâr™„×nC¾D­‘wXæöCÞ§ÌÕäJ¦&M¯5I!Ix¬Œ2âjò3ƒäý œ¾÷ÍÖ—˜Ówã—…¾é^¤¯Ýê‹£ï F_·«¾ÎÃ-é›z‡aüñä]Vj!ŸOØö¨™?ň/Û1ªp´ÑŸ´ò§Mɱ„G»o@Y?ót¶Éí3;κSr{ìo§cñâÞ£è“ý’«—¬¬,ßaå¶A²®­c1Iž6Ýn§_†ðmeø¯—Ö?ᯒáß²€Gz%Wï¥ò ëjùX¾¹\~B­íZ¢¥§W­ðK´ô4ªþ‰%Zz¾^‰ž±KÂé´DKÏså9¼' Ï›v¢ lóN“ îYAÆ7oË+líæ+l¯(ã+lϾ]£‹ßÖ§I®ßàk«ÞÞž/¯_l _“ØÖòJµÉ“/b“ã•&×Wò&W-àM>¢6 í¥‚ÿ¨m/UÛžc§µöy“§u”á{+ý÷VÆï‰úï‰QfúD÷„!qPвž0EŽŸÔó¶²ð ˜Å¤’‡§m«ÛàáØõº—¼žx–k/N·JO;æã³P2ÓnsÌ›…9)ì:Hˆ b®¤s¸3ÅjcÝÀQšiÇë"=ó$Ì%Ö/9#9æ/‡ <âægÿsFȯÒ×;Jƒ7bÁ#ÎÒÓE¹à-9…ü BúƒvGÉØV0vnfÏYÎÂvØ>B{tÅÉŠDhô…ÖÔèüòÂ9àÒg@É^¼¤X°¾9T/]ûQ× ’·»ä%:ûõ‹ÐMm¶’j@è-°/x„ú¡fX oƼJi9ψÏLK£ Y]]ÛPct{1™iéx²gÊñ…í¼@äoÒ=€ÄGñ„„™ë“Ž ñèÍåÛ={%Þƒ§¨2 6'ðÈ÷`¨vxàá´â®’Ž¡º~ûÄ^ÉB~?{iùœŒÇJ6;…’-Òo²B§’±PVmžäª›3’$º”£JfZOÖ7\Áûf ôÍɶ¸"•j§:£é ³qØA멃À¼1'%UðRÌüÈ ±ÚXÖGÕ¬öÄÉ}ä‡>ê ÷´«é£­x9J'Å¡ó·ÒYñ*€MÍ¡~謦©$qp²?8ÞYºgNG~XûtE¹Ÿ8› %úgEÿúæeý“‚}¥²Œäã7DŽý}¢×–.إ͢09³Fòçë÷ÃδE†U½Îöçä öÒ­s -â\e@Ï]ƒ@›^§M;P!íé¾Õ/–wßã£áüû‡k¤Ìž3çŒy ˆwÚ…ü]¾ômŽ’±DŠÏÛsÖ&ç,7þ c7yæ9‰Ã <%ðÍ:Yìv>ëùO_Å·ùÒ·:JJc‘Ë:=eîæe³uJ›Óg&ΆÔïrABáÁ 1®Ýë(_Ð-¬.ú›Œ†’8«ïÎŽ±rw¢~€ÐЉž ; ¢Fb*ö$vƒ=–÷d™½Ó'6Oöçïô§ß_~‹a_ðäÙ¾bæBg™b`_"5¿t6Ÿmcâ°@ûEx?¯ÀK ÀøNYÅpôcÇ”¼@·•e.pJmñ9ž/îÌL»ÌÈË¿;iÕL`jæ˜ß7Uh¦ÝWÒÇV˜*4c†F…Ú\E÷Ÿ¬â*‘ÀÙ×°Ù>4(t5–9ôšFƒ>M§AŠuËNä ú«-ÖKºs^ÃìÏ¢‘ÙŸëuçÝhEwFëtgi´¬;@w®5·o߸ݹ#š .W›œh£ÚTuâj“¯Q›FÉŠÚ\m¦6dµ‰M&µiBjs}ªMµOÜónmKþÝRmèy²`ýÝ\m%K®7AøKf&ÚæL €Ð{á5—û`SO ¨mVoüí öÚ4o®¢6p71í‡ZMÜÆ´f‹£d`Óš²~Í3ˆ€öûFeŠ•é¤:ßtDÕ¡Á#•A^zebÉî!ú]5Gß§mr?2¥tJ,••f¯MVš ¨4-Yg‹ 3wS»Í“™ |‡Ì™¡]¡äLÒ@X6SÎŒÌi}0>‚G(Àƒfšß  ¡Çj>iîKVùl×,%Ÿ)Šü¢w²ß£Q·,v§—tYìk_2ÉbÛ_2f±ã_Òe±Í¦“ú ’éýâyò™1Þî§ýe…NøG“~^à½7þ¥Á;êm]výãI”ž\bã ø”ÿ’g¦u§$w*WÜÉ“k糂G*³‚P;]G„úù¼à³3¨âa4´Srí~à­ÕN?úŽÛJ}©¡e6ºá´1Þÿ @RQ?‰¥:ʼ‰!ñÆ›ä»ÃîWÙ̯a+‘|Ó\½rFÀÊÅ„©‹‘°F+dµ·±†])š1`:°®ÅÏÁ¸'ùáãu¼Ÿ|à íücù%ÜOîZ¡‹Æ+LÄãC½Xüþ"Ó“‹K5ù‡É¤îËi'îv®ß•DíG“•àX¾ß”’‚<+ˆåîC3ñétÒáið¢"¡šõcÏdNZ÷RÏ?ÃLrªÄK¬ 5ÈS§ P7N',οƒ0÷ârÚJæÑ~øéÜ ÁïÅø»T*¸8‡Gsogíãç +Cn~]5¿<ÝÐ<õieHÀ› ÔOîÞÞôÏå—¢*rñçó²\°yIŠv3x)ç æ÷5€±Ë`ýe§Ä UJ®Ï[Ð#Ò3ëX2;ad+t·’žçžëÀ$°ïô00 p‚øüáQì¼”®’õ€@­gë—äù, {$}2 ×”XžFpø €½2à¿Èb¦àOLæºT à@ø,§§ÈH!¤Äç8R%×t‚H(ƒ¢Út>—¤«ä #ãgJ'<\JUð.q³bÅb‡KŒÅî‚Ïþôiöiƒ9ÒÛ©TVêÉ¿a©WÃJ]C¥fÚ§]É)`¥ö-§.ÖŒñÑÏòG)’ëåJ>ǘ_I ½þ›&)ˆçcÿ…ËãÈ"R—µ\–:@(c¹ì˜À—›–P¾¿½&ˆÚñÕ2Õ£ÙöW‹3Ë40|uY-ã|—öüu¿|ŠÞ¦‹ÈïÊJÒm…}€µîÀhóîe²‹ãµ™ÐïD¦ýý²8Çfãñdú}…œEŠ ‹äÞ¯‰ä9QŒ2—©‘\ê²Ú"¹5‰XƹLÉ{Ö<’[Þ öH®XÉaW`Æ ¯Kw½] ãZè¸+ømµa\ŠƱî·(³.ÅÃ%P —?NÃ]hŒü9ñLÄnJKÚþŒu ÷s!áÆw×e>îÔ‡p¦©ŒáSžú´©Q©®ÒÇo×=£Æo­Ÿ±ˆß* ñ[‘Uüö¢Uüöp¤øí¥§ÕømÚ(}ìöw<æ<­Æn½”ØÍp²¿äš4‰i·= 4Y~¼™¿£ÿN{Z7(î§Êž¢cHÈ®œÛ/Ø ÁYHè\‚e?Zªªêpòž£sÆiTu7B¤«©ÈHˆ%\+?©a×I¯=sZ -ÎIµÖâãÓhñ¢F(Ls–ªZŒó!‘µ8¥–¹}©F‹o\j®Å?Æÿßkñ”Ø:kñœ+.Z‹;´ØIZüüX§5DþtXQ‹5G ¸%ÖZÜnêÿ±µÖkqÞSªyªNZü!I¹•"?g¥È%‘ùà“EaL\‚o¼öIs-vëµøõñ\m‹)D{C~œBW>ɵü ·äù¤ìOlòtŠÒÏçt4Ó~Ï©u½'Ý6µd-ù Á÷ÈF÷ØÛýÙ'Eµ‰ð/‰G·üËzz²ó·FaÈ5ÆY6&v]ó#ïh†œ(Ðl÷Ø2Á™™v.ÜC?®ÿý4=Ÿêw”ì¨*ÁI(¸ù»…ô,èòQ”•ª„€zvÚbAœžå)îc# $ÜOGk÷¶áÄâ´ûxWmzý¬98±u„ÐBÞ]%¨ýÂhnjN€©9Ý“>’Ö $­ AZ !4ìøzÄMš©­ Ið¬bg©äX@CŠx'˜œ£Bú0û4ˆØb‡gö,`s[ÐÜ 'só¹lnšÚä¹-ða€©¹Ë>íVg™‘ÐÌtiyAªê¿;DþfÌÂMKdóNþõxØÿÿÿ¼} \”U÷ÿ  ã‚3¨£¤–X˜¤f$V(jŒ0ù ƒâ–”š¤iæR¤ ¸/@:=MÙ[–e––•Ù¦-f¶¼ &(îZn-j™£X©•Š Ïÿœsï³Îƒ¯?ÿ}>á<Ï=÷Ü{Ï÷œsÏ]ž{í’ã¶Ç*%ËRv‹ä¸õùJÉícOòŒ±$‹»$Ç]“*%A,‘‘è²4t…x\!bªU*tÅþ&ˆß¬µX,‚芑Ú}ѼJ’Çcž° *áa0<ˆ©!ÁZ/ßž‡/ï%Šƒ‚¸Ë÷uÅâ ñA,•ùbJ)Ë'xÇŒsŽt>äá>ò¡a#ŠÝ?Ô9Ø9Äy¿s¨{&Rð:¾]d©’#r.r7Gjr3¸Åϱ²Éb¿´yÝ,’cä¨Ji^T¾Dð$Gñ#XºK<®ø7 þ„ÓáßhAÌŽ¼7ÆÅ}¬Ì6Pf^A֣ʋ›¨ú—€>AwJŽ *%ç†PÐ’ãgð&¿ ûódñ¡´y÷Âkònx7/~—º½.» î–=Gçg1Zø( Úûý(¹½/ŒE…N‰hµ z±Õ¾T zX›¹ˆÂ—ÏReÝb‘ .Ù@‰PÁ`䋵Úû,ÐÔ¤€íê‚è´êžsÏ€¨’¡A‚´x±ÌuLC'ZK«Ò;3Q° RZ¤‘ˆ§ ž.ˆi‘Œf¯Že!K"Ú-^v‹§qº äK¶¼¯°vù{my§ñGî\«Å–÷.Õ¸4è‡Ûëø¶êŠä”š$‰g¶Ú¸Å>Öd(Ä9¿ Èæe×Ï-çlœâ–W.– £7 ñ…¶ÜéMÖ<{þ…œq¨]ý³ÿ Õ–»Ù{C†%uËÚdÏn‹%á#”–* âàMPªPI¥ædw»ÿ€uÊH$³ ›˺R»Kl6”Ⴈ@ÊVØ!¾·uJ'È’,¦Fwdék¥—m“-Æp«þóT„¥^üÛþ2y)ÖrYË2ü0ÕÑ–_DÒ™W/ø³*·÷¸Üâ‚’ÅІ¯ÔBrúIneµ’[XÉÝT!K®ƒ"¹zÉulÂ%GÍåö'Q£ÜœÖ)‘(7AL*Œ,;(9ÊFsYmQJIbU"ÊJÕ¿= òLÌ|_þ,©ºî—ç{¥Æôßô*Mºm½³“Öf๣ṃáù6½=Ng±½\P¤>§ž3´Ï?Ú`ß‘ÚgÐÔ §mý9A< ú‘Œ²é BJÈ»Õ<‡`ÛøkÐäPAÚW $Öu‹‰U‚hO–v—µÔ¤5Æ4Aih´¶õè˜.Ì@éF>])-J·"Fo£”Ÿ NVha=´¥™hKkÈ–R¸zð²Î’q&4µ¿Á’^¬g´¤EðFÌZˆ†4Ä Ú]%ÄÏCz¢Ò\ÅRÐæê éÄEÙæÒtÈyϩӟ«$œb%X}²¸Ã÷!ènî%¨ÐÛu¡øÝøêð¯ë“¢â„ü[ÞÓu± aMŠ]…-*§ÂÜâY$JHŠ»è²£Oå!”ó:Õ€§-wm]ª3ÐSNxr{gZS»µÏ»Urä=˜>‹ñ„7¬}U9¨Ó ‹Ð‚wS< ¿“Å{…Ñ}¬ùs°çÏ-¶ àßï³<_]†<'RT%èŸá¡81R’°!)ÑIqm“Å ÞG/qdJ™sm’<î¨dNè”kйŠè: ´Ð’s¶Ü“ðRLúž_3–t‰´3TÈ u‚bS!+hð-Ð'‰P¶Š”ÈY€Œš@ÊV%‰ƒ¢ÜÞ vPéŽîщ`C©€C›ÄúäH±ŠîÂfž”HooÉ“uêvˆÎÄ”ˆdûâ«-¿ ’ÔmtÄœ  L©âýVaô®äøm¶Ü‚jJ²«Û#[ì³ðgª7d‹s~Ž$ÜÀ™+ˆ_A'Ø_q^„2úAöm`­¶Üù!(ågíNOB3ϬgíRqüÌ´ÛÄFUj¨y޲|ÔŒÉøûú]ã ¬ÍèMñ¼[Ì…#œÞ„ ObÉDBC8w¡=Ylá½ÓßγÁÛ¨°·˜ô´Ýãl&x“#¨ðQTø S†B"íSÈûù&À?*âCVC7¢’»hîó$-²K‰­ñµ(u ³'ÅÝ ½7(Kqý* ÌÀlyÎ`ƒñô¹_c<§/#°?æ©ÆS’ÈxÖK˜gužÆx^È37žOÿåÆããù·b7W4’þ¶Jú;½Ý|\G±›‡uvóRÙn&‚Ý´3·›N+v3 ¹Yn2éuŒ&s[n2£5&Ó(J1™[똙L'ÙdB¢ÈdÂÉdþ¬1™Åå(¥¹5šÌò]#rÍM¦Q”䘞Q)åÎŒ°Ìy"Þ¯¹Î{ƒ˜É8sgm±[fõÂßÉÞ[æÏÕ™LÖ´·˜û˜Å”Ørû1‹ñôi–ä –ãí”$îŽO$³i]_o6Ÿ-@ØNš»zb6z/Xd™Å:‹‘ʳÏ"[ÌD½Ål´Ô`11Tn³(¦ÏYŒ3 :Œ¬ £™2Q5˜bè»Ó´Ã·=Àƒ¾ó1 9)?~K>ùñczÜ*?® ÇmòãóôOÃ`䣋Ÿp1Ç9@:è%ŒYM†]Â׆„ vÅ0$.ñÅóh‘G1ƒØˆîäÛº„ò~ó=2¬’Æ¥ÃÍs =‹n ¼ûM—%ç—u)ø¡ìö…ß&ùÉTú›‰¾±J,ºFúD¦Ò¯«™þüHßH¥Ÿ_3ýa¢o¨Ò¨™~Ñ7PéÛÖL¿†èë«ôçÚ×Hÿ¢·ªô…5ÓÏhbÿófôšñ— bìTŒs£žˆa=‰šT:”Fíä¿€ß.»oNB«ÛÉ¢k«\/ØPì °›Ò ª²Vb –ZO܆‰ ÁñAb}LÔ†¶ëÿSë¿7\–g¯¿Û¼þ&ãƒÆC¯u|öâ «×U=hN¥ä©fx€õ+NŠŠ´ œ½à3ê“Cp <}Qò…EI’É9®È _­ úÕ¶:ûðAlá)CÐ ÓïD ¿ Û¡Ø­‚'Õ ÿ[à»Û:žÿVýKÌ-¬Ai6É †nˆ74`T?ºˆó_¾!°»tNa€?ÍMÙï˜hœ ƒbcXJÛûÙÜJ ·÷8Öž–ДtòÙ—ðF5êÈWäg_- ö[­½üOíVÚ¿YûñöûxûoókÿjÛ?Ä´ý]Zš¶¿Cuí¯c3ø£¶ÕÙóÿÔþ_ëÊíbÖþ†ŒêÓ¼ýÑ~í\mû›¶`cÓöߦo þfåãèo† RýMŸAÜ߸µþ&™ù›Zs£ ;¬^ÇQñ˜†xØ5þ²]þu ÑÛ4ýÏàé;DüwÐÍ5Ò7!úz*ýž¶5Òoað÷+Íèåþ\rîKÝyÙë4îkJAünš /î[)÷èÌ¿&°Éð±0Íù* }ãŽ4o<^¼Iª´Sr$#‚Þ°ñ-˜>Ôåž1Æ0@µZ’©A8¯$ÇþT‹0bÿ\M 3Y€Ípãïø;_Ê1TÙzµƒ'{·ÈÅoHÅ—57O!Înß²h^<è×Ð|ö¿ý€ÆA£PG@ŒÇ*…ä°¥¯ã?/ò8ëÎAU+¯ñgtò"ýp΋³@ âëÇÖ+hñ"-ø~¿Œê(9†ä¬ÃNÆÏÊæ´µd8¯ñkÙz”çw!>nþ€‚_ —â ôóƒàÑa€t lóñ‹(¾%˰°…ýaè½}CSäP—9œZ{£¥DëWÞ°Ï)ÉÊ’| [bãK€Í‰GPOßà•…A~ öSÂú,ÞŠß[4Ñv&ñz=‹†`žFVlíjÜ0ÒýS‰µui_n‹a;["·q”ëäz¤Ÿ,îgú‚¹òØòžÄ’ÑN3èMAÖí$jGùHlcE½ÌŽv„Ê©â.·XDã;·¬9¼ÿ[ÿþ ¼Ÿ×Ëb÷ê18ìGò?¾V( ¯#ëÉÓõ*–uO¿J½ ï¿AäkMÔQù§²VIÎzªøð5í'Ñ’ÈÓT\ج&jâßA¢ÆÄBÊ›=P6]‡É²ê]WËæ™8×\@z´Ë÷«{ñQªìÒW°²O§*û^„\ÙTh3ŽºóF¦U1ú±ìÞaÜOMÕB ŸIK2ê3tMå³DÂÿÑ‹XØâI Á(fQjÛë ¼CìÄan A<®H—£ ÏÝ)ÑXmѮȱ¶¹®m,ÚDãtS¤Û›ãÜÁš¶q€–&áôÀl‹Å¶¤`‘+HÀådG:ð±­‡'‘ã eÑ ¦õHaž Îà_‡h hþ™¥TÊQó,8ëòÄd¬Ø¤ÕP¹µ©â9œf àFr„º±%Ъ—6{]Ûm.ÀÛ»ˆ`ÏŸGL†0OP,x² Š]êPÚˆµn¯}±Û3d…Û;dEªgP‚[t„AIÎùÝQ ³êJŽºð(A‡™ºV(v&P-†‚ñÛünDÒ$´üÕ“X‹ì‚ü‚œpht„n±T̲€­ëÏ”xaMo²Æ¯^ö^î8^(Í@µ\ž¬Ä -òû†òÜÛlæñÂéHm¼°*™jñ­ë̤Þ5bŠ/¼–¬Æ ™É~ñÂòd9^Xö /þUŠƒÂRŒÅs{œ©Æ í“yÏS²¼0pViÿr¼0êÙRx®³OT/ˆå¹—ƒ²oL&'ºaŠªI&ï®ú§CY·ôÉòëñ•üó¦gX²“·8F «; ë"(uiþ¯Ë7 ÔØª\’¢- zC3}EÌñúâ¶«±Å¼éé–ì-Îô "U$G>ÃÆ>Œå}\.çÂ^Î}y›+¯4Õ' nKóUË' Ÿ™É òl¢¨“–~ÝKD‰X:Î#ÒR½<|7ï·5—„Ò‡„rîÌ«T6è?¼²«ï%¡œZ@B¹Ü…’'„rK©iÀ¥©_2Õ¯FGÛÈõsùÕO¥ŒèK‘þ&™ÞQý|Ö~¤o¦´°={MÞB]]Â$¹És^âMž-p|†O2ÁâR–ÿ)ÊoQòßÿ2ÏßSÎßÚ˜ßt>fŽ‹ÍÇ´ÖÏÇDi‡p-4 þƒ79 ãõ eõûý!¬_ÆD¹~?Èõ[Þ‡×oÖD}ýX”€ýüq•&â,-NBÜè"Mèßð2jBÄ‘ èV㈹”{ƒÛaïž Ù*& é v5òß6Aåÿgñ¯ÇøÌfzþ© ÿeµå?^ÃÿYÆÿ¿ ˆÚa¿úÿ%ó¯-ÿþÿ;‹?¯ÂÿÈøjøã€\D +âÝñ¸ ,5=Y¤ ûs±©ù[³‚“=A8o==)Ñ©D‚9öÛ‰òN*°PZß >H -èï›BÝVuåÓ/`•=õDš²Y½ì4À¶xÝÏ]M¤ˆ#Ò!zÌOÔPrÜE³:€ÇùƨÐ$Ú¦mœR @дnëc•ͽÇh›Ó2‘GøÐœzk›ƒûY’Ps+õ£zGȵ&¿àÁPåPo¹R»X¥´Ó”¼>qšút{ Åý ’cDoª•] b»ñ+`PsÃÀºD^(õ5§ (ªG0«‡•ê¡Á3KˆLFƒ?ëÖ´•öótî­¶õæÞœ9zVÓ‡! k§ *Ô‹N¹…ßÅŽD¸B&îM 1`5ìÇ2Á£Û8Ô6WÉ€t­uÍ¢­ðl§ZánNeמ¸ ™^¦hZ#R±§^ÅŽ>j•¢^iªzHЫ—MiçZ$µö£iÎxÚÆù|‚Ëc§ËO©2a/^)W Ýç{ê&ÙL»>ª7Sþ dþwøŸó!ÿAŒWÿ[þûÇâ¿f,ç¿ç^=ÿ?!ÿ¿ÇV²ËTÄ)˜DÆ3ÇÖ࿘2 ž!͆$oV°à©#›„Ê€¯#–ê æú§ 4þ N¬ù½zÀºWìk®˜#âµq oï±^úö¶=€í ®¦½â˜Z¶÷rìÏhçÖN)«!Ü¡_Á`ز¨3©.Ü)“¼1O÷2¸]›üÛsúÞžpC{œû±=±c´íI•dÅøê‘@íáüŸ–ù¯ë©çç^ä¿ï-ÿ?ªdþêãoð†¡y%ïïÉv¦=",|ÍTÄ8[™ÿS¤¯z|B©íMn/²b¥ZÇu«ä‹[XsO¢UÄ9&¬ÿݬþòTvÚ×Ý+xì‚' ¤2µV2…rA«õ5ú«Ã#¹¿ ê¦×ýù¿¡nÞBE†…\Ðö—šËnkÕÈ@ýå ™ÿÊ8=ÿ â_D ÂVý«åÿ’Â?) ÿ–2§ÿÿ"ÿÑ#µþvv¹Ì¸ì¡ZúóÉýß=zþëŽSÿ§ã£Â_¬-ÿeþó ü÷Cþo=¤åÿÍ%™ÇÚò¿<‚óokà?ÿ(òOÔñ¡ð/QKþ¯Êü ïÖóþäÿë-ÿ+eþjË¿»Ìÿaÿsû®Žÿr…ÃÚò?<\Öÿ;IÿuüS/Èüß^Kþ3dþ+ï2Ä»¥¤ÿÄ'ì· þ7Uôx@ý—ù; ü÷l%ýgü“Îkùß­ðÿm˜‘?ͳå­þtõ*àÛÒiÈt<öïY" ~óŒÁ⤮wÓeçrþ…¬‡qÊ?TðÜT6TL°:=!Q„{f’¯®|xê:s{DY¦Æ‹©â«¸+9~×”›Å=Báñ!¸¹  …ÇB’ƒwú|8y]ìŒï5ùPY#ɱ˜fÏÃ_¨ˆâë ãáNk¿R>v9Ç<ÿì ìêºãZYV£!‚˜Þí¦©-@ŒP¤ló¹°È¢ø›&Ÿ¡dÄó8D,ÆÅ 1Á]å>b†È›Î¿‰õ!^‹EÄR@j“b ób =ï¥Ó§&'6–º4Ü *9n¥‡D+ûÔ;%Ã|ÿ"Ý|ÿÜ+•ù~¨C±kƒ…ÏÚCµÛ#©Çµà^‘ìILÄС@??Nž´𠊹 yè'îc4NÜw~¢ 7c\‘dN A*Å…©p ~UtdN]©ÝEð¸ÂQnjß?^ÓÆS·= ÆS©]ôñÔòÔù' Oø«ÜOýdWâ©Ó鵌§¾LWâ)Ú©pà Ãz< Z“E€cÚjTõéú¨jÐÝ|O³’‹l¾ç­.úàjPz-ƒ«¾wꃔwšWþóc§‡ªócï$AM»ˆ_Í9|[üæÇ&…+㟡µœ{ZÃÿ£6ÿÆø/öç¦ðV[þ4üû1þ'/ÿ8þÚ•ùÏûk;ÿy¿fþóâÿãô;?þ© ÿeµå?^ÃÿYÆ(ã¿ÈŸÿ6eþ³¶ühøwfümŒŒ?¯ÂÿÈZòÇýY2ÿ‰ñ¿Äÿàf?þ·+ügÖ–_ ÿÇÿ¹Œÿ<þ;Ëü[Õ–ÿéÁýgüïaü£ýùORø5¸¶ú¯áÿÑíÄÿ¯ˆÿî"ýWø«-ÿþýÿwÿþ†)ú?¨þ|E¤±"Šaå©èdaPìë¤,„"t²  òÚ0zYÜm7Ž­j‹&ßÝE6hn«NI<>H7”7ÖG¤âíA·xÌ-fçàpöuµÃÙ9Tóâ +¿u¤^öäîÄ}rj¦ä¨Â¨Y´àΪ‡_>,™Gøvß\Ü ìÙàñÈ åÈ3½æaDœê›sTßüQG½oþ½£ì›?b¾9Î48ØÎâhñžT¬òa\³ÇEéŽÊH7%‹ûiŸ²Ñ¿zuøEwÔãwð¿ˆ_ßW‰ßÃUJßùÑ€êñ3ê댪¾¾Þù£óÌ üôõJY_SÔÒZjø÷bü‹Ï1êϹ¿,­–ü7¦©ü¾ù#Æ?ÆŸO…¿X[þjøÏaüïaüþ×ÿOõ•ñ_mù_î¯ò¿‰ñÿë,ó§þüg+üKû×’ÿ«þßF3Äø þüoTøO¨-ÿîþ1þ#ÿ³ßúñÿƪŒÿjËÿp?•U{âß‚ñ_êÏ„Âÿý~µä?CÃÿuÆ×_Ä?ÁŸÿ•zŠþ×–K ÿ^ŒÿBÆõ7þú¯ð/K­­þ§jôÿVâ/ãŸæÏ¿§Â_¬‰¿nþ#Užÿ¸Õ0¿²‡æ?RÙøøOÝøX)&ÊPŒÉü‡[žÿ0ð"þ‰Œÿ‹:þw+ü7»ñUæ_ØNÏ¿ínšÿp³ñ·Žÿueþäß]æÿ°ÿšŸÈeüÿùC˪Â?$ ÿÃ)òü‡ÿ±4ÿÁø¯Òño®ð_•pþOæ¿2JÏß¹‰æ?ØnªÁ:þëC•ù€ü[Êüþ뾦ùÆ?DÇÿ~…ÿo}ñߨWžÿ»Å0?´‘æÿÿÏÎèæGCdþyù?(óŸoàïÜ@ú϶{u:ªÓ…”)ÿOÖøÿ[˜ÿÿ…ù·/ýý°âÿ“këÿ5ü¿½™øQÆü›?ÿþªãoX‰§­k0ÔÅxOÞn°WàMcúÝ>O°—4gL­*Ï!iŸp£9ñr Å Š%¾“¿Éµú^`µRóëó¾Àë³·-ÕçïÓTŸfj}Êë(õyJð«‘ß0™ßÆïSÆoû…ß{*¿»Møù­ ¸}Crti«W§O¾Du,Ðt&›Íl£¬ÖþÔÇ ÓõÂdi·Ùza¶æë…~ëÿ}¸¾?©¯`ø´þßÇ|=9Þ¼†ª<Õåsý²yy±]Y?žy_mÇ¿÷iÆ¿mØüÀdOk?õwªªäñomùŸvAÜ/ÿþŒÆzÎ?Iõ2ÿ¯\µÿjøtñ?p„ø[ýù_Qø«-ÿþýÿçN²xð?þß\–ùW$Õvþ'IåÿçÄ¿ãŸîÏ¿§Rÿeµå?>I3ÿÃøK¿ÿ‚’*ãþºŸ*eþñµåß@ÿ3ãÿ_Æ?ßÿl…ÿ‘Äjç ó¡ï%òh.t+û’Ï üW”ò9ÍX,m ö–âw'ië[øß׺R™#}®µfNÏ~[\%© þ«’ɲ—ZW·…̤ÿë­éÿZ³øû‹_‹ýä1IÁ³´wmû? ÿo[1}düÏnñã¦ðŸP[þÝ5übüûŸàó©~ü?Tô½amùvjÆ?-Ùøœññ矪ðßYÛñ†ÿëŒÿ®ßØøÿ;?þ”ËüSjË¿¥†/Æÿ9Æ?ÓŸ¿Wá_–PÛñO‚füs“?ão÷ç»Â_¬-ÿ5üç0þ6Æíf?þ;.Éü;ÖÄ_7þ¹WÿÜ`ÿ|DãŸm5éÿÝ[}«õËh?š¾ç•ŸGhVW¨ltÖîÆÇŠ0í—ë rÇüI„lëE†½ æímp/" ñO!Å?÷jãeµø§^ÕÅ?Æö¾O»Æ´mÝÚB¿ÒóeaãÒ¼^þí»k!·kŸ¦]¬=Úñ_/Úl<. /Óž’:x‘í-Á%N›Ýb wEHŽž-T¹§ZÝ^<§·çžøŒA ì*a5û½'Õ,F·¿w”ŒÓcÐY©k¡aŒæ{)>ùøtOÚ &×?†Šà²âõ´KŽ%ÍåzŠ®L6ù袉ÎhÑ•Á¦ùaXûz$Ú™Õ;R©·ÀdúTs¹ž{X=óýÚúîý=n^ÓÕ—íÙ±ÚìuXu¨gaò S¨2Í%ËÓ¥<,ð±ãzß\â©™¸‰'HÙÃ3¹‡YÝI¦.ýÊ!oáÙ¤m Öþö–Ò„&Mà*ÍE¯´£•¶‘¼9¼™þí¨x·ƒ>û²Ù]‘¶pu‘ýíxƒñưoÄ »’|Í »OýÐ1íÿâ5ý_3æqü*:Ž:*þñ›”þ/ÞÔ¢ýÖÓËšV*cµ¡Ýy¸°þ=ëÅÅy+¼VWØõÿñõ|Çà[q\Ù8ž7Ž·W²Xè«fôƒ¾?3Yá׌_;¾¦ÄæÝ÷â"”¤oÈ‘*)¿`zcà|¼ø·_ì±*‰opÖ•ráÓ-ø”;Ûjɉ-(ƒª…½×ŽÖí»ñªñMf£âs‘Î/ƒãM´[4õóÍ[Ê>-òŠ]²žjäWܤ’Ý@Ÿ$¾‡OÆ¡¥<§!(ßÿ¾¹³œÇé;iQ¾ÿUÓרé/èG“®;oB3^•Å ®Ð’äM¨‹ûd/\’|Ñ¿àM¡؇~×_䂌k*‡­ï¬Ç°5¡›V°‡Ã+Ù®Þý%;‚û¼^'÷:7°~À?uQ^¡3[/“&(â¸g#jÜNßÉõÒ8|3Þ$à›x“†Õ^DUMÌÀj/¥ß)™¨›ó7c-+ÂõóoT¢ûûéçßå(áck¡Ÿ‚á„ÞÚs¨^•‡H='·wi¿ Ð#0õÄü9¬~ tv"þIÃ?éø‡â¢qÈ6W×õî!&¿4ª,Gç Ë"?׋W;AVŒHY1bP1ÐŽU}®®þóQ2=Xýo§úŸìbVÿt ýã‡rå0šBÔˆ_z’\&Ñ•d¢¸_K®ˆZ5Eák²÷ŒO¡Bï¤ µ&f÷w¹½òO`ù? ln–?ågª¢"C‰sN¿œÚ¬Ò70šxnº³¦ þç‡qqwÿxî8@µ{èo䔜PÚŠ°xA$˜×µ’íÑc¸À`ˆ¾dÛÍDy *çQéòWÆò‹r~ò'ÈßÚXþ09$å œ!lo]þÍõkΟeÌ_¦/?/@þfÆüoéó'ÈÿÙ=Ú ÿ?Fù-KзßZsþùéóçÈßÕ˜¿¡>R€üûúSt¯¾ýW[þû÷êôos½šóÝ݈Ÿ¡ýò7æfh€ü•Ý ù›êó‡Èÿš1©^~›ë°¿£ü{êÛ #~‰½ôíÿÇ.†üúòCäŸeÌÿ©>ÿæÐúw§!ÿhCûäÿÍh¿JûãXûäŸh̯´Ÿå ÿ뻫k&kH€üwòOó°öÈßÕ˜ÿ–ž:ûK ”ߨ?3{èñ?ëcþx=þÁü·1¬>^€ü~úó‚>R€üYþ«»¾ýêoÌ?ZŸsý‡ÑþKõùóäϺÝÿH7}ûäofÌÿ‚>H€üŸu2äOÑçßTsþÁÆüÁúüyòÿÓÑÿ«8}ûäјŠ>H€ü]ù;êóo¶h¿Q‡Ú ¿Ÿþ45à ÿ‹~úgh€ü]ùs í—*j¶_£ýÇëóçÈïçþ¾GßþùýüÏûúü!òægÿúü›«jÎ?ؘ¿•>^€üÿíwßÝúö*ßh‰úü!Ê7êÅ]úö_ cýêËÏ °ŸýêËO ÿŸFûÕç ¿‡1ÿ¯]yþÖþÊšóÿx›qüÐUßþùgóÒçO ¿µ1C}þù›åŸ(çO`í¯¨9ÿ×íŒò뢋¿òüóG»ÅmÂh§5¿ §w¼³Ñìž‚8ÀN7ZÝ.HBp[LÚdM· b³M’£ïÍ•R²7!–=ñA,¤YhÉ1ôf\(oTÉ–*,7ûŸ÷Vì´âÕÊœ_?aÅÄâSq:ßÇ:¥'+^[ â¬b<öÆïp"à ÚÚ&°ƒS7²vчø _ TÒ«3^®Î“mñ_ð ‰Vçô“GÐeT­°Ì£!B0JJ‹N<ÚZVWrL¥=px #¤qöTj2Ä*9î 'y:_rH yÑpkšºØ©™‘3\ç¶e|GÎøM¤A„ÆùA¬Ãáîm¥¾–AìLUïà¨H/®…ÛïüS¶ÜuJÈßjËÛfÁ³æ¶ºò³=ûꓘLGfGÇFÑ5GŸÓëVM²˜õ%Uœõb3¿±Ù׆Ž2eBŒk"9šGj0ªOa‰|UY€?HŽ¿Øƒ=ÉŽ·AÕúÄ0§›˜-9ãDÍŽ&Þ¶gÆ€DÙmPºS8Å¢²3’c\.Åimèþ=ø»] õëe´ŠDú€9ܪh@!dNí ×¼’É".;&Ã*Ãcô÷v2ÐÂå⬔zÔmíùß™ìxL·xÎ+¹=³¢bðOþIÀ?éø'ƒîª: BwåØý?Ä'­€uÇâø-¶gÎÛ@/WHl’ÎKÇDþJ»Ù“…˯x‚+Þb²Sr|Íí?S½mÎ_}è´Kˆ¿lËÅéno)x‹{t;~“-÷e ž¾™• `x†ãÝEôåtÇm‚·u[ü9YhGEJO{G¸qš¶¯7¤))9ZÓŽ:!‚¼… R½íɹeAYu¥výW]‘ÔFŸz=ƒã+cáˆÚ§òã'ôø™üø=ž¸ñªíãò~X/ÚÇ•ìþ¬%ÜHÖéWdÕ/f%G&Y<›_ÕÔwæZ<&ñ„¼û j41ïBkóLi²*k%ˆŽÜ¯¯€.lÂý”#ôA|!ZüÎ,‘¿çž7½‰%ûF2@¯ã¿ÇÑT‚h\4#±Ù–Ä&A‚˜ØÌo˜Õ7†_°æ! ví9»hb÷Ikœ—M°ÚÂ#ÎÛìü O‰FÆ6{b„-¼.¼L‰†—ýìl©+†îº4«ßÇáí•úEðúET_¿ójýfñú¥svVb×DS?¬Öž£á9žqD€g\"I‡g\!ϸ@’Ï9¶p—FÔÕÃöØÂûÛár÷‰„gÈÝ?ž!wŸx†ÜýÓàr÷ɰÙE¶¸Ò?/CâÐ÷g5µçvÞž³¿`{z·ª¾=5TÒPrMøÊpl8‚åím©”׌óo¨j° į\ÿÃÈoAKZÄeà(&Ø=‰­‘ïyd ïꊉ­=÷YüÍô¥=1¼³å5ê³\¿t:¼ýø þòÕh3“«N—k+Oë!jÿ ×*O™_&žÞES_M=ÿ~—Pû#®ª~&úzçGüþ£áÇåÅõµ+·¿$µÔÛ(®·]¹q%¡qýúù#ÙĵÊGöw—¾§u¨j{¥‘ú»¨ý]+Îp×Adø$0õõ$6E&bbSÓó6j¡¿²×›[ü/ú‹¡^%÷bsÖ÷¯(§šÑÇ»xwît{°-—.Ål‡8åáãåR¿Ñ¹ƒKœÒ±²*xõQoq& bØ] °—•pc[/9&ÀÖ ãtÚ¶ƒV§ ÿì|ëÞ®‡Y¶S–Ƹ0Ê»¤Pn—äT…„ð£ ü(k,äBHõ8Œ Ëêð vuǬ™¶‡ÝŸBw§\†øÉ74ÔbÉ+Èj„ëP¡Sß„à÷×»|»à_Û7¶¥±Q؆çñ¬ýX3¯cXokB¿å`! õÛƒYê–)9Yj4¥ÞÍRe©gµc©ì¾³–ú$KMõXª@©—i¯;çÅ+¤IŽSôBH§ÔYj>KÍ¥,u¥~ËRŸc©™0Îa©9”º‚¥¾B©´¿Kr„ÍÁÔ¯›àh†ì]»>ÚÐö,#‚~VNI[Å=D1QØ9E4Q¬b¢Ê("8E Q,`S%ïb‘œ"Ž(2Å¢hÊ(¢9EQôbŹp¢ˆáQ´f^¢ØÅ(â8EQ\¦ýnaˆâCF‘À)҉⠣H& ‘Qœ"ƒ(Ö3ŠöDñ£HããˆâEFJ}E:§È$ŠÉŒâ×:HÑŽQdpŠ¢Hc…DÌ(Æ1Šçæ‘RQGFôýl'¢LN´H&ªËˆ¦Ñ—Œ(‡-–‰ŽÙqÄŒGAÒÿ`#4óûšØ|ÉWHÑŸ(G½d§á}!ð(ÿþàL…”_0'Š\mHÈͱZ¦¶‰ÝK©CêØøƒÙ¡_Åc€ûE‘2gò¢]? ‚N#<‡ûÇri¬­ó&ÛÓ6° [~}ø ïVÛЭð¾ÐöôEä”ÎÂÞÛÙËãôòGþ2‚½ÜA/‹ùËHör½ü„¿Œfe½E/—ó—1ŒòYz¹ˆ¿Œc/§ÓË)üe¾œ1.{,>ô0,û|HCÆ3úe»ñ!RœÙ÷âC=tÍî‚ãè¡}v;|Ȥ‡–Ù8LræƒúÙõ6à‰²euc Êêt)¨u…ã}_û:JõA<ôWA¬¿jS%I¶/.m c~Æ*¢ŸÙÖ˜<Óûñrµ®±Ü_A¿»Š¥~/÷W/4–û+H}Š¥~/÷WÙåþ RDZÔÂx¹¿ÚXî¯ µ/K-—û«nåþ R;±ÔïãåþªUc¹¿‚ÔÆ,õ‰žru%Œ»ÒO3WÊn ûn6ñdE‡Í† @r<†®4ÇÊFÓ`"ßAئ>¦q¥)Ì•.g‰ -LëJS˜+É(ÎPô®4…¹ÒÅ6¢h¦u¥)Ì•ÞÃ(VÅéFZWšÂ\©ƒQ9"!îN_£˜ÿl¾dË+¤éÇÄNâ À#%Ò7€®?ç‚ÝO3¢ƒâðj q§óË NŒßÇLg_xÃN¾ZÎ.­HD¼|ó¤*ô‡tÛÔ%5Î[à†*aݬŠvø7"Ží`o{¼çÌîáçì`CÀÞhH_<×l~N•båß”ø¬«¤²™‚˜jÇýñ¾yRà‚I„7‰Bq°Dó[}B ÀOQrØò¥ì:e­é³©”8ß]—ì‚ÖM˜A­£‰h’vqPM,Ziz߈‘wLºö«Fß ”»‚nÿTHÅl{=8œõš•É­=ì+ É1çGœÒ }­ºÓuµóc 5’cÉ y{MàÃsÊnþ÷è÷.p| ì¯:Èjø× ”|V=Á{×`Zn A´°>ùår^ÉÑ“†Àlð߆›ÊüÇ[ç胇]¾KÁèn³ÝxV^ù;Ü/r:~ÁõŸúÜùì¤}8UH´{Ê]¾ž¤Þ˜‚Ø xax,–a¾5T1׌|¯ÉOÁõ©#ÕÏ·§¹ðì©KI4ú޽ à´*þ‰£ù÷ýAx\T-ﻹ ª8)«ø—}ÆQx•,íwÆo±=õ£ÀÇŽ[’ÄUQ‰;ã7Û|É4Ï{ ¼ŸR^¼J–¾wÆÛžz›QàcÇbʳR)ïßÉ8«ù äqæî +µ%y&FuMX”S:›iÅ™Ÿ¤¨®[’¢b‚XA”i ÎÌB†z8?WfÃk©io~’8ξ6ȶ~pTLÞ¦¬¶}¼îGuuÿäyW éãÝ– §p°·7«cl‹AÆÇêQß²Ÿ›G²ïç ‰ã\âvÊì£:•K`•‚&uÌ{ïÀÌ«”ÌYæ>Þ;úzÅPNºÛ=ò–õÅï)Çd%GLÿTé «ÎBš;u¨óKô3®à’ è3%uÜæÜsª·7¤A¡ï&‰ÍÃ;7 1?ô ï ™_ ú6O˜¿…ÞÏ/¢r¡? ÊÞ‡+ iÂh¼‘½í#”†’y&‰»Ø}X¨]Œ ?R!ÑÂ*8„Ä#Ì!æhýïûÃû׎ÐymIâßøo´²ø…§â·e×[tê=ò—Ž_Ÿ {>CÑ"v‘‚[]â_®.¿á%=éVTnçgAx;+ÄYD»2” !«¬%IÌåOê~4ÛæÊû-«qrüÏÙ¡®ø¿Êúv)(Kfz};f»ÂýrrЫ“!|ˆ ýŒcÓ|6jžã‹l/À|ÍVs%›¢ è ïü­Ymó÷ættåätÖŽ›œñ³â\b‰ï5%tõíy—³ˆ¾A²öuÀ'—üÔŸZÈOCŒ+¾\¿~ê dgW›èë=Âýƒÿ¥`u9¥ÁtõE†I55žŽqŠçœäKÝb96i4i6é¥Iãœñ…³žtÅ—Ìš•ˆx…ÚÉ™¯Œ¹Ä-Éì‚«ÿP#ŠdI’w¾ÃEpW0Ú‡$±'úzËO7àS3ù©!>Õ•Ÿ®ÀÈÏ!‹ç¯:~ â\>·£|v¿kjòøÁ)–;ñ dš¹CƒÄðöGb˜Ñ /Öð°Á”ü 9Cò¥œ‘bRT&HcŒBáŽ;ãwÍz ä0%¾xÖl ^èFDZup•Êî™ÙÌ“‘,Öƒ8%ZŒúv¼ÍÐÒ¿Š7ëV|rÊOø+?5À§Pù©2ž.^áOâS}Y8Gƒü÷<<ƒq•19QmM^Z›\Ø”"ˆÂþPÓÑ0Y´/©óŸ Ïž±P]†©òdû ¼Žø–8&ÿ÷'9^éH èâÇ®½€ñM¸ÊŸjRnüm 'îÓ‡Èì{Ú£ö=-FÈô|Žú5& AMÄœÿ§ t6ûÒ•‚çüìg&ü¤ÙO>vó;»¢)œ+vѾÅ4\†¹—‚lyÛÙÊ}Ý¿:4\Å»³e×!9:îÇ.lâªr~¦ï±‹fa²\x DÈqX5øwVÝͪž ÿæðêõ"F ?]VOïùâéîìPk6c…k} ‹§q!©O ®7¬Âxº¿•é[t¦J:u#Å$©–2´%Ž…Ô}亼é“P\#êSŸ±GÓ%àbvX‰Þô‰aDп(AÃ×1ˆ"el½— Ù‹¢LkY«ÒÜ íháïïùh¡^k]-t„!¨ßÆï«3î¾Ô§p‚}î Ì«nÌ@_{æ_R§mÉ6/¢×ß³× ¶%Åìõvzý­B]ìÊ»mKÈ“²¸ðÏ)5üÊz‚Ñ¿FôÍÿå~ëB¶ƒÍ!™JÝ4Þlÿ!ä@6Þ5MIYÉ&¶àå™<;ØÕå{—Ï$Ý`¿¸DkÁaÃÉøÿ­†hŸ@P♉ûtæ…¢Ÿ>Ofü«TÁ¬;,|7ù‹óø|¬÷6²^æVß>T!qËó;a0ØâÑ`Ï‚Á^Ðl•Æ`7‘ynÑìúê v™Æ`³Ì Öõ£Ñ`ÓìþÁn– 6äÇ@»æ'4Âáç™Ážù‘[hÅ9|ýöyü{Ïyãð@]?8‡V‹×YÓMÖaQÃP¨OÑ’Á¥$÷¢D}¨ÅÊÌ¿ÚÈS#’#³ˆÍ„ôÈ¿0ç>\C¤¹ŽÀË3hì|ù,Ʀ> г S]½oï‹\ñ»AF_ïI½ŽÛƒj¸$ñ~ù©ŸÎREqaƳíùbçÐÞ©^€duþÖw¶Â8{2ÍøKò×goècíµýJK<ìKbÎÚY0ŠaNð c1¤]:RÂÂB:Cî®)ʪ…~‹šöþÊHœlß!¶b{:Ûȶ[„-ŸÅä#À,R-¾… ñˆšãqÊWòæûÄ/Ÿˆ'{Z!´Iöa$ ¦ò;;ÿÝÖžý¶ËáÔ?ÇQ¬Oý¥ RÞøĸf`êJLÌÛÊnì["Ëö|éwÝÝÌåËà!Ô“•û²µ§T^£ò5¾(ÃZI°Öý‹ÃºE®BÞ6é-TPèbÃÒ¼:v«6~áþ¨} xò}Ú67¦T€HÈÿS'€›¶ùÍGyC÷ÜjÔº¯~uÁôS¨†û¼Få>¯±4å>¯rå¾IÆ™³ Š]B,5]ð5¨|eþ‰MhI2Ô]ðÕÉt|¥R;g:óÖÒ Z0ðú¹˜¼¼›¸ûÏþàP›à7j+z¡KãGƒénþÒ»{p€ÛÿÐ6³Ü¢Y—=Ûvá`О%H5Èæ_¿ò[±ò÷Ó´Qg*?âÿªüq&åËû…iE4ô{™ ‹¡·á\s7kV¨ –YÑÝØ,~gø©çƒâ\I3ÁE4Wi÷õ§úºìÚ+[1¸Èn„*9žƒUY{A‘0/70€¾?³Ÿ–Ö°§Ç÷Ó n¹dåÁÄxD»<0f½hºÉÊ÷Â7£Ù…àÝ‚gt˜¡ÃäͲœlëE Ý‚l/@Eµõ‘öùÕ§rŸ¶>?ïó«ùCÚ- "ô€ŠÅ_šÖÀ3³wzà>ŽB,ÇŸ"Wéë@uß ½ ~oHk¿@?±À]Åž¦ŸÒDà·ÊÛjü¥èŒÐDg3ý÷aƒ˜fx¨ÈBåïÔ5¥)-ˆ©ðsÑf6eÕœR¯ŸÜ\á'£nóÍví~ AtFã0õ>h1îÀñZl[?4’zƒy4­‡Óêû\±’#ýë,ò ²S˲釲`Ô›ìß°­wFêâõ{8OªÝw;i(ÍURT¡ùܽ{½È¸AÓ€g[LH—‚²¦|!¬eïCd-kƒtô´É¿‹Ûp<özüÁ>61R‡ç3Û_§\Á™Š{$cD/†Í<‚"s¤15‰kÝ.duÄÆfõŰú‘â pÚÖÛ VP¡ž‘t3w2MGIŽw0‹öˤ,ÿ(û-b{Úaœ‰¼éLFú‘îä¤uÒFÒ4Fê!Ò·8i°Bz¬JÚHOÍ b׉Y~`Û볡ʩQ8ûòþïäßÚÈÛÿó»f&Ÿ¥þÜFÞ˜ÅRÙöÀGYª¯¼=ð~–ʶ&³Ôómäíq,•mìÈRŸqÈÛ[þÎ#þu›Ø¿PVÍãb5C~—›WÀfp¢B¿Åïí Ë¢ÔgO( ([ü62‚AD0鄊…²Åo)£¸“(RN¨([ü¦2 QÜvB•¼²Åoð Tðñ[dÖì€!þ˜¿ƒ+»ö®5Í|Ýb‹ß#@ WH·ŠãÑH%ç‘ÍÆê.ÝoéåÁ±{X(¦é¼‡‹4‡K58 9–¨!O="g³g*ÝÉò]€‡do¿9ÓXM¦½ÛÕL[´™ Y&«œéNM¦åšLÏk3=Ã25’3UþWÍ4Ž{1ÂÑÛy Üw{M;ÿ¶ÙƒlëàÎEøé ±uî‚;ÿ¦=Ž{[qç">4‚‡Fþ;«‘ÿl²­>¿ªòÿ´”Ë¿ûvUþ½¶ëåÿQ©Ÿü[i¸°T•ÊÌRT¦–êåÿû·j¦¥©¤–r©t-5— 4úÝfQL.!Q3ZeßÀ“â×~à ï¿Œò8ϼá$Òø{Ž«òxk—G‡RUKõòxˆÂÚOþÕ)¶ð„SKaT]ùÿmªx†oÓˆg(cst?²ùDaÓ'Ä$óùLÃ'Rç™–ñYJ|Æ«|¬*Ÿ‰>·j$¿o+—üÆ­µÑG®‚L¹ 2}¬aó¬šßöt#Ú&_þbÖfµ™v<¾Œ†—ÑÚ=òø²¼ì¡Ý#¯TC»G^©Žv¼b&Ú=òøÒ/íÚ=òø2^Fày|h ­q<>DÂC$î‘LJ(xˆÂ]ÆøÐ :ɶ1² ºÂCWYPqðg.(Y¾œ;³jÎ ›sgÂæÜ©2w’eu0Ð\—<*¯\‰¬0šÈ*á‡&ÿ¨Êïü ÝyÏ€¦±óòÅC¸0=‚ïÙ8þµÓ§ö7T°Sœ1Á‹Sn•$yû@Øëx«^K}ÁÏÈçißÉ2ØÙpó×/*øûC_ðÕNíáíqŒê…êÓ/ølÀ/(çWÿ'ϯnU÷ÕËÓ_ÍÒ'SzL?™k’>žÒ1–ðMûÚ$ýAJǵLßËLÒ»Sz=Êo–Þ2W9_{¨YzJÇ­c¾‹ «?[žàc$‹çèîÖsÊò°•­ 0_®gÞžq¿ñzrЉÛàè^YÏqðh.lunhÎfÒö¹/b7Oëk‹ùùÝÄáèÌ~AtµžÅö‘üªR¹zq,K¨’¥âs9˹Ï9‚fük¼_½ÍOêdE*´œŒÄs’ãs*Tªtx©9nÑ•ÉÎÊÀÙ!O6ÎÔÔá35zãIÌnÏn艹A nѱFÎùÝ婚ïp€±Û0M³úGã4Í;4êË.È/È ÷»e\¶Xâ+[PeØoÈí±Ã|f=n_ãoŒ¾Á|JûþSò0â“à§QÅ.+!¡¨ŽAÙ–~ê¯Xª}Ÿ‹öÕíëÀÇ&ö·—ÒÃÉþÞ3IßDé8gé{w®Iú›”ŽS¾…fé¯Rzc²ÿÏLÒgP:zgß=féR:Î÷ûZ˜¥wŸ«ø¿‹ŸšùŸ¹Šÿ;`–~yŽâÿ¾0K?=GñC¿0I?ˆÕä°þΟ=tï ;×R¤oŸŸÇyÞ+>FKŽ´AÎ{Óx5¶ÿ3Cþ&·©ðscv8ƒ'ÙÈ yS-I^'nGÙÿm_ñ5†—;B¿¸È·ŽT<+«Ã¹õ¨ ©øƒŠ&EqMàÇý½u«AP½Ù@¸•ö±Û½´{%ƒ4ï {ÿ3¦쀞åô4&A=‡eá59{CµoÜ¿ðêpÃ~ŠùªWÖÓëíUÓ+oB+ÝÊ¥eA‰¹½¡ã™e}uß%¶Ï§Á³´/‡dÊöÒ*—ÀÏ>ÏìïóŒ*þ¡˜ÜðeÏ(ÛväoábXÊüg¸ªaÓeÿááalÿ!~ë¦î?<³°ºý‡3Ë%•®ë*“Æëè»$âéJD\lœß¦ý ½µüy»f~à»"ðœ•éy¥Óô¿Â?MýÓó¶¢ñó1oX-ÓûuÏÊï¨-`²-˜|"[ª‚ïAœsç¬åý0ò;¿Nå÷’¿fz~Ëe~ÿ¬« ík,¯è„2w™ÒÄß–òŒ_­«ðßo Èç§"Ÿ&ʤ²ï]9óœuFù†ûÁ₱ ˜Ûà»”o9ã´([.åóÊÑñÅkt9„7lš´>à»c}Øb —Lw&±ã¶Ær‡å¿˜ô¶Æ Ëï#‘¿#нLûŽv»½DTêëð\•ªî5ï7Ùµ¶Bóñó!`ùNú8Þ]Ë|“èh‡‹ÀŽ¡OóÝ'™_ÙÝn9 ªqËÉôµº-'“ךl9²Ö¸åd•=Â|ËÉ'Atžˆ§‚ÁùñÇ~çG >Ð·Ñ -²a¬dt‘¶Ù{o¹äó÷`±tÇh·/ÒÂOKr4^bÅ™ù;úØrŠGþl ÚIW¨Ÿ¾:B‘¿Ù÷ÿ˜N ì8ŽëoŽÖZ¶wø³½þñQƒÁºø¨ì£šâ£%eÌ?;ŽÔ*>–UðøèôBYé.½Jzý¡Y|t£!>jS«øè5ñÑ+Ö­ú0@|û”_|´ûÅk~Î7ÆG³^ u¢ÄGõšÄG-‡(ñÑɧþ—øèÉtñѨ®G|ôÈ)ý3]´ãÊRykí‘|YN-Eu({ßµ2ÄG7Ö*>ÊÊ3ÄGâûÚøhöûæñQϼâ£ÿûùˆ—ÒtóÓÞ¯i>¢ÛI6]yÀt>bA y>bEžr>ÞCÎ¥któ+טÎG \c˜˜¿&Ð|„-×ÏF>xþZç#6.0ÚHúóWµ9P±—fß?] ØËfé§(ãÓwû]çù¨—úéñ¯&üüÆñÿÞÿñÿ2þ÷;ÿÕzüW›ã¿Úˆÿê€øÏ÷Çÿ¹kÆžþÏ]ÕzdšŠÿ|³ù‰4³ô&i*þîë¿[ÿ»5áÿØ1Žÿ>óùÈ þó”ùÈ„ÿ;züß1Çÿ#þïÄ®?þÞkÆŽþÞ«Zì§âoö}Rß~*þféÝû©ø÷½Þø÷ÕãÿvMøÏÿ™ã¿Çÿá þsdü{…þ«ôø¯2Ç•ÿUñŸí¿xÍøÏòÃ_¼ªõH·Š¿ñû&Zt«ø›¥r«ø ×Aÿ[5áÿÊŽÿ.Sü»ÛügÉøßj#üßÔãÿ¦9þoñ3 þ3ýñ÷\3þ3üð÷\Õzd_ÿ™fë‘}UüÍÒŸî«âßõÆÿ>=þ+kÂÝAŽÿsûRðŸ¡Øá¿Bÿ süWñ_ÿéþø/¼füsüð_xUë‘‚Šÿt³õHAÅß,ýAÅ?éz㟤Çÿ¿Gùžã_jnÿ7+øç(ö3áÿºÿ×Íñ݈ÿëñŸæþ5ã?Õÿü«Zì£â?Í,¾¿OÅß,ýð}*þ½¯7þ½õø/¯ñ{”½ÿ­¦ø¿©à?UÆÿÙHÄ¿à5=þ¯™ãÿšÿ×âŸíî5ãŸå‡îÕàßÅ¥âŸmß»TüÍÒë¸Tü®7þ zü—Õø=Ê.ޱ)þ½Û(øgÉøwnCø¿ªÇÿUsü_5âÿj@ü§øã?ÿšñŸì‡ÿü«ÁÿÁDÿ)fñ}¢Š¿Yz‡Dÿ^×ÿ^zü_©ñ{”ÿïLð `”ÌñŸ,O©=þN©-]ªÇ©9þKø/ ˆÿ“þøÏ½fü3ýðŸ{5ø/pªø?iß;UüÍÒ:Uü{T‹?;¯1 O˽àÉÂ+l²¢âÄŸäxñA+^ü²‘]vç x^ÅK´|#x2íHÔ¼ÛdkV¸§¦ ÞÞt8M\Y(»/R¼x«|ŽnOJä6œKÝå[Z$I¬\$ÍÝœ†r‘[_RÎçx7A‘Gq¦Ùy Š<ÞÕ¥ÇqØA‘ÇɉUM'ÿm³'EáYeP3[³’±‚mN‰~í:îßy8^·§÷K5íßiTÂŒfâ¦Úîßy³=ß¿óÀ²}®=ºÑéKÔý;í–T·'eI ý;‡&ùYÍÔYÿëþg&­¥Ã¬š÷ï¼ÚKч/7Ó‡^Š>¼l–>¾—¢ »ýÿدõd7Þ_¬ ï®ß1¼gÔïQïÇ&Éx¿KÃ&Ï *Þw½PÞÃ^„÷© ~x?5ãÅûµ F¼»Ï¨ï½=TûŸh6Þé¡Ú¿Yú›=Ôó2î1O«ü§ÝSƒ>òiólˆ¼zäö†f0è‚å;CÎN`;C´B&nb ŽŸQ´Vóüxv$W„æ¸]x=c¼Éq»šý uz¨ûA\Ê„ÖÃÍöƒÐx!^=íîÿ¹}ÿÿÿÌý \”Õ÷8Ž :©8ã2ŠÚ‚…Ši…¡‰¡Åȃ€â‚KfZ¤¹¥”`¸¢)=NZi™e.™™K¹.e¡˜¸”ûn¦™ÙÚÛ]áùsî½Ï2 X¿ÿ×ß×Kæ¹ÏÝιg¹çœç.’öhp±"É;›uD©"åÆ˜äö UM&dfçß‚ÌÝÎCO•+³$9ѤØÖ +…GÇàÀþ zJYƒØëyô:k[Pÿ‚Ÿµ.É[$G@ˆÛJatp4#ïH·Jò>g‡þåT+¼@-"Éû¡üÓPÞ¾Á ¥_ g…B (ßY ^òtrOêÁ12À€K…1&ü—àðÄPXäÏñ îŽß'?0ü®·UñÛ?Ô#~›†VŒßð`Oø=ÿœ7üz#~Õ¿[ýþ/øÙ«rü’†éð3³Ìo¾gøÕÓð»òŠGüN¼R1~›Ú{Âob?oø-nø!*Ξÿ ¿ ^å  °EÐ/u¨;ý}ÇðkÓFů¶güJ†xÁÃëÛ^¥GlØÿ…krxó_q§G½M Þì0Þ‡x„·‹7x¿…{¢ÇÅ>^ù-\¥Ç+½þ~ùŒÝ~g†¸Ó£ÍF†ß’'Uü¦öˆßðÁÓcm;Uþ×·þ?ÉÇ !ÿàM[Ï念&ÿ/{–ÿ—+¦G£vå¿·7zø~†sÚ0=~á6ÃpH²oðc ¾»î–âL8%òy›¬=ìëSÐÞÆšXuý{ŒÚª+¼ÑÁC¥Üèà|„OAκ|LÃõ¥†ñíÝÇÞËÞÛÞj¦â8G&ä:¦Cá8ùJŒ\œ ÿ–?5줔;À ÿ­r)ü ÌPeÖ,)7Ùš §[sn¦ÕTl£Ó€);ü®HðâÍæÔÚðÂÙ,T¬Š8EU[5ÊÉŽY86'}ùœü´²2̦´þŒ­.eå[¡´¹¨ºb{ím$+ô-'Âÿùs¡–e½Â$9à?`.å&‡J¹ƒC¢áOP¢¼Ç¾¡Žªl[1©T‰“vfù`µÓ\?ŽŠü-¢‰Ž”R:›sò'¿œ Ë¥ òà@¨8œ*Ö¼£Øv¤ ¸;à]oxrŸ[ª$È—%yA±í„¤1Ù<æ!)«ÐWrÔ,“(¶ñðÚå#“‹ã"NO\…ˆ/…wÔ»U’'J9JÚ£’œC`¤H朓C®|E.äš…Š­ùK¼ )â·‰%y¯bۖʘ7Ô>Ðþ‚ýù/ ëH(| L™€Ô’ZÆ­ˆµ²"¼'ÂN¡]`Ãò„9/€¼Û'^¢’cP*“$]ã®ü–LM½8—Xe0²‚b[0)ýê2 üêDùJ\J±$Ÿ€Þ[Û—0jJ$y“Û ù{Îþ” 'fDË1©0øD¬B)7=¿0fƒ/IÌ€Õ [0>æ&/Lp$/LÌí™ ÛÚ@—ö©Oãp¤ƒ¤²8mµTh¤A’¬ðrj{*R]r$.TlµP9=?'?£6ÃLˆàääƒògÏÇË¡rv¦5Ég6¥7ÞäC¼ZV Œ4pZ©RI´(òC)wf^_¯aøàU¤„I¾z$”`„‡pÿçáOÎI”ÈS©$‘’ßqÉ"J%$AÞÍæB`êÜñˆÌgHfM„-sòµ³¶Eê èB¿éœ~Sˆ~§ßòF¿ôA*ýþf¼e SÎ[è4ô-W: y«R:­hEtÒ·”@úû¥Ö¤ïÃï£Æ£ƒ3pðBñ)•†1thšÑòÖ8ù€ó`'¦¥7ú1ýýpkÔßfM·–<ëo^Þ—ÊWÓÊ/òVÞ8_"$¡"C&9¢ Šþ£B5Y¬¾–Úäš3(Á‘hæ“¥lËÌ(Uœ›h²ÈÀ¡Ã‰qç BýõÉ@õ·%ÓûHÔWÁ³Åc¶Ôöc…Ó0ý4S6aFÿL)„4̯8GÆÉÇTVl-ï²oðg€{7²\ÑÍõC¦ä­hóóÙ'Øü\ª0²à }ÿ¬ûüÌg\Ù6›­® ^eü­diü—rL’o+¶pήw‚i«”; Ù¹J…ì¼9ËÀÎyYØù£,Wvþû—'åÏrãe·ùŸÏú®³>áÇ”üPšíiš¿œ ß¦yÉ$&yšÚ¡/+ΠHfäŠä¶ZÊ\…Ôâ¼soj–½vBÆ÷¡±8…îueIëXb)³bû¼`©;éÈR£Þä,å`ݛҶ¿ÒƨR…Ž¢ÂÅÖâMmR%ÿç9ÑÍ_Ï1f Á±R§J†}¤nŒäù†ljësµ­Ÿcü«–½‹ùlïToúppý|öœÃR§˜iØTÌ”0Õ•™ºL­T7ÎlnÐB?Jrv0™+Ä ÕØLelW•³T ã· ä·$»e½žáÌŒ›=ñ\ÐpÏ!¿b둚Õ'8p0žŽû¸t/øo#%ÉÚ”dlÃR(ØrŒ ŸmSÆ"ÿÖO°ÙÈ1ÈfG2|ùC¦ŽÕ ü[}½3X’ŽÁ’Ü,‰µÝ—±U’‘­¼ÙKË8-$pFgzã/ÿ~zþšÅù+Ôh;9a‹¶¬ÿ/Œwk²ñ.OöÀx'»2ÞþÉ•2^ý¦‚ñ´ùðD]œm:ÿ'¼Âùs9•¯§•_SqùñT¾®V~ª·òîóm$ŸoaJ( ËGÏ$×mŽ­Ã¸²îk0çô~¦2à>˜4mÕ_Ü7¨à¾›©È}}'imô$QªNoÆsVà ·“=̰îðzä'¦¯š¤›ßîj«ˆÐ”\›á :ËÃËIA;“;âÃnò‡>šÈ´6°‰³äðoN¼³[—úÕœ.¸gíDĹ ÃSÏAïNtå ™¹o`=fYC[ÒD HÊŒ¼ÑÓÍÿ°ÿ+UWs¼AÕù÷ êœã\yâü¸JU]Ë WÿƒÛ³'j1{¶–Áž½ØÆÍž•Ô)8\‚CÑ;ÑpáÀŠsÞ§NΕÙ/–qÞÆóówíÿ»q^’açùÆyJ†ë8OΨtœ þ8÷ï†0ÿ®¦é®ü»km¼ùwdMçöPx寑ûø÷†‘§:Ü@ºmÇèÒ W¤ª…ò$M†MÂhÔšqäùØ WW-ÖxøS;1H(p€4g‡-Ru¸%eå–ÔÄà4¤ÔøYÊp¦@z2+j®°›À볂×$A'ñÜ"Jë&âSFÑ—e'wcz¢¯ÐÚ73Çz³oš&éùo®I }èmÇtnßÜ [§ÆXcVë1Ñ_12æŸé•2æ£÷ë@…ó5#ÎÆxiº/RL ãò Õ¯¯0c~â%E™Å°ÃŽ6SGÌchÖ}†Ç8êÌþnqÔ‰ýu–»7BK5Ìê®Å8­0XjÒC¡‹”Öèoˆ”šŠ›Ïy²ôÿX¼º>¿¢~8Àˆbwâf öñ`ßåçÕ`úÅ AÖ€ï‚IàäìÅØ´e½Ÿäèï;¼ž¬¿}€›¸}GÑZ@\¨p¾ýßë:+ggz]@Ö•ÉÛ»]K•¬|Ë”9PiÄ_Ë”Ä;+5?|]o‹½û:Úb-ǸÙb©¯»ŠÝ(,'ß’óª‚fOÀö—±ê2²´Ñ·¢Ù‰ø ês÷k˜•ôöŠ·x Úº†yÔçqñ¨Jê›YýZXËc^ãY•Äo4zl0è˯iôHÈÅ©÷vœ|9­Ë߉7™BŸ»J"ˆãƒö‘â Mv}¡ H®cK|ÖÅ9!'…ô®«1â_fßUvûм0EÌ ø=%†k“Ž òdP%gP•È5ËO´.m¡hJÈð잪ûPòëÄÃØÿ¤T’k3ŠU&HT*—¨1:¹®”}ú¦êÙ§ùT‡RÝØ§uª+û<‘ªÓ³IRúTîrD*¶S£õ.ÁoÉF¡áßQTDÞPUÃ]y%ŽÑzT¦FTîsGeÈhWT^­©wÅšÌÞºX–QíTh*£¼Ùƒs$ñ¬h°þµ ¸p”aªýh”‡©vâ(WLǪtªý±ž§x··»ø1{»ªÁÞnêfo‹ïÁ¼¼¿¡ü'<}&{Þã ,,Ìâÿ¡Æ#æQy?­ü¢ŠË_õß8(¯=å¥|j"c‡-û‰[Jñ—ø÷HoöØœÎ*ýÿYõÍH­Wô@ëY#]iýÎÈJi}°Ñõq’Þÿ´[¬&Km²y…#ŠOá,Rƒ#ÃW F&ñ~ö9ž{£Inñ9Œ£‹&“ÀôGE̬ëìà¹Â¼†çYì=ó˰Ã$­CÖáíǠÚœC=Ë£Küo„×ø_ŒJ/á?EœÀ¥ôßQ®d¸r׆{ Ü‘á®”;4¼RÊ5¬­“RŒé’° úvÜK¾HØel`ÉïZÈ<4G9ÅÞ‚{ñÛ­`ˆl3‚Wã”-¡5´-ãnÆ[æ•ÿ£<Æ[½…[ÿÓ€ç3 ø›Ã< øàa®ž2¬Ò_fÑÇN>Â}ù˜ú·c>H]ÖòpKóÏËÑ˦1·X¡)Kmº¶^æÉzêú¥/ ëXþ£õÔõ4í:{Èo¤ÕÅSý:Zý7ÚyÈ÷Õê7÷T¿¤®Z}Œ1ßaû%LÚ Püø¬~ÞŸ‹ëÃ<ék‡m>–O…ò® š$É'bÐ’¶ž~ šg/U¦„£#%ÎxúèÅ®ÔáK•x{1PÒi5é¿djýAcŠ-LmÚè}Nm#Ä-žíšíMY/ðÉ{ñÙ1Ä+>«°üÂõ•à“ùâóe¤ŸñG<à“ %‘Š{{<ÒxÈ?“§Â‰ð…x‡/Ë›*ƒïÇl„ï>#|Õy€oN6â[î¾Æ!ü*|ÏtDø¦ö ߥ (?(¯ø”7¾aÏÇÏ?~ñõßæ7þoUøžEøœ/{…Ïåó¿­¾g§"|;Ÿ1À÷·'úÚ¦"¾e^á0áWáÛðIÞá{ ËU_f&Â×Âßsù/ñ½ã¾²4„ÿ>¢ï²¯ðý‚å§|SÿMBør;àÛuØÿMB|½Ã7 ¿NÀ×õi„Ïì¾W±ü™u•ñß„ïB£>òßaÒ¥^áëø:¯Á×áú’Wø°|deð=;á‹3·ü'þ#ýQâ¾S©ÿZß‘pÒ/z×X~áÚÊø/ƒô_„¾FžàKF}é¾I£þ5¾òv¤ÿ¼Ã—ˆåM•Á÷ãXÒFøÞ>è‰ÿPÞÞö®ÿF!ü*|OÿMä]ÿ½Šò´Æk{˜yµhï¢G¸÷öNÄùÐ{{e#~µ½ÎÔžs wùÅòù«+¿ÃIÿµ7ê—Æ/}8â[ì¾Ï_@z©ðoKúÏ;|éX>¨2øN%ýg„o×~ð};ñ½å¾˜ÿ×¾Zß²¼ÂgÃòS¾®¾‡^!ýnÔ/žà»1ñõߟýþ¯Tú’|˜½Ã÷-–?óU%ð Lú¯Q¿ìó_ÛÁˆïM¯ðå<‡ð«ð}Fúo€Wø’±|deð-H!ýg„¯‘'øÒSß^ákÚá_%àûáIÒÏ{…ïF_”§U•ñß‹¤ÿž2ê—½žøïEÄ×;|?õAøW øn¶!ýç¾9XÞT| "ýg„¯d'þˆø^÷ ßàÞ¿ ߃ßôþ^ák‹å­¬Œÿ^ û¯­¾ážàkKúÃ;|þÉÿ ß?ÄÎç¼Âw¸ÊÓ ðaà3D±¥=Žã€?« ' #P§•2 ¸;ßÚÓjºb Àïn‹º™ÁUl]!c{¬Õg{lˆ Ýaèƒ/bÑü¯ÅUUÿl´ëæŸÍS»v6ÐVîø=Fðåà{V…¯lÂ7­Ÿ_K=|_õ«¾pæ/Q,)ÜG’ëU²O©T1츨0¾¶ Ÿ>z|\’m›£1Òû†º,íF7L7ãцƒÄšÓ?ð©0~×Ï?èÔÏCü i?×øÁ#Ô÷Ïñƒµ>8TÇZ—² 8™}K=ÄWC¦´¨:™ÙÀ4¸žþ~¥;éÿ' ü=o·þîØõçïú5ùu™àïÉ­Iÿ÷õ®_±ü™e^Û»Ñ ýuµ½#s±½ }¼¶÷–Ÿå½½Ä®¿ÚÞ§¤_‡zoï,é­=yKÎŒ@`ãün+<úûY²§óz£ÏéÞéÃê7†úï@}-–½OkâsÑÄ'.Mhñç— .TÿÕÏ%~"Úïjh?I-æŒíwðÜ>ÇïA ¿°>je³¨¬${Ããã¾*|¯$»ÂùC¾¶¯«JoÁóŸõñÿØžƒÂ§TãjwcäŸý«PÞ¿Nv•÷_"Q¾§%—rÆXÑî !ïÿBÞû%ä½G²yo›ì*ïaɕʻó1.ï3{U0Þ|Ôñ.LôOÃ|ç¥d—a6êƒ8Š=nÔ…žôAÚû—¼ÊÛªXÈ_½DµgGy+îéUÞ&aù}K*“·}4~ì¡òcÆΣ{V"oÿ˜ôò0Hk¢­h¢UOoò¶Õ¤Žï½½ÈÛW†öÏhò|ø2oÿçÉÛd“Š_ºÇ×9WTžÙ£ú÷Ðà»Õ݃¼u1éåmvÊäíI“QÞÞìú¯åíÙ®ò6 #Ê[ƒBÞbã [þy»ÐÝ og»{·Ÿº»Ê[A÷Jå-ãQ.ou¯`¼ŸTŠÅx÷éâAÞ0ß9Éu˜ôþà·bAï”n*½—ýÃé½ ©~î õ5~›¥51X4ñ|’7~žwJ…ÿ‹ž^øùƒSúö­Kú‹öït«ˆŸ;ŸRñ»–¤VÞq‰Wþ±[Eü¬Á×¼›~nù?oëV?Çötü¼6þ_óóèn®üìxù9¶›àgÿÈÏ·Bþ ?ãú!?×éæŸotuåçk]+åçeÍ9?wëZÁx/ÿUïi±øyÞIàçU®Ã,¹®Ÿ“Äê|ZJi\˜¯-ÉqØúﺩ8¯N;àGOºÅ×5ÿÒ\ŒdŠÍþ”ÈÖ`³ÜiÍ0lP ©{ÝF7+¤ÔéëÒbž±ºIÛð aþŠ$߆áåØåÕmÕÒHÈ%­äˆŽ*¶z´Ž©€{$ÎùB®'°¡Õ·—T†í-Öµ×Ù­½m †öE{Kx{Ÿœi(PMS‰äû¤*P†•³Z£\ ¬³˜,óÚiOz•}74!ìñ¸Ú_sÑ_y¼çþösùu>æIÿ}ý-PÖ'j<5 íƒcñÞã•ß@ùÕŸx¶Àýàõ@¯ß;‘긆Û9õZ™;Š®å—Py\öåì~7ågPy\5àlr7åÓ¨<®íu^¹zåŸw ®un¹›òÑT—É9ß½›ò­¨<ñ¢·ò’ì#)I¡žØÃ­½ë¿b{¨í©=±9„ôeœhyß$vã.´©_(Éæ°“RÊA)âè˜vqr¯# 9»˜Á… гl:ã󺜲¯¨VœY5QŽ,Å i¥ âZúnì­lVl›âYWÝ­ ²=Õ9ÛåÒ€ÒýÖ=P²l: =IÊ¢Z’Ü©*èn,i#J ÿ8Ö¾¼Ó;€†xÛså[Ï&(ð{¡¿?¨¿ƒØ_TÕ9J_QÝR$ô Œœlû_;œmŽÓÚ¯¾€WFPØ Á Í8 oJbº¤eÑ‹'"°ü—+j3«Ä<&^JÙ%9ü_«w[‘RjN—üv(¿•ÃPîJÑð‡Íàá><Ux¨-ñ¡<¼„¾aùnëgÕõy!´!úªFyËh )'&È©ŒôqGÆÄésÒ÷@ÒAj$È…’|$ÈR;Nîô7AÞ.Éuâ°¬ ¢#JÓi?›õ#¬‰-5¬gzu‡fíHcä‡8y—ôª'ÉRU¦ãä’|ð|¹ùÙ¥U¯'ïBb 5°—Ьü Ñ%öÅX+Ô–ä$ì2TÖñØŸ¶a1(‘V“&à n”|è!/ H¦´…ÒÇâpaéƒø\-A÷@ŸçÔDPäÀÊä±Þ0xùÔmïr˜›Á›ó´²§F¸Šíf+Zµ+ A:ŠXÔB,ˆ. ¿ yãKã¯lò¥ ?`Ó&$þ¯1l6tH0>G`þôC ¤Ép} ®q튬^¥-"ø•>ã ’˜4K“w/òs/ÆÏ[j»òóè2`Ñpx¸YÆû1ÁؽcË圱}c·×3¶ËÒó8&‚©ŸþñJˆáLL!˜úè˜n˜ºãâ8Æ×õ‘¯%vHÄžDsÿ‘¾QrY¬®ØD—÷/„{æï†ÈbS“ë×K&Êûp]•h¡𠔎CMâ™Ë]û÷Âïõ‰S8F¦×ñ‹8%:Õ’œÞÇÛ } {{~ ¶q§Ewðš(%kã¦N‡­Ç²›Páå@®Gv¨û'ƒØþAIìŠcçÃ…í”rÍð\üDk‚ÿ—ž:lY;n1±z ¹ð¸oÚ¬m—®Á¢~q•©óۿ˘g[ãȧÿ¸>ùúVÜ3ÆŽ åçßÕ相\àïþÁÿÞÿ’„½íb±zˆ[ê'|¨‚´ý?!k}ÛÊàR¯þ°} xßsQ$d+ùîùš=$/ƒ|dž"Mr;0¨·97É,6ó_„—RÄóà¾x@¾æmšæÃëhG¬€BT•à+p#”ÑÓ?€Fà»±Dÿú:úèéÏý‹ª³”êØNEÈ ±ï¶Të}^+Äåþ5ÍåLiØJâ°‰Xοí3|ëñ9žõXÀ¤ºöƯð1¬Áõíhà[Ÿï2!ö—ðÛ^“ðëã¿u5ï¿9Ÿsü¢m„ßO\ðkUMà׈ðûäiÂ/¹£†ßñº~«êzÂï¯VXuØÓ¿[½9~Ø8~9O ü¼Ì·KÖ ŠÎ{-Ž&/[7ßv„ùö¨—ùæºîܼSº“z-½Ð8ÙvÝ«þ°ÁþÜÉí!°?q®ÝïÕzúL­+1ÛÕ»ÍÉã¢KìK³7k  fô¬<ίi|÷×Öæ×͵=ϯÐbÖbf!º¸žçǽ«pð¯„çÇãáêüØ,°¶w·Õ\ý1¿þ¢¿ë|¥3m¬˜W‰ið˜K…™Öë¿î2>Ž€ߢ ]cÕÆg¾õ¿Žj,Pg7{$*$Ýüün·ù?ÏÏŸæñùy~ŸŸ?ÉãósK¨xSÌÿ¹yºùÙC®øÅ ü: üb~Ñ¿ßÓ¿öw_r3Š©ø¥†ø)54üŠjÜkü¦š\ñûp-Ç¿9k9~³×ºâ÷öZŽ_îÚÊñK'Qy¢†À϶ñ{H‡_{Žß¥Ø¿¿Öpüίáøý¹†ãwn+~'Öpüޝ©¿9h.ÛºUøýBô{¶º†_Ëê÷?7üÚ üžøµøµqÃïQ_‹»ÀïÛG¿Ñ÷ ü:æ!~ïÓð‹¿ïžË_¹+~ÓVsüÞZÍñËYÍñË^íŠßøÕ¿q«+ÇïðÈßL³ÀïY ™f ¿æ{.e®øýšãwäkŽßá¯9~‡¾vÅo×׿_WŽß&ˆß×Õ~’þ\PMÃO®vÏåïŽ+~Í~Í~M~Ánø5ø5¼ ül„ßÞª*n@ü~¬ªá·¢ê=—?7üÞøŠã7ö+Ž_úW¿´¯\ñþÇoØWw1ÿÑüç/ðËYIóŸ¿nþó¿çòWêŠ_á*ŽßöU¿ŸVqü¶­rÅoÓ*ŽßÆUw1ÿ=DóŸŠß`ÂO©¢›ÿªÜsù+qůÀ¯¾ÀÏ&ð«ç†_uß}w_úƒ4ÿUøù~éð«qÏñÛrÛ¿WVrü†¬äø ^Éñ{y¥+~Ï­äøõ[yóß4ÿù©óß šÿütóŸß=—?7üÖ¯àøå­àø}»‚ã÷Í Wü–¯àø}¹â.æ¿ûiþóø•ý@óŸ¯nþó½çòWìŠ_5_UŸ¿À¯Š~%Ë9~·—ßÅüטæ?ß?Òü磛ÿ|î¹üÝrůÏr¯^.âÕËE¼z¹+~]~qwßF4ÿ™~§ˆ~ LºùÏtÏåï¦+~_|Éñ[ú%Çïó/9~K¾tÅoÞ—¿¾¼‹ùðÛ«”püæ~?RšÏô|OåÏ ¿[Ë„ÿ·LøË„ÿ·Ì¿¿— ÿoÙ]Ì iþ+ø üN—køí.¿×øÙo¸â+ðë,ð‹øE»á÷´À¯ý]à—HóŸŠß„ŸR¦áWTv¯ñ›zÝ¿¿þßÂÿûBø_¸ù_ÿï‹»˜ÿÐüW&ð;µ™æ?~5î9~[®¹ùK…ÿ·TøK…ÿ·ÔÍÿ[*ü¿¥w1ÿÕ§ùïŽ*„ß³w4üZÞ¹çòç†_;ßS¿¶¿6nø=*ðkqø}k£ù¯T•?Âo`©†_|é=—¿«nþßçÂÿû\øŸ ÿïs7ÿïsáÿ}~ó_=šÿJTù#ü2K4üF”Üsù»âæÿ-þßáÿ-þß7ÿo‰ðÿ–ÜÅüW—æ¿Ûªü}Oóßm ?ùö=—¿ËnþŸÀ¯™À¯©À/Ø ¿F¿†wŸðÛ[¬Êá÷c±nþ+¾çòç†ßŸ ÿï3áÿ}&ü¿ÏÜü¿Ï„ÿ÷™?ZK£}]Ý ™Ûgñ©‹©ì}χܾ¤n¨Ê?ˆ²Øýkéã×^ø¿6ñÿ-1~¿l%þ¿¥ãÿ[÷zü~ÿÇuüF/æã7j1¿Wóñ¹Øuü^ZÌÇïÅÅwÁÿVâÿ›¿ÂoÁMÿß¼×ø5qÃïÇE¿qü6/âø}¿È¿µ‹8~kÝÿ~{oü:~?ÞÐñÿ{ß‹—\ñ³üj ü~5ÝðóøùèñÓŸ[û§òö[ÈÛ?µ·ÿëBÞþI| ù{¡˜¿êÚ¯øü÷ûK½œÏvƒFO=ÿýß¾Vó~ãùï÷{:ÿ½±Ûùï´°âóß·–é·,Tˆ_fãR/çoÚÜñûwgn¶jlÀ¯icø™Ýð«Z9~ñ[î¿Ju÷OÜŽKá§ +¶¶×9~m® üpõ/â·=áY!~ øÅ4ò€_H#Wüš5ÂÏlâ7"¿"üŒç§þØÐ&_Séçz~ê¿?ª|PC®ýzÀµCCW\ŸnX)-3,sݯUáyÄ{½ñëà«z|‘ ð Á1x”ðB;&1”7/NpŸeçU:MÑ2}þï˜<6Ð00‘æ‘@×ih˜@3ä10ò÷ ¼Gòu<¢ÿÓ½ #PÜÀjRWÔ:7¨”æòfwþÖãFÇÑç,¤E…—u‡ÏãN&Ús ÿÃᤸ[ þ‡Ì‚?ARn²Yœò\S±…b‡î{ &8 &ŽL~ÿüS‚ë¤ gÞTèó÷ã—Kv1ÂÝý'lub º§oæä›thü´RZ8ë&.–Â%{7ÿ‡Ã;½&Þĉ¶~}Z$nÐb€ç­Šm1öMµæÒ¼w îëáÂ'·ãâ>…²æzªÍÅÁÓ,}YaôÒ)Dùw;.P¦q¥sFè‰ )W»f‚Ô)ÉßÓ›‰ðF~þ°cácT‚Ž…Çí¦}Ø*ÏÝŠmÎ¥Ån±V ¶´ÀC>åì`“z?6*Žù¼lWZ8øáä!ìDð=ê¹_òѾ€£-Îl•¦nC4<ÜÊíYv»Ú— úÄà\Äi‘r»±šø'ÿH´_6ÚaÇe2=&ÓÒ7g£M¸TÓ¿5TÕ&6F¡ î&Ó7ØãSM’A™J~û%¨ ~=`m Áë.,™t¡X/,ÈŠTl ˜Å(­ÆöA5,À«|?i#6ë&ÝTœ¿í-ãÄŽ¾X¢Û8úÌEâ$·£Í.–¸ìNfÅP¾ÂõŠz /Ýš6K.Å‘~òx&Xò.çIìÞÛ] &¼k¡ ®ñ£ ù…ð³i÷òcî¶iE8Ä{œ_Xô‚œŒ0í0M¨ÍÚʬÍv„yžÎšd2m“ Gš’U>±oÌCRJ%¯f¸}zÍÚtþ|DŒi ÞˆgVš~÷áhm0µ€z¤Æß%œkŠKØU†Ñ¥Bç/ˆB3Š=¨a|ø²R››0é¾é€|:.¢þó(mŒîú£ŸþæìŒÅ4ü<ŸÎÕ ð– l|â®÷ôpí›ñ·àžgÈ=s­Œ{¤O^ 2„žØ’kÜ \uµœé‚fQç+¨5ˆn»´ËÇéŽ@òJLØM0a"M7'0,•}©ÙÁ±<}a™ Ã2äWèîˆqt6£\8™˜˜n"#ØÞ¥/IØÌ QÙ6É—Îu±ès žuªˆºÞ´%±²¨eë:²›3\è—ö„;ùz3Ä2gqĪ^ðH>Ra»£V‹Ç3ò ¿FX}ç#°ÚèX}áƒX­©¥ÇjÌ_ÒaUD¾xµœý/ùh}ž%ï®a_NMöÉôjÑôp§ßoûATS*&{gz/5èò8-&,¤¸K?ÐxéDË:°Ò2÷Þ )U°d ”ŒÒ–ºoaYk¨¤}O4.Bf!ô·t!š="DÓ“_ŸÍ–>ƒlû{7òø\¼r?Ìbíc¶´€éèªÅx×½f±Ö°<¾k|BzxŒªiy<:¸ÕøgÒ;`ª ¦‚LJ¦?Ž)3¦BÆ?œ„)+¦BÇ×K¯ƒ©z˜j3¾jz•Õ>E¾Ob @è"êú`0:§³pÃ&6|9õ[‡wlÊ3&ºî¨b›ŠôÆÁýKhÅz×*”ó qÂ>È Tlo³vŒ@¢š¤ØRYNMÊ SsB[o–ÃÖ#7Ts`ŠîÀrêQŽò—È Wl³5ór,Î÷dÎtê¨áÁ¾Àó¦´Vû ÂÐݪ€]Ï~ø Ìðxsémñ ¤Ÿ,žë)¶×ñYŒàf¿ Ø1zß…ô®Y“Ñ»5Ñ Œ« ‹5>ÐÒ¼ Á7‘ð&ß$¡„²öø£ìD¡úý«*½Ž]cô²2zÝΤ}ë5\éµÿ¼Ž^׫ëèµþ¼Ž^'ªëè5ÿ¼Ž^ùÕuôÊ:¯£×Òê:z ;¯£×Œê:zu;¯£×ë,‡í¨m«æ€­Õ—åÜO9Õ ì#–Ãöú¨9IŠ-˜åSÎ_Šœ¾0°œÊùEͤØ.ßG9­(gš3T±a9¡”ó‘š“ªØ¾g9m('SÍɾa9á”3XäÈÌÃËa™(SR3§SæPÊ´ ‹Ñ8÷ðŸ.œkä×òs¿^=§ñëùs¿ž8§ñëžs¿nÏ@ë<ñ|¿b[.žÒŸŠç`Åöžx:¿%ž[)¶ñ⨹LdǾép³SSœ×Ž©Œ>ï*F%vu8g$º>;A¾B$êÕäËq bÀ÷ø\ Ï@¢(|>Ï@¢vðï±·IØî[š„mUL¿¿ŒÁ¿˜àŸèÏà”±Q¬y|Púƒœbkrdb­Þ‘1àÓåáSdWñ¹ì£¼´<£ÉËCg4y©{F“—jð-çk"3bk»Çí äçQÂwF•ÿGò£á‘á©â?èAwü½ÄƒÙlñµ_©þü.2ÛÉ&ß”ÌD²s6sΗåQh þ³°Žï%Ö‘}ÀYŸüNûª fmî$ŒV1Ut­ØS~úkÅBýМÿ›z3-~®Ã*4)_>¦ØjEg‚ù9'3«„åYÀEVÂ@×ñõtsš¶;. å|ù"=¼¯ë'r[ÎŒ¼‰{[ÐÙ˜ýÅao{q5î6f×é¡sí|äL™’àh›ö8?‰ˆr¯.=¶¿ ªkpÜoÈ®Ù:ãä#ÎÖ`Û.*€ôÕcsã®2%â˜eæh“)«ÔÇ2c†(7R ØØSÙ½ÎS¿å64Ö*OÜ%HM½`¡ T(X+Ôg—`­ÐûT¨µVèI*ÔÖPhÂGGBpª³u‘BQÅTŠ*R@ ÖkÇPßñÄoǘ:§ÄNL‹ Sr'GÆ„íŒ ËÇ]h‡üŒ^6âlƒÇ@ã¡t(þ Á?ä°Ë… °uGcñËAvKmÜãppU±ãˆmˆ#¬üŠáÈ}t:ɵòÅîèãƒüÙ¼Ha1ªÁgH1LP}mð¾z4œ²¶^Vl/Òê6ÿê—±µÀ'ævÆñú&Êrê¶)"âë~£©6?9úžG©Ã²ÏyÎ;[ˆ‹[U¬ˆ«&:c-ó´Úµ½éÔÞÖ2}{3¶R{—Vb{Õy{·NzoOÓG¶3”ðáØžÃ?žöÿ\–²ÂM–œ¿ !gkŠ”W•²ŠM–ìÃÔôdó”gM¸{Ñaó{ ãuŸs`2]AÐÒ øÑ¿€Át@³“jt5Æ”1ûUgƒƒ¢vÙL<ºï/¶>-ÁáŸ8œ´“©Whålæ…(ªšRùi±0M¸Ç`Ç—ÑËtéÞFš?ÌyžbBƒÚûK)’b&ÑÑ0Å®Äb†)v9-ÈKÏ—‹Aî-á!û=9JzŠ]ÙDG\¶ÌèIÜ2„&·c˜l…˜ìì}% ³Qø1<‹Ï»{;ÆùøL_ÄåÉ›\-lGöS(ÅúÝ$\æ¡xéM1µ]O“4+OÆ~À‡mé{±äsãW‚…fùú÷ ]óçs žÅ7´±í¼_7¶;oPgà ž‰7ÄØ¯mç_I§ézÒ_€ÎçcT¹P[ÇÇ ÏÛs“ó…:ó;ß<ç fq>E ÈCk³áëXy ´ÉTѸ%:×ÇÙ’8&ý»J<уû÷3@þ„m5ØK˜FRCâ2”'ç•‘ÑÐržY¢7Øç‘¶c½G*]ÚS’›Ž>Ÿ˜ —xvþâñ8 /gЧ¢áu®4a`7ïL Œy½„G™*a×Q—â´ÖÆaÄu1¶kÒ1}ðš Tk‚éµ70Å`´É uƒí?àç[˜Ù™ºc çaíRl'ÝdEËߊÇ/`¿ßº–Rë¿ï¹¾ 7È£óòárTƒ/n›Ÿ$rÝ|Qú ‘kÿbýÈgÍS“ñýã{:Š—Ÿ¹­Œšpõ÷8}ØyÉ‚$F¥ðB>-(8iÉþ–6e8‚õûU20¤œ›é­ð|±ZriÌûögùüt4É:“vàC⢢XÔ¯ä^šÁyx™éŸN×P9è\øa™¸²´°Ã ƒ’à°µ™.€`’„îËГ qn<ÿw‘fèïsÖÚ§bÌ)/âE'p…J‰­sX°‹&†b:±°d7M¤Uæo çµdFQDc» ¹i>Ê>çg{Å]B_²²ÏQÙ•¬ì¤E.ç2}³Uiü"q2r´Ïy_!oÉVÁ5ˆàz Zµ;A¸‡O+4%é¢SºïÝ¡øEÏbŸ¹ù7lÛõ¾Ô¤ƒŽ¼³0ࡋع*õÎP9 {[ ßÖÿ€Ìi–Ñ2ºÖäàþ½…ÀUlömÔÚK™Å 9dþEYX …#°¦ùޤ®¡Dº%FÞžµ‹i°ÈÂöð×vþP•ª±Ö‡’Õ¨@”„¬oQ )Ò=ÎÛÌù`œ¢ØÖn×õ²y;ë%Qëå„h|ªxX¸ÝÐË¢í¼2ýщluù*O§õä&²Yt~ gS´EF_Ç}Îátïw™ùb Õõ.ÿ¥h¨,IÎ04Òì7ûÙÀé† žç7ù6ZÇMq=Ðã¤qîÛMh=Aü²×ù’‰Šçº~OÜÿ–´³Hý€©†Â©\ýO‰ërO~þyÀç¡¥ŠsÖûîö^ ^DâI8ÒnKÙÂ\ºŠµ µÐ"ï‹‹aÁ>ˆ¶¼•Ù¶û/áø6ÞFX‚Wg³þ¦Ë]¹‘áU`n:YÔD’Oãòœ†úÓqòùä’f•$m#FÀ{Ú(Ïü˪Ռ §2#± @&á"—$Ðx9±¢¿ù 0A`'mAŠ%G:_¼X®ð—Ûñ%_’G@|»;j bw3-Øz9wyâòŒ†ÌÀ$¬}ü¢fÈLÚ¡3dL™¸Ðý…uTêr‚ìß°€ÖóØ­ƒŠí£ƒ”îò5à±äf«6ô}›‘…*þsuÖeôA 4¤[À~;Ì™túE.H¯4ÄLÇ@§æq‡›Ÿà™ÿ{HQlÿ;Äãߟ>ÄV4™ÜÄÎáÿ§QhþŒCuÓÏlg õËOôóaüvP´ä ZòÀÕø~‹ÑJbßï‡X2Ó‚“ìò1¹Än±¶†Ff7%%~8•ø_?•(T$r®N¡s4ÃQñ˳Ù]è½pmÞl~:^öîè…¬ßK„çȾúº  ýè?ð{ë¿ë?™úEëŸß[Ï×GÏ îùºÉ¼ÿv{d´\À,Íe‡=ÿ\1þÁ ÿ^„ÿ6=þ™ ó¿Åÿ+ø³5Al]‡XÏý©çW¡g±à*ךVg3vÇ^lñ§ã€b\úå ŸmP«þÔj=>s|îû—ø¼´Ê>È¥Çuðq¿Âíû^Ç-ü›žbkÏÅa¥ò…ù‘rÂNCÊO±Ù Vàa‰‹fψ—Ó÷¯ LŸ’£È×-È$–¼^ÁMÂ{·H†ÇGÓëâã#ðØ2Ýüô—o^zûx³ƒ+Pæ(iñèÍQ¬PVW Ab2K°5¯Ab{Ti{Tš¢@ÅG…[ò@ÿOŠÔdÙ¹OæÓP;§:ñp5綪ŠíÔ~+:YS¹‰Ðy\ –¬fâ“Ãù=a¸­02ͽƒMøaþpMâ™>W±§[Êþ3C,àÜqÔi/üÅœvØ6ݲ•ëÀ å¬/¦ÐWÿr™BÅyuŸP;õÿÒ&€[…oéñ{ðmKž½Iv¾%û´=e\¤)½&¼ Â7%Ø[Ö8É”> Ô`8ëU)ìð|¶yY9Pð  Tó|êþ•¿ˆI,ÙÇ(c¿h„¢-*¾á…}äa¯¬É¾Ôd7 ïutØ1s±èÈ^5`¯"Ž¥¿®•{ò9z™Uì—ÞD*Œ eO‘›Ö—rÑPÉESØ ØG’DÐ(T],Þœ ‹ wvegû6égXz¥»…;‡´fÏœùÓ«=s8ìk®»=ÃùCù[%´¶éšþœÃO–+Âö|f¯«“Gç‡  M·Ì† #hZ-§Sø,¸ÝÞÄl¸ôZÎ5âuqØ…ðdŸ’iJ¡Ûá<]èõbEþÎ$*ŠGCra9ö=›FùyÅAt*²< T*Œ¢á»a j8ÑzÇ926×0¹â‹1%91t»=ÈçrVÍ`¨mAQ€1 ! –ò³aBåãy£Ú‡7ÜïôáùÉ8Þ §y°Ù|(律ÊUb|0§ÓÞŽ„àÔDy«óÜas2-Lž$2yô€ú©h¹äÿ!B“‘¸åòö.°°ùª¼Gpësê=‚4{8»OãêgßèîŸàë ž´bßÿ!ìuhªÏÎxfd’ÓyÏÜOb<Ó…ñ ˜(¹ÕÜCTêšX<¯•éDÀÔ™SBþpçç°¯Ëg>0…*BãlÚI!’ÄÇ×¾QÃSoì&=ì+3ðvH¹5ÜÃ7.õ»Ö¦ñ=««ÿq?ª&¸ÖælÏ•rýDSlü,y8mDf+èšÄ‘£2eB )ý!ÐEã.úã,›°X³ç²!-Œ"'=Ò’Ç6¬œ[WbˆšM‚_L²Æ™,æãÄe„ʸ•ä?Za¾zž7DÙ™ˆ3ì“8  v˜:Ïr 7*ì_¦±å‡ÙP8œæ—Tõûfˆl!Üñ¿ë狎¿»¨(./«š¢~2©×šiðõá+OÞÆQœ³™O(2ê·g m†ì¤6ºñú¯Òö'PûmÕö{Ät²±ýóß¹¶ïiüL 8~WϸßÞ¾Œß{¿ññkÁbÄj`êH9÷iÊN{¿W¿PÅÛýÕÂ>ÑNÚÏåŽG@ãÐL´”‰ÛºÞÚ=ÆèQ‡ €ÆÎ?Oq`Gbê …ƒ÷‚+x¾ÿóîúÒý<Ï’ý3„¿é‚ .=M§L~ÔdÉ>9YŠŸ%«%Ù{A¥ç}mÜŠM&Kµ*ñÞ_ þ–;¤ˆÝ–ï`_~ÇÆ^xÍ€§¡Õ]0ƒžçj¯Ø@±±Í@7'm0±S¤ˆciþÒ­“">&µ,•"nµ a­O)ù•f¤óXΫDÈ C¡-çÂ_ù 6ÇÞyV pCL}£ÒSo‰TÙ)HÍ©ÿaêm‘úSEê ¦ºŠÔO˜²ŠÔúS*™„½öó/ÈîCNiöÚâo<Ùkœžéàn8ÝñvmÀaÌôžNsЇ|ýùÞÇ‘adžÍnX*Ñ~‚ò2 Wê”  3ç²íLâdÛ‰•ÏñÄ1–~>™¥#ŽáÆ££ìåÑžÚ•»4Uö#åøÆ­ø§S&¦žÌþ¦jŠ’)¿–(~9ˆÿÂR¯øoÆ|ÓåAì§{yðR' Köü&æìô_àŒàéÑ¥ëd¶·Ì(°Ì8H pñ9Äħm,ù׿җgû”É! ?…´* €1dÜø$_¶ÃL'ïeߨR/—Ì~êà9ÖÚqnì@_ˆŠ§|O[¾M–Ù[¦sç|ºêœCbŠø´H¡Üjô]¾P¿ÔY0™[#ž„aŒ9ʇø~Lµ©Z˜vˆ§L˜qœ§®ž€T€þs'JÝøây©Î ÷ñOîÙG “ú´d.æÂ‰^ú‹^fà‡˜ƒáÝ"Aø ,HöBù³doœdRƒŽrò¡ä‡ª®PÒ’ ^ Z»£®`³Å–¼ø ìé5Uë?²è"7š§LÏo§pž½Šfê„pÓX+&¯]ÁÞw;wÓoT(oÍΓæAÜhs}9[®xø'äÇgõdö)ùÃávù¶]½y+ç@Z?༌—brvfŒ`,gØ2qWZ:Y)“$#<ø «pD´¼¸ë2šnàB†òy8œ³Â†‰œGù:̇¾6¦Z‰”¦^8ÀS7ŽAj`“¿0å+æÈãÇJ\Γá![½@…ˆvt›b/1†:Õ-¶ ¹iM ”;1xhîóÁ#ÅZ0ßeûm3——ЧŠP Æïè® ŒúÉJF-Ù­†Q3ùKð„¢—£È7»¬ŸŒ’l‡Þ|X»lc›sk)_ä¶ÛÙ ŒQ>Rì: ¢èI?+F6{Jk?…ñÓïŽò°h7¶þ³£†ÏÏZŸ8c×XL³&˜Mc«Zò¢¬EEŠmÕÑ×û1Üõ×7BmäúëGƒþúQ¯¿ò™þšÓ õ×G…þjúk^MŽàʋ鬓½t:ëŽ(ŸÓzÐY¤¦¦“šâ|ÊtUü5w]Õ`L˜Š)Y¤^ÂÔ‘ê…©…æêŒ©ßos ?¢1¨»>Z>ŽwVûˆ¦ÒEKw£>jïÞí9 }4*ñM="ô‘‰é£jwxÉõÑo½ê#MõûÇ ˆ†_bЍÝF/'šœ}éÉU™œ‘—Üî| 7þ˜%øcç üñ±ž?>aü‘‰ü1í°ž?fR¸ÒŠ-o.´ç¬Ò}Ÿ «ä÷ V!>ï…gìp¾ÊU“z¶³q'Ç›‡€†ó÷q:¤aj¦H ÁÔ‹"ÕS‚º`êÏ[œ-dýs“Ù'Î÷ÞçìâÍ{ƒàC˸lïuAÏ8ÄÜI3~Úø‹ÖQî”NÅJÊŽ¬b0—ãH¤ìÀs<—¸ÈXx!3ñépÚlu[©®Õ¥û¤@åN­Vpa3Q«6MÐEï~ÇES«›LæÑà'†+¶F_Ї«F"´Öwâ*¶:_`ñDSžŽ!ç÷™LE)o­Á—¢ÿd&õi,BµïwÂ’ñ[•³æåNÂ=¼]ýÈpVõãËœ¶†]8l¤»"ˆ¢xiö0K–Fq:mýŒ¢CûyÔ”ó¬ú}*•“ÈÁLšùyD­@<_OÕ˜N‡&ðAbB¡o»•¢€Û‚k.eñÆk4ׇš,9hP£XÙv|Dþý"³þ?ã ŒLgÐüæx_]>ë÷‹%EGɪOhdmÀy“Íô¹©PrÀ˜QÇŸ}Ž¥ ®@ø$påü ²(´=ÆÇY)lI'$³hèëÑ 5¥U…">x¼ƽ“Û,¶Ú šI†ÉëMH]µXcB®ZjG…L™d2¥Õf»ôá…"2•QM$¹~õ¢²ÕYÙšKô Y\>H‡Jòf¦«÷:;uÒâ’Û¯Õà4o@Lè?ËÃWeAy²iÚ©+Åb¾@ÅXœŸñäxs¸èsÖ&©Àf,D#Åv)¹Ï(ÏüÞ´ãêê ÑC]ÖÜý|ªo…d‚ž(ï1EÚ9‰_²¦ûÇ××cÔ)@¢ïÓŦžÕ|t×ý´%l½Ý¹Î(Aïìu翜§zw®÷+iú™_–‰×Ãö\AMùŸì}‡Éd_ܯ\éxí=ìꑽ"˜×½ ¢ß€º»ô-rå@s4}Å›XLVWe«êC&lѹš ss˜™œ,*Ô£zì~ZŸÒ7ÉÊæÐ!¯sùËd'Aƒ™ŸÓ8×rž篂|ä~¨ fÔ¤@:ØGp~F~Ø õé ¤å¨ êTŽ ÁŽécäàÛ(‹A2:CÍY³ä?«Ø'-0«Ø7­.èdè{¸è»5ô¦äNª‚Ÿ Hò€ ö‘þxg\Þ¿‡+=º^¦ñf øIðóá&›íÁ?8–-Ãñ`.ê ÞG­ØŠiÐcÌEÍQ]¸Š€ôúÓ  í(º8<’²/7¦ ˜OuòÈøÔ0y(¶Ÿb·Ra,«#+¶ùðFN®‚²2”QÑsÐ0ê÷T§ ÈÅci®ä‚@”.$y$ÒYirñ¿±[N¶âäÐ7}‘Éq?Ts.ÅŸšÎìƒÀ\¡N4…®'AÁ7Z • }n¢¹0d—¿úhª^ÝF …BÀNš¯"X8Ò‘O›)ƒ¦¨µ`oUÛZ æ?ܽ9"©b«¶˜øî>Jø.ÖGxÅ÷Íã*“®],TQ´PEG©~zM©°÷?.6è”cët:å3Öç9®¶ŒûcCéî)ß¼ÒXSv\›\žÁdäOïÛ°fZäŽA§Ó²É8- !>’ƒ¹ÿ þ]°çbÚRãûd¾%ï¾ÙÈ xöÞ×|c¾þü"¼%¯ÀîWõ¡Ã6¯M¿z»uúðt¢W}˜Û ³ÛŠ-l)}IÉî…Êjì.-Þõè"Ý“ù»PÐÖL`Ô†ÞFžfà)¶å‹§éS +Zà²MÅÆ<êS‚M ¼«;ö|g§¶8táçbq(ôüÈ.üö X©™:ÕI A:2(³u§P°ì“Óç3VóU}{Û†Õl‰–ÃÖ™j–ûX‹»;jcä¾ÜJ¿~ÚNFòŠŽ†15œ÷u…oî¸Ï`WMwjkvv.¡‘nÔ3jîÔF:n¤Ã ÃŽµ×yùJTZŽntx{úÒ^…B¬YïݾæzïÒ×ïϺý¸¸Ž‹À™;´ÁcPe÷ úïÐÑŽ ó!#1çfZ.χ± %A P2ósШ…×ÈÄ·DÿÂÜé"üâù€$w6ë7ýø÷™ÈVDò Wè+òï_VýûÂ+®þ½%ݦ&€÷pño†.þ±B±tüûLСYM"§§Zu®>5=‘Cà¿.tŸÇÿøªß=`Î=ð“øþCÝÙNþ›ð‰£c>q îd  Êyô7Ý {”©'õ+R"ê·Ö´¾v;‹]À¦WsÖ«(„‡ˆŽ$>m825´ƒ¾©ßnÚmã¤ùá'W†sõ?]œÏ†ñáÏþ‰ˆ:çiVËvó9ü'á|îk¯w>?SíéÎçû¨0L`×,ýåÂIë‹-yìcÛ0R´…V©^ç鎚×Ùxó:#À»ð¶êu.™ëÙëüd®›×ùb5ÌžÀ+Ë©áÛ ^fAUÕË\æË<ŽA¾:/óZ¸ÞËŒ©Ä˜ӖSàù9ž¼L£|ÝKó“+ñ7_î",ûi…¦D7³ÚZÚÿV üÍæ:³îrÏþf؇ÿÑßô]áÍß *Ðû›èyö7w,¿{ó“åwëoÓüÍÞüÍ/0&ïÙßÔü·ßâ ¾¼Ug¯,h_©ÿ¶¤òæž-®þÛ'Ëþ•ÿ6YTž@Mù'K³ ÿ­u6v•²E¨Ìwý4ÿí1ò/Zÿä¿-}™«€-zÿmï‡Ük&ò/åWà¿mXö_ý··–y÷ß–§ð¾_£¾}L.î[—öè¾ázw7÷mÉ‘O¾pߊÆÏ|ÌÝ·gtî[ûeÜ}kAÃS©7=ÆÇàÀ½£*ࣳ\¸ßf¹:p¿ÎBÛ&w8CŒÜÎ<{píß÷äÁ…½ïɃ³]ƒ6þ¥çóE¥\M¯Ü™F®^(WÀ¥3Uîò%áÁýðC%\Ìl9Û0ƒÿ¶i¶›ÿÖ`ŽgÿM™mp—êü·?f{ôß4ÿgñEò~Ðé“mïÂÿù; h³ Yq¬Xî‚ßSß×û?›ÉÿyGó¨þÏ;ªÿóŽÑÿÙìÍÿñä/LhÚæÆ÷šÑ÷MÑóVb׿~¯AÖW™ßfÕ_È¿ìî/zß“¿Ðù3.h›¿¯Ø_ø3¡ø½æ/ø1¨ªTuPm}Oç/¼ú½áò{nþBÀûš¿°¿È›¿0s1·Ö÷Øët‘T¾s÷tç9$íÁÒó¾#^þLø<ô ¨àæC¡À¬ï´oþ!ùBÅÑdÿ|§áöà{ºÇø>Ûeøð.(´Â ç&œ[Äaìo„‘Ç,ylqÏ8~€Œë{x>Ö»,à0} ô¸T¶›8¬ø;‚‚gßu 8°1þò]Wicïß}× mËiT»2iK×KÀA]¿xMs96°½dNï:õ¶)ÒÇ4©³ßÎMø ØhÖÑ.öïÑ弉SîÓ Ջ츂ä„FýÜ6›Hh´õýl½w¿@)+”þ€”Ò…e5j¥F÷‘º³±Dñ°œQøïV­ûøÒbqÔUç|©’üp}¾n½•±GËñ­brvNnF>dÏ@ª3 \FµâÊÎÊ^ÎÕ§’’øªZgÜ®šƒ6º¯_víï1+Tk«/ ‹ÕªeTˆ_T°4W¢#è°íK…¼ímJ§ÅÄ×eäm=‰õº>¥ŠóòQïëçÄr¤× c5@Ɖ1xT²×q(ŒB ~ðTu§·(±Aï½µP›‡Æ£«ë!ÕQ¬I9·Þ¸þÅ„ë~p__¤í'˜žˆ[ feH«+e³šÒj:ñà'òQÔ!J„ù { ˜À¹±¹dX71vàþY²’=+ˆs×Ç*Ú™_h§- ¬¥²éw »ÑÛ’ý'}ígãÛÂÇÂòs£ZG–üE°ðÚGO¡É’ý½Ik¢jßàc ÊÞZ0˜‚cI 0êSÒaûý!T“u×3ß·‹±×¢§Ý+¬¦ góX…ZÆ ØWu׊óª]Pótë³ùxÈöööàôêPÍ’k.zŠFHåÁÇî5Ö—MðÔÊo2ÇùçR90×™CäÃ-ǵ"jtTñÎíÚza/á)Ür÷`¹_HË´½¾‰=âød?»|ÙŽÁ-Ñù¸ÛE±õ€‰Âg`0ù:ánþ²q=A º<%l©«%{¿‰¯'8bbë ~5é×üÊ>Ï16;E R—È,¿×íŽÁ5KdkDƒ É—# ÒÆääOöoy,Úï2 ilHnlpûØVtR‡óL¹¢D šØPW³\»WÔK5‚I:ZîD£×'ÐXZ4tû 4é·Ý° až Ý•u¸þP,íøSå¥ t·Ý<æK^´µè7gÂN•G Uçd±†ÍFžbŸµí½^œãÃqÏZq‚|D\PËøVv¹6Ì<’ ø´³”…›.A„Ë®5:\¾…Þsƒ”n²]n@§§ÓÞgL+{‹§ì>íh©ž— .tö}tnBpF4.‹³Ë·che›4-Ù)>\‡ÒÃNËôW}„(Ú#¶XfŒôÑÉâ«>‚ ó²&´â«‘ŠH|`>¯DûݲLyÞöm÷+´LéŽmÆÉûýö[¦DÓùB“àVÑ ~'­>ÜTÎˆŽˆšØº¢Û­jf¹b¼Ñ¹j”£Ê}Ñ—3»Ø§–£1˜iÇ¢Ûn( ÛÔ)ö(¬ž ´‰‘÷TÔmÑ'åFË{cxHŸ{áe\Q>YhƒÃ_7]‹Ž 1uV¤6bª(¹SÛà\8SB7Ìü¹°ýjŠõü%P.×õÛâ{§(.Ý"vÐÑu6‹ë:Þ¦õe:F}#µÉ×:F­ù5 ]+9*HÙ±câ¿°µ¨XJ|+‡ÝgÛˆ.æ1-"§§U·ªë½·oø…ø £jæWˆÿþ¯ÜÖ“â&Xº ˆ¾¢r$÷©zˆì½D¬nd¸<ã¦ì¹ñÛJ{… Æàšé#Ú;ÔmE©rlHËØ`èö¬rŸt°p[^Æéá·ëtƳ§ZàûÉ}‚[ôÛ—Û'8ãa¹[HÄþI[î’Rö ûÀЉYLg’¼]·guå÷|d²VÁÈì¹ÎG&}•XïébG û8N³M öTZ<Åcnòï1‚Pùz“÷)ÁØkˆ€A'ÜÙEÀpS#ËÄü·R¿þþÆ-È[¸ÎÃúzIœoûúáM9«i&Šºþ™öO‹8¬¼Ëyðãr%Ú²†= u!²ÅÓ+iêÚ)`ýr6¤Ÿ²„lWlß¼ÅFÿ-KMr¶æÄM›·­?B‰7Yõ±f*–¦ÕŸj¨¯««¯ß‰×·R±6Z}»¡~ ]ýjúú¾¼~ »ž£Ö÷1ÔߥÕ/|ŸüÏ{¿„íÒ–uëæ{Ãý/¦ó¿W0«x Åړ´X£ñÚ€$‹ÕB[¬±V|:ïŽx*½ÍU<ø3d^,o–LéPJ'¯Ÿ^Òá”îk8ÜXw&€Ëþôr´3§kç¨ûÓAáøå¨ûÓq]JöÝìOÏ9Ã\5ŠÎXÎ U/Ic)Œ[¿= ’Þ]Û$[ÀzƸF¼µÌÎ×ÉLü*÷3g•ú¤Œ,w?0øÄúè[ä} Ów_3[ få×ö°_G¶S§P½7V¯›Uì‹÷×)QÒªÑ=â%Ú>q§¯8ŒA£ZMB¨>Gh‚Åoæ—=T#ĆXÆ3±L·‡ãø°;¢ ½~#ª_B#{¿CÁ¹½ŒTòˆßBsNR $•YAe³27¿¥2& ée¢Äi¸±¾¯ûù¦W-­|üìæ}BHà§¾+"_ΪsË9Þœ³3<è:ŒÊ£ÀàЕ_Öm©sSðèÅöëúîs‡·êå÷gºów£Wé»÷²ÿ¿àïŽojüÝúM•¿£ySåoàvÛ›^ø[RÏ{xs®»6ÕÅÇ®t>tÝK°m™Š=ÀëÎfÃû/§280ÇjÈ™9•Á„9†œt] CN]CŽ}*ÇI¿~¨§z³Ÿ*²ýùz":³«ÍŒn+¼×¤'d7+æ Já›TvÑ 쬋“ª/ŒÝÎCáúB1xªÎ¡ÎšÂ>Øâ)x…ŽY-oæEQ‘´ñ˜mÕ²­o–Ý_»€‡²yöÃ,»fiÙbÔüXö˜¢e‹}+ç2A.túIãµ3ÿ%,Êô̹™Þøct¦7þHÎôÆ™Þø£‰®N¨!§š®N¸!çÒd­N¤!çàd­ŽdÈÙ¨«“dÈY «Óד£«3È3BWg¨!§»®Nª!§®N†!çÉ.2Bò⣓Kö ”‘†¢ŒX—”°c5¶`<ï.îÿ7]Àoúâ›Tx3ÔÒ‚m¼—–3hs‰V6Lä‹Ä,;8Ÿ½Å÷ûØûH|†½OÂ÷—ÙûAø/{„÷©øÞJ§ª“|²“„(>ÁE16¬Çô*ŸLdòyËÇ«|æó"‡x2:ŸYËxÓñ¼È;¼ˆYíËŒàE<Èk^DâEBµ"¡¼Hm^¤)/® çEþ™ÀŠøò"‘Z‘H^d7/ršë-I+"ñ"Ëx‘ïy‘$­H’àû Fí×W+Ò—yi‚¦ýiÙƒxvÔMû Õ²‡òìà šöKÕ²Sy¶ÿMûehÙ<ûüx£ösÛ_ŸÌåÔ`²ÿ²ÉĨ™ì¼SÓÔ˜A½q¦^7Íëù›”ÿ±÷|¦_Êõ¨ÝîkO %,Tí“X&¿€g7dCĦÛpXQ2ñ2õ€õ/# qºC2ã½'µ}Hºü8«Ó¬‹8«ã6Îóù¤†ûs>§ÝŸóö8¯÷¡aùMtº³#`]#ŒU¤*Öt„ýMP…ÕïSZŸ§ØÌӸ˄÷-Ó5ڶȿٺ&ÅÖìS4~½E«ÐzÍj…ÅV•UhÎ*„‚_2Ÿ{`3Æ—¸íµÓëÑk ÑÿSFÿzø-·½_zMi‹3¨}TÍýЭOgçÿ–ÄÉ;Á>Kß_%˦?|^÷—”ƒE5€»«JrIéYŽËùÝb ¶é}ÍF <øÉÀ6s¢;Žp@/&#ï‡0ÕÁ!}x¾p4Ù‚ýÖ t^Å|ÚÄhö€íÏ?g•˜ÇÄK)€¨“QF®røØþðð>'ùÏÁ‡àa6>Ôƒ‡Šž<ôÀß0×óRô Ä ‡í˺´övý˜@\9vRJ9(EÓ.NîuÄŠ‡%È‚â,›ÎÀ¨Å)ûŠjÅÉ‘UåHàv+¤•‚ˆké»õ_ÔŸû¬«îÖÙÈjÃ8íriÀ iÎÛ¼| Ðç,Ñç@Q-Iîôé!m¶ô!޵/ïô ¯æÆׯ¸òôU5AŽâ)ºÿÓ;Héj3wUG8?d¾¥ØºO?\¶à×ùØÀ¸Paí}0îðZŽRÛÔÑß>Ò•þÏ–I‘þKñéßWˆþ×ñéßXÐÿ%Ïô×}pØ.=Œn¸2 SuqÂrÑÃüà€ç1ÿôà{lÁ<ò>ùñA‘A|ÁWÿPikžKr¤•ªÃJagloÚò¯¸ Ä®éYo§éÁ•“Ñò„À9•±r\Ä‘11Àʇ9+÷@V«A‡‰6«'÷¨—kle§ô(W ¢#JÓéþ1vžn“Hò}0hånœÕߥU¯aÇ;±…Ø‚KEVþ„èûb,jKrv™ˆ`ÔñØŸî{c¢|;Žà°Ñ¥vÿK冀äÙNS¯mûˆž«EË5ð´o˦ójŒ¼9Z9‚2Ó»*}ò„n{—³3¬õ÷¦»éuëô&_6íª‰Ä»ø!Ÿˆ$vRá!JÄ<Ó ‘›af+janYÿ!ž=Ž‘ÛªLÃÿ|ÈWÆë 7 “/Ÿa®òµ ŒËW3!_„|e ùú®œËדÞõ«ûø®Ú†ã;s´ßtÁÍ­ïˆÑ÷d|õúc¨+~o4æóÇØÆ|þHoÌç´Æ|þˆúcxcºÔ÷îðû¥ñûz”ÀoIô‚Q~ò¨ÿÇüóvu䟕sŒü3›ÒþOì#ÿ¼1GåŸmÙXà«9ÿ†^qÓÏøø>ÓˆoÇF||;4âãëLºJÓ’¡¨^”¦óFÿ»ß’Éì÷,OWÏd¿/ñßFü÷ê$ö»œ—ï2›ý¾7[Ÿ¡néÔräÛì÷}þ;ÁTF¿ë|Øo¼/ûýŒ§‹øo/ó´ŸûmÌßûUa¿¥üý5þ[Äÿà¿'ùïAþ»›ÿðßïøï:þ»‚ÿ~Æ?æ¿ïó_™ÿfñß üw ÿÁSøoþö‡ÿ>ÂOòߨ öÁýøûïDù`Ž?ÿ {˜—ç¿Qål|Kksüëqxm_þ[ÊÃêsxøoTCŽÿ]Ç‹øo/Ñ€ãÈ~k4¬¸¾Ìëõô5Ò;ŠÿFøéÝ’ÿ>âk¤w]þ[CПÿ–rþ¸æÂ?ðß“ü÷ ÿÝ-ò;ó|þ{ÿîæ¿ü÷;þ»Žÿ®à¿ŸñßùïûüWæ¿YüwÿÃGðßþ[ãsŽÿ-]Âñá¿Eü÷þ{’ÿd¿ÿÿÿì}|W™ïÇvRDL‘Э©·[S P“V­*hÝd¡".ÔÔDB©qx /¦Ù-"jD¼,¦e¤ÑËH#iܶ&­HÊÅÄ…ª1/¢Y¨_À­.‹šðb’–ÜsæüÎÌ™5ér·ŸÏýÜÛ?øÊò8ÒÌóœçýK?yúþûïôxœ5P÷‚=÷0vƒcx'¸Ün‡Àp¸ì×køçÝ¿ÇõÇðýÀ¼Ja<ñ;ƸçqÝ~>p\¿‰ÿ~ùŸÙýãï&ÀÝà¸ÜÁ¿w‚;ÀmàVp7À>p¸ì»ÁU`'ØžZÆxl€ àQpœÀiPp/8îÇÀàp¸ÀMà°\®{ÀnpØ v€§:pÿ`\‚óàx<Nƒ¨€{Á p78îw€ÛÀ­à8n7€}à:p Øvƒ«ÀN°<ÕŽûàxœçÀCàpÔ@Ü N€»Á1p'¸Ün‡Àp¸ì×kÀ°\v‚à©6Ü?ØÀ£à<8€Ó *à^pÜ Ž;Áà6p+8€›À `¸\ö€Ýà*°ìOI¸°.€GÁyp<§A TÀ½à¸w‚;ÀmàVp7À>p¸ì»ÁU`'Øž: ÿ6Àð(8·Àà4¨ ¸œwƒcàNp¸ Ü à&pØ®×€=`7¸ ì;ÀSÆýƒ p< ΃sà!ð8 j î'ÀÝà¸Ün·‚CภÜöëÀ5`Ø ®;ÁðÔ“¸°.€GÁyp<§A TÀ½à¸w‚;ÀmàVp7À>p¸ì»ÁU`'Øžz÷6Àð(8·Àà4¨ ¸œwƒcàNp¸ Ü à&pØ®×€=`7¸ ì;ÀS§pÿ`\‚óàx<Nƒ¨€{Á ~ý»`/A TÀ¹÷þ‚Ài°û“øý£ø÷ñþ@öì(áßÓq=Ø(Â>~Ÿ÷p/8îÇÀàp¸ÀMà°\®{ÀnpØ v€§ÊðW`\‚óàx<Nƒ¨€{Á °ûÂÓìûƒ8öý-ãVp<]Ä86ÀUÆ v¾q¸<®YÍ8À¹y<ÏáþÀó;«xþà6p<Åÿ žxl|òÄç Óà¶ö=&ÀS`Ï‹wƒÓ`÷Åx¾ ΃}/ÆóçÀàÐKð|ÁMø~‡.Áó{^߃=¸®?¯;ñþÂKñ3þ~á ÆmWB^àØÑy‚ÓàÐëw€›À­`8®7€Ýà:°ìOm€>áç~îÀÏGñó p\Øàý÷Û®ÁïÁîkñ}À9þ3~<á¸þP?ô?+!ÜžãVp7¸Ïq'¸ן×áº!p®ÛnÀus`ÇöÏãòëÆõ«pÝ^pÞñyÓ¸¾Áÿþr<Ÿý~øçáºà4®ï Ùï‡Þ€ãþÇp}gÈ~?üóz÷?€ë^n¿þy'Ï»×i—Ûï‡Þ!ÇýŸ¸ ò¸Ü~?üó&÷×oºÜ~?üó¶:î×w_n¿þyë÷¿×7.³ßÿ¼Çý¯Ãu.³ßÿ¼yÇýwà÷»/³ßÿ<Åqÿóë!Ëì÷Ã?o‡ãþ\¿æ2ûýðÏÛà¸ÿ¸þÔzûýðÏ[å¸ÿ ¸nn½ý~øç-8î®Û»Þ~?üó¦÷¿ð*Èc½ý~øç9î×÷­·ßÿ¼ÇýáúÎõöûáŸ×ã¸ÿ\ôUöûáŸwÂñ¼W]…ûÇõŠãï;pýÇß7Öáûã: œwãºCüïqýü~§ãó†pݸׯÂï7½Êþy}oÃý¯·ÿýÖOâgpáføßâûÁ®÷9üÍnî_üÀŽcx^àØß+àQûõ§/Þø–“§½xÉß3~³—¼“ñ ~n»ÁÎÕ[¼Ø?öߣ·¾Ïølãõ÷Z¯¯z¿õzÝ]6_ÿZx§ðúÞ˜õzaR¸þƒÖûÛ„×—n·^OüÎzýSáý!áõvï ןÿ!áû ×|\x=ñ/Ö÷¹ç‹ÖkMøÛ«„×'„¿ýé—¬ëw ÿÎ>,\/üíÂëNá³î°^|Þºæáõðútº~^x}ðºï„u½¾ÓzÃG­×7 ¯;ßk½¾Hx=6gý;¿¾_׬××ÿÐz­ïÿQx½æïë;„÷sO÷+¼ÿ°ðº§*|áõáõLJ­ï¿êˆ ¯yëu·ðþ×Þ)<ŸŸ ×ÿD—ðþUÂëñzáõΨõo~¼&|ÿŸY¯/gïßo¬ïµX¯›±>Á“xÿ1ð!ð0¸,€ww‚·#àMàFðJððpxÒñ}ö€ÛÁ-àJð¶›í¼<ù¶ÿWâï7^ûôxS n»7·\kçÉëí<S›ð<À‡À `S¸î®!<<Ÿps?ãÕàZp5¸lã{€Á}` Üîâ÷í¸¿ÍàÕàZp5¸l_ƒÏ€Á}` Üî·ƒ7]cÿ^»÷Óî¸ïÇ®³ó8'~ <÷)p¸ Üné÷–Ç÷¿¿÷±k£#àMàFp-x¸ýf;W_cç ðäëñ÷ŽßsùÜŽ8ä³ñû÷çß{ŵvþ¥zñžÃ1°ààþœÀíàp3¸ëìäMÐð!ð0¸,€ww‚·#àð ¸_‡Ïo€Á}` ÜÃïxn|¶ã÷Çñ9ÇÀKðþCöû] ®W‚nÀ;ÁÛÀð&p#x%x x¸‚ÛÃ7âù‚+#øÞàñöò ¸ÛOp¸ Ün7ƒ«¯®ÛÁãðSÇÀ#Ü‚ûÀ¸Ün·€Çñï€Á}` Üî·ƒ[ÀÍàÕàZ~žßp3x5¸\ÍŸ3ؾ'x<îÂõ‡Áý`¼ ¼¼ o7‚W‚—€ð¿ƒüGÀ›Àà•à%àà ûÙÇÀ‡ÀÃàþëŸÙÏ9†ë€Á}` Üî·ƒ[ÀÍàÕàZpuä™]?‡á7öƒð.ðNð¶3ø¡+ÁKþhÅÆgö~Ž Î:îãq¸Ün·€›Á«Áµàjp%¸ñM¸oððpx÷ûøxÜÀ»À;¹>ãón⟠^ ò<ŸÇ—+À“ˆ;sÄ¡‡Áý`aÓ3û9Ï”¾=Sö홊¿S°Û{À]àvp ¸¼\ ®W‚íàqÈñ×w€Îøp%ØGy <÷)pøLù¹}dJæÃôN¯ß÷úôÅ7Ÿ<="5Þ5ÒÖˆŽ´7Þ6ÒÑYÖ¸ady#2rNãïGžÕèñ5^7òìFxdEãïF:¡‘ç4^5rn£wä¯/yn#8âo¼häyÀÈÊÆßŒ¬jt<¿ñü‘óþ‘¿nÇs&OÐþüüäIÙŸÓ y"öç%Ï`&[ÉÎf«ÙZ¶žæb¹Ñ\œ<‘Dn2'㹌'VûÕˆ:¨Êê”:£VÔYµªÖÔ@¾7ʇóýùH~0/ç§ò3ùJ~6_Í×òBo!Tú ‘Â`A5žäl¡Z¨ê…ÅBHë×"Ú Õ†µ˜6ªÉÚ¢¶¤IE_Ñ_ì*ŠýÅÁb´8\ŒG‹ñ¢Zœ)VгÅj±V¬ƒ¥P)\ê/EJƒ¥hI-Í”*¥ÙRµT+ÕKÁr¨.÷—#åÁr´<\//–—Ê’Î%8Lä$8«K½D×Û‰ºCv!9,÷Ë9FäG¥7#WdººF’S‚ “ý¦ôFSñ”_ (A¥W )a…®™©–k#j[ Ki»ÆO:4;dÓàQ"³Ål3»”•r¾œ?ו 䂹Þ\(Îõç"¹Á\47œ›ÍÕrõÜ"tÛG4{T«ãjB$²”òþ|W>rœÌ«‚—ò¾‚¿ÐU‚D’ƒ…a²"F ñÂ8Y3‚$›….-¨õj!-lÈtT×Ú$‘¨ªMi5"צ)Ù‘kÄ”ì$‘ë”)Ù¥¢¯ä/u•¥`©—Hv¸+–â¥ñR‚H˜Ëw±Ô,u•ƒå^SΣDƉòdY.«å©r¥\-×Êu"õ¦!w?VmHëýzDê1›Ù‡%©FÖo”H|TŽÛV.—=µ‹rS^’“ÃÉXr4OŽ'ÉɤL¤¿””R¾”?Õ• ¤‚©^׊¦:1žJ¤&Éš¦+ZR|Š_é"ZwXЀÃRÊ6ËpØÁŠÃÞE ­ˆgdz bßd¢v­açfsUC+ÂÆŠŽˆZQW›êÑ ´"žOM­°¯è(YÏ–,$²–éJŽ›ò—°–ƒÅ^"ùpQ¤]+†ÌL%]…l—JRÙGäÊVoŒH7Nä:Cä: ÉúLKLej_ϳzM¯ë‹z“Úåˆ$}–¬k‰¬j¾¦å(‘¯(ÝE"S)éKú“]ÄJ÷'“QSÂvKí³ÉwR­švšJuØf§%›ÿ³¯õŠÍ×Ù%·ùµYÈx)ÃüY¾lV`Ü5õfØñEb¸Eè5­Á°iÛ-=¨™–Á¯Ô aéûMˆÃJ¨Äâ3{O5„é·ü‘|4?œ™º2ëQÏ7‰‘ˆaö# ‰Á‚Èð­Uè¤ùµ.-@ìHžaº4iØ‘m–Ø’ºaM¸^õ¢ö$ /1nZ”YÃO,›EÉ´)\߆aU&‰ç˜2, ·+RÙ_î*ˆmádØÐ?j_db[˜Ö\¶%LìÊ ±,Ãfl ê3z…hc•h£¡‹QIR ]ä^& m6cÖ¦K#%­¸ëå(¬Ž¥™õdö§ËÔ͈iwƉ͑SªS4Më„4ã¹,Q¾ª ?Å­RÄŒÝÆÍ¸ÍÒc¦&G$ Ë–½rêrÄ´] èò ‰JZé²§L"F±¼Õä.•Æ,&$韡—~C3¹Íó>Ë3«43@»G–‰Ož"^y¡Û+÷›Z7ôTMM=­M­c—9 •–I¡ÊR²È.¡ 0(TV&…ªJM¨¨t Õg…@uØNî¥yÅ n³£öì"n³£Ü S,gSÈ.zͺ]C¹õ 6³þ–g “ë¹è°œ^–’ÙGª‹2‰ ©î‰öP¬(pËøô¬â8Ñ@+ï+F¢ÛòO»V,“8±Í^‹ˆQb̰–“†/fž¸îˆÃÐI+"´2RRËXKͺ³…³u æs©Ýó{ÔßXvBµÄgf!ÜË6Õˆ¨UUæ_©Wå±Ï;ÃFì%zR‰X¦¨­úP)ømþ1BìPÕáÃÄÞÈ6ÿ'éFm^¯Rò;ä/;ëTf3‚„ &IS’ô•6{œ4e«„M 3üÕ¢C*¬:@kM¡À×>[õt½û=ê§, ¢P¯GU”E@´"1+EV Ä2D*Ÿ˜+ú+  H‡­[ºb§<¢ÛP)Í:♑ÅÉÈãèº\Òìõ½q"%Ÿc-ŽºÖ_ˆ¬1^çaÈR¹ß±ªft©"IE£Ûe«èÖ¼.Øó±Ý–妑Œ£’7N,´Ï&™~"çŠ ñ³ŠÍâ*›)Ú4‹¨lªF–c¯ã1ßOeÓ4Ö‹eMéÊañ(•߬ÝY¶”YQ*›^¡VÃ׳šT2³öjUl˜½¤ñdÌ óÙ|1Må“0ò)[å•YGj§„èÛC¶¢¨”ªâ:ª’ذÍòÁdÔÒÙ+3A²šìõ˜A[mÍ.%»„¸¬Úz ¼æbÏê=o°rPjÓ&ÍõB-[«¦ß–-Ì"7`–Ga,»ä¹eÐVM6¼óiÎê¸jÔ?¨tÜë†×:ÜÖÇNb.H+¥V ÃY 5ëur4|¯‹ÎzX·Q3*·÷2x„c¯†óloI»CÈZê¶ÎOVmÖ¶rx|Jè\,f¨qÚeãú‰Ûr7…XÙ>]15X2»·á5N»Dx|°å\ÜãôÛ¢žŸóúõR‰E1SÔ¢Õ±Nm¯gJM" "«jDeÂ=Ž=&„=óÛªÑqÃ×È$Ê} ¯WÖmÞŸ×yfmݸ<Ýšñ¼fÒ–]û°VìQ!·eöD «Ä¾F*ð.!›ãë# øþA3¡µ¿«šrö½£&¢<ûz©Ùb:»|d互Ô&ýˆÈ§ËÖArú±j"›>‡Y³£c0¢µ)‡¼ÂˆÚT[¿•×KmÕš­Ê­Ý¸­cZźâÁÞÕ8^YæÑ_m£Ç3å̳S{ . ›Èc ž«m~KµÕNš¨›ØûN<+µÇ<õÛª ¼*lõyXìÆÒ\d¾Éß&EÛEß$ÊÖZ…gêñZ¯“qk¹”d±`rç5 g÷Ðê¸ËÈ«ðm¼¿À«±,úÚbõ[äÑ/t½j­WÿЫJVqEô¼n6xÕ2{‰E/²GåŒçÜ »´ jF™>ä}ÜJÇ…äŒQ›°gÜnºbf'œùaÔaËyìÃm¹3+LØ¢T+’V¹6é>ÇÜÓ®˜C§ê6 ³½a[’Wš½ÈˆQK 6Æ«ܲp›2Ž*,×.«fåb”kÕûíq”åü¶Ù {DÅ{×<®%ºUËùŒÙ‹I¢-KÄ: ?0Ct¡‹hÀ(±ýµÍ%†m+•pݱò©Ýg‘QÕì±ÈÕ-Ç‘ S~q6è°ü,Z’zÛ¤ú:ÎlCuÙ‚€YÏ´$7kÚK^4SçRâ“ ]fíÒš©Ø*å,ƶU~x&vMÎÌõûŒLÔì%Θ‘••ÏMË0j³5!¯÷;¢`jf]ë>d«ûL:êææ ¨Ç§½±BîÌûY<–°å–UWÀ4"î¹Ò뮼“×€¼r÷ôU’=Ö;‚-ê?öÞ ò¡p›Y+÷Mòö4›iÕ½¶,ƒ³þqÌ%ØõLô=NÏÃ"žWmU yØ'‘xü²Õ)xÅÑ>ÁÀuÑ饘NÚódž‹ Ú|÷PuA#»Ì¼Ùwr¿Ô´y#‘Œšy*T;$['‡G¦ãFP5½’—®òÌaÒ¦©Ü+Ùóï©§&|R¯MK½'dxµ2lÓPžmÌ:&dX2bÓN>ßFã!:KÑ&UÛݾJÔNÙý<µnŠu·×£a±VλØÖMØ5•(NÔ,º&Å9DwÝFŒ™C®Úš8—QwMŠ3†ã®Ž£¨½½®«µEe‘´Õ‘Œ»"+ѾÚcjVW·4¹êêJ…™«›íÔec\vWô󮞤è‘ÝÕ$Ñ7w¹¬¯ÝK7]Õw»åµë³O˜ùrNûñj»m“Þåá·Ï.®Ÿ4·êè÷´žý’]ZÜÕ"Îg:\±M •ß§3ëöô–¦ YÕÓ¥,«ÚS­”˜¬îÒÆVò™§ˆòCDcB½eÖQ3û94žwöÀcŽ˜ZÐEþ·5 Dµj™ßû3ýR]¾Ö™¤^=ïV¾w¿þb¬MúŠéËû¡YÜJò™ê.Ûl0«à8«Ò^óg\ÇÆ=5ËY³fÚ5ã9…ʬâ çT6³ŠÎ¹TÞñ¦ºæœQåõªmöÎ×1Wú<çUFniéšsB‘ùîYC·z[øê³Ã휿°×&[Ì`V.æÊ7¨îÙcLûÜ…¥³¶ü£U¿qÒ#Îtv¦ìF§ï:CŸ‘Ú>[Ý=Þ&…ÎPÛH´¬hˆ™¨;£qk™ðÐW>§Ñëi ½ç§{[ì(˜qì# :KuÕêYú ëGu“j$õÊTyÇ…Ö6x’jõ³Ô¢ñÞÕµŠ`¿Ü:Um1Í3hj›ûÅ«ûãð”büg×¢°£Wí¬L0M’<ªÎyijÓœÓÜoVœùI¢Mú9ËCµÆ=5ÍôEj̶±øÏY÷äca—ÏdvÍ91tôÄ©ªÎ jÞY¨¸ö–0{uùKfËœ{%xN" ŸO¤]ØU1cÑ\Ýå;™çlÅݼaG=ܲi,‰º´Ù3ÉcŠŒZ3YÈ>XÄÆ|hØe½˜Ö]¶‹Y®¸Kß˜Ý ¸lÖÙ{Ï„sˆ3mÒ{=ögŒzÆgK²Ï¦yQOÛ$ÎHð\ÊÕfm6ÉoZ%Ë"Y•X¦u|?Ž¥w£ð£<»h"§°<(Ï&¸÷d¾3èÚ©Ä<¨»Ktä¾Lßœ¹/‹Öœ¹/Ó:gîË'Ƽü)Ó=gÇ2æ¹Ä+Wàñ›³Ûÿ1ue <çåšÇ<&³uVeFì<[SÛÌK2MsÎàÄ…½[ÖŒ¶5×Ñ&ÝûÖëQéõÍ»*kM†%<WµeÔVѳª-n?lY·ºkw0³i^ޗƉQKÆý­3/±r`ŸYËC«˜u„VÑ©BªKÌQÏɧ i6ÁtÆšòZÂü Õ >=ȪkÔÓQ=šmÒŒ‘X3ƒTúÞ=×øSô¨¼½b°YϹÜ~CÆîsÜRfÞk´ÅR‹êíx‹ó üg8Åà좮xK_ÖÛb¿S«ŠnXè/ˆØl‹}OÎ n‡ÕZìzrî$~ªŠ/ón­&9Z÷wcžu·§ªS»5ãÊbydæœD¶"3Áï£õ2ÏØl¸eÇ¡Ul残>½Ì&=¨&óée6WN#-ª«VWk¨[7­øJvi¦a…mV(nórnKdiç¤kªÈаB.­´,SÍÕe°,”sAMÐI÷T4×HwÍÄ^îmQåeZ7ø”µ8ïX‰k‘sˆ¡-þviÖ6]õ¬ÀrpÏ‘ˆ±—·²2Éþ§<—%ö”g­´²ALÚ¸Ùê!µŽ“©u±bãVöƒ×î-Y9w8#KfÏÌͲ–ìZMyqËÐjÂ'LÚ©»’ÜЏÎûÈì½Eç™0vÏÄ=Qý çîxíå±ò&¾’§Œ8;8<6>Ãd µjÝ+Õ.i+ ò{fí–ܹŸŸ¡;Óºïl^­lÇ»hÉÝZ»õ3ìð’zÛ¥Az¥½³*Eî ¥ûœ ïòbНåÉ3œÂtæúÓ km{ç.–6X™Š»öm× ^Õ‰a.”Ç^Ó b<Á÷ÛÓ> ­ã„ }°v¤N sŸÖ™bµ±ß˜²a1ë³ðN³b¥›ÛõqÇDXë½­úuÌòs»Ïgoø öŒG¶Í‡þ€$ÌŸtÌ,0Íb=ñ\Í]Ô|¯jÜŒØNÕE#"¦UI>sÃ*’ c±"œÁ¬ Ÿ@g;¨'1‡^Å :ë%‡°u»PYîlíšà„ˆöŸRÛ3…J#«ÙðYšvòX‚íø¨!šða¯tú7Œm“ÈoØÞœEÌ'w¡ÞoÎ(3MTÍ!¨…ò™qD?öìðZ8‹&ÄXšOûSy5|óɳè.!®{§™ž2‹%û¦ùITC»Ì‰ÅAL2XZ&“©§ bŒé':øi&쪑A£*1û0TyׯKÆçeB8Ç„j!Ÿ›ÂÞÇ:Ÿ‰´Kã¦r 44íæg{ùYÖí3÷KS dvŽŸ_¦•«ÐA»ˆ³_|Gó_|§´WmHtk–|Z”|ÿzÎgì{‹“»¨ª"+‰<I‹ï®’Ï‹’O ’Ï Ó.².5—I÷ÓçKþ}‰<[‰<½fj”¬¥Q²&èNçÑÜf¶Ÿh]3×O4%Dô Bòâa"Í)òl|D^!b[c¦Çf;Ãie”ZZfe™e–ÙWŸ°û‘YVfWY³dì‡d'ÞÑ3¨W¦™Õ5#¨h°*ó´–¹\R%~Î󿬊É|/ó¼Ìï2ËÆ|.ó¸ÔßÖ“á<ó̪2kʬ(«Óò½ 4ßjß²×ͬhFÅO ò#ÿæ;kX/±‚¹”n£ÏœÆ |¶‘æJ,KbÖ—Y^~î#›¢v–ÙW>kKí)³£|^‘ÚMÞËfÓÔ>2»Èì!µƒÌúñiÄ9;Km³i¼†JmŸL¬˜Õ|¶Ó…Z&qBÑoæÕ£†þñn ŸTŒš³ŠUaÚÚêR[À,€5ù@=#ó‡ÔòsZe³–Φ¨tFÍœ£bæÁ,æ¹/ŸR¥RbõV'áÚDåĪR`¹ôucÿ4ëå1)ñÝN¬ NåÄwÒR91¿ÈçK©œ¸ÿcžwq¨¤˜‡ã•ÙìöúÍ> Û¹4cø&摬“n¨ßáµ±0| õ)Ì“0Â<óÖ 3ßw3kÖTBÔê3[Ïl<³í̦3[Îl8Ÿ&­xÕ«‘ ½CAÃÂQY±…E&,"a5 ^ãbÒâûؘ´X­‚×(Xeš×·ø«Lb¼›™õ.7ÎÀaÕ^ábQŽuZ •¯C°Zhø$ ÛÝoìReýV6E%Ç»õQD",a‘‹8X¤Á" Y°ˆ‚÷ÓXgžE,f`±ŸY¢rä'âPÿO½>? ‡úõ¨±U6<7ë«÷¾Yì«SŸË<-ó°Ì³ò} Tb,æd±&1YdIm4#ù¾.*+š=±*ŸïB=‰÷Ÿ¨„øü$›¢òá½&V/’ÂË¥3?âó’T6üÔ[¶3=ˆI5*]ò‰HÖ5&(¨<ø•ß-œ0ö¨P‰ðyl*ûшŸ€ÃO´eÓdTS±þ?™\5α¦Òóãlr¶Ÿ”M€Q ² €Eþ¼·H¥X5öûÍÉ*CZ¤]–éÒ,—\\5ê+Ës$ØîO6±EåÆg¨ÜøÜ=ë&p–•¯Sɱ¨ŸEû,ÊgÑ=‹êgŒ½ÃlҊͺ ›;‹êÆn>uÀ«sTr‹fuŽJŽEÚ<Âf±5©Y4Í£h¾'“ÉŒEÌ©…ò­fYÿûs¾3ïs÷ ¨Æ^º0Y q")GЈ/L¤Gä@×DÖA˜è*TrSËéQ*äYˆäh„ T'Ò 1Aœh]…D‰³Ut‚ Á£¨¥Mû׬ónº„sZÙ.NAaõ³æ_XÏ很V x7bžÈ*ÎPñù©ACï­)¼0…GgçiÿrJ—üçHijpš*ï$õW?N8¸*¯uœkÎ*®ìÌTvb*;ó‹–0kÿ1T[Ù)©uœC@×)fÌij~S•Ÿ8Ǩ¯²óÊ{q ¥»Æ/á¬Þ¿´RÅ윌ÓÇ­~R¯Y¯ãÞ ¤+áT#ÚIŠáäÝ)H•UCÙ”zM ƒTŬ¥'ŠGŒ<>º<;ÏhÓ‹š| gM™" 'ì²j| ç…Wp®.;U—Õ࣮ <;K×.û§'ùÀÓ¼—Ü{õé)÷Þs¤ÅöVr:R·ËÜ’8—w²®9ˆŒ£D¾ÿ• á2_Îä3û2û33‡3󙣙…L#ó»Ì™eÙçdÏË^˜½8ûòìeÙ×d¯É¾1{cöÙ[³0«ƒ_Î&³ùìÝÙýÙƒÙÃÙ#٣مl#{"ûDvYî9¹çç.Ì]œ{yn}î5¹kro̽%÷ŽÜ­¹ä>jÖ“¹|îîÜ7ss‡sGrGs ¹Fîw¹'rËÔsÕóÔ Õ«¯P/S¯R¯U¯G¼óAõcê'Ô;Õ=ê]ª¢jê´ú-õ>õ~õÇê#ê£ê õ´ú¬üóòæ/Î_šïËoÈ¿!ÿÖü;óÛòÍ<ÿ™üó_ÍçòÓùùïæÌÿ,¿ÿmþù¶Â¹…ó ÖÖ^]¸®ð¦ÂPáÖ +|ªðO…/”‚VøFá;…ïæ ~]x¼ðda¹ö\­[ëÑ^®]¡½Vۤݨ½C{öaíµ;´ m¯–ÑîÖ¾©ÒÐ~ª=¢ýFû½vZë,®*^X|iñ•žâµÅë‹o-ÞR|_ñ£Å±âîâ‹Éb¾8]¼·ø½âƒÅŸYümñT±£tn饖֔./]Uº®ôæÒÍ¥[K;J;KŸ*}¡ôå’RÒK÷”¾SúAéÇ¥‡KÒ‰Ò“¥g—ŸWî.¿¤|iùŠòëËo(ßXÞZÞVþpùåÏ”'Ê_+çÊw—¿]þnùòÑòBù7å?•ÛôNý|ý"ý¥úzýÕúµú€>¤ß¢Pÿ˜>¦^ÿ’žÔKú7ô{õ9}^ÿ¹þ¨þ8ÉΑnè¤6ù\ù<ù"y¼N~µ|ü&yH¾Uþ€ü1ùSò?É_’Y“¿!Gþ¾5º7õ½Ôƒ©Ÿ§~™úmêTªC9WyòBer¹r•ròfåfåVe‡²Sù”òåËŠ¢èÊ=Êw”(?VVÊ åIåÙé祻Ó/I_š¾"ýúôÒ7¦·¦·¥?œþDú3é‰ô×Ò¹ôÝéo§¿›~ }4½þMúOé¶LgæüÌE™—fÖg^¹63ÊÜ’ù`æc™±Ìç3_Ê$3¥Ì72÷fæÈÊúyæÑÌã™S™geŸ›}±ª.ÏnÈnʾ9ûÎì{²;²ÏÞ‘ýBö«ÙLVÏÈÊþ û³ì#d5ý1{:ûìÜyd-½$·.×—{}îM¹·æ¶ëè¹Ê}1÷µœ–›Î};÷ý܃d ý:÷ÛÜŸrËÉ :_íQרëÕתשê;Ô[ÉêùGõSêçÕ½dí”ÔoªßQçÔŸª“•ó{õI²rVå»ÉÊyeþ ²r®ÏßHVÎûò&+gw~‚¬œ|þn²r¾—€¬œ_æCVNG¡“¬œ^JVÎU…kÉʹ¹p Y9; cdå|¹$+çžÂ½dåü¸ðs²rNN‘•ó<ídå\ª]nÆOïÔÞGÖÎíÚ§µÏiÿ¢}MÓ´ií[Ú}ÚýÚ´Ÿkjk'µöâŠâÊbwñ%ÅKÍþ[A)Þ^ütñsÅ/•¢Vüzñ[ÅûŠ÷\|¤øhñxñd±½´¢´ªtQé%æ ë›È:º¥ôþÒGJ·—>]ú|i/YIÅÒ×Kß*ÝGÖÒÏJ”+/,µ—;Ëç—/2çÖ®+¿¹|sùÝå÷—?R¾½|YO{ËJ¹\Þ_>X¾s)'ʧËÏÒŸ§_¨_¬_ª÷éô7èoÕß©oÓ?ª\ÿŒþEý«zNŸÖèßÕÔ¦/è¿Õÿhä®çH¯{ZkêAù?ŒŒö¤|Nò¯’]É%_–¼,yurcò£.±=y{òÓÄ»|%™N–“û““÷5£Ç’Hþ9éK=?õ7©§Ö¦®L½Î¨ümI½?õâq>›úçÔ¿¦ ©}©oµÚ‡R¿Jýgj)µLyŽò×ÊjååUF·k³òvåÝ$òøå“Êç”»””R42ãÃÊO”_(‹Êï”'”sÒ+Ó¤_ddÉW§ß˜~ ñTïM(}{úÎôžôWŒÊÓþôÿHÿø­ÿ™þ_é?¤Û3+2Ï7|ÖÚÌk2×õ¶Ì»2ïÏÜ–Ù•ù¬QO,dþ-óï$ŸþQæ?2¿ÊÏœ$¾ŠVWg_Až}2»‹œdZ»WûñIi¿ÒþSûƒÖV<·xñIÁ⫊W|ñICÅ­ÅmdEí*ÞYÜSü*ñIw¿IÖ«_7Š,ž.>«´²ô·¥—^Qº¢ôúÒˆWŠ–Þ[úPéJw”&ŒµT.í/,.)ýÂ\IU¾ ¼º|Iymù5ååÍå›Ê[È:º­¼«|gyOù_Ë…ò>cý°üPùXù±òñòŸË+ô•úújýú•úÕúF}³þv}D߮ߦïÒ?§ß¥§ô¢Ñ«8¬ÿDÿ…1×pŽôÞ¶ÿï—þûýÒYüÿ_þ?þßÿÿÿPKŠØv`Žžž PK-3kJApp1.exeì›MoãÆÇg 7Ø80°È©è¡%„H˜–´îÆ1ÖYË’v­T²IöAŠÝ9–“CfHz¥öTôÚkO½öÖc¢h=ôÐPô’~€ Ûg(R/‡3Ô.‚ñ”Dræ7ÿyæí™!Ýùe†ƒìËKqN›™¿]G?ùÍ?.’ëÿE%ôÎÚ;­!ôãr|ñy| ôòÿ¼‡¢ûoñ4ûF^t…5ôü÷“ˆhú•î¥N>ãßï¶ ¡4„~˜s[g&0Bv"ˆç­Å8èå>‰6aiq¼»éxèKùÌ@qÊñ±±ï@È(HÏcÞÞ’ôç²â݆Ûp¾á½xð|a~ßÕ²ã5RãoÍó*ºiÛ“³îÑ¡EMž=ë]YNsD:Ø¢“[Z|܆ïtH×ïmøþ…;à<üü³÷ààÍá¾¹ŸŽÝËtLîÃy‚ë¿\ÿcÆõ·×úK†þ)¸þ/8îU€÷óÙ?ƒûÍÚÜù¯úÏÙù»è5Q>kðÙBçpvˆÎàW5àó=F'ï0´lSë=²§1lnlnô]×ÖŒ+Ñö67,ÛÐɈhIØÓ*úŽ^Ý©–wõrE@ò@Œ÷´ÓîÉÇÍzÿøI£[ÝÜ a`<3™uCX”\1»À¸:Ëp>™8•?\¥*Ë3ÛÜx̰C^¸ì Z·YÝu‡ôŠþ·Mñ†ïhžÄÁ'¡Å®s cdÛt˜nX°Ë€fߪc?cY6O¯dPiÃ"¹X)È&+ eISЦFib»a05@µšò¤)sŽmV¡Ô(оærÝENBõÇ[víP¬râÛ6ˆM†üº ¥JT¶h@˜ë‰Ì®Hé’ËÜ!BÒ¿‡Â/#Ô(лý¸XY | _w&KB0Š%rÍ“¼[­fuଆX󼋼f­Þ–ÃquÊÜÑX¾nµÕæKÂh—TeÝ&¢ˆ--¤¨—4¡ËV4ù;õ'EFe%vBdËÜb“a?uY4Ç1šIúW!Mÿ«™1¡g_¿’æzîdoÞ9JsDŒP4š¼¶Æ¸- ,Û –z²ÆŽ+:ìó÷Cš#ƒxSÉù”ù:V˜Ý•—‘ 6ŽF;wuú˜—µz4„i–ù*ù•¶Ë@¥æ^°>ΘõæÝÖ¾0äÌÇŤÏU¾ÒŽ~!ésp¥Ñ=Û)–ÍÃ…)MXÛ5RZíhÏ^¸z“S¤»Z¯aô¤zP´ŒQZK®ª‘÷H© Â}7dÜïK~u¢Q—/WÜt}-¸R£X~„©i+l ¬—yÞ™p)E´|ͧ¨.ÉE Æä9ÌêEZ¤|ìK\B÷K#Ÿ¹pDv¾ɦ‚À¾Š )?ké8Kg!x<0Æý£º-ç w)ª…–‡%›&ájÞU&<›Ò=›ŒÞÜí¸Â®ìêpX³ô|üêp…Énuø´¼¡ytþ”H÷g²›EŸ{}Mj¸‘{]Ø)L—ªå`V}è·H)øÐo‘Ò%ÃÐÆL削¨H’ÍE1SÊÉ a6ö¼œw3–ÄHšÀ ÞÇ>,ƒù^Ï¥í¾(ø+y£Cc.{ %×s‚d'¬xfY̓›RÄ“fQJ‘‡‡bJ7zªñ¹aæåUg'_ÌEÃ5B'÷¥-1p(ƒò1I±¤¶QÞøfm!tŽzèYôg~µÐ :†óÙ[œýmýëÿMÞM^|?ŠÏÖçî$¡];G1àXÈF˜]"7ºÿÓ(Uîb¸êÃ}Œˆç"š¼uºþÙgôà:ƒ; 3H»Qœòôo ¢ïùQôžlâ8ðG ~¹LBiîžå?†Òâ(^ªè'ɯ‡ŒH‡· ³çªD¿Ëèî\šs8¤šÅ­ â$ÏãmˆßŠ´ñ¸4ØsJ¶g£XÛzÒ´álÅæ¥ð@?W6DWˆÿÿÒò5 ý  r,ïˆó~dƒgR&œ;Q]O­ÅÿŠë<‰yV¬3)'ÍÕû~dÇSHë= ¶ž·ßNd¿Å¸i+¦m¸¥©A ?Ò>Mc(©,_ÿõï[ƒO­û¥Š^.i$ž¸÷KgýÇ[»%Í`¹‰m—’ýÒ˜ø¥Gñ|bß'ÎÀk€ þ~)dtÏ7®ˆƒý-'yáuËp=ì;úM¥¤9˜Z—ÄÎçó˜¦Ma-F ð®4ñ¿’F±:ãšçÙ–1Ý`.mO ý€?ˆWÔSä )ýØ£‹Ïá ›<'&߸³lž”"õ~iJ™çL7ŽÛüù‹fóÏýö[ôƽ&¬¤…VÍ€e2dp‰mŸÄ…Š ÛjéÛ ÚnOç·£~„¾•ðêgk_¾zõÕWßNn·á»þÿÿPK¡ O s <PK-3kJ resources.priäWOˆe3“Iv7›uûÖPélmµ‡nšl²IZ/&› {Ø•ÀVV°e;M&›ÐLgfé(º]°"¡ ôàÅ“ <‰ˆ ñTcó¦õ#@’8ʤ'ƒHƉˆqšãrxÔ˜gú4ðp2Â9¾¾Ÿ>NnFó¾ë5}TLsÓxªqÅjj¾ÌÇ¢K.®E°¥ • ¼Ê®í›vvÑ^”\Ît)ªËfÿÐ/L5ß}íÌ«¼»ösŠOãŸòS‚Àã°Š‚Ìþ£dHeÂÈ^gþê:IÜž]—FÕyu !ݸg¿©-<ïù†_]K Jþ¾P\€XI¼éÙ™*CeÁê !óÔ€.‰Ø€ à–D”•tùœÓÒzŒ©îaÂLcBãC‰¬‘ë´ºm]§ Ùš%ÒÂRï7/tíêÆygW@ÊÃ&¹Ÿ÷t%Ñý£#} éyÞEFQ‘AUcÀE‚SC7¸fçw÷ý9„CMÕÏDnq()­ô?‚w:ZZS úÖ­@Ý+Ï–k5 #=»ôÿ‰|žbïD€¸ýcÜþ‰»ä˜¢½9HžŠàÀØR`Ü“x“âûæhڨ߽²Àñz¹CÏ9Ð0?êÛÀ=ìRFƒ luTMf虀ÿMc´ƒ Uz-eׂ 4Vb?ã4V…TÛq0’FòDeJ؈A„£Jµ¶Î]Lcñ5%Ü–³Ã¯ndTM~¬ÑÍ^:øþÎ÷ß0¯ä¹ñd h¢²ÊæÞ K6BoÄg «‰qÓ Dx@_àš¤L-)ã3 ¡éžâ<’gÛÇxo¹]óe%,×TGÎ=û4‰e¡ìZ¥ &Ô³—íÇË (‚ûŸ”®¹t¬ *:$ŸˆU3OxLŸAàv",Eµî$6|,›ä¥UŸp'árÀ •†”×Wœÿn¯QHõ-*‡Ë­ŒÜ Û-ŠÒXï@ý:p&ønLL¼YìqXGuTµ©mü"¤ûE"q4´ŽÛj¸m·ýáLèÃx¹Ñ*Ý»bY!cG °¼–ßt-÷L@ÜaàÚ¿{«üÕn ¼f÷¶Ý¸nÚ ÿƹmÜø·ˆn0l»Í¦;ìvþ>”² ïÚ“A×µ7{·TÆjiÌæ¿ŽËéÝpîM(ŽOú'ftÉ9C"WPË“¾ïû(aÈÕT>‹[l¾PwC´bûŠ…E«¸K‘ž Z.í;ˆAÐ@"‡ôkMå»ÊX½Œ­ßHÄŠ².‰|–o´åuÎtBžñõŒçÿ@¼fóæ}‘dìó: ÷¢å}¨vÅI?ãOž¦«ÿÿPK“YµÁ| PK-3kJAppxBlockMap.xmlŒ–i¢H€¿o²ÿ¡ã~4#UP\›é™€ ÝžŠJ6Ùp‡r‡‚üú¥§3­íÌôì?¨Ôó>Ô{T}þÚ$ñÃe”¥=8½œº™¥Áco³}zee§žg)~ì¥Yïë—?ÿø,Ç™{œÛä¡[Ÿ–½°ªÈßUº!NìrDn‘•™_ Ü,¡lBŠPÎ˲Ä&½‡'» ç¸ 3ïmq]׃šdEÐ= ÕÑ;¿ÊЦY®÷åó(ŠñÃÂN:“a\ ³„¸|qxqÜ{0¢¶û‡c®Óžùáëw$t+¿ ‹úØ›6NìÔª;›9MMeǵîh«R¤ ËBŒ dðÛ£¿CÐlúò™zqxg"•%®ÊŒüd¡¡YdƒÒµcü©{“IƒïÈ!þFãîÔ¶ÃÈb¹ŽuöåP§ýÈ"‰hžÈ±89²—,6š%0ü´èÔ>²™uLÃ-0N?A ¸•aïdˆœ–-¥”suâíbÅd–dtàÒ­%EÓp ã"}º¤ûe wÀWŸ«ð_÷gºÈG3çââ~hTfTMyšÿÆ§Ê üÍã]bXxwÑ ²# ÙÇã¢@L¸›´6ã–ýæ4›åŠhOyµ0qÇMövãZ³•]Ýï]ÔO4ú׎+?+’O§´Ë_…½wŠ4ËÞ( ü¢1ï»Ï¡™[È$CKîï&h—†!¹˜sÛ÷ÍÌ@v&+n#3ð7ébh€>j«Êñ ¡Ì¤C|à =rÇ“ÁN“ír}"Ӽū–ü<øeºï†ŒÈ àvÊ0÷%ÂÊç<Ý.3ض&iµƒ»Ÿ,Q“jåGì2=X+2ÝT¨rémÊ@ÀÓàEàh–kí¥½ŒÏûóIöƒ&ôÃE¤’¶ ,}³£<Ý _ü|sAN€÷ ÃY`",ÎK¡‡›%ØÖÔA­x&”M°äí¦*ûÇr9~¯FÝÖñôììíålUk¹–XuXÍÅz¤#JMõfGë ¢gfì,¢ÕÄ nôÝÜþd}±!½¥ºS˜bOsd,/ð¸ëyLi'CȇÆÓñ Dó€áîAIجa>"#è«ß`š_PiÜNm F>Ëõ_{^½èÃç+ˆð£yDïØÍ¡&r¹Ú™Ê³ò³yrTîDr…ø˜‘úYb’À_¸7 öõl¸¥íÙw½vÃé²¢/ò"/Ò'Ž—35a‚]Ðêl)ãëfÓ]ã£{ÉÜŒ$Ïv%””“o炬®•¦ž[{ÜqNt^9|_Ãó+ˆæ¸²Vû*juu®±¦d—±¹V/âV Ÿ%Ôéø2™˜Ä©]•Jë+qà‡‚Ä@¯Ãõ~rQ»)¥Z¼æ]Håªf¼r%²óùpìjüÙÏàZ ì:ä×­…üöhw~ƒ:ë\Oa|¦´w’ͳ(½TùÊߪ^Ÿµt1)•ä”oÕ›yÍõá»Dv*\\H½U+‡nÐýyT¢zÒ#‚ãV¿ÈÏb9ï§«‘(7•€»=Îú£eà Ë«D¯9ùé4s;|\VƒîÎsm>‘»µ¸kÊÑÚé•Á.]l)ËzµWV#Ú¦GríL¥ÚÜdÉR7,_à›CÃÛ{ õý&÷å?ÿÿPK7‘Öî- PK3kJC-ä[Content_Types].xml¥’ÍJC1…÷‚ﲕ›T*ÒÛ¢UÁEU°>À˜LoCóG2­·oobq#µ-¸Ì™s¾3$Ž{gÙS6Á·ü\ 8C¯‚6¾kùûì±¹æ,x 6xlù3NO†³MÄÌJÚç–/ˆâ”Y-ÐA!¢/“yH¨S'#¨%t(/ƒK©‚'ôÔPeðÑðç°²Äú"o7ÑÖr6ÙújUË!FkP˾qY‡Ooh.w¢ï~Œ« T}w{üge2{AR“)!¸?åÎöÖ^ —›"õÂ7sÌtV#öRž0ì=ƒ+Qy[Œw6¨å¢8ýQ#â!ô›é<Ð*¡ˆWý‘ìü“Ùž"9 Ÿ µK†6¢à·Ä¥É¨ªµÈï?:úPK3kJ=¸VÞL AppxMetadata/CodeIntegrity.catÍU{4”iÿn†1øŒ»Ü)—ø¾wÙM*£3r_bÕŠ$—ˆJQmƺ”¶:.ãšj±ÉJV«¢A4+lÊ-Å ±Ja‡£Ø9íé¯Îé|ÿ¼¿ßù½ßó¼Ïû{žeài8¼N-nF„X ¼ÊÀA ˆ‘Pþ‡8¸F P@Ð^" ÃDd1 FUqÂË$±ì'“ñgìSïï'·%—Ɉ`&(Ã(CŒj슮’ÃK¥£Ñ鈊¸í³Õ¹§1¼s†Þ(ÑsXS[|”L{©}-i¡d;€Š­l…` :  ^D0{Ô•]ÅPa`±ÜE-ÆwžÏ‡\ÊkÝ 5‘Ä(@rezn¾QYl­I°±ëHžu'p\ÿòú\È¥¼2{%i¹¥ô¹÷µY›Ô9Þ"jÅû¥­F²_%–]Žÿy}.$ªƒ mR,®x—È»v9…5ìÂ$>‘ O;R°»N;D‘³ð®Ü«ôˆéKÞäýÚѪ ï´ìÌp³" †µla¿…ïáOiYƨ!JYÅ‚ˆª¦DìgÀ 8Èû D @„HÌc@ ÀbÀÃ(àù±‰C áÜžî_6z….b¹E@ϲј(Ïö°+¼ƒBý1”°ˆ$ŒB5Ä(~â$!këZÁÚe,ćñ|X˜ø0‘‹/cTe¥å¨&cYûñµ€ª­.9Ï{0$ñF( 1@`Eï r5{ôkJbqžôj&'µ<¿"L‡&3fí#íù‚ã’¸ëSCß*Mu^ÐUý0é¤qª…™f5V&@$>¨§=*”›Ú"t·¬¹©)Ø”k9Ž(š¬Ë*aZ Y ;¿N±ÎŽÙŸ4h¿ 6‹dj:rTóTO§l•œwRïâÛF/¿“*V—*=,vÎ6Qß<ÁçéØ :x?|ÍVò𦶏˜ îÉý/3žõh í»7å•öáe§­rú¦vj~C±Þ\@8Ÿƒ¥7¾R,q&fO™ùîvC—Ïüújó¢ £²zaÑSˆ}Ã6ôÓ‚yöÍÓDÕ—.i$¡*½¸ÔÁ„ˆ ã„xþ„a>ï‹õcÂvl½íR3OÉ?êô´Ï8È©÷U‰z"‘]Ô¯¨rimÁ/Ô¸ÉatíèÆf¹«ƒÕ}¦±-¯‹Ï<¸Ÿq|J>³zËu¥­æ×iº÷páUaûôÅNPý,&˜¦ÜBƒ=EáFa"Q÷¬«ß,vBxâHt]IçP¤G5!âŠqYRW’o\{V‰†Æ5nÊèbذ§>•‰ì6$¿íÈ>´á–絉 [wUVøÉħÎJ‡ž×e‘„F'nkÔ%éZº½ŒI¥ï ”í.­Ø{Zæï›zØŽa½É”5î¥mG;¾½:xNù )Ω `Ãs»‚-£Æ#ø³1äÌës{Þ+‡F_ýÊ»o­Ȭ¨Uã‰Ø¢ d?^7Æ«ÞkTq•–°eK¡+ûL Q‰äj³‹ó*NNÉOû?ì×r }_C=fPäã–p;Ë‘ækP„ë¸OõºÝâ#»Û]dÖÍ‚J™^õC äzŽùO ÷ÚCSåÍؽY>.oÇ;icêïL?juuRxlm:Cþyîùösò>ÛÓOÑjÔt&šµ1د8÷KÕýn«"inÓ°“¢pÓW<$T•y· '=ðÙ±sz÷é™ÙÔM\ ,s±±XÓî?‘; tØ}\rϵ;—U%Æ•ÿ•Cr;ß„¤Z#’ƒ<É5Æš´Âš‘_“BŒÆ«UT›•´ZÅö¯µÉ!çœ=ª2°ëâ+ž¯Ú·³ò¤ ) ¸EÉ™U;©¾àæ÷ü“¯Èu²7=ûS?Å¡=è<½'„~ñ_PK3kJ†{B´„AppxSignature.p7x ðvŽ0hbogãÔjóhûÎËÈδ ‰½(TÂÄÈhÈoÀËÆ™ÐæÁ˜ÊÌÂÄÈÊ`ÐÄ(ÁÆ¥ÍÆÈÂØdÎÄȲ ‰‘(Æb`Š$*g ÎÄÂÈÈÀÀ"à}ÿ(û¹G¾Ûó”OX.˜‘‰‘4žB³€¥q‡c@@„cD€³Ãï«öýËzí»|qBZxï–͇.Ø)Îö{j¿ÿH^N¨c„³‹Âñú—âwÔ„8-&®)·«œiipïÓïk e_e7Õ„<\675C´[ðŶ@vã??ÝKf/ùÝÁýÇDz¸¤­z±“c„“ï÷’G¬3J|}·*\¯sÜê¢ÝÐÜ\R7ù ×ï+—¢Avy2XG/*é°Î5xóÈ´ÿYwégŸ¸ÚËšç{¿÷Ì· 3òYÐÄü ‰ù‰AÓ™ÌLŒLLœ ‹îÝYã[`ò%4€¹ÁØ8ÇÏ€‡9”…™‡µ(1§ Ã×€Ägf14261ä6àqÙ„™œajÙaj¡|4>'Ÿ ÏÆçAã Bù ⼆æƆ†FFÆæFfQüF†(ƒÝMŒJÈALXÌMŒü äËÅÔLš #/©Þ;¡·o]+[´Ïž‰§&mY²­HËC¨ùs²XôËS!Ýñ¥Uô'+þcUd~³•ûSJçù‰“ßmbåá9yÜãâ2É/®G6=³l"û™E¶X¤³³­²4æ­›hÍ2¯É7íú©GÌå¾ï´÷=õÿ¯ô‹e®jà)ÅÅŠ]Ü„ÿ)ß0l¼òfùoѵʢëër¦xvëYu$³–jù¿[ñ4 ñÜçír±_χX²3Dô?~5çáý'ªyŒwœLæ¬Äƽ©Ëqá£/qªvFÎ.;²¸…¦-4œzú½Ìº`žùŸLÍ+Âöž¸üOwÕŠm¦›:|–­xÀ´}³gÁ¬‰¶LÌŒ Œ‹U ”Á‘$+ÌÂdÀ` bª22þgá1àbãægVVvff´´Ë ¿‰Ì¾ÛuüD¿?‰ª×ºwíEî÷IåµvòE³XëGUÝ6Þ"å•ûRéR‘fãé³’«®?ÝóÈ¢õüç ™Àµ='Í©ý"5wëF97«ÚGÙ>ï,ÊÔ,ùhœfýq¢µÅ¥eú©+ŠM‹x«:ïùñŸŸ!c{Gq÷ëÆÃën<«J>¥ØQ¾ÒlSßí¾”¶kóÖ©¨¬¾4asÝ ™¢ÑzÆY’LD~^Ÿ_j³;zõR¡nñ;¶¥‰·Oú%V0M{?Ç›{U÷iÛ†¿jžä“ž-q‡ký¶ô.ñ·»t #_h)è|š ±þJåuûUO§Èÿào ZšeóÜwéyµÓÓ‡2¶61…óy°Œ4h\5ÈsZ9„TÚ.¨1@*¸y AÉ@ݜ̆<È¥½ l ‚/j ÐÈb¨Ä¢%óh›º]wô´ý9Ç.=ØÚô(äbǦôîÁ'Ʊ{ÿé,AË¡,MŒ S÷þãÁŸ¨ëm‡kÀÛ÷¶EÈq´™ØÇ]×dݲ›o¡MåbV;®0óŸÊ6…Zö+*sNš°>ŠsÏuŸÈ~çŒYF/4d8<]Yc²éæÏ”GÔ]]ÞN~îŸS·Å~©NÙÝ…G?Ë<\9Ghæ,)qî§¢Š Vy•Õ½W”9|{î(<»zãÃRámóYCUÖl•u›ïòû™ŽìÆ['úŒçøü]£ÊðÃiQpÛ;¯oF©ÕÎvØöaê:« oö¿Ykô|ïõäøÝgnÅ¥q³°våó{åâÀúÄô¢¯¾Rä½K!ŽÅ¼í«{¸ÜmüÖKÚžÌ{ºHç‘vÇ¿6ž7îŸæPK--3kJ1ÒÔ‹”XÿClrCompression.dllPK--3kJzóø oo$Ó”Assets/Square44x44Logo.scale-200.pngPK--3kJ—z²––#œ›Assets/LockScreenLogo.scale-200.pngPK--3kJe h“!‹¡Assets/SplashScreen.scale-200.pngPK--3kJ]^}y y &ö¿Assets/Square150x150Logo.scale-200.pngPK--3kJ2ˆåq««ËËAssets/StoreLogo.pngPK--3kJô½ çç9ÀÑAssets/Square44x44Logo.targetsize-24_altform-unplated.pngPK--3kJ¯ÜB„ „ $×Assets/Wide310x150Logo.scale-200.pngPK--3kJŠØv`Žžž ôãApp1.dllPK--3kJ¡ O s <À‚App1.exePK--3kJ¦7[Ž¿8 qŒresources.priPK--3kJ“YµÁ| s’AppxManifest.xmlPK--3kJ7‘Öî- z—AppxBlockMap.xmlPK-3kJC-äíœ[Content_Types].xmlPK-3kJ=¸VÞL KžAppxMetadata/CodeIntegrity.catPK-3kJ†{B´„e¤AppxSignature.p7xPK,--pªPKˆ®PKÿÿÿÿÿÿÿÿÿÿÿÿrelic-7.6.1/functest/packages/ClassLibrary1.dll000066400000000000000000000110001455105530300213750ustar00rootroot00000000000000MZÿÿ¸@€º´ Í!¸LÍ!This program cannot be run in DOS mode. $PELs®Xà" 0 ¦( @ €£˜`…T(O@˜` '  H.text¬  `.rsrc˜@ @@.reloc `@Bˆ(H` < œ&€rp*( *BSJB v4.0.30319l´#~ D#Stringsd#US|#GUIDŒ°#BlobG ú3§o6Ü.^ÀåÀÆÀVÀ"À;ÀuÀJï(ï©À=¹AP †ÒÅW †Ö ÖÖÖ )Ö1Ö9ÖAÖIÖQÖYÖaÖiÖqÖyÖÖ. É.Ò.ñ.#ú.+ .3 .; .Cú.K.S .[ .c+.kU.sb€#Class1ClassLibrary1mscorlibGuidAttributeDebuggableAttributeComVisibleAttributeAssemblyTitleAttributeAssemblyTrademarkAttributeTargetFrameworkAttributeAssemblyFileVersionAttributeAssemblyConfigurationAttributeAssemblyDescriptionAttributeCompilationRelaxationsAttributeAssemblyProductAttributeAssemblyCopyrightAttributeAssemblyCompanyAttributeRuntimeCompatibilityAttributeSystem.Runtime.VersioningClassLibrary1.dllSystemSystem.ReflectionFoo.ctorSystem.DiagnosticsSystem.Runtime.InteropServicesSystem.Runtime.CompilerServicesDebuggingModesObjecturgleblurgø®@Û“Ÿ!I±‰êÒ… µ     ·z\V4à‰€ $€”$RSA1CMyÆ(%iz­a'zœ „¯2'fÿ¨®!pÍ÷ƒ^7Vh‚ŠÁEQ÷Ðó‘¡úzµ¡}3RÛÒÇ‘ŽS©³2l–5à¾$=”ÂÖ‹µ½kJÆz?a4K«áç1Ãl “\üKBPåÿØê$¤oßò5$æ•a:kD7=g£Å TWrapNonExceptionThrows ClassLibrary1Copyright © 2017)$825e64e8-309e-408d-8e99-d325d21d892e 1.0.0.0M.NETFramework,Version=v4.5.2TFrameworkDisplayName.NET Framework 4.5.2¿«Ô4þ6Žt?fô‹xg'²uø¡÷Í–=¶÷¶‹œvmð—E¿ïp\¦ùš)òÜ1/ˆ¢bIG´0Šz£Új‚Цe@2gaô­ò  _Ñ5WÖh7áùHpYCãŠäÒYd­qÉïEðÂõ±ÏèªO¯¨Ö©l€/ÿßvls®X8'8 RSDSø·˜È®ÍéJ°‘j'­¹U:C:\Users\radigan\Documents\Visual Studio 2015\Projects\ClassLibrary1\ClassLibrary1\obj\Release\ClassLibrary1.pdb|(–( ˆ(_CorDllMainmscoree.dllÿ% €0€HX@<<4VS_VERSION_INFO½ïþ?DVarFileInfo$Translation°œStringFileInfox000004b0Comments"CompanyNameDFileDescriptionClassLibrary10FileVersion1.0.0.0DInternalNameClassLibrary1.dllHLegalCopyrightCopyright © 2017*LegalTrademarksLOriginalFilenameClassLibrary1.dll<ProductNameClassLibrary14ProductVersion1.0.0.08Assembly Version1.0.0.0 ¨8relic-7.6.1/functest/packages/InRelease000066400000000000000000002635211455105530300200400ustar00rootroot00000000000000-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA512 Origin: Ubuntu Label: Ubuntu Suite: zesty-updates Version: 17.04 Codename: zesty Date: Tue, 21 Mar 2017 7:49:42 UTC Architectures: amd64 arm64 armhf i386 ppc64el s390x Components: main restricted universe multiverse Description: Ubuntu Zesty Updates MD5Sum: 2fca0dd3eb74d8f0472c2e53fb62cc5e 695 Contents-armhf.gz 0fa24d86f4eb65e88acd569bff0b53a8 1416 Contents-armhf 0fa24d86f4eb65e88acd569bff0b53a8 1416 Contents-ppc64el 2fca0dd3eb74d8f0472c2e53fb62cc5e 695 Contents-ppc64el.gz 0fa24d86f4eb65e88acd569bff0b53a8 1416 Contents-arm64 0fa24d86f4eb65e88acd569bff0b53a8 1416 Contents-amd64 2fca0dd3eb74d8f0472c2e53fb62cc5e 695 Contents-arm64.gz 2fca0dd3eb74d8f0472c2e53fb62cc5e 695 Contents-amd64.gz 0fa24d86f4eb65e88acd569bff0b53a8 1416 Contents-s390x 0fa24d86f4eb65e88acd569bff0b53a8 1416 Contents-i386 2fca0dd3eb74d8f0472c2e53fb62cc5e 695 Contents-s390x.gz 2fca0dd3eb74d8f0472c2e53fb62cc5e 695 Contents-i386.gz d41d8cd98f00b204e9800998ecf8427e 0 main/binary-amd64/Packages fb41d367e17e92ff1c969c003f725eea 103 main/binary-amd64/Release e62ff0123a74adfc6903d59a449cbdb0 40 main/binary-amd64/Packages.gz 05a2a1d284b1f093d602cff1b16f23dd 64 main/binary-amd64/Packages.xz b422be4e75917c4d80f0c746e84ab664 103 main/binary-arm64/Release d41d8cd98f00b204e9800998ecf8427e 0 main/binary-arm64/Packages 05a2a1d284b1f093d602cff1b16f23dd 64 main/binary-arm64/Packages.xz e62ff0123a74adfc6903d59a449cbdb0 40 main/binary-arm64/Packages.gz c00018de88988653aa7553db62035050 103 main/binary-armhf/Release d41d8cd98f00b204e9800998ecf8427e 0 main/binary-armhf/Packages 05a2a1d284b1f093d602cff1b16f23dd 64 main/binary-armhf/Packages.xz e62ff0123a74adfc6903d59a449cbdb0 40 main/binary-armhf/Packages.gz 05a2a1d284b1f093d602cff1b16f23dd 64 main/binary-i386/Packages.xz 1ac58296c3f51f39c001af8f22b9e395 102 main/binary-i386/Release e62ff0123a74adfc6903d59a449cbdb0 40 main/binary-i386/Packages.gz d41d8cd98f00b204e9800998ecf8427e 0 main/binary-i386/Packages 05a2a1d284b1f093d602cff1b16f23dd 64 main/binary-ppc64el/Packages.xz 9491af1e767c9702f498fc40a6bdfc5d 105 main/binary-ppc64el/Release e62ff0123a74adfc6903d59a449cbdb0 40 main/binary-ppc64el/Packages.gz d41d8cd98f00b204e9800998ecf8427e 0 main/binary-ppc64el/Packages e0d7277f148627f9afd40ad5612ad7e6 103 main/binary-s390x/Release e62ff0123a74adfc6903d59a449cbdb0 40 main/binary-s390x/Packages.gz d41d8cd98f00b204e9800998ecf8427e 0 main/binary-s390x/Packages 05a2a1d284b1f093d602cff1b16f23dd 64 main/binary-s390x/Packages.xz e62ff0123a74adfc6903d59a449cbdb0 40 main/debian-installer/binary-amd64/Packages.gz d41d8cd98f00b204e9800998ecf8427e 0 main/debian-installer/binary-amd64/Packages 05a2a1d284b1f093d602cff1b16f23dd 64 main/debian-installer/binary-amd64/Packages.xz d41d8cd98f00b204e9800998ecf8427e 0 main/debian-installer/binary-arm64/Packages e62ff0123a74adfc6903d59a449cbdb0 40 main/debian-installer/binary-arm64/Packages.gz 05a2a1d284b1f093d602cff1b16f23dd 64 main/debian-installer/binary-arm64/Packages.xz e62ff0123a74adfc6903d59a449cbdb0 40 main/debian-installer/binary-armhf/Packages.gz 05a2a1d284b1f093d602cff1b16f23dd 64 main/debian-installer/binary-armhf/Packages.xz d41d8cd98f00b204e9800998ecf8427e 0 main/debian-installer/binary-armhf/Packages d41d8cd98f00b204e9800998ecf8427e 0 main/debian-installer/binary-i386/Packages e62ff0123a74adfc6903d59a449cbdb0 40 main/debian-installer/binary-i386/Packages.gz 05a2a1d284b1f093d602cff1b16f23dd 64 main/debian-installer/binary-i386/Packages.xz 05a2a1d284b1f093d602cff1b16f23dd 64 main/debian-installer/binary-ppc64el/Packages.xz e62ff0123a74adfc6903d59a449cbdb0 40 main/debian-installer/binary-ppc64el/Packages.gz d41d8cd98f00b204e9800998ecf8427e 0 main/debian-installer/binary-ppc64el/Packages 05a2a1d284b1f093d602cff1b16f23dd 64 main/debian-installer/binary-s390x/Packages.xz e62ff0123a74adfc6903d59a449cbdb0 40 main/debian-installer/binary-s390x/Packages.gz d41d8cd98f00b204e9800998ecf8427e 0 main/debian-installer/binary-s390x/Packages 5e0ea1acc9dcb26e00bbfbff42a554e0 156 main/dep11/Components-armhf.yml.gz 655201ea420cdaab7f427b7f5a77f6f8 208 main/dep11/Components-s390x.yml.xz 0f343b0931126a20f133d67c2b018a3b 1024 main/dep11/icons-128x128.tar fddea25d2dc78fc230a7104c5d8cf7f7 208 main/dep11/Components-powerpc.yml.xz 22f645a237984b9b3c187082cb29b969 208 main/dep11/Components-arm64.yml.xz 31f6566d35ccd604be46ed5b1f813cdf 29 main/dep11/icons-128x128.tar.gz aa0500b418a16eb0fbde645195bfcc65 156 main/dep11/Components-s390x.yml.gz b6f86bd6f4233ccf99f4649e494b07bd 164 main/dep11/Components-arm64.yml c8fef3ed74ae361749a8951e8514e922 164 main/dep11/Components-armhf.yml 309d9e8155b625fe1caa80c28700b723 164 main/dep11/Components-s390x.yml f4f2b1e94f3978d41febc76bb326832a 208 main/dep11/Components-amd64.yml.xz 11764f2ea218e789540806926d5f557e 208 main/dep11/Components-ppc64el.yml.xz a304b1e75ab3f654de81d1a30ba53c0a 156 main/dep11/Components-arm64.yml.gz 3591fd561d9a33ca799ada4d1fab41af 164 main/dep11/Components-i386.yml 1569849bb6b847ec2d8e7d6d9a929ee4 208 main/dep11/Components-i386.yml.xz 60316e5f3a0c0a6f0e4f7e871666f875 164 main/dep11/Components-ppc64el.yml 0f343b0931126a20f133d67c2b018a3b 1024 main/dep11/icons-64x64.tar 49de8ce14ae338352ac794b7b15d0a4c 156 main/dep11/Components-powerpc.yml.gz c5b8fc38a5e334df369d26b934ff17d4 164 main/dep11/Components-powerpc.yml 95ff05a99b311eb1283e678559e6743d 164 main/dep11/Components-amd64.yml ed1ad4855d2356f4e0031dab6d9cec6a 156 main/dep11/Components-ppc64el.yml.gz 31f6566d35ccd604be46ed5b1f813cdf 29 main/dep11/icons-64x64.tar.gz 717988572b189b610ee5e0494ac2494b 156 main/dep11/Components-i386.yml.gz 57e3ebdbef7eb79cfa0c8b345e374a6f 156 main/dep11/Components-amd64.yml.gz c45ed773024f7a97a3ce0d5cf4ac1788 208 main/dep11/Components-armhf.yml.xz e62ff0123a74adfc6903d59a449cbdb0 40 main/i18n/Translation-en.gz 05a2a1d284b1f093d602cff1b16f23dd 64 main/i18n/Translation-en.xz d41d8cd98f00b204e9800998ecf8427e 0 main/i18n/Translation-en 83f113e5a6e4d07ca337fc40833470ee 192 main/i18n/Index e3649003f9216becf39a6705be18682a 104 main/source/Release d41d8cd98f00b204e9800998ecf8427e 0 main/source/Sources e62ff0123a74adfc6903d59a449cbdb0 40 main/source/Sources.gz 05a2a1d284b1f093d602cff1b16f23dd 64 main/source/Sources.xz e62ff0123a74adfc6903d59a449cbdb0 40 multiverse/binary-amd64/Packages.gz 05a2a1d284b1f093d602cff1b16f23dd 64 multiverse/binary-amd64/Packages.xz 4adce606e1d09408a0a5817799e3ddb1 109 multiverse/binary-amd64/Release d41d8cd98f00b204e9800998ecf8427e 0 multiverse/binary-amd64/Packages e62ff0123a74adfc6903d59a449cbdb0 40 multiverse/binary-arm64/Packages.gz d41d8cd98f00b204e9800998ecf8427e 0 multiverse/binary-arm64/Packages b0112975f068a1135f87b1e29a620e4b 109 multiverse/binary-arm64/Release 05a2a1d284b1f093d602cff1b16f23dd 64 multiverse/binary-arm64/Packages.xz 56d232eca5cacc6878f544b97696667f 109 multiverse/binary-armhf/Release e62ff0123a74adfc6903d59a449cbdb0 40 multiverse/binary-armhf/Packages.gz 05a2a1d284b1f093d602cff1b16f23dd 64 multiverse/binary-armhf/Packages.xz d41d8cd98f00b204e9800998ecf8427e 0 multiverse/binary-armhf/Packages e62ff0123a74adfc6903d59a449cbdb0 40 multiverse/binary-i386/Packages.gz d41d8cd98f00b204e9800998ecf8427e 0 multiverse/binary-i386/Packages 05a2a1d284b1f093d602cff1b16f23dd 64 multiverse/binary-i386/Packages.xz cf309f58ebd409b624a87091d728e338 108 multiverse/binary-i386/Release d41d8cd98f00b204e9800998ecf8427e 0 multiverse/binary-ppc64el/Packages e62ff0123a74adfc6903d59a449cbdb0 40 multiverse/binary-ppc64el/Packages.gz 952d7d03742eaf9a144060438f6bcb07 111 multiverse/binary-ppc64el/Release 05a2a1d284b1f093d602cff1b16f23dd 64 multiverse/binary-ppc64el/Packages.xz c36fe18fc7c258535aa07b15156e66a4 109 multiverse/binary-s390x/Release d41d8cd98f00b204e9800998ecf8427e 0 multiverse/binary-s390x/Packages e62ff0123a74adfc6903d59a449cbdb0 40 multiverse/binary-s390x/Packages.gz 05a2a1d284b1f093d602cff1b16f23dd 64 multiverse/binary-s390x/Packages.xz 05a2a1d284b1f093d602cff1b16f23dd 64 multiverse/debian-installer/binary-amd64/Packages.xz d41d8cd98f00b204e9800998ecf8427e 0 multiverse/debian-installer/binary-amd64/Packages e62ff0123a74adfc6903d59a449cbdb0 40 multiverse/debian-installer/binary-amd64/Packages.gz d41d8cd98f00b204e9800998ecf8427e 0 multiverse/debian-installer/binary-arm64/Packages 05a2a1d284b1f093d602cff1b16f23dd 64 multiverse/debian-installer/binary-arm64/Packages.xz e62ff0123a74adfc6903d59a449cbdb0 40 multiverse/debian-installer/binary-arm64/Packages.gz d41d8cd98f00b204e9800998ecf8427e 0 multiverse/debian-installer/binary-armhf/Packages e62ff0123a74adfc6903d59a449cbdb0 40 multiverse/debian-installer/binary-armhf/Packages.gz 05a2a1d284b1f093d602cff1b16f23dd 64 multiverse/debian-installer/binary-armhf/Packages.xz 05a2a1d284b1f093d602cff1b16f23dd 64 multiverse/debian-installer/binary-i386/Packages.xz e62ff0123a74adfc6903d59a449cbdb0 40 multiverse/debian-installer/binary-i386/Packages.gz d41d8cd98f00b204e9800998ecf8427e 0 multiverse/debian-installer/binary-i386/Packages d41d8cd98f00b204e9800998ecf8427e 0 multiverse/debian-installer/binary-ppc64el/Packages e62ff0123a74adfc6903d59a449cbdb0 40 multiverse/debian-installer/binary-ppc64el/Packages.gz 05a2a1d284b1f093d602cff1b16f23dd 64 multiverse/debian-installer/binary-ppc64el/Packages.xz d41d8cd98f00b204e9800998ecf8427e 0 multiverse/debian-installer/binary-s390x/Packages e62ff0123a74adfc6903d59a449cbdb0 40 multiverse/debian-installer/binary-s390x/Packages.gz 05a2a1d284b1f093d602cff1b16f23dd 64 multiverse/debian-installer/binary-s390x/Packages.xz 45a91bdecec65f9a8c8fcfa3c6ecc18b 160 multiverse/dep11/Components-armhf.yml.gz 31f6566d35ccd604be46ed5b1f813cdf 29 multiverse/dep11/icons-64x64.tar.gz 0da6aee1bf382e07bbc4f9ad205fdff4 212 multiverse/dep11/Components-armhf.yml.xz 0ee168266345ef9a1f580828c0fcad40 212 multiverse/dep11/Components-ppc64el.yml.xz ed9e024d2ff1e05b6e3b83317812562c 170 multiverse/dep11/Components-i386.yml 37d90127f72cd853a41af522444870a0 160 multiverse/dep11/Components-s390x.yml.gz 31f6566d35ccd604be46ed5b1f813cdf 29 multiverse/dep11/icons-128x128.tar.gz 365c0fceb27a07710c2cf3edd257907e 212 multiverse/dep11/Components-amd64.yml.xz 240e80aadba3dcc41f4c552c21e26631 170 multiverse/dep11/Components-armhf.yml bc42d767074e976157ac6a2c3cd2b4e9 170 multiverse/dep11/Components-arm64.yml befa7682bfea5216137f2b23f0dea96f 212 multiverse/dep11/Components-powerpc.yml.xz 574c4390ea4e37347f473a33b4369e89 160 multiverse/dep11/Components-arm64.yml.gz 825de64b1f26718bcb4300cca962d7aa 212 multiverse/dep11/Components-s390x.yml.xz 1dc6abb3671f02ff3634bb3023142ec8 160 multiverse/dep11/Components-i386.yml.gz 78a255ad9db5dff2dd862a191e26d2b7 159 multiverse/dep11/Components-ppc64el.yml.gz 009e42022ca06b2374c155d28c395cc6 160 multiverse/dep11/Components-amd64.yml.gz 0f343b0931126a20f133d67c2b018a3b 1024 multiverse/dep11/icons-128x128.tar 435ded8b8a604ec1cb4371687ce20ef9 212 multiverse/dep11/Components-i386.yml.xz ae61431e9f2de661d77b50460a9cb5f0 159 multiverse/dep11/Components-powerpc.yml.gz 0f343b0931126a20f133d67c2b018a3b 1024 multiverse/dep11/icons-64x64.tar d9cab359ea78d8d385f2ce4fb9b9208c 170 multiverse/dep11/Components-ppc64el.yml 3282d81b8251982d7d9a578d14c9f066 170 multiverse/dep11/Components-amd64.yml 59d3311cc00227d26c699d39ada2571f 212 multiverse/dep11/Components-arm64.yml.xz b9a68d645ca4ff08f56f7587a2b54198 170 multiverse/dep11/Components-s390x.yml abb8b247e2445635abedc38de6716e11 170 multiverse/dep11/Components-powerpc.yml e62ff0123a74adfc6903d59a449cbdb0 40 multiverse/i18n/Translation-en.gz d41d8cd98f00b204e9800998ecf8427e 0 multiverse/i18n/Translation-en 05a2a1d284b1f093d602cff1b16f23dd 64 multiverse/i18n/Translation-en.xz 83f113e5a6e4d07ca337fc40833470ee 192 multiverse/i18n/Index e62ff0123a74adfc6903d59a449cbdb0 40 multiverse/source/Sources.gz 05a2a1d284b1f093d602cff1b16f23dd 64 multiverse/source/Sources.xz d41d8cd98f00b204e9800998ecf8427e 0 multiverse/source/Sources e5ae32d22f198cb433d072b54617ce83 110 multiverse/source/Release d41d8cd98f00b204e9800998ecf8427e 0 restricted/binary-amd64/Packages e62ff0123a74adfc6903d59a449cbdb0 40 restricted/binary-amd64/Packages.gz a37bbc3f1d8249aab62ee2c89b78daa8 109 restricted/binary-amd64/Release 05a2a1d284b1f093d602cff1b16f23dd 64 restricted/binary-amd64/Packages.xz 2e5598616ace40ddfd5ff8679a2fe6bb 109 restricted/binary-arm64/Release 05a2a1d284b1f093d602cff1b16f23dd 64 restricted/binary-arm64/Packages.xz e62ff0123a74adfc6903d59a449cbdb0 40 restricted/binary-arm64/Packages.gz d41d8cd98f00b204e9800998ecf8427e 0 restricted/binary-arm64/Packages e62ff0123a74adfc6903d59a449cbdb0 40 restricted/binary-armhf/Packages.gz 05a2a1d284b1f093d602cff1b16f23dd 64 restricted/binary-armhf/Packages.xz 01f8d8e6015e1ab0a7971bcafa7d56f5 109 restricted/binary-armhf/Release d41d8cd98f00b204e9800998ecf8427e 0 restricted/binary-armhf/Packages e62ff0123a74adfc6903d59a449cbdb0 40 restricted/binary-i386/Packages.gz 05a2a1d284b1f093d602cff1b16f23dd 64 restricted/binary-i386/Packages.xz d41d8cd98f00b204e9800998ecf8427e 0 restricted/binary-i386/Packages c4084535080eccc834fe4792f8fe7681 108 restricted/binary-i386/Release e62ff0123a74adfc6903d59a449cbdb0 40 restricted/binary-ppc64el/Packages.gz d41d8cd98f00b204e9800998ecf8427e 0 restricted/binary-ppc64el/Packages 05a2a1d284b1f093d602cff1b16f23dd 64 restricted/binary-ppc64el/Packages.xz 3bd36c3da42585c8cd0b331e0aa59b30 111 restricted/binary-ppc64el/Release e62ff0123a74adfc6903d59a449cbdb0 40 restricted/binary-s390x/Packages.gz ca83008367c0556d0a12e53c527fdd6a 109 restricted/binary-s390x/Release d41d8cd98f00b204e9800998ecf8427e 0 restricted/binary-s390x/Packages 05a2a1d284b1f093d602cff1b16f23dd 64 restricted/binary-s390x/Packages.xz 05a2a1d284b1f093d602cff1b16f23dd 64 restricted/debian-installer/binary-amd64/Packages.xz d41d8cd98f00b204e9800998ecf8427e 0 restricted/debian-installer/binary-amd64/Packages e62ff0123a74adfc6903d59a449cbdb0 40 restricted/debian-installer/binary-amd64/Packages.gz e62ff0123a74adfc6903d59a449cbdb0 40 restricted/debian-installer/binary-arm64/Packages.gz 05a2a1d284b1f093d602cff1b16f23dd 64 restricted/debian-installer/binary-arm64/Packages.xz d41d8cd98f00b204e9800998ecf8427e 0 restricted/debian-installer/binary-arm64/Packages e62ff0123a74adfc6903d59a449cbdb0 40 restricted/debian-installer/binary-armhf/Packages.gz d41d8cd98f00b204e9800998ecf8427e 0 restricted/debian-installer/binary-armhf/Packages 05a2a1d284b1f093d602cff1b16f23dd 64 restricted/debian-installer/binary-armhf/Packages.xz 05a2a1d284b1f093d602cff1b16f23dd 64 restricted/debian-installer/binary-i386/Packages.xz d41d8cd98f00b204e9800998ecf8427e 0 restricted/debian-installer/binary-i386/Packages e62ff0123a74adfc6903d59a449cbdb0 40 restricted/debian-installer/binary-i386/Packages.gz 05a2a1d284b1f093d602cff1b16f23dd 64 restricted/debian-installer/binary-ppc64el/Packages.xz d41d8cd98f00b204e9800998ecf8427e 0 restricted/debian-installer/binary-ppc64el/Packages e62ff0123a74adfc6903d59a449cbdb0 40 restricted/debian-installer/binary-ppc64el/Packages.gz 05a2a1d284b1f093d602cff1b16f23dd 64 restricted/debian-installer/binary-s390x/Packages.xz e62ff0123a74adfc6903d59a449cbdb0 40 restricted/debian-installer/binary-s390x/Packages.gz d41d8cd98f00b204e9800998ecf8427e 0 restricted/debian-installer/binary-s390x/Packages 1d9563e57bb2119461c1cae8863d0c8e 185 restricted/dep11/Components-arm64.yml 7581f281195f7de431e41c7ed413ded1 193 restricted/dep11/Components-amd64.yml.gz 1d9563e57bb2119461c1cae8863d0c8e 185 restricted/dep11/Components-armhf.yml 30ff84dba7e918730640b17475acd601 195 restricted/dep11/Components-ppc64el.yml.gz 53b6b0755f8bf1f5101e540c97fbe3b0 193 restricted/dep11/Components-s390x.yml.gz 1d9563e57bb2119461c1cae8863d0c8e 185 restricted/dep11/Components-s390x.yml 1d9563e57bb2119461c1cae8863d0c8e 185 restricted/dep11/Components-amd64.yml 1d9563e57bb2119461c1cae8863d0c8e 185 restricted/dep11/Components-ppc64el.yml 20882b6062bb4faa5356c745ff4a070b 192 restricted/dep11/Components-i386.yml.gz 3699e90df09d6f015dc7b45a839a81d4 193 restricted/dep11/Components-arm64.yml.gz 271082486d72b948525f7fbedceddf37 193 restricted/dep11/Components-armhf.yml.gz 1d9563e57bb2119461c1cae8863d0c8e 185 restricted/dep11/Components-i386.yml 83f113e5a6e4d07ca337fc40833470ee 192 restricted/i18n/Index d41d8cd98f00b204e9800998ecf8427e 0 restricted/i18n/Translation-en e62ff0123a74adfc6903d59a449cbdb0 40 restricted/i18n/Translation-en.gz 05a2a1d284b1f093d602cff1b16f23dd 64 restricted/i18n/Translation-en.xz 2819041faa2373ede941d8d188032338 110 restricted/source/Release d41d8cd98f00b204e9800998ecf8427e 0 restricted/source/Sources e62ff0123a74adfc6903d59a449cbdb0 40 restricted/source/Sources.gz 05a2a1d284b1f093d602cff1b16f23dd 64 restricted/source/Sources.xz d41d8cd98f00b204e9800998ecf8427e 0 universe/binary-amd64/Packages 05a2a1d284b1f093d602cff1b16f23dd 64 universe/binary-amd64/Packages.xz 4809710ebab965bd73ea07db2599fec6 107 universe/binary-amd64/Release e62ff0123a74adfc6903d59a449cbdb0 40 universe/binary-amd64/Packages.gz 05a2a1d284b1f093d602cff1b16f23dd 64 universe/binary-arm64/Packages.xz e62ff0123a74adfc6903d59a449cbdb0 40 universe/binary-arm64/Packages.gz b95b33caeede3aab8a9b9917e859a30b 107 universe/binary-arm64/Release d41d8cd98f00b204e9800998ecf8427e 0 universe/binary-arm64/Packages e62ff0123a74adfc6903d59a449cbdb0 40 universe/binary-armhf/Packages.gz 1472d880e9d9674788bafc690b76b418 107 universe/binary-armhf/Release 05a2a1d284b1f093d602cff1b16f23dd 64 universe/binary-armhf/Packages.xz d41d8cd98f00b204e9800998ecf8427e 0 universe/binary-armhf/Packages 025e459ad92bf55a0defeab90ad058e7 106 universe/binary-i386/Release d41d8cd98f00b204e9800998ecf8427e 0 universe/binary-i386/Packages 05a2a1d284b1f093d602cff1b16f23dd 64 universe/binary-i386/Packages.xz e62ff0123a74adfc6903d59a449cbdb0 40 universe/binary-i386/Packages.gz e62ff0123a74adfc6903d59a449cbdb0 40 universe/binary-ppc64el/Packages.gz 05a2a1d284b1f093d602cff1b16f23dd 64 universe/binary-ppc64el/Packages.xz 77b8015992ac4208f1b790b4c6a19521 109 universe/binary-ppc64el/Release d41d8cd98f00b204e9800998ecf8427e 0 universe/binary-ppc64el/Packages d41d8cd98f00b204e9800998ecf8427e 0 universe/binary-s390x/Packages 4be0f7f3fb4c02e939fb310541e4215c 107 universe/binary-s390x/Release e62ff0123a74adfc6903d59a449cbdb0 40 universe/binary-s390x/Packages.gz 05a2a1d284b1f093d602cff1b16f23dd 64 universe/binary-s390x/Packages.xz d41d8cd98f00b204e9800998ecf8427e 0 universe/debian-installer/binary-amd64/Packages e62ff0123a74adfc6903d59a449cbdb0 40 universe/debian-installer/binary-amd64/Packages.gz 05a2a1d284b1f093d602cff1b16f23dd 64 universe/debian-installer/binary-amd64/Packages.xz 05a2a1d284b1f093d602cff1b16f23dd 64 universe/debian-installer/binary-arm64/Packages.xz e62ff0123a74adfc6903d59a449cbdb0 40 universe/debian-installer/binary-arm64/Packages.gz d41d8cd98f00b204e9800998ecf8427e 0 universe/debian-installer/binary-arm64/Packages d41d8cd98f00b204e9800998ecf8427e 0 universe/debian-installer/binary-armhf/Packages e62ff0123a74adfc6903d59a449cbdb0 40 universe/debian-installer/binary-armhf/Packages.gz 05a2a1d284b1f093d602cff1b16f23dd 64 universe/debian-installer/binary-armhf/Packages.xz 05a2a1d284b1f093d602cff1b16f23dd 64 universe/debian-installer/binary-i386/Packages.xz e62ff0123a74adfc6903d59a449cbdb0 40 universe/debian-installer/binary-i386/Packages.gz d41d8cd98f00b204e9800998ecf8427e 0 universe/debian-installer/binary-i386/Packages d41d8cd98f00b204e9800998ecf8427e 0 universe/debian-installer/binary-ppc64el/Packages e62ff0123a74adfc6903d59a449cbdb0 40 universe/debian-installer/binary-ppc64el/Packages.gz 05a2a1d284b1f093d602cff1b16f23dd 64 universe/debian-installer/binary-ppc64el/Packages.xz e62ff0123a74adfc6903d59a449cbdb0 40 universe/debian-installer/binary-s390x/Packages.gz 05a2a1d284b1f093d602cff1b16f23dd 64 universe/debian-installer/binary-s390x/Packages.xz d41d8cd98f00b204e9800998ecf8427e 0 universe/debian-installer/binary-s390x/Packages 0f343b0931126a20f133d67c2b018a3b 1024 universe/dep11/icons-128x128.tar d9f4b4316ab5294f94501d435359c7a4 212 universe/dep11/Components-ppc64el.yml.xz 48db98c85168a4c064517aad8d96ad5c 212 universe/dep11/Components-s390x.yml.xz b7c271bbe3ababbbcd3a7be28847829e 212 universe/dep11/Components-armhf.yml.xz 31f6566d35ccd604be46ed5b1f813cdf 29 universe/dep11/icons-64x64.tar.gz 3ad6793bd536673fd4d270b3c3a9793d 168 universe/dep11/Components-i386.yml e7be84b75402f29623d5702299c6485f 159 universe/dep11/Components-armhf.yml.gz 3cd0458409024567bfbb5f22c6c815fd 158 universe/dep11/Components-ppc64el.yml.gz 31f6566d35ccd604be46ed5b1f813cdf 29 universe/dep11/icons-128x128.tar.gz 5ba9c1973daeb505c295566b1befdaee 159 universe/dep11/Components-powerpc.yml.gz 88e3e6da357a2e63b22bf0e1f406266d 168 universe/dep11/Components-powerpc.yml e08cd1ae60d1330016c7264e871c2520 159 universe/dep11/Components-s390x.yml.gz ec5e4ca6416a4fc30f74a3f944be3021 212 universe/dep11/Components-amd64.yml.xz ccc682b957113f0e45e1337cb834a53e 159 universe/dep11/Components-i386.yml.gz 815983c409210b058ad2a9ca5b9457c7 168 universe/dep11/Components-arm64.yml 1cbc9bb155c24c0837c16555bc5e3e25 212 universe/dep11/Components-powerpc.yml.xz 3b8525d5f3dd95612a2db2f2a63a6ba2 212 universe/dep11/Components-arm64.yml.xz d297bcffa2936a5c717bb4acea44e53f 159 universe/dep11/Components-arm64.yml.gz f6fcae6f88ddee0049ee468a1ba7d1af 168 universe/dep11/Components-s390x.yml 0f343b0931126a20f133d67c2b018a3b 1024 universe/dep11/icons-64x64.tar c2e47b5c36d7f9ffc302838bcb141188 168 universe/dep11/Components-ppc64el.yml 2ba5be4714bd52446fc7364b1154b2d4 212 universe/dep11/Components-i386.yml.xz bd4b25dad377474fe4539bde31d934ed 168 universe/dep11/Components-armhf.yml a3a0d80a622521d57fcf98e3263f89fd 159 universe/dep11/Components-amd64.yml.gz f1dbe7f5fb56cd96e213f0858b03e467 168 universe/dep11/Components-amd64.yml d41d8cd98f00b204e9800998ecf8427e 0 universe/i18n/Translation-en e62ff0123a74adfc6903d59a449cbdb0 40 universe/i18n/Translation-en.gz 83f113e5a6e4d07ca337fc40833470ee 192 universe/i18n/Index 05a2a1d284b1f093d602cff1b16f23dd 64 universe/i18n/Translation-en.xz d41d8cd98f00b204e9800998ecf8427e 0 universe/source/Sources 05a2a1d284b1f093d602cff1b16f23dd 64 universe/source/Sources.xz e62ff0123a74adfc6903d59a449cbdb0 40 universe/source/Sources.gz b66064a5569daf693735d5cbdc455a5b 108 universe/source/Release SHA1: 2c879fcb3d471450d31020e6bd0492269299eb11 695 Contents-armhf.gz c82143474d47c5b30e4e362acac8a9944bf03f74 1416 Contents-armhf c82143474d47c5b30e4e362acac8a9944bf03f74 1416 Contents-ppc64el 2c879fcb3d471450d31020e6bd0492269299eb11 695 Contents-ppc64el.gz c82143474d47c5b30e4e362acac8a9944bf03f74 1416 Contents-arm64 c82143474d47c5b30e4e362acac8a9944bf03f74 1416 Contents-amd64 2c879fcb3d471450d31020e6bd0492269299eb11 695 Contents-arm64.gz 2c879fcb3d471450d31020e6bd0492269299eb11 695 Contents-amd64.gz c82143474d47c5b30e4e362acac8a9944bf03f74 1416 Contents-s390x c82143474d47c5b30e4e362acac8a9944bf03f74 1416 Contents-i386 2c879fcb3d471450d31020e6bd0492269299eb11 695 Contents-s390x.gz 2c879fcb3d471450d31020e6bd0492269299eb11 695 Contents-i386.gz da39a3ee5e6b4b0d3255bfef95601890afd80709 0 main/binary-amd64/Packages 97b962297a1c704ddb43fa8469aa3bd298ffa925 103 main/binary-amd64/Release e3f4c61a216c2c9613cd3bdd1420dde095b296b3 40 main/binary-amd64/Packages.gz 8f7b953c891feb6c6ff36727888b9fae846b49d5 64 main/binary-amd64/Packages.xz c7a84a86970ceda96c6e5959830f8527d875e603 103 main/binary-arm64/Release da39a3ee5e6b4b0d3255bfef95601890afd80709 0 main/binary-arm64/Packages 8f7b953c891feb6c6ff36727888b9fae846b49d5 64 main/binary-arm64/Packages.xz e3f4c61a216c2c9613cd3bdd1420dde095b296b3 40 main/binary-arm64/Packages.gz e5ac4cfc6fbfc2af212737c9794e37b945c33f6d 103 main/binary-armhf/Release da39a3ee5e6b4b0d3255bfef95601890afd80709 0 main/binary-armhf/Packages 8f7b953c891feb6c6ff36727888b9fae846b49d5 64 main/binary-armhf/Packages.xz e3f4c61a216c2c9613cd3bdd1420dde095b296b3 40 main/binary-armhf/Packages.gz 8f7b953c891feb6c6ff36727888b9fae846b49d5 64 main/binary-i386/Packages.xz d3960ddc2228aacb5ba8277326d0e4b44d1b5a0b 102 main/binary-i386/Release e3f4c61a216c2c9613cd3bdd1420dde095b296b3 40 main/binary-i386/Packages.gz da39a3ee5e6b4b0d3255bfef95601890afd80709 0 main/binary-i386/Packages 8f7b953c891feb6c6ff36727888b9fae846b49d5 64 main/binary-ppc64el/Packages.xz 0dbc5f367ec4dd46d5ec2cb2880da6133d9343a7 105 main/binary-ppc64el/Release e3f4c61a216c2c9613cd3bdd1420dde095b296b3 40 main/binary-ppc64el/Packages.gz da39a3ee5e6b4b0d3255bfef95601890afd80709 0 main/binary-ppc64el/Packages c861357b9d9309ac48ad8aeac2d13e6d35ccf2e3 103 main/binary-s390x/Release e3f4c61a216c2c9613cd3bdd1420dde095b296b3 40 main/binary-s390x/Packages.gz da39a3ee5e6b4b0d3255bfef95601890afd80709 0 main/binary-s390x/Packages 8f7b953c891feb6c6ff36727888b9fae846b49d5 64 main/binary-s390x/Packages.xz e3f4c61a216c2c9613cd3bdd1420dde095b296b3 40 main/debian-installer/binary-amd64/Packages.gz da39a3ee5e6b4b0d3255bfef95601890afd80709 0 main/debian-installer/binary-amd64/Packages 8f7b953c891feb6c6ff36727888b9fae846b49d5 64 main/debian-installer/binary-amd64/Packages.xz da39a3ee5e6b4b0d3255bfef95601890afd80709 0 main/debian-installer/binary-arm64/Packages e3f4c61a216c2c9613cd3bdd1420dde095b296b3 40 main/debian-installer/binary-arm64/Packages.gz 8f7b953c891feb6c6ff36727888b9fae846b49d5 64 main/debian-installer/binary-arm64/Packages.xz e3f4c61a216c2c9613cd3bdd1420dde095b296b3 40 main/debian-installer/binary-armhf/Packages.gz 8f7b953c891feb6c6ff36727888b9fae846b49d5 64 main/debian-installer/binary-armhf/Packages.xz da39a3ee5e6b4b0d3255bfef95601890afd80709 0 main/debian-installer/binary-armhf/Packages da39a3ee5e6b4b0d3255bfef95601890afd80709 0 main/debian-installer/binary-i386/Packages e3f4c61a216c2c9613cd3bdd1420dde095b296b3 40 main/debian-installer/binary-i386/Packages.gz 8f7b953c891feb6c6ff36727888b9fae846b49d5 64 main/debian-installer/binary-i386/Packages.xz 8f7b953c891feb6c6ff36727888b9fae846b49d5 64 main/debian-installer/binary-ppc64el/Packages.xz e3f4c61a216c2c9613cd3bdd1420dde095b296b3 40 main/debian-installer/binary-ppc64el/Packages.gz da39a3ee5e6b4b0d3255bfef95601890afd80709 0 main/debian-installer/binary-ppc64el/Packages 8f7b953c891feb6c6ff36727888b9fae846b49d5 64 main/debian-installer/binary-s390x/Packages.xz e3f4c61a216c2c9613cd3bdd1420dde095b296b3 40 main/debian-installer/binary-s390x/Packages.gz da39a3ee5e6b4b0d3255bfef95601890afd80709 0 main/debian-installer/binary-s390x/Packages 851e39e03cba132064f1c7ac4c827e9cfcad64d7 156 main/dep11/Components-armhf.yml.gz 6526b598d89fe2e26836de6625f8ee382ea87a54 208 main/dep11/Components-s390x.yml.xz 60cacbf3d72e1e7834203da608037b1bf83b40e8 1024 main/dep11/icons-128x128.tar c50afaeb71543fbcb23861d5357ca7ba1a439a3b 208 main/dep11/Components-powerpc.yml.xz 3dae71dff151c46a798f321616948fa5932198eb 208 main/dep11/Components-arm64.yml.xz aa27f39a547726f9f7afd043711756ae57a5feb8 29 main/dep11/icons-128x128.tar.gz 40b0029afdc841da2de66cae7505be157f3e803c 156 main/dep11/Components-s390x.yml.gz fe74f4ee4ec928cde2584917f02191f517666d6f 164 main/dep11/Components-arm64.yml 55b6e603ef4c5f1182b7c5196cdf03737a86fb50 164 main/dep11/Components-armhf.yml 495bd43a765bbacc0174a26c6d0c463ce4e42b19 164 main/dep11/Components-s390x.yml ad7d58397b2587baf5bc001ecf8d8aeac32809fa 208 main/dep11/Components-amd64.yml.xz 84ba2de12648a7ae7f12a5e9c43398f6b630dc56 208 main/dep11/Components-ppc64el.yml.xz 12f0a0f68da7749819cdfa9405a682fa7873d069 156 main/dep11/Components-arm64.yml.gz 82fd659cf263d08aa9b849ced214acdaaacf6734 164 main/dep11/Components-i386.yml 40e91fab8024aa563c4a9da90fe262a8cf0f21ee 208 main/dep11/Components-i386.yml.xz 0cb327bc78cbfc4aa35731fcf2933115186aee01 164 main/dep11/Components-ppc64el.yml 60cacbf3d72e1e7834203da608037b1bf83b40e8 1024 main/dep11/icons-64x64.tar 8f9415e27b7a4d064e12b9b7a52e703b04de0702 156 main/dep11/Components-powerpc.yml.gz 1c28d36f1f568704557a70d0df3b427bfb3b16eb 164 main/dep11/Components-powerpc.yml b95d0a623fd2d14dde459559cd9956433d14f52a 164 main/dep11/Components-amd64.yml 868a0f58783d47b85f4fd42607279c5452764e7b 156 main/dep11/Components-ppc64el.yml.gz aa27f39a547726f9f7afd043711756ae57a5feb8 29 main/dep11/icons-64x64.tar.gz 864d323276d1f222ae6315472da789722fccd173 156 main/dep11/Components-i386.yml.gz 5fe792f1a67342e803f3fc5c836fe0487b16d78d 156 main/dep11/Components-amd64.yml.gz 76e6a48f55eb8f919065bff9be0163dae74a46a3 208 main/dep11/Components-armhf.yml.xz e3f4c61a216c2c9613cd3bdd1420dde095b296b3 40 main/i18n/Translation-en.gz 8f7b953c891feb6c6ff36727888b9fae846b49d5 64 main/i18n/Translation-en.xz da39a3ee5e6b4b0d3255bfef95601890afd80709 0 main/i18n/Translation-en 1d57840eb63860f364ad5d68178069658a9dba9a 192 main/i18n/Index b1940df5056c7e046e0ec0df4bd3d1ee2c922792 104 main/source/Release da39a3ee5e6b4b0d3255bfef95601890afd80709 0 main/source/Sources e3f4c61a216c2c9613cd3bdd1420dde095b296b3 40 main/source/Sources.gz 8f7b953c891feb6c6ff36727888b9fae846b49d5 64 main/source/Sources.xz e3f4c61a216c2c9613cd3bdd1420dde095b296b3 40 multiverse/binary-amd64/Packages.gz 8f7b953c891feb6c6ff36727888b9fae846b49d5 64 multiverse/binary-amd64/Packages.xz 0395274e28c295a08f6c59c82bc96e852bfd4d99 109 multiverse/binary-amd64/Release da39a3ee5e6b4b0d3255bfef95601890afd80709 0 multiverse/binary-amd64/Packages e3f4c61a216c2c9613cd3bdd1420dde095b296b3 40 multiverse/binary-arm64/Packages.gz da39a3ee5e6b4b0d3255bfef95601890afd80709 0 multiverse/binary-arm64/Packages 2e7039b84ad8c9b22ae9709e8ba99a46c2f363d6 109 multiverse/binary-arm64/Release 8f7b953c891feb6c6ff36727888b9fae846b49d5 64 multiverse/binary-arm64/Packages.xz e3e468b5ccc12d3debd7e31bbddac72a80b06834 109 multiverse/binary-armhf/Release e3f4c61a216c2c9613cd3bdd1420dde095b296b3 40 multiverse/binary-armhf/Packages.gz 8f7b953c891feb6c6ff36727888b9fae846b49d5 64 multiverse/binary-armhf/Packages.xz da39a3ee5e6b4b0d3255bfef95601890afd80709 0 multiverse/binary-armhf/Packages e3f4c61a216c2c9613cd3bdd1420dde095b296b3 40 multiverse/binary-i386/Packages.gz da39a3ee5e6b4b0d3255bfef95601890afd80709 0 multiverse/binary-i386/Packages 8f7b953c891feb6c6ff36727888b9fae846b49d5 64 multiverse/binary-i386/Packages.xz cedd151bc0cd42113b0b1773d28f6ccb829567b2 108 multiverse/binary-i386/Release da39a3ee5e6b4b0d3255bfef95601890afd80709 0 multiverse/binary-ppc64el/Packages e3f4c61a216c2c9613cd3bdd1420dde095b296b3 40 multiverse/binary-ppc64el/Packages.gz 9c8f085d9b63255bd3f6f0fe2e07aca328cebcb3 111 multiverse/binary-ppc64el/Release 8f7b953c891feb6c6ff36727888b9fae846b49d5 64 multiverse/binary-ppc64el/Packages.xz e74aca93ccccdebbb04fc7e62de6335dfc6b66a4 109 multiverse/binary-s390x/Release da39a3ee5e6b4b0d3255bfef95601890afd80709 0 multiverse/binary-s390x/Packages e3f4c61a216c2c9613cd3bdd1420dde095b296b3 40 multiverse/binary-s390x/Packages.gz 8f7b953c891feb6c6ff36727888b9fae846b49d5 64 multiverse/binary-s390x/Packages.xz 8f7b953c891feb6c6ff36727888b9fae846b49d5 64 multiverse/debian-installer/binary-amd64/Packages.xz da39a3ee5e6b4b0d3255bfef95601890afd80709 0 multiverse/debian-installer/binary-amd64/Packages e3f4c61a216c2c9613cd3bdd1420dde095b296b3 40 multiverse/debian-installer/binary-amd64/Packages.gz da39a3ee5e6b4b0d3255bfef95601890afd80709 0 multiverse/debian-installer/binary-arm64/Packages 8f7b953c891feb6c6ff36727888b9fae846b49d5 64 multiverse/debian-installer/binary-arm64/Packages.xz e3f4c61a216c2c9613cd3bdd1420dde095b296b3 40 multiverse/debian-installer/binary-arm64/Packages.gz da39a3ee5e6b4b0d3255bfef95601890afd80709 0 multiverse/debian-installer/binary-armhf/Packages e3f4c61a216c2c9613cd3bdd1420dde095b296b3 40 multiverse/debian-installer/binary-armhf/Packages.gz 8f7b953c891feb6c6ff36727888b9fae846b49d5 64 multiverse/debian-installer/binary-armhf/Packages.xz 8f7b953c891feb6c6ff36727888b9fae846b49d5 64 multiverse/debian-installer/binary-i386/Packages.xz e3f4c61a216c2c9613cd3bdd1420dde095b296b3 40 multiverse/debian-installer/binary-i386/Packages.gz da39a3ee5e6b4b0d3255bfef95601890afd80709 0 multiverse/debian-installer/binary-i386/Packages da39a3ee5e6b4b0d3255bfef95601890afd80709 0 multiverse/debian-installer/binary-ppc64el/Packages e3f4c61a216c2c9613cd3bdd1420dde095b296b3 40 multiverse/debian-installer/binary-ppc64el/Packages.gz 8f7b953c891feb6c6ff36727888b9fae846b49d5 64 multiverse/debian-installer/binary-ppc64el/Packages.xz da39a3ee5e6b4b0d3255bfef95601890afd80709 0 multiverse/debian-installer/binary-s390x/Packages e3f4c61a216c2c9613cd3bdd1420dde095b296b3 40 multiverse/debian-installer/binary-s390x/Packages.gz 8f7b953c891feb6c6ff36727888b9fae846b49d5 64 multiverse/debian-installer/binary-s390x/Packages.xz 3de326aa7ea62a2523453a589b322bcb1e34b01d 160 multiverse/dep11/Components-armhf.yml.gz aa27f39a547726f9f7afd043711756ae57a5feb8 29 multiverse/dep11/icons-64x64.tar.gz 4285a05208fa2d2433decb5d549b40b0d08f1e74 212 multiverse/dep11/Components-armhf.yml.xz cee7686e6ad23b7fe556f36f88c7f58b81ef7e0c 212 multiverse/dep11/Components-ppc64el.yml.xz b0d41b27771b3973d74e664ac6acbee73e513387 170 multiverse/dep11/Components-i386.yml c44a50a22787bfa48887bd1516276fe8fec693f7 160 multiverse/dep11/Components-s390x.yml.gz aa27f39a547726f9f7afd043711756ae57a5feb8 29 multiverse/dep11/icons-128x128.tar.gz 88115316f9bb8918c4584f7a5a063afb50f2b3f7 212 multiverse/dep11/Components-amd64.yml.xz 5c1c3c3239ae2435d0f07aa53eec231902b388c8 170 multiverse/dep11/Components-armhf.yml e9d007b178b219bdce92e13d797ed3341add07dc 170 multiverse/dep11/Components-arm64.yml 9b3fbd26ce21b03e662984f478705832482193b9 212 multiverse/dep11/Components-powerpc.yml.xz d72d2ead29a88f2a218e9792ed1535fef7f842b3 160 multiverse/dep11/Components-arm64.yml.gz 6f53a81935b3722fdc70a7b76c5e38bccf04e4f6 212 multiverse/dep11/Components-s390x.yml.xz c38c6de26cc3c0189bd0cfdef0fee8fd1472e59c 160 multiverse/dep11/Components-i386.yml.gz 936447cc3ebe3f615fa769bc7ccb37fe0eac4e84 159 multiverse/dep11/Components-ppc64el.yml.gz 9ce577d6486bf000046b4d4c10d3dd5f73a03877 160 multiverse/dep11/Components-amd64.yml.gz 60cacbf3d72e1e7834203da608037b1bf83b40e8 1024 multiverse/dep11/icons-128x128.tar a655b589ccf10105c30c116ca53764af0a620122 212 multiverse/dep11/Components-i386.yml.xz f3c952352503be295abf0b1f9ed83d1b37848682 159 multiverse/dep11/Components-powerpc.yml.gz 60cacbf3d72e1e7834203da608037b1bf83b40e8 1024 multiverse/dep11/icons-64x64.tar 84039d0d2dd427e49c76d3cb163829b258dfc456 170 multiverse/dep11/Components-ppc64el.yml b39c02408165fb854f68c3e5ed90abeba9f2deb2 170 multiverse/dep11/Components-amd64.yml 71854ad0213720145be375d9509f8993a7b4c32b 212 multiverse/dep11/Components-arm64.yml.xz ba622fb794f009501740ba2009a2a80b984745ae 170 multiverse/dep11/Components-s390x.yml 972b9d5726c0ce4ed0ebe88b73cc5ad4b839ceb4 170 multiverse/dep11/Components-powerpc.yml e3f4c61a216c2c9613cd3bdd1420dde095b296b3 40 multiverse/i18n/Translation-en.gz da39a3ee5e6b4b0d3255bfef95601890afd80709 0 multiverse/i18n/Translation-en 8f7b953c891feb6c6ff36727888b9fae846b49d5 64 multiverse/i18n/Translation-en.xz 1d57840eb63860f364ad5d68178069658a9dba9a 192 multiverse/i18n/Index e3f4c61a216c2c9613cd3bdd1420dde095b296b3 40 multiverse/source/Sources.gz 8f7b953c891feb6c6ff36727888b9fae846b49d5 64 multiverse/source/Sources.xz da39a3ee5e6b4b0d3255bfef95601890afd80709 0 multiverse/source/Sources e9473be01d1990f2af9eb185573099a3532ec061 110 multiverse/source/Release da39a3ee5e6b4b0d3255bfef95601890afd80709 0 restricted/binary-amd64/Packages e3f4c61a216c2c9613cd3bdd1420dde095b296b3 40 restricted/binary-amd64/Packages.gz 15a8a0d275cbfde6c973274ec32e3e05c0136a21 109 restricted/binary-amd64/Release 8f7b953c891feb6c6ff36727888b9fae846b49d5 64 restricted/binary-amd64/Packages.xz af60ccbdbef4ccd6a291d65583b97f696517f614 109 restricted/binary-arm64/Release 8f7b953c891feb6c6ff36727888b9fae846b49d5 64 restricted/binary-arm64/Packages.xz e3f4c61a216c2c9613cd3bdd1420dde095b296b3 40 restricted/binary-arm64/Packages.gz da39a3ee5e6b4b0d3255bfef95601890afd80709 0 restricted/binary-arm64/Packages e3f4c61a216c2c9613cd3bdd1420dde095b296b3 40 restricted/binary-armhf/Packages.gz 8f7b953c891feb6c6ff36727888b9fae846b49d5 64 restricted/binary-armhf/Packages.xz a5f5ebf2f0230f70cd26f618ccb4579e6e636dcb 109 restricted/binary-armhf/Release da39a3ee5e6b4b0d3255bfef95601890afd80709 0 restricted/binary-armhf/Packages e3f4c61a216c2c9613cd3bdd1420dde095b296b3 40 restricted/binary-i386/Packages.gz 8f7b953c891feb6c6ff36727888b9fae846b49d5 64 restricted/binary-i386/Packages.xz da39a3ee5e6b4b0d3255bfef95601890afd80709 0 restricted/binary-i386/Packages a5d94d0769b7a0cd1272b185d2445528ab5270ff 108 restricted/binary-i386/Release e3f4c61a216c2c9613cd3bdd1420dde095b296b3 40 restricted/binary-ppc64el/Packages.gz da39a3ee5e6b4b0d3255bfef95601890afd80709 0 restricted/binary-ppc64el/Packages 8f7b953c891feb6c6ff36727888b9fae846b49d5 64 restricted/binary-ppc64el/Packages.xz 9b21107e19fbe9a281b31b99461c829274b651c0 111 restricted/binary-ppc64el/Release e3f4c61a216c2c9613cd3bdd1420dde095b296b3 40 restricted/binary-s390x/Packages.gz 350dfa82ce988377c0865a6db30fdd295465e680 109 restricted/binary-s390x/Release da39a3ee5e6b4b0d3255bfef95601890afd80709 0 restricted/binary-s390x/Packages 8f7b953c891feb6c6ff36727888b9fae846b49d5 64 restricted/binary-s390x/Packages.xz 8f7b953c891feb6c6ff36727888b9fae846b49d5 64 restricted/debian-installer/binary-amd64/Packages.xz da39a3ee5e6b4b0d3255bfef95601890afd80709 0 restricted/debian-installer/binary-amd64/Packages e3f4c61a216c2c9613cd3bdd1420dde095b296b3 40 restricted/debian-installer/binary-amd64/Packages.gz e3f4c61a216c2c9613cd3bdd1420dde095b296b3 40 restricted/debian-installer/binary-arm64/Packages.gz 8f7b953c891feb6c6ff36727888b9fae846b49d5 64 restricted/debian-installer/binary-arm64/Packages.xz da39a3ee5e6b4b0d3255bfef95601890afd80709 0 restricted/debian-installer/binary-arm64/Packages e3f4c61a216c2c9613cd3bdd1420dde095b296b3 40 restricted/debian-installer/binary-armhf/Packages.gz da39a3ee5e6b4b0d3255bfef95601890afd80709 0 restricted/debian-installer/binary-armhf/Packages 8f7b953c891feb6c6ff36727888b9fae846b49d5 64 restricted/debian-installer/binary-armhf/Packages.xz 8f7b953c891feb6c6ff36727888b9fae846b49d5 64 restricted/debian-installer/binary-i386/Packages.xz da39a3ee5e6b4b0d3255bfef95601890afd80709 0 restricted/debian-installer/binary-i386/Packages e3f4c61a216c2c9613cd3bdd1420dde095b296b3 40 restricted/debian-installer/binary-i386/Packages.gz 8f7b953c891feb6c6ff36727888b9fae846b49d5 64 restricted/debian-installer/binary-ppc64el/Packages.xz da39a3ee5e6b4b0d3255bfef95601890afd80709 0 restricted/debian-installer/binary-ppc64el/Packages e3f4c61a216c2c9613cd3bdd1420dde095b296b3 40 restricted/debian-installer/binary-ppc64el/Packages.gz 8f7b953c891feb6c6ff36727888b9fae846b49d5 64 restricted/debian-installer/binary-s390x/Packages.xz e3f4c61a216c2c9613cd3bdd1420dde095b296b3 40 restricted/debian-installer/binary-s390x/Packages.gz da39a3ee5e6b4b0d3255bfef95601890afd80709 0 restricted/debian-installer/binary-s390x/Packages 004f1a8144d7f023b397a2b72a7e6147869be784 185 restricted/dep11/Components-arm64.yml 89e557923babea0ac51f908188aaa0dc6edf1248 193 restricted/dep11/Components-amd64.yml.gz 004f1a8144d7f023b397a2b72a7e6147869be784 185 restricted/dep11/Components-armhf.yml 47e2dd1d3008a66db6e91468d614487490d2ee92 195 restricted/dep11/Components-ppc64el.yml.gz 0540459419464e979cec66ba3e1d70d049e46c23 193 restricted/dep11/Components-s390x.yml.gz 004f1a8144d7f023b397a2b72a7e6147869be784 185 restricted/dep11/Components-s390x.yml 004f1a8144d7f023b397a2b72a7e6147869be784 185 restricted/dep11/Components-amd64.yml 004f1a8144d7f023b397a2b72a7e6147869be784 185 restricted/dep11/Components-ppc64el.yml 10956f4af4282fee33da9a60ff03d84074a01494 192 restricted/dep11/Components-i386.yml.gz 48905f8e17b8dc6e365843223acf8211394c3ea5 193 restricted/dep11/Components-arm64.yml.gz 36f81c016a2c54cff565775e2f42ae43388c7ed4 193 restricted/dep11/Components-armhf.yml.gz 004f1a8144d7f023b397a2b72a7e6147869be784 185 restricted/dep11/Components-i386.yml 1d57840eb63860f364ad5d68178069658a9dba9a 192 restricted/i18n/Index da39a3ee5e6b4b0d3255bfef95601890afd80709 0 restricted/i18n/Translation-en e3f4c61a216c2c9613cd3bdd1420dde095b296b3 40 restricted/i18n/Translation-en.gz 8f7b953c891feb6c6ff36727888b9fae846b49d5 64 restricted/i18n/Translation-en.xz a5d86c27bbc13ad17f7a55373e858ee4e971f297 110 restricted/source/Release da39a3ee5e6b4b0d3255bfef95601890afd80709 0 restricted/source/Sources e3f4c61a216c2c9613cd3bdd1420dde095b296b3 40 restricted/source/Sources.gz 8f7b953c891feb6c6ff36727888b9fae846b49d5 64 restricted/source/Sources.xz da39a3ee5e6b4b0d3255bfef95601890afd80709 0 universe/binary-amd64/Packages 8f7b953c891feb6c6ff36727888b9fae846b49d5 64 universe/binary-amd64/Packages.xz 7a0d07a062cdf9764f1e1a9ab6bbe15dbb1804da 107 universe/binary-amd64/Release e3f4c61a216c2c9613cd3bdd1420dde095b296b3 40 universe/binary-amd64/Packages.gz 8f7b953c891feb6c6ff36727888b9fae846b49d5 64 universe/binary-arm64/Packages.xz e3f4c61a216c2c9613cd3bdd1420dde095b296b3 40 universe/binary-arm64/Packages.gz da5f7f652e146bee1d645bf077dc6a716cc3c154 107 universe/binary-arm64/Release da39a3ee5e6b4b0d3255bfef95601890afd80709 0 universe/binary-arm64/Packages e3f4c61a216c2c9613cd3bdd1420dde095b296b3 40 universe/binary-armhf/Packages.gz 283560a8dc23772f101a1cd64faa5837ce04e725 107 universe/binary-armhf/Release 8f7b953c891feb6c6ff36727888b9fae846b49d5 64 universe/binary-armhf/Packages.xz da39a3ee5e6b4b0d3255bfef95601890afd80709 0 universe/binary-armhf/Packages 8a7a5edcf4f568320951c8406932ca2a2c2bef51 106 universe/binary-i386/Release da39a3ee5e6b4b0d3255bfef95601890afd80709 0 universe/binary-i386/Packages 8f7b953c891feb6c6ff36727888b9fae846b49d5 64 universe/binary-i386/Packages.xz e3f4c61a216c2c9613cd3bdd1420dde095b296b3 40 universe/binary-i386/Packages.gz e3f4c61a216c2c9613cd3bdd1420dde095b296b3 40 universe/binary-ppc64el/Packages.gz 8f7b953c891feb6c6ff36727888b9fae846b49d5 64 universe/binary-ppc64el/Packages.xz 54151b6578703386a9de0c6900b652cc18aca34c 109 universe/binary-ppc64el/Release da39a3ee5e6b4b0d3255bfef95601890afd80709 0 universe/binary-ppc64el/Packages da39a3ee5e6b4b0d3255bfef95601890afd80709 0 universe/binary-s390x/Packages 51889f02ee18fef351b6476a898334d4c1dc1a23 107 universe/binary-s390x/Release e3f4c61a216c2c9613cd3bdd1420dde095b296b3 40 universe/binary-s390x/Packages.gz 8f7b953c891feb6c6ff36727888b9fae846b49d5 64 universe/binary-s390x/Packages.xz da39a3ee5e6b4b0d3255bfef95601890afd80709 0 universe/debian-installer/binary-amd64/Packages e3f4c61a216c2c9613cd3bdd1420dde095b296b3 40 universe/debian-installer/binary-amd64/Packages.gz 8f7b953c891feb6c6ff36727888b9fae846b49d5 64 universe/debian-installer/binary-amd64/Packages.xz 8f7b953c891feb6c6ff36727888b9fae846b49d5 64 universe/debian-installer/binary-arm64/Packages.xz e3f4c61a216c2c9613cd3bdd1420dde095b296b3 40 universe/debian-installer/binary-arm64/Packages.gz da39a3ee5e6b4b0d3255bfef95601890afd80709 0 universe/debian-installer/binary-arm64/Packages da39a3ee5e6b4b0d3255bfef95601890afd80709 0 universe/debian-installer/binary-armhf/Packages e3f4c61a216c2c9613cd3bdd1420dde095b296b3 40 universe/debian-installer/binary-armhf/Packages.gz 8f7b953c891feb6c6ff36727888b9fae846b49d5 64 universe/debian-installer/binary-armhf/Packages.xz 8f7b953c891feb6c6ff36727888b9fae846b49d5 64 universe/debian-installer/binary-i386/Packages.xz e3f4c61a216c2c9613cd3bdd1420dde095b296b3 40 universe/debian-installer/binary-i386/Packages.gz da39a3ee5e6b4b0d3255bfef95601890afd80709 0 universe/debian-installer/binary-i386/Packages da39a3ee5e6b4b0d3255bfef95601890afd80709 0 universe/debian-installer/binary-ppc64el/Packages e3f4c61a216c2c9613cd3bdd1420dde095b296b3 40 universe/debian-installer/binary-ppc64el/Packages.gz 8f7b953c891feb6c6ff36727888b9fae846b49d5 64 universe/debian-installer/binary-ppc64el/Packages.xz e3f4c61a216c2c9613cd3bdd1420dde095b296b3 40 universe/debian-installer/binary-s390x/Packages.gz 8f7b953c891feb6c6ff36727888b9fae846b49d5 64 universe/debian-installer/binary-s390x/Packages.xz da39a3ee5e6b4b0d3255bfef95601890afd80709 0 universe/debian-installer/binary-s390x/Packages 60cacbf3d72e1e7834203da608037b1bf83b40e8 1024 universe/dep11/icons-128x128.tar b214a2e85379c1a105172099eee0254b26766d46 212 universe/dep11/Components-ppc64el.yml.xz 4da550de259edd904f005199a8514ca4e8e5b4d8 212 universe/dep11/Components-s390x.yml.xz 396152d200cb60b6c12f5085c88d26d72af5465a 212 universe/dep11/Components-armhf.yml.xz aa27f39a547726f9f7afd043711756ae57a5feb8 29 universe/dep11/icons-64x64.tar.gz 48092c4ee3640754d5b117be3c7b75ffaed9e6c6 168 universe/dep11/Components-i386.yml 445f551cb26fdce4591c6c459c77c4b6b197787f 159 universe/dep11/Components-armhf.yml.gz caeaf925808ba7a988924fba9c5fd2b164b90bb0 158 universe/dep11/Components-ppc64el.yml.gz aa27f39a547726f9f7afd043711756ae57a5feb8 29 universe/dep11/icons-128x128.tar.gz 8319f16486df49af5736d82f035ff22f869dd319 159 universe/dep11/Components-powerpc.yml.gz 2dcd43838b14e47f56f90074cecd3fd62108d9fc 168 universe/dep11/Components-powerpc.yml 1d095238d3caff573725701351817a8e9c75ddfa 159 universe/dep11/Components-s390x.yml.gz c02daf3eb32c48a09d5e2ae4bbf12350b1aa735a 212 universe/dep11/Components-amd64.yml.xz 93b2f2999a12e0a3dc43712ac7bb16c2bd75c43a 159 universe/dep11/Components-i386.yml.gz aa27a20d8d6e0e690504dc873c58b8db7e8ce646 168 universe/dep11/Components-arm64.yml 7c55a0a485ad19edeab3e0ed75437791e4a8eb08 212 universe/dep11/Components-powerpc.yml.xz cec876cd4c49c4cff2a693cd08a35190799000d6 212 universe/dep11/Components-arm64.yml.xz 9b68a322dbc4b4f17f77372ec46287763dffa92a 159 universe/dep11/Components-arm64.yml.gz 126e4ca28937aa03990bd9ba6ba1cba96c2c51b4 168 universe/dep11/Components-s390x.yml 60cacbf3d72e1e7834203da608037b1bf83b40e8 1024 universe/dep11/icons-64x64.tar ecebb99f2c5fbbb7fac710e7e1e64fb13b34c150 168 universe/dep11/Components-ppc64el.yml 758c8bffdf82ca3ec8dd21f03e66de2cfe8594f3 212 universe/dep11/Components-i386.yml.xz 9ac0ce60079b401997353a52da6f8837dc650ef0 168 universe/dep11/Components-armhf.yml 60840408aa292209e3d0436991341d950fac846e 159 universe/dep11/Components-amd64.yml.gz 2f0ccbc919e0dcbf22f49ecf05128d1eb8be0240 168 universe/dep11/Components-amd64.yml da39a3ee5e6b4b0d3255bfef95601890afd80709 0 universe/i18n/Translation-en e3f4c61a216c2c9613cd3bdd1420dde095b296b3 40 universe/i18n/Translation-en.gz 1d57840eb63860f364ad5d68178069658a9dba9a 192 universe/i18n/Index 8f7b953c891feb6c6ff36727888b9fae846b49d5 64 universe/i18n/Translation-en.xz da39a3ee5e6b4b0d3255bfef95601890afd80709 0 universe/source/Sources 8f7b953c891feb6c6ff36727888b9fae846b49d5 64 universe/source/Sources.xz e3f4c61a216c2c9613cd3bdd1420dde095b296b3 40 universe/source/Sources.gz 2b7a2e5b55652139efcc80961de977bb9adc53da 108 universe/source/Release SHA256: f4b4696e464d8e50409edb1eae347deb38c10d58486391e9dd374a03081bf6a3 695 Contents-armhf.gz 170d8d14eca6da1611be301c1489325b06f9f7179a2833456e06aa0469a58271 1416 Contents-armhf 170d8d14eca6da1611be301c1489325b06f9f7179a2833456e06aa0469a58271 1416 Contents-ppc64el f4b4696e464d8e50409edb1eae347deb38c10d58486391e9dd374a03081bf6a3 695 Contents-ppc64el.gz 170d8d14eca6da1611be301c1489325b06f9f7179a2833456e06aa0469a58271 1416 Contents-arm64 170d8d14eca6da1611be301c1489325b06f9f7179a2833456e06aa0469a58271 1416 Contents-amd64 f4b4696e464d8e50409edb1eae347deb38c10d58486391e9dd374a03081bf6a3 695 Contents-arm64.gz f4b4696e464d8e50409edb1eae347deb38c10d58486391e9dd374a03081bf6a3 695 Contents-amd64.gz 170d8d14eca6da1611be301c1489325b06f9f7179a2833456e06aa0469a58271 1416 Contents-s390x 170d8d14eca6da1611be301c1489325b06f9f7179a2833456e06aa0469a58271 1416 Contents-i386 f4b4696e464d8e50409edb1eae347deb38c10d58486391e9dd374a03081bf6a3 695 Contents-s390x.gz f4b4696e464d8e50409edb1eae347deb38c10d58486391e9dd374a03081bf6a3 695 Contents-i386.gz e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 main/binary-amd64/Packages c0dd76328e5100f3103b3209c1634ecb4eb942d28aa2c3304b0c48415b061a44 103 main/binary-amd64/Release e7ab72b8f37c7c9c9f6386fb8e3dfa40bf6fe4b67876703c5927e47cb8664ce4 40 main/binary-amd64/Packages.gz 65de7e01eff8cf2f9a287153971467b1c11811213b64a38bdf673bad240436ff 64 main/binary-amd64/Packages.xz 7a9810d25ba3c9eb6fa071535d9c8732683afb41877268a7a41148fc6212be1d 103 main/binary-arm64/Release e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 main/binary-arm64/Packages 65de7e01eff8cf2f9a287153971467b1c11811213b64a38bdf673bad240436ff 64 main/binary-arm64/Packages.xz e7ab72b8f37c7c9c9f6386fb8e3dfa40bf6fe4b67876703c5927e47cb8664ce4 40 main/binary-arm64/Packages.gz fdea71a135849604866e73d95ac5364eedd6e057c15cc92d2ff03922fb2a23a2 103 main/binary-armhf/Release e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 main/binary-armhf/Packages 65de7e01eff8cf2f9a287153971467b1c11811213b64a38bdf673bad240436ff 64 main/binary-armhf/Packages.xz e7ab72b8f37c7c9c9f6386fb8e3dfa40bf6fe4b67876703c5927e47cb8664ce4 40 main/binary-armhf/Packages.gz 65de7e01eff8cf2f9a287153971467b1c11811213b64a38bdf673bad240436ff 64 main/binary-i386/Packages.xz 076aa6c2f01f279711ebc8d983b6370a734e09f0b4465622cc21323dc61258e7 102 main/binary-i386/Release e7ab72b8f37c7c9c9f6386fb8e3dfa40bf6fe4b67876703c5927e47cb8664ce4 40 main/binary-i386/Packages.gz e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 main/binary-i386/Packages 65de7e01eff8cf2f9a287153971467b1c11811213b64a38bdf673bad240436ff 64 main/binary-ppc64el/Packages.xz dfc991824a783be2424c45030e4d41d352d7955b61c3de9a921252830f180e2d 105 main/binary-ppc64el/Release e7ab72b8f37c7c9c9f6386fb8e3dfa40bf6fe4b67876703c5927e47cb8664ce4 40 main/binary-ppc64el/Packages.gz e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 main/binary-ppc64el/Packages f493d8f3660c85f80a301fd39e202d8a625448c4b7708a219a1ec719eebf0c9e 103 main/binary-s390x/Release e7ab72b8f37c7c9c9f6386fb8e3dfa40bf6fe4b67876703c5927e47cb8664ce4 40 main/binary-s390x/Packages.gz e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 main/binary-s390x/Packages 65de7e01eff8cf2f9a287153971467b1c11811213b64a38bdf673bad240436ff 64 main/binary-s390x/Packages.xz e7ab72b8f37c7c9c9f6386fb8e3dfa40bf6fe4b67876703c5927e47cb8664ce4 40 main/debian-installer/binary-amd64/Packages.gz e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 main/debian-installer/binary-amd64/Packages 65de7e01eff8cf2f9a287153971467b1c11811213b64a38bdf673bad240436ff 64 main/debian-installer/binary-amd64/Packages.xz e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 main/debian-installer/binary-arm64/Packages e7ab72b8f37c7c9c9f6386fb8e3dfa40bf6fe4b67876703c5927e47cb8664ce4 40 main/debian-installer/binary-arm64/Packages.gz 65de7e01eff8cf2f9a287153971467b1c11811213b64a38bdf673bad240436ff 64 main/debian-installer/binary-arm64/Packages.xz e7ab72b8f37c7c9c9f6386fb8e3dfa40bf6fe4b67876703c5927e47cb8664ce4 40 main/debian-installer/binary-armhf/Packages.gz 65de7e01eff8cf2f9a287153971467b1c11811213b64a38bdf673bad240436ff 64 main/debian-installer/binary-armhf/Packages.xz e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 main/debian-installer/binary-armhf/Packages e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 main/debian-installer/binary-i386/Packages e7ab72b8f37c7c9c9f6386fb8e3dfa40bf6fe4b67876703c5927e47cb8664ce4 40 main/debian-installer/binary-i386/Packages.gz 65de7e01eff8cf2f9a287153971467b1c11811213b64a38bdf673bad240436ff 64 main/debian-installer/binary-i386/Packages.xz 65de7e01eff8cf2f9a287153971467b1c11811213b64a38bdf673bad240436ff 64 main/debian-installer/binary-ppc64el/Packages.xz e7ab72b8f37c7c9c9f6386fb8e3dfa40bf6fe4b67876703c5927e47cb8664ce4 40 main/debian-installer/binary-ppc64el/Packages.gz e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 main/debian-installer/binary-ppc64el/Packages 65de7e01eff8cf2f9a287153971467b1c11811213b64a38bdf673bad240436ff 64 main/debian-installer/binary-s390x/Packages.xz e7ab72b8f37c7c9c9f6386fb8e3dfa40bf6fe4b67876703c5927e47cb8664ce4 40 main/debian-installer/binary-s390x/Packages.gz e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 main/debian-installer/binary-s390x/Packages afa853bff55edf90a800dad9924685edace36c60246f80da48355921a7004dda 156 main/dep11/Components-armhf.yml.gz 86660295b572919f5973ac2f2a05ef289de6490bfb00ac3e041994e8156f746e 208 main/dep11/Components-s390x.yml.xz 5f70bf18a086007016e948b04aed3b82103a36bea41755b6cddfaf10ace3c6ef 1024 main/dep11/icons-128x128.tar dab0fe1a29f35a998297732d2d0aadafc5d803f8697eb6bdb2c60968964687d7 208 main/dep11/Components-powerpc.yml.xz 6fa5c059763c0f58ca740ce124191bbc1f34caa88e5e772598c7e7d638970a52 208 main/dep11/Components-arm64.yml.xz 7989bb311baa38ef545250282aa065d23281c46dfb8faabe4c653487bdbded5c 29 main/dep11/icons-128x128.tar.gz ae46664deaf6ea0a58f268fbddf5fbb256061c82657e9c90d87a1ed7b91c687a 156 main/dep11/Components-s390x.yml.gz 0ecd16061fb6fe59a95941338b7127a4c43fc392938048b4581e4bbda92852c4 164 main/dep11/Components-arm64.yml 9e97dbbd19ad7cbbc8c3eb4e6caeed206219ff23e1592426598839a067fe4d1e 164 main/dep11/Components-armhf.yml d01f311bbd135187844c1ad17a626a9a1a6e4b39d4f28e5df2f8f0096d5bfb81 164 main/dep11/Components-s390x.yml d2c7adad253ffc01f3edb52830c60fc4c0c9807ca8cc1789fa5bafbe3018a82e 208 main/dep11/Components-amd64.yml.xz ace5bf3cff4bab9646fbf78970164ca725a0d9694f6a1ebe0801d886d9d68084 208 main/dep11/Components-ppc64el.yml.xz e0aa2212f8c4e1bb4336502d63d92ed17e15c6ef0bede630cdf716140258018e 156 main/dep11/Components-arm64.yml.gz 2dd1f89f391a813bf6c4261ae25c7fb02d784975e57db5ce1be1f91b2887d4d2 164 main/dep11/Components-i386.yml 55eecf7211a6af51490556995566dd8a191a0fa22afdb7cd82ba4e52e7208cf0 208 main/dep11/Components-i386.yml.xz 205e2068d89792aa279519f35ff73ff1842168a0ec4cc27eb476ff468d5d47df 164 main/dep11/Components-ppc64el.yml 5f70bf18a086007016e948b04aed3b82103a36bea41755b6cddfaf10ace3c6ef 1024 main/dep11/icons-64x64.tar a44256d7a35854c019c6fa7ea34c685dc40792190e678fa99e1ce87101619ca7 156 main/dep11/Components-powerpc.yml.gz 7f83334ba24389a0df335cc90ede51b6c1b1cb2de4fa40ee671ddddfdbe04362 164 main/dep11/Components-powerpc.yml ddca6b28eb11b686b652bcbeb155e50610605cf7fdb0cb1d4f6499da9344ac87 164 main/dep11/Components-amd64.yml cd66854bcc7f359ab817d0800d34f784df06afa7b8cc897e99cfd16f62b1f462 156 main/dep11/Components-ppc64el.yml.gz 7989bb311baa38ef545250282aa065d23281c46dfb8faabe4c653487bdbded5c 29 main/dep11/icons-64x64.tar.gz 9ca49dc222807369461b502286611ffd7b9ae3afc991dcc7b18d8e2335ab5a32 156 main/dep11/Components-i386.yml.gz af7d5f3cacee916cd4d27a288803888948e0532d7f5c5fd07090bcb636eb9bed 156 main/dep11/Components-amd64.yml.gz 59126c1dcfeb0ba42f31081a3746c203cdbe615180c5ad91bdbee0ac72a9f9d9 208 main/dep11/Components-armhf.yml.xz e7ab72b8f37c7c9c9f6386fb8e3dfa40bf6fe4b67876703c5927e47cb8664ce4 40 main/i18n/Translation-en.gz 65de7e01eff8cf2f9a287153971467b1c11811213b64a38bdf673bad240436ff 64 main/i18n/Translation-en.xz e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 main/i18n/Translation-en aea852a7918a6c61c359cd57114f60ca95551f449f06cb1c9f296b8326b78101 192 main/i18n/Index 10f3c7f40819ceead0072b084772123cfbb71e34bc9b79bce51d241cbeaabb78 104 main/source/Release e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 main/source/Sources e7ab72b8f37c7c9c9f6386fb8e3dfa40bf6fe4b67876703c5927e47cb8664ce4 40 main/source/Sources.gz 65de7e01eff8cf2f9a287153971467b1c11811213b64a38bdf673bad240436ff 64 main/source/Sources.xz e7ab72b8f37c7c9c9f6386fb8e3dfa40bf6fe4b67876703c5927e47cb8664ce4 40 multiverse/binary-amd64/Packages.gz 65de7e01eff8cf2f9a287153971467b1c11811213b64a38bdf673bad240436ff 64 multiverse/binary-amd64/Packages.xz 70f3e88210ab4c261cb9fadd69d4ab4e6380979461285f37ec543497bbd1738f 109 multiverse/binary-amd64/Release e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 multiverse/binary-amd64/Packages e7ab72b8f37c7c9c9f6386fb8e3dfa40bf6fe4b67876703c5927e47cb8664ce4 40 multiverse/binary-arm64/Packages.gz e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 multiverse/binary-arm64/Packages 86cee4f7b02ceace16dda79b1e1ba1ab17417c17d50040803bc6e3fa621ba7dc 109 multiverse/binary-arm64/Release 65de7e01eff8cf2f9a287153971467b1c11811213b64a38bdf673bad240436ff 64 multiverse/binary-arm64/Packages.xz ec343a438e9f7dc2190f5c19ebbd55d581170aacab1049de7067949b0f8e4db2 109 multiverse/binary-armhf/Release e7ab72b8f37c7c9c9f6386fb8e3dfa40bf6fe4b67876703c5927e47cb8664ce4 40 multiverse/binary-armhf/Packages.gz 65de7e01eff8cf2f9a287153971467b1c11811213b64a38bdf673bad240436ff 64 multiverse/binary-armhf/Packages.xz e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 multiverse/binary-armhf/Packages e7ab72b8f37c7c9c9f6386fb8e3dfa40bf6fe4b67876703c5927e47cb8664ce4 40 multiverse/binary-i386/Packages.gz e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 multiverse/binary-i386/Packages 65de7e01eff8cf2f9a287153971467b1c11811213b64a38bdf673bad240436ff 64 multiverse/binary-i386/Packages.xz 642a5cda6e7517b554671c0da90fb68a1d23a436148177fb54d54eaeeaafc020 108 multiverse/binary-i386/Release e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 multiverse/binary-ppc64el/Packages e7ab72b8f37c7c9c9f6386fb8e3dfa40bf6fe4b67876703c5927e47cb8664ce4 40 multiverse/binary-ppc64el/Packages.gz 1845d88d795056c706947ad08e58120780f4677fa4bd7bcffc10538fd8ba73e4 111 multiverse/binary-ppc64el/Release 65de7e01eff8cf2f9a287153971467b1c11811213b64a38bdf673bad240436ff 64 multiverse/binary-ppc64el/Packages.xz d4ea08d9d5712a93799617474b78b05a9e15d70395bce791239a94d3ba6ee2d8 109 multiverse/binary-s390x/Release e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 multiverse/binary-s390x/Packages e7ab72b8f37c7c9c9f6386fb8e3dfa40bf6fe4b67876703c5927e47cb8664ce4 40 multiverse/binary-s390x/Packages.gz 65de7e01eff8cf2f9a287153971467b1c11811213b64a38bdf673bad240436ff 64 multiverse/binary-s390x/Packages.xz 65de7e01eff8cf2f9a287153971467b1c11811213b64a38bdf673bad240436ff 64 multiverse/debian-installer/binary-amd64/Packages.xz e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 multiverse/debian-installer/binary-amd64/Packages e7ab72b8f37c7c9c9f6386fb8e3dfa40bf6fe4b67876703c5927e47cb8664ce4 40 multiverse/debian-installer/binary-amd64/Packages.gz e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 multiverse/debian-installer/binary-arm64/Packages 65de7e01eff8cf2f9a287153971467b1c11811213b64a38bdf673bad240436ff 64 multiverse/debian-installer/binary-arm64/Packages.xz e7ab72b8f37c7c9c9f6386fb8e3dfa40bf6fe4b67876703c5927e47cb8664ce4 40 multiverse/debian-installer/binary-arm64/Packages.gz e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 multiverse/debian-installer/binary-armhf/Packages e7ab72b8f37c7c9c9f6386fb8e3dfa40bf6fe4b67876703c5927e47cb8664ce4 40 multiverse/debian-installer/binary-armhf/Packages.gz 65de7e01eff8cf2f9a287153971467b1c11811213b64a38bdf673bad240436ff 64 multiverse/debian-installer/binary-armhf/Packages.xz 65de7e01eff8cf2f9a287153971467b1c11811213b64a38bdf673bad240436ff 64 multiverse/debian-installer/binary-i386/Packages.xz e7ab72b8f37c7c9c9f6386fb8e3dfa40bf6fe4b67876703c5927e47cb8664ce4 40 multiverse/debian-installer/binary-i386/Packages.gz e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 multiverse/debian-installer/binary-i386/Packages e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 multiverse/debian-installer/binary-ppc64el/Packages e7ab72b8f37c7c9c9f6386fb8e3dfa40bf6fe4b67876703c5927e47cb8664ce4 40 multiverse/debian-installer/binary-ppc64el/Packages.gz 65de7e01eff8cf2f9a287153971467b1c11811213b64a38bdf673bad240436ff 64 multiverse/debian-installer/binary-ppc64el/Packages.xz e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 multiverse/debian-installer/binary-s390x/Packages e7ab72b8f37c7c9c9f6386fb8e3dfa40bf6fe4b67876703c5927e47cb8664ce4 40 multiverse/debian-installer/binary-s390x/Packages.gz 65de7e01eff8cf2f9a287153971467b1c11811213b64a38bdf673bad240436ff 64 multiverse/debian-installer/binary-s390x/Packages.xz 46a6f48f8aea81f4bd76f1c579329ced669351142854a7653caf161bc056224f 160 multiverse/dep11/Components-armhf.yml.gz 7989bb311baa38ef545250282aa065d23281c46dfb8faabe4c653487bdbded5c 29 multiverse/dep11/icons-64x64.tar.gz 7efc535da833779e4af36f0935d03a28bbac3b3cd8df087834fd932a42328b2e 212 multiverse/dep11/Components-armhf.yml.xz 0648c7a0f7337de83380d872bc692701d782c824f65db6734edd45f23d959526 212 multiverse/dep11/Components-ppc64el.yml.xz 6e2e3533720c07dadc6dfa99736d85ade4a44b4604283760fe592f0e731f47a2 170 multiverse/dep11/Components-i386.yml 74e400fa9da540e23736f795a3ef6857b5dfec8dd88ca12e784d17545241b1f5 160 multiverse/dep11/Components-s390x.yml.gz 7989bb311baa38ef545250282aa065d23281c46dfb8faabe4c653487bdbded5c 29 multiverse/dep11/icons-128x128.tar.gz 1b127e371a253d10f7491026c7a5278de362a48cde4117bbb7457ccef680592e 212 multiverse/dep11/Components-amd64.yml.xz 2e452c148d8a0540a2748d2c37d8f6962100c8b62055fc3770a8390a4aa059ae 170 multiverse/dep11/Components-armhf.yml df229da30d17df1f942f15a434f7dbacecfadd3517123a4d1d16df7498630df8 170 multiverse/dep11/Components-arm64.yml a6624cecabb006c95323a2574b2056f24cb259dc3ad243f3f16cc5b018f62e58 212 multiverse/dep11/Components-powerpc.yml.xz 01b7ea61717594c7a17a98a7596e650567fd3d7233dfe085a0a6a5b695cd1acc 160 multiverse/dep11/Components-arm64.yml.gz 1658e250ade16fa0b88d52cb00b5f96dec5d65a313d6c810d2b168da9a27df13 212 multiverse/dep11/Components-s390x.yml.xz 7a1571b6e411f4006942e118337ff762f7726fad6f7cd4ee9bfba78f78df3d7d 160 multiverse/dep11/Components-i386.yml.gz 137f6ec387ed4407d813da03a63ce39fc5f9d362a583b95ecff6a890d632ad68 159 multiverse/dep11/Components-ppc64el.yml.gz dbc595e3bccb2b4129e125c109253972e456588be2c72a96af3fd7b07f3c9151 160 multiverse/dep11/Components-amd64.yml.gz 5f70bf18a086007016e948b04aed3b82103a36bea41755b6cddfaf10ace3c6ef 1024 multiverse/dep11/icons-128x128.tar 3a143f7d353eb42bd0ab9935b153b9865e1be5bd003ca32b1cf4b6c8c2c130fb 212 multiverse/dep11/Components-i386.yml.xz b884e0fa5b1d3ed047b9232a3bf674478cdd73ecabb4d5f0872409170b283a94 159 multiverse/dep11/Components-powerpc.yml.gz 5f70bf18a086007016e948b04aed3b82103a36bea41755b6cddfaf10ace3c6ef 1024 multiverse/dep11/icons-64x64.tar 18a870fd9ecfb350e17d141c57cc92c2c5f81592d53cfa324c265a6b94ad2ccd 170 multiverse/dep11/Components-ppc64el.yml 177cdaa6fb2dfce560ec991c51128523df8626167ec5399539dcd8be2b377d97 170 multiverse/dep11/Components-amd64.yml adbdbbace61e34c08c1d67700cb576fabacba43b7b68016725eb3d721c4d04a9 212 multiverse/dep11/Components-arm64.yml.xz 4e6c388fa2aa26845dc95f2daaeadffcb0115dc6f11456f495c8fed103d74d94 170 multiverse/dep11/Components-s390x.yml 4caef1d09bfcb71c0ad78e30b6b87a529648d46110edc2dfc3cf170764e04ca6 170 multiverse/dep11/Components-powerpc.yml e7ab72b8f37c7c9c9f6386fb8e3dfa40bf6fe4b67876703c5927e47cb8664ce4 40 multiverse/i18n/Translation-en.gz e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 multiverse/i18n/Translation-en 65de7e01eff8cf2f9a287153971467b1c11811213b64a38bdf673bad240436ff 64 multiverse/i18n/Translation-en.xz aea852a7918a6c61c359cd57114f60ca95551f449f06cb1c9f296b8326b78101 192 multiverse/i18n/Index e7ab72b8f37c7c9c9f6386fb8e3dfa40bf6fe4b67876703c5927e47cb8664ce4 40 multiverse/source/Sources.gz 65de7e01eff8cf2f9a287153971467b1c11811213b64a38bdf673bad240436ff 64 multiverse/source/Sources.xz e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 multiverse/source/Sources fb39de5c83517a08c482d0c3f369be383de6d1fe04e92f93a5e462c650846530 110 multiverse/source/Release e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 restricted/binary-amd64/Packages e7ab72b8f37c7c9c9f6386fb8e3dfa40bf6fe4b67876703c5927e47cb8664ce4 40 restricted/binary-amd64/Packages.gz e792d703a4aaf927cc6aed9d7896253a1607e4d4517b6f8043ffd037fd5de580 109 restricted/binary-amd64/Release 65de7e01eff8cf2f9a287153971467b1c11811213b64a38bdf673bad240436ff 64 restricted/binary-amd64/Packages.xz fb4573c406d10bcf0f1df1969449802e309279a2e4dba09212945f768a5ddff9 109 restricted/binary-arm64/Release 65de7e01eff8cf2f9a287153971467b1c11811213b64a38bdf673bad240436ff 64 restricted/binary-arm64/Packages.xz e7ab72b8f37c7c9c9f6386fb8e3dfa40bf6fe4b67876703c5927e47cb8664ce4 40 restricted/binary-arm64/Packages.gz e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 restricted/binary-arm64/Packages e7ab72b8f37c7c9c9f6386fb8e3dfa40bf6fe4b67876703c5927e47cb8664ce4 40 restricted/binary-armhf/Packages.gz 65de7e01eff8cf2f9a287153971467b1c11811213b64a38bdf673bad240436ff 64 restricted/binary-armhf/Packages.xz 0e1158efbc2b13d2173d09c295bd9ff59abb568f9caac5514b30e264d51bb27d 109 restricted/binary-armhf/Release e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 restricted/binary-armhf/Packages e7ab72b8f37c7c9c9f6386fb8e3dfa40bf6fe4b67876703c5927e47cb8664ce4 40 restricted/binary-i386/Packages.gz 65de7e01eff8cf2f9a287153971467b1c11811213b64a38bdf673bad240436ff 64 restricted/binary-i386/Packages.xz e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 restricted/binary-i386/Packages 8265e48aa494f2c16e7e9b05c12aba8d0cbfe1f6d301ccd80e25e9de0cacf58a 108 restricted/binary-i386/Release e7ab72b8f37c7c9c9f6386fb8e3dfa40bf6fe4b67876703c5927e47cb8664ce4 40 restricted/binary-ppc64el/Packages.gz e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 restricted/binary-ppc64el/Packages 65de7e01eff8cf2f9a287153971467b1c11811213b64a38bdf673bad240436ff 64 restricted/binary-ppc64el/Packages.xz 7419494b81b1a724937da60ebd9ee6895d8b9bed4926048b4c156f736d29b498 111 restricted/binary-ppc64el/Release e7ab72b8f37c7c9c9f6386fb8e3dfa40bf6fe4b67876703c5927e47cb8664ce4 40 restricted/binary-s390x/Packages.gz e9d5e1442194bab9feec15754e85cec9fb49f03e678dae88a83c722eecbf8db9 109 restricted/binary-s390x/Release e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 restricted/binary-s390x/Packages 65de7e01eff8cf2f9a287153971467b1c11811213b64a38bdf673bad240436ff 64 restricted/binary-s390x/Packages.xz 65de7e01eff8cf2f9a287153971467b1c11811213b64a38bdf673bad240436ff 64 restricted/debian-installer/binary-amd64/Packages.xz e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 restricted/debian-installer/binary-amd64/Packages e7ab72b8f37c7c9c9f6386fb8e3dfa40bf6fe4b67876703c5927e47cb8664ce4 40 restricted/debian-installer/binary-amd64/Packages.gz e7ab72b8f37c7c9c9f6386fb8e3dfa40bf6fe4b67876703c5927e47cb8664ce4 40 restricted/debian-installer/binary-arm64/Packages.gz 65de7e01eff8cf2f9a287153971467b1c11811213b64a38bdf673bad240436ff 64 restricted/debian-installer/binary-arm64/Packages.xz e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 restricted/debian-installer/binary-arm64/Packages e7ab72b8f37c7c9c9f6386fb8e3dfa40bf6fe4b67876703c5927e47cb8664ce4 40 restricted/debian-installer/binary-armhf/Packages.gz e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 restricted/debian-installer/binary-armhf/Packages 65de7e01eff8cf2f9a287153971467b1c11811213b64a38bdf673bad240436ff 64 restricted/debian-installer/binary-armhf/Packages.xz 65de7e01eff8cf2f9a287153971467b1c11811213b64a38bdf673bad240436ff 64 restricted/debian-installer/binary-i386/Packages.xz e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 restricted/debian-installer/binary-i386/Packages e7ab72b8f37c7c9c9f6386fb8e3dfa40bf6fe4b67876703c5927e47cb8664ce4 40 restricted/debian-installer/binary-i386/Packages.gz 65de7e01eff8cf2f9a287153971467b1c11811213b64a38bdf673bad240436ff 64 restricted/debian-installer/binary-ppc64el/Packages.xz e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 restricted/debian-installer/binary-ppc64el/Packages e7ab72b8f37c7c9c9f6386fb8e3dfa40bf6fe4b67876703c5927e47cb8664ce4 40 restricted/debian-installer/binary-ppc64el/Packages.gz 65de7e01eff8cf2f9a287153971467b1c11811213b64a38bdf673bad240436ff 64 restricted/debian-installer/binary-s390x/Packages.xz e7ab72b8f37c7c9c9f6386fb8e3dfa40bf6fe4b67876703c5927e47cb8664ce4 40 restricted/debian-installer/binary-s390x/Packages.gz e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 restricted/debian-installer/binary-s390x/Packages b3c3dcae030c11651aaee486cd61a02fa1c8d9d306d856758e13adbe58ac4486 185 restricted/dep11/Components-arm64.yml 211672efb527870236f13cd24b999b20cb6fd1eaa41df2ac22644520aba57ef6 193 restricted/dep11/Components-amd64.yml.gz b3c3dcae030c11651aaee486cd61a02fa1c8d9d306d856758e13adbe58ac4486 185 restricted/dep11/Components-armhf.yml e08bfb8001fe8448907e64efa77ed68687acceba7bdf6a2f9013cb42ed193bf9 195 restricted/dep11/Components-ppc64el.yml.gz d35455d8a0709f52842b976acf91a7401bd601831de19b15336be7d082b19ebb 193 restricted/dep11/Components-s390x.yml.gz b3c3dcae030c11651aaee486cd61a02fa1c8d9d306d856758e13adbe58ac4486 185 restricted/dep11/Components-s390x.yml b3c3dcae030c11651aaee486cd61a02fa1c8d9d306d856758e13adbe58ac4486 185 restricted/dep11/Components-amd64.yml b3c3dcae030c11651aaee486cd61a02fa1c8d9d306d856758e13adbe58ac4486 185 restricted/dep11/Components-ppc64el.yml d9261cd240210b36a1c113d44ecdcc9f0c6dc17ab67ce1ffc82d10b97633d041 192 restricted/dep11/Components-i386.yml.gz e863ace85973a6544bdd695c8ca85d34f6dfd0f07f034a2edcdf7992fb7f73fd 193 restricted/dep11/Components-arm64.yml.gz f711ab672837d3af106b9a4f23c513e659621df8681e20119096b3b51be27ead 193 restricted/dep11/Components-armhf.yml.gz b3c3dcae030c11651aaee486cd61a02fa1c8d9d306d856758e13adbe58ac4486 185 restricted/dep11/Components-i386.yml aea852a7918a6c61c359cd57114f60ca95551f449f06cb1c9f296b8326b78101 192 restricted/i18n/Index e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 restricted/i18n/Translation-en e7ab72b8f37c7c9c9f6386fb8e3dfa40bf6fe4b67876703c5927e47cb8664ce4 40 restricted/i18n/Translation-en.gz 65de7e01eff8cf2f9a287153971467b1c11811213b64a38bdf673bad240436ff 64 restricted/i18n/Translation-en.xz f9ceb7cca4c66f7c4e02a6822f766d8f82c53c9b70ffe6d7f8bdc7dad12642af 110 restricted/source/Release e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 restricted/source/Sources e7ab72b8f37c7c9c9f6386fb8e3dfa40bf6fe4b67876703c5927e47cb8664ce4 40 restricted/source/Sources.gz 65de7e01eff8cf2f9a287153971467b1c11811213b64a38bdf673bad240436ff 64 restricted/source/Sources.xz e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 universe/binary-amd64/Packages 65de7e01eff8cf2f9a287153971467b1c11811213b64a38bdf673bad240436ff 64 universe/binary-amd64/Packages.xz bb767e55d40e43348103df023f84659d144ea26024c600e8cb883a154fd7f261 107 universe/binary-amd64/Release e7ab72b8f37c7c9c9f6386fb8e3dfa40bf6fe4b67876703c5927e47cb8664ce4 40 universe/binary-amd64/Packages.gz 65de7e01eff8cf2f9a287153971467b1c11811213b64a38bdf673bad240436ff 64 universe/binary-arm64/Packages.xz e7ab72b8f37c7c9c9f6386fb8e3dfa40bf6fe4b67876703c5927e47cb8664ce4 40 universe/binary-arm64/Packages.gz 3fe8e2af23a46475b19f3d554f758338b61d7d38ea56388c196922cef9a67236 107 universe/binary-arm64/Release e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 universe/binary-arm64/Packages e7ab72b8f37c7c9c9f6386fb8e3dfa40bf6fe4b67876703c5927e47cb8664ce4 40 universe/binary-armhf/Packages.gz 29c60b9d256aae19cd602d044f990c7f7fbce4d242a8486ad8a848c0adb76ef1 107 universe/binary-armhf/Release 65de7e01eff8cf2f9a287153971467b1c11811213b64a38bdf673bad240436ff 64 universe/binary-armhf/Packages.xz e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 universe/binary-armhf/Packages 3c99a1a9f0c79c92d56d03b2895763c247e7a77de4eae08c7c0bb232694f8a5d 106 universe/binary-i386/Release e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 universe/binary-i386/Packages 65de7e01eff8cf2f9a287153971467b1c11811213b64a38bdf673bad240436ff 64 universe/binary-i386/Packages.xz e7ab72b8f37c7c9c9f6386fb8e3dfa40bf6fe4b67876703c5927e47cb8664ce4 40 universe/binary-i386/Packages.gz e7ab72b8f37c7c9c9f6386fb8e3dfa40bf6fe4b67876703c5927e47cb8664ce4 40 universe/binary-ppc64el/Packages.gz 65de7e01eff8cf2f9a287153971467b1c11811213b64a38bdf673bad240436ff 64 universe/binary-ppc64el/Packages.xz 937c250625b6fa4233fb9e9c823d8c1669b2cf41cf998d53c133bbfc27ffe60e 109 universe/binary-ppc64el/Release e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 universe/binary-ppc64el/Packages e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 universe/binary-s390x/Packages f0303814e670da6a8ff16b42605c628ee3c60df110e64d66ab145c3b9d3eaba5 107 universe/binary-s390x/Release e7ab72b8f37c7c9c9f6386fb8e3dfa40bf6fe4b67876703c5927e47cb8664ce4 40 universe/binary-s390x/Packages.gz 65de7e01eff8cf2f9a287153971467b1c11811213b64a38bdf673bad240436ff 64 universe/binary-s390x/Packages.xz e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 universe/debian-installer/binary-amd64/Packages e7ab72b8f37c7c9c9f6386fb8e3dfa40bf6fe4b67876703c5927e47cb8664ce4 40 universe/debian-installer/binary-amd64/Packages.gz 65de7e01eff8cf2f9a287153971467b1c11811213b64a38bdf673bad240436ff 64 universe/debian-installer/binary-amd64/Packages.xz 65de7e01eff8cf2f9a287153971467b1c11811213b64a38bdf673bad240436ff 64 universe/debian-installer/binary-arm64/Packages.xz e7ab72b8f37c7c9c9f6386fb8e3dfa40bf6fe4b67876703c5927e47cb8664ce4 40 universe/debian-installer/binary-arm64/Packages.gz e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 universe/debian-installer/binary-arm64/Packages e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 universe/debian-installer/binary-armhf/Packages e7ab72b8f37c7c9c9f6386fb8e3dfa40bf6fe4b67876703c5927e47cb8664ce4 40 universe/debian-installer/binary-armhf/Packages.gz 65de7e01eff8cf2f9a287153971467b1c11811213b64a38bdf673bad240436ff 64 universe/debian-installer/binary-armhf/Packages.xz 65de7e01eff8cf2f9a287153971467b1c11811213b64a38bdf673bad240436ff 64 universe/debian-installer/binary-i386/Packages.xz e7ab72b8f37c7c9c9f6386fb8e3dfa40bf6fe4b67876703c5927e47cb8664ce4 40 universe/debian-installer/binary-i386/Packages.gz e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 universe/debian-installer/binary-i386/Packages e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 universe/debian-installer/binary-ppc64el/Packages e7ab72b8f37c7c9c9f6386fb8e3dfa40bf6fe4b67876703c5927e47cb8664ce4 40 universe/debian-installer/binary-ppc64el/Packages.gz 65de7e01eff8cf2f9a287153971467b1c11811213b64a38bdf673bad240436ff 64 universe/debian-installer/binary-ppc64el/Packages.xz e7ab72b8f37c7c9c9f6386fb8e3dfa40bf6fe4b67876703c5927e47cb8664ce4 40 universe/debian-installer/binary-s390x/Packages.gz 65de7e01eff8cf2f9a287153971467b1c11811213b64a38bdf673bad240436ff 64 universe/debian-installer/binary-s390x/Packages.xz e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 universe/debian-installer/binary-s390x/Packages 5f70bf18a086007016e948b04aed3b82103a36bea41755b6cddfaf10ace3c6ef 1024 universe/dep11/icons-128x128.tar a110a98f9cd66a8e07569bcfe2c078d8125e57d643e680574317e29e199cdbed 212 universe/dep11/Components-ppc64el.yml.xz 24794f666af2990afd73f7ff4ffe879bafefe0fe4ab93a262f253785a95c44a3 212 universe/dep11/Components-s390x.yml.xz 8de10d975bdf23dbc52cb4448be329eb514ac511c14541111401ad377588254c 212 universe/dep11/Components-armhf.yml.xz 7989bb311baa38ef545250282aa065d23281c46dfb8faabe4c653487bdbded5c 29 universe/dep11/icons-64x64.tar.gz 8365bc0cc164c4bb41ec537baf72c80e8fd476f9efab24394b39e794c5745769 168 universe/dep11/Components-i386.yml 74de71af2121f3920326c74c64f335ed040b56274de572d8d3591a7ac46c262f 159 universe/dep11/Components-armhf.yml.gz 9cda01194ec1cbe297ab054b90df2e667edb2d8ae07502ced6e9d8c9a63387e4 158 universe/dep11/Components-ppc64el.yml.gz 7989bb311baa38ef545250282aa065d23281c46dfb8faabe4c653487bdbded5c 29 universe/dep11/icons-128x128.tar.gz c84f03d89e60ee37035287c3d168dfa44b9d11501be0f13aecdb9b227f2631ff 159 universe/dep11/Components-powerpc.yml.gz 68f71b6e31391b2d63db7633ce220ed5f4b2888ad7f4bf1b46ad2bb6bc3cb5b7 168 universe/dep11/Components-powerpc.yml 19b3ec327c77d18fca98d58eaaef9b5b5cb9b3c81ef694848ff63bed51114766 159 universe/dep11/Components-s390x.yml.gz 19f88dd048c5bb9d4108a83f564e8a3019445855085c312d87853d31827c535b 212 universe/dep11/Components-amd64.yml.xz a091c6bf27f844d65064f22a99c1828e4f4a0d3b82ecd4bfb535a825a7ca6d0f 159 universe/dep11/Components-i386.yml.gz 089eda669a556a52ee5058eee5fd587e443306571beb53ad2417947c0b969a92 168 universe/dep11/Components-arm64.yml 294f102f8656ffd38cf1d6f0a4db53c8e90fece577d4c2a073f699ab4099572b 212 universe/dep11/Components-powerpc.yml.xz a8faf48f6fd8d8737fcbf8caff9b79bd3339c656f3c8be74b9afc54c306f4c53 212 universe/dep11/Components-arm64.yml.xz 182d54f805163ed2b25db3f1b8c9855736402f78b4f6153e49e405fdd10b52b9 159 universe/dep11/Components-arm64.yml.gz 47a270e183c981d0a4bda886e40893f4dee8c4fbe06f1fc6704f0cbcf4bde712 168 universe/dep11/Components-s390x.yml 5f70bf18a086007016e948b04aed3b82103a36bea41755b6cddfaf10ace3c6ef 1024 universe/dep11/icons-64x64.tar aa9a7548f42c2f424182282307205e828df9c6626820083ae70873c1fce8d354 168 universe/dep11/Components-ppc64el.yml b66cd0531e9fe65264ab1ff88c27d655feead4517171fd138ee659d7f4964224 212 universe/dep11/Components-i386.yml.xz d38e4960f419a9e3fc59e1879c76488aacb8322aca4b94e63cd04789118d100a 168 universe/dep11/Components-armhf.yml 0177dfb98e660f31dc420b851d1b0005b04486f2e1febf38efd70e0bf63f0fbc 159 universe/dep11/Components-amd64.yml.gz 24767211c7924ae74c0030267d5e97cc1ea56b9a50aeb811d0225847b18152c2 168 universe/dep11/Components-amd64.yml e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 universe/i18n/Translation-en e7ab72b8f37c7c9c9f6386fb8e3dfa40bf6fe4b67876703c5927e47cb8664ce4 40 universe/i18n/Translation-en.gz aea852a7918a6c61c359cd57114f60ca95551f449f06cb1c9f296b8326b78101 192 universe/i18n/Index 65de7e01eff8cf2f9a287153971467b1c11811213b64a38bdf673bad240436ff 64 universe/i18n/Translation-en.xz e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 universe/source/Sources 65de7e01eff8cf2f9a287153971467b1c11811213b64a38bdf673bad240436ff 64 universe/source/Sources.xz e7ab72b8f37c7c9c9f6386fb8e3dfa40bf6fe4b67876703c5927e47cb8664ce4 40 universe/source/Sources.gz 6546244ece6e2b0d2ee5b8e696040d2d6abb865ec125f5eaeb5ddd756eff49e0 108 universe/source/Release Acquire-By-Hash: yes -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.11 (GNU/Linux) iQIcBAEBCgAGBQJY0NsrAAoJEDtP5qzAsh8y8OQQAMXiOfHTiVk/N9e2Lnt+3l1U mAD1Q2InessuCbRgg0ghn+TCAGQn7D3o4V7NQaG58iBwBYrfwLXcfWiGKou6fTlo lOm10D1vQcaa9m8Fnsbh7FNHBG99tov4fl1XTC1DF5hr4NsFIjDlc8KlVAB83KLT cefNY91V57uwQE42Qf6Gd42GRyprOx4uGRnjGSeZUTnRwYzNe4l+PCBWP0PTpBxJ r7ggH7aINGwoCkFrZFZwridzMg6hSC4dUIH4ud3lQ+TmzzUmOiddhlY67kmrgI1h U4FKmXXjwemfsLPOjU5lMtZQJsEjptCzAyoAMEkG4UxgK1Xbg8lwhoMZsTaW1N/S 3vyWDi8i+2FDksPdlKbhaT4WfpHtCrMmuD9wgdZtwm8F8FlAEUakt486/vSKRHsa wnX9XVV463hQ8kgqZ6cPkuMtVceI0Y6VmKW0jJvyhKz6t7bg0gx2WWetuGkyqFBs wY4wA52ARzlpkhL0LOT41iAd7BGgjv1uUkzWlj5CK9eNqI8IIZiNSwq1cYetzE24 cl2XyDnh8oFPtaIrnjy7mzo2988aa/Izn1bhRyvhtpF4cx3SDojumrPZc6l/wAmi cLUMF+SZJXbxO8OlJBEBs5cheoGt7kwsnkLu3GRxQ5YXugIE0p6V6XFsv8CKPTsX Q9kgcZHseH4+gDvp9ylB =aOmr -----END PGP SIGNATURE----- relic-7.6.1/functest/packages/Release000066400000000000000000002617341455105530300175550ustar00rootroot00000000000000Origin: Ubuntu Label: Ubuntu Suite: zesty-updates Version: 17.04 Codename: zesty Date: Tue, 21 Mar 2017 7:49:42 UTC Architectures: amd64 arm64 armhf i386 ppc64el s390x Components: main restricted universe multiverse Description: Ubuntu Zesty Updates MD5Sum: 2fca0dd3eb74d8f0472c2e53fb62cc5e 695 Contents-armhf.gz 0fa24d86f4eb65e88acd569bff0b53a8 1416 Contents-armhf 0fa24d86f4eb65e88acd569bff0b53a8 1416 Contents-ppc64el 2fca0dd3eb74d8f0472c2e53fb62cc5e 695 Contents-ppc64el.gz 0fa24d86f4eb65e88acd569bff0b53a8 1416 Contents-arm64 0fa24d86f4eb65e88acd569bff0b53a8 1416 Contents-amd64 2fca0dd3eb74d8f0472c2e53fb62cc5e 695 Contents-arm64.gz 2fca0dd3eb74d8f0472c2e53fb62cc5e 695 Contents-amd64.gz 0fa24d86f4eb65e88acd569bff0b53a8 1416 Contents-s390x 0fa24d86f4eb65e88acd569bff0b53a8 1416 Contents-i386 2fca0dd3eb74d8f0472c2e53fb62cc5e 695 Contents-s390x.gz 2fca0dd3eb74d8f0472c2e53fb62cc5e 695 Contents-i386.gz d41d8cd98f00b204e9800998ecf8427e 0 main/binary-amd64/Packages fb41d367e17e92ff1c969c003f725eea 103 main/binary-amd64/Release e62ff0123a74adfc6903d59a449cbdb0 40 main/binary-amd64/Packages.gz 05a2a1d284b1f093d602cff1b16f23dd 64 main/binary-amd64/Packages.xz b422be4e75917c4d80f0c746e84ab664 103 main/binary-arm64/Release d41d8cd98f00b204e9800998ecf8427e 0 main/binary-arm64/Packages 05a2a1d284b1f093d602cff1b16f23dd 64 main/binary-arm64/Packages.xz e62ff0123a74adfc6903d59a449cbdb0 40 main/binary-arm64/Packages.gz c00018de88988653aa7553db62035050 103 main/binary-armhf/Release d41d8cd98f00b204e9800998ecf8427e 0 main/binary-armhf/Packages 05a2a1d284b1f093d602cff1b16f23dd 64 main/binary-armhf/Packages.xz e62ff0123a74adfc6903d59a449cbdb0 40 main/binary-armhf/Packages.gz 05a2a1d284b1f093d602cff1b16f23dd 64 main/binary-i386/Packages.xz 1ac58296c3f51f39c001af8f22b9e395 102 main/binary-i386/Release e62ff0123a74adfc6903d59a449cbdb0 40 main/binary-i386/Packages.gz d41d8cd98f00b204e9800998ecf8427e 0 main/binary-i386/Packages 05a2a1d284b1f093d602cff1b16f23dd 64 main/binary-ppc64el/Packages.xz 9491af1e767c9702f498fc40a6bdfc5d 105 main/binary-ppc64el/Release e62ff0123a74adfc6903d59a449cbdb0 40 main/binary-ppc64el/Packages.gz d41d8cd98f00b204e9800998ecf8427e 0 main/binary-ppc64el/Packages e0d7277f148627f9afd40ad5612ad7e6 103 main/binary-s390x/Release e62ff0123a74adfc6903d59a449cbdb0 40 main/binary-s390x/Packages.gz d41d8cd98f00b204e9800998ecf8427e 0 main/binary-s390x/Packages 05a2a1d284b1f093d602cff1b16f23dd 64 main/binary-s390x/Packages.xz e62ff0123a74adfc6903d59a449cbdb0 40 main/debian-installer/binary-amd64/Packages.gz d41d8cd98f00b204e9800998ecf8427e 0 main/debian-installer/binary-amd64/Packages 05a2a1d284b1f093d602cff1b16f23dd 64 main/debian-installer/binary-amd64/Packages.xz d41d8cd98f00b204e9800998ecf8427e 0 main/debian-installer/binary-arm64/Packages e62ff0123a74adfc6903d59a449cbdb0 40 main/debian-installer/binary-arm64/Packages.gz 05a2a1d284b1f093d602cff1b16f23dd 64 main/debian-installer/binary-arm64/Packages.xz e62ff0123a74adfc6903d59a449cbdb0 40 main/debian-installer/binary-armhf/Packages.gz 05a2a1d284b1f093d602cff1b16f23dd 64 main/debian-installer/binary-armhf/Packages.xz d41d8cd98f00b204e9800998ecf8427e 0 main/debian-installer/binary-armhf/Packages d41d8cd98f00b204e9800998ecf8427e 0 main/debian-installer/binary-i386/Packages e62ff0123a74adfc6903d59a449cbdb0 40 main/debian-installer/binary-i386/Packages.gz 05a2a1d284b1f093d602cff1b16f23dd 64 main/debian-installer/binary-i386/Packages.xz 05a2a1d284b1f093d602cff1b16f23dd 64 main/debian-installer/binary-ppc64el/Packages.xz e62ff0123a74adfc6903d59a449cbdb0 40 main/debian-installer/binary-ppc64el/Packages.gz d41d8cd98f00b204e9800998ecf8427e 0 main/debian-installer/binary-ppc64el/Packages 05a2a1d284b1f093d602cff1b16f23dd 64 main/debian-installer/binary-s390x/Packages.xz e62ff0123a74adfc6903d59a449cbdb0 40 main/debian-installer/binary-s390x/Packages.gz d41d8cd98f00b204e9800998ecf8427e 0 main/debian-installer/binary-s390x/Packages 5e0ea1acc9dcb26e00bbfbff42a554e0 156 main/dep11/Components-armhf.yml.gz 655201ea420cdaab7f427b7f5a77f6f8 208 main/dep11/Components-s390x.yml.xz 0f343b0931126a20f133d67c2b018a3b 1024 main/dep11/icons-128x128.tar fddea25d2dc78fc230a7104c5d8cf7f7 208 main/dep11/Components-powerpc.yml.xz 22f645a237984b9b3c187082cb29b969 208 main/dep11/Components-arm64.yml.xz 31f6566d35ccd604be46ed5b1f813cdf 29 main/dep11/icons-128x128.tar.gz aa0500b418a16eb0fbde645195bfcc65 156 main/dep11/Components-s390x.yml.gz b6f86bd6f4233ccf99f4649e494b07bd 164 main/dep11/Components-arm64.yml c8fef3ed74ae361749a8951e8514e922 164 main/dep11/Components-armhf.yml 309d9e8155b625fe1caa80c28700b723 164 main/dep11/Components-s390x.yml f4f2b1e94f3978d41febc76bb326832a 208 main/dep11/Components-amd64.yml.xz 11764f2ea218e789540806926d5f557e 208 main/dep11/Components-ppc64el.yml.xz a304b1e75ab3f654de81d1a30ba53c0a 156 main/dep11/Components-arm64.yml.gz 3591fd561d9a33ca799ada4d1fab41af 164 main/dep11/Components-i386.yml 1569849bb6b847ec2d8e7d6d9a929ee4 208 main/dep11/Components-i386.yml.xz 60316e5f3a0c0a6f0e4f7e871666f875 164 main/dep11/Components-ppc64el.yml 0f343b0931126a20f133d67c2b018a3b 1024 main/dep11/icons-64x64.tar 49de8ce14ae338352ac794b7b15d0a4c 156 main/dep11/Components-powerpc.yml.gz c5b8fc38a5e334df369d26b934ff17d4 164 main/dep11/Components-powerpc.yml 95ff05a99b311eb1283e678559e6743d 164 main/dep11/Components-amd64.yml ed1ad4855d2356f4e0031dab6d9cec6a 156 main/dep11/Components-ppc64el.yml.gz 31f6566d35ccd604be46ed5b1f813cdf 29 main/dep11/icons-64x64.tar.gz 717988572b189b610ee5e0494ac2494b 156 main/dep11/Components-i386.yml.gz 57e3ebdbef7eb79cfa0c8b345e374a6f 156 main/dep11/Components-amd64.yml.gz c45ed773024f7a97a3ce0d5cf4ac1788 208 main/dep11/Components-armhf.yml.xz e62ff0123a74adfc6903d59a449cbdb0 40 main/i18n/Translation-en.gz 05a2a1d284b1f093d602cff1b16f23dd 64 main/i18n/Translation-en.xz d41d8cd98f00b204e9800998ecf8427e 0 main/i18n/Translation-en 83f113e5a6e4d07ca337fc40833470ee 192 main/i18n/Index e3649003f9216becf39a6705be18682a 104 main/source/Release d41d8cd98f00b204e9800998ecf8427e 0 main/source/Sources e62ff0123a74adfc6903d59a449cbdb0 40 main/source/Sources.gz 05a2a1d284b1f093d602cff1b16f23dd 64 main/source/Sources.xz e62ff0123a74adfc6903d59a449cbdb0 40 multiverse/binary-amd64/Packages.gz 05a2a1d284b1f093d602cff1b16f23dd 64 multiverse/binary-amd64/Packages.xz 4adce606e1d09408a0a5817799e3ddb1 109 multiverse/binary-amd64/Release d41d8cd98f00b204e9800998ecf8427e 0 multiverse/binary-amd64/Packages e62ff0123a74adfc6903d59a449cbdb0 40 multiverse/binary-arm64/Packages.gz d41d8cd98f00b204e9800998ecf8427e 0 multiverse/binary-arm64/Packages b0112975f068a1135f87b1e29a620e4b 109 multiverse/binary-arm64/Release 05a2a1d284b1f093d602cff1b16f23dd 64 multiverse/binary-arm64/Packages.xz 56d232eca5cacc6878f544b97696667f 109 multiverse/binary-armhf/Release e62ff0123a74adfc6903d59a449cbdb0 40 multiverse/binary-armhf/Packages.gz 05a2a1d284b1f093d602cff1b16f23dd 64 multiverse/binary-armhf/Packages.xz d41d8cd98f00b204e9800998ecf8427e 0 multiverse/binary-armhf/Packages e62ff0123a74adfc6903d59a449cbdb0 40 multiverse/binary-i386/Packages.gz d41d8cd98f00b204e9800998ecf8427e 0 multiverse/binary-i386/Packages 05a2a1d284b1f093d602cff1b16f23dd 64 multiverse/binary-i386/Packages.xz cf309f58ebd409b624a87091d728e338 108 multiverse/binary-i386/Release d41d8cd98f00b204e9800998ecf8427e 0 multiverse/binary-ppc64el/Packages e62ff0123a74adfc6903d59a449cbdb0 40 multiverse/binary-ppc64el/Packages.gz 952d7d03742eaf9a144060438f6bcb07 111 multiverse/binary-ppc64el/Release 05a2a1d284b1f093d602cff1b16f23dd 64 multiverse/binary-ppc64el/Packages.xz c36fe18fc7c258535aa07b15156e66a4 109 multiverse/binary-s390x/Release d41d8cd98f00b204e9800998ecf8427e 0 multiverse/binary-s390x/Packages e62ff0123a74adfc6903d59a449cbdb0 40 multiverse/binary-s390x/Packages.gz 05a2a1d284b1f093d602cff1b16f23dd 64 multiverse/binary-s390x/Packages.xz 05a2a1d284b1f093d602cff1b16f23dd 64 multiverse/debian-installer/binary-amd64/Packages.xz d41d8cd98f00b204e9800998ecf8427e 0 multiverse/debian-installer/binary-amd64/Packages e62ff0123a74adfc6903d59a449cbdb0 40 multiverse/debian-installer/binary-amd64/Packages.gz d41d8cd98f00b204e9800998ecf8427e 0 multiverse/debian-installer/binary-arm64/Packages 05a2a1d284b1f093d602cff1b16f23dd 64 multiverse/debian-installer/binary-arm64/Packages.xz e62ff0123a74adfc6903d59a449cbdb0 40 multiverse/debian-installer/binary-arm64/Packages.gz d41d8cd98f00b204e9800998ecf8427e 0 multiverse/debian-installer/binary-armhf/Packages e62ff0123a74adfc6903d59a449cbdb0 40 multiverse/debian-installer/binary-armhf/Packages.gz 05a2a1d284b1f093d602cff1b16f23dd 64 multiverse/debian-installer/binary-armhf/Packages.xz 05a2a1d284b1f093d602cff1b16f23dd 64 multiverse/debian-installer/binary-i386/Packages.xz e62ff0123a74adfc6903d59a449cbdb0 40 multiverse/debian-installer/binary-i386/Packages.gz d41d8cd98f00b204e9800998ecf8427e 0 multiverse/debian-installer/binary-i386/Packages d41d8cd98f00b204e9800998ecf8427e 0 multiverse/debian-installer/binary-ppc64el/Packages e62ff0123a74adfc6903d59a449cbdb0 40 multiverse/debian-installer/binary-ppc64el/Packages.gz 05a2a1d284b1f093d602cff1b16f23dd 64 multiverse/debian-installer/binary-ppc64el/Packages.xz d41d8cd98f00b204e9800998ecf8427e 0 multiverse/debian-installer/binary-s390x/Packages e62ff0123a74adfc6903d59a449cbdb0 40 multiverse/debian-installer/binary-s390x/Packages.gz 05a2a1d284b1f093d602cff1b16f23dd 64 multiverse/debian-installer/binary-s390x/Packages.xz 45a91bdecec65f9a8c8fcfa3c6ecc18b 160 multiverse/dep11/Components-armhf.yml.gz 31f6566d35ccd604be46ed5b1f813cdf 29 multiverse/dep11/icons-64x64.tar.gz 0da6aee1bf382e07bbc4f9ad205fdff4 212 multiverse/dep11/Components-armhf.yml.xz 0ee168266345ef9a1f580828c0fcad40 212 multiverse/dep11/Components-ppc64el.yml.xz ed9e024d2ff1e05b6e3b83317812562c 170 multiverse/dep11/Components-i386.yml 37d90127f72cd853a41af522444870a0 160 multiverse/dep11/Components-s390x.yml.gz 31f6566d35ccd604be46ed5b1f813cdf 29 multiverse/dep11/icons-128x128.tar.gz 365c0fceb27a07710c2cf3edd257907e 212 multiverse/dep11/Components-amd64.yml.xz 240e80aadba3dcc41f4c552c21e26631 170 multiverse/dep11/Components-armhf.yml bc42d767074e976157ac6a2c3cd2b4e9 170 multiverse/dep11/Components-arm64.yml befa7682bfea5216137f2b23f0dea96f 212 multiverse/dep11/Components-powerpc.yml.xz 574c4390ea4e37347f473a33b4369e89 160 multiverse/dep11/Components-arm64.yml.gz 825de64b1f26718bcb4300cca962d7aa 212 multiverse/dep11/Components-s390x.yml.xz 1dc6abb3671f02ff3634bb3023142ec8 160 multiverse/dep11/Components-i386.yml.gz 78a255ad9db5dff2dd862a191e26d2b7 159 multiverse/dep11/Components-ppc64el.yml.gz 009e42022ca06b2374c155d28c395cc6 160 multiverse/dep11/Components-amd64.yml.gz 0f343b0931126a20f133d67c2b018a3b 1024 multiverse/dep11/icons-128x128.tar 435ded8b8a604ec1cb4371687ce20ef9 212 multiverse/dep11/Components-i386.yml.xz ae61431e9f2de661d77b50460a9cb5f0 159 multiverse/dep11/Components-powerpc.yml.gz 0f343b0931126a20f133d67c2b018a3b 1024 multiverse/dep11/icons-64x64.tar d9cab359ea78d8d385f2ce4fb9b9208c 170 multiverse/dep11/Components-ppc64el.yml 3282d81b8251982d7d9a578d14c9f066 170 multiverse/dep11/Components-amd64.yml 59d3311cc00227d26c699d39ada2571f 212 multiverse/dep11/Components-arm64.yml.xz b9a68d645ca4ff08f56f7587a2b54198 170 multiverse/dep11/Components-s390x.yml abb8b247e2445635abedc38de6716e11 170 multiverse/dep11/Components-powerpc.yml e62ff0123a74adfc6903d59a449cbdb0 40 multiverse/i18n/Translation-en.gz d41d8cd98f00b204e9800998ecf8427e 0 multiverse/i18n/Translation-en 05a2a1d284b1f093d602cff1b16f23dd 64 multiverse/i18n/Translation-en.xz 83f113e5a6e4d07ca337fc40833470ee 192 multiverse/i18n/Index e62ff0123a74adfc6903d59a449cbdb0 40 multiverse/source/Sources.gz 05a2a1d284b1f093d602cff1b16f23dd 64 multiverse/source/Sources.xz d41d8cd98f00b204e9800998ecf8427e 0 multiverse/source/Sources e5ae32d22f198cb433d072b54617ce83 110 multiverse/source/Release d41d8cd98f00b204e9800998ecf8427e 0 restricted/binary-amd64/Packages e62ff0123a74adfc6903d59a449cbdb0 40 restricted/binary-amd64/Packages.gz a37bbc3f1d8249aab62ee2c89b78daa8 109 restricted/binary-amd64/Release 05a2a1d284b1f093d602cff1b16f23dd 64 restricted/binary-amd64/Packages.xz 2e5598616ace40ddfd5ff8679a2fe6bb 109 restricted/binary-arm64/Release 05a2a1d284b1f093d602cff1b16f23dd 64 restricted/binary-arm64/Packages.xz e62ff0123a74adfc6903d59a449cbdb0 40 restricted/binary-arm64/Packages.gz d41d8cd98f00b204e9800998ecf8427e 0 restricted/binary-arm64/Packages e62ff0123a74adfc6903d59a449cbdb0 40 restricted/binary-armhf/Packages.gz 05a2a1d284b1f093d602cff1b16f23dd 64 restricted/binary-armhf/Packages.xz 01f8d8e6015e1ab0a7971bcafa7d56f5 109 restricted/binary-armhf/Release d41d8cd98f00b204e9800998ecf8427e 0 restricted/binary-armhf/Packages e62ff0123a74adfc6903d59a449cbdb0 40 restricted/binary-i386/Packages.gz 05a2a1d284b1f093d602cff1b16f23dd 64 restricted/binary-i386/Packages.xz d41d8cd98f00b204e9800998ecf8427e 0 restricted/binary-i386/Packages c4084535080eccc834fe4792f8fe7681 108 restricted/binary-i386/Release e62ff0123a74adfc6903d59a449cbdb0 40 restricted/binary-ppc64el/Packages.gz d41d8cd98f00b204e9800998ecf8427e 0 restricted/binary-ppc64el/Packages 05a2a1d284b1f093d602cff1b16f23dd 64 restricted/binary-ppc64el/Packages.xz 3bd36c3da42585c8cd0b331e0aa59b30 111 restricted/binary-ppc64el/Release e62ff0123a74adfc6903d59a449cbdb0 40 restricted/binary-s390x/Packages.gz ca83008367c0556d0a12e53c527fdd6a 109 restricted/binary-s390x/Release d41d8cd98f00b204e9800998ecf8427e 0 restricted/binary-s390x/Packages 05a2a1d284b1f093d602cff1b16f23dd 64 restricted/binary-s390x/Packages.xz 05a2a1d284b1f093d602cff1b16f23dd 64 restricted/debian-installer/binary-amd64/Packages.xz d41d8cd98f00b204e9800998ecf8427e 0 restricted/debian-installer/binary-amd64/Packages e62ff0123a74adfc6903d59a449cbdb0 40 restricted/debian-installer/binary-amd64/Packages.gz e62ff0123a74adfc6903d59a449cbdb0 40 restricted/debian-installer/binary-arm64/Packages.gz 05a2a1d284b1f093d602cff1b16f23dd 64 restricted/debian-installer/binary-arm64/Packages.xz d41d8cd98f00b204e9800998ecf8427e 0 restricted/debian-installer/binary-arm64/Packages e62ff0123a74adfc6903d59a449cbdb0 40 restricted/debian-installer/binary-armhf/Packages.gz d41d8cd98f00b204e9800998ecf8427e 0 restricted/debian-installer/binary-armhf/Packages 05a2a1d284b1f093d602cff1b16f23dd 64 restricted/debian-installer/binary-armhf/Packages.xz 05a2a1d284b1f093d602cff1b16f23dd 64 restricted/debian-installer/binary-i386/Packages.xz d41d8cd98f00b204e9800998ecf8427e 0 restricted/debian-installer/binary-i386/Packages e62ff0123a74adfc6903d59a449cbdb0 40 restricted/debian-installer/binary-i386/Packages.gz 05a2a1d284b1f093d602cff1b16f23dd 64 restricted/debian-installer/binary-ppc64el/Packages.xz d41d8cd98f00b204e9800998ecf8427e 0 restricted/debian-installer/binary-ppc64el/Packages e62ff0123a74adfc6903d59a449cbdb0 40 restricted/debian-installer/binary-ppc64el/Packages.gz 05a2a1d284b1f093d602cff1b16f23dd 64 restricted/debian-installer/binary-s390x/Packages.xz e62ff0123a74adfc6903d59a449cbdb0 40 restricted/debian-installer/binary-s390x/Packages.gz d41d8cd98f00b204e9800998ecf8427e 0 restricted/debian-installer/binary-s390x/Packages 1d9563e57bb2119461c1cae8863d0c8e 185 restricted/dep11/Components-arm64.yml 7581f281195f7de431e41c7ed413ded1 193 restricted/dep11/Components-amd64.yml.gz 1d9563e57bb2119461c1cae8863d0c8e 185 restricted/dep11/Components-armhf.yml 30ff84dba7e918730640b17475acd601 195 restricted/dep11/Components-ppc64el.yml.gz 53b6b0755f8bf1f5101e540c97fbe3b0 193 restricted/dep11/Components-s390x.yml.gz 1d9563e57bb2119461c1cae8863d0c8e 185 restricted/dep11/Components-s390x.yml 1d9563e57bb2119461c1cae8863d0c8e 185 restricted/dep11/Components-amd64.yml 1d9563e57bb2119461c1cae8863d0c8e 185 restricted/dep11/Components-ppc64el.yml 20882b6062bb4faa5356c745ff4a070b 192 restricted/dep11/Components-i386.yml.gz 3699e90df09d6f015dc7b45a839a81d4 193 restricted/dep11/Components-arm64.yml.gz 271082486d72b948525f7fbedceddf37 193 restricted/dep11/Components-armhf.yml.gz 1d9563e57bb2119461c1cae8863d0c8e 185 restricted/dep11/Components-i386.yml 83f113e5a6e4d07ca337fc40833470ee 192 restricted/i18n/Index d41d8cd98f00b204e9800998ecf8427e 0 restricted/i18n/Translation-en e62ff0123a74adfc6903d59a449cbdb0 40 restricted/i18n/Translation-en.gz 05a2a1d284b1f093d602cff1b16f23dd 64 restricted/i18n/Translation-en.xz 2819041faa2373ede941d8d188032338 110 restricted/source/Release d41d8cd98f00b204e9800998ecf8427e 0 restricted/source/Sources e62ff0123a74adfc6903d59a449cbdb0 40 restricted/source/Sources.gz 05a2a1d284b1f093d602cff1b16f23dd 64 restricted/source/Sources.xz d41d8cd98f00b204e9800998ecf8427e 0 universe/binary-amd64/Packages 05a2a1d284b1f093d602cff1b16f23dd 64 universe/binary-amd64/Packages.xz 4809710ebab965bd73ea07db2599fec6 107 universe/binary-amd64/Release e62ff0123a74adfc6903d59a449cbdb0 40 universe/binary-amd64/Packages.gz 05a2a1d284b1f093d602cff1b16f23dd 64 universe/binary-arm64/Packages.xz e62ff0123a74adfc6903d59a449cbdb0 40 universe/binary-arm64/Packages.gz b95b33caeede3aab8a9b9917e859a30b 107 universe/binary-arm64/Release d41d8cd98f00b204e9800998ecf8427e 0 universe/binary-arm64/Packages e62ff0123a74adfc6903d59a449cbdb0 40 universe/binary-armhf/Packages.gz 1472d880e9d9674788bafc690b76b418 107 universe/binary-armhf/Release 05a2a1d284b1f093d602cff1b16f23dd 64 universe/binary-armhf/Packages.xz d41d8cd98f00b204e9800998ecf8427e 0 universe/binary-armhf/Packages 025e459ad92bf55a0defeab90ad058e7 106 universe/binary-i386/Release d41d8cd98f00b204e9800998ecf8427e 0 universe/binary-i386/Packages 05a2a1d284b1f093d602cff1b16f23dd 64 universe/binary-i386/Packages.xz e62ff0123a74adfc6903d59a449cbdb0 40 universe/binary-i386/Packages.gz e62ff0123a74adfc6903d59a449cbdb0 40 universe/binary-ppc64el/Packages.gz 05a2a1d284b1f093d602cff1b16f23dd 64 universe/binary-ppc64el/Packages.xz 77b8015992ac4208f1b790b4c6a19521 109 universe/binary-ppc64el/Release d41d8cd98f00b204e9800998ecf8427e 0 universe/binary-ppc64el/Packages d41d8cd98f00b204e9800998ecf8427e 0 universe/binary-s390x/Packages 4be0f7f3fb4c02e939fb310541e4215c 107 universe/binary-s390x/Release e62ff0123a74adfc6903d59a449cbdb0 40 universe/binary-s390x/Packages.gz 05a2a1d284b1f093d602cff1b16f23dd 64 universe/binary-s390x/Packages.xz d41d8cd98f00b204e9800998ecf8427e 0 universe/debian-installer/binary-amd64/Packages e62ff0123a74adfc6903d59a449cbdb0 40 universe/debian-installer/binary-amd64/Packages.gz 05a2a1d284b1f093d602cff1b16f23dd 64 universe/debian-installer/binary-amd64/Packages.xz 05a2a1d284b1f093d602cff1b16f23dd 64 universe/debian-installer/binary-arm64/Packages.xz e62ff0123a74adfc6903d59a449cbdb0 40 universe/debian-installer/binary-arm64/Packages.gz d41d8cd98f00b204e9800998ecf8427e 0 universe/debian-installer/binary-arm64/Packages d41d8cd98f00b204e9800998ecf8427e 0 universe/debian-installer/binary-armhf/Packages e62ff0123a74adfc6903d59a449cbdb0 40 universe/debian-installer/binary-armhf/Packages.gz 05a2a1d284b1f093d602cff1b16f23dd 64 universe/debian-installer/binary-armhf/Packages.xz 05a2a1d284b1f093d602cff1b16f23dd 64 universe/debian-installer/binary-i386/Packages.xz e62ff0123a74adfc6903d59a449cbdb0 40 universe/debian-installer/binary-i386/Packages.gz d41d8cd98f00b204e9800998ecf8427e 0 universe/debian-installer/binary-i386/Packages d41d8cd98f00b204e9800998ecf8427e 0 universe/debian-installer/binary-ppc64el/Packages e62ff0123a74adfc6903d59a449cbdb0 40 universe/debian-installer/binary-ppc64el/Packages.gz 05a2a1d284b1f093d602cff1b16f23dd 64 universe/debian-installer/binary-ppc64el/Packages.xz e62ff0123a74adfc6903d59a449cbdb0 40 universe/debian-installer/binary-s390x/Packages.gz 05a2a1d284b1f093d602cff1b16f23dd 64 universe/debian-installer/binary-s390x/Packages.xz d41d8cd98f00b204e9800998ecf8427e 0 universe/debian-installer/binary-s390x/Packages 0f343b0931126a20f133d67c2b018a3b 1024 universe/dep11/icons-128x128.tar d9f4b4316ab5294f94501d435359c7a4 212 universe/dep11/Components-ppc64el.yml.xz 48db98c85168a4c064517aad8d96ad5c 212 universe/dep11/Components-s390x.yml.xz b7c271bbe3ababbbcd3a7be28847829e 212 universe/dep11/Components-armhf.yml.xz 31f6566d35ccd604be46ed5b1f813cdf 29 universe/dep11/icons-64x64.tar.gz 3ad6793bd536673fd4d270b3c3a9793d 168 universe/dep11/Components-i386.yml e7be84b75402f29623d5702299c6485f 159 universe/dep11/Components-armhf.yml.gz 3cd0458409024567bfbb5f22c6c815fd 158 universe/dep11/Components-ppc64el.yml.gz 31f6566d35ccd604be46ed5b1f813cdf 29 universe/dep11/icons-128x128.tar.gz 5ba9c1973daeb505c295566b1befdaee 159 universe/dep11/Components-powerpc.yml.gz 88e3e6da357a2e63b22bf0e1f406266d 168 universe/dep11/Components-powerpc.yml e08cd1ae60d1330016c7264e871c2520 159 universe/dep11/Components-s390x.yml.gz ec5e4ca6416a4fc30f74a3f944be3021 212 universe/dep11/Components-amd64.yml.xz ccc682b957113f0e45e1337cb834a53e 159 universe/dep11/Components-i386.yml.gz 815983c409210b058ad2a9ca5b9457c7 168 universe/dep11/Components-arm64.yml 1cbc9bb155c24c0837c16555bc5e3e25 212 universe/dep11/Components-powerpc.yml.xz 3b8525d5f3dd95612a2db2f2a63a6ba2 212 universe/dep11/Components-arm64.yml.xz d297bcffa2936a5c717bb4acea44e53f 159 universe/dep11/Components-arm64.yml.gz f6fcae6f88ddee0049ee468a1ba7d1af 168 universe/dep11/Components-s390x.yml 0f343b0931126a20f133d67c2b018a3b 1024 universe/dep11/icons-64x64.tar c2e47b5c36d7f9ffc302838bcb141188 168 universe/dep11/Components-ppc64el.yml 2ba5be4714bd52446fc7364b1154b2d4 212 universe/dep11/Components-i386.yml.xz bd4b25dad377474fe4539bde31d934ed 168 universe/dep11/Components-armhf.yml a3a0d80a622521d57fcf98e3263f89fd 159 universe/dep11/Components-amd64.yml.gz f1dbe7f5fb56cd96e213f0858b03e467 168 universe/dep11/Components-amd64.yml d41d8cd98f00b204e9800998ecf8427e 0 universe/i18n/Translation-en e62ff0123a74adfc6903d59a449cbdb0 40 universe/i18n/Translation-en.gz 83f113e5a6e4d07ca337fc40833470ee 192 universe/i18n/Index 05a2a1d284b1f093d602cff1b16f23dd 64 universe/i18n/Translation-en.xz d41d8cd98f00b204e9800998ecf8427e 0 universe/source/Sources 05a2a1d284b1f093d602cff1b16f23dd 64 universe/source/Sources.xz e62ff0123a74adfc6903d59a449cbdb0 40 universe/source/Sources.gz b66064a5569daf693735d5cbdc455a5b 108 universe/source/Release SHA1: 2c879fcb3d471450d31020e6bd0492269299eb11 695 Contents-armhf.gz c82143474d47c5b30e4e362acac8a9944bf03f74 1416 Contents-armhf c82143474d47c5b30e4e362acac8a9944bf03f74 1416 Contents-ppc64el 2c879fcb3d471450d31020e6bd0492269299eb11 695 Contents-ppc64el.gz c82143474d47c5b30e4e362acac8a9944bf03f74 1416 Contents-arm64 c82143474d47c5b30e4e362acac8a9944bf03f74 1416 Contents-amd64 2c879fcb3d471450d31020e6bd0492269299eb11 695 Contents-arm64.gz 2c879fcb3d471450d31020e6bd0492269299eb11 695 Contents-amd64.gz c82143474d47c5b30e4e362acac8a9944bf03f74 1416 Contents-s390x c82143474d47c5b30e4e362acac8a9944bf03f74 1416 Contents-i386 2c879fcb3d471450d31020e6bd0492269299eb11 695 Contents-s390x.gz 2c879fcb3d471450d31020e6bd0492269299eb11 695 Contents-i386.gz da39a3ee5e6b4b0d3255bfef95601890afd80709 0 main/binary-amd64/Packages 97b962297a1c704ddb43fa8469aa3bd298ffa925 103 main/binary-amd64/Release e3f4c61a216c2c9613cd3bdd1420dde095b296b3 40 main/binary-amd64/Packages.gz 8f7b953c891feb6c6ff36727888b9fae846b49d5 64 main/binary-amd64/Packages.xz c7a84a86970ceda96c6e5959830f8527d875e603 103 main/binary-arm64/Release da39a3ee5e6b4b0d3255bfef95601890afd80709 0 main/binary-arm64/Packages 8f7b953c891feb6c6ff36727888b9fae846b49d5 64 main/binary-arm64/Packages.xz e3f4c61a216c2c9613cd3bdd1420dde095b296b3 40 main/binary-arm64/Packages.gz e5ac4cfc6fbfc2af212737c9794e37b945c33f6d 103 main/binary-armhf/Release da39a3ee5e6b4b0d3255bfef95601890afd80709 0 main/binary-armhf/Packages 8f7b953c891feb6c6ff36727888b9fae846b49d5 64 main/binary-armhf/Packages.xz e3f4c61a216c2c9613cd3bdd1420dde095b296b3 40 main/binary-armhf/Packages.gz 8f7b953c891feb6c6ff36727888b9fae846b49d5 64 main/binary-i386/Packages.xz d3960ddc2228aacb5ba8277326d0e4b44d1b5a0b 102 main/binary-i386/Release e3f4c61a216c2c9613cd3bdd1420dde095b296b3 40 main/binary-i386/Packages.gz da39a3ee5e6b4b0d3255bfef95601890afd80709 0 main/binary-i386/Packages 8f7b953c891feb6c6ff36727888b9fae846b49d5 64 main/binary-ppc64el/Packages.xz 0dbc5f367ec4dd46d5ec2cb2880da6133d9343a7 105 main/binary-ppc64el/Release e3f4c61a216c2c9613cd3bdd1420dde095b296b3 40 main/binary-ppc64el/Packages.gz da39a3ee5e6b4b0d3255bfef95601890afd80709 0 main/binary-ppc64el/Packages c861357b9d9309ac48ad8aeac2d13e6d35ccf2e3 103 main/binary-s390x/Release e3f4c61a216c2c9613cd3bdd1420dde095b296b3 40 main/binary-s390x/Packages.gz da39a3ee5e6b4b0d3255bfef95601890afd80709 0 main/binary-s390x/Packages 8f7b953c891feb6c6ff36727888b9fae846b49d5 64 main/binary-s390x/Packages.xz e3f4c61a216c2c9613cd3bdd1420dde095b296b3 40 main/debian-installer/binary-amd64/Packages.gz da39a3ee5e6b4b0d3255bfef95601890afd80709 0 main/debian-installer/binary-amd64/Packages 8f7b953c891feb6c6ff36727888b9fae846b49d5 64 main/debian-installer/binary-amd64/Packages.xz da39a3ee5e6b4b0d3255bfef95601890afd80709 0 main/debian-installer/binary-arm64/Packages e3f4c61a216c2c9613cd3bdd1420dde095b296b3 40 main/debian-installer/binary-arm64/Packages.gz 8f7b953c891feb6c6ff36727888b9fae846b49d5 64 main/debian-installer/binary-arm64/Packages.xz e3f4c61a216c2c9613cd3bdd1420dde095b296b3 40 main/debian-installer/binary-armhf/Packages.gz 8f7b953c891feb6c6ff36727888b9fae846b49d5 64 main/debian-installer/binary-armhf/Packages.xz da39a3ee5e6b4b0d3255bfef95601890afd80709 0 main/debian-installer/binary-armhf/Packages da39a3ee5e6b4b0d3255bfef95601890afd80709 0 main/debian-installer/binary-i386/Packages e3f4c61a216c2c9613cd3bdd1420dde095b296b3 40 main/debian-installer/binary-i386/Packages.gz 8f7b953c891feb6c6ff36727888b9fae846b49d5 64 main/debian-installer/binary-i386/Packages.xz 8f7b953c891feb6c6ff36727888b9fae846b49d5 64 main/debian-installer/binary-ppc64el/Packages.xz e3f4c61a216c2c9613cd3bdd1420dde095b296b3 40 main/debian-installer/binary-ppc64el/Packages.gz da39a3ee5e6b4b0d3255bfef95601890afd80709 0 main/debian-installer/binary-ppc64el/Packages 8f7b953c891feb6c6ff36727888b9fae846b49d5 64 main/debian-installer/binary-s390x/Packages.xz e3f4c61a216c2c9613cd3bdd1420dde095b296b3 40 main/debian-installer/binary-s390x/Packages.gz da39a3ee5e6b4b0d3255bfef95601890afd80709 0 main/debian-installer/binary-s390x/Packages 851e39e03cba132064f1c7ac4c827e9cfcad64d7 156 main/dep11/Components-armhf.yml.gz 6526b598d89fe2e26836de6625f8ee382ea87a54 208 main/dep11/Components-s390x.yml.xz 60cacbf3d72e1e7834203da608037b1bf83b40e8 1024 main/dep11/icons-128x128.tar c50afaeb71543fbcb23861d5357ca7ba1a439a3b 208 main/dep11/Components-powerpc.yml.xz 3dae71dff151c46a798f321616948fa5932198eb 208 main/dep11/Components-arm64.yml.xz aa27f39a547726f9f7afd043711756ae57a5feb8 29 main/dep11/icons-128x128.tar.gz 40b0029afdc841da2de66cae7505be157f3e803c 156 main/dep11/Components-s390x.yml.gz fe74f4ee4ec928cde2584917f02191f517666d6f 164 main/dep11/Components-arm64.yml 55b6e603ef4c5f1182b7c5196cdf03737a86fb50 164 main/dep11/Components-armhf.yml 495bd43a765bbacc0174a26c6d0c463ce4e42b19 164 main/dep11/Components-s390x.yml ad7d58397b2587baf5bc001ecf8d8aeac32809fa 208 main/dep11/Components-amd64.yml.xz 84ba2de12648a7ae7f12a5e9c43398f6b630dc56 208 main/dep11/Components-ppc64el.yml.xz 12f0a0f68da7749819cdfa9405a682fa7873d069 156 main/dep11/Components-arm64.yml.gz 82fd659cf263d08aa9b849ced214acdaaacf6734 164 main/dep11/Components-i386.yml 40e91fab8024aa563c4a9da90fe262a8cf0f21ee 208 main/dep11/Components-i386.yml.xz 0cb327bc78cbfc4aa35731fcf2933115186aee01 164 main/dep11/Components-ppc64el.yml 60cacbf3d72e1e7834203da608037b1bf83b40e8 1024 main/dep11/icons-64x64.tar 8f9415e27b7a4d064e12b9b7a52e703b04de0702 156 main/dep11/Components-powerpc.yml.gz 1c28d36f1f568704557a70d0df3b427bfb3b16eb 164 main/dep11/Components-powerpc.yml b95d0a623fd2d14dde459559cd9956433d14f52a 164 main/dep11/Components-amd64.yml 868a0f58783d47b85f4fd42607279c5452764e7b 156 main/dep11/Components-ppc64el.yml.gz aa27f39a547726f9f7afd043711756ae57a5feb8 29 main/dep11/icons-64x64.tar.gz 864d323276d1f222ae6315472da789722fccd173 156 main/dep11/Components-i386.yml.gz 5fe792f1a67342e803f3fc5c836fe0487b16d78d 156 main/dep11/Components-amd64.yml.gz 76e6a48f55eb8f919065bff9be0163dae74a46a3 208 main/dep11/Components-armhf.yml.xz e3f4c61a216c2c9613cd3bdd1420dde095b296b3 40 main/i18n/Translation-en.gz 8f7b953c891feb6c6ff36727888b9fae846b49d5 64 main/i18n/Translation-en.xz da39a3ee5e6b4b0d3255bfef95601890afd80709 0 main/i18n/Translation-en 1d57840eb63860f364ad5d68178069658a9dba9a 192 main/i18n/Index b1940df5056c7e046e0ec0df4bd3d1ee2c922792 104 main/source/Release da39a3ee5e6b4b0d3255bfef95601890afd80709 0 main/source/Sources e3f4c61a216c2c9613cd3bdd1420dde095b296b3 40 main/source/Sources.gz 8f7b953c891feb6c6ff36727888b9fae846b49d5 64 main/source/Sources.xz e3f4c61a216c2c9613cd3bdd1420dde095b296b3 40 multiverse/binary-amd64/Packages.gz 8f7b953c891feb6c6ff36727888b9fae846b49d5 64 multiverse/binary-amd64/Packages.xz 0395274e28c295a08f6c59c82bc96e852bfd4d99 109 multiverse/binary-amd64/Release da39a3ee5e6b4b0d3255bfef95601890afd80709 0 multiverse/binary-amd64/Packages e3f4c61a216c2c9613cd3bdd1420dde095b296b3 40 multiverse/binary-arm64/Packages.gz da39a3ee5e6b4b0d3255bfef95601890afd80709 0 multiverse/binary-arm64/Packages 2e7039b84ad8c9b22ae9709e8ba99a46c2f363d6 109 multiverse/binary-arm64/Release 8f7b953c891feb6c6ff36727888b9fae846b49d5 64 multiverse/binary-arm64/Packages.xz e3e468b5ccc12d3debd7e31bbddac72a80b06834 109 multiverse/binary-armhf/Release e3f4c61a216c2c9613cd3bdd1420dde095b296b3 40 multiverse/binary-armhf/Packages.gz 8f7b953c891feb6c6ff36727888b9fae846b49d5 64 multiverse/binary-armhf/Packages.xz da39a3ee5e6b4b0d3255bfef95601890afd80709 0 multiverse/binary-armhf/Packages e3f4c61a216c2c9613cd3bdd1420dde095b296b3 40 multiverse/binary-i386/Packages.gz da39a3ee5e6b4b0d3255bfef95601890afd80709 0 multiverse/binary-i386/Packages 8f7b953c891feb6c6ff36727888b9fae846b49d5 64 multiverse/binary-i386/Packages.xz cedd151bc0cd42113b0b1773d28f6ccb829567b2 108 multiverse/binary-i386/Release da39a3ee5e6b4b0d3255bfef95601890afd80709 0 multiverse/binary-ppc64el/Packages e3f4c61a216c2c9613cd3bdd1420dde095b296b3 40 multiverse/binary-ppc64el/Packages.gz 9c8f085d9b63255bd3f6f0fe2e07aca328cebcb3 111 multiverse/binary-ppc64el/Release 8f7b953c891feb6c6ff36727888b9fae846b49d5 64 multiverse/binary-ppc64el/Packages.xz e74aca93ccccdebbb04fc7e62de6335dfc6b66a4 109 multiverse/binary-s390x/Release da39a3ee5e6b4b0d3255bfef95601890afd80709 0 multiverse/binary-s390x/Packages e3f4c61a216c2c9613cd3bdd1420dde095b296b3 40 multiverse/binary-s390x/Packages.gz 8f7b953c891feb6c6ff36727888b9fae846b49d5 64 multiverse/binary-s390x/Packages.xz 8f7b953c891feb6c6ff36727888b9fae846b49d5 64 multiverse/debian-installer/binary-amd64/Packages.xz da39a3ee5e6b4b0d3255bfef95601890afd80709 0 multiverse/debian-installer/binary-amd64/Packages e3f4c61a216c2c9613cd3bdd1420dde095b296b3 40 multiverse/debian-installer/binary-amd64/Packages.gz da39a3ee5e6b4b0d3255bfef95601890afd80709 0 multiverse/debian-installer/binary-arm64/Packages 8f7b953c891feb6c6ff36727888b9fae846b49d5 64 multiverse/debian-installer/binary-arm64/Packages.xz e3f4c61a216c2c9613cd3bdd1420dde095b296b3 40 multiverse/debian-installer/binary-arm64/Packages.gz da39a3ee5e6b4b0d3255bfef95601890afd80709 0 multiverse/debian-installer/binary-armhf/Packages e3f4c61a216c2c9613cd3bdd1420dde095b296b3 40 multiverse/debian-installer/binary-armhf/Packages.gz 8f7b953c891feb6c6ff36727888b9fae846b49d5 64 multiverse/debian-installer/binary-armhf/Packages.xz 8f7b953c891feb6c6ff36727888b9fae846b49d5 64 multiverse/debian-installer/binary-i386/Packages.xz e3f4c61a216c2c9613cd3bdd1420dde095b296b3 40 multiverse/debian-installer/binary-i386/Packages.gz da39a3ee5e6b4b0d3255bfef95601890afd80709 0 multiverse/debian-installer/binary-i386/Packages da39a3ee5e6b4b0d3255bfef95601890afd80709 0 multiverse/debian-installer/binary-ppc64el/Packages e3f4c61a216c2c9613cd3bdd1420dde095b296b3 40 multiverse/debian-installer/binary-ppc64el/Packages.gz 8f7b953c891feb6c6ff36727888b9fae846b49d5 64 multiverse/debian-installer/binary-ppc64el/Packages.xz da39a3ee5e6b4b0d3255bfef95601890afd80709 0 multiverse/debian-installer/binary-s390x/Packages e3f4c61a216c2c9613cd3bdd1420dde095b296b3 40 multiverse/debian-installer/binary-s390x/Packages.gz 8f7b953c891feb6c6ff36727888b9fae846b49d5 64 multiverse/debian-installer/binary-s390x/Packages.xz 3de326aa7ea62a2523453a589b322bcb1e34b01d 160 multiverse/dep11/Components-armhf.yml.gz aa27f39a547726f9f7afd043711756ae57a5feb8 29 multiverse/dep11/icons-64x64.tar.gz 4285a05208fa2d2433decb5d549b40b0d08f1e74 212 multiverse/dep11/Components-armhf.yml.xz cee7686e6ad23b7fe556f36f88c7f58b81ef7e0c 212 multiverse/dep11/Components-ppc64el.yml.xz b0d41b27771b3973d74e664ac6acbee73e513387 170 multiverse/dep11/Components-i386.yml c44a50a22787bfa48887bd1516276fe8fec693f7 160 multiverse/dep11/Components-s390x.yml.gz aa27f39a547726f9f7afd043711756ae57a5feb8 29 multiverse/dep11/icons-128x128.tar.gz 88115316f9bb8918c4584f7a5a063afb50f2b3f7 212 multiverse/dep11/Components-amd64.yml.xz 5c1c3c3239ae2435d0f07aa53eec231902b388c8 170 multiverse/dep11/Components-armhf.yml e9d007b178b219bdce92e13d797ed3341add07dc 170 multiverse/dep11/Components-arm64.yml 9b3fbd26ce21b03e662984f478705832482193b9 212 multiverse/dep11/Components-powerpc.yml.xz d72d2ead29a88f2a218e9792ed1535fef7f842b3 160 multiverse/dep11/Components-arm64.yml.gz 6f53a81935b3722fdc70a7b76c5e38bccf04e4f6 212 multiverse/dep11/Components-s390x.yml.xz c38c6de26cc3c0189bd0cfdef0fee8fd1472e59c 160 multiverse/dep11/Components-i386.yml.gz 936447cc3ebe3f615fa769bc7ccb37fe0eac4e84 159 multiverse/dep11/Components-ppc64el.yml.gz 9ce577d6486bf000046b4d4c10d3dd5f73a03877 160 multiverse/dep11/Components-amd64.yml.gz 60cacbf3d72e1e7834203da608037b1bf83b40e8 1024 multiverse/dep11/icons-128x128.tar a655b589ccf10105c30c116ca53764af0a620122 212 multiverse/dep11/Components-i386.yml.xz f3c952352503be295abf0b1f9ed83d1b37848682 159 multiverse/dep11/Components-powerpc.yml.gz 60cacbf3d72e1e7834203da608037b1bf83b40e8 1024 multiverse/dep11/icons-64x64.tar 84039d0d2dd427e49c76d3cb163829b258dfc456 170 multiverse/dep11/Components-ppc64el.yml b39c02408165fb854f68c3e5ed90abeba9f2deb2 170 multiverse/dep11/Components-amd64.yml 71854ad0213720145be375d9509f8993a7b4c32b 212 multiverse/dep11/Components-arm64.yml.xz ba622fb794f009501740ba2009a2a80b984745ae 170 multiverse/dep11/Components-s390x.yml 972b9d5726c0ce4ed0ebe88b73cc5ad4b839ceb4 170 multiverse/dep11/Components-powerpc.yml e3f4c61a216c2c9613cd3bdd1420dde095b296b3 40 multiverse/i18n/Translation-en.gz da39a3ee5e6b4b0d3255bfef95601890afd80709 0 multiverse/i18n/Translation-en 8f7b953c891feb6c6ff36727888b9fae846b49d5 64 multiverse/i18n/Translation-en.xz 1d57840eb63860f364ad5d68178069658a9dba9a 192 multiverse/i18n/Index e3f4c61a216c2c9613cd3bdd1420dde095b296b3 40 multiverse/source/Sources.gz 8f7b953c891feb6c6ff36727888b9fae846b49d5 64 multiverse/source/Sources.xz da39a3ee5e6b4b0d3255bfef95601890afd80709 0 multiverse/source/Sources e9473be01d1990f2af9eb185573099a3532ec061 110 multiverse/source/Release da39a3ee5e6b4b0d3255bfef95601890afd80709 0 restricted/binary-amd64/Packages e3f4c61a216c2c9613cd3bdd1420dde095b296b3 40 restricted/binary-amd64/Packages.gz 15a8a0d275cbfde6c973274ec32e3e05c0136a21 109 restricted/binary-amd64/Release 8f7b953c891feb6c6ff36727888b9fae846b49d5 64 restricted/binary-amd64/Packages.xz af60ccbdbef4ccd6a291d65583b97f696517f614 109 restricted/binary-arm64/Release 8f7b953c891feb6c6ff36727888b9fae846b49d5 64 restricted/binary-arm64/Packages.xz e3f4c61a216c2c9613cd3bdd1420dde095b296b3 40 restricted/binary-arm64/Packages.gz da39a3ee5e6b4b0d3255bfef95601890afd80709 0 restricted/binary-arm64/Packages e3f4c61a216c2c9613cd3bdd1420dde095b296b3 40 restricted/binary-armhf/Packages.gz 8f7b953c891feb6c6ff36727888b9fae846b49d5 64 restricted/binary-armhf/Packages.xz a5f5ebf2f0230f70cd26f618ccb4579e6e636dcb 109 restricted/binary-armhf/Release da39a3ee5e6b4b0d3255bfef95601890afd80709 0 restricted/binary-armhf/Packages e3f4c61a216c2c9613cd3bdd1420dde095b296b3 40 restricted/binary-i386/Packages.gz 8f7b953c891feb6c6ff36727888b9fae846b49d5 64 restricted/binary-i386/Packages.xz da39a3ee5e6b4b0d3255bfef95601890afd80709 0 restricted/binary-i386/Packages a5d94d0769b7a0cd1272b185d2445528ab5270ff 108 restricted/binary-i386/Release e3f4c61a216c2c9613cd3bdd1420dde095b296b3 40 restricted/binary-ppc64el/Packages.gz da39a3ee5e6b4b0d3255bfef95601890afd80709 0 restricted/binary-ppc64el/Packages 8f7b953c891feb6c6ff36727888b9fae846b49d5 64 restricted/binary-ppc64el/Packages.xz 9b21107e19fbe9a281b31b99461c829274b651c0 111 restricted/binary-ppc64el/Release e3f4c61a216c2c9613cd3bdd1420dde095b296b3 40 restricted/binary-s390x/Packages.gz 350dfa82ce988377c0865a6db30fdd295465e680 109 restricted/binary-s390x/Release da39a3ee5e6b4b0d3255bfef95601890afd80709 0 restricted/binary-s390x/Packages 8f7b953c891feb6c6ff36727888b9fae846b49d5 64 restricted/binary-s390x/Packages.xz 8f7b953c891feb6c6ff36727888b9fae846b49d5 64 restricted/debian-installer/binary-amd64/Packages.xz da39a3ee5e6b4b0d3255bfef95601890afd80709 0 restricted/debian-installer/binary-amd64/Packages e3f4c61a216c2c9613cd3bdd1420dde095b296b3 40 restricted/debian-installer/binary-amd64/Packages.gz e3f4c61a216c2c9613cd3bdd1420dde095b296b3 40 restricted/debian-installer/binary-arm64/Packages.gz 8f7b953c891feb6c6ff36727888b9fae846b49d5 64 restricted/debian-installer/binary-arm64/Packages.xz da39a3ee5e6b4b0d3255bfef95601890afd80709 0 restricted/debian-installer/binary-arm64/Packages e3f4c61a216c2c9613cd3bdd1420dde095b296b3 40 restricted/debian-installer/binary-armhf/Packages.gz da39a3ee5e6b4b0d3255bfef95601890afd80709 0 restricted/debian-installer/binary-armhf/Packages 8f7b953c891feb6c6ff36727888b9fae846b49d5 64 restricted/debian-installer/binary-armhf/Packages.xz 8f7b953c891feb6c6ff36727888b9fae846b49d5 64 restricted/debian-installer/binary-i386/Packages.xz da39a3ee5e6b4b0d3255bfef95601890afd80709 0 restricted/debian-installer/binary-i386/Packages e3f4c61a216c2c9613cd3bdd1420dde095b296b3 40 restricted/debian-installer/binary-i386/Packages.gz 8f7b953c891feb6c6ff36727888b9fae846b49d5 64 restricted/debian-installer/binary-ppc64el/Packages.xz da39a3ee5e6b4b0d3255bfef95601890afd80709 0 restricted/debian-installer/binary-ppc64el/Packages e3f4c61a216c2c9613cd3bdd1420dde095b296b3 40 restricted/debian-installer/binary-ppc64el/Packages.gz 8f7b953c891feb6c6ff36727888b9fae846b49d5 64 restricted/debian-installer/binary-s390x/Packages.xz e3f4c61a216c2c9613cd3bdd1420dde095b296b3 40 restricted/debian-installer/binary-s390x/Packages.gz da39a3ee5e6b4b0d3255bfef95601890afd80709 0 restricted/debian-installer/binary-s390x/Packages 004f1a8144d7f023b397a2b72a7e6147869be784 185 restricted/dep11/Components-arm64.yml 89e557923babea0ac51f908188aaa0dc6edf1248 193 restricted/dep11/Components-amd64.yml.gz 004f1a8144d7f023b397a2b72a7e6147869be784 185 restricted/dep11/Components-armhf.yml 47e2dd1d3008a66db6e91468d614487490d2ee92 195 restricted/dep11/Components-ppc64el.yml.gz 0540459419464e979cec66ba3e1d70d049e46c23 193 restricted/dep11/Components-s390x.yml.gz 004f1a8144d7f023b397a2b72a7e6147869be784 185 restricted/dep11/Components-s390x.yml 004f1a8144d7f023b397a2b72a7e6147869be784 185 restricted/dep11/Components-amd64.yml 004f1a8144d7f023b397a2b72a7e6147869be784 185 restricted/dep11/Components-ppc64el.yml 10956f4af4282fee33da9a60ff03d84074a01494 192 restricted/dep11/Components-i386.yml.gz 48905f8e17b8dc6e365843223acf8211394c3ea5 193 restricted/dep11/Components-arm64.yml.gz 36f81c016a2c54cff565775e2f42ae43388c7ed4 193 restricted/dep11/Components-armhf.yml.gz 004f1a8144d7f023b397a2b72a7e6147869be784 185 restricted/dep11/Components-i386.yml 1d57840eb63860f364ad5d68178069658a9dba9a 192 restricted/i18n/Index da39a3ee5e6b4b0d3255bfef95601890afd80709 0 restricted/i18n/Translation-en e3f4c61a216c2c9613cd3bdd1420dde095b296b3 40 restricted/i18n/Translation-en.gz 8f7b953c891feb6c6ff36727888b9fae846b49d5 64 restricted/i18n/Translation-en.xz a5d86c27bbc13ad17f7a55373e858ee4e971f297 110 restricted/source/Release da39a3ee5e6b4b0d3255bfef95601890afd80709 0 restricted/source/Sources e3f4c61a216c2c9613cd3bdd1420dde095b296b3 40 restricted/source/Sources.gz 8f7b953c891feb6c6ff36727888b9fae846b49d5 64 restricted/source/Sources.xz da39a3ee5e6b4b0d3255bfef95601890afd80709 0 universe/binary-amd64/Packages 8f7b953c891feb6c6ff36727888b9fae846b49d5 64 universe/binary-amd64/Packages.xz 7a0d07a062cdf9764f1e1a9ab6bbe15dbb1804da 107 universe/binary-amd64/Release e3f4c61a216c2c9613cd3bdd1420dde095b296b3 40 universe/binary-amd64/Packages.gz 8f7b953c891feb6c6ff36727888b9fae846b49d5 64 universe/binary-arm64/Packages.xz e3f4c61a216c2c9613cd3bdd1420dde095b296b3 40 universe/binary-arm64/Packages.gz da5f7f652e146bee1d645bf077dc6a716cc3c154 107 universe/binary-arm64/Release da39a3ee5e6b4b0d3255bfef95601890afd80709 0 universe/binary-arm64/Packages e3f4c61a216c2c9613cd3bdd1420dde095b296b3 40 universe/binary-armhf/Packages.gz 283560a8dc23772f101a1cd64faa5837ce04e725 107 universe/binary-armhf/Release 8f7b953c891feb6c6ff36727888b9fae846b49d5 64 universe/binary-armhf/Packages.xz da39a3ee5e6b4b0d3255bfef95601890afd80709 0 universe/binary-armhf/Packages 8a7a5edcf4f568320951c8406932ca2a2c2bef51 106 universe/binary-i386/Release da39a3ee5e6b4b0d3255bfef95601890afd80709 0 universe/binary-i386/Packages 8f7b953c891feb6c6ff36727888b9fae846b49d5 64 universe/binary-i386/Packages.xz e3f4c61a216c2c9613cd3bdd1420dde095b296b3 40 universe/binary-i386/Packages.gz e3f4c61a216c2c9613cd3bdd1420dde095b296b3 40 universe/binary-ppc64el/Packages.gz 8f7b953c891feb6c6ff36727888b9fae846b49d5 64 universe/binary-ppc64el/Packages.xz 54151b6578703386a9de0c6900b652cc18aca34c 109 universe/binary-ppc64el/Release da39a3ee5e6b4b0d3255bfef95601890afd80709 0 universe/binary-ppc64el/Packages da39a3ee5e6b4b0d3255bfef95601890afd80709 0 universe/binary-s390x/Packages 51889f02ee18fef351b6476a898334d4c1dc1a23 107 universe/binary-s390x/Release e3f4c61a216c2c9613cd3bdd1420dde095b296b3 40 universe/binary-s390x/Packages.gz 8f7b953c891feb6c6ff36727888b9fae846b49d5 64 universe/binary-s390x/Packages.xz da39a3ee5e6b4b0d3255bfef95601890afd80709 0 universe/debian-installer/binary-amd64/Packages e3f4c61a216c2c9613cd3bdd1420dde095b296b3 40 universe/debian-installer/binary-amd64/Packages.gz 8f7b953c891feb6c6ff36727888b9fae846b49d5 64 universe/debian-installer/binary-amd64/Packages.xz 8f7b953c891feb6c6ff36727888b9fae846b49d5 64 universe/debian-installer/binary-arm64/Packages.xz e3f4c61a216c2c9613cd3bdd1420dde095b296b3 40 universe/debian-installer/binary-arm64/Packages.gz da39a3ee5e6b4b0d3255bfef95601890afd80709 0 universe/debian-installer/binary-arm64/Packages da39a3ee5e6b4b0d3255bfef95601890afd80709 0 universe/debian-installer/binary-armhf/Packages e3f4c61a216c2c9613cd3bdd1420dde095b296b3 40 universe/debian-installer/binary-armhf/Packages.gz 8f7b953c891feb6c6ff36727888b9fae846b49d5 64 universe/debian-installer/binary-armhf/Packages.xz 8f7b953c891feb6c6ff36727888b9fae846b49d5 64 universe/debian-installer/binary-i386/Packages.xz e3f4c61a216c2c9613cd3bdd1420dde095b296b3 40 universe/debian-installer/binary-i386/Packages.gz da39a3ee5e6b4b0d3255bfef95601890afd80709 0 universe/debian-installer/binary-i386/Packages da39a3ee5e6b4b0d3255bfef95601890afd80709 0 universe/debian-installer/binary-ppc64el/Packages e3f4c61a216c2c9613cd3bdd1420dde095b296b3 40 universe/debian-installer/binary-ppc64el/Packages.gz 8f7b953c891feb6c6ff36727888b9fae846b49d5 64 universe/debian-installer/binary-ppc64el/Packages.xz e3f4c61a216c2c9613cd3bdd1420dde095b296b3 40 universe/debian-installer/binary-s390x/Packages.gz 8f7b953c891feb6c6ff36727888b9fae846b49d5 64 universe/debian-installer/binary-s390x/Packages.xz da39a3ee5e6b4b0d3255bfef95601890afd80709 0 universe/debian-installer/binary-s390x/Packages 60cacbf3d72e1e7834203da608037b1bf83b40e8 1024 universe/dep11/icons-128x128.tar b214a2e85379c1a105172099eee0254b26766d46 212 universe/dep11/Components-ppc64el.yml.xz 4da550de259edd904f005199a8514ca4e8e5b4d8 212 universe/dep11/Components-s390x.yml.xz 396152d200cb60b6c12f5085c88d26d72af5465a 212 universe/dep11/Components-armhf.yml.xz aa27f39a547726f9f7afd043711756ae57a5feb8 29 universe/dep11/icons-64x64.tar.gz 48092c4ee3640754d5b117be3c7b75ffaed9e6c6 168 universe/dep11/Components-i386.yml 445f551cb26fdce4591c6c459c77c4b6b197787f 159 universe/dep11/Components-armhf.yml.gz caeaf925808ba7a988924fba9c5fd2b164b90bb0 158 universe/dep11/Components-ppc64el.yml.gz aa27f39a547726f9f7afd043711756ae57a5feb8 29 universe/dep11/icons-128x128.tar.gz 8319f16486df49af5736d82f035ff22f869dd319 159 universe/dep11/Components-powerpc.yml.gz 2dcd43838b14e47f56f90074cecd3fd62108d9fc 168 universe/dep11/Components-powerpc.yml 1d095238d3caff573725701351817a8e9c75ddfa 159 universe/dep11/Components-s390x.yml.gz c02daf3eb32c48a09d5e2ae4bbf12350b1aa735a 212 universe/dep11/Components-amd64.yml.xz 93b2f2999a12e0a3dc43712ac7bb16c2bd75c43a 159 universe/dep11/Components-i386.yml.gz aa27a20d8d6e0e690504dc873c58b8db7e8ce646 168 universe/dep11/Components-arm64.yml 7c55a0a485ad19edeab3e0ed75437791e4a8eb08 212 universe/dep11/Components-powerpc.yml.xz cec876cd4c49c4cff2a693cd08a35190799000d6 212 universe/dep11/Components-arm64.yml.xz 9b68a322dbc4b4f17f77372ec46287763dffa92a 159 universe/dep11/Components-arm64.yml.gz 126e4ca28937aa03990bd9ba6ba1cba96c2c51b4 168 universe/dep11/Components-s390x.yml 60cacbf3d72e1e7834203da608037b1bf83b40e8 1024 universe/dep11/icons-64x64.tar ecebb99f2c5fbbb7fac710e7e1e64fb13b34c150 168 universe/dep11/Components-ppc64el.yml 758c8bffdf82ca3ec8dd21f03e66de2cfe8594f3 212 universe/dep11/Components-i386.yml.xz 9ac0ce60079b401997353a52da6f8837dc650ef0 168 universe/dep11/Components-armhf.yml 60840408aa292209e3d0436991341d950fac846e 159 universe/dep11/Components-amd64.yml.gz 2f0ccbc919e0dcbf22f49ecf05128d1eb8be0240 168 universe/dep11/Components-amd64.yml da39a3ee5e6b4b0d3255bfef95601890afd80709 0 universe/i18n/Translation-en e3f4c61a216c2c9613cd3bdd1420dde095b296b3 40 universe/i18n/Translation-en.gz 1d57840eb63860f364ad5d68178069658a9dba9a 192 universe/i18n/Index 8f7b953c891feb6c6ff36727888b9fae846b49d5 64 universe/i18n/Translation-en.xz da39a3ee5e6b4b0d3255bfef95601890afd80709 0 universe/source/Sources 8f7b953c891feb6c6ff36727888b9fae846b49d5 64 universe/source/Sources.xz e3f4c61a216c2c9613cd3bdd1420dde095b296b3 40 universe/source/Sources.gz 2b7a2e5b55652139efcc80961de977bb9adc53da 108 universe/source/Release SHA256: f4b4696e464d8e50409edb1eae347deb38c10d58486391e9dd374a03081bf6a3 695 Contents-armhf.gz 170d8d14eca6da1611be301c1489325b06f9f7179a2833456e06aa0469a58271 1416 Contents-armhf 170d8d14eca6da1611be301c1489325b06f9f7179a2833456e06aa0469a58271 1416 Contents-ppc64el f4b4696e464d8e50409edb1eae347deb38c10d58486391e9dd374a03081bf6a3 695 Contents-ppc64el.gz 170d8d14eca6da1611be301c1489325b06f9f7179a2833456e06aa0469a58271 1416 Contents-arm64 170d8d14eca6da1611be301c1489325b06f9f7179a2833456e06aa0469a58271 1416 Contents-amd64 f4b4696e464d8e50409edb1eae347deb38c10d58486391e9dd374a03081bf6a3 695 Contents-arm64.gz f4b4696e464d8e50409edb1eae347deb38c10d58486391e9dd374a03081bf6a3 695 Contents-amd64.gz 170d8d14eca6da1611be301c1489325b06f9f7179a2833456e06aa0469a58271 1416 Contents-s390x 170d8d14eca6da1611be301c1489325b06f9f7179a2833456e06aa0469a58271 1416 Contents-i386 f4b4696e464d8e50409edb1eae347deb38c10d58486391e9dd374a03081bf6a3 695 Contents-s390x.gz f4b4696e464d8e50409edb1eae347deb38c10d58486391e9dd374a03081bf6a3 695 Contents-i386.gz e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 main/binary-amd64/Packages c0dd76328e5100f3103b3209c1634ecb4eb942d28aa2c3304b0c48415b061a44 103 main/binary-amd64/Release e7ab72b8f37c7c9c9f6386fb8e3dfa40bf6fe4b67876703c5927e47cb8664ce4 40 main/binary-amd64/Packages.gz 65de7e01eff8cf2f9a287153971467b1c11811213b64a38bdf673bad240436ff 64 main/binary-amd64/Packages.xz 7a9810d25ba3c9eb6fa071535d9c8732683afb41877268a7a41148fc6212be1d 103 main/binary-arm64/Release e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 main/binary-arm64/Packages 65de7e01eff8cf2f9a287153971467b1c11811213b64a38bdf673bad240436ff 64 main/binary-arm64/Packages.xz e7ab72b8f37c7c9c9f6386fb8e3dfa40bf6fe4b67876703c5927e47cb8664ce4 40 main/binary-arm64/Packages.gz fdea71a135849604866e73d95ac5364eedd6e057c15cc92d2ff03922fb2a23a2 103 main/binary-armhf/Release e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 main/binary-armhf/Packages 65de7e01eff8cf2f9a287153971467b1c11811213b64a38bdf673bad240436ff 64 main/binary-armhf/Packages.xz e7ab72b8f37c7c9c9f6386fb8e3dfa40bf6fe4b67876703c5927e47cb8664ce4 40 main/binary-armhf/Packages.gz 65de7e01eff8cf2f9a287153971467b1c11811213b64a38bdf673bad240436ff 64 main/binary-i386/Packages.xz 076aa6c2f01f279711ebc8d983b6370a734e09f0b4465622cc21323dc61258e7 102 main/binary-i386/Release e7ab72b8f37c7c9c9f6386fb8e3dfa40bf6fe4b67876703c5927e47cb8664ce4 40 main/binary-i386/Packages.gz e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 main/binary-i386/Packages 65de7e01eff8cf2f9a287153971467b1c11811213b64a38bdf673bad240436ff 64 main/binary-ppc64el/Packages.xz dfc991824a783be2424c45030e4d41d352d7955b61c3de9a921252830f180e2d 105 main/binary-ppc64el/Release e7ab72b8f37c7c9c9f6386fb8e3dfa40bf6fe4b67876703c5927e47cb8664ce4 40 main/binary-ppc64el/Packages.gz e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 main/binary-ppc64el/Packages f493d8f3660c85f80a301fd39e202d8a625448c4b7708a219a1ec719eebf0c9e 103 main/binary-s390x/Release e7ab72b8f37c7c9c9f6386fb8e3dfa40bf6fe4b67876703c5927e47cb8664ce4 40 main/binary-s390x/Packages.gz e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 main/binary-s390x/Packages 65de7e01eff8cf2f9a287153971467b1c11811213b64a38bdf673bad240436ff 64 main/binary-s390x/Packages.xz e7ab72b8f37c7c9c9f6386fb8e3dfa40bf6fe4b67876703c5927e47cb8664ce4 40 main/debian-installer/binary-amd64/Packages.gz e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 main/debian-installer/binary-amd64/Packages 65de7e01eff8cf2f9a287153971467b1c11811213b64a38bdf673bad240436ff 64 main/debian-installer/binary-amd64/Packages.xz e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 main/debian-installer/binary-arm64/Packages e7ab72b8f37c7c9c9f6386fb8e3dfa40bf6fe4b67876703c5927e47cb8664ce4 40 main/debian-installer/binary-arm64/Packages.gz 65de7e01eff8cf2f9a287153971467b1c11811213b64a38bdf673bad240436ff 64 main/debian-installer/binary-arm64/Packages.xz e7ab72b8f37c7c9c9f6386fb8e3dfa40bf6fe4b67876703c5927e47cb8664ce4 40 main/debian-installer/binary-armhf/Packages.gz 65de7e01eff8cf2f9a287153971467b1c11811213b64a38bdf673bad240436ff 64 main/debian-installer/binary-armhf/Packages.xz e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 main/debian-installer/binary-armhf/Packages e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 main/debian-installer/binary-i386/Packages e7ab72b8f37c7c9c9f6386fb8e3dfa40bf6fe4b67876703c5927e47cb8664ce4 40 main/debian-installer/binary-i386/Packages.gz 65de7e01eff8cf2f9a287153971467b1c11811213b64a38bdf673bad240436ff 64 main/debian-installer/binary-i386/Packages.xz 65de7e01eff8cf2f9a287153971467b1c11811213b64a38bdf673bad240436ff 64 main/debian-installer/binary-ppc64el/Packages.xz e7ab72b8f37c7c9c9f6386fb8e3dfa40bf6fe4b67876703c5927e47cb8664ce4 40 main/debian-installer/binary-ppc64el/Packages.gz e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 main/debian-installer/binary-ppc64el/Packages 65de7e01eff8cf2f9a287153971467b1c11811213b64a38bdf673bad240436ff 64 main/debian-installer/binary-s390x/Packages.xz e7ab72b8f37c7c9c9f6386fb8e3dfa40bf6fe4b67876703c5927e47cb8664ce4 40 main/debian-installer/binary-s390x/Packages.gz e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 main/debian-installer/binary-s390x/Packages afa853bff55edf90a800dad9924685edace36c60246f80da48355921a7004dda 156 main/dep11/Components-armhf.yml.gz 86660295b572919f5973ac2f2a05ef289de6490bfb00ac3e041994e8156f746e 208 main/dep11/Components-s390x.yml.xz 5f70bf18a086007016e948b04aed3b82103a36bea41755b6cddfaf10ace3c6ef 1024 main/dep11/icons-128x128.tar dab0fe1a29f35a998297732d2d0aadafc5d803f8697eb6bdb2c60968964687d7 208 main/dep11/Components-powerpc.yml.xz 6fa5c059763c0f58ca740ce124191bbc1f34caa88e5e772598c7e7d638970a52 208 main/dep11/Components-arm64.yml.xz 7989bb311baa38ef545250282aa065d23281c46dfb8faabe4c653487bdbded5c 29 main/dep11/icons-128x128.tar.gz ae46664deaf6ea0a58f268fbddf5fbb256061c82657e9c90d87a1ed7b91c687a 156 main/dep11/Components-s390x.yml.gz 0ecd16061fb6fe59a95941338b7127a4c43fc392938048b4581e4bbda92852c4 164 main/dep11/Components-arm64.yml 9e97dbbd19ad7cbbc8c3eb4e6caeed206219ff23e1592426598839a067fe4d1e 164 main/dep11/Components-armhf.yml d01f311bbd135187844c1ad17a626a9a1a6e4b39d4f28e5df2f8f0096d5bfb81 164 main/dep11/Components-s390x.yml d2c7adad253ffc01f3edb52830c60fc4c0c9807ca8cc1789fa5bafbe3018a82e 208 main/dep11/Components-amd64.yml.xz ace5bf3cff4bab9646fbf78970164ca725a0d9694f6a1ebe0801d886d9d68084 208 main/dep11/Components-ppc64el.yml.xz e0aa2212f8c4e1bb4336502d63d92ed17e15c6ef0bede630cdf716140258018e 156 main/dep11/Components-arm64.yml.gz 2dd1f89f391a813bf6c4261ae25c7fb02d784975e57db5ce1be1f91b2887d4d2 164 main/dep11/Components-i386.yml 55eecf7211a6af51490556995566dd8a191a0fa22afdb7cd82ba4e52e7208cf0 208 main/dep11/Components-i386.yml.xz 205e2068d89792aa279519f35ff73ff1842168a0ec4cc27eb476ff468d5d47df 164 main/dep11/Components-ppc64el.yml 5f70bf18a086007016e948b04aed3b82103a36bea41755b6cddfaf10ace3c6ef 1024 main/dep11/icons-64x64.tar a44256d7a35854c019c6fa7ea34c685dc40792190e678fa99e1ce87101619ca7 156 main/dep11/Components-powerpc.yml.gz 7f83334ba24389a0df335cc90ede51b6c1b1cb2de4fa40ee671ddddfdbe04362 164 main/dep11/Components-powerpc.yml ddca6b28eb11b686b652bcbeb155e50610605cf7fdb0cb1d4f6499da9344ac87 164 main/dep11/Components-amd64.yml cd66854bcc7f359ab817d0800d34f784df06afa7b8cc897e99cfd16f62b1f462 156 main/dep11/Components-ppc64el.yml.gz 7989bb311baa38ef545250282aa065d23281c46dfb8faabe4c653487bdbded5c 29 main/dep11/icons-64x64.tar.gz 9ca49dc222807369461b502286611ffd7b9ae3afc991dcc7b18d8e2335ab5a32 156 main/dep11/Components-i386.yml.gz af7d5f3cacee916cd4d27a288803888948e0532d7f5c5fd07090bcb636eb9bed 156 main/dep11/Components-amd64.yml.gz 59126c1dcfeb0ba42f31081a3746c203cdbe615180c5ad91bdbee0ac72a9f9d9 208 main/dep11/Components-armhf.yml.xz e7ab72b8f37c7c9c9f6386fb8e3dfa40bf6fe4b67876703c5927e47cb8664ce4 40 main/i18n/Translation-en.gz 65de7e01eff8cf2f9a287153971467b1c11811213b64a38bdf673bad240436ff 64 main/i18n/Translation-en.xz e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 main/i18n/Translation-en aea852a7918a6c61c359cd57114f60ca95551f449f06cb1c9f296b8326b78101 192 main/i18n/Index 10f3c7f40819ceead0072b084772123cfbb71e34bc9b79bce51d241cbeaabb78 104 main/source/Release e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 main/source/Sources e7ab72b8f37c7c9c9f6386fb8e3dfa40bf6fe4b67876703c5927e47cb8664ce4 40 main/source/Sources.gz 65de7e01eff8cf2f9a287153971467b1c11811213b64a38bdf673bad240436ff 64 main/source/Sources.xz e7ab72b8f37c7c9c9f6386fb8e3dfa40bf6fe4b67876703c5927e47cb8664ce4 40 multiverse/binary-amd64/Packages.gz 65de7e01eff8cf2f9a287153971467b1c11811213b64a38bdf673bad240436ff 64 multiverse/binary-amd64/Packages.xz 70f3e88210ab4c261cb9fadd69d4ab4e6380979461285f37ec543497bbd1738f 109 multiverse/binary-amd64/Release e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 multiverse/binary-amd64/Packages e7ab72b8f37c7c9c9f6386fb8e3dfa40bf6fe4b67876703c5927e47cb8664ce4 40 multiverse/binary-arm64/Packages.gz e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 multiverse/binary-arm64/Packages 86cee4f7b02ceace16dda79b1e1ba1ab17417c17d50040803bc6e3fa621ba7dc 109 multiverse/binary-arm64/Release 65de7e01eff8cf2f9a287153971467b1c11811213b64a38bdf673bad240436ff 64 multiverse/binary-arm64/Packages.xz ec343a438e9f7dc2190f5c19ebbd55d581170aacab1049de7067949b0f8e4db2 109 multiverse/binary-armhf/Release e7ab72b8f37c7c9c9f6386fb8e3dfa40bf6fe4b67876703c5927e47cb8664ce4 40 multiverse/binary-armhf/Packages.gz 65de7e01eff8cf2f9a287153971467b1c11811213b64a38bdf673bad240436ff 64 multiverse/binary-armhf/Packages.xz e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 multiverse/binary-armhf/Packages e7ab72b8f37c7c9c9f6386fb8e3dfa40bf6fe4b67876703c5927e47cb8664ce4 40 multiverse/binary-i386/Packages.gz e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 multiverse/binary-i386/Packages 65de7e01eff8cf2f9a287153971467b1c11811213b64a38bdf673bad240436ff 64 multiverse/binary-i386/Packages.xz 642a5cda6e7517b554671c0da90fb68a1d23a436148177fb54d54eaeeaafc020 108 multiverse/binary-i386/Release e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 multiverse/binary-ppc64el/Packages e7ab72b8f37c7c9c9f6386fb8e3dfa40bf6fe4b67876703c5927e47cb8664ce4 40 multiverse/binary-ppc64el/Packages.gz 1845d88d795056c706947ad08e58120780f4677fa4bd7bcffc10538fd8ba73e4 111 multiverse/binary-ppc64el/Release 65de7e01eff8cf2f9a287153971467b1c11811213b64a38bdf673bad240436ff 64 multiverse/binary-ppc64el/Packages.xz d4ea08d9d5712a93799617474b78b05a9e15d70395bce791239a94d3ba6ee2d8 109 multiverse/binary-s390x/Release e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 multiverse/binary-s390x/Packages e7ab72b8f37c7c9c9f6386fb8e3dfa40bf6fe4b67876703c5927e47cb8664ce4 40 multiverse/binary-s390x/Packages.gz 65de7e01eff8cf2f9a287153971467b1c11811213b64a38bdf673bad240436ff 64 multiverse/binary-s390x/Packages.xz 65de7e01eff8cf2f9a287153971467b1c11811213b64a38bdf673bad240436ff 64 multiverse/debian-installer/binary-amd64/Packages.xz e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 multiverse/debian-installer/binary-amd64/Packages e7ab72b8f37c7c9c9f6386fb8e3dfa40bf6fe4b67876703c5927e47cb8664ce4 40 multiverse/debian-installer/binary-amd64/Packages.gz e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 multiverse/debian-installer/binary-arm64/Packages 65de7e01eff8cf2f9a287153971467b1c11811213b64a38bdf673bad240436ff 64 multiverse/debian-installer/binary-arm64/Packages.xz e7ab72b8f37c7c9c9f6386fb8e3dfa40bf6fe4b67876703c5927e47cb8664ce4 40 multiverse/debian-installer/binary-arm64/Packages.gz e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 multiverse/debian-installer/binary-armhf/Packages e7ab72b8f37c7c9c9f6386fb8e3dfa40bf6fe4b67876703c5927e47cb8664ce4 40 multiverse/debian-installer/binary-armhf/Packages.gz 65de7e01eff8cf2f9a287153971467b1c11811213b64a38bdf673bad240436ff 64 multiverse/debian-installer/binary-armhf/Packages.xz 65de7e01eff8cf2f9a287153971467b1c11811213b64a38bdf673bad240436ff 64 multiverse/debian-installer/binary-i386/Packages.xz e7ab72b8f37c7c9c9f6386fb8e3dfa40bf6fe4b67876703c5927e47cb8664ce4 40 multiverse/debian-installer/binary-i386/Packages.gz e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 multiverse/debian-installer/binary-i386/Packages e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 multiverse/debian-installer/binary-ppc64el/Packages e7ab72b8f37c7c9c9f6386fb8e3dfa40bf6fe4b67876703c5927e47cb8664ce4 40 multiverse/debian-installer/binary-ppc64el/Packages.gz 65de7e01eff8cf2f9a287153971467b1c11811213b64a38bdf673bad240436ff 64 multiverse/debian-installer/binary-ppc64el/Packages.xz e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 multiverse/debian-installer/binary-s390x/Packages e7ab72b8f37c7c9c9f6386fb8e3dfa40bf6fe4b67876703c5927e47cb8664ce4 40 multiverse/debian-installer/binary-s390x/Packages.gz 65de7e01eff8cf2f9a287153971467b1c11811213b64a38bdf673bad240436ff 64 multiverse/debian-installer/binary-s390x/Packages.xz 46a6f48f8aea81f4bd76f1c579329ced669351142854a7653caf161bc056224f 160 multiverse/dep11/Components-armhf.yml.gz 7989bb311baa38ef545250282aa065d23281c46dfb8faabe4c653487bdbded5c 29 multiverse/dep11/icons-64x64.tar.gz 7efc535da833779e4af36f0935d03a28bbac3b3cd8df087834fd932a42328b2e 212 multiverse/dep11/Components-armhf.yml.xz 0648c7a0f7337de83380d872bc692701d782c824f65db6734edd45f23d959526 212 multiverse/dep11/Components-ppc64el.yml.xz 6e2e3533720c07dadc6dfa99736d85ade4a44b4604283760fe592f0e731f47a2 170 multiverse/dep11/Components-i386.yml 74e400fa9da540e23736f795a3ef6857b5dfec8dd88ca12e784d17545241b1f5 160 multiverse/dep11/Components-s390x.yml.gz 7989bb311baa38ef545250282aa065d23281c46dfb8faabe4c653487bdbded5c 29 multiverse/dep11/icons-128x128.tar.gz 1b127e371a253d10f7491026c7a5278de362a48cde4117bbb7457ccef680592e 212 multiverse/dep11/Components-amd64.yml.xz 2e452c148d8a0540a2748d2c37d8f6962100c8b62055fc3770a8390a4aa059ae 170 multiverse/dep11/Components-armhf.yml df229da30d17df1f942f15a434f7dbacecfadd3517123a4d1d16df7498630df8 170 multiverse/dep11/Components-arm64.yml a6624cecabb006c95323a2574b2056f24cb259dc3ad243f3f16cc5b018f62e58 212 multiverse/dep11/Components-powerpc.yml.xz 01b7ea61717594c7a17a98a7596e650567fd3d7233dfe085a0a6a5b695cd1acc 160 multiverse/dep11/Components-arm64.yml.gz 1658e250ade16fa0b88d52cb00b5f96dec5d65a313d6c810d2b168da9a27df13 212 multiverse/dep11/Components-s390x.yml.xz 7a1571b6e411f4006942e118337ff762f7726fad6f7cd4ee9bfba78f78df3d7d 160 multiverse/dep11/Components-i386.yml.gz 137f6ec387ed4407d813da03a63ce39fc5f9d362a583b95ecff6a890d632ad68 159 multiverse/dep11/Components-ppc64el.yml.gz dbc595e3bccb2b4129e125c109253972e456588be2c72a96af3fd7b07f3c9151 160 multiverse/dep11/Components-amd64.yml.gz 5f70bf18a086007016e948b04aed3b82103a36bea41755b6cddfaf10ace3c6ef 1024 multiverse/dep11/icons-128x128.tar 3a143f7d353eb42bd0ab9935b153b9865e1be5bd003ca32b1cf4b6c8c2c130fb 212 multiverse/dep11/Components-i386.yml.xz b884e0fa5b1d3ed047b9232a3bf674478cdd73ecabb4d5f0872409170b283a94 159 multiverse/dep11/Components-powerpc.yml.gz 5f70bf18a086007016e948b04aed3b82103a36bea41755b6cddfaf10ace3c6ef 1024 multiverse/dep11/icons-64x64.tar 18a870fd9ecfb350e17d141c57cc92c2c5f81592d53cfa324c265a6b94ad2ccd 170 multiverse/dep11/Components-ppc64el.yml 177cdaa6fb2dfce560ec991c51128523df8626167ec5399539dcd8be2b377d97 170 multiverse/dep11/Components-amd64.yml adbdbbace61e34c08c1d67700cb576fabacba43b7b68016725eb3d721c4d04a9 212 multiverse/dep11/Components-arm64.yml.xz 4e6c388fa2aa26845dc95f2daaeadffcb0115dc6f11456f495c8fed103d74d94 170 multiverse/dep11/Components-s390x.yml 4caef1d09bfcb71c0ad78e30b6b87a529648d46110edc2dfc3cf170764e04ca6 170 multiverse/dep11/Components-powerpc.yml e7ab72b8f37c7c9c9f6386fb8e3dfa40bf6fe4b67876703c5927e47cb8664ce4 40 multiverse/i18n/Translation-en.gz e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 multiverse/i18n/Translation-en 65de7e01eff8cf2f9a287153971467b1c11811213b64a38bdf673bad240436ff 64 multiverse/i18n/Translation-en.xz aea852a7918a6c61c359cd57114f60ca95551f449f06cb1c9f296b8326b78101 192 multiverse/i18n/Index e7ab72b8f37c7c9c9f6386fb8e3dfa40bf6fe4b67876703c5927e47cb8664ce4 40 multiverse/source/Sources.gz 65de7e01eff8cf2f9a287153971467b1c11811213b64a38bdf673bad240436ff 64 multiverse/source/Sources.xz e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 multiverse/source/Sources fb39de5c83517a08c482d0c3f369be383de6d1fe04e92f93a5e462c650846530 110 multiverse/source/Release e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 restricted/binary-amd64/Packages e7ab72b8f37c7c9c9f6386fb8e3dfa40bf6fe4b67876703c5927e47cb8664ce4 40 restricted/binary-amd64/Packages.gz e792d703a4aaf927cc6aed9d7896253a1607e4d4517b6f8043ffd037fd5de580 109 restricted/binary-amd64/Release 65de7e01eff8cf2f9a287153971467b1c11811213b64a38bdf673bad240436ff 64 restricted/binary-amd64/Packages.xz fb4573c406d10bcf0f1df1969449802e309279a2e4dba09212945f768a5ddff9 109 restricted/binary-arm64/Release 65de7e01eff8cf2f9a287153971467b1c11811213b64a38bdf673bad240436ff 64 restricted/binary-arm64/Packages.xz e7ab72b8f37c7c9c9f6386fb8e3dfa40bf6fe4b67876703c5927e47cb8664ce4 40 restricted/binary-arm64/Packages.gz e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 restricted/binary-arm64/Packages e7ab72b8f37c7c9c9f6386fb8e3dfa40bf6fe4b67876703c5927e47cb8664ce4 40 restricted/binary-armhf/Packages.gz 65de7e01eff8cf2f9a287153971467b1c11811213b64a38bdf673bad240436ff 64 restricted/binary-armhf/Packages.xz 0e1158efbc2b13d2173d09c295bd9ff59abb568f9caac5514b30e264d51bb27d 109 restricted/binary-armhf/Release e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 restricted/binary-armhf/Packages e7ab72b8f37c7c9c9f6386fb8e3dfa40bf6fe4b67876703c5927e47cb8664ce4 40 restricted/binary-i386/Packages.gz 65de7e01eff8cf2f9a287153971467b1c11811213b64a38bdf673bad240436ff 64 restricted/binary-i386/Packages.xz e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 restricted/binary-i386/Packages 8265e48aa494f2c16e7e9b05c12aba8d0cbfe1f6d301ccd80e25e9de0cacf58a 108 restricted/binary-i386/Release e7ab72b8f37c7c9c9f6386fb8e3dfa40bf6fe4b67876703c5927e47cb8664ce4 40 restricted/binary-ppc64el/Packages.gz e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 restricted/binary-ppc64el/Packages 65de7e01eff8cf2f9a287153971467b1c11811213b64a38bdf673bad240436ff 64 restricted/binary-ppc64el/Packages.xz 7419494b81b1a724937da60ebd9ee6895d8b9bed4926048b4c156f736d29b498 111 restricted/binary-ppc64el/Release e7ab72b8f37c7c9c9f6386fb8e3dfa40bf6fe4b67876703c5927e47cb8664ce4 40 restricted/binary-s390x/Packages.gz e9d5e1442194bab9feec15754e85cec9fb49f03e678dae88a83c722eecbf8db9 109 restricted/binary-s390x/Release e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 restricted/binary-s390x/Packages 65de7e01eff8cf2f9a287153971467b1c11811213b64a38bdf673bad240436ff 64 restricted/binary-s390x/Packages.xz 65de7e01eff8cf2f9a287153971467b1c11811213b64a38bdf673bad240436ff 64 restricted/debian-installer/binary-amd64/Packages.xz e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 restricted/debian-installer/binary-amd64/Packages e7ab72b8f37c7c9c9f6386fb8e3dfa40bf6fe4b67876703c5927e47cb8664ce4 40 restricted/debian-installer/binary-amd64/Packages.gz e7ab72b8f37c7c9c9f6386fb8e3dfa40bf6fe4b67876703c5927e47cb8664ce4 40 restricted/debian-installer/binary-arm64/Packages.gz 65de7e01eff8cf2f9a287153971467b1c11811213b64a38bdf673bad240436ff 64 restricted/debian-installer/binary-arm64/Packages.xz e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 restricted/debian-installer/binary-arm64/Packages e7ab72b8f37c7c9c9f6386fb8e3dfa40bf6fe4b67876703c5927e47cb8664ce4 40 restricted/debian-installer/binary-armhf/Packages.gz e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 restricted/debian-installer/binary-armhf/Packages 65de7e01eff8cf2f9a287153971467b1c11811213b64a38bdf673bad240436ff 64 restricted/debian-installer/binary-armhf/Packages.xz 65de7e01eff8cf2f9a287153971467b1c11811213b64a38bdf673bad240436ff 64 restricted/debian-installer/binary-i386/Packages.xz e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 restricted/debian-installer/binary-i386/Packages e7ab72b8f37c7c9c9f6386fb8e3dfa40bf6fe4b67876703c5927e47cb8664ce4 40 restricted/debian-installer/binary-i386/Packages.gz 65de7e01eff8cf2f9a287153971467b1c11811213b64a38bdf673bad240436ff 64 restricted/debian-installer/binary-ppc64el/Packages.xz e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 restricted/debian-installer/binary-ppc64el/Packages e7ab72b8f37c7c9c9f6386fb8e3dfa40bf6fe4b67876703c5927e47cb8664ce4 40 restricted/debian-installer/binary-ppc64el/Packages.gz 65de7e01eff8cf2f9a287153971467b1c11811213b64a38bdf673bad240436ff 64 restricted/debian-installer/binary-s390x/Packages.xz e7ab72b8f37c7c9c9f6386fb8e3dfa40bf6fe4b67876703c5927e47cb8664ce4 40 restricted/debian-installer/binary-s390x/Packages.gz e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 restricted/debian-installer/binary-s390x/Packages b3c3dcae030c11651aaee486cd61a02fa1c8d9d306d856758e13adbe58ac4486 185 restricted/dep11/Components-arm64.yml 211672efb527870236f13cd24b999b20cb6fd1eaa41df2ac22644520aba57ef6 193 restricted/dep11/Components-amd64.yml.gz b3c3dcae030c11651aaee486cd61a02fa1c8d9d306d856758e13adbe58ac4486 185 restricted/dep11/Components-armhf.yml e08bfb8001fe8448907e64efa77ed68687acceba7bdf6a2f9013cb42ed193bf9 195 restricted/dep11/Components-ppc64el.yml.gz d35455d8a0709f52842b976acf91a7401bd601831de19b15336be7d082b19ebb 193 restricted/dep11/Components-s390x.yml.gz b3c3dcae030c11651aaee486cd61a02fa1c8d9d306d856758e13adbe58ac4486 185 restricted/dep11/Components-s390x.yml b3c3dcae030c11651aaee486cd61a02fa1c8d9d306d856758e13adbe58ac4486 185 restricted/dep11/Components-amd64.yml b3c3dcae030c11651aaee486cd61a02fa1c8d9d306d856758e13adbe58ac4486 185 restricted/dep11/Components-ppc64el.yml d9261cd240210b36a1c113d44ecdcc9f0c6dc17ab67ce1ffc82d10b97633d041 192 restricted/dep11/Components-i386.yml.gz e863ace85973a6544bdd695c8ca85d34f6dfd0f07f034a2edcdf7992fb7f73fd 193 restricted/dep11/Components-arm64.yml.gz f711ab672837d3af106b9a4f23c513e659621df8681e20119096b3b51be27ead 193 restricted/dep11/Components-armhf.yml.gz b3c3dcae030c11651aaee486cd61a02fa1c8d9d306d856758e13adbe58ac4486 185 restricted/dep11/Components-i386.yml aea852a7918a6c61c359cd57114f60ca95551f449f06cb1c9f296b8326b78101 192 restricted/i18n/Index e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 restricted/i18n/Translation-en e7ab72b8f37c7c9c9f6386fb8e3dfa40bf6fe4b67876703c5927e47cb8664ce4 40 restricted/i18n/Translation-en.gz 65de7e01eff8cf2f9a287153971467b1c11811213b64a38bdf673bad240436ff 64 restricted/i18n/Translation-en.xz f9ceb7cca4c66f7c4e02a6822f766d8f82c53c9b70ffe6d7f8bdc7dad12642af 110 restricted/source/Release e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 restricted/source/Sources e7ab72b8f37c7c9c9f6386fb8e3dfa40bf6fe4b67876703c5927e47cb8664ce4 40 restricted/source/Sources.gz 65de7e01eff8cf2f9a287153971467b1c11811213b64a38bdf673bad240436ff 64 restricted/source/Sources.xz e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 universe/binary-amd64/Packages 65de7e01eff8cf2f9a287153971467b1c11811213b64a38bdf673bad240436ff 64 universe/binary-amd64/Packages.xz bb767e55d40e43348103df023f84659d144ea26024c600e8cb883a154fd7f261 107 universe/binary-amd64/Release e7ab72b8f37c7c9c9f6386fb8e3dfa40bf6fe4b67876703c5927e47cb8664ce4 40 universe/binary-amd64/Packages.gz 65de7e01eff8cf2f9a287153971467b1c11811213b64a38bdf673bad240436ff 64 universe/binary-arm64/Packages.xz e7ab72b8f37c7c9c9f6386fb8e3dfa40bf6fe4b67876703c5927e47cb8664ce4 40 universe/binary-arm64/Packages.gz 3fe8e2af23a46475b19f3d554f758338b61d7d38ea56388c196922cef9a67236 107 universe/binary-arm64/Release e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 universe/binary-arm64/Packages e7ab72b8f37c7c9c9f6386fb8e3dfa40bf6fe4b67876703c5927e47cb8664ce4 40 universe/binary-armhf/Packages.gz 29c60b9d256aae19cd602d044f990c7f7fbce4d242a8486ad8a848c0adb76ef1 107 universe/binary-armhf/Release 65de7e01eff8cf2f9a287153971467b1c11811213b64a38bdf673bad240436ff 64 universe/binary-armhf/Packages.xz e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 universe/binary-armhf/Packages 3c99a1a9f0c79c92d56d03b2895763c247e7a77de4eae08c7c0bb232694f8a5d 106 universe/binary-i386/Release e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 universe/binary-i386/Packages 65de7e01eff8cf2f9a287153971467b1c11811213b64a38bdf673bad240436ff 64 universe/binary-i386/Packages.xz e7ab72b8f37c7c9c9f6386fb8e3dfa40bf6fe4b67876703c5927e47cb8664ce4 40 universe/binary-i386/Packages.gz e7ab72b8f37c7c9c9f6386fb8e3dfa40bf6fe4b67876703c5927e47cb8664ce4 40 universe/binary-ppc64el/Packages.gz 65de7e01eff8cf2f9a287153971467b1c11811213b64a38bdf673bad240436ff 64 universe/binary-ppc64el/Packages.xz 937c250625b6fa4233fb9e9c823d8c1669b2cf41cf998d53c133bbfc27ffe60e 109 universe/binary-ppc64el/Release e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 universe/binary-ppc64el/Packages e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 universe/binary-s390x/Packages f0303814e670da6a8ff16b42605c628ee3c60df110e64d66ab145c3b9d3eaba5 107 universe/binary-s390x/Release e7ab72b8f37c7c9c9f6386fb8e3dfa40bf6fe4b67876703c5927e47cb8664ce4 40 universe/binary-s390x/Packages.gz 65de7e01eff8cf2f9a287153971467b1c11811213b64a38bdf673bad240436ff 64 universe/binary-s390x/Packages.xz e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 universe/debian-installer/binary-amd64/Packages e7ab72b8f37c7c9c9f6386fb8e3dfa40bf6fe4b67876703c5927e47cb8664ce4 40 universe/debian-installer/binary-amd64/Packages.gz 65de7e01eff8cf2f9a287153971467b1c11811213b64a38bdf673bad240436ff 64 universe/debian-installer/binary-amd64/Packages.xz 65de7e01eff8cf2f9a287153971467b1c11811213b64a38bdf673bad240436ff 64 universe/debian-installer/binary-arm64/Packages.xz e7ab72b8f37c7c9c9f6386fb8e3dfa40bf6fe4b67876703c5927e47cb8664ce4 40 universe/debian-installer/binary-arm64/Packages.gz e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 universe/debian-installer/binary-arm64/Packages e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 universe/debian-installer/binary-armhf/Packages e7ab72b8f37c7c9c9f6386fb8e3dfa40bf6fe4b67876703c5927e47cb8664ce4 40 universe/debian-installer/binary-armhf/Packages.gz 65de7e01eff8cf2f9a287153971467b1c11811213b64a38bdf673bad240436ff 64 universe/debian-installer/binary-armhf/Packages.xz 65de7e01eff8cf2f9a287153971467b1c11811213b64a38bdf673bad240436ff 64 universe/debian-installer/binary-i386/Packages.xz e7ab72b8f37c7c9c9f6386fb8e3dfa40bf6fe4b67876703c5927e47cb8664ce4 40 universe/debian-installer/binary-i386/Packages.gz e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 universe/debian-installer/binary-i386/Packages e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 universe/debian-installer/binary-ppc64el/Packages e7ab72b8f37c7c9c9f6386fb8e3dfa40bf6fe4b67876703c5927e47cb8664ce4 40 universe/debian-installer/binary-ppc64el/Packages.gz 65de7e01eff8cf2f9a287153971467b1c11811213b64a38bdf673bad240436ff 64 universe/debian-installer/binary-ppc64el/Packages.xz e7ab72b8f37c7c9c9f6386fb8e3dfa40bf6fe4b67876703c5927e47cb8664ce4 40 universe/debian-installer/binary-s390x/Packages.gz 65de7e01eff8cf2f9a287153971467b1c11811213b64a38bdf673bad240436ff 64 universe/debian-installer/binary-s390x/Packages.xz e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 universe/debian-installer/binary-s390x/Packages 5f70bf18a086007016e948b04aed3b82103a36bea41755b6cddfaf10ace3c6ef 1024 universe/dep11/icons-128x128.tar a110a98f9cd66a8e07569bcfe2c078d8125e57d643e680574317e29e199cdbed 212 universe/dep11/Components-ppc64el.yml.xz 24794f666af2990afd73f7ff4ffe879bafefe0fe4ab93a262f253785a95c44a3 212 universe/dep11/Components-s390x.yml.xz 8de10d975bdf23dbc52cb4448be329eb514ac511c14541111401ad377588254c 212 universe/dep11/Components-armhf.yml.xz 7989bb311baa38ef545250282aa065d23281c46dfb8faabe4c653487bdbded5c 29 universe/dep11/icons-64x64.tar.gz 8365bc0cc164c4bb41ec537baf72c80e8fd476f9efab24394b39e794c5745769 168 universe/dep11/Components-i386.yml 74de71af2121f3920326c74c64f335ed040b56274de572d8d3591a7ac46c262f 159 universe/dep11/Components-armhf.yml.gz 9cda01194ec1cbe297ab054b90df2e667edb2d8ae07502ced6e9d8c9a63387e4 158 universe/dep11/Components-ppc64el.yml.gz 7989bb311baa38ef545250282aa065d23281c46dfb8faabe4c653487bdbded5c 29 universe/dep11/icons-128x128.tar.gz c84f03d89e60ee37035287c3d168dfa44b9d11501be0f13aecdb9b227f2631ff 159 universe/dep11/Components-powerpc.yml.gz 68f71b6e31391b2d63db7633ce220ed5f4b2888ad7f4bf1b46ad2bb6bc3cb5b7 168 universe/dep11/Components-powerpc.yml 19b3ec327c77d18fca98d58eaaef9b5b5cb9b3c81ef694848ff63bed51114766 159 universe/dep11/Components-s390x.yml.gz 19f88dd048c5bb9d4108a83f564e8a3019445855085c312d87853d31827c535b 212 universe/dep11/Components-amd64.yml.xz a091c6bf27f844d65064f22a99c1828e4f4a0d3b82ecd4bfb535a825a7ca6d0f 159 universe/dep11/Components-i386.yml.gz 089eda669a556a52ee5058eee5fd587e443306571beb53ad2417947c0b969a92 168 universe/dep11/Components-arm64.yml 294f102f8656ffd38cf1d6f0a4db53c8e90fece577d4c2a073f699ab4099572b 212 universe/dep11/Components-powerpc.yml.xz a8faf48f6fd8d8737fcbf8caff9b79bd3339c656f3c8be74b9afc54c306f4c53 212 universe/dep11/Components-arm64.yml.xz 182d54f805163ed2b25db3f1b8c9855736402f78b4f6153e49e405fdd10b52b9 159 universe/dep11/Components-arm64.yml.gz 47a270e183c981d0a4bda886e40893f4dee8c4fbe06f1fc6704f0cbcf4bde712 168 universe/dep11/Components-s390x.yml 5f70bf18a086007016e948b04aed3b82103a36bea41755b6cddfaf10ace3c6ef 1024 universe/dep11/icons-64x64.tar aa9a7548f42c2f424182282307205e828df9c6626820083ae70873c1fce8d354 168 universe/dep11/Components-ppc64el.yml b66cd0531e9fe65264ab1ff88c27d655feead4517171fd138ee659d7f4964224 212 universe/dep11/Components-i386.yml.xz d38e4960f419a9e3fc59e1879c76488aacb8322aca4b94e63cd04789118d100a 168 universe/dep11/Components-armhf.yml 0177dfb98e660f31dc420b851d1b0005b04486f2e1febf38efd70e0bf63f0fbc 159 universe/dep11/Components-amd64.yml.gz 24767211c7924ae74c0030267d5e97cc1ea56b9a50aeb811d0225847b18152c2 168 universe/dep11/Components-amd64.yml e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 universe/i18n/Translation-en e7ab72b8f37c7c9c9f6386fb8e3dfa40bf6fe4b67876703c5927e47cb8664ce4 40 universe/i18n/Translation-en.gz aea852a7918a6c61c359cd57114f60ca95551f449f06cb1c9f296b8326b78101 192 universe/i18n/Index 65de7e01eff8cf2f9a287153971467b1c11811213b64a38bdf673bad240436ff 64 universe/i18n/Translation-en.xz e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 universe/source/Sources 65de7e01eff8cf2f9a287153971467b1c11811213b64a38bdf673bad240436ff 64 universe/source/Sources.xz e7ab72b8f37c7c9c9f6386fb8e3dfa40bf6fe4b67876703c5927e47cb8664ce4 40 universe/source/Sources.gz 6546244ece6e2b0d2ee5b8e696040d2d6abb865ec125f5eaeb5ddd756eff49e0 108 universe/source/Release Acquire-By-Hash: yes relic-7.6.1/functest/packages/Release.gpg000066400000000000000000000015041455105530300203140ustar00rootroot00000000000000-----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.11 (GNU/Linux) iQIcBAABCgAGBQJY0NsqAAoJEDtP5qzAsh8yL6sQANVj6UzIwHggJDdr2KqsozxQ b55dOIdj1TjDR9mXN6gRPnA5wCmxUfeL9wr/Zu9Vtn3q0doVL6GUUF8uAxXwMcBs pwrZwqmmQ23aHJRykbNwqyqsljZUFomhWojy0/tFKMHA4QUwSkL0F7eed73AoHxT tdYRRTY0i1jIsd7vuKhPYx9PALERpMeSz3DgSZLuw4C0xKfInxFFR0GXBUCv86cy +b5+qiH2nnesC7CriIUAlywFsgDNNeMKK9gwX64hveglcxSOdK4BY6C7KvRj+omI 0lEiZCnE7LJpFd2w12Ldxc6jyqlMc2S1XLBs6z4OVkKg4loMZPMKyuukwuJ5mZB4 uVfoXW3Jrxdq8iJoDaaa+GT4LzasnfGlDuAZPW8T8AO7pB7D0+nzpyFGi0Ajwafw f/PyjwxRo6mK1MJ8ifitYMiIdBX1K5w1J3vT03AM/xp6eO0cUYW7DU3WjFRCFnmI ZQ79Gk56OezZkpXuvf/lzS+38gWdprCpciVxG85LtGSJbpigW9DwuA6LzygOIhgZ WfppditHMpQJEanVsv2/hzgYt7mNWGJQfdvmiiWnboG9GN5u3XYWlDEK1OQgPRvT qG9uK6lroKPWsroSLndgSGGiKGAYOoESSYVkcyo69zmHVTg2+eg2Z3QgTVb8ka1s UG4/sOuyWG5++ofvOh+C =XcUC -----END PGP SIGNATURE----- relic-7.6.1/functest/packages/VSIXProject1.vsix000077500000000000000000000534561455105530300213710ustar00rootroot00000000000000PK项J _rels/.rels¥½Â0 „_%òN]@âOM» $VT jMÑ&‘¼=™ LŒöÝw>¹jÓ¨îÄâ‚×0/JPä»Ð;o5œÛÃlM]h4);dpQTF¼hRŠ;D隌!’ÏÊ%ðdRÙb4ÝÕXÂEY®?3à;Sµ†-% oDˆï®#ÁÞY—Ì8g½I7& œW¾ˆÒuì5œæÛÃj¿^îAµÏHÿUûyÖ~½¢~PK³„W(°@PK项J catalog.json¥RÉNÃ0ýäRbemD%N%‡@ˆˆ ‰ôàÆ“2űƒíT„ªÿŽÓ”E•¸Ð£Çožßâi˜ÄŒ]€6¨$™’†Ä#(kE¦;‚ÜÅüé^« T6¤3Õ´Lö”ÙÕ*ʸ¿¸ò“ NüŸ¤>w¬B…‘·i¯C½GZV½²52}>RlJ‚´ôß8µÛ_êw¶} ¿ÉÝÞ-ÈduáЂä +ôìþoòûÕ[¬´2ªv^ÐtL¶ã¨èÅ™Òs´J»ç0¥Nhp9D#TÅ~£:]CL®;™[é?ƒ9´NZ!ƒSilí˜CÞ´¶¿ G %ûåÞ;·Ñ¿Ã^|'CŽPŒêkpÇšSµt;bóK¦Q!zëmG;/Ms\''Šlg/a[” È÷³¶ÞHøìN›ÛæLN¥ÌàÑYâY˜RxGä~¤Iq1òù3ÌqR)Ãh0¨þKª,_1>zÀ³§èT4”$Ü%Lq±=1jklRnÃÖx•¾#ay@ÕþbìjwÑîZ»]½þ¨5¯ âCK8¿ì·‡úmWènþWþN÷ ¶ >íÕþPKWKÇePK项J/package/services/digital-signature/origin.psdorPKPK项JQpackage/services/digital-signature/xml-signature/27jgiftvsf5ljdlq5l35xzaye.psdsxsÍXi“¢Èý+öGÃbÑŽªêHVY•M…//R@VEøõ*{qºk:¦çõÄ|° ñyô!ÖžŸx!\íÐoFVW¿Bþóú—'«òòúPTY}7þY#÷ lºùEþÅßXß 6^z†/áñT.Ø:É®õàÇk@»‰5éO ´³5#väEHWÙ©}~Bî=Ÿ¯ÙÆ÷ÒUé¶Ð]H¸ÒMv½îÍÐâú̺øD³œ¶ŸmÙ.Ó%'´g+ÓÖysæ…¡zg!\ž· Îlº•mq ¯t/é›’%érf '0†(0£œò{ÔžKÓ¥ F_‚±loµt*ç±ä"q«Í§Å%†xRbÛs«°ÜT¼,”xÓRj–}j/V½§¹š£œœS§µ€QÛjy*+ª&¤éN¾úÝEç¤jj4«L÷Ä& }ä¨:c FõOha¶È&tèR(A,VŠ¡héUÈ÷yÞºœõ ï4gÔ«œÜ%vE:šÙ¡Óæ!æ\°rí)bé{ ÍÄŽ +7«¦ûÙ:\ˆLé®ÖL,í¡yÑkŸŸoI¿Kô“ »›;]°^ãÝF ¬šø0œÅ¾¨¢È’,Àº A+Ò % +¯`·¨ P1O‚)î Vçh¦µ* ¾ªO[8ìF×Yl:g{m½A>ÔÀlŽ6TNµÄ^c9EÉÍ©ŒŽªWöT:Ô64ðUz'Eû%]Üá‰wx+´o¾ŒÞò~Æ×¾Þùïù+ïù³àšûæÏ€kq„ʪ×!îNíýNK ‡íÅáÙoÕž»ªÇO/âÛ#CçwØ ½Ã¶€r‹Í¶†í<àèW¦ÒÍæX ÑØ»X—ïr}/V¼ÇU~׿}Ï¿}Ïßøæ?ìSlYݑ䣋¯AoZlrkð:¯Ì0¦ÌÐ[kmJ%*ÕìéîÞR:£+e´TÜNNB3“¬su÷3¿´cl%¦~Ö]5BÂ`#êëºÑ/›RO –g­·jç"ê9ū¢h½ìô¼‹×À xS­²¢ÈO3cÕš0L° ›W1 üÂr—~4¥R ™ÇãÐ ¤ÍR=5ŠKZàÒ­®:K›’ï¡GÞ[Œ÷c›N}^Ñ‘NyB5Ë5u:¦¶°\Í)îvßc:I·´s¯Oùjy [܇K_?n£½ŒÄtxîâDˆ¦JÖ´ÓQsê…µ™ž¸kê_.K™´X é4+˜n*BÄñõXÙ·³“â`u©åé‚ öæŽñ dçðÔTeÔ–Ñ} Ô¢Øl_5\š6 sn8p2†r<­û,èF‡É)JbaÑ¢4Ðk€Õ ]Óšîºâ‡çKDn¨íL ›NŽ”I6­¹U·ð`S÷1Q ízfF”b#¯ý¤-Cÿ¢ ^µ#÷}¼wë9&ú¶‡Zyþá {ùþÊ×)¬*^”ç¶0ß­H?KÎÜκª¸CÍ]ŽZir¦àÛÂï7»»ð:ÕO‘v<ŠþôR…þꌯܙ3/¢x K­’Wø”L¢ÆƒU¬'§…¬×5ök7ãë³JrœsÔíqÕ „Î)WFÂŽŠq7‚ËäRl}É© 0©µ°ÔÕJî±Ý¬X™–T‚ÖÊåæqthÎq{ÝÂð$2}—ИS- ëÇ'ÒÕc0ìÓæ´^Zéϳ »S *Í[±ý¾’Þ,·*‹|­¼·FàA žGß7/Oª—LJáô‡ÆÉ>Ï<ë"ÿÄyóæ­ùðÊ2}}áÐB!¯“£ßÞ`ižŸC.j¾iË6-§v‘˜ÌiI‰e,¾’‚î"¦qòÖ|OicÛ§*^©ceâýX¯ R¦^üPÚ ‚m¥1¶ÜÛ’¼˜kb —ãcõ%¯¿iúڞǧü ¥A/-ÂC¤\SR‚Ï‚@‘˜¤¡6uÄÖš¯¶'Ó>í-RÓ%È“¿/R°ÿSV…ßÀfR7ô²ßÏ®‘9#åS^îÃÜB}¦D¢fƒíÆ ¦r”ûRJy" Åßf¤é¿ÄnŽêâ¡ßÚNE’ wŽ+˜[dêÐô›ŽågÝ ¸Cÿñ+ì†óó×kÜ㥎¯_*Èglˆí÷s §Y»eŒ§Ç.;™ |ÝPí&KDŠVÖMkÙÏ1|üÓ{Ã÷œÊ[ÅDjX]bÖH‡ñpè&õ—Vãü±¬ƒ¢úSA/yðX”08¼Þ›¼¦ž|†~üqrCüý9šR,§—Çù’6½1b‰ÙvLJ <ëÐH2RSKië DdžyûWrôŸ ¦5òøúýwø~okê¡_ªÇÿÈæXžÔ~¾¦åÝÒ©Xž'xyÑ7Yè”ì‘«cÁÀ6Þ8gXvâoÞ·ÜÜo‘ÿç·'_íVœÁуåU!lžG£;×™ïþªýf^ý]ÚÞHh ?ûQœ—[üâ Ÿ‰ªNXÖŠ¢Yö±®kËeŸÏ žn,q£&(9ÁI G?bäGlöˆNÐéG}B¾$â±Þ?aûŽí--È­/»Ÿ~ù/PKhÑÜsPK项JVSIXProject1.dllí} `ÕõþÝd³YB^‚E³jDˆ BxY"’V ›dÁ$›î&H¨š•—XBKUŠòª-¾úÂøhë£Zjí¿­ÄʯÚ"%¨l‘ýß;3»Ùlfö5»;;ÙïÀ™Ù™sçžó{¾;w†M(ýV+ÑB’¨º\„¼@)"ÅI5=çéä™Ô·½ÀÍxkÐÜ%5sƒÝ¶Øn©3WZêëmæ «ÙÞTo®©7—Ì,3×Ùª¬y½{›†ˆ}ÌšLÈ NOþÙpU¹»ßvr¹¹—OÈ.!áÜß_¥ÌÌÈŸÈä?넸™Ü›Søó‹?3P\‹îcMÙßνgÇK+íw&ú]®—B™BÒèvÁ+20ˆœx„Ægô:4Òãi^ÇyÖåtÿé".†U×­‹Eyv‡½’~æccØà=†.íŠèß<»µÖF¦‰1ó}=Õ­]±o˜¯¾*´a±éH2™1ˆ£½ IuÇp-­‰È/øñ$åëHÝs„dér/"ĤÞËN}èÁ þ.Š$I—Kq~)m˜Û—ž·õ£›&Úœ3† ½› ¬™õḘžï¥»``•àèORè6Í`´]B÷C/¹›v”4ô2SÜu?t„ÎA¯6äÒÃЋò d$då~ƒ^eÏ¢ñèr2Ç4Sîetc²—Ò³½„È )—ôë—›CO_9´Ø¾…OGÃäÔ Nr1ߣu¹É´­.÷rvD“[vS1Ç¢&Â8,•—Ÿ72dÁ8v&™ÔÒí(Úûà» ÙN÷wÒ«—5Úkê;X‹&:dt?x^yÝ$Ôéà©ó¦—Ðýôø<õ>¸¸ÖV!æšr·\úc.•¹ü/7’ôãóNr©êyÂ8²ýåB{ÒGÜsâ^/~N÷„<¦öòÝ—I2B϶Ïq/'e/ù»ˆžÙ£cÛuüv€žmÿÿ|ßþ‡oó÷Gº½š?“©³è ¤<ùMº]CÞÔ›Hn?ýÜÀ}™d"ï’ýútZž Û§éÖ@j“XËiɬåÝ~º­ä·[ø«îç{¾‚¶I'¿æÛ/àXû!üv=ŸMŽòçÇ'] ÛÕzv~SëmLò~}?2ƒLMêGóÛ[è–aæŠðcÈe’Ád™Àqô(F&Ó<É|ç]d7š,JÞÀ "¹V.‹l ›¹ùÎïéæf™Y+û?›ôÇ‘6þèAãçd§#¯ñG›Œ.²‹½#´4Þ•ôÿ$´$óõOÓ£ã‚< žK"ƒñ×õÿeÒaÎ@FñG«©ø£Wéu~ähDl;KßùyGí…úTòlG²»rÝšÈÕt›I øí8~;‘ßNç·³ùm9¿µÐm_RÃþ.¿ÝÄ÷ó¿=B·—ÐQ|6iù3omæÏÄ>MFÓùüiò CvÐ34«Ü³I%d/ý|Iåv‘2’ÅÝÆ}‡^5«¢í§q5d7'¹Ž\ÍÝž¼Œf‚õ³ŽïsÕH§Âñ\:­è".ô"oÒíEätû ò%Ý&_Óí5$C—GÙ϶ãùí$þü7ùÏeüöÛü¶’ô¥ÛÛÉåº"â µºEü¶’||—nï!›èö~òsºý!yG·’·^Es£ãy£ç^¦»•I¯ÑqÕQVqTõôS­˜$§›[Â~ ­«Î;“5¤‘ßgxk¯éÚîM½ï¹VÒÁŸK¡íuŸ›êiÂzé“Ì,­©´Û¶êƼù5Ž&KmYcSU-¯l‰µ¶6¯àº¼|2ÉVWg©¯* ó˦/˜e·-µV6JmUMµÖÝf:#Íœ1¹´lêôI3o?–LžR_ÛÓiëI4]ÔÚ…qyUµµ~Ù*ºr{dÚêiéQ¢Zkóhâj×»]¦Òõ‹–Ï›'bku-õËÎL´/nª£=ÝL |òòJ+Ÿ~"–ôôúj›ÿYdz}£Õnk ÓE Š¥mçùá{nú›½ÎÒè9vXëÙnò2ê_àšäU6ÚØ–ß¹ÕX×Û5•ßœ‹þEOÝÌnJzìóËÄ¡¡ hy‘qtX6im^#þz‚æÉA„øÉÌ 6jÄ35’²%¶;J­í¸Ø¶œŸÚè´’Þ_,tÍa%dütROï!5tµl¥+ýï‘|rÉ#¥ô¨ž4Ñûo#ýTGïVºn¬%âÕ¹ôŽM²&µÕÑsõôúBè´:‚Þå*èŸ|z­&ÃéÑhzÎJ?¢íÆÒ³Ã龚¶fçØUci›JúiÝŽå[Yø»Åsc Çw,Ù4ãÀSUå¿¿böÍt-ÎqF=]'ÓYYì0mt³žKOOO6ë¸t£1EŸ]š=;{žpÙÍYwÑÆ³› Ô–5ÑÈš ìo¤ÛlçÊìÙ¬£ìyI):z1!ÙÎut¹ŸÝL]¤‰>=Û¹)}`&1³ËÓÓ³œ[²œf9wŸ_qëü£Ú×.lÉú£ézçvBŸ#Ùíu3[°²õ,=žS6±€Ý!ÙÂmró­{ªoKyδ°âË‹¿zpá±]cÚ’FÜ{áî?]ùÄ/+o™UöÈ£—^u隞ؙ3mÿ®ÿ]ò伡¸é¿_úÁȼ1ûîÏ ^uáÓUoÜþXã ?}²©á¡c®z±äêo?ô~û;©Ÿn¹ãt~©ù»nYq|OG®íº)+üñê×X¦\üÅ£ï?úÖÑýI,¡ÓXÊói’+è°aN*ø3lp èçQôÓ8ÚŠ ;7†ÝuüP²O•ÔVMUg0ê Yõ†ìÉT:C:ŸÇÉl¶1™®©²,åüÇÉ,ó³ocËÀ$#m”KMFN|,½Œ¥g®®ß-vKÃͶzÕç.±Ûîpp´ð4šÅ‘´.‹’̯KÒ8’R@ŸoèZ~¹$ïæÉs=÷’aâÜ7>¦~ÒûxL%5ކZK3»yf³kÌ‹™¶Mbç±]<Ïa-µÔSU¹o‹Ž™õµÍÜUI\PÏ6#ô4 ÚÚ˜>½’Ý„vÓKZiG}iìlžuÐI„ÎiM‚êjŽ )°æWVŒ¶Tϯȯ>ª bÔðqÖê‚á£ÆŒ¸.̘•㪅gCôÙ÷í7Nf—n¢Zë™1Å€<ŸæXé Ö>Ìì^ñ 3»s0JHÓ0³8‹N¨·6Ñ{bí0󬦊ښÊoZ›çÚn·ÖO¨3Ær]åu£ ÆeÍ;npwgÂ<æ>A§¸dV»çG»å:}屌 3w=NXù#«¯«S]PPu]¾e¤eÓ-«{ý§vu¯³T÷~G?q¥Ù‹*{aû†ZÉùüiþãþÉÓ0ŠžË'ì¶¿{¦s¶ön¤³™püñz6¸×gæÉË­õ,^.ÏsN¸ï˜…Og s‰µÑBg÷*3»Oñ)qQq¯½ÓX®fG<<.’H=sgdJ,þC]%¤$é|³Ó¬1ÓTþÉý< [®Uò5UK K¬lC=½_e°Ë[gà_Þ„½E l!K¶ò>=ÊÏé£â;1›…ãOiû]»iëLá8—‚úþ>÷„‘äáÈs?a× Ç{Ò ùðŽ^/ÓYeõ‡½^8^B§õ¼O8ñz3É5'‘?ãHQ‘x\LÈá4ƒØŸ™ì¹‚Ülû£Ç´Ÿï7ÄþÌd ­ñÑ÷øwî—œû9ÈIdÄ)ØœNaÇþ°StÓÖÖÆªƒÿË^|ò;öÇÅ×L—^\1’ðÿðÿðÿð¯žÿ;¢$ðÿðÿðÿðÿðÿðßþ]2"×·¿vðÿðÿðÿðÿðÿðÿðÿðÿðÿðÿðÿðþ#)ðÿðÿðÿðÿðÿðßþ]2"×·¿vðÿðÿðÿðÿðÿðÿðÿðÿðÿðÿðÿðþ#)ðÿðÿðÿðÿðÿðþedM°~–è(<ÿgƒÚÿçBÉôÆÈûŽüã`Øþƒ|Aü–@þφæÞ „î?¤ÁÄO „î?t÷þJ dÿ!¾ ò%ªÿ³á¸÷S!úcð‘+ý‡ë^¶BóÖà "S!ù?¾{¹Å؃/ˆd „â_™{éóÏ$£kSƒ/Èòà]KäàlÀîèS!¹<ø-#ô-¿î}²°ï;‚k½ÁÆð%âàóƒ\« F ÔÁçÇ6ÈfAòà Cl»@#ó[‚¦ J äÁÁ%úà»ÓTÓ@%Æà»³Üíšö[Bª%ÆàwB ®µ¿gðCõï§Â|/@gCŒ×w|Ü{Ó÷à 2_¾«Áéì (Ä„u_ŠÉ¹QKgó`â•Z Jf?Ø1É¿ôJ¼CÞà>½‡Þí_.€cÐQ€lùu/]{m :YrÂò@0Ývm©|˜c¬ÿ Ü‡>-Áe*ˆÜ‡7>°d,øÐÇÀ·•r÷!AKi !÷!AþCÒto÷ÁŽDj6à%÷ Ç %€=\ðÁŽAg#å>˜1ðë_A¯>»•‚b :]xùïfSàžøï^»@r g»¦¿ëD ¼¿ÎÉû¨{âÿ„<Ë„Ù)ázÜw0þÃDAdI™{ÿ½wŽQ©w¿Ýô¯¼ÿþÝ ¢ë^ÞAæˆd :‚³F-€NkÔÝKúÎ1ñMAGP¦è ï?:î‰Ožƒ2DX2d@Êb2þ£ëžt¦:˜³Áu'ol Ð'ì°Ã;ì°Ã;ì°Ã;ì°Ã;ì°Ã;ì°Ã{|Ú]Ni+çEîrÑ|^Î^(ØÏÉÙs{»œ=E°·ÉÙõþü ooñž ŸÑÊðž SÞÎùP6»L üƒ[†Ÿì2Iñ ?3Ͱßðh€~²Ë$'ÝozX‚Úü™õ~‡—à@ù8¾ê'Pý¬ï@ü‘u Ò[ÎÞÍo9n»—ÎÙ½{o^vO÷ÞôMs:í¤ìÝ{9Hi—èÞËA†‡9Þö ÝíÞÝw:ðÔc—î;xì9]ín…ç%»÷8h¹ Ù½ÇÛžãkÏ»ùèÛ½ÛhïÖ½è€ùÞ­{Ñ^ì§{÷‚AïÙI;Hæ‰î¢]ª{¾ç ~>“ìžwãÞÊ8ìÒÝ3…l>íÒ}. 'Ÿê"–’ÄgÏ™DdÄÉÿ%N§°cØ)ºikc‰qñÙ(ð;öÇåþŽÄWˆä¾¿à¼ïW­#ž÷Ö¹ëñç‘O-åÓåõUôå^¼èò%ü¯ó]~|acçyŸîùóžÎÝ?Û¾œ7tö²ñ\gO¼×sîë=§… Ãr±CvšŸDÄø[øpùøÅÙEè‹ÏåFW—•Ÿ`ñ»ö]&$áD·Ó|_ì¼§~úÏÁ>Ô};Û§ÒÆözaú§7 "”!üý¾¦ÚÉî®tßÁ93Øþ¼¾­‚{^bCÁî3fªE¤ó>CtDFœáßgºÜ6:$޽9ܱ›ãÑ:Öv<Þ?ùÑÑeZ½æ*6-ð× ¿·£…ÍüÕÂ\³‘6®g›å^=^îa³{.ð”K;ŒíÂ9êJ«POK’cŸù%çt¹>Zï§2‰PëžõT÷Å~»ÄÉO\ ݻζ¸¤ön{íØxldýó#Èötl¯Î¶œeãMX®é| OÛé\rz£0°y„Í1Šùà:SÃu“ÒÒRR^^NªªªˆÍf#+V¬ «W¯&›6m"Û¶m#»wï&‡"‡&GŽ!G%íííäĉäô©ræÌÙQ€@ ñ'ÝWu’ÝÓ$ÄܨnÄøøøøøøø?üœÆøøøøøøøøøøøøÃÇŸ¢Š;Rý?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ðü ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð‡?Ñÿýøøøøøøøøøøøøøøøøøøøøøøø_ øãU€øøøøøøøøøøø?0þФ·>Òx’Ò•EKü™ÆH£g’ªü‘|A”•@¬ðGgðQR1­ÁDA Ä4_°K ø£;ø‚„[ÑÇýÁ$¼ˆ:þX ¾ a•@”ñÇjð £¢‹?vƒ/Hè%Mü±|AB-(âõà b D ¿ƒ/HH%-üê ¾ ¡”@tð«7ø‚_!â.Š_¯´ƒ%.ê àG`ð{§)î D}$˜oÈÎNVÜIôJ ʃÏé2²³3”w¥ˆöà‹q§F £h”@´ŸN~B_¹Fº¢?øtòzS>2‰l DðùÉOåS /‘+X >?ù ‰)I¤J ƒße¸"1úövb2øîÉOLjÄzU^±|Ïä'Hd¦@^”•@Œßkò$BS %%àïˆ>êuN~‚Dj ä%Üð{YdŸó»…±)Ix%àï’?çë»;ˆìk”ÐKÀïÙ™½"ÈPŸÉONtvéåN÷ hO%#5bCdê?b Ö›2¤ú÷—€Àíy颋D€¾“Ÿ˜ßHtÍéR‚}':üìñ@†œ˜Ö½tB¸(<è>ù‰¹UÚo0u/‰?ÔîRƉÉO%S`ðu/>ô(àää'Hø}†R÷RàcÇéÉOì2,ðJê^QÂâß•IèS òº-ä&?±»K*BuCÈN~‚„2F²îcÅ?“Ÿ Á–Säë><ð7ù Ü¥ºW”‚`xÄ?מ£Y÷Š2þ'?ALѯ{E)ðσ“Ÿ þ¦ÀÕ½¢Èó àä'ˆ\cY÷Š2 éÉOªgÉ)0öu¯(R<šü$»í>ªT÷ŠRàË©ÉOºSŸ)PͺW”®<šüdºôšÕ¯{E)èäÔä'Û¡ç¢ø¨{E)x =ùÉu—ou¯(ŒÒ“Ÿlg©ñW÷ŠRé7âàÚÇø0R~ €WyºQE"·VÁ ¢~u°J‹âà5ž—XâW_¢€æÁó¢B@ÏK4ñÇK˜Œž—ÈãmüСô ð‚„ ¦GçE9þØÅ% PìÁ»\.yc‹Ë;ì°Ã;ì°Ã;ì°Ã;ì°Ã;ì°Ã;ì°Ã®A»Ë)må\¢È].šÏËÙ û99{Ž`o—³§ö69»Þxb€äí-~Ãì·gø O Ð)oçü‡Ç(›]&…~Ããl÷gOñ ?3Ͱßðh€~²Ë$'ÝozX‚Úü™õ~‡—à@ù8¾ê'Pý¬ï@ü‘u Ò[ÎÞÍo9n»—ÎÙ½{o^vO÷ÞôMs:í¤ìÝ{9Hi—èÞËA†‡9Þö ÝíÞÝw:ðÔc—î;xì9]ín…ç%»÷8h¹ Ù½ÇÛžãkÏ»ùèÛ½ÛhïÖ½è€ùÞ­{Ñ^ì§{÷‚AïÙI;Hæ‰î¢]ª{¾ç ~>“ìžwãÞÊ8ìÒÝ3…l>íÒ}. 'Ÿê"–b²›J:…•Jqq1)--%UUUÄf³‘+VÕ«W“M›6‘Ý»w“C‡‘Ç“#GŽ£G’öövrâÄ rúT9sæ 9w.Àl@âN\îY73(é¼0í¥~VÖ[´Ð>Ã#ÝÐ^ûí£]?ÑnoùDûȶvýD³½ Æn¿oAg”¿¿ôN’ê9©·Lûîwuѵ}×ÎS Ý]¸[guï\—žÞõ˜w‘ÕŒ©k©™¾¿˜Ôä)K.Ýèõ˒دc½1]h•ÕPffšç§öÓØa/w,)iÞ½¯È4ñ?°ž"ð“M™ÝZ{_BãÒQçYô0]燯¸/á±ûkÜ%.¯A÷ÓÚsIV—]„rÉÁ>Ô};Û§ÒÆözBœlOŸ'ù÷„ðï éÓÉžÎ龃sf°ýy}[!Û»îy‰-?Ùsª™jñzNMò ‰çÔòòònϪ۶m“}VÅs*¢]ŸS3eEhØ.}¿¨Ýw}¦5;ò£b~|י—ÝgYhìj÷\ìùÿ×ø.ÜæÎ‹ÓzuéBìÝäùýD™™ž&·‡¬L÷²“­ðÒy7©éÞ«ª,q™ÉGeÔ¥ôî¾@Ër#’_½eñ§³ºÛ…#‰º¶ÑÕŒ“®8Øgþ•<çt¹>ZÿOgY0¯u ç ŸuŠ{â^—ø¾?ÿꫯ$Ò@´$Â:%ÍKØY¯_z'÷êEg_¶ òØ÷ú÷—–Æ&u]Š;^v0Ø£¾h§ŸSSé­D8f' ½{ Bs*½Ù§´´Þìs›>Õ¡iU»¾ÕÆïùÿÁ-×/òþ+ð_Ûõ‹üÿJü×vý"à¿ÿµ]¿Èø¯DÀm×/òþ+ð_Ûõ‹üÿJü×vý"à¿ÿµ]¿Èø¯DÀm×/òþ+ð_Ûõ‹üÿJü×vý"à¿ÿµ]¿Èø¯DÀm×/òþ+ð_Ûõ‹üÿJü×vý"à¿ÿµ]¿Èø¯DÀm×/òþ+ð_Ûõ‹üÿJü×vý"à¿ÿµ]¿Èø¯DÀm×/òþ+ð_Ûõ‹üÿJü×vý"à¿ÿµ]¿Èø¯DÀm×/òþ+ð_Ûõ‹üÿJü×vý"à¿ÿµ]¿Èø¯DÀm×/òþ+ð_Ûõ‹üÿJü×vý"à¿ÿµ]¿Èø¯DÀm×/òþ+ð_Ûõ‹üÿJü×vý"à¿ÿµ]¿Èø¯DÀm×/òþ+ð_Ûõ‹üÿJü×vý"à¿ÿµ]¿Èø¯DÀm×/òþ+ð_Ûõ‹üÿJü×vý"à¿ÿµ]¿Èø¯DÀm×/òþ+ð_Ûõ‹üÿJü×vý"࿉wþC¡ÑTðü‡&®‚ÿà?4qüÿ¡‰«à?øM\ÿÁhâ*øþCWÁ𚸠þƒÿÐÄUðü‡&®‚ÿà?4qüÿ¡‰«à?øM\ÿÁhâ*øþCWÁ𚸠þƒÿÐÄUðü‡&®‚ÿà?4qüÿ¡‰«à?øM\ÿÁhâ*øþCWÁ𚸠þÇ7ÿÕþÿ'âýÿ¯@þÀ%þk»~‘?ð_‰€ÿÚ®_äüW"à¿¶ëùÿ•ø¯íúEþÀ%þk»~‘?ð_‰€ÿÚ®_äüW"à¿¶ëùÿ•ø¯íúEþÀ%þk»~‘?ð_‰€ÿÚ®_äüW"à¿¶ëùÿ•ø¯íúEþÀ%þk»~‘?ð_‰€ÿÚ®_äüW"à¿¶ëùÿ•ø¯íúEþÀ%þk»~‘?ð_‰€ÿÚ®_äüW"à¿¶ëùÿ•ø¯íúEþÀ%þk»~‘?ð_‰€ÿÚ®_äüW"à¿¶ëùÿ•ø¯íúEþÀ%þk»~‘?ð_‰€ÿÚ®_äüW"à¿¶ëùÿ•ø¯íúEþÀ%þk»~‘?ð_‰€ÿÚ®_äüW"à¿¶ëùÿ•ø¯íúEþÀ%þk»~‘?ð_‰€ÿÚ®_äüW"à¿¶ëùÿ•ø¯íúEþÀ%þk»~‘?ð_‰€ÿÚ®_äüW"à¿¶ëùÿ•ø¯íúEþÀ%þk»~‘?ð_‰€ÿÚ®_äüW"à¿¶ëùÿ•ø¯íúEþÀ%þk»~‘?ð_‰€ÿÚ®_äüW"à¿¶ëùÿ•H¼ó ¦‚ÿà?4qüÿ¡‰«à?øM\ÿÁhâ*øþCWÁ𚸠þƒÿÐÄUðü‡&®‚ÿà?4qüÿ¡‰«à?øM\ÿÁhâ*øþCWÁ𚸠þƒÿÐÄUðü‡&®‚ÿà?4qüÿ¡‰«à?øM\ÿÁhâ*øþCWÁ𚸠þƒÿÐÄUð_9~-ê‘#G\åååª×ŸÖµªªÊuôèQÕÇSMU›ÃJDíÜÅZOœ8áÚ´i“ê¼éiºmÛ6ÕÇü]ÔÎ],÷üèj¢®Ôæ°Q;w±PÜóc«‰¶P›ÃJDíÜE[qÏWGi- 6‡•ˆÚ¹‹–âžškµ9¬DÔÎ]4÷üøÒž¾P›ÃJDíÜERqÏoí©kµ9¬DÔÎ]¤÷|mhO\ ¨Ía%¢vî”*îùÚÔž´P›ÃJDíÜ)QÜóµ­=e- 6‡•ˆÚ¹ GqÏïYªõµ€ÚV"jç.TÅ=¿gª–×jsX‰¨»`÷üÄP-®Ôæ°Q;wÁ(îù‰¥Z[ ¨Ía%¢vîü)îù‰­ZY ¨Íáh‹9Å=¿˜ÇŸè9Ps- 6ïâApÏWOÙxøðaÕãˆUk- 6ÿÔÜóÕÕ+Vxòb³ÙT'kØîùêjii©«½½Ý“öYí˜âI±ˆŽàž*UßìœÚqÅ“b-YÁ=?>”Í…rùÂ<Ù]±P&¸çÇ—²ÜÈå ï¥kð÷üøRïw~rŠwòе@p¢FŽpÏ÷¯¾ïüäïý+Öþ%Ö9Á=?8 åÞ…w‘Íg"Ìjä÷üàT9y ¬X kì¸ç‡¦þÞùÉ)Þ¯‰ºP3îù¡i0ïüäïƒ×D[ Ä#îù¡k°ïüäïCמ¾PîùêÕ"Þ†®=u-k,¸ç‡¯¡¾óó7˜{ÃÓž²Pƒ»ö:„çÐ04œw~rŠw¡+{ïÂjW-îDjP3~oeÏ¢l>Ž(¸Ú‹tþ1VV›»wïVôÎ%^æµãö§ìÞ¶zõjþý–ÚcoªôŸ¿ùWmlñ¨,߬ãý÷ öî{+žºk4Ÿ9ñ.°Sãa}­9@í8ÃQ<D&ú»Àx\ßGƒÿZž˜&êóA$ßùÉi¢½ ÔÊú>’¼ï)sA"=D㟜&J>µ¶¾4ç{Ò\ГŸ¢õÎÏ_.ÕÆ M´õ}"ÎL{Úóß3é)ï±¾Œ¨ƒp´'<DûŸ¿Üiy-…õ}ôDíÜ„£Z}>ˆÅ;?9ÕÚ»@¬ïc+jç+\ÕÊóA,ßùÉi¼¯°¾Q;‡áhˆô;¿Xä0LXßC¢!j×F8*÷|­w~ÑÎÔ»@¬ï!±µë%\õ~>ˆÖ;¿XäŒÅŽõ=$Dí GÙóu¯ñƒóHˆÚµï5­vœñžHϵë-žk[íxã97ž'j×`<Ö·ÚqÇcN =_Ô®Íx©uµãŽ—<@SÔ®×x¨yµã‡@ j×0øÎCâCÔ®íXs@mà<$Eíz%ÔÆÞCâYÔæ@¢ñ?Òø HIOåFOÅDCzOz$VÒS¸ÒSp@ j‰Ö9£õø!x­òG«qC ñ*ZãÖâ…H‹Ëåg(Z\.Øa‡vØa‡vØa‡vØa‡vØa‡vØa‡v Ú]Ni+çEîrÑ|^Î^(ØÏÉÙs{»œ=E°·ÉÙõþü ooñž`‡¼=Ãoxb€Ny;ç?<>@Ùì2)ô`»?{Šßðøý™i†ý†Gô“]&9ì~ÓÃÔæÏ¬÷;¼üÊÀñ T?ê/`}⬑Þrôn~Ë8ÈqÛ½pÎîÝ{;ð²{º÷v ïlšÓi¿ eïìÞËAJ»D÷^2®ÌG¼áÖ˜G²¢…?c‰Z¿WfQ=­1///ç5еÀbŸîLªû£=æGŽq>|8µÀ°dÆÃ˜»uÅŠž~m6[,濵«1gZZZêjoo÷ôÍ>GªïpjÁߘ»õСC'©ø"•_#‹5À|Ø¥Äùþñ@9p‹5Üy‹]'Õï‰'õÉbò®©ØÖ",¢úb0÷6­^½š¯ç`ce×ÈõÊ\È|2ßþúóÑElÁ΃fªNªÇõÍÆ.~xÏyr¨w}3ŸA`>&b0ƒÙO.óÃwÎó“}¯V}+%ü™“óEí6F»¾#‹ø!q~£W_}ÛDz¾•J(üðªÑL¯ë3C¼6*õ­TBàÇ"‰kÅS}+?üxÑÏ5¾y‹z}‹¿Ÿ¡ûøØ‹¿£E<ίGF›°×;…½û÷‘píÂ>Eüý…ÂïÁàÄß“Â~/m[èr¹œœ“ÿÍGíú6þOi/~OD>ÕE¤ó÷D8¯ AIó•Z„øµ$ŸÃQÄøã)þpÿý?Üë?âGüˆñ#~įýõâGüJâ×:?âGüˆñ#~-ÆkEüˆ_Íøåt÷îÝ!}¿HNY¬¯`|F"~ö]•H}_Î[YŸA|FQü‘ʹ‚±ÿá;‹Éù¦M›øïÚDp,ù‰[ò{¦áæœ]ãþ¾L¸×ËŒEÐßUòfïïj²8Âí‡Å Áó=Ï3Â÷1—ŠŸ)ËóÊ÷›ÙøûöÊõ¬-óéç»Nîïú˜}êÇïwlØw YMª©ïJ³ï›ªÖw€Ÿ¿xñLßÕ9ÄwÈØ¸²ïMJqÖ'Ožq•â2ëC¦FÜz쌂ïÚœ ð0ïúrsÖËîÕÓ–µ ¡>ŠÂ‰YGÀúò‰ç˜×užq p ª>"€%˜ï(yµïvO‰T}D‹T}í—hç=n«þûHØ»ï÷ml¯þÂuæÓ}žoŸCÿZtãj㈞íÏqζwéÛØ~ľ§e&ÂÿéãùžVöø©åˆj¼ûwMHá?tÿJßÃÀ?ükÙ¿ÚüKtÿj?üÃ?æŸÄòï«¡þ} ßwŠ%?g-÷så2¾Ùû€wBÁÌïƒÈóáûž’=+ s(¿Á'ÌW‘—ï.ï€BýÙà`ÚJüŒû)¯$ßáû³¹r?ƒËÞEÉü ¯ì»œ32ï`d~6öwÍxÿ ¬Dí;Æ»˜3N³èŒüï~‰Ø»™ñyÜËþx°ùõ•váÿ’Žª¶ ?‡Æ¿Í`ûB2.°ÿžxåJþçÉX fÒùž¢(I>^¹9-ع.”ùÝßõR÷Xßsñ|½RüÈ_øø½ç4﹕Í_¾ÿæ u={ŸîýolÎóþ7 fs¿s—¸žŸË¼Û³9Ú{g÷LñúS¾×‹}¼ã8ÿlô:æçf©k½úpÿûÅ)¯sîyÖéÛ¾Mž¶§rz#!½ÌåôÓ¤)“æ&ÓÞÒÒGì¹ø{·³ãcŦ^AÛühÏÃë¶oyxëºÖíÛ÷mOM5š¶›ö‰ië>²w;•‡×î[¿~ë–uk{÷mÝJ֭ݲvë–Vç#5lÙkÚ·e˾í¦í­[÷î3“Ó´vK«i¯iOjëzÚËÖu{Z÷ìÝ»wýÃë—ÓkM[ÉÖõ[knݺ¾yíö‡×QKÜÝjÜžê,Oã›—ºˆv㜵lË"Ú!!Õµ’Ö=$Õº—&©ÕÔz‡qmj"«lK¶Ï7lMÝNZ—.5±«[÷5Sëvi]ŸÚÊl'&ãBȉû,£ݹÊvÿÌ×_[øÑ€MÏyå¿ûúÎ ûúdõÇ.|ràšö|ò†ÜGw㳪–üþÄìË-¸¯â%ËmæK‰)}óÍÿ¾áäã)©C6|úrKãWùI¿2«F7ýÒüÿ­&Ù·¼ÿÑ©%E^œôÍ¿<÷ÈÍíïýrÒõ_*7þúÇ;»ìî]§g½ñõ¹wŽÏ;íî²AÙ{~<™{òíÛÉ_Ënýºî7ƒþVÔ«èÚÖ×Ú[×^qsÿ–¾^»ø_»/LúÕ«‡× çn+ùù¥‹¯Yðeûõ÷/<ž;|Ù™¬¿ÖœÚ^TrûøÒŸp;ôÁÚÙ~–ÜÊÓ:ýÆó—?rÏ‹¯m{=ù¥ûz§-[=ö™Ê§.{þÔuW?ô¥ŸåZ‡nÓ½ùúˆÇ6¯±¿åg#?¿gÅ—÷ë8¸÷ÊêwôYò»ÆãÍ+—5¼R¶òñÒûz¢yì?çÙV]ÿS{ÑÎ#뿾i€}\~)˜y×çÏ·}¶ãÝß\¸ïÚÍßmÿ»_n|ö§“K‡®ïûê²gwÎíûƾô§/ü¿[gœM.Êz{ýÆ¿o}aÒÀšÜòqŽk†z"É+µß8ÙoÕ”)M…U÷ï+9©þõW&ät$M\³vüw¾õÜ“ór–œl/þ¢ªâóÚ¤»_I{ü±·ÏI惗ÿwЇž–5\ñ«ƒ[ûóß|ÿçÇ}À®]ùúÀ±ìX?êÒ!C 惆ƒ³Ÿ5²~ÿÂãÊ/Ë|°òŠ^)–Zcÿ_¾sMʤk“ŒÒ »¦(=Õeõ¾ºaBµcÃ[¯\vïýÿÎnšû䱓oÞøäh[Þ_ß=~õ—êŸüÙÌ·¶tëOÆüä'o~ê4¾aý×á‹ûê~±ÒtÙ\ñÓeÉy9¿­o¸xÃ_ª^Î9Ù‰ÿË–—7¼{·éŠ#¦c3ž±:hXY6àï•é–7veNÞ0·WòË—]_ÿö†çîÿûۜƂӳ_}ý&Ãö¢;¯U¾¿zî(cïóÏÿåg¿ÈÿªÏ¾ú×ëÞÛòñÉ£+_›™2Ããµ*9o]?]c™%¥¯Q¿·);muþ¤sšv¬ûhÀì/~æÃ~—6zÉ ¹”)Å;³›G/øüá‡~ý‡Y~¾ë±_ÕÔ·íÅLï^Tm(.¼Ódü®}@¿ m Þÿ]ßgr_)ï?lÚæúd–é–[0´uÖËëW¤¼·sÉ»µh¾m§nËÙòÖZ¿ÐÍ(â¾×W7©:ó,1(žµôçºçg0Ý»·A÷Ñz}7ϼý^Ý-;R.ÚüÙ™£ÿ´WwhLÓÊ‘½î¼ØzOÉiÝñß~ÿ«óÞƒ…Ã3_¸·Í9Àô¯Â¡×ÜKÒmZ4’;`J;pñ¢q–¾#‡§<µeÉû5ÿ¬®1~cÏRݾ®Üùʽ¿¦2üÚ±»Ö¾ùHNIõ7&¿23mšù¹'ÖÕOokpo§)/í¿Ÿ°ò–ÿqý#ΦûúÎøÆðáÿ¾vWû®'_¶”µU_YZý#Î5̰`ÝcW~2á`Ö]•[¾}ƒ¡èÁ'¿ÿñfcÚŸ‹í_êLcßêè—bœuêß–Eۇݒu×ÝOüä%üçñ·ŸÝ“þ¯ßÚr/½ñ?O|ü‹þÏþ±gôý×ÝøÆŠv3šžùaí§UÝðºë¨ùW Ÿœdxyë^·ùgä²;Æ]²êdÖfûòÉýá;îY•êÚúVkÇÁkNØ0d˹'W­š²Èrï%5Å‹§™ÒL7]üÙêƒú ºdUÚ‚×FÞû袗SŠGçR‰»ÆÅ7Ï~ÙåÔ ›ÓúQË™¦ÿmÎ ãï˜rìOÌøæëw´¾µsjéÇO÷~eZÓ·Sÿ7c[sÝÊCo9ÆÖÕV<5¦×gC矲6Ï·ÜríO.͵®¿â†ßÎnžþWûÔ7ªO|]rºüø7›Üõg¶>õÎK'­[1¹ñ¨G×Ì8ôɼæ¢k.oXÿuÒ{ï¿¿‚Åðφ«ÊÙ^Gu }Úh|Å@Ÿ49e%e¿èsáü˜Ë'¯Y?ü•«z—ü’=ŒT^k“ÃjwÜj·TÕ,¶ÔßZe«lª³Ö7:n]Vãh²ÔšMU56óˆü‚1·Î²Û–Z+©m~ÙôâAA×[ÅÒ[çXk­‡µ‹!¯¡ªÂÿMW5yáUƒçó{ì³YºÝ«^íY8Éf/©­-µÔÔ“:G¥ÍnµæUÕÖò6×´MüÎõ…ãÕ_üç~ïó¬>ó%Î3™FuA» ÔwZêGÑí|RFÒíd2‡~šNf’›éñtºB?3ùuÒ© B?\—> Å#öè­ó‰²„o5ŸXˆöSCj‰•öYOª‰·ᯚK­zÖAíÒHÛÙè‘ ’îÔ±>Êèy;µÔ“Å=-àÛä{þŒ"üïš¹„ÏÇ$Ú¦Žþ±ÒöÔ‹ —{ÙxÿÍ­…oç–bÒ›¶qû+¡ê •| ]âœÏçl™Em6²”¶«¤öÞ–OŒ^}̧j§½t^[@òh·2ŸY´ýt>VÖ¶žÆT뙜¯®™b5b\n\õ!Åw=ŸW¡Eiâ[xçÞ_>Gñùìz­oV}s:–¿f"máà±UÐ8šéd躸’6I£»ã£Ô¢†üPK~E€oe7®PK项JVSIXProject1.pdbíZ}lE{-WÚRኑB€b‘ûèA{b#`[ð `?R•ëݶ,\oËí«1¦ñÔ€A£QùÃh 1$`HTL”ÿÐhH¬ 1ƨ1*F£C}oçíÞ\{E®ÒÖÚýÁtvæ½7ofÞÌ›·;Ñb)ÝÐ[Óõ¾úE‹*"M«*j½~YIyC \æ˜é˜þ/èés0žøy´ûà`ôPz* „ÈLádf}}…˜÷õŒžcr0"Èecÿqƒg½廎Ê;|~kÚù¿j箼q瓋߯,kxÛâQ8ÉèÝì_å` "×þ§sAÞÿTΕ;ûpì?¾ñGˆå'Š>õìi‚žu¯¸^>Þxó‹ß}ó‰gÏrQ.Ý»ã.Oë§žcUÈ;1|ì‡é3úîy}Jdçïz8öÈ’MÓ¸J¯9×Ûufþ»ïmxâŽ}6>ä;}bTæà¢p©ìÓªÏ~®rÁûÇ&¬nYÙwnÝn}Tæ /|Ñ/w0>pþ§ó?Q ÿ(ý‰-kî4Ô”ÑœŠÆµ¶h²9®Ç:ÛÕdÚhÞ®ÑD…‘îŒkzEµ?PÛ¼>¥oQcHÛØ¾ƒ ìB½ÞÞMÆ룱­Ñ6Õ3òUÒa)Ùnh]V»Y…+éÈ(¶äßx Æ‹Ø.*¦S Þ»Z1UòÛÖ„KlÿY_ù•ƒ˜Ÿ{öþì%ß—:º X‘ä&qõw!·ýÌî¹o43]eš*ÉÓ¸,Þ®Ùï)æ!šÌëàÒƒlç…*=ŸaãIô‰î“ÀïÿÞXZOL‡ÉíW¯E/yžÛšî7Òj;ÌÍ<{qótèIÜ =®&¼ ª¡µ%ÚÉð4hѶ¤n¤5Ü r×c;que2š¸ßÐ êN¹Ä·:¡·DÚÑ´¦SÛ²þ[:“i­]õ†“i5¥w4©©íZL5„|ĺón47o“¹wErÓ¼ëÖ6Zm =ƒð5mV ìKÕ?ñØ­ñœdxo×’Áj« ÜP …….ÿþÄá;?¿3¿Øõö#›ˆ ¦BEØs ˆ}YjË ûÞä6-“ì[Œöýزo8©¥5šE×ÄEèRÄž§5Bz~Ág¿½{‘Úyü¼ Ûùõ˜ÃÔ_bÊÕaú˜å¬þp{„F–[…9é\yT"ïl³_E?ìïTü* Ÿ^KcdýwÈû¿‡'/.ѱÊÍù€ýÿ¸’áÊþÿÇý{I÷Ug ˆ8çƒRL=î¿Ç3ŸÇì'„ì·Xß­N€ë$Y¥Ÿ,Æ,„°|5ò¸rð„i’@ìO+廿'°| Ûï/¶Ÿ[²ò¹ã–ýÚÔô½á¤‘Ž&iXY²!ö ²ïÇýçÞbÉeo`Ùƒ9d±Ÿîm²^žMŒ¨¶kq5e.§<|ŒÂ>ÆòeÓøjb’¤çÃÝÿΗYz–±žÓ¬'(éÁùu?j鉰1룉D F  ÖË ”{µ@ÈY¼å¼Ö\YkÁ00h5iJ?ZZK'¬¹Ê¬}]W(Ú³|"ÛÊ}ì}mnì¡è ß½‡Û$ßmùƒ7a!ûî*Óç›þ¸›ü²sú-IóÌQG7í-ܧÝagŃHëŽa2å«ÐçÏÁIªÃ|¦'1-ÄòL1-µÏ:sâÜ/·ÝG€ü¼Ü·dÞ&ïL›wÛx× ¯æÛçaóZkU>oE^:§o3Û÷Ú}¿ ¬u£ØkÌXÇÏ›xÏãÜA6Új¶ó0ˆ³1”5G¹=kÈ}¾ÊœÛ˜}ŽžâúÐiL»0íÇÔƒ©ñÎÀFü˜ÖÒY ÿú¦#ÎíИ}Ë//ö¥=Ü€ ó5ëâKΣ¹ÿ£ÔÉy@ùb.?Æå79ŸÉy„óÝÌ÷ç{¹þ .«\sù7Iç…÷2så#_ñ¯õçM/$W3D¬ê7ÜúúÓÿ èÎQG¾“|ùSòè7M¿Dq ùWò•´†_ᣎ€ðS wgÇ8¦Â|3F ¥ãâ:\#Õùkü„Å/‚÷k…ê¥ À–4–¹.»}%GûŠÅÈ´ÜôÙÁª%#h,S‘¡…¬ô R-Ac™ ÅíB¦ÎeéœZ²u²ì’Œ¾Cf}î98$Ë,¥w!C1¡’#`´äåj¨^Èõˆ¹o_þ^™¿vhöŸ2H}Ñ™³ah-;Kpá¿2ópPìû}º šÈ„|÷ïñF+Çû#qu0¬_õ!sÂÓAæç’%ÒóXeøA¹¯8`~÷/xê2˜pïd|þ€ˆŠøvvZ⟎i¦ùôcÜFv.k˜+8.9£ Œ?d{x-{@°fU(l¬¿˜öKa-¯Ñ]p‘íóš‚%Áú¿¡Xü–÷¬%‡P¸Þ=Þ‹¡m»b—IƒÐ(¦ B£w¹\¾›h'%}J?}c-“ÊúýߟXç[«%·†“­:ø’ÑvÕŸ‘Šù6«Q<&[:_fE«–P ßH\í„:ÒC×Jx’¾á óHKô¹ ^–‰ÆÓ`~P71F~ Ik¨ÓÝŠx׿¾@äz‡§»ÁfNtO÷îµfë×.á>RÄwzò 4£_ bšè»ß˜ÓBÓDkï2L“Y7MéTL—ƒøVOþåŠá´å£Ý8pàÀÁˆãoPK¿tIrù>PK项Jextension.vsixmanifest…S[oÚ0}Ÿ´ÿ`ù­Rã\í:%TÓJ%¤"ÁФiNü<;³Öüû™@Bb{ógŸãs‘>¿9ÚƒÒ\ŠûÄÃD*Y„+³q>âçÉûwaLÓÍ`N߀6hÝRâHö¡#¼5¦üäº:ÝBA5)xª¤–CRY¸ öË”»×oÎãžïc«€P8C5´™ìù"‹’ŠšPïñ) ™“<9#ïÃÈIØÃØa`O¼Ä§à³×&ÞW*²ÊF‰0çÛ£¸Jr®· "|º#·õðÂu™ÓzA ˜ô-„nÿ¤ƒN/›L‹Ò:?PЉC,§h"»™Ã™Ð†æ9탽U˜¦yWíšëŠæKS1.Í•°¥õbÿðÇÄûÙ† Ý+k¼aÛN9ès˜Ó^=Ð{U6ò©vdñcÔ롇Bd1]¡Úw3"ãû»~Å·„.‚ÍãWrÈ1ý\ûΩÙþQ¹ËlΊÝõVüòÃOþPKPüF9Ü*PK项J manifest.json¥’ÉnÛ0†ßE·¶:ÜÉܸ ð¡EA(Ù¤:²äJJ×ð»—2Ò¹´‡ž8äÌüß,<)wÅýzõõËÐïãvB¥íǦ;• µÁ",71ª%B—›ÀÙ2Äì j0ÂÅ¢ø‡1õ]VA%äût:ÆYrL¯³7«ÿEÄ×)v3Ä¥!K=¤nœš¶ ix¬ë¬uè;Q×+çëÚÿëšPºß=?íËï'’Uv©cq÷p¾ZŸ›Ã\æÇ?Úå\ê¡éÒ.ŽSŸÌx‘D*È‚æØi&+¬gV{çJ-ÃNiЉj%œ3CxåLE˜3Þ—Å{ä»Q„¶½…U¹h"§*ÊqÎ-„g‚VÜIcœWŽxA$Ê“Ã;+ ÚJHÎôßaǰ¹…ªheÖZyÅ=(G‘J4öƒcL1š+@®ò¢©ö–ñÜ®äÿ‚= qwËã–#E©gÒ0ä …«Àaä$寖ß\5#¬Ê½Y+"+À¯5†âò¸(Þ–¿N?3’2 [!cb·MóŠÏŧ´ú±ßMå}_šv=½„Ô_ÿ]ßÅnÊÖ}HSýPˆ•°@¼„Ååò PKb$¼ÝÌPK项JVSIXProject1.pkgdef­’]KQ†ßë ÿêeŠkÒE©¤môAv±î®b©…[AD½zθÚ%]È0gÏ̼;3ïÌùü¸QAgz@žt¬X¯Ø55V‚'ЉéT0‘žâOÀÔ°GH6’" vÏÙç[­kMÚWNy¢žÊÜœ/÷™3̈L¨®v-^Äêš§¨m¢]΢ö@õ°œ¯ªŠv@Tíë¡ï™:MÕÓüžJ dÙÔÉé¸àš¤øiÏ•ù-ð}ô;cžÊóœ¿Ïq‘©›Ú*yfg¼|3n¯Æ=”¢;·Œ©ËSÐ%sÃr€ßqðuž«¡¶á:|}êÀ·”À¶YS à:›ä覮ҙßQÏÍÜíã=×lS±-k<ïs}jh¸i×WìM/ÛZ›Û˜·‘¤^õëtýoj#S§dÝRÛż´C§_PK§ñÒ?R¦PK项J[Content_Types].xml¥’=nÃ0 …¯"h-d¹Š¢°“¡?'H J´¬D–‘1ÜÛW‰ƒ©ë!I>¾ï`³ÏFÈèbhù}UsAGã‚mùÇî]<ñí¦Ù}%@V¤[Þ¥g)Q÷0(¬b‚P&]̃¢Rf+“ÒeA>Ôõ£Ô1tòà›æ:uôÄÞ¦Òž±ÿé0¢›\HWVÉ•øé` tKKɫ˭åùƒ7ßPK·—‘ïPK-项J³„W(°@ _rels/.relsPK-项JÿLßm/ écatalog.jsonPK-项JWKÇe:package/services/digital-signature/_rels/origin.psdor.relsPK-项J/¿package/services/digital-signature/origin.psdorPK-项JhÑÜsQpackage/services/digital-signature/xml-signature/27jgiftvsf5ljdlq5l35xzaye.psdsxsPK-项J~E€oe7® VSIXProject1.dllPK-项J¿tIrù>±DVSIXProject1.pdbPK-项JPüF9Ü*èLextension.vsixmanifestPK-项Jb$¼ÝÌ Omanifest.jsonPK-项J§ñÒ?R¦QVSIXProject1.pkgdefPK-项J·—‘ï¢R[Content_Types].xmlPK 4äSrelic-7.6.1/functest/packages/WindowsFormsApplication1.exe000066400000000000000000000211101455105530300236410ustar00rootroot00000000000000MZÿÿ¸@€º´ Í!¸LÍ!This program cannot be run in DOS mode. $PEL„s®Xà" 0v5 @@ €¨`…$5O@øH` ì3  H.text|  `.rsrcø@@@.reloc `@BX5H"è ü1pl3€b( (s }*^{{o o *z,{, {o ( *0çs }( {o {s o {rpo {# s o! {o" {rpo "À@"PAs# ($ (%   s (& (' {o( rp( rpo þs) (* (+ (, *Z(- (. s(/ *(0 *®~-rpÐ(1 o2 s3 €~*~*€*~*(4 *Vs (5 t€*BSJB v4.0.30319l#~€´#Strings4 x#US¬ #GUID¼ ,#BlobW  ú3, 5€æíæ›´xÜ c D Ô   ¹ ó ÈÇYÇ' X ÇÞ'.œ‘ ‹ÞÀÇÀ+Àg„~´>ægH ‘¯‘öö °ÞÀ_|N|E| §Þ ‚Þ2wÀ ÝÞÉÀœÀ¨ ö<A€¸Q ‡Q¾‡y XW [_ÌcægnkP †§i So Ä  K“!‘Økª!ƒ§²!“cvÞ!“Î{å!“Ú€í!–†ô!†§ü!‘­k\Qr  §§§ )§1§9§A§I§Q§Y§a§i§q§y§±§¹§Á§ɧé§ §‰§‰T&ùŽ™§ùeù7 §*ùé0ùÀ§*ù.7ù—§>!óD!~KD7ùÑR1]X9§^Tdùsù€A«kA)oADt¡§I®zI¤ƒÑ§‰ñ§aa‘)›Â. š.£.Â.#Ë.+é.3é.;é.CË.Kï.Sé.[é.c.k1.s>I›Âƒƒ‘ƒ‹Œƒ“Œ {Œ£“Œ£ƒÒg‹îC•  €µšKšÞ£.šÀ¬|¸@label1Form1WindowsFormsApplication1Class1ClassLibrary1SizeFmscorlibadd_LoadAddSynchronizeddefaultInstanceset_AutoScaleModeIDisposableRuntimeTypeHandleGetTypeFromHandleset_NameTypeget_Cultureset_CultureresourceCultureApplicationSettingsBaseDisposeEditorBrowsableStateSTAThreadAttributeCompilerGeneratedAttributeGuidAttributeGeneratedCodeAttributeDebuggerNonUserCodeAttributeDebuggableAttributeEditorBrowsableAttributeComVisibleAttributeAssemblyTitleAttributeAssemblyTrademarkAttributeTargetFrameworkAttributeAssemblyFileVersionAttributeAssemblyConfigurationAttributeAssemblyDescriptionAttributeCompilationRelaxationsAttributeAssemblyProductAttributeAssemblyCopyrightAttributeAssemblyCompanyAttributeRuntimeCompatibilityAttributevalueWindowsFormsApplication1.exeset_Sizeset_AutoSizeset_ClientSizeasdfSystem.Runtime.VersioningdisposingSystem.DrawingLabelSystem.ComponentModelContainerControlProgramSystemFormresourceManMainApplicationset_LocationSystem.ConfigurationSystem.GlobalizationSystem.ReflectionControlCollectionRunCultureInfoFoofoosenderget_ResourceManagerEventHandlerSystem.CodeDom.CompilerIContainer.ctor.cctorSystem.DiagnosticsSystem.Runtime.InteropServicesSystem.Runtime.CompilerServicesSystem.ResourcesWindowsFormsApplication1.Form1.resourcesWindowsFormsApplication1.Properties.Resources.resourcesDebuggingModesWindowsFormsApplication1.PropertiesEnableVisualStylesSettingsEventArgsget_ControlsSystem.Windows.Formsset_AutoScaleDimensionscomponentsObjectget_DefaultSetCompatibleTextRenderingDefaultInitializeComponentPointSuspendLayoutResumeLayoutPerformLayoutset_Textset_TabIndexget_Assembly label1 Form1[WindowsFormsApplication1.Properties.Resources¦ì­ u5VE¯‚E¶¾ûó       q   €… €‰   € €• €™ }  €A€¥€© €­ €­€±€±·z\V4à‰[Tí©Ä"°?_Õ :€ $€”$RSA1CMyÆ(%iz­a'zœ „¯2'fÿ¨®!pÍ÷ƒ^7Vh‚ŠÁEQ÷Ðó‘¡úzµ¡}3RÛÒÇ‘ŽS©³2l–5à¾$=”ÂÖ‹µ½kJÆz?a4K«áç1Ãl “\üKBPåÿØê$¤oßò5$æ•a:kD7=g£ÅEIMim UimmimTWrapNonExceptionThrowsWindowsFormsApplication1Copyright © 2017)$9a873a8a-ab20-4250-afe6-3a39da3ee84d 1.0.0.0M.NETFramework,Version=v4.5.2TFrameworkDisplayName.NET Framework 4.5.2@3System.Resources.Tools.StronglyTypedResourceBuilder4.0.0.0YKMicrosoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator11.0.0.0´ÎÊムlSystem.Resources.ResourceReader, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089#System.Resources.RuntimeResourceSetPADPADP´´ÎÊムlSystem.Resources.ResourceReader, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089#System.Resources.RuntimeResourceSetPADPADP´Íé`J"cÉJõâ‹Î¹˜Àá#T(Sôß|‘òQšÎŒ°ÿ4͵zd ç¯RQÛV¿ß¾ë#UÛ7œEð× ÛWKÒf.‹i!A:u_% ¨Äƒ¹¸;7~šGÅÒÚýü«fNþ.‡òÿüKmÊy”3œñf¶ü¿@è „s®X4RSDSK?·6ø?QE•‡xj¥DýC:\Users\radigan\Documents\Visual Studio 2015\Projects\ClassLibrary1\WindowsFormsApplication1\obj\Release\WindowsFormsApplication1.pdbL5f5 X5_CorExeMainmscoree.dllÿ% @€0€HX@œœ4VS_VERSION_INFO½ïþ?DVarFileInfo$Translation°üStringFileInfoØ000004b0Comments"CompanyNameZFileDescriptionWindowsFormsApplication10FileVersion1.0.0.0ZInternalNameWindowsFormsApplication1.exeHLegalCopyrightCopyright © 2017*LegalTrademarksbOriginalFilenameWindowsFormsApplication1.exeRProductNameWindowsFormsApplication14ProductVersion1.0.0.08Assembly Version1.0.0.00 x5H0‚7 *†H†÷  ‚(0‚$10  `†He0\ +‚7 N0L0 +‚70  ¢€010  `†He ²ÿ,aùñPl=³r26|ð2ÞåÖOóθyQÿ²yc ‚50‚10‚š ?ÇÓƒZ›ƒEÃs“Ëdí¯0  *†H†÷  0W1U0SUL{4E7ADCCD-7571-4FED-81D0-6F93A58F6866}0 170223050937Z 180223110937Z0W1U0SUL{4E7ADCCD-7571-4FED-81D0-6F93A58F6866}0Ÿ0  *†H†÷ 0‰Å£g=7Dk:a•æ$5òßo¤$êØÿåPBKü\“ lÃ1çá«K4a?zÆJk½µ‹Ö”=$¾à5–l2³©SŽ‘ÇÒÛR3}¡µzú¡‘óÐ÷QEÁŠ‚hV7^ƒ÷Íp!®¨ÿf'2¯„ œz'a­zi%(ÆyMC0  *†H†÷  ³MÉÒ]W`0^†Äq¶k•…4 ØÈ§:ûQè*Ã4ʈ`„Î.„.ˆN‡EÒÏ’БëL̓4Ïðõü½òá©ÃœµÑRTæ B? ÝÂ)ˆ²x}4ižÑÖTÕ þÏø9o}/‹AEfèÁ=r|Ã\qdÐ\x*"*01‚u0‚q0k0W1U0SUL{4E7ADCCD-7571-4FED-81D0-6F93A58F6866}?ÇÓƒZ›ƒEÃs“Ëdí¯0  `†He ^0 +‚7 100 *†H†÷  1  +‚70/ *†H†÷  1" Ï ~Fþÿ.x* r}y-0ÙþQzý*Eú¡<Ø0  *†H†÷ €¡í‚°Õ;iÔÅà¡çÿï¿ÌBõ¯¿Ÿ 0ÿpÈëàÕ“³éõÂyUÊ”¤{|ç,{Òy•õ·—ƒÄ‰ò‹¶ð h£ÿbu0+daŸ…VêcÃÑ´jÝMq—Ú®…Í 8D‘£Y;%/M sä¿é‰ð5T)é¾Ò”2 »relic-7.6.1/functest/packages/WindowsFormsApplication1.exe.manifest000066400000000000000000000074321455105530300254610ustar00rootroot00000000000000 mEIDHZ9DNGbmD71u7W8cE7E8w+gluSLrxEL4Yc6pDqA= BNK5eAh/wvVKrg5vt+3W1rXKffKcVFRYk2ZA+4Gf8IU= relic-7.6.1/functest/packages/dummy.apk000066400000000000000000000020561455105530300200700ustar00rootroot00000000000000PKL|ŽKYîÇ:ß­AndroidManifest.xml“OOAÆg»ô´…R -åïx v‹Ðx«H„Š)™a;À¦ÛÝÍîíIŒgO^ýFôìÙá0ñîAŸ¡3tÜ@tš_fö™÷}Þ™éŒI2ä}ŠƒÌ“/ B–‰l!ïȰe@”Á"¸6ÀcÐ DàxÞ’Aþð|ßÀ0ïxÁ1x >ïà'øÒXÏ$H&ù9ë2ô.=a.qlßCçÑ+õ‚º=Fòù®ã´;G,ŒLg³ƒÑ¦ßfï¦È)8 Ïÿ+œº®ÿüµ;½_Q/üG-î’ÑÑÐïyíQÕ4ïYk$•Zß°Ä`bbý¾µf­Õš§ÛûÏ0Em.31¸pxŸ¤ÓÔk‡¾Ó&‹‹rdÑÐ>·\ç”Ù}ÛeÖÑVë`g¿IªUàxœyܸY{&Y^ŽMÚ”³3?ì[»ÃææöV‹”Ë*DîàÚ; ×AÊ`u*®¶ßµØ Ú °Sß?¡¡ÕíëѵڿB¬=êx µáÕÕs΃‡õzd㯣‘¥›: :õEuu&ùü`7µSÇå,ÄÒºÔÃÁD'ßeœÖÚ”Sb€?‡ž1ô"Õ¢v‡\ò`pa $€ V@.as`à|4 ãnR„HY¸^ä7Ú$úªø†~¬é¢Íc\ÂÏ”oAÄ¥ä<ÞEÁZäoÅÌÉyñV*R«Hÿ]«ü—¤BóOjþ©e4M|›rq/QC¼½¬¦ÏÈ)­úI’{Y<ûkm‰d¯´„¦áõ^i£C­(z¡Ý eµ\¹bmâÿ*jk+ȵÚÚÄYÏÊs›•{ZA?¥åÍÉ<Õ”wI‹™¸Å{JzOiçÏS~3š^¾Å¯$ýJš_3M=CÊT?å‘ÌT¶×ý(KÍ?›Ëú7´Ì Í“ÿP=bZpÖ ÊJè¢GÈ|#³¸ˆ4¹c)84.ÒôBÉ-ÅžEº Û &—¿UìÐïíÃc~,/‹QëN½©; —†=cäŽ=c:±'ÆÒ›.;ð=l·Â5‡‚Pe[Ã!%¥C^?ÆJžÞÀB\‹F6ß°èÓš*È/¸œ2#°•Pš% HJ|P‘ä¹.õWMœT=\56ä_ b‹­WWÒ¦]Íêµè4— `1P”< “—͘uùó[ˆ9«J·QÌv\€vh^ ±%Aºƒ_5†­Òç"ÉK¥Þº÷7ÁÖ_ÝÓfjY!#À¤WPï8dTòT˜%q‰©–nªX§û¤\Ý=lÝõúÍb5¬WJJ™¡+É-ã"8CTh¶K€’›‚ctøîÐZZ†= ~#Ëwqw„#ÃøÏŸôÎ}ËÚ*ËàOÐ.ï%L©5ßIm÷ã$ÁsõÒP­I|™õÄq¨ WÜ7¦ŸßSn5»nÞ@1;*á¡ÏX¹ÁªJÓ4âXÃ’²ØÎ©[ðö_l·Ù8V®Ù|¹xp˜xràß7à GžÎˆMöÈN_rpʳG+g@T}=áÎ91º“1ŒEï/relic-7.6.1/functest/packages/dummy.dmg000066400000000000000000000774051455105530300200760ustar00rootroot00000000000000xíб Â`àÛ@ã6B°²rAÒ;AÄ "V¶â àÇ‘ÁÞVÁ4vð½æÞÁU_6ë&)U©²èVÏ{™,sé·Íµ¯çÙíUç{£ñð ±Ë!ç3ýä)möé~ @€ @€ @€ @€ @€ @€ @€ ð·Àifxxc`#9ä…PaÀ€ 0`ÀÀËÀ xsuóTp a``dˆa``Ø9¹d5b0‘ QP‚ÒBPñ‹Í{yš¿›øtÇ_Èzþ€0T¼¨„ýÈ©Q0ÈC‚TZxc`£!0£!0£!0£!0£!0#%<´X ô @~¾Ëùcó].ƒ/`6—Ág ÍÄì @Šñ/Oâß@-@ ˆ¤˜ Ì‚‰@9ÈTˆÿùðÛÁ«!b ³@n Ä$ˆ$é$£POHŸ3AiŸ<€jÞ' ! sÉ ó >™#Ä”™G¾KFuކJH÷fà Û‹Ñê üá‹Ñ^k­ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @àAhDôõŠec4†s{ÉC™“¹È4P‹w9½xC9 @€ @€ @€è <ÞŠVœˆåÝ * @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€xH}yêYTy]§>Íþ¬¯Ê¼²l›Œ8Ÿå@æE¾[ÔÑ ž @€ÜžÀŠV´âD,ï´¾Þˆò7ÿHæF,‰}q<ŽäÏÉêm@ýn yixâJ;_íôËæ¿g=ß<ÿÛ̶[ÖÊ»„ª½å B3syëpã1'sK3‹ñØÛcWl±3ŽÅ¡x?vÇ{±?×_ȵcYÛ›ëïfídµ­ìÝË2êr&%ΊºŠ¾ÞÜ^6·ÉZyëQ¥1™?ó¤bÔŒ¡Y>ãy¼£ùÓˆéÜWFZyixÓÅ:¯Ms[¦9NÝ#Öûê囿LOµ×‹L&²hÆêÎê¼"õÙÏœûÈüç>YíFò¯ç^f¬¨Ö3vg1•¾£³úÞÍÜ–±Ê»¨N½fcMÕf0¶åµñNuÅ”åT{––äÞ±{0Kƒ9NJLc™ë˜VÆÍGÞTµXoT1íË+wW^Á3º2kÇ«kyæZu¢«c)ñm¨Ž=/æÑ¤Èx^¯oçѧòÈÍ8]šdZ?ÿ1O}žmºWKÕ£½èyÝVûú/·›d¿ÆŸYïÙ¿ÝæþåJË'ÝKé¿7^ÎÙ˜Ñ_3¿Dõ¤ëy&³$fô˻ӱXšO°Ã9ß9˜qß0ÿÑâž×_Šûµ¬öŒ¶Ýæþå :–OÐWò˜Êk±\ù{«zí_¢Y—"ê\=A/Ô!ÞÑ´îR–åÞ,sPÏÈX~æÍwn¬Z/ëÞ¥7Ç93s«çÎÜßdßL82sƒgê1ÊŒ5¾/ƒµ×ÿÿ¢8–Ï¡ò^Dç3*mË3¸c>û>+wÞš9¿c̈®+zãgç©ò;FO‘žw^6Žæg“¥È´5»–ï.²|@RQZ›¹kRö•{¥#ØëŽ)mÖwûEï'|iU>ê£Üü$Ê],f'ný<ôíЙ¡ ¾6¸eéÈÀÏG—ìéoõýÒ÷Vßöæͯšš[Ÿ6Æã“Ùÿ/æó; @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ÿJà§¹FBxsuóTp a``dˆa``øðêÌo äA€±„V‚ò… ü‹Í{yš¿›øtÇ_ÈzþT¾Hƒ°Ÿã9 5 yžcoxc`C8þýÿÿˆ^0 Ý¡«½a*xí xSU¶øw‡(UPê“Pq…¤…RP $M éPh ¥VG<=MNÛH^$'´ÔŒ¯±"ÿ[ÇÇxÿãpqFqäósf?øF™Q`ô¯~ÜúšÆã  ù¯µÏ9ÉÉI“âˆß½sïÚ|ûì½öÞkíµ{ŸWè9§zvw(h].ÅâHxfY…­¼Ì*…} Ü1³lqÓœÉ3ÊfÏ^=®¶ÑÕt¹§Î â²Õ³¸¦¡Þe-›l·;£Ñ d·×6ÕZ= õÞ&+ذÛë”YË:e9z‰ÝÞÕÕe±•Í aøÝ‹D¥˜œlc“AÁæ—ýeÐb=Ç(õ|ò¬áê—JÉYí ¯¶cJ°ÆžS5¥ÿºX¨ZŠÅ«IñH"æ“âvMwXµKHh8¯‰íÂ+mÁh,rµ®±êŸÚ8•¥ÌÓYSÌuIŽNYW“Äଊòòòj;Ïb¿vÍ*¶ÎɬÁˆOEeãàÆgLjÈ4—B}w™Áuü9wjĸ4’B®(ÒC¡‘iÎgq>|~sç>3{Ìï½|þ„ñ«ì !*àbEalÚ'ξÒVë¼r$&WúSŠ.† sbbHêŠÄ–ÆWy;Řä×x‚‰Žúp|¦“iñ¸¼RlyVø*·„/¾j¾èkô®j´ÅÄXÒ>Á™#!F±ÊÈA\ž«"p½,…â'\ìa).Kþ Ôc^ê…±Ú.ÌX3î…õáöÈ•ßÁRŸRdz–ÌöÈqžÜÂÃïï`¤ Ší®ÅƵ™=vd!çZ§£3s‘84”;zýà*û’‹ .šã¿×H¡6Éï—ü°{Ä"Ëx6‡ ž, :Ñÿ°Š­#õ<Ð.ø kñlÊOd³†3 D€"@ˆÀwFà•£?ÆÍ&Ɔ@jaÌÁ˜© 6ìd()È©L—iCˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€W¯þø(<øo‚ÿ™âï9™±›˜rŒÑ Q<ιuWÔ-jÄÒÜï 0 –Ah§©®¥)·9×wä–ák0 VãI\B}Yê–U£½MÓa0P~!ô…e5« b)%£l!.'Úâ™"£½·.Uì-P[h²¦0Tµw‚Z Ø:ùkÖ ÌhoBµbïÇj{”‘{&äû×h—§ RXŽ%óí=¡ÚÓÆˆr޽Œa%#¾H8žhôo‹jÏ£N*ÊÅíÅÿä$¼WNj7Ú+™¥Œ7¢ú¡É·2"ðS쵤 ?ä7ÚK©ö¬ªÊÇäøŒË1£½uª=í…S( ÿÄx<â““F{U{媔É?x[— ŒöÞQí Uí¡|Löp>`Yí}`°‡rq{‰pW ìð<ðÁhï#ÕÞ½ª(·'u íøöBEÁhï–ÙÊz‰ªöPÆcS¡€Ç•°V¡ÖÙä\ ¼ÊÑÅ¡Øaò`HѦK /‘ì^QØÞfn 6Ž\¿pÿ׎QØ$?Ëõo3èã.¦ÙC¹Ø8!ÒvµO„Ä §Â8Þ¨S±§­”‹ÍC.7pD )7yp(‰ÆLóU‚¢O†Ú"A!*ÇÔyPÚòmJ±×©¬éb¾tfÖž·øqBê–| Yl J¬–N»ÍfÏ¿¡„ïAÄóÃ~8ßáØ'ª2žï0œ‘×ï‚óÝ<^D"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€üKXìîùpOüMî’çFŽ­eŒË—ä Sï™–<×Þ‹õÎËœÍÎÅÎ&¯ÇÝóœûi· F&Y»ÜOEbAÿ8Æ>ÞóüMçÈß¾ìîyÉݳ«ç•†žgœ›‡ó÷¸íÄ¿Á½šÿÛó<´tßð¼µ¾çÅúž7œ=˜ßóžõPØÐ3µ¡gåPgêSí-¾’†ž¥¥·¸¬î—\ð—þ ò’ ÿSükRL˜¾èr›ÀÜÐ8¯t.q^åÀgÛȱ7b9ÖÁÇç\ìÙ3d÷š™…»>²ÇVlAeç„¡èxÜ«ß3Ý´E6_þ¨< 0Ük¦ÅAsO9zß3¸ \®ðs¯섚ŠÏµgxBk$ø8þ¥ì’½ûàïo³>!Ï%{ñA ¥½û†J¡ËÉj—ƒÁŸRìu<ìg—˜{õIø‹»§aüPÈ?ª˜/q¯ž¹ókš ¼çÅ=wA7àÚs A#Ǧ@›yÝ«‡ÀXºÍ?xÎÝó•»çY÷3‡­î/ßrûö»MGŸNC“Š—ë*¶ìiäú–<×›`|IèÞ8¾„:¾áùãÓÍAÎúÁNp÷Ôm‚ñ5€Ÿ{>…ÞÝ«Ýë{žw÷Ìßì^ítÔ÷üÕ}Ö‘îúö×O|©¡çkwÏ»î‰Ï@õvwÏÜ=ï»'Â@Þq÷,ÙZßóaýÄz>ÚóÚéyÌ^“N§ÝkNû5§ôfÅ{ª`V'6ׯ^PF¶4ô¤Ý=»zÞoèy/½c<ŚϺW×m‡š†ž¿ì9æ¯nª4t¦_oèùÔ½:½¿åL7[Ý«¯ÙÔÐó HéÅ[@Ø ÖÔF<ã^#½©[Œ|ÿѯIã|¼è~æ(ÌÇn·ï ÛôŸ;+^Þó4÷ f×/®Ç uàdf=Þ  Ûß¿go:x4P×#¶g9íß«…†Ÿ.«g†q-ÃbéTôÞ€ªËu“5r,ß_–ìý-`LŸo…êôùe|;žo¿Ç·øöB¾Ä·6¾-çÛ)|[É·U|;¶ kι§7}~rc½èÛû観 sï`.й˜{s[1·sOaî æÃ\ Ø»sÛ0·sÓ`î½s×aî6Ì=€¹ë1·sݘ+worðäÎWé´? %Á°öC+–áoá›R[@ÙGGN÷B‡À»LõqKXba°èX”á#‡˜Š ßv8ÆôN5½ÒÈ÷CŠ›¡ÅÅØó`}4AÚO©q7¤§@ûG!}"d™æ7Oáé(â¾ÎP}uƒ¯°Úº6z_a2î€t¦š®Ôz˜ºaÁh¾U«¾½s4ß·ÐGÌ‹ B4½XzÈA>6óP•4σ­ùØÌeããÓÕG*‘¯‡±n–¶O!1a¦‡Ü ó O™¤ùØÂ±0«7Fëfîa/ªð€O 0ö„A®‰ø“Œo€Ô¥ "q8fóy½ ä[ âÒÊw@W†²â×ÐÓýb=<—ÀÝ$ˆ0…a0íh MÌf6ÈlÆçÆk¡ãÛð 8ÌãÜ`#á@´ÂŸÍ3í‡Ç­0åðÌô ­`ÖØàý&f)K053SSfÙdaf+¨8Ø2óƒ,ÌdÍ­+1Sw‰©dÄ¡¸VÑ_Œ-‡`@´ç”Õæl =3ÓŸž¦oLûªµUc¬!¹(uåñSËo›™åê2þˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€d ÌPŸ÷¯QÓ5mVÓV5½ZMe5½VMoQÓ«é½jú šnRÓl¹¹µO¦ÓŸÂ vBzÒÜZ’ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€üï pJYIt³9Z²ð<¶G<ÊáOýB\N´ m°_б…±M¦ÞÑa|¼²&âON÷vÚåÅõSѨ§)ÈR[ÂsûL½y*›R¶âUšjEÅe`:Ò57ID›ç‡Yjh‘úd÷\§sš×'…%§s¾Ï`ªRðˆ~ ÜÑ &# ¹Ù鬘‚Îøí)æt^æ@Èí~šªìwE²–ÑÅZ$»— à&¯sŠX·h…ÓY^7Cób‘ ]G€ó£¹†9TŽÙé´Q7zWµÁ\ð– W,ïhZÖÝe¬©l’º‘’âi¥Ú8^ÞØè«2·åÓ„¸Tr•W.êUp4!Çaì‚kzcBF ç¤B˜£Íb0!5ÃŒÂà°IU=oß,Ϲ"ÏQ¾t4ó• ¸œ×…¯nà>|uvRQ) óØ…+’Ëê(¼owÓ'ÍÑl=ÍÌšXcœ0…TeÑS82>Í)qÜÇ„9‹\uBC£³V¯Á’™Yª¥÷ô"­\ÁHÂÏÛ•öŽÁ}2)_ÖÌR%ö–ӊĤZQYj‹²?ã}Æ s€—_”؉·fÕÎ@/†€/ÎzÏ e}HìÀ÷Þ³hØñ©~”öžS¤m­ë €··d½=·Xó@<*ʾN̯ºelÚrlMëøßÃUÄÉâ´ê5/ Èa)oÂõf3 B¨Sº%_B–„NIÄK¶1fe/ÊßnŠ”””l1•——XKZ°I™KÊËlíPÆàUM¬¦NŒZƒ Ž4•`–™ –*)óP[(ÏÈõ9iï2ÈOäÏ òy`\o¿Æ ·ä ònƒ¼ß BP”ñ¿Ò /4ȃÜc4Èäó,¹ýUä; òùbðTïo‹A^aß4ÈÕ0«zý”A¾S‘‡À4ðp/—°U“Š|‚&÷)2¬%X ÆÄÃ4y’"×d¯"Ÿ¨É]ŠE>]“p™ÁV çÀÈgF®1ÈË òE†YULWF8t;?â9Ï…8âTˆ•§A„%ÅT8} 00Ë:‚F¸¡!(„ÆAâ@Æ@Äýø ˆgB< âXˆUÿ@‡þŽ@…îåÊ¥1Ü BÅ7:Ùl÷.댷’ú†Çz­ ×9¦›Ïb gû»ñÔ«çÕÛm§ÞDW /ltŠxIw'†Oæ®G½C‡[Ÿ¼&Æûcƒœ %c¥v«+ï÷ÒI_Ÿw‹›W™Ë]¹éý®®²ôãõU.ÅãmìÏ£]̧ -¸QÏëâ^aõg"³’øýs^ywÏy-Žëek1ëljc>EÁ7©·‘ù|ç7üþŒP¸Aæ„"M´Ÿ Š7ÑÝo˜ùI x3å÷€âm2?n¦þ P¤vß_¸É1 M¹Ù/lD¹Í/\¯»Á/ÜHwG_¸Q{ù xó^¸îÖºCÊÖ¹7:–FÆ»¢¬NÎýPÞO×1Ñ»ÄnŸVUQ9­/§ØáÝ#¶ÀÕÎgÕ®mñò .­,—(e–9ÂuÝ ñÒ} ƒõ'03^%a[¶iˆ×p¹µk¿ÕÌ2ײPV$8´:ÜÃÛâbÜæO„BIvóîîCÛV¯üyGkKbûµkG¼ÿðØmÕŒùÉuÏ\±ôOk5µBé¸{䕟Ü}crØ#µc~© î»»ñÆ‹v|õ例?Ÿ}hnß›…ô´òö›w—üüVû»gUmÛÚ¶që¯=¿ï”¥óúûW]ñIÛÚŸÞ“´ö.xH:9Ñ}Ûàï¯Û´hÁÕ©§_:ó“îœþàס›×—8çš5oWwß¼ùÕûw½þuߨ_/Øyté«£…øˆÒû¯ýM¥5îÏ/Úf>µrïÛ£¿|½±ªóÊYþèW­Ëß~âó–ÒUϺï’í×<{î|•Þ½l쳪gw‡‚ÖåR,?Ï,«°•—Y¥°þë%Ü1³lqÓœÉ3ÊfÏ^=®¶ÑÕt¹§Î ÂoìVÏ⚆z—µl²ÝW•AÉn¯mªµzê½MV°a·×-(³–uÊrvÞ®®.›ˆ­l°¿`øÝ‹D¥˜œÄì'ƒ‚Í/ûË ÅzŽ;PêøäYÇU/•’³p—SŒÅá“X@N¢89.†ým‘îj;¶¦r,!Ù‹è´‚RÜ–ˆK±Éq)?ÀJ~[ ~z™ “F+ÕvŃj;÷oÖp ’íW–Ÿ7âÜâ.™L¦òi#ÊûiTÔP;¼ûDœ/¼A¤@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆÀwBàµÃ…Oºð/á÷%/€x|Ñè&fåýá·"ÝÁãœ[wEÝ¢F^œ³áÊ9%ùÚÁo– BS]KSn=×wä–1UÆ4aÔ> *2|¿>ÛÖhï1ø˜%Ú›Ä?ÕÄÊ8®LÄR™Ä™R«x¢~kš{ŽÁGuþ•ÃG{Ñ?u¸ å{zcøvv< Ðh¯Rµçá“ßÐÿJò4A†¯Ǥv£½g*þET?4ÙàVFÌŒ·= ý!¿ÑÞÕžUÕ@¹øxUÿÀ· |¾Ûhoô,Å?íCµ( ÿÄx<â““F{VÕ~ÊÇä_4‘’ÑžGµ§}Æåc²‡óËÚh¯Å`åâöá®@Ø/àÃ×0£½VÕÞ¯øhC¹¸=©ShÁ×±£½¡³•ù8¨ÚC9óÑ\µLŸàqe¬UA¨u69Wã¯rtq(v¤<R´©Eüþ® tD²{Ea{n 6Ž\¿ð[¼Ú1 ›äïg¹þy@w1«ºŸ¡\lœ‚i»Ú'Bb‡„SaoÔ©ØÓÖ ÊÅæ!—8¢…”È<8”Dc¦ù*AQˆ'Cm‘ •cê<(mù6¥ØëÔŠ@Öt±? ‡ß%Ö‚ à·Ï5I›‡¬Ü™RìÍP‹PÖÛ˶Tr‚ÐÏÝóí­}>|£èhÇR”tż¹¹! BCý‚yuµõÊjcl‹Ú6•¶ð-s´))ÚÑÛ*c,ÅÐ×_ ¤[p©aû‹ŒM€|™1l‡sY Ññ cÓ1}•±M0ÙøÍgmMj}`Šíñ›Ñ…¦?26¾ŸJüv´"žÏì‰xÌ ´Ùñ»Š cÀoF£e¯ÿížÀ?ªî{jÒ<˜¼lÓ(³Bľ¹W–yžÏͰ[ÍB)ŽS `ühç6Œ.`¿­±Ç -ÿ¨L2ٽɸ,…ì ¶˜KÚçà!¤+[·g?Ãiã,µ7+ߤ‹Û]ºz°‡ög@Ôì3öH¦ìXa¼¸§Ùœ6u  *y0ê ö€Ë’\=ÅE[^õZ fûCB Wx<Þ®@»¼¸¾¿Á8íj%ÚK9rìâ¦ó‡Ÿöpöx>³7/ ëË×ßSUP_ûlÆŽKß)û ì6…õác±]PíÇ\)EõkáÀ±þã~˜áz‚ÊU[³¹ãwAÿºu’Ú<;|ÕõCûPmÆ—þÇ3 ?üK¶š‘|?𪷨ÚWn5èF?\±´ å3¸: è‡C±£ðôÑW¿’›±`ÔgíxÄ,ØÞœäéƒn}þuÝLçŠß¹þ[Šêó¯ïÓWÎ.ýÏ~W5ò ýÏ~¸W§Çs°Ãù³a«ÌEÆßÿ7}uÊóÇT”|òW§¬òœ¡óg°O±ãD¾fË›gÃ:Âó?NHÝð±MYl JBT”;í6›îø |"ž&Àù Ç>Q•ñ|‡áˆX¿œïæa "@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ Dà_‰À9&6z,Ä#+Ÿ^ÄÂn‡üã;õÈÊg~µÅ"ìJ»L; ¦õ_\f^ Ñ²þÈÊAÐÎtû?,C7°õ·íL­yø³¾5/|Æ>øä³wçÿŸ?\³ñ³qƒ¶|fZwä³ÃŒÝ9ÔšZ´gK¦-ìP Ø…G²v†xÄ!±Í Ûð¹Å´á ‹y׈ϠLnfë·šÙ©5ì⮓١ÖëÙÅðçå‘Ö믯n½þ†ê ,µ¨³Ìtñ‘•µà— ü«YPp®oG¿4>¾šÖq±Û‡ƒ½w¡T+kµüûƒ?}¼þ¬rÑ‘•NЖ`½£ÎÜÒ »i ¬*z?ü¾’¾ºjÍÜ;ò[µvl®Ro‚q¡?<_‘Ë Ÿ?06þZttú¶ýâýÔ˜[¬föGbëÕO¬ëœÍZ08‹µì¹€ñö)È¿sô,´ëtèû‡qßÀrÆý#cŽC)vðwú1¾wCj›ø¬uuߨÔ" ø‰óñØk5³C_¤ÓcZ]¹cpŽÏŽá§à?ŽëëÈãëw˜€·¸[€¬‰•ƒµéö¸.ªß`¾ïD?ÎH 'œç8´|:âÇ«~<Í4tÛÓfv;–UAþ?ÕütȧÕü È…zxléÎ÷ÁŽõë4gþ쥊O{,c6 ë+ þ’‡ú,§mxÊÞ³ŒÚà°ÌÞå„ràµaåô Jݨ ‹ ìÒ!ZÛQZ¡Ý„Q(—‚îé\—™fï곌¹4c«ì¾ v±½ÒVÉ? ûÇ‘•uÀ X¸€I °q®ÿ:ò"_SúµXhNÞ…9i…91ÃXpNjÁGœ/a>´5¹®Â°&KâT%…G÷î¼¥\¿&”ýWÓï³çê÷Ù”¶r:ýÜ7p­¥.d-¡µN¿¶?_°Íþ æf» óÛuùMº|¯.Õå=º|¹._¢Ëï¿@g_—ߤË÷êòQ5¿ìe<ûç*ûðþóáQðþþ»ôøŽÇdÌã‡ÇXyþªùýDÍ ùj~äªù¿B¾vb´óä·©ùw!?ͬ”ï†üujþmÈ? æß„üv5ÿgȇ¿ü¾ò«tÚŸ…’`_ àGÚ Ëðˆjs@êb ¡úJq´ï…>1€)öCxœ Êy<¹…<%  ÿL_Ÿž}8F¹TMG«éxHñïá1|àbìy°Ú öLP†öSjÜ éi ÿ(¤¯CħK4¿y OÄ@÷u†êëfðõB@[烾ÞWÙ°¹ÃÞzX¾õë¬o[UßÞ9šï[ èÿ‚ýš!š^ƒF,=äF>6ó]ÐwÒüKØš¯‡Í\6>>]}ì§ùÂTv³ô°v…Äl„™2|…‹¹îE\M§7FëO¸‡½¨Âþu>cO䚈?Éø˜A] "‡ÇÍø¼^ò-×éÊw@þzˆVÊVˆ_I§ï‡õàY>cVH1X!‚‡Ìø|6>_€ñ]x¶æq¾F°‘𬜠!@ßíŒ {²#LlHŽšØ ÛMlp «4³A­ ä·0Ë: 3÷Y˜iÌ ´·l†Ø q,<& ëY*!ކ&Áó‡8­ƒJ°¦‹˜©û\SɈ±ÜïR(»÷ ‚`¨=¹d­égf× ˆ}j}+äypŸlÅúñ ´@´ÂΜ£¯Ößa¹ûÎ<§ïu±ïn˜e ð¶ ºèƒâmj;Ìó ó± 6ªõøÌj}Æ9úÔ…vÜÆ©Úcpòúg¹9† ‡»("@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆÈ¸¾?ϱ¯SÓjºYMŸWÓíjúŽšîQÓjÊÔ÷ŒPÓR5?ÀûN}2þÞo0Òf£  D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€ÿEN)cÑ>SÔ½ð<¶‡=ÊáOýB\N´ m°_б…1wïh‡0>^Yñ'§{»íòâú©ÎhÔÓd©”¹7¯²²9 uñÚ_Á3ý<˜x+M»¢â20éš‹$¢ÍóÃ`æè14LvÏu:§y}RXr:çûX®ÍJÁ#úýpGƒ˜Œ$äf§³b :2?â´¤˜Óy™gð$׿4UÙe),£¯´Hv/ÀMÎÀ9E¬[´Âé,¯›¡y±Hˆ®#ÌùQ–ºEa©QâŒ9u§KÐÞdp@iÔSÃ[.\±¼£iK Íñ’7êNúÚ]P—SSÙ$u#*Å]$”Ú~¤ß©PZÇÏõÐ÷_åΰ²ʧ !q©ä*¯\QGrpT®é %œ¦ anLŒv6‹Á„Ô “ ƒÅ&Uõ¼}³<ç RI®ã|iT6âr^'¾º{ñÕåvÓO'•º]¸"‰°¬ŽÃðvpG}Ò͆ øÚ‘Ì<Š50…©7UÆéþ°UæM§5Ç¢² uGïiNAˆã>)Ìi\䪵Âx¥–Õ¼€ÌR›²;×éE𻂑„Ÿ+”öŽÁ:)_Ö Üß[ZL+“jEY„å›Ó(ÌŽ~QDÅÖ¬Ú™¨ñeðÅYïY´¬‰®™³hØñ©~”öžS¤m­ë €·ºAž[¬y e_'î‰c‹´Óƒ(éµ–iZßÈçZR¤Õ|Iá° ­N-Òª±íjÉ'–K.–º'Ëß:ªˆÎ„“W¸`ºYª»×:ºHÓÅá@{$jJF¥z?6ùñ7ÎRët][¸-ð«O׸ز€á¡½ÊI;Îø©J;‚æžg¦û”ƒ¸ÓǘîdK Ñõ²øÐ\­)üS“áØ\QÞ†¹š`Ä·4ÙÝ »¼«|ÑŠàœ+P³$Ws.§``…ä÷Ê18Í“’ÍSã<ߥ˜tº¼ÞLÇ¥9êü0ítÖU†Ä@8™Tz°æ4©¬ówHS½’ÜZrÃqS9wHrcT\–p'€«ÜİOÂV}+¬¯IÎÃAÉsUæÝ26m9¶¦õaÜïáˆ*âdñNZõš—ä°7áz³ðÒ'Au R·äKÈ’Ð)‰x©7ƬìEùÛÇBƒ•n6U:JJµâf£yPµÃÍÖeøê'VS'F­AGšJ0Ëß+Uª¤XÌC9lá}S¹Þ ' ò]ù)ƒü™A>Œëí×ä6ƒü€AÞm÷äQ@Š2þWä…9`{ òƒùqƒ|ž%·¿*ƒ|‡Aþ _ žêým1È+ ò›¹fU¯Ÿ2Èw*ò˜îå²¶JxR‘OÐä>E†U£ Ô˜˜c˜&ORäášìUä5¹K‘GhòZE>I“ŸVä“5ù=E©ÉCÀSèOY Px"Ÿ¢ÕÏUäS59¨È£4ùvE­ÉÏ*òišÜ§È§kò.3Ø*á9ðÌÈ5y¹A^£È0«J€éáÊŽbçgC<â¹§@œ ±â4ˆpÚ ``Ž!lŒPq`8˜*ˆÿ¤mÖß‘¤Ðí¢±\¹M€ûH¨øF'í‚íá¥`ñþQßðXÏùzcºã,¦`p¶¿»M½z^ý±ÝkêMôwÖ/x¢SÄK¸á0”x2w5êm9ÜÚä51ÞÌä\ð+µ›V]y¿—@úú¼[Ú¼Ê\îÊíwuµ¤ï¯“r)ÿûdcí¢ì8]0Á­x^ßðJ©?™•ÄïózÈ»%Îkq\/?‹Y?Nó)¾!H½Ìà;¿qðw 2¿i¢Ýöo¢»¿-Þ0sk_¼™r__¼Mæ–¾p3õn¾Híþ½p“cšrÓ^؈r»^¸^w£^¸‘îμp£÷ä…ð&¼p-Üut§“­/rs,Œw7Yœûš¼Ÿ®c¢?v‰Ý>­ª¢rZ%^±Ã»Gl+Ï «]£âe\u[.QÊ,s …ë³A7â…¤û@ëO`f¼B¶&lÓ¯ãàZs×~«™e®I¡¬Hphu&¸·ÅŸ͟…’ìæÝ݇¶­^ùóŽÖ–Äök׎xÿá±Úª?ó“ëž¹béŸÖjj…Òq÷È+?¹ûÆä°GjÇü(RÜwwãíøêÉ}>ûÐܾ7 éiåí7ï.ùù­öwϪڶ9´mãÖ ^{~ß)Kç?ô÷¯ºâ“¶µ?½'ií]ðtr¢û¶Áß_·iÑ‚«SO¿tæ/&Ü9ýÁ1®C7ÿ®/qÎ5kÞ®î¾yóª÷ïzýë¾Q¿^°óèÒWG ñ¥÷_ûËïWuüõ¬é7”­î\~ÿ™•ãNõXDßøÉÛ.xðÜŽ‰¿Xòðum‰¼¸·}Ë 7vìXjnlœ^õت7½õäjwϤ_T¿pǯ¿ê|)õ§Ÿ•‡Ã¥=M/þß*~»q£m“í…aµk_òêŠ}ãüíÖÇÿíÀÃnø·Ç&ÝqßÝß=õGû~zÑûþvÓ„ô@ö¿mý½W-Û|FÔõÿï¾W퇆–ˆ¿ëš¾þà5Ž£oõÖ¼pËï¿­ýôÇÍ;òýk6>ð—øÒ3ž~Æ;·Þ×zåEßúÐ~òè­gÜõæ¾§Vî}{ô—¯7Vu^9ëý³ÖýªuùÛO|ÞRºêùC÷]²ýš‡`/Àý¯òû—-ƒ½`VõìîPк\ŠÅá‡Ü™e¶ò2«öÁÿ·„;f–-nš3yFÙìYëÇÕ6ºš.÷ÔY£Aø ÝêY\ÓPï²–M¶Ûáª2(ÙíµMµVOC½·É 6ìöºeÖ²NYŽÂÎÛÕÕe±• öl·{b‘¨““øƒüdP°ùet£XÏqJýŸmDy?Šúj‡wŸˆó…7|ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆø/!ð/÷WþQ¦ù¢¯3–á+Eða¦Ì W•Ï,ÍšR>gzEµ]•Ô7¿ºæ(_4ª•–KÁH?‚´Hê€×Öµ%(é_µŽ,¿ cÔáop.¤–ý–Q ßêšytAuørP-¼Â\cÉfå]»FKU¶òBúøE!có¢þzDßRøv¾²Û¨çôx õãíŒÄdÕ=/ÿÞ•QÞÔ[P9‚ºä÷E¿”Ï(‹±˜ˆ/åV­Î L~£·EoÉ®µáïøÕfºª¼•QÛ䊄¢ðFßX¦Oµ§ì[w!Ç[ÄmÁàòÍ„¯5Ù*„¼ñÔ6ièweVL©›V>]ï:¾p8«Ôß\…D_$¯&Î]“YBì°MÍWòÖÎë×±)åuS**ûm_اŠ~»h×?ç-œŠ)ÓúáΛöëOÿ ¼óá@(ò&ã²*(Ÿ•#åÓònl¿[§bíhlkg«}yôHÙQç•dÚ´$ê*úzwwÙÜw"Öò­G••Ùø IiÔ,K|¦ã|'ãÓ(óq,{Ú¼ÐÚ}¹Îéµûrì‹´Ì©ÆúX½|ûêü\w;eê4E³lím©¯~ñÚ'‡_ûlvÚä_¯=G,Uë»·˜²í¦%mïgl³¯|Õ‹cÐhLUuÆË¾˜W3&—sÝQZGÛ`”Æ£Ÿ:eLíÈuL›ËgÞ]ÕØXÞªb:3÷pÌà÷"ºµÓÕ\^œC[@tu,ßÎêÜ­òJœíxˆLÇ|ý(Î>gn–/³J¤ÃÏyӟ-U‹îb༭Ž^ëV‰v?c}`ûn‡[äLnÇ“îÕð?ZÆh,êO —¨žt¯d‰Ä¢~¾;m—õñû0Æ»7‹î;‡Ÿm5î1ÿê”î7cu`´Ý:·È'h;ž ¯Å0s1gþÑj½öÏh¶‡È¥:WOÐKuˆ÷ô­›ä2ï̓zDÚñ7ìÜUÕÞпKïŒsqä¶.¹gж‘zq®fäÆÏ×}äˆ5~ÉκÛÿ‘Žù=”ßá):Ì(ëæ3¸g¾ô>Ë;ojÙß‹¢Û–‹ÞþÝy.ÿÆ(2ð΋ʥùíl‘öFÓüí"ÊG$¥Ò¶Èµèr“<–÷JOpГuvôÛ·Ëà'|ÖÊo€ú,w>‰âD€kYà¹'Z͉+?OœŸø|üÔøã{ÖOŽ];¹îÑÎÈÕ‘÷Gö7mþÐ<ÞÜÓø¦1]¾^úÿÿZ¾n± @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ø¯þ«ä6Ë resource-fork blkx Attributes 0x0050 CFName Protective Master Boot Record (MBR : 0) Data bWlzaAAAAAEAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAA AAgIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAIAAAAgEqo+1wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAACgAAABQAAAAsAAAAAAAAAAAAAAAAAAAABAAAA AAAAFnMAAAAAAAAAHv////8AAAAAAAAAAAAAAAEAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAA= ID -1 Name Protective Master Boot Record (MBR : 0) Attributes 0x0050 CFName GPT Header (Primary GPT Header : 1) Data bWlzaAAAAAEAAAAAAAAAAQAAAAAAAAABAAAAAAAAAAAA AAgIAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAIAAAAgh7OCeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAACgAAABQAAAAcAAAAAAAAAAAAAAAAAAAABAAAA AAAAFiYAAAAAAAAATf////8AAAAAAAAAAAAAAAEAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAA= ID 0 Name GPT Header (Primary GPT Header : 1) Attributes 0x0050 CFName GPT Partition Data (Primary GPT Table : 2) Data bWlzaAAAAAEAAAAAAAAAAgAAAAAAAAAgAAAAAAAAAAAA AAgIAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAIAAAAgHsBBTgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAACgAAABQAAAEEAAAAAAAAAAAAAAAAAAAAgAAAA AAAAAAAAAAAAAAAArv////8AAAAAAAAAAAAAACAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAA= ID 1 Name GPT Partition Data (Primary GPT Table : 2) Attributes 0x0050 CFName (Apple_Free : 3) Data bWlzaAAAAAEAAAAAAAAAIgAAAAAAAAAGAAAAAAAAAAAA AAgIAAAAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAIAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAACAAAAAgAAAEEAAAAAAAAAAAAAAAAAAAAGAAAA AAAAAK4AAAAAAAAAAP////8AAAAAAAAAAAAAAAYAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAA= ID 2 Name (Apple_Free : 3) Attributes 0x0050 CFName disk image (Apple_HFS : 4) Data bWlzaAAAAAEAAAAAAAAAKAAAAAAAAA/oAAAAAAAAAAAA AAgIAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAIAAAAgDvhBBgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAIgAAABQAAAEEAAAAAAAAAAAAAAAAAAAfaAAAA AAAARy0AAAAAAAAVkAAAAAAAAAAAAAAAAAAAB9oAAAAA AAAAJgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAgAAAAAAAAABnAAAAAAAAAAAAAAAAAAAAAAgAAABQAA ABMAAAAAAAAOcAAAAAAAAAFwAAAAAAAAFpEAAAAAAAAv IQAAAAIAAAATAAAAAAAAD+AAAAAAAAAABgAAAAAAAACu AAAAAAAAAACAAAAFAAAAEwAAAAAAAA/mAAAAAAAAAAEA AAAAAABFsgAAAAAAAACGAAAAAgAAABMAAAAAAAAP5wAA AAAAAAABAAAAAAAAAK4AAAAAAAAAAP////8AAAAAAAAA AAAAD+gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA= ID 3 Name disk image (Apple_HFS : 4) Attributes 0x0050 CFName (Apple_Free : 5) Data bWlzaAAAAAEAAAAAAAAQEAAAAAAAAAADAAAAAAAAAAAA AAgIAAAABQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAIAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAACAAAAAgAAABMAAAAAAAAAAAAAAAAAAAADAAAA AAAARkEAAAAAAAAAAP////8AAAAAAAAAAAAAAAMAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAA= ID 4 Name (Apple_Free : 5) Attributes 0x0050 CFName GPT Partition Data (Backup GPT Table : 6) Data bWlzaAAAAAEAAAAAAAAQEwAAAAAAAAAgAAAAAAAAAAAA AAgIAAAABgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAIAAAAgHsBBTgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAACgAAABQAAABMAAAAAAAAAAAAAAAAAAAAgAAAA AAAARlkAAAAAAAAArv////8AAAAAAAAAAAAAACAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAA= ID 5 Name GPT Partition Data (Backup GPT Table : 6) Attributes 0x0050 CFName GPT Header (Backup GPT Header : 7) Data bWlzaAAAAAEAAAAAAAAQMwAAAAAAAAABAAAAAAAAAAAA AAgIAAAABwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAIAAAAgvmnIQQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAACgAAABQAAABMAAAAAAAAAAAAAAAAAAAABAAAA AAAAANIAAAAAAAAATv////8AAAAAAAAAAAAAAAEAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAA= ID 6 Name GPT Header (Backup GPT Header : 7) plst Attributes 0x0050 Data AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAQAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAA ID 0 Name koly\½\½ H ç4relic-7.6.1/functest/packages/dummy.msi000066400000000000000000001000001455105530300200710ustar00rootroot00000000000000ÐÏࡱá>þÿ þÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýÿÿÿþÿÿÿþÿÿÿþÿÿÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿRoot Entryÿÿÿÿÿÿÿÿ„ ÀFÀš’áA§ÒsEòA¸A·G&A%Hÿÿÿÿÿÿÿÿ©SummaryInformation(ÿÿÿÿÿÿÿÿÿÿÿÿ @Hÿ?äCìAäE¬D1H ÿÿÿÿ(Ð@HÊA0C±;;B&F7BB4FhD&B ÿÿÿÿ@0@HÊA0C±??(E8B±A(HÿÿÿÿÿÿÿÿÿÿÿÿA@HÊAùEÎF¨AøE(?(E8B±A(HÿÿÿÿÿÿÿÿÿÿÿÿB*@HŒDðDrDhD7HÿÿÿÿÿÿÿÿÿÿÿÿC @H C5BæErE²D/HÿÿÿÿÿÿÿÿÿÿÿÿVl@H??wElDj;äE$Hÿÿÿÿ×ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ þÿÿÿ !"#$%&'þÿÿÿ)*+,-./0123456789:;<=>?þÿÿÿþÿÿÿþÿÿÿþÿÿÿþÿÿÿþÿÿÿþÿÿÿþÿÿÿþÿÿÿIþÿÿÿþÿÿÿþÿÿÿþÿÿÿþÿÿÿOPQRSTUþÿÿÿWXYZ[\]^_þÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿMSCF©,@{JÌ‹ EXE,ƒ½faCKíW}pTW?oI @‹áAR¨¼în6°¡‰$Ù °4›„¢;%owo6¼}oûÞ[šÍTÅj ÒiEÆ¡#ˆƒ`…R…~8S«8EûGFÍ8Çv¦t´~ô*Ö2Nã¹ç½ýJ¢Ø:ŽzßÞß½çãž{î¹÷Ýw¶ó3‡a”`œxìÒ·/û°V¬üa\œ;¶êE¡clUïbŠ)COrRŒÉš¦[b”‰FZM v…ŤgRyù¼ÇFw;@‡€˜çÈÚ½ «Åù‚`³mÞé:1çX%õ]¶ßW³Bþ©c€ëxŒ«ò_¾Í5TzÑn—³àc³fXäÀج¨ÅúoÄ$Wп²² é­´d± Ûï•9ëšgû=ÅÄ€d˜F ßZœº£XÙ-’ÁT=æˆ[•ÓôÚ¦º¹¿În·ÒRÀI›0˜s§èÿ¤ÚÛ•@jm•«n®i-´…·µ Œû¶×'¹¥zw½§‘sJAE¼ˆâêÏãzÑç Öê°e(ZÂäq\ï2l«ûÂðH‰½¤ê-}¡ ¶_EúÛ¦êQg~.lYìøK¨‡¥@³W:!áu?·`ómϾ#Î)r ®Ù ®‡7„ ¹8ÿAø±0Þ&¼BØ/p\MxašøÛàbáYâ†CˆÍ®—h’¥4“@O%Ö2h¥~7?ð^Ej§M-›À‘®f=\œZŽ8Ö V‚‡°‘°•0Dx?á.Bq (Ôˆ0CøE²&Á|x ñNG¼ þ‚X ·ïrA‚zÂûÄßwb?LœÏÆàÄaèC4!*”ìËÆÕn«éM()<%°2Û ¨²izì¦C‰²‘ñ@S§O«ìÓ4cº¡*QØ’Vâ­ž‹hÚbdÑt"!GU–çôd¿b*E¼VÓdɨšéU¬Ù†gIÙ΋ze#Á¬Íx±‡õBAvÌfEeýÌ0]›. èÚ ’H²5£8È̘¡¤Š…èwJQiDSåê™Ów“˜5Ó¤©Œ¡$†f%S²–É zÒš¥$ñ-%ª¨ŠU gL‹%%GIr–‰¯bñöHqUutsCØ ÊbÜsجë Å,ÝÈÊ‚ŠœÐtÓRbæÔBšÅ =fÆ^%Ʀ‰íÐ0#'·÷ÂódWtN °$ $ð.aE´)€›ç[^ò›«BÏ=þNéøcs/A‰(e³DJ±³p!'+8¸Ê^ô/÷]}|ß À/?ÀOñë€ßH÷„[=üüò×4Й¹Rw÷aeôœ\U;Zu¼üËÏzk'Ïœ_•»ñ¥7ô=zà'í¥÷ßøå_œ¼5zéäœÏÕ÷¼>þ³#O„ÏþÀ«mp͹zèG5ÍO]ž8xéåámWF7É>×ögÞú£ç§O«Ÿ8ñdäï‹¶Wµu¿=ù›wj¾½LózC;&onhN¬<õ*º_Q&8ß–*îQ¯kéNCNíеö‘£ÓÕ;dè›êÙŸ”E”¿aPJoèÇX”;?âå³¢èu{6¬ Æïm`ë}Ì¿®ÞÝÈÖùÜþø:?kl\¯÷6Ľž¸¿ÑËðR`Žït|:X!íhïͽ?ŸrÎP3Þû ’­Xœ3¥Ê™H.æ£ÄœD$íWžùµïƒõOX›ß;8’¨ý~úæÉcG›Ÿ¿ñüÁã{“ï~½²ý•å ¯¥"§ßÿÆœ5p½bÉž{÷+ÚrÑ}`ôÔo÷ctÑð2y7Ú¨üO\ëW(È˦¥zv瞟ÍÀ䲩 nšåCìÇ5æ~ÓJùÚxn’ÁHÜnÜGZDûÙÿÿo¿ÿÅòþÿà…ŸòùOh«‘+'³Ù0Üx€ ¸ÈÜ4 H x „˜ ÔäInstallation DatabaseInstall dummydummy InstallerOThis installer database contains the logic and data required to install dummy. Intel;1033'{71978EBD-79FE-450E-A2E6-1FC75128A33D}@1áA§Ò@1áA§ÒÈ,Windows Installer XML Toolset (3.10.3.3007) """)))***++++++111>>>>>>>>MMRRRRRRRR```aaabbbbbbnn  #%'#%'#%'%+-0361:< 03>@BEGNP'3PRUXZ\#%'#%'cegikmn€üÿÿüÿÿüÿÿ€€€€€üÿÿüÿÿ€€ €ÿÿÿÿÿÿÿÿÿ€ÿ€ÿ€ÿ€ÿ€ÿÿÿÿÿ€ÿÿÿÿÿ€ÿ€ÿ€ÿÿÿÿ1711>>++R€€€€€€€€€%%%%.<IVZ\%%inK! $&($&($&(5,/2489;=DJL?ACFHOQ_^TSWY[]$&($&(dfhjloqprstuvwxy ƒ„ƒèƒx…Ü…< È™rstz ƒ„ƒèƒ…rtuvy{| ƒèƒx…Ü…È™œ˜™}~€€ƒƒ‚„…†€€€†}€}‡€ˆ‰‚€rstuvxy{|Š‹ŒŽ ƒ„ƒèƒx…Ü… È™œ˜™¼‚@†‡¬p—Ô—rstzŠ ƒ„ƒèƒ…¼‚€€‘’”–—™‚“•‚˜š")*+1>MR`abn""")))***++++++111>>>>>>>>MMRRRRRRRR```aaabbbbbbnn€€€€€€€€ € €€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€ #%'#%'#%'+-03%61:<>@B EG03NPRPUXZ\3'#%'#%'cegikmn ­ ­‘‘ÿ• ÿÿH­ÿ•H­ÿ•H­ÿ•H­&H…ÿHH­Hÿ&­&@ŸÿŸ•…H…&­H­H­HÿH•H­ÿ•H­ÿ•¥@Ÿÿ HH­ä      !6$õ  B Ÿ o ; >  Ò 'èㆦ )9”.:> kQ !s9c%A?vJ 7 6 „f #7>0   &        &  &escription of columnAdminExecuteSequenceActionName of action to invoke, either in the engine or the handler DLL.ConditionOptional expression which skips the action if evaluates to expFalse.If the expression syntax is invalid, the engine will terminate, returning iesBadActionData.SequenceNumber that determines the sort order in which the actions are to be executed. Leave blank to suppress action.AdminUISequenceAdvtExecuteSequenceComponentPrimary key used to identify a particular component record.ComponentIdGuidA string GUID unique to this component, version, and language.Directory_DirectoryRequired key of a Directory table record. This is actually a property name whose value contains the actual path, set either by the AppSearch action or with the default setting obtained from the Directory table.AttributesRemote execution option, one of irsEnumA conditional statement that will disable this component if the specified condition evaluates to the 'True' state. If a component is disabled, it will not be installed, regardless of the 'Action' state associated with the component.KeyPathFile;Registry;ODBCDataSourceEither the primary key into the File table, Registry table, or ODBCDataSource table. This extract path is stored when the component is installed, and is used to detect the presence of the component and to return the path to it.Unique identifier for directory entry, primary key. If a property by this name is defined, it contains the full path to the directory.Directory_ParentReference to the entry in this table specifying the default parent directory. A record parented to itself or with a Null parent represents a root of the install tree.DefaultDirThe default sub-path under parent's path.FeaturePrimary key used to identify a particular feature record.Feature_ParentOptional key of a parent record in the same table. If the parent is not selected, then the record will not be installed. Null indicates a root item.TitleShort text identifying a visible feature item.Longer descriptive text describing a visible feature item.DisplayNumeric sk. This will be used to prompt the user when this disk needs to be inserted.CabinetIf some or all of the files stored on the media are compressed in a cabinet, the name of that cabinet.VolumeLabelThe label attributed to the volume.SourcePropertyThe property defining the location of the cabinet file.Name of property, uppercase if settable by launcher or loader.String value for property. Never null or empty.CostInitializeFileCostCostFinalizeInstallValidateInstallInitializeInstallAdminPackageInstallFilesInstallFinalizeExecuteActionPublishFeaturesPublishProductMainExecutable{8EDA30B0-14E7-40DA-98F4-A8D2CD9328D4}INSTALLDIREXEProgramFilesFolderdummyTARGETDIRPFilesSourceDirCompletecb0qscio.dll|ClassLibrary1.dll1.0.0.00ValidateProductIDProcessComponentsUnpublishFeaturesRemoveFilesRegisterUserRegisterProduct#product.cabManufacturerProductCode{01D48E5F-8885-47C2-8D89-793CFC9DE165}ProductLanguage1033ProductNameProductVersion1.0.0UpgradeCode{A9C9A3FB-AF3C-4A5C-9818-BC9BE1EDCD51}TableColumnValue_ValidationNPropertyId_SummaryInformationDescriptionSetCategoryKeyColumnMaxValueNullableKeyTableMinValueIdentifierName of tableName of columnY;NWhether the column is nullableYMinimum value allowedMaximum value allowedFor foreign key, Name of table to which data must linkColumn to which foreign key connectsText;Formatted;Template;Condition;Guid;Path;Version;Language;Identifier;Binary;UpperCase;LowerCase;Filename;Paths;AnyPath;WildCardFilename;RegPath;CustomSource;Property;Cabinet;Shortcut;FormattedSDDLText;Integer;DoubleInteger;TimeDate;DefaultDirString categoryTextSet of values that are permittedDescription of columnAdminExecuteSequenceActionName of action to invoke, either in the engine or the handler DLL.ConditionOptional expression which skips the action if evaluates to expFalse.If the expression syntax is invalid, the engine will terminate, returning iesBadActionData.SequenceNumber that determines the sort order in which the actions are to be executed. Leave blank to suppress action.AdminUISequenceAdvtExecuteSequenceComponentPrimary key used to identify a particular component record.ComponentIdGuidA string GUID unique to this component, version, and language.Directory_DirectoryRequired key of a Directory table record. This is actually a property name whose value contains the actual path, set either by the AppSearch action or with the default setting obtained from the Directory table.AttributesRemote execution option, one of irsEnumA conditional statement that will disable this component if the specified condition evaluates to the 'True' state. If a component is disabled, it will not be installed, regardless of the 'Action' state associated with the component.KeyPathFile;Registry;ODBCDataSourceEither the primary key into the File table, Registry table, or ODBCDataSource table. This extract path is stored when the component is installed, and is used to detect the presence of the component and to return the path to it.Unique identifier for directory entry, primary key. If a property by this name is defined, it contains the full path to the directory.Directory_ParentReference to the entry in this table specifying the default parent directory. A record parented to itself or with a Null parent represents a root of the install tree.DefaultDirThe default sub-path under parent's path.FeaturePrimary key used to identify a particular feature record.Feature_ParentOptional key of a parent record in the same table. If the parent is not selected, then the record will not be installed. Null indicates a root item.TitleShort text identifying a visible feature item.Longer descriptive text describing a visible feature item.DisplayNumeric sort order, used to force a specific display ordering.LevelThe install level at which record will be initially selected. An install level of 0 will disable an item and prevent its display.UpperCaseThe name of the Directory that can be configured by the UI. A non-null value will enable the browse button.0;1;2;4;5;6;8;9;10;16;17;18;20;21;22;24;25;26;32;33;34;36;37;38;48;49;50;52;53;54Feature attributesFeatureComponentsFeature_Foreign key into Feature table.Component_Foreign key into Component table.FilePrimary key, non-localized token, must match identifier in cabinet. For uncompressed files, this field is ignored.Foreign key referencing Component that controls the file.FileNameFilenameFile name used for installation, may be localized. This may contain a "short name|long name" pair.FileSizeSize of file in bytes (long integer).VersionVersion string for versioned files; Blank for unversioned files.LanguageList of decimal language Ids, comma-separated if more than one.Integer containing bit flags representing file attributes (with the decimal value of each bit position in parentheses)Sequence with respect to the media images; order must track cabinet order.InstallExecuteSequenceInstallUISequenceMediaDiskIdPrimary key, integer to determine sort order for table.LastSequenceFile sequence number for the last file for this media.DiskPromptDisk name: the visible text actually printed on the disrelic-7.6.1/functest/packages/dummy.pkg000066400000000000000000000324051455105530300200770ustar00rootroot00000000000000xar!Š ñxÚì–M£F†ïû+ïúûcİR­”[“KnMwµÆ€xv¼¿>M6Þñxf³Šr‰„DѼtAÕût“}~©vÉ3´]ÙÔ+|‡V Ô¶qe½yXýùøe­VŸóOÙ‹ióOIÖ76œ’̶`úðĺ/+È "xä‰G,îQ8X–^JâC[°OÝ¡Jºþ¸ƒ‡U·5x5ÜI²ÆûúeéÅÑ®ü6Lž¥1¦Hç9â•/w”îaŧiœéMŒ’lõ¦ß挆ǧx¼1%À„ }‘nʇ‘çŒat.ÇüÚf¿ß•6~[ú²Þ|+÷«t–¾ô­±=¸õõo%‚PùGaÐ࡞êyÁ©/há©aÀ ¼ò뙦¦µÛòùÍ †S«¼Ãà‘’H T e‰¦ ³@KæDÈðj¢±„驆YÜC>Ô8Kckºù[ÙõmY†diŠÝÄ—™l£ °–h‚Wð#B÷ñø+ôtvH’U7dÕYfnÈÌY¶i›Ã>ÿºØeéx1Ž—n°ÚpŠ×‡Ú¼mš>Kc8Ž¢Ã,ª9’(ŒÅp4<—êfPžâx§¬£>KÇà\PW¶`û¦=¾ªªmª»ÎtwîPUÇ»ýÓæ\ÚEEÉjrÂÙì'·c„É¥ÝO~'—lÍn§‚K¶´ûøý}Ãs*]aÁZ¥™ALªTaãF)¡ E’)iˆzÛðï:ž™x’Pa5óR ϱ°ÕŽ;,$—š0ÎÄ[Ž_Z>ɾ”µƒö×aí‚ÇÙH±AuÓmj× M]^NŠÉ’ÁX E—˜Fkâ{JïyèËɘ!åõ<&WÖÓ LfN®ëª…ÎÜЙ…n„£ë÷KRFTÈ‚• g¾ÖExù'hÌŒÐp„ÏØÌÜÆÜ,À  ëÅßñ3„ƒ?ÂR½ÄèúÂ41ôKS-©9­H €èM€Vßt ˆ&X¶_‡ÕLuÆbÞc%W©›Ò\P÷IÚHƒ)f qž*‰ˆuJ‹°eHE´6Œ(bíOôñÿ I£w ûŸ¤Ný0I˜ã®1î=šØ š(×oìF+~}CRý{Û‘f\…-Ü„?-B ·\#E@…Ý(üz l¼âNR)"¯ÁÂ{‹¶^I'± œYÉ fsX’– ¢›ý±Of¿×¾¹Ú£SþÚ³4þÃÿ ÿÿ©=}Ly÷ú5dùþ¢ëZàqTegõlîxÚíÜßoSeðç´ë~t¿\) cc³ÅF–`{ºU@ö#šMT@¹˜,[Ön]g„İ…Eo½ ÆBÌ¢3Áÿ/Œ‰7¯Œ1‹rg‰Æx!<ïyžŽw²È’õ‚,ßOòä=ïóž÷é9mw–¼9§]ý}©|6—&"‡c€(y”èÄ'DÑ÷lvæs/ó¶2£Yí› |.]ø^ˆžY™P3Ź U¡š£Šd—ZŽ-!Žm[9vp<ËÑı‹Ã|³žãxžc7GÇN޽{ð<¬•¿® íd9ÇÌÊÞ­…U†Ê‘ô²´›™µJ´o*pF¦ÇÇ/¶ML˜ž£†£ò©X0˜ã+ëÊŽ÷ʾÀÀPþÜo•”õ¼Ù›1ËD¥¥' ›¥©±KéD‹›ŸÏ¡ÚÎ{…Þ™»¹Ó¦qþwxÙ^3ðug3ùt&oÖÌ­TçðœÙÏVÏ)ùÓ]ú&gÍó›•ŠÖ‰ cSyÓ-á?á`̬¾¡áþ”Ù2+Û§¹vvõßb‡3ó æ˜#Gï=7â''ÒQ¾§iÞ‘§@ÈôoÎÝŸ³*¼Õ³UÊQ¿ÖIP¹óbÏñïìÃ8VW\¼j|ì NqÓ±ê Ž§§²Ó¹á´÷†™ÛPš×qÚïvgGÒ©±³™¡ü´÷(UpD×:ÊòëNx~_Èš]a&¯zYóhLËF—~¬u²ÌYòûø|Ö(í¬±µÑ×mµ_w ¿d®ã8PÄâ »x˜Ã-bñ—íâÛ9’E,þŠ]¼žãp‹wÙÅ9:‹Xü5»x3Ç«E,þº]<ÊÑ[ÄâGíâ-}:P©\Uû£þm•®pÚ¯²ûü·ÿÅ£9Is™8D2_É­|ÁLÈ‚·¯Aú¡I훫¹ÿ†ä#‹äÝ>™”~tPös¿"ïºÐ¹Ž†Få”|ä#òVÍ#W5ÿ¡äc R?¶¤ù;òFÆ~"ïy½ØïšÿCöå"K>nŽ·‡ÛÍg4ÿ¾Ô‰ß ï}ˆ®ùÛZç{9žøÏšÿMò®y·p«õÝ©ã!ï¹A÷˜æoÈù»ïȇYeî—Ò&êtü÷Ð|‹¶ïÉxB/ ‰Ëš¿¢í·:þ±Žÿ¢ùeiÛvëø]oKjþ°¶³2ÞÖ©ã×4?¯í¯:~KÇÿÒü?Ò¶Ôñe¼}@óoi{]ÆÛßÖñÛš¿£íß:þµŒ'ƒ’OÖè¿|€õù\ò~n5ò)y?˜2¿AêçÍjmͤVýwâC@/Ç4‹í tU¶°Ow'$`Ô(Q‚/šˆ#(ô#o5@:@g¤I‡±Sé®$-Ý]MW5IŽ=ŠŽùo¼>ÆûÃÅuÄ‘årf—|!ʌ㠿º¸Qqn`|0* Ðÿ>õHª«Óô_þwZ}ήÚ{Ÿ}uªNUe©²ÐÿÔ”[ª**,ZÊ¤Ä G­ð¿µT2*U©˜ˆEëÍš¥·²RYTy3{#@ÔÄ„BÉ~KSø­Õo©ZQå×lç‚øäÊN)ðò`vÛ9/ëòu!f“Ë+·Z,• nä‚F-¯RiöòÒ²lÊ3S©…å¹HØÃò¤fv_Ào\Á†yœYb5YJŒlÐÃy}Áî™%‹Zç̨.™=k\Íäúf{ëµÎcÈïã£sQ]S£ÝX2Ãl¶…B~Öl®o­7:›]­Fða67,(1–ôBèj³¹··—FägM.@y³3̅ذmg3ÀÀä¼%PŒä=!Øëõy„YãÆÖ,c£³º|~–¯1Ó,ì¡GÌ ‡JG?ŽhÍ •]?ÜfEalްæÑTLW\gò‡ÂÜ *eŤ̅žñWy“4zY_w :fÿ,Ú¿5f1KšS9ŸóÈMenJÊ>á”ñfNó™š$U…­iJHU3%øaý‘þ-½ƒiƒy]×Ο:eµyÚ)†hMàTÓæi³¯3Õ»Ü. ³§µõKÓ†©sÂL€íåÂËøÕ®&ÌzU;œþHwcPJgø Óî´»Øð ôÖjë‡_=Ÿñ4»V7ù:ÃL8jžj‹\€Z¬v…8ÁO#ZÝÄuû‚à§MKÙ³A–Xï©õÔu3]‘ò,l vq×} C½4M8ÎeÝ´Øï¬¼Ñ&£lFjv>qöúŽf¯]o^zeÊþ³† t²^/ë…Ó#Ì­ðÑV… ½"¦ âÔÇ‘Ü]™NÁSp¬dFRÑõ¬qIwE§tVV–é.Lœ“o¾*aÜŒVÌè7_åÒîŠR:ÜR# ¯ž|òBˆ^GH¤Bj Ñ•À9 öŠ.6¼ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ rÚyýøÇ'óÑ ¯‡írØî?‹[‰Q<>6ln·Ó6·aICKs²]åP?í9ÔOkC{ë(öµYΕ·3åÝn·Àö #jZ[«$WäŒÈ9j¿9$¦Ýn^ˆtò)ý½}äoFVÈ—ýIðçî_i3Š¿©5’ý¿«d}úøz}]B…› áh²¿§d9*9ÁŸ·ÛÃyA%'úÛ.ûsêFäôþäø„(T˜íÒú+œ%ùã4rF]>Öï xµþb²½Q%gÄæç…°ÖßFÙß8•œŽa Ïs!ªõ·EögQÉYÅ s—ÜïÊþòUrÖýÁ'û; ñw £¿H°×ôº}Á.Ž$ûûHö·^%§÷Çö¸»è‹¡9Ñßí³%!•lHãÎ+Õ:ê§ÞÖjsÛ›¸$oµšyE“T[¡G7§šVRúÛFFü¨ã¢çÿ™ öÚó,Ñß6¿mµéëévs7xܾÓÍÒ®Ðú Ùû•Êéú!±ÝTbšv‹Ô—¢n7).?ãæ£NÎï á”þzT²ºžô¥3SêéeF-'úë‘ýU«äôíÖÉó r¢¿²?õ¤¤¾^h窑ëaSã‚y õÊØÝ.ëÆFdJm爵¯дÈztüBzû˜‘ã÷ï€kTlû+Ò\Bû²¶üW ©¢~_ƒktöwªbU6ª_‘ÜÃôý™ë(ûé ~ŒDzñ9‡Í~_§Ùõ{åãÊqL^æ/_8ayeýŽu÷¨½Cm?F—É^5NÛ!Áø›ÖÏÜ0êñyø õÉO#Ë'Éqäf²oâ<ÚÚŒIç§ž ÃÕ=͸ÐysÒÚûø#xzú%¡ü®‰éì“úäÛ±±9qX'ÇoHk?Ÿ:û ã²¹óÖ#øV°öoÿÂVªÎjïTÛ]­Oc¿(èëâÂV¸ÙkôÂÁ·Ía>Ýy®KO»Óž8ïQûÄyo(Ý<Á)e_$_ßÄy‚íc=éô³n-=f“I5ßIåü€HׇïHeM“ez½£\¬ß×»yAAAAAAAAAAä{Ã"Gÿ‡C 9„,}áìIõ²|F¶‚{_·ô…®*ÛÛÚl‹l­.§£ÿdzÖïç¦{Ïpa¿w2!½k äìß¿âèÙÑ¿¯ÿÕ¦þçlâƒ)¶?ÒßúoXoÿz4·¼hlìßÑØÿ¦­ÿOóû_Ú;›úËšúWåÛbtõ·{ ›ú—ßn7:^¶O¥. µÈiµœÖÒt‡Ý¡wù?²]g[j»Þ憘MgOZ#ÖõìIµb}l‹œCôǺ™æCB‹>1d‚B­Û©±m)­¼dãt¬Í½.(ݺ]Ð_û˜<ŸC-+x°²Ðèûs{AáZ©½ërmpĺ÷…®áömÚ‚OÒ)»ôà!=!#1ÑãKÒ5$}Ç-C‘3ä"s!žbZêØ14›½öLú‹£¿iJ>äŸ#å kgn§ùu­°¿Çнz1´sÄö-†èb´<—cm8îÓÿèGÿWŽþçÏ7:¾|Ûá9ìÐ|6*ÖW¬Û‡šE{Ã^HêÍK®ßdžáúK®ß ‰òðøÊNuô7l…ú5AœCŸŠõsmìÑÑ?›c­­¶±ÿ¿·l?ÛqËàáÆi/7õíèÏ1í98¼ÛÑÿ'GÿŽiP‘wýKw6öØ8í¥¦þ†Þ¤~úŸ·7ÆãqǺó~+¶Ò[Ö/†*!Ȧµ‘mk‚“íMýqGÿþ¦þšúßïú³hù¼cmÃn8ÒÔÿ·¡3À`þÚ®|IÑ£©ÿSÇÚ”þ¶-Þft¬½qkSÿ' Åma爒˜q¬cßR Æm{hûc‡ã¹“Ðûž£ÝY÷Z_z–È=AÇ/SÔãñ68wåBnUsó†Ç#Õ' úïÃÞ¡ÏO@»¬Ì‡ÿPd÷f=µSŸ0òøü=4cü2#Ž_V"þN þN¯§‹¿&ñ×"þ–Š¿åâo¥ø[ ¿Më.¾ ›+~Y‚Ÿç:ø7(¦§’æÞ¥9úÐÞÁ½4÷Íí¤¹½4÷ Í¥¹'h®šààfšÛEsi®†áÁûhî&𻓿¢¹›in7ÍõÑÜ8¹ƒ#äÀWñ¸ôóÃùCçm>¶—,„}`ßG°}ÈaÑË~#¸=Q â3|ý°>2ð½”2 ßy<§é=rºR¤§¶’Ãv;!/‚W£öQÿ1yÛÛ9 ÿ8¤oé‰%n1µ…ÄÇi¬Õr¬ˆõ‰õ}b¬ô© :eÜ éL9]©ìhê81[Û»'“c«ƒ6s ¤~Øt¯ƒ‰ç릟‡¢úyð«?sɾJ~©œ¶¯“>{Hj‰ÙqÑvýW¢íÓ:jû˜Žš©ìÊ ¶N°i#®“ô©ù§4rçJ?ÐVp,&¶¯ôœ#í×ëa»¶ªý{ OGª²¶¯¡¤OJÇ/"Òó}Óå¶N†Ÿ“ÑíiϽžäèõô¹qïâöÎ5’B½Üwäl⟄«šî0!yFhV§ŽŒé0ÆrëˆÁžjÁyHOt!0Œˆa«è9â3{ð›£/†ÑéŠäxt+[ˆ®¯PWX—OÇêErÌíÇâ"ÊsÊ ™ìÈñÑíR1X“Ýóüˆy%µŸ¼ÿΙ#ûkórr¿ÃÈAAAAAAAAAAAAAAAïÕòóêurÚ$§mrÚ!§7È© §?–ÓÛ•ïÙËéz9}XN·fxŸÀ†§ãñOãñø^H ¿ûAAAAAAAAAAAAAAAAþgqNIah›>T¸ðR²”Êãk½Q¿×Í ‘Nw§/èeÃdax«n ¨Ö=…/¯ã¼Ñ*W¯¯KXÔXf …œ­~Ûy2.¡HR*oó±½TK<¤ìµZƒk®wn˜‹„ÚæI,?Íñhß\›­Âåaƒ¬Í6ߣqUîv2^¯/ØÝÄD¹ˆÐf³YKi¡ó9¯¯Ëdžm¶ÅÎ &@¢B…lìµsA f£í[î†0ÅúÚJ™†–•6›¥¡Z‰¢ÅökC§ 8?”¼Ï)¶ ÍîVjÝM©”DÍ…+Wt·.E¥/êé²k”·²}´•¤H“G:¬­¯¨¬Uê[K…;À,cí–ò Vú‚¡ˆÀCÝ­n{UsD í«{n˜ õ´1þK{Ô-©T6Šúmœ%I&¸/oòñBRž†Ìex2#‚z¦EعHP‹pù\Ý’ vŽâÃÒÉp¯1uÚ“Òö˜Ôմϳ¹Ý<ÝéžÓÜbop75ÛêÝS¤]Эó|‰µœŸFËîç"^Q¯x`-$*,n#±ÂÇŠÓYqa¶žÛ>£'f0˜íåe'ñγ 2˜‰åóðdà š¦›…Ø.Ê ØÄyä8Š.N£[Ï„{} uûH´—¤S÷ñ!FðôÐ9jR=uC Ó¨66‹}Zç¤ÑšÏ Œ_Ô:7Vsç ¬Gð­`í$vÿHŒãÓØ,Œ0aa%m3ë0¥Q]ôuqá@k4Ä6zaž'3žÄ6ªŠJ7^Û× J9ݰåxÚÈÒu‰'E©¯UiÖµÙá,ï‹¶7¸˜éÈáó­JÅ“ª.âóÃ…¬Íj餹:?çYíëƒSÛniY音„Z&ZVÓáä÷­d½.! —Šyl´ÍZÆ‹ù&ŸÀ†¿Íîr \\”4ýÚl åÆŒF¥Œ *å Þn¶¹ÌÅ meŒßokXÑ-jM-JžŽÝW L§Ÿ]ÀØÊNf~Z  -౪Ûåru»¸ê®:Qßîêæ-•.¨§ÑÒæêVâ¶%OTøi›ÕiÕéË8MS;-©vô’Nq~§ž0oJã´›šCÌòKO¸ÜÓó zXªåTkÑãuÑùL°ÛÏzi_5Å»ƒ>ª¶g§Ú¤ç=L$ í,±µåbŸdy¾•ŽBÏÛèq³}¬'"°î–¡·lô©î÷¶r………ÛuK¡±°£~búB‹¥–lÈ'¤Ž×50¹T/¶³u…¢ }S±”æ(~,¹Q#G5ò½ùü™F¾T—(×iäNüFÞ¯‘käñúD¹\#/ÔÈ>ܯ‘ÖÈOjäK ‰r¥F¾[#¡‘¯ÊI”Û5òJü–F®ÉM”cùIÎSäõ¢\›«ÈOKòE”ä|E6Œ届<]’Ç)²K’ÏPä^I.Pä ’|¦"?+Ég)òû’|¶"çå‰r¡"_.Éç(ò\I>W‘ý’<^‘ï’ä"E~^’Ï®Ÿ$Ÿ¯È'òÛçâüD¹N#¯ÐÈë$Ù È´áéAÚ´4PZøEÔ7l—ÀV [lå°UÀFOjÙí>B;ƒvmtÚдqiÒF£ EƒV˜V’Vd‘Î㉰]Û…°M‚­òÆ£0Ú ”jµ˜rxªûtËɬ—’ßä^ᔟ–ž™ž§aÙùol’W«Y¬U3®TÓÞ(¥ZÄfºuJ»ÄmûÝe²ŽþVWÑßÁú4¬ 3¬Ÿ3¯ž¿ÝÛÖïâ†õtܪŽöeÁŸñÏYü!‹?dý‚,ÿ$Íß²üS@Æ¿d±îÏjÉŸi±Ÿi™ŸÕ?«ý)¯åÓ/ÞÓ®Ö3­²Z}“UQŠõPÒŸ®ÃŒ7|µÙ\Qi-¯(§·/äøþ‚íp·C—ʽm¹tke¸ZÚg˜#Ý å¬‘^\›slôâ®^¼]¦wöT§]¾e‚Û­}‡zR@²¢VÉèàêbâÞäQrÛþ¾c»Vº_ý랎öÈîo(øàÑIšjLøÙMÏ-Yö— ™üN¾_XõÉ}k¢c«ŸðS®Þè¾æ5WîùêéCW}>ûØÜÁ·2Ùwݶ¿ð—w˜ß»°r×¶À®-;/ýÅCç,›ÿÈ?Ö\¿ä“Î ?¿?jXð{V¤ïÎÜnÜÚ²à†Ø³/_ð«G÷V=<Á~ì¶? F.¾qÝ;5}·mûSÍá}o|=8þ· öž\öZ‘›/(~ðÇ¿+7'—NÑÕ^[zßö[.zùøãWŸÜõÆçËžüû ?¸ñÑ›¶<ÞÎí8ص=çÍ={–é›Ûs㫟X½òÖ·Ÿ^ëèŸþëLÇïíså›^óÈs÷ýæ)ÃîݹÓþcá¦UUßjüÙוÿ±¥pëÎ ¿|tÍgæýAÝ¥•¯ZßxüíÇ^=Ó³ø[Þýñrþ_-?ÓñßþȽrãùºð×üj†óñ»¶Sùðîì>–wý‘ ÖÏ[4ëÛ.ÿ×qeÓ'ïÿúà79þqëÚšéc¼/mxûS¸n÷ä¿î0Í|fÕÁwо|£¹²çºY\¸éäW+ÞyêóöâÕ/{àêÝ7>g= ¤³hùrÀ³jf÷üÆ0À$8³Äj²”Ù ‡£w¸3KµÎ™Q]2{Ö¸šÉõÍöÖk Æ.lF確¦F»±d†Ù W"?k6×·ÖM®V#ø0›”Kz!'ooo¯‰¡Z&8_¨"ov†¹¢ô*9 L^Á[ÅHÞ½^ŸG˜5nlÍ26:‹žr’3–+aŸ¥â ž z;¹¾3ÕU!aÍilº|~–7Ex6<ƒgý0?³^S>3¸ ?ªõRc–"¨1‹ñÍ­¦'œõ:Ë¥—¤I§ÓY* ,§˜ßí¯ü´'=‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ÿ¯ÿødô‰Wñû’—ÃvÿY„ÜJŒâqú­H¡ßxtÚæ6,ihiNö¡Ë¢ê‡~³ÔínmhožVc ˹ò¦|Ôí?5ŒÖß•’¿é9#rÂÇpsHL-ºÅïEñ)ý ^-ù[¯’ÕþòeÃöâ÷§zXˆ âoõ5’¿'Tr†øè§®*ÜlPG“ýYj$9*9åÇE.È *9Ñ_¹ìÏ©‘Óû“ã¢Pa¶KëoÍLɧ‘3úëò±~oÀ«õwD¶7ªä¬âƒØü¼Öú+š%ù§’Ó1ìáyÎ#DµþŒ²?‹JÎ*¾P˜¸äþpÊþòUrÖýÁ'ûk×økÏè/ìõ½n_°‹#Éþ:d¿QÉéý±=î®0`9Ñ_þlÉßQ•lÐ:QAç•jõSokµ¹íÍ \’·Zͼ¢I ª­PŒ£›SM+)ý9Ɉu\ô[¼g&ØkϳDNÙŸQ>œµéëévs7xÜ>úõCÚZ![b¿R9]?$¶›ê@LÓn±‘úRÔí&ÅågÜ|4ÐÉùÝ!!œÒ_JVד~@oBB=éWþÔr¢¿Ù_µJNßn<Ÿ 'úÛ(ûSOJ9*}í\5r=lj\0¯¡¾Q»ÛeÝØˆL©íñ£öUšYŽ¿BH·9¾{!S¡bG_‘æÚ—ôÛ͵¯REÓ×Ùš/}óY“:ÕFõÕßhÖ²õÏ„Le?ýv´‘H×3s„›ý¾N3ý®¢|üB9Ž’7þ~¿ïŸ•<3ý‹?.ÞZ*ÛIu„¨ gˆy±oÆÞ¡¿‚HõT˜ õ§~î¬J,Ÿ–Û!—AëÀ®øZeºÎìŠò07ù:ÃL8jžC§^.¼Œ7|†ÓÔ¥ì5·IãÍvÕqÙµÊ?!Ĩ‡ë =ÓL6ÔÚשŽKm—ë-ÈÕÚI!šêÔ–Ô®=¡<©‡ÒÔGþœëh•±)©¿Xm‚ßžÄzˆ—šS}ðUX²ý>C{å{°Ãh½ÔöÅäw9éì¹0›Ð ÉåÒů|lvصw¨íÇè2Ù«ÆIb;$øÓúQ>T›¡>ã¿d«8IŽ#7“½ò•Û qt~¤Ïà¦:oNZ{ù+¹ ý’P~×ÄtöI}rŠí(~]WÝIñÒÚ‹_ßMgŸa\Ž|÷›Å?òá^•½Sm?vµ>ýèßôMwžëÒÆÓî´'Î{Ô>qÞJ7OpJÙÉ×7qž`ûXODü¸ºFKÙdRÍwR9? Òõaê«RYÓd™^ï(Ëòá}p½›GAAAAAAAAAùžq±ŽM‚íĪg70» ÏdBÎ=±ê¹ßl7¸÷Åíº=GݺM_,Öo:ÒlØtbUèéîú§!3ÙtçÞØºG?\÷ÒgäÀ'Ÿ½7ÿíýɺ-ŸMÎÙþ™nã‰ÏŽrO¾1Ö24žDãr¬üæØÞ\ØÆÀ–Õ9b ›?7è6aÐoþÒ`ØüìÚȦzrnO¹ª÷,r¬ãfr9@¸Ž›o®é¸ù–š©$ÖÒS¢»êĪzˆËñÕm:ê¶m‚xï¢qi~bÕm:a'wïA±&R8Éhø+-oì#àãÄ*Ø?/Ö•¶=^Û oï ä ¥ ŒWHv?ù¡”¾¶SëõbžœøÏŠ™+×A½¨ü“Ë$¹Êù!E:Øâ·=4.±œ:}»QO~©¨wŽ'Í÷Ì&í46‹´]NDýä߇>zôzjÕåC½o! õþ'ø!¤öXŒýƒºŽïßÛÅ©—ûãKèeLn´jÆd¡ÔL™”ΛÛ-ÉcR±4'Úš$]!ÿ™2æbWö+©S²ŸO ºïðT};ÑOÚGó»Uù­ªü€*R媼E•/Tå_®ò¯ÊoUåTùœ_~¹óá¹R}_FÄýNBŠAìt®£y:Æ+åü?!’ó‡!ÿ˜œÿò{åü!È•óÿ ùn”?ù]rþ=ÈWè¥ü~Èß$çßüCrþ-Èï–ó…ü8!×}{#@T5Cq;Ø Ðæc{ÉBØeÅ?‚m#èÈ>Ñ?þ“Ba¿´Ç >7}ôŸé«Ó‹ŽÇã4-–Ó"9rœÚJØ y¼ö?ì£þcò¶¶ó@ÿqHß Ò¿âWâS[ˆ6£kµë6ˆõ‰õ2M¬ôIˆ‰ðs·&}ìhºóë‘Øvʱ½{296˜cÈ? 8¯‰6Ýë Dâykĺéïݨþ×ð«¿~æ’)|•üØO9m_èŽ>Û%µÄì¸h[ð•h{•h{¥h¦²+ƒÚ:Áæ1Âá:IO-?¥‘ë8oTú¶‚c1±}¥g i¿^Ûí´oUû÷@þfØŒP•°}}"ð¤tü"’øÌ®‘ ?›¢§ÏgÓç èöÞÕÒÎj¹¿ ÈÙÄ!?™ eŒ»±oA¶@GòšÀaHGÆìÖ‘ÜBpV®'9`ä5ÃFшn*ô è¶Á6Û$BrZ!-‡­Hj9yÒã~9ʳiº•-D×w‰®°`’7}aýѸH­2àëÏ*Ü û…þ"9ÖAùx‡rÜq–‘§Ï×Ñg¿ŒÇ4öòñ» ÷ˆŽVö¹ì¾ã’mM [õ{çñÔ1Ò¸¶ÈÇšã­5Ù=¿ÿ½Eh§­žÊCUžcOGm^NîéAAAAAAAAAAAAAAAùÿ‡{äïÑo”Ó-rºMN_”ÓÝrú®œÉé9%òóõrZ,§S2^H*ÄÓ¹OCb1£Äz¦EعHP‹pù\Ý’ vŽâÃÒÉp?2uÐ…±·F?1dݤî4&hI#¶zíÀy6·›§»Ýsš[ì î¦f[½{Š´ z}žO ±­#'×ùiÔí~.â Š&Ðò¢Ââ6h÷?§³âÂl=#00|Gê41ƒÁhG/#ø8hŠ#fd0;ÐçáÉÀ…4L7KÇÌE›8GñÀÅitë™p¯/˜PÉKÒ©ûø#xzè™8)žº! Œ…iT›Å¾­sÒhÍgÆ/j›F«¹óÖ#øV°v»¤FÆñilF˜°°’¶‰õ ‹Ò¨. úº¸p 5b½0)Š3ObUE¥¸íNˆkP¥œnØrª¶g§Ú¤ç=L$ í,±µåbŸdy¾•ŽBÏÛèq³}¬'"°î–¡·zô©îŸääoӕ׿tП-úœšZÙ/¾ú‰Ô50¹T/¶³u…¢ø()ÍQüX4r£FŽjä{5ò3ù3|©.Q®ÓÈù!¼_#ÖÈãõ‰r¹F^¨‘}¹_#?¬‘ŸÔÈ—åJ|·FþB#_•“(·kä•ù-\“›(Ç4ò=’œ§ÈëE¹6W‘Ÿ–ä1Š<(ÉùŠl#Êcyº$Sd—$Ÿ¡È½’\ È$ùLE~V’ÏRä÷%ùlEÎËåBE¾\’ÏQä¹’|®"û%y¼"ß%ÉEŠü¼$Ÿ7\?I>_‘Oä%¶ÏÅù‰rF^¡‘×I²A‘iÃÓƒ´h%h ´ð‹¨oØ.­¶2ØÊa«Ìh·Ú ´áicÓ¦J+F+SùÍ}“Ñf’TËÅ”+ÅS½h§[Lf½~ü&×üS^qfXofZmž†µæ7¾AI^¢f±:͸6M{ÓjÑšé(í’v´Uìwv·4Êbù[]'käÓ°Bΰ>μ$þvo?¿‹ÏÓqË9Ú”…{Æ¿ dñ—€,–ýY/ô³\Úg³®ÏrIŸq5ŸÅú=«¥{¦E{¦åzV õ¬Væ§¼&O¿O»êδÒÉjóMV7)Ö5Iº3ÞðÕfsE¥µ¼¢œÞºãû ¶Ã]6(÷¨ôVîº WKû s¤Û œ5Ò‹ks^¼SÕ‹·½ôê´Ë·Kp¯¹ï°QO†ïIÓS«dtpu1ñ oòF(¹mß±]+ݯþuOG{d÷7|ðè¤M5&üì¦ç–,ûˆL~'ß/¬úä¾5ѱÕOø)Wï?t_óš+÷|õô¡«>Ÿ}lîà[™ì»nÛ_øË;Ìï]X¹k[`×–—¿þâ¡s–Íäk®_òI熟ß5,x„=+Òwgî7nmYpCìÙ—/øÕŒ£{«ž`?vÛ#߸۶ý©æð¾7¾ÿÛ{O.{­ÈÍ?øãþ¸ºû¿/¬º¥dmÏŠ/(Ÿ|®ÓÀx¦ÌØuù×tOûÕÒGoÚòx;·ã`×öœ7÷ìY¦onϯ~bõÊ[ß~z­£ú¯3_¸ç·_õ¼ûË/,Á`qëŽÿý‘õ÷[¶˜¶š^[¿á¼×Vš’{ô÷;Ÿü·#½åßž˜~÷÷}|_ÙOýüÊ#üýÖ©ñµüLÇ×÷\¿|ÛÄ÷†%ÿç×ÌÇò ™?ôVm:zcíÉ·ê^ºýßvù“çøá[ú¿lâSã&¾{Ç×]9î™Á§OÞ1ñÞ·=³êà;E_¾Ñ\Ùsݬ.Ütò«Žï<õy{ñê=põî³@|Á³t-_xVÍ쾀߸¦˜g–XM–#ôpôwfÉ¢Ö93ªKfÏW3¹¾ÙÞz­³ÁòÃ…Íè\T×Ôh7–Ì0›áJägÍæúÖz£³©ÑÕjfsÂcI „àäííí51TËç UäÍÎ0bÃB”^%g€É+xK É{B8°×ë󳯭YÆFgÑSNrÆÃr%ì¢TœÁ3Ao'×Wc¦: *„#¬9M—ÏÏò¦φgð¬ægÖk ÃÂgôGµ^jÌR5f1¾Yã õÂô„³^g¹´à’ô!ét:KEåTc³ãûÏ ý•Ÿö¤GAAAAAAAAAAAAAAAAA¾§XªÄÿF¨²”[ª**,ZJ¥Ä G­ð¿µT4*S›šÌâ›°è‹nÌò .ys Ës‘°‡å“в–Êòò¤¢¬š¢ÊIJ‡õJË*KG-ª1ØÅ™ÄwôïÝ‹Ä÷–Îg<=¾ Ûì¥á÷I/÷œUj™Se­1Ë’lgŸ#½º²ž]Áú¹}Ûe Û ¥h­Ù`*ÓñͯôlZ±S™¼ƒNk–ð^´”æÐYõÐ*޶IM£õTi²¤²§ï—;¥xŒgÓ-¾ãNkgs:›R™¹z¸° ‡'½9TkmM¤+ 9ëuú¾V6fÂa†f†Ý@ç7»Ú՞̊N‚ÏM•42ê[í\ äóÞ?ÃWÔàM~ÿŠ€Éãg‚Ý&«;©>õ­JF™ÖÒ† KUj£Ñú*Àx8¾/µMªjZMeÉF®úy)N™†Rkù¨ú©c½ˆv˜<’ ¬¥£´»¨z Õäšï ú‘€+Ê lšš«ú%é¥dI³«õ”fײ •¨³«sY7=g =YfI–&•P vo™Jhm±565´Lž<ùÿõõ9­ü_²öv·¶xÚ•SËnÂ0¼÷+,ŸÚƒ Ri¥Tªz@ân’ XõKë …¿¯“@…ÈÍã™]'›îf;À œÍù0p¶p¥²›œ×T‰W>EG‘Ö][š¾{¯Uw œÉš¶9Gç(ºB€;{I„Çh“¿Ì˃v²d¶6kÀ¯j¡4Äôo'‰ÏÙd8ñôHZ×¶ÔÀ¼l4’´ ŸHï›Ù\Íd¾˜µåË­CZuÉ–„í:Úíô«ÓˆþõóèÀK×r=3½EÍj¿AY‚è.ïêw‹û•’z¬/+4ª7ïB ^8?¡»<üÃÎŽ¯ô¾@gR–ö?Ãä`ã bxÚ…SMOã0½ó+,s%1E¨ppЉ {«ÄgÚX8¶±Ç|ì¯Çqštƒ¶§NfüÞ›y3üæ³S䜗FWtUžSZ˜Fê}EîŠkz³9áR{¬•Wìƒ,¼pÒ"é¤ÞZOãë º9!ñÇíë¾p°#²©¨0]ék_6¡ë¾rA*z ºQPdîCb’$÷wé϶53Ï]—´Ž?Vt•ØÛЦ ¬­¥l¢ƒ- á,÷0väL.aφ—9JT°Iœ Á0c±'"x4ü Õ!(qð¤ƒ<\_Ñ]­z,½|éãìÓàè#jLëÀn­Urˆütñ¿6oè¿×4Û½þŒ¾7Z¨0|ÔFG]ùtï¾°ßÕU<ÓÓN& ÏÙÒµo¾v¹Mrelic-7.6.1/functest/packages/dummy.xap000066400000000000000000000003021455105530300200750ustar00rootroot00000000000000PK Ô­{J-;¯ AppManifest.xamlUT ?ˆÙX?ˆÙXux ôôhello world PK Ô­{J-;¯ ´AppManifest.xamlUT?ˆÙXux ôôPKVVrelic-7.6.1/functest/packages/fatfile.app/000077500000000000000000000000001455105530300204265ustar00rootroot00000000000000relic-7.6.1/functest/packages/fatfile.app/Contents/000077500000000000000000000000001455105530300222235ustar00rootroot00000000000000relic-7.6.1/functest/packages/fatfile.app/Contents/Info.plist000066400000000000000000000023621455105530300241760ustar00rootroot00000000000000 BuildMachineOSBuild 20F71 CFBundleDevelopmentRegion en CFBundleExecutable dummy CFBundleIdentifier com.sas.dummy CFBundleInfoDictionaryVersion 6.0 CFBundleName dummy CFBundlePackageType APPL CFBundleShortVersionString 1.0 CFBundleSupportedPlatforms MacOSX CFBundleVersion 1 DTCompiler com.apple.compilers.llvm.clang.1_0 DTPlatformBuild 12E507 DTPlatformName macosx DTPlatformVersion 11.3 DTSDKBuild 20E214 DTSDKName macosx11.3 DTXcode 1251 DTXcodeBuild 12E507 LSMinimumSystemVersion 11.0 relic-7.6.1/functest/packages/fatfile.app/Contents/MacOS/000077500000000000000000000000001455105530300231655ustar00rootroot00000000000000relic-7.6.1/functest/packages/fatfile.app/Contents/MacOS/dummy000077500000000000000000005211001455105530300242450ustar00rootroot00000000000000Êþº¾@"@ €"@Ïúíþ'˜… H__PAGEZEROX__TEXT@@ __text__TEXT°7*°7€__stubs__TEXTÚ;NÚ;€__stub_helper__TEXT(<’(<€__swift5_entry__TEXT¼<¼<__const__TEXTÀ<PÀ<__swift5_typeref__TEXT>o>__swift5_fieldmd__TEXT€> €>__swift5_reflstr__TEXT >  >__swift5_assocty__TEXT¬>0¬>__swift5_proto__TEXTÜ>Ü>__swift5_types__TEXTä>ä>__unwind_info__TEXTì>œì>__eh_frame__TEXTˆ?pˆ?8__DATA_CONST@@@@__got__DATA_CONST@¸@ __const__DATA_CONST¸@¸¸@__objc_imageinfo__DATA_CONSTpApA8__DATA€@€@__la_symbol_ptr__DATA€h€$__data__DATAh€8h€__bss__DATA €H__LINKEDITÀ€À@b"€0ÀÀˆ˜Æ(ÀÉ0Ê7@ΰ P5xÍ1 /usr/lib/dyld!œ ¢Ãý0ÉœÆëc¾"2    Š*(€ 8 `evï,/System/Library/Frameworks/Foundation.framework/Versions/C/Foundation 8ä/usr/lib/libobjc.A.dylib 8d /usr/lib/libSystem.B.dylib X/System/Library/Frameworks/SwiftUI.framework/Versions/A/SwiftUI€@h/usr/lib/swift/libswiftAppKit.dylib€@Ö/usr/lib/swift/libswiftCloudKit.dylib @µ/usr/lib/swift/libswiftCore.dylib€@/usr/lib/swift/libswiftCoreData.dylib€H/usr/lib/swift/libswiftCoreFoundation.dylib€H/usr/lib/swift/libswiftCoreGraphics.dylib€@/usr/lib/swift/libswiftCoreImage.dylib€H/usr/lib/swift/libswiftCoreLocation.dylib€@/usr/lib/swift/libswiftDarwin.dylib€@d/usr/lib/swift/libswiftDispatch.dylib€@f/usr/lib/swift/libswiftFoundation.dylib€@/usr/lib/swift/libswiftIOKit.dylib€@/usr/lib/swift/libswiftMetal.dylib€@/usr/lib/swift/libswiftObjectiveC.dylib€@/usr/lib/swift/libswiftQuartzCore.dylib€P |/usr/lib/swift/libswiftUniformTypeIdentifiers.dylib€@/usr/lib/swift/libswiftXPC.dylib€8dè/usr/lib/swift/libswiftos.dylib€0 @executable_path/../Frameworks&ðÉ)ÊðÖPKUH‰åèE]ÃDUH‰åè;]ÃDUH‰åè1€â]ÃfUH‰åAWAVAUATSPH‰ÃH¿Hello, wH¾orld!íèܶÉH‰ÇH‰Ö‰ÊL‰ÁA¸A¹hjjjèÄHƒÄ I‰ÆI‰×A‰ÌM‰Åè©L‰3L‰{A€äDˆcL‰kˆC HÇC(HÇC0HÇC8HÇC@ÆCHHƒÄ[A\A]A^A_]Ãf.„@UH‰åAUPè$H=­H‰ÆI‰ýè.1ÀHƒÄA]]ÄPH‹˜GH…ÀtYÃH=ÂH5sè0H‰wGYÃDHA1ÒÃfDUH‰åH51º]éêf.„UH‰å]éfDHƒìH‹-GH…ÀtHƒÄÃH=$Gè?H‹ H‰L$H‹ ´H‰L$H‹=ÀHT$H‰Æè•H‰äFHƒÄÀSH‹H…Àx[ÃH‰ûH‰ÂHÁú H÷ÚHcðHþ¿ÿ1ÉE1ÀèOH‰[ÃHy1ÒÃfDUH‰åH5íº]é f.„UH‰å]Ãf.„UH‰åAWAVAUATSHƒì(H‰E°H=LFèïH‹HøI‰ÄH‰M¸H‹A@I‰çHƒÀHƒàðI)ÇL‰üH‰áH)ÁH‰MÐH‰ÌH‰ãH)ÃH‰ÜH‰]ÈI‰åI)ÅL‰ìè×H‰ÁH=}ÿÿÿH®H‰Ø1öè6L‹u¸I‹NH‰MÀL‰ÿH‰ÞL‰ãL‰âÿÑèÍH‰ÂH‹EÐL‰ÿL‰æè M‹fL‰ÿH‰ÞAÿÔL‰ïH‹uÐH‰ÚAÿV H‹}°L‰îH‰ÚÿUÀH‹}ÈH‰ÞAÿÔL‰ïH‰ÞAÿÔHeØ[A\A]A^A_]ÃUH‰å]Ãf.„SH‹H…Àx[ÃH‰ûH‰ÆHÁþ H÷ÞHcøHß1Ò1Éè¿H‰[Ã@PH‹(EH…ÀtYÃH=†H5Ëè H‰EYÃDPH‹EH…ÀtYÃH=âDèõýÿÿH‹=nH‰ÆèhH‰×DYÃf.„UH‰å]é¶ÿÿÿÿ% Dÿ%"Dÿ%$Dÿ%&Dÿ%(Dÿ%*Dÿ%,Dÿ%.Dÿ%0Dÿ%2Dÿ%4Dÿ%6Dÿ%8DL9DASÿ%yhéæÿÿÿh6éÜÿÿÿhpéÒÿÿÿh¯éÈÿÿÿhÒé¾ÿÿÿhøé´ÿÿÿhgéªÿÿÿhËé ÿÿÿh5é–ÿÿÿhéŒÿÿÿh¥é‚ÿÿÿhÐéxÿÿÿh énÿÿÿäûÿÿdummyðÿÿÿContentViewQäÿÿÿìÿÿÿüÿÿ}àÿÿÿ‰ñaŒúÿÿa”úÿÿaœúÿÿi¤úÿÿXCÄxÿÿÿ€€Þ²ÔdummydummyAppQ8ÿÿÿìÿÿÿHüÿÿô¡àÿÿÿ•“=“PüÿÿHýÿÿ­¬²XoÆéfÀ×ÑÑkOXÿ|´|z…Ú½‹H‰,§­¬²XoÆéfÀ×ÑÑkOXÿ|´|z…Ú½‹H‰,§­¬²XoÆéfÀ×ÑÑkOXÿ|´|z…Ú½‹H‰,§§¯ +Lîâ§éŒHñ…‹<,dÅžÚ¡.rBÐ!ÕÆ.=¾{éÛ÷ÔO6h\>ã©þû`vÛ¼õX|Äù¡:Ð}«úÞ  úÞqq> com.apple.security.app-sandbox com.apple.security.files.user-selected.read-only úÞqrf1\0# com.apple.security.app-sandbox05 0com.apple.security.files.user-selected.read-onlyúÞ Ïúíþ '˜… H__PAGEZEROX__TEXT@@ __text__TEXT´6,´6€__stubs__TEXTà:œà:€ __stub_helper__TEXT|;´|;€__swift5_entry__TEXT0<0<__const__TEXT4<P4<__swift5_typeref__TEXT„=o„=__swift5_fieldmd__TEXTô= ô=__swift5_reflstr__TEXT> >__swift5_assocty__TEXT >0 >__swift5_proto__TEXTP>P>__swift5_types__TEXTX>X>__unwind_info__TEXT`>¨`>__eh_frame__TEXT?ø?8__DATA_CONST@@@@__got__DATA_CONST@P@ __const__DATA_CONSTP@ P@__objc_imageinfo__DATA_CONSTpApA8__DATA€@€@__la_symbol_ptr__DATA€h€__data__DATAh€8h€__bss__DATA €H__LINKEDITÀ€À@b"€0ÀÀÀÐÆ(øÉ0@Ê7@ΰ P5°Í$ /usr/lib/dyld"Ôë˜ió6¡¾,öÍøyW°2    Š*(€Œ7 `evï,/System/Library/Frameworks/Foundation.framework/Versions/C/Foundation 8ä/usr/lib/libobjc.A.dylib 8d /usr/lib/libSystem.B.dylib X/System/Library/Frameworks/SwiftUI.framework/Versions/A/SwiftUI€@h/usr/lib/swift/libswiftAppKit.dylib€@Ö/usr/lib/swift/libswiftCloudKit.dylib @µ/usr/lib/swift/libswiftCore.dylib€@/usr/lib/swift/libswiftCoreData.dylib€H/usr/lib/swift/libswiftCoreFoundation.dylib€H/usr/lib/swift/libswiftCoreGraphics.dylib€@/usr/lib/swift/libswiftCoreImage.dylib€H/usr/lib/swift/libswiftCoreLocation.dylib€@/usr/lib/swift/libswiftDarwin.dylib€@d/usr/lib/swift/libswiftDispatch.dylib€@f/usr/lib/swift/libswiftFoundation.dylib€@/usr/lib/swift/libswiftIOKit.dylib€@/usr/lib/swift/libswiftMetal.dylib€@/usr/lib/swift/libswiftObjectiveC.dylib€@/usr/lib/swift/libswiftQuartzCore.dylib€P |/usr/lib/swift/libswiftUniformTypeIdentifiers.dylib€@/usr/lib/swift/libswiftXPC.dylib€8dè/usr/lib/swift/libswiftos.dylib€0 @executable_path/../Frameworks&(Ê)@ÊðÖPKý{¿©ý‘”!ý{Á¨À_ÖÿCÑø_©öW©ôO©ý{©ý‘󪩌Ҁ­òàÅòäîòáMŽÒ¬ò!Àò ýòú” €RèyÿùB€Ò€Ò€Ò€Òú”ôªõªöª÷ªò”tV©ÈhB9wù`‚9äo`‚‚<`‚ƒ<(€Rh"9ý{D©ôOC©öWB©ø_A©ÿC‘À_ÖôO¾©ý{©ýC‘ ”᪀L ÕôªÖ”€Rý{A©ôO¨À_Öý{¿© Õ@EX`´ý{Á¨À_Ö * ÕJ Õâ”(D ÕýŸÈý{Á¨À_ÖG Õ€ÒÀ_Ö% Õ"€RÌÿƒÑý{© Õ@BX µ@B Ք᪠Õh?X Õ)>Xè'© Õ€>Xâ‘”h@ ÕýŸÈý{A©ÿƒ‘À_ÖôO¾©ý{©óª@ù€ø·ý{A©ôO¨À_Ö⃀Ëa ‹à€R€Ò€Ò«”`ùöÿÿ`C Õ€ÒÀ_ÖA$ Õ"€R›À_Öüoº©úg©ø_©öW©ôO©ý{©ýC‘ôª< ÕB”óª€_ø(#@ùé‘=‘í|’5Ë¿‘é‘6Ëß‘é‘7Ëÿ‘é‘8Ë‘?”㪠üÿ ÕÂ; Õ誀ÒZ”: @ùàªáªâª@?ÖA”âªèªàªáªR”;@ùàªáª`?Ö(@ùàªáªâª?Öàªáªâª@?Öàªáª`?Öàªáª`?Ö¿CÑý{E©ôOD©öWC©ø_B©úgA©üoƨÀ_ÖÀ_ÖôO¾©ý{©óª@ù€ø·ý{A©ôO¨À_ÖჀË` ‹€Ò€ÒD”`ù÷ÿÿý{¿© Õ 1X`´ý{Á¨À_Ö  Õa3 Õ=”ˆ0 ÕýŸÈý{Á¨À_Öý{¿© Õà/X`´ý{Á¨À_Öà. Õtÿÿ—᪠Հ*X+”ˆ. ÕýŸÈý{Á¨À_Öîÿÿ Õð(XÖ ÕÐ(XÖ Õ°(XÖ Õ(XÖ Õp(XÖ ÕP(XÖ Õ0(XÖ Õ(XÖ Õð'XÖ ÕÐ'XÖ Õ°'XÖ Õ'XÖ Õp'XÖq' ÕðG¿© Õð%XÖPùÿÿPöÿÿ6PóÿÿpPðÿÿ¯PíÿÿÒPêÿÿøPçÿÿgPäÿÿËPáÿÿ5PÞÿÿPÛÿÿ¥PØÿÿÐPÕÿÿ \ûÿÿdummyðÿÿÿContentViewQäÿÿÿìÿÿÿ ûÿÿàÿÿÿuuuúÿÿuúÿÿuúÿÿu$úÿÿäCÄxÿÿÿ€€Þ²ÔdummydummyAppQ8ÿÿÿìÿÿÿ¸ûÿÿô%àÿÿÿ““¤ûÿÿÈüÿÿÈCÄÜþÿÿ€€BñRlÏþÿÿÿ„úÿÿ•y§‚G$s7SwiftUI4ViewPxÿ fúÿÿ?ÿÿÿÿ ûÿÿ9y+G$s7SwiftUI3AppPÿ îüÿÿÿÿÿ ¼ÿÿÿ BodyBodydÿÿÿ€ÿÿÿäÿÿÿ^ÿÿÿˆÿÿÿ ÿÿÿÑÿÿÿ‚ÿÿÿ þÿÿÈþÿÿüýÿÿ¤þÿÿ ´688á:8 H  Ø LpÐ4`d à(ظT4zRx œøÿÿÿÿÿÿ@Dž8àøÿÿÿÿÿÿ`H ž$X ùÿÿÿÿÿÿ@H ž“”zRx $xúÿÿÿÿÿÿ<H ž“”@Œúÿÿÿÿÿÿ@Dž`¬úÿÿÿÿÿÿHDžT<T<=@”; ;¬;¸;Ä;Ð;Ü;è;ô;< <<$<½ÿÿïÿÿÿF½ÿÿôÿÿÿ"pàpHQ#]@dyld_stub_binderQrH@_$s4Body7SwiftUI3AppPTl€€@_$s4Body7SwiftUI4ViewPTl€¨ÿÿÿÿÿÿÿÿ@_$s7SwiftUI11WindowGroupVMn€€þÿÿÿÿÿÿÿ@_$s7SwiftUI11WindowGroupVyxGAA5SceneAAMc@_$s7SwiftUI14_PaddingLayoutVAA12ViewModifierAAWP@_$s7SwiftUI14_PaddingLayoutVMn@_$s7SwiftUI15ModifiedContentVMn@_$s7SwiftUI15ModifiedContentVyxq_GAA4ViewA2aERzAA0E8ModifierR_rlMc@_$s7SwiftUI3AppMp€ˆ@_$s7SwiftUI3AppP4BodyAC_AA5SceneTn@_$s7SwiftUI3AppP4body4BodyQzvgTq€@_$s7SwiftUI3AppPxycfCTq@_$s7SwiftUI4TextVAA4ViewAAWP€Ðýÿÿÿÿÿÿÿ@_$s7SwiftUI4TextVMn@_$s7SwiftUI4ViewMp€¨@_$s7SwiftUI4ViewP05_makeC04view6inputsAA01_C7OutputsVAA11_GraphValueVyxG_AA01_C6InputsVtFZTq€@_$s7SwiftUI4ViewP05_makeC4List4view6inputsAA01_cE7OutputsVAA11_GraphValueVyxG_AA01_cE6InputsVtFZTq@_$s7SwiftUI4ViewP14_viewListCount6inputsSiSgAA01_ceF6InputsV_tFZTq@_$s7SwiftUI4ViewP4BodyAC_AaBTn€Øÿÿÿÿÿÿÿÿ@_$s7SwiftUI4ViewP4body4BodyQzvgTq€ @_$s7SwiftUI5SceneMp€@A__swift_FORCE_LOAD_$_swiftAppKit€°þÿÿÿÿÿÿÿA__swift_FORCE_LOAD_$_swiftCloudKit€@_$sytWV€¹A__swift_FORCE_LOAD_$_swiftCoreData€ˆÿÿÿÿÿÿÿÿA__swift_FORCE_LOAD_$_swiftCoreFoundation€ÈÿÿÿÿÿÿÿÿA__swift_FORCE_LOAD_$_swiftCoreGraphicsA__swift_FORCE_LOAD_$_swiftCoreImage€A__swift_FORCE_LOAD_$_swiftCoreLocation€A__swift_FORCE_LOAD_$_swiftDarwin€ˆÿÿÿÿÿÿÿÿA__swift_FORCE_LOAD_$_swiftDispatch€A__swift_FORCE_LOAD_$_swiftFoundation€ A__swift_FORCE_LOAD_$_swiftIOKit€ A__swift_FORCE_LOAD_$_swiftMetal€ A__swift_FORCE_LOAD_$_swiftObjectiveC€˜ÿÿÿÿÿÿÿÿ A__swift_FORCE_LOAD_$_swiftQuartzCore€x A__swift_FORCE_LOAD_$_swiftUniformTypeIdentifiers€ ÿÿÿÿÿÿÿÿ A__swift_FORCE_LOAD_$_swiftXPC€àÿÿÿÿÿÿÿÿ A__swift_FORCE_LOAD_$_swiftos€s@_$s7SwiftUI11WindowGroupV7contentACyxGxyXE_tcfCs@_$s7SwiftUI12SceneBuilderV10buildBlockyxxAA0C0RzlFZs@_$s7SwiftUI18LocalizedStringKeyV13stringLiteralACSS_tcfCs@_$s7SwiftUI3AppPAAE4mainyyFZs @_$s7SwiftUI4EdgeO3SetV3allAEvgZs(@_$s7SwiftUI4TextV_9tableName6bundle7commentAcA18LocalizedStringKeyV_SSSgSo8NSBundleCSgs06StaticI0VSgtcfCs0@_$s7SwiftUI4ViewPAAE05_makeC04view6inputsAA01_C7OutputsVAA11_GraphValueVyxG_AA01_C6InputsVtFZs8@_$s7SwiftUI4ViewPAAE05_makeC4List4view6inputsAA01_cE7OutputsVAA11_GraphValueVyxG_AA01_cE6InputsVtFZs@@_$s7SwiftUI4ViewPAAE14_viewListCount6inputsSiSgAA01_ceF6InputsV_tFZsH@_swift_getOpaqueTypeConformancesP@_swift_getTypeByMangledNameInContextsX@_swift_getTypeByMangledNameInContextInMetadataStates`@_swift_getWitnessTable__mh_execute_header´m¸4@`@¬<@Hž<BEa0Iy•¾ò#Bb¥Þð4Qi‰¦º#6“ö9XzØ<€”œ@½@à@@ ,@ S@ w@ ž@ ¿@â@@'@G@l@‘@Â@à@ýBv 2345€ 6 2345__mh_execute_header_$s4Body7SwiftUI3AppPTl_$s4Body7SwiftUI4ViewPTl_$s7SwiftUI11WindowGroupV7contentACyxGxyXE_tcfC_$s7SwiftUI11WindowGroupVMn_$s7SwiftUI11WindowGroupVyxGAA5SceneAAMc_$s7SwiftUI12SceneBuilderV10buildBlockyxxAA0C0RzlFZ_$s7SwiftUI14_PaddingLayoutVAA12ViewModifierAAWP_$s7SwiftUI14_PaddingLayoutVMn_$s7SwiftUI15ModifiedContentVMn_$s7SwiftUI15ModifiedContentVyxq_GAA4ViewA2aERzAA0E8ModifierR_rlMc_$s7SwiftUI18LocalizedStringKeyV13stringLiteralACSS_tcfC_$s7SwiftUI3AppMp_$s7SwiftUI3AppP4BodyAC_AA5SceneTn_$s7SwiftUI3AppP4body4BodyQzvgTq_$s7SwiftUI3AppPAAE4mainyyFZ_$s7SwiftUI3AppPxycfCTq_$s7SwiftUI4EdgeO3SetV3allAEvgZ_$s7SwiftUI4TextVAA4ViewAAWP_$s7SwiftUI4TextVMn_$s7SwiftUI4TextV_9tableName6bundle7commentAcA18LocalizedStringKeyV_SSSgSo8NSBundleCSgs06StaticI0VSgtcfC_$s7SwiftUI4ViewMp_$s7SwiftUI4ViewP05_makeC04view6inputsAA01_C7OutputsVAA11_GraphValueVyxG_AA01_C6InputsVtFZTq_$s7SwiftUI4ViewP05_makeC4List4view6inputsAA01_cE7OutputsVAA11_GraphValueVyxG_AA01_cE6InputsVtFZTq_$s7SwiftUI4ViewP14_viewListCount6inputsSiSgAA01_ceF6InputsV_tFZTq_$s7SwiftUI4ViewP4BodyAC_AaBTn_$s7SwiftUI4ViewP4body4BodyQzvgTq_$s7SwiftUI4ViewPAAE05_makeC04view6inputsAA01_C7OutputsVAA11_GraphValueVyxG_AA01_C6InputsVtFZ_$s7SwiftUI4ViewPAAE05_makeC4List4view6inputsAA01_cE7OutputsVAA11_GraphValueVyxG_AA01_cE6InputsVtFZ_$s7SwiftUI4ViewPAAE14_viewListCount6inputsSiSgAA01_ceF6InputsV_tFZ_$s7SwiftUI5SceneMp_$sytWV__swift_FORCE_LOAD_$_swiftAppKit__swift_FORCE_LOAD_$_swiftCloudKit__swift_FORCE_LOAD_$_swiftCoreData__swift_FORCE_LOAD_$_swiftCoreFoundation__swift_FORCE_LOAD_$_swiftCoreGraphics__swift_FORCE_LOAD_$_swiftCoreImage__swift_FORCE_LOAD_$_swiftCoreLocation__swift_FORCE_LOAD_$_swiftDarwin__swift_FORCE_LOAD_$_swiftDispatch__swift_FORCE_LOAD_$_swiftFoundation__swift_FORCE_LOAD_$_swiftIOKit__swift_FORCE_LOAD_$_swiftMetal__swift_FORCE_LOAD_$_swiftObjectiveC__swift_FORCE_LOAD_$_swiftQuartzCore__swift_FORCE_LOAD_$_swiftUniformTypeIdentifiers__swift_FORCE_LOAD_$_swiftXPC__swift_FORCE_LOAD_$_swiftos_swift_getOpaqueTypeConformance_swift_getTypeByMangledNameInContext_swift_getTypeByMangledNameInContextInMetadataState_swift_getWitnessTabledyld_stub_binderradr://5614542úÞ Àò4:F„êúÞ FXÖð  @com.sas.dummy†ÞxùËz_ÊÕÑ`XuÐ~ž ã­å.<ä—ÁZkÓž!˜t{î–„y ¯D‡oDlê–O„+Ñû½ê9õ?ùGàØf†Þ¢Š/á6˸mˬÈ'ÏÄêkM«ñ„^Zîbž›˜y N«euxŒJ °RNj€¿Ç£-øÒ7¦Cù†·àu}Ûã©þû`vÛ¼õX|Äù¡:Ð}«úÞ  úÞqq> com.apple.security.app-sandbox com.apple.security.files.user-selected.read-only úÞqrf1\0# com.apple.security.app-sandbox05 0com.apple.security.files.user-selected.read-onlyúÞ relic-7.6.1/functest/packages/fatfile.app/Contents/PkgInfo000066400000000000000000000000101455105530300234720ustar00rootroot00000000000000APPL????relic-7.6.1/functest/packages/fatfile.app/Contents/_CodeSignature/000077500000000000000000000000001455105530300251165ustar00rootroot00000000000000relic-7.6.1/functest/packages/fatfile.app/Contents/_CodeSignature/CodeResources000066400000000000000000000042301455105530300276050ustar00rootroot00000000000000 files files2 rules ^Resources/ ^Resources/.*\.lproj/ optional weight 1000 ^Resources/.*\.lproj/locversion.plist$ omit weight 1100 ^Resources/Base\.lproj/ weight 1010 ^version.plist$ rules2 .*\.dSYM($|/) weight 11 ^(.*/)?\.DS_Store$ omit weight 2000 ^(Frameworks|SharedFrameworks|PlugIns|Plug-ins|XPCServices|Helpers|MacOS|Library/(Automator|Spotlight|LoginItems))/ nested weight 10 ^.* ^Info\.plist$ omit weight 20 ^PkgInfo$ omit weight 20 ^Resources/ weight 20 ^Resources/.*\.lproj/ optional weight 1000 ^Resources/.*\.lproj/locversion.plist$ omit weight 1100 ^Resources/Base\.lproj/ weight 1010 ^[^/]+$ nested weight 10 ^embedded\.provisionprofile$ weight 20 ^version\.plist$ weight 20 relic-7.6.1/functest/packages/hello.jar000066400000000000000000000007221455105530300200370ustar00rootroot00000000000000PKåª{J META-INF/þÊPKPKåª{JMETA-INF/MANIFEST.MFóMÌËLK-.Ñ K-*ÎÌϳR0Ô3àår.JM,IMÑuª XèÄ*hø%&ç¤*8çä%–ÕkòrñrPKÓ<2‘DEPKê{J hello.txtËHÍÉÉW(Ï/ÊIáPK-;¯ PKåª{J META-INF/þÊPKåª{JÓ<2‘DE=META-INF/MANIFEST.MFPKê{J-;¯ Ãhello.txtPK´relic-7.6.1/functest/packages/hello.mof000066400000000000000000000001541455105530300200430ustar00rootroot00000000000000#pragma autorecover #pragma classflags("forceupdate") #pragma namespace("\\\\.\\root\\ServiceModel") relic-7.6.1/functest/packages/hello.ps1000066400000000000000000000000211455105530300177560ustar00rootroot00000000000000echo hello world relic-7.6.1/functest/packages/hello.ps1xml000066400000000000000000000000741455105530300205070ustar00rootroot00000000000000 relic-7.6.1/functest/packages/hyperv.cat000066400000000000000000000324141455105530300202470ustar00rootroot000000000000000‚5 *†H†÷  ‚4ù0‚4õ1 0 +0‚ý +‚7  ‚î0‚ê0  +‚7  =”%,K‹m°‡Èfºd 101120192600Z0 +‚7 0‚L0‚&R0EB0437FC77CDABF1B479D3E39E0FC1F4E3D08971Ï0b +‚7 1T0RL{C689AAB8-8E78-11D0-8C47-00C04FC295EE}0i +‚71[0Y04 +‚70&   ¢€<<<Obsolete>>>0!0 +°CÇ|Ú¿G>9àüN=—0‚&R13EA2DC1A641C0E93928855F97C05E37171828CB1Ï0b +‚7 1T0RL{C689AAB8-8E78-11D0-8C47-00C04FC295EE}0i +‚71[0Y04 +‚70&   ¢€<<<Obsolete>>>0!0 +ê-Á¦AÀé9(…_—À^7(Ë0‚&R15B52F64C14EA48A9E391E5F73D3348F156514F91Ï0b +‚7 1T0RL{C689AAB8-8E78-11D0-8C47-00C04FC295EE}0i +‚71[0Y04 +‚70&   ¢€<<<Obsolete>>>0!0 +µ/dÁN¤Šž9_sÓ4eù0‚&R33811C2F753F0B615BE04B10AF4C7B5B29F0BF0B1Ï0b +‚7 1T0RL{C689AAB8-8E78-11D0-8C47-00C04FC295EE}0i +‚71[0Y04 +‚70&   ¢€<<<Obsolete>>>0!0 +3/u? a[àK¯L{[)ð¿ 0‚R3D5356310576FAED5840C53E8475F433676FF1821Ç0a +‚71S0Q0, +‚7¢€<<<Obsolete>>>0!0 +=SV1vúíX@Å>„uô3goñ‚0b +‚7 1T0RL{DE351A42-8E59-11D0-8C47-00C04FC295EE}0‚R580D96C0735328806D95D43986BF3613852AF39D1Ç0a +‚71S0Q0, +‚7¢€<<<Obsolete>>>0!0 +X –ÀsS(€m•Ô9†¿6…*ó0b +‚7 1T0RL{DE351A42-8E59-11D0-8C47-00C04FC295EE}0‚&R590E5B90D116FE0262B58245FEC908DE81625D0A1Ï0b +‚7 1T0RL{C689AAB8-8E78-11D0-8C47-00C04FC295EE}0i +‚71[0Y04 +‚70&   ¢€<<<Obsolete>>>0!0 +Y[Ñþbµ‚EþÉÞb] 0‚&R5C167AC1B868ADBC5079BE62394EBF353C132A361Ï0b +‚7 1T0RL{C689AAB8-8E78-11D0-8C47-00C04FC295EE}0i +‚71[0Y04 +‚70&   ¢€<<<Obsolete>>>0!0 +\zÁ¸h­¼Py¾b9N¿5<*60‚R5F7A0470DD00C55BF0996018F79CA324C2B3D69A1Ç0a +‚71S0Q0, +‚7¢€<<<Obsolete>>>0!0 +_zpÝÅ[ð™`÷œ£$³֚0b +‚7 1T0RL{DE351A42-8E59-11D0-8C47-00C04FC295EE}0‚R64062ADD9E7ECA2E6E74B460954965B9A1FE1E781Ç0a +‚71S0Q0, +‚7¢€<<<Obsolete>>>0!0 +d*Ýž~Ê.nt´`•Ie¹¡þx0b +‚7 1T0RL{DE351A42-8E59-11D0-8C47-00C04FC295EE}0‚R732A9B73E6E841F902A38357FC58E36E187F2B5A1Ç0a +‚71S0Q0, +‚7¢€<<<Obsolete>>>0!0 +s*›sæèAù£ƒWüXãn+Z0b +‚7 1T0RL{DE351A42-8E59-11D0-8C47-00C04FC295EE}0‚R7399E9107A0EB8F74280F4793C7A29E649FE7E0C1Ç0a +‚71S0Q0, +‚7¢€<<<Obsolete>>>0!0 +s™éz¸÷B€ôy>>0!0 +„{·€5VXõ§m!$˜»Ðd£<>0b +‚7 1T0RL{DE351A42-8E59-11D0-8C47-00C04FC295EE}0‚&RB49A245077CA686DD375B81CA227C3CB6F94789F1Ï0b +‚7 1T0RL{C689AAB8-8E78-11D0-8C47-00C04FC295EE}0i +‚71[0Y04 +‚70&   ¢€<<<Obsolete>>>0!0 +´š$PwÊhmÓu¸¢'ÃËo”xŸ0‚&RB67E35226B74F5E54FE9A6E7E75C1E2C4B94BC6D1Ï0b +‚7 1T0RL{C689AAB8-8E78-11D0-8C47-00C04FC295EE}0i +‚71[0Y04 +‚70&   ¢€<<<Obsolete>>>0!0 +¶~5"ktõåOé¦çç\,K”¼m0‚&RC10DC33ADC50E0E54E33FB9B0D4967B06E0F74AE1Ï0b +‚7 1T0RL{C689AAB8-8E78-11D0-8C47-00C04FC295EE}0i +‚71[0Y04 +‚70&   ¢€<<<Obsolete>>>0!0 +Á Ã:ÜPàåN3û› Ig°nt®0‚RC3D994131CE8D4F601F5916F3B41564EE16934A41Ç0a +‚71S0Q0, +‚7¢€<<<Obsolete>>>0!0 +ÃÙ”èÔöõ‘o;AVNái4¤0b +‚7 1T0RL{DE351A42-8E59-11D0-8C47-00C04FC295EE}0‚&RC945A2FBF674F2E78DC4C701578677E1C911CB731Ï0b +‚7 1T0RL{C689AAB8-8E78-11D0-8C47-00C04FC295EE}0i +‚71[0Y04 +‚70&   ¢€<<<Obsolete>>>0!0 +ÉE¢ûötòçÄÇW†wáÉËs0‚RECDA9136EE1E7EB2454B85CEBFBB37B3FC3A88F21Ç0a +‚71S0Q0, +‚7¢€<<<Obsolete>>>0!0 +ìÚ‘6î~²EK…ο»7³ü:ˆò0b +‚7 1T0RL{DE351A42-8E59-11D0-8C47-00C04FC295EE}0‚&REDF354C548AB8B1FFA7BA1BF920C958BA94EC85F1Ï0b +‚7 1T0RL{C689AAB8-8E78-11D0-8C47-00C04FC295EE}0i +‚71[0Y04 +‚70&   ¢€<<<Obsolete>>>0!0 +íóTÅH«‹ú{¡¿’ •‹©NÈ_0‚&REEF87596309F6C3D0D9E50240DB9F9A9071C9EF91Ï0b +‚7 1T0RL{C689AAB8-8E78-11D0-8C47-00C04FC295EE}0i +‚71[0Y04 +‚70&   ¢€<<<Obsolete>>>0!0 +îøu–0Ÿl= žP$ ¹ù©žù0‚RF4C57B1170F17466C9D55DE7441A61CA9B1817F31Ç0a +‚71S0Q0, +‚7¢€<<<Obsolete>>>0!0 +ôÅ{pñtfÉÕ]çDaÊ›ó0b +‚7 1T0RL{DE351A42-8E59-11D0-8C47-00C04FC295EE} Y0W0U +‚7 G0E OSAttr02:5.1,2:5.2,2:6.0,2:6.1 ‚O0‚Ê0‚²  aÜö 0  *†H†÷ 0w1 0 UUS10U Washington10URedmond10U Microsoft Corporation1!0UMicrosoft Time-Stamp PCA0 080725191250Z 110725192250Z0³1 0 UUS10U Washington10URedmond10U Microsoft Corporation1 0 U MOPR1'0%U nCipher DSE ESN:159C-A3F7-25701%0#UMicrosoft Time-Stamp Service0‚"0  *†H†÷ ‚0‚ ‚Àí¡^wÀ[ôv‰bú­|h´÷½5ØyZÊÙlQEb&z/ØêÁnùægÛQ°,ÞŠíÿ ­4ê˜û¥Ö*ÒñD'Z-:“ÿVS°ÈõóòIÌÐõLXø›õ%±fÀ½ÈÒR…Â+8²Ã6¾ù‡ÚôŽ]C׿™Ÿ¤Îþ«acç9ÅóöØü16irZ¢L>ê‡%BÑ>ñ—Ò2“pUSã; 辂xmæú͘¤oÛîfô•ÈÍ5Éž»6 ƒ–”&§à©4;ÕÀž>ðÔG† ‚¤X0:vã­•f´·ý Š`£â“–X"œ+Û¢”•½@•£‚0‚0UÒí $»7©Ø jMÒÕ.¾žë0U#0€#4øÙRFp í@ûvû³+°Ã5³0TUM0K0I G E†Chttp://crl.microsoft.com/pki/crl/products/MicrosoftTimeStampPCA.crl0X+L0J0H+0†ú82ƒSòù2õÅnp¡É±ck gÙëŠjÛ`fééR&ó;ÆjÓÂR¾¨¹ëjªxŒÉ}• Ì!³ž½ÍÁ‹)½b%ï Wç†N*ì€Ê»ü!Ä?NRæ±ØÁÂyd´Ps5^]Á¸ºªÏRö€‘æïQCFéÐè”ö,$ ŠÆ²1Š£~6l¤Lg*»»¥¥0rÐ ;$“[Ù9“Ós-ÄÔl¡ìö1¸kKìî\32Œ| / ù ¸Óž_Öü ™¸»UnÍBK:MŒ+ÊÈÓbnê £fÙyOŽ¢ÿ̘0‚Í0‚µ  a# 0  *†H†÷ 01 0 UUS10U Washington10URedmond10U Microsoft Corporation1+0)U"Microsoft Windows Verification PCA0 091207215740Z 110307215740Z01 0 UUS10U Washington10URedmond10U Microsoft Corporation1 0 U MOPR10UMicrosoft Windows0‚"0  *†H†÷ ‚0‚ ‚ÈØzdÉ–DãxÔûÊ—=Ërš¥®Z ¦þs1,CGcO(ןG›3Õ95©­Ï]ê½»É(¯2—HäÜï3!HW>ªñŠRÔ_:0 ¶]nP’¶ì<°Šî<•EŒHÅÌit&(I×ÓÛ«ÚF]™Y`¼¬:V…hz¢JLê–¹*\øÀ+NRö@ìÝõ—¹ÀAjºØÞMùÏJS#íïûS¹i4cõê €»‰¿WÔ¶ö¥"—–Žç"®f‡[¦ƒüå¹r Þ˜8ÿÿglÕ\Næ­?ÐdЪU©vÿ¢gÒ ]g”CþŠ–”îʽl» ¢w®a ÈÍ. £‚F0‚B0U%0+ +‚7 0UÚU“4ùEÜkߣæÝÀãr¼þ0Uÿ€0U#0€‹¥píàù©À¬ǵô‚±<ÅJ0{Ut0r0p n l†4http://crl.microsoft.com/pki/crl/products/WinPCA.crl†4http://www.microsoft.com/pki/crl/products/WinPCA.crl0R+F0D0B+0†6http://www.microsoft.com/pki/certs/MicrosoftWinPCA.crt0  *†H†÷ ‚15™i¦—¶Ð]^vÂÜ5•o&ŸÝ-a7mgË¥Tì,ÔµdüÔ•õ¾Ô¢1~4Ê*í0Ü/ö‘ˆp—~ÔqûbllFí”3¨™Ñ‚ÁAÒ\ïÔn m[³G€~Œ‚s·é_V"cso[<ÿÞÛšÿ–Œtõà„TÑmdd,“G9KžázÃýŸ{øú÷nñÕZ•þÕ°=z·fIi-¥Å<:{bE/VrílÔPuÅì^ÊÏ3 6Ç4ûáCI:é°Ã5ÿWõsNw@¹•ÐHˆ‹<ÁÈý÷‡w̉0é4Å5è?xjEÐè×ã+Z¬‰²úRŠ0Hmm,0‚0‚ï  ah40  *†H†÷ 0_10 ’&‰“ò,dcom10 ’&‰“ò,d microsoft1-0+U$Microsoft Root Certificate Authority0 070403125309Z 210403130309Z0w1 0 UUS10U Washington10URedmond10U Microsoft Corporation1!0UMicrosoft Time-Stamp PCA0‚"0  *†H†÷ ‚0‚ ‚Ÿ¡l±ßÛH’*|k.á½âãÅ™Q#P­ÎÝN$îѧÑL­t0 ëÕT•I”B’®˜\0&Úkè{½ì‰÷˜ð‰ÌË3$‡òôg,ü{çˆêçN£¡Á#SÊúEÏ Ð^¯Ð°B¢ù¦l“g×(ÜFS°†Ðå(F.'¬†OUR ä,ûj0n‡óY0ú~Ö—³è!—~øÒó·SmR´EŸHJGf')f¨—äÓJ¢ù„§G ‚Ÿ„íUxþšP…0Fí·Þ#F»Ä-TŸ¯xA1wÌ›ß;ƒ“¡aµ ±ü÷›²Î"KTÿùࣂ«0‚§0Uÿ0ÿ0U#4øÙRFp í@ûvû³+°Ã5³0 U†0 +‚70˜U#0€¬‚`@V'—å%ü*á S•Y䤡c¤a0_10 ’&‰“ò,dcom10 ’&‰“ò,d microsoft1-0+U$Microsoft Root Certificate Authority‚y­¡J ¥­LsXô.e0PUI0G0E C A†?http://crl.microsoft.com/pki/crl/products/microsoftrootcert.crl0T+H0F0D+0†8http://www.microsoft.com/pki/certs/MicrosoftRootCert.crt0U% 0 +0  *†H†÷ ‚—ŠÃ\D6Ýé´­wÛÎyQM±.tq[m «Î¾{¸.Ô¢Œmb¸WËN P™Ýz@âW»¯Xšá†¬»xò‹Ðì;îâ¾ ÈHâðSÝO«’äçjÕ€Áæ”ò/…é‘*$"pûöüäx™. ÷âp¼NŽk r•¸¡9œg-ÅQêb\?˜‹ ?â2ùÌ1Jv1=+r Èêp=ÿ… ß aŽð׸ëN‹Å5+^£¿ë¼} B{ÔSr!î0Ê»xe\[ ÒÚ˜õ<¹fX³-/çù…†ÌQVèp”l¬9LÔöy¿ªzb))4wbÉ=m‚ð Ç,»ÏC³åùì}µã¤¨t5¸NÅq#&v ÞCf¹ÌƒH:Å6zH ‰=c¢v)—uéæ— ’øâ'ð)gCwÃP–S1á¶q‹ìwÇ|1HÕ¸%"Œ÷(8z½|Ç?Í@&wÝš•¾Û£‚:0‚60 +‚70U‹¥píàù©À¬ǵô‚±<ÅJ0 UÆ0Uÿ0ÿ0˜U#0€¬‚`@V'—å%ü*á S•Y䤡c¤a0_10 ’&‰“ò,dcom10 ’&‰“ò,d microsoft1-0+U$Microsoft Root Certificate Authority‚y­¡J ¥­LsXô.e0PUI0G0E C A†?http://crl.microsoft.com/pki/crl/products/microsoftrootcert.crl0T+H0F0D+0†8http://www.microsoft.com/pki/certs/MicrosoftRootCert.crt0€U y0w0u +‚7/0h0f+0ZXCopyright © 1999-2005 Microsoft Corporation.0U%0+ +‚7 0  *†H†÷ ‚%1¡XêRåá æñ?x£?r¯§WS‰^2œ¶pÃ+M04èÀjäÓ. eHטb!ºEŸJìÛ/ Qåí2Q/áÝûÆRýëÆ‚%B ¦8¶6ÌÉ€»Zi1ó ³gG¾Çâ?–³ˆø¾9¹é•Îü|¯¨ÍÐAàÕ³/h»ÛljÝò¯ÞòµÞ ¶¦Zð† ¹m™K?{-„l‡Ü«ˆÐ‘4¾‚"¤¼UŠ­›üsÄÉ}›À•&]Ƭ´ò~º%pJ{׎ѠIz°RR$ô¯Ý@-å>2X³JjÝYª-¼¤ s8ù@wk4WÍ8h'‚øÑoë#À?RóNÕ>jš+Áõ1qÛAM;Þï­¯ˆeCQ·šuÊŽiIxŠtE¹ Žsw2JK×h+˜ÅºTê?Ë¢Œ»ØXòÛÜ›ÍØêHCâJ~e²Üõ-N%g¨àµº§Ý~^ÁLtɳnãøð íü¹)Å[É6QÛx}¹2^vÒ\;7!ÆÛÉnít*\, QIES°²³#Ô¡°_ ͧã<›—r”ßÿÁ¥ßõ©óÚµüDàèâ<¢z»»eæM±µ¡Ùg;° };éîQ*GõŒ­].5ßô¤.ö7Z+èUšI,—Ξ—F\Ù-¼$Z•YoMÊÖW&1‚0‚‰001 0 UUS10U Washington10URedmond10U Microsoft Corporation1+0)U"Microsoft Windows Verification PCA a# 0 + ±0 *†H†÷  1  +‚7 0 +‚7 10  +‚70# *†H†÷  1űà–zõ¨uÒþ¬ÚÜå­£Ö<0R +‚7 1D0B $€"Microsoft Windows¡€http://www.microsoft.com0  *†H†÷ ‚OÔ(‹ ¬c%ùH­q`sm™ãà†w¼!Ú­Yõƒ€4¾P¦]% 7_(óà‚Ò¬ºžb–hy¸"·ˆë9T„Á&eæö¶ëüBFgûâôŒ…÷ýκ§*'ªBÀ†O91l¯.·œ² Nº€¡‹Y…‡Æ­ýü×èÃ’´è´{—þ¨Ân=뜓Wä´¥#ß9Õ¸( ‘Áð½×1 Ð+sSµ…àÔñÍ¿hë?^ÿœÑÕI(x‘Çg¸Úÿq³O?' )¥miû·þ„Ó^Ú§ÉQxGð¥_;ÏÝI*9™Ìîð[>¼6ÏÏΡ‚0‚ *†H†÷  1‚ 0‚0…0w1 0 UUS10U Washington10URedmond10U Microsoft Corporation1!0UMicrosoft Time-Stamp PCA aÜö 0+ ]0 *†H†÷  1  *†H†÷ 0 *†H†÷  1 101120193521Z0# *†H†÷  1¢êB‹ï€Û!‡¾Cð1¯¢]½0  *†H†÷ ‚^K›¡’køn•Ž.®egÄÍêvÚ[ÞD3IŸCG2Ž·xÁ_bQ–@ôýo†éÕp—wÉiïÐ^€Q5/W¨O#ä?Äö=Ðcý¡÷#mÝ&%¢gz¹Kòy`ÊÅOõ IºSrÍ:xMX4K4jir¶QÓcýÛU}ÄìDÅœÎ*Ù6º:v‚ÙB/¯ê´úå£'&¤‰>÷@_¤‡xëMSZ4­'ÿÞ/ŠãD†·#3ÞI‰>ïeIf~c°Ô=ýåýL1¨儺â¤y,ö")pÄjI]MíEg¦‘%ǯeËÒ líµ{•5ïT0µ´¾ relic-7.6.1/functest/packages/slimfile.app/000077500000000000000000000000001455105530300206205ustar00rootroot00000000000000relic-7.6.1/functest/packages/slimfile.app/Info.plist000066400000000000000000000022411455105530300225670ustar00rootroot00000000000000bplist00ß  !"#%&'()'*+,-./-3679=_BuildMachineOSBuild_CFBundleDevelopmentRegion_CFBundleExecutable_CFBundleIdentifier_CFBundleInfoDictionaryVersion\CFBundleName_CFBundlePackageType_CFBundleShortVersionString_CFBundleSupportedPlatforms_CFBundleVersionZDTCompiler_DTPlatformBuild^DTPlatformName_DTPlatformVersionZDTSDKBuildYDTSDKNameWDTXcode\DTXcodeBuild_LSRequiresIPhoneOS_MinimumOSVersion_UIApplicationSceneManifest_(UIApplicationSupportsIndirectInputEvents^UIDeviceFamily^UILaunchScreen_UIRequiredDeviceCapabilities_ UISupportedInterfaceOrientations_%UISupportedInterfaceOrientations~ipadU20F71RenXdummyapp_com.sas.dummyappS6.0TAPPLS1.0¡$XiPhoneOSQ1_"com.apple.compilers.llvm.clang.1_0V18E182XiphoneosT14.5\iphoneos14.5T1251V12E507 T14.0Ñ0-_#UIApplicationSupportsMultipleScenes ¢45С8Uarm64£:;<_UIInterfaceOrientationPortrait_#UIInterfaceOrientationLandscapeLeft_$UIInterfaceOrientationLandscapeRight¤:>;<_(UIInterfaceOrientationPortraitUpsideDownAWsˆ½Êàý,7IXlw‰–«¾Û$CfŽ”— ³·¼ÀÂËÍòù !&)OPQTVXY[ae†¬ÓØ?relic-7.6.1/functest/packages/slimfile.app/PkgInfo000066400000000000000000000000101455105530300220670ustar00rootroot00000000000000APPL????relic-7.6.1/functest/packages/slimfile.app/_CodeSignature/000077500000000000000000000000001455105530300235135ustar00rootroot00000000000000relic-7.6.1/functest/packages/slimfile.app/_CodeSignature/CodeResources000066400000000000000000000034611455105530300262070ustar00rootroot00000000000000 files Info.plist sJr7cuUY4P92+wG/iO2GN67gag8= PkgInfo n57qDP4tZfLD1rCS43W0B4LQjzE= rules ^.* ^.*\.lproj/ optional weight 1000 ^.*\.lproj/locversion.plist$ omit weight 1100 ^Base\.lproj/ weight 1010 ^version.plist$ rules2 .*\.dSYM($|/) weight 11 ^(.*/)?\.DS_Store$ omit weight 2000 ^.* ^.*\.lproj/ optional weight 1000 ^.*\.lproj/locversion.plist$ omit weight 1100 ^Base\.lproj/ weight 1010 ^Info\.plist$ omit weight 20 ^PkgInfo$ omit weight 20 ^embedded\.provisionprofile$ weight 20 ^version\.plist$ weight 20 relic-7.6.1/functest/packages/slimfile.app/dummyapp000077500000000000000000003532301455105530300224100ustar00rootroot00000000000000Ïúíþ & … H__PAGEZEROX__TEXT€€ __text__TEXTxvlxv€__stubs__TEXTäzœäz€ __stub_helper__TEXT€{´€{€__swift5_entry__TEXT4|4|__const__TEXT8|L8|__swift5_typeref__TEXT„}o„}__swift5_fieldmd__TEXTô} ô}__swift5_reflstr__TEXT~ ~__swift5_assocty__TEXT ~0 ~__swift5_proto__TEXTP~P~__swift5_types__TEXTX~X~__unwind_info__TEXT`~¨`~__eh_frame__TEXTø8__DATA_CONST€@€@__got__DATA_CONST€X€ __const__DATA_CONSTX€X€__objc_imageinfo__DATA_CONSThh8__DATAÀ@À@__la_symbol_ptr__DATAÀhÀ__data__DATAhÀ8hÀ__bss__DATA À˜__LLVMÀÀ__bundle__LLVMf¥H__LINKEDITÀ€À˜"€0ÀÀ˜¨Æ(ÐÉ0 Ê6΀ P4€Í% /usr/lib/dyldõ‹o_cÑ8>¸á¯È÷3½X2  Š*(€Pw,@@ Xevï,/System/Library/Frameworks/Foundation.framework/Foundation 8ä/usr/lib/libobjc.A.dylib 8d /usr/lib/libSystem.B.dylib P/System/Library/Frameworks/SwiftUI.framework/SwiftUI€@Ö/usr/lib/swift/libswiftCloudKit.dylib @µ/usr/lib/swift/libswiftCore.dylib€@/usr/lib/swift/libswiftCoreData.dylib€H/usr/lib/swift/libswiftCoreFoundation.dylib€H/usr/lib/swift/libswiftCoreGraphics.dylib€@/usr/lib/swift/libswiftCoreImage.dylib€H/usr/lib/swift/libswiftCoreLocation.dylib€@/usr/lib/swift/libswiftDarwin.dylib€@d/usr/lib/swift/libswiftDispatch.dylib€@f/usr/lib/swift/libswiftFoundation.dylib€@/usr/lib/swift/libswiftMetal.dylib€@/usr/lib/swift/libswiftObjectiveC.dylib€@/usr/lib/swift/libswiftQuartzCore.dylib€@j/usr/lib/swift/libswiftUIKit.dylib€P|/usr/lib/swift/libswiftUniformTypeIdentifiers.dylib€8dè/usr/lib/swift/libswiftos.dylib€( @executable_path/Frameworks&Ê ) Ê-/ý{¿©ý‘/”!ý{Á¨À_ÖÿCÑø_©öW©ôO©ý{©ý‘󪩌Ҁ­òàÅòäîòáMŽÒ¬ò!Àò ýò ” €RèyÿùB€Ò€Ò€Ò€Ò ”ôªõªöª÷ª”tV©ÈhB9wù`‚9äO`‚‚<`‚ƒ<(€Rh"9ý{D©ôOC©öWB©ø_A©ÿC‘À_ÖôO¾©ý{©ýC‘ ”᪠N Õôªæ”€Rý{A©ôO¨À_Öý{¿© Õ GX`´ý{Á¨À_Ö, Õ!L Õò”F ÕýŸÈý{Á¨À_Ö H Õ€ÒÀ_Ö¡' Õ"€RÜÿƒÑý{© Õ DX µ D Ք᪠ÕAX ÕÉ?Xè'© Õ @Xâ‘Ò”HB ÕýŸÈý{A©ÿƒ‘À_ÖôO¾©ý{©óª@ù€ø·ý{A©ôO¨À_Ö⃀Ëa ‹à€R€Ò€Ò»”`ùöÿÿE Õ€ÒÀ_Ö!& Õ"€R«À_Öüoº©úg©ø_©öW©ôO©ý{©ýC‘ôªà= ÕR”óª€_ø(#@ùéª Õð:X?Öé‘ =‘Lí|’5 Ë¿‘éª ÕÐ9X?Öé‘6 Ëß‘éª Õð8X?Öé‘7 Ëÿ‘éª Õ8X?Öè‘ Ë‘?”㪠úÿ Õb; Õ誀ÒZ”: @ùàªáªâª@?ÖA”âªèªàªáªR”;@ùàªáª`?Ö(@ùàªáªâª?Öàªáªâª@?Öàªáª`?Öàªáª`?Ö¿CÑý{E©ôOD©öWC©ø_B©úgA©üoƨÀ_ÖÀ_ÖôO¾©ý{©óª@ù€ø·ý{A©ôO¨À_ÖჀË` ‹€Ò€ÒD”`ù÷ÿÿý{¿© Õ€1X`´ý{Á¨À_Ö@ Õ3 Õ=”h0 ÕýŸÈý{Á¨À_Öý{¿© ÕÀ/X`´ý{Á¨À_ÖÀ. Õdÿÿ—áª Õ *X+”h. ÕýŸÈý{Á¨À_Öîÿÿ ÕÐ(XÖ Õ°(XÖ Õ(XÖ Õp(XÖ ÕP(XÖ Õ0(XÖ Õ(XÖ Õð'XÖ ÕÐ'XÖ Õ°'XÖ Õ'XÖ Õp'XÖ ÕP'XÖQ' ÕðG¿© Õ&XÖPùÿÿPöÿÿ6PóÿÿpPðÿÿ¯PíÿÿÒPêÿÿøPçÿÿgPäÿÿËPáÿÿ5PÞÿÿPÛÿÿ¥PØÿÿÐPÕÿÿ ûÿÿdummyappìÿÿÿContentViewQäÿÿÿìÿÿÿ\ûÿÿˆqàÿÿÿeûeûeØùÿÿeÔùÿÿeÐùÿÿeàùÿÿÜCÄxÿÿÿ€€ÖªÌdummyappAppQ@ÿÿÿìÿÿÿ|ûÿÿôàÿÿÿ““hûÿÿÌüÿÿÈCÄäþÿÿ€€BéRl×þÿÿÿHúÿÿyŸzG$s7SwiftUI4ViewPxÿ *úÿÿ?ÿÿÿÿÐúÿÿ1ykG$s7SwiftUI3AppPÿ òüÿÿÿÿÿ ¼ÿÿÿ BodyBodydÿÿÿ€ÿÿÿäÿÿÿ^ÿÿÿˆÿÿÿ ÿÿÿÑÿÿÿ‚ÿÿÿ(þÿÿÈþÿÿþÿÿ¤þÿÿ xv88åz8 H  Ø LpÐ4 ¤à hظT4zRx `øÿÿÿÿÿÿ@Dž8¤øÿÿÿÿÿÿ`H ž$Xäøÿÿÿÿÿÿ@H ž“”zRx $|úÿÿÿÿÿÿ<H ž“”@úÿÿÿÿÿÿ@Dž`°úÿÿÿÿÿÿHDž\|\|}@˜{¤{°{¼{È{Ô{à{ì{ø{|||(|½ÿÿïÿÿÿF½ÿÿôÿÿÿxar!ö™xÚìWKs£8¾Ï¯ |Ç‚_S ³É¤²›šle¦’TÍÍ%Pb%‘ØÙÚÿ¾’ÇÎL–Ìqê××íÖ‡ZÆŸÖEî<€Œ—Ç#ì(NYyw<º½9wç£OѼ&"úà`YÇ”'NóZ–¤€ãÑ%i“ƒsV®\^) $ÂÁ¹]J%X…Q«{nG·RçG”<–±².Y,ˆØ 3x€œWZó]×ZìèQѺ(6¤ªÜ4¡üEîãrU®á® ÷P®U¶Î*‘¡Óšå]” D”r\rVRX£‘dnǸ…Õ1R‘<'¦F‹òUpZ'J^òÄ*ÑIUå¬Y÷qcÛ ¯ kHjEâ–QÙA§ß^8¡s¡»ýÈÅJ¯B- ÂÊW*9¨öÃñä5ƒwÐÀø+UUº)Ų¥Ø²S F/ù„é&gqK-½Šþ¾>ûòõäæP-…v‘|d©2+»àrlC Tüö°Û’™Šo6\P(K™®}ÔŦE~«‰POŸ¹€AáWñ=$Jsûó ð?A3Pä9¯Kj¿ˆAágLj'Ù°`"Ù°¼¦ÑÝg=ࢠw08úwAªŒ%r0À;[o ̹:8xX`Îkúóäz#۱П…èºùÎÆi§ê4?-D? ìøô¥ë‡Ç÷ÉødpµÏö¨/¸Wn1ê;lå¶h4q¸pô© £]‹õ¥«NlNñg cïÎãˆ]]c´•Œ)c\¹)bžËÈÇhG6ÄŒK¥Ï•Z@DD1 1Úѧmv3)ú̺{g0Ke_Nô°7¿ÚU¬€(ðßõf®7½ñ'ƒðc0Ãh×Åe¬d]8Rmr}ûñGí4IS *2ªYY­dO\`¶·†•R–ƒÃèñ(halSíRg+hÔÏr#õz·Ð×\s Ú³Q&Íèvóü¡0Cì©”{>W{bÚ9 ÝZ‚«b¶gÿnà^jíN¸º@NÏáfF¿TDÜÚSCÁ”Ëí¬èm-­šµéŽ«ô(ŒN™2W0Œz•õÐÌ%-fåÊ"áM5•©1uwÊnÛHuBÀ=¼¬9ÀC„ƒ<´ƒ=à¬;”Ã;¸¬Á< C8Ȭ>ŒC;°ÁèÃ8è@Ôƒ;ÌC8˜C9´9ÀC´C8Ð:æì0åõ0åÐóðæ@m`ìðá@€9„;ÌC9çÿÿÿÿ  èƒ |°ˆ`Ù@HàHfúÿÿÿÿЇwy(‡q vȇ6‡w¨w ‡r ‡6 ‡t°‡t ‡rhƒyˆy ‡60xhƒvz@ÀÂæ¡Âä!Ú¡ÚÞ!ÜÊAwxwhv(‡p0€`‡r˜‡yhx‡r‡t˜‡rhs€‡vrÌ!ØaÊ ÜáÚ ÜÁæ¡ ÌÚ ÂÐ0‡p`‡y(€p‡whww˜‡60xhƒvz@ÀÂæ¡ÂÞ¡ æ!ÎÁÊÚ@ÊAÞaÚÀà¡ Ú!èsv˜‡rwx‡6 yx€‡tp‡shƒvz@€ä¡Ê àAÞAÊ¡ æÂaÖø…_tX‡y ‡uøry¸‡tp€˜z‡qX‡6€yxz(‡q ‡w‡6‡z0s(yhƒyH}(‚ÂAΡè¡ Æêx€v(‡6‡;zy8‡r ‡60‡rz¨y(‡yÖ ÊaØ` ÆAòèáØ` ÌÚ ä¡ìØ` Ü¡ÞÁØ` æÂAØ` ôaÚ Ö@ÆA¢ÜaÂÁÊ¡ ÌÚ ÂÐ0‡p`‡y(€¨‡y(‡6˜‡w0zhs`‡wzÌ!ØaÊØ`P°Ô£úÿÿÿÿh@Ú Xÿÿÿÿ?mH@t°Á¸`è`l ²ÿÿÿÿg¡ ÀlP¶8ƒ @‚4ƒ ÷ÿÿÿÿ Ô¢ €3Ø`xp€l ¾ÿÿÿÿipIŠ@ˆbÂÇ„! BLeB°L˜ A3!p& M‚ C`Š@i‚0Q‚jÂ`]Ø„"Bš0dF1AÈ´ 6!È&Û„€› tÞ„áë´ ×M¾ ªp‡v°:hƒp€x`‡rhƒtx‡yˆ:p8p8ØåÐí ïÐé`t v@mq x q xÐî0r v@m0 q x°Â ~áh¡pØ ~áæáÒÁè¡~¡Üä!Ø ä¡ΡØ!ä ÀÀÞáÈ¡Â~aèAÒV8¿ð ´P ¸@ lð ¿0ïàó@+Þà~Jh ~áæáÒÁè¡~òÊAÊÁØ ä¡ΡØ!ä ÀÀÞáÈ¡Â~aèAÒV¼Á/ü-”.ÐüÂ/ÌÃ=¤ƒ9ÐCü‚9¤C9°9´9°ÈC9œC=°C8È€;¼Ã/C9„9üÂ<Ѓ<¤<¬xƒ_øZ(\ 6ø…_˜‡{Hs ‡:øy(s`‡y y`0‡r8‡z`‡p6wx‡_ ‡rrø…y yHxXñ¿ð ´P ¸@ lð ¿0÷æ@uð á0óðã@ùÀ` åpõÀá lîð¿@åäð ó@òð°Â ~áh¡pØ ~áæáÒÁè¡~äáèáØ ä¡ΡØ!ä ÀÀÞáÈ¡Â~aèAÒV¸Á/ü-”.ЀÁ/üÂ<ÜC:˜=ÔÁ/ÐC>ÀC9Ì€<”Ã9Ô;„ƒ<°¸Ã;ü9”C8Ã/Ì=ÈC:ÀʆØÁÚÁ Ú¡è!È!è!”"$‡ )2„h„0,@†€Â‰i a& Èf4  vHfà(<$C@;$:hœa" È€R(4ÎÀ dÀÉgh…2`‡´ 3¸B°CB‡Æ^!€‚ Ø!ÕCã °@AìD¢q†Xˆ € vH/Ñ8ƒ,D@;$žhœa" È€RZ4Î@ dÀÉ.g¨…2`‡43ØB°C‚Æn!€‚ Ø!õFã ¸@AìÔ£q†\ˆ € x`@@Ø!e4ÄC°CêFòa„‚ Ø!e5èC°CR‘Æf„‚ Ø!ÝHƒ 51@AìȤцž˜„!€ vHqÒtÃXL@;$?i¾a-&a È. x„ vHéâˆÁÐ%@¼ ìRƃáK€ x9Ð0Ø!Ñ#C$@;$ÑiÊ`ø‹I2`‡ô:  ¡1 CAì2@ ÑH€€ vH¼Ó°ÁpÓ`dÓ"\;$ýqÞ`8ƒ 2€ˆ1ƒR9r0¤AÀY‚‹˜€ÁI•9Ö ‚ 6ÁMÀ`‡4WN mA0¸ Œ&`°C3§†7H€ kD0Ø!ÅÛSC$@<¡ ìÀa€ È€ÒÛ5w àÆ°Câ»&=!€ vH©×ìÁÀ'“0dÀÉöšBX‡‰@2€ãAƒM¡2@A0aƒ €@f„‚ÂiƒæPe†‚ Ø!¡dÐüÁ@.À dÀ©&ƒ'tI†€ vHd<¤0´K2 °CšË áw™ € œpJF ‡T©0«SCªÚ ˜U€€)€€È€!UxŒ+@À$@@dÀª=F ` 2`H•ŸA 0¤êÏ`³( € RáhÀàL€@ ©È4¨t€@ †TyŒAC*^ ¾ †Tûl @ ©l6xƒ †T¥ŒAC*ô Π‚È€!Õ aÐÁdÀ Žƒ2h€à2`HUË×ÁdÀjž1h€à2`HUÓ×ÁdÀj®ƒ1h€à2`HeÛA4@p0¤ºï  8€ RáxP @ ©ú<`z †T©0¿SCªZ¼p€àÈ€!Õ¼Ø8 dÀjåk€P2`HöÁ¤ €@ ©p?8v€`È€!Õ ž;@°dÀªQ€(€ Rý¤ ò@ CªxA h2`HÕ«‚C80¤ZWÁ˜ € RÝ«ÀØL€@ ©FV|€àÈ€!UÝ ”>dÀœ&u…&L2@bƒ@a—€,¸2˜$LŒ &GÆC* Ë*#Ë…Q†`Ø ÚH7±È@1 <Œ€eñ uHSuÄÃ1`ÉG7ÊÈC=ЀIà'΢؅sš‰h¦!,:(Ѱœ¨ ƒ ¾¡뺎x8ø,ùúF‚}ð:0ø#Ë â갮눇cÀ’O0`”!‰‘ Â°ôÊ¢ƒ2 ¾¡뺎x8N‚,ùF“H :0P K :(â갮눇£%À’Ï1`”!`‰— ȲÀC B‹"Å™Cˆ@8L˜tJ€,/.ˆo¨CÛ²#~ K¾Ë€Q†À&p‚ 2h`€4p¨¤iXndŒ2=Át`à@#…AX|Š*T¨r K0-âêøÀ`;âᘰä3 eÊâ,èÀ5€>E*Õ&(°é ¾¡Ž2018âáà°äk eØÂ-èÀà5€H2È#Ë‘âê(3ƒ#Ž– K¾Ù€Q†@.è‚ jÀr$„Šø†:ÊÀ Äàˆ‡/À’/7`”!¸ ½ €Ý"ÉàˆJ0Y+Ö*[«xõÚD' ‘ÌÝLÝ¥™ÑU%ÑX¥•ÝA ¿6‘Ê›3``ÙR2Êô„hÐÀpX¦ŒßP´AñpðXòŒ2¥qt`€P`¹BB|ClÐepÄÃÑ`ÉÇ0ʬFkЀsXšˆÐA)ßP´AñpÄXòIŒ2°1t`@@,hKÆGÙ³jt3@–0'–:5£ nähŒ2»ÁÄ7Ô‘zPG<ü@–|Ý£ Mø| XΤßPGèAñpÄXòŒ2âAt`P0ʘ?ÁPÇÛG<@ ÁPÅ@ `I @|Cy upÄÃ`ÉÇ0ʬG{Ѐ{XάÐA9ßPGèAñpÄXòÉŒ2ð1t`@0ÊÔ?ÁPG˜ñpÄÃPÅ@Ü`I€@|Cy upÄá`ɧ0ÊäÇ~ÐXÚ´ÐAIßPGèAñpüXò Œ2þ"t` (0ÊŒ?€%“ñ uäÔÁ‡‰€%ß¡À(CP"(B‰`ióBeA|Cy upÄÃÁ"`É×(0ʬˆ‹ÐÀ£À(#ü@–LCtPÄ7Ô‘zPG<3B–|”£ ŒÔX €¥M ”ñ uäÔÁGŽ€%Ÿ¦À(C€#;Bœ£ @ðX>ÑAqßPGèAñp€ Xò… Œ2?"&t`0*@–63tPÄ7Ô‘zPG<fB–|§£ A™  ¤ Œ2jÂ`9UDB|Cy upÄÃÑ&`Éç*0ʰɛЬq”XâÐÐA¡ßPGèAñpÐ XòÕ Œ2sb't`p+@9Xq'®sïìõ#¨0âE$WÆ%3N"ÉvPй‰,}rFØà:0è¨êAÁçO( °¬##¾¡Ž øàˆ‡cÀ’oX`”!R¡€bú@bTP£›è !ˆo¨ã@¡Žx8ø,ù’F‚}P:0X Ž"   ƒ KâêøPèƒ#!K¾hQ†Vd… ¦ˆo¨ã@¡Žx8f„,ùªFܰ:0¸ep…ˆo¨ã@¡Žx8r„,ù´FØØ:0à KâêøPèƒ#0!K¾oQ†ÀWÀ… Âè¤LeÊl Êé¦M›žRÍÚU,X ùŒ2¹A.t`P.@A˜%Ë¢ŸQ†À&Ð… ÒhµD„ÁZâ("°]°ò±-ú±m)±õа5²}ˆ°}€°}H°}X°}h°}°}(°}P°}@°}p°} °}x°}`°}8°}0°­2¢#´Ü¾…Wî\ºuíÞÅ›Wï^¾}ýþXð`êêÀ2숇ƒHÀ¶¯p€þÆœ‚ 0€J¡èl è @? @?±3€Äáf=ˆC8„ÃŒB€yxs˜q æíô€3 BÂÁΡf0=ˆC8„ƒÌ=ÈC=Œ=ÌxŒtp{yH‡ppzpvx‡p ‡Ììá0n0ãððP3ÄÞ!Ø!Âaf0‰;¼ƒ;ÐC9´<¼ƒ<„;Ìðv`{h7h‡rh7€‡p‡p`v(vøvx‡w€‡_‡q‡r˜‡y˜,îðîàõÀì0bÈ¡ä¡Ì¡ä¡ÜaÊ!ÄÊaÖC9ÈC9˜C9ÈC9¸Ã8”C8ˆ;”Ã/¼ƒ<ü‚;Ô;°Ã Çi‡pX‡rpƒthx`‡t‡t ‡ÎSîòPäã@á ìP3 (ÜÁÂAÒ!ÜÜàäáêfQ8°C:œƒ;ÌP$v`{h7`‡wxx˜QLôðP3jÊaè!ÞÁ~ä¡Ì!ðaT…ƒ8ÌÃ;°C=ÐC9üÂ<äC;ˆÃ;°ÃŒÅ ‡y˜‡w‡tz(r˜\ãìÀåPó0#ÁÒAäáØáÞfH;°ƒ=´ƒ„Ã8ŒC9ÌÃ<¸Á9ÈÃ;Ô<ÌH´qv`q‡qX‡ÛÆì`íàð å0å öPnã0å0óàéàäPø0CáÚ!æ¡ðÊaèaFÔÙÃ8„;°Ã/ØC:ÌC:ˆC:°C:ÐC>ÌX¼ppwxzzH‡wp‡·åðìÀï0óôPy árH Cˆ  r2H #Œ‘‘ÑD (d<12BŽ!£ t{Ëäymàq¤$M4M Ã0 Ã@Ê-MÒY‘R9Ï$ÃPÂI‚‘‚DLõ 21SDK VersionObjective-C VersionObjective-C Image Info VersionObjective-C Image Info Section__DATA,__objc_imageinfo,regular,no_dead_stripObjective-C Garbage CollectionObjective-C Class PropertiesDwarf VersionDebug Info Versionwchar_sizePIC LevelSwift VersionSwift ABI VersionSwift Major VersionSwift Minor Version__hidden#51___hidden#49___hidden#50___hidden#46___hidden#47___hidden#48___hidden#45_standard-library-frameworkSwiftUI-lswiftObjectiveC-lswiftCore-lswiftDarwin-lswiftos-lswiftUniformTypeIdentifiersUniformTypeIdentifiers-lswiftFoundationFoundation-lswiftCoreFoundationCoreFoundation-lswiftDispatchCombineSecurityCFNetwork-lswiftCoreGraphicsCoreGraphics-lswiftUIKitUIKit-lswiftCoreImageCoreImage-lswiftMetalMetalIOSurfaceCoreVideoOpenGLESImageIO-lswiftQuartzCoreQuartzCoreCoreTextUserNotificationsFileProviderDeveloperToolsSupport-lswiftCoreDataCoreData-lswiftCloudKitCloudKit-lswiftCoreLocationCoreLocation-lobjc__hidden#53_F0‚pÌÃÂ?œÌÂÁ#G<Œ ;1‚p ÌÂ#GKŒ '1‚0¤Ì 2#ç°2#S5‚p,3‚p]#[6‚ài#`° ˜Œ ¤ÁŒ ¬Œ ´AŒ èAŒ ðŒ €Œ €BŒ ˆ‚Œ ” ež;HP5”@ï`€€LÔ ƒ(£0Ã@ ‚(Ì0ÂP 3 ¤@3 ¤`˜Â )§0À H*Ì0ˆB¢ 3 ¤ ¤Â *,¢0Ã@ *Ì0Bƒ 3 ¤à¬Â )<¬0P@ TU3W+Ì0¸B+¼Â ,´Â+Ì0ÄB+¼Â ƒ,´Â+Ì0ÌB+¼Â -´Â+Ì0ÔB+¼Â ƒ-´Â+Ì0ÜB+¼Â .´Â+Ì0äB+¼Â ƒ.´Â+Ì0ìB+¼Â /´Â+Ì0ôB+¼Â B+¼Â –Íh3Û 7CÐÍx3Ø7C3ẌÁ 63d0ƒ€•Á f3ØÌ Á –3j0ƒ€­Á Ì `m0Cà3ØÌ `p0ƒ€ÅÁ &3ØÌÐÁ V3˜Ì `w0ƒ€áÁ –3z0ƒ€íÁ Ì `}0Cà3ØÌ€Â@ ÌÌD€Z§€ ˆˆˆˆ ‡lj     N脸 ¸¸¸¸¸¸ÐÐÐÐÐÐÐÐp–eYlj'œˆpbÀ‰'œˆˆpb b bÀ‰'œptÉH`‚2bc³ksi{#«c+s1c ;›Åùpqr(s8tHuXvȅͮͅLìÌe¬n¡|!¥¹»43:—¶7²:¶23¶°³¹Q‚wȈͮÍ%l.ÌÍåŒíM,Œmn”â!äaè¡ìáð!ôaø¡üárbc³ksaKs[+“syƒ£K{s› @"$Db$H¢$Lâ$P"%Tb%X¢%\â%`"&db&h¢&lâ&p"'tb'x¢'|â'À",Äb,È¢,Ìâ,‘ Œ¢  °© r(‡w€zXp˜C=¸Ã8°C9ÐÂæÆ¡ èAÂÁæ!è!ÞÁ4ã`çPá ä@á çPô°€y(‡p`vx‡qz(rXpœÃ8´;¤ƒ=”ÂèA¡èÑÌ<¤ƒ;œ;”= ƒ<”C8Ãa A< "GÈQ __hidden#65_«¤#-ØH `-_#J†` rb„ 3´è„[€-a A< "GÈQ __hidden#64_«¤#-ØH `-_#J†` sb„ 3´è„[€-a A< "GÈQ __hidden#63_«¤#-ØH `-_#ˆ†`rBÁ0cP@[ÀhBL€ˆN³ CLL1[€-a ŽA, P•ãÆí›– î5jË™;}ÿÿ_Bf¦”ÄQ0P@ñ0"GÈQ  __hidden#61___hidden#62_«¤#-ØH `-_VI‡;°àËZ,¾¬’Ž´h -€µ|Ðy !2d$Ô’Ñ€j*Ü".3ÃÁ¡ ãŽS‚A:ô bÌŒóqŒ&À„0š Àh L£ Ä0aŒ‚Á8Ê]3“¼E\y4î„0l@UL„ „  Ž˜00h.`Â10(ƒq8•½X& €§ C0`‡RA£hš¦i‚ `4!& €Ñ!˜0FaÂM † àÑ `¸!x‘0˜0F ˆ* ÁÀVeÆ ¸ ;Ç1(œâ-âHïq' €ü"wÂH0èÛ `Ø€˜0:ä dt2a ÁB@­N& € wÂ6 ‚„& €^h¨ˆŠ¨ˆÊ„0l@ L}ÑÐ:<•Q• `Ø€˜0úb±yy"*y2adи@_4:0ØR10˜0F  Á@`%è0hƒ `؀؀& 1(À- [ -a *A< "GÈQ  __hidden#59___hidden#60_«¤ÃØp‡`-_VIGZ´ÀZ¾è<@†2 h #Ä‚`€ ©2cP(Á[À0bp`a¶°*2ÈÌ„°a2cP€[@@ [ -a )C,d0ñ0 "GÈQ __hidden#58_«¤#-¶H `-@Áô2dÆ €¶€á†àLÀ`˜e„`Â18²]H˜‘™a˜60d& "˜0f „ ` ¢0!˜06˜0b[€-a A,0@\s&3ñ0 "GÈQ __hidden#57_«¤#-¶H `-@ÁÌÐ[€-!1 †ÈÂpa &A,D0@"¶@=ñ0 "GÈQ __hidden#56_«¤#-¶H `-@Á#dž`y"k1cP@[À@@ [€-!1 †àƒ qÆ<, a A< "GÈQ __hidden#55_«¤#-¶H `-@Á#Ä‚`€ k2cP@[À@@ [€-a SC,0ñ0 "GÈQ __hidden#54_«¤#-¶H `-@Á35qð)®ƒÚ†4ƒ `–!‚ `Ä ÐB ì¡MteÂè•QÀ°ì L½r( ¶˜0† ˆ`W`Â186]h_„ `ÚÀ—€ˆ`˜%& €Š‚„`ÂØ`ˆ[€-!1  )Œ#Qvb!^,° ¢0$Q¦qÔ` (’&ª².,ä Óæ€6®ó>016h2pƒ20ƒ3@ƒ7€ƒ4ˆ‡Æ@  2 ¢0J"i¢*K¹ž…i~a GC,0P@å?@"#ñ0"GÈQÄŒ“__hidden#52_branch_weightsmisexpect„£e&@zL—Y%i°EZk ¾lÚ‚ÞbáppYôíŒAr“F€Là À,ƒÀ@@•0ad  ÀÀHN¨!0aœ¡Ü„€Á’@:9aœÀ„N#Ɇ`€yâFc'@9aLN@B0aÌ@@ [ .¶ƒ Ü\q 2"„Bî8”hD¤XDlFÔˆˆY«¨ÕŒdõX™fåe û”à/Ö LXXjH Ü à jñ É ÿÿÿÿ$ü Õ ÿÿÿÿ$ á ÿÿÿÿ$ í ÿÿÿÿ$ ùÿÿÿÿ€$" ýÿÿÿÿ$>  ÿÿÿÿ&I $ÿÿÿÿ$_ $9#ÿÿÿÿ$ƒ \ÿÿÿÿ$£ {ÿÿÿÿ$¿ 0–/ÿÿÿÿ$ï $Å#ÿÿÿÿ$ (è'ÿÿÿÿ$; "!ÿÿÿÿ$] &0%ÿÿÿÿ$ƒ Uÿÿÿÿ$¢ #s"ÿÿÿÿ$Å •ÿÿÿÿ$ä $³#ÿÿÿÿ$ "Ö!ÿÿÿÿ$* "÷!ÿÿÿÿ$L &%ÿÿÿÿ$r = ÿÿÿÿ$} I ÿÿÿÿ$ˆ Uÿÿÿÿ$§ sÿÿÿÿ,À ‹ÿÿÿÿ,× 8¡7ÿÿÿÿ$ hØgÿÿÿÿ$w ?ÿÿÿÿ$– C]Bÿÿÿÿ$Ù cŸbÿÿÿÿ$<] \ÿÿÿÿ$™] ÿÿÿÿ(© k ÿÿÿÿ&´ w ÿÿÿÿ&¿3ƒ 2ÿÿÿÿ$òµ ÿÿÿÿ, ÿÿÿÿ ÿÿÿÿ  ÿÿÿÿ*&ÿÿÿÿ„h 4 ÿÿÿÿ‘t A ÿÿÿÿ‘€ N ÿÿÿÿ‘Œ [ ÿÿÿÿ‘˜ h ÿÿÿÿ‘¤ u ÿÿÿÿ‘° ‚ ÿÿÿÿ‘¼ ÿÿÿÿ‘È œ ÿÿÿÿ‘Ô © ÿÿÿÿ‘à ¶ ÿÿÿÿ‘ì à ÿÿÿÿ‘ø Ð ÿÿÿÿ‘ Ý ÿÿÿÿ‘ ê ÿÿÿÿ‘÷ÿÿÿÿ# ÿÿÿÿ€0 ý ÿÿÿÿJ ÿÿÿÿ€W  ÿÿÿÿc  ÿÿÿÿo $ ÿÿÿÿª 1 ÿÿÿÿ•å > ÿÿÿÿñKÿÿÿÿiÿÿÿÿ wÿÿÿÿ3‰ÿÿÿÿC—ÿÿÿÿa´ÿÿÿÿq  ÿÿÿÿ} Ï ÿÿÿÿ‰ ÿÿÿÿ„Å Ü ÿÿÿÿ•éÿÿÿÿúÿÿÿÿ"ÿÿÿÿ@%ÿÿÿÿP3ÿÿÿÿhJÿÿÿÿx\X[ÿÿÿÿÔ³ÿÿÿÿäbÁaÿÿÿÿF"ÿÿÿÿVB0Aÿÿÿÿ˜qÿÿÿÿ©!€ ÿÿÿÿÊ ÿÿÿÿÛ ¯ ÿÿÿÿç ¼ ÿÿÿÿó É ÿÿÿÿÿ Ö ÿÿÿÿ BãAÿÿÿÿM $ ÿÿÿÿY1ÿÿÿÿu0L/ÿÿÿÿ¥ { ÿÿÿÿ± ˆ ÿÿÿÿ½•ÿÿÿÿ„ü¤ÿÿÿÿ„; ³ ÿÿÿÿ‘G À ÿÿÿÿ ^ É ÿÿÿÿA:.<<{/¶/{/{/{/–/Ñ/<{/{/<Î. .Q ] ^”êZ__hidden#9___hidden#10___hidden#11___ir_hidden#1___hidden#12___hidden#13___hidden#14___hidden#15___hidden#16___hidden#17___hidden#18___hidden#19___hidden#20___hidden#21___hidden#22___hidden#23___hidden#24___hidden#25___hidden#26_$sytWV__hidden#27___hidden#28___hidden#29___hidden#30___hidden#31___hidden#32_$s7SwiftUI15ModifiedContentVMn__ir_hidden#2_$s7SwiftUI4TextVMn__ir_hidden#3_$s7SwiftUI14_PaddingLayoutVMn__ir_hidden#4___hidden#33___hidden#34___hidden#35_$s7SwiftUI4ViewMp__ir_hidden#5_$s7SwiftUI4ViewP4BodyAC_AaBTn__ir_hidden#6_$s4Body7SwiftUI4ViewPTl__ir_hidden#7_$s7SwiftUI4ViewP05_makeC04view6inputsAA01_C7OutputsVAA11_GraphValueVyxG_AA01_C6InputsVtFZTq__ir_hidden#8_$s7SwiftUI4ViewP05_makeC4List4view6inputsAA01_cE7OutputsVAA11_GraphValueVyxG_AA01_cE6InputsVtFZTq__ir_hidden#9_$s7SwiftUI4ViewP14_viewListCount6inputsSiSgAA01_ceF6InputsV_tFZTq__ir_hidden#10_$s7SwiftUI4ViewP4body4BodyQzvgTq__ir_hidden#11___hidden#36___hidden#37___hidden#38___hidden#39_$s7SwiftUI15ModifiedContentVyxq_GAA4ViewA2aERzAA0E8ModifierR_rlMc__hidden#40_$s7SwiftUI4TextVAA4ViewAAWP$s7SwiftUI14_PaddingLayoutVAA12ViewModifierAAWP__hidden#41___hidden#42___ir_hidden#12___ir_hidden#13___hidden#43_llvm.used__hidden#0___hidden#1___hidden#2___hidden#3_main$s7SwiftUI3AppPAAE4mainyyFZ__hidden#4_swift_getWitnessTable_swift_FORCE_LOAD_$_swiftObjectiveC_swift_FORCE_LOAD_$_swiftDarwin_swift_FORCE_LOAD_$_swiftos_swift_FORCE_LOAD_$_swiftUniformTypeIdentifiers_swift_FORCE_LOAD_$_swiftFoundation_swift_FORCE_LOAD_$_swiftCoreFoundation_swift_FORCE_LOAD_$_swiftDispatch_swift_FORCE_LOAD_$_swiftCoreGraphics_swift_FORCE_LOAD_$_swiftUIKit_swift_FORCE_LOAD_$_swiftCoreImage_swift_FORCE_LOAD_$_swiftMetal_swift_FORCE_LOAD_$_swiftQuartzCore_swift_FORCE_LOAD_$_swiftCoreData_swift_FORCE_LOAD_$_swiftCloudKit_swift_FORCE_LOAD_$_swiftCoreLocation__hidden#5___hidden#6_swift_getOpaqueTypeConformancellvm.lifetime.start.p0i8llvm.lifetime.end.p0i8$s7SwiftUI18LocalizedStringKeyV13stringLiteralACSS_tcfC$s7SwiftUI4TextV_9tableName6bundle7commentAcA18LocalizedStringKeyV_SSSgSo8NSBundleCSgs06StaticI0VSgtcfC$s7SwiftUI4EdgeO3SetV3allAEvgZ$s7SwiftUI4ViewPAAE14_viewListCount6inputsSiSgAA01_ceF6InputsV_tFZ$s7SwiftUI4ViewPAAE05_makeC4List4view6inputsAA01_cE7OutputsVAA11_GraphValueVyxG_AA01_cE6InputsVtFZ$s7SwiftUI4ViewPAAE05_makeC04view6inputsAA01_C7OutputsVAA11_GraphValueVyxG_AA01_C6InputsVtFZ__ir_hidden#0___hidden#7___hidden#8_swift_getTypeByMangledNameInContextInMetadataStatellvm.memset.p0i8.i64__hidden#44_12.0.5arm64-apple-ios14.0.0__hidden#0___hidden#1___hidden#2___hidden#3__main_$s7SwiftUI3AppPAAE4mainyyFZ__hidden#4__swift_getWitnessTable__swift_FORCE_LOAD_$_swiftObjectiveC__swift_FORCE_LOAD_$_swiftDarwin__swift_FORCE_LOAD_$_swiftos__swift_FORCE_LOAD_$_swiftUniformTypeIdentifiers__swift_FORCE_LOAD_$_swiftFoundation__swift_FORCE_LOAD_$_swiftCoreFoundation__swift_FORCE_LOAD_$_swiftDispatch__swift_FORCE_LOAD_$_swiftCoreGraphics__swift_FORCE_LOAD_$_swiftUIKit__swift_FORCE_LOAD_$_swiftCoreImage__swift_FORCE_LOAD_$_swiftMetal__swift_FORCE_LOAD_$_swiftQuartzCore__swift_FORCE_LOAD_$_swiftCoreData__swift_FORCE_LOAD_$_swiftCloudKit__swift_FORCE_LOAD_$_swiftCoreLocation__hidden#5___hidden#6__swift_getOpaqueTypeConformance_llvm.lifetime.start.p0i8_llvm.lifetime.end.p0i8_$s7SwiftUI18LocalizedStringKeyV13stringLiteralACSS_tcfC_$s7SwiftUI4TextV_9tableName6bundle7commentAcA18LocalizedStringKeyV_SSSgSo8NSBundleCSgs06StaticI0VSgtcfC_$s7SwiftUI4EdgeO3SetV3allAEvgZ_$s7SwiftUI4ViewPAAE14_viewListCount6inputsSiSgAA01_ceF6InputsV_tFZ_$s7SwiftUI4ViewPAAE05_makeC4List4view6inputsAA01_cE7OutputsVAA11_GraphValueVyxG_AA01_cE6InputsVtFZ_$s7SwiftUI4ViewPAAE05_makeC04view6inputsAA01_C7OutputsVAA11_GraphValueVyxG_AA01_C6InputsVtFZL___ir_hidden#0___hidden#7___hidden#8__swift_getTypeByMangledNameInContextInMetadataState_llvm.memset.p0i8.i64__hidden#9___hidden#10___hidden#11_L___ir_hidden#1___TEXT, __swift5_entry, regular, no_dead_strip__hidden#12___hidden#13___hidden#14___hidden#15___hidden#16___hidden#17___hidden#18___hidden#19___hidden#20___hidden#21___hidden#22___hidden#23___hidden#24___hidden#25___hidden#26__$sytWVL___unnamed_1__hidden#27___TEXT,__constL___unnamed_2__hidden#28___hidden#29___hidden#30___TEXT,__swift5_typeref, regular, no_dead_strip__hidden#31___TEXT,__swift5_fieldmd, regular, no_dead_strip__hidden#32__$s7SwiftUI15ModifiedContentVMnL___ir_hidden#2__$s7SwiftUI4TextVMnL___ir_hidden#3__$s7SwiftUI14_PaddingLayoutVMnL___ir_hidden#4___hidden#33___hidden#34_L___unnamed_3__TEXT,__swift5_reflstr, regular, no_dead_strip__hidden#35___TEXT,__swift5_assocty, regular, no_dead_strip_$s7SwiftUI4ViewMpL___ir_hidden#5__$s7SwiftUI4ViewP4BodyAC_AaBTnL___ir_hidden#6__$s4Body7SwiftUI4ViewPTlL___ir_hidden#7__$s7SwiftUI4ViewP05_makeC04view6inputsAA01_C7OutputsVAA11_GraphValueVyxG_AA01_C6InputsVtFZTqL___ir_hidden#8__$s7SwiftUI4ViewP05_makeC4List4view6inputsAA01_cE7OutputsVAA11_GraphValueVyxG_AA01_cE6InputsVtFZTqL___ir_hidden#9__$s7SwiftUI4ViewP14_viewListCount6inputsSiSgAA01_ceF6InputsV_tFZTqL___ir_hidden#10__$s7SwiftUI4ViewP4body4BodyQzvgTqL___ir_hidden#11___hidden#36___hidden#37___hidden#38___hidden#39__$s7SwiftUI15ModifiedContentVyxq_GAA4ViewA2aERzAA0E8ModifierR_rlMc__hidden#40__$s7SwiftUI4TextVAA4ViewAAWP_$s7SwiftUI14_PaddingLayoutVAA12ViewModifierAAWP__hidden#41___hidden#42_L___ir_hidden#12___TEXT, __swift5_proto, regular, no_dead_stripL___ir_hidden#13___TEXT, __swift5_types, regular, no_dead_strip__hidden#43__llvm.usedllvm.metadata__hidden#44_ÞÀ ¨JÿÿÿÿBCÀÞ5b 0$––¦¥÷×mÓ¹Oû¶í×õO Q€L!  !#‘AÈI29’„ %‹b€EB’ Bä28K 2rˆHpÄ!#D‡ŒA’dȱ CFˆ É2r„*(*1|°\‘ Çȉ  2"È d…“#¤„“#ã„¡LŽŒ „äLàÃ@  ÁB*ds``‡D"AŠd ÉD"‘H$È1G v0@±!A ÉdŠD¢RIÅB‚@˜™ž_ø…t‡_@‡t r(wƒ8°ƒ_#HC› ؃èyò`BY@ ‘1‰D"‘ ÒLÏ/üB:ÈÃ/ C:9”ƒ;ŒAÜÁ/ §P( …B!‘H$ :j¦ç~!äáÐ!ÈÊÁÆ ðàHŠV3=¿ð é ¿€é@äPî0q¿€„D‚ZäJˆÁL”H$‰D"‘H$‰J%‘ ™ ¢ÍôüÂ/¤ƒ<ü:¤9C9¸ÃÄü d#ÃLÏ/üB:ÈÃ/ C:9”ƒ;ŒAÔÁ/ "‘H$(g…vöI$*•J…@ ‰D"A½ HÐo¦ç~!äáÐ!ÈÊÁÆ@ààPÈ€ ©’*ÎôüÂ/¤ƒ<ü:¤9C9¸ÃÌün"!„,„áv3²†ÂBÌ™ž_ø…t‡_@‡t r(w9ƒ_#ÈYÀ“ 3=¿ð é ¿€é@äPî0r¿0Ft¦ç~!äáÐ!ÈÊÁÆ€èàÆ¢–0­Þ d- z»[„[´¥¸ qË€È[ À´0½\‰ aÐÝpD.`:½È\Œ¤Ù]CÔ” È@@ $æ‚BL§ÓÅI^ÀôFtªOQÞH&øÿÿÿÿp8$€"ÆáäAÊaèò¡ äáêÁȡȡ È!ì!È¡Ú`âAè¡ ÌÚ ÂÐ0‡p`‡y(€0y‡v(‡6€‡wHw ‡rÀÞÁÊ Ø¡æaÚ䡯!æ¡ÚÀà¡Âsv˜‡rwx‡6Hw0‡yhs€‡6h‡p tÌ!ØaÊ ÜáÚÀÂÁæ¡ ÌÚ ÂÐ0‡p`‡y(€p‡whƒyH‡sp‡r ‡6Їr‡w˜‡60xhƒvz@ÀÂæ¡ÂÞ¡ èAÂà!ÜáÚ ÂРy¨‡rx‡w‡rhƒy ‡p‡u~áÆÖaèa~ÂAî!Ü æÂaÖ¡ àAÞÊaèáä¡ Ä¡ÌÁÊAÚ`ÒAÊÀ€ ‡p‡s(zhƒq€‡zÂàÊ¡ Âá‚ÂAΡè¡ Ì¡ÂêAÊa€5‡r˜6Xƒq‡|€zx6Xs€‡6yh{À6Xw(‡wp6Xƒy@‡p6X}‡v`ƒ5ЇqЀ¨w˜‡p0‡rhs€‡6h‡p tÌ!ØaÊ êaÊ¡ æáÌÚÀØáÂsv˜‡r6ˆŒðÿÿÿÿ ãðò å0ôÀùÐòðõàäPäÐäöäPm0ñ ôÐæmÐá@è˜C8°Ã<”@˜ƒ<„C;”CÀÃ;¤ƒ;ÐC9ÈàïàmÀåæìPó0mòPãóPm`ðÐá@€9„;ÌC9„;¼C¤ƒ;˜Ã<´9ÀC´C8Ð:æì0åîðmàáàóÐæmÐá@è˜C8°Ã<”@¸Ã;´Á<¤Ã9¸C9CèC9ÈÃ;ÌC˜<´A;„= `áÀóPáïÐô áðîpmÐá@èЃ<ÔC9<ÈÃ;ˆC9´Á<ÐC8ŒÃ:¿ð ã€ë0ô°¿@á ÷îó@á0ëÐð ï@å0ôðòÐâPæ`å m0é åà@ÐC8ÈÃ9”=´Á8ÀC=áðÀåÐápAá çPôÐæPá@õ å0À„C9̬Á8ÈC>À=¼¬9ÀC„ƒ<´ƒ=à¬;”Ã;¸¬Á< C8Ȭ>ŒC;°ÁèÃ8è@Ôƒ;ÌC8˜C9´9ÀC´C8Ð:æì0åõ0åÐóðæ@m`ìðá@€9„;ÌC9Œáÿÿÿÿ@ª Cüÿÿÿÿ@ŒÃ;ȃ<”Ã8Ð;äCÈÃ;Ôƒ;C9CC:ØC:C9´Á<ă<ÐC˜<´A;„= `áÀóPaòíPmïî@å €;¼ƒ;´;”C8˜@°C9ÌÃ<´<ÈC9ŒC:ÌC9´9ÀC;„9æì0åîðmî`óÐæmÐá@è˜C8°Ã<”@¸Ã;´;„ƒ;ÌC˜<´A;„= `áÀóPáïÐóçàå@m å ï0m`ðÐíô€€9„;ÌC9„;¼CЃ<„<ÀC:¸Ã9´A;„= @òPåð ï åÐó@á0ëüÂ/Œ:¬Ã<ÐÃ:ü9„ƒ<ÜC:¸@Ì=„Ã8¬CÀƒ<¼=”Ã8ÐÃ;ÈCˆC=˜ƒ9”ƒ<´Á<¤ƒ>”€Aá çPôÐãõ„<À;”C„Ã=„ƒ<œC9ÐC˜C9„=Ôƒ<”Ã<kå0l°ã ùôðl°æmòÐö€l°îPïàl°ó€á l°ú0íÀk ã Qî0á`åÐæmÐá@è˜C8°Ã<”@ÔÃ<”CÌÃ;˜=´9°Ã;„=æì0ål@ŠH€è6 †$À¸Áâ7Ø 2Èÿÿÿÿ€HEŒÃ;ȃ<”Ã8Ð;äCÈÃ;Ôƒ;C9CC:ØC:C9´Á<ă<ÐC˜<´A;„= `áÀóPaòíPmïî@å €;¼ƒ;”@°C9ÌÃ<´<ÈC9ŒC:ÌC9´9ÀC;„9æì0åîðmî`óÐæmÐá@è˜C8°Ã<”@¸Ã;´;„ƒ;ÌC˜<´A;„= `áÀóPáïÐóçàå@m å ï0m`ðÐíô€€9„;ÌC9„;¼CЃ<„<ÀC:¸Ã9´A;„= @òPåð ï åÐó@á0ëüÂ/Œ:¬Ã<ÐÃ:ü9„ƒ<ÜC:¸@Ì=„Ã8¬CÀƒ<¼=”Ã8ÐÃ;ÈCˆC=˜ƒ9”ƒ<´Á<¤ƒ>”€Aá çPôÐãõ„<À;”C„Ã=„ƒ<œC9ÐC˜C9„=Ôƒ<”Ã<kå0l°ã ùôðl°æmòÐö€l°îPïàl°ó€á l°ú0íÀk ã Qî0á`åÐæmÐá@è˜C8°Ã<”@ÔÃ<”CÌÃ;˜=´9°Ã;„=æì0ål0’ÿÿÿÿ€6¤ ˆòÿÿÿÿЀôÁb >Ø@0°l0H€eÉ8ÿÿÿÿ?@ãðò å0ôÀùÐòðõàäPäÐäöäPm0ñ ôÐæmÐá@è˜C8°Ã<”@˜ƒ<„C;”CÀÃ;¤ƒ;ÐC9ÈàïàmÀåæìPó0mòPãóPm`ðÐá@€9„;ÌC9„;¼C¤ƒ;˜Ã<´9ÀC´C8Ð:æì0åîðmàáàóÐæmÐá@è˜C8°Ã<”@¸Ã;´Á<¤Ã9¸C9CèC9ÈÃ;ÌC˜<´A;„= `áÀóPáïÐô áðîpmÐá@èЃ<ÔC9<ÈÃ;ˆC9´Á<ÐC8ŒÃ:¿ð ã€ë0ô°¿@á ÷îó@á0ëÐð ï@å0ôðòÐâPæ`å m0é åà@ÐC8ÈÃ9”=´Á8ÀC=áðÀåÐápAá çPôÐæPá@õ å0À„C9̬Á8ÈC>À=¼¬9ÀC„ƒ<´ƒ=à¬;”Ã;¸¬Á< C8Ȭ>ŒC;°ÁèÃ8è@Ôƒ;ÌC8˜C9´9ÀC´C8Ð:æì0åõ0åÐóðæ@m`ìðá@€9„;ÌC9Œ'€Ú`@ÿÿÿÿ?@m ¢8ƒ „$g°˜þÿÿÿœ  °gtˆŠÎ`aýÿÿÿÿHŒ+à 6˜$Àl@2H€8ƒ „ I‚`B L† QLˆÂ˜0Å1!@&É„AY˜ Q4ç™ @Ñ„@š@†‰ª& Ö…M&*› XׄÁÒ° ƒI+ªp‡v°:hƒp€x`‡rhƒtx‡yˆ:p8p8ØåÐí ïÐé`t v@mq x q xÐî0r v@m0 q x°Âù…_ …RÀZ`ƒ_ø…qxw˜zXñ¿ð ´P ¸@ lð ¿0÷æ@uð ôðPòPæÀ` åpõÀá lîð¿@åäð ó@òð°â ~áh¡pØà~aî!ÌêàÌ!ÊÈ¡È À@ÊáêÂAØ Üá~Ê!Èáæä!àaÅüÂ/ÐB)à-°Á/üÂ<ÜC:˜=ÔÁ/ÈC9˜;Ì=È€<”Ã9Ô;„ƒ<°¸Ã;ü9”C8Ã/Ì=ÈC:ÀÊ7ø…_ …RÀZ`ƒ_ø…y¸‡t0z¨ƒ_‡y˜‡wzÈ6y(‡s¨vy`0p‡wør(‡p ‡_˜z‡t€‡nð ¿@ ¥€ ´À`ð ¿0÷æ@uð ð ï@ïÀ` åpõÀá lîð¿@åäð ó@òð°Â ~áh¡pØ ~áæáÒÁè¡~òÊaØ ä¡ΡØ!ä ÀÀÞáÈ¡Â~aèAÒV4ìÀöÐnÐå@á@á@á ô€!9dÈH‘!D#„aÐD@Ð;¤¦†>ˆ € vHZ# ~@AìΠ†?ˆ € vHtÐ(D@Ð;¤Ph„!" h€’+4 4Àia…`‡„0B °Cª‡FJ!€‚Ø!‰D# ¦@Aì^¢†Sˆ € vH<Ñ*D@Ð;¤´h„!" h€’]4  4‡ ;¤8_H€ vH©Ñ(ƒ9LC@Ð;¤`†sH€ vH¶Ñ8HC@Ð;¤ñh¢A$& h€|4Ó “ 4À©?k€‰i`‡¤" 6àÄ$ ÀK €Á)NžmÈ€ h 4h vH­òlÖAìx¥ñ³˜!€ vHéÒ„Á€“ 4À©Æ`H‹ `‡d/M ~1 EAppâ$`°Cš™ †0H€ (:¢0Ø!γÃ$@Д°P ìÐçYƒ¡ € hÞ|´ vH6ô¸Áp @4K ,Z;¤=zÜ`Hƒ `‡¤J 1Aìn©‘ƒE€! h€95t ´ÈD Àx‚›ÂÉŸCp‘‰@€‹ 9ƒN5†à"€4K Nrd.8 ì|ëAƒa€ h€R{5{0ÀÈ$ °CÒ¯nd„‚Ø!XÓƒC@Ð;${þ`Гd0`‡4f( ’ Aì䬩P™ € økPjE‡T¨6@ ©Öí €à`H¥r[ÐCª²{ƒ †T´7@ ©îï  8€Rù`@ ©þ0(ƒ †Täx@ ©D2ƒ †Tdx@ ©D3ƒ †Tå„AÐC*  ‚h€!Õ™eÁ4ÀŠUƒ  `H•«A£ 0  0¤2×Àc(€R‘lÐÀL@ ©b6hl¦ †T>4¸“ÐCªµ ]€é€€h€!ÞcÀ P  0¤ÚÝó €RÍoàà ÐCª0q€é€€h€!•904Àj”90(€RÕsЈL€@ ©D:`t¦@ †T.È:@€4À ´Ë€H`HeÛÁ£, 0§)ÀV€€ $6¾`Èt2˜$LŒ &GÆC:,2P ƒÏC‰‚X" (QÄêP–(ñ…bÉe~A(a#^¼!Æ.Å0ð<ÚóP¢Db¹ˆ D {¨Ãy¦Ä’t ˆ%[m”!@‡u „„ – bu8Ï”øB±d¿2î”0€ÄÄ2©D‰2 öP‡óL‰/$ô@KvÝ(C0ö@ ÈA,/”(bu8Ï”øB¢±dï2ùÀ”0€ô‘3mäàñã A "8<4%@ËDZ‡:&ªJ|!I‚X² F‚‘( J@ ˆ0@BDKJÅ(C ù@ ÈbUF ƒ,X0òÒÈ‘"Ô@9Är¹Dì¡,»_HÀ –ìÉ€Q†@&h‚Ê )’%DD(‚Xr2ˆ=Ôá}\â I:Ä’­0ÊäÄNPÂÂ@ òbÉÙ öP‡÷q‰/$ú@K6hÀ(CðaA ˆhu28"˜:yÂ4Š"åC2w3u—fFW•4E!L!¢Ñ›3`±4}Œ2*‘”0€¨ÄRÔA졎20ƒ/ñ…$bÉn eØÂ-(ay (‚X’<ˆ=ÔQfð%¾èA,Ù³£ \Ð% µ±üpP¢ˆ=ÔQfð%¾àA,Ù¹£ Á]è% »TBc¬4±lñ"ØA,S!–³•Q†à/@ƒà€Q@4ÒØCr0køB’±dŒ2#Q”0€ A,Y#Äêƒ9XƒÄ¼ ˆ%[9`”!HÕ „„9`”ht ÂP–]‰/$¾@ ÂPQ¼±,DP¢ˆ=Ô!s°‰/$³AK6uÀ(C µA ˆuA,Y%”(buÈÁ¬Aâ InÄ’­0ÊàÆnPÂÂ0ÊôF:Pa¨Ãû¸Ä_ª Šß€`ƒ –ðbuÈÁ¬Aâ I\Ä’-0Ê„ÇxPÂB@Ë× %J‚ØCr0køBr±d£Œ2æ‘”0€¨Œ2둱¼:Q¢(ˆ=Ô!s°‰/$îAKö{À(CÐðA H|A,_)”( buÈÁ¬Aâ }Ä’]0Ê̇}PÂr0ÊàG:Ä2ûD‰Â öP‡ÌÁ$¾ìA,Ùü£ ~ô% þT)‚XÆT(Q Äêƒ9XƒÄ!ˆ%{P`”!¡„¤P€d ;æ™g×¼•c'/ß¿F½$ˆee”!¸ ¡„$Q 2ˆ†‚Â…d Nx´CÁ!""”A,×[ÄêÀ²:H|!‚X²%F‚™J@(ƒ$ˆQ¢!ˆ=ÔÑ~`‰/$é@K6¦À(C€9B ˆ¦UŠ€(@‰ ˆ%†ØC}àvøBB±dƒ Œ2?&”0€ˆ Ä25C졎>ð;H|!Ù‚X²KF21J@NF4I‡Ò öPGø$¾¨ A,Ù«£ Á]° % ­±ÌÔP¢8ˆ=ÔÑ~`‰/$pBKv¬À(Cð&rB Ȭõ𹸠@Ž:"B„ÈãÈ”.iÄx3Ê€†PÂr+@? pÄrˆ3ÊŒ„žP²+@A x’ƒžT)*Øol‡8¶¦Û‹…-1ÇvÐ`;`°Øl ¶ÛA€íÀÀv@`;P°Øl¶ƒÛ‘TèP¢EEšTéR¦M>…UêTjuD•øB’t³mW Ð{,Á)(Q€D(…B ø@ ¤`(Q )б3€Äáf=ˆC8„ÃŒB€yxs˜q æíô€3 BÂÁΡf0=ˆC8„ƒÌ=ÈC=Œ=ÌxŒtp{yH‡ppzpvx‡p ‡Ììá0n0ãððP3ÄÞ!Ø!Âaf0‰;¼ƒ;ÐC9´<¼ƒ<„;Ìðv`{h7h‡rh7€‡p‡p`v(vøvx‡w€‡_‡q‡r˜‡y˜,îðîàõÀì0bÈ¡ä¡Ì¡ä¡ÜaÊ!ÄÊaÖC9ÈC9˜C9ÈC9¸Ã8”C8ˆ;”Ã/¼ƒ<ü‚;Ô;°Ã Çi‡pX‡rpƒthx`‡t‡t ‡ÎSîòPäã@á ìP3 (ÜÁÂAÒ!ÜÜàäáêfQ8°C:œƒ;ÌP$v`{h7`‡wxx˜QLôðP3jÊaè!ÞÁ~ä¡Ì!ðaT…ƒ8ÌÃ;°C=ÐC9üÂ<äC;ˆÃ;°ÃŒÅ ‡y˜‡w‡tz(r˜\ãìÀåPó0#ÁÒAäáØáÞfH;°ƒ=´ƒ„Ã8ŒC9ÌÃ<¸Á9ÈÃ;Ô<ÌH´qv`q‡qX‡ÛÆì`íàð å0å öPnã0å0óàéàäPø0CáÚ!æ¡ðÊaèaFÔÙÃ8„;°Ã/ØC:ÌC:ˆC:°C:ÐC>ÌX¼ppwxzzH‡wp‡·åðìÀï0óôPy írH Cˆ  r2H #Œ‘‘ÑD (d<12BŽ!£8 tËäymàq¤$M4M Ã0 Ã@Ê-MÒY‘R9Ï$ÃPÂI‚‘‚DLõ 21ã$SDK VersionObjective-C VersionObjective-C Image Info VersionObjective-C Image Info Section__DATA,__objc_imageinfo,regular,no_dead_stripObjective-C Garbage CollectionObjective-C Class PropertiesDwarf VersionDebug Info Versionwchar_sizePIC LevelSwift VersionSwift ABI VersionSwift Major VersionSwift Minor Version__hidden#88___hidden#49___hidden#50___hidden#46___hidden#47___hidden#48___hidden#45_standard-library-frameworkSwiftUI-lswiftObjectiveC-lswiftCore-lswiftDarwin-lswiftos-lswiftUniformTypeIdentifiersUniformTypeIdentifiers-lswiftFoundationFoundation-lswiftCoreFoundationCoreFoundation-lswiftDispatchCombineSecurityCFNetwork-lswiftCoreGraphicsCoreGraphics-lswiftUIKitUIKit-lswiftCoreImageCoreImage-lswiftMetalMetalIOSurfaceCoreVideoOpenGLESImageIO-lswiftQuartzCoreQuartzCoreCoreTextUserNotificationsFileProviderDeveloperToolsSupport-lswiftCoreDataCoreData-lswiftCloudKitCloudKit-lswiftCoreLocationCoreLocation-lobjc__hidden#53_branch_weightsmisexpectF0‚€ÃÂ;ÜÊB’#‰/Œ (1‚àÊB’#‰>Œ $ô0‚@äÊ¡+#ð°+#K4‚¼2‚ðL#T5‚pY#Ù5‚ðq#bÐ 7‚`߬ÁÐÁBV#~`# p#£€#‰¯Œ @°1‚ýÊPVà̓ 0QCYDV€@ÀDÍ0BP 3 ¦  ƒ) §0Ã` D1Ã` *Ì0˜Â‘ 3 ª€¬Â )$¬0Ã` Ê*Ì0¨ÂB 3 ¦À¨Â ƒ)4ª0Ã` N+Ì0˜Âã À@PE0ƒp½Â ,¼B,Ì0ÈÂ+Ä Ã,¼B,Ì0ÐÂ+Ä C-¼B,Ì0ØÂ+Ä Ã-¼B,Ì0àÂ+Ä C.¼B,Ì0èÂ+Ä Ã.¼B,Ì0ðÂ+Ä C/¼B,Ì0øÂ+Ä Ã/¼B,Ì ¼B,Ì `Ù 6C°Íp3Ý 7ƒ€}3`0ƒ€…Á Ì `c0C@3XÌ `f0ƒ€Á Ì `i0C 3ØÌ°Á Ö3n0ƒ€½Á 3XÌ `r0ƒ€ÍÁ Ì `u0ƒ€ÙÁ v3Ì `y0C 3ØÌðÁ Ö3~0ƒ€ýÁ ( T¡À ÀL„¼0à ¦3£â÷€ ˆˆˆˆ ‡Çqœˆ ˆ ˆˆN脸 ¸¸¸¸¸¸ÐÐÐÐÐÐÐÐp–eYlj'œˆpbÀ‰'œˆˆpb b bÀ‰'œpt¹ÈH`‚2bc³ksi{#«c+s1c ;›Å!‡r0‡s@‡tP‡u`‡vp‡w€‡xȅͮͅLìÌe¬nAÆ!¥¹»43:—¶7²:¶23¶°³¹QzȈͮÍ%l.ÌÍåŒíM,Œmn”¨ìÁîòAöúÁþ$BB$F‚$rbc³ksaKs[+“syƒ£K{s› JÂ$N%RB%V‚%ZÂ%^&bB&f‚&jÂ&n'rB'v‚'zÂ'~,ÂB,Æ‚,ÊÂ,Î-ÒB-Ö‚-‘ Œâ[0À© r(‡w€zXp˜C=¸Ã8°C9ÐÂæÆ¡ èAÂÁæ!è!ÞÁ4ã`çPá ä@á çPô°€y(‡p`vx‡qz(rXpœÃ8´;¤ƒ=”ÂèA¡èÑÌ<¤ƒ;œ;”= ƒ<”C8Ãa A, ã‚­„ ñ0 "GÈQ __hidden#96_«¤Ã-€¹p .@aÌ@[`.!1 †¨Â0a &A,T˜ ­Ïñ0 "GÈQ __hidden#95_«¤Ã-€¹p .@a#dž`À 9"51cP]À@@ [`.!1 †¨ƒÂ †ˆa A< "GÈQ __hidden#94_«¤Ã-€¹p .@a:cP]@@ [`.a ™A,©‚ ‚m´‰¦#)‚Bñ0"GÈQ __hidden#61_¨WVI‡[Ìà\㬒Žy˜ `¸`ÆÐy !2d$£A€ ]#‚`  :â'3ØÅ]Ý&Ç„n' dÂè!H& € Ú;aP!ÀLÁK)L#F‚`à `â@Ó0f"^JaÂ10_(˜02˜î„0A@ðR ÀˆÑ€ ø™HÁ„Át' €™‚—R˜0F ŒÁÀÌ„ & € ¦;aŒN‚8”ÉŒAÑ$vqÀˆAò€!€Ã™ÔÔ*Á„Ð B#MÌw @N& €ƒA0ˆ‡`9¦ `Ä€pB ÄáLf F³‹»F  ÁÀÒdiª`ÂèAz‹  ûNÔÉ„0b`H ô@؄РB•ÍMbwd0ß €9™0F Á ‚&Ú& €ƒcA0ˆÅ nÆ € »¸ `ÄÀ@ èÁˆº `ÄÀ@ èÁxº `ÄÀh@ |¡MÆš1( .`ÄÀh@ |¡MÆ@š0F ŒÁÀÚd ¨ `ÄÀh@ |¡MÆÀš0tˆ[€.¶ƒáÌ]l Ã-¶ —á[†Îp‹-Cna A< "GÈQ __hidden#93_«¤Ã-æp .@a:cP]@@ [`.a :C,( ÚŒñ0 "GÈQ __hidden#92_«¤Ã-€¹p .@aI´wÆ º€I#€@& €á† @ƒ `–A‚ ` "€J˜02¸é„°Á„pÄ‘&ALg$7a` dÀ„ $' €˜02(è„0b€8a¼ð#ьÄ$' €ià @& €Y‚`ˆ[`.¶ƒ\\a )C,„›ñ0 "GÈQ __hidden#91_«¤Ã-€¹p .@a4'2cP]ÀpCP `0aÌ2B0aŒT‚9¸ØÉ0aLvL³„0Pa€LL1[`.a BC,„›ñ0 "GÈQ __hidden#90_«¤Ã-€¹p .@a'2cP]ÀpCP `0aÌ2B0aŒL‚.¬ÈL#G‚`@/2'1aLuL³„0Pa €LL1[`.!1  Œ(%ú°à QFhŽ(ŒtX@ DatÞ$ÊÂA4ŽœÁEÒDUÖW`™¶‰Áp¾°  àa :C,( ò Íñ0 "GÈQ __hidden#52_«¤Ã-€¹p .@aY´wÆ º€I#€@& €á†À@ƒ `–A‚ ` "€J˜02¸é„°Á„p’*ALg(7a`°dÀ„$' €˜02(è„0b8aè€#N`4ä0adÉ `ÚÀp‚ `– ˜0b[`.¶ƒ\\a A< "GÈQ __hidden#89_«¤Ã-€¹p .@a#„‚` *2cP]À@@ [`.q 2"„J€¸$JMÊè$NOýUiTÑxÅoXÀ¸Åe §”0-ñLXX] ÷ ] $H#ÿÿÿÿ$0 kÿÿÿÿ$PŠÿÿÿÿ$l0¥/ÿÿÿÿ$œ$Ô#ÿÿÿÿ$À(÷'ÿÿÿÿ$è"!ÿÿÿÿ$ &?%ÿÿÿÿ$0dÿÿÿÿ$O#‚"ÿÿÿÿ$r¤ÿÿÿÿ$‘$Â#ÿÿÿÿ$µ"å!ÿÿÿÿ$×"!ÿÿÿÿ$ù&'%ÿÿÿÿ$ L ÿÿÿÿ$+ Y ÿÿÿÿ$7 fÿÿÿÿ$V „ ÿÿÿÿ$b ‘ ÿÿÿÿ$n ž ÿÿÿÿ$z « ÿÿÿÿ&† $¸#ÿÿÿÿ$ª Ûÿÿÿÿ,à /ó.ÿÿÿÿ$ò ! ÿÿÿÿ&þ .ÿÿÿÿ$ 3C2ÿÿÿÿ$G u ÿÿÿÿ&S ‚ ÿÿÿÿ&^ 3Ž2ÿÿÿÿ$‘ Àÿÿÿÿ,¨ Öÿÿÿÿ(¹ ÿÿÿÿ‘Å ÿÿÿÿ‘Ñ  ÿÿÿÿ‘Ý ' ÿÿÿÿ‘é 4 ÿÿÿÿ‘õ A ÿÿÿÿ‘ N ÿÿÿÿ‘ [ ÿÿÿÿ‘ h ÿÿÿÿ‘% u ÿÿÿÿ‘1 ‚ ÿÿÿÿ‘= ÿÿÿÿ‘I œ ÿÿÿÿ‘U © ÿÿÿÿ‘a ¶ ÿÿÿÿ‘m Ãÿÿÿÿt ÿÿÿÿ€ É ÿÿÿÿ› ÿÿÿÿ€¨ Ö ÿÿÿÿ´ ã ÿÿÿÿÀ ð ÿÿÿÿû ý ÿÿÿÿ•6  ÿÿÿÿB ÿÿÿÿ] 1ÿÿÿÿn @ ÿÿÿÿz Mÿÿÿÿ‹ \ ÿÿÿÿ— i ÿÿÿÿ£ ÿÿÿÿ„ß v ÿÿÿÿ• ƒÿÿÿÿ+ “ÿÿÿÿ< "¢!ÿÿÿÿ^ Ãÿÿÿÿo Òÿÿÿÿ† èÿÿÿÿ— ÷ÿÿÿÿ· ÿÿÿÿÈ %ÿÿÿÿß ;ÿÿÿÿð J ÿÿÿÿü W ÿÿÿÿ d ÿÿÿÿ q ÿÿÿÿ  ~ ÿÿÿÿ, ‹ ÿÿÿÿ8 ˜ ÿÿÿÿD(¥'ÿÿÿÿlÌÿÿÿÿÞÿÿÿÿ í ÿÿÿÿœ ú ÿÿÿÿ¨  ÿÿÿÿ´ÿÿÿÿ„ó#ÿÿÿÿ„2 2 ÿÿÿÿ‘> ? ÿÿÿÿ U å ÿÿÿÿA   Ì /  / Ì / Ì / Ì / ° / ë /  Ì / Ì /  Å. . H ] Û”à>__hidden#12___hidden#13___hidden#14___hidden#15___hidden#16___hidden#17___hidden#18___hidden#19___hidden#20___hidden#21___hidden#22___hidden#23___hidden#24___hidden#25___hidden#26_$sytWV__hidden#27___hidden#74___hidden#75___hidden#76___hidden#77___hidden#78_$s7SwiftUI11WindowGroupVMn__ir_hidden#36___hidden#28___ir_hidden#37___hidden#79___hidden#80___hidden#81_$s7SwiftUI3AppMp__ir_hidden#38_$s7SwiftUI3AppP4BodyAC_AA5SceneTn__ir_hidden#39_$s4Body7SwiftUI3AppPTl__ir_hidden#40_$s7SwiftUI3AppP4body4BodyQzvgTq__ir_hidden#41_$s7SwiftUI3AppPxycfCTq__ir_hidden#42___hidden#82___hidden#11___hidden#83___hidden#44___hidden#84___hidden#37___hidden#85_$s7SwiftUI11WindowGroupVyxGAA5SceneAAMc$s7SwiftUI5SceneMp__ir_hidden#43___hidden#38___hidden#86___hidden#87___ir_hidden#12___ir_hidden#13___hidden#43_llvm.used_swift_FORCE_LOAD_$_swiftObjectiveC_swift_FORCE_LOAD_$_swiftDarwin_swift_FORCE_LOAD_$_swiftos_swift_FORCE_LOAD_$_swiftUniformTypeIdentifiers_swift_FORCE_LOAD_$_swiftFoundation_swift_FORCE_LOAD_$_swiftCoreFoundation_swift_FORCE_LOAD_$_swiftDispatch_swift_FORCE_LOAD_$_swiftCoreGraphics_swift_FORCE_LOAD_$_swiftUIKit_swift_FORCE_LOAD_$_swiftCoreImage_swift_FORCE_LOAD_$_swiftMetal_swift_FORCE_LOAD_$_swiftQuartzCore_swift_FORCE_LOAD_$_swiftCoreData_swift_FORCE_LOAD_$_swiftCloudKit_swift_FORCE_LOAD_$_swiftCoreLocation__hidden#66___hidden#67_swift_getOpaqueTypeConformance__hidden#68___hidden#69___hidden#70___hidden#71_swift_getTypeByMangledNameInContextllvm.lifetime.start.p0i8$s7SwiftUI11WindowGroupV7contentACyxGxyXE_tcfC__hidden#72_swift_getWitnessTable$s7SwiftUI12SceneBuilderV10buildBlockyxxAA0C0RzlFZ__hidden#73___hidden#8_swift_getTypeByMangledNameInContextInMetadataStatellvm.lifetime.end.p0i8__ir_hidden#35___hidden#9_12.0.5arm64-apple-ios14.0.0__swift_FORCE_LOAD_$_swiftObjectiveC__swift_FORCE_LOAD_$_swiftDarwin__swift_FORCE_LOAD_$_swiftos__swift_FORCE_LOAD_$_swiftUniformTypeIdentifiers__swift_FORCE_LOAD_$_swiftFoundation__swift_FORCE_LOAD_$_swiftCoreFoundation__swift_FORCE_LOAD_$_swiftDispatch__swift_FORCE_LOAD_$_swiftCoreGraphics__swift_FORCE_LOAD_$_swiftUIKit__swift_FORCE_LOAD_$_swiftCoreImage__swift_FORCE_LOAD_$_swiftMetal__swift_FORCE_LOAD_$_swiftQuartzCore__swift_FORCE_LOAD_$_swiftCoreData__swift_FORCE_LOAD_$_swiftCloudKit__swift_FORCE_LOAD_$_swiftCoreLocation__hidden#66___hidden#67__swift_getOpaqueTypeConformance__hidden#68___hidden#69___hidden#70___hidden#71__swift_getTypeByMangledNameInContext_llvm.lifetime.start.p0i8_$s7SwiftUI11WindowGroupV7contentACyxGxyXE_tcfC__hidden#72__swift_getWitnessTable_$s7SwiftUI12SceneBuilderV10buildBlockyxxAA0C0RzlFZ__hidden#73___hidden#8__swift_getTypeByMangledNameInContextInMetadataState_llvm.lifetime.end.p0i8L___ir_hidden#35___hidden#12___hidden#13___hidden#14___hidden#15___hidden#16___hidden#17___hidden#18___hidden#19___hidden#20___hidden#21___hidden#22___hidden#23___hidden#24___hidden#25___hidden#26__$sytWVL___unnamed_1__hidden#27___TEXT,__constL___unnamed_2__hidden#74___hidden#75___hidden#76___TEXT,__swift5_typeref, regular, no_dead_strip__hidden#77___TEXT,__swift5_fieldmd, regular, no_dead_strip__hidden#78__$s7SwiftUI11WindowGroupVMnL___ir_hidden#36___hidden#28_L___ir_hidden#37___hidden#79___hidden#80_L___unnamed_3__TEXT,__swift5_reflstr, regular, no_dead_strip__hidden#81___TEXT,__swift5_assocty, regular, no_dead_strip_$s7SwiftUI3AppMpL___ir_hidden#38__$s7SwiftUI3AppP4BodyAC_AA5SceneTnL___ir_hidden#39__$s4Body7SwiftUI3AppPTlL___ir_hidden#40__$s7SwiftUI3AppP4body4BodyQzvgTqL___ir_hidden#41__$s7SwiftUI3AppPxycfCTqL___ir_hidden#42___hidden#82___hidden#11___hidden#83___hidden#44___hidden#84___hidden#37___hidden#85__$s7SwiftUI11WindowGroupVyxGAA5SceneAAMc_$s7SwiftUI5SceneMpL___ir_hidden#43___hidden#38___hidden#86___hidden#87_L___ir_hidden#12___TEXT, __swift5_proto, regular, no_dead_stripL___ir_hidden#13___TEXT, __swift5_types, regular, no_dead_strip__hidden#43__llvm.usedllvm.metadata__hidden#9_"@p˜pHQ#]@___chkstk_darwinQrH@dyld_stub_binder@_$s4Body7SwiftUI3AppPTl€ð@_$s4Body7SwiftUI4ViewPTl€¨ÿÿÿÿÿÿÿÿ@_$s7SwiftUI11WindowGroupVMn€€þÿÿÿÿÿÿÿ@_$s7SwiftUI11WindowGroupVyxGAA5SceneAAMc@_$s7SwiftUI14_PaddingLayoutVAA12ViewModifierAAWP@_$s7SwiftUI14_PaddingLayoutVMn@_$s7SwiftUI15ModifiedContentVMn@_$s7SwiftUI15ModifiedContentVyxq_GAA4ViewA2aERzAA0E8ModifierR_rlMc@_$s7SwiftUI3AppMp€ˆ@_$s7SwiftUI3AppP4BodyAC_AA5SceneTn@_$s7SwiftUI3AppP4body4BodyQzvgTq€@_$s7SwiftUI3AppPxycfCTq@_$s7SwiftUI4TextVAA4ViewAAWP€Ðýÿÿÿÿÿÿÿ@_$s7SwiftUI4TextVMn@_$s7SwiftUI4ViewMp€¨@_$s7SwiftUI4ViewP05_makeC04view6inputsAA01_C7OutputsVAA11_GraphValueVyxG_AA01_C6InputsVtFZTq€@_$s7SwiftUI4ViewP05_makeC4List4view6inputsAA01_cE7OutputsVAA11_GraphValueVyxG_AA01_cE6InputsVtFZTq@_$s7SwiftUI4ViewP14_viewListCount6inputsSiSgAA01_ceF6InputsV_tFZTq@_$s7SwiftUI4ViewP4BodyAC_AaBTn€Øÿÿÿÿÿÿÿÿ@_$s7SwiftUI4ViewP4body4BodyQzvgTq€ @_$s7SwiftUI5SceneMp€@A__swift_FORCE_LOAD_$_swiftCloudKit€Øþÿÿÿÿÿÿÿ@_$sytWV€¹A__swift_FORCE_LOAD_$_swiftCoreData€ÿÿÿÿÿÿÿÿA__swift_FORCE_LOAD_$_swiftCoreFoundation€ÀÿÿÿÿÿÿÿÿA__swift_FORCE_LOAD_$_swiftCoreGraphics€A__swift_FORCE_LOAD_$_swiftCoreImage€A__swift_FORCE_LOAD_$_swiftCoreLocation€ A__swift_FORCE_LOAD_$_swiftDarwin€ÿÿÿÿÿÿÿÿA__swift_FORCE_LOAD_$_swiftDispatch€ A__swift_FORCE_LOAD_$_swiftFoundation€èÿÿÿÿÿÿÿÿA__swift_FORCE_LOAD_$_swiftMetal€( A__swift_FORCE_LOAD_$_swiftObjectiveC€¨ÿÿÿÿÿÿÿÿ A__swift_FORCE_LOAD_$_swiftQuartzCore€P A__swift_FORCE_LOAD_$_swiftUIKit€àÿÿÿÿÿÿÿÿ A__swift_FORCE_LOAD_$_swiftUniformTypeIdentifiers€Ðÿÿÿÿÿÿÿÿ A__swift_FORCE_LOAD_$_swiftos€ðÿÿÿÿÿÿÿÿs@_$s7SwiftUI11WindowGroupV7contentACyxGxyXE_tcfCs@_$s7SwiftUI12SceneBuilderV10buildBlockyxxAA0C0RzlFZs@_$s7SwiftUI18LocalizedStringKeyV13stringLiteralACSS_tcfCs@_$s7SwiftUI3AppPAAE4mainyyFZs @_$s7SwiftUI4EdgeO3SetV3allAEvgZs(@_$s7SwiftUI4TextV_9tableName6bundle7commentAcA18LocalizedStringKeyV_SSSgSo8NSBundleCSgs06StaticI0VSgtcfCs0@_$s7SwiftUI4ViewPAAE05_makeC04view6inputsAA01_C7OutputsVAA11_GraphValueVyxG_AA01_C6InputsVtFZs8@_$s7SwiftUI4ViewPAAE05_makeC4List4view6inputsAA01_cE7OutputsVAA11_GraphValueVyxG_AA01_cE6InputsVtFZs@@_$s7SwiftUI4ViewPAAE14_viewListCount6inputsSiSgAA01_ceF6InputsV_tFZsH@_swift_getOpaqueTypeConformancesP@_swift_getTypeByMangledNameInContextsX@_swift_getTypeByMangledNameInContextInMetadataStates`@_swift_getWitnessTable__mh_execute_headerøì¸4@`@ì<@Hp<BEa0Iy•¾ò#Bb¥Þð4Qi‰¦º#6“ö9XzØ<€”œ­@Ð@ó@@ C@ g@ Ž@ ¯@ Ò@÷@@<@a@@²@ÏïH_ 1234 €!5 1234__mh_execute_header_$s4Body7SwiftUI3AppPTl_$s4Body7SwiftUI4ViewPTl_$s7SwiftUI11WindowGroupV7contentACyxGxyXE_tcfC_$s7SwiftUI11WindowGroupVMn_$s7SwiftUI11WindowGroupVyxGAA5SceneAAMc_$s7SwiftUI12SceneBuilderV10buildBlockyxxAA0C0RzlFZ_$s7SwiftUI14_PaddingLayoutVAA12ViewModifierAAWP_$s7SwiftUI14_PaddingLayoutVMn_$s7SwiftUI15ModifiedContentVMn_$s7SwiftUI15ModifiedContentVyxq_GAA4ViewA2aERzAA0E8ModifierR_rlMc_$s7SwiftUI18LocalizedStringKeyV13stringLiteralACSS_tcfC_$s7SwiftUI3AppMp_$s7SwiftUI3AppP4BodyAC_AA5SceneTn_$s7SwiftUI3AppP4body4BodyQzvgTq_$s7SwiftUI3AppPAAE4mainyyFZ_$s7SwiftUI3AppPxycfCTq_$s7SwiftUI4EdgeO3SetV3allAEvgZ_$s7SwiftUI4TextVAA4ViewAAWP_$s7SwiftUI4TextVMn_$s7SwiftUI4TextV_9tableName6bundle7commentAcA18LocalizedStringKeyV_SSSgSo8NSBundleCSgs06StaticI0VSgtcfC_$s7SwiftUI4ViewMp_$s7SwiftUI4ViewP05_makeC04view6inputsAA01_C7OutputsVAA11_GraphValueVyxG_AA01_C6InputsVtFZTq_$s7SwiftUI4ViewP05_makeC4List4view6inputsAA01_cE7OutputsVAA11_GraphValueVyxG_AA01_cE6InputsVtFZTq_$s7SwiftUI4ViewP14_viewListCount6inputsSiSgAA01_ceF6InputsV_tFZTq_$s7SwiftUI4ViewP4BodyAC_AaBTn_$s7SwiftUI4ViewP4body4BodyQzvgTq_$s7SwiftUI4ViewPAAE05_makeC04view6inputsAA01_C7OutputsVAA11_GraphValueVyxG_AA01_C6InputsVtFZ_$s7SwiftUI4ViewPAAE05_makeC4List4view6inputsAA01_cE7OutputsVAA11_GraphValueVyxG_AA01_cE6InputsVtFZ_$s7SwiftUI4ViewPAAE14_viewListCount6inputsSiSgAA01_ceF6InputsV_tFZ_$s7SwiftUI5SceneMp_$sytWV___chkstk_darwin__swift_FORCE_LOAD_$_swiftCloudKit__swift_FORCE_LOAD_$_swiftCoreData__swift_FORCE_LOAD_$_swiftCoreFoundation__swift_FORCE_LOAD_$_swiftCoreGraphics__swift_FORCE_LOAD_$_swiftCoreImage__swift_FORCE_LOAD_$_swiftCoreLocation__swift_FORCE_LOAD_$_swiftDarwin__swift_FORCE_LOAD_$_swiftDispatch__swift_FORCE_LOAD_$_swiftFoundation__swift_FORCE_LOAD_$_swiftMetal__swift_FORCE_LOAD_$_swiftObjectiveC__swift_FORCE_LOAD_$_swiftQuartzCore__swift_FORCE_LOAD_$_swiftUIKit__swift_FORCE_LOAD_$_swiftUniformTypeIdentifiers__swift_FORCE_LOAD_$_swiftos_swift_getOpaqueTypeConformance_swift_getTypeByMangledNameInContext_swift_getTypeByMangledNameInContextInMetadataState_swift_getWitnessTabledyld_stub_binderradr://5614542relic-7.6.1/functest/packages/zlib-1.2.8-10.fc24.i686.rpm000066400000000000000000003036561455105530300220440ustar00rootroot00000000000000í«îÛzlib-1.2.8-10.fc24Ž­è„>t  èDêHì`ïpðt‰VþuŒ@‰Øòý±œ˜ý¼Ôà7æþy»f¢+­'.#®Ãû…©|ƒ8êË'›d=´Ôôà¢hn‰LŒÂ#Å¥ÊãmGÆàˆ¢Ð˜lÆ=\E…üâ!ì:îÿ‰fž«`*åIæê0iî\%£¯ˆëq¿ÚËä¯1ä8ƒú­­\y}2`Ï¢2¾þ=0Ä»ro„¥üëœs´Ä±º‹/h«¦š=»uÅ:¡§²¨’œ@ùtú ~µ•D¿ª¾ý1¸«¯­^æÐDƪÖc{ÑzWD8‘nÓó tLÁÙ¥aù\ (ïº=ޣިÓúgßÇ~cr:p˜–ÒÙÀ{‚3ÝÕJ”·cth!¾uTq5|¨å¹·¹Œ¡×﫨±îazÿQ"AüIX‡šx•=Šï$ü4ö0Ú~gгËn¦v€aÃUó£éóiQâk»¶ðfÚ'ŸÓÄ$y¹eLwé<Í~sS#1#lרõõ8LOÒõ°ð꯭Š`1–בx•Rz÷°ùÜG8àvw.‚pÅ!‰›°Ï†”œòoçLüN%¬XåSV‡ÿÍèU†çí뜞ëT• pà¸Î•¤ýõ²m/O…Á }Eì`Œ'åKT•Í77€’m»ôÐ7p±×ϳ©×%øúgIcôß­NŽ1"›a"»ëŠ{GUÏS_ÏIÙ€¡\3¹½0º¼Þ>Kq¯H6k8Ö7T â˜Ò††ÙŽM¡ý†²à\ œk42¦|¹ZC¢Ïì¼ÅFœº,/©ÿ(fŽ0Ë{Iièh¨R¨ƒµ. œ@,÷, DáºÐ R4™žk¸¢]FH!i:{cF'M•é©qŽŸÅmQ@õ vëz<­t0å˜ÇÊt½a€J'#QHÐò¬÷ÑÄ­ø'ég,ß!ÃDöË¿[%@;—S뺲JÕvW9Ö{¥~k¶ÚûÈÕ)+…uFUûç„t Ê8’mwxò,™(Ø_ÒpÂF{BÞ‡“«,7J✰®Ëäþ)(|Œû¢Ã°R \ƒk©úùem¹rþ`¿7Êϲ|»ŠXWnzX1Y$&™šÕmİíB8'i&…ÿŽñ‘ àòØVÜ”vŒç¨.’ÅÌ.‘7©½¦UO'väiR’VL%2B0ƘOÃà&o{#{™ï4Wcyû‡$ “ô7~9û³iÎó÷ÜàdɘZûAžùXÙ§Òß$#Wª‚_úÁÖÚJSP¶Ñ•m%Ä1°Qð>ÿÿÿ€Ž­è<0Ž?0~dèéê ì í ?î´ï¸ñÜòàóïöþ÷ ø ü9ýNþT\x † ” ° · Ìè .Lh  Ô Û (8 e9 e:ne>,^@,mG,|H,˜I,´X,¼Y,ø\-$]-@^-wb-Õd.×e.Üf.ßl.át.øu/v/0w/ìx0y0$“0tÆ0xCzlib1.2.810.fc24The compression and decompression libraryZlib is a general-purpose, patent-free, lossless data compression library which is used by many different programs.V´š*buildhw-02.phx2.fedoraproject.orgë¿Fedora ProjectFedora Projectzlib and BoostFedora ProjectSystem Environment/Librarieshttp://www.zlib.net/linuxi686 lL*u@½A¡ÿíAí¤¤Aí¤V´š)V´š)V´š*V´šNÒ¶„V´š*Q}©Fdeb73c4a30b71abf7c95b930addb2108b91a5acb1df2d153a13289ded784db9842d6c2d57aeb387d074128666d84e0b45157df80f34feb48bbf22649479bd71e1e8a0078be0ff1b60d57561a9e4a8cad72892318a8831946cba1abd30d65521c53a466b504371dcdda1504c90d8121d4823921f03554c3526995fb2bae7159f9libz.so.1.2.8€rootrootrootrootrootrootrootrootrootrootrootrootrootrootzlib-1.2.8-10.fc24.src.rpmÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿlibz.so.1libz.so.1(ZLIB_1.2.0)libz.so.1(ZLIB_1.2.0.2)libz.so.1(ZLIB_1.2.0.8)libz.so.1(ZLIB_1.2.2)libz.so.1(ZLIB_1.2.2.3)libz.so.1(ZLIB_1.2.2.4)libz.so.1(ZLIB_1.2.3.3)libz.so.1(ZLIB_1.2.3.4)libz.so.1(ZLIB_1.2.3.5)libz.so.1(ZLIB_1.2.5.1)libz.so.1(ZLIB_1.2.5.2)libz.so.1(ZLIB_1.2.7.1)zlibzlib(x86-32)@@@@@@    @/sbin/ldconfig/sbin/ldconfiglibc.so.6libc.so.6(GLIBC_2.0)libc.so.6(GLIBC_2.1)libc.so.6(GLIBC_2.1.3)libc.so.6(GLIBC_2.3.4)libc.so.6(GLIBC_2.4)rpmlib(CompressedFileNames)rpmlib(FileDigests)rpmlib(PayloadFilesHavePrefix)rpmlib(PayloadIsXz)rtld(GNU_HASH)3.0.4-14.6.0-14.0-15.2-14.13.0-rc1V´ŽÀUÍØ@U„@SñêÀSâÀS’þÀRûb@R+@QþB@Q±Ë@Q#@Pmz@P>@P;a@P(ì@PkÀP ë@OÕÝÀOÕÝÀO«­ÀO (@N½@MœUÀMQ0@L¼ÀKÐ:@K°–@K”æÀJ]@Jm–ÀIÀâ@I¿ÀI¥2ÀI3ÑÀG²ÛÀG°8ÀGFÀÀG:ã@FÂêÀFQ‰ÀFQ‰ÀF,ŸÀFÁ@EñL@EÜ4@EÚâÀEÚâÀEÙ‘@E<®ÀD¾ÀD´ä@Cì€@Cè‹ÀC™qÀC a@BàßÀBÍ@BJ”ÀB(MÀAÖÀA”¥ÀAD:@@ÎäÀ@Dw@@,¼@@ u@?ÉÛÀ?Š“À?ƒü@?p5À?'@>Ýß@>ÈÇ@>Z @>.‡À=ÜÊÀ=Ðí@=@<ìÙÀ<ÉA@<ÇïÀ9@9ò@8ž³À6ôß@5ömÀ5¼kÀ5RóÀ5+fÀ4:$@3©@Fedora Release Engineering - 1.2.8-10Adam Jackson 1.2.8-9Fedora Release Engineering - 1.2.8-8Fedora Release Engineering - 1.2.8-7Tom Callaway - 1.2.8-6Fedora Release Engineering - 1.2.8-5jchaloup - 1.2.8-4Kalev Lember - 1.2.8-3Fedora Release Engineering - 1.2.8-2Peter Schiffer - 1.2.8-1Fedora Release Engineering - 1.2.7-10Peter Schiffer - 1.2.7-9Peter Schiffer - 1.2.7-8Peter Schiffer - 1.2.7-7Peter Schiffer - 1.2.7-6Peter Schiffer - 1.2.7-5Fedora Release Engineering - 1.2.7-4Peter Schiffer - 1.2.7-3Peter Schiffer - 1.2.7-2Peter Schiffer - 1.2.7-1Peter Schiffer - 1.2.5-6Tom Callaway - 1.2.5-5Ivana Hutarova Varekova - 1.2.5-4Fedora Release Engineering - 1.2.5-3Ivana Hutarova Varekova - 1.2.5-2Ivana Hutarova Varekova - 1.2.5-1Ivana Hutarova Varekova - 1.2.4-1Ivana Hutarova Varekova - 1.2.3-25Ville Skyttä - 1.2.3-24Fedora Release Engineering - 1.2.3-23Stepan Kasal - 1.2.3-22Stepan Kasal - 1.2.3-21Fedora Release Engineering - 1.2.3-20Ivana Varekova - 1.2.3-19Ivana Varekova - 1.2.3-18Ivana Varekova - 1.2.3-17Ivana Varekova - 1.2.3-16Ivana Varekova - 1.2.3-15Ivana Varekova - 1.2.3-14Ivana Varekova - 1.2.3-13Ivana Varekova - 1.2.3-12Ivana Varekova - 1.2.3-11Ivana Varekova - 1.2.3-10Ivana Varekova - 1.2.3-9Adam Tkac - 1.2.3-8Adam Tkac - 1.2.3-7Ivana Varekova - 1.2.3-6Ivana Varekova - 1.2.3-5Ivana Varekova - 1.2.3-4Ivana Varekova - 1.2.3-3Jesse Keating - 1.2.3-2Jesse Keating - 1.2.3-1.2.1Jesse Keating - 1.2.3-1.2Jesse Keating Florian La Roche Ivana Varekova 1.2.2.2-5Ivana Varekova 1.2.2.2-4Ivana Varekova 1.2.2.2-3Jeff Johnson 1.2.2.2-2Jeff Johnson 1.2.2.2-1Jeff Johnson 1.2.2.1-1Jeff Johnson 1.2.1.2-1Elliot Lee Elliot Lee Elliot Lee Jeff Johnson 1.2.1.1-1Florian La Roche Jeff Johnson 1.2.0.7-3Jeff Johnson 1.2.0.7-2Jeff Johnson 1.2.0.7-1Jeff Johnson 1.2.0.3-0.1Elliot Lee Jeff Johnson 1.1.4-9Jeff Johnson 1.1.4-8Tim Powers 1.1.4-7Elliot Lee 1.1.4-6Jeff Johnson 1.1.4-5Tim Powers Tim Powers Jakub Jelinek 1.1.4-2Trond Eivind Glomsrød 1.1.4-1Trond Eivind Glomsrød 1.1.3-25.7Trond Eivind Glomsrød 1.1.3-24Trond Eivind Glomsrød Trond Eivind Glomsrød Florian La Roche Jeff Johnson Jeff Johnson Prospector Trond Eivind Glomsrød Jeff Johnson Trond Eivind Glomsrød Trond Eivind Glomsrød Jeff Johnson Cristian Gafton Cristian Gafton Jeff Johnson Prospector System Cristian Gafton Donnie Barnes Erik Troan - Rebuilt for https://fedoraproject.org/wiki/Fedora_24_Mass_Rebuild- Link with -z now for full RELRO- Rebuilt for https://fedoraproject.org/wiki/Fedora_23_Mass_Rebuild- Rebuilt for https://fedoraproject.org/wiki/Fedora_21_22_Mass_Rebuild- fix license handling- Rebuilt for https://fedoraproject.org/wiki/Fedora_21_Mass_Rebuild- resolves: #1064213 recompiled with -O3 flag for ppc64 arch- resolves: #985344 add a patch to fix missing minizip include- Rebuilt for https://fedoraproject.org/wiki/Fedora_20_Mass_Rebuild- resolves: #957680 updated to 1.2.8- Rebuilt for https://fedoraproject.org/wiki/Fedora_19_Mass_Rebuild- updated patch optimizing deflate on s390(x) architectures- related: #832545 reverted changes for this bug, static libraries shouldn't be compiled with -fPIC flag- resolves: #844791 rank Z_BLOCK flush below Z_PARTIAL_FLUSH only when last flush was Z_BLOCK - done some minor .spec file cleanup- added patch from IBM which optimizes deflate on s390(x) architectures- resolves: #832545 recompiled with -fPIC flag- Rebuilt for https://fedoraproject.org/wiki/Fedora_18_Mass_Rebuild- moved /lib* to /usr/lib*- recompiled with -Wl,-z,relro flags- resolves: #785726 - resolves: #805874 update to 1.2.7- resolves: #719139 Zlib fails to read zip64 files on 64-bit system- fix minizip to permit uncrypt when NOUNCRYPT is not defined- Resolves: #678603 zlib from minizip allowed NULL pointer parameter of function unzGetCurrentFileInfo- Rebuilt for https://fedoraproject.org/wiki/Fedora_15_Mass_Rebuild- Resolves: #591317 pdfedit fails to compile on i686 with zlib.h errors- update to 1.2.5- update to 1.2.4 use the upstream make/configure files for zlib, change additional makefile/configure file to be used only to minizip add pkgconfig to zlib- add Boost license- Use bzipped upstream tarball.- Rebuilt for https://fedoraproject.org/wiki/Fedora_12_Mass_Rebuild- fix the libz.so symlink- consolidate the autoconfiscation patches into one and clean it up - consequently, clean up the %build and %install sections - zconf.h includes unistd.h again (#479133)- Rebuilt for https://fedoraproject.org/wiki/Fedora_11_Mass_Rebuild- fix 473490 - unchecked malloc- change license tag (226671#c29)- spec file changes- remove minizip headers to minizip-devel - spec file cleanup - fix minizip.pc file- separate static subpackage- create minizip subpackage- remove .so,.a- Resolves #240277 Move libz to /lib(64)- Resolves: 237295 fix Summary tag- remove zlib .so.* packages to /lib- incorporate package review feedback- fixed broken version of libz- building is now automatized - specfile cleanup- remove the compilation part to build section some minor changes- incorporate package review feedback- fix #209424 - fix libz.a permissions- add cflags (#199379)- rebuild- bump again for double-long bug on ppc(64)- rebuilt for new gcc4.1 snapshot and glibc changes- rebuilt- update to 1.2.3- fix bug 163038 - CAN-2005-1849 - zlib buffer overflow- fix bug 162392 - CAN-2005-2096- fix bug 122408 - zlib build process runs configure twice- rebuild with gcc4.- upgrade to 1.2.2.2.- upgrade to 1.2.2.1.- update to 1.2.1.2 to fix 2 DoS problems (#131385).- rebuilt- rebuilt- rebuilt- upgrade to zlib-1.2.1.1.- update to 1.2.1 release- unrevert zlib.h include constants (#106291), rejected upstream.- fix: gzeof not set when reading compressed file (#106424). - fix: revert zlib.h include constants for now (#106291).- update to 1.2.0.7, penultimate 1.2.1 release candidate.- update to release candidate.- rebuilt- rebuild, revert from 1.2.0.1.- fix gzprintf buffer overrun (#84961).- rebuilt- Make ./configure use $CC to ease cross-compilation- rebuild from cvs.- automated rebuild- automated rebuild- remove glibc patch, it is no longer needed (zlib uses gcc -shared as it should) - run tests and only build the package if they succeed- 1.1.4- Fix double free- Add example.c and minigzip.c to the doc files, as they are listed as examples in the README (#52574)- Updated URL - Add version dependency for zlib-devel - s/Copyright/License/- bumped version number - this is the old version without the performance enhancements- add -fPIC for shared libs (patch by Fritz Elfert)- on 64bit systems, make sure libraries are located correctly.- summaries from specspo.- automatic rebuild- rebuild- FHS packaging to build on solaris2.5.1.- use %{_mandir} and %{_tmppath}- updated URL and source location - moved README to main package- compress man page.- auto rebuild in the new build environment (release 5)- link against glibc- upgrade to 1.1.3- translations modified for de, fr, tr- upgraded to 1.1.2 - buildroot- added URL tag (down at the moment so it may not be correct) - made zlib-devel require zlib- built against glibc/sbin/ldconfig/sbin/ldconfig€€€€€€€€€€€€€1.2.8-10.fc241.2.8-10.fc24libz.so.1libz.so.1.2.8zlibChangeLogFAQzlibREADME/usr/lib//usr/share/doc//usr/share/doc/zlib//usr/share/licenses//usr/share/licenses/zlib/-O2 -g -pipe -Wall -Werror=format-security -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector-strong --param=ssp-buffer-size=4 -grecord-gcc-switches -specs=/usr/lib/rpm/redhat/redhat-hardened-cc1 -m32 -march=i686 -mtune=atom -fasynchronous-unwind-tablescpioxz2i686-redhat-linux-gnuELF 32-bit LSB shared object, Intel 80386, version 1 (SYSV), dynamically linked, BuildID[sha1]=2175d2c6cdf24bb0b3363c006667afe70ce0e55c, strippeddirectoryUTF-8 Unicode textASCII textPPPPPPPPP P P P PRRRRRRR utf-8?ÿÿü@ý7zXZ áû ¡!#¸‡,â ëïþ] Ýb2ùu BÛä0R‡¸Å“ÄÒ^DÉ!]Á¨ð¹‰>Ÿ»EPWævÄ!úARôµ.¨aØä‘ìhRŸ"+`ó5ß®‹3?GôeÓ’?·ûlؼhžwžlÝ¡è§Jàn@€¾Œ‰-åc“‰i “ûfªHb}†óMMk¡l• ù1q*¦òÂ6jYè7E ¹2‘ôß„“®× ŒàY„LØ/ä·ä`g+Z”¦.Ðkn½Ðk/uZ:!­ˆVËçÍó·qP ¨¯<–2oKpC¤ÀÖ‚§ê1‘•&¯Ÿ£uÀRÓ‡ü&ÈDȹµ§ü¶žàãìÌÔY?ˆ£¤µ×ÄùŸò&«6sCƒ©–Khhû¡.±gyœ ;!;×ëÖÜ„Iw«ÔP^¸ÄÕ×Y-ÂùB(kÉÝ‹ë'¼a‰ˆ»FymŠ”߯Ò~ÙÚ”¾;ÂI½#“çÂk˜éÔIYð-*–ʺó¹O4Ù@øúOþ2壿 ‘o¢ 2ÙäcÜ’V‰+W%Ejlp6µYf{˜™?(¤k¯I“@DìäG$õý1œË€/±š/2½m#šârÀnDж ùιxÊ•„Nı” üoE¿µÚ:”‘®| qZx¿Á€ÿšíVjà \š¸#wD‹`Â_¼] 7I*](;êùÎ;üÉ7¶a“.]ü ö_xŒJªëuºƒDP&uÁäfJ~W,JeF}·ì*Xµƒ´ÆÀ×s”`Çòa|¨I1±ï)—ìsÉSjZ².²"xØžEEU®6UYïöÄÝCa˜ãta*J¼Ô wB ÇÈ‹ÚëçroÄ’\Šº¨5!%Ãû "{‘¬Y©½··T£qé¤qròRå. ,y»ê"µ'Lq¤N+ÿk芇fÌd¶5Œ^Nä’7Jx'\=È>Ìì ,7êšë®Vkþ¾`æì[#[ècWJ¾­% Whâ¯L·Z±É$IR0„#ÀTvú2ÝX£ÁGFÛÍûâú~~}ã'ÖÅöI׈+-8œV`^Á2™€ÉqrÈ1|@$bá΀Ö.¡hÁ•ì6û`z m›šx€’LÉfü ¬VcÑèä‹UC£ÚH,4ÊÓg|¼%’0)f@Î W„hЮÛÖ&±Q¥YjëªÚ±m¡†°ÜB][†«o'ÐMæoÚjÊù\Üù¶k …‰ÄÇê½æÄ IHWy*¹g©Ç ®“IºëÄŸ šË?{ÔTÀ@sEÙŒØ)âÅûW+ós[i¼?Þr‡HÜ®,»+ÿÕò§Ú\6îEcfUy×&ã¿öª²ô÷ä"{‚¯øä…¬ç]Õ—•ʱašË¦t`lÔMói*»ÀP-<Úy´%SÉ?÷%6f ‡ºW¯^+9UÎsB—C×a ý¾¬¢°5ºÔ¡ß¦2nD 1Œmx·Ä|­nA¬ ö-¹¨/4êÝôœ—U¶‰Q|åo^æ÷)]”98: CE®À[èàÖU)f˜8õ·á¢9ãÇe‚  N©è˜7x!ȬƛÏ‘«»o£/³gB÷àÖwH°8‘õ²!‚~_¤ä4»F[Å,Nna mŠTÝö =TD#©šg²ÑÑ€M*¹© ¢­Ÿ4O{‘Ûº“2Q÷óÜ–<ž“Þä­²ý½-yŸ¶°K&ÝêLʪÝhÏúãNŒhIMiÍÄç!ºt‰†Xj²˜¯¶í¨ýÑ(ñõ‚¨²5ÉH¸Nuê$F' ÛRæéë`•Ú¿äVøóhËÙ*µ?3 á9ðh¡.ælLì-°±“†¶‘]‰ýP›~¿«Jyàû³¸„‹<﯑TQÿk𠪵>rQ¦ê‹&Ÿ°ov£h¤—|Ÿk1jÇù¼Q¹¦'¸¿âF—v}8ä'<3‹ÈÜvƒ¦ÂÇ|3ÂÕàË€2×êF˜¡ëNy|}Öö:@)lIŠ|tó±ôó×k¡ã5i¨ŒMyQŽ`adŒh'‹ñxúFöûMrF<¡Ç{:“QAí.ج s¨uWw€¢Lg±±Bõf ¥)0n·¨ôwI.ƺ©*T‘•Ò Í¸sªª¼×4› à£îòs{ʶµÝè˜xá+(7Џ¯kݽªz?X›@@T߀o&êw"/¯øÒþ?tô2˜z8›ÇVmƒz8ã5¾ƒ¼Á\Õ%ȼˆ$ø²´h”úüœc{Š8ê” ó.žÕ‡ø¤N-ÞiÍšu‰¼ö^Ñ:æðâ"tÕ8æW–k¯ÇùD$€%¬yßÄÝô¯¿€Ü•Y? ÉòY½MóOÛ¸w{‰ãS]V½~çt ¤Ù/¦¡û¿nψ\'@'ÆJ(>µù&@ÜYT{›bˆ—¦œžõC|Ý·~å!÷!âÍ ûÃÂP®­WôhW°F8ŠÁõ™ÇTpnk]hG¬loÿ?)\=>& ÒnÕÇPÎÈÕQ}ià,0ZtlÓaYzX °AÖ5k€%&òð™/º¸îI$ìŒûó Éÿ?EÄÏ@¿Þ‘ç³ÌËéöŸšpÙY¼£:2“VûcÏ}¯˜åF~.bÛá a$•ÄCœõÙB‰5JSNÝøŸ®ž±NàÍî*w^}gn¾ßLü•Rœ“uéןٓQÃ1¯8f‰\2ðæ E\¹Iôj4¶“ÛéÅò†`–0Pû]à&Åz”WÖâÁŒ6LF:a€'áú½á!£â÷ík²n¢óHPá<ŒÐÊ—¿¡Dí®á‚Š$G þ[yØ–Õ¤}o#V4Ü"'«(u<â‹ëœ+…͸˜ò…4Ó +L£ÿ*{dö h·>¸©”7Æ•…]ŠæNl)è‡:]{kϵAá™/è"¬u-œrÀ$¨ÿ|6Ûð@\vÅ-ô¨íç½ÂKh¤Ñƒ›š “<ÎÞµ×þëø“ÔC ½*€Ô—&!@,Ò{í´JŽu`‚]å]EOg\…4’ÿÊ€"Pã\ lçªþú%“ÌTó\ñ½:`Šn ˆÎ·›û- }Rµé[×tÍbbånuá¯RŠkë+\ÝÖêÒÅ¥XŒÉí¼ý2»;ÅßJÀ<ڌχô¸ü ¼ì ’bÏFÀuUÁ¶%â?ŽÐ':§"ÐÜÏ´o`ožøm·u0É­]¨«¼£€ÊÑß±sl¾âEŸýihU‰$®6ù7a§ ý@®‡É¾­Zº!lÐ’YI`ü|23Ý¿ù3ß­œfnŒkE%ÿêm­#Ãæò`Ó‘nÔ½1f*Cùh~ß¶`ÂK¢pl;®ã2X{bÙèžJê!NS$#O¾¢IÀöÅŠ9]«õ—íèiþó·÷Œ‘i­ÃÄ"m­œUV’³<3GàVê?‹Ž€oÐT…ªÖ¿ÍÃS3H™r›Ö¦¯êÞG$a.<ܯ?öþ…7„²oSämκuÛP<ÀLÿšÔÓïHx…³,aËŸùÑ(þso•¬ ,âg‹ ´[ª@Â.üþɈ|ZIOˆ @#àuII&—Íw;õm\ÖÎ~Sÿ>t¬oÉ"MCæ”,‘t¨u›³´ +.7ÁjqÛÄ2phé±m¬Sù3Ü‹=8ÁøûÖíõúÅ¥œDóúœß•Õ šêkÚ9vóþÿIH®î[ Cvw}W„¸cyÊ µR!ÑýŽ!`¾<ú>\g ØîÁ¯ÛcA«}L½IG—uv/2õ¬~{RÏV¹S%8®ËIïוaÁEDÝç4FŽêªz'ˆ[4tÏE¤X KåøÿR8 Ù¾'g¾F4à‡¶žKÍ1•iIØ×N!UGÿ÷„mHQB;¢•÷ÜeÞ%9Ñk4³9òr?Ypw¥• \¾T„?Lé³ÿÐMšVøXÆ™ûOݱÍ,T2ö ÃÃ-••Ô/ÂÖØ:4çÚ¶¬F$K݆¤}~”†µb‰ª¸õJõ¯”8Y]výß¡ú5 AüŒ"Ì¿$±—âV9ƒŒsJCží,?¹j¦£6jš@“=ö@ ½¯åÝÔMÝ Ü˜y¢ëÒâ¼måc†Ë;.¤Ep#/´9íÐÆžX!ËK]ã¡kŠì½•H`íp†Å×ϱŸ ìÌÖg‡p`màkäé/ŒVdóxºóubøÑÇÙt£Ô'Ùm” ZÉÔÄ× ¼‘ás‰A¢d‘7Ã÷¼\ì¥ØdxkxÜ“ÐßC¨n‘*Ecb R¶ÏžA¨Í`F×iö PËò-®.ÛÆ[Á+ c€,âº:Å'Wã_ú<¡¤™ŽAàíbjë (n]OR>oOÄþ¬¡Èî<µb|5º™TþôG³o–ɱÒ|X¦»]³0ºJá©ÑdR¯¤¤t±.æÕŒ"Tæ=áâÀŸMF&Þw»)VÖÓ—Þ-°Î4Lî?Â^š‘ Íp«;œ€¿j•aU“ZZ9Ü‚Ô'Öà/ZÚ™ÇrgjD‘Sä†õj¿¡e/´Žy¼roËß-JDXc¹´çkV³žãÑË”nVÝÆe×áär“تâîÔ€Ìêa¯ 5¡|ûec §7ôÇd|%Ôn1ÖÍ“)‚M䉂é·ji>„/fjçˆÜ4fû¨-)¤jÏ F l«0ùIs/ Üs©ðó\IäVøw6êFì>Çl Ö­[H¡„…îç “¨€C¼gêý–Ñ1°7NȰuFÄù‡A By,(›ÓzÜ@OƒÜ® ¸½j_‰ÜEÄt‡Œît Ò ô–ì Ô¦ó@±ÌYbåFêG:%D{|FÓh;ø)pzëôô Dcƒæ°~]m€ª˜ÁùÃMÓTŒ5–-3Ø~Å?ïÍz¤X”yR׫ï?ÂNƒ-Fc@»/kSš:U[wODéXkOÉhƒã=¸fRå°îÛÑí,Ò nJàaR öMª”vL†—ØÀ|r5i·#²öFq?ÌÞ³Ÿ-SæÉ8KÅK­VÌG4w¦â‰J3ᬖ(ý±@´‰îöØâ Ì\3A}²![Öh_òFçà—ðÁQòQ¹/YŸ:qÐAs"€Õ!‘zIɵùX'ʹm‰Xðbå¢t»mÒ%s¡ñ4/'踅±oFoCX®£‰Þ±yÇ^ærÀbš’Ù3¸’tP*]Ö¼ûðbKØÛ–N_Íè¢ML8",þó‚-­qÐÄ n-x,íèq¼ÜU‚ª'èÒòÛ¡"ø’窹zþÊHÐí>æäxGã³ïg¼0ÕØÛõ Hˆ¹°LQkÊ#lW‰ÕwôœÉ«ó—%/¼Âtq›L–.±­—1‚Cª¨´–sò0 ñô£­aÊW÷<S^¡>Õôž¶ò½÷¬B)aM5¯2ܘkóäKhÝ/ºÒ×lß½Sך\¡+UkÐM,Š2þì\óŸŒú¬GÒÈüS›÷S|­äÃ.áa oMà¬B˜J–‘•ÐEe>ŒÙe¯®iížÍciò—&ßý¼”ú²t‚™œzfQ³QyMç¢?Á,nqψÅC(:qv—©jø"ÄVéÆQ28Ž`_ábæêñ+j¬ù=¥0ÜC$2#˜ ÿ´ãê"æ_â]eì¬R]ì¸9Ù~]ž‰ û†#›\í?óO?¢SÝ í<¦õT­ß.æŠÉ.ÄÂdžÛí3Üä„#€‰˜k—‘ÉRóÌ7™â(ç!»Üw"¼ÐË=Z÷n~þ Ã_x{)@öÃÓÏÞ‹¶°Ïë¦ÜQfùË,ôÏW28Ž9ö˜VõI¢µˆð©¶õCx-aY_¸ÑUÀO˜~@l6’†i]¢+{Ð…£í.ëë[Êä~ 7jÌà\Ý£§F#'à‡‹iÖíÀkçË@°å^+S…,eÇÕ5®~7Äu‰Ì“à+ îå°š¯•qÞc0a£!žû‚wŽdñ;,JðG7ix\S§*Hê;æ¥4@; *¦Œ ¦„ÓèÆFmÐ1õFTsôk—/)O‘béö+}Ëp©E²hy.?¾Rÿ=ólLš½§›ÍÐYu,]œøº"»#ôI;÷2w2ß ¦œÄ¤HÐé§øÊ;úhb~à±í¶8Õ*ã0YfZF\‘ ê¾ H# Åÿ<<¾Êo9x¹{8tÍÆº:´suÌE(®œ˜oΜÂþQ<Û¦ÿÍ+?‹ˆÒ8Ïé?G¦³<~ôýŠÓ /‹=ôó‹mY˺spÍÁdV¦Ý⥣çQÚæáy |Å1•Wư‚( µƒŸ’ÅàÒѽOÊÐ?Ì\g7±¹ŠZ{îÈì0c ÷áÏ?ºdž^eé¨Rí€Ö-’v-»%Ê,˜Z¾À´ª¾‡šûÓÛ·]„ý¯MÌR¤0,:n®Ì6‚"—Òͽ¢G¹zŠZ ù*ë£ÆŠ‡òÊ€¬úpUÛ'„§í©FÔc”[ "ƒÜ/gXwar'Ïó¥GÌ )‘„csÁ‰óš˜kÑ®6Qã¡ææTßIs¾8Væ™#—šŸ$_ÀEÚ\·Äò†"Ð58fñxëŠsx2?-—üæ/€\…8|»«“B#f[…[½5Äz7„¥]fI`™'—÷ßo ôé|yÆá¯3°5Š;Õf¢`BÜ‚AçNC ÿÁ¢×ûŽqcM>1$Ì™Àgéï*iÂn»’yáˆm˜ot`8»î+`&N.îrß’.m=eªûoâ~~¼-Ü9ž¶h{áVÕñ´µ¹ùxsËå%<'ª‹­£ßÖ0à ‰½·µMÔõÑ!T:ÛD–ðžl A‡oÐøx¸µøº›bÔíJÛRÙØù’Ï–Þ°ÀF¥ŸYa¼‚@x2E=oH9œe/ï b;4•x—@w¾æ©Kò­ê©r~·ôÆ=2OÌRÏ&?Kd;§Ã·1>ìàšƒ:ϧÄH¬=æ8Î fp£eMÏ÷4gå™ü,ßÎcF¸Á[Û"s8oAþÆËåziVäe;<³qæc¤ÖºömrÚu0Èêm{(Дoc>¦F/ÉèÂÄÇÕ.Cõþ¹4˜“ó\…xzG†y²ËÀDDº¹üØmŸ}‰¶¯`ü“þÖmʆTÐ;ÃP(ÃP´í(À¸–’Τ¶ÒÅWq(ãÅí^à@M?Ý;ÔÓ‘4ñš– iV©–ÚIbñ† îwjB®)%]Yiô`È™bÿs¾iÎÉ¿>éGs ÚZ—r|Q[”D¡7òÇËlãØ›ÇÁ–Ý“ú'ð~½FiB@<ˆöýj†³ 3^?˘ö}UŠ*¯žëöûíØã×­Ç#ŠqÇ랊\õ[ý]ŠÑ›Þ¹ÙàD¶¨äÛ!ÀQQÃU{}ꦟÀ=1y©ÐÒ!4î ûƒAðyõiªÑÛv-áw a{¯º)s‚‰ÄpÔÑQãÑMâ§Þ3­§ââVBó?ïÒœ•§rl(M÷‹{ŠI{pÐ6L`Ïûð¥©J ˆ—6]ÙðK±íøô¾O;ÚË­·ÉZ3ÙÉþíÞƒªŸ AðGØÈ»CÔ“|”²¤Ÿã¹ÀhåZ‡•N¡Þ. 1ìø:£ç Œ’÷ÂûÌûÇðYõ<íTŽ·xóC­Óz´Î‡Påg÷¾™„M“úê.<ï·0gc¢ÿJI„“ÏÕ$|Öõô+‘¤ÉvÚ¥ù?ç§Üügd¥$¯zUG[Gð¬×äQ9EGÙ›ÚYg‹Fcû¬iY_ û³Rx×iSs܃­:L3ÌE|*Kß·\~¸Ò¯‚ûÑe»™>>ÿSþ·ðÊzó/KÒ¯W=’õX£G4¬=) 4j¥WÍWž0p<Ÿó¹ß”RõF×r/=´E'É{¥YŠØÛððp+It¦žOí:½‘zlÇ¥ 5XœÁKl\‰TŠ=S[J¿×¦ë¸Í/9.šOÞÁ²È8 IÇ ñxg©DÈÖ›°¦ Vä# —wnÌZKFu<ÂÍÛâ^o„3Ì¡@ü` ƒLTw‚&̃nÐc1 ¥­’é #ƒa3bv Dƒ¥îíp¬îÁ³[aq@ëÐø6†Í6KgÚð ³]wµ¨_0¥Z{…Ç×zúbXâCd¼¨oܶìœgò»IŒL›C1N@‰p•œÝë,¥³V11Ãï &›Ù–¦Âo™Ûa$¯•Ü\ò°|âÜŠbu«¤(vNÜ6L7LçâÍåìb€7-EùÁ},“ch=¸Ú¥NÂ+@öJÂñÑþ'»Êé(c·‘Ž«ŸÉ&Dx†’§ŸÈЂõÚ.ÐÔ³ìÓ_,K«eŸ©kØy«8§fsðÒ,H²€ù±~c˜œ«8ÍE©œÀh>¹4¦ÿ:¢r_”'¿L¢µØnaJè çO(Ãð (¹½ÃNÿ*á®ÉªnÇiQ6e[ºþ·¿\T­jv5‹”!VNIwÊÜ{‰q<ä7Φ‚ë|}¦(1®^¶v¤UÅ{Ð7¢°üuNá:R@:¹øá–’ê˜YJY½I,í]Щƒg7hªê®|Í .ëJ##b= ¯dê¸Là{ Y~Å·nÛëÿ©Ö–§UÝC—'ôí>”¹Sf…S½Ï¦¦gÑ`ÍN#;ëÍ>ÕˆÆa8ä×zäœjZÅT«XêÆ¢¡è“R¤¤XpA7»1dïÝðû #{<:ïùÈÁ¼í’°ÇÇ õ¦ú Á¼ºîŽ$:næ,k(Ê‹-èáAcÑ5NÎ?XûÿøÄ¹s.§Ì $ dq³x´Â(-m„As…æÉAZ|ƒ Ú‚#öœµ*±Uð4M`Hk/gÒ#<¹‰IC”ö—o¥´'$G{«3Ü»+üßts,•m (}Œ›ä+=*;­é±sêí¦Æ«UA’Ó$¨ÖYØ‹ÆúZÞb6kNºÝԂʉڲ'LáöÊÇ`£/_³LÌ\Se}gÝA4µP¤ÅUTûàJCšP1Òó§‰ÉýÄJièHXÈX>¼2®@Ò²¼©«+îBˆ%y"o›Ú‚J“Z†o5úû^§Un[vÕìŽ(æ<ί¤NÕùÜC•|©*ÅZºÆ üæ Y³§½Â]tw›rÔ@.â`àËÓ5ÉÛ['ÒiüN^ƒJíLÛ•6ÇAED󠱨“—Ä’É4‰U­ úx…¨Z.Ú54@Ìy4’tPJâàn%` Þ#uQ±²Ð~‡Ylkƒ`¨KÍßßrVsÀß]vÔóQÀ7=fþkºj0" ð_'+¸ä)Öá³¼Œu ˆ»ˆ@˜oMz` °ò‘ZÕ q—µŸ­$úF6¹“!͘„jUϼxó BQ¬Ë¼u÷€e·R;ôú]æÍ­µM6é!7Ýù˜ºxûª>²hN@xT°;¬š¿UAŒì J¶Xñö^Þú›ïã Ýÿó¥´vLØ@-ÔhÒãƒ0âI¥ç(Z£$Ûn AŒ}ë¬ö®×¾ß³-݃ì«)=¹š9B™D&¦Å¡¤¨¡U'‰½ “Fd_«{ºv’ÈK"KÒ6Oëìí1þ’=̰€X4õÙ gRà[]6÷É{rZêÌàäË!Í6]f`.''³u´ RzæÙó ©žö­Yõñ%pOмƒÑ“-Œ†)5SwÐvg·‰¼ÁõLvX0DÛ(˜qaì©é‚‘* ªÆÕ@š–Û"´3®r>Cæwêµpoq¥ÓK*Ow(ú™ mïî)$Û¡ôqÜÕ-ýÕ0u30Ä—œm°XŸo`¹Ž¯˜#„Õ KŒ€úwÖðlgHðÿ30žìvê :qlþå´àÂ!ã,° ¡kò¤)“”ºo1¶ ØÉi<§x`øÁ„Jà ¾ÃÎJÕ…e¡áæ•ÙÛ˜æ®5|s6 x—«ÁÖxOX¶'åõ‚‚“~…Pñ&¶¶OžÝJIöÒ€ÐxN®D¨ýâÒ®óÉsØiŸÇ4esÑ0ؘ‘ M\˜*{{ÉÓbÌŽdkèdEöb¬Ñdê-ÙÞêc_©“ш §ž&}x ÊR 7€ƒðbǧ,z·€°“ðÏŸŸ4m â£RIx8ß3 ˜pì#MuQ±¹6_k¦ôolé¶™L˜cÐ ;/Ä,ö Žrø‰Â$Öé~pjàü÷²Ò€1:¾Ýí”V€´:Wyçltê(A¡øBIž4[CŠ}ú(õ˜¸uÈçóyúùùõ{Á¡jÞ)¡Çûâ rχldÄ"i¾ðˆ<(Øg7¢Ý÷ñÃÐ%eq?ð†mB‡5T¥¹ºLšâý¸•^˜îÒh@LMæÎb;!O¹>oÇ™õ¹øÛERÎÃåÚý¤{"Óp·Ù(‹»„ÄŠ|P=—b–ÚòQZ%9ë`Ûdš³…91(Íl¢Í=$åKWÇ´ Ià>Ç÷Tž¨2ïy»¦ŸÒäÖñ/!ù¸š!ñv§¸ü“ɲî^‹éãK;ÕBRìŠ(rþññ@Mu6"© 9RS·³€:ŽLa3ÞÆ¹Žc¢Z³¡]´÷Òb3Ûhð{4LÓÔm *¹ÿu2b£ðøªÿ<ñèñ×™w–µaÁ˜C$ýîÂLÌüÆÞtH? Ê—Þ)—w©Ø]¶0®ú=|“‚RÝMã “+ŸšÏùÔÔ%,eXµØWhÑ*!awËìx»}̓gºö&BïÏ÷iÓú«á/L´,bTPÁÐ`ÚžCàôkG)'\[¶mïšË[r $}Ò Ð\·Í«®è‘®÷±™1ÍóÝÛŒ¸2°x[ð¥ñç{‰} úq‹…¶¸qÍ8ÅbŽRjhVn†‘iCœì”tÄöCvu_4˜ËbbÏÀ!¬±¼K"ùðJ ×±êtýëROc· ‹£o8Ã\ ˆ+raÈóÄ;aßœÚÀZ ®’#w,àâ+†zÐdªÈŸ=’‚³•ÅEEˆ5n«×ñŽ0f,LC‚εÂÄÑðD¤ÚìÎ ”3™6ùáO¹KM‡¬[Xí~³_̾ø°hc¥êÚÛ-4cׇ¯êcÿu`÷4ÅI²ž€Îª&ÀȨȆœð©yÅCÆ-H¿Yj Å©‡ØQˆß«À.¯súÅÒ0‰â4­[ }n‚²èÜ|÷ß°qVÛ¡ă¬Þ"ñ]5ƒOüõÍ@·Rx<4áeG mÞD§Î¹ŒïL_2"í…>mXÍtÄ8œðGÞ…œu¢v´~^¨ÂªT÷{:vŽ"ÄökßY^¹I}ªï¬¥›FÌ&@V¨§*UJ—®iÏúBcÆ: q Éê.mÏõ‡ÇHW&‹3¼n„eEŽ*ÿC‰‰l^ÙîŸ:ït‚N²/S×A]Mp†ý=ó®Ã¾ï…¾2pê–¬¡¾„H”>mìxN[fÚ(~,ú´“-ãîøáïÜ~&§ÔDæaª}áù†'.ÿäôßÍ‚2n`n”âõr&i«ãƒšÏÛé–™ËFVÃZ)ÏyÖ"áFm¶úUkH¯kQZ€‹]I lgh¨Ê º€Z×üý×D­•Z}•J¨05ûËï¼ ø¤º. 7 hD½åÜ¥‘‰ †±ò éeÀWaÐZA/ÎBÕëöaIH ¾+um‹Y^wC.ó»Û[ÚÇ–.Ø{а‚} ±â¬õq±åä;Ÿ5¶ë +x‘¨.AEùæ>b®ê[ÁXÝŸuÊ[õZ‘‹IqjûW-ôÍÀ…¸; !ñ¾ùš/Ì(¿¤ xïɽÁý %Âm ´w´Š4®ñ[Hì ƒXÝ€ŠãC6¼~ä„?†2Ë™ƒ,iаå^„RÃSß–Ð Ž “Ö±©e9<á\•Zƒˆuõ2ÓNIÞKúeÝŒG¢azÈêÚTN˜IIwï /—Ÿ/téi‰ïl~³ë5•õÊ—ùé$E"\EÍWÅ£!´‡”T£Mçïèe0/uZ×ü°NZóê\5ØK'zI$dë݇øu¸Ì¦«RáIÿ|éYŽž^Èo+Œæ «¦ÚåWðÚÿÜ€­yîŠÖÓÀü)cKÚí‚„—>!Å×k+yÁàì 3ÁR2%ÖG²Dw;ò›qO5àÃCº“UHô÷^d ¿rà¼Úû쨹‰K±û ‘sÐÃ}>. ‚Ÿ©“€ØÄë)0I¹Ü•…»Cäj åÀšªÜÍP‰ÚÕ‰k/YFZ¬‘ˆ}sO¿X–“ ¿§¹Måå PØ+÷xtg†;¥Ëò.nÔO,™/»¬ŽÌ‡s¼™nk‹µY…=¦gJs¬îÊý’„çŠÐ”y=­ø4¿R•þ\|Jk9°ÎúÒR…ËA¯N{躧4ò+íMÇñé‘¢}°±¸¢K ‚ÄÙ¹?åuç'žåzÙ‹Q Äoµ¿ÒœŒ '¢¥úÝõùhN)µ:?´3öí ûŽ{nÏr•§_~aË„ô5 ÄzE0²’ž­1λ€Þä° ‚ºãWs:ØÁócå@#y4T01³s3ÒëC’ÑLÓ¹_ëVÞÈ&—?Ã(>Ç Çýꎞ¥ŽTÕkòíÈ«W7b&?HÕì XYSV$A‹îd1n[¹ñªzX™¹OËßu]° ÙÛ‚ºš(âðKLaIYV"º3™¤Hî§ýÌf3+S6.¨°î1!jvL¿’ê”ûê€Ïgú'+Ù”«3\=çeÀ”B}ÿú_I=Êæ(Ó…í‹nyXÙœ>zÊ€¢ G“ëåxFzíª²\óÂÃñhøë¨–Ri«Ubß›õ¶`×Êõ”%àBªñpü±–Ã’ÿ%YôqÞÐv)À¡J;žÎiÏs¾¯Q¢/¿˜$PÀãÙMK,w$—v8»”Ãrj¾X }š.3¹²Í§î À§×I‘© ç«Ì@ógÛ-B]JÀ÷%[ !å•êÞÝìnÆ„ÅÕ¢¸f |ö·³dôûX±§æÖ ¢Ì³êñ-"%8 Í®¯ÆS`¸DS[â{œ»Qß/à÷wTtaß«ÍÛÊ×&&šÝ$´Û¦qéPgC0ðù®SZJ“ñ‘Ö\¯‰­b’º×GVçÁL»÷Šî§~ñYà­6ö2h]fþªJ~"D^LaïÃðy&l×½‘ÖŸ  yÒ%ÄÍ—ÝCÁñ†0äåVMâÐ:têwé&4ÿZVªNoÛ@‘“~!®LéMÛøþÄëÛ/Úúñ#Ä`CeÓ­¥ÄŽR¤}1™¬˜TÉ"í&¢rgê‰Úb #o胧tí‚ä@d‹RÅ>kÿ°Ó±°I°ù-×\¯LskM §)\‡~À¿dk‰j+Å®©¹ WX=«ô!¿Vÿå'Å•Æç×5žCX0ìK&¾w6_¥ð—Y±3ð©0ÔbCgçêÈÊ’dƒÒ·9}´wÉxr‘‰žwîæï+¬9yä)äµ³P¸AæUŽ¢ía+Êb[n$²‹ÃRßH•ê>ùs=aiokñxû™¾a@jøúŸÛ|µÍQU\–?jU»Öðä¯ }¯} éóM©ã`Ï_ó¤4\$Ý­ªã·pÅ‚V 6=m]Ø*Š=õa+ÃP°ºªmÛD NmÿFa¾"ðÕOnKByMwÖsòm6ˆ =Km“ȾðH™‰'½śï-•% DuÅJWÅÚpèuû!p˜Áíç[fÏ4ÛwN%ÕUgFá…•ƒÆ=N3EÔzX,M¢I·fÖ/ m©æAáì‘% ß0¬ì.aŸvaú‘t\ °8UN;]bY.ÖiÆQl(Q•1[B7þMo;­Ã@-‘v^‘}4,„FìÞ´~¸ÀÚêÈUÝA‰™J»Áòšý|ÝwÁ#!DþÂÜ7ß-k±]~úØG šðs¨Â:÷€ÜÍþ ¢)ð Ï-ÎaŸ¬7•¾ŒÿÙД肄¼­ÌÛ”œ×]Çf·Ãúþ¹ªMì8’HIo.(Öwµ16ÌÍûMøFMê`iâ°î?Ä&5vV¿,Å÷'ä 3ÂDxÐ]/¾¼H~µw»¯Ó<›ššNàm&z&2Ôý;ŒÓ¼SûÌGÕŸÞ${øâKl/±çí8äüœ>ë*ú¸T)*´œt7î.ªÉ}Oé3θùåo4¢xÿ_ÇÅú@NN+IŒ n%:ðàj¡ñõPþØœQÁ—_&æÍ¢tƒì›ô#þ öÑ ÷ÏT½çß)ä˜á*WYN^–âÂ)Q®~ï™ÁSFö©"¹’îý’6ŸšÏUkáã/ 3[†Î ,ö¢dR ‹îTè9!¬øÿm¬\)dÇ ´Ô= X<‘aò ia®þ]š(ÆÍøm§ÿz¶~[‰W Ñ¢'OkaÞ=¹ÀÐæ¥$*Ýýâ„îTôÕáÅÁüÃ>þ(¼Áª÷²òÏØÎ™á¯ÄaÒ­o«Ù QÌÆ\<&ú1ýªz›kN›†šfTÉ<¥ƒ¨^_¨yd|j9’§æ˜žèQM­ÞÁ+Ì#›Ä§º-QîíÚáaž;"¿Í—¦ã”ûôM| þîK6±P6c‹zbEψ ÄÖ k°8—éÆêÄ‚FSŠPË`={(DĆ¥Ô&äBŠ\ëH{[FºQ˜#ü£ŒT¯ùdã¢9ö^Lô pá¬ô q¤£S …ƒÙ=a–Aü@¡e3þ´A…ï Ï;ØKQÛ„½q`鿥< ;éRŸ=ìƒÖã³i4*Éÿš±-âœzÁøíTƒM¤œ Ý,ô^…uÇÿcX3oàsƒÂ˜Ø/Ö4Ëèë›’éS õJà½G¿Õ‚S ,WòÖpí@›ÜØ÷G2éÆÉX?¿Ã¡¼™¶ E)pðÒ€g6cÛâÄÎØÈz‘äkuƒ“s.Vá™pb‚Ì ãGTåâêNÝÀww!Ÿ¹ƒÿV‡Æ´vÛ«› ðTÒ±òsŸ ¯²Ì.Ï^Î{up4Ûy:ÎTϘ‰~ÊÏRø_:ÔÌë/û _%î¡þ –U2vÝØû ¿ŒPÓd¹¹zý¤gW¤É ÚˆEéËýŒ/ú«E:EMQŒ]‚ Í«kÇ@fzÊ`@?2(¤­a¾ô¸õeù‹¾ŠgмބïHxϽjÿ0È'º‘5vnÄ;Ãûþ‘êÙÌtѯEÜ·ŠRk1aáÐ92Eéž±t›X‰G=½e£b2Ï hîqGÚªßçwK}Àë­LÔ÷G Èz êTç4G %jÄìFkd¼´Nœ—[öNkêª>ëwÙáE¦üO[íb$6ÔÖé,µ‘ðC÷}í_Vuˆ„nÛ›Í~ArœûI?ÍHð*Ó¯ùн'>y&öžfDq—ÚÖ4ê _ÈD¿Ù6£G™ÕsÁnm̽k=Œi§mjþ¼Oÿ“•¢Œ·0P¿ÂƺZ¬4 h!¸‡ ýï¼n÷ÿñŠ8-›ønsn ŠR¯á 5-˜*;Ï룎ﰭ*`NLè8¸å()D»á ÚþDbJÜ¿wÓØõ^äŸEbs!¥åÝ——íúÁtàž§„‚dî'ÍeþVïI°èàÓ¶S·{]¦¢q’ÜP>ò7n½Í²‡±½$õâìG´Ž¯«Ðêˆùˆ‡”Õ”,™#WÓj½‹†ƒÖd*˜¦èÔ[…éº4\rß ¡4&=xˆîØ´µGè+¤ÜmtªA1ÿ]ÑœÐíÍ>üž.ís.{<Âù¹þÁHñ‚ÁyQ,öÌbÙ±/·ª¡ø4¾•¼šµx1Ýaw^¸1Úˆ-œŠšE’ïÕòðõ™Ûa‰úKlåÖ“'·.HDZEn@ñýç¥ÇÆû$w6tÀTÞ]´Pð,u(;V#µS€©e®€ª³F®õò~Ã,Áá7Yî+où‹Pn¯®Z;ãÀLyÚ]àl ·: õ£Ÿêiq¤Sû?Z,¬?Àá bÿàŒˆ&qæ]†ù;î7Ê.íp²­ ÖI³Z»+é%¢/…eÎ={ñ²ÿ‚RúŸyÜâ­-²«[ëÇ}\V.Šú"€"s¬ú“iÚóÚ®Wþ¼|Xf=謦4ÌPpFè ,zY‡çè/Äâ'Ò5Ì^=ŸdØIA}÷¬ YâÚäð¯‹xQ•Ýà›Â&'`gÃêdqsˆùÿñ?⬠¨ÇKz€@*@w–B¡}_"þÂõ˜oRi8Ó‹ø©Zõ±-‡°Þ­{˰.ÌØŸ¦ 2O)>–j{ÒÇÛ´`¤ö©\ùÆÊè;ªE12–R2S}«ÇǤœ °¢qlY|˜Úêc5”¡­ó¾Ùí˜ÞÏDû7gp´É®*|j‰/íD=/)V\ï^E%oÍlrÑ1.z®™ORÉS¡ê¶±uóùƒøC‚ðØ:Õý0tð4Ï$Êb)o¡÷­[´K`ëX.GÜV¤-3Ãn'ý=.;.úÎh‡±Æ¤Ù,5ùßè´MO¤êRÖ,ì·š6œ¹AÆCÿ%×bq‰ ü/Ü6Ï‘òþÑ,赨öM+;•ÖØ|Ňb¥g˜wɾ¸ÔWö^ˆëÑŒâäq{Õ~Çö_HQe†[RÝ]‰Œ¢¡Ø! ô 9=èëý±M'ôË‚ 2ñ ­G®×àY >€ŸL€0UÊs‹Äšð ÄG*|¥ìÕœ…RL#Ÿ9(³Ç+„nã»Ï>Ïs­´D¦¡ŠËõ­þc-}2ì”°3éÝ2K++Üzµà^`ž‹}zKE¤M;ά_ ºÏéM9C!l!.BøHZµ¨‚s>¯oצcæsOFŠ·`‘ÖÎ}`öڇεF‰)ŒD¥]ð¿»-àÆ¨¯yq‰Ù ý—¿èIJVcÒ8²A°†Ða«H%wÞoKqÓÅ7ÀÏw¹®–GÃ54€nž€“’0f÷œÝ"xèÚŽ¥%µ¼ÉEžA´$/p纫 ¶ð$ŒF$¦|áW7);ÃaŽµÒØã¨Àp=Ö&Ñö4±3õÍ!Øf¹€[&N»¦§ ™Êÿ¢Í2ZG•9˜ŽœEΖÑö0#ß^ͪ]ï ã '¿e¥ÁV ƒè@^j=lbèñóvwQ£,À®ùÎt¸; ]þ® “˜ƒ |F#²×ö%M—û6ÇÃQ,gk`l§ ’¬ywÉ‹´/ ¼þê»NêØ¥¬ •Ø/ ëÇâX†7ö³î;Ç-õ¥Q|iŸûX‚²:T}ñ°ÇÚoáÕÈuMŒh®'söáG-¢­ê(»šV£»‘)ƒðÌîŸOJ ·{ÿ"wäˆ EEÚǯ×è¾ÀZ=°Ì\TX,ˆ7§S.=ùuÎí[6Gðzâ<¤_^Íc ­Ôó" ¨è›•Pt‡52àû‚Õ,ö®dEμä=±àJXÒJ ”«÷¼¿]¬ßˆ 'çCÒ!lڶר£‹Ó‚HŠ©,úìÿdr…ˆþÒ4ó¤Þ½Vò¶˜ù~œ|\‰oë.ŒéÓEl)ÜöÜ—ÁY!Ò+*[à®i¡Ü>'òJÊîɦ@T€¯µ9 êv¡Š„(aì».6à럻ԾÓiÀô&‡P ÈÌã'ŒÎGž“´]¬ÚPu˜SÉ F;°Â\ÓzË;@2Ð4l4ùŽ >kÞATv¾³ŠÁÂ`ÍM}*–þ†(hæ ñÏ[ÅØ÷|ä…® ƒœ#å?‘ð*Ùuï4…}¿GH+ŸJ=àäËžôU$ô.ø òõë.ŠØìíù­0žQfóeö5sAk!¸s ABíò¨U¨ÌœdÚ¤¥ÑËuÈýj°–&Ó0·ŒEx>:6 z°õC¤×ˆ¨5ƒ •{ …Bv‚‘,.Èf94Ñ™øøYœ7ƨÄñb‚³B½i/@{ëñ³mv²•iD[0¬üPf®Fq€\ 3'Y!$—%ËY¢‘‹¿^j,“Ë }ÊNreGl»F¨ñíœ5“üÆ)ê=tn58˜ An¬ZçÞº¶Óñ³ì² g‚d4îêôÄÀÈU°ÇÕáÀØhöœ¯ãÝè%úGl«E&$’¸„FPä¼Ë}RmŸPë0¿n-©Å.âÐ*݇YùüdÙìêÎù6À«•°h-,ÀçàÒ‚ÄErìHÚ=0ÎÅa[”è84lÅlr`‚åÐlMÂϯÙz•%ÞVºä”›É+_âv6‘“³ˆ„,2#–äWýøú ÂíÇûaòFiå1Ä1üaï‚ÂÌ_?iS@2Î|H­éóú¸½ó’Õ«W”¾N.}ç­×óÖËEwƒí'Ä“`°÷gô8Ðãa5ceŠ~kÌpBÿ r@ äìõ$Xõ¹ÁQ¸Z‘ííÝM(ç¤ý ˆú®-«†Ò0NQ“çôu¤BÅÆ‘=õøu‘ƒ9{dÜS~äH7Ö£!ÐÊÃ% Éi0 ¶#؉ÖV9–ó$ÙQœ?º#`ù³ ‚‹ÐLÂMͱHîè*ÙYëOê5wóßr¼3g §Ö»q€žÖ”LÃw9wèÝ2¡Dòcc6™CaÕ9¢> ¢ÀÉÏ=>ZTC^±z0²i[ @ç¦\™™ ½Ù¢"Ï•4þíeÄÝEBß*‰^a@(ðáã(Ïå‘kÊìvNQ“ñ†ÂÈ3™ŒKDnD"}E2"´œÈõ×%¾)|=Yä0¶Ö]3NvY ” z5^·4ô¡Së¥LL°Œ¨P‘ä²é¢ O°ÖÄQ~ßNòþ&tA1‹Ùµ A½²Ýw¾f¼Ä¥b“ˆ›ÆáÒ*jB³³>MÀ– áIV€]¨{=†Ò9¥’FMõTص&Ñø»ôÖU\¥Z]7‡&š’ã2Ÿ$¼tI[¼èÔ"˜ó˜íL‹ÎA–u¦UÄÈŽÙ¤û¨¡òÍäúKl’B8‘¹$£øø'Æ*‘½¼FœD¡Bßhê!<¦ó8cŠ?Üo½õ¤’˜ž]áìp—ì.R¶Áô¬™gÝLÁßMèn –òõ—JÄ9iã+¼óÃÙ¢VŸÛœ'p±gs!_4üâX= b± îòëB#\ȸ‘V&㡎žþÃ<5é…iZ{êÿ“ÜifùŠ¥¡n6Ç3ì8nA?5¸l\™CJo~?=昳DrJüãÇrµ~XŒãž‰â¢”yu”¹b‡²O”‡­•uYqÉ÷„t¨àårÜÕÖGm‚-W¬åØS£S‚ƒÆO²\‚[ ÍKk‰½%¨‰ úU~(î’Α€ñn|{šxhººÿè56ñEý;£ë¾ <Û³$HæÅ¤Á‘ùýÊ•ò?€Á>]+ ™‘øÍØœX;±3U_òå.U `=­ÀÇwª†ú¤c=ŠÌ,ƒÔøËȽ öQAàn§R×_rª1¤M EfKÈ! Þ'ƇÝá™M×áðÃçÆw\‡ äcUPuWo•+bñ(æ+†Ì7%¨ÉËð $Ya¨!ÞÑçsµΦòhfÿfci„ÂR¯f–ÅÏr“KA3é£ÏÌû”ßk³¤W;D9åVd…—Þ|þI&§IyÒÏw­¾òAòý®t ø æÔ:!ÄJ k åÎ6¢„8ØÔ,—ª´©¡/™ÛEUÀ]b7Vå,iÜ>’* I™ýn”õÞ¶áNŠxËë•Ø¾‰´¸2c…¼? ,FõÃC€f¡oÙÒK~ŽP"Sóôö&Å dËOaô\Xó˜‡Ñјš÷掎«& 7n‡ó’³ò7oqfš¿ í’•ú6Ysü°t‰{Œ‘V½_Òíb>fwˆ`ànoñtl³…scÜÈQKÖK‚ ï9ÅܹEÌN¦[ÊpÍÿ3¸¯Ùª*©Áwо¸‚e‘·ï^ 8>©7ª’[ËË<¥ÂsF` %*žuA¶xÃàÑŸõq7¢ÞšìŒ bsÖOvÚS‡ž‰àæ,ú{~ÛB¶CœÔ‹AØB“ewB4If}ÕÖWð…¬KªúǪaJ^„}½¾^_i ´B2‘9áóìÈ9 ‘ÍvrJÀ–kêýë. öà2}·ƒ}ur‰KÌütXgNÒ©*°ëtî>­Õæ«*{°qK¾;T.¾Êä` ÙåJ¤íNnŸöeuaÜ?*–-& G_þŠ˜ zu4x½„¦Ú s´©Až3Ôp~»É|¶ XÁVU´”±$Bð'¥‹tO\„Áßc©x¾ýRI¶Çí™C öÙ¸®¬]^Üäö¤%í¦C{´Éê fÁŠÄÆ«œô·þtÄr„ÿ¨t…8øÍ«vΚ©ž9µ®+‘éÏ´_ŠÒ¢å’­‹L*ÿM!ÊÉO2ÌOsåq.ž3N´ƒû¨­à œjw8¨Û¢šXtd5ÇD>s5¶…`ÎÙï(­-½-Ò±QwÙriºZä*eͲ•e‚ó1~„3æ&ºá¨XKì+9µ|aÍÎ1€ š9€T1œ€ïöP˜£W˜&hÕ¿Wj<%?që¬1J º èa^¯Åùãøtë>ûHPä² ph)£ÙØ‘h÷Iˆ*gOƒ#…QćЮã›Z¼$uqªî’ ,˲ mô¼\gÈÖ!Ø9Êd—(Oƒ’IÎÃñ$ñ®ƒÙŸïN#þJ; M‚Ýy(m·“^§%^泎í’…à‘“ÑÏDj`Xý³Žù“ÂQؾLP8õj¹¸ˆlé–ü¨[¹ÁÄzgI„Üàt ìÞ@cºâ»=ÿ'ÆƦævJá6g.7<®ä÷¡ÂCíðêžkyæPa6-”6Â6n‚ïXY¸MùÞz¥™)IåeË8Q©Î>£:¨öÂïùn¸ð5ØrzÓïÆ åîžL§p˜_üL÷™ÖÄ#Z@ ¹ "¬D}'˜ÅlsŠ˜ßÄ7› 2Æ›ªÜÕ¾¿=r‘Y,ˆIaÂ8lâˆÕÚøÑlð¶\S[»Aœ€DX~ײ ^¿ö“¡Âûh=YþA_a}®bŒà‰þHtbÞƒ•œ¤l;\ RðhÕ¦å“K£BCëÓÉ?IâùãYóWdµ­G<ÄW*„ëö¿Q¡•ÝÜ\ 7_2NŒ@=v20Oå²D[‹†|^±£¹"G•˜ó”ÊÒšFw0E­ã4eÍy³†Iù$®M9‰ôàÈš ûäóAú¹žö_¯©É(¾ZÄ ¬âƒS¦V®»ë´«8—@}rý0ôT3//ýõtaÞ¡ð†ð[qgÚX™G¦‚0° zÀj©1ƒß„`éÖi·ˆ2¬3¸§$(ÄðmãKŸC¡Zꀪ€—ØÞ¹ÞLäGÑõIï8Iitoœ 8¼èwäç;ðõbÝ©Ã`?EóÌ5 t&ßwä§·#? …Þϧ㌸–ŸCÜýÀœ"µÚ®P¹âü¼£«Éï¹uLK>Ï%\ue`‹¾|•r½õèô yjæä<äKj} j0eÙ_`ìõ« ¾!}ßîZÀ¨Ûm9OE:d‡ƒ§'¤î¹TG‹o(ò"$ ª?ºGUÄ6ÝK„ÒNK£‹Ê’äaw ¢¥¤a‰¨¥1„Ö0ºâ£-÷ƒà’þqšîÔ``è‰i¬ØeÛ°7¿\T¢"Ý0iXÍ%€ >¥"ߺ«¿w4 Ó¸³*\&C§…å¬3Ç» ‰Ìgð+·áGþŠ’idkߥlãUL0Hé‡îôhÌ©6¾C‚¯Ž›ñm÷å(pßÔ)ˆa Ï÷È? lÏùrkòÈf!w¤Ôl_ùqJYax2SkˆÓýµáÔAk~ŠÅêŸLžð³žÏÊÎ4Båûâ¬î%Õ¯K8R VQ-Ô¹mšB˜dYÂج9Â, õ ¾ËÅ¥‡kÿœ_i`dJêÎeº§Õå¦À2ÖMz£> Ç_®Âk-öäõàôúIñ¦Óþw]SqnT’—¨-††¡‘*³†¨yxÌr0æËVr+9E¿|»›zø8«=(Qi Œ½‚k¬ÆJ^ÕV°ËZWåŠ{ÒÖ·ÊÐ}Ê_¡ÞÅX-b–(v˜\=y 5d®;©Àp‡AU 3–.cgfA·S«çóì-­Os²Ï"‚;ÃS‡!'nÚŠRœX'½9ÿEqñ7h› ¿ýkytÇ"´cß/áª6×À]:$ÿæL‚ͳȄ-廯25E×.Ÿ`N/±~iEpWݰ$ÃpÊß¿"_ò•Ôh´kõ.i¶:së-Ãbíô·¿0ªF+š^C¾(’2–ÔÜÓ$@ÞYú£%в\GÓ°Ò¸$¾žCÞ̈ ÇÐZ´iï)kf>Átï"}ÈrÇø]º‘Èâ´ÎÒX»ÉM~#R´,Ü@µ”ÁP*·&uY0JS¯ú×|`Ò:Hnv¹ ßBîRNñÉne „š õ`ìaxEi&Õ«?Öœû6‚®RP<Óèç5þèåì^çµvYåÊAÑ&ÿ¹49÷Œ©SŒÅû§¼Ò8ú?XIž`•†…ò–á ¬ª°Y¾SîÕ§¦ÕGÑZßþn­äª‰jï—¨D¬¨½Ñr‡}‚AêÎÖ'všÏ„28¸–¹}¡€1‰Z—Ö=7„¡g­U‰³ŽŒ1 CLæ§‹ÑP^…Úõ÷Mœ$$B,Ö:OÉFþ Ž"I©7õ¡-‘¾®éñó fÄ(7uÒÀ†¼Ë4B|ŽßR„s •Vø¶u9Ó!;u@C& å È¢¹å{{6–µÔý ù¥âÐR*’mAK°üïÔcÖHö´õá#IoÝ9$9„÷¢{$2åÝÖíþ‰ÿ@ P_JmS ƒfaƒ˜ÁA’šA1­çÁ‚z>K7“8Ûz¡ŸÙyhw¦Õ”>ÔY?ëíc×aí‘€KddM9=ª¼QL•cðlE˜Ê~s2lƒÍW¬å ¦•.îú2™§  ÚÔô”VŠ‘IƾìöO¶MW0Æ ‹uPf#:Nµí=ošÏiØ þδr¾ÌïxÍщ—1´—óá¾Ø O} ²¸nãùRgžÿ¹lå²ëŒZ@µyÎ-kÐþ¥@ÇËS¯\ðöû[‹²°@GøðúL¹Ï³‰þ˜F’À¡+àpU”˜ZÆÔÛ5‘^/$ßaCnr®…7Å›ïêÊÜðùw1“Šo!ÞõÄ~üzpü”gŒ7¶ðXQÓ6(*€oÎèñ%ÈùªMüC~—–÷塎µÒX—·;RNžücyzòKÑbÖœå³ iœv™‘Ão(½¾ÑZQ]9˜|‘ø×. uÑ\À§Ë©Õa8›B…fô®ÖIóÕˆI¹ö^F Áh¶1;¡~Íz­$åh/!\–—ëwYx=;õjR;FñIô‰Ô. ãM´‹±î]áÏìpPç` ä Ĭâp–YNý”çŒÐ§î iïÔ ¿iA{î2z,vŸ'¾‚UÉåPÒö9¢õ´Fõ,§õ¶8`››T‰É{@ÈÝ£CÁa¨…íøì»Ë·úx.Úø%˜éŸ—ð¶ÇzuÃ5|ÍqcgªçßhÔ]Éêâ¦S™>2ú¡iö[^°¬Bw¿02é£Â¸Å±qÉ·¿Ñ;7‘f/ª]î–Ú,žJkD0¼K¤!©¸_·„ñR’H¢[^iK÷†J„e|¾|ñ½”^’î5ì§èj`Fÿ™(( ù_GO(“ªàäµ.žh4à-¶Jµ~)š)ïÜNZ…Eéw ~‘GÎ;¸6íÚqESØ>ÎÚ¸I34I»3ÇÝíí¤Þ«ož¾¿5 Ø>»)íäÌÁês˜îC’ò8W%Î!i“ í‡\ý| E@µ^—›U¤ÖŸXÐv#—³ûrܺw(ï˜Ðc68ÏJ¸ÀP4¨Ç·ÅüZ3ȹ͌£ß¸¼¬gý³'‡ý`‚JjëýßÿçOÃmÆÙ>î‚m=ÜXÇ‘ܱîRd ¢ø…@ëö袨Ÿ¿˜}Ð8‹ ˜Â ›¿Ðà´¶)ORiĬ:) mVe}K§ÙÐK—ÃÒcñ ÿØÊ‹sãh!k6*Î4Æù¸Ôb81òÑHdÕâô¬ÝŠÍ;Õ–{³T~¿ Ü,÷К§þÑùÖ>Ú9Åk§—â¯kb¶À-=DòTãÐ×ÜïR DûrocP¦ÈÛJžùm¿öïÁ袂Bîc=wÏLD`Àa`7Àp_Îî3Ó\ñ“ÿTÅt·áP¶¤Iû¯R¬+!Þ¾u Ó-"H÷ú™Çû}{óÈ1„³ûà%þ—¼¦L5}œº—9SÀ–õl,›K)¡ › ËHåÚñXi‡„$pX$’Ùƒ k$1áùx.uqÝ &üà {d2õ|H«ìŨÿ5´zH“€KL´ ÅLoÇ@즇vƒätÐÔ2x² Ù±ÓïºùÍd( ÃVzBðÙ¾u=R f¼ø3“7uE[Gƒ ?BÆvÍ»ñš›ÇaGÌÿ%Í8ò1ÙE¿ïÛï§cøí–|•»LÄÞ±T¼ØÈ Òáªwùj°*ÜþzGœ#¥ìw› –€ôƒ¹¥¤yJßßM]N79òJZQÍo6ëÁï¥i.4¥i‹ ÃÁÞT¤ówYkR3ôjWX À—js¬\:äŠÿ~›m¥F!NGãž³.ºÈU(®yóHþOÊÖ\wΗvZI‹eƱl¶ÌÅìlĘ.H 7ÿõS¾Ús¥Ä>´~@ÎòoMãk!z¿ °*ãê/ 1”8ù"ªtŸÛGm“{ møi‰³uïK‰/övEþ· íŒïh¸;/ ŸÏÆÓðzýj³“B£¶•A• ˆ°Z½Š` } ©à:KpÀîÆ¶§Sv'‚H’Ü¡šáôÏßÚXц Xð1ªl-Q=É;Ìhò=lìQq>\ ˜·dË®)\Ïb^QÛÜ6‘ikfüfÄÞú $MÔ0ïUyåÓé—`÷ÊÄa Œ{ÈëçЭ _Áyþ’zSxÜ@Ëí†ë!U72nºHCtD¢&ðz¹ÎC³xÐ÷AdJ1]ñù˜¦(âCë.<“wF»Áß‚ÉšØ V_<÷õìfA&ÉMI¹Îž*æôѶtЫ°©p £èBCkØO>NS…oì ¦mÃaQà ÍE®±æd[h*”±PÏIÑ%43N0uÓ0߸°Öþ.'0W­Á)t¹ô™ÝêáÖm9á5Ú=ÔŠ`\Ž=I8ØjÊ{Q–v…í³¦ûfYðãŸåÙó«$;OÔÂ9“Nþ#(ŽšS€ÒÑ¿v}N¾Ö*¨t7ìx°4/]Dà )T«8ñjÌ f±R»# ; ïZ DöžW,ëB›ñËÎôÑ1“]tƒužá ~é ê¸l#– 6 %=ÂKÝÔèkndxŽ+Âeý¥$î…ø´Hºµ¨ÐGÒäµÒ¥ôSÞHÈ­œÕƆR"\(Õ(ëÉk…y;J'j1‰x¨ ,I›,° §ûðêQångh°Ë0(Æà°Ï¿˜C$‘ßïy¯ˆŸLêÙHýÇ8Òäf¦ôÞà9þò„cÒ­ºÝ\ðö<ÛÒ59íG€¡8FIŽä:‡g*ÀÏ¿¿a$¸TqüÃç@`dCqá‚%ƒZûtVÝ.8˜Y9øjV”<‚È䯬&ëÊòP®o>&¦‘çÓÛ­Ý;޽)ŸIÌ{;ÝoÄq.V˜:­O\aÉ@[,íÆuó––ŒÑGŸ¶ÿµ© <þGù7 ÓHÔŽ¸ ÆrŠÓÉKÃW)>/øˆ¤ S Â-ÛP=ú—éÏ—âñ…Œ‰D#Ü++þ|¸ÎLÿ>”THïÂQK`i‹E@ÚrçnÉߺyã«{àyÆ_gªP1OChH|vâªlÕÐX»81Çÿ­ÙV°±Ð ŠÀ§ÝjãN·'·Áá½Ø›DÉ£«Ûe¹ÿÔHË˫ڒ5ý¥šr\ÍéÍòi¹úa+8ô N¦ö\±=Ú_‡fÈ“,Y½œÝvýÝŒ¾"H™ÔÓ¾ð|»m„Þo»¡î--Q›Ã,ï:ýÍðìB²~Ÿ=ÇÂîü‚[c~N6âzÞ8‰¢pì•RŒJXÇe¯nu#ú$ÑVÛ§¢!©a-;g‚@'dRp j};!u@mÞæÅ´Ø‚ëN`GH̶Š$ci§·nsvŒ]¦—v~‚MÑØ!_£,<–Çj›X*²4 Û>^ókàÆJUœ£J¨˜,ÓdŸ2|Î\%ŸÐQÂâ˜a íXh;ª˜°î¥àUPâù.âÐuX‘´’þcêBN÷µ–1ÓöΕ˜^Þ 3Ø‚rÇ‚V´ÐxËÅ`)jHÕ“a'1ß™ðÁ@tÊßð Ü­ÛPÑ|ÐP¥L‚„cù}áÄáj5 Þþp(JÒð‰"Àtd‘}¸±ý–6tÎû¸‡­Žú^B–´X=9rÙ§rþ—š4vmÍ5X¹í%EcQŠù¶~ê®òs„]9JKÌj5Ú`7¼äøÇP¥Tmz(ÿ¶B,Ô˜Ñí+£1sq´O÷ªÃ–(мôW°šá¼üÆqK˜Uk}°ö5r5BÀªè¿:ï|Ú„/¾~5wš"û±ÝšôX²iŽÕîÜÒÅå`±G‡ÿ6k†ùѧEkUm[Áɯ¨•¤”ÓÀ^öTTQ…Wv¦ä¾mKZ±¼ ì×IÓú𬮮'#v<ð|»¸ è>ì…Ä£×ÚbÕŽ3<°ÎovL„ô£øóF,ùÿ\ª+ØÉSѲô†Åš´úBNlf{×ă…Ød0Џê+¯ <«Žßé`•$Öôì>ú$¢ü£…µÜéTøÞóSx›ým (,ÃèOVµz`7Þî3 îŒážüíbï‹w¡kÿ|7Jjhôë×è@W$"›ëS¾»:žƒÎ[=²ÿhCâŒ@ï=z¸½ùÛ3Xªn/CºDWV³|¶P£Ø{3´ V¿fBÊ[4äxý ¸ Ÿº»^™­üÜ+‹üà‘ù£ƒð’¥æ‘Rñç].Ûäu‰¯¡ïÂmæ.Ñ’ÜÕÃ^ö°[¥býo÷˪S‘3ä ò‡ ÈlZIÓ"ìŠP˜±hcžËþ;]åg1Ù,åÀ²ö­ ¿Ÿjmìú–uêø’+ߺ£¶íudrntFš(Ã>Rä ÉÈ„Î_±ò°y]äJÜA µ–|Ô,zg2ƒã1ïÓgGGšcø£†þ‹‚q=P)X2žøøöQ'Ðö~T¥i¶Uhlœv?J%}çê¬àÓ–Ã&ïüß&J½õ6WûWÔò…`@Í…‡¸7 Ù•a3Û IñFCž?ç-ƒ-n~4ƒ½VÞ$»ÆhAáÿéEÏäÖbÓº8þÊEbáÅ­újð¼×9«»âÑ©{ÑÏU‘S±)¤Yêq€#σÒDh`¿: œQ¯¨5ÞZ:â°f“Ù9²»œ4sŒe=ãwÚ*Û^¨ÖܻŠæYšÏ]ýª£àŠä=:7œqž µóï{|®ºôRÌ=÷~Ä"^eýŠô†ˆ·dÆOŒ¸ Õ!¿<$Žw®[SœéȦ:-°5g|‡ ÝÚ5¬³¼?P×j"f7–AÎûâÞ‚ÞE  œ|öš1(Þ£%,Š ®n,h½±3Èí…uþ£˜ ØHÚï2k5,¿‘ò"­8”[q>=?»,ýNaZÿ YöeÀwl é>\†Þyݦ÷˜]‡Ã«¬r¬ÂýHÁت‹¤à ·º /Ð<‡X³Z($t~˜•£œ§SÜñÚÈ_BÍ6H †¸¦dоTì-Ë̶K €:Ïw¢xÓCwdñ¬ øŽP9ÿhEÖPõÃ,NªµÒ.Â"Ï%0<çE?A›¨—cøÃ2v*¥gAŸ9 KôV¯V€J¿² _düªH´Ýz0õd¹s!ÃAúõí,R/‘øcxËÜ ƒNÝSlËj5!‚Q®âmW‡·ü6Ѭ¦DhW$p –—bTÑLÈû¶MNä]No}[oeü.úÒ½¬Í2>µ‘¥'Z‹´N­?0„‰ #ºtïD ¥";ßۚώ‹R9°‚8jBSå1”€™ ¶üõ.¯>É9gðVìÚ%M *ü“C2½8ýÖ’ÇÆStÔjÕÈõA¦®w_íÂ5¸Ò°o¢´H†£e{3¡wüš¾—pŠ+i“¡é:|ÂÎ?ܪ]½/}ÚyÅT~Wm¹KRo=è`þP/pÚÒmXVJžVÜÁ ŒlUfážõ…yN‡Ç8îÎ’Ê ©`Âñý–óé ®#Å粄'N¬É3°yƒìõ”øTŽý2>ýš·‹TèØº+ÇhS^¿OûwŽÜk¬Xè ×ÇLUyäh®ûó­ ðíœ$–´¶ÔÓØä’à3ç¤ AÊ‹ö~­±ª3z¼ø© ”T‘W‘h‡÷Réïmþf˜ý®‚Æ”¯ò 4)Áj Yj,auÀ“ñé7úD %Tïn.Êb…³àDÜbjˆáʨx—‰~kϪz¯DÃ`P1®5e*Òü¦Äó¾úõvV³Ë*Ø{«Šø±-~F…ê%Ø•^*á¬>œ”Ð)G°·ö‹-2Ѓ91žY¼€ÇC£âÞ’Æ×,D€ë^ÐÆÃ‰`ªš/9Á0Šý}ò§è¶o½,ŸØ~—úcœ“q{‚†öú]ŒüaX;of 7´0RøÚòAè=_Ö@ +¸¢=Û=)n Afg‚j¼£.+Ù…A2;H©“ ã ò'šxgÐÈ{Üf&éŸ^iŽ"&øó9¬V‹£ÀX˜[‡ÔšKª8êówT‘Z‚Fþ§X±ˆŸAšðZƒE4­ ±×§b™RÎîÒPBÉο m£"1¢I†¹¦ y…f’ÉFG†‚Æ!+;Xùàìo⨮r¶(+6Úa5·öŸu¹ ÞàQìú/;X¸áµ¢ aJª_Ë I¬ÚM‰W"/jûT³èóo›ª^èd‘M¢åà}GY2± Òa÷Â^²º2X¦©¨R攊Çud苨ÅW±ÐÑfª1ßÅï/@Ów¶¦;ï#’4E7ö²q‘˜‡¿<]<·Úw 3v?J¾oæ–0;BWÉæH¥59ÆYli_k´uÒH0v¥-xi4eL!êçÕ†òfƾÒ05–!A(ü$Š•/ÏoóÅÛDƒA&ÏH_mÃÊRÏRËLñ$}Õ ð=°=èôLåäG {)sŽBũԯêeç4ªÊ½G }*G»¾™9ùÄŠ×üV:ë±÷§9ÞÁ¯Pƒ  ‘Ó±V|PßHà}Œ®ÞUãúñ ì RuÓN_Ö«HÛ9i<¾ (yx.”ö¨KU/OBñÙŒ'}ˆ3Ò?LV¯þ<Ù#ÊM«:ÀÁÇ^²p%¢OˆÖàÛìÒ‰×Ìe.¬Œ Ô¤.Â9FÊÌZºªHC”h- d7F#>^¨K–[‚Ê0Ú"˜Â4h[Úݳ§jãæÃаBÛ …&¨êGjõx¬¢Î~ÆfÑz}Ö‰)—ÑI>:C¶ùåF{!?«J;nGô«Èj>ÎylÙY×›Û÷`Ê¡miÎÁ×ÈéP¦x!ËJV ˆCÏ ¾¥%*°¶Û, ^ALíWÄ¡þP•žmñx5mé¯eêÉÔ϶ÌkaŒuæœw@“b4’CwzÕGðA~ÃQÚÐ:6Rk<Úâ [»¶Jb(Äèq(|k‡ßW\Ï R38y=È[ à×2.¬—EßïbÑK¹Âzl?bF’”îïãnšVö{¡=—Ù[ðH·¯cùDí¢ôêvÁÈÆ*»³æŽ!®d glh@‹0t)…Ô×TjM÷sVñtm`1‰}Qú Ã08A}Š©#óãQö$&K,, ¬:)§­°b¶…Ê •íR>¥fFd.ã¢K…*z’~þöè"až«O&‡ÏRÖ#N»¤±1¶ÃÜà=mY>£¾§£w`Õùô?j_°–<›uuß|Ètw)@‰‘úÔߎ-ùQ·ÊF¿b.Ì"egŽ×'ø#t.FSápÐ`ºjÌx‰íx£–â¤Æ¦¦Ý\–[3eµ’Â..D›á퇟¾}Çi'ÉÅ$õ¿òL§?(ð DàdN;K á¦KÞ§¶ô9ü* :@µËçÁþÁ€Û»0Xê%P4¹|ñýŠœÄæàÀ,Ïæ«xê±^jïòiCítßä“Uq[ßì”üÚ¸¶æéXÆã¸Y»LŽB´K^-#-ÕuG.Ô¤S' F  ž ÝÚ-b*ø Î=wÀü|nÂKCÔÖÅ? ,ºU0J‚šd'F¯%—h³ÝÅ-øÅ!7\íÅSRl§|ü6<( =·áü;©û¢¡ÛsíëŽÕ ¼ÃVroÿ³¾ù+=³5nÕKRˆ>E­K=¨À2<ÓDôØJNõÎ=¿_sbå3ùhÚ[zºöÄFáKt)ÉÏ[G4;D%}<cƒ|÷³pÞ­à9Ö†«Ë˜t[„~Oqç²DEã$À&»ŒVt]'}³< ¥ò[Ûôÿ MÞRÅqºOir…Qß- 0{l-r+eÉÑ~Ÿ¡Ÿ}n2¶²Úä©rèÿ½D.–„^D—Cσ$³ÚVpÓœÄ[Ä”i§e' Ki/À ÷^% RÈiÖò~Êb©µ¾ó­ ±5æË \ú¿L¨e( xæ¼Ê°(b «_Çö?ÑÃÅÉ@ñéN¬ˆûUAoéÊæ.àgM K†{Jdê„§w0Åñ_”›¸¬…j}ý‚ʈ%(ªu™XËÂÙÂÜkç±ho€ýT’Ž­D‡(ëºî”@÷ü/B“†‡¹ï¸SŸ.È39ë;ˆ¹ÁŽƒX(„û·dôæµ¹«½o31¶¿‘ÕÕúìÔ¥„µÁgÍJG ì]¿TWúL~íÅö*¤â ljó¤äø+•I­(`ö2iÉlÔñæï3-¤±á ÉÕX¥Iê ÛóœDPJf_ÏùJ¶‘C›óëSà {”–Ë©ØÅG¾Ìmç¡„¸ò4~h&«.'ÐSiaÎ!ÙE‚…:,„Q03ã?1.!Œ¡w.íeBÕÃɱÎŽÇ‚ó >{@À(ׯxȬIìߨ‘ (-#ë‹o.‘꽆JæA`c±—£¼æ”=c‘ÝiäãÒ3©€’×ÞІ‚¿Ê‰kÊW/¡„ÖaŠÍ†”¬<(‰¼îÜ´Æ×&/j?ÁÈ•ªžØ7 Ï|^§à4ëº[8Ïø}¶-K48ôuÈ+]ü°“× -—6™9QKößnˆÅ:;v 3K*²Åy] Žìë t†#?v¾•*÷X)Ðåã W!hÊõ}è¨å3š—e¾ÂQ» ˜}x”£¿[á¥ïw|ÑLµk{j~ ¿Ý ¿î{>F_JyRð$ÙÉ_Fªç@½jÒ¬ëKú?É™­eæŸy´Á@Àk´–›½PUV‡º:=ÁÓEIzÆBOoæÉûgÒYë¢ðÓ‘^ rW¼v! 3Q—¨,ÜÙwl‹ü R:´@øšÁ›£4n›u5[Uͧ[ÃÌœä».ôΙ‹‡DL\,ƒzn ePý¾h‰Í·Ï¥JzòqM C§-Úœ¶{¾‹éHÇ@ÏdIf3´L¹±+ï£YS‡àš“-ZÜÜÂnó/Ž´¯]µ[)ü.‡:nLÇq¢ýâ :}˜ºÝƒÒî=¦ëœnFVýiP³!*ÒeôÒâLúVø¬ÆŽz ó<>mR覼¥;ŠÏ”Œ½”<˜sÂc›êî(ï¢Á¢Z[ }ªzbEÎ_'%®§’ËXÝ2ýE¥*rÑUùªÜrÏ–nÌ@< ¤‚伓‹ììQ½nʰÛKÒ{}sÙGÃ!ç}%2˜:ÓĨ*´™Sj‡8\nzÿ‚L*×p³ã.Öo>»!!1œKÛÐ'ïže¶‹<å— ¤ò¿=á˜õêÄþö2m¡Õ¡H {Yà€0"®¹@”#ŸýFë>×ÜAEÿl“²±2'¯9g†G,üqÆn›ñ1ªÇCûKéŽÝP¼ØY´·¿Ñd§$%®†ð{ûÌL(ÄíDl dn+ÚäÃ%äs×›ö »ÿCLù¨Í¾þçßK˜Pó—ÞKÏ‘wJòà»ó¦Ñ½ãAù‡…aEÆ¡îujdSؽQ6°’~|3)‹5| f¡Á®,®²”åÔO°° z‚ñ¥v__ðË*kNAšE[Á†Å´ Žc?¨·òôPMV`$‚îæ÷OFðV…Jôþw¹IÖ'šDÖ0/Ã9ÿØÆX µähò¡z:f<µxX\ú^N™„؂爠o }­5Ù}…:ÍtµÛdWÕÃÆÈ´×³D ¯ ‰#4H¥8ò*’Ë“Šóê­—¾Dò®4öR„n:¬¤ÃÓì06ÕGÁLÂh /ÚtzM Èû¹‰m:‘¼¬¨lÜ+ˆ´ÐGpÿºœá¦µÎÎ7òTÌ< ÕÆE¢ªM!NL0*XW“î'nDûýçÓ¡ %ª ñ49;…}»»ÉÖT4ÒµØ~4ÅØÙc ‡¼ñdHëñ’Âýêë?j&;Õ&'—’EC±¥Y]'L‘s9Z*¢Øë¸“-Sx_P Èžµíôª3l÷g¤‹Õ==þX½Õh‡ØA‚Àbt Ìó7¼Óð16i’z ±B'•¸îö·9O=„ßÜÎt[««“ bYÃûbÈZ6íö‘ù]RÖõ&zr«¶g o£ìÛ áDsƒødR«mª -†&ÝÔâx(·Éäú.yT÷8EŸðèj?y¹ßîÕ)¸yiAEÖÿ¸ÓBTò ù²‹ N0õõJ ù9sÁˆ¾œ?*³%Ž0`y‹Håqû‰_î*íÄáT=³9÷L^#°v _â øŠz+-“)ðz(ì§C%ä`þwSA…pŠî²GÞ3‚¸-h›¿U™7ûE¤ª±óU\÷§Y®¡$Ãʦ{èz€U¾K%£bãfÖf¥á¢ðô‚û2žAzæD`«•Í&hLS˜Nœa$éÈáôl¶ùøÉoÍ€¾ÆêÈG’<Ù–—È_Ø»!¢[·xs֋ɹIûŽbUžì¤©ä¿ïðPÑ*µӗ縓O+>úk²ü£§gÉý$—<ÿÄþCáw‡Äl¥:"²—Ï0;æÑÈ€›*8‚¢t#;³ O{U‡‡ÜÝoóÏ…GZÔ6A(mL¸ ¼:¾ Ï|WÄ(ux™Ñ¬"3?».”|Fʘ–M¯0–4-ZlZýÖ" 8ºž õýVR €7A¶ÐlÞãgÖIt…NÈ­V¿[v„þT/N ±e5%!†õ„é(ÅÓ©FÊïg©A¸N;N…G3'à´…܈~”ÎtPvRB–»iÀ©­ñ´ bõ ªl_LÈ Ä:<\IÉ>t !f'¢3Ú(–."9ãKÿ=íNiÂÈ~ää)d!]quø¬ž¶Úû!OÌyÏk=yÒDÖ£®P[wRˆfönKPÈ]qJlϨ#fUe…|}I!â™ÿ¨}aÕF£ðoùUxЊtÖK%s€µ¸:Ï[ÈÖƒ"UŸh¬ k²Ç’k¦Ò7Åj¡8v°nn"g8׳…Œçb&÷ñU|†ò*ãÝvÃ{¹çôì¾öñF–ÌÀ–b˜‰ŽJ°LÉe±Ì¿“k!¯€\¨Aæ†mDÁÁâ>z=Ï/a"¶v{',ëòïa¿ ļì×W«ÝçMQ˜B Gǧ³öþ38¡ô PTMÅtÐt œ”&.§ÝKúä‘¶RlCI÷€Ô¤÷äßD_`³ üÒe… åëù{5,¾W· ZÉü&…eß[þ¨÷§v¥"ëÒ€g{{ðMÑæD”´ÜšÄ«rÓê]DcmJ?ÑPÙ Ädùmryi–ÖH.ãŽ(¥ù=½‡­ÜR‘\eu†êžLÑ.¦³ÊâEkÅ6$n¦dÖÕ¥ëÀb^Âß)÷}ÁËôã[hô=û×e)^ªü‹Gäß¿ÅB¶ÎƾBà5KÓ™~<ž×©…^ ÁóÍX‰¢¤Q˜¶++@t< üCªæTË‘WËRÿÌwÅñÚU„Uj„ÏóÆ ¹”t;·iø`ýLz­*h—âéOQ´Ñiÿ¼+¯Ì;G[W#<Ûî˜Zü;ûÌûýðõa‡±š/GA6–"dÌ´žö´€Þ v¥(x¿áj(ØÕ[ÕoE·”=$µíƒ~oÉ­³ÎVTú?dÐYWÂJ:˜ê1y6ôÂá”r§y°ý?Ãæy×°4}ñg¶+Õ(Ø9â³Ɖ²ªuϧg òp”½ÃWHÉ1_™;€Í»Øw¬ \ï—ˆÁ4o®'}P4@ðV²9ãÿt½VpUj^>·‚•[è¡V ÿ<ð2¯(]”7Ó’3À ,XiŒÀòaQ”‹ ʼnw5þÖ¥yXÿ'4NŒVb"›vÎÃXjwr…Ívž3Ä5ªMKÙS§¦ !œ0$(,Ýÿx¶<ø×›Ø$Rß.|š›™…8š’Â0ñ†ýÉg6ö½8’é¹úÝX3@‰"¤„'µâL2×9 ;eš8Æ‘Xƒ–¤ˆqÁÞ ¨ ™3£¯(§Xk4ô¥.ý Û³¿¿Æ¼Fœ¢Å¨Â8¼ò‡Ðâ]Ø‹k¶ükòø¥˜Øòµ§)€W ¯ŠýR, Œå”›¢0¿KiØ,¨— –ë“y®mËjx¤“ÁŒp\v×BÎ\d«p“´ ÷fxéo ÇûDª lÿNˆ›˜KÀök[îºH?Z§'~A(.“H‡ÐÕ™<ÊÊìE!cê&ØÍ;O„²}Ø1 (-ì0±{:Ÿ¡¡‰øÖ€žÔÒ;‚vªèÒB>Þ²*4Ëbiã‰fšù€¸åã¤\ MðûLkðübÊ~ùèmTM7›õ+ø}iDñRR}Ìš‘F»Ó1Hî”Zt‰çÓƒ[½´_x‡G³¤d;˜ÝglMâ‡jWA<–¬ï+üû˜vdDë­zr¸'-uy¼Èó¢&­ýkwe E5¼€¬ãTˆ.¢G)½¬ˆ©<ò³É9ö Û\k=Q¿¦«|7º^s³ Ògœ¦P< €R™º°¸Ô½~ç]Œii?œ`ºD³1 ¶,Ñ.ã¢Ã—Mßhÿ=»rÖfn¸ÃªZô•è6(åÄwÛ[ÚÐÙ`b†•^Š'za5´à)¢7ïK÷[ˆÍQœª7È5ÌÅGWÍ"%xös”X Î4WŒ'¾u4úxŸèla ¯Yð…†‘;†6ˆSÄ­•–ÔÏg¢”íŸ6/ÞžvC°ÞI$~K5H:o®QÛ4[ùvR$nVD s@´%ˆ—ï…$È„^xþâˆ:é.X÷yß^{½Þè\†Á¬=¹‘yP"3HÚÑI*¯ÊEv[¨–¶A´ø ¼ ð¦AÔõÃ(Ý!û+]î²û‹PµœÞŒTø…4ž¨=—z÷¸±h0Fè ’ˆ/ȼÓù¥]ræÛuíôíqåÿS0ünªzÈ×À­ñÁ ïãÑßL®mˆ³ Ç9Ÿ{b^R4¥íñDùûèì%|ÍŸîÕW³Se2÷÷üKžœÞ˜4WbÝzpT7‰g›Óó ªà{„‹Ã¬Ä0Ö&s >þw¶µõÂZtÛšûó$)p\2Ím³#aª³™4‹Ô7€Ðßý’ØŸˆˆÏ2:Ý8n9±©ñ™m÷ËÁ'»±šH¼dßç©[$NÒ£ê,´‘Pc!\q|ˆ÷™ŸóÂJgih òëy0`¾2ºPRÀ·¹3µ#Ž|áÌ«ýtŠà $>“z›nü¥Ä9p°X©g}ÊvÅÉ’bí $ñYŽ¿ÞÈÚ”F¤N :Týs’˜Ã›TXÌ&ПÞ/…<à«€[|w7žÓ¾—ßÉI‚UaCº¬éÁCd±¿„Ç óÓu0Ëùš&î¨y˜Ðoø‚ؽEÔ„k;…>ÝPšÐÓ½âý'·‘ÓN4À<£²úƒnðƒæ¢µ­_;|£†ÿ/E;ôAG¤aá=^Ž:3ØfQöP—@†3öû¦Uàè;Qg÷TªÖ†=ëQ´­V%œ0銈VŸ)þP|Žˆ!Å|tOjxš¨ÞD´ê“§‰¤ë‡Hee0öæxÔ‹¸[a‹Çi™•†> Þ³ Å8-Ω †u^šƒÍiØ=WìßÜ"S[#ªè}æ‚î)¨c"L#ñý ¯SÙFÀ帴÷ðæk8 u°pΗü&/;Ê+à »ä³Ù ,Eò¾rÞØ«+þ„þ XKǤ¡hÌôØ" ’e2$kþŠ,@ºáæ ä®âQòbû1Òõ/™pT3Á6”‡Æ?Âïx5Ê))˜µ‘¬0®B¡o1bGž#SG¨uœ.´ñºa‹8™ú4–³Üý¤¤ÐqXùv#Ui³l²D{qUÁOD£-o»bÒOýZlø0ÚAÖzXÂ}Û3È\R³SCÉ”­nZ½mn$‡Z ôµÜºÌptE¿8´Ó¸…ú[}êÕ¹§mæ ñ¤œÙt*cC…pEزá;‹Y¯{5Ð]mSzç•b»Þ­÷H¯tÏé3êâÕ¿jŠ2ìûgëqËð”qÒu¦›¼†#.MÌnUííVØj»'8ª±½;å.RÉ´ÂþY}ZJ2 ¯¾•vÙ7`çÊÇ»¦)e{†þ°^s+…Ü`K;Þ1ªÂ 2jâ(i_µ,¢Û)l‡“éJ˜^Y›‘[©ö˜ÖÆØ  €à³C›ObCÑìž5’4Åcû®õÄýå°³¿SO/ôÏ s·ª¦8ô‚’Ôꉷ“ô¢˜c´wÈgeŠöT‡=Õó´È3v„o@4ž´+üçgNÙ†%¦~Zà9 ü_Á!aüHAq>«vÐüªD§íÐýp.ËÁ v“WÛs‚â7XÒîÅrª(„IwÂìÆ¨€s€ôK ”îîç:ÜK·çÌSëJÊÿ—`'&5¬còÚÇ¿…ÅjÇ®m[zÅ— }‚ 3½ÈWXõ¿®ƒ ÕX‘©]Àͺ&„0Å _ø‰}º¨m)çWGÁ‡ž}•j4wÀ nëಧpì8_@Ðéá;Áÿz6=öªúä~Þ3 [iJá?MóOTz±'sÚΞm›'÷ŠxûDeyjüÝYqÆ´–åfˆ;kÌìi»ÜSá?v—<5U„Wu‘øå®?ªÛŠ%t¶ÍíúŸ* ®ç.©¨ï·Ï*cñƒúí*ãó!ýчø"pÌó÷¼X•ÇŠ) ¸] Ãí)|Úl]*Ÿõ}¯÷¥x%₎J%zõkQD ü±‡íþæ—)÷òËœL;H!„[l¦<‘%[>·{+£6@œÕo*˜îÌ8x1!Ú„8šœÀîà8`nÖCèþW ,?!»éÇW#x«ðs•WÇÁ)8ÞWYØ´e'oEÝ“N÷‹ÌÞÅ6Ñþ@evâk/áÍœB½?iC•ÓÐckó3ÑškıûdA@M¿Bûe¥f€ãæP.(ŒÒ-ÿãNvU^â sÑÖ1FBÕ‰ÞýpêÈGÁår3û†â–¸'Ô¡ƒ«vêwòË)¹ rº†úrÇd‡.õO&ˆc<.”@m ³×ódO¢¢B²yx2³¤-ŠH²øq¢Âæf*÷ë§1KMÔ€®#òªŠŒí&†Ê÷-;ž„¯¬—ákîøóHÐ¥—";#­ ìߟx yûã¯þýÐåšrÛR;WkéWzܧT Q†0ò¯p­Œ’F¨ãÞ2§°´_XÅ/ØîÜȈ›†-E¢Ò.ˆp6ûRýÉIÜü l†íš:Pùc`< Ó¨¼µ Õf¸Ú’Í2·HsLÒRéÂW·M_íM݈ç÷MB³ýÄ^¡*¯§w eaZ§Ÿb3­,*Z Žûp%â-`–¯YQBk%ý£H”2U¬jc¶àæ%Ú¨|\‘«Ê÷ŠXfwùø­¡Tg5Ùه׉ëpÚ¢3ýÚPŒüËÚ5tö 4 "Ü×-hÓ¤Sº_£®¡æ¨'4Öi’³`šM}’9œñÃÒ©@|zÉÅp_6“¼mpª¸¬[Q±ï¥¤½jg¸$ÈW”º0ìõ|™Æ_aI€ºß Ö‰§¤Bjýtìé¾'l‡þÕ/Wî£Ú¿Tþv[àœcBN)•kB“Ý>ôÍÎ9oÈø.­á$]-T¶99C%:˜*§” ßbØ{+[ÿ4T¼“z<¥ V\§œYq4ãÕ‰7¨ï ´À-¹¡6ÐêÈÿšC=sb /‹wmI6öÉ-’“œ™ÚXfxcZÉ*·RRv Ó×ñødžû.²ðØÛÿp^óä7òø0îò$/IÀá.“÷èpÖUÖTµ<ðD81ètË J-EX]® D;0XÜÈ9YêÝ^¤há|TŒ&NOCOBZw'·Ô^d’¨ùåf¥Û'+6À ·¾Å3°RÖ—£ö?s\½ý!#áâ }O0Ÿž %„¾±ÜZíÔ² i¸­ÇYQÈ&7GJaÙ¡^Lqg¥•þR¥Røpiq™H/ókJMUßS[ªœÜ8™cE‘þÛ~-œ€Žšs\@aX Z¡³´)ºæ¶YQ;f Fø‡u¤vÞ2~r¿³SZŸPÎÿHÖ]5&£c}Á0¯:®ty;ˆ´ˆ­ø;½3S'oÞ#Sùåp%<›ûbw£‘vð÷åätpÎrÍ‘Ä=nzL„Åý¦£Ï¢#Ü|Õáå¿Ú±—Æé%ÕMW:KÎÀ‚Ñï n» MH_5º¾Ù–=œwT>èNuÅ À×èy\ïÆÒH'å"íôû¥T>ëì@_œ A{-QŠjx¦|³³SÊó‹ø_ú­6qâ8ž©¼·u¸ôoà”ÏÞVR›6-‹^Éor>®i{eú<(Û°)˜¥'Gv_ùá«ç¥S»Xzp¡ajCjrÙÑP­XgPE®I¡|™Ù³[¡òõzë@Ïu8•€µÙ˜óÌ!÷0óÀNš¨äÝ&rWûèGÈþªUÈØAÜ(uæË½ ëAݼRžÄû?<«¸¥´pðWêÀŽåïf÷®i ¬¯ùuWvØÙÜÐUâêK»jÞǘô½ìxm)Œ¼§«ãê®6Žü]ð–ðõñþ žž±;E•îDÍ]u´Þ´õ®câ’Ò5M+dhsŸGkÌì¼c›¡úlÊ%žF»p¬`è¾@0 ǬBn >G!¥û­#{Æ_Š·Æé“¥TÓp~Ó…²å ›š[»Ìq-_-hdxÆü¤«.k­˜j¬a ‚ÐÁ?ö3µý!§)øi¼ú‘`;Àó5¤|ÇN'0=\þjþ‰È¯;3zñ¾f]3ùÄ<=<Å{I0©Ø£ Ž~âæÃmRÖ™^Û&o2ဌƒΩoÎÙÅV¯i‚µ®(«›;g_d{UÊÛãÉ&{~Øô™f*[»;>æÕ6ŒS-6á”±GøL®ãc{1(UË]~cwKRùH¾äF%ÀËå«·{s Ú^ªfNëô?¦Ø°/Î}Åg_ó>";Ë/4w:»ý9uýºÕ<€€eüÔD³Ámžâ¤¹8h¹r=/”ß¡8sÍ5¢â¹Rº±­›¯Ëÿ,lEïm¹øöكĀË[F`KÉ 錦¦E\s‡ˆÔ€výX—{M%Sw^v¸w¨ù]ðRL4PákU÷Ëh°úÆGc±5{aâŸú‚Y‡¯:ü қʎÕ[8ª{Ql)´„õ(Zù&¯ £yÓÝnßÒݱ—b >h46´þX2ÝRÝ#á\t8ƒ‹RU|“¨)R`×ROÁ.qZüêHäÏbdU¨˜,3m}³®xkKƒÄ´¢9Q‚°¢°Éß b<¯'°gÓ©ƒƒèÇÿøà#ÃÉ­—½Râ<òÿfpøÞò K@#”Oo ,TÒƒÀøz8 •á†2 6£•ØØ¨žÑcï¯hœ¿ÁåÀê+uˆ%v£ D’ñÃø÷üÇŸw#£ZÕ,'3öÑ©±üI$ö„8šöÿK¾œ­íi]ÙJÆ7Áå>ÄÅÖºt 4 #ð³'Ù­‘¨ÙÀAèú©ÝãBÞ!Ÿû²A.†ÛTMšS± /X¡‡Ã%§Öþg ã’#qßã+Ð.ᣟ´ ´oî±”-B |VÁÓ4 Q‚xémqñ!iÂeÂá"uÙºÓúUJ'1TÈe-cáÀ+6©û ˜A+·×C&Té'c#d<øaüIî¿ Þ*²$Ñš-Ô„=ËW4­a;ŽèÜT+Ç­m¢á¿éË%,6÷«WÒÁë1à.%¬]=ìaù›“`jA)^Lݼ .Sý祺 vw^8ŸB‘>0çÚ-â#ÆýrD>m¡ u5€w%öÑceD´+¸ÑZÒ$ž½ öàCèd{úm›žQqg–ˆ¯dAl#¹X:‡ÏR²ßU ¾~?¼ [î†É4£Š VÙã6ȶö³–þóf˜"¡iz²\ŽÃËóBÀPI#s¤>g¾F?éÎȪ`Œz|ÅQ”9ò¶wbÌ'Ùl˜}6ïsÐÃftÓ×éͧk”ÃØUÉr3Îù+ËÂäÐ1Ý–êc— ™EìÌSc>7VzïÛ™ò…°/PÙÕXC¬Ab-óDQýáòR9ð霖âÌwÂè~µŸZzóƒBµ}<ÔH Nœ³ÕœKÇ avéäñr"µ±Û™è¯Êäd‹t E)a]Ëý¯G«ñM‘ëV$qÁÃGø[ò1°4˜¨mƒz¥Jž-b*Z¬Ý)²g<-sù]  Ëmà‡taµ *¹X N^ÚÔyøráéðyUdê¯Éß«\)f0—M‚FSýçóç*€6èñ±¤ÍD¢‰_³¿ZÛï6øüœySÙ¸¸‘ÁæÁOdºnÄcØ*w:"ñ6y ¿–ö{Ö¥%ö]à"ç~´~óÈ^‰/y  ›°=‰2Æ]âl| Š”Ä/¸Òø·/3{F‰Qu²¿–¨¼îP}ï]»SX8~ .˜Eë‰ÌË;õÈ« %&ÚÁúEB| .禧q„üïãË »±a°ç<¼:nU¶„CgM–šü]%ÁÕ®À¼]ì»Ó²sÃ[)!==ØD>WI[ÑŠ>‡5Ñ–¬ï­ÛIÁ'½ ÄExÚúp„‰ÆôàìýêýÔê@¿|ßæ’ÛTGÂL¨":”ÆA^ Vƒ­äU •æi­ëWÛ%_ô¹UoK(8ZØ®Óõ;$«.Tn° œ¢çªlî ;Ÿ`'æZáñ¦êƒ˜íKîXÕo dÀÿküå– —·†×­`Øü þ9/hw™aò…J¾ Occ–ØVaîp;YŸw•móõ—ñŸ0­ÊòwµiÜÁ/Xñ¥`àm×];C£Ù\Ø^x³î•ô‰å{ß_º‡ç‘­Ú¢MpÙCµwÕïáHõ¿& eÇlôõ‡‚ØŽ(ö¶ÉdªWtF²µ>!¶0 .®çšOäù뱚mÁàpº¨[4VgËüU劥᱊Yç4ß¾]/Ôâ7É=kWÆh«(4Å#Ô~j×iZjyDÎR¸DHáqрѶiNUmШ³¹€(@$j"tqÞ¦Mˆ:{útM硨NXRïEŽuùÖ 8 X×Ú…›¡vv¢ºŸ˜Âšî¡uƒ"äG×ItjÙ^Ø<1u°¿>6Z5mM§\muÛïè h¨¨;Zª–åE\"ÁÇláæî6ëµû‘y¹sBäÉœOŠèãÂT“Xù–Ÿ}&ôçvk%ƶºyP’‰Ÿp˜B&ùW“e’îÖnwti'´µªµæt8bÔWj€È–Ò¿AìŽ!AP›Ži-Ðÿ`´å‹9†CèìuHò€Oµ&#ÜbŠrNӽхIpY þ:!'ŽRs©4ávYm¯?¬ë´vk=³hÐø³~p¥üÈMPp¨x^q6áîÒò}LAÔ ×2nêyìÅw.Ð:Á#,’ûmײˆ¤xÜg¬ý=Æá[Ó¤¬åò¸q¯Ìáý%Â!Nï‡mi?nwð«…ч0jêÝý®‰)½™}””££;ü穎eI\XÐØPô£ß2V—“6Bi#ÃÚ QµyÜUžq$ߟ¬ïô†è‚âBáΑ ¦Ú—æôÉ#ª’x¨æ5¼:X Fà=¨2%ÎX$wo¸áøjL±òzßD<Ä´ü?» /Úl#×2âÅÛ{= §÷ÑòrB~]Óà4%Ÿot\ê_Éì¸/”âƒ,Q†Ç)<­qkÖ&ÑÁ‘oÀÔpŒygÔ¨ÒäSÿ…w•n½©¨ Q¾`‡=sU"ëÈ»]Ki{I€¯z ¢®7mPKœ>R¥&gx¥ÒÐtVdÅ Š™=±d¸Kü(ç]çÒ~sëÓ5és·*ñæ[»Œ\Bsw côIT"í-;HRöË@o„:c5ßÂ*¢Î’ˆù64ÒÁÚUk]’ˆjqÑ­úyˆº ¸¶G_¢ßÉ×/ k¸&J½o•Í·eá ŠÃ‹o'„ÿ6“çª ¬0ècdˆz‚ËLón@ÊÊif/®ôžVšÙž' A*SÄ&Z¥ô}}aH|ÜJUŠ'2ZcI°ªØ«QöU Äìpbߛ܉ :žK»5öawò£Úë}Ön/b–8µÓ?îá)û6(.óU¢:¹´úû‚OŽÂ¸ ’Š3ú‘„æxŸåœÙMvGmw(ÎUQ@_Ki”˜9Q/p܉¸Æ'ÉÜFZñ%ÀÒ}Š3àöe†ÆÑ;áï Ðc†ÃÜVGáº5òO¯"PiˆŠó¥°lÉñìR¹bmÏFØA—ô‡ûør`TT4Aï@mƒâŽrê,tòü ©zã=$½ o©Ý¸2K˜·“fOf¾ú¨ëϸéäüÖ¯ÏÁÚÆ¿âX ì µß7À‚_“ 6–-ä+ÞRAGFù߸mÓÆ#°E ‡ Â¼zvÙûõ €¤·X$­„óp¤U;p}I‡«E7À¦É+‡«mò}.%Q.¯Ö¯^Åë~ø¥ÂÐÔôÔÅÏ ç€v‰¡… ÈUÜAµüÝ”+òY#+áf”{Î=±­>ÜàhÒÁƒh)û¢´ù±“Z-ƒ¨eùûЊ=uÎ5©Ë s\¡àÝÉ`M°Š~¾) Xr°¬eº)&ÕÎc#w¿ÉŽL >1i#yeCøbd9ÿ×\>˜Qv‚¤çVY, $ö–4„•¯8²v¥='…ä.™tbáLJVEîÅrº¤íÒ³<=W®¹1Žø"‡öma([ ÅM5™ékNM7 Ná7}Úˆ¦]•ò™¢pÊVnð“U)Áót´í+­@I¹l€æõž@i í‡ü²–º™OŒDòì×”ùq,ŒLM%Sä4™bz’ÚAÑ…¨"õó‘œÑæ L™åT ßy¦g1ÅHÉnÚX‰D‚;¿†ÙÆjù¹P¼ê˜çZ;Êý&Sñ8åáÇêy㉓%sŠíÜG+…%ýkJ]'ïà°ÂÓŽšg-8/S(ltp/ƒÕ@LC>×á w{Ù]wU Å~Ãgm™·Jp5íǰ‚_¼c¦ÏAPdF ½Ë ü lTg?fÊžµ[µ@X2ñ`¿óÅâ¨Eã‹…’þMãÜDãíUþ¥}7Q*dRæh¥sôãɹ<…¼xC $¤I›gd_b‚BHÔýÑä àæ¡Xúv•[âD§‘ºÀí5@T4 }ÄSz@š¿œcé9žŽ'þ‹‚ÑN”à"ô¤Œ"6ÅQLà¸v' ê']‹ãYÈþ¤wƒº'Ðâeʦ~,vÀ7jÒ×64–ƒVc®Zý=ª„ËùÎÙicΠb|>pºàž»Gc æI®Üd^âÔ‘;Ï–ÙÑ•‚ª,Œ ]ßR×í]å hÂ’í ³Ä2É<&¸™Îjg"|«ÇÊÐÌ =DU¦\‡ ýÝoæ'†OÖâ&¥¸F~xÎÖ[f­‚™ŒjŽkƒ’sÝ[ ^9ÄBô¾?CIÓhP[f¢2­‹7‡qÄn¤6Ì!HøŽÉŒ¡HÉ¡$'ÕåISŒ#¶J½jý¡¨ÖÆÖx xÿ1˜ïáŠ*+d“½@ëðÆtÑΦ6’¹[^,ªxlíf,¸ô‘9»™T WÑâÑ»wÚ1•ú p¨ kY¡wÅB(à4dȶ‹²H±iU€KÚÊFû¨Ö0ЛßWÒ\£É•×VÝèÇí?Ø!ª;3±^2åoÏtM²¼.d%ɨÑîæ³è '˜ki qøú±:© ‘+!a…dCrïÞÏ ¢«ÍÕ‘+•Sj©˜ÖÆlË£Ñ,_ª~ßïfŸºAg‘ÜÆá§ørÙ2á×ô_S>KPËÓuà*wŠüªM«”wp£n¸€Î8 mFÐÅË~¬M¢¢¿ * ›û´À×Ä|ô)yë’–SEO–}ûdÚ“.½¿ÝÆVQ¤¦= ˜àÓÚf~¶2§«ÏÓV$¶¯xÇÒ^p@ÿ%q–³µ:£Ôiù>NQêÛÓó?5|æsA…9¹•õõðƒ ¢îþÆŒp •Æ«q‡–8Ap'G›ÏN^ð;ärŸé;Á¦¾"Sh¨Ûf/øÕß ”Å=m#¢¬0Ü$÷¦oÔ'çÿ…øF›!§bÞ­†FDn‚×uç¬SPÿ~øú‘ˆÌ|×Üy…õºšŽþ7Åôªƒa…EɘNæq‰ªa2Boz¼hh_²Š›K†œá$¸ +vZ,ÿ˜O)v-pƒO1<Š™òçÆ±–C•|Ûç«A†2Xé3€è™E‰ÑÙ{ή/-u°7ņ ÅÛ‡<é"¤‡»ÀODDQnÊãµô ô›&áàr—AÜíÛ²GëŒu \êÝÝP‰jÛ‚CÕvŽ»%£´v[Ñb%‹šKD„¶°í를ÅoÚûºK[Ä^^ =+—“©ÄÃë›7ÂÂ÷ˆ^R)RyNYò‹b·O¶ØT„å7—b3°MÇhSŠ6”À?e¶Ow²üþ…'ô\çSa@‹„´•}@u9Ã#ïòäD š¨ÂÜn¥bT:xaORòž§Fº½s&ŸâÀJŽìâWàé5Za8ÓQLZÑ÷^G•%Â1æåbbö3·‰j¿V½¹™4»º®ŽÄÚ‚Íé›ã¢ùï×ä}°íƒà‹&;s /$¡?‚’™¥µTc9, N ÉvÀã i)r(?î …£•e]sŒ£éb·èïÍíé+Kz˜¢Þþáô¦«T‰ÀµxŸ×˜|‹ËÒ~>r¢Ã÷@ª=XCÃ"\€SÙ-x(â ÌàRáëâ^à%z÷`7ýí€5´1D/|މœ“3ÎüÔ¥|îú2÷rpG'*l“èF„ê7ßV*<²±ÿ2^7|Ñë$ò‰ÝÖL0î\ˆ+ì`Æñ½‘Ë4u Ñ=1,bWÐ!¸t_Ó±O§à6EUÕ¹ ¿¦ &nU Á‹tŸ-üd‹*Ù©Ó ÜÝë;ÜY#ÿØ*¡·HÄü}ˆ2hN5牕=ˆ˜×YÓLÐwÞÛ÷)úoóÈÄ­Ÿ*Ë mꑌÉ(M™G¥´º€ ³ºf‘ÍùZi’,ñ×ÇÞºL>ƒ€Ùyo¼Û{Vm²‰§ÊS©]“¸Ü}ªÐ|Iýè‘áÏ#-+Ñ[ hüÉ™ºÌ4uÞ·ŸÙpGàŠ»¸âyï 0.8¢@ºp‰è‘ûm³‰¿Œ¶ê?:Ú/þ ½çÌ9i=Lˆe9JT¤÷ÔÚÿïVf½ÓgrHHÊÍG ²˜M·&+žBF"-´ëôl Àmýb…ãjltóOlDVzJÒ?H—£¥%_ãäŠ3,|*‘D¤KЧí<ˆAÞ¿tççñf¡Iž¤Ù»M»Š{‚ýR‹Aþ=¤åüÀè¿™ÁgÛC¡ 5·Þ޳(,óÒÍ‘-bŽÓ;ÄØ[}ÝC®Uy{5W(KÜãC AeßêÛ)±ÚßíEäºtÖY»»‰~ú¾ö7í‚O–'xo±Ÿ!†Ödª‹7C ûÖCC†Ï‘Wé]„·w¸Þr‘V`]¦É#Îh®âáŽk|ŒAIÖVJe (®¹TÖ’Á©Â{ÛæåÓ«Ã> 1ê¢üîmÏì Ó騔ú;² ÿ+3Àê )p¾;âby>ªÅ¡Ãz"G:@þs!ÛϽÿAýæè ‹Â²ú…#u7a“ÅÒÑL`x†üÛ$}Ê1¬ Ò‰f¨.pF­ê‘1N˜–÷[svÝÚUû$u¬ad7pP,$²¶¿oTYcQç¢MÃô$TWßî‡æ¬wD kÛqndO“èÉÙJ%wÅù)ø¹ =ê(ý\¹RZyNdŸ5$߇º ‚³ÞVÖƒ‰GpY» ߢ¢p4ªžî:ùñmðš´’bœÞÎ?™ö/¹Ó†‡Wõ¬AEãÖ¬á4u<Jþ6³ ä,O¬,i6X{ìöaKp3]­¦kÀNj¨N& gnd#à/0Ÿ‡!“ÕwfumüKÆ´$ˆL^†j¥u9B·T(Ýt– ]rc}Æ¡ºZÒTí(­"ßÀ‘¦ ÈLõî£ØRf}UÞå÷µ“`šžmievhûå·PÍïé½·Z‚¹øÜ±u¹9§‚&.7 (-~]2é ïqÉÒ‚1lµK6á;û÷ì²Hš£"óŽÿÐN‰ìz)K&@OÇôË—âˆu`‚¨R™ߪ›QŽYëÜ£ÞxP,Èy4¬± h&‹máZýtjBÇf±¢ŠŠTUr™òd\ÖÔ]ÚòužÃ#=fq‹¢=§=œ.J±Bŷ팷ÄÖé°f–&% °k"e;#”Ùu²ú Q%XÕ~À_æ­é<Œ'Vêš–Û"æ¸y>*7زÃèŽ%[û°:ÕoÆ–Õ¿Ñæ åÙ%ËÌšnÏPÞ¯Õ;Á|ÍþjùVØÒ„kCTOïy°†áYpmÔyé¤x+ÜáX3wø»Ö¨j%/üŠ-g7ü´*Ž@oJÎAóª|@8m!¹;:"uŠæjšÞyÂ21òíœÃÒòÁ1aY²ù’ÿ[þº¤ŠY|ªy·0±ÀhݼöÚÑù§ÙÉ^“I«ñØ.­@‘ÐE]Z¸à‘¶ûGcJêf‰Gq´î\:%Ü^ ôï†_ýYÔ9ä×òí>§xM&6Õêýš2tc½0ar ›x'Ö˜n\B¿ %Š:¯‹æÇ¤¼Nþy^r‡¥·°¸µVß:ß}˜к+‚éÑG7ÏÔÈF hÁµ yc¬™2JVìàØª~(åæŽã‡ú ¬Dt}K­1í¯À• †%„¹üWZã†Bñùgú6¸S•|ukýX-Dêl[º<9_±ÜˆP‚™¥Ú²Ø2Oàœº.¥gZq?Õr&À…LìZÃ?/zå{î#îq˜Þó³”Èò[·ñ\êÝÛ Ž¦,î§£Œ”˜3 fé™1¨qã+°JÉâ÷c:^û}vŠõFS÷™©*—.ÿ®«ÆÐæ ‡úÊÎ^¼7¶̦¢x–þXf`±üiTØ‚C¿©—ÞíkˆQ@và4¥ïâÇ ±sW¹BñrõØB÷˜[ß²Ñ?„3æ*7ÍéêÞfOþAŠm˜}yö„žo0ÿ¡÷ñ<¾Ä'r«HMÈ1@°þ’‚ùOÞ a*ìý£úÞx࢚WÏü„ŸÒ M!mµVë ‘æ`,?Úz çïÓÌø4+LJ–ætÄK¥;óï‘r>o$xËBTÚ´r®­Rí]×Á€F~þˆà`€ÀÑíÚ7× ©âmŤµOì–ÆÇzÑfeÇïðÇmƒ¡7µþ^¦¬¸æ£»Zê)Š)Ve÷lKGòÎ/㕨ªz{BèÄ1Ϻp½ñ¤Šgûî~Bî¹Q¢; Ð,ûþ¨yL^PHE‡²p ±Ê¢2žœpˆÒœ Ÿ ­`êƒh=Ðÿœ÷:ER$^8‘…D)ÏÝ] ÀHŸì, =ªÁi.‡nDà;3MZØyÿÕŠŠ_k®®®Ë¢85×z"Ö,°ÓZI ãH0ä|òñ ˆ¤°÷ äxz³„‹Q?$I2‹-v{¼E#xÝç,–Õ;Ñ=ô´lØaݸRÈñòÞ-¼¬y™€8ÁÝœ[1Æ_¿´Ey祡 Ïyv\ý’žõr›3|¡§œÁ}šcÌs6Q¯[²¿\ seR•[Ÿ‘ y{+‡Á° ½³è Gê²pá–ld_›òÁÅ>T›îÖlñMˆ{g¤S}ÛJs­ÈÌPiLHaü"‹UU÷,èÀf·hº²•ˆg¸à´M‰Oñ'·”د `V¾iH2\Mž%7¿\ÁÆuäßY¢vO–NÕ ‡Õdpº:$·±õ0¥:βAú!ÁʤÃ&5Å Ö%·ü‹¢áD$É@ø´ÞÁ¯dÇ>=³ )S-y%Œ²eþh27ô¦.9Áoà9á­`¥—ް¤wôžÝ×èàÆ„~©Šg¸hG¬»äüPU· Oí¹P¯íw?ö<œH»FåèEýE.ǯ’Сë¨xc£ÓÒ(!\3žÁÅe‡Ê#°4ZïéÐýÄÜBÀÎÚ-F…?‚ÈòƾN£²²5P’£ýD Ò" }N”к!€=&²(~„É#Pn^ôAS•}ö Gê«7j;øs´Rtü[¯1*$þ†gˆ Öå°æa&6:›)}“»‡3ó@*ú¾ÐawÞÕ˜‚²ÃXMsŒI¾ˆyNwu:aQíqzé)ûbž&B¿‡Â#°Ao™—¶ÐM{òŒ=@¬ö(BS¬ç¾=V%mT˜d)Ǧÿ‰…]º iÞp¨®@¬* ïÀ‘…|?A>ñ›„*i£(qÄMà Ê¥òO(ñƵ赳Ÿüïäbâ‹Ø޼ÐÖÚ].}»à4IulâD¥½jŽwEÔXœÚ ¹­(×#?Ï;F^©ÂoØFóĹ7E+´˜Ð‚•‚>yî~!+YîkóJât¸™¡úÇqp¼œ]µhé1{oÓyèo»lxó ³‰Í(~ C$)¿N©/@sÝêx›ºhæ.IbÛóµ$b@¾â¹}6ü®Ê“s¦~»¥¤ÜÞŠQ 7â\ø¢/ê•6ȶÐg!vôåáó„¥BU𯳠gæçŒ•q#å(¸ÚÆâä=/'‰¹z€šùK!g@Ò8€6¹Uä–Ñ@è­l Îià˜häV51Ñ 2„$[Ç»xXq“}»—G~ošÄu¥ìɃ­ê¨šØ‡Ïw³aŠñ¶s°8ª\ßãiÿ{³™iu šŽÛòèm[+¾9úw7V¢¢µ&°å‰0›È¢U9ª>B) Å^j*›{ 8eruÍÂ÷!.’ìAW©Bí8|Ôë ½±æ+„%jCÅÓ ”íÍÓ¾’i³lÖâ»! ¤ñT¬õ#Ï– ª¹x)…å¢MŠë”cÅY"gºAúÂïÙ,]ŸŸ“Îb~Á2%Õæô¬ ö03@UGým@axlÁöÆÜ$^¿:Ik¦›HÝÃE`É}%µ|¤Mí6¾ôÿëßÛc@dd§#âîÇÆ†:Tor±B”oÜT› Pì2(ªQgE(ÐO2Ázü}ŸîR@’+Å_¨¸(òÜ0˜êØÞ£´£êFjú8øIÁe•‡-ð¥ëÔòn$ à ÈP2xÊ5_z*Ûk½x˜d2ävpõ‡vߥ>ð §Ã7Ǣσ˜uˆçS7dÙøÐ ƒ3°šD4]ŽOÕ†ýù±³.ážhNºôZ¡! WKW45¼V¢*‚ ê#¹Â0úà&« ƒûF–äJUío’QK÷«Hp‹.aBÅ.ý¦SÞ90Öl ªÓ†ƒM¢ñŠÕ‰—ÄÒüŠí4ndK1µ{½èLÛ^”ÆoCsÔÖ5iwTE¥Ó×<ñ6+’:Åu1á_t=‡l毨Ԭ¨4å¹%&„/³yÖ4 ªÉnì„»UºVÒ; &iȉÙí=Ž)ÁeQŸÿß97û†kçßœçR'ç¨Wå  6Ÿ·‚ó¾3Eãàm7ÝÆºB)ªà-°næl6 þŠa2­@ºh‹Œ¦+ºÙ_EQÂR¦JGzŒ]„/ÆÿÛâõø" Z{öǾH¥ìC%î[r…µƒP¢ËãHõÌý„T`JÈ£‘•9ŒTd”x¶ýIYÚRe-S•àÌM’hÕAâqmÅÄzoµÄÔ2Dµ‹Ÿd=¬íÓÀÔa¾¾¯„Þ ·¿‡¶ž<ÂÒbEE²@e KÜ—ê~´ó—ZU²ú“ __°·Ø‰eÔ=Ç`}ƒ$Ù/% …T"ÞZ<ªð£&ÀdŸÆü×åp#öð?>bðó@NtQŒ6‹¾‚îvj£uZr‰20FÊ÷ˆØC¹xCñ¾µùÄJS­­ÍYLš8Ø› ߘ?/ü®ø >\"Ê1ö/:O™mPºT#Äì·øBÍ^A¹[Ó‰†ìîûÓêåšØP½Öb" î èè C0DÍCù 1LÌ\TÞ è5ÏØõdèh¡6謀ûxB¾×œ¹#êG8ǘB"ʱü5(ü;Ç&/ÄÔ )mNÖŽöêN´&fIµše\1×Íœ"rÉ!¿['ÞÙàr{;ýüfwÊÙî¼ÀÌÓ¹$xøÔVà hÎ (ŽÉÎSÝì"`U£q E [ X÷¤Á싲]hÑÜ.'Ö’Ïáw 3á~ÈÂ-3öKŠ_ßG5>T-œÀÕÁ&ö´¨¸hº­ÚßcƒOÐCÙ[­Ö籺„ ’%"Åz»žtï> óÐâ@ìÀìÃÎÀ Ž W{¨°³§¼vXÑq<7–¶Åg;빚Óõ¿Ww +Ø*@¹ †'(¿×˜ó:£ÛÁç ÆZyˆFÎùQ?2D–"órÂÎtUdâ™îÇáÎ4Ãmñ÷±çé5§}D»)cÖaŠÌŽÆUô$Io[Ûï g>¸Xct‡Ät>- qÔs#Áí´¨d̨…6SôÝÒà¡ÁœÞx^_n|Ñ㼉‡ šqÕ%Œ ;4uNAÖå¥Ì¹GnâÃ#øßþá#=MˆøËú^ý0ô5½9³çó²Ì`õ H`S<Ý|ª –‹kG>ϸ`¥Þ €¹aâ6Ûf…Ôqª¹~¶>>¬LÎ2ÛöáKÁ7®®ìAb4‚òz&ïû"°±°§±‘"$òÐÍÚ3†­î¢ÙÀ ®~ŠJ:ºƒ›??Ò Y|nBÈ£©‘ù>˜\žŸ¨&I|&¤1ËÙ¬èšG©kHæ~Ñ‘iG­Ç_¡*+ÂNÎnæÔÀæBBÙ-¯¨°>Ñ¿‡îksœÏp\„¿‡?K­*úM†‘Ó¬8Å>žÝôyåœ&¥š Ö©±ŽÚ‘ùPÄÓI7‰zÙì᤟+Á§i_$§2HÝ0Ïê‚ïã˜æx C2Å¥x£«hiáôëVAk€¾Ï²åP[ÓäisÁö*¤oî÷æõ äÄèn{É@„£8>L’|ö Èýѓҽø`ðª@ø?@ªúô:Ÿ?ìÎsÌïm¤£øãÑÙ3þ¿u;ž¨ÆçF<»>Ô8Ä}9gØûò¥mÛ(ÛI+¸›à¦Ѣ¯Åeªéþ'w3$»«ß1ü|zË ¦ÜØÉµ^Žå¦Hœûi¢aŽ#-q†v\·Jb~S¯É$]Üec&óÇ Åc6¾rJÌš¶[÷¸\WOÊ¿ŠOŸCû’f>×+õ$?D<¹,rʪ²ƒ„'-“kË -x0ÄîW3¤~ ¶ŸkQéñ€Ì²£Jy?Ô£qlgä»Nv³7»…œ™ÙÀq|ß ÝÜ7}/²î™@¹ó*““&ž&£êî ö†Æ½ªÌwqÙ¯Ž+fcù.³Užœ‡:MR—h¢3ð4˜¯gÁm½i[KWð͉ñæ ÏV’xs7 L´Q„j:ŽHÛÒ‰uðÇa†ÐZ¿u¨î 5½ ½äIcßDÎP°•wµÐ¶£ ujRº<ã«W5ÃùIÅž”³3+§cn¬u;^:¼Ç®º ³L/ã°Üñ¨¦u‰`|2˜Í'WðãO®]ÄE,kÚ´¥ô„n÷¼+ðo,hÁ¡›ÓOÕGWœÁ/ǵD3ɽÞqpr@è}2"2"`lr‡é\çî~­{D•u´gÚš5‚tÂpcÃ?Ñn~ÞqØÌQü^2ovC(iݵ%5v§ëSûb»„‘»“#û º'”, ý°¯Ð/©’Wß<Q\^}Ô_;7þ¯}þj¥$H§ÖIiùÄÓ¤ë£0ætK ±Ö÷ØÏß.­£ˆv¿ nùtËÅWlc=@Þ‹cL”Áö•| Å­Ý$w“ëÞ!×1ËÚÆœ~†.K$/G±mØÀï«Á!‹_¾¬ÜB7ñL:Æ8S'Ê¥·¸nÈz×Ë-¡» ¶ÓRàŽXã8Èïðâ­Þ”å› „Ãɪ4Â? |xϦ'¤Êá!'W†ºqÁ|Žâø‘ê¤âh‹2Ó?1¥§¿ýÇãHŒ•eåªüÊ D&á;šl{ÕÙ( h¿Î[`Fm8½]¶ @_Oùž{¬Ì–j®À –èû ƒÕñ&Ò>¾±¿rŸ{òñ;¢ÝPq6iÐ> FgÙ¸¥!(`QºY2±¼bHÅã `°{ÛÄ7zÊk–>_’„JH*Œ…ã£KþÞ(`Axò^à„ÈÌ.AY€–¯«t¯Ëê8ƒ­6–àRÛ-Pé:Ë:ù¤é6[f€ÿÁèU—À§€?m q=>c#áI|ݹ@é*•ìŒ$pIø*|€ˆÃ„rˆ!õ™c Ag‘º³ÈÕqr²ÃÐêaDÏ©syÔFÁDùÌ.½Q5D{Š5sªmb¡¼­ªÄ 1hrºÕ%YüÂ,;¥“µ¹ÀÛáHs'•„±Ùhéô®,ÙD™éä‚~9z…õâCðš€ø¡ÆB;Yýö•¶ ï[¤{­ÖâtÞ"¬cåóGmkëF?ꨋ¶1sÕþ°6üòkÉa/Ù Ûx^ž_´é(6RÛa¤9lÉö—© I° L*^ÙèNWcÈE6P#ùMòɯf{c &19®îŒŸL¶§/“ÔÏ,„wbíJvpF S"p’‹Ç4ž8ý#%´8R(ÑH¹Pê[xPËŒo¾t_×Nü±þ†ÁVgX0šÏ7`³š%ý‚Óлsùáùsìú=î2âzþsþŽ«bŽZ€®‹ÒŒsI»ÿ¥AN¤èlÌ!™ØÈÔuøJ-Š52'Jÿ`$C¼¨ìßóÓÉ*ÅÎî÷ñW¼ &oË/ÀúQ•qd–F¹îÉ?ysºk1~Öøðb07¬D]ÉÕn}ª5ì=÷‹3ú™‰VÓú—(ðÆØ,U…¬H¹ÉÈÛÉôCS&#Ò¹{FЇ@ñÞ6 5üÉd{Õdá“>¦VÜž±U»¯zAÏ|ªÎvÃé—&²rKí^í0ŠANnï-ýñqò6é÷½9œV™W´pþ)RAú3‰džµPŽ[‘<$•ëFRr3v°æf‡}….«à§¯ýŒS_æÇéúÅf›JƒuüžÄ"D³K08è äIu©ú - dúKÑ=ºßˆõSZ5ʘ'¡ J'³9[t$·ÆÀâ’„®2ì ]IfåÛ1Å Ä»48£NÙ çá’Ù༎i;`ëžû¨¬Š©‘…qª¦OAJ0¥‡×ø äe™©oWSõò-YÿÁ°0ü^.Z¯ÛŠ‘Q?p­6ù.ûmo©_Wûƒ`mAËÔÁ;¶À)Ï’©‡?-3Äö?I¸ÙåsÎëÜ”GcŒ&¾WÔ·¤)Zñ;$°àÝÎÚµYÒ´_8<‰Z!^™D¯e¿õe/Å íßÇóqgbÀËV$éT^ã¦!ÎÝuÄ›|*Ë[$Í3L‰è{?ð }£ð¹·…AuÝã3ª ?qÏzÊ>LÕñ¶çГÈÙwÕEu׿qMä÷Ú˜#ЩÉR€PPXe|Õ"xJ­ù7ì¥ LçÉL£Á_LÅ«Ç÷ŽO98Þ3 ™~[*…ƒ©ml‰ÓÌaàS C¿Å˜,pX\䌔]wéâŸ>Ì[ 8Ȧ8 6F¥mvË¢´5éQëà@×qzr¬î¿ü / ظ$`Fƒ€4РMùè9Ÿçm”ûè†k†ä§tD!Mbäú­SdMStÇ'«U¶Â½¯¤M|~qfX·Ü ¥•%u¹WL5YyOÝ¿œ1Ú|\µ‡88¤ô%?òx´á#€‚ªq3”Ž_!ðYÐâòöòKÚÜÝÁçìXÎ[/ؽFÛ"“òšTˆe§Á;Ô€Le¸ìU£E¾xŒš>r£Ôš!,%±ÙftäáHB…¹›Î£³ä ÆÕ¿Ð×ë"G¥|'誰 5ÆŒ¹‚QG“#Û3+U<a'@k3± ý{Gž<«îGdÇ·vOeËÁ($ùš½Ìx+MÕçF=^zÉ™DÁ Îë‘1×ögÕÎD^̇ƸÓ=Ç‘Úܘ¯Å—5Ôá m,•EÄXÝÉ9H³Šð*‡¶îèz¦$…mœžÍÊ+—rBÉH±Äàö©fàd&ÜdCÏ./ORqd?DÕÚ’çÕŒ ¸ür‰D`ñQ‘Œ xM²o/ÞÁIA¾ \ÓŽÌoJÈ„ðHïYBŽº`¶’ñûöaYIBèõ=æºJ“Xר=ÃÁz9vå¯iÖ›r"z’x¬\³ZÓl™Þ”êÚΪbúl½”‡7 a°7 "0 VÚí7ªCi¶·lí]«ÅíKb@ЕrXQ³êˆ®G¹ù#b"œ| WËÂõ ß´C9ª_MÒòâ3–ÄqBÓŽxŸ’x ˜­ÿ÷yÜóê‡Ò‚ÈiVgÄÝpªü7ׇ›ŽÐC!1¸812bèÁýY¤ -3+ò÷hÀæ÷WéZ-ïÊ4É`nó%y‹ŒKéó° 9ñßëZ&ùeUÐ&ËëÍA &‹˜ÐM i—ýúè­C½ö×$=ØD[qöoKÄ_•\ûÜa ¼¸¬oD¶H%—U­Ž—ÖRþ ÈO ÿuR=d:_Ù;†:‚6‚m‡ësN‰¾Üf5]÷Fø‡[)ßûŸ¶£{Ì‘®ÃúÊ eüÞ“RÅ·>õI æá ƒ¦ò¸ïU²ìÞmq>õ½Ú =Z&¿×uâÜÐF›ñ›ˆ³n@!³¥Š–é]@¬@aƸØ3ÍM‹NÑÇÁy¬ßì­×ÒÈðkh÷û7vðj¾·BÚ»¹z†w¾³¬,ç‘¿œV˜,!×b  1RsjüþŸÚ`†Á ÐÕ,¦2 ^0ÛgÉV5f!l${ î+ò&‰µ¹ÿfûþ7G5@P ç W5‡Æ[]«åüQLxÿI¾W#uó-b~÷“ϙһÙQúÐú%Çáõ\¬Èû$ú"‡½eEûŸI„aÁ9U"ltŸŠãáG’颩6·´FW 2¦Ð ¸7 -«tÍ£Í ú—àzY6c´i™H¿×a2¬ö“GyÚ“qc~AX›°ñ‰!&›È5iy5JZ¾p‚ž7 ™¤xÈÁ©lí6Ò‹:o'^,ÊÀµ&nð(±zhµ•8ÃëÂ\p›¾ôéªnˆÏ*ù¾¦åžË6Ø‹¼e ¿Þ2nc¦›Dz"^ÔT™¸„)?ÚY§·Ù±Ž§áÝ[.4²Ó“]»ê{mp¯þCÇaÓS;H½A$¼ií|®ÇR±! ©ÆHÆðZEµVŠÿŽ›¦wìÅ¢$E¼or*Y­ÛH}¬¶n>v È–FeSàô þªDæ†5¬,$%£]ÿ7ŸÞ¼§þƒUá%°wßñ‹±¦ÉK‘f)%â©™oŒ²’7“@ôü<˜(àèS1ÁJ5,P?áF‚ákÂÇÉ4Ü•ȺÊõ÷¸>|C=J:ˆ./tkÛb>Rùß—žD_fÊE¥ÆÊÊëj‹‘Ø8®ò%”-£#âŸQ†¾¯•;âµê,¡£aNèš³‡ï"÷ô¸à¾ +™bÑ#Æÿ—‚ jÄH’ƒ0¶œ1¦Hµ§Ã—W˜åO·€žþðÕ{‡sƒð‡¾(±Š±n™~¹=~êÃEÁðX¬ƒX§zÇñæ¶.qâÉ2jS§§^L}`[io—¹%’xDmA€ÊåN½W"ÓâKåk|mŠôìÆˆ˜‚ˆÆú˜“&S\W:]çEoÖͱÆZpœW´ØÃü’Šä6¦îyu±ø=£ŽåÒa–_½É¬o$Leí]ךL-n­Ÿ =)ÕŽLá,ÏÛHÿÌôauld²?‹uœ9ÐÂjÍí\€Nù¡xtKÕýÏ÷™éJè„?“1S€ZvŽüsÐn…ïåM5qùò½º–O€Ð0+D hÇLƾL¹S×L»8—KðœU m¾7¾zz[iäç÷’…€¿±…ln …òÁ}¦ CdÛǦ¢àµ º¥õ'&#ØÊv·À)jålõüeÑ“¹ —ðq5º/½Çr¢Wfçôå]zú:ˆ•æš¼NN¿ü¶’gCyÈžtûQ^Ül8ËÍjÂM^oñ+±çƒW¸,$Õnœ:±+jhiÑ5FʼIóéà#j¸#¢“žÏƒƒ,ó$MÝÌ…# ShJ¨'<Àí,_Ggç˜Í:fÙWì]_ã8³ÉÅjÁóiîPÃEß@:-I23³O %j8·(½'…™‹XµG¹·Á5$àôyˆmŽÛ<ä‰>²YZmå ×° w^"ˆÿ½Îü€™÷‹9zñwº 3‹ëgcÀqˆÿâ{G2ªU°>\Y´¾5IÁ}ìíÆhctѶ‡‘¬t/Ø8ƹý %g(7›ÏrÐ?<Á³{¢—Ɔ#†¹¤ Ñ7¯í÷30o‰Ã…Uš³l=`nÖÓÜ !²¾t¿£“ %à ۠œsªaiN2?ÏtÈ—¥¬Þbì¿úÕrVËÂ;<°`ËNðË G’Z0a(Áeûon”*/é>ú˯‘§-hIøìº ±c Q‡,8ÆÑù]Óz*8":gš¯+ƒ?Iî ck)eBP3<ÀÆßsø! ¶Ã[=§Äd‘n^›Àÿ¬$ôKkš‚çø§÷°á"êÒÅÚl$ž½ C_aŠâ£À”"=˜ãïêTGELWVºþ‹)55 ßšUO¦]Ãx ß7Ë1·Ýð8ö13Ã¥Ùó—|\/í™X笲î>Ò µ È–wÝZfÁ7¨‚1ܼ߭Ņ>VáLUq+®;ùŸž2²ÙCî¯y@Xà/SHä_9ã¿RÜ`Ê4…™Ê”úÀx®iâmÞ8ãŽsõ>øýF:[Ëìh>.´‰3£_´,•W¥y¥­óôû„Ei| ·AÅ;óYö¿ ßÖÓóë}."dOÉÁ_Ù/îëÌ.K Â)-—šCíVCAS”NhÛ0ö‚<· 5ÔóŸnÑi' ‹32(PSG§=w4C#s‡×ª{ú¤ºÒ;‡O Q†E¹Ö¤(~Ú|56ª{ƒs5M†÷èþ±xmay)K}ꛕ³~ååc^[,Å.†wHµ½|@4(A!b¹4løÀ‰)ê":ÕæN9œgP;¬ºÐLIÔÆ¦ |A!›ÀK†.C½ÐRÕäOé j-šÐŠÕ™Ã1éÉåá¶€ª€«Éµ®+ÜtM €a2À=5„Ð<ïnƒþÃñ“I…I8\÷2);',%9XgϽ$Fs³™ çwTeƒà|GV2|¬0Í¢ )P#²âW¼Nf!"; 3…×";û¡KÿÆÝùìòIþNTqc_Šo¸J_–šÓÛ6!͹zx.⊹'Of‡Sø³U‰Åk%ú{#‡×›„èD¨óê×_c8°¨›¥Ük—Í2È©¢uëŽnb£ïÙñF…NãjHæâ‘OÙ** 8w˜h½ðÒsí˜XNÛî†$³"ß%IöE9>ìos@Ük¬½½ 'DQÒ­øžQ5yŒÉ±ç‚žâSy$ Ô¿O_[ø¨¾8!Î,NßÓ^RJ'NÙ9íõÉþŸ~±W Cþ:ÙKh'm{¸ÔþJ3};w(. TmºË(Õ©Þò&{†ßîÃMl/™UG#PyæDŽ2§íSu˜ÖSËÃg×·'×Äßò}ºÓÔj¤‘pöÆ3 ‘B5p¢Ôv ùè£ÄÎíc…þg¬/ ü½},+`ölqZ dŒ9îJ)?†$V¨Ï'|1ãþåpX3"ÏVõ‹)r;lD(” 6$_%¶ê®ß÷£IÄÆ™xöç煉Dpëi obPObF<ÿóêsÛõñÁ'…X­Ó_G¬É/ÊÌ„)„ôNÃ?Z­ž&4oãå–Oã‹)Q_Õ´u0Jâª<Ì.=¹ñÕu·‚4ÖsDE S÷ršE‹X õùõ¶êAF hŸ7w­ªXr»¶sÌDuÉ]€Ý¸^9Œò‡`|ÜOJ&è‘X2ŒÅBgTËŠÌpz8Ä*]ž8*‚"Dâ­ Ç-Ú¨ÓÝý ¸?Ý[¯h°#ufFÏ) ‰S&(8/5nó šúP8)iaµ?â¦ÆZð)áBøûi‡ qE>:ªé£¡R?^,Õ84¹ð‚ñošqWiÌs²þÆš¬¦ý¹ø„ÎÜ "-}¼ ©brŒÊ2‡4_gØwÕt Ã<¼ž'6’ù½Ñç.X—’D¥>û@7\@¥Nü”zãTUCmKê%°¹©k øÔâûë'ŒéþR7qs‡/Û¨ò©Ã}@å¯ßûI_-´*ˆÿöwøS›¦Ñó÷-Kl\=ÓÒms^ïœm(;>v³zBóÐ5Q/%ÍX:|ö2³k}áTmÍɱDäÀ%ª¿ºÝcß›5r\­nÒ#G¹ëîd®NL;>³Î©NHœ-Fƒ…Ò“û¶˜9&œ²ˆß©=p*Ö}a<•Ðx9hî‘h¾¶±ÙÓ«”ÕØ2 ÷ì¼nÜ0†$ϰL]*pP½”ô ŽŠþ g"× yÊñí¨Ô<¨p‹fB=óÂ*¥;{ ~5PŒä—ÉW4 Yο©Ú©¼4‘o²8( |øû‹µÞý‰¥S|c?ò§ƒ¢§”;yzáª8×@ߙʩë:Y§ë£U$@‘Ò¿%é;¾Ä©tÿâãó?"‚l} ].00€(=rƒ4 kk ‹j³-WMpÍó S O苃às*ìMx!Xª%»Å¿Ik J¤y8tH©únßþÁAÓ‚^Mý´âŽÚåÈ)Èft²OCPeÔ ç¯+I9HM2i—æÍ‹Ý3…D›]I‘ 7E 9mþà»Å e]­Vçw°scçU äAçâúü€l‡Ò¥¸hÌŽqÅq2(Úâ‚àÕÐÿ¡;°u·Ø§š6ŸT5 Íe*œZ ©ÇC#e“Wï9/ió0¹ ¡Ì1ãMØYU_íñ[í%ŒÖc)ΰÝUºÚ^^jˆGZ©¶‡+î#ОáKbäeÕì‡)ÕÏ*ïqÀ IK–ÚMY…E¿–lSTØk`¾ ÁœM@ï íš¹'2DÍÊš‘XíqÓôy]ôÍáfuvƒ‘º¹X³\Õzšn†[­ tVZzßþ:¸éºu`“e}3ÄJ§ðb„tú)‘;?`'I¸óuŠØSÝ"nÿ|i‹Š¢}ˆÓ)t“Rƒò ®žÑ=µ§0Š-Áq¯7z×ö—;¦¼àð›VÀiêçFºÞò‘f9©øÃCDdzp¹¾Gaý8\é°;dV7çtþâ´j—O¨²¾Ü`©­¡’„1JöYЈ2b0«”+[o–÷{8×ÏUÂÐù))ü›9¨Åªc>"4åiÇÃ>ܶÔiš“U—°Ê­Ÿë\Vˆ^;´÷°êp9RX³@Å<ð NHù#ñ…lnzŽk¡ÅW˜_ƒ¦¿¬tW\¤HísDèZY Y³‘ ÒÉh%MQ ñO[f±BlH`"öÆ@wè¨éE´²ûÀ¶·*ÒÇÈ~Sœ€ê r p>ûöÆAÑÙ~ÀB[øµ *ÆjØCϼ”o ¥fËg¤¢¬šd à˜±•{ef“¶ç¼;ɰàÿuÙªAc5‚·rn‘ö-V|úp‘º˜ø“BîmÆ}û³ñò'5C b.°"WÍý-Ïx,cZ’ϼ†oŸ{1wÏ”7 •ÖÍ œ*Nc| è¯?µ.Š«#ÿ…XQÊ•½Žì‚”“JyÞ $sæ‡79yH}æ%1küÇÞH»ê{"½xáž`ˆ“Àû“ú$Ìáv¹˜r“¹JÊŠ¦ÖbÜ£··Þ#Èã9vìIi“¯?uú–ì®›O"–Imó_½Ý²ô\kHRÿ\á@”×+vÝ’»ž0ŒÖÚ,õõ˜Z¤ÖP§½“ º€V¦$qt ]!rOU–u‰l¢ 5Ëà(Åd»×}qÏsóþ‰ð@þoâ&¯Û³q %d(åù³ ÆpÐ÷}¿|Ü0á>ôüýànæ}PM£ˆ“ù«ƒTÆî‘ãü.®n†PÌ#^9í‰ãÇhŠS*r?Ù²_`¢ø«ß^/Iš[ׇ»+£vÇ÷DÚ˜FÖZ¬mÑ”®õ ü¬s^¸u¥Àñ±¥½`ŸMNÑŸ4OöâäFXQ¬ÌÂ3:ÿ-*xO0ær˜F Q1´âí/C`pe=k…_Rûzݘ‘êIR[F­J2£(»@|¯Lì’³®â×üúRœÃøi_ ñijf?“&öKÓû,n ‰ËkK%ÇédY«èÆ+Ù·‘‘+Z¿F³öÑ-àI¼X®L\Ÿ¹ Ý$Ö9 4I‘ø8×I,#NÛZÅòÚ–—ˆé Š*M ÜPMÂÆþa@"k }áUjþ|˜²s ÚŧAtq*UÊW¥Íþ û‚(bÕeq0súÔ^Ó^‚0Ò_'$"ó­¨IúÊ.Ub,Ò¸,ÞÔúªw(SbÝ7ãÊÚ–t~Ñq©G•ÔGÁr¤€q¯>¢DÁµ­6òÅ<í¢ ±°í̮ס ÝTÐ#žódÈÁS&x)bßÃ,§*â9Já.Aú¿cI–·Ö¹|¹70“À¢zÆVŠþ˜K>ŒÞîÉ{ýÍEU¸2Ä„áFʃkú×U^|4ît±ÎË`læŒwÝ!ýÉ5ícÃJlâ>ܶ˜޼‘GÀäÑ9n·XvG^M”êÆ×/ís•šÇïH¿ñæøÜuÍ;4_Ô¬D/NŽË…|o _toJ4FðUfŽßïÜý¨YˆN%ñ˜¨N.'`k;ÌO¬(Þ2pjÍî…ÏJ¥.=g1˜â¼¾ W';ΖD>JçÆ–ÐbÌ÷R3wó§¡Ã>x8­‡÷*ÕGËÄ =¼š>°á«X¸¦ñ/flåƒurm¥‘óCG‘‰ú¿­ xäáÖ}ȼ ]§Æ2XÚV.¿w±Ya矞7‚hòðÚ:»…Ês:ˆ‘§«±ŒB¾ÜyᘧxO”/ 3­©{ ~0»l¢iS:;CS`Õ¤ï¥Jú-¸Xbd'ºÇ¦àlú³%&%‰ðXµˆ3œÞгÓl@¤ERýð–ÎóÔö‹A-6â—‚\Ƴ›QÀ¿¼²f¨3¤_,ÔüÏFe3š`ù)%âï™*&<Í3Ú˜,,7WÄï÷8cÄ}¯…Yù%Zw†ñ˜ê²‰:gW+«Ë¬·À°‚—6F ã¶;ê¿ãí$³ ÍB·•ã6#–qê6J (ôi¼ÊÝYNÔ©©,Ä3µ W­žj‚…™N'ê?EI̧yPd/³!wMâ@Fi§º.UCk[ÄáÍZ DÅ¿¡?c©ùgÒFÆ Üg„ùfU§xÆ’ˆ1&¥we.’Éüt}C“&(òä…J¶ª]øº+äšžüÁÛº—*TËlªÞsŽ! {%bù7¬…±…f×åíÝ’°Ýš—DÊ õû**oÁÎ( $×k¤!lØOà„Xú0€ý‰O( Ÿü<=Ç! và;់7³¬•ƒÀ®çƒÚ°ØF>átO§ Y‡V>{Ñfi17Üö/¿%Âùý5)Xt<Àfþ?µH <g{›Œc´ÔL0Pèx 0~–Õ”@¹¤Œ©÷´‹Ê’j}›!É7ò!´·÷(Åÿº‹s™û²—:zDæ–þ·ê÷Ž-×+‹ìS­ýP/âÒ ]±j͔ӬFzŒWŸ"ùãÿ_Kȧ)ró\?% ŽâúŽX1ãH“ˆà‚çPô{o0.JP=mÁxТßÁÀ2˜ê‡]>-é5¢ùe(á3‹ìÈP¶Û™xT¹Aøº4ŠÐIšsˆ=ùõñJ¦ž³§jîpÁÌÓÈ’6ßp¹@!qKÔÞpâ`–€ÿøˆ#2@Ê÷êóe¤ø¹Êÿ¼™dšÔr,w˜«RŽÞˆö%«ùÜÇA'à‰¿4xsë¬ÃIg„ÀH VVÚOº÷„WuN|àÌX¨p‡-ºŠekEÙT êyÜŒˆ†·sü0g˜,¿šò£>ÁM— ªorÓ[…ÊÍÄ·VhvËÞëJ†1ßú×ì2Gè­% yï£²Í ³TD:dë.J±,¶Î­Æ€ëmñy—ÄÏáC¡ëQк(™«®)Q¿4VZwÈ>4@oê(Š=óÇl Ñù Œ=7#â…ê4³s;ÈëŒ%»Ä?°ÚÝ÷²œŽ›JN8] ™ÂALfUé‹6½¸Ê*Eþ¸ó”“D]ŒcͲ÷¬w»ÎòЧ”ó24 ËþÙÇÝ’l|=ÖXòÐ=çXׄ}Û àGN¿;«&®œÚ¾Qì4u˜k€ ¨ÞslõJH ë¦Ô|ät‘ðTŽÇ°ø Êúvýém}¹¹œY“qáÇI6¯7#p´ôPB;ŸÐöë`ïÉ5¤:\BÍbu9 Vn3’Üüœ»EŸ`¥0ßóá.^jÒžâ䯗“Ó>’U0'µY@ë¡ÈPM{w9ÁˆÉ½oý¨œZ$ú `æ¦bLxú o‹]hYœ­àLJ{_Á·”¤ÞÙ@V¬+=w¤u8Ë¿(RLrŒ‡ï[Ø!㳞 w¹`,à)ë ûpo³˜“'ógk´ 2€|A… ux$g²ìÚ"K³®0‘ÆÔàö·’hæîQߢ`€d³R¥8F4%)‹2σ%"…»T´eyVJÁš)Útò•‹\ëÎûº$•R÷†0-ãob;ø®‘½7¥+¶‚¤¦`m<œŸÌûwÊ!;.‰?hw‹KÃ(‘Î[Š6h?X³Àäþ‰R|•ךò¦èyPuÈ酪`ÇÙ½p‘1GS[>gü(](Œ(‚öͼFgZ X€O   Ë•%Å>KAgîÔË]¥D>_í·¿½sÏ®ì,³Vºý=ÊpŠÃÑþ«pt1gÊE„J`9ëÞÊ_«êÆ<Ò"NÄÙ±qØx™l»I«kul0|úíN=—êÙc•åùï3‰YœþjS‰–§Õ7@¤çªþèIêÄ®eœ‡½\!ðß7Èà^¢—4M<&é^Ñà¸=Sòž â®è€€äTê õK | nâ߽㣪>2³r m­Ò¿îWMŸ1®õ4ÃÀ¦0ÏiŽ &v2ÙÌâàNF‘80rZMv'Ü·+WÈÝqÍÀðQÿ>PW¡þ£~ ŸC§dSibú¤Ð7“™Iáúäo[ãÿÇj§\VÖÜK"ýT FSY§ñ½Œñæi©²(“¥ã[ð 7]F! ²H3I]c}=rWÇÛFƒ?ز‹™ÃwP(k×=´‡ú Àñ€ÚQÇâ–ÉRµ„°æ3¸íkÍm²bœX€ˆÐb vîœr9“à„ü £GùÑ›H¦­S|q©ÑP’…Åà7¥á§ÿ¨pÊÜèoáv¦ææ3“–À‘ÔîiËôj!/L¦IJ2z¢‚}0386åÍ‹O‹`î°ÕŒƒ˜¸n¥ à×h ¯<-¦OÛÒüyƯÊ×o î[åDé9RÙ2LdãËpöFöˆzˆF\®>GB Pj:ìçÂ|  8OýUpÑE™y½¥†ÚÁK5aGD/×Ì7‚WßS”6¤Ù‰j«ý™b•¯˜y™ê,S¡s‹Q{W>XBY3,ž^Ö{ž#×Oñ³ª5‚‰oº]¿—ŸàÌo\èÀ9Ú¼nÛû›#x¾J7ƒÖ”³øª Åð?ö¸¦û‹’núò¹¾À¡:u׮׫C†sI0PåøÒä]4ûˆFôÇÂV±çÛHå_G]§eJµ•OKpŠŒ{€-m(^‡ZCe}¯•ýÁsÒs„>Àˆ+7U,aó< ".c¡"‘XE$ BÒ*¥;£Ö ƆND@oGdöð˜=¸£»J„(„“™?Ë8¼ýÕÚ0‘ 'Ó'qÇâæwç¬e±¥U}êô°nHœ¦Û·ôn¼n¥àÿÃEÊÀ©€))ÓÎoÅ4‰4†ßI·wUØI}l£:ÖŠt'ŸI¬Éý¥¿-x ‹ŽÇº\* j¤"Ÿ±á›gP’±7–$6mf6ˆê¡nPN'¿Î$s}ØŸ¼b³'Çþ‰qõ¹úùÀƒXË4Vÿ)šÅƒ¼¢6äYýG"W)®Õ-Y f8’€:Hæoòd½bšMÄméaú÷HZ·¦0æвWÊGž‡ß%P¹`pÕv+<¡‚B‰wª-„R4®§ñ½þùqÜÆœ’w¬‡üP œ±”ÕJò„ÕYzÍå²?Fà_T[*4+l_jk~%¶ƒ¸Êm Ëô [hdzÖ\u_ŠÀà­o~k"W³ ÎB•'þH„]ðíÞ‡êÜ/gÙõŒ×´Áh”$AôK²ú§kè à„བÔeç½÷!EQ–ÈWÖ¬ ==eáãŠHQT¿.z±¦NØ}[~מi§`ST ªÛ¶›‰þÍihlDþ<ǹ¾×©éD2Øöÿø-†T»¦Î\îPi…Ý‘\ø.ô¸ü ûßrdÏ×ðÓ%!„wÚa óùšQÊ’Þ¿:Ðê*“'wùˆJàö2¾3 Ì^¦ M™'¸ÖÔ>yÕÕÅ´¬ÆLU‰ÝB­EŽ[ìKûe™#+–+7@NjÞúRnÿâ5kT-ÊšªPN•L#¶ßx€œ˜“Ølr)sN¿¶p?4¦u(•݇&ŠŸ"òlq¥ño:˜ÚT’¼{¡?§«øÏyù'K} ÂjVwLŒ,½?QX¿1ÃmM€jÛ—ÎÍŒlœÃã^/-²M‡a€Ê’á$])™õW‹“X ‹r„yn—€”(èäÇ“MCâM…sjñùŸQ@´«F”H0WƲ÷ñ+ÉhÕ7Ó#”ÿ7úk5¤Àvê:,”‰ög#âdA¼\壢óXDî’ñÿ"½¦f"±ÇgQY+}‡‹Ë,ýï&[æäí?òª2ÁÔUzå•Lwxô󔽓ñª_ô‚ä NkïbÞǯœ…2Þ(¹C­|EÏŠô+èÞ9éŽï‚Ýmê¡Àúó‚õ%ôø5Û$]ÞX53ûæ¸\„;ÑfaýDƒú€†Šï²óšq–)£¹304˜+;9¶?×Ì ŒŸµöÁò,¥§ºÜ;ß÷H3Ö[Ly.-7Û½˜M©´ˆ™z\f<…ˆP¥çÙì× ¥®A:qÄkêÙåSÚ«š¨ìÖÁ#~2±°@í×eýá…Yn,"kîwcW¿È, b–_>y€w¿ü»Þd*­Õý›–ç·rà™“Ge<€tM2A¢Ý}S•_¯•C>ÑKÍxz Riqt*Vç÷8vXqÿâ™úU½aδ0Z vzŒC ´Ë!ÞO1Þ‡ÇÎW?eïôœt|þâɪCbfË¡P1Ö[Њ¢òMz;l‰¼Î8ê¸öëVCý0g+Q‡K¿/"QS´/^ƒÞ5÷­5 ¢x2¬‹KU<|4Í¿GaçÐ;Ñ QõWMÀýÑGÓ¼D¤·|Ú‡ò\~c¼D;T~ÊÐéWãŠèŸ]£œ=ÚÙØHìÿ´mqk”l¨TUJ«$â¹æ<- ̪´B´¾&ó-K•¨ˆÅïu€£ ï°*Ê:W'™gÜTEè'¾â(¢ ¨ßP$uBe.*¿tf{> Ã}¿Þ—WÔÇ;¯ÀßI£~š2G—6k©Ù<œ£vYıž#ŠFIT‚ìŽþ–P+ !Þ’ >R ô ðeÅ ¼i‚=ÿ¹:yþtTØ!/+* %;d^cõX,½xš ¥8÷¦¬Yõ²wt6“` &-â_¯ŸQsPäw7ë™Ô5žå¯ozûýÉ,nõíGs$Ï[hFæúÒVªÓ~â²Tt\U8‹×²]/á 'Oî²jh­Ï¥í(†êßÀaúz=<å¡Y—‹¾žÑ9ƒl9¬4ÙC¥|l`œÜ¨tP@3RX÷-Õ±&_ZDní:Äéœ{.?Fo´/âß›ôݶh¦Þø…Àtn4r&ß¶7ºÁHå’ø‚ÁH½PVJµ1QÃs·”‰1¥c[ÒÇÏj_7@‚”ÎYgº’•£¥‘»)¾©Ý©bÏçðähô*øâ9#´Šª;%ôl#V–ÅP ‹ž”Í?óýZs—Ä;Èš¢¬eQ‡-²é•Ç?Ùè ·¼nø.guAVãxž‘`³D­qºÔFî@†tæÚÛLä+HA6›RHŠ;®ËWò޳‚¿¸ƒ™¢þï·ý³nBšÖ!!T¡—'“J^{0‚(ÕZÔ½ö±/;å,b¨E#gûØ‘ì¼Ð*«ès ×ˆÉ´Æï@º¤Šèð4ýAZ˜ócžÎÙæsy ,ø.ôÙ%stˆüdÑÈ™3!î ÝÕ+'ÙŠÂWX*rB޽Å[3à ”š·¥ŒKŒ]µÅR• ·Æ»ÃGè·ts¸bj|â Óé/€ãÿ¤£åë*™-VªÑ¸ ÀqŒÚš×Ì·àÌ>ß(ãÀØ{jxõÌ;ˆÝ\v ´·ÖüŒÚ±6CP§ïç¸|~ªñ{¾'@ç Ø§ +9À“X+¼\éX ªº¨“”ëó–ßA\ë-Êû.º±çÇ›Þà»_¬èLÞytq™Å‰!¢M¯ÈéˆÁ 'g®©Ôe9ö†@7¯,Ò®xBm„™ºð$æé-–XÈè“ ·î$Ä…"¡ŒX"T—À2 xi´"˜=j;¸ºYÍ”ûJí2HQ¿ åÍéa ïÕ8õ€{ˆÖßt:»ÞBvàð'ÿžç",—©[92 ÊK[£&š`–lú+YšgÉ팮LdÕÊy~8u8ÑgÄõÖ©Ö‡CLy úXíO'¶úÈÛÈ&œò«<—þ¶ù£¤«l_¤¬®!¡œ0uP{Åšfò¸Zð–ŦÎ@€â'M|ûª—ÂkY¼@„&^‹ö¸ûf§––r3UÏšÌ@ø:ŠIý—Œ¬H¦ ˆùà€½%AUÅGÕmZ$§S!™äìk_ê£ Ú¹»æöBj âma&Yëœß_?Ä„ö˧™RàùyÎ4߃q_ë…Æ& T>xOÙ’–By ~ÈÑUŸ %‰ PwTЗŠ2”õÃò1¢ ÊÑ´ÿè§zîžëô\ÆSóž6í³Â¾o°z´èÖŠu7#Ø7ΕÚwÐ^ñÊf­`±ëÏç'€KCªNÃl"Ë~ª‘‘! ”Ô'øéyØf±c…ñŸü–‘6Ô¿¾¸±c@ñxâ@†Äã.ë%¿Ñvä GË åù*@×Ãü&]”è 9}ÓkÞ‹²ª –±·HvòqÙµn?zÒãÕ8E7m™d¹p$?¸MwÓùàlÆ”Å_p"ºCeÃýËÿ5[V4¾¶\ á×LîS$úzEˆB‚ÏX£O¸Êï‹Í«ö üÂ>öJÄ?bÁPڇܠ¯š½5ó\›Í/1|¹¸s´0¼YYKÇv xoqº~%6Hɦtk†±ë6WŠgsêV× Š‚tV0$õðÍI°tõyºßÃ5§$ÓŒºM€ÌÔ^ÃE}J¥ÿ¹O-Èñ²§2™¤“‰·SgùŠíáiíÕ¹€ƒ)á œ u0û9£›é¯£=KóFL˜ØpëüÝ è”ŽÍ f,u€ÒßË+'^«O™À$ˆÊ‹¤ –ìCN¦ž|H2;jÆg3îä“Í5ÄŸu€•ß$%ë L6{nú¹×Kw¯”6áw;´y"" îŒM…@à‰ƒYdH&~óÆŒi^¹¤( q 5G«à¾éŸÉ<Õ”ÓThÆøDÊO/ß ÿØÃiZYž‚psì<}®ª?OÈãrFwÝ£©¯hàW—2tì²w¤²CÜã[ï@ç6Ÿòy£VÚ¥‚¢ä’£¬ZAæè.®ëŽEÖÕªîÔé=üÿÜåíêyШ…¸¤7Vð]¿¼ÂÕõïg_Š!u5°5Òâ3¢x¶ôkÆï¼¥^·TaðïáïèÀ-xŸ‰˜÷pˆžÒx«%¹=ò²Þº¿``UB›‚ý»ŸšÁuÿR"j3_q-ÿq"NZ9{ܼ˜¶ò»®€æ³ñ¯&6t®?3ídJ\×,Ä{~Ôâ H½ ~ÐB|OÈ„w%—ùÚóºß2Ç=ï R¦Flo~fº¹Â€Ù¥ úvƒ^ ƒ5'p¹mõæ©‹ Ël9ðMØØ¼6 ~´‹÷ýRû Ûæ!•4pBSI„™ÉÛm°–„Uî²ip-‘›ð¹œùî{)5D`Û>±gupÍn iÒí}žÞú¸MĘ̈®ü†P¶ã›îÞzæâ€*%z¶²š¥îÅL8º‹ã/_ê’Z{LÄn6».–ô9jgîG>ÚóˆÇ½ôºT]¾¦^ÚïýRÜxM4ê Ûš\Þ‹ãÎþ>ÕÓzgÉ,ó‘ 쀧qñ”îÍd¢L5FˆÊÛé¸È›>Ä6‚Ȩ/,ÿá„ôÔÀœ¿ƒ+çîêàm|—é„`š/ €ÑHéY L$Ðïùžâ«žWÕäÛe iòiíÜMñ˜â¼nMår( ß"£ÃOSï83u¼Ó¿íæ úÌWNê‚»›’Ññ|q R›GŸ ç‘ê©û‚ØMÉÂíæ>¡‘P”~ÙÛ|Ñ~JÄFúŠ˜^õ¨âáCä zxšÊŠØZ¿ÿºò"Xµž†òñÆ`iÄý^Rƒß;@›‡žëèþîëF–¬ª”åv¦zhªI³0|î4êÊLërÙþ•uÈã @K´@ Žµ‡Î¿Šf«uiÖì‘ö.ÍÀè³%VÏŸ:.>0'à Ý3ƒ ù”^#w¶XEä»zá8¿=7Ê·,G÷›úª0Úx%9 ø –å3~håNK’œÌÕøø&›t >g:òQÚd\ q³F«1ŽUoœ‰Iž*ä,Ãà}¶ïNgÀö¥™³ã³?m3ªüÔ‚©t%s&tOJxM¥¥Ú…AzJ—ÀÃ2oíª™öÀÝ€Ã- .’'6ÁV—œpJBuÁ{¢^ž©8ŽÁ ¡ï055j ¥Æ_ñuzŒq„rýhñ@H­ûÅ÷½¤Üòq²çXÖá¼ËC”LWÈŒS¥i5ï1ŽœÕÉf/‚þ§Á-ð:Ž<&Åýpl4•Êìò:~yÜd‚xma H*‘ÑÙ}]l^ KǼ¬XQ Ðé®l‘ÛȨһՃ3_PädÅËÄ+îõq(3Êc‰ŒéR§¦Å¶/ਪçÔšÈ ¸ûß>G ùÅôíwÿpLGµ(™Áûº NIìðY=dQÌì‡ÀA§¾ÜqňB ®ºø¦CA€tƒL>xÙƒ"1Š˜’U÷¡¿Dà_•KÍ7ïHå߯šGâ Ôv_î¼èP7§hPç5P,ÈuÆ}Ì7Ñ8R—1¹¨xîéü ì«Ïâ0D¤_cjl|Ï<µ ‘Ždê‘âK“~¬Õ{'9 ú“¨MÕr*{”`œ0ïpåŽSÝA!^Ÿ:¹ãog†ñyÁ^$‹ ¢ɯFO9À]MDG!kÊ,ê¤Ïm}§;8f+¦DìçvÕyý,Wîdÿ6á$¨•=»,_7 ¸CÊÓb¤«ýðÑ>¡³g\t:»“å¹éž|·ò4Ž•+?s“žîåZ&4—<žˆ-½ª}¨'Mfn®"Ù-¸MÓ£Ü2t‘—&öã»QQØðH"õ*ì 1½±ZÏÛRv꛳çÒËÒC„vjÔÆC™AràÃpTAÿ<½žQQ¸¡ãԣݘãt}Þ°ûb:žÜëANëh Ê(egSÅ mÜáèb¡ø63ÅìÆ§Ô‹JÅŸÀ kÛŠû:ª}k’Ô/(â”8‡PÓƒtB´šZ²"ñ|V»ñÍoÄ0îTñ=ZêLýZØëhG‚yT g8,À(^™s<š 0 ,—å{‘ÐÂ4ãh¿ù®ÊÂ(jÍHʯÓdŒ>j˜jt+)(v†ˆ¡6zªÍ¡Ñçz`™Mu²°0v›µëR1Ÿr‚T¥—€Õ?nom…–©aº:÷µFG“ƒ9. ”t™%ÉÓÑÀcƒ^Ę6 ù(ã¡+Ù!6x‡=y/H¦X±8K= C¿¢HÑ5Î GÎ †½¡éK« hwÇrPž0½þ68€Zax²‹éˆvÚY¯íŒôì¼¼¢ DÆô€–«-—1….žöiÿ*OXœUw1a–‡Üd­<èEݲ¯$ã.o]Ú5²5z€÷|êÙJU:;|¦2öœyƪr_á0 Îæ" p®xª·?xÜ &jðŠ¼Ø9)ÞWƒÄtóÝè{à{ß·Ú3,[†´åîÔïÉoýl*µö 0*LBö9†™Õ,°ÿu¡„X? KTï*Ö|&alÑÁº/^ýÞ"­¯Q–N|…ÃbnúÜííu¯]°‚©ËÒŒìkë£ÕjË"š/_öf™Dq¨M›‚ܯý*ƒzá0 Ù‘?!Î"${Þüó[%õî^ña±C‚),} q¯ÊÉTÐfj9¢ö3¹Ò|/e¿2ûÚƒ‡Aè眪¬UïÞyÆ"j$[à >\ÚúÉä)³iÓ 3ÐÆ/0„ŸIÁ ¸ô‰ÄóL–ÕÑïîržÕƒ¿ƒ²Ûð¶GçžœÊR-O(2ýb9'gàënEìþGÞƒƒ÷­ü!r I÷Tº ökåGŠó€\x+‘dx;þŒ³ IüT5®mד¸×çF7í馇gÜ””«üaÔ¶'`ðÔ°òÄÏ8‹KúvÕzäB³Öï•_±Š»²F ƪôµß›isµ‚«l]Þ5b×ïµ<›D16æÃa1ÞàæÃD²mÆžZl*fÆOûQ •2—åäê£ztš!ÍÇ?÷ªÖ2Äa²ib*ÿQÁßHܽ¬–Phì»//\µ†· • Á×,¬¡ÒùdÃy~¨]+d=/¯µjáéV\¾Oé—%"HLì…Þ˜¤sPoýzh˜ãE•2Sè’%ÿ°§À— ±nȼµø”Þ -·º4Áµx”ì)é¡6Rš½ž¯þ¡¾˜$¦Ckx‚—¾MF cXÆ‘P¥n Kø{ÓûùË©’¡ÌÓ¿±Ò0RÞör¸Dsx‰ôf‰rïÄó_ŒU¥ ºªpnVÄå½:Ë]ë5ô«~x‰ÄƲá"/nû,ÀÕ,ñ“µÄ“Ï6W’[hÏøâ, «›ú+ä"âÔé 'äA?Ü y¿3£O[c ò4kMJ™0W6Ÿ„x±²‹ýý[ÜeÃÅT0˜íò:v\®>ŽøUntW(޲#ü®¾êËêÝj¦ŽÙ,@häHÊßôp„ØÕ4"F#¿—á³þîÐâh¡ èU#Œ¢©ÌŸûò®YÓD¦äÕÏ ‰ûD¬î8:Š­eŒ0eK%U>Äql4ë;ñ‡õþÊ&“ï-ˆª¯gä%0²ëå»eƒÏeÒ0E˜aS¯÷ðW£šµ(7}£× Q6·]Ç´° û³]°$\ãVHô€‹¿'Û‹^=hƒYÜÛk³ð·9œ¸D°B±x·Ì­i[Óî|l¢ö²&§–ö*Ÿ…œ Î;¼ºâà,Í’íø™·CêJbA]{ Vý\,µû2!;Ëÿr¨ùÅÅr; ÐÃ:g€wåN=åœ_ž‹³í Ïíñ¾~:ð" ˜ÕÍC¦ü+¸0BŠ`~G¬Á¿V±\î5¯Ãmññ¾l·ö‡Z)üûÁöÖ<=CÁLåeÿ\À¶3 ^zÅ©ØbÐÍ¢–YRnˆú¨Q—ê$Ósëh&qÝg6 Ì}KˆÚÌkVˆ¦ ëéÌW*utYŒæ ÜŠîã±ý÷¬u”ÐÇ0ósÿ—Éûü ê¯Óï'v^,×\4ŽÔù\ûò›¸#®žo~õ«WT__#ÁLZèñî wŒOâ {k„q}Rļ6âšVnvGÒ4Ši@äkæ¶9íá©4¿ñx,àNì^dÌý…mF!÷Á3äKç ‚‹p¬ÅQη‚¶Zt™ P"gy¡•§ ´ÿúÚ]f26’ÙŒ|°æ‰g®êøN#@=,EµÇTtƒÌ­ª/nfÑ‘4ÿÌÍeG ½|-•n¢{ñ’ˆç Êù-pï¦5RöÃ>$‰WUƒM9x¤zEæé@ ÁzÛzéòáØ¶ÿU’&&IæÌH¦È\MßmÊ>b U)îD<%+±šn¿U¥¡ua¹æbi©(ºÆùÜüsäçßhV«K…ãzèêˆÂýdL1æã&Ù€Õ™¨³ÿ½S½âõíú0š–­Ež—ÿFÔ­ÓHÓÆ¦P3›ÿ:Úœ¬J—­ž¸s12£¥ë^PÇy Ý «”Øžÿ•s´ (ý‹X¸€ H+«7øD.å1[©2'¾‡eä'ZDÛ²ý;í’ÿþt{fÄõ¯Êv  ÂÚ=¹ÔE™Ê¡ƒø¾thÞÏÓþ’ªðÚ„¯Õ-ŠÇó×#9ÁÀ;-ü‚hçÞ°^Ý¿o4ÆKÒâ}¸ö´³÷Å"ËB i‘±¸9c ì‚ïºaMåiâKB—·ùU— Z\·àËÒ­b‹ô˜ªd¦Ô&Üð^ãE·(5P `¹óúÏt(¾„ú†µ þæÅ5\ÛËŽ ¢ÅÚk˜|/}²üøÙ–tâõ›E|YÝù}ÿœ{³”d1üÞÕ§u9”¢`¯Ô,ía˜sPtµ2&N9^âé“ùÕr7¥3Ò×c†©MuƒâÕu¶S `;j´®ZS¨¢4rËGêщneE\À Õ³äë¼v¤©ÂÆQ¯øé3‡jº#iGÔ'?U´1ˆeÉÚ$`' (9ý¸àl’•„ftùQ­¨CÔ²îy§-»¡Ö¤ñnEL¯nWìd’²*‡;N£—•/éÏûî‚g‰sâ3¾ºØÈcØÌNV\•‰7mñá);ÇIMÔü¡JéËM.é®ÆÞŽ÷À&á7ÿ]:õ{QÈœâá–™P·HÈÖ;°ŒÄG.ÅQ÷³ Ž™´-,R}Ûò›ö{uc£kLÖâK\¶Ÿ¬ÚPeñöwÈDuQýFgwcÕ»{Ä6ØWk}+ô¯„ëšM€Dxí‚ÜÆà}N¿½~ááÔ¡q^i•::X6®J¼¯&]x¹eŽ;‰<3¬;wñÏFIͨ=q"÷ÏÄø:íB$XÕ:„à`t>Îï‘Ì׬ûÜSi÷Ÿ'oþìéÊÚK@øKÂðÀïzýOÉYAêïq¡9aåô ŽZož¨Ië’p“²¹³LˆN¶Ïcj’¾À&JÔ‹Ëc0ÌÛÕ(-ù˜MZJA`F€íjÉßbøj¸›™e¶§ KL{•šÜ ß"^È•Š71FÉ>HÈM—€ú0ždü„[dHv­Ò•N°ß˜KÙŒÇl^ iïy¢'H ? a5«DÊò§˜®ée†½ KOþ \p󋵨3`!鸒ˆº3)Š ÿ²z<šgPUä X¤oº:.(:`h ô¥úÅ‚@LQgˆq=ìˆYù…Ò›òÔ"› pUãàPÓA§GÙk6 Dà8ô8Q–ypúÌn;Ì:¢Íþ$0þ2—fæ ºK`“2ûàBŽu¢JUÔêõiÒÎ.ÅlÑ=«§Cá–ɤŸê™iQ^Úgs×·|»Ñæð0äùç‘õöjY6ë ð@Ô¢2â ›ﯫ“|ÊÙ‘ŠpÆRp¼åô£)®Þ 3Š}Ä(£OÌ/¬¼j$h%7DŽÓp{Íu>Æxº£æÀj'‹Âð-«6t½î?Vïë¥ã0Õ”»!"p“¾ËoMÏ„¸V3 L_ZXØM™u.ûÈl¼ØùœÔ4ÑQÆ*¯ù²¡ À5YWæw[¡Î¤…y¢bϦKwõ™!G[€äÓ±¿W…f½¦`ÍéÄ$r…0>K,jpÒ›+Ù"øB’,;—œA„C÷šøñ •_1 §¼@» ÑÅŸÁˆügYY©¢¾%MS¾6)ÈêJôü®G–°ã$Œ£¸È€žé#­ö«qúNXàk } Ž’úÔ»™ûˆSôü¹¡ôWfL7„)ú†Â& x×­‡eé”RŠYëòã)} ,÷tÕa$Ÿ´_|Ë)˜«ó— WQ…¯¨S—©R>HcËUÔ`$ØZÈÓ?é×ïðÖÑßdᘰoOˆO&/>/jÈÝÉ„þGo$yOÛ)O¤‰ªj4Ý~¬$éÞ¦{yíPåº"t@Ïî•ãW·½L§QPNSý(Hjb7—[ÅùN?·Ë´à×p¸‰ &òÊ e#\«¢ëÖB|áP•;»µ˜g_ ·kh‰¡¼ÊòBgŒë^«5æ%_EÞד:ÏS»joÍðöD¸[#4?/‘×àSËŸ7Š·„”üt vÒ(isoöæúΑ! V‹á4öà­®’~°Åæ Ìßäé4q¦ÂX|¼îða^gãâ¡ü»ãŒzžo ëé(lwÞ %pÄ •V;ÉɱΠ%¥iÀ:³¢ X|rÊ⯲ ›]wv÷éáqÔf´_¨¶R^côeh4@÷PvÛ±svÓ÷¼H`wø¸“€.úUN „{§j l>v&oì}‹D2&T³’.ÄYi#‚íõÕ6Ei(•¯Q2ì@õê€ÃÈÃíνA5]bŽÄ´¼`º ÆÈ@Æ=þì£$ç {_Œ#¹Í §V:_+RŸÛ(ðÂÐäý>¬1•>xE3/y)¶¡IÿQ9rz*™Ÿñ›j:D¦™ü]¨Ȩ׈ l(‘oðVèÖ¡¸)€ð=ñœ¡‘æïçXªWU¼ßHÇ÷ò9ÂÚ&SšnÀ Â6½}‡­å—†Äò™v ¬?§‚xÙdýÙäèïCˆ:Ö/_¬Ã{kÀà¶É‡5~¨xŸ”$¼’·ý®Îèå^’·ŽÊœ®TFȹè§jyÞ#—,7dÂÆN¶c³âvÌ@•·Ÿ*ß]ŠÑÚ=«i7££SAcô§¤¼pÐ|ëƒy…ƒ_•S3À¸ÿ®ýKXÞ[Ìq¿-mxGDzªieø_€–/‡çºÍLǂˎ˜«è¼]°Ëõƒ×8f™e&töqÛµâçúÓvh_¢]áTåÿl²Áùv¼]0c´”OjBlHOƾ§4~Õ¿Éè‡ò5²×AZ§}X²ÖŹq]—GOâ鎎4¶7çW[Ò©™^œP­Ô¬3÷|,÷ùÅ-íúÓıX–uROƸG‘ÇC¶â %"@Ì(C˜,ë·æÐ<«¿¼yñ+[M·vJ'¿QÕDP–h2,AucÁÆ<¨ :k´JA­7×HWÆÁŠ%$c˜…»TM¤'¾¡\V&ðGùÉ¢ß/Øõ«ŠÆ(';Ë]Óxë¼äÌ™9jQhÙ”GšaG{/\Gw¥ÞìrŽ õ¼b²Úæ,`a‰v€áã¨.ö"ˆìPèd Ý ¯X†²­gµ3A_^I¿ü´%†§^å!öëµ+¸rv?Àaù¢?ðx?ÜbÎ+YÓK±@fÁè µ‚tÊY§HŽXµ|¼w—ò g¢n°›Ý¯ÜòúþJSB/†¡€tÌdæ*œ}0UN*»¥ý "{ÌsŒjºÄ‹­¿”ߨl3Í^Ž~¬éS§GÐ-Dn]ÀžèÒ [Û"œ:÷PEÑ[O×0—w8®¿cAÀÖNôŒœƒÊVn“ÕÄüßIs`”R ¹Oÿ­ éʫńo¤šÍÜ=­FÊ!ckÅØí7\5°2@˜£©ßkY˜v÷TµûFÇ ŸµÉÿDÝ›ž¯‘‚Ü–"sÎì‚cCý€íz»ô^)HòÛdjü¨Ø×’¶%£ç2rÄÜ6ÑÀ%µµgV½êw1¼ Ý¥G,¦­ ý”þ†å:í}¤¤°¨}× ö¥ãÉŽœã É ºÊ¥|& ‹ù³3Ú«dvŸ“?ü¥Åd^me eíÉ$s|®¿þ™<º†_©†DÈ£¥ Åÿap #:šW ÑÉ”BÙßK™¥fÖ„Í4©ðÑ-º)0ÌϬ0£¾“êF÷ǹMJ[âŽF+õ3LÚ¡9NpÚ%'û©ˆ/õÌË©$쎧Õ]#ùô]¼²ñcÕaÃWE“«7ûTN¯ìÆdÇœÀÊ[‡Å¡xÀÿ4ê\WÈ|;,>üÓdåP€eaÉðˆ‰DeÑË÷Ú \ÍÁ²u§•hlj*xHûÿJ[#O‘üˆÄÿZÕ­_o4|ÏY@ó›PÎï²ÞŠÜ÷Ø/±Ë¥Ü÷y ¤ÓZŒ•mx“ÿèXq• pÿ[Òáô}ò•tѧ ‹?œ^$ƒ Dbkža«³ÂÔ‰öé”ÐÂÜ~-ysý¦_9‘i@¬‰±c|ª |)l¯Ä4k¦Ôv®Ê‘w±Kô )©›+ÛÙæðN7FÞcE0Ö]('ÀutíΞQjJpÊ”g(I€¯K0°÷¨˜LQJ»ƒ¢‘o•-ÛÔ`±Ôš;0xo°ƒø/D nO.,°Jb#‚˜¯~†qwz¿ñ›e ’'"5~çU)Wz†yÐÏݹ½\f‹ïQÜ Ç¨ ^+dò‚Ѿ$·ì™±GÌN}¿ÃƠ㯑iBz7 K#=GƒÂäsBnøµXÉï¤w­†¨ö®¼oh™ûâR ¿\zp¬÷þÃy¶ókÎÔ GÝI‘€¸l~±W|)§õ§Êˆ…š•§émåÁñ›Êë>ýþ! §µeîD[w©­JòĽêЦúñÙ·sŸ5eì®— ¶Ò·÷@¶EóéƒU™^›^ +¦º­Ñkðˆê¬ãký{tsئâúê”¶&Œ“MSïœÛ#F­ÞpDÜ3r~×·& f຋Sð€•;†5wGmÚߍ޲ÄÖˆ·ëÎå&¨v ¯¢b’Fãµ? Øñ¨‹0…Xˆ]ž'â@‚næñ›"º;ÅŽˆ²‡‚øgû¦f¢WâfreP=ÔÅôµÔªˆ'ÌP5sì³Ñ"ߤÂð]ùªÏm°ÇÜE±@™¬¤Î‹–Ǟ߃M øèâ}i¸Ÿ†Cå Þù·‚ k4€ÖeŠMÙb©²¡/yѬ£éÊ{ ð™Ý«ßå¼;¤Zï$óâgˆ5-¾¾&=Þó`#ÚC{ÆÔaz{ÃT·BÞ3=Uµ‘¿¿£|@/öåqS#JûØm“Ó¸¼ÆeGdb¢?uOÖùå=Šw—*³á¨^­ á­?½ì5†…MSª+ÍÌ̳ Ri3‘_#Ï‚Êéš‚òŽÆõ×)6r•µ’÷‰ŠØ,æœ^©¼¡'0¥”«SØ“{p@¼¾ë} Ø“$ƒköG¯{÷†ôèºÚ¥y^ßdžyëž7óó¡þÿVÚV°è>åã¤HñÞ!Au±æO÷ü=§çx“Ü~³[¤ˆã;×â.¶úÖ4ìùóÞñ°7oä¢jÔ{W¬šÄ¡F± Ãd·|żùe2©"B,ˆŸž'._IhWJ+)¾îš ºVþ€¾"ÚcÃ>ÎHL£ŠÁÈ14áéãøƒÙ&ÝÍ"`ä}líš }¥`ô#Òü%SNáƒÁ·6É¢D¥ë˜j\N]k.ÉÕ÷·Û®íA(^×ú³Ýf‘ý>ý§ލ±uÚ¼“9bè—V‰|à¥ðf(q.òÖ)/~"(š¾~…O5 ßbðüµƒAMê8~ÏFR¾ "Gsª‚?%ÈdØ—AÀa¬I’«tgÝç) VJ#›»kG¶Ù¯&N£AÈ€¨ÅàúoTtÜ<†kä?²Î‰N?Û¯ŠSqQ1Œn¿t_àhóv‘TwžöÌD7›¤<ƆðóˆQv}²âx¶p0ôÉØÑÂk(¾v#)aLBØ™2¹—Ù}CÛßÐ °Ö÷àM1ŽA×Á>¥G¬ ôL ƒÁኻ'8¿¬棚П;re,üË4†]àdÉì7»xšîƒí­û·#pØØnósï6>¹ŒŸwáZ A¡M@P|ìµõÕuÀh¯Km#¿tÖú!™‹wÚjΫ¡]D̹ ÞC²*Ø”œ÷ôŸó§âîù F{‰ Èš1¹b_6Prx˜%ŒcÖÁy/=n4bëà‚|ùn™,br.Ë ÖI 1õ¬Cp01 òˆŠkº ‚°çOœœ©-ð5Êbö^eŒ~WÔ¬´o;WŸªÏsL´âjROÐ1èA;Ÿ ÛH7áthó‰â6N6è[°£ðN¿× ʼ”7µkøÉQ’9.ìí¡RÑúלYÞ~%=˜ÔG!\ñ?qéEÆÀ B††ÁAšvˆŒÊoIMð¸Qå{Ä™†ùî”§q]T™½€‘<‘ðöIg'òñt:Æ*H? 7QãÖ=Úª.‡sPŸ«ŽoÙñ*Ö)Ô¥ÌO”¤äÚ-A5ÑËXVÒE’&ðar„ÏÏ{<ùY!L(BjœËïõwðÙãì+©_¦1ñ±Hº(‚ì€ç`WÃã ‰üÈ ösŸ­TtµŸ ½3©(£öȯ<¡Zš•Gü{™0lѬ1˜>‡!ëElÁ|)w÷À=WݶG¬Šü.¢Év‚Cc±¼°hG"†Q/ƒEJ„MUýî–Vàžãÿº)¥öÅU ´”kj£IJ»^ž¦úûÏ 3xáTdI Æô©RžÒÖxtg¤ú®…ÉÅ+åxs(³lWOZk!xªúý´ ûª’(Àµb‚ãÑAº×)×gï~Šb•!K‹:K4˜ÛsÕ"þ ¤=žŸœ†>á$skM–î7Ú}P/“ûäù ÓÎ5L›¹Äã ²n¹T‚¿½ y^¥WÝÛ•Úh;ÕË#oc·]öT5ˆG®‡Ÿ|¹YÕqt £M‰?x«L›à Fd˜ÿ5–ù†ÀüüÌ)bìV/yåÔ÷ý¡ð¯®®8×ý¾¤€4â]=Úà¯Ô,|Èð¹GrPÉ*©o1.Å;úUöÆÕh®›IúÜ–Ä ´Û÷ãÕ iá«¡lëŽÇC¿gFΔá/±7A[ð¼¬T11„}Ú3ò`ƒ#5¦ó€Z* ÿ —Ga±ìiý¥Ëu &¾°±°'ǺqÞýk€fÅÏÉÀ´ÚŸb/ ° K18§ŠŸi š 8s“Ø è(ÙŸx&c”O¢ `PVlÄ+E‰¸‡oÛŽ³E%9{bXQ¤ÿ XÛÞÿo…À Òz"EEÀ¡#fZk©iÛ¼¢L€é¢m}cpu1¡¢Þ…ÌÿùÇ:§Þ|,Þ M  e~ÛèW5H£×. H¾b .­7K¹#Jíla£Ø)Âêq‘(Ÿsä{慓툌®Êyʼžýª±¦x}°Øg#õ¸¾6Ok`D8°ªÍ¶@§Ž¹¼ÊËuÀv©,dTªì!¼Ü‹gà¶ðœÍšÊþâÄ®5pʘ\ë–£ª^þ#B°(‰œ½8ÃFú¤ä²éˆî9£ìrÖmµÚD ÐѨâB3pºÝŽh‚Ûax†gLg_‚ëv¢ÒN°[} È9¼}ç£ãF‚ÇoKRȧxäâ~y$u©yD2Î"ƒ:œÚ‘§ÞÙX\Ó;^0ûƒ‚1ßnÂõæs°‹asHµ¥6µ±µ_¡-5ÞeÀuºHE@Õ‘U€pŽ%$ääôp¥ÇGr‚q,èjO¡ ¥S=]µ-÷þsQ ¥Žè#nÆ´hÀx×äàÄVF9W#qÙË+„!.ÁGðк å^Ê>3%}Ÿv—>¥N§£Õ/Þ<ìÙ5¢•Y·o$Õk8='++=-úN1P†üa P0§ðŠ@À±¶ ;<8ñɲR€Ñ ³‹™CÿœJûbƒ þ¦T!Þ`ÜNɲY;ñÔ›® ÍRƒÆvpkœ,¹þ/Õ)É+LEËÿíÃ3m´ º7«Ç¥uudAòH’4MIŒ½®0Ò©ÆYJf¤.¤-ÊI§.$¤ö·°(ŠêÍ–N£Yh$¢/Y«ìõÇ"KÊÛp}wGÑOçÈòÀ\×ä4%òN‡Fíû|ˆD Ž"é»[§d)ä±C :µöJ',2°<ÿÊ¿mZxn³qÖ/ýÃpæ—iÉ“¾÷›2ò• Ïë„~2~ï CCþiПÇRe±•WBç’Г‹å$~gf<Ï$æÎ5ÕSe§†÷fÏŸTÝ&!íy"þ×>Ë¿°oC#ƒ2Ö熩Ôçö4 1"Wh fËWxU¿0ï¾+Z 0WIfÝÑý}„/J­à&âO ®ÿ+H9êµà³c…ù^»¯ÓŸÝ;®Pîe`ÜaWóäHò‚çÃÄjŠu›0¯åFCÛ§¤ÀÞß´J)?2¸8áÐ6§E\tyا½—¢/Üt!ãsÉ,'4ÒjÎ02¶Ùw‡‡ÂÊ-ùœÿD>Dì+~Ö- Éí¦—ÁŸ-ÖâÖ w|£ÿÂÆYk–`„­õYõvwÿÀBpšÞFB4TP.ßeûÈ(s½Ng‚ËH¦—™Ï;÷émiŸ¦øÅ^uÝFsšúßNO…äþç7¤ÒêÖDnèýù>4Y ö )Ñø B¨38¿µÞ¹\qG¸Þ²ï'Ɇ£åÑò4Â4ŽÓÛÌ妃߻L:)¡lÏš÷ÂcªŽy{„幆ëªèdå@˃qœ.Îáü8†3a£5.X»ÆÔZïUî¤Ï^P›ÜÇjªöéh© ºïˆl5´EÌá™C7™ñ¦Šê¤9ÒG¹ËÈ->žSÏô%üY¢oÞW»û›  ih/ÿwb\$<„|ôëWÿ·|½@ÂÈN«ä‡ϰ.*óߣƒ?‰ç/{Vd܉5%/ûý¥I,iSïf´"c9R]Ña@â1Øa’]cåÚ/òºØ>¢«!3õÉ6'šÃ>¬¦CµdÔ&ˆ<øb´kvÿœ@<«%0©+7'„»OGðDÚ­˜ÍH)çDñ! D*®‹üå¥"²5w¾Û±Rd~”)t¨3Ôçt ”‚}ãNÚ’`°Ief“ÿ§^™ÆÉõµSOCˆÂÙºí¸»'ƒ¤áNpŒ_vEÞa9²ûŠpçËY. 7]Äs¸ª¼yJ08 ¯Z~ÝÑÐùQeÊ`eáBñz!¾619Ã!¾†{O±†`£ŠTæÃæõõÍj}Q§º?Õ¤¶aZ Ì‚Za*Šèþ€ßÛ™ÿ:®f-ï먥aS¤=qÑ iÁ]ëÜCGHÎq©.ÁjÀmÀÒ³½šTêV,ZS¤ÕóÿL¦•lKJôè Tã¸Ñ?¨òl5yF`Õ†œø!®x(wb“”7lÙ±ëÇšŽ·gÛ}ÑËn‘m92–ìî<³ÆE2*ŠFâøýXìDµ K½ Étú„K--M‘hµ‰ûx¸•=<›êu°éîŒj¡\¾!ÛOgšƒþÛ›·È)ðy¿æñ£É%úèvi¸G¸Âä„q‡¾pîÙĨ¤C’ñ?[.¡Î±_*ñ¼Š4ù;Ù fâê=4;½oùéta¡a³ÒEî7ìËÿxwNTk„â¬?1 4sö$[ìO°í&rÞÍsóã8cøÔ›¹¹ÆKí ¿TD‘ù*–Ù6»Bj–¼uÿbµ¸ŸÔc@|õƵ¥E|¿˜q áTÇYUkNðm¬Ng¨h¹¦ýMÜ’Y+Ù¢]ÈíÛÛ©Ì€ÍGI›•ÚL:e¨´‰<“ÿ…« t¤±‡A§äÙ–È҇ƉÈM³CulPD{JVÕÂK¬Î×Sá=üak&Ü`¤{?ù`b7$€È TØèW+”`8!sû·ÃÚvéŠø8„œúæCçüêbÑ®Š QÝÓ_!~VF~JwHÆü°©Z÷o.âS¿Ñ?£¬Ü늰Í ÒYøúÏâE'lSlÂqA ¤â_bû9œÞÐs×5¹Ga™j0Y%O³ðŒ”§ÍSú›ÙÐúLÍùÚfÊËùûZï*;(¬wÆ ÊØäï €ŽGÖõ‹y$;¤L`i&®O_Ùì—×y€s/'¾"»u¨ö†¬Ñ›JÊó#fË©HþÚgÙGï軣0 ýC›iüøËIWvou)ó1©wúµ'êÈÏÔ¾U¤¨?d·ìÔ ½À¹¶f&À*ED×LÑuõïÿ¹JN½×=¡ åØXy¶êåy»4˜É+þA ®¿„·©n£Ùµ„ôËè®Fq(©¿¦­½¤þxx/úx¥±ÐîtYZ.QíTµJ«ôá/´aÔø ¤l2ú”Ñ´Óï YÉDÒS•°fã¸zEV ퟓ¥d 4ÆÜ«–ßýüWµíè]žáº;vj¡h7¨'¼IŸnú'_l*ì,ëævÑø>2¦ˆ£òÚ kºßìE½€RdrìÜqOãèßPQÛ¨$y¾úîœD£‹âg ³‘ÅKæ–€¯^rrdõŽTì0âµôèžR$W§ú»ef¹yæÓÆüLÄ‚—ÚlžoáM*ý©¸ò~º¼Gø’"V §’>Ú<›jÿ\ié"Í[¦í#cy,•·™ (lqbR“<•T`S+–ðF ]ì ÷!籕ÐâŸî¼aJßûáÔº³ÎôbŒ{˜†äf*F x°†²õèºu5 ²éÑŸç¢ÛÜŽ€\õ>1iØMþš¬•ÞråïÈK Ò>©éõ©>_³hÔ@7F7ô Ü Ë"ì± &Ý©jHÀ-hàÞ Ó§@ïœ*i Ï´û³Õ¦Ö8Ç­´œ/Üd—¨FC4ò܇fAþüöæRéKÀëܠ͈ÇFj”Áíà‹À愆٪ȥ%5f «¸E†›×i²ùçºå òw€9«ž®ë5 :ˆïêO.w…¸¸¯NëFV‚¬:þÑÀ—Á†²ÓQÍç}ëÐPŸCG䯩e)ÍO4þ•õÁ3"ÌÑö®¤/ü ü ”¿?äq_9J8ìyEêKÑwº¯ô[k1';þ‹Xô‘xW÷»„Ë —ª‚hÂF“ÛŒ¶ßAæ-][pl«ìmi³^ášØšLó¥& «wø™õþ.ô÷Äå»ãT”J[èô²—Яj baBÓk¢àš`ßqûؤ(6ÿÍmN¦ý*œYð"aœtÛÝÀ½.xSÁ£Èï/Gh»l€ózH}ªyÉ6ff)v[:>¬°ìW¡)K Æ*[°\5X0 ɬ‹ ”@X8(Ýéà­ƒ à ó>ªOŽR·¾PªþÙÚ•H$oç‚5Þ¿I¨ÏØy'†}$ Ì »1CtìGŽè¨ï¨xìணŠ¸Î«`‘«OïiMÓlK÷áË…@ð¬i®Ìì¹Ø³Ì8ñö0Æ*¤ø™mDg "I»åvçþ=:i¯w˜%šöü¤›”NÎ#jaòЃA,ãN…ÒRÉLæ‰q¢°« ‰:´Ï‚ûÚobè«‚%UZ¡oPO[çbŸ!˜d´ P{ óß)A錨!þ#ºGi'µ¨ŽÏ¬àú¾ÀW9¸‰5·æ- 'P1À÷¸Eè»NþzqØ–=^´—…¥ÔªËQ²âò²Û0|]ÅÈAXüL! ·Ò`X¥ü>ôf}hÃÔ2ù袯ÚÛ«Áƒu‰Å1áJÙ@‰$]í €nä¯ÌÞßRÄüàr¡áÚ|¨Pغ-ëgWx´Ò=ö½IOâHþQ-9¾—áÖº## õÒºkš¹íÑÂõ} ¸›8§¯+Íâ°€X'oÓÔ—âÔ⃠ÌVCÉm^f$Ú£ˆàp7E‹Ï’º˜Ê ´Ym¢E}9 êË¿}ÑÔ1ÊbptÊ™»ÐRÄñq}9“³êóC›¦Á!|šB×I⧉u^ ”ÚËéG7bfÏV]¬g4\’]Ç¥¥`üŒzrX?[€¼‡É˜µ{–¯Ã(¶´'ÂóÂŽËÏpû®kÚ)$¼é˘.…,ØAJh Ÿ'KΓ¦åضJê_ÖT“uï&m¿¨šNZ.ºK¸D¡ëYµzk¤Ê²q‹¼þ4 ætö:87 âz+V“¶M-ä'ÕØ;–¹¬NJÚx)!„YJPþ=}¬xW†J÷ `Øäô(‘¦wÍäÿZ/⣬>™¤x}ÕD5ö½n’ ™S÷¦1/`5~º (Û.ûÍéà§/dXëÎQF"9”wmjÂ=h%KðÂhI·K[2q…³#9%ýÎäæþqü^>͇°>Í ãÚ”–ã•_üÐÙvµè6¹–ÿ~ðñ ´nï<À)³fÚÏ/\ª=—æ–ÞMC!1y“xž3¬è0x~>áhIø›Sõ¤š ¹[Îã/8s?1é39ÅŒ«ÚǶx}¬A¼?)j£oµå‹#b<üzCvqÆû›ã[¾*?û+CóÔ3êTš-^MË‘é³.Þ:)/Àªñc)ë#Ü>ûtß,ç Ôy¢T ¤[j"¥ÕÜgFUÿ;L_ÿöÓ,?C·›‹ÙV<)Æ,H¦ã ÚM8éݹúLg Tÿ’”@7©hµÎƒÑEI ÍvûźUûts=‡Ì•›é¤B³ö„y‹B-¡ìC 5ón›{uYéàV'h^Ç:w²ôò‡–ðÓ?™=¦À7EgŽ:’(Rƒp…i'þ¹1l‹šN"Mèd>eÐ R3þ|k2Im½Â'+Jß2 ÛiI\¢¾ýÒ{‰}­±{W) µÂ€ÌÌj3B–D¯«îvõÿú–ç«6%A‹—¿C$F&Âö/[3ŒjñŸKÒpÍVo ¬³±åïSJ¤ã-ácâØG >µÑ€PBXŽþÐtf˜­Ù‚;æ¹;nsVÄ¿ê 'V°6(€Á)<13†/9úNkÞxZˆ±äðçtU±ÞðCïÖ=¸4ðªXäᑲ¤ÒïÑû<Ú‘k¸=9–tQ§£üdôׯo¯=âKÉ´<ë- ŸõÅkÍ6ªyUîdš†„âmû*.t~œ/Ç^(@¡ ‡¹tPòJJ(OK{qõ«OfœAõâµì'[UÅÛàÁë®ãŒ§*Ì\HxÀ%×ï/øT®lk5QÌ !xàÚÓ˜ï$ø¼ËS^‹l»DgÕ²(Hù{ˆ¿¡ÀaI¶s ô‚îú-O•-5Ÿ\ø"J.÷R¡&q¿86 7+Ø(Šž6m/D(¨çèü ·,ä2òŸBž„Ö­Í)ÂYÍ=ý¬¼Ÿt¿Ú©¯ïÖ2iØaž-ƯøpŸ£@Ó­‘ÍÇš*¼8ü,™Mâþ÷tù~ö¿s¢Ö!4‚O¹'˜¾5ê§édãg‡oÀ*×?æ Œg1Vú%V1‚@IÃBVcÞCøüPÞîèÎá×¼~d¤zTãѽ*„]Ô Ì þ7ÁFGõk³Të£Ãꪺڂ¼+åXµF×XÚòɤ‚*™Nô^À,‘ì|Ðv%eüUµ)£o¾‡îO-ælÖ.¿ À>r#YV¼“©ÿ™æ wð¿0îé+ƒîŸºÿ/û-_îQC±ƒL²ù”½/Ƈðÿ©KòìÄU:<l•7C¢<Ö0oÁYúáÔ³SB´üb5lðWÝ‚ˆ†Êë¨ÅzZjæ0 t,X×h~©šý¹`V‚j_„wƒùÊ'« WÒ… fsˉ ëRRÛÓçÇ›…Ét¿æë\kJ¿µœ&~s‚ T%Öÿ£Šð˜¶¢«tPº°WÄo/Ÿ€öpH¿Å0¬€!¹Ë冶êlgŧê¤}Ës6¥XX«2Yþ¹ëQÉdeü ÖhçuWU½÷H¾°[œ±q¡ûîóÝ%ï$ÓRmŒŽ úm‘MGŒ¥l¸KâdÖYØyàEn]#k6ôHç&ÎØ¡7]Ç,|i„j‘%ÄjŽßÀØÿzüÄ •鯬VD½øý‹N-G_u–Úf›Ä6Ažç,\¶åõ;Õ¨ ×Dv‡f0*²'0Äȳtgn6k^ï«…\¥€,ŠÒ…âàÁÔ˱ÿ9Æýð­Ä_n×yýŸƒn:{û¾^ºh¿,‚PÞ)aód-ÃrÊ=BÄ1›ã¡Vµá¿Ú'f5G\˜¨¼æFBÚÄ–XÌcâhü àŠ2FíL[XöcLNÖö}㕚¶mÎÉh÷#Ié÷bÖ²1#£ÝyïsŸíx zšž×†èáÏdcm5%™/UÌîÏLíò¼ŠïËgK‘Þ¯Ó1NX4¨„~7}€ô_š·:>ˆGðöÆ)ó€.N=9Z|¿íN0±Ò»ËIUwГ¼Vþ˜»=¦Ýy½®,'›_Û@» Ò¯«ŸbýS9‰ ;ÿFB.5—7÷"ØÖ.ñ¬,¡Ð"ž™²ê¡"IÎÊÂRº&Â›â­æ‹5n€Ôx& DS‹hrÿ$Ò2’îï¶Éæ6õ¦`ÉT‡dl[8L¥VHÛf$†ïͯKc–*ÐsÁ6´×e ±&€éÕKÈÆ»âùɇU<ñÊÚm…ï *Þéy]bŠ›6 ïæž5NX`¿ÅΛ§Ðîq Ðæº!öÕûÿ€' a/§´™òo)ýŸ—‚9ògŠ;$lB7ÁVu¸¹F®þ·Î‹ÅÒÀÈÎf=Ï´û”à íI>Á¶éß YZrelic-7.6.1/functest/packages/zlib1g_1.2.8.dfsg-5_i386.deb000066400000000000000000002547101455105530300224750ustar00rootroot00000000000000! debian-binary 1485710543 0 0 100644 4 ` 2.0 control.tar.gz 1485710543 0 0 100644 1392 ` ‹íYÛnÛ8ͳ¾‚@_`-ëbI®ÑÙ´ÙmÐÍ"hº}è‹A‘#™ˆDj)*ýR{%KrX4AQØš9^†s8TÌùÉwKKàyÕ·–Ãïê·íZ wá8®h}ØÖ òNž@Š\a‰Ð‰BÃ}ËþƒŠ9'‚+)’ﱋ¿mÙ^7þ¶í–ñ·¦øw¹ÆäǰBÛ„…vl܈B’æÑø2g‚¯½²MÇ\š4Êã™gü*Ɇ) ªÊÜ¥o\aÆ•þ¹BWXÞ¢s)î9zêuã Î(„ sSÈø­qÉõš' ÐÙ Ûj¶çï!NóÒý½|û9æâÔx'x”0¢òf„èåë7Õp,s1 Ns ø¶nö&Ž6¿Ö Ó¯Çêœþ‚<$ìf!ãX2ÈkŒe½šÙΩq-Å£P»ØÚÆžV5gý˜k+’©Ç’ðwÁ$PãªH›•K°B9NÁø RȪ5Ü(•­æór &5׳ʉdYíˆ4“—+Z:—X>¢’WL{©¦‡XŽðÞÈÒ,´ÇHmQˆ¬ ã)µE¢àÔ@Œ£xË2„9E׿^^›}Þh¯Yg I¡§[ùË7XÏhןiœLò³‰9O©—iþ|üo-÷ÿ'˜øÿ)Ä‚¨MÝhé'xeD»®9„¨d‡yIñ³„ñâaób^2¥™ ³: ߎˆC-Œ­ÂÈ'dá8ˆ|¿r„Š\Î+®™SAæõI3'ÌcHDl¾¯Ï†xkØ®P+´C+±í-=×suéèFdéÛ¡C¿éJû—z¯y~-}iSgP ĵlßµŒùÙ£dñF?Yþç›ò¤;yÆü·ûù¯+À)ÿŸBÊTFvSýUeW]ë¹úSUPö©¡ë…°.öÈY©O‡ê•ÿi(’g<ÿ5Å[½ûŸcOùÿTù_å; xquùç—‹O/ ôõËóu™Ü–霵šœ×¿: e´Üƒ–mP ²´ÌšGÎÚ Dÿê€mÐbZ´A-ÈÐ2»íŽÜ}GnLJÛîÈÝwtòÚ ¯ù-ˆgÚgí‡AˆÓ†8 í%Ø{Ñ«‹iÒuÎÎq•Ö.GØh×ú®¨/¾à/†'ìöƒ+·»qö²Ó¨ÏË[é`̉$½!Wºÿ0ànp¸ÍEùÀ£Tx§KÁᦺá á’3å¬Çm#¦k,qšØ€S}ñÙ:;d)Œæ]ú9¨áN*ÓG€ldû5°PïYõ^Ë‘¥Ñ€)Ècù\tÖÍðÔZ‡v­p˜†.Þ†Eµwr-Þ’tA/å(Q•‘÷ýVÚõ1Ç¥ý~ÔN™¢Æ'µ¥"ÞëDÔ×I)dO«.ÎGÂSZ‹|Ók£—’ *ף޴5ï5Q¤÷G7;³¯ãf=õ#‰\z½f’qÕ_ž¬˜”Vö‡-õFPÞ3ÞWç·cCÜZ©l(wã­‚$9ê¯ôZ¼ Ùøæ½keøˆ·÷’õøŽñ!l´ç˜Ü’`Ë~1“-HMnG@™±÷)µ1üÞáœáyÿ =Â= h¢[¶Sù2{´ h0,|P”4 Šjo¹âê¶éW7°c\½ƒRl/Ø­¬9ßiyÂÌ&£\H½Zx¶¤Dâf t‹rŸ‰¨[šÅÃU}dÈ#ª4 ”ž<îê—ß^fÐ ÁI>  ¢€^Û Œµ8€“Ñ0Ë£@Õ¿~\‚ ª„î4/&xóðºRÈúï8Ñõ>aè”RÊRÀ’¹º6ºŠ´´dÁ¶I× È¨ZÔ­¢aÿ÷iZq­3ÛšX)ô¹*w:;¬×)&Ùz…y7v¾Ôè¸N=#Я¢ aÒNíR/GÚ±îôLö¯((Ç7÷­ÍQïxE^0ç°uþÍð?¬êN¢›oUtÝÅ "èL:Æg¡66ú rù¾3ŸJëmzþ5ψٵ¤¦Ëòº¸‹…W|â–|WnL„**CxâãLÿüKÔàCLKåIø°íB7ç c#ç!¢žäE({À‚ÝA·3ð­ÿGO øsï¸JW6•¢—@ÑZˆþo‘–x­a4Ôó׳Á÷S~"}ö%2ô ö‡9CœvGãlâ&™Ë4„¯ øNB¹±Ï™LM°Dt‹¾J÷ùΖª«ñ+ÿH=¨·7N´êKŒoÚv Øá±èíýÕWOGÑσ@6é\J~:Þ^EæÒ^=Wh©Êõ1ˆšãã—Qþå“îAìs/õëˆCÙ¾lxt2ºKÒ±Ò¤ÔÂãpx$·Åš¾MÿgrÓBVŒ$¨1+c|ÆïÌÉNêTuç̼I“"HÝ~¹ÃÄÇVÛ›½'#ˆ–‘PQºÛÆþ5Þ_ ÖîÒŒ‚€¤<ðIÜS Ù­qAÒ «€ ©tÃî3û–æ­´÷¥’ŠóMqe²¨@ÛBÒ·YßuÞ?€¼7p׳6(²RÒjˆš¯_+þ_QÙýVoܺ^òÑW… y×] œœ¹HC’UjthLrî.F˜P  ÒªZœ¦¤GŒßëå/Y/•ºÌ[¿P:L÷¤ú\ÊD_v:¯ÐæÐÚ«"ŽI”•¡ /Θ;Án,ë:ú#úrìCwµQ·Êe<`,VG˜Ö 0¤l6fÜŒñTÁaÞ^íÝ!\KB}Ú^ÿEo2 ˜Íb £ÏCˆŸÃ² rÁõvkx¶zŒº7Y€‹œ48»JÆîcÓ¹Ù¥±X¸¹„P“Å]C†þtÑ÷ñÃJSÌ6~ù8hœó¿ÓœÈ»A¾rž]‰€ˆ–vO§âœn™­ýM(kiþîz@6_ßÕù˜Få¶ÙÞñ 'YVî2ò±Ì Lº,_C2×= €ÙÇ„”Î(&?Â6Û¿a^Î2ãÉ!Á(Â%ßxk£ð¶Y“W–ƒ?ÝZGjÝ_&†µ9‹=¸Ìwм$ÃcBŠZ‚!°÷jTäS¿•Gn"“óQÐOdZ–æXËNÁÜ ™.ކý‡»×žÔ[ôâ³¼}¶"!öz˜QÔˆ6„?´!Õª.öŸÆç¶Ý­„‹X,Â~`¾“þ5é„$p,?D‘™tef O÷¶Ûê™DçhWŸœ|ûA_g kŽŽ6îT.Uîj `ÅØßš1«»÷ì¿Îîà4³~L°¤ȦžÁôåä|l;9‡–ñ»½ÊÌÔ?2üÂê«Ó¨‰“}kEuDßëPâõV#6Mô–¦bÅ-ô!‡ÒQey¨ I¿ÿ ¦ù-1øp\;É_ŽÜªÐsçNoŠÎñMÔæµ6:ª"ºòϺû«}«ü–ÎÒ¾Þ÷ÝÃOÌìæh؇À~À/”8ÑIê¢ÔÄþà<‡É66îÝ–ƧH¾“ id¼u3Cª«ý’"v™Ôzú2,Lé‘Hx­ªúŒxds¼>œw-¼Ä/ žêN-t- J kÁÄýÇóQXpRÒ2(š/ùË(™·+Uj°srõ¡lØß=ö(*él¦ÎI‹ôüj­•̺V† ø³Ý:2Þ”½')Š^"Tñ–ZK] »î5v¯kUF‰üDúÅÇjœ­ª¸Š.Aä]ç¼ct+ëZi¾¦¨Kg®XxÞ\Ýoô"CÐèçÒRIE·˜Í£xx଎½·®Ì.®Qgpûý•^?þë‹6á è€`™¿NG„ÓMPÕú  ·M®ü'Κ™ÁëÐ_P>O±ÆõqS!c¯ÖÏ®Êù‚1i§pš Ìù«ˆ¾/ôçÓZá–›Ù%I”Lë¶óXËyzs ñ¢à!±ñÆÓ;oÇߨD“n×ÒÜú„-½ýúrlƒ/‰ Xd•è*üÙ˜ Ýœã=Â!§óÍ<~!5F¦ßoãYÿp¾´<5 Â)M­t¼o"ª¢EIO4~’wƒØ,}^õþ»5‘ ²üŒOÊá‚ÎMt .@›‰"Õ™¿ÅÑ1õÓÖõ¸vhgš'¿P .ÝÓNe=Od‡wW¿'°½ôÁá;¾öÃ:FcÆýŽ©*j¯_ÀQÀ|R¥¼èȯX*r¼ÚQÕe£E€“©öfÊô›ÊPûU|â9[›•¿7Zœþ÷‚|Yg6~ú} Ÿ"¹ý›ƒbä±Õi ó/Ӿ虶6Ð>%´éÁ:wÀ5&"ÒšF = À_q‹RÂ~ût§¬zÀÉÕî• £ZÉéfÜoªx±gxq›ë|Òm¬E;YŒñÛ·‡­1¸‘¥¶–Š,Ò;Âll)`Ÿð›½&#†„Ÿ6ˆ8ôÑÐ[½{L´:^q™fÎI4rë/fÖI¬Ó_†üR|Ùeòs‚Œò« '¶ˆ™$Ýå¼_Ë~9¨8Qè'éɼ]kÝêT¥¸&%á€"—æK/ùˆÇúhÝÑ“o V·äϺFÃÞtÑ¿$p^Ì(ÜàüO+Yj™#—­8k.3KIõº³žÞ/ø rêÒùyÍ kʬ¾¢ŠgG#M}0ïåh©H¹¶Hçø? /Üô:"L»XГa2Õn AáWvGåŒ;ˆK:±ßfhþ9]öXåVãË«Š£ATÒóàŸ½à»,ü˜Ï £Š_Ã*Õ_ š¹‘F #£ü¿ ´~ú‚¥ÓàDÒèBK [k=x¬lúZ llk| ¸g±l á”éþ²TÃ;àÙú+“Gôí!.;4ªÄõEDa|¸ò I;5%Enþ(+µ‘<ä¨G N• ‰-¸ð™cs]ሃN–Yw87Õ¹(Ïåi¦) ß’»±ážýÞÏ­"ìö"¿²±%ãÄçÛ% vfºà=™^xƒ®Å¦£MûÒßtuaŽ/PÍËn]¥Žçþmÿñ=bïœN¶©¤a×NêwB%82Û Ák*ÈùxR««ßÜûйÒb”Xü¶œîÚ`@Ú0Íà³Ù€´6¶¶I@«m7hÒÏ ðZšé(WwGÿ¹‚—Ozœ;oÅ y²gùI›¾Ù9³»Ð¨E_ΓŽßLÍ@tÌRRQOËŽöäßpôož,È>iîf}wU©ˆ²ï—&¢…0£6åjœ½f Z¨ qïK]y¼ûÌî\|ì°ËQ¬|’_bÈwe„7¬@#$Oˆé/t¨‚aì§·ä½@TÜ‘6ï±Â¿Ò9" Ý¹7äÓ6-†»X/"'‰;±ƒ´§6»}¨«SçùJ ä®>ênúî·ú˜^жÌ o$Ðì'»÷ø›?ãnG-Æ´½"xceªáXˆL<ËÅJzYÜ-;½½Q4ÖNÓ?G2Ú­¨ÒûAÅ,Òï¾UØð#gœÅ¿“à'-l‚ý•ª¢KáW|M8¨Üݰ¨“Øp¬í\‹œ¹µW»McîFq Q†=-ãqL R®-„ÿqاäzâ/Z͸¬žÉÅ|À[qZÄ;Fü“$þõ°(Åà*‚+ˆý—NÅ‚Ã}ø«è_bŠ¥4½bå –ªMÅ2”Ñùç_”¤ødÅKÒÕCÛýu8µÇüÎä  t2·„M WÇj@6ke¥­y¢¶"šm]5Ó›tþ‹, X9m`%º}M²¤LœWÊϪӂž#Ò¿ùh¿ÂqÖ9˜ÇŒ‰ãbÚº†*¦ÙÐkPÈæw¬Ã¼7[ÇU^p»Ï®˜B8&NT8¿bbÒ¶:ïb[:áÙæ9N´?±„TòÇãäßw–m^]/he“f{2˜xÓÌÿ9¸¸¼߃³À!žsšªé.jC»È2HàÌŒ8'Y‚—þK[ÇClÂ}V$2Lu|OCb8é†è¹ À˜½VdÂ¥Ž(‰S´¡»7Ì–„Ë^¼Çßš¾Á/8&FÆrIT n¥­&‰ï°†=à©7É$Ž6…—`ù6Í ¢ÝÛJW[Ѳʟ±A«ÔÚIdɨ6Ïq,›ðk+°= Èýfh| ⻅߃^}ÿ©•Æ à9¸bŠt¶b@|6jâ˜hõüÝü`ëèö?=«Ö_ƒŠ ê¦þßüùô˜ûÀ¶ÒÇé¨ùÌ9ÄÃSý (˜ß™8d*EÏbä¨ü뤯1:«õù†Á§NÉÖìQ¿ßf,ëç! b[ò„€½¯ä5»mؓ؂Y‚‚øôôd¹Â8¯KávÚD±ZkPž’ r*/Y?È «‰ß{¾ŸK"ã ÛŠx¯€¦^«ua2‰Ù”'–I]ÆÏC¼“ Ž!L¥àbá%‡ÛF€ÓT©ª’,Ù£Îô “ŽµÑ ¡ÔÙuTё軗kÑ™;´;kvoj¤ˆÁu“wؽFÃÉy “jÏG±•·ÅÀ4SA°qOμsÍwžÔwàiÍãôŸA×&²#´h§Ñu—U[Mmk™¥«±KàÚûüdm9uIJóžÉÉgó[I[šúSê@ž&VÌ6îýv¸]RƦïÖœ‚u’âIÊå&È%LÖrIêjRÆû?òi­˜§xK¤äT.±­Fwüfõµqû¶{v›ã¬:øšä8e®]"¼XP§Ý–«™»ÜûŒ³lz°¥üÊ# ¡ W(À}Ñßê'¹á W½ÑŽqó:ØVöÝFꥀùº‚%ÌbRU‹ðãýHÖ8œzáÉy°þ³ÛäÓœ“²dÉË‚–«VfªfC ËZ¼V•·GÉðë•ßF#òƇ3²Bœ­v!€+á³ô€/¼IðÆuH« —ƹ’˜>ÒܘÇæûÒ`šôÒ>K’U'JxóÍ= ¿Ï?eÉV¼2Ê ø=*©¥Æ 2ÜLȈ5uwÿrü†rACb;ƒ²ïö{ŠÊ—´K†¤x`5W@$/Ùoý&m'Nf1×8PÛ6 EJy!„SUd…+©E,î£J\RµŒ©•Ô°C¥+5:ù°*.U8‡Y!Ì®Üs¤^ÄÞϙؘ¬¨§‚gï¡`¡oý«&Èz2>§?«+ŽVr!hºm’†‰¯.£fø´ŽÁ2wl8b`÷ˆd+¤ãÃU$é;Åûrój‘nZáe„D’«v•˜Þ¯3ýÍÈ¢@µßÜÔ MèÀ\¯X9.º›ùioÏrÈb7ÀÊw} _u§AÊuIm±3h©êaU¿þýMa»fåEµÁZFz ë\£[í|Kì ½Û~Ï•¯‰Ñ•q€o™<Ï*3ìà†Ã­Sl°ƒÞ”Å&0jË ûâi_RÇàlk‰u‡LÀn{-Ѓã_“©¢¸Êu^` ÿñ8§úı:fsÚúì6¢È@‘MoÑlQÕœWßO2§h¨^¬åŽïÚ¶›H‹®ïî¾a©?ö’Ü]”AUðœ£IÁï?²¿JœTtÓò`5/¸Ö#¨ò²“ä.}ì,« E«[MÕuâ[0V:8¹ƒ›ë1qT:ïïk×+ñz'Ôläkk¨_‰ËóÔÞÌ_G É—¶¨Ó‡’ÌùÛT ^³Ng² ¼+¨Œxްó<§Ì®¨3ʬpüb̯µG!2[ŠÎÜÌ^à þïÖUÊk(”Hò^v‹µ• ¶b ].¼Zëófâ"XÙàôƒI“SKœ‘ðMf1{hêO‰)ÆÔ¶y9U‘ò¬Sj0IBNãr6KæÙÓZr«É`ét’Y}OãÇåàØWuéZŠ¿‡ý2çdft®!%nà-E@¿·Ú$© h;Pv 6/2¥v/o™'£’k¡F3SBÔ‰y0»Ø"vá¹á3] ÒÐ2-^=ÉÆÿ«èÓK|&í¹Ä2³CníÂÔïëÖ£ñ; ØôRå©Vñ[ÌÐEá3æôfº@°_.€ . x Çòdö‡ëæ*bäé>qÂÌ—© bqÔ’¢ Y×O~ ŒÚ'‰'v ©r2I 3¬±š9†ÙB)/WÕGÇ¿[œHП¿Ã‹>W›EʧR·¾kÐ>Áx1É.kSƒŽÑçH½ÿsé/YD´ê\'+$¶ðJ糈©$%#ZãqÛžÒÍÄíVåF¹ÞË}+šlKd_å–Í ½gp“½ŽI$ÈBË ák·º"娢>D4I.ŠÝ@*áaiëü0hÙûA¹”!–oùŸ9?nvF¦¡±ëÜö‘?Â@gN@Éx¯\¤±â-–[ ³Æ‘¹ zÄÍB”ο ¯ãÇ·¦H®bû…6DÞö¾"¯Š„èºJOŸvD¬ÝöÇ…ëwhPGOúr|jœÇÊÍ$¦[Í0Ö­J4Õ­S\Ú7SÔ |ÙùZÄ›¯qÈQŒïA Ê?ÇàÀL()lSX^t?éC<—M†Ïs}øó ¬bÔýåN+~sÓ¸#XÃ7V¯vm‘æ-Gâò°`!FéÏggxâçkòˆÖTÆÅ㘜Á-‹ÞG ØXÏÖZ„ýPOïcÉ:¹ä’E.öì+†™®GúM¨e¬šKámÒ8Væ\‡uødìQÏ\M˜!¼¬.Ý«å3Yíx®Éñ øÆ™u÷¹±nñ‚)Ù¨Ñü®:CÑR;ç»_ŠÑÜ}/L–+,(­PYÀœêjðV…D˜ÿAj}7ÈðþÇz¦ÔõQê¾’åîæ;ŠtÝ%e¼Š¥Œ¦oP#G;¶|Càf9 _òM7²¯¾ž0»b’—mT »„¿¯ž pçÍxØÐûÌÈßäzŒT&˜¯¨JÏ=;Ñ97}ÛÒ¿eêþ7ãª/jë™{–„Y+ dÏ}èóÛIz Æ_‹æQz#9;39pÍìõfÛRn07ôZÞ¢W\ÁGÂôŒ¸»þ¹mútˆ®Å^>çõ‚qäXŒŒ&ÁHm9ܼþ†ˆ9û'k‚ç<‘_Ž:Ž:8>=’Ú_Ÿ’)NçÏV”gó¼Ù~]9R¯Cõ·£~ç!;dáÅà¿ù5:AÓŠT?õdâšÉ·:q^óëF@I;b/˜uóÞscÿo2}œ‘°¶cÀ²Ú ÞÝ\¸uxº hÿŠRIo£| 0pÅÉЭ¤Õ1©ÁM¼Šjöüëb ¯éùýÐi}Zýì¸!ÂñêÏW3஦¤~ç‘@xúÿ/ÞgäxV8Üøõ¦¤!Í|Mè±!‚O¼ö®“ìã5š&DÚnŸW±“ðê pwļø.{Á¥C[O2)ÚúÏ2Åk°‰0´zì òkÐ#z «“’Uˆ‡c®I U:Þ/Àç(ù›è_ ÿD/{º†Üÿ&bå~heR1‚4Œ!—eSE/ñªé Æ®³_&N<3îÎÈ£בîÕ ’³á6æºþŃQAš§/âÒü‘k¬ÖguEve×–(?Ûñ^”Ak¨Î²à£N¦bÇ–Òšï]vZ¡~Dù³IR:Ù'†‡7!g%úw6ÍS1³aO6Rf7gš§Ç2<w߿Lj¹GþW^ü{¼Y-NÙ³çÿû²æCux7'öŸìÍè«iµ’À¥m½Ê˜7)($PS÷˜ÈªIŒð%3Çœ'#Ë~gàL~¥3ãôîúèŸ Û’b "x·ÿ¸HÖþx´Æ,û Y÷“ÿÅÎ,VëÑjM«¢°¶Vôl’mø; 7rƒï¼Œ/‚xŠƒ\²ìÕj¿… Ú¢{`l¨OÀ´Nwÿ8fkðL3|*rú¾_Ï?BR0ÇŒ;Nkß2Qöâ‰Ák}+èÓ^j‡eFa¸&¹Y!ÙH·E¨Ú.ÕÕ}dþ”ð“b‹Æ±‹¬WT÷]’R’ɹ“kÜeô"Þ^TOË(CŒ\’ûìäæÇÆ‘Œ@Ú~RKM«*ÅÊ •Îmn}£ºåd±;¨ýh 3tá=©ÊŽ3€+7"ÇY2ƒiP®¸D©Vóy_ØÑ)玩_¾>r_ÞÆ<õú¾>d‡rÓ vBEŒÓ‘%%/“+Y²¿ÿp~Qq¦ jcE4ã:—1šÅЕ‰ÿ~ Z'±|FÍžÖBÍŽ]Æ~öð‘$]¿Tv>¤m;šÅXQ»·1[‘' $ö™jx\kõ¸¶Qz+|]\Rý¥Gâ[‰—+¼J›þŽSÆ|1 ftŒéò8ö‚§Iùz‡?‹×òäªJe ˜.+²§øxдÎBc ‰˜#ËE"vÉKV<Ï$÷ ÁöâÙ”>þâƒË5ô)Ñk!ÈÅÞU){‹äÍ4Ì‹cŽÝ9×T šUÓ—Ó˜;+Ìí9fsÒÁEXn·vÿ¨dŸÜÞya`Pµ… Ф`O˜FÁ‡†.C˜2‚ÛÒBeRŸ9a*k¤{ YóÏϨêB¹dh (þJ®¶t„2ÐpÉ ÐT¼7xë:rC¢ô9ƒEa[dY”W· Ê¤¹€Ÿ1mÔ.Ó?’íøÕÎå͇H"©e§pŽˆ¯i‰êÞßúéeéìøâ3›A B­øé½ßB“§VæA˜vÄ­B­/ª±„¥š2Óµ¸¦‡™6•…¦~òáü\R€Õ:¶¯·åN›P(î3l¤‰‡!( õl@ƒ‰4ÆÌé#±{7J;ØÑI©%56¯ðo(airT0oŽLã”S7–tÁƒ‡&˜Ä4íN°ëHüÛsFÈ‘ãHµ'ø^9H aÒa±3Ñ^›µC90ˆò‚l È<ÔĦZ×kÒ"kpµH¦0êß|^žNNz"D¥_ÓþËq Õ—p>:¿ÈRé˜ÑñZ¥ä’ÂxÒµ óhÿÈÇ"ÝñD0)‚:ª‰ˆù75Yv6<+ÿù7F]R/Ù‚ò½z8ѧÜ“ZHÛ) ölIØ÷C'o»$•¡^‘½´-ÅfQ qvý‡Ü…LH«Š\R%÷‘\ó7q°¡Ú3ÎìhßYêåÊ)#Í_P˜m\âdÂp0\lÏM†/ñ$ãê³nF´oGõ( ^’üúõòØô»†a‰ìñ”ú¤V.3ðxḥ¤O¤íÖÛ<¥áý*3¿Ê{ Ò%‚U®2í`w5à“‰Õ™” ŠHšl°DÂªŽ›M¨0èÀ›h>YÏÔÓ% íÙj·úªÅmä?ÎiøgïènnèGäʺ„ Ì›±>í0=ú Yú)} Ñr‘êœÆQfµiJVp•5çWêÂS>/ñëzóİ@`šTøI^à&S”ÞæNY3‘þvԪͲªÄ¨êå‘­"ä}ûIˆõïä2—?pvõÏØC?èÄHþÚz¢À&A´ 1xõÑ"!w´­1 ±cÇ]ýÒ.ifî¶>ìþ¿ˆéVF™‹Ë¤Ró—óDb-U“¸…'½vzÔ›0JåÎ3¹eØöÃ=áDU’ÝÖHÝÚh¥Vsû‹êEò ƒÁt…CT~£¾@÷&h](ù›è X‚§¥ýçìà²÷T‰¼1³~‹!?Ⴓú"=ˆ°—Ù£­`›{pr- Š¸·2ÅÖ+.êõÁ¨P)µþþý^pÚî¡‚“ÞæQɬ¢u\£Ý»–-kŒào¦”…È dÈ`šÓ’ kg”XI­që92¤¬ä~l¹vEµã …¢Q\Ù¨±3"¿˜ƒÒj_äÄ - [ˆíèÆÂþ¼–"›f vÆÞ9ºþÔÖADl¶üÚ¤™:'o7Ò¼)¡¬J¼qÔ+3þöÄ'@#QÃé­Vd”X…_DKéoY⣡ãB%Ã÷'‰*F#Ùx½…„Ýî‹Ò-Caiæ0é½&(YÎä"il™Ì ˆ©-éè Yˆ¿hüy2º^Ûô3aÆÞ·Ø´Y _[q­âM„On› ÏGyi7¤cŸ²¾¦œÈ ù£gãŽNÉíšCé®Ñ¾‚¬Jc‰é"µSEbi€ÓB7¶<}ÉIKâ4±âheú@XòéJ!’‡0˜+¹¦]é‹@xí]-ºW+¬€šÂí|z u…̺¿k³7^WLLœ’zwPµv#+ÞÆ…Çð4ŠÍWºêt¨Z½‰‡UÀ7uvm†ðÑ iQ¼-RܲIU›/ëEš<9çIGó5’>‡b»£*1Ä÷›Î¿1Ïy¥ý™Y¤¾[5m¶O°b³øŽô“)Š‹Zн¢@`*À¾KDµ (…o»éâý½@øßÅéÛU™ïÝø«ñ[+¢‡ó÷Tʯ§)%™˜£”ž‹Q´€Ì’H' ƒ‰>¨ ʇV W+­AZùÁ¤"§Üp¯‚2C’‡ü‡%W|Üþd“g 7Z‰^!Ö?CZG6‹ì7\e Žº<׺³{: ÆÑs§aÕODFÿ>Rø¥N›z O•©ü~#Ërü곕yjÕ‹(ÿÝuÄ›fÅ{PJû•Q“dú}>4[2ð)ÀkaNêiwÖ7‘üÏ6”bë$ ˜oé=ÒÛ% ¿ŸÕ@¨ INÑåA¹°Í=´Êã}"Jà9šºFFIõ°®7å¶Mèg -¡&lž@Yk°Â“2"Bƒ±O _ZY]k¹×#ëµÌªh5+<–Τ÷×G~y?ðÌÕ¯“3H“LÌ’‡:ÀNõ‘µ5´‚-ÚÛJ¿¬2Ϫ\8í\e+œ×­v¼ÌÈ)«5qê}&‚T#àf×ꇮˆFkÉß~¦]Óe'XXî0euè~ž¸m$ÄI˶M´¼ÙåCoH ‚–~“©AXrÉ^ÝmJÍ’,8Lºs§„á“Ø@R'1A–´J}ޏøM4c‚QVP1ÿæz;Sãµ}ÜC"BaDq/HQ#B­Ý%ä[Eeh¾Û/¯w˜•*ÆT£`ÓàÛ.'¶kèOñc¢•¥Ð5F_ÙŒiàîO§•Hb+~اRl(WhºùÊÑÌ‚ß"9lïßF±áRL¾ï0ü$›ÚTñغKao‹Ìô^:FÅxdèŠÕep+?]X`î@!p0«¯¼_×ñóÜwnòtölÛ¡×H&Ð9å·+“ëhe¾¹zˆ'müÐi9¢8)ë$47ÊR¯£¨É9¾ÝÉg´–Oóô†§u] Ð8žµ®…¬ðSÜ;¤´BL[ 8µ~P#ä/‘vfø½f™šƒvg…AÛ+‰§é1Ø·• ]¤ës¯N¡?L¥kª™ØW£©I–åg ðrv²~U.`²/îŽNSŸb|å­eH_¢žc†í) Pª<µ £wsà{oÆvîËFˆdªœÏ»#Þ÷eÆ©³ yý×¶ê?]^ÆQÒœ4\ãaNŠ„z…<&8Kèɇ†®©…/ WT`ý Îs‚Ëý\¹]‡o‹/*â5µcJëÍîU xÔ( ×ÈõQÆŒy¢Ì‚8ŸÕ9x§ûÒ ¹ ÑnÞKn¦ïX1¦Œ”ØÂg'ºÝÛ¯„º…矗cÆ—ˆãUæ´øxÔ«à4DKõÞµ_Má‘ÞHñþSó~ÚáëD)*­¬¦—:–£¾Ww&Çÿ&¼E bò¯é‹Ê‹2‰y׈U1 m8ÖÏßà•™ÍŽÜÇ#~H s¥Ô—hòDrÊmt×jˈ²» ‘Vµ=w .m$›:F>«Ä*Éݯ‡_–kŽ{|ÍiR¼« U¤*mõ%j+"¶Ëª÷/†áµî}Eí“"x×}D«zæÍ,69/ø‡²¦HŠÑpÎõ.Kš÷'Æd÷'»éßJº¼‰Ý75°¸ »ž_95‘ìÁ³pQN¦}»>=‚"‡=)EëæûèRùÕ2QÍ©˜ÓU4|…è ÆÑ©zÝÆÙIXÎ.Jrr¬c«à(ôÙýƒÛ}UxvÚöÉ/ΰÁ°F+«*³L¥ðd¿^³2Q@C<€–™W4fÔ'5¿Ããó"äMñÿÔ‰¨‰º^0ã%§‚3ƒæ°ŒìN*²31qk‚]¬Ûuxv©¹ƒ½×XKb,?¥LõÊ0ödî± ¨ :Àst Œ×mnL2ô2ÑR'6Î% ·$ð°QÁ^{Ú G®vôQ~—nOÚÊ‚p£ÐI%\~2l#'¾Xñ‘µÒh+‰‡µÈ÷ŸiËáç “(ìa ›áÀþ.ÃþÁ%B:—" [»²ƒfµc@„ö@øYäÌhöèf'•¬°ZþÌg¶õôÖ‹8ïõF@¯””ðØ&D4’ÚÙ´‚ˆË<ùµsãÁ`:â˜Z„ªïUx9_ÀãÍN¯Ãâl6vŠV¼WW`oIsýóû–W0öÙÄw‘~Qëªß±•&ÝÚ¤ðç±á:-ßûÇ'&LÄ)ú‡RãÄAOfppe] hL.6ˆócpØ´dÞa”ZÂî 3…Ñlûél½Ð»¸z*\@Q7õìãËþ§Êx÷Z¿ùE12Æ3‹w*jÖä2i1®5pç²Í¨ð" ºi)°Ãá%®YÊìã!——ˆð^y¤âq)ƒRôÝ1¼¿°C?Þ[ÿM¡ žófÜEŒ£„¥"Tšìi\(Òö'¢¡óˆœâô)vKÐÌÄË£ù(û0fFLY>dNBþåÙ_²afહ(kŸÿu›]ÄmDµOhl“íÈcþr-( x®³zËm>«$þŠ{ (æJŠl@œ"²‘‚ÏzhU]$è-׉­žØ qÒã>ò½dÛ ø=‘£æëe6Zÿø~èoY¶.-Í-á ¨lß4Π‚øOUÃÔâ¶D¬×— ÄÒÒÐRæºÝ€´7¨ûô›’ï l’ô»®ß‘M†:SìÌC†ÊИäQܽ6ÏFä‡ýO\ ‡Êxœ½¦Ú™ü¸Ë£d Ì¢dÔG3uY¾¦Qq™xÐ ,Ô¹ ² ÷’2qع×Õáä¹h¬JW÷§Î‹‰ Âå5ŠÏ™÷HÆTº ¼i²iXpê¦fÛ,`”s[KýšK‰Oå­í\7Sq5ù W±lå ç®×«pÎ|å–(Ù“qÙðSû( n¢+›‰ô–}ÐT&ïµ7Ó"f¦ù6“q”³¨{ã6S·Nþ]PÒí*‰””ô„Eq("fcS‘|é®Ú0‘W'Šœ+š_”ƒ”ßðì…5åÀ¨z S6:Þ î6Sã/%¼ãBóª É_à‰öÍHìó[´‘Y@8^­EQ3;ƒ59qLžÑ5¦¤A F¤œ~Û;ü·ÙÅèÄv€Ý`{bIG;ÙöâµØ+æ|çµÜÀ„›]ÆöîλO®—-Â6Ó34,X\†_Á’£)^©¹É=3ŸÜ³í—+Ø‚ÐÿõEd0FTöÞðغzW,ÉÅ|Ǻ(DÃ:G°޽Åü€þ6#äQç¤XöbްQEF[ \ËVãÜ*7QÜ„4P?¥}NEJA&ßÄoûg¡£Ï÷G±‘++¶(ä Z¹¾ý[Ùï¹ß_?à€Þ_dÊ—¶Ý°~úÄãc‚—ÐÛ×ì"ØûL&TµÜÄÏO,ú£È øÊ9Å‹?B®Ya{¶dƒÎ>”TxÀ)ŸÖœž™Ðr²ÊG¼ÈÒàÌÊA0Q¢iS¼(”alnÅv¿~ªu˜o¹ 5²»4·gÔ•<ɈæÉ Xmú-ÈÆìk„oh 3 ”º«Q„90Xó}™¸rœ¢ëº@âó"ûã´Vz3=ßþ±‰·ô¶”"ÁàÀËö0¿–éý=‡ê)GGnК?ÃîRA '¯å¹è„Ñ;JßìúãD’åͦÔs*¨M+Z2¯7´—Ó‰ÎìË…:o­Ý=Qt·¯©ÅÃÀÁç*ظ[‘Æ'¨÷¯t< ¸|P£VþÃÌ–Èhߣõ¯…}ňi~«._ƒ,MóPoÔ".t±…Ìu…é·Í*¦È÷¬hý–®º Y®âaþ ü:dÙ›ùy y¨|Ð/÷&ëÆ>bR^ÅÉ-u¦‘¯¤è‹ˆZ-îkGg”õ+qï(¸$ê]_ùÖ4L(…Íú0«©PÍ&ã/"°jœ¼¤ {¦CéŒmV\=BnWÀR<™ý‚74Adš:}Ÿa &åc¬Ë|GMŒ#ÛÕøì<Ž¿o›Ê°:ñ¸˜NVÝþ8ȃZ¿28+´Sˆ¸]1²º87,ŸŠò)^`Â’H[˜|êýï½I¥¸ì‡éó{ÌIƒÑ*]ËÌçëÐ7CgÇ#šlÑõ­Àü_]of¨á@›¤ÖyÌËò‡dˆq@ŽÌoTàr7’cEyàNÔm ð-M]Ùú'w>=•NR?κ^V»‰+ݘ÷D{7µ´£G’ã"²ç6ø?¶kiÓäbä¹aßÚr9‚ãpã½~Ȅ׎G°.ÝÀX¿Ùª%k \ýR¤èٟ}…7Ä"à„îoÎø.äb+L²F\ÐxO ŸˆBäe2ÆCd]j9áopU¤{S?Ý ÔÈlîñºàØk=˜‘R¯Ž m>¢‚ÔþÿXáÈHÌS.Å<ѽ¯r/kC—HTI€TÂúÒ˜0¬à¯í=)ªgß ;ç0€M÷EXã7Óo&Ôµb\*Ä ì»w`MÖø†Úûè×ðFªî`}ˆêQJœ+iÀÍ’÷cÕ¦oÓb ‰÷#-|ÙÇü3Ÿæm= ÛY} ²KWZΚ§³’Óv’>¸AõDÖ0˜9°XÂòÂûÔê)·‡UÁ#†ýç¾â3Yf’ݦÄdù'òìœ<­Z=Z„l\Ï÷äWÛÎþ¶Ž? ‚hæ µ1Fà0lòÃçègž·ö £†ÿ/A>œ fß]äî‹AeqP£yë^° î5š¬!ÜZ«¡ œ„݋֤³èˑÂ;ÖL¨eCÄ>J¥ö.(QÆ-ºÇ¢‹ f'½=r"Yó8ލ°WÂ0?…|Z»³ÈÝ;E\ÿ]CðñìËx¶—oiÿ±Ú¡:uµ; A?]ùIgèü¬üVÀ1€eßÙ¢ô}ÝnÎ;¶%y¥þé¡‹@0âb>nú}"VËxžÆ“V¶ð Ÿ›=ü†Å0C€èø”–£çË6]"ZÝeI·Ùð~`Ÿ'ƒ^ûÈ(Žúkæ´ßKÝùL±.åÁ±:äÕYd³®ÕÜ1Ípû m ‘K^ò’ÅÀz»r ÀD0ªðzËä— LÖù_ú¿Ð¤„á{ m…KÌŠ_˜7[ý`‹2[^Ë®\˜oÒï±%sëŠX¨é&\wµT½·|FÍ9‘vN”¯q‰,©gÌÌ·f»q.=fNÇ­`Ô<¿jWoQKèœÔM‘^rR4¼–ŸsqRh¬®,› S)VôBŒª"ž__ GnK†ŽlÍ"3lÂQN5¿Âuû(ÕÔ5ªá{„m$Ø.€0¨šP«%0¢üOýb«20dÙÔ*´ÛõÑXNIñ8,áJÒIMb[ƒ¼J Ý.ÎírÇÜm¤cÛi…¥V?ß$î.1+2Öm½q¯_kÄnŸùÁÃÚ÷–ÎÌÎg Kd[Ã{æ4ž“0S¨Ò0n`ÙÓ~ä‡çÍø=“Ž GsËbuñg÷Æm{êmìÅ]Z´_ItE'§ã¶ø>·ÂÄ¥vm¾÷\˜ø0éèmdzæµ××Á/~–¶‘þ˜Ðs®ý]ýë¨cëëW[!Áë1á»ãÎ0iG(N£[2ÿì”à!Àá´œ'¼(‡$€©¹¤þ§&(ï‚}-uO¸bÙÏ8•´E‡6@µœZyóLª¨¹ÿ@¹9m~ú©°L+äí¤Ž#œxgYc%-¨6¡eáìlú៟½ålû3l•À`|QÙVG:²|† ‚#/î îcîfé&©’ ÐȨ‘$~"ÑaÅÁˆIÿPVJ=c$L=tšŽË¤ú¡ÇRö@¸sÕzcW®À¤iKÅâiÄZþ,Þàl›•¨=aË#i=­vâ·¿QA?¾x£FÆ;5>´ŒÈ´qÅË%›„lª”„,s™²\¡v¿9÷“#±ËÏQ­åðl%Lu]¸zvv½ -+RÕÜÛFœYLQ>Éš·ïsôi—rÿ,™k§kÁ ÓÞ”VA[φ}¬ó–€V£›xƣ͸Šlp²¸bæð J½\Å(øL¥þ‰è°Â­¹j˜µŒ-™"šHvªz_–½rU>æ OGõ@𷢉ˢÕGsxCä\×6’QAŽLû »!Ëêú˜ÉÖ*…J­ˆoR,OŽ_¼ üdÚW-8î…;Çj ÇB¥ÖrG6Œ«KvÌIXK-VC4ƒ\+jH$€—Ø4† 4ª¡É¤ÄA-¦å§ÂR…EÆï-¶k÷Ï猓ßÖ”ç^É ·U]Oí¤{éìø7 aͱIùvö;B$’è{m£@ÅÁõeæ'âÈK@t‰ËȤ-†{‘‘Z¹0ú†Ö*e+P ©Î2´Ûº{i¯ '¬3— Ø¢+ œ˧œŸ+*Šsê#7ƒé½þþîRZöƒâeM'¨FpÛðfÁ8¹×^¿`0npê«Øƒ}¸÷ó@\Õ™âYì–ÆÆXîß Q½Ø+.ƒB–ü&½YÝ}»?¹xÂb€y´ôYD Âøk»[¥lÄ$@ŽLŽ«ÝÖ¸Ó2ÅxIêsãSdA&é¼p#îÏÇÕ Þû|³”‘Øæ}œ\ûùô &×íúHòòT_ÞÈï^‹——_!⑞†¦‡t¼4˜a÷®k¸×ãæÐeÿKä‰õ¨‘Üj è±E#¾øÿwO‡OÆýjLkź:/R»çÖÈ/{h¬ûÇi-DפGšåbÌvI¦!®µ£uÿ{¬J(¯ 1έnešrm•æ²’"¿¤á™JÿNŸ¶ðçaEÒ´Ó¤@Ö0›±Gi¤ü\—¨Ü[RÎû-Ð@ˆ’“$‚èÔœÍ8",'×”®a%k¼Ü([ÿÔÚL?Óµ-èž]¡Ö¦}ïõ™T³2ø¿5Ö,µ:|m–}oyŠHkÔ;Ü5õª»Çè3…}1³`f}íA©ë£F(Q’‰»(ɉ‹tÔ<ê‘ÿ‹ü*¼ˆváÛ\[×[ºq¹q;"|³Lú7ïû«T™°…^5•¹Ðbd#$uË“ÉT.Œ$³qýÝÊže4(£-c[ÈMð]Y±Â០¸ΤÀH‚—2÷‹ýü÷<æKáÜõëûgsÙø>ÿODb´ci3mÇ•£]ºñE:=][‚³É̤ï;$Œ¬—Ɖ¥1¬èÌMlÿ«Û(8_xt©~«£ôbŠÇ’sÙãÊÏÏìPPÕ–_«¬Ý ³nHhÝÕÎì/Y³þ2¨ýèõ¯tPY­ùURùj[:é¤tÂq›Dâþi+'dÈ×JÔƒVdø<Ì9hb V}äl2½«Jéþ”¹Ò+û{xؽAÓAÏ ø%¹#H <×8¤ÉÙ“ú—Ê hô z3HÕÁÏKc[[Ôç“Bœ¿P}Û@q‡£ŸŠ8bóïÎ Ô?³º¿®~P3W•‹»&ï¥rsà[úw‡b¶û!?‹¹íBŒ’Ü;ôR¡ü2DOþg,ÒrXõ2ís]Žß0–JÌη|2~/Ïè ©é÷™r tA²æl¯~7ˆ·mCÈ$ÅØö:\"ˆ,Ä9odqŽyZõr07òlú0ÔlþÙs”@ ê˜$ÖšË.z­U«¸ -¹kåŒâ€Ã'—œ%NÙÒðu×¹„Ïgô½üZH…€VÈÒª¬! -ýx@F²*¹ÝjDa9»oÂ7ªswב0áõ9¤Ûóç#?¿SH†>ÐEŠ4Ec·| Ç&¹­ñ RàzO\¨Á¥½Cw*‹¿KÅ/cV4za– gÖ!*ØP2ƒæ¹Ûe3©´Kˆ%G øý󬬪áG8ÅÔ)á²Pw¿jî¸çüÈŒÏh_“E-Ž5_%èYŸiÊÞw« ÙLu¢åįÒÛ’Š`ëLI\%Üd˜a¨É] ÷Ï)¸–òÇWÿŠár?À<°ˆíí÷ˆá-Š·¸Ýp{übé À^`îÍð[Ô`ø1s%Ñÿ)4Do·8V¬˜ü¹\^ó_å8?y¦½Àâ9XÔìZ`ÇÐN-÷5ZÙ‰¦OÂC íÒçiˆ‚щ½´)ÿ,ÿù8l®Ðf †µë'q¥åpe\ßh[y,æ^kõ#5Ñ»xÀò=|F —›÷¿”žN…Æ/¥^ÕæXäP_^áè¾ñÝ÷C«Ë|á§²{ýÿ>›Qn­Ûq^c.hÍ:@ ó½%êËØ½LÖì‘Å\½é>á‡(3üi3,9¥<8oÎäÑ¢ÊËÏŽü«¿Bwã·š4f@•X—N§•#ªÝ¨ÞÝëT̸'3èÜAqÙm !ae­üœ>‰m'òcaúÌþÜ©wð ”ã!YÕXÊÀzziø ^ŸÎU{èjödXEöü‡üâ%ónŸ‹É›y85hE±Hœ TÝ2uS¦Ã©1xpç(¼Ú!ó«ó—P¡dÆ““V³ô%1'ÊM‹qÒñW0­7¥`’é.·Æó2Tã{q›ÖüGW²©Ø¡—Ýå.ßHÜ!ÇžS¾Q©&{OßÌ¡ŒF_¬Úfî+¤3µð„QHîÉXÅ@f醌™Y_F†f‡?sü—\’ékâ="’På½tsf+ V'•µ±¨‹¸­q´={‚ÐÖÛ§j\Î&£;,·‘ñ–‘‘y `H¸‚#®Ý€Pâ+¾rS .*°Ä5T:½Â ¢C‰òÒy]Þkì¦4“š¨ûúåÝ^åžÒ¯æ¿-Ô6_Jàã›&(¾5ùKð¯›ë¹>å$ÎôÛ³ÿ{ƒ¸ç®Ï­¡„Ð~£ ±¶L=š³,nˆ€"]†XÛÆ‚:Zäá-µÿ+¬Šÿ«zô î¤ã‡W,·›ü¶õÅÊ»¦šøÈ³sD‡ñ¤(1XFQáI(T!U Gˆæ¦ðSùMz¶ê~·O°‘Að=}õÁìL9“L|w¤Ï!òO‰ImAz“Z柹{‹\S|î§Gò½G]×Ö1¹s}ô×€µþè̬AšéѨ†´ÕõßòƒÐY‰#ÈCQȰh‡s ó–­ ê…áÎ̈%“ ·Méj¿È×e†ÞšYåº[·ORѾÒG¾ø`xz6GÝG&cê µ¨:ëK§gã<²¥˜¢ÝÛj\ÙÖC˜} C"£f~\,@É’Ä}½àÎÜuvÄå:âT‘÷‘º¡×ù·²‡»KW*²’P4:î>,6Òâ€ÄÑg¨ùÑØ×M Ƭ‰)n´mßÔ •@˜ì]•—’b~åa¡cdžÉa  {†œó¨˜;‰J:ù3ËžD÷‹»_L&Œ7 - /"ª£ñ#Ð0éýF©ÁS!õzm‹Äxä„ÀÆ™°Zs^f¨¡Úàj™5…‚Cz ²OW㡊aæ³Þ¬zÀ `ÁnÂÆÛÁ„ÞõÔ·Ý5Ûí^OÐvyÿh>^ ?lÐÓS Œ˜ ¿ÙŸªeúÛv.ƒïëõÐÿK êplú's¸Ö+Ƚ¾"ˆþ­3Ãt *aôɨ~ÌN>À_øþþ|<èÝÉánOÕáòHÂgv{æ‰]ÃÌI@óag:[ÅTLÀïùéZ ?E‡LWž§ÒPü,‘{3½/bCjíÈòÊïT²qÁ³¿gôE é($ÖúMгX冶î?ðÄ}ÂI›¢ƒ™Þvݰã°Y߯ŠrýtÞgÌNv3*1> ­õ(ÎѦ&ÙêÌsœÓCZe1Ÿ·¤X µ9tuë®Ê_¹ß^K»×åu(U‹£¤Sô¯’£!+Vós%­ó”%ÞZ3K:[ïë%¶ÙXRTÈÏ‹|Öœš{Âé*ëBA)´-sÑzÑß­IŒB­T=C§² Ãà‚˜³žvej£ )›™„Ûü–ÈÚ®f²…Ã(¶s™õ¦ÇZ.¿TO¤à{%ÌŸNçž<6r·1Aµ©»óÉVc²¸¦SÈ‘2Õ@к/Ê~ ¼Z¦ÄF²Í«” >™3[»·É;ùð»M¨mÜDX^Ü#apÐÝ VÆ—ÌmòÏ}Æÿ„›ÚŒ+ÈJöôóc¤¨á…á_'ôh"^4ïP´Y™98§nœÑ6;¶Ò*€«ÝjR ÐÇç|ˆ³±_M^h˜‰2t¢bz¶• DûÖÊ;XåÿTùð@·uÔ9§òhÌŒ¢íkÇ–’¿Ò[ëvµ3ä×v¢]W;q xÄ¿-£3 ÓN¢SˆFdum¯å/ȯÉ 'ÿ'±ñåW‘*ºIÏ*Iùc›²ö‡/Ä‘ûŸ/$v"£^#8+ݰ-hÇ«vè¯q£Ì¾"]çJ’Y{DÁ|X #«¯lQÞ4z“mBJÇ]6š§ÛÙª@x·-ð*:BMÏsoéÒN¼~¸­Ê+§ eTû†L$Ûþrcruàq9Ê(¯Bxä!c³J¹µ8Q8ˆT’1lÁyUe}â.yÂð®×eƒÍÍVêÐr5,¿wdìŸ!¥Çßf¡¦6*Œ\u¶Æø¿ô6,…AYbLñûYçåÚûðæ6¹ˆ;æSUÕ‚$Wl¡Ú¼¡Á¬ø»ð˜³°øZ´" ŠU§í‘› %•ì†?âAÖ¾SÃT 9Äè†ëYmK÷<~.ÑÂ,loŸ0¥ìì£eä¿ËÝŸ­Ító·T»i’ÃDÉý”Uc3ì–ß·îì{â¥äý¾Ã㔫 ž‰A¿ÁáSM ¢,ôÉ\ztù#°ÿJÇù6ŪaînBDæêæuuf‡pVÆÅLFÈhQôâl øEÛéÌÅÓh%MˆÄÐ>½TU×f5™”jÊüâñ}ê© ¡lèkŸn,êŸi—ŽöP1ø~GÒ,ü‡?âµRaö‡ìȵ@H* A•\8ƒ„x8\"¸ï`fc˜UY1Íódý,+eÐß0ð„UI"0×Õü\6½óÚ„Ä/i3Ñ#¬Š®ó›ìïë@&Süb.¼Ç9µ½ yG´å[[Ã+Öt{jn^¬r§-’±8j$ 9¸jà ’#—¶¼„ܽZÛbSkDDðŒ#!Cå˜Yn­q~‹£ Ù&A¸à7hM~}þjÙ€'ô*Z×Û´øÑ]zk¿®ƒyFÐ_èšvQÉÝ}v]Î?*…°ògù“&Óð‚°Q·ö{‹«¤¶Ý•d+¿Œ}N5®Ï´}ªïVšDÁâ/VVFû§¾éÅ4k½ô ;G"¨é5ÁÏ.µ7ôë$L™_±ë'Ü&Á«xHf(²—x`t/׊¯%gv_¸Ïr Ćë–£Ø5^5ôšý®÷¿U=¨ Ö˜kExîÕþ¾}=ôFIRd¤ÕÊi6Y©$›¢û“~žCFŠ“£æ‡vínÏ»º檱“‡§ßÍY߸ÜPW¡½Ùß™Á‘U û“¦?¦}€€°Hf(²§'§B‘ü‰þ°~Œúîy‚Ï{ÿÀÀgpºÎlkϹäad}Ûâô™péò^g×UNƒÕHˆ´7ìÇUáXH 1`CVZ› )4RÕö„wF‰WJ­„ãoVŠXÔ_Ù!JÒÑéÒ^AÙÒ¡oÊbÃíô3­ð)òà¿Y2ÐÓ9½å³"O%Côë5áý«¥žeý® Uq‡ ¯É÷w‘:Z ‰Çyæñ?6dæ9é·«º|0@‰°ÕìlZ‘”1òÔΪ+°‰èw„í–z~Jáp„äL5jÕN𠙯a¤T|_§bà2’Moðì5•ø)^½³Çõ6,(.ÛDÞþê d×â{ýA’jÓË#¾Ú Ħ2iß{7+äòpkÏ´l¤ÔiúCíç:/ù”9IF½4¤iâ¸þz7 T¥¶Sû·´'ã³}™šˆÀQÖ‚þóâ ü|:¡•Ðñ+PÚþÐRèž^ ‡¤Õ¡<’ùÇà"*3ÑIÕñ¥­æ0?Ø0Ijå©w–ï’Kf–jZ¼}y"¨Ñ8úñÖ4ÞÁKJ8 ÿÎH˜1ì' ¿ñGM9 •WÀÍü³h»jtÌZž€KDv#ª¿[4^pb(ÎH™ ¡ÿ¨V5‚/e•L S#/1h™¤[ÅÞô=^¶$hÐx"S„Bßô¼íq:šÎ>*({|ã’¼P™³Q–xDõrÕ\§Ð¤‘L·w±l–,R(ð†Dp!“ªt]’{3¥@ð ÌF-cÎëF£½ÇW'ÒUd²-E¤Ë,3+ý˜RÃícUþwlš_@‚s¢CbýËö@‰‡š/ZØ…‹@9 #]|ÜÍ«iï-¾©mà/Þ”­La&†6 ÃfuàŸHzjòÿ‹øGp@ûä©’Z‹ÐÚ W,­ À÷>¤Ù‹à*ë‚° %Xÿf æÍtaÌézL×JŽuè6G$‘ Ç òÚzHùZž°v.¥ qFNâoO™µD«0Ü,Ü%µ}YÚ=fÇ pg²Å¶ê ÝΛ ÒÀååHÛPRe`õq^¼ü´8ª`djöXm3ÜZÑnv;¾}Â.˜Ä>xO’gŸÒI$h9Žœ¿´ÄiGؙʟiq)̹Õdº+s,Àj#'Þ©Z„" ®½¹þÌloœ7 Ç@Ýÿ 0ƒØlëqp°Å;¬š’ìßfôÏ™]5øöì¡}0ÎXÌþùcíT’`HV¶Ä„%{eŠO/¢(Yv$/<ûZß虺rcÝ_b€ ‰ÏöTö>‹Êï¦eâÿŠ({–`Ͼc@mzÅ(z©CN¸˜RB.¡ÄññYÖv54cª9ÊöÖ雚,õöJa6ÎÒ|Ÿì¯BŒóÈÿ ˜Õ'¦¶…¡@Ù¿/ÃÅOONQ¢Á‚ìB àíÈïV{²‚¼ÀÂý5ÙÓzPÛ“Ú0„//³ ” ”'âÜ'0Vl{™{죓IvÁ*‰àUoœa‚ãF…N@…  „kJÖ!•5v£œ•¾‰gðÉê‚t= j´`‡MyN©‹ÅXB‹ä`¹;›š^á@–¡ñ¥ä(N(pf—¤¯ÕÈ<÷-@ÇKUŸÉñm–ñÓ¿VÄÆŠT”[>4³žæi±?zwÓJ…CYÀ³õB³„Ý!˜B9–À»VùLÐ8/ç²~TôDëJ»ÃÐ94Ø„{IKÄÕï±>V6,ínSƶµr œ'×¹Å7Í„ fpkÎ,X'RØ“)n7¦":Z_Ô¡{Ûì‚ûÓ܃…g–=Hét°Ð.9Ò,ndVðÔ¶ëKwÿy\Ú4„:WEY*·ß¬¢r: Bg Þ}hBòs»vz Íĉ®ÎŒs/ñê‡x0þè#Öcj/èòš[èzja g!xJ×_0uþ^ƒ­†–¾ú¯€ÃÕ¬×SBŸV#–lØDPëÕê1jÙˆøŒÎw7ÊõÔŽÅ’ã¶É‘­O‰×l¨¢áÄ5m9Ë"C¡·WÍŠO¨‰2©£ {áZä ÁW^TÝÜ)¯ï}°{üQtiè¹ÿ²¼™ø!ž8üR,ßv,›ƒ#þ819ÞŒöó–ˆ T•KڥܴJ—^ vß°ëÕ°d¯¥«ËòZFŒ„#Rü+i{Ì«1†“}‡n¾VéÅÂ^‘&¥^ÖErì²´‘ÚdnÛ yL™H1˜júÅR’¦h<ÏôdÚrDh‰qÃõ\àLJTÖ'û&¶%õËQ?Yœë·—«®1¡ÌãLbl{f¯PŒÑ·8„^ÌÖ ÒOë½FšúÞBôs;Ÿ!<€X15Äɨ£óA6aÔÙÄï½Puû)8n=YÃÂ䆦PûÓµ&µqí{F9b4ß•øFåîÞ$¦@—å-Kʦ°¬a ¸5`Xk5,$¼½WM32m¯V/‰kvÌ£ÖµÖW—ß1û‘spA@C7FË +q”u›d>gÔ¼/kLýc”ÓŠÕ쬴(‘TpØÅØ$t 7”ÏØ£ O¼ø&0¶}ª¡$}KT uY£æÁÆÜC–E‹šùøÃ¤yÂUŒ´ìçLYSéoõR #nwbÜQ¼¢"¦\J¡e€vÕ\JþïTMæ¦: 0ȇò•4Ÿ–jgýeJ<²ã×Õljêž0<^˨ΚýOµRÖ?ÞŽ÷¡Ú£"µŒº¶„Ñ)»7C£.cq÷qRN²ƒ¦ËoXºCÆéLØäz‘H׋ØlǼëï„Ë„ y(®å–Ï‚žT0h{ ñ,…#*uÎ W‰Mú h»ÈC¯àxYá]IìƒÓi`1§Á‡"#ÞìTí’íäO°ð-Õæä®—(£F€Q'‚ô©N¬BRù ~K„÷IÌ%¾h¥¢åÊÔD|®ãQ?Î ‘-ÀäPi*fúh¥´ñ­þâÏ ߆ŠTÖYÉïlż‚ãѨT/¤çJöÆàùG€ žlÓŽÊÂò{±¹½CÍñ€áõ²ÎZÏmL.þW‰b•Ö½ÉÔíövR³y® ½ÉŒðKUë×®èYʦ}㫾7óø²>Y“è RÜVOF‹¥G?R°öÈܱ5ÞÈ6îÚ¦ˆÂ¼¸ ÆpSèû•”Ó ßE}ˆx&ci¬ëÒ¥»¥?ÿHÏðü¦ªjÅ»Ësv òobIŧaÑdÐÙM|÷˃V‡Ðj6sR2ò‹º` ‰àãsjUå(áô?nç4$7õ­ÏjžÀåXS”@û×ÀWshåðè†D“º·[ú8wÄØ°ðu#;PgQÑkóå‘,jœV//›¥‘Xéß2Û}#È´ñ2wä_ÃëSÓ:dÆÙÙT?îÙoýÌ:—þpÆ1¦Mûf"{ò£^†ÅévoIëÙ­Ÿ¹ø~”Çi{*?Úø“Ùí°I›E¯‰(CiFIlvic§%ðÃËzo]B˜CUýc Q¶;ôŠ×$Ãÿìg¿6Pa ¹Ï5(jûäaºA—©æ¤¯ëIŠ•™¸$í_ l½#E1Áh8Cpk½Ñ|ÌL{7Uqš ¹zî®þ¢§ÏÙ£n–™Âcä)fåm­žéû(µâ—b«²‡7ûÙ̉†PpS-î^…wtg×£þé”49v‚šì•ö˜¼SñÖ¾œíwòY]¤„S>$ùä²$j»«ø]ð³_(?ªûLŒ×f%ÞY¾4ÑGZp’ÝëL—Åöü0õ¬z@ãT¸kÂñ0›°8¾+ ¶Ê©?|òhþD— Ú¬åÊ’ãñ«-05!ûœŠœª‹šæW–g€SzÎUtwlÔQöñ23m Àn2/‹xi‘ýÅD¼ôÓ+lt•‹Sý†àÏ·óv4ÕѤ}“- 4RßEôçú>¥Û¤[wHŒ'“ÌÞ ëüI™7Š€<•쇑½1—˜¥›Ê•ƒà*œ¤ZÎÑÿÜV9V O‘ê¦bY”„ßCN«µ1cwPoÃ-Ü“r9þ¥“Ç•  “‡ž «;@XÇdÏG‚#6>µYRÕ.±Þk\«ÆßCe´äÃÝ˦º4[MK*8êAD£ uÚ+ÞŠñ´ÐŽ”Šp¢ÇãèO×ý-pÅNZ *25ô_ d!­J#8GîWÝîµRSªÝµí—ßk,Áo,ø§HUoâlM¬(r¢sódQ”¢ «>‘ªx·„߀^tG57fˆ\GM~Æ–¡@âú–9ŸOú®ÌJ¿½<7›~x¸é¯Öb2Þ­á.::WNë#ª¿é…£«·6¼·×&ößìI\“°Çm’|)šåBÙ£Žµþûqòä—6§ÈhXµ¡Z€ƒ—ÎñÉM‡"!¡ÐˆØI˜$iºììGš˜¹ðJpËÅ.­Ã³|»>‹æ’ž¦[ícémã%H“÷R6âµ>qåùʾ3ç^Ï`c¨ƒG‰æ2d)¹1-FÞ$^Öuå û¥Ë2*2 î×Ê’:êj¿›ê‚eãÒB&¹) ÑtÜ¢ S.Ô+Õ¦öðoñeÐÔPë6} ª>–«Ø[ìù©å·˜’ ·šHb²£Gï0nR#R bµä-“–z%#ÿ™N©yaRÇ£Ð2Dœˆ€[.[ña§ÁÏ\ïÕC‡ë1ZE6itSúªÖ£OÈØA-@öøˆ’Ù³©?O°Sž!Õ…·?·Iˆ%]BA¹ÄáÇW‹D-ÍIÔQa•´TëY3lò R“¶óE2Ùl³Ôÿ5†è¼oëâPEÊzÂêio±æãžë'bƒ§cˆ_‡*·ŒÀkÑV9Ó%Z6ŠÌõÊp"r¿öó}zXæjƒð5:aJRÌÒ=Ö KkV£ßÌ"gKiÜÅèúý:íR‚+Óø]›ãŒ,g¯Ô÷oUí>Á~ñM …–Ùq6<òïh š÷sAÙ¦;—$ð1’ÌVÔž¶:+å3F@0Ê##”Ló 'VÅL`(ž¶!¡, “#{”ãiÕ놓Ãàbhý&±w\Ù šz±¯@¬¿ÐCûi°Ù$ðàc°ÄhY&Ê<øÊ5~tãžL#¯™x›ˆÊª`|¼‡&—v˜Ïœ4Ûê?|çà >p+-7?^È.AÁR囕Q.?Ió뱓‹\GÑi;ÍÇ&úîÛ&t[<‰“… s¥^þ¼Äù¸v-ÀSEÿ{JÃüÄì£óÙ Õ×ß{^(æÆu 3w~e'y­zëbEÊúïÓë“Jlìïù¾3L€ó¶wa] õ;å(2X(ù`Qï@ŸØºÚe™hž`H5´Êhªâsúðà}ü}³¥Ñ2;Þ‹:¨¸E77.M'éÒôî0 AÎÓ¼Wѧd™¤Pê¶™J’ÛÍOu¶‚·ì•²yL k‹!{ì'èBûRå¼&Ðåp¤,Rf™aÚyï_^þ-Øövrks"ÚµAžùºt%ëØ1­*³5!ü‰„•™¡'tÖLóÖƒåiõêÚù¹]A¸4Uí ãžÑ#»ŽM^Y5ß*}}ç:ò­¿ï¶–iÎ^ƒRÒÚ–ɲA»×Ï~ÝŸ_ ÞsÅ,$XmËr‘l7{bîæ:øtDÙ÷n¾Tõœu´&!µm>ã¹»J—çè9¼ä‘Ôj€²ð ~”ºš„4Úû˜©1ìÙ¸ †S…˜Rf y>`Âí}—(J4ÅÒÎ< ç¹_óM쀩™K£ßŒîé!š¶ ˜ÜJÓI^ h(„¥*&R5î³Dš.¹ç^{1¯J ìN©ÖÍœ–{èTW¬}ïÞ„ˆ^miSpªÆ„ ¹¤•ÆÈÁóËÕËXûU¨œ5Bô£*z·yd¾imî°ÏÛxúr\šcª¸zÌí,GÅíaÀÊrþì º:On\ ^†g%|Ê–«_ös`®ÉžIËîÏ¥Xܨò.š ªæ®NìBÁx¿t0Ñ p¨À›5Å&çCMb@r3À|"q‰]/bâ’{g@¡@³ÐX…ocŠÀ6 Ùi:?J[£7¡HsÐí7ª|a ’âE:›ž9Ž­hÖÛ0HI#<xåTÓ ÑN¡ÜÔÈ3Œ ›ý¸à2A׬Ɗ2ÃÀ“æ¥3µýÃq6ÿ†tÖ[ÕÚº“¨Û“yVì·ê ÞìëtrQZYDî®ø®É»mZä©}1t’&úMIY•—óx©'‰ŸÇtTx:³dó¬#Œ´yfíæ6àJÔ!”îƒ(=ÝòR:—!9ü)Ax \¶g;Ç/@*(s¦šöÎ]DÐîGQø²}ÜÇŧBía8D´†G´\èí×Ù’ÒüPƒPèZõ,™ f²¥G³¼ží'u[µªØëí[®.ØNS$s+G˜M– z³ÈñÒü@áÝG]ÁÔï›Ç o,2ÔN]’Ãú¼ÒOt–û›•ê;Ÿ†>àtY‰(bð /#õ蔃%;Ååk•5N Îu˜&Šà:pætìE†Á'©À3JùtUQ³m˜XT£Ÿ[Ôu™Æ8Q^š)VšÖ ˜"Ële;eÄüøÖ<¯ìUã3ቇ¥UÕþÍ^N¯‘Ú±Gž šB‹,¼å„æ÷C1­Ñ©NuÓú'¦a+?ʉý€£R,liN3¶RªäÔ9‡¥è9ûzW˜Ž‰‡.(.`Úîy}V_# °!ä*{Óçõë'Ë"§ùXS`?µk ^ûn§ðý9k ¡ÚiŠ Çg•X>¶¦1åT&]°‰8›ãþ¤‹½Î:ÓPð@Q©`ï@@ò ¬p]ðW Òï2a_ÅXŒ°Q8^×í!»ìÕ¥–¢^±Í’.ÕR? ±u¸çDti꾘¹¡tà@ØØ=ÎÔ»}•Îm$ö½ëþ·SDmb ²Á¦Ð¿ÆfLBŽö‘bÕºë%¦œåÛàÿ'K‹ËŽVF]c`ø6ãòëú0O.õ&[غ!3lvgÍÕ|Oi¨U^FôrÐù©4P!­;rÿþÙATÞ[x3Ž`3Í­C!Rch· ‡€`ü×WànYîp- éLŵ:œ¬ç ÍhƳt­(j±R‡õÜà‡ÊúuEØÊú|œÏ$Ъ-Q/ó,«@SDÂMI5…nÿ–}œ6w•’I-¹:55úµy6 DÓP½­b\Î<Æ`òºÇ±>Íjhñ\_úš–ìkðÃTRø÷ñ^ÞùuÊ¢¿û4‘Ƣߓ“Ê÷µ ìrYãØ¼Ì¡vÀ‹Œo†a)•&0÷°2 5J³1XsëƒÙ É “5*¸bʆœ§S7æÿå·Ody6JS ŠrQoƒQв֡¾’+Óâ¸Mõ¢õŒ´ðï›…¿8OM! Ïp*lx ×ÓœÂø-rG³~ìE–k†‰;À͸J(qpõ™êm&ˆdWSmšèÕ_!{ÄEÆ`ìD¨SE†„ÝPO}G~òÄp±¡ßï@YxO±§…¢ð¦kÈx~3”ãcµ§ '‹þtrA -+¤<7n'¾';þ‹vÇ“¿Â´ ºÍðkK…X*bzäß´‡±ö"C]9§Á›…n)…m;—h àÕÉ‚â{¦i ©Õtî§d"ÎòÇçéÞµ^£ÁPÕð?;)í´{:® vÁNs6È)„:ço†Í]R â1 ™zÖpšÂše œ>¶Ê¼¯0MÌ‘!/&}2ÎÀª(B­ùÁŽŽÍ΄ò>d[_5ø‹2 Hn%^ÉÈ1I·ÍßÏv?‡BÞ²_FÚõ^Y¸mnN$…Ò²ïNT`MéË4Qd!¸íkkZC™Fƒ¼äÛ WÿZða/›Ãý1÷´5•\ŽD¾uŠÌVr¾N;8̘Ÿ·õGãtçÕRRyõ‚©ð‘ê¶ø>o‹vˆÂ|Mt ªÆrG™¯ƒ|sÏmÀ#áIÜ(2{ŸÖ/ *(\áÌA9¹jËFF>WО+ ²F車c è6& r—ñhlŠýÌ¢V×E¥T{¥s¢„a( 8›..ê†þ*™}Éù’»tùX¡˜¾o­½5yÈÃoù²FÅç€å¾uç¿ÕÓyp”)ň­íÃX ÂùVÖ| {Àžÿ…º¨|ðî‡7n]—GÕ¡ÜJÑxëf¢jÕ§ñ]»açø<#œ!)åR½ÁjÔ9bß?ö®Ç£pI ´àÉsŸ÷ÚÑX=XXõzÊOß<'CqùF°^Ô´N ü¬ $9ç•Ü<ýMesmÉ©AþéB¼üpß8+øéèMdé$ìÌëïÄÐ ULs…ú‚àã_Fèºz¯Åâ3ô{“«Ùµ1þ¥Mù¸¢¨Eá—jYi¾Õ a nšfr[ÂIѦé¢4ó ¿JýàxÀ ò1¨òM žKkž!@u›X†lh›ZÃÙ˜ UÓŸ4ÌÄ6ð çs[Çæ%—PŠÄ¶ys(säÛ v^†Ó€rn”֎݉¬ì7F[•(>ì‹[ÌKÎLF¾Fù-Êñ9/KD—@ ùø ?ƒÊ á¼YñU¸š… ‚),¡j-þ*µH,Ò9ö9¬ñ¬Ì•,ð37žJX–ü€l1›D÷G6àÎKȦ Nw£´gàÇìYºì[Iº@¤‚rô'¿Fƒoøp ¹|:µ[†§gȘǔ¨Mâdu°Ñø£ý­Ú ¸F8Ëe¥š¨÷£Š"(}B©U>æEú[mü6jñ8¶G«Å˜ûƒc`ßí&OV²0è# ~Q[ð¢­É8-©r\¢ØœwWã÷ÝëNÕÀý2óg¬üvÒeºööùw>¿x[Ö#ÌÂ{ágvæäví¢~(çK94ˆ—'º@Ž„8…±A¶>ëØ7 ·nÔ¡ÈoŒÝÐho{zÄQú­WJŠÈ‚?²­C±][Öü| SˆÙ/:rj2J¬ ûÉä%á ’ z)MHÜ{ ~×¥¡&[ƒ‚€n]8¶Im{îiΪÖ(žwÐê ç­nzÍ[P¸" ðH*˜)Ê‚W!³uÆÌ&IÖ*ã'‡ÊõÓ^4Ðö›€5”¹kúåA¥š„¸””#k±ÌæÑÖ=Ï+ ÅG¨ÃQ ×Ŧµhqôû°…sDQ[ZÑr €_äºVqí"Юê;ƒú  ßmŽåÕME†AŒüòµŒ°‰íöJ£wNIáqPAáë5ŠÝZpUkÞ¯ÌÌÄ`§fÿz^€x Ø>;™Ù'G…ê¤*w¡\È$¸­ï%²þÝdbI½û.ˆúmlǸ² ú86¥†ìIÝqaÃãn8ZÔ„ÑëD\Y‡ Èÿãè­ù(`2<´ð¯÷”÷÷­+ê˜ä^׿“¤+|±,¥F[•*a$é½°NËW æoäÔSÎaœ‚Æ÷40Çò0^jö‡MÅU +µïfáÐ"«QÓÞ‡÷c ¬-eÇ#ºVd‹Róìź¸ë6{ýä¤5÷cCFx½}×<Ë¢®ºÉ¥kî*´”OZ½ÀJ4츮)Ý/{sˆ¥ëøˆ¿.ê!!ôÞÅ}¦.º¤¤Kß‘§Ðýãt޲ô ð"{ÃÊWðï£÷ ²UÖÄc÷©ïÞ,€^<9›¯Ã»‘à‚‚q &® ŸðÐÀnÊ|ÂÆ"›^qrÐyžK4Ó\2¬Zzj¨.fJ¡H{ŒžÊáqøÓ] Mî)åHt~øLÓÉ.Ù^mŠÿUI&ùgŽèE&‚{Á,Õ~¾‘Uh:LŒ}íh™jÁ|ü, °AÆ®$ʰ8±ÉPI÷^3„*p`;‰!H$ * k2XÃâ©äUÌÀ–Wׯ¤cì’Ά|À+é:÷¯ñ¹”5-=‘òe¤IJëg6Éš²ÎMVŒ,óı^-:AÛÍd@N(ïx·ž{X4ÔNñßdƒ`›É?´@Äq®ÈÝ Ñ4“úT;€Óèo°*Ü R3ƒ‚ïÿ¥ÂÁA–9|Ñ,/e¸õyŒ²+ä$Bi—nV¿yéÝôszuQ|mo3A×H0:}§…Ž×eØ›4bL¯ä^ZO~ÆÑ Ï}eSÊ6õªåüúéÜ?Ù^ÏáŒ&NÝõÉÃÁX°Ð'Q•ÅÁ3Ì¥ÙªÖPÊžËŧ¯ãX—V¾È¡äÙV€‚¸‡½h\¢‚KÍáh 9¼æÂ#EàºÅ ɜֺ†EÇŸ)ïåralô€½:àò-àGiœP»É\ÔJ©^Dçñ‹þ$ügt:Ó–FíV2ãô¨F^zív¦@$C®%mr;u-ä…¶ÑÊi‡—®Kxµ±=C˜Ì²ÙêÜGžæc+ÔQžpk6˜í¥ß£ËØÐ°Œ˜ó7É hºm õ2­-ÔÔ{³3ðuáºs/)“­¾ŽV¯©Å”$Såø]ñ3~r%Ü¡ù‘Tî,&?;Å…lÄCw åóÛÕtaöœÄjßK†BsŒ…°0bÃNæÑb(È{ïDéJ4¼Œ‚¾(´'Y·úâÃT*®‘mI#)[6‘Ýã®i>ùÈ À¶ÊtàlEàMä¹&Øë¯Àåçeêh`ÅTœcB)ó3`–°7yÊj«öÍÒÞj"Z,äW‘b9ŒM%r2ža8­@WMº‚’«E3Kº–ûØtÂpÖ7'f´îÇš‚D2õg Œšà'_) Ç/ù¸Â+t"‹“GÅÁ€_¦˜‹þmóñ ¼ Fרë´Í ÉœcJ‡o@$«¦©ßˆß3«âòpm¶ÁÆErù-Fc4]WéF(°l><BÑvIPŠ.UÏý jÀDéÝ(­òùŠ¿‘ÌÑuó:›ÀÊFPë{Ïò¶OÅÊÚ‚÷Yí_æHY ¢ ¢†m-ˆ´èN ʽ”‘ÓJÜÕ ÎYn†n†›Ï¦[4°}a ÎýVw ý~"º‚ Ó4«Œ–4Ën©ï ;^‘[ù½š÷}G?/Ò䬷…SG+Ìßg`ñ]´&ñà†<’ú§|GåÈñýB<è&Èdú¢»Øè$^{ÀäSùŠà˜A ©û`‚ŠÅ 3½Ù®5gb7äH€†êÐ,«ðž%òYØŠs©º©áÜ•[p4MijIŸ¨˜ðc”ˆ®< 6H–Å M§Çù@ÞÃ,%)r¹÷c½.ÃY¿—¦É±:šÙ“ÁŸºHѻӷ'ü7áì‡ÔI.”TkI¢§²i©ÿ6ñÄ_•{L§sO¯_ŒX|[RÁÖA'¤ãO_)”ÛZT¬ ìäg¤ßo~p4(ÚÈvá¯5S>š»\¾6Öñr‡òþÆÇCóQÔ\mz„°þ©æ)«7ú›jÓÖáá`/Ó^…-ÓÙñš.>0¶*Ûð!²ðÜ»P-n£~Ã^yRŒÎE—)2¥vZï×¶á¶öÅJ ñƒx6wa ͹NQc)!F…I¥ágÙùÚU §¬†S¢2G Ee‰Ô”êœ}ÅÒ CopÿÇ Àó8ªé #9rá øknçx½–]d^öÉþµå¹¥ßäÅoû\5;Rn´2rþJ9Ò[kÞ;cìã(…LÈ3•³öMòßÿÃ:VdìÒ–y§#Œª&6cdJW1ýÁø‘â ú.B!43À62—ø$_>²ƒU(sú[ˆ …Êåº]ð²VËyß\ÍSé×ñ‰ŠA½âPМ«¢ ¹üUeOcˆÝ؋ʕc¢wX^KÇþuí—VÙú1‘z»…þx]@èÏ=VTK4IØ€| Ëm±Ÿ1þ ÅP‹ÖäP…Ït¨¹Óߨ8. üÞhû†2´ÎÍ-S!åZ<ž:+dó’ §LS¨ƒÂ¡›è÷ö%}ÿK'jñÜã¤Êçã£N¶@›¢8’ÊÙî^ð¡%û\ÏÅQ½îŸY¹fÞ&Å r_òŽ~Ù¤VG)p¡ÖàûflR‹1qàëPÖF8cnq8@fæ•ó†Oœ2ßëVˆÑœÀõÏ‹Êáq¹Ž]í§“Åöä6U99ê‰v­L¼»ƒ$,BÊ%(¹;.Ðôb?Œ?ÐaÔqœŒø ê[a¿m$œ}¬àË›0Eí¹!ð×6{õ‘µhzʈ_ ã0rU!?Á{¥~ó/Z‡rðUZ#:×ö3²ØfDLí •–Ýob^áAÆEŠntŒß"ïHCëT;Èì™#tÝ 1[+èŽd•Q¥«’³Ðsqµ®z(xÉ~ÇÈNÚ ë.=hÎq¾ºìÒäoýC—žð6*;D¼SN%Ç© P´EYìæ¾9Ę¿÷ÎÛÊRj.džŠp¾Lûê1Œí±Ù½»‚¥»™¹R¹WšbhMbñ9_!g­µ6ÊNÔú%æZ}@©XBò/ºÈ}šu7ô3Ï~³­û$Àäe™Ž;1oÍá>éGu|MØ“5“ÒQx ©;gBĨÑ<ÒøJ¸¼ÌÚÛ‚x¶D•¡Ÿîý’wpÊ4ùˆc çà_¼;DT&Ãe”åŽpÉûŸ®N?û8oz¯ 'r;’‘ ~…Z˜ï,åo‚èUáüí´Ðß&óÅ"üØæäØ²ö”MÜF;ŸU±¬oéH¡ö)£+ÒAän}Ý›êÑdâAyBd§ÿƒ?JÏâú1'}ìºLà æŸâýKð;÷‚[;´vNhBgbÅëkvŠlŸ‘?Ftd5ÐEªK·Ô‹z¹šK çÿ·þ_uíÞZ%°zHÅl+gúnß’ ¹2’ÜèÈÛî !Ȳey¯Ê>'7":Íß Y&(@Àº¦kÉvFÓmV…x>ëP‚ï5¨Î}”T™#¯Æ_~¬àÂEd'¥àÞ}šë´g΃°sT­‡DwŽRgŠã?§L€îTžy‰}PŠÎô€.ŶÚÉcHÏíÀÛ6d÷*ýÜc¨b?/›¯œ”G’çÞ³Ðs¿(T¶S+ØQ|²™'6ú¤ó’ñZÝL1•æ°ßÏGÿaõµ›”}c"eÄþõ8\£”Fƒê„Äõ•5kSk®–j3¬?´ÁõmÜÑ mÛ+ÆSY]÷#¶¦ÃÉtt)MŒNsB ˜…C´ôa瑱 Õ?KMõˆDà ¯uÔá«ö¦ªà° gîˆG=€LÞëÇdKaânyFœD =¨Íî•V&øÌÃØ"µž¾Ì®„u.XC/FúÄ »Wú³‡}«9"‹©Ìt.àzé¯a·Š øát¡í¶êlpÅP¯ñ*·§2UŒ~Þ5¬^@ErôØ= ¤ÀK³£UvßÁíÉßÇÅÔ¿‘l¡Ás-°ÿ1à{A‡a PDŒœZ¿nædQñ£ºê@ÜQý§ß}¯G/N9Ó·ÓÎܿ䧸½§g§#§‘)\’•{u.}Û±·Üj¼ü` Nj Öݦvgð šgÃY sp'¬Y#$n‰üf½ÏiË?¯{{<ªL†êL¹$±r›ÇÔ=¾Uµ`v rÇ"jX£ãHÙü+½: ¶]vM~©\(ºLPÀ X¹·üLcã=ö1áàæÿž4>UĵZ¾’MW½"„óôé󼤡À´1bZº-µdã`-­ÒÅ uô,2‚¾£­#ÝÞ¥¥¤XTQÜOpäuhÅ&lËÇ„‰“ìݾ//NŒ8Øô‚BËh‹hl¼6Üë^Ÿ…á\¤"WHäü¶äÖ¹‘óéþ ÚHÓæ' †EÏMÆ4h¨¿ø‚8[DáˆF¾Z€'/£+¯•Õ£!² Gà™ù`‚…T3¨1åšS"D0Ty¹ªÈý5½Èç½¹¼PzîÜ6†ƒ «bi‚6,/D§0dzŸÍê1×Å9z\ )Æê ê c¡Ðò8¸uQ‡fš†‹cÛ·Ä:>˜ƒU7‘1Žd®è#jÄ])_3Ä[.*õ­!ËT BIkãð76wNªØ,ª<먂Õ:§§ -›¬<@Kœ¹îZ©yʆ1tXÒ‘UÅôÝUtôbââdÀV?ÕÓd%z,£šeº—~øðÜVLh—(+çkí•ñª2;ñ‘~›XT¬YgDgK #ðx[ˆ²Å/ûJÈóÞ¨-Û—›±O]P'P/I!©°ÄH5 {öÆ]—ð!!›ÿí- Ò' M IQ9ކŸƒò3v`· çz7‰3æð³bTH€D1-)0…Ðéz.v~.‡ÓáÔ§c»i_­¿í|' iJ¤èI0’î­U_?‘Ñ+1»@zü#úOK9¸òI&Çne¹°ŽçpÙµkdXE½j¬ãáŒÒwx@@Þ _/Ëûš›³4wºwÝá‰)gœÚ9àîDZ«¯û}K|Gäqw„Óò}è4›Õ÷ÆþãJ}°“JXrŠÊþ+3Á?aÒÇi7áü Lu‰B%ZºñQ{t× ÑÒ¥Fª&sË}Eí:òÞbP[O­?E¾]¼Ü—s…Ùæ>mvøÀUƒ ¨#¤¬Ôe ?òƼY·Bð è4ªY«‹î‹2_ñ,RK0!!ĤÁàc¸”Qbí5ô{ä¦v ò:5`á? þ³8†8‘ï¯ àï¯É¦f7kû:Ø@ ³¾šr楺æX8Ò`«5Aô½¶ïiÞ³Ñh’¿Æ¤8p~… R¢ëŽ)/¡:&]8ш@ÑÀ.C“ÈÓÌ’xð“6ˆŒ%†}ë߸ÖT9äP€Á_»¾±Qü#+Ö ìàÁ˜‡'—!Øœˆ2lêl¯3³ÃùÛ~ƹOj=ìTžBl$m?aŸÄÐK‘áyizb¼ïÏ ƒW³%“qE{]{'j?@&!þ .¬b žY)L ¶óíÏÌwçY-ïX%ûñˆ4îÀ‹'ùdèÝÕ¹`.ÜêQʯ!íYNJÃ>ÅKG[#r©øÏ…sÙ}²Cø‘KFIIý}×Ì8°U­Ó,ùù¸(ªB£/n$”Yœòz[e}£å<®ë¾C6•·£Ý€Eÿt¨ˆ}–$Z°á‡‹ÙÈ:Jvä7ßî‹9^ÞGÔK‰Ê¼Â؃¨S<ÿ·=¼Ë8´‹÷nTz—Ø)›F†–:`ƒÝû«™Þ²×D¢],’-4øðKÍˈ‘è2{ÿÅУÞâ ^º™`5eÖèêéþë0ïð× WÆ-‚è‡0@Uˆ‘vnÚÿÌ->º±Ç8_,¸Ït'mã*ÄWŠÜkZò ¿î‹ÅÛa–Yy?B% န÷­ÑĽ¨WùôRÚ†ŒÄb}´‹úDI5Ý„ó<Ô $u—$5m©¡Ï¢Ö`ï‹1Ž‘µ" wwÈÏ gó¬8Vf<1vÇ!WhŒáŒ“e0ÑäáÛ_ä6zG½Þ«!+S©;þâ´µ`aìݨ&²¤Ï=œSögÈDŒ— Ìi/ˆÍD Sqg2; zó|A8PŸO"YÏQç*üwô†hïçÊJqÙ¿Zø[*|šÖWÜAážÂJ çgéïw×ÚÓûs ã…`p­’µŽÛŒJÝ´×F$Îç~».kÊh_­ŠMC Lê‚ê鯤u°ÄPŽÞëÔZëi"š…Þ5̈÷™­¯áгy’§ø’í/a¬­¨#€È(-¿ÿŠ˜ qwKÀ¹©`ét aÁ™1ž¨ ÄÑœÕÝÈXG‡ÑßLà )90iöewqó¡S¥fŸ¿?-&Œp’욆ª¡7»ˆºG]SVãÙKëÏZÈr·Ü‹©p£Bm?´µ;\jþ'cƆ6iJÝ·ÅÇÁÒ¬ö:#‘ªß|TŒH”•'$ÅÆ/¦0p©ŠR’Ï<¹Q`tEö„Ø 1#ÃÀH쵕SfʳÍÝžûé9ma¯ÐÜ!Í9â:3ÖΜ£ÔbZC+d»†1ÅÀ×ÓÖßmë&}×Ç +ûºšlë´f ÐCb@/Z€0%¸ßS¹->ÿ9b· ôwêø2h×Þ,ØžÁ)ëÉIÊÀí»ÂæÔë¨4`‰T‘sí=ñ:;õ9tÉJh':ê/y7óˆbDeï¸n.¤Þ)JÝÊ&Å_Xh˜öj›®E ‰ÀŽ+ЂuÄ(àì³dÿüžS׊ ·ËðÖ·nLO5ؾtÁ¥XÃñë9ºÊ¾zÞt|:cžyƒžïAÑdÉ+ì·‡D?ÑRˆÊ?ÊáCñ7#µÿÁÓv$ˆe‚×’¿®Zk„ó‚‰MHf~‡¢FCà¸ÍÞ%oà¥ò㘆ÔÖØ69:®ïSÙ\<ýè›»àXïŽ(®ØÉ §œã"¬“èYß©\ë¿/¾”QÁ#«ÉJu%Dz%Cp¼s×ÖR!…Y•Os§®—48Žª/ú½x[>ÌÅçt‹×RpEßÖ8B”Ü L8äªÄyQ—r¯œKhê%Z´éˆáÓÐí!»}B»pý¤­Š©Îbˆ7­Øœ!¥!+Ô·ƒ±+եǀ‚`&sË/¦í¦˜wË=œœ5YbTÕý +¹÷vÆKþn !½jž¤«>}­…^÷¸â‡%N·Q]ÏAÚ“|ˆYÖ£Q‰.m´zÅXÝ9–mÕ¡PëödkÊ2 ‚p‚ÁK÷)Ø{d س¤ƒñg XØøðWàªìÓw‡•ÈÒ9â“äsŸ¶ûA—|N™ÊQù›27ÿ§å³åÃícÎÆ»õýŠM•Ân& ˆR²Ì¯*¼á¾O˜”W,l9íæ‰a»6±V¬…CéC²¸c! ¥=YJ7t„5èã/DZÉË«6¨{;Õ•´ÆxÏT¸·|œÞèÜHõ‰Ÿ%ž<Ä3_ ,xùB–Ýh¹´¤ÌÎB ¦ d:qlV¬Jã* 7DÆ÷æXaåù?x,7\å[ DòÎ6 Z°ŸOª†\‹h^/$Ó›WRð—ó ¹ë­f¢çËUúâ…'ÓKÜ?…eb,M¥Ù›Æ.+!%å ¡´S`ãÇßfòº†AsÕž%U7KÖQÌt ólbä¾Å…sCeRçp]K†Ó¾0w_ä¿ïDï7Õ’ôá+7¯]S/Ë?ñ­ÈT<î B+UkþíBí5·1 êIüÇêjlïMWÔw+O”²W½bŠº…c³Ô6sý– û¤x7Çl?TG_ìĈ“G¼ædA’@ˆ¼‹Y“܂׳ÜgÂl뫽aôʱzXÄc½,¶˜ÐûìD *Þ»bVáƒ;‚ ó ¶1O-ÂbXÛ¡G ˜O>¸zXÿ©– |Öý‰Þõ£…µ#Ç9:¼(ˆ!nf4WøAÆe¼®òÀÂù[Dúqm®D&áS@oZ[7g§ì¤ë`7Ñ¡íã`¦ý›­cûÔ|I¨ŠÑ öÕrá“vû4£|Þ½^=îôÞG*­ŒÛƒtg@ÿ'½;°uŧY€$õ*÷׊™1=7ÃÖÀnœfס@©¶‹kúÖ ·Ö7¬Âê?+óïã65œÙ}>d&Ó §ÄªOZi£W‰^Å$PKÖŸÐtR0ê*s½GE»þ0Å𔘱O©GôWG+FþìØs6eüˬiq¢(µ !©Ð‹õ'P“Ä °¬³ÐJßDäNîB}öXÝ%¶÷{ŽÈçà“ ‡ôºN*¹Ïë¶ôUÁyv‹ÍÆN*è– ·dbê38ýçऊ¸!Xð=Îzu)t$¾ªÀudëHeÏõ{’1‡Ó…¶EÔÀ0è0ò–£eÞ¨\tÆA—Ð`+¡ Øpõßåx\ç·îÎá:½‹B[ý`c†«ˆ-lôèÊsIk¢‘mE.1*ëTBùéE­EDâ­Ýì*¾²KÐr,žä9TÖ(î¿zãÙ5Ú7ØX!~}Q¸wáâu3¬H÷»Þ×qk¦.Êè6ÕˆY ú]µô_#„±4Nì™ÉzœfÊ[­"ÞfOAË¡YúÕ‡* €_,s…ŒVk¯ ?‘E–ëzåÎ^ˆä•ºbÞöÞ!jËSr·gäE7f¤¢˜Æ'¾¨ õƉW.K— åòvò0yÇ ——^­ìóÃÉ`æp?!´Ðsé‡ùÃ/&ŠA¾ŽkYýšÐÇðR4­V«¨4+œŒ€WÜÄô‚è1¯’‘å°œ¢` yB6ŠydÐîŸ'Tj‚õü@Gàb’H&4ˆ<ÎÂS5× q{´B0ËRð’%ÒoëcVz$´ÓFrŒù¸Jßs=þЯ<[V¡4#N8·MÓý¾%¹°°=4 Qh3Å/Þ²¹~&¹æ­Ù ˆ¸Ö‚Û‡ õŠfî>Z+UÚ¡»£¹ùŠ=T×' ]MÌnº‘9¬Vf¾çzë·ôÞ±&ò;ýÉ)õ€y,¼§Èo ±Á¤ëCf ^¥æ€‡µG·>XmÜ4¨°¾ÂgW¼D³qfyPËAS|èHü_|$r)mF”Düê“1šŠ×NÜY$7ú~ +½1¸Q IK&‰ãHOñü:[7ÏcÔÍ¿5€KÃ8GÕG^Nˆ*g¨•ª Ìáí+0¹j5_ðŽÉí\ô¢h4… ï²Âädöa&=ó“d³WÓåí¡ì§«J´~fŸ^m XåŸSeKá=: Í#BÁÔqç Šñ>õ(ÜÉ9ºT­×êhY΀Ö”¦·ý¡lP€ŸLšãgdà¯ÜšzÌÒ§YJšFå¢ÊåŽÙ##ÐcâVq•¬Q{¾êQì}#I¯×/G:.Ý“ÄÊ¶Ž¬òcgbû0Æ ™QƒJ÷0L/LWQŒAçŽ& Åîé jâÀÒ~¹KéÂB‰UèQÅB „JÑfÁøØÈ;åö²9êÁ Ôü"ß9ì¸xâ°Bžy=S«Wc‚Èß8 MÕÊït02b¨–±k¹]›U ¾=vÕUΘÝй•“-çaé5PÍivŠ’±µ—¿.fÉ;fË—íÖ´R ÎÀ ³#5gc!Gv[·Ý‰Š1 &oŽÐuÑÜYÑ|Úî“g2/+f¾ ùAÒ¿À€.â>êìjÊýb°ÁÉCZ&y°”»„ ²D·ÀFÿPÏܳûæ¶£!ñHxÒ‚¡Z4Gq=õÃ3Nr"Åpµ_o±U˜¿†Ë?.S 4¿†øiåìÕHÃlLómÞ«ÝýGaI¶ÂëB‚Ȓ¯]þ•ÇéPÊn®éÛ:_Ö5&Ó}ÌU0×­|褄íÒpcÐ; MÛvœèÀޏc/˜Hã{…ƒZÒ&å jÇxt¡e*Az >áÙ ž¼~·ñø d3ìÔÆ:`·¤6ó›Ê±Fêi®C»¨<+Oч¹ËÐò¶('ê;XàN—¤×žîò²å<Õ Q06œ‚Ê&CLô­BRÇ¿-_Ral 6ž€ÌH&†X= ÏäôßšÑÐ2(Wƒø ž¸›SBó#Ýcò¯U²NÜô÷jZv±¿¸ ‘º¶æf*’•+ÍrrNçèbPÝÖ~ XuͶïá@‚JQÿް,:`ï¿«Y*üH>—†‰B³£±cèÁâËf¾ØÁêí•Xr¹ ~±‹·n`J› …æ&î³ ë«@§Ú‚÷°œJ “ÞzZÑi6~‡Af$QÐÅÓéÈ#o6jæö¨Mêa‚õ×®…tªJl.'°õ‚Š”W"–f`ÙYL;ùÿì ÀëŸ=ßHØ¢2ã¯æÑÏA %̇¿íÅѵ*ãKTûYO¸GD;ó‰íRUN)´<Ý´åÓ8ÚgkŸæäfÛÆÈ¸`$­rÛŠtA©DÓaQÿÔ[KçÛ¤éþh°IÒˆgÁìbxcYCg|šÉIªìx;a©åßù¿ý‘ ÿ\ÓEÑK?±‰<°ÃZÅ{þ!°×û£|™ñ,jOñøˆ€*(ì9Ê")ô*j{k¥XeãÅÏøa­]•$Aõ|Àfx¦Fû3P0v„ø.˜L“uR tRo…±™®««÷…ÔïDz1æ½-³‡&èT»*O¾–ubN3Ò/†¨„– ¶=2X–!‰Pn´­ “]¼-ä¹nÄZÒR±¤=ƒ[Ž3C$X(Wu$›—½Ÿa^­òóÁý”2…yýYŒJs¿WÏÍpèÀÓÕ‹+t) ÆÀ¯$2ÈvÎ ¬åîº~­9Оfmæê«ÎÅ ¢çö!D;¨hב’ÅVm‡ {:†.º„Q}.PgBS4£=·+ހʎ_ÃΨÂrÀ±ÿ&ßœifKÄ0Ö Ð+ëO¸Mö&6›ZÎÀsìÞ/-¢Zu.Ú™«P¿„#Q«¿V½ž3§¨K~^Ô[§øOgÃe2Þ Ü ˜™ucጠñB³yXâ”Ið ¾U¯‡Xçj®5pë×ýf›Hé‹3ûâê  Ó=žLóuê…*æý¢é ¹ K@Œ«65³@xmë™9 ­—2xÃHÕ^\oÄ9íÆÈgbòÃ/éLôÏÁ'rgmj–jêÓN¿üç ;Š4M1×TÕ{ë‹xq¨¿Çœ³çÆ÷#ÅR$5 PSF³Â„,ºîaxGýyFÐ8KŽ•P –Ê}¾ÃÅå½@9»yœüN®FùsUWûžÓ¶§kÝs¾J^T0œÊŸpÀ—ý¥B¶™ˆ]X`õfç±»Žfò€œTËË‘sž!z“*ÁóÚhðO¡€ùޤQ EP déjú $åž ü¿mSž\8I@¨ÉÓì¿ wDŒBdW Ò±Dœ;ÃR»/BjD¤LjÀ]ØšGŸ$ ” ²'¯vÃÌ…m¦‰mšÖ§×æ•I|ŽG™[Ýô²—ß¹á§ð®ðÂ+Oò´%ã‹×s*;¦,t'uT‰‘F«,ò¶Vnºû4摃ʾ×òIìÊk¢¦ô¥ˆHL¾!:2ú…%ߡçóBp“?(Xâ¥èÍ ð®©—šø£ã¡#»HÚ9ÈqYkPìOÕN>Â01gÓR÷×"žTíèv˜Î9Ô?»ÐòS0Æt0µßæ<è«nZ…u{Ÿ6K¿Ø "&ŠS,}‹«É´þ‹ýѲ üb€ýÂÕÁÛNN˘¬§2Ï6¥Ž€SÛ ˆï´}ãÒT‰MÞ¤ °^åõÿO1ôÅݲkTïnÓÜüT¸»0ßñ xÄ€&Ad°œ ÒÛp#*ÿÝv“‹Ú¥î8óqY@ßk& n—ëøòPbj¢©ùêh\•ÉzHãÒGãL!Ö*ˆàœOh±ðÍ$M~‚tðŸƒ"Îô·zvÿÜÞ—g´ÀþD‘ÂÿÑqM¶{È¥*¥¶"@ ›òeHUFxý pxzLëHîºåLhìÅgž-O÷uß1­a‚ ~*Ì0 iÁŧZWUfæ’¬5ó,T àÓ/VïÏk…:°<¾ìý¤0¢%~Ê€À˜äFÃg³¯Ìcu:WÓõ"üM3{36­qæ6.°°!:zÊE?¶4e§¸Ä•—8”„p]´8¿×5ã,M”_¬Ñ¥l¸Ìv£G®: Ì{,ÿ½«DæÚ.æÚ/í7¹|èuÑó±¸zyˆÇÅÊÛq8„wÂyGS#N;fÏ­ )þ_ˆÚÞû­­Ó½õü«S´á2ÆÙF_2€IrŽî¯·¢™I¶1?Ú¡ ÀÒ4ßòʸŒ£Ê¢xÉ ª&«c²|Åe¼ X¡è‰7ÁH"¾eq[órfST­ } –# ñp$»“¾wjkÚŇ dY¶k1ó”xÃÒ d4³RŒî¡+’s—cÁl" gç'Þõ1]Nõ„ŠŽRòl£a`vÎ÷xN—SbØ3§£UÉt¯vu¶ X*Òu—9üÐ1ÍÊØ)¤ Ë?†Á4ìBÂÿûí˜{Ì Ø§ ô™3¹Á¤PhõÏ…ÙÉ‘L4 éI§±ýPµ£>²òþ)ÌÝŒÃ(ÿâõ¥¬ λMáËz'€±ûÖ»˜½Uܲ´¨€ø}=æú¯7ûÍå!½¬ã]È0è½ÚËÓ>®‘yâÀÖRß™zû¡t&rÊjöŒÕU5¤Ðçÿ*D]´†gGΙ¦äè…øxx^ GòFÜ0vFrsLríóg‘T§Ã„,².±ÙÛèm!Ψ—Á.ýøD_¿ÐAtê°]Ç—q’¢G[˜ðRûq²ˆ‚[¸>tÙì캻'Ùö5bŽ—û|v™½3ß±í¹Q䆇|p2[ dò‹¸—\ YZx1lY<íÕs³>ÑEŒ!=‡ÊÂÉ›‰h¶ËŠÇè÷¸€ ¾ÜÐ<å5{ðÊšKk}¦R£'ê'pàMe—ì«÷êž.ãö_Ó²‚õž]NƒlJ¸^-ùCU=Dò«-åGñRÂÖç2Dx.pao) ù„m'ÐeösŠKÎ3â=çôdàºgC¸‡Ñ ä’öÁHÆ¿ËÐ/¶0Aaøiã ~惴WǹÀÛt"ÀSyzÊÙWÆScˆÝÀ_¨i×÷ RåžµŠ—¿ ¦æÌ²¢’ú÷Ö.wüdb£²GóO­ôe9“5MÒQWQª„ÛåØPa <2K3=&þ*ï<Íãª6I—,ãÆ˜0Øæ½P ®Ä¡b-xNÌ÷®"0Pkåò>ˆü¼ið‡©’ßÝQ4aö Me« -jeà)R'(2Ù`X%/‰s¿¥i-JÆAŒÕ¡ÉÏËß×Ùh&ƒ‹°lù7–Y«5‹ÖUå'È £oû¹ÊªÄ´"ÞõúN‡*,“Ü-xÛû  …¯ÓÒ;kòMÔàX;á0t…µg‹FZ—è|æ(JBÑXÀ¶Vjk2¦ˆ¬Ýì¶‘ÇÒÅ~æ@³y¬}¿òC4¦é/šè\£¤Qâ~õºüÔäŸSŸMÈ;"GÞz©Bu»¦Á½'Ù½­˜Á‡ò-ÐÍ„|Ò‘ty{®'C¥Ã˜AÌ«C’Fi%ÇI8û‡L§²­Ÿºù‰ªUÿØÁCœwë!ïgˆFž¨ ž”5ôÙcOY–bPÓ µÆ.c&æŽ_@غÈܵ9Àý>Â;Xгé "¸çA ÷cÖ•5%[ÑÛÍžO,xBçLš?á;5ø’]wþ4žc”ÛåV ¼rɆFÍA'/@ñÄÿb]õ × ;æ”7Šñe ár’1Î9W¦ÒÊKj‹VPzHÜ29´ï¾Bˆ#ãTY;±c§N:ì=ºÚ-¯£`×\T4º€U³7+úóF]ŸÁá&öÂ^ùf+¶XMcRý@¡I_@Άe<ï_´5„“gƒÚF¸sá½ó'œÖõLv ÿ¶ºf…:xx7x÷Êð@)c”Xm:ì‰åÚK4gJÿä7²Ÿ„‰ ŠÖ­à£ò)ÑïW‹N™1oÅ)?w™©ÑuñÄgËù;™ŒÊ%›/Ûñ”QÆ„r¢ ÙЛä?ˆž©ÝñzÕ€ŠE‹b2n˜­µk'æÀ>FÅ2[G×r&¡gxKÖn0Táp^N{Ž÷>îžxÅ“–|Q¶EH⣵¦ÐâÏÍ4Ðhhò˜K0 ¸k,f9ýà!œ÷¢#3m ä|×E,™ ßú#j8 %r9U©ŸC‚Ú7hVŠÜj¬)æ;á¢õhí3%x¹î²meãxçoJ‘–›•,_kÂôyYä‹zú‹TB埆WlÉ%µŒ Iÿ‰`Y*•W•JBðïÐ_­Æ5fD/{½oÏÍY$Yeâè¶ÄáÞˆf×Ô×'… kÀ43tSÆÏŒu]Zb‚¢„V¬°â‚g©06-øà.­GƒMõŠÝ߀ŽÈ<H7°Õ’„ÂŽƒO´UTø_S:üÜ&öt»#ܦáä{Cå‘“ip}‚Æá“¶°ç¹³U”¨aÝm–®LFvêç†a’Œ݈#‰ªOá‚ÿ2ß´€0žðóéA«jÊãb+<ÌÙÀà© Nƒ®ì\½þ)“e é3§4C,÷­Êw„–fh?§}ÍÇ`j1®úÕçoða5}"TZ¨8s?¸[i }®©H½x­HHÌ“¦|q1nûôøh,rY&>D2 B@`‚nFʧl±›¸rí† 7îÌžkj¿¦‡œöÀ¯VèýøzhJf¹| ¤Æ‡œ¶š&ø¹OUÒº`þL®Š¡×B§Üÿ¾5!QØ‹»³ØóHq"›&)<Ø-©á, â8E.VÓ NNBv×ËŸ^Ÿ? Äe}¡k¾â£§µÿ™!H|ýVd qåŽuD…ÝüçÇš€à1V”öÅø]^R{Ì °Á×SõÍýèÐ"ŒDø€ ·ÅJ,tdü(öZ”ïê”ÚÛÒöhéÜj,%jÆÖF®0†KW Çί…¸L„pH bd¤ Æø£¿Uâ±fŠŠ\§µ‹È”æôáit/–`ª˜iÛï˜%XÔŠ4ƒú&®ÆÐбK´›#H­ÊšÃW¹ ?Îþ#Œ÷ÔòÀ÷Ç=ÍÂ=–g\ÖJ¿M©`_ 𨙯-÷i7«Õ9-V¨ ØCQ ƒ²#”ŒÐc¥Ì^ùêHì?Ó-¹ÊÃÿÏ/Ù2ÜìîeôºÕ£žs½>C*®¦)ÖüüS0²MydXö¯Nò¼º0Ì8 žé-âìœfn “´^$/áPFO]0nûÕ”%>%Þˆ¢¼¾FÀãÀ0ç2¶ÀC³>d€m¶½ÇË ‚aüÃðñåÿ(qr 0Ex.½u‡€¡|Æ<¨zŸ‰ ì“v)s%2¯ûdícrôhæ¡âö´Âf×1Ú/#€¼³ÆXIV?–éoöŒ%0vÞ¤¥ÜËÞ„Ø'§ñò6T·«;;úNÀê»nÅáŒK5¬ë¨¿¯#²óì0Ø/E<F½Ì;L­1>oóå²-mŸxÐ.zW»r»A.¡Êàu\<ÄiŽGÏûŠÊ~l?š;˜HÁ “Çlÿ«ÂH®<|çä‚ã¿è­÷ L¢Ïþ è+â\iGÎÐ R¾¬%µªƒ¶«öh„gá#ô1[2ü©ý2d°f:"")hòÑ&_ŸQÂWsC|ŽMvÔgÔ(¶¡úò{l€ÖÑÈ ®%Ìîz°,ss0Ïo÷hwò@ I‚j[u(ž°àd(6˜[íywï,°ûɈZ8÷AP:óO^f«_„ϦD–ŽÒ>HN[v€™u{)ŸKˆÅ¾JÙ˜n«O¡3¯­O§–¹†tÑÖtñÙ2¥˜qqÍ9m  L³Ýôäëg}Õ¡o2Rz½Ñ郆×hB:?0am]JrtïT,ȳâ}&[‡­”éŠ7°y;9a¯¤¬q¾.Èw.“âP:@'ö¿A{ý›[‡u¾tÚOÄLN¢OH>R‘”‰hmŽU qg(J‰Åì`ÆA6Ï•tzºÛ°‡½À‡äÛ3›~õt’~1C1`ÿÚiûƒ#ƒl¥R{Àb WŽ¢Y°¯Ô@6Tæ®F³hËh1“ºrK*•)Dçk×&Ûï W[«$ó‘Áû?Þ”Ì0ÇPíŤ=ÌÀœ§Ø¢ƒzi;<Ù„ç…sZ II<)A¦¢“Ä¿o÷™> ]IhgÛdÚH$s:ùÞ“Áú‚y÷ !{óÖáq#‡¼ýÑeç])ç YÔ&÷Vgè:Ÿ‡m,;ßßX0ÄáŒK”»1;Üðz¥¸á¹MñsÝjú;ÕÁùiãÕQ Ëîà •€{WÓc!pÉ{Í»1´"µ¹Þ@ÙK–nvé›õ0q§ûY'î5è î}úÖ Mo‚“JL«Æß‰FŒý^¿BÂúþq¿Fh‚cìƒÚùH¬!Gâð?•t¾íé )* þSr«û¸ô®÷ԱŖ_µ'ÌsMÒ=ÁÏv\{ªŠ·ÄeÄPƒ©°ä¿§—{²ë‹3-Öæ{Ö†×Mwßjx f‚v3É„þ~û…ÁÜñÐÐ;[ùØMPÏׂZfÝ<Õ{™á¡_”N¼hé -b€²ƒËO©hÎn¿À«ÝéÏ0·ú!oÄ]?%\‰“$Æ5AŽš9ë•e8ür^Ø$Ôú>Lëì%ûÎü>ˆÜå(QŽ„âˆhª 0uwø^c¥ïO½n5<í˜Taèýewu+ |d5 ø'³êìó‹"5ûR'S,ý zõQ¶GصÞlÊ2©¢¿$s;Ç­67'ÅÁû"_C—´:6ÇûmÀ³?>r2U$ÏÇ8 ‰0³ù =6MêXÔ‡Ÿ´cp€cŠ:-ØW~G¥qm æ? ´—ÃoâEº½Á7^hÇv¨Œá«3ÉÅÂÀª<…¶Ó·«x=ŽØ6†ÀŠãÊØÑuæ”ìåÝT-<ñ+¤ò(z™l×§™(*ªM‚%SW‚K<`7/íUó×ZÿeP~ 6"Â8èñd4¼bGìaF-$áoÔç| *` pú„š% L¿D9ÿëÄhŒšÄ›õ2MZò¢WŒß˜†Lý1©4Ć›“o¾v^ÐSÁ0—à"¥â©ñlfjá¨]-Õk<@gÕ^ -âÝ:Yìi©æ÷¶Ï« E lÇ"qœúñ**€ð†‘v/F$á©q.úTuÊ€ ×oqRRt&„F.i ¥¾qpÓ²ÊÔæ«Óx¼¹nÂËL$ òñƒà¨AêBo ÈÚªÃV0/\ã6Ÿp¸æbôé/eh¨™ñÕ¨!‘nì…þ€­’V ÅéÚ“ì–-ùèê›HüÌ  w(ÃóQn®¬“5Þ~“S3´ÒOίþ¨™Ü£);|ôKzØê¡pØ‘‰f^kØcê„À¿£ÙÉS¸iˆê¯æ—ù¥óM“p¥¹—G:¤÷aÊå&ú‘8î”iÇ ååÏ󊾛‡Ft‹ :­¼Í°½…†¾ §®2 ;OY»H®E¿ ±µú…Þ¬^M¦ÐÙL8®e2mÄ«W{}¬È«˜” 'ƒžäT&gÕ¤‘ Ø~Ì/#±Ž¼¡–()—òã2àOe.†›ýãòKQÑÆR žŽ`Ž]­®ÈÌ ×'?Ö…¬ê!,’B‰Å!ZÄ3JÝg".脶ǧtó'ƒ´Þh.ñ³:!¼k’¢y “Öߴ¹sä^buˆcê=‰óˆHnãõŽ FH"¹ÜŠüK —Ú”É4WÐú@ò,X„íŒÖ§1"pŠ|I•ëü»ŠøÂj54ŸÜì›§RCT§W‘´š,vòy:b‹ ÍÜF=x úV]7ð¥ÈEË[]ÛS«ækJ.êÈ=‘žå‘#×É—Æç‘ØÄ›d°`¬.QH’®UµÄê“<¢ìvçHÛ›LÊ1 ÐùùR–Gb6„xþß^ôþ74M5t¶ mXƒ¬Újæ/™@ÊxNªò3Zq nyžÄN²Pý¼”ÌÿQKœ % qnîr‘7-ów6P—±#»/J¥˜ %gän1ÏDGÊ(µUivÒCOl7ò³O»î‡ýÌ·&tbìae †OŠÅç06O'Øó½ 0GÀvn½¡¼%‰úxp‰ÃÐü'€ °Ëè:D˜½žwæÉE©ß†C®Ã}k=S¾»„ Amíh!Ž’ŸS>tÊÿ©3¢¥ 4z‡à7XrªmHo,b~"„¦i^d0œðçæóN>†¿ûøGm¶}án„U“Ø…8¯¬à1>Þ«æIºx1î#`H¶˜ƒSün;s‰×¥|y/h¼ÃMÜÈ@±LÃì½  @ß^—YGïDÇ&`[›Ñd¹‘·µJ¬š…p¡ª{Ü’¸\‰n¹âh¾Û#Ø{É\"k«?X`&ˆnHuŠ3N—aksgH€¤½Ljë/ôrnöf"ßMÚ•«]TJv~åU‰OÍI~Êýsû½ïóÊñ¢sr§zKj!ó?QÚ玶Sêóö.»\ÀØÓš—Åàù#€ì0 gK]¥ø„%n"ÒwK¢¨ÑW·QDòO·ôÔx¿g7½<%¨1"PÕ(ñóFí0y tçW8K[}‡Óü;åÊᆠ|JpÖh Ð ¾È¥Ñê‹©2zú#–VÑÕÖ £ëyÛÒ³ äXz÷‘¦Çß„B=‰°ƒaP’ûÞ¯ONù•Rã/ú™Ã{@Â_±¨‡õrAôŠ~ˆ#¼Ú½ô«Î¢o:çEÄ;͹ðÎUm«™q¾<${Ì®HºÂ¨èËꆘ˜ Rž bJ”:j‡:̃im‹[%«²ùÁÆ]%§’U‡Ú˜Ç0K§T›]Ïô¥*3x#£UÖo²‡p忉ZsŒèÝÓû4•ß°™|§û˜Û¨ô#éû¤?n=b£¯8òíö®bÎÒfùKèöÇ;” /H¦AA-ÁFÄcv2¦šrÞGÓwÙØSîŸÜª½·Âñ‹`Æd T >ݬLÉÿ z'AuN%ÕÌAKM d‡/Ç'’ð ó’@BÒ©?7„tNYtœÅ8ZZðÒ8=Ë´åyQ¬!$C-gËöï* Á,hª¿¼3)O°ÈaV¥—o øÔÎ"r.U§\j‰AAËcU*rÓ|Àý›ðôÝì-' `` ,ï·öÆ(2P›9Üžò~ÀÞìD½ÉZ'-5C37Y¨)7ŠF5ØV¡‰ø:ëDœf®uGœ?¼¨]à7ÙÉ =`ð$žÇ.˜£( 9ÌÖéö4÷E“‘Þü>£0šé/l…ëA+Ö®˜6NáN,Õê×;½³LeÆvÃÐsâ±™áöð{N¢ÔY„™ì²ðdoŸ†wd“wJ¦­z˜¼Äýà–îÅïOÕëÓ‡óbÀ£N[‹T]×&‘,² °hüú_ÏTsˆŒf.òšä°p!G(Pz¯¼Îg(}Óµ¡ÎK#χG¢aÏØuçxÄU'‡XV‘À²9 !NÕ#÷b#tÐ ¯àD‚9¡+_:€ë²Ê‘1#•Ûª?Éq]ªûRqÍ•/ZÆYMü³Ê K±×4¹Ÿw•M!O9˜Y#DÅwiFN2 Jë8Uþ¼QÓ¨êÛþÔ‡—;6pœøLðåéÊkòk¤ÆÒ­›þÒ© Åg8«X±Wð´â°Öõ‹½¦èF~ oH Ö¨°WM½qÜ÷àt´N„ñÓ‡D§äþUÌ‘xÌšÈÕÝý¸æ¯A;9÷~Þ~Çaóvo^åâKtÌ#ÇŸ­¥Öb4% ©Yñá¿ñ¼Ž2J–^{‰ÀÅ«SΑÁ]@v€ïMûÁ£–  ‰]ÒŒ½ÆŸ¡¢j&e˜·ÍâÍJE8sˆžLfNÄ0éyÀ"/­ú3¼Æë·x=ˆmÞüñÜiw>Lüs˜0lK2«Ï;´GÂLëw5ür ‘½ Jé4J¡ì{/™£!ö»IÀÃ##êh>xÌwš3yBUº%k©ªó™gb1Î(Ö/댥¹I¦ÝÉ܈tw–³]°—ý%%OT*à+¥Þ§ …šú )ª8hu±qÛÂOâßÙ\áh Ðw'ÃåÜ÷eMye}û2 ŸÎš]Ÿiá;÷ÏP#9WZ˜·býåi\B~Vºšb@)üh¼~‹lz«) ÒÂ(ôüS›!ŒÛ…z„7 NOºÿ·édz rüÙu¥ŸY \nÊlhêçRžùì|Øú–¶b ï2êôoÎÌ @sô„‚ ›|ÄSE,´âƒ­¾P=ŠÃY̾©?g.Ï LãÏuÐKôA_X©Ãë,ãð:Î{¤1lÿ:,y¸">ê?…y(½ vjE éüIpmGAvi\5&ÐÛ_SÓWx-¯@Ñ#Âñ’qJ§5Æ ,@¦êD,†ŽË=i¢b4ÿÉFÁ“ý Ï僯(Òpy¸tÙË&W‡Ä¬§uGˆ=’,T˜ª'‘¡$_P!ëÈ…ÁfÐÅF¾4èÎOز&ˆÛ\l«n…D6e´¥ë ‰_ üPÏnƒ‹Emä¢Á_M¦žÄþǰ`¥ÐØíè˜ìª‘£¬§g‚>#• MF <vuSwE¾ªG½Öe%igT€º¿\$L__°‹«óã®l™r([(#8é–ð›ZX„RF‰ Ø,ÂÒxõ„ùëÝ×…Sæy3Y\|ÆÄp æ^Eð:R#§z[±Aˆf‘š†·‡ý!MT"ºË&Mt·þu8¯©±ro·£¢¢Å¿¬ÂuÝã#™ÑûBŽ˜ÉoöZ×Ä#*huÌ9k5ðdÄk;~Ž­åźP…ûð½Ób°|ŸñÓÕx±¦Ä:7;Q”9”_ià=E£!®ÌÄyñÏ"³¢0NI5W·¯¶‰cÑæ ÂÖâ(Oêþç„&h¾‰Ê§8MÆöŸö>©f…Ùr2êr÷¬Ë¯ñôþ=ο»-ÐEQc¥XpÝÿÊYúÔy;¬}òàá#¦Õ–æõý~š£7ÒÇ·Ô&þ@wÉ^ U^0QÊgŸØZ€ã ó$› œõ䔃Ø!è}ãÄ$rh f<º³Ž@!°P©V*Œ‰âOˆú•s;\Ü´˜ç(Œ t‚?¬7SŸŒ²£Aî×ìuFœB™ÏŸujëTM0Ž'dНV{pJ¿ùu~_áœsF®“¿íùCÚŸËÀ ßXZ»Ér¯|·É÷¥†˜¬»9L»Qwà3ê©/Ç\à+P§ä–¥¸µFŒwX9o]nÑu·/Ë wGŸMÁánƒ' Ó\¶dïãÀIô-¾Ê|Œ[gÔ³jíé®Å«Åx¾ˆiæj»È¿Îá>szžî§9èâÙ¾æ¹Ä¢¨Ó:\²&ü5×ý*öŸ”zó±­>%èÎÌL+!hd=9F°TÉŒŒ? '9 f“Ci;ì$ޤª™Ú‰‹ ð=)Ζ´³q_ÜšW‡u ¦ã!Nxú<«Ê ëLflFc àùªXº™ŠÑóg°:î QZ0°”çª>„d3¯ÂÊÜ[ο"ÈeÆt rÀ¨¼ÖswåvwoF¿É匷1û‹´qy½‡(‚uÝ<ÑÿZgâZBÞoÃŽa\Ö#;û»Îõb S;'êŸÝç „È€ã±ÍYŸpg®*Q&‘Œù'•IºÂ$Õ+OO- v¨MèñÕ¦9ü5ˆ+`¨Úþ¾ƒTJb‡þÐ5‘•æs–·ÚèçÄ-¾q;š/€â0¯{biJ&1‹–¹­*«>ôv.MV5m—oøW‡Vhu®çIKK7z"ìô·ÞåŽ/ÃP< þC’JnÐ)Iè°¿aœ’ËMI˜ ß-Z¥ 6æ¶ð›£ÊÅW©^?M¡|t@k†WA6ªNK*«{Š™'qÈ›«ï¤myˆŽŠ»y‡‹¯mF»¼AlÙW¢eS"ï/Ó½Ì\½••[¾k1R•ë`^ßôÖH˜È–Ò(]9Üä}ü }>¨nÔÃøAHÕºü,0…^ÄÌü}9¶?ÃLúïÂ:³üË×μK•8å èælÖ,dú¼Dè|j3•7稻RBˆƒüK„ð¯ÃvÂÿøK¦ÛÒC*áö :gøÄ?:5î¹çTÚ{kžÎŽ Œ)Îó8dö†ç_ÒÁê]"0u×¹»(儸šÃËrS•¹Ùný¯€îª KþåoÃÀOüd)pñ•\z†U~ã|!…®tÎ4müŸ™9/‰0Õp2n¥¸©ÅÅ&_RÉ»\³“ª —Ò¸u^68âEψlK*áFÊG@WÊ–,5ûèÓú£Â—ðøCˆ)ï>ÚQ˜9<˜:\:ŸÜ(wÅÜêÙS•Ú ÕçxÍÔ‹‚Û.Øqã‹· ð#Úûoä¾Mä€ ­üo$–¬®=¸‚ØuùߟTÑ/JÊDW¬µŽœîÅ þîQãcëÔ>¡ŒÍ9÷µ«%R8ÄÖZ^üÑt+—4mÚ„Ætƒ¦ —Y2 T#º_(h ~mÖݽ‘jûA°R‘FëpÄ—‡)÷O‡™°4ÓTïÅñ3Ö=€­ï5±ü4 +~u¿1B[ûáßhf6^ÍLÎ#:vÂn$ò’]œbÖšömG—ôŠ…#.ÛcÝþÂÙž$}Ãö³jW‡.4!ff9NНøµenæ QµZhŽØAVÈtm$|é#·>bb‡’øƒ8jº›Ä (U¤Q;ó](qº€6À›ìZÐæ¹úÂÒäè™õ— ¨ßÖÛø—E·Ðàëëõ‚ú¢G&™éÙ ÁÑÕ@•W(<‘zÖlÏæÛÙL1S3M°ÝGgŠqð.ã¥Clã…÷9±¶%¼Wé|¦<€ÖˆŽÈÍ|ãH|*“Jw} 9Üý= [ØáEâ{½˜ýq´ ›Ü$óê›+Ñç]R££?À‡îk2Þά½ôiÐàl¥{É«"à{9]Å䈊nÔùMö*M§®áÁ/”l•I~/çH±j%…â Ò—-€KøKBzwUy£O•£2ø~béZê7óÓžøÃtg™Nþð ™éW­æZ´×œ×XájÃC(oÊÔ’ KÒ‹pŸb¸‡=†Î`‹ôÊ~ƒx"wÛýrÞ3£,~[nŠ1EÅ&dmØ‚™¹ÀúáÇäýGË"žßéå/ð8(:î›6©ìøÄx1ÏŒ5æ~ RÔ˜tŒ:òÔJ›$$ðÈin×šËØœ[–Kí—ãòhZ'ÒO^¢z.‡l›]÷ÆFz¼ÔÎXXÜ@÷3úʺÎÖRåYû«S³™ïî_0 Öµ4˜ €Ö(î….8>_Ìø™¡[ŸÌ ’”ô‘l°·†Ô»P¿Ni–þq(o§÷š|H†,Öu|È4ú… 2,†Aeûeé¡\¹Ä¼7ÙŸ»ãþ}Šá¤™ö '­éo™«×ì"þðÊš/‚Mä x¤ ü9gè{~¶í…HãV¹ æÇâOì-Ó«¤ÇA€1˜!ˆT~èuæ#¥,0ª "l–ÝÑ(’Ž’?pÊ-Æ~ò¶¿ Af¯¦bƤ®I†O†bí—Õ“kgùÎÇ"×,Ø%$^µƒ/ß.{"aº³,!8¥«æEó©P×wpŽEêm”<§… ½›ê…¨UšÕ+"qíûú`iÕSµêuò_kE&üâê·¡°'Tò´,Ë5™YÓ‚_ú}xÂYÇ|ô02v‘|bªmG¿ƒÞz-ðÕE]E§S™ÐS~àÃ}³±LìˆÀNuÇêä?#"Ûƒ¤ooЬ÷w/t…JãfYûgçܵ%~/4ºˆO¥Ì° ¶íÊ… «€)¡èh7”Lì¾Ü~p~ÀŸD¯pôx;'9ê˱ímD¹ýr˜¨4ÛóV‰élš›ðÈi’'±[@ާÿM4a*]¥Äœ½Æ§Jó/[OqÅ”X7¼•‘Öìr '‹ Mh¿ç%hJ>&ÿ4žpUÆ1¯3¡- K7p_?—tX2ªÈ… ŸoÝ3ë4 „-#…¼èt,E3¨yä˜Y2DIT{\ÿ?µ´7›û´ þÀˆ£m%£×³Qä¼…èÀgâÖzÜ g¨Xï§Î׋íåÕv¶nGýÎ]§™¢ªÁ ¢\ô@_ÚÖz‡JGÂ]CÔ71o¨ž²Y®újÐ4C“¡¯&o5„âº7 ï$êà¾/ýV8YÏúÍa+¨ÌØw¬[vÂÁ‹ð]?—U×y·©W,‘€ÞnGçÌÚ+˜ó‡ŽÞù,®j?ý´IÞ)¨šÚIŸA';³ Ü™œš4™m)Sþ•ˆzÏé$0ÏYª¾é½°ö6ÌJý¿ÙoZQ /âDQ]hFíB6CþT’ÃÊ;Ie|Þ[*©&ÌS…ApÝ›°~¿TIB'»oËÙÒŠ~c‚c3ͱ¶¯´ª§ufÀ½•K'Fb‘÷a†Ë^ÎzÍÿEYçí âÁùÝ×Ä*øj§²d­qÎÚäGsÆ ºiÚ§}‡1|?šJk^6ŒäŸ~ã´ÏgÒ¡pëÂ1=[‡sŸˆ¯L÷´SJc¤ãºøš‘r~ åb &0X”Ç@¿€Ò’ƒÑÏH‘Ð2öŽ*TÙá¶Ì.8ë7 9w¯ÞÇfV™"›ÿvGóâ¾õK…X']fCœU>­^k'èËܯ‘õÓ$ƒJ¶†n,”’Ú^r2¿‰!W™š²˜. ŠË zØ‚ò&ÄðIòQ‡³‘Ï׈i?=2¨òŸcƒçFûõÍ—¥¯¸øËÄŸcEº×w1¿+®^V­ÌNÔ/š¡ÜëÈ2]ÔF¥ïõ­³(° A-·ÒÜPâÀH‹û§˜„éÄó\Âï•C™zÇè[lý8y8Ó™0k£rʸ§ýX½0Çv’´KáÖ´Ø=±L³‘¡Ne±Þ7åòFÅß­Í{—ô‡ÎñÄ!ü%áÂÿ’Õ\ûãż:Â>²Â6Öɱÿ¤¨JžH£ fq–NDáó[M2XC;ÿI¡uÄ"–·å >ïÜÿ²ß‹Ï.üý5'‘JmC@2„‘ÛÓœ…xN9Ç7µéiQØÉKRQ¬g#Ip?Ùø~ÄÑ^…׿/T~&|çísàä/½°ã<Õ û_<ÙàÃÙ¿2Ö@+IAz@mH¾ )KÎ.ºGö&OÐéȪÿ¿âEÇ\Æ\•òÚøeøñRÑ…dD¼:ø‡Ä‰}s±Œ”ÀÞ«ž÷Wôé¯ÂFOÿͬO@‘‹Ÿò²ýG\+Ò ¾ üù†ƒú;mÑA̪©êcPÆ wžÔøï„à´xºL Bd°MM ½\†O²þ”jIy†8¥î²½ø <1PÝ© ¶?ïç-5Fà g$™¯|²èº3†¬¹LB¦¬`ÎŽ¡"¨meÒAL^îÂÄq§;ǘӧ¢ ™>´ý²Œ+í)ÚWvZ矌¿Nÿ1ÝályŸdGúlCº /»þì©BÔjœ˜ŽD»ŠÆ¨nR³™&§ï‘]aÂÙÇîåê3€³3…µWªèB×°i/ãuÇ\¡6ˆ#ÄNÛP°¢Ìœ~è˜ñ=ñCI 3†òHg$³gÙ‰Öÿ{_ SaEAÂhéëa±e¦Ñt}¢O2¢Še½Ì­”†Às•öfC.•ÖéÂøÉ©°/ÕËãƒ"w~ œˆzôÙ5²6xƒ¯&¤¬ÿëÚ p¼ÐH¸Çrøæ•vIÅ`f¤®àZCó†-¥Î{©¦€­ ²“Œ¼Æ³Ž~cTd=­îã±Óš´zިͨ$¨ va¯åCÚK%U fÈÂi\žˆ}h¿‰[¶[¬ó{3†šÜ+泆Qð6¡ú"^çNI%Pß°n`ÇnSùüZ®âÞŒ¦à‘«*5 ’™!C“2Ó.…³é¹,qsbqD‚ ´Ñÿ“gƒÒ¯”÷B¸h¯Ìê3`ýØ)ÝþÌ0ýöÓ#ì¡T‹Š=cvÂLòœw(Îéþ¹›€5C÷YÀÿÉ5H-Ƭ³a}|ýÉEçªãd~l±z4,ìg‚æ[<²ž`Ø#‚y=õäM‡çö¥¿[ÎÂÌßzpš0ïéU"ܲÒ]š8@㮜ÿ!ê’áÖ£sÄ OùÞ%Z`Ûwá@÷ÖÊ,­¶Q¶N„3è)‡»Š×?´SÒxPT-»X1%Ø}GßY‡Ðªß' ïÒ×áð0“0²‘òRe<ÖEj'|RÃJcªS,)~1¶`Kt-´Dÿ[ƒ·Ð¬ âˆÎg("´Ñ>è Ùϲ2äÞ›½‹Yr§’GRC†°ÔÃ9Ɇ¤Î®àðCeŒVu„À!xG„¤ñª¸~þ\ðêerœ&à_Ý L½«Ìy"רÂ"#Qæ;DR&»µ;üJ^û‚(9П„JÝÆïIÙ04VšøÑd»Ž?ózš)ƒÍ¸¥.!€D‰G>ˆ¦p'¤äB_3×fŸ$Àï¤Ü,™‡UÎG¹|5î‹…cÌs{?¯´UUo¦¾U±÷³èFh­hùö~òÃëÔ–´}÷BbG|»“øûÁ–v…ñ?ˆB QÅj5.¤¯5MŒ˜óW;åÏ SÆco&Ï*á-ó¢ñPÖ:]ã¯ç3þIÍ{­µ Qüý¾¤&|ƒ1ôåE渇Å"òþü-³HîZnmÐ46Íô-£ WqE)Ç…_XåƒUýT¾’C{¤™Oh×Qø‡úI$A‰¥Mñ¯~Qtªìžeèö¬†À Fx–f ¾}†·šÂYŽ1Ö>…MÍ«z¥d­·÷jìC½G¯&i¼á^ÃØ]öÉ—]rÔíZ:aUÓ¶?&ÏüûÈøÌd6ôÒðÜ6HLl±suD“kûš‹íŽýPÆ;í¾Èè/nÞ)VøI¾.F$ª¤Ðvž¿Û¢ÿÒ[lˆcdÏ^‚ªZ9ñà}D˜ŽdÒáÂ8ÔR,×'½›§(ÄÄ«I.].G ®1Bðn}jÙR‡õ!}[ÞJ¯™¼þ>€Sjú*$1t–ý÷ƒªøÔæÅ_ØAà¾A‹EDA€#Åá›@´¼Ú/ÕX–âpc´Ì+S‘tÂC<²»±‡á•Þ¾]bNÚ©§>²ï¥a¨~p½ùg57¬\œ=è:€'_€4¥’JòÚ2+k§£0Û_y;˜*Ž»¦çá»Ö\‚Õyß÷oˆy‚bôÝY­È0XñÔcŠAìÚF¨Çá±a³]Ì¡K©j‚«’y18ëò—)T‚rœ§Çý_Ì×úl‡‡.a`Œï‰îûË'*/ºo®ÿÛìÈiáf»Šv½‡’¦¯Çô;¡ÄµÌ¾JGç1‰1ɶN¯N)@rSù÷ —¯›Àvl²pÓ^J› @úœÝDÑÏbæ`—4–«3÷{ªÊ úÐD=qÑ¥U…>0²ù/X;ë¶Ú¼þ·µ,xk„@ì…T§ØvÎeÈy¥õ;© fÔ±ù´1À h ßêbâÌMÅÐrøÀ2/`ÿ®ƒð`¼BR0Zg“ B  LÖ¢#«‡ëÚ!bçBc¨ø1ºª…Uõbµ(¥Ûdë¢Bk“Ùy©ªsŒ¼ß¨ð¢@¼¿]~˜ŸúÒʧÕÛ‘€õàoi'°lýq%›=³ßÂ×94æ¸V÷Z*W ƒ(ïÙѪ’AóD«F3Àé8g¾l/¿Ì¡”’+êñø¼1¼t~)¾ í¨Ží$´ŽD:G£÷ < Üýù!Òj­©¯¶/û¦Nõœ|WPý™ Ž}!|où¾®4m,ªS'o×AT7¿c¥ vóº5檧'äÒœ© Y†}˜™è*ÁN'c§žY[0qðǤI²h½¸Ïô˜,š¥⿃»G×Þl NûÁ’­Ã±¢òÝ8I^ŸÇ È ?™ x´èÙâ'§ƒÎ¸ƒ1£µæpM}½/ü´Õ2±ÀçákGIãP• ëE ¶@!î”Ýà\²¿‰ý“ß5pÝZ× õ³É,‡hH½ ~B…³eiw6*Ì_,ÝfpLvΧ!åèù> DñJÄ8¬?6ÙÈA-£6ÂÉ2hÈIaú7„,ÁIXØûd^ߢgß8G÷ôá•#-å>uª*^5PÅþŒôNz¡ô+0 ñNôþ¸ãÑ[’$òòª( £–ý=df °줆C­> ÞȹwµÖÎ ‹e˜fÕ©û1åúXü”Ur¶¼—ˆgGâ:ÂdáüDð!7_%ÆÆ‡ž1ØåÆgÎZSp¼³6àš ÒNœŒû­¾â¢ÃI£‘4Îø¡-ѱ¬[âÁÍ”Ÿ‚î× ÙB˜ ﺢ²4´B/–Hz•‘žL3ÍÜôêèz*è[ұѓ­àä.a{_*_qëæZºì#ΗbÏÐ÷rGË{§“– O$1Œ; a9Òj¶ãËrŠí6¿‘îÞþÀU+]IåM@õÏ+ÂXí"¿iŠ’ÐËaCÞ¾Ýß!ï'—~CÓ·;Š a7ÜœjNÇGí®{NÌM~¢ø&û£MúÝ>žoÉ PÅöUp«è=ô]ž¼{òªÌµöiOÐ ‘-J‚±ÒY­Ãæ–ÇlAKIT0Rm;ôU… æCíQAljRå>VÃáŠÄT(O‘mm§»‚à&èÅ*’Û£5Ði׌si’†Å`¿Ø¢ ÙÖ±a2f˜‹‹¬á:Ãêuw–Oóº lQ ˆŽìr)”‰`9gÓ¬›žš?œÚä壛ÒBT±é@݉¨píXS› F– &¦—ÕÛnûú.Iåb){Á×Ä.¥¢¢²8?ÉwÝ=5­Únú¾åšçÇ@vãIÃ=‰O4À€ ‡°¯‡ˆ$]˜¢‹ML‘ĶÄЫü;‡åúHžObI ‚£ƒ°(ˆ½NÍììî1 Y °QÝ/ ˜3‚¹_[«Zø[˸ ¾ùA…ßèï]繸&VË8{˜5xY˜“¤ÐBFÀûü ~B46KæžLÓÏX²~òP“‹mbDQ‹Ø ßᬩùìBG¹é-ª&¾ø¥©×'«jßF0p¯Ö 3ºÜ86jìö·!ù ²j¥ø¤6vHbcÏ Xný:žÐ|öGʘAð 5Ð[ßDB±EÄ,«ÌxIi͇ ÞIî$@=bà‹“&û¤ÃÀ$>†ö ïMsp6ÕCÎTTQì¨9'î~:P.Æ—·íhÿ”Ò`ªèŒEܳ/v|1OýD™wý(‘ýYø•°àë§<c/ÚEðGÙÏÁ¡»íËfÌ’+Ö\\ÚgíýÁêi ÚÓ¾“ö(p¤€"ࣻ3Á3,Ž9±íA!¹4öWPþêBWÐó©©…S×ÏȦœFÁ½ßZÎõËŸì4°a±££Ü2fFô}ã, oN“nð¼öD ‚Þx#·<„\šh±³°»lƒ2³"1tðÌf'¨È›ÙN‚3Ýùu2 X‚çD’„äzñäC\ÿ9Ñ#Ž”ž¸ÚzH£ž¶äù•¡ûà-ýPß¿?­%ÊvE¶Wâ–˜x€YšÚûüâgÜ ZÝ›¶ÛijS¡ˆ£V˜ííÔ£Þ j<Å ?X¶-¥ž“U û¤9r¥M²“¿ÁS†Ïj%ÛF£|ïQÒ2È\Ññ²º$e|þùˆ ôØn¿—j‹Kvbž âw-6‹a`uÇ>ñޚéÙî‘À.¡¤•—¡ìÚˆ #†™OCbÁv¿$ò&“A¤¨x”íÇÔÃJãóU IÏK|ØH;žçZ*þÊ`7oì„×Yy€¢°2 ?të0øpUÿtT¢öº´ÍŒÜ{\ENÚ—æ¦ÆÄ£ãÖ¦ãE#`šÑ2ÑùÏ¥÷& ½ÎŠ)‘Mlú›Ç¯©üPžÊDÀx¬—"i衟‹bî¡ c£îHyä6`Ž;¥‡ñté *J“·)ø ­ûÖXˆ­LÕ®v'À¯h¿êo³™Š4BðSïòF#îÈ)™ý¶Ê©ÈCGøB´”,¬¼ÎÍ/ÎûÍè&[}9¦7žu;À9‰Åc2°æ7ŽðGr´ØþàöVЧd&Å6ÎAêuz½Áï’áB |3Žè*PÍIDbå”:ÚM÷³ ËF¢Àð8é[å>3ÍÛôß}8n %2qÌÑlôÔ­NâZªé/`—7@u+Ô“ÿ’+|ŒkóžÓqƒÕF±|ÿ×—˜¤uµJ§ô.°`6Ä‚ËM‘‚¦zØ)©C;ôñ,j¾†à§D­Ü{n Uä‚eÓ˜XŒ‚ûÈ‘’æ:t§F ¸jlÂçÖ¦žŸ[ì ”AŸß…½¾¦õÿØ«Üë&zˆIº­;Û[Ì}Ó©aöJÙ-vÆ’„C” .N³%ò}M\É'õþyÏ~ø+Š´—IøC´€ ñî ZnVšˆ¬¾y.z°Z© \_iòU1À‘`IÊv…|&ûî—^ùŠ¢ˆ²›¡…Ån?6eî×c¤"“n&‘ÃlµÅüIÑ¥Bô&@™Š(Ÿñ~,jV–Ùgq뻟&2”#Ü:ÜdBAþºìñ ÖÅv@DÉ¡X¹â³åúâk 0£zþ‹Ÿ•W§¤(<$Ú)ù,²)eIlæ"ïÚ4¸\³ Yo¯•Ào‰÷3fÂüSkÄË6R~y–œ0¡œ~?”µea“gÌ )˜×¯Æ#<殲9“lÒœt߳؀íä;6¨Îþô­Ë³Ô9õ9Ÿl₊¿9ˆ§ã23‰ç¬Ë¢‚rÇ®ôûáîõöòël¡IÖDŸm×ìÉhÔ­Íq’£hñÛ2`ºˆ•–p=@ž‡8Q‚ ™œ¹Û]Y°~µ,fü¨£¯V¶„b™2‰7oÑ?Q‰1£¨S§ºìØötâ ã°A¡C«Fìì }ñúr=äQÉâz«h;w/j±¸ïLr‰ã÷³µdµô8ÉãÙDzº>ùøaè¾É0ß› @ÒË‹šÞsfÃmí³p$GÛÞ[º°B²Àwp`V¡R  <Ë6Ñ›…jÍîŸÕ·ðøÿEâ£i2™eS¬àLI5LUÌG©Ì±Y¤„ηzCäØ?‡ÛàTÇ´òlåocy6]EÁñÑ/ŽžóÛSR1F>+ò²@×ïÉ6U] +Z¼'0â:«Ûuè8µä qˆ!Ödmè™8é÷/©×‰©nfJŸ+e\®×âZ™ ¨ؾrP÷P,èõ¤|‚½ ^ßu«=†äé%2ãâº÷8åâ—®…—iðª§÷6Œ5ê*‹{¸›+ULµXT@TK„’Ï«Nh[t=QÑ"/ŠÐXöÑæû‚¢;rÒ!‘÷ŒÊ'qæÁemÿûBIËMÙãÚëMÁÁ¬[øŸñ \«9¾=xÖGù yt•²A ;ÂZ¾“ÇÓ}iÀÚï·µ\10§f^ÌÂjnØ¥º¦å¸\~(­ÑÂ#¹ÝZ*ä;_…«ššõúcÜq1V6¸4È˘Ã?÷¾r°Å,ˆ_,è×Zã9z*0¢û>ßpU÷rÆO4Žd/€1Bf° ~œÐÝh ÛM¼Õ/¤ 3™•d0E"¢0 NW‰£8t 9nRTH<Ø0à=kÃÇÑe”Ü"ñ™î.NU¶NñUh]‡‘2TTV÷s0âlIË•âìiœ=ežV—«>TÝ:<¾3 5ši6Å*±d'ú€ =×0ýcŒrîžOç5ìIs'£8 -žýcñÞ½ýGÂÔ‡r÷zNp±,U&q«x3ˆ¤ûJ. PO(8ÑíË7ÿvU‹‡…HCÒ íý‘©¨a­X@íü¯‘ê/YÆ(B¾¶´Éòd ¤Í’–Û~±8P4ž†ƒGôèäÞé¡"‘꣛»ÿZ4s0çKW9Ѿ„îmôð»ÓV:ß/T ¸Éä­hÈýÅ;uÔìdéº×yf“(9¯žÎ–½$±·Ñ+;˜aÍñ?á‘Ù•å äg1zS«ÜÉÈC˜­~”ÎŽ|Úråå/e)eeÅÜPjÿŽVOüÎ7LÃÓKqÜvÂÎûƒ^µI7L9˜z*û…~¨@ õ'wø–ÉŽ)P•ÊFRYz†$Ôj[‡ÄM Òí Ó­Ø‚è.ŠB=l›3Bëˆ ZZ•*E>¬Ÿ‡ `X¨åΜ ›e#¢>øýiü+§Úå~ö2ïÚù°Š4`Ü'8{h€˜ ,’0Ür‡4qÖ*†R‘ +îæ_™€y!ç·!…_jl5™Ð‹¢ùÂï°=±ÇÄ;…|ºvÿgÍÍÔY=È>p:ÉFv ÖÐö·ÜGˆÐ=þI¡«‹6F‹ª½\ôN±ó2´c>Þ}O¯X÷˜'–Ú¿­~U’¶Ví~>eN×ñ]¼¡ÔWV$~m_ൌ šš‚ €næ7€¯üaÔ½‡æìP·}WÇ(È©ù˜„FrJåîÙ†/ûö@%.Ñ`¤üJbm5³ƒHdlë€-Øþ„’X‡8^§½ÙÞ;£É<[!\NyˆÞ|é½#Ûk,Ç=;é|`y|É&0äÍÐB/Úo…[®Â¿SCªxþbñxÊŽõ+Ü"Sp<¿’a ‘÷ëwIx`ï~™TÁ ¹_}êë„r]Óˆ©3â×N:BOçʆ^áÉ¿ôÝ×"=Hä÷WåÃ'O}“ÈE5 ƒ¶!©ÓPÝÜ&1ç³9Ð¥&¯w ƒ€ÃÑ7£]{GãÛ»š(¢VÛƒôñíº¼Ð#Þ?¬ùYâ&øƒqô›¦iáxX\rb.½Ó÷%Ã-Ý2Ð\j»6¤ZNxýqºu÷ªŽõöªC‘,”.eGÈAE2Þ«àn&à*×ù@$uŠê}Ìþ‚ŽìW#Ÿë Û/XÝ.ÀÏD·Þa8ˆ'ž:·HbFBYbQu»ãö0:Æ *åŒyÔL~Ä÷4…uë`¬ !5§dImÊ»´BÁyѺRFÓ#$È´1©ô˜‰,î{qŒ>­õ²­Ïüâ"EàXs d‰ŒÍ…õ éEGTggËç´‚¹§‹÷ ZL”„µªaz•XùDœbÎ :¥dê~mˆp¿rÍàj ốÚWZlsÑl}ïã6 êz )äYFIŽØ¨ö7yX ¯Ë^0Üù¸æœ)}›îåû²6  Tà®&Äfçuá®Â<D`õÞÁ¼ÕA½dsUªŠ’»­44óW¿3\ólˆÔ”Ó 1Ø7Ì=:úk`ø750Ÿ¯+}1¶¡pL"´™“œ>øß È<× 1[ÉIªäÃWþFñ\ÍØýá jÜ_)›Åd »rR®`jlU•‹e ° oÐc§„ƒ!¦"A 5|!}‘ƒ€Œê-Ió(LJ¤›?Û‚£•õ±é",Jp§ ýã'‹ã•0°èšuß{ ¦Y1\“þ(©Àd h i²U«ÄI½m )ZÒÍÌë~,cä6bydÿ#X•Nm˜þ&îŠYVÔ}mα ÅÜþW |ýâØ½Ïx _§€z‘cL³ Ý`z¼Æóêh÷W8ar ~@ìHò€ßÜF‚hSÖýëÁ}‘GbÞöàÖ²9tsÖ”ÓÊòħ' Èü†É±wCÉòXŸðn˜ktZÀÌS¡PZ‡ü h[¶*° ¶€ÃQ 6Ëhtß@*"8¤dΡyäÐ;/e¦%Š¥¼Ù{È&4vçb8€m¿¿\*pA)üì¡çgð~ ôq?pÈßöÆ=Ójù€:bNh‘ˆe)*r×çQWû_îcFí¶ùŒÝ#Œ0„Ÿf޲"¾h [ wÃñÑàÍnw¡’1Èß?‰várÚlÄ@Åùl²z6gÍËóqgm„ Vô|q$õ½4#žÈc¯-Kj.t ߌˆ)üLOÀÓY"tÚzEm©ý‚æxƒ™ú¸¯ìBýÃRÍ:¬ÔÒœg´ÙØ88my«¼ÞJFxAe[Ö¯T[}0².š¬ÉÁ–ç|¸]=‰ïTybÇu'¾6ÖøÃe ݤ7ÆÌËi.Y!sCôtó[(Ã<1Èæë™£ªx!·` érúÊDP¯éÉjø¹ xvŒ¦Ej;¡b5VHDîŸöà _H¸©¢¹ů'ÃG#Cá†o¸¼/„+cßJšþŽ×R!Ãeð "D¿Þ?Âåû 1ì)×5x‚‹5»dÿ`(‡ACÍ( x„‹eJXÒ¤o’P=?éØÝ¸“~XÏDÛNòmÀôÏ).K.¸¦Å¤ACÆz§ûÅHÐípœ¬Mq…ò²¢{¿~* Ê´³f:ê§frÆ}‰„äBÁUÒ“+…¹›¥çÊ|¾V»ì|н’@˜Ä¤½ùik»ß[Ú0di“Ë"¯+­lK»m6yç²’1Lt—ÃöÓ±ï鳃+5ÑÈwg ºìCgãGó7Äg @ “4à@ªÆâO÷´ßÕð¯™ÂÈ­1̘Õ ›€uÒÚ¹Örûµi…•ÇS£Ô¢ík•È~²YQ¯§N¿©ÉÀdžŽz=fýs6éQ–ru;$Dz 6 åò*» ¬:óM¹é£r‹â݃š¬wÇϪsD\!»½†ðÀ«·)¤Î–ëU¾Nä2¨×¥» Zþ¦jv¤êU¬\`»÷öÀâ·J£{]#Ä·-ÔìâÃË7øX….Ü”"ë¿Ì3W‚ÿ‘߆(¼øë›Tç‚q#-—,¼Ò—[èhz„tü¬ødÅ0_§`>eôN°Ò™nv>':ßp¸_ÓϱHêØNÝÆ£‚[°Ô•ïö¼#ÝŠæÅ®˜xÚg•' HþZ"´¬(z@ A¹°¤ôÆPŠ»ÃÓqžòVœÎ3*÷Eèž”§¨eBãV £2'?ôU'Ð7ÓÍ8‘»}©²V•ßoeÔrƒzÝe¨Œ¿46Åã늾ý£ùLÈÓZÅ“¨Å•±ZD_rèÉ[ÊXšÁ{A, ®À]ã° ˆ©cÙ€þ«¿£¨U|K¯aeéÒ¢vŽ-–‘„[= sí=õÑ£üFE¥,¼dÚÙ¨k“’ýÖ8ú»|b³¾IǶ¤¸ÎUJPr×)õ-ÑÚ¯ÑúÊç†Ô?õ¸Ê gHÞ½³¢Ë)ˆï·ÊâÁ6MðŸÈÒÖ~\ž¸Ì̹üòÉX4JÀit1éIáKµ®8K»Ç?‘À7z!i ÷UÙ!ÊpÐ5ɸÉçÕŒgs9:nZ³~(ý'"ç§µeï­’>ÉÆÑKÏô¨Ä®ßDèúÓäÎÊV™ ˜Šèœnu ¼u wl¨>B· ¬"%ÊoÐÊòa©ðÈѹy¸B£LaÓ7~¤ÜæhíöÔWb„½™Ñ¬ÑE1Áóáp †óVØëׯ8’Þbˆí-R«‘Ñ$æ¸E?a¬4Çø€òà¶N`wõ~ÓR輚rQY(á"Ÿ Å…ñœ e+ºr%8)† ·øf¯“ÃqMà¨IfF*„ÀmÿÙ*”b¦Bz“Ò÷å»-ÝFÊ ~ïN­ïæA^Øb€.‚÷oâìH›¾Ìmö—köÀÑwakçŽÕß]÷ß mѺ©™ã"úPúÊD÷ÃÅ@ÀÓf°2ìâ`Š ^Ã5·«œ·‹…¯ñ-5qÃÃëkõ›‘j¸b—fR2MNÊ”y ã«Óë_rŒÍ¬Öà°_YŽ×òÛiðKTUOŒZÓ¨jÆÙº C·Ät³Óu+Úf>Ô“Ï?Sô€jŽhÉoA’H§snR‚?^íýmJÉÏê(,ÝÝ;QzÃsÚýÚ|ÚØ3Ž-Áv]]ï™m«bÏVyŽoPÏ¡]L›”¯RŽÑω$ÝŽ^àSÈÝíå* Ã=cÏ”Ggý"²~¨…0BÇŽ¿}™8‚š×œæOE°3+4· st:^*× IMZnËå¶û…"è ×/ݶýXÅ%$!˜ŽOXEªdU¿käÑßNÉ.è®söÏdœßwÎ'u¡Íï×—ìâV|bªíÖ­©afgÿýRkþåõ¥€°ˆÕê¦Zp<ßГ Týµ?°Iûš°2‡Ú íP•Ãeª6KXCzìS;?£s.Ï ]Wý¦üiA'z¯(©·™Óÿehlø+±à[w¿Ïˆ`¾>÷`1†QVî¥5–þ Ž©ôæ3ó¦8>ú0e¶¸x×¥ÌÞÄ}¾`Ç´¨®6*ÖŸãïIV]ô1SÒþv yŒh¨Aù‚mJ¤âÜB÷rž•k™¯7@鈣m‹9ŽíÁ$»QAÚ^4YÛü8‚A‰,Ð ÀÙ"2XÓÎOiev(ªây: EÄ–-/Û;‘ä§ÅE¶âÂ|{F—ÀKå´ÔçÊÞS¿C쉢•hȾߨZ³h +ˆF›?º{ňÙî³es cú8 Ú0„«û½þ}JxÀHPÊ;àºÞ ¤ ‚âôÚUcªï[¶Þ¡CÆ 3sÍâÀ#0`#j–žð-l+\p0pM ±Ø®ø¼øe˺Ã|¸‡rß åšÅh/{k¾LÞ*Èü¥®U6 ,¯´x[0 ¿z MËà ʓ‡ÂEx3û¶üY/ÇG ÏØ _lZZXt¯’pú{üá§iª¾¿†·€JAC)>E¾PH•h";-ÌÕMVоJ`‡ĨÓnQ‡&‘|œéì.ªóŽ£PìéøY…ØNÇQÕP|­JÌóÈÄ¿62+¿æ;2ìµxXR‘‰}`Ϙ÷Ñm¹¼>¨öÖŸ6ÅÍ3¹}‚\Ë7zï1zÉÃa’~ãzhÊt4”W‹—Œ¯wr®dn’†‚OÏ“‚ oŽ7ùiùÖr {ìôž‹?¯p“¾«bag3“œÐÁXó‡Råñ¢íuöó9î†TÍÀ<Asë –‹_C† ã¨aµlzâ¤=ò«ãÁA¬á^£UØJÀ¶ÜV²[`ôdUZOQÉÀAvk㤟¹gc‘Ö‹»a0KHƒ,mñyH]ïMçü‹÷s&)Ÿ‘úi±_tÊ Å_æØ—sÙŒÀãøy°äé½W1©ˆ¿ ¥ {„€|ì|ÂͽÓ¬""˜ëSÀÌpKµeÐŒµ–´Äd¨%ÚöØže„«VÜYG¥J½k»sGÛü8UuSªà…ü;“1t0¼…vµÏîfö=IT)Ù`/:²ëJF1î«å&á-x¬chÃ š˜¦ QÑüÂHê°µ‘òôu¸,ÑI¿`ÝÜ|Ú¥Ò(­§W…M8¤6êGÿ§d5à7v+JTfˆzFÊãµÊàHÖ¯1ìˆdö0ûh§ ^ýÚ2â=ns8ÒÉP'«-ÂÚ‹«·äÌG‰®Èç3¥Z»QØþ£ÅÙ¢XÔUÕâ–åÕk<’ð íWŸÀOæuêGÙZDÖíü(òÉ} }\¡@,›çYÙîLÊÄ«fB}›üó@"¥g«|~uj+ü®DNôH3‰ø¯>¿ ©‰‘Z¢SØÉlmÇÛQqyÏo™³ìp—÷Ä‚ecµ8–EÈû‰eÐjÊ‹­îLîÖ„ ËH`À¨! ùriŠŒvÉh~øæ˃z9†ðè5”éu;5ë³ûL ýùÜ/ÌÁ©þFÖ–`ÛÍ…ÃÙªá5…Ø-úÑnø›ßäOñ~°~Ù›ezƒ&‰dÒ‰ÆeÂ¥‘oí8¹I~_-“Íê<(øGõNõú\†·Ñ¨?{ûÄÆégW{0ù&ép‰#`Ý ü›c¡‰åšå´â˜®dÏ$Ž<ƒ%®X>4΢g‹98`kÆê¨*£ I«:¾ß1Âöš]9üu©<#ƒqĦòæ±£iùïÌÏÁõ½PkR©í—~µú?Úè¢_ab@L½mýÁž ìÓNJCÍw—þžf—uK ubÿ°gL¢‡˜€3‰%šlo7Rªßàtüó‰ËÖATyb¡˜Ðò(}ýS} B4Õ}”:Îoø‘¨9h¡4mÚ@>Ï÷œ½Èý¯â¦šöNba7ÜR¾=¬oq¨ ëv øÙ¯@\þÀr9;ÓM‡Þ'ö›æÊy®×>ýfÃŽ·Nº_xo27Êýa«62®)vÁ·ènžçÎî'vbç +³–ïŒx”…‹ îh‡Ð=üüÐ7Kòœhr“<¾qq›ÇùÇä+°èîê[½¾aoµ®ßý¦:~Ö>Óú»c‹Io—ÂnQÜñ0ùx‡V¹?ÍÉŸ¾ä™d1V¨è/ÊK§9"(ý,BaÅ{:åõ(ÖÜ‚Jvµ E¿ö[k”SM :ƒÝuÜmݨ9Š‚¯ R§°v&šãÌúVŒÒcëkàñÕmÛ\+#ÕDiº V¡†»Æ}Ng- º˜÷ºïÌ0ìÁkû°­­\€×>#0Aó¡€R*¬>p'^ÏÚòí:AéKÊû°œ¤szÐ4Í쯌îÂ^6¨Eš·_ð2òYží*½íq§E¸ÔObB§+.IŠ k€a˜[«Á¸¡KœxZ8ÄVÞiO|‘^^¶kXÇ ÿµ¥ÚZ/rΡ&—|>3Ãˊݰ­é(Ù+o¹a×É*ˆ€ hî .BÚ7õ˜à!þÚäâA&,FÞxš–jº:TøÇÈû~Êt (: X´Æá@…Íd ¥~ÔöÜ·oëÓgj߯‰ãè5ãbU3»zkm9Ó»h˜Š´„ÎϧLÀÇ ûJ›OP"ÎÜŽÖÊÑ®Q$ºzçÁ°á,éá?$Qî”Çð»qS‰. å¹–âA|Ì]‡|8L! =Þ£'æÜç"í_Ls§šË.3A4y“F?­gœC,P±Ç ZU˜ÿü@9 <zÒ‰^gŒƒ©#·r¥ukŠcæ<×øù0X™B u«÷]fú€IÜ…5Á+¨Ø²ãábˆÁ|ÊYᘗ‡ÀaèdžøTëv wÀ̪ €®:6V¸c‘#‚‹t}\……¿fY´§¡¢Ì£¸©Œ³RÎ}a1óÐŒòç~B’¦n6èZP'çS¾Äö’m©ÏÁ‚¡lÔN6Øé7ÑdHÄ@ÿªŸjï » ,­Ôqæ01qKÖÙ$I=ÏBƒ’ËÈL‰tåâNã{j ³.ðv(Ï3¾òã¨²ä§ ƒ5Æäçóýžo~šTÈsV’¯Oóß­ògÀKˆ&Øc3œ€ªò9vÇÐøÔ(Ø‚´¡ãc’¹Ü(OÈ©hÕ#9;yý5hú¬Bgè[%¤¦˜òò™Ÿd#øeó¯)•âSwå•ÙµÌÜÂÙFW„g´…K_[ູƃëÞ,‘—3Çâ›Õþ#ÖÎ q(\3Ë&u¯Wù2ì%pïúD<Ôr2^<«RþÓ=²hpÞw·-}Íu#ɪª´íùæuÆfĤôؼÈ>ž—ÚÅÑP×B¡¾…¶öáÇôZ¨HgPÑ_+qà’ÍÌ4!@O}à†XP§&jÝÅöXö9íãQ,ÒªáãL®’¤\fÓ‘ÖfRBa×,¹3.b° Ç“¾ù¨—ehÑZÚdÓ[ù5~=É·2_ÍpÞáô€¹%ÛÝSu=$MØ¡%#6®ô]/ÙçŒÛŽ“ocÎÏ‹!ýñ" *g&TE#~î~D¸0mEÏg´ËÏ3<òûÂzAµƒ_Æ5t³Gñƒ—ûd{÷€¥Ý­TÚè[þœµå†„é_»˜f¿ëÿ형'ní@#\aÚvU¹¢zþ ó+)‰þ‰.a-0*#‰š®s£RQ‹\…呚¬É3™Î9â|úà÷ž|uuá ~FÞ£»fýdé^a8"úÜKRô0ýKM´^­ŠŽ-d dvU¾QÆÑÍ,¨>Å 9àÎàpßT–'·7­× Í‹•~% Ál,ò”‡¥tÇŒ±dn}Ì H§u”±(B’Ô–}HÇo_G_f©_ ›·ìœ~„´¢‚ny¡W(/µø‹‰Šô2`ZE‹4~,Ãïs1—ØH~u+Bß»î ö#œ/"WÍ8ÌOÝ]‰»k¯n˜ð¦ÚµÅPÐ 44=!]Úò¢°ŠŽƒŒ}«!ëŸ;ìw6ý«ÊBý‘ýŽ|½÷üùfKû>µ½ÍUÀSD+aµÅ‰#?÷{´DŒÃr}cù1¯š ö1F¶ÊÃ(-EKÛ opºÍ÷Ÿ`7²[•?E^4l‚k6!bWÖa4Ws?Å –V"úQób²»D¢˜ x½"&¬N>pÙçÕòà`4VÉ·lº@K½|çŸW ~¬“&s/œßú˜E¶^®E&Mû_½òvñ+ÉÀßZKôbem͹ðµ.÷ ÿ7J_r›G¤¼9ïÙÖrË€AÇ…W5‚ÖZoÎÈý=ð–ç0–Ø…oiDáŒl5í5¸H”‚hÏèUŒ‡X³“ÏM Ìá|fÀ…S>bO"ã ¶«ÒG>³Þ¿Ç÷—09¢´'õçTeˆ1fI9œ#IþÛ‚ÙÊÝ tV-Õ Ó³ºsW×2iíô@l“uÒ¡æêŽÓÑgñ¶hÜ’óéܳˆ£í¨<Ï™âu{‡Þx§¦MÚ¤EµªADH/Ej «ÓG/+M&}íØãª¿g¼t8c{ý“¾FÄÛ!Iƒ‚q!FVG¬›5+š~ž0í Z~lX3™¹#a›N“Xë…³dÌÛ;Ô€…¶MgM°§–†a<;{‡¹=råk4ÿÊÖ®(ÓÛ^´¡E‹¡Ó,å7Ró+¥I#ͩ۷çå“òH”äRÖªÝðë ÝíöäE5¡Žü._Ö—úæw™?£ÕB3eZÇ”bc‡’µÿŠÿš(¸v8I`ç—ªO•Âûá'·7DkŒšû¯?8ö {‹~¿­¿ÅõÊ;p·ª÷Îí7AÝÀDƒNÂÛ'_Ó}V1îÏ‘ ûß=æÏ2Kè@õ(¬ApÚ°¹èµ6!ú,xVÝýyåQâ ˇ³ö3(FÒS8ív¿¹LV®ýÂÿšcËÛ†2µbeÐc8mi~ÒÇð_å'ææïNÓjÄ„¹º=˜´‰è[µó4wϺÂÐðÖà´NVp¢¾!â ‰–˜ã.e¼4SH½ G,õ‹=@wowc†Ø°"Á¶þ&Ü_lÇþÝF0FûR~Ñnb¢®zÆÍç®ÔÝá;NV~EÀjåÔ0$Ûæ¦çá¡Ç2ËþfÊI&˜P dDš®?é¦ |X¨‰Ü Ïܔܽæh^Ðß·Ñ2ó<Ÿÿn›°†ŸšÅµ| €Z]«À§ò4¦ÇfÒ¥ßE΄܃N¦^k]lÕH›"{Âiæ]5žÀ™-{§ßaš˜ %‚ëî ´z%™-ŽÞØ3Èã:’’ º[£D1›þ•0aÅg{=Sþ©ñi8äòõˆ&ÀlI×jZÛ4™ÖD(\âȶÝÀRŽ<Þ°>²)»¿UòcròiòÍèp»„©WYÏ! ’sÝCãbšÐþ¸æ>òìßȈ8 3§›/G'‚5™ vðqÅÅÞ¸yô¸¯ÕRNsöÖ(.³ Ý@;”2ŒÛ¸ÅH$݃•›L8^ÅâP^ãJ宸¬y&ˆ0ô£c¥qåÒN{ ͘0Θ¦”ò»¶¦B•×iü¯Ge2ë;xÃQœ}´ñT™‹ËÜÌŠy+vËÁ6’«`뛋»dÇEQGú¾’Z¬ª÷ra&s©¶Sl²œ!\Åm˜”Ìp¼ó=øË({†âÞ¯°ãâ}D;Zb/]/ƒá‹ìNÏÓ "•aá‡x/Ôê?ߺ ‚—ݪm±%²Á^ ‰¯4±90ÊÌ?@ÖÀh©±½% ¡ø*׺ŠÖýÓµSF¢GC1©Õ‚`ÑÚÕvCßÐ&åøÞ¥ÓÝ‚„·*Ízå¼Þ?ô´  —Tä‚ñ³®ÄÙ¨Hj¿yc¤þ=q7'ÚG˜$õŽÀ‘³i…¡¢Œ ,Z«Ó°ˆ;­§¯H¸ÓsIêG&º‹O B™Áq€7q8F[ ùöw·{Þû6·Î“Ò5G?}CË­Mº+é '^AH$ò€à8b¤‡­I`Gœ‘½OÊJlLýe±O!x†„O_­TŠXdzMŒdJb ¼•'Ï1”©ø$z˜"r`ËH*ÉBÿ¸X >áØqu•"0Ë<4ƒžØ IͲÞש‚…M½€G;ΛD¼Ì¬wæõž@{u¹nL„¯K‹®9 Líµ¿¾Ü5÷:›Bù),sAiF3~ŒÜlèåøi{§“š0AýìØùáM=ÀûKùZµ“£/MïøÀ›¥a 'M2©1ýÆ¢bøßˆ#;JáÈ[qtšit]^0[5è’Y¬A÷›Šzmˆ”1ôêúBÖ³*;½èÜIRQî °ˆûëÜ@ðɬ©®éfì•ܳWúGâëÊbËÍrŒÓmÊ‹^s\_Ÿ<×ÓÀ|J÷Y,ÂÈ÷[;ÂÎXÞ¶¡¾o`€ba5%[ß6}¿ìÊêLìç?û`&þv ù*jOÞ AØð~§“kèÏ› rž|œi[}lõÈ‚yM‡aŽ7AyÕ¯En8Â”Ü î—ùñÀ×Ïë¡Wjší|ç{[=m¯/Rj@ZR±¡¿=òÖ+îiÓ‡~º@1>I7Žãm%û¼Ð2§aµskD¤³ûè'Ø×3Ò“uW?,-€çó±[œËµ¥Ú»‘]@¢Ý óΟ:í@,ÃÆWq¥ ÛÇjq.iÏË,šrU”/v|KTmTÃ)`éžØF±µÉæŒ í€¹÷Ÿè@¢Ï¨Æ•ŸžF’CvVº•D¡˜áWG£o¾Lt£©ˆq÷ž”î ÒÉB1{Z{ükD›BÎݾKˆ£´¯ï땵‘Ú¬7¶pc0S‘E…óbˆHÁDŸ?fn dµ2£‡L™÷‹m?”xùXÑ À%ÉnŒï?¦ðO‘%ìípÏü«5W³Ý î~Ä-àWvãnåüa—ñ¦ZB#ê ¾Çý2>ª5îè3î{™(Ï5|zÁ«‹½ÑÅlÐ8FõS_øÜ+¦X›¨¿lÔ†&ز©‹;”(º˜Ç×ÙµÄðÏfN該=þ£Ðå’=ž¿Ót2 ··È®¤Ï\==|rE-ANªb!B©e ÈE”×Ôþ±©#ðˆ«ñEØ< ‹)ɦ%¶¦‡ýÈKÛr8 A±{°µ/ÏÁWíJ¥6¦ Ðv#ýóêÃâ<Û…Ò¦'‡k‹Ý›•‰3jkM•].&¬¸&ÿ}jÔÇ~A|Õ•¿;¤ÛóýdÐ0h˰\ò r’ÇùÀXÏn‚¿jɪ`W+¹•xP¤5‚\EëçtèXJû3•;ª‰šuÁðOÁ¸Çެ`êÆ÷,F«üÛDºÕ‘ÉÚ5K4Æ Tb¨{®27Âøïê ÅÎOõ©Ž&m2ëŠã–m4Ã$¯’a½¤K˜âûLa»ßÞû²_¦·‡Uè;ËâÀ(:!«uR¨ŽÏôJ7Q›’Îãh¹=”n)–ŽT½–}íU_î ØwÁf)…,DRé$Q=ÎxdW…a£sOö*³Øµ¦Þ[ºŒÍ·ßÙ/†?¯q@L¶:~{þ/ª ñ•ôýÝŸ¨Î'öìñ§’¸DâöOµÍ~“¥†2xÄâ;–<Í:Åÿr-ê¬íâÛAUI9[Ý'E%[g â][¼’*Yákr¿åÃV ÈR“3ÆÅN¿¢¼¤‹Ð"Ü×:Çq Fëõ´È¡©ÏŒPΣìx8a)³r( ;–Í{œWàZq‰Qðu–B©pö…˜fš‡‹×b_w ZçËP7„/“;Õt0mÔÿí\!æ·…üˆϺõ˜Unÿä½p8:&Ãä •ìþ8Ëg·ÚúDSEѰOî•ò¤ú¦BÜD ЧD†"(SÑ(Äi³Ž‹cgŽ‘H ªÍ,$Ò|KëÙ•Šº=BPþµxtûœò¨ ˜p¨·8{ÃAše샒¢ÉØ—üL3VQë©Z¨U–£LFÛqç8lÀßh3pxOF±Òt.+¶¤—çõHzwg„'M É>¬ÓªÀ8i²B·²žïÆ^ ߟ€ôDÎm{’ßA°Z@;“ê¨`ЕT–¨¬K`TjDE…sÿ \ýGR®6ƒÅoôŸ©˜Ón÷ÑÌêÞOÐKÔRãƜÄ¥X®z˜~àÐjÁ`sã$Y©´WÂ…IÞ«¬]¸§ÁQ\TŠªÔK[«F"t¢Êúk¸V‰.Þpõ‰A<Œ42ì& ­L^•Î%Ý\&F8½w¹hVü3|H{³èû!é)uMÞ¢Fã•ÿ¬ykÛ Øª¯ß‘ù!=rÝÿ§{¼…¹Pa{úuäÿem.Ý Ô¦`E›® Я~æYWá$~¹{Cœºä_Œ–«@éÈGWøug+2]jÏÚ)ŸTE ¬sÜroÝ“jÊeW*Uˆ,Ó›¤uCæEr][° hÛÕú-]¼wqØœò^ÈݶfÚŸç"Œ" P.'¬6sá⤂™)²uA‘°Žñ"W™t-A Ç"+¯éJB«ç6*,Ú™·v‹­Ñk€Ú†Ðz" Dê5dQ~e•@(;g̨`Cs7KÔ;ÑKî ÀÑkªCµøˆ–rE)ï£î8lS™ÉW+Ú/I Ë€Ÿ‹ýbQѪ†+TÒåu²í$›: E‹2’æcåÔ?&Â驊wãÜrÆgÝV­°lÜ{º*Ø•Ü<¹+î,xBµË?N§R&Þ¶…²[”l¡ð“—v¨VÜË üíÑÚC±7xÛ¦ûzÔÕÛ2ªéÎŒ°´WN.…5|_íü8ïð/˜„p÷~ZÔiÅÄÛŸºÚ›’_R|{bìƒüÉíÎúEä”ËýZ³êµyç(‚ŠÐø¹¬ÅOËÑHLÊëBºÄ`ãYAªÍM9•±1#Ÿd‚pz ³H_µ€´ô`N]‡„÷`“ôu„»om5·/ÃEGžòÉê ËÚ¦õuUŽÆbÎU#$~_‡ L‹ä°$Ú ¸r<VõVWÕ{sT“=!htõ~eœ-}€G•dlòPJ_’|z£ï{-".Ê¥ÚÑ`ÖÈN_ÖCÔq±â‹¦ÈÂ/ccªÏyJ?I*™‘Œ²'7“\Ác‚‹ØÂ&§ÁoÇó™álsà”ØW„q±HÞzØki‹"~q×cœ*gÄ_ƒ$Á]žÿ™ÝF°ìBýT1²ÿ¬ãD,¶œØuE~„Qóéýñ,4_gYx"ªá:ÜF¾±#‚ËÙÛõ"Oõ+ŸDÁ*VÁKÏ™?HEEÄ“¦$¯g,zIqwöN:ž5ÞmdM!µüDÄ7±±üCvÏ•¯)´†lœdf&$.Í:º:»óºóP—…È~S³bL°ô–g®) ˆ kÕ’«Œdƒ°Qg? ø9h”¬ÖV0ñxA&)2oÑx€S q:’ ´G{8P1´‡uåŸe±9Úcåþy ¸‹ÿÍÇe `…Í{A]ª¢…ðŸÉrÞök,$y0 '!½¶½”ÐŒÚzM3’z–•¥ªmÊÆ/oé¬ÇµÞÐ Q-Éߦ»¨je…nn* [HüƒU÷“¬'2ù,‚Òn:©6½6¤¸JǾæ´â‘´Œ3Ë6ÏîíÅÂv1»Š `ðtìni2SP¥§çÓ·@µ—¬‘œ_ŠèeÏcBkÁÞ¶×¶üݰIú,ÿè…·J€ŠP²×]Ø^Ñ|”9mžâ”W®˜ OE0剧¯ÜMëOéñS$E”ݦʳrA‚O;3yW¢¡û4›XlMâmÆ×2‡71̘š:SÙåÝäµÍøÝ—Æ5›;g)ÿjÐókk9‰êÅä ç¼¿>ïVá=±ø™PƒŠ¶¨VKrlæ&ýœ×’¿·¨Û¿?ÈÝv¿³ô%cˆ“"1¼ áUÖ# }>ÔKY¥ò'êP·4­ EÒ3ž¿h˜³bn2¢Ñâo‡u)¹¿vám:\d|!»¨Ô„SË + »3ÁññÄ©G95êÁjÙ«%އܧÞlËfMßÈ(-W©p[ÛUH¯=ªßj¬xµ˜Ïê…ªÜ=¾‘:XÝ–ˆíØ;€C™ý—<™/yÊóÀŠn²¼ãœcˆôä"ºç@º(§xƱY&Aƒ ,ü†8l¦ _ÒÚ×Ýõã÷Cå IœË’Ù*Ĉœpʲƒá kòb’ìvìòy·5].eª»›0IwG}íªFÍȈq¥l t½¶ôŠËS%Ša¶0O¸Tͤâ8š¯/þ¸gnÙÕ¬SrïÝ`þÛÜl=nqÙ6'½:f—ÑÿŸß{b~“ŸdX 7£Y€’.Ô_þ†¨“Ux³ŸÀ)*M‡Òi?^›VH ^›üp±±pÛé®Ë5zŽæ ø+|ô·Ã^Ìiü6>î€ñ0.î‘%§07ü3]üÑZàjR?$«ÐQ›Úp~ -›@ËÆ&]“‘²ÚB é, ó K”·ûëtZc7iZ?ˆ©‰d+G%†:ABv¢lw„Eù/ìjã*Iímÿ\:€›$/hD·xR6}û;g9ÎâJã]½îÇ8 àÛ˜ò¶»B/‰1–­r¸‡4öÂœÈ ²à îýË›±Û´lÍzšaY½†$O¹ÉY£EÉ9©°íƒŒ¹tf¨mp ÈX6°$-â| ôkì© ôGåtþ@±[ó4‰®MÑÎ(ÞC”Ë7dS iEéGêÜÁfM’‹/…œW™ì¥ÀÕEÙL ã£ÁxãW‡¿RÓU©ýžV¬E„DHf(y’óÃà€‘‡"5 Â\ó l±¸7ùvJ~kñüÊßÕáîÊ,pyf“EÔ£ÁiCèîýÜÃRÒë¥öý¢¨8êâÁ ýîýw6Ý)K|Eõ”^Dk#™« ªf]Ú^æèÜ cÅh`­›A‹¾¹áFgv¼¨°j¯dêUÐÞí°s/îQv¬°?*â™jÖ¦“ȰÅ4Ôë˜çýz|ü ½ˆÈî"å!) c/ýÑ¢`r^teö’›j>agÊÆÏx!&íYï/mvJí<½¿ßãøBýž»(DCcNDî•QƒkVPYADÙtuøyÒÖ𘡳{æîa4B4G^9¦ÏK‘;ãS‡–è%U>¹`¨a×lBS}/SÝÎV£¼“Ú.a%uÇíÈŒ¶¡šNuIt8ïüÉAíÎèYoÑŒ™É˜Ç™•ŽÑ‡\‘£j‰>’M¥›š*w6UûMé˜3’ ÙÍo‚ÏGtФe ÒõÂ9'’OCö>hA›÷í%·[ã `¯Ö²]ðnLñž† ßÎÌ,.Fïy¤U¯0®TS¼‰pF#µ/Áí3Õ6éW ŽyO)JQe¯ Ñ“nBB?œ Ð§jI±ûáˆý6»ÄQ4ŽÊïÑs:~’AA'p=žÃÛwѧ žãÜ`. â9É`7]Ã!·PÍjRY"Çw,·õCܬïu–— O[ù'dº+Ï[å@*@Å&4!Ç“)x¨DuÀW¨­Ü¼læ?^£3+!8§™ü­vÈÌ‚..|âí7ÆÆ26$ù“‰¥Âöko5ô²fì`tá2KÍ8Œfy ƒ½*ÒÝ?£Û‹ùçÁ=òó’!;Y_… +ª'šèQ|g(‡Ür9ûn[¾»ùS.&ùà–39‘¾ã‚aCÙn ±˜• GC…ä´)£C$[äà•Á,G­0¾pÉ[Uùé“'ÛvŒ–@mPXˆfÒ;Û\æÙ€ÃôÃ4g •€ _ ѲÊ,]\ÌÛ€œ³{ïœCmùqê‘&Ò d‹+á3ÄÐG_Ðϯ\‰uÚ†Õ*¸ôÆ3Åçúí¦RÓãBŒßJÎë…r_Ò‹/çÝaj  eTCÒæYDª×4Þ1ׯkÇHíôéªÈt[@ƒkçÛPÂ=†¼cÒ‰%Vå?d¯²D&¡ =Ð(Ý‹‡œe¶y"yuH·ÒU%ÅwØEÝPüRTH h¹ÆvÀ§2n¾¦«—LÉÖZ‘ÉS¨’$ðy„»Eø•ŒºÂFFÒdKÕª†`AZ\˜êI0"¹4A×ýõ—xjFËÞÌP¸°~ܺ Ë»\j=Cdç%‘ÇgoÇyíT4—Ĺõˆ9•9Èäåy…­¸Ú÷}š)\ãzS Õ©[µäÒÉwƒ6‘ "½†dкÀ¥Ý7 ŸjëäeFû=hR{5¹ÁŸ!Ö+à´ÀÞ>2JC¼Ý/¶Ê-Eaˆe!ø 4_xFϤ G‡¥Fk1Ëþ{\³‚½÷&·Ýõj˜é ‘÷DK¿ ºVJ¶ÝÃT#ææ^ËR$x”ÊM×]uPcÙÁ{MÆÏä\èÕóïÃ:`DhÇŽˆ4UI0NlMª·"x÷¡°ÿç[¬9þ,¿…¡̽š|Z‰•²UÁ‡Rß‚‚â³n1A¾jjÂ:’‘ÂäYŒ+ºÏÇïÁB˜¶—X .Ðä%ËwžŠÔ¾ª&ÒŒjˆâðo4/„tË*zñÍñêñâÖ®´ƒÀWÙ¢…†"wcÚÆ<ÉÞpùpÄWAbó‰çÕk¬b'ºÆnfв-áÓ$ÖU`á=Ev(J¢gÁL \zŸ ²ŽäDaškƒyðìoØ15µ<%#3»¥wR/Ñ»ÂÉ¿‹ëþ×ÊÒÅ;Ð_*séó¼<¹ ð‡õ ïªs”ÿ-ýL%DFøR"3êÜÙz:þ+R»âù4|ÆÛ†)¼3¬Áß(G7LõÓztú^µ¡ F•Ås(k=.•ÝÊ}ÑÁzÆ„µš›M‚nû@2óÏsaZª ¡¹.]i¤\è¹Zü6ÞãÏD0*b0º‡äOÆß+gn§Æ¢6í–Äø>a1ÚBNÅ‹m„ºöD•{›²¢Wæ‹7‡1/n‰ó¼›…4ø $²ÔÇU9µC_47ô)-V©èÈ©Ý꽩éÐI+»ŽÌ;!Eñ[ÄéA^1:U²k9Ãúo§©­[Œþ˜Îi³qkŒíãîW›Ùåÿgî,ÎÉp+ëª|e+@‘E~ž¦p¦W7v«pÙ‰F]»Â³ŽÅfͱBwÈ"N¾ì5¡¶¡yZ©ÐTÿÕTëj[‹è´0n#rÏ¥_O!äîU–NÏ<]ßC<Ô´ð——L®²›j ÇË/¨Vd“…œ®Ynd—[LzE‰ÈZ=S§¸´¤@[ щŸßðb™à}ô SC%¡/ßð8¨ÈQ«å²%Á¤­-‘¨^ßÿS¼UºZ–ñ#Þ›9Ǩ¥`4ÆŠ`n[t8ÇçfSëI<~'SmÊ­„Π…ÿªB¶jšÈŒ¡âG©´±YÖÓË6®ž¹.ö¼§RßäÜ!Tj–Þ•W{9X˜tÝŠÄdqYÞ—4æDtƒìº:ÕÐil)žËYlüÐ\[°Â[Ù¾àyWwîOCŸr Ü¢£#x‰Î\ˆ†—ýHÚUÁmÂÄp4­ÆN£^° qÖä(¼³Á,”hfŸ3€¢çëèiakÙˆû …ׯ&€11²¹ˆµt’æË M4 ­8ãÇHåiC/hߘþà'Á qä #xD!lkÙ\¨R‰RÏ1<ä za’­.ˆàžªs?µb ¥¿¶¸>H„ïlã—4SC‘G'»Ç,ã‰9ŸÛ»È±<Ç£&äUÉéV¾¹Å"9š…½ûôA´.š¯@ mÌš÷ÑÜRyCa±Ärºwº³p¿ÙÞðü|\(!ÚtÜdô¹Ÿ/›U— Ü®Õm‰aú3Œ9ÀhVÚìð&hÅîŽ15Ø/Çÿ»‘ Žk¨hŠŒ) DyÓ„ òOË+1È6[mT@^Ë([ùIØPn›!½ÇÎ{¿¨=Öåkè<úrò¯$ÈùFl[S•xàx«5÷ÑÞBWM­^­Ṳ̈Òqx`ŠD®ÎÐë3ÅQE,‰| ƒÙP¶Š5K ݱ)Ÿ ‹ïÐ,Þ˜¥H•Ò„ÚùÏV{k¨,Ÿ4L"s¥f¶yQ/ÔXöì¿y˜–-;ÏjýA‹Êî_UÛ–C§ÓD*‚ÅÎØÎ}$ñ—Û¼)ä[4ºû†=Z2ÖZVõ ©àÁüêñiØ‘yì«nA+.riÄ-·ÕYk÷qº_™»éÞÚø-Õ µò‚Z}Ü…®ô W rP)ÞŠsRø¼÷¾ú~v>ÇJlêѱ–ûÔc‰ kÀYOŽ r4Ïpr ‘È B¡•|^u¢~˜íD—îcÿz”ꜾpöϱÆ"ägº·ïl‹u[Yäòu½ø;Ê tEºD°Õ€F^s Üݸk’™ïÿ©½ÿ$4 uz,r@UÞˆ×äÔµñyg›´vúðçų~à&N¡¶@Ío-¸Ø»pfoNnޤ¤AŽÕ×h1W&ä aó˜wXsWŽ´±“ï,d?Xð¤´`ÄêfÍ.µgDSaá>Ú‡,$?êª->Ò”t.å©%³>[þHU{ð8Žþ ü(ž>Üä'ß' ·ònÞ÷„pès îýޏ€k1<ãV dí1Ñd~TŸ›/.FRÁ½Ã‡¢¬œwR8¥û)˜6µþ~LÓfQ‘Ñ…ïùaoMn^A`œèé”L4Lû››MAá$´ç(´÷ s‹j*Ïe¯nòÕ=9‹pSJùØDVtà jXvh3Eãz¢9Ͳ¿,Á‘¶Þ¼]ºrëàv]ãž7޹Ô/ûËTÓd‚¢–[‰Ü1,Àé¼á¢ï‘Ë«qoÖRFR ¥oh‰Áùd2T:¼Lq}ð¨ÿP½!:ÈJJuJ¡çÉ‡Ãæ_¾Û08ú$]aŠ^ ¹·› Që ~Ul¢ùïý5á€Þý€ D¢ŽÁÊv×|z¸¯Îî?ÙAãÀ(—¾JüÙàTðpf|aNi{qߪɸ'ld7õš4¨@´ÆŸùîžGáõá­ QÄ&[×3ª›(+‘}YeÞ”;˜Bd¶.®•ç(–Go€Û?¨è×ÛÍr‘·‚(1}˜ïÇÅ#É\ù¦ºÎôÓI‡:Sb¼½  P‘ÉÞÅàÞ¦™ñz=ÓöBfÄMÜ+zýàrÕ…ø[)wl0îå¦jÔÂf¯Ô¬LÁ» ²E¾Üg-­Å} œ°QDkÌâQ{ …©Ž$0ë!E­ŒÊÎÄ/'àG‡Íó~ãÈåLè!¹MTE5€$.=?xêÑÿ*‘ÒuuqSJ ¼ùNE,~mvLìÅad¯!ctÝâ›øêH°a2Ó×½l™Ç»`8ŽŠ& Êz“Ò"r3ÑÊÏ·ük8«?€Ú¦–Åå&×xc$þP7 ,ØÂÍÐÙªRgûýÒ$S'ºÕyõï=ãÔz¸†> ùÅí‡iŽ‚=Ä­ lQÐZmúËXG>¹Âã?êÊ›Ȉ¤†z¾òc=è„ðŽ,mÆž"®ÓN½Ç߇òID¨IŒTyª½œS¬™«i“1Œ £‹ :©ˆû¿RÍ;󱫖=W¸Z ó-’{b4B‚T¶÷ ÿÕu‘!oÑs8Ö0¸IËùÊ=šÞ{MT†k´{™"ÿŒ"5·Õ ¤ áhß½*âeŒ³ vP]æ¼áÒð hŒ§ò`­}¬D ǣƒ²ÝÚšût}ä=MòغžÅ°È¢*ŸØb¨iŽ?¬Â•‡žŒ ÜßR±Xʧôán2Žرtž|Nëe¤dc(ðɶ&—œÎ¬»d¥ÒµeÑjÓòJt±÷Ê“;9ƒHÁÆ\mRhܦþèxÙ¥©\¶A©ñ¼(e>fBc‚=äé. 2´Lݹ®mhÖOfðË‹æE½ëe¥³=.8ÄÿÄ(&vZ¨‘6´†™õÊåâõÌÙO9’JÒR†o©ƒÅ yŸ3µ–(±5»pëžu€›Ò†wÍÔ–&¾æ4óT>³ µºÒo>!‘8¦Ò6…¿^ö0À´.—M;Pኪ ‚ã.qÿ–ÔýƒÚÅ~¢Äã'mÇ -ŸTF“ÊIc…L9)R<àoí§ÏxÒ ZÉ[6†(µ—LÄvàCošwv½3~¯lJ3“‹Eœ,[‹h¨/ú·%œnޤY¦‰·ç—#Õ¹–ÍpìŽáýY© HXôÙ÷síŽåÍáàºÛF $g€R”ƒÜ;¹S0®ñ̘=ªvr…ß”l9,˜M‘v`D±_Z×q(ì³%8ª»J Ggbí;Û¤òÝ©—Ù2+’^ŒÐ2iË)tÁã½SAŠ”ð†£®°3{Æy¾ÆÚ²Ö:c´ìU+|À±ùÓoC©l= v~ qB„ÔU:^õJSŸ>Ø>AB7ª¨† ¨/—2=êoj\ÂPÁÃÞýFóëéq†"Tú*{8¨ŠÃyÔLlÌuiš´¡_#ÖËåõ(e*]+:Ö‘¡ùL²ò×:GAAý«›+5ªðЧ Ö„O¾ >ä°æðð ÖK¹C'RÀâfOs"•dk¨à(>%éâWø¨n`ßm›»ÙØ‚È19H«Æ´üÇ OHb~½Þžòä+kºYÞn1Uç>Ó¸Wc܂׉áú#þÏaŒÁý©¤ó¸q¡ö)çµӪKC‚û¢Çè`:Ç©•ДžµÎB®Š]R:· }i}1pî4k3Ìÿ¿hðÚzËÛJZº®8Að…Ÿ ‘ X$ŠÙþÆóÀÕ*ð1œOë4µ”ÇŠ’aÜÎOxÿ¹áŒ;L³ËÃE1¿¯šf¢ßb7S¾3B™å°_‡ в…,ä««o-—¬Cy·f2=Å%ó `ƒSÞÿzèü÷| oŸÌSèëÚKo#ߨsåÊvPç!1m3Cb ñLÔnzEšV¾³¹Z0ø{Q$ÑUÛ§Íù8Èõ"ì&›¹eʰ0Gß©jBülºò$[7prQ«5ìjØA9¸c[úØNÓšW¯ ˆÀ‚Ê—M?/;b†Î‘Y=VQˆÿÙ_û¶}Lœš=-ÐzÈt *îÞÇ@9ú×dcÆ2ö&.<Ù:¿E ôÀž–‡Îv}¦%9vþ´Uí²7”Z޽G«#ˆHp\8‡öÅÛ»Ø@‚H ¼£Í'Oä *$^ðÖACK8å¦5"·Ð°GD³ëbA|ƒÙƒbÉ \ qE"A4¾LÊ#%Ü6)C”Á6óÀk²}®¿Œÿ½:šÍ;eÅ€†ˆãT ä -Èö¼d…oxÿw[e—‹5q*»P9QIý)ú2ÐŽ…‡{8ȬK$a„z?í4vƒ 7çiÞ³¨ÇI>“n½k×…»Y“ßW%V)àØ<ñ€hæÂ*ÿ¯QZû…xltJr%2˜ ?¾ —ÙÞ|b8©pÚ;²°kÔ»I³ ?Á1ÇžŽEtešUøRó¯·_…üs-¦­ŸŠ“L4`qÆbŠF<‚`]mxQø!–¥nV·îÁÃ…W{KæµâЦÉü¡L‰®m´iLý¬KKl*ãÚÆFŵá€\aú€úŠìoìp+Rîñt=þ9MÚ¿g)O¿‚½§^ D¿³É§(7¦iÊ=¦AÂ"bå&¢`¯Ak¬3‚Ä?ˆ^vO¾0ÏõÒ…dœ>ÿR æHîåËi~ãYŸ×ßR&`6êb„÷çOiKY1G¸¶ï^? qûuîAäwÇ2«-—ÈTÃG”Bh\#[—˜o2[S¿2Ç¢¾²”%mAHÃÕø–ëº[LY-ˆxï÷p®'“e÷øô(‘{(fhÔB°ó5 ég©1ßêíç½·ºs#iVœ‡.4î€9KWôp|]*åRÙKýGõ–=žýः_m:.gâÞªæÒë å®:éöÔp±upw[ÛGq“›Íž/d{Ü•¼p9†—”.rGU-û»©÷k†fM/ŽRâ}R¢#¿yÖy»˜š4-•£ÌÌ—‚ÞÁ̳…$²ðRF=ß<’_À÷á0ðÆÝ +ÖËcÒñ›{ð?Æ-Ýz›‰5ˆÂÜ´˜DÔýÊ´?IÿËQ€¦½MmqDSKïá× {Gßóóð.DW:Þˆ k7—eñ#ì‚ø@쳎8õ4f ‡mJ‚­þmQÍϧa¿/ÕLºýȃҵTáûH“Û¢ª²äaê­Y ·„~*È~‰r±ñ"³Ó’ýÖ#+çÕi¢€ª‰%q9Qà ]kà40„XîÏb×êÅÃÀK}³íþ ÝPé†|¤æ œH4Iþd;´e^=ŸÛÔ; é¯lJ¸šD½*#n©ås÷¢O¡,ýÐÚežµ[ ž[âgZn%9Aíý‘^HEY‡™4µÑ€Ý—øg; H›ù½ÓpYP§økõ-:çù“Çed‡¨6áL0΀aMwML·<:!³&_« °µĺBèëÅ´ #lÑäUsK:>WÔ>_ï#{¡¦qLÿDI’E·Ó{w¶ï»; ¡xpkŒ0ød˜‰ï– ܲp×︊ÆdëR‚¡TA^ tƒfR1dÓE+¨–¨f×$"ãÌG‹52êÌi£+'8ËO‹Eä• š˜•5^t`”h_>¿ŠÐX€{S¾Uˆ®,"ÁŠJû—.÷à1ö®I“ÅìÇ^qÌaAäc½Mñ2[ø.„ôŬ\Õz¯4U>Ïho6åäKÈóúˆUÞ >Œ'bcîZêzàÍ,py„pä´,> ­öIQ˜þÁùuC[ŠÙ่á›gÙ|Ì o°ôÉ‹çcÍB3½õrmà‡—±4Ñp^ìx[$£û̓ÈdRãq§ÛÕ(ÚÔñVÛÌúïñŽŒgwouÔ.>I›Æ Òº4† ^†j qR‹Y)Òò¹@0"ÙawvFºliç ‚³Ê[± ¼áaIL’3ìÑq°U¯-ÖŽ?ŸñÂðÍ”Z0èu4fl8ÖLÁדLþ°º4È :wÿ –©ˆ«\úWå›Ï(oQèêe’~^’v $ Œùÿáè©ü’ìeúË÷²Ï:ÝÞ†ejlû¾£ŠRQ‘9²Wô<ž¸‹„ꘔç4ñ ܰ ªÜÞ óJ€ßw•×ÀYqçmâŽ4;õLnÿÃÛ8Üáô6dM·(Wú ¡[ü ÈŒxÙ(“t‡ m-¥ò€0Ó“‘Säþž)‚%SÙ 'õC°[ ,^©%Ã!Œdì®E9¢œ™„e7‡XÔÍEµªñ Ÿߠ/ªü˜hYmIRGX)h(rn$wxçðVÛéôÔk|Iõò­³ò«èº¼âŒäµQj‹ückDí®×CeójLE¡¨Š¯ÐH¤üƾ™œwCÞøÓ*vb]:å7€ 'uÞ…§§ÞêfÝN©ôS%zÖù„u…Q=çéËÜbîBn=ÒCúh¨‰V­îì„cêÛ¾jŒlíº¬{1ø§ÎòLÅ] Îï3ëüîáŒÜÄ¡D,a©có™®Fç ¢Ï¸ùÞŽã•›]àdd¤œ°`ñFx×@ôÚ¶++¼¢ÓÓ`FÅqfâÉm¹ôö1w²6âžÉN @í¤N¹xÈl‚}|€ôF֡Ήù'}8(üñ̈lÓÆ‘©ôq×÷B>cý?¤~ÕXÚæwžîˆrR¤×!"ÔóazF&9ì{ùD~ŒŒ/&‰McÂxÚ§j_ZðÀë6¦béD`ÑVjcþm«•s²Ÿ*·kÙÉ´>ã!èÏ·Áœp†ëþRX‰'ÉšÞ%h–êa¥VN§ã½ÇF I!î·Ç:Ø•aÐZr:ª£Ñ‡q–z3Ì~ݺż­¥Þ×ï:¶+O!Hœ­ M ’@U)î“/Tqò÷ùnŽBqÔº^Éèü¨a¬“a‹Öãy„±Ãƒ¸Ñ´8Ö©òþ×劥O|6s¯9D~q?Ö¢e²T:ñD€Ö„O·Õ0*Ö"*œÒ‰ïäç·ô>ј>Xœs3Ãì‹;8×W»Ïì6Ç+‰»©þšàr>=ZrÍ„" *oµ$#ÍŠÒiƒUîÌMcVì‡C.ê¾ïh_óÚ\§I•¨É] Bv »+±|ñ&£ Ýs9ä솿ù Gñ·ÌÖ†«?‚xô_‘»–Ï }ï³F~dKí£.®„Ýúec¼7ùÈ3ÃMÀCü6ż)`ÈxVíÿÏÙNP=ÑBbaTÓE’˜ÜJŠTþ¨ŒÚ77ˆ­”-?{cš™ÚÿfÎ Ôg¦r ¼? ûª cÁ±³- mÒî³ãßÚÈ` ŽD)õº½(!‘ºíÓ9»2ø"å@Ÿ|y£Ê¯úèmU¦´Z—¾CzCJ.©äkYÝRÑ‘i×Ì ǬڳD_,bÚê*°ÐÏ…d½·IæÄ·®¾y!áà}ʺ°Áe…ðL|Ü9}ìÆz‹(¥ç×ä4>…–ðÌÍS›Ð‚½uûÇ —SÁ3”©èm:`ÇIëûRNô€Á.¢›“¸ío 4™Ie?ÑÁÛm…¨?TUnúN]úÔt[‚3:Z{?whZè~èr0=¥Ýçõ²c`+¡`ÐØ4ÜÜÞå7Ø}&¤1RÒm2˜¶5X‹ Þw¢}å|WU×#0èr/5ßV¥¤¥b™–Þ‹(ÖŽNZVfÃ8J§{sLrk#ÒÂÛ…KÃiÁ/½ð¡Ô™~öØSª ’(ñ/£¸ƒ×RÜÕ@ÕäUŒ{{n͈۬üÈ"žI ùf§¾?ÛÌvþ±·ížb𳙯‡_×qÆÂ-´áÛÃÅ´BFÇmª|™l›wh qX0¸™Ç ];®f³ NÆ3òžóÙ3~ J¯âË=9Ò8²¶0Ö4{˜šß& ²…s©;]“ë©»˜äªí41)ÓŒꛊj%Þ¶ühù!Ôÿ–hÁ( éÔ?W©ªÚƒ~* ªœç>ûvö¯Ï±\t•Îô¼29R°n{LqáÚÿhØcßQu¯ ÞÙŸ $Í­IJ(Ð[;A|[Ñ úgØÒI[ô©"ÇPsXÈ3@/…ýÒ[ÙÈ®3 Ü~SŒ}öUJ+ägÀ%qìÖ}ó#ùÆ€`ëØÍÑénUÒÐà³Þh 2wêCR]*¼gåž'¤oÕ窶‡âë&¯;äÙZ…¶Øý`cƒG.öeP¶¯_ô‚P¬@ϦwJ%‹½/-cæÏЇÊG¥0Ï;–ÙsŠ#\Ïï ûb±zK5/\[Ÿ oIàUu¸‘¨ašÄ¶WÑíbƒì”IÿR+tJ©ÚúÿòÎ š·³EÖùý°î͆+Ò]f¯9jšýv­~ú¿1ü|/"¥L¾[ľ¹&¨ÎqÍlÈžÃ}õï;ÑÞQ[h8Âàúÿ,§˜ya·5TL~ªw*ÀÞÐd団Š@ÁNù]ñÞÙ1ò u)–,#}°kU0Ôùc~áÒÞ¢4“¡ÍÓßßœíî1|єۨÖôAáIçð_Q4d}G²Y㯮UÂï¾BÌɨÝO–…Æn,^€Ûñ\š¦[®ËŠÓH®p:Á±,ë>MHmoËçß/Õ6¯ðtYñ„]C™U‡!n峯wÛÆ—A°Öå\)0•À×â4®LqtÔé›Öô&SGcì-aßÔÌ…%Þd•#¾©÷kã¾;WÓ_s‹ü£h\‡»ÄK°JÉh؇ËÓgþžTá§r–]@¤Uš÷ðÊH°+£ðÜà²Y%Ý|)”ÓЯUaühì´~&­s:%IØÀØAH'ôãÊòl4¿Ü”œZ¢¹5€l_Ç tÿ'ìљҳAE&0ã8¿«Ãè.šèávMIe2Ñp Ó܇ürFPuQ/Cümz§jÇ2¶v3 zÉ×µ;§­;Þ;l÷{7iÃF¬TB²yóÝðCªïŒÂä´ HÒÕÉ!¿ü¤š*jÅgDJ]<}©¹ÔÓ‹\ºy[”.‡$O W:!'À´ä@ç?À`^³Ǩ¬ÈC°;³êÀ-{s.òHþí ¿1P"æs‚Í 8u£wPÒ_,B5¢œú|e~¹±Å$ë·yn,×gqæÒq´ 2ã©x$RPŸÉ§">Ë]±Ñ ZêV2¼J&è¢Wvä~WÌc䊖:-B$ªÒŸ¥íô_á­¾[©.€*´ÐÅHfº¯ˆcÚS¥¥tzèܽ]z\¯!k ާ)Ýu¸†²gfžMBµÄi˜|pÖ«“ózÓõ4÷§-çi­³PÚíqµ‘ÿÂÂ犎t¥oÜÀ{¯kÀ™^0ö½~†×ÂÜàûªxY§#›A ÷á´KZ5Dµ;kw·G"5j•^Qƒ“Á ã6ïŸKw¹=”eÔçƒÌ?ÎÉOŒÀäiå‹Âƒó]YD×r+ÿåW:à¾Ã¾òÎw€Í߯ÿe¨–Ù""^ï¥ã4î¾óófŽŽve0c5Ä–È‹»57•ÚÜÍŽ§ùL×ïÌ=›:¬{ŒJ–Í’‡«ÌgOG¢è‹"¹I2Üj]tûÎì].X‰ûø x©èTÃTWóª˜sŒ±R»8±f_&V¦§LLöEÞº£Î^;—·aÉÞä2˜ÌgñA¼?×Ì¡ÂQÅk’j#¥K·!ïó¦³áºDâµË¡Èž@å½Öé#•gMí=Òè'\  .Ï'>“´Kx]jæ/ÂÌG3î€ÜO1êˆ7ÙgßB(«âj‡Ê£=ääøqªlÂ…)Ÿ®Ñ¡ŸfÈnþn‰¢Yó¨sëê+f6·&¦Gt»Q|n¡Š0±U·³˜«|Izо6?Òt“F«Q¾:ÚKžê%Y¶þú¤’ªï.åPe#‰R×ÏrÑß èoòEÕ‰ï “ /ȽŪ›BIŸ,M¿À ”éÑckÃ(4Ç´'yŽøG—ùâPnÙO™aÅ—‰ÅX‹èÎ…Éaõ\Õæ@º À‡’P[®°b¦ó) xk>ÌZ0°Ð‘¾žrGÅFV[‹ø²ªãÒ-[}PÞµl]UB µþ©_åm®×9 ¿JxE©ñ›q,qÐHp—5€“Ò¾}‰’O¤(I‹b7ãâyËnÛW?‡ø9(Jê£óô`ÿ üÝé}…ñ„‚±-ÓnMù²èÆö•Þ Qzç¸ðPÜ/½ÞÛìâ!Ïž ÷…bø¬/בF›¼Á3uõ‹&ZgÂ7šË O4‰´JVPó“«n„j ™¢c°sªÝáI‹+‚ÿƧg’¨[¹á›¡UÀO+ß•”“lÐE3šÈ­öýEÊ£D}†Kú‰:lkì–\}¬Š,±Ío¯„^®¼sxlôï?‘ˆ)y¦Ð!þ.ÕÔÃ|û-)Ø…i4z(]é˜.3ßq§?ê™h%¿þBªEk¦§‚°)w°2è·.½‘”O‘­‘“‡ì³66“*Ð `Û¥8 P l1"ë«‘ë·ˆºûÆâåpJi¶}ñÇC$AŽkô´ÓðKv»XÛ y…C‘1…ßoÚ²ÝCF4ÈåˆK%è‘LVÿo¼³œ/Ë·J¡F¿†™‡*H3)«¥ÊÿQ·»d‰PÒc8ˆûª½³cÞKëàzŸi“è_°çˆ©½m-Û˜“ÇìÍ3«V”U5;/ç2ŠP™s£`µØõ²lNJÉ‹øÉOYF³cÆg‡à9µô¦€° JÃù±ÄgûYZrelic-7.6.1/functest/testconf.yml000066400000000000000000000015501455105530300170300ustar00rootroot00000000000000--- tokens: softhsm: provider: /usr/lib64/pkcs11/libsofthsm2.so label: functest pin: 123456 keys: rsa2048: token: softhsm label: rsa2048 pgpcertificate: ./testkeys/rsa2048.pgp x509certificate: ./testkeys/rsa2048.crt roles: ['client'] root: token: softhsm label: root x509certificate: ./scratch/signed/root.crt inter: token: softhsm label: inter x509certificate: ./scratch/signed/inter.crt leaf: token: softhsm label: leaf x509certificate: ./scratch/signed/leaf.crt server: listen: ":6363" keyfile: ./testkeys/server.key certfile: ./testkeys/server.crt remote: url: https://localhost:6363 keyfile: ./testkeys/client.pem certfile: ./testkeys/client.pem cacert: ./testkeys/server.crt clients: 426886bcf5dedbd73f78477d5151738e39c245c27c3cae792503592ae4417c59: roles: ['client'] relic-7.6.1/functest/testkeys/000077500000000000000000000000001455105530300163325ustar00rootroot00000000000000relic-7.6.1/functest/testkeys/RPM-GPG-KEY-fedora-25-i386000066400000000000000000000032061455105530300221260ustar00rootroot00000000000000-----BEGIN PGP PUBLIC KEY BLOCK----- Version: GnuPG v1 mQINBFb9YzMBEACy1RmbMa6MNIpfHYxLwgCgBVnFYCdCHZqWfYYYK14potfJ9uI2 4Y4w+oHiLeZ/HoG1EBQiDfXHetGZECAKEYQlE7BbRBcd3An9GalKTkWzcshhHFx7 f5JIprL0uY8x2D9HmCfAjMxoh6usWjmAQ+DUYd48iYCkahyZa0/2CgX9HIcEz/M/ oDeQbTwzw9AQbQz382oOErfRaXE/DQrjlx2ln0iejidiOe7DzGZOH9/Foc2KN062 A9VnZ7tU1ACKT8NxZ78RaBL3qmvMGdb7kf7GywjpRNo4J7XCQUP+nP51eCur2wMS 4mY2idDL8Ojouta79pPrviVLmwzunJoFnBcnIhbndebdxPqgOA5XAOaTdLtgurMq 90V45DPyJpkdEyptovksH7zYNGEIGB8cFmrVgUwriB0TLNJTEcM4Knbh4imfTX42 vCE+rEHn3YVqubG7rggibKznJbflwQcqOYZHLlPGYCxO47aaFUo5qJN7QN3lxajb SzL/SdoHrVL67unzmHyktx5uF8Fv6EDgUV6NCb/IBiEwhR8YHi86NQ8nsI3K8Zhv EnIxghJQD+cn3ykthwqYmZwi2PJDBiZsOGf3iXbalAjU3JVqoA7mboRPR+IBXQxK xvAEpyIGeSUN8yBn+JVDRwZ37kkUVs2AOeUwMlnfFSqYFfmqbeQ73A9ECwARAQAB tDxGZWRvcmEgMjUgUHJpbWFyeSAoMjUpIDxmZWRvcmEtMjUtcHJpbWFyeUBmZWRv cmFwcm9qZWN0Lm9yZz6JAjgEEwECACIFAlb9YzMCGw8GCwkIBwMCBhUIAgkKCwQW AgMBAh4BAheAAAoJEECJ2PL9sZyY1TEP/0u/v4g8HEdl9gqlhV179vXCJJiGtzB0 7IGAu++mrsxBrDpqPZTEs6dG5MyzvhhHcmHYrZIiicPAeL9xlZ75oIqQuvjDncoM kROSGvtfUnvocZhQIPvvkgWe3UAmmP3cSlVzu3KtbTpM+KL71incWo4Tentq9L/f vsow7vvGbKUMoSSZbAMfjJkzlzSDNlFtaRkrCBQFJ76EKeggjnEZ8H0cowCdGuyv uBoxQeeQM13b2T9c/uyrXCIcasaOTIKTcqTjbJUTIC2NIZ8OHjtlxZacEaN3ml1M lNRtbIvqzbtv+sb+DsOVTyd1XIcxU9s+TDKvUm0OBNvj3Bm2BQbi8RHyLFbHWvhx Gjzb8Wb/MnlcdTlk3M2iPv8dWHXjEM9n9TKyStdpBD9X3P/Gy2gUquHgkl8p+r8o xmzNH534mKH47kPL/trKInKwv0fkBwxvuPgHG0n79eMHQenVA8gXzG4P6JkcyObA 6xGEEQ/wXFF0gLksmwFWuPm2GcnOI5KmGNgDP2PMhS8/cfJfW04a/tL2T2zr4CmE LynbOvY/yOJk7/2W3Cb47+yhqo/htrpJDP6n6zQNNk3+e8EVgfhkQqFxom8yCmEP pW0gBFeE83VoytYPXRkavwmFR+tplyZfOkXG9gysTn8SpRp5+B44O+VeaZumanQZ kRFmBygMR6M/ =NrXo -----END PGP PUBLIC KEY BLOCK----- relic-7.6.1/functest/testkeys/client.pem000066400000000000000000000013611455105530300203140ustar00rootroot00000000000000-----BEGIN EC PRIVATE KEY----- MHcCAQEEII5Bonav90GQoH9oIYzCwCzSb17HLwWgV6FvY9s6r6OioAoGCCqGSM49 AwEHoUQDQgAEg7wgNyhX1+WpW4G08pRWogyFPjRQwvwXYuv1XcSl8BdNTF94RLLT CCcPJPAqSIimie2eh7LBfvBL+HpyrRP/ig== -----END EC PRIVATE KEY----- -----BEGIN CERTIFICATE----- MIIBVjCB/qADAgECAgxAwYqFabHk96+ieNYwCgYIKoZIzj0EAwIwLTErMCkGA1UE AxMic2VsZi1zaWduZWQgY2VydCBmb3IgbWl0aGFyLWZlZG9yYTAgFw0xNzAzMjYy MTA3NTVaGA8yMTE3MDMyODIxMDc1NVowLTErMCkGA1UEAxMic2VsZi1zaWduZWQg Y2VydCBmb3IgbWl0aGFyLWZlZG9yYTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IA BIO8IDcoV9flqVuBtPKUVqIMhT40UML8F2Lr9V3EpfAXTUxfeESy0wgnDyTwKkiI pontnoeywX7wS/h6cq0T/4qjAjAAMAoGCCqGSM49BAMCA0cAMEQCIF3O5QQFfGul FlsCwyBjLl35RoAnE6PyoxPCJTaCJp0TAiADm8eMClyK75N72NQdE2ilb0CFyJ7E YvF20i2xFdRUKg== -----END CERTIFICATE----- relic-7.6.1/functest/testkeys/msroot.crt000066400000000000000000000041311455105530300203660ustar00rootroot00000000000000subject=/DC=com/DC=microsoft/CN=Microsoft Root Certificate Authority issuer=/DC=com/DC=microsoft/CN=Microsoft Root Certificate Authority -----BEGIN CERTIFICATE----- MIIFmTCCA4GgAwIBAgIQea0WoUqgpa1Mc1j0BxMuZTANBgkqhkiG9w0BAQUFADBf MRMwEQYKCZImiZPyLGQBGRYDY29tMRkwFwYKCZImiZPyLGQBGRYJbWljcm9zb2Z0 MS0wKwYDVQQDEyRNaWNyb3NvZnQgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkw HhcNMDEwNTA5MjMxOTIyWhcNMjEwNTA5MjMyODEzWjBfMRMwEQYKCZImiZPyLGQB GRYDY29tMRkwFwYKCZImiZPyLGQBGRYJbWljcm9zb2Z0MS0wKwYDVQQDEyRNaWNy b3NvZnQgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEB AQUAA4ICDwAwggIKAoICAQDzXfqAZ9Rap6kMLJAg0DUIPHWEzbcHiZyJ2t7Ow2D6 kWhanpRxKRh2fMLgyCV2lA5Y+gQ0Nubfr/eAuulYCyuT5Z0F43cikfc0ZDwikR1e 4QmQvBT+/HVYGeF5tweSo66IWQjYnwfKA1j8aCltMtfSqMtL/OELSDJP5uu4rU/k XG8TlJnbldV126gat5SRtHdb9UgMj2p5fRRwBH1tr5D12nDYR7e/my9s5wW34RFg rHmRFHzF1qbk4X7Vw37lktI8ALU2gt554W3ztW74nzPJy1J9c5g224uha6KVl5uj 3sJNJv8GlmclBsjnrOTuEjOVMZnINQhONMp5U9W1vmMyWUA2wKVOBE0921sHM+RY v+8/U2TYQlk1V/0PRXwkBE2e1jh0EZcikM5oRHSSb9VLb7CG48c2QqDQ/MHAWvmj YbkwR3GWChawkcBCle8Qfyhq4yofseTNAz93cQTHIPxJDx1FiKTXy36IrY4t7EXb xFEEySr87IaemhGXW97OU4jm4rf9rJXCKEDb7wSQ34EzOdmyRaUjhwalVYkxuwYt YA5BGH0fLrWXyxHrFdUkpZTvFRSJ/Utz+jJb/NEzAPlZYnAHMuouq0Ate8rdIWcb MJmPFqojqEHRsG4RmzbE3kB0nOFYZcFgHnpbOMiPuwQmfNQWQOW2a2yqhv0Av87B NQIDAQABo1EwTzALBgNVHQ8EBAMCAcYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4E FgQUDqyCYEBWJ5flJRP8KuEKU5VZ5KQwEAYJKwYBBAGCNxUBBAMCAQAwDQYJKoZI hvcNAQEFBQADggIBAMURTQM6YN1dUhF3j7K7NsiyBb+0t6jYIJ1cEwO2HCL6BhM1 tshj1JpHbyZX0lXxBLEmX9apUGigvNK4bszD6azfGc14rFl0rGY0NsQbPmw4TDMO MBINoyb+UVMA/69aToQNDx/kbQUuToVLjWwzb1TSZKu/UK99ejmgN+1jAw/8EwbO FjbUVDuVG1FiOuVNF9QFOZKaJ6hbqr3su77jIIlgcWxWs6UT0G0OI36VA+1oPfLY Y7hrTbboMLXhypRL96KqXZkwsj2nwlFsKCABJCcrSwC3nRFrcL6yEIK8DJto0I07 JIeqmShynTNfWZC99d6TnjpiWjQ54ohVHbkGsMGJay3XacMZEjaE0Mmg2v8vaXiy 5Xra69cMwPe9Yxe4ORM4ojZbe/KFVmodZGLBOOKqv1FmopT1EpxmIhBr8rcwki3y KfA9OxRDaKLxnCk3y844ICVtfGfzfiQSJAMIgUfspZ6X9RjXz7vV73aW7/3O21ad laBC+ZdY4dcxItNfWeY+biIA6kOEtiXb2fMIVmjAZGsdfOy2k6JiV24u2OdYj8Qx SSbd3ik1h/UwcXBbFDxpvYkSfesuo/7Yf56CWlIKK8FDK9kwiJ/IEPuJjeahhXUz fmye23MTZGJppS99ypZtn/gETTCSPW4hFCHJPeDD/YprnUr90aGdmUN3P7Da -----END CERTIFICATE----- relic-7.6.1/functest/testkeys/ralph.crt000066400000000000000000000104721455105530300201560ustar00rootroot00000000000000Certificate: Data: Version: 3 (0x2) Serial Number: a2:de:dc:ac:40:5d:70:34 Signature Algorithm: sha256WithRSAEncryption Issuer: CN=ralph/serialNumber=1234, C=CC, L=ralph, ST=ralph/street=ralph, O=ralph, OU=ralph/title=ralph/postalCode=ralph Validity Not Before: Mar 11 22:37:26 2017 GMT Not After : Mar 11 22:37:26 2117 GMT Subject: CN=ralph/serialNumber=1234, C=CC, L=ralph, ST=ralph/street=ralph, O=ralph, OU=ralph/title=ralph/postalCode=ralph Subject Public Key Info: Public Key Algorithm: rsaEncryption Public-Key: (2048 bit) Modulus: 00:a0:81:59:33:d2:25:de:c8:2e:be:ae:85:06:5b: 4c:bc:91:ca:92:b4:a4:b6:72:2a:48:12:83:ee:43: 63:16:5b:e9:ca:54:8b:5f:75:91:24:2e:41:93:21: fe:07:55:21:03:ec:b5:0b:f2:52:24:89:cf:91:93: 41:ee:b2:05:0c:0c:c9:c7:48:d1:a6:19:f4:45:08: c4:b2:cd:cc:a6:91:07:cc:a2:3d:f0:04:1b:6b:6b: 3a:6a:28:9e:ae:91:3b:04:9e:82:4d:66:d7:ca:e2: 03:77:28:4d:91:dc:87:8e:e5:4f:ff:22:fa:04:9d: 25:51:ca:21:a3:21:8a:90:46:13:fe:52:23:dc:10: 31:81:d4:ec:a7:fb:15:ad:23:15:af:7e:6d:10:94: 49:8b:2e:3a:88:63:06:51:77:31:2a:4f:ee:a8:e5: 50:61:ce:f3:b7:1e:5d:f5:33:cf:54:39:07:00:58: 8f:e3:ea:9c:e1:df:e4:25:08:6e:01:dc:42:35:a1: 6c:af:e2:06:0b:b2:8a:41:a1:e2:f4:5e:25:3e:32: 43:44:b8:6a:0b:12:96:a1:31:95:cb:ef:1c:ae:53: 0c:9f:f2:35:37:78:56:bd:c8:db:63:fe:2d:bc:3a: a8:b6:35:b2:88:4c:a6:a8:e0:02:b7:b3:49:70:9a: 91:3d Exponent: 65537 (0x10001) X509v3 extensions: X509v3 Basic Constraints: CA:FALSE X509v3 Extended Key Usage: critical Code Signing Signature Algorithm: sha256WithRSAEncryption 91:03:4d:b7:2c:4e:15:f7:e0:14:5a:7f:2a:de:d6:e8:6d:f7: 50:19:23:ad:e2:b9:0e:5b:04:3b:2f:5a:7a:db:33:88:5d:7b: b4:16:5a:4a:6d:e9:22:d2:8f:72:4c:29:81:cb:cd:19:aa:d7: e5:bc:e2:38:85:cf:f3:52:1c:51:ad:8c:c9:c6:9c:7d:f4:1a: 9d:bc:45:b1:1e:46:3a:b1:48:2b:c5:06:f1:73:b9:72:69:2e: 11:74:f1:33:66:3b:f1:91:3b:38:d2:a6:2f:65:a8:73:35:72: 0d:7b:c5:43:bc:f8:ff:0f:00:68:b7:88:73:8b:eb:81:c3:ae: d8:e6:7a:63:ca:21:88:77:a9:36:b2:8e:db:8e:64:86:d6:9e: ae:24:24:ab:d2:90:b3:7e:e8:30:1c:72:e8:5b:2e:33:91:04: 62:34:14:f9:d7:9f:75:3c:bb:5b:ab:a5:12:98:46:5f:b8:b6: 66:17:87:92:fa:16:70:96:2b:a0:0f:08:ec:f1:bd:24:c3:8e: 2b:3d:57:ea:83:92:4c:67:6b:18:dc:0a:af:b6:67:8a:17:ed: ba:2c:31:59:e8:2a:20:2c:f2:90:1d:58:af:d4:79:d7:3f:aa: e5:94:1f:f8:0f:86:52:a5:6a:3c:e7:4d:a5:cf:26:54:35:cb: 78:c2:68:b7 -----BEGIN CERTIFICATE----- MIID5DCCAsygAwIBAgIJAKLe3KxAXXA0MA0GCSqGSIb3DQEBCwUAMIGcMQ4wDAYD VQQDDAVyYWxwaDENMAsGA1UEBRMEMTIzNDELMAkGA1UEBhMCQ0MxDjAMBgNVBAcM BXJhbHBoMQ4wDAYDVQQIDAVyYWxwaDEOMAwGA1UECQwFcmFscGgxDjAMBgNVBAoM BXJhbHBoMQ4wDAYDVQQLDAVyYWxwaDEOMAwGA1UEDAwFcmFscGgxDjAMBgNVBBEM BXJhbHBoMCAXDTE3MDMxMTIyMzcyNloYDzIxMTcwMzExMjIzNzI2WjCBnDEOMAwG A1UEAwwFcmFscGgxDTALBgNVBAUTBDEyMzQxCzAJBgNVBAYTAkNDMQ4wDAYDVQQH DAVyYWxwaDEOMAwGA1UECAwFcmFscGgxDjAMBgNVBAkMBXJhbHBoMQ4wDAYDVQQK DAVyYWxwaDEOMAwGA1UECwwFcmFscGgxDjAMBgNVBAwMBXJhbHBoMQ4wDAYDVQQR DAVyYWxwaDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKCBWTPSJd7I Lr6uhQZbTLyRypK0pLZyKkgSg+5DYxZb6cpUi191kSQuQZMh/gdVIQPstQvyUiSJ z5GTQe6yBQwMycdI0aYZ9EUIxLLNzKaRB8yiPfAEG2trOmoonq6ROwSegk1m18ri A3coTZHch47lT/8i+gSdJVHKIaMhipBGE/5SI9wQMYHU7Kf7Fa0jFa9+bRCUSYsu OohjBlF3MSpP7qjlUGHO87ceXfUzz1Q5BwBYj+PqnOHf5CUIbgHcQjWhbK/iBguy ikGh4vReJT4yQ0S4agsSlqExlcvvHK5TDJ/yNTd4Vr3I22P+Lbw6qLY1sohMpqjg ArezSXCakT0CAwEAAaMlMCMwCQYDVR0TBAIwADAWBgNVHSUBAf8EDDAKBggrBgEF BQcDAzANBgkqhkiG9w0BAQsFAAOCAQEAkQNNtyxOFffgFFp/Kt7W6G33UBkjreK5 DlsEOy9aetsziF17tBZaSm3pItKPckwpgcvNGarX5bziOIXP81IcUa2MycacffQa nbxFsR5GOrFIK8UG8XO5cmkuEXTxM2Y78ZE7ONKmL2WoczVyDXvFQ7z4/w8AaLeI c4vrgcOu2OZ6Y8ohiHepNrKO245khtaeriQkq9KQs37oMBxy6FsuM5EEYjQU+def dTy7W6ulEphGX7i2ZheHkvoWcJYroA8I7PG9JMOOKz1X6oOSTGdrGNwKr7Znihft uiwxWegqICzykB1Yr9R51z+q5ZQf+A+GUqVqPOdNpc8mVDXLeMJotw== -----END CERTIFICATE----- relic-7.6.1/functest/testkeys/rsa2048.crt000066400000000000000000000021421455105530300201460ustar00rootroot00000000000000-----BEGIN CERTIFICATE----- MIIC3DCCAcSgAwIBAgINALb1U12rz2uVLlMaQDANBgkqhkiG9w0BAQsFADASMRAw DgYDVQQDEwdyc2EyMDQ4MCAXDTE3MDMyNjIwNTUwMloYDzIxMTcwMzI4MjA1NTAy WjASMRAwDgYDVQQDEwdyc2EyMDQ4MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB CgKCAQEAz9g2yUawKYGKq3oYugqBJaOVUvF+7SQnNRCThEnaCfdBL+w1liagyXL0 nijNsk9l4AIyTn77AOFowgt5wmP6AYilZdfACDN0Cq0WsEtyrLNzLadcT3p/CUki xN95QXUG2xMntRawLahdqZo3JntW6ky7OKXwJWURaBafzhWaRQ6X63Ew3wNKaBl5 +XnA/0rrpqXObFzt3RwF3jAkXVpn5bmymXPw1jKMwb1HzCtF6ZBlqq7y3afxbN+a 45JqIHuPDPhHz/SxyRfMwy1icrsLX+XYcXOSFMu5fUREQI0ASvyaf45x87poyrhh W1R6hqXyzysKrpBx8wP/4swjBwWA7wIDAQABoy8wLTAMBgNVHRMBAf8EAjAAMB0G A1UdDgQWBBShm5sPlp4cWRA2/jjMqE2XEtpShjANBgkqhkiG9w0BAQsFAAOCAQEA YbkR4O1fnTyh3TncJEjU6gnMmoIpPJzChLsh6ZmeYwJNSmSk5dWfACC/8IDqY0k5 9vu4/Xeu9fkGS5Yf001jxGo5p7P4/SxnBra6Cp/okf0C5cUFnrAP/ydjcTslTyP5 PSUqZYrMfwqhExZVLp71qRfO442aMOqWLpOMNi6i8pkQLM4+q2RPW/GuAEPz+lBy aQ3xyAUb7YFiZ87EoHGC1VQiTfcXwTNYLVrdS7hL8euA9rUJt/9c8CRIVeif5g8m C2B+PtHlSY5MzxH7g+TFY1CJ6/iNs3W/+InNQg/FhHvXCYWOskRPWRnMvy2YiTML OkQq2VK4Zu1GQeXrqhQsuA== -----END CERTIFICATE----- CKA_ID: 0c:44:1a:fe:53:f5:fc:e7:f5:1e:77:ac:11:09:97:a2:9f:51:a1:2f relic-7.6.1/functest/testkeys/rsa2048.key000066400000000000000000000032131455105530300201460ustar00rootroot00000000000000-----BEGIN RSA PRIVATE KEY----- MIIEowIBAAKCAQEAz9g2yUawKYGKq3oYugqBJaOVUvF+7SQnNRCThEnaCfdBL+w1 liagyXL0nijNsk9l4AIyTn77AOFowgt5wmP6AYilZdfACDN0Cq0WsEtyrLNzLadc T3p/CUkixN95QXUG2xMntRawLahdqZo3JntW6ky7OKXwJWURaBafzhWaRQ6X63Ew 3wNKaBl5+XnA/0rrpqXObFzt3RwF3jAkXVpn5bmymXPw1jKMwb1HzCtF6ZBlqq7y 3afxbN+a45JqIHuPDPhHz/SxyRfMwy1icrsLX+XYcXOSFMu5fUREQI0ASvyaf45x 87poyrhhW1R6hqXyzysKrpBx8wP/4swjBwWA7wIDAQABAoIBAQCFRAgU1Es9wb7N JjNWjkeyf4nOLeQJX2X4Q1Giv8ubGjtk49qUFN6BbEDmbBk/3GLg1+ezpv1/2gfW a489kecKNrh+bXDGkV81PThP/4BetY0DV8z7dPqhVJCDlRplWwjzTPvnw81g6RRt G+GCvhDDmEFvGA67m2smx2Ldu8I3xfSoyD/xgQFDt433N8KuGZxDphWIK6za0W65 vbeu3lK7gDOxpg/h2o3TWz0xXu7Z18ToYDO3SdfaXtrVV+RumK6JcHL4ditljILD VC8rQtlx8hbUm/CFCAYKqmAQWQPZ9lXJV93WLzmBOs9Ll4y/7UgtKsdYSeJwn/EW jkOzjPYxAoGBAO8yvonHpGuB/EUKsibdiWwofGY0Ifan+THF8hJM2qYLCvxc80Cd hGgUrSME21OEEyCUAfdSed5EwjMb+Dw/rr4OXsS0+XuaIiwjyt4A9HC2aZu/7Kla /hbXhSpL9jdZpcslNjx/28lAPL3VbHd8AL5ZFQP1RDr8NXwkXa58UgupAoGBAN5x q2eYCCsENFvpdn9SKzIBMMPvoOAtEwczdzyzO9A+vCTaae5Id/L2233ZSiIw6PHe TEMsNJSXEVE2h9MmdR5T/Odb6sgpAHtYb3wPEero3w3F8w0BRgdfHf65mpqAiPGz 4KX/3Jo8692tO3qaolQrHBW6fYnrGezvSegX8cbXAoGATWmk1L5x7gFBfa66fW2R PhnUONeCbmtqcLtZXqbU1IwaYBZM+4p/AZg/eGfn+51w+0grYrmki1TBbP0OV6Fc klHgRdU0MNZvXM2wqCyy7495mliA5UZ3mTji9g9jfgJbb+V5KhdPMFDD18QsuHa/ XttBu+gyzsw1GxpB32iIvRECgYBE7cxDaaT+T2s6H/oMMhYYhK3ryVF8pfsDfDsy oAek3AvJD7VLlUE2FLCnKrICLk3AxTW0ivowIS/IjrFACOa54yS3PJBgPfRcRGcn djQDKu4TgodyPDbLCfpteG4j+jh1TVwLlGFzmfUs2AVaVJiApGsrgIJuPl/mP7AT L3hIOQKBgDZivTlUH8OfIoCGvNBOWmuM6g7nD6w8h+5OA3haD8dPziAE1wmb9B2z ttdkTdqVY7Qr2gxl7Fe1GNf2g9pdF8irOG6TMlGXM/HPDgURmMPtv2ycbGsiHN3h IGbzl6AZNUr5qbfM4et0BHekAC/Y/3+SwET2fQdki1tAFa05w+H9 -----END RSA PRIVATE KEY----- relic-7.6.1/functest/testkeys/rsa2048.pgp000066400000000000000000000015331455105530300201470ustar00rootroot00000000000000-----BEGIN PGP PUBLIC KEY BLOCK----- xsBNBFjZfC8BCADP2DbJRrApgYqrehi6CoElo5VS8X7tJCc1EJOESdoJ90Ev7DWW JqDJcvSeKM2yT2XgAjJOfvsA4WjCC3nCY/oBiKVl18AIM3QKrRawS3Kss3Mtp1xP en8JSSLE33lBdQbbEye1FrAtqF2pmjcme1bqTLs4pfAlZRFoFp/OFZpFDpfrcTDf A0poGXn5ecD/Suumpc5sXO3dHAXeMCRdWmflubKZc/DWMozBvUfMK0XpkGWqrvLd p/Fs35rjkmoge48M+EfP9LHJF8zDLWJyuwtf5dhxc5IUy7l9RERAjQBK/Jp/jnHz umjKuGFbVHqGpfLPKwqukHHzA//izCMHBYDvABEBAAHNB3JzYTIwNDjCwGIEEwEK ABYFAljZfC8JEMJ4shZYunePAhsDAhkBAACPJQgAsVq2K9ZRY8PJDiruQ6eqxbtA tA/ZXOe6rMMAxwQZWRI+iWvzNm8TpcWPiJjH5HLTBSssO2xp9H7iRyxXxpfi+7d2 Bbn4EnvV6KFm2mQcCL4M2AmCvXdNf1enISjnvxAsQWCO/TcKoyZGR9RHS2YZXuWF S43Mu4RZmfM+jkfdsi4vuPoOYOckCurJghHtNVZZ+i1UP53J3Xr8gjfx0edAJyhc vNY1dXxh0Sl7cRnIfH0X26lYnpGNtK/NHvYjTQSnloZLsZyQHwVUCy1VRXPpIx8f 0XfnVw2OvQd7plp4PGnIrMx0bT7hD9i7oFevle2AaKP/ndmgjpRpgW1jRnosdA== =7e1B -----END PGP PUBLIC KEY BLOCK----- relic-7.6.1/functest/testkeys/server.crt000066400000000000000000000010521455105530300203500ustar00rootroot00000000000000-----BEGIN CERTIFICATE----- MIIBbTCCAROgAwIBAgINAOKuxZhfM9YxRcodqzAKBggqhkjOPQQDAjAUMRIwEAYD VQQDEwlsb2NhbGhvc3QwIBcNMTcwMzI2MjEwNDQ1WhgPMjExNzAzMjgyMTA0NDVa MBQxEjAQBgNVBAMTCWxvY2FsaG9zdDBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IA BB6c7tcxllwqltCSW4bEDD3x2tUkKhWjyi/wkrm0ilRRAL6YyyksNLUGCrSxK5Qt 0Ml5YniRIKwJrAJFuBdwGSejSDBGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYE FKayhRDqZfpSeJvKHbT2PDWTtouDMBQGA1UdEQQNMAuCCWxvY2FsaG9zdDAKBggq hkjOPQQDAgNIADBFAiEAuMGBYHFgeWvFbSOwe+SeBI6Ljjt2I6jXLqkSbPq+swkC IDPqEZiisoBbiKGJrG64bQif7jJ8LrtJ4OwM4tdWl6Yp -----END CERTIFICATE----- relic-7.6.1/functest/testkeys/server.key000066400000000000000000000003431455105530300203520ustar00rootroot00000000000000-----BEGIN EC PRIVATE KEY----- MHcCAQEEIOMU6XzCGwCaZKZxP+2NqQOVOyUp+UT42TblC76kR5shoAoGCCqGSM49 AwEHoUQDQgAEHpzu1zGWXCqW0JJbhsQMPfHa1SQqFaPKL/CSubSKVFEAvpjLKSw0 tQYKtLErlC3QyXlieJEgrAmsAkW4F3AZJw== -----END EC PRIVATE KEY----- relic-7.6.1/functest/testkeys/ubuntu2012.pgp000066400000000000000000000142411455105530300206730ustar00rootroot00000000000000-----BEGIN PGP PUBLIC KEY BLOCK----- Version: GnuPG v2 mQINBE+tgXgBEADfiL1KNFHT4H4Dw0OR9LemR8ebsFl+b9E44IpGhgWYDufj0gaM /UJ1Ti3bHfRT39VVZ6cv1P4mQy0bnAKFbYz/wo+GhzjBWtn6dThYv7n+KL8bptSC Xgg1a6en8dCCIA/pwtS2Ut/g4Eu6Z467dvYNlMgCqvg+prKIrXf5ibio48j3AFvd 1dDJl2cHfyuON35/83vXKXz0FPohQ7N7kPfI+qrlGBYGWFzC/QEGje360Q2Yo+rf MoyDEXmPsoZVqf7EE8gjfnXiRqmz/Bg5YQb5bgnGbLGiHWtjS+ACIdLUq/h+jlSp 57jw8oQktMh2xVMX4utDM0UENeZnPllVJSlR0b+ZmZz7paeSar8Yxn4wsNlL7GZb pW5A/WmcmWfuMYoPhBo5Fq1V2/siKNU3UKuf1KH+X0p1oZ4oOcZ2bS0Zh3YEG8IQ ce9Bferq4QMKsekcG9IKS6WBIU7BwaElI2ILD0gSwu8KzvNSEeIJhYSsBIEzrWxI BXoN2AC9PCqqXkWlI5Xr/86RWllB3CsoPwEfO8CLJW2LlXTen/Fkq4wT+apdhHei WiSsq/J5OEff0rKHBQ3fK7fyVuVNrJFb2CopaBLyCxTupvxs162jjUNopt0c7OqN BoPoUoVFAxUSpeEwAw6xrM5vROyLMSeh/YnTuRy8WviRapZCYo6naTCY5wARAQAB tEJVYnVudHUgQXJjaGl2ZSBBdXRvbWF0aWMgU2lnbmluZyBLZXkgKDIwMTIpIDxm dHBtYXN0ZXJAdWJ1bnR1LmNvbT6JARwEEAEIAAYFAlRTaC8ACgkQXLm7OxIWZIqJ 5Qf9EAtNNZHC4Wrqz3LIFqLNolmvM7n1TaWcTE6CpkwaXurBWeHtfLawgeOPKaZ1 JhzyjJ05A8xZX15WZzCBuB41+8DbxY1fqovl9JFQtez5mBjnPYcm79odSTYznz1D JRTYyXfD+zhFb4JxYnKIBEHtgaQuvR89iHsGIxZk/7+y+s+1omYlounm8mflgqQI FVdBxni9UHsV332CgGG0iAheM8ayQk193U/QCj7UJ3LUHh2ZfKkaNkKwl9FvXqrn FY+EiT4mnWgzUIHDyPRqkzQGa2ZgsD7m9lsHb+FDey0cnHQgtN0o9XTLRAhe54SC tscTRlAizmDVWSPRCbf/B1hFbIkBnAQQAQgABgUCVzop0AAKCRCg8hPxRutYH7o9 DACMcKjM4JfWfaVUXrxVzCn32Sd9UmVFkk5QSlT8KCPQ/J0t24NPgWEPKSIIK7iq 1XDJF4T/qNtOujvdlcmJfbcAXbVrhS4DTdMQOD2Wkp+xRdF4olG1VPkoCltpzmTE l95SnHlhXWvyyi84vUshLInRWXWz2D3DQdOigNnKmekegpoEVLRJQy/3ylJs1mEO N++Zhs55XPr6F5ZQZjXKg0LhpHqJL7YuUAysojDcliH59en7g9F1SnxHKC3FFbhh DSq33oelXGXx3ob5xYpycRQyOEoYfQbn0cb2L7r+YiL64mSeK4u8397rL9Bpmh9i 67NE8cZSSwQMHz/cAlfiOnuKKyvIQgdlvIW65ILEabn54dnc9yDCOEjyPDpcjat3 ccHS+qjeCF5B9hRbKIVSbQMorH7ccMH+CapFWpCeeKX53SqGLGYDKK5Vyz0kthAR xnR0sXVSvkg08afajHNGMxxqpX1E6axwx8enu5ixjk+6mZDf84HW9Ye4+UmVm8DO 8FWJAhwEEAECAAYFAk+tiWIACgkQC/uEfz8nL1tnahAApP6U3tEciHCnP5O5OsEl IYViEu6vlP0WDXWYgptD2F1UwBL1c719X8fg52iLPr6dxtYi7zOc+yprOI+hIS6C PgKsF9XMOdwPM456K8kjrrK6J6Sznc/jyi6AaAzcbZpMUCezyFcKBJUFl3gPExDB H3D0+a5eAaexUhKGyf1Os3P5Q5KlNfYXs8bBP685byEzpbQalpSEW41W+LgcAZxb ceBwPL8q4pTknMsp1RucZ7Lk34e1xNir4ptEEcKl8bqk82NEhV2X9fpBwgUNpQ7b gvW1hOe+B6FCeTPcbIuR6qKqbC5vuxNdiaFRK3N08zY1cZs9VAY1e7m3K+68sBz4 lOufgAMiMMPfmhj/i2L4twKS7IO7NTwRZwxsb9CoXdUfZtQjSk/hMUiR4taQw7uJ 04I5/r6e29BoZrcGudGz0J6YMppXJbCM0kRduhLz4vAGlaY3D+J1aAvN/03tcXX4 Z+sXrUV5hxyW1wHVfwzgSUj8JyG+Tnb44byPpRJ6YI4vU5KKttiq+PxSuhBptVTB 73RNmtug4vTpDZ095fR8EiTzftRdEMmnMph7Fm9aD2SDsht/0xi6h23luQb7qyXC Zs3tbUjhQCCsiPgSGUT3CyZ2wxzZ41eToM4KU3yyx5c/Hj3ScvrbdJBLHi1tim6T NoRdERc5BPa5jkYe8NUy/tuJAhwEEAECAAYFAk+tipcACgkQ18PxMasqkfWskBAA mv4B90tbmgH3kjry1khhXC+pnjXBTymR/yJe42ZPgLbrB2PbTiNCXzqJImV/wSK6 qzDMPR4Gtj9A084voAFwq+E7gqW5pGJQuu5pcjuh2SYgMC3LmBh7TicPsTfC/nmV A5OtPu7tBmd/L//Jj7dDDgRrox5J4Zb6zdESmYA9KX6JpaSpiKjH+2YJA5AkrSPP FNxjIdwvOg1on4ioA1lQzxREKttFqlFubdfED0V4PRmWDvs0YJnJPWnDpeQbuQuU TgG9SbNL90hCTHyqekmFyEtDbSXGpwFpPVu5FPGS29+VY95WE/LQwuXvaX8F2GXL bVhxBuZsKWRtYwaAty1uo3bkfAfwujqC5ST2haWq+25c4QjfWVmBJ6y2mwLP8m+V 9i7J68FrfeytShFMU2dmyEY3ORPbQZeOfHtYNvqYfHBykBkUIILaS18PB379TSZT r7TbNgdvLo4iNEecC9sIaHPnUR098TfrZbcJjTbWS0SC73FikgY59ESdyuOjQCg7 CqhZCESgsbeKEXwsxSvJU1pzR7CysZjuDEUKc/5oe1fNpryYxbx2RaA3CLpF8l+m GuAeR2ApWMq6SQipGtmsOgPk7sDXyhNveIysEod1JVmnSJymYAdvR10E2Vj3O6ft qS4GtXMzloq6jbWX9Cz3PdQrmTgON96B50UKWqmfftuJAhwEEAEIAAYFAk+tilwA CgkQOTWH2X2GUAu10w//X2Vlad44IUQV2wrojV+JRKuuyL5FF5/EZPlXBRZNBgiv tTBMksY+P4aQtB7Z14SPW/6aZpk2xvzchAkqhxBujbU5kCvdS/gbord8DCPvMTdX 7aTWJkoMf/cx8eln3H7WxRA9wL5jQ6HSNbDYYY8KViMvA/tYZ6BrIJ8916OUA5F6 6UqLaB76aSg0zZmbUCkDWrXmsdJPlC8inVapqsrTTdA8AGt4NjfvN98qJDBOj+NT OdxOVMWVOEtaq+HYfuD6vquOsOHL/K0H6U44AqnSowcbV6i+pDqWlpHH43RRH5sf j5YwECDvnq16v/eH9n3ACkg+Cl0D8GTEVC73fYluF5mr7xZdDdfC9eP89WSpGvfM 0AxgpVxIgVDSECF3ZIrxevMpQw88OoMXqVQDiG/dpTSSYuJYmJsVYbGUTksulI/D LcR39tK/FjET44T6yR0HeI+oc3sYcuKVMdexFMRc8sw31B8oYpeIsUZecstzuEws Vu2hflR1tpoQrv6sSQhzXNnPD2iuB58fahdBJ6G6HDOZCl+YYz/MTBRKZt5nBdbz a3F5oZGf9Dkx8Ah6rEEotJPou/xI1+kj5/W9Y0Nl5FRPDcXzJYuuWXxrlEocO+cr eNfN5Px0tk8iMZQszIWbyvdTZ2MqEitQmON3z1AzupCc0V1TstHdgbR7NPmkm76J AhwEEAEIAAYFAk+2+noACgkQV1nzUAGqSmQXHw//VctYFNYgJHYbG0o4DfzvuRf4 hDvANwLiLW1wIBPfT7Rge3+Wgz40SSk7lVRthMjRVfbX1u85/msChzsGDMSo9hu8 SyvROWK2rLQWDb/MuRpxZvdp5NKy0qAT7sxhNgOrOv1lebjwAWq4+2qdAqeH9I8H s+niAuYEz736u6I9CwZB0VLWdKEmz5AlTY8l3kZydQE/yZ/BxsOXBpxQvVW57vX7 JZ4mNjTWgLjy1a5jiiMrygymoUaB870CuDgHdkiCskPTlMi9LPtaiWHgYipemSr5 lL/qkE50xz1244dRoAGtu42rYR7xnbFYxTnpQisMejMGvxgEWszmQS4kzj21wlaP XuIecB5B7/a7QplfrOBLAn72qpWr8bldbqYDYEI2Qzd5pFZZlgj7KIP2Jloa4/eM na4AK8UhH62r8LecLjY/cevQQaQE+V1qkJ4SJujWGvxeowm63Bup8FisFpcRjeWY ZuNFBZiAZLk1RQVBx9QZXKsf1lnFBVAUK+fuqQUoykHLhKfkcy3AP+n3VbbT2H9y nynN3ASHbkS/d7N3GBvXUm6BkqrA+uiuCHDIA8WtR2gs+z2NVSfl6wu2dKpzGBVy iC8+id9yb54TKgC7A0KF/oT1mlYIwpgplby0Kj/4Qjb8M7H4aITTXmClH46D0HGF g0v3Jk0Il+fv9jOS+cWJAhwEEAEKAAYFAlDC/7sACgkQH2dQ/Ty9zOBwUg/+Pp0O I6gQCmu4F8ksYnQtI+rREUpfn4cK7Ksv6KKXLaQKGCbE+wpn7gt62/fwbKN4d0AC EDGE7ePgRcbcpo25gOQOP/YWEZws8Ashwx1DLdo/K9a13PqQUujQ8F2Bg+8aPXjN a90G5Mzhoy6d8SWtJF77ftT+zUMiYPVLTAjPIyattOaZiftd81+3bUQORY4PH8GC SswFmE1EY0dOOMHOTQxxB3sgiv3y9iQosa9Ca/xNtdqfSoOMui5O3LwDkehBsLhJ IaAt2yVT2uKP4HEEStcVtnNNPIBpErYrxdke3yg+gQdlp8tnPMTxB2fuvoELOeNM 3TLr0M8xaWFw7e2gFhawZRttxp7c7/JQn8xwDQsJ8WZs0s9LzMAz9BPk9Bb+fG5g V4+yFOQNkOYgJEf7mnR/A3v+klgwl9VqlC4s8KEwnZLxjRQpoJjCBobwW9EHa7Qc ht/AhWUJJt4n00KMZj8DxHWnQEUgRtbQW82pbrpE//2430nggPNBxb2dUpH9ZYWY akq0lMCTQWXQYsY2Jwp8RTTq6BkJxFiooeeJ6RNHDYubxau/uCTpQaDqCb+bN9Of SrRatE2BHnTwauH7JkSHhaDEEJsiX/jHvCUK9qGqa+WeAFwShtgFIXBDT22n+fIw cHGcwpbM6SRdr0CBenAH1e9vsql8GuN6FZo+92qJAjgEEwECACIFAk+tgXgCGwMG CwkIBwMCBhUIAgkKCwQWAgMBAh4BAheAAAoJEDtP5qzAsh8yXX4QAJHUdK6eYMyJ crFP3yKXtUYQMpaHRM/floqZtOFhlmcLVMgBNOr0eLvBU0JcZyZpHMvZciTDBMWX 8ItCYVjRejf0K0lPvHHRGaE7t6JHVUCeznNbDMnOPYVwlVJdZLOa6PmE5WXVXpk8 uTA8vm6RO2rS23vE7U0pQlV+1GVXMWH4ZLjaQs/Tm7wdvRxeqTbtfOEeHGLjmsoh 0erHfzMV4wA/9Zq86WzuJS1HxXR6OYDC3/aQX7CxYT1MQxEw/PObnHtkl3PRMWdT W7fSQtulEXzpr2/JCev6Mfc8Uy0aD3jng9byVk9GpdNFEjGgaUqjqyZosvwAZ4/d mRjmMEibXeNUGC8HeWC3WOVV8L/DiA+miJlwPvwPiA1ZuKBI5A8VF0rNHW7QVsG8 kQ+PDHgRdsmhpzSRgykN1PgK6UxScKX8LqNKCtKpuEPApka7FQ1u4BoZKjjpBhY1 R4TpfFkMIe7qW8XfqoaP99pED3xXch2zFRNHitNJr+yQJH4z/o+2UvnTA2niUTHl FSCBoU1MvSq1N2J3qU6oR2cOYJ4ZxqWyCoeQR1x8aPnLlcn4le6HU7TocYbHaImc It7qnG4Ni0OWP4giEhjOpgxtrWgl36mdufvriwya+EHXzn36EvQ9O+bm3fyarsnh Pe01rlsRxqBiK1JOw/g4GnpX8iLGEX1V =cCF0 -----END PGP PUBLIC KEY BLOCK----- relic-7.6.1/functest/token.conf000066400000000000000000000001671455105530300164520ustar00rootroot00000000000000# SoftHSM v2 configuration file directories.tokendir = ./scratch/token objectstore.backend = file log.level = WARNING relic-7.6.1/go.mod000066400000000000000000000116771455105530300137460ustar00rootroot00000000000000module github.com/sassoftware/relic/v7 go 1.18 require ( cloud.google.com/go/kms v1.9.0 github.com/Azure/azure-sdk-for-go v68.0.0+incompatible github.com/Azure/go-autorest/autorest v0.11.28 github.com/Azure/go-autorest/autorest/adal v0.9.22 github.com/Azure/go-autorest/autorest/azure/auth v0.5.12 github.com/AzureAD/microsoft-authentication-library-for-go v0.8.1 github.com/aws/aws-sdk-go-v2/config v1.18.15 github.com/aws/aws-sdk-go-v2/service/kms v1.20.6 github.com/beevik/etree v1.1.0 github.com/bradfitz/gomemcache v0.0.0-20230124162541-5f7a7d875746 github.com/go-asn1-ber/asn1-ber v1.5.4 github.com/go-chi/chi/v5 v5.0.8 github.com/go-jose/go-jose/v3 v3.0.0 github.com/golang/snappy v0.0.4 github.com/google/uuid v1.3.0 github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 github.com/howeyc/gopass v0.0.0-20210920133722-c8aef6fb66ef github.com/kr/pretty v0.3.1 github.com/lib/pq v1.10.7 github.com/miekg/pkcs11 v1.1.1 github.com/opencontainers/go-digest v1.0.0 github.com/opencontainers/image-spec v1.1.0-rc3 github.com/peterbourgon/diskv v2.0.1+incompatible github.com/prometheus/client_golang v1.14.0 github.com/qur/ar v0.0.0-20130629153254-282534b91770 github.com/rs/zerolog v1.29.0 github.com/sassoftware/go-rpmutils v0.2.0 github.com/spf13/cobra v1.6.1 github.com/spf13/pflag v1.0.5 github.com/streadway/amqp v1.0.0 github.com/stretchr/testify v1.8.2 github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 github.com/zalando/go-keyring v0.2.2 golang.org/x/crypto v0.7.0 golang.org/x/net v0.8.0 golang.org/x/oauth2 v0.6.0 golang.org/x/sync v0.1.0 golang.org/x/sys v0.6.0 golang.org/x/term v0.6.0 golang.org/x/time v0.3.0 google.golang.org/api v0.111.0 google.golang.org/genproto v0.0.0-20230306155012-7f2fa6fef1f4 gopkg.in/yaml.v3 v3.0.1 howett.net/plist v1.0.0 software.sslmate.com/src/go-pkcs12 v0.2.0 ) require ( cloud.google.com/go/compute v1.18.0 // indirect cloud.google.com/go/compute/metadata v0.2.3 // indirect cloud.google.com/go/iam v0.12.0 // indirect github.com/Azure/go-autorest v14.2.0+incompatible // indirect github.com/Azure/go-autorest/autorest/azure/cli v0.4.6 // indirect github.com/Azure/go-autorest/autorest/date v0.3.0 // indirect github.com/Azure/go-autorest/autorest/to v0.4.0 // indirect github.com/Azure/go-autorest/autorest/validation v0.3.1 // indirect github.com/Azure/go-autorest/logger v0.2.1 // indirect github.com/Azure/go-autorest/tracing v0.6.0 // indirect github.com/DataDog/zstd v1.4.5 // indirect github.com/alessio/shellescape v1.4.1 // indirect github.com/aws/aws-sdk-go-v2 v1.17.5 // indirect github.com/aws/aws-sdk-go-v2/credentials v1.13.15 // indirect github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.12.23 // indirect github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.29 // indirect github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.23 // indirect github.com/aws/aws-sdk-go-v2/internal/ini v1.3.30 // indirect github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.23 // indirect github.com/aws/aws-sdk-go-v2/service/sso v1.12.4 // indirect github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.4 // indirect github.com/aws/aws-sdk-go-v2/service/sts v1.18.5 // indirect github.com/aws/smithy-go v1.13.5 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect github.com/danieljoos/wincred v1.1.2 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/dimchansky/utfbom v1.1.1 // indirect github.com/godbus/dbus/v5 v5.1.0 // indirect github.com/golang-jwt/jwt/v4 v4.4.3 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/protobuf v1.5.2 // indirect github.com/google/btree v1.0.0 // indirect github.com/google/go-cmp v0.5.9 // indirect github.com/googleapis/enterprise-certificate-proxy v0.2.3 // indirect github.com/googleapis/gax-go/v2 v2.7.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/klauspost/compress v1.15.14 // indirect github.com/kr/text v0.2.0 // indirect github.com/kylelemons/godebug v1.1.0 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.16 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect github.com/mitchellh/go-homedir v1.1.0 // indirect github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/prometheus/client_model v0.3.0 // indirect github.com/prometheus/common v0.37.0 // indirect github.com/prometheus/procfs v0.8.0 // indirect github.com/rogpeppe/go-internal v1.9.0 // indirect github.com/rs/xid v1.4.0 // indirect github.com/ulikunitz/xz v0.5.11 // indirect go.opencensus.io v0.24.0 // indirect golang.org/x/text v0.8.0 // indirect google.golang.org/appengine v1.6.7 // indirect google.golang.org/grpc v1.53.0 // indirect google.golang.org/protobuf v1.28.1 // indirect gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect ) relic-7.6.1/go.sum000066400000000000000000002121371455105530300137650ustar00rootroot00000000000000cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= cloud.google.com/go v0.110.0 h1:Zc8gqp3+a9/Eyph2KDmcGaPtbKRIoqq4YTlL4NMD0Ys= cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= cloud.google.com/go/compute v1.18.0 h1:FEigFqoDbys2cvFkZ9Fjq4gnHBP55anJ0yQyau2f9oY= cloud.google.com/go/compute v1.18.0/go.mod h1:1X7yHxec2Ga+Ss6jPyjxRxpu2uu7PLgsOVXvgU0yacs= cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY= cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA= cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= cloud.google.com/go/iam v0.12.0 h1:DRtTY29b75ciH6Ov1PHb4/iat2CLCvrOm40Q0a6DFpE= cloud.google.com/go/iam v0.12.0/go.mod h1:knyHGviacl11zrtZUoDuYpDgLjvr28sLQaG0YB2GYAY= cloud.google.com/go/kms v1.9.0 h1:b0votJQa/9DSsxgHwN33/tTLA7ZHVzfWhDCrfiXijSo= cloud.google.com/go/kms v1.9.0/go.mod h1:qb1tPTgfF9RQP8e1wq4cLFErVuTJv7UsSC915J8dh3w= cloud.google.com/go/longrunning v0.4.1 h1:v+yFJOfKC3yZdY6ZUI933pIYdhyhV8S3NpWrXWmg7jM= cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= github.com/Azure/azure-sdk-for-go v68.0.0+incompatible h1:fcYLmCpyNYRnvJbPerq7U0hS+6+I79yEDJBqVNcqUzU= github.com/Azure/azure-sdk-for-go v68.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= github.com/Azure/go-autorest v14.2.0+incompatible h1:V5VMDjClD3GiElqLWO7mz2MxNAK/vTfRHdAubSIPRgs= github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= github.com/Azure/go-autorest/autorest v0.11.24/go.mod h1:G6kyRlFnTuSbEYkQGawPfsCswgme4iYf6rfSKUDzbCc= github.com/Azure/go-autorest/autorest v0.11.28 h1:ndAExarwr5Y+GaHE6VCaY1kyS/HwwGGyuimVhWsHOEM= github.com/Azure/go-autorest/autorest v0.11.28/go.mod h1:MrkzG3Y3AH668QyF9KRk5neJnGgmhQ6krbhR8Q5eMvA= github.com/Azure/go-autorest/autorest/adal v0.9.18/go.mod h1:XVVeme+LZwABT8K5Lc3hA4nAe8LDBVle26gTrguhhPQ= github.com/Azure/go-autorest/autorest/adal v0.9.22 h1:/GblQdIudfEM3AWWZ0mrYJQSd7JS4S/Mbzh6F0ov0Xc= github.com/Azure/go-autorest/autorest/adal v0.9.22/go.mod h1:XuAbAEUv2Tta//+voMI038TrJBqjKam0me7qR+L8Cmk= github.com/Azure/go-autorest/autorest/azure/auth v0.5.12 h1:wkAZRgT/pn8HhFyzfe9UnqOjJYqlembgCTi72Bm/xKk= github.com/Azure/go-autorest/autorest/azure/auth v0.5.12/go.mod h1:84w/uV8E37feW2NCJ08uT9VBfjfUHpgLVnG2InYD6cg= github.com/Azure/go-autorest/autorest/azure/cli v0.4.5/go.mod h1:ADQAXrkgm7acgWVUNamOgh8YNrv4p27l3Wc55oVfpzg= github.com/Azure/go-autorest/autorest/azure/cli v0.4.6 h1:w77/uPk80ZET2F+AfQExZyEWtn+0Rk/uw17m9fv5Ajc= github.com/Azure/go-autorest/autorest/azure/cli v0.4.6/go.mod h1:piCfgPho7BiIDdEQ1+g4VmKyD5y+p/XtSNqE6Hc4QD0= github.com/Azure/go-autorest/autorest/date v0.3.0 h1:7gUk1U5M/CQbp9WoqinNzJar+8KY+LPI6wiWrP/myHw= github.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSYnokU+TrmwEsOqdt8Y6sso74= github.com/Azure/go-autorest/autorest/mocks v0.4.1/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k= github.com/Azure/go-autorest/autorest/mocks v0.4.2 h1:PGN4EDXnuQbojHbU0UWoNvmu9AGVwYHG9/fkDYhtAfw= github.com/Azure/go-autorest/autorest/mocks v0.4.2/go.mod h1:Vy7OitM9Kei0i1Oj+LvyAWMXJHeKH1MVlzFugfVrmyU= github.com/Azure/go-autorest/autorest/to v0.4.0 h1:oXVqrxakqqV1UZdSazDOPOLvOIz+XA683u8EctwboHk= github.com/Azure/go-autorest/autorest/to v0.4.0/go.mod h1:fE8iZBn7LQR7zH/9XU2NcPR4o9jEImooCeWJcYV/zLE= github.com/Azure/go-autorest/autorest/validation v0.3.1 h1:AgyqjAd94fwNAoTjl/WQXg4VvFeRFpO+UhNyRXqF1ac= github.com/Azure/go-autorest/autorest/validation v0.3.1/go.mod h1:yhLgjC0Wda5DYXl6JAsWyUe4KVNffhoDhG0zVzUMo3E= github.com/Azure/go-autorest/logger v0.2.1 h1:IG7i4p/mDa2Ce4TRyAO8IHnVhAVF3RFU+ZtXWSmf4Tg= github.com/Azure/go-autorest/logger v0.2.1/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8= github.com/Azure/go-autorest/tracing v0.6.0 h1:TYi4+3m5t6K48TGI9AUdb+IzbnSxvnvUMfuitfgcfuo= github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU= github.com/AzureAD/microsoft-authentication-library-for-go v0.8.1 h1:oPdPEZFSbl7oSPEAIPMPBMUmiL+mqgzBJwM/9qYcwNg= github.com/AzureAD/microsoft-authentication-library-for-go v0.8.1/go.mod h1:4qFor3D/HDsvBME35Xy9rwW9DecL+M2sNw1ybjPtwA0= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/DataDog/zstd v1.4.5 h1:EndNeuB0l9syBZhut0wns3gV1hL8zX8LIu6ZiVHWLIQ= github.com/DataDog/zstd v1.4.5/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= github.com/alessio/shellescape v1.4.1 h1:V7yhSDDn8LP4lc4jS8pFkt0zCnzVJlG5JXy9BVKJUX0= github.com/alessio/shellescape v1.4.1/go.mod h1:PZAiSCk0LJaZkiCSkPv8qIobYglO3FPpyFjDCtHLS30= github.com/aws/aws-sdk-go-v2 v1.17.5 h1:TzCUW1Nq4H8Xscph5M/skINUitxM5UBAyvm2s7XBzL4= github.com/aws/aws-sdk-go-v2 v1.17.5/go.mod h1:uzbQtefpm44goOPmdKyAlXSNcwlRgF3ePWVW6EtJvvw= github.com/aws/aws-sdk-go-v2/config v1.18.15 h1:509yMO0pJUGUugBP2H9FOFyV+7Mz7sRR+snfDN5W4NY= github.com/aws/aws-sdk-go-v2/config v1.18.15/go.mod h1:vS0tddZqpE8cD9CyW0/kITHF5Bq2QasW9Y1DFHD//O0= github.com/aws/aws-sdk-go-v2/credentials v1.13.15 h1:0rZQIi6deJFjOEgHI9HI2eZcLPPEGQPictX66oRFLL8= github.com/aws/aws-sdk-go-v2/credentials v1.13.15/go.mod h1:vRMLMD3/rXU+o6j2MW5YefrGMBmdTvkLLGqFwMLBHQc= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.12.23 h1:Kbiv9PGnQfG/imNI4L/heyUXvzKmcWSBeDvkrQz5pFc= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.12.23/go.mod h1:mOtmAg65GT1HIL/HT/PynwPbS+UG0BgCZ6vhkPqnxWo= github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.29 h1:9/aKwwus0TQxppPXFmf010DFrE+ssSbzroLVYINA+xE= github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.29/go.mod h1:Dip3sIGv485+xerzVv24emnjX5Sg88utCL8fwGmCeWg= github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.23 h1:b/Vn141DBuLVgXbhRWIrl9g+ww7G+ScV5SzniWR13jQ= github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.23/go.mod h1:mr6c4cHC+S/MMkrjtSlG4QA36kOznDep+0fga5L/fGQ= github.com/aws/aws-sdk-go-v2/internal/ini v1.3.30 h1:IVx9L7YFhpPq0tTnGo8u8TpluFu7nAn9X3sUDMb11c0= github.com/aws/aws-sdk-go-v2/internal/ini v1.3.30/go.mod h1:vsbq62AOBwQ1LJ/GWKFxX8beUEYeRp/Agitrxee2/qM= github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.23 h1:QoOybhwRfciWUBbZ0gp9S7XaDnCuSTeK/fySB99V1ls= github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.23/go.mod h1:9uPh+Hrz2Vn6oMnQYiUi/zbh3ovbnQk19YKINkQny44= github.com/aws/aws-sdk-go-v2/service/kms v1.20.6 h1:gnCeEJCh+3+RWiloIyXJ5AhakBKckP2uiRVa3G4J1ug= github.com/aws/aws-sdk-go-v2/service/kms v1.20.6/go.mod h1:oTK4GAHgyFSGKzhReYfD19/vjtgUOPwCbm7v5MgWLW4= github.com/aws/aws-sdk-go-v2/service/sso v1.12.4 h1:qJdM48OOLl1FBSzI7ZrA1ZfLwOyCYqkXV5lko1hYDBw= github.com/aws/aws-sdk-go-v2/service/sso v1.12.4/go.mod h1:jtLIhd+V+lft6ktxpItycqHqiVXrPIRjWIsFIlzMriw= github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.4 h1:YRkWXQveFb0tFC0TLktmmhGsOcCgLwvq88MC2al47AA= github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.4/go.mod h1:zVwRrfdSmbRZWkUkWjOItY7SOalnFnq/Yg2LVPqDjwc= github.com/aws/aws-sdk-go-v2/service/sts v1.18.5 h1:L1600eLr0YvTT7gNh3Ni24yGI7NSHkq9Gp62vijPRCs= github.com/aws/aws-sdk-go-v2/service/sts v1.18.5/go.mod h1:1mKZHLLpDMHTNSYPJ7qrcnCQdHCWsNQaT0xRvq2u80s= github.com/aws/smithy-go v1.13.5 h1:hgz0X/DX0dGqTYpGALqXJoRKRj5oQ7150i5FdTePzO8= github.com/aws/smithy-go v1.13.5/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA= github.com/beevik/etree v1.1.0 h1:T0xke/WvNtMoCqgzPhkX2r4rjY3GDZFi+FjpRZY2Jbs= github.com/beevik/etree v1.1.0/go.mod h1:r8Aw8JqVegEf0w2fDnATrX9VpkMcyFeM0FhwO62wh+A= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/bradfitz/gomemcache v0.0.0-20230124162541-5f7a7d875746 h1:wAIE/kN63Oig1DdOzN7O+k4AbFh2cCJoKMFXrwRJtzk= github.com/bradfitz/gomemcache v0.0.0-20230124162541-5f7a7d875746/go.mod h1:H0wQNHz2YrLsuXOZozoeDmnHXkNCRmMW0gwFWDfEZDA= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/coreos/go-systemd/v22 v22.3.3-0.20220203105225-a9a7ef127534/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/danieljoos/wincred v1.1.2 h1:QLdCxFs1/Yl4zduvBdcHB8goaYk9RARS2SgLLRuAyr0= github.com/danieljoos/wincred v1.1.2/go.mod h1:GijpziifJoIBfYh+S7BbkdUTU4LfM+QnGqR5Vl2tAx0= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dimchansky/utfbom v1.1.1 h1:vV6w1AhK4VMnhBno/TPVCoK9U/LP0PkLCS9tbxHdi/U= github.com/dimchansky/utfbom v1.1.1/go.mod h1:SxdoEBH5qIqFocHMyGOXVAybYJdr71b1Q/j0mACtrfE= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/go-asn1-ber/asn1-ber v1.5.4 h1:vXT6d/FNDiELJnLb6hGNa309LMsrCoYFvpwHDF0+Y1A= github.com/go-asn1-ber/asn1-ber v1.5.4/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0= github.com/go-chi/chi/v5 v5.0.8 h1:lD+NLqFcAi1ovnVZpsnObHGW4xb4J8lNmoYVfECH1Y0= github.com/go-chi/chi/v5 v5.0.8/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-jose/go-jose/v3 v3.0.0 h1:s6rrhirfEP/CGIoc6p+PZAeogN2SxKav6Wp7+dyMWVo= github.com/go-jose/go-jose/v3 v3.0.0/go.mod h1:RNkWWRld676jZEYoV3+XK8L2ZnNSvIsxFMht0mSX+u8= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= github.com/go-kit/log v0.2.0/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/golang-jwt/jwt/v4 v4.0.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg= github.com/golang-jwt/jwt/v4 v4.2.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg= github.com/golang-jwt/jwt/v4 v4.4.3 h1:Hxl6lhQFj4AnOX6MLrsCb/+7tCj7DxP7VA+2rDIq5AU= github.com/golang-jwt/jwt/v4 v4.4.3/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0 h1:0udJVsspx3VBr5FwtLhQQtuAsVc79tTq0ocGIPAU6qo= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/enterprise-certificate-proxy v0.2.3 h1:yk9/cqRKtT9wXZSsRH9aurXEpJX+U6FLtpYTdC3R06k= github.com/googleapis/enterprise-certificate-proxy v0.2.3/go.mod h1:AwSRAtLfXpU5Nm3pW+v7rGDHp09LsPtGY9MduiEsR9k= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/googleapis/gax-go/v2 v2.7.0 h1:IcsPKeInNvYi7eqSaDjiZqDDKu5rsmunY0Y1YupQSSQ= github.com/googleapis/gax-go/v2 v2.7.0/go.mod h1:TEop28CZZQ2y+c0VxMUmu1lV+fQx57QpBWsYpwqHJx8= github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 h1:+ngKgrYPPJrOjhax5N+uePQ0Fh1Z7PheYoUI/0nzkPA= github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/howeyc/gopass v0.0.0-20210920133722-c8aef6fb66ef h1:A9HsByNhogrvm9cWb28sjiS3i7tcKCkflWFEkHfuAgM= github.com/howeyc/gopass v0.0.0-20210920133722-c8aef6fb66ef/go.mod h1:lADxMC39cJJqL93Duh1xhAs4I2Zs8mKS89XWXFGp9cs= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/inconshreveable/mousetrap v1.0.1/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/klauspost/compress v1.11.7/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= github.com/klauspost/compress v1.15.14 h1:i7WCKDToww0wA+9qrUZ1xOjp218vfFo3nTU6UHp+gOc= github.com/klauspost/compress v1.15.14/go.mod h1:QPwzmACJjUTFsnSHH934V6woptycfrDDJnH7hvFVbGM= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= github.com/lib/pq v1.10.7 h1:p7ZhMD+KsSRozJr34udlUrhboJwWAgCg34+/ZZNvZZw= github.com/lib/pq v1.10.7/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/miekg/pkcs11 v1.1.1 h1:Ugu9pdy6vAYku5DEpVWVFPYnzV+bxB+iRdbuFSu7TvU= github.com/miekg/pkcs11 v1.1.1/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs= github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.1.0-rc3 h1:fzg1mXZFj8YdPeNkRXMg+zb88BFV0Ys52cJydRwBkb8= github.com/opencontainers/image-spec v1.1.0-rc3/go.mod h1:X4pATf0uXsnn3g5aiGIsVnJBR4mxhKzfwmvK/B2NTm8= github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+vxiaj6gdUUzhl4XmI= github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 h1:KoWmjvw+nsYOo29YJK9vDA65RGE3NrOnUtO7a+RF9HU= github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8/go.mod h1:HKlIX3XHQyzLZPlr7++PzdhaXEj94dEiJgZDTsxEqUI= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY= github.com/prometheus/client_golang v1.14.0 h1:nJdhIvne2eSX/XRAFV9PcvFFRbrjbcTUj0VP62TMhnw= github.com/prometheus/client_golang v1.14.0/go.mod h1:8vpkKitgIVNcqrRBWh1C4TIUQgYNtG/XQE4E/Zae36Y= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.3.0 h1:UBgGFHqYdG/TPFD1B1ogZywDqEkwp3fBMvqdiQ7Xew4= github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w= github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= github.com/prometheus/common v0.37.0 h1:ccBbHCgIiT9uSoFY0vX8H3zsNR5eLt17/RQLUvn8pXE= github.com/prometheus/common v0.37.0/go.mod h1:phzohg0JFMnBEFGxTDbfu3QyL5GI8gTQJFhYO5B3mfA= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= github.com/prometheus/procfs v0.8.0 h1:ODq8ZFEaYeCaZOJlZZdJA2AbQR98dSHSM1KW/You5mo= github.com/prometheus/procfs v0.8.0/go.mod h1:z7EfXMXOkbkqb9IINtpCn86r/to3BnA0uaxHdg830/4= github.com/qur/ar v0.0.0-20130629153254-282534b91770 h1:A6sXY4zAECrW5Obx41PVMGr4kOw1rd1kmwcHa5M0dTg= github.com/qur/ar v0.0.0-20130629153254-282534b91770/go.mod h1:SjlYv2m9lpV0UW6K7lDqVJwEIIvSjaHbGk7nIfY8Hxw= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/rs/xid v1.4.0 h1:qd7wPTDkN6KQx2VmMBLrpHkiyQwgFXRnkOLacUiaSNY= github.com/rs/xid v1.4.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= github.com/rs/zerolog v1.29.0 h1:Zes4hju04hjbvkVkOhdl2HpZa+0PmVwigmo8XoORE5w= github.com/rs/zerolog v1.29.0/go.mod h1:NILgTygv/Uej1ra5XxGf82ZFSLk58MFGAUS2o6usyD0= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/sassoftware/go-rpmutils v0.2.0 h1:pKW0HDYMFWQ5b4JQPiI3WI12hGsVoW0V8+GMoZiI/JE= github.com/sassoftware/go-rpmutils v0.2.0/go.mod h1:TJJQYtLe/BeEmEjelI3b7xNZjzAukEkeWKmoakvaOoI= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= github.com/spf13/cobra v1.6.1 h1:o94oiPyS4KD1mPy2fmcYYHHfCxLqYjJOhGsCHFZtEzA= github.com/spf13/cobra v1.6.1/go.mod h1:IOw/AERYS7UzyrGinqmz6HLUo219MORXGxhbaJUqzrY= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/streadway/amqp v1.0.0 h1:kuuDrUJFZL1QYL9hUNuCxNObNzB0bV/ZG5jV3RWAQgo= github.com/streadway/amqp v1.0.0/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/ulikunitz/xz v0.5.9/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= github.com/ulikunitz/xz v0.5.11 h1:kpFauv27b6ynzBNT/Xy+1k+fK4WswhN/6PN5WhFAGw8= github.com/ulikunitz/xz v0.5.11/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 h1:nIPpBwaJSVYIxUFsDv3M8ofmx9yWTog9BfvIu0q41lo= github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8/go.mod h1:HUYIGzjTL3rfEspMxjDjgmT5uz5wzYJKVo23qUhYTos= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/zalando/go-keyring v0.2.2 h1:f0xmpYiSrHtSNAVgwip93Cg8tuF45HJM6rHq/A5RI/4= github.com/zalando/go-keyring v0.2.2/go.mod h1:sI3evg9Wvpw3+n4SqplGSJUMwtDeROfD4nsFz4z9PG0= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= go.uber.org/goleak v1.1.10 h1:z+mqJhf6ss6BSfSM671tgKyZBFPTTJM+HLxnhPC3wu0= go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220331220935-ae2d96664a29/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.7.0 h1:AvwMYaRytfdeVt3u6mLaxYtErKYjxA2OXjJ1HHq6t3A= golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/lint v0.0.0-20200302205851-738671d3881b h1:Wh+f8QHJXR411sJR8/vRBTZ7YapZaRvUcLFFJhusH0k= golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ= golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= golang.org/x/oauth2 v0.6.0 h1:Lh8GPgSKBfWSwFvtuWOfeI3aAAnbXTSutYxJiOJFgIw= golang.org/x/oauth2 v0.6.0/go.mod h1:ycmewcwgD4Rpr3eZJLSB4Kyyljb3qDh40vJ8STE5HKw= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210616045830-e2b7044e8c71/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210819135213-f52c844e1c1c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.6.0 h1:clScbb1cHjoCkyRbWwBEUZ5H/tIFu5TAXIqaZD0Gcjw= golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.8.0 h1:57P1ETyNKtuIjB4SRd15iJxuhj8Gc416Y78H3qgMh68= golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.6.0 h1:BOw41kyTf3PuCW1pVQf8+Cyg8pMlkYB1oo9iJ6D/lKM= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= google.golang.org/api v0.111.0 h1:bwKi+z2BsdwYFRKrqwutM+axAlYLz83gt5pDSXCJT+0= google.golang.org/api v0.111.0/go.mod h1:qtFHvU9mhgTJegR31csQ+rwxyUTHOKFqCKWp1J0fdw0= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20230306155012-7f2fa6fef1f4 h1:DdoeryqhaXp1LtT/emMP1BRJPHHKFi5akj/nbx/zNTA= google.golang.org/genproto v0.0.0-20230306155012-7f2fa6fef1f4/go.mod h1:NWraEVixdDnqcqQ30jipen1STv2r/n24Wb7twVTGR4s= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= google.golang.org/grpc v1.53.0 h1:LAv2ds7cmFV/XTS3XG1NneeENYrXGmorPxsBbptIjNc= google.golang.org/grpc v1.53.0/go.mod h1:OnIrk0ipVdj4N5d9IUoFUx72/VlD7+jUsHwZgwSMQpw= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w= google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/yaml.v1 v1.0.0-20140924161607-9f9df34309c0/go.mod h1:WDnlLJ4WF5VGsH/HVa3CI79GS0ol3YnhVnKP89i0kNg= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= howett.net/plist v1.0.0 h1:7CrbWYbPPO/PyNy38b2EB/+gYbjCe2DXBxgtOOZbSQM= howett.net/plist v1.0.0/go.mod h1:lqaXoTrLY4hg8tnEzNru53gicrbv7rrk+2xJA/7hw9g= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= software.sslmate.com/src/go-pkcs12 v0.2.0 h1:nlFkj7bTysH6VkC4fGphtjXRbezREPgrHuJG20hBGPE= software.sslmate.com/src/go-pkcs12 v0.2.0/go.mod h1:23rNcYsMabIc1otwLpTkCCPwUq6kQsTyowttG/as0kQ= relic-7.6.1/internal/000077500000000000000000000000001455105530300144405ustar00rootroot00000000000000relic-7.6.1/internal/activation/000077500000000000000000000000001455105530300166015ustar00rootroot00000000000000relic-7.6.1/internal/activation/activatecmd/000077500000000000000000000000001455105530300210655ustar00rootroot00000000000000relic-7.6.1/internal/activation/activatecmd/cmd.go000066400000000000000000000053661455105530300221710ustar00rootroot00000000000000// Copyright © SAS Institute Inc. // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package activatecmd import ( "fmt" "net" "os" "os/exec" "strings" "syscall" ) var prefixes = []string{"NOTIFY_", "LISTEN_", "EINHORN_"} // ClearEnv removes all items from an environment list that might be interpreted // as a notification socket or inherited fd func ClearEnv(env []string) (ret []string) { for _, e := range env { ok := true for _, prefix := range prefixes { if strings.HasPrefix(e, prefix) { ok = false break } } if ok { ret = append(ret, e) } } return } type ListenerSet struct { files []*os.File } // NewListenerSet prepares a set of listeners that can be attached to child processes. // // The underyling files are duplicated, so the original Listener objects can be // closed if desired. func NewListenerSet(listeners []net.Listener) (*ListenerSet, error) { s := new(ListenerSet) for i, lis := range listeners { lf, ok := lis.(filer) if !ok { return nil, fmt.Errorf("unable to get file from listener %d (type %T)", i, lis) } f, err := lf.File() if err != nil { return nil, fmt.Errorf("unable to get file from listener %d: %w", i, err) } // File() puts the file description into blocking mode. Put it back and // leave it that way, otherwise it will race with child processes // trying to accept from it. if err := syscall.SetNonblock(int(f.Fd()), true); err != nil { return nil, fmt.Errorf("unable to get file from listener %d: %w", i, err) } s.files = append(s.files, f) } return s, nil } // Close frees the extra file descriptors owned by the listener set func (s *ListenerSet) Close() error { for _, f := range s.files { f.Close() } return nil } // Attach all the listeners in the set to a new child process. func (s *ListenerSet) Attach(cmd *exec.Cmd) error { if cmd.Env == nil { cmd.Env = ClearEnv(os.Environ()) } for i, f := range s.files { cmd.Env = append(cmd.Env, fmt.Sprintf("EINHORN_FD_%d=%d", i, 3+len(cmd.ExtraFiles))) cmd.ExtraFiles = append(cmd.ExtraFiles, f) } cmd.Env = append(cmd.Env, fmt.Sprintf("EINHORN_FD_COUNT=%d", len(s.files))) cmd.Env = append(cmd.Env, fmt.Sprintf("EINHORN_MASTER_PID=%d", os.Getpid())) return nil } type filer interface { File() (*os.File, error) } relic-7.6.1/internal/activation/activatecmd/notify_unix.go000066400000000000000000000121051455105530300237660ustar00rootroot00000000000000// Copyright © SAS Institute Inc. // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //go:build darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris // +build darwin dragonfly freebsd linux netbsd openbsd solaris package activatecmd import ( "context" "encoding/json" "errors" "fmt" "log" "net" "os" "os/exec" "strconv" "sync" "syscall" "time" "golang.org/x/sync/errgroup" "golang.org/x/sys/unix" "github.com/sassoftware/relic/v7/internal/closeonce" ) // Listener implements a channel that receives ready notifications from spawned processes type Listener struct { once sync.Once closed closeonce.Closed ready chan int stopping chan int eg *errgroup.Group ctx context.Context cancel context.CancelFunc } func (l *Listener) initialize() { l.ctx, l.cancel = context.WithCancel(context.Background()) l.eg, l.ctx = errgroup.WithContext(l.ctx) l.ready = make(chan int, 10) l.stopping = make(chan int, 10) } // Ready returns a channel that receives PIDs that are ready func (l *Listener) Ready() <-chan int { l.once.Do(l.initialize) return l.ready } // Stopping returns a channel that receives PIDs that are stopping func (l *Listener) Stopping() <-chan int { l.once.Do(l.initialize) return l.stopping } // Close shuts down all notification sockets func (l *Listener) Close() error { l.once.Do(l.initialize) return l.closed.Close(func() error { l.cancel() err := l.eg.Wait() close(l.ready) close(l.stopping) return err }) } // Attach a notification socket to a new child process. The returned "detach" // function must be invoked on cleanup, and should be invoked after Start() // returns. func (l *Listener) Attach(cmd *exec.Cmd) (detach func(), err error) { l.once.Do(l.initialize) if l.closed.Closed() { return nil, errors.New("listener is closed") } if cmd.Env == nil { cmd.Env = ClearEnv(os.Environ()) } parentEnd, childEnd, err := socketpair() if err != nil { return nil, err } cmd.Env = append(cmd.Env, fmt.Sprintf("EINHORN_SOCK_FD=%d", 3+len(cmd.ExtraFiles))) cmd.ExtraFiles = append(cmd.ExtraFiles, childEnd) go func() { <-l.ctx.Done() parentEnd.Close() }() l.eg.Go(func() error { return l.listen(parentEnd) }) detach = func() { childEnd.Close() } return detach, nil } // Consume packets from a socket and forward them to the main channel until ctx is cancelled func (l *Listener) listen(sock net.PacketConn) error { buf := make([]byte, 4096) failures := 0 var payload struct { Command string `json:"command"` PID int `json:"pid"` } for l.ctx.Err() == nil { n, _, err := sock.ReadFrom(buf) if err != nil { if l.ctx.Err() != nil { return nil } log.Printf("error: failed to read from notify socket: %s", err) failures++ if failures > 100 { return err } time.Sleep(100 * time.Millisecond) } failures = 0 if err := json.Unmarshal(buf[:n], &payload); err != nil { log.Printf("error: failed to decode notification: %s", err) continue } switch payload.Command { case "worker:ack": l.ready <- payload.PID case "worker:stopping": l.stopping <- payload.PID } } return nil } // Create a socketpair with the parent end wrapped in a PacketConn and the child end as a plain *os.File func socketpair() (parentEnd net.PacketConn, childEnd *os.File, err error) { files, err := socketpairFiles() if err != nil { return nil, nil, err } defer func() { // FilePacketConn will dup this so always close it _ = files[0].Close() }() childEnd = files[1] if err = unix.SetNonblock(int(files[0].Fd()), true); err == nil { parentEnd, err = net.FilePacketConn(files[0]) if err == nil { return parentEnd, childEnd, nil } } _ = files[1].Close() return nil, nil, err } // Create a socketpair as *os.File objects. must hold the fork lock to ensure that no file descriptors are leaked to a child process before CloseOnExec can be set. func socketpairFiles() (files [2]*os.File, err error) { syscall.ForkLock.RLock() defer syscall.ForkLock.RUnlock() fds, err := unix.Socketpair(unix.AF_UNIX, unix.SOCK_DGRAM, 0) if err == nil { unix.CloseOnExec(fds[0]) unix.CloseOnExec(fds[1]) files[0] = os.NewFile(uintptr(fds[0]), "") files[1] = os.NewFile(uintptr(fds[1]), "") } return } // DaemonStopping is used by the child process to indicate it is no longer // serving requests and will exit soon. func DaemonStopping() error { fd, err := strconv.Atoi(os.Getenv("EINHORN_SOCK_FD")) if err != nil { return err } message := fmt.Sprintf(`{"command":"worker:stopping", "pid":%d}`+"\n", os.Getpid()) _, err = unix.Write(fd, []byte(message)) return err } relic-7.6.1/internal/activation/listener.go000066400000000000000000000101521455105530300207540ustar00rootroot00000000000000// Copyright © SAS Institute Inc. // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // Package activation provides utilities for inheriting listening sockets from // systemd, einhorn, socketmaster, and crank. package activation import ( "errors" "net" "os" "strconv" "strings" "syscall" ) // GetListener checks if a daemon manager has passed a pre-activated listener // socket. If not, then net.Listener is used to open a new one. index starts at // 0 and increments for each additional socket being inherited. func GetListener(index uint, family, laddr string) (listener net.Listener, err error) { listener, err = einhornListener(index) if listener != nil || err != nil { return } listener, err = socketmasterListener(index) if listener != nil || err != nil { return } listener, err = systemdListener(index) if listener != nil || err != nil { return } if family == "unix" || family == "unixpacket" { os.Remove(laddr) } return net.Listen(family, laddr) } // Env vars are unset as they are read to avoid passing them to child // processes, but keep their values locally in case more than one socket is // being inherited var savedEnv map[string]string func popEnv(name string) string { if savedEnv == nil { savedEnv = make(map[string]string, 1) } val := savedEnv[name] if val != "" { return val } val = os.Getenv(name) savedEnv[name] = val os.Unsetenv(name) return val } func popEnvInt(name string) (int, error) { str := popEnv(name) if str == "" { return -1, nil } return strconv.Atoi(str) } func fdListener(fd uintptr) (net.Listener, error) { if err := syscall.SetNonblock(int(fd), true); err != nil { return nil, err } file := os.NewFile(fd, "FD_"+strconv.Itoa(int(fd))) // FileListener dupes the fd so make sure the originally inherited one gets closed defer file.Close() return net.FileListener(file) } func systemdListener(index uint) (net.Listener, error) { // systemd's socket activation places all fds sequentially starting at 3. // It also sets LISTEN_PID to this process' PID as a safety check. Other // runners may not set LISTEN_PID so don't worry if it's not set. pid, err := popEnvInt("LISTEN_PID") if err != nil || (pid != -1 && pid != os.Getpid()) { // This FD is not for us return nil, err } nfds, err := popEnvInt("LISTEN_FDS") if err != nil || nfds < int(index)+1 { return nil, err } return fdListener(uintptr(3 + index)) } func einhornListener(index uint) (net.Listener, error) { // github.com/stripe/einhorn // Verify the parent PID as a safety check. Each fd is passed in its own // environment variable. ppid, err := popEnvInt("EINHORN_MASTER_PID") if err != nil || (ppid != -1 && ppid != os.Getppid()) { // This FD is not for us return nil, err } numfds, err := popEnvInt("EINHORN_FD_COUNT") if err != nil || numfds < int(index)+1 { return nil, err } name := "EINHORN_FD_" + strconv.Itoa(int(index)) fd, err := popEnvInt(name) if err != nil { return nil, err } else if fd < 0 { return nil, errors.New("Missing environment variable " + name) } // Make sure the old-style var is not inherited by anybody os.Unsetenv("EINHORN_FDS") return fdListener(uintptr(fd)) } func socketmasterListener(index uint) (net.Listener, error) { // github.com/zimbatm/socketmaster // Old style of einhorn fd passing, which socketmaster emulates. Uses a // single environment variable with a space-separated list of fds. fdstr := popEnv("EINHORN_FDS") if fdstr == "" { return nil, nil } fds := strings.Split(fdstr, " ") if len(fds) < int(index)+1 { return nil, nil } fd, err := strconv.Atoi(fds[index]) if err != nil { return nil, err } return fdListener(uintptr(fd)) } relic-7.6.1/internal/activation/notify_unix.go000066400000000000000000000042371455105530300215110ustar00rootroot00000000000000// Copyright © SAS Institute Inc. // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //go:build !windows // +build !windows package activation import ( "fmt" "net" "os" "strconv" "syscall" ) // DaemonReady signals to a parent init system or daemon manager that the // daemon is finished starting up. Use after all listening sockets have been // opened. func DaemonReady() (err error) { if name := os.Getenv("NOTIFY_SOCKET"); name != "" { // systemd if err2 := writePath(name, "unixgram", "READY=1"); err2 != nil { err = err2 } } if fdstr := os.Getenv("NOTIFY_FD"); fdstr != "" { // github.com/pusher/crank if err2 := writeFd(fdstr, "READY=1"); err2 != nil { err = err2 } } if fdstr := os.Getenv("EINHORN_SOCK_FD"); fdstr != "" { // einhorn -g if err2 := writeFd(fdstr, einhornReadyStr()); err2 != nil { err = err2 } } else if name := os.Getenv("EINHORN_SOCK_PATH"); name != "" { // github.com/stripe/einhorn if err2 := writePath(name, "unix", einhornReadyStr()); err2 != nil { err = err2 } } return } // write a string to the unix socket at the named path func writePath(path, netType, message string) error { sockAddr := &net.UnixAddr{Name: path, Net: netType} conn, err := net.DialUnix(netType, nil, sockAddr) if err != nil { return err } defer conn.Close() _, err = conn.Write([]byte(message)) return err } // write a string to a numeric file descriptor func writeFd(fdstr, message string) error { fd, err := strconv.Atoi(fdstr) if err != nil { return err } _, err = syscall.Write(fd, []byte(message)) return err } func einhornReadyStr() string { return fmt.Sprintf(`{"command":"worker:ack", "pid":%d}`+"\n", os.Getpid()) } relic-7.6.1/internal/activation/notify_windows.go000066400000000000000000000012141455105530300222100ustar00rootroot00000000000000// Copyright © SAS Institute Inc. // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package activation func DaemonReady() error { return nil } relic-7.6.1/internal/authmodel/000077500000000000000000000000001455105530300164225ustar00rootroot00000000000000relic-7.6.1/internal/authmodel/authmodel.go000066400000000000000000000037611455105530300207420ustar00rootroot00000000000000package authmodel import ( "context" "net/http" "github.com/sassoftware/relic/v7/config" "github.com/sassoftware/relic/v7/internal/zhttp" "github.com/sassoftware/relic/v7/lib/audit" ) type ctxKey int var ctxKeyUserInfo ctxKey = 1 type Authenticator interface { Authenticate(req *http.Request) (UserInfo, error) } type UserInfo interface { // Allowed checks whether the named key is visible to the current user Allowed(*config.KeyConfig) bool // AuditContext amends an audit record with the authenticated user's name // and other relevant details AuditContext(info *audit.Info) } // New creates an authenticator based on the provided server configuration func New(conf *config.Config) (Authenticator, error) { switch { case conf.Server.PolicyURL != "": return newPolicyAuthenticator(conf) default: return &CertificateAuth{Config: conf}, nil } } // Middleware checks each request for authentication. If successful, the user's // information is appended to the log context and request context. If not, an // error is returned and the inner handler is skipped. func Middleware(a Authenticator) func(http.Handler) http.Handler { return func(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { info, err := a.Authenticate(r) if err != nil { if h, ok := err.(http.Handler); ok { h.ServeHTTP(w, r) } else { zhttp.WriteUnhandledError(w, r, err, "") } return } else if info == nil { panic("authenticator returned nil without an error") } ctx := r.Context() ctx = context.WithValue(ctx, ctxKeyUserInfo, info) r = r.WithContext(ctx) next.ServeHTTP(w, r) }) } } // RequestInfo returns information about the calling user func RequestInfo(req *http.Request) UserInfo { info, ok := req.Context().Value(ctxKeyUserInfo).(UserInfo) if !ok { // middleware should guarantee this is always present, otherwise // something is seriously wrong panic("userinfo missing from request context") } return info } relic-7.6.1/internal/authmodel/certificate.go000066400000000000000000000052011455105530300212310ustar00rootroot00000000000000package authmodel import ( "crypto/sha256" "crypto/x509" "encoding/hex" "net/http" "github.com/rs/zerolog" "github.com/sassoftware/relic/v7/config" "github.com/sassoftware/relic/v7/internal/httperror" "github.com/sassoftware/relic/v7/internal/realip" "github.com/sassoftware/relic/v7/internal/zhttp" "github.com/sassoftware/relic/v7/lib/audit" "github.com/sassoftware/relic/v7/lib/x509tools" ) // CertificateAuth requires all callers have a client certificate that either // has its fingerprint explicitly configured with access, or is signed by a // configured CA. type CertificateAuth struct { Config *config.Config } func (a *CertificateAuth) Authenticate(req *http.Request) (UserInfo, error) { peerCerts, err := realip.PeerCertificates(req) if err != nil { return nil, err } else if len(peerCerts) == 0 { return nil, httperror.ErrCertificateRequired } cert := peerCerts[0] encoded := fingerprint(cert) var useDN bool var saved error client := a.Config.Clients[encoded] if client == nil { for _, c2 := range a.Config.Clients { match, err := c2.Match(peerCerts) if match { client = c2 useDN = true break } else if err != nil { // preserve any potentially interesting validation errors saved = err } } } if client == nil { zhttp.AppendAccessLog(req, func(e *zerolog.Event) { e.Str("fingerprint", encoded) e.Str("subject", formatSubject(cert)) if saved != nil { e.AnErr("validation_error", saved) } }) return nil, httperror.ErrCertificateNotRecognized } user := &CertificateInfo{ Name: client.Nickname, Roles: client.Roles, } if user.Name == "" { user.Name = encoded[:12] } if useDN { user.Subject = formatSubject(cert) } // amend access log with user info zhttp.AppendAccessLog(req, func(e *zerolog.Event) { e.Str("user", user.Name) if user.Subject != "" { e.Str("subject", user.Subject) } }) return user, nil } type CertificateInfo struct { Name string Subject string Roles []string } func (c *CertificateInfo) AuditContext(info *audit.Info) { info.Attributes["client.name"] = c.Name if c.Subject != "" { info.Attributes["client.dn"] = c.Subject } } func (c *CertificateInfo) Allowed(keyConf *config.KeyConfig) bool { for _, keyRole := range keyConf.Roles { for _, clientRole := range c.Roles { if keyRole == clientRole { return true } } } return false } func fingerprint(cert *x509.Certificate) string { digest := sha256.Sum256(cert.RawSubjectPublicKeyInfo) return hex.EncodeToString(digest[:]) } func formatSubject(cert *x509.Certificate) string { return x509tools.FormatPkixName(cert.RawSubject, x509tools.NameStyleOpenSsl) } relic-7.6.1/internal/authmodel/metadata.go000066400000000000000000000011071455105530300205300ustar00rootroot00000000000000package authmodel type AuthType string const ( AuthTypeCertificate AuthType = "https://relic.sas.com/auth/certificate" AuthTypeBearerToken AuthType = "https://relic.sas.com/auth/bearer-token" AuthTypeAzureAD AuthType = "https://relic.sas.com/auth/azure-ad" ) type Metadata struct { Hosts []string `json:"hosts"` Auth []AuthMetadata `json:"auth"` } type AuthMetadata struct { Type AuthType `json:"type"` // azure AD Authority string `json:"authority,omitempty"` ClientID string `json:"client_id,omitempty"` Scopes []string `json:"scopes,omitempty"` } relic-7.6.1/internal/authmodel/opa.go000066400000000000000000000124121455105530300175300ustar00rootroot00000000000000package authmodel import ( "bytes" "context" "encoding/json" "fmt" "io" "net/http" "net/url" "strings" "github.com/rs/zerolog" "github.com/sassoftware/relic/v7/config" "github.com/sassoftware/relic/v7/internal/httperror" "github.com/sassoftware/relic/v7/internal/realip" "github.com/sassoftware/relic/v7/internal/zhttp" "github.com/sassoftware/relic/v7/lib/audit" ) type PolicyAuth struct { cli *http.Client destURL string usesDefault bool } func newPolicyAuthenticator(conf *config.Config) (*PolicyAuth, error) { usesDefault := !strings.Contains(conf.Server.PolicyURL, "/v1/data") return &PolicyAuth{ cli: new(http.Client), destURL: conf.Server.PolicyURL, usesDefault: usesDefault, }, nil } func (a *PolicyAuth) Authenticate(req *http.Request) (UserInfo, error) { // build input to policy input := policyInput{ Path: req.URL.Path, Query: req.URL.Query(), Token: bearerToken(req), } if peerCerts, err := realip.PeerCertificates(req); err != nil { return nil, err } else if len(peerCerts) != 0 { input.Fingerprint = fingerprint(peerCerts[0]) } if input.Token == "" && input.Fingerprint == "" { return nil, httperror.ErrTokenRequired } result, err := a.evaluate(req.Context(), input) if err != nil { return nil, err } // amend access log with authorization metadata zhttp.AppendAccessLog(req, func(e *zerolog.Event) { if result.Result.Subject != "" { e.Str("user", result.Result.Subject) } e.Object("policy", result) }) if !result.Result.Allow { code := http.StatusForbidden for _, e := range result.Result.Errors { if should401[e] { code = http.StatusUnauthorized } } return nil, httperror.TokenAuthorizationError(code, result.Result.Errors) } return &PolicyInfo{ Subject: result.Result.Subject, Roles: result.Result.Roles, AllowedKeys: result.Result.AllowedKeys, Claims: result.Result.Claims, }, nil } func (a *PolicyAuth) evaluate(ctx context.Context, input policyInput) (*policyResponse, error) { // marshal request var blob []byte var err error if a.usesDefault { // default decision takes just the input blob, err = json.Marshal(input) } else { // posting to a specific package needs a structured request blob, err = json.Marshal(policyRequest{Input: input}) } if err != nil { return nil, fmt.Errorf("marshaling decision: %w", err) } preq, err := http.NewRequestWithContext(ctx, http.MethodPost, a.destURL, bytes.NewReader(blob)) if err != nil { return nil, fmt.Errorf("executing decision: %w", err) } preq.Header.Set("Content-Type", "application/json") // execute decision resp, err := a.cli.Do(preq) if err != nil { return nil, fmt.Errorf("executing decision: %w", err) } defer resp.Body.Close() if resp.StatusCode >= 300 { err = httperror.FromResponse(resp) return nil, fmt.Errorf("executing decision: %w", err) } body, err := io.ReadAll(resp.Body) if err != nil { return nil, fmt.Errorf("parsing decision: %w", err) } decision := new(policyResponse) if err := json.Unmarshal(body, decision); err != nil { return nil, fmt.Errorf("parsing decision: %w", err) } return decision, nil } type PolicyInfo struct { Subject string Roles []string AllowedKeys []string Claims map[string]interface{} } // Allowed checks whether the named key is visible to the current user func (i *PolicyInfo) Allowed(keyConf *config.KeyConfig) bool { for _, allowedKey := range i.AllowedKeys { if allowedKey == keyConf.Name() { return true } } for _, keyRole := range keyConf.Roles { for _, allowedRole := range i.Roles { if keyRole == allowedRole { return true } } } return false } // AuditContext amends an audit record with the authenticated user's name // and other relevant details func (i *PolicyInfo) AuditContext(info *audit.Info) { info.Attributes["client.sub"] = i.Subject if v := i.Claims["iss"]; v != nil { info.Attributes["client.iss"] = v } } type policyRequest struct { Input policyInput `json:"input"` } type policyInput struct { Path string `json:"path"` Query url.Values `json:"query"` Token string `json:"token"` Fingerprint string `json:"fingerprint"` } type policyResponse struct { Result struct { Allow bool `json:"allow"` Subject string `json:"sub"` Errors []string `json:"errors"` Roles []string `json:"roles"` AllowedKeys []string `json:"allowed_keys"` Claims map[string]interface{} `json:"claims"` } `json:"result"` ID string `json:"decision_id"` } func (r *policyResponse) MarshalZerologObject(f *zerolog.Event) { if r.Result.Allow { f.Str("decision", "allowed") } else { f.Str("decision", "denied") } if len(r.Result.Errors) != 0 { f.Strs("errors", r.Result.Errors) } if len(r.Result.Claims) != 0 { f.Interface("claims", r.Result.Claims) } if r.ID != "" { f.Str("id", r.ID) } } func bearerToken(req *http.Request) string { auth := req.Header.Get("Authorization") const prefix = "Bearer " if len(auth) < len(prefix) || !strings.EqualFold(auth[:len(prefix)], prefix) { return "" } return auth[len(prefix):] } var should401 = map[string]bool{ "token is missing or not well-formed": true, "token issuer is not in known_issuers": true, "token is expired": true, "token is not yet valid": true, } relic-7.6.1/internal/closeonce/000077500000000000000000000000001455105530300164125ustar00rootroot00000000000000relic-7.6.1/internal/closeonce/closeonce.go000066400000000000000000000017231455105530300207160ustar00rootroot00000000000000// Copyright © SAS Institute Inc. // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package closeonce import ( "sync" "sync/atomic" ) type Closed struct { done uintptr mu sync.Mutex err error } func (o *Closed) Closed() bool { return atomic.LoadUintptr(&o.done) != 0 } func (o *Closed) Close(f func() error) error { o.mu.Lock() defer o.mu.Unlock() if o.Closed() { return o.err } o.err = f() atomic.StoreUintptr(&o.done, 1) o.done = 1 return o.err } relic-7.6.1/internal/httperror/000077500000000000000000000000001455105530300164715ustar00rootroot00000000000000relic-7.6.1/internal/httperror/problem.go000066400000000000000000000063241455105530300204650ustar00rootroot00000000000000package httperror import ( "encoding/json" "fmt" "net/http" "strings" "github.com/rs/zerolog" "github.com/sassoftware/relic/v7/internal/zhttp" ) // Problem implements a RFC 7807 HTTP "problem" response type Problem struct { Status int `json:"status"` Type string `json:"type"` Title string `json:"title,omitempty"` Detail string `json:"detail,omitempty"` Instance string `json:"instance,omitempty"` // error-specific Param string `json:"param,omitempty"` Errors []string `json:"errors,omitempty"` } func (e Problem) Error() string { title := e.Title if title == "" { title = "[" + e.Type + "]" } m := fmt.Sprintf("HTTP %d %s", e.Status, title) if e.Detail != "" { m += ": " + e.Detail } return m } func (e Problem) ServeHTTP(rw http.ResponseWriter, req *http.Request) { if e.Type != "" { zhttp.AppendAccessLog(req, func(ev *zerolog.Event) { ev.Str("problem", e.Type) }) } blob, _ := json.MarshalIndent(e, "", " ") rw.Header().Set("Content-Type", "application/problem+json") rw.WriteHeader(e.Status) _, _ = rw.Write(blob) } func (e Problem) Temporary() bool { return statusIsTemporary(e.Status) } const ( ProblemBase = "https://relic.sas.com/" ProblemKeyUsage = ProblemBase + "key-usage" ) var ( ErrForbidden = &Problem{ Status: http.StatusForbidden, Type: ProblemBase + "forbidden", } ErrCertificateRequired = &Problem{ Status: http.StatusUnauthorized, Type: ProblemBase + "certificate-required", Detail: "A client certificate must be provided to use this service", } ErrCertificateNotRecognized = &Problem{ Status: http.StatusUnauthorized, Type: ProblemBase + "certificate-not-recognized", Detail: "The provided client certificate was not recognized or does not grant access to any resources", } ErrTokenRequired = &Problem{ Status: http.StatusUnauthorized, Type: ProblemBase + "token-required", Detail: "A bearer token or client certificate must be provided to use this service", } ErrUnknownSignatureType = &Problem{ Status: http.StatusBadRequest, Type: ProblemBase + "unknown-signature-type", Detail: "Unknown signature type specified", } ErrUnknownDigest = &Problem{ Status: http.StatusBadRequest, Type: ProblemBase + "unknown-digest-algorithm", Detail: "Unknown digest algorithm specified", } ) func MissingParameterError(param string) Problem { return Problem{ Status: http.StatusBadRequest, Type: ProblemBase + "missing-parameter", Detail: "Parameter " + param + " is required", Param: param, } } func BadParameterError(err error) Problem { return Problem{ Status: http.StatusBadRequest, Type: ProblemBase + "bad-parameter", Detail: "Failed to parse signer parameters: " + err.Error(), } } func TokenAuthorizationError(code int, errors []string) Problem { p := Problem{ Status: code, Type: ProblemBase + "token-authorization-failed", Errors: errors, } if len(p.Errors) == 0 { p.Detail = "denied by policy" } else { p.Detail = strings.Join(errors, ", ") } return p } func NoCertificateError(certType string) Problem { return Problem{ Status: http.StatusBadRequest, Type: ProblemBase + "certificate-not-defined", Detail: "No certificate of type \"" + certType + "\" is defined for this key", } } relic-7.6.1/internal/httperror/response.go000066400000000000000000000023461455105530300206630ustar00rootroot00000000000000package httperror import ( "encoding/json" "fmt" "io" "net/http" "strings" ) type ResponseError struct { Method string URL string Status string StatusCode int BodyText string } func (e ResponseError) Error() string { return fmt.Sprintf("HTTP error:\n%s %s\n%s\n%s", e.Method, e.URL, e.Status, e.BodyText) } func (e ResponseError) Temporary() bool { return statusIsTemporary(e.StatusCode) } func FromResponse(resp *http.Response) error { defer resp.Body.Close() blob, err := io.ReadAll(io.LimitReader(resp.Body, 100000)) if err != nil { return err } if strings.Contains(resp.Header.Get("Content-Type"), "problem+json") { var p Problem if err := json.Unmarshal(blob, &p); err == nil { if p.Status == 0 { p.Status = resp.StatusCode } return p } } return ResponseError{ Method: resp.Request.Method, URL: resp.Request.URL.String(), Status: resp.Status, StatusCode: resp.StatusCode, BodyText: string(blob), } } func statusIsTemporary(code int) bool { switch code { case http.StatusGatewayTimeout, http.StatusBadGateway, http.StatusServiceUnavailable, http.StatusInsufficientStorage, http.StatusInternalServerError: return true default: return false } } relic-7.6.1/internal/httperror/temporary.go000066400000000000000000000007761455105530300210540ustar00rootroot00000000000000package httperror import ( "context" "errors" "io" "os" ) type temporary interface { Temporary() bool } func Temporary(err error) bool { if err == nil { return false } if e, ok := err.(temporary); ok && e.Temporary() { return true } switch { case errors.As(err, new(*os.SyscallError)): return true case errors.Is(err, context.Canceled): return true case errors.Is(err, context.DeadlineExceeded): return true case errors.Is(err, io.ErrUnexpectedEOF): return true } return false } relic-7.6.1/internal/logrotate/000077500000000000000000000000001455105530300164405ustar00rootroot00000000000000relic-7.6.1/internal/logrotate/logrotate.go000066400000000000000000000033321455105530300207700ustar00rootroot00000000000000// Copyright © SAS Institute Inc. // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package logrotate import ( "os" "sync" ) type Writer struct { path string f *os.File fi os.FileInfo mu sync.Mutex } func NewWriter(path string) (*Writer, error) { w := &Writer{path: path} return w, w.openLocked() } func (w *Writer) openLocked() error { // open for appending f, err := os.OpenFile(w.path, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0666) if err != nil { return err } // stat file to get inode to compare against later fi, err := f.Stat() if err != nil { return err } if w.f != nil { w.f.Close() } w.f = f w.fi = fi return nil } func (w *Writer) reopenLocked() error { if w.f != nil { fi, err := os.Stat(w.path) if err != nil && !os.IsNotExist(err) { return err } if os.SameFile(fi, w.fi) { // file has not changed; do nothing return nil } } // file missing or changed, reopen return w.openLocked() } func (w *Writer) Write(d []byte) (n int, err error) { w.mu.Lock() err = w.reopenLocked() if err == nil { n, err = w.f.Write(d) } w.mu.Unlock() return } func (w *Writer) Close() (err error) { w.mu.Lock() if w.f != nil { err = w.f.Close() w.f = nil } w.mu.Unlock() return } relic-7.6.1/internal/realip/000077500000000000000000000000001455105530300157145ustar00rootroot00000000000000relic-7.6.1/internal/realip/realip.go000066400000000000000000000123351455105530300175230ustar00rootroot00000000000000// Copyright © SAS Institute Inc. // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package realip import ( "context" "crypto/x509" "encoding/pem" "fmt" "net" "net/http" "net/url" "strings" "github.com/sassoftware/relic/v7/internal/zhttp" ) const ( forwardedFor = "X-Forwarded-For" forwardedHost = "X-Forwarded-Host" forwardedProto = "X-Forwarded-Proto" sslClientCert = "Ssl-Client-Cert" ) // Middleware processes headers set by a trusted reverse proxy in front of the // server. Client IPs are checked against the provided list of networks, and the // "real" IP passed by the trusted proxy replaces req.RemoteAddr. func Middleware(trustedProxies []string) (func(http.Handler) http.Handler, error) { // parse net list trustedNets, err := parseTrusted(trustedProxies) if err != nil { return nil, err } return func(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { remoteIP, proxied := trustedClient(trustedNets, r) r.RemoteAddr = remoteIP if proxied { ctx := context.WithValue(r.Context(), ctxKeyTrusted, proxied) r = r.WithContext(ctx) } next.ServeHTTP(w, r) }) }, nil } // BaseURL returns an estimate of the URL that the client used to access the // service. Headers like X-Forwarded-Host are taken into account if the // connection is from a trusted proxy. func BaseURL(req *http.Request) *url.URL { u := &url.URL{Scheme: "http", Host: req.Host} if req.TLS != nil { u.Scheme = "https" } if requestTrusted(req) { if host := req.Header.Get(forwardedHost); host != "" { u.Host = host } if scheme := req.Header.Get(forwardedProto); scheme != "" { u.Scheme = scheme } } return u } // PeerCertificates returns the unverified set of certificates provided by the // client, using the Ssl-Client-Cert header if the connection is from a trusted // proxy. func PeerCertificates(req *http.Request) ([]*x509.Certificate, error) { if !requestTrusted(req) { // direct untrusted connection if req.TLS != nil { return req.TLS.PeerCertificates, nil } return nil, nil } certString := req.Header.Get(sslClientCert) if certString == "" { return nil, nil } certString, err := url.PathUnescape(certString) if err != nil { return nil, fmt.Errorf("%s: invalid URL encoding: %w", sslClientCert, err) } pemBytes := []byte(certString) var ret []*x509.Certificate for { var b *pem.Block b, pemBytes = pem.Decode(pemBytes) if b == nil { break } else if b.Type != "CERTIFICATE" { continue } cert, err := x509.ParseCertificate(b.Bytes) if err != nil { return nil, fmt.Errorf("%s: parsing certificate %d: %w", sslClientCert, len(ret), err) } ret = append(ret, cert) } return ret, nil } func parseTrusted(trustedProxies []string) ([]*net.IPNet, error) { var trustedNets []*net.IPNet for _, v := range trustedProxies { var ipnet *net.IPNet if strings.ContainsRune(v, '/') { var err error _, ipnet, err = net.ParseCIDR(v) if err != nil { return nil, fmt.Errorf("trusted_proxies %q: %w", v, err) } } else { ip := net.ParseIP(v) if ip == nil { return nil, fmt.Errorf("trusted_proxies %q: invalid IP or IP network", v) } ipnet = &net.IPNet{IP: ip} if ip.To4() != nil { ipnet.Mask = net.CIDRMask(32, 32) } else { ipnet.Mask = net.CIDRMask(128, 128) } } trustedNets = append(trustedNets, ipnet) } return trustedNets, nil } func trustedClient(trustedNets []*net.IPNet, req *http.Request) (string, bool) { remoteIP := zhttp.StripPort(req.RemoteAddr) if !hopTrusted(trustedNets, remoteIP) { // first hop is not trusted return remoteIP, false } // Parse all XFF headers into a single list of hops var hops []string for _, xff := range req.Header.Values(forwardedFor) { for _, hop := range strings.Split(xff, ",") { hop = strings.TrimSpace(hop) if hop != "" { hops = append(hops, hop) } } } // Check each hop, starting from the closest one to us for i := len(hops) - 1; i >= 0; i-- { nextHop := strings.TrimSpace(hops[i]) if !hopTrusted(trustedNets, nextHop) { // The previous hop is trusted but this one is not, so this is the // best client IP return nextHop, true } } // No untrusted hops were found, so whatever the last one in the chain is is // the client IP. if len(hops) != 0 { return hops[0], true } return remoteIP, false } func hopTrusted(trustedNets []*net.IPNet, addr string) bool { if addr == "@" { // UNIX socket return true } ip := net.ParseIP(addr) if ip == nil { return false } for _, p := range trustedNets { if p.Contains(ip) { return true } } return false } type ctxKey int var ctxKeyTrusted ctxKey = 1 func requestTrusted(req *http.Request) bool { trusted, _ := req.Context().Value(ctxKeyTrusted).(bool) return trusted } relic-7.6.1/internal/realip/realip_test.go000066400000000000000000000037171455105530300205660ustar00rootroot00000000000000package realip import ( "net/http" "net/http/httptest" "strings" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) func TestTrusted(t *testing.T) { nets, err := parseTrusted([]string{"10.0.0.0/8"}) require.NoError(t, err) cases := []struct { Case, IP, XFF, Expected string Trusted bool }{ // direct from an untrusted IP {"Direct", "172.16.0.1", "", "172.16.0.1", false}, // direct from a trusted IP {"DirectInternal", "10.0.0.1", "", "10.0.0.1", false}, // direct from an untrusted IP with an untrusted hop {"DirectUntrusted", "10.0.0.1", "", "10.0.0.1", false}, // connected via trusted proxy {"OneHop", "10.0.0.1", "192.168.100.1", "192.168.100.1", true}, // connected via trusted proxy and the client is also a trusted IP {"OneHopInternal", "10.0.0.1", "10.0.0.2", "10.0.0.2", true}, // connected via trusted proxy with an untrusted hop {"OneHopUntrusted", "10.0.0.1", "172.16.0.1, 192.168.100.1", "192.168.100.1", true}, // connected via two trusted proxies {"TwoHops", "10.0.0.1", "192.168.100.1, 10.0.0.2", "192.168.100.1", true}, } for _, c := range cases { t.Run(c.Case, func(t *testing.T) { req := httptest.NewRequest(http.MethodGet, "/", nil) req.RemoteAddr = c.IP + ":12345" if c.XFF != "" { req.Header.Set("X-Forwarded-For", c.XFF) } actual, trusted := trustedClient(nets, req) assert.Equal(t, c.Expected, actual) assert.Equal(t, c.Trusted, trusted) req.Header.Del("X-Forwarded-For") }) if !strings.ContainsRune(c.XFF, ',') { continue } // do it again with separate XFF headers t.Run(c.Case+"Multi", func(t *testing.T) { req := httptest.NewRequest(http.MethodGet, "/", nil) req.RemoteAddr = c.IP + ":12345" for _, v := range strings.Split(c.XFF, ",") { req.Header.Add("X-Forwarded-For", v) } actual, trusted := trustedClient(nets, req) assert.Equal(t, c.Expected, actual) assert.Equal(t, c.Trusted, trusted) }) } } relic-7.6.1/internal/signinit/000077500000000000000000000000001455105530300162645ustar00rootroot00000000000000relic-7.6.1/internal/signinit/signinit.go000066400000000000000000000055571455105530300204530ustar00rootroot00000000000000// // Copyright (c) SAS Institute Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package signinit import ( "context" "crypto" "fmt" "time" "github.com/sassoftware/relic/v7/cmdline/shared" "github.com/sassoftware/relic/v7/config" "github.com/sassoftware/relic/v7/lib/audit" "github.com/sassoftware/relic/v7/lib/certloader" "github.com/sassoftware/relic/v7/signers" "github.com/sassoftware/relic/v7/signers/sigerrors" "github.com/sassoftware/relic/v7/token" ) // InitKey loads the cert chain for a key func InitKey(ctx context.Context, tok token.Token, keyName string) (*certloader.Certificate, *config.KeyConfig, error) { key, err := tok.GetKey(ctx, keyName) if err != nil { return nil, nil, err } kconf := key.Config() // parse certificates cert, err := certloader.LoadTokenCertificates(key, kconf.X509Certificate, kconf.PgpCertificate, key.Certificate()) if err != nil { return nil, nil, err } cert.KeyName = keyName return cert, kconf, nil } // InitKey prepares to sign using the named key, preparing a cert chain and // signing options according to the server configuration func Init(ctx context.Context, mod *signers.Signer, tok token.Token, keyName string, hash crypto.Hash, flags *signers.FlagValues) (*certloader.Certificate, *signers.SignOpts, error) { cert, kconf, err := InitKey(ctx, tok, keyName) if err != nil { return nil, nil, err } // create audit info auditInfo := audit.New(kconf.Name(), mod.Name, hash) now := time.Now().UTC() auditInfo.SetTimestamp(now) if cert.Leaf != nil { auditInfo.SetX509Cert(cert.Leaf) } else if mod.CertTypes&signers.CertTypeX509 != 0 { return nil, nil, sigerrors.ErrNoCertificate{Type: "x509"} } if cert.PgpKey != nil { auditInfo.SetPgpCert(cert.PgpKey) } else if mod.CertTypes&signers.CertTypePgp != 0 { return nil, nil, sigerrors.ErrNoCertificate{Type: "pgp"} } if kconf.Timestamp && !flags.GetBool("no-timestamp") { cert.Timestamper, err = GetTimestamper() if err != nil { return nil, nil, err } } opts := signers.SignOpts{ Hash: hash, Time: now, Audit: auditInfo, Flags: flags, } opts = opts.WithContext(ctx) return cert, &opts, nil } func PublishAudit(info *audit.Info) error { aconf := shared.CurrentConfig.Amqp if aconf != nil && aconf.URL != "" { if err := info.Publish(aconf); err != nil { return fmt.Errorf("failed to publish audit log: %w", err) } } return nil } relic-7.6.1/internal/signinit/timestamper.go000066400000000000000000000023401455105530300211440ustar00rootroot00000000000000// Copyright © SAS Institute Inc. // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package signinit import ( "sync" "github.com/sassoftware/relic/v7/cmdline/shared" "github.com/sassoftware/relic/v7/lib/pkcs9" "github.com/sassoftware/relic/v7/lib/pkcs9/tsclient" ) var ( mu sync.Mutex ts pkcs9.Timestamper ) func GetTimestamper() (pkcs9.Timestamper, error) { mu.Lock() defer mu.Unlock() var err error if ts == nil { ts, err = newTimestamper() } return ts, err } func newTimestamper() (timestamper pkcs9.Timestamper, err error) { tsconf, err := shared.CurrentConfig.GetTimestampConfig() if err != nil { return nil, err } timestamper, err = tsclient.New(tsconf) if err != nil { return } return timestamper, nil } relic-7.6.1/internal/workerrpc/000077500000000000000000000000001455105530300164565ustar00rootroot00000000000000relic-7.6.1/internal/workerrpc/workerrpc.go000066400000000000000000000016661455105530300210340ustar00rootroot00000000000000// // Copyright (c) SAS Institute Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package workerrpc const ( Ping = "/ping" GetKey = "/getKey" Sign = "/sign" ) type Request struct { KeyName string KeyID []byte Digest []byte Hash uint SaltLength *int } type Response struct { Value []byte ID []byte Cert []byte Key string Err string Retryable bool Usage bool } relic-7.6.1/internal/zhttp/000077500000000000000000000000001455105530300156115ustar00rootroot00000000000000relic-7.6.1/internal/zhttp/logging.go000066400000000000000000000107331455105530300175720ustar00rootroot00000000000000package zhttp import ( "context" "fmt" stdlog "log" "net/http" "os" "strings" "time" "github.com/rs/zerolog" "github.com/rs/zerolog/log" "github.com/sassoftware/relic/v7/internal/logrotate" ) type ctxKey int var ( ctxAccessCallbacks ctxKey = 1 ctxDontLog ctxKey = 2 ) const rfc3339Milli = "2006-01-02T15:04:05.000Z07:00" // RFC3339 with 3 decimal places, padded // SetupLogging initializes zerolog with reasonable defaults func SetupLogging(levelName, logFile string) error { zerolog.TimeFieldFormat = rfc3339Milli zerolog.DurationFieldInteger = true switch logFile { case "-": // write JSON to stderr case "": // write pretty text to stderr log.Logger = log.Logger.Output(zerolog.ConsoleWriter{ Out: os.Stderr, TimeFormat: "15:04:05", }) default: // write JSON to file w, err := logrotate.NewWriter(logFile) if err != nil { return fmt.Errorf("log_file: %w", err) } log.Logger = log.Logger.Output(w) } // set default log level if levelName == "" { levelName = zerolog.InfoLevel.String() } level, err := zerolog.ParseLevel(levelName) if err != nil { return fmt.Errorf("log_level: %w", err) } log.Logger = log.Logger.Level(level) // pass stdlib logger through stdlog.SetFlags(0) stdlog.SetOutput(log.Logger) return nil } // LoggingMiddleware creates a logging context for each request, and emits an // access log entry at the completion of the request. func LoggingMiddleware(opts ...LoggingOption) func(http.Handler) http.Handler { cfg := loggingConfig{ logger: log.Logger, now: time.Now, } for _, o := range opts { o(&cfg) } return func(next http.Handler) http.Handler { return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { // Make a new log context for the scope of the request with basic // request metadata suitable for every log entry lc := cfg.logger.With(). Str("ip", StripPort(req.RemoteAddr)). Str("req_id", req.Header.Get("X-Request-Id")) // build request context and execute the next handler baseLogger := lc.Logger() ctx := req.Context() ctx = baseLogger.WithContext(ctx) var callbacks []AccessLogCallback var dontLog bool ctx = context.WithValue(ctx, ctxAccessCallbacks, &callbacks) ctx = context.WithValue(ctx, ctxDontLog, &dontLog) start := cfg.now() lw := &Logger{ ResponseWriter: rw, Now: cfg.now, } req = req.WithContext(ctx) next.ServeHTTP(lw, req) // emit the access log entry if !dontLog { // Logger.WithContext makes a copy of the logger to put into // ctx, so make sure we use that as a base to get the effects of // any UpdateContext calls from inside the view funcs. logger := zerolog.Ctx(ctx) ev := logger.Info(). Str("method", req.Method). Stringer("url", req.URL). Int("status", lw.Status()). Int64("len", lw.Length()). Dur("dur", cfg.now().Sub(start)). Dur("ttfb", lw.Started().Sub(start)). Str("ua", req.UserAgent()) for _, cb := range callbacks { cb(ev) } ev.Send() } }) } } type AccessLogCallback func(*zerolog.Event) // AppendAccessLog adds a callback function which will be invoked to amend the // access log with additional fields. func AppendAccessLog(req *http.Request, f AccessLogCallback) { AppendAccessLogContext(req.Context(), f) } // AppendAccessLog adds a callback function which will be invoked to amend the // access log with additional fields. func AppendAccessLogContext(ctx context.Context, f AccessLogCallback) { callbacks, _ := ctx.Value(ctxAccessCallbacks).(*[]AccessLogCallback) if callbacks != nil { *callbacks = append(*callbacks, f) } } // DontLog marks that the current request should not generate an access log // entry func DontLog(req *http.Request) { dontLog, _ := req.Context().Value(ctxDontLog).(*bool) if dontLog != nil { *dontLog = true } } type loggingConfig struct { logger zerolog.Logger now func() time.Time } type LoggingOption func(*loggingConfig) // WithLogger sets the base logger for the middleware func WithLogger(logger zerolog.Logger) LoggingOption { return func(lc *loggingConfig) { lc.logger = logger } } // StripPort returns just the IP part from e.g. Request.RemoteAddr func StripPort(clientIP string) string { i := strings.IndexByte(clientIP, ':') j := strings.IndexByte(clientIP, ']') if j > 1 && clientIP[0] == '[' { // [fe80::]:1234 return clientIP[1:j] } else if i > 0 && strings.Count(clientIP, ":") == 1 { // 127.0.0.1:1234 return clientIP[:i] } return clientIP } relic-7.6.1/internal/zhttp/logging_test.go000066400000000000000000000043461455105530300206340ustar00rootroot00000000000000package zhttp import ( "bytes" "net/http" "net/http/httptest" "testing" "time" "github.com/rs/zerolog" "github.com/rs/zerolog/hlog" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) func TestLoggingMiddleware(t *testing.T) { h := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { switch r.URL.Path { case "/dontlog": DontLog(r) case "/changeme": r.URL.Path = "/changed" default: http.NotFound(w, r) return } // updates to the log context affect every message hlog.FromRequest(r).UpdateContext(func(c zerolog.Context) zerolog.Context { return c.Str("append1", "always") }) // append only to the access log without affecting the context AppendAccessLog(r, func(e *zerolog.Event) { e.Str("append2", "access") }) hlog.FromRequest(r).Info().Msg("a message") }) var buf bytes.Buffer logger := zerolog.New(&buf) now := fakeTime() mw := LoggingMiddleware( WithLogger(logger), func(lc *loggingConfig) { lc.now = now }, ) newReq := func(path string) *http.Request { r := httptest.NewRequest(http.MethodGet, path, nil) r.RemoteAddr = "192.168.1.1:12345" r.Header.Set("X-Request-Id", "00000000") r.Header.Set("User-Agent", "unittest") return r } t.Run("Changed", func(t *testing.T) { buf.Reset() r, w := newReq("/changeme"), httptest.NewRecorder() mw(h).ServeHTTP(w, r) resp := w.Result() require.Equal(t, http.StatusOK, resp.StatusCode) assert.Equal(t, `{"level":"info","ip":"192.168.1.1","req_id":"00000000","append1":"always","message":"a message"} {"level":"info","ip":"192.168.1.1","req_id":"00000000","append1":"always","method":"GET","url":"/changed","status":200,"len":0,"dur":1000,"ttfb":2000,"ua":"unittest","append2":"access"} `, buf.String()) }) t.Run("DontLog", func(t *testing.T) { buf.Reset() r, w := newReq("/dontlog"), httptest.NewRecorder() mw(h).ServeHTTP(w, r) resp := w.Result() require.Equal(t, http.StatusOK, resp.StatusCode) assert.Equal(t, `{"level":"info","ip":"192.168.1.1","req_id":"00000000","append1":"always","message":"a message"} `, buf.String()) }) } func fakeTime() func() time.Time { ts := time.Date(2001, 1, 1, 0, 0, 0, 0, time.UTC) return func() time.Time { ts = ts.Add(time.Second) return ts } } relic-7.6.1/internal/zhttp/recovery.go000066400000000000000000000031631455105530300200010ustar00rootroot00000000000000package zhttp import ( "context" "fmt" "net/http" "runtime" "strings" "github.com/rs/zerolog" ) // RecoveryMiddleware catches panics, logs the error, and writes a generic Internal Server Error response func RecoveryMiddleware(next http.Handler) http.Handler { return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { defer func() { if caught := recover(); caught != nil { if caught == http.ErrAbortHandler { // explicit signal to stop panic(caught) } const size = 64 << 10 buf := make([]byte, size) buf = buf[:runtime.Stack(buf, false)] tb := "\n " + strings.Replace(string(buf), "\n", "\n ", -1) err, ok := caught.(error) if !ok { err = fmt.Errorf("%v", caught) } WriteUnhandledError(rw, req, err, tb) } }() next.ServeHTTP(rw, req) }) } // WriteUnhandledError writes a generic 500 Internal Server Error response while // logging the actual unhandled error and optional traceback func WriteUnhandledError(w http.ResponseWriter, req *http.Request, err error, traceback string) { status := http.StatusInternalServerError field := "error" text := "An unhandled exception occurred while processing your request. Please contact your administrator." if e := req.Context().Err(); e != nil { field = "cancel" text = "" if e == context.DeadlineExceeded { status = http.StatusGatewayTimeout } else { // borrow nginx's fake 499 status for client closing connection status = 499 } } AppendAccessLog(req, func(e *zerolog.Event) { e.AnErr(field, err) if traceback != "" { e.Str("stack", traceback) } }) http.Error(w, text, status) } relic-7.6.1/internal/zhttp/responselogger.go000066400000000000000000000062241455105530300212020ustar00rootroot00000000000000package zhttp import ( "bufio" "fmt" "net" "net/http" "time" ) // Logger wraps a ResponseWriter and records the resulting status code // and how many bytes are written type Logger struct { http.ResponseWriter length int64 status int started time.Time Now func() time.Time } // Write implements ResponseWriter func (l *Logger) Write(d []byte) (size int, err error) { if l.status == 0 { l.WriteHeader(http.StatusOK) } size, err = l.ResponseWriter.Write(d) l.length += int64(size) return } // WriteHeader implements ResponseWriter func (l *Logger) WriteHeader(status int) { // suppress duplicate WriteHeader calls, but do save the status code if l.status == 0 { l.ResponseWriter.WriteHeader(status) l.started = l.Now() } l.status = status } // Flush wraps a nested Flusher func (l *Logger) Flush() { if flusher, ok := l.ResponseWriter.(http.Flusher); ok { flusher.Flush() } } // Hijack wraps a nested Hijacker func (l *Logger) Hijack() (net.Conn, *bufio.ReadWriter, error) { if h, ok := l.ResponseWriter.(http.Hijacker); ok { conn, rw, err := h.Hijack() if err == nil && l.status == 0 { l.status = http.StatusSwitchingProtocols } if hc, ok := conn.(halfCloser); ok { // more featureful for tcp.Conn common case conn = halfCloseLogger{halfCloser: hc, rl: l} } else { conn = hijackLogger{Conn: conn, rl: l} } return conn, rw, err } return nil, nil, fmt.Errorf("Hijacker interface not supported by type %T", l.ResponseWriter) } // CloseNotify wraps a nested CloseNotifier func (l *Logger) CloseNotify() <-chan bool { //nolint // provided for backwards compatibility if cn, ok := l.ResponseWriter.(http.CloseNotifier); ok { return cn.CloseNotify() } return make(chan bool) } // Push wraps a nested Pusher func (l *Logger) Push(target string, opts *http.PushOptions) error { if p, ok := l.ResponseWriter.(http.Pusher); ok { return p.Push(target, opts) } return fmt.Errorf("Pusher interface not supported by type %T", l.ResponseWriter) } // Length returns the number of bytes written func (l *Logger) Length() int64 { return l.length } // Status returns the response status func (l *Logger) Status() int { if l.status == 0 { return http.StatusOK } return l.status } // Started returns the time at which headers were written func (l *Logger) Started() time.Time { if l.started.IsZero() { return l.Now() } return l.started } // hijackLogger wraps a hijacked connection in order to enable WriteErrorReason // to set the status of the request for logging purposes type hijackLogger struct { net.Conn rl *Logger } // SetStatus records the final status of a hijacked connection func (h hijackLogger) SetStatus(status int, length int64) { h.rl.status = status h.rl.length = length } type halfCloser interface { net.Conn CloseRead() error CloseWrite() error } // halfCloseLogger wraps a hijacked connection in order to enable // WriteErrorReason to set the status of the request for logging purposes type halfCloseLogger struct { halfCloser rl *Logger } // SetStatus records the final status of a hijacked connection func (h halfCloseLogger) SetStatus(status int, length int64) { h.rl.status = status h.rl.length = length } relic-7.6.1/lib/000077500000000000000000000000001455105530300133725ustar00rootroot00000000000000relic-7.6.1/lib/appmanifest/000077500000000000000000000000001455105530300157015ustar00rootroot00000000000000relic-7.6.1/lib/appmanifest/publictoken.go000066400000000000000000000117671455105530300205630ustar00rootroot00000000000000// // Copyright (c) SAS Institute Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package appmanifest import ( "bytes" "crypto" "crypto/ecdsa" "crypto/elliptic" "crypto/rsa" "encoding/binary" "encoding/hex" "errors" "math/big" "github.com/sassoftware/relic/v7/lib/certloader" "github.com/sassoftware/relic/v7/lib/x509tools" ) func PublisherIdentity(cert *certloader.Certificate) (string, string, error) { issuer := cert.Issuer() if issuer == nil { return "", "", errors.New("unable to find issuer certificate in chain") } aki, err := x509tools.SubjectKeyID(issuer.PublicKey) if err != nil { return "", "", err } name := x509tools.FormatPkixName(cert.Leaf.RawSubject, x509tools.NameStyleMsOsco) return name, hex.EncodeToString(aki), nil } const ( snkRsaPub = 0x06 snkRsaVersion = 0x02 // https://msdn.microsoft.com/en-us/library/windows/desktop/aa375549(v=vs.85).aspx calgRsaSign = 0x2400 // CALG_RSA_SIGN calgSha1 = 0x8004 // CALG_SHA1 calgEcdsa = 0x2203 // CALG_ECDSA // bcrypt.h bcryptRsaPubMagic = 0x31415352 // BCRYPT_RSAPUBLIC_MAGIC bcryptEcdsaPubP256 = 0x31534345 // BCRYPT_ECDSA_PUBLIC_P256_MAGIC bcryptEcdsaPubP384 = 0x33534345 // BCRYPT_ECDSA_PUBLIC_P384_MAGIC bcryptEcdsaPubP521 = 0x35534345 // BCRYPT_ECDSA_PUBLIC_P521_MAGIC ) type snkHeader struct { PubAlgorithm uint32 HashAlgorithm uint32 BlobSize uint32 KeyType uint8 Version uint8 Reserved uint16 PubAlgorithm2 uint32 } type blobRsaPub struct { snkHeader // BCRYPT_RSAKEY_BLOB KeyMagic uint32 BitLength uint32 PubExponent uint32 } // N [BitLength/8]byte type blobEcdsaPub struct { snkHeader // BCRYPT_ECCKEY_BLOB KeyMagic uint32 ByteLength uint32 } // X, Y [ByteLength]byte // Calculate the publicKeyToken from a public key. This involves mangling it // into a .snk file format, then hashing it. // // http://www.developerfusion.com/article/84422/the-key-to-strong-names/ func PublicKeyToken(pubKey crypto.PublicKey) (string, error) { snk, err := PublicKeyToSnk(pubKey) if err != nil { return "", err } d := crypto.SHA1.New() d.Write(snk) // token is the low 64 bits of sum decoded as a little-endian number, or in // other words the last 8 bytes in reverse order sum := d.Sum(nil) token := make([]byte, 8) for i := 0; i < 8; i++ { token[i] = sum[19-i] } return hex.EncodeToString(token), nil } // Convert public key to "snk" format func PublicKeyToSnk(pubKey crypto.PublicKey) ([]byte, error) { var buf bytes.Buffer switch k := pubKey.(type) { case *rsa.PublicKey: modulus := bigIntToLE(k.N) if err := binary.Write(&buf, binary.LittleEndian, blobRsaPub{ snkHeader: snkHeader{ PubAlgorithm: calgRsaSign, HashAlgorithm: calgSha1, BlobSize: uint32(20 + len(modulus)), KeyType: snkRsaPub, Version: snkRsaVersion, PubAlgorithm2: calgRsaSign, }, KeyMagic: bcryptRsaPubMagic, BitLength: uint32(8 * len(modulus)), PubExponent: uint32(k.E), }); err != nil { return nil, nil } buf.Write(modulus) case *ecdsa.PublicKey: // TODO: This is a best guess based on piecing together various // Microsoft documentation and header values, but some pieces are still // missing. ECDSA isn't supported for strong name signing, and // calcuating the publicKeyToken for SN is the only reason this // function is even here. var keyMagic uint32 switch k.Curve { case elliptic.P256(): keyMagic = bcryptEcdsaPubP256 case elliptic.P384(): keyMagic = bcryptEcdsaPubP384 case elliptic.P521(): keyMagic = bcryptEcdsaPubP521 default: return nil, errors.New("unsupported ECDSA curve") } // TODO: are these supposed to be big-endian? the documentation for // BCRYPT_ECCKEY_BLOB says so, but it also said that about the RSA one // and yet the SNK format actually uses little endian... x := k.X.Bytes() y := k.Y.Bytes() if err := binary.Write(&buf, binary.LittleEndian, blobEcdsaPub{ snkHeader: snkHeader{ PubAlgorithm: calgEcdsa, HashAlgorithm: calgSha1, BlobSize: uint32(12 + 2*len(x)), KeyType: snkRsaPub, // TODO Version: snkRsaVersion, // TODO PubAlgorithm2: calgEcdsa, }, KeyMagic: keyMagic, ByteLength: uint32(len(x)), }); err != nil { return nil, nil } buf.Write(x) buf.Write(y) default: return nil, errors.New("unsupported key type for strong name signing") } return buf.Bytes(), nil } func bigIntToLE(x *big.Int) []byte { b := x.Bytes() for i := 0; i < len(b)/2; i++ { j := len(b) - i - 1 b[i], b[j] = b[j], b[i] } return b } relic-7.6.1/lib/appmanifest/signmanifest.go000066400000000000000000000157671455105530300207370ustar00rootroot00000000000000// // Copyright (c) SAS Institute Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package appmanifest import ( "bytes" "crypto" "encoding/base64" "encoding/hex" "errors" "fmt" "github.com/beevik/etree" "github.com/sassoftware/relic/v7/lib/certloader" "github.com/sassoftware/relic/v7/lib/pkcs7" "github.com/sassoftware/relic/v7/lib/pkcs9" "github.com/sassoftware/relic/v7/lib/xmldsig" ) const ( NsMsRel = "http://schemas.microsoft.com/windows/rel/2005/reldata" NsMpeg21 = "urn:mpeg:mpeg21:2003:01-REL-R-NS" NsAuthenticode = "http://schemas.microsoft.com/windows/pki/2005/Authenticode" ) type SignedManifest struct { ManifestSignature Signed []byte EncryptedDigest []byte } // Sign an application manifest func Sign(manifest []byte, cert *certloader.Certificate, opts crypto.SignerOpts) (*SignedManifest, error) { doc := etree.NewDocument() if err := doc.ReadFromString(string(manifest)); err != nil { return nil, err } root := doc.Root() // Update signer-related attributes asi, err := setAssemblyIdentity(root, cert) if err != nil { return nil, err } subjectName, err := setPublisherIdentity(root, cert) if err != nil { return nil, err } // Primary signature sigopts := xmldsig.SignOptions{MsCompatHashNames: true, IncludeKeyValue: true} if err := xmldsig.Sign(root, root, opts.HashFunc(), cert.Signer(), cert.Chain(), sigopts); err != nil { return nil, err } sig, keyinfo := setSigIds(root, "StrongNameSignature", "StrongNameKeyInfo") // Create authenticode structure manifestHash := makeManifestHash(sig) license, sigDestNode := makeLicense(asi, subjectName, manifestHash) // Sign authenticode structure sigopts.IncludeX509 = true if err := xmldsig.Sign(license, sigDestNode, opts.HashFunc(), cert.Signer(), cert.Chain(), sigopts); err != nil { return nil, err } aSig, _ := setSigIds(sigDestNode, "AuthenticodeSignature", "") // Attach authenticode to the primary document license.AddChild(sigDestNode) reldata := keyinfo.CreateElement("msrel:RelData") reldata.CreateAttr("xmlns:msrel", NsMsRel) reldata.AddChild(license) // Serialize signed, err := doc.WriteToBytes() if err != nil { return nil, err } // Get authenticode signature value for timestamping encryptedDigest, _ := base64.StdEncoding.DecodeString(aSig.SelectElement("SignatureValue").Text()) return &SignedManifest{ Signed: signed, EncryptedDigest: encryptedDigest, ManifestSignature: ManifestSignature{ Signature: &pkcs9.TimestampedSignature{Signature: pkcs7.Signature{ Certificate: cert.Leaf, Intermediates: cert.Chain(), }}, AssemblyName: asi.SelectAttrValue("name", ""), AssemblyVersion: asi.SelectAttrValue("version", ""), Hash: opts.HashFunc(), PublicKeyToken: asi.SelectAttrValue("publicKeyToken", ""), }}, nil } // Update the assemblyIdentity element with the actual signer public key. Only // the top-level one is updated, not the ones underneath individual manifest // entries. func setAssemblyIdentity(root *etree.Element, cert *certloader.Certificate) (*etree.Element, error) { token, err := PublicKeyToken(cert.Leaf.PublicKey) if err != nil { return nil, err } asi := root.SelectElement("assemblyIdentity") if asi == nil { return nil, errors.New("manifest has no top-level assemblyIdentity element") } asi.CreateAttr("publicKeyToken", token) return asi, nil } // Add/replace the publisherIdentity element func setPublisherIdentity(root *etree.Element, cert *certloader.Certificate) (string, error) { // add or replace publisherIdentity subjectName, issuerKeyHash, err := PublisherIdentity(cert) if err != nil { return "", err } xmldsig.RemoveElements(root, "publisherIdentity") ident := root.CreateElement("publisherIdentity") ident.CreateAttr("name", subjectName) ident.CreateAttr("issuerKeyHash", issuerKeyHash) return subjectName, nil } // Create the "license" block that goes inside the inner signature func makeLicense(asi *etree.Element, subjectName, manifestHash string) (*etree.Element, *etree.Element) { license := etree.NewElement("r:license") license.CreateAttr("xmlns:r", NsMpeg21) license.CreateAttr("xmlns:as", NsAuthenticode) grant := license.CreateElement("r:grant") minfo := grant.CreateElement("as:ManifestInformation") minfo.CreateAttr("Hash", manifestHash) minfo.CreateAttr("Description", "") minfo.CreateAttr("Url", "") massy := asi.Copy() massy.Space = "as" minfo.AddChild(massy) grant.CreateElement("as:SignedBy") grant.CreateElement("as:AuthenticodePublisher").CreateElement("as:X509SubjectName").SetText(subjectName) issuer := license.CreateElement("r:issuer") return license, issuer } // ManifestInformation contains a hash value which is, for some inane reason, // the same hash that the outer signature references but in reverse byte order. func makeManifestHash(sig *etree.Element) string { dv := sig.FindElement(".//DigestValue") blob, _ := base64.StdEncoding.DecodeString(dv.Text()) for i := 0; i < len(blob)/2; i++ { j := len(blob) - i - 1 blob[i], blob[j] = blob[j], blob[i] } return hex.EncodeToString(blob) } // Set Id attributes on signature elements func setSigIds(root *etree.Element, sigName, keyinfoName string) (sig, keyinfo *etree.Element) { sig = root.SelectElement("Signature") if sigName != "" { sig.CreateAttr("Id", sigName) } keyinfo = sig.SelectElement("KeyInfo") if keyinfoName != "" { keyinfo.CreateAttr("Id", keyinfoName) } return sig, keyinfo } // Attach a timestamp counter-signature func (m *SignedManifest) AddTimestamp(token *pkcs7.ContentInfoSignedData) error { doc := etree.NewDocument() if err := doc.ReadFromString(string(m.Signed)); err != nil { return err } aSig := doc.Root().FindElement("Signature/KeyInfo/msrel:RelData/r:license/r:issuer/Signature") if aSig == nil { return errors.New("manifest has no authenticode signature") } siblob, err := token.Marshal() if err != nil { return err } lines := (47 + len(siblob)) / 48 buf := bytes.NewBuffer(make([]byte, 0, (64+2)*lines)) for len(siblob) > 0 { n := len(siblob) if n > 48 { n = 48 } chunk := siblob[:n] siblob = siblob[n:] buf.WriteString(base64.StdEncoding.EncodeToString(chunk)) buf.WriteString("\r\n") } aSig.CreateElement("Object").CreateElement("as:Timestamp").SetText(buf.String()) signed, err := doc.WriteToBytes() if err != nil { return err } cs, err := VerifyTimestamp(token, m.EncryptedDigest, m.Signature.Intermediates) if err != nil { return fmt.Errorf("failed to validate timestamp: %w", err) } m.Signed = signed m.Signature.CounterSignature = cs return nil } relic-7.6.1/lib/appmanifest/verify.go000066400000000000000000000076771455105530300175550ustar00rootroot00000000000000// // Copyright (c) SAS Institute Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package appmanifest import ( "crypto" "crypto/x509" "encoding/base64" "errors" "fmt" "github.com/beevik/etree" "github.com/sassoftware/relic/v7/lib/pkcs7" "github.com/sassoftware/relic/v7/lib/pkcs9" "github.com/sassoftware/relic/v7/lib/x509tools" "github.com/sassoftware/relic/v7/lib/xmldsig" "github.com/sassoftware/relic/v7/signers/sigerrors" ) type ManifestSignature struct { Signature *pkcs9.TimestampedSignature Hash crypto.Hash AssemblyName string AssemblyVersion string PublicKeyToken string } // Extract and verify signature on an application manifest. Does not verify X509 chains. func Verify(manifest []byte) (*ManifestSignature, error) { doc := etree.NewDocument() if err := doc.ReadFromString(string(manifest)); err != nil { return nil, err } root := doc.Root() primary, err := xmldsig.Verify(root, "Signature", nil) if err != nil { if _, ok := err.(sigerrors.NotSignedError); ok { return nil, err } return nil, fmt.Errorf("invalid primary signature: %w", err) } license := root.FindElement("Signature/KeyInfo/msrel:RelData/r:license") if license == nil { return nil, sigerrors.NotSignedError{Type: "application manifest"} } secondary, err := xmldsig.Verify(license, "issuer/Signature", nil) if err != nil { if _, ok := err.(sigerrors.NotSignedError); ok { return nil, err } return nil, fmt.Errorf("invalid authenticode signature: %w", err) } if !x509tools.SameKey(primary.PublicKey, secondary.PublicKey) { return nil, errors.New("signatures were made with different keys") } asi := root.SelectElement("assemblyIdentity") if asi == nil { return nil, errors.New("missing assemblyIdentity") } token, err := PublicKeyToken(primary.PublicKey) if err != nil { return nil, err } if token2 := asi.SelectAttrValue("publicKeyToken", ""); token2 != token { return nil, fmt.Errorf("publicKeyToken mismatch: expected %s, got %s", token, token2) } sig := pkcs7.Signature{Intermediates: secondary.Certificates, Certificate: secondary.Leaf()} if sig.Certificate == nil { return nil, errors.New("leaf x509 certificate not found") } ts := &pkcs9.TimestampedSignature{Signature: sig} if tse := license.FindElement("r:issuer/Signature/Object/as:Timestamp"); tse != nil { blob, err := base64.StdEncoding.DecodeString(tse.Text()) if err != nil { return nil, fmt.Errorf("invalid timestamp: %w", err) } timestamp, err := pkcs7.Unmarshal(blob) if err != nil { return nil, fmt.Errorf("invalid timestamp: %w", err) } cs, err := VerifyTimestamp(timestamp, secondary.EncryptedDigest, secondary.Certificates) if err != nil { return nil, fmt.Errorf("invalid timestamp: %w", err) } ts.CounterSignature = cs } return &ManifestSignature{ Signature: ts, AssemblyName: asi.SelectAttrValue("name", ""), AssemblyVersion: asi.SelectAttrValue("version", ""), Hash: primary.Hash, PublicKeyToken: token, }, nil } func VerifyTimestamp(timestamp *pkcs7.ContentInfoSignedData, encryptedDigest []byte, extraCerts []*x509.Certificate) (*pkcs9.CounterSignature, error) { var cs *pkcs9.CounterSignature var err error if timestamp.Content.ContentInfo.ContentType.Equal(pkcs9.OidTSTInfo) { // pkcs9 timestamp cs, err = pkcs9.Verify(timestamp, encryptedDigest, extraCerts) } else { // legacy timestamp cs, err = pkcs9.VerifyMicrosoftToken(timestamp, encryptedDigest) } return cs, err } relic-7.6.1/lib/assuan/000077500000000000000000000000001455105530300146645ustar00rootroot00000000000000relic-7.6.1/lib/assuan/assuan.go000066400000000000000000000106011455105530300165030ustar00rootroot00000000000000// // Copyright (c) SAS Institute Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package assuan import ( "bufio" "errors" "fmt" "net" "net/url" "strings" "sync" "github.com/sassoftware/relic/v7/lib/dlog" ) // Simple libassuan client // // See: https://www.gnupg.org/software/libassuan/index.html const ( StatusOk = "OK" StatusErr = "ERR" StatusInquire = "INQUIRE" StatusData = "D" StatusLines = "S" StatusComment = "#" ) type Conn struct { conn net.Conn r *bufio.Reader mu sync.Mutex } type Response struct { Status string StatusMessage string Lines []string Blob []byte } func (r Response) Error() string { return fmt.Sprintf("response error: %s", r.StatusMessage) } type InquireFunc func(inquireLine string, msgLines []string) (string, error) var InquireCancel = errors.New("inquiry cancelled") func Dial(path string) (*Conn, error) { conn, err := net.Dial("unix", path) if err != nil { return nil, err } s := &Conn{ conn: conn, r: bufio.NewReader(conn), } status, msg, err := s.readLine() if err != nil { conn.Close() return nil, err } else if status != StatusOk { conn.Close() return nil, fmt.Errorf("failed to connect to %s: %s", path, msg) } return s, nil } func (c *Conn) write(cmd string) error { dlog.Printf(7, "> %#v", cmd) _, err := c.conn.Write([]byte(cmd)) return err } func (c *Conn) data(data string) error { data = url.PathEscape(data) for len(data) > 0 { n := 512 if n > len(data) { n = len(data) } chunk := data[:n] data = data[n:] if err := c.write(fmt.Sprintf("D %s\n", chunk)); err != nil { return err } } return c.write("END\n") } func (c *Conn) readLine() (string, string, error) { line, err := c.r.ReadString('\n') if err != nil { return "", "", err } line = line[:len(line)-1] dlog.Printf(7, "< %#v", line) parts := strings.SplitN(line, " ", 2) status := parts[0] if len(parts) > 1 { return status, parts[1], nil } return status, "", nil } func (c *Conn) read(inquire InquireFunc) (res Response, err error) { var quotedBlob string var saved error readloop: for { status, msg, err := c.readLine() if err != nil { return res, err } switch status { case StatusData: quotedBlob += msg case StatusLines: msg, err := url.PathUnescape(msg) if err != nil { return res, err } res.Lines = append(res.Lines, msg) case StatusInquire: if inquire != nil { d, err := inquire(msg, res.Lines) if err != nil { _ = c.write("CANCEL\n") if err != InquireCancel { // raise this once the ERR has been received saved = err } } else { if err := c.data(d); err != nil { return res, err } } } else { _ = c.write("CANCEL\n") } case StatusComment: // no-op default: res.Status = status res.StatusMessage = msg break readloop } } if len(quotedBlob) > 0 { blob, err := url.PathUnescape(quotedBlob) if err != nil { return res, err } res.Blob = []byte(blob) } err = saved return } // Execute a command and retrieve the result. // // If an INQUIRE is received then inquire() will be invoked with the text after // INQUIRE and all status lines received so far. It should return data to send, // or it can return an err of InquireCancel which will cause a CANCEL to be // sent and the resulting response to be returned. If inquire is nil then a // CANCEL is always sent. func (c *Conn) Transact(command string, inquire InquireFunc) (res Response, err error) { c.mu.Lock() defer c.mu.Unlock() if c.conn == nil { return res, errors.New("connection is closed") } if err := c.write(command + "\n"); err != nil { return res, err } res, err = c.read(inquire) if err == nil && res.Status != StatusOk { err = res } return } func (c *Conn) Close() error { c.mu.Lock() defer c.mu.Unlock() if c.conn != nil { c.conn.Close() c.conn = nil c.r = nil } return nil } relic-7.6.1/lib/assuan/csexp.go000066400000000000000000000033121455105530300163340ustar00rootroot00000000000000// // Copyright (c) SAS Institute Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package assuan // http://people.csail.mit.edu/rivest/Sexp.txt import ( "bytes" "errors" "strconv" ) type csExp struct { Value []byte Items []*csExp } var InvalidCsExp = errors.New("invalid cs-exp") func parseCsExp(blob []byte) (*csExp, error) { root := new(csExp) stack := []*csExp{root} top := root csloop: for { switch { case len(blob) == 0: if len(stack) > 1 { return nil, InvalidCsExp } break csloop case blob[0] == '(': item := new(csExp) stack = append(stack, item) top.Items = append(top.Items, item) top = item blob = blob[1:] case blob[0] == ')': if len(stack) < 2 { return nil, InvalidCsExp } stack = stack[:len(stack)-1] top = stack[len(stack)-1] blob = blob[1:] default: n := bytes.IndexByte(blob, ':') if n < 1 { return nil, InvalidCsExp } length, err := strconv.ParseUint(string(blob[:n]), 10, 64) if err != nil || length > uint64(len(blob)-n-1) { return nil, InvalidCsExp } blob = blob[n+1:] data := blob[:length] blob = blob[length:] top.Items = append(top.Items, &csExp{Value: data}) } } return root, nil } relic-7.6.1/lib/assuan/csexp_test.go000066400000000000000000000013621455105530300173760ustar00rootroot00000000000000// // Copyright (c) SAS Institute Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package assuan import "testing" func TestCsExp(t *testing.T) { _, err := parseCsExp([]byte("(3:foo3:bar3:baz)")) if err != nil { t.Fatal(err) } } relic-7.6.1/lib/assuan/scd.go000066400000000000000000000132471455105530300157730ustar00rootroot00000000000000// // Copyright (c) SAS Institute Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package assuan // Implement a libassuan client and wrap useful functions in scdaemon import ( "bytes" "crypto" "crypto/rsa" "errors" "fmt" "math/big" "strings" "github.com/sassoftware/relic/v7/lib/dlog" "github.com/sassoftware/relic/v7/lib/x509tools" "github.com/sassoftware/relic/v7/signers/sigerrors" ) type ScdConn struct { *Conn Serial string } type ScdKey struct { Serial string Fingerprint string KeyGrip string KeyId string conn *ScdConn } func DialScd(path string) (*ScdConn, error) { conn, err := Dial(path) if err != nil { return nil, err } return &ScdConn{Conn: conn}, nil } // Invoke LEARN and return info about the keys in the token func (s *ScdConn) Learn() ([]*ScdKey, error) { res, err := s.Conn.Transact("LEARN", func(inquiry string, lines []string) (string, error) { return "", nil }) if err != nil { return nil, fmt.Errorf("failed to enumerate token: %w", err) } var keyids, keygrips []string fingerprints := make(map[string]string) for _, line := range res.Lines { parts := strings.Split(line, " ") switch parts[0] { case "SERIALNO": if len(parts) < 2 { continue } s.Serial = parts[1] case "KEYPAIRINFO": if len(parts) < 3 || parts[1] == "X" { continue } keygrips = append(keygrips, parts[1]) keyids = append(keyids, parts[2]) case "KEY-FPR": if len(parts) < 3 { continue } keyid := "OPENPGP." + parts[1] fingerprints[keyid] = parts[2] } } if len(keyids) == 0 { return nil, errors.New("failed to enumerate token: no valid key found") } infos := make([]*ScdKey, 0, len(keyids)) dlog.Printf(3, "scdaemon token with serial %s has keys:", s.Serial) for i, keyid := range keyids { info := &ScdKey{ conn: s, Serial: s.Serial, KeyId: keyid, KeyGrip: keygrips[i], Fingerprint: fingerprints[keyid], } dlog.Printf(3, " keyid=%s keygrip=%s fingerprint=%s", info.KeyId, info.KeyGrip, info.Fingerprint) infos = append(infos, info) } return infos, nil } // Verify that the token can be unlocked with the given pin func (s *ScdConn) CheckPin(pin string) error { if s.Serial == "" { infos, err := s.Learn() if err != nil { return err } s.Serial = infos[0].Serial } _, err := s.Conn.Transact("CHECKPIN "+s.Serial, func(inquiry string, lines []string) (string, error) { if strings.HasPrefix(inquiry, "NEEDPIN") { return pin + "\x00", nil } else { return "", fmt.Errorf("unexpected INQUIRE: %s", inquiry) } }) if eres, ok := err.(Response); ok && strings.Contains(eres.StatusMessage, "Bad PIN") { return sigerrors.PinIncorrectError{} } else if err != nil { return fmt.Errorf("failed to validate PIN: %w", err) } return nil } // Get the public key from the token func (k *ScdKey) Public() (crypto.PublicKey, error) { res, err := k.conn.Transact("READKEY "+k.KeyId, nil) if err != nil { return nil, err } exp, err := parseCsExp(res.Blob) if err != nil { return nil, err } if len(exp.Items) != 1 { return nil, errors.New("invalid public key in token") } exp = exp.Items[0] if len(exp.Items) != 2 || !bytes.Equal(exp.Items[0].Value, []byte("public-key")) { return nil, errors.New("invalid public key in token") } exp = exp.Items[1] if len(exp.Items) == 0 { return nil, errors.New("invalid public key in token") } keyType := string(exp.Items[0].Value) values := make(map[string][]byte) for _, item := range exp.Items[1:] { if len(item.Items) != 2 { return nil, errors.New("invalid public key in token") } name := string(item.Items[0].Value) value := item.Items[1].Value values[name] = value } switch keyType { case "rsa": n := values["n"] e := values["e"] if n == nil || e == nil { return nil, errors.New("invalid RSA public key in token") } return &rsa.PublicKey{ N: new(big.Int).SetBytes(n), E: int(new(big.Int).SetBytes(e).Int64()), }, nil default: return nil, fmt.Errorf("unsupported public key of type %s in token", keyType) } } // Create a signature over the given (unpadded) digest. func (k *ScdKey) Sign(hashValue []byte, opts crypto.SignerOpts, pin string) ([]byte, error) { if opts == nil || opts.HashFunc() == 0 { return nil, errors.New("Signer options are required") } else if _, ok := opts.(*rsa.PSSOptions); ok { return nil, errors.New("RSA-PSS not implemented") } hashName := x509tools.HashNames[opts.HashFunc()] if hashName == "" { return nil, errors.New("unsupported hash algorithm") } hashName = strings.ToLower(strings.ReplaceAll(hashName, "-", "")) res, err := k.conn.Transact(fmt.Sprintf("SETDATA %X", hashValue), nil) if err != nil { return nil, err } res, err = k.conn.Transact(fmt.Sprintf("PKSIGN --hash=%s %s\n", hashName, k.KeyId), func(inquiry string, lines []string) (string, error) { if strings.HasPrefix(inquiry, "NEEDPIN") { return pin + "\x00", nil } else { return "", fmt.Errorf("unexpected INQUIRE: %s", inquiry) } }) if eres, ok := err.(Response); ok && strings.Contains(eres.StatusMessage, "Bad PIN") { return nil, sigerrors.PinIncorrectError{} } else if err != nil { return nil, fmt.Errorf("failed to sign: %w", err) } return res.Blob, nil } relic-7.6.1/lib/atomicfile/000077500000000000000000000000001455105530300155065ustar00rootroot00000000000000relic-7.6.1/lib/atomicfile/atomicfile.go000066400000000000000000000050561455105530300201570ustar00rootroot00000000000000// // Copyright (c) SAS Institute Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // // Implement atomic write-rename file pattern. Instead of opening the named // file it creates a temporary file next to it, then on Commit() renames it. If // the file is Close()d before Commit() then it is unlinked instead. package atomicfile import ( "errors" "io" "io/ioutil" "os" "path/filepath" "runtime" ) // File-like interface used by several functions in this package. Some of them // may open a file or stdio directly without atomic semantics, in which case // Commit() is an alias for Close() type AtomicFile interface { io.Reader io.ReaderAt io.Writer io.WriterAt io.Seeker Truncate(size int64) error // Close and unlink the underlying file object, discarding the contents. // No-op if Close() or Commit() was already called. io.Closer // Get the underlying *File object GetFile() *os.File // Complete the write-rename pattern and close the file Commit() error } type atomicFile struct { *os.File name string } // Open a temporary file for reading and writing which will ultimately be // renamed to the given name when Commit() is called. func New(name string) (AtomicFile, error) { tempfile, err := ioutil.TempFile(filepath.Dir(name), filepath.Base(name)+".tmp") if err != nil { return nil, err } f := &atomicFile{tempfile, name} runtime.SetFinalizer(f, (*atomicFile).Close) return f, nil } func (f *atomicFile) GetFile() *os.File { return f.File } func (f *atomicFile) Close() error { if f.File == nil { return nil } f.File.Close() os.Remove(f.File.Name()) f.File = nil runtime.SetFinalizer(f, nil) return nil } func (f *atomicFile) Commit() error { if f.File == nil { return errors.New("file is closed") } _ = f.File.Chmod(0644) if err := f.File.Close(); err != nil { return err } // rename can't overwrite on windows if err := os.Remove(f.name); err != nil && !os.IsNotExist(err) { return err } if err := os.Rename(f.File.Name(), f.name); err != nil { return err } f.File = nil runtime.SetFinalizer(f, nil) return nil } relic-7.6.1/lib/atomicfile/openany.go000066400000000000000000000042721455105530300175130ustar00rootroot00000000000000// // Copyright (c) SAS Institute Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package atomicfile import ( "io" "os" ) type nopAtomic struct { *os.File doClose bool } func (a nopAtomic) GetFile() *os.File { return a.File } func (a nopAtomic) Commit() error { if a.doClose { return a.Close() } return nil } func isSpecial(path string) bool { if stat, err := os.Stat(path); err == nil { if !stat.Mode().IsRegular() { return true } } return false } // Pick the best strategy for writing to the given path. Pipes and devices will // be written to directly, otherwise write-rename. func WriteAny(path string) (AtomicFile, error) { if path == "-" { return nopAtomic{os.Stdout, false}, nil } if isSpecial(path) { f, err := os.Create(path) return nopAtomic{f, true}, err } return New(path) } // If src and dest are the same, use src for reading and writing. If they are // different, make a copy and open the destination as an atomicfile, after // which src will be closed. func WriteInPlace(src *os.File, dest string) (AtomicFile, error) { if src.Name() == dest { return nopAtomic{src, false}, nil } outfile, err := New(dest) if err != nil { return nil, err } if _, err := src.Seek(0, 0); err != nil { return nil, err } if _, err := io.Copy(outfile, src); err != nil { return nil, err } if _, err := outfile.Seek(0, 0); err != nil { return nil, err } err = src.Close() return outfile, err } // Write bytes to a file, using write-rename when appropriate func WriteFile(path string, data []byte) error { f, err := WriteAny(path) if err != nil { return err } defer f.Close() if _, err := f.Write(data); err != nil { return err } return f.Commit() } relic-7.6.1/lib/audit/000077500000000000000000000000001455105530300145005ustar00rootroot00000000000000relic-7.6.1/lib/audit/amqp.go000066400000000000000000000054041455105530300157700ustar00rootroot00000000000000// // Copyright (c) SAS Institute Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package audit import ( "crypto/tls" "errors" "time" "github.com/streadway/amqp" "github.com/sassoftware/relic/v7/config" "github.com/sassoftware/relic/v7/lib/certloader" "github.com/sassoftware/relic/v7/lib/x509tools" ) // Publish audit record to a AMQP exchange func (info *Info) Publish(aconf *config.AmqpConfig) error { blob, err := info.Marshal() if err != nil { return err } msg := amqp.Publishing{ DeliveryMode: amqp.Persistent, Timestamp: time.Now(), ContentType: "application/json", Body: blob, } conn, err := Connect(aconf) if err != nil { return err } defer conn.Close() ch, err := conn.Channel() if err != nil { return err } defer ch.Close() if err := ch.ExchangeDeclare(aconf.ExchangeName(), amqp.ExchangeFanout, true, false, false, false, nil); err != nil { return err } if err := ch.Confirm(false); err != nil { return err } notify := ch.NotifyPublish(make(chan amqp.Confirmation, 1)) if err := ch.Publish(aconf.ExchangeName(), aconf.RoutingKey(), false, false, msg); err != nil { return err } confirm := <-notify if !confirm.Ack { return errors.New("message was NACKed") } return nil } // Connect to the configured AMQP broker func Connect(aconf *config.AmqpConfig) (*amqp.Connection, error) { uri, err := amqp.ParseURI(aconf.URL) if err != nil { return nil, err } var tconf *tls.Config var auth []amqp.Authentication if uri.Scheme == "amqps" { tconf = &tls.Config{} if aconf.CaCert != "" { if err := x509tools.LoadCertPool(aconf.CaCert, tconf); err != nil { return nil, err } } if aconf.CertFile != "" { cert, err := certloader.LoadX509KeyPair(aconf.CertFile, aconf.KeyFile) if err != nil { return nil, err } tconf.Certificates = []tls.Certificate{cert.TLS()} } x509tools.SetKeyLogFile(tconf) if len(tconf.Certificates) != 0 { auth = append(auth, externalAuth{}) } } if uri.Password != "" { auth = append(auth, uri.PlainAuth()) } qconf := amqp.Config{SASL: auth, TLSClientConfig: tconf} return amqp.DialConfig(aconf.URL, qconf) } type externalAuth struct{} func (externalAuth) Mechanism() string { return "EXTERNAL" } func (externalAuth) Response() string { return "" } relic-7.6.1/lib/audit/audit.go000066400000000000000000000103541455105530300161400ustar00rootroot00000000000000// // Copyright (c) SAS Institute Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package audit import ( "crypto" "crypto/x509" "encoding/base64" "encoding/json" "errors" "fmt" "os" "strings" "time" "golang.org/x/crypto/openpgp" "github.com/rs/zerolog" "github.com/sassoftware/relic/v7/lib/pgptools" "github.com/sassoftware/relic/v7/lib/pkcs9" "github.com/sassoftware/relic/v7/lib/x509tools" ) type Info struct { Attributes map[string]interface{} StartTime time.Time } // Create a new audit record, starting with the given key name, signature type, // and digest func New(keyName, sigType string, hash crypto.Hash) *Info { now := time.Now().UTC() a := make(map[string]interface{}) a["sig.type"] = sigType a["sig.keyname"] = keyName a["sig.hash"] = x509tools.HashNames[hash] a["sig.timestamp"] = now if hostname, _ := os.Hostname(); hostname != "" { a["sig.hostname"] = hostname } return &Info{Attributes: a, StartTime: now} } // Set a PGP certificate for this audit record func (info *Info) SetPgpCert(entity *openpgp.Entity) { info.Attributes["sig.pgp.fingerprint"] = fmt.Sprintf("%x", entity.PrimaryKey.Fingerprint[:]) info.Attributes["sig.pgp.entity"] = pgptools.EntityName(entity) } // Set a X509 certificate for this audit record func (info *Info) SetX509Cert(cert *x509.Certificate) { info.Attributes["sig.x509.subject"] = x509tools.FormatSubject(cert) info.Attributes["sig.x509.issuer"] = x509tools.FormatIssuer(cert) d := crypto.SHA1.New() d.Write(cert.Raw) info.Attributes["sig.x509.fingerprint"] = fmt.Sprintf("%x", d.Sum(nil)) } // Override the default timestamp for this audit record func (info *Info) SetTimestamp(t time.Time) { info.Attributes["sig.timestamp"] = t.UTC() } // Add a PKCS#9 timestamp (counter-signature) to this audit record func (info *Info) SetCounterSignature(cs *pkcs9.CounterSignature) { if cs == nil { return } info.Attributes["sig.ts.timestamper"] = x509tools.FormatSubject(cs.Certificate) info.Attributes["sig.ts.timestamp"] = cs.SigningTime info.Attributes["sig.ts.hash"] = x509tools.HashNames[cs.Hash] } // Set the MIME type (Content-Type) that the server will use when returning a // result to the client. This is not the MIME type of the package being signed. func (info *Info) SetMimeType(mimeType string) { info.Attributes["content-type"] = mimeType } // Get the MIME type that the server will use when returning a result to the // client. This is not the MIME type of the package being signed. func (info *Info) GetMimeType() string { v := info.Attributes["content-type"] if v != nil { return v.(string) } return "application/octet-stream" } // Marshal the audit record to JSON func (info *Info) Marshal() ([]byte, error) { if info.Attributes["perf.elapsed.ms"] == nil && !info.StartTime.IsZero() { info.Attributes["perf.elapsed.ms"] = time.Since(info.StartTime).Nanoseconds() / 1e6 } return json.Marshal(info.Attributes) } func (info *Info) AttrsForLog(prefix string) *zerolog.Event { ev := zerolog.Dict() for name, value := range info.Attributes { if !strings.HasPrefix(name, prefix) { continue } name = name[len(prefix):] if s, ok := value.(string); ok { ev.Str(name, s) } else { ev.Interface(name, s) } } return ev } // Parse audit data from a JSON blob func Parse(blob []byte) (*Info, error) { if len(blob) == 0 { return nil, errors.New("missing attributes") } info := new(Info) if err := json.Unmarshal(blob, &info.Attributes); err != nil { return nil, err } if sealed := info.Attributes["attributes"]; sealed != nil { blob, err := base64.StdEncoding.DecodeString(sealed.(string)) if err != nil { return nil, err } info.Attributes = nil if err := json.Unmarshal(blob, &info.Attributes); err != nil { return nil, err } } return info, nil } relic-7.6.1/lib/authenticode/000077500000000000000000000000001455105530300160465ustar00rootroot00000000000000relic-7.6.1/lib/authenticode/authenticode.go000066400000000000000000000056341455105530300210610ustar00rootroot00000000000000// // Copyright (c) SAS Institute Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package authenticode import ( "context" "crypto" "encoding/asn1" "errors" "github.com/sassoftware/relic/v7/lib/certloader" "github.com/sassoftware/relic/v7/lib/pkcs7" "github.com/sassoftware/relic/v7/lib/pkcs9" "github.com/sassoftware/relic/v7/lib/x509tools" ) type OpusParams struct { Description string URL string } func makePeIndirect(imprint []byte, hash crypto.Hash, oid asn1.ObjectIdentifier) (indirect SpcIndirectDataContentPe, err error) { alg, ok := x509tools.PkixDigestAlgorithm(hash) if !ok { err = errors.New("unsupported digest algorithm") return } indirect.Data.Type = oid indirect.MessageDigest.Digest = imprint indirect.MessageDigest.DigestAlgorithm = alg indirect.Data.Value.File.File = NewSpcString("") return } func signIndirect(ctx context.Context, indirect interface{}, hash crypto.Hash, cert *certloader.Certificate, params *OpusParams) (*pkcs9.TimestampedSignature, error) { sig := pkcs7.NewBuilder(cert.Signer(), cert.Chain(), hash) if err := sig.SetContent(OidSpcIndirectDataContent, indirect); err != nil { return nil, err } if err := addOpusAttrs(sig, params); err != nil { return nil, err } psd, err := sig.Sign() if err != nil { return nil, err } return pkcs9.TimestampAndMarshal(ctx, psd, cert.Timestamper, true) } func addOpusAttrs(sig *pkcs7.SignatureBuilder, params *OpusParams) error { if err := sig.AddAuthenticatedAttribute(OidSpcStatementType, SpcSpStatementType{Type: OidSpcIndividualPurpose}); err != nil { return err } var info SpcSpOpusInfo if params != nil { if params.Description != "" { info.ProgramName = NewSpcString(params.Description) } if params.URL != "" { info.MoreInfo.URL = params.URL } } if err := sig.AddAuthenticatedAttribute(OidSpcSpOpusInfo, info); err != nil { return err } return nil } func SignSip(ctx context.Context, imprint []byte, hash crypto.Hash, sipInfo SpcSipInfo, cert *certloader.Certificate, params *OpusParams) (*pkcs9.TimestampedSignature, error) { alg, ok := x509tools.PkixDigestAlgorithm(hash) if !ok { return nil, errors.New("unsupported digest algorithm") } var indirect SpcIndirectDataContentMsi indirect.Data.Type = OidSpcSipInfo indirect.Data.Value = sipInfo indirect.MessageDigest.Digest = imprint indirect.MessageDigest.DigestAlgorithm = alg return signIndirect(ctx, indirect, hash, cert, params) } relic-7.6.1/lib/authenticode/cabfile.go000066400000000000000000000067011455105530300177660ustar00rootroot00000000000000// // Copyright (c) SAS Institute Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package authenticode import ( "context" "crypto" "crypto/hmac" "errors" "fmt" "io" "github.com/sassoftware/relic/v7/lib/binpatch" "github.com/sassoftware/relic/v7/lib/cabfile" "github.com/sassoftware/relic/v7/lib/certloader" "github.com/sassoftware/relic/v7/lib/pkcs7" "github.com/sassoftware/relic/v7/lib/pkcs9" "github.com/sassoftware/relic/v7/lib/x509tools" "github.com/sassoftware/relic/v7/signers/sigerrors" ) type CabSignature struct { pkcs9.TimestampedSignature Indirect *SpcIndirectDataContentPe OpusInfo *SpcSpOpusInfo HashFunc crypto.Hash PatchSet *binpatch.PatchSet } // Extract and verify the signature of a CAB file. Does not check X509 chains. func VerifyCab(f io.ReaderAt, skipDigests bool) (*CabSignature, error) { cab, err := cabfile.Parse(io.NewSectionReader(f, 0, 1<<63-1)) if err != nil { return nil, err } if len(cab.Signature) == 0 { return nil, sigerrors.NotSignedError{Type: "cabinet"} } psd, err := pkcs7.Unmarshal(cab.Signature) if err != nil { return nil, err } if !psd.Content.ContentInfo.ContentType.Equal(OidSpcIndirectDataContent) { return nil, errors.New("not an authenticode signature") } pksig, err := psd.Content.Verify(nil, false) if err != nil { return nil, err } ts, err := pkcs9.VerifyOptionalTimestamp(pksig) if err != nil { return nil, err } indirect := new(SpcIndirectDataContentPe) if err := psd.Content.ContentInfo.Unmarshal(indirect); err != nil { return nil, err } hash, err := x509tools.PkixDigestToHashE(indirect.MessageDigest.DigestAlgorithm) if err != nil { return nil, err } opus, err := GetOpusInfo(pksig.SignerInfo) if err != nil { return nil, err } cabsig := &CabSignature{ TimestampedSignature: ts, Indirect: indirect, HashFunc: hash, OpusInfo: opus, } if !skipDigests { digest, err := cabfile.Digest(io.NewSectionReader(f, 0, 1<<63-1), hash) if err != nil { return nil, err } if !hmac.Equal(digest.Imprint, indirect.MessageDigest.Digest) { return nil, fmt.Errorf("digest mismatch: %x != %x", digest.Imprint, indirect.MessageDigest.Digest) } // cab signatures seem to come with a "page hash" link in the same way // as PE files can, using OidSpcCabPageHash as the type, but it's not // clear what it's hashing. } return cabsig, nil } // Create the Authenticode structure for a CAB file signature using a previously-calculated digest (imprint). func SignCabImprint(ctx context.Context, digest *cabfile.CabinetDigest, cert *certloader.Certificate, params *OpusParams) (*binpatch.PatchSet, *pkcs9.TimestampedSignature, error) { indirect, err := makePeIndirect(digest.Imprint, digest.HashFunc, OidSpcCabImageData) if err != nil { return nil, nil, err } ts, err := signIndirect(ctx, indirect, digest.HashFunc, cert, params) if err != nil { return nil, nil, err } patch := digest.MakePatch(ts.Raw) return patch, ts, nil } relic-7.6.1/lib/authenticode/catalog.go000066400000000000000000000101661455105530300200130ustar00rootroot00000000000000// // Copyright (c) SAS Institute Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package authenticode import ( "context" "crypto" "crypto/x509/pkix" "encoding/asn1" "encoding/binary" "encoding/hex" "errors" "time" "unicode/utf16" "github.com/google/uuid" "github.com/sassoftware/relic/v7/lib/certloader" "github.com/sassoftware/relic/v7/lib/pkcs7" "github.com/sassoftware/relic/v7/lib/pkcs9" "github.com/sassoftware/relic/v7/lib/x509tools" ) type Catalog struct { Version int Hash crypto.Hash Sha1Entries, Sha2Entries []CertTrustEntry } func NewCatalog(hash crypto.Hash) *Catalog { if hash == crypto.SHA1 { return &Catalog{Version: 1, Hash: hash} } return &Catalog{Version: 2, Hash: hash} } func (cat *Catalog) makeCatalog() CertTrustList { memberOid := OidCatalogListMember if cat.Version == 2 { memberOid = OidCatalogListMemberV2 } listID := uuid.Must(uuid.NewRandom()) return CertTrustList{ SubjectUsage: []asn1.ObjectIdentifier{OidCatalogList}, ListIdentifier: listID[:], EffectiveDate: time.Now().UTC(), SubjectAlgorithm: pkix.AlgorithmIdentifier{Algorithm: memberOid, Parameters: asn1.NullRawValue}, Entries: append(cat.Sha2Entries, cat.Sha1Entries...), } } func (cat *Catalog) Marshal() ([]byte, error) { return asn1.Marshal(cat.makeCatalog()) } func (cat *Catalog) Sign(ctx context.Context, cert *certloader.Certificate, params *OpusParams) (*pkcs9.TimestampedSignature, error) { sig := pkcs7.NewBuilder(cert.Signer(), cert.Chain(), cat.Hash) if err := sig.SetContent(OidCertTrustList, cat.makeCatalog()); err != nil { return nil, err } if err := addOpusAttrs(sig, params); err != nil { return nil, err } psd, err := sig.Sign() if err != nil { return nil, err } return pkcs9.TimestampAndMarshal(ctx, psd, cert.Timestamper, true) } func (cat *Catalog) Add(indirect SpcIndirectDataContentPe) error { sha2 := !indirect.MessageDigest.DigestAlgorithm.Algorithm.Equal(x509tools.OidDigestSHA1) if sha2 && cat.Version == 1 { return errors.New("can't add SHA2 digest to v1 catalog") } indirectBytes, err := asn1.Marshal(indirect) if err != nil { return err } indirectEntry := CertTrustValue{Attribute: OidSpcIndirectDataContent, Value: makeSet(indirectBytes)} value := indirect.MessageDigest.Digest if cat.Version == 1 { memberInfo := CertTrustMemberInfoV1{ ClassID: x509tools.ToBMPString(CryptSipCreateIndirectData), Unknown1: 512, } memberInfoEnc, err := asn1.Marshal(memberInfo) if err != nil { return err } catValue := CertTrustValue{Attribute: OidCatalogMemberInfo, Value: makeSet(memberInfoEnc)} cat.Sha1Entries = append(cat.Sha1Entries, CertTrustEntry{ Tag: tagV1(value), Values: []CertTrustValue{indirectEntry, catValue}, }) } else { // this supposed to always be empty? memberInfoEnc := []byte{0x80, 0} catValue := CertTrustValue{Attribute: OidCatalogMemberInfoV2, Value: makeSet(memberInfoEnc)} if sha2 { cat.Sha2Entries = append(cat.Sha2Entries, CertTrustEntry{ Tag: value, Values: []CertTrustValue{catValue, indirectEntry}, }) } else { cat.Sha1Entries = append(cat.Sha1Entries, CertTrustEntry{ Tag: value, Values: []CertTrustValue{catValue}, }) } } return nil } func tagV1(value []byte) []byte { // The tag is a UTF-16-LE encoding of the hex of the imprint runes := utf16.Encode([]rune(hex.EncodeToString(value))) tag := make([]byte, 2*len(runes)) for i, r := range runes { binary.LittleEndian.PutUint16(tag[i*2:], r) } return tag } func makeSet(contents []byte) asn1.RawValue { return asn1.RawValue{Tag: asn1.TagSet, IsCompound: true, Bytes: contents} } relic-7.6.1/lib/authenticode/checksum.go000066400000000000000000000051701455105530300202020ustar00rootroot00000000000000// // Copyright (c) SAS Institute Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package authenticode import ( "encoding/binary" "errors" "hash" "io" "os" ) // An undocumented, non-CRC checksum used in PE images // https://www.codeproject.com/Articles/19326/An-Analysis-of-the-Windows-PE-Checksum-Algorithm func FixPEChecksum(f *os.File) error { if _, err := f.Seek(0, 0); err != nil { return err } peStart, err := readDosHeader(f, nil) if err != nil { return err } ck := NewPEChecksum(int(peStart)) if _, err := f.Seek(0, 0); err != nil { return err } if _, err := io.Copy(ck, f); err != nil { return err } if _, err := f.WriteAt(ck.Sum(nil), peStart+88); err != nil { return err } return nil } type peChecksum struct { cksumPos int sum, size uint32 odd bool } // Hasher that calculates the undocumented, non-CRC checksum used in PE images. // peStart is the offset found at 0x3c in the DOS header. func NewPEChecksum(peStart int) hash.Hash { var cksumPos int if peStart <= 0 { cksumPos = -1 } else { cksumPos = peStart + 88 } return &peChecksum{cksumPos: cksumPos} } func (peChecksum) Size() int { return 4 } func (peChecksum) BlockSize() int { return 2 } func (h *peChecksum) Reset() { h.cksumPos = -1 h.sum = 0 h.size = 0 } func (h *peChecksum) Write(d []byte) (int, error) { // tolerate odd-sized files by adding a final zero byte, but odd writes anywhere but the end are an error n := len(d) if h.odd { return 0, errors.New("odd write") } else if n%2 != 0 { h.odd = true d2 := make([]byte, n+1) copy(d2, d) d = d2 } ckpos := -1 if h.cksumPos > n { h.cksumPos -= n } else if h.cksumPos >= 0 { ckpos = h.cksumPos h.cksumPos = -1 } sum := h.sum for i := 0; i < n; i += 2 { val := uint32(d[i+1])<<8 | uint32(d[i]) if i == ckpos || i == ckpos+2 { val = 0 } sum += val sum = 0xffff & (sum + (sum >> 16)) } h.sum = sum h.size += uint32(n) return n, nil } func (h *peChecksum) Sum(buf []byte) []byte { sum := h.sum sum = 0xffff & (sum + (sum >> 16)) sum += h.size d := make([]byte, 4) binary.LittleEndian.PutUint32(d, sum) return append(buf, d...) } relic-7.6.1/lib/authenticode/msinames.go000066400000000000000000000026731455105530300202210ustar00rootroot00000000000000// // Copyright (c) SAS Institute Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package authenticode // MSI table names are packed in pairs into a Chinese codepoint which then // becomes 2 bytes of UTF-16. This seems to be an attempt to support longer // table names than CDF would otherwise allow but it's incredibly silly. func msiDecodeName(msiName string) string { out := "" for _, x := range msiName { if x >= 0x3800 && x < 0x4800 { x -= 0x3800 out += string(msiDecodeRune(x&0x3f)) + string(msiDecodeRune(x>>6)) } else if x >= 0x4800 && x < 0x4840 { x -= 0x4800 out += string(msiDecodeRune(x)) } else if x == 0x4840 { out += "Table." } else { out += string(x) } } return out } func msiDecodeRune(x rune) rune { if x < 10 { return x + '0' } else if x < 10+26 { return x - 10 + 'A' } else if x < 10+26+26 { return x - 10 - 26 + 'a' } else if x == 10+26+26 { return '.' } else { return '_' } } relic-7.6.1/lib/authenticode/msisign.go000066400000000000000000000030621455105530300200470ustar00rootroot00000000000000// // Copyright (c) SAS Institute Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package authenticode import ( "context" "crypto" "github.com/sassoftware/relic/v7/lib/certloader" "github.com/sassoftware/relic/v7/lib/comdoc" "github.com/sassoftware/relic/v7/lib/pkcs9" ) // Create the Authenticode structure for a MSI file signature using a previously-calculated digest (imprint). func SignMSIImprint(ctx context.Context, digest []byte, hash crypto.Hash, cert *certloader.Certificate, params *OpusParams) (*pkcs9.TimestampedSignature, error) { return SignSip(ctx, digest, hash, msiSipInfo, cert, params) } // Add a signature blob to an open MSI file. The extended signature blob is // added or updated if provided, or deleted if nil. func InsertMSISignature(cdf *comdoc.ComDoc, pkcs, exsig []byte) error { if len(exsig) > 0 { if err := cdf.AddFile(msiDigitalSignatureEx, exsig); err != nil { return err } } else { if err := cdf.DeleteFile(msiDigitalSignatureEx); err != nil { return err } } return cdf.AddFile(msiDigitalSignature, pkcs) } relic-7.6.1/lib/authenticode/msitar.go000066400000000000000000000070331455105530300176770ustar00rootroot00000000000000// // Copyright (c) SAS Institute Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package authenticode import ( "archive/tar" "bytes" "crypto" "fmt" "io" "io/ioutil" "github.com/sassoftware/relic/v7/lib/comdoc" ) const ( msiTarExMeta = "__exmeta" msiTarStorageUID = "__storage_uid" ) // Convert MSI to a tar archive in a form that can be digested and signed as a // stream func MsiToTar(cdf *comdoc.ComDoc, w io.Writer) error { tw := tar.NewWriter(w) // First write the metadata that is needed for an extended signature var buf bytes.Buffer if err := prehashMsiDir(cdf, cdf.RootStorage(), &buf); err != nil { return err } if err := tarAddFile(tw, msiTarExMeta, buf.Bytes()); err != nil { return err } // Now all of the files in the same order they would get digested in if err := msiToTarDir(cdf, tw, cdf.RootStorage(), ""); err != nil { return err } return tw.Close() } // Digset a tarball produced by MsiToTar func DigestMsiTar(r io.Reader, hash crypto.Hash, extended bool) ([]byte, error) { tr := tar.NewReader(r) d := hash.New() for { hdr, err := tr.Next() if err == io.EOF { break } else if err != nil { return nil, err } if hdr.Name == msiTarExMeta { if !extended { continue } exmeta, err := ioutil.ReadAll(tr) if err != nil { return nil, err } d2 := hash.New() d2.Write(exmeta) prehash := d2.Sum(nil) d.Write(prehash) } else if hdr.Name == msiDigitalSignature || hdr.Name == msiDigitalSignatureEx { continue } if _, err := io.Copy(d, tr); err != nil { return nil, err } } return d.Sum(nil), nil } // Add a file with contents blob to an open tar.Writer func tarAddFile(tw *tar.Writer, name string, contents []byte) error { hdr := &tar.Header{Name: name, Mode: 0644, Size: int64(len(contents))} if err := tw.WriteHeader(hdr); err != nil { return err } _, err := tw.Write(contents) return err } // Recursively copy streams from a MSI directory (storage) to a tar.Writer func msiToTarDir(cdf *comdoc.ComDoc, tw *tar.Writer, parent *comdoc.DirEnt, path string) error { files, err := cdf.ListDir(parent) if err != nil { if path == "" { return fmt.Errorf("listing root storage: %w", err) } return fmt.Errorf("listing storage %q: %w", path, err) } sortMsiFiles(files) for _, item := range files { itemPath := path + msiDecodeName(item.Name()) switch item.Type { case comdoc.DirStream: r, err := cdf.ReadStream(item) if err != nil { return fmt.Errorf("reading MSI stream %q: %w", itemPath, err) } hdr := &tar.Header{ Name: itemPath, Mode: 0644, Size: int64(item.StreamSize), } if err := tw.WriteHeader(hdr); err != nil { return err } if _, err := io.Copy(tw, r); err != nil { return fmt.Errorf("transforming MSI stream %q: %w", itemPath, err) } case comdoc.DirStorage: if err := msiToTarDir(cdf, tw, item, itemPath+"/"); err != nil { return err } } } // The UID of each storage gets hashed after its contents so include that, too return tarAddFile(tw, path+msiTarStorageUID, parent.UID[:]) } relic-7.6.1/lib/authenticode/msiverify.go000066400000000000000000000145011455105530300204130ustar00rootroot00000000000000// // Copyright (c) SAS Institute Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package authenticode import ( "bytes" "crypto" "crypto/hmac" "encoding/binary" "errors" "fmt" "io" "sort" "github.com/sassoftware/relic/v7/lib/comdoc" "github.com/sassoftware/relic/v7/lib/pkcs7" "github.com/sassoftware/relic/v7/lib/pkcs9" "github.com/sassoftware/relic/v7/lib/x509tools" "github.com/sassoftware/relic/v7/signers/sigerrors" ) type MSISignature struct { pkcs9.TimestampedSignature Indirect *SpcIndirectDataContentMsi HashFunc crypto.Hash OpusInfo *SpcSpOpusInfo } // Extract and verify the signature of a MSI file. Does not check X509 chains. func VerifyMSI(f io.ReaderAt, skipDigests bool) (*MSISignature, error) { cdf, err := comdoc.ReadFile(f) if err != nil { return nil, err } var sig, exsig []byte files, err := cdf.ListDir(nil) if err != nil { return nil, err } for _, item := range files { name := item.Name() if name == msiDigitalSignature { r, err := cdf.ReadStream(item) if err == nil { sig, err = io.ReadAll(r) } if err != nil { return nil, err } } else if name == msiDigitalSignatureEx { r, err := cdf.ReadStream(item) if err == nil { exsig, err = io.ReadAll(r) } if err != nil { return nil, err } } } if len(sig) == 0 { return nil, sigerrors.NotSignedError{Type: "MSI"} } psd, err := pkcs7.Unmarshal(sig) if err != nil { return nil, err } if !psd.Content.ContentInfo.ContentType.Equal(OidSpcIndirectDataContent) { return nil, errors.New("not an authenticode signature") } pksig, err := psd.Content.Verify(nil, false) if err != nil { return nil, err } ts, err := pkcs9.VerifyOptionalTimestamp(pksig) if err != nil { return nil, err } indirect := new(SpcIndirectDataContentMsi) if err := psd.Content.ContentInfo.Unmarshal(indirect); err != nil { return nil, err } hash, err := x509tools.PkixDigestToHashE(indirect.MessageDigest.DigestAlgorithm) if err != nil { return nil, err } opus, err := GetOpusInfo(pksig.SignerInfo) if err != nil { return nil, err } msisig := &MSISignature{ TimestampedSignature: ts, Indirect: indirect, HashFunc: hash, OpusInfo: opus, } if !skipDigests { imprint, prehash, err := DigestMSI(cdf, hash, exsig != nil) if err != nil { return nil, err } if exsig != nil && !hmac.Equal(prehash, exsig) { return nil, fmt.Errorf("MSI extended digest mismatch: %x != %x", prehash, exsig) } if !hmac.Equal(imprint, indirect.MessageDigest.Digest) { return nil, fmt.Errorf("MSI digest mismatch: %x != %x", imprint, indirect.MessageDigest.Digest) } } return msisig, nil } // Calculate the digest (imprint) of a MSI file. If extended is true then the // MsiDigitalSignatureEx value is also hashed and returned. func DigestMSI(cdf *comdoc.ComDoc, hash crypto.Hash, extended bool) (imprint, prehash []byte, err error) { d := hash.New() if extended { prehash, err = PrehashMSI(cdf, hash) if err != nil { return nil, nil, err } d.Write(prehash) } if err := hashMsiDir(cdf, cdf.RootStorage(), d); err != nil { return nil, nil, err } imprint = d.Sum(nil) return } // Calculates the MsiDigitalSignatureEx blob for a MSI file func PrehashMSI(cdf *comdoc.ComDoc, hash crypto.Hash) ([]byte, error) { d2 := hash.New() if err := prehashMsiDir(cdf, cdf.RootStorage(), d2); err != nil { return nil, err } return d2.Sum(nil), nil } // Recursively hash a MSI directory (storage) func hashMsiDir(cdf *comdoc.ComDoc, parent *comdoc.DirEnt, d io.Writer) error { files, err := cdf.ListDir(parent) if err != nil { return err } sortMsiFiles(files) for _, item := range files { name := item.Name() if name == msiDigitalSignature || name == msiDigitalSignatureEx { continue } switch item.Type { case comdoc.DirStream: r, err := cdf.ReadStream(item) if err != nil { return err } if _, err := io.Copy(d, r); err != nil { return err } case comdoc.DirStorage: if err := hashMsiDir(cdf, item, d); err != nil { return err } } } _, _ = d.Write(parent.UID[:]) return nil } // Recursively hash a MSI directory's extended metadata func prehashMsiDir(cdf *comdoc.ComDoc, parent *comdoc.DirEnt, d io.Writer) error { files, err := cdf.ListDir(parent) if err != nil { return err } sortMsiFiles(files) prehashMsiDirent(parent, d) for _, item := range files { name := item.Name() if name == msiDigitalSignature || name == msiDigitalSignatureEx { continue } switch item.Type { case comdoc.DirStream: prehashMsiDirent(item, d) case comdoc.DirStorage: if err := prehashMsiDir(cdf, item, d); err != nil { return err } } } return nil } // Hash a MSI stream's extended metadata func prehashMsiDirent(item *comdoc.DirEnt, d io.Writer) { buf := bytes.NewBuffer(make([]byte, 0, 128)) _ = binary.Write(buf, binary.LittleEndian, item.RawDirEnt) enc := buf.Bytes() // Name if item.Type != comdoc.DirRoot { _, _ = d.Write(enc[:item.NameLength-2]) } // UID if item.Type == comdoc.DirRoot || item.Type == comdoc.DirStorage { _, _ = d.Write(item.UID[:]) } // Size if item.Type == comdoc.DirStream { _, _ = d.Write(enc[120:124]) } // flags _, _ = d.Write(enc[96:100]) // ctime, mtime if item.Type != comdoc.DirRoot { _, _ = d.Write(enc[100:116]) } } // Sort a list of MSI streams in the order needed for hashing func sortMsiFiles(files []*comdoc.DirEnt) { sort.Slice(files, func(i, j int) bool { a, b := files[i], files[j] n := a.NameLength if b.NameLength < n { n = b.NameLength } // do a comparison of the utf16 in its original LE form for k := uint16(0); k < n; k++ { x, y := a.NameRunes[k], b.NameRunes[k] x1, y1 := x&0xff, y&0xff if x1 != y1 { return x1 < y1 } x2, y2 := x>>8, y>>8 if x2 != y2 { return x2 < y2 } } return a.NameLength > b.NameLength // yes, greater than }) } relic-7.6.1/lib/authenticode/pedigest.go000066400000000000000000000246771455105530300202210ustar00rootroot00000000000000// // Copyright (c) SAS Institute Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package authenticode import ( "bytes" "crypto" "debug/pe" "encoding/binary" "errors" "fmt" "hash" "io" "io/ioutil" ) // PE-COFF: https://www.microsoft.com/en-us/download/details.aspx?id=19509 // PE Authenticode: http://msdn.microsoft.com/en-us/windows/hardware/gg463180.aspx type PEDigest struct { OrigSize int64 CertStart int64 Imprint []byte PageHashes []byte Hash crypto.Hash markers *peHeaderValues } const dosHeaderSize = 64 // Calculate a digest (message imprint) over a PE image. Returns a structure // that can be used to sign the imprint and produce a binary patch to apply the // signature. func DigestPE(r io.Reader, hash crypto.Hash, doPageHash bool) (*PEDigest, error) { // Read and buffer all the headers buf := bytes.NewBuffer(make([]byte, 0, 4096)) peStart, err := readDosHeader(r, buf) if err != nil { return nil, err } if _, err := io.CopyN(buf, r, peStart-dosHeaderSize); err != nil { return nil, err } fh, err := readCoffHeader(r, buf) if err != nil { return nil, err } hvals, err := readOptHeader(r, buf, peStart, fh) if err != nil { return nil, err } sections, err := readSections(r, buf, fh, hvals) if err != nil { return nil, err } digester := setupDigester(hash, buf.Bytes(), hvals, sections, doPageHash) // Hash sections nextSection := hvals.sizeOfHdr for i, sh := range sections { if sh.SizeOfRawData == 0 { continue } if int64(sh.PointerToRawData) != nextSection { return nil, fmt.Errorf("PE section %d begins at 0x%x but expected 0x%x", i, sh.PointerToRawData, nextSection) } if err := digester.section(r, sh); err != nil { return nil, err } nextSection += int64(sh.SizeOfRawData) } // Hash trailer after the sections and cert table origSize, err := readTrailer(r, digester.imageDigest, nextSection, hvals.certStart, hvals.certSize) if err != nil { return nil, err } certStart := origSize if n := origSize % 8; n != 0 { // pad to 8 bytes padding := 8 - n digester.imageDigest.Write(make([]byte, padding)) certStart += padding } imprint, pagehashes, err := digester.finish() if err != nil { return nil, err } return &PEDigest{origSize, certStart, imprint, pagehashes, hash, hvals}, nil } type imageHasher struct { hashFunc crypto.Hash imageDigest hash.Hash pageHashes []byte zeroPage []byte pageBuf []byte doPageHash bool lastPage uint32 } func setupDigester(hash crypto.Hash, header []byte, hvals *peHeaderValues, sections []pe.SectionHeader32, doPageHash bool) *imageHasher { imageDigest := hash.New() imageDigest.Write(header) h := &imageHasher{hashFunc: hash, imageDigest: imageDigest, doPageHash: doPageHash} if doPageHash { h.zeroPage = make([]byte, hvals.pageSize) // full page of zeroes, for padding h.pageBuf = make([]byte, hvals.pageSize) // scratch space // make space for all the page hashes pages := 2 for _, sh := range sections { spage := (sh.SizeOfRawData + hvals.pageSize - 1) / hvals.pageSize pages += int(spage) } h.pageHashes = make([]byte, 0, pages*(4+hash.Size())) // the first page is the headers padded out to a full page with the // signature bits snipped out in the same way as for the regular // imprint. the padding is done based on the full size of the // header, so the data being hashed is 12 bytes short of a full // page removed := int(hvals.sizeOfHdr) - len(header) h.addPageHash(0, header, removed) } return h } func (h *imageHasher) section(r io.Reader, sh pe.SectionHeader32) error { if !h.doPageHash { _, err := io.CopyN(h.imageDigest, r, int64(sh.SizeOfRawData)) return err } position := sh.PointerToRawData remaining := int(sh.SizeOfRawData) for remaining > 0 { n := remaining if n > len(h.pageBuf) { n = len(h.pageBuf) } buf := h.pageBuf[:n] if _, err := io.ReadFull(r, buf); err != nil { return err } h.imageDigest.Write(buf) h.addPageHash(position, buf, 0) position += uint32(n) remaining -= n h.lastPage = position } return nil } func (h *imageHasher) finish() ([]byte, []byte, error) { sum := h.imageDigest.Sum(nil) if h.doPageHash { h.addPageHash(h.lastPage, nil, 0) } return sum, h.pageHashes, nil } func (h *imageHasher) addPageHash(offset uint32, blob []byte, removed int) { var obytes [4]byte binary.LittleEndian.PutUint32(obytes[:], offset) h.pageHashes = append(h.pageHashes, obytes[:]...) if len(blob) == 0 { // last "page" has a null digest h.pageHashes = append(h.pageHashes, make([]byte, h.hashFunc.Size())...) return } d := h.hashFunc.New() d.Write(blob) needzero := len(h.zeroPage) - len(blob) - removed d.Write(h.zeroPage[:needzero]) h.pageHashes = d.Sum(h.pageHashes) } func readDosHeader(r io.Reader, d io.Writer) (int64, error) { dosheader, err := readAndHash(r, d, dosHeaderSize) if err != nil { return 0, err } else if dosheader[0] != 'M' || dosheader[1] != 'Z' { return 0, errors.New("not a PE file") } return int64(binary.LittleEndian.Uint32(dosheader[0x3c:])), nil } func readCoffHeader(r io.Reader, d io.Writer) (*pe.FileHeader, error) { if magic, err := readAndHash(r, d, 4); err != nil { return nil, err } else if magic[0] != 'P' || magic[1] != 'E' || magic[2] != 0 || magic[3] != 0 { return nil, errors.New("not a PE file") } buf, err := readAndHash(r, d, 20) if err != nil { return nil, err } hdr := new(pe.FileHeader) if err := binaryReadBytes(buf, hdr); err != nil { return nil, err } return hdr, nil } const ( // https://learn.microsoft.com/en-us/windows/win32/debug/pe-format#optional-header-image-only optHeaderMagicPE32 = 0x10b optHeaderMagicPE32Plus = 0x20b // https://learn.microsoft.com/en-us/windows/win32/debug/pe-format#machine-types imageFileMachineAlpha = 0x184 imageFileMachineAlpha64 = 0x284 ) func readOptHeader(r io.Reader, d io.Writer, peStart int64, fh *pe.FileHeader) (*peHeaderValues, error) { hvals := new(peHeaderValues) hvals.peStart = peStart // https://devblogs.microsoft.com/oldnewthing/20210510-00/ switch fh.Machine { case pe.IMAGE_FILE_MACHINE_IA64, imageFileMachineAlpha, imageFileMachineAlpha64: hvals.pageSize = 8192 default: hvals.pageSize = 4096 } buf := make([]byte, fh.SizeOfOptionalHeader) if _, err := io.ReadFull(r, buf); err != nil { return nil, err } // locate the bits that need to be omitted from hash cksumStart := 64 cksumEnd := cksumStart + 4 var dd4Start int64 var dd pe.DataDirectory optMagic := binary.LittleEndian.Uint16(buf[:2]) switch optMagic { case optHeaderMagicPE32: // PE32 var opt pe.OptionalHeader32 if err := binaryReadBytes(buf, &opt); err != nil { return nil, err } if opt.NumberOfRvaAndSizes < 5 { return nil, errors.New("PE header did not leave room for signature") } dd = opt.DataDirectory[4] dd4Start = 128 hvals.sizeOfHdr = int64(opt.SizeOfHeaders) hvals.fileAlign = opt.FileAlignment case optHeaderMagicPE32Plus: // PE32+ var opt pe.OptionalHeader64 if err := binaryReadBytes(buf, &opt); err != nil { return nil, err } if opt.NumberOfRvaAndSizes < 5 { return nil, errors.New("PE header did not leave room for signature") } dd = opt.DataDirectory[4] dd4Start = 144 hvals.sizeOfHdr = int64(opt.SizeOfHeaders) hvals.fileAlign = opt.FileAlignment default: return nil, errors.New("unrecognized optional header magic") } dd4End := dd4Start + 8 hvals.certStart = int64(dd.VirtualAddress) hvals.certSize = int64(dd.Size) hvals.secTblStart = peStart + 24 + int64(fh.SizeOfOptionalHeader) _, _ = d.Write(buf[:cksumStart]) _, _ = d.Write(buf[cksumEnd:dd4Start]) _, _ = d.Write(buf[dd4End:]) hvals.posDDCert = peStart + 24 + dd4Start return hvals, nil } func readSections(r io.Reader, d io.Writer, fh *pe.FileHeader, hvals *peHeaderValues) ([]pe.SectionHeader32, error) { // read and hash section table sections := make([]pe.SectionHeader32, fh.NumberOfSections) size := int(fh.NumberOfSections) * 40 secTblEnd := hvals.secTblStart + int64(size) if secTblEnd > hvals.sizeOfHdr { return nil, errors.New("PE section overlaps section table") } if buf, err := readAndHash(r, d, size); err != nil { return nil, err } else if err := binaryReadBytes(buf, sections); err != nil { return nil, err } // look for start of first section and check for overlap for i, section := range sections { if section.SizeOfRawData == 0 { continue } if p := int64(section.PointerToRawData); p < secTblEnd { return nil, fmt.Errorf("PE section %d at 0x%x overlaps section table at 0x%x", i, p, secTblEnd) } else if p < hvals.sizeOfHdr { // some samples have a SizeOfHeaders that goes past the start of the first section hvals.sizeOfHdr = p } // Adjust any sections that are not properly aligned sections[i].SizeOfRawData = align32(section.SizeOfRawData, hvals.fileAlign) } // hash the padding after the section table if _, err := io.CopyN(d, r, hvals.sizeOfHdr-secTblEnd); err != nil { return nil, err } return sections, nil } func readTrailer(r io.Reader, d io.Writer, lastSection, certStart, certSize int64) (int64, error) { if certSize == 0 { n, err := io.Copy(d, r) return lastSection + n, err } if certStart < lastSection { return 0, errors.New("existing signature overlaps with PE sections") } if _, err := io.CopyN(d, r, certStart-lastSection); err != nil { return 0, err } if _, err := io.CopyN(ioutil.Discard, r, certSize); err != nil { return 0, err } if n, _ := io.Copy(ioutil.Discard, r); n > 0 { return 0, errors.New("trailing garbage after existing certificate") } return certStart, nil } type peHeaderValues struct { // start of PE header peStart int64 // file offset to the data directory entry for the cert table, in the optional header posDDCert int64 // file offset to the end of the optional header and the start of the section table secTblStart int64 // size of all headers plus padding sizeOfHdr int64 // architecture page size pageSize uint32 // section alignment in file fileAlign uint32 // file offset and size of the certificate table certStart, certSize int64 } relic-7.6.1/lib/authenticode/pesign.go000066400000000000000000000067221455105530300176710ustar00rootroot00000000000000// // Copyright (c) SAS Institute Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package authenticode import ( "bytes" "context" "crypto" "debug/pe" "encoding/asn1" "encoding/binary" "errors" "github.com/sassoftware/relic/v7/lib/binpatch" "github.com/sassoftware/relic/v7/lib/certloader" "github.com/sassoftware/relic/v7/lib/pkcs9" ) // Sign the digest and return an Authenticode structure func (pd *PEDigest) Sign(ctx context.Context, cert *certloader.Certificate, params *OpusParams) (*binpatch.PatchSet, *pkcs9.TimestampedSignature, error) { indirect, err := pd.GetIndirect() if err != nil { return nil, nil, err } ts, err := signIndirect(ctx, indirect, pd.Hash, cert, params) if err != nil { return nil, nil, err } patch, err := pd.MakePatch(ts.Raw) if err != nil { return nil, nil, err } return patch, ts, nil } func (pd *PEDigest) GetIndirect() (indirect SpcIndirectDataContentPe, err error) { indirect, err = makePeIndirect(pd.Imprint, pd.Hash, OidSpcPeImageData) if err != nil { return } if len(pd.PageHashes) > 0 { if err2 := pd.imprintPageHashes(&indirect); err2 != nil { err = err2 return } } return } func (pd *PEDigest) imprintPageHashes(indirect *SpcIndirectDataContentPe) error { var attr SpcAttributePageHashes switch pd.Hash { case crypto.SHA1: attr.Type = OidSpcPageHashV1 case crypto.SHA256: attr.Type = OidSpcPageHashV2 default: return errors.New("unsupported page hash type") } attr.Hashes = make([][]byte, 1) attr.Hashes[0] = pd.PageHashes blob, err := asn1.Marshal(attr) if err != nil { return err } serdata, err := asn1.Marshal(makeSet(blob)) if err != nil { return err } indirect.Data.Value.File = SpcLink{} indirect.Data.Value.File.Moniker.ClassID = SpcUUIDPageHashes indirect.Data.Value.File.Moniker.SerializedData = serdata return nil } // Create a patchset that will add or replace the signature from a previously // digested image with a new one func (pd *PEDigest) MakePatch(sig []byte) (*binpatch.PatchSet, error) { // pack new cert table padded := (len(sig) + 7) / 8 * 8 info := certInfo{ Length: uint32(8 + padded), Revision: 0x0200, CertificateType: 0x0002, } var buf bytes.Buffer pad2 := pd.CertStart - pd.OrigSize if pad2 != 0 { buf.Write(make([]byte, pad2)) } _ = binary.Write(&buf, binary.LittleEndian, info) _, _ = buf.Write(sig) _, _ = buf.Write(make([]byte, padded-len(sig))) // pack data directory certTbl := buf.Bytes() var dd pe.DataDirectory if pd.CertStart >= (1 << 32) { return nil, errors.New("PE file is too big") } dd.VirtualAddress = uint32(pd.CertStart) dd.Size = uint32(len(certTbl)) - uint32(pad2) var buf2 bytes.Buffer _ = binary.Write(&buf2, binary.LittleEndian, dd) // make patch patch := binpatch.New() patch.Add(pd.markers.posDDCert, 8, buf2.Bytes()) patch.Add(pd.OrigSize, int64(pd.markers.certSize), certTbl) return patch, nil } type certInfo struct { Length uint32 Revision uint16 CertificateType uint16 } relic-7.6.1/lib/authenticode/peverify.go000066400000000000000000000146201455105530300202310ustar00rootroot00000000000000// // Copyright (c) SAS Institute Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package authenticode import ( "bytes" "crypto" "crypto/hmac" "encoding/asn1" "encoding/binary" "errors" "fmt" "io" "github.com/sassoftware/relic/v7/lib/pkcs7" "github.com/sassoftware/relic/v7/lib/pkcs9" "github.com/sassoftware/relic/v7/lib/x509tools" "github.com/sassoftware/relic/v7/signers/sigerrors" ) type PESignature struct { pkcs9.TimestampedSignature Indirect *SpcIndirectDataContentPe OpusInfo *SpcSpOpusInfo ImageHashFunc crypto.Hash PageHashes []byte PageHashFunc crypto.Hash } // Extract and verify the signature from a PE/COFF image file. Does not check X509 chains. func VerifyPE(r io.ReadSeeker, skipDigests bool) ([]PESignature, error) { hvals, err := findSignatures(r) if err != nil { return nil, err } else if hvals.certSize == 0 { return nil, sigerrors.NotSignedError{Type: "PECOFF"} } // Read certificate table sigblob := make([]byte, hvals.certSize) if _, err := r.Seek(hvals.certStart, 0); err != nil { return nil, err } if _, err := io.ReadFull(r, sigblob); err != nil { return nil, err } // Parse and verify signatures if skipDigests { r = nil } return checkSignatures(sigblob, r) } func findSignatures(r io.ReadSeeker) (*peHeaderValues, error) { if _, err := r.Seek(0, 0); err != nil { return nil, err } d := io.Discard peStart, err := readDosHeader(r, d) if err != nil { return nil, err } if _, err := r.Seek(peStart, 0); err != nil { return nil, err } fh, err := readCoffHeader(r, d) if err != nil { return nil, err } return readOptHeader(r, d, peStart, fh) } func checkSignatures(blob []byte, image io.ReadSeeker) ([]PESignature, error) { values := make(map[crypto.Hash][]byte) phvalues := make(map[crypto.Hash][]byte) allhashes := make(map[crypto.Hash]bool) sigs := make([]PESignature, 0, 1) for len(blob) != 0 { if len(blob) < 4 { return nil, errors.New("invalid certificate table") } wLen := binary.LittleEndian.Uint32(blob[:4]) end := (int(wLen) + 7) / 8 * 8 size := int(wLen) - 8 if end > len(blob) || size < 0 { return nil, errors.New("invalid certificate table") } cert := blob[8 : 8+size] blob = blob[end:] sig, err := checkSignature(cert) if err != nil { return nil, err } allhashes[sig.ImageHashFunc] = true if len(sig.PageHashes) > 0 { phvalues[sig.PageHashFunc] = sig.PageHashes allhashes[sig.PageHashFunc] = true } sigs = append(sigs, *sig) imageDigest := sig.Indirect.MessageDigest.Digest if existing := values[sig.ImageHashFunc]; existing == nil { values[sig.ImageHashFunc] = imageDigest } else if !hmac.Equal(imageDigest, existing) { // they can't both be right... return nil, fmt.Errorf("digest mismatch: %x != %x", imageDigest, existing) } } if image == nil { return sigs, nil } for hash := range allhashes { imagehash := values[hash] pagehashes := phvalues[hash] if _, err := image.Seek(0, 0); err != nil { return nil, err } doPageHashes := len(pagehashes) > 0 digest, err := DigestPE(image, hash, doPageHashes) if err != nil { return sigs, err } if imagehash != nil && !hmac.Equal(digest.Imprint, imagehash) { return sigs, fmt.Errorf("digest mismatch: %x != %x", digest.Imprint, imagehash) } if pagehashes != nil && !hmac.Equal(digest.PageHashes, pagehashes) { return sigs, fmt.Errorf("page hash mismatch") } } return sigs, nil } func checkSignature(der []byte) (*PESignature, error) { psd, err := pkcs7.Unmarshal(der) if err != nil { return nil, fmt.Errorf("unmarshaling authenticode signature: %w", err) } if !psd.Content.ContentInfo.ContentType.Equal(OidSpcIndirectDataContent) { return nil, errors.New("not an authenticode signature") } sig, err := psd.Content.Verify(nil, false) if err != nil { return nil, fmt.Errorf("verifying indirect signature: %w", err) } ts, err := pkcs9.VerifyOptionalTimestamp(sig) if err != nil { return nil, fmt.Errorf("verifying timestamp: %w", err) } indirect := new(SpcIndirectDataContentPe) if err := psd.Content.ContentInfo.Unmarshal(indirect); err != nil { return nil, fmt.Errorf("unmarshaling SpcIndirectDataContentPe: %w", err) } hash, err := x509tools.PkixDigestToHashE(indirect.MessageDigest.DigestAlgorithm) if err != nil { return nil, err } opus, err := GetOpusInfo(sig.SignerInfo) if err != nil { return nil, err } pesig := &PESignature{ TimestampedSignature: ts, Indirect: indirect, OpusInfo: opus, ImageHashFunc: hash, } if err := readPageHashes(pesig); err != nil { return nil, err } return pesig, nil } func GetOpusInfo(si *pkcs7.SignerInfo) (*SpcSpOpusInfo, error) { opus := new(SpcSpOpusInfo) err := si.AuthenticatedAttributes.GetOne(OidSpcSpOpusInfo, opus) if err == nil { return opus, nil } else if errors.As(err, &pkcs7.ErrNoAttribute{}) { return nil, nil } return nil, fmt.Errorf("parsing SpcSpOpusInfo attribute: %w", err) } func readPageHashes(sig *PESignature) error { serObj := sig.Indirect.Data.Value.File.Moniker if !bytes.Equal(serObj.ClassID, SpcUUIDPageHashes) { // not present return nil } // unnecessary SET wrapped around the solitary attribute SEQ var attrRaw asn1.RawValue if _, err := asn1.Unmarshal(serObj.SerializedData, &attrRaw); err != nil { return fmt.Errorf("unmarshaling page hashes: %w", err) } var attr SpcAttributePageHashes if _, err := asn1.Unmarshal(attrRaw.Bytes, &attr); err != nil { return fmt.Errorf("unmarshaling SpcAttributePageHashes: %w", err) } switch { case attr.Type.Equal(OidSpcPageHashV1): sig.PageHashFunc = crypto.SHA1 case attr.Type.Equal(OidSpcPageHashV2): sig.PageHashFunc = crypto.SHA256 default: return errors.New("unknown page hash format") } // unnecessary SET wrapped around the octets too sig.PageHashes = attr.Hashes[0] if len(sig.PageHashes) == 0 || len(sig.PageHashes)%(4+sig.PageHashFunc.Size()) != 0 { return errors.New("malformed page hash") } return nil } relic-7.6.1/lib/authenticode/powershell.go000066400000000000000000000213741455105530300205700ustar00rootroot00000000000000// // Copyright (c) SAS Institute Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package authenticode import ( "bufio" "bytes" "context" "crypto" "crypto/hmac" "encoding/base64" "encoding/binary" "errors" "fmt" "io" "path/filepath" "strings" "unicode/utf16" "github.com/sassoftware/relic/v7/lib/binpatch" "github.com/sassoftware/relic/v7/lib/certloader" "github.com/sassoftware/relic/v7/lib/pkcs7" "github.com/sassoftware/relic/v7/lib/pkcs9" "github.com/sassoftware/relic/v7/lib/x509tools" "github.com/sassoftware/relic/v7/signers/sigerrors" ) // Type of signature formatting used for different PowerShell file formats type PsSigStyle int const ( // "Hash" style used by e.g. .ps1 files SigStyleHash PsSigStyle = iota + 1 // XML style used by e.g. .ps1xml files SigStyleXML // C# style used by .mof files SigStyleC ) var psExtMap = map[string]PsSigStyle{ ".ps1": SigStyleHash, ".ps1xml": SigStyleXML, ".psc1": SigStyleXML, ".psd1": SigStyleHash, ".psm1": SigStyleHash, ".cdxml": SigStyleXML, ".mof": SigStyleC, } const psBegin = "SIG # Begin signature block" const psEnd = "SIG # End signature block" type sigStyle struct{ start, end string } var psStyles = map[PsSigStyle]sigStyle{ SigStyleHash: {"# ", ""}, SigStyleXML: {""}, SigStyleC: {"/* ", " */"}, } // Get the PowerShell signature style for a filename or extension func GetSigStyle(filename string) (PsSigStyle, bool) { style, ok := psExtMap[filepath.Ext(filename)] return style, ok } // Return all supported PowerShell signature styles func AllSigStyles() []string { var ret []string for k := range psExtMap { ret = append(ret, k) } return ret } type PsDigest struct { Imprint []byte HashFunc crypto.Hash TextSize, SigSize int64 SigStyle PsSigStyle IsUtf16 bool } // Digest a PowerShell script from a stream, returning the sum and the length of the digested bytes. // // PowerShell scripts are digested in UTF-16-LE format so, unless already in // that format, the text is converted first. Existing signatures are discarded. func DigestPowershell(r io.Reader, style PsSigStyle, hash crypto.Hash) (*PsDigest, error) { si, ok := psStyles[style] if !ok { return nil, errors.New("invalid powershell signature style") } br := bufio.NewReader(r) isUtf16, first, _ := detectUtf16(br, si.start, si.end) d := hash.New() var textSize, sigSize int64 var saved string for { line, err := readLine(br, isUtf16) if err != nil && err != io.EOF { return nil, err } if line == first { // remove EOL from previous line if isUtf16 { saved = saved[:len(saved)-4] sigSize = 4 } else { saved = saved[:len(saved)-2] sigSize = 2 } // count the size of the signature sigSize += int64(len(line)) n, err := io.Copy(io.Discard, br) if err != nil { return nil, err } sigSize += n break } else { _ = writeUtf16(d, saved, isUtf16) textSize += int64(len(saved)) saved = line } if err == io.EOF { break } } _ = writeUtf16(d, saved, isUtf16) textSize += int64(len(saved)) return &PsDigest{d.Sum(nil), hash, textSize, sigSize, style, isUtf16}, nil } func detectUtf16(br *bufio.Reader, start, end string) (bool, string, string) { first := start + psBegin + end + "\r\n" last := start + psEnd + end + "\r\n" if bom, err := br.Peek(2); err == nil && bom[0] == 0xff && bom[1] == 0xfe { // UTF-16-LE return true, toUtf16(first), toUtf16(last) } return false, first, last } type PowershellSignature struct { pkcs9.TimestampedSignature OpusInfo *SpcSpOpusInfo HashFunc crypto.Hash } // Verify a PowerShell script. The signature "style" must already have been // determined by calling GetSigStyle func VerifyPowershell(r io.ReadSeeker, style PsSigStyle, skipDigests bool) (*PowershellSignature, error) { si, ok := psStyles[style] if !ok { return nil, errors.New("invalid powershell signature style") } br := bufio.NewReader(r) isUtf16, first, last := detectUtf16(br, si.start, si.end) found := false var textSize int64 var pkcsb bytes.Buffer for { line, err := readLine(br, isUtf16) if err == io.EOF && !found { return nil, sigerrors.NotSignedError{Type: "powershell document"} } else if err != nil { return nil, err } if found && line == last { break } else if found { lstr := string(line) if isUtf16 { lstr = fromUtf16(line) } if !strings.HasPrefix(lstr, si.start) || !strings.HasSuffix(lstr, si.end+"\r\n") { return nil, errors.New("malformed powershell signature") } i := len(si.start) j := len(lstr) - len(si.end) - 2 lder, err := base64.StdEncoding.DecodeString(lstr[i:j]) if err != nil { return nil, err } pkcsb.Write(lder) } else if line == first { // remove preceding \r\n from size if isUtf16 { textSize -= 4 } else { textSize -= 2 } found = true } else { textSize += int64(len(line)) } } psd, err := pkcs7.Unmarshal(pkcsb.Bytes()) if err != nil { return nil, err } if !psd.Content.ContentInfo.ContentType.Equal(OidSpcIndirectDataContent) { return nil, errors.New("not an authenticode signature") } sig, err := psd.Content.Verify(nil, false) if err != nil { return nil, err } ts, err := pkcs9.VerifyOptionalTimestamp(sig) if err != nil { return nil, err } indirect := new(SpcIndirectDataContentMsi) if err := psd.Content.ContentInfo.Unmarshal(indirect); err != nil { return nil, err } hash, err := x509tools.PkixDigestToHashE(indirect.MessageDigest.DigestAlgorithm) if err != nil { return nil, err } if !skipDigests { if _, err := r.Seek(0, 0); err != nil { return nil, err } digest, err := DigestPowershell(r, style, hash) if err != nil { return nil, err } if !hmac.Equal(digest.Imprint, indirect.MessageDigest.Digest) { return nil, fmt.Errorf("digest mismatch: %x != %x", digest.Imprint, indirect.MessageDigest.Digest) } } opus, err := GetOpusInfo(ts.SignerInfo) if err != nil { return nil, err } pss := &PowershellSignature{ TimestampedSignature: ts, HashFunc: hash, OpusInfo: opus, } return pss, nil } // Sign a previously digested PowerShell script and return the Authenticode structure func (pd *PsDigest) Sign(ctx context.Context, cert *certloader.Certificate, params *OpusParams) (*binpatch.PatchSet, *pkcs9.TimestampedSignature, error) { ts, err := SignSip(ctx, pd.Imprint, pd.HashFunc, psSipInfo, cert, params) if err != nil { return nil, nil, err } patch, err := pd.MakePatch(ts.Raw) if err != nil { return nil, nil, err } return patch, ts, nil } // Create a patchset that will add or replace the signature on the digested script func (pd *PsDigest) MakePatch(sig []byte) (*binpatch.PatchSet, error) { si, ok := psStyles[pd.SigStyle] if !ok { return nil, errors.New("invalid powershell signature style") } var buf bytes.Buffer buf.WriteString("\r\n" + si.start + psBegin + si.end + "\r\n") b64 := base64.StdEncoding.EncodeToString(sig) for i := 0; i < len(b64); i += 64 { j := i + 64 if j > len(b64) { j = len(b64) } buf.WriteString(si.start + b64[i:j] + si.end + "\r\n") } buf.WriteString(si.start + psEnd + si.end + "\r\n") patch := binpatch.New() var encoded []byte if pd.IsUtf16 { encoded = []byte(toUtf16(buf.String())) } else { encoded = buf.Bytes() } patch.Add(pd.TextSize, int64(pd.SigSize), encoded) return patch, nil } func readLine(br *bufio.Reader, isUtf16 bool) (string, error) { line, err := br.ReadString('\n') if isUtf16 && err == nil { // \n\0 var zero byte zero, err = br.ReadByte() if zero != 0 { return "", errors.New("malformed utf16") } line += "\x00" } return line, err } // Convert UTF8 to UTF-16-LE func toUtf16(x string) string { runes := utf16.Encode([]rune(x)) buf := bytes.NewBuffer(make([]byte, 0, 2*len(runes))) _ = binary.Write(buf, binary.LittleEndian, runes) return buf.String() } // Convert UTF8 to UTF-16-LE and write it to "d" func writeUtf16(d io.Writer, x string, isUtf16 bool) error { if isUtf16 { _, err := d.Write([]byte(x)) return err } runes := utf16.Encode([]rune(x)) return binary.Write(d, binary.LittleEndian, runes) } // Convert UTF-16-LE to UTF8 func fromUtf16(x string) string { runes := make([]uint16, len(x)/2) _ = binary.Read(bytes.NewReader([]byte(x)), binary.LittleEndian, runes) return string(utf16.Decode(runes)) } relic-7.6.1/lib/authenticode/structs.go000066400000000000000000000134531455105530300201120ustar00rootroot00000000000000// // Copyright (c) SAS Institute Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package authenticode import ( "crypto/x509/pkix" "encoding/asn1" "encoding/binary" "time" "unicode/utf16" ) var ( OidSpcIndirectDataContent = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 311, 2, 1, 4} OidSpcStatementType = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 311, 2, 1, 11} OidSpcSpOpusInfo = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 311, 2, 1, 12} OidSpcPeImageData = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 311, 2, 1, 15} OidSpcIndividualPurpose = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 311, 2, 1, 21} OidSpcCabImageData = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 311, 2, 1, 25} OidSpcSipInfo = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 311, 2, 1, 30} OidSpcPageHashV1 = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 311, 2, 3, 1} OidSpcPageHashV2 = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 311, 2, 3, 2} OidSpcCabPageHash = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 311, 2, 5, 1} OidCertTrustList = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 311, 10, 1} OidCatalogList = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 311, 12, 1, 1} OidCatalogListMember = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 311, 12, 1, 2} OidCatalogListMemberV2 = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 311, 12, 1, 3} OidCatalogNameValue = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 311, 12, 2, 1} OidCatalogMemberInfo = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 311, 12, 2, 2} OidCatalogMemberInfoV2 = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 311, 12, 2, 3} SpcUUIDPageHashes = []byte{0xa6, 0xb5, 0x86, 0xd5, 0xb4, 0xa1, 0x24, 0x66, 0xae, 0x05, 0xa2, 0x17, 0xda, 0x8e, 0x60, 0xd6} // SIP or Subject Interface Package is an internal Microsoft API for // transforming arbitrary files into a digestible stream. These ClassIDs // are found in the indirect data section and identify the type of processor needed to validate the signature. // SIP related DLLs are registered at // HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Cryptography\OID\EncodingType 0\CryptSIPDllCreateIndirectData // although these particular ClassIDs do not seem to appear there. // Relevant DLLs include: WINTRUST.DLL, MSISIP.DLL, pwrshsip.dll SpcUUIDSipInfoMsi = []byte{0xf1, 0x10, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46} SpcUUIDSipInfoPs = []byte{0x1f, 0xcc, 0x3b, 0x60, 0x59, 0x4b, 0x08, 0x4e, 0xb7, 0x24, 0xd2, 0xc6, 0x29, 0x7e, 0xf3, 0x51} // This one is used in V1 security catalogs CryptSipCreateIndirectData = "{C689AAB8-8E78-11D0-8C47-00C04FC295EE}" // Filenames for MSI streams holding signature data msiDigitalSignature = "\x05DigitalSignature" msiDigitalSignatureEx = "\x05MsiDigitalSignatureEx" ) type SpcIndirectDataContentPe struct { Data SpcAttributePeImageData MessageDigest DigestInfo } type SpcAttributePeImageData struct { Type asn1.ObjectIdentifier Value SpcPeImageData `asn1:"optional"` } type DigestInfo struct { DigestAlgorithm pkix.AlgorithmIdentifier Digest []byte } type SpcPeImageData struct { Flags asn1.BitString File SpcLink `asn1:"tag:0"` } type SpcLink struct { URL string `asn1:"optional,tag:0,ia5"` Moniker SpcSerializedObject `asn1:"optional,tag:1"` File SpcString `asn1:"optional,tag:2"` } type SpcString struct { Unicode []byte `asn1:"optional,tag:0"` // BMPString ASCII string `asn1:"optional,tag:1,ia5"` } func NewSpcString(value string) SpcString { runes := utf16.Encode([]rune(value)) raw := make([]byte, 2*len(runes)) for i, r := range runes { binary.BigEndian.PutUint16(raw[i*2:], r) } return SpcString{Unicode: raw} } func (s SpcString) String() string { if len(s.Unicode) != 0 && len(s.Unicode)%2 == 0 { words := make([]uint16, len(s.Unicode)/2) for i := range words { words[i] = binary.BigEndian.Uint16(s.Unicode[i*2:]) } runes := utf16.Decode(words) return string(runes) } return s.ASCII } type SpcSerializedObject struct { ClassID []byte SerializedData []byte } type SpcAttributePageHashes struct { Type asn1.ObjectIdentifier Hashes [][]byte `asn1:"set"` } type SpcSpOpusInfo struct { ProgramName SpcString `asn1:"optional,tag:0"` MoreInfo SpcLink `asn1:"optional,tag:1"` } type SpcSpStatementType struct { Type asn1.ObjectIdentifier } type SpcIndirectDataContentMsi struct { Data SpcAttributeMsiImageData MessageDigest DigestInfo } type SpcAttributeMsiImageData struct { Type asn1.ObjectIdentifier Value SpcSipInfo `asn1:"optional"` } type SpcSipInfo struct { A int UUID []byte B, C, D, E, F int } var msiSipInfo = SpcSipInfo{1, SpcUUIDSipInfoMsi, 0, 0, 0, 0, 0} var psSipInfo = SpcSipInfo{65536, SpcUUIDSipInfoPs, 0, 0, 0, 0, 0} type CertTrustList struct { SubjectUsage []asn1.ObjectIdentifier ListIdentifier []byte EffectiveDate time.Time SubjectAlgorithm pkix.AlgorithmIdentifier Entries []CertTrustEntry Attributes *CertTrustAttributes `asn1:"optional,explicit,tag:0"` } type CertTrustEntry struct { Tag []byte Values []CertTrustValue `asn1:"set"` } type CertTrustValue struct { Attribute asn1.ObjectIdentifier Value asn1.RawValue } type CertTrustMemberInfoV1 struct { ClassID asn1.RawValue Unknown1 int } type CertTrustAttributes struct { // TODO } relic-7.6.1/lib/authenticode/util.go000066400000000000000000000025251455105530300173560ustar00rootroot00000000000000// // Copyright (c) SAS Institute Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package authenticode import ( "bytes" "encoding/binary" "io" ) // read bytes from a stream and return a byte slice, also feeding a hash func readAndHash(r io.Reader, d io.Writer, n int) ([]byte, error) { if n == 0 { return nil, nil } buf := make([]byte, n) if _, err := io.ReadFull(r, buf); err != nil { return nil, err } if d != nil { if _, err := d.Write(buf); err != nil { return nil, err } } return buf, nil } // read from a byte slice into a structure func binaryReadBytes(buf []byte, val interface{}) error { return binary.Read(bytes.NewReader(buf), binary.LittleEndian, val) } // pad an address to a multiple of align func align32(addr, align uint32) uint32 { n := addr % align if n != 0 { addr += align - n } return addr } relic-7.6.1/lib/binpatch/000077500000000000000000000000001455105530300151625ustar00rootroot00000000000000relic-7.6.1/lib/binpatch/binpatch.go000066400000000000000000000147141455105530300173100ustar00rootroot00000000000000// // Copyright (c) SAS Institute Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // // A means of conveying a series of edits to binary files. Each item in a // patchset consists of an offset into the old file, the number of bytes to // remove, and the octet string to replace it with. package binpatch import ( "bytes" "encoding/binary" "errors" "fmt" "io" "os" "sort" "github.com/sassoftware/relic/v7/lib/atomicfile" ) const ( MimeType = "application/x-binary-patch" uint32Max = 0xffffffff ) type PatchSet struct { Patches []PatchHeader Blobs [][]byte } type PatchSetHeader struct { Version, NumPatches uint32 } type PatchHeader struct { Offset int64 OldSize, NewSize uint32 } // Create a new, empty PatchSet func New() *PatchSet { return new(PatchSet) } // Add a new patch region to a PatchSet. The bytes beginning at "offset" and // running for "oldSize" are removed and replaced with "blob". oldSize may be 0. func (p *PatchSet) Add(offset, oldSize int64, blob []byte) { if len(p.Patches) > 0 { i := len(p.Patches) - 1 last := p.Patches[i] lastEnd := last.Offset + int64(last.OldSize) lastBlob := p.Blobs[i] oldCombo := int64(last.OldSize) + oldSize newCombo := int64(len(lastBlob)) + int64(len(blob)) if offset == lastEnd && oldCombo <= uint32Max && newCombo <= uint32Max { // coalesce this patch into the previous one p.Patches[i].OldSize = uint32(oldCombo) p.Patches[i].NewSize = uint32(newCombo) if len(blob) > 0 { newBlob := make([]byte, newCombo) copy(newBlob, lastBlob) copy(newBlob[len(lastBlob):], blob) p.Blobs[i] = newBlob } return } } for oldSize > uint32Max { p.Patches = append(p.Patches, PatchHeader{offset, uint32Max, 0}) p.Blobs = append(p.Blobs, nil) offset += uint32Max oldSize -= uint32Max } p.Patches = append(p.Patches, PatchHeader{offset, uint32(oldSize), uint32(len(blob))}) p.Blobs = append(p.Blobs, blob) } // Unmarshal a PatchSet from bytes func Load(blob []byte) (*PatchSet, error) { r := bytes.NewReader(blob) var h PatchSetHeader if err := binary.Read(r, binary.BigEndian, &h); err != nil { return nil, err } else if h.Version != 1 { return nil, fmt.Errorf("unsupported binpatch version %d", h.Version) } num := int(h.NumPatches) p := &PatchSet{ Patches: make([]PatchHeader, num), Blobs: make([][]byte, num), } if err := binary.Read(r, binary.BigEndian, p.Patches); err != nil { return nil, err } for i, hdr := range p.Patches { p.Blobs[i] = make([]byte, int(hdr.NewSize)) if _, err := io.ReadFull(r, p.Blobs[i]); err != nil { return nil, err } } return p, nil } // Marshal a PatchSet to bytes func (p *PatchSet) Dump() []byte { sort.Sort(sorter{p}) header := PatchSetHeader{1, uint32(len(p.Patches))} size := 8 + 16*len(p.Patches) for _, hdr := range p.Patches { size += int(hdr.NewSize) } buf := bytes.NewBuffer(make([]byte, 0, size)) _ = binary.Write(buf, binary.BigEndian, header) _ = binary.Write(buf, binary.BigEndian, p.Patches) for _, blob := range p.Blobs { _, _ = buf.Write(blob) } return buf.Bytes() } // Apply a PatchSet by taking the input file, transforming it, and writing the // result to outpath. If outpath is the same name as infile then the file will // be updated in-place if a direct overwrite is possible. If they are not the // same file, or the patch requires moving parts of the old file, then the // output will be written to a temporary file then renamed over the destination // path. func (p *PatchSet) Apply(infile *os.File, outpath string) error { if outpath == "" { outpath = infile.Name() } // Determine if an in-place overwrite is possible. If any test fails then // fall back to doing a full copy (write-rename). ininfo, err := infile.Stat() if err != nil { return p.applyRewrite(infile, outpath) } outinfo, err := os.Lstat(outpath) if err != nil || !canOverwrite(ininfo, outinfo) { return p.applyRewrite(infile, outpath) } size := ininfo.Size() for i, patch := range p.Patches { // All patches except the last must have oldsize == newsize if patch.OldSize == patch.NewSize { continue } else if i != len(p.Patches)-1 { return p.applyRewrite(infile, outpath) } // For the last patch, either oldsize == newsize or the patch must extend // or truncate the file, i.e. the end of the old chunk must coincide // with the end of the file. oldEnd := patch.Offset + int64(patch.OldSize) if oldEnd != ininfo.Size() { return p.applyRewrite(infile, outpath) } size = patch.Offset + int64(patch.NewSize) } // Do in-place rewrite for i, patch := range p.Patches { if _, err := infile.WriteAt(p.Blobs[i], patch.Offset); err != nil { return err } } return infile.Truncate(size) } // Apply a patch by writing the patched result to a new file. This is the // fallback case whenever an in-place write isn't possible. func (p *PatchSet) applyRewrite(infile *os.File, outpath string) error { if _, err := infile.Seek(0, 0); err != nil { return err } outfile, err := atomicfile.New(outpath) if err != nil { return err } defer outfile.Close() var pos int64 for i, patch := range p.Patches { blob := p.Blobs[i] delta := patch.Offset - pos if delta < 0 { return errors.New("patches out of order") } // Copy data before the patch if delta > 0 { if _, err := io.CopyN(outfile, infile, delta); err != nil { return err } pos += delta } // Skip the old data on the input file delta = int64(patch.OldSize) if _, err := infile.Seek(delta, io.SeekCurrent); err != nil { return err } pos += delta // Write the new data to the output file if _, err := outfile.Write(blob); err != nil { return err } } // Copy everything after the last patch if _, err := io.Copy(outfile, infile); err != nil { return err } infile.Close() return outfile.Commit() } func canOverwrite(ininfo, outinfo os.FileInfo) bool { if !outinfo.Mode().IsRegular() { return false } if !os.SameFile(ininfo, outinfo) { return false } if hasLinks(outinfo) { return false } return true } relic-7.6.1/lib/binpatch/fileutil_unix.go000066400000000000000000000014671455105530300204010ustar00rootroot00000000000000//go:build !windows // +build !windows // // Copyright (c) SAS Institute Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package binpatch import ( "os" "syscall" ) func hasLinks(info os.FileInfo) bool { stat, ok := info.Sys().(*syscall.Stat_t) if !ok { return false } return stat.Nlink != 1 } relic-7.6.1/lib/binpatch/fileutil_windows.go000066400000000000000000000012641455105530300211030ustar00rootroot00000000000000// // Copyright (c) SAS Institute Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package binpatch import ( "os" ) func hasLinks(info os.FileInfo) bool { return false } relic-7.6.1/lib/binpatch/sort.go000066400000000000000000000005531455105530300165030ustar00rootroot00000000000000package binpatch type sorter struct { p *PatchSet } func (s sorter) Len() int { return len(s.p.Patches) } func (s sorter) Less(i, j int) bool { return s.p.Patches[i].Offset < s.p.Patches[j].Offset } func (s sorter) Swap(i, j int) { s.p.Patches[i], s.p.Patches[j] = s.p.Patches[j], s.p.Patches[i] s.p.Blobs[i], s.p.Blobs[j] = s.p.Blobs[j], s.p.Blobs[i] } relic-7.6.1/lib/cabfile/000077500000000000000000000000001455105530300147575ustar00rootroot00000000000000relic-7.6.1/lib/cabfile/cabfile.go000066400000000000000000000150011455105530300166700ustar00rootroot00000000000000// // Copyright (c) SAS Institute Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package cabfile import ( "bytes" "crypto" "encoding/binary" "errors" "fmt" "hash" "io" "github.com/sassoftware/relic/v7/lib/binpatch" ) // Calculate the digest (imprint) of a CAB file for signing purposes func Digest(r io.Reader, hashFunc crypto.Hash) (*CabinetDigest, error) { var d hash.Hash var dw io.Writer if hashFunc == 0 { dw = io.Discard } else { d = hashFunc.New() dw = d } cab := new(Cabinet) if err := binary.Read(r, binary.LittleEndian, &cab.Header); err != nil { return nil, err } if cab.Header.Magic != Magic { return nil, errors.New("not a cab file") } outHeader := cab.Header var addOffset int if cab.Header.Flags&FlagReservePresent != 0 { // read reserve header if err := binary.Read(r, binary.LittleEndian, &cab.ReserveHeader); err != nil { return nil, err } if cab.ReserveHeader.HeaderSize < signatureHeaderSize || cab.ReserveHeader.FolderSize != 0 || cab.ReserveHeader.DataSize != 0 { return nil, errors.New("unknown reserved data") } cab.SignatureHeader = new(SignatureHeader) if err := binary.Read(r, binary.LittleEndian, cab.SignatureHeader); err != nil { return nil, err } if padding := cab.ReserveHeader.HeaderSize - signatureHeaderSize; padding > 0 { // cabinet files may be created with space reserved; ensure in this case that it's all zeroed out if cab.SignatureHeader.CabinetSize != 0 { // must not have both padding and an actual signature header return nil, fmt.Errorf("reserve header for signature is %d bytes; expected %d bytes", cab.ReserveHeader.HeaderSize, signatureHeaderSize) } pbuf := make([]byte, padding) if _, err := io.ReadFull(r, pbuf); err != nil { return nil, err } for _, v := range pbuf { if v != 0 { return nil, errors.New("invalid padding in signature reserve header") } } // remove padding, since the actual signature goes at the end of the file addOffset -= int(padding) cab.SignatureHeader = nil } else if cab.Header.TotalSize != cab.SignatureHeader.CabinetSize { // signature header (if present) must agree with cabinet header return nil, fmt.Errorf("cabinet size is %d but signature header specifies %d bytes", cab.Header.TotalSize, cab.SignatureHeader.CabinetSize) } } else { // make space for a new reserve header addOffset += reserveHeaderSize + signatureHeaderSize } if cab.Header.Flags&(FlagPrevCabinet|FlagNextCabinet) != 0 { return nil, errors.New("multipart cab files are not supported") } else if cab.Header.Flags&^FlagReservePresent != 0 { return nil, errors.New("unsupported flags in cabinet file") } // add space for signature header, or remove excess padding add32(&outHeader.TotalSize, addOffset) add32(&outHeader.OffsetFiles, addOffset) outHeader.Flags |= FlagReservePresent // construct signature header outReserveHeader := ReserveHeader{HeaderSize: signatureHeaderSize} outSigHeader := SignatureHeader{ Unknown1: 0x100000, CabinetSize: outHeader.TotalSize, } if cab.SignatureHeader != nil { // preserve unknown fields from old signature header just in case they're important outSigHeader.Unknown1 = cab.SignatureHeader.Unknown1 outSigHeader.Unknown2 = cab.SignatureHeader.Unknown2 outSigHeader.Unknown3 = cab.SignatureHeader.Unknown3 } // digest the header sb := sigBlob{ Magic: outHeader.Magic, TotalSize: outHeader.TotalSize, Reserved2: outHeader.Reserved2, OffsetFiles: outHeader.OffsetFiles, Reserved3: outHeader.Reserved3, Version: outHeader.Version, NumFolders: outHeader.NumFolders, NumFiles: outHeader.NumFiles, Flags: outHeader.Flags, SetID: outHeader.SetID, SigUnknown3: outSigHeader.Unknown3, } _ = binary.Write(dw, binary.LittleEndian, sb) // save the updated header for writing out later patched := bytes.NewBuffer(make([]byte, 0, outHeader.OffsetFiles)) _ = binary.Write(patched, binary.LittleEndian, outHeader) _ = binary.Write(patched, binary.LittleEndian, outReserveHeader) _ = binary.Write(patched, binary.LittleEndian, outSigHeader) w := io.MultiWriter(dw, patched) // add offset to folder headers var fh FolderHeader for i := 0; i < int(cab.Header.NumFolders); i++ { if err := binary.Read(r, binary.LittleEndian, &fh); err != nil { return nil, err } add32(&fh.Offset, addOffset) _ = binary.Write(w, binary.LittleEndian, fh) } // digest file contents if _, err := io.CopyN(dw, r, int64(cab.Header.TotalSize-cab.Header.OffsetFiles)); err != nil { return nil, err } if cab.SignatureHeader != nil { // read old signature for verification purposes cab.Signature = make([]byte, cab.SignatureHeader.SignatureSize) if _, err := io.ReadFull(r, cab.Signature); err != nil { return nil, err } } // ensure there is nothing after the cabinet and signature if _, err := r.Read(make([]byte, 1)); err == nil { return nil, errors.New("trailing garbage after cabinet") } else if err != io.EOF { return nil, err } var imprint []byte if d != nil { imprint = d.Sum(nil) } return &CabinetDigest{ Cabinet: cab, Imprint: imprint, HashFunc: hashFunc, Patched: patched.Bytes(), }, nil } // Parse the cabinet file header and return it func Parse(r io.Reader) (*Cabinet, error) { digest, err := Digest(r, 0) if err != nil { return nil, err } return digest.Cabinet, nil } // Create a patchset that will apply the given signature blob to a previously // digested cabinet file, replacing any existing signature func (d *CabinetDigest) MakePatch(pkcs []byte) *binpatch.PatchSet { // pad signature to 8 byte boundary padded := make([]byte, (len(pkcs)+7)/8*8) copy(padded, pkcs) // update the signature header with the final size hdr := d.Patched binary.LittleEndian.PutUint32(hdr[48:], uint32(len(padded))) p := binpatch.New() p.Add(0, int64(d.Cabinet.Header.OffsetFiles), hdr) p.Add(int64(d.Cabinet.Header.TotalSize), int64(d.Cabinet.SignatureHeader.Size()), padded) return p } func add32(offset *uint32, increment int) { *offset = uint32(int64(*offset) + int64(increment)) } relic-7.6.1/lib/cabfile/structs.go000066400000000000000000000041401455105530300170140ustar00rootroot00000000000000// // Copyright (c) SAS Institute Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package cabfile import "crypto" const Magic = 0x4643534d // MSCF type CabinetFlag uint16 const ( FlagPrevCabinet CabinetFlag = 1 << iota FlagNextCabinet FlagReservePresent ) type Header struct { Magic uint32 Reserved1 uint32 TotalSize uint32 Reserved2 uint32 OffsetFiles uint32 Reserved3 uint32 Version uint16 NumFolders uint16 NumFiles uint16 Flags CabinetFlag SetID uint16 CabNumber uint16 } const reserveHeaderSize = 4 type ReserveHeader struct { HeaderSize uint16 FolderSize uint8 DataSize uint8 } const signatureHeaderSize = 20 type SignatureHeader struct { Unknown1 uint32 CabinetSize uint32 SignatureSize uint32 Unknown2, Unknown3 uint32 } func (sh *SignatureHeader) Size() uint32 { if sh == nil { return 0 } return sh.SignatureSize } type FolderHeader struct { Offset uint32 NumData uint16 Compression uint16 } // the pieces of Header that are part of the signature type sigBlob struct { // skipped fields: Reserved1, CabNumber Magic uint32 TotalSize uint32 Reserved2 uint32 OffsetFiles uint32 Reserved3 uint32 Version uint16 NumFolders uint16 NumFiles uint16 Flags CabinetFlag SetID uint16 SigUnknown3 uint32 } type Cabinet struct { Header Header ReserveHeader ReserveHeader ReserveData []byte SignatureHeader *SignatureHeader Signature []byte } type CabinetDigest struct { Cabinet *Cabinet Imprint []byte HashFunc crypto.Hash Patched []byte } relic-7.6.1/lib/certloader/000077500000000000000000000000001455105530300155165ustar00rootroot00000000000000relic-7.6.1/lib/certloader/anyprivkey.go000066400000000000000000000071271455105530300202550ustar00rootroot00000000000000// // Copyright (c) SAS Institute Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package certloader import ( "bytes" "crypto" "crypto/x509" "encoding/pem" "errors" "fmt" "io" "os" "strings" "golang.org/x/crypto/openpgp" "golang.org/x/crypto/openpgp/armor" "golang.org/x/crypto/openpgp/packet" "github.com/sassoftware/relic/v7/lib/passprompt" ) // Parse and decrypt a private key. It can be a RSA or ECDA key in PKCS#1 or // PKCS#8 format and DER or PEM encoding, or it can be a PGP private key. If // the private key is encrypted then the given prompter will be invoked to ask // for the passphrase, if provided. func ParseAnyPrivateKey(blob []byte, prompt passprompt.PasswordGetter) (crypto.PrivateKey, error) { if bytes.HasPrefix(blob, []byte("-----BEGIN PGP")) { return parsePgpPrivateKey(blob, prompt) } else if bytes.HasPrefix(blob, []byte("-----BEGIN")) { var block *pem.Block for { block, blob = pem.Decode(blob) if block == nil { break } else if block.Type == "PRIVATE KEY" || strings.HasSuffix(block.Type, " PRIVATE KEY") { return parsePemPrivateKey(block, prompt) } } return nil, errors.New("failed to find any private keys in PEM data") } else if blob[0] == asn1Magic { return parsePrivateKey(blob) } else if blob[0]&0x80 != 0 { return parsePgpPrivateKey(blob, prompt) } else { return nil, errors.New("unrecognized private key format") } } func parsePemPrivateKey(block *pem.Block, prompt passprompt.PasswordGetter) (crypto.PrivateKey, error) { if !x509.IsEncryptedPEMBlock(block) { //nolint:staticcheck return parsePrivateKey(block.Bytes) } if prompt == nil { return nil, errors.New("private key is encrypted and no password was provided") } for { password, err := prompt.GetPasswd("Password for private key: ") if err != nil { return nil, err } else if password == "" { return nil, errors.New("aborted") } keyblob, err := x509.DecryptPEMBlock(block, []byte(password)) //nolint:staticcheck if err == x509.IncorrectPasswordError { continue } else if err != nil { return nil, err } else { return parsePrivateKey(keyblob) } } } func parsePgpPrivateKey(blob []byte, prompt passprompt.PasswordGetter) (crypto.PrivateKey, error) { var reader io.Reader = bytes.NewReader(blob) if blob[0] == '-' { block, err := armor.Decode(reader) if err != nil { return nil, err } reader = block.Body } entity, err := openpgp.ReadEntity(packet.NewReader(reader)) if err != nil { return nil, err } if entity.PrivateKey == nil { return nil, errors.New("file does not contain a private key") } if entity.PrivateKey.Encrypted { fmt.Fprintln(os.Stderr, "Key fingerprint:", entity.PrimaryKey.KeyIdString()) for name := range entity.Identities { fmt.Fprintln(os.Stderr, "UID:", name) } fmt.Fprintln(os.Stderr) for { password, err := prompt.GetPasswd("Passphrase for key: ") if err != nil { return nil, err } else if password == "" { return nil, errors.New("Aborted") } err = entity.PrivateKey.Decrypt([]byte(password)) if err == nil { break } } } return entity.PrivateKey.PrivateKey, nil } relic-7.6.1/lib/certloader/certloader.go000066400000000000000000000156241455105530300202010ustar00rootroot00000000000000// // Copyright (c) SAS Institute Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package certloader import ( "bytes" "crypto" "crypto/ecdsa" "crypto/rsa" "crypto/tls" "crypto/x509" "encoding/pem" "errors" "fmt" "io/ioutil" "golang.org/x/crypto/openpgp" "golang.org/x/crypto/openpgp/packet" "github.com/sassoftware/relic/v7/lib/pkcs7" "github.com/sassoftware/relic/v7/lib/pkcs9" "github.com/sassoftware/relic/v7/lib/x509tools" ) const asn1Magic = 0x30 // weak but good enough? var pkcs7SignedData = []byte{0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x07, 0x02} // A bundle of X509 certificate chain and/or PGP certificate, with optional private key type Certificate struct { Leaf *x509.Certificate Certificates []*x509.Certificate PgpKey *openpgp.Entity PrivateKey crypto.PrivateKey Timestamper pkcs9.Timestamper KeyName string } // Return the X509 certificates in the chain up to, but not including, the root CA certificate func (s *Certificate) Chain() []*x509.Certificate { var chain []*x509.Certificate if s.Leaf != nil { // ensure leaf comes first chain = append(chain, s.Leaf) } for i, cert := range s.Certificates { if i > 0 && bytes.Equal(cert.RawIssuer, cert.RawSubject) { // omit root CA continue } else if cert == s.Leaf { // already in list continue } chain = append(chain, cert) } return chain } // Return the certificate that issued the leaf certificate func (s *Certificate) Issuer() *x509.Certificate { if s.Leaf == nil { return nil } for _, cert := range s.Certificates { if bytes.Equal(cert.RawSubject, s.Leaf.RawIssuer) { return cert } } return nil } // Return the private key in the form of a crypto.Signer func (s *Certificate) Signer() crypto.Signer { if s.PrivateKey == nil { return nil } return s.PrivateKey.(crypto.Signer) } // Return a tls.Certificate structure containing the X509 certificate chain and // private key func (s *Certificate) TLS() tls.Certificate { var raw [][]byte for _, cert := range s.Certificates { raw = append(raw, cert.Raw) } return tls.Certificate{Leaf: s.Leaf, Certificate: raw, PrivateKey: s.PrivateKey} } // Parse a private key from a DER block // See crypto/tls.parsePrivateKey func parsePrivateKey(der []byte) (crypto.PrivateKey, error) { if key, err := x509.ParsePKCS1PrivateKey(der); err == nil { return key, nil } if key, err := x509.ParsePKCS8PrivateKey(der); err == nil { switch key := key.(type) { case *rsa.PrivateKey, *ecdsa.PrivateKey: return key, nil default: return nil, errors.New("tls: found unknown private key type in PKCS#8 wrapping") } } if key, err := x509.ParseECPrivateKey(der); err == nil { return key, nil } return nil, errors.New("tls: failed to parse private key") } // Parse a list of certificates, PEM or DER, X509 or PKCS#7 func parseCertificates(pemData []byte) (*Certificate, error) { if len(pemData) >= 1 && pemData[0] == asn1Magic { // already in DER form return parseCertificatesDer(pemData) } var certs []*x509.Certificate for { var block *pem.Block block, pemData = pem.Decode(pemData) if block == nil { break } else if block.Type == "CERTIFICATE" || block.Type == "PKCS7" { newcerts, err := parseCertificatesDer(block.Bytes) if err != nil { return nil, err } certs = append(certs, newcerts.Certificates...) } } if len(certs) == 0 { return nil, ErrNoCerts } return &Certificate{Leaf: certs[0], Certificates: certs}, nil } // Parse certificates from DER func parseCertificatesDer(der []byte) (*Certificate, error) { var certs []*x509.Certificate if bytes.Contains(der[:32], pkcs7SignedData) { psd, err := pkcs7.Unmarshal(der) if err != nil { return nil, err } certs, err = psd.Content.Certificates.Parse() if err != nil { return nil, err } } else { var err error certs, err = x509.ParseCertificates(der) if err != nil { return nil, err } } if len(certs) == 0 { return nil, ErrNoCerts } return &Certificate{Leaf: certs[0], Certificates: certs}, nil } // ParseX509Certificates parses a blob in PEM or DER, X509 or PKCS#7 format and returns a list of certificates func ParseX509Certificates(blob []byte) ([]*x509.Certificate, error) { cert, err := parseCertificates(blob) if err != nil { return nil, err } return cert.Certificates, nil } // Load a X509 private key and certificate func LoadX509KeyPair(certFile, keyFile string) (*Certificate, error) { keyblob, err := ioutil.ReadFile(keyFile) if err != nil { return nil, err } certblob, err := ioutil.ReadFile(certFile) if err != nil { return nil, err } key, err := ParseAnyPrivateKey(keyblob, nil) if err != nil { return nil, err } cert, err := parseCertificates(certblob) if err != nil { return nil, err } if !x509tools.SameKey(cert.Leaf.PublicKey, key) { return nil, errors.New("Private key does not match certificate") } cert.PrivateKey = key return cert, nil } // Load X509 and/or PGP certificates from named paths and return a Certificate // structure together with the given private key func LoadTokenCertificates(key crypto.PrivateKey, x509cert, pgpcert string, x509contents []byte) (*Certificate, error) { var cert *Certificate var err error switch { case x509cert != "": // load X509 cert from file x509contents, err = ioutil.ReadFile(x509cert) if err != nil { return nil, err } fallthrough case len(x509contents) != 0: // load X509 cert from blob cert, err = parseCertificates(x509contents) if err != nil { return nil, err } if !x509tools.SameKey(key, cert.Leaf.PublicKey) { return nil, errors.New("certificate does not match key in token") } cert.PrivateKey = key default: cert = &Certificate{PrivateKey: key} } if pgpcert != "" { blob, err := ioutil.ReadFile(pgpcert) if err != nil { return nil, err } keyring, err := parsePGP(blob) if err != nil { return nil, err } if len(keyring) != 1 { return nil, fmt.Errorf("expected exactly 1 entity in pgp certificate %s", pgpcert) } entity := keyring[0] priv := &packet.PrivateKey{ PublicKey: *entity.PrimaryKey, Encrypted: false, PrivateKey: key, } if !x509tools.SameKey(key, priv.PublicKey.PublicKey) { return nil, errors.New("certificate does not match key in token") } entity.PrivateKey = priv cert.PgpKey = entity } return cert, nil } type errNoCerts struct{} func (errNoCerts) Error() string { return "failed to find any certificates in PEM file" } var ErrNoCerts = errNoCerts{} relic-7.6.1/lib/certloader/loadany.go000066400000000000000000000034401455105530300174750ustar00rootroot00000000000000// // Copyright (c) SAS Institute Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package certloader import ( "bytes" "crypto/x509" "fmt" "io" "io/ioutil" "golang.org/x/crypto/openpgp" "golang.org/x/crypto/openpgp/armor" ) type AnyCerts struct { X509Certs []*x509.Certificate PGPCerts openpgp.EntityList } // Load X509 and/or PGP certificates from the named file paths func LoadAnyCerts(paths []string) (any AnyCerts, err error) { for _, path := range paths { blob, err := ioutil.ReadFile(path) if err != nil { return any, err } x509certs, err := parseCertificates(blob) if err == nil { any.X509Certs = append(any.X509Certs, x509certs.Certificates...) continue } else if err != ErrNoCerts { return any, fmt.Errorf("%s: %w", path, err) } pgpcerts, err := parsePGP(blob) if err == nil { any.PGPCerts = append(any.PGPCerts, pgpcerts...) } else { return any, fmt.Errorf("%s: %w", path, err) } } return any, nil } // Parse one or more PGP certificates from the given possibly-armored blob func parsePGP(blob []byte) (openpgp.EntityList, error) { reader := io.Reader(bytes.NewReader(blob)) if blob[0] == '-' { block, err := armor.Decode(reader) if err != nil { return nil, err } reader = block.Body } return openpgp.ReadKeyRing(reader) } relic-7.6.1/lib/certloader/pkcs12.go000066400000000000000000000026521455105530300171550ustar00rootroot00000000000000// // Copyright (c) SAS Institute Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package certloader import ( "crypto/x509" "errors" "software.sslmate.com/src/go-pkcs12" "github.com/sassoftware/relic/v7/lib/passprompt" ) func ParsePKCS12(blob []byte, prompt passprompt.PasswordGetter) (*Certificate, error) { var password string var triedEmpty bool for { var err error password, err = prompt.GetPasswd("Password for PKCS12: ") if err != nil { return nil, err } else if password == "" { if triedEmpty { return nil, errors.New("aborted") } triedEmpty = true } priv, leaf, chain, err := pkcs12.DecodeChain(blob, password) if errors.Is(err, pkcs12.ErrIncorrectPassword) { continue } else if err != nil { return nil, err } certs := append([]*x509.Certificate{leaf}, chain...) return &Certificate{ PrivateKey: priv, Leaf: leaf, Certificates: certs, }, nil } } relic-7.6.1/lib/comdoc/000077500000000000000000000000001455105530300146365ustar00rootroot00000000000000relic-7.6.1/lib/comdoc/dirent.go000066400000000000000000000132121455105530300164510ustar00rootroot00000000000000// // Copyright (c) SAS Institute Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package comdoc import ( "bytes" "encoding/binary" "errors" "unicode/utf16" "github.com/sassoftware/relic/v7/lib/redblack" ) // Parse the directory stream func (r *ComDoc) readDir() error { var files []DirEnt count := r.SectorSize / 128 raw := make([]RawDirEnt, count) cooked := make([]DirEnt, count) rootIndex := -1 for sector := r.Header.DirNextSector; sector >= 0; sector = r.SAT[sector] { if err := r.readSectorStruct(sector, raw); err != nil { return err } for i, raw := range raw { cooked[i] = DirEnt{ RawDirEnt: raw, Index: len(files) + i, name: raw.Name(), } if raw.Type == DirRoot { rootIndex = len(files) + i } } files = append(files, cooked...) } if rootIndex < 0 { return errors.New("missing root storage") } r.Files = files r.rootStorage = rootIndex rootFiles, err := r.ListDir(nil) if err != nil { return err } r.rootFiles = make([]int, 0, len(r.Files)) for _, f := range rootFiles { r.rootFiles = append(r.rootFiles, f.Index) } return nil } // Return a pointer to the root storage. func (r *ComDoc) RootStorage() *DirEnt { return &r.Files[r.rootStorage] } // List the items in a storage. If parent is nil, the root storage is used. func (r *ComDoc) ListDir(parent *DirEnt) ([]*DirEnt, error) { if parent == nil { parent = r.RootStorage() } if parent.Type != DirRoot && parent.Type != DirStorage { return nil, errors.New("ListDir() on a non-directory object") } top := &r.Files[parent.StorageRoot] stack := []*DirEnt{top} var files []*DirEnt for len(stack) > 0 { i := len(stack) - 1 item := stack[i] stack = stack[:i] files = append(files, item) if item.LeftChild != -1 { stack = append(stack, &r.Files[item.LeftChild]) } if item.RightChild != -1 { stack = append(stack, &r.Files[item.RightChild]) } } return files, nil } // Create a new stream and add it to the directory stream. func (r *ComDoc) newDirEnt(name string, size uint32, sector SecID) (*DirEnt, error) { runes := utf16.Encode([]rune(name)) runes = append(runes, 0) if len(runes) > 32 { return nil, errors.New("name is too long") } dirent := &DirEnt{ RawDirEnt: RawDirEnt{ NameLength: uint16(2 * len(runes)), Type: DirStream, LeftChild: -1, RightChild: -1, StorageRoot: -1, StreamSize: size, NextSector: sector, }, Index: -1, name: name, } copy(dirent.NameRunes[:], runes) dirent = r.appendDirEnt(dirent) return dirent, nil } // Add a DirEnt to the directory stream, extending it if necessary func (r *ComDoc) appendDirEnt(dirent *DirEnt) *DirEnt { // look for a free slot index := -1 for i, j := range r.Files { if j.Type == DirEmpty { index = i break } } if index < 0 { // extend the dir stream index = len(r.Files) newDirs := make([]DirEnt, r.SectorSize/128) r.Files = append(r.Files, newDirs...) } r.Files[index] = *dirent r.Files[index].Index = index return &r.Files[index] } // Rewrite the red-black tree on the root storage and write the directory // stream to disk. func (r *ComDoc) writeDirStream() error { // Presently there's no way to modify any storage other than the root one // so it's only needed to relabance that. r.rebuildTree(r.rootStorage, r.rootFiles) freeSectors(r.SAT, r.Header.DirNextSector) perSector := r.SectorSize / 128 if len(r.Files)%perSector != 0 { panic("irregularly sized directory stream") } freeList := r.makeFreeSectors(len(r.Files)/perSector, false) chunk := make([]RawDirEnt, perSector) buf := bytes.NewBuffer(r.sectorBuf) first := SecIDEndOfChain previous := first for i, sector := range freeList { j := i * perSector for k, f := range r.Files[j : j+perSector] { if f.Type != DirEmpty { chunk[k] = f.RawDirEnt } else { chunk[k] = RawDirEnt{LeftChild: -1, RightChild: -1, StorageRoot: -1} } } buf.Reset() _ = binary.Write(buf, binary.LittleEndian, chunk) if err := r.writeSector(sector, buf.Bytes()); err != nil { return err } if previous == SecIDEndOfChain { first = sector } else { r.SAT[previous] = sector } previous = sector } r.SAT[previous] = SecIDEndOfChain r.Header.DirNextSector = first r.Header.DirSectorCount = uint32(len(freeList)) return nil } // Rebuild the red-black directory tree of the root storage after files have // been added or removed func (r *ComDoc) rebuildTree(parent int, files []int) { tree := redblack.New(lessDirEnt) for _, i := range files { tree.Insert(&r.Files[i]) } nodes := tree.Nodes() for _, n := range nodes { e := n.Item.(*DirEnt) if n == tree.Root { r.Files[parent].StorageRoot = int32(e.Index) } if n.Red { e.Color = Red } else { e.Color = Black } if n.Children[0] != nil { left := n.Children[0].Item.(*DirEnt) e.LeftChild = int32(left.Index) } else { e.LeftChild = -1 } if n.Children[1] != nil { right := n.Children[1].Item.(*DirEnt) e.RightChild = int32(right.Index) } else { e.RightChild = -1 } } } func lessDirEnt(i, j interface{}) bool { e, f := i.(*DirEnt), j.(*DirEnt) if e.NameLength != f.NameLength { return e.NameLength < f.NameLength } return e.name < f.name } relic-7.6.1/lib/comdoc/msat.go000066400000000000000000000073261455105530300161410ustar00rootroot00000000000000// // Copyright (c) SAS Institute Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package comdoc import ( "bytes" "encoding/binary" ) // Read the master/meta sector allocation table. It is an array of all the // sectors holding pieces of the sector allocation table (SAT). The main file // header holds 109 MSAT entries and MSATNextSector may point to a sector // holding yet more. The last entry in each additional sector points to another // sector holding more MSAT entries, or -2 if none. func (r *ComDoc) readMSAT() error { r.MSAT = r.Header.MSAT[:] r.msatList = nil nextSector := r.Header.MSATNextSector count := r.SectorSize / 4 values := make([]SecID, count) for nextSector >= 0 { if err := r.readSectorStruct(nextSector, values); err != nil { return err } r.MSAT = append(r.MSAT, values[:count-1]...) r.msatList = append(r.msatList, nextSector) nextSector = values[count-1] } // trim free slots for i := len(r.MSAT) - 1; i >= 0; i-- { if r.MSAT[i] >= 0 { r.MSAT = r.MSAT[:i+1] break } } return nil } // Allocate sectors for the SAT and MSAT func (r *ComDoc) allocSectorTables() { // work out how many sectors are needed for both satPerSector := r.SectorSize / 4 msatPerSector := satPerSector - 1 for { if len(r.SAT)%satPerSector != 0 { panic("irregularly sized sector table") } satSectors := len(r.SAT) / satPerSector if satSectors > len(r.MSAT) { // allocate a new SAT sector sector := r.makeFreeSectors(1, false)[0] r.MSAT = append(r.MSAT, sector) r.SAT[sector] = SecIDSAT // a new SAT might be needed so check again continue } // 109 MSAT entries fit into the file header, the rest need more sectors msatSectors := (len(r.MSAT) - msatInHeader + msatPerSector - 1) / msatPerSector if msatSectors > len(r.msatList) { // allocate a new MSAT sector sector := r.makeFreeSectors(1, false)[0] r.msatList = append(r.msatList, sector) r.SAT[sector] = SecIDMSAT // a new SAT might be needed so check again continue } break } } // Write an updated MSAT out to the header and msatSectors, with satSectors // being the contents of the MSAT func (r *ComDoc) writeMSAT() error { satPerSector := r.SectorSize / 4 msatPerSector := satPerSector - 1 // round MSAT up to the next full sector and mark all unused spaces as free msatCount := msatInHeader + len(r.msatList)*msatPerSector msat := make([]SecID, msatCount) copy(msat, r.MSAT) for i := len(r.MSAT); i < msatCount; i++ { msat[i] = SecIDFree } // copy the first 109 into the file header copy(r.Header.MSAT[:], msat) msat = msat[msatInHeader:] // write remaining sectors buf := bytes.NewBuffer(r.sectorBuf) chunk := make([]SecID, r.SectorSize/4) for i, sector := range r.msatList { j := i * msatPerSector copy(chunk, msat[j:j+msatPerSector]) // set pointer to next MSAT sector if i < len(r.msatList)-1 { chunk[msatPerSector] = r.msatList[i+1] } else { chunk[msatPerSector] = SecIDEndOfChain } // write buf.Reset() _ = binary.Write(buf, binary.LittleEndian, chunk) if err := r.writeSector(sector, buf.Bytes()); err != nil { return err } } if len(r.msatList) > 0 { r.Header.MSATNextSector = r.msatList[0] } else { r.Header.MSATNextSector = SecIDEndOfChain } return nil } relic-7.6.1/lib/comdoc/reader.go000066400000000000000000000065551455105530300164420ustar00rootroot00000000000000// // Copyright (c) SAS Institute Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // // Microsoft Compound Document File // Reference: https://www.openoffice.org/sc/compdocfileformat.pdf // ERRATA: The above document says the 0th sector is always 512 bytes into the // file. This is not correct. If SectorSize > 512 bytes then the 0th sector is // SectorSize bytes into the file. package comdoc import ( "bytes" "encoding/binary" "errors" "io" "os" ) // CDF file open for reading or writing type ComDoc struct { File io.ReaderAt Header *Header SectorSize int ShortSectorSize int FirstSector int64 // MSAT is a list of sector IDs holding a SAT MSAT []SecID // SAT is a table where the index is the sector ID and the value is a pointer to the next sector ID in the same stream SAT []SecID SSAT []SecID Files []DirEnt sectorBuf []byte changed bool rootStorage int // index into files rootFiles []int // index into Files msatList []SecID // list of sector IDs holding a MSAT writer *os.File closer io.Closer } // Open a CDF file for reading func ReadPath(path string) (*ComDoc, error) { f, err := os.Open(path) if err != nil { return nil, err } return openFile(f, nil, f) } // Open a CDF file for reading and writing func WritePath(path string) (*ComDoc, error) { f, err := os.OpenFile(path, os.O_RDWR, 0) if err != nil { return nil, err } return openFile(f, f, f) } // Parse an already-open CDF file for reading func ReadFile(reader io.ReaderAt) (*ComDoc, error) { return openFile(reader, nil, nil) } // Parse an already-open CDF file for reading and writing func WriteFile(f *os.File) (*ComDoc, error) { return openFile(f, f, nil) } func openFile(reader io.ReaderAt, writer *os.File, closer io.Closer) (*ComDoc, error) { header := new(Header) r := &ComDoc{ File: reader, Header: header, writer: writer, closer: closer, } sr := io.NewSectionReader(reader, 0, 512) if err := binary.Read(sr, binary.LittleEndian, header); err != nil { return nil, err } if !bytes.Equal(header.Magic[:], fileMagic) { return nil, errors.New("not a compound document file") } if header.ByteOrder != byteOrderMarker { return nil, errors.New("incorrect byte order marker") } if header.SectorSize < 5 || header.SectorSize > 28 || header.ShortSectorSize >= header.SectorSize { return nil, errors.New("unreasonable header values") } r.SectorSize = 1 << header.SectorSize r.ShortSectorSize = 1 << header.ShortSectorSize if r.SectorSize < 512 { r.FirstSector = 512 } else { r.FirstSector = int64(r.SectorSize) } r.sectorBuf = make([]byte, r.SectorSize) if err := r.readMSAT(); err != nil { return nil, err } if err := r.readSAT(); err != nil { return nil, err } if err := r.readShortSAT(); err != nil { return nil, err } if err := r.readDir(); err != nil { return nil, err } return r, nil } relic-7.6.1/lib/comdoc/sectors.go000066400000000000000000000101751455105530300166530ustar00rootroot00000000000000// // Copyright (c) SAS Institute Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package comdoc import ( "bytes" "encoding/binary" "errors" "io" ) // Convert a sector ID to an absolute file position func (r *ComDoc) sectorToOffset(sector SecID) int64 { if sector < 0 { return -1 } return r.FirstSector + int64(sector)*int64(r.SectorSize) } // Read the specified sector func (r *ComDoc) readSector(sector SecID, buf []byte) (int, error) { return r.File.ReadAt(buf, r.sectorToOffset(sector)) } // Read the specified sector into a binary structure. It must be exactly 1 // sector in size already. func (r *ComDoc) readSectorStruct(sector SecID, v interface{}) error { n, err := r.readSector(sector, r.sectorBuf) if err != nil && err != io.EOF { return err } else if n < r.SectorSize { return io.ErrUnexpectedEOF } return binary.Read(bytes.NewReader(r.sectorBuf), binary.LittleEndian, v) } // Write to a specified sector. Content will be padded with zeroes if it is // less than a full sector in length. func (r *ComDoc) writeSector(sector SecID, content []byte) error { if len(content) > r.SectorSize { panic("excessive write") } else if len(content) < r.SectorSize { buf := r.sectorBuf copy(buf, content) for i := len(content); i < r.SectorSize; i++ { buf[i] = 0 } content = buf[:r.SectorSize] } _, err := r.writer.WriteAt(content, r.sectorToOffset(sector)) return err } // Mark a chain of sectors as free func freeSectors(sat []SecID, sector SecID) { for { nextSector := sat[sector] sat[sector] = SecIDFree if nextSector < 0 { break } sector = nextSector } } // Return a list of "count" free sectors or short sectors, extending the table if needed func (r *ComDoc) makeFreeSectors(count int, short bool) []SecID { if count <= 0 { return nil } freeList := make([]SecID, 0, count) var sat []SecID if short { sat = r.SSAT } else { sat = r.SAT } // scan for existing free sectors for i, j := range sat { if j != SecIDFree { continue } freeList = append(freeList, SecID(i)) count-- if count == 0 { return freeList } } // extend the sector table sectorsPerBlock := r.SectorSize / 4 needBlocks := (count + sectorsPerBlock - 1) / sectorsPerBlock oldCount := len(sat) newSAT := append(sat, make([]SecID, needBlocks*sectorsPerBlock)...) for i := oldCount; i < len(newSAT); i++ { newSAT[i] = SecIDFree if count > 0 { freeList = append(freeList, SecID(i)) count-- } } if short { r.SSAT = newSAT } else { r.SAT = newSAT } return freeList } // Read the sector allocation table. // // Each index within the table corresponds to a sector within the file itself. // The value held at that index in the SAT either points to the ID of the // sector holding the next chunk of data for that stream, or -2 to indicate // there are no more sectors. func (r *ComDoc) readSAT() error { count := r.SectorSize / 4 sat := make([]SecID, count*int(r.Header.SATSectors)) position := 0 for _, sector := range r.MSAT { if sector < 0 { continue } if position >= len(sat) { return errors.New("msat has more sectors than indicated") } if err := r.readSectorStruct(sector, sat[position:position+count]); err != nil { return err } position += count } r.SAT = sat return nil } // Write the new sector allocation table to the listed sector IDs func (r *ComDoc) writeSAT() error { satPerSector := r.SectorSize / 4 buf := bytes.NewBuffer(r.sectorBuf) for i, sector := range r.MSAT { j := i * satPerSector buf.Reset() _ = binary.Write(buf, binary.LittleEndian, r.SAT[j:j+satPerSector]) if err := r.writeSector(sector, buf.Bytes()); err != nil { return err } } return nil } relic-7.6.1/lib/comdoc/shortsector.go000066400000000000000000000110771455105530300175520ustar00rootroot00000000000000// // Copyright (c) SAS Institute Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package comdoc import ( "bytes" "encoding/binary" "errors" ) // Read the short sector allocation table // // The SSAT works the same way as the SAT, except that the indices correspond // to positions within the short sector stream instead of the file at large, // and those sectors are ShortSectorSize in length instead of SectorSize. It's // also stored differently, using the SAT to chain to each subsequent block // instead of using the MSAT array, in the same way that any other stream works. func (r *ComDoc) readShortSAT() error { count := r.SectorSize / 4 sat := make([]SecID, count*int(r.Header.SSATSectorCount)) position := 0 for sector := r.Header.SSATNextSector; sector >= 0; sector = r.SAT[sector] { if position >= len(sat) { return errors.New("ssat has more sectors than indicated") } if err := r.readSectorStruct(sector, sat[position:position+count]); err != nil { return err } position += count } r.SSAT = sat return nil } // Free the old short-sector allocation table and write a new one func (r *ComDoc) writeShortSAT() error { freeSectors(r.SAT, r.Header.SSATNextSector) perSector := r.SectorSize / 4 freeList := r.makeFreeSectors(len(r.SSAT)/perSector, false) buf := bytes.NewBuffer(r.sectorBuf) first := SecIDEndOfChain previous := first for i, sector := range freeList { j := i * perSector chunk := r.SSAT[j : j+perSector] buf.Reset() _ = binary.Write(buf, binary.LittleEndian, chunk) if err := r.writeSector(sector, buf.Bytes()); err != nil { return err } if previous == SecIDEndOfChain { first = sector } else { r.SAT[previous] = sector } previous = sector } r.SAT[previous] = SecIDEndOfChain r.Header.SSATNextSector = first r.Header.SSATSectorCount = uint32(len(freeList)) return nil } // Read a short sector at the given position. // // Short sectors work similarly to big sectors except they are smaller, have // their own allocation table, and they point to a position within the "short // sector stream" instead of within the file at large. The short sector stream // is a regular stream whose first block is pointed to by the root storage // dirent. func (r *ComDoc) readShortSector(shortSector SecID, buf []byte) (int, error) { // figure out which big sector holds the short sector bigSectorIndex := int(shortSector) * r.ShortSectorSize / r.SectorSize bigSectorID := r.Files[r.rootStorage].NextSector for i := 0; i < bigSectorIndex; i++ { bigSectorID = r.SAT[bigSectorID] } // translate to a file position n := r.sectorToOffset(bigSectorID) n += int64(int(shortSector)*r.ShortSectorSize - bigSectorIndex*r.SectorSize) return r.File.ReadAt(buf, n) } // Write a short sector at the given position. This will allocate new space in // the short-sector stream if needed. func (r *ComDoc) writeShortSector(shortSector SecID, content []byte) error { if len(content) > r.ShortSectorSize { panic("excessive write") } else if len(content) < r.ShortSectorSize { buf := r.sectorBuf copy(buf, content) for i := len(content); i < r.ShortSectorSize; i++ { buf[i] = 0 } content = buf[:r.ShortSectorSize] } // walk the short stream to find the big sector that holds the target small sector bigSectorIndex := int(shortSector) * r.ShortSectorSize / r.SectorSize offset := int(shortSector)*r.ShortSectorSize - bigSectorIndex*r.SectorSize root := &r.Files[r.rootStorage] bigSectorID := root.NextSector for ; bigSectorIndex > 0; bigSectorIndex-- { next := r.SAT[bigSectorID] if next < 0 { break } bigSectorID = next } if bigSectorIndex > 0 { // extend short sector stream freeList := r.makeFreeSectors(bigSectorIndex, false) for _, sector := range freeList { r.SAT[bigSectorID] = sector bigSectorID = sector } r.SAT[bigSectorID] = SecIDEndOfChain } n := r.sectorToOffset(bigSectorID) + int64(offset) if _, err := r.writer.WriteAt(content, n); err != nil { return err } streamLength := uint32(int(shortSector+1) * r.ShortSectorSize) if streamLength > root.StreamSize { root.StreamSize = streamLength } return nil } relic-7.6.1/lib/comdoc/stream.go000066400000000000000000000103331455105530300164600ustar00rootroot00000000000000// // Copyright (c) SAS Institute Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package comdoc import ( "errors" "fmt" "io" ) type streamReader struct { remaining uint32 nextSector SecID sat []SecID sectorSize int readSector func(SecID, []byte) (int, error) buf, saved []byte } // Open a stream for reading func (r *ComDoc) ReadStream(e *DirEnt) (io.Reader, error) { if e.Type != DirStream { return nil, errors.New("not a stream") } sr := &streamReader{ remaining: e.StreamSize, nextSector: e.NextSector, } if e.StreamSize < r.Header.MinStdStreamSize { sr.sectorSize = r.ShortSectorSize sr.sat = r.SSAT sr.readSector = r.readShortSector } else { sr.sectorSize = r.SectorSize sr.sat = r.SAT sr.readSector = r.readSector } sr.buf = make([]byte, sr.sectorSize) return sr, nil } func (sr *streamReader) Read(d []byte) (copied int, err error) { if sr.remaining == 0 { return 0, io.EOF } else if len(d) == 0 { return 0, nil } if int64(len(d)) > int64(sr.remaining) { d = d[:int(sr.remaining)] } // read from previously buffered sector if len(sr.saved) > 0 { n := copy(d, sr.saved) d = d[n:] sr.saved = sr.saved[n:] copied += n sr.remaining -= uint32(n) } // read whole sectors for len(d) >= sr.sectorSize { if sr.nextSector < 0 { return copied, errors.New("unexpected end to stream") } n, err := sr.readSector(sr.nextSector, d[:sr.sectorSize]) if n > 0 { d = d[n:] copied += n sr.remaining -= uint32(n) } if err != io.EOF && err != nil { return copied, err } else if n < sr.sectorSize && sr.remaining > 0 { return copied, fmt.Errorf("short read of sector %d: expected %d bytes but got %d", sr.nextSector, sr.sectorSize, n) } sr.nextSector = sr.sat[sr.nextSector] } // read partial sector and buffer the rest if len(d) > 0 { if sr.nextSector < 0 { return copied, errors.New("unexpected end to stream") } // read the full sector sectorN, err := sr.readSector(sr.nextSector, sr.buf) if sectorN > 0 { // fill the rest of the result copyN := copy(d, sr.buf) copied += copyN sr.remaining -= uint32(copyN) } if err != io.EOF && err != nil { return copied, err } else if sectorN < sr.sectorSize && sr.remaining > 0 { // it's ok if the final sector is truncated if there are no more bytes in the stream return copied, fmt.Errorf("short read of sector %d: expected %d bytes but got %d", sr.nextSector, sr.remaining, sectorN) } // save the remainder, if anything sr.saved = sr.buf[len(d):] sr.nextSector = sr.sat[sr.nextSector] } return copied, nil } // Store a blob as a chain of sectors, updating the sector table (or // short-sector table if "short" is set) and return the first sector ID func (r *ComDoc) addStream(contents []byte, short bool) (SecID, error) { var sectorSize int var sat, freeList []SecID if short { sectorSize = int(r.ShortSectorSize) needSectors := (len(contents) + sectorSize - 1) / sectorSize freeList = r.makeFreeSectors(needSectors, true) sat = r.SSAT } else { sectorSize = int(r.SectorSize) needSectors := (len(contents) + sectorSize - 1) / sectorSize freeList = r.makeFreeSectors(needSectors, false) sat = r.SAT } first := SecIDEndOfChain previous := first for _, i := range freeList { if previous == SecIDEndOfChain { first = i } else { sat[previous] = i } previous = i // write to file n := sectorSize if n > len(contents) { n = len(contents) } var err error if short { err = r.writeShortSector(i, contents[:n]) } else { err = r.writeSector(i, contents[:n]) } if err != nil { return 0, err } contents = contents[n:] } sat[previous] = SecIDEndOfChain if len(contents) > 0 { panic("didn't allocate enough sectors") } return first, nil } relic-7.6.1/lib/comdoc/stream_test.go000066400000000000000000000055471455105530300175320ustar00rootroot00000000000000package comdoc_test import ( "bytes" "fmt" "io" "os" "testing" "testing/iotest" "github.com/sassoftware/relic/v7/lib/comdoc" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) func TestStreamRead(t *testing.T) { f, err := comdoc.ReadPath("../../functest/packages/dummy.msi") require.NoError(t, err) files, err := f.ListDir(nil) require.NoError(t, err) // go through all the files once and collect the content contents := make([][]byte, len(files)) for i, ff := range files { reader, err := f.ReadStream(ff) require.NoError(t, err) buf := make([]byte, ff.StreamSize) _, err = io.ReadFull(reader, buf) require.NoError(t, err) contents[i] = buf } t.Run("OddSectors", func(t *testing.T) { b := new(bytes.Buffer) // ensure a mix of full and partial sectors is used buf := make([]byte, f.SectorSize+1) for i, ff := range files { reader, err := f.ReadStream(ff) require.NoError(t, err) b.Reset() n, err := io.CopyBuffer(nonReaderFrom{w: b}, reader, buf) require.NoError(t, err) assert.Equal(t, int64(ff.StreamSize), n) assert.Equal(t, contents[i], b.Bytes()) } }) t.Run("SmallReads", func(t *testing.T) { for i, ff := range files { reader, err := f.ReadStream(ff) require.NoError(t, err) assert.NoError(t, iotest.TestReader(reader, contents[i])) } }) require.NoError(t, f.Close()) } // hide ReadFrom method so CopyBuffer uses the provided buffer type nonReaderFrom struct{ w io.Writer } func (w nonReaderFrom) Write(d []byte) (int, error) { return w.w.Write(d) } func TestTruncated(t *testing.T) { f, err := os.Open("../../functest/packages/dummy.msi") require.NoError(t, err) defer f.Close() info, err := f.Stat() require.NoError(t, err) // vary how many bytes are chopped off for _, shortenBy := range []int64{1, 4095, 4097} { // exercise both full and partial sector errors for _, bufSize := range []int{4096, 1023} { t.Run(fmt.Sprintf("%d-%d", shortenBy, bufSize), func(t *testing.T) { buf := make([]byte, bufSize) trunc := truncatedReaderAt{r: f, size: info.Size() - shortenBy} cdf, err := comdoc.ReadFile(trunc) require.NoError(t, err) files, err := cdf.ListDir(nil) require.NoError(t, err) var someError error for _, ff := range files { reader, err := cdf.ReadStream(ff) require.NoError(t, err) if _, err := io.CopyBuffer(nonReaderFrom{w: io.Discard}, reader, buf); err != nil { someError = err } } assert.ErrorContains(t, someError, "short read") }) } } } type truncatedReaderAt struct { r io.ReaderAt size int64 } // pretend the file is shorter func (r truncatedReaderAt) ReadAt(d []byte, offset int64) (int, error) { dlen := int64(len(d)) overage := offset + dlen - r.size if overage > dlen { return 0, io.EOF } else if overage > 0 { dlen -= overage d = d[:int(dlen)] } return r.r.ReadAt(d, offset) } relic-7.6.1/lib/comdoc/structs.go000066400000000000000000000046111455105530300166760ustar00rootroot00000000000000// // Copyright (c) SAS Institute Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package comdoc import ( "unicode/utf16" ) type SecID int32 type DirType uint8 type Color uint8 const ( SecIDFree SecID = -1 SecIDEndOfChain SecID = -2 SecIDSAT SecID = -3 SecIDMSAT SecID = -4 ) const ( DirEmpty DirType = 0 DirStorage DirType = 1 DirStream DirType = 2 DirRoot DirType = 5 ) const ( Red Color = 0 Black Color = 1 ) const byteOrderMarker uint16 = 0xfffe const msatInHeader = 109 var fileMagic = []byte{0xd0, 0xcf, 0x11, 0xe0, 0xa1, 0xb1, 0x1a, 0xe1} // Raw CDF file header type Header struct { Magic [8]byte UID [16]byte Revision uint16 Version uint16 ByteOrder uint16 SectorSize uint16 // power of 2 ShortSectorSize uint16 // power of 2 Reserved1 [6]byte DirSectorCount uint32 // undocumented? SATSectors uint32 DirNextSector SecID Reserved2 uint32 MinStdStreamSize uint32 SSATNextSector SecID SSATSectorCount uint32 MSATNextSector SecID MSATSectorCount uint32 MSAT [msatInHeader]SecID } // Raw CDF directory entry type RawDirEnt struct { NameRunes [32]uint16 NameLength uint16 Type DirType Color Color LeftChild int32 RightChild int32 StorageRoot int32 UID [16]byte UserFlags uint32 CreateTime uint64 ModifyTime uint64 NextSector SecID StreamSize uint32 _ uint32 } // Return the UTF8 name of this entry func (e RawDirEnt) Name() string { used := e.NameLength/2 - 1 if e.Type == DirEmpty || used > 32 { return "" } return string(utf16.Decode(e.NameRunes[:used])) } // Parsed CDF directory entry type DirEnt struct { RawDirEnt // Index into the directory stream holding this entry Index int name string } // Return the UTF8 name of this entry func (e DirEnt) Name() string { return e.name } relic-7.6.1/lib/comdoc/writer.go000066400000000000000000000062471455105530300165120ustar00rootroot00000000000000// // Copyright (c) SAS Institute Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package comdoc import ( "bytes" "encoding/binary" "errors" "strings" ) // Add or replace a named stream with the given contents. Only streams within // the root storage are currently supported. func (r *ComDoc) AddFile(name string, contents []byte) error { if r.writer == nil { return errors.New("file is not open for writing") } if err := r.DeleteFile(name); err != nil { return err } // store contents isShort := len(contents) < int(r.Header.MinStdStreamSize) nextSector, err := r.addStream(contents, isShort) if err != nil { return err } // create new dirent dirent, err := r.newDirEnt(name, uint32(len(contents)), nextSector) if err != nil { return err } r.rootFiles = append(r.rootFiles, dirent.Index) r.changed = true return nil } // Delete a file from the root storage if it exists func (r *ComDoc) DeleteFile(name string) error { keepFiles := make([]int, 0, len(r.rootFiles)) for _, index := range r.rootFiles { item := &r.Files[index] if !strings.EqualFold(item.name, name) { keepFiles = append(keepFiles, index) continue } if item.Type != DirStream { return errors.New("can't delete or replace storages") } // free storage if item.StreamSize < r.Header.MinStdStreamSize { freeSectors(r.SSAT, item.NextSector) } else { freeSectors(r.SAT, item.NextSector) } // blank out the dirent *item = DirEnt{} r.changed = true } r.rootFiles = keepFiles return nil } // Close the CDF and, if open for writing, commit the remainder of structures // to disk. func (r *ComDoc) Close() error { if !r.changed { if r.closer != nil { r.closer.Close() r.closer = nil } return nil } if err := r.writeShortSAT(); err != nil { return err } if err := r.writeDirStream(); err != nil { return err } // Write MSAT and SAT r.allocSectorTables() if err := r.writeSAT(); err != nil { return err } if err := r.writeMSAT(); err != nil { return err } // Write file header copy(r.Header.Magic[:], fileMagic) r.Header.ByteOrder = byteOrderMarker r.Header.SATSectors = uint32(len(r.MSAT)) r.Header.MSATSectorCount = uint32(len(r.msatList)) buf := bytes.NewBuffer(make([]byte, 0, 512)) _ = binary.Write(buf, binary.LittleEndian, r.Header) if _, err := r.writer.WriteAt(buf.Bytes(), 0); err != nil { return err } // Truncate past last sector. Trailing garbage will cause validation to fail. for i := len(r.SAT) - 1; i >= 0; i-- { if r.SAT[i] != SecIDFree { if err := r.writer.Truncate(r.sectorToOffset(SecID(i + 1))); err != nil { return err } break } } if r.closer != nil { r.closer.Close() r.closer = nil } return nil } relic-7.6.1/lib/compresshttp/000077500000000000000000000000001455105530300161255ustar00rootroot00000000000000relic-7.6.1/lib/compresshttp/compress.go000066400000000000000000000115541455105530300203150ustar00rootroot00000000000000// // Copyright (c) SAS Institute Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package compresshttp import ( "compress/gzip" "errors" "io" "io/ioutil" "net/http" "strings" "sync/atomic" "github.com/golang/snappy" ) const ( acceptEncoding = "Accept-Encoding" contentEncoding = "Content-Encoding" contentLength = "Content-Length" EncodingIdentity = "identity" EncodingGzip = "gzip" EncodingSnappy = "x-snappy-framed" AcceptedEncodings = EncodingSnappy + ", " + EncodingGzip ) // higher is better var prefs = map[string]int{ EncodingGzip: 1, EncodingSnappy: 2, } var ErrUnacceptableEncoding = errors.New("unknown Content-Encoding") func selectEncoding(acceptEncoding string) string { var pref int var best string for _, encoding := range strings.Split(acceptEncoding, ",") { encoding = strings.TrimSpace(strings.Split(encoding, ";")[0]) if p2 := prefs[encoding]; p2 > pref { pref = p2 best = encoding } } return best } func setupCompression(encoding string, w io.Writer) (io.WriteCloser, error) { switch encoding { case EncodingIdentity, "": return nopCloseWriter{Writer: w}, nil case EncodingGzip: return gzip.NewWriterLevel(w, gzip.BestSpeed) case EncodingSnappy: return snappy.NewBufferedWriter(w), nil default: return nil, ErrUnacceptableEncoding } } func compress(encoding string, r io.Reader, w io.Writer) (err error) { compr, err := setupCompression(encoding, w) if err == nil { _, err = io.Copy(compr, r) } if err == nil { err = compr.Close() } return } func decompress(encoding string, r io.Reader) (io.Reader, error) { switch encoding { case EncodingIdentity, "": return ioutil.NopCloser(r), nil case EncodingGzip: return gzip.NewReader(r) case EncodingSnappy: return snappy.NewReader(r), nil default: return nil, ErrUnacceptableEncoding } } func CompressRequest(request *http.Request, acceptEncoding string) error { encoding := selectEncoding(acceptEncoding) if encoding == "" { return nil } plain := &readBlocker{Reader: request.Body} pr, pw := io.Pipe() go func() { err := compress(encoding, plain, pw) _ = plain.Close() _ = pw.CloseWithError(err) }() // Ensure reads inside the goroutine fail after the request terminates. // Otherwise there could be reads happening in parallel from multiple, // different requests, if those requests are reading from the same // underlying file. That could cause file pointers to move unexpectedly, // and it's easier to prevent here than to make sure every use case is // thread-safe. request.Body = alsoClose{ReadCloser: pr, also: plain} request.ContentLength = -1 request.Header.Set(contentEncoding, encoding) return nil } // Wrap a reader and block all reads once Close() is called type readBlocker struct { io.Reader closed uint32 } func (r *readBlocker) Read(d []byte) (int, error) { if atomic.LoadUint32(&r.closed) != 0 { return 0, errors.New("stream is closed") } return r.Reader.Read(d) } func (r *readBlocker) Close() error { if c, ok := r.Reader.(io.Closer); ok { if err := c.Close(); err != nil { return err } } atomic.StoreUint32(&r.closed, 1) return nil } type alsoClose struct { io.ReadCloser also io.Closer } func (a alsoClose) Close() error { a.also.Close() return a.ReadCloser.Close() } func DecompressRequest(request *http.Request) error { r, err := decompress(request.Header.Get(contentEncoding), request.Body) if err == nil { request.Body = ioutil.NopCloser(r) request.ContentLength = -1 } return err } func CompressResponse(r io.Reader, acceptEncoding string, writer http.ResponseWriter, status int) error { encoding := selectEncoding(acceptEncoding) if encoding != "" { writer.Header().Set(contentEncoding, encoding) writer.Header().Del(contentLength) } else { writer.Header().Del(contentEncoding) } writer.WriteHeader(status) return compress(encoding, r, writer) } func DecompressResponse(response *http.Response) error { r, err := decompress(response.Header.Get(contentEncoding), response.Body) if err == nil { response.Body = readAndClose{r: r, c: response.Body} response.ContentLength = -1 } return err } type readAndClose struct { r io.Reader c io.Closer } func (rc readAndClose) Read(d []byte) (int, error) { return rc.r.Read(d) } func (rc readAndClose) Close() error { return rc.c.Close() } type nopCloseWriter struct { io.Writer } func (nopCloseWriter) Close() error { return nil } relic-7.6.1/lib/compresshttp/compress_test.go000066400000000000000000000061271455105530300213540ustar00rootroot00000000000000package compresshttp_test import ( "bytes" "context" "io" "net/http" "net/http/httptest" "testing" "time" "github.com/sassoftware/relic/v7/lib/compresshttp" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) func TestCompress(t *testing.T) { h := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if r.URL.Path == "/fail" { http.NotFound(w, r) return } rbody, err := io.ReadAll(r.Body) if assert.NoError(t, err) { assert.Len(t, rbody, 8192) } // write 16384 bytes in two chunks with a flush in between d := make([]byte, 8192) _, err = w.Write(d) require.NoError(t, err) w.(http.Flusher).Flush() _, err = w.Write(d) require.NoError(t, err) }) srv := httptest.NewServer(compresshttp.Middleware(h)) defer srv.Close() for _, ae := range []string{"", "identity", "gzip", "x-snappy-framed"} { t.Run("Accept_"+ae, func(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer cancel() d := make([]byte, 8192) req, err := http.NewRequestWithContext(ctx, http.MethodPost, srv.URL, bytes.NewReader(d)) require.NoError(t, err) require.NoError(t, compresshttp.CompressRequest(req, ae)) if ae != "" { req.Header.Set("Accept-Encoding", ae) } resp, err := http.DefaultClient.Do(req) require.NoError(t, err) assert.Equal(t, http.StatusOK, resp.StatusCode) if ae == "identity" { assert.Empty(t, resp.Header.Get("Content-Encoding")) } else { assert.Equal(t, ae, resp.Header.Get("Content-Encoding")) } require.NoError(t, compresshttp.DecompressResponse(resp)) body, err := io.ReadAll(resp.Body) require.NoError(t, err) assert.Len(t, body, 16384) assert.Equal(t, compresshttp.AcceptedEncodings, resp.Header.Get("Accept-Encoding")) }) } t.Run("Error", func(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer cancel() d := make([]byte, 8192) req, err := http.NewRequestWithContext(ctx, http.MethodPost, srv.URL+"/fail", bytes.NewReader(d)) require.NoError(t, err) require.NoError(t, compresshttp.CompressRequest(req, compresshttp.EncodingSnappy)) req.Header.Set("Accept-Encoding", compresshttp.AcceptedEncodings) resp, err := http.DefaultClient.Do(req) require.NoError(t, err) assert.Equal(t, http.StatusNotFound, resp.StatusCode) assert.Empty(t, resp.Header.Get("Content-Encoding")) require.NoError(t, compresshttp.DecompressResponse(resp)) body, err := io.ReadAll(resp.Body) require.NoError(t, err) assert.Contains(t, string(body), "not found") }) t.Run("Unsupported", func(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer cancel() d := make([]byte, 8192) req, err := http.NewRequestWithContext(ctx, http.MethodPost, srv.URL, bytes.NewReader(d)) require.NoError(t, err) req.Header.Set("Content-Encoding", "spam") resp, err := http.DefaultClient.Do(req) require.NoError(t, err) assert.Equal(t, http.StatusUnsupportedMediaType, resp.StatusCode) assert.Equal(t, compresshttp.AcceptedEncodings, resp.Header.Get("Accept-Encoding")) }) } relic-7.6.1/lib/compresshttp/middleware.go000066400000000000000000000047641455105530300206040ustar00rootroot00000000000000package compresshttp import ( "io" "log" "net/http" ) func Middleware(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Header().Set(acceptEncoding, AcceptedEncodings) // decompress request if err := DecompressRequest(r); err == ErrUnacceptableEncoding { http.Error(w, "invalid content-encoding", http.StatusUnsupportedMediaType) return } else if err != nil { log.Printf("error: decoding request from %s: %+v", r.RemoteAddr, err) http.Error(w, "failed to decompress request", http.StatusBadRequest) return } // choose response encoding encoding := selectEncoding(r.Header.Get(acceptEncoding)) if encoding == "" || encoding == EncodingIdentity { // shortcut if no encoding is possible next.ServeHTTP(w, r) return } w.Header().Del("Content-Length") // wrap writer in compression and call handler wrapped := &responseCompressor{ rw: w, encoding: encoding, } next.ServeHTTP(wrapped, r) // flush if err := wrapped.Close(); err != nil { log.Printf("error: flushing response to %s: %+v", r.RemoteAddr, err) } }) } type responseCompressor struct { rw http.ResponseWriter wc io.WriteCloser encoding string wroteHeader bool } func (w *responseCompressor) WriteHeader(status int) { if !w.wroteHeader { if status >= 300 { // don't compress errors w.encoding = "" } else if w.encoding != "" && w.encoding != EncodingIdentity { w.Header().Set(contentEncoding, w.encoding) } w.wroteHeader = true } w.rw.WriteHeader(status) } func (w *responseCompressor) Write(d []byte) (int, error) { // wait until the first byte to start compressing so that it can be // selectively disabled in the case of errors if w.wc == nil { if !w.wroteHeader { w.Header().Set(contentEncoding, w.encoding) w.rw.WriteHeader(http.StatusOK) w.wroteHeader = true } var err error w.wc, err = setupCompression(w.encoding, w.rw) if err != nil { return 0, err } } return w.wc.Write(d) } func (w *responseCompressor) Header() http.Header { return w.rw.Header() } func (w *responseCompressor) Flush() { // flush compressor if flusher, ok := w.wc.(flusher); ok { if err := flusher.Flush(); err != nil { log.Println("warning: flushing compressor:", err) } } // flush response if flusher, ok := w.rw.(http.Flusher); ok { flusher.Flush() } } func (w *responseCompressor) Close() error { if w.wc == nil { return nil } return w.wc.Close() } type flusher interface { Flush() error } relic-7.6.1/lib/dlog/000077500000000000000000000000001455105530300143175ustar00rootroot00000000000000relic-7.6.1/lib/dlog/dlog.go000066400000000000000000000020121455105530300155660ustar00rootroot00000000000000// // Copyright (c) SAS Institute Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package dlog import ( "fmt" "log" "sync/atomic" "github.com/kr/pretty" ) var debugLevel uint32 func SetLevel(level uint32) { atomic.StoreUint32(&debugLevel, level) } func Printf(level uint32, format string, args ...interface{}) { if debugLevel >= level { log.Printf("[debug.%d] %s", level, fmt.Sprintf(format, args...)) } } func PrettyPrint(level uint32, v interface{}) { Printf(level, "%# v", pretty.Formatter(v)) } relic-7.6.1/lib/fruit/000077500000000000000000000000001455105530300145235ustar00rootroot00000000000000relic-7.6.1/lib/fruit/csblob/000077500000000000000000000000001455105530300157675ustar00rootroot00000000000000relic-7.6.1/lib/fruit/csblob/asn1.go000066400000000000000000000116351455105530300171660ustar00rootroot00000000000000package csblob import ( "crypto/x509" "encoding/asn1" ) // Extensions for specific types of key usage. // These endorse a leaf certificate to create signatures with the named capability. // https://images.apple.com/certificateauthority/pdf/Apple_WWDR_CPS_v1.22.pdf var ( CodeSign = asn1.ObjectIdentifier{1, 2, 840, 113635, 100, 6, 1} CodeSignApple = asn1.ObjectIdentifier{1, 2, 840, 113635, 100, 6, 1, 1} CodeSignIphoneDev = asn1.ObjectIdentifier{1, 2, 840, 113635, 100, 6, 1, 2} CodeSignIphoneApple = asn1.ObjectIdentifier{1, 2, 840, 113635, 100, 6, 1, 3} CodeSignIphoneSubmit = asn1.ObjectIdentifier{1, 2, 840, 113635, 100, 6, 1, 4} CodeSignSafariExtension = asn1.ObjectIdentifier{1, 2, 840, 113635, 100, 6, 1, 5} CodeSignMacAppSubmit = asn1.ObjectIdentifier{1, 2, 840, 113635, 100, 6, 1, 7} CodeSignMacInstallerSubmit = asn1.ObjectIdentifier{1, 2, 840, 113635, 100, 6, 1, 8} CodeSignMacAppStore = asn1.ObjectIdentifier{1, 2, 840, 113635, 100, 6, 1, 9} CodeSignMacAppStoreInstaller = asn1.ObjectIdentifier{1, 2, 840, 113635, 100, 6, 1, 10} CodeSignMacDev = asn1.ObjectIdentifier{1, 2, 840, 113635, 100, 6, 1, 12} CodeSignDevIDExecute = asn1.ObjectIdentifier{1, 2, 840, 113635, 100, 6, 1, 13} CodeSignDevIDInstall = asn1.ObjectIdentifier{1, 2, 840, 113635, 100, 6, 1, 14} CodeSignDevIDKernel = asn1.ObjectIdentifier{1, 2, 840, 113635, 100, 6, 1, 18} ) // These endorse an intermediate certificate to sign a certain type of leaf. var ( Intermediate = asn1.ObjectIdentifier{1, 2, 840, 113635, 100, 6, 2} IntermediateWWDR = asn1.ObjectIdentifier{1, 2, 840, 113635, 100, 6, 2, 1} IntermediateITMS = asn1.ObjectIdentifier{1, 2, 840, 113635, 100, 6, 2, 2} IntermediateAAI = asn1.ObjectIdentifier{1, 2, 840, 113635, 100, 6, 2, 3} IntermediateDevID = asn1.ObjectIdentifier{1, 2, 840, 113635, 100, 6, 2, 6} ) // Authenticated attributes found in a signature var ( // AttrCodeDirHashPlist holds a plist with (truncated) hashes of each code // directory found in the signature AttrCodeDirHashPlist = asn1.ObjectIdentifier{1, 2, 840, 113635, 100, 9, 1} // AttrCodeDirHashes is a set of code directory digests identified by ASN.1 // algorithm AttrCodeDirHashes = asn1.ObjectIdentifier{1, 2, 840, 113635, 100, 9, 2} ) func hasPrefix(id, prefix asn1.ObjectIdentifier) bool { if len(id) < len(prefix) { return false } return id[:len(prefix)].Equal(prefix) } // MarkHandledExtensions marks proprietary critical extensions as handled so // that chain verification can proceed func MarkHandledExtensions(cert *x509.Certificate) { var unhandled []asn1.ObjectIdentifier for _, ext := range cert.UnhandledCriticalExtensions { if !hasPrefix(ext, CodeSign) { unhandled = append(unhandled, ext) } } cert.UnhandledCriticalExtensions = unhandled } // TeamID returns the team identifier found in an apple-issued leaf certificate, // or "" if none was found func TeamID(cert *x509.Certificate) string { for _, ext := range cert.Extensions { if hasPrefix(ext.Id, CodeSign) { // team id should be in the OU field if v := cert.Subject.OrganizationalUnit; len(v) == 1 { return v[0] } } } return "" } // RootCA lists known proprietary certificate roots const RootCA = `-----BEGIN CERTIFICATE----- MIIEuzCCA6OgAwIBAgIBAjANBgkqhkiG9w0BAQUFADBiMQswCQYDVQQGEwJVUzET MBEGA1UEChMKQXBwbGUgSW5jLjEmMCQGA1UECxMdQXBwbGUgQ2VydGlmaWNhdGlv biBBdXRob3JpdHkxFjAUBgNVBAMTDUFwcGxlIFJvb3QgQ0EwHhcNMDYwNDI1MjE0 MDM2WhcNMzUwMjA5MjE0MDM2WjBiMQswCQYDVQQGEwJVUzETMBEGA1UEChMKQXBw bGUgSW5jLjEmMCQGA1UECxMdQXBwbGUgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkx FjAUBgNVBAMTDUFwcGxlIFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw ggEKAoIBAQDkkakJH5HbHkdQ6wXtXnmELes2oldMVeyLGYne+Uts9QerIjAC6Bg+ +FAJ039BqJj50cpmnCRrEdCju+QbKsMflZ56DKRHi1vUFjczy8QPTc4UadHJGXL1 XQ7Vf1+b8iUDulWPTV0N8WQ1IxVLFVkds5T39pyez1C6wVhQZ48ItCD3y6wsIG9w tj8BMIy3Q88PnT3zK0koGsj+zrW5DtleHNbLPbU6rfQPDgCSC7EhFi501TwN22IW q6NxkkdTVcGvL0Gz+PvjcM3mo0xFfh9Ma1CWQYnEdGILEINBhzOKgbEwWOxaBDKM aLOPHd5lc/9nXmW8Sdh2nzMUZaF3lMktAgMBAAGjggF6MIIBdjAOBgNVHQ8BAf8E BAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUK9BpR5R2Cf70a40uQKb3 R01/CF4wHwYDVR0jBBgwFoAUK9BpR5R2Cf70a40uQKb3R01/CF4wggERBgNVHSAE ggEIMIIBBDCCAQAGCSqGSIb3Y2QFATCB8jAqBggrBgEFBQcCARYeaHR0cHM6Ly93 d3cuYXBwbGUuY29tL2FwcGxlY2EvMIHDBggrBgEFBQcCAjCBthqBs1JlbGlhbmNl IG9uIHRoaXMgY2VydGlmaWNhdGUgYnkgYW55IHBhcnR5IGFzc3VtZXMgYWNjZXB0 YW5jZSBvZiB0aGUgdGhlbiBhcHBsaWNhYmxlIHN0YW5kYXJkIHRlcm1zIGFuZCBj b25kaXRpb25zIG9mIHVzZSwgY2VydGlmaWNhdGUgcG9saWN5IGFuZCBjZXJ0aWZp Y2F0aW9uIHByYWN0aWNlIHN0YXRlbWVudHMuMA0GCSqGSIb3DQEBBQUAA4IBAQBc NplMLXi37Yyb3PN3m/J20ncwT8EfhYOFG5k9RzfyqZtAjizUsZAS2L70c5vu0mQP y3lPNNiiPvl4/2vIB+x9OYOLUyDTOMSxv5pPCmv/K/xZpwUJfBdAVhEedNO3iyM7 R6PVbyTi69G3cN8PReEnyvFteO3ntRcXqNx+IjXKJdXZD9Zr1KIkIxH3oayPc4Fg xhtbCS+SsvhESPBgOJ4V9T0mZyCKM2r3DYLP3uujL/lTaltkwGMzd/c6ByxW69oP IQ7aunMZT7XZNn/Bh1XZp5m5MkL72NVxnn6hUrcbvZNCJBIqxw8dtk2cXmPIS4AX UKqK1drk/NAJBzewdXUh -----END CERTIFICATE----- ` relic-7.6.1/lib/fruit/csblob/attrs.go000066400000000000000000000045361455105530300174630ustar00rootroot00000000000000package csblob import ( "crypto" "crypto/hmac" "crypto/x509/pkix" "encoding/asn1" "fmt" "howett.net/plist" "github.com/sassoftware/relic/v7/lib/pkcs7" "github.com/sassoftware/relic/v7/lib/x509tools" ) func checkCDHashes(si *pkcs7.SignerInfo, computed map[crypto.Hash][]byte) error { var cdHashes []cdHashAttrib if err := si.AuthenticatedAttributes.GetAll(AttrCodeDirHashes, &cdHashes); err != nil { if _, ok := err.(pkcs7.ErrNoAttribute); ok { return nil } return err } for _, cd := range cdHashes { hash, err := x509tools.PkixDigestToHashE(pkix.AlgorithmIdentifier{Algorithm: cd.Algorithm}) if err != nil { return err } hc := computed[hash] if hc == nil { return fmt.Errorf("missing hash with algorithm %s", hash) } if !hmac.Equal(hc, cd.Digest) { return fmt.Errorf("digest mismatch: expected %x, got %x", cd.Digest, hc) } } return nil } func addCSHashes(builder *pkcs7.SignatureBuilder, hashes []cdHashAttrib) error { for _, h := range hashes { if err := builder.AddAuthenticatedAttribute(AttrCodeDirHashes, h); err != nil { return err } } return nil } func checkPlistHashes(dirs []*CodeDirectory, si *pkcs7.SignerInfo, computed map[crypto.Hash][]byte) error { var computedList [][]byte for _, dir := range dirs { computedList = append(computedList, computed[dir.HashFunc][:20]) } var plistText []byte if err := si.AuthenticatedAttributes.GetOne(AttrCodeDirHashPlist, &plistText); err != nil { if _, ok := err.(pkcs7.ErrNoAttribute); ok { return nil } return err } var parsed cdHashPlist if _, err := plist.Unmarshal(plistText, &parsed); err != nil { return err } if len(parsed.CDHashes) != len(computedList) { return fmt.Errorf("expected %d hashes but got %d", len(parsed.CDHashes), len(computedList)) } for i, expected := range parsed.CDHashes { actual := computedList[i] if !hmac.Equal(expected, actual) { return fmt.Errorf("digest mismatch: expected %x, got %x", expected, actual) } } return nil } func addPlistHashes(builder *pkcs7.SignatureBuilder, pl cdHashPlist) error { blob, err := plist.MarshalIndent(pl, plist.XMLFormat, " ") if err != nil { return err } return builder.AddAuthenticatedAttribute(AttrCodeDirHashPlist, blob) } type cdHashAttrib struct { Algorithm asn1.ObjectIdentifier Digest []byte } type cdHashPlist struct { CDHashes [][]byte `plist:"cdhashes"` } relic-7.6.1/lib/fruit/csblob/codedir.go000066400000000000000000000147021455105530300177330ustar00rootroot00000000000000package csblob import ( "bytes" "crypto" "encoding/binary" "errors" ) type CodeDirectoryHeader struct { Magic csMagic Length uint32 Version uint32 Flags SignatureFlags HashOffset uint32 IdentOffset uint32 SpecialSlotCount uint32 CodeSlotCount uint32 CodeLimit uint32 HashSize uint8 HashType HashType _ uint8 PageSizeLog2 uint8 _ uint32 // Version >= 0x20100 ScatterOffset uint32 // Version >= 0x20200 TeamOffset uint32 _ uint32 // Version >= 0x20300 CodeLimit64 int64 // Version >= 0x20400 ExecSegmentBase int64 ExecSegmentLimit int64 ExecSegmentFlags int64 } type CodeDirectory struct { Header CodeDirectoryHeader SigningIdentity string TeamIdentifier string HashFunc crypto.Hash CodeHashes [][]byte ManifestHash []byte RequirementsHash []byte ResourcesHash []byte EntitlementsHash []byte EntitlementsDERHash []byte RepSpecificHash []byte Raw []byte CDHash []byte IType uint32 } type SignatureFlags uint32 // CSCommon.h const ( FlagHost SignatureFlags = 0x000001 FlagAdhoc SignatureFlags = 0x000002 FlagForceHard SignatureFlags = 0x000100 FlagForceKill SignatureFlags = 0x000200 FlagForceExpiration SignatureFlags = 0x000400 FlagRestrict SignatureFlags = 0x000800 FlagEnforcement SignatureFlags = 0x001000 FlagLibraryValidation SignatureFlags = 0x002000 FlagRuntime SignatureFlags = 0x010000 FlagLinkerSigned SignatureFlags = 0x020000 ) // don't propagate these to a new signature const clearFlags = FlagAdhoc | FlagLinkerSigned func parseCodeDirectory(blob []byte, itype uint32) (*CodeDirectory, error) { var hdr CodeDirectoryHeader if err := binary.Read(bytes.NewReader(blob), binary.BigEndian, &hdr); err != nil { return nil, err } // zero out fields that aren't present in the current version switch { case hdr.Version < 0x20100: hdr.ScatterOffset = 0 fallthrough case hdr.Version < 0x20200: hdr.TeamOffset = 0 fallthrough case hdr.Version < 0x20300: hdr.CodeLimit64 = 0 fallthrough case hdr.Version < 0x20400: hdr.ExecSegmentBase = 0 hdr.ExecSegmentFlags = 0 hdr.ExecSegmentLimit = 0 } dir := &CodeDirectory{ Header: hdr, Raw: blob, IType: itype, } // read indirect fields var err error if hdr.IdentOffset != 0 { dir.SigningIdentity, err = cstring(blob, int(hdr.IdentOffset)) if err != nil { return nil, err } } if hdr.TeamOffset != 0 { dir.TeamIdentifier, err = cstring(blob, int(hdr.TeamOffset)) if err != nil { return nil, err } } if hdr.ScatterOffset != 0 { return nil, errors.New("scatterOffset is not supported") } dir.HashFunc, err = hashFunc(hdr.HashType, hdr.HashSize) if err != nil { return nil, err } // hash over whole directory for signature to use h := dir.HashFunc.New() h.Write(blob) dir.CDHash = h.Sum(nil) // read hash slots hashBase := int(hdr.HashOffset) hashLen := int(hdr.HashSize) slot := func(i int) []byte { hash := blob[hashBase+i*hashLen : hashBase+(i+1)*hashLen] for _, c := range hash { if c != 0 { return hash } } // all zero return nil } dir.CodeHashes = make([][]byte, hdr.CodeSlotCount) for i := 0; i < int(hdr.CodeSlotCount); i++ { dir.CodeHashes[i] = slot(i) } for i := 1; i <= int(hdr.SpecialSlotCount); i++ { // special slots are placed before the code slots with an index equal to // the negative of their superblob itype v := slot(-i) switch i { case cdInfoSlot: dir.ManifestHash = v case cdRequirementsSlot: dir.RequirementsHash = v case cdResourceDirSlot: dir.ResourcesHash = v case cdEntitlementSlot: dir.EntitlementsHash = v case cdEntitlementDERSlot: dir.EntitlementsDERHash = v case cdRepSpecificSlot: dir.RepSpecificHash = v } } return dir, nil } func cstring(blob []byte, i int) (string, error) { if i >= len(blob) { return "", errShort } blob = blob[i:] j := bytes.IndexByte(blob, 0) if j < 0 { return "", errShort } return string(blob[:j]), nil } type codeDirParams struct { *SignatureParams Specials [][]byte CodeSlots []byte CodeSlotCount uint32 HashFunc crypto.Hash CodeLimit int64 SinglePage bool } type codeDirResult struct { Raw []byte Digest []byte HashFunc crypto.Hash } func newCodeDirectory(params codeDirParams) (codeDirResult, error) { hdr := CodeDirectoryHeader{ Magic: csCodeDirectory, Version: 0x20300, Flags: params.Flags, SpecialSlotCount: uint32(len(params.Specials)), CodeSlotCount: params.CodeSlotCount, HashSize: uint8(params.HashFunc.Size()), PageSizeLog2: defaultPageSizeLog2, ExecSegmentBase: params.ExecSegmentBase, ExecSegmentLimit: params.ExecSegmentLimit, ExecSegmentFlags: params.ExecSegmentFlags, } var err error hdr.HashType, err = hashType(params.HashFunc) if err != nil { return codeDirResult{}, err } if hdr.ExecSegmentBase != 0 || hdr.ExecSegmentLimit != 0 || hdr.ExecSegmentFlags != 0 { hdr.Version = 0x20400 } if params.SinglePage { // single slot DMG hdr.PageSizeLog2 = 0 } // compute special slots h := params.HashFunc.New() specialSlots := make([]byte, 0, h.Size()*len(params.Specials)) for _, special := range params.Specials { if special != nil { h.Reset() h.Write(special) specialSlots = h.Sum(specialSlots) } else { specialSlots = specialSlots[:len(specialSlots)+h.Size()] } } // fill out header if params.CodeLimit > (1<<31)-2 { hdr.CodeLimit64 = params.CodeLimit } else { hdr.CodeLimit = uint32(params.CodeLimit) } offset := binary.Size(hdr) hdr.IdentOffset = uint32(offset) offset += len(params.SigningIdentity) + 1 if params.TeamIdentifier != "" { hdr.TeamOffset = uint32(offset) offset += len(params.TeamIdentifier) + 1 } hdr.HashOffset = uint32(offset) + hdr.SpecialSlotCount*uint32(hdr.HashSize) offset += len(specialSlots) + len(params.CodeSlots) hdr.Length = uint32(offset) // marshal b := bytes.NewBuffer(make([]byte, 0, offset)) if err := binary.Write(b, binary.BigEndian, hdr); err != nil { return codeDirResult{}, err } b.WriteString(params.SigningIdentity) b.WriteByte(0) if params.TeamIdentifier != "" { b.WriteString(params.TeamIdentifier) b.WriteByte(0) } b.Write(specialSlots) b.Write(params.CodeSlots) blob := b.Bytes() // compute cd hash h.Reset() h.Write(blob) return codeDirResult{ Raw: blob, Digest: h.Sum(nil), HashFunc: params.HashFunc, }, nil } relic-7.6.1/lib/fruit/csblob/csblob.go000066400000000000000000000037001455105530300175620ustar00rootroot00000000000000package csblob import ( "fmt" "sort" ber "github.com/go-asn1-ber/asn1-ber" "github.com/sassoftware/relic/v7/lib/pkcs7" ) type SigBlob struct { // with blob header Entitlement []byte EntitlementDER []byte RawRequirements []byte NotaryTicket []byte Unknowns [][]byte Directories []*CodeDirectory CMS *pkcs7.ContentInfoSignedData } func parseSignature(blob []byte) (*SigBlob, error) { magic, items, err := parseSuper(blob) if err != nil { return nil, err } if magic != csEmbeddedSignature && magic != csDetachedSignature { return nil, fmt.Errorf("expected embedded signature but got %08x", magic) } sig := new(SigBlob) for _, item := range items { switch { case item.itype == cdRequirementsSlot: sig.RawRequirements = item.data case item.itype == cdEntitlementSlot: sig.Entitlement = item.data case item.itype == cdEntitlementDERSlot: sig.EntitlementDER = item.data case item.itype == cdTicketSlot: sig.NotaryTicket = item.data case item.itype == cdCodeDirectorySlot || item.itype >= cdAlternateCodeDirectorySlots && item.itype < cdAlternateCodeDirectorySlots+6: dir, err := parseCodeDirectory(item.data, item.itype) if err != nil { return nil, err } sig.Directories = append(sig.Directories, dir) case item.itype == cdSignatureSlot: if len(item.data) <= 8 { sig.CMS = nil continue } // For some inane reason signatures are encoded with an indefinite // length content, which go's asn1 lib chokes on because it's not // DER. Use BER library to reencode. pkt, err := ber.DecodePacketErr(item.data[8:]) if err != nil { return nil, err } der := pkt.Bytes() psd, err := pkcs7.Unmarshal(der) if err != nil { return nil, err } sig.CMS = psd default: sig.Unknowns = append(sig.Unknowns, item.data) } } sort.Slice(sig.Directories, func(i, j int) bool { return sig.Directories[i].IType < sig.Directories[j].IType }) return sig, nil } relic-7.6.1/lib/fruit/csblob/pagehash.go000066400000000000000000000033201455105530300200740ustar00rootroot00000000000000package csblob import ( "crypto" "fmt" "hash" "io" ) type HashType uint8 // CSCommon.h const ( HashNone HashType = iota HashSHA1 HashSHA256 HashSHA256Truncated HashSHA384 HashSHA512 ) func hashFunc(hashType HashType, hashLen uint8) (h crypto.Hash, err error) { switch hashType { case HashSHA1: h = crypto.SHA1 case HashSHA256: h = crypto.SHA256 case HashSHA384: h = crypto.SHA384 } if h == 0 { err = fmt.Errorf("unknown hash type %d", err) } else if h.Size() != int(hashLen) { err = fmt.Errorf("expected size %d for hash %d (%s) but got %d", h.Size(), hashType, h, hashLen) } return } func hashType(h crypto.Hash) (HashType, error) { switch h { case crypto.SHA1: return HashSHA1, nil case crypto.SHA256: return HashSHA256, nil case crypto.SHA384: return HashSHA384, nil default: return 0, fmt.Errorf("unsupported hash type %s", h) } } func hashPages(hashFuncs []crypto.Hash, pages io.Reader, singlePage bool) (slots [][]byte, slotCount uint32, codeLimit int64, err error) { hashers := make([]hash.Hash, len(hashFuncs)) writers := make([]io.Writer, len(hashFuncs)) slots = make([][]byte, len(hashFuncs)) for i, f := range hashFuncs { hashers[i] = f.New() writers[i] = hashers[i] } if singlePage { codeLimit, err = io.Copy(io.MultiWriter(writers...), pages) if err != nil { return } for i, h := range hashers { slots[i] = h.Sum(nil) } slotCount = 1 return } buf := make([]byte, 1<= 0x80 { v >>= 7 } else { break } } // reverse and set MSB on all but the last word for i := len(outv) - 1; i >= 0; i-- { vv := outv[i] if i != 0 { vv |= 0x80 } out = append(out, vv) } } b.putData(out) } func (b *reqBuilder) and(items ...func()) func() { return func() { for len(items) > 1 { b.putUint32(opAnd) items[0]() items = items[1:] } items[0]() } } func (b *reqBuilder) identifier(v string) func() { return func() { b.putUint32(opIdent) b.putData([]byte(v)) } } func (b *reqBuilder) anchorAppleGeneric() func() { return func() { b.putUint32(opAppleGenericAnchor) } } func (b *reqBuilder) certFieldEqual(slot int32, field, value string) func() { return func() { b.putUint32(opCertField) b.putUint32(uint32(slot)) b.putData([]byte(field)) b.putUint32(uint32(matchEqual)) b.putData([]byte(value)) } } func (b *reqBuilder) certExtensionExists(slot int32, oid asn1.ObjectIdentifier) func() { return func() { b.putUint32(opCertGeneric) b.putUint32(uint32(slot)) b.putOID(oid) b.putUint32(uint32(matchExists)) } } relic-7.6.1/lib/fruit/csblob/reqparse.go000066400000000000000000000215521455105530300201450ustar00rootroot00000000000000package csblob import ( "encoding/asn1" "encoding/binary" "errors" "fmt" "io" "strings" "time" "unicode" ) type RequirementType uint32 // CSCommon.h const ( HostRequirement RequirementType = iota + 1 GuestRequirement DesignatedRequirement LibraryRequirement PluginRequirement ) func (t RequirementType) String() string { switch t { case HostRequirement: return "host" case GuestRequirement: return "guest" case DesignatedRequirement: return "designated" case LibraryRequirement: return "library" case PluginRequirement: return "plugin" default: return fmt.Sprintf("/*unknown type*/ %d", uint32(t)) } } type Requirements map[RequirementType]*Requirement type Requirement struct { Raw []byte } func (b *SigBlob) Requirements() (Requirements, error) { magic, items, err := parseSuper(b.RawRequirements) if err != nil { return nil, fmt.Errorf("internal requirements: %w", err) } if magic != csRequirements { return nil, errors.New("internal requirements: bad magic") } reqs := make(Requirements) for _, item := range items { reqs[RequirementType(item.itype)] = &Requirement{Raw: item.data[8:]} } return reqs, nil } func (r Requirements) Dump(w io.Writer) error { for reqType, req := range r { formatted, err := req.Format() if err != nil { return fmt.Errorf("%s: %w", reqType, err) } fmt.Fprintln(w, reqType, "=>", formatted) } return nil } func (r *Requirement) Format() (string, error) { d := &reqDumper{buf: r.Raw} if n, ok := d.getUint32(); !ok { return "", d.err } else if n != 1 { return "", fmt.Errorf("unsupported requirement format %d", n) } d.op(levelTop) if d.err != nil { return "", d.err } return d.out.String(), nil } type reqDumper struct { buf []byte out strings.Builder err error } func (d *reqDumper) getUint32() (uint32, bool) { if len(d.buf) < 4 { d.err = io.ErrUnexpectedEOF return 0, false } n := binary.BigEndian.Uint32(d.buf) d.buf = d.buf[4:] return n, true } func (d *reqDumper) getInt32() (int32, bool) { n, ok := d.getUint32() return int32(n), ok } func (d *reqDumper) op(level syntaxLevel) { if d.err != nil { return } op, ok := d.getUint32() if !ok { return } opN := opCode(op &^ opFlagMask) switch opN { case opFalse: d.out.WriteString("never") case opTrue: d.out.WriteString("always") case opIdent: d.out.WriteString("identifier ") d.data() case opAppleAnchor: d.out.WriteString("anchor apple") case opAppleGenericAnchor: d.out.WriteString("anchor apple generic") case opAnchorHash: d.out.WriteString("certificate") d.certSlot() d.out.WriteString(" = ") d.hashData() case opInfoKeyValue: d.out.WriteString("info[") d.dotString() d.out.WriteString("] = ") d.data() case opAnd: if level < levelAnd { d.out.WriteByte('(') } d.op(levelAnd) d.out.WriteString(" and ") d.op(levelAnd) if level < levelAnd { d.out.WriteByte(')') } case opOr: if level < levelOr { d.out.WriteByte('(') } d.op(levelOr) d.out.WriteString(" or ") d.op(levelOr) if level < levelOr { d.out.WriteByte(')') } case opNot: d.out.WriteString("! ") d.op(levelPrimary) case opCDHash: d.out.WriteString("cdhash ") d.hashData() case opInfoKeyField: d.out.WriteString("info[") d.dotString() d.out.WriteByte(']') d.match() case opEntitlementField: d.out.WriteString("entitlement[") d.dotString() d.out.WriteByte(']') d.match() case opCertField: d.out.WriteString("certificate") d.certSlot() d.out.WriteByte('[') d.dotString() d.out.WriteByte(']') d.match() case opCertFieldDate: d.out.WriteString("certificate") d.certSlot() d.out.WriteString("[timestamp.") d.oidData() d.out.WriteByte(']') case opCertGeneric: d.out.WriteString("certificate") d.certSlot() d.out.WriteString("[field.") d.oidData() d.out.WriteByte(']') d.match() case opCertPolicy: d.out.WriteString("certificate") d.certSlot() d.out.WriteString("[policy.") d.oidData() d.out.WriteByte(']') d.match() case opTrustedCert: d.out.WriteString("certificate") d.certSlot() d.out.WriteString("trusted") case opTrustedCerts: d.out.WriteString("anchor trusted") case opNamedAnchor: d.out.WriteString("anchor apple ") d.data() case opNamedCode: d.out.WriteByte('(') d.data() d.out.WriteByte(')') case opPlatform: n, ok := d.getInt32() if !ok { return } fmt.Fprintf(&d.out, "platform = %d", n) d.buf = d.buf[4:] case opNotarized: d.out.WriteString("notarized") case opLegacyDevID: d.out.WriteString("legacy") default: switch { case op&opGenericFalse != 0: fmt.Fprintf(&d.out, " false /* opcode %d */", opN) case op&opGenericSkip != 0: fmt.Fprintf(&d.out, " /* opcode %d */", opN) default: d.err = fmt.Errorf("unrecognized opcode %d", opN) return } } } func (d *reqDumper) getData() []byte { length, ok := d.getUint32() if !ok { return nil } aligned := length if n := length % 4; n != 0 { aligned += 4 - n } if uint32(len(d.buf)) < aligned { d.err = io.ErrUnexpectedEOF return nil } v := d.buf[:length] d.buf = d.buf[aligned:] return v } func (d *reqDumper) certSlot() { n, ok := d.getInt32() if !ok { return } switch n { case 0: d.out.WriteString(" leaf") case -1: d.out.WriteString(" root") default: fmt.Fprintf(&d.out, " %d", n) } } func (d *reqDumper) match() { n, ok := d.getUint32() if !ok { return } switch matchOp(n) { case matchExists: d.out.WriteString(" /* exists */") case matchAbsent: d.out.WriteString(" absent ") case matchEqual: d.out.WriteString(" = ") d.data() case matchContains: d.out.WriteString(" ~ ") d.data() case matchBeginsWith: d.out.WriteString(" = ") d.data() d.out.WriteByte('*') case matchEndsWith: d.out.WriteString(" = *") d.data() case matchLessThan: d.out.WriteString(" < ") d.data() case matchGreaterEqual: d.out.WriteString(" >= ") d.data() case matchLessEqual: d.out.WriteString(" <= ") d.data() case matchGreaterThan: d.out.WriteString(" > ") d.data() case matchOn: d.out.WriteString(" = ") d.timestamp() case matchBefore: d.out.WriteString(" < ") d.timestamp() case matchAfter: d.out.WriteString(" > ") d.timestamp() case matchOnOrBefore: d.out.WriteString(" <= ") d.timestamp() case matchOnOrAfter: d.out.WriteString(" >= ") d.timestamp() default: d.err = fmt.Errorf("unrecognized match opcode %d", n) } } func (d *reqDumper) dataExt(dotOK bool) { v := d.getData() if len(v) == 0 { if d.err == nil { d.out.WriteString("\"\"") } return } simple, printable := true, true scan: for i, vv := range v { switch { case vv == '.' && dotOK: // simple case vv >= '0' && vv <= '9': if i == 0 { // can't start with a digit simple = false } case vv >= 'a' && vv <= 'z' || vv >= 'A' && vv <= 'Z': // simple case vv < 128 && unicode.IsGraphic(rune(vv)): simple = false default: printable = false simple = false break scan } } switch { case simple: d.out.Write(v) case printable: d.out.WriteByte('"') for _, vv := range v { if vv == '"' || vv == '\\' { d.out.WriteByte('\\') } d.out.WriteByte(vv) } d.out.WriteByte('"') default: fmt.Fprintf(&d.out, "0x%x", v) } } func (d *reqDumper) data() { d.dataExt(false) } func (d *reqDumper) dotString() { d.dataExt(true) } func (d *reqDumper) hashData() { fmt.Fprintf(&d.out, "H\"%x\"", d.getData()) } func (d *reqDumper) oidData() { buf := d.getData() var oid asn1.ObjectIdentifier for len(buf) > 0 { var n int for len(buf) > 0 { var x byte x, buf = buf[0], buf[1:] n |= int(x &^ 0x80) if x&0x80 == 0 { break } else { n <<= 7 } } if len(oid) == 0 { // first two digits are packed together n1 := n / 40 n2 := n - n1*40 oid = append(oid, n1, n2) } else { oid = append(oid, n) } } d.out.WriteString(oid.String()) } func (d *reqDumper) timestamp() { if len(d.buf) < 8 { d.err = io.ErrUnexpectedEOF return } n := binary.BigEndian.Uint64(d.buf) d.buf = d.buf[8:] epoch := int64(n) + time.Date(2001, 1, 1, 0, 0, 0, 0, time.UTC).Unix() t := time.Unix(epoch, 0).UTC() d.out.Write(t.AppendFormat(nil, "<2006-01-02 15:04:05Z>")) } type opCode uint32 // requirement.h const ( opFalse = iota opTrue opIdent opAppleAnchor opAnchorHash opInfoKeyValue opAnd opOr opCDHash opNot opInfoKeyField opCertField opTrustedCert opTrustedCerts opCertGeneric opAppleGenericAnchor opEntitlementField opCertPolicy opNamedAnchor opNamedCode opPlatform opNotarized opCertFieldDate opLegacyDevID opFlagMask = 0xff000000 opGenericFalse = 0x80000000 opGenericSkip = 0x40000000 ) type matchOp uint32 const ( matchExists matchOp = iota matchEqual matchContains matchBeginsWith matchEndsWith matchLessThan matchGreaterThan matchLessEqual matchGreaterEqual matchOn matchBefore matchAfter matchOnOrBefore matchOnOrAfter matchAbsent ) type syntaxLevel int const ( levelPrimary = iota levelAnd levelOr levelTop ) relic-7.6.1/lib/fruit/csblob/sign.go000066400000000000000000000165731455105530300172720ustar00rootroot00000000000000package csblob import ( "context" "crypto" "encoding/binary" "errors" "fmt" "io" "io/ioutil" "time" "howett.net/plist" "github.com/sassoftware/relic/v7/lib/certloader" "github.com/sassoftware/relic/v7/lib/pkcs7" "github.com/sassoftware/relic/v7/lib/pkcs9" "github.com/sassoftware/relic/v7/lib/x509tools" ) type SignatureParams struct { Pages io.Reader // read page contents OldSignature io.Reader // read the existing signature, if any, after the pages HashFunc crypto.Hash InfoPlist []byte // manifest to bind to signature Resources []byte // CodeResources to bind to signature // the following are copied from the old signature if empty Flags SignatureFlags Requirements []byte // requirements to embed in signature Entitlement []byte // entitlement to embed in signature EntitlementDER []byte // entitlement in DER format RepSpecific []byte // DMG header SigningIdentity string // bundle ID TeamIdentifier string // team ID from signing cert (set automatically if empty) ExecSegmentBase int64 ExecSegmentLimit int64 ExecSegmentFlags int64 } func (p *SignatureParams) hashFuncs() []crypto.Hash { return []crypto.Hash{p.HashFunc} } func (p *SignatureParams) DefaultsFromSignature() error { // parse old signature if p.OldSignature == nil { return nil } blob, err := ioutil.ReadAll(p.OldSignature) if err != nil { return err } oldSig, err := parseSignature(blob) if err != nil { return err } // copy embedded items if p.Entitlement == nil && oldSig.Entitlement != nil { p.Entitlement = oldSig.Entitlement[8:] if p.EntitlementDER == nil && oldSig.EntitlementDER != nil { // copy DER only if xml entitlements were also not set p.EntitlementDER = oldSig.EntitlementDER[8:] } } // copy code dir fields oldDir := oldSig.bestDir() if oldDir == nil { return nil } if p.Flags == 0 { p.Flags = oldDir.Header.Flags &^ clearFlags } if p.ExecSegmentBase == 0 && p.ExecSegmentLimit == 0 && p.ExecSegmentFlags == 0 { p.ExecSegmentBase = oldDir.Header.ExecSegmentBase p.ExecSegmentLimit = oldDir.Header.ExecSegmentLimit p.ExecSegmentFlags = oldDir.Header.ExecSegmentFlags } return nil } func (p *SignatureParams) DefaultsFromBundle(cert *certloader.Certificate) error { if p.SigningIdentity == "" && len(p.InfoPlist) != 0 { var bundle bundlePlist if _, err := plist.Unmarshal(p.InfoPlist, &bundle); err != nil { return fmt.Errorf("info.plist: %w", err) } p.SigningIdentity = bundle.BundleID } if p.TeamIdentifier == "" { p.TeamIdentifier = TeamID(cert.Leaf) } if p.Requirements == nil { req, err := DefaultRequirement(p.SigningIdentity, cert.Chain()) if err != nil { return fmt.Errorf("computing default designated requirement: %w", err) } p.Requirements = req } return nil } const defaultPageSizeLog2 = 12 func Sign(ctx context.Context, cert *certloader.Certificate, params *SignatureParams) ([]byte, *pkcs9.TimestampedSignature, error) { // hash code pages hashFuncs := params.hashFuncs() singlePage := params.RepSpecific != nil // DMG codeSlots, slotCount, codeLimit, err := hashPages(hashFuncs, params.Pages, singlePage) if err != nil { return nil, nil, fmt.Errorf("hashing code pages: %w", err) } // read the old signature to extract entitlements etc. if err := params.DefaultsFromSignature(); err != nil { return nil, nil, fmt.Errorf("parsing old signature: %w", err) } if err := params.DefaultsFromBundle(cert); err != nil { return nil, nil, fmt.Errorf("setting signature params: %w", err) } // build list of special slots to hash. for these the hash covers the blob // header as well so they have to be marshalled here var entBlob, entDERBlob, reqBlob []byte var hashedItems []superItem if params.Requirements != nil { v := params.Requirements if len(v) < 8 { return nil, nil, errors.New("requirements blob must be a binary requirement or requirement set") } switch csMagic(binary.BigEndian.Uint32(v)) { case csRequirements: // already a set of requirements case csRequirement: // assume single requirement is the DR j := newSuperItem(csRequirement, v[8:]) j.itype = uint32(DesignatedRequirement) v = marshalSuperBlob(csRequirements, []superItem{j}) default: return nil, nil, errors.New("requirements blob must be a binary requirement or requirement set") } // strip off the superblob header and let marshalSuperBlob put it back on i := newSuperItem(csRequirements, v[8:]) reqBlob = i.data hashedItems = append(hashedItems, i) } if params.Entitlement != nil { i := newSuperItem(csEntitlement, params.Entitlement) entBlob = i.data hashedItems = append(hashedItems, i) } if params.EntitlementDER != nil { i := newSuperItem(csEntitlementDER, params.EntitlementDER) entDERBlob = i.data hashedItems = append(hashedItems, i) } specials := [][]byte{ entDERBlob, // -7 entitlements DER params.RepSpecific, // -6 DMG entBlob, // -5 entitlements nil, // -4 app-specific params.Resources, // -3 resource manifest reqBlob, // -2 requirements params.InfoPlist, // -1 info manifest } for specials[0] == nil && len(specials) > 5 { specials = specials[1:] } var plistHashes cdHashPlist var attrHashes []cdHashAttrib var firstCD []byte var items []superItem for i, hashFunc := range hashFuncs { cdParams := codeDirParams{ SignatureParams: params, Specials: specials, CodeSlots: codeSlots[i], CodeSlotCount: slotCount, CodeLimit: codeLimit, HashFunc: hashFunc, SinglePage: singlePage, } result, err := newCodeDirectory(cdParams) if err != nil { return nil, nil, fmt.Errorf("populating code directory: %w", err) } alg, ok := x509tools.PkixDigestAlgorithm(hashFunc) if !ok { return nil, nil, fmt.Errorf("unsupported algorithm %s", hashFunc) } attrHashes = append(attrHashes, cdHashAttrib{ Algorithm: alg.Algorithm, Digest: result.Digest, }) plistHashes.CDHashes = append(plistHashes.CDHashes, result.Digest[:20]) item := superItem{magic: csCodeDirectory, data: result.Raw} if i == 0 { firstCD = item.data item.itype = cdCodeDirectorySlot } else { item.itype = uint32(cdAlternateCodeDirectorySlots + i - 1) } items = append(items, item) } items = append(items, hashedItems...) // sign builder := pkcs7.NewBuilder(cert.Signer(), cert.Chain(), params.HashFunc) if err := builder.SetContentData(firstCD); err != nil { return nil, nil, err } if err := addCSHashes(builder, attrHashes); err != nil { return nil, nil, fmt.Errorf("adding cdhashes: %w", err) } if err := addPlistHashes(builder, plistHashes); err != nil { return nil, nil, fmt.Errorf("adding cdhash plist: %w", err) } if err := builder.AddAuthenticatedAttribute(pkcs7.OidAttributeSigningTime, time.Now().UTC()); err != nil { return nil, nil, err } psd, err := builder.Sign() if err != nil { return nil, nil, err } tssig, err := pkcs9.TimestampAndMarshal(ctx, psd, cert.Timestamper, false) if err != nil { return nil, nil, err } // marshal if _, err := psd.Detach(); err != nil { return nil, nil, err } tssig.Raw, err = psd.Marshal() if err != nil { return nil, nil, err } items = append(items, newSuperItem(csBlobWrapper, tssig.Raw)) blob := marshalSuperBlob(csEmbeddedSignature, items) return blob, tssig, err } type bundlePlist struct { Executable string `plist:"CFBundleExecutable"` BundleID string `plist:"CFBundleIdentifier"` } relic-7.6.1/lib/fruit/csblob/superblob.go000066400000000000000000000060431455105530300203160ustar00rootroot00000000000000package csblob import ( "bytes" "encoding/binary" "errors" ) type csMagic uint32 const ( csEmbeddedSignature csMagic = 0xfade0cc0 csDetachedSignature csMagic = 0xfade0cc1 csRequirement csMagic = 0xfade0c00 csRequirements csMagic = 0xfade0c01 csCodeDirectory csMagic = 0xfade0c02 csEntitlement csMagic = 0xfade7171 csEntitlementDER csMagic = 0xfade7172 csBlobWrapper csMagic = 0xfade0b01 ) // codedirectory.h var csItypes = map[csMagic]uint32{ csRequirements: 0x2, csEntitlement: 0x5, csEntitlementDER: 0x7, csBlobWrapper: 0x10000, } // codedirectory.h //nolint:deadcode,varcheck // for doc purposes const ( cdInfoSlot = 1 cdRequirementsSlot = 2 cdResourceDirSlot = 3 cdTopDirectorySlot = 4 cdEntitlementSlot = 5 cdRepSpecificSlot = 6 cdEntitlementDERSlot = 7 cdCodeDirectorySlot = 0 cdAlternateCodeDirectorySlots = 0x1000 cdSignatureSlot = 0x10000 cdIdentificationSlot = 0x10001 cdTicketSlot = 0x10002 ) type superItem struct { magic csMagic itype uint32 data []byte } func parseSuper(blob []byte) (magic csMagic, items []superItem, err error) { if len(blob) < 12 { return 0, nil, errShort } origLen := len(blob) // read magic magic = csMagic(binary.BigEndian.Uint32(blob)) length := binary.BigEndian.Uint32(blob[4:]) count := int(binary.BigEndian.Uint32(blob[8:])) if length < 8 || length > uint32(len(blob)) { return 0, nil, errors.New("invalid length in signature blob") } blob = blob[12:] // read indexes if len(blob) < 8*count { return 0, nil, errShort } indexes, blob := blob[:8*count], blob[8*count:] dataOffset := origLen - len(blob) for i := 0; i < count; i++ { itype := binary.BigEndian.Uint32(indexes[8*i:]) offset := int(binary.BigEndian.Uint32(indexes[4+8*i:])) offset -= dataOffset if offset > len(blob)-8 { return 0, nil, errShort } length := int(binary.BigEndian.Uint32(blob[offset+4:])) if offset+length > len(blob) { return 0, nil, errShort } items = append(items, superItem{ magic: csMagic(binary.BigEndian.Uint32(blob[offset:])), itype: itype, data: blob[offset : offset+length], }) } return magic, items, nil } func newSuperItem(magic csMagic, payload []byte) superItem { packed := make([]byte, 8+len(payload)) binary.BigEndian.PutUint32(packed, uint32(magic)) binary.BigEndian.PutUint32(packed[4:], uint32(len(payload)+8)) copy(packed[8:], payload) return superItem{ magic: magic, itype: csItypes[magic], data: packed, } } func marshalSuperBlob(magic csMagic, items []superItem) []byte { ints := make([]uint32, 3+2*len(items)) ints[0] = uint32(magic) length := uint32(4 * len(ints)) ints[2] = uint32(len(items)) for i, item := range items { ints[3+2*i] = item.itype ints[4+2*i] = length length += uint32(len(item.data)) } ints[1] = length b := bytes.NewBuffer(make([]byte, 0, length)) _ = binary.Write(b, binary.BigEndian, ints) for _, item := range items { b.Write(item.data) } return b.Bytes() } var errShort = errors.New("short read in signature blob") relic-7.6.1/lib/fruit/csblob/verify.go000066400000000000000000000115131455105530300176230ustar00rootroot00000000000000package csblob import ( "crypto" "crypto/hmac" "errors" "fmt" "hash" "io" "github.com/sassoftware/relic/v7/lib/pkcs9" ) type VerifiedBlob struct { Blob *SigBlob Signature *pkcs9.TimestampedSignature HashFunc crypto.Hash } type VerifyParams struct { InfoPlist []byte Resources []byte RepSpecific []byte } func Verify(blob []byte, params VerifyParams) (*VerifiedBlob, error) { sig, err := parseSignature(blob) if err != nil { return nil, err } // verify hashes in each code directory computedHashes := make(map[crypto.Hash][]byte) var hashFunc crypto.Hash for _, dir := range sig.Directories { hashFunc = dir.HashFunc h := dir.HashFunc.New() h.Write(dir.Raw) computedHashes[dir.HashFunc] = h.Sum(nil) if dir.EntitlementsDERHash != nil { if err := hashCheck(h, sig.EntitlementDER, dir.EntitlementsDERHash); err != nil { return nil, fmt.Errorf("entitlementsDER: %w", err) } } if dir.EntitlementsHash != nil { if err := hashCheck(h, sig.Entitlement, dir.EntitlementsHash); err != nil { return nil, fmt.Errorf("entitlements: %w", err) } } if dir.RequirementsHash != nil { if err := hashCheck(h, sig.RawRequirements, dir.RequirementsHash); err != nil { return nil, fmt.Errorf("requirements: %w", err) } } if dir.RepSpecificHash != nil { if err := hashCheck(h, params.RepSpecific, dir.RepSpecificHash); err != nil { return nil, fmt.Errorf("rep_specific: %w", err) } } if dir.ManifestHash != nil && params.InfoPlist != nil { if err := hashCheck(h, params.InfoPlist, dir.ManifestHash); err != nil { return nil, fmt.Errorf("info_plist: %w", err) } } if dir.ResourcesHash != nil && params.Resources != nil { if err := hashCheck(h, params.Resources, dir.ResourcesHash); err != nil { return nil, fmt.Errorf("resources: %w", err) } } } // verify CMS signature against the first code dir mdContent := sig.Directories[0].Raw if sig.CMS == nil { return nil, errors.New("signature wrapper not found, possibly an adhoc signature") } pksig, err := sig.CMS.Content.Verify(mdContent, false) if err != nil { return nil, err } // verify all code dirs using a propretiary attribute if present if err := checkCDHashes(pksig.SignerInfo, computedHashes); err != nil { return nil, fmt.Errorf("verifying cd hashes: %w", err) } // verify using plist attribute if present -- unnecessary but useful for documentation if err := checkPlistHashes(sig.Directories, pksig.SignerInfo, computedHashes); err != nil { return nil, fmt.Errorf("verifying cd hashes: plist: %w", err) } // mark proprietary certificate extensions as handled so it doesn't fail the chain for _, cert := range pksig.Intermediates { MarkHandledExtensions(cert) } // validate timestamp token ts, err := pkcs9.VerifyOptionalTimestamp(pksig) if err != nil { return nil, err } return &VerifiedBlob{ Blob: sig, Signature: &ts, HashFunc: hashFunc, }, nil } func hashCheck(h hash.Hash, blob, expected []byte) error { h.Reset() h.Write(blob) actual := h.Sum(nil) if hmac.Equal(actual, expected) { return nil } return fmt.Errorf("digest mismatch: expected %x but got %x", expected, actual) } func (s *SigBlob) bestDir() *CodeDirectory { var dir *CodeDirectory for _, dir2 := range s.Directories { if dir == nil || dir2.Header.HashType > dir.Header.HashType { dir = dir2 } } return dir } func (s *SigBlob) CodeSize() int64 { dir := s.bestDir() if dir == nil { return 0 } if dir.Header.CodeLimit64 != 0 { return dir.Header.CodeLimit64 } return int64(dir.Header.CodeLimit) } func (s *SigBlob) VerifyPages(r io.Reader) error { dir := s.bestDir() if dir == nil { return errors.New("no valid code dir found") } remaining := s.CodeSize() if dir.Header.PageSizeLog2 == 0 { // single page for DMG bundles if len(dir.CodeHashes) != 1 { return fmt.Errorf("expected 1 hash slot but found %d", len(dir.CodeHashes)) } h := dir.HashFunc.New() n, err := io.Copy(h, r) if err != nil { return err } else if n != remaining { return fmt.Errorf("expected code size of %d but got %d", remaining, n) } computed := h.Sum(nil) if !hmac.Equal(computed, dir.CodeHashes[0]) { return fmt.Errorf("digest mismatch: expected %x, got %x", dir.CodeHashes[0], computed) } return nil } pageSize := int64(1 << dir.Header.PageSizeLog2) page := make([]byte, pageSize) h := dir.HashFunc.New() for i, expected := range dir.CodeHashes { if remaining <= 0 { return errors.New("not enough hash slots to cover indicated size") } else if remaining < pageSize { page = page[:remaining] } if _, err := io.ReadFull(r, page); err != nil { return err } h.Reset() h.Write(page) computed := h.Sum(nil) if !hmac.Equal(computed, expected) { return fmt.Errorf("digest mismatch: page %d: expected %x, got %x", i, expected, computed) } remaining -= int64(len(page)) } return nil } relic-7.6.1/lib/fruit/dmg/000077500000000000000000000000001455105530300152725ustar00rootroot00000000000000relic-7.6.1/lib/fruit/dmg/dmg.go000066400000000000000000000035061455105530300163740ustar00rootroot00000000000000package dmg import ( "bytes" "encoding/binary" "errors" "fmt" "io" "os" ) type DMG struct { r io.ReaderAt udifOffset int64 sigBlob []byte rsf udifResourceFile } func Open(f *os.File) (*DMG, error) { // read and parse UDIF header udifOffset, err := f.Seek(-512, io.SeekEnd) if err != nil { return nil, err } d := &DMG{r: f, udifOffset: udifOffset} if err := binary.Read(f, binary.BigEndian, &d.rsf); err != nil { return nil, err } if d.rsf.Signature != udifSignature { return nil, errors.New("dmg file magic not found") } // read and parse signature if d.rsf.SignatureLength != 0 { if d.rsf.SignatureLength > 10e6 { return nil, fmt.Errorf("unreasonably large dmg signature of %d bytes", d.rsf.SignatureLength) } d.sigBlob = make([]byte, d.rsf.SignatureLength) if _, err := f.ReadAt(d.sigBlob, d.rsf.SignatureOffset); err != nil { return nil, err } } return d, nil } const udifSignature = 0x6B6F6C79 // koly type udifFlags uint32 type udifResourceFile struct { Signature uint32 Version uint32 HeaderSize uint32 Flags udifFlags RunningDataForkOffset int64 DataForkOffset int64 DataForkLength int64 ResourceForkOffset int64 ResourceForkLength int64 SegmentNumber uint32 SegmentCount uint32 SegmentID [4]uint32 DataForkChecksum udifChecksum XMLOffset int64 XMLLength int64 _ [64]byte SignatureOffset int64 SignatureLength int64 _ [40]byte MasterChecksum udifChecksum ImageVariant uint32 SectorCount int64 _ [3]uint32 } // serialize with zeroed signature length for hashing func (rsf udifResourceFile) ForHashing() []byte { rsf.SignatureLength = 0 var b bytes.Buffer _ = binary.Write(&b, binary.BigEndian, rsf) return b.Bytes() } type udifChecksum struct { Type uint32 Size uint32 Data [32]uint32 // TODO } relic-7.6.1/lib/fruit/dmg/sign.go000066400000000000000000000045221455105530300165640ustar00rootroot00000000000000package dmg import ( "bytes" "context" "crypto" "encoding/binary" "errors" "fmt" "io" "github.com/sassoftware/relic/v7/lib/binpatch" "github.com/sassoftware/relic/v7/lib/certloader" "github.com/sassoftware/relic/v7/lib/fruit/csblob" "github.com/sassoftware/relic/v7/lib/pkcs9" ) type SignatureParams struct { HashFunc crypto.Hash Requirements []byte // requirements to embed in signature SigningIdentity string TeamIdentifier string } func Sign(ctx context.Context, rsfBytes []byte, r io.Reader, cert *certloader.Certificate, params *SignatureParams) (*binpatch.PatchSet, *pkcs9.TimestampedSignature, error) { // parse UDIF header var rsf udifResourceFile if err := binary.Read(bytes.NewReader(rsfBytes), binary.BigEndian, &rsf); err != nil { return nil, nil, fmt.Errorf("udif header: %w", err) } nr := &counter{r: r} bundleSize := rsf.XMLOffset + rsf.XMLLength oldOffset, oldLength := rsf.SignatureOffset, rsf.SignatureLength rsf.SignatureOffset = bundleSize blobParams := &csblob.SignatureParams{ HashFunc: params.HashFunc, Requirements: params.Requirements, SigningIdentity: params.SigningIdentity, TeamIdentifier: params.TeamIdentifier, Pages: io.LimitReader(nr, bundleSize), RepSpecific: rsf.ForHashing(), } if oldOffset != 0 { // provide old signature to copy requirements and flags if oldOffset != bundleSize { return nil, nil, errors.New("overlap or gap between bundle and signature") } blobParams.OldSignature = io.LimitReader(nr, oldLength) } blob, tsig, err := csblob.Sign(ctx, cert, blobParams) if err != nil { return nil, nil, err } // drain rest of input file if _, err := io.Copy(io.Discard, nr); err != nil { return nil, nil, err } oldSize := nr.n // generate patch rsf.SignatureLength = int64(len(blob)) var b bytes.Buffer _, _ = b.Write(blob) _ = binary.Write(&b, binary.BigEndian, rsf) patch := binpatch.New() patch.Add(rsf.SignatureOffset, oldSize-rsf.SignatureOffset, b.Bytes()) // copy values back to params params.Requirements = blobParams.Requirements params.SigningIdentity = blobParams.SigningIdentity params.TeamIdentifier = blobParams.TeamIdentifier return patch, tsig, nil } type counter struct { r io.Reader n int64 } func (c *counter) Read(d []byte) (n int, err error) { n, err = c.r.Read(d) if n > 0 { c.n += int64(n) } return } relic-7.6.1/lib/fruit/dmg/verify.go000066400000000000000000000013101455105530300171200ustar00rootroot00000000000000package dmg import ( "fmt" "io" "github.com/sassoftware/relic/v7/lib/fruit/csblob" "github.com/sassoftware/relic/v7/signers/sigerrors" ) type Signature struct { *csblob.VerifiedBlob } func (d *DMG) Verify(skipDigests bool) (*Signature, error) { if len(d.sigBlob) == 0 { return nil, sigerrors.NotSignedError{Type: "dmg"} } sig, err := csblob.Verify(d.sigBlob, csblob.VerifyParams{RepSpecific: d.rsf.ForHashing()}) if err != nil { return nil, fmt.Errorf("dmg signature: %w", err) } if !skipDigests { page := io.NewSectionReader(d.r, 0, d.rsf.XMLOffset+d.rsf.XMLLength) if err := sig.Blob.VerifyPages(page); err != nil { return nil, err } } return &Signature{VerifiedBlob: sig}, nil } relic-7.6.1/lib/fruit/machos/000077500000000000000000000000001455105530300157755ustar00rootroot00000000000000relic-7.6.1/lib/fruit/machos/header.go000066400000000000000000000175641455105530300175710ustar00rootroot00000000000000package machos import ( "bytes" "debug/macho" "encoding/binary" "errors" "io" "github.com/sassoftware/relic/v7/lib/binpatch" ) const ( loadCmdCodeSignature macho.LoadCmd = 0x1d segLinkEdit = "__LINKEDIT" alignSegmentFile = 8 alignSegmentMem = 4096 ) type machoMarkers struct { macho.FileHeader ByteOrder binary.ByteOrder Magic uint32 // start and length of existing signature sigStart int64 sigLen int64 // position of signature loadcmd if present loadCsStart int64 // position of __LINKEDIT segment header linkEditHdrPos int64 // original __LINKEDIT segment header linkEditHdr macho.SegmentHeader // end of load commands nextLc int64 // start of first section firstSh int64 // size of image without signature codeSize int64 } type codeSigCmd struct { Cmd macho.LoadCmd Len uint32 SigOffset uint32 SigLength uint32 } // scan the header of a mach-o binary, taking note of important offsets in the // header so that it can be patched func scanFile(r io.Reader) (*machoMarkers, error) { f := new(machoMarkers) // Read and decode Mach magic to determine byte order, size. // Magic32 and Magic64 differ only in the bottom bit. var ident [4]byte if _, err := io.ReadFull(r, ident[0:]); err != nil { return nil, err } be := binary.BigEndian.Uint32(ident[0:]) le := binary.LittleEndian.Uint32(ident[0:]) switch macho.Magic32 &^ 1 { case be &^ 1: f.ByteOrder = binary.BigEndian f.Magic = be case le &^ 1: f.ByteOrder = binary.LittleEndian f.Magic = le default: return nil, errors.New("invalid magic number") } // Read entire file header. rr := io.MultiReader(bytes.NewReader(ident[:]), r) if err := binary.Read(rr, f.ByteOrder, &f.FileHeader); err != nil { return nil, err } endOfHeader := binary.Size(f.FileHeader) if f.Magic == macho.Magic64 { // discard reserved field _, _ = io.ReadFull(r, ident[:]) endOfHeader += 4 } dat := make([]byte, f.Cmdsz) if _, err := io.ReadFull(r, dat); err != nil { return nil, err } endOfHeader += len(dat) f.nextLc = int64(endOfHeader) f.firstSh = 1<<63 - 1 bo := f.ByteOrder for i := 0; i < int(f.Ncmd); i++ { // Each load command begins with uint32 command and length. if len(dat) < 8 { return nil, errors.New("command block too small") } cmd, siz := macho.LoadCmd(bo.Uint32(dat[0:4])), bo.Uint32(dat[4:8]) if siz < 8 || siz > uint32(len(dat)) { return nil, errors.New("invalid command block size") } cmdPos := int64(endOfHeader - len(dat)) var cmddat []byte cmddat, dat = dat[0:siz], dat[siz:] switch cmd { case macho.LoadCmdSegment: var seg macho.Segment32 b := bytes.NewReader(cmddat) if err := binary.Read(b, bo, &seg); err != nil { return nil, err } if cstring(seg.Name[:]) == segLinkEdit { f.linkEditHdrPos = cmdPos f.linkEditHdr = macho.SegmentHeader{ Addr: uint64(seg.Addr), Memsz: uint64(seg.Memsz), Offset: uint64(seg.Offset), Filesz: uint64(seg.Filesz), } } for i := 0; i < int(seg.Nsect); i++ { var sh macho.Section32 if err := binary.Read(b, bo, &sh); err != nil { return nil, err } if sh.Size != 0 && sh.Offset != 0 && int64(sh.Offset) < f.firstSh { f.firstSh = int64(sh.Offset) } } case macho.LoadCmdSegment64: var seg macho.Segment64 b := bytes.NewReader(cmddat) if err := binary.Read(b, bo, &seg); err != nil { return nil, err } if cstring(seg.Name[:]) == segLinkEdit { f.linkEditHdrPos = cmdPos f.linkEditHdr = macho.SegmentHeader{ Addr: seg.Addr, Memsz: seg.Memsz, Offset: seg.Offset, Filesz: seg.Filesz, } } for i := 0; i < int(seg.Nsect); i++ { var sh macho.Section64 if err := binary.Read(b, bo, &sh); err != nil { return nil, err } if seg.Filesz != 0 && sh.Size != 0 && sh.Offset != 0 && int64(sh.Offset) < f.firstSh { f.firstSh = int64(sh.Offset) } } // TODO: look for embedded plist case loadCmdCodeSignature: var sig codeSigCmd b := bytes.NewReader(cmddat) if err := binary.Read(b, bo, &sig); err != nil { return nil, err } f.sigStart = int64(sig.SigOffset) f.sigLen = int64(sig.SigLength) f.loadCsStart = cmdPos } } linkEditEnd := int64(f.linkEditHdr.Offset) + int64(f.linkEditHdr.Filesz) if f.sigLen != 0 { f.codeSize = f.sigStart sigEnd := f.sigStart + f.sigLen if sigEnd > linkEditEnd || sigEnd < linkEditEnd-16 { return nil, errors.New("old signature is not coterminous with __LINKEDIT segment") } } else { f.codeSize = linkEditEnd } return f, nil } func (f *machoMarkers) PatchSignature(oldHeader []byte, sigSize int64) (newHeader, sigBuf []byte, sigStart int64, patch *binpatch.PatchSet, padding int64, err error) { patch = binpatch.New() newHeader = oldHeader sigStart = f.sigStart if f.sigLen >= sigSize { // existing block is big enough, overwrite it sigBuf = make([]byte, f.sigLen) patch.Add(f.sigStart, f.sigLen, sigBuf) return } sigSize = align(sigSize, alignSegmentFile) if sigStart == 0 { // place signature after the current end of __LINKEDIT sigStart = align(f.codeSize, alignSegmentFile) } // allocate patch buffer for signature padding = sigStart - f.codeSize padded := make([]byte, padding+sigSize) sigBuf = padded[padding:] // make room for signature loadcmd if there isn't one already newHeader, err = f.patchNcmd(newHeader, patch) if err != nil { return } // update __LINKEDIT bounds f.patchLinkEdit(newHeader, patch, sigStart, sigSize) // write signature loadcmd f.patchLoadCmd(newHeader, patch, sigStart, sigSize) // record patch now, buffer to be filled by caller patch.Add(f.codeSize, f.sigLen, padded) return } // make room for a new loadcmd func (f *machoMarkers) patchNcmd(newHeader []byte, patch *binpatch.PatchSet) ([]byte, error) { if f.loadCsStart != 0 { return newHeader, nil } f.loadCsStart = f.nextLc loadCsEnd := f.nextLc + 16 if loadCsEnd > f.firstSh { return nil, errors.New("mach-o loader cmd for signature would overflow next section") } if int64(len(newHeader)) < loadCsEnd { // extend buffer buf := make([]byte, loadCsEnd) copy(buf, newHeader) newHeader = buf } // update Ncmd f.ByteOrder.PutUint32(newHeader[16:], f.ByteOrder.Uint32(newHeader[16:])+1) // update Cmdsz f.ByteOrder.PutUint32(newHeader[20:], f.ByteOrder.Uint32(newHeader[20:])+16) patch.Add(16, 8, newHeader[16:16+8]) return newHeader, nil } // write loadcmd for signature func (f *machoMarkers) patchLoadCmd(newHeader []byte, patch *binpatch.PatchSet, sigStart, sigSize int64) { f.ByteOrder.PutUint32(newHeader[f.loadCsStart:], uint32(loadCmdCodeSignature)) f.ByteOrder.PutUint32(newHeader[f.loadCsStart+4:], 16) f.ByteOrder.PutUint32(newHeader[f.loadCsStart+8:], uint32(sigStart)) f.ByteOrder.PutUint32(newHeader[f.loadCsStart+12:], uint32(sigSize)) patch.Add(f.loadCsStart, 16, newHeader[f.loadCsStart:f.loadCsStart+16]) } func (f *machoMarkers) patchLinkEdit(newHeader []byte, patch *binpatch.PatchSet, sigStart, sigSize int64) { hdr := f.linkEditHdr end := uint64(sigStart + sigSize) hdr.Filesz = end - hdr.Offset hdr.Memsz = uint64(align(int64(end-hdr.Offset), alignSegmentMem)) var patchStart, patchSize int64 if f.Magic == macho.Magic64 { // update Memsz f.ByteOrder.PutUint64(newHeader[f.linkEditHdrPos+32:], hdr.Memsz) // update Filesz f.ByteOrder.PutUint64(newHeader[f.linkEditHdrPos+48:], hdr.Filesz) patchStart, patchSize = f.linkEditHdrPos+32, 24 } else { // update Memsz f.ByteOrder.PutUint32(newHeader[f.linkEditHdrPos+28:], uint32(hdr.Memsz)) // update Filesz f.ByteOrder.PutUint32(newHeader[f.linkEditHdrPos+36:], uint32(hdr.Filesz)) patchStart, patchSize = f.linkEditHdrPos+28, 12 } patch.Add(patchStart, patchSize, newHeader[patchStart:patchStart+patchSize]) } func cstring(b []byte) string { i := bytes.IndexByte(b, 0) if i == -1 { i = len(b) } return string(b[0:i]) } func align(addr, align int64) int64 { n := addr % align if n != 0 { addr += align - n } return addr } relic-7.6.1/lib/fruit/machos/sign.go000066400000000000000000000040301455105530300172610ustar00rootroot00000000000000package machos import ( "bytes" "context" "fmt" "io" "io/ioutil" "github.com/sassoftware/relic/v7/lib/binpatch" "github.com/sassoftware/relic/v7/lib/certloader" "github.com/sassoftware/relic/v7/lib/fruit/csblob" "github.com/sassoftware/relic/v7/lib/pkcs9" ) func Sign(ctx context.Context, r io.Reader, cert *certloader.Certificate, params *csblob.SignatureParams) (*binpatch.PatchSet, *pkcs9.TimestampedSignature, error) { var saved bytes.Buffer tee := io.TeeReader(r, &saved) markers, err := scanFile(tee) if err != nil { return nil, nil, err } headerBuf := saved.Bytes() // estimate size of signature estimatedSize := markers.codeSize * int64(20+params.HashFunc.Size()) / 4096 estimatedSize += int64(len(params.Entitlement) + len(params.Requirements)) estimatedSize += 16384 // patch header to make space oldHeaderSize := len(headerBuf) headerBuf, sigBuf, sigStart, patch, padding, err := markers.PatchSignature(headerBuf, estimatedSize) if err != nil { return nil, nil, err } // if PatchSignature extended the header then the bytes that its extension // replaced haven't been read yet, so discard them now if extended := len(headerBuf) - oldHeaderSize; extended > 0 { if _, err := io.ReadFull(r, make([]byte, extended)); err != nil { return nil, nil, err } } // splice the patched header with the rest of the stream params.Pages = io.LimitReader(io.MultiReader(bytes.NewReader(headerBuf), r, bytes.NewReader(make([]byte, padding))), sigStart) if markers.sigLen != 0 { // read the old signature after the pages are hashed params.OldSignature = io.LimitReader(r, markers.sigLen) } blob, tsig, err := csblob.Sign(ctx, cert, params) if err != nil { return nil, nil, err } // fill patch buffer with signature if len(blob) > len(sigBuf) { return nil, nil, fmt.Errorf("signature overflows reserved space: have %d bytes, need %d", len(sigBuf), len(blob)) } copy(sigBuf, blob) // discard remainder of stream if _, err := io.Copy(ioutil.Discard, r); err != nil { return nil, nil, err } return patch, tsig, nil } relic-7.6.1/lib/fruit/machos/verify.go000066400000000000000000000037421455105530300176360ustar00rootroot00000000000000package machos import ( "debug/macho" "fmt" "io" "github.com/sassoftware/relic/v7/lib/fruit/csblob" "github.com/sassoftware/relic/v7/signers/sigerrors" ) func Verify(r io.ReaderAt, infoPlist, resources []byte, skipDigests bool) (*csblob.VerifiedBlob, error) { hdr, err := macho.NewFile(r) if err != nil { return nil, err } buf, err := readSigBlob(r, hdr) if err != nil { return nil, err } if infoPlist == nil { // check if info.plist is embedded in the image infoPlist, err = readPlist(hdr) if err != nil { return nil, err } } sig, err := csblob.Verify(buf, csblob.VerifyParams{ InfoPlist: infoPlist, Resources: resources, }) if err != nil { return nil, fmt.Errorf("verifying mach-O signature: %w", err) } if !skipDigests { r := io.NewSectionReader(r, 0, sig.Blob.CodeSize()) if err := sig.Blob.VerifyPages(r); err != nil { return nil, err } } return sig, nil } func readSigBlob(r io.ReaderAt, hdr *macho.File) ([]byte, error) { for _, loadCmd := range hdr.Loads { raw := loadCmd.Raw() cmd := macho.LoadCmd(hdr.ByteOrder.Uint32(raw)) if cmd != loadCmdCodeSignature { continue } if len(raw) != 16 { return nil, fmt.Errorf("expected LC_CODE_SIGNATURE to be 16 bytes not %d bytes", len(raw)) } offset := int64(hdr.ByteOrder.Uint32(raw[8:])) length := int64(hdr.ByteOrder.Uint32(raw[12:])) if length > 10e6 { return nil, fmt.Errorf("unreasonably large LC_CODE_SIGNATURE of %d bytes", length) } buf := make([]byte, length) if _, err := r.ReadAt(buf, offset); err != nil { return nil, fmt.Errorf("reading LC_CODE_SIGNATURE: %w", err) } return buf, nil } return nil, sigerrors.NotSignedError{Type: "Mach-O"} } func readPlist(hdr *macho.File) ([]byte, error) { for _, sec := range hdr.Sections { if sec.Seg == "__TEXT" && sec.Name == "__info_plist" { infoPlist, err := sec.Data() if err != nil { return nil, fmt.Errorf("reading embedded info_plist: %w", err) } return infoPlist, nil } } return nil, nil } relic-7.6.1/lib/fruit/xar/000077500000000000000000000000001455105530300153155ustar00rootroot00000000000000relic-7.6.1/lib/fruit/xar/sign.go000066400000000000000000000164341455105530300166140ustar00rootroot00000000000000package xar import ( "bytes" "compress/zlib" "context" "crypto" "crypto/rand" "crypto/rsa" "crypto/x509" "encoding/base64" "encoding/binary" "errors" "fmt" "io" "strconv" "strings" "github.com/beevik/etree" "github.com/sassoftware/relic/v7/lib/binpatch" "github.com/sassoftware/relic/v7/lib/certloader" "github.com/sassoftware/relic/v7/lib/pkcs7" "github.com/sassoftware/relic/v7/lib/pkcs9" ) type SignatureParams struct { HashFunc crypto.Hash } func Sign(ctx context.Context, r io.Reader, cert *certloader.Certificate, hashType crypto.Hash) (*binpatch.PatchSet, *pkcs9.TimestampedSignature, error) { hdr, _, err := parseHeader(r) if err != nil { return nil, nil, err } else if hdr.CompressedSize > 1e6 || hdr.UncompressedSize > 10e6 { return nil, nil, errors.New("unreasonably large TOC") } // parse TOC and remove old signatures, tracking how much heap space they occupied doc, err := tocEtree(r, hdr.CompressedSize) if err != nil { return nil, nil, err } toc := doc.FindElement("/xar/toc") if toc == nil { return nil, nil, errors.New("missing xar/toc element") } origSigSize := removeSigs(toc) // reserve space for new signatures and insert elements into TOC newSigSize := reserveSignatures(toc, hashType, cert.Chain()) // verify and discard remaining input files heap := &streamReaderAt{r: r} if err := checkFiles(toc, heap); err != nil { return nil, nil, err } // move offsets of files in accordance with the change in signature size adjustOffsets(doc, newSigSize-origSigSize) // encode TOC ztocBytes, uncompSize, err := compress(doc) if err != nil { return nil, nil, err } // write new header and TOC newByteLen := 28 + len(ztocBytes) + int(newSigSize) newBytes := bytes.NewBuffer(make([]byte, 0, newByteLen)) tssig, err := appendSignatures(ctx, newBytes, ztocBytes, uncompSize, newSigSize, cert, hashType) if err != nil { return nil, nil, err } // patch signature into result origTotal := 28 + hdr.CompressedSize + origSigSize p := binpatch.New() p.Add(0, origTotal, newBytes.Bytes()) return p, tssig, nil } func tocEtree(r io.Reader, compressedSize int64) (*etree.Document, error) { origBytes, err := decompress(io.LimitReader(r, compressedSize)) if err != nil { return nil, err } doc := etree.NewDocument() if err := doc.ReadFromBytes(origBytes); err != nil { return nil, err } return doc, nil } // remove checksum and signatures and return the heap size they occupied func removeSigs(toc *etree.Element) (size int64) { for _, key := range []string{"checksum", "signature", "x-signature"} { for _, el := range toc.SelectElements(key) { se := el.SelectElement("size") if se != nil { n, _ := strconv.ParseInt(se.Text(), 10, 64) size += n } el.Parent().RemoveChild(el) } } return } // add space for new signatures and return the heap space required for them func reserveSignatures(toc *etree.Element, hashType crypto.Hash, certs []*x509.Certificate) (newSigSize int64) { hashName := strings.ReplaceAll(strings.ToLower(hashType.String()), "-", "") // encode cert chain var b strings.Builder certText := make([]string, len(certs)) for i, cert := range certs { buf := make([]byte, base64.StdEncoding.EncodedLen(len(cert.Raw))) base64.StdEncoding.Encode(buf, cert.Raw) b.Reset() for len(buf) > 72 { b.Write(buf[:72]) b.WriteByte('\n') buf = buf[72:] } b.Write(buf) certText[i] = b.String() } // reserve space for checksum cksumSize := int64(hashType.Size()) cksumEl := newSigElement("checksum", hashName, newSigSize, cksumSize, nil) toc.InsertChildAt(0, cksumEl) newSigSize += cksumSize added := 1 // reserve space for classic signature if n, ok := certs[0].PublicKey.(*rsa.PublicKey); ok { classicSize := int64(n.Size()) classicEl := newSigElement("signature", "RSA", newSigSize, classicSize, certText) toc.InsertChildAt(added, classicEl) newSigSize += classicSize added++ } // reserve space for CMS cmsSize := int64(6144) for _, cert := range certs { cmsSize += int64(len(cert.Raw)) } cmsEl := newSigElement("x-signature", "CMS", newSigSize, cmsSize, certText) toc.InsertChildAt(added, cmsEl) newSigSize += cmsSize return } // create one signature element func newSigElement(key, style string, offset, size int64, certs []string) *etree.Element { el := etree.NewElement(key) el.CreateAttr("style", style) se := etree.NewElement("size") se.SetText(strconv.FormatInt(size, 10)) el.AddChild(se) off := etree.NewElement("offset") off.SetText(strconv.FormatInt(offset, 10)) el.AddChild(off) if certs != nil { keyInfo := etree.NewElement("KeyInfo") keyInfo.CreateAttr("xmlns", "http://www.w3.org/2000/09/xmldsig#") x5d := etree.NewElement("X509Data") keyInfo.AddChild(x5d) for _, cert := range certs { x5c := etree.NewElement("X509Certificate") x5c.SetText(cert) x5d.AddChild(x5c) } el.AddChild(keyInfo) } return el } func adjustOffsets(doc *etree.Document, delta int64) { for _, offsetEl := range doc.FindElements("//data/offset") { offset, err := strconv.ParseInt(offsetEl.Text(), 10, 64) if err != nil { continue } offset += delta offsetEl.SetText(strconv.FormatInt(offset, 10)) } } func compress(doc *etree.Document) ([]byte, int64, error) { var b bytes.Buffer zw := zlib.NewWriter(&b) uncompSize, err := doc.WriteTo(zw) if err != nil { return nil, 0, err } if err := zw.Close(); err != nil { return nil, 0, err } return b.Bytes(), uncompSize, nil } func appendSignatures(ctx context.Context, out *bytes.Buffer, ztoc []byte, uncompSize, reservedSigSize int64, cert *certloader.Certificate, hashType crypto.Hash) (*pkcs9.TimestampedSignature, error) { // write file header hdr := fileHeader{ Magic: xarMagic, HeaderSize: 28, Version: 1, UncompressedSize: uncompSize, CompressedSize: int64(len(ztoc)), } switch hashType { case crypto.SHA1: hdr.HashType = hashSHA1 case crypto.SHA256: hdr.HashType = hashSHA256 case crypto.SHA512: hdr.HashType = hashSHA512 default: return nil, fmt.Errorf("unsupported hash type %s", hashType) } _ = binary.Write(out, binary.BigEndian, hdr) out.Write(ztoc) // write checksum var usedSigSize int64 d := hashType.New() d.Write(ztoc) ztocHash := d.Sum(nil) out.Write(ztocHash) usedSigSize += int64(len(ztocHash)) // classic signature if _, ok := cert.Leaf.PublicKey.(*rsa.PublicKey); ok { classicBytes, err := cert.Signer().Sign(rand.Reader, ztocHash, hashType) if err != nil { return nil, fmt.Errorf("signing xar TOC: %w", err) } out.Write(classicBytes) usedSigSize += int64(len(classicBytes)) } // CMS signature builder := pkcs7.NewBuilder(cert.Signer(), cert.Chain(), hashType) if err := builder.SetContentData(ztocHash); err != nil { return nil, err } psd, err := builder.Sign() if err != nil { return nil, err } tssig, err := pkcs9.TimestampAndMarshal(ctx, psd, cert.Timestamper, false) if err != nil { return nil, err } if _, err := psd.Detach(); err != nil { return nil, err } tssig.Raw, err = psd.Marshal() if err != nil { return nil, err } out.Write(tssig.Raw) usedSigSize += int64(len(tssig.Raw)) if usedSigSize > reservedSigSize { return nil, fmt.Errorf("signature overflows reserved space: have %d bytes, need %d", reservedSigSize, usedSigSize) } else { // pad out remaining space out.Write(make([]byte, reservedSigSize-usedSigSize)) } return tssig, nil } relic-7.6.1/lib/fruit/xar/structs.go000066400000000000000000000031061455105530300173530ustar00rootroot00000000000000package xar type fileHeader struct { Magic uint32 HeaderSize uint16 Version uint16 CompressedSize int64 UncompressedSize int64 HashType hashType } const xarMagic = 0x78617221 // xar! type hashType uint32 //nolint:deadcode,varcheck // for doc purposes const ( hashNone hashType = iota hashSHA1 hashMD5 hashSHA256 hashSHA512 ) type tocXar struct { TOC tocToc `xml:"toc"` } type tocToc struct { CreationTime string `xml:"creation-time"` Checksum tocChecksum `xml:"checksum"` Signature *tocSignature `xml:"signature"` XSignature *tocSignature `xml:"x-signature"` Files []*tocFile `xml:"file"` } type tocChecksum struct { Style string `xml:"style,attr"` Offset int64 `xml:"offset"` Size int64 `xml:"size"` } type tocSignature struct { Style string `xml:"style,attr"` Offset int64 `xml:"offset"` Size int64 `xml:"size"` Certificates []string `xml:"KeyInfo>X509Data>X509Certificate,omitempty"` } type tocFile struct { Name string `xml:"name"` Type string `xml:"type"` ArchivedChecksum tocFileSum `xml:"data>archived-checksum"` ExtractedChecksum tocFileSum `xml:"data>extracted-checksum"` Encoding tocEncoding `xml:"data>encoding"` Size int64 `xml:"data>size"` Offset int64 `xml:"data>offset"` Length int64 `xml:"data>length"` Files []*tocFile `xml:"file"` } type tocEncoding struct { Style string `xml:"style,attr"` } type tocFileSum struct { Style string `xml:"style,attr"` Digest string `xml:",chardata"` } relic-7.6.1/lib/fruit/xar/verify.go000066400000000000000000000120211455105530300171440ustar00rootroot00000000000000package xar import ( "crypto" "crypto/hmac" "crypto/sha1" "crypto/sha256" "crypto/sha512" "encoding/hex" "fmt" "hash" "io" "io/ioutil" "sort" "strconv" "github.com/beevik/etree" ber "github.com/go-asn1-ber/asn1-ber" "github.com/sassoftware/relic/v7/lib/fruit/csblob" "github.com/sassoftware/relic/v7/lib/pkcs7" "github.com/sassoftware/relic/v7/lib/pkcs9" "github.com/sassoftware/relic/v7/lib/x509tools" "github.com/sassoftware/relic/v7/signers/sigerrors" ) type Signature struct { HashFunc crypto.Hash Signature *pkcs9.TimestampedSignature NotaryTicket []byte } func (x *XAR) Verify(skipDigests bool) (*Signature, error) { var tsig pkcs9.TimestampedSignature if x.CMSSignature != nil { // repack BER as DER pkt, err := ber.DecodePacketErr(x.CMSSignature) if err != nil { return nil, err } der := pkt.Bytes() psd, err := pkcs7.Unmarshal(der) if err != nil { return nil, fmt.Errorf("reading CMS signature: %w", err) } sig, err := psd.Content.Verify(x.TOCHash, false) if err != nil { return nil, fmt.Errorf("verifying CMS signature: %w", err) } tsig, err = pkcs9.VerifyOptionalTimestamp(sig) if err != nil { return nil, err } } else if x.ClassicSignature != nil { pub := x.Certificates[0].PublicKey if err := x509tools.Verify(pub, x.HashFunc, x.TOCHash, x.ClassicSignature); err != nil { // try again with a hash of a hash, which seems to be found on some older packages d := x.HashFunc.New() d.Write(x.TOCHash) err2 := x509tools.Verify(pub, x.HashFunc, d.Sum(nil), x.ClassicSignature) if err2 != nil { // use original error return nil, fmt.Errorf("verifying RSA signature: %w", err) } } tsig = pkcs9.TimestampedSignature{ Signature: pkcs7.Signature{ Certificate: x.Certificates[0], Intermediates: x.Certificates[1:], }, } } else { return nil, sigerrors.NotSignedError{Type: "xar"} } if !skipDigests { if err := x.checkFiles(); err != nil { return nil, err } } // mark proprietary certificate extensions as handled so it doesn't fail the chain for _, cert := range tsig.Intermediates { csblob.MarkHandledExtensions(cert) } return &Signature{ HashFunc: x.HashFunc, Signature: &tsig, NotaryTicket: x.NotaryTicket, }, nil } func (x *XAR) checkFiles() error { // gather all files with checksums into a flat list and sort by offset var dataFiles []*tocFile gatherDataFiles(x.toc.Files, &dataFiles) sort.Slice(dataFiles, func(i, j int) bool { return dataFiles[i].Offset < dataFiles[j].Offset }) for _, f := range dataFiles { if err := checkFile(x.heap, f); err != nil { return fmt.Errorf("checksumming %q: %w", f.Name, err) } } return nil } func checkFiles(toc *etree.Element, heap io.ReaderAt) error { // gather all files with checksums into a flat list and sort by offset var dataFiles []*tocFile for _, ed := range toc.FindElements("//file/data") { ef := ed.Parent() ek := ed.SelectElement("archived-checksum") if ek == nil { continue } offset, _ := strconv.ParseInt(textOf(ed.SelectElement("offset")), 10, 64) length, _ := strconv.ParseInt(textOf(ed.SelectElement("length")), 10, 64) f := &tocFile{ Name: textOf(ef.SelectElement("name")), Offset: offset, Length: length, ArchivedChecksum: tocFileSum{ Style: ek.SelectAttrValue("style", ""), Digest: ek.Text(), }, } dataFiles = append(dataFiles, f) } sort.Slice(dataFiles, func(i, j int) bool { return dataFiles[i].Offset < dataFiles[j].Offset }) for _, f := range dataFiles { if err := checkFile(heap, f); err != nil { return fmt.Errorf("checksumming %q: %w", f.Name, err) } } return nil } func textOf(e *etree.Element) string { if e == nil { return "" } return e.Text() } func checkFile(heap io.ReaderAt, f *tocFile) error { var h hash.Hash switch f.ArchivedChecksum.Style { case "sha1": h = sha1.New() case "sha256": h = sha256.New() case "sha512": h = sha512.New() default: return fmt.Errorf("unsupported hash type %s", f.ArchivedChecksum.Style) } expected, err := hex.DecodeString(f.ArchivedChecksum.Digest) if err != nil { return err } r := io.NewSectionReader(heap, f.Offset, f.Length) if n, err := io.Copy(h, r); err != nil { return err } else if n != f.Length { return io.ErrUnexpectedEOF } calculated := h.Sum(nil) if !hmac.Equal(expected, calculated) { return fmt.Errorf("digest mismatch: expected %x but got %x", expected, calculated) } return nil } func gatherDataFiles(dirFiles []*tocFile, dataFiles *[]*tocFile) { for _, f := range dirFiles { if f.Length != 0 { *dataFiles = append(*dataFiles, f) } else if len(f.Files) != 0 { gatherDataFiles(f.Files, dataFiles) } } } type streamReaderAt struct { r io.Reader pos int64 } func (r *streamReaderAt) ReadAt(d []byte, p int64) (int, error) { if p > r.pos { if _, err := io.CopyN(ioutil.Discard, r.r, p-r.pos); err != nil { return 0, err } r.pos = p } else if p < r.pos { return 0, fmt.Errorf("attempted to seek backwards: at %d, to %d", r.pos, p) } n, err := io.ReadFull(r.r, d) r.pos += int64(n) return n, err } relic-7.6.1/lib/fruit/xar/xar.go000066400000000000000000000076451455105530300164520ustar00rootroot00000000000000package xar import ( "compress/zlib" "crypto" "crypto/hmac" "crypto/x509" "encoding/base64" "encoding/binary" "encoding/xml" "errors" "fmt" "io" ) type XAR struct { HashFunc crypto.Hash TOCHash []byte Certificates []*x509.Certificate ClassicSignature []byte CMSSignature []byte NotaryTicket []byte toc *tocToc heap io.ReaderAt } func Open(r io.ReaderAt, size int64) (*XAR, error) { hdr, hashType, err := parseHeader(io.NewSectionReader(r, 0, 28)) if err != nil { return nil, err } base := int64(hdr.HeaderSize) toc, tocHash, err := parseTOC(io.NewSectionReader(r, base, hdr.CompressedSize), hashType) if err != nil { return nil, err } base += hdr.CompressedSize if toc.Checksum.Size != int64(hashType.Size()) { return nil, errors.New("checksum is missing or invalid") } checkHash := make([]byte, toc.Checksum.Size) if _, err := r.ReadAt(checkHash, base+toc.Checksum.Offset); err != nil { return nil, fmt.Errorf("checksum: %w", err) } if !hmac.Equal(checkHash, tocHash) { return nil, errors.New("checksum mismatch in TOC") } s := &XAR{ HashFunc: hashType, TOCHash: tocHash, toc: toc, heap: io.NewSectionReader(r, base, 1<<62), } if toc.Signature != nil { s.ClassicSignature = make([]byte, toc.Signature.Size) if _, err := r.ReadAt(s.ClassicSignature, base+toc.Signature.Offset); err != nil { return nil, fmt.Errorf("reading signature: %w", err) } s.Certificates, err = parseCertificates(toc.Signature) if err != nil { return nil, fmt.Errorf("reading signature: %w", err) } } if toc.XSignature != nil { s.CMSSignature = make([]byte, toc.XSignature.Size) if _, err := r.ReadAt(s.CMSSignature, base+toc.XSignature.Offset); err != nil { return nil, fmt.Errorf("reading CMS signature: %w", err) } } lo := lastOffset(toc.Files) + base if trailer := size - lo; trailer > 0 && trailer < 1e6 { ticket := make([]byte, trailer) if _, err := r.ReadAt(ticket, lo); err != nil { return nil, fmt.Errorf("reading trailer: %w", err) } s.NotaryTicket = ticket } return s, nil } func parseHeader(r io.Reader) (hdr fileHeader, hashType crypto.Hash, err error) { if err := binary.Read(r, binary.BigEndian, &hdr); err != nil { return fileHeader{}, 0, err } if hdr.Magic != xarMagic { return fileHeader{}, 0, errors.New("incorrect magic") } else if hdr.Version != 1 { return fileHeader{}, 0, fmt.Errorf("unsupported xar version %d", hdr.Version) } switch hdr.HashType { case hashSHA1: hashType = crypto.SHA1 case hashSHA256: hashType = crypto.SHA256 case hashSHA512: hashType = crypto.SHA512 default: return fileHeader{}, 0, fmt.Errorf("unknown hash algorithm %d", hdr.HashType) } return } func decompress(r io.Reader) ([]byte, error) { zr, err := zlib.NewReader(r) if err != nil { return nil, err } return io.ReadAll(zr) } func parseTOC(r io.Reader, hashType crypto.Hash) (*tocToc, []byte, error) { tocHash := hashType.New() r = io.TeeReader(r, tocHash) decomp, err := decompress(r) if err != nil { return nil, nil, fmt.Errorf("decompressing TOC: %W", err) } toc := new(tocXar) if err := xml.Unmarshal(decomp, toc); err != nil { return nil, nil, fmt.Errorf("decoding TOC: %w", err) } return &toc.TOC, tocHash.Sum(nil), nil } func parseCertificates(sig *tocSignature) ([]*x509.Certificate, error) { if len(sig.Certificates) == 0 { return nil, errors.New("no certificates found") } parsed := make([]*x509.Certificate, len(sig.Certificates)) for i, cert := range sig.Certificates { certBytes, err := base64.StdEncoding.DecodeString(cert) if err != nil { return nil, err } parsed[i], err = x509.ParseCertificate(certBytes) if err != nil { return nil, err } } return parsed, nil } func lastOffset(files []*tocFile) (end int64) { for _, f := range files { if fileEnd := f.Offset + f.Length; fileEnd > end { end = fileEnd } if subEnd := lastOffset(f.Files); subEnd > end { end = subEnd } } return } relic-7.6.1/lib/magic/000077500000000000000000000000001455105530300144525ustar00rootroot00000000000000relic-7.6.1/lib/magic/magic.go000066400000000000000000000123421455105530300160630ustar00rootroot00000000000000// // Copyright (c) SAS Institute Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package magic import ( "archive/zip" "bufio" "bytes" "compress/gzip" "encoding/binary" "errors" "io" "os" "path" "strings" "github.com/xi2/xz" ) type FileType int type CompressionType int const ( FileTypeUnknown FileType = iota FileTypeRPM FileTypeDEB FileTypePGP FileTypeJAR FileTypePKCS7 FileTypePECOFF FileTypeMSI FileTypeCAB FileTypeAppManifest FileTypeCAT FileTypeAPPX FileTypeVSIX FileTypeXAP FileTypeAPK FileTypeMachO FileTypeMachOFat FileTypeIPA FileTypeXAR ) const ( CompressedNone CompressionType = iota CompressedGzip CompressedXz ) func hasPrefix(br *bufio.Reader, blob []byte) bool { return atPosition(br, blob, 0) } func contains(br *bufio.Reader, blob []byte, n int) bool { d, _ := br.Peek(n) if len(d) < len(blob) { return false } return bytes.Contains(d, blob) } func atPosition(br *bufio.Reader, blob []byte, n int) bool { l := n + len(blob) d, _ := br.Peek(l) if len(d) < l { return false } return bytes.Equal(d[n:], blob) } // Detect a handful of package and signature file types based on the first few // bytes of the file contents. func Detect(r io.Reader) FileType { br := bufio.NewReader(r) switch { case hasPrefix(br, []byte{0xed, 0xab, 0xee, 0xdb}): return FileTypeRPM case hasPrefix(br, []byte("!\ndebian")): return FileTypeDEB case hasPrefix(br, []byte("-----BEGIN PGP")): return FileTypePGP case contains(br, []byte{0x06, 0x09, 0x2B, 0x06, 0x01, 0x04, 0x01, 0x82, 0x37, 0x0A, 0x01}, 256): // OID certTrustList return FileTypeCAT case contains(br, []byte{0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x07, 0x02}, 256): // OID signedData return FileTypePKCS7 case isTar(br): return detectTar(br) case hasPrefix(br, []byte("MZ")): if blob, _ := br.Peek(0x3e); len(blob) == 0x3e { reloc := binary.LittleEndian.Uint16(blob[0x3c:0x3e]) if blob, err := br.Peek(int(reloc) + 4); err == nil { if bytes.Equal(blob[reloc:reloc+4], []byte("PE\x00\x00")) { return FileTypePECOFF } } } case hasPrefix(br, []byte{0xd0, 0xcf}): return FileTypeMSI case hasPrefix(br, []byte("MSCF")): return FileTypeCAB case contains(br, []byte("= 0 { if err := serializeLiteral(armorer, message, size, filename); err != nil { return err } } else { litWriter, err := packet.SerializeLiteral(nopCloseWriter{armorer}, true, filename, 0) if err != nil { return err } if _, err := io.Copy(litWriter, message); err != nil { return err } if err := litWriter.Close(); err != nil { return err } } // write signature if _, err := armorer.Write(sig); err != nil { return err } return armorer.Close() } // write a one-pass signature header with the fields copied from the detached signature in sig func writeOnePass(w io.Writer, sig []byte) error { genpkt, err := packet.Read(bytes.NewReader(sig)) if err != nil { return err } // parse op := packet.OnePassSignature{IsLast: true} switch pkt := genpkt.(type) { case *packet.SignatureV3: op.SigType = pkt.SigType op.Hash = pkt.Hash op.PubKeyAlgo = pkt.PubKeyAlgo op.KeyId = pkt.IssuerKeyId case *packet.Signature: op.SigType = pkt.SigType op.Hash = pkt.Hash op.PubKeyAlgo = pkt.PubKeyAlgo if pkt.IssuerKeyId != nil { op.KeyId = *pkt.IssuerKeyId } default: return errors.New("not a PGP signature") } return op.Serialize(w) } // write size bytes from r into w as a literal data packet func serializeLiteral(w io.Writer, r io.Reader, size int32, filename string) error { if len(filename) > 255 { filename = filename[:255] } var buf bytes.Buffer buf.WriteByte('b') // binary mode buf.WriteByte(byte(len(filename))) // filename buf.WriteString(filename) // filename buf.Write([]byte{0, 0, 0, 0}) // timestamp packetType := 11 // literal data psize := int64(size) + int64(buf.Len()) if psize > (2<<31)-1 { return errors.New("literal too big") } if err := serializeHeader(w, packetType, int(psize)); err != nil { return err } if _, err := w.Write(buf.Bytes()); err != nil { return err } _, err := io.CopyN(w, r, int64(size)) return err } // get the size from a reader if it's seekable and not too big to fit in a single literal data packet, otherwise returns -1 func getSize(r io.Reader) int32 { seek, ok := r.(io.Seeker) if !ok { return -1 } start, err := seek.Seek(0, io.SeekCurrent) if err != nil { return -1 } end, err := seek.Seek(0, io.SeekEnd) if err != nil { return -1 } _, err = seek.Seek(start, io.SeekStart) if err != nil { return -1 } size := end - start if size > maxLiteralSize { return -1 } return int32(size) } // serializeHeader writes an OpenPGP packet header to w. See RFC 4880, section // 4.2. func serializeHeader(w io.Writer, ptype int, length int) (err error) { var buf [6]byte var n int buf[0] = 0x80 | 0x40 | byte(ptype) if length < 192 { buf[1] = byte(length) n = 2 } else if length < 8384 { length -= 192 buf[1] = 192 + byte(length>>8) buf[2] = byte(length) n = 3 } else { buf[1] = 255 buf[2] = byte(length >> 24) buf[3] = byte(length >> 16) buf[4] = byte(length >> 8) buf[5] = byte(length) n = 6 } _, err = w.Write(buf[:n]) return } type nopCloseWriter struct { io.Writer } func (nopCloseWriter) Close() error { return nil } relic-7.6.1/lib/pgptools/util.go000066400000000000000000000017751455105530300165570ustar00rootroot00000000000000// // Copyright (c) SAS Institute Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package pgptools import "golang.org/x/crypto/openpgp" // Return the primary identity name of a PGP entity func EntityName(entity *openpgp.Entity) string { if entity == nil { return "" } var name string for _, ident := range entity.Identities { if name == "" { name = ident.Name } if ident.SelfSignature.IsPrimaryId != nil && *ident.SelfSignature.IsPrimaryId { return ident.Name } } return name } relic-7.6.1/lib/pgptools/verify.go000066400000000000000000000113011455105530300170700ustar00rootroot00000000000000// // Copyright (c) SAS Institute Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package pgptools import ( "bytes" "crypto" "errors" "fmt" "io" "io/ioutil" "time" "golang.org/x/crypto/openpgp" "golang.org/x/crypto/openpgp/clearsign" "golang.org/x/crypto/openpgp/packet" ) type PgpSignature struct { Key *openpgp.Key CreationTime time.Time Hash crypto.Hash } // Verify a detached PGP signature in "signature" over the document in // "signed", using keys from "keyring". Returns a value of ErrNoKey if the key // cannot be found. func VerifyDetached(signature, signed io.Reader, keyring openpgp.EntityList) (*PgpSignature, error) { packetReader := packet.NewReader(signature) genpkt, err := packetReader.Next() if err == io.EOF { return nil, errors.New("no PGP signature found") } else if err != nil { return nil, err } // parse var hash crypto.Hash var keyID uint64 var creationTime time.Time switch pkt := genpkt.(type) { case *packet.SignatureV3: hash = pkt.Hash keyID = pkt.IssuerKeyId creationTime = pkt.CreationTime case *packet.Signature: if pkt.IssuerKeyId == nil { return nil, errors.New("Missing keyId in signature") } hash = pkt.Hash keyID = *pkt.IssuerKeyId creationTime = pkt.CreationTime default: return nil, errors.New("not a PGP signature") } // find key keys := keyring.KeysById(keyID) if len(keys) == 0 { return nil, ErrNoKey(keyID) } // calculate hash if !hash.Available() { return nil, errors.New("signature uses unknown digest") } d := hash.New() if _, err := io.Copy(d, signed); err != nil { return nil, err } // check signature switch pkt := genpkt.(type) { case *packet.SignatureV3: err = keys[0].PublicKey.VerifySignatureV3(d, pkt) case *packet.Signature: err = keys[0].PublicKey.VerifySignature(d, pkt) } return &PgpSignature{&keys[0], creationTime, hash}, err } // Verify a cleartext PGP signature in "signature" using keys from "keyring". // Returns a value of ErrNoKey in the key cannot be found. If "cleartext" is // not nil, then write the embedded cleartext as it is verified. func VerifyClearSign(signature io.Reader, cleartext io.Writer, keyring openpgp.EntityList) (*PgpSignature, error) { blob, err := ioutil.ReadAll(signature) if err != nil { return nil, err } csblock, rest := clearsign.Decode(blob) if csblock == nil { return nil, errors.New("malformed clearsign signature") } else if bytes.Contains(rest, []byte("-----BEGIN")) { return nil, errors.New("clearsign contains multiple documents") } if cleartext != nil { if _, err := cleartext.Write(csblock.Bytes); err != nil { return nil, err } } sig, err := VerifyDetached(csblock.ArmoredSignature.Body, bytes.NewReader(csblock.Bytes), keyring) return sig, err } // Verify an inline PGP signature in "signature" using keys from "keyring". // Returns a value of ErrNoKey if the key cannot be found. If "cleartext" is // not nil, then write the embedded cleartext as it is verified. func VerifyInline(signature io.Reader, cleartext io.Writer, keyring openpgp.EntityList) (*PgpSignature, error) { md, err := openpgp.ReadMessage(signature, keyring, nil, nil) if err == io.EOF { return nil, ErrNoContent{} } else if err != nil { return nil, err } else if md.SignedBy == nil { return nil, ErrNoKey(md.SignedByKeyId) } if cleartext == nil { cleartext = ioutil.Discard } if _, err := io.Copy(cleartext, md.UnverifiedBody); err != nil { return nil, err } // reading UnverifiedBody in full triggers the signature validation sig := &PgpSignature{Key: md.SignedBy} if md.Signature != nil { sig.CreationTime = md.Signature.CreationTime sig.Hash = md.Signature.Hash } else if md.SignatureV3 != nil { sig.CreationTime = md.SignatureV3.CreationTime sig.Hash = md.Signature.Hash } return sig, md.SignatureError } // Returned by Verify* functions when the key used for signing is not in the // keyring. The value is the KeyID of the missing key. type ErrNoKey uint64 func (e ErrNoKey) Error() string { return fmt.Sprintf("keyId %x not found", uint64(e)) } // Returned by VerifyInline if the signature is actually a detached signature type ErrNoContent struct{} func (ErrNoContent) Error() string { return "missing content for detached signature" } relic-7.6.1/lib/pkcs7/000077500000000000000000000000001455105530300144215ustar00rootroot00000000000000relic-7.6.1/lib/pkcs7/attributes.go000066400000000000000000000101471455105530300171410ustar00rootroot00000000000000// // Copyright (c) SAS Institute Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package pkcs7 import ( "encoding/asn1" "errors" "fmt" "time" ) type ErrNoAttribute struct { ID asn1.ObjectIdentifier } func (e ErrNoAttribute) Error() string { return fmt.Sprintf("attribute not found: %s", e.ID) } // Bytes returns a SET OF form of the attribute list for digesting, per RFC 2315 9.3, 2nd paragraph func (l *AttributeList) Bytes() ([]byte, error) { return marshalUnsortedSet(*l) } // Need to marshal authenticated attributes as a SET OF in order to digest them, // but since go 1.15 sets get sorted which breaks the digest. Marshal as a // sequence and then change the tag. func marshalUnsortedSet(v interface{}) ([]byte, error) { encoded, err := asn1.Marshal(v) if err != nil { return nil, err } if len(encoded) > 0 { if encoded[0]&0x1f != asn1.TagSequence { return nil, fmt.Errorf("expected sequence, got %d", encoded[0]&0x1f) } // sequence 16 -> set 17 encoded[0] |= 1 } return encoded, nil } // GetOne unmarshals a single attribute, if it exists func (l *AttributeList) GetOne(oid asn1.ObjectIdentifier, dest interface{}) error { for _, raw := range *l { if !raw.Type.Equal(oid) { continue } rest, err := asn1.Unmarshal(raw.Values.Bytes, dest) if err != nil { return err } else if len(rest) != 0 { return fmt.Errorf("attribute %s: expected one, found multiple", oid) } else { return nil } } return ErrNoAttribute{oid} } // GetAll unmarshals all values for an attribute. dest should be a pointer to a slice. func (l *AttributeList) GetAll(oid asn1.ObjectIdentifier, dest interface{}) error { for _, raw := range *l { if raw.Type.Equal(oid) { _, err := asn1.UnmarshalWithParams(raw.Values.FullBytes, dest, "set") return err } } return ErrNoAttribute{oid} } // create or append to an attribute func (l *AttributeList) Add(oid asn1.ObjectIdentifier, obj interface{}) error { value, err := asn1.Marshal(obj) if err != nil { return err } *l = appendAttr(*l, oid, value) return nil } func appendAttr(attrList AttributeList, oid asn1.ObjectIdentifier, value []byte) AttributeList { for i, attr := range attrList { if attr.Type.Equal(oid) { attr.Values.Bytes = append(attr.Values.Bytes, value...) attrList[i] = attr return attrList } } return append(attrList, Attribute{ Type: oid, Values: asn1.RawValue{ Class: asn1.ClassUniversal, Tag: asn1.TagSet, IsCompound: true, Bytes: value, }}) } func (l AttributeList) Exists(oid asn1.ObjectIdentifier) bool { for _, attr := range l { if attr.Type.Equal(oid) { return true } } return false } func (i SignerInfo) SigningTime() (time.Time, error) { var raw asn1.RawValue if err := i.AuthenticatedAttributes.GetOne(OidAttributeSigningTime, &raw); err != nil { return time.Time{}, err } return ParseTime(raw) } // AuthenticatedAttributesBytes returns a SET OF form of the attribute list for digesting, per RFC 2315 9.3, 2nd paragraph func (i SignerInfo) AuthenticatedAttributesBytes() ([]byte, error) { if i.RawContent == nil { return i.AuthenticatedAttributes.Bytes() } // decode the SignerInfo as a sequence of raw values to extract how the // authenticated attributes were originally encoded extract the original var seq []asn1.RawValue if _, err := asn1.Unmarshal(i.RawContent, &seq); err != nil { return nil, err } if len(seq) < 4 { return nil, errors.New("short sequence in SignerInfo") } raw := seq[3] // tweak the attribute sequence to be a set return marshalUnsortedSet(asn1.RawValue{ Tag: asn1.TagSequence, IsCompound: true, Bytes: raw.Bytes, }) } relic-7.6.1/lib/pkcs7/attributes_test.go000066400000000000000000000021531455105530300201760ustar00rootroot00000000000000package pkcs7 import ( "encoding/asn1" "testing" "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) // marshal and unmarshal so FullBytes is set func roundTrip(t *testing.T, l AttributeList) AttributeList { t.Helper() raw, err := marshalUnsortedSet(l) require.NoError(t, err) var l2 AttributeList _, err = asn1.UnmarshalWithParams(raw, &l2, "set") require.NoError(t, err) return l2 } func TestAttributeList(t *testing.T) { var l AttributeList assert.False(t, l.Exists(OidAttributeSigningTime)) a := time.Date(2000, 1, 1, 0, 0, 0, 0, time.UTC) assert.NoError(t, l.Add(OidAttributeSigningTime, a)) ll := roundTrip(t, l) assert.True(t, ll.Exists(OidAttributeSigningTime)) var x time.Time if assert.NoError(t, ll.GetOne(OidAttributeSigningTime, &x)) { assert.Equal(t, a, x) } b := a.AddDate(0, 0, 1) assert.NoError(t, l.Add(OidAttributeSigningTime, b)) ll = roundTrip(t, l) assert.Error(t, ll.GetOne(OidAttributeSigningTime, &x)) var times []time.Time if assert.NoError(t, ll.GetAll(OidAttributeSigningTime, ×)) { assert.Equal(t, []time.Time{a, b}, times) } } relic-7.6.1/lib/pkcs7/builder.go000066400000000000000000000106471455105530300164060ustar00rootroot00000000000000// // Copyright (c) SAS Institute Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package pkcs7 import ( "crypto" "crypto/rand" "crypto/x509" "crypto/x509/pkix" "encoding/asn1" "errors" "fmt" "github.com/sassoftware/relic/v7/lib/x509tools" ) type SignatureBuilder struct { contentInfo ContentInfo digest []byte certs []*x509.Certificate privateKey crypto.Signer signerOpts crypto.SignerOpts authAttrs AttributeList } // Build a PKCS#7 signature procedurally. Returns a structure that can have // content and attributes attached to it. func NewBuilder(privKey crypto.Signer, certs []*x509.Certificate, opts crypto.SignerOpts) *SignatureBuilder { return &SignatureBuilder{ privateKey: privKey, signerOpts: opts, certs: certs, } } // Embed bytes or a structure into the PKCS#7 content func (sb *SignatureBuilder) SetContent(ctype asn1.ObjectIdentifier, data interface{}) error { cinfo, err := NewContentInfo(ctype, data) if err != nil { return err } return sb.SetContentInfo(cinfo) } // Set content to a generic "data" blob func (sb *SignatureBuilder) SetContentData(data []byte) error { return sb.SetContent(OidData, data) } // Set a ContentInfo structure as the PKCS#7 content func (sb *SignatureBuilder) SetContentInfo(cinfo ContentInfo) error { blob, err := cinfo.Bytes() if err != nil { return err } d := sb.signerOpts.HashFunc().New() d.Write(blob) sb.contentInfo = cinfo sb.digest = d.Sum(nil) return nil } // Set a "detached" content type, with digest func (sb *SignatureBuilder) SetDetachedContent(ctype asn1.ObjectIdentifier, digest []byte) error { if len(digest) != sb.signerOpts.HashFunc().Size() { return errors.New("digest size mismatch") } cinfo, _ := NewContentInfo(ctype, nil) sb.contentInfo = cinfo sb.digest = digest return nil } // Add an authenticated attribute to SignerInfo func (sb *SignatureBuilder) AddAuthenticatedAttribute(oid asn1.ObjectIdentifier, data interface{}) error { return sb.authAttrs.Add(oid, data) } // Complete the signature and return the full PKCS#7 structure func (sb *SignatureBuilder) Sign() (*ContentInfoSignedData, error) { if sb.digest == nil { return nil, errors.New("SetContent was not called") } pubKey := sb.privateKey.Public() digestAlg, pkeyAlg, err := x509tools.PkixAlgorithms(pubKey, sb.signerOpts) if err != nil { return nil, fmt.Errorf("pkcs7: %w", err) } if len(sb.certs) < 1 || !x509tools.SameKey(pubKey, sb.certs[0].PublicKey) { return nil, errors.New("pkcs7: first certificate must match private key") } digest := sb.digest if sb.authAttrs != nil { // When authenticated attributes are present, then these are required. if err := sb.authAttrs.Add(OidAttributeContentType, sb.contentInfo.ContentType); err != nil { return nil, err } if err := sb.authAttrs.Add(OidAttributeMessageDigest, sb.digest); err != nil { return nil, err } // Now the signature is over the authenticated attributes instead of // the content directly. attrbytes, err := sb.authAttrs.Bytes() if err != nil { return nil, err } w := sb.signerOpts.HashFunc().New() w.Write(attrbytes) digest = w.Sum(nil) } sig, err := sb.privateKey.Sign(rand.Reader, digest, sb.signerOpts) if err != nil { return nil, err } return &ContentInfoSignedData{ ContentType: OidSignedData, Content: SignedData{ Version: 1, DigestAlgorithmIdentifiers: []pkix.AlgorithmIdentifier{digestAlg}, ContentInfo: sb.contentInfo, Certificates: marshalCertificates(sb.certs), CRLs: nil, SignerInfos: []SignerInfo{{ Version: 1, IssuerAndSerialNumber: IssuerAndSerial{ IssuerName: asn1.RawValue{FullBytes: sb.certs[0].RawIssuer}, SerialNumber: sb.certs[0].SerialNumber, }, DigestAlgorithm: digestAlg, DigestEncryptionAlgorithm: pkeyAlg, AuthenticatedAttributes: sb.authAttrs, EncryptedDigest: sig, }}, }, }, nil } relic-7.6.1/lib/pkcs7/content.go000066400000000000000000000046001455105530300164220ustar00rootroot00000000000000// // Copyright (c) SAS Institute Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package pkcs7 import ( "encoding/asn1" ) type contentInfo2 struct { ContentType asn1.ObjectIdentifier Value asn1.RawValue } // Create a ContentInfo structure for the given bytes or structure. data can be // nil for detached signatures. func NewContentInfo(contentType asn1.ObjectIdentifier, data interface{}) (ci ContentInfo, err error) { if data == nil { return ContentInfo{ContentType: contentType}, nil } // There's no way to just encode the struct with the asn1.RawValue directly // while also supporting the ability to not emit the 2nd field for the nil // case, so instead this stupid dance of encoding it with the field then // stuffing it into Raw is necessary... encoded, err := asn1.Marshal(data) if err != nil { return ContentInfo{}, err } ci2 := contentInfo2{ ContentType: contentType, Value: asn1.RawValue{ Class: asn1.ClassContextSpecific, Tag: 0, IsCompound: true, Bytes: encoded, }, } ciblob, err := asn1.Marshal(ci2) if err != nil { return ContentInfo{}, nil } return ContentInfo{Raw: ciblob, ContentType: contentType}, nil } // Unmarshal a structure from a ContentInfo. func (ci ContentInfo) Unmarshal(dest interface{}) (err error) { // First re-decode the contentinfo but this time with the second field var ci2 contentInfo2 _, err = asn1.Unmarshal(ci.Raw, &ci2) if err == nil { // Now decode the raw value in the second field _, err = asn1.Unmarshal(ci2.Value.Bytes, dest) } return } // Get raw content in DER encoding, or nil if it's not present func (ci ContentInfo) Bytes() ([]byte, error) { var value asn1.RawValue if err := ci.Unmarshal(&value); err != nil { if _, ok := err.(asn1.SyntaxError); ok { // short sequence because the value was omitted return nil, nil } return nil, err } return value.Bytes, nil } relic-7.6.1/lib/pkcs7/marshal.go000066400000000000000000000063761455105530300164130ustar00rootroot00000000000000// // Copyright (c) SAS Institute Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package pkcs7 import ( "bytes" "crypto/x509" "encoding/asn1" "errors" "fmt" "time" ) // Parse a signature from bytes func Unmarshal(blob []byte) (*ContentInfoSignedData, error) { psd := new(ContentInfoSignedData) if rest, err := asn1.Unmarshal(blob, psd); err != nil { return nil, err } else if len(bytes.TrimRight(rest, "\x00")) != 0 { return nil, errors.New("pkcs7: trailing garbage after PKCS#7 structure") } return psd, nil } // Marshal the signature to bytes func (psd *ContentInfoSignedData) Marshal() ([]byte, error) { return asn1.Marshal(*psd) } // Remove and return inlined content from the document, leaving a detached signature func (psd *ContentInfoSignedData) Detach() ([]byte, error) { content, err := psd.Content.ContentInfo.Bytes() if err != nil { return nil, fmt.Errorf("pkcs7: %w", err) } psd.Content.ContentInfo, _ = NewContentInfo(psd.Content.ContentInfo.ContentType, nil) return content, nil } // dump raw certificates to structure func marshalCertificates(certs []*x509.Certificate) RawCertificates { c := make(RawCertificates, len(certs)) for i, cert := range certs { c[i] = asn1.RawValue{FullBytes: cert.Raw} } return c } // Parse raw certificates from structure. If any cert is invalid, the remaining valid certs are returned along with a CertificateError. func (raw RawCertificates) Parse() ([]*x509.Certificate, error) { var invalid CertificateError var certs []*x509.Certificate for _, rawCert := range raw { cert, err := x509.ParseCertificate(rawCert.FullBytes) if err != nil { invalid.Invalid = append(invalid.Invalid, rawCert.FullBytes) invalid.Err = err } else { certs = append(certs, cert) } } if invalid.Err != nil { return certs, invalid } return certs, nil } type CertificateError struct { Invalid [][]byte Err error } func (c CertificateError) Error() string { return c.Err.Error() } func (c CertificateError) Unwrap() error { return c.Err } // ParseTime parses a GeneralizedTime or UTCTime value that potentially has a fractional seconds part func ParseTime(raw asn1.RawValue) (ret time.Time, err error) { // as of go 1.12 fractional timestamps fail to parse with a "did not serialize back to the original value" error, so this implementation without the serialize check is needed s := string(raw.Bytes) switch raw.Tag { case asn1.TagGeneralizedTime: formatStr := "20060102150405Z0700" return time.Parse(formatStr, s) case asn1.TagUTCTime: formatStr := "0601021504Z0700" ret, err = time.Parse(formatStr, s) if err != nil { formatStr = "060102150405Z0700" ret, err = time.Parse(formatStr, s) } return default: err = fmt.Errorf("unknown tag %d in timestamp field", raw.Tag) return } } relic-7.6.1/lib/pkcs7/structs.go000066400000000000000000000055441455105530300164670ustar00rootroot00000000000000// // Copyright (c) SAS Institute Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // // PKCS#7 is a specification for signing or encrypting data using ASN.1 // structures. It is also known as CMS (cryptographic message syntax) and is // discussed in RFC 2315, RFC 3369, RFC 3852, and RFC 5652. // // This package implements signature operations needed for creating and // validating signature technologies based on PKCS#7 including Java and // Microsoft Authenticode package pkcs7 import ( "crypto/x509/pkix" "encoding/asn1" "math/big" ) var ( OidData = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 7, 1} OidSignedData = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 7, 2} OidAttributeContentType = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 9, 3} OidAttributeMessageDigest = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 9, 4} OidAttributeSigningTime = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 9, 5} ) const MimeType = "application/pkcs7-mime" type ContentInfo struct { Raw asn1.RawContent ContentType asn1.ObjectIdentifier } type ContentInfoSignedData struct { ContentType asn1.ObjectIdentifier Content SignedData `asn1:"explicit,optional,tag:0"` } type SignedData struct { Version int `asn1:"default:1"` DigestAlgorithmIdentifiers []pkix.AlgorithmIdentifier `asn1:"set"` ContentInfo ContentInfo `` Certificates RawCertificates `asn1:"optional,tag:0"` CRLs []pkix.CertificateList `asn1:"optional,tag:1"` SignerInfos []SignerInfo `asn1:"set"` } type RawCertificates []asn1.RawValue type Attribute struct { Type asn1.ObjectIdentifier Values asn1.RawValue } type AttributeList []Attribute type SignerInfo struct { RawContent asn1.RawContent Version int `asn1:"default:1"` IssuerAndSerialNumber IssuerAndSerial `` DigestAlgorithm pkix.AlgorithmIdentifier `` AuthenticatedAttributes AttributeList `asn1:"optional,tag:0"` DigestEncryptionAlgorithm pkix.AlgorithmIdentifier `` EncryptedDigest []byte `` UnauthenticatedAttributes AttributeList `asn1:"optional,tag:1"` } type IssuerAndSerial struct { IssuerName asn1.RawValue SerialNumber *big.Int } relic-7.6.1/lib/pkcs7/verify.go000066400000000000000000000145521455105530300162630ustar00rootroot00000000000000// // Copyright (c) SAS Institute Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package pkcs7 import ( "bytes" "crypto/hmac" "crypto/rsa" "crypto/x509" "encoding/asn1" "errors" "fmt" "math/big" "time" "github.com/sassoftware/relic/v7/lib/x509tools" "github.com/sassoftware/relic/v7/signers/sigerrors" ) type Signature struct { SignerInfo *SignerInfo Certificate *x509.Certificate Intermediates []*x509.Certificate CertError error } // Verify the content in a SignedData structure. External content may be // provided if it is a detached signature. Information about the signature is // returned, however X509 chains are not validated. Call VerifyChain() to // complete the verification process. // // If skipDigests is true, then the main content section is not checked, but // the SignerInfos are still checked for a valid signature. func (sd *SignedData) Verify(externalContent []byte, skipDigests bool) (Signature, error) { var content []byte if !skipDigests { var err error content, err = sd.ContentInfo.Bytes() if err != nil { return Signature{}, err } else if content == nil { if externalContent == nil { return Signature{}, errors.New("pkcs7: missing content") } content = externalContent } else if externalContent != nil { if !bytes.Equal(externalContent, content) { return Signature{}, errors.New("pkcs7: internal and external content were both provided but are not equal") } } } if len(sd.SignerInfos) == 0 { return Signature{}, sigerrors.NotSignedError{Type: "pkcs7"} } certs, certErr := sd.Certificates.Parse() // postpone handling of cert parse error until something is actually missing var cert *x509.Certificate var sig Signature for _, si := range sd.SignerInfos { var err error cert, err = si.Verify(content, skipDigests, certs) if err != nil { if errors.As(err, &MissingCertificateError{}) && certErr != nil { // now surface the parse error err = certErr } return Signature{}, err } sig = Signature{ SignerInfo: &si, Certificate: cert, Intermediates: certs, CertError: certErr, } } return sig, nil } // Find the certificate that signed this SignerInfo from the bucket of certs func (si *SignerInfo) FindCertificate(certs []*x509.Certificate) (*x509.Certificate, error) { is := si.IssuerAndSerialNumber for _, cert := range certs { if bytes.Equal(cert.RawIssuer, is.IssuerName.FullBytes) && cert.SerialNumber.Cmp(is.SerialNumber) == 0 { return cert, nil } } return nil, MissingCertificateError{Issuer: is.IssuerName, SerialNumber: is.SerialNumber} } type MissingCertificateError struct { Issuer asn1.RawValue SerialNumber *big.Int } func (e MissingCertificateError) Error() string { name := x509tools.FormatPkixName(e.Issuer.FullBytes, x509tools.NameStyleLdap) return fmt.Sprintf("certificate missing from signedData: serial=%x issuer: %s", e.SerialNumber, name) } // Verify the signature contained in this SignerInfo and return the leaf // certificate. X509 chains are not validated. func (si *SignerInfo) Verify(content []byte, skipDigests bool, certs []*x509.Certificate) (*x509.Certificate, error) { hash, err := x509tools.PkixDigestToHashE(si.DigestAlgorithm) if err != nil { return nil, fmt.Errorf("pkcs7: %w", err) } var digest []byte if !skipDigests { w := hash.New() w.Write(content) digest = w.Sum(nil) } if len(si.AuthenticatedAttributes) != 0 { // check the content digest against the messageDigest attribute var md []byte if err := si.AuthenticatedAttributes.GetOne(OidAttributeMessageDigest, &md); err != nil { return nil, err } else if digest != nil && !hmac.Equal(md, digest) { return nil, errors.New("pkcs7: content digest does not match") } // now pivot to verifying the hash over the authenticated attributes w := hash.New() attrbytes, err := si.AuthenticatedAttributesBytes() if err != nil { return nil, fmt.Errorf("verifying authenticated attributes: %w", err) } w.Write(attrbytes) digest = w.Sum(nil) } // otherwise the content hash is verified directly cert, err := si.FindCertificate(certs) if err != nil { return nil, err } // If skipDigests is set and AuthenticatedAttributes is not present then // there's no digest to check the signature against. For RSA at least it's // possible to decrypt the signature and check the padding but there's not // much point to it. if digest != nil { err = x509tools.PkixVerify(cert.PublicKey, si.DigestAlgorithm, si.DigestEncryptionAlgorithm, digest, si.EncryptedDigest) if err == rsa.ErrVerification { // "Symantec Time Stamping Services Signer" seems to be emitting // signatures without the AlgorithmIdentifier strucuture, so try // without it. err = x509tools.Verify(cert.PublicKey, 0, digest, si.EncryptedDigest) } } return cert, err } // Verify the X509 chain from a signature against the given roots. extraCerts // will be added to the intermediates if provided. usage gives the certificate // usage required for the leaf certificate, or ExtKeyUsageAny otherwise. If a // PKCS#9 trusted timestamp was found, pass that timestamp in currentTime to // validate the chain as of the time of the signature. func (info Signature) VerifyChain(roots *x509.CertPool, extraCerts []*x509.Certificate, usage x509.ExtKeyUsage, currentTime time.Time) error { pool := x509.NewCertPool() for _, cert := range extraCerts { pool.AddCert(cert) } for _, cert := range info.Intermediates { pool.AddCert(cert) } opts := x509.VerifyOptions{ Intermediates: pool, Roots: roots, CurrentTime: currentTime, KeyUsages: []x509.ExtKeyUsage{usage}, } _, err := info.Certificate.Verify(opts) if err == nil { return nil } if e := new(x509.UnknownAuthorityError); errors.As(err, e) && info.CertError != nil { // surface a saved cert parse error return fmt.Errorf("%w: after failing to parse a bundled certificate: %s", err, info.CertError) } return err } relic-7.6.1/lib/pkcs9/000077500000000000000000000000001455105530300144235ustar00rootroot00000000000000relic-7.6.1/lib/pkcs9/http.go000066400000000000000000000066451455105530300157440ustar00rootroot00000000000000// // Copyright (c) SAS Institute Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package pkcs9 import ( "bytes" "crypto" "crypto/hmac" "encoding/asn1" "errors" "fmt" "net/http" "github.com/sassoftware/relic/v7/lib/pkcs7" "github.com/sassoftware/relic/v7/lib/x509tools" ) // RFC 3161 timestamping // Create a HTTP request to request a token from the given URL func NewRequest(url string, hash crypto.Hash, hashValue []byte) (msg *TimeStampReq, req *http.Request, err error) { alg, ok := x509tools.PkixDigestAlgorithm(hash) if !ok { return nil, nil, errors.New("unknown digest algorithm") } msg = &TimeStampReq{ Version: 1, MessageImprint: MessageImprint{ HashAlgorithm: alg, HashedMessage: hashValue, }, Nonce: x509tools.MakeSerial(), CertReq: true, } reqbytes, err := asn1.Marshal(*msg) if err != nil { return } req, err = http.NewRequest("POST", url, bytes.NewReader(reqbytes)) if err != nil { return } req.Header.Set("Content-Type", "application/timestamp-query") return } // Parse a timestamp token from a HTTP response, sanity checking it against the original request nonce func (req *TimeStampReq) ParseResponse(body []byte) (*pkcs7.ContentInfoSignedData, error) { respmsg := new(TimeStampResp) if rest, err := asn1.Unmarshal(body, respmsg); err != nil { return nil, fmt.Errorf("pkcs9: unmarshalling response: %w", err) } else if len(rest) != 0 { return nil, errors.New("pkcs9: trailing bytes in response") } else if respmsg.Status.Status > StatusGrantedWithMods { return nil, fmt.Errorf("pkcs9: request denied: status=%d failureInfo=%x", respmsg.Status.Status, respmsg.Status.FailInfo.Bytes) } if err := req.SanityCheckToken(&respmsg.TimeStampToken); err != nil { return nil, fmt.Errorf("pkcs9: token sanity check failed: %w", err) } return &respmsg.TimeStampToken, nil } // Sanity check a timestamp token against the nonce in the original request func (req *TimeStampReq) SanityCheckToken(psd *pkcs7.ContentInfoSignedData) error { if _, err := psd.Content.Verify(nil, false); err != nil { return err } info, err := unpackTokenInfo(psd) if err != nil { return err } if req.Nonce.Cmp(info.Nonce) != 0 { return errors.New("request nonce mismatch") } if !hmac.Equal(info.MessageImprint.HashedMessage, req.MessageImprint.HashedMessage) { return errors.New("message imprint mismatch") } return nil } // Unpack TSTInfo from a timestamp token func unpackTokenInfo(psd *pkcs7.ContentInfoSignedData) (*TSTInfo, error) { infobytes, err := psd.Content.ContentInfo.Bytes() if err != nil { return nil, fmt.Errorf("unpack TSTInfo: %w", err) } else if infobytes[0] == 0x04 { // unwrap dummy OCTET STRING _, err = asn1.Unmarshal(infobytes, &infobytes) if err != nil { return nil, fmt.Errorf("unpack TSTInfo: %w", err) } } info := new(TSTInfo) if _, err := asn1.Unmarshal(infobytes, info); err != nil { return nil, fmt.Errorf("unpack TSTInfo: %w", err) } return info, nil } relic-7.6.1/lib/pkcs9/iface.go000066400000000000000000000023371455105530300160260ustar00rootroot00000000000000// Copyright © SAS Institute Inc. // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package pkcs9 import ( "context" "crypto" "github.com/sassoftware/relic/v7/lib/pkcs7" ) // Timestamper is the common interface for the timestamp client and middleware type Timestamper interface { Timestamp(ctx context.Context, req *Request) (*pkcs7.ContentInfoSignedData, error) } // Request holds parameters for a timestamp operation type Request struct { // EncryptedDigest is the raw encrypted signature value EncryptedDigest []byte // Hash is the desired hash function for the timestamp. Ignored for legacy requests. Hash crypto.Hash // Legacy indicates a nonstandard microsoft timestamp request, otherwise RFC 3161 is used Legacy bool } relic-7.6.1/lib/pkcs9/microsoft.go000066400000000000000000000036311455105530300167620ustar00rootroot00000000000000// // Copyright (c) SAS Institute Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package pkcs9 import ( "bytes" "encoding/asn1" "encoding/base64" "net/http" "github.com/sassoftware/relic/v7/lib/pkcs7" ) // Microsoft non-RFC-3161 timestamping // https://msdn.microsoft.com/en-us/library/windows/desktop/bb931395(v=vs.85).aspx type MicrosoftTimeStampRequest struct { CounterSignatureType asn1.ObjectIdentifier Attributes struct{} `asn1:"optional"` Content struct { ContentType asn1.ObjectIdentifier Content []byte `asn1:"explicit,tag:0"` } } func NewLegacyRequest(url string, encryptedDigest []byte) (*http.Request, error) { var msg MicrosoftTimeStampRequest msg.CounterSignatureType = OidSpcTimeStampRequest msg.Content.ContentType = pkcs7.OidData msg.Content.Content = encryptedDigest blob, err := asn1.Marshal(msg) if err != nil { return nil, err } req, err := http.NewRequest("POST", url, bytes.NewReader(blob)) if err != nil { return nil, err } req.Header.Set("Content-Type", "application/octet-stream") return req, nil } func ParseLegacyResponse(body []byte) (*pkcs7.ContentInfoSignedData, error) { rblob, err := base64.StdEncoding.DecodeString(string(bytes.TrimRight(body, "\x00"))) if err != nil { return nil, err } psd := new(pkcs7.ContentInfoSignedData) if _, err := asn1.Unmarshal(rblob, psd); err != nil { return nil, err } return psd, nil } relic-7.6.1/lib/pkcs9/pkcs7.go000066400000000000000000000145751455105530300160150ustar00rootroot00000000000000// // Copyright (c) SAS Institute Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package pkcs9 import ( "bytes" "context" "crypto" "crypto/x509" "errors" "fmt" "time" "github.com/sassoftware/relic/v7/lib/pkcs7" "github.com/sassoftware/relic/v7/lib/x509tools" ) func TimestampAndMarshal(ctx context.Context, psd *pkcs7.ContentInfoSignedData, timestamper Timestamper, authenticode bool) (*TimestampedSignature, error) { if timestamper != nil { signerInfo := &psd.Content.SignerInfos[0] hash, err := x509tools.PkixDigestToHashE(signerInfo.DigestAlgorithm) if err != nil { return nil, err } token, err := timestamper.Timestamp(ctx, &Request{EncryptedDigest: signerInfo.EncryptedDigest, Hash: hash}) if err != nil { return nil, err } if authenticode { err = AddStampToSignedAuthenticode(signerInfo, *token) } else { err = AddStampToSignedData(signerInfo, *token) } if err != nil { return nil, err } } verified, err := psd.Content.Verify(nil, false) if err != nil { return nil, fmt.Errorf("pkcs7: failed signature self-check: %w", err) } ts, err := VerifyOptionalTimestamp(verified) if err != nil { return nil, fmt.Errorf("pkcs7: failed signature self-check: %w", err) } blob, err := psd.Marshal() if err != nil { return nil, err } ts.Raw = blob return &ts, err } // Attach a RFC 3161 timestamp to a PKCS#7 SignerInfo func AddStampToSignedData(signerInfo *pkcs7.SignerInfo, token pkcs7.ContentInfoSignedData) error { return signerInfo.UnauthenticatedAttributes.Add(OidAttributeTimeStampToken, token) } // Attach a RFC 3161 timestamp to a PKCS#7 SignerInfo using the OID for authenticode signatures func AddStampToSignedAuthenticode(signerInfo *pkcs7.SignerInfo, token pkcs7.ContentInfoSignedData) error { return signerInfo.UnauthenticatedAttributes.Add(OidSpcTimeStampToken, token) } // Validated timestamp token type CounterSignature struct { pkcs7.Signature Hash crypto.Hash SigningTime time.Time } // Validated signature containing a optional timestamp token type TimestampedSignature struct { pkcs7.Signature CounterSignature *CounterSignature Raw []byte } // Look for a timestamp (counter-signature or timestamp token) in the // UnauthenticatedAttributes of the given already-validated signature and check // its integrity. The certificate chain is not checked; call VerifyChain() on // the result to validate it fully. Returns nil if no timestamp is present. func VerifyPkcs7(sig pkcs7.Signature) (*CounterSignature, error) { var tst pkcs7.ContentInfoSignedData // check several OIDs for timestamp tokens err := sig.SignerInfo.UnauthenticatedAttributes.GetOne(OidAttributeTimeStampToken, &tst) if _, ok := err.(pkcs7.ErrNoAttribute); ok { err = sig.SignerInfo.UnauthenticatedAttributes.GetOne(OidSpcTimeStampToken, &tst) } var imprintHash crypto.Hash if err == nil { // timestamptoken is a fully nested signedData containing a TSTInfo // that digests the parent signature blob return Verify(&tst, sig.SignerInfo.EncryptedDigest, sig.Intermediates) } else if _, ok := err.(pkcs7.ErrNoAttribute); ok { tsi := new(pkcs7.SignerInfo) if err := sig.SignerInfo.UnauthenticatedAttributes.GetOne(OidAttributeCounterSign, tsi); err != nil { if _, ok := err.(pkcs7.ErrNoAttribute); ok { return nil, nil } return nil, err } // counterSignature is simply a signerinfo. The certificate chain is // included in the parent structure, and the timestamp signs the // signature blob from the parent signerinfo imprintHash, _ = x509tools.PkixDigestToHash(sig.SignerInfo.DigestAlgorithm) return finishVerify(tsi, sig.SignerInfo.EncryptedDigest, sig.Intermediates, imprintHash, tsi, nil) } return nil, err } // Look for a timestamp token or counter-signature in the given signature and // return a structure that can be used to validate the signature's certificate // chain. If no timestamp is present, then the current time will be used when // validating the chain. func VerifyOptionalTimestamp(sig pkcs7.Signature) (TimestampedSignature, error) { tsig := TimestampedSignature{Signature: sig} ts, err := VerifyPkcs7(sig) if err != nil { return tsig, err } tsig.CounterSignature = ts return tsig, nil } // Verify that the timestamp token has a valid certificate chain func (cs CounterSignature) VerifyChain(roots *x509.CertPool, extraCerts []*x509.Certificate) error { return cs.Signature.VerifyChain(roots, extraCerts, x509.ExtKeyUsageTimeStamping, cs.SigningTime) } // Verify the certificate chain of a PKCS#7 signature. If the signature has a // valid timestamp token attached, then the timestamp is used for validating // the primary signature's chain, making the signature valid after the // certificates have expired. func (sig TimestampedSignature) VerifyChain(roots *x509.CertPool, extraCerts []*x509.Certificate, usage x509.ExtKeyUsage) error { var signingTime time.Time if sig.CounterSignature != nil { if err := sig.CounterSignature.VerifyChain(roots, extraCerts); err != nil { return fmt.Errorf("validating timestamp: %w", err) } signingTime = sig.CounterSignature.SigningTime } return sig.Signature.VerifyChain(roots, extraCerts, usage, signingTime) } // Verify a non-RFC-3161 timestamp token against the given encrypted digest // from the primary signature. func VerifyMicrosoftToken(token *pkcs7.ContentInfoSignedData, encryptedDigest []byte) (*CounterSignature, error) { sig, err := token.Content.Verify(nil, false) if err != nil { return nil, err } content, err := token.Content.ContentInfo.Bytes() if err != nil { return nil, err } if !bytes.Equal(content, encryptedDigest) { return nil, errors.New("timestamp does not match the enclosing signature") } hash, _ := x509tools.PkixDigestToHash(sig.SignerInfo.DigestAlgorithm) signingTime, err := sig.SignerInfo.SigningTime() if err != nil { return nil, err } return &CounterSignature{ Signature: sig, Hash: hash, SigningTime: signingTime, }, nil } relic-7.6.1/lib/pkcs9/ratelimit/000077500000000000000000000000001455105530300164155ustar00rootroot00000000000000relic-7.6.1/lib/pkcs9/ratelimit/ratelimit.go000066400000000000000000000032161455105530300207400ustar00rootroot00000000000000// Copyright © SAS Institute Inc. // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package ratelimit import ( "context" "time" "golang.org/x/time/rate" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promauto" "github.com/sassoftware/relic/v7/lib/pkcs7" "github.com/sassoftware/relic/v7/lib/pkcs9" ) var metricRateLimited = promauto.NewCounter(prometheus.CounterOpts{ Name: "timestamper_rate_limited_seconds", Help: "Cumulative number of seconds waiting for rate limits", }) type limiter struct { Timestamper pkcs9.Timestamper Limit *rate.Limiter } func New(t pkcs9.Timestamper, r float64, burst int) pkcs9.Timestamper { if r == 0 { return t } if burst < 1 { burst = 1 } return &limiter{t, rate.NewLimiter(rate.Limit(r), burst)} } func (l *limiter) Timestamp(ctx context.Context, req *pkcs9.Request) (*pkcs7.ContentInfoSignedData, error) { start := time.Now() if err := l.Limit.Wait(ctx); err != nil { return nil, err } if waited := time.Since(start); waited > 1*time.Millisecond { metricRateLimited.Add(time.Since(start).Seconds()) } return l.Timestamper.Timestamp(ctx, req) } relic-7.6.1/lib/pkcs9/structs.go000066400000000000000000000071541455105530300164700ustar00rootroot00000000000000// // Copyright (c) SAS Institute Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // // PKCS#9 is a specification for trusted timestamping. Timestamping services // create a timestamp token which includes a known-good timestamp with a // signature over it. The token can be attached to a document to prove that it // existed at the indicated time. When attached to a PKCS#7 signedData // structure, the timestamp proves that the primary signature was created // during the valid lifespan of the signing certificate, allowing it to be // validated after the certificates have expired. // // See RFC 3161 package pkcs9 import ( "crypto/x509/pkix" "encoding/asn1" "math/big" "time" "github.com/sassoftware/relic/v7/lib/pkcs7" ) const ( StatusGranted = iota StatusGrantedWithMods StatusRejection StatusWaiting StatusRevocationWarning StatusRevocationNotification FailureBadAlg = 0 FailureBadRequest = 2 FailureBadDataFormat = 5 FailureTimeNotAvailable = 14 FailureUnacceptedPolicy = 15 FailureUnacceptedExtension = 16 FailureAddInfoNotAvailable = 17 SystemFailure = 25 ) var ( OidKeyPurposeTimeStamping = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 3, 8} OidTSTInfo = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 9, 16, 1, 4} OidAttributeTimeStampToken = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 9, 16, 2, 14} OidAttributeCounterSign = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 9, 6} OidSpcTimeStampRequest = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 311, 3, 2, 1} // undocumented(?) alternative to OidAttributeTimeStampToken found in Authenticode signatures OidSpcTimeStampToken = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 311, 3, 3, 1} ) type TimeStampReq struct { Version int MessageImprint MessageImprint ReqPolicy asn1.ObjectIdentifier `asn1:"optional"` Nonce *big.Int `asn1:"optional"` CertReq bool `asn1:"default:false"` Extensions []pkix.Extension `asn1:"optional,implicit,tag:0"` } type MessageImprint struct { HashAlgorithm pkix.AlgorithmIdentifier HashedMessage []byte } type TimeStampResp struct { Status PKIStatusInfo TimeStampToken pkcs7.ContentInfoSignedData `asn1:"optional"` } type PKIStatusInfo struct { Status int StatusString []string `asn1:"optional"` FailInfo asn1.BitString `asn1:"optional"` } type TSTInfo struct { Version int Policy asn1.ObjectIdentifier MessageImprint MessageImprint SerialNumber *big.Int GenTime asn1.RawValue Accuracy Accuracy `asn1:"optional"` Ordering bool `asn1:"optional,default:false"` Nonce *big.Int `asn1:"optional"` TSA GeneralName `asn1:"optional,implicit,tag:0"` Extensions []pkix.Extension `asn1:"optional,implicit,tag:1"` } func (i *TSTInfo) SigningTime() (time.Time, error) { return pkcs7.ParseTime(i.GenTime) } type Accuracy struct { Seconds int `asn1:"optional"` Millis int `asn1:"optional,tag:0"` Micros int `asn1:"optional,tag:1"` } type GeneralName struct { // See RFC 3280 Value asn1.RawValue } relic-7.6.1/lib/pkcs9/timestampcache/000077500000000000000000000000001455105530300174125ustar00rootroot00000000000000relic-7.6.1/lib/pkcs9/timestampcache/timestampcache.go000066400000000000000000000052571455105530300227410ustar00rootroot00000000000000// Copyright © SAS Institute Inc. // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package timestampcache import ( "context" "crypto/sha256" "fmt" "time" "github.com/bradfitz/gomemcache/memcache" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promauto" "github.com/rs/zerolog/log" "github.com/sassoftware/relic/v7/lib/pkcs7" "github.com/sassoftware/relic/v7/lib/pkcs9" ) const ( memcacheTimeout = 1 * time.Second memcacheExpiry = 7 * 24 * time.Hour ) var metricHits = promauto.NewCounterVec( prometheus.CounterOpts{ Name: "timestamper_cache", Help: "Timestamper cache hit and miss count", }, []string{"result"}, ) type timestampCache struct { Timestamper pkcs9.Timestamper Memcache *memcache.Client } func New(t pkcs9.Timestamper, servers []string) (pkcs9.Timestamper, error) { selector := new(memcache.ServerList) if err := selector.SetServers(servers...); err != nil { return nil, fmt.Errorf("parsing memcache servers: %w", err) } mc := memcache.NewFromSelector(selector) mc.Timeout = memcacheTimeout return ×tampCache{t, mc}, nil } func (c *timestampCache) Timestamp(ctx context.Context, req *pkcs9.Request) (*pkcs7.ContentInfoSignedData, error) { key := cacheKey(req) item, err := c.Memcache.Get(key) if err == nil { token, err := pkcs7.Unmarshal(item.Value) if err == nil { metricHits.WithLabelValues("hit").Inc() return token, nil } log.Warn().Err(err).Str("key", key).Msg("failed to parse cached value for timestamp") // bad cached value, fall through } token, err := c.Timestamper.Timestamp(ctx, req) if err == nil { blob, err := token.Marshal() if err != nil { return nil, err } if err := c.Memcache.Set(&memcache.Item{ Key: key, Value: blob, Expiration: int32(memcacheExpiry / time.Second), }); err != nil { log.Warn().Err(err).Msg("failed to save cached value for timestamp") } metricHits.WithLabelValues("miss").Inc() } return token, err } func cacheKey(req *pkcs9.Request) string { d := sha256.New() d.Write(req.EncryptedDigest) prefix := "pkcs9" if req.Legacy { prefix = "msft" } return fmt.Sprintf("%s-%d-%x", prefix, req.Hash, d.Sum(nil)) } relic-7.6.1/lib/pkcs9/tsclient/000077500000000000000000000000001455105530300162505ustar00rootroot00000000000000relic-7.6.1/lib/pkcs9/tsclient/tsclient.go000066400000000000000000000101061455105530300204220ustar00rootroot00000000000000// Copyright © SAS Institute Inc. // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package tsclient import ( "context" "crypto/tls" "errors" "fmt" "io" "log" "net/http" "time" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promauto" "github.com/prometheus/client_golang/prometheus/promhttp" "github.com/sassoftware/relic/v7/config" "github.com/sassoftware/relic/v7/lib/pkcs7" "github.com/sassoftware/relic/v7/lib/pkcs9" "github.com/sassoftware/relic/v7/lib/pkcs9/ratelimit" "github.com/sassoftware/relic/v7/lib/pkcs9/timestampcache" "github.com/sassoftware/relic/v7/lib/x509tools" ) type tsClient struct { conf *config.TimestampConfig client *http.Client } var ( buckets = []float64{.01, .025, .05, .1, .25, .5, 1, 2.5, 5, 10} metricCount = promauto.NewCounterVec( prometheus.CounterOpts{ Name: "timestamper_request_count", Help: "Outcome of timestamper requests", }, []string{"code"}, ) metricDuration = promauto.NewHistogramVec( prometheus.HistogramOpts{ Name: "timestamper_request_duration_seconds", Help: "Histogram of timestamper request durations", Buckets: buckets, }, nil, ) ) func New(conf *config.TimestampConfig) (t pkcs9.Timestamper, err error) { tlsconf := &tls.Config{} if err := x509tools.LoadCertPool(conf.CaCert, tlsconf); err != nil { return nil, err } client := &http.Client{ Timeout: time.Second * time.Duration(conf.Timeout), Transport: &http.Transport{ TLSClientConfig: tlsconf, }, } client.Transport = promhttp.InstrumentRoundTripperCounter(metricCount, client.Transport) client.Transport = promhttp.InstrumentRoundTripperDuration(metricDuration, client.Transport) t = tsClient{conf, client} if conf.RateLimit != 0 { t = ratelimit.New(t, conf.RateLimit, conf.RateBurst) } if len(conf.Memcache) != 0 { t, err = timestampcache.New(t, conf.Memcache) if err != nil { return nil, err } } return } func (c tsClient) Timestamp(ctx context.Context, req *pkcs9.Request) (*pkcs7.ContentInfoSignedData, error) { var urls []string if req.Legacy { urls = c.conf.MsURLs if len(urls) == 0 { return nil, errors.New("timestamp.msurls is empty") } } else { urls = c.conf.URLs if len(urls) == 0 { return nil, errors.New("timestamp.urls is empty") } } imprint := req.EncryptedDigest if !req.Legacy { d := req.Hash.New() d.Write(imprint) imprint = d.Sum(nil) } var err error for _, url := range urls { if err != nil { log.Printf("warning: timestamping failed: %s\n trying next server %s...\n", err, url) } var token *pkcs7.ContentInfoSignedData token, err = c.do(ctx, url, req, imprint) if err == nil { return token, nil } if ctx.Err() != nil { return nil, err } } return nil, fmt.Errorf("timestamping failed: %w", err) } func (c tsClient) do(ctx context.Context, url string, req *pkcs9.Request, imprint []byte) (*pkcs7.ContentInfoSignedData, error) { var msg *pkcs9.TimeStampReq var httpReq *http.Request var err error if !req.Legacy { msg, httpReq, err = pkcs9.NewRequest(url, req.Hash, imprint) } else { httpReq, err = pkcs9.NewLegacyRequest(url, imprint) } if err != nil { return nil, err } httpReq.Header.Set("User-Agent", config.UserAgent) resp, err := c.client.Do(httpReq.WithContext(ctx)) if err != nil { return nil, err } body, err := io.ReadAll(resp.Body) resp.Body.Close() if err != nil { return nil, err } else if resp.StatusCode != 200 { return nil, fmt.Errorf("%s: HTTP %s\n%s", url, resp.Status, body) } if req.Legacy { return pkcs9.ParseLegacyResponse(body) } return msg.ParseResponse(body) } relic-7.6.1/lib/pkcs9/verify.go000066400000000000000000000057031455105530300162630ustar00rootroot00000000000000// // Copyright (c) SAS Institute Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package pkcs9 import ( "crypto" "crypto/hmac" "crypto/x509" "errors" "fmt" "time" "github.com/sassoftware/relic/v7/lib/pkcs7" "github.com/sassoftware/relic/v7/lib/x509tools" ) // Verify that the digest (imprint) in a timestamp token matches the given data func (i MessageImprint) Verify(data []byte) error { hash, err := x509tools.PkixDigestToHashE(i.HashAlgorithm) if err != nil { return fmt.Errorf("pkcs9: %w", err) } w := hash.New() w.Write(data) digest := w.Sum(nil) if !hmac.Equal(digest, i.HashedMessage) { return errors.New("pkcs9: digest check failed") } return nil } // Verify a timestamp token using external data func Verify(tst *pkcs7.ContentInfoSignedData, data []byte, certs []*x509.Certificate) (*CounterSignature, error) { if len(tst.Content.SignerInfos) != 1 { return nil, errors.New("timestamp should have exactly one SignerInfo") } tsi := tst.Content.SignerInfos[0] tsicerts, certErr := tst.Content.Certificates.Parse() if len(tsicerts) != 0 { // keep both sets of certs just in case certs = append(certs, tsicerts...) } // verify the imprint in the TSTInfo tstinfo, err := unpackTokenInfo(tst) if err != nil { return nil, err } if err := tstinfo.MessageImprint.Verify(data); err != nil { return nil, fmt.Errorf("verifying timestamp imprint: %w", err) } imprintHash, _ := x509tools.PkixDigestToHash(tstinfo.MessageImprint.HashAlgorithm) // now the signature is over the TSTInfo blob verifyBlob, err := tst.Content.ContentInfo.Bytes() if err != nil { return nil, err } return finishVerify(&tsi, verifyBlob, certs, imprintHash, tstinfo, certErr) } type timeSource interface { SigningTime() (time.Time, error) } func finishVerify(tsi *pkcs7.SignerInfo, blob []byte, certs []*x509.Certificate, hash crypto.Hash, timeSource timeSource, certErr error) (*CounterSignature, error) { cert, err := tsi.Verify(blob, false, certs) if err != nil { if errors.As(err, &pkcs7.MissingCertificateError{}) && certErr != nil { // surface saved parse error return nil, certErr } return nil, err } signingTime, err := timeSource.SigningTime() if err != nil { return nil, fmt.Errorf("parsing timestamp: %w", err) } return &CounterSignature{ Signature: pkcs7.Signature{ SignerInfo: tsi, Certificate: cert, Intermediates: certs, CertError: certErr, }, Hash: hash, SigningTime: signingTime, }, nil } relic-7.6.1/lib/readercounter/000077500000000000000000000000001455105530300162345ustar00rootroot00000000000000relic-7.6.1/lib/readercounter/counter.go000066400000000000000000000017261455105530300202500ustar00rootroot00000000000000// // Copyright (c) SAS Institute Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package readercounter import "io" // Wraps a Reader and counts how many bytes are read from it type ReaderCounter struct { R io.Reader // underlying Reader N int64 // number of bytes read } func New(r io.Reader) *ReaderCounter { return &ReaderCounter{R: r} } func (c *ReaderCounter) Read(d []byte) (int, error) { n, err := c.R.Read(d) c.N += int64(n) return n, err } relic-7.6.1/lib/redblack/000077500000000000000000000000001455105530300151415ustar00rootroot00000000000000relic-7.6.1/lib/redblack/redblack.go000066400000000000000000000044131455105530300172410ustar00rootroot00000000000000// // Copyright (c) SAS Institute Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // // Simple, incomplete red-black tree implementation meant only to rebuild the // directory tree of a CDF file. package redblack func New(less LessFunc) *Tree { return &Tree{Less: less} } func (t *Tree) Insert(item interface{}) { node := &Node{Item: item, Less: t.Less} t.Root = t.Root.insert(node) t.Count++ } func (t *Tree) Nodes() []*Node { if t.Root == nil { return nil } ret := make([]*Node, 0, t.Count) stack := []*Node{t.Root} for len(stack) > 0 { i := len(stack) - 1 node := stack[i] stack = stack[:i] ret = append(ret, node) if node.Children[0] != nil { stack = append(stack, node.Children[0]) } if node.Children[1] != nil { stack = append(stack, node.Children[1]) } } return ret } type LessFunc func(i, j interface{}) bool type Tree struct { Root *Node Less LessFunc Count uint } type Node struct { Item interface{} Less LessFunc Red bool Children [2]*Node } func (n *Node) isRed() bool { return n != nil && n.Red } func (n *Node) rotate(dir int) *Node { a := n.Children[1-dir] n.Children[1-dir] = a.Children[dir] a.Children[dir] = n n.Red = true a.Red = false return a } func (n *Node) insert(a *Node) *Node { if n == nil { return a } dir := 0 if n.Less(n.Item, a.Item) { dir = 1 } n.Children[dir] = n.Children[dir].insert(a) if !n.Children[dir].isRed() { return n } else if n.Children[1-dir].isRed() { n.Red = true n.Children[0].Red = false n.Children[1].Red = false return n } else if n.Children[dir].Children[dir].isRed() { return n.rotate(1 - dir) } else if n.Children[dir].Children[1-dir].isRed() { n.Children[dir] = n.Children[dir].rotate(dir) return n.rotate(1 - dir) } else { return n } } relic-7.6.1/lib/signappx/000077500000000000000000000000001455105530300152235ustar00rootroot00000000000000relic-7.6.1/lib/signappx/blockmap.go000066400000000000000000000146511455105530300173510ustar00rootroot00000000000000// // Copyright (c) SAS Institute Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package signappx import ( "archive/zip" "crypto" "crypto/hmac" "encoding/base64" "encoding/xml" "errors" "fmt" "io" "strings" "github.com/sassoftware/relic/v7/lib/zipslicer" ) const blockMapSize = 64 * 1024 var hashAlgs = map[crypto.Hash]string{ crypto.SHA256: "http://www.w3.org/2001/04/xmlenc#sha256", crypto.SHA384: "http://www.w3.org/2001/04/xmldsig-more#sha384", crypto.SHA512: "http://www.w3.org/2001/04/xmlenc#sha512", } var noHashFiles = map[string]bool{ appxSignature: true, appxCodeIntegrity: true, appxContentTypes: true, appxBlockMap: true, } type blockMap struct { XMLName xml.Name `xml:"http://schemas.microsoft.com/appx/2010/blockmap BlockMap"` HashMethod string `xml:",attr"` File []blockFile Hash crypto.Hash `xml:"-"` unverifiedSizes bool } type blockFile struct { Name string `xml:",attr"` Size uint64 `xml:",attr"` LfhSize int `xml:",attr"` Block []block } type block struct { Hash string `xml:",attr"` Size uint64 `xml:",attr,omitempty"` } func verifyBlockMap(inz *zip.Reader, files zipFiles, skipDigests bool) error { isBundle := files[bundleManifestFile] != nil zf := files[appxBlockMap] if zf == nil { return errors.New("missing block map") } blob, err := readZipFile(zf) if err != nil { return err } var bm blockMap if err := xml.Unmarshal(blob, &bm); err != nil { return fmt.Errorf("error parsing block map: %w", err) } var hash crypto.Hash for hash2, alg := range hashAlgs { if alg == bm.HashMethod { hash = hash2 break } } if hash == 0 { return errors.New("unsupported hash in block map") } bm.Hash = hash bmfiles := bm.File for _, zf := range inz.File { if noHashFiles[zf.Name] || (isBundle && strings.HasSuffix(zf.Name, ".appx")) { continue } if len(bmfiles) == 0 { return fmt.Errorf("blockmap: unhashed zip file %s", zf.Name) } bmf := bmfiles[0] bmfiles = bmfiles[1:] name := zipToDos(zf.Name) if bmf.Name != name { return fmt.Errorf("blockmap: file mismatch: %s != %s", bmf.Name, name) } else if bmf.Size != zf.UncompressedSize64 { return fmt.Errorf("blockmap: file mismatch: %s: size %d != %d", name, bmf.Size, zf.UncompressedSize64) } if len(bmf.Block) != int((zf.UncompressedSize64+blockMapSize-1)/blockMapSize) { return errors.New("blockmap: file mismatch") } if skipDigests { continue } r, err := zf.Open() if err != nil { return err } remaining := zf.UncompressedSize64 for i, block := range bmf.Block { count := remaining if count > blockMapSize { count = blockMapSize } remaining -= count d := hash.New() if _, err := io.CopyN(d, r, int64(count)); err != nil { return err } calc := d.Sum(nil) expected, err := base64.StdEncoding.DecodeString(block.Hash) if err != nil { return fmt.Errorf("blockmap: %w", err) } if !hmac.Equal(calc, expected) { return fmt.Errorf("blockmap: digest mismatch for %s block %d: calculated %x != found %x", name, i, calc, expected) } } if err := r.Close(); err != nil { return err } if remaining > 0 { return errors.New("blockmap: file mismatch") } } return nil } func (b *blockMap) SetHash(hash crypto.Hash) error { alg := hashAlgs[hash] if alg == "" { return errors.New("unsupported hash algorithm") } b.HashMethod = alg b.Hash = hash return nil } // Copy compressed sizes from the old blockmap since I can't figure out how // they come up with the numbers and the thing won't install if they're // wrong... func (b *blockMap) CopySizes(blob []byte) error { var orig blockMap if err := xml.Unmarshal(blob, &orig); err != nil { return fmt.Errorf("error parsing block map: %w", err) } for i, oldf := range orig.File { zipName := dosToZip(oldf.Name) if zipName == appxManifest || zipName == bundleManifestFile { // The only file that gets changed by us. It's stored with no // compression to avoid screwing up the sizes. continue } else if i >= len(b.File) { return errors.New("old block map has too many files") } newf := &b.File[i] if newf.Name != oldf.Name { return fmt.Errorf("old block map doesn't match new: %s", oldf.Name) } for j, oldblock := range oldf.Block { newf.Block[j].Size = oldblock.Size } } b.unverifiedSizes = false return nil } func (b *blockMap) AddFile(f *zipslicer.File, raw, cooked io.Writer) error { bmf := blockFile{Name: zipToDos(f.Name)} lfh, err := f.GetLocalHeader() if err != nil { return fmt.Errorf("hashing zip metadata: %w", err) } bmf.LfhSize = len(lfh) if raw != nil { if _, err := raw.Write(lfh); err != nil { return err } } rc, err := f.OpenAndTeeRaw(raw) if err != nil { return fmt.Errorf("hashing zip metadata: %w", err) } // Copy 64K of uncompressed data at a time, adding block elements as we go for { d := b.Hash.New() w := io.Writer(d) if cooked != nil { w = io.MultiWriter(d, cooked) } n, err := io.CopyN(w, rc, blockMapSize) if n > 0 { bmf.Size += uint64(n) hash := base64.StdEncoding.EncodeToString(d.Sum(nil)) bmf.Block = append(bmf.Block, block{Hash: hash}) } if err == io.EOF { break } else if err != nil { return err } } if err := rc.Close(); err != nil { return err } dd, err := f.GetDataDescriptor() if err != nil { return fmt.Errorf("hashing zip metadata: %w", err) } if raw != nil { if _, err := raw.Write(dd); err != nil { return err } } if !(noHashFiles[f.Name] || strings.HasSuffix(f.Name, ".appx")) { if f.Method != zip.Store { b.unverifiedSizes = true } b.File = append(b.File, bmf) } return nil } func (b *blockMap) Marshal() ([]byte, error) { if b.unverifiedSizes { return nil, errors.New("found compressed files not already in blockmap") } return marshalXML(b, false) } func zipToDos(name string) string { return strings.ReplaceAll(name, "/", "\\") } func dosToZip(name string) string { return strings.ReplaceAll(name, "\\", "/") } relic-7.6.1/lib/signappx/bundle.go000066400000000000000000000101421455105530300170210ustar00rootroot00000000000000// // Copyright (c) SAS Institute Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package signappx import ( "archive/zip" "bytes" "crypto/x509" "encoding/xml" "errors" "fmt" "io" "strings" "github.com/beevik/etree" "github.com/sassoftware/relic/v7/lib/x509tools" ) type bundleManifest struct { XMLName xml.Name `xml:"http://schemas.microsoft.com/appx/2013/bundle Bundle"` SchemaVersion string `xml:",attr"` Identity appxIdentity Packages []bundlePackage `xml:"Packages>Package"` Etree *etree.Document `xml:"-"` } type bundlePackage struct { Type string `xml:",attr"` Version string `xml:",attr"` Architecture string `xml:",attr"` FileName string `xml:",attr"` Offset int64 `xml:",attr"` Size uint64 `xml:",attr"` } func verifyBundle(r io.ReaderAt, files zipFiles, sig *AppxSignature, skipDigests bool) error { blob, err := readZipFile(files[bundleManifestFile]) if err != nil { return fmt.Errorf("bundle manifest: %w", err) } var bundle bundleManifest if err := xml.Unmarshal(blob, &bundle); err != nil { return fmt.Errorf("bundle manifest: %w", err) } packages := make(map[string]int) for i, pkg := range bundle.Packages { packages[pkg.FileName] = i } sig.Bundled = make(map[string]*AppxSignature) publisher := x509tools.FormatPkixName(sig.Signature.Certificate.RawSubject, x509tools.NameStyleMsOsco) if bundle.Identity.Publisher != publisher { return fmt.Errorf("bundle manifest: publisher identity mismatch:\nexpected: %s\nactual: %s", publisher, bundle.Identity.Publisher) } for _, zf := range files { if !strings.HasSuffix(zf.Name, ".appx") { continue } if zf.Method != zip.Store { return errors.New("bundle manifest: contains compressed appx") } dosname := strings.ReplaceAll(zf.Name, "/", "\\") pkgIndex, ok := packages[dosname] if !ok { return fmt.Errorf("bundle manifest: missing file %s", zf.Name) } packages[dosname] = -1 // mark as seen pkg := bundle.Packages[pkgIndex] offset, err := zf.DataOffset() if err != nil { return fmt.Errorf("bundle manifest: %w", err) } if pkg.Offset != offset { return fmt.Errorf("bundle manifest: %s claimed offset of %d but actual offset is %d", zf.Name, pkg.Offset, offset) } else if pkg.Size != zf.UncompressedSize64 { return fmt.Errorf("bundle manifest: %s claimed size of %d but actual size is %d", zf.Name, pkg.Size, zf.UncompressedSize64) } nested := io.NewSectionReader(r, offset, int64(zf.UncompressedSize64)) nestedSig, err := Verify(nested, int64(zf.UncompressedSize64), skipDigests) if err != nil { return fmt.Errorf("bundled file %s: %w", zf.Name, err) } if !bytes.Equal(nestedSig.Signature.Certificate.Raw, sig.Signature.Certificate.Raw) { return fmt.Errorf("bundled file %s signed by different publisher", zf.Name) } sig.Bundled[zf.Name] = nestedSig } for name, unseen := range packages { if unseen >= 0 { return fmt.Errorf("bundle missing file: %s", name) } } return nil } func parseBundle(blob []byte) (*bundleManifest, error) { manifest := new(bundleManifest) if err := xml.Unmarshal(blob, manifest); err != nil { return nil, err } manifest.Etree = etree.NewDocument() if err := manifest.Etree.ReadFromBytes(blob); err != nil { return nil, err } return manifest, nil } func (m *bundleManifest) SetPublisher(cert *x509.Certificate) { subj := x509tools.FormatPkixName(cert.RawSubject, x509tools.NameStyleMsOsco) m.Identity.Publisher = subj el := m.Etree.FindElement("Bundle/Identity") if el != nil { el.CreateAttr("Publisher", subj) } } func (m *bundleManifest) Marshal() ([]byte, error) { return m.Etree.WriteToBytes() } relic-7.6.1/lib/signappx/contenttypes.go000066400000000000000000000073621455105530300203210ustar00rootroot00000000000000// // Copyright (c) SAS Institute Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package signappx import ( "encoding/xml" "path" "sort" ) var defaultExtensions = map[string]string{ "dll": "application/x-msdownload", "exe": "application/x-msdownload", "png": "image/png", "xml": "application/vnd.ms-appx.manifest+xml", "appx": "application/vnd.ms-appx", } var defaultOverrides = map[string]string{ "/AppxBlockMap.xml": "application/vnd.ms-appx.blockmap+xml", "/AppxSignature.p7x": "application/vnd.ms-appx.signature", "/AppxMetadata/CodeIntegrity.cat": "application/vnd.ms-pkiseccat", } const ( octetStreamType = "application/octet-stream" bundleManifestType = "application/vnd.ms-appx.bundlemanifest+xml" ) type ContentTypes struct { ByExt map[string]string ByOverride map[string]string } type xmlContentTypes struct { XMLName xml.Name `xml:"http://schemas.openxmlformats.org/package/2006/content-types Types"` Default []contentTypeDefault Override []contentTypeOverride } type contentTypeDefault struct { Extension string `xml:",attr"` ContentType string `xml:",attr"` } type contentTypeOverride struct { PartName string `xml:",attr"` ContentType string `xml:",attr"` } func NewContentTypes() *ContentTypes { return &ContentTypes{ ByExt: make(map[string]string), ByOverride: make(map[string]string), } } func (c *ContentTypes) Parse(blob []byte) error { var xct xmlContentTypes if err := xml.Unmarshal(blob, &xct); err != nil { return err } for _, def := range xct.Default { c.ByExt[def.Extension] = def.ContentType } for _, ovr := range xct.Override { c.ByOverride[ovr.PartName] = ovr.ContentType } return nil } func (c *ContentTypes) Add(name string) { if name == bundleManifestFile { c.ByExt["xml"] = bundleManifestType return } oname := "/" + name if ctype := defaultOverrides[oname]; ctype != "" { c.ByOverride[oname] = ctype return } else if ctype := c.ByOverride[oname]; ctype != "" { return } ext := path.Ext(path.Base(name)) if ext[0] == '.' { ext = ext[1:] if ctype := defaultExtensions[ext]; ctype != "" { c.ByExt[ext] = ctype } else if ctype := c.ByExt[ext]; ctype != "" { return } else { c.ByExt[ext] = octetStreamType } } else { c.ByOverride[oname] = octetStreamType } } func (c *ContentTypes) Find(name string) string { oname := "/" + name if ctype := c.ByOverride[oname]; ctype != "" { return ctype } ext := path.Ext(path.Base(name)) if ext[0] == '.' { if ctype := c.ByExt[ext[1:]]; ctype != "" { return ctype } } return "" } func (c *ContentTypes) Marshal() ([]byte, error) { var xct xmlContentTypes extnames := make([]string, 0, len(c.ByExt)) for name := range c.ByExt { extnames = append(extnames, name) } sort.Strings(extnames) for _, name := range extnames { xct.Default = append(xct.Default, contentTypeDefault{ Extension: name, ContentType: c.ByExt[name], }) } ovrnames := make([]string, 0, len(c.ByOverride)) for name := range c.ByOverride { ovrnames = append(ovrnames, name) } sort.Strings(ovrnames) for _, name := range ovrnames { xct.Override = append(xct.Override, contentTypeOverride{ PartName: name, ContentType: c.ByOverride[name], }) } return marshalXML(xct, true) } relic-7.6.1/lib/signappx/manifest.go000066400000000000000000000052701455105530300173640ustar00rootroot00000000000000// // Copyright (c) SAS Institute Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package signappx import ( "bytes" "crypto/x509" "encoding/xml" "fmt" "github.com/beevik/etree" "github.com/sassoftware/relic/v7/lib/x509tools" ) type appxPackage struct { XMLName xml.Name Identity appxIdentity DisplayName string `xml:"Properties>DisplayName"` PublisherDisplayName string `xml:"Properties>PublisherDisplayName"` Logo string `xml:"Properties>Logo"` Etree *etree.Document `xml:"-"` } type appxIdentity struct { Name string `xml:",attr"` Publisher string `xml:",attr"` Version string `xml:",attr"` ProcessorArchitecture string `xml:",attr"` } func parseManifest(blob []byte) (*appxPackage, error) { manifest := new(appxPackage) if err := xml.Unmarshal(blob, manifest); err != nil { return nil, err } manifest.Etree = etree.NewDocument() if err := manifest.Etree.ReadFromBytes(blob); err != nil { return nil, err } return manifest, nil } func checkManifest(files zipFiles, sig *AppxSignature) error { blob, err := readZipFile(files[appxManifest]) if err != nil { return fmt.Errorf("appx manifest: %w", err) } var manifest appxPackage if err := xml.Unmarshal(blob, &manifest); err != nil { return fmt.Errorf("appx manifest: %w", err) } sig.Name = manifest.Identity.Name sig.DisplayName = manifest.DisplayName sig.Version = manifest.Identity.Version publisher := x509tools.FormatPkixName(sig.Signature.Certificate.RawSubject, x509tools.NameStyleMsOsco) if manifest.Identity.Publisher != publisher { return fmt.Errorf("appx manifest: publisher identity mismatch:\nexpected: %s\nactual: %s", publisher, manifest.Identity.Publisher) } return nil } func (m *appxPackage) SetPublisher(cert *x509.Certificate) { subj := x509tools.FormatPkixName(cert.RawSubject, x509tools.NameStyleMsOsco) m.Identity.Publisher = subj el := m.Etree.FindElement("Package/Identity") if el != nil { el.CreateAttr("Publisher", subj) } } func (m *appxPackage) Marshal() ([]byte, error) { b, err := m.Etree.WriteToBytes() if err != nil { return nil, err } return bytes.ReplaceAll(b, []byte{'\n'}, []byte{'\r', '\n'}), nil } relic-7.6.1/lib/signappx/sign.go000066400000000000000000000126721455105530300165220ustar00rootroot00000000000000// // Copyright (c) SAS Institute Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package signappx import ( "bytes" "context" "crypto/x509" "errors" "github.com/sassoftware/relic/v7/lib/authenticode" "github.com/sassoftware/relic/v7/lib/binpatch" "github.com/sassoftware/relic/v7/lib/certloader" "github.com/sassoftware/relic/v7/lib/pkcs9" ) var ( SpcUUIDSipInfoAppx = []byte{0x4B, 0xDF, 0xC5, 0x0A, 0x07, 0xCE, 0xE2, 0x4D, 0xB7, 0x6E, 0x23, 0xC8, 0x39, 0xA0, 0x9F, 0xD1} appxSipInfo = authenticode.SpcSipInfo{A: 0x1010000, UUID: SpcUUIDSipInfoAppx} ) func (i *AppxDigest) Sign(ctx context.Context, cert *certloader.Certificate, params *authenticode.OpusParams) (patch *binpatch.PatchSet, priSig, catSig *pkcs9.TimestampedSignature, err error) { if err := i.writeManifest(cert.Leaf); err != nil { return nil, nil, nil, err } if err := i.writeBlockMap(); err != nil { return nil, nil, nil, err } if err := i.writeContentTypes(); err != nil { return nil, nil, nil, err } catSig, err = i.writeCodeIntegrity(ctx, cert, params) if err != nil { return nil, nil, nil, err } ts, err := i.writeSignature(ctx, cert, params) if err != nil { return nil, nil, nil, err } w := &i.patchBuf if err := i.outz.WriteDirectory(w, w, true); err != nil { return nil, nil, nil, err } patch = binpatch.New() patch.Add(i.patchStart, i.patchLen, i.patchBuf.Bytes()) return patch, ts, catSig, nil } func (i *AppxDigest) addZipEntry(name string, contents []byte) error { // Don't deflate the manifest because I can't figure out how to correctly // calculate block sizes. The blocks for the rest of the files can be // cribbed from the old blockmap. deflate := !(name == appxManifest || name == bundleManifestFile) // Don't use descriptors for the 3 files that signtool adds, it seems to // aggrevate the generic verifier although the appx works fine. useDesc := !(name == appxContentTypes || name == appxCodeIntegrity || name == appxSignature) f, err := i.outz.NewFile(name, nil, contents, &i.patchBuf, i.mtime, deflate, useDesc) if err != nil { return err } return i.blockMap.AddFile(f, i.axpc, nil) } func (i *AppxDigest) writeManifest(leaf *x509.Certificate) error { if i.manifest != nil { i.manifest.SetPublisher(leaf) manifest, err := i.manifest.Marshal() if err != nil { return err } return i.addZipEntry(appxManifest, manifest) } else if i.bundle != nil { i.bundle.SetPublisher(leaf) manifest, err := i.bundle.Marshal() if err != nil { return err } return i.addZipEntry(bundleManifestFile, manifest) } return errors.New("manifest not found") } func (i *AppxDigest) writeBlockMap() error { blockmap, err := i.blockMap.Marshal() if err != nil { return err } if err := i.addZipEntry(appxBlockMap, blockmap); err != nil { return err } d := i.Hash.New() d.Write(blockmap) i.axbm = d.Sum(nil) return nil } func (i *AppxDigest) writeContentTypes() error { for _, f := range i.outz.File { if f.Name != appxContentTypes { i.contentTypes.Add(f.Name) } } if len(i.peDigests) != 0 { i.contentTypes.Add(appxCodeIntegrity) } i.contentTypes.Add(appxSignature) ctypes, err := i.contentTypes.Marshal() if err != nil { return err } if err := i.addZipEntry(appxContentTypes, ctypes); err != nil { return err } d := i.Hash.New() d.Write(ctypes) i.axct = d.Sum(nil) return nil } func (i *AppxDigest) writeCodeIntegrity(ctx context.Context, cert *certloader.Certificate, params *authenticode.OpusParams) (*pkcs9.TimestampedSignature, error) { if len(i.peDigests) == 0 { return nil, nil } cat := authenticode.NewCatalog(i.Hash) for _, d := range i.peDigests { indirect, err := d.GetIndirect() if err != nil { return nil, err } if err := cat.Add(indirect); err != nil { return nil, err } } ts, err := cat.Sign(ctx, cert, params) if err != nil { return nil, err } catalog := ts.Raw if err := i.addZipEntry(appxCodeIntegrity, catalog); err != nil { return nil, err } d := i.Hash.New() d.Write(catalog) i.axci = d.Sum(nil) return ts, nil } func (i *AppxDigest) writeSignature(ctx context.Context, cert *certloader.Certificate, params *authenticode.OpusParams) (*pkcs9.TimestampedSignature, error) { axcd := i.Hash.New() if err := i.outz.WriteDirectory(axcd, axcd, true); err != nil { return nil, err } digest := bytes.NewBuffer(make([]byte, 0, 4+5*(4+i.Hash.Size()))) digest.WriteString("APPX") digest.WriteString("AXPC") digest.Write(i.axpc.Sum(nil)) digest.WriteString("AXCD") digest.Write(axcd.Sum(nil)) digest.WriteString("AXCT") digest.Write(i.axct) digest.WriteString("AXBM") digest.Write(i.axbm) if len(i.axci) != 0 { digest.WriteString("AXCI") digest.Write(i.axci) } ts, err := authenticode.SignSip(ctx, digest.Bytes(), i.Hash, appxSipInfo, cert, params) if err != nil { return nil, err } pkcx := make([]byte, 4, 4+len(ts.Raw)) copy(pkcx, "PKCX") pkcx = append(pkcx, ts.Raw...) if err := i.addZipEntry(appxSignature, pkcx); err != nil { return nil, err } return ts, nil } relic-7.6.1/lib/signappx/structs.go000066400000000000000000000025611455105530300172650ustar00rootroot00000000000000// // Copyright (c) SAS Institute Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package signappx import ( "archive/zip" "crypto" "github.com/sassoftware/relic/v7/lib/authenticode" "github.com/sassoftware/relic/v7/lib/pkcs9" ) const ( appxSignature = "AppxSignature.p7x" appxCodeIntegrity = "AppxMetadata/CodeIntegrity.cat" appxBlockMap = "AppxBlockMap.xml" appxManifest = "AppxManifest.xml" appxContentTypes = "[Content_Types].xml" bundleManifestFile = "AppxMetadata/AppxBundleManifest.xml" ) type AppxSignature struct { Signature *pkcs9.TimestampedSignature Name, DisplayName string Version string IsBundle bool Hash crypto.Hash HashValues map[string][]byte Bundled map[string]*AppxSignature OpusInfo *authenticode.SpcSpOpusInfo } type zipFiles map[string]*zip.File relic-7.6.1/lib/signappx/tarappx.go000066400000000000000000000126771455105530300172460ustar00rootroot00000000000000// // Copyright (c) SAS Institute Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package signappx import ( "bytes" "crypto" "errors" "fmt" "hash" "io" "io/ioutil" "strings" "time" "github.com/sassoftware/relic/v7/lib/authenticode" "github.com/sassoftware/relic/v7/lib/zipslicer" ) type AppxDigest struct { Hash crypto.Hash blockMap blockMap manifest *appxPackage bundle *bundleManifest contentTypes *ContentTypes peDigests []*authenticode.PEDigest outz *zipslicer.Directory patchStart int64 patchLen int64 patchBuf bytes.Buffer mtime time.Time axpc hash.Hash axbm, axct, axci []byte } func DigestAppxTar(r io.Reader, hash crypto.Hash, doPageHash bool) (*AppxDigest, error) { info := &AppxDigest{ Hash: hash, axpc: hash.New(), contentTypes: NewContentTypes(), outz: &zipslicer.Directory{}, } if err := info.blockMap.SetHash(hash); err != nil { return nil, err } inz, err := zipslicer.ReadZipTar(r) if err != nil { return nil, err } // digest non-signature-related files copyf: for _, f := range inz.File { switch f.Name { case appxManifest, appxBlockMap, appxContentTypes, appxCodeIntegrity, appxSignature, bundleManifestFile: info.patchStart = int64(f.Offset) info.patchLen = inz.Size - info.patchStart break copyf default: info.mtime = f.ModTime() if err := info.digestFile(f, doPageHash); err != nil { return nil, err } if _, err := info.outz.AddFile(f); err != nil { return nil, err } } } idx := len(info.outz.File) // parse signature-related files for later for _, f := range inz.File[idx:] { blob, err := readSlicerFile(f) if err != nil { return nil, err } switch f.Name { case appxManifest: manifest, err := parseManifest(blob) if err != nil { return nil, err } info.manifest = manifest case bundleManifestFile: manifest, err := parseBundle(blob) if err != nil { return nil, err } info.bundle = manifest case appxBlockMap: if err := info.blockMap.CopySizes(blob); err != nil { return nil, err } case appxContentTypes: if err := info.contentTypes.Parse(blob); err != nil { return nil, err } case appxCodeIntegrity, appxSignature: // discard default: // regular files can't come after files we mangle return nil, fmt.Errorf("file %s is out of order", f.Name) } } if info.manifest == nil && info.bundle == nil { return nil, errors.New("missing manifest") } // drain the input to ensure the request is completely read before the response goes out if _, err := io.Copy(ioutil.Discard, r); err != nil { return nil, err } return info, nil } func readSlicerFile(f *zipslicer.File) ([]byte, error) { r, err := f.Open() if err != nil { return nil, err } blob, err := ioutil.ReadAll(r) if err != nil { return nil, err } return blob, r.Close() } // 4 different digests need to happen concurrently: // - AXPC for the raw zip headers and data // - blockmap 64KiB chunks of cooked data // - SHA1 and SHA256 digests over PE files for CodeIntegrity.cat func (i *AppxDigest) digestFile(f *zipslicer.File, doPageHash bool) error { var peWriters []io.WriteCloser var peResults []<-chan peDigestResult var sink io.Writer if strings.HasSuffix(f.Name, ".exe") || strings.HasSuffix(f.Name, ".dll") { // DigestPE wants a Reader so make a pipe for each one and sink data into the pipes peWriters, peResults = setupPeDigests(f.Name, i.Hash, doPageHash) defer func() { for _, w := range peWriters { w.Close() } }() mw := make([]io.Writer, len(peWriters)) for i, w := range peWriters { mw[i] = w } sink = io.MultiWriter(mw...) } if err := i.blockMap.AddFile(f, i.axpc, sink); err != nil { return err } if peWriters != nil { for _, w := range peWriters { w.Close() } for _, ch := range peResults { result := <-ch if result.err != nil { return result.err } i.peDigests = append(i.peDigests, result.digest) } } return nil } type peDigestResult struct { digest *authenticode.PEDigest err error } func setupPeDigests(name string, hash crypto.Hash, doPageHash bool) (writers []io.WriteCloser, results []<-chan peDigestResult) { w, r := setupPeDigest(name, hash, doPageHash) writers = append(writers, w) results = append(results, r) if hash != crypto.SHA1 { // SHA1 for catalog compatibility w, r := setupPeDigest(name, crypto.SHA1, doPageHash) writers = append(writers, w) results = append(results, r) } return } func setupPeDigest(name string, hash crypto.Hash, doPageHash bool) (io.WriteCloser, <-chan peDigestResult) { r, w := io.Pipe() ch := make(chan peDigestResult, 1) go func() { digest, err := authenticode.DigestPE(r, hash, doPageHash) if err != nil { err = fmt.Errorf("failed to update CodeIntegrity catalog for %s: %w", name, err) } ch <- peDigestResult{digest, err} _ = r.CloseWithError(err) close(ch) }() return w, ch } relic-7.6.1/lib/signappx/verify.go000066400000000000000000000133171455105530300170630ustar00rootroot00000000000000// // Copyright (c) SAS Institute Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package signappx import ( "archive/zip" "bytes" "crypto/hmac" "errors" "fmt" "io" "github.com/sassoftware/relic/v7/lib/authenticode" "github.com/sassoftware/relic/v7/lib/pkcs7" "github.com/sassoftware/relic/v7/lib/pkcs9" "github.com/sassoftware/relic/v7/lib/x509tools" "github.com/sassoftware/relic/v7/signers/sigerrors" ) func Verify(r io.ReaderAt, size int64, skipDigests bool) (*AppxSignature, error) { inz, err := zip.NewReader(r, size) if err != nil { return nil, err } files := make(zipFiles, len(inz.File)) for _, file := range inz.File { files[file.Name] = file } sig, err := readSignature(files[appxSignature]) if err != nil { return nil, err } sig.IsBundle = files[bundleManifestFile] != nil if err := verifyFile(files, sig, "AXBM", appxBlockMap); err != nil { return nil, err } if err := verifyFile(files, sig, "AXCI", appxCodeIntegrity); err != nil { return nil, err } if err := verifyFile(files, sig, "AXCT", appxContentTypes); err != nil { return nil, err } if err := verifyBlockMap(inz, files, skipDigests); err != nil { return nil, err } if err := verifyCatalog(files[appxCodeIntegrity], sig); err != nil { return nil, err } if err := verifyMeta(r, size, sig, skipDigests); err != nil { return nil, err } if sig.IsBundle { if err := verifyBundle(r, files, sig, skipDigests); err != nil { return nil, err } } else { if err := checkManifest(files, sig); err != nil { return nil, err } } return sig, nil } func readSignature(zf *zip.File) (*AppxSignature, error) { if zf == nil { return nil, sigerrors.NotSignedError{Type: "appx"} } blob, err := readZipFile(zf) if err != nil { return nil, err } if !bytes.HasPrefix(blob, []byte("PKCX")) { return nil, errors.New("invalid appx signature") } psd, err := pkcs7.Unmarshal(blob[4:]) if err != nil { return nil, fmt.Errorf("invalid appx signature: %w", err) } if !psd.Content.ContentInfo.ContentType.Equal(authenticode.OidSpcIndirectDataContent) { return nil, fmt.Errorf("invalid appx signature: %s", "not an authenticode signature") } pksig, err := psd.Content.Verify(nil, false) if err != nil { return nil, fmt.Errorf("invalid appx signature: %w", err) } ts, err := pkcs9.VerifyOptionalTimestamp(pksig) if err != nil { return nil, err } indirect := new(authenticode.SpcIndirectDataContentMsi) if err := psd.Content.ContentInfo.Unmarshal(indirect); err != nil { return nil, fmt.Errorf("invalid appx signature: %w", err) } hash, err := x509tools.PkixDigestToHashE(indirect.MessageDigest.DigestAlgorithm) if err != nil { return nil, err } digests := indirect.MessageDigest.Digest if !bytes.HasPrefix(digests, []byte("APPX")) { return nil, errors.New("invalid appx signature") } digests = digests[4:] digestmap := make(map[string][]byte) for len(digests) > 0 { if len(digests) < 4+hash.Size() { return nil, errors.New("invalid appx signature") } name := string(digests[:4]) digestmap[name] = digests[4 : 4+hash.Size()] digests = digests[4+hash.Size():] } opus, err := authenticode.GetOpusInfo(ts.SignerInfo) if err != nil { return nil, err } return &AppxSignature{ Signature: &ts, Hash: hash, HashValues: digestmap, OpusInfo: opus, }, nil } func verifyFile(files zipFiles, sig *AppxSignature, tag, name string) error { expected := sig.HashValues[tag] zf := files[name] if zf == nil { if expected == nil { return nil } return fmt.Errorf("appx missing signed file: %s", name) } else if expected == nil { return fmt.Errorf("appx missing signature for file: %s", name) } r, err := zf.Open() if err != nil { return err } d := sig.Hash.New() if _, err := io.Copy(d, r); err != nil { return err } if err := r.Close(); err != nil { return err } calc := d.Sum(nil) if !hmac.Equal(calc, expected) { return fmt.Errorf("appx digest mismatch for %s: calculated %x != found %x", name, calc, expected) } return nil } func readZipFile(zf *zip.File) ([]byte, error) { if zf == nil { return nil, errors.New("file not found") } r, err := zf.Open() if err != nil { return nil, err } blob, err := io.ReadAll(r) if err != nil { return nil, err } if err := r.Close(); err != nil { return nil, err } return blob, nil } func verifyCatalog(zf *zip.File, sig *AppxSignature) error { if zf == nil { if sig.IsBundle { return nil } return errors.New("missing security catalog") } blob, err := readZipFile(zf) if err != nil { return err } psd, err := pkcs7.Unmarshal(blob) if err != nil { return fmt.Errorf("security catalog: %w", err) } if !psd.Content.ContentInfo.ContentType.Equal(authenticode.OidCertTrustList) { return fmt.Errorf("security catalog: %s", "not a security catalog") } pksig, err := psd.Content.Verify(nil, false) if err != nil { return fmt.Errorf("security catalog: %w", err) } ts, err := pkcs9.VerifyOptionalTimestamp(pksig) if err != nil { return fmt.Errorf("security catalog: %w", err) } if !bytes.Equal(ts.Certificate.Raw, sig.Signature.Certificate.Raw) { return fmt.Errorf("security catalog: %s", "catalog signed by different certificate than appx") } // TODO: figure out what the things in the catalog actually are return nil } relic-7.6.1/lib/signappx/xml.go000066400000000000000000000020521455105530300163510ustar00rootroot00000000000000// // Copyright (c) SAS Institute Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package signappx import ( "encoding/xml" "fmt" ) const xmlHdr = "\r\n" func marshalXML(v interface{}, standalone bool) ([]byte, error) { x, err := xml.Marshal(v) if err != nil { return nil, err } sstr := "no" if standalone { sstr = "yes" } hdr := []byte(fmt.Sprintf(xmlHdr, sstr)) ret := make([]byte, len(hdr), len(hdr)+len(x)) copy(ret, hdr) ret = append(ret, x...) return ret, nil } relic-7.6.1/lib/signappx/zipmeta.go000066400000000000000000000035241455105530300172270ustar00rootroot00000000000000// // Copyright (c) SAS Institute Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package signappx import ( "crypto/hmac" "errors" "fmt" "io" "github.com/sassoftware/relic/v7/lib/zipslicer" ) func verifyMeta(r io.ReaderAt, size int64, sig *AppxSignature, skipDigests bool) error { dir, err := zipslicer.Read(r, size) if err != nil { return err } sigIdx := -1 for i, f := range dir.File { if f.Name == appxSignature { sigIdx = i } else if sigIdx >= 0 { return errors.New("zip elements out of order") } } // AXPC is a hash of everything except the central directory and signature file axpc := sig.Hash.New() sink := io.Writer(axpc) if skipDigests { sink = nil } // AXCD is a hash of the zip central directory with the signature file removed axcd := sig.Hash.New() if err := dir.Truncate(sigIdx, sink, axcd); err != nil { return fmt.Errorf("verifying zip metadata: %w", err) } if !skipDigests { calc := axpc.Sum(nil) if expected := sig.HashValues["AXPC"]; !hmac.Equal(calc, expected) { return fmt.Errorf("appx digest mismatch for zip contents: calculated %x != found %x", calc, expected) } } calc := axcd.Sum(nil) if expected := sig.HashValues["AXCD"]; !hmac.Equal(calc, expected) { return fmt.Errorf("appx digest mismatch for zip directory: calculated %x != found %x", calc, expected) } return nil } relic-7.6.1/lib/signdeb/000077500000000000000000000000001455105530300150055ustar00rootroot00000000000000relic-7.6.1/lib/signdeb/control.go000066400000000000000000000043431455105530300170200ustar00rootroot00000000000000// // Copyright (c) SAS Institute Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package signdeb import ( "archive/tar" "bufio" "bytes" "compress/bzip2" "compress/gzip" "errors" "io" "io/ioutil" "path" "strings" "github.com/xi2/xz" ) type PackageInfo struct { Package, Version, Arch string } // Parse basic package info from a control.tar.gz stream func parseControl(r io.Reader, ext string) (*PackageInfo, error) { var err error switch ext { case ".gz": r, err = gzip.NewReader(r) case ".bz2": r = bzip2.NewReader(r) case ".xz": r, err = xz.NewReader(r, 0) case "": default: err = errors.New("unrecognized compression on control.tar") } if err != nil { return nil, err } tr := tar.NewReader(r) found := false for { hdr, err := tr.Next() if err == io.EOF { break } else if err != nil { return nil, err } if path.Clean(hdr.Name) == "control" { found = true break } } if !found { return nil, errors.New("control.tar has no control file") } blob, err := ioutil.ReadAll(tr) if err != nil { return nil, err } info := new(PackageInfo) scanner := bufio.NewScanner(bytes.NewReader(blob)) for scanner.Scan() { line := scanner.Text() i := strings.IndexAny(line, " \t\r\n") j := strings.Index(line, ":") if j < 0 || i < j { continue } key := line[:j] value := strings.Trim(line[j+1:], " \t\r\n") switch strings.ToLower(key) { case "package": info.Package = value case "version": info.Version = value case "architecture": info.Arch = value } } if scanner.Err() != nil { return nil, scanner.Err() } if info.Package == "" || info.Version == "" { return nil, errors.New("control file is missing package and/or version fields") } return info, nil } relic-7.6.1/lib/signdeb/debsign.go000066400000000000000000000100011455105530300167370ustar00rootroot00000000000000// // Copyright (c) SAS Institute Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package signdeb import ( "bytes" "crypto" "errors" "fmt" "io" "io/ioutil" "path" "strings" "time" "github.com/sassoftware/relic/v7/lib/binpatch" "github.com/sassoftware/relic/v7/lib/pgptools" "github.com/sassoftware/relic/v7/lib/readercounter" "github.com/qur/ar" "golang.org/x/crypto/openpgp" "golang.org/x/crypto/openpgp/packet" ) type DebSignature struct { Info PackageInfo CreationTime time.Time PatchSet *binpatch.PatchSet } // Sign a .deb file with the given PGP key. A role name is needed for the // signature, e.g. "builder". Returns a structure holding a PatchSet that can // be applied to the original file to add or replace the signature. func Sign(r io.Reader, signer *openpgp.Entity, opts crypto.SignerOpts, role string) (*DebSignature, error) { counter := readercounter.New(r) now := time.Now().UTC() reader := ar.NewReader(counter) msg := new(bytes.Buffer) fmt.Fprintln(msg, "Version: 4") fmt.Fprintln(msg, "Signer:", pgptools.EntityName(signer)) fmt.Fprintln(msg, "Date:", now.Format(time.ANSIC)) fmt.Fprintln(msg, "Role:", role) fmt.Fprintln(msg, "Files: ") var patchOffset, patchLength int64 var info *PackageInfo filename := "_gpg" + role for { hdr, err := reader.Next() if err == io.EOF { break } else if err != nil { return nil, err } name := path.Clean(hdr.Name) if name == filename { // mark the old signature for removal patchOffset = counter.N - 60 patchLength = int64(60 + ((hdr.Size+1)/2)*2) } if strings.HasPrefix(name, "_gpg") { continue } save := io.Writer(ioutil.Discard) var closer io.Closer var infoch chan *PackageInfo var errch chan error if strings.HasPrefix(name, "control.tar") { // use a goroutine pipe to parse the control tarball as it's digested ext := name[11:] r, w := io.Pipe() save = w closer = w infoch = make(chan *PackageInfo, 1) errch = make(chan error, 1) go func() { info, err := parseControl(r, ext) // ensure whole file is read, otherwise pipe will stall _, _ = io.Copy(ioutil.Discard, r) infoch <- info errch <- err }() } md5 := crypto.MD5.New() sha1 := crypto.SHA1.New() if _, err := io.Copy(io.MultiWriter(md5, sha1, save), reader); err != nil { return nil, err } if closer != nil { closer.Close() } fmt.Fprintf(msg, "\t%x %x %d %s\n", md5.Sum(nil), sha1.Sum(nil), hdr.Size, hdr.Name) if errch != nil { // retrieve the result of parsing the control file info = <-infoch if err := <-errch; err != nil { return nil, err } } } if info == nil { return nil, errors.New("deb has no control.tar") } fmt.Fprintln(msg) signed := new(bytes.Buffer) config := &packet.Config{ DefaultHash: opts.HashFunc(), Time: func() time.Time { return now }, } if err := pgptools.ClearSign(signed, signer, msg, config); err != nil { return nil, err } // Format as an ar fragment and turn it into a binpatch that will update // the original archive pbuf := new(bytes.Buffer) writer := ar.NewWriter(pbuf) hdr := &ar.Header{ Name: filename, Size: int64(signed.Len()), ModTime: now, Mode: 0100644, } if err := writer.WriteHeader(hdr); err != nil { return nil, err } if _, err := writer.Write(signed.Bytes()); err != nil { return nil, err } if patchOffset == 0 { patchOffset = counter.N // end of file } patch := binpatch.New() patch.Add(patchOffset, patchLength, pbuf.Bytes()) return &DebSignature{*info, now, patch}, nil } relic-7.6.1/lib/signdeb/verify.go000066400000000000000000000062541455105530300166470ustar00rootroot00000000000000// // Copyright (c) SAS Institute Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package signdeb import ( "bufio" "bytes" "crypto" "errors" "fmt" "io" "io/ioutil" "strings" "github.com/qur/ar" "golang.org/x/crypto/openpgp" "github.com/sassoftware/relic/v7/lib/pgptools" ) // Extract and verify signatures from a Debian package. A keyring of known PGP // certificates must be provided to validate the signatures; if the needed key // is missing then an ErrNoKey value is returned. func Verify(r io.Reader, keyring openpgp.EntityList, skipDigest bool) (map[string]*pgptools.PgpSignature, error) { reader := ar.NewReader(r) digests := make(map[string]string) sigs := make(map[string][]byte) for { hdr, err := reader.Next() if err == io.EOF { break } else if err != nil { return nil, err } if strings.HasPrefix(hdr.Name, "_gpg") { role := hdr.Name[4:] sigs[role], err = ioutil.ReadAll(reader) if err != nil { return nil, err } } else if !skipDigest { md5 := crypto.MD5.New() sha1 := crypto.SHA1.New() if _, err := io.Copy(io.MultiWriter(md5, sha1), reader); err != nil { return nil, err } digests[hdr.Name] = fmt.Sprintf("%x %x", md5.Sum(nil), sha1.Sum(nil)) } } ret := make(map[string]*pgptools.PgpSignature, len(sigs)) for role, sig := range sigs { var body bytes.Buffer info, err := pgptools.VerifyClearSign(bytes.NewReader(sig), &body, keyring) if err != nil { return nil, err } if !skipDigest { if err := checkSig(role, &body, digests); err != nil { return nil, err } } ret[role] = info } return ret, nil } func checkSig(role string, body io.Reader, digests map[string]string) error { sawFiles := false scanner := bufio.NewScanner(body) // read header for scanner.Scan() { line := scanner.Text() if line == "Files:" { sawFiles = true break } } if !sawFiles { return errors.New("malformed signature") } // read digests checked := make(map[string]bool, len(digests)) for scanner.Scan() { line := scanner.Text() if line == "" { break } else if line[0] != '\t' || len(line) < 76 { return errors.New("malformed signature") } parts := strings.SplitN(line[1:], " ", 4) sums := parts[0] + " " + parts[1] name := parts[3] calculated := digests[name] if calculated == "" { return fmt.Errorf("signature references unknown file %s", name) } else if calculated != sums { return fmt.Errorf("signature mismatch on file %s: (%s) != (%s)", name, calculated, sums) } checked[name] = true } // make sure everything was checked for name := range digests { if checked[name] { continue } return fmt.Errorf("signature does not cover file: %s", name) } return nil } relic-7.6.1/lib/signjar/000077500000000000000000000000001455105530300150275ustar00rootroot00000000000000relic-7.6.1/lib/signjar/digest.go000066400000000000000000000105341455105530300166400ustar00rootroot00000000000000// // Copyright (c) SAS Institute Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package signjar import ( "crypto" "encoding/base64" "errors" "fmt" "io" "net/http" "strings" "github.com/sassoftware/relic/v7/lib/x509tools" "github.com/sassoftware/relic/v7/lib/zipslicer" ) // found in the "extra" field of JAR files, not strictly required but it makes // `file` output actually say JAR var jarMagic = []byte{0xfe, 0xca, 0, 0} type JarDigest struct { Digests map[string]string Manifest []byte Hash crypto.Hash inz *zipslicer.Directory } func DigestJarStream(r io.Reader, hash crypto.Hash) (*JarDigest, error) { inz, err := zipslicer.ReadZipTar(r) if err != nil { return nil, err } return updateManifest(inz, hash) } // Digest all of the files in the JAR func digestFiles(jar *zipslicer.Directory, hash crypto.Hash) (*JarDigest, error) { jd := &JarDigest{ Hash: hash, Digests: make(map[string]string), inz: jar, } for _, f := range jar.File { if f.Name == manifestName { r, err := f.Open() if err != nil { return nil, fmt.Errorf("failed to read JAR manifest: %w", err) } jd.Manifest, err = io.ReadAll(r) if err != nil { return nil, fmt.Errorf("failed to read JAR manifest: %w", err) } } else if strings.HasSuffix(f.Name, "/") || !keepFile(f.Name) { // not hashing } else { r, err := f.Open() if err != nil { return nil, err } d := hash.New() if _, err := io.Copy(d, r); err != nil { return nil, fmt.Errorf("failed to digest JAR file %s: %w", f.Name, err) } if err := r.Close(); err != nil { return nil, fmt.Errorf("failed to digest JAR file %s: %w", f.Name, err) } jd.Digests[f.Name] = base64.StdEncoding.EncodeToString(d.Sum(nil)) } // Ensure we get a copy of the zip metadata even if the file isn't // digested, because if we're reading from a stream we can't go back // and get it later. if _, err := f.GetDataDescriptor(); err != nil { return nil, fmt.Errorf("failed to read JAR manifest: %w", err) } } return jd, nil } // Check JAR contents against its manifest and adds digests if necessary func updateManifest(jar *zipslicer.Directory, hash crypto.Hash) (*JarDigest, error) { jd, err := digestFiles(jar, hash) if err != nil { return nil, err } else if jd.Manifest == nil { return nil, errors.New("JAR did not contain a manifest") } files, malformed, err := parseManifest(jd.Manifest) if err != nil { return nil, err } hashName := x509tools.HashNames[hash] if hashName == "" { return nil, errors.New("unsupported hash type") } hashName += "-Digest" changed := false for name, calculated := range jd.Digests { // if the manifest has a matching digest, check it. otherwise add to the manifest. attrs := files.Files[name] if attrs == nil { // file is not mentioned in the manifest at all files.Files[name] = http.Header{ "Name": []string{name}, hashName: []string{calculated}, } files.Order = append(files.Order, name) changed = true } else if attrs.Get("Magic") != "" { // magic means a special digester is required. hopefully it's already been digested. } else if existing := attrs.Get(hashName); existing != "" { // manifest has a digest already, check it if existing != calculated { return nil, fmt.Errorf("%s mismatch for JAR file %s: manifest %s != calculated %s", hashName, name, existing, calculated) } } else { // file in manifest but no matching digest attrs.Set(hashName, calculated) changed = true } } // Add empty digests for any dir entries with metadata for name, attrs := range files.Files { if strings.HasSuffix(name, "/") && attrs.Get(hashName) == "" { d := hash.New() calculated := base64.StdEncoding.EncodeToString(d.Sum(nil)) attrs.Set(hashName, calculated) changed = true } } if changed || malformed { jd.Manifest = files.Dump() } return jd, nil } relic-7.6.1/lib/signjar/manifest.go000066400000000000000000000140051455105530300171640ustar00rootroot00000000000000// // Copyright (c) SAS Institute Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package signjar import ( "bytes" "crypto" "encoding/base64" "errors" "fmt" "net/http" "sort" "strings" "github.com/sassoftware/relic/v7/config" "github.com/sassoftware/relic/v7/lib/x509tools" ) // See https://docs.oracle.com/javase/8/docs/technotes/guides/jar/jar.html#JAR_Manifest const ( metaInf = "META-INF/" manifestName = metaInf + "MANIFEST.MF" ) var ErrManifestLineEndings = errors.New("manifest has incorrect line ending sequence") type FilesMap struct { Main http.Header Order []string Files map[string]http.Header } func ParseManifest(manifest []byte) (files *FilesMap, err error) { files, malformed, err := parseManifest(manifest) if err != nil { return nil, err } else if malformed { return nil, ErrManifestLineEndings } return files, nil } func parseManifest(manifest []byte) (files *FilesMap, malformed bool, err error) { sections, malformed := splitManifest(manifest) if len(sections) == 0 { return nil, false, errors.New("manifest has no sections") } files = &FilesMap{ Order: make([]string, 0, len(sections)-1), Files: make(map[string]http.Header, len(sections)-1), } for i, section := range sections { if i > 0 && len(section) == 0 { continue } hdr, err := parseSection(section) if err != nil { return nil, false, err } if i == 0 { files.Main = hdr } else { name := hdr.Get("Name") if name == "" { return nil, false, errors.New("manifest has section with no \"Name\" attribute") } files.Order = append(files.Order, name) files.Files[name] = hdr } } return files, malformed, nil } func (m *FilesMap) Dump() []byte { var out bytes.Buffer writeSection(&out, m.Main, "Manifest-Version") for _, name := range m.Order { section := m.Files[name] if section != nil { writeSection(&out, section, "Name") } } return out.Bytes() } func splitManifest(manifest []byte) ([][]byte, bool) { var malformed bool sections := make([][]byte, 0) for len(manifest) != 0 { i1 := bytes.Index(manifest, []byte("\r\n\r\n")) i2 := bytes.Index(manifest, []byte("\n\n")) var idx int switch { case i1 >= 0: idx = i1 + 4 case i2 >= 0: idx = i2 + 2 default: // If there is not a proper 2x line ending, // then it's technically not valid but we can sign it anyway // as long as it gets rewritten with correct endings. idx = len(manifest) malformed = true } section := manifest[:idx] manifest = manifest[idx:] if len(bytes.TrimSpace(section)) == 0 { // Excessive line endings have created an empty section malformed = true continue } sections = append(sections, section) } return sections, malformed } func parseSection(section []byte) (http.Header, error) { section = bytes.ReplaceAll(section, []byte("\r\n"), []byte{'\n'}) section = bytes.ReplaceAll(section, []byte("\n "), []byte{}) keys := bytes.Split(section, []byte{'\n'}) hdr := make(http.Header) for _, line := range keys { if len(line) == 0 { continue } idx := bytes.IndexRune(line, ':') if idx < 0 { return nil, errors.New("jar manifest is malformed") } key := strings.TrimSpace(string(line[:idx])) value := strings.TrimSpace(string(line[idx+1:])) hdr.Set(key, value) } return hdr, nil } func hashSection(hash crypto.Hash, section []byte) string { d := hash.New() d.Write(section) return base64.StdEncoding.EncodeToString(d.Sum(nil)) } // Transform a MANIFEST.MF into a *.SF by digesting each section with the // specified hash func DigestManifest(manifest []byte, hash crypto.Hash, sectionsOnly, apkV2 bool) ([]byte, error) { sections, malformed := splitManifest(manifest) if malformed { return nil, ErrManifestLineEndings } hashName := x509tools.HashNames[hash] if hashName == "" { return nil, errors.New("unsupported hash type") } var output bytes.Buffer writeAttribute(&output, "Signature-Version", "1.0") writeAttribute(&output, hashName+"-Digest-Manifest-Main-Attributes", hashSection(hash, sections[0])) if !sectionsOnly { writeAttribute(&output, hashName+"-Digest-Manifest", hashSection(hash, manifest)) } writeAttribute(&output, "Created-By", fmt.Sprintf("%s (%s)", config.UserAgent, config.Author)) if apkV2 { writeAttribute(&output, "X-Android-APK-Signed", "2") } output.WriteString("\r\n") for _, section := range sections[1:] { hdr, err := parseSection(section) if err != nil { return nil, err } name := hdr.Get("Name") if name == "" { return nil, errors.New("File section was missing Name attribute") } writeAttribute(&output, "Name", name) writeAttribute(&output, hashName+"-Digest", hashSection(hash, section)) output.WriteString("\r\n") } return output.Bytes(), nil } const maxLineLength = 70 // Write a key-value pair, wrapping long lines as necessary func writeAttribute(out *bytes.Buffer, key, value string) { line := []byte(fmt.Sprintf("%s: %s", key, value)) for i := 0; i < len(line); { goal := maxLineLength if i != 0 { out.Write([]byte{' '}) goal-- } j := i + goal if j > len(line) { j = len(line) } out.Write(line[i:j]) out.Write([]byte("\r\n")) i = j } } func writeSection(out *bytes.Buffer, hdr http.Header, first string) { value := hdr.Get(first) if value != "" { writeAttribute(out, first, value) } keys := make([]string, 0, len(hdr)) for key := range hdr { if key == first { continue } keys = append(keys, key) } sort.Strings(keys) for _, key := range keys { for _, value := range hdr[key] { writeAttribute(out, key, value) } } out.Write([]byte("\r\n")) } relic-7.6.1/lib/signjar/manifest_test.go000066400000000000000000000101241455105530300202210ustar00rootroot00000000000000package signjar import ( "net/http" "strings" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) func TestParse(t *testing.T) { t.Parallel() t.Run("Full", func(t *testing.T) { const manifest = `Manifest-Version: 1.0 Built-By: nobody Long-Header-Line: 0123456789abcdef0123456789abcdef0123456789abcdef 0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef 0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef Name: foo Ham: spam Eggs: bacon ` main := http.Header{ "Manifest-Version": []string{"1.0"}, "Built-By": []string{"nobody"}, "Long-Header-Line": []string{ "0123456789abcdef0123456789abcdef" + "0123456789abcdef0123456789abcdef0123456789abcdef" + "0123456789abcdef0123456789abcdef0123456789abcdef" + "0123456789abcdef0123456789abcdef0123456789abcdef"}, } file := http.Header{ "Name": []string{"foo"}, "Ham": []string{"spam"}, "Eggs": []string{"bacon"}, } expected := &FilesMap{ Main: main, Files: map[string]http.Header{"foo": file}, Order: []string{"foo"}, } parsed, err := ParseManifest([]byte(manifest)) require.NoError(t, err) assert.Equal(t, expected, parsed) crlfManifest := []byte(strings.ReplaceAll(manifest, "\n", "\r\n")) parsed, err = ParseManifest(crlfManifest) require.NoError(t, err) assert.Equal(t, expected, parsed) }) t.Run("TruncatedMain", func(t *testing.T) { const manifest = "Manifest-Version: 1.0\n" expected := &FilesMap{ Main: http.Header{ "Manifest-Version": []string{"1.0"}, }, Order: []string{}, Files: map[string]http.Header{}, } _, err := ParseManifest([]byte(manifest)) require.ErrorIs(t, err, ErrManifestLineEndings) parsed, malformed, err := parseManifest([]byte(manifest)) require.NoError(t, err) assert.True(t, malformed) assert.Equal(t, expected, parsed) }) t.Run("TruncatedFile", func(t *testing.T) { const manifest = "Manifest-Version: 1.0\n\nName: foo\n" file := http.Header{ "Name": []string{"foo"}, } expected := &FilesMap{ Main: http.Header{ "Manifest-Version": []string{"1.0"}, }, Order: []string{"foo"}, Files: map[string]http.Header{"foo": file}, } _, err := ParseManifest([]byte(manifest)) require.ErrorIs(t, err, ErrManifestLineEndings) parsed, malformed, err := parseManifest([]byte(manifest)) require.NoError(t, err) assert.True(t, malformed) assert.Equal(t, expected, parsed) }) t.Run("TrailingWhitespace", func(t *testing.T) { const manifest = "Manifest-Version: 1.0\n\nName: foo\n\n\n" file := http.Header{ "Name": []string{"foo"}, } expected := &FilesMap{ Main: http.Header{ "Manifest-Version": []string{"1.0"}, }, Order: []string{"foo"}, Files: map[string]http.Header{"foo": file}, } _, err := ParseManifest([]byte(manifest)) require.ErrorIs(t, err, ErrManifestLineEndings) parsed, malformed, err := parseManifest([]byte(manifest)) require.NoError(t, err) assert.True(t, malformed) assert.Equal(t, expected, parsed) }) t.Run("InvalidNoName", func(t *testing.T) { const manifest = "Manifest-Version: 1.0\n\nFoo: bar\n\n" _, err := ParseManifest([]byte(manifest)) require.Error(t, err) }) } func TestDump(t *testing.T) { manifest := &FilesMap{ Main: http.Header{ "Manifest-Version": []string{"1.0"}, "D": []string{"D"}, "C": []string{"C"}, "B": []string{"B"}, "A": []string{"A"}, "Long-Header": []string{strings.Repeat("0123456789abcdef", 10)}, }, Files: map[string]http.Header{ "foo": {"Name": []string{"foo"}, "Foo": []string{"bar"}}, "bar": {"Name": []string{"bar"}, "Foo": []string{"bar"}}, }, Order: []string{"bar", "foo"}, } result := string(manifest.Dump()) expected := `Manifest-Version: 1.0 A: A B: B C: C D: D Long-Header: 0123456789abcdef0123456789abcdef0123456789abcdef012345678 9abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcd ef0123456789abcdef0123456789abcdef Name: bar Foo: bar Name: foo Foo: bar ` expected = strings.ReplaceAll(expected, "\n", "\r\n") assert.Equal(t, expected, result) } relic-7.6.1/lib/signjar/sign.go000066400000000000000000000114421455105530300163200ustar00rootroot00000000000000// // Copyright (c) SAS Institute Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package signjar import ( "archive/zip" "bytes" "context" "crypto" "crypto/ecdsa" "crypto/rsa" "crypto/x509" "encoding/asn1" "errors" "path" "strings" "time" "github.com/sassoftware/relic/v7/lib/binpatch" "github.com/sassoftware/relic/v7/lib/certloader" "github.com/sassoftware/relic/v7/lib/pkcs7" "github.com/sassoftware/relic/v7/lib/pkcs9" "github.com/sassoftware/relic/v7/lib/zipslicer" ) func (jd *JarDigest) Sign(ctx context.Context, cert *certloader.Certificate, alias string, sectionsOnly, inlineSignature, apkV2 bool) (*binpatch.PatchSet, *pkcs9.TimestampedSignature, error) { // Create sigfile from the manifest sf, err := DigestManifest(jd.Manifest, jd.Hash, sectionsOnly, apkV2) if err != nil { return nil, nil, err } // Sign sigfile sig := pkcs7.NewBuilder(cert.Signer(), cert.Chain(), jd.Hash) if err := sig.SetContentData(sf); err != nil { return nil, nil, err } psd, err := sig.Sign() if err != nil { return nil, nil, err } ts, err := pkcs9.TimestampAndMarshal(ctx, psd, cert.Timestamper, false) if err != nil { return nil, nil, err } // Rebuild zip with updated manifest, sigfile, and signature psig := ts.Raw if !inlineSignature { if _, err := psd.Detach(); err != nil { return nil, nil, err } psig, err = asn1.Marshal(*psd) if err != nil { return nil, nil, err } } patch, err := jd.insertSignature(cert.Leaf, alias, sf, psig) if err != nil { return nil, nil, err } return patch, ts, nil } func (jd *JarDigest) insertSignature(cert *x509.Certificate, alias string, sf, sig []byte) (*binpatch.PatchSet, error) { signame, pkcsname := sigNames(cert.PublicKey, alias) deflate := jd.shouldDeflate() // Add new files to beginning of zip outz := new(zipslicer.Directory) var zipcon bytes.Buffer mtime := time.Now() if _, err := outz.NewFile(metaInf, jarMagic, nil, &zipcon, mtime, false, false); err != nil { return nil, err } if _, err := outz.NewFile(manifestName, jarMagic, jd.Manifest, &zipcon, mtime, deflate, false); err != nil { return nil, err } if _, err := outz.NewFile(metaInf+signame, nil, sf, &zipcon, mtime, deflate, false); err != nil { return nil, err } if _, err := outz.NewFile(metaInf+pkcsname, nil, sig, &zipcon, mtime, deflate, false); err != nil { return nil, err } // Patch out old files patch := binpatch.New() patch.Add(0, 0, zipcon.Bytes()) for _, f := range jd.inz.File { if keepFile(f.Name) { // Add existing file to the new zip directory. Its offset will be changed. if _, err := outz.AddFile(f); err != nil { return nil, err } } else { // remove this region from the old zip size, err := f.GetTotalSize() if err != nil { return nil, err } if size > 0xffffffff { return nil, errors.New("signature file too big") } patch.Add(int64(f.Offset), size, nil) } } zipdir := new(bytes.Buffer) if err := outz.WriteDirectory(zipdir, zipdir, false); err != nil { return nil, err } patch.Add(jd.inz.DirLoc, jd.inz.Size-jd.inz.DirLoc, zipdir.Bytes()) return patch, nil } // name for the signature file is based on the key type func sigNames(pubkey crypto.PublicKey, alias string) (signame, pkcsname string) { signame = strings.ToUpper(alias) + ".SF" pkcsname = strings.ToUpper(alias) switch pubkey.(type) { case *rsa.PublicKey: pkcsname += ".RSA" case *ecdsa.PublicKey: pkcsname += ".EC" default: signame = "SIG-" + signame pkcsname = "SIG-" + pkcsname + ".SIG" } return } // deflate if the original manifest was deflated func (jd *JarDigest) shouldDeflate() bool { for _, f := range jd.inz.File { if f.Name == manifestName { return f.Method != zip.Store } } return false } func keepFile(name string) bool { if name == metaInf { // META-INF/ itself gets updated return false } if path.Dir(name)+"/" != metaInf { // everything not an immediate child of META-INF/ is kept return true } name = path.Base(name) switch { case strings.HasPrefix(name, "SIG-"): // delete all old signatures return false case name == "MANIFEST.MF": // replace the manifest return false } switch path.Ext(name) { case ".SF": // delete all old signatures return false case ".RSA", ".DSA", ".EC", ".SIG": return false default: // all other META-INF/ files are kept return true } } relic-7.6.1/lib/signjar/verify.go000066400000000000000000000145611455105530300166710ustar00rootroot00000000000000// // Copyright (c) SAS Institute Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package signjar import ( "archive/zip" "bytes" "crypto" "encoding/base64" "errors" "fmt" "hash" "io" "net/http" "path" "strings" "github.com/sassoftware/relic/v7/lib/pkcs7" "github.com/sassoftware/relic/v7/lib/pkcs9" "github.com/sassoftware/relic/v7/lib/x509tools" "github.com/sassoftware/relic/v7/signers/sigerrors" ) var errNoDigests = errors.New("no recognized digests found") type JarSignature struct { pkcs9.TimestampedSignature SignatureHeader http.Header Hash crypto.Hash } func Verify(inz *zip.Reader, skipDigests bool) ([]*JarSignature, error) { var manifest []byte sigfiles := make(map[string][]byte) sigblobs := make(map[string][]byte) for _, f := range inz.File { dir, name := path.Split(strings.ToUpper(f.Name)) if dir != "META-INF/" || name == "" { continue } i := strings.LastIndex(name, ".") if i < 0 { continue } base, ext := name[:i], name[i:] r2, err := f.Open() if err != nil { return nil, err } contents, err := io.ReadAll(r2) if err != nil { return nil, err } if err := r2.Close(); err != nil { return nil, err } if name == "MANIFEST.MF" { manifest = contents } else if ext == ".SF" { sigfiles[base] = contents } else if ext == ".RSA" || ext == ".DSA" || ext == ".EC" || strings.HasPrefix(name, "SIG-") { sigblobs[base] = contents } } if manifest == nil { return nil, errors.New("JAR contains no META-INF/MANIFEST.MF") } else if len(sigfiles) == 0 { return nil, sigerrors.NotSignedError{Type: "JAR"} } sigs := make([]*JarSignature, 0, len(sigfiles)) for base, sigfile := range sigfiles { pkcs := sigblobs[base] if pkcs == nil { return nil, fmt.Errorf("JAR contains sigfile META-INF/%s.SF with no matching signature", base) } psd, err := pkcs7.Unmarshal(pkcs) if err != nil { return nil, err } sig, err := psd.Content.Verify(sigfile, false) if err != nil { return nil, err } ts, err := pkcs9.VerifyOptionalTimestamp(sig) if err != nil { return nil, err } ts.Raw = pkcs hdr, err := verifySigFile(sigfile, manifest) if err != nil { return nil, err } hash, _ := x509tools.PkixDigestToHash(ts.SignerInfo.DigestAlgorithm) sigs = append(sigs, &JarSignature{ TimestampedSignature: ts, Hash: hash, SignatureHeader: hdr, }) } if !skipDigests { if err := verifyManifest(inz, manifest); err != nil { return nil, err } } return sigs, nil } // Verify all digests in MANIFEST.MF func verifyManifest(inz *zip.Reader, manifest []byte) error { parsed, err := ParseManifest(manifest) if err != nil { return err } zipfiles := make(map[string]*zip.File, len(inz.File)) for _, fh := range inz.File { zipfiles[fh.Name] = fh } for filename, keys := range parsed.Files { if keys.Get("Magic") != "" { continue } fh := zipfiles[filename] if fh == nil { return fmt.Errorf("file %s is in manifest but not JAR", filename) } r, err := fh.Open() if err != nil { return err } if err := hashFile(keys, r, ""); err != nil { return fmt.Errorf("file \"%s\" in MANIFEST.MF: %w", filename, err) } if err := r.Close(); err != nil { return err } } return nil } type digester struct { key, value string hash hash.Hash } // Verify any hashes present in a single manifest section against the given content. func hashFile(keys http.Header, content io.Reader, suffix string) error { digesters := make([]digester, 0) suffix = "-Digest" + suffix for key, value := range keys { if !strings.HasSuffix(key, suffix) { continue } hashName := strings.ToUpper(key[:len(key)-len(suffix)]) hash := x509tools.HashByName(hashName) if !hash.Available() { return fmt.Errorf("unknown digest key in manifest: %s", hashName) } digesters = append(digesters, digester{key, value[0], hash.New()}) } if len(digesters) == 0 { return errNoDigests } buf := make([]byte, 32*1024) for { n, err := content.Read(buf) if n > 0 { for _, digester := range digesters { digester.hash.Write(buf[:n]) } } if err == io.EOF { break } else if err != nil { return err } } for _, digester := range digesters { calculated := base64.StdEncoding.EncodeToString(digester.hash.Sum(nil)) if calculated != digester.value { return fmt.Errorf("%s mismatch: manifest %s != calculated %s", digester.key, digester.value, calculated) } } return nil } func verifySigFile(sigfile, manifest []byte) (http.Header, error) { sfParsed, err := ParseManifest(sigfile) if err != nil { return nil, err } if err := hashFile(sfParsed.Main, bytes.NewReader(manifest), "-Manifest"); err != nil { if err != errNoDigests { return nil, fmt.Errorf("manifest signature: %w", err) } // fall through and verify all the section digests } else { // if the whole-file digest passed then skip the sections return sfParsed.Main, nil } sections, malformed := splitManifest(manifest) if malformed { return nil, ErrManifestLineEndings } sectionMap := make(map[string][]byte, len(sections)-1) for i, section := range sections { if i == 0 { if err := hashFile(sfParsed.Main, bytes.NewReader(sections[0]), "-Manifest-Main-Attributes"); err != nil { return nil, fmt.Errorf("manifest main attributes signature: %w", err) } } else { hdr, err := parseSection(section) if err != nil { return nil, err } name := hdr.Get("Name") if name == "" { return nil, errors.New("manifest has section with no \"Name\" attribute") } sectionMap[name] = section } } for name, keys := range sfParsed.Files { section := sectionMap[name] if section == nil { return nil, fmt.Errorf("manifest is missing signed section \"%s\"", name) } if err := hashFile(keys, bytes.NewReader(section), ""); err != nil { return nil, fmt.Errorf("manifest signature over section \"%s\": %w", name, err) } } return sfParsed.Main, nil } relic-7.6.1/lib/signxap/000077500000000000000000000000001455105530300150435ustar00rootroot00000000000000relic-7.6.1/lib/signxap/sign.go000066400000000000000000000056371455105530300163450ustar00rootroot00000000000000// // Copyright (c) SAS Institute Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package signxap import ( "archive/tar" "bytes" "context" "crypto" "encoding/binary" "errors" "fmt" "io" "io/ioutil" "github.com/sassoftware/relic/v7/lib/authenticode" "github.com/sassoftware/relic/v7/lib/binpatch" "github.com/sassoftware/relic/v7/lib/certloader" "github.com/sassoftware/relic/v7/lib/pkcs9" "github.com/sassoftware/relic/v7/lib/zipslicer" ) type XapDigest struct { Hash crypto.Hash Imprint []byte PatchStart int64 PatchLen int64 } func DigestXapTar(r io.Reader, hash crypto.Hash, doPageHash bool) (*XapDigest, error) { tr := tar.NewReader(r) var cd []byte var totalSize int64 for { hdr, err := tr.Next() if err == io.EOF { return nil, errors.New("invalid tarzip") } else if err != nil { return nil, fmt.Errorf("reading tar: %w", err) } if hdr.Name == zipslicer.TarMemberCD { cd, err = ioutil.ReadAll(tr) if err != nil { return nil, fmt.Errorf("reading tar: %w", err) } } else if hdr.Name == zipslicer.TarMemberZip { totalSize = hdr.Size break } } bodySize := totalSize - int64(len(cd)) d := hash.New() if _, err := io.CopyN(d, tr, bodySize); err != nil { return nil, err } cd = removeSignature(cd) d.Write(cd) zipSize := bodySize + int64(len(cd)) return &XapDigest{ Hash: hash, Imprint: d.Sum(nil), PatchStart: zipSize, PatchLen: totalSize - zipSize, }, nil } func removeSignature(cd []byte) []byte { size := len(cd) var tr xapTrailer _ = binary.Read(bytes.NewReader(cd[size-10:size]), binary.LittleEndian, &tr) if tr.Magic == trailerMagic { size -= int(tr.TrailerSize) + 10 return cd[:size] } return cd } func (d *XapDigest) Sign(ctx context.Context, cert *certloader.Certificate, params *authenticode.OpusParams) (*binpatch.PatchSet, *pkcs9.TimestampedSignature, error) { ts, err := authenticode.SignSip(ctx, d.Imprint, d.Hash, xapSipInfo, cert, params) if err != nil { return nil, nil, err } var w bytes.Buffer hdr := xapHeader{ Unknown1: 1, Unknown2: 1, SignatureSize: uint32(len(ts.Raw)), } _ = binary.Write(&w, binary.LittleEndian, hdr) w.Write(ts.Raw) tr := xapTrailer{ Magic: trailerMagic, Unknown1: 1, TrailerSize: uint32(len(ts.Raw) + 8), } _ = binary.Write(&w, binary.LittleEndian, tr) patch := binpatch.New() patch.Add(d.PatchStart, d.PatchLen, w.Bytes()) return patch, ts, nil } relic-7.6.1/lib/signxap/structs.go000066400000000000000000000021141455105530300170770ustar00rootroot00000000000000// // Copyright (c) SAS Institute Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package signxap import "github.com/sassoftware/relic/v7/lib/authenticode" const ( trailerMagic = 0x53706158 // XapS ) var ( SpcUUIDSipInfoXap = []byte{0x6F, 0xA6, 0x08, 0xBA, 0x3B, 0x11, 0x58, 0x4D, 0x93, 0x29, 0xA1, 0xB3, 0x7A, 0xF3, 0x0F, 0x0E} xapSipInfo = authenticode.SpcSipInfo{A: 1, UUID: SpcUUIDSipInfoXap} ) type xapTrailer struct { Magic uint32 Unknown1 uint16 TrailerSize uint32 } type xapHeader struct { Unknown1, Unknown2 uint16 SignatureSize uint32 } relic-7.6.1/lib/signxap/verify.go000066400000000000000000000064631455105530300167070ustar00rootroot00000000000000// // Copyright (c) SAS Institute Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package signxap import ( "crypto" "crypto/hmac" "encoding/binary" "errors" "fmt" "io" "github.com/sassoftware/relic/v7/lib/authenticode" "github.com/sassoftware/relic/v7/lib/pkcs7" "github.com/sassoftware/relic/v7/lib/pkcs9" "github.com/sassoftware/relic/v7/lib/x509tools" "github.com/sassoftware/relic/v7/signers/sigerrors" ) type XapSignature struct { pkcs9.TimestampedSignature Hash crypto.Hash OpusInfo *authenticode.SpcSpOpusInfo } func Verify(r io.ReaderAt, size int64, skipDigests bool) (*XapSignature, error) { var tr xapTrailer if err := binary.Read(io.NewSectionReader(r, size-10, 10), binary.LittleEndian, &tr); err != nil { return nil, err } if tr.Magic != trailerMagic { var zipMagic uint32 if err := binary.Read(io.NewSectionReader(r, size-22, 4), binary.LittleEndian, &zipMagic); err != nil { return nil, err } if zipMagic == 0x06054b50 { return nil, sigerrors.NotSignedError{Type: "XAP"} } return nil, errors.New("invalid xap file") } size -= int64(tr.TrailerSize) + 10 var hdr xapHeader if err := binary.Read(io.NewSectionReader(r, size, 8), binary.LittleEndian, &hdr); err != nil { return nil, err } if hdr.SignatureSize != tr.TrailerSize-8 { return nil, errors.New("invalid xap file") } blob := make([]byte, hdr.SignatureSize) if n, err := r.ReadAt(blob, size+8); err != nil { return nil, err } else if n < len(blob) { return nil, io.ErrUnexpectedEOF } psd, err := pkcs7.Unmarshal(blob) if err != nil { return nil, fmt.Errorf("invalid signature: %w", err) } if !psd.Content.ContentInfo.ContentType.Equal(authenticode.OidSpcIndirectDataContent) { return nil, fmt.Errorf("invalid signature: %s", "not an authenticode signature") } pksig, err := psd.Content.Verify(nil, false) if err != nil { return nil, fmt.Errorf("invalid signature: %w", err) } ts, err := pkcs9.VerifyOptionalTimestamp(pksig) if err != nil { return nil, err } indirect := new(authenticode.SpcIndirectDataContentMsi) if err := psd.Content.ContentInfo.Unmarshal(indirect); err != nil { return nil, fmt.Errorf("invalid signature: %w", err) } hash, err := x509tools.PkixDigestToHashE(indirect.MessageDigest.DigestAlgorithm) if err != nil { return nil, err } opus, err := authenticode.GetOpusInfo(pksig.SignerInfo) if err != nil { return nil, err } if !skipDigests { d := hash.New() if _, err := io.Copy(d, io.NewSectionReader(r, 0, size)); err != nil { return nil, err } calc := d.Sum(nil) expected := indirect.MessageDigest.Digest if !hmac.Equal(calc, expected) { return nil, fmt.Errorf("digest mismatch: calculated %x != found %x", calc, expected) } } return &XapSignature{ TimestampedSignature: ts, Hash: hash, OpusInfo: opus, }, nil } relic-7.6.1/lib/x509tools/000077500000000000000000000000001455105530300151605ustar00rootroot00000000000000relic-7.6.1/lib/x509tools/certpool.go000066400000000000000000000027731455105530300173470ustar00rootroot00000000000000// // Copyright (c) SAS Institute Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package x509tools import ( "crypto/tls" "crypto/x509" "fmt" "io/ioutil" "strings" ) // Load a certificate pool from a file and set it as the root CA for a TLS // config. If path is empty then the system pool will be used. If the filename // starts with + then both the system pool and the contents of the file will be // used. func LoadCertPool(path string, tconf *tls.Config) error { if path == "" { return nil } else if path[0] == '+' { pool, err := x509.SystemCertPool() if err != nil { return err } tconf.RootCAs = pool path = path[1:] } else { tconf.RootCAs = x509.NewCertPool() } var contents []byte if strings.Contains(path, "-----BEGIN") { contents = []byte(path) } else { var err error contents, err = ioutil.ReadFile(path) if err != nil { return err } } if !tconf.RootCAs.AppendCertsFromPEM(contents) { return fmt.Errorf("no CA certificates in %s", path) } return nil } relic-7.6.1/lib/x509tools/digests.go000066400000000000000000000047761455105530300171670ustar00rootroot00000000000000// // Copyright (c) SAS Institute Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package x509tools import ( "crypto" "crypto/x509/pkix" "encoding/asn1" "strings" "sync" ) var ( // RFC 3279 OidDigestMD5 = asn1.ObjectIdentifier{1, 2, 840, 113549, 2, 5} OidDigestSHA1 = asn1.ObjectIdentifier{1, 3, 14, 3, 2, 26} // RFC 5758 OidDigestSHA224 = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 2, 4} OidDigestSHA256 = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 2, 1} OidDigestSHA384 = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 2, 2} OidDigestSHA512 = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 2, 3} ) var HashOids = map[crypto.Hash]asn1.ObjectIdentifier{ crypto.MD5: OidDigestMD5, crypto.SHA1: OidDigestSHA1, crypto.SHA224: OidDigestSHA224, crypto.SHA256: OidDigestSHA256, crypto.SHA384: OidDigestSHA384, crypto.SHA512: OidDigestSHA512, } var HashNames = map[crypto.Hash]string{ crypto.MD5: "MD5", crypto.SHA1: "SHA1", crypto.SHA224: "SHA-224", crypto.SHA256: "SHA-256", crypto.SHA384: "SHA-384", crypto.SHA512: "SHA-512", } var ( hashesByName map[string]crypto.Hash once sync.Once ) func HashShortName(hash crypto.Hash) string { return normalName(HashNames[hash]) } func normalName(name string) string { return strings.Replace(strings.ToLower(name), "-", "", 1) } func HashByName(name string) crypto.Hash { name = normalName(name) once.Do(func() { hashesByName = make(map[string]crypto.Hash, len(HashNames)) for h, hn := range HashNames { hashesByName[normalName(hn)] = h } }) return hashesByName[name] } type digestInfo struct { DigestAlgorithm pkix.AlgorithmIdentifier Digest []byte } // Pack a digest along with an algorithm identifier. Mainly useful for // PKCS#1v1.5 padding (RSA). func MarshalDigest(hash crypto.Hash, digest []byte) (der []byte, ok bool) { alg, ok := PkixDigestAlgorithm(hash) if !ok { return nil, false } der, err := asn1.Marshal(digestInfo{alg, digest}) if err != nil { return nil, false } return der, true } relic-7.6.1/lib/x509tools/ecdsa_curves.go000066400000000000000000000117531455105530300201640ustar00rootroot00000000000000// // Copyright (c) SAS Institute Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package x509tools import ( "crypto/ecdsa" "crypto/elliptic" "encoding/asn1" "errors" "fmt" "math/big" "strconv" "strings" ) // Predefined named ECDSA curve type CurveDefinition struct { Bits uint Curve elliptic.Curve Oid asn1.ObjectIdentifier } var DefinedCurves = []CurveDefinition{ {256, elliptic.P256(), asn1.ObjectIdentifier{1, 2, 840, 10045, 3, 1, 7}}, {384, elliptic.P384(), asn1.ObjectIdentifier{1, 3, 132, 0, 34}}, {521, elliptic.P521(), asn1.ObjectIdentifier{1, 3, 132, 0, 35}}, } // Return the DER encoding of the ASN.1 OID of this named curve func (def *CurveDefinition) ToDer() []byte { der, err := asn1.Marshal(def.Oid) if err != nil { panic(err) } return der } // Return the names of all supported ECDSA curves func SupportedCurves() string { curves := make([]string, len(DefinedCurves)) for i, def := range DefinedCurves { curves[i] = strconv.FormatUint(uint64(def.Bits), 10) } return strings.Join(curves, ", ") } // Get a curve by its ASN.1 object identifier func CurveByOid(oid asn1.ObjectIdentifier) (*CurveDefinition, error) { for _, def := range DefinedCurves { if oid.Equal(def.Oid) { return &def, nil } } return nil, fmt.Errorf("Unsupported ECDSA curve with OID: %s\nSupported curves: %s", oid, SupportedCurves()) } // Get a curve by a dotted decimal OID string func CurveByOidString(oidstr string) (*CurveDefinition, error) { parts := strings.Split(oidstr, ".") oid := make(asn1.ObjectIdentifier, 0, len(parts)) for _, n := range parts { v, err := strconv.Atoi(n) if err != nil { return nil, errors.New("invalid OID") } oid = append(oid, v) } return CurveByOid(oid) } // Get a curve by the DER encoding of its OID func CurveByDer(der []byte) (*CurveDefinition, error) { var oid asn1.ObjectIdentifier _, err := asn1.Unmarshal(der, &oid) if err != nil { return nil, err } return CurveByOid(oid) } // Get a curve by an elliptic.Curve value func CurveByCurve(curve elliptic.Curve) (*CurveDefinition, error) { for _, def := range DefinedCurves { if curve == def.Curve { return &def, nil } } return nil, fmt.Errorf("Unsupported ECDSA curve: %v\nSupported curves: %s", curve, SupportedCurves()) } // Get a curve by a number of bits func CurveByBits(bits uint) (*CurveDefinition, error) { for _, def := range DefinedCurves { if bits == def.Bits { return &def, nil } } return nil, fmt.Errorf("Unsupported ECDSA curve: %v\nSupported curves: %s", bits, SupportedCurves()) } // Decode an ECDSA public key from its DER encoding. Both octet and bitstring // encodings are supported. func DerToPoint(curve elliptic.Curve, der []byte) (*big.Int, *big.Int) { var blob []byte switch der[0] { case asn1.TagOctetString: _, err := asn1.Unmarshal(der, &blob) if err != nil { return nil, nil } case asn1.TagBitString: var bits asn1.BitString _, err := asn1.Unmarshal(der, &bits) if err != nil { return nil, nil } blob = bits.Bytes default: return nil, nil } return elliptic.Unmarshal(curve, blob) } func PointToDer(pub *ecdsa.PublicKey) []byte { blob := elliptic.Marshal(pub.Curve, pub.X, pub.Y) der, err := asn1.Marshal(blob) if err != nil { return nil } return der } // ASN.1 structure used to encode an ECDSA signature type EcdsaSignature struct { R, S *big.Int } // Unpack an ECDSA signature from an ASN.1 DER sequence func UnmarshalEcdsaSignature(der []byte) (sig EcdsaSignature, err error) { der, err = asn1.Unmarshal(der, &sig) if err != nil || len(der) != 0 { err = errors.New("invalid ECDSA signature") } return } // Unpack an ECDSA signature consisting of two numbers concatenated per IEEE 1363 func UnpackEcdsaSignature(packed []byte) (sig EcdsaSignature, err error) { byteLen := len(packed) / 2 if len(packed) != byteLen*2 { err = errors.New("ecdsa signature is incorrect size") } else { sig.R = new(big.Int).SetBytes(packed[0:byteLen]) sig.S = new(big.Int).SetBytes(packed[byteLen:]) } return } // Marshal an ECDSA signature as an ASN.1 structure func (sig EcdsaSignature) Marshal() []byte { ret, _ := asn1.Marshal(sig) return ret } // Pack an ECDSA signature by concatenating the two numbers per IEEE 1363 func (sig EcdsaSignature) Pack() []byte { // allocate space to hold both numbers nbits := sig.R.BitLen() if s := sig.S.BitLen(); s > nbits { nbits = s } nbytes := (nbits + 7) / 8 ret := make([]byte, 2*nbytes) // serialize with padding sig.R.FillBytes(ret[0:nbytes]) sig.S.FillBytes(ret[nbytes:]) return ret } relic-7.6.1/lib/x509tools/ecdsa_curves_test.go000066400000000000000000000022471455105530300212210ustar00rootroot00000000000000package x509tools import ( "math/big" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) func TestUnpack(t *testing.T) { packed := []byte{1, 0, 2, 0} sig, err := UnpackEcdsaSignature(packed) require.NoError(t, err) assert.Equal(t, int64(256), sig.R.Int64()) assert.Equal(t, int64(512), sig.S.Int64()) invalid := []byte{1, 2, 3} _, err = UnpackEcdsaSignature(invalid) require.Error(t, err) } func TestUnmarshal(t *testing.T) { packed := []byte{0x30, 0x8, 0x2, 0x2, 0x2, 0x0, 0x2, 0x2, 0x0, 0xff} sig, err := UnmarshalEcdsaSignature(packed) require.NoError(t, err) assert.Equal(t, int64(512), sig.R.Int64()) assert.Equal(t, int64(255), sig.S.Int64()) invalid := []byte{1, 2, 3} _, err = UnmarshalEcdsaSignature(invalid) require.Error(t, err) } func TestMarshal(t *testing.T) { sig := EcdsaSignature{ R: big.NewInt(512), S: big.NewInt(255), } assert.Equal(t, []byte{0x30, 0x8, 0x2, 0x2, 0x2, 0x0, 0x2, 0x2, 0x0, 0xff}, sig.Marshal()) // test both ways around to ensure it's padded correctly assert.Equal(t, []byte{2, 0, 0, 255}, sig.Pack()) sig.R, sig.S = sig.S, sig.R assert.Equal(t, []byte{0, 255, 2, 0}, sig.Pack()) } relic-7.6.1/lib/x509tools/keylogfile.go000066400000000000000000000022521455105530300176420ustar00rootroot00000000000000// // Copyright (c) SAS Institute Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package x509tools import ( "crypto/tls" "fmt" "os" ) // If the SSLKEYLOGFILE environment variable is set, then open it for appending // and write TLS master secrets there in the "NSS Key Log Format". Use this for // debugging TLS and HTTP problems with Wireshark. func SetKeyLogFile(tconf *tls.Config) { if klf := os.Getenv("SSLKEYLOGFILE"); klf != "" { fmt.Fprintln(os.Stderr, "WARNING: SSLKEYLOGFILE is set! TLS master secrets will be logged.") f, err := os.OpenFile(klf, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0600) if err != nil { panic(err) } tconf.KeyLogWriter = f } } relic-7.6.1/lib/x509tools/names.go000066400000000000000000000131251455105530300166140ustar00rootroot00000000000000// // Copyright (c) SAS Institute Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package x509tools import ( "crypto/x509" "crypto/x509/pkix" "encoding/asn1" "encoding/binary" "fmt" "strings" "unicode/utf16" ) type NameStyle int const ( NameStyleOpenSsl NameStyle = iota NameStyleLdap NameStyleMsOsco ) type attrName struct { Type asn1.ObjectIdentifier Name string } var nameStyleLdap = []attrName{ {asn1.ObjectIdentifier{2, 5, 4, 3}, "CN"}, {asn1.ObjectIdentifier{2, 5, 4, 4}, "surname"}, {asn1.ObjectIdentifier{2, 5, 4, 5}, "serialNumber"}, {asn1.ObjectIdentifier{2, 5, 4, 6}, "C"}, {asn1.ObjectIdentifier{2, 5, 4, 7}, "L"}, {asn1.ObjectIdentifier{2, 5, 4, 8}, "ST"}, {asn1.ObjectIdentifier{2, 5, 4, 9}, "street"}, {asn1.ObjectIdentifier{2, 5, 4, 10}, "O"}, {asn1.ObjectIdentifier{2, 5, 4, 11}, "OU"}, {asn1.ObjectIdentifier{2, 5, 4, 12}, "title"}, {asn1.ObjectIdentifier{2, 5, 4, 13}, "description"}, {asn1.ObjectIdentifier{2, 5, 4, 17}, "postalCode"}, {asn1.ObjectIdentifier{2, 5, 4, 18}, "postOfficeBox"}, {asn1.ObjectIdentifier{2, 5, 4, 20}, "telephoneNumber"}, {asn1.ObjectIdentifier{2, 5, 4, 42}, "givenName"}, {asn1.ObjectIdentifier{2, 5, 4, 43}, "initials"}, {asn1.ObjectIdentifier{0, 9, 2342, 19200300, 100, 1, 1}, "UID"}, {asn1.ObjectIdentifier{0, 9, 2342, 19200300, 100, 1, 25}, "dc"}, {asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 9, 1}, "emailAddress"}, } // Per [MS-OSCO] // https://msdn.microsoft.com/en-us/library/dd947276(v=office.12).aspx var nameStyleMsOsco = []attrName{ {asn1.ObjectIdentifier{2, 5, 4, 3}, "CN"}, {asn1.ObjectIdentifier{2, 5, 4, 7}, "L"}, {asn1.ObjectIdentifier{2, 5, 4, 10}, "O"}, {asn1.ObjectIdentifier{2, 5, 4, 11}, "OU"}, {asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 9, 1}, "E"}, {asn1.ObjectIdentifier{2, 5, 4, 6}, "C"}, {asn1.ObjectIdentifier{2, 5, 4, 8}, "S"}, {asn1.ObjectIdentifier{2, 5, 4, 9}, "STREET"}, {asn1.ObjectIdentifier{2, 5, 4, 12}, "T"}, {asn1.ObjectIdentifier{2, 5, 4, 42}, "G"}, {asn1.ObjectIdentifier{2, 5, 4, 43}, "I"}, {asn1.ObjectIdentifier{2, 5, 4, 4}, "SN"}, {asn1.ObjectIdentifier{2, 5, 4, 5}, "SERIALNUMBER"}, {asn1.ObjectIdentifier{0, 9, 2342, 19200300, 100, 1, 1}, "UID"}, {asn1.ObjectIdentifier{0, 9, 2342, 19200300, 100, 1, 25}, "DC"}, {asn1.ObjectIdentifier{2, 5, 4, 13}, "Description"}, {asn1.ObjectIdentifier{2, 5, 4, 17}, "PostalCode"}, {asn1.ObjectIdentifier{2, 5, 4, 18}, "POBox"}, {asn1.ObjectIdentifier{2, 5, 4, 20}, "Phone"}, } // returned by the Format* functions in case there's something cripplingly // wrong with it const InvalidName = "" // Format the name (RDN sequence) from its raw DER to a readable style. func FormatPkixName(der []byte, style NameStyle) string { var seq pkix.RDNSequence if _, err := asn1.Unmarshal(der, &seq); err != nil { return InvalidName } var b strings.Builder if style == NameStyleOpenSsl { for _, rdnSet := range seq { for _, attr := range rdnSet { b.WriteByte('/') b.WriteString(attName(attr.Type, style)) b.WriteByte('=') b.WriteString(attValue(attr.Value, style)) } } } else { // Per RFC 2253 2.1, reverse the order for i := len(seq) - 1; i >= 0; i-- { if i < len(seq)-1 { b.WriteString(", ") } rdnSet := seq[i] for j, attr := range rdnSet { if j > 0 { b.WriteString(" + ") } b.WriteString(attName(attr.Type, style)) b.WriteByte('=') b.WriteString(attValue(attr.Value, style)) } } } return b.String() } func attName(t asn1.ObjectIdentifier, style NameStyle) string { var names []attrName var defaultPrefix string switch style { case NameStyleLdap, NameStyleOpenSsl: names = nameStyleLdap case NameStyleMsOsco: names = nameStyleMsOsco defaultPrefix = "OID." default: panic("invalid style argument") } for _, name := range names { if name.Type.Equal(t) { return name.Name } } return defaultPrefix + t.String() } func attValue(raw interface{}, style NameStyle) string { var value string switch v := raw.(type) { case string: value = v case fmt.Stringer: value = v.String() case int64: value = fmt.Sprint(v) default: return InvalidName } switch style { case NameStyleOpenSsl: value = strings.ReplaceAll(value, "/", "\\/") case NameStyleLdap, NameStyleMsOsco: quote := false if len(value) == 0 { quote = true } if strings.HasPrefix(value, " ") || strings.HasSuffix(value, " ") { quote = true } if i := strings.IndexAny(value, ",+=\n<>#;'\""); i >= 0 { quote = true } value = strings.ReplaceAll(value, "\"", "\"\"") if quote { value = "\"" + value + "\"" } } return value } func ToBMPString(value string) asn1.RawValue { runes := utf16.Encode([]rune(value)) raw := make([]byte, 2*len(runes)) for i, r := range runes { binary.BigEndian.PutUint16(raw[i*2:], r) } return asn1.RawValue{Tag: asn1.TagBMPString, Bytes: raw} } // Format the certificate subject name in LDAP style func FormatSubject(cert *x509.Certificate) string { return FormatPkixName(cert.RawSubject, NameStyleLdap) } // Format the certificate issuer name in LDAP style func FormatIssuer(cert *x509.Certificate) string { return FormatPkixName(cert.RawIssuer, NameStyleLdap) } relic-7.6.1/lib/x509tools/names_test.go000066400000000000000000000030741455105530300176550ustar00rootroot00000000000000package x509tools import ( "crypto/x509/pkix" "encoding/asn1" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) func TestNames(t *testing.T) { n := pkix.Name{ CommonName: "foo/bar", Organization: []string{"ham ", "\"spam\"", " eggs"}, Locality: []string{"north+southville"}, Country: []string{""}, ExtraNames: []pkix.AttributeTypeAndValue{ {Type: asn1.ObjectIdentifier{1, 2, 3, 4}, Value: "5678"}, {Type: asn1.ObjectIdentifier{2, 5, 4, 17}, Value: "888"}, }, } name, err := asn1.Marshal(n.ToRDNSequence()) require.NoError(t, err) assert.Equal(t, `/C=/L=north+southville/O=ham /O= eggs/O="spam"/CN=foo\/bar/1.2.3.4=5678/postalCode=888`, FormatPkixName(name, NameStyleOpenSsl)) assert.Equal(t, `postalCode=888, 1.2.3.4=5678, CN=foo/bar, O="ham " + O=" eggs" + O="""spam""", L="north+southville", C=""`, FormatPkixName(name, NameStyleLdap)) assert.Equal(t, `PostalCode=888, OID.1.2.3.4=5678, CN=foo/bar, O="ham " + O=" eggs" + O="""spam""", L="north+southville", C=""`, FormatPkixName(name, NameStyleMsOsco)) } func TestBMP(t *testing.T) { str := "✔" bmp := ToBMPString(str) assert.Equal(t, []byte{0x27, 0x14}, bmp.Bytes) } func TestBMPName(t *testing.T) { n := pkix.RDNSequence{ pkix.RelativeDistinguishedNameSET{pkix.AttributeTypeAndValue{ Type: asn1.ObjectIdentifier{2, 5, 4, 10}, Value: asn1.RawValue{ Tag: asn1.TagBMPString, Bytes: []byte{0x27, 0x14}, }, }}, } blob, err := asn1.Marshal(n) require.NoError(t, err) assert.Equal(t, `O=✔`, FormatPkixName(blob, NameStyleLdap)) } relic-7.6.1/lib/x509tools/pkix.go000066400000000000000000000155451455105530300164740ustar00rootroot00000000000000// // Copyright (c) SAS Institute Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package x509tools import ( "crypto" "crypto/ecdsa" "crypto/rsa" "crypto/x509" "crypto/x509/pkix" "encoding/asn1" "errors" "fmt" ) var ( // RFC 3279 OidPublicKeyRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 1} OidPublicKeyDSA = asn1.ObjectIdentifier{1, 2, 840, 10040, 4, 1} OidPublicKeyECDSA = asn1.ObjectIdentifier{1, 2, 840, 10045, 2, 1} oidSignatureMD5WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 4} oidSignatureSHA1WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 5} oidSignatureSHA256WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 11} oidSignatureSHA384WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 12} oidSignatureSHA512WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 13} oidSignatureDSAWithSHA1 = asn1.ObjectIdentifier{1, 2, 840, 10040, 4, 3} oidSignatureDSAWithSHA256 = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 3, 2} oidSignatureECDSAWithSHA1 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 1} oidSignatureECDSAWithSHA256 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 3, 2} oidSignatureECDSAWithSHA384 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 3, 3} oidSignatureECDSAWithSHA512 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 3, 4} oidISOSignatureSHA1WithRSA = asn1.ObjectIdentifier{1, 3, 14, 3, 2, 29} // RFC 4055 OidMGF1 = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 8} OidSignatureRSAPSS = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 10} oidExtensionSubjectKeyId = asn1.ObjectIdentifier{2, 5, 29, 14} oidExtensionKeyUsage = asn1.ObjectIdentifier{2, 5, 29, 15} oidExtensionSubjectAltName = asn1.ObjectIdentifier{2, 5, 29, 17} oidExtensionBasicConstraints = asn1.ObjectIdentifier{2, 5, 29, 19} oidExtensionAuthorityKeyId = asn1.ObjectIdentifier{2, 5, 29, 35} oidExtensionExtendedKeyUsage = asn1.ObjectIdentifier{2, 5, 29, 37} ) type sigAlgInfo struct { oid asn1.ObjectIdentifier pubKeyAlgo x509.PublicKeyAlgorithm hash crypto.Hash } var sigAlgInfos = []sigAlgInfo{ // without a digest {OidPublicKeyRSA, x509.RSA, 0}, {OidSignatureRSAPSS, x509.RSA, 0}, {OidPublicKeyDSA, x509.DSA, 0}, {OidPublicKeyECDSA, x509.ECDSA, 0}, // with a digest {oidSignatureMD5WithRSA, x509.RSA, crypto.MD5}, {oidSignatureSHA1WithRSA, x509.RSA, crypto.SHA1}, {oidISOSignatureSHA1WithRSA, x509.RSA, crypto.SHA1}, {oidSignatureSHA256WithRSA, x509.RSA, crypto.SHA256}, {oidSignatureSHA384WithRSA, x509.RSA, crypto.SHA384}, {oidSignatureSHA512WithRSA, x509.RSA, crypto.SHA512}, {oidSignatureDSAWithSHA1, x509.DSA, crypto.SHA1}, {oidSignatureDSAWithSHA256, x509.DSA, crypto.SHA256}, {oidSignatureECDSAWithSHA1, x509.ECDSA, crypto.SHA1}, {oidSignatureECDSAWithSHA256, x509.ECDSA, crypto.SHA256}, {oidSignatureECDSAWithSHA384, x509.ECDSA, crypto.SHA384}, {oidSignatureECDSAWithSHA512, x509.ECDSA, crypto.SHA512}, } // Given a public key and signer options, return the appropriate X.509 digest and signature algorithms func PkixAlgorithms(pub crypto.PublicKey, opts crypto.SignerOpts) (digestAlg, sigAlg pkix.AlgorithmIdentifier, err error) { digestAlg, ok := PkixDigestAlgorithm(opts.HashFunc()) if !ok { err = errors.New("unsupported digest algorithm") return } if pss, ok := opts.(*rsa.PSSOptions); ok { rsapub, ok := pub.(*rsa.PublicKey) if !ok { err = errors.New("RSA-PSS is only valid for RSA keys") return } var params asn1.RawValue params, err = MarshalRSAPSSParameters(rsapub, pss) if err != nil { return } sigAlg = pkix.AlgorithmIdentifier{ Algorithm: OidSignatureRSAPSS, Parameters: params, } return } switch pub.(type) { case *rsa.PublicKey: sigAlg.Algorithm = OidPublicKeyRSA case *ecdsa.PublicKey: sigAlg.Algorithm = OidPublicKeyECDSA default: err = errors.New("unsupported public key algorithm") return } sigAlg.Parameters = asn1.NullRawValue return } // Convert a crypto.Hash to a X.509 AlgorithmIdentifier func PkixDigestAlgorithm(hash crypto.Hash) (alg pkix.AlgorithmIdentifier, ok bool) { if oid, ok2 := HashOids[hash]; ok2 { alg.Algorithm = oid alg.Parameters = asn1.NullRawValue ok = true } return } // Convert a X.509 AlgorithmIdentifier to a crypto.Hash func PkixDigestToHash(alg pkix.AlgorithmIdentifier) (hash crypto.Hash, ok bool) { for hash, oid := range HashOids { if alg.Algorithm.Equal(oid) { return hash, true } } return 0, false } // Convert a X.509 AlgorithmIdentifier to a crypto.Hash func PkixDigestToHashE(alg pkix.AlgorithmIdentifier) (hash crypto.Hash, err error) { hash, ok := PkixDigestToHash(alg) if ok && hash.Available() { return hash, nil } return 0, UnknownDigestError{Algorithm: alg.Algorithm} } // Convert a crypto.PublicKey to a X.509 AlgorithmIdentifier func PkixPublicKeyAlgorithm(pub crypto.PublicKey) (alg pkix.AlgorithmIdentifier, ok bool) { _, alg, err := PkixAlgorithms(pub, nil) return alg, err == nil } // Verify a signature using the algorithm specified by the given X.509 AlgorithmIdentifier func PkixVerify(pub crypto.PublicKey, digestAlg, sigAlg pkix.AlgorithmIdentifier, digest, sig []byte) error { hash, err := PkixDigestToHashE(digestAlg) if err != nil { return err } var info sigAlgInfo for _, a := range sigAlgInfos { if sigAlg.Algorithm.Equal(a.oid) { info = a } } if info.hash != 0 && info.hash != hash { return errors.New("signature type does not match digest type") } switch info.pubKeyAlgo { case x509.RSA: key, ok := pub.(*rsa.PublicKey) if !ok { return errors.New("incorrect key type for signature") } if sigAlg.Algorithm.Equal(OidSignatureRSAPSS) { opts, err := UnmarshalRSAPSSParameters(hash, sigAlg.Parameters) if err != nil { return err } return rsa.VerifyPSS(key, hash, digest, sig, opts) } return rsa.VerifyPKCS1v15(key, hash, digest, sig) case x509.ECDSA: key, ok := pub.(*ecdsa.PublicKey) if !ok { return errors.New("incorrect key type for signature") } esig, err := UnmarshalEcdsaSignature(sig) if err != nil { return err } if !ecdsa.Verify(key, digest, esig.R, esig.S) { return errors.New("ECDSA verification failed") } return nil default: return errors.New("unsupported public key algorithm") } } type UnknownDigestError struct { Algorithm asn1.ObjectIdentifier } func (e UnknownDigestError) Error() string { return fmt.Sprintf("unsupported hash algorithm %s", e.Algorithm) } relic-7.6.1/lib/x509tools/printcert.go000066400000000000000000000213561455105530300175300ustar00rootroot00000000000000// // Copyright (c) SAS Institute Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package x509tools import ( "crypto/ecdsa" "crypto/rsa" "crypto/x509" "encoding/asn1" "fmt" "io" "time" ) var keyUsageNames = map[x509.KeyUsage]string{ x509.KeyUsageDigitalSignature: "digitalSignature", x509.KeyUsageContentCommitment: "nonRepudiation", x509.KeyUsageKeyEncipherment: "keyEncipherment", x509.KeyUsageDataEncipherment: "dataEncipherment", x509.KeyUsageKeyAgreement: "keyAgreement", x509.KeyUsageCertSign: "keyCertSign", x509.KeyUsageCRLSign: "cRLSign", x509.KeyUsageEncipherOnly: "encipherOnly", x509.KeyUsageDecipherOnly: "decipherOnly", } var extKeyUsageNames = map[x509.ExtKeyUsage]string{ x509.ExtKeyUsageAny: "any", x509.ExtKeyUsageServerAuth: "serverAuth", x509.ExtKeyUsageClientAuth: "clientAuth", x509.ExtKeyUsageCodeSigning: "codeSigning", x509.ExtKeyUsageEmailProtection: "emailProtection", x509.ExtKeyUsageIPSECEndSystem: "ipsecEndSystem", x509.ExtKeyUsageIPSECTunnel: "ipsecTunnel", x509.ExtKeyUsageIPSECUser: "ipsecUser", x509.ExtKeyUsageTimeStamping: "timeStamping", x509.ExtKeyUsageOCSPSigning: "OCSPSigning", x509.ExtKeyUsageMicrosoftServerGatedCrypto: "msServerGatedCrypto", x509.ExtKeyUsageNetscapeServerGatedCrypto: "nsServerGatedCrypto", x509.ExtKeyUsageMicrosoftCommercialCodeSigning: "msCodeCom", x509.ExtKeyUsageMicrosoftKernelCodeSigning: "msKernCode", } var knownExtensions = []asn1.ObjectIdentifier{ asn1.ObjectIdentifier{2, 5, 29, 14}, // oidExtensionSubjectKeyId asn1.ObjectIdentifier{2, 5, 29, 15}, // oidExtensionKeyUsage asn1.ObjectIdentifier{2, 5, 29, 37}, // oidExtensionExtendedKeyUsage asn1.ObjectIdentifier{2, 5, 29, 35}, // oidExtensionAuthorityKeyId asn1.ObjectIdentifier{2, 5, 29, 19}, // oidExtensionBasicConstraints asn1.ObjectIdentifier{2, 5, 29, 17}, // oidExtensionSubjectAltName asn1.ObjectIdentifier{2, 5, 29, 32}, // oidExtensionCertificatePolicies asn1.ObjectIdentifier{2, 5, 29, 30}, // oidExtensionNameConstraints asn1.ObjectIdentifier{2, 5, 29, 31}, // oidExtensionCRLDistributionPoints asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 1, 1}, // oidExtensionAuthorityInfoAccess asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 48, 1}, // oidAuthorityInfoAccessOcsp asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 48, 2}, // oidAuthorityInfoAccessIssuers } // FprintCertificate formats a certificate for display func FprintCertificate(w io.Writer, cert *x509.Certificate) { fmt.Fprintln(w, "Version:", cert.Version) if cert.SerialNumber.BitLen() > 63 { fmt.Fprintf(w, "Serial: 0x%x\n", cert.SerialNumber) } else { fmt.Fprintf(w, "Serial: %d (0x%x)\n", cert.SerialNumber, cert.SerialNumber) } fmt.Fprintln(w, "Subject:", FormatSubject(cert)) fmt.Fprintln(w, "Issuer: ", FormatIssuer(cert)) fmt.Fprintln(w, "Valid: ", cert.NotBefore) fmt.Fprintln(w, "Expires:", cert.NotAfter) fmt.Fprintln(w, "Period: ", subDate(cert.NotAfter, cert.NotBefore)) switch k := cert.PublicKey.(type) { case *rsa.PublicKey: n := fmt.Sprintf("%x", k.N) fmt.Fprintf(w, "Pub key: RSA bits=%d e=%d n=%s...%s\n", k.N.BitLen(), k.E, n[:8], n[len(n)-8:]) case *ecdsa.PublicKey: p := k.Params() x := fmt.Sprintf("%x", k.X) y := fmt.Sprintf("%x", k.Y) fmt.Fprintf(w, "Public key: ECDSA bits=%d name=%s x=%s... y=...%s\n", p.BitSize, p.Name, x[:8], y[len(y)-8:]) default: fmt.Fprintf(w, "Public key: %T\n", k) } fmt.Fprintln(w, "Sig alg:", cert.SignatureAlgorithm) fmt.Fprintln(w, "Extensions:") // subject alternate names printSAN(w, cert) // basic constraints if cert.BasicConstraintsValid { cons := fmt.Sprintf("isCA=%t", cert.IsCA) if cert.MaxPathLenZero { cons += " MaxPathLen=0" } else if cert.MaxPathLen > 0 { cons += fmt.Sprintf(" MaxPathLen=%d", cert.MaxPathLen) } fmt.Fprintln(w, " Basic constraints: "+cons) } // Name constraints printNameConstraints(w, cert) // key usage usage := "" for n, name := range keyUsageNames { if cert.KeyUsage&n != 0 { usage += ", " + name } } if usage != "" { fmt.Fprintln(w, " Key Usage:", usage[2:]) } // extended key usage usage = "" for _, u := range cert.ExtKeyUsage { name := extKeyUsageNames[u] if name == "" { name = fmt.Sprintf("%d", u) } usage += ", " + name } for _, u := range cert.UnknownExtKeyUsage { usage += ", " + u.String() } if usage != "" { fmt.Fprintln(w, " Extended key usage:", usage[2:]) } // keyids if len(cert.SubjectKeyId) != 0 { fmt.Fprintf(w, " Subject key ID: %x\n", cert.SubjectKeyId) } if len(cert.AuthorityKeyId) != 0 { fmt.Fprintf(w, " Authority key ID: %x\n", cert.AuthorityKeyId) } // authority info if len(cert.OCSPServer) != 0 { fmt.Fprintln(w, " OCSP Servers:") for _, s := range cert.OCSPServer { fmt.Fprintln(w, " ", s) } } if len(cert.IssuingCertificateURL) != 0 { fmt.Fprintln(w, " Issuing authority URLs:") for _, s := range cert.IssuingCertificateURL { fmt.Fprintln(w, " ", s) } } // CRL if len(cert.CRLDistributionPoints) != 0 { fmt.Fprintln(w, " CRL Distribution Points:") for _, s := range cert.CRLDistributionPoints { fmt.Fprintln(w, " ", s) } } // Policy IDs if len(cert.PolicyIdentifiers) != 0 { fmt.Fprintln(w, " Policy Identifiers:") for _, s := range cert.PolicyIdentifiers { fmt.Fprintln(w, " ", s.String()) } } // Other for _, ex := range cert.Extensions { if knownExtension(ex.Id) { continue } critical := "" if ex.Critical { critical = " (critical)" } fmt.Fprintf(w, " Extension %s%s: %x\n", ex.Id, critical, ex.Value) } } func knownExtension(id asn1.ObjectIdentifier) bool { for _, known := range knownExtensions { if known.Equal(id) { return true } } return false } const ( durYear = 8766 * time.Hour yearSlop = 2 * 24 * time.Hour ) // calculate duration using calendar dates func subDate(end, start time.Time) string { approx := "~" dur := end.Sub(start) switch { case dur >= durYear-yearSlop: years := int((dur + yearSlop) / durYear) if start.AddDate(years, 0, 0).Equal(end) { approx = "" } if years > 1 { return fmt.Sprintf("%s%d years", approx, years) } return approx + "1 year" case dur >= 24*time.Hour: days := int(dur / (24 * time.Hour)) if start.AddDate(0, 0, days).Equal(end) { approx = "" } if days > 1 { return fmt.Sprintf("%s%d days", approx, days) } return approx + "1 day" default: return dur.String() } } func printSAN(w io.Writer, cert *x509.Certificate) { if len(cert.DNSNames) != 0 || len(cert.EmailAddresses) != 0 || len(cert.IPAddresses) != 0 || len(cert.URIs) != 0 { fmt.Fprintln(w, " Subject alternate names:") for _, s := range cert.DNSNames { fmt.Fprintln(w, " dns:"+s) } for _, s := range cert.EmailAddresses { fmt.Fprintln(w, " email:"+s) } for _, s := range cert.IPAddresses { fmt.Fprintln(w, " ip:"+s.String()) } for _, s := range cert.URIs { fmt.Fprintln(w, " uri:"+s.String()) } } } func printNameConstraints(w io.Writer, cert *x509.Certificate) { if len(cert.PermittedDNSDomains) != 0 || len(cert.ExcludedDNSDomains) != 0 || len(cert.PermittedIPRanges) != 0 || len(cert.ExcludedIPRanges) != 0 || len(cert.PermittedEmailAddresses) != 0 || len(cert.ExcludedEmailAddresses) != 0 || len(cert.PermittedURIDomains) != 0 || len(cert.ExcludedURIDomains) != 0 { fmt.Fprintln(w, " Name constraints:") for _, s := range cert.PermittedDNSDomains { fmt.Fprintln(w, " Permitted DNS domain:", s) } for _, s := range cert.ExcludedDNSDomains { fmt.Fprintln(w, " Excluded DNS domain:", s) } for _, s := range cert.PermittedIPRanges { fmt.Fprintln(w, " Permitted IP range:", s) } for _, s := range cert.ExcludedIPRanges { fmt.Fprintln(w, " Excluded IP range:", s) } for _, s := range cert.PermittedEmailAddresses { fmt.Fprintln(w, " Permitted Email Addresses:", s) } for _, s := range cert.ExcludedEmailAddresses { fmt.Fprintln(w, " Excluded Email Addresses:", s) } for _, s := range cert.PermittedURIDomains { fmt.Fprintln(w, " Permitted URI domain:", s) } for _, s := range cert.ExcludedURIDomains { fmt.Fprintln(w, " Excluded URI domain:", s) } } } relic-7.6.1/lib/x509tools/rsapss.go000066400000000000000000000070611455105530300170260ustar00rootroot00000000000000// // Copyright (c) SAS Institute Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package x509tools import ( "crypto" "crypto/rsa" "crypto/x509/pkix" "encoding/asn1" "errors" ) // pssParameters reflects the parameters in an AlgorithmIdentifier that // specifies RSA PSS. See https://tools.ietf.org/html/rfc3447#appendix-A.2.3 type pssParameters struct { // The following three fields are not marked as // optional because the default values specify SHA-1, // which is no longer suitable for use in signatures. Hash pkix.AlgorithmIdentifier `asn1:"explicit,tag:0"` MGF pkix.AlgorithmIdentifier `asn1:"explicit,tag:1"` SaltLength int `asn1:"explicit,tag:2"` TrailerField int `asn1:"optional,explicit,tag:3,default:1"` } func MarshalRSAPSSParameters(pub *rsa.PublicKey, opts *rsa.PSSOptions) (asn1.RawValue, error) { hashAlg, ok := PkixDigestAlgorithm(opts.Hash) if !ok { return asn1.RawValue{}, errors.New("unsupported digest algorithm") } hashRaw, err := asn1.Marshal(hashAlg) if err != nil { return asn1.RawValue{}, err } saltLength := opts.SaltLength switch saltLength { case rsa.PSSSaltLengthAuto: saltLength = (pub.N.BitLen()+7)/8 - 2 - opts.Hash.Size() case rsa.PSSSaltLengthEqualsHash: saltLength = opts.Hash.Size() } params := pssParameters{ Hash: hashAlg, MGF: pkix.AlgorithmIdentifier{ Algorithm: OidMGF1, Parameters: asn1.RawValue{FullBytes: hashRaw}, }, SaltLength: saltLength, TrailerField: 1, } serialized, err := asn1.Marshal(params) if err != nil { return asn1.RawValue{}, err } return asn1.RawValue{FullBytes: serialized}, nil } func UnmarshalRSAPSSParameters(hash crypto.Hash, raw asn1.RawValue) (*rsa.PSSOptions, error) { hashOid, ok := HashOids[hash] if !ok { return nil, errors.New("unsupported digest algorithm") } if raw.Tag == asn1.TagNull { // defaults if hash != crypto.SHA1 { return nil, errors.New("RSA-PSS parameters not provided but digest type is not SHA-1") } return &rsa.PSSOptions{Hash: crypto.SHA1, SaltLength: 20}, nil } var params pssParameters if rest, err := asn1.Unmarshal(raw.FullBytes, ¶ms); err != nil || len(rest) > 0 { return nil, errors.New("invalid RSA-PSS parameters") } // validate that all hash OIDs match if !params.Hash.Algorithm.Equal(hashOid) { return nil, errors.New("digest type mismatch in RSA-PSS parameters") } if !params.MGF.Algorithm.Equal(OidMGF1) { return nil, errors.New("unsupported MGF in RSA-PSS parameters") } var mgfDigest pkix.AlgorithmIdentifier if rest, err := asn1.Unmarshal(params.MGF.Parameters.FullBytes, &mgfDigest); err != nil || len(rest) > 0 { return nil, errors.New("invalid RSA-PSS parameters") } if !mgfDigest.Algorithm.Equal(hashOid) { return nil, errors.New("digest type mismatch in RSA-PSS parameters") } if params.TrailerField != 0 && params.TrailerField != 1 { return nil, errors.New("invalid RSA-PSS parameters") } if params.SaltLength == 0 { params.SaltLength = hash.Size() } return &rsa.PSSOptions{Hash: hash, SaltLength: params.SaltLength}, nil } relic-7.6.1/lib/x509tools/util.go000066400000000000000000000070571455105530300164750ustar00rootroot00000000000000// // Copyright (c) SAS Institute Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package x509tools import ( "crypto" "crypto/ecdsa" "crypto/elliptic" "crypto/rand" "crypto/rsa" "crypto/sha1" "crypto/x509" "crypto/x509/pkix" "encoding/asn1" "errors" "io" "math/big" ) // Make a random 12 byte big.Int func MakeSerial() *big.Int { blob := make([]byte, 12) if _, err := io.ReadFull(rand.Reader, blob); err != nil { panic(err) } return new(big.Int).SetBytes(blob) } // Choose a X509 signature algorithm suitable for the specified public key func X509SignatureAlgorithm(pub crypto.PublicKey) x509.SignatureAlgorithm { switch k := pub.(type) { case *rsa.PublicKey: if ArgRSAPSS { return x509.SHA256WithRSAPSS } return x509.SHA256WithRSA case *ecdsa.PublicKey: switch k.Curve { case elliptic.P256(): return x509.ECDSAWithSHA256 case elliptic.P384(): return x509.ECDSAWithSHA384 case elliptic.P521(): return x509.ECDSAWithSHA512 } return x509.ECDSAWithSHA256 default: return x509.UnknownSignatureAlgorithm } } type pkixPublicKey struct { Algo pkix.AlgorithmIdentifier BitString asn1.BitString } // Calculcate subject key identifier from a public key per RFC 3280 func SubjectKeyID(pub crypto.PublicKey) ([]byte, error) { der, err := x509.MarshalPKIXPublicKey(pub) if err != nil { return nil, err } // extract the raw "bit string" part of the public key bytes var pki pkixPublicKey if rest, err := asn1.Unmarshal(der, &pki); err != nil { return nil, err } else if len(rest) != 0 { return nil, errors.New("trailing garbage on public key") } digest := sha1.Sum(pki.BitString.RightAlign()) return digest[:], nil } // Test whether two public or private keys have the same public key func SameKey(pub1, pub2 interface{}) bool { if privkey, ok := pub1.(crypto.Signer); ok { pub1 = privkey.Public() } if privkey, ok := pub2.(crypto.Signer); ok { pub2 = privkey.Public() } switch key1 := pub1.(type) { case *rsa.PublicKey: key2, ok := pub2.(*rsa.PublicKey) return ok && key1.E == key2.E && key1.N.Cmp(key2.N) == 0 case *ecdsa.PublicKey: key2, ok := pub2.(*ecdsa.PublicKey) return ok && key1.X.Cmp(key2.X) == 0 && key1.Y.Cmp(key2.Y) == 0 default: return false } } // Verify an RSA or ECDSA signature func Verify(pub interface{}, hash crypto.Hash, hashed []byte, sig []byte) error { switch pubk := pub.(type) { case *rsa.PublicKey: return rsa.VerifyPKCS1v15(pubk, hash, hashed, sig) case *ecdsa.PublicKey: esig, err := UnmarshalEcdsaSignature(sig) if err != nil { return err } if !ecdsa.Verify(pubk, hashed, esig.R, esig.S) { return errors.New("ECDSA verification failed") } return nil } return errors.New("unsupported public key algorithm") } // Determine the type of a public or private key func GetPublicKeyAlgorithm(key interface{}) x509.PublicKeyAlgorithm { if privkey, ok := key.(crypto.Signer); ok { key = privkey.Public() } switch key.(type) { case *rsa.PublicKey: return x509.RSA case *ecdsa.PublicKey: return x509.ECDSA default: return x509.UnknownPublicKeyAlgorithm } } relic-7.6.1/lib/x509tools/util_test.go000066400000000000000000000020701455105530300175220ustar00rootroot00000000000000package x509tools_test import ( "crypto/ecdsa" "crypto/ed25519" "crypto/elliptic" "crypto/rsa" "crypto/x509" "testing" "github.com/sassoftware/relic/v7/lib/x509tools" "github.com/stretchr/testify/assert" ) func TestSignatureAlgorithm(t *testing.T) { t.Parallel() assert.Equal(t, x509.SHA256WithRSA, x509tools.X509SignatureAlgorithm(&rsa.PublicKey{})) x509tools.ArgRSAPSS = true assert.Equal(t, x509.SHA256WithRSAPSS, x509tools.X509SignatureAlgorithm(&rsa.PublicKey{})) x509tools.ArgRSAPSS = false assert.Equal(t, x509.ECDSAWithSHA256, x509tools.X509SignatureAlgorithm(&ecdsa.PublicKey{Curve: elliptic.P224()})) assert.Equal(t, x509.ECDSAWithSHA256, x509tools.X509SignatureAlgorithm(&ecdsa.PublicKey{Curve: elliptic.P256()})) assert.Equal(t, x509.ECDSAWithSHA384, x509tools.X509SignatureAlgorithm(&ecdsa.PublicKey{Curve: elliptic.P384()})) assert.Equal(t, x509.ECDSAWithSHA512, x509tools.X509SignatureAlgorithm(&ecdsa.PublicKey{Curve: elliptic.P521()})) assert.Equal(t, x509.UnknownSignatureAlgorithm, x509tools.X509SignatureAlgorithm(ed25519.PublicKey{})) } relic-7.6.1/lib/x509tools/x509cmd.go000066400000000000000000000303751455105530300167100ustar00rootroot00000000000000// // Copyright (c) SAS Institute Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package x509tools import ( "bytes" "crypto" "crypto/ecdsa" "crypto/ed25519" "crypto/rand" "crypto/rsa" "crypto/x509" "crypto/x509/pkix" "encoding/pem" "errors" "fmt" "io" "math/big" "os" "strings" "time" "github.com/spf13/cobra" "golang.org/x/term" ) var ( ArgCountry string ArgOrganization string ArgOrganizationalUnit string ArgLocality string ArgProvince string ArgCommonName string ArgDNSNames string ArgEmailNames string ArgKeyUsage string ArgExpireDays uint ArgCertAuthority bool ArgSerial string ArgInteractive bool ArgRSAPSS bool ) // Add flags associated with X509 requests to the given command func AddRequestFlags(cmd *cobra.Command) { cmd.Flags().StringVar(&ArgCountry, "countryName", "", "Subject name") cmd.Flags().StringVar(&ArgProvince, "stateOrProvinceName", "", "Subject name") cmd.Flags().StringVar(&ArgLocality, "localityName", "", "Subject name") cmd.Flags().StringVar(&ArgOrganization, "organizationName", "", "Subject name") cmd.Flags().StringVar(&ArgOrganizationalUnit, "organizationalUnitName", "", "Subject name") cmd.Flags().StringVarP(&ArgCommonName, "commonName", "n", "", "Subject commonName") cmd.Flags().StringVar(&ArgDNSNames, "alternate-dns", "", "DNS subject alternate name (comma or space separated)") cmd.Flags().StringVar(&ArgEmailNames, "alternate-email", "", "Email subject alternate name (comma or space separated)") cmd.Flags().BoolVarP(&ArgInteractive, "interactive", "i", false, "Prompt before signing certificate") cmd.Flags().BoolVar(&ArgRSAPSS, "rsa-pss", false, "Use RSA-PSS signature") } // Add flags associated with X509 certificate creation to the given command func AddCertFlags(cmd *cobra.Command) { AddRequestFlags(cmd) cmd.Flags().BoolVar(&ArgCertAuthority, "cert-authority", false, "If this certificate is an authority") cmd.Flags().StringVarP(&ArgKeyUsage, "key-usage", "U", "", "Key usage, one of: serverAuth clientAuth codeSigning emailProtection keyCertSign") cmd.Flags().UintVarP(&ArgExpireDays, "expire-days", "e", 36523, "Number of days before certificate expires") cmd.Flags().StringVar(&ArgSerial, "serial", "", "Set the serial number of the certificate. Random if not specified.") } // Split a space- and/or comma-seperated string func splitAndTrim(s string) []string { if s == "" { return nil } s = strings.ReplaceAll(s, ",", " ") pieces := strings.Split(s, " ") ret := make([]string, 0, len(pieces)) for _, p := range pieces { p = strings.Trim(p, " ") if p != "" { ret = append(ret, p) } } return ret } // Build a subject name from command-line arguments func subjName() (name pkix.Name) { if ArgCountry != "" { name.Country = []string{ArgCountry} } if ArgProvince != "" { name.Province = []string{ArgProvince} } if ArgLocality != "" { name.Locality = []string{ArgLocality} } if ArgOrganization != "" { name.Organization = []string{ArgOrganization} } if ArgOrganizationalUnit != "" { name.OrganizationalUnit = []string{ArgOrganizationalUnit} } name.CommonName = ArgCommonName return } // Set both basic and extended key usage func setUsage(template *x509.Certificate) error { usage := x509.KeyUsageDigitalSignature var extended []x509.ExtKeyUsage switch strings.ToLower(ArgKeyUsage) { case "serverauth": usage |= x509.KeyUsageKeyEncipherment | x509.KeyUsageKeyAgreement extended = append(extended, x509.ExtKeyUsageServerAuth) case "clientauth": usage |= x509.KeyUsageKeyEncipherment | x509.KeyUsageKeyAgreement extended = append(extended, x509.ExtKeyUsageClientAuth) case "codesigning": extended = append(extended, x509.ExtKeyUsageCodeSigning) case "emailprotection": usage |= x509.KeyUsageContentCommitment | x509.KeyUsageKeyEncipherment | x509.KeyUsageKeyAgreement extended = append(extended, x509.ExtKeyUsageEmailProtection) case "keycertsign": usage |= x509.KeyUsageCertSign | x509.KeyUsageCRLSign case "": return nil default: return errors.New("invalid key-usage") } template.KeyUsage = usage template.ExtKeyUsage = extended return nil } func fillCertFields(template *x509.Certificate, subjectPub, issuerPub crypto.PublicKey) error { if ArgSerial != "" { serial, ok := new(big.Int).SetString(ArgSerial, 0) if !ok { return errors.New("invalid serial number, must be decimal or hexadecimal format") } template.SerialNumber = serial } else if template.SerialNumber == nil { template.SerialNumber = MakeSerial() if template.SerialNumber == nil { return errors.New("Failed to generate a serial number") } } if ArgCommonName != "" { template.Subject = subjName() } if ArgDNSNames != "" { template.DNSNames = splitAndTrim(ArgDNSNames) } if ArgEmailNames != "" { template.EmailAddresses = splitAndTrim(ArgEmailNames) } template.SignatureAlgorithm = X509SignatureAlgorithm(issuerPub) template.NotBefore = time.Now().Add(time.Hour * -24) template.NotAfter = time.Now().Add(time.Hour * 24 * time.Duration(ArgExpireDays)) template.IsCA = ArgCertAuthority template.BasicConstraintsValid = true ski, err := SubjectKeyID(subjectPub) if err != nil { return err } template.SubjectKeyId = ski return setUsage(template) } func toPemString(der []byte, pemType string) string { block := &pem.Block{Type: pemType, Bytes: der} return string(pem.EncodeToMemory(block)) } // Make a X509 certificate request using command-line arguments and return the // PEM string func MakeRequest(rand io.Reader, key crypto.Signer) (string, error) { var template x509.CertificateRequest template.Subject = subjName() template.DNSNames = splitAndTrim(ArgDNSNames) template.EmailAddresses = splitAndTrim(ArgEmailNames) template.SignatureAlgorithm = X509SignatureAlgorithm(key.Public()) csr, err := x509.CreateCertificateRequest(rand, &template, key) if err != nil { return "", err } return toPemString(csr, "CERTIFICATE REQUEST"), nil } // Make a self-signed X509 certificate using command-line arguments and return // the PEM string func MakeCertificate(rand io.Reader, key crypto.Signer) (string, error) { var template x509.Certificate if err := fillCertFields(&template, key.Public(), key.Public()); err != nil { return "", err } template.Issuer = template.Subject cert, err := confirmAndCreate(&template, &template, key.Public(), key) if err != nil { return "", err } return toPemString(cert, "CERTIFICATE"), nil } // SignCSR takes a PKCS#10 signing request in PEM or DER format as input and // produces a signed certificate in PEM format. Any command-line flags set will // override the CSR contents. func SignCSR(csrBytes []byte, rand io.Reader, key crypto.Signer, cacert *x509.Certificate, copyExtensions bool) (string, error) { // parse and validate CSR csrBytes, err := parseMaybePEM(csrBytes, "CERTIFICATE REQUEST") if err != nil { return "", err } csr, err := x509.ParseCertificateRequest(csrBytes) if err != nil { return "", fmt.Errorf("parsing CSR: %w", err) } if err := csr.CheckSignature(); err != nil { return "", fmt.Errorf("validating CSR: %w", err) } // update fields template := &x509.Certificate{Subject: csr.Subject} if copyExtensions { // drop CSR extensions that are overridden or merged with our args for _, ex := range csr.Extensions { switch { case ArgCertAuthority && ex.Id.Equal(oidExtensionBasicConstraints): // arg has set CA constraint case ArgKeyUsage != "" && (ex.Id.Equal(oidExtensionKeyUsage) || ex.Id.Equal(oidExtensionExtendedKeyUsage)): // arg has set key usage case ex.Id.Equal(oidExtensionSubjectKeyId) || ex.Id.Equal(oidExtensionAuthorityKeyId): // we always set this case ex.Id.Equal(oidExtensionSubjectAltName): // these are copied piecemeal below default: // copy the extension as-is template.ExtraExtensions = append(template.ExtraExtensions, ex) } } template.DNSNames = csr.DNSNames template.EmailAddresses = csr.EmailAddresses template.IPAddresses = csr.IPAddresses template.URIs = csr.URIs } if err := fillCertFields(template, csr.PublicKey, key.Public()); err != nil { return "", err } certDer, err := confirmAndCreate(template, cacert, csr.PublicKey, key) if err != nil { return "", err } return toPemString(certDer, "CERTIFICATE"), nil } // CrossSign takes a certificate as input and re-signs it using the given key. // Any command-line flags set will override the CSR contents. func CrossSign(certBytes []byte, rand io.Reader, key crypto.Signer, cacert *x509.Certificate) (string, error) { certBytes, err := parseMaybePEM(certBytes, "CERTIFICATE") if err != nil { return "", err } template, err := x509.ParseCertificate(certBytes) if err != nil { return "", fmt.Errorf("parsing certificate: %w", err) } if err := fillCertFields(template, template.PublicKey, key.Public()); err != nil { return "", err } newCert, err := confirmAndCreate(template, cacert, template.PublicKey, key) if err != nil { return "", err } return toPemString(newCert, "CERTIFICATE"), nil } func confirmAndCreate(template, parent *x509.Certificate, leafPub crypto.PublicKey, issuerPriv crypto.PrivateKey) ([]byte, error) { if ArgInteractive { origSigner, ok := issuerPriv.(crypto.Signer) if !ok { return nil, errors.New("private key must satisfy crypto.Signer") } ok, err := confirmCertificate(template, parent, leafPub, origSigner.Public()) if err != nil { return nil, fmt.Errorf("mocking cert for interactive confirmation: %w", err) } else if !ok { fmt.Fprintln(os.Stderr, "operation canceled") os.Exit(2) } } return x509.CreateCertificate(rand.Reader, template, parent, leafPub, issuerPriv) } func confirmCertificate(template, parent *x509.Certificate, leafPub, origSigner crypto.PublicKey) (bool, error) { // generate a key with the same parameters as the real signer fakePriv, err := generateAlike(origSigner) if err != nil { return false, err } // mangle parent cert fakeParent := new(x509.Certificate) *fakeParent = *parent fakeParent.PublicKey = fakePriv.Public() // call CreateCertificate with a fake signer to get what the final cert will look like der, err := x509.CreateCertificate(rand.Reader, template, fakeParent, leafPub, fakePriv) if err != nil { return false, err } cert, err := x509.ParseCertificate(der) if err != nil { return false, err } fmt.Fprintln(os.Stderr, "Signing certificate:") fmt.Fprintln(os.Stderr) FprintCertificate(os.Stderr, cert) fmt.Fprintln(os.Stderr) return promptYN("Sign this cert? [Y/n] "), nil } func generateAlike(pub crypto.PublicKey) (crypto.Signer, error) { // generate a dummy key of the same type as pub switch pub := pub.(type) { case *rsa.PublicKey: return rsa.GenerateKey(rand.Reader, 1024) case *ecdsa.PublicKey: return ecdsa.GenerateKey(pub.Curve, rand.Reader) case ed25519.PublicKey: _, priv, err := ed25519.GenerateKey(rand.Reader) return priv, err default: return nil, fmt.Errorf("unrecognized key type %T", pub) } } func promptYN(prompt string) bool { fmt.Fprint(os.Stderr, prompt) if !term.IsTerminal(0) { fmt.Fprintln(os.Stderr, "input is not a terminal, assuming true") return true } state, err := term.MakeRaw(0) if err == nil { defer fmt.Fprintln(os.Stderr) defer func() { _ = term.Restore(0, state) }() } var d [1]byte if _, err := os.Stdin.Read(d[:]); err != nil { return false } if d[0] == 'Y' || d[0] == 'y' { return true } return false } func parseMaybePEM(blob []byte, pemType string) ([]byte, error) { if bytes.Contains(blob, []byte("-----BEGIN")) { for { var block *pem.Block block, blob = pem.Decode(blob) if block == nil { break } else if block.Type == pemType { return block.Bytes, nil } } } else if len(blob) > 0 && blob[0] == 0x30 { return blob, nil } return nil, fmt.Errorf("expected a %s in PEM or DER format", strings.ToLower(pemType)) } relic-7.6.1/lib/xmldsig/000077500000000000000000000000001455105530300150415ustar00rootroot00000000000000relic-7.6.1/lib/xmldsig/canonicalize.go000066400000000000000000000107061455105530300200330ustar00rootroot00000000000000// // Copyright (c) SAS Institute Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package xmldsig import ( "sort" "github.com/beevik/etree" ) // Canonicalize a document starting from the given element and return the // serialized bytes. Implements something vaguely like xml-exc-c14n. Namespaces // declared in parent nodes are pulled in, and namespaces not used in the // element where they are declared are pushed further down to the elements that // use them. // // This is not a standards-conforming implementation. Use at your own peril. func SerializeCanonical(oldroot *etree.Element) ([]byte, error) { // make a deep copy before mangling things root := oldroot.Copy() // remake the document without any xml declarations etc. doc := etree.NewDocument() doc.SetRoot(root) doc.WriteSettings.CanonicalEndTags = true doc.WriteSettings.CanonicalText = true doc.WriteSettings.CanonicalAttrVal = true pullDown(oldroot, root) walkAttributes(root) return doc.WriteToBytes() } // if the attribute is a namespace declaration then return the namespace func getDecl(attr etree.Attr) (string, bool) { if attr.Space == "" && attr.Key == "xmlns" { return "", true } else if attr.Space == "xmlns" { return attr.Key, true } else { return "", false } } // attribute name for declaring the given namespace func putDecl(space string) string { if space == "" { return "xmlns" } return "xmlns:" + space } func walkAttributes(elem *etree.Element) { // remove unused spaces and push ones this element doesn't use down to child elements for i := 0; i < len(elem.Attr); { attr := elem.Attr[i] if space, isDecl := getDecl(attr); isDecl && !usesSpace(elem, space) { pushDown(elem, elem, space, putDecl(space), attr.Value) elem.Attr = append(elem.Attr[:i], elem.Attr[i+1:]...) continue } i++ } sort.Slice(elem.Attr, func(i, j int) bool { x := elem.Attr[i] y := elem.Attr[j] // default namespace node sorts first if x.Space == "" && x.Key == "xmlns" { return true } else if y.Space == "" && y.Key == "xmlns" { return false } // then all other namespace nodes if x.Space == "xmlns" && y.Space != "xmlns" { return true } else if y.Space == "xmlns" && x.Space != "xmlns" { return false } // then order by namespace and finally by key if x.Space != y.Space { return x.Space < y.Space } return x.Key < y.Key }) for i := 0; i < len(elem.Child); { token := elem.Child[i] switch t := token.(type) { case *etree.Element: walkAttributes(t) case *etree.CharData: // keep default: // remove elem.Child = append(elem.Child[:i], elem.Child[i+1:]...) continue } i++ } } // does this element or its attributes reference the given namespace? func usesSpace(elem *etree.Element, space string) bool { if elem.Space == space { return true } else if space == "" { // if the element doesn't use the default namespace, then neither do the attributes return false } for _, attr := range elem.Attr { if attr.Space == space { return true } } return false } // if the root element used to be the child of another element, pull down // namespaces that were declared in its ancestors func pullDown(oldroot, newroot *etree.Element) { spaces := make(map[string]string) for p := oldroot.Parent(); p != nil; p = p.Parent() { for _, attr := range p.Attr { space, isDecl := getDecl(attr) if !isDecl { continue } if spaces[space] != "" { continue } spaces[space] = attr.Value } } for space, value := range spaces { pushDown(nil, newroot, space, putDecl(space), value) } } // add a namespace to child elements that need it func pushDown(top, elem *etree.Element, space, key, value string) { if elem != top && elem.SelectAttr(key) != nil { // redeclared here already return } else if usesSpace(elem, space) { // used here, declare it here elem.CreateAttr(key, value) } else { // recurse further for _, elem := range elem.ChildElements() { pushDown(top, elem, space, key, value) } } } relic-7.6.1/lib/xmldsig/sign.go000066400000000000000000000207301455105530300163320ustar00rootroot00000000000000// // Copyright (c) SAS Institute Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // // Implements a useful subset of the xmldsig specification for creating // signatures over XML documents. package xmldsig import ( "crypto" "crypto/ecdsa" "crypto/rand" "crypto/rsa" "crypto/x509" "encoding/base64" "errors" "fmt" "math/big" "github.com/sassoftware/relic/v7/lib/x509tools" "github.com/beevik/etree" ) type SignOptions struct { // Use non-standard namespace for SHA-256 found in Microsoft ClickOnce manifests MsCompatHashNames bool // Use REC namespace for c14n method instead of the finalized one UseRecC14n bool // Add the X509 certificate chain to the KeyInfo IncludeX509 bool // Add a KeyValue element with the public key IncludeKeyValue bool } func (s SignOptions) c14nNamespace() string { if s.UseRecC14n { return AlgXMLExcC14nRec } else { return AlgXMLExcC14n } } // Create an enveloped signature from the document rooted at "root", replacing // any existing signature and adding it as a last child of "parent". func Sign(root, parent *etree.Element, hash crypto.Hash, privKey crypto.Signer, certs []*x509.Certificate, opts SignOptions) error { pubKey := privKey.Public() if len(certs) < 1 || !x509tools.SameKey(pubKey, certs[0].PublicKey) { return errors.New("xmldsig: first certificate must match private key") } RemoveElements(parent, "Signature") // canonicalize the enveloping document and digest it refDigest, err := hashCanon(root, hash) if err != nil { return err } hashAlg, sigAlg, err := hashAlgs(hash, pubKey, opts) if err != nil { return err } // build a signedinfo that references the enveloping document signature := parent.CreateElement("Signature") signature.CreateAttr("xmlns", NsXMLDsig) signedinfo := buildSignedInfo(signature, "", hashAlg, sigAlg, refDigest, opts) return finishSignature(signature, signedinfo, hash, privKey, certs, opts) } // Build an enveloping Signature document around the given Object element func SignEnveloping(object *etree.Element, hash crypto.Hash, privKey crypto.Signer, certs []*x509.Certificate, opts SignOptions) (*etree.Element, error) { pubKey := privKey.Public() if len(certs) < 1 || !x509tools.SameKey(pubKey, certs[0].PublicKey) { return nil, errors.New("xmldsig: first certificate must match private key") } // insert the object into the signature element before canonicalizing so // that the namespace gets pushed down properly signature := etree.NewElement("Signature") signature.CreateAttr("xmlns", NsXMLDsig) signature.AddChild(object) refDigest, err := hashCanon(object, hash) if err != nil { return nil, err } hashAlg, sigAlg, err := hashAlgs(hash, pubKey, opts) if err != nil { return nil, err } if object.Tag != "Object" { return nil, errors.New("object must have tag \"Object\"") } refId := object.SelectAttrValue("Id", "") if refId == "" { return nil, errors.New("object lacks an Id attribute") } // build a signedinfo that references the enveloping document signedinfo := buildSignedInfo(signature, refId, hashAlg, sigAlg, refDigest, opts) // canonicalize the signedinfo section and sign it if err := finishSignature(signature, signedinfo, hash, privKey, certs, opts); err != nil { return nil, err } signature.RemoveChild(object) signature.AddChild(object) return signature, nil } func buildSignedInfo(signature *etree.Element, refId, hashAlg, sigAlg string, refDigest []byte, opts SignOptions) *etree.Element { signedinfo := signature.CreateElement("SignedInfo") signedinfo.CreateElement("CanonicalizationMethod").CreateAttr("Algorithm", opts.c14nNamespace()) signedinfo.CreateElement("SignatureMethod").CreateAttr("Algorithm", sigAlg) reference := signedinfo.CreateElement("Reference") if refId == "" { reference.CreateAttr("URI", "") } else { reference.CreateAttr("URI", "#"+refId) reference.CreateAttr("Type", NsXMLDsig+"Object") } transforms := reference.CreateElement("Transforms") if refId == "" { transforms.CreateElement("Transform").CreateAttr("Algorithm", AlgDsigEnvelopedSignature) } transforms.CreateElement("Transform").CreateAttr("Algorithm", opts.c14nNamespace()) reference.CreateElement("DigestMethod").CreateAttr("Algorithm", hashAlg) reference.CreateElement("DigestValue").SetText(base64.StdEncoding.EncodeToString(refDigest)) return signedinfo } func finishSignature(signature, signedinfo *etree.Element, hash crypto.Hash, privKey crypto.Signer, certs []*x509.Certificate, opts SignOptions) error { siDigest, err := hashCanon(signedinfo, hash) if err != nil { return err } sig, err := privKey.Sign(rand.Reader, siDigest, hash) if err != nil { return err } // build the rest of the signature element if _, ok := privKey.Public().(*ecdsa.PublicKey); ok { // reformat the signature without ASN.1 structure esig, err := x509tools.UnmarshalEcdsaSignature(sig) if err != nil { return err } sig = esig.Pack() } signature.CreateElement("SignatureValue").SetText(base64.StdEncoding.EncodeToString(sig)) keyinfo := etree.NewElement("KeyInfo") if opts.IncludeKeyValue { if err := addKeyInfo(keyinfo, privKey.Public()); err != nil { return err } } if opts.IncludeX509 && len(certs) > 0 { addCerts(keyinfo, certs) } if len(keyinfo.Child) > 0 { signature.AddChild(keyinfo) } return nil } func hashCanon(root *etree.Element, hash crypto.Hash) ([]byte, error) { canon, err := SerializeCanonical(root) if err != nil { return nil, fmt.Errorf("xmldsig: %w", err) } d := hash.New() d.Write(canon) return d.Sum(nil), nil } // Remove all child elements with this tag from the element func RemoveElements(root *etree.Element, tag string) { for i := 0; i < len(root.Child); { token := root.Child[i] if elem, ok := token.(*etree.Element); ok && elem.Tag == tag { root.Child = append(root.Child[:i], root.Child[i+1:]...) } else { i++ } } } // Determine algorithm URIs for hashing and signing func hashAlgs(hash crypto.Hash, pubKey crypto.PublicKey, opts SignOptions) (string, string, error) { hashName := hashNames[hash] if hashName == "" { return "", "", errors.New("unsupported hash type") } var pubName string switch pubKey.(type) { case *rsa.PublicKey: pubName = "rsa" case *ecdsa.PublicKey: pubName = "ecdsa" default: return "", "", errors.New("unsupported key type") } var hashAlg, sigAlg string if opts.MsCompatHashNames { hashAlg = NsXMLDsig + hashName } else { hashAlg = HashUris[hash] } if pubName == "rsa" && (hashName == "sha1" || opts.MsCompatHashNames) { sigAlg = NsXMLDsig + pubName + "-" + hashName } else { sigAlg = NsXMLDsigMore + pubName + "-" + hashName } return hashAlg, sigAlg, nil } // Add public key and optional X509 certificate chain to KeyInfo func addKeyInfo(keyinfo *etree.Element, pubKey crypto.PublicKey) error { keyvalue := keyinfo.CreateElement("KeyValue") switch k := pubKey.(type) { case *rsa.PublicKey: e := big.NewInt(int64(k.E)) rkv := keyvalue.CreateElement("RSAKeyValue") rkv.CreateElement("Modulus").SetText(base64.StdEncoding.EncodeToString(k.N.Bytes())) rkv.CreateElement("Exponent").SetText(base64.StdEncoding.EncodeToString(e.Bytes())) case *ecdsa.PublicKey: curve, err := x509tools.CurveByCurve(k.Curve) if err != nil { return err } curveUrn := fmt.Sprintf("urn:oid:%s", curve.Oid) ekv := keyvalue.CreateElement("ECDSAKeyValue") ekv.CreateElement("DomainParameters").CreateElement("NamedCurve").CreateAttr("URN", curveUrn) pk := ekv.CreateElement("PublicKey") x := pk.CreateElement("X") x.CreateAttr("Value", k.X.String()) x.CreateAttr("xmlns:xsi", NsXsi) x.CreateAttr("xsi:type", "PrimeFieldElemType") y := pk.CreateElement("Y") y.CreateAttr("Value", k.Y.String()) y.CreateAttr("xmlns:xsi", NsXsi) y.CreateAttr("xsi:type", "PrimeFieldElemType") default: return errors.New("unsupported key type") } return nil } func addCerts(keyinfo *etree.Element, certs []*x509.Certificate) { x509data := keyinfo.CreateElement("X509Data") for _, cert := range certs { x509data.CreateElement("X509Certificate").SetText(base64.StdEncoding.EncodeToString(cert.Raw)) } } relic-7.6.1/lib/xmldsig/structs.go000066400000000000000000000056151455105530300171060ustar00rootroot00000000000000// // Copyright (c) SAS Institute Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package xmldsig import ( "crypto" "encoding/xml" ) const ( NsXMLDsig = "http://www.w3.org/2000/09/xmldsig#" NsXMLDsigMore = "http://www.w3.org/2001/04/xmldsig-more#" NsXMLEnc = "http://www.w3.org/2001/04/xmlenc#" NsXsi = "http://www.w3.org/2001/XMLSchema-instance" AlgXMLExcC14n = "http://www.w3.org/2001/10/xml-exc-c14n#" AlgXMLExcC14nRec = "http://www.w3.org/TR/2001/REC-xml-c14n-20010315" // draft version AlgDsigEnvelopedSignature = "http://www.w3.org/2000/09/xmldsig#enveloped-signature" ) // the best thing about namespaces is there are so many to choose from var nsPrefixes = []string{NsXMLDsig, NsXMLDsigMore, NsXMLEnc} var hashNames = map[crypto.Hash]string{ crypto.SHA1: "sha1", crypto.SHA224: "sha224", crypto.SHA256: "sha256", crypto.SHA384: "sha384", crypto.SHA512: "sha512", } var HashUris = map[crypto.Hash]string{ crypto.SHA1: NsXMLDsig + "sha1", crypto.SHA224: NsXMLDsigMore + "sha224", crypto.SHA256: NsXMLEnc + "sha256", crypto.SHA384: NsXMLDsigMore + "sha384", crypto.SHA512: NsXMLEnc + "sha512", } type signature struct { XMLName xml.Name `xml:"http://www.w3.org/2000/09/xmldsig# Signature"` CanonicalizationMethod method `xml:"SignedInfo>CanonicalizationMethod"` SignatureMethod method `xml:"SignedInfo>SignatureMethod"` Reference reference `xml:"SignedInfo>Reference"` SignatureValue string `xml:"SignatureValue"` KeyName string `xml:"KeyInfo>KeyName,omitempty"` KeyValue *keyValue `xml:"KeyInfo>KeyValue,omitempty"` X509Certificates []string `xml:"KeyInfo>X509Data>X509Certificate,omitempty"` } type reference struct { URI string `xml:",attr"` Transforms []method `xml:"Transforms>Transform"` DigestMethod method DigestValue string } type method struct { Algorithm string `xml:",attr"` } type keyValue struct { Modulus string `xml:"RSAKeyValue>Modulus,omitempty"` Exponent string `xml:"RSAKeyValue>Exponent,omitempty"` NamedCurve namedCurve `xml:"ECDSAKeyValue>DomainParameters>NamedCurve,omitempty"` X pointValue `xml:"ECDSAKeyValue>PublicKey>X,omitempty"` Y pointValue `xml:"ECDSAKeyValue>PublicKey>Y,omitempty"` } type namedCurve struct { URN string `xml:",attr"` } type pointValue struct { Value string `xml:",attr"` } relic-7.6.1/lib/xmldsig/verify.go000066400000000000000000000174261455105530300167060ustar00rootroot00000000000000// // Copyright (c) SAS Institute Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package xmldsig import ( "crypto" "crypto/ecdsa" "crypto/hmac" "crypto/rsa" "crypto/x509" "encoding/base64" "encoding/xml" "errors" "fmt" "math/big" "strings" "github.com/sassoftware/relic/v7/lib/x509tools" "github.com/sassoftware/relic/v7/signers/sigerrors" "github.com/beevik/etree" ) type Signature struct { PublicKey crypto.PublicKey Certificates []*x509.Certificate Hash crypto.Hash EncryptedDigest []byte Reference *etree.Element } func (s Signature) Leaf() *x509.Certificate { for _, cert := range s.Certificates { if x509tools.SameKey(cert.PublicKey, s.PublicKey) { return cert } } return nil } // Extract and verify an enveloped signature at the given root func Verify(root *etree.Element, sigpath string, extraCerts []*x509.Certificate) (*Signature, error) { root = root.Copy() sigs := root.FindElements(sigpath) if len(sigs) == 0 { return nil, sigerrors.NotSignedError{Type: "xmldsig"} } else if len(sigs) > 1 { return nil, errors.New("xmldsig: multiple signatures found") } sigEl := sigs[0] // parse signature tree sigbytes, err := SerializeCanonical(sigEl) if err != nil { return nil, fmt.Errorf("xmldsig: %w", err) } var sig signature if err := xml.Unmarshal(sigbytes, &sig); err != nil { return nil, fmt.Errorf("xmldsig: %w", err) } // parse algorithms if sig.CanonicalizationMethod.Algorithm != AlgXMLExcC14n && sig.CanonicalizationMethod.Algorithm != AlgXMLExcC14nRec { return nil, errors.New("xmldsig: unsupported canonicalization method") } hash, pubtype, err := parseAlgs(sig.Reference.DigestMethod.Algorithm, sig.SignatureMethod.Algorithm) if err != nil { return nil, err } // parse public key var pubkey crypto.PublicKey if sig.KeyValue != nil { pubkey, err = parseKey(sig.KeyValue, pubtype) if err != nil { return nil, err } } // parse x509 certs certs := make([]*x509.Certificate, len(extraCerts)) copy(certs, extraCerts) for _, b64 := range sig.X509Certificates { der, err := base64.StdEncoding.DecodeString(b64) if err != nil { return nil, fmt.Errorf("xmldsig: invalid X509 certificate") } cert, err := x509.ParseCertificate(der) if err != nil { return nil, fmt.Errorf("xmldsig: invalid X509 certificate: %w", err) } certs = append(certs, cert) } // check signature signedinfo := sigEl.SelectElement("SignedInfo") if signedinfo == nil { return nil, errors.New("xmldsig: invalid signature") } siCalc, err := hashCanon(signedinfo, hash) if err != nil { return nil, err } sigv, err := base64.StdEncoding.DecodeString(sig.SignatureValue) if err != nil { return nil, errors.New("xmldsig: invalid signature") } if pubtype == "ecdsa" { // reformat with ASN.1 structure sig, err := x509tools.UnpackEcdsaSignature(sigv) if err != nil { return nil, err } sigv = sig.Marshal() } if pubkey == nil { // if no KeyValue is present then use the X509 certificate if len(certs) == 0 { return nil, errors.New("xmldsig: missing public key") } // no guarantee is made about the order in which certs appear, so try all of them for _, cert := range certs { err = x509tools.Verify(cert.PublicKey, hash, siCalc, sigv) if err == nil { pubkey = cert.PublicKey break } } } else { err = x509tools.Verify(pubkey, hash, siCalc, sigv) } if err != nil { return nil, fmt.Errorf("xmldsig: %w", err) } // check reference digest var reference *etree.Element if sig.Reference.URI == "" { // enveloped signature if len(sig.Reference.Transforms) != 2 || sig.Reference.Transforms[0].Algorithm != AlgDsigEnvelopedSignature || (sig.Reference.Transforms[1].Algorithm != AlgXMLExcC14n && sig.Reference.Transforms[1].Algorithm != AlgXMLExcC14nRec) { return nil, errors.New("xmldsig: unsupported reference transform") } sigEl.Parent().RemoveChild(sigEl) reference = root } else { // enveloping signature if len(sig.Reference.Transforms) != 1 || (sig.Reference.Transforms[0].Algorithm != AlgXMLExcC14n && sig.Reference.Transforms[0].Algorithm != AlgXMLExcC14nRec) { return nil, errors.New("xmldsig: unsupported reference transform") } if sig.Reference.URI[0] != '#' { return nil, errors.New("xmldsig: unsupported reference URI") } reference = root.FindElement(fmt.Sprintf("[@Id='%s']", sig.Reference.URI[1:])) } if reference == nil { return nil, errors.New("xmldsig: unable to locate reference") } refCalc, err := hashCanon(reference, hash) if err != nil { return nil, err } refGiven, err := base64.StdEncoding.DecodeString(sig.Reference.DigestValue) if len(refGiven) != len(refCalc) || err != nil { return nil, errors.New("xmldsig: invalid signature") } if !hmac.Equal(refGiven, refCalc) { return nil, fmt.Errorf("xmldsig: digest mismatch: calculated %x, found %x", refCalc, refGiven) } return &Signature{ PublicKey: pubkey, Certificates: certs, Hash: hash, EncryptedDigest: sigv, Reference: reference, }, nil } func HashAlgorithm(hashAlg string) (string, crypto.Hash) { for _, prefix := range nsPrefixes { if strings.HasPrefix(hashAlg, prefix) { hashAlg = hashAlg[len(prefix):] break } } for hash, name := range hashNames { if hashAlg == name { return hashAlg, hash } } return hashAlg, 0 } func parseAlgs(hashAlg, sigAlg string) (crypto.Hash, string, error) { hashAlg, hash := HashAlgorithm(hashAlg) if !hash.Available() { return 0, "", errors.New("xmldsig: unsupported digest algorithm") } for _, prefix := range nsPrefixes { if strings.HasPrefix(sigAlg, prefix) { sigAlg = sigAlg[len(prefix):] break } } if !strings.HasSuffix(sigAlg, "-"+hashAlg) { return 0, "", errors.New("xmldsig: unsupported signature algorithm") } sigAlg = sigAlg[:len(sigAlg)-len(hashAlg)-1] if sigAlg != "rsa" && sigAlg != "ecdsa" { return 0, "", errors.New("xmldsig: unsupported signature algorithm") } return hash, sigAlg, nil } func parseKey(kv *keyValue, pubtype string) (crypto.PublicKey, error) { switch pubtype { case "rsa": nbytes, err := base64.StdEncoding.DecodeString(kv.Modulus) if len(nbytes) == 0 || err != nil { return nil, errors.New("xmldsig: invalid public key") } n := new(big.Int).SetBytes(nbytes) ebytes, err := base64.StdEncoding.DecodeString(kv.Exponent) if len(ebytes) == 0 || err != nil { return nil, errors.New("xmldsig: invalid public key") } ebig := new(big.Int).SetBytes(ebytes) if ebig.BitLen() > 30 { return nil, errors.New("xmldsig: invalid public key") } e := int(ebig.Int64()) return &rsa.PublicKey{N: n, E: e}, nil case "ecdsa": if !strings.HasPrefix(kv.NamedCurve.URN, "urn:oid:") { return nil, errors.New("xmldsig: unsupported ECDSA curve") } curve, err := x509tools.CurveByOidString(kv.NamedCurve.URN[8:]) if err != nil { return nil, fmt.Errorf("xmldsig: %w", err) } x, ok := new(big.Int).SetString(kv.X.Value, 10) if !ok { return nil, errors.New("xmldsig: invalid public key") } y, ok := new(big.Int).SetString(kv.Y.Value, 10) if !ok { return nil, errors.New("xmldsig: invalid public key") } if !curve.Curve.IsOnCurve(x, y) { return nil, errors.New("xmldsig: invalid public key") } return &ecdsa.PublicKey{Curve: curve.Curve, X: x, Y: y}, nil default: return nil, errors.New("xmldsig: unsupported signature algorithm") } } relic-7.6.1/lib/zipslicer/000077500000000000000000000000001455105530300153765ustar00rootroot00000000000000relic-7.6.1/lib/zipslicer/directory.go000066400000000000000000000260331455105530300177350ustar00rootroot00000000000000// // Copyright (c) SAS Institute Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package zipslicer import ( "bufio" "bytes" "encoding/binary" "errors" "fmt" "io" "io/ioutil" ) type Directory struct { File []*File Size int64 DirLoc int64 r io.ReaderAt end64 zip64End loc64 zip64Loc end zipEndRecord } // Return the offset of the zip central directory func FindDirectory(r io.ReaderAt, size int64) (int64, error) { pos := size - directoryEndLen - directory64LocLen var endb [directoryEndLen + directory64LocLen]byte if _, err := r.ReadAt(endb[:], pos); err != nil { return 0, err } re := bytes.NewReader(endb[:]) var loc64 zip64Loc var end zipEndRecord _ = binary.Read(re, binary.LittleEndian, &loc64) _ = binary.Read(re, binary.LittleEndian, &end) if end.Signature != directoryEndSignature { return 0, errors.New("zip central directory not found") } if end.TotalCDCount == uint16Max || end.CDSize == uint32Max || end.CDOffset == uint32Max { if loc64.Signature != directory64LocSignature { return 0, errors.New("expected ZIP64 locator") } // ZIP64 var end64b [directory64EndLen]byte if _, err := r.ReadAt(end64b[:], int64(loc64.Offset)); err != nil { return 0, err } var end64 zip64End _ = binary.Read(bytes.NewReader(end64b[:]), binary.LittleEndian, &end64) if end64.Signature != directory64EndSignature { return 0, errors.New("zip central directory not found") } return int64(end64.CDOffset), nil } return int64(end.CDOffset), nil } // Read a zip from a ReaderAt, with a separate copy of the central directory func ReadWithDirectory(r io.ReaderAt, size int64, cd []byte) (*Directory, error) { dirLoc := size - int64(len(cd)) files := make([]*File, 0) for { if binary.LittleEndian.Uint32(cd) != directoryHeaderSignature { break } var hdr zipCentralDir _ = binary.Read(bytes.NewReader(cd), binary.LittleEndian, &hdr) f := &File{ CreatorVersion: hdr.CreatorVersion, ReaderVersion: hdr.ReaderVersion, Flags: hdr.Flags, Method: hdr.Method, ModifiedTime: hdr.ModifiedTime, ModifiedDate: hdr.ModifiedDate, CRC32: hdr.CRC32, CompressedSize: uint64(hdr.CompressedSize), UncompressedSize: uint64(hdr.UncompressedSize), InternalAttrs: hdr.InternalAttrs, ExternalAttrs: hdr.ExternalAttrs, Offset: uint64(hdr.Offset), r: r, rs: size, } f.raw = make([]byte, directoryHeaderLen+int(hdr.FilenameLen)+int(hdr.ExtraLen)+int(hdr.CommentLen)) copy(f.raw, cd) cd = cd[directoryHeaderLen:] f.Name, cd = string(cd[:int(hdr.FilenameLen)]), cd[int(hdr.FilenameLen):] f.Extra, cd = cd[:int(hdr.ExtraLen)], cd[int(hdr.ExtraLen):] f.Comment, cd = cd[:int(hdr.CommentLen)], cd[int(hdr.CommentLen):] needUSize := f.UncompressedSize == uint32Max needCSize := f.CompressedSize == uint32Max needOffset := f.Offset == uint32Max extra := f.Extra for len(extra) >= 4 { tag := binary.LittleEndian.Uint16(extra[:2]) size := binary.LittleEndian.Uint16(extra[2:4]) if int(size) > len(extra)-4 { break } if tag == zip64ExtraID { e := extra[4 : 4+size] if needUSize && size >= 8 { f.UncompressedSize = binary.LittleEndian.Uint64(e) } if needCSize && size >= 16 { f.CompressedSize = binary.LittleEndian.Uint64(e[8:]) needCSize = false } if needOffset && size >= 24 { f.Offset = binary.LittleEndian.Uint64(e[16:]) needOffset = false } break } extra = extra[4+size:] } if needCSize || needOffset { return nil, errors.New("missing ZIP64 header") } files = append(files, f) } d := &Directory{ File: files, Size: size, DirLoc: dirLoc, r: r, } rd := bytes.NewReader(cd) switch binary.LittleEndian.Uint32(cd) { case directory64EndSignature: _ = binary.Read(rd, binary.LittleEndian, &d.end64) _ = binary.Read(rd, binary.LittleEndian, &d.loc64) case directoryEndSignature: default: return nil, errors.New("expected end record") } _ = binary.Read(rd, binary.LittleEndian, &d.end) return d, nil } // Read a zip from a ReaderAt func Read(r io.ReaderAt, size int64) (*Directory, error) { loc, err := FindDirectory(r, size) if err != nil { return nil, err } cd := make([]byte, size-loc) if _, err := r.ReadAt(cd, loc); err != nil { return nil, err } return ReadWithDirectory(r, size, cd) } // Read a zip from a stream, using a separate copy of the central directory. // Contents must be read in zip order or an error will be raised. func ReadStream(r io.Reader, size int64, cd []byte) (*Directory, error) { ra := &streamReaderAt{r: r} return ReadWithDirectory(ra, size, cd) } // Serialize a zip file with all of the files up to, but not including, the // given index. The contents and central directory are written to separate // writers, which may be the same writer. func (d *Directory) Truncate(n int, body, dir io.Writer) error { if body != nil { for i := 0; i < n; i++ { f := d.File[i] fs, err := f.GetTotalSize() if err != nil { return err } if _, err := io.Copy(body, io.NewSectionReader(d.r, int64(f.Offset), fs)); err != nil { return err } } } cdOffset := d.File[n].Offset var size uint64 for i := 0; i < n; i++ { blob, err := d.File[i].GetDirectoryHeader() if err != nil { return err } if _, err := dir.Write(blob); err != nil { return err } size += uint64(len(blob)) } end := d.end if d.end64.Signature != 0 { end64 := d.end64 end64.DiskCDCount = uint64(n) end64.TotalCDCount = uint64(n) end64.CDSize = size end64.CDOffset = cdOffset if err := binary.Write(dir, binary.LittleEndian, end64); err != nil { return err } loc := d.loc64 loc.Offset = cdOffset + size if err := binary.Write(dir, binary.LittleEndian, loc); err != nil { return err } } else { if cdOffset >= uint32Max || n >= uint16Max { return errors.New("file too big for 32-bit ZIP") } end.DiskCDCount = uint16(n) end.TotalCDCount = uint16(n) end.CDSize = uint32(size) end.CDOffset = uint32(cdOffset) } return binary.Write(dir, binary.LittleEndian, end) } // Get the original central directory and end-of-directory from a previously-read file. // // If trim is true, then the end-of-directory will be updated to skip over any // non-ZIP data between the last file's contents and the first central // directory entry. func (d *Directory) GetOriginalDirectory(trim bool) (cdEntries, endOfDir []byte, err error) { if d.end.Signature == 0 { return nil, nil, errors.New("new zipfile, can't produce original directory") } var wcd, weod bytes.Buffer if err := d.WriteDirectory(&wcd, nil, false); err != nil { return nil, nil, err } end64 := d.end64 loc64 := d.loc64 end := d.end if trim { contentEnd, err := d.NextFileOffset() if err != nil { return nil, nil, err } delta := d.DirLoc - contentEnd if delta < 0 || delta > uint32Max { return nil, nil, errors.New("non-ZIP data out of bounds") } if end64.Signature != 0 { end64.CDOffset -= uint64(delta) } if loc64.Signature != 0 { loc64.Offset -= uint64(delta) } if end.CDOffset != uint32Max || loc64.Signature == 0 { end.CDOffset -= uint32(delta) } } _ = binary.Write(&weod, binary.LittleEndian, end64) _ = binary.Write(&weod, binary.LittleEndian, loc64) _ = binary.Write(&weod, binary.LittleEndian, end) return wcd.Bytes(), weod.Bytes(), nil } // Serialize a zip central directory to file. The file entries will be written // to wcd, and the end-of-directory markers will be written to weod. // // If forceZip64 is true then a ZIP64 end-of-directory marker will always be // written; otherwise it is only done if ZIP64 features are required. func (d *Directory) WriteDirectory(wcd, weod io.Writer, forceZip64 bool) error { buf := bufio.NewWriter(wcd) cdoff := d.DirLoc var count, size uint64 minVersion := uint16(zip20) for _, f := range d.File { if f.ReaderVersion > minVersion { minVersion = f.ReaderVersion } blob, err := f.GetDirectoryHeader() if err != nil { return err } if _, err := buf.Write(blob); err != nil { return err } count++ size += uint64(len(blob)) } if wcd != weod { if err := buf.Flush(); err != nil { return err } buf.Reset(weod) } else if weod == nil { return nil } var end zipEndRecord if count >= uint16Max || size >= uint32Max || cdoff >= uint32Max || forceZip64 { minVersion = zip45 } if minVersion == zip45 { end64off := cdoff + int64(size) end64 := zip64End{ Signature: directory64EndSignature, RecordSize: directory64EndLen - 12, CreatorVersion: zip45, ReaderVersion: minVersion, DiskCDCount: count, TotalCDCount: count, CDSize: size, CDOffset: uint64(cdoff), } if err := binary.Write(buf, binary.LittleEndian, end64); err != nil { return err } loc64 := zip64Loc{ Signature: directory64LocSignature, Offset: uint64(end64off), DiskCount: 1, } if err := binary.Write(buf, binary.LittleEndian, loc64); err != nil { return err } end = zipEndRecord{ Signature: directoryEndSignature, DiskCDCount: uint16Max, TotalCDCount: uint16Max, CDSize: uint32Max, CDOffset: uint32Max, } } else { end = zipEndRecord{ Signature: directoryEndSignature, DiskCDCount: uint16(count), TotalCDCount: uint16(count), CDSize: uint32(size), CDOffset: uint32(cdoff), } } if err := binary.Write(buf, binary.LittleEndian, end); err != nil { return err } return buf.Flush() } type streamReaderAt struct { r io.Reader pos int64 } func (r *streamReaderAt) ReadAt(d []byte, p int64) (int, error) { if p > r.pos { if _, err := io.CopyN(ioutil.Discard, r.r, p-r.pos); err != nil { return 0, err } r.pos = p } else if p < r.pos { return 0, fmt.Errorf("attempted to seek backwards: at %d, to %d", r.pos, p) } n, err := io.ReadFull(r.r, d) r.pos += int64(n) return n, err } // Add a file to the central directory. Its contents are assumed to be already // located after the last added file. func (d *Directory) AddFile(f *File) (*File, error) { size, err := f.GetTotalSize() if err != nil { return nil, err } offset := uint64(d.DirLoc) if f.Offset != offset { f.raw = nil } f.Offset = offset d.DirLoc += size d.File = append(d.File, f) return f, nil } // Get the offset immediately following the last file's contents. This is the // same as DirLoc unless there is non-zip data in between. func (d *Directory) NextFileOffset() (int64, error) { if len(d.File) == 0 { return 0, nil } lastFile := d.File[len(d.File)-1] size, err := lastFile.GetTotalSize() if err != nil { return 0, err } return int64(lastFile.Offset) + size, nil } relic-7.6.1/lib/zipslicer/file.go000066400000000000000000000251041455105530300166460ustar00rootroot00000000000000// // Copyright (c) SAS Institute Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package zipslicer import ( "archive/zip" "bufio" "bytes" "compress/flate" "crypto" "encoding/binary" "errors" "hash" "hash/crc32" "io" "io/ioutil" "time" ) type File struct { CreatorVersion uint16 ReaderVersion uint16 Flags uint16 Method uint16 ModifiedTime uint16 ModifiedDate uint16 CRC32 uint32 CompressedSize uint64 UncompressedSize uint64 Name string Extra []byte Comment []byte InternalAttrs uint16 ExternalAttrs uint32 Offset uint64 r io.ReaderAt rs int64 raw []byte lfh zipLocalHeader lfhName, lfhExtra []byte ddb []byte compd []byte } type Reader struct { f *File rc io.ReadCloser crc hash.Hash32 err error nread uint64 } func (f *File) readLocalHeader() error { if f.lfh.Signature != 0 { // already done return nil } sr := io.NewSectionReader(f.r, int64(f.Offset), f.rs) var lfhb [fileHeaderLen]byte if _, err := io.ReadFull(sr, lfhb[:]); err != nil { return err } _ = binary.Read(bytes.NewReader(lfhb[:]), binary.LittleEndian, &f.lfh) if f.lfh.Signature != fileHeaderSignature { return errors.New("local file header not found") } f.lfhName = make([]byte, f.lfh.FilenameLen) if _, err := io.ReadFull(sr, f.lfhName); err != nil { return err } f.lfhExtra = make([]byte, f.lfh.ExtraLen) if _, err := io.ReadFull(sr, f.lfhExtra); err != nil { return err } return nil } func (f *File) readDataDesc() error { if err := f.readLocalHeader(); err != nil { return err } if f.lfh.Flags&0x8 == 0 { // no descriptor return nil } if len(f.ddb) != 0 { // already done return nil } lfhSize := fileHeaderLen + len(f.lfhName) + len(f.lfhExtra) pos := int64(f.Offset) + int64(lfhSize) + int64(f.CompressedSize) f.ddb = make([]byte, dataDescriptor64Len) if _, err := f.r.ReadAt(f.ddb[:dataDescriptorLen], pos); err != nil { return err } // Read the 32-bit len so we don't overshoot. The underlying stream might // not be seekable. var desc zipDataDesc _ = binary.Read(bytes.NewReader(f.ddb[:dataDescriptorLen]), binary.LittleEndian, &desc) if desc.Signature != dataDescriptorSignature { return errors.New("data descriptor signature is missing") } if f.UncompressedSize >= uint32Max || desc.UncompressedSize != uint32(f.UncompressedSize) || desc.CompressedSize != uint32(f.CompressedSize) { // 64-bit if _, err := f.r.ReadAt(f.ddb[dataDescriptorLen:], pos+dataDescriptorLen); err != nil { return err } var desc64 zipDataDesc64 _ = binary.Read(bytes.NewReader(f.ddb), binary.LittleEndian, &desc64) if desc64.CompressedSize != f.CompressedSize || desc64.UncompressedSize != f.UncompressedSize { return errors.New("data descriptor is invalid") } f.CRC32 = desc64.CRC32 } else { // 32-bit f.ddb = f.ddb[:dataDescriptorLen] f.CRC32 = desc.CRC32 } return nil } func (f *File) GetDirectoryHeader() ([]byte, error) { if len(f.raw) > 0 { return f.raw, nil } hdr := zipCentralDir{ Signature: directoryHeaderSignature, CreatorVersion: f.CreatorVersion, ReaderVersion: f.ReaderVersion, Flags: f.Flags, Method: f.Method, ModifiedTime: f.ModifiedTime, ModifiedDate: f.ModifiedDate, CRC32: f.CRC32, CompressedSize: uint32(f.CompressedSize), UncompressedSize: uint32(f.UncompressedSize), InternalAttrs: f.InternalAttrs, ExternalAttrs: f.ExternalAttrs, Offset: uint32(f.Offset), FilenameLen: uint16(len(f.Name)), ExtraLen: uint16(len(f.Extra)), CommentLen: uint16(len(f.Comment)), } if f.CompressedSize >= uint32Max || f.UncompressedSize >= uint32Max || f.Offset >= uint32Max { hdr.CompressedSize = uint32Max hdr.UncompressedSize = uint32Max hdr.Offset = uint32Max extra := zip64Extra{ Signature: zip64ExtraID, RecordSize: zip64ExtraLen, UncompressedSize: f.UncompressedSize, CompressedSize: f.CompressedSize, Offset: f.Offset, } b := bytes.NewBuffer(make([]byte, 0, zip64ExtraLen+4+len(f.Extra))) _ = binary.Write(b, binary.LittleEndian, extra) b.Write(f.Extra) f.Extra = b.Bytes() hdr.ExtraLen = uint16(b.Len()) hdr.ReaderVersion = zip45 } b := bytes.NewBuffer(make([]byte, 0, directoryHeaderLen+len(f.Name)+len(f.Extra)+len(f.Comment))) _ = binary.Write(b, binary.LittleEndian, hdr) b.WriteString(f.Name) b.Write(f.Extra) b.Write(f.Comment) return b.Bytes(), nil } func (f *File) GetLocalHeader() ([]byte, error) { if err := f.readLocalHeader(); err != nil { return nil, err } b := bytes.NewBuffer(make([]byte, 0, fileHeaderLen+len(f.lfhName)+len(f.lfhExtra))) _ = binary.Write(b, binary.LittleEndian, f.lfh) b.Write(f.lfhName) b.Write(f.lfhExtra) return b.Bytes(), nil } func (f *File) GetDataDescriptor() ([]byte, error) { if err := f.readDataDesc(); err != nil { return nil, err } return f.ddb, nil } func (f *File) GetTotalSize() (int64, error) { if err := f.readDataDesc(); err != nil { return 0, err } return fileHeaderLen + int64(len(f.lfhName)+len(f.lfhExtra)+len(f.ddb)) + int64(f.CompressedSize), nil } func (d *Directory) NewFile(name string, extra, contents []byte, w io.Writer, mtime time.Time, deflate, useDesc bool) (*File, error) { var zh zip.FileHeader // need the side effect of this conversion zh.SetModTime(mtime) //nolint:staticcheck var fb bytes.Buffer method := zip.Deflate if deflate { c, err := flate.NewWriter(&fb, 9) if err != nil { return nil, err } if _, err := c.Write(contents); err != nil { return nil, err } if err := c.Close(); err != nil { return nil, err } } else { method = zip.Store fb.Write(contents) } crc := crc32.NewIEEE() crc.Write(contents) sum := crc.Sum32() buf := bufio.NewWriter(w) f := &File{ CreatorVersion: zip45, ReaderVersion: zip20, Method: method, ModifiedTime: zh.ModifiedTime, ModifiedDate: zh.ModifiedDate, CRC32: sum, CompressedSize: uint64(fb.Len()), UncompressedSize: uint64(len(contents)), Name: name, Extra: extra, lfhName: []byte(name), lfhExtra: extra, compd: fb.Bytes(), } if useDesc { f.Flags = 0x8 f.ReaderVersion = zip45 } f.lfh = zipLocalHeader{ Signature: fileHeaderSignature, ReaderVersion: f.ReaderVersion, Flags: f.Flags, Method: f.Method, ModifiedTime: f.ModifiedTime, ModifiedDate: f.ModifiedDate, FilenameLen: uint16(len(name)), ExtraLen: uint16(len(extra)), } if !useDesc { f.lfh.CRC32 = f.CRC32 f.lfh.CompressedSize = uint32(f.CompressedSize) f.lfh.UncompressedSize = uint32(f.UncompressedSize) } if err := binary.Write(buf, binary.LittleEndian, f.lfh); err != nil { return nil, err } if _, err := buf.WriteString(name); err != nil { return nil, err } if _, err := buf.Write(extra); err != nil { return nil, err } if _, err := buf.Write(fb.Bytes()); err != nil { return nil, err } if useDesc { desc := zipDataDesc64{ Signature: dataDescriptorSignature, CRC32: sum, CompressedSize: uint64(fb.Len()), UncompressedSize: uint64(len(contents)), } ddb := bytes.NewBuffer(make([]byte, 0, dataDescriptor64Len)) _ = binary.Write(ddb, binary.LittleEndian, desc) f.ddb = ddb.Bytes() if _, err := buf.Write(f.ddb); err != nil { return nil, err } } if err := buf.Flush(); err != nil { return nil, err } return d.AddFile(f) } func (f *File) ModTime() time.Time { fh := zip.FileHeader{ModifiedDate: f.ModifiedDate, ModifiedTime: f.ModifiedTime} // need the side effect of this conversion return fh.ModTime() //nolint:staticcheck } func (f *File) Open() (io.ReadCloser, error) { return f.OpenAndTeeRaw(nil) } // Open zip file for reading, but also write raw zip data to 'sink'. func (f *File) OpenAndTeeRaw(sink io.Writer) (*Reader, error) { if err := f.readLocalHeader(); err != nil { return nil, err } var r io.Reader if f.compd != nil { r = bytes.NewReader(f.compd) } else { pos := int64(f.Offset) + fileHeaderLen + int64(f.lfh.FilenameLen) + int64(f.lfh.ExtraLen) r = io.NewSectionReader(f.r, pos, int64(f.CompressedSize)) } if sink != nil { r = io.TeeReader(r, sink) } crc := crc32.NewIEEE() var rc io.ReadCloser switch f.Method { case zip.Store: rc = ioutil.NopCloser(r) case zip.Deflate: rc = flate.NewReader(r) default: return nil, errors.New("unsupported zip compression") } return &Reader{f: f, rc: rc, crc: crc}, nil } func (r *Reader) Read(d []byte) (int, error) { if r.err != nil { return 0, r.err } n, err := r.rc.Read(d) r.crc.Write(d[:n]) r.nread += uint64(n) if err == nil { return n, nil } if err != io.EOF { r.err = err return n, err } if r.nread != r.f.UncompressedSize { return 0, io.ErrUnexpectedEOF } if r.f.lfh.Flags&0x8 != 0 { if err2 := r.f.readDataDesc(); err2 != nil { if err2 == io.EOF { err = io.ErrUnexpectedEOF } else { err = err2 } } } if r.f.CRC32 != 0 && r.crc.Sum32() != r.f.CRC32 { err = zip.ErrChecksum } r.err = err return n, err } func (r *Reader) Close() error { return r.rc.Close() } func (f *File) Digest(hash crypto.Hash) ([]byte, error) { fc, err := f.Open() if err != nil { return nil, err } d := hash.New() if _, err := io.Copy(d, fc); err != nil { return nil, err } return d.Sum(nil), fc.Close() } // Dump the local header and contents of this file to a writer func (f *File) Dump(w io.Writer) (int64, error) { lfh, err := f.GetLocalHeader() if err != nil { return 0, err } if _, err := w.Write(lfh); err != nil { return 0, err } if f.compd != nil { if _, err := w.Write(f.compd); err != nil { return 0, err } } else { pos := int64(f.Offset) + fileHeaderLen + int64(f.lfh.FilenameLen) + int64(f.lfh.ExtraLen) r := io.NewSectionReader(f.r, pos, int64(f.CompressedSize)) if _, err := io.Copy(w, r); err != nil { return 0, err } } ddb, err := f.GetDataDescriptor() if err != nil { return 0, err } if _, err := w.Write(ddb); err != nil { return 0, err } return f.GetTotalSize() } relic-7.6.1/lib/zipslicer/mangle.go000066400000000000000000000045511455105530300171750ustar00rootroot00000000000000// // Copyright (c) SAS Institute Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package zipslicer import ( "bytes" "time" "github.com/sassoftware/relic/v7/lib/binpatch" ) type Mangler struct { outz *Directory patch *binpatch.PatchSet newcontents bytes.Buffer indir, insize int64 } type MangleFunc func(*MangleFile) error // Walk all the files in the directory in-order, invoking a callback that can // decide whether to keep or discard each one. Returns a Mangler that can be // used to add more files and eventually produce a binary patch against the // original zip. func (d *Directory) Mangle(callback MangleFunc) (*Mangler, error) { m := &Mangler{ outz: new(Directory), patch: binpatch.New(), indir: d.DirLoc, insize: d.Size, } for _, f := range d.File { mf := &MangleFile{File: *f, m: m} if err := callback(mf); err != nil { return nil, err } if mf.deleted { size, err := mf.GetTotalSize() if err != nil { return nil, err } m.patch.Add(int64(mf.Offset), size, nil) } else { if _, err := m.outz.AddFile(&mf.File); err != nil { return nil, err } } } return m, nil } // Add a new file to a zip mangler func (m *Mangler) NewFile(name string, contents []byte) error { deflate := len(contents) != 0 _, err := m.outz.NewFile(name, nil, contents, &m.newcontents, time.Now(), deflate, true) return err } // Create a binary patchset out of the operations performed in this mangler func (m *Mangler) MakePatch(forceZip64 bool) (*binpatch.PatchSet, error) { w := &m.newcontents if err := m.outz.WriteDirectory(w, w, forceZip64); err != nil { return nil, err } m.patch.Add(m.indir, m.insize-m.indir, m.newcontents.Bytes()) return m.patch, nil } type MangleFile struct { File m *Mangler deleted bool } // Mark this file for deletion func (f *MangleFile) Delete() { f.deleted = true } relic-7.6.1/lib/zipslicer/structs.go000066400000000000000000000056101455105530300174360ustar00rootroot00000000000000// // Copyright (c) SAS Institute Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package zipslicer const ( fileHeaderSignature = 0x04034b50 directoryHeaderSignature = 0x02014b50 directoryEndSignature = 0x06054b50 directory64LocSignature = 0x07064b50 directory64EndSignature = 0x06064b50 dataDescriptorSignature = 0x08074b50 fileHeaderLen = 30 directoryHeaderLen = 46 directoryEndLen = 22 directory64LocLen = 20 directory64EndLen = 56 dataDescriptorLen = 16 dataDescriptor64Len = 24 zip64ExtraID = 0x0001 zip64ExtraLen = 24 zip20 = 20 zip45 = 45 uint32Max = 0xffffffff uint16Max = 0xffff ) type zipCentralDir struct { Signature uint32 CreatorVersion uint16 ReaderVersion uint16 Flags uint16 Method uint16 ModifiedTime uint16 ModifiedDate uint16 CRC32 uint32 CompressedSize uint32 UncompressedSize uint32 FilenameLen uint16 ExtraLen uint16 CommentLen uint16 StartDisk uint16 InternalAttrs uint16 ExternalAttrs uint32 Offset uint32 } type zip64End struct { Signature uint32 RecordSize uint64 CreatorVersion uint16 ReaderVersion uint16 Disk uint32 FirstDisk uint32 DiskCDCount uint64 TotalCDCount uint64 CDSize uint64 CDOffset uint64 } type zip64Loc struct { Signature uint32 Disk uint32 Offset uint64 DiskCount uint32 } type zipEndRecord struct { Signature uint32 DiskNumber uint16 DiskCD uint16 DiskCDCount uint16 TotalCDCount uint16 CDSize uint32 CDOffset uint32 CommentLength uint16 } type zipLocalHeader struct { Signature uint32 ReaderVersion uint16 Flags uint16 Method uint16 ModifiedTime uint16 ModifiedDate uint16 CRC32 uint32 CompressedSize uint32 UncompressedSize uint32 FilenameLen uint16 ExtraLen uint16 } type zip64Extra struct { Signature uint16 RecordSize uint16 UncompressedSize uint64 CompressedSize uint64 Offset uint64 } type zipDataDesc struct { Signature uint32 CRC32 uint32 CompressedSize uint32 UncompressedSize uint32 } type zipDataDesc64 struct { Signature uint32 CRC32 uint32 CompressedSize uint64 UncompressedSize uint64 } relic-7.6.1/lib/zipslicer/tarzip.go000066400000000000000000000054341455105530300172440ustar00rootroot00000000000000// // Copyright (c) SAS Institute Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package zipslicer import ( "archive/tar" "errors" "fmt" "io" "io/ioutil" "os" ) const ( TarMemberCD = "zipdir.bin" TarMemberZip = "contents.zip" ) // Make a tar archive with two members: // - the central directory of the zip file // - the complete zip file // This lets us process the zip in one pass, which normally isn't possible with // the directory at the end. func ZipToTar(r *os.File, w io.Writer) error { size, err := r.Seek(0, io.SeekEnd) if err != nil { return err } dirLoc, err := FindDirectory(r, size) if err != nil { return err } tw := tar.NewWriter(w) if _, err := r.Seek(dirLoc, 0); err != nil { return err } if err := tarAddStream(tw, r, TarMemberCD, size-dirLoc); err != nil { return err } if _, err := r.Seek(0, 0); err != nil { return err } if err := tarAddStream(tw, r, TarMemberZip, size); err != nil { return err } return tw.Close() } func tarAddStream(tw *tar.Writer, r io.Reader, name string, size int64) error { hdr := &tar.Header{Name: name, Mode: 0644, Size: size} if err := tw.WriteHeader(hdr); err != nil { return err } if _, err := io.CopyN(tw, r, size); err != nil { return err } return nil } // Read a tar stream produced by ZipToTar and return the zip directory. Files // must be read from the zip in order or an error will be raised. func ReadZipTar(r io.Reader) (*Directory, error) { tr := tar.NewReader(r) hdr, err := tr.Next() if err != nil { return nil, fmt.Errorf("error reading tar: %w", err) } else if hdr.Name != TarMemberCD { return nil, errors.New("invalid tarzip") } zipdir, err := ioutil.ReadAll(tr) if err != nil { return nil, fmt.Errorf("error reading tar: %w", err) } hdr, err = tr.Next() if err != nil { return nil, err } else if hdr.Name != TarMemberZip { return nil, errors.New("invalid tarzip") } zr := &zipTarReader{tr: tr} return ReadStream(zr, hdr.Size, zipdir) } type zipTarReader struct { tr *tar.Reader } func (z *zipTarReader) Read(d []byte) (int, error) { if z.tr == nil { return 0, io.EOF } n, err := z.tr.Read(d) if err == io.EOF { _, err2 := z.tr.Next() if err2 == nil { err = errors.New("invalid tarzip") } else if err2 != io.EOF { err = err2 } z.tr = nil } return n, err } relic-7.6.1/main_client.go000066400000000000000000000045151455105530300154420ustar00rootroot00000000000000// // Copyright (c) SAS Institute Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package main // Commands and signers for the basic client only (pure Go, all OSes) import ( "runtime/debug" "strings" "github.com/sassoftware/relic/v7/cmdline/shared" "github.com/sassoftware/relic/v7/config" _ "github.com/sassoftware/relic/v7/cmdline/remotecmd" _ "github.com/sassoftware/relic/v7/cmdline/verify" _ "github.com/sassoftware/relic/v7/signers/apk" _ "github.com/sassoftware/relic/v7/signers/appmanifest" _ "github.com/sassoftware/relic/v7/signers/appx" _ "github.com/sassoftware/relic/v7/signers/cab" _ "github.com/sassoftware/relic/v7/signers/cat" _ "github.com/sassoftware/relic/v7/signers/cosign" _ "github.com/sassoftware/relic/v7/signers/deb" _ "github.com/sassoftware/relic/v7/signers/dmg" _ "github.com/sassoftware/relic/v7/signers/jar" _ "github.com/sassoftware/relic/v7/signers/macho" _ "github.com/sassoftware/relic/v7/signers/msi" _ "github.com/sassoftware/relic/v7/signers/pecoff" _ "github.com/sassoftware/relic/v7/signers/pgp" _ "github.com/sassoftware/relic/v7/signers/pkcs" _ "github.com/sassoftware/relic/v7/signers/ps" _ "github.com/sassoftware/relic/v7/signers/rpm" _ "github.com/sassoftware/relic/v7/signers/vsix" _ "github.com/sassoftware/relic/v7/signers/xap" _ "github.com/sassoftware/relic/v7/signers/xar" ) var ( version = "unknown" // set this at link time commit = "unknown" // set this at link time ) func main() { if version != "unknown" { // normal CI compilation path config.Version = version config.Commit = commit } else if bi, ok := debug.ReadBuildInfo(); ok { // built from go module with `go install` if strings.HasPrefix(bi.Main.Version, "v") { config.Version = bi.Main.Version config.Commit = bi.Main.Sum } } config.UserAgent = "relic/" + config.Version shared.Main() } relic-7.6.1/main_server.go000066400000000000000000000015761455105530300154760ustar00rootroot00000000000000//go:build !clientonly && !windows // +build !clientonly,!windows // // Copyright (c) SAS Institute Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package main // Server and server-adjacent commands import ( _ "github.com/sassoftware/relic/v7/cmdline/auditor" _ "github.com/sassoftware/relic/v7/cmdline/servecmd" _ "github.com/sassoftware/relic/v7/cmdline/workercmd" ) relic-7.6.1/main_token.go000066400000000000000000000013621455105530300153010ustar00rootroot00000000000000//go:build !clientonly // +build !clientonly // // Copyright (c) SAS Institute Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package main // local token commands (no server) import _ "github.com/sassoftware/relic/v7/cmdline/token" relic-7.6.1/scripts/000077500000000000000000000000001455105530300143135ustar00rootroot00000000000000relic-7.6.1/scripts/Dockerfile.clientbuild000066400000000000000000000015241455105530300206040ustar00rootroot00000000000000ARG goversion FROM golang:${goversion} WORKDIR /src ENV GO111MODULE=on CGO_ENABLED=0 ARG GOPROXY COPY go.mod go.sum ./ RUN go mod download COPY . . RUN mkdir /out ARG ldflags RUN GOOS=linux GOARCH=amd64 go build -ldflags "$ldflags" -tags clientonly -o /out/relic-client-linux-amd64 RUN GOOS=linux GOARCH=arm64 go build -ldflags "$ldflags" -tags clientonly -o /out/relic-client-linux-arm64 RUN GOOS=linux GOARCH=ppc64le go build -ldflags "$ldflags" -tags clientonly -o /out/relic-client-linux-ppc64le RUN GOOS=darwin GOARCH=amd64 go build -ldflags "$ldflags" -tags clientonly -o /out/relic-client-darwin-amd64 RUN GOOS=darwin GOARCH=arm64 go build -ldflags "$ldflags" -tags clientonly -o /out/relic-client-darwin-arm64 RUN GOOS=windows GOARCH=amd64 go build -ldflags "$ldflags" -tags clientonly -o /out/relic-client-windows-amd64.exe relic-7.6.1/scripts/Dockerfile.fullbuild000066400000000000000000000011271455105530300202670ustar00rootroot00000000000000FROM fedora:30 WORKDIR /scratch RUN dnf install -y wget git gcc mingw64-gcc ARG goversion RUN wget -q https://dl.google.com/go/go${goversion}.linux-amd64.tar.gz && tar -xf go*.tar.gz -C /opt ENV PATH=/opt/go/bin:/usr/bin:/usr/sbin WORKDIR /src ENV GO111MODULE=on CGO_ENABLED=1 ARG GOPROXY COPY go.mod go.sum ./ RUN go mod download COPY . . RUN mkdir /out ARG ldflags RUN GOOS=linux GOARCH=amd64 CC=x86_64-redhat-linux-gcc go build -ldflags "$ldflags" -o /out/relic-linux-amd64 RUN GOOS=windows GOARCH=amd64 CC=x86_64-w64-mingw32-gcc go build -ldflags "$ldflags" -o /out/relic-windows-amd64.exe relic-7.6.1/scripts/build-all.sh000077500000000000000000000030131455105530300165140ustar00rootroot00000000000000#!/bin/bash # # Copyright (c) SAS Institute Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # set -ex -o pipefail version=$(./scripts/version.sh) commit=$(git rev-parse HEAD) ldflags="-s -w -X main.version=$version -X main.commit=$commit" goversion=1.20 rm -rf build mkdir build ## non-cgo build of client docker rmi relic-build 2>/dev/null ||: docker build \ -f scripts/Dockerfile.clientbuild \ --pull \ --build-arg ldflags="$ldflags" \ --build-arg GOPROXY=$GOPROXY \ --build-arg goversion=$goversion \ -t relic-build . container=$(docker create relic-build) docker cp $container:out build/ docker rm $container docker rmi relic-build ## cgo build of full program docker build \ -f scripts/Dockerfile.fullbuild \ --pull \ --build-arg ldflags="$ldflags" \ --build-arg GOPROXY=$GOPROXY \ --build-arg goversion=$goversion \ -t relic-build . container=$(docker create relic-build) docker cp $container:out build/ docker rm $container docker rmi relic-build mv build/out/* build/ rmdir build/out relic-7.6.1/scripts/ci-set-ldflags.sh000066400000000000000000000005441455105530300174500ustar00rootroot00000000000000# If this is a release build, set version to the tag. Otherwise set it to the commit hash. version="${GITHUB_REF#refs/tags/v*}" [ "$version" == "$GITHUB_REF" ] && version=$GITHUB_SHA ldflags="-s -w -X main.version=$version -X main.commit=$GITHUB_SHA" # Propagate these flags to all further steps in the current job echo "ldflags=$ldflags" >> $GITHUB_ENV relic-7.6.1/scripts/rpmbuild.sh000077500000000000000000000020421455105530300164660ustar00rootroot00000000000000#!/bin/bash # # Copyright (c) SAS Institute Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. set -ex version=$(./scripts/version.sh) [ -e build/relic-linux-amd64 ] || ./scripts/build-all.sh cd build rm -f *.rpm tdir="relic-$version" rm -rf "$tdir" mkdir -p "$tdir" cp -a ../distro/linux/* doc/relic.yml "$tdir/" cp relic-linux-amd64 "$tdir/relic" tar -cf relic.tar "$tdir" sed -i -e "s/^Version:.*/Version: $version/" $tdir/relic.spec rpmbuild -bb -D "_rpmdir $(pwd)" -D "_sourcedir $(pwd)" $tdir/relic.spec rm -rf "$tdir" cd .. ls -l build/x86_64/*.rpm relic-7.6.1/scripts/version.sh000077500000000000000000000001331455105530300163340ustar00rootroot00000000000000#!/bin/bash git describe --tags --dirty=+ |sed -e 's/-\([0-9]*\).*/+\1/' | sed -e 's/^v//' relic-7.6.1/server/000077500000000000000000000000001455105530300141325ustar00rootroot00000000000000relic-7.6.1/server/daemon/000077500000000000000000000000001455105530300153755ustar00rootroot00000000000000relic-7.6.1/server/daemon/daemon.go000066400000000000000000000133371455105530300171760ustar00rootroot00000000000000// // Copyright (c) SAS Institute Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package daemon import ( "context" "crypto/tls" "errors" "fmt" "io" "net" "net/http" "os" "time" "golang.org/x/net/http2" "golang.org/x/sync/errgroup" "github.com/prometheus/client_golang/prometheus/promhttp" "github.com/rs/zerolog/log" "github.com/sassoftware/relic/v7/config" "github.com/sassoftware/relic/v7/internal/activation" "github.com/sassoftware/relic/v7/internal/zhttp" "github.com/sassoftware/relic/v7/lib/certloader" "github.com/sassoftware/relic/v7/lib/x509tools" "github.com/sassoftware/relic/v7/server" ) type Daemon struct { server *server.Server httpServer *http.Server listeners []net.Listener metrics net.Listener addrs []string eg errgroup.Group } func makeTLSConfig(config *config.Config) (*tls.Config, error) { cert, err := certloader.LoadX509KeyPair(config.Server.CertFile, config.Server.KeyFile) if err != nil { return nil, err } var keyLog io.Writer if klf := os.Getenv("SSLKEYLOGFILE"); klf != "" { fmt.Fprintln(os.Stderr, "WARNING: SSLKEYLOGFILE is set! TLS master secrets will be logged.") keyLog, err = os.OpenFile(klf, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0666) if err != nil { return nil, err } } tconf := &tls.Config{ Certificates: []tls.Certificate{cert.TLS()}, PreferServerCipherSuites: true, SessionTicketsDisabled: true, ClientAuth: tls.RequestClientCert, MinVersion: tls.VersionTLS12, KeyLogWriter: keyLog, } x509tools.SetKeyLogFile(tconf) return tconf, nil } func getListener(index uint, laddr string, tconf *tls.Config) (net.Listener, error) { listener, err := activation.GetListener(index, "tcp", laddr) if err == nil { if listener.Addr().Network() != "tcp" { return nil, errors.New("inherited a listener but it isn't tcp") } listener = tls.NewListener(listener, tconf) } return listener, err } func New(config *config.Config, test bool) (*Daemon, error) { if err := zhttp.SetupLogging(config.Server.LogLevel, config.Server.LogFile); err != nil { return nil, fmt.Errorf("configuring logging: %w", err) } srv, err := server.New(config) if err != nil { return nil, err } httpServer := &http.Server{ Handler: srv.Handler(), ReadHeaderTimeout: time.Second * time.Duration(config.Server.ReadHeaderTimeout), ReadTimeout: time.Second * time.Duration(config.Server.ReadTimeout), WriteTimeout: time.Second * time.Duration(config.Server.WriteTimeout), IdleTimeout: 10 * time.Second, } // configure TLS listener if config.Server.Listen != "" { tconf, err := makeTLSConfig(config) if err != nil { return nil, err } httpServer.TLSConfig = tconf if err := http2.ConfigureServer(httpServer, nil); err != nil { return nil, err } } if test { srv.Close() return nil, nil } var listeners []net.Listener var addrs []string var index uint // open TLS listener if config.Server.Listen != "" { listener, err := getListener(index, config.Server.Listen, httpServer.TLSConfig) if err != nil { return nil, err } listeners = append(listeners, listener) addrs = append(addrs, "https://"+listener.Addr().String()) index++ } // open plaintext listener if config.Server.ListenHTTP != "" { httpListener, err := activation.GetListener(index, "tcp", config.Server.ListenHTTP) if err != nil { return nil, err } listeners = append(listeners, httpListener) addrs = append(addrs, "http://"+httpListener.Addr().String()) index++ } if len(listeners) == 0 { return nil, errors.New("no listeners configured") } // open metrics listener var metricsListener net.Listener if config.Server.ListenMetrics != "" { metricsListener, err = activation.GetListener(index, "tcp", config.Server.ListenMetrics) if err != nil { return nil, err } // index++ } return &Daemon{ server: srv, httpServer: httpServer, listeners: listeners, metrics: metricsListener, addrs: addrs, }, nil } func (d *Daemon) Serve() error { _ = activation.DaemonReady() for _, listener := range d.listeners { listener := listener // re-scope to loop d.eg.Go(func() error { err := d.httpServer.Serve(listener) if err == http.ErrServerClosed { err = nil } return err }) } log.Info().Strs("urls", d.addrs).Msg("listening for requests") if d.metrics != nil { srv := &http.Server{ Handler: promhttp.Handler(), ReadTimeout: 10 * time.Second, WriteTimeout: 10 * time.Second, IdleTimeout: 65 * time.Second, } go func() { err := srv.Serve(d.metrics) log.Err(err).Msg("metrics listener stopped") }() log.Info().Str("url", fmt.Sprintf("http://%s/metrics", d.metrics.Addr())). Msg("listening for metrics") } return d.eg.Wait() } func (d *Daemon) Close() error { // do Shutdown() inside errgroup because it will cause the ongoing Serve() // calls to return immediately and we need something to keep blocking until // all ongoing requests are done and Shutdown() returns d.eg.Go(func() error { ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute) defer cancel() err := d.httpServer.Shutdown(ctx) err2 := d.server.Close() if err == nil { err = err2 } return err }) return d.eg.Wait() } relic-7.6.1/server/response.go000066400000000000000000000042231455105530300163200ustar00rootroot00000000000000// // Copyright (c) SAS Institute Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package server import ( "context" "encoding/json" "errors" "net/http" "github.com/sassoftware/relic/v7/internal/httperror" "github.com/sassoftware/relic/v7/internal/zhttp" "github.com/sassoftware/relic/v7/signers/sigerrors" "github.com/sassoftware/relic/v7/token" ) func handleFunc(f func(http.ResponseWriter, *http.Request) error) http.HandlerFunc { return func(rw http.ResponseWriter, req *http.Request) { ctx := req.Context() if srv, ok := ctx.Value(http.ServerContextKey).(*http.Server); ok && srv.WriteTimeout > 0 { // timeout request context when WriteTimeout is reached ctx, cancel := context.WithTimeout(req.Context(), srv.WriteTimeout) defer cancel() req = req.WithContext(ctx) } err := f(rw, req) if err == nil { return } if resp, ok := err.(http.Handler); ok { resp.ServeHTTP(rw, req) } else if h := errToProblem(err); h != nil { h.ServeHTTP(rw, req) } else { zhttp.WriteUnhandledError(rw, req, err, "") } } } func errToProblem(err error) http.Handler { if e := new(token.KeyUsageError); errors.As(err, e) { return httperror.Problem{ Status: http.StatusBadRequest, Type: httperror.ProblemKeyUsage, Title: "Incorrect Key Usage", Detail: e.Error(), } } else if e := new(sigerrors.ErrNoCertificate); errors.As(err, e) { return httperror.NoCertificateError(e.Type) } return nil } func writeJSON(rw http.ResponseWriter, data interface{}) error { blob, err := json.Marshal(data) if err != nil { return err } rw.Header().Set("Content-Type", "application/json") _, err = rw.Write(blob) return err } relic-7.6.1/server/server.go000066400000000000000000000070541455105530300157750ustar00rootroot00000000000000// // Copyright (c) SAS Institute Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package server import ( "fmt" "net/http" "time" "github.com/go-chi/chi/v5" "github.com/sassoftware/relic/v7/config" "github.com/sassoftware/relic/v7/internal/authmodel" "github.com/sassoftware/relic/v7/internal/realip" "github.com/sassoftware/relic/v7/internal/zhttp" "github.com/sassoftware/relic/v7/lib/compresshttp" "github.com/sassoftware/relic/v7/token" "github.com/sassoftware/relic/v7/token/open" "github.com/sassoftware/relic/v7/token/tokencache" "github.com/sassoftware/relic/v7/token/worker" ) type Server struct { Config *config.Config Closed <-chan bool closeCh chan<- bool tokens map[string]token.Token auth authmodel.Authenticator realIP func(http.Handler) http.Handler } func (s *Server) Handler() http.Handler { r := chi.NewRouter() r.Use(s.realIP) r.Use(zhttp.LoggingMiddleware()) r.Use(zhttp.RecoveryMiddleware) r.Use(compresshttp.Middleware) // unauthenticated methods r.Get("/health", s.serveHealth) r.Get("/directory", handleFunc(s.serveDirectory)) // authenticated methods a := r.With(authmodel.Middleware(s.auth)) a.Get("/", handleFunc(s.serveHome)) a.Get("/list_keys", handleFunc(s.serveListKeys)) a.Get("/keys/{key}", handleFunc(s.serveGetKey)) a.Post("/sign", handleFunc(s.serveSign)) return r } func (s *Server) Close() error { if s.closeCh != nil { close(s.closeCh) s.closeCh = nil } for _, t := range s.tokens { t.Close() } return nil } func New(config *config.Config) (*Server, error) { closed := make(chan bool) auth, err := authmodel.New(config) if err != nil { return nil, fmt.Errorf("configuration authentication: %w", err) } realIP, err := realip.Middleware(config.Server.TrustedProxies) if err != nil { return nil, err } s := &Server{ Config: config, Closed: closed, closeCh: closed, auth: auth, realIP: realIP, tokens: make(map[string]token.Token), } if err := s.openTokens(); err != nil { for _, t := range s.tokens { t.Close() } return nil, err } if err := s.startHealthCheck(); err != nil { return nil, err } return s, nil } // Open each token used by any key. pkcs11 tokens get a worker, while other // types are used in-process via a cache. func (s *Server) openTokens() error { expiry := time.Second * time.Duration(s.Config.Server.TokenCacheSeconds) for _, name := range s.Config.ListServedTokens() { tconf, err := s.Config.GetToken(name) if err != nil { return err } var tok token.Token switch tconf.Type { case "pkcs11": // worker is responsible for metrics and caching tok, err = worker.New(s.Config, name) default: tok, err = open.Token(s.Config, name, nil) if err == nil { // instrument token with metrics and caching tok = tokencache.Metrics{Token: tok} if tconf.RateLimit != 0 { tok = tokencache.NewLimiter(tok, tconf.RateLimit, tconf.RateBurst) } tok = tokencache.New(tok, expiry) } } if err != nil { return fmt.Errorf("configuring token %q: %w", name, err) } s.tokens[name] = tok } return nil } relic-7.6.1/server/view_directory.go000066400000000000000000000035121455105530300175200ustar00rootroot00000000000000// // Copyright (c) SAS Institute Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package server import ( "bytes" "fmt" "math/rand" "net/http" "strings" "github.com/sassoftware/relic/v7/internal/authmodel" "github.com/sassoftware/relic/v7/internal/realip" ) func (s *Server) serveDirectory(rw http.ResponseWriter, req *http.Request) error { sibs := append([]string{}, s.Config.Server.Siblings...) rand.Shuffle(len(sibs), func(i, j int) { sibs[i], sibs[j] = sibs[j], sibs[i] }) if !strings.Contains(req.Header.Get("Accept"), "json") { // legacy path var buf bytes.Buffer if len(sibs) == 0 { u := realip.BaseURL(req) sibs = []string{u.String()} } for _, h := range sibs { _, _ = fmt.Fprintf(&buf, "%s\r\n", h) } var err error _, err = rw.Write(buf.Bytes()) return err } md := authmodel.Metadata{ Hosts: sibs, Auth: []authmodel.AuthMetadata{ {Type: authmodel.AuthTypeCertificate}, }, } if _, ok := s.auth.(*authmodel.PolicyAuth); ok { md.Auth = append(md.Auth, authmodel.AuthMetadata{Type: authmodel.AuthTypeBearerToken}) if aad := s.Config.Server.AzureAD; aad != nil { md.Auth = append(md.Auth, authmodel.AuthMetadata{ Type: authmodel.AuthTypeAzureAD, Authority: aad.Authority, ClientID: aad.ClientID, Scopes: aad.Scopes, }) } } return writeJSON(rw, md) } relic-7.6.1/server/view_getkey.go000066400000000000000000000054661455105530300170160ustar00rootroot00000000000000// // Copyright (c) SAS Institute Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package server import ( "bytes" "context" "crypto/x509" "encoding/pem" "fmt" "net/http" "golang.org/x/crypto/openpgp" "golang.org/x/crypto/openpgp/armor" "github.com/go-chi/chi/v5" "github.com/sassoftware/relic/v7/config" "github.com/sassoftware/relic/v7/internal/authmodel" "github.com/sassoftware/relic/v7/internal/httperror" "github.com/sassoftware/relic/v7/internal/signinit" ) type keyInfo struct { X509Certificate string PGPCertificate string } func (s *Server) serveGetKey(rw http.ResponseWriter, req *http.Request) error { userInfo := authmodel.RequestInfo(req) keyName := chi.URLParam(req, "key") keyConf, err := s.Config.GetKey(keyName) if err == nil && userInfo.Allowed(keyConf) { info, err := s.getKeyInfo(req.Context(), keyConf) if err != nil { return err } return writeJSON(rw, info) } return httperror.ErrForbidden } func (s *Server) getKeyInfo(ctx context.Context, keyConf *config.KeyConfig) (keyInfo, error) { tok := s.tokens[keyConf.Token] if tok == nil { return keyInfo{}, fmt.Errorf("missing token \"%s\" for key \"%s\"", keyConf.Token, keyConf.Name()) } cert, _, err := signinit.InitKey(ctx, tok, keyConf.Name()) if err != nil { return keyInfo{}, err } var info keyInfo if cert.PgpKey != nil { info.PGPCertificate, err = marshalPGPCert(cert.PgpKey) if err != nil { return keyInfo{}, err } } if cert.Leaf != nil { info.X509Certificate, err = marshalX509Cert(cert.Certificates) if err != nil { return keyInfo{}, err } } return info, nil } // marshal entire X509 certificate chain in PEM format func marshalX509Cert(certs []*x509.Certificate) (string, error) { var buf bytes.Buffer for _, cert := range certs { block := &pem.Block{Type: "CERTIFICATE", Bytes: cert.Raw} if err := pem.Encode(&buf, block); err != nil { return "", err } } return buf.String(), nil } // marshal PGP public certificate in ASCII armor func marshalPGPCert(entity *openpgp.Entity) (string, error) { var buf bytes.Buffer w, err := armor.Encode(&buf, "PGP PUBLIC KEY BLOCK", nil) if err != nil { return "", err } if err := entity.Serialize(w); err != nil { return "", err } if err := w.Close(); err != nil { return "", err } buf.WriteString("\n") return buf.String(), nil } relic-7.6.1/server/view_health.go000066400000000000000000000071361455105530300167670ustar00rootroot00000000000000// // Copyright (c) SAS Institute Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package server import ( "context" "net/http" "sync" "time" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promauto" "github.com/rs/zerolog/log" "github.com/sassoftware/relic/v7/internal/zhttp" "github.com/sassoftware/relic/v7/token" ) var ( healthStatus int healthLastPing time.Time healthMu sync.Mutex metricTokenCheckErrors = promauto.NewGaugeVec( prometheus.GaugeOpts{ Name: "token_check_sequential_errors", Help: "Number of sequential errors seen while checking the token's status", }, []string{"token"}, ) ) func (s *Server) healthCheckInterval() time.Duration { return time.Second * time.Duration(s.Config.Server.TokenCheckInterval) } func (s *Server) startHealthCheck() error { healthStatus = s.Config.Server.TokenCheckFailures healthLastPing = time.Now() go s.healthCheckLoop() return nil } func (s *Server) healthCheckLoop() { interval := s.healthCheckInterval() t := time.NewTimer(0) defer t.Stop() for { select { case <-t.C: s.healthCheck() t.Reset(interval) case <-s.Closed: break } } } func (s *Server) healthCheck() bool { healthMu.Lock() last := healthStatus healthMu.Unlock() var notOK []string for name, token := range s.tokens { metric := metricTokenCheckErrors.WithLabelValues(name) if s.pingOne(token) { metric.Set(0) } else { metric.Inc() notOK = append(notOK, name) } } next := last if len(notOK) == 0 { ev := log.Info().Str("token_state", "OK") if last == 0 { ev.Msg("recovered to normal state, status is now OK") } else if last < s.Config.Server.TokenCheckFailures { ev.Msg("recovered to normal state") } next = s.Config.Server.TokenCheckFailures } else if last > 0 { next-- if next == 0 { log.Error().Str("token_state", "ERROR"). Msg("exceeded maximum health check failures, flagging as ERROR") } } healthMu.Lock() defer healthMu.Unlock() healthStatus = next healthLastPing = time.Now() return len(notOK) == 0 } func (s *Server) pingOne(tok token.Token) bool { ctx, cancel := context.WithTimeout(context.Background(), time.Second*time.Duration(s.Config.Server.TokenCheckTimeout)) defer cancel() if err := tok.Ping(ctx); err != nil { ev := log.Error().Str("token", tok.Config().Name()) if ctx.Err() != nil { ev.Msg("token health check timed out") } else { ev.Err(err).Msg("token health check failed") } return false } return true } func (s *Server) Healthy(request *http.Request) bool { if s.Config.Server.Disabled { return false } healthMu.Lock() defer healthMu.Unlock() if time.Since(healthLastPing) > 3*s.healthCheckInterval() { if request != nil { log.Error().Dur("stale_for", time.Since(healthLastPing)).Msg("health check is stale") } return false } return healthStatus > 0 } func (s *Server) serveHealth(rw http.ResponseWriter, request *http.Request) { zhttp.DontLog(request) if s.Healthy(request) { _, _ = rw.Write([]byte("OK\r\n")) } else { http.Error(rw, "health check failed", http.StatusServiceUnavailable) } } relic-7.6.1/server/view_home.go000066400000000000000000000014161455105530300164450ustar00rootroot00000000000000// // Copyright (c) SAS Institute Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package server import ( "net/http" ) func (*Server) serveHome(rw http.ResponseWriter, req *http.Request) error { _, err := rw.Write([]byte("Welcome to relic!\n")) return err } relic-7.6.1/server/view_listkeys.go000066400000000000000000000022261455105530300173640ustar00rootroot00000000000000// // Copyright (c) SAS Institute Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package server import ( "net/http" "sort" "github.com/sassoftware/relic/v7/internal/authmodel" ) func (s *Server) serveListKeys(rw http.ResponseWriter, req *http.Request) error { userInfo := authmodel.RequestInfo(req) keys := []string{} for key, keyConf := range s.Config.Keys { if keyConf.Hide { continue } if keyConf.Alias != "" { keyConf = s.Config.Keys[keyConf.Alias] if keyConf == nil { continue } } if !keyConf.Hide && userInfo.Allowed(keyConf) { keys = append(keys, key) } } sort.Strings(keys) return writeJSON(rw, keys) } relic-7.6.1/server/view_sign.go000066400000000000000000000071661455105530300164650ustar00rootroot00000000000000// // Copyright (c) SAS Institute Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package server import ( "crypto" "fmt" "net/http" "github.com/rs/zerolog/hlog" "github.com/sassoftware/relic/v7/internal/authmodel" "github.com/sassoftware/relic/v7/internal/httperror" "github.com/sassoftware/relic/v7/internal/signinit" "github.com/sassoftware/relic/v7/internal/zhttp" "github.com/sassoftware/relic/v7/lib/readercounter" "github.com/sassoftware/relic/v7/lib/x509tools" "github.com/sassoftware/relic/v7/signers" ) const defaultHash = crypto.SHA256 func (s *Server) serveSign(rw http.ResponseWriter, request *http.Request) error { // parse parameters query := request.URL.Query() keyName := query.Get("key") if keyName == "" { return httperror.MissingParameterError("key") } filename := query.Get("filename") if filename == "" { return httperror.MissingParameterError("filename") } sigType := query.Get("sigtype") // authorize key userInfo := authmodel.RequestInfo(request) keyConf, err := s.Config.GetKey(keyName) if err != nil { hlog.FromRequest(request).Err(err).Str("key", keyName).Msg("key not found") return httperror.ErrForbidden } else if !userInfo.Allowed(keyConf) { hlog.FromRequest(request).Error().Str("key", keyName).Msg("access to key denied") return httperror.ErrForbidden } // configure signer mod := signers.ByName(sigType) if mod == nil { hlog.FromRequest(request).Error().Str("sigtype", sigType).Msg("signature type not found") return httperror.ErrUnknownSignatureType } hash := defaultHash if digest := request.URL.Query().Get("digest"); digest != "" { hash = x509tools.HashByName(digest) if hash == 0 { hlog.FromRequest(request).Error().Str("digest", digest).Msg("digest type not found") return httperror.ErrUnknownDigest } } // parse flags for signer flags, err := mod.FlagsFromQuery(query) if err != nil { hlog.FromRequest(request).Err(err).Str("sigtype", sigType). Msg("failed to parse signer arguments") return httperror.BadParameterError(err) } // get key from token and initialize signer context tok := s.tokens[keyConf.Token] if tok == nil { return fmt.Errorf("missing token \"%s\" for key \"%s\"", keyConf.Token, keyName) } cert, opts, err := signinit.Init(request.Context(), mod, tok, keyName, hash, flags) if err != nil { return err } opts.Audit.Attributes["client.ip"] = zhttp.StripPort(request.RemoteAddr) opts.Audit.Attributes["client.filename"] = filename userInfo.AuditContext(opts.Audit) // sign the request stream and output a binpatch or signature blob counter := readercounter.New(request.Body) blob, err := mod.Sign(counter, cert, *opts) if err != nil { return err } opts.Audit.Attributes["perf.size.in"] = counter.N opts.Audit.Attributes["perf.size.patch"] = len(blob) if err := signinit.PublishAudit(opts.Audit); err != nil { return err } ev := hlog.FromRequest(request).Info(). Str("key", keyConf.Name()). Str("filename", filename) if mod.FormatLog != nil { ev.Dict("package", mod.FormatLog(opts.Audit)) } ev.Msg("signed package") rw.Header().Set("Content-Type", opts.Audit.GetMimeType()) _, err = rw.Write(blob) return err } relic-7.6.1/signers/000077500000000000000000000000001455105530300142765ustar00rootroot00000000000000relic-7.6.1/signers/apk/000077500000000000000000000000001455105530300150515ustar00rootroot00000000000000relic-7.6.1/signers/apk/digest.go000066400000000000000000000072721455105530300166670ustar00rootroot00000000000000// // Copyright (c) SAS Institute Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package apk import ( "bytes" "crypto" "crypto/rand" "encoding/binary" "errors" "io" "github.com/sassoftware/relic/v7/lib/binpatch" "github.com/sassoftware/relic/v7/lib/certloader" "github.com/sassoftware/relic/v7/lib/x509tools" "github.com/sassoftware/relic/v7/lib/zipslicer" ) type Digest struct { inz *zipslicer.Directory hash crypto.Hash value []byte sigLoc int64 } func digestApkStream(r io.Reader, hash crypto.Hash) (*Digest, error) { inz, err := zipslicer.ReadZipTar(r) if err != nil { return nil, err } hasher := newMerkleHasher([]crypto.Hash{hash}) for _, f := range inz.File { _, err := f.Dump(hasher) if err != nil { return nil, err } } sigLoc, err := inz.NextFileOffset() if err != nil { return nil, err } origDirLoc := inz.DirLoc inz.DirLoc = sigLoc digests, err := hasher.Finish(inz, true) if err != nil { return nil, err } inz.DirLoc = origDirLoc return &Digest{ inz: inz, hash: hash, value: digests[0], sigLoc: sigLoc, }, nil } func (d *Digest) Sign(cert *certloader.Certificate) (*binpatch.PatchSet, error) { // select a signature type alg := x509tools.GetPublicKeyAlgorithm(cert.Leaf.PublicKey) var st sigType for _, s := range sigTypes { if s.hash == d.hash && s.alg == alg && !s.pss { st = s break } // TODO: PSS } if st.id == 0 { return nil, errors.New("unsupported public key algorithm") } // build signed data sd := apkSignedData{ Digests: []apkDigest{apkDigest{ID: st.id, Value: d.value}}, } for _, cert := range cert.Chain() { sd.Certificates = append(sd.Certificates, cert.Raw) } signedData, err := marshal(sd) if err != nil { return nil, err } // sign digest := st.hash.New() digest.Write(signedData.Bytes()) sigv, err := cert.Signer().Sign(rand.Reader, digest.Sum(nil), st.hash) if err != nil { return nil, err } // build signer block signerList := []apkSigner{apkSigner{ SignedData: signedData, Signatures: []apkSignature{apkSignature{ID: st.id, Value: sigv}}, PublicKey: cert.Leaf.RawSubjectPublicKeyInfo, }} sblob, err := marshal(signerList) if err != nil { return nil, err } block := makeSigBlock(sblob) // patch patchset := binpatch.New() origDirLoc := d.inz.DirLoc patchset.Add(d.sigLoc, origDirLoc-d.sigLoc, block) d.inz.DirLoc = d.sigLoc + int64(len(block)) var dirEnts, endOfDir bytes.Buffer if err := d.inz.WriteDirectory(&dirEnts, &endOfDir, false); err != nil { return nil, err } patchset.Add(origDirLoc+int64(dirEnts.Len()), int64(endOfDir.Len()), endOfDir.Bytes()) return patchset, nil } func makeSigBlock(sblob []byte) []byte { block := make([]byte, 8+12+len(sblob)+24) // length prefix on signing block, includes the magic suffix but not itself binary.LittleEndian.PutUint64(block, uint64(8+4+len(sblob)+8+16)) // length prefix on the inner block binary.LittleEndian.PutUint64(block[8:], uint64(4+len(sblob))) // block type binary.LittleEndian.PutUint32(block[8+8:], sigApkV2) // the block itself copy(block[8+8+4:], sblob) // magic suffix suffix := block[8+8+4+len(sblob):] copy(suffix, block[:8]) // length again copy(suffix[8:], sigMagic) // magic return block } relic-7.6.1/signers/apk/merkle.go000066400000000000000000000063261455105530300166660ustar00rootroot00000000000000// // Copyright (c) SAS Institute Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package apk import ( "bytes" "crypto" "encoding/binary" "errors" "github.com/sassoftware/relic/v7/lib/zipslicer" ) const merkleBlock = 1048576 // compute hashes of each 1MiB written type merkleHasher struct { hashes []crypto.Hash blocks [][]byte buf []byte n int count uint32 } func newMerkleHasher(hashes []crypto.Hash) *merkleHasher { return &merkleHasher{ buf: make([]byte, merkleBlock), hashes: hashes, blocks: make([][]byte, len(hashes)), } } func (h *merkleHasher) block(block []byte) { var pref [5]byte pref[0] = 0xa5 binary.LittleEndian.PutUint32(pref[1:], uint32(len(block))) for i, hash := range h.hashes { d := hash.New() d.Write(pref[:]) d.Write(block) h.blocks[i] = d.Sum(h.blocks[i]) } h.count++ } func (h *merkleHasher) Write(d []byte) (int, error) { w := len(d) // completing previously buffered data if h.n != 0 && h.n+len(d) >= merkleBlock { n := h.n copy(h.buf[n:merkleBlock], d) d = d[merkleBlock-n:] h.block(h.buf) h.n = 0 } // larger than a block -- hash it directly for len(d) >= merkleBlock { h.block(d[:merkleBlock]) d = d[merkleBlock:] } // save the rest for later if len(d) != 0 { copy(h.buf[h.n:], d) h.n += len(d) } return w, nil } func (h *merkleHasher) flush() { if h.n != 0 { h.block(h.buf[:h.n]) h.n = 0 } } // after content is written, finish the digest by adding the central directory and end of directory func (h *merkleHasher) Finish(inz *zipslicer.Directory, modified bool) ([][]byte, error) { // https://source.android.com/security/apksigning/v2#integrity-protected-contents // section 1: contents of zip entries (already written to hasher) h.flush() // section 2 is the signature block itself (not digested obviously) // section 3: central directory // TODO: zip64 support? android doesn't support it if inz.DirLoc >= (1 << 32) { return nil, errors.New("ZIP64 is not yet supported") } var cdirEntries, endOfDir []byte if modified { var b1, b2 bytes.Buffer if err := inz.WriteDirectory(&b1, &b2, false); err != nil { return nil, err } cdirEntries = b1.Bytes() endOfDir = b2.Bytes() } else { var err error cdirEntries, endOfDir, err = inz.GetOriginalDirectory(true) if err != nil { return nil, err } } _, _ = h.Write(cdirEntries) h.flush() // section 4: end of central directory _, _ = h.Write(endOfDir) h.flush() // compute final hash var pref [5]byte pref[0] = 0x5a binary.LittleEndian.PutUint32(pref[1:], h.count) ret := make([][]byte, len(h.hashes)) for i, hash := range h.hashes { master := hash.New() master.Write(pref[:]) master.Write(h.blocks[i]) ret[i] = master.Sum(nil) } return ret, nil } relic-7.6.1/signers/apk/serializer.go000066400000000000000000000112171455105530300175530ustar00rootroot00000000000000// // Copyright (c) SAS Institute Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package apk import ( "encoding/binary" "errors" "fmt" "io" "reflect" ) // implement the uint32-prefixed structure of a APK Signature Scheme v2 Block // https://source.android.com/security/apksigning/v2#apk-signature-scheme-v2-block-format var errTrailingData = errors.New("trailing data after structure") type apkRaw []byte // Bytes returns the inner content of the raw item, without the length prefix func (r apkRaw) Bytes() []byte { return []byte(r[4:]) } var ( bytesType = reflect.TypeOf([]byte(nil)) rawType = reflect.TypeOf(apkRaw(nil)) uint32Type = reflect.TypeOf(uint32(0)) ) func unmarshal(blob []byte, dest interface{}) error { v := reflect.ValueOf(dest) if v.Kind() != reflect.Ptr || v.IsNil() { return errors.New("target of unmarshal must be a non-nil pointer") } v = v.Elem() blob, err := unmarshalR(blob, v) //, "x") if err != nil { return err } else if len(blob) != 0 { return errTrailingData } return nil } func unmarshalR(blob []byte, v reflect.Value /*, path string*/) ([]byte, error) { // scalar types (no prefix) switch { case v.Type() == uint32Type: if len(blob) < 4 { return nil, io.ErrUnexpectedEOF } i := binary.LittleEndian.Uint32(blob) //fmt.Printf("%s = 0x%x\n", path, i) v.SetUint(uint64(i)) return blob[4:], nil } // read uint32 length prefix if len(blob) < 4 { return nil, io.ErrUnexpectedEOF } size := int(binary.LittleEndian.Uint32(blob)) if 4+len(blob) < size { return nil, io.ErrUnexpectedEOF } remainder := blob[4+size:] raw := blob[:4+size] blob = raw[4:] switch { case v.Type() == bytesType: // []byte //fmt.Printf("%s = []byte(\"%x\")\n", path, blob) v.SetBytes(blob) case v.Type() == rawType: // apkRaw (same as above but keep the prefix) //fmt.Printf("%s = raw(\"%x\")\n", path, raw) v.SetBytes(raw) // compound types case v.Kind() == reflect.Slice: // slice other than []byte itemType := v.Type().Elem() //fmt.Printf("%s = %s{}\n", path, v.Type()) v.SetLen(0) for len(blob) > 0 { var err error // append a zero value and unmarshal directly into the slice n := v.Len() v.Set(reflect.Append(v, reflect.Zero(itemType))) blob, err = unmarshalR(blob, v.Index(n)) //, fmt.Sprintf("%s[%d]", path, n)) if err != nil { return nil, err } } case v.Kind() == reflect.Struct: // structure //fmt.Printf("%s = %s{}\n", path, v.Type()) for i := 0; i < v.NumField(); i++ { var err error blob, err = unmarshalR(blob, v.Field(i)) //, fmt.Sprintf("%s.%s", path, v.Type().Field(i).Name)) if err != nil { return nil, err } } if len(blob) > 0 { return nil, errTrailingData } default: panic("can't unmarshal type " + v.Type().String()) } return remainder, nil } func marshal(src interface{}) (apkRaw, error) { v := reflect.ValueOf(src) m := new(marshaller) if err := m.marshal(v); err != nil { return nil, err } return apkRaw(m.buf), nil } type marshaller struct { buf []byte pos int } func (m *marshaller) grow(n int) []byte { if cap(m.buf)-m.pos < n { buf := make([]byte, 2*cap(m.buf)+n) copy(buf, m.buf) m.buf = buf } m.buf = m.buf[:m.pos+n] ret := m.buf[m.pos : m.pos+n] m.pos += n return ret } func (m *marshaller) write(d []byte) { copy(m.grow(len(d)), d) } func (m *marshaller) marshal(v reflect.Value) error { if v.Type() == rawType { // raw m.write(v.Bytes()) return nil } // scalar types switch { case v.Type() == uint32Type: binary.LittleEndian.PutUint32(m.grow(4), uint32(v.Uint())) return nil } // prefixed types start := m.pos m.grow(4) switch { case v.Type() == bytesType: // []byte m.write(v.Bytes()) case v.Kind() == reflect.Slice: // slice other than []byte for i := 0; i < v.Len(); i++ { if err := m.marshal(v.Index(i)); err != nil { return err } } case v.Kind() == reflect.Struct: // structure for i := 0; i < v.NumField(); i++ { if err := m.marshal(v.Field(i)); err != nil { return err } } default: return fmt.Errorf("can't marshal type %s", v.Type()) } // put prefix end := m.pos binary.LittleEndian.PutUint32(m.buf[start:], uint32(end-start-4)) return nil } relic-7.6.1/signers/apk/signer.go000066400000000000000000000030571455105530300166740ustar00rootroot00000000000000// // Copyright (c) SAS Institute Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package apk import ( "errors" "io" "github.com/sassoftware/relic/v7/lib/certloader" "github.com/sassoftware/relic/v7/lib/magic" "github.com/sassoftware/relic/v7/signers" "github.com/sassoftware/relic/v7/signers/zipbased" ) // Sign Android packages var ApkSigner = &signers.Signer{ Name: "apk", Magic: magic.FileTypeAPK, CertTypes: signers.CertTypeX509, Transform: zipbased.Transform, Sign: sign, Verify: verify, } const ( sigMagic = "APK Sig Block 42" sigApkV2 = 0x7109871a ) var ( errMalformed = errors.New("malformed APK signing block") errTruncated = errors.New("truncated APK signing block sequence") ) func init() { signers.Register(ApkSigner) } func sign(r io.Reader, cert *certloader.Certificate, opts signers.SignOpts) ([]byte, error) { digest, err := digestApkStream(r, opts.Hash) if err != nil { return nil, err } patchset, err := digest.Sign(cert) if err != nil { return nil, err } return opts.SetBinPatch(patchset) } relic-7.6.1/signers/apk/structs.go000066400000000000000000000044011455105530300171060ustar00rootroot00000000000000// // Copyright (c) SAS Institute Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package apk import ( "crypto" "crypto/x509" "fmt" ) type apkSigner struct { SignedData apkRaw Signatures []apkSignature PublicKey []byte } type apkSignedData struct { Digests []apkDigest Certificates [][]byte Attributes []apkAttribute } type apkAttribute struct { ID uint32 Value []byte } type apkSignature apkAttribute type apkDigest apkAttribute func (sd *apkSignedData) ParseCertificates() (certs []*x509.Certificate, err error) { certs = make([]*x509.Certificate, len(sd.Certificates)) for i, der := range sd.Certificates { certs[i], err = x509.ParseCertificate(der) if err != nil { return nil, err } } return } type sigType struct { id uint32 hash crypto.Hash alg x509.PublicKeyAlgorithm pss bool } var sigTypes = []sigType{ sigType{0x0101, crypto.SHA256, x509.RSA, true}, // RSASSA-PSS with SHA2-256 digest sigType{0x0102, crypto.SHA512, x509.RSA, true}, // RSASSA-PSS with SHA2-512 digest sigType{0x0103, crypto.SHA256, x509.RSA, false}, // RSASSA-PKCS1-v1_5 with SHA2-256 digest sigType{0x0104, crypto.SHA512, x509.RSA, false}, // RSASSA-PKCS1-v1_5 with SHA2-512 digest sigType{0x0201, crypto.SHA256, x509.ECDSA, false}, // ECDSA with SHA2-256 digest sigType{0x0202, crypto.SHA512, x509.ECDSA, false}, // ECDSA with SHA2-512 digest sigType{0x0301, crypto.SHA256, x509.DSA, false}, // DSA with SHA2-256 digest } func sigTypeByID(id uint32) (st sigType, err error) { for _, s := range sigTypes { if s.id == id { st = s break } } if st.id == 0 { return st, fmt.Errorf("unknown signature type 0x%04x", id) } if !st.hash.Available() { return st, fmt.Errorf("unsupported signature type 0x%04x", id) } return } relic-7.6.1/signers/apk/verify.go000066400000000000000000000157621455105530300167170ustar00rootroot00000000000000// // Copyright (c) SAS Institute Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package apk import ( "archive/zip" "bytes" "crypto" "crypto/ecdsa" "crypto/hmac" "crypto/rsa" "crypto/x509" "encoding/binary" "errors" "fmt" "io" "os" "strings" "github.com/sassoftware/relic/v7/lib/pkcs7" "github.com/sassoftware/relic/v7/lib/pkcs9" "github.com/sassoftware/relic/v7/lib/signjar" "github.com/sassoftware/relic/v7/lib/x509tools" "github.com/sassoftware/relic/v7/lib/zipslicer" "github.com/sassoftware/relic/v7/signers" "github.com/sassoftware/relic/v7/signers/sigerrors" ) func verify(f *os.File, opts signers.VerifyOpts) ([]*signers.Signature, error) { // verify v2 inz, block, err := getSigBlock(f) if err != nil { return nil, err } var allSigs []*signers.Signature for len(block) > 0 { if len(block) < 12 { return nil, errTruncated } partSize := binary.LittleEndian.Uint64(block) block = block[8:] if partSize < 4 || partSize > uint64(len(block)) { return nil, errTruncated } partType := binary.LittleEndian.Uint32(block) partBlob := block[4:partSize] block = block[partSize:] if partType != sigApkV2 { continue } var signerList []apkSigner if err := unmarshal(partBlob, &signerList); err != nil { return nil, fmt.Errorf("parsing signature block: %w", err) } else if len(signerList) == 0 { return nil, errors.New("empty APK signing block") } for i, signer := range signerList { sig, err := signer.Verify(nil) if err != nil { return nil, fmt.Errorf("APK signature #%d: %w", i+1, err) } allSigs = append(allSigs, sig) } } v2present := len(allSigs) != 0 // verify v1 inzr, err := zip.NewReader(f, inz.Size) if err != nil { return nil, err } jarSigs, err := signjar.Verify(inzr, false) if err != nil { if _, ok := err.(sigerrors.NotSignedError); !ok { return nil, err } } for _, jarSig := range jarSigs { apk := jarSig.SignatureHeader.Get("X-Android-APK-Signed") if strings.ContainsRune(apk, '2') && !v2present { return nil, errors.New("V1 signature contains X-Android-APK-Signed header but no V2 signature exists") } allSigs = append(allSigs, &signers.Signature{ SigInfo: "v1", Hash: jarSig.Hash, X509Signature: &jarSig.TimestampedSignature, }) } if len(allSigs) == 0 { return nil, sigerrors.NotSignedError{Type: "APK"} } return allSigs, nil } func getSigBlock(f *os.File) (*zipslicer.Directory, []byte, error) { size, err := f.Seek(0, io.SeekEnd) if err != nil { return nil, nil, err } inz, err := zipslicer.Read(f, size) if err != nil { return nil, nil, err } // check that there is stuff between the last file and the start of the directory if len(inz.File) == 0 { return nil, nil, errors.New("no files in APK") } sigLoc, err := inz.NextFileOffset() if err != nil { return nil, nil, err } if sigLoc == inz.DirLoc { // not signed return inz, nil, nil } // read signature block blob := make([]byte, inz.DirLoc-sigLoc) if _, err := f.ReadAt(blob, sigLoc); err != nil { return nil, nil, err } // check magic if !bytes.HasSuffix(blob, []byte(sigMagic)) { return nil, nil, errMalformed } expected := uint64(len(blob) - 8) size1 := binary.LittleEndian.Uint64(blob) size2 := binary.LittleEndian.Uint64(blob[len(blob)-24:]) if size1 != expected || size2 != expected { return nil, nil, errMalformed } return inz, blob[8 : len(blob)-24], nil } func (s *apkSigner) Verify(inz *zipslicer.Directory) (*signers.Signature, error) { if len(s.Signatures) == 0 { return nil, errors.New("no signatures in APK signer block") } // check signatures over SignedData publicKey, err := x509.ParsePKIXPublicKey(s.PublicKey) if err != nil { return nil, err } var bestHash crypto.Hash for _, sig := range s.Signatures { hash, err := sig.VerifySignature(publicKey, s.SignedData.Bytes()) if err != nil { return nil, err } // check them all but only need to report one per signature if hash > bestHash { bestHash = hash } } // check digests var signedData apkSignedData if err := unmarshal(s.SignedData, &signedData); err != nil { return nil, err } if len(signedData.Digests) == 0 { return nil, errors.New("no digests in APK signed data block") } if inz != nil { hashes := make([]crypto.Hash, len(signedData.Digests)) for i, digest := range signedData.Digests { st, err := sigTypeByID(digest.ID) if err != nil { return nil, err } hashes[i] = st.hash } hasher := newMerkleHasher(hashes) for _, f := range inz.File { if _, err := f.Dump(hasher); err != nil { return nil, err } } digests, err := hasher.Finish(inz, false) if err != nil { return nil, err } for i, digest := range signedData.Digests { if !hmac.Equal(digest.Value, digests[i]) { return nil, fmt.Errorf("digest mismatch for algorithm 0x%04x", digest.ID) } } } // identify which certificate is the leaf certs, err := signedData.ParseCertificates() if err != nil { return nil, err } var leaf *x509.Certificate var intermediates []*x509.Certificate for _, cert := range certs { if bytes.Equal(cert.RawSubjectPublicKeyInfo, s.PublicKey) { leaf = cert } else { intermediates = append(intermediates, cert) } } if leaf == nil { return nil, errors.New("public key does not match any certificate") } return &signers.Signature{ SigInfo: "v2", Hash: bestHash, X509Signature: &pkcs9.TimestampedSignature{ Signature: pkcs7.Signature{ Certificate: leaf, Intermediates: intermediates, }, }, }, nil } func (sig *apkSignature) VerifySignature(publicKey interface{}, signedData []byte) (crypto.Hash, error) { st, err := sigTypeByID(sig.ID) if err != nil { return 0, err } d := st.hash.New() d.Write(signedData) hashed := d.Sum(nil) switch st.alg { case x509.RSA: pub, ok := publicKey.(*rsa.PublicKey) if !ok { return 0, errors.New("public key algorithm mismatch") } if st.pss { err = rsa.VerifyPSS(pub, st.hash, hashed, sig.Value, &rsa.PSSOptions{SaltLength: rsa.PSSSaltLengthEqualsHash}) } else { err = rsa.VerifyPKCS1v15(pub, st.hash, hashed, sig.Value) } if err != nil { return 0, err } case x509.ECDSA: pub, ok := publicKey.(*ecdsa.PublicKey) if !ok { return 0, errors.New("public key algorithm mismatch") } esig, err := x509tools.UnmarshalEcdsaSignature(sig.Value) if err != nil { return 0, err } if !ecdsa.Verify(pub, hashed, esig.R, esig.S) { return 0, errors.New("ECDSA verification failed") } default: return 0, errors.New("unsupported public key algorithm") } return st.hash, nil } relic-7.6.1/signers/appmanifest/000077500000000000000000000000001455105530300166055ustar00rootroot00000000000000relic-7.6.1/signers/appmanifest/signer.go000066400000000000000000000057661455105530300204410ustar00rootroot00000000000000// // Copyright (c) SAS Institute Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package appmanifest // Sign Microsoft ClickOnce application manifests and deployment manifests. // These take the form of an XML file using XML DSIG signatures and, unlike all // other Microsoft signatures, does not use an Authenticode PKCS#7 structure. import ( "fmt" "io" "io/ioutil" "github.com/rs/zerolog" "github.com/sassoftware/relic/v7/lib/appmanifest" "github.com/sassoftware/relic/v7/lib/audit" "github.com/sassoftware/relic/v7/lib/certloader" "github.com/sassoftware/relic/v7/lib/magic" "github.com/sassoftware/relic/v7/lib/pkcs9" "github.com/sassoftware/relic/v7/signers" ) var AppSigner = &signers.Signer{ Name: "appmanifest", Magic: magic.FileTypeAppManifest, CertTypes: signers.CertTypeX509, FormatLog: formatLog, Sign: sign, VerifyStream: verify, } func init() { AppSigner.Flags().Bool("rfc3161-timestamp", true, "(APPMANIFEST) Timestamp with RFC3161 server") signers.Register(AppSigner) } func formatLog(info *audit.Info) *zerolog.Event { return info.AttrsForLog("assembly.") } func sign(r io.Reader, cert *certloader.Certificate, opts signers.SignOpts) ([]byte, error) { blob, err := ioutil.ReadAll(r) if err != nil { return nil, err } signed, err := appmanifest.Sign(blob, cert, opts.Hash) if err != nil { return nil, err } if cert.Timestamper != nil { tsreq := &pkcs9.Request{ EncryptedDigest: signed.EncryptedDigest, Legacy: !opts.Flags.GetBool("rfc3161-timestamp"), Hash: opts.Hash, } token, err := cert.Timestamper.Timestamp(opts.Context(), tsreq) if err != nil { return nil, err } if err := signed.AddTimestamp(token); err != nil { return nil, err } } opts.Audit.SetMimeType("application/xml") opts.Audit.Attributes["assembly.name"] = signed.AssemblyName opts.Audit.Attributes["assembly.version"] = signed.AssemblyVersion opts.Audit.Attributes["assembly.publicKeyToken"] = signed.PublicKeyToken opts.Audit.SetCounterSignature(signed.Signature.CounterSignature) return signed.Signed, nil } func verify(r io.Reader, opts signers.VerifyOpts) ([]*signers.Signature, error) { blob, err := ioutil.ReadAll(r) if err != nil { return nil, err } sig, err := appmanifest.Verify(blob) if err != nil { return nil, err } return []*signers.Signature{&signers.Signature{ Package: fmt.Sprintf("%s %s", sig.AssemblyName, sig.AssemblyVersion), Hash: sig.Hash, X509Signature: sig.Signature, }}, nil } relic-7.6.1/signers/appx/000077500000000000000000000000001455105530300152465ustar00rootroot00000000000000relic-7.6.1/signers/appx/signer.go000066400000000000000000000043411455105530300170660ustar00rootroot00000000000000// // Copyright (c) SAS Institute Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package appx // Sign Windows Universal (UWP) .appx and .appxbundle import ( "fmt" "io" "os" "github.com/sassoftware/relic/v7/lib/certloader" "github.com/sassoftware/relic/v7/lib/magic" "github.com/sassoftware/relic/v7/lib/signappx" "github.com/sassoftware/relic/v7/signers" "github.com/sassoftware/relic/v7/signers/pecoff" "github.com/sassoftware/relic/v7/signers/zipbased" ) var AppxSigner = &signers.Signer{ Name: "appx", Magic: magic.FileTypeAPPX, CertTypes: signers.CertTypeX509, Transform: zipbased.Transform, Sign: sign, Verify: verify, } func init() { pecoff.AddOpusFlags(AppxSigner) signers.Register(AppxSigner) } func sign(r io.Reader, cert *certloader.Certificate, opts signers.SignOpts) ([]byte, error) { digest, err := signappx.DigestAppxTar(r, opts.Hash, false) if err != nil { return nil, err } patch, priSig, _, err := digest.Sign(opts.Context(), cert, pecoff.OpusFlags(opts)) if err != nil { return nil, err } opts.Audit.SetCounterSignature(priSig.CounterSignature) return opts.SetBinPatch(patch) } func verify(f *os.File, opts signers.VerifyOpts) ([]*signers.Signature, error) { size, err := f.Seek(0, io.SeekEnd) if err != nil { return nil, err } sig, err := signappx.Verify(f, size, opts.NoDigests) if err != nil { return nil, err } appxSig := sig if sig.IsBundle { for _, nested := range sig.Bundled { appxSig = nested break } } return []*signers.Signature{{ Package: fmt.Sprintf("{%s} %s %s", appxSig.Name, appxSig.DisplayName, appxSig.Version), SigInfo: pecoff.FormatOpus(sig.OpusInfo), Hash: sig.Hash, X509Signature: sig.Signature, }}, nil } relic-7.6.1/signers/cab/000077500000000000000000000000001455105530300150235ustar00rootroot00000000000000relic-7.6.1/signers/cab/signer.go000066400000000000000000000036241455105530300166460ustar00rootroot00000000000000// // Copyright (c) SAS Institute Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package cab // Sign Microsoft cabinet files import ( "io" "os" "github.com/sassoftware/relic/v7/lib/authenticode" "github.com/sassoftware/relic/v7/lib/cabfile" "github.com/sassoftware/relic/v7/lib/certloader" "github.com/sassoftware/relic/v7/lib/magic" "github.com/sassoftware/relic/v7/signers" "github.com/sassoftware/relic/v7/signers/pecoff" ) var CabSigner = &signers.Signer{ Name: "cab", Magic: magic.FileTypeCAB, CertTypes: signers.CertTypeX509, Sign: sign, Verify: verify, } func init() { pecoff.AddOpusFlags(CabSigner) signers.Register(CabSigner) } func sign(r io.Reader, cert *certloader.Certificate, opts signers.SignOpts) ([]byte, error) { digest, err := cabfile.Digest(r, opts.Hash) if err != nil { return nil, err } patch, ts, err := authenticode.SignCabImprint(opts.Context(), digest, cert, pecoff.OpusFlags(opts)) if err != nil { return nil, err } opts.Audit.SetCounterSignature(ts.CounterSignature) return opts.SetBinPatch(patch) } func verify(f *os.File, opts signers.VerifyOpts) ([]*signers.Signature, error) { sig, err := authenticode.VerifyCab(f, opts.NoDigests) if err != nil { return nil, err } return []*signers.Signature{{ Hash: sig.HashFunc, X509Signature: &sig.TimestampedSignature, SigInfo: pecoff.FormatOpus(sig.OpusInfo), }}, nil } relic-7.6.1/signers/cat/000077500000000000000000000000001455105530300150455ustar00rootroot00000000000000relic-7.6.1/signers/cat/signer.go000066400000000000000000000037031455105530300166660ustar00rootroot00000000000000// // Copyright (c) SAS Institute Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package cat // Sign Microsoft security catalog files import ( "errors" "io" "io/ioutil" "github.com/sassoftware/relic/v7/lib/authenticode" "github.com/sassoftware/relic/v7/lib/certloader" "github.com/sassoftware/relic/v7/lib/magic" "github.com/sassoftware/relic/v7/lib/pkcs7" "github.com/sassoftware/relic/v7/lib/pkcs9" "github.com/sassoftware/relic/v7/signers" "github.com/sassoftware/relic/v7/signers/pkcs" ) var CatSigner = &signers.Signer{ Name: "cat", Magic: magic.FileTypeCAT, CertTypes: signers.CertTypeX509, Sign: sign, Verify: pkcs.Verify, } func init() { signers.Register(CatSigner) } func sign(r io.Reader, cert *certloader.Certificate, opts signers.SignOpts) ([]byte, error) { blob, err := ioutil.ReadAll(r) if err != nil { return nil, err } oldpsd, err := pkcs7.Unmarshal(blob) if err != nil { return nil, err } if !oldpsd.Content.ContentInfo.ContentType.Equal(authenticode.OidCertTrustList) { return nil, errors.New("not a security catalog") } sig := pkcs7.NewBuilder(cert.Signer(), cert.Chain(), opts.Hash) if err := sig.SetContentInfo(oldpsd.Content.ContentInfo); err != nil { return nil, err } newpsd, err := sig.Sign() if err != nil { return nil, err } ts, err := pkcs9.TimestampAndMarshal(opts.Context(), newpsd, cert.Timestamper, true) if err != nil { return nil, err } return opts.SetPkcs7(ts) } relic-7.6.1/signers/cosign/000077500000000000000000000000001455105530300155605ustar00rootroot00000000000000relic-7.6.1/signers/cosign/digest.go000066400000000000000000000034041455105530300173670ustar00rootroot00000000000000package cosign import ( "crypto" "encoding/json" "errors" "fmt" "github.com/opencontainers/go-digest" oci "github.com/opencontainers/image-spec/specs-go/v1" ) var algorithms = map[crypto.Hash]digest.Algorithm{ crypto.SHA256: digest.SHA256, crypto.SHA384: digest.SHA384, crypto.SHA512: digest.SHA512, } // digest a payload and return it both formatted and raw func digestPayload(hash crypto.Hash, blob []byte) ([]byte, digest.Digest) { alg := algorithms[hash] if !alg.Available() { return nil, "" } digester := hash.New() digester.Write(blob) rawDigest := digester.Sum(nil) layerDigest := digest.NewDigestFromBytes(alg, rawDigest) return rawDigest, layerDigest } // legacy container media types const ( dockerImageType = "application/vnd.docker.distribution.manifest.v2+json" dockerListType = "application/vnd.docker.distribution.manifest.list.v2+json" ) var allowedManifestTypes = map[string]bool{ oci.MediaTypeImageManifest: true, oci.MediaTypeImageIndex: true, dockerImageType: true, dockerListType: true, } type objectWithMediaType struct { MediaType string `json:"mediaType"` } // return the digest and mediaType of a manifest func digestManifest(hash crypto.Hash, blob []byte) (digest.Digest, string, error) { alg := algorithms[hash] if !alg.Available() { return "", "", fmt.Errorf("unsupported digest %s", hash) } var mt objectWithMediaType if err := json.Unmarshal(blob, &mt); err != nil { return "", "", fmt.Errorf("unable to determine mediaType: %w", err) } if mt.MediaType == "" { return "", "", errors.New("unable to determine mediaType") } if !allowedManifestTypes[mt.MediaType] { return "", "", fmt.Errorf("mediaType %q cannot be signed", mt.MediaType) } return alg.FromBytes(blob), mt.MediaType, nil } relic-7.6.1/signers/cosign/payload.go000066400000000000000000000045501455105530300175440ustar00rootroot00000000000000package cosign import ( "encoding/json" "fmt" "github.com/opencontainers/go-digest" "github.com/sassoftware/relic/v7/config" "github.com/sassoftware/relic/v7/signers" ) const ( signatureType = "cosign container image signature" cosignPayloadMediaType = "application/vnd.dev.cosign.simplesigning.v1+json" cosignArtifactType = "application/vnd.dev.cosign.artifact.sig.v1+json" // annotations used to encode the signature signatureAnnotationKey = "dev.cosignproject.cosign/signature" certificateAnnotationKey = "dev.sigstore.cosign/certificate" chainAnnotationKey = "dev.sigstore.cosign/chain" rfc3161TimestampAnnotationKey = "dev.sigstore.cosign/rfc3161timestamp" ) func newPayload(manifestDigest digest.Digest, opts signers.SignOpts) ([]byte, error) { payload := simpleContainerImage{ Critical: critical{ Image: image{DockerManifestDigest: manifestDigest}, Type: signatureType, }, } // set optionals if optional := opts.Flags.GetString("optional"); optional != "" { if err := json.Unmarshal([]byte(optional), &payload.Optional); err != nil { return nil, fmt.Errorf("invalid annotations: %w", err) } } if payload.Optional == nil { payload.Optional = make(map[string]any) } payload.Optional["creator"] = config.UserAgent // record image digest in audit log opts.Audit.Attributes["cosign.manifest-digest"] = manifestDigest.String() return json.Marshal(payload) } // SimpleContainerImage describes the structure of a basic container image signature payload, as defined at: // https://github.com/containers/image/blob/master/docs/containers-signature.5.md#json-data-format type simpleContainerImage struct { Critical critical `json:"critical"` // Critical data critical to correctly evaluating the validity of the signature Optional map[string]any `json:"optional"` // Optional optional metadata about the image } // Critical data critical to correctly evaluating the validity of a signature type critical struct { Image image `json:"image"` // Image identifies the container that the signature applies to Type string `json:"type"` // Type must be 'atomic container signature' } // Image identifies the container image that the signature applies to type image struct { DockerManifestDigest digest.Digest `json:"docker-manifest-digest"` // DockerManifestDigest the manifest digest of the signed container image } relic-7.6.1/signers/cosign/signer.go000066400000000000000000000075731455105530300174120ustar00rootroot00000000000000package cosign import ( "crypto/rand" "encoding/base64" "encoding/json" "encoding/pem" "fmt" "io" "strings" "github.com/opencontainers/image-spec/specs-go" oci "github.com/opencontainers/image-spec/specs-go/v1" "github.com/sassoftware/relic/v7/lib/certloader" "github.com/sassoftware/relic/v7/lib/pkcs9" "github.com/sassoftware/relic/v7/signers" ) var signer = &signers.Signer{ Name: "cosign", CertTypes: signers.CertTypeX509, Sign: sign, } func init() { signer.Flags().String("optional", "", "extra JSON options to sign") signers.Register(signer) } func sign(r io.Reader, cert *certloader.Certificate, opts signers.SignOpts) ([]byte, error) { // Parse and digest image manifest const maxSize = 4 * 1024 * 1024 // spec recommends 4MiB maximum for a manifest manifestBlob, err := io.ReadAll(io.LimitReader(r, maxSize+1)) if err != nil { return nil, err } else if len(manifestBlob) > maxSize { return nil, fmt.Errorf("image manifest exceeds %d bytes", maxSize) } manifestDigest, manifestType, err := digestManifest(opts.Hash, manifestBlob) if err != nil { return nil, err } // Generate the signing payload payloadToSign, err := newPayload(manifestDigest, opts) if err != nil { return nil, err } // Sign payload rawDigest, layerDigest := digestPayload(opts.Hash, payloadToSign) rawSignature, err := cert.Signer().Sign(rand.Reader, rawDigest, opts.Hash) if err != nil { return nil, err } // Encode payload and signature into an OCI v1.1 manifest // https://github.com/opencontainers/image-spec/blob/main/manifest.md#guidelines-for-artifact-usage resp := oci.Manifest{ Versioned: specs.Versioned{SchemaVersion: 2}, MediaType: oci.MediaTypeImageManifest, ArtifactType: cosignArtifactType, Config: oci.ScratchDescriptor, // Subject is the OCI manifest (image) to which this artifact is attached Subject: &oci.Descriptor{ MediaType: manifestType, Digest: manifestDigest, Size: int64(len(manifestBlob)), }, Layers: []oci.Descriptor{{ // The signature payload is the contents of the layer MediaType: cosignPayloadMediaType, Digest: layerDigest, Size: int64(len(payloadToSign)), Data: payloadToSign, // The signature itself is stored in an annotation Annotations: map[string]string{ signatureAnnotationKey: base64.StdEncoding.EncodeToString(rawSignature), }, }}, } if err := attachTimestamp(&resp.Layers[0], cert, opts, rawSignature); err != nil { return nil, fmt.Errorf("timestamping failed: %w", err) } if err := attachCertificates(&resp.Layers[0], cert); err != nil { return nil, err } opts.Audit.SetMimeType(resp.MediaType) return json.Marshal(resp) } func attachCertificates(layer *oci.Descriptor, cert *certloader.Certificate) error { s := new(strings.Builder) b := &pem.Block{Type: "CERTIFICATE"} for i, certDER := range cert.Chain() { b.Bytes = certDER.Raw if err := pem.Encode(s, b); err != nil { return err } if i == 0 { // leaf layer.Annotations[certificateAnnotationKey] = s.String() s.Reset() } // otherwise keep appending certs to the buffer } if s.Len() != 0 { layer.Annotations[chainAnnotationKey] = s.String() } return nil } func attachTimestamp(layer *oci.Descriptor, cert *certloader.Certificate, opts signers.SignOpts, rawSignature []byte) error { if cert.Timestamper == nil { return nil } timestamp, err := cert.Timestamper.Timestamp(opts.Context(), &pkcs9.Request{ EncryptedDigest: rawSignature, Hash: opts.Hash, }) if err != nil { return err } rawTimestamp, err := timestamp.Marshal() if err != nil { return err } counterSig, err := pkcs9.Verify(timestamp, rawSignature, nil) if err != nil { return fmt.Errorf("timestamp failed signature self-check: %w", err) } opts.Audit.SetCounterSignature(counterSig) layer.Annotations[rfc3161TimestampAnnotationKey] = base64.StdEncoding.EncodeToString(rawTimestamp) return nil } relic-7.6.1/signers/deb/000077500000000000000000000000001455105530300150305ustar00rootroot00000000000000relic-7.6.1/signers/deb/signer.go000066400000000000000000000045301455105530300166500ustar00rootroot00000000000000// // Copyright (c) SAS Institute Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package deb // Sign Debian packages import ( "io" "os" "github.com/rs/zerolog" "github.com/sassoftware/relic/v7/lib/audit" "github.com/sassoftware/relic/v7/lib/certloader" "github.com/sassoftware/relic/v7/lib/magic" "github.com/sassoftware/relic/v7/lib/signdeb" "github.com/sassoftware/relic/v7/signers" "github.com/sassoftware/relic/v7/signers/sigerrors" ) var DebSigner = &signers.Signer{ Name: "deb", Magic: magic.FileTypeDEB, CertTypes: signers.CertTypePgp, FormatLog: formatLog, Sign: sign, Verify: verify, } func init() { DebSigner.Flags().StringP("role", "r", "builder", "(DEB) signing role: builder, origin, maint, archive") signers.Register(DebSigner) } func formatLog(attrs *audit.Info) *zerolog.Event { return attrs.AttrsForLog("deb.") } func sign(r io.Reader, cert *certloader.Certificate, opts signers.SignOpts) ([]byte, error) { role := opts.Flags.GetString("role") if role == "" { role = "builder" } sig, err := signdeb.Sign(r, cert.PgpKey, opts.Hash, role) if err != nil { return nil, err } opts.Audit.Attributes["deb.name"] = sig.Info.Package opts.Audit.Attributes["deb.version"] = sig.Info.Version opts.Audit.Attributes["deb.arch"] = sig.Info.Arch return opts.SetBinPatch(sig.PatchSet) } func verify(f *os.File, opts signers.VerifyOpts) ([]*signers.Signature, error) { sigmap, err := signdeb.Verify(f, opts.TrustedPgp, opts.NoDigests) if err != nil { return nil, err } if len(sigmap) == 0 { return nil, sigerrors.NotSignedError{Type: "DEB"} } var ret []*signers.Signature for role, sig := range sigmap { rsig := &signers.Signature{ SigInfo: role, CreationTime: sig.CreationTime, Hash: sig.Hash, } rsig.SignerPgp = sig.Key.Entity ret = append(ret, rsig) } return ret, nil } relic-7.6.1/signers/dmg/000077500000000000000000000000001455105530300150455ustar00rootroot00000000000000relic-7.6.1/signers/dmg/dmg.go000066400000000000000000000040331455105530300161430ustar00rootroot00000000000000package dmg import ( "fmt" "io" "os" "strings" "github.com/sassoftware/relic/v7/lib/certloader" "github.com/sassoftware/relic/v7/lib/fruit/dmg" "github.com/sassoftware/relic/v7/signers" ) var signer = &signers.Signer{ Name: "dmg", CertTypes: signers.CertTypeX509, TestPath: testPath, Verify: verify, Sign: sign, Transform: transform, } func init() { signer.Flags().String("bundle-id", "", "(Apple) app bundle ID") signer.Flags().String("requirements", "", "(Apple) requirements file to embed (binary only)") signers.Register(signer) } var fileArgs = []string{"requirements"} func testPath(s string) bool { return strings.HasSuffix(s, ".dmg") } func sign(r io.Reader, cert *certloader.Certificate, opts signers.SignOpts) ([]byte, error) { args, payload, err := extractFiles(r) if err != nil { return nil, err } params := &dmg.SignatureParams{ HashFunc: opts.Hash, SigningIdentity: opts.Flags.GetString("bundle-id"), } if v := args["requirements"]; v != nil { params.Requirements = v } udifBytes := args[udifName] patch, tsig, err := dmg.Sign(opts.Context(), udifBytes, payload, cert, params) if err != nil { return nil, err } opts.Audit.Attributes["mach-o.bundle-id"] = params.SigningIdentity opts.Audit.Attributes["mach-o.team-id"] = params.TeamIdentifier opts.Audit.SetCounterSignature(tsig.CounterSignature) return opts.SetBinPatch(patch) } func verify(f *os.File, opts signers.VerifyOpts) ([]*signers.Signature, error) { d, err := dmg.Open(f) if err != nil { return nil, err } sig, err := d.Verify(opts.NoDigests) if err != nil { return nil, err } si := sig.Blob.Directories[0].SigningIdentity if t := sig.Blob.Directories[0].TeamIdentifier; t != "" { si += fmt.Sprintf("[TeamID:%s]", t) } if len(sig.Blob.NotaryTicket) > 0 { si += "[HasNotaryTicket]" } for _, unk := range sig.Blob.Unknowns { si += fmt.Sprintf("[Unknown:%x.%x]", unk[:4], unk[4:8]) } return []*signers.Signature{{ Hash: sig.HashFunc, X509Signature: sig.Signature, SigInfo: si, }}, nil } relic-7.6.1/signers/dmg/transform.go000066400000000000000000000047201455105530300174120ustar00rootroot00000000000000package dmg import ( "archive/tar" "errors" "fmt" "io" "io/ioutil" "os" "github.com/sassoftware/relic/v7/signers" ) const ( udifName = "udifheader.bin" dmgName = "contents.dmg" ) func transform(f *os.File, opts signers.SignOpts) (signers.Transformer, error) { if _, err := f.Seek(-512, io.SeekEnd); err != nil { return nil, err } udifBytes := make([]byte, 512) if _, err := io.ReadFull(f, udifBytes); err != nil { return nil, err } t := &transformer{ f: f, files: []tarFile{{Name: udifName, Data: udifBytes}}, } for _, argName := range fileArgs { if fp := opts.Flags.GetString(argName); fp != "" { d, err := ioutil.ReadFile(fp) if err != nil { return nil, err } t.files = append(t.files, tarFile{argName, d}) } } return t, nil } type transformer struct { f *os.File files []tarFile } type tarFile struct { Name string Data []byte } func (t *transformer) GetReader() (io.Reader, error) { r, w := io.Pipe() go func() { _ = w.CloseWithError(t.send(w)) }() return r, nil } func (t *transformer) send(w io.Writer) error { tw := tar.NewWriter(w) // write extra files for _, f := range t.files { hdr := &tar.Header{Name: f.Name, Mode: 0644, Size: int64(len(f.Data))} if err := tw.WriteHeader(hdr); err != nil { return err } if _, err := tw.Write(f.Data); err != nil { return err } } // write binary size, err := t.f.Seek(0, io.SeekEnd) if err != nil { return err } hdr := &tar.Header{Name: dmgName, Mode: 0644, Size: size} if err := tw.WriteHeader(hdr); err != nil { return err } if _, err := t.f.Seek(0, 0); err != nil { return err } if _, err := io.Copy(tw, t.f); err != nil { return err } return tw.Close() } func (t *transformer) Apply(dest, mimeType string, result io.Reader) error { return signers.ApplyBinPatch(t.f, dest, result) } func extractFiles(r io.Reader) (args map[string][]byte, exec io.Reader, err error) { tr := tar.NewReader(r) args = make(map[string][]byte) files: for { hdr, err := tr.Next() if err == io.EOF { return nil, nil, errors.New("tar missing file \"exec\"") } else if err != nil { return nil, nil, err } if hdr.Name == dmgName { return args, tr, nil } for _, argName := range append(fileArgs, udifName) { if argName == hdr.Name { blob, err := ioutil.ReadAll(tr) if err != nil { return nil, nil, err } args[argName] = blob continue files } } return nil, nil, fmt.Errorf("unexpected tar file %q", hdr.Name) } } relic-7.6.1/signers/jar/000077500000000000000000000000001455105530300150525ustar00rootroot00000000000000relic-7.6.1/signers/jar/signer.go000066400000000000000000000055461455105530300167020ustar00rootroot00000000000000// // Copyright (c) SAS Institute Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package jar // Sign Java archives import ( "archive/zip" "io" "os" "github.com/sassoftware/relic/v7/lib/certloader" "github.com/sassoftware/relic/v7/lib/magic" "github.com/sassoftware/relic/v7/lib/signjar" "github.com/sassoftware/relic/v7/signers" "github.com/sassoftware/relic/v7/signers/zipbased" ) var JarSigner = &signers.Signer{ Name: "jar", Magic: magic.FileTypeJAR, CertTypes: signers.CertTypeX509, Transform: zipbased.Transform, Sign: sign, Verify: verify, } func init() { JarSigner.Flags().Bool("sections-only", false, "(JAR) Don't compute hash of entire manifest") JarSigner.Flags().Bool("inline-signature", false, "(JAR) Include .SF inside the signature block") JarSigner.Flags().Bool("apk-v2-present", false, "(JAR) Add X-Android-APK-Signed header to signature") JarSigner.Flags().String("key-alias", "RELIC", "(JAR, APK) Alias to use for the signed manifest") signers.Register(JarSigner) } // sign a manifest and return the PKCS#7 blob func sign(r io.Reader, cert *certloader.Certificate, opts signers.SignOpts) ([]byte, error) { argSectionsOnly := opts.Flags.GetBool("sections-only") argInlineSignature := opts.Flags.GetBool("inline-signature") argApkV2 := opts.Flags.GetBool("apk-v2-present") argAlias := opts.Flags.GetString("key-alias") if argAlias == "" { argAlias = "RELIC" } digest, err := signjar.DigestJarStream(r, opts.Hash) if err != nil { return nil, err } patch, ts, err := digest.Sign(opts.Context(), cert, argAlias, argSectionsOnly, argInlineSignature, argApkV2) if err != nil { return nil, err } opts.Audit.SetCounterSignature(ts.CounterSignature) return opts.SetBinPatch(patch) } func verify(f *os.File, opts signers.VerifyOpts) ([]*signers.Signature, error) { inz, err := openZip(f) if err != nil { return nil, err } sigs, err := signjar.Verify(inz, opts.NoDigests) if err != nil { return nil, err } var ret []*signers.Signature for _, ts := range sigs { ret = append(ret, &signers.Signature{ Hash: ts.Hash, X509Signature: &ts.TimestampedSignature, }) } return ret, nil } func openZip(f *os.File) (*zip.Reader, error) { size, err := f.Seek(0, io.SeekEnd) if err != nil { return nil, err } if _, err := f.Seek(0, 0); err != nil { return nil, err } return zip.NewReader(f, size) } relic-7.6.1/signers/macho/000077500000000000000000000000001455105530300153655ustar00rootroot00000000000000relic-7.6.1/signers/macho/fatfile.go000066400000000000000000000023261455105530300173310ustar00rootroot00000000000000package macho import ( "debug/macho" "fmt" "io" "os" "github.com/sassoftware/relic/v7/lib/magic" "github.com/sassoftware/relic/v7/signers" ) var fatVerifier = &signers.Signer{ Name: "mach-o-fat", Magic: magic.FileTypeMachOFat, CertTypes: signers.CertTypeX509, Verify: verifyFatFile, } func init() { signers.Register(fatVerifier) } func verifyFatFile(f *os.File, opts signers.VerifyOpts) ([]*signers.Signature, error) { return verifyFat(f, nil, nil, opts) } func verifyFat(fr io.ReaderAt, infoPlist, resources []byte, opts signers.VerifyOpts) ([]*signers.Signature, error) { fatFile, err := macho.NewFatFile(fr) if err == macho.ErrNotFat { sig, err := verifyMacho(fr, infoPlist, resources, opts) if err != nil { return nil, err } return []*signers.Signature{sig}, nil } else if err != nil { return nil, err } var sigs []*signers.Signature for _, arch := range fatFile.Arches { r := io.NewSectionReader(fr, int64(arch.Offset), int64(arch.Size)) sig, err := verifyMacho(r, nil, nil, opts) if err != nil { return nil, fmt.Errorf("%s.%d: %w", arch.Cpu, arch.SubCpu, err) } sig.Package = fmt.Sprintf("%s.%d", arch.Cpu, arch.SubCpu) sigs = append(sigs, sig) } return sigs, nil } relic-7.6.1/signers/macho/ipa.go000066400000000000000000000072211455105530300164670ustar00rootroot00000000000000package macho import ( "archive/zip" "errors" "fmt" "io" "os" "path" "strings" "howett.net/plist" "github.com/sassoftware/relic/v7/lib/magic" "github.com/sassoftware/relic/v7/signers" ) var ipaVerifier = &signers.Signer{ Name: "ipa", Magic: magic.FileTypeIPA, CertTypes: signers.CertTypeX509, Verify: verifyIPA, } func init() { signers.Register(ipaVerifier) } func verifyIPA(f *os.File, opts signers.VerifyOpts) ([]*signers.Signature, error) { size, err := f.Seek(0, io.SeekEnd) if err != nil { return nil, err } if _, err := f.Seek(0, 0); err != nil { return nil, err } zr, err := zip.NewReader(f, size) if err != nil { return nil, err } // find Info.plist appDir, plistBytes, err := readPlist(zr) if err != nil { return nil, err } var bundle bundlePlist if _, err := plist.Unmarshal(plistBytes, &bundle); err != nil { return nil, err } if bundle.Executable == "" { return nil, errors.New("plist: CFBundleExecutable is missing") } // find resource manifest resources, err := readResources(zr, appDir) if err != nil { return nil, err } // find tickets notaryTicket, masTicket, err := findTickets(zr, appDir) if err != nil { return nil, err } // extract executable to temp file fe, err := os.CreateTemp("", "") if err != nil { return nil, err } defer os.Remove(fe.Name()) defer fe.Close() if strings.HasSuffix(appDir, "/Contents") { appDir = path.Join(appDir, "MacOS") } if err := extractExecutable(zr, fe, path.Join(appDir, bundle.Executable)); err != nil { return nil, err } // digest executable sigs, err := verifyFat(fe, plistBytes, resources, opts) for _, sig := range sigs { if len(notaryTicket) > 0 { sig.SigInfo += "[HasNotaryTicket]" } if len(masTicket) > 0 { sig.SigInfo += "[HasMASTicket]" } } return sigs, err } func readZipFile(zf *zip.File) ([]byte, error) { f, err := zf.Open() if err != nil { return nil, err } d, err := io.ReadAll(f) if err != nil { return nil, err } return d, f.Close() } func readPlist(zr *zip.Reader) (string, []byte, error) { var plist *zip.File for _, zf := range zr.File { if path.Base(zf.Name) != "Info.plist" { continue } parts := strings.Split(path.Clean(zf.Name), "/") if parts[0] == "Payload" { parts = parts[1:] } if len(parts) > 3 || !strings.HasSuffix(parts[0], ".app") { continue } if len(parts) == 3 && parts[1] != "Contents" { continue } plist = zf break } if plist == nil { return "", nil, fmt.Errorf("info.plist: %w", os.ErrNotExist) } plistBytes, err := readZipFile(plist) return path.Dir(plist.Name), plistBytes, err } func readResources(zr *zip.Reader, appDir string) ([]byte, error) { fp := path.Join(appDir, "_CodeSignature", "CodeResources") for _, zf := range zr.File { if zf.Name == fp { return readZipFile(zf) } } return nil, fmt.Errorf("%s: %w", fp, os.ErrNotExist) } func extractExecutable(zr *zip.Reader, w io.Writer, fp string) error { for _, zf := range zr.File { if zf.Name == fp { f, err := zf.Open() if err != nil { return err } if _, err := io.Copy(w, f); err != nil { return err } return f.Close() } } return fmt.Errorf("%s: %w", fp, os.ErrNotExist) } func findTickets(zr *zip.Reader, appDir string) (notaryTicket, masTicket []byte, err error) { ntName := appDir + "/CodeResources" masName := appDir + "/_MASReceipt/receipt" for _, zf := range zr.File { switch zf.Name { case ntName: notaryTicket, err = readZipFile(zf) if err != nil { return } case masName: masTicket, err = readZipFile(zf) if err != nil { return } } } return } type bundlePlist struct { Executable string `plist:"CFBundleExecutable"` } relic-7.6.1/signers/macho/macho.go000066400000000000000000000057771455105530300170230ustar00rootroot00000000000000package macho import ( "fmt" "io" "os" "github.com/sassoftware/relic/v7/lib/certloader" "github.com/sassoftware/relic/v7/lib/fruit/csblob" "github.com/sassoftware/relic/v7/lib/fruit/machos" "github.com/sassoftware/relic/v7/lib/magic" "github.com/sassoftware/relic/v7/signers" ) var signer = &signers.Signer{ Name: "mach-o", Magic: magic.FileTypeMachO, CertTypes: signers.CertTypeX509, Transform: transform, Sign: sign, Verify: verifyMachoFile, } func init() { signer.Flags().String("bundle-id", "", "(Apple) app bundle ID") signer.Flags().String("info-plist", "", "(Apple) Info.plist file to bind to the signature") signer.Flags().String("entitlements", "", "(Apple) entitlements file to embed") signer.Flags().Bool("hardened-runtime", false, "(Apple) enable hardened runtime") signer.Flags().String("requirements", "", "(Apple) requirements file to embed (binary only)") signer.Flags().String("resources", "", "(Apple) CodeResources file to bind to the signature") signers.Register(signer) } var fileArgs = []string{"info-plist", "entitlements", "requirements", "resources"} func sign(r io.Reader, cert *certloader.Certificate, opts signers.SignOpts) ([]byte, error) { args, exec, err := extractFiles(r) if err != nil { return nil, err } params := &csblob.SignatureParams{ HashFunc: opts.Hash, SigningIdentity: opts.Flags.GetString("bundle-id"), } if v := args["info-plist"]; v != nil { params.InfoPlist = v } if v := args["entitlements"]; v != nil { params.Entitlement = v } if v := args["requirements"]; v != nil { params.Requirements = v } if v := args["resources"]; v != nil { params.Resources = v } if opts.Flags.GetBool("hardened-runtime") { params.Flags |= csblob.FlagRuntime } patch, tsig, err := machos.Sign(opts.Context(), exec, cert, params) if err != nil { return nil, err } opts.Audit.Attributes["mach-o.bundle-id"] = params.SigningIdentity opts.Audit.Attributes["mach-o.team-id"] = params.TeamIdentifier opts.Audit.SetCounterSignature(tsig.CounterSignature) return opts.SetBinPatch(patch) } func verifyMachoFile(f *os.File, opts signers.VerifyOpts) ([]*signers.Signature, error) { sig, err := verifyMacho(f, nil, nil, opts) if err != nil { return nil, err } return []*signers.Signature{sig}, nil } func verifyMacho(r io.ReaderAt, infoPlist, resources []byte, opts signers.VerifyOpts) (*signers.Signature, error) { sig, err := machos.Verify(r, infoPlist, resources, opts.NoDigests) if err != nil { return nil, err } si := sig.Blob.Directories[0].SigningIdentity if t := sig.Blob.Directories[0].TeamIdentifier; t != "" { si += fmt.Sprintf("[TeamID:%s]", t) } if len(sig.Blob.NotaryTicket) > 0 { si += "[HasNotaryTicket]" } if sig.Blob.Directories[0].Header.Flags&csblob.FlagRuntime != 0 { si += "[HardenedRuntime]" } for _, unk := range sig.Blob.Unknowns { si += fmt.Sprintf("[Unknown:%x.%x]", unk[:4], unk[4:8]) } return &signers.Signature{ Hash: sig.HashFunc, X509Signature: sig.Signature, SigInfo: si, }, nil } relic-7.6.1/signers/macho/transform.go000066400000000000000000000043241455105530300177320ustar00rootroot00000000000000package macho import ( "archive/tar" "errors" "fmt" "io" "io/ioutil" "os" "github.com/sassoftware/relic/v7/signers" ) func transform(f *os.File, opts signers.SignOpts) (signers.Transformer, error) { // this transformer packs extra files specified on the cmdline into a tarball t := &transformer{f: f} for _, argName := range fileArgs { if fp := opts.Flags.GetString(argName); fp != "" { d, err := ioutil.ReadFile(fp) if err != nil { return nil, err } t.files = append(t.files, tarFile{argName, d}) } } return t, nil } type transformer struct { f *os.File files []tarFile } type tarFile struct { Name string Data []byte } func (t *transformer) GetReader() (io.Reader, error) { r, w := io.Pipe() go func() { _ = w.CloseWithError(t.send(w)) }() return r, nil } func (t *transformer) send(w io.Writer) error { tw := tar.NewWriter(w) // write extra files for _, f := range t.files { hdr := &tar.Header{Name: f.Name, Mode: 0644, Size: int64(len(f.Data))} if err := tw.WriteHeader(hdr); err != nil { return err } if _, err := tw.Write(f.Data); err != nil { return err } } // write binary size, err := t.f.Seek(0, io.SeekEnd) if err != nil { return err } hdr := &tar.Header{Name: "exec", Mode: 0755, Size: size} if err := tw.WriteHeader(hdr); err != nil { return err } if _, err := t.f.Seek(0, 0); err != nil { return err } if _, err := io.Copy(tw, t.f); err != nil { return err } return tw.Close() } func (t *transformer) Apply(dest, mimeType string, result io.Reader) error { return signers.ApplyBinPatch(t.f, dest, result) } func extractFiles(r io.Reader) (args map[string][]byte, exec io.Reader, err error) { tr := tar.NewReader(r) args = make(map[string][]byte) files: for { hdr, err := tr.Next() if err == io.EOF { return nil, nil, errors.New("tar missing file \"exec\"") } else if err != nil { return nil, nil, err } if hdr.Name == "exec" { return args, tr, nil } for _, argName := range fileArgs { if argName == hdr.Name { blob, err := ioutil.ReadAll(tr) if err != nil { return nil, nil, err } args[argName] = blob continue files } } return nil, nil, fmt.Errorf("unexpected tar file %q", hdr.Name) } } relic-7.6.1/signers/msi/000077500000000000000000000000001455105530300150665ustar00rootroot00000000000000relic-7.6.1/signers/msi/signer.go000066400000000000000000000067301455105530300167120ustar00rootroot00000000000000// // Copyright (c) SAS Institute Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package msi // Sign Microsoft Installer files import ( "io" "io/ioutil" "os" "github.com/sassoftware/relic/v7/lib/atomicfile" "github.com/sassoftware/relic/v7/lib/authenticode" "github.com/sassoftware/relic/v7/lib/certloader" "github.com/sassoftware/relic/v7/lib/comdoc" "github.com/sassoftware/relic/v7/lib/magic" "github.com/sassoftware/relic/v7/signers" "github.com/sassoftware/relic/v7/signers/pecoff" ) var MsiSigner = &signers.Signer{ Name: "msi", Aliases: []string{"msi-tar"}, Magic: magic.FileTypeMSI, CertTypes: signers.CertTypeX509, Transform: transform, Sign: sign, Verify: verify, } func init() { MsiSigner.Flags().Bool("no-extended-sig", false, "(MSI) Don't emit a MsiDigitalSignatureEx digest") pecoff.AddOpusFlags(MsiSigner) signers.Register(MsiSigner) } type msiTransformer struct { f *os.File cdf *comdoc.ComDoc exsig []byte } func transform(f *os.File, opts signers.SignOpts) (signers.Transformer, error) { cdf, err := comdoc.ReadFile(f) if err != nil { return nil, err } var exsig []byte noExtended := opts.Flags.GetBool("no-extended-sig") if !noExtended { exsig, err = authenticode.PrehashMSI(cdf, opts.Hash) if err != nil { return nil, err } } return &msiTransformer{f, cdf, exsig}, nil } // transform the MSI to a tar stream for upload func (t *msiTransformer) GetReader() (io.Reader, error) { r, w := io.Pipe() go func() { _ = w.CloseWithError(authenticode.MsiToTar(t.cdf, w)) }() return r, nil } // apply a signed PKCS#7 blob to an already-open MSI document func (t *msiTransformer) Apply(dest, mimeType string, result io.Reader) error { t.cdf.Close() blob, err := ioutil.ReadAll(result) if err != nil { return err } // copy src to dest if needed, otherwise open in-place f, err := atomicfile.WriteInPlace(t.f, dest) if err != nil { return err } defer f.Close() cdf, err := comdoc.WriteFile(f.GetFile()) if err != nil { return err } if err := authenticode.InsertMSISignature(cdf, blob, t.exsig); err != nil { return err } if err := cdf.Close(); err != nil { return err } return f.Commit() } // sign a transformed tarball and return the PKCS#7 blob func sign(r io.Reader, cert *certloader.Certificate, opts signers.SignOpts) ([]byte, error) { noExtended := opts.Flags.GetBool("no-extended-sig") sum, err := authenticode.DigestMsiTar(r, opts.Hash, !noExtended) if err != nil { return nil, err } ts, err := authenticode.SignMSIImprint(opts.Context(), sum, opts.Hash, cert, pecoff.OpusFlags(opts)) if err != nil { return nil, err } return opts.SetPkcs7(ts) } func verify(f *os.File, opts signers.VerifyOpts) ([]*signers.Signature, error) { sig, err := authenticode.VerifyMSI(f, opts.NoDigests) if err != nil { return nil, err } return []*signers.Signature{{ Hash: sig.HashFunc, X509Signature: &sig.TimestampedSignature, SigInfo: pecoff.FormatOpus(sig.OpusInfo), }}, nil } relic-7.6.1/signers/options.go000066400000000000000000000110551455105530300163220ustar00rootroot00000000000000// // Copyright (c) SAS Institute Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package signers import ( "context" "crypto" "crypto/x509" "fmt" "net/url" "strconv" "time" "github.com/spf13/pflag" "golang.org/x/crypto/openpgp" "github.com/sassoftware/relic/v7/lib/audit" "github.com/sassoftware/relic/v7/lib/binpatch" "github.com/sassoftware/relic/v7/lib/magic" "github.com/sassoftware/relic/v7/lib/pkcs7" "github.com/sassoftware/relic/v7/lib/pkcs9" ) var common *pflag.FlagSet func init() { common = pflag.NewFlagSet("common", pflag.ExitOnError) common.Bool("no-timestamp", false, "Do not attach a trusted timestamp even if the selected key configures one") } type SignOpts struct { Path string Hash crypto.Hash Time time.Time Flags *FlagValues Audit *audit.Info ctx context.Context } // Convenience method to return a binary patch func (o SignOpts) SetBinPatch(p *binpatch.PatchSet) ([]byte, error) { o.Audit.SetMimeType(binpatch.MimeType) return p.Dump(), nil } // Convenience method to return a PKCS#7 blob func (o SignOpts) SetPkcs7(ts *pkcs9.TimestampedSignature) ([]byte, error) { o.Audit.SetCounterSignature(ts.CounterSignature) o.Audit.SetMimeType(pkcs7.MimeType) return ts.Raw, nil } // WithContext attaches a context to the signature operation, and can be used to cancel long-running operations. func (o SignOpts) WithContext(ctx context.Context) SignOpts { o.ctx = ctx return o } // Context returns the context attached to the signature operation. // // The returned context is always non-nil; it defaults to the background context. func (o SignOpts) Context() context.Context { if o.ctx != nil { return o.ctx } return context.Background() } type VerifyOpts struct { FileName string TrustedX509 []*x509.Certificate TrustedPgp openpgp.EntityList TrustedPool *x509.CertPool NoDigests bool NoChain bool Content string Compression magic.CompressionType } type FlagValues struct { Defs *pflag.FlagSet Values map[string]string } func (v *FlagValues) mergeAll(defs *pflag.FlagSet, getter func(string) string) { if defs != nil { v.mergeSet(defs, getter) } v.mergeSet(common, getter) } func (v *FlagValues) mergeSet(defs *pflag.FlagSet, getter func(string) string) { defs.VisitAll(func(flag *pflag.Flag) { value := getter(flag.Name) if value != "" { v.Values[flag.Name] = value } }) } // FlagsFromCmdline creates a FlagValues from the (merged) command-line options of a command func (s *Signer) FlagsFromCmdline(fs *pflag.FlagSet) (*FlagValues, error) { for flag, users := range flagMap { if !fs.Changed(flag) { continue } allowed := false for _, name := range users { if name == s.Name { allowed = true break } } if !allowed { return nil, fmt.Errorf("flag \"%s\" is not allowed for signature type \"%s\"", flag, s.Name) } } values := &FlagValues{ Defs: s.flags, Values: make(map[string]string), } values.mergeAll(s.flags, func(name string) string { if !fs.Changed(name) { return "" } return fs.Lookup(name).Value.String() }) return values, nil } // FlagsFromQuery creates a FlagValues from URL query parameters func (s *Signer) FlagsFromQuery(q url.Values) (*FlagValues, error) { values := &FlagValues{ Defs: s.flags, Values: make(map[string]string), } values.mergeAll(s.flags, q.Get) return values, nil } // ToQuery appends query parameters to a URL for each option in the flag set func (values *FlagValues) ToQuery(q url.Values) error { for key, value := range values.Values { q.Set(key, value) } return nil } // GetString returns the flag's value as a string func (values *FlagValues) GetString(name string) string { flag := common.Lookup(name) if flag == nil && values.Defs != nil { flag = values.Defs.Lookup(name) } if flag == nil { panic("flag " + name + " not defined for signer module") } if v, ok := values.Values[name]; ok { return v } return flag.DefValue } // GetBool returns the flag's value as a bool func (values *FlagValues) GetBool(name string) bool { str := values.GetString(name) b, _ := strconv.ParseBool(str) return b } relic-7.6.1/signers/pecoff/000077500000000000000000000000001455105530300155405ustar00rootroot00000000000000relic-7.6.1/signers/pecoff/signer.go000066400000000000000000000055711455105530300173660ustar00rootroot00000000000000// // Copyright (c) SAS Institute Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package pecoff // Sign Microsoft PE/COFF executables import ( "fmt" "io" "os" "strings" "github.com/sassoftware/relic/v7/lib/authenticode" "github.com/sassoftware/relic/v7/lib/certloader" "github.com/sassoftware/relic/v7/lib/magic" "github.com/sassoftware/relic/v7/signers" ) var PeSigner = &signers.Signer{ Name: "pe-coff", Magic: magic.FileTypePECOFF, CertTypes: signers.CertTypeX509, Sign: sign, Fixup: authenticode.FixPEChecksum, Verify: verify, } func init() { PeSigner.Flags().Bool("page-hashes", false, "(PE-COFF) Add page hashes to signature") AddOpusFlags(PeSigner) signers.Register(PeSigner) } func AddOpusFlags(s *signers.Signer) { s.Flags().String("description", "", "(Win) Set description of signed content") s.Flags().String("desc-url", "", "(Win) Set URL for description of signed content") } func OpusFlags(opts signers.SignOpts) *authenticode.OpusParams { return &authenticode.OpusParams{ Description: opts.Flags.GetString("description"), URL: opts.Flags.GetString("desc-url"), } } func sign(r io.Reader, cert *certloader.Certificate, opts signers.SignOpts) ([]byte, error) { pageHashes := opts.Flags.GetBool("page-hashes") digest, err := authenticode.DigestPE(r, opts.Hash, pageHashes) if err != nil { return nil, err } patch, ts, err := digest.Sign(opts.Context(), cert, OpusFlags(opts)) if err != nil { return nil, err } opts.Audit.Attributes["pe-coff.pagehashes"] = pageHashes opts.Audit.SetCounterSignature(ts.CounterSignature) return opts.SetBinPatch(patch) } func FormatOpus(info *authenticode.SpcSpOpusInfo) string { if info == nil { return "" } var infos []string if desc := info.ProgramName.String(); desc != "" { infos = append(infos, fmt.Sprintf("[desc:%q]", desc)) } if u := info.MoreInfo.URL; u != "" { infos = append(infos, fmt.Sprintf("[url:%q]", u)) } return strings.Join(infos, "") } func verify(f *os.File, opts signers.VerifyOpts) ([]*signers.Signature, error) { sigs, err := authenticode.VerifyPE(f, opts.NoDigests) if err != nil { return nil, err } var ret []*signers.Signature for _, sig := range sigs { ret = append(ret, &signers.Signature{ SigInfo: FormatOpus(sig.OpusInfo), Hash: sig.ImageHashFunc, X509Signature: &sig.TimestampedSignature, }) } return ret, nil } relic-7.6.1/signers/pgp/000077500000000000000000000000001455105530300150645ustar00rootroot00000000000000relic-7.6.1/signers/pgp/pgpcmd.go000066400000000000000000000066041455105530300166730ustar00rootroot00000000000000// // Copyright (c) SAS Institute Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package pgp // Implementation for the "relic sign-pgp" and "relic remote sign-pgp" // commands, that sort of looks like gpg arguments so it can be used where gpg // is. This just transforms the "compatible" arguments into an ordinary sign // command and calls it. import ( "errors" "strings" "github.com/sassoftware/relic/v7/cmdline/shared" "github.com/spf13/cobra" "github.com/spf13/pflag" ) var ( argDigest string argOutput string argPgpUser string argPgpArmor bool argPgpDetached bool argPgpClearsign bool argPgpTextMode bool ) func AddCompatFlags(cmd *cobra.Command) { flags := cmd.Flags() flags.StringVarP(&argPgpUser, "local-user", "u", "", "Specify keyname or cfgfile:keyname") flags.StringVarP(&argOutput, "output", "o", "", "Write output to file") flags.BoolVarP(&argPgpArmor, "armor", "a", false, "Create ASCII armored output") flags.BoolVarP(&argPgpTextMode, "textmode", "t", false, "Sign in CRLF canonical text form") flags.BoolVarP(&argPgpDetached, "detach-sign", "b", false, "Create a detached signature") flags.BoolVar(&argPgpClearsign, "clearsign", false, "Create a cleartext signature") flags.StringVar(&argDigest, "digest-algo", "", "Digest algorithm") flags.BoolP("sign", "s", false, "(ignored)") flags.BoolP("verbose", "v", false, "(ignored)") flags.Bool("no-armor", false, "(ignored)") flags.Bool("no-verbose", false, "(ignored)") flags.BoolP("quiet", "q", false, "(ignored)") flags.Bool("no-secmem-warning", false, "(ignored)") flags.String("status-fd", "", "(ignored)") flags.String("logger-fd", "", "(ignored)") flags.String("attribute-fd", "", "(ignored)") } func CallCmd(src, dest *cobra.Command, args []string) error { if argPgpUser == "" { return errors.New("-u must be set to a keyname or cfgpath:keyname") } setFlag(dest.Flags(), "sig-type", "pgp") idx := strings.LastIndex(argPgpUser, ":") if idx <= 0 { setFlag(dest.Flags(), "key", argPgpUser) } else { setFlag(shared.RootCmd.PersistentFlags(), "config", argPgpUser[:idx]) setFlag(dest.Flags(), "key", argPgpUser[idx+1:]) } if len(args) == 0 { setFlag(dest.Flags(), "file", "-") } else if len(args) == 1 { setFlag(dest.Flags(), "file", args[0]) } else { return errors.New("expected 0 or 1 argument") } if argOutput == "" { argOutput = "-" } setFlag(dest.Flags(), "output", argOutput) if argPgpArmor { setFlag(dest.Flags(), "armor", "true") } if argPgpTextMode { setFlag(dest.Flags(), "textmode", "true") } if argPgpClearsign { setFlag(dest.Flags(), "clearsign", "true") } else if !argPgpDetached { setFlag(dest.Flags(), "inline", "true") } if argDigest != "" { setFlag(dest.Flags(), "digest", argDigest) } return dest.RunE(dest, []string{}) } func setFlag(flags *pflag.FlagSet, name, value string) { if err := flags.Set(name, value); err != nil { panic(err) } } relic-7.6.1/signers/pgp/signer.go000066400000000000000000000137201455105530300167050ustar00rootroot00000000000000// // Copyright (c) SAS Institute Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package pgp // Sign arbitrary data using PGP detached or cleartext signatures import ( "bufio" "bytes" "errors" "fmt" "io" "io/ioutil" "os" "path/filepath" "time" "golang.org/x/crypto/openpgp" "golang.org/x/crypto/openpgp/armor" "golang.org/x/crypto/openpgp/packet" "github.com/sassoftware/relic/v7/lib/atomicfile" "github.com/sassoftware/relic/v7/lib/certloader" "github.com/sassoftware/relic/v7/lib/magic" "github.com/sassoftware/relic/v7/lib/pgptools" "github.com/sassoftware/relic/v7/signers" ) var PgpSigner = &signers.Signer{ Name: "pgp", Magic: magic.FileTypePGP, CertTypes: signers.CertTypePgp, AllowStdin: true, Transform: transform, Sign: sign, VerifyStream: verify, } const maxStreamClearSignSize = 10 * 1000 * 1000 func init() { PgpSigner.Flags().BoolP("armor", "a", false, "(PGP) Create ASCII armored output") PgpSigner.Flags().Bool("inline", false, "(PGP) Create a signed message instead of a detached signature") PgpSigner.Flags().Bool("clearsign", false, "(PGP) Create a cleartext signature") PgpSigner.Flags().BoolP("textmode", "t", false, "(PGP) Sign in CRLF canonical text form") // for compat with 2.0 clients PgpSigner.Flags().String("pgp", "", "") _ = PgpSigner.Flags().MarkHidden("pgp") signers.Register(PgpSigner) } type pgpTransformer struct { inline, clearsign, armor bool filename string stream io.ReadSeeker closer io.Closer } func transform(f *os.File, opts signers.SignOpts) (signers.Transformer, error) { armor := opts.Flags.GetBool("armor") inline := opts.Flags.GetBool("inline") if inline { // always get a non-armored sig from the server opts.Flags.Values["armor"] = "false" } clearsign := opts.Flags.GetBool("clearsign") stream := io.ReadSeeker(f) if _, err := f.Seek(0, 0); err != nil { // not seekable so consume it all now contents, err := ioutil.ReadAll(io.LimitReader(stream, maxStreamClearSignSize)) if err != nil { return nil, err } else if len(contents) == maxStreamClearSignSize { return nil, errors.New("input stream is too big, try writing it to file first") } stream = bytes.NewReader(contents) } return &pgpTransformer{ inline: inline, clearsign: clearsign, armor: armor, filename: filepath.Base(f.Name()), stream: stream, closer: f, }, nil } func (t *pgpTransformer) GetReader() (io.Reader, error) { if _, err := t.stream.Seek(0, 0); err != nil { return nil, err } return t.stream, nil } func sign(r io.Reader, cert *certloader.Certificate, opts signers.SignOpts) ([]byte, error) { armor := opts.Flags.GetBool("armor") clearsign := opts.Flags.GetBool("clearsign") textmode := opts.Flags.GetBool("textmode") if pgpcompat := opts.Flags.GetString("pgp"); pgpcompat == "mini-clear" { clearsign = true } var sf func(io.Writer, *openpgp.Entity, io.Reader, *packet.Config) error if clearsign { sf = pgptools.DetachClearSign } else if armor { if textmode { sf = openpgp.ArmoredDetachSignText } else { sf = openpgp.ArmoredDetachSign } } else { if textmode { sf = openpgp.DetachSignText } else { sf = openpgp.DetachSign } } var buf bytes.Buffer config := &packet.Config{ DefaultHash: opts.Hash, Time: func() time.Time { return opts.Time }, } if err := sf(&buf, cert.PgpKey, r, config); err != nil { return nil, err } else if armor { buf.WriteByte('\n') } return buf.Bytes(), nil } func (t *pgpTransformer) Apply(dest, mimeType string, result io.Reader) error { outfile, err := atomicfile.WriteAny(dest) if err != nil { return err } defer outfile.Close() if t.inline || t.clearsign { // reassemble signature if _, err := t.stream.Seek(0, 0); err != nil { return err } sig, err := ioutil.ReadAll(result) if err != nil { return err } if t.clearsign { err = pgptools.MergeClearSign(outfile, sig, t.stream) } else { err = pgptools.MergeSignature(outfile, sig, t.stream, t.armor, t.filename) } if err != nil { return err } } else { if _, err := io.Copy(outfile, result); err != nil { return err } } t.closer.Close() return outfile.Commit() } func verify(r io.Reader, opts signers.VerifyOpts) ([]*signers.Signature, error) { br := bufio.NewReader(r) // remove ASCII armor reader := io.Reader(br) if x, _ := br.Peek(34); len(x) >= 1 && x[0] == '-' { if bytes.HasPrefix(x, []byte("-----BEGIN PGP SIGNED MESSAGE-----")) { // clearsign sig, err := pgptools.VerifyClearSign(reader, nil, opts.TrustedPgp) return verifyPgp(sig, opts.FileName, err) } block, err := armor.Decode(reader) if err != nil { return nil, err } reader = block.Body } if opts.Content != "" { // detached signature fc, err := os.Open(opts.Content) if err != nil { return nil, err } defer fc.Close() sig, err := pgptools.VerifyDetached(reader, fc, opts.TrustedPgp) return verifyPgp(sig, opts.FileName, err) } // inline signature sig, err := pgptools.VerifyInline(reader, nil, opts.TrustedPgp) return verifyPgp(sig, opts.FileName, err) } func verifyPgp(sig *pgptools.PgpSignature, name string, err error) ([]*signers.Signature, error) { if err == nil { return []*signers.Signature{{ CreationTime: sig.CreationTime, Hash: sig.Hash, SignerPgp: sig.Key.Entity, }}, nil } else if sig != nil { return nil, fmt.Errorf("bad signature from %s(%x) [%s]: %w", pgptools.EntityName(sig.Key.Entity), sig.Key.PublicKey.KeyId, sig.CreationTime, err) } return nil, err } relic-7.6.1/signers/pkcs/000077500000000000000000000000001455105530300152365ustar00rootroot00000000000000relic-7.6.1/signers/pkcs/timestamp.go000066400000000000000000000036421455105530300175750ustar00rootroot00000000000000// // Copyright (c) SAS Institute Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package pkcs // Verify PKCS#7 SignedData structures. import ( "io/ioutil" "os" "github.com/sassoftware/relic/v7/lib/magic" "github.com/sassoftware/relic/v7/lib/pkcs7" "github.com/sassoftware/relic/v7/lib/pkcs9" "github.com/sassoftware/relic/v7/lib/x509tools" "github.com/sassoftware/relic/v7/signers" ) var PkcsSigner = &signers.Signer{ Name: "pkcs7", Magic: magic.FileTypePKCS7, CertTypes: signers.CertTypeX509, Sign: nil, Verify: Verify, } func init() { PkcsSigner.Flags().String("content", "", "Specify file containing contents for detached signatures") signers.Register(PkcsSigner) } func Verify(f *os.File, opts signers.VerifyOpts) ([]*signers.Signature, error) { blob, err := ioutil.ReadAll(f) if err != nil { return nil, err } psd, err := pkcs7.Unmarshal(blob) if err != nil { return nil, err } var cblob []byte if !opts.NoDigests && opts.Content != "" { cblob, err = ioutil.ReadFile(opts.Content) if err != nil { return nil, err } } sig, err := psd.Content.Verify(cblob, opts.NoDigests) if err != nil { return nil, err } ts, err := pkcs9.VerifyOptionalTimestamp(sig) if err != nil { return nil, err } hash, _ := x509tools.PkixDigestToHash(ts.SignerInfo.DigestAlgorithm) return []*signers.Signature{&signers.Signature{ Hash: hash, X509Signature: &ts, }}, nil } relic-7.6.1/signers/ps/000077500000000000000000000000001455105530300147205ustar00rootroot00000000000000relic-7.6.1/signers/ps/signer.go000066400000000000000000000057531455105530300165500ustar00rootroot00000000000000// // Copyright (c) SAS Institute Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package ps // Sign Microsoft PowerShell scripts, modules, and other bits that can be signed import ( "errors" "io" "os" "path/filepath" "strings" "github.com/sassoftware/relic/v7/lib/authenticode" "github.com/sassoftware/relic/v7/lib/certloader" "github.com/sassoftware/relic/v7/lib/x509tools" "github.com/sassoftware/relic/v7/signers" "github.com/sassoftware/relic/v7/signers/pecoff" ) var PsSigner = &signers.Signer{ Name: "ps", CertTypes: signers.CertTypeX509, TestPath: testPath, Transform: transform, Sign: sign, Verify: verify, } func init() { PsSigner.Flags().String("ps-style", "", "(Powershell) signature type") pecoff.AddOpusFlags(PsSigner) signers.Register(PsSigner) } func testPath(fp string) bool { _, ok := authenticode.GetSigStyle(fp) return ok } func transform(f *os.File, opts signers.SignOpts) (signers.Transformer, error) { // detect signature style and explicitly set it for the request argStyle := opts.Flags.GetString("ps-style") if argStyle == "" { argStyle = filepath.Ext(opts.Path) } opts.Flags.Values["ps-style"] = argStyle return signers.DefaultTransform(f), nil } func sign(r io.Reader, cert *certloader.Certificate, opts signers.SignOpts) ([]byte, error) { argStyle := opts.Flags.GetString("ps-style") if argStyle == "" { argStyle = opts.Path } style, err := getStyle(argStyle) if err != nil { return nil, err } digest, err := authenticode.DigestPowershell(r, style, opts.Hash) if err != nil { return nil, err } patch, ts, err := digest.Sign(opts.Context(), cert, pecoff.OpusFlags(opts)) if err != nil { return nil, err } opts.Audit.SetCounterSignature(ts.CounterSignature) return opts.SetBinPatch(patch) } func verify(f *os.File, opts signers.VerifyOpts) ([]*signers.Signature, error) { style, err := getStyle(f.Name()) if err != nil { return nil, err } ts, err := authenticode.VerifyPowershell(f, style, opts.NoDigests) if err != nil { return nil, err } hash, _ := x509tools.PkixDigestToHash(ts.SignerInfo.DigestAlgorithm) return []*signers.Signature{{ Hash: hash, X509Signature: &ts.TimestampedSignature, SigInfo: pecoff.FormatOpus(ts.OpusInfo), }}, nil } func getStyle(name string) (authenticode.PsSigStyle, error) { style, ok := authenticode.GetSigStyle(name) if !ok { return 0, errors.New("unknown powershell style, expected: " + strings.Join(authenticode.AllSigStyles(), " ")) } return style, nil } relic-7.6.1/signers/rpm/000077500000000000000000000000001455105530300150745ustar00rootroot00000000000000relic-7.6.1/signers/rpm/signer.go000066400000000000000000000063351455105530300167210ustar00rootroot00000000000000// // Copyright (c) SAS Institute Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package rpm // Sign RedHat packages import ( "encoding/hex" "fmt" "io" "os" "strings" "time" "github.com/rs/zerolog" rpmutils "github.com/sassoftware/go-rpmutils" "github.com/sassoftware/relic/v7/lib/audit" "github.com/sassoftware/relic/v7/lib/binpatch" "github.com/sassoftware/relic/v7/lib/certloader" "github.com/sassoftware/relic/v7/lib/magic" "github.com/sassoftware/relic/v7/lib/pgptools" "github.com/sassoftware/relic/v7/signers" "github.com/sassoftware/relic/v7/signers/sigerrors" ) var RpmSigner = &signers.Signer{ Name: "rpm", Magic: magic.FileTypeRPM, CertTypes: signers.CertTypePgp, FormatLog: formatLog, Sign: sign, Verify: verify, } func init() { signers.Register(RpmSigner) } func formatLog(attrs *audit.Info) *zerolog.Event { return attrs.AttrsForLog("rpm.") } func sign(r io.Reader, cert *certloader.Certificate, opts signers.SignOpts) ([]byte, error) { config := &rpmutils.SignatureOptions{ Hash: opts.Hash, CreationTime: opts.Time.UTC().Round(time.Second), } header, err := rpmutils.SignRpmStream(r, cert.PgpKey.PrivateKey, config) if err != nil { return nil, err } blob, err := header.DumpSignatureHeader(true) if err != nil { return nil, err } patch := binpatch.New() patch.Add(0, int64(header.OriginalSignatureHeaderSize()), blob) md5, _ := header.GetBytes(rpmutils.SIG_MD5) sha1, _ := header.GetString(rpmutils.SIG_SHA1) opts.Audit.Attributes["rpm.nevra"] = nevra(header) opts.Audit.Attributes["rpm.md5"] = hex.EncodeToString(md5) opts.Audit.Attributes["rpm.sha1"] = sha1 return opts.SetBinPatch(patch) } func verify(f *os.File, opts signers.VerifyOpts) ([]*signers.Signature, error) { // TODO: add a flag to skip payload digest to rpmutils.Verify header, sigs, err := rpmutils.Verify(f, opts.TrustedPgp) if err != nil { return nil, err } if len(sigs) == 0 { return nil, sigerrors.NotSignedError{Type: "RPM"} } var ret []*signers.Signature seen := make(map[uint64]bool) for _, sig := range sigs { if seen[sig.KeyId] { continue } seen[sig.KeyId] = true rsig := &signers.Signature{ Package: nevra(header), CreationTime: sig.CreationTime, Hash: sig.Hash, } if sig.Signer == nil { if !opts.NoChain { return nil, pgptools.ErrNoKey(sig.KeyId) } rsig.Signer = fmt.Sprintf("UNKNOWN(%x)", sig.KeyId) } else { rsig.SignerPgp = sig.Signer } ret = append(ret, rsig) } return ret, nil } func nevra(header *rpmutils.RpmHeader) string { nevra, _ := header.GetNEVRA() snevra := nevra.String() // strip .rpm snevra = snevra[:len(snevra)-4] // strip zero epoch snevra = strings.ReplaceAll(snevra, "-0:", "-") return snevra } relic-7.6.1/signers/sigerrors/000077500000000000000000000000001455105530300163155ustar00rootroot00000000000000relic-7.6.1/signers/sigerrors/errors.go000066400000000000000000000023531455105530300201630ustar00rootroot00000000000000// // Copyright (c) SAS Institute Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package sigerrors import ( "errors" ) var ( ErrExist = errors.New("object already exists in token") ) type KeyNotFoundError struct{} func (KeyNotFoundError) Error() string { return "No object found in token with the specified label" } type PinIncorrectError struct{} func (PinIncorrectError) Error() string { return "The entered PIN was incorrect" } type ErrNoCertificate struct { Type string } func (e ErrNoCertificate) Error() string { return "no certificate of type \"" + e.Type + "\" defined for this key" } type NotSignedError struct { Type string } func (e NotSignedError) Error() string { return e.Type + " contains no signatures" } relic-7.6.1/signers/signers.go000066400000000000000000000140271455105530300163030ustar00rootroot00000000000000// // Copyright (c) SAS Institute Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package signers import ( "crypto" "errors" "fmt" "io" "os" "time" "github.com/rs/zerolog" "github.com/spf13/cobra" "github.com/spf13/pflag" "golang.org/x/crypto/openpgp" "github.com/sassoftware/relic/v7/lib/audit" "github.com/sassoftware/relic/v7/lib/certloader" "github.com/sassoftware/relic/v7/lib/magic" "github.com/sassoftware/relic/v7/lib/pgptools" "github.com/sassoftware/relic/v7/lib/pkcs9" "github.com/sassoftware/relic/v7/lib/x509tools" "github.com/sassoftware/relic/v7/signers/sigerrors" ) type Signer struct { Name string Aliases []string Magic magic.FileType CertTypes CertType AllowStdin bool // Return true if the given filename is associated with this signer TestPath func(string) bool // Format audit attributes for logfile FormatLog func(*audit.Info) *zerolog.Event // Verify a file, returning the set of signatures found. Performs integrity // checks but does not build X509 chains. Verify func(*os.File, VerifyOpts) ([]*Signature, error) // VerifyStream is like Verify but doesn't need to seek. VerifyStream func(io.Reader, VerifyOpts) ([]*Signature, error) // Transform a file into a stream to upload Transform func(*os.File, SignOpts) (Transformer, error) // Sign a input stream (possibly transformed) and return a mode-specific result blob Sign func(io.Reader, *certloader.Certificate, SignOpts) ([]byte, error) // Final step to run on the client after the file is patched Fixup func(*os.File) error flags *pflag.FlagSet } type CertType uint const ( CertTypeX509 CertType = 1 << iota CertTypePgp ) type Signature struct { Package string SigInfo string CreationTime time.Time Hash crypto.Hash Signer string SignerPgp *openpgp.Entity X509Signature *pkcs9.TimestampedSignature } func (s *Signature) SignerName() string { if s.Signer != "" { return s.Signer } if s.X509Signature != nil { return fmt.Sprintf("`%s`", x509tools.FormatSubject(s.X509Signature.Certificate)) } if s.SignerPgp != nil { return fmt.Sprintf("`%s`(%x)", pgptools.EntityName(s.SignerPgp), s.SignerPgp.PrimaryKey.KeyId) } return "UNKNOWN" } var registered []*Signer var flagMap map[string][]string func Register(s *Signer) { registered = append(registered, s) } // Return the signer module with the given name or alias func ByName(name string) *Signer { for _, s := range registered { if s.Name == name { return s } for _, n2 := range s.Aliases { if n2 == name { return s } } } return nil } // Return the signer module responsible for the given file magic func ByMagic(m magic.FileType) *Signer { if m == magic.FileTypeUnknown { return nil } for _, s := range registered { if s.Magic == m { return s } } return nil } // Return the signer associated with the given filename extension func ByFileName(name string) *Signer { for _, s := range registered { if s.TestPath != nil && s.TestPath(name) { return s } } return nil } // Return the named signer module if given, otherwise identify the file at the // given path by contents or extension func ByFile(name, sigtype string) (*Signer, error) { if sigtype != "" { mod := ByName(sigtype) if mod == nil { return nil, errors.New("no signer with that name") } return mod, nil } if name == "-" { return nil, errors.New("reading from standard input is not supported") } f, err := os.Open(name) if err != nil { return nil, err } defer f.Close() fileType, compressionType := magic.DetectCompressed(f) if compressionType != magic.CompressedNone { return nil, errors.New("cannot sign compressed file") } if mod := ByMagic(fileType); mod != nil { return mod, nil } else if mod := ByFileName(name); mod != nil { return mod, nil } return nil, errors.New("unknown filetype") } // Create a FlagSet for flags associated with this module. These will be added // to "sign" and "remote sign", and transferred to a remote server via the URL // query parameters. func (s *Signer) Flags() *pflag.FlagSet { if s.flags == nil { s.flags = pflag.NewFlagSet(s.Name, pflag.ExitOnError) } return s.flags } // Add this module's flags to a command FlagSet func MergeFlags(cmd *cobra.Command) { if flagMap == nil { flagMap = make(map[string][]string) } fs := cmd.Flags() fs.AddFlagSet(common) for _, s := range registered { if s.flags == nil { continue } fs.AddFlagSet(s.flags) s.flags.VisitAll(func(flag *pflag.Flag) { flagMap[flag.Name] = append(flagMap[flag.Name], s.Name) }) } // customize the usage function so that if --sig-type is set then only those flags are displayed orig := cmd.UsageFunc() cmd.SetUsageFunc(func(c *cobra.Command) error { if t, _ := fs.GetString("sig-type"); t != "" { for name, signers := range flagMap { var ok bool for _, signer := range signers { if signer == t { ok = true break } } if !ok { _ = fs.MarkHidden(name) } } } return orig(c) }) } // IsSigned checks if a file contains a signature func (s *Signer) IsSigned(f *os.File) (bool, error) { var err error if s.VerifyStream != nil { _, err = s.VerifyStream(f, VerifyOpts{NoDigests: true, NoChain: true}) } else if s.Verify != nil { _, err = s.Verify(f, VerifyOpts{NoDigests: true, NoChain: true}) } else { return false, errors.New("cannot check if this type of file is signed") } if err == nil { return true, nil } switch err.(type) { case sigerrors.NotSignedError: return false, nil case pgptools.ErrNoKey: return true, nil } return false, err } relic-7.6.1/signers/transform.go000066400000000000000000000056541455105530300166520ustar00rootroot00000000000000// // Copyright (c) SAS Institute Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package signers // Some package types can't be signed as a stream as-is, so we transform them // into something else (a tarball) and upload that to the server. The server // signs the tar, returns the signature blob, and the client inserts the blob // into the original file. This mechanism can also be used for cases that don't // need a transform on the upload but do need special processing on the result // side. A default implementation that handles patching, copying, and // overwriting is provided. import ( "fmt" "io" "io/ioutil" "os" "github.com/sassoftware/relic/v7/lib/atomicfile" "github.com/sassoftware/relic/v7/lib/binpatch" ) type Transformer interface { // Return a stream that will be uploaded to a remote server. This may be // called multiple times in case of failover. GetReader() (stream io.Reader, err error) // Apply a HTTP response to the named destination file Apply(dest, mimetype string, result io.Reader) error } // Return the transform for the given module if it has one, otherwise return // the default transform. func (s *Signer) GetTransform(f *os.File, opts SignOpts) (Transformer, error) { if s != nil && s.Transform != nil { return s.Transform(f, opts) } return fileProducer{f}, nil } func DefaultTransform(f *os.File) Transformer { return fileProducer{f} } // Dummy implementation that sends the original file as a request, and // either applies a binary patch or overwrites the whole file depending on the // MIME type. type fileProducer struct { f *os.File } func (p fileProducer) GetReader() (io.Reader, error) { if _, err := p.f.Seek(0, io.SeekStart); err != nil { return nil, fmt.Errorf("seeking input file: %w", err) } return p.f, nil } // If the response is a binpatch, apply it. Otherwise overwrite the destination // file with the response func (p fileProducer) Apply(dest, mimetype string, result io.Reader) error { if mimetype == binpatch.MimeType { return ApplyBinPatch(p.f, dest, result) } f, err := atomicfile.WriteAny(dest) if err != nil { return err } if _, err := io.Copy(f, result); err != nil { return err } p.f.Close() return f.Commit() } func ApplyBinPatch(src *os.File, dest string, result io.Reader) error { blob, err := ioutil.ReadAll(result) if err != nil { return err } patch, err := binpatch.Load(blob) if err != nil { return err } return patch.Apply(src, dest) } relic-7.6.1/signers/vsix/000077500000000000000000000000001455105530300152675ustar00rootroot00000000000000relic-7.6.1/signers/vsix/consts.go000066400000000000000000000034331455105530300171320ustar00rootroot00000000000000// // Copyright (c) SAS Institute Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package vsix const ( contentTypesPath = "[Content_Types].xml" rootRelsPath = "_rels" digSigPath = "package/services/digital-signature" originPath = digSigPath + "/origin.psdor" xmlSigPath = digSigPath + "/xml-signature" xmlCertPath = digSigPath + "/certificate" nsDigSig = "http://schemas.openxmlformats.org/package/2006/digital-signature" sigOriginType = "http://schemas.openxmlformats.org/package/2006/relationships/digital-signature/origin" sigType = "http://schemas.openxmlformats.org/package/2006/relationships/digital-signature/signature" certType = "http://schemas.openxmlformats.org/package/2006/relationships/digital-signature/certificate" defaultContentType = "application/octet-stream" tsFormatXML = "YYYY-MM-DDThh:mm:ss.sTZD" tsFormatGo = "2006-01-02T15:04:05.0-07:00" ) var contentTypes = map[string]string{ "cer": "application/vnd.openxmlformats-package.digital-signature-certificate", "psdor": "application/vnd.openxmlformats-package.digital-signature-origin", "psdsxs": "application/vnd.openxmlformats-package.digital-signature-xmlsignature+xml", "rels": "application/vnd.openxmlformats-package.relationships+xml", } relic-7.6.1/signers/vsix/contenttypes.go000066400000000000000000000024031455105530300203540ustar00rootroot00000000000000// // Copyright (c) SAS Institute Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package vsix import ( "fmt" "io/ioutil" "github.com/sassoftware/relic/v7/lib/zipslicer" ) func (m *mangler) parseTypes(f *zipslicer.MangleFile) error { fc, err := f.Open() if err != nil { return err } blob, err := ioutil.ReadAll(fc) if err != nil { return err } if err := m.ctypes.Parse(blob); err != nil { return fmt.Errorf("parsing %s: %w", f.Name, err) } return nil } func (m *mangler) newCtypes(hasCer bool) error { for ext, ctype := range contentTypes { if ext == "cer" && !hasCer { continue } m.ctypes.ByExt[ext] = ctype } contents, err := m.ctypes.Marshal() if err != nil { return err } return m.m.NewFile(contentTypesPath, contents) } relic-7.6.1/signers/vsix/mangle.go000066400000000000000000000034461455105530300170700ustar00rootroot00000000000000// // Copyright (c) SAS Institute Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package vsix import ( "crypto" "io" "path" "strings" "github.com/sassoftware/relic/v7/lib/signappx" "github.com/sassoftware/relic/v7/lib/zipslicer" ) type mangler struct { m *zipslicer.Mangler digests map[string][]byte ctypes *signappx.ContentTypes hash crypto.Hash } func mangleZip(r io.Reader, hash crypto.Hash) (*mangler, error) { inz, err := zipslicer.ReadZipTar(r) if err != nil { return nil, err } m := &mangler{ digests: make(map[string][]byte), ctypes: signappx.NewContentTypes(), hash: hash, } zm, err := inz.Mangle(func(f *zipslicer.MangleFile) error { if keepFile(f.Name) { sum, err := f.Digest(hash) if err != nil { return err } m.digests[f.Name] = sum return nil } else { if f.Name == contentTypesPath { if err := m.parseTypes(f); err != nil { return err } } f.Delete() } return nil }) if err != nil { return nil, err } m.m = zm return m, nil } func keepFile(fp string) bool { switch fp { case rootRelsPath + "/", contentTypesPath: return false } switch path.Ext(fp) { case ".rels", ".psdsxs", ".psdor": return false } switch { case strings.HasPrefix(fp, digSigPath+"/"): return false } return true } relic-7.6.1/signers/vsix/oxmlsig.go000066400000000000000000000165731455105530300173140ustar00rootroot00000000000000// // Copyright (c) SAS Institute Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package vsix import ( "crypto" "crypto/hmac" "crypto/x509" "encoding/base32" "encoding/base64" "encoding/xml" "errors" "fmt" "io" "path" "sort" "strings" "github.com/beevik/etree" "github.com/sassoftware/relic/v7/lib/certloader" "github.com/sassoftware/relic/v7/lib/pkcs7" "github.com/sassoftware/relic/v7/lib/pkcs9" "github.com/sassoftware/relic/v7/lib/xmldsig" "github.com/sassoftware/relic/v7/signers" "github.com/sassoftware/relic/v7/signers/sigerrors" ) type oxmlManifest struct { References []reference `xml:"Manifest>Reference"` Properties []property `xml:"SignatureProperties>SignatureProperty"` } type reference struct { URI string `xml:",attr"` Transforms []method `xml:"Transforms>Transform"` DigestMethod method DigestValue string } type method struct { Algorithm string `xml:",attr"` } type property struct { Id string `xml:",attr"` SignatureTimeFormat string `xml:"SignatureTime>Format"` SignatureTimeValue string `xml:"SignatureTime>Value"` } func checkManifest(files zipFiles, manifest *etree.Element) error { doc := etree.NewDocument() doc.SetRoot(manifest.Copy()) blob, err := doc.WriteToBytes() if err != nil { return fmt.Errorf("validation failed: %w", err) } var m oxmlManifest if err := xml.Unmarshal(blob, &m); err != nil { return fmt.Errorf("validation failed: %w", err) } for _, ref := range m.References { p := path.Join("./" + ref.URI) i := strings.IndexByte(p, '?') if i >= 0 { p = p[:i] } zf := files[p] if zf == nil { return fmt.Errorf("validation failed: file not found: %s", p) } f, err := zf.Open() if err != nil { return fmt.Errorf("validation failed: %w", err) } _, hash := xmldsig.HashAlgorithm(ref.DigestMethod.Algorithm) if !hash.Available() { return errors.New("validation failed: unsupported digest algorithm") } d := hash.New() if _, err := io.Copy(d, f); err != nil { return err } refCalc := d.Sum(nil) refv, err := base64.StdEncoding.DecodeString(ref.DigestValue) if err != nil { return errors.New("validation failed: invalid digest") } if !hmac.Equal(refv, refCalc) { return fmt.Errorf("validation failed: digest mismatch for %s: calculated %x, found %x", p, refCalc, refv) } } return nil } func checkTimestamp(root *etree.Element, encryptedDigest []byte) (*pkcs9.CounterSignature, error) { tsEl := root.FindElement("Object/TimeStamp/EncodedTime") if tsEl == nil { return nil, nil } blob, err := base64.StdEncoding.DecodeString(tsEl.Text()) if err != nil { return nil, fmt.Errorf("timestamp check failed: %w", err) } tst, err := pkcs7.Unmarshal(blob) if err != nil { return nil, fmt.Errorf("timestamp check failed: %w", err) } return pkcs9.Verify(tst, encryptedDigest, nil) } // looks sorta like the official vsixsigntool output, close enough func calcFileName(cert *x509.Certificate) string { d := crypto.SHA1.New() d.Write(cert.Raw) sum := d.Sum(nil) return strings.ToLower(base32.StdEncoding.EncodeToString(sum))[:25] } func readSignature(files zipFiles) ([]byte, []*x509.Certificate, error) { top := relPath("") if files[top] == nil { return nil, nil, sigerrors.NotSignedError{Type: "vsix"} } // top rels file r, err := parseRels(files, top) if err != nil { return nil, nil, err } origin := r.Find(sigOriginType) if origin == "" { return nil, nil, sigerrors.NotSignedError{Type: "vsix"} } // signature rels file r, err = parseRels(files, relPath(origin)) if err != nil { return nil, nil, err } sigpath := r.Find(sigType) if sigpath == "" { return nil, nil, sigerrors.NotSignedError{Type: "vsix"} } sigblob, err := readZip(files, sigpath) if err != nil { return nil, nil, err } // certificates (optional) var certs []*x509.Certificate if files[relPath(sigpath)] != nil { r, err := parseRels(files, relPath(sigpath)) if err != nil { return nil, nil, err } for _, rel := range r.Relationship { if rel.Type != certType { continue } p := path.Clean("./" + rel.Target) blob, err := readZip(files, p) if err != nil { return nil, nil, err } certs2, err := x509.ParseCertificates(blob) if err != nil { return nil, nil, fmt.Errorf("failed to parse certificate %s: %w", p, err) } certs = append(certs, certs2...) } } return sigblob, certs, nil } func (m *mangler) makeSignature(cert *certloader.Certificate, opts signers.SignOpts, detachCerts bool) ([]byte, error) { hashUri := xmldsig.HashUris[opts.Hash] if hashUri == "" { return nil, errors.New("unsupported digest algorithm") } pkg := etree.NewElement("Object") pkg.CreateAttr("Id", "idPackageObject") // file manifest manifest := pkg.CreateElement("Manifest") names := make([]string, 0, len(m.digests)) for name := range m.digests { names = append(names, name) } sort.Strings(names) for _, name := range names { digest := m.digests[name] ctype := m.ctypes.Find(name) if ctype == "" { ext := path.Ext(path.Base(name)) if ext[0] == '.' { ctype = contentTypes[ext[1:]] } } if ctype == "" { ctype = defaultContentType } ref := manifest.CreateElement("Reference") ref.CreateAttr("URI", "/"+name+"?ContentType="+ctype) ref.CreateElement("DigestMethod").CreateAttr("Algorithm", hashUri) ref.CreateElement("DigestValue").SetText(base64.StdEncoding.EncodeToString(digest)) } // signature time props := pkg.CreateElement("SignatureProperties") proptime := props.CreateElement("SignatureProperty") proptime.CreateAttr("Id", "idSignatureTime") proptime.CreateAttr("Target", "") sigtime := proptime.CreateElement("SignatureTime") sigtime.CreateAttr("xmlns", nsDigSig) sigtime.CreateElement("Format").SetText(tsFormatXML) sigtime.CreateElement("Value").SetText(opts.Time.Format(tsFormatGo)) // sign xopts := xmldsig.SignOptions{UseRecC14n: true, IncludeKeyValue: true} if !detachCerts { xopts.IncludeX509 = true } sigel, err := xmldsig.SignEnveloping(pkg, opts.Hash, cert.Signer(), cert.Chain(), xopts) if err != nil { return nil, err } // timestamp if cert.Timestamper != nil { encryptedDigest, _ := base64.StdEncoding.DecodeString(sigel.SelectElement("SignatureValue").Text()) req := &pkcs9.Request{EncryptedDigest: encryptedDigest, Hash: opts.Hash} tst, err := cert.Timestamper.Timestamp(opts.Context(), req) if err != nil { return nil, fmt.Errorf("failed to timestamp signature: %w", err) } blob, err := tst.Marshal() if err != nil { return nil, fmt.Errorf("failed to timestamp signature: %w", err) } tsob := sigel.CreateElement("Object") tsob.CreateAttr("xmlns", xmldsig.NsXMLDsig) ts := tsob.CreateElement("TimeStamp") ts.CreateAttr("xmlns", nsDigSig) ts.CreateAttr("Id", "idSignatureTimestamp") ts.CreateElement("Comment") ts.CreateElement("EncodedTime").SetText(base64.StdEncoding.EncodeToString(blob)) } doc := etree.NewDocument() doc.SetRoot(sigel) return doc.WriteToBytes() } relic-7.6.1/signers/vsix/rels.go000066400000000000000000000072351455105530300165720ustar00rootroot00000000000000// // Copyright (c) SAS Institute Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package vsix import ( "crypto" "encoding/xml" "fmt" "io/ioutil" "path" "github.com/sassoftware/relic/v7/lib/certloader" ) type oxfRelationships struct { XMLName xml.Name `xml:"http://schemas.openxmlformats.org/package/2006/relationships Relationships"` Relationship []oxfRelationship } type oxfRelationship struct { Target string `xml:",attr"` Id string `xml:",attr"` Type string `xml:",attr"` } func readZip(files zipFiles, path string) ([]byte, error) { zf := files[path] if zf == nil { return nil, fmt.Errorf("file missing from zip: %s", path) } f, err := zf.Open() if err != nil { return nil, fmt.Errorf("failed to read zip file %s: %w", path, err) } return ioutil.ReadAll(f) } func parseRels(files zipFiles, path string) (*oxfRelationships, error) { blob, err := readZip(files, path) if err != nil { return nil, err } rels := new(oxfRelationships) if err := xml.Unmarshal(blob, rels); err != nil { return nil, fmt.Errorf("error parsing rels: %w", err) } return rels, nil } func (rels *oxfRelationships) Find(rType string) string { for _, rel := range rels.Relationship { if rel.Type == rType { return path.Clean("./" + rel.Target) } } return "" } func (rels *oxfRelationships) Append(zipPath, relType string) { d := crypto.SHA1.New() d.Write([]byte(zipPath)) d.Write([]byte(relType)) rel := oxfRelationship{Target: path.Clean("/" + zipPath), Type: relType} for { rel.Id = fmt.Sprintf("R%X", d.Sum(nil)[:4]) ok := true for _, rel2 := range rels.Relationship { if rel2.Id == rel.Id { ok = false } } if ok { break } d.Write([]byte{0}) } rels.Relationship = append(rels.Relationship, rel) } func (rels *oxfRelationships) Marshal() ([]byte, error) { x, err := xml.Marshal(rels) if err != nil { return nil, err } ret := make([]byte, len(xml.Header), len(xml.Header)+len(x)) copy(ret, xml.Header) ret = append(ret, x...) return ret, nil } func relPath(fp string) string { base := path.Base(fp) if base == "." { base = "" } return path.Join(path.Dir(fp), "_rels", base+".rels") } func (m *mangler) addFile(name string, contents []byte) error { d := m.hash.New() d.Write(contents) m.digests[name] = d.Sum(nil) return m.m.NewFile(name, contents) } func (m *mangler) newRels(parent, child, relType string) error { var rels oxfRelationships rels.Append(child, relType) contents, err := rels.Marshal() if err != nil { return err } return m.addFile(relPath(parent), contents) } func (m *mangler) addOrigin() error { return m.addFile(originPath, nil) } func (m *mangler) addCerts(cert *certloader.Certificate, sigName string) error { // NB: neither the certs nor the rels file are part of the signature, so // bypass m.digests and just call NewFile directly var rels oxfRelationships for _, chain := range cert.Chain() { certpath := path.Join(xmlCertPath, calcFileName(chain)+".cer") if err := m.m.NewFile(certpath, chain.Raw); err != nil { return err } rels.Append(certpath, certType) } contents, err := rels.Marshal() if err != nil { return err } return m.m.NewFile(relPath(sigName), contents) } relic-7.6.1/signers/vsix/signer.go000066400000000000000000000072171455105530300171140ustar00rootroot00000000000000// // Copyright (c) SAS Institute Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package vsix import ( "archive/zip" "errors" "io" "os" "path" "github.com/beevik/etree" "github.com/sassoftware/relic/v7/lib/certloader" "github.com/sassoftware/relic/v7/lib/magic" "github.com/sassoftware/relic/v7/lib/pkcs7" "github.com/sassoftware/relic/v7/lib/pkcs9" "github.com/sassoftware/relic/v7/lib/xmldsig" "github.com/sassoftware/relic/v7/signers" "github.com/sassoftware/relic/v7/signers/zipbased" ) var Signer = &signers.Signer{ Name: "vsix", Magic: magic.FileTypeVSIX, CertTypes: signers.CertTypeX509, Transform: zipbased.Transform, Sign: sign, Verify: verify, } type zipFiles map[string]*zip.File func init() { signers.Register(Signer) Signer.Flags().Bool("detach-certs", false, "(VSIX) Package certificates separately in the archive") } func sign(r io.Reader, cert *certloader.Certificate, opts signers.SignOpts) ([]byte, error) { detachCerts := opts.Flags.GetBool("detach-certs") m, err := mangleZip(r, opts.Hash) if err != nil { return nil, err } // add rels and origin to zip sigName := path.Join(xmlSigPath, calcFileName(cert.Leaf)+".psdsxs") if err := m.newRels("", originPath, sigOriginType); err != nil { return nil, err } if err := m.newRels(originPath, sigName, sigType); err != nil { return nil, err } if err := m.addOrigin(); err != nil { return nil, err } // add certs (optional) if detachCerts { if err := m.addCerts(cert, sigName); err != nil { return nil, err } } // sign and add ctypes sigfile, err := m.makeSignature(cert, opts, detachCerts) if err != nil { return nil, err } if err := m.m.NewFile(sigName, sigfile); err != nil { return nil, err } if err := m.newCtypes(detachCerts); err != nil { return nil, err } patch, err := m.m.MakePatch(true) if err != nil { return nil, err } return opts.SetBinPatch(patch) } func verify(f *os.File, opts signers.VerifyOpts) ([]*signers.Signature, error) { // read zip file size, err := f.Seek(0, io.SeekEnd) if err != nil { return nil, err } inz, err := zip.NewReader(f, size) if err != nil { return nil, err } files := make(zipFiles, len(inz.File)) for _, f := range inz.File { files[f.Name] = f } // find and parse the signature XML sig, certs, err := readSignature(files) if err != nil { return nil, err } doc := etree.NewDocument() if err := doc.ReadFromString(string(sig)); err != nil { return nil, err } root := doc.Root() // basic verification of XML xs, err := xmldsig.Verify(root, ".", certs) if err != nil { return nil, err } // verify digests of files if err := checkManifest(files, xs.Reference); err != nil { return nil, err } // verify PKCS#9 timestamp token cs, err := checkTimestamp(root, xs.EncryptedDigest) if err != nil { return nil, err } psig := pkcs7.Signature{Intermediates: xs.Certificates, Certificate: xs.Leaf()} if psig.Certificate == nil { return nil, errors.New("leaf x509 certificate not found") } return []*signers.Signature{&signers.Signature{ Hash: xs.Hash, X509Signature: &pkcs9.TimestampedSignature{ Signature: psig, CounterSignature: cs, }, }}, nil } relic-7.6.1/signers/xap/000077500000000000000000000000001455105530300150665ustar00rootroot00000000000000relic-7.6.1/signers/xap/signer.go000066400000000000000000000037771455105530300167220ustar00rootroot00000000000000// // Copyright (c) SAS Institute Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package xap import ( "io" "os" "github.com/sassoftware/relic/v7/lib/certloader" "github.com/sassoftware/relic/v7/lib/magic" "github.com/sassoftware/relic/v7/lib/signxap" "github.com/sassoftware/relic/v7/signers" "github.com/sassoftware/relic/v7/signers/pecoff" "github.com/sassoftware/relic/v7/signers/zipbased" ) // Sign Silverlight / legacy Windows Phone apps var XapSigner = &signers.Signer{ Name: "xap", Magic: magic.FileTypeXAP, CertTypes: signers.CertTypeX509, Transform: zipbased.Transform, Sign: sign, Verify: verify, } func init() { pecoff.AddOpusFlags(XapSigner) signers.Register(XapSigner) } func sign(r io.Reader, cert *certloader.Certificate, opts signers.SignOpts) ([]byte, error) { digest, err := signxap.DigestXapTar(r, opts.Hash, false) if err != nil { return nil, err } patch, sig, err := digest.Sign(opts.Context(), cert, pecoff.OpusFlags(opts)) if err != nil { return nil, err } opts.Audit.SetCounterSignature(sig.CounterSignature) return opts.SetBinPatch(patch) } func verify(f *os.File, opts signers.VerifyOpts) ([]*signers.Signature, error) { size, err := f.Seek(0, io.SeekEnd) if err != nil { return nil, err } sig, err := signxap.Verify(f, size, opts.NoDigests) if err != nil { return nil, err } return []*signers.Signature{{ Hash: sig.Hash, X509Signature: &sig.TimestampedSignature, SigInfo: pecoff.FormatOpus(sig.OpusInfo), }}, nil } relic-7.6.1/signers/xar/000077500000000000000000000000001455105530300150705ustar00rootroot00000000000000relic-7.6.1/signers/xar/xar.go000066400000000000000000000025361455105530300162170ustar00rootroot00000000000000package xar import ( "io" "os" "github.com/sassoftware/relic/v7/lib/certloader" "github.com/sassoftware/relic/v7/lib/fruit/csblob" "github.com/sassoftware/relic/v7/lib/fruit/xar" "github.com/sassoftware/relic/v7/lib/magic" "github.com/sassoftware/relic/v7/signers" ) var signer = &signers.Signer{ Name: "xar", Magic: magic.FileTypeXAR, CertTypes: signers.CertTypeX509, Sign: sign, Verify: verify, } func init() { signers.Register(signer) } func sign(r io.Reader, cert *certloader.Certificate, opts signers.SignOpts) ([]byte, error) { patch, tsig, err := xar.Sign(opts.Context(), r, cert, opts.Hash) if err != nil { return nil, err } if teamID := csblob.TeamID(cert.Leaf); teamID != "" { opts.Audit.Attributes["mach-o.team-id"] = teamID } opts.Audit.SetCounterSignature(tsig.CounterSignature) return opts.SetBinPatch(patch) } func verify(f *os.File, opts signers.VerifyOpts) ([]*signers.Signature, error) { size, err := f.Seek(0, io.SeekEnd) if err != nil { return nil, err } x, err := xar.Open(f, size) if err != nil { return nil, err } sig, err := x.Verify(opts.NoDigests) if err != nil { return nil, err } var si string if len(sig.NotaryTicket) > 0 { si += "[HasNotaryTicket]" } return []*signers.Signature{{ Hash: sig.HashFunc, X509Signature: sig.Signature, SigInfo: si, }}, nil } relic-7.6.1/signers/zipbased/000077500000000000000000000000001455105530300160775ustar00rootroot00000000000000relic-7.6.1/signers/zipbased/zipbased.go000066400000000000000000000024201455105530300202250ustar00rootroot00000000000000// // Copyright (c) SAS Institute Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package zipbased import ( "io" "os" "github.com/sassoftware/relic/v7/lib/zipslicer" "github.com/sassoftware/relic/v7/signers" ) type zipTransformer struct { f *os.File } func Transform(f *os.File, opts signers.SignOpts) (signers.Transformer, error) { return &zipTransformer{f}, nil } // Wrap the zip in a tarball with the central directory first so that it can be // processed as a stream func (t *zipTransformer) GetReader() (io.Reader, error) { r, w := io.Pipe() go func() { _ = w.CloseWithError(zipslicer.ZipToTar(t.f, w)) }() return r, nil } func (t *zipTransformer) Apply(dest, mimeType string, result io.Reader) error { return signers.ApplyBinPatch(t.f, dest, result) } relic-7.6.1/token/000077500000000000000000000000001455105530300137445ustar00rootroot00000000000000relic-7.6.1/token/awstoken/000077500000000000000000000000001455105530300155775ustar00rootroot00000000000000relic-7.6.1/token/awstoken/token.go000066400000000000000000000102271455105530300172500ustar00rootroot00000000000000package awstoken import ( "context" "crypto" "crypto/ecdsa" "crypto/rsa" "crypto/x509" "fmt" "io" awsconfig "github.com/aws/aws-sdk-go-v2/config" "github.com/aws/aws-sdk-go-v2/service/kms" "github.com/aws/aws-sdk-go-v2/service/kms/types" "github.com/sassoftware/relic/v7/config" "github.com/sassoftware/relic/v7/lib/passprompt" "github.com/sassoftware/relic/v7/token" ) const tokenType = "aws" type awsToken struct { config *config.Config tconf *config.TokenConfig cli *kms.Client } type awsKey struct { kconf *config.KeyConfig cli *kms.Client pub crypto.PublicKey } func init() { token.Openers[tokenType] = open } func open(conf *config.Config, tokenName string, pinProvider passprompt.PasswordGetter) (token.Token, error) { tconf, err := conf.GetToken(tokenName) if err != nil { return nil, err } cfg, err := awsconfig.LoadDefaultConfig(context.Background()) if err != nil { return nil, err } cli := kms.NewFromConfig(cfg) return &awsToken{ config: conf, tconf: tconf, cli: cli, }, nil } func (t *awsToken) Close() error { return nil } func (t *awsToken) Ping(ctx context.Context) error { // TODO return nil } func (t *awsToken) Config() *config.TokenConfig { return t.tconf } func (t *awsToken) GetKey(ctx context.Context, keyName string) (token.Key, error) { keyConf, err := t.config.GetKey(keyName) if err != nil { return nil, err } if keyConf.ID == "" { return nil, fmt.Errorf("key %q must have \"id\" set to the ID or ARN of the key", keyName) } id := keyConf.ID resp, err := t.cli.GetPublicKey(ctx, &kms.GetPublicKeyInput{KeyId: &id}) if err != nil { return nil, err } pub, err := x509.ParsePKIXPublicKey(resp.PublicKey) if err != nil { return nil, err } return &awsKey{ kconf: keyConf, cli: t.cli, pub: pub, }, nil } func (t *awsToken) Import(keyName string, privKey crypto.PrivateKey) (token.Key, error) { return nil, token.NotImplementedError{Op: "import-key", Type: tokenType} } func (t *awsToken) ImportCertificate(cert *x509.Certificate, labelBase string) error { return token.NotImplementedError{Op: "import-certificate", Type: tokenType} } func (t *awsToken) Generate(keyName string, keyType token.KeyType, bits uint) (token.Key, error) { return nil, token.NotImplementedError{Op: "generate-key", Type: tokenType} } func (t *awsToken) ListKeys(opts token.ListOptions) error { return token.NotImplementedError{Op: "list-keys", Type: tokenType} } func (k *awsKey) Public() crypto.PublicKey { return k.pub } func (k *awsKey) Sign(rand io.Reader, digest []byte, opts crypto.SignerOpts) (signature []byte, err error) { return k.SignContext(context.Background(), digest, opts) } func (k *awsKey) SignContext(ctx context.Context, digest []byte, opts crypto.SignerOpts) (signature []byte, err error) { alg, err := k.sigAlgorithm(opts) if err != nil { return nil, err } id := k.kconf.ID resp, err := k.cli.Sign(ctx, &kms.SignInput{ KeyId: &id, Message: digest, SigningAlgorithm: types.SigningAlgorithmSpec(alg), MessageType: types.MessageTypeDigest, }) if err != nil { return nil, err } return resp.Signature, nil } func (k *awsKey) Config() *config.KeyConfig { return k.kconf } func (k *awsKey) Certificate() []byte { return nil } func (k *awsKey) GetID() []byte { return nil } func (k *awsKey) ImportCertificate(cert *x509.Certificate) error { return token.NotImplementedError{Op: "import-certificate", Type: tokenType} } func (k *awsKey) sigAlgorithm(opts crypto.SignerOpts) (string, error) { var alg string switch opts.HashFunc() { case crypto.SHA256: alg = "SHA_256" case crypto.SHA384: alg = "SHA_384" case crypto.SHA512: alg = "SHA_512" default: return "", token.KeyUsageError{ Key: k.kconf.Name(), Err: fmt.Errorf("unsupported digest algorithm %s", opts.HashFunc()), } } switch k.pub.(type) { case *rsa.PublicKey: if _, ok := opts.(*rsa.PSSOptions); ok { return "RSASSA_PSS_" + alg, nil } else { return "RSASSA_PKCS1_V1_5_" + alg, nil } case *ecdsa.PublicKey: return "ECDSA_" + alg, nil default: return "", token.KeyUsageError{ Key: k.kconf.Name(), Err: fmt.Errorf("unsupported public key type %T", k.pub), } } } relic-7.6.1/token/azuretoken/000077500000000000000000000000001455105530300161335ustar00rootroot00000000000000relic-7.6.1/token/azuretoken/auth.go000066400000000000000000000067161455105530300174350ustar00rootroot00000000000000package azuretoken import ( "bytes" "errors" "fmt" "net/url" "os" kvauth "github.com/Azure/azure-sdk-for-go/services/keyvault/auth" "github.com/Azure/go-autorest/autorest" "github.com/Azure/go-autorest/autorest/adal" "github.com/Azure/go-autorest/autorest/azure" "github.com/Azure/go-autorest/autorest/azure/auth" "github.com/sassoftware/relic/v7/config" ) // Configure azure authentication based on the token config and/or process // environment. func newAuthorizer(tconf *config.TokenConfig) (autorest.Authorizer, error) { if tconf.Pin == nil { // PIN not present means use environment return newAuthorizerFromEnvironment() } credFile := *tconf.Pin if credFile == "" { // PIN present but empty means use azure CLI auth return kvauth.NewAuthorizerFromCLI() } // PIN is path to a file with credentials and settings os.Setenv("AZURE_AUTH_LOCATION", credFile) return kvauth.NewAuthorizerFromFile() } // If AZURE_BEARER_TOKEN_FILE is set then auth using that file, otherwise follow // the same route as keyvault/auth. // // The bearer token case looks something like this: // https://docs.microsoft.com/en-us/azure/active-directory/develop/workload-identity-federation-create-trust-gcp?tabs=typescript#exchange-a-google-token-for-an-access-token func newAuthorizerFromEnvironment() (autorest.Authorizer, error) { // parse settings from environment resource, err := getResource() if err != nil { return nil, fmt.Errorf("AZURE_ENVIRONMENT: %w", err) } settings, err := auth.GetSettingsFromEnvironment() if err != nil { return nil, fmt.Errorf("parsing environment: %w", err) } settings.Values[auth.Resource] = resource // read bearer token from file tokenFile := os.Getenv("AZURE_BEARER_TOKEN_FILE") if tokenFile == "" { // MSI or other env auth return settings.GetAuthorizer() } clientID := settings.Values[auth.ClientID] tenantID := settings.Values[auth.TenantID] if clientID == "" || tenantID == "" { return nil, errors.New("AZURE_CLIENT_ID and AZURE_TENANT_ID are required") } secret := &bearerTokenFileSecret{tokenFile: tokenFile} oauthConf, err := adal.NewOAuthConfig(settings.Environment.ActiveDirectoryEndpoint, tenantID) if err != nil { return nil, fmt.Errorf("ADAL.NewOAuthConfig: %w", err) } spt, err := adal.NewServicePrincipalTokenWithSecret(*oauthConf, clientID, resource, secret) if err != nil { return nil, fmt.Errorf("configuring service principal: %w", err) } return autorest.NewBearerAuthorizer(spt), nil } // copied from keyvault/auth func getResource() (string, error) { var env azure.Environment if envName := os.Getenv("AZURE_ENVIRONMENT"); envName == "" { env = azure.PublicCloud } else { var err error env, err = azure.EnvironmentFromName(envName) if err != nil { return "", err } } resource := os.Getenv("AZURE_KEYVAULT_RESOURCE") if resource == "" { resource = env.ResourceIdentifiers.KeyVault } return resource, nil } type bearerTokenFileSecret struct { tokenFile string } func (b *bearerTokenFileSecret) SetAuthenticationValues(spt *adal.ServicePrincipalToken, v *url.Values) error { blob, err := os.ReadFile(b.tokenFile) if err != nil { return err } bearerToken := string(bytes.TrimSpace(blob)) v.Set("client_assertion", bearerToken) v.Set("client_assertion_type", "urn:ietf:params:oauth:client-assertion-type:jwt-bearer") return nil } // MarshalJSON implements the json.Marshaler interface. func (bearerTokenFileSecret) MarshalJSON() ([]byte, error) { return nil, errors.New("not implemented") } relic-7.6.1/token/azuretoken/findcert.go000066400000000000000000000065631455105530300202720ustar00rootroot00000000000000package azuretoken import ( "bytes" "context" "errors" "fmt" "net/url" "strings" "time" ) type certRef struct { KeyName, KeyVersion string CertBlob []byte } // encode the exact key version into our ID field so that when we're being used // via a worker token, callers get a consistent signing key from GetKey() to // Sign() func (r certRef) KeyID() []byte { return []byte(r.KeyName + "/" + r.KeyVersion) } // decode a previous KeyID() call back into a key version func refFromKeyID(keyID []byte) *certRef { words := bytes.Split(keyID, []byte{'/'}) if len(words) != 2 { return nil } return &certRef{ KeyName: string(words[0]), KeyVersion: string(words[1]), } } // load the named certificate version and return the key it references func (t *kvToken) loadCertificateVersion(ctx context.Context, baseURL, certName, certVersion string) (*certRef, error) { cert, err := t.cli.GetCertificate(ctx, baseURL, certName, certVersion) if err != nil { return nil, err } if cert.Kid == nil { return nil, errors.New("missing key ID in certificate") } keyWords, _, err := parseKeyURL(*cert.Kid) if err != nil { return nil, err } if len(keyWords) != 4 || keyWords[1] != "keys" { return nil, fmt.Errorf("unexpected format for key ID: %s", *cert.Kid) } var blob []byte if cert.Cer != nil { blob = *cert.Cer } return &certRef{ KeyName: keyWords[2], KeyVersion: keyWords[3], CertBlob: blob, }, nil } // load the latest enabled version of the named certificate and return the key it references func (t *kvToken) loadCertificateLatest(ctx context.Context, baseURL, certName string) (*certRef, error) { // list all versions of the cert and pick the latest enabled one certs, err := t.cli.GetCertificateVersions(ctx, baseURL, certName, nil) if err != nil { return nil, fmt.Errorf("listing cert versions: %w", err) } var best string var bestNBF time.Time for certs.NotDone() { for _, cert := range certs.Values() { if cert.Attributes == nil || cert.Attributes.Enabled == nil || !*cert.Attributes.Enabled { continue } if cert.Attributes.NotBefore == nil || cert.ID == nil { continue } nbf := time.Time(*cert.Attributes.NotBefore) if nbf.After(bestNBF) { best, bestNBF = *cert.ID, nbf } } if err := certs.NextWithContext(ctx); err != nil { return nil, fmt.Errorf("listing cert versions: %w", err) } } // parse the cert ID and load it if best == "" || bestNBF.IsZero() { return nil, fmt.Errorf("cert %s has no enabled versions", certName) } words, _, err := parseKeyURL(best) if err != nil { return nil, fmt.Errorf("cert %s has invalid key ID: %w", certName, err) } if len(words) != 4 || words[1] != "certificates" { return nil, fmt.Errorf("unexpected format for certificate ID: %s", best) } return t.loadCertificateVersion(ctx, baseURL, words[2], words[3]) } var errKeyID = errors.New("id: expected URL of a certificate, certificate version, or key version") func parseKeyURL(keyURL string) (words []string, baseURL string, err error) { if keyURL == "" { return nil, "", errKeyID } // deconstruct URL to call GetKey so it can put it back together again u, err := url.Parse(keyURL) if err != nil { return nil, "", fmt.Errorf("id: %w", err) } else if u.Scheme == "" || u.Host == "" { return nil, "", errKeyID } words = strings.Split(u.Path, "/") u.Path = "" baseURL = u.String() return words, baseURL, nil } relic-7.6.1/token/azuretoken/token.go000066400000000000000000000163501455105530300176070ustar00rootroot00000000000000package azuretoken import ( "context" "crypto" "crypto/ecdsa" "crypto/rsa" "crypto/x509" "encoding/base64" "encoding/json" "errors" "fmt" "io" "strings" "github.com/Azure/azure-sdk-for-go/services/keyvault/2016-10-01/keyvault" "github.com/go-jose/go-jose/v3" "github.com/sassoftware/relic/v7/config" "github.com/sassoftware/relic/v7/lib/passprompt" "github.com/sassoftware/relic/v7/lib/x509tools" "github.com/sassoftware/relic/v7/token" ) const tokenType = "azure" type kvToken struct { config *config.Config tconf *config.TokenConfig cli *keyvault.BaseClient } type kvKey struct { kconf *config.KeyConfig cli *keyvault.BaseClient pub crypto.PublicKey kbase string kname string kversion string id []byte cert []byte } func init() { token.Openers[tokenType] = open } func open(conf *config.Config, tokenName string, pinProvider passprompt.PasswordGetter) (token.Token, error) { tconf, err := conf.GetToken(tokenName) if err != nil { return nil, err } auth, err := newAuthorizer(tconf) if err != nil { return nil, fmt.Errorf("configuring azure auth: %w", err) } cli := keyvault.New() cli.Authorizer = auth return &kvToken{ config: conf, tconf: tconf, cli: &cli, }, nil } func (t *kvToken) Close() error { return nil } func (t *kvToken) Ping(ctx context.Context) error { // query info for one of the keys in this token for _, keyConf := range t.config.Keys { if keyConf.Token != t.tconf.Name() || keyConf.Hide { continue } ctx, cancel := context.WithTimeout(ctx, keyConf.GetTimeout()) defer cancel() _, err := t.getKey(ctx, keyConf, true) if err != nil { return fmt.Errorf("checking key %q: %w", keyConf.Name(), err) } break } return nil } func (t *kvToken) Config() *config.TokenConfig { return t.tconf } func (t *kvToken) GetKey(ctx context.Context, keyName string) (token.Key, error) { keyConf, err := t.config.GetKey(keyName) if err != nil { return nil, err } return t.getKey(ctx, keyConf, false) } func (t *kvToken) getKey(ctx context.Context, keyConf *config.KeyConfig, pingOnly bool) (token.Key, error) { words, baseURL, err := parseKeyURL(keyConf.ID) if err != nil { return nil, fmt.Errorf("key %q: %w", keyConf.Name(), err) } wantKeyID := token.KeyID(ctx) var cert *certRef switch { case len(wantKeyID) != 0: // reusing a key the client saw before cert = refFromKeyID(wantKeyID) if cert == nil { return nil, errors.New("invalid keyID") } cert = &certRef{KeyName: words[0], KeyVersion: words[1]} case len(words) == 4 && words[1] == "keys": // directly to a key version, no cert provided cert = &certRef{KeyName: words[2], KeyVersion: words[3]} case len(words) == 4 && words[1] == "certificates": // link to a cert version, get the key version and cert contents from it cert, err = t.loadCertificateVersion(ctx, baseURL, words[2], words[3]) if err != nil { return nil, fmt.Errorf("key %q: fetching certificate: %w", keyConf.Name(), err) } else if pingOnly { return nil, nil } case len(words) == 3 && words[1] == "certificates": // link to a cert, pick the latest version cert, err = t.loadCertificateLatest(ctx, baseURL, words[2]) if err != nil { return nil, fmt.Errorf("key %q: fetching certificate: %w", keyConf.Name(), err) } else if pingOnly { return nil, nil } default: return nil, fmt.Errorf("key %q: %w", keyConf.Name(), errKeyID) } key, err := t.cli.GetKey(ctx, baseURL, cert.KeyName, cert.KeyVersion) if err != nil { return nil, fmt.Errorf("key %q: %w", keyConf.Name(), err) } else if pingOnly { return nil, nil } // strip off -HSM suffix to get a key type jose will accept kty := strings.TrimSuffix(string(key.Key.Kty), "-HSM") key.Key.Kty = keyvault.JSONWebKeyType(kty) // marshal back to JSON and then parse using jose to get a PublicKey keyBlob, err := json.Marshal(key.Key) if err != nil { return nil, fmt.Errorf("marshaling public key: %w", err) } var jwk jose.JSONWebKey if err := json.Unmarshal(keyBlob, &jwk); err != nil { return nil, fmt.Errorf("unmarshaling public key: %w", err) } return &kvKey{ kconf: keyConf, cli: t.cli, pub: jwk.Key, kbase: baseURL, kname: cert.KeyName, kversion: cert.KeyVersion, cert: cert.CertBlob, id: cert.KeyID(), }, nil } func (t *kvToken) Import(keyName string, privKey crypto.PrivateKey) (token.Key, error) { return nil, token.NotImplementedError{Op: "import-key", Type: tokenType} } func (t *kvToken) ImportCertificate(cert *x509.Certificate, labelBase string) error { return token.NotImplementedError{Op: "import-certificate", Type: tokenType} } func (t *kvToken) Generate(keyName string, keyType token.KeyType, bits uint) (token.Key, error) { return nil, token.NotImplementedError{Op: "generate-key", Type: tokenType} } func (t *kvToken) ListKeys(opts token.ListOptions) error { return token.NotImplementedError{Op: "list-keys", Type: tokenType} } func (k *kvKey) Public() crypto.PublicKey { return k.pub } func (k *kvKey) Sign(rand io.Reader, digest []byte, opts crypto.SignerOpts) (signature []byte, err error) { return k.SignContext(context.Background(), digest, opts) } func (k *kvKey) SignContext(ctx context.Context, digest []byte, opts crypto.SignerOpts) (signature []byte, err error) { alg, err := k.sigAlgorithm(opts) if err != nil { return nil, err } encoded := base64.RawURLEncoding.EncodeToString(digest) kname, kversion := k.kname, k.kversion if wantKeyID := token.KeyID(ctx); len(wantKeyID) != 0 { // reusing a key the client saw before cert := refFromKeyID(wantKeyID) if cert == nil { return nil, errors.New("invalid keyID") } kname, kversion = cert.KeyName, cert.KeyVersion } resp, err := k.cli.Sign(ctx, k.kbase, kname, kversion, keyvault.KeySignParameters{ Algorithm: keyvault.JSONWebKeySignatureAlgorithm(alg), Value: &encoded, }) if err != nil { return nil, err } sig, err := base64.RawURLEncoding.DecodeString(*resp.Result) if err != nil { return nil, err } if _, ok := k.pub.(*ecdsa.PublicKey); ok { // repack as ASN.1 unpacked, err := x509tools.UnpackEcdsaSignature(sig) if err != nil { return nil, err } sig = unpacked.Marshal() } return sig, nil } func (k *kvKey) Config() *config.KeyConfig { return k.kconf } func (k *kvKey) Certificate() []byte { return k.cert } func (k *kvKey) GetID() []byte { return k.id } func (k *kvKey) ImportCertificate(cert *x509.Certificate) error { return token.NotImplementedError{Op: "import-certificate", Type: tokenType} } // select a JOSE signature algorithm based on the public key algorithm and requested hash func func (k *kvKey) sigAlgorithm(opts crypto.SignerOpts) (string, error) { var alg string switch opts.HashFunc() { case crypto.SHA256: alg = "256" case crypto.SHA384: alg = "384" case crypto.SHA512: alg = "512" default: return "", token.KeyUsageError{ Key: k.kconf.Name(), Err: fmt.Errorf("unsupported digest algorithm %s", opts.HashFunc()), } } switch k.pub.(type) { case *rsa.PublicKey: if _, ok := opts.(*rsa.PSSOptions); ok { return "PS" + alg, nil } else { return "RS" + alg, nil } case *ecdsa.PublicKey: return "ES" + alg, nil default: return "", token.KeyUsageError{ Key: k.kconf.Name(), Err: fmt.Errorf("unsupported public key type %T", k.pub), } } } relic-7.6.1/token/context.go000066400000000000000000000004451455105530300157620ustar00rootroot00000000000000package token import "context" type ctxKey int var ctxKeyID ctxKey = 1 func WithKeyID(ctx context.Context, keyID []byte) context.Context { return context.WithValue(ctx, ctxKeyID, keyID) } func KeyID(ctx context.Context) []byte { keyID, _ := ctx.Value(ctxKeyID).([]byte) return keyID } relic-7.6.1/token/filetoken/000077500000000000000000000000001455105530300157245ustar00rootroot00000000000000relic-7.6.1/token/filetoken/filetoken.go000066400000000000000000000103001455105530300202250ustar00rootroot00000000000000// // Copyright (c) SAS Institute Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package filetoken import ( "context" "crypto" "crypto/rand" "crypto/x509" "fmt" "io" "io/ioutil" "github.com/sassoftware/relic/v7/config" "github.com/sassoftware/relic/v7/lib/certloader" "github.com/sassoftware/relic/v7/lib/passprompt" "github.com/sassoftware/relic/v7/token" ) const tokenType = "file" func init() { token.Openers[tokenType] = Open } type fileToken struct { config *config.Config tokenConf *config.TokenConfig prompt passprompt.PasswordGetter } type fileKey struct { keyConf *config.KeyConfig signer crypto.Signer cert []byte } func Open(conf *config.Config, tokenName string, prompt passprompt.PasswordGetter) (token.Token, error) { tconf, err := conf.GetToken(tokenName) if err != nil { return nil, err } return &fileToken{ config: conf, tokenConf: tconf, prompt: prompt, }, nil } func (tok *fileToken) Ping(context.Context) error { return nil } func (tok *fileToken) Close() error { return nil } func (tok *fileToken) Config() *config.TokenConfig { return tok.tokenConf } func (tok *fileToken) ListKeys(opts token.ListOptions) error { return token.NotImplementedError{Op: "list-keys", Type: tokenType} } func (tok *fileToken) GetKey(ctx context.Context, keyName string) (token.Key, error) { keyConf, err := tok.config.GetKey(keyName) if err != nil { return nil, err } if keyConf.KeyFile == "" { return nil, fmt.Errorf("key \"%s\" needs a KeyFile setting", keyName) } blob, err := ioutil.ReadFile(keyConf.KeyFile) if err != nil { return nil, err } /* TODO: keyring support loginFunc := func(pin string) (bool, error) { keyringUser := fmt.Sprintf("%s.%s", tok.tokenConf.Name(), keyName) if savedPass != "" { tok.mu.Lock() defer tok.mu.Unlock() if err := token.Login(tok.tokenConf, tok.prompt, loginFunc, keyringUser, ""); err != nil { return nil, err } } */ var privateKey crypto.PrivateKey var certBlob []byte if keyConf.IsPkcs12 { cert, err := certloader.ParsePKCS12(blob, tok.prompt) if err != nil { return nil, err } privateKey = cert.PrivateKey for _, oneCert := range cert.Chain() { certBlob = append(certBlob, oneCert.Raw...) } } else { var err error privateKey, err = certloader.ParseAnyPrivateKey(blob, tok.prompt) if err != nil { return nil, err } } return &fileKey{ keyConf: keyConf, signer: privateKey.(crypto.Signer), cert: certBlob, }, nil } func (key *fileKey) Public() crypto.PublicKey { return key.signer.Public() } func (key *fileKey) Sign(rand io.Reader, digest []byte, opts crypto.SignerOpts) ([]byte, error) { return key.signer.Sign(rand, digest, opts) } func (key *fileKey) SignContext(ctx context.Context, digest []byte, opts crypto.SignerOpts) ([]byte, error) { return key.signer.Sign(rand.Reader, digest, opts) } func (key *fileKey) Config() *config.KeyConfig { return key.keyConf } func (key *fileKey) Certificate() []byte { return key.cert } func (key *fileKey) GetID() []byte { return nil } func (tok *fileToken) Import(keyName string, privKey crypto.PrivateKey) (token.Key, error) { return nil, token.NotImplementedError{Op: "import-key", Type: tokenType} } func (tok *fileToken) ImportCertificate(cert *x509.Certificate, labelBase string) error { return token.NotImplementedError{Op: "import-certificate", Type: tokenType} } func (tok *fileToken) Generate(keyName string, keyType token.KeyType, bits uint) (token.Key, error) { // TODO - probably useful return nil, token.NotImplementedError{Op: "generate-key", Type: tokenType} } func (key *fileKey) ImportCertificate(cert *x509.Certificate) error { return token.NotImplementedError{Op: "import-certificate", Type: tokenType} } relic-7.6.1/token/gcloudtoken/000077500000000000000000000000001455105530300162625ustar00rootroot00000000000000relic-7.6.1/token/gcloudtoken/token.go000066400000000000000000000135741455105530300177430ustar00rootroot00000000000000package gcloudtoken import ( "context" "crypto" "crypto/rsa" "crypto/x509" "encoding/pem" "errors" "fmt" "io" "time" kms "cloud.google.com/go/kms/apiv1" "google.golang.org/api/option" kmspb "google.golang.org/genproto/googleapis/cloud/kms/v1" "github.com/sassoftware/relic/v7/config" "github.com/sassoftware/relic/v7/lib/passprompt" "github.com/sassoftware/relic/v7/token" ) const tokenType = "gcloud" type gcloudToken struct { config *config.Config tconf *config.TokenConfig cli *kms.KeyManagementClient } type gcloudKey struct { kconf *config.KeyConfig cli *kms.KeyManagementClient pub crypto.PublicKey hash crypto.Hash pss bool } func init() { token.Openers[tokenType] = open } func open(conf *config.Config, tokenName string, pinProvider passprompt.PasswordGetter) (token.Token, error) { tconf, err := conf.GetToken(tokenName) if err != nil { return nil, err } ctx, cancel := context.WithTimeout(context.Background(), time.Minute) defer cancel() var opts []option.ClientOption if tconf.Pin != nil { opts = append(opts, option.WithCredentialsFile(*tconf.Pin)) } cli, err := kms.NewKeyManagementClient(ctx, opts...) if err != nil { return nil, err } return &gcloudToken{ config: conf, tconf: tconf, cli: cli, }, nil } func (t *gcloudToken) Close() error { return t.cli.Close() } func (t *gcloudToken) Ping(ctx context.Context) error { // TODO return nil } func (t *gcloudToken) Config() *config.TokenConfig { return t.tconf } func (t *gcloudToken) GetKey(ctx context.Context, keyName string) (token.Key, error) { keyConf, err := t.config.GetKey(keyName) if err != nil { return nil, err } if keyConf.ID == "" { return nil, fmt.Errorf("key %q must have \"id\" set to the fully-quaified resource name of a Cloud KMS key version", keyName) } resp, err := t.cli.GetPublicKey(ctx, &kmspb.GetPublicKeyRequest{Name: keyConf.ID}) if err != nil { return nil, err } hashFunc, pss := pubKeyAlgorithm(resp) if hashFunc == 0 { return nil, fmt.Errorf("key %q: unsupported type %q", keyName, resp.Algorithm.String()) } block, _ := pem.Decode([]byte(resp.Pem)) if block == nil { return nil, errors.New("expected PEM in public key response") } else if block.Type != "PUBLIC KEY" { return nil, fmt.Errorf("expected PUBLIC KEY in response but got %q", block.Type) } pub, err := x509.ParsePKIXPublicKey(block.Bytes) if err != nil { return nil, err } return &gcloudKey{ kconf: keyConf, cli: t.cli, pub: pub, hash: hashFunc, pss: pss, }, nil } func (t *gcloudToken) Import(keyName string, privKey crypto.PrivateKey) (token.Key, error) { return nil, token.NotImplementedError{Op: "import-key", Type: tokenType} } func (t *gcloudToken) ImportCertificate(cert *x509.Certificate, labelBase string) error { return token.NotImplementedError{Op: "import-certificate", Type: tokenType} } func (t *gcloudToken) Generate(keyName string, keyType token.KeyType, bits uint) (token.Key, error) { return nil, token.NotImplementedError{Op: "generate-key", Type: tokenType} } func (t *gcloudToken) ListKeys(opts token.ListOptions) error { return token.NotImplementedError{Op: "list-keys", Type: tokenType} } func (k *gcloudKey) Public() crypto.PublicKey { return k.pub } func (k *gcloudKey) Sign(rand io.Reader, digest []byte, opts crypto.SignerOpts) (signature []byte, err error) { return k.SignContext(context.Background(), digest, opts) } func (k *gcloudKey) SignContext(ctx context.Context, digest []byte, opts crypto.SignerOpts) (signature []byte, err error) { if opts.HashFunc() != k.hash { return nil, token.KeyUsageError{ Key: k.kconf.Name(), Err: fmt.Errorf("tried to use digest %s but key requires digest %s", opts.HashFunc(), k.hash), } } if _, ok := opts.(*rsa.PSSOptions); ok && !k.pss { return nil, token.KeyUsageError{ Key: k.kconf.Name(), Err: errors.New("tried to use RSA-PSS signature but key uses PKCS#1"), } } else if k.pss && !ok { return nil, token.KeyUsageError{ Key: k.kconf.Name(), Err: errors.New("tried to use PKCS#1 signature but key uses RSA-PSS"), } } req := &kmspb.AsymmetricSignRequest{ Name: k.kconf.ID, Digest: &kmspb.Digest{}, } switch k.hash { case crypto.SHA256: req.Digest.Digest = &kmspb.Digest_Sha256{Sha256: digest} case crypto.SHA384: req.Digest.Digest = &kmspb.Digest_Sha384{Sha384: digest} case crypto.SHA512: req.Digest.Digest = &kmspb.Digest_Sha512{Sha512: digest} default: return nil, token.KeyUsageError{ Key: k.kconf.Name(), Err: fmt.Errorf("unsupported digest algorithm %s", k.hash), } } resp, err := k.cli.AsymmetricSign(ctx, req) if err != nil { return nil, err } return resp.Signature, nil } func (k *gcloudKey) Config() *config.KeyConfig { return k.kconf } func (k *gcloudKey) Certificate() []byte { return nil } func (k *gcloudKey) GetID() []byte { return nil } func (k *gcloudKey) ImportCertificate(cert *x509.Certificate) error { return token.NotImplementedError{Op: "import-certificate", Type: tokenType} } func pubKeyAlgorithm(pub *kmspb.PublicKey) (h crypto.Hash, pss bool) { switch pub.Algorithm { case kmspb.CryptoKeyVersion_RSA_SIGN_PSS_2048_SHA256: return crypto.SHA256, true case kmspb.CryptoKeyVersion_RSA_SIGN_PSS_3072_SHA256: return crypto.SHA256, true case kmspb.CryptoKeyVersion_RSA_SIGN_PSS_4096_SHA256: return crypto.SHA256, true case kmspb.CryptoKeyVersion_RSA_SIGN_PSS_4096_SHA512: return crypto.SHA512, true case kmspb.CryptoKeyVersion_RSA_SIGN_PKCS1_2048_SHA256: return crypto.SHA256, false case kmspb.CryptoKeyVersion_RSA_SIGN_PKCS1_3072_SHA256: return crypto.SHA256, false case kmspb.CryptoKeyVersion_RSA_SIGN_PKCS1_4096_SHA256: return crypto.SHA256, false case kmspb.CryptoKeyVersion_RSA_SIGN_PKCS1_4096_SHA512: return crypto.SHA512, false case kmspb.CryptoKeyVersion_EC_SIGN_P256_SHA256: return crypto.SHA256, false case kmspb.CryptoKeyVersion_EC_SIGN_P384_SHA384: return crypto.SHA384, false } return 0, false } relic-7.6.1/token/login.go000066400000000000000000000033011455105530300154000ustar00rootroot00000000000000// // Copyright (c) SAS Institute Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package token import ( "errors" "fmt" "io" "github.com/sassoftware/relic/v7/config" "github.com/sassoftware/relic/v7/lib/passprompt" "github.com/sassoftware/relic/v7/signers/sigerrors" ) func Login(tokenConf *config.TokenConfig, pinProvider passprompt.PasswordGetter, loginFunc passprompt.LoginFunc, keyringUser, initialPrompt string) error { if tokenConf.Pin != nil { ok, err := loginFunc(*tokenConf.Pin) if err != nil { return err } else if !ok { return sigerrors.PinIncorrectError{} } else { return nil } } if initialPrompt == "" { initialPrompt = fmt.Sprintf("PIN for token %s: ", tokenConf.Name()) } failPrefix := "Incorrect PIN\r\n" var keyringService string if tokenConf.UseKeyring { keyringService = "relic" } err := passprompt.Login(loginFunc, pinProvider, keyringService, keyringUser, initialPrompt, failPrefix) if err == io.EOF { if pinProvider == nil { msg := "PIN required but none was provided" if tokenConf.UseKeyring { msg += "; use 'relic ping' to save password in keyring" } return errors.New(msg) } return errors.New("Aborted") } return err } relic-7.6.1/token/open/000077500000000000000000000000001455105530300147055ustar00rootroot00000000000000relic-7.6.1/token/open/impure.go000066400000000000000000000014141455105530300165350ustar00rootroot00000000000000//go:build cgo && !pure && !clientonly // +build cgo,!pure,!clientonly // // Copyright (c) SAS Institute Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package open import ( // Token types that need cgo _ "github.com/sassoftware/relic/v7/token/p11token" ) relic-7.6.1/token/open/open.go000066400000000000000000000044571455105530300162070ustar00rootroot00000000000000// // Copyright (c) SAS Institute Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package open import ( "context" "fmt" "io" "github.com/sassoftware/relic/v7/config" "github.com/sassoftware/relic/v7/lib/passprompt" "github.com/sassoftware/relic/v7/token" // Token types that don't require cgo _ "github.com/sassoftware/relic/v7/token/awstoken" _ "github.com/sassoftware/relic/v7/token/azuretoken" _ "github.com/sassoftware/relic/v7/token/filetoken" _ "github.com/sassoftware/relic/v7/token/gcloudtoken" _ "github.com/sassoftware/relic/v7/token/scdtoken" ) func Token(cfg *config.Config, tokenName string, prompt passprompt.PasswordGetter) (token.Token, error) { tcfg, err := cfg.GetToken(tokenName) if err != nil { return nil, err } if ofunc := token.Openers[tcfg.Type]; ofunc != nil { return ofunc(cfg, tokenName, prompt) } var msg string if tcfg.Type == "pkcs11" { msg = " -- built without pkcs11 support" } return nil, fmt.Errorf("unknown token type %s%s", tcfg.Type, msg) } func Key(cfg *config.Config, keyName string, prompt passprompt.PasswordGetter) (token.Key, error) { keyConf, err := cfg.GetKey(keyName) if err != nil { return nil, err } token, err := Token(cfg, keyConf.Token, prompt) if err != nil { return nil, err } key, err := token.GetKey(context.Background(), keyName) if err != nil { token.Close() return nil, err } return key, nil } func List(tokenType, provider string, w io.Writer) error { if listFunc := token.Listers[tokenType]; listFunc != nil { return listFunc(provider, w) } if token.Openers[tokenType] != nil { return fmt.Errorf("list operation not supported for token type %s", tokenType) } var msg string if tokenType == "pkcs11" { msg = " -- built without pkcs11 support" } return fmt.Errorf("unknown token type %s%s", tokenType, msg) } relic-7.6.1/token/p11token/000077500000000000000000000000001455105530300154065ustar00rootroot00000000000000relic-7.6.1/token/p11token/byteorder.go000066400000000000000000000031361455105530300177370ustar00rootroot00000000000000// // Copyright (c) SAS Institute Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package p11token import ( "encoding/binary" "fmt" "unsafe" ) var ( nativeOrder binary.ByteOrder ulongSize int ) func init() { var i uint32 = 0x1 bs := (*[4]byte)(unsafe.Pointer(&i)) if bs[0] == 0 { nativeOrder = binary.BigEndian } else { nativeOrder = binary.LittleEndian } var j uint ulongSize = int(unsafe.Sizeof(j)) } func putUlong(buf []byte, v uint) { switch ulongSize { case 4: nativeOrder.PutUint32(buf, uint32(v)) case 8: nativeOrder.PutUint64(buf, uint64(v)) default: panic("can't determine native integer size") } } func getUlong(value []byte) (uint, error) { switch len(value) { case 8: return uint(nativeOrder.Uint64(value)), nil case 4: return uint(nativeOrder.Uint32(value)), nil case 2: return uint(nativeOrder.Uint16(value)), nil case 1: return uint(value[0]), nil } return 0, IntegerError{Raw: value} } type IntegerError struct { Raw []byte } func (e IntegerError) Error() string { return fmt.Sprintf("unable to parse value as unsigned integer: %x", e.Raw) } relic-7.6.1/token/p11token/byteorder_test.go000066400000000000000000000016431455105530300207770ustar00rootroot00000000000000package p11token import ( "encoding/binary" "testing" "github.com/stretchr/testify/assert" ) func TestUlong(t *testing.T) { values := []uint{1, 65537, 0x12345678} for _, v := range values { b := make([]byte, ulongSize) putUlong(b, v) vv, err := getUlong(b) if assert.NoError(t, err) { assert.Equal(t, v, vv) } } raw := [][]byte{ []byte("a"), []byte("ab"), []byte("abcd"), []byte("abcdefgh"), } var expect []uint if nativeOrder == binary.LittleEndian { expect = []uint{0x61, 0x6261, 0x64636261, 0x6867666564636261} } else { expect = []uint{0x61, 0x6162, 0x61626364, 0x6162636465666768} } for i, rawv := range raw { vv, err := getUlong(rawv) if assert.NoError(t, err) { assert.Equal(t, expect[i], vv) } } bad := make([]byte, 9) _, err := getUlong(bad) if assert.Error(t, err) { assert.Equal(t, "unable to parse value as unsigned integer: 000000000000000000", err.Error()) } } relic-7.6.1/token/p11token/certs.go000066400000000000000000000070131455105530300170560ustar00rootroot00000000000000// // Copyright (c) SAS Institute Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package p11token import ( "crypto/sha1" "crypto/x509" "errors" "fmt" "github.com/miekg/pkcs11" "github.com/sassoftware/relic/v7/signers/sigerrors" ) var newCertAttrs = []*pkcs11.Attribute{ pkcs11.NewAttribute(pkcs11.CKA_CLASS, pkcs11.CKO_CERTIFICATE), pkcs11.NewAttribute(pkcs11.CKA_TOKEN, true), pkcs11.NewAttribute(pkcs11.CKA_PRIVATE, false), pkcs11.NewAttribute(pkcs11.CKA_CERTIFICATE_TYPE, pkcs11.CKC_X_509), } func (tk *Token) ImportCertificate(cert *x509.Certificate, labelBase string) error { fingerprint := sha1.Sum(cert.Raw) if labelBase == "" { return errors.New("label is required") } // make a label from the private key label plus the certificate fingerprint label := fmt.Sprintf("%s_chain_%x", labelBase, fingerprint[:8]) tk.mutex.Lock() defer tk.mutex.Unlock() if err := tk.certExists(label); err != nil { return err } keyID := makeKeyID() if keyID == nil { return errors.New("failed to make key ID") } attrs := []*pkcs11.Attribute{ pkcs11.NewAttribute(pkcs11.CKA_ID, keyID), pkcs11.NewAttribute(pkcs11.CKA_LABEL, label), pkcs11.NewAttribute(pkcs11.CKA_SUBJECT, cert.RawSubject), pkcs11.NewAttribute(pkcs11.CKA_ISSUER, cert.RawIssuer), pkcs11.NewAttribute(pkcs11.CKA_VALUE, cert.Raw), } attrs = append(attrs, newCertAttrs...) _, err := tk.ctx.CreateObject(tk.sh, attrs) return err } func (tk *Token) certExists(label string) error { attrs := []*pkcs11.Attribute{ pkcs11.NewAttribute(pkcs11.CKA_CLASS, pkcs11.CKO_CERTIFICATE), pkcs11.NewAttribute(pkcs11.CKA_LABEL, label), } objects, err := tk.findObject(attrs) if err != nil { return err } else if len(objects) != 0 { return sigerrors.ErrExist } else { return nil } } func (key *Key) ImportCertificate(cert *x509.Certificate) error { keyID, handle, err := key.findCertificate() if err != nil { return err } else if handle != 0 { return sigerrors.ErrExist } label := key.getLabel() if label == "" { return errors.New("label is required") } attrs := []*pkcs11.Attribute{ pkcs11.NewAttribute(pkcs11.CKA_ID, keyID), pkcs11.NewAttribute(pkcs11.CKA_LABEL, label), pkcs11.NewAttribute(pkcs11.CKA_SUBJECT, cert.RawSubject), pkcs11.NewAttribute(pkcs11.CKA_ISSUER, cert.RawIssuer), pkcs11.NewAttribute(pkcs11.CKA_VALUE, cert.Raw), } attrs = append(attrs, newCertAttrs...) key.token.mutex.Lock() defer key.token.mutex.Unlock() _, err = key.token.ctx.CreateObject(key.token.sh, attrs) return err } func (key *Key) findCertificate() (keyID []byte, handle pkcs11.ObjectHandle, err error) { keyID = key.GetID() if len(keyID) == 0 { return nil, 0, errors.New("no keyID") } key.token.mutex.Lock() defer key.token.mutex.Unlock() attrs := []*pkcs11.Attribute{ pkcs11.NewAttribute(pkcs11.CKA_CLASS, pkcs11.CKO_CERTIFICATE), pkcs11.NewAttribute(pkcs11.CKA_ID, keyID), } objects, err := key.token.findObject(attrs) if err != nil { return nil, 0, err } if len(objects) == 0 { return keyID, 0, nil } return keyID, objects[0], nil } relic-7.6.1/token/p11token/ecdsa.go000066400000000000000000000056171455105530300170250ustar00rootroot00000000000000// // Copyright (c) SAS Institute Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package p11token import ( "crypto" "crypto/ecdsa" "errors" "github.com/miekg/pkcs11" "github.com/sassoftware/relic/v7/lib/x509tools" ) // Convert token ECDSA public key to *ecdsa.PublicKey func (key *Key) toEcdsaKey() (crypto.PublicKey, error) { ecparams := key.token.getAttribute(key.pub, pkcs11.CKA_EC_PARAMS) ecpoint := key.token.getAttribute(key.pub, pkcs11.CKA_EC_POINT) if len(ecparams) == 0 || len(ecpoint) == 0 { return nil, errors.New("Unable to retrieve ECDSA public key") } curve, err := x509tools.CurveByDer(ecparams) if err != nil { return nil, err } x, y := x509tools.DerToPoint(curve.Curve, ecpoint) if x == nil || y == nil { return nil, errors.New("Invalid elliptic curve point") } eckey := &ecdsa.PublicKey{Curve: curve.Curve, X: x, Y: y} return eckey, nil } // Sign a digest using token ECDSA private key func (key *Key) signECDSA(digest []byte) (der []byte, err error) { mech := pkcs11.NewMechanism(pkcs11.CKM_ECDSA, nil) err = key.token.ctx.SignInit(key.token.sh, []*pkcs11.Mechanism{mech}, key.priv) if err != nil { return nil, err } sig, err := key.token.ctx.Sign(key.token.sh, digest) if err != nil { return nil, err } parsed, err := x509tools.UnpackEcdsaSignature(sig) if err != nil { return nil, err } return parsed.Marshal(), nil } // Generate ECDSA-specific public and private key attributes from a PrivateKey func ecdsaImportAttrs(priv *ecdsa.PrivateKey) (pubAttrs, privAttrs []*pkcs11.Attribute, err error) { curve, err := x509tools.CurveByCurve(priv.Curve) if err != nil { return nil, nil, err } pubAttrs = []*pkcs11.Attribute{ pkcs11.NewAttribute(pkcs11.CKA_EC_PARAMS, curve.ToDer()), pkcs11.NewAttribute(pkcs11.CKA_EC_POINT, x509tools.PointToDer(&priv.PublicKey)), } privAttrs = []*pkcs11.Attribute{ pkcs11.NewAttribute(pkcs11.CKA_EC_PARAMS, curve.ToDer()), pkcs11.NewAttribute(pkcs11.CKA_VALUE, priv.D.Bytes()), } return } // Generate ECDSA-specific public attributes to generate an ECSDA key in the token func ecdsaGenerateAttrs(bits uint) ([]*pkcs11.Attribute, *pkcs11.Mechanism, error) { curve, err := x509tools.CurveByBits(bits) if err != nil { return nil, nil, err } pubAttrs := []*pkcs11.Attribute{pkcs11.NewAttribute(pkcs11.CKA_EC_PARAMS, curve.ToDer())} mech := pkcs11.NewMechanism(pkcs11.CKM_EC_KEY_PAIR_GEN, nil) return pubAttrs, mech, nil } relic-7.6.1/token/p11token/import.go000066400000000000000000000147361455105530300172620ustar00rootroot00000000000000// // Copyright (c) SAS Institute Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package p11token import ( "crypto" "crypto/ecdsa" "crypto/rand" "crypto/rsa" "crypto/x509" "encoding/hex" "errors" "fmt" "io" "github.com/miekg/pkcs11" "github.com/sassoftware/relic/v7/token" ) // Common attributes for new public keys var newPublicKeyAttrs = []*pkcs11.Attribute{ pkcs11.NewAttribute(pkcs11.CKA_CLASS, pkcs11.CKO_PUBLIC_KEY), pkcs11.NewAttribute(pkcs11.CKA_TOKEN, true), pkcs11.NewAttribute(pkcs11.CKA_PRIVATE, false), pkcs11.NewAttribute(pkcs11.CKA_VERIFY, true), } // Common attributes for new private keys var newPrivateKeyAttrs = []*pkcs11.Attribute{ pkcs11.NewAttribute(pkcs11.CKA_CLASS, pkcs11.CKO_PRIVATE_KEY), pkcs11.NewAttribute(pkcs11.CKA_TOKEN, true), pkcs11.NewAttribute(pkcs11.CKA_PRIVATE, true), pkcs11.NewAttribute(pkcs11.CKA_SENSITIVE, true), pkcs11.NewAttribute(pkcs11.CKA_EXTRACTABLE, false), pkcs11.NewAttribute(pkcs11.CKA_SIGN, true), } // Import a PKCS#8 encoded key using a random 3DES key and the Unwrap function. // For some HSMs this is the only way to import keys. func (tok *Token) importPkcs8(pk8 []byte, attrs []*pkcs11.Attribute) (err error) { // Generate a temporary 3DES key genMech := []*pkcs11.Mechanism{pkcs11.NewMechanism(pkcs11.CKM_DES3_KEY_GEN, nil)} wrapKey, err := tok.ctx.GenerateKey(tok.sh, genMech, []*pkcs11.Attribute{ pkcs11.NewAttribute(pkcs11.CKA_TOKEN, false), pkcs11.NewAttribute(pkcs11.CKA_ENCRYPT, true), pkcs11.NewAttribute(pkcs11.CKA_UNWRAP, true), }) if err != nil { return err } defer func() { err2 := tok.ctx.DestroyObject(tok.sh, wrapKey) if err2 != nil && err == nil { err = fmt.Errorf("destroying temporary key: %w", err2) } }() // Encrypt key iv := make([]byte, 8) if _, err := io.ReadFull(rand.Reader, iv); err != nil { return err } encMech := []*pkcs11.Mechanism{pkcs11.NewMechanism(pkcs11.CKM_DES3_CBC_PAD, iv)} if err := tok.ctx.EncryptInit(tok.sh, encMech, wrapKey); err != nil { return err } wrapped, err := tok.ctx.Encrypt(tok.sh, pk8) if err != nil { return err } // Unwrap key into token if _, err := tok.ctx.UnwrapKey(tok.sh, encMech, wrapKey, wrapped, attrs); err != nil { return err } return nil } // Import an RSA or ECDSA private key into the token func (tok *Token) Import(keyName string, privKey crypto.PrivateKey) (token.Key, error) { keyConf, err := tok.config.GetKey(keyName) if err != nil { return nil, err } if keyConf.Label == "" { return nil, errors.New("Key attribute 'label' must be defined in order to create an object") } keyID := makeKeyID() if keyID == nil { return nil, errors.New("failed to make key ID") } var pubTypeAttrs, privTypeAttrs []*pkcs11.Attribute var keyType uint switch priv := privKey.(type) { case *rsa.PrivateKey: keyType = pkcs11.CKK_RSA pubTypeAttrs, privTypeAttrs, err = rsaImportAttrs(priv) case *ecdsa.PrivateKey: keyType = pkcs11.CKK_ECDSA pubTypeAttrs, privTypeAttrs, err = ecdsaImportAttrs(priv) default: return nil, errors.New("Unsupported key type") } if err != nil { return nil, err } commonAttrs := []*pkcs11.Attribute{ pkcs11.NewAttribute(pkcs11.CKA_KEY_TYPE, keyType), pkcs11.NewAttribute(pkcs11.CKA_ID, keyID), pkcs11.NewAttribute(pkcs11.CKA_LABEL, keyConf.Label), } pubAttrs := attrConcat(commonAttrs, newPublicKeyAttrs, pubTypeAttrs) privAttrsSensitive := attrConcat(commonAttrs, newPrivateKeyAttrs, privTypeAttrs) pubHandle, err := tok.ctx.CreateObject(tok.sh, pubAttrs) if err != nil { return nil, err } _, err = tok.ctx.CreateObject(tok.sh, privAttrsSensitive) if err2, ok := err.(pkcs11.Error); ok && err2 == pkcs11.CKR_TEMPLATE_INCONSISTENT { // Some HSMs don't seem to allow importing private keys directly so use // key wrapping to sneak it in. Exclude the "sensitive" attrs since // only the flags, label etc. are useful for Unwrap privAttrsUnwrap := attrConcat(commonAttrs, newPrivateKeyAttrs) var pk8 []byte pk8, err = x509.MarshalPKCS8PrivateKey(privKey) if err == nil { err = tok.importPkcs8(pk8, privAttrsUnwrap) } } if err != nil { _ = tok.ctx.DestroyObject(tok.sh, pubHandle) return nil, err } keyConf.ID = hex.EncodeToString(keyID) return tok.getKey(keyConf, keyName) } // Generate an RSA or ECDSA key in the token func (tok *Token) Generate(keyName string, keyType token.KeyType, bits uint) (token.Key, error) { tok.mutex.Lock() defer tok.mutex.Unlock() keyConf, err := tok.config.GetKey(keyName) if err != nil { return nil, err } if keyConf.Label == "" { return nil, errors.New("Key attribute 'label' must be defined in order to create an object") } keyID := makeKeyID() if keyID == nil { return nil, errors.New("failed to make key ID") } var pubTypeAttrs []*pkcs11.Attribute var mech *pkcs11.Mechanism switch keyType { case token.KeyTypeRsa: pubTypeAttrs, mech, err = rsaGenerateAttrs(bits) case token.KeyTypeEcdsa: pubTypeAttrs, mech, err = ecdsaGenerateAttrs(bits) default: return nil, errors.New("Unsupported key type") } if err != nil { return nil, err } commonAttrs := []*pkcs11.Attribute{ pkcs11.NewAttribute(pkcs11.CKA_ID, keyID), pkcs11.NewAttribute(pkcs11.CKA_LABEL, keyConf.Label), } pubAttrs := attrConcat(commonAttrs, newPublicKeyAttrs, pubTypeAttrs) privAttrs := attrConcat(commonAttrs, newPrivateKeyAttrs) if _, _, err := tok.ctx.GenerateKeyPair(tok.sh, []*pkcs11.Mechanism{mech}, pubAttrs, privAttrs); err != nil { if err2, ok := err.(pkcs11.Error); ok && err2 == pkcs11.CKR_MECHANISM_INVALID && mech.Mechanism == pkcs11.CKM_RSA_X9_31_KEY_PAIR_GEN { mech.Mechanism = pkcs11.CKM_RSA_PKCS_KEY_PAIR_GEN if _, _, err := tok.ctx.GenerateKeyPair(tok.sh, []*pkcs11.Mechanism{mech}, pubAttrs, privAttrs); err != nil { return nil, err } } else { return nil, err } } keyConf.ID = hex.EncodeToString(keyID) return tok.getKey(keyConf, keyName) } func attrConcat(attrSets ...[]*pkcs11.Attribute) []*pkcs11.Attribute { ret := make([]*pkcs11.Attribute, 0) for _, attrs := range attrSets { ret = append(ret, attrs...) } return ret } relic-7.6.1/token/p11token/key.go000066400000000000000000000100071455105530300165230ustar00rootroot00000000000000// // Copyright (c) SAS Institute Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package p11token import ( "context" "crypto" "crypto/rand" "errors" "fmt" "io" "github.com/miekg/pkcs11" "github.com/sassoftware/relic/v7/config" "github.com/sassoftware/relic/v7/signers/sigerrors" "github.com/sassoftware/relic/v7/token" ) type Key struct { PgpCertificate string X509Certificate string token *Token keyConf *config.KeyConfig keyType uint pub pkcs11.ObjectHandle priv pkcs11.ObjectHandle pubParsed crypto.PublicKey } func (token *Token) GetKey(ctx context.Context, keyName string) (token.Key, error) { token.mutex.Lock() defer token.mutex.Unlock() keyConf, err := token.config.GetKey(keyName) if err != nil { return nil, err } return token.getKey(keyConf, keyName) } func (token *Token) getKey(keyConf *config.KeyConfig, keyName string) (*Key, error) { var err error key := &Key{ token: token, keyConf: keyConf, PgpCertificate: keyConf.PgpCertificate, X509Certificate: keyConf.X509Certificate, } key.priv, err = token.findKey(keyConf, pkcs11.CKO_PRIVATE_KEY) if err != nil { return nil, err } key.pub, err = token.findKey(keyConf, pkcs11.CKO_PUBLIC_KEY) if err != nil { return nil, err } keyTypeBlob := token.getAttribute(key.priv, pkcs11.CKA_KEY_TYPE) if len(keyTypeBlob) == 0 { return nil, errors.New("private key: CKA_KEY_TYPE is missing") } key.keyType, err = getUlong(keyTypeBlob) if err != nil { return nil, fmt.Errorf("private key: CKA_KEY_TYPE: %w", err) } switch key.keyType { case CKK_RSA: key.pubParsed, err = key.toRsaKey() case CKK_ECDSA: key.pubParsed, err = key.toEcdsaKey() default: return nil, errors.New("Unsupported key type") } if err != nil { return nil, err } return key, nil } func (token *Token) findKey(keyConf *config.KeyConfig, class uint) (pkcs11.ObjectHandle, error) { attrs := []*pkcs11.Attribute{ pkcs11.NewAttribute(pkcs11.CKA_CLASS, class), } if keyConf.Label != "" { attrs = append(attrs, pkcs11.NewAttribute(pkcs11.CKA_LABEL, keyConf.Label)) } if keyConf.ID != "" { keyID, err := parseKeyID(keyConf.ID) if err != nil { return 0, err } attrs = append(attrs, pkcs11.NewAttribute(pkcs11.CKA_ID, keyID)) } objects, err := token.findObject(attrs) if err != nil { return 0, err } else if len(objects) > 1 { return 0, errors.New("Multiple token objects with the specified attributes") } else if len(objects) == 0 { return 0, sigerrors.KeyNotFoundError{} } return objects[0], nil } func (key *Key) Config() *config.KeyConfig { return key.keyConf } func (key *Key) Certificate() []byte { return nil } func (key *Key) Public() crypto.PublicKey { return key.pubParsed } func (key *Key) getLabel() string { key.token.mutex.Lock() defer key.token.mutex.Unlock() return string(key.token.getAttribute(key.priv, pkcs11.CKA_LABEL)) } func (key *Key) GetID() []byte { key.token.mutex.Lock() defer key.token.mutex.Unlock() return key.token.getAttribute(key.priv, pkcs11.CKA_ID) } func (key *Key) Sign(rand io.Reader, digest []byte, opts crypto.SignerOpts) ([]byte, error) { key.token.mutex.Lock() defer key.token.mutex.Unlock() switch key.keyType { case CKK_RSA: return key.signRSA(digest, opts) case CKK_ECDSA: return key.signECDSA(digest) default: return nil, errors.New("Unsupported key type") } } func (key *Key) SignContext(ctx context.Context, digest []byte, opts crypto.SignerOpts) ([]byte, error) { return key.Sign(rand.Reader, digest, opts) } relic-7.6.1/token/p11token/list.go000066400000000000000000000132211455105530300167070ustar00rootroot00000000000000// // Copyright (c) SAS Institute Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package p11token import ( "bytes" "crypto" "crypto/x509" "encoding/base64" "errors" "fmt" "io" "strings" "github.com/miekg/pkcs11" "github.com/sassoftware/relic/v7/lib/x509tools" "github.com/sassoftware/relic/v7/token" ) var classNames = map[uint]string{ pkcs11.CKO_DATA: "data", pkcs11.CKO_CERTIFICATE: "certificate", pkcs11.CKO_PUBLIC_KEY: "public_key", pkcs11.CKO_PRIVATE_KEY: "private_key", pkcs11.CKO_SECRET_KEY: "secret_key", pkcs11.CKO_HW_FEATURE: "hw_feature", pkcs11.CKO_DOMAIN_PARAMETERS: "domain_parameters", pkcs11.CKO_MECHANISM: "mechanism", pkcs11.CKO_OTP_KEY: "otp_key", } var keyTypes = map[uint]string{ pkcs11.CKK_RSA: "rsa", pkcs11.CKK_DSA: "dsa", pkcs11.CKK_EC: "ec", } func (tok *Token) ListKeys(opts token.ListOptions) (err error) { filterKeyId, err := parseKeyID(opts.ID) if err != nil { return errors.New("invalid filter id") } tok.mutex.Lock() defer tok.mutex.Unlock() if err := tok.ctx.FindObjectsInit(tok.sh, nil); err != nil { return err } defer func() { err2 := tok.ctx.FindObjectsFinal(tok.sh) if err2 != nil && err == nil { err = err2 } }() for { objects, _, err := tok.ctx.FindObjects(tok.sh, 1) if err != nil { return err } else if len(objects) == 0 { break } for _, handle := range objects { objId := tok.getAttribute(handle, pkcs11.CKA_ID) label := tok.getAttribute(handle, pkcs11.CKA_LABEL) if opts.Label != "" && string(label) != opts.Label { continue } if len(filterKeyId) != 0 && !bytes.Equal(filterKeyId, objId) { continue } fmt.Fprintf(opts.Output, "handle 0x%08x:\n", handle) rawClass := tok.getAttribute(handle, pkcs11.CKA_CLASS) class, err := getUlong(rawClass) if name := classNames[class]; name != "" && err == nil { fmt.Fprintf(opts.Output, " class: %s\n", name) } else { fmt.Fprintf(opts.Output, " class: 0x%x\n", rawClass) } if len(objId) > 0 { fmt.Fprintf(opts.Output, " id: %s\n", formatKeyID(objId)) } if len(label) > 0 { fmt.Fprintf(opts.Output, " label: %s\n", label) } switch class { case pkcs11.CKO_PUBLIC_KEY: tok.printKey(opts, handle) case pkcs11.CKO_PRIVATE_KEY: tok.printKey(opts, handle) case pkcs11.CKO_CERTIFICATE: tok.printCertificate(opts, handle) case pkcs11.CKO_DATA: value := tok.getAttribute(handle, pkcs11.CKA_VALUE) fmt.Fprintf(opts.Output, " size: %d\n", len(value)) if opts.Values { fmt.Fprintln(opts.Output, " value: !!binary |") dumpData(opts.Output, value) } } fmt.Fprintln(opts.Output) } } return nil } func (tok *Token) printKey(opts token.ListOptions, handle pkcs11.ObjectHandle) { rawKeyType := tok.getAttribute(handle, pkcs11.CKA_KEY_TYPE) keyType, err := getUlong(rawKeyType) if name := keyTypes[keyType]; name != "" && err == nil { fmt.Fprintf(opts.Output, " type: %s\n", name) } else { fmt.Fprintf(opts.Output, " type: 0x%x\n", keyType) } switch keyType { case pkcs11.CKK_RSA: if n := tok.getAttribute(handle, pkcs11.CKA_MODULUS); len(n) != 0 { fmt.Fprintf(opts.Output, " bits: %d\n", len(n)*8) if opts.Values { fmt.Fprintf(opts.Output, " n: 0x%x\n", bytesToBig(n)) } } if e := tok.getAttribute(handle, pkcs11.CKA_PUBLIC_EXPONENT); len(e) != 0 && opts.Values { fmt.Fprintf(opts.Output, " e: %s\n", bytesToBig(e)) } case pkcs11.CKK_EC: ecparams := tok.getAttribute(handle, pkcs11.CKA_EC_PARAMS) if len(ecparams) == 0 { return } curve, err := x509tools.CurveByDer(ecparams) if err != nil { fmt.Fprintf(opts.Output, " curve: %x\n", ecparams) return } fmt.Fprintf(opts.Output, " bits: %d\n", curve.Bits) ecpoint := tok.getAttribute(handle, pkcs11.CKA_EC_POINT) if len(ecpoint) > 0 && opts.Values { x, y := x509tools.DerToPoint(curve.Curve, ecpoint) if x != nil { fmt.Fprintf(opts.Output, " x: 0x%x\n", x) fmt.Fprintf(opts.Output, " y: 0x%x\n", y) } } } } func (tok *Token) printCertificate(opts token.ListOptions, handle pkcs11.ObjectHandle) { blob := tok.getAttribute(handle, pkcs11.CKA_VALUE) if len(blob) == 0 { fmt.Fprintln(opts.Output, "certificate is missing") return } cert, err := x509.ParseCertificate(blob) if err != nil { fmt.Fprintln(opts.Output, "certificate is invalid:", err) } d := crypto.SHA1.New() d.Write(blob) fmt.Fprintf(opts.Output, " subject: %s\n issuer: %s\n sha1: %x\n", x509tools.FormatSubject(cert), x509tools.FormatIssuer(cert), d.Sum(nil)) if opts.Values { fmt.Fprintln(opts.Output, " value: |\n -----BEGIN CERTIFICATE-----") dumpData(opts.Output, blob) fmt.Fprintln(opts.Output, " -----END CERTIFICATE-----") } } func formatKeyID(keyID []byte) string { chunks := make([]string, len(keyID)) for i, j := range keyID { chunks[i] = fmt.Sprintf("%02x", j) } return strings.Join(chunks, ":") } func dumpData(w io.Writer, d []byte) { encoded := base64.StdEncoding.EncodeToString(d) for len(encoded) > 0 { n := 64 if n > len(encoded) { n = len(encoded) } fmt.Fprintln(w, " ", encoded[:n]) encoded = encoded[n:] } } relic-7.6.1/token/p11token/rsa.go000066400000000000000000000116661455105530300165340ustar00rootroot00000000000000// // Copyright (c) SAS Institute Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package p11token import ( "crypto" "crypto/rsa" "errors" "math" "math/big" "github.com/miekg/pkcs11" "github.com/sassoftware/relic/v7/lib/x509tools" ) // Convert token RSA public key to *rsa.PublicKey func (key *Key) toRsaKey() (crypto.PublicKey, error) { modulus := key.token.getAttribute(key.pub, pkcs11.CKA_MODULUS) exponent := key.token.getAttribute(key.pub, pkcs11.CKA_PUBLIC_EXPONENT) if len(modulus) == 0 || len(exponent) == 0 { return nil, errors.New("unable to retrieve RSA public key") } n := new(big.Int).SetBytes(modulus) e := new(big.Int).SetBytes(exponent) eInt := e.Int64() if !e.IsInt64() || eInt > math.MaxInt || eInt < 3 { return nil, errors.New("RSA exponent is out of bounds") } return &rsa.PublicKey{N: n, E: int(eInt)}, nil } func (key *Key) newPssMech(opts *rsa.PSSOptions) (*pkcs11.Mechanism, error) { var hashAlg, mgfType uint switch opts.Hash { case crypto.SHA1: hashAlg = pkcs11.CKM_SHA_1 mgfType = pkcs11.CKG_MGF1_SHA1 case crypto.SHA224: hashAlg = pkcs11.CKM_SHA224 mgfType = pkcs11.CKG_MGF1_SHA224 case crypto.SHA256: hashAlg = pkcs11.CKM_SHA256 mgfType = pkcs11.CKG_MGF1_SHA256 case crypto.SHA384: hashAlg = pkcs11.CKM_SHA384 mgfType = pkcs11.CKG_MGF1_SHA384 case crypto.SHA512: hashAlg = pkcs11.CKM_SHA512 mgfType = pkcs11.CKG_MGF1_SHA512 default: return nil, errors.New("unsupported hash type for PSS") } saltLength := opts.SaltLength switch saltLength { case rsa.PSSSaltLengthAuto: pub := key.pubParsed.(*rsa.PublicKey) saltLength = (pub.N.BitLen()+7)/8 - 2 - opts.Hash.Size() case rsa.PSSSaltLengthEqualsHash: saltLength = opts.Hash.Size() } args := make([]byte, ulongSize*3) putUlong(args, hashAlg) putUlong(args[ulongSize:], mgfType) putUlong(args[ulongSize*2:], uint(saltLength)) return pkcs11.NewMechanism(pkcs11.CKM_RSA_PKCS_PSS, args), nil } // Sign a digest using token RSA private key func (key *Key) signRSA(digest []byte, opts crypto.SignerOpts) ([]byte, error) { var mech *pkcs11.Mechanism if opts == nil || opts.HashFunc() == 0 { return nil, errors.New("signer options are required") } else if pss, ok := opts.(*rsa.PSSOptions); ok { var err error mech, err = key.newPssMech(pss) if err != nil { return nil, err } } else { var ok bool digest, ok = x509tools.MarshalDigest(opts.HashFunc(), digest) if !ok { return nil, errors.New("unsupported hash function") } mech = pkcs11.NewMechanism(pkcs11.CKM_RSA_PKCS, nil) } err := key.token.ctx.SignInit(key.token.sh, []*pkcs11.Mechanism{mech}, key.priv) if err != nil { return nil, err } return key.token.ctx.Sign(key.token.sh, digest) } // Generate RSA-specific public and private key attributes from a PrivateKey func rsaImportAttrs(priv *rsa.PrivateKey) (pubAttrs, privAttrs []*pkcs11.Attribute, err error) { if len(priv.Primes) != 2 || priv.Precomputed.Dp == nil || priv.Precomputed.Dq == nil || priv.Precomputed.Qinv == nil { // multi-prime keys and keys without the precomputed values are rare // enough not to be interesting return nil, nil, errors.New("unsupported RSA key") } pubAttrs = []*pkcs11.Attribute{ pkcs11.NewAttribute(pkcs11.CKA_PUBLIC_EXPONENT, big.NewInt(int64(priv.E)).Bytes()), pkcs11.NewAttribute(pkcs11.CKA_MODULUS, priv.N.Bytes()), } privAttrs = []*pkcs11.Attribute{ pkcs11.NewAttribute(pkcs11.CKA_PUBLIC_EXPONENT, big.NewInt(int64(priv.E)).Bytes()), pkcs11.NewAttribute(pkcs11.CKA_MODULUS, priv.N.Bytes()), pkcs11.NewAttribute(pkcs11.CKA_PRIVATE_EXPONENT, priv.D.Bytes()), pkcs11.NewAttribute(pkcs11.CKA_PRIME_1, priv.Primes[0].Bytes()), pkcs11.NewAttribute(pkcs11.CKA_PRIME_2, priv.Primes[1].Bytes()), pkcs11.NewAttribute(pkcs11.CKA_EXPONENT_1, priv.Precomputed.Dp.Bytes()), pkcs11.NewAttribute(pkcs11.CKA_EXPONENT_2, priv.Precomputed.Dq.Bytes()), pkcs11.NewAttribute(pkcs11.CKA_COEFFICIENT, priv.Precomputed.Qinv.Bytes()), } return } // Generate RSA-specific public attributes to generate an RSA key in the token func rsaGenerateAttrs(bits uint) ([]*pkcs11.Attribute, *pkcs11.Mechanism, error) { if bits < 1024 || bits > 4096 { return nil, nil, errors.New("unsupported number of bits") } pubExponent := []byte{1, 0, 1} // 65537 attrs := []*pkcs11.Attribute{ pkcs11.NewAttribute(pkcs11.CKA_MODULUS_BITS, bits), pkcs11.NewAttribute(pkcs11.CKA_PUBLIC_EXPONENT, pubExponent), } mech := pkcs11.NewMechanism(pkcs11.CKM_RSA_X9_31_KEY_PAIR_GEN, nil) return attrs, mech, nil } relic-7.6.1/token/p11token/token.go000066400000000000000000000161721455105530300170640ustar00rootroot00000000000000// // Copyright (c) SAS Institute Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package p11token import ( "context" "errors" "fmt" "io" "runtime" "sync" "github.com/miekg/pkcs11" "github.com/sassoftware/relic/v7/config" "github.com/sassoftware/relic/v7/lib/passprompt" "github.com/sassoftware/relic/v7/signers/sigerrors" "github.com/sassoftware/relic/v7/token" ) const ( CKS_RO_PUBLIC_SESSION = 0 CKS_RO_USER_FUNCTIONS = 1 CKS_RW_PUBLIC_SESSION = 2 CKS_RW_USER_FUNCTIONS = 3 CKS_RW_SO_FUNCTIONS = 4 CKA_ID = pkcs11.CKA_ID CKA_LABEL = pkcs11.CKA_LABEL CKA_SERIAL_NUMBER = pkcs11.CKA_SERIAL_NUMBER CKK_RSA = pkcs11.CKK_RSA CKK_ECDSA = pkcs11.CKK_ECDSA ) func init() { token.Openers["pkcs11"] = open token.Listers["pkcs11"] = List } var providerMap map[string]*pkcs11.Ctx var providerMutex sync.Mutex type Token struct { config *config.Config tokenConf *config.TokenConfig ctx *pkcs11.Ctx sh pkcs11.SessionHandle mutex sync.Mutex } func List(provider string, output io.Writer) error { ctx := pkcs11.New(provider) if ctx == nil { return errors.New("Failed to initialize pkcs11 provider") } defer ctx.Destroy() if err := ctx.Initialize(); err != nil { return err } slots, err := ctx.GetSlotList(false) if err != nil { return err } for _, slot := range slots { info, err := ctx.GetTokenInfo(slot) if rv, ok := err.(pkcs11.Error); ok && rv == pkcs11.CKR_TOKEN_NOT_PRESENT { continue } fmt.Fprintf(output, "slot %d:\n manuf: %s\n model: %s\n label: %s\n serial: %s\n", slot, info.ManufacturerID, info.Model, info.Label, info.SerialNumber) } return nil } // Load a PKCS#11 provider, open a session, and login func Open(config *config.Config, tokenName string, pinProvider passprompt.PasswordGetter) (*Token, error) { tokenConf, err := config.GetToken(tokenName) if err != nil { return nil, err } ctx, err := openLib(tokenConf, true) if err != nil { return nil, err } tok := &Token{ ctx: ctx, config: config, tokenConf: tokenConf, } runtime.SetFinalizer(tok, (*Token).Close) slot, err := tok.findSlot() if err != nil { tok.Close() return nil, err } mode := uint(pkcs11.CKF_SERIAL_SESSION | pkcs11.CKF_RW_SESSION) sh, err := tok.ctx.OpenSession(slot, mode) if err != nil { tok.Close() return nil, err } tok.sh = sh err = tok.autoLogIn(pinProvider) if err != nil { tok.Close() return nil, err } return tok, nil } // compat shim for token.Openers func open(cfg *config.Config, tokenName string, prompt passprompt.PasswordGetter) (token.Token, error) { return Open(cfg, tokenName, prompt) } func openLib(tokenConf *config.TokenConfig, write bool) (*pkcs11.Ctx, error) { if tokenConf.Provider == "" { return nil, errors.New("Missing attribute \"provider\" in token configuration") } providerMutex.Lock() defer providerMutex.Unlock() if providerMap == nil { providerMap = make(map[string]*pkcs11.Ctx) } ctx, ok := providerMap[tokenConf.Provider] if ok { return ctx, nil } ctx = pkcs11.New(tokenConf.Provider) if ctx == nil { return nil, errors.New("Failed to initialize pkcs11 provider") } err := ctx.Initialize() if err != nil { ctx.Destroy() return nil, err } providerMap[tokenConf.Provider] = ctx return ctx, nil } // Close the token session func (tok *Token) Close() error { tok.mutex.Lock() defer tok.mutex.Unlock() var err error if tok.ctx != nil { err = tok.ctx.CloseSession(tok.sh) tok.ctx = nil runtime.SetFinalizer(tok, nil) } return err } func (tok *Token) Config() *config.TokenConfig { return tok.tokenConf } func (tok *Token) findSlot() (uint, error) { tokenConf := tok.tokenConf slots, err := tok.ctx.GetSlotList(false) if err != nil { return 0, nil } candidates := make([]uint, 0, len(slots)) for _, slot := range slots { info, err := tok.ctx.GetTokenInfo(slot) if err != nil { if rv, ok := err.(pkcs11.Error); ok && rv == pkcs11.CKR_TOKEN_NOT_PRESENT { continue } return 0, err } if tokenConf.Label != "" && tokenConf.Label != info.Label { continue } else if tokenConf.Serial != "" && tokenConf.Serial != info.SerialNumber { continue } candidates = append(candidates, slot) } if len(candidates) == 0 { return 0, errors.New("No token found with the specified attributes") } else if len(candidates) != 1 { return 0, errors.New("Multiple tokens matched the specified attributes") } else { return candidates[0], nil } } // Test that the token is responding and the user is (still) logged in func (tok *Token) isLoggedIn() (bool, error) { tok.mutex.Lock() defer tok.mutex.Unlock() info, err := tok.ctx.GetSessionInfo(tok.sh) if err != nil { return false, err } return (info.State == CKS_RO_USER_FUNCTIONS || info.State == CKS_RW_USER_FUNCTIONS || info.State == CKS_RW_SO_FUNCTIONS), nil } func (tok *Token) Ping(ctx context.Context) error { loggedIn, err := tok.isLoggedIn() if err != nil { return err } else if !loggedIn { return errors.New("token not logged in") } return nil } func (tok *Token) login(user uint, pin string) error { tok.mutex.Lock() defer tok.mutex.Unlock() err := tok.ctx.Login(tok.sh, user, pin) if err != nil { if rv, ok := err.(pkcs11.Error); ok && rv == pkcs11.CKR_PIN_INCORRECT { return sigerrors.PinIncorrectError{} } } return err } func (tok *Token) autoLogIn(pinProvider passprompt.PasswordGetter) error { tokenConf := tok.tokenConf loggedIn, err := tok.isLoggedIn() if err != nil { return err } if loggedIn { return nil } var user uint = pkcs11.CKU_USER if tokenConf.User != nil { user = *tokenConf.User } loginFunc := func(pin string) (bool, error) { if err := tok.login(user, pin); err == nil { return true, nil } else if _, ok := err.(sigerrors.PinIncorrectError); ok { return false, nil } else { return false, err } } initialPrompt := fmt.Sprintf("PIN for token %s user %08x: ", tokenConf.Name(), user) keyringUser := fmt.Sprintf("%s.%08x", tokenConf.Name(), user) return token.Login(tokenConf, pinProvider, loginFunc, keyringUser, initialPrompt) } func (tok *Token) getAttribute(handle pkcs11.ObjectHandle, attr uint) []byte { attrs, err := tok.ctx.GetAttributeValue(tok.sh, handle, []*pkcs11.Attribute{pkcs11.NewAttribute(attr, nil)}) if err != nil { return nil } return attrs[0].Value } func (tok *Token) findObject(attrs []*pkcs11.Attribute) ([]pkcs11.ObjectHandle, error) { if err := tok.ctx.FindObjectsInit(tok.sh, attrs); err != nil { return nil, err } objects, _, err := tok.ctx.FindObjects(tok.sh, 10) if err != nil { _ = tok.ctx.FindObjectsFinal(tok.sh) return nil, err } if err := tok.ctx.FindObjectsFinal(tok.sh); err != nil { return nil, err } return objects, nil } relic-7.6.1/token/p11token/util.go000066400000000000000000000020011455105530300167030ustar00rootroot00000000000000// // Copyright (c) SAS Institute Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package p11token import ( "crypto/rand" "encoding/hex" "io" "math/big" "strings" ) func makeKeyID() []byte { keyID := make([]byte, 20) if _, err := io.ReadFull(rand.Reader, keyID); err != nil { return nil } return keyID } func parseKeyID(value string) ([]byte, error) { return hex.DecodeString(strings.ReplaceAll(value, ":", "")) } func bytesToBig(val []byte) *big.Int { return new(big.Int).SetBytes(val) } relic-7.6.1/token/register.go000066400000000000000000000017171455105530300161250ustar00rootroot00000000000000// // Copyright (c) SAS Institute Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package token import ( "io" "github.com/sassoftware/relic/v7/config" "github.com/sassoftware/relic/v7/lib/passprompt" ) type ( OpenFunc func(cfg *config.Config, tokenName string, prompt passprompt.PasswordGetter) (Token, error) ListFunc func(provider string, dest io.Writer) error ) var ( Openers = make(map[string]OpenFunc) Listers = make(map[string]ListFunc) ) relic-7.6.1/token/scdtoken/000077500000000000000000000000001455105530300155565ustar00rootroot00000000000000relic-7.6.1/token/scdtoken/scdtoken.go000066400000000000000000000157551455105530300177340ustar00rootroot00000000000000// // Copyright (c) SAS Institute Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package scdtoken import ( "context" "crypto" "crypto/ecdsa" "crypto/rand" "crypto/rsa" "crypto/x509" "errors" "fmt" "io" "os" "strings" "sync" "github.com/sassoftware/relic/v7/config" "github.com/sassoftware/relic/v7/lib/assuan" "github.com/sassoftware/relic/v7/lib/passprompt" "github.com/sassoftware/relic/v7/lib/x509tools" "github.com/sassoftware/relic/v7/signers/sigerrors" "github.com/sassoftware/relic/v7/token" ) const tokenType = "scdtoken" func init() { token.Openers[tokenType] = Open token.Listers[tokenType] = List } var defaultScdSockets = []string{ "/run/user/$UID/gnupg/S.scdaemon", "/var/run/user/$UID/gnupg/S.scdaemon", "$HOME/.gnupg/S.scdaemon", } type scdToken struct { config *config.Config tokenConf *config.TokenConfig sock *assuan.ScdConn serial, pin string keyInfos []*assuan.ScdKey mu sync.Mutex } type scdKey struct { token *scdToken keyConf *config.KeyConfig key *assuan.ScdKey publicKey crypto.PublicKey } func findSock() string { uid := fmt.Sprintf("%d", os.Getuid()) for _, fp := range defaultScdSockets { fp = strings.ReplaceAll(fp, "$UID", uid) fp = os.ExpandEnv(fp) _, err := os.Stat(fp) if err == nil { return fp } } return "" } func List(sockPath string, output io.Writer) error { if sockPath == "" { sockPath = findSock() } if sockPath == "" { return errors.New("scdaemon not found; provide an explicit path to the scdaemon socket") } sock, err := assuan.DialScd(sockPath) if err != nil { return err } defer sock.Close() keyInfos, err := sock.Learn() if err != nil { return err } if len(keyInfos) == 0 { fmt.Fprintln(output, "token is empty or missing") } else { fmt.Fprintf(output, "serial: %s\n", keyInfos[0].Serial) } return nil } func Open(conf *config.Config, tokenName string, prompt passprompt.PasswordGetter) (token.Token, error) { tconf, err := conf.GetToken(tokenName) if err != nil { return nil, err } sockPath := tconf.Provider if sockPath == "" { sockPath = findSock() } if sockPath == "" { return nil, fmt.Errorf("scdaemon not found; set tokens.%s.provider to the path to the scdaemon socket", tokenName) } sock, err := assuan.DialScd(sockPath) if err != nil { return nil, err } tok := &scdToken{ config: conf, tokenConf: tconf, sock: sock, } if err := tok.login(prompt); err != nil { sock.Close() return nil, err } return tok, nil } func (tok *scdToken) login(prompt passprompt.PasswordGetter) error { tconf := tok.tokenConf keyInfos, err := tok.sock.Learn() if err != nil { return err } tok.keyInfos = keyInfos tok.serial = keyInfos[0].Serial if tconf.Serial != "" && tconf.Serial != tok.serial { return fmt.Errorf("scdaemon token %s has serial %s but configuration specifies %s", tconf.Name(), tok.serial, tconf.Serial) } loginFunc := func(pin string) (bool, error) { if err := tok.sock.CheckPin(pin); err == nil { tok.pin = pin return true, nil } else if _, ok := err.(sigerrors.PinIncorrectError); ok { return false, nil } else { return false, err } } initialPrompt := fmt.Sprintf("PIN for token %s (serial %s): ", tconf.Name(), tok.serial) keyringUser := tok.serial return token.Login(tconf, prompt, loginFunc, keyringUser, initialPrompt) } func (tok *scdToken) Ping(ctx context.Context) error { tok.mu.Lock() defer tok.mu.Unlock() // TODO return nil } func (tok *scdToken) Close() error { tok.mu.Lock() defer tok.mu.Unlock() if tok.sock != nil { tok.sock.Close() tok.sock = nil } return nil } func (tok *scdToken) Config() *config.TokenConfig { return tok.tokenConf } func (tok *scdToken) ListKeys(opts token.ListOptions) error { tok.mu.Lock() defer tok.mu.Unlock() fmt.Fprintf(opts.Output, "serial: %#v\n", tok.serial) for i, key := range tok.keyInfos { if opts.ID != "" && opts.ID != key.KeyId { continue } fmt.Fprintf(opts.Output, "key %d:\n id: %s\n fingerprint: %s\n keygrip: %s\n", i+1, key.KeyId, key.Fingerprint, key.KeyGrip) if opts.Values { pub, err := key.Public() if err != nil { fmt.Fprintln(opts.Output, " error reading key:", err) continue } switch k := pub.(type) { case *rsa.PublicKey: fmt.Fprintf(opts.Output, " n: 0x%x\n e: %d\n", k.N, k.E) case *ecdsa.PublicKey: curve, err := x509tools.CurveByCurve(k.Curve) if err == nil { fmt.Fprintf(opts.Output, " bits: %d\n", curve.Bits) } fmt.Fprintf(opts.Output, " x: %x\n y: %x\n", k.X, k.Y) } } } return nil } func (tok *scdToken) GetKey(ctx context.Context, keyName string) (token.Key, error) { tok.mu.Lock() defer tok.mu.Unlock() keyConf, err := tok.config.GetKey(keyName) if err != nil { return nil, err } var key *assuan.ScdKey for _, kc := range tok.keyInfos { if keyConf.ID != "" && keyConf.ID != kc.KeyId { continue } key = kc break } if key.KeyId == "" { return nil, fmt.Errorf("key %s not found in token %s", keyName, tok.tokenConf.Name()) } pubkey, err := key.Public() if err != nil { return nil, err } return &scdKey{ token: tok, keyConf: keyConf, key: key, publicKey: pubkey, }, nil } func (key *scdKey) Public() crypto.PublicKey { return key.publicKey } func (key *scdKey) Sign(rand io.Reader, digest []byte, opts crypto.SignerOpts) ([]byte, error) { key.token.mu.Lock() defer key.token.mu.Unlock() return key.key.Sign(digest, opts, key.token.pin) } func (key *scdKey) SignContext(ctx context.Context, digest []byte, opts crypto.SignerOpts) ([]byte, error) { return key.Sign(rand.Reader, digest, opts) } func (key *scdKey) Config() *config.KeyConfig { return key.keyConf } func (key *scdKey) Certificate() []byte { return nil } func (key *scdKey) GetID() []byte { return []byte(key.token.serial) } func (tok *scdToken) Import(keyName string, privKey crypto.PrivateKey) (token.Key, error) { return nil, token.NotImplementedError{Op: "import-key", Type: tokenType} } func (tok *scdToken) ImportCertificate(cert *x509.Certificate, labelBase string) error { return token.NotImplementedError{Op: "import-certificate", Type: tokenType} } func (tok *scdToken) Generate(keyName string, keyType token.KeyType, bits uint) (token.Key, error) { // TODO - probably useful return nil, token.NotImplementedError{Op: "generate-key", Type: tokenType} } func (key *scdKey) ImportCertificate(cert *x509.Certificate) error { return token.NotImplementedError{Op: "import-certificate", Type: tokenType} } relic-7.6.1/token/token.go000066400000000000000000000051031455105530300154120ustar00rootroot00000000000000// // Copyright (c) SAS Institute Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package token import ( "context" "crypto" "crypto/x509" "fmt" "io" "github.com/sassoftware/relic/v7/config" ) type KeyType uint const ( // Values match CKK_RSA etc. KeyTypeRsa KeyType = 0 KeyTypeEcdsa KeyType = 3 ) type Token interface { io.Closer // Check that the token is still alive Ping(ctx context.Context) error // Return the token config object used to instantiate this token Config() *config.TokenConfig // Get a key from the token by its config alias GetKey(ctx context.Context, keyName string) (Key, error) // Import a public+private keypair into the token Import(keyName string, privKey crypto.PrivateKey) (Key, error) // Import an issuer certificate into the token. The new object label will // be labelBase plus the fingerprint of the certificate. ImportCertificate(cert *x509.Certificate, labelBase string) error // Generate a new key in the token Generate(keyName string, keyType KeyType, bits uint) (Key, error) // Print key info ListKeys(opts ListOptions) error } type Key interface { crypto.Signer SignContext(context.Context, []byte, crypto.SignerOpts) ([]byte, error) // Return the key config object used to instantiate this key Config() *config.KeyConfig // Return the X509 certificate chain stored in the token, if any Certificate() []byte // Get the CKK_ID or equivalent for the key GetID() []byte // Import a leaf certificate for this key ImportCertificate(cert *x509.Certificate) error } type ListOptions struct { // Destination stream Output io.Writer // Filter by attributes Label string ID string // Print key and certificate contents Values bool } type NotImplementedError struct { Op, Type string } func (e NotImplementedError) Error() string { return fmt.Sprintf("operation %s not implemented for tokens of type %s", e.Op, e.Type) } type KeyUsageError struct { Key string Err error } func (e KeyUsageError) Error() string { return fmt.Sprintf("key %q: %+v", e.Key, e.Err) } func (e KeyUsageError) Unwrap() error { return e.Err } relic-7.6.1/token/tokencache/000077500000000000000000000000001455105530300160505ustar00rootroot00000000000000relic-7.6.1/token/tokencache/cache.go000066400000000000000000000023431455105530300174440ustar00rootroot00000000000000package tokencache import ( "bytes" "context" "sync" "time" "github.com/sassoftware/relic/v7/token" ) // Cache keys fetched from an underlying token type Cache struct { token.Token keys map[string]cachedKey mu sync.Mutex expiry time.Duration } func New(base token.Token, expiry time.Duration) *Cache { return &Cache{ Token: base, keys: make(map[string]cachedKey), expiry: expiry, } } type cachedKey struct { expires time.Time key token.Key } func (c *Cache) GetKey(ctx context.Context, keyName string) (token.Key, error) { c.mu.Lock() defer c.mu.Unlock() wantKeyID := token.KeyID(ctx) cached := c.keys[keyName] if cached.key != nil && cached.expires.After(time.Now()) { // if caller is looking for a particular key ID, make sure the cached // one matches before returning it haveKeyID := cached.key.GetID() if len(wantKeyID) == 0 || bytes.Equal(wantKeyID, haveKeyID) { return cached.key, nil } } key, err := c.Token.GetKey(ctx, keyName) if err != nil { return nil, err } if c.expiry > 0 && len(wantKeyID) == 0 { // only cache if the caller did not request a specific key ID c.keys[keyName] = cachedKey{ expires: time.Now().Add(c.expiry), key: key, } } return key, nil } relic-7.6.1/token/tokencache/metrics.go000066400000000000000000000047251455105530300200550ustar00rootroot00000000000000package tokencache import ( "context" "crypto" "errors" "io" "net/http" "strconv" "time" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promauto" "github.com/sassoftware/relic/v7/internal/httperror" "github.com/sassoftware/relic/v7/token" ) var ( buckets = []float64{.01, .025, .05, .1, .25, .5, 1, 2.5, 5, 10, 30, 60} MetricOperations = promauto.NewHistogramVec( prometheus.HistogramOpts{ Name: "token_operation_seconds", Help: "A histogram of latencies for token operations", Buckets: buckets, }, []string{"token", "op"}, ) MetricResponses = promauto.NewCounterVec( prometheus.CounterOpts{ Name: "token_responses", Help: "Response codes from token operations", }, []string{"token", "op", "code"}, ) ) // Metrics wraps a token and updates metrics when methods are called type Metrics struct { token.Token } func observe(name, op string, start time.Time, err error) { dur := time.Since(start).Seconds() var code int switch { case err == nil: code = http.StatusOK case errors.Is(err, context.DeadlineExceeded): code = http.StatusGatewayTimeout case errors.Is(err, context.Canceled): code = 499 case httperror.Temporary(err): code = http.StatusServiceUnavailable default: code = http.StatusInternalServerError } scode := strconv.FormatInt(int64(code), 10) MetricOperations.WithLabelValues(name, op).Observe(dur) MetricResponses.WithLabelValues(name, op, scode).Inc() } func (m Metrics) Ping(ctx context.Context) (err error) { defer func(start time.Time) { observe(m.Token.Config().Name(), "ping", start, err) }(time.Now()) return m.Token.Ping(ctx) } func (m Metrics) GetKey(ctx context.Context, keyName string) (key token.Key, err error) { defer func(start time.Time) { observe(m.Token.Config().Name(), "getKey", start, err) }(time.Now()) key, err = m.Token.GetKey(ctx, keyName) if err == nil { key = metricsKey{Key: key} } return } type metricsKey struct { token.Key } func (k metricsKey) Sign(rand io.Reader, digest []byte, opts crypto.SignerOpts) (sig []byte, err error) { defer func(start time.Time) { observe(k.Config().Token, "sign", start, err) }(time.Now()) return k.Key.Sign(rand, digest, opts) } func (k metricsKey) SignContext(ctx context.Context, digest []byte, opts crypto.SignerOpts) (sig []byte, err error) { defer func(start time.Time) { observe(k.Config().Token, "sign", start, err) }(time.Now()) return k.Key.SignContext(ctx, digest, opts) } relic-7.6.1/token/tokencache/ratelimit.go000066400000000000000000000036201455105530300203720ustar00rootroot00000000000000package tokencache import ( "context" "crypto" "io" "time" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promauto" "github.com/sassoftware/relic/v7/token" "golang.org/x/time/rate" ) var metricRateLimited = promauto.NewCounter(prometheus.CounterOpts{ Name: "token_operation_limited_seconds", Help: "Cumulative number of seconds waiting for rate limits", }) type RateLimited struct { token.Token limit *rate.Limiter } func NewLimiter(base token.Token, limit float64, burst int) *RateLimited { if burst < 1 { burst = 1 } return &RateLimited{ Token: base, limit: rate.NewLimiter(rate.Limit(limit), burst), } } type rateLimitedKey struct { token.Key limit *rate.Limiter } func (r *RateLimited) GetKey(ctx context.Context, keyName string) (token.Key, error) { start := time.Now() if err := r.limit.Wait(ctx); err != nil { return nil, err } if waited := time.Since(start); waited > 1*time.Millisecond { metricRateLimited.Add(time.Since(start).Seconds()) } key, err := r.Token.GetKey(ctx, keyName) if err != nil { return nil, err } return &rateLimitedKey{ Key: key, limit: r.limit, }, nil } func (k *rateLimitedKey) Sign(rand io.Reader, digest []byte, opts crypto.SignerOpts) (sig []byte, err error) { start := time.Now() if err := k.limit.Wait(context.Background()); err != nil { return nil, err } if waited := time.Since(start); waited > 1*time.Millisecond { metricRateLimited.Add(time.Since(start).Seconds()) } return k.Key.Sign(rand, digest, opts) } func (k *rateLimitedKey) SignContext(ctx context.Context, digest []byte, opts crypto.SignerOpts) (sig []byte, err error) { start := time.Now() if err := k.limit.Wait(ctx); err != nil { return nil, err } if waited := time.Since(start); waited > 1*time.Millisecond { metricRateLimited.Add(time.Since(start).Seconds()) } return k.Key.SignContext(ctx, digest, opts) } relic-7.6.1/token/worker/000077500000000000000000000000001455105530300152555ustar00rootroot00000000000000relic-7.6.1/token/worker/client.go000066400000000000000000000075751455105530300171000ustar00rootroot00000000000000// // Copyright (c) SAS Institute Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package worker import ( "bytes" "context" "crypto" "crypto/rsa" "crypto/x509" "encoding/json" "io" "net/http" "net/url" "time" "github.com/sassoftware/relic/v7/config" "github.com/sassoftware/relic/v7/internal/workerrpc" "github.com/sassoftware/relic/v7/token" ) const ( defaultRetries = 5 defaultTimeout = 60 * time.Second tokenType = "worker" ) func (t *WorkerToken) request(ctx context.Context, path string, rr workerrpc.Request) (*workerrpc.Response, error) { req := &http.Request{ Method: http.MethodPost, URL: &url.URL{Scheme: "http", Host: t.addr, Path: path}, Header: http.Header{"Auth-Cookie": []string{t.cookie}}, } blob, err := json.Marshal(rr) if err != nil { return nil, err } req.GetBody = func() (io.ReadCloser, error) { return io.NopCloser(bytes.NewReader(blob)), nil } return t.doRetry(req.WithContext(ctx)) } func (t *WorkerToken) Ping(ctx context.Context) error { _, err := t.request(ctx, workerrpc.Ping, workerrpc.Request{}) return err } func (t *WorkerToken) Config() *config.TokenConfig { return t.tconf } func (t *WorkerToken) GetKey(ctx context.Context, keyName string) (token.Key, error) { kconf, err := t.config.GetKey(keyName) if err != nil { return nil, err } res, err := t.request(ctx, workerrpc.GetKey, workerrpc.Request{KeyName: kconf.Name()}) if err != nil { return nil, err } pub, err := x509.ParsePKIXPublicKey(res.Value) if err != nil { return nil, err } return &workerKey{ token: t, kconf: kconf, public: pub, id: res.ID, cert: res.Cert, }, nil } func (t *WorkerToken) Import(keyName string, privKey crypto.PrivateKey) (token.Key, error) { return nil, token.NotImplementedError{Op: "import-key", Type: tokenType} } func (t *WorkerToken) ImportCertificate(cert *x509.Certificate, labelBase string) error { return token.NotImplementedError{Op: "import-certificate", Type: tokenType} } func (t *WorkerToken) Generate(keyName string, keyType token.KeyType, bits uint) (token.Key, error) { return nil, token.NotImplementedError{Op: "generate-key", Type: tokenType} } func (t *WorkerToken) ListKeys(opts token.ListOptions) error { return token.NotImplementedError{Op: "list-keys", Type: tokenType} } type workerKey struct { token *WorkerToken kconf *config.KeyConfig public crypto.PublicKey id []byte cert []byte } func (k *workerKey) Config() *config.KeyConfig { return k.kconf } func (k *workerKey) Certificate() []byte { return k.cert } func (k *workerKey) Public() crypto.PublicKey { return k.public } func (k *workerKey) GetID() []byte { return k.id } func (k *workerKey) Sign(rand io.Reader, digest []byte, opts crypto.SignerOpts) ([]byte, error) { return k.SignContext(context.Background(), digest, opts) } func (k *workerKey) SignContext(ctx context.Context, digest []byte, opts crypto.SignerOpts) ([]byte, error) { rr := workerrpc.Request{ KeyName: k.kconf.Name(), KeyID: k.id, Digest: digest, } if opts != nil { rr.Hash = uint(opts.HashFunc()) if o, ok := opts.(*rsa.PSSOptions); ok { rr.SaltLength = &o.SaltLength } } res, err := k.token.request(ctx, workerrpc.Sign, rr) if err != nil { return nil, err } return res.Value, nil } func (k *workerKey) ImportCertificate(cert *x509.Certificate) error { return token.NotImplementedError{Op: "import-certificate", Type: tokenType} } relic-7.6.1/token/worker/retry.go000066400000000000000000000101161455105530300167500ustar00rootroot00000000000000// // Copyright (c) SAS Institute Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package worker import ( "context" "encoding/json" "errors" "io" "net/http" "strconv" "strings" "time" "github.com/rs/zerolog/log" "github.com/sassoftware/relic/v7/internal/httperror" "github.com/sassoftware/relic/v7/internal/workerrpc" "github.com/sassoftware/relic/v7/token" "github.com/sassoftware/relic/v7/token/tokencache" ) const ( initialDelay = 1 * time.Second scaleFactor = 2.718 maxDelay = 30 * time.Second ) func (t *WorkerToken) doRetry(req *http.Request) (rresp *workerrpc.Response, err error) { retries := t.tconf.Retries if retries == 0 { retries = defaultRetries } timeout := time.Duration(t.tconf.Timeout) * time.Second if timeout == 0 { timeout = defaultTimeout } baseCtx := req.Context() delay := float32(initialDelay) var last error for i := 0; i < retries; i++ { if i != 0 { log.Warn(). Int("attempt", i). Int("max_attempts", retries). AnErr("last_error", last). Msg("token error; retrying") // delay retry with backoff ctx, cancel := context.WithTimeout(baseCtx, time.Duration(delay)) <-ctx.Done() cancel() if baseCtx.Err() != nil { // cancelled return nil, baseCtx.Err() } delay *= scaleFactor if delay > float32(maxDelay) { delay = float32(maxDelay) } } start := time.Now() rresp, err := t.doOnce(req, timeout) if err == nil { t.observe(req, start, http.StatusOK) return rresp, nil } // translate various outcomes into a status code for metrics var retry bool code := http.StatusInternalServerError if httperror.Temporary(err) { code = http.StatusServiceUnavailable retry = true } else if errors.As(err, new(token.KeyUsageError)) { code = http.StatusBadRequest } if e := new(httperror.ResponseError); errors.As(err, e) { code = e.StatusCode } if baseCtx.Err() != nil { // request canceled code = 499 } else if errors.Is(err, context.DeadlineExceeded) { code = http.StatusGatewayTimeout } t.observe(req, start, code) if !retry { return nil, err } last = err } return nil, last } func (t *WorkerToken) doOnce(req *http.Request, timeout time.Duration) (*workerrpc.Response, error) { ctx, cancel := context.WithTimeout(req.Context(), timeout) defer cancel() if req.GetBody != nil { var err error req.Body, err = req.GetBody() if err != nil { return nil, err } } resp, err := http.DefaultClient.Do(req.WithContext(ctx)) if err != nil { return nil, err } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { // http error return nil, httperror.FromResponse(resp) } // json response blob, err := io.ReadAll(resp.Body) if err != nil { return nil, err } rresp := new(workerrpc.Response) if err := json.Unmarshal(blob, rresp); err != nil { return nil, err } if rresp.Err == "" { // success return rresp, nil } else if rresp.Usage { return nil, token.KeyUsageError{ Key: rresp.Key, Err: errors.New(rresp.Err), } } return nil, tokenError{Err: rresp.Err, Retryable: rresp.Retryable} } func (t *WorkerToken) observe(req *http.Request, start time.Time, code int) { dur := time.Since(start).Seconds() name := t.tconf.Name() op := strings.TrimLeft(req.URL.Path, "/") scode := strconv.FormatInt(int64(code), 10) tokencache.MetricOperations.WithLabelValues(name, op).Observe(dur) tokencache.MetricResponses.WithLabelValues(name, op, scode).Inc() } type tokenError struct { Err string Retryable bool } func (e tokenError) Error() string { return e.Err } func (e tokenError) Temporary() bool { return e.Retryable } relic-7.6.1/token/worker/worker.go000066400000000000000000000115471455105530300171250ustar00rootroot00000000000000// // Copyright (c) SAS Institute Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package worker import ( "bytes" "context" "crypto/rand" "fmt" "io" "log" "net" "os" "os/exec" "sync" "syscall" "time" "github.com/sassoftware/relic/v7/config" "github.com/sassoftware/relic/v7/internal/activation/activatecmd" "github.com/sassoftware/relic/v7/internal/closeonce" ) const ( startTimeout = 60 * time.Second restartDelay = 10 * time.Second ) func getCookie() string { cookieBytes := make([]byte, 8) if _, err := io.ReadFull(rand.Reader, cookieBytes); err != nil { panic(err) } return fmt.Sprintf("%x", cookieBytes) } type WorkerToken struct { config *config.Config tconf *config.TokenConfig cookie string addr string fdset *activatecmd.ListenerSet notify *activatecmd.Listener closed closeonce.Closed wg sync.WaitGroup ctx context.Context cancel context.CancelFunc mu sync.Mutex procs map[int]struct{} procsExited chan int } func New(config *config.Config, tokenName string) (*WorkerToken, error) { tconf, err := config.GetToken(tokenName) if err != nil { return nil, err } lis, err := net.Listen("tcp", "127.0.0.1:0") if err != nil { return nil, err } fdset, err := activatecmd.NewListenerSet([]net.Listener{lis}) if err != nil { return nil, err } ctx, cancel := context.WithCancel(context.Background()) t := &WorkerToken{ config: config, tconf: tconf, cookie: getCookie(), fdset: fdset, notify: new(activatecmd.Listener), addr: lis.Addr().String(), ctx: ctx, cancel: cancel, procs: make(map[int]struct{}), procsExited: make(chan int, 10), } lis.Close() // NewListenerSet dupes this so it's not needed anymore if err := t.spawn(); err != nil { return nil, err } t.wg.Add(1) go t.monitor() return t, nil } func (t *WorkerToken) spawn() error { // build cmdline and worker options self, err := os.Executable() if err != nil { return err } cmd := exec.Command(os.Args[0], "worker", t.config.Path(), t.tconf.Name()) cmd.Path = self cmd.Stdin = bytes.NewReader([]byte(t.cookie)) cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true} // attach shared listener socket cmd.Env = activatecmd.ClearEnv(os.Environ()) if err := t.fdset.Attach(cmd); err != nil { return err } // attach notify socket so we know when the process is ready or if it died on init detach, err := t.notify.Attach(cmd) if err != nil { return err } defer detach() // start process and wait for ready state if err := cmd.Start(); err != nil { return err } detach() pid := cmd.Process.Pid exited := make(chan struct{}) t.wg.Add(1) go func() { defer t.wg.Done() _ = cmd.Wait() t.procsExited <- pid close(exited) }() t.mu.Lock() t.procs[pid] = struct{}{} t.mu.Unlock() ctx, cancel := context.WithTimeout(context.Background(), startTimeout) defer cancel() select { case <-t.notify.Ready(): // ready case <-ctx.Done(): // timed out _ = cmd.Process.Kill() return fmt.Errorf("token \"%s\" worker timed out during startup", t.tconf.Name()) case <-exited: // terminated return fmt.Errorf("token \"%s\" worker exited prematurely", t.tconf.Name()) } return nil } func (t *WorkerToken) countWorkers() int { t.mu.Lock() n := len(t.procs) t.mu.Unlock() return n } func (t *WorkerToken) monitor() { defer t.wg.Done() target := 1 if t.config.Server != nil && t.config.Server.NumWorkers > 0 { target = t.config.Server.NumWorkers } for t.ctx.Err() == nil { for t.countWorkers() < target { if err := t.spawn(); err != nil { log.Printf("error: failed to spawn worker process: %s", err) select { case <-time.After(restartDelay): case <-t.ctx.Done(): return } } } select { case <-t.ctx.Done(): return case pid := <-t.procsExited: // process exited t.removePid(pid) case pid := <-t.notify.Stopping(): // process hit an error and will exit soon t.removePid(pid) } } } func (t *WorkerToken) removePid(pid int) { t.mu.Lock() delete(t.procs, pid) t.mu.Unlock() } func (t *WorkerToken) Close() error { if t == nil { return nil } return t.closed.Close(func() error { t.cancel() t.mu.Lock() for pid := range t.procs { _ = syscall.Kill(pid, syscall.SIGTERM) } t.mu.Unlock() t.wg.Wait() t.fdset.Close() t.notify.Close() return nil }) }