pax_global_header00006660000000000000000000000064141546734110014520gustar00rootroot0000000000000052 comment=60a32b32095ab361c827116afd3f0041874c6c9c saml-0.4.6/000077500000000000000000000000001415467341100124635ustar00rootroot00000000000000saml-0.4.6/.github/000077500000000000000000000000001415467341100140235ustar00rootroot00000000000000saml-0.4.6/.github/dependabot.yml000066400000000000000000000001771415467341100166600ustar00rootroot00000000000000version: 2 updates: - package-ecosystem: gomod directory: "/" schedule: interval: daily open-pull-requests-limit: 10 saml-0.4.6/.github/stale.yml000066400000000000000000000012531415467341100156570ustar00rootroot00000000000000# Number of days of inactivity before an issue becomes stale daysUntilStale: 60 # Number of days of inactivity before a stale issue is closed daysUntilClose: 7 # Issues with these labels will never be considered stale exemptLabels: - pinned - security # Label to use when marking an issue as stale staleLabel: wontfix # Comment to post when marking an issue as stale. Set to `false` to disable markComment: > This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions. # Comment to post when closing a stale issue. Set to `false` to disable closeComment: true saml-0.4.6/.github/workflows/000077500000000000000000000000001415467341100160605ustar00rootroot00000000000000saml-0.4.6/.github/workflows/lint.yml000066400000000000000000000005051415467341100175510ustar00rootroot00000000000000name: lint on: push: branches: [ 'main' ] pull_request: branches: [ 'main' ] jobs: golangci: name: Run golangci-lint runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - name: golangci-lint uses: golangci/golangci-lint-action@v2 with: version: v1.43.0 saml-0.4.6/.github/workflows/maint.yml000066400000000000000000000016251415467341100177170ustar00rootroot00000000000000name: Maintainer on: workflow_dispatch: schedule: - cron: "0 12 * * 0" jobs: upgrade_go: name: Upgrade go.mod runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - uses: actions/setup-go@v2 with: go-version: "^1.15.6" - name: Install goupdate run: | ( cd $(mktemp -d) go get github.com/crewjam/goupdate ) git config --global user.email noreply@github.com git config --global user.name "Github Actions" - name: Update go.mod run: | go version go env $(go env GOPATH)/bin/goupdate -test 'go test ./...' --commit -v - name: Create Pull Request uses: peter-evans/create-pull-request@v3 with: commit-message: "Update go.mod" branch: auto/update-go title: "Update go.mod" body: "" saml-0.4.6/.github/workflows/test.yml000066400000000000000000000010261415467341100175610ustar00rootroot00000000000000name: test on: push: branches: [ 'main' ] pull_request: branches: [ 'main' ] jobs: tests: name: Run tests runs-on: ubuntu-latest strategy: matrix: go: [ '1.13.x', '1.14.x', '1.15.x', '1.16.x', '1.17.x' ] steps: - name: Set up Go ${{ matrix.go }} uses: actions/setup-go@v2 - name: Check out code into the Go module directory uses: actions/checkout@v2 with: go-version: ${{ matrix.go }} - name: Run Go tests run: go test -v ./... saml-0.4.6/.gitignore000066400000000000000000000001111415467341100144440ustar00rootroot00000000000000coverage.out coverage.html vendor/ # IDE-specific settings .idea .vscodesaml-0.4.6/.golangci.yml000066400000000000000000000103701415467341100150500ustar00rootroot00000000000000# Configuration file for golangci-lint # # https://github.com/golangci/golangci-lint # # fighting with false positives? # https://github.com/golangci/golangci-lint#nolint linters: enable: - gofmt # Gofmt checks whether code was gofmt-ed. By default this tool runs with -s option to check for code simplification [fast: true, auto-fix: true] - goimports # Goimports does everything that gofmt does. Additionally it checks unused imports [fast: true, auto-fix: true] - gosec # Errcheck is a program for checking for unchecked errors in go programs. These unchecked errors can be critical bugs in some cases [fast: true, auto-fix: false] - misspell # Finds commonly misspelled English words in comments [fast: true, auto-fix: true] - deadcode # Finds unused code [fast: true, auto-fix: false] - revive # Golint differs from gofmt. Gofmt reformats Go source code, whereas golint prints out style mistakes [fast: true, auto-fix: false] - unconvert # Remove unnecessary type conversions [fast: true, auto-fix: false] disable: # TODO(ross): fix errors reported by these checkers and enable them - bodyclose # checks whether HTTP response body is closed successfully [fast: false, auto-fix: false] - depguard # Go linter that checks if package imports are in a list of acceptable packages [fast: true, auto-fix: false] - dupl # Tool for code clone detection [fast: true, auto-fix: false] - errcheck # Inspects source code for security problems [fast: true, auto-fix: false] - gochecknoglobals # Checks that no globals are present in Go code [fast: true, auto-fix: false] - gochecknoinits # Checks that no init functions are present in Go code [fast: true, auto-fix: false] - goconst # Finds repeated strings that could be replaced by a constant [fast: true, auto-fix: false] - gocritic # The most opinionated Go source code linter [fast: true, auto-fix: false] - gocyclo # Computes and checks the cyclomatic complexity of functions [fast: true, auto-fix: false] - gosimple # Linter for Go source code that specializes in simplifying a code [fast: false, auto-fix: false] - govet # Vet examines Go source code and reports suspicious constructs, such as Printf calls whose arguments do not align with the format string [fast: false, auto-fix: false] - ineffassign # Detects when assignments to existing variables are not used [fast: true, auto-fix: false] - interfacer # Linter that suggests narrower interface types [fast: false, auto-fix: false] - lll # Reports long lines [fast: true, auto-fix: false] - maligned # Tool to detect Go structs that would take less memory if their fields were sorted [fast: true, auto-fix: false] - nakedret # Finds naked returns in functions greater than a specified function length [fast: true, auto-fix: false] - prealloc # Finds slice declarations that could potentially be preallocated [fast: true, auto-fix: false] - scopelint # Scopelint checks for unpinned variables in go programs [fast: true, auto-fix: false] - staticcheck # Staticcheck is a go vet on steroids, applying a ton of static analysis checks [fast: false, auto-fix: false] - structcheck # Finds unused struct fields [fast: true, auto-fix: false] - stylecheck # Stylecheck is a replacement for golint [fast: false, auto-fix: false] - typecheck # Like the front-end of a Go compiler, parses and type-checks Go code [fast: true, auto-fix: false] - unparam # Reports unused function parameters [fast: false, auto-fix: false] - unused # Checks Go code for unused constants, variables, functions and types [fast: false, auto-fix: false] - varcheck # Finds unused global variables and constants [fast: true, auto-fix: false] linters-settings: goimports: local-prefixes: github.com/crewjam/saml govet: disable: - shadow enable: - asmdecl - assign - atomic - bools - buildtag - cgocall - composites - copylocks - errorsas - httpresponse - loopclosure - lostcancel - nilfunc - printf - shift - stdmethods - structtag - tests - unmarshal - unreachable - unsafeptr - unusedresult issues: exclude-use-default: false exclude: - G104 # 'Errors unhandled. (gosec) saml-0.4.6/LICENSE000066400000000000000000000024121415467341100134670ustar00rootroot00000000000000Copyright (c) 2015, Ross Kinder All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. saml-0.4.6/README.md000066400000000000000000000157661415467341100137610ustar00rootroot00000000000000# SAML [![](https://godoc.org/github.com/crewjam/saml?status.svg)](http://godoc.org/github.com/crewjam/saml) ![Build Status](https://github.com/crewjam/saml/workflows/Presubmit/badge.svg) Package saml contains a partial implementation of the SAML standard in golang. SAML is a standard for identity federation, i.e. either allowing a third party to authenticate your users or allowing third parties to rely on us to authenticate their users. ## Introduction In SAML parlance an **Identity Provider** (IDP) is a service that knows how to authenticate users. A **Service Provider** (SP) is a service that delegates authentication to an IDP. If you are building a service where users log in with someone else's credentials, then you are a **Service Provider**. This package supports implementing both service providers and identity providers. The core package contains the implementation of SAML. The package samlsp provides helper middleware suitable for use in Service Provider applications. The package samlidp provides a rudimentary IDP service that is useful for testing or as a starting point for other integrations. ## Getting Started as a Service Provider Let us assume we have a simple web application to protect. We'll modify this application so it uses SAML to authenticate users. ```golang package main import ( "fmt" "net/http" ) func hello(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "Hello, World!") } func main() { app := http.HandlerFunc(hello) http.Handle("/hello", app) http.ListenAndServe(":8000", nil) } ``` Each service provider must have an self-signed X.509 key pair established. You can generate your own with something like this: openssl req -x509 -newkey rsa:2048 -keyout myservice.key -out myservice.cert -days 365 -nodes -subj "/CN=myservice.example.com" We will use `samlsp.Middleware` to wrap the endpoint we want to protect. Middleware provides both an `http.Handler` to serve the SAML specific URLs **and** a set of wrappers to require the user to be logged in. We also provide the URL where the service provider can fetch the metadata from the IDP at startup. In our case, we'll use [samltest.id](https://samltest.id/), an identity provider designed for testing. ```golang package main import ( "context" "crypto/rsa" "crypto/tls" "crypto/x509" "fmt" "net/http" "net/url" "github.com/crewjam/saml/samlsp" ) func hello(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "Hello, %s!", samlsp.AttributeFromContext(r.Context(), "cn")) } func main() { keyPair, err := tls.LoadX509KeyPair("myservice.cert", "myservice.key") if err != nil { panic(err) // TODO handle error } keyPair.Leaf, err = x509.ParseCertificate(keyPair.Certificate[0]) if err != nil { panic(err) // TODO handle error } idpMetadataURL, err := url.Parse("https://samltest.id/saml/idp") if err != nil { panic(err) // TODO handle error } idpMetadata, err := samlsp.FetchMetadata(context.Background(), http.DefaultClient, *idpMetadataURL) if err != nil { panic(err) // TODO handle error } rootURL, err := url.Parse("http://localhost:8000") if err != nil { panic(err) // TODO handle error } samlSP, _ := samlsp.New(samlsp.Options{ URL: *rootURL, Key: keyPair.PrivateKey.(*rsa.PrivateKey), Certificate: keyPair.Leaf, IDPMetadata: idpMetadata, }) app := http.HandlerFunc(hello) http.Handle("/hello", samlSP.RequireAccount(app)) http.Handle("/saml/", samlSP) http.ListenAndServe(":8000", nil) } ``` Next we'll have to register our service provider with the identity provider to establish trust from the service provider to the IDP. For [samltest.id](https://samltest.id/), you can do something like: mdpath=saml-test-$USER-$HOST.xml curl localhost:8000/saml/metadata > $mdpath Navigate to https://samltest.id/upload.php and upload the file you fetched. Now you should be able to authenticate. The flow should look like this: 1. You browse to `localhost:8000/hello` 1. The middleware redirects you to `https://samltest.id/idp/profile/SAML2/Redirect/SSO` 1. samltest.id prompts you for a username and password. 1. samltest.id returns you an HTML document which contains an HTML form setup to POST to `localhost:8000/saml/acs`. The form is automatically submitted if you have javascript enabled. 1. The local service validates the response, issues a session cookie, and redirects you to the original URL, `localhost:8000/hello`. 1. This time when `localhost:8000/hello` is requested there is a valid session and so the main content is served. ## Getting Started as an Identity Provider Please see `example/idp/` for a substantially complete example of how to use the library and helpers to be an identity provider. ## Support The SAML standard is huge and complex with many dark corners and strange, unused features. This package implements the most commonly used subset of these features required to provide a single sign on experience. The package supports at least the subset of SAML known as [interoperable SAML](https://kantarainitiative.github.io/SAMLprofiles/saml2int.html). This package supports the **Web SSO** profile. Message flows from the service provider to the IDP are supported using the **HTTP Redirect** binding and the **HTTP POST** binding. Message flows from the IDP to the service provider are supported via the **HTTP POST** binding. The package can produce signed SAML assertions, and can validate both signed and encrypted SAML assertions. It does not support signed or encrypted requests. ## RelayState The _RelayState_ parameter allows you to pass user state information across the authentication flow. The most common use for this is to allow a user to request a deep link into your site, be redirected through the SAML login flow, and upon successful completion, be directed to the originally requested link, rather than the root. Unfortunately, _RelayState_ is less useful than it could be. Firstly, it is **not** authenticated, so anything you supply must be signed to avoid XSS or CSRF. Secondly, it is limited to 80 bytes in length, which precludes signing. (See section 3.6.3.1 of SAMLProfiles.) ## References The SAML specification is a collection of PDFs (sadly): - [SAMLCore](http://docs.oasis-open.org/security/saml/v2.0/saml-core-2.0-os.pdf) defines data types. - [SAMLBindings](http://docs.oasis-open.org/security/saml/v2.0/saml-bindings-2.0-os.pdf) defines the details of the HTTP requests in play. - [SAMLProfiles](http://docs.oasis-open.org/security/saml/v2.0/saml-profiles-2.0-os.pdf) describes data flows. - [SAMLConformance](http://docs.oasis-open.org/security/saml/v2.0/saml-conformance-2.0-os.pdf) includes a support matrix for various parts of the protocol. [SAMLtest](https://samltest.id/) is a testing ground for SAML service and identity providers. ## Security Issues Please do not report security issues in the issue tracker. Rather, please contact me directly at ross@kndr.org ([PGP Key `78B6038B3B9DFB88`](https://keybase.io/crewjam)). If your issue is *not* a security issue, please use the issue tracker so other contributors can help. saml-0.4.6/duration.go000066400000000000000000000056161415467341100146470ustar00rootroot00000000000000package saml import ( "fmt" "regexp" "strconv" "strings" "time" ) // Duration is a time.Duration that uses the xsd:duration format for text // marshalling and unmarshalling. type Duration time.Duration // MarshalText implements the encoding.TextMarshaler interface. func (d Duration) MarshalText() ([]byte, error) { if d == 0 { return nil, nil } out := "PT" if d < 0 { d *= -1 out = "-" + out } h := time.Duration(d) / time.Hour m := time.Duration(d) % time.Hour / time.Minute s := time.Duration(d) % time.Minute / time.Second ns := time.Duration(d) % time.Second if h > 0 { out += fmt.Sprintf("%dH", h) } if m > 0 { out += fmt.Sprintf("%dM", m) } if s > 0 || ns > 0 { out += fmt.Sprintf("%d", s) if ns > 0 { out += strings.TrimRight(fmt.Sprintf(".%09d", ns), "0") } out += "S" } return []byte(out), nil } const ( day = 24 * time.Hour month = 30 * day // Assumed to be 30 days. year = 365 * day // Assumed to be non-leap year. ) var ( durationRegexp = regexp.MustCompile(`^(-?)P(?:(\d+)Y)?(?:(\d+)M)?(?:(\d+)D)?(?:T(.+))?$`) durationTimeRegexp = regexp.MustCompile(`^(?:(\d+)H)?(?:(\d+)M)?(?:(\d+(?:\.\d+)?)S)?$`) ) // UnmarshalText implements the encoding.TextUnmarshaler interface. func (d *Duration) UnmarshalText(text []byte) error { if text == nil { *d = 0 return nil } var ( out time.Duration sign time.Duration = 1 ) match := durationRegexp.FindStringSubmatch(string(text)) if match == nil || strings.Join(match[2:6], "") == "" { return fmt.Errorf("invalid duration (%s)", text) } if match[1] == "-" { sign = -1 } if match[2] != "" { y, err := strconv.Atoi(match[2]) if err != nil { return fmt.Errorf("invalid duration years (%s): %s", text, err) } out += time.Duration(y) * year } if match[3] != "" { m, err := strconv.Atoi(match[3]) if err != nil { return fmt.Errorf("invalid duration months (%s): %s", text, err) } out += time.Duration(m) * month } if match[4] != "" { d, err := strconv.Atoi(match[4]) if err != nil { return fmt.Errorf("invalid duration days (%s): %s", text, err) } out += time.Duration(d) * day } if match[5] != "" { match := durationTimeRegexp.FindStringSubmatch(match[5]) if match == nil { return fmt.Errorf("invalid duration (%s)", text) } if match[1] != "" { h, err := strconv.Atoi(match[1]) if err != nil { return fmt.Errorf("invalid duration hours (%s): %s", text, err) } out += time.Duration(h) * time.Hour } if match[2] != "" { m, err := strconv.Atoi(match[2]) if err != nil { return fmt.Errorf("invalid duration minutes (%s): %s", text, err) } out += time.Duration(m) * time.Minute } if match[3] != "" { s, err := strconv.ParseFloat(match[3], 64) if err != nil { return fmt.Errorf("invalid duration seconds (%s): %s", text, err) } out += time.Duration(s * float64(time.Second)) } } *d = Duration(sign * out) return nil } saml-0.4.6/duration_test.go000066400000000000000000000053501415467341100157010ustar00rootroot00000000000000package saml import ( "errors" "strconv" "testing" "time" "gotest.tools/assert" is "gotest.tools/assert/cmp" ) var durationMarshalTests = []struct { in time.Duration expected []byte }{ {0, nil}, {time.Nanosecond, []byte("PT0.000000001S")}, {time.Millisecond, []byte("PT0.001S")}, {time.Second, []byte("PT1S")}, {time.Minute, []byte("PT1M")}, {time.Hour, []byte("PT1H")}, {-time.Hour, []byte("-PT1H")}, {2*time.Hour + 3*time.Minute + 4*time.Second + 5*time.Nanosecond, []byte("PT2H3M4.000000005S")}, } func TestDuration(t *testing.T) { for i, testCase := range durationMarshalTests { t.Run(strconv.Itoa(i), func(t *testing.T) { actual, err := Duration(testCase.in).MarshalText() assert.Check(t, err) assert.Check(t, is.DeepEqual(testCase.expected, actual)) }) } } var durationUnmarshalTests = []struct { in []byte expected time.Duration err error }{ {nil, 0, nil}, {[]byte("PT0.0000000001S"), 0, nil}, {[]byte("PT0.000000001S"), time.Nanosecond, nil}, {[]byte("PT0.001S"), time.Millisecond, nil}, {[]byte("PT1S"), time.Second, nil}, {[]byte("PT1M"), time.Minute, nil}, {[]byte("PT1H"), time.Hour, nil}, {[]byte("-PT1H"), -time.Hour, nil}, {[]byte("P1D"), 24 * time.Hour, nil}, {[]byte("P1M"), 720 * time.Hour, nil}, {[]byte("P1Y"), 8760 * time.Hour, nil}, {[]byte("P2Y3M4DT5H6M7.000000008S"), 19781*time.Hour + 6*time.Minute + 7*time.Second + 8*time.Nanosecond, nil}, {[]byte("P0Y0M0DT0H0M0S"), 0, nil}, {[]byte("PT0001.0000S"), time.Second, nil}, {[]byte(""), 0, errors.New("invalid duration ()")}, {[]byte("12345"), 0, errors.New("invalid duration (12345)")}, {[]byte("P1D1M1Y"), 0, errors.New("invalid duration (P1D1M1Y)")}, {[]byte("P1H1M1S"), 0, errors.New("invalid duration (P1H1M1S)")}, {[]byte("PT1S1M1H"), 0, errors.New("invalid duration (PT1S1M1H)")}, {[]byte(" P1Y "), 0, errors.New("invalid duration ( P1Y )")}, {[]byte("P"), 0, errors.New("invalid duration (P)")}, {[]byte("-P"), 0, errors.New("invalid duration (-P)")}, {[]byte("PT"), 0, errors.New("invalid duration (PT)")}, {[]byte("P1YMD"), 0, errors.New("invalid duration (P1YMD)")}, {[]byte("P1YT"), 0, errors.New("invalid duration (P1YT)")}, {[]byte("P-1Y"), 0, errors.New("invalid duration (P-1Y)")}, {[]byte("P1.5Y"), 0, errors.New("invalid duration (P1.5Y)")}, {[]byte("PT1.S"), 0, errors.New("invalid duration (PT1.S)")}, } func TestDurationUnmarshal(t *testing.T) { for i, testCase := range durationUnmarshalTests { t.Run(strconv.Itoa(i), func(t *testing.T) { var actual Duration err := actual.UnmarshalText(testCase.in) if testCase.err == nil { assert.Check(t, err) } else { assert.Check(t, is.Error(err, testCase.err.Error())) } assert.Check(t, is.Equal(Duration(testCase.expected), actual)) }) } } saml-0.4.6/example/000077500000000000000000000000001415467341100141165ustar00rootroot00000000000000saml-0.4.6/example/idp/000077500000000000000000000000001415467341100146725ustar00rootroot00000000000000saml-0.4.6/example/idp/idp.go000066400000000000000000000106221415467341100157760ustar00rootroot00000000000000package main import ( "crypto" "crypto/x509" "encoding/pem" "flag" "net/url" "github.com/zenazn/goji" "golang.org/x/crypto/bcrypt" "github.com/crewjam/saml/logger" "github.com/crewjam/saml/samlidp" ) var key = func() crypto.PrivateKey { b, _ := pem.Decode([]byte(`-----BEGIN RSA PRIVATE KEY----- MIIEpAIBAAKCAQEA0OhbMuizgtbFOfwbK7aURuXhZx6VRuAs3nNibiuifwCGz6u9 yy7bOR0P+zqN0YkjxaokqFgra7rXKCdeABmoLqCC0U+cGmLNwPOOA0PaD5q5xKhQ 4Me3rt/R9C4Ca6k3/OnkxnKwnogcsmdgs2l8liT3qVHP04Oc7Uymq2v09bGb6nPu fOrkXS9F6mSClxHG/q59AGOWsXK1xzIRV1eu8W2SNdyeFVU1JHiQe444xLoPul5t InWasKayFsPlJfWNc8EoU8COjNhfo/GovFTHVjh9oUR/gwEFVwifIHihRE0Hazn2 EQSLaOr2LM0TsRsQroFjmwSGgI+X2bfbMTqWOQIDAQABAoIBAFWZwDTeESBdrLcT zHZe++cJLxE4AObn2LrWANEv5AeySYsyzjRBYObIN9IzrgTb8uJ900N/zVr5VkxH xUa5PKbOcowd2NMfBTw5EEnaNbILLm+coHdanrNzVu59I9TFpAFoPavrNt/e2hNo NMGPSdOkFi81LLl4xoadz/WR6O/7N2famM+0u7C2uBe+TrVwHyuqboYoidJDhO8M w4WlY9QgAUhkPyzZqrl+VfF1aDTGVf4LJgaVevfFCas8Ws6DQX5q4QdIoV6/0vXi B1M+aTnWjHuiIzjBMWhcYW2+I5zfwNWRXaxdlrYXRukGSdnyO+DH/FhHePJgmlkj NInADDkCgYEA6MEQFOFSCc/ELXYWgStsrtIlJUcsLdLBsy1ocyQa2lkVUw58TouW RciE6TjW9rp31pfQUnO2l6zOUC6LT9Jvlb9PSsyW+rvjtKB5PjJI6W0hjX41wEO6 fshFELMJd9W+Ezao2AsP2hZJ8McCF8no9e00+G4xTAyxHsNI2AFTCQcCgYEA5cWZ JwNb4t7YeEajPt9xuYNUOQpjvQn1aGOV7KcwTx5ELP/Hzi723BxHs7GSdrLkkDmi Gpb+mfL4wxCt0fK0i8GFQsRn5eusyq9hLqP/bmjpHoXe/1uajFbE1fZQR+2LX05N 3ATlKaH2hdfCJedFa4wf43+cl6Yhp6ZA0Yet1r8CgYEAwiu1j8W9G+RRA5/8/DtO yrUTOfsbFws4fpLGDTA0mq0whf6Soy/96C90+d9qLaC3srUpnG9eB0CpSOjbXXbv kdxseLkexwOR3bD2FHX8r4dUM2bzznZyEaxfOaQypN8SV5ME3l60Fbr8ajqLO288 wlTmGM5Mn+YCqOg/T7wjGmcCgYBpzNfdl/VafOROVbBbhgXWtzsz3K3aYNiIjbp+ MunStIwN8GUvcn6nEbqOaoiXcX4/TtpuxfJMLw4OvAJdtxUdeSmEee2heCijV6g3 ErrOOy6EqH3rNWHvlxChuP50cFQJuYOueO6QggyCyruSOnDDuc0BM0SGq6+5g5s7 H++S/wKBgQDIkqBtFr9UEf8d6JpkxS0RXDlhSMjkXmkQeKGFzdoJcYVFIwq8jTNB nJrVIGs3GcBkqGic+i7rTO1YPkquv4dUuiIn+vKZVoO6b54f+oPBXd4S0BnuEqFE rdKNuCZhiaE2XD9L/O9KP1fh5bfEcKwazQ23EvpJHBMm8BGC+/YZNw== -----END RSA PRIVATE KEY-----`)) k, _ := x509.ParsePKCS1PrivateKey(b.Bytes) return k }() var cert = func() *x509.Certificate { b, _ := pem.Decode([]byte(`-----BEGIN CERTIFICATE----- MIIDBzCCAe+gAwIBAgIJAPr/Mrlc8EGhMA0GCSqGSIb3DQEBBQUAMBoxGDAWBgNV BAMMD3d3dy5leGFtcGxlLmNvbTAeFw0xNTEyMjgxOTE5NDVaFw0yNTEyMjUxOTE5 NDVaMBoxGDAWBgNVBAMMD3d3dy5leGFtcGxlLmNvbTCCASIwDQYJKoZIhvcNAQEB BQADggEPADCCAQoCggEBANDoWzLos4LWxTn8Gyu2lEbl4WcelUbgLN5zYm4ron8A hs+rvcsu2zkdD/s6jdGJI8WqJKhYK2u61ygnXgAZqC6ggtFPnBpizcDzjgND2g+a ucSoUODHt67f0fQuAmupN/zp5MZysJ6IHLJnYLNpfJYk96lRz9ODnO1Mpqtr9PWx m+pz7nzq5F0vRepkgpcRxv6ufQBjlrFytccyEVdXrvFtkjXcnhVVNSR4kHuOOMS6 D7pebSJ1mrCmshbD5SX1jXPBKFPAjozYX6PxqLxUx1Y4faFEf4MBBVcInyB4oURN B2s59hEEi2jq9izNE7EbEK6BY5sEhoCPl9m32zE6ljkCAwEAAaNQME4wHQYDVR0O BBYEFB9ZklC1Ork2zl56zg08ei7ss/+iMB8GA1UdIwQYMBaAFB9ZklC1Ork2zl56 zg08ei7ss/+iMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAAVoTSQ5 pAirw8OR9FZ1bRSuTDhY9uxzl/OL7lUmsv2cMNeCB3BRZqm3mFt+cwN8GsH6f3uv NONIhgFpTGN5LEcXQz89zJEzB+qaHqmbFpHQl/sx2B8ezNgT/882H2IH00dXESEf y/+1gHg2pxjGnhRBN6el/gSaDiySIMKbilDrffuvxiCfbpPN0NRRiPJhd2ay9KuL /RxQRl1gl9cHaWiouWWba1bSBb2ZPhv2rPMUsFo98ntkGCObDX6Y1SpkqmoTbrsb GFsTG2DLxnvr4GdN1BSr0Uu/KV3adj47WkXVPeMYQti/bQmxQB8tRFhrw80qakTL UzreO96WzlBBMtY= -----END CERTIFICATE-----`)) c, _ := x509.ParseCertificate(b.Bytes) return c }() func main() { logr := logger.DefaultLogger baseURLstr := flag.String("idp", "", "The URL to the IDP") flag.Parse() baseURL, err := url.Parse(*baseURLstr) if err != nil { logr.Fatalf("cannot parse base URL: %v", err) } idpServer, err := samlidp.New(samlidp.Options{ URL: *baseURL, Key: key, Logger: logr, Certificate: cert, Store: &samlidp.MemoryStore{}, }) if err != nil { logr.Fatalf("%s", err) } hashedPassword, _ := bcrypt.GenerateFromPassword([]byte("hunter2"), bcrypt.DefaultCost) err = idpServer.Store.Put("/users/alice", samlidp.User{Name: "alice", HashedPassword: hashedPassword, Groups: []string{"Administrators", "Users"}, Email: "alice@example.com", CommonName: "Alice Smith", Surname: "Smith", GivenName: "Alice", }) if err != nil { logr.Fatalf("%s", err) } err = idpServer.Store.Put("/users/bob", samlidp.User{ Name: "bob", HashedPassword: hashedPassword, Groups: []string{"Users"}, Email: "bob@example.com", CommonName: "Bob Smith", Surname: "Smith", GivenName: "Bob", }) if err != nil { logr.Fatalf("%s", err) } goji.Handle("/*", idpServer) goji.Serve() } saml-0.4.6/example/service.go000066400000000000000000000112631415467341100161100ustar00rootroot00000000000000// This is an example that implements a bitly-esque short link service. package main import ( "bytes" "context" "crypto/rsa" "crypto/tls" "crypto/x509" "encoding/xml" "flag" "fmt" "net/http" "net/url" "strings" "github.com/dchest/uniuri" "github.com/kr/pretty" "github.com/zenazn/goji" "github.com/zenazn/goji/web" "github.com/crewjam/saml/samlsp" ) var links = map[string]Link{} // Link represents a short link type Link struct { ShortLink string Target string Owner string } // CreateLink handles requests to create links func CreateLink(c web.C, w http.ResponseWriter, r *http.Request) { account := r.Header.Get("X-Remote-User") l := Link{ ShortLink: uniuri.New(), Target: r.FormValue("t"), Owner: account, } links[l.ShortLink] = l fmt.Fprintf(w, "%s\n", l.ShortLink) return } // ServeLink handles requests to redirect to a link func ServeLink(c web.C, w http.ResponseWriter, r *http.Request) { l, ok := links[strings.TrimPrefix(r.URL.Path, "/")] if !ok { http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound) return } http.Redirect(w, r, l.Target, http.StatusFound) return } // ListLinks returns a list of the current user's links func ListLinks(c web.C, w http.ResponseWriter, r *http.Request) { account := r.Header.Get("X-Remote-User") for _, l := range links { if l.Owner == account { fmt.Fprintf(w, "%s\n", l.ShortLink) } } } var ( key = []byte(`-----BEGIN RSA PRIVATE KEY----- MIICXgIBAAKBgQDU8wdiaFmPfTyRYuFlVPi866WrH/2JubkHzp89bBQopDaLXYxi 3PTu3O6Q/KaKxMOFBqrInwqpv/omOGZ4ycQ51O9I+Yc7ybVlW94lTo2gpGf+Y/8E PsVbnZaFutRctJ4dVIp9aQ2TpLiGT0xX1OzBO/JEgq9GzDRf+B+eqSuglwIDAQAB AoGBAMuy1eN6cgFiCOgBsB3gVDdTKpww87Qk5ivjqEt28SmXO13A1KNVPS6oQ8SJ CT5Azc6X/BIAoJCURVL+LHdqebogKljhH/3yIel1kH19vr4E2kTM/tYH+qj8afUS JEmArUzsmmK8ccuNqBcllqdwCZjxL4CHDUmyRudFcHVX9oyhAkEA/OV1OkjM3CLU N3sqELdMmHq5QZCUihBmk3/N5OvGdqAFGBlEeewlepEVxkh7JnaNXAXrKHRVu/f/ fbCQxH+qrwJBANeQERF97b9Sibp9xgolb749UWNlAdqmEpmlvmS202TdcaaT1msU 4rRLiQN3X9O9mq4LZMSVethrQAdX1whawpkCQQDk1yGf7xZpMJ8F4U5sN+F4rLyM Rq8Sy8p2OBTwzCUXXK+fYeXjybsUUMr6VMYTRP2fQr/LKJIX+E5ZxvcIyFmDAkEA yfjNVUNVaIbQTzEbRlRvT6MqR+PTCefC072NF9aJWR93JimspGZMR7viY6IM4lrr vBkm0F5yXKaYtoiiDMzlOQJADqmEwXl0D72ZG/2KDg8b4QZEmC9i5gidpQwJXUc6 hU+IVQoLxRq0fBib/36K9tcrrO5Ba4iEvDcNY+D8yGbUtA== -----END RSA PRIVATE KEY----- `) cert = []byte(`-----BEGIN CERTIFICATE----- MIIB7zCCAVgCCQDFzbKIp7b3MTANBgkqhkiG9w0BAQUFADA8MQswCQYDVQQGEwJV UzELMAkGA1UECAwCR0ExDDAKBgNVBAoMA2ZvbzESMBAGA1UEAwwJbG9jYWxob3N0 MB4XDTEzMTAwMjAwMDg1MVoXDTE0MTAwMjAwMDg1MVowPDELMAkGA1UEBhMCVVMx CzAJBgNVBAgMAkdBMQwwCgYDVQQKDANmb28xEjAQBgNVBAMMCWxvY2FsaG9zdDCB nzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA1PMHYmhZj308kWLhZVT4vOulqx/9 ibm5B86fPWwUKKQ2i12MYtz07tzukPymisTDhQaqyJ8Kqb/6JjhmeMnEOdTvSPmH O8m1ZVveJU6NoKRn/mP/BD7FW52WhbrUXLSeHVSKfWkNk6S4hk9MV9TswTvyRIKv Rsw0X/gfnqkroJcCAwEAATANBgkqhkiG9w0BAQUFAAOBgQCMMlIO+GNcGekevKgk akpMdAqJfs24maGb90DvTLbRZRD7Xvn1MnVBBS9hzlXiFLYOInXACMW5gcoRFfeT QLSouMM8o57h0uKjfTmuoWHLQLi6hnF+cvCsEFiJZ4AbF+DgmO6TarJ8O05t8zvn OwJlNCASPZRH/JmF8tX0hoHuAQ== -----END CERTIFICATE----- `) ) func main() { rootURLstr := flag.String("url", "https://962766ce.ngrok.io", "The base URL of this service") idpMetadataURLstr := flag.String("idp", "https://516becc2.ngrok.io/metadata", "The metadata URL for the IDP") flag.Parse() keyPair, err := tls.X509KeyPair(cert, key) if err != nil { panic(err) // TODO handle error } keyPair.Leaf, err = x509.ParseCertificate(keyPair.Certificate[0]) if err != nil { panic(err) // TODO handle error } idpMetadataURL, err := url.Parse(*idpMetadataURLstr) if err != nil { panic(err) // TODO handle error } idpMetadata, err := samlsp.FetchMetadata(context.Background(), http.DefaultClient, *idpMetadataURL) if err != nil { panic(err) // TODO handle error } rootURL, err := url.Parse(*rootURLstr) if err != nil { panic(err) // TODO handle error } samlSP, err := samlsp.New(samlsp.Options{ URL: *rootURL, Key: keyPair.PrivateKey.(*rsa.PrivateKey), Certificate: keyPair.Leaf, AllowIDPInitiated: true, IDPMetadata: idpMetadata, }) if err != nil { panic(err) // TODO handle error } // register with the service provider spMetadataBuf, _ := xml.MarshalIndent(samlSP.ServiceProvider.Metadata(), "", " ") spURL := *idpMetadataURL spURL.Path = "/services/sp" http.Post(spURL.String(), "text/xml", bytes.NewReader(spMetadataBuf)) goji.Handle("/saml/*", samlSP) authMux := web.New() authMux.Use(samlSP.RequireAccount) authMux.Get("/whoami", func(w http.ResponseWriter, r *http.Request) { pretty.Fprintf(w, "%# v", r) }) authMux.Post("/", CreateLink) authMux.Get("/", ListLinks) goji.Handle("/*", authMux) goji.Get("/:link", ServeLink) goji.Serve() } saml-0.4.6/example/trivial/000077500000000000000000000000001415467341100155705ustar00rootroot00000000000000saml-0.4.6/example/trivial/trivial.go000066400000000000000000000036161415467341100175770ustar00rootroot00000000000000package main import ( "context" "crypto/rsa" "crypto/tls" "crypto/x509" "fmt" "net/http" "net/url" "github.com/crewjam/saml/samlsp" ) var samlMiddleware *samlsp.Middleware func hello(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "Hello, %s!", samlsp.AttributeFromContext(r.Context(), "displayName")) } func logout(w http.ResponseWriter, r *http.Request) { nameID := samlsp.AttributeFromContext(r.Context(), "urn:oasis:names:tc:SAML:attribute:subject-id") url, err := samlMiddleware.ServiceProvider.MakeRedirectLogoutRequest(nameID, "") if err != nil { panic(err) // TODO handle error } err = samlMiddleware.Session.DeleteSession(w, r) if err != nil { panic(err) // TODO handle error } w.Header().Add("Location", url.String()) w.WriteHeader(http.StatusFound) } func main() { keyPair, err := tls.LoadX509KeyPair("myservice.cert", "myservice.key") if err != nil { panic(err) // TODO handle error } keyPair.Leaf, err = x509.ParseCertificate(keyPair.Certificate[0]) if err != nil { panic(err) // TODO handle error } idpMetadataURL, err := url.Parse("https://samltest.id/saml/idp") if err != nil { panic(err) // TODO handle error } idpMetadata, err := samlsp.FetchMetadata(context.Background(), http.DefaultClient, *idpMetadataURL) if err != nil { panic(err) // TODO handle error } rootURL, err := url.Parse("http://localhost:8000") if err != nil { panic(err) // TODO handle error } samlMiddleware, _ = samlsp.New(samlsp.Options{ URL: *rootURL, Key: keyPair.PrivateKey.(*rsa.PrivateKey), Certificate: keyPair.Leaf, IDPMetadata: idpMetadata, SignRequest: true, // some IdP require the SLO request to be signed }) app := http.HandlerFunc(hello) slo := http.HandlerFunc(logout) http.Handle("/hello", samlMiddleware.RequireAccount(app)) http.Handle("/saml/", samlMiddleware) http.Handle("/logout", slo) http.ListenAndServe(":8000", nil) } saml-0.4.6/go.mod000066400000000000000000000012211415467341100135650ustar00rootroot00000000000000module github.com/crewjam/saml go 1.13 require ( github.com/beevik/etree v1.1.0 github.com/crewjam/httperr v0.2.0 github.com/davecgh/go-spew v1.1.1 // indirect github.com/dchest/uniuri v0.0.0-20200228104902-7aecb25e1fe5 github.com/golang-jwt/jwt/v4 v4.1.0 github.com/google/go-cmp v0.5.6 github.com/kr/pretty v0.3.0 github.com/mattermost/xml-roundtrip-validator v0.1.0 github.com/pkg/errors v0.9.1 // indirect github.com/russellhaering/goxmldsig v1.1.1 github.com/zenazn/goji v1.0.1 golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2 golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect gotest.tools v2.2.0+incompatible ) saml-0.4.6/go.sum000066400000000000000000000134141415467341100136210ustar00rootroot00000000000000github.com/beevik/etree v1.1.0 h1:T0xke/WvNtMoCqgzPhkX2r4rjY3GDZFi+FjpRZY2Jbs= github.com/beevik/etree v1.1.0/go.mod h1:r8Aw8JqVegEf0w2fDnATrX9VpkMcyFeM0FhwO62wh+A= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/crewjam/httperr v0.2.0 h1:b2BfXR8U3AlIHwNeFFvZ+BV1LFvKLlzMjzaTnZMybNo= github.com/crewjam/httperr v0.2.0/go.mod h1:Jlz+Sg/XqBQhyMjdDiC+GNNRzZTD7x39Gu3pglZ5oH4= 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/dchest/uniuri v0.0.0-20200228104902-7aecb25e1fe5 h1:RAV05c0xOkJ3dZGS0JFybxFKZ2WMLabgx3uXnd7rpGs= github.com/dchest/uniuri v0.0.0-20200228104902-7aecb25e1fe5/go.mod h1:GgB8SF9nRG+GqaDtLcwJZsQFhcogVCJ79j4EdT0c2V4= github.com/golang-jwt/jwt/v4 v4.1.0 h1:XUgk2Ex5veyVFVeLm0xhusUTQybEbexJXrvPNOKkSY0= github.com/golang-jwt/jwt/v4 v4.1.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg= github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/jonboulle/clockwork v0.2.2 h1:UOGuzwb1PwsrDAObMuhUnj0p5ULPj8V/xJ7Kx9qUBdQ= github.com/jonboulle/clockwork v0.2.2/go.mod h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUBGnn1kMkgxc8= 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.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= 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/mattermost/xml-roundtrip-validator v0.1.0 h1:RXbVD2UAl7A7nOTR4u7E3ILa4IbtvKBHw64LDsmu9hU= github.com/mattermost/xml-roundtrip-validator v0.1.0/go.mod h1:qccnGMcpgwcNaBnxqpJpWWUiPNr5H3O8eDgGV9gT5To= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= 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/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8= github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE= github.com/russellhaering/goxmldsig v1.1.1 h1:vI0r2osGF1A9PLvsGdPUAGwEIrKa4Pj5sesSBsebIxM= github.com/russellhaering/goxmldsig v1.1.1/go.mod h1:gM4MDENBQf7M+V824SGfyIUVFWydB7n0KkEubVJl+Tw= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/zenazn/goji v1.0.1 h1:4lbD8Mx2h7IvloP7r2C0D6ltZP6Ufip8Hn0wmSK5LR8= github.com/zenazn/goji v1.0.1/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q= golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2 h1:It14KIkyBFYkHkwZ7k45minvA9aorojkyjGk9KJ5B/w= golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 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-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.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo= gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= saml-0.4.6/identity_provider.go000066400000000000000000001047621415467341100165670ustar00rootroot00000000000000package saml import ( "bytes" "compress/flate" "crypto" "crypto/tls" "crypto/x509" "encoding/base64" "encoding/xml" "fmt" "io" "io/ioutil" "net/http" "net/url" "os" "regexp" "strconv" "text/template" "time" "github.com/beevik/etree" xrv "github.com/mattermost/xml-roundtrip-validator" dsig "github.com/russellhaering/goxmldsig" "github.com/crewjam/saml/logger" "github.com/crewjam/saml/xmlenc" ) // Session represents a user session. It is returned by the // SessionProvider implementation's GetSession method. Fields here // are used to set fields in the SAML assertion. type Session struct { ID string CreateTime time.Time ExpireTime time.Time Index string NameID string Groups []string UserName string UserEmail string UserCommonName string UserSurname string UserGivenName string UserScopedAffiliation string CustomAttributes []Attribute } // SessionProvider is an interface used by IdentityProvider to determine the // Session associated with a request. For an example implementation, see // GetSession in the samlidp package. type SessionProvider interface { // GetSession returns the remote user session associated with the http.Request. // // If (and only if) the request is not associated with a session then GetSession // must complete the HTTP request and return nil. GetSession(w http.ResponseWriter, r *http.Request, req *IdpAuthnRequest) *Session } // ServiceProviderProvider is an interface used by IdentityProvider to look up // service provider metadata for a request. type ServiceProviderProvider interface { // GetServiceProvider returns the Service Provider metadata for the // service provider ID, which is typically the service provider's // metadata URL. If an appropriate service provider cannot be found then // the returned error must be os.ErrNotExist. GetServiceProvider(r *http.Request, serviceProviderID string) (*EntityDescriptor, error) } // AssertionMaker is an interface used by IdentityProvider to construct the // assertion for a request. The default implementation is DefaultAssertionMaker, // which is used if not AssertionMaker is specified. type AssertionMaker interface { // MakeAssertion constructs an assertion from session and the request and // assigns it to req.Assertion. MakeAssertion(req *IdpAuthnRequest, session *Session) error } // IdentityProvider implements the SAML Identity Provider role (IDP). // // An identity provider receives SAML assertion requests and responds // with SAML Assertions. // // You must provide a keypair that is used to // sign assertions. // // You must provide an implementation of ServiceProviderProvider which // returns // // You must provide an implementation of the SessionProvider which // handles the actual authentication (i.e. prompting for a username // and password). type IdentityProvider struct { Key crypto.PrivateKey Logger logger.Interface Certificate *x509.Certificate Intermediates []*x509.Certificate MetadataURL url.URL SSOURL url.URL LogoutURL url.URL ServiceProviderProvider ServiceProviderProvider SessionProvider SessionProvider AssertionMaker AssertionMaker SignatureMethod string ValidDuration *time.Duration } // Metadata returns the metadata structure for this identity provider. func (idp *IdentityProvider) Metadata() *EntityDescriptor { certStr := base64.StdEncoding.EncodeToString(idp.Certificate.Raw) var validDuration time.Duration if idp.ValidDuration != nil { validDuration = *idp.ValidDuration } else { validDuration = DefaultValidDuration } ed := &EntityDescriptor{ EntityID: idp.MetadataURL.String(), ValidUntil: TimeNow().Add(validDuration), CacheDuration: validDuration, IDPSSODescriptors: []IDPSSODescriptor{ { SSODescriptor: SSODescriptor{ RoleDescriptor: RoleDescriptor{ ProtocolSupportEnumeration: "urn:oasis:names:tc:SAML:2.0:protocol", KeyDescriptors: []KeyDescriptor{ { Use: "signing", KeyInfo: KeyInfo{ X509Data: X509Data{ X509Certificates: []X509Certificate{ {Data: certStr}, }, }, }, }, { Use: "encryption", KeyInfo: KeyInfo{ X509Data: X509Data{ X509Certificates: []X509Certificate{ {Data: certStr}, }, }, }, EncryptionMethods: []EncryptionMethod{ {Algorithm: "http://www.w3.org/2001/04/xmlenc#aes128-cbc"}, {Algorithm: "http://www.w3.org/2001/04/xmlenc#aes192-cbc"}, {Algorithm: "http://www.w3.org/2001/04/xmlenc#aes256-cbc"}, {Algorithm: "http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p"}, }, }, }, }, NameIDFormats: []NameIDFormat{NameIDFormat("urn:oasis:names:tc:SAML:2.0:nameid-format:transient")}, }, SingleSignOnServices: []Endpoint{ { Binding: HTTPRedirectBinding, Location: idp.SSOURL.String(), }, { Binding: HTTPPostBinding, Location: idp.SSOURL.String(), }, }, }, }, } if idp.LogoutURL.String() != "" { ed.IDPSSODescriptors[0].SSODescriptor.SingleLogoutServices = []Endpoint{ { Binding: HTTPRedirectBinding, Location: idp.LogoutURL.String(), }, } } return ed } // Handler returns an http.Handler that serves the metadata and SSO // URLs func (idp *IdentityProvider) Handler() http.Handler { mux := http.NewServeMux() mux.HandleFunc(idp.MetadataURL.Path, idp.ServeMetadata) mux.HandleFunc(idp.SSOURL.Path, idp.ServeSSO) return mux } // ServeMetadata is an http.HandlerFunc that serves the IDP metadata func (idp *IdentityProvider) ServeMetadata(w http.ResponseWriter, r *http.Request) { buf, _ := xml.MarshalIndent(idp.Metadata(), "", " ") w.Header().Set("Content-Type", "application/samlmetadata+xml") w.Write(buf) } // ServeSSO handles SAML auth requests. // // When it gets a request for a user that does not have a valid session, // then it prompts the user via XXX. // // If the session already exists, then it produces a SAML assertion and // returns an HTTP response according to the specified binding. The // only supported binding right now is the HTTP-POST binding which returns // an HTML form in the appropriate format with Javascript to automatically // submit that form the to service provider's Assertion Customer Service // endpoint. // // If the SAML request is invalid or cannot be verified a simple StatusBadRequest // response is sent. // // If the assertion cannot be created or returned, a StatusInternalServerError // response is sent. func (idp *IdentityProvider) ServeSSO(w http.ResponseWriter, r *http.Request) { req, err := NewIdpAuthnRequest(idp, r) if err != nil { idp.Logger.Printf("failed to parse request: %s", err) http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest) return } if err := req.Validate(); err != nil { idp.Logger.Printf("failed to validate request: %s", err) http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest) return } // TODO(ross): we must check that the request ID has not been previously // issued. session := idp.SessionProvider.GetSession(w, r, req) if session == nil { return } assertionMaker := idp.AssertionMaker if assertionMaker == nil { assertionMaker = DefaultAssertionMaker{} } if err := assertionMaker.MakeAssertion(req, session); err != nil { idp.Logger.Printf("failed to make assertion: %s", err) http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) return } if err := req.WriteResponse(w); err != nil { idp.Logger.Printf("failed to write response: %s", err) http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) return } } // ServeIDPInitiated handes an IDP-initiated authorization request. Requests of this // type require us to know a registered service provider and (optionally) the RelayState // that will be passed to the application. func (idp *IdentityProvider) ServeIDPInitiated(w http.ResponseWriter, r *http.Request, serviceProviderID string, relayState string) { req := &IdpAuthnRequest{ IDP: idp, HTTPRequest: r, RelayState: relayState, Now: TimeNow(), } session := idp.SessionProvider.GetSession(w, r, req) if session == nil { // If GetSession returns nil, it must have written an HTTP response, per the interface // (this is probably because it drew a login form or something) return } var err error req.ServiceProviderMetadata, err = idp.ServiceProviderProvider.GetServiceProvider(r, serviceProviderID) if err == os.ErrNotExist { idp.Logger.Printf("cannot find service provider: %s", serviceProviderID) http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound) return } else if err != nil { idp.Logger.Printf("cannot find service provider %s: %v", serviceProviderID, err) http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) return } // find an ACS endpoint that we can use for _, spssoDescriptor := range req.ServiceProviderMetadata.SPSSODescriptors { for _, endpoint := range spssoDescriptor.AssertionConsumerServices { if endpoint.Binding == HTTPPostBinding { // explicitly copy loop iterator variables // // c.f. https://github.com/golang/go/wiki/CommonMistakes#using-reference-to-loop-iterator-variable // // (note that I'm pretty sure this isn't strictly necessary because we break out of the loop immediately, // but it certainly doesn't hurt anything and may prevent bugs in the future.) endpoint, spssoDescriptor := endpoint, spssoDescriptor req.ACSEndpoint = &endpoint req.SPSSODescriptor = &spssoDescriptor break } } if req.ACSEndpoint != nil { break } } if req.ACSEndpoint == nil { idp.Logger.Printf("saml metadata does not contain an Assertion Customer Service url") http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) return } assertionMaker := idp.AssertionMaker if assertionMaker == nil { assertionMaker = DefaultAssertionMaker{} } if err := assertionMaker.MakeAssertion(req, session); err != nil { idp.Logger.Printf("failed to make assertion: %s", err) http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) return } if err := req.WriteResponse(w); err != nil { idp.Logger.Printf("failed to write response: %s", err) http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) return } } // IdpAuthnRequest is used by IdentityProvider to handle a single authentication request. type IdpAuthnRequest struct { IDP *IdentityProvider HTTPRequest *http.Request RelayState string RequestBuffer []byte Request AuthnRequest ServiceProviderMetadata *EntityDescriptor SPSSODescriptor *SPSSODescriptor ACSEndpoint *IndexedEndpoint Assertion *Assertion AssertionEl *etree.Element ResponseEl *etree.Element Now time.Time } // NewIdpAuthnRequest returns a new IdpAuthnRequest for the given HTTP request to the authorization // service. func NewIdpAuthnRequest(idp *IdentityProvider, r *http.Request) (*IdpAuthnRequest, error) { req := &IdpAuthnRequest{ IDP: idp, HTTPRequest: r, Now: TimeNow(), } switch r.Method { case "GET": compressedRequest, err := base64.StdEncoding.DecodeString(r.URL.Query().Get("SAMLRequest")) if err != nil { return nil, fmt.Errorf("cannot decode request: %s", err) } req.RequestBuffer, err = ioutil.ReadAll(flate.NewReader(bytes.NewReader(compressedRequest))) if err != nil { return nil, fmt.Errorf("cannot decompress request: %s", err) } req.RelayState = r.URL.Query().Get("RelayState") case "POST": if err := r.ParseForm(); err != nil { return nil, err } var err error req.RequestBuffer, err = base64.StdEncoding.DecodeString(r.PostForm.Get("SAMLRequest")) if err != nil { return nil, err } req.RelayState = r.PostForm.Get("RelayState") default: return nil, fmt.Errorf("method not allowed") } return req, nil } // Validate checks that the authentication request is valid and assigns // the AuthnRequest and Metadata properties. Returns a non-nil error if the // request is not valid. func (req *IdpAuthnRequest) Validate() error { if err := xrv.Validate(bytes.NewReader(req.RequestBuffer)); err != nil { return err } if err := xml.Unmarshal(req.RequestBuffer, &req.Request); err != nil { return err } // We always have exactly one IDP SSO descriptor if len(req.IDP.Metadata().IDPSSODescriptors) != 1 { panic("expected exactly one IDP SSO descriptor in IDP metadata") } idpSsoDescriptor := req.IDP.Metadata().IDPSSODescriptors[0] // TODO(ross): support signed authn requests // For now we do the safe thing and fail in the case where we think // requests might be signed. if idpSsoDescriptor.WantAuthnRequestsSigned != nil && *idpSsoDescriptor.WantAuthnRequestsSigned { return fmt.Errorf("authn request signature checking is not currently supported") } // In http://docs.oasis-open.org/security/saml/v2.0/saml-bindings-2.0-os.pdf §3.4.5.2 // we get a description of the Destination attribute: // // If the message is signed, the Destination XML attribute in the root SAML // element of the protocol message MUST contain the URL to which the sender // has instructed the user agent to deliver the message. The recipient MUST // then verify that the value matches the location at which the message has // been received. // // We require the destination be correct either (a) if signing is enabled or // (b) if it was provided. mustHaveDestination := idpSsoDescriptor.WantAuthnRequestsSigned != nil && *idpSsoDescriptor.WantAuthnRequestsSigned mustHaveDestination = mustHaveDestination || req.Request.Destination != "" if mustHaveDestination { if req.Request.Destination != req.IDP.SSOURL.String() { return fmt.Errorf("expected destination to be %q, not %q", req.IDP.SSOURL.String(), req.Request.Destination) } } if req.Request.IssueInstant.Add(MaxIssueDelay).Before(req.Now) { return fmt.Errorf("request expired at %s", req.Request.IssueInstant.Add(MaxIssueDelay)) } if req.Request.Version != "2.0" { return fmt.Errorf("expected SAML request version 2.0 got %v", req.Request.Version) } // find the service provider serviceProviderID := req.Request.Issuer.Value serviceProvider, err := req.IDP.ServiceProviderProvider.GetServiceProvider(req.HTTPRequest, serviceProviderID) if err == os.ErrNotExist { return fmt.Errorf("cannot handle request from unknown service provider %s", serviceProviderID) } else if err != nil { return fmt.Errorf("cannot find service provider %s: %v", serviceProviderID, err) } req.ServiceProviderMetadata = serviceProvider // Check that the ACS URL matches an ACS endpoint in the SP metadata. if err := req.getACSEndpoint(); err != nil { return fmt.Errorf("cannot find assertion consumer service: %v", err) } return nil } func (req *IdpAuthnRequest) getACSEndpoint() error { if req.Request.AssertionConsumerServiceIndex != "" { for _, spssoDescriptor := range req.ServiceProviderMetadata.SPSSODescriptors { for _, spAssertionConsumerService := range spssoDescriptor.AssertionConsumerServices { if strconv.Itoa(spAssertionConsumerService.Index) == req.Request.AssertionConsumerServiceIndex { // explicitly copy loop iterator variables // // c.f. https://github.com/golang/go/wiki/CommonMistakes#using-reference-to-loop-iterator-variable // // (note that I'm pretty sure this isn't strictly necessary because we break out of the loop immediately, // but it certainly doesn't hurt anything and may prevent bugs in the future.) spssoDescriptor, spAssertionConsumerService := spssoDescriptor, spAssertionConsumerService req.SPSSODescriptor = &spssoDescriptor req.ACSEndpoint = &spAssertionConsumerService return nil } } } } if req.Request.AssertionConsumerServiceURL != "" { for _, spssoDescriptor := range req.ServiceProviderMetadata.SPSSODescriptors { for _, spAssertionConsumerService := range spssoDescriptor.AssertionConsumerServices { if spAssertionConsumerService.Location == req.Request.AssertionConsumerServiceURL { // explicitly copy loop iterator variables // // c.f. https://github.com/golang/go/wiki/CommonMistakes#using-reference-to-loop-iterator-variable // // (note that I'm pretty sure this isn't strictly necessary because we break out of the loop immediately, // but it certainly doesn't hurt anything and may prevent bugs in the future.) spssoDescriptor, spAssertionConsumerService := spssoDescriptor, spAssertionConsumerService req.SPSSODescriptor = &spssoDescriptor req.ACSEndpoint = &spAssertionConsumerService return nil } } } } // Some service providers, like the Microsoft Azure AD service provider, issue // assertion requests that don't specify an ACS url at all. if req.Request.AssertionConsumerServiceURL == "" && req.Request.AssertionConsumerServiceIndex == "" { // find a default ACS binding in the metadata that we can use for _, spssoDescriptor := range req.ServiceProviderMetadata.SPSSODescriptors { for _, spAssertionConsumerService := range spssoDescriptor.AssertionConsumerServices { if spAssertionConsumerService.IsDefault != nil && *spAssertionConsumerService.IsDefault { switch spAssertionConsumerService.Binding { case HTTPPostBinding, HTTPRedirectBinding: // explicitly copy loop iterator variables // // c.f. https://github.com/golang/go/wiki/CommonMistakes#using-reference-to-loop-iterator-variable // // (note that I'm pretty sure this isn't strictly necessary because we break out of the loop immediately, // but it certainly doesn't hurt anything and may prevent bugs in the future.) spssoDescriptor, spAssertionConsumerService := spssoDescriptor, spAssertionConsumerService req.SPSSODescriptor = &spssoDescriptor req.ACSEndpoint = &spAssertionConsumerService return nil } } } } // if we can't find a default, use *any* ACS binding for _, spssoDescriptor := range req.ServiceProviderMetadata.SPSSODescriptors { for _, spAssertionConsumerService := range spssoDescriptor.AssertionConsumerServices { switch spAssertionConsumerService.Binding { case HTTPPostBinding, HTTPRedirectBinding: // explicitly copy loop iterator variables // // c.f. https://github.com/golang/go/wiki/CommonMistakes#using-reference-to-loop-iterator-variable // // (note that I'm pretty sure this isn't strictly necessary because we break out of the loop immediately, // but it certainly doesn't hurt anything and may prevent bugs in the future.) spssoDescriptor, spAssertionConsumerService := spssoDescriptor, spAssertionConsumerService req.SPSSODescriptor = &spssoDescriptor req.ACSEndpoint = &spAssertionConsumerService return nil } } } } return os.ErrNotExist // no ACS url found or specified } // DefaultAssertionMaker produces a SAML assertion for the // given request and assigns it to req.Assertion. type DefaultAssertionMaker struct { } // MakeAssertion implements AssertionMaker. It produces a SAML assertion from the // given request and assigns it to req.Assertion. func (DefaultAssertionMaker) MakeAssertion(req *IdpAuthnRequest, session *Session) error { attributes := []Attribute{} var attributeConsumingService *AttributeConsumingService for _, acs := range req.SPSSODescriptor.AttributeConsumingServices { if acs.IsDefault != nil && *acs.IsDefault { // explicitly copy loop iterator variables // // c.f. https://github.com/golang/go/wiki/CommonMistakes#using-reference-to-loop-iterator-variable // // (note that I'm pretty sure this isn't strictly necessary because we break out of the loop immediately, // but it certainly doesn't hurt anything and may prevent bugs in the future.) acs := acs attributeConsumingService = &acs break } } if attributeConsumingService == nil { for _, acs := range req.SPSSODescriptor.AttributeConsumingServices { // explicitly copy loop iterator variables // // c.f. https://github.com/golang/go/wiki/CommonMistakes#using-reference-to-loop-iterator-variable // // (note that I'm pretty sure this isn't strictly necessary because we break out of the loop immediately, // but it certainly doesn't hurt anything and may prevent bugs in the future.) acs := acs attributeConsumingService = &acs break } } if attributeConsumingService == nil { attributeConsumingService = &AttributeConsumingService{} } for _, requestedAttribute := range attributeConsumingService.RequestedAttributes { if requestedAttribute.NameFormat == "urn:oasis:names:tc:SAML:2.0:attrname-format:basic" || requestedAttribute.NameFormat == "urn:oasis:names:tc:SAML:2.0:attrname-format:unspecified" { attrName := requestedAttribute.Name attrName = regexp.MustCompile("[^A-Za-z0-9]+").ReplaceAllString(attrName, "") switch attrName { case "email", "emailaddress": attributes = append(attributes, Attribute{ FriendlyName: requestedAttribute.FriendlyName, Name: requestedAttribute.Name, NameFormat: requestedAttribute.NameFormat, Values: []AttributeValue{{ Type: "xs:string", Value: session.UserEmail, }}, }) case "name", "fullname", "cn", "commonname": attributes = append(attributes, Attribute{ FriendlyName: requestedAttribute.FriendlyName, Name: requestedAttribute.Name, NameFormat: requestedAttribute.NameFormat, Values: []AttributeValue{{ Type: "xs:string", Value: session.UserCommonName, }}, }) case "givenname", "firstname": attributes = append(attributes, Attribute{ FriendlyName: requestedAttribute.FriendlyName, Name: requestedAttribute.Name, NameFormat: requestedAttribute.NameFormat, Values: []AttributeValue{{ Type: "xs:string", Value: session.UserGivenName, }}, }) case "surname", "lastname", "familyname": attributes = append(attributes, Attribute{ FriendlyName: requestedAttribute.FriendlyName, Name: requestedAttribute.Name, NameFormat: requestedAttribute.NameFormat, Values: []AttributeValue{{ Type: "xs:string", Value: session.UserSurname, }}, }) case "uid", "user", "userid": attributes = append(attributes, Attribute{ FriendlyName: requestedAttribute.FriendlyName, Name: requestedAttribute.Name, NameFormat: requestedAttribute.NameFormat, Values: []AttributeValue{{ Type: "xs:string", Value: session.UserName, }}, }) } } } if session.UserName != "" { attributes = append(attributes, Attribute{ FriendlyName: "uid", Name: "urn:oid:0.9.2342.19200300.100.1.1", NameFormat: "urn:oasis:names:tc:SAML:2.0:attrname-format:uri", Values: []AttributeValue{{ Type: "xs:string", Value: session.UserName, }}, }) } if session.UserEmail != "" { attributes = append(attributes, Attribute{ FriendlyName: "eduPersonPrincipalName", Name: "urn:oid:1.3.6.1.4.1.5923.1.1.1.6", NameFormat: "urn:oasis:names:tc:SAML:2.0:attrname-format:uri", Values: []AttributeValue{{ Type: "xs:string", Value: session.UserEmail, }}, }) } if session.UserSurname != "" { attributes = append(attributes, Attribute{ FriendlyName: "sn", Name: "urn:oid:2.5.4.4", NameFormat: "urn:oasis:names:tc:SAML:2.0:attrname-format:uri", Values: []AttributeValue{{ Type: "xs:string", Value: session.UserSurname, }}, }) } if session.UserGivenName != "" { attributes = append(attributes, Attribute{ FriendlyName: "givenName", Name: "urn:oid:2.5.4.42", NameFormat: "urn:oasis:names:tc:SAML:2.0:attrname-format:uri", Values: []AttributeValue{{ Type: "xs:string", Value: session.UserGivenName, }}, }) } if session.UserCommonName != "" { attributes = append(attributes, Attribute{ FriendlyName: "cn", Name: "urn:oid:2.5.4.3", NameFormat: "urn:oasis:names:tc:SAML:2.0:attrname-format:uri", Values: []AttributeValue{{ Type: "xs:string", Value: session.UserCommonName, }}, }) } if session.UserScopedAffiliation != "" { attributes = append(attributes, Attribute{ FriendlyName: "uid", Name: "urn:oid:1.3.6.1.4.1.5923.1.1.1.9", NameFormat: "urn:oasis:names:tc:SAML:2.0:attrname-format:uri", Values: []AttributeValue{{ Type: "xs:string", Value: session.UserScopedAffiliation, }}, }) } for _, ca := range session.CustomAttributes { attributes = append(attributes, ca) } if len(session.Groups) != 0 { groupMemberAttributeValues := []AttributeValue{} for _, group := range session.Groups { groupMemberAttributeValues = append(groupMemberAttributeValues, AttributeValue{ Type: "xs:string", Value: group, }) } attributes = append(attributes, Attribute{ FriendlyName: "eduPersonAffiliation", Name: "urn:oid:1.3.6.1.4.1.5923.1.1.1.1", NameFormat: "urn:oasis:names:tc:SAML:2.0:attrname-format:uri", Values: groupMemberAttributeValues, }) } // allow for some clock skew in the validity period using the // issuer's apparent clock. notBefore := req.Now.Add(-1 * MaxClockSkew) notOnOrAfterAfter := req.Now.Add(MaxIssueDelay) if notBefore.Before(req.Request.IssueInstant) { notBefore = req.Request.IssueInstant notOnOrAfterAfter = notBefore.Add(MaxIssueDelay) } req.Assertion = &Assertion{ ID: fmt.Sprintf("id-%x", randomBytes(20)), IssueInstant: TimeNow(), Version: "2.0", Issuer: Issuer{ Format: "urn:oasis:names:tc:SAML:2.0:nameid-format:entity", Value: req.IDP.Metadata().EntityID, }, Subject: &Subject{ NameID: &NameID{ Format: "urn:oasis:names:tc:SAML:2.0:nameid-format:transient", NameQualifier: req.IDP.Metadata().EntityID, SPNameQualifier: req.ServiceProviderMetadata.EntityID, Value: session.NameID, }, SubjectConfirmations: []SubjectConfirmation{ { Method: "urn:oasis:names:tc:SAML:2.0:cm:bearer", SubjectConfirmationData: &SubjectConfirmationData{ Address: req.HTTPRequest.RemoteAddr, InResponseTo: req.Request.ID, NotOnOrAfter: req.Now.Add(MaxIssueDelay), Recipient: req.ACSEndpoint.Location, }, }, }, }, Conditions: &Conditions{ NotBefore: notBefore, NotOnOrAfter: notOnOrAfterAfter, AudienceRestrictions: []AudienceRestriction{ { Audience: Audience{Value: req.ServiceProviderMetadata.EntityID}, }, }, }, AuthnStatements: []AuthnStatement{ { AuthnInstant: session.CreateTime, SessionIndex: session.Index, SubjectLocality: &SubjectLocality{ Address: req.HTTPRequest.RemoteAddr, }, AuthnContext: AuthnContext{ AuthnContextClassRef: &AuthnContextClassRef{ Value: "urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport", }, }, }, }, AttributeStatements: []AttributeStatement{ { Attributes: attributes, }, }, } return nil } // The Canonicalizer prefix list MUST be empty. Various implementations // (maybe ours?) do not appear to support non-empty prefix lists in XML C14N. const canonicalizerPrefixList = "" // MakeAssertionEl sets `AssertionEl` to a signed, possibly encrypted, version of `Assertion`. func (req *IdpAuthnRequest) MakeAssertionEl() error { keyPair := tls.Certificate{ Certificate: [][]byte{req.IDP.Certificate.Raw}, PrivateKey: req.IDP.Key, Leaf: req.IDP.Certificate, } for _, cert := range req.IDP.Intermediates { keyPair.Certificate = append(keyPair.Certificate, cert.Raw) } keyStore := dsig.TLSCertKeyStore(keyPair) signatureMethod := req.IDP.SignatureMethod if signatureMethod == "" { signatureMethod = dsig.RSASHA1SignatureMethod } signingContext := dsig.NewDefaultSigningContext(keyStore) signingContext.Canonicalizer = dsig.MakeC14N10ExclusiveCanonicalizerWithPrefixList(canonicalizerPrefixList) if err := signingContext.SetSignatureMethod(signatureMethod); err != nil { return err } assertionEl := req.Assertion.Element() signedAssertionEl, err := signingContext.SignEnveloped(assertionEl) if err != nil { return err } sigEl := signedAssertionEl.Child[len(signedAssertionEl.Child)-1] req.Assertion.Signature = sigEl.(*etree.Element) signedAssertionEl = req.Assertion.Element() certBuf, err := req.getSPEncryptionCert() if err == os.ErrNotExist { req.AssertionEl = signedAssertionEl return nil } else if err != nil { return err } var signedAssertionBuf []byte { doc := etree.NewDocument() doc.SetRoot(signedAssertionEl) signedAssertionBuf, err = doc.WriteToBytes() if err != nil { return err } } encryptor := xmlenc.OAEP() encryptor.BlockCipher = xmlenc.AES128CBC encryptor.DigestMethod = &xmlenc.SHA1 encryptedDataEl, err := encryptor.Encrypt(certBuf, signedAssertionBuf, nil) if err != nil { return err } encryptedDataEl.CreateAttr("Type", "http://www.w3.org/2001/04/xmlenc#Element") encryptedAssertionEl := etree.NewElement("saml:EncryptedAssertion") encryptedAssertionEl.AddChild(encryptedDataEl) req.AssertionEl = encryptedAssertionEl return nil } // WriteResponse writes the `Response` to the http.ResponseWriter. If // `Response` is not already set, it calls MakeResponse to produce it. func (req *IdpAuthnRequest) WriteResponse(w http.ResponseWriter) error { if req.ResponseEl == nil { if err := req.MakeResponse(); err != nil { return err } } doc := etree.NewDocument() doc.SetRoot(req.ResponseEl) responseBuf, err := doc.WriteToBytes() if err != nil { return err } // the only supported binding is the HTTP-POST binding switch req.ACSEndpoint.Binding { case HTTPPostBinding: tmpl := template.Must(template.New("saml-post-form").Parse(`` + `
` + `` + `` + `` + `
` + `` + `` + ``)) data := struct { URL string SAMLResponse string RelayState string }{ URL: req.ACSEndpoint.Location, SAMLResponse: base64.StdEncoding.EncodeToString(responseBuf), RelayState: req.RelayState, } buf := bytes.NewBuffer(nil) if err := tmpl.Execute(buf, data); err != nil { return err } if _, err := io.Copy(w, buf); err != nil { return err } return nil default: return fmt.Errorf("%s: unsupported binding %s", req.ServiceProviderMetadata.EntityID, req.ACSEndpoint.Binding) } } // getSPEncryptionCert returns the certificate which we can use to encrypt things // to the SP in PEM format, or nil if no such certificate is found. func (req *IdpAuthnRequest) getSPEncryptionCert() (*x509.Certificate, error) { certStr := "" for _, keyDescriptor := range req.SPSSODescriptor.KeyDescriptors { if keyDescriptor.Use == "encryption" { certStr = keyDescriptor.KeyInfo.X509Data.X509Certificates[0].Data break } } // If there are no certs explicitly labeled for encryption, return the first // non-empty cert we find. if certStr == "" { for _, keyDescriptor := range req.SPSSODescriptor.KeyDescriptors { if keyDescriptor.Use == "" && len(keyDescriptor.KeyInfo.X509Data.X509Certificates) != 0 && keyDescriptor.KeyInfo.X509Data.X509Certificates[0].Data != "" { certStr = keyDescriptor.KeyInfo.X509Data.X509Certificates[0].Data break } } } if certStr == "" { return nil, os.ErrNotExist } // cleanup whitespace and re-encode a PEM certStr = regexp.MustCompile(`\s+`).ReplaceAllString(certStr, "") certBytes, err := base64.StdEncoding.DecodeString(certStr) if err != nil { return nil, fmt.Errorf("cannot decode certificate base64: %v", err) } cert, err := x509.ParseCertificate(certBytes) if err != nil { return nil, fmt.Errorf("cannot parse certificate: %v", err) } return cert, nil } // unmarshalEtreeHack parses `el` and sets values in the structure `v`. // // This is a hack -- it first serializes the element, then uses xml.Unmarshal. func unmarshalEtreeHack(el *etree.Element, v interface{}) error { doc := etree.NewDocument() doc.SetRoot(el) buf, err := doc.WriteToBytes() if err != nil { return err } return xml.Unmarshal(buf, v) } // MakeResponse creates and assigns a new SAML response in ResponseEl. `Assertion` must // be non-nil. If MakeAssertionEl() has not been called, this function calls it for // you. func (req *IdpAuthnRequest) MakeResponse() error { if req.AssertionEl == nil { if err := req.MakeAssertionEl(); err != nil { return err } } response := &Response{ Destination: req.ACSEndpoint.Location, ID: fmt.Sprintf("id-%x", randomBytes(20)), InResponseTo: req.Request.ID, IssueInstant: req.Now, Version: "2.0", Issuer: &Issuer{ Format: "urn:oasis:names:tc:SAML:2.0:nameid-format:entity", Value: req.IDP.MetadataURL.String(), }, Status: Status{ StatusCode: StatusCode{ Value: StatusSuccess, }, }, } responseEl := response.Element() responseEl.AddChild(req.AssertionEl) // AssertionEl either an EncryptedAssertion or Assertion element // Sign the response element (we've already signed the Assertion element) { keyPair := tls.Certificate{ Certificate: [][]byte{req.IDP.Certificate.Raw}, PrivateKey: req.IDP.Key, Leaf: req.IDP.Certificate, } for _, cert := range req.IDP.Intermediates { keyPair.Certificate = append(keyPair.Certificate, cert.Raw) } keyStore := dsig.TLSCertKeyStore(keyPair) signatureMethod := req.IDP.SignatureMethod if signatureMethod == "" { signatureMethod = dsig.RSASHA1SignatureMethod } signingContext := dsig.NewDefaultSigningContext(keyStore) signingContext.Canonicalizer = dsig.MakeC14N10ExclusiveCanonicalizerWithPrefixList(canonicalizerPrefixList) if err := signingContext.SetSignatureMethod(signatureMethod); err != nil { return err } signedResponseEl, err := signingContext.SignEnveloped(responseEl) if err != nil { return err } sigEl := signedResponseEl.ChildElements()[len(signedResponseEl.ChildElements())-1] response.Signature = sigEl responseEl = response.Element() responseEl.AddChild(req.AssertionEl) } req.ResponseEl = responseEl return nil } saml-0.4.6/identity_provider_go116_test.go000066400000000000000000000040671415467341100205400ustar00rootroot00000000000000//go:build !go1.17 // +build !go1.17 package saml import ( "bytes" "compress/flate" "encoding/base64" "io/ioutil" "net/http" "net/http/httptest" "net/url" "testing" "gotest.tools/assert" is "gotest.tools/assert/cmp" ) func TestIDPHTTPCanHandleSSORequest(t *testing.T) { test := NewIdentifyProviderTest(t) w := httptest.NewRecorder() const validRequest = `lJJBayoxFIX%2FypC9JhnU5wszAz7lgWCLaNtFd5fMbQ1MkmnunVb%2FfUfbUqEgdhs%2BTr5zkmLW8S5s8KVD4mzvm0Cl6FIwEciRCeCRDFuznd2sTD5Upk2Ro42NyGZEmNjFMI%2BBOo9pi%2BnVWbzfrEqxY27JSEntEPfg2waHNnpJ4JtcgiWRLfoLXYBjwDfu6p%2B8JIoiWy5K4eqBUipXIzVRUwXKKtRK53qkJ3qqQVuNPUjU4TIQQ%2BBS5EqPBzofKH2ntBn%2FMervo8jWnyX%2BuVC78FwKkT1gopNKX1JUxSklXTMIfM0gsv8xeeDL%2BPGk7%2FF0Qg0GdnwQ1cW5PDLUwFDID6uquO1Dlot1bJw9%2FPLRmia%2BzRMCYyk4dSiq6205QSDXOxfy3KAq5Pkvqt4DAAD%2F%2Fw%3D%3D` r, _ := http.NewRequest("GET", "https://idp.example.com/saml/sso?RelayState=ThisIsTheRelayState&"+ "SAMLRequest="+validRequest, nil) test.IDP.Handler().ServeHTTP(w, r) assert.Check(t, is.Equal(http.StatusOK, w.Code)) // rejects requests that are invalid w = httptest.NewRecorder() r, _ = http.NewRequest("GET", "https://idp.example.com/saml/sso?RelayState=ThisIsTheRelayState&"+ "SAMLRequest=PEF1dGhuUmVxdWVzdA%3D%3D", nil) test.IDP.Handler().ServeHTTP(w, r) assert.Check(t, is.Equal(http.StatusBadRequest, w.Code)) // rejects requests that contain malformed XML { a, _ := url.QueryUnescape(validRequest) b, _ := base64.StdEncoding.DecodeString(a) c, _ := ioutil.ReadAll(flate.NewReader(bytes.NewReader(b))) d := bytes.Replace(c, []byte("]]"), 1) f := bytes.Buffer{} e, _ := flate.NewWriter(&f, flate.DefaultCompression) e.Write(d) e.Close() g := base64.StdEncoding.EncodeToString(f.Bytes()) invalidRequest := url.QueryEscape(g) w = httptest.NewRecorder() r, _ = http.NewRequest("GET", "https://idp.example.com/saml/sso?RelayState=ThisIsTheRelayState&"+ "SAMLRequest="+invalidRequest, nil) test.IDP.Handler().ServeHTTP(w, r) assert.Check(t, is.Equal(http.StatusBadRequest, w.Code)) } } saml-0.4.6/identity_provider_test.go000066400000000000000000001147561415467341100176320ustar00rootroot00000000000000package saml import ( "crypto" "crypto/rsa" "crypto/x509" "encoding/base64" "encoding/pem" "encoding/xml" "fmt" "math/rand" "net/http" "net/http/httptest" "net/url" "os" "strings" "testing" "time" "gotest.tools/assert" is "gotest.tools/assert/cmp" "gotest.tools/golden" "github.com/beevik/etree" "github.com/golang-jwt/jwt/v4" "github.com/crewjam/saml/logger" "github.com/crewjam/saml/testsaml" "github.com/crewjam/saml/xmlenc" ) type IdentityProviderTest struct { SPKey *rsa.PrivateKey SPCertificate *x509.Certificate SP ServiceProvider Key crypto.PrivateKey Certificate *x509.Certificate SessionProvider SessionProvider IDP IdentityProvider } func mustParseURL(s string) url.URL { rv, err := url.Parse(s) if err != nil { panic(err) } return *rv } func mustParsePrivateKey(pemStr []byte) crypto.PrivateKey { b, _ := pem.Decode(pemStr) if b == nil { panic("cannot parse PEM") } k, err := x509.ParsePKCS1PrivateKey(b.Bytes) if err != nil { panic(err) } return k } func mustParseCertificate(pemStr []byte) *x509.Certificate { b, _ := pem.Decode(pemStr) if b == nil { panic("cannot parse PEM") } cert, err := x509.ParseCertificate(b.Bytes) if err != nil { panic(err) } return cert } func NewIdentifyProviderTest(t *testing.T) *IdentityProviderTest { test := IdentityProviderTest{} TimeNow = func() time.Time { rv, _ := time.Parse("Mon Jan 2 15:04:05 MST 2006", "Mon Dec 1 01:57:09 UTC 2015") return rv } jwt.TimeFunc = TimeNow RandReader = &testRandomReader{} // TODO(ross): remove this and use the below generator xmlenc.RandReader = rand.New(rand.NewSource(0)) //nolint:gosec // deterministic random numbers for tests test.SPKey = mustParsePrivateKey(golden.Get(t, "sp_key.pem")).(*rsa.PrivateKey) test.SPCertificate = mustParseCertificate(golden.Get(t, "sp_cert.pem")) test.SP = ServiceProvider{ Key: test.SPKey, Certificate: test.SPCertificate, MetadataURL: mustParseURL("https://sp.example.com/saml2/metadata"), AcsURL: mustParseURL("https://sp.example.com/saml2/acs"), IDPMetadata: &EntityDescriptor{}, } test.Key = mustParsePrivateKey(golden.Get(t, "idp_key.pem")) test.Certificate = mustParseCertificate(golden.Get(t, "idp_cert.pem")) test.IDP = IdentityProvider{ Key: test.Key, Certificate: test.Certificate, Logger: logger.DefaultLogger, MetadataURL: mustParseURL("https://idp.example.com/saml/metadata"), SSOURL: mustParseURL("https://idp.example.com/saml/sso"), ServiceProviderProvider: &mockServiceProviderProvider{ GetServiceProviderFunc: func(r *http.Request, serviceProviderID string) (*EntityDescriptor, error) { if serviceProviderID == test.SP.MetadataURL.String() { return test.SP.Metadata(), nil } return nil, os.ErrNotExist }, }, SessionProvider: &mockSessionProvider{ GetSessionFunc: func(w http.ResponseWriter, r *http.Request, req *IdpAuthnRequest) *Session { return nil }, }, } // bind the service provider and the IDP test.SP.IDPMetadata = test.IDP.Metadata() return &test } type mockSessionProvider struct { GetSessionFunc func(w http.ResponseWriter, r *http.Request, req *IdpAuthnRequest) *Session } func (msp *mockSessionProvider) GetSession(w http.ResponseWriter, r *http.Request, req *IdpAuthnRequest) *Session { return msp.GetSessionFunc(w, r, req) } type mockServiceProviderProvider struct { GetServiceProviderFunc func(r *http.Request, serviceProviderID string) (*EntityDescriptor, error) } func (mspp *mockServiceProviderProvider) GetServiceProvider(r *http.Request, serviceProviderID string) (*EntityDescriptor, error) { return mspp.GetServiceProviderFunc(r, serviceProviderID) } func TestIDPCanProduceMetadata(t *testing.T) { test := NewIdentifyProviderTest(t) expected := &EntityDescriptor{ ValidUntil: TimeNow().Add(DefaultValidDuration), CacheDuration: DefaultValidDuration, EntityID: "https://idp.example.com/saml/metadata", IDPSSODescriptors: []IDPSSODescriptor{ { SSODescriptor: SSODescriptor{ RoleDescriptor: RoleDescriptor{ ProtocolSupportEnumeration: "urn:oasis:names:tc:SAML:2.0:protocol", KeyDescriptors: []KeyDescriptor{ { Use: "signing", KeyInfo: KeyInfo{ XMLName: xml.Name{}, X509Data: X509Data{ X509Certificates: []X509Certificate{ {Data: "MIIB7zCCAVgCCQDFzbKIp7b3MTANBgkqhkiG9w0BAQUFADA8MQswCQYDVQQGEwJVUzELMAkGA1UECAwCR0ExDDAKBgNVBAoMA2ZvbzESMBAGA1UEAwwJbG9jYWxob3N0MB4XDTEzMTAwMjAwMDg1MVoXDTE0MTAwMjAwMDg1MVowPDELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkdBMQwwCgYDVQQKDANmb28xEjAQBgNVBAMMCWxvY2FsaG9zdDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA1PMHYmhZj308kWLhZVT4vOulqx/9ibm5B86fPWwUKKQ2i12MYtz07tzukPymisTDhQaqyJ8Kqb/6JjhmeMnEOdTvSPmHO8m1ZVveJU6NoKRn/mP/BD7FW52WhbrUXLSeHVSKfWkNk6S4hk9MV9TswTvyRIKvRsw0X/gfnqkroJcCAwEAATANBgkqhkiG9w0BAQUFAAOBgQCMMlIO+GNcGekevKgkakpMdAqJfs24maGb90DvTLbRZRD7Xvn1MnVBBS9hzlXiFLYOInXACMW5gcoRFfeTQLSouMM8o57h0uKjfTmuoWHLQLi6hnF+cvCsEFiJZ4AbF+DgmO6TarJ8O05t8zvnOwJlNCASPZRH/JmF8tX0hoHuAQ=="}, }, }, }, EncryptionMethods: nil, }, { Use: "encryption", KeyInfo: KeyInfo{ XMLName: xml.Name{}, X509Data: X509Data{ X509Certificates: []X509Certificate{ {Data: "MIIB7zCCAVgCCQDFzbKIp7b3MTANBgkqhkiG9w0BAQUFADA8MQswCQYDVQQGEwJVUzELMAkGA1UECAwCR0ExDDAKBgNVBAoMA2ZvbzESMBAGA1UEAwwJbG9jYWxob3N0MB4XDTEzMTAwMjAwMDg1MVoXDTE0MTAwMjAwMDg1MVowPDELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkdBMQwwCgYDVQQKDANmb28xEjAQBgNVBAMMCWxvY2FsaG9zdDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA1PMHYmhZj308kWLhZVT4vOulqx/9ibm5B86fPWwUKKQ2i12MYtz07tzukPymisTDhQaqyJ8Kqb/6JjhmeMnEOdTvSPmHO8m1ZVveJU6NoKRn/mP/BD7FW52WhbrUXLSeHVSKfWkNk6S4hk9MV9TswTvyRIKvRsw0X/gfnqkroJcCAwEAATANBgkqhkiG9w0BAQUFAAOBgQCMMlIO+GNcGekevKgkakpMdAqJfs24maGb90DvTLbRZRD7Xvn1MnVBBS9hzlXiFLYOInXACMW5gcoRFfeTQLSouMM8o57h0uKjfTmuoWHLQLi6hnF+cvCsEFiJZ4AbF+DgmO6TarJ8O05t8zvnOwJlNCASPZRH/JmF8tX0hoHuAQ=="}, }, }, }, EncryptionMethods: []EncryptionMethod{ {Algorithm: "http://www.w3.org/2001/04/xmlenc#aes128-cbc"}, {Algorithm: "http://www.w3.org/2001/04/xmlenc#aes192-cbc"}, {Algorithm: "http://www.w3.org/2001/04/xmlenc#aes256-cbc"}, {Algorithm: "http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p"}, }, }, }, }, NameIDFormats: []NameIDFormat{NameIDFormat("urn:oasis:names:tc:SAML:2.0:nameid-format:transient")}, }, SingleSignOnServices: []Endpoint{ { Binding: "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect", Location: "https://idp.example.com/saml/sso", }, { Binding: "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST", Location: "https://idp.example.com/saml/sso", }, }, }, }, } assert.Check(t, is.DeepEqual(expected, test.IDP.Metadata())) } func TestIDPHTTPCanHandleMetadataRequest(t *testing.T) { test := NewIdentifyProviderTest(t) w := httptest.NewRecorder() r, _ := http.NewRequest("GET", "https://idp.example.com/saml/metadata", nil) test.IDP.Handler().ServeHTTP(w, r) assert.Check(t, is.Equal(http.StatusOK, w.Code)) assert.Check(t, is.Equal("application/samlmetadata+xml", w.Header().Get("Content-type"))) assert.Check(t, strings.HasPrefix(string(w.Body.Bytes()), "" + " https://sp.example.com/saml2/metadata" + " urn:oasis:names:tc:SAML:2.0:nameid-format:transient" + ""), } assert.Check(t, req.Validate()) assert.Check(t, req.ServiceProviderMetadata != nil) assert.Check(t, is.DeepEqual(&IndexedEndpoint{ Binding: "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST", Location: "https://sp.example.com/saml2/acs", Index: 1, }, req.ACSEndpoint)) req = IdpAuthnRequest{ Now: TimeNow(), IDP: &test.IDP, RequestBuffer: []byte("" + " https://sp.example.com/saml2/metadata" + " urn:oasis:names:tc:SAML:2.0:nameid-format:transient" + ""), } assert.Check(t, is.Error(req.Validate(), "expected destination to be \"https://idp.example.com/saml/sso\", not \"https://idp.wrongDestination.com/saml/sso\"")) req = IdpAuthnRequest{ Now: TimeNow(), IDP: &test.IDP, RequestBuffer: []byte("" + "" + " https://sp.example.com/saml2/metadata" + " urn:oasis:names:tc:SAML:2.0:nameid-format:transient" + ""), } assert.Check(t, is.Error(req.Validate(), "request expired at 2014-12-01 01:58:39 +0000 UTC")) req = IdpAuthnRequest{ Now: TimeNow(), IDP: &test.IDP, RequestBuffer: []byte("" + "" + " https://sp.example.com/saml2/metadata" + " urn:oasis:names:tc:SAML:2.0:nameid-format:transient" + ""), } assert.Check(t, is.Error(req.Validate(), "expected SAML request version 2.0 got 4.2")) req = IdpAuthnRequest{ Now: TimeNow(), IDP: &test.IDP, RequestBuffer: []byte("" + "" + " https://unknownSP.example.com/saml2/metadata" + " urn:oasis:names:tc:SAML:2.0:nameid-format:transient" + ""), } assert.Check(t, is.Error(req.Validate(), "cannot handle request from unknown service provider https://unknownSP.example.com/saml2/metadata")) req = IdpAuthnRequest{ Now: TimeNow(), IDP: &test.IDP, RequestBuffer: []byte("" + "" + " https://sp.example.com/saml2/metadata" + " urn:oasis:names:tc:SAML:2.0:nameid-format:transient" + ""), } assert.Check(t, is.Error(req.Validate(), "cannot find assertion consumer service: file does not exist")) } func TestIDPMakeAssertion(t *testing.T) { test := NewIdentifyProviderTest(t) req := IdpAuthnRequest{ Now: TimeNow(), IDP: &test.IDP, RequestBuffer: []byte("" + "" + " https://sp.example.com/saml2/metadata" + " urn:oasis:names:tc:SAML:2.0:nameid-format:transient" + ""), } req.HTTPRequest, _ = http.NewRequest("POST", "http://idp.example.com/saml/sso", nil) assert.Check(t, req.Validate()) err := DefaultAssertionMaker{}.MakeAssertion(&req, &Session{ ID: "f00df00df00d", UserName: "alice", }) assert.Check(t, err) expected := &Assertion{ ID: "id-00020406080a0c0e10121416181a1c1e20222426", IssueInstant: TimeNow(), Version: "2.0", Issuer: Issuer{ Format: "urn:oasis:names:tc:SAML:2.0:nameid-format:entity", Value: "https://idp.example.com/saml/metadata", }, Signature: nil, Subject: &Subject{ NameID: &NameID{Format: "urn:oasis:names:tc:SAML:2.0:nameid-format:transient", NameQualifier: "https://idp.example.com/saml/metadata", SPNameQualifier: "https://sp.example.com/saml2/metadata", Value: ""}, SubjectConfirmations: []SubjectConfirmation{ { Method: "urn:oasis:names:tc:SAML:2.0:cm:bearer", SubjectConfirmationData: &SubjectConfirmationData{ Address: "", InResponseTo: "id-00020406080a0c0e10121416181a1c1e", NotOnOrAfter: TimeNow().Add(MaxIssueDelay), Recipient: "https://sp.example.com/saml2/acs", }, }, }, }, Conditions: &Conditions{ NotBefore: TimeNow(), NotOnOrAfter: TimeNow().Add(MaxIssueDelay), AudienceRestrictions: []AudienceRestriction{ { Audience: Audience{Value: "https://sp.example.com/saml2/metadata"}, }, }, }, AuthnStatements: []AuthnStatement{ { AuthnInstant: time.Time{}, SessionIndex: "", SubjectLocality: &SubjectLocality{}, AuthnContext: AuthnContext{ AuthnContextClassRef: &AuthnContextClassRef{Value: "urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport"}, }, }, }, AttributeStatements: []AttributeStatement{ { Attributes: []Attribute{ { FriendlyName: "uid", Name: "urn:oid:0.9.2342.19200300.100.1.1", NameFormat: "urn:oasis:names:tc:SAML:2.0:attrname-format:uri", Values: []AttributeValue{ { Type: "xs:string", Value: "alice", }, }, }, }, }, }, } assert.Check(t, is.DeepEqual(expected, req.Assertion)) err = DefaultAssertionMaker{}.MakeAssertion(&req, &Session{ ID: "f00df00df00d", CreateTime: TimeNow(), ExpireTime: TimeNow().Add(time.Hour), Index: "9999", NameID: "ba5eba11", Groups: []string{"Users", "Administrators", "♀"}, UserName: "alice", UserEmail: "alice@example.com", UserCommonName: "Alice Smith", UserSurname: "Smith", UserGivenName: "Alice", }) assert.Check(t, err) expectedAttributes := []Attribute{ { FriendlyName: "uid", Name: "urn:oid:0.9.2342.19200300.100.1.1", NameFormat: "urn:oasis:names:tc:SAML:2.0:attrname-format:uri", Values: []AttributeValue{ { Type: "xs:string", Value: "alice", }, }, }, { FriendlyName: "eduPersonPrincipalName", Name: "urn:oid:1.3.6.1.4.1.5923.1.1.1.6", NameFormat: "urn:oasis:names:tc:SAML:2.0:attrname-format:uri", Values: []AttributeValue{ { Type: "xs:string", Value: "alice@example.com", }, }, }, { FriendlyName: "sn", Name: "urn:oid:2.5.4.4", NameFormat: "urn:oasis:names:tc:SAML:2.0:attrname-format:uri", Values: []AttributeValue{ { Type: "xs:string", Value: "Smith", }, }, }, { FriendlyName: "givenName", Name: "urn:oid:2.5.4.42", NameFormat: "urn:oasis:names:tc:SAML:2.0:attrname-format:uri", Values: []AttributeValue{ { Type: "xs:string", Value: "Alice", }, }, }, { FriendlyName: "cn", Name: "urn:oid:2.5.4.3", NameFormat: "urn:oasis:names:tc:SAML:2.0:attrname-format:uri", Values: []AttributeValue{ { Type: "xs:string", Value: "Alice Smith", }, }, }, { FriendlyName: "eduPersonAffiliation", Name: "urn:oid:1.3.6.1.4.1.5923.1.1.1.1", NameFormat: "urn:oasis:names:tc:SAML:2.0:attrname-format:uri", Values: []AttributeValue{ { Type: "xs:string", Value: "Users", }, { Type: "xs:string", Value: "Administrators", }, { Type: "xs:string", Value: "♀", }, }, }, } assert.Check(t, is.DeepEqual(expectedAttributes, req.Assertion.AttributeStatements[0].Attributes)) } func TestIDPMarshalAssertion(t *testing.T) { test := NewIdentifyProviderTest(t) req := IdpAuthnRequest{ Now: TimeNow(), IDP: &test.IDP, RequestBuffer: []byte("" + "" + " https://sp.example.com/saml2/metadata" + " urn:oasis:names:tc:SAML:2.0:nameid-format:transient" + ""), } req.HTTPRequest, _ = http.NewRequest("POST", "http://idp.example.com/saml/sso", nil) err := req.Validate() assert.Check(t, err) err = DefaultAssertionMaker{}.MakeAssertion(&req, &Session{ ID: "f00df00df00d", UserName: "alice", }) assert.Check(t, err) err = req.MakeAssertionEl() assert.Check(t, err) // Compare the plaintext first expectedPlaintext := "https://idp.example.com/saml/metadatagjE0eLUMVt+kK0rIGYvnzHV/2Ok=Jm1rrxo2x7SYTnaS97bCdnVLQGeQuCMTjiSUvwzBkWFR+xcPr+n38dXmv0q0R68tO7L2ELhLtBdLm/dWsxruN23TMGVQyHIPMgJExdnYb7fwqx6es/NAdbDUBTbSdMX0vhIlTsHu5F0bJ0Tg0iAo9uRk9VeBdkaxtPa7+4yl1PQ=MIIB7zCCAVgCCQDFzbKIp7b3MTANBgkqhkiG9w0BAQUFADA8MQswCQYDVQQGEwJVUzELMAkGA1UECAwCR0ExDDAKBgNVBAoMA2ZvbzESMBAGA1UEAwwJbG9jYWxob3N0MB4XDTEzMTAwMjAwMDg1MVoXDTE0MTAwMjAwMDg1MVowPDELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkdBMQwwCgYDVQQKDANmb28xEjAQBgNVBAMMCWxvY2FsaG9zdDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA1PMHYmhZj308kWLhZVT4vOulqx/9ibm5B86fPWwUKKQ2i12MYtz07tzukPymisTDhQaqyJ8Kqb/6JjhmeMnEOdTvSPmHO8m1ZVveJU6NoKRn/mP/BD7FW52WhbrUXLSeHVSKfWkNk6S4hk9MV9TswTvyRIKvRsw0X/gfnqkroJcCAwEAATANBgkqhkiG9w0BAQUFAAOBgQCMMlIO+GNcGekevKgkakpMdAqJfs24maGb90DvTLbRZRD7Xvn1MnVBBS9hzlXiFLYOInXACMW5gcoRFfeTQLSouMM8o57h0uKjfTmuoWHLQLi6hnF+cvCsEFiJZ4AbF+DgmO6TarJ8O05t8zvnOwJlNCASPZRH/JmF8tX0hoHuAQ==https://sp.example.com/saml2/metadataurn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransportalice" actualPlaintext := "" { doc := etree.NewDocument() doc.SetRoot(req.AssertionEl) el := doc.FindElement("//EncryptedAssertion/EncryptedData") actualPlaintextBuf, err := xmlenc.Decrypt(test.SPKey, el) assert.Check(t, err) actualPlaintext = string(actualPlaintextBuf) } assert.Check(t, is.Equal(expectedPlaintext, actualPlaintext)) doc := etree.NewDocument() doc.SetRoot(req.AssertionEl) assertionBuffer, err := doc.WriteToBytes() assert.Check(t, err) golden.Assert(t, string(assertionBuffer), t.Name()+"_encrypted_assertion") } func TestIDPMakeResponse(t *testing.T) { test := NewIdentifyProviderTest(t) req := IdpAuthnRequest{ Now: TimeNow(), IDP: &test.IDP, RequestBuffer: golden.Get(t, "TestIDPMakeResponse_request_buffer"), } req.HTTPRequest, _ = http.NewRequest("POST", "http://idp.example.com/saml/sso", nil) err := req.Validate() assert.Check(t, err) err = DefaultAssertionMaker{}.MakeAssertion(&req, &Session{ ID: "f00df00df00d", UserName: "alice", }) assert.Check(t, err) err = req.MakeAssertionEl() assert.Check(t, err) req.AssertionEl = etree.NewElement("this-is-an-encrypted-assertion") err = req.MakeResponse() assert.Check(t, err) response := Response{} err = unmarshalEtreeHack(req.ResponseEl, &response) assert.Check(t, err) doc := etree.NewDocument() doc.SetRoot(req.ResponseEl) doc.Indent(2) responseStr, err := doc.WriteToString() assert.Check(t, err) golden.Assert(t, responseStr, t.Name()+"_response.xml") } func TestIDPWriteResponse(t *testing.T) { test := NewIdentifyProviderTest(t) req := IdpAuthnRequest{ Now: TimeNow(), IDP: &test.IDP, RelayState: "THIS_IS_THE_RELAY_STATE", RequestBuffer: golden.Get(t, "TestIDPWriteResponse_RequestBuffer.xml"), ResponseEl: etree.NewElement("THIS_IS_THE_SAML_RESPONSE"), } req.HTTPRequest, _ = http.NewRequest("POST", "http://idp.example.com/saml/sso", nil) err := req.Validate() assert.Check(t, err) w := httptest.NewRecorder() err = req.WriteResponse(w) assert.Check(t, err) assert.Check(t, is.Equal(200, w.Code)) golden.Assert(t, w.Body.String(), t.Name()+"response.html") } func TestIDPIDPInitiatedNewSession(t *testing.T) { test := NewIdentifyProviderTest(t) test.IDP.SessionProvider = &mockSessionProvider{ GetSessionFunc: func(w http.ResponseWriter, r *http.Request, req *IdpAuthnRequest) *Session { fmt.Fprintf(w, "RelayState: %s", req.RelayState) return nil }, } w := httptest.NewRecorder() r, _ := http.NewRequest("GET", "https://idp.example.com/services/sp/whoami", nil) test.IDP.ServeIDPInitiated(w, r, test.SP.MetadataURL.String(), "ThisIsTheRelayState") assert.Check(t, is.Equal(200, w.Code)) assert.Check(t, is.Equal("RelayState: ThisIsTheRelayState", string(w.Body.Bytes()))) } func TestIDPIDPInitiatedExistingSession(t *testing.T) { test := NewIdentifyProviderTest(t) test.IDP.SessionProvider = &mockSessionProvider{ GetSessionFunc: func(w http.ResponseWriter, r *http.Request, req *IdpAuthnRequest) *Session { return &Session{ ID: "f00df00df00d", UserName: "alice", } }, } w := httptest.NewRecorder() r, _ := http.NewRequest("GET", "https://idp.example.com/services/sp/whoami", nil) test.IDP.ServeIDPInitiated(w, r, test.SP.MetadataURL.String(), "ThisIsTheRelayState") assert.Check(t, is.Equal(200, w.Code)) golden.Assert(t, w.Body.String(), t.Name()+"_response") } func TestIDPIDPInitiatedBadServiceProvider(t *testing.T) { test := NewIdentifyProviderTest(t) test.IDP.SessionProvider = &mockSessionProvider{ GetSessionFunc: func(w http.ResponseWriter, r *http.Request, req *IdpAuthnRequest) *Session { return &Session{ ID: "f00df00df00d", UserName: "alice", } }, } w := httptest.NewRecorder() r, _ := http.NewRequest("GET", "https://idp.example.com/services/sp/whoami", nil) test.IDP.ServeIDPInitiated(w, r, "https://wrong.url/metadata", "ThisIsTheRelayState") assert.Check(t, is.Equal(http.StatusNotFound, w.Code)) } func TestIDPCanHandleUnencryptedResponse(t *testing.T) { test := NewIdentifyProviderTest(t) test.IDP.SessionProvider = &mockSessionProvider{ GetSessionFunc: func(w http.ResponseWriter, r *http.Request, req *IdpAuthnRequest) *Session { return &Session{ID: "f00df00df00d", UserName: "alice"} }, } metadata := EntityDescriptor{} err := xml.Unmarshal( golden.Get(t, "TestIDPCanHandleUnencryptedResponse_idp_metadata.xml"), &metadata) assert.Check(t, err) test.IDP.ServiceProviderProvider = &mockServiceProviderProvider{ GetServiceProviderFunc: func(r *http.Request, serviceProviderID string) (*EntityDescriptor, error) { if serviceProviderID == "https://gitlab.example.com/users/saml/metadata" { return &metadata, nil } return nil, os.ErrNotExist }, } req := IdpAuthnRequest{ Now: TimeNow(), IDP: &test.IDP, RequestBuffer: golden.Get(t, "TestIDPCanHandleUnencryptedResponse_request"), } req.HTTPRequest, _ = http.NewRequest("POST", "http://idp.example.com/saml/sso", nil) err = req.Validate() assert.Check(t, err) err = DefaultAssertionMaker{}.MakeAssertion(&req, &Session{ ID: "f00df00df00d", UserName: "alice", }) assert.Check(t, err) err = req.MakeAssertionEl() assert.Check(t, err) err = req.MakeResponse() assert.Check(t, err) doc := etree.NewDocument() doc.SetRoot(req.ResponseEl) doc.Indent(2) responseStr, _ := doc.WriteToString() golden.Assert(t, responseStr, t.Name()+"_response") } func TestIDPRequestedAttributes(t *testing.T) { test := NewIdentifyProviderTest(t) metadata := EntityDescriptor{} err := xml.Unmarshal(golden.Get(t, "TestIDPRequestedAttributes_idp_metadata.xml"), &metadata) assert.Check(t, err) requestURL, err := test.SP.MakeRedirectAuthenticationRequest("ThisIsTheRelayState") assert.Check(t, err) r, _ := http.NewRequest("GET", requestURL.String(), nil) req, err := NewIdpAuthnRequest(&test.IDP, r) req.ServiceProviderMetadata = &metadata req.ACSEndpoint = &metadata.SPSSODescriptors[0].AssertionConsumerServices[0] req.SPSSODescriptor = &metadata.SPSSODescriptors[0] assert.Check(t, err) err = DefaultAssertionMaker{}.MakeAssertion(req, &Session{ ID: "f00df00df00d", UserName: "alice", UserEmail: "alice@example.com", UserGivenName: "Alice", UserSurname: "Smith", UserCommonName: "Alice Smith", }) assert.Check(t, err) expectedAttributes := []AttributeStatement{{ Attributes: []Attribute{ { FriendlyName: "Email address", Name: "email", NameFormat: "urn:oasis:names:tc:SAML:2.0:attrname-format:basic", Values: []AttributeValue{ { Type: "xs:string", Value: "alice@example.com", }, }, }, { FriendlyName: "Full name", Name: "name", NameFormat: "urn:oasis:names:tc:SAML:2.0:attrname-format:basic", Values: []AttributeValue{ { Type: "xs:string", Value: "Alice Smith", }, }, }, { FriendlyName: "Given name", Name: "first_name", NameFormat: "urn:oasis:names:tc:SAML:2.0:attrname-format:basic", Values: []AttributeValue{ { Type: "xs:string", Value: "Alice", }, }, }, { FriendlyName: "Family name", Name: "last_name", NameFormat: "urn:oasis:names:tc:SAML:2.0:attrname-format:basic", Values: []AttributeValue{ { Type: "xs:string", Value: "Smith", }, }, }, { FriendlyName: "uid", Name: "urn:oid:0.9.2342.19200300.100.1.1", NameFormat: "urn:oasis:names:tc:SAML:2.0:attrname-format:uri", Values: []AttributeValue{ { Type: "xs:string", Value: "alice", }, }, }, { FriendlyName: "eduPersonPrincipalName", Name: "urn:oid:1.3.6.1.4.1.5923.1.1.1.6", NameFormat: "urn:oasis:names:tc:SAML:2.0:attrname-format:uri", Values: []AttributeValue{ { Type: "xs:string", Value: "alice@example.com", }, }, }, { FriendlyName: "sn", Name: "urn:oid:2.5.4.4", NameFormat: "urn:oasis:names:tc:SAML:2.0:attrname-format:uri", Values: []AttributeValue{ { Type: "xs:string", Value: "Smith", }, }, }, { FriendlyName: "givenName", Name: "urn:oid:2.5.4.42", NameFormat: "urn:oasis:names:tc:SAML:2.0:attrname-format:uri", Values: []AttributeValue{ { Type: "xs:string", Value: "Alice", }, }, }, { FriendlyName: "cn", Name: "urn:oid:2.5.4.3", NameFormat: "urn:oasis:names:tc:SAML:2.0:attrname-format:uri", Values: []AttributeValue{ { Type: "xs:string", Value: "Alice Smith", }, }, }, }}} assert.Check(t, is.DeepEqual(expectedAttributes, req.Assertion.AttributeStatements)) } func TestIDPNoDestination(t *testing.T) { test := NewIdentifyProviderTest(t) test.IDP.SessionProvider = &mockSessionProvider{ GetSessionFunc: func(w http.ResponseWriter, r *http.Request, req *IdpAuthnRequest) *Session { return &Session{ID: "f00df00df00d", UserName: "alice"} }, } metadata := EntityDescriptor{} err := xml.Unmarshal(golden.Get(t, "TestIDPNoDestination_idp_metadata.xml"), &metadata) assert.Check(t, err) test.IDP.ServiceProviderProvider = &mockServiceProviderProvider{ GetServiceProviderFunc: func(r *http.Request, serviceProviderID string) (*EntityDescriptor, error) { if serviceProviderID == "https://gitlab.example.com/users/saml/metadata" { return &metadata, nil } return nil, os.ErrNotExist }, } req := IdpAuthnRequest{ Now: TimeNow(), IDP: &test.IDP, RequestBuffer: golden.Get(t, "TestIDPNoDestination_request"), } req.HTTPRequest, _ = http.NewRequest("POST", "http://idp.example.com/saml/sso", nil) err = req.Validate() assert.Check(t, err) err = DefaultAssertionMaker{}.MakeAssertion(&req, &Session{ ID: "f00df00df00d", UserName: "alice", }) assert.Check(t, err) err = req.MakeAssertionEl() assert.Check(t, err) err = req.MakeResponse() assert.Check(t, err) } saml-0.4.6/logger/000077500000000000000000000000001415467341100137425ustar00rootroot00000000000000saml-0.4.6/logger/logger.go000066400000000000000000000020101415467341100155410ustar00rootroot00000000000000package logger import ( "log" "os" ) // Interface provides the minimal logging interface type Interface interface { // Printf prints to the logger using the format. Printf(format string, v ...interface{}) // Print prints to the logger. Print(v ...interface{}) // Println prints new line. Println(v ...interface{}) // Fatal is equivalent to Print() followed by a call to os.Exit(1). Fatal(v ...interface{}) // Fatalf is equivalent to Printf() followed by a call to os.Exit(1). Fatalf(format string, v ...interface{}) // Fatalln is equivalent to Println() followed by a call to os.Exit(1). Fatalln(v ...interface{}) // Panic is equivalent to Print() followed by a call to panic(). Panic(v ...interface{}) // Panicf is equivalent to Printf() followed by a call to panic(). Panicf(format string, v ...interface{}) // Panicln is equivalent to Println() followed by a call to panic(). Panicln(v ...interface{}) } // DefaultLogger logs messages to os.Stdout var DefaultLogger = log.New(os.Stdout, "", log.LstdFlags) saml-0.4.6/metadata.go000066400000000000000000000311151415467341100145730ustar00rootroot00000000000000package saml import ( "encoding/xml" "time" "github.com/beevik/etree" ) // HTTPPostBinding is the official URN for the HTTP-POST binding (transport) const HTTPPostBinding = "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" // HTTPRedirectBinding is the official URN for the HTTP-Redirect binding (transport) const HTTPRedirectBinding = "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" // HTTPArtifactBinding is the official URN for the HTTP-Artifact binding (transport) const HTTPArtifactBinding = "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Artifact" // SOAPBinding is the official URN for the SOAP binding (transport) const SOAPBinding = "urn:oasis:names:tc:SAML:2.0:bindings:SOAP" // EntitiesDescriptor represents the SAML object of the same name. // // See http://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf §2.3.1 type EntitiesDescriptor struct { XMLName xml.Name `xml:"urn:oasis:names:tc:SAML:2.0:metadata EntitiesDescriptor"` ID *string `xml:",attr,omitempty"` ValidUntil *time.Time `xml:"validUntil,attr,omitempty"` CacheDuration *time.Duration `xml:"cacheDuration,attr,omitempty"` Name *string `xml:",attr,omitempty"` Signature *etree.Element EntitiesDescriptors []EntitiesDescriptor `xml:"urn:oasis:names:tc:SAML:2.0:metadata EntitiesDescriptor"` EntityDescriptors []EntityDescriptor `xml:"urn:oasis:names:tc:SAML:2.0:metadata EntityDescriptor"` } // Metadata as been renamed to EntityDescriptor // // This change was made to be consistent with the rest of the API which uses names // from the SAML specification for types. // // This is a tombstone to help you discover this fact. You should update references // to saml.Metadata to be saml.EntityDescriptor. var Metadata = struct{}{} // EntityDescriptor represents the SAML EntityDescriptor object. // // See http://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf §2.3.2 type EntityDescriptor struct { XMLName xml.Name `xml:"urn:oasis:names:tc:SAML:2.0:metadata EntityDescriptor"` EntityID string `xml:"entityID,attr"` ID string `xml:",attr,omitempty"` ValidUntil time.Time `xml:"validUntil,attr,omitempty"` CacheDuration time.Duration `xml:"cacheDuration,attr,omitempty"` Signature *etree.Element RoleDescriptors []RoleDescriptor `xml:"RoleDescriptor"` IDPSSODescriptors []IDPSSODescriptor `xml:"IDPSSODescriptor"` SPSSODescriptors []SPSSODescriptor `xml:"SPSSODescriptor"` AuthnAuthorityDescriptors []AuthnAuthorityDescriptor `xml:"AuthnAuthorityDescriptor"` AttributeAuthorityDescriptors []AttributeAuthorityDescriptor `xml:"AttributeAuthorityDescriptor"` PDPDescriptors []PDPDescriptor `xml:"PDPDescriptor"` AffiliationDescriptor *AffiliationDescriptor Organization *Organization ContactPerson *ContactPerson AdditionalMetadataLocations []string `xml:"AdditionalMetadataLocation"` } // MarshalXML implements xml.Marshaler func (m EntityDescriptor) MarshalXML(e *xml.Encoder, start xml.StartElement) error { type Alias EntityDescriptor aux := &struct { ValidUntil RelaxedTime `xml:"validUntil,attr,omitempty"` CacheDuration Duration `xml:"cacheDuration,attr,omitempty"` *Alias }{ ValidUntil: RelaxedTime(m.ValidUntil), CacheDuration: Duration(m.CacheDuration), Alias: (*Alias)(&m), } return e.Encode(aux) } // UnmarshalXML implements xml.Unmarshaler func (m *EntityDescriptor) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { type Alias EntityDescriptor aux := &struct { ValidUntil RelaxedTime `xml:"validUntil,attr,omitempty"` CacheDuration Duration `xml:"cacheDuration,attr,omitempty"` *Alias }{ Alias: (*Alias)(m), } if err := d.DecodeElement(aux, &start); err != nil { return err } m.ValidUntil = time.Time(aux.ValidUntil) m.CacheDuration = time.Duration(aux.CacheDuration) return nil } // Organization represents the SAML Organization object. // // See http://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf §2.3.2.1 type Organization struct { OrganizationNames []LocalizedName `xml:"OrganizationName"` OrganizationDisplayNames []LocalizedName `xml:"OrganizationDisplayName"` OrganizationURLs []LocalizedURI `xml:"OrganizationURL"` } // LocalizedName represents the SAML type localizedNameType. // // See http://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf §2.2.4 type LocalizedName struct { Lang string `xml:"http://www.w3.org/XML/1998/namespace lang,attr"` Value string `xml:",chardata"` } // LocalizedURI represents the SAML type localizedURIType. // // See http://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf §2.2.5 type LocalizedURI struct { Lang string `xml:"http://www.w3.org/XML/1998/namespace lang,attr"` Value string `xml:",chardata"` } // ContactPerson represents the SAML element ContactPerson. // // See http://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf §2.3.2.2 type ContactPerson struct { ContactType string `xml:"contactType,attr"` Company string GivenName string SurName string EmailAddresses []string `xml:"EmailAddress"` TelephoneNumbers []string `xml:"TelephoneNumber"` } // RoleDescriptor represents the SAML element RoleDescriptor. // // See http://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf §2.4.1 type RoleDescriptor struct { ID string `xml:",attr,omitempty"` ValidUntil *time.Time `xml:"validUntil,attr,omitempty"` CacheDuration time.Duration `xml:"cacheDuration,attr,omitempty"` ProtocolSupportEnumeration string `xml:"protocolSupportEnumeration,attr"` ErrorURL string `xml:"errorURL,attr,omitempty"` Signature *etree.Element KeyDescriptors []KeyDescriptor `xml:"KeyDescriptor,omitempty"` Organization *Organization `xml:"Organization,omitempty"` ContactPeople []ContactPerson `xml:"ContactPerson,omitempty"` } // KeyDescriptor represents the XMLSEC object of the same name type KeyDescriptor struct { Use string `xml:"use,attr"` KeyInfo KeyInfo `xml:"http://www.w3.org/2000/09/xmldsig# KeyInfo"` EncryptionMethods []EncryptionMethod `xml:"EncryptionMethod"` } // EncryptionMethod represents the XMLSEC object of the same name type EncryptionMethod struct { Algorithm string `xml:"Algorithm,attr"` } // KeyInfo represents the XMLSEC object of the same name type KeyInfo struct { XMLName xml.Name `xml:"http://www.w3.org/2000/09/xmldsig# KeyInfo"` X509Data X509Data `xml:"X509Data"` } // X509Data represents the XMLSEC object of the same name type X509Data struct { XMLName xml.Name `xml:"http://www.w3.org/2000/09/xmldsig# X509Data"` X509Certificates []X509Certificate `xml:"X509Certificate"` } // X509Certificate represents the XMLSEC object of the same name type X509Certificate struct { XMLName xml.Name `xml:"http://www.w3.org/2000/09/xmldsig# X509Certificate"` Data string `xml:",chardata"` } // Endpoint represents the SAML EndpointType object. // // See http://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf §2.2.2 type Endpoint struct { Binding string `xml:"Binding,attr"` Location string `xml:"Location,attr"` ResponseLocation string `xml:"ResponseLocation,attr,omitempty"` } // IndexedEndpoint represents the SAML IndexedEndpointType object. // // See http://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf §2.2.3 type IndexedEndpoint struct { Binding string `xml:"Binding,attr"` Location string `xml:"Location,attr"` ResponseLocation *string `xml:"ResponseLocation,attr,omitempty"` Index int `xml:"index,attr"` IsDefault *bool `xml:"isDefault,attr"` } // SSODescriptor represents the SAML complex type SSODescriptor // // See http://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf §2.4.2 type SSODescriptor struct { RoleDescriptor ArtifactResolutionServices []IndexedEndpoint `xml:"ArtifactResolutionService"` SingleLogoutServices []Endpoint `xml:"SingleLogoutService"` ManageNameIDServices []Endpoint `xml:"ManageNameIDService"` NameIDFormats []NameIDFormat `xml:"NameIDFormat"` } // IDPSSODescriptor represents the SAML IDPSSODescriptorType object. // // See http://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf §2.4.3 type IDPSSODescriptor struct { XMLName xml.Name `xml:"urn:oasis:names:tc:SAML:2.0:metadata IDPSSODescriptor"` SSODescriptor WantAuthnRequestsSigned *bool `xml:",attr"` SingleSignOnServices []Endpoint `xml:"SingleSignOnService"` ArtifactResolutionServices []Endpoint `xml:"ArtifactResolutionService"` NameIDMappingServices []Endpoint `xml:"NameIDMappingService"` AssertionIDRequestServices []Endpoint `xml:"AssertionIDRequestService"` AttributeProfiles []string `xml:"AttributeProfile"` Attributes []Attribute `xml:"Attribute"` } // SPSSODescriptor represents the SAML SPSSODescriptorType object. // // See http://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf §2.4.2 type SPSSODescriptor struct { XMLName xml.Name `xml:"urn:oasis:names:tc:SAML:2.0:metadata SPSSODescriptor"` SSODescriptor AuthnRequestsSigned *bool `xml:",attr"` WantAssertionsSigned *bool `xml:",attr"` AssertionConsumerServices []IndexedEndpoint `xml:"AssertionConsumerService"` AttributeConsumingServices []AttributeConsumingService `xml:"AttributeConsumingService"` } // AttributeConsumingService represents the SAML AttributeConsumingService object. // // See http://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf §2.4.4.1 type AttributeConsumingService struct { Index int `xml:"index,attr"` IsDefault *bool `xml:"isDefault,attr"` ServiceNames []LocalizedName `xml:"ServiceName"` ServiceDescriptions []LocalizedName `xml:"ServiceDescription"` RequestedAttributes []RequestedAttribute `xml:"RequestedAttribute"` } // RequestedAttribute represents the SAML RequestedAttribute object. // // See http://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf §2.4.4.2 type RequestedAttribute struct { Attribute IsRequired *bool `xml:"isRequired,attr"` } // AuthnAuthorityDescriptor represents the SAML AuthnAuthorityDescriptor object. // // See http://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf §2.4.5 type AuthnAuthorityDescriptor struct { RoleDescriptor AuthnQueryServices []Endpoint `xml:"AuthnQueryService"` AssertionIDRequestServices []Endpoint `xml:"AssertionIDRequestService"` NameIDFormats []NameIDFormat `xml:"NameIDFormat"` } // PDPDescriptor represents the SAML PDPDescriptor object. // // See http://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf §2.4.6 type PDPDescriptor struct { RoleDescriptor AuthzServices []Endpoint `xml:"AuthzService"` AssertionIDRequestServices []Endpoint `xml:"AssertionIDRequestService"` NameIDFormats []NameIDFormat `xml:"NameIDFormat"` } // AttributeAuthorityDescriptor represents the SAML AttributeAuthorityDescriptor object. // // See http://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf §2.4.7 type AttributeAuthorityDescriptor struct { RoleDescriptor AttributeServices []Endpoint `xml:"AttributeService"` AssertionIDRequestServices []Endpoint `xml:"AssertionIDRequestService"` NameIDFormats []NameIDFormat `xml:"NameIDFormat"` AttributeProfiles []string `xml:"AttributeProfile"` Attributes []Attribute `xml:"Attribute"` } // AffiliationDescriptor represents the SAML AffiliationDescriptor object. // // See http://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf §2.5 type AffiliationDescriptor struct { AffiliationOwnerID string `xml:"affiliationOwnerID,attr"` ID string `xml:",attr"` ValidUntil time.Time `xml:"validUntil,attr,omitempty"` CacheDuration time.Duration `xml:"cacheDuration,attr"` Signature *etree.Element AffiliateMembers []string `xml:"AffiliateMember"` KeyDescriptors []KeyDescriptor `xml:"KeyDescriptor"` } saml-0.4.6/metadata_test.go000066400000000000000000000126631415467341100156410ustar00rootroot00000000000000package saml import ( "encoding/xml" "testing" "time" "gotest.tools/assert" is "gotest.tools/assert/cmp" "gotest.tools/golden" ) func TestCanParseMetadata(t *testing.T) { buf := golden.Get(t, "TestCanParseMetadata_metadata.xml") metadata := EntityDescriptor{} err := xml.Unmarshal(buf, &metadata) assert.Check(t, err) var False = false var True = true expected := EntityDescriptor{ EntityID: "https://dev.aa.kndr.org/users/auth/saml/metadata", ID: "_af805d1c-c2e3-444e-9cf5-efc664eeace6", ValidUntil: time.Date(2001, time.February, 3, 4, 5, 6, 789000000, time.UTC), CacheDuration: time.Hour, SPSSODescriptors: []SPSSODescriptor{ { XMLName: xml.Name{Space: "urn:oasis:names:tc:SAML:2.0:metadata", Local: "SPSSODescriptor"}, SSODescriptor: SSODescriptor{ RoleDescriptor: RoleDescriptor{ ProtocolSupportEnumeration: "urn:oasis:names:tc:SAML:2.0:protocol", }, }, AuthnRequestsSigned: &False, WantAssertionsSigned: &False, AssertionConsumerServices: []IndexedEndpoint{ { Binding: "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST", Location: "https://dev.aa.kndr.org/users/auth/saml/callback", Index: 0, IsDefault: &True, }, }, AttributeConsumingServices: []AttributeConsumingService{ { Index: 1, IsDefault: &True, ServiceNames: []LocalizedName{{Lang: "en", Value: "Required attributes"}}, RequestedAttributes: []RequestedAttribute{ { Attribute: Attribute{ FriendlyName: "Email address", Name: "email", NameFormat: "urn:oasis:names:tc:SAML:2.0:attrname-format:basic", }, }, { Attribute: Attribute{ FriendlyName: "Full name", Name: "name", NameFormat: "urn:oasis:names:tc:SAML:2.0:attrname-format:basic", }, }, { Attribute: Attribute{ FriendlyName: "Given name", Name: "first_name", NameFormat: "urn:oasis:names:tc:SAML:2.0:attrname-format:basic", }, }, { Attribute: Attribute{ FriendlyName: "Family name", Name: "last_name", NameFormat: "urn:oasis:names:tc:SAML:2.0:attrname-format:basic", }, }, }, }, }, }, }, } assert.Check(t, is.DeepEqual(expected, metadata)) } func TestCanProduceSPMetadata(t *testing.T) { validUntil, _ := time.Parse("2006-02-01T15:04:05.000000", "2013-10-03T00:32:19.104000") AuthnRequestsSigned := true WantAssertionsSigned := true metadata := EntityDescriptor{ EntityID: "http://localhost:5000/e087a985171710fb9fb30f30f41384f9/saml2/metadata/", ValidUntil: validUntil, CacheDuration: time.Hour, SPSSODescriptors: []SPSSODescriptor{ { AuthnRequestsSigned: &AuthnRequestsSigned, WantAssertionsSigned: &WantAssertionsSigned, SSODescriptor: SSODescriptor{ RoleDescriptor: RoleDescriptor{ ProtocolSupportEnumeration: "urn:oasis:names:tc:SAML:2.0:protocol", KeyDescriptors: []KeyDescriptor{ { Use: "encryption", KeyInfo: KeyInfo{ X509Data: X509Data{ X509Certificates: []X509Certificate{ { Data: `MIIB7zCCAVgCCQDFzbKIp7b3MTANBgkqhkiG9w0BAQUFADA8MQswCQYDVQQGEwJVUzELMAkGA1UE CAwCR0ExDDAKBgNVBAoMA2ZvbzESMBAGA1UEAwwJbG9jYWxob3N0MB4XDTEzMTAwMjAwMDg1MVoX DTE0MTAwMjAwMDg1MVowPDELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkdBMQwwCgYDVQQKDANmb28x EjAQBgNVBAMMCWxvY2FsaG9zdDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA1PMHYmhZj308 kWLhZVT4vOulqx/9ibm5B86fPWwUKKQ2i12MYtz07tzukPymisTDhQaqyJ8Kqb/6JjhmeMnEOdTv SPmHO8m1ZVveJU6NoKRn/mP/BD7FW52WhbrUXLSeHVSKfWkNk6S4hk9MV9TswTvyRIKvRsw0X/gf nqkroJcCAwEAATANBgkqhkiG9w0BAQUFAAOBgQCMMlIO+GNcGekevKgkakpMdAqJfs24maGb90Dv TLbRZRD7Xvn1MnVBBS9hzlXiFLYOInXACMW5gcoRFfeTQLSouMM8o57h0uKjfTmuoWHLQLi6hnF+ cvCsEFiJZ4AbF+DgmO6TarJ8O05t8zvnOwJlNCASPZRH/JmF8tX0hoHuAQ==`, }, }, }, }, }, { Use: "signing", KeyInfo: KeyInfo{ X509Data: X509Data{ X509Certificates: []X509Certificate{ { Data: `MIIB7zCCAVgCCQDFzbKIp7b3MTANBgkqhkiG9w0BAQUFADA8MQswCQYDVQQGEwJVUzELMAkGA1UE CAwCR0ExDDAKBgNVBAoMA2ZvbzESMBAGA1UEAwwJbG9jYWxob3N0MB4XDTEzMTAwMjAwMDg1MVoX DTE0MTAwMjAwMDg1MVowPDELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkdBMQwwCgYDVQQKDANmb28x EjAQBgNVBAMMCWxvY2FsaG9zdDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA1PMHYmhZj308 kWLhZVT4vOulqx/9ibm5B86fPWwUKKQ2i12MYtz07tzukPymisTDhQaqyJ8Kqb/6JjhmeMnEOdTv SPmHO8m1ZVveJU6NoKRn/mP/BD7FW52WhbrUXLSeHVSKfWkNk6S4hk9MV9TswTvyRIKvRsw0X/gf nqkroJcCAwEAATANBgkqhkiG9w0BAQUFAAOBgQCMMlIO+GNcGekevKgkakpMdAqJfs24maGb90Dv TLbRZRD7Xvn1MnVBBS9hzlXiFLYOInXACMW5gcoRFfeTQLSouMM8o57h0uKjfTmuoWHLQLi6hnF+ cvCsEFiJZ4AbF+DgmO6TarJ8O05t8zvnOwJlNCASPZRH/JmF8tX0hoHuAQ==`, }, }, }, }, }, }, }, SingleLogoutServices: []Endpoint{{ Binding: "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect", Location: "http://localhost:5000/e087a985171710fb9fb30f30f41384f9/saml2/ls/", }}, }, AssertionConsumerServices: []IndexedEndpoint{{ Binding: "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST", Location: "http://localhost:5000/e087a985171710fb9fb30f30f41384f9/saml2/ls/", Index: 1, }}, }, }, } buf, err := xml.MarshalIndent(metadata, "", " ") assert.Check(t, err) golden.Assert(t, string(buf), "TestCanProduceSPMetadata_expected") } saml-0.4.6/saml.go000066400000000000000000000214401415467341100137470ustar00rootroot00000000000000// Package saml contains a partial implementation of the SAML standard in golang. // SAML is a standard for identity federation, i.e. either allowing a third party to authenticate your users or allowing third parties to rely on us to authenticate their users. // // Introduction // // In SAML parlance an Identity Provider (IDP) is a service that knows how to authenticate users. A Service Provider (SP) is a service that delegates authentication to an IDP. If you are building a service where users log in with someone else's credentials, then you are a Service Provider. This package supports implementing both service providers and identity providers. // // The core package contains the implementation of SAML. The package samlsp provides helper middleware suitable for use in Service Provider applications. The package samlidp provides a rudimentary IDP service that is useful for testing or as a starting point for other integrations. // // Breaking Changes // // Version 0.4.0 introduces a few breaking changes to the _samlsp_ package in order to make the package more extensible, and to clean up the interfaces a bit. The default behavior remains the same, but you can now provide interface implementations of _RequestTracker_ (which tracks pending requests), _Session_ (which handles maintaining a session) and _OnError_ which handles reporting errors. // // Public fields of _samlsp.Middleware_ have changed, so some usages may require adjustment. See [issue 231](https://github.com/crewjam/saml/issues/231) for details. // // The option to provide an IDP metadata URL has been deprecated. Instead, we recommend that you use the `FetchMetadata()` function, or fetch the metadata yourself and use the new `ParseMetadata()` function, and pass the metadata in _samlsp.Options.IDPMetadata_. // // Similarly, the _HTTPClient_ field is now deprecated because it was only used for fetching metdata, which is no longer directly implemented. // // The fields that manage how cookies are set are deprecated as well. To customize how cookies are managed, provide custom implementation of _RequestTracker_ and/or _Session_, perhaps by extending the default implementations. // // The deprecated fields have not been removed from the Options structure, but will be in future. // // In particular we have deprecated the following fields in // _samlsp.Options_: // // - `Logger` - This was used to emit errors while validating, which is an anti-pattern. // - `IDPMetadataURL` - Instead use `FetchMetadata()` // - `HTTPClient` - Instead pass httpClient to FetchMetadata // - `CookieMaxAge` - Instead assign a custom CookieRequestTracker or CookieSessionProvider // - `CookieName` - Instead assign a custom CookieRequestTracker or CookieSessionProvider // - `CookieDomain` - Instead assign a custom CookieRequestTracker or CookieSessionProvider // - `CookieDomain` - Instead assign a custom CookieRequestTracker or CookieSessionProvider // // Getting Started as a Service Provider // // Let us assume we have a simple web application to protect. We'll modify this application so it uses SAML to authenticate users. // // ```golang // package main // // import ( // "fmt" // "net/http" // ) // // func hello(w http.ResponseWriter, r *http.Request) { // fmt.Fprintf(w, "Hello, World!") // } // // func main() { // app := http.HandlerFunc(hello) // http.Handle("/hello", app) // http.ListenAndServe(":8000", nil) // } // ``` // // Each service provider must have an self-signed X.509 key pair established. You can generate your own with something like this: // // openssl req -x509 -newkey rsa:2048 -keyout myservice.key -out myservice.cert -days 365 -nodes -subj "/CN=myservice.example.com" // // We will use `samlsp.Middleware` to wrap the endpoint we want to protect. Middleware provides both an `http.Handler` to serve the SAML specific URLs and a set of wrappers to require the user to be logged in. We also provide the URL where the service provider can fetch the metadata from the IDP at startup. In our case, we'll use [samltest.id](https://samltest.id/), an identity provider designed for testing. // // ```golang // package main // // import ( // "crypto/rsa" // "crypto/tls" // "crypto/x509" // "fmt" // "net/http" // "net/url" // // "github.com/crewjam/saml/samlsp" // ) // // func hello(w http.ResponseWriter, r *http.Request) { // fmt.Fprintf(w, "Hello, %s!", samlsp.Token(r.Context()).Attributes.Get("cn")) // } // // func main() { // keyPair, err := tls.LoadX509KeyPair("myservice.cert", "myservice.key") // if err != nil { // panic(err) // TODO handle error // } // keyPair.Leaf, err = x509.ParseCertificate(keyPair.Certificate[0]) // if err != nil { // panic(err) // TODO handle error // } // // idpMetadataURL, err := url.Parse("https://samltest.id/saml/idp") // if err != nil { // panic(err) // TODO handle error // } // // rootURL, err := url.Parse("http://localhost:8000") // if err != nil { // panic(err) // TODO handle error // } // // samlSP, _ := samlsp.New(samlsp.Options{ // URL: *rootURL, // Key: keyPair.PrivateKey.(*rsa.PrivateKey), // Certificate: keyPair.Leaf, // IDPMetadataURL: idpMetadataURL, // }) // app := http.HandlerFunc(hello) // http.Handle("/hello", samlSP.RequireAccount(app)) // http.Handle("/saml/", samlSP) // http.ListenAndServe(":8000", nil) // } // ``` // // Next we'll have to register our service provider with the identity provider to establish trust from the service provider to the IDP. For [samltest.id](https://samltest.id/), you can do something like: // // mdpath=saml-test-$USER-$HOST.xml // curl localhost:8000/saml/metadata > $mdpath // // Navigate to https://samltest.id/upload.php and upload the file you fetched. // // Now you should be able to authenticate. The flow should look like this: // // 1. You browse to `localhost:8000/hello` // // 1. The middleware redirects you to `https://samltest.id/idp/profile/SAML2/Redirect/SSO` // // 1. samltest.id prompts you for a username and password. // // 1. samltest.id returns you an HTML document which contains an HTML form setup to POST to `localhost:8000/saml/acs`. The form is automatically submitted if you have javascript enabled. // // 1. The local service validates the response, issues a session cookie, and redirects you to the original URL, `localhost:8000/hello`. // // 1. This time when `localhost:8000/hello` is requested there is a valid session and so the main content is served. // // Getting Started as an Identity Provider // // Please see `example/idp/` for a substantially complete example of how to use the library and helpers to be an identity provider. // // Support // // The SAML standard is huge and complex with many dark corners and strange, unused features. This package implements the most commonly used subset of these features required to provide a single sign on experience. The package supports at least the subset of SAML known as [interoperable SAML](http://saml2int.org). // // This package supports the Web SSO profile. Message flows from the service provider to the IDP are supported using the HTTP Redirect binding and the HTTP POST binding. Message flows from the IDP to the service provider are supported via the HTTP POST binding. // // The package can produce signed SAML assertions, and can validate both signed and encrypted SAML assertions. It does not support signed or encrypted requests. // // RelayState // // The _RelayState_ parameter allows you to pass user state information across the authentication flow. The most common use for this is to allow a user to request a deep link into your site, be redirected through the SAML login flow, and upon successful completion, be directed to the originally requested link, rather than the root. // // Unfortunately, _RelayState_ is less useful than it could be. Firstly, it is not authenticated, so anything you supply must be signed to avoid XSS or CSRF. Secondly, it is limited to 80 bytes in length, which precludes signing. (See section 3.6.3.1 of SAMLProfiles.) // // References // // The SAML specification is a collection of PDFs (sadly): // // - [SAMLCore](http://docs.oasis-open.org/security/saml/v2.0/saml-core-2.0-os.pdf) defines data types. // // - [SAMLBindings](http://docs.oasis-open.org/security/saml/v2.0/saml-bindings-2.0-os.pdf) defines the details of the HTTP requests in play. // // - [SAMLProfiles](http://docs.oasis-open.org/security/saml/v2.0/saml-profiles-2.0-os.pdf) describes data flows. // // - [SAMLConformance](http://docs.oasis-open.org/security/saml/v2.0/saml-conformance-2.0-os.pdf) includes a support matrix for various parts of the protocol. // // [SAMLtest](https://samltest.id/) is a testing ground for SAML service and identity providers. // // Security Issues // // Please do not report security issues in the issue tracker. Rather, please contact me directly at ross@kndr.org ([PGP Key `78B6038B3B9DFB88`](https://keybase.io/crewjam)). package saml saml-0.4.6/saml_gen.go000066400000000000000000000002441415467341100145770ustar00rootroot00000000000000package saml //go:generate bash -c "(cat README.md | grep -E -v '^# SAML' | sed 's|^## ||g' | sed 's|\\*\\*||g' | sed 's|^|// |g'; echo 'package saml') > saml.go" saml-0.4.6/samlidp/000077500000000000000000000000001415467341100141145ustar00rootroot00000000000000saml-0.4.6/samlidp/memory_store.go000066400000000000000000000025131415467341100171700ustar00rootroot00000000000000package samlidp import ( "encoding/json" "strings" "sync" ) // MemoryStore is an implementation of Store that resides completely // in memory. type MemoryStore struct { mu sync.RWMutex data map[string]string } // Get fetches the data stored in `key` and unmarshals it into `value`. func (s *MemoryStore) Get(key string, value interface{}) error { s.mu.RLock() defer s.mu.RUnlock() v, ok := s.data[key] if !ok { return ErrNotFound } return json.Unmarshal([]byte(v), value) } // Put marshals `value` and stores it in `key`. func (s *MemoryStore) Put(key string, value interface{}) error { s.mu.Lock() defer s.mu.Unlock() if s.data == nil { s.data = map[string]string{} } buf, err := json.Marshal(value) if err != nil { return err } s.data[key] = string(buf) return nil } // Delete removes `key` func (s *MemoryStore) Delete(key string) error { s.mu.Lock() defer s.mu.Unlock() delete(s.data, key) return nil } // List returns all the keys that start with `prefix`. The prefix is // stripped from each returned value. So if keys are ["aa", "ab", "cd"] // then List("a") would produce []string{"a", "b"} func (s *MemoryStore) List(prefix string) ([]string, error) { rv := []string{} for k := range s.data { if strings.HasPrefix(k, prefix) { rv = append(rv, strings.TrimPrefix(k, prefix)) } } return rv, nil } saml-0.4.6/samlidp/samlidp.go000066400000000000000000000070711415467341100161010ustar00rootroot00000000000000// Package samlidp a rudimentary SAML identity provider suitable for // testing or as a starting point for a more complex service. package samlidp import ( "crypto" "crypto/x509" "net/http" "net/url" "sync" "github.com/zenazn/goji/web" "github.com/crewjam/saml" "github.com/crewjam/saml/logger" ) // Options represent the parameters to New() for creating a new IDP server type Options struct { URL url.URL Key crypto.PrivateKey Logger logger.Interface Certificate *x509.Certificate Store Store } // Server represents an IDP server. The server provides the following URLs: // // /metadata - the SAML metadata // /sso - the SAML endpoint to initiate an authentication flow // /login - prompt for a username and password if no session established // /login/:shortcut - kick off an IDP-initiated authentication flow // /services - RESTful interface to Service objects // /users - RESTful interface to User objects // /sessions - RESTful interface to Session objects // /shortcuts - RESTful interface to Shortcut objects type Server struct { http.Handler idpConfigMu sync.RWMutex // protects calls into the IDP logger logger.Interface serviceProviders map[string]*saml.EntityDescriptor IDP saml.IdentityProvider // the underlying IDP Store Store // the data store } // New returns a new Server func New(opts Options) (*Server, error) { metadataURL := opts.URL metadataURL.Path = metadataURL.Path + "/metadata" ssoURL := opts.URL ssoURL.Path = ssoURL.Path + "/sso" logr := opts.Logger if logr == nil { logr = logger.DefaultLogger } s := &Server{ serviceProviders: map[string]*saml.EntityDescriptor{}, IDP: saml.IdentityProvider{ Key: opts.Key, Logger: logr, Certificate: opts.Certificate, MetadataURL: metadataURL, SSOURL: ssoURL, }, logger: logr, Store: opts.Store, } s.IDP.SessionProvider = s s.IDP.ServiceProviderProvider = s if err := s.initializeServices(); err != nil { return nil, err } s.InitializeHTTP() return s, nil } // InitializeHTTP sets up the HTTP handler for the server. (This function // is called automatically for you by New, but you may need to call it // yourself if you don't create the object using New.) func (s *Server) InitializeHTTP() { mux := web.New() s.Handler = mux mux.Get("/metadata", func(w http.ResponseWriter, r *http.Request) { s.idpConfigMu.RLock() defer s.idpConfigMu.RUnlock() s.IDP.ServeMetadata(w, r) }) mux.Handle("/sso", func(w http.ResponseWriter, r *http.Request) { s.idpConfigMu.RLock() defer s.idpConfigMu.RUnlock() s.IDP.ServeSSO(w, r) }) mux.Handle("/login", s.HandleLogin) mux.Handle("/login/:shortcut", s.HandleIDPInitiated) mux.Handle("/login/:shortcut/*", s.HandleIDPInitiated) mux.Get("/services/", s.HandleListServices) mux.Get("/services/:id", s.HandleGetService) mux.Put("/services/:id", s.HandlePutService) mux.Post("/services/:id", s.HandlePutService) mux.Delete("/services/:id", s.HandleDeleteService) mux.Get("/users/", s.HandleListUsers) mux.Get("/users/:id", s.HandleGetUser) mux.Put("/users/:id", s.HandlePutUser) mux.Delete("/users/:id", s.HandleDeleteUser) mux.Get("/sessions/", s.HandleListSessions) mux.Get("/sessions/:id", s.HandleGetSession) mux.Delete("/sessions/:id", s.HandleDeleteSession) mux.Get("/shortcuts/", s.HandleListShortcuts) mux.Get("/shortcuts/:id", s.HandleGetShortcut) mux.Put("/shortcuts/:id", s.HandlePutShortcut) mux.Delete("/shortcuts/:id", s.HandleDeleteShortcut) } saml-0.4.6/samlidp/samlidp_test.go000066400000000000000000000070511415467341100171360ustar00rootroot00000000000000package samlidp import ( "crypto" "crypto/rsa" "crypto/x509" "encoding/pem" "net/http" "net/http/httptest" "net/url" "strings" "testing" "time" "gotest.tools/assert" is "gotest.tools/assert/cmp" "gotest.tools/golden" "github.com/golang-jwt/jwt/v4" "github.com/crewjam/saml" "github.com/crewjam/saml/logger" ) type testRandomReader struct { Next byte } func (tr *testRandomReader) Read(p []byte) (n int, err error) { for i := 0; i < len(p); i++ { p[i] = tr.Next tr.Next += 2 } return len(p), nil } func mustParseURL(s string) url.URL { rv, err := url.Parse(s) if err != nil { panic(err) } return *rv } func mustParsePrivateKey(pemStr []byte) crypto.PrivateKey { b, _ := pem.Decode(pemStr) if b == nil { panic("cannot parse PEM") } k, err := x509.ParsePKCS1PrivateKey(b.Bytes) if err != nil { panic(err) } return k } func mustParseCertificate(pemStr []byte) *x509.Certificate { b, _ := pem.Decode(pemStr) if b == nil { panic("cannot parse PEM") } cert, err := x509.ParseCertificate(b.Bytes) if err != nil { panic(err) } return cert } type ServerTest struct { SPKey *rsa.PrivateKey SPCertificate *x509.Certificate SP saml.ServiceProvider Key crypto.PrivateKey Certificate *x509.Certificate Server *Server Store MemoryStore } func NewServerTest(t *testing.T) *ServerTest { test := ServerTest{} saml.TimeNow = func() time.Time { rv, _ := time.Parse("Mon Jan 2 15:04:05 MST 2006", "Mon Dec 1 01:57:09 UTC 2015") return rv } jwt.TimeFunc = saml.TimeNow saml.RandReader = &testRandomReader{} test.SPKey = mustParsePrivateKey(golden.Get(t, "sp_key.pem")).(*rsa.PrivateKey) test.SPCertificate = mustParseCertificate(golden.Get(t, "sp_cert.pem")) test.SP = saml.ServiceProvider{ Key: test.SPKey, Certificate: test.SPCertificate, MetadataURL: mustParseURL("https://sp.example.com/saml2/metadata"), AcsURL: mustParseURL("https://sp.example.com/saml2/acs"), IDPMetadata: &saml.EntityDescriptor{}, } test.Key = mustParsePrivateKey(golden.Get(t, "idp_key.pem")).(*rsa.PrivateKey) test.Certificate = mustParseCertificate(golden.Get(t, "idp_cert.pem")) test.Store = MemoryStore{} var err error test.Server, err = New(Options{ Certificate: test.Certificate, Key: test.Key, Logger: logger.DefaultLogger, Store: &test.Store, URL: url.URL{Scheme: "https", Host: "idp.example.com"}, }) if err != nil { panic(err) } test.SP.IDPMetadata = test.Server.IDP.Metadata() test.Server.serviceProviders["https://sp.example.com/saml2/metadata"] = test.SP.Metadata() return &test } func TestHTTPCanHandleMetadataRequest(t *testing.T) { test := NewServerTest(t) w := httptest.NewRecorder() r, _ := http.NewRequest("GET", "https://idp.example.com/metadata", nil) test.Server.ServeHTTP(w, r) assert.Check(t, is.Equal(http.StatusOK, w.Code)) assert.Check(t, strings.HasPrefix(string(w.Body.Bytes()), "

"), string(w.Body.Bytes())) golden.Assert(t, w.Body.String(), "http_sso_response.html") } saml-0.4.6/samlidp/service.go000066400000000000000000000101251415467341100161020ustar00rootroot00000000000000package samlidp import ( "encoding/json" "encoding/xml" "fmt" "net/http" "os" "github.com/zenazn/goji/web" "github.com/crewjam/saml" ) // Service represents a configured SP for whom this IDP provides authentication services. type Service struct { // Name is the name of the service provider Name string // Metdata is the XML metadata of the service provider. Metadata saml.EntityDescriptor } // GetServiceProvider returns the Service Provider metadata for the // service provider ID, which is typically the service provider's // metadata URL. If an appropriate service provider cannot be found then // the returned error must be os.ErrNotExist. func (s *Server) GetServiceProvider(r *http.Request, serviceProviderID string) (*saml.EntityDescriptor, error) { s.idpConfigMu.RLock() defer s.idpConfigMu.RUnlock() rv, ok := s.serviceProviders[serviceProviderID] if !ok { return nil, os.ErrNotExist } return rv, nil } // HandleListServices handles the `GET /services/` request and responds with a JSON formatted list // of service names. func (s *Server) HandleListServices(c web.C, w http.ResponseWriter, r *http.Request) { services, err := s.Store.List("/services/") if err != nil { s.logger.Printf("ERROR: %s", err) http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) return } json.NewEncoder(w).Encode(struct { Services []string `json:"services"` }{Services: services}) } // HandleGetService handles the `GET /services/:id` request and responds with the service // metadata in XML format. func (s *Server) HandleGetService(c web.C, w http.ResponseWriter, r *http.Request) { service := Service{} err := s.Store.Get(fmt.Sprintf("/services/%s", c.URLParams["id"]), &service) if err != nil { s.logger.Printf("ERROR: %s", err) http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) return } xml.NewEncoder(w).Encode(service.Metadata) } // HandlePutService handles the `PUT /shortcuts/:id` request. It accepts the XML-formatted // service metadata in the request body and stores it. func (s *Server) HandlePutService(c web.C, w http.ResponseWriter, r *http.Request) { service := Service{} metadata, err := getSPMetadata(r.Body) if err != nil { s.logger.Printf("ERROR: %s", err) http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest) return } service.Metadata = *metadata err = s.Store.Put(fmt.Sprintf("/services/%s", c.URLParams["id"]), &service) if err != nil { s.logger.Printf("ERROR: %s", err) http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) return } s.idpConfigMu.Lock() s.serviceProviders[service.Metadata.EntityID] = &service.Metadata s.idpConfigMu.Unlock() w.WriteHeader(http.StatusNoContent) } // HandleDeleteService handles the `DELETE /services/:id` request. func (s *Server) HandleDeleteService(c web.C, w http.ResponseWriter, r *http.Request) { service := Service{} err := s.Store.Get(fmt.Sprintf("/services/%s", c.URLParams["id"]), &service) if err != nil { s.logger.Printf("ERROR: %s", err) http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) return } if err := s.Store.Delete(fmt.Sprintf("/services/%s", c.URLParams["id"])); err != nil { s.logger.Printf("ERROR: %s", err) http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) return } s.idpConfigMu.Lock() delete(s.serviceProviders, service.Metadata.EntityID) s.idpConfigMu.Unlock() w.WriteHeader(http.StatusNoContent) } // initializeServices reads all the stored services and initializes the underlying // identity provider to accept them. func (s *Server) initializeServices() error { serviceNames, err := s.Store.List("/services/") if err != nil { return err } for _, serviceName := range serviceNames { service := Service{} if err := s.Store.Get(fmt.Sprintf("/services/%s", serviceName), &service); err != nil { return err } s.idpConfigMu.Lock() s.serviceProviders[service.Metadata.EntityID] = &service.Metadata s.idpConfigMu.Unlock() } return nil } saml-0.4.6/samlidp/service_test.go000066400000000000000000000033671415467341100171530ustar00rootroot00000000000000package samlidp import ( "bytes" "net/http" "net/http/httptest" "testing" "gotest.tools/assert" is "gotest.tools/assert/cmp" "gotest.tools/golden" ) func TestServicesCrud(t *testing.T) { test := NewServerTest(t) w := httptest.NewRecorder() r, _ := http.NewRequest("GET", "https://idp.example.com/services/", nil) test.Server.ServeHTTP(w, r) assert.Check(t, is.Equal(http.StatusOK, w.Code)) assert.Check(t, is.Equal("{\"services\":[]}\n", string(w.Body.Bytes()))) w = httptest.NewRecorder() r, _ = http.NewRequest("PUT", "https://idp.example.com/services/sp", bytes.NewReader(golden.Get(t, "sp_metadata.xml"))) test.Server.ServeHTTP(w, r) assert.Check(t, is.Equal(http.StatusNoContent, w.Code)) w = httptest.NewRecorder() r, _ = http.NewRequest("GET", "https://idp.example.com/services/sp", nil) test.Server.ServeHTTP(w, r) assert.Check(t, is.Equal(http.StatusOK, w.Code)) golden.Assert(t, w.Body.String(), "sp_metadata.xml") w = httptest.NewRecorder() r, _ = http.NewRequest("GET", "https://idp.example.com/services/", nil) test.Server.ServeHTTP(w, r) assert.Check(t, is.Equal(http.StatusOK, w.Code)) assert.Check(t, is.Equal("{\"services\":[\"sp\"]}\n", string(w.Body.Bytes()))) assert.Check(t, is.Len(test.Server.serviceProviders, 2)) w = httptest.NewRecorder() r, _ = http.NewRequest("DELETE", "https://idp.example.com/services/sp", nil) test.Server.ServeHTTP(w, r) assert.Check(t, is.Equal(http.StatusNoContent, w.Code)) w = httptest.NewRecorder() r, _ = http.NewRequest("GET", "https://idp.example.com/services/", nil) test.Server.ServeHTTP(w, r) assert.Check(t, is.Equal(http.StatusOK, w.Code)) assert.Check(t, is.Equal("{\"services\":[]}\n", string(w.Body.Bytes()))) assert.Check(t, is.Len(test.Server.serviceProviders, 1)) } saml-0.4.6/samlidp/session.go000066400000000000000000000143001415467341100161240ustar00rootroot00000000000000package samlidp import ( "encoding/base64" "encoding/hex" "encoding/json" "fmt" "net/http" "text/template" "time" "golang.org/x/crypto/bcrypt" "github.com/zenazn/goji/web" "github.com/crewjam/saml" ) var sessionMaxAge = time.Hour // GetSession returns the *Session for this request. // // If the remote user has specified a username and password in the request // then it is validated against the user database. If valid it sets a // cookie and returns the newly created session object. // // If the remote user has specified invalid credentials then a login form // is returned with an English-language toast telling the user their // password was invalid. // // If a session cookie already exists and represents a valid session, // then the session is returned // // If neither credentials nor a valid session cookie exist, this function // sends a login form and returns nil. func (s *Server) GetSession(w http.ResponseWriter, r *http.Request, req *saml.IdpAuthnRequest) *saml.Session { // if we received login credentials then maybe we can create a session if r.Method == "POST" && r.PostForm.Get("user") != "" { user := User{} if err := s.Store.Get(fmt.Sprintf("/users/%s", r.PostForm.Get("user")), &user); err != nil { s.sendLoginForm(w, r, req, "Invalid username or password") return nil } if err := bcrypt.CompareHashAndPassword(user.HashedPassword, []byte(r.PostForm.Get("password"))); err != nil { s.sendLoginForm(w, r, req, "Invalid username or password") return nil } session := &saml.Session{ ID: base64.StdEncoding.EncodeToString(randomBytes(32)), NameID: user.Email, CreateTime: saml.TimeNow(), ExpireTime: saml.TimeNow().Add(sessionMaxAge), Index: hex.EncodeToString(randomBytes(32)), UserName: user.Name, Groups: user.Groups[:], UserEmail: user.Email, UserCommonName: user.CommonName, UserSurname: user.Surname, UserGivenName: user.GivenName, UserScopedAffiliation: user.ScopedAffiliation, } if err := s.Store.Put(fmt.Sprintf("/sessions/%s", session.ID), &session); err != nil { http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) return nil } http.SetCookie(w, &http.Cookie{ Name: "session", Value: session.ID, MaxAge: int(sessionMaxAge.Seconds()), HttpOnly: true, Secure: r.URL.Scheme == "https", Path: "/", }) return session } if sessionCookie, err := r.Cookie("session"); err == nil { session := &saml.Session{} if err := s.Store.Get(fmt.Sprintf("/sessions/%s", sessionCookie.Value), session); err != nil { if err == ErrNotFound { s.sendLoginForm(w, r, req, "") return nil } http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) return nil } if saml.TimeNow().After(session.ExpireTime) { s.sendLoginForm(w, r, req, "") return nil } return session } s.sendLoginForm(w, r, req, "") return nil } // sendLoginForm produces a form which requests a username and password and directs the user // back to the IDP authorize URL to restart the SAML login flow, this time establishing a // session based on the credentials that were provided. func (s *Server) sendLoginForm(w http.ResponseWriter, r *http.Request, req *saml.IdpAuthnRequest, toast string) { tmpl := template.Must(template.New("saml-post-form").Parse(`` + `` + `

{{.Toast}}

` + `` + `` + `` + `` + `` + `` + `
` + ``)) data := struct { Toast string URL string SAMLRequest string RelayState string }{ Toast: toast, URL: req.IDP.SSOURL.String(), SAMLRequest: base64.StdEncoding.EncodeToString(req.RequestBuffer), RelayState: req.RelayState, } if err := tmpl.Execute(w, data); err != nil { panic(err) } } // HandleLogin handles the `POST /login` and `GET /login` forms. If credentials are present // in the request body, then they are validated. For valid credentials, the response is a // 200 OK and the JSON session object. For invalid credentials, the HTML login prompt form // is sent. func (s *Server) HandleLogin(c web.C, w http.ResponseWriter, r *http.Request) { if err := r.ParseForm(); err != nil { http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest) return } session := s.GetSession(w, r, &saml.IdpAuthnRequest{IDP: &s.IDP}) if session == nil { return } json.NewEncoder(w).Encode(session) } // HandleListSessions handles the `GET /sessions/` request and responds with a JSON formatted list // of session names. func (s *Server) HandleListSessions(c web.C, w http.ResponseWriter, r *http.Request) { sessions, err := s.Store.List("/sessions/") if err != nil { http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) return } json.NewEncoder(w).Encode(struct { Sessions []string `json:"sessions"` }{Sessions: sessions}) } // HandleGetSession handles the `GET /sessions/:id` request and responds with the session // object in JSON format. func (s *Server) HandleGetSession(c web.C, w http.ResponseWriter, r *http.Request) { session := saml.Session{} err := s.Store.Get(fmt.Sprintf("/sessions/%s", c.URLParams["id"]), &session) if err != nil { http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) return } json.NewEncoder(w).Encode(session) } // HandleDeleteSession handles the `DELETE /sessions/:id` request. It invalidates the // specified session. func (s *Server) HandleDeleteSession(c web.C, w http.ResponseWriter, r *http.Request) { err := s.Store.Delete(fmt.Sprintf("/sessions/%s", c.URLParams["id"])) if err != nil { http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) return } w.WriteHeader(http.StatusNoContent) } saml-0.4.6/samlidp/session_test.go000066400000000000000000000067511415467341100171760ustar00rootroot00000000000000package samlidp import ( "net/http" "net/http/httptest" "strings" "testing" "gotest.tools/assert" is "gotest.tools/assert/cmp" ) func TestSessionsCrud(t *testing.T) { test := NewServerTest(t) w := httptest.NewRecorder() r, _ := http.NewRequest("GET", "https://idp.example.com/sessions/", nil) test.Server.ServeHTTP(w, r) assert.Check(t, is.Equal(http.StatusOK, w.Code)) assert.Check(t, is.Equal("{\"sessions\":[]}\n", string(w.Body.Bytes()))) w = httptest.NewRecorder() r, _ = http.NewRequest("PUT", "https://idp.example.com/users/alice", strings.NewReader(`{"name": "alice", "password": "hunter2"}`+"\n")) test.Server.ServeHTTP(w, r) assert.Check(t, is.Equal(http.StatusNoContent, w.Code)) w = httptest.NewRecorder() r, _ = http.NewRequest("POST", "https://idp.example.com/login", strings.NewReader("user=alice&password=hunter2")) r.Header.Set("Content-type", "application/x-www-form-urlencoded") test.Server.ServeHTTP(w, r) assert.Check(t, is.Equal(http.StatusOK, w.Code)) assert.Check(t, is.Equal("session=AAIEBggKDA4QEhQWGBocHiAiJCYoKiwuMDI0Njg6PD4=; Path=/; Max-Age=3600; HttpOnly; Secure", w.Header().Get("Set-Cookie"))) assert.Check(t, is.Equal("{\"ID\":\"AAIEBggKDA4QEhQWGBocHiAiJCYoKiwuMDI0Njg6PD4=\",\"CreateTime\":\"2015-12-01T01:57:09Z\",\"ExpireTime\":\"2015-12-01T02:57:09Z\",\"Index\":\"40424446484a4c4e50525456585a5c5e60626466686a6c6e70727476787a7c7e\",\"NameID\":\"\",\"Groups\":null,\"UserName\":\"alice\",\"UserEmail\":\"\",\"UserCommonName\":\"\",\"UserSurname\":\"\",\"UserGivenName\":\"\",\"UserScopedAffiliation\":\"\",\"CustomAttributes\":null}\n", string(w.Body.Bytes()))) w = httptest.NewRecorder() r, _ = http.NewRequest("GET", "https://idp.example.com/login", nil) r.Header.Set("Cookie", "session=AAIEBggKDA4QEhQWGBocHiAiJCYoKiwuMDI0Njg6PD4=") test.Server.ServeHTTP(w, r) assert.Check(t, is.Equal(http.StatusOK, w.Code)) assert.Check(t, is.Equal("{\"ID\":\"AAIEBggKDA4QEhQWGBocHiAiJCYoKiwuMDI0Njg6PD4=\",\"CreateTime\":\"2015-12-01T01:57:09Z\",\"ExpireTime\":\"2015-12-01T02:57:09Z\",\"Index\":\"40424446484a4c4e50525456585a5c5e60626466686a6c6e70727476787a7c7e\",\"NameID\":\"\",\"Groups\":null,\"UserName\":\"alice\",\"UserEmail\":\"\",\"UserCommonName\":\"\",\"UserSurname\":\"\",\"UserGivenName\":\"\",\"UserScopedAffiliation\":\"\",\"CustomAttributes\":null}\n", string(w.Body.Bytes()))) w = httptest.NewRecorder() r, _ = http.NewRequest("GET", "https://idp.example.com/sessions/AAIEBggKDA4QEhQWGBocHiAiJCYoKiwuMDI0Njg6PD4=", nil) test.Server.ServeHTTP(w, r) assert.Check(t, is.Equal(http.StatusOK, w.Code)) assert.Check(t, is.Equal("{\"ID\":\"AAIEBggKDA4QEhQWGBocHiAiJCYoKiwuMDI0Njg6PD4=\",\"CreateTime\":\"2015-12-01T01:57:09Z\",\"ExpireTime\":\"2015-12-01T02:57:09Z\",\"Index\":\"40424446484a4c4e50525456585a5c5e60626466686a6c6e70727476787a7c7e\",\"NameID\":\"\",\"Groups\":null,\"UserName\":\"alice\",\"UserEmail\":\"\",\"UserCommonName\":\"\",\"UserSurname\":\"\",\"UserGivenName\":\"\",\"UserScopedAffiliation\":\"\",\"CustomAttributes\":null}\n", string(w.Body.Bytes()))) w = httptest.NewRecorder() r, _ = http.NewRequest("DELETE", "https://idp.example.com/sessions/AAIEBggKDA4QEhQWGBocHiAiJCYoKiwuMDI0Njg6PD4=", nil) test.Server.ServeHTTP(w, r) assert.Check(t, is.Equal(http.StatusNoContent, w.Code)) w = httptest.NewRecorder() r, _ = http.NewRequest("GET", "https://idp.example.com/sessions/", nil) test.Server.ServeHTTP(w, r) assert.Check(t, is.Equal(http.StatusOK, w.Code)) assert.Check(t, is.Equal("{\"sessions\":[]}\n", string(w.Body.Bytes()))) } saml-0.4.6/samlidp/shortcut.go000066400000000000000000000075521415467341100163270ustar00rootroot00000000000000package samlidp import ( "encoding/json" "fmt" "net/http" "github.com/zenazn/goji/web" ) // Shortcut represents an IDP-initiated SAML flow. When a user // navigates to /login/:shortcut it initiates the login flow // to the specified service provider with the specified // RelayState. type Shortcut struct { // The name of the shortcut. Name string `json:"name"` // The entity ID of the service provider to use for this shortcut, i.e. // https://someapp.example.com/saml/metadata. ServiceProviderID string `json:"service_provider"` // If specified then the relay state is the fixed string provided RelayState *string `json:"relay_state,omitempty"` // If true then the URL suffix is used as the relayState. So for example, a user // requesting https://idp.example.com/login/myservice/foo will get redirected // to the myservice endpoint with a RelayState of "foo". URISuffixAsRelayState bool `json:"url_suffix_as_relay_state,omitempty"` } // HandleListShortcuts handles the `GET /shortcuts/` request and responds with a JSON formatted list // of shortcut names. func (s *Server) HandleListShortcuts(c web.C, w http.ResponseWriter, r *http.Request) { shortcuts, err := s.Store.List("/shortcuts/") if err != nil { http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) return } json.NewEncoder(w).Encode(struct { Shortcuts []string `json:"shortcuts"` }{Shortcuts: shortcuts}) } // HandleGetShortcut handles the `GET /shortcuts/:id` request and responds with the shortcut // object in JSON format. func (s *Server) HandleGetShortcut(c web.C, w http.ResponseWriter, r *http.Request) { shortcut := Shortcut{} err := s.Store.Get(fmt.Sprintf("/shortcuts/%s", c.URLParams["id"]), &shortcut) if err != nil { http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) return } json.NewEncoder(w).Encode(shortcut) } // HandlePutShortcut handles the `PUT /shortcuts/:id` request. It accepts a JSON formatted // shortcut object in the request body and stores it. func (s *Server) HandlePutShortcut(c web.C, w http.ResponseWriter, r *http.Request) { shortcut := Shortcut{} if err := json.NewDecoder(r.Body).Decode(&shortcut); err != nil { http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest) return } shortcut.Name = c.URLParams["id"] err := s.Store.Put(fmt.Sprintf("/shortcuts/%s", c.URLParams["id"]), &shortcut) if err != nil { http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) return } w.WriteHeader(http.StatusNoContent) } // HandleDeleteShortcut handles the `DELETE /shortcuts/:id` request. func (s *Server) HandleDeleteShortcut(c web.C, w http.ResponseWriter, r *http.Request) { err := s.Store.Delete(fmt.Sprintf("/shortcuts/%s", c.URLParams["id"])) if err != nil { http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) return } w.WriteHeader(http.StatusNoContent) } // HandleIDPInitiated handles a request for an IDP initiated login flow. It looks up // the specified shortcut, generates the appropriate SAML assertion and redirects the // user via the HTTP-POST binding to the service providers ACS URL. func (s *Server) HandleIDPInitiated(c web.C, w http.ResponseWriter, r *http.Request) { shortcutName := c.URLParams["shortcut"] shortcut := Shortcut{} if err := s.Store.Get(fmt.Sprintf("/shortcuts/%s", shortcutName), &shortcut); err != nil { s.logger.Printf("ERROR: %s", err) http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) return } relayState := "" switch { case shortcut.RelayState != nil: relayState = *shortcut.RelayState case shortcut.URISuffixAsRelayState: relayState, _ = c.URLParams["*"] } s.idpConfigMu.RLock() defer s.idpConfigMu.RUnlock() s.IDP.ServeIDPInitiated(w, r, shortcut.ServiceProviderID, relayState) } saml-0.4.6/samlidp/shortcut_test.go000066400000000000000000000063721415467341100173650ustar00rootroot00000000000000package samlidp import ( "net/http" "net/http/httptest" "strings" "testing" "gotest.tools/assert" is "gotest.tools/assert/cmp" ) func TestShortcutsCrud(t *testing.T) { test := NewServerTest(t) w := httptest.NewRecorder() r, _ := http.NewRequest("GET", "https://idp.example.com/shortcuts/", nil) test.Server.ServeHTTP(w, r) assert.Check(t, is.Equal(http.StatusOK, w.Code)) assert.Check(t, is.Equal("{\"shortcuts\":[]}\n", string(w.Body.Bytes()))) w = httptest.NewRecorder() r, _ = http.NewRequest("PUT", "https://idp.example.com/shortcuts/bob", strings.NewReader("{\"url_suffix_as_relay_state\": true, \"service_provider\": \"https://example.com/saml2/metadata\"}")) test.Server.ServeHTTP(w, r) assert.Check(t, is.Equal(http.StatusNoContent, w.Code)) w = httptest.NewRecorder() r, _ = http.NewRequest("GET", "https://idp.example.com/shortcuts/bob", nil) test.Server.ServeHTTP(w, r) assert.Check(t, is.Equal(http.StatusOK, w.Code)) assert.Check(t, is.Equal("{\"name\":\"bob\",\"service_provider\":\"https://example.com/saml2/metadata\",\"url_suffix_as_relay_state\":true}\n", string(w.Body.Bytes()))) w = httptest.NewRecorder() r, _ = http.NewRequest("GET", "https://idp.example.com/shortcuts/", nil) test.Server.ServeHTTP(w, r) assert.Check(t, is.Equal(http.StatusOK, w.Code)) assert.Check(t, is.Equal("{\"shortcuts\":[\"bob\"]}\n", string(w.Body.Bytes()))) w = httptest.NewRecorder() r, _ = http.NewRequest("DELETE", "https://idp.example.com/shortcuts/bob", nil) test.Server.ServeHTTP(w, r) assert.Check(t, is.Equal(http.StatusNoContent, w.Code)) w = httptest.NewRecorder() r, _ = http.NewRequest("GET", "https://idp.example.com/shortcuts/", nil) test.Server.ServeHTTP(w, r) assert.Check(t, is.Equal(http.StatusOK, w.Code)) assert.Check(t, is.Equal("{\"shortcuts\":[]}\n", string(w.Body.Bytes()))) } func TestShortcut(t *testing.T) { test := NewServerTest(t) w := httptest.NewRecorder() r, _ := http.NewRequest("PUT", "https://idp.example.com/shortcuts/bob", strings.NewReader("{\"url_suffix_as_relay_state\": true, \"service_provider\": \"https://sp.example.com/saml2/metadata\"}")) test.Server.ServeHTTP(w, r) assert.Check(t, is.Equal(http.StatusNoContent, w.Code)) w = httptest.NewRecorder() r, _ = http.NewRequest("PUT", "https://idp.example.com/users/alice", strings.NewReader(`{"name": "alice", "password": "hunter2"}`+"\n")) test.Server.ServeHTTP(w, r) assert.Check(t, is.Equal(http.StatusNoContent, w.Code)) w = httptest.NewRecorder() r, _ = http.NewRequest("POST", "https://idp.example.com/login", strings.NewReader("user=alice&password=hunter2")) r.Header.Set("Content-type", "application/x-www-form-urlencoded") test.Server.ServeHTTP(w, r) assert.Check(t, is.Equal(http.StatusOK, w.Code)) w = httptest.NewRecorder() r, _ = http.NewRequest("GET", "https://idp.example.com/login/bob/whoami", nil) r.Header.Set("Cookie", "session=AAIEBggKDA4QEhQWGBocHiAiJCYoKiwuMDI0Njg6PD4=") test.Server.ServeHTTP(w, r) assert.Check(t, is.Equal(http.StatusOK, w.Code)) body := string(w.Body.Bytes()) assert.Check(t, strings.Contains(body, ""), body) assert.Check(t, strings.Contains(body, ""), body) } saml-0.4.6/samlidp/store.go000066400000000000000000000013471415467341100156040ustar00rootroot00000000000000package samlidp import "errors" // ErrNotFound is returned from Store.Get() when a stored item is not present var ErrNotFound = errors.New("not found") // Store is an interface that describes an abstract key-value store. type Store interface { // Get fetches the data stored in `key` and unmarshals it into `value`. Get(key string, value interface{}) error // Put marshals `value` and stores it in `key`. Put(key string, value interface{}) error // Delete removes `key` Delete(key string) error // List returns all the keys that start with `prefix`. The prefix is // stripped from each returned value. So if keys are ["aa", "ab", "cd"] // then List("a") would produce []string{"a", "b"} List(prefix string) ([]string, error) } saml-0.4.6/samlidp/testdata/000077500000000000000000000000001415467341100157255ustar00rootroot00000000000000saml-0.4.6/samlidp/testdata/http_metadata_response.html000066400000000000000000000057651415467341100233650ustar00rootroot00000000000000 MIIB7zCCAVgCCQDFzbKIp7b3MTANBgkqhkiG9w0BAQUFADA8MQswCQYDVQQGEwJVUzELMAkGA1UECAwCR0ExDDAKBgNVBAoMA2ZvbzESMBAGA1UEAwwJbG9jYWxob3N0MB4XDTEzMTAwMjAwMDg1MVoXDTE0MTAwMjAwMDg1MVowPDELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkdBMQwwCgYDVQQKDANmb28xEjAQBgNVBAMMCWxvY2FsaG9zdDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA1PMHYmhZj308kWLhZVT4vOulqx/9ibm5B86fPWwUKKQ2i12MYtz07tzukPymisTDhQaqyJ8Kqb/6JjhmeMnEOdTvSPmHO8m1ZVveJU6NoKRn/mP/BD7FW52WhbrUXLSeHVSKfWkNk6S4hk9MV9TswTvyRIKvRsw0X/gfnqkroJcCAwEAATANBgkqhkiG9w0BAQUFAAOBgQCMMlIO+GNcGekevKgkakpMdAqJfs24maGb90DvTLbRZRD7Xvn1MnVBBS9hzlXiFLYOInXACMW5gcoRFfeTQLSouMM8o57h0uKjfTmuoWHLQLi6hnF+cvCsEFiJZ4AbF+DgmO6TarJ8O05t8zvnOwJlNCASPZRH/JmF8tX0hoHuAQ== MIIB7zCCAVgCCQDFzbKIp7b3MTANBgkqhkiG9w0BAQUFADA8MQswCQYDVQQGEwJVUzELMAkGA1UECAwCR0ExDDAKBgNVBAoMA2ZvbzESMBAGA1UEAwwJbG9jYWxob3N0MB4XDTEzMTAwMjAwMDg1MVoXDTE0MTAwMjAwMDg1MVowPDELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkdBMQwwCgYDVQQKDANmb28xEjAQBgNVBAMMCWxvY2FsaG9zdDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA1PMHYmhZj308kWLhZVT4vOulqx/9ibm5B86fPWwUKKQ2i12MYtz07tzukPymisTDhQaqyJ8Kqb/6JjhmeMnEOdTvSPmHO8m1ZVveJU6NoKRn/mP/BD7FW52WhbrUXLSeHVSKfWkNk6S4hk9MV9TswTvyRIKvRsw0X/gfnqkroJcCAwEAATANBgkqhkiG9w0BAQUFAAOBgQCMMlIO+GNcGekevKgkakpMdAqJfs24maGb90DvTLbRZRD7Xvn1MnVBBS9hzlXiFLYOInXACMW5gcoRFfeTQLSouMM8o57h0uKjfTmuoWHLQLi6hnF+cvCsEFiJZ4AbF+DgmO6TarJ8O05t8zvnOwJlNCASPZRH/JmF8tX0hoHuAQ== urn:oasis:names:tc:SAML:2.0:nameid-format:transient saml-0.4.6/samlidp/testdata/http_sso_response.html000066400000000000000000000022711415467341100223760ustar00rootroot00000000000000

saml-0.4.6/samlidp/testdata/idp_cert.pem000066400000000000000000000013351415467341100202230ustar00rootroot00000000000000-----BEGIN CERTIFICATE----- MIIB7zCCAVgCCQDFzbKIp7b3MTANBgkqhkiG9w0BAQUFADA8MQswCQYDVQQGEwJV UzELMAkGA1UECAwCR0ExDDAKBgNVBAoMA2ZvbzESMBAGA1UEAwwJbG9jYWxob3N0 MB4XDTEzMTAwMjAwMDg1MVoXDTE0MTAwMjAwMDg1MVowPDELMAkGA1UEBhMCVVMx CzAJBgNVBAgMAkdBMQwwCgYDVQQKDANmb28xEjAQBgNVBAMMCWxvY2FsaG9zdDCB nzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA1PMHYmhZj308kWLhZVT4vOulqx/9 ibm5B86fPWwUKKQ2i12MYtz07tzukPymisTDhQaqyJ8Kqb/6JjhmeMnEOdTvSPmH O8m1ZVveJU6NoKRn/mP/BD7FW52WhbrUXLSeHVSKfWkNk6S4hk9MV9TswTvyRIKv Rsw0X/gfnqkroJcCAwEAATANBgkqhkiG9w0BAQUFAAOBgQCMMlIO+GNcGekevKgk akpMdAqJfs24maGb90DvTLbRZRD7Xvn1MnVBBS9hzlXiFLYOInXACMW5gcoRFfeT QLSouMM8o57h0uKjfTmuoWHLQLi6hnF+cvCsEFiJZ4AbF+DgmO6TarJ8O05t8zvn OwJlNCASPZRH/JmF8tX0hoHuAQ== -----END CERTIFICATE----- saml-0.4.6/samlidp/testdata/idp_key.pem000066400000000000000000000015731415467341100200620ustar00rootroot00000000000000-----BEGIN RSA PRIVATE KEY----- MIICXgIBAAKBgQDU8wdiaFmPfTyRYuFlVPi866WrH/2JubkHzp89bBQopDaLXYxi 3PTu3O6Q/KaKxMOFBqrInwqpv/omOGZ4ycQ51O9I+Yc7ybVlW94lTo2gpGf+Y/8E PsVbnZaFutRctJ4dVIp9aQ2TpLiGT0xX1OzBO/JEgq9GzDRf+B+eqSuglwIDAQAB AoGBAMuy1eN6cgFiCOgBsB3gVDdTKpww87Qk5ivjqEt28SmXO13A1KNVPS6oQ8SJ CT5Azc6X/BIAoJCURVL+LHdqebogKljhH/3yIel1kH19vr4E2kTM/tYH+qj8afUS JEmArUzsmmK8ccuNqBcllqdwCZjxL4CHDUmyRudFcHVX9oyhAkEA/OV1OkjM3CLU N3sqELdMmHq5QZCUihBmk3/N5OvGdqAFGBlEeewlepEVxkh7JnaNXAXrKHRVu/f/ fbCQxH+qrwJBANeQERF97b9Sibp9xgolb749UWNlAdqmEpmlvmS202TdcaaT1msU 4rRLiQN3X9O9mq4LZMSVethrQAdX1whawpkCQQDk1yGf7xZpMJ8F4U5sN+F4rLyM Rq8Sy8p2OBTwzCUXXK+fYeXjybsUUMr6VMYTRP2fQr/LKJIX+E5ZxvcIyFmDAkEA yfjNVUNVaIbQTzEbRlRvT6MqR+PTCefC072NF9aJWR93JimspGZMR7viY6IM4lrr vBkm0F5yXKaYtoiiDMzlOQJADqmEwXl0D72ZG/2KDg8b4QZEmC9i5gidpQwJXUc6 hU+IVQoLxRq0fBib/36K9tcrrO5Ba4iEvDcNY+D8yGbUtA== -----END RSA PRIVATE KEY----- saml-0.4.6/samlidp/testdata/sp_cert.pem000066400000000000000000000013341415467341100200700ustar00rootroot00000000000000-----BEGIN CERTIFICATE----- MIIB7zCCAVgCCQDFzbKIp7b3MTANBgkqhkiG9w0BAQUFADA8MQswCQYDVQQGEwJV UzELMAkGA1UECAwCR0ExDDAKBgNVBAoMA2ZvbzESMBAGA1UEAwwJbG9jYWxob3N0 MB4XDTEzMTAwMjAwMDg1MVoXDTE0MTAwMjAwMDg1MVowPDELMAkGA1UEBhMCVVMx CzAJBgNVBAgMAkdBMQwwCgYDVQQKDANmb28xEjAQBgNVBAMMCWxvY2FsaG9zdDCB nzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA1PMHYmhZj308kWLhZVT4vOulqx/9 ibm5B86fPWwUKKQ2i12MYtz07tzukPymisTDhQaqyJ8Kqb/6JjhmeMnEOdTvSPmH O8m1ZVveJU6NoKRn/mP/BD7FW52WhbrUXLSeHVSKfWkNk6S4hk9MV9TswTvyRIKv Rsw0X/gfnqkroJcCAwEAATANBgkqhkiG9w0BAQUFAAOBgQCMMlIO+GNcGekevKgk akpMdAqJfs24maGb90DvTLbRZRD7Xvn1MnVBBS9hzlXiFLYOInXACMW5gcoRFfeT QLSouMM8o57h0uKjfTmuoWHLQLi6hnF+cvCsEFiJZ4AbF+DgmO6TarJ8O05t8zvn OwJlNCASPZRH/JmF8tX0hoHuAQ== -----END CERTIFICATE-----saml-0.4.6/samlidp/testdata/sp_key.pem000066400000000000000000000015721415467341100177270ustar00rootroot00000000000000-----BEGIN RSA PRIVATE KEY----- MIICXgIBAAKBgQDU8wdiaFmPfTyRYuFlVPi866WrH/2JubkHzp89bBQopDaLXYxi 3PTu3O6Q/KaKxMOFBqrInwqpv/omOGZ4ycQ51O9I+Yc7ybVlW94lTo2gpGf+Y/8E PsVbnZaFutRctJ4dVIp9aQ2TpLiGT0xX1OzBO/JEgq9GzDRf+B+eqSuglwIDAQAB AoGBAMuy1eN6cgFiCOgBsB3gVDdTKpww87Qk5ivjqEt28SmXO13A1KNVPS6oQ8SJ CT5Azc6X/BIAoJCURVL+LHdqebogKljhH/3yIel1kH19vr4E2kTM/tYH+qj8afUS JEmArUzsmmK8ccuNqBcllqdwCZjxL4CHDUmyRudFcHVX9oyhAkEA/OV1OkjM3CLU N3sqELdMmHq5QZCUihBmk3/N5OvGdqAFGBlEeewlepEVxkh7JnaNXAXrKHRVu/f/ fbCQxH+qrwJBANeQERF97b9Sibp9xgolb749UWNlAdqmEpmlvmS202TdcaaT1msU 4rRLiQN3X9O9mq4LZMSVethrQAdX1whawpkCQQDk1yGf7xZpMJ8F4U5sN+F4rLyM Rq8Sy8p2OBTwzCUXXK+fYeXjybsUUMr6VMYTRP2fQr/LKJIX+E5ZxvcIyFmDAkEA yfjNVUNVaIbQTzEbRlRvT6MqR+PTCefC072NF9aJWR93JimspGZMR7viY6IM4lrr vBkm0F5yXKaYtoiiDMzlOQJADqmEwXl0D72ZG/2KDg8b4QZEmC9i5gidpQwJXUc6 hU+IVQoLxRq0fBib/36K9tcrrO5Ba4iEvDcNY+D8yGbUtA== -----END RSA PRIVATE KEY-----saml-0.4.6/samlidp/testdata/sp_metadata.xml000066400000000000000000000053241415467341100207350ustar00rootroot00000000000000MIIB7zCCAVgCCQDFzbKIp7b3MTANBgkqhkiG9w0BAQUFADA8MQswCQYDVQQGEwJVUzELMAkGA1UECAwCR0ExDDAKBgNVBAoMA2ZvbzESMBAGA1UEAwwJbG9jYWxob3N0MB4XDTEzMTAwMjAwMDg1MVoXDTE0MTAwMjAwMDg1MVowPDELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkdBMQwwCgYDVQQKDANmb28xEjAQBgNVBAMMCWxvY2FsaG9zdDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA1PMHYmhZj308kWLhZVT4vOulqx/9ibm5B86fPWwUKKQ2i12MYtz07tzukPymisTDhQaqyJ8Kqb/6JjhmeMnEOdTvSPmHO8m1ZVveJU6NoKRn/mP/BD7FW52WhbrUXLSeHVSKfWkNk6S4hk9MV9TswTvyRIKvRsw0X/gfnqkroJcCAwEAATANBgkqhkiG9w0BAQUFAAOBgQCMMlIO+GNcGekevKgkakpMdAqJfs24maGb90DvTLbRZRD7Xvn1MnVBBS9hzlXiFLYOInXACMW5gcoRFfeTQLSouMM8o57h0uKjfTmuoWHLQLi6hnF+cvCsEFiJZ4AbF+DgmO6TarJ8O05t8zvnOwJlNCASPZRH/JmF8tX0hoHuAQ==MIIB7zCCAVgCCQDFzbKIp7b3MTANBgkqhkiG9w0BAQUFADA8MQswCQYDVQQGEwJVUzELMAkGA1UECAwCR0ExDDAKBgNVBAoMA2ZvbzESMBAGA1UEAwwJbG9jYWxob3N0MB4XDTEzMTAwMjAwMDg1MVoXDTE0MTAwMjAwMDg1MVowPDELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkdBMQwwCgYDVQQKDANmb28xEjAQBgNVBAMMCWxvY2FsaG9zdDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA1PMHYmhZj308kWLhZVT4vOulqx/9ibm5B86fPWwUKKQ2i12MYtz07tzukPymisTDhQaqyJ8Kqb/6JjhmeMnEOdTvSPmHO8m1ZVveJU6NoKRn/mP/BD7FW52WhbrUXLSeHVSKfWkNk6S4hk9MV9TswTvyRIKvRsw0X/gfnqkroJcCAwEAATANBgkqhkiG9w0BAQUFAAOBgQCMMlIO+GNcGekevKgkakpMdAqJfs24maGb90DvTLbRZRD7Xvn1MnVBBS9hzlXiFLYOInXACMW5gcoRFfeTQLSouMM8o57h0uKjfTmuoWHLQLi6hnF+cvCsEFiJZ4AbF+DgmO6TarJ8O05t8zvnOwJlNCASPZRH/JmF8tX0hoHuAQ==saml-0.4.6/samlidp/user.go000066400000000000000000000074661415467341100154360ustar00rootroot00000000000000package samlidp import ( "encoding/json" "fmt" "net/http" "github.com/zenazn/goji/web" "golang.org/x/crypto/bcrypt" ) // User represents a stored user. The data here are used to // populate user once the user has authenticated. type User struct { Name string `json:"name"` PlaintextPassword *string `json:"password,omitempty"` // not stored HashedPassword []byte `json:"hashed_password,omitempty"` Groups []string `json:"groups,omitempty"` Email string `json:"email,omitempty"` CommonName string `json:"common_name,omitempty"` Surname string `json:"surname,omitempty"` GivenName string `json:"given_name,omitempty"` ScopedAffiliation string `json:"scoped_affiliation,omitempty"` } // HandleListUsers handles the `GET /users/` request and responds with a JSON formatted list // of user names. func (s *Server) HandleListUsers(c web.C, w http.ResponseWriter, r *http.Request) { users, err := s.Store.List("/users/") if err != nil { s.logger.Printf("ERROR: %s", err) http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) return } json.NewEncoder(w).Encode(struct { Users []string `json:"users"` }{Users: users}) } // HandleGetUser handles the `GET /users/:id` request and responds with the user object in JSON // format. The HashedPassword field is excluded. func (s *Server) HandleGetUser(c web.C, w http.ResponseWriter, r *http.Request) { user := User{} err := s.Store.Get(fmt.Sprintf("/users/%s", c.URLParams["id"]), &user) if err != nil { s.logger.Printf("ERROR: %s", err) http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) return } user.HashedPassword = nil json.NewEncoder(w).Encode(user) } // HandlePutUser handles the `PUT /users/:id` request. It accepts a JSON formatted user object in // the request body and stores it. If the PlaintextPassword field is present then it is hashed // and stored in HashedPassword. If the PlaintextPassword field is not present then // HashedPassword retains it's stored value. func (s *Server) HandlePutUser(c web.C, w http.ResponseWriter, r *http.Request) { user := User{} if err := json.NewDecoder(r.Body).Decode(&user); err != nil { s.logger.Printf("ERROR: %s", err) http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest) return } user.Name = c.URLParams["id"] if user.PlaintextPassword != nil { var err error user.HashedPassword, err = bcrypt.GenerateFromPassword([]byte(*user.PlaintextPassword), bcrypt.DefaultCost) if err != nil { s.logger.Printf("ERROR: %s", err) http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) return } } else { existingUser := User{} err := s.Store.Get(fmt.Sprintf("/users/%s", c.URLParams["id"]), &existingUser) switch { case err == nil: user.HashedPassword = existingUser.HashedPassword case err == ErrNotFound: // nop default: s.logger.Printf("ERROR: %s", err) http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) return } } user.PlaintextPassword = nil err := s.Store.Put(fmt.Sprintf("/users/%s", c.URLParams["id"]), &user) if err != nil { s.logger.Printf("ERROR: %s", err) http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) return } w.WriteHeader(http.StatusNoContent) } // HandleDeleteUser handles the `DELETE /users/:id` request. func (s *Server) HandleDeleteUser(c web.C, w http.ResponseWriter, r *http.Request) { err := s.Store.Delete(fmt.Sprintf("/users/%s", c.URLParams["id"])) if err != nil { s.logger.Printf("ERROR: %s", err) http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) return } w.WriteHeader(http.StatusNoContent) } saml-0.4.6/samlidp/user_test.go000066400000000000000000000032021415467341100164550ustar00rootroot00000000000000package samlidp import ( "net/http" "net/http/httptest" "strings" "testing" "gotest.tools/assert" is "gotest.tools/assert/cmp" ) func TestUsersCrud(t *testing.T) { test := NewServerTest(t) w := httptest.NewRecorder() r, _ := http.NewRequest("GET", "https://idp.example.com/users/", nil) test.Server.ServeHTTP(w, r) assert.Check(t, is.Equal(http.StatusOK, w.Code)) assert.Check(t, is.Equal("{\"users\":[]}\n", string(w.Body.Bytes()))) w = httptest.NewRecorder() r, _ = http.NewRequest("PUT", "https://idp.example.com/users/alice", strings.NewReader(`{"name": "alice", "password": "hunter2"}`+"\n")) test.Server.ServeHTTP(w, r) assert.Check(t, is.Equal(http.StatusNoContent, w.Code)) w = httptest.NewRecorder() r, _ = http.NewRequest("GET", "https://idp.example.com/users/alice", nil) test.Server.ServeHTTP(w, r) assert.Check(t, is.Equal(http.StatusOK, w.Code)) assert.Check(t, is.Equal("{\"name\":\"alice\"}\n", string(w.Body.Bytes()))) w = httptest.NewRecorder() r, _ = http.NewRequest("GET", "https://idp.example.com/users/", nil) test.Server.ServeHTTP(w, r) assert.Check(t, is.Equal(http.StatusOK, w.Code)) assert.Check(t, is.Equal("{\"users\":[\"alice\"]}\n", string(w.Body.Bytes()))) w = httptest.NewRecorder() r, _ = http.NewRequest("DELETE", "https://idp.example.com/users/alice", nil) test.Server.ServeHTTP(w, r) assert.Check(t, is.Equal(http.StatusNoContent, w.Code)) w = httptest.NewRecorder() r, _ = http.NewRequest("GET", "https://idp.example.com/users/", nil) test.Server.ServeHTTP(w, r) assert.Check(t, is.Equal(http.StatusOK, w.Code)) assert.Check(t, is.Equal("{\"users\":[]}\n", string(w.Body.Bytes()))) } saml-0.4.6/samlidp/util.go000066400000000000000000000022271415467341100154230ustar00rootroot00000000000000package samlidp import ( "bytes" "encoding/xml" "errors" "io" "io/ioutil" xrv "github.com/mattermost/xml-roundtrip-validator" "github.com/crewjam/saml" ) func randomBytes(n int) []byte { rv := make([]byte, n) if _, err := saml.RandReader.Read(rv); err != nil { panic(err) } return rv } func getSPMetadata(r io.Reader) (spMetadata *saml.EntityDescriptor, err error) { var data []byte if data, err = ioutil.ReadAll(r); err != nil { return nil, err } spMetadata = &saml.EntityDescriptor{} if err := xrv.Validate(bytes.NewBuffer(data)); err != nil { return nil, err } if err := xml.Unmarshal(data, &spMetadata); err != nil { if err.Error() == "expected element type but have " { entities := &saml.EntitiesDescriptor{} if err := xml.Unmarshal(data, &entities); err != nil { return nil, err } for _, e := range entities.EntityDescriptors { if len(e.SPSSODescriptors) > 0 { return &e, nil } } // there were no SPSSODescriptors in the response return nil, errors.New("metadata contained no service provider metadata") } return nil, err } return spMetadata, nil } saml-0.4.6/samlidp/util_go116_test.go000066400000000000000000000026121415467341100173750ustar00rootroot00000000000000//go:build !go1.17 // +build !go1.17 package samlidp import ( "strings" "testing" "gotest.tools/assert" is "gotest.tools/assert/cmp" ) func TestGetSPMetadata(t *testing.T) { good := "" + "\n" + "" _, err := getSPMetadata(strings.NewReader(good)) assert.Check(t, err) bad := "" + "\n" + "" _, err = getSPMetadata(strings.NewReader(bad)) assert.Check(t, is.Error(err, "validator: in token starting at 1:1: roundtrip error: expected {{ EntityDescriptor} [{{ xmlns} urn:oasis:names:tc:SAML:2.0:metadata} {{ :attr} foo} {{ validUntil} 2013-03-10T00:32:19.104Z} {{ cacheDuration} PT1H} {{ entityID} http://localhost:5000/e087a985171710fb9fb30f30f41384f9/saml2/metadata/}]}, observed {{ EntityDescriptor} [{{ xmlns} urn:oasis:names:tc:SAML:2.0:metadata} {{ attr} foo} {{ validUntil} 2013-03-10T00:32:19.104Z} {{ cacheDuration} PT1H} {{ entityID} http://localhost:5000/e087a985171710fb9fb30f30f41384f9/saml2/metadata/}]}")) } saml-0.4.6/samlidp/util_go117_test.go000066400000000000000000000016671415467341100174070ustar00rootroot00000000000000//go:build go1.17 // +build go1.17 package samlidp import ( "strings" "testing" "gotest.tools/assert" is "gotest.tools/assert/cmp" ) func TestGetSPMetadata(t *testing.T) { good := "" + "\n" + "" _, err := getSPMetadata(strings.NewReader(good)) assert.Check(t, err) bad := "" + "]]>\n" + "" _, err = getSPMetadata(strings.NewReader(bad)) assert.Check(t, is.Error(err, "XML syntax error on line 1: unescaped ]]> not in CDATA section")) } saml-0.4.6/samlsp/000077500000000000000000000000001415467341100137625ustar00rootroot00000000000000saml-0.4.6/samlsp/error.go000066400000000000000000000014371415467341100154470ustar00rootroot00000000000000package samlsp import ( "log" "net/http" "github.com/crewjam/saml" ) // ErrorFunction is a callback that is invoked to return an error to the // web user. type ErrorFunction func(w http.ResponseWriter, r *http.Request, err error) // DefaultOnError is the default ErrorFunction implementation. It prints // an message via the standard log package and returns a simple text // "Forbidden" message to the user. func DefaultOnError(w http.ResponseWriter, r *http.Request, err error) { if parseErr, ok := err.(*saml.InvalidResponseError); ok { log.Printf("WARNING: received invalid saml response: %s (now: %s) %s", parseErr.Response, parseErr.Now, parseErr.PrivateErr) } else { log.Printf("ERROR: %s", err) } http.Error(w, http.StatusText(http.StatusForbidden), http.StatusForbidden) } saml-0.4.6/samlsp/fetch_metadata.go000066400000000000000000000034501415467341100172440ustar00rootroot00000000000000package samlsp import ( "bytes" "context" "encoding/xml" "errors" "io/ioutil" "net/http" "net/url" "github.com/crewjam/httperr" xrv "github.com/mattermost/xml-roundtrip-validator" "github.com/crewjam/saml" ) // ParseMetadata parses arbitrary SAML IDP metadata. // // Note: this is needed because IDP metadata is sometimes wrapped in // an , and sometimes the top level element is an // . func ParseMetadata(data []byte) (*saml.EntityDescriptor, error) { entity := &saml.EntityDescriptor{} if err := xrv.Validate(bytes.NewBuffer(data)); err != nil { return nil, err } err := xml.Unmarshal(data, entity) // this comparison is ugly, but it is how the error is generated in encoding/xml if err != nil && err.Error() == "expected element type but have " { entities := &saml.EntitiesDescriptor{} if err := xml.Unmarshal(data, entities); err != nil { return nil, err } for i, e := range entities.EntityDescriptors { if len(e.IDPSSODescriptors) > 0 { return &entities.EntityDescriptors[i], nil } } return nil, errors.New("no entity found with IDPSSODescriptor") } if err != nil { return nil, err } return entity, nil } // FetchMetadata returns metadata from an IDP metadata URL. func FetchMetadata(ctx context.Context, httpClient *http.Client, metadataURL url.URL) (*saml.EntityDescriptor, error) { req, err := http.NewRequest("GET", metadataURL.String(), nil) if err != nil { return nil, err } req = req.WithContext(ctx) resp, err := httpClient.Do(req) if err != nil { return nil, err } defer resp.Body.Close() if resp.StatusCode >= 400 { return nil, httperr.Response(*resp) } data, err := ioutil.ReadAll(resp.Body) if err != nil { return nil, err } return ParseMetadata(data) } saml-0.4.6/samlsp/fetch_metadata_go116_test.go000066400000000000000000000035031415467341100212170ustar00rootroot00000000000000//go:build !go1.17 // +build !go1.17 package samlsp import ( "bytes" "context" "fmt" "net/http" "net/http/httptest" "net/url" "testing" "gotest.tools/assert" is "gotest.tools/assert/cmp" ) func TestFetchMetadataRejectsInvalid(t *testing.T) { test := NewMiddlewareTest(t) test.IDPMetadata = bytes.Replace(test.IDPMetadata, []byte("]]"), -1) testServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { assert.Check(t, is.Equal("/metadata", r.URL.String())) w.Write(test.IDPMetadata) })) fmt.Println(testServer.URL + "/metadata") u, _ := url.Parse(testServer.URL + "/metadata") md, err := FetchMetadata(context.Background(), testServer.Client(), *u) assert.Check(t, is.Error(err, "expected element in name space urn:oasis:names:tc:SAML:2.0:metadata but have no name space")) assert.Check(t, is.Nil(md)) } saml-0.4.6/samlsp/fetch_metadata_test.go000066400000000000000000000012561415467341100203050ustar00rootroot00000000000000package samlsp import ( "context" "fmt" "net/http" "net/http/httptest" "net/url" "testing" "gotest.tools/assert" is "gotest.tools/assert/cmp" ) func TestFetchMetadata(t *testing.T) { test := NewMiddlewareTest(t) testServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { assert.Check(t, is.Equal("/metadata", r.URL.String())) w.Write(test.IDPMetadata) })) fmt.Println(testServer.URL + "/metadata") u, _ := url.Parse(testServer.URL + "/metadata") md, err := FetchMetadata(context.Background(), testServer.Client(), *u) assert.Check(t, err) assert.Check(t, is.Equal("https://idp.testshib.org/idp/shibboleth", md.EntityID)) } saml-0.4.6/samlsp/middleware.go000066400000000000000000000200501415467341100164230ustar00rootroot00000000000000package samlsp import ( "encoding/xml" "net/http" "github.com/crewjam/saml" ) // Middleware implements middleware than allows a web application // to support SAML. // // It implements http.Handler so that it can provide the metadata and ACS endpoints, // typically /saml/metadata and /saml/acs, respectively. // // It also provides middleware RequireAccount which redirects users to // the auth process if they do not have session credentials. // // When redirecting the user through the SAML auth flow, the middleware assigns // a temporary cookie with a random name beginning with "saml_". The value of // the cookie is a signed JSON Web Token containing the original URL requested // and the SAML request ID. The random part of the name corresponds to the // RelayState parameter passed through the SAML flow. // // When validating the SAML response, the RelayState is used to look up the // correct cookie, validate that the SAML request ID, and redirect the user // back to their original URL. // // Sessions are established by issuing a JSON Web Token (JWT) as a session // cookie once the SAML flow has succeeded. The JWT token contains the // authenticated attributes from the SAML assertion. // // When the middleware receives a request with a valid session JWT it extracts // the SAML attributes and modifies the http.Request object adding a Context // object to the request context that contains attributes from the initial // SAML assertion. // // When issuing JSON Web Tokens, a signing key is required. Because the // SAML service provider already has a private key, we borrow that key // to sign the JWTs as well. type Middleware struct { ServiceProvider saml.ServiceProvider OnError func(w http.ResponseWriter, r *http.Request, err error) Binding string // either saml.HTTPPostBinding or saml.HTTPRedirectBinding ResponseBinding string // either saml.HTTPPostBinding or saml.HTTPArtifactBinding RequestTracker RequestTracker Session SessionProvider } // ServeHTTP implements http.Handler and serves the SAML-specific HTTP endpoints // on the URIs specified by m.ServiceProvider.MetadataURL and // m.ServiceProvider.AcsURL. func (m *Middleware) ServeHTTP(w http.ResponseWriter, r *http.Request) { if r.URL.Path == m.ServiceProvider.MetadataURL.Path { m.ServeMetadata(w, r) return } if r.URL.Path == m.ServiceProvider.AcsURL.Path { m.ServeACS(w, r) return } http.NotFoundHandler().ServeHTTP(w, r) } // ServeMetadata handles requests for the SAML metadata endpoint. func (m *Middleware) ServeMetadata(w http.ResponseWriter, r *http.Request) { buf, _ := xml.MarshalIndent(m.ServiceProvider.Metadata(), "", " ") w.Header().Set("Content-Type", "application/samlmetadata+xml") w.Write(buf) return } // ServeACS handles requests for the SAML ACS endpoint. func (m *Middleware) ServeACS(w http.ResponseWriter, r *http.Request) { r.ParseForm() possibleRequestIDs := []string{} if m.ServiceProvider.AllowIDPInitiated { possibleRequestIDs = append(possibleRequestIDs, "") } trackedRequests := m.RequestTracker.GetTrackedRequests(r) for _, tr := range trackedRequests { possibleRequestIDs = append(possibleRequestIDs, tr.SAMLRequestID) } assertion, err := m.ServiceProvider.ParseResponse(r, possibleRequestIDs) if err != nil { m.OnError(w, r, err) return } m.CreateSessionFromAssertion(w, r, assertion, m.ServiceProvider.DefaultRedirectURI) return } // RequireAccount is HTTP middleware that requires that each request be // associated with a valid session. If the request is not associated with a valid // session, then rather than serve the request, the middleware redirects the user // to start the SAML auth flow. func (m *Middleware) RequireAccount(handler http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { session, err := m.Session.GetSession(r) if session != nil { r = r.WithContext(ContextWithSession(r.Context(), session)) handler.ServeHTTP(w, r) return } if err == ErrNoSession { m.HandleStartAuthFlow(w, r) return } m.OnError(w, r, err) return }) } // HandleStartAuthFlow is called to start the SAML authentication process. func (m *Middleware) HandleStartAuthFlow(w http.ResponseWriter, r *http.Request) { // If we try to redirect when the original request is the ACS URL we'll // end up in a loop. This is a programming error, so we panic here. In // general this means a 500 to the user, which is preferable to a // redirect loop. if r.URL.Path == m.ServiceProvider.AcsURL.Path { panic("don't wrap Middleware with RequireAccount") } var binding, bindingLocation string if m.Binding != "" { binding = m.Binding bindingLocation = m.ServiceProvider.GetSSOBindingLocation(binding) } else { binding = saml.HTTPRedirectBinding bindingLocation = m.ServiceProvider.GetSSOBindingLocation(binding) if bindingLocation == "" { binding = saml.HTTPPostBinding bindingLocation = m.ServiceProvider.GetSSOBindingLocation(binding) } } authReq, err := m.ServiceProvider.MakeAuthenticationRequest(bindingLocation, binding, m.ResponseBinding) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } // relayState is limited to 80 bytes but also must be integrity protected. // this means that we cannot use a JWT because it is way to long. Instead // we set a signed cookie that encodes the original URL which we'll check // against the SAML response when we get it. relayState, err := m.RequestTracker.TrackRequest(w, r, authReq.ID) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } if binding == saml.HTTPRedirectBinding { redirectURL, err := authReq.Redirect(relayState, &m.ServiceProvider) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } w.Header().Add("Location", redirectURL.String()) w.WriteHeader(http.StatusFound) return } if binding == saml.HTTPPostBinding { w.Header().Add("Content-Security-Policy", ""+ "default-src; "+ "script-src 'sha256-AjPdJSbZmeWHnEc5ykvJFay8FTWeTeRbs9dutfZ0HqE='; "+ "reflected-xss block; referrer no-referrer;") w.Header().Add("Content-type", "text/html") w.Write([]byte(``)) w.Write(authReq.Post(relayState)) w.Write([]byte(``)) return } panic("not reached") } // CreateSessionFromAssertion is invoked by ServeHTTP when we have a new, valid SAML assertion. func (m *Middleware) CreateSessionFromAssertion(w http.ResponseWriter, r *http.Request, assertion *saml.Assertion, redirectURI string) { if trackedRequestIndex := r.Form.Get("RelayState"); trackedRequestIndex != "" { trackedRequest, err := m.RequestTracker.GetTrackedRequest(r, trackedRequestIndex) if err != nil { m.OnError(w, r, err) return } m.RequestTracker.StopTrackingRequest(w, r, trackedRequestIndex) redirectURI = trackedRequest.URI } if err := m.Session.CreateSession(w, r, assertion); err != nil { m.OnError(w, r, err) return } http.Redirect(w, r, redirectURI, http.StatusFound) } // RequireAttribute returns a middleware function that requires that the // SAML attribute `name` be set to `value`. This can be used to require // that a remote user be a member of a group. It relies on the Claims assigned // to to the context in RequireAccount. // // For example: // // goji.Use(m.RequireAccount) // goji.Use(RequireAttributeMiddleware("eduPersonAffiliation", "Staff")) // func RequireAttribute(name, value string) func(http.Handler) http.Handler { return func(handler http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if session := SessionFromContext(r.Context()); session != nil { // this will panic if we have the wrong type of Session, and that is OK. sessionWithAttributes := session.(SessionWithAttributes) attributes := sessionWithAttributes.GetAttributes() if values, ok := attributes[name]; ok { for _, v := range values { if v == value { handler.ServeHTTP(w, r) return } } } } http.Error(w, http.StatusText(http.StatusForbidden), http.StatusForbidden) }) } } saml-0.4.6/samlsp/middleware_test.go000066400000000000000000000462231415467341100174740ustar00rootroot00000000000000package samlsp import ( "bytes" "crypto/rsa" "crypto/sha256" "crypto/x509" "encoding/base64" "encoding/json" "encoding/xml" "io/ioutil" "net" "net/http" "net/http/httptest" "net/url" "strings" "testing" "time" "github.com/golang-jwt/jwt/v4" dsig "github.com/russellhaering/goxmldsig" "gotest.tools/assert" is "gotest.tools/assert/cmp" "gotest.tools/golden" "github.com/crewjam/saml" "github.com/crewjam/saml/testsaml" ) type MiddlewareTest struct { AuthnRequest []byte SamlResponse []byte Key *rsa.PrivateKey Certificate *x509.Certificate IDPMetadata []byte Middleware *Middleware expectedSessionCookie string } type testRandomReader struct { Next byte } func (tr *testRandomReader) Read(p []byte) (n int, err error) { for i := 0; i < len(p); i++ { p[i] = tr.Next tr.Next += 2 } return len(p), nil } func NewMiddlewareTest(t *testing.T) *MiddlewareTest { test := MiddlewareTest{} saml.TimeNow = func() time.Time { rv, _ := time.Parse("Mon Jan 2 15:04:05.999999999 MST 2006", "Mon Dec 1 01:57:09.123456789 UTC 2015") return rv } jwt.TimeFunc = saml.TimeNow saml.Clock = dsig.NewFakeClockAt(saml.TimeNow()) saml.RandReader = &testRandomReader{} test.AuthnRequest = golden.Get(t, "authn_request.url") test.SamlResponse = golden.Get(t, "saml_response.xml") test.Key = mustParsePrivateKey(golden.Get(t, "key.pem")).(*rsa.PrivateKey) test.Certificate = mustParseCertificate(golden.Get(t, "cert.pem")) test.IDPMetadata = golden.Get(t, "idp_metadata.xml") var metadata saml.EntityDescriptor if err := xml.Unmarshal(test.IDPMetadata, &metadata); err != nil { panic(err) } opts := Options{ URL: mustParseURL("https://15661444.ngrok.io/"), Key: test.Key, Certificate: test.Certificate, IDPMetadata: &metadata, } var err error test.Middleware, err = New(opts) if err != nil { panic(err) } sessionProvider := DefaultSessionProvider(opts) sessionProvider.Name = "ttt" sessionProvider.MaxAge = 7200 * time.Second sessionCodec := sessionProvider.Codec.(JWTSessionCodec) sessionCodec.MaxAge = 7200 * time.Second sessionProvider.Codec = sessionCodec test.Middleware.Session = sessionProvider test.Middleware.ServiceProvider.MetadataURL.Path = "/saml2/metadata" test.Middleware.ServiceProvider.AcsURL.Path = "/saml2/acs" test.Middleware.ServiceProvider.SloURL.Path = "/saml2/slo" var tc JWTSessionClaims if err := json.Unmarshal(golden.Get(t, "token.json"), &tc); err != nil { panic(err) } test.expectedSessionCookie, err = sessionProvider.Codec.Encode(tc) if err != nil { panic(err) } return &test } func (test *MiddlewareTest) makeTrackedRequest(id string) string { codec := test.Middleware.RequestTracker.(CookieRequestTracker).Codec token, err := codec.Encode(TrackedRequest{ Index: "KCosLjAyNDY4Ojw-QEJERkhKTE5QUlRWWFpcXmBiZGZoamxucHJ0dnh6", SAMLRequestID: id, URI: "/frob", }) if err != nil { panic(err) } return token } func TestMiddlewareCanProduceMetadata(t *testing.T) { test := NewMiddlewareTest(t) req, _ := http.NewRequest("GET", "/saml2/metadata", nil) resp := httptest.NewRecorder() test.Middleware.ServeHTTP(resp, req) assert.Check(t, is.Equal(http.StatusOK, resp.Code)) assert.Check(t, is.Equal("application/samlmetadata+xml", resp.Header().Get("Content-type"))) golden.Assert(t, resp.Body.String(), "expected_middleware_metadata.xml") } func TestMiddlewareFourOhFour(t *testing.T) { test := NewMiddlewareTest(t) req, _ := http.NewRequest("GET", "/this/is/not/a/supported/uri", nil) resp := httptest.NewRecorder() test.Middleware.ServeHTTP(resp, req) assert.Check(t, is.Equal(http.StatusNotFound, resp.Code)) respBuf, _ := ioutil.ReadAll(resp.Body) assert.Check(t, is.Equal("404 page not found\n", string(respBuf))) } func TestMiddlewareRequireAccountNoCreds(t *testing.T) { test := NewMiddlewareTest(t) test.Middleware.ServiceProvider.AcsURL.Scheme = "http" handler := test.Middleware.RequireAccount( http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { panic("not reached") })) req, _ := http.NewRequest("GET", "/frob", nil) resp := httptest.NewRecorder() handler.ServeHTTP(resp, req) assert.Check(t, is.Equal(http.StatusFound, resp.Code)) assert.Check(t, is.Equal("saml_KCosLjAyNDY4Ojw-QEJERkhKTE5QUlRWWFpcXmBiZGZoamxucHJ0dnh6="+ test.makeTrackedRequest("id-00020406080a0c0e10121416181a1c1e20222426")+"; Path=/saml2/acs; Max-Age=90; HttpOnly", resp.Header().Get("Set-Cookie"))) redirectURL, err := url.Parse(resp.Header().Get("Location")) assert.Check(t, err) decodedRequest, err := testsaml.ParseRedirectRequest(redirectURL) assert.Check(t, err) golden.Assert(t, string(decodedRequest), "expected_authn_request.xml") } func TestMiddlewareRequireAccountNoCredsSecure(t *testing.T) { test := NewMiddlewareTest(t) handler := test.Middleware.RequireAccount( http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { panic("not reached") })) req, _ := http.NewRequest("GET", "/frob", nil) resp := httptest.NewRecorder() handler.ServeHTTP(resp, req) assert.Check(t, is.Equal(http.StatusFound, resp.Code)) assert.Check(t, is.Equal("saml_KCosLjAyNDY4Ojw-QEJERkhKTE5QUlRWWFpcXmBiZGZoamxucHJ0dnh6="+test.makeTrackedRequest("id-00020406080a0c0e10121416181a1c1e20222426")+"; Path=/saml2/acs; Max-Age=90; HttpOnly; Secure", resp.Header().Get("Set-Cookie"))) redirectURL, err := url.Parse(resp.Header().Get("Location")) assert.Check(t, err) decodedRequest, err := testsaml.ParseRedirectRequest(redirectURL) assert.Check(t, err) golden.Assert(t, string(decodedRequest), "expected_authn_request_secure.xml") } func TestMiddlewareRequireAccountNoCredsPostBinding(t *testing.T) { test := NewMiddlewareTest(t) test.Middleware.ServiceProvider.IDPMetadata.IDPSSODescriptors[0].SingleSignOnServices = test.Middleware.ServiceProvider.IDPMetadata.IDPSSODescriptors[0].SingleSignOnServices[1:2] assert.Check(t, is.Equal("", test.Middleware.ServiceProvider.GetSSOBindingLocation(saml.HTTPRedirectBinding))) handler := test.Middleware.RequireAccount( http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { panic("not reached") })) req, _ := http.NewRequest("GET", "/frob", nil) resp := httptest.NewRecorder() handler.ServeHTTP(resp, req) assert.Check(t, is.Equal(http.StatusOK, resp.Code)) assert.Check(t, is.Equal("saml_KCosLjAyNDY4Ojw-QEJERkhKTE5QUlRWWFpcXmBiZGZoamxucHJ0dnh6="+test.makeTrackedRequest("id-00020406080a0c0e10121416181a1c1e20222426")+"; Path=/saml2/acs; Max-Age=90; HttpOnly; Secure", resp.Header().Get("Set-Cookie"))) golden.Assert(t, resp.Body.String(), "expected_post_binding_response.html") // check that the CSP script hash is set correctly scriptContent := "document.getElementById('SAMLSubmitButton').style.visibility=\"hidden\";document.getElementById('SAMLRequestForm').submit();" scriptSum := sha256.Sum256([]byte(scriptContent)) scriptHash := base64.StdEncoding.EncodeToString(scriptSum[:]) assert.Check(t, is.Equal("default-src; script-src 'sha256-"+scriptHash+"'; reflected-xss block; referrer no-referrer;", resp.Header().Get("Content-Security-Policy"))) assert.Check(t, is.Equal("text/html", resp.Header().Get("Content-type"))) } func TestMiddlewareRequireAccountCreds(t *testing.T) { test := NewMiddlewareTest(t) handler := test.Middleware.RequireAccount( http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { genericSession := SessionFromContext(r.Context()) jwtSession := genericSession.(JWTSessionClaims) assert.Check(t, is.Equal("555-5555", jwtSession.Attributes.Get("telephoneNumber"))) assert.Check(t, is.Equal("And I", jwtSession.Attributes.Get("sn"))) assert.Check(t, is.Equal("urn:mace:dir:entitlement:common-lib-terms", jwtSession.Attributes.Get("eduPersonEntitlement"))) assert.Check(t, is.Equal("", jwtSession.Attributes.Get("eduPersonTargetedID"))) assert.Check(t, is.Equal("Me Myself", jwtSession.Attributes.Get("givenName"))) assert.Check(t, is.Equal("Me Myself And I", jwtSession.Attributes.Get("cn"))) assert.Check(t, is.Equal("myself", jwtSession.Attributes.Get("uid"))) assert.Check(t, is.Equal("myself@testshib.org", jwtSession.Attributes.Get("eduPersonPrincipalName"))) assert.Check(t, is.DeepEqual([]string{"Member@testshib.org", "Staff@testshib.org"}, jwtSession.Attributes["eduPersonScopedAffiliation"])) assert.Check(t, is.DeepEqual([]string{"Member", "Staff"}, jwtSession.Attributes["eduPersonAffiliation"])) w.WriteHeader(http.StatusTeapot) })) req, _ := http.NewRequest("GET", "/frob", nil) req.Header.Set("Cookie", ""+ "ttt="+test.expectedSessionCookie+"; "+ "Path=/; Max-Age=7200") resp := httptest.NewRecorder() handler.ServeHTTP(resp, req) assert.Check(t, is.Equal(http.StatusTeapot, resp.Code)) } func TestMiddlewareRequireAccountBadCreds(t *testing.T) { test := NewMiddlewareTest(t) handler := test.Middleware.RequireAccount( http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { panic("not reached") })) req, _ := http.NewRequest("GET", "/frob", nil) req.Header.Set("Cookie", ""+ "ttt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.yejJbiI6Ik1lIE15c2VsZiBBbmQgSSIsImVkdVBlcnNvbkFmZmlsaWF0aW9uIjoiU3RhZmYiLCJlZHVQZXJzb25FbnRpdGxlbWVudCI6InVybjptYWNlOmRpcjplbnRpdGxlbWVudDpjb21tb24tbGliLXRlcm1zIiwiZWR1UGVyc29uUHJpbmNpcGFsTmFtZSI6Im15c2VsZkB0ZXN0c2hpYi5vcmciLCJlZHVQZXJzb25TY29wZWRBZmZpbGlhdGlvbiI6IlN0YWZmQHRlc3RzaGliLm9yZyIsImVkdVBlcnNvblRhcmdldGVkSUQiOiIiLCJleHAiOjE0NDg5Mzg2MjksImdpdmVuTmFtZSI6Ik1lIE15c2VsZiIsInNuIjoiQW5kIEkiLCJ0ZWxlcGhvbmVOdW1iZXIiOiI1NTUtNTU1NSIsInVpZCI6Im15c2VsZiJ9.SqeTkbGG35oFj_9H-d9oVdV-Hb7Vqam6LvZLcmia7FY; "+ "Path=/; Max-Age=7200; Secure") resp := httptest.NewRecorder() handler.ServeHTTP(resp, req) assert.Check(t, is.Equal(http.StatusFound, resp.Code)) assert.Check(t, is.Equal("saml_KCosLjAyNDY4Ojw-QEJERkhKTE5QUlRWWFpcXmBiZGZoamxucHJ0dnh6="+test.makeTrackedRequest("id-00020406080a0c0e10121416181a1c1e20222426")+"; Path=/saml2/acs; Max-Age=90; HttpOnly; Secure", resp.Header().Get("Set-Cookie"))) redirectURL, err := url.Parse(resp.Header().Get("Location")) assert.Check(t, err) decodedRequest, err := testsaml.ParseRedirectRequest(redirectURL) assert.Check(t, err) golden.Assert(t, string(decodedRequest), "expected_authn_request_secure.xml") } func TestMiddlewareRequireAccountExpiredCreds(t *testing.T) { test := NewMiddlewareTest(t) jwt.TimeFunc = func() time.Time { rv, _ := time.Parse("Mon Jan 2 15:04:05 UTC 2006", "Mon Dec 1 01:31:21 UTC 2115") return rv } handler := test.Middleware.RequireAccount( http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { panic("not reached") })) req, _ := http.NewRequest("GET", "/frob", nil) req.Header.Set("Cookie", ""+ "ttt="+test.expectedSessionCookie+"; "+ "Path=/; Max-Age=7200") resp := httptest.NewRecorder() handler.ServeHTTP(resp, req) assert.Check(t, is.Equal(http.StatusFound, resp.Code)) assert.Check(t, is.Equal("saml_KCosLjAyNDY4Ojw-QEJERkhKTE5QUlRWWFpcXmBiZGZoamxucHJ0dnh6="+test.makeTrackedRequest("id-00020406080a0c0e10121416181a1c1e20222426")+"; Path=/saml2/acs; Max-Age=90; HttpOnly; Secure", resp.Header().Get("Set-Cookie"))) redirectURL, err := url.Parse(resp.Header().Get("Location")) assert.Check(t, err) decodedRequest, err := testsaml.ParseRedirectRequest(redirectURL) assert.Check(t, err) golden.Assert(t, string(decodedRequest), "expected_authn_request_secure.xml") } func TestMiddlewareRequireAccountPanicOnRequestToACS(t *testing.T) { test := NewMiddlewareTest(t) handler := test.Middleware.RequireAccount( http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { panic("not reached") })) req, _ := http.NewRequest("POST", "https://15661444.ngrok.io/saml2/acs", nil) resp := httptest.NewRecorder() assert.Check(t, is.Panics(func() { handler.ServeHTTP(resp, req) })) } func TestMiddlewareRequireAttribute(t *testing.T) { test := NewMiddlewareTest(t) handler := test.Middleware.RequireAccount( RequireAttribute("eduPersonAffiliation", "Staff")( http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusTeapot) }))) req, _ := http.NewRequest("GET", "/frob", nil) req.Header.Set("Cookie", ""+ "ttt="+test.expectedSessionCookie+"; "+ "Path=/; Max-Age=7200") resp := httptest.NewRecorder() handler.ServeHTTP(resp, req) assert.Check(t, is.Equal(http.StatusTeapot, resp.Code)) } func TestMiddlewareRequireAttributeWrongValue(t *testing.T) { test := NewMiddlewareTest(t) handler := test.Middleware.RequireAccount( RequireAttribute("eduPersonAffiliation", "DomainAdmins")( http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { panic("not reached") }))) req, _ := http.NewRequest("GET", "/frob", nil) req.Header.Set("Cookie", ""+ "ttt="+test.expectedSessionCookie+"; "+ "Path=/; Max-Age=7200") resp := httptest.NewRecorder() handler.ServeHTTP(resp, req) assert.Check(t, is.Equal(http.StatusForbidden, resp.Code)) } func TestMiddlewareRequireAttributeNotPresent(t *testing.T) { test := NewMiddlewareTest(t) handler := test.Middleware.RequireAccount( RequireAttribute("valueThatDoesntExist", "doesntMatter")( http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { panic("not reached") }))) req, _ := http.NewRequest("GET", "/frob", nil) req.Header.Set("Cookie", ""+ "ttt="+test.expectedSessionCookie+"; "+ "Path=/; Max-Age=7200") resp := httptest.NewRecorder() handler.ServeHTTP(resp, req) assert.Check(t, is.Equal(http.StatusForbidden, resp.Code)) } func TestMiddlewareRequireAttributeMissingAccount(t *testing.T) { test := NewMiddlewareTest(t) handler := RequireAttribute("eduPersonAffiliation", "DomainAdmins")( http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { panic("not reached") })) req, _ := http.NewRequest("GET", "/frob", nil) req.Header.Set("Cookie", ""+ "ttt="+test.expectedSessionCookie+"; "+ "Path=/; Max-Age=7200") resp := httptest.NewRecorder() handler.ServeHTTP(resp, req) assert.Check(t, is.Equal(http.StatusForbidden, resp.Code)) } func TestMiddlewareCanParseResponse(t *testing.T) { test := NewMiddlewareTest(t) v := &url.Values{} v.Set("SAMLResponse", base64.StdEncoding.EncodeToString(test.SamlResponse)) v.Set("RelayState", "KCosLjAyNDY4Ojw-QEJERkhKTE5QUlRWWFpcXmBiZGZoamxucHJ0dnh6") req, _ := http.NewRequest("POST", "/saml2/acs", bytes.NewReader([]byte(v.Encode()))) req.Header.Set("Content-Type", "application/x-www-form-urlencoded") req.Header.Set("Cookie", ""+ "saml_KCosLjAyNDY4Ojw-QEJERkhKTE5QUlRWWFpcXmBiZGZoamxucHJ0dnh6="+test.makeTrackedRequest("id-9e61753d64e928af5a7a341a97f420c9")) resp := httptest.NewRecorder() test.Middleware.ServeHTTP(resp, req) assert.Check(t, is.Equal(http.StatusFound, resp.Code)) assert.Check(t, is.Equal("/frob", resp.Header().Get("Location"))) assert.Check(t, is.DeepEqual([]string{ "saml_KCosLjAyNDY4Ojw-QEJERkhKTE5QUlRWWFpcXmBiZGZoamxucHJ0dnh6=; Domain=15661444.ngrok.io; Expires=Thu, 01 Jan 1970 00:00:01 GMT", "ttt=" + test.expectedSessionCookie + "; " + "Path=/; Domain=15661444.ngrok.io; Max-Age=7200; HttpOnly; Secure"}, resp.Header()["Set-Cookie"])) } func TestMiddlewareDefaultCookieDomainIPv4(t *testing.T) { test := NewMiddlewareTest(t) ipv4Loopback := net.IP{127, 0, 0, 1} sp := DefaultSessionProvider(Options{ URL: mustParseURL("https://" + net.JoinHostPort(ipv4Loopback.String(), "54321")), Key: test.Key, }) req, _ := http.NewRequest("GET", "/", nil) resp := httptest.NewRecorder() sp.CreateSession(resp, req, &saml.Assertion{}) assert.Check(t, strings.Contains(resp.Header().Get("Set-Cookie"), "Domain=127.0.0.1;"), "Cookie domain must not contain a port or the cookie cannot be set properly: %v", resp.Header().Get("Set-Cookie")) } func TestMiddlewareDefaultCookieDomainIPv6(t *testing.T) { t.Skip("fails") // TODO(ross): fix this test test := NewMiddlewareTest(t) sp := DefaultSessionProvider(Options{ URL: mustParseURL("https://" + net.JoinHostPort(net.IPv6loopback.String(), "54321")), Key: test.Key, }) req, _ := http.NewRequest("GET", "/", nil) resp := httptest.NewRecorder() sp.CreateSession(resp, req, &saml.Assertion{}) assert.Check(t, strings.Contains(resp.Header().Get("Set-Cookie"), "Domain=::1;"), "Cookie domain must not contain a port or the cookie cannot be set properly: %v", resp.Header().Get("Set-Cookie")) } func TestMiddlewareRejectsInvalidRelayState(t *testing.T) { test := NewMiddlewareTest(t) test.Middleware.OnError = func(w http.ResponseWriter, r *http.Request, err error) { assert.Check(t, is.Error(err, http.ErrNoCookie.Error())) http.Error(w, "forbidden", http.StatusTeapot) } v := &url.Values{} v.Set("SAMLResponse", base64.StdEncoding.EncodeToString(test.SamlResponse)) v.Set("RelayState", "ICIkJigqLC4wMjQ2ODo8PkBCREZISkxOUFJUVlhaXF5gYmRmaGpsbnBy") req, _ := http.NewRequest("POST", "/saml2/acs", bytes.NewReader([]byte(v.Encode()))) req.Header.Set("Content-Type", "application/x-www-form-urlencoded") req.Header.Set("Cookie", ""+ "saml_KCosLjAyNDY4Ojw-QEJERkhKTE5QUlRWWFpcXmBiZGZoamxucHJ0dnh6="+test.makeTrackedRequest("id-9e61753d64e928af5a7a341a97f420c9")) resp := httptest.NewRecorder() test.Middleware.ServeHTTP(resp, req) assert.Check(t, is.Equal(http.StatusTeapot, resp.Code)) assert.Check(t, is.Equal("", resp.Header().Get("Location"))) assert.Check(t, is.Equal("", resp.Header().Get("Set-Cookie"))) } func TestMiddlewareRejectsInvalidCookie(t *testing.T) { test := NewMiddlewareTest(t) test.Middleware.OnError = func(w http.ResponseWriter, r *http.Request, err error) { assert.Check(t, is.Error(err, "Authentication failed")) http.Error(w, "forbidden", http.StatusTeapot) } v := &url.Values{} v.Set("SAMLResponse", base64.StdEncoding.EncodeToString(test.SamlResponse)) v.Set("RelayState", "KCosLjAyNDY4Ojw-QEJERkhKTE5QUlRWWFpcXmBiZGZoamxucHJ0dnh6") req, _ := http.NewRequest("POST", "/saml2/acs", bytes.NewReader([]byte(v.Encode()))) req.Header.Set("Content-Type", "application/x-www-form-urlencoded") req.Header.Set("Cookie", ""+ "saml_KCosLjAyNDY4Ojw-QEJERkhKTE5QUlRWWFpcXmBiZGZoamxucHJ0dnh6="+test.makeTrackedRequest("wrong")) resp := httptest.NewRecorder() test.Middleware.ServeHTTP(resp, req) assert.Check(t, is.Equal(http.StatusTeapot, resp.Code)) assert.Check(t, is.Equal("", resp.Header().Get("Location"))) assert.Check(t, is.Equal("", resp.Header().Get("Set-Cookie"))) } func TestMiddlewareHandlesInvalidResponse(t *testing.T) { test := NewMiddlewareTest(t) v := &url.Values{} v.Set("SAMLResponse", "this is not a valid saml response") v.Set("RelayState", "KCosLjAyNDY4Ojw-QEJERkhKTE5QUlRWWFpcXmBiZGZoamxucHJ0dnh6") req, _ := http.NewRequest("POST", "/saml2/acs", bytes.NewReader([]byte(v.Encode()))) req.Header.Set("Content-Type", "application/x-www-form-urlencoded") req.Header.Set("Cookie", ""+ "saml_KCosLjAyNDY4Ojw-QEJERkhKTE5QUlRWWFpcXmBiZGZoamxucHJ0dnh6="+test.makeTrackedRequest("wrong")) resp := httptest.NewRecorder() test.Middleware.ServeHTTP(resp, req) // note: it is important that when presented with an invalid request, // the ACS handles DOES NOT reveal detailed error information in the // HTTP response. assert.Check(t, is.Equal(http.StatusForbidden, resp.Code)) respBody, _ := ioutil.ReadAll(resp.Body) assert.Check(t, is.Equal("Forbidden\n", string(respBody))) assert.Check(t, is.Equal("", resp.Header().Get("Location"))) assert.Check(t, is.Equal("", resp.Header().Get("Set-Cookie"))) } saml-0.4.6/samlsp/new.go000066400000000000000000000106241415467341100151050ustar00rootroot00000000000000// Package samlsp provides helpers that can be used to protect web services using SAML. package samlsp import ( "crypto/rsa" "crypto/x509" "net/http" "net/url" dsig "github.com/russellhaering/goxmldsig" "github.com/crewjam/saml" ) // Options represents the parameters for creating a new middleware type Options struct { EntityID string URL url.URL Key *rsa.PrivateKey Certificate *x509.Certificate Intermediates []*x509.Certificate AllowIDPInitiated bool DefaultRedirectURI string IDPMetadata *saml.EntityDescriptor SignRequest bool UseArtifactResponse bool ForceAuthn bool // TODO(ross): this should be *bool CookieSameSite http.SameSite RelayStateFunc func(w http.ResponseWriter, r *http.Request) string } // DefaultSessionCodec returns the default SessionCodec for the provided options, // a JWTSessionCodec configured to issue signed tokens. func DefaultSessionCodec(opts Options) JWTSessionCodec { return JWTSessionCodec{ SigningMethod: defaultJWTSigningMethod, Audience: opts.URL.String(), Issuer: opts.URL.String(), MaxAge: defaultSessionMaxAge, Key: opts.Key, } } // DefaultSessionProvider returns the default SessionProvider for the provided options, // a CookieSessionProvider configured to store sessions in a cookie. func DefaultSessionProvider(opts Options) CookieSessionProvider { return CookieSessionProvider{ Name: defaultSessionCookieName, Domain: opts.URL.Host, MaxAge: defaultSessionMaxAge, HTTPOnly: true, Secure: opts.URL.Scheme == "https", SameSite: opts.CookieSameSite, Codec: DefaultSessionCodec(opts), } } // DefaultTrackedRequestCodec returns a new TrackedRequestCodec for the provided // options, a JWTTrackedRequestCodec that uses a JWT to encode TrackedRequests. func DefaultTrackedRequestCodec(opts Options) JWTTrackedRequestCodec { return JWTTrackedRequestCodec{ SigningMethod: defaultJWTSigningMethod, Audience: opts.URL.String(), Issuer: opts.URL.String(), MaxAge: saml.MaxIssueDelay, Key: opts.Key, } } // DefaultRequestTracker returns a new RequestTracker for the provided options, // a CookieRequestTracker which uses cookies to track pending requests. func DefaultRequestTracker(opts Options, serviceProvider *saml.ServiceProvider) CookieRequestTracker { return CookieRequestTracker{ ServiceProvider: serviceProvider, NamePrefix: "saml_", Codec: DefaultTrackedRequestCodec(opts), MaxAge: saml.MaxIssueDelay, RelayStateFunc: opts.RelayStateFunc, SameSite: opts.CookieSameSite, } } // DefaultServiceProvider returns the default saml.ServiceProvider for the provided // options. func DefaultServiceProvider(opts Options) saml.ServiceProvider { metadataURL := opts.URL.ResolveReference(&url.URL{Path: "saml/metadata"}) acsURL := opts.URL.ResolveReference(&url.URL{Path: "saml/acs"}) sloURL := opts.URL.ResolveReference(&url.URL{Path: "saml/slo"}) var forceAuthn *bool if opts.ForceAuthn { forceAuthn = &opts.ForceAuthn } signatureMethod := dsig.RSASHA1SignatureMethod if !opts.SignRequest { signatureMethod = "" } if opts.DefaultRedirectURI == "" { opts.DefaultRedirectURI = "/" } return saml.ServiceProvider{ EntityID: opts.EntityID, Key: opts.Key, Certificate: opts.Certificate, Intermediates: opts.Intermediates, MetadataURL: *metadataURL, AcsURL: *acsURL, SloURL: *sloURL, IDPMetadata: opts.IDPMetadata, ForceAuthn: forceAuthn, SignatureMethod: signatureMethod, AllowIDPInitiated: opts.AllowIDPInitiated, DefaultRedirectURI: opts.DefaultRedirectURI, } } // New creates a new Middleware with the default providers for the // given options. // // You can customize the behavior of the middleware in more detail by // replacing and/or changing Session, RequestTracker, and ServiceProvider // in the returned Middleware. func New(opts Options) (*Middleware, error) { m := &Middleware{ ServiceProvider: DefaultServiceProvider(opts), Binding: "", ResponseBinding: saml.HTTPPostBinding, OnError: DefaultOnError, Session: DefaultSessionProvider(opts), } m.RequestTracker = DefaultRequestTracker(opts, &m.ServiceProvider) if opts.UseArtifactResponse { m.ResponseBinding = saml.HTTPArtifactBinding } return m, nil } saml-0.4.6/samlsp/request_tracker.go000066400000000000000000000034161415467341100175200ustar00rootroot00000000000000package samlsp import ( "net/http" ) // RequestTracker tracks pending authentication requests. // // There are two main reasons for this: // // 1. When the middleware initiates an authentication request it must track the original URL // in order to redirect the user to the right place after the authentication completes. // // 2. After the authentication completes, we want to ensure that the user presenting the // assertion is actually the one the request it, to mitigate request forgeries. type RequestTracker interface { // TrackRequest starts tracking the SAML request with the given ID. It returns an // `index` that should be used as the RelayState in the SAMl request flow. TrackRequest(w http.ResponseWriter, r *http.Request, samlRequestID string) (index string, err error) // StopTrackingRequest stops tracking the SAML request given by index, which is a string // previously returned from TrackRequest StopTrackingRequest(w http.ResponseWriter, r *http.Request, index string) error // GetTrackedRequests returns all the pending tracked requests GetTrackedRequests(r *http.Request) []TrackedRequest // GetTrackedRequest returns a pending tracked request. GetTrackedRequest(r *http.Request, index string) (*TrackedRequest, error) } // TrackedRequest holds the data we store for each pending request. type TrackedRequest struct { Index string `json:"-"` SAMLRequestID string `json:"id"` URI string `json:"uri"` } // TrackedRequestCodec handles encoding and decoding of a TrackedRequest. type TrackedRequestCodec interface { // Encode returns an encoded string representing the TrackedRequest. Encode(value TrackedRequest) (string, error) // Decode returns a Tracked request from an encoded string. Decode(signed string) (*TrackedRequest, error) } saml-0.4.6/samlsp/request_tracker_cookie.go000066400000000000000000000060551415467341100210530ustar00rootroot00000000000000package samlsp import ( "encoding/base64" "fmt" "net/http" "strings" "time" "github.com/crewjam/saml" ) var _ RequestTracker = CookieRequestTracker{} // CookieRequestTracker tracks requests by setting a uniquely named // cookie for each request. type CookieRequestTracker struct { ServiceProvider *saml.ServiceProvider NamePrefix string Codec TrackedRequestCodec MaxAge time.Duration RelayStateFunc func(w http.ResponseWriter, r *http.Request) string SameSite http.SameSite } // TrackRequest starts tracking the SAML request with the given ID. It returns an // `index` that should be used as the RelayState in the SAMl request flow. func (t CookieRequestTracker) TrackRequest(w http.ResponseWriter, r *http.Request, samlRequestID string) (string, error) { trackedRequest := TrackedRequest{ Index: base64.RawURLEncoding.EncodeToString(randomBytes(42)), SAMLRequestID: samlRequestID, URI: r.URL.String(), } if t.RelayStateFunc != nil { relayState := t.RelayStateFunc(w, r) if relayState != "" { trackedRequest.Index = relayState } } signedTrackedRequest, err := t.Codec.Encode(trackedRequest) if err != nil { return "", err } http.SetCookie(w, &http.Cookie{ Name: t.NamePrefix + trackedRequest.Index, Value: signedTrackedRequest, MaxAge: int(t.MaxAge.Seconds()), HttpOnly: true, SameSite: t.SameSite, Secure: t.ServiceProvider.AcsURL.Scheme == "https", Path: t.ServiceProvider.AcsURL.Path, }) return trackedRequest.Index, nil } // StopTrackingRequest stops tracking the SAML request given by index, which is a string // previously returned from TrackRequest func (t CookieRequestTracker) StopTrackingRequest(w http.ResponseWriter, r *http.Request, index string) error { cookie, err := r.Cookie(t.NamePrefix + index) if err != nil { return err } cookie.Value = "" cookie.Domain = t.ServiceProvider.AcsURL.Hostname() cookie.Expires = time.Unix(1, 0) // past time as close to epoch as possible, but not zero time.Time{} http.SetCookie(w, cookie) return nil } // GetTrackedRequests returns all the pending tracked requests func (t CookieRequestTracker) GetTrackedRequests(r *http.Request) []TrackedRequest { rv := []TrackedRequest{} for _, cookie := range r.Cookies() { if !strings.HasPrefix(cookie.Name, t.NamePrefix) { continue } trackedRequest, err := t.Codec.Decode(cookie.Value) if err != nil { continue } index := strings.TrimPrefix(cookie.Name, t.NamePrefix) if index != trackedRequest.Index { continue } rv = append(rv, *trackedRequest) } return rv } // GetTrackedRequest returns a pending tracked request. func (t CookieRequestTracker) GetTrackedRequest(r *http.Request, index string) (*TrackedRequest, error) { cookie, err := r.Cookie(t.NamePrefix + index) if err != nil { return nil, err } trackedRequest, err := t.Codec.Decode(cookie.Value) if err != nil { return nil, err } if trackedRequest.Index != index { return nil, fmt.Errorf("expected index %q, got %q", index, trackedRequest.Index) } return trackedRequest, nil } saml-0.4.6/samlsp/request_tracker_jwt.go000066400000000000000000000041251415467341100204020ustar00rootroot00000000000000package samlsp import ( "crypto/rsa" "fmt" "time" "github.com/golang-jwt/jwt/v4" "github.com/crewjam/saml" ) var defaultJWTSigningMethod = jwt.SigningMethodRS256 // JWTTrackedRequestCodec encodes TrackedRequests as signed JWTs type JWTTrackedRequestCodec struct { SigningMethod jwt.SigningMethod Audience string Issuer string MaxAge time.Duration Key *rsa.PrivateKey } var _ TrackedRequestCodec = JWTTrackedRequestCodec{} // JWTTrackedRequestClaims represents the JWT claims for a tracked request. type JWTTrackedRequestClaims struct { jwt.StandardClaims TrackedRequest SAMLAuthnRequest bool `json:"saml-authn-request"` } // Encode returns an encoded string representing the TrackedRequest. func (s JWTTrackedRequestCodec) Encode(value TrackedRequest) (string, error) { now := saml.TimeNow() claims := JWTTrackedRequestClaims{ StandardClaims: jwt.StandardClaims{ Audience: s.Audience, ExpiresAt: now.Add(s.MaxAge).Unix(), IssuedAt: now.Unix(), Issuer: s.Issuer, NotBefore: now.Unix(), // TODO(ross): correct for clock skew Subject: value.Index, }, TrackedRequest: value, SAMLAuthnRequest: true, } token := jwt.NewWithClaims(s.SigningMethod, claims) return token.SignedString(s.Key) } // Decode returns a Tracked request from an encoded string. func (s JWTTrackedRequestCodec) Decode(signed string) (*TrackedRequest, error) { parser := jwt.Parser{ ValidMethods: []string{s.SigningMethod.Alg()}, } claims := JWTTrackedRequestClaims{} _, err := parser.ParseWithClaims(signed, &claims, func(*jwt.Token) (interface{}, error) { return s.Key.Public(), nil }) if err != nil { return nil, err } if !claims.VerifyAudience(s.Audience, true) { return nil, fmt.Errorf("expected audience %q, got %q", s.Audience, claims.Audience) } if !claims.VerifyIssuer(s.Issuer, true) { return nil, fmt.Errorf("expected issuer %q, got %q", s.Issuer, claims.Issuer) } if claims.SAMLAuthnRequest != true { return nil, fmt.Errorf("expected saml-authn-request") } claims.TrackedRequest.Index = claims.Subject return &claims.TrackedRequest, nil } saml-0.4.6/samlsp/samlsp_test.go000066400000000000000000000026761415467341100166620ustar00rootroot00000000000000package samlsp import ( "bytes" "context" "crypto" "crypto/x509" "encoding/pem" "io/ioutil" "net/http" "net/url" "testing" "gotest.tools/assert" "gotest.tools/golden" ) type mockTransport func(req *http.Request) (*http.Response, error) func (mt mockTransport) RoundTrip(req *http.Request) (*http.Response, error) { return mt(req) } func mustParseURL(s string) url.URL { rv, err := url.Parse(s) if err != nil { panic(err) } return *rv } func mustParsePrivateKey(pemStr []byte) crypto.PrivateKey { b, _ := pem.Decode(pemStr) if b == nil { panic("cannot parse PEM") } k, err := x509.ParsePKCS1PrivateKey(b.Bytes) if err != nil { panic(err) } return k } func mustParseCertificate(pemStr []byte) *x509.Certificate { b, _ := pem.Decode(pemStr) if b == nil { panic("cannot parse PEM") } cert, err := x509.ParseCertificate(b.Bytes) if err != nil { panic(err) } return cert } func TestCanParseTestshibMetadata(t *testing.T) { httpClient := http.Client{ Transport: mockTransport(func(req *http.Request) (*http.Response, error) { responseBody := golden.Get(t, "testshib_metadata.xml") return &http.Response{ Header: http.Header{}, Request: req, StatusCode: http.StatusOK, Body: ioutil.NopCloser(bytes.NewReader(responseBody)), }, nil }), } _, err := FetchMetadata(context.Background(), &httpClient, mustParseURL("https://ipa.example.com/idp/saml2/metadata")) assert.Check(t, err) } saml-0.4.6/samlsp/session.go000066400000000000000000000054121415467341100157760ustar00rootroot00000000000000package samlsp import ( "context" "errors" "net/http" "github.com/crewjam/saml" ) // Session is an interface implemented to contain a session. type Session interface{} // SessionWithAttributes is a session that can expose the // attributes provided by the SAML identity provider. type SessionWithAttributes interface { Session GetAttributes() Attributes } // ErrNoSession is the error returned when the remote user does not have a session var ErrNoSession = errors.New("saml: session not present") // SessionProvider is an interface implemented by types that can track // the active session of a user. The default implementation is CookieSessionProvider type SessionProvider interface { // CreateSession is called when we have received a valid SAML assertion and // should create a new session and modify the http response accordingly, e.g. by // setting a cookie. CreateSession(w http.ResponseWriter, r *http.Request, assertion *saml.Assertion) error // DeleteSession is called to modify the response such that it removed the current // session, e.g. by deleting a cookie. DeleteSession(w http.ResponseWriter, r *http.Request) error // GetSession returns the current Session associated with the request, or // ErrNoSession if there is no valid session. GetSession(r *http.Request) (Session, error) } // SessionCodec is an interface to convert SAML assertions to a // Session. The default implementation uses JWTs, JWTSessionCodec. type SessionCodec interface { // New creates a Session from the SAML assertion. New(assertion *saml.Assertion) (Session, error) // Encode returns a serialized version of the Session. // // Note: When implementing this function, it is reasonable to expect that // Session is of the exact type returned by New(), and panic if it is not. Encode(s Session) (string, error) // Decode parses the serialized session that may have been returned by Encode // and returns a Session. Decode(string) (Session, error) } type indexType int const sessionIndex indexType = iota // SessionFromContext returns the session associated with ctx, or nil // if no session are associated func SessionFromContext(ctx context.Context) Session { v := ctx.Value(sessionIndex) if v == nil { return nil } return v.(Session) } // ContextWithSession returns a new context with session associated func ContextWithSession(ctx context.Context, session Session) context.Context { return context.WithValue(ctx, sessionIndex, session) } // AttributeFromContext is a convenience method that returns the named attribute // from the session, if available. func AttributeFromContext(ctx context.Context, name string) string { s := SessionFromContext(ctx) if s == nil { return "" } sa, ok := s.(SessionWithAttributes) if !ok { return "" } return sa.GetAttributes().Get(name) } saml-0.4.6/samlsp/session_cookie.go000066400000000000000000000046531415467341100173350ustar00rootroot00000000000000package samlsp import ( "net" "net/http" "time" "github.com/crewjam/saml" ) const defaultSessionCookieName = "token" var _ SessionProvider = CookieSessionProvider{} // CookieSessionProvider is an implementation of SessionProvider that stores // session tokens in an HTTP cookie. type CookieSessionProvider struct { Name string Domain string HTTPOnly bool Secure bool SameSite http.SameSite MaxAge time.Duration Codec SessionCodec } // CreateSession is called when we have received a valid SAML assertion and // should create a new session and modify the http response accordingly, e.g. by // setting a cookie. func (c CookieSessionProvider) CreateSession(w http.ResponseWriter, r *http.Request, assertion *saml.Assertion) error { // Cookies should not have the port attached to them so strip it off if domain, _, err := net.SplitHostPort(c.Domain); err == nil { c.Domain = domain } session, err := c.Codec.New(assertion) if err != nil { return err } value, err := c.Codec.Encode(session) if err != nil { return err } http.SetCookie(w, &http.Cookie{ Name: c.Name, Domain: c.Domain, Value: value, MaxAge: int(c.MaxAge.Seconds()), HttpOnly: c.HTTPOnly, Secure: c.Secure || r.URL.Scheme == "https", SameSite: c.SameSite, Path: "/", }) return nil } // DeleteSession is called to modify the response such that it removed the current // session, e.g. by deleting a cookie. func (c CookieSessionProvider) DeleteSession(w http.ResponseWriter, r *http.Request) error { // Cookies should not have the port attached to them so strip it off if domain, _, err := net.SplitHostPort(c.Domain); err == nil { c.Domain = domain } cookie, err := r.Cookie(c.Name) if err == http.ErrNoCookie { return nil } if err != nil { return err } cookie.Value = "" cookie.Expires = time.Unix(1, 0) // past time as close to epoch as possible, but not zero time.Time{} cookie.Path = "/" cookie.Domain = c.Domain http.SetCookie(w, cookie) return nil } // GetSession returns the current Session associated with the request, or // ErrNoSession if there is no valid session. func (c CookieSessionProvider) GetSession(r *http.Request) (Session, error) { cookie, err := r.Cookie(c.Name) if err == http.ErrNoCookie { return nil, ErrNoSession } else if err != nil { return nil, err } session, err := c.Codec.Decode(cookie.Value) if err != nil { return nil, ErrNoSession } return session, nil } saml-0.4.6/samlsp/session_cookie_test.go000066400000000000000000000020451415467341100203650ustar00rootroot00000000000000package samlsp import ( "net/http" "net/http/httptest" "testing" "gotest.tools/assert" is "gotest.tools/assert/cmp" "github.com/crewjam/saml" ) func TestCookieSameSite(t *testing.T) { t.Parallel() csp := CookieSessionProvider{ Name: "token", Domain: "localhost", Codec: DefaultSessionCodec(Options{ Key: NewMiddlewareTest(t).Key, }), } getSessionCookie := func(tb testing.TB) *http.Cookie { resp := httptest.NewRecorder() req := httptest.NewRequest(http.MethodGet, "/", nil) err := csp.CreateSession(resp, req, &saml.Assertion{}) assert.Check(t, err) cookies := resp.Result().Cookies() assert.Check(t, is.Len(cookies, 1), "Expected to have a cookie set") return cookies[0] } t.Run("no same site", func(t *testing.T) { cookie := getSessionCookie(t) assert.Check(t, is.Equal(http.SameSite(0), cookie.SameSite)) }) t.Run("with same site", func(t *testing.T) { csp.SameSite = http.SameSiteStrictMode cookie := getSessionCookie(t) assert.Check(t, is.Equal(http.SameSiteStrictMode, cookie.SameSite)) }) } saml-0.4.6/samlsp/session_jwt.go000066400000000000000000000073531415467341100166700ustar00rootroot00000000000000package samlsp import ( "crypto/rsa" "errors" "fmt" "time" "github.com/golang-jwt/jwt/v4" "github.com/crewjam/saml" ) const ( defaultSessionMaxAge = time.Hour claimNameSessionIndex = "SessionIndex" ) // JWTSessionCodec implements SessionCoded to encode and decode Sessions from // the corresponding JWT. type JWTSessionCodec struct { SigningMethod jwt.SigningMethod Audience string Issuer string MaxAge time.Duration Key *rsa.PrivateKey } var _ SessionCodec = JWTSessionCodec{} // New creates a Session from the SAML assertion. // // The returned Session is a JWTSessionClaims. func (c JWTSessionCodec) New(assertion *saml.Assertion) (Session, error) { now := saml.TimeNow() claims := JWTSessionClaims{} claims.SAMLSession = true claims.Audience = c.Audience claims.Issuer = c.Issuer claims.IssuedAt = now.Unix() claims.ExpiresAt = now.Add(c.MaxAge).Unix() claims.NotBefore = now.Unix() if sub := assertion.Subject; sub != nil { if nameID := sub.NameID; nameID != nil { claims.Subject = nameID.Value } } claims.Attributes = map[string][]string{} for _, attributeStatement := range assertion.AttributeStatements { for _, attr := range attributeStatement.Attributes { claimName := attr.FriendlyName if claimName == "" { claimName = attr.Name } for _, value := range attr.Values { claims.Attributes[claimName] = append(claims.Attributes[claimName], value.Value) } } } // add SessionIndex to claims Attributes for _, authnStatement := range assertion.AuthnStatements { claims.Attributes[claimNameSessionIndex] = append(claims.Attributes[claimNameSessionIndex], authnStatement.SessionIndex) } return claims, nil } // Encode returns a serialized version of the Session. // // The provided session must be a JWTSessionClaims, otherwise this // function will panic. func (c JWTSessionCodec) Encode(s Session) (string, error) { claims := s.(JWTSessionClaims) // this will panic if you pass the wrong kind of session token := jwt.NewWithClaims(c.SigningMethod, claims) signedString, err := token.SignedString(c.Key) if err != nil { return "", err } return signedString, nil } // Decode parses the serialized session that may have been returned by Encode // and returns a Session. func (c JWTSessionCodec) Decode(signed string) (Session, error) { parser := jwt.Parser{ ValidMethods: []string{c.SigningMethod.Alg()}, } claims := JWTSessionClaims{} _, err := parser.ParseWithClaims(signed, &claims, func(*jwt.Token) (interface{}, error) { return c.Key.Public(), nil }) // TODO(ross): check for errors due to bad time and return ErrNoSession if err != nil { return nil, err } if !claims.VerifyAudience(c.Audience, true) { return nil, fmt.Errorf("expected audience %q, got %q", c.Audience, claims.Audience) } if !claims.VerifyIssuer(c.Issuer, true) { return nil, fmt.Errorf("expected issuer %q, got %q", c.Issuer, claims.Issuer) } if claims.SAMLSession != true { return nil, errors.New("expected saml-session") } return claims, nil } // JWTSessionClaims represents the JWT claims in the encoded session type JWTSessionClaims struct { jwt.StandardClaims Attributes Attributes `json:"attr"` SAMLSession bool `json:"saml-session"` } var _ Session = JWTSessionClaims{} // GetAttributes implements SessionWithAttributes. It returns the SAMl attributes. func (c JWTSessionClaims) GetAttributes() Attributes { return c.Attributes } // Attributes is a map of attributes provided in the SAML assertion type Attributes map[string][]string // Get returns the first attribute named `key` or an empty string if // no such attributes is present. func (a Attributes) Get(key string) string { if a == nil { return "" } v := a[key] if len(v) == 0 { return "" } return v[0] } saml-0.4.6/samlsp/testdata/000077500000000000000000000000001415467341100155735ustar00rootroot00000000000000saml-0.4.6/samlsp/testdata/authn_request.url000066400000000000000000000012571415467341100212130ustar00rootroot00000000000000https://idp.testshib.org/idp/profile/SAML2/Redirect/SSO?RelayState=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1cmkiOiIvIn0.eoUmy2fQduAz--6N82xIOmufY1ZZeRi5x--B7m1pNIY&SAMLRequest=lJJBj9MwEIX%2FSuR7Yzt10sZKIpWtkCotsGqB%2B5BMW4vELp4JsP8et4DYE5Tr%2BPnN957dbGY%2B%2Bz1%2BmZE4%2Bz6NnloxR28DkCPrYUKy3NvD5s2jLXJlLzFw6MMosg0RRnbBPwRP84TxgPGr6%2FHD%2FrEVZ%2BYLWSl1WVXaGJP7UwyfcxckwTQWEnoS2TbtdB6uHn9uuOGSczqgs%2FuUh3i6DmTaenQjyitGIfc4uIg9y8Phnch221a4YVFjpVflcqgM1sUajiWsYGk01KujKVRfJyHRjDtPDJ5bUShdLrReLNX7QtmysrrMK6Pqem3MeqFKq5TInn6lfeX84PypFSL7iJFuwKkN0TU303hPc%2FC7L5G9DnEC%2Frv8OkmxjjepRc%2BOn0X3r14nZBiAoZE%2FwbrmbfLZbZ%2FC6Prn%2F3zgcQzfHiICYys4zii6%2B4E5gieXsBv5kqBr5Msf1%2F0IAAD%2F%2Fw%3D%3D saml-0.4.6/samlsp/testdata/cert.pem000066400000000000000000000013351415467341100172350ustar00rootroot00000000000000-----BEGIN CERTIFICATE----- MIIB7zCCAVgCCQDFzbKIp7b3MTANBgkqhkiG9w0BAQUFADA8MQswCQYDVQQGEwJV UzELMAkGA1UECAwCR0ExDDAKBgNVBAoMA2ZvbzESMBAGA1UEAwwJbG9jYWxob3N0 MB4XDTEzMTAwMjAwMDg1MVoXDTE0MTAwMjAwMDg1MVowPDELMAkGA1UEBhMCVVMx CzAJBgNVBAgMAkdBMQwwCgYDVQQKDANmb28xEjAQBgNVBAMMCWxvY2FsaG9zdDCB nzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA1PMHYmhZj308kWLhZVT4vOulqx/9 ibm5B86fPWwUKKQ2i12MYtz07tzukPymisTDhQaqyJ8Kqb/6JjhmeMnEOdTvSPmH O8m1ZVveJU6NoKRn/mP/BD7FW52WhbrUXLSeHVSKfWkNk6S4hk9MV9TswTvyRIKv Rsw0X/gfnqkroJcCAwEAATANBgkqhkiG9w0BAQUFAAOBgQCMMlIO+GNcGekevKgk akpMdAqJfs24maGb90DvTLbRZRD7Xvn1MnVBBS9hzlXiFLYOInXACMW5gcoRFfeT QLSouMM8o57h0uKjfTmuoWHLQLi6hnF+cvCsEFiJZ4AbF+DgmO6TarJ8O05t8zvn OwJlNCASPZRH/JmF8tX0hoHuAQ== -----END CERTIFICATE----- saml-0.4.6/samlsp/testdata/expected_authn_request.xml000066400000000000000000000012401415467341100230620ustar00rootroot00000000000000https://15661444.ngrok.io/saml2/metadatasaml-0.4.6/samlsp/testdata/expected_authn_request_secure.xml000066400000000000000000000012411415467341100244310ustar00rootroot00000000000000https://15661444.ngrok.io/saml2/metadatasaml-0.4.6/samlsp/testdata/expected_middleware_metadata.xml000066400000000000000000000044651415467341100241640ustar00rootroot00000000000000 MIIB7zCCAVgCCQDFzbKIp7b3MTANBgkqhkiG9w0BAQUFADA8MQswCQYDVQQGEwJVUzELMAkGA1UECAwCR0ExDDAKBgNVBAoMA2ZvbzESMBAGA1UEAwwJbG9jYWxob3N0MB4XDTEzMTAwMjAwMDg1MVoXDTE0MTAwMjAwMDg1MVowPDELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkdBMQwwCgYDVQQKDANmb28xEjAQBgNVBAMMCWxvY2FsaG9zdDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA1PMHYmhZj308kWLhZVT4vOulqx/9ibm5B86fPWwUKKQ2i12MYtz07tzukPymisTDhQaqyJ8Kqb/6JjhmeMnEOdTvSPmHO8m1ZVveJU6NoKRn/mP/BD7FW52WhbrUXLSeHVSKfWkNk6S4hk9MV9TswTvyRIKvRsw0X/gfnqkroJcCAwEAATANBgkqhkiG9w0BAQUFAAOBgQCMMlIO+GNcGekevKgkakpMdAqJfs24maGb90DvTLbRZRD7Xvn1MnVBBS9hzlXiFLYOInXACMW5gcoRFfeTQLSouMM8o57h0uKjfTmuoWHLQLi6hnF+cvCsEFiJZ4AbF+DgmO6TarJ8O05t8zvnOwJlNCASPZRH/JmF8tX0hoHuAQ== saml-0.4.6/samlsp/testdata/expected_post_binding_response.html000066400000000000000000000025761415467341100247510ustar00rootroot00000000000000
saml-0.4.6/samlsp/testdata/idp_metadata.xml000066400000000000000000000220221415467341100207270ustar00rootroot00000000000000 testshib.org TestShib Test IdP TestShib IdP. Use this as a source of attributes for your test SP. https://www.testshib.org/testshibtwo.jpg MIIEDjCCAvagAwIBAgIBADANBgkqhkiG9w0BAQUFADBnMQswCQYDVQQGEwJVUzEV MBMGA1UECBMMUGVubnN5bHZhbmlhMRMwEQYDVQQHEwpQaXR0c2J1cmdoMREwDwYD VQQKEwhUZXN0U2hpYjEZMBcGA1UEAxMQaWRwLnRlc3RzaGliLm9yZzAeFw0wNjA4 MzAyMTEyMjVaFw0xNjA4MjcyMTEyMjVaMGcxCzAJBgNVBAYTAlVTMRUwEwYDVQQI EwxQZW5uc3lsdmFuaWExEzARBgNVBAcTClBpdHRzYnVyZ2gxETAPBgNVBAoTCFRl c3RTaGliMRkwFwYDVQQDExBpZHAudGVzdHNoaWIub3JnMIIBIjANBgkqhkiG9w0B AQEFAAOCAQ8AMIIBCgKCAQEArYkCGuTmJp9eAOSGHwRJo1SNatB5ZOKqDM9ysg7C yVTDClcpu93gSP10nH4gkCZOlnESNgttg0r+MqL8tfJC6ybddEFB3YBo8PZajKSe 3OQ01Ow3yT4I+Wdg1tsTpSge9gEz7SrC07EkYmHuPtd71CHiUaCWDv+xVfUQX0aT NPFmDixzUjoYzbGDrtAyCqA8f9CN2txIfJnpHE6q6CmKcoLADS4UrNPlhHSzd614 kR/JYiks0K4kbRqCQF0Dv0P5Di+rEfefC6glV8ysC8dB5/9nb0yh/ojRuJGmgMWH gWk6h0ihjihqiu4jACovUZ7vVOCgSE5Ipn7OIwqd93zp2wIDAQABo4HEMIHBMB0G A1UdDgQWBBSsBQ869nh83KqZr5jArr4/7b+QazCBkQYDVR0jBIGJMIGGgBSsBQ86 9nh83KqZr5jArr4/7b+Qa6FrpGkwZzELMAkGA1UEBhMCVVMxFTATBgNVBAgTDFBl bm5zeWx2YW5pYTETMBEGA1UEBxMKUGl0dHNidXJnaDERMA8GA1UEChMIVGVzdFNo aWIxGTAXBgNVBAMTEGlkcC50ZXN0c2hpYi5vcmeCAQAwDAYDVR0TBAUwAwEB/zAN BgkqhkiG9w0BAQUFAAOCAQEAjR29PhrCbk8qLN5MFfSVk98t3CT9jHZoYxd8QMRL I4j7iYQxXiGJTT1FXs1nd4Rha9un+LqTfeMMYqISdDDI6tv8iNpkOAvZZUosVkUo 93pv1T0RPz35hcHHYq2yee59HJOco2bFlcsH8JBXRSRrJ3Q7Eut+z9uo80JdGNJ4 /SJy5UorZ8KazGj16lfJhOBXldgrhppQBb0Nq6HKHguqmwRfJ+WkxemZXzhediAj Geka8nz8JjwxpUjAiSWYKLtJhGEaTqCYxCCX2Dw+dOTqUzHOZ7WKv4JXPK5G/Uhr 8K/qhmFT2nIQi538n6rVYLeWj8Bbnl+ev0peYzxFyF5sQA== urn:mace:shibboleth:1.0:nameIdentifier urn:oasis:names:tc:SAML:2.0:nameid-format:transient MIIEDjCCAvagAwIBAgIBADANBgkqhkiG9w0BAQUFADBnMQswCQYDVQQGEwJVUzEV MBMGA1UECBMMUGVubnN5bHZhbmlhMRMwEQYDVQQHEwpQaXR0c2J1cmdoMREwDwYD VQQKEwhUZXN0U2hpYjEZMBcGA1UEAxMQaWRwLnRlc3RzaGliLm9yZzAeFw0wNjA4 MzAyMTEyMjVaFw0xNjA4MjcyMTEyMjVaMGcxCzAJBgNVBAYTAlVTMRUwEwYDVQQI EwxQZW5uc3lsdmFuaWExEzARBgNVBAcTClBpdHRzYnVyZ2gxETAPBgNVBAoTCFRl c3RTaGliMRkwFwYDVQQDExBpZHAudGVzdHNoaWIub3JnMIIBIjANBgkqhkiG9w0B AQEFAAOCAQ8AMIIBCgKCAQEArYkCGuTmJp9eAOSGHwRJo1SNatB5ZOKqDM9ysg7C yVTDClcpu93gSP10nH4gkCZOlnESNgttg0r+MqL8tfJC6ybddEFB3YBo8PZajKSe 3OQ01Ow3yT4I+Wdg1tsTpSge9gEz7SrC07EkYmHuPtd71CHiUaCWDv+xVfUQX0aT NPFmDixzUjoYzbGDrtAyCqA8f9CN2txIfJnpHE6q6CmKcoLADS4UrNPlhHSzd614 kR/JYiks0K4kbRqCQF0Dv0P5Di+rEfefC6glV8ysC8dB5/9nb0yh/ojRuJGmgMWH gWk6h0ihjihqiu4jACovUZ7vVOCgSE5Ipn7OIwqd93zp2wIDAQABo4HEMIHBMB0G A1UdDgQWBBSsBQ869nh83KqZr5jArr4/7b+QazCBkQYDVR0jBIGJMIGGgBSsBQ86 9nh83KqZr5jArr4/7b+Qa6FrpGkwZzELMAkGA1UEBhMCVVMxFTATBgNVBAgTDFBl bm5zeWx2YW5pYTETMBEGA1UEBxMKUGl0dHNidXJnaDERMA8GA1UEChMIVGVzdFNo aWIxGTAXBgNVBAMTEGlkcC50ZXN0c2hpYi5vcmeCAQAwDAYDVR0TBAUwAwEB/zAN BgkqhkiG9w0BAQUFAAOCAQEAjR29PhrCbk8qLN5MFfSVk98t3CT9jHZoYxd8QMRL I4j7iYQxXiGJTT1FXs1nd4Rha9un+LqTfeMMYqISdDDI6tv8iNpkOAvZZUosVkUo 93pv1T0RPz35hcHHYq2yee59HJOco2bFlcsH8JBXRSRrJ3Q7Eut+z9uo80JdGNJ4 /SJy5UorZ8KazGj16lfJhOBXldgrhppQBb0Nq6HKHguqmwRfJ+WkxemZXzhediAj Geka8nz8JjwxpUjAiSWYKLtJhGEaTqCYxCCX2Dw+dOTqUzHOZ7WKv4JXPK5G/Uhr 8K/qhmFT2nIQi538n6rVYLeWj8Bbnl+ev0peYzxFyF5sQA== urn:mace:shibboleth:1.0:nameIdentifier urn:oasis:names:tc:SAML:2.0:nameid-format:transient TestShib Two Identity Provider TestShib Two http://www.testshib.org/testshib-two/ Nate Klingenstein ndk@internet2.edu saml-0.4.6/samlsp/testdata/key.pem000066400000000000000000000015731415467341100170740ustar00rootroot00000000000000-----BEGIN RSA PRIVATE KEY----- MIICXgIBAAKBgQDU8wdiaFmPfTyRYuFlVPi866WrH/2JubkHzp89bBQopDaLXYxi 3PTu3O6Q/KaKxMOFBqrInwqpv/omOGZ4ycQ51O9I+Yc7ybVlW94lTo2gpGf+Y/8E PsVbnZaFutRctJ4dVIp9aQ2TpLiGT0xX1OzBO/JEgq9GzDRf+B+eqSuglwIDAQAB AoGBAMuy1eN6cgFiCOgBsB3gVDdTKpww87Qk5ivjqEt28SmXO13A1KNVPS6oQ8SJ CT5Azc6X/BIAoJCURVL+LHdqebogKljhH/3yIel1kH19vr4E2kTM/tYH+qj8afUS JEmArUzsmmK8ccuNqBcllqdwCZjxL4CHDUmyRudFcHVX9oyhAkEA/OV1OkjM3CLU N3sqELdMmHq5QZCUihBmk3/N5OvGdqAFGBlEeewlepEVxkh7JnaNXAXrKHRVu/f/ fbCQxH+qrwJBANeQERF97b9Sibp9xgolb749UWNlAdqmEpmlvmS202TdcaaT1msU 4rRLiQN3X9O9mq4LZMSVethrQAdX1whawpkCQQDk1yGf7xZpMJ8F4U5sN+F4rLyM Rq8Sy8p2OBTwzCUXXK+fYeXjybsUUMr6VMYTRP2fQr/LKJIX+E5ZxvcIyFmDAkEA yfjNVUNVaIbQTzEbRlRvT6MqR+PTCefC072NF9aJWR93JimspGZMR7viY6IM4lrr vBkm0F5yXKaYtoiiDMzlOQJADqmEwXl0D72ZG/2KDg8b4QZEmC9i5gidpQwJXUc6 hU+IVQoLxRq0fBib/36K9tcrrO5Ba4iEvDcNY+D8yGbUtA== -----END RSA PRIVATE KEY----- saml-0.4.6/samlsp/testdata/saml_response.xml000066400000000000000000000312321415467341100211700ustar00rootroot00000000000000https://idp.testshib.org/idp/shibbolethMIIB7zCCAVgCCQDFzbKIp7b3MTANBgkqhkiG9w0BAQUFADA8MQswCQYDVQQGEwJVUzELMAkGA1UE CAwCR0ExDDAKBgNVBAoMA2ZvbzESMBAGA1UEAwwJbG9jYWxob3N0MB4XDTEzMTAwMjAwMDg1MVoX DTE0MTAwMjAwMDg1MVowPDELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkdBMQwwCgYDVQQKDANmb28x EjAQBgNVBAMMCWxvY2FsaG9zdDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA1PMHYmhZj308 kWLhZVT4vOulqx/9ibm5B86fPWwUKKQ2i12MYtz07tzukPymisTDhQaqyJ8Kqb/6JjhmeMnEOdTv SPmHO8m1ZVveJU6NoKRn/mP/BD7FW52WhbrUXLSeHVSKfWkNk6S4hk9MV9TswTvyRIKvRsw0X/gf nqkroJcCAwEAATANBgkqhkiG9w0BAQUFAAOBgQCMMlIO+GNcGekevKgkakpMdAqJfs24maGb90Dv TLbRZRD7Xvn1MnVBBS9hzlXiFLYOInXACMW5gcoRFfeTQLSouMM8o57h0uKjfTmuoWHLQLi6hnF+ cvCsEFiJZ4AbF+DgmO6TarJ8O05t8zvnOwJlNCASPZRH/JmF8tX0hoHuAQ==i/wh2ubXbhTH5W3hwc5VEf4DH1xifeTuxoe64ULopGJ0M0XxBKgDEIfTg59JUMmDYB4L8UStTFfqJk9BRGcMeYWVfckn5gCwLptD9cz26irw+7Ud7MIorA7z68v8rEyzwagKjz8VKvX1afgec0wobVTNN3M1Bn+SOyMhAu+Z4tE=a6PZohc8i16b2HG5irLqbzAt8zMI6OAjBprhcDb+w6zvjU2Pi9KgGRBAESLKmVfBR0Nf6C/cjozCGyelfVMtx9toIV1C3jtanoI45hq2EZZVprKMKGdCsAbXbhwYrd06QyGYvLjTn9iqako6+ifxtoFHJOkhMQShDMv8l3p5n36iFrJ4kUT3pSOIl4a479INcayp2B4u9MVJybvN7iqp/5dMEG5ZLRCmtczfo6NsUmu+bmT7O/Xs0XeDmqICrfI3TTLzKSOb8r0iZOaii5qjfTALDQ10hlqxV4fgd51FFGG7eHr+HHD+FT6Q9vhNjKd+4UVT2LZlaEiMw888vyBKtfl6gTsuJbln0fHRPmOGYeoJlAdfpukhxqTbgdzOke2NY5VLw72ieUWREAEdVXBolrzbSaafumQGuW7c8cjLCDPOlaYIvWsQzQOp5uL5mw4y4S7yNPtTAa5czcf+xgw4MGatcWeDFv0gMTlnBAGIT+QNLK/+idRSpnYwjPO407UNNa2HSX3QpZsutbxyskqvuMgp08DcI2+7+NrTXtQjR5knhCwRNkGTOqVxEBD6uExSjbLBbFmd4jgKn73SqHStk0wCkKatxbZMD8YosTu9mrU2wuWacZ1GFRMlk28oaeXl9qUDnqBwZ5EoxT/jDjWIMWw9b40InvZK6kKzn+v3BSGKqzq2Ecj9yxE7u5/51NC+tFyZiN2J9Lu9yehvW46xRrqFWqCyioFza5bw1yd3bzkuMMpd6UvsZPHKvWwap3+O6ngc8bMBBCLltJVOaTn/cBGsUvoARY6Rfftsx7BamrfGURd8vqq+AI6Z1OC8N3bcRCymIzw0nXdbUSqhKWwbw6P2szvAB6kCdu4+C3Bo01CEQyerCCbpfn/cZ+rPsBVlGdBOLl5eCW8oJOODruYgSRshrTnDffLQprxCddj7vSnFbVHirU8a0KwpCVCdAAL9nKppTHs0Mq2YaiMDo8mFvx+3kan/IBnJSOVL19vdLfHDbZqVh7UVFtiuWv3T15BoiefDdF/aR5joN0zRWf8l6IYcjBOskk/xgxOZhZzbJl8DcgTawD8giJ31SJ1NoOqgrSD4wBHGON4mInHkO0X5+vw1jVNPGF3BwHw0kxoCT3ZKdSsi8O4tlf1y227cf794AGnyQe13O032jYgOmM5qNkET6PyfkyD/h0ufgQq2vJvxSOiRv76Kdg0SeRuNPW9MyjO/5APHl7tBlDBEVq+LWDHl4g9h/bw+Fsi0WN4pLN1Yv9RANWpIsXWyvxTWIZHTuZEjNbHqFKpsefx/oY1b9cSzKR5fQ9vc32e17WykL0O7pwpzV6TrFN874GdmW5lG5zfqnRHUQh1aV2WwBJ74mB4tv/y5rmRjTe5h/rN90kN+eQGeR3eG7XUHLhK/yCV+xq8KKPxNZexcdHGA905rvYokbtmr/jIN5kAMBdlOU8akPAZdSMMh+g/RZo5MO50/gdg6MTpB4onU2FBd54FNDp2fuBUxBsnTqpZXkDcAPEfSBr+z2l8jTRmxMricWyeC55ILgxM4er68n0xYjwb2jyQum3IQq7TSYYU/qjNiH1fQBtdRmBkzXJYYk+9q7C6OZJUdR96ERnTIi93NaYmtpSEvZU9vS6MV1VBOnEf8UzUUT9ibMpP9XDSINX7dN24rKIufSY+3+70orQB07XOWp6++SWKgA+WThaoPhp8sWWMeSZuda/wq6jdVTAB8FOPiP3lNl0BqxagQEPmNxDWXwTplSFSR3SP0e4sHMSjLvysibV9Z87LZa1FG0cWU2hrhiyOLsIWMnd4vdTLaWjhXuGlrDShxSAiI39wsl5RB59E+DXVSTBQAoAkHCKGK69YiMKU9K8K/LeodApgw46oPL08EWvleKPCbdTyjKUADtxfAujR84GMEUz9Aml4Q497MfvABQOW6Hwg54Z3UbwLczDCOZyK1wIwZTyS9w3eTH/6EBeyzhtt4G2e/60jkywHOKn17wQgww2ZsDcukdsCMfo4FV0NzfhSER8BdL+hdLJS3R1F/Vf4aRBEuOuycv2AqB1ZqHhcjZh7yDv0RpBvn3+2rzfzmYIBlqL16d1aBnvL4C03I0J59AtXN9WlfJ8SlJhrduW/PF4pSCAQEyHGprP9hVhaXCOUuXCbjA2FI57NkxALQ2HpCVpXKGw0qO0rYxRYIRlKTl43VFcrSGJdVYOFUk0ZV3b+k+KoxLVSgBjIUWxio/tvVgUYDZsO3M3x0I+0r9xlWZSFFmhwdOFouD+Xy1NPTmgwlUXqZ4peyIE1oVntpcrTJuev2jNScXbU9PG8b589GM4Z09KS/fAyytTFKmUpBuTme969qu0eA7/kBSHAkKvbfj0hsrbkkF9y/rXi8xgcMXNgYayW8MHEhm506AyPIvJAreZL637/BENO1ABdWS1Enj/uGaLM1ED8UY94boh/lMhqa9jALgEOHHxspavexi3HIFwJ55s4ocQnjb4p6op4CRPUdPCfli5st9m3NtQoH9kT1FTRZa9sG8Ybhey5wP17YgPIg9ZZtvlvpSTwCwZxHZ348wXJWhbtId9DyOcIzsyK5HaJcRsp8SQVR5nbRW0pUyC/bFAtX1KOGJmtro/QfmnLG9ksuaZvxP6+bH1K+CibEFIRDllAUFFPiuT+2b3Yp3Tu1VvXokMAgmcB5iFDgTAglw5meJYJ99uIBmj0EVZm8snMhRrHjMPTAYD5kwPK/YDShPFFV3XEIFzLD3iYrzb7sub/Z4gTTELWzzS3bCpYPAh4KWeTih+p7Xj0Xf04nSONHZXsQnNenc+PNae+Zj5iCfJ/PpqhMn61n/YBP7gipYYEtOZYzDtvMz+mytYRUOaZTq3W4Wp64f+XVekn49CLarLm6qPyiz5kJwaT8lJ+VEZDPpS/ChLM4eq90GogJBvK0jxmQ1AGvnKpV2lw9XCudf3PXbaTb+r2QPcihKnmqcEgPgYlN8VLclicNW1WyjBJ+HvDTQPbs1r1/KnBK4O5HTT6ehuHpJsYlBN9vzjsD+ov6SRkBqiGPUg9CoKKmWS6dirxwOXi3OUFzkWFVDyDezfkJAzqkmG0nlEGb9mTHdVDfX010bPJ4ZQzQSyHp7Ht2mATyQwOEem2AMB/RpNwlOKXWIdsQ5p3dHF+kmsJHI8xjEv2GeUa/aXX3MF3fPfUA7La8J8fbnaDLbnEqMCLMfdfc9+kY7EKyqPiE5KFpF0EhQBrHl8SiPuFQCoxvlH2u+ujncW7Z5JiBmMKUWOXUHhIe4NckP1awRsEcfhEs664DqOp9CbLwTXk71hHVBtINylFcf7uBZwjxNW+hCfZEoVEjjs/V4J9QeXCxpTu5TcXxBxwN5zBdkCodNFPLUg+3UicaykaH0+wrGoTu/ugjF9rz7OezMMs3pep+bzLp+yZbFAL/z/yATY3UG+lpk6Rw4SkjbnAxBSedaEdqbotddkGzVQubHvHqCiKpkAw58rAa2v15hc+UmkrRFslS8SYxTIPXs2sTNhnCCrUn8nlKufeoAm65vgYtEQ4NzmG9tqKtTeBfZAvSToYaiQq+kPii1ssuu1OULAVuSx8x/CYO6orgX7h5wI0R/Ug1nux7cb2/+pFLbNyGvwKf1TLym2NvFMJpvFlTsOJJ4DxXM/v2JkC9umm93quXLsojx7KTEOFDQLsnMKsVo6ZzRQidEwK5gQPyZL1yjGirJcEuGMAEf6LA2AsKIIZhsMEPlLpzMiVo5Y0LoL6NFsXigceLaaJMEMuYNJJdh+uxyfW57+PoQ7V8KkzSHFsKan14GnpWeOV7r13uopwCPeIsEKUVG77ypd+ILQkbKxH2lQdsFyjpofqkbgEVM5XAnVbdhfwyebNHn5OJtadVkOMcJc/WMWJef1idcSfvP5ENkwp3pKg9Ljoi+hU2Chp1vTmksO2HJt0of4QnQ8jGlcqnOrAMiWUCd2W/8AmhRBjevt3UqxnqELVvg+HJPlyqFyuUlDxx25mXEdW0COpA3s9OlSgcMjvQbIJ42NUhGFZLoK1pvPLZo711w2Ex3Lm5qqcr/7I4+vTntd/Id5aJiP18LQpslTy614Wd4eD8+RfjEtmDAPXhgvfekVkS/rDnI/9H0k3AdHc78fJCJRPNwJrDTozzjxTvmVv9r4MtpoDELmnMxb3o7ZibUMxgptCTyDF+Q5m6T3GeD9G5ehgB3Tqsx3gcUGuDtP6KIqMGbj8YCFt8tjihDctYFAXj4AwPnIjMiI4T7skXwfrBLWCKfN1j5XrIn2paQgKln9hvaiRUpNpD3IXVyFl1WNrb21IcRinfkuCtrP2tTHqct6eSEh8sOzRkvZEArBQYD5paYyuNBcbVtsnl6PNE+DIcSIGvCVnzpMw1BeUExvQZoNdpHwhTQ3FSd1XN1nt0EWx6lve0Azl/zJBhj5hTdCd2RHdJWDtCZdOwWy/G+4dx3hEed0x6SoopOYdt5bq3lW+Ol0mbRzr1QJnuvt8FYjIfL8cIBqidkTpDjyh6V88yg1DNHDOBBqUz8IqOJ//vY0bmQMJp9gb+05UDW7u/Oe4gGIODQlswv534KF2DcaXW9OB7JQyl6f5+O8W6+zBYZ6DAL+J2vtf3CWKSZFomTwu65vrVaLRmTXIIBjQmZEUxWVeC4xN+4Cj5ORvO8GwzoePGDvqwKzrKoupSjqkL5eKqMpCLouOn8n/x5UWtHQS1NlKgMDFhRObzKMqQhS1S4mz84F3L492GFAlie0xRhywnF+FvAkm+ZIRO0UqM4IwvUXdlqTajjmUz2T0+eXKTKTR5UoNRgP51gdUMT5A4ggT5wU9WkRx7CR9KdWJwwcWzv2YrchoHIXBidQSk+f1ZSzqR7krKSOwFTVJUvEenU17qVaHoAf2he0dMgURJ8PM9JxnSr7p2pZeNPu/O5oPmLuOCmEPVRPSahJL7yj9PK5z3q57e5POIp/wXqFoniFdxRmtmpfZBxoKVlADkwRy34h8k6ZmgtqPTQfUUk/+yH2CAoQu+HyOtUnQof8vc1k4zs8nCTrCSjqvFPjU8mHtVHy1RY0qmK9t99ugXyAKaGON3PlseetIC8WCTt84nM5XGD3VQpbv139yhSPhp2Oiz0IiOsr+L9idVKSvfNSkdNq9aUC7963uAQNud8c4GuDmbENvZYvGNIMxxZhYA86n1RMNtGDZJs6/4hZTL18Kz1yCY9zbbSXTxWTmkaHJziHtgrEPoYpUeb85J229PDEX08yHOkj2HXVdnKKmEaHw3VkB4eM3PhGGdrw2CSUejSaqPQFLdhabcB2zdB4lj/AUnZvNaJc23nHHIauHnhhVrxh/KQ1H4YaYKT9ji/69BIfrTgvoGaPZC10pQKinBHEPMXoFrCd1RX1vutnXXcyT2KTBP4GG+Or0j6Sqxtp5WhxR0aJqIKM6LqMHtTooI0QhWbmSqDEBX/wRS70csVeJSrZ4dqRKit+hz8OalHA7At9e+7gSWTfHAwjl5JhtrltyAab/FII4yKQeZWG8j1fSFGHN+EbOrum2uWuVhxkUPy4coMu+yKY4GxlXfvP+yEVK5GrMECRmFBlySetJK3JOoQXiuLirlHUq+0u88QFMdAJ9+fIdU4+FxneqgW7qM7CHRE8jV4pPSWGFbGzxVZ9CWRWaYIw26VsC1qQJe1WmU7Mrp26IxmWHGwHvZ50uB0mjAHFCiln5QAvqTm2/fsY+Puk+Irt3LQbMwGVWPnb4eona2dSha+eMLOiAQkBvbaitsRqqrAVnndP7gHmO+nYZEKNx/740zTRrFBpOelrGdOa0/eV2mPhUQfozGooxoRADmT8fAcDXo0SsXCHzg9tBnmVMvInQ7+8nXfhcF/fEBjvW3gIWOmp2EWutHQ/sl73MieJWnP/n3DMk2HHcatoIZOMUzo4S4uztODHoSiOJDA1hVj7qADvKB37/OX0opnbii9o6W8naFkWG5Ie7+EWQZdo+xeVYpwGOzcNwDRrxbZpV3fTvWyWKToovncZq+TQj7c4Yhz6XDF0ffljN5hTm4ONwYViFNB4gTJlFxFX00wcWfwWah4uJs2Oa8dHPVT+7viagZiPrSDk/gythdY8glGm+F0DWlzQpWbgSI3ZbdiUQ+ox4GtLUtYgGIQFUvRYbuHqH6CXQ3SM6vkbhV/nAn6UDEWKXdJsO0u5q6UpXci7MlWDNLxoQ9dfGjSc28mX+q+4hkyho4u1XSMy9B6IdH304J7fuAQ88tTorT67AiqvqR6qnZ0icV+MMLh95moxFbrvch6sGAmMEixqeujmiZzBqBmNbzZVORiv9qcbe3CQ6X2i+9D8hMpaWj5jI0u+0wk3bRFK4uDn8T1mnD6l4TrJayf3cZI+duhKcabNj71i5w76S8RZSC6RX4ks0x+XIDc5v3223NmGvceYklbuOJtJa0/MBTOcSDKCM2kUXqPV2BlA9Za8WEO2UrdcyP+AXgM20af3thjlZvA494zdZ0mqjrsKp+VS2MVrBBtj+puSuSHJYf6bnA5/yjqQtbGvAp8hfXQURC53J5oD8rb9F7vQRqdfqpe6xd7DVd+wWZS86mWjyZYKXw312t8nM/gxo0pdvZ8F0x9y3xb9UBM2pZtdYvk3hPz6swhuE1N5j2u7nwtXuEDNcGCSfr+IempeFHFRqO8n8ikASEdKcq2XHGJwfc3lVXOQ5K4JlewcC7yQL1uNtL6iNKCtJmjJiH2PMmXrtpmCeTspFNZlwmiICyPWV9B5ce9H/qP1xjndBzFz0rn75SGDnWUhNZI/aYKNVyzkOleS5VSNxBx1hoiFuG8r+6ctYwF7XL94b95tXQ/+0V5dt0H1xVaOZ7QluoDtMSzuUjV4yUoQESa3zCfZwnW+b5SKndX5nx0GYrVxydMkUdfimZpX/fezcMiaAGwG/jgWF0zS+EL4T7gR8I5R3qUNTifKFJKJL1+AL8CgL+SRB1lgHDp2wQ7cqgqcmskAsT60qisL/UZGgmnlgZ8FkNhv0vAMkzIsz7o6cuLo15hZnrsZveIo+mZKY2cMJjJb4ZlJLcE+YcnpiM84OYjypa9lA7kv4XJaDX9oirhsl9IO/ImbFgYpR73y+xSolXYdDKfZjf/8NR7vE8fu+LYXGoZHO/hxousED6y3sCo/ItECYHWYIui+V5SmAoEvVV8FY8fFMYIc+Llc2CoX5HQISfUAtLu+fGNNV0muidXnBdtnJo25UEqxwvoENdI1lGPhlrXY6/h4kIT5djmsxxSG/EgG/4fPnrThgF9/fbG8n/3LweXvQOGjX0F1Ngt5wuMIWRQk5vtLdvv2M+BNwthHZ7xzIU7zqSVvngVPwgcsTr2d5pTVOxauT1K6ffiBF04jVZEcna+NXhJM5EcRHNuT/iOb0ncn1yuKU8JJnztEzMDjO1qCmaBTyWBR7nQS6K+nfstd/AnBWyGeC5Yi3wlvZAVMpc0m7I7McXb+rXiHM0mHoq0Z/2HOki5LP2cBuIkk84tJ3SRZwWnocrz4aTEIOmwftqMATy5Ur0KRxoUSFNMJYyc1iOfjk3H2JjgecWlQdYHcIEjxGDGeo4S9EKTRokMGNUN2nTj3SO2nHoWbx9WhGe6uB3OgDENGL9aNoPnYKXs4WcobctMxQjjBWa/zpCFwP8nr78xIFfy/64ZtsFBrxSrEHxeXiPa2Kpv456aQ9kDQjJt9XrWKe+JBawtpPUYHmWkUb3Gznp3tC2LbowvJlEe/17srb5yi+sUHEF1z/8Uk4eVYcUUXzyq3YEuqumIBIYqO8J3K5Us7tEXyzhHH8TMLNSQxmDi/w5oYccIwNFMM1+xRTsyjHHtB/rHYJjPW/50Xxb0CZF84NqotCcgIMrR4nUiPnAPd8ZvHeB/235gS1NtzBWtfcDmP8khibSQpY3JW+fdY/9W6iGlPyPIwOgH06fJayaT44sPFIm+QGIkPKSAJOFDeJNG8oc6SAqrYSfCffYfOAx3IsjSdnxQy9JAcS0HxjWnEO3rgSh7bNEecO3f4hb3TRNlczdzhfrwgxUZ0rURI3LfMCpGntF+8NrhtB7RT8sEOaa4NM13T7LWjykRQJFYKNZY0siPBP2WJxjBqL0KynlTPhAcfFyiLZbAhe7YC0XmYo8iJQqdzJQwBK9iOoDkg1XuGy7+Kfe0scamvHN2Z85umcPSiPEQRP3zAWcP5kRNDath7DKrBfQtvOJvEHiihE+qiASrCZep+m7jTD261U9vQGAnR4xBY08ChSh8XItWHvDHARN+GP08h9u6nlJ3rpOoVn9y22NNgx7bOe6QIYe9f6iYbbAzLR1/7AP1A4CQwFi39eZI9BZteze5eas+6JR2s1LqH9tncOmWAhXjE8p3hOtplh/tMbrx+pySNX4BKfZva54zccIa+e59NUifTRsq27AwAtcxg2Bk1Tu7B+LT9Yw2K8tRH6XTcGlvqDM4sYjNBqzh3yAga5iro706tg/Qaa50eln8rjISularEHlfaggogjvd+wNLg44Rj8pMr25+xxS0e9KoEGon5SutuhJ/HBGnEj3+4qNxHu27nkAmZIADiF+Jh53osDuA1fsUnRXf2lJABa30KDkG8E/eci+TkESrdfsPMo6yhWoyjtjYdJbGkjtsQCMW5DOSNYDH0FqDiiVU0nBLJ4+A4ep6aWTrv6w/ozuO4educ7x9IBpGmEY30rsXWwiGJbLGyIo+6qz6J5JBKdjNBsDO7RRweDNMp8ospaGNQSa4NKAHTG8BsGqJSP8oebpVqYpgPS1TiBWnYZKQSRJ5NFs+ULpdICekxevVXAH8uh+De9GT7KsJJzg0CFjALDbC0YrbmCigspJAh2455I6/xyWbPXCYMXwBzbioMgWcNhQBJJ6oIoQ7shwf2TP0Z+X/3NoMpWHmGpoV/JZind8lb9lcxoI44uf37+xc03O1R1bNucf0F5ljrgj2sZlGz/591EJen5GZhrT6qSTIcMu+xIyxyA/zzhy0jjkVfkDKfQ8mE9AmVtbbzHAQNy2PhDIeu7ngoFN635tSOJLR2c6pC/m6n50slFbo0oeHbbiGHyxDk7q3zXHWoHzeF1k4iVdHumYg/nwZOuRzms6rvkmwkJv59Z1p05jxA+Y0yHvDeq1WR8PfS/esm3RHfP3fM+zTlj9ZBJfzvn4OL+IIHRQ5l8pGKAeRL58OjeaU5QU98lAKHydOPDGBalsEHyIKD6iy3RZ65qIm956zQd98htZ1Vgkd7LVC7LSnLb9jRbqS1vHN7lR6bQMmXtQBYSA/+ZW2RQqSo7sToVh+Pxl3EVmsgyO8dXPL4biz7XM8eVz7CqHkrQUinnr79HJWC6Uk19cBurOD6PeOqNYy08Og/A0hbHOgN3dKmVRAPf7itK6x0eb5F70T2zVqG12GHVZieXwIcp/vahuFvriHLJtuM04laiRWNXSiL2MPHQ8e9rr8NIlWDm9uev55FI9zZxwFUPBSewawPe5vkqRLfwZCYd5mZoxtBhNBWvY3ZOVD/21dIUlQanG1n6RygbmAwCHnIB4c7EH2CBYEMDToRQuAuIssviIfdaJglwDgHbLWKNUVDOdqeclBNZjfQfVXbVukPk8DfWLqj9pD4xAOzDeVQcdmg2aLvNKgpZsWs4d+6GlKrpS7qEGvoBkIFh/cVY7DMYrt/JXYuF6DpwB+HbfnuDFc2p47SPNhnmt/ez6/DACBPQ+tgpyWYXUsiviGSp72JNTzd8uFJJZNeKUJZw1c0UTjxdwigh5tL/hWhPl48DY937zymSr1xVqC3RV6wSIpuplH+hss/rsRPAp1/TfxvhJuFsoPbW0586y9YzqEHT4FUu6WSRy0gMJLP2sLqiiZXZ6kPicXsW7M55mV3ugbGQjB7YS7EVqsQzvJTiQbOlcPqwoKK7DTqaeCOXd8kH1tNoe7hjx/UNNdLQQ7IhrJIzxqTTgwcXYMCxhoezDsIHReTIymsHPkCurfteTQcbfwoKN5E9zC2hINOPmhAxLvONzaLXQGMqofuTbFshkB4eUj8U4vBCNp+60iCLnibt4rPuyoWKEHWBYa6FfIykxVKuXkfcb64dCdGCWjv7x1XqkbpHxQB80qhipoSo244pyhIsN91ASu1Q7L75LxGXibY3jb0Y4KZ5zIWsH4kVlvPhangohDO1J9gmL9inGr9hy5BHTQiMcktGoUgOIbFJ72381vYpPxn3ngBbp48mVZd0w6xV8RBaqR3l7CxI9vvMAPYPoXBB18ERoZypza8mAlzv2QxIkNGuRzFENh1SXegBfN7eiazZnwnhbyeMghJpnXzfvHACyjkdH3shRYcJ+oMiOSpInGxm/hxFQxHJZA0Ft/lzasaml-0.4.6/samlsp/testdata/testshib_metadata.xml000066400000000000000000000627441415467341100220170ustar00rootroot00000000000000 testshib.org TestShib Test IdP TestShib IdP. Use this as a source of attributes for your test SP. https://www.testshib.org/testshibtwo.jpg MIIDAzCCAeugAwIBAgIVAPX0G6LuoXnKS0Muei006mVSBXbvMA0GCSqGSIb3DQEB CwUAMBsxGTAXBgNVBAMMEGlkcC50ZXN0c2hpYi5vcmcwHhcNMTYwODIzMjEyMDU0 WhcNMzYwODIzMjEyMDU0WjAbMRkwFwYDVQQDDBBpZHAudGVzdHNoaWIub3JnMIIB IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAg9C4J2DiRTEhJAWzPt1S3ryh m3M2P3hPpwJwvt2q948vdTUxhhvNMuc3M3S4WNh6JYBs53R+YmjqJAII4ShMGNEm lGnSVfHorex7IxikpuDPKV3SNf28mCAZbQrX+hWA+ann/uifVzqXktOjs6DdzdBn xoVhniXgC8WCJwKcx6JO/hHsH1rG/0DSDeZFpTTcZHj4S9MlLNUtt5JxRzV/MmmB 3ObaX0CMqsSWUOQeE4nylSlp5RWHCnx70cs9kwz5WrflnbnzCeHU2sdbNotBEeTH ot6a2cj/pXlRJIgPsrL/4VSicPZcGYMJMPoLTJ8mdy6mpR6nbCmP7dVbCIm/DQID AQABoz4wPDAdBgNVHQ4EFgQUUfaDa2mPi24x09yWp1OFXmZ2GPswGwYDVR0RBBQw EoIQaWRwLnRlc3RzaGliLm9yZzANBgkqhkiG9w0BAQsFAAOCAQEASKKgqTxhqBzR OZ1eVy++si+eTTUQZU4+8UywSKLia2RattaAPMAcXUjO+3cYOQXLVASdlJtt+8QP dRkfp8SiJemHPXC8BES83pogJPYEGJsKo19l4XFJHPnPy+Dsn3mlJyOfAa8RyWBS 80u5lrvAcr2TJXt9fXgkYs7BOCigxtZoR8flceGRlAZ4p5FPPxQR6NDYb645jtOT MVr3zgfjP6Wh2dt+2p04LG7ENJn8/gEwtXVuXCsPoSCDx9Y0QmyXTJNdV1aB0AhO RkWPlFYwp+zOyOIR+3m1+pqWFpn0eT/HrxpdKa74FA3R2kq4R7dXe4G0kUgXTdqX MLRKhDgdmA== urn:mace:shibboleth:1.0:nameIdentifier urn:oasis:names:tc:SAML:2.0:nameid-format:transient MIIDAzCCAeugAwIBAgIVAPX0G6LuoXnKS0Muei006mVSBXbvMA0GCSqGSIb3DQEB CwUAMBsxGTAXBgNVBAMMEGlkcC50ZXN0c2hpYi5vcmcwHhcNMTYwODIzMjEyMDU0 WhcNMzYwODIzMjEyMDU0WjAbMRkwFwYDVQQDDBBpZHAudGVzdHNoaWIub3JnMIIB IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAg9C4J2DiRTEhJAWzPt1S3ryh m3M2P3hPpwJwvt2q948vdTUxhhvNMuc3M3S4WNh6JYBs53R+YmjqJAII4ShMGNEm lGnSVfHorex7IxikpuDPKV3SNf28mCAZbQrX+hWA+ann/uifVzqXktOjs6DdzdBn xoVhniXgC8WCJwKcx6JO/hHsH1rG/0DSDeZFpTTcZHj4S9MlLNUtt5JxRzV/MmmB 3ObaX0CMqsSWUOQeE4nylSlp5RWHCnx70cs9kwz5WrflnbnzCeHU2sdbNotBEeTH ot6a2cj/pXlRJIgPsrL/4VSicPZcGYMJMPoLTJ8mdy6mpR6nbCmP7dVbCIm/DQID AQABoz4wPDAdBgNVHQ4EFgQUUfaDa2mPi24x09yWp1OFXmZ2GPswGwYDVR0RBBQw EoIQaWRwLnRlc3RzaGliLm9yZzANBgkqhkiG9w0BAQsFAAOCAQEASKKgqTxhqBzR OZ1eVy++si+eTTUQZU4+8UywSKLia2RattaAPMAcXUjO+3cYOQXLVASdlJtt+8QP dRkfp8SiJemHPXC8BES83pogJPYEGJsKo19l4XFJHPnPy+Dsn3mlJyOfAa8RyWBS 80u5lrvAcr2TJXt9fXgkYs7BOCigxtZoR8flceGRlAZ4p5FPPxQR6NDYb645jtOT MVr3zgfjP6Wh2dt+2p04LG7ENJn8/gEwtXVuXCsPoSCDx9Y0QmyXTJNdV1aB0AhO RkWPlFYwp+zOyOIR+3m1+pqWFpn0eT/HrxpdKa74FA3R2kq4R7dXe4G0kUgXTdqX MLRKhDgdmA== urn:mace:shibboleth:1.0:nameIdentifier urn:oasis:names:tc:SAML:2.0:nameid-format:transient TestShib Two Identity Provider TestShib Two http://www.testshib.org/testshib-two/ Nate Klingenstein ndk@internet2.edu TestShib Test SP TestShib SP. Log into this to test your machine. Once logged in check that all attributes that you expected have been released. https://www.testshib.org/testshibtwo.jpg MIIEPjCCAyagAwIBAgIBADANBgkqhkiG9w0BAQUFADB3MQswCQYDVQQGEwJVUzEV MBMGA1UECBMMUGVubnN5bHZhbmlhMRMwEQYDVQQHEwpQaXR0c2J1cmdoMSIwIAYD VQQKExlUZXN0U2hpYiBTZXJ2aWNlIFByb3ZpZGVyMRgwFgYDVQQDEw9zcC50ZXN0 c2hpYi5vcmcwHhcNMDYwODMwMjEyNDM5WhcNMTYwODI3MjEyNDM5WjB3MQswCQYD VQQGEwJVUzEVMBMGA1UECBMMUGVubnN5bHZhbmlhMRMwEQYDVQQHEwpQaXR0c2J1 cmdoMSIwIAYDVQQKExlUZXN0U2hpYiBTZXJ2aWNlIFByb3ZpZGVyMRgwFgYDVQQD Ew9zcC50ZXN0c2hpYi5vcmcwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB AQDJyR6ZP6MXkQ9z6RRziT0AuCabDd3x1m7nLO9ZRPbr0v1LsU+nnC363jO8nGEq sqkgiZ/bSsO5lvjEt4ehff57ERio2Qk9cYw8XCgmYccVXKH9M+QVO1MQwErNobWb AjiVkuhWcwLWQwTDBowfKXI87SA7KR7sFUymNx5z1aoRvk3GM++tiPY6u4shy8c7 vpWbVfisfTfvef/y+galxjPUQYHmegu7vCbjYP3On0V7/Ivzr+r2aPhp8egxt00Q XpilNai12LBYV3Nv/lMsUzBeB7+CdXRVjZOHGuQ8mGqEbsj8MBXvcxIKbcpeK5Zi JCVXPfarzuriM1G5y5QkKW+LAgMBAAGjgdQwgdEwHQYDVR0OBBYEFKB6wPDxwYrY StNjU5P4b4AjBVQVMIGhBgNVHSMEgZkwgZaAFKB6wPDxwYrYStNjU5P4b4AjBVQV oXukeTB3MQswCQYDVQQGEwJVUzEVMBMGA1UECBMMUGVubnN5bHZhbmlhMRMwEQYD VQQHEwpQaXR0c2J1cmdoMSIwIAYDVQQKExlUZXN0U2hpYiBTZXJ2aWNlIFByb3Zp ZGVyMRgwFgYDVQQDEw9zcC50ZXN0c2hpYi5vcmeCAQAwDAYDVR0TBAUwAwEB/zAN BgkqhkiG9w0BAQUFAAOCAQEAc06Kgt7ZP6g2TIZgMbFxg6vKwvDL0+2dzF11Onpl 5sbtkPaNIcj24lQ4vajCrrGKdzHXo9m54BzrdRJ7xDYtw0dbu37l1IZVmiZr12eE Iay/5YMU+aWP1z70h867ZQ7/7Y4HW345rdiS6EW663oH732wSYNt9kr7/0Uer3KD 9CuPuOidBacospDaFyfsaJruE99Kd6Eu/w5KLAGG+m0iqENCziDGzVA47TngKz2v PVA+aokoOyoz3b53qeti77ijatSEoKjxheBWpO+eoJeGq/e49Um3M2ogIX/JAlMa Inh+vYSYngQB2sx9LGkR9KHaMKNIGCDehk93Xla4pWJx1w== urn:oasis:names:tc:SAML:2.0:nameid-format:transient urn:mace:shibboleth:1.0:nameIdentifier TestShib Two Service Provider TestShib Two http://www.testshib.org/testshib-two/ Nate Klingenstein ndk@internet2.edu saml-0.4.6/samlsp/testdata/token.json000066400000000000000000000015461415467341100176140ustar00rootroot00000000000000{ "aud": "https://15661444.ngrok.io/", "iss": "https://15661444.ngrok.io/", "exp": 1448942229, "iat": 1448935029, "nbf": 1448935029, "sub": "_41bd295976dadd70e1480f318e772841", "attr": { "SessionIndex": [ "_6149230ee8fb88d3635c238509d9a35a" ], "cn": [ "Me Myself And I" ], "eduPersonAffiliation": [ "Member", "Staff" ], "eduPersonEntitlement": [ "urn:mace:dir:entitlement:common-lib-terms" ], "eduPersonPrincipalName": [ "myself@testshib.org" ], "eduPersonScopedAffiliation": [ "Member@testshib.org", "Staff@testshib.org" ], "eduPersonTargetedID": [ "" ], "givenName": [ "Me Myself" ], "sn": [ "And I" ], "telephoneNumber": [ "555-5555" ], "uid": [ "myself" ] }, "saml-session": true }saml-0.4.6/samlsp/util.go000066400000000000000000000003211415467341100152620ustar00rootroot00000000000000package samlsp import ( "io" "github.com/crewjam/saml" ) func randomBytes(n int) []byte { rv := make([]byte, n) if _, err := io.ReadFull(saml.RandReader, rv); err != nil { panic(err) } return rv } saml-0.4.6/schema.go000066400000000000000000001252071415467341100142610ustar00rootroot00000000000000package saml import ( "bytes" "compress/flate" "encoding/xml" "strconv" "time" "github.com/beevik/etree" "github.com/russellhaering/goxmldsig/etreeutils" ) // AuthnRequest represents the SAML object of the same name, a request from a service provider // to authenticate a user. // // See http://docs.oasis-open.org/security/saml/v2.0/saml-core-2.0-os.pdf type AuthnRequest struct { XMLName xml.Name `xml:"urn:oasis:names:tc:SAML:2.0:protocol AuthnRequest"` ID string `xml:",attr"` Version string `xml:",attr"` IssueInstant time.Time `xml:",attr"` Destination string `xml:",attr"` Consent string `xml:",attr"` Issuer *Issuer `xml:"urn:oasis:names:tc:SAML:2.0:assertion Issuer"` Signature *etree.Element Subject *Subject NameIDPolicy *NameIDPolicy `xml:"urn:oasis:names:tc:SAML:2.0:protocol NameIDPolicy"` Conditions *Conditions //RequestedAuthnContext *RequestedAuthnContext // TODO //Scoping *Scoping // TODO ForceAuthn *bool `xml:",attr"` IsPassive *bool `xml:",attr"` AssertionConsumerServiceIndex string `xml:",attr"` AssertionConsumerServiceURL string `xml:",attr"` ProtocolBinding string `xml:",attr"` AttributeConsumingServiceIndex string `xml:",attr"` ProviderName string `xml:",attr"` } // LogoutRequest represents the SAML object of the same name, a request from an IDP // to destroy a user's session. // // See http://docs.oasis-open.org/security/saml/v2.0/saml-core-2.0-os.pdf type LogoutRequest struct { XMLName xml.Name `xml:"urn:oasis:names:tc:SAML:2.0:protocol LogoutRequest"` ID string `xml:",attr"` Version string `xml:",attr"` IssueInstant time.Time `xml:",attr"` NotOnOrAfter *time.Time `xml:",attr"` Destination string `xml:",attr"` Issuer *Issuer `xml:"urn:oasis:names:tc:SAML:2.0:assertion Issuer"` NameID *NameID Signature *etree.Element SessionIndex *SessionIndex `xml:"SessionIndex"` } // Element returns an etree.Element representing the object in XML form. func (r *LogoutRequest) Element() *etree.Element { el := etree.NewElement("samlp:LogoutRequest") el.CreateAttr("xmlns:saml", "urn:oasis:names:tc:SAML:2.0:assertion") el.CreateAttr("xmlns:samlp", "urn:oasis:names:tc:SAML:2.0:protocol") el.CreateAttr("ID", r.ID) el.CreateAttr("Version", r.Version) el.CreateAttr("IssueInstant", r.IssueInstant.Format(timeFormat)) if r.NotOnOrAfter != nil { el.CreateAttr("NotOnOrAfter", r.NotOnOrAfter.Format(timeFormat)) } if r.Destination != "" { el.CreateAttr("Destination", r.Destination) } if r.Issuer != nil { el.AddChild(r.Issuer.Element()) } if r.NameID != nil { el.AddChild(r.NameID.Element()) } if r.Signature != nil { el.AddChild(r.Signature) } if r.SessionIndex != nil { el.AddChild(r.SessionIndex.Element()) } return el } // MarshalXML implements xml.Marshaler func (r *LogoutRequest) MarshalXML(e *xml.Encoder, start xml.StartElement) error { type Alias LogoutRequest aux := &struct { IssueInstant RelaxedTime `xml:",attr"` NotOnOrAfter *RelaxedTime `xml:",attr"` *Alias }{ IssueInstant: RelaxedTime(r.IssueInstant), NotOnOrAfter: (*RelaxedTime)(r.NotOnOrAfter), Alias: (*Alias)(r), } return e.Encode(aux) } // UnmarshalXML implements xml.Unmarshaler func (r *LogoutRequest) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { type Alias LogoutRequest aux := &struct { IssueInstant RelaxedTime `xml:",attr"` NotOnOrAfter *RelaxedTime `xml:",attr"` *Alias }{ Alias: (*Alias)(r), } if err := d.DecodeElement(&aux, &start); err != nil { return err } r.IssueInstant = time.Time(aux.IssueInstant) r.NotOnOrAfter = (*time.Time)(aux.NotOnOrAfter) return nil } // Bytes returns a byte array representation of the LogoutRequest func (r *LogoutRequest) Bytes() ([]byte, error) { doc := etree.NewDocument() doc.SetRoot(r.Element()) buf, err := doc.WriteToBytes() if err != nil { return nil, err } return buf, nil } // Deflate returns a compressed byte array of the LogoutRequest func (r *LogoutRequest) Deflate() ([]byte, error) { buf, err := r.Bytes() if err != nil { return nil, err } var b bytes.Buffer writer, err := flate.NewWriter(&b, flate.DefaultCompression) if err != nil { return nil, err } if _, err := writer.Write(buf); err != nil { return nil, err } if err := writer.Close(); err != nil { return nil, err } return b.Bytes(), nil } // Element returns an etree.Element representing the object // Element returns an etree.Element representing the object in XML form. func (r *AuthnRequest) Element() *etree.Element { el := etree.NewElement("samlp:AuthnRequest") el.CreateAttr("xmlns:saml", "urn:oasis:names:tc:SAML:2.0:assertion") el.CreateAttr("xmlns:samlp", "urn:oasis:names:tc:SAML:2.0:protocol") el.CreateAttr("ID", r.ID) el.CreateAttr("Version", r.Version) el.CreateAttr("IssueInstant", r.IssueInstant.Format(timeFormat)) if r.Destination != "" { el.CreateAttr("Destination", r.Destination) } if r.Consent != "" { el.CreateAttr("Consent", r.Consent) } if r.Issuer != nil { el.AddChild(r.Issuer.Element()) } if r.Signature != nil { el.AddChild(r.Signature) } if r.Subject != nil { el.AddChild(r.Subject.Element()) } if r.NameIDPolicy != nil { el.AddChild(r.NameIDPolicy.Element()) } if r.Conditions != nil { el.AddChild(r.Conditions.Element()) } //if r.RequestedAuthnContext != nil { // el.AddChild(r.RequestedAuthnContext.Element()) //} //if r.Scoping != nil { // el.AddChild(r.Scoping.Element()) //} if r.ForceAuthn != nil { el.CreateAttr("ForceAuthn", strconv.FormatBool(*r.ForceAuthn)) } if r.IsPassive != nil { el.CreateAttr("IsPassive", strconv.FormatBool(*r.IsPassive)) } if r.AssertionConsumerServiceIndex != "" { el.CreateAttr("AssertionConsumerServiceIndex", r.AssertionConsumerServiceIndex) } if r.AssertionConsumerServiceURL != "" { el.CreateAttr("AssertionConsumerServiceURL", r.AssertionConsumerServiceURL) } if r.ProtocolBinding != "" { el.CreateAttr("ProtocolBinding", r.ProtocolBinding) } if r.AttributeConsumingServiceIndex != "" { el.CreateAttr("AttributeConsumingServiceIndex", r.AttributeConsumingServiceIndex) } if r.ProviderName != "" { el.CreateAttr("ProviderName", r.ProviderName) } return el } // MarshalXML implements xml.Marshaler func (r *AuthnRequest) MarshalXML(e *xml.Encoder, start xml.StartElement) error { type Alias AuthnRequest aux := &struct { IssueInstant RelaxedTime `xml:",attr"` *Alias }{ IssueInstant: RelaxedTime(r.IssueInstant), Alias: (*Alias)(r), } return e.Encode(aux) } // UnmarshalXML implements xml.Unmarshaler func (r *AuthnRequest) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { type Alias AuthnRequest aux := &struct { IssueInstant RelaxedTime `xml:",attr"` *Alias }{ Alias: (*Alias)(r), } if err := d.DecodeElement(&aux, &start); err != nil { return err } r.IssueInstant = time.Time(aux.IssueInstant) return nil } // Issuer represents the SAML object of the same name. // // See http://docs.oasis-open.org/security/saml/v2.0/saml-core-2.0-os.pdf type Issuer struct { XMLName xml.Name `xml:"urn:oasis:names:tc:SAML:2.0:assertion Issuer"` NameQualifier string `xml:",attr"` SPNameQualifier string `xml:",attr"` Format string `xml:",attr"` SPProvidedID string `xml:",attr"` Value string `xml:",chardata"` } // Element returns an etree.Element representing the object in XML form. func (a *Issuer) Element() *etree.Element { el := etree.NewElement("saml:Issuer") if a.NameQualifier != "" { el.CreateAttr("NameQualifier", a.NameQualifier) } if a.SPNameQualifier != "" { el.CreateAttr("SPNameQualifier", a.SPNameQualifier) } if a.Format != "" { el.CreateAttr("Format", a.Format) } if a.SPProvidedID != "" { el.CreateAttr("SPProvidedID", a.SPProvidedID) } el.SetText(a.Value) return el } // NameIDPolicy represents the SAML object of the same name. // // See http://docs.oasis-open.org/security/saml/v2.0/saml-core-2.0-os.pdf type NameIDPolicy struct { XMLName xml.Name `xml:"urn:oasis:names:tc:SAML:2.0:protocol NameIDPolicy"` Format *string `xml:",attr"` SPNameQualifier *string `xml:",attr"` AllowCreate *bool `xml:",attr"` } // Element returns an etree.Element representing the object in XML form. func (a *NameIDPolicy) Element() *etree.Element { el := etree.NewElement("samlp:NameIDPolicy") if a.Format != nil && *a.Format != "" { el.CreateAttr("Format", *a.Format) } if a.SPNameQualifier != nil { el.CreateAttr("SPNameQualifier", *a.SPNameQualifier) } if a.AllowCreate != nil { el.CreateAttr("AllowCreate", strconv.FormatBool(*a.AllowCreate)) } return el } // ArtifactResolve represents the SAML object of the same name. type ArtifactResolve struct { XMLName xml.Name `xml:"urn:oasis:names:tc:SAML:2.0:protocol ArtifactResponse"` ID string `xml:",attr"` Version string `xml:",attr"` IssueInstant time.Time `xml:",attr"` Issuer *Issuer `xml:"urn:oasis:names:tc:SAML:2.0:assertion Issuer"` Signature *etree.Element Artifact string `xml:"urn:oasis:names:tc:SAML:2.0:protocol Artifact"` } // Element returns an etree.Element representing the object in XML form. func (r *ArtifactResolve) Element() *etree.Element { el := etree.NewElement("samlp:ArtifactResolve") el.CreateAttr("xmlns:saml", "urn:oasis:names:tc:SAML:2.0:assertion") el.CreateAttr("xmlns:samlp", "urn:oasis:names:tc:SAML:2.0:protocol") // Note: This namespace is not used by any element or attribute name, but // is required so that the AttributeValue type element can have a value like // "xs:string". If we don't declare it here, then it will be stripped by the // cannonicalizer. This could be avoided by providing a prefix list to the // cannonicalizer, but prefix lists do not appear to be implemented correctly // in some libraries, so the safest action is to always produce XML that is // (a) in canonical form and (b) does not require prefix lists. el.CreateAttr("xmlns:xs", "http://www.w3.org/2001/XMLSchema") el.CreateAttr("ID", r.ID) el.CreateAttr("Version", r.Version) el.CreateAttr("IssueInstant", r.IssueInstant.Format(timeFormat)) if r.Issuer != nil { el.AddChild(r.Issuer.Element()) } artifact := etree.NewElement("samlp:Artifact") artifact.SetText(r.Artifact) el.AddChild(artifact) if r.Signature != nil { el.AddChild(r.Signature) } return el } // SoapRequest returns a SOAP Envelope contining the ArtifactResolve request func (r *ArtifactResolve) SoapRequest() *etree.Element { envelope := etree.NewElement("soapenv:Envelope") envelope.CreateAttr("xmlns:soapenv", "http://schemas.xmlsoap.org/soap/envelope/") envelope.CreateAttr("xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance") body := etree.NewElement("soapenv:Body") envelope.AddChild(body) body.AddChild(r.Element()) return envelope } // MarshalXML implements xml.Marshaler func (r *ArtifactResolve) MarshalXML(e *xml.Encoder, start xml.StartElement) error { type Alias ArtifactResolve aux := &struct { IssueInstant RelaxedTime `xml:",attr"` *Alias }{ IssueInstant: RelaxedTime(r.IssueInstant), Alias: (*Alias)(r), } return e.Encode(aux) } // UnmarshalXML implements xml.Unmarshaler func (r *ArtifactResolve) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { type Alias ArtifactResolve aux := &struct { IssueInstant RelaxedTime `xml:",attr"` *Alias }{ Alias: (*Alias)(r), } if err := d.DecodeElement(&aux, &start); err != nil { return err } r.IssueInstant = time.Time(aux.IssueInstant) return nil } // ArtifactResponse represents the SAML object of the same name. type ArtifactResponse struct { XMLName xml.Name `xml:"urn:oasis:names:tc:SAML:2.0:protocol ArtifactResponse"` ID string `xml:",attr"` InResponseTo string `xml:",attr"` Version string `xml:",attr"` IssueInstant time.Time `xml:",attr"` Issuer *Issuer `xml:"urn:oasis:names:tc:SAML:2.0:assertion Issuer"` Signature *etree.Element Status Status `xml:"urn:oasis:names:tc:SAML:2.0:protocol Status"` Response Response `xml:"urn:oasis:names:tc:SAML:2.0:protocol Response"` } // Element returns an etree.Element representing the object in XML form. func (r *ArtifactResponse) Element() *etree.Element { el := etree.NewElement("samlp:ArtifactResponse") el.CreateAttr("xmlns:saml", "urn:oasis:names:tc:SAML:2.0:assertion") el.CreateAttr("xmlns:samlp", "urn:oasis:names:tc:SAML:2.0:protocol") // Note: This namespace is not used by any element or attribute name, but // is required so that the AttributeValue type element can have a value like // "xs:string". If we don't declare it here, then it will be stripped by the // cannonicalizer. This could be avoided by providing a prefix list to the // cannonicalizer, but prefix lists do not appear to be implemented correctly // in some libraries, so the safest action is to always produce XML that is // (a) in canonical form and (b) does not require prefix lists. el.CreateAttr("xmlns:xs", "http://www.w3.org/2001/XMLSchema") el.CreateAttr("ID", r.ID) if r.InResponseTo != "" { el.CreateAttr("InResponseTo", r.InResponseTo) } el.CreateAttr("Version", r.Version) el.CreateAttr("IssueInstant", r.IssueInstant.Format(timeFormat)) if r.Issuer != nil { el.AddChild(r.Issuer.Element()) } if r.Signature != nil { el.AddChild(r.Signature) } el.AddChild(r.Status.Element()) el.AddChild(r.Response.Element()) return el } // MarshalXML implements xml.Marshaler func (r *ArtifactResponse) MarshalXML(e *xml.Encoder, start xml.StartElement) error { type Alias ArtifactResponse aux := &struct { IssueInstant RelaxedTime `xml:",attr"` *Alias }{ IssueInstant: RelaxedTime(r.IssueInstant), Alias: (*Alias)(r), } return e.Encode(aux) } // UnmarshalXML implements xml.Unmarshaler func (r *ArtifactResponse) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { type Alias ArtifactResponse aux := &struct { IssueInstant RelaxedTime `xml:",attr"` *Alias }{ Alias: (*Alias)(r), } if err := d.DecodeElement(&aux, &start); err != nil { return err } r.IssueInstant = time.Time(aux.IssueInstant) return nil } // Response represents the SAML object of the same name. // // See http://docs.oasis-open.org/security/saml/v2.0/saml-core-2.0-os.pdf type Response struct { XMLName xml.Name `xml:"urn:oasis:names:tc:SAML:2.0:protocol Response"` ID string `xml:",attr"` InResponseTo string `xml:",attr"` Version string `xml:",attr"` IssueInstant time.Time `xml:",attr"` Destination string `xml:",attr"` Consent string `xml:",attr"` Issuer *Issuer `xml:"urn:oasis:names:tc:SAML:2.0:assertion Issuer"` Signature *etree.Element Status Status `xml:"urn:oasis:names:tc:SAML:2.0:protocol Status"` // TODO(ross): more than one EncryptedAssertion is allowed EncryptedAssertion *etree.Element `xml:"urn:oasis:names:tc:SAML:2.0:assertion EncryptedAssertion"` // TODO(ross): more than one Assertion is allowed Assertion *Assertion `xml:"urn:oasis:names:tc:SAML:2.0:assertion Assertion"` } // Element returns an etree.Element representing the object in XML form. func (r *Response) Element() *etree.Element { el := etree.NewElement("samlp:Response") el.CreateAttr("xmlns:saml", "urn:oasis:names:tc:SAML:2.0:assertion") el.CreateAttr("xmlns:samlp", "urn:oasis:names:tc:SAML:2.0:protocol") // Note: This namespace is not used by any element or attribute name, but // is required so that the AttributeValue type element can have a value like // "xs:string". If we don't declare it here, then it will be stripped by the // cannonicalizer. This could be avoided by providing a prefix list to the // cannonicalizer, but prefix lists do not appear to be implemented correctly // in some libraries, so the safest action is to always produce XML that is // (a) in canonical form and (b) does not require prefix lists. el.CreateAttr("xmlns:xs", "http://www.w3.org/2001/XMLSchema") el.CreateAttr("ID", r.ID) if r.InResponseTo != "" { el.CreateAttr("InResponseTo", r.InResponseTo) } el.CreateAttr("Version", r.Version) el.CreateAttr("IssueInstant", r.IssueInstant.Format(timeFormat)) if r.Destination != "" { el.CreateAttr("Destination", r.Destination) } if r.Consent != "" { el.CreateAttr("Consent", r.Consent) } if r.Issuer != nil { el.AddChild(r.Issuer.Element()) } if r.Signature != nil { el.AddChild(r.Signature) } el.AddChild(r.Status.Element()) if r.EncryptedAssertion != nil { el.AddChild(r.EncryptedAssertion) } if r.Assertion != nil { el.AddChild(r.Assertion.Element()) } return el } // MarshalXML implements xml.Marshaler func (r *Response) MarshalXML(e *xml.Encoder, start xml.StartElement) error { type Alias Response aux := &struct { IssueInstant RelaxedTime `xml:",attr"` *Alias }{ IssueInstant: RelaxedTime(r.IssueInstant), Alias: (*Alias)(r), } return e.Encode(aux) } // UnmarshalXML implements xml.Unmarshaler func (r *Response) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { type Alias Response aux := &struct { IssueInstant RelaxedTime `xml:",attr"` *Alias }{ Alias: (*Alias)(r), } if err := d.DecodeElement(&aux, &start); err != nil { return err } r.IssueInstant = time.Time(aux.IssueInstant) return nil } // Status represents the SAML object of the same name. // // See http://docs.oasis-open.org/security/saml/v2.0/saml-core-2.0-os.pdf type Status struct { XMLName xml.Name `xml:"urn:oasis:names:tc:SAML:2.0:protocol Status"` StatusCode StatusCode StatusMessage *StatusMessage StatusDetail *StatusDetail } // Element returns an etree.Element representing the object in XML form. func (s *Status) Element() *etree.Element { el := etree.NewElement("samlp:Status") el.AddChild(s.StatusCode.Element()) if s.StatusMessage != nil { el.AddChild(s.StatusMessage.Element()) } if s.StatusDetail != nil { el.AddChild(s.StatusDetail.Element()) } return el } // StatusCode represents the SAML object of the same name. // // See http://docs.oasis-open.org/security/saml/v2.0/saml-core-2.0-os.pdf type StatusCode struct { XMLName xml.Name `xml:"urn:oasis:names:tc:SAML:2.0:protocol StatusCode"` Value string `xml:",attr"` StatusCode *StatusCode } // Element returns an etree.Element representing the object in XML form. func (s *StatusCode) Element() *etree.Element { el := etree.NewElement("samlp:StatusCode") el.CreateAttr("Value", s.Value) if s.StatusCode != nil { el.AddChild(s.StatusCode.Element()) } return el } // StatusSuccess means the request succeeded. Additional information MAY be returned in the and/or elements. // // TODO(ross): this value is mostly constant, but is mutated in tests. Fix the hacky test so this can be const. var StatusSuccess = "urn:oasis:names:tc:SAML:2.0:status:Success" const ( // The permissible top-level values are as follows: // StatusRequester means the request could not be performed due to an error on the part of the requester. StatusRequester = "urn:oasis:names:tc:SAML:2.0:status:Requester" // StatusResponder means the request could not be performed due to an error on the part of the SAML responder or SAML authority. StatusResponder = "urn:oasis:names:tc:SAML:2.0:status:Responder" // StatusVersionMismatch means the SAML responder could not process the request because the version of the request message was incorrect. StatusVersionMismatch = "urn:oasis:names:tc:SAML:2.0:status:VersionMismatch" // The following second-level status codes are referenced at various places in this specification. Additional // second-level status codes MAY be defined in future versions of the SAML specification. System entities // are free to define more specific status codes by defining appropriate URI references. // StatusAuthnFailed means the responding provider was unable to successfully authenticate the principal. StatusAuthnFailed = "urn:oasis:names:tc:SAML:2.0:status:AuthnFailed" // StatusInvalidAttrNameOrValue means Unexpected or invalid content was encountered within a or element. StatusInvalidAttrNameOrValue = "urn:oasis:names:tc:SAML:2.0:status:InvalidAttrNameOrValue" // StatusInvalidNameIDPolicy means the responding provider cannot or will not support the requested name identifier policy. StatusInvalidNameIDPolicy = "urn:oasis:names:tc:SAML:2.0:status:InvalidNameIDPolicy" // StatusNoAuthnContext means the specified authentication context requirements cannot be met by the responder. StatusNoAuthnContext = "urn:oasis:names:tc:SAML:2.0:status:NoAuthnContext" // StatusNoAvailableIDP is used by an intermediary to indicate that none of the supported identity provider elements in an can be resolved or that none of the supported identity providers are available. StatusNoAvailableIDP = "urn:oasis:names:tc:SAML:2.0:status:NoAvailableIDP" // StatusNoPassive means Indicates the responding provider cannot authenticate the principal passively, as has been requested. StatusNoPassive = "urn:oasis:names:tc:SAML:2.0:status:NoPassive" //nolint:gosec // StatusNoSupportedIDP is used by an intermediary to indicate that none of the identity providers in an are supported by the intermediary. StatusNoSupportedIDP = "urn:oasis:names:tc:SAML:2.0:status:NoSupportedIDP" // StatusPartialLogout is used by a session authority to indicate to a session participant that it was not able to propagate logout to all other session participants. StatusPartialLogout = "urn:oasis:names:tc:SAML:2.0:status:PartialLogout" // StatusProxyCountExceeded means Indicates that a responding provider cannot authenticate the principal directly and is not permitted to proxy the request further. StatusProxyCountExceeded = "urn:oasis:names:tc:SAML:2.0:status:ProxyCountExceeded" // StatusRequestDenied means the SAML responder or SAML authority is able to process the request but has chosen not to respond. This status code MAY be used when there is concern about the security context of the request message or the sequence of request messages received from a particular requester. StatusRequestDenied = "urn:oasis:names:tc:SAML:2.0:status:RequestDenied" // StatusRequestUnsupported means the SAML responder or SAML authority does not support the request. StatusRequestUnsupported = "urn:oasis:names:tc:SAML:2.0:status:RequestUnsupported" // StatusRequestVersionDeprecated means the SAML responder cannot process any requests with the protocol version specified in the request. StatusRequestVersionDeprecated = "urn:oasis:names:tc:SAML:2.0:status:RequestVersionDeprecated" // StatusRequestVersionTooHigh means the SAML responder cannot process the request because the protocol version specified in the request message is a major upgrade from the highest protocol version supported by the responder. StatusRequestVersionTooHigh = "urn:oasis:names:tc:SAML:2.0:status:RequestVersionTooHigh" // StatusRequestVersionTooLow means the SAML responder cannot process the request because the protocol version specified in the request message is too low. StatusRequestVersionTooLow = "urn:oasis:names:tc:SAML:2.0:status:RequestVersionTooLow" // StatusResourceNotRecognized means the resource value provided in the request message is invalid or unrecognized. StatusResourceNotRecognized = "urn:oasis:names:tc:SAML:2.0:status:ResourceNotRecognized" // StatusTooManyResponses means the response message would contain more elements than the SAML responder is able to return. StatusTooManyResponses = "urn:oasis:names:tc:SAML:2.0:status:TooManyResponses" // StatusUnknownAttrProfile means an entity that has no knowledge of a particular attribute profile has been presented with an attribute means drawn from that profile. StatusUnknownAttrProfile = "urn:oasis:names:tc:SAML:2.0:status:UnknownAttrProfile" // StatusUnknownPrincipal means the responding provider does not recognize the principal specified or implied by the request. StatusUnknownPrincipal = "urn:oasis:names:tc:SAML:2.0:status:UnknownPrincipal" // StatusUnsupportedBinding means the SAML responder cannot properly fulfill the request using the protocol binding specified in the request. StatusUnsupportedBinding = "urn:oasis:names:tc:SAML:2.0:status:UnsupportedBinding" ) // StatusMessage represents the SAML element StatusMessage. // // See http://docs.oasis-open.org/security/saml/v2.0/saml-core-2.0-os.pdf §3.2.2.3 type StatusMessage struct { XMLName xml.Name `xml:"urn:oasis:names:tc:SAML:2.0:protocol StatusMessage"` Value string `xml:",chardata"` } // Element returns an etree.Element representing the object in XML form. func (sm StatusMessage) Element() *etree.Element { el := etree.NewElement("samlp:StatusMessage") el.SetText(sm.Value) return el } // StatusDetail represents the SAML element StatusDetail. // // See http://docs.oasis-open.org/security/saml/v2.0/saml-core-2.0-os.pdf §3.2.2.4 type StatusDetail struct { Children []*etree.Element } // Element returns an etree.Element representing the object in XML form. func (sm StatusDetail) Element() *etree.Element { el := etree.NewElement("samlp:StatusDetail") for _, child := range sm.Children { el.AddChild(child) } return el } // Assertion represents the SAML element Assertion. // // See http://docs.oasis-open.org/security/saml/v2.0/saml-core-2.0-os.pdf §2.3.3 type Assertion struct { XMLName xml.Name `xml:"urn:oasis:names:tc:SAML:2.0:assertion Assertion"` ID string `xml:",attr"` IssueInstant time.Time `xml:",attr"` Version string `xml:",attr"` Issuer Issuer `xml:"urn:oasis:names:tc:SAML:2.0:assertion Issuer"` Signature *etree.Element Subject *Subject Conditions *Conditions // Advice *Advice // Statements []Statement AuthnStatements []AuthnStatement `xml:"AuthnStatement"` // AuthzDecisionStatements []AuthzDecisionStatement AttributeStatements []AttributeStatement `xml:"AttributeStatement"` } // Element returns an etree.Element representing the object in XML form. func (a *Assertion) Element() *etree.Element { el := etree.NewElement("saml:Assertion") el.CreateAttr("xmlns:saml", "urn:oasis:names:tc:SAML:2.0:assertion") el.CreateAttr("Version", "2.0") el.CreateAttr("ID", a.ID) el.CreateAttr("IssueInstant", a.IssueInstant.Format(timeFormat)) el.AddChild(a.Issuer.Element()) if a.Signature != nil { el.AddChild(a.Signature) } if a.Subject != nil { el.AddChild(a.Subject.Element()) } if a.Conditions != nil { el.AddChild(a.Conditions.Element()) } for _, authnStatement := range a.AuthnStatements { el.AddChild(authnStatement.Element()) } for _, attributeStatement := range a.AttributeStatements { el.AddChild(attributeStatement.Element()) } err := etreeutils.TransformExcC14n(el, canonicalizerPrefixList) if err != nil { panic(err) } return el } // UnmarshalXML implements xml.Unmarshaler func (a *Assertion) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { type Alias Assertion aux := &struct { IssueInstant RelaxedTime `xml:",attr"` *Alias }{ Alias: (*Alias)(a), } if err := d.DecodeElement(&aux, &start); err != nil { return err } a.IssueInstant = time.Time(aux.IssueInstant) return nil } // Subject represents the SAML element Subject. // // See http://docs.oasis-open.org/security/saml/v2.0/saml-core-2.0-os.pdf §2.4.1 type Subject struct { XMLName xml.Name `xml:"urn:oasis:names:tc:SAML:2.0:assertion Subject"` // BaseID *BaseID ... TODO NameID *NameID // EncryptedID *EncryptedID ... TODO SubjectConfirmations []SubjectConfirmation `xml:"SubjectConfirmation"` } // Element returns an etree.Element representing the object in XML form. func (a *Subject) Element() *etree.Element { el := etree.NewElement("saml:Subject") if a.NameID != nil { el.AddChild(a.NameID.Element()) } for _, v := range a.SubjectConfirmations { el.AddChild(v.Element()) } return el } // NameID represents the SAML element NameID. // // See http://docs.oasis-open.org/security/saml/v2.0/saml-core-2.0-os.pdf §2.2.3 type NameID struct { NameQualifier string `xml:",attr"` SPNameQualifier string `xml:",attr"` Format string `xml:",attr"` SPProvidedID string `xml:",attr"` Value string `xml:",chardata"` } // Element returns an etree.Element representing the object in XML form. func (a *NameID) Element() *etree.Element { el := etree.NewElement("saml:NameID") if a.NameQualifier != "" { el.CreateAttr("NameQualifier", a.NameQualifier) } if a.SPNameQualifier != "" { el.CreateAttr("SPNameQualifier", a.SPNameQualifier) } if a.Format != "" { el.CreateAttr("Format", a.Format) } if a.SPProvidedID != "" { el.CreateAttr("SPProvidedID", a.SPProvidedID) } if a.Value != "" { el.SetText(a.Value) } return el } // SessionIndex represents the SAML element SessionIndex. // // See http://docs.oasis-open.org/security/saml/v2.0/saml-core-2.0-os.pdf §3.7.1 type SessionIndex struct { Value string `xml:",chardata"` } // Element returns an etree.Element representing the object in XML form. func (s *SessionIndex) Element() *etree.Element { el := etree.NewElement("samlp:SessionIndex") el.CreateAttr("xmlns:samlp", "urn:oasis:names:tc:SAML:2.0:protocol") if s.Value != "" { el.SetText(s.Value) } return el } // SubjectConfirmation represents the SAML element SubjectConfirmation. // // See http://docs.oasis-open.org/security/saml/v2.0/saml-core-2.0-os.pdf §2.4.1.1 type SubjectConfirmation struct { Method string `xml:",attr"` // BaseID *BaseID ... TODO NameID *NameID // EncryptedID *EncryptedID ... TODO SubjectConfirmationData *SubjectConfirmationData } // Element returns an etree.Element representing the object in XML form. func (a *SubjectConfirmation) Element() *etree.Element { el := etree.NewElement("saml:SubjectConfirmation") el.CreateAttr("Method", a.Method) if a.NameID != nil { el.AddChild(a.NameID.Element()) } if a.SubjectConfirmationData != nil { el.AddChild(a.SubjectConfirmationData.Element()) } return el } // SubjectConfirmationData represents the SAML element SubjectConfirmationData. // // See http://docs.oasis-open.org/security/saml/v2.0/saml-core-2.0-os.pdf §2.4.1.2 type SubjectConfirmationData struct { NotBefore time.Time `xml:",attr"` NotOnOrAfter time.Time `xml:",attr"` Recipient string `xml:",attr"` InResponseTo string `xml:",attr"` Address string `xml:",attr"` } // Element returns an etree.Element representing the object in XML form. func (s *SubjectConfirmationData) Element() *etree.Element { el := etree.NewElement("saml:SubjectConfirmationData") if !s.NotBefore.IsZero() { el.CreateAttr("NotBefore", s.NotBefore.Format(timeFormat)) } if !s.NotOnOrAfter.IsZero() { el.CreateAttr("NotOnOrAfter", s.NotOnOrAfter.Format(timeFormat)) } if s.Recipient != "" { el.CreateAttr("Recipient", s.Recipient) } if s.InResponseTo != "" { el.CreateAttr("InResponseTo", s.InResponseTo) } if s.Address != "" { el.CreateAttr("Address", s.Address) } return el } // MarshalXML implements xml.Marshaler func (s *SubjectConfirmationData) MarshalXML(e *xml.Encoder, start xml.StartElement) error { type Alias SubjectConfirmationData aux := &struct { NotOnOrAfter RelaxedTime `xml:",attr"` *Alias }{ NotOnOrAfter: RelaxedTime(s.NotOnOrAfter), Alias: (*Alias)(s), } return e.EncodeElement(aux, start) } // UnmarshalXML implements xml.Unmarshaler func (s *SubjectConfirmationData) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { type Alias SubjectConfirmationData aux := &struct { NotOnOrAfter RelaxedTime `xml:",attr"` *Alias }{ Alias: (*Alias)(s), } if err := d.DecodeElement(&aux, &start); err != nil { return err } s.NotOnOrAfter = time.Time(aux.NotOnOrAfter) return nil } // Conditions represents the SAML element Conditions. // // See http://docs.oasis-open.org/security/saml/v2.0/saml-core-2.0-os.pdf §2.5.1 type Conditions struct { NotBefore time.Time `xml:",attr"` NotOnOrAfter time.Time `xml:",attr"` AudienceRestrictions []AudienceRestriction `xml:"AudienceRestriction"` OneTimeUse *OneTimeUse ProxyRestriction *ProxyRestriction } // Element returns an etree.Element representing the object in XML form. func (c *Conditions) Element() *etree.Element { el := etree.NewElement("saml:Conditions") if !c.NotBefore.IsZero() { el.CreateAttr("NotBefore", c.NotBefore.Format(timeFormat)) } if !c.NotOnOrAfter.IsZero() { el.CreateAttr("NotOnOrAfter", c.NotOnOrAfter.Format(timeFormat)) } for _, v := range c.AudienceRestrictions { el.AddChild(v.Element()) } if c.OneTimeUse != nil { el.AddChild(c.OneTimeUse.Element()) } if c.ProxyRestriction != nil { el.AddChild(c.ProxyRestriction.Element()) } return el } // MarshalXML implements xml.Marshaler func (c *Conditions) MarshalXML(e *xml.Encoder, start xml.StartElement) error { type Alias Conditions aux := &struct { NotBefore RelaxedTime `xml:",attr"` NotOnOrAfter RelaxedTime `xml:",attr"` *Alias }{ NotBefore: RelaxedTime(c.NotBefore), NotOnOrAfter: RelaxedTime(c.NotOnOrAfter), Alias: (*Alias)(c), } return e.EncodeElement(aux, start) } // UnmarshalXML implements xml.Unmarshaler func (c *Conditions) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { type Alias Conditions aux := &struct { NotBefore RelaxedTime `xml:",attr"` NotOnOrAfter RelaxedTime `xml:",attr"` *Alias }{ Alias: (*Alias)(c), } if err := d.DecodeElement(&aux, &start); err != nil { return err } c.NotBefore = time.Time(aux.NotBefore) c.NotOnOrAfter = time.Time(aux.NotOnOrAfter) return nil } // AudienceRestriction represents the SAML element AudienceRestriction. // // See http://docs.oasis-open.org/security/saml/v2.0/saml-core-2.0-os.pdf §2.5.1.4 type AudienceRestriction struct { Audience Audience } // Element returns an etree.Element representing the object in XML form. func (a *AudienceRestriction) Element() *etree.Element { el := etree.NewElement("saml:AudienceRestriction") el.AddChild(a.Audience.Element()) return el } // Audience represents the SAML element Audience. // // See http://docs.oasis-open.org/security/saml/v2.0/saml-core-2.0-os.pdf §2.5.1.4 type Audience struct { Value string `xml:",chardata"` } // Element returns an etree.Element representing the object in XML form. func (a *Audience) Element() *etree.Element { el := etree.NewElement("saml:Audience") el.SetText(a.Value) return el } // OneTimeUse represents the SAML element OneTimeUse. // // See http://docs.oasis-open.org/security/saml/v2.0/saml-core-2.0-os.pdf §2.5.1.5 type OneTimeUse struct{} // Element returns an etree.Element representing the object in XML form. func (a *OneTimeUse) Element() *etree.Element { return etree.NewElement("saml:OneTimeUse") } // ProxyRestriction represents the SAML element ProxyRestriction. // // See http://docs.oasis-open.org/security/saml/v2.0/saml-core-2.0-os.pdf §2.5.1.6 type ProxyRestriction struct { Count *int Audiences []Audience } // Element returns an etree.Element representing the object in XML form. func (a *ProxyRestriction) Element() *etree.Element { el := etree.NewElement("saml:ProxyRestriction") if a.Count != nil { el.CreateAttr("Count", strconv.Itoa(*a.Count)) } for _, v := range a.Audiences { el.AddChild(v.Element()) } return el } // AuthnStatement represents the SAML element AuthnStatement. // // See http://docs.oasis-open.org/security/saml/v2.0/saml-core-2.0-os.pdf §2.7.2 type AuthnStatement struct { AuthnInstant time.Time `xml:",attr"` SessionIndex string `xml:",attr"` SessionNotOnOrAfter *time.Time `xml:",attr,omitempty"` SubjectLocality *SubjectLocality AuthnContext AuthnContext } // Element returns an etree.Element representing the object in XML form. func (a *AuthnStatement) Element() *etree.Element { el := etree.NewElement("saml:AuthnStatement") el.CreateAttr("AuthnInstant", a.AuthnInstant.Format(timeFormat)) if a.SessionIndex != "" { el.CreateAttr("SessionIndex", a.SessionIndex) } if a.SessionNotOnOrAfter != nil { el.CreateAttr("SessionNotOnOrAfter", a.SessionNotOnOrAfter.Format(timeFormat)) } if a.SubjectLocality != nil { el.AddChild(a.SubjectLocality.Element()) } el.AddChild(a.AuthnContext.Element()) return el } // MarshalXML implements xml.Marshaler func (a *AuthnStatement) MarshalXML(e *xml.Encoder, start xml.StartElement) error { type Alias AuthnStatement aux := &struct { AuthnInstant RelaxedTime `xml:",attr"` SessionNotOnOrAfter *RelaxedTime `xml:",attr,omitempty"` *Alias }{ AuthnInstant: RelaxedTime(a.AuthnInstant), SessionNotOnOrAfter: (*RelaxedTime)(a.SessionNotOnOrAfter), Alias: (*Alias)(a), } return e.EncodeElement(aux, start) } // UnmarshalXML implements xml.Unmarshaler func (a *AuthnStatement) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { type Alias AuthnStatement aux := &struct { AuthnInstant RelaxedTime `xml:",attr"` SessionNotOnOrAfter *RelaxedTime `xml:",attr,omitempty"` *Alias }{ Alias: (*Alias)(a), } if err := d.DecodeElement(&aux, &start); err != nil { return err } a.AuthnInstant = time.Time(aux.AuthnInstant) a.SessionNotOnOrAfter = (*time.Time)(aux.SessionNotOnOrAfter) return nil } // SubjectLocality represents the SAML element SubjectLocality. // // See http://docs.oasis-open.org/security/saml/v2.0/saml-core-2.0-os.pdf §2.7.2.1 type SubjectLocality struct { Address string `xml:",attr"` DNSName string `xml:",attr"` } // Element returns an etree.Element representing the object in XML form. func (a *SubjectLocality) Element() *etree.Element { el := etree.NewElement("saml:SubjectLocality") if a.Address != "" { el.CreateAttr("Address", a.Address) } if a.DNSName != "" { el.CreateAttr("DNSName", a.DNSName) } return el } // AuthnContext represents the SAML element AuthnContext. // // See http://docs.oasis-open.org/security/saml/v2.0/saml-core-2.0-os.pdf §2.7.2.2 type AuthnContext struct { AuthnContextClassRef *AuthnContextClassRef //AuthnContextDecl *AuthnContextDecl ... TODO //AuthnContextDeclRef *AuthnContextDeclRef ... TODO //AuthenticatingAuthorities []AuthenticatingAuthority... TODO } // Element returns an etree.Element representing the object in XML form. func (a *AuthnContext) Element() *etree.Element { el := etree.NewElement("saml:AuthnContext") if a.AuthnContextClassRef != nil { el.AddChild(a.AuthnContextClassRef.Element()) } return el } // AuthnContextClassRef represents the SAML element AuthnContextClassRef. // // See http://docs.oasis-open.org/security/saml/v2.0/saml-core-2.0-os.pdf §2.7.2.2 type AuthnContextClassRef struct { Value string `xml:",chardata"` } // Element returns an etree.Element representing the object in XML form. func (a *AuthnContextClassRef) Element() *etree.Element { el := etree.NewElement("saml:AuthnContextClassRef") el.SetText(a.Value) return el } // AttributeStatement represents the SAML element AttributeStatement. // // See http://docs.oasis-open.org/security/saml/v2.0/saml-core-2.0-os.pdf §2.7.3 type AttributeStatement struct { Attributes []Attribute `xml:"Attribute"` } // Element returns an etree.Element representing the object in XML form. func (a *AttributeStatement) Element() *etree.Element { el := etree.NewElement("saml:AttributeStatement") for _, v := range a.Attributes { el.AddChild(v.Element()) } return el } // Attribute represents the SAML element Attribute. // // See http://docs.oasis-open.org/security/saml/v2.0/saml-core-2.0-os.pdf §2.7.3.1 type Attribute struct { FriendlyName string `xml:",attr"` Name string `xml:",attr"` NameFormat string `xml:",attr"` Values []AttributeValue `xml:"AttributeValue"` } // Element returns an etree.Element representing the object in XML form. func (a *Attribute) Element() *etree.Element { el := etree.NewElement("saml:Attribute") if a.FriendlyName != "" { el.CreateAttr("FriendlyName", a.FriendlyName) } if a.Name != "" { el.CreateAttr("Name", a.Name) } if a.NameFormat != "" { el.CreateAttr("NameFormat", a.NameFormat) } for _, v := range a.Values { el.AddChild(v.Element()) } return el } // AttributeValue represents the SAML element AttributeValue. // // See http://docs.oasis-open.org/security/saml/v2.0/saml-core-2.0-os.pdf §2.7.3.1.1 type AttributeValue struct { Type string `xml:"http://www.w3.org/2001/XMLSchema-instance type,attr"` Value string `xml:",chardata"` NameID *NameID } // Element returns an etree.Element representing the object in XML form. func (a *AttributeValue) Element() *etree.Element { el := etree.NewElement("saml:AttributeValue") el.CreateAttr("xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance") el.CreateAttr("xmlns:xs", "http://www.w3.org/2001/XMLSchema") el.CreateAttr("xsi:type", a.Type) if a.NameID != nil { el.AddChild(a.NameID.Element()) } el.SetText(a.Value) return el } // LogoutResponse represents the SAML object of the same name. // // See http://docs.oasis-open.org/security/saml/v2.0/saml-core-2.0-os.pdf type LogoutResponse struct { XMLName xml.Name `xml:"urn:oasis:names:tc:SAML:2.0:protocol LogoutResponse"` ID string `xml:",attr"` InResponseTo string `xml:",attr"` Version string `xml:",attr"` IssueInstant time.Time `xml:",attr"` Destination string `xml:",attr"` Consent string `xml:",attr"` Issuer *Issuer `xml:"urn:oasis:names:tc:SAML:2.0:assertion Issuer"` Signature *etree.Element Status Status `xml:"urn:oasis:names:tc:SAML:2.0:protocol Status"` } // Element returns an etree.Element representing the object in XML form. func (r *LogoutResponse) Element() *etree.Element { el := etree.NewElement("samlp:LogoutResponse") el.CreateAttr("xmlns:saml", "urn:oasis:names:tc:SAML:2.0:assertion") el.CreateAttr("xmlns:samlp", "urn:oasis:names:tc:SAML:2.0:protocol") el.CreateAttr("ID", r.ID) if r.InResponseTo != "" { el.CreateAttr("InResponseTo", r.InResponseTo) } el.CreateAttr("Version", r.Version) el.CreateAttr("IssueInstant", r.IssueInstant.Format(timeFormat)) if r.Destination != "" { el.CreateAttr("Destination", r.Destination) } if r.Consent != "" { el.CreateAttr("Consent", r.Consent) } if r.Issuer != nil { el.AddChild(r.Issuer.Element()) } if r.Signature != nil { el.AddChild(r.Signature) } el.AddChild(r.Status.Element()) return el } // MarshalXML implements xml.Marshaler func (r *LogoutResponse) MarshalXML(e *xml.Encoder, start xml.StartElement) error { type Alias LogoutResponse aux := &struct { IssueInstant RelaxedTime `xml:",attr"` *Alias }{ IssueInstant: RelaxedTime(r.IssueInstant), Alias: (*Alias)(r), } return e.Encode(aux) } // UnmarshalXML implements xml.Unmarshaler func (r *LogoutResponse) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { type Alias LogoutResponse aux := &struct { IssueInstant RelaxedTime `xml:",attr"` *Alias }{ Alias: (*Alias)(r), } if err := d.DecodeElement(&aux, &start); err != nil { return err } r.IssueInstant = time.Time(aux.IssueInstant) return nil } saml-0.4.6/schema_test.go000066400000000000000000000136271415467341100153220ustar00rootroot00000000000000package saml import ( "encoding/xml" "testing" "time" "github.com/beevik/etree" "gotest.tools/assert" is "gotest.tools/assert/cmp" ) func TestAttributeXMLRoundTrip(t *testing.T) { expected := Attribute{ FriendlyName: "TestFriendlyName", Name: "TestName", NameFormat: "urn:oasis:names:tc:SAML:2.0:attrname-format:basic", Values: []AttributeValue{{ Type: "xs:string", Value: "test", }}, } doc := etree.NewDocument() doc.SetRoot(expected.Element()) x, err := doc.WriteToBytes() assert.Check(t, err) assert.Check(t, is.Equal("test", string(x))) var actual Attribute err = xml.Unmarshal(x, &actual) assert.Check(t, err) assert.Check(t, is.DeepEqual(expected, actual)) } func TestNameIDFormat(t *testing.T) { var emptyString string el := NameIDPolicy{ Format: &emptyString, } doc := etree.NewDocument() doc.SetRoot(el.Element()) x, err := doc.WriteToBytes() assert.Check(t, err) assert.Check(t, is.Equal("", string(x))) } func TestAuthnStatementXMLRoundTrip(t *testing.T) { authnInstant := time.Date(2020, 7, 21, 12, 30, 45, 0, time.UTC) sessionNotOnOrAfter := time.Date(2020, 7, 22, 15, 0, 0, 0, time.UTC) expected := AuthnStatement{ AuthnInstant: authnInstant, SessionIndex: "index", SessionNotOnOrAfter: &sessionNotOnOrAfter, } doc := etree.NewDocument() doc.SetRoot(expected.Element()) x, err := doc.WriteToBytes() assert.Check(t, err) assert.Check(t, is.Equal(``, string(x))) var actual AuthnStatement err = xml.Unmarshal(x, &actual) assert.Check(t, err) assert.Check(t, is.DeepEqual(expected, actual)) x, err = xml.Marshal(expected) assert.Check(t, err) assert.Check(t, is.Equal(``, string(x))) } func TestAuthnStatementMarshalWithoutSessionNotOnOrAfter(t *testing.T) { authnInstant := time.Date(2020, 7, 21, 12, 30, 45, 0, time.UTC) expected := AuthnStatement{ AuthnInstant: authnInstant, SessionIndex: "index", SessionNotOnOrAfter: nil, } doc := etree.NewDocument() doc.SetRoot(expected.Element()) x, err := doc.WriteToBytes() assert.Check(t, err) assert.Check(t, is.Equal(``, string(x))) var actual AuthnStatement err = xml.Unmarshal(x, &actual) assert.Check(t, err) assert.Check(t, is.DeepEqual(expected, actual)) } func TestLogoutRequestXMLRoundTrip(t *testing.T) { issueInstant := time.Date(2021, 10, 8, 12, 30, 0, 0, time.UTC) notOnOrAfter := time.Date(2021, 10, 8, 12, 35, 0, 0, time.UTC) expected := LogoutRequest{ ID: "request-id", Version: "2.0", IssueInstant: issueInstant, NotOnOrAfter: ¬OnOrAfter, Issuer: &Issuer{ XMLName: xml.Name{ Space: "urn:oasis:names:tc:SAML:2.0:assertion", Local: "Issuer", }, Value: "uri:issuer", }, NameID: &NameID{ Value: "name-id", }, SessionIndex: &SessionIndex{ Value: "index", }, } doc := etree.NewDocument() doc.SetRoot(expected.Element()) x, err := doc.WriteToBytes() assert.Check(t, err) assert.Check(t, is.Equal(`uri:issuername-idindex`, string(x))) var actual LogoutRequest err = xml.Unmarshal(x, &actual) assert.Check(t, err) assert.Check(t, is.DeepEqual(expected, actual)) x, err = xml.Marshal(expected) assert.Check(t, err) assert.Check(t, is.Equal(`uri:issuername-idindex`, string(x))) } func TestLogoutRequestMarshalWithoutNotOnOrAfter(t *testing.T) { issueInstant := time.Date(2021, 10, 8, 12, 30, 0, 0, time.UTC) expected := LogoutRequest{ ID: "request-id", Version: "2.0", IssueInstant: issueInstant, Issuer: &Issuer{ XMLName: xml.Name{ Space: "urn:oasis:names:tc:SAML:2.0:assertion", Local: "Issuer", }, Value: "uri:issuer", }, NameID: &NameID{ Value: "name-id", }, SessionIndex: &SessionIndex{ Value: "index", }, } doc := etree.NewDocument() doc.SetRoot(expected.Element()) x, err := doc.WriteToBytes() assert.Check(t, err) assert.Check(t, is.Equal(`uri:issuername-idindex`, string(x))) var actual LogoutRequest err = xml.Unmarshal(x, &actual) assert.Check(t, err) assert.Check(t, is.DeepEqual(expected, actual)) } saml-0.4.6/service_provider.go000066400000000000000000001443551415467341100164000ustar00rootroot00000000000000package saml import ( "bytes" "compress/flate" "crypto/rsa" "crypto/tls" "crypto/x509" "encoding/base64" "encoding/xml" "errors" "fmt" "html/template" "io/ioutil" "net/http" "net/url" "regexp" "time" xrv "github.com/mattermost/xml-roundtrip-validator" "github.com/beevik/etree" dsig "github.com/russellhaering/goxmldsig" "github.com/russellhaering/goxmldsig/etreeutils" "github.com/crewjam/saml/xmlenc" ) // NameIDFormat is the format of the id type NameIDFormat string // Element returns an XML element representation of n. func (n NameIDFormat) Element() *etree.Element { el := etree.NewElement("") el.SetText(string(n)) return el } // Name ID formats const ( UnspecifiedNameIDFormat NameIDFormat = "urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified" TransientNameIDFormat NameIDFormat = "urn:oasis:names:tc:SAML:2.0:nameid-format:transient" EmailAddressNameIDFormat NameIDFormat = "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress" PersistentNameIDFormat NameIDFormat = "urn:oasis:names:tc:SAML:2.0:nameid-format:persistent" ) // SignatureVerifier verifies a signature // // Can be implemented in order to override ServiceProvider's default // way of verifying signatures. type SignatureVerifier interface { VerifySignature(validationContext *dsig.ValidationContext, el *etree.Element) error } // ServiceProvider implements SAML Service provider. // // In SAML, service providers delegate responsibility for identifying // clients to an identity provider. If you are writing an application // that uses passwords (or whatever) stored somewhere else, then you // are service provider. // // See the example directory for an example of a web application using // the service provider interface. type ServiceProvider struct { // Entity ID is optional - if not specified then MetadataURL will be used EntityID string // Key is the RSA private key we use to sign requests. Key *rsa.PrivateKey // Certificate is the RSA public part of Key. Certificate *x509.Certificate Intermediates []*x509.Certificate // MetadataURL is the full URL to the metadata endpoint on this host, // i.e. https://example.com/saml/metadata MetadataURL url.URL // AcsURL is the full URL to the SAML Assertion Customer Service endpoint // on this host, i.e. https://example.com/saml/acs AcsURL url.URL // SloURL is the full URL to the SAML Single Logout endpoint on this host. // i.e. https://example.com/saml/slo SloURL url.URL // IDPMetadata is the metadata from the identity provider. IDPMetadata *EntityDescriptor // AuthnNameIDFormat is the format used in the NameIDPolicy for // authentication requests AuthnNameIDFormat NameIDFormat // MetadataValidDuration is a duration used to calculate validUntil // attribute in the metadata endpoint MetadataValidDuration time.Duration // ForceAuthn allows you to force re-authentication of users even if the user // has a SSO session at the IdP. ForceAuthn *bool // AllowIdpInitiated AllowIDPInitiated bool // DefaultRedirectURI where untracked requests (as of IDPInitiated) are redirected to DefaultRedirectURI string // SignatureVerifier, if non-nil, allows you to implement an alternative way // to verify signatures. SignatureVerifier SignatureVerifier // SignatureMethod, if non-empty, authentication requests will be signed SignatureMethod string } // MaxIssueDelay is the longest allowed time between when a SAML assertion is // issued by the IDP and the time it is received by ParseResponse. This is used // to prevent old responses from being replayed (while allowing for some clock // drift between the SP and IDP). var MaxIssueDelay = time.Second * 90 // MaxClockSkew allows for leeway for clock skew between the IDP and SP when // validating assertions. It defaults to 180 seconds (matches shibboleth). var MaxClockSkew = time.Second * 180 // DefaultValidDuration is how long we assert that the SP metadata is valid. const DefaultValidDuration = time.Hour * 24 * 2 // DefaultCacheDuration is how long we ask the IDP to cache the SP metadata. const DefaultCacheDuration = time.Hour * 24 * 1 // Metadata returns the service provider metadata func (sp *ServiceProvider) Metadata() *EntityDescriptor { validDuration := DefaultValidDuration if sp.MetadataValidDuration > 0 { validDuration = sp.MetadataValidDuration } authnRequestsSigned := len(sp.SignatureMethod) > 0 wantAssertionsSigned := true validUntil := TimeNow().Add(validDuration) var keyDescriptors []KeyDescriptor if sp.Certificate != nil { certBytes := sp.Certificate.Raw for _, intermediate := range sp.Intermediates { certBytes = append(certBytes, intermediate.Raw...) } keyDescriptors = []KeyDescriptor{ { Use: "encryption", KeyInfo: KeyInfo{ X509Data: X509Data{ X509Certificates: []X509Certificate{ {Data: base64.StdEncoding.EncodeToString(certBytes)}, }, }, }, EncryptionMethods: []EncryptionMethod{ {Algorithm: "http://www.w3.org/2001/04/xmlenc#aes128-cbc"}, {Algorithm: "http://www.w3.org/2001/04/xmlenc#aes192-cbc"}, {Algorithm: "http://www.w3.org/2001/04/xmlenc#aes256-cbc"}, {Algorithm: "http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p"}, }, }, } if len(sp.SignatureMethod) > 0 { keyDescriptors = append(keyDescriptors, KeyDescriptor{ Use: "signing", KeyInfo: KeyInfo{ X509Data: X509Data{ X509Certificates: []X509Certificate{ {Data: base64.StdEncoding.EncodeToString(certBytes)}, }, }, }, }) } } return &EntityDescriptor{ EntityID: firstSet(sp.EntityID, sp.MetadataURL.String()), ValidUntil: validUntil, SPSSODescriptors: []SPSSODescriptor{ { SSODescriptor: SSODescriptor{ RoleDescriptor: RoleDescriptor{ ProtocolSupportEnumeration: "urn:oasis:names:tc:SAML:2.0:protocol", KeyDescriptors: keyDescriptors, ValidUntil: &validUntil, }, SingleLogoutServices: []Endpoint{ { Binding: HTTPPostBinding, Location: sp.SloURL.String(), ResponseLocation: sp.SloURL.String(), }, }, }, AuthnRequestsSigned: &authnRequestsSigned, WantAssertionsSigned: &wantAssertionsSigned, AssertionConsumerServices: []IndexedEndpoint{ { Binding: HTTPPostBinding, Location: sp.AcsURL.String(), Index: 1, }, { Binding: HTTPArtifactBinding, Location: sp.AcsURL.String(), Index: 2, }, }, }, }, } } // MakeRedirectAuthenticationRequest creates a SAML authentication request using // the HTTP-Redirect binding. It returns a URL that we will redirect the user to // in order to start the auth process. func (sp *ServiceProvider) MakeRedirectAuthenticationRequest(relayState string) (*url.URL, error) { req, err := sp.MakeAuthenticationRequest(sp.GetSSOBindingLocation(HTTPRedirectBinding), HTTPRedirectBinding, HTTPPostBinding) if err != nil { return nil, err } return req.Redirect(relayState, sp) } // Redirect returns a URL suitable for using the redirect binding with the request func (req *AuthnRequest) Redirect(relayState string, sp *ServiceProvider) (*url.URL, error) { w := &bytes.Buffer{} w1 := base64.NewEncoder(base64.StdEncoding, w) w2, _ := flate.NewWriter(w1, 9) doc := etree.NewDocument() doc.SetRoot(req.Element()) if _, err := doc.WriteTo(w2); err != nil { panic(err) } w2.Close() w1.Close() rv, _ := url.Parse(req.Destination) // We can't depend on Query().set() as order matters for signing query := rv.RawQuery if len(query) > 0 { query += "&SAMLRequest=" + url.QueryEscape(string(w.Bytes())) } else { query += "SAMLRequest=" + url.QueryEscape(string(w.Bytes())) } if relayState != "" { query += "&RelayState=" + relayState } if len(sp.SignatureMethod) > 0 { query += "&SigAlg=" + url.QueryEscape(sp.SignatureMethod) signingContext, err := GetSigningContext(sp) if err != nil { return nil, err } sig, err := signingContext.SignString(query) if err != nil { return nil, err } query += "&Signature=" + url.QueryEscape(base64.StdEncoding.EncodeToString(sig)) } rv.RawQuery = query return rv, nil } // GetSSOBindingLocation returns URL for the IDP's Single Sign On Service binding // of the specified type (HTTPRedirectBinding or HTTPPostBinding) func (sp *ServiceProvider) GetSSOBindingLocation(binding string) string { for _, idpSSODescriptor := range sp.IDPMetadata.IDPSSODescriptors { for _, singleSignOnService := range idpSSODescriptor.SingleSignOnServices { if singleSignOnService.Binding == binding { return singleSignOnService.Location } } } return "" } // GetArtifactBindingLocation returns URL for the IDP's Artifact binding of the // specified type func (sp *ServiceProvider) GetArtifactBindingLocation(binding string) string { for _, idpSSODescriptor := range sp.IDPMetadata.IDPSSODescriptors { for _, artifactResolutionService := range idpSSODescriptor.ArtifactResolutionServices { if artifactResolutionService.Binding == binding { return artifactResolutionService.Location } } } return "" } // GetSLOBindingLocation returns URL for the IDP's Single Log Out Service binding // of the specified type (HTTPRedirectBinding or HTTPPostBinding) func (sp *ServiceProvider) GetSLOBindingLocation(binding string) string { for _, idpSSODescriptor := range sp.IDPMetadata.IDPSSODescriptors { for _, singleLogoutService := range idpSSODescriptor.SingleLogoutServices { if singleLogoutService.Binding == binding { return singleLogoutService.Location } } } return "" } // getIDPSigningCerts returns the certificates which we can use to verify things // signed by the IDP in PEM format, or nil if no such certificate is found. func (sp *ServiceProvider) getIDPSigningCerts() ([]*x509.Certificate, error) { var certStrs []string // We need to include non-empty certs where the "use" attribute is // either set to "signing" or is missing for _, idpSSODescriptor := range sp.IDPMetadata.IDPSSODescriptors { for _, keyDescriptor := range idpSSODescriptor.KeyDescriptors { if len(keyDescriptor.KeyInfo.X509Data.X509Certificates) != 0 { switch keyDescriptor.Use { case "", "signing": for _, certificate := range keyDescriptor.KeyInfo.X509Data.X509Certificates { certStrs = append(certStrs, certificate.Data) } } } } } if len(certStrs) == 0 { return nil, errors.New("cannot find any signing certificate in the IDP SSO descriptor") } var certs []*x509.Certificate // cleanup whitespace regex := regexp.MustCompile(`\s+`) for _, certStr := range certStrs { certStr = regex.ReplaceAllString(certStr, "") certBytes, err := base64.StdEncoding.DecodeString(certStr) if err != nil { return nil, fmt.Errorf("cannot parse certificate: %s", err) } parsedCert, err := x509.ParseCertificate(certBytes) if err != nil { return nil, err } certs = append(certs, parsedCert) } return certs, nil } // MakeArtifactResolveRequest produces a new ArtifactResolve object to send to the idp's Artifact resolver func (sp *ServiceProvider) MakeArtifactResolveRequest(artifactID string) (*ArtifactResolve, error) { req := ArtifactResolve{ ID: fmt.Sprintf("id-%x", randomBytes(20)), IssueInstant: TimeNow(), Version: "2.0", Issuer: &Issuer{ Format: "urn:oasis:names:tc:SAML:2.0:nameid-format:entity", Value: firstSet(sp.EntityID, sp.MetadataURL.String()), }, Artifact: artifactID, } if len(sp.SignatureMethod) > 0 { if err := sp.SignArtifactResolve(&req); err != nil { return nil, err } } return &req, nil } // MakeAuthenticationRequest produces a new AuthnRequest object to send to the idpURL // that uses the specified binding (HTTPRedirectBinding or HTTPPostBinding) func (sp *ServiceProvider) MakeAuthenticationRequest(idpURL string, binding string, resultBinding string) (*AuthnRequest, error) { allowCreate := true nameIDFormat := sp.nameIDFormat() req := AuthnRequest{ AssertionConsumerServiceURL: sp.AcsURL.String(), Destination: idpURL, ProtocolBinding: resultBinding, // default binding for the response ID: fmt.Sprintf("id-%x", randomBytes(20)), IssueInstant: TimeNow(), Version: "2.0", Issuer: &Issuer{ Format: "urn:oasis:names:tc:SAML:2.0:nameid-format:entity", Value: firstSet(sp.EntityID, sp.MetadataURL.String()), }, NameIDPolicy: &NameIDPolicy{ AllowCreate: &allowCreate, // TODO(ross): figure out exactly policy we need // urn:mace:shibboleth:1.0:nameIdentifier // urn:oasis:names:tc:SAML:2.0:nameid-format:transient Format: &nameIDFormat, }, ForceAuthn: sp.ForceAuthn, } // We don't need to sign the XML document if the IDP uses HTTP-Redirect binding if len(sp.SignatureMethod) > 0 && binding == HTTPPostBinding { if err := sp.SignAuthnRequest(&req); err != nil { return nil, err } } return &req, nil } // GetSigningContext returns a dsig.SigningContext initialized based on the Service Provider's configuration func GetSigningContext(sp *ServiceProvider) (*dsig.SigningContext, error) { keyPair := tls.Certificate{ Certificate: [][]byte{sp.Certificate.Raw}, PrivateKey: sp.Key, Leaf: sp.Certificate, } // TODO: add intermediates for SP //for _, cert := range sp.Intermediates { // keyPair.Certificate = append(keyPair.Certificate, cert.Raw) //} keyStore := dsig.TLSCertKeyStore(keyPair) if sp.SignatureMethod != dsig.RSASHA1SignatureMethod && sp.SignatureMethod != dsig.RSASHA256SignatureMethod && sp.SignatureMethod != dsig.RSASHA512SignatureMethod { return nil, fmt.Errorf("invalid signing method %s", sp.SignatureMethod) } signatureMethod := sp.SignatureMethod signingContext := dsig.NewDefaultSigningContext(keyStore) signingContext.Canonicalizer = dsig.MakeC14N10ExclusiveCanonicalizerWithPrefixList(canonicalizerPrefixList) if err := signingContext.SetSignatureMethod(signatureMethod); err != nil { return nil, err } return signingContext, nil } // SignArtifactResolve adds the `Signature` element to the `ArtifactResolve`. func (sp *ServiceProvider) SignArtifactResolve(req *ArtifactResolve) error { signingContext, err := GetSigningContext(sp) if err != nil { return err } assertionEl := req.Element() signedRequestEl, err := signingContext.SignEnveloped(assertionEl) if err != nil { return err } sigEl := signedRequestEl.Child[len(signedRequestEl.Child)-1] req.Signature = sigEl.(*etree.Element) return nil } // SignAuthnRequest adds the `Signature` element to the `AuthnRequest`. func (sp *ServiceProvider) SignAuthnRequest(req *AuthnRequest) error { signingContext, err := GetSigningContext(sp) if err != nil { return err } assertionEl := req.Element() signedRequestEl, err := signingContext.SignEnveloped(assertionEl) if err != nil { return err } sigEl := signedRequestEl.Child[len(signedRequestEl.Child)-1] req.Signature = sigEl.(*etree.Element) return nil } // MakePostAuthenticationRequest creates a SAML authentication request using // the HTTP-POST binding. It returns HTML text representing an HTML form that // can be sent presented to a browser to initiate the login process. func (sp *ServiceProvider) MakePostAuthenticationRequest(relayState string) ([]byte, error) { req, err := sp.MakeAuthenticationRequest(sp.GetSSOBindingLocation(HTTPPostBinding), HTTPPostBinding, HTTPPostBinding) if err != nil { return nil, err } return req.Post(relayState), nil } // Post returns an HTML form suitable for using the HTTP-POST binding with the request func (req *AuthnRequest) Post(relayState string) []byte { doc := etree.NewDocument() doc.SetRoot(req.Element()) reqBuf, err := doc.WriteToBytes() if err != nil { panic(err) } encodedReqBuf := base64.StdEncoding.EncodeToString(reqBuf) tmpl := template.Must(template.New("saml-post-form").Parse(`` + `
` + `` + `` + `` + `
` + ``)) data := struct { URL string SAMLRequest string RelayState string }{ URL: req.Destination, SAMLRequest: encodedReqBuf, RelayState: relayState, } rv := bytes.Buffer{} if err := tmpl.Execute(&rv, data); err != nil { panic(err) } return rv.Bytes() } // AssertionAttributes is a list of AssertionAttribute type AssertionAttributes []AssertionAttribute // Get returns the assertion attribute whose Name or FriendlyName // matches name, or nil if no matching attribute is found. func (aa AssertionAttributes) Get(name string) *AssertionAttribute { for _, attr := range aa { if attr.Name == name { return &attr } if attr.FriendlyName == name { return &attr } } return nil } // AssertionAttribute represents an attribute of the user extracted from // a SAML Assertion. type AssertionAttribute struct { FriendlyName string Name string Value string } // InvalidResponseError is the error produced by ParseResponse when it fails. // The underlying error is in PrivateErr. Response is the response as it was // known at the time validation failed. Now is the time that was used to validate // time-dependent parts of the assertion. type InvalidResponseError struct { PrivateErr error Response string Now time.Time } func (ivr *InvalidResponseError) Error() string { return fmt.Sprintf("Authentication failed") } // ErrBadStatus is returned when the assertion provided is valid but the // status code is not "urn:oasis:names:tc:SAML:2.0:status:Success". type ErrBadStatus struct { Status string } func (e ErrBadStatus) Error() string { return e.Status } func responseIsSigned(response *etree.Element) (bool, error) { signatureElement, err := findChild(response, "http://www.w3.org/2000/09/xmldsig#", "Signature") if err != nil { return false, err } return signatureElement != nil, nil } // validateDestination validates the Destination attribute. // If the response is signed, the Destination is required to be present. func (sp *ServiceProvider) validateDestination(response *etree.Element, responseDom *Response) error { signed, err := responseIsSigned(response) if err != nil { return err } // Compare if the response is signed OR the Destination is provided. // (Even if the response is not signed, if the Destination is set it must match.) if signed || responseDom.Destination != "" { if responseDom.Destination != sp.AcsURL.String() { return fmt.Errorf("`Destination` does not match AcsURL (expected %q, actual %q)", sp.AcsURL.String(), responseDom.Destination) } } return nil } // ParseResponse extracts the SAML IDP response received in req, resolves // artifacts when necessary, validates it, and returns the verified assertion. func (sp *ServiceProvider) ParseResponse(req *http.Request, possibleRequestIDs []string) (*Assertion, error) { now := TimeNow() var assertion *Assertion retErr := &InvalidResponseError{ Now: now, Response: req.PostForm.Get("SAMLResponse"), } if req.Form.Get("SAMLart") != "" { retErr.Response = req.Form.Get("SAMLart") req, err := sp.MakeArtifactResolveRequest(req.Form.Get("SAMLart")) if err != nil { retErr.PrivateErr = fmt.Errorf("Cannot generate artifact resolution request: %s", err) return nil, retErr } doc := etree.NewDocument() doc.SetRoot(req.SoapRequest()) var requestBuffer bytes.Buffer doc.WriteTo(&requestBuffer) response, err := http.Post(sp.GetArtifactBindingLocation(SOAPBinding), "text/xml", &requestBuffer) if err != nil { retErr.PrivateErr = fmt.Errorf("Error during artifact resolution: %s", err) return nil, retErr } defer response.Body.Close() if response.StatusCode != 200 { retErr.PrivateErr = fmt.Errorf("Error during artifact resolution: HTTP status %d (%s)", response.StatusCode, response.Status) return nil, retErr } rawResponseBuf, err := ioutil.ReadAll(response.Body) if err != nil { retErr.PrivateErr = fmt.Errorf("Error during artifact resolution: %s", err) return nil, retErr } assertion, err = sp.ParseXMLArtifactResponse(rawResponseBuf, possibleRequestIDs, req.ID) if err != nil { return nil, err } } else { rawResponseBuf, err := base64.StdEncoding.DecodeString(req.PostForm.Get("SAMLResponse")) if err != nil { retErr.PrivateErr = fmt.Errorf("cannot parse base64: %s", err) return nil, retErr } retErr.Response = string(rawResponseBuf) assertion, err = sp.ParseXMLResponse(rawResponseBuf, possibleRequestIDs) if err != nil { return nil, err } } return assertion, nil } // ParseXMLArtifactResponse validates the SAML Artifact resolver response // and returns the verified assertion. // // This function handles verifying the digital signature, and verifying // that the specified conditions and properties are met. // // If the function fails it will return an InvalidResponseError whose // properties are useful in describing which part of the parsing process // failed. However, to discourage inadvertent disclosure the diagnostic // information, the Error() method returns a static string. func (sp *ServiceProvider) ParseXMLArtifactResponse(decodedResponseXML []byte, possibleRequestIDs []string, artifactRequestID string) (*Assertion, error) { now := TimeNow() //var err error retErr := &InvalidResponseError{ Now: now, Response: string(decodedResponseXML), } // ensure that the response XML is well formed before we parse it if err := xrv.Validate(bytes.NewReader(decodedResponseXML)); err != nil { retErr.PrivateErr = fmt.Errorf("invalid xml: %s", err) return nil, retErr } envelope := &struct { XMLName xml.Name `xml:"http://schemas.xmlsoap.org/soap/envelope/ Envelope"` Body struct { ArtifactResponse ArtifactResponse } `xml:"http://schemas.xmlsoap.org/soap/envelope/ Body"` }{} if err := xml.Unmarshal(decodedResponseXML, &envelope); err != nil { retErr.PrivateErr = fmt.Errorf("cannot unmarshal response: %s", err) return nil, retErr } resp := envelope.Body.ArtifactResponse // Validate ArtifactResponse if resp.InResponseTo != artifactRequestID { retErr.PrivateErr = fmt.Errorf("`InResponseTo` does not match the artifact request ID (expected %v)", artifactRequestID) return nil, retErr } if resp.IssueInstant.Add(MaxIssueDelay).Before(now) { retErr.PrivateErr = fmt.Errorf("response IssueInstant expired at %s", resp.IssueInstant.Add(MaxIssueDelay)) return nil, retErr } if resp.Issuer != nil && resp.Issuer.Value != sp.IDPMetadata.EntityID { retErr.PrivateErr = fmt.Errorf("response Issuer does not match the IDP metadata (expected %q)", sp.IDPMetadata.EntityID) return nil, retErr } if resp.Status.StatusCode.Value != StatusSuccess { retErr.PrivateErr = ErrBadStatus{Status: resp.Status.StatusCode.Value} return nil, retErr } doc := etree.NewDocument() if err := doc.ReadFromBytes(decodedResponseXML); err != nil { retErr.PrivateErr = err return nil, retErr } artifactEl := doc.FindElement("Envelope/Body/ArtifactResponse") if artifactEl == nil { retErr.PrivateErr = fmt.Errorf("missing ArtifactResponse") return nil, retErr } responseEl := doc.FindElement("Envelope/Body/ArtifactResponse/Response") if responseEl == nil { retErr.PrivateErr = fmt.Errorf("missing inner Response") return nil, retErr } haveSignature := false var err error if err = sp.validateArtifactSigned(artifactEl); err != nil && err.Error() != "either the Response or Assertion must be signed" { retErr.PrivateErr = err return nil, retErr } if err == nil { haveSignature = true } assertion, updatedResponse, err := sp.validateXMLResponse(&resp.Response, responseEl, possibleRequestIDs, now, !haveSignature) if err != nil { retErr.PrivateErr = err if updatedResponse != nil { retErr.Response = *updatedResponse } return nil, retErr } return assertion, nil } // ParseXMLResponse parses and validates the SAML IDP response and // returns the verified assertion. // // This function handles decrypting the message, verifying the digital // signature on the assertion, and verifying that the specified conditions // and properties are met. // // If the function fails it will return an InvalidResponseError whose // properties are useful in describing which part of the parsing process // failed. However, to discourage inadvertent disclosure the diagnostic // information, the Error() method returns a static string. func (sp *ServiceProvider) ParseXMLResponse(decodedResponseXML []byte, possibleRequestIDs []string) (*Assertion, error) { now := TimeNow() var err error retErr := &InvalidResponseError{ Now: now, Response: string(decodedResponseXML), } // ensure that the response XML is well formed before we parse it if err := xrv.Validate(bytes.NewReader(decodedResponseXML)); err != nil { retErr.PrivateErr = fmt.Errorf("invalid xml: %s", err) return nil, retErr } // do some validation first before we decrypt resp := Response{} if err := xml.Unmarshal(decodedResponseXML, &resp); err != nil { retErr.PrivateErr = fmt.Errorf("cannot unmarshal response: %s", err) return nil, retErr } doc := etree.NewDocument() if err := doc.ReadFromBytes(decodedResponseXML); err != nil { retErr.PrivateErr = err return nil, retErr } assertion, updatedResponse, err := sp.validateXMLResponse(&resp, doc.Root(), possibleRequestIDs, now, true) if err != nil { retErr.PrivateErr = err if updatedResponse != nil { retErr.Response = *updatedResponse } return nil, retErr } return assertion, nil } // validateXMLResponse validates the SAML IDP response and returns // the verified assertion. // // This function handles decrypting the message, verifying the digital // signature on the assertion, and verifying that the specified conditions // and properties are met. func (sp *ServiceProvider) validateXMLResponse(resp *Response, responseEl *etree.Element, possibleRequestIDs []string, now time.Time, needSig bool) (*Assertion, *string, error) { var err error var updatedResponse *string if err := sp.validateDestination(responseEl, resp); err != nil { return nil, updatedResponse, err } requestIDvalid := false if sp.AllowIDPInitiated { requestIDvalid = true } else { for _, possibleRequestID := range possibleRequestIDs { if resp.InResponseTo == possibleRequestID { requestIDvalid = true } } } if !requestIDvalid { return nil, updatedResponse, fmt.Errorf("`InResponseTo` does not match any of the possible request IDs (expected %v)", possibleRequestIDs) } if resp.IssueInstant.Add(MaxIssueDelay).Before(now) { return nil, updatedResponse, fmt.Errorf("response IssueInstant expired at %s", resp.IssueInstant.Add(MaxIssueDelay)) } if resp.Issuer != nil && resp.Issuer.Value != sp.IDPMetadata.EntityID { return nil, updatedResponse, fmt.Errorf("response Issuer does not match the IDP metadata (expected %q)", sp.IDPMetadata.EntityID) } if resp.Status.StatusCode.Value != StatusSuccess { return nil, updatedResponse, ErrBadStatus{Status: resp.Status.StatusCode.Value} } var assertion *Assertion if resp.EncryptedAssertion == nil { // TODO(ross): verify that the namespace is urn:oasis:names:tc:SAML:2.0:protocol if responseEl.Tag != "Response" { return nil, updatedResponse, fmt.Errorf("expected to find a response object, not %s", responseEl.Tag) } if err = sp.validateSigned(responseEl); err != nil && !(!needSig && err.Error() == "either the Response or Assertion must be signed") { return nil, updatedResponse, err } assertion = resp.Assertion } // decrypt the response if resp.EncryptedAssertion != nil { // encrypted assertions are part of the signature // before decrypting the response verify that responseSigned, err := responseIsSigned(responseEl) if err != nil { return nil, updatedResponse, err } if responseSigned { if err := sp.validateSigned(responseEl); err != nil { return nil, updatedResponse, err } } var key interface{} = sp.Key keyEl := responseEl.FindElement("//EncryptedAssertion/EncryptedKey") if keyEl != nil { key, err = xmlenc.Decrypt(sp.Key, keyEl) if err != nil { return nil, updatedResponse, fmt.Errorf("failed to decrypt key from response: %s", err) } } el := responseEl.FindElement("//EncryptedAssertion/EncryptedData") plaintextAssertion, err := xmlenc.Decrypt(key, el) if err != nil { return nil, updatedResponse, fmt.Errorf("failed to decrypt response: %s", err) } updatedResponse = new(string) *updatedResponse = string(plaintextAssertion) // TODO(ross): add test case for this if err := xrv.Validate(bytes.NewReader(plaintextAssertion)); err != nil { return nil, updatedResponse, fmt.Errorf("plaintext response contains invalid XML: %s", err) } doc := etree.NewDocument() if err := doc.ReadFromBytes(plaintextAssertion); err != nil { return nil, updatedResponse, fmt.Errorf("cannot parse plaintext response %v", err) } // the decrypted assertion may be signed too // otherwise, a signed response is sufficient if err := sp.validateSigned(doc.Root()); err != nil && !((responseSigned || !needSig) && err.Error() == "either the Response or Assertion must be signed") { return nil, updatedResponse, err } assertion = &Assertion{} // Note: plaintextAssertion is known to be safe to parse because // plaintextAssertion is unmodified from when xrv.Validate() was called above. if err := xml.Unmarshal(plaintextAssertion, assertion); err != nil { return nil, updatedResponse, err } } if err := sp.validateAssertion(assertion, possibleRequestIDs, now); err != nil { return nil, updatedResponse, fmt.Errorf("assertion invalid: %s", err) } return assertion, updatedResponse, nil } // validateAssertion checks that the conditions specified in assertion match // the requirements to accept. If validation fails, it returns an error describing // the failure. (The digital signature on the assertion is not checked -- this // should be done before calling this function). func (sp *ServiceProvider) validateAssertion(assertion *Assertion, possibleRequestIDs []string, now time.Time) error { if assertion.IssueInstant.Add(MaxIssueDelay).Before(now) { return fmt.Errorf("expired on %s", assertion.IssueInstant.Add(MaxIssueDelay)) } if assertion.Issuer.Value != sp.IDPMetadata.EntityID { return fmt.Errorf("issuer is not %q", sp.IDPMetadata.EntityID) } for _, subjectConfirmation := range assertion.Subject.SubjectConfirmations { requestIDvalid := false // We *DO NOT* validate InResponseTo when AllowIDPInitiated is set. Here's why: // // The SAML specification does not provide clear guidance for handling InResponseTo for IDP-initiated // requests where there is no request to be in response to. The specification says: // // InResponseTo [Optional] // The ID of a SAML protocol message in response to which an attesting entity can present the // assertion. For example, this attribute might be used to correlate the assertion to a SAML // request that resulted in its presentation. // // The initial thought was that we should specify a single empty string in possibleRequestIDs for IDP-initiated // requests so that we would ensure that an InResponseTo was *not* provided in those cases where it wasn't // expected. Even that turns out to be frustrating for users. And in practice some IDPs (e.g. Rippling) // set a specific non-empty value for InResponseTo in IDP-initiated requests. // // Finally, it is unclear that there is significant security value in checking InResponseTo when we allow // IDP initiated assertions. if !sp.AllowIDPInitiated { for _, possibleRequestID := range possibleRequestIDs { if subjectConfirmation.SubjectConfirmationData.InResponseTo == possibleRequestID { requestIDvalid = true break } } if !requestIDvalid { return fmt.Errorf("assertion SubjectConfirmation one of the possible request IDs (%v)", possibleRequestIDs) } } if subjectConfirmation.SubjectConfirmationData.Recipient != sp.AcsURL.String() { return fmt.Errorf("assertion SubjectConfirmation Recipient is not %s", sp.AcsURL.String()) } if subjectConfirmation.SubjectConfirmationData.NotOnOrAfter.Add(MaxClockSkew).Before(now) { return fmt.Errorf("assertion SubjectConfirmationData is expired") } } if assertion.Conditions.NotBefore.Add(-MaxClockSkew).After(now) { return fmt.Errorf("assertion Conditions is not yet valid") } if assertion.Conditions.NotOnOrAfter.Add(MaxClockSkew).Before(now) { return fmt.Errorf("assertion Conditions is expired") } audienceRestrictionsValid := len(assertion.Conditions.AudienceRestrictions) == 0 audience := firstSet(sp.EntityID, sp.MetadataURL.String()) for _, audienceRestriction := range assertion.Conditions.AudienceRestrictions { if audienceRestriction.Audience.Value == audience { audienceRestrictionsValid = true } } if !audienceRestrictionsValid { return fmt.Errorf("assertion Conditions AudienceRestriction does not contain %q", audience) } return nil } func findChild(parentEl *etree.Element, childNS string, childTag string) (*etree.Element, error) { for _, childEl := range parentEl.ChildElements() { if childEl.Tag != childTag { continue } ctx, err := etreeutils.NSBuildParentContext(childEl) if err != nil { return nil, err } ctx, err = ctx.SubContext(childEl) if err != nil { return nil, err } ns, err := ctx.LookupPrefix(childEl.Space) if err != nil { return nil, fmt.Errorf("[%s]:%s cannot find prefix %s: %v", childNS, childTag, childEl.Space, err) } if ns != childNS { continue } return childEl, nil } return nil, nil } // validateArtifactSigned returns a nil error iff each of the signatures on the ArtifactResponse, Response // and Assertion elements are valid and there is at least one signature. func (sp *ServiceProvider) validateArtifactSigned(artifactEl *etree.Element) error { haveSignature := false sigEl, err := findChild(artifactEl, "http://www.w3.org/2000/09/xmldsig#", "Signature") if err != nil { return err } if sigEl != nil { if err = sp.validateSignature(artifactEl); err != nil { return fmt.Errorf("cannot validate signature on Response: %v", err) } haveSignature = true } responseEl, err := findChild(artifactEl, "urn:oasis:names:tc:SAML:2.0:protocol", "Response") if err != nil { return err } if responseEl != nil { err = sp.validateSigned(responseEl) if err != nil && err.Error() != "either the Response or Assertion must be signed" { return err } if err == nil { haveSignature = true // guaranteed by validateSigned } } if !haveSignature { return errors.New("either the ArtifactResponse, Response or Assertion must be signed") } return nil } // validateSigned returns a nil error iff each of the signatures on the Response and Assertion elements // are valid and there is at least one signature. func (sp *ServiceProvider) validateSigned(responseEl *etree.Element) error { haveSignature := false // Some SAML responses have the signature on the Response object, and some on the Assertion // object, and some on both. We will require that at least one signature be present and that // all signatures be valid sigEl, err := findChild(responseEl, "http://www.w3.org/2000/09/xmldsig#", "Signature") if err != nil { return err } if sigEl != nil { if err = sp.validateSignature(responseEl); err != nil { return fmt.Errorf("cannot validate signature on Response: %v", err) } haveSignature = true } assertionEl, err := findChild(responseEl, "urn:oasis:names:tc:SAML:2.0:assertion", "Assertion") if err != nil { return err } if assertionEl != nil { sigEl, err := findChild(assertionEl, "http://www.w3.org/2000/09/xmldsig#", "Signature") if err != nil { return err } if sigEl != nil { if err = sp.validateSignature(assertionEl); err != nil { return fmt.Errorf("cannot validate signature on Response: %v", err) } haveSignature = true } } if !haveSignature { return errors.New("either the Response or Assertion must be signed") } return nil } // validateSignature returns nill iff the Signature embedded in the element is valid func (sp *ServiceProvider) validateSignature(el *etree.Element) error { certs, err := sp.getIDPSigningCerts() if err != nil { return err } certificateStore := dsig.MemoryX509CertificateStore{ Roots: certs, } validationContext := dsig.NewDefaultValidationContext(&certificateStore) validationContext.IdAttribute = "ID" if Clock != nil { validationContext.Clock = Clock } // Some SAML responses contain a RSAKeyValue element. One of two things is happening here: // // (1) We're getting something signed by a key we already know about -- the public key // of the signing cert provided in the metadata. // (2) We're getting something signed by a key we *don't* know about, and which we have // no ability to verify. // // The best course of action is to just remove the KeyInfo so that dsig falls back to // verifying against the public key provided in the metadata. if el.FindElement("./Signature/KeyInfo/X509Data/X509Certificate") == nil { if sigEl := el.FindElement("./Signature"); sigEl != nil { if keyInfo := sigEl.FindElement("KeyInfo"); keyInfo != nil { sigEl.RemoveChild(keyInfo) } } } ctx, err := etreeutils.NSBuildParentContext(el) if err != nil { return err } ctx, err = ctx.SubContext(el) if err != nil { return err } el, err = etreeutils.NSDetatch(ctx, el) if err != nil { return err } if sp.SignatureVerifier != nil { return sp.SignatureVerifier.VerifySignature(validationContext, el) } _, err = validationContext.Validate(el) return err } // SignLogoutRequest adds the `Signature` element to the `LogoutRequest`. func (sp *ServiceProvider) SignLogoutRequest(req *LogoutRequest) error { keyPair := tls.Certificate{ Certificate: [][]byte{sp.Certificate.Raw}, PrivateKey: sp.Key, Leaf: sp.Certificate, } // TODO: add intermediates for SP //for _, cert := range sp.Intermediates { // keyPair.Certificate = append(keyPair.Certificate, cert.Raw) //} keyStore := dsig.TLSCertKeyStore(keyPair) if sp.SignatureMethod != dsig.RSASHA1SignatureMethod && sp.SignatureMethod != dsig.RSASHA256SignatureMethod && sp.SignatureMethod != dsig.RSASHA512SignatureMethod { return fmt.Errorf("invalid signing method %s", sp.SignatureMethod) } signatureMethod := sp.SignatureMethod signingContext := dsig.NewDefaultSigningContext(keyStore) signingContext.Canonicalizer = dsig.MakeC14N10ExclusiveCanonicalizerWithPrefixList(canonicalizerPrefixList) if err := signingContext.SetSignatureMethod(signatureMethod); err != nil { return err } assertionEl := req.Element() signedRequestEl, err := signingContext.SignEnveloped(assertionEl) if err != nil { return err } sigEl := signedRequestEl.Child[len(signedRequestEl.Child)-1] req.Signature = sigEl.(*etree.Element) return nil } // MakeLogoutRequest produces a new LogoutRequest object for idpURL. func (sp *ServiceProvider) MakeLogoutRequest(idpURL, nameID string) (*LogoutRequest, error) { req := LogoutRequest{ ID: fmt.Sprintf("id-%x", randomBytes(20)), IssueInstant: TimeNow(), Version: "2.0", Destination: idpURL, Issuer: &Issuer{ Format: "urn:oasis:names:tc:SAML:2.0:nameid-format:entity", Value: firstSet(sp.EntityID, sp.MetadataURL.String()), }, NameID: &NameID{ Format: sp.nameIDFormat(), Value: nameID, NameQualifier: sp.IDPMetadata.EntityID, SPNameQualifier: sp.Metadata().EntityID, }, } if len(sp.SignatureMethod) > 0 { if err := sp.SignLogoutRequest(&req); err != nil { return nil, err } } return &req, nil } // MakeRedirectLogoutRequest creates a SAML authentication request using // the HTTP-Redirect binding. It returns a URL that we will redirect the user to // in order to start the auth process. func (sp *ServiceProvider) MakeRedirectLogoutRequest(nameID, relayState string) (*url.URL, error) { req, err := sp.MakeLogoutRequest(sp.GetSLOBindingLocation(HTTPRedirectBinding), nameID) if err != nil { return nil, err } return req.Redirect(relayState), nil } // Redirect returns a URL suitable for using the redirect binding with the request func (req *LogoutRequest) Redirect(relayState string) *url.URL { w := &bytes.Buffer{} w1 := base64.NewEncoder(base64.StdEncoding, w) w2, _ := flate.NewWriter(w1, 9) doc := etree.NewDocument() doc.SetRoot(req.Element()) if _, err := doc.WriteTo(w2); err != nil { panic(err) } w2.Close() w1.Close() rv, _ := url.Parse(req.Destination) query := rv.Query() query.Set("SAMLRequest", string(w.Bytes())) if relayState != "" { query.Set("RelayState", relayState) } rv.RawQuery = query.Encode() return rv } // MakePostLogoutRequest creates a SAML authentication request using // the HTTP-POST binding. It returns HTML text representing an HTML form that // can be sent presented to a browser to initiate the logout process. func (sp *ServiceProvider) MakePostLogoutRequest(nameID, relayState string) ([]byte, error) { req, err := sp.MakeLogoutRequest(sp.GetSLOBindingLocation(HTTPPostBinding), nameID) if err != nil { return nil, err } return req.Post(relayState), nil } // Post returns an HTML form suitable for using the HTTP-POST binding with the request func (req *LogoutRequest) Post(relayState string) []byte { doc := etree.NewDocument() doc.SetRoot(req.Element()) reqBuf, err := doc.WriteToBytes() if err != nil { panic(err) } encodedReqBuf := base64.StdEncoding.EncodeToString(reqBuf) tmpl := template.Must(template.New("saml-post-form").Parse(`` + `
` + `` + `` + `` + `
` + ``)) data := struct { URL string SAMLRequest string RelayState string }{ URL: req.Destination, SAMLRequest: encodedReqBuf, RelayState: relayState, } rv := bytes.Buffer{} if err := tmpl.Execute(&rv, data); err != nil { panic(err) } return rv.Bytes() } // MakeLogoutResponse produces a new LogoutResponse object for idpURL and logoutRequestID. func (sp *ServiceProvider) MakeLogoutResponse(idpURL, logoutRequestID string) (*LogoutResponse, error) { response := LogoutResponse{ ID: fmt.Sprintf("id-%x", randomBytes(20)), InResponseTo: logoutRequestID, Version: "2.0", IssueInstant: TimeNow(), Destination: idpURL, Issuer: &Issuer{ Format: "urn:oasis:names:tc:SAML:2.0:nameid-format:entity", Value: firstSet(sp.EntityID, sp.MetadataURL.String()), }, Status: Status{ StatusCode: StatusCode{ Value: StatusSuccess, }, }, } if len(sp.SignatureMethod) > 0 { if err := sp.SignLogoutResponse(&response); err != nil { return nil, err } } return &response, nil } // MakeRedirectLogoutResponse creates a SAML LogoutResponse using // the HTTP-Redirect binding. It returns a URL that we will redirect the user to // for LogoutResponse. func (sp *ServiceProvider) MakeRedirectLogoutResponse(logoutRequestID, relayState string) (*url.URL, error) { resp, err := sp.MakeLogoutResponse(sp.GetSLOBindingLocation(HTTPRedirectBinding), logoutRequestID) if err != nil { return nil, err } return resp.Redirect(relayState), nil } // Redirect returns a URL suitable for using the redirect binding with the LogoutResponse. func (resp *LogoutResponse) Redirect(relayState string) *url.URL { w := &bytes.Buffer{} w1 := base64.NewEncoder(base64.StdEncoding, w) w2, _ := flate.NewWriter(w1, 9) doc := etree.NewDocument() doc.SetRoot(resp.Element()) if _, err := doc.WriteTo(w2); err != nil { panic(err) } w2.Close() w1.Close() rv, _ := url.Parse(resp.Destination) query := rv.Query() query.Set("SAMLResponse", string(w.Bytes())) if relayState != "" { query.Set("RelayState", relayState) } rv.RawQuery = query.Encode() return rv } // MakePostLogoutResponse creates a SAML LogoutResponse using // the HTTP-POST binding. It returns HTML text representing an HTML form that // can be sent presented to a browser for LogoutResponse. func (sp *ServiceProvider) MakePostLogoutResponse(logoutRequestID, relayState string) ([]byte, error) { resp, err := sp.MakeLogoutResponse(sp.GetSLOBindingLocation(HTTPPostBinding), logoutRequestID) if err != nil { return nil, err } return resp.Post(relayState), nil } // Post returns an HTML form suitable for using the HTTP-POST binding with the LogoutResponse. func (resp *LogoutResponse) Post(relayState string) []byte { doc := etree.NewDocument() doc.SetRoot(resp.Element()) reqBuf, err := doc.WriteToBytes() if err != nil { panic(err) } encodedReqBuf := base64.StdEncoding.EncodeToString(reqBuf) tmpl := template.Must(template.New("saml-post-form").Parse(`` + `
` + `` + `` + `` + `
` + ``)) data := struct { URL string SAMLResponse string RelayState string }{ URL: resp.Destination, SAMLResponse: encodedReqBuf, RelayState: relayState, } rv := bytes.Buffer{} if err := tmpl.Execute(&rv, data); err != nil { panic(err) } return rv.Bytes() } // SignLogoutResponse adds the `Signature` element to the `LogoutResponse`. func (sp *ServiceProvider) SignLogoutResponse(resp *LogoutResponse) error { keyPair := tls.Certificate{ Certificate: [][]byte{sp.Certificate.Raw}, PrivateKey: sp.Key, Leaf: sp.Certificate, } // TODO: add intermediates for SP //for _, cert := range sp.Intermediates { // keyPair.Certificate = append(keyPair.Certificate, cert.Raw) //} keyStore := dsig.TLSCertKeyStore(keyPair) if sp.SignatureMethod != dsig.RSASHA1SignatureMethod && sp.SignatureMethod != dsig.RSASHA256SignatureMethod && sp.SignatureMethod != dsig.RSASHA512SignatureMethod { return fmt.Errorf("invalid signing method %s", sp.SignatureMethod) } signatureMethod := sp.SignatureMethod signingContext := dsig.NewDefaultSigningContext(keyStore) signingContext.Canonicalizer = dsig.MakeC14N10ExclusiveCanonicalizerWithPrefixList(canonicalizerPrefixList) if err := signingContext.SetSignatureMethod(signatureMethod); err != nil { return err } assertionEl := resp.Element() signedRequestEl, err := signingContext.SignEnveloped(assertionEl) if err != nil { return err } sigEl := signedRequestEl.Child[len(signedRequestEl.Child)-1] resp.Signature = sigEl.(*etree.Element) return nil } func (sp *ServiceProvider) nameIDFormat() string { var nameIDFormat string switch sp.AuthnNameIDFormat { case "": // To maintain library back-compat, use "transient" if unset. nameIDFormat = string(TransientNameIDFormat) case UnspecifiedNameIDFormat: // Spec defines an empty value as "unspecified" so don't set one. default: nameIDFormat = string(sp.AuthnNameIDFormat) } return nameIDFormat } // ValidateLogoutResponseRequest validates the LogoutResponse content from the request func (sp *ServiceProvider) ValidateLogoutResponseRequest(req *http.Request) error { if data := req.URL.Query().Get("SAMLResponse"); data != "" { return sp.ValidateLogoutResponseRedirect(data) } err := req.ParseForm() if err != nil { return fmt.Errorf("unable to parse form: %v", err) } return sp.ValidateLogoutResponseForm(req.PostForm.Get("SAMLResponse")) } // ValidateLogoutResponseForm returns a nil error if the logout response is valid. func (sp *ServiceProvider) ValidateLogoutResponseForm(postFormData string) error { rawResponseBuf, err := base64.StdEncoding.DecodeString(postFormData) if err != nil { return fmt.Errorf("unable to parse base64: %s", err) } // TODO(ross): add test case for this (SLO does not have tests right now) if err := xrv.Validate(bytes.NewReader(rawResponseBuf)); err != nil { return fmt.Errorf("response contains invalid XML: %s", err) } var resp LogoutResponse if err := xml.Unmarshal(rawResponseBuf, &resp); err != nil { return fmt.Errorf("cannot unmarshal response: %s", err) } if err := sp.validateLogoutResponse(&resp); err != nil { return err } doc := etree.NewDocument() if err := doc.ReadFromBytes(rawResponseBuf); err != nil { return err } responseEl := doc.Root() return sp.validateSigned(responseEl) } // ValidateLogoutResponseRedirect returns a nil error if the logout response is valid. // // URL Binding appears to be gzip / flate encoded // See https://www.oasis-open.org/committees/download.php/20645/sstc-saml-tech-overview-2%200-draft-10.pdf 6.6 func (sp *ServiceProvider) ValidateLogoutResponseRedirect(queryParameterData string) error { rawResponseBuf, err := base64.StdEncoding.DecodeString(queryParameterData) if err != nil { return fmt.Errorf("unable to parse base64: %s", err) } gr, err := ioutil.ReadAll(flate.NewReader(bytes.NewBuffer(rawResponseBuf))) if err != nil { return err } if err := xrv.Validate(bytes.NewReader(gr)); err != nil { return err } decoder := xml.NewDecoder(bytes.NewReader(gr)) var resp LogoutResponse err = decoder.Decode(&resp) if err != nil { return fmt.Errorf("unable to flate decode: %s", err) } if err := sp.validateLogoutResponse(&resp); err != nil { return err } doc := etree.NewDocument() if _, err := doc.ReadFrom(bytes.NewReader(gr)); err != nil { return err } responseEl := doc.Root() return sp.validateSigned(responseEl) } // validateLogoutResponse validates the LogoutResponse fields. Returns a nil error if the LogoutResponse is valid. func (sp *ServiceProvider) validateLogoutResponse(resp *LogoutResponse) error { if resp.Destination != sp.SloURL.String() { return fmt.Errorf("`Destination` does not match SloURL (expected %q)", sp.SloURL.String()) } now := time.Now() if resp.IssueInstant.Add(MaxIssueDelay).Before(now) { return fmt.Errorf("issueInstant expired at %s", resp.IssueInstant.Add(MaxIssueDelay)) } if resp.Issuer.Value != sp.IDPMetadata.EntityID { return fmt.Errorf("issuer does not match the IDP metadata (expected %q)", sp.IDPMetadata.EntityID) } if resp.Status.StatusCode.Value != StatusSuccess { return fmt.Errorf("status code was not %s", StatusSuccess) } return nil } func firstSet(a, b string) string { if a == "" { return b } return a } saml-0.4.6/service_provider_go116_test.go000066400000000000000000000150221415467341100203400ustar00rootroot00000000000000//go:build !go1.17 // +build !go1.17 package saml import ( "encoding/base64" "encoding/xml" "net/http" "net/url" "strings" "testing" "time" dsig "github.com/russellhaering/goxmldsig" "gotest.tools/assert" is "gotest.tools/assert/cmp" "gotest.tools/golden" ) func TestSPRejectsMalformedResponse(t *testing.T) { test := NewServiceProviderTest(t) // An actual response from google TimeNow = func() time.Time { rv, _ := time.Parse("Mon Jan 2 15:04:05 UTC 2006", "Tue Jan 5 16:55:39 UTC 2016") return rv } Clock = dsig.NewFakeClockAt(TimeNow()) SamlResponse := golden.Get(t, "TestSPRejectsMalformedResponse_response") test.IDPMetadata = golden.Get(t, "TestSPRejectsMalformedResponse_IDPMetadata") s := ServiceProvider{ Key: test.Key, Certificate: test.Certificate, MetadataURL: mustParseURL("https://29ee6d2e.ngrok.io/saml/metadata"), AcsURL: mustParseURL("https://29ee6d2e.ngrok.io/saml/acs"), IDPMetadata: &EntityDescriptor{}, } err := xml.Unmarshal(test.IDPMetadata, &s.IDPMetadata) assert.Check(t, err) // this is a valid response { req := http.Request{PostForm: url.Values{}} req.PostForm.Set("SAMLResponse", string(SamlResponse)) assertion, err := s.ParseResponse(&req, []string{"id-fd419a5ab0472645427f8e07d87a3a5dd0b2e9a6"}) assert.Check(t, err) assert.Check(t, is.Equal("ross@octolabs.io", assertion.Subject.NameID.Value)) } // this is a valid response but with a comment injected { x, _ := base64.StdEncoding.DecodeString(string(SamlResponse)) y := strings.Replace(string(x), "World!"))) _, err = s.ParseResponse(&req, []string{"id-9e61753d64e928af5a7a341a97f420c9"}) assert.Check(t, is.Error(err.(*InvalidResponseError).PrivateErr, "cannot unmarshal response: expected element type but have ")) req.PostForm.Set("SAMLResponse", base64.StdEncoding.EncodeToString(test.SamlResponse)) _, err = s.ParseResponse(&req, []string{"wrongRequestID"}) assert.Check(t, is.Error(err.(*InvalidResponseError).PrivateErr, "`InResponseTo` does not match any of the possible request IDs (expected [wrongRequestID])")) TimeNow = func() time.Time { rv, _ := time.Parse("Mon Jan 2 15:04:05 MST 2006", "Mon Nov 30 20:57:09 UTC 2016") return rv } Clock = dsig.NewFakeClockAt(TimeNow()) req.PostForm.Set("SAMLResponse", base64.StdEncoding.EncodeToString(test.SamlResponse)) _, err = s.ParseResponse(&req, []string{"id-9e61753d64e928af5a7a341a97f420c9"}) assert.Check(t, is.Error(err.(*InvalidResponseError).PrivateErr, "response IssueInstant expired at 2015-12-01 01:57:51.375 +0000 UTC")) TimeNow = func() time.Time { rv, _ := time.Parse("Mon Jan 2 15:04:05 MST 2006", "Mon Dec 1 01:57:09 UTC 2015") return rv } Clock = dsig.NewFakeClockAt(TimeNow()) s.IDPMetadata.EntityID = "http://snakeoil.com" req.PostForm.Set("SAMLResponse", base64.StdEncoding.EncodeToString(test.SamlResponse)) _, err = s.ParseResponse(&req, []string{"id-9e61753d64e928af5a7a341a97f420c9"}) assert.Check(t, is.Error(err.(*InvalidResponseError).PrivateErr, "response Issuer does not match the IDP metadata (expected \"http://snakeoil.com\")")) s.IDPMetadata.EntityID = "https://idp.testshib.org/idp/shibboleth" oldSpStatusSuccess := StatusSuccess StatusSuccess = "not:the:success:value" req.PostForm.Set("SAMLResponse", base64.StdEncoding.EncodeToString(test.SamlResponse)) _, err = s.ParseResponse(&req, []string{"id-9e61753d64e928af5a7a341a97f420c9"}) assert.Check(t, is.Error(err.(*InvalidResponseError).PrivateErr, "urn:oasis:names:tc:SAML:2.0:status:Success")) StatusSuccess = oldSpStatusSuccess s.IDPMetadata.IDPSSODescriptors[0].KeyDescriptors[0].KeyInfo.X509Data.X509Certificates[0].Data = "invalid" req.PostForm.Set("SAMLResponse", base64.StdEncoding.EncodeToString(test.SamlResponse)) _, err = s.ParseResponse(&req, []string{"id-9e61753d64e928af5a7a341a97f420c9"}) assert.Check(t, is.Error(err.(*InvalidResponseError).PrivateErr, "cannot validate signature on Response: cannot parse certificate: illegal base64 data at input byte 4")) s.IDPMetadata.IDPSSODescriptors[0].KeyDescriptors[0].KeyInfo.X509Data.X509Certificates[0].Data = "aW52YWxpZA==" req.PostForm.Set("SAMLResponse", base64.StdEncoding.EncodeToString(test.SamlResponse)) _, err = s.ParseResponse(&req, []string{"id-9e61753d64e928af5a7a341a97f420c9"}) assert.Check(t, is.Error(err.(*InvalidResponseError).PrivateErr, "cannot validate signature on Response: asn1: structure error: tags don't match (16 vs {class:1 tag:9 length:110 isCompound:true}) {optional:false explicit:false application:false private:false defaultValue: tag: stringType:0 timeType:0 set:false omitEmpty:false} certificate @2")) } saml-0.4.6/service_provider_go117_test.go000066400000000000000000000132431415467341100203440ustar00rootroot00000000000000//go:build go1.17 // +build go1.17 package saml import ( "encoding/base64" "encoding/xml" "net/http" "net/url" "strings" "testing" "time" dsig "github.com/russellhaering/goxmldsig" "gotest.tools/assert" is "gotest.tools/assert/cmp" "gotest.tools/golden" ) func TestSPRejectsMalformedResponse(t *testing.T) { test := NewServiceProviderTest(t) // An actual response from google TimeNow = func() time.Time { rv, _ := time.Parse("Mon Jan 2 15:04:05 UTC 2006", "Tue Jan 5 16:55:39 UTC 2016") return rv } Clock = dsig.NewFakeClockAt(TimeNow()) SamlResponse := golden.Get(t, "TestSPRejectsMalformedResponse_response") test.IDPMetadata = golden.Get(t, "TestSPRejectsMalformedResponse_IDPMetadata") s := ServiceProvider{ Key: test.Key, Certificate: test.Certificate, MetadataURL: mustParseURL("https://29ee6d2e.ngrok.io/saml/metadata"), AcsURL: mustParseURL("https://29ee6d2e.ngrok.io/saml/acs"), IDPMetadata: &EntityDescriptor{}, } err := xml.Unmarshal(test.IDPMetadata, &s.IDPMetadata) assert.Check(t, err) // this is a valid response { req := http.Request{PostForm: url.Values{}} req.PostForm.Set("SAMLResponse", string(SamlResponse)) assertion, err := s.ParseResponse(&req, []string{"id-fd419a5ab0472645427f8e07d87a3a5dd0b2e9a6"}) assert.Check(t, err) assert.Check(t, is.Equal("ross@octolabs.io", assertion.Subject.NameID.Value)) } // this is a valid response but with a comment injected { x, _ := base64.StdEncoding.DecodeString(string(SamlResponse)) y := strings.Replace(string(x), "World!"))) _, err = s.ParseResponse(&req, []string{"id-9e61753d64e928af5a7a341a97f420c9"}) assert.Check(t, is.Error(err.(*InvalidResponseError).PrivateErr, "cannot unmarshal response: expected element type but have ")) req.PostForm.Set("SAMLResponse", base64.StdEncoding.EncodeToString(test.SamlResponse)) _, err = s.ParseResponse(&req, []string{"wrongRequestID"}) assert.Check(t, is.Error(err.(*InvalidResponseError).PrivateErr, "`InResponseTo` does not match any of the possible request IDs (expected [wrongRequestID])")) TimeNow = func() time.Time { rv, _ := time.Parse("Mon Jan 2 15:04:05 MST 2006", "Mon Nov 30 20:57:09 UTC 2016") return rv } Clock = dsig.NewFakeClockAt(TimeNow()) req.PostForm.Set("SAMLResponse", base64.StdEncoding.EncodeToString(test.SamlResponse)) _, err = s.ParseResponse(&req, []string{"id-9e61753d64e928af5a7a341a97f420c9"}) assert.Check(t, is.Error(err.(*InvalidResponseError).PrivateErr, "response IssueInstant expired at 2015-12-01 01:57:51.375 +0000 UTC")) TimeNow = func() time.Time { rv, _ := time.Parse("Mon Jan 2 15:04:05 MST 2006", "Mon Dec 1 01:57:09 UTC 2015") return rv } Clock = dsig.NewFakeClockAt(TimeNow()) s.IDPMetadata.EntityID = "http://snakeoil.com" req.PostForm.Set("SAMLResponse", base64.StdEncoding.EncodeToString(test.SamlResponse)) _, err = s.ParseResponse(&req, []string{"id-9e61753d64e928af5a7a341a97f420c9"}) assert.Check(t, is.Error(err.(*InvalidResponseError).PrivateErr, "response Issuer does not match the IDP metadata (expected \"http://snakeoil.com\")")) s.IDPMetadata.EntityID = "https://idp.testshib.org/idp/shibboleth" oldSpStatusSuccess := StatusSuccess StatusSuccess = "not:the:success:value" req.PostForm.Set("SAMLResponse", base64.StdEncoding.EncodeToString(test.SamlResponse)) _, err = s.ParseResponse(&req, []string{"id-9e61753d64e928af5a7a341a97f420c9"}) assert.Check(t, is.Error(err.(*InvalidResponseError).PrivateErr, "urn:oasis:names:tc:SAML:2.0:status:Success")) StatusSuccess = oldSpStatusSuccess s.IDPMetadata.IDPSSODescriptors[0].KeyDescriptors[0].KeyInfo.X509Data.X509Certificates[0].Data = "invalid" req.PostForm.Set("SAMLResponse", base64.StdEncoding.EncodeToString(test.SamlResponse)) _, err = s.ParseResponse(&req, []string{"id-9e61753d64e928af5a7a341a97f420c9"}) assert.Check(t, is.Error(err.(*InvalidResponseError).PrivateErr, "cannot validate signature on Response: cannot parse certificate: illegal base64 data at input byte 4")) s.IDPMetadata.IDPSSODescriptors[0].KeyDescriptors[0].KeyInfo.X509Data.X509Certificates[0].Data = "aW52YWxpZA==" req.PostForm.Set("SAMLResponse", base64.StdEncoding.EncodeToString(test.SamlResponse)) _, err = s.ParseResponse(&req, []string{"id-9e61753d64e928af5a7a341a97f420c9"}) assert.Check(t, is.Error(err.(*InvalidResponseError).PrivateErr, "cannot validate signature on Response: x509: malformed certificate")) } saml-0.4.6/service_provider_test.go000066400000000000000000001556061415467341100174400ustar00rootroot00000000000000package saml import ( "bytes" "crypto/rsa" "crypto/x509" "encoding/base64" "encoding/xml" "html" "net/http" "net/url" "regexp" "strings" "testing" "time" "gotest.tools/assert" is "gotest.tools/assert/cmp" "gotest.tools/golden" "github.com/beevik/etree" dsig "github.com/russellhaering/goxmldsig" "github.com/crewjam/saml/testsaml" ) type ServiceProviderTest struct { AuthnRequest []byte SamlResponse []byte Key *rsa.PrivateKey Certificate *x509.Certificate IDPMetadata []byte } // Helper to decode SAML redirect binding requests // http://play.golang.org/p/sTlV0pCS2y // x1 := "lJJBj9MwEIX%2FSuR7Y4%2FJRisriVS2Qqq0QNUAB27GmbYWiV08E6D%2FHqeA6AnKdfz85nvPbtYzn8Iev8xIXHyfxkCtmFMw0ZInE%2ByEZNiZfv362ehSmXOKHF0cRbEmwsQ%2BhqcYaJ4w9Zi%2Beofv98%2BtODGfyUgJD3UNVVWV4Zji59JHSXYatbSORLHJO32wi8efG344l5wP6OQ%2FlTEdl4HMWw9%2BRLlgaLnHwSd0LPv%2BrSi2m1b4YaWU0qpStXpUVjmFoEBDBTU8ggUHmIVEM24DsQ3cCq3gYQV6peCdAvMCjIaPotj9ivfSh8GHYytE8QETXQlzfNE1V5d0T1X2d0GieBXTZPnv8mWScxyuUoOBPV9E968iJ2Q7WLaN%2FAnWNW%2Byz3azi6N3l%2F980XGM354SWsZWcJpRdPcDc7KBfMZu5C1B18jbL9b9CAAA%2F%2F8%3D" // x2, _ := url.QueryUnescape(x1) // x3, _ := base64.StdEncoding.DecodeString(x2) // x4, _ := ioutil.ReadAll(flate.NewReader(bytes.NewReader(x3))) // fmt.Printf("%s\n", x4) type testRandomReader struct { Next byte } func (tr *testRandomReader) Read(p []byte) (n int, err error) { for i := 0; i < len(p); i++ { p[i] = tr.Next tr.Next += 2 } return len(p), nil } func NewServiceProviderTest(t *testing.T) *ServiceProviderTest { TimeNow = func() time.Time { rv, _ := time.Parse("Mon Jan 2 15:04:05 MST 2006", "Mon Dec 1 01:57:09 UTC 2015") return rv } Clock = dsig.NewFakeClockAt(TimeNow()) RandReader = &testRandomReader{} test := ServiceProviderTest{} test.AuthnRequest = golden.Get(t, "SP_AuthnRequest") test.SamlResponse = golden.Get(t, "SP_SamlResponse") test.Key = mustParsePrivateKey(golden.Get(t, "sp_key.pem")).(*rsa.PrivateKey) test.Certificate = mustParseCertificate(golden.Get(t, "sp_cert.pem")) test.IDPMetadata = golden.Get(t, "SP_IDPMetadata") return &test } func TestSPCanSetAuthenticationNameIDFormat(t *testing.T) { test := NewServiceProviderTest(t) s := ServiceProvider{ Key: test.Key, Certificate: test.Certificate, MetadataURL: mustParseURL("https://15661444.ngrok.io/saml2/metadata"), AcsURL: mustParseURL("https://15661444.ngrok.io/saml2/acs"), } // defaults to "transient" req, err := s.MakeAuthenticationRequest("", HTTPRedirectBinding, HTTPPostBinding) assert.Check(t, err) assert.Check(t, is.Equal(string(TransientNameIDFormat), *req.NameIDPolicy.Format)) // explicitly set to "transient" s.AuthnNameIDFormat = TransientNameIDFormat req, err = s.MakeAuthenticationRequest("", HTTPRedirectBinding, HTTPPostBinding) assert.Check(t, err) assert.Check(t, is.Equal(string(TransientNameIDFormat), *req.NameIDPolicy.Format)) // explicitly set to "unspecified" s.AuthnNameIDFormat = UnspecifiedNameIDFormat req, err = s.MakeAuthenticationRequest("", HTTPRedirectBinding, HTTPPostBinding) assert.Check(t, err) assert.Check(t, is.Equal("", *req.NameIDPolicy.Format)) // explicitly set to "emailAddress" s.AuthnNameIDFormat = EmailAddressNameIDFormat req, err = s.MakeAuthenticationRequest("", HTTPRedirectBinding, HTTPPostBinding) assert.Check(t, err) assert.Check(t, is.Equal(string(EmailAddressNameIDFormat), *req.NameIDPolicy.Format)) } func TestSPCanProduceMetadataWithEncryptionCert(t *testing.T) { test := NewServiceProviderTest(t) s := ServiceProvider{ Key: test.Key, Certificate: test.Certificate, MetadataURL: mustParseURL("https://example.com/saml2/metadata"), AcsURL: mustParseURL("https://example.com/saml2/acs"), SloURL: mustParseURL("https://example.com/saml2/slo"), IDPMetadata: &EntityDescriptor{}, } err := xml.Unmarshal(test.IDPMetadata, &s.IDPMetadata) assert.Check(t, err) spMetadata, err := xml.MarshalIndent(s.Metadata(), "", " ") assert.Check(t, err) golden.Assert(t, string(spMetadata), t.Name()+"_metadata") } func TestSPCanProduceMetadataWithBothCerts(t *testing.T) { test := NewServiceProviderTest(t) s := ServiceProvider{ Key: test.Key, Certificate: test.Certificate, MetadataURL: mustParseURL("https://example.com/saml2/metadata"), AcsURL: mustParseURL("https://example.com/saml2/acs"), SloURL: mustParseURL("https://example.com/saml2/slo"), IDPMetadata: &EntityDescriptor{}, SignatureMethod: "not-empty", } err := xml.Unmarshal(test.IDPMetadata, &s.IDPMetadata) assert.Check(t, err) spMetadata, err := xml.MarshalIndent(s.Metadata(), "", " ") assert.Check(t, err) golden.Assert(t, string(spMetadata), t.Name()+"_metadata") } func TestCanProduceMetadataNoCerts(t *testing.T) { test := NewServiceProviderTest(t) s := ServiceProvider{ MetadataURL: mustParseURL("https://example.com/saml2/metadata"), AcsURL: mustParseURL("https://example.com/saml2/acs"), IDPMetadata: &EntityDescriptor{}, } err := xml.Unmarshal(test.IDPMetadata, &s.IDPMetadata) assert.Check(t, err) spMetadata, err := xml.MarshalIndent(s.Metadata(), "", " ") assert.Check(t, err) golden.Assert(t, string(spMetadata), t.Name()+"_metadata") } func TestCanProduceMetadataEntityID(t *testing.T) { test := NewServiceProviderTest(t) s := ServiceProvider{ EntityID: "spn:11111111-2222-3333-4444-555555555555", MetadataURL: mustParseURL("https://example.com/saml2/metadata"), AcsURL: mustParseURL("https://example.com/saml2/acs"), IDPMetadata: &EntityDescriptor{}, } err := xml.Unmarshal(test.IDPMetadata, &s.IDPMetadata) assert.Check(t, err) spMetadata, err := xml.MarshalIndent(s.Metadata(), "", " ") assert.Check(t, err) golden.Assert(t, string(spMetadata), t.Name()+"_metadata") } func TestSPCanProduceRedirectRequest(t *testing.T) { test := NewServiceProviderTest(t) TimeNow = func() time.Time { rv, _ := time.Parse("Mon Jan 2 15:04:05.999999999 UTC 2006", "Mon Dec 1 01:31:21.123456789 UTC 2015") return rv } Clock = dsig.NewFakeClockAt(TimeNow()) s := ServiceProvider{ Key: test.Key, Certificate: test.Certificate, MetadataURL: mustParseURL("https://15661444.ngrok.io/saml2/metadata"), AcsURL: mustParseURL("https://15661444.ngrok.io/saml2/acs"), IDPMetadata: &EntityDescriptor{}, } err := xml.Unmarshal(test.IDPMetadata, &s.IDPMetadata) assert.Check(t, err) redirectURL, err := s.MakeRedirectAuthenticationRequest("relayState") assert.Check(t, err) decodedRequest, err := testsaml.ParseRedirectRequest(redirectURL) assert.Check(t, err) assert.Check(t, is.Equal("idp.testshib.org", redirectURL.Host)) assert.Check(t, is.Equal("/idp/profile/SAML2/Redirect/SSO", redirectURL.Path)) golden.Assert(t, string(decodedRequest), t.Name()+"_decoded_request") } func TestSPCanProducePostRequest(t *testing.T) { test := NewServiceProviderTest(t) TimeNow = func() time.Time { rv, _ := time.Parse("Mon Jan 2 15:04:05 UTC 2006", "Mon Dec 1 01:31:21 UTC 2015") return rv } s := ServiceProvider{ Key: test.Key, Certificate: test.Certificate, MetadataURL: mustParseURL("https://15661444.ngrok.io/saml2/metadata"), AcsURL: mustParseURL("https://15661444.ngrok.io/saml2/acs"), IDPMetadata: &EntityDescriptor{}, } err := xml.Unmarshal(test.IDPMetadata, &s.IDPMetadata) assert.Check(t, err) form, err := s.MakePostAuthenticationRequest("relayState") assert.Check(t, err) golden.Assert(t, string(form), t.Name()+"_form") } func TestSPCanProduceSignedRequestRedirectBinding(t *testing.T) { test := NewServiceProviderTest(t) TimeNow = func() time.Time { rv, _ := time.Parse("Mon Jan 2 15:04:05.999999999 UTC 2006", "Mon Dec 1 01:31:21.123456789 UTC 2015") return rv } Clock = dsig.NewFakeClockAt(TimeNow()) s := ServiceProvider{ Key: test.Key, Certificate: test.Certificate, MetadataURL: mustParseURL("https://15661444.ngrok.io/saml2/metadata"), AcsURL: mustParseURL("https://15661444.ngrok.io/saml2/acs"), IDPMetadata: &EntityDescriptor{}, SignatureMethod: dsig.RSASHA1SignatureMethod, } err := xml.Unmarshal(test.IDPMetadata, &s.IDPMetadata) assert.Check(t, err) redirectURL, err := s.MakeRedirectAuthenticationRequest("relayState") assert.Check(t, err) // Signature we check against in the query string was validated with // https://www.samltool.com/validate_authn_req.php . Once we add // support for validating signed AuthN requests in the IDP implementation // we can switch to testing using that. golden.Assert(t, redirectURL.RawQuery, t.Name()+"_queryString") decodedRequest, err := testsaml.ParseRedirectRequest(redirectURL) assert.Check(t, err) assert.Check(t, is.Equal("idp.testshib.org", redirectURL.Host)) assert.Check(t, is.Equal("/idp/profile/SAML2/Redirect/SSO", redirectURL.Path)) // Contains no enveloped signature golden.Assert(t, string(decodedRequest), t.Name()+"_decodedRequest") } func TestSPCanProduceSignedRequestPostBinding(t *testing.T) { test := NewServiceProviderTest(t) TimeNow = func() time.Time { rv, _ := time.Parse("Mon Jan 2 15:04:05.999999999 UTC 2006", "Mon Dec 1 01:31:21.123456789 UTC 2015") return rv } Clock = dsig.NewFakeClockAt(TimeNow()) s := ServiceProvider{ Key: test.Key, Certificate: test.Certificate, MetadataURL: mustParseURL("https://15661444.ngrok.io/saml2/metadata"), AcsURL: mustParseURL("https://15661444.ngrok.io/saml2/acs"), IDPMetadata: &EntityDescriptor{}, SignatureMethod: dsig.RSASHA1SignatureMethod, } err := xml.Unmarshal(test.IDPMetadata, &s.IDPMetadata) assert.Check(t, err) htmlForm, err := s.MakePostAuthenticationRequest("relayState") assert.Check(t, err) rgx := regexp.MustCompile(`\"SAMLRequest\" value=\"(.*?)\" />octolabs.io", 1) SamlResponse = []byte(base64.StdEncoding.EncodeToString([]byte(y))) req := http.Request{PostForm: url.Values{}} req.PostForm.Set("SAMLResponse", string(SamlResponse)) assertion, err := s.ParseResponse(&req, []string{"id-fd419a5ab0472645427f8e07d87a3a5dd0b2e9a6"}) // Note: I would expect the injected comment to be stripped and for the signature // to validate. Less ideal, but not insecure is the case where the comment breaks // the signature, perhaps because xml-c18n isn't being implemented correctly by // dsig. if err == nil { assert.Check(t, is.Equal("ross@octolabs.io", assertion.Subject.NameID.Value)) } } // this is an invalid response with a commend injected per CVE-2018-7340 // ref: https://duo.com/blog/duo-finds-saml-vulnerabilities-affecting-multiple-implementations // it *MUST NOT* validate { x, _ := base64.StdEncoding.DecodeString(string(SamlResponse)) y := strings.Replace(string(x), "ross@octolabs.io", "ross@octolabs.io.example.com", 1) SamlResponse = []byte(base64.StdEncoding.EncodeToString([]byte(y))) req := http.Request{PostForm: url.Values{}} req.PostForm.Set("SAMLResponse", string(SamlResponse)) _, err := s.ParseResponse(&req, []string{"id-fd419a5ab0472645427f8e07d87a3a5dd0b2e9a6"}) assert.Check(t, err != nil) realErr := err.(*InvalidResponseError).PrivateErr assert.Check(t, is.Error(realErr, "cannot validate signature on Response: Signature could not be verified")) } } func TestSPCanParseResponse(t *testing.T) { test := NewServiceProviderTest(t) s := ServiceProvider{ Key: test.Key, Certificate: test.Certificate, MetadataURL: mustParseURL("https://15661444.ngrok.io/saml2/metadata"), AcsURL: mustParseURL("https://15661444.ngrok.io/saml2/acs"), IDPMetadata: &EntityDescriptor{}, } err := xml.Unmarshal(test.IDPMetadata, &s.IDPMetadata) assert.Check(t, err) req := http.Request{PostForm: url.Values{}} req.PostForm.Set("SAMLResponse", base64.StdEncoding.EncodeToString(test.SamlResponse)) assertion, err := s.ParseResponse(&req, []string{"id-9e61753d64e928af5a7a341a97f420c9"}) assert.Check(t, err) assert.Check(t, is.DeepEqual([]Attribute{ { FriendlyName: "uid", Name: "urn:oid:0.9.2342.19200300.100.1.1", NameFormat: "urn:oasis:names:tc:SAML:2.0:attrname-format:uri", Values: []AttributeValue{ { Type: "xs:string", Value: "myself", }, }, }, { FriendlyName: "eduPersonAffiliation", Name: "urn:oid:1.3.6.1.4.1.5923.1.1.1.1", NameFormat: "urn:oasis:names:tc:SAML:2.0:attrname-format:uri", Values: []AttributeValue{ { Type: "xs:string", Value: "Member", }, { Type: "xs:string", Value: "Staff", }, }, }, { FriendlyName: "eduPersonPrincipalName", Name: "urn:oid:1.3.6.1.4.1.5923.1.1.1.6", NameFormat: "urn:oasis:names:tc:SAML:2.0:attrname-format:uri", Values: []AttributeValue{ { Type: "xs:string", Value: "myself@testshib.org", }, }, }, { FriendlyName: "sn", Name: "urn:oid:2.5.4.4", NameFormat: "urn:oasis:names:tc:SAML:2.0:attrname-format:uri", Values: []AttributeValue{ { Type: "xs:string", Value: "And I", }, }, }, { FriendlyName: "eduPersonScopedAffiliation", Name: "urn:oid:1.3.6.1.4.1.5923.1.1.1.9", NameFormat: "urn:oasis:names:tc:SAML:2.0:attrname-format:uri", Values: []AttributeValue{ { Type: "xs:string", Value: "Member@testshib.org", }, { Type: "xs:string", Value: "Staff@testshib.org", }, }, }, { FriendlyName: "givenName", Name: "urn:oid:2.5.4.42", NameFormat: "urn:oasis:names:tc:SAML:2.0:attrname-format:uri", Values: []AttributeValue{ { Type: "xs:string", Value: "Me Myself", }, }, }, { FriendlyName: "eduPersonEntitlement", Name: "urn:oid:1.3.6.1.4.1.5923.1.1.1.7", NameFormat: "urn:oasis:names:tc:SAML:2.0:attrname-format:uri", Values: []AttributeValue{ { Type: "xs:string", Value: "urn:mace:dir:entitlement:common-lib-terms", }, }, }, { FriendlyName: "cn", Name: "urn:oid:2.5.4.3", NameFormat: "urn:oasis:names:tc:SAML:2.0:attrname-format:uri", Values: []AttributeValue{ { Type: "xs:string", Value: "Me Myself And I", }, }, }, { FriendlyName: "eduPersonTargetedID", Name: "urn:oid:1.3.6.1.4.1.5923.1.1.1.10", NameFormat: "urn:oasis:names:tc:SAML:2.0:attrname-format:uri", Values: []AttributeValue{ { NameID: &NameID{Format: "urn:oasis:names:tc:SAML:2.0:nameid-format:persistent", NameQualifier: "https://idp.testshib.org/idp/shibboleth", SPNameQualifier: "https://15661444.ngrok.io/saml2/metadata", Value: "8F+M9ovyaYNwCId0pVkVsnZYRDo="}, }, }, }, { FriendlyName: "telephoneNumber", Name: "urn:oid:2.5.4.20", NameFormat: "urn:oasis:names:tc:SAML:2.0:attrname-format:uri", Values: []AttributeValue{ { Type: "xs:string", Value: "555-5555", }, }, }, }, assertion.AttributeStatements[0].Attributes)) } func (test *ServiceProviderTest) replaceDestination(newDestination string) { newStr := "" if newDestination != "" { newStr = `Destination="` + newDestination + `"` } test.SamlResponse = bytes.Replace(test.SamlResponse, []byte(`Destination="https://15661444.ngrok.io/saml2/acs"`), []byte(newStr), 1) } func TestSPCanProcessResponseWithoutDestination(t *testing.T) { test := NewServiceProviderTest(t) s := ServiceProvider{ Key: test.Key, Certificate: test.Certificate, MetadataURL: mustParseURL("https://15661444.ngrok.io/saml2/metadata"), AcsURL: mustParseURL("https://15661444.ngrok.io/saml2/acs"), IDPMetadata: &EntityDescriptor{}, } err := xml.Unmarshal(test.IDPMetadata, &s.IDPMetadata) assert.Check(t, err) req := http.Request{PostForm: url.Values{}} test.replaceDestination("") req.PostForm.Set("SAMLResponse", base64.StdEncoding.EncodeToString(test.SamlResponse)) _, err = s.ParseResponse(&req, []string{"id-9e61753d64e928af5a7a341a97f420c9"}) assert.Check(t, err) } func (test *ServiceProviderTest) responseDom() (doc *etree.Document) { doc = etree.NewDocument() doc.ReadFromBytes(test.SamlResponse) return doc } func addSignatureToDocument(doc *etree.Document) *etree.Document { responseEl := doc.FindElement("//Response") signatureEl := doc.CreateElement("xmldsig:Signature") signatureEl.CreateAttr("xmlns:xmldsig", "http://www.w3.org/2000/09/xmldsig#") responseEl.AddChild(signatureEl) return doc } func removeDestinationFromDocument(doc *etree.Document) *etree.Document { responseEl := doc.FindElement("//Response") responseEl.RemoveAttr("Destination") return doc } func TestServiceProviderMismatchedDestinationsWithSignaturePresent(t *testing.T) { test := NewServiceProviderTest(t) s := ServiceProvider{ Key: test.Key, Certificate: test.Certificate, MetadataURL: mustParseURL("https://15661444.ngrok.io/saml2/metadata"), AcsURL: mustParseURL("https://15661444.ngrok.io/saml2/acs"), IDPMetadata: &EntityDescriptor{}, } err := xml.Unmarshal(test.IDPMetadata, &s.IDPMetadata) assert.Check(t, err) req := http.Request{PostForm: url.Values{}} s.AcsURL = mustParseURL("https://wrong/saml2/acs") bytes, _ := addSignatureToDocument(test.responseDom()).WriteToBytes() req.PostForm.Set("SAMLResponse", base64.StdEncoding.EncodeToString(bytes)) _, err = s.ParseResponse(&req, []string{"id-9e61753d64e928af5a7a341a97f420c9"}) assert.Check(t, is.Error(err.(*InvalidResponseError).PrivateErr, "`Destination` does not match AcsURL (expected \"https://wrong/saml2/acs\", actual \"https://15661444.ngrok.io/saml2/acs\")")) } func TestServiceProviderMissingDestinationWithSignaturePresent(t *testing.T) { test := NewServiceProviderTest(t) s := ServiceProvider{ Key: test.Key, Certificate: test.Certificate, MetadataURL: mustParseURL("https://15661444.ngrok.io/saml2/metadata"), AcsURL: mustParseURL("https://15661444.ngrok.io/saml2/acs"), IDPMetadata: &EntityDescriptor{}, } err := xml.Unmarshal(test.IDPMetadata, &s.IDPMetadata) assert.Check(t, err) req := http.Request{PostForm: url.Values{}} bytes, _ := removeDestinationFromDocument(addSignatureToDocument(test.responseDom())).WriteToBytes() req.PostForm.Set("SAMLResponse", base64.StdEncoding.EncodeToString(bytes)) _, err = s.ParseResponse(&req, []string{"id-9e61753d64e928af5a7a341a97f420c9"}) assert.Check(t, is.Error(err.(*InvalidResponseError).PrivateErr, "`Destination` does not match AcsURL (expected \"https://15661444.ngrok.io/saml2/acs\", actual \"\")")) } func TestSPMismatchedDestinationsWithSignaturePresent(t *testing.T) { test := NewServiceProviderTest(t) s := ServiceProvider{ Key: test.Key, Certificate: test.Certificate, MetadataURL: mustParseURL("https://15661444.ngrok.io/saml2/metadata"), AcsURL: mustParseURL("https://15661444.ngrok.io/saml2/acs"), IDPMetadata: &EntityDescriptor{}, } err := xml.Unmarshal(test.IDPMetadata, &s.IDPMetadata) assert.Check(t, err) req := http.Request{PostForm: url.Values{}} test.replaceDestination("https://wrong/saml2/acs") bytes, _ := addSignatureToDocument(test.responseDom()).WriteToBytes() req.PostForm.Set("SAMLResponse", base64.StdEncoding.EncodeToString(bytes)) _, err = s.ParseResponse(&req, []string{"id-9e61753d64e928af5a7a341a97f420c9"}) assert.Check(t, is.Error(err.(*InvalidResponseError).PrivateErr, "`Destination` does not match AcsURL (expected \"https://15661444.ngrok.io/saml2/acs\", actual \"https://wrong/saml2/acs\")")) } func TestSPMismatchedDestinationsWithNoSignaturePresent(t *testing.T) { test := NewServiceProviderTest(t) s := ServiceProvider{ Key: test.Key, Certificate: test.Certificate, MetadataURL: mustParseURL("https://15661444.ngrok.io/saml2/metadata"), AcsURL: mustParseURL("https://15661444.ngrok.io/saml2/acs"), IDPMetadata: &EntityDescriptor{}, } err := xml.Unmarshal(test.IDPMetadata, &s.IDPMetadata) assert.Check(t, err) req := http.Request{PostForm: url.Values{}} test.replaceDestination("https://wrong/saml2/acs") bytes, _ := test.responseDom().WriteToBytes() req.PostForm.Set("SAMLResponse", base64.StdEncoding.EncodeToString(bytes)) _, err = s.ParseResponse(&req, []string{"id-9e61753d64e928af5a7a341a97f420c9"}) assert.Check(t, is.Error(err.(*InvalidResponseError).PrivateErr, "`Destination` does not match AcsURL (expected \"https://15661444.ngrok.io/saml2/acs\", actual \"https://wrong/saml2/acs\")")) } func TestSPMissingDestinationWithSignaturePresent(t *testing.T) { test := NewServiceProviderTest(t) s := ServiceProvider{ Key: test.Key, Certificate: test.Certificate, MetadataURL: mustParseURL("https://15661444.ngrok.io/saml2/metadata"), AcsURL: mustParseURL("https://15661444.ngrok.io/saml2/acs"), IDPMetadata: &EntityDescriptor{}, } err := xml.Unmarshal(test.IDPMetadata, &s.IDPMetadata) assert.Check(t, err) req := http.Request{PostForm: url.Values{}} test.replaceDestination("") bytes, _ := addSignatureToDocument(test.responseDom()).WriteToBytes() req.PostForm.Set("SAMLResponse", base64.StdEncoding.EncodeToString(bytes)) _, err = s.ParseResponse(&req, []string{"id-9e61753d64e928af5a7a341a97f420c9"}) assert.Check(t, is.Error(err.(*InvalidResponseError).PrivateErr, "`Destination` does not match AcsURL (expected \"https://15661444.ngrok.io/saml2/acs\", actual \"\")")) } func TestSPInvalidAssertions(t *testing.T) { test := NewServiceProviderTest(t) s := ServiceProvider{ Key: test.Key, Certificate: test.Certificate, MetadataURL: mustParseURL("https://15661444.ngrok.io/saml2/metadata"), AcsURL: mustParseURL("https://15661444.ngrok.io/saml2/acs"), IDPMetadata: &EntityDescriptor{}, } err := xml.Unmarshal(test.IDPMetadata, &s.IDPMetadata) assert.Check(t, err) req := http.Request{PostForm: url.Values{}} req.PostForm.Set("SAMLResponse", base64.StdEncoding.EncodeToString(test.SamlResponse)) s.IDPMetadata.IDPSSODescriptors[0].KeyDescriptors[0].KeyInfo.X509Data.X509Certificates[0].Data = "invalid" _, err = s.ParseResponse(&req, []string{"id-9e61753d64e928af5a7a341a97f420c9"}) assertionBuf := []byte(err.(*InvalidResponseError).Response) assertion := Assertion{} err = xml.Unmarshal(assertionBuf, &assertion) assert.Check(t, err) err = s.validateAssertion(&assertion, []string{"id-9e61753d64e928af5a7a341a97f420c9"}, TimeNow().Add(time.Hour)) assert.Check(t, is.Error(err, "expired on 2015-12-01 01:57:51.375 +0000 UTC")) assertion.Issuer.Value = "bob" err = s.validateAssertion(&assertion, []string{"id-9e61753d64e928af5a7a341a97f420c9"}, TimeNow()) assert.Check(t, is.Error(err, "issuer is not \"https://idp.testshib.org/idp/shibboleth\"")) assertion = Assertion{} xml.Unmarshal(assertionBuf, &assertion) assertion.Subject.NameID.NameQualifier = "bob" err = s.validateAssertion(&assertion, []string{"id-9e61753d64e928af5a7a341a97f420c9"}, TimeNow()) assert.Check(t, err) // not verified assertion = Assertion{} xml.Unmarshal(assertionBuf, &assertion) assertion.Subject.NameID.SPNameQualifier = "bob" err = s.validateAssertion(&assertion, []string{"id-9e61753d64e928af5a7a341a97f420c9"}, TimeNow()) assert.Check(t, err) // not verified assertion = Assertion{} xml.Unmarshal(assertionBuf, &assertion) err = s.validateAssertion(&assertion, []string{"any request id"}, TimeNow()) assert.Check(t, is.Error(err, "assertion SubjectConfirmation one of the possible request IDs ([any request id])")) assertion.Subject.SubjectConfirmations[0].SubjectConfirmationData.Recipient = "wrong/acs/url" err = s.validateAssertion(&assertion, []string{"id-9e61753d64e928af5a7a341a97f420c9"}, TimeNow()) assert.Check(t, is.Error(err, "assertion SubjectConfirmation Recipient is not https://15661444.ngrok.io/saml2/acs")) assertion = Assertion{} xml.Unmarshal(assertionBuf, &assertion) assertion.Subject.SubjectConfirmations[0].SubjectConfirmationData.NotOnOrAfter = TimeNow().Add(-1 * time.Hour) err = s.validateAssertion(&assertion, []string{"id-9e61753d64e928af5a7a341a97f420c9"}, TimeNow()) assert.Check(t, is.Error(err, "assertion SubjectConfirmationData is expired")) assertion = Assertion{} xml.Unmarshal(assertionBuf, &assertion) assertion.Conditions.NotBefore = TimeNow().Add(time.Hour) err = s.validateAssertion(&assertion, []string{"id-9e61753d64e928af5a7a341a97f420c9"}, TimeNow()) assert.Check(t, is.Error(err, "assertion Conditions is not yet valid")) assertion = Assertion{} xml.Unmarshal(assertionBuf, &assertion) assertion.Conditions.NotOnOrAfter = TimeNow().Add(-1 * time.Hour) err = s.validateAssertion(&assertion, []string{"id-9e61753d64e928af5a7a341a97f420c9"}, TimeNow()) assert.Check(t, is.Error(err, "assertion Conditions is expired")) assertion = Assertion{} xml.Unmarshal(assertionBuf, &assertion) assertion.Conditions.AudienceRestrictions[0].Audience.Value = "not/our/metadata/url" err = s.validateAssertion(&assertion, []string{"id-9e61753d64e928af5a7a341a97f420c9"}, TimeNow()) assert.Check(t, is.Error(err, "assertion Conditions AudienceRestriction does not contain \"https://15661444.ngrok.io/saml2/metadata\"")) assertion = Assertion{} xml.Unmarshal(assertionBuf, &assertion) // Not having an audience is not an error assertion.Conditions.AudienceRestrictions = []AudienceRestriction{} err = s.validateAssertion(&assertion, []string{"id-9e61753d64e928af5a7a341a97f420c9"}, TimeNow()) assert.Check(t, err) } func TestXswPermutationOneIsRejected(t *testing.T) { test := NewServiceProviderTest(t) idpMetadata := golden.Get(t, "TestSPCanHandleOneloginResponse_IDPMetadata") respStr := golden.Get(t, "TestXswPermutationOneIsRejected_response") TimeNow = func() time.Time { rv, _ := time.Parse("Mon Jan 2 15:04:05 UTC 2006", "Tue Jan 5 17:53:12 UTC 2016") return rv } Clock = dsig.NewFakeClockAt(TimeNow()) s := ServiceProvider{ Key: test.Key, Certificate: test.Certificate, MetadataURL: mustParseURL("https://29ee6d2e.ngrok.io/saml/metadata"), AcsURL: mustParseURL("https://29ee6d2e.ngrok.io/saml/acs"), IDPMetadata: &EntityDescriptor{}, } err := xml.Unmarshal(idpMetadata, &s.IDPMetadata) assert.Check(t, err) req := http.Request{PostForm: url.Values{}} req.PostForm.Set("SAMLResponse", string(respStr)) _, err = s.ParseResponse(&req, []string{"id-d40c15c104b52691eccf0a2a5c8a15595be75423"}) assert.Check(t, is.Error(err.(*InvalidResponseError).PrivateErr, "cannot validate signature on Response: Missing signature referencing the top-level element")) } func TestXswPermutationTwoIsRejected(t *testing.T) { test := NewServiceProviderTest(t) idpMetadata := golden.Get(t, "TestSPCanHandleOneloginResponse_IDPMetadata") respStr := golden.Get(t, "TestXswPermutationTwoIsRejected_response") TimeNow = func() time.Time { rv, _ := time.Parse("Mon Jan 2 15:04:05 UTC 2006", "Tue Jan 5 17:53:12 UTC 2016") return rv } Clock = dsig.NewFakeClockAt(TimeNow()) s := ServiceProvider{ Key: test.Key, Certificate: test.Certificate, MetadataURL: mustParseURL("https://29ee6d2e.ngrok.io/saml/metadata"), AcsURL: mustParseURL("https://29ee6d2e.ngrok.io/saml/acs"), IDPMetadata: &EntityDescriptor{}, } err := xml.Unmarshal(idpMetadata, &s.IDPMetadata) assert.Check(t, err) req := http.Request{PostForm: url.Values{}} req.PostForm.Set("SAMLResponse", string(respStr)) _, err = s.ParseResponse(&req, []string{"id-d40c15c104b52691eccf0a2a5c8a15595be75423"}) assert.Check(t, is.Error(err.(*InvalidResponseError).PrivateErr, "cannot validate signature on Response: Missing signature referencing the top-level element")) } func TestXswPermutationThreeIsRejected(t *testing.T) { test := NewServiceProviderTest(t) idpMetadata := golden.Get(t, "TestServiceProviderCanHandleSignedAssertionsResponse_IDPMetadata") respStr := golden.Get(t, "TestXswPermutationThreeIsRejected_response") TimeNow = func() time.Time { rv, _ := time.Parse(timeFormat, "2014-07-17T01:02:59Z") return rv } Clock = dsig.NewFakeClockAt(TimeNow()) s := ServiceProvider{ Key: test.Key, Certificate: test.Certificate, MetadataURL: mustParseURL("http://sp.example.com/demo1/metadata.php"), AcsURL: mustParseURL("http://sp.example.com/demo1/index.php?acs"), IDPMetadata: &EntityDescriptor{}, } err := xml.Unmarshal(idpMetadata, &s.IDPMetadata) assert.Check(t, err) req := http.Request{PostForm: url.Values{}} req.PostForm.Set("SAMLResponse", string(respStr)) _, err = s.ParseResponse(&req, []string{"ONELOGIN_4fee3b046395c4e751011e97f8900b5273d56685"}) // Because this permutation contains an unsigned assertion as child of the response assert.Check(t, is.Error(err.(*InvalidResponseError).PrivateErr, "either the Response or Assertion must be signed")) } func TestXswPermutationFourIsRejected(t *testing.T) { test := NewServiceProviderTest(t) idpMetadata := golden.Get(t, "TestServiceProviderCanHandleSignedAssertionsResponse_IDPMetadata") respStr := golden.Get(t, "TestXswPermutationFourIsRejected_response") TimeNow = func() time.Time { rv, _ := time.Parse(timeFormat, "2014-07-17T01:02:59Z") return rv } Clock = dsig.NewFakeClockAt(TimeNow()) s := ServiceProvider{ Key: test.Key, Certificate: test.Certificate, MetadataURL: mustParseURL("http://sp.example.com/demo1/metadata.php"), AcsURL: mustParseURL("http://sp.example.com/demo1/index.php?acs"), IDPMetadata: &EntityDescriptor{}, } err := xml.Unmarshal(idpMetadata, &s.IDPMetadata) assert.Check(t, err) req := http.Request{PostForm: url.Values{}} req.PostForm.Set("SAMLResponse", string(respStr)) _, err = s.ParseResponse(&req, []string{"ONELOGIN_4fee3b046395c4e751011e97f8900b5273d56685"}) // Because this permutation contains an unsigned assertion as child of the response assert.Check(t, is.Error(err.(*InvalidResponseError).PrivateErr, "either the Response or Assertion must be signed")) } func TestXswPermutationFiveIsRejected(t *testing.T) { test := NewServiceProviderTest(t) idpMetadata := golden.Get(t, "TestServiceProviderCanHandleSignedAssertionsResponse_IDPMetadata") respStr := golden.Get(t, "TestXswPermutationFiveIsRejected_response") TimeNow = func() time.Time { rv, _ := time.Parse(timeFormat, "2014-07-17T01:02:59Z") return rv } Clock = dsig.NewFakeClockAt(TimeNow()) s := ServiceProvider{ Key: test.Key, Certificate: test.Certificate, MetadataURL: mustParseURL("http://sp.example.com/demo1/metadata.php"), AcsURL: mustParseURL("http://sp.example.com/demo1/index.php?acs"), IDPMetadata: &EntityDescriptor{}, } err := xml.Unmarshal(idpMetadata, &s.IDPMetadata) assert.Check(t, err) req := http.Request{PostForm: url.Values{}} req.PostForm.Set("SAMLResponse", string(respStr)) _, err = s.ParseResponse(&req, []string{"ONELOGIN_4fee3b046395c4e751011e97f8900b5273d56685"}) assert.Check(t, is.Error(err.(*InvalidResponseError).PrivateErr, "cannot validate signature on Response: Missing signature referencing the top-level element")) } func TestXswPermutationSixIsRejected(t *testing.T) { test := NewServiceProviderTest(t) idpMetadata := golden.Get(t, "TestServiceProviderCanHandleSignedAssertionsResponse_IDPMetadata") respStr := golden.Get(t, "TestXswPermutationSixIsRejected_response") TimeNow = func() time.Time { rv, _ := time.Parse(timeFormat, "2014-07-17T01:02:59Z") return rv } Clock = dsig.NewFakeClockAt(TimeNow()) s := ServiceProvider{ Key: test.Key, Certificate: test.Certificate, MetadataURL: mustParseURL("http://sp.example.com/demo1/metadata.php"), AcsURL: mustParseURL("http://sp.example.com/demo1/index.php?acs"), IDPMetadata: &EntityDescriptor{}, } err := xml.Unmarshal(idpMetadata, &s.IDPMetadata) assert.Check(t, err) req := http.Request{PostForm: url.Values{}} req.PostForm.Set("SAMLResponse", string(respStr)) _, err = s.ParseResponse(&req, []string{"ONELOGIN_4fee3b046395c4e751011e97f8900b5273d56685"}) assert.Check(t, is.Error(err.(*InvalidResponseError).PrivateErr, "cannot validate signature on Response: Missing signature referencing the top-level element")) } func TestXswPermutationSevenIsRejected(t *testing.T) { test := NewServiceProviderTest(t) idpMetadata := golden.Get(t, "TestServiceProviderCanHandleSignedAssertionsResponse_IDPMetadata") respStr := golden.Get(t, "TestXswPermutationSevenIsRejected_response") TimeNow = func() time.Time { rv, _ := time.Parse(timeFormat, "2014-07-17T01:02:59Z") return rv } Clock = dsig.NewFakeClockAt(func() time.Time { rv, _ := time.Parse(timeFormat, "2014-07-17T14:12:57Z") return rv }()) s := ServiceProvider{ Key: test.Key, Certificate: test.Certificate, MetadataURL: mustParseURL("http://sp.example.com/demo1/metadata.php"), AcsURL: mustParseURL("http://sp.example.com/demo1/index.php?acs"), IDPMetadata: &EntityDescriptor{}, } err := xml.Unmarshal(idpMetadata, &s.IDPMetadata) assert.Check(t, err) req := http.Request{PostForm: url.Values{}} req.PostForm.Set("SAMLResponse", string(respStr)) _, err = s.ParseResponse(&req, []string{"ONELOGIN_4fee3b046395c4e751011e97f8900b5273d56685"}) //It's the assertion signature that can't be verified. The error message is generic and always mentions Response assert.Check(t, is.Error(err.(*InvalidResponseError).PrivateErr, "cannot validate signature on Response: Signature could not be verified")) } func TestXswPermutationEightIsRejected(t *testing.T) { test := NewServiceProviderTest(t) idpMetadata := golden.Get(t, "TestServiceProviderCanHandleSignedAssertionsResponse_IDPMetadata") respStr := golden.Get(t, "TestXswPermutationEightIsRejected_response") TimeNow = func() time.Time { rv, _ := time.Parse(timeFormat, "2014-07-17T01:02:59Z") return rv } Clock = dsig.NewFakeClockAt(func() time.Time { rv, _ := time.Parse(timeFormat, "2014-07-17T14:12:57Z") return rv }()) s := ServiceProvider{ Key: test.Key, Certificate: test.Certificate, MetadataURL: mustParseURL("http://sp.example.com/demo1/metadata.php"), AcsURL: mustParseURL("http://sp.example.com/demo1/index.php?acs"), IDPMetadata: &EntityDescriptor{}, } err := xml.Unmarshal(idpMetadata, &s.IDPMetadata) assert.Check(t, err) req := http.Request{PostForm: url.Values{}} req.PostForm.Set("SAMLResponse", string(respStr)) _, err = s.ParseResponse(&req, []string{"ONELOGIN_4fee3b046395c4e751011e97f8900b5273d56685"}) //It's the assertion signature that can't be verified. The error message is generic and always mentions Response assert.Check(t, is.Error(err.(*InvalidResponseError).PrivateErr, "cannot validate signature on Response: Signature could not be verified")) } func TestXswPermutationNineIsRejected(t *testing.T) { test := NewServiceProviderTest(t) idpMetadata := golden.Get(t, "TestServiceProviderCanHandleSignedAssertionsResponse_IDPMetadata") respStr := golden.Get(t, "TestXswPermutationNineIsRejected_response") TimeNow = func() time.Time { rv, _ := time.Parse(timeFormat, "2014-07-17T01:02:59Z") return rv } Clock = dsig.NewFakeClockAt(func() time.Time { rv, _ := time.Parse(timeFormat, "2014-07-17T14:12:57Z") return rv }()) s := ServiceProvider{ Key: test.Key, Certificate: test.Certificate, MetadataURL: mustParseURL("http://sp.example.com/demo1/metadata.php"), AcsURL: mustParseURL("http://sp.example.com/demo1/index.php?acs"), IDPMetadata: &EntityDescriptor{}, } err := xml.Unmarshal(idpMetadata, &s.IDPMetadata) assert.Check(t, err) req := http.Request{PostForm: url.Values{}} req.PostForm.Set("SAMLResponse", string(respStr)) _, err = s.ParseResponse(&req, []string{"ONELOGIN_4fee3b046395c4e751011e97f8900b5273d56685"}) //It's the assertion signature that can't be verified. The error message is generic and always mentions Response assert.Check(t, is.Error(err.(*InvalidResponseError).PrivateErr, "cannot validate signature on Response: Missing signature referencing the top-level element")) } func TestSPRealWorldKeyInfoHasRSAPublicKeyNotX509Cert(t *testing.T) { // This is a real world SAML response that we observed. It contains elements idpMetadata := golden.Get(t, "TestSPRealWorldKeyInfoHasRSAPublicKeyNotX509Cert_idp_metadata") respStr := golden.Get(t, "TestSPRealWorldKeyInfoHasRSAPublicKeyNotX509Cert_response") TimeNow = func() time.Time { rv, _ := time.Parse("Mon Jan 2 15:04:05 MST 2006", "Fri Apr 21 13:12:51 UTC 2017") return rv } Clock = dsig.NewFakeClockAt(TimeNow()) s := ServiceProvider{ Key: mustParsePrivateKey(golden.Get(t, "key_2017.pem")).(*rsa.PrivateKey), Certificate: mustParseCertificate(golden.Get(t, "cert_2017.pem")), MetadataURL: mustParseURL("https://preview.docrocket-ross.test.octolabs.io/saml/metadata"), AcsURL: mustParseURL("https://preview.docrocket-ross.test.octolabs.io/saml/acs"), IDPMetadata: &EntityDescriptor{}, } err := xml.Unmarshal(idpMetadata, &s.IDPMetadata) assert.Check(t, err) req := http.Request{PostForm: url.Values{}} req.PostForm.Set("SAMLResponse", base64.StdEncoding.EncodeToString(respStr)) _, err = s.ParseResponse(&req, []string{"id-3992f74e652d89c3cf1efd6c7e472abaac9bc917"}) if err != nil { assert.Check(t, err.(*InvalidResponseError).PrivateErr) } assert.Check(t, err) } func TestSPRealWorldAssertionSignedNotResponse(t *testing.T) { // This is a real world SAML response that we observed. It contains elements rather than // a certificate in the response. idpMetadata := golden.Get(t, "TestSPRealWorldAssertionSignedNotResponse_idp_metadata") respStr := golden.Get(t, "TestSPRealWorldAssertionSignedNotResponse_response") TimeNow = func() time.Time { rv, _ := time.Parse("Mon Jan 2 15:04:05 MST 2006", "Fri Apr 21 13:12:51 UTC 2017") return rv } Clock = dsig.NewFakeClockAt(TimeNow()) s := ServiceProvider{ Key: mustParsePrivateKey(golden.Get(t, "key_2017.pem")).(*rsa.PrivateKey), Certificate: mustParseCertificate(golden.Get(t, "cert_2017.pem")), MetadataURL: mustParseURL("https://preview.docrocket-ross.test.octolabs.io/saml/metadata"), AcsURL: mustParseURL("https://preview.docrocket-ross.test.octolabs.io/saml/acs"), IDPMetadata: &EntityDescriptor{}, } err := xml.Unmarshal(idpMetadata, &s.IDPMetadata) assert.Check(t, err) req := http.Request{PostForm: url.Values{}} req.PostForm.Set("SAMLResponse", base64.StdEncoding.EncodeToString(respStr)) _, err = s.ParseResponse(&req, []string{"id-3992f74e652d89c3cf1efd6c7e472abaac9bc917"}) if err != nil { assert.Check(t, err.(*InvalidResponseError).PrivateErr) } assert.Check(t, err) } func TestServiceProviderCanHandleSignedAssertionsResponse(t *testing.T) { test := NewServiceProviderTest(t) // Note: This test uses an actual response from onelogin, submitted by a user. // However, the test data below isn't actually valid -- the issue instant is // before the certificate's issued time. In order to preserve this test data and // signatures, we assign a different time to Clock, used by xmldsig than to // TimeNow which is used to verify the issue time of the SAML assertion. Clock = dsig.NewFakeClockAt(func() time.Time { rv, _ := time.Parse(timeFormat, "2014-07-17T14:12:57Z") return rv }()) TimeNow = func() time.Time { rv, _ := time.Parse(timeFormat, "2014-07-17T01:02:59Z") return rv } SamlResponse := golden.Get(t, "TestServiceProviderCanHandleSignedAssertionsResponse_response") test.IDPMetadata = golden.Get(t, "TestServiceProviderCanHandleSignedAssertionsResponse_IDPMetadata") s := ServiceProvider{ Key: test.Key, Certificate: test.Certificate, MetadataURL: mustParseURL("http://sp.example.com/demo1/metadata.php"), AcsURL: mustParseURL("http://sp.example.com/demo1/index.php?acs"), IDPMetadata: &EntityDescriptor{}, } err := xml.Unmarshal(test.IDPMetadata, &s.IDPMetadata) assert.Check(t, err) req := http.Request{PostForm: url.Values{}} req.PostForm.Set("SAMLResponse", string(SamlResponse)) assertion, err := s.ParseResponse(&req, []string{"ONELOGIN_4fee3b046395c4e751011e97f8900b5273d56685"}) if err != nil { t.Logf("%s", err.(*InvalidResponseError).PrivateErr) } assert.Check(t, err) assert.Check(t, is.Equal("_ce3d2948b4cf20146dee0a0b3dd6f69b6cf86f62d7", assertion.Subject.NameID.Value)) assert.Check(t, is.DeepEqual([]Attribute{ { Name: "uid", NameFormat: "urn:oasis:names:tc:SAML:2.0:attrname-format:basic", Values: []AttributeValue{ { Type: "xs:string", Value: "test", }, }, }, { Name: "mail", NameFormat: "urn:oasis:names:tc:SAML:2.0:attrname-format:basic", Values: []AttributeValue{ { Type: "xs:string", Value: "test@example.com", }, }, }, { Name: "eduPersonAffiliation", NameFormat: "urn:oasis:names:tc:SAML:2.0:attrname-format:basic", Values: []AttributeValue{ { Type: "xs:string", Value: "users", }, { Type: "xs:string", Value: "examplerole1", }, }, }, }, assertion.AttributeStatements[0].Attributes)) } func TestSPResponseWithNoIssuer(t *testing.T) { test := NewServiceProviderTest(t) // This test case for the IdP response with no element. SAML standard says // that the element MAY be omitted in the (but MUST present in the ). s := ServiceProvider{ Key: test.Key, Certificate: test.Certificate, MetadataURL: mustParseURL("https://15661444.ngrok.io/saml2/metadata"), AcsURL: mustParseURL("https://15661444.ngrok.io/saml2/acs"), IDPMetadata: &EntityDescriptor{}, } err := xml.Unmarshal(test.IDPMetadata, &s.IDPMetadata) assert.Check(t, err) req := http.Request{PostForm: url.Values{}} // Response with no (modified ServiceProviderTest.SamlResponse) samlResponse := golden.Get(t, "TestSPResponseWithNoIssuer_response") req.PostForm.Set("SAMLResponse", base64.StdEncoding.EncodeToString(samlResponse)) _, err = s.ParseResponse(&req, []string{"id-9e61753d64e928af5a7a341a97f420c9"}) assert.Check(t, err) } saml-0.4.6/testdata/000077500000000000000000000000001415467341100142745ustar00rootroot00000000000000saml-0.4.6/testdata/SP_AuthnRequest000066400000000000000000000012561415467341100172550ustar00rootroot00000000000000https://idp.testshib.org/idp/profile/SAML2/Redirect/SSO?RelayState=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1cmkiOiIvIn0.eoUmy2fQduAz--6N82xIOmufY1ZZeRi5x--B7m1pNIY&SAMLRequest=lJJBj9MwEIX%2FSuR7Yzt10sZKIpWtkCotsGqB%2B5BMW4vELp4JsP8et4DYE5Tr%2BPnN957dbGY%2B%2Bz1%2BmZE4%2Bz6NnloxR28DkCPrYUKy3NvD5s2jLXJlLzFw6MMosg0RRnbBPwRP84TxgPGr6%2FHD%2FrEVZ%2BYLWSl1WVXaGJP7UwyfcxckwTQWEnoS2TbtdB6uHn9uuOGSczqgs%2FuUh3i6DmTaenQjyitGIfc4uIg9y8Phnch221a4YVFjpVflcqgM1sUajiWsYGk01KujKVRfJyHRjDtPDJ5bUShdLrReLNX7QtmysrrMK6Pqem3MeqFKq5TInn6lfeX84PypFSL7iJFuwKkN0TU303hPc%2FC7L5G9DnEC%2Frv8OkmxjjepRc%2BOn0X3r14nZBiAoZE%2FwbrmbfLZbZ%2FC6Prn%2F3zgcQzfHiICYys4zii6%2B4E5gieXsBv5kqBr5Msf1%2F0IAAD%2F%2Fw%3D%3Dsaml-0.4.6/testdata/SP_IDPMetadata000066400000000000000000000224761415467341100167110ustar00rootroot00000000000000 testshib.org TestShib Test IdP TestShib IdP. Use this as a source of attributes for your test SP. https://www.testshib.org/testshibtwo.jpg MIIEDjCCAvagAwIBAgIBADANBgkqhkiG9w0BAQUFADBnMQswCQYDVQQGEwJVUzEV MBMGA1UECBMMUGVubnN5bHZhbmlhMRMwEQYDVQQHEwpQaXR0c2J1cmdoMREwDwYD VQQKEwhUZXN0U2hpYjEZMBcGA1UEAxMQaWRwLnRlc3RzaGliLm9yZzAeFw0wNjA4 MzAyMTEyMjVaFw0xNjA4MjcyMTEyMjVaMGcxCzAJBgNVBAYTAlVTMRUwEwYDVQQI EwxQZW5uc3lsdmFuaWExEzARBgNVBAcTClBpdHRzYnVyZ2gxETAPBgNVBAoTCFRl c3RTaGliMRkwFwYDVQQDExBpZHAudGVzdHNoaWIub3JnMIIBIjANBgkqhkiG9w0B AQEFAAOCAQ8AMIIBCgKCAQEArYkCGuTmJp9eAOSGHwRJo1SNatB5ZOKqDM9ysg7C yVTDClcpu93gSP10nH4gkCZOlnESNgttg0r+MqL8tfJC6ybddEFB3YBo8PZajKSe 3OQ01Ow3yT4I+Wdg1tsTpSge9gEz7SrC07EkYmHuPtd71CHiUaCWDv+xVfUQX0aT NPFmDixzUjoYzbGDrtAyCqA8f9CN2txIfJnpHE6q6CmKcoLADS4UrNPlhHSzd614 kR/JYiks0K4kbRqCQF0Dv0P5Di+rEfefC6glV8ysC8dB5/9nb0yh/ojRuJGmgMWH gWk6h0ihjihqiu4jACovUZ7vVOCgSE5Ipn7OIwqd93zp2wIDAQABo4HEMIHBMB0G A1UdDgQWBBSsBQ869nh83KqZr5jArr4/7b+QazCBkQYDVR0jBIGJMIGGgBSsBQ86 9nh83KqZr5jArr4/7b+Qa6FrpGkwZzELMAkGA1UEBhMCVVMxFTATBgNVBAgTDFBl bm5zeWx2YW5pYTETMBEGA1UEBxMKUGl0dHNidXJnaDERMA8GA1UEChMIVGVzdFNo aWIxGTAXBgNVBAMTEGlkcC50ZXN0c2hpYi5vcmeCAQAwDAYDVR0TBAUwAwEB/zAN BgkqhkiG9w0BAQUFAAOCAQEAjR29PhrCbk8qLN5MFfSVk98t3CT9jHZoYxd8QMRL I4j7iYQxXiGJTT1FXs1nd4Rha9un+LqTfeMMYqISdDDI6tv8iNpkOAvZZUosVkUo 93pv1T0RPz35hcHHYq2yee59HJOco2bFlcsH8JBXRSRrJ3Q7Eut+z9uo80JdGNJ4 /SJy5UorZ8KazGj16lfJhOBXldgrhppQBb0Nq6HKHguqmwRfJ+WkxemZXzhediAj Geka8nz8JjwxpUjAiSWYKLtJhGEaTqCYxCCX2Dw+dOTqUzHOZ7WKv4JXPK5G/Uhr 8K/qhmFT2nIQi538n6rVYLeWj8Bbnl+ev0peYzxFyF5sQA== urn:mace:shibboleth:1.0:nameIdentifier urn:oasis:names:tc:SAML:2.0:nameid-format:transient MIIEDjCCAvagAwIBAgIBADANBgkqhkiG9w0BAQUFADBnMQswCQYDVQQGEwJVUzEV MBMGA1UECBMMUGVubnN5bHZhbmlhMRMwEQYDVQQHEwpQaXR0c2J1cmdoMREwDwYD VQQKEwhUZXN0U2hpYjEZMBcGA1UEAxMQaWRwLnRlc3RzaGliLm9yZzAeFw0wNjA4 MzAyMTEyMjVaFw0xNjA4MjcyMTEyMjVaMGcxCzAJBgNVBAYTAlVTMRUwEwYDVQQI EwxQZW5uc3lsdmFuaWExEzARBgNVBAcTClBpdHRzYnVyZ2gxETAPBgNVBAoTCFRl c3RTaGliMRkwFwYDVQQDExBpZHAudGVzdHNoaWIub3JnMIIBIjANBgkqhkiG9w0B AQEFAAOCAQ8AMIIBCgKCAQEArYkCGuTmJp9eAOSGHwRJo1SNatB5ZOKqDM9ysg7C yVTDClcpu93gSP10nH4gkCZOlnESNgttg0r+MqL8tfJC6ybddEFB3YBo8PZajKSe 3OQ01Ow3yT4I+Wdg1tsTpSge9gEz7SrC07EkYmHuPtd71CHiUaCWDv+xVfUQX0aT NPFmDixzUjoYzbGDrtAyCqA8f9CN2txIfJnpHE6q6CmKcoLADS4UrNPlhHSzd614 kR/JYiks0K4kbRqCQF0Dv0P5Di+rEfefC6glV8ysC8dB5/9nb0yh/ojRuJGmgMWH gWk6h0ihjihqiu4jACovUZ7vVOCgSE5Ipn7OIwqd93zp2wIDAQABo4HEMIHBMB0G A1UdDgQWBBSsBQ869nh83KqZr5jArr4/7b+QazCBkQYDVR0jBIGJMIGGgBSsBQ86 9nh83KqZr5jArr4/7b+Qa6FrpGkwZzELMAkGA1UEBhMCVVMxFTATBgNVBAgTDFBl bm5zeWx2YW5pYTETMBEGA1UEBxMKUGl0dHNidXJnaDERMA8GA1UEChMIVGVzdFNo aWIxGTAXBgNVBAMTEGlkcC50ZXN0c2hpYi5vcmeCAQAwDAYDVR0TBAUwAwEB/zAN BgkqhkiG9w0BAQUFAAOCAQEAjR29PhrCbk8qLN5MFfSVk98t3CT9jHZoYxd8QMRL I4j7iYQxXiGJTT1FXs1nd4Rha9un+LqTfeMMYqISdDDI6tv8iNpkOAvZZUosVkUo 93pv1T0RPz35hcHHYq2yee59HJOco2bFlcsH8JBXRSRrJ3Q7Eut+z9uo80JdGNJ4 /SJy5UorZ8KazGj16lfJhOBXldgrhppQBb0Nq6HKHguqmwRfJ+WkxemZXzhediAj Geka8nz8JjwxpUjAiSWYKLtJhGEaTqCYxCCX2Dw+dOTqUzHOZ7WKv4JXPK5G/Uhr 8K/qhmFT2nIQi538n6rVYLeWj8Bbnl+ev0peYzxFyF5sQA== urn:mace:shibboleth:1.0:nameIdentifier urn:oasis:names:tc:SAML:2.0:nameid-format:transient TestShib Two Identity Provider TestShib Two http://www.testshib.org/testshib-two/ Nate Klingenstein ndk@internet2.edu saml-0.4.6/testdata/SP_SamlResponse000066400000000000000000000312321415467341100172350ustar00rootroot00000000000000https://idp.testshib.org/idp/shibbolethMIIB7zCCAVgCCQDFzbKIp7b3MTANBgkqhkiG9w0BAQUFADA8MQswCQYDVQQGEwJVUzELMAkGA1UE CAwCR0ExDDAKBgNVBAoMA2ZvbzESMBAGA1UEAwwJbG9jYWxob3N0MB4XDTEzMTAwMjAwMDg1MVoX DTE0MTAwMjAwMDg1MVowPDELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkdBMQwwCgYDVQQKDANmb28x EjAQBgNVBAMMCWxvY2FsaG9zdDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA1PMHYmhZj308 kWLhZVT4vOulqx/9ibm5B86fPWwUKKQ2i12MYtz07tzukPymisTDhQaqyJ8Kqb/6JjhmeMnEOdTv SPmHO8m1ZVveJU6NoKRn/mP/BD7FW52WhbrUXLSeHVSKfWkNk6S4hk9MV9TswTvyRIKvRsw0X/gf nqkroJcCAwEAATANBgkqhkiG9w0BAQUFAAOBgQCMMlIO+GNcGekevKgkakpMdAqJfs24maGb90Dv TLbRZRD7Xvn1MnVBBS9hzlXiFLYOInXACMW5gcoRFfeTQLSouMM8o57h0uKjfTmuoWHLQLi6hnF+ cvCsEFiJZ4AbF+DgmO6TarJ8O05t8zvnOwJlNCASPZRH/JmF8tX0hoHuAQ==i/wh2ubXbhTH5W3hwc5VEf4DH1xifeTuxoe64ULopGJ0M0XxBKgDEIfTg59JUMmDYB4L8UStTFfqJk9BRGcMeYWVfckn5gCwLptD9cz26irw+7Ud7MIorA7z68v8rEyzwagKjz8VKvX1afgec0wobVTNN3M1Bn+SOyMhAu+Z4tE=a6PZohc8i16b2HG5irLqbzAt8zMI6OAjBprhcDb+w6zvjU2Pi9KgGRBAESLKmVfBR0Nf6C/cjozCGyelfVMtx9toIV1C3jtanoI45hq2EZZVprKMKGdCsAbXbhwYrd06QyGYvLjTn9iqako6+ifxtoFHJOkhMQShDMv8l3p5n36iFrJ4kUT3pSOIl4a479INcayp2B4u9MVJybvN7iqp/5dMEG5ZLRCmtczfo6NsUmu+bmT7O/Xs0XeDmqICrfI3TTLzKSOb8r0iZOaii5qjfTALDQ10hlqxV4fgd51FFGG7eHr+HHD+FT6Q9vhNjKd+4UVT2LZlaEiMw888vyBKtfl6gTsuJbln0fHRPmOGYeoJlAdfpukhxqTbgdzOke2NY5VLw72ieUWREAEdVXBolrzbSaafumQGuW7c8cjLCDPOlaYIvWsQzQOp5uL5mw4y4S7yNPtTAa5czcf+xgw4MGatcWeDFv0gMTlnBAGIT+QNLK/+idRSpnYwjPO407UNNa2HSX3QpZsutbxyskqvuMgp08DcI2+7+NrTXtQjR5knhCwRNkGTOqVxEBD6uExSjbLBbFmd4jgKn73SqHStk0wCkKatxbZMD8YosTu9mrU2wuWacZ1GFRMlk28oaeXl9qUDnqBwZ5EoxT/jDjWIMWw9b40InvZK6kKzn+v3BSGKqzq2Ecj9yxE7u5/51NC+tFyZiN2J9Lu9yehvW46xRrqFWqCyioFza5bw1yd3bzkuMMpd6UvsZPHKvWwap3+O6ngc8bMBBCLltJVOaTn/cBGsUvoARY6Rfftsx7BamrfGURd8vqq+AI6Z1OC8N3bcRCymIzw0nXdbUSqhKWwbw6P2szvAB6kCdu4+C3Bo01CEQyerCCbpfn/cZ+rPsBVlGdBOLl5eCW8oJOODruYgSRshrTnDffLQprxCddj7vSnFbVHirU8a0KwpCVCdAAL9nKppTHs0Mq2YaiMDo8mFvx+3kan/IBnJSOVL19vdLfHDbZqVh7UVFtiuWv3T15BoiefDdF/aR5joN0zRWf8l6IYcjBOskk/xgxOZhZzbJl8DcgTawD8giJ31SJ1NoOqgrSD4wBHGON4mInHkO0X5+vw1jVNPGF3BwHw0kxoCT3ZKdSsi8O4tlf1y227cf794AGnyQe13O032jYgOmM5qNkET6PyfkyD/h0ufgQq2vJvxSOiRv76Kdg0SeRuNPW9MyjO/5APHl7tBlDBEVq+LWDHl4g9h/bw+Fsi0WN4pLN1Yv9RANWpIsXWyvxTWIZHTuZEjNbHqFKpsefx/oY1b9cSzKR5fQ9vc32e17WykL0O7pwpzV6TrFN874GdmW5lG5zfqnRHUQh1aV2WwBJ74mB4tv/y5rmRjTe5h/rN90kN+eQGeR3eG7XUHLhK/yCV+xq8KKPxNZexcdHGA905rvYokbtmr/jIN5kAMBdlOU8akPAZdSMMh+g/RZo5MO50/gdg6MTpB4onU2FBd54FNDp2fuBUxBsnTqpZXkDcAPEfSBr+z2l8jTRmxMricWyeC55ILgxM4er68n0xYjwb2jyQum3IQq7TSYYU/qjNiH1fQBtdRmBkzXJYYk+9q7C6OZJUdR96ERnTIi93NaYmtpSEvZU9vS6MV1VBOnEf8UzUUT9ibMpP9XDSINX7dN24rKIufSY+3+70orQB07XOWp6++SWKgA+WThaoPhp8sWWMeSZuda/wq6jdVTAB8FOPiP3lNl0BqxagQEPmNxDWXwTplSFSR3SP0e4sHMSjLvysibV9Z87LZa1FG0cWU2hrhiyOLsIWMnd4vdTLaWjhXuGlrDShxSAiI39wsl5RB59E+DXVSTBQAoAkHCKGK69YiMKU9K8K/LeodApgw46oPL08EWvleKPCbdTyjKUADtxfAujR84GMEUz9Aml4Q497MfvABQOW6Hwg54Z3UbwLczDCOZyK1wIwZTyS9w3eTH/6EBeyzhtt4G2e/60jkywHOKn17wQgww2ZsDcukdsCMfo4FV0NzfhSER8BdL+hdLJS3R1F/Vf4aRBEuOuycv2AqB1ZqHhcjZh7yDv0RpBvn3+2rzfzmYIBlqL16d1aBnvL4C03I0J59AtXN9WlfJ8SlJhrduW/PF4pSCAQEyHGprP9hVhaXCOUuXCbjA2FI57NkxALQ2HpCVpXKGw0qO0rYxRYIRlKTl43VFcrSGJdVYOFUk0ZV3b+k+KoxLVSgBjIUWxio/tvVgUYDZsO3M3x0I+0r9xlWZSFFmhwdOFouD+Xy1NPTmgwlUXqZ4peyIE1oVntpcrTJuev2jNScXbU9PG8b589GM4Z09KS/fAyytTFKmUpBuTme969qu0eA7/kBSHAkKvbfj0hsrbkkF9y/rXi8xgcMXNgYayW8MHEhm506AyPIvJAreZL637/BENO1ABdWS1Enj/uGaLM1ED8UY94boh/lMhqa9jALgEOHHxspavexi3HIFwJ55s4ocQnjb4p6op4CRPUdPCfli5st9m3NtQoH9kT1FTRZa9sG8Ybhey5wP17YgPIg9ZZtvlvpSTwCwZxHZ348wXJWhbtId9DyOcIzsyK5HaJcRsp8SQVR5nbRW0pUyC/bFAtX1KOGJmtro/QfmnLG9ksuaZvxP6+bH1K+CibEFIRDllAUFFPiuT+2b3Yp3Tu1VvXokMAgmcB5iFDgTAglw5meJYJ99uIBmj0EVZm8snMhRrHjMPTAYD5kwPK/YDShPFFV3XEIFzLD3iYrzb7sub/Z4gTTELWzzS3bCpYPAh4KWeTih+p7Xj0Xf04nSONHZXsQnNenc+PNae+Zj5iCfJ/PpqhMn61n/YBP7gipYYEtOZYzDtvMz+mytYRUOaZTq3W4Wp64f+XVekn49CLarLm6qPyiz5kJwaT8lJ+VEZDPpS/ChLM4eq90GogJBvK0jxmQ1AGvnKpV2lw9XCudf3PXbaTb+r2QPcihKnmqcEgPgYlN8VLclicNW1WyjBJ+HvDTQPbs1r1/KnBK4O5HTT6ehuHpJsYlBN9vzjsD+ov6SRkBqiGPUg9CoKKmWS6dirxwOXi3OUFzkWFVDyDezfkJAzqkmG0nlEGb9mTHdVDfX010bPJ4ZQzQSyHp7Ht2mATyQwOEem2AMB/RpNwlOKXWIdsQ5p3dHF+kmsJHI8xjEv2GeUa/aXX3MF3fPfUA7La8J8fbnaDLbnEqMCLMfdfc9+kY7EKyqPiE5KFpF0EhQBrHl8SiPuFQCoxvlH2u+ujncW7Z5JiBmMKUWOXUHhIe4NckP1awRsEcfhEs664DqOp9CbLwTXk71hHVBtINylFcf7uBZwjxNW+hCfZEoVEjjs/V4J9QeXCxpTu5TcXxBxwN5zBdkCodNFPLUg+3UicaykaH0+wrGoTu/ugjF9rz7OezMMs3pep+bzLp+yZbFAL/z/yATY3UG+lpk6Rw4SkjbnAxBSedaEdqbotddkGzVQubHvHqCiKpkAw58rAa2v15hc+UmkrRFslS8SYxTIPXs2sTNhnCCrUn8nlKufeoAm65vgYtEQ4NzmG9tqKtTeBfZAvSToYaiQq+kPii1ssuu1OULAVuSx8x/CYO6orgX7h5wI0R/Ug1nux7cb2/+pFLbNyGvwKf1TLym2NvFMJpvFlTsOJJ4DxXM/v2JkC9umm93quXLsojx7KTEOFDQLsnMKsVo6ZzRQidEwK5gQPyZL1yjGirJcEuGMAEf6LA2AsKIIZhsMEPlLpzMiVo5Y0LoL6NFsXigceLaaJMEMuYNJJdh+uxyfW57+PoQ7V8KkzSHFsKan14GnpWeOV7r13uopwCPeIsEKUVG77ypd+ILQkbKxH2lQdsFyjpofqkbgEVM5XAnVbdhfwyebNHn5OJtadVkOMcJc/WMWJef1idcSfvP5ENkwp3pKg9Ljoi+hU2Chp1vTmksO2HJt0of4QnQ8jGlcqnOrAMiWUCd2W/8AmhRBjevt3UqxnqELVvg+HJPlyqFyuUlDxx25mXEdW0COpA3s9OlSgcMjvQbIJ42NUhGFZLoK1pvPLZo711w2Ex3Lm5qqcr/7I4+vTntd/Id5aJiP18LQpslTy614Wd4eD8+RfjEtmDAPXhgvfekVkS/rDnI/9H0k3AdHc78fJCJRPNwJrDTozzjxTvmVv9r4MtpoDELmnMxb3o7ZibUMxgptCTyDF+Q5m6T3GeD9G5ehgB3Tqsx3gcUGuDtP6KIqMGbj8YCFt8tjihDctYFAXj4AwPnIjMiI4T7skXwfrBLWCKfN1j5XrIn2paQgKln9hvaiRUpNpD3IXVyFl1WNrb21IcRinfkuCtrP2tTHqct6eSEh8sOzRkvZEArBQYD5paYyuNBcbVtsnl6PNE+DIcSIGvCVnzpMw1BeUExvQZoNdpHwhTQ3FSd1XN1nt0EWx6lve0Azl/zJBhj5hTdCd2RHdJWDtCZdOwWy/G+4dx3hEed0x6SoopOYdt5bq3lW+Ol0mbRzr1QJnuvt8FYjIfL8cIBqidkTpDjyh6V88yg1DNHDOBBqUz8IqOJ//vY0bmQMJp9gb+05UDW7u/Oe4gGIODQlswv534KF2DcaXW9OB7JQyl6f5+O8W6+zBYZ6DAL+J2vtf3CWKSZFomTwu65vrVaLRmTXIIBjQmZEUxWVeC4xN+4Cj5ORvO8GwzoePGDvqwKzrKoupSjqkL5eKqMpCLouOn8n/x5UWtHQS1NlKgMDFhRObzKMqQhS1S4mz84F3L492GFAlie0xRhywnF+FvAkm+ZIRO0UqM4IwvUXdlqTajjmUz2T0+eXKTKTR5UoNRgP51gdUMT5A4ggT5wU9WkRx7CR9KdWJwwcWzv2YrchoHIXBidQSk+f1ZSzqR7krKSOwFTVJUvEenU17qVaHoAf2he0dMgURJ8PM9JxnSr7p2pZeNPu/O5oPmLuOCmEPVRPSahJL7yj9PK5z3q57e5POIp/wXqFoniFdxRmtmpfZBxoKVlADkwRy34h8k6ZmgtqPTQfUUk/+yH2CAoQu+HyOtUnQof8vc1k4zs8nCTrCSjqvFPjU8mHtVHy1RY0qmK9t99ugXyAKaGON3PlseetIC8WCTt84nM5XGD3VQpbv139yhSPhp2Oiz0IiOsr+L9idVKSvfNSkdNq9aUC7963uAQNud8c4GuDmbENvZYvGNIMxxZhYA86n1RMNtGDZJs6/4hZTL18Kz1yCY9zbbSXTxWTmkaHJziHtgrEPoYpUeb85J229PDEX08yHOkj2HXVdnKKmEaHw3VkB4eM3PhGGdrw2CSUejSaqPQFLdhabcB2zdB4lj/AUnZvNaJc23nHHIauHnhhVrxh/KQ1H4YaYKT9ji/69BIfrTgvoGaPZC10pQKinBHEPMXoFrCd1RX1vutnXXcyT2KTBP4GG+Or0j6Sqxtp5WhxR0aJqIKM6LqMHtTooI0QhWbmSqDEBX/wRS70csVeJSrZ4dqRKit+hz8OalHA7At9e+7gSWTfHAwjl5JhtrltyAab/FII4yKQeZWG8j1fSFGHN+EbOrum2uWuVhxkUPy4coMu+yKY4GxlXfvP+yEVK5GrMECRmFBlySetJK3JOoQXiuLirlHUq+0u88QFMdAJ9+fIdU4+FxneqgW7qM7CHRE8jV4pPSWGFbGzxVZ9CWRWaYIw26VsC1qQJe1WmU7Mrp26IxmWHGwHvZ50uB0mjAHFCiln5QAvqTm2/fsY+Puk+Irt3LQbMwGVWPnb4eona2dSha+eMLOiAQkBvbaitsRqqrAVnndP7gHmO+nYZEKNx/740zTRrFBpOelrGdOa0/eV2mPhUQfozGooxoRADmT8fAcDXo0SsXCHzg9tBnmVMvInQ7+8nXfhcF/fEBjvW3gIWOmp2EWutHQ/sl73MieJWnP/n3DMk2HHcatoIZOMUzo4S4uztODHoSiOJDA1hVj7qADvKB37/OX0opnbii9o6W8naFkWG5Ie7+EWQZdo+xeVYpwGOzcNwDRrxbZpV3fTvWyWKToovncZq+TQj7c4Yhz6XDF0ffljN5hTm4ONwYViFNB4gTJlFxFX00wcWfwWah4uJs2Oa8dHPVT+7viagZiPrSDk/gythdY8glGm+F0DWlzQpWbgSI3ZbdiUQ+ox4GtLUtYgGIQFUvRYbuHqH6CXQ3SM6vkbhV/nAn6UDEWKXdJsO0u5q6UpXci7MlWDNLxoQ9dfGjSc28mX+q+4hkyho4u1XSMy9B6IdH304J7fuAQ88tTorT67AiqvqR6qnZ0icV+MMLh95moxFbrvch6sGAmMEixqeujmiZzBqBmNbzZVORiv9qcbe3CQ6X2i+9D8hMpaWj5jI0u+0wk3bRFK4uDn8T1mnD6l4TrJayf3cZI+duhKcabNj71i5w76S8RZSC6RX4ks0x+XIDc5v3223NmGvceYklbuOJtJa0/MBTOcSDKCM2kUXqPV2BlA9Za8WEO2UrdcyP+AXgM20af3thjlZvA494zdZ0mqjrsKp+VS2MVrBBtj+puSuSHJYf6bnA5/yjqQtbGvAp8hfXQURC53J5oD8rb9F7vQRqdfqpe6xd7DVd+wWZS86mWjyZYKXw312t8nM/gxo0pdvZ8F0x9y3xb9UBM2pZtdYvk3hPz6swhuE1N5j2u7nwtXuEDNcGCSfr+IempeFHFRqO8n8ikASEdKcq2XHGJwfc3lVXOQ5K4JlewcC7yQL1uNtL6iNKCtJmjJiH2PMmXrtpmCeTspFNZlwmiICyPWV9B5ce9H/qP1xjndBzFz0rn75SGDnWUhNZI/aYKNVyzkOleS5VSNxBx1hoiFuG8r+6ctYwF7XL94b95tXQ/+0V5dt0H1xVaOZ7QluoDtMSzuUjV4yUoQESa3zCfZwnW+b5SKndX5nx0GYrVxydMkUdfimZpX/fezcMiaAGwG/jgWF0zS+EL4T7gR8I5R3qUNTifKFJKJL1+AL8CgL+SRB1lgHDp2wQ7cqgqcmskAsT60qisL/UZGgmnlgZ8FkNhv0vAMkzIsz7o6cuLo15hZnrsZveIo+mZKY2cMJjJb4ZlJLcE+YcnpiM84OYjypa9lA7kv4XJaDX9oirhsl9IO/ImbFgYpR73y+xSolXYdDKfZjf/8NR7vE8fu+LYXGoZHO/hxousED6y3sCo/ItECYHWYIui+V5SmAoEvVV8FY8fFMYIc+Llc2CoX5HQISfUAtLu+fGNNV0muidXnBdtnJo25UEqxwvoENdI1lGPhlrXY6/h4kIT5djmsxxSG/EgG/4fPnrThgF9/fbG8n/3LweXvQOGjX0F1Ngt5wuMIWRQk5vtLdvv2M+BNwthHZ7xzIU7zqSVvngVPwgcsTr2d5pTVOxauT1K6ffiBF04jVZEcna+NXhJM5EcRHNuT/iOb0ncn1yuKU8JJnztEzMDjO1qCmaBTyWBR7nQS6K+nfstd/AnBWyGeC5Yi3wlvZAVMpc0m7I7McXb+rXiHM0mHoq0Z/2HOki5LP2cBuIkk84tJ3SRZwWnocrz4aTEIOmwftqMATy5Ur0KRxoUSFNMJYyc1iOfjk3H2JjgecWlQdYHcIEjxGDGeo4S9EKTRokMGNUN2nTj3SO2nHoWbx9WhGe6uB3OgDENGL9aNoPnYKXs4WcobctMxQjjBWa/zpCFwP8nr78xIFfy/64ZtsFBrxSrEHxeXiPa2Kpv456aQ9kDQjJt9XrWKe+JBawtpPUYHmWkUb3Gznp3tC2LbowvJlEe/17srb5yi+sUHEF1z/8Uk4eVYcUUXzyq3YEuqumIBIYqO8J3K5Us7tEXyzhHH8TMLNSQxmDi/w5oYccIwNFMM1+xRTsyjHHtB/rHYJjPW/50Xxb0CZF84NqotCcgIMrR4nUiPnAPd8ZvHeB/235gS1NtzBWtfcDmP8khibSQpY3JW+fdY/9W6iGlPyPIwOgH06fJayaT44sPFIm+QGIkPKSAJOFDeJNG8oc6SAqrYSfCffYfOAx3IsjSdnxQy9JAcS0HxjWnEO3rgSh7bNEecO3f4hb3TRNlczdzhfrwgxUZ0rURI3LfMCpGntF+8NrhtB7RT8sEOaa4NM13T7LWjykRQJFYKNZY0siPBP2WJxjBqL0KynlTPhAcfFyiLZbAhe7YC0XmYo8iJQqdzJQwBK9iOoDkg1XuGy7+Kfe0scamvHN2Z85umcPSiPEQRP3zAWcP5kRNDath7DKrBfQtvOJvEHiihE+qiASrCZep+m7jTD261U9vQGAnR4xBY08ChSh8XItWHvDHARN+GP08h9u6nlJ3rpOoVn9y22NNgx7bOe6QIYe9f6iYbbAzLR1/7AP1A4CQwFi39eZI9BZteze5eas+6JR2s1LqH9tncOmWAhXjE8p3hOtplh/tMbrx+pySNX4BKfZva54zccIa+e59NUifTRsq27AwAtcxg2Bk1Tu7B+LT9Yw2K8tRH6XTcGlvqDM4sYjNBqzh3yAga5iro706tg/Qaa50eln8rjISularEHlfaggogjvd+wNLg44Rj8pMr25+xxS0e9KoEGon5SutuhJ/HBGnEj3+4qNxHu27nkAmZIADiF+Jh53osDuA1fsUnRXf2lJABa30KDkG8E/eci+TkESrdfsPMo6yhWoyjtjYdJbGkjtsQCMW5DOSNYDH0FqDiiVU0nBLJ4+A4ep6aWTrv6w/ozuO4educ7x9IBpGmEY30rsXWwiGJbLGyIo+6qz6J5JBKdjNBsDO7RRweDNMp8ospaGNQSa4NKAHTG8BsGqJSP8oebpVqYpgPS1TiBWnYZKQSRJ5NFs+ULpdICekxevVXAH8uh+De9GT7KsJJzg0CFjALDbC0YrbmCigspJAh2455I6/xyWbPXCYMXwBzbioMgWcNhQBJJ6oIoQ7shwf2TP0Z+X/3NoMpWHmGpoV/JZind8lb9lcxoI44uf37+xc03O1R1bNucf0F5ljrgj2sZlGz/591EJen5GZhrT6qSTIcMu+xIyxyA/zzhy0jjkVfkDKfQ8mE9AmVtbbzHAQNy2PhDIeu7ngoFN635tSOJLR2c6pC/m6n50slFbo0oeHbbiGHyxDk7q3zXHWoHzeF1k4iVdHumYg/nwZOuRzms6rvkmwkJv59Z1p05jxA+Y0yHvDeq1WR8PfS/esm3RHfP3fM+zTlj9ZBJfzvn4OL+IIHRQ5l8pGKAeRL58OjeaU5QU98lAKHydOPDGBalsEHyIKD6iy3RZ65qIm956zQd98htZ1Vgkd7LVC7LSnLb9jRbqS1vHN7lR6bQMmXtQBYSA/+ZW2RQqSo7sToVh+Pxl3EVmsgyO8dXPL4biz7XM8eVz7CqHkrQUinnr79HJWC6Uk19cBurOD6PeOqNYy08Og/A0hbHOgN3dKmVRAPf7itK6x0eb5F70T2zVqG12GHVZieXwIcp/vahuFvriHLJtuM04laiRWNXSiL2MPHQ8e9rr8NIlWDm9uev55FI9zZxwFUPBSewawPe5vkqRLfwZCYd5mZoxtBhNBWvY3ZOVD/21dIUlQanG1n6RygbmAwCHnIB4c7EH2CBYEMDToRQuAuIssviIfdaJglwDgHbLWKNUVDOdqeclBNZjfQfVXbVukPk8DfWLqj9pD4xAOzDeVQcdmg2aLvNKgpZsWs4d+6GlKrpS7qEGvoBkIFh/cVY7DMYrt/JXYuF6DpwB+HbfnuDFc2p47SPNhnmt/ez6/DACBPQ+tgpyWYXUsiviGSp72JNTzd8uFJJZNeKUJZw1c0UTjxdwigh5tL/hWhPl48DY937zymSr1xVqC3RV6wSIpuplH+hss/rsRPAp1/TfxvhJuFsoPbW0586y9YzqEHT4FUu6WSRy0gMJLP2sLqiiZXZ6kPicXsW7M55mV3ugbGQjB7YS7EVqsQzvJTiQbOlcPqwoKK7DTqaeCOXd8kH1tNoe7hjx/UNNdLQQ7IhrJIzxqTTgwcXYMCxhoezDsIHReTIymsHPkCurfteTQcbfwoKN5E9zC2hINOPmhAxLvONzaLXQGMqofuTbFshkB4eUj8U4vBCNp+60iCLnibt4rPuyoWKEHWBYa6FfIykxVKuXkfcb64dCdGCWjv7x1XqkbpHxQB80qhipoSo244pyhIsN91ASu1Q7L75LxGXibY3jb0Y4KZ5zIWsH4kVlvPhangohDO1J9gmL9inGr9hy5BHTQiMcktGoUgOIbFJ72381vYpPxn3ngBbp48mVZd0w6xV8RBaqR3l7CxI9vvMAPYPoXBB18ERoZypza8mAlzv2QxIkNGuRzFENh1SXegBfN7eiazZnwnhbyeMghJpnXzfvHACyjkdH3shRYcJ+oMiOSpInGxm/hxFQxHJZA0Ft/lzasaml-0.4.6/testdata/TestCanParseMetadata_metadata.xml000066400000000000000000000025041415467341100226540ustar00rootroot00000000000000Required attributessaml-0.4.6/testdata/TestCanProduceMetadataEntityID_metadata000066400000000000000000000015401415467341100240150ustar00rootroot00000000000000 saml-0.4.6/testdata/TestCanProduceMetadataNoCerts_metadata000066400000000000000000000015321415467341100237020ustar00rootroot00000000000000 saml-0.4.6/testdata/TestCanProduceSPMetadata_expected000066400000000000000000000054301415467341100226710ustar00rootroot00000000000000 MIIB7zCCAVgCCQDFzbKIp7b3MTANBgkqhkiG9w0BAQUFADA8MQswCQYDVQQGEwJVUzELMAkGA1UE CAwCR0ExDDAKBgNVBAoMA2ZvbzESMBAGA1UEAwwJbG9jYWxob3N0MB4XDTEzMTAwMjAwMDg1MVoX DTE0MTAwMjAwMDg1MVowPDELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkdBMQwwCgYDVQQKDANmb28x EjAQBgNVBAMMCWxvY2FsaG9zdDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA1PMHYmhZj308 kWLhZVT4vOulqx/9ibm5B86fPWwUKKQ2i12MYtz07tzukPymisTDhQaqyJ8Kqb/6JjhmeMnEOdTv SPmHO8m1ZVveJU6NoKRn/mP/BD7FW52WhbrUXLSeHVSKfWkNk6S4hk9MV9TswTvyRIKvRsw0X/gf nqkroJcCAwEAATANBgkqhkiG9w0BAQUFAAOBgQCMMlIO+GNcGekevKgkakpMdAqJfs24maGb90Dv TLbRZRD7Xvn1MnVBBS9hzlXiFLYOInXACMW5gcoRFfeTQLSouMM8o57h0uKjfTmuoWHLQLi6hnF+ cvCsEFiJZ4AbF+DgmO6TarJ8O05t8zvnOwJlNCASPZRH/JmF8tX0hoHuAQ== MIIB7zCCAVgCCQDFzbKIp7b3MTANBgkqhkiG9w0BAQUFADA8MQswCQYDVQQGEwJVUzELMAkGA1UE CAwCR0ExDDAKBgNVBAoMA2ZvbzESMBAGA1UEAwwJbG9jYWxob3N0MB4XDTEzMTAwMjAwMDg1MVoX DTE0MTAwMjAwMDg1MVowPDELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkdBMQwwCgYDVQQKDANmb28x EjAQBgNVBAMMCWxvY2FsaG9zdDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA1PMHYmhZj308 kWLhZVT4vOulqx/9ibm5B86fPWwUKKQ2i12MYtz07tzukPymisTDhQaqyJ8Kqb/6JjhmeMnEOdTv SPmHO8m1ZVveJU6NoKRn/mP/BD7FW52WhbrUXLSeHVSKfWkNk6S4hk9MV9TswTvyRIKvRsw0X/gf nqkroJcCAwEAATANBgkqhkiG9w0BAQUFAAOBgQCMMlIO+GNcGekevKgkakpMdAqJfs24maGb90Dv TLbRZRD7Xvn1MnVBBS9hzlXiFLYOInXACMW5gcoRFfeTQLSouMM8o57h0uKjfTmuoWHLQLi6hnF+ cvCsEFiJZ4AbF+DgmO6TarJ8O05t8zvnOwJlNCASPZRH/JmF8tX0hoHuAQ== saml-0.4.6/testdata/TestIDPCanHandlePostRequestWithExistingSession_http_response_body000066400000000000000000000264201415467341100313610ustar00rootroot00000000000000
saml-0.4.6/testdata/TestIDPCanHandleRequestWithExistingSession_decodedRequest000066400000000000000000000012001415467341100275260ustar00rootroot00000000000000https://sp.example.com/saml2/metadatasaml-0.4.6/testdata/TestIDPCanHandleRequestWithExistingSession_http_response_body000066400000000000000000000264201415467341100305130ustar00rootroot00000000000000
saml-0.4.6/testdata/TestIDPCanHandleRequestWithNewSession_http_response_body000066400000000000000000000012551415467341100274510ustar00rootroot00000000000000RelayState: ThisIsTheRelayState SAMLRequest: https://sp.example.com/saml2/metadatasaml-0.4.6/testdata/TestIDPCanHandleUnencryptedResponse_idp_metadata.xml000066400000000000000000000024201415467341100264620ustar00rootroot00000000000000Required attributessaml-0.4.6/testdata/TestIDPCanHandleUnencryptedResponse_request000066400000000000000000000007601415467341100247440ustar00rootroot00000000000000 https://gitlab.example.com/users/saml/metadatasaml-0.4.6/testdata/TestIDPCanHandleUnencryptedResponse_response000066400000000000000000000155651415467341100251230ustar00rootroot00000000000000 https://idp.example.com/saml/metadata EJWYGjZq4zltPha+UU/Pcqs+JSc= C4qEE/hh8tqaM47F6VK9toHJqQxnzzzfwxIc5IUOO1izD/vIFfn4OwKw/SfCFhYj8ZgnVM/BF3oaiWhuAMgFS+MKz2RYnY5h0+DUb1Mv4SjtEPQIv+TL/LGsMJuzPoEkXcxXefz2JCJMXeYM66PfeuBxRpETIe2zIJzZhd9mIrs= MIIB7zCCAVgCCQDFzbKIp7b3MTANBgkqhkiG9w0BAQUFADA8MQswCQYDVQQGEwJVUzELMAkGA1UECAwCR0ExDDAKBgNVBAoMA2ZvbzESMBAGA1UEAwwJbG9jYWxob3N0MB4XDTEzMTAwMjAwMDg1MVoXDTE0MTAwMjAwMDg1MVowPDELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkdBMQwwCgYDVQQKDANmb28xEjAQBgNVBAMMCWxvY2FsaG9zdDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA1PMHYmhZj308kWLhZVT4vOulqx/9ibm5B86fPWwUKKQ2i12MYtz07tzukPymisTDhQaqyJ8Kqb/6JjhmeMnEOdTvSPmHO8m1ZVveJU6NoKRn/mP/BD7FW52WhbrUXLSeHVSKfWkNk6S4hk9MV9TswTvyRIKvRsw0X/gfnqkroJcCAwEAATANBgkqhkiG9w0BAQUFAAOBgQCMMlIO+GNcGekevKgkakpMdAqJfs24maGb90DvTLbRZRD7Xvn1MnVBBS9hzlXiFLYOInXACMW5gcoRFfeTQLSouMM8o57h0uKjfTmuoWHLQLi6hnF+cvCsEFiJZ4AbF+DgmO6TarJ8O05t8zvnOwJlNCASPZRH/JmF8tX0hoHuAQ== https://idp.example.com/saml/metadata XPlQkPZr16jJADNHhQ/sma8PBC4= zDZndnR6twoH0l7j5Qv7hrWxszt+UYSpJ07L0bnN9kD/3jUFkSStok5ubRP5rvOLH6cg4sQX97VuU7EPAmNhj9XcEH7hGMkAAxV/9pbrocSMAm4+HgpyoVl4NSvh9HVWA7tq2WMBgNl6qi05xGws2Fr+zlsax7yr9/hQKdNXL04= MIIB7zCCAVgCCQDFzbKIp7b3MTANBgkqhkiG9w0BAQUFADA8MQswCQYDVQQGEwJVUzELMAkGA1UECAwCR0ExDDAKBgNVBAoMA2ZvbzESMBAGA1UEAwwJbG9jYWxob3N0MB4XDTEzMTAwMjAwMDg1MVoXDTE0MTAwMjAwMDg1MVowPDELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkdBMQwwCgYDVQQKDANmb28xEjAQBgNVBAMMCWxvY2FsaG9zdDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA1PMHYmhZj308kWLhZVT4vOulqx/9ibm5B86fPWwUKKQ2i12MYtz07tzukPymisTDhQaqyJ8Kqb/6JjhmeMnEOdTvSPmHO8m1ZVveJU6NoKRn/mP/BD7FW52WhbrUXLSeHVSKfWkNk6S4hk9MV9TswTvyRIKvRsw0X/gfnqkroJcCAwEAATANBgkqhkiG9w0BAQUFAAOBgQCMMlIO+GNcGekevKgkakpMdAqJfs24maGb90DvTLbRZRD7Xvn1MnVBBS9hzlXiFLYOInXACMW5gcoRFfeTQLSouMM8o57h0uKjfTmuoWHLQLi6hnF+cvCsEFiJZ4AbF+DgmO6TarJ8O05t8zvnOwJlNCASPZRH/JmF8tX0hoHuAQ== https://gitlab.example.com/users/auth/saml/metadata urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport alice saml-0.4.6/testdata/TestIDPIDPInitiatedExistingSession_response000066400000000000000000000261541415467341100246700ustar00rootroot00000000000000
saml-0.4.6/testdata/TestIDPMakeResponse_request_buffer000066400000000000000000000011521415467341100231100ustar00rootroot00000000000000 https://sp.example.com/saml2/metadata urn:oasis:names:tc:SAML:2.0:nameid-format:transientsaml-0.4.6/testdata/TestIDPMakeResponse_response.xml000066400000000000000000000045541415467341100225350ustar00rootroot00000000000000 https://idp.example.com/saml/metadata KsbyS2V2/QCarAksPQyV5s3PVDk= paj/Jq/TTvYXu35Jtyevmu8bn2DZecfaj/wu8l7mY2sN++w9QL/sLZoyyJk6WsAsS0NMMOt8o5WN7EU+bVlbQ6VQbf2VO9gEPbONMdpQ8gfrvMiLo5vRS22iRaPehIH8gvWxAq64vWWt94OihpndNRt782K/0h/NvXBj+4vK7V8= MIIB7zCCAVgCCQDFzbKIp7b3MTANBgkqhkiG9w0BAQUFADA8MQswCQYDVQQGEwJVUzELMAkGA1UECAwCR0ExDDAKBgNVBAoMA2ZvbzESMBAGA1UEAwwJbG9jYWxob3N0MB4XDTEzMTAwMjAwMDg1MVoXDTE0MTAwMjAwMDg1MVowPDELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkdBMQwwCgYDVQQKDANmb28xEjAQBgNVBAMMCWxvY2FsaG9zdDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA1PMHYmhZj308kWLhZVT4vOulqx/9ibm5B86fPWwUKKQ2i12MYtz07tzukPymisTDhQaqyJ8Kqb/6JjhmeMnEOdTvSPmHO8m1ZVveJU6NoKRn/mP/BD7FW52WhbrUXLSeHVSKfWkNk6S4hk9MV9TswTvyRIKvRsw0X/gfnqkroJcCAwEAATANBgkqhkiG9w0BAQUFAAOBgQCMMlIO+GNcGekevKgkakpMdAqJfs24maGb90DvTLbRZRD7Xvn1MnVBBS9hzlXiFLYOInXACMW5gcoRFfeTQLSouMM8o57h0uKjfTmuoWHLQLi6hnF+cvCsEFiJZ4AbF+DgmO6TarJ8O05t8zvnOwJlNCASPZRH/JmF8tX0hoHuAQ== saml-0.4.6/testdata/TestIDPMarshalAssertion_encrypted_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saml-0.4.6/testdata/TestIDPNoDestination_idp_metadata.xml000066400000000000000000000024201415467341100234630ustar00rootroot00000000000000Required attributessaml-0.4.6/testdata/TestIDPNoDestination_request000066400000000000000000000006771415467341100217540ustar00rootroot00000000000000 https://gitlab.example.com/users/saml/metadatasaml-0.4.6/testdata/TestIDPRequestedAttributes_idp_metadata.xml000066400000000000000000000024121415467341100247160ustar00rootroot00000000000000Required attributessaml-0.4.6/testdata/TestIDPWriteResponse_RequestBuffer.xml000066400000000000000000000011521415467341100236650ustar00rootroot00000000000000 https://sp.example.com/saml2/metadata urn:oasis:names:tc:SAML:2.0:nameid-format:transientsaml-0.4.6/testdata/TestIDPWriteResponseresponse.html000066400000000000000000000007501415467341100227510ustar00rootroot00000000000000
saml-0.4.6/testdata/TestSPCanHandleOktaResponseEncryptedAssertionBothSigned_IDPMetadata000066400000000000000000000044141415467341100313520ustar00rootroot00000000000000 MIIDpDCCAoygAwIBAgIGAWW0dDUQMA0GCSqGSIb3DQEBCwUAMIGSMQswCQYDVQQGEwJVUzETMBEG A1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNU2FuIEZyYW5jaXNjbzENMAsGA1UECgwET2t0YTEU MBIGA1UECwwLU1NPUHJvdmlkZXIxEzARBgNVBAMMCmRldi01MTMzOTQxHDAaBgkqhkiG9w0BCQEW DWluZm9Ab2t0YS5jb20wHhcNMTgwOTA3MTQzMjU5WhcNMjgwOTA3MTQzMzU5WjCBkjELMAkGA1UE BhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExFjAUBgNVBAcMDVNhbiBGcmFuY2lzY28xDTALBgNV BAoMBE9rdGExFDASBgNVBAsMC1NTT1Byb3ZpZGVyMRMwEQYDVQQDDApkZXYtNTEzMzk0MRwwGgYJ KoZIhvcNAQkBFg1pbmZvQG9rdGEuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA oggcfiSRJ6PGoI8XHKUYd89/BPMmduzR365yUEKSK6TIOcA/jrnJzxWHT9PsvB4znaoEdg27dmX0 IZ2I0bjSoyvp4BT8ZtsuqpamsJOFDajfzrU/dMLIQCwY0+38F+x/gNNL+BhYb6zmrdvomb7yqI2E JuHMXMS786UY5GfD+/n0gRSvd+DpIW8ZlsZMG/llyxO1ZccuUqzkbiVV4w1y5PMvSBL7BAWsTn9G IckQsyF+fsG0bKlN3JQjHmjFUrT0cnWkAJjGIVmmrp9NUWyc/SI01i6WlwcQsKw4PB7EU3J8BINv 9mCGXpwp5vWXRdRGjTT4BmFm8lY0QXHqXa/2+QIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQBypFox /IaTXAKFsRQi6WUG0QiBLCR8eLhSUDF3xkgELkNYDErQKNyVaXrYoHwPoWYpok6MYddMkoo2YuPG W6V4zDa0k0ulbzKlvbbZQpkzIJEj4dr+PaqmtHAe7C7YNkj4jlfJP6QdqMK+rCBVU3kCX2c/ARun Vy/pIuLowXrQUCF0cccePD8jryej+cmm9jjHWmQNfHDMAv/vpGSXV2W3bzNALXxfCoKqU15ii6YQ hXU85OE5qXEY92ab3D67gppte7eNn/G7D7cuAZhkt7wfLsjoCVK4bZOwxqUw6mPoXXFpkTnlSo86 p7wkbeii7Epjm5HcXTPPC7jd7ZOu3Hsr urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress saml-0.4.6/testdata/TestSPCanHandleOktaResponseEncryptedAssertionBothSigned_response000066400000000000000000000356741415467341100311470ustar00rootroot00000000000000PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz48c2FtbDJwOlJlc3BvbnNlIERlc3RpbmF0aW9uPSJodHRwOi8vbG9jYWxob3N0OjgwMDAvc2FtbC9hY3MiIElEPSJpZDg0ODk4NzY1MjE1NzUzNDAxOTcxNzMyNzQ2IiBJblJlc3BvbnNlVG89ImlkLTk1M2Q0Y2FiNjlmZjQ3NWM1OTAxZDEyZTU4NWIwYmIxNWE3Yjg1ZmUiIElzc3VlSW5zdGFudD0iMjAyMC0wMy0wM1QxOTo0MDo1NC42OTlaIiBWZXJzaW9uPSIyLjAiIHhtbG5zOnNhbWwycD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOnByb3RvY29sIj48c2FtbDI6SXNzdWVyIEZvcm1hdD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOm5hbWVpZC1mb3JtYXQ6ZW50aXR5IiB4bWxuczpzYW1sMj0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmFzc2VydGlvbiI+aHR0cDovL3d3dy5va3RhLmNvbS9leGtwcHNhMXF3dUZWNEQ3ejBoNzwvc2FtbDI6SXNzdWVyPjxkczpTaWduYXR1cmUgeG1sbnM6ZHM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvMDkveG1sZHNpZyMiPjxkczpTaWduZWRJbmZvPjxkczpDYW5vbmljYWxpemF0aW9uTWV0aG9kIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS8xMC94bWwtZXhjLWMxNG4jIi8+PGRzOlNpZ25hdHVyZU1ldGhvZCBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvMDQveG1sZHNpZy1tb3JlI3JzYS1zaGEyNTYiLz48ZHM6UmVmZXJlbmNlIFVSST0iI2lkODQ4OTg3NjUyMTU3NTM0MDE5NzE3MzI3NDYiPjxkczpUcmFuc2Zvcm1zPjxkczpUcmFuc2Zvcm0gQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwLzA5L3htbGRzaWcjZW52ZWxvcGVkLXNpZ25hdHVyZSIvPjxkczpUcmFuc2Zvcm0gQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzEwL3htbC1leGMtYzE0biMiLz48L2RzOlRyYW5zZm9ybXM+PGRzOkRpZ2VzdE1ldGhvZCBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvMDQveG1sZW5jI3NoYTI1NiIvPjxkczpEaWdlc3RWYWx1ZT5qSkxWRHRsRjNlTzl0ZHdaZVJxUWdIR0hLMHJJNWxXb1J2VHBDS3dIWGUwPTwvZHM6RGlnZXN0VmFsdWU+PC9kczpSZWZlcmVuY2U+PC9kczpTaWduZWRJbmZvPjxkczpTaWduYXR1cmVWYWx1ZT5tUTJ1SEJWdVR1YTMxNHozTnBWMEZ6RC9CUFFUL3JuZWZLcGc0bnlDYXp6RjM0Mk9LaFN0Z1Z5NHZTbVJFUmpVRnFkUjV0SFpLWlJQZWxwVEFPS01kZmxXNlV6QnphWDBpSTgwKzQ3Rm02akNtaVN3RUhyaUVIMWRBblVPZWM5MytSTzFYb2xFSlo0aXF2L3N0VTNvSS9KamlzU1VHeVVhZDVwZFA1NGFLSzZ3cDFjRW95ckxmOEVYRDUvYjREb1U4elVHZWdXdEw2NVpMYkVSWWZkL0hGWVRhRWkzVjlpalJ2SDN3WmlGeWNqNXI4YjRvWUZPOWFZaWZOOXlmMnF6QW1NTUcyS0M1R0RNUWk1WFZJbVBVVS9DeDg4SUFIbHN3TkMxZFRrUDhkOW9lRDBLeFVaQU5xTDcxZGNOTCtuU2Yrd2dDN09xMTdYSDRtcHg1YnpXMGc9PTwvZHM6U2lnbmF0dXJlVmFsdWU+PGRzOktleUluZm8+PGRzOlg1MDlEYXRhPjxkczpYNTA5Q2VydGlmaWNhdGU+TUlJRHBEQ0NBb3lnQXdJQkFnSUdBV1cwZERVUU1BMEdDU3FHU0liM0RRRUJDd1VBTUlHU01Rc3dDUVlEVlFRR0V3SlZVekVUTUJFRwpBMVVFQ0F3S1EyRnNhV1p2Y201cFlURVdNQlFHQTFVRUJ3d05VMkZ1SUVaeVlXNWphWE5qYnpFTk1Bc0dBMVVFQ2d3RVQydDBZVEVVCk1CSUdBMVVFQ3d3TFUxTlBVSEp2ZG1sa1pYSXhFekFSQmdOVkJBTU1DbVJsZGkwMU1UTXpPVFF4SERBYUJna3Foa2lHOXcwQkNRRVcKRFdsdVptOUFiMnQwWVM1amIyMHdIaGNOTVRnd09UQTNNVFF6TWpVNVdoY05Namd3T1RBM01UUXpNelU1V2pDQmtqRUxNQWtHQTFVRQpCaE1DVlZNeEV6QVJCZ05WQkFnTUNrTmhiR2xtYjNKdWFXRXhGakFVQmdOVkJBY01EVk5oYmlCR2NtRnVZMmx6WTI4eERUQUxCZ05WCkJBb01CRTlyZEdFeEZEQVNCZ05WQkFzTUMxTlRUMUJ5YjNacFpHVnlNUk13RVFZRFZRUUREQXBrWlhZdE5URXpNemswTVJ3d0dnWUoKS29aSWh2Y05BUWtCRmcxcGJtWnZRRzlyZEdFdVkyOXRNSUlCSWpBTkJna3Foa2lHOXcwQkFRRUZBQU9DQVE4QU1JSUJDZ0tDQVFFQQpvZ2djZmlTUko2UEdvSThYSEtVWWQ4OS9CUE1tZHV6UjM2NXlVRUtTSzZUSU9jQS9qcm5KenhXSFQ5UHN2QjR6bmFvRWRnMjdkbVgwCklaMkkwYmpTb3l2cDRCVDhadHN1cXBhbXNKT0ZEYWpmenJVL2RNTElRQ3dZMCszOEYreC9nTk5MK0JoWWI2em1yZHZvbWI3eXFJMkUKSnVITVhNUzc4NlVZNUdmRCsvbjBnUlN2ZCtEcElXOFpsc1pNRy9sbHl4TzFaY2N1VXF6a2JpVlY0dzF5NVBNdlNCTDdCQVdzVG45RwpJY2tRc3lGK2ZzRzBiS2xOM0pRakhtakZVclQwY25Xa0FKakdJVm1tcnA5TlVXeWMvU0kwMWk2V2x3Y1FzS3c0UEI3RVUzSjhCSU52CjltQ0dYcHdwNXZXWFJkUkdqVFQ0Qm1GbThsWTBRWEhxWGEvMitRSURBUUFCTUEwR0NTcUdTSWIzRFFFQkN3VUFBNElCQVFCeXBGb3gKL0lhVFhBS0ZzUlFpNldVRzBRaUJMQ1I4ZUxoU1VERjN4a2dFTGtOWURFclFLTnlWYVhyWW9Id1BvV1lwb2s2TVlkZE1rb28yWXVQRwpXNlY0ekRhMGswdWxiektsdmJiWlFwa3pJSkVqNGRyK1BhcW10SEFlN0M3WU5rajRqbGZKUDZRZHFNSytyQ0JWVTNrQ1gyYy9BUnVuClZ5L3BJdUxvd1hyUVVDRjBjY2NlUEQ4anJ5ZWorY21tOWpqSFdtUU5mSERNQXYvdnBHU1hWMlczYnpOQUxYeGZDb0txVTE1aWk2WVEKaFhVODVPRTVxWEVZOTJhYjNENjdncHB0ZTdlTm4vRzdEN2N1QVpoa3Q3d2ZMc2pvQ1ZLNGJaT3d4cVV3Nm1Qb1hYRnBrVG5sU284NgpwN3drYmVpaTdFcGptNUhjWFRQUEM3amQ3Wk91M0hzcjwvZHM6WDUwOUNlcnRpZmljYXRlPjwvZHM6WDUwOURhdGE+PC9kczpLZXlJbmZvPjwvZHM6U2lnbmF0dXJlPjxzYW1sMnA6U3RhdHVzIHhtbG5zOnNhbWwycD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOnByb3RvY29sIj48c2FtbDJwOlN0YXR1c0NvZGUgVmFsdWU9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpzdGF0dXM6U3VjY2VzcyIvPjwvc2FtbDJwOlN0YXR1cz48c2FtbDI6RW5jcnlwdGVkQXNzZXJ0aW9uIHhtbG5zOnNhbWwyPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YXNzZXJ0aW9uIj48eGVuYzpFbmNyeXB0ZWREYXRhIElkPSJfNThkYTEwZGU4ZjhjMzBlZWM1NmY0MWU4NjQzZDA3NzkiIFR5cGU9Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvMDQveG1sZW5jI0VsZW1lbnQiIHhtbG5zOnhlbmM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvMDQveG1sZW5jIyI+PHhlbmM6RW5jcnlwdGlvbk1ldGhvZCBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvMDQveG1sZW5jI2FlczI1Ni1jYmMiIHhtbG5zOnhlbmM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvMDQveG1sZW5jIyIvPjxkczpLZXlJbmZvIHhtbG5zOmRzPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwLzA5L3htbGRzaWcjIj48ZHM6UmV0cmlldmFsTWV0aG9kIFR5cGU9Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvMDQveG1sZW5jI0VuY3J5cHRlZEtleSIgVVJJPSIjXzdkMWM1OGFkNDIzYTdiNDM4MTdiZGFmMWU5ZGI0ZDRmIi8+PC9kczpLZXlJbmZvPjx4ZW5jOkNpcGhlckRhdGEgeG1sbnM6eGVuYz0iaHR0cDovL3d3dy53My5vcmcvMjAwMS8wNC94bWxlbmMjIj48eGVuYzpDaXBoZXJWYWx1ZT5tbnBtYS9PL0ppdkZEaXhnOC85K0xVMlJTVXJzVU0yOUZEVEhYdkNFbGVoVzB0ZFNwUzQxOTBOaHEzbm8yMU53TjBQZ3RGTUdkUE84eEMvbEhwOXRqcXFEejBjTHh1NzVyVzlmL3hLbEhja0pYTXBxOVJmL1UyTHN3SUltMEtZQzREcXorQ3U4djlhRVA4YlB0Y3krQnQ0bm5MV3lvQlFzNnpxbW5YNmltTWkwaGNVeDhGaVYyOSsxTmRvOU5VelFxdGo4cngrYS82RGlRVjBoMjJOS2MzSHN3VlEvbWhzOVcyelFjZ0tvSldBWDV3dmIxYlVKcDNmZkJIUnVwWmZLdUh4SCtnYkNYcU9naFJpNXh3UVVMTmw1RzVLYzZhQmxGSTdxL3Ntcml3a1VlS1NUYjRwakN4MmV2d0Q4KzRobDRxem5TREN0R05TOUM3QjRnNm0zSWpkY2ZSNWd0TElKS3lVdGxpaVFzVVl5UThOc1YyaUMraUlONjRVSStJUU5CZXRNQm81WDd2RlpZenNRVm9yNUc3a1JGRUk5SUdFTFBHOGRpOFZrL3krSWJ4aldnQU5IUHBKeDJER1BUOWJZazlpc01lT21ib1FZWE5tcjBJUWsreUtOQThwTXNWK2pYSERqTnJDY3lPcTA4OXcyWjJWeHJUT3JMeHNRdVM0c2RzMTVVNk5VaTFBUXE5TUQrbWs2ZkxrOW9NNVhrV0MvUThLM2ZXNG5aZFJhTnhIb0NoaXhFc1ZXeGRVU3IvNENtSWtXSVRqUityeTcxRXJnK0JzY3JvVUJZTDNUMHBaeWdkaTd6d1pqNW0zZjE0cDRsazZpWStIZ1hzaVRLSjhsYXdWTEQ4cHFENDZzT0RkcFc1TVBDRHJ1Uk90WjZsb2dJeXJVZkd3cnh5dzJsbzlGRktYMklJamNMN3hKT3FCbmUzaldocHhTcjJjMjVJQXkrblBPVkFFMlVFSEh6a0RLcWd4bktySDZGUVhkZGdlMVdNcUt6b1BWYjRhd0hmTW9OaURHeGVURjNCWGFXcDhnSEJiOUJ0WGg1MlB4d0xTMWJvb054TjBRZ1ZOOXpaby9JOWp5aXc2d0pPbDBHV3BKaktPamFkYkl1bEJQQzlDZkJPOTJxWXhuV1RnMTRGZTV0emt5SmFEbkRjUmMwUldBQnlFRzlHNkIycUpSRU5Dc25MVjlmWG1jU0RZZmp4UGszK3hVaUYzQjdCQTlvUVBQcDJMU0JSWU1QcjExUEJZaHFHU2o3Y2hGSm1iT0FvUmFtVy9idU9IQnZKN3lCVnhiUjlhRjR0VTIrMWxTdHJ4aGZNandJQllZaWNIRHBHUDFSMTVPYk9GV2hsY2NSay9kWDdWcEhvcStQSVdXRm5tMjZRcHprYWE3UytWUXdZcVNjaVJvajZadXVBcm1nQkNtTytPU1JwY1hmaXkzcHdVMDVob24vSncycWJDZUF4U3JXTGRCMGl4L2loUkQyZmlKelJ2L21vZnVHT3p1eFBwR1JyTjh5dCttWFExREVQaWw3V29Xbm5qNFZtSFJyakdUbEF0RnBqZFdsd1NCMGFOQ21PZHhhNGUxcWMzRmw5c2pWM2EwQ3dKeTFEOUVjcWhTZE1RUWNiRjZMWGFVdjlMYll1bUlwYlJrN0FRc2s2eTloOHBPOEE1THN0RmR3VWlBdWllVkY0YlpucWxpK0ttdnVUMDJQRjVFVXQ0ZWhENE0xR2t6Y3d2VUlLKzNiUjg3dFhmQ2M0TXo5d1RJSUJEcU8wUGhEQjY4RkpIUDZlWlJqSXN5VHN3V1ZQSDg4MFpHYlhyK1MxNk5FNE5JSEl4M3FzTmplMXhoSTBCUFVlbFFuQS9VZC9sSDBjblVrVEg5ZDgrR002eXZuSStsdXVDVzBudVB5QzhES0hYTS9SZXROdW5TMlQ1UE5WTnpac2FVaUZUbThFS3FPRHNHakZoczYzZmVOWmtLaUt1T2JOMnhtNnBZekU5QVROMExQVktOT2lFVHpiYWNSc1FMTFFFWHlGU28ydkoyV0JxVkovaGFIb3Vpd2txeHdWOUFQTHFvYmwxVGJITTUyK21zOWhZcHM0bjZhVHpyZkJWSVdIOGxobUwxQU9rejRTeldjVW5yM0J4a1NhRTFseW9zZy9TbVB4M2g4SGw3ZFRLUnh0MXpISGdvV3l4ckVzR21WbjNmZ2p6NTJqaWZ5bldETTZUYzFQa240NzllYnZxd2psTUZjdlNZNzM2TWc2NE1VM3NLSEV0a0FZKzNDdGtlcm1STGprSXRlOUs3NlJzTzFkcm9IUlJ0OHhYZTlFQmFZR1IxWDUzOXFpVTh0ZnV6SjBXQXhCY3dwQ2RhclpUaXM3VjRnOTlUN0NhY2o3Z0RDOEwvQVpTdEJOZ1dFdFdnTytPTkRrK3JLeEFrYW9FOTAxU1Zzd2NxdVQ0dHR3ZXpEWmNJVVdWcjlLUWllenoxWWt6cC81TytPZ0J5cmVTLzZrUy9ibndDWlFDU2ozRWthV0syVCtTL2JTL3NJRWlmOWtpclBORUNPZ2JpM3F0YnpxMXMzS1ZLV2ZhMHJUY09nbHkzdUJMU0VRcG9pWlJ6SEdMZ2Z5N2tyVWFQcTlydDByMzEwSlEwUjJsaGxrUHlPQ1F2RXhEOW5lSjV3a25XNWlZMUFyR3FRVkxCdzkyc2hmK0NKV0FPK0RndllUNm9yQVF5dzBDYnB0a3E5anJRc25TVlpycEZRKzdoc1dFT2pQNnFXejFHWnJvRFdkckF5eUJlbXZHNmRWRTc1dHRHU1JqV1RNK3FEckZVdXlEM2pRcDFiWjZjRzlFSEs0TEdEeDFXTFY5OUp2cGV4VWFLeHNhSmRKQTlzZTdhS3k1MjVkeXA2R3ZObXBURWxIUEZQL1c4aVlCUlNidUZiNWJmYVpMOW4yWmNaQ3NHSzVPQ3BPVnpidFVyN0I3R1hCNlJ1d05wcldnck5SeUgreU14THFvdXJDczM0ODFoZXRZVmRUOTRkVnA2OW4xRTBtSVJSTU0xTXIyelByVHJMVUd3eDVkMjM1V2U2Vng3Ymt4SXl3clN2eGZQZzR6RWZ4eHdQc3JFT0M1M0pHMjJMU1JzamZoZnAvQ0xMOVdrUUt6bWYzZHRCZEZLNmk1WkIrNmtiVlNPYkZGSU80TFNHeWZwdEhJTFB4dlduMXk0TkVaY3pFOEZLNmllMHJPVisxdUFQY2FsbUdER2QrSVR4NnZnVTFZUlltcmdqc2pmY3crdFpWYnB1NFFGN1hhTlNWb0R0ZHJ6SWdycGVmanJDL0RJQnpQWGxMNFRya3V1OFFTdHRGMEx5ZU8yNEprQ1Fmdy9DbEtGUFhWUnV4V3BMVVk2WldKTkJCbDlQaWVqdi9EdUJ2NnRremNmL05sb01jeGtpYTZiTTZ0TEQ1WmNjcGJ0aC85T2NHSWJRUllETmdSM05SWndxamhwUVR0MlQ1aFh1ZTBJMWF4V2NwTlJDVmViajRaRmNvSGI3YVJKUEs3WXN6QTRHMEN5VmRPNDJkUVVnd0g1aGVKam0wbVlhZkJiU1dxeUJKZzdBOWFkRGR3ZXZqT05RMjRZMDRCY1V5RnBVb0pNTFNrN01vd2lrc3M1bWlGWDBjUmFDWThlbVZvQ2tMZERGSW5CcHRmd3paQjA4cmI0QUdWcEJkSkVLcmd4ZldBTkROYmI4WnZLTVE0bGZ3ZHVQSWVsZVdzQ29Jb283RmF6UHdQWHVCeHA2U1llc2YrVXRrbUhMTlhnSGl2Tk9lb3RSTWdwV1kzUzl2aVFDUnl4Sk00S2J1aVNiQzliSnk3UkZvOUVMQ2VqZXFITW5pL3RUWEtWTUxIMFlnblZkNjE3QVpoOGlzSHo4SW1kTmdYaXU3WUU3eFBSTkRkQ2N6S0luVXpvWm9OVlV4VGtZcHBuYlRsRUxGU0U0cVVoRVBuYXFUVU5HdFBWcElXZFpIZzZTWURwZ3VYNWF2c01nWWV3ZG9WM0lwUVA2VVBvcUFoQ3pKWGsyQm8yT1RhVTcwK21iSzh4Rjh6YUk5dGJuNmxJMDdRQXBZTWl0WDgzc0V4aW1YK3dTOEpqcG9hOFQ5aDJBMjFGczdreHZRaDdSVlJjS3JZcGY0R3U3OCs2cEtRYXcrendNcGovbDhudmtkMWhiMFJTNDNwN1Y3TjdIUnJkTjZ3dUd1ZmlOVHF0bXNycXZBeHRCM3h1c044R0V4aUt5QVl5eFgyVFMvK0hmb3FKSlhxYTZtL0g5dm5QU1Z4MUVoVXNvdUN3c1FFR3IxQkFDQVRiaEVGdCttUzhxRVo1eEx5WnNZRTBEYk1YWWRUSXErVkF4eDZnaytJcm5lbmdjN0ZVcXhhbzkzV3VDYWFzbGdqSVRZc1o5UGV5Mm9ZbmdrSzd4R2wrcWc5TG5nUmF1VzR2ZXpvVzY1dlc1TWpaajhyOXh4eDVSOHhvUmRDTEtLaUZsbFNFWnA5NUl5dzBxdEFmWmFYR0FvRTlYeFlmWldDS2RPRnI4VytpVUtVUnhGT0pEL0hjNlVkNTZZemp6bVF4N0w1a0laNW9Rc0pzMS9pcUNlZENJUlNNai80c25BSDZCK2svSWd3UnIzVncvc3QzOFJJOEhUekxyYk1sS1Q5OTdEcmdoQnZBYVcrZWN3Q1h2Vm9BYjRzQ1dsY0dpaDRsRDlyTUJMVUxTVzl5MTBpYmFWNWFPb3BrVVBud3FoRitnVjZzaHBvVFNnMjhYUjdMRjhVbXVCWHVTTkZwektUNXBPMGpmeElFRnNNb1VrSk5UeTN3OG44WHVubUhTTU9hakVKS2xVK1BOVndncUdtb3hGL2VSMTRWUmlzRzJYSWFEdXNqOW05d2lKTEh1NzF1S0d3QlVuS2xQekVXWVZFcVBWWDl2ZHBVVEVzL1k0ckdxd0RtOGhSVlUzRlR3cXZNaW11UWliZDVaRW1IWWY4ZDBkOEJNZVQ2Q2ZPQkdrWStMdTBwV0o1UXBkcng2b0Vzc3Q4eHp5REtKQmcyczBFQXFpbFpWeldEeWQrVEE0RC9ta0o4OWdiK2ROSjhtbjl2S2VHaWtnWHo2enQ3akFnbzk2anBtVlpnNTYvRjRBdzBBUERXNFRrSTJXb3pIcUVBTnZFZ0VvV2QwVHA3TkM1NUtZZnFrMVp3TVlyV0dIclJ4b2Y3dHFtNHExUzJjOVJ4Yk13Wm0yUlg2V01UeVQ5U0VMbHAzTHNBekZGV3B4Q3RMT1hHWDRWNDZOTFpPY1ZmSE41ekd2L25JT1ZMc2VGcGl3M0I1aVVzbHZkMkp5TUo2ZEVEWHNRUDlWWTk4c1h5VWwzYzFsVXlFUytVQ056ZmNJUThXb2VkR1RnWnFWMFRZcUYveGtLcE0rMTRaY3kxSys5UkNaNDNIRlZCUjhFUjZqbThMODBVWXJGZGgwUDdITEtsU2U2Z2ozOEd5MFhlaVNMay9SL3JLNUZjVzh3VnVvNjA3VjkvdmM2b09veUhyU2RvaUtyRzNDWFNiWmZEN3ZUTk1vU3dCckJqaGVxOVVtN1dMMzFpUTZlUjZKOWk3QXRRMHhRVEN6ejY2cHVEeXVPa0FBR0lwUXhXVjFNUEEwM0RkckFncXYrRmFKamZrMC9hV3p3ZkdqRnlTY2pyYktKUyt1ekF1MVdjTkw3MWYrVVhyYWZFUC9sY0ZhTkNzRU1UMmV1d0JWcFZleGpLNTlIeGZXMC95SjhNV1FYYmlZR0pOM3FIM2IwNkx5aVFRQWFONFBKUnBnMmlIK1R2b0E3ZGc0b1N2Y0RKOVNaTlpvck9KRTBYV3RWSm8wNGVvWkNoNUZ0K0p5Yzg3WEFFY1luRXovSGNHaGswVU8zNTA2YmgvK0prRDJLTjNDMUw5ZG5xdHJpUUpFdmJnTDd6VHRpemJzZTNqTU5rOEVWRjN1QWQxZWJPTFQ2bm5nNVN6TGNmOVNLa3dYVFBROWJvcGlrTUhWdUZEbjBUdDVkcGtQeUQxQS91M0w4UEpLamw4TStXeUsvVm1aNG9pSlZNWHp4bytZMDJrRTN3bGRWa2hhOEdRbDNFcDdJb2c5Uk1selc3SmtDMUR5a0dLRmUxQ1ljYUJISmp0b1dFUVN6UXJBNExidzRiV2hqVmtSRXNtdFI4VlZQTWZTUmpGaURsOHFzbFYxNVhzbTBnMVl1Zi9tNG00b0Ivckk5WHJHT2hTc1dWb1NKb0pndlE1UmFpSENSN3lGL3lUSWVEV1NXMXlvbGJQS3hZRkxhYS81Y1RlY1MrSitpTEZYZ01nM1pwMktGUDNxSHBEMnN1Tmdxb0FUSlA0cE9JeUdQSlc5b3ZvQnVPUzRhSm0rUzVUbUN0dzN0M3F1Zm9GWGpyWm1BdWp5NGdCYmdRT2g2eFNaNk9RVVViMm9odVB4M3E0dEl2eXl1VE9VaGhQU3d1TytyVDR0eGc4b2RmNUFQYWNNUmFJelROWlhtbHc2WXZnb2Q2cWFEbkRiZkxvNWN3VVBWODNzZkRwU3JFZWVLVkRoME9LbE8rZXI3aVdxUzdWMTJ4eDdNa0hPN2pEbUw2Q28wMWZGR0V4T3BQQUFWc1F1ZkpqOVZSYjhxSkpUbTRGVnFJTGxFMWR6L1hLWWZCNGI3Nk5ueEhia0M1eGRhNDRYRmU5cUhOWXV6V2xvVVBveXEyVnNraVcwVEZFSHpOTCtJWkdsN2ExcTNSUTZ1UnlnM3U0dGE0cWVQbkdNcittMUZTM2xySFVNUG9sRzZKVysxdk9xbUVmM1ZMaGxhUG1LZXh4K1lLejhsRUdzMHNqWlFuRDRjWG8xQzF5YjdEb3JUTk9UcVdMN1JLcFpYQjl6MVlRWHJ2TW1WSThJeDEzL2k2ZE5Ndy9pK0lpRzh0TWJuNkRGTk1NdEdIallndDAyM0pOMVVkVHpwVG15YmptQW9URzlqL2ErUXY4dW5vWS9rT29qb1BJWVh3blJNSWFYT1dzYWl4OEVTdHE3WUdvYU5Nd3JxTzBJdDdyWUxHTXlZQklaZTNHR1BhRUdvc0tjMmtQbjk3WXNxaHB5R01HVkdIeXlidXlDdkd1blphM2pRRVBjc3ovOVA5Zmh3a1FUOGUxOGdMdjFrTVRwdis3MWg5SFQ2NmswZWdoMTA4bncxd1htdFNpSExRbWkvL0EySlV4Z05QWHdxVndpMzA2ZWVBSEcwTmY4dnpqQ2ZqNm5tcUxGbkM1K0dRVVJ1cFZrY1drSEZ5TnkxMWNwYUlIL1VBbUU4cFhQVHVPTUxXdGhLeTVJNEhEK1NmZDZWVkVpa0plcnMzVGIzMXEzMStkdCtDVmpGZ0hVNDMyQzBiOVJwNnVXY3lOMFZ5K1VHR0N2L0RjNW5kRjAyTmtkRUZ2SmZ3RlF0T1gxbWVXZVYyeHFQWGJzL3BlRllHWkpYZjFBQ0FCQWtGaFZsdkxkaDBNcWpyd1ZYeU85dW1wc1RCeWxlb0w2SlZXZlZRd3JsVU10YmVvRXErMW0rZTBDajJhSTNIb3RkelNLL2Y3U3RobEU1ODFyblFvcGN1VFNOcHk4Q0hYVU5iTjNpQWVEWnNnVEJjbk5LL1dOZ1dlVjg0dWN0ZHJydE5lZHFzZFlsRmR3R2YzdkpnTWlwWm0ybVE3M0U3THVmU0pERTRyWlRLV05abTdxb3dGQUJLRUJlUlQ0ZVlLNkFLbjAzbXFsSVgzb1FqanI1NnlLdTc3Mm9DaVhWYTlJUVJpRDN0OEVZeUZyVGQ4eXlLdWdvaUZlS0xDTDBwVkU4TzJ5UHlWSTBZMnI1Z0NLcFpVOVY0Y1ZNKzlkdS9Sem8xcFpoTUZNc0o0VUZaQURwY1Y3VEt5YjZqWWt5VzErUUZ4QXh3WUJLcFNzS0p6Q2dzVFVianRtUzhwa1VVb3pRMUR0NFBnS1k4ak1YL1VUSWRRVC8ydHZIbUdaM0IvdWZIUmdSV2FVMnRxcjFDQUxwREMrUXNiZmZtNHhrd0lmMHBWMFpPNGRUZXR6QlpUTExDbnppUXJ1c3B0ZVZnYnBNcWVOVi9RcXlWT2QzOWsxTW9PdE50amhLQy9yNnB6bUZGRjZNMDRpdHRCaldQQzJLSXk5NmQvWmRoRGJ2NUlXL0diVElyb2M4anhxeVkxV082bXc3SDhsVldPUjlwTEE9PTwveGVuYzpDaXBoZXJWYWx1ZT48L3hlbmM6Q2lwaGVyRGF0YT48L3hlbmM6RW5jcnlwdGVkRGF0YT48eGVuYzpFbmNyeXB0ZWRLZXkgSWQ9Il83ZDFjNThhZDQyM2E3YjQzODE3YmRhZjFlOWRiNGQ0ZiIgeG1sbnM6eGVuYz0iaHR0cDovL3d3dy53My5vcmcvMjAwMS8wNC94bWxlbmMjIj48eGVuYzpFbmNyeXB0aW9uTWV0aG9kIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS8wNC94bWxlbmMjcnNhLW9hZXAtbWdmMXAiIHhtbG5zOnhlbmM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvMDQveG1sZW5jIyI+PGRzOkRpZ2VzdE1ldGhvZCBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvMDkveG1sZHNpZyNzaGExIiB4bWxuczpkcz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC8wOS94bWxkc2lnIyIvPjwveGVuYzpFbmNyeXB0aW9uTWV0aG9kPjxkczpLZXlJbmZvIHhtbG5zOmRzPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwLzA5L3htbGRzaWcjIj48ZHM6WDUwOURhdGE+PGRzOlg1MDlDZXJ0aWZpY2F0ZT5NSUlCN3pDQ0FWZ0NDUURGemJLSXA3YjNNVEFOQmdrcWhraUc5dzBCQVFVRkFEQThNUXN3Q1FZRFZRUUdFd0pWVXpFTE1Ba0dBMVVFCkNBd0NSMEV4RERBS0JnTlZCQW9NQTJadmJ6RVNNQkFHQTFVRUF3d0piRzlqWVd4b2IzTjBNQjRYRFRFek1UQXdNakF3TURnMU1Wb1gKRFRFME1UQXdNakF3TURnMU1Wb3dQREVMTUFrR0ExVUVCaE1DVlZNeEN6QUpCZ05WQkFnTUFrZEJNUXd3Q2dZRFZRUUtEQU5tYjI4eApFakFRQmdOVkJBTU1DV3h2WTJGc2FHOXpkRENCbnpBTkJna3Foa2lHOXcwQkFRRUZBQU9CalFBd2dZa0NnWUVBMVBNSFltaFpqMzA4CmtXTGhaVlQ0dk91bHF4LzlpYm01Qjg2ZlBXd1VLS1EyaTEyTVl0ejA3dHp1a1B5bWlzVERoUWFxeUo4S3FiLzZKamhtZU1uRU9kVHYKU1BtSE84bTFaVnZlSlU2Tm9LUm4vbVAvQkQ3Rlc1MldoYnJVWExTZUhWU0tmV2tOazZTNGhrOU1WOVRzd1R2eVJJS3ZSc3cwWC9nZgpucWtyb0pjQ0F3RUFBVEFOQmdrcWhraUc5dzBCQVFVRkFBT0JnUUNNTWxJTytHTmNHZWtldktna2FrcE1kQXFKZnMyNG1hR2I5MER2ClRMYlJaUkQ3WHZuMU1uVkJCUzloemxYaUZMWU9JblhBQ01XNWdjb1JGZmVUUUxTb3VNTThvNTdoMHVLamZUbXVvV0hMUUxpNmhuRisKY3ZDc0VGaUpaNEFiRitEZ21PNlRhcko4TzA1dDh6dm5Pd0psTkNBU1BaUkgvSm1GOHRYMGhvSHVBUT09PC9kczpYNTA5Q2VydGlmaWNhdGU+PC9kczpYNTA5RGF0YT48L2RzOktleUluZm8+PHhlbmM6Q2lwaGVyRGF0YSB4bWxuczp4ZW5jPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzA0L3htbGVuYyMiPjx4ZW5jOkNpcGhlclZhbHVlPm9qZlNObDlBTWNIMHd3V25FVjlSNkpHWVVlczNDclFEWTFTMDZ3ZkwrV20zYVQrNDE4aWUxQ25BLzZTU3BQY1Q1c3NsUVJMdkQ5dGlaYWZ0ai9XcGZyMGhITzJWT2k4QXQ5VGRYcmExSWprRkNyL0ZLQys4VkMvL2VGWVNtT0piSFo5TzZFRFBXSDArcm14WEZ5cmlFYXptOHhsb3J4bGdFejFiM1ArZHVkND08L3hlbmM6Q2lwaGVyVmFsdWU+PC94ZW5jOkNpcGhlckRhdGE+PHhlbmM6UmVmZXJlbmNlTGlzdD48eGVuYzpEYXRhUmVmZXJlbmNlIFVSST0iI181OGRhMTBkZThmOGMzMGVlYzU2ZjQxZTg2NDNkMDc3OSIvPjwveGVuYzpSZWZlcmVuY2VMaXN0PjwveGVuYzpFbmNyeXB0ZWRLZXk+PC9zYW1sMjpFbmNyeXB0ZWRBc3NlcnRpb24+PC9zYW1sMnA6UmVzcG9uc2U+saml-0.4.6/testdata/TestSPCanHandleOktaResponseEncryptedSignedAssertion_IDPMetadata000066400000000000000000000044141415467341100305350ustar00rootroot00000000000000 MIIDpDCCAoygAwIBAgIGAWW0dDUQMA0GCSqGSIb3DQEBCwUAMIGSMQswCQYDVQQGEwJVUzETMBEG A1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNU2FuIEZyYW5jaXNjbzENMAsGA1UECgwET2t0YTEU MBIGA1UECwwLU1NPUHJvdmlkZXIxEzARBgNVBAMMCmRldi01MTMzOTQxHDAaBgkqhkiG9w0BCQEW DWluZm9Ab2t0YS5jb20wHhcNMTgwOTA3MTQzMjU5WhcNMjgwOTA3MTQzMzU5WjCBkjELMAkGA1UE BhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExFjAUBgNVBAcMDVNhbiBGcmFuY2lzY28xDTALBgNV BAoMBE9rdGExFDASBgNVBAsMC1NTT1Byb3ZpZGVyMRMwEQYDVQQDDApkZXYtNTEzMzk0MRwwGgYJ KoZIhvcNAQkBFg1pbmZvQG9rdGEuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA oggcfiSRJ6PGoI8XHKUYd89/BPMmduzR365yUEKSK6TIOcA/jrnJzxWHT9PsvB4znaoEdg27dmX0 IZ2I0bjSoyvp4BT8ZtsuqpamsJOFDajfzrU/dMLIQCwY0+38F+x/gNNL+BhYb6zmrdvomb7yqI2E JuHMXMS786UY5GfD+/n0gRSvd+DpIW8ZlsZMG/llyxO1ZccuUqzkbiVV4w1y5PMvSBL7BAWsTn9G IckQsyF+fsG0bKlN3JQjHmjFUrT0cnWkAJjGIVmmrp9NUWyc/SI01i6WlwcQsKw4PB7EU3J8BINv 9mCGXpwp5vWXRdRGjTT4BmFm8lY0QXHqXa/2+QIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQBypFox /IaTXAKFsRQi6WUG0QiBLCR8eLhSUDF3xkgELkNYDErQKNyVaXrYoHwPoWYpok6MYddMkoo2YuPG W6V4zDa0k0ulbzKlvbbZQpkzIJEj4dr+PaqmtHAe7C7YNkj4jlfJP6QdqMK+rCBVU3kCX2c/ARun Vy/pIuLowXrQUCF0cccePD8jryej+cmm9jjHWmQNfHDMAv/vpGSXV2W3bzNALXxfCoKqU15ii6YQ hXU85OE5qXEY92ab3D67gppte7eNn/G7D7cuAZhkt7wfLsjoCVK4bZOwxqUw6mPoXXFpkTnlSo86 p7wkbeii7Epjm5HcXTPPC7jd7ZOu3Hsr urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress saml-0.4.6/testdata/TestSPCanHandleOktaResponseEncryptedSignedAssertion_response000066400000000000000000000274741415467341100303310ustar00rootroot00000000000000PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz48c2FtbDJwOlJlc3BvbnNlIERlc3RpbmF0aW9uPSJodHRwOi8vbG9jYWxob3N0OjgwMDAvc2FtbC9hY3MiIElEPSJpZDg0OTM4NjUxODIwNjgwNTY5NDI1MDUxNzciIEluUmVzcG9uc2VUbz0iaWQtNmQ5NzZjZGRlOGU3NmRmNWRmMGE4ZmY1ODE0OGZjMGI3ZWM2Nzk2ZCIgSXNzdWVJbnN0YW50PSIyMDIwLTAzLTAzVDE5OjMxOjU1Ljg5NVoiIFZlcnNpb249IjIuMCIgeG1sbnM6c2FtbDJwPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6cHJvdG9jb2wiPjxzYW1sMjpJc3N1ZXIgRm9ybWF0PSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6bmFtZWlkLWZvcm1hdDplbnRpdHkiIHhtbG5zOnNhbWwyPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YXNzZXJ0aW9uIj5odHRwOi8vd3d3Lm9rdGEuY29tL2V4a3Bwc2ExcXd1RlY0RDd6MGg3PC9zYW1sMjpJc3N1ZXI+PHNhbWwycDpTdGF0dXMgeG1sbnM6c2FtbDJwPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6cHJvdG9jb2wiPjxzYW1sMnA6U3RhdHVzQ29kZSBWYWx1ZT0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOnN0YXR1czpTdWNjZXNzIi8+PC9zYW1sMnA6U3RhdHVzPjxzYW1sMjpFbmNyeXB0ZWRBc3NlcnRpb24geG1sbnM6c2FtbDI9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphc3NlcnRpb24iPjx4ZW5jOkVuY3J5cHRlZERhdGEgSWQ9Il8yZTYyMjQxZWU1Mzg5Zjc4OWM2NmI0OTc1NDdmZDkwYiIgVHlwZT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS8wNC94bWxlbmMjRWxlbWVudCIgeG1sbnM6eGVuYz0iaHR0cDovL3d3dy53My5vcmcvMjAwMS8wNC94bWxlbmMjIj48eGVuYzpFbmNyeXB0aW9uTWV0aG9kIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS8wNC94bWxlbmMjYWVzMjU2LWNiYyIgeG1sbnM6eGVuYz0iaHR0cDovL3d3dy53My5vcmcvMjAwMS8wNC94bWxlbmMjIi8+PGRzOktleUluZm8geG1sbnM6ZHM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvMDkveG1sZHNpZyMiPjxkczpSZXRyaWV2YWxNZXRob2QgVHlwZT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS8wNC94bWxlbmMjRW5jcnlwdGVkS2V5IiBVUkk9IiNfNDM0MDhiODA0Zjg3MDE2YzdkZTk5MjM5ODFmNTg3MjQiLz48L2RzOktleUluZm8+PHhlbmM6Q2lwaGVyRGF0YSB4bWxuczp4ZW5jPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzA0L3htbGVuYyMiPjx4ZW5jOkNpcGhlclZhbHVlPktxU3hnbzI4cldjc0pzL0F5K3ZPMHpkLy9CK3EvRHo1ZkZmY3A2cWQ0U0pPbTY4ekxFSEFkTkQ0TFExYjdCdkxrTzYzOW5RZ3JZcWJwM2UvVDNIOEdrSlg2REtHbDB5MnpkdjlJNjRJNUg3K1NFNFM2cjRFWmZlNzJXKzl5bm5FeUR3bmcxLy9FdUlDb1BsOUs4dE9lSTliUTZacjVreUt4OENTNGJFL01CMjJRSGhzc0pUMG9oa2kyTE9LUmg2MTlYbDh6Y1RJem1uZmRNb2tEUm1renNuVmlOa3NaK1Z3ZSt3dkgyUUNnWTFKK28yKzRmY28ya3pGdWN2ZUpkZng4dVRxVFp4dzRhRnRGQ2YvYXprOGpha0ZpazVSN0tZam1mZGxOL0QrOWFDY0tiYlFPMHFxd3AxKzNTNWFiSWczaHRIREIybkNYNnluSWl3S1ozc1B3bzNYVVRsREg2Q1NkTzRFSDVUN3NHcUhRV0ZIN0tLS0lGdHZrMUVhYjI4WXl2UnNrbVJZTXNNUG9tTUZGekt6MlpyM00yY1RBclZDS1dRSzh2RG9laFZyTzZyaHFHbmpCcWxpYkFHZjhleXRpbndEQjgvNm50RnU2aG1LZ0pOdzdFbEpwcU9sQm1ZUE5mU2MxMEMxTjZXS0hqOEZudGVHenE0MmxJdUIzcnFrME0zeGtSdytTYkhxU3FqRDFCbDFDM1gwWlRsMDh6Y2VJbmZ6Vmo1TEZ6YU5iRXFmV0ptRmNOaHhSMGNTekhjelBPT1RzdUlrTEdZZTkxejFkRG1SWTVMNmFyTlhWbzJadjc3NnI1dGgwSVk4cTUva1pjY3pzVERySW1sY1YrTWFtOTZRNjdOOWQ3VVhpVWpUcXdTUjh1TkxVMXpCWFppekpEU0sva25LMDlxZWg0UGxKcHdZcDVab0pLYVp2V0JsV1J2Mm9ZWDJkN3ZyS1kzSVpCanRLWllNQjZpR1MzWUZBbTc3d09xNGs0WWFjSkVBc2ZQaVZqNlJQaTQ4bTZOYnBSZ1ZzMmhZYTE0MzQ2amJqZlFVaWhrTnZ0Q00rM1VlcWU0bGowQVNwR3c3b1hkNVlkQWw4Z3hIc2xyTzNoY3J3Q0JqZTFhOXovaHRYdUU4RldWd2dSODgzbU1tR1hBZ2x1SkNUOUtaaUZvaXpFS0JyaW41NHFpT2R4YzU3ZEVPRmRna05XRXMvdTUwWjZCYWVzaXJvaGN6WFdsUGlWVWFaV0RlTzRzR3p6ajNiaHlPay9MZE53VDJGQ3VvWUlZbC9PWURhUWZVVjdUV3VLeHo5YlRaMVFKRTBMbjQxVjY0QzhGbmpZaldReDhWemFRUkFzVm55NFRSMkJvWmJrQ0VTUHo2eTZRUU1DNG90RC9YcStPMUlzT1pUTlJxNXUzNmo4T0xUVmNIZmdaNTJxVlRnNEJLL01PTmVEM2k3amZsTjNFR05LdGlmMzJkT2lGcTFRY0ZuTXFFL0xFYVJTMHNCdGpUaHRvanBxKzBpNHJrUjVISS9aLzVBOWZHb2hOZkhWa2dEakkvNmdGcGNGQ3lTZWJocEMwRXAzSXA5U09KRmRIQ2tjbjVLNXJYNDlSR0xFTXZtNzVTS05UOVl3RGtOSTlVcWdiQmxGMytMOXp6ZzhhWDFYbm41TWVPSDZCK2lZVlBZYXZkUjZ1WDZYbDVzR0pHRUFUNkNmQVBhdVBGUkNVU2d1LzJiUDlRYzV2Y2ZOeVhmTUJtNGVyTm5jd0NNbGhMRHN4NW1DQWVLdGRWaUxZWTlOclVTNy8vaEp0MmZseEZ5Q29tVXdlRi9pUDZVTVo2ZTV0UHIzK2hVdUZkY0FTMUNsU3FvSDRuVjJVSmVHR3ladC92c1orQmRNdkpoRmp1TEg5REtOVmRtSjFNM0ExRjZ0Ymx6dnFXajlRaVhEV2E2amxhRGxHRXR1NzdHUW5iOTU0MUVMYUZCYnhvRWYvbTNpS1NGUm04REE3NytaanhNUzMzTCtzUldaWm0rNWZJeGtvdUFWR0dGRHJ0M3lQMyt6bWVyWTVPUkN3cW9ZekdyMFRPZGRJS21UcmFrWi9uZnY4a0l1eFpIeUppdHBXSENZTkxMQlhFU3hLaXVKbFRSd0x6U2FWRFhhT3pGVTA2dWxUUVhHOThqazRqU1VhWHR5amV5NGUxT0V0V29oM3ZHVEFRNjczaVY2RlZ3VXdxYzVkQUh2MHV4aVVGaTlXWUsyckpqZ1dLVTN4VDRFZjlWUUZ5OVYrMUhZUDdpalRMWFF4eVlZZjFjTVV5OVlJVGlXbjVIS0ZwT0l6YWxLK2I1R1ZUbWdoZ0F5R0RZTjllOVo1ejg0V3BKN1N0L3FpLzM0a3V5bjRNN21WV2tPRGU4TElyZjkwWnViSk5ObkUwTTdMUldXYXZEUC8wak5OMEtJcW80cnVwRldEN2R3bDNKQWlSbzdJcEl0Rm0vQkpGb1hDWmZFTUgzOUxqL3dZNFF4Z2NuRFRKbmo1bGdUK3cvT0xvN1hJYlZRRHhjaE9WUkVqTWtKM3lKa2VmSktNUFFQUThGMGpKZ1NmM3VraWpuaTlSbG5Pd2EvVi8wVnF4RCs5NUtwNDhiMU5MR0FLN2hidVFZM3c3VFlzZWtteWZlbzRqbGZqWkZYWmFyTUExQWJ6cENvMTR2K2FCcGhnYXNzVjBjTDlxL0FyVERlK0pJM2c5Q09pN05PYm5FOHVhYjl3Z3FETzFCV1lEQ0Rnc2Z6WXFiRU9kYlE4SHZtZlBIaks5Nit3Tk55K2lDSFBvcnFiRzA1SDFsZHJyb2hVKzhkeklnOVkwMVlCc0RIUkVCU0lZMHU0QjdIYXMrQ1RnS2hSaysyQ3lWcmF0U3ZHWStuQkF6U0U5enBkTWpKbEJWaTRxeWgwcHlKeU1qQ2xESzNBMGxCYjlGd0M4RWpEdjVpbTMxZWJLMmhBQTJTRVBYZC91N01DeG1veDhKYVJBZzBxTVorZGtMZnpqNjBlUWlBalp1dGhNZ1prMWhiVzl5ZFordmRwRlBoeFVHTzd1QTA1ajc4QjkyRUxCYkxCWTMvZ09tMEplN3QrTmorTlMvbHZRN1Q4RytpT0lFZnY1S2ljU2M4dE1vWUVRWEtrK3VyZVFDQzBpOG5sOXZEdWFLMTNyNTZkM0YxMGxHN284SDYyR0ZBQm1rNnBlMVVZdDdWL3dwM2xPR09TcVBkUzJ1Q3hlR2g0aWNHcHJYeDNod3lmT3Q0TTErMnhvMjhkbHJqREtuOEhaTTZnTGRUcGlhMVc5WTFIallMNDdCcU12OEpuZGhQd2RTWkduUGhlWjJQQ00wN010bmNlRzBsemNBQmhJYUU4R0tDZ2RaRXpuODlwK3gwbVhtT2JOckJ4MTRKQU9Dd2F0N2tZdk5IS3VwckNrK1g3aW9pUTJmZmRzTkxOYktyZTdHT3cyRmZ0OUVXQU95RCsxQlFBbW4wdVI3NFBoSUJQTmFGN1o1dTdEYnRBZnV4cXR4Ym9ReFpEM1Q5M3ZWNTBnUFdIcHlVbHBRTUQ3ZEhiNVZ6aDlNWlFQYnNnSWhxeGV6U0x1dlI4dmFMT1lXbGxQcTFLTUY5dkZaNWl3U0Rjd0VTVGlXV3owNnRUMlZvZTRud2FxQk40MkhiTmVFdy9kMVpvNUR6Ujg2ZmYrQjE5c3crbGZZNTVHU0NkcENTM2kzWi9oRTVuNEZ6czhLdWhSTHlJaExKQTlYY3RTcjdyenljUFpyekhhN29QWFM3eUtCalRZVFN0ZTIzYVEvM2swQ2hoVXU5TVBzdlBiekNzV0NTQ1NtdFFZNjJtQy8vdVVQR1lFZUUxclZIWU9BeS9uWU9sc2R6NXc1Myt3VnVtVFVjTUx0V0hob1dsSVJEVlRhVklRL0FGcC9DbHpzV3lvRC90TlRoZ2Z2VlUrcVN3eTRvRVVVWHorRm9rU1dXYWFtRG11TkRFL2dJTUxWcExXNUd3UHdDWWFrNi9NUkJtVDFoVW1vdHVSL1hRUHpveElEc0UzZjQyN3V3amF0WW1wMGVqYUtWMVNXbG1NYVIzNmQ3MkJLQVovNHdvQkFXYXA3M05UVEFlZ25SL25EclZwRnNZbVpySFhQNldYaTNmY2dSWkR1QXNFdHg3R0tLNlNmMTlIMExoSVhiTXRiS0c1KzhRZFE4MnpjRW01bURreW9Ha3B2dzVuemhYNy9MOWV6SGY0Yiszcm9YU3dYTHVRRmNlT3RyZlkxb3FSWFo1YlI0MDdabXJiTTA1cXBVdUdNOThweHZybjgxaldvMXpxQ0lmckJORGNPN1hDZXVoS2JWTlJObis4TGJmV2FjMStWeUh2bUNScDJRTWxiTDVvdndodFM5UXhTSTQzRUpJbU4vczJVUS9JYVFXZXJYYVFJZjg1SEs1bjVaeTBqOWxBNFN5MlQzTXVnOXBSNitNU1RyWWNnRjVneTBxdzdEUWRoRTlhS3ZqSUY3REUzK0YwU21peHF5UzNBODBzeTZKUi96NlE1dDJKb3FjSVcyNTZHb2JXaEFYdytOWk9aT29ROXVEbkJnQ3E3VC9qcVMxamFhVlUxNVJ1TUNDSnpBTzlYSHpPVENxcURPeitMLzVqaU1xODd0ZThRTy8vZGlTVlErY1VaeHYxcGxmWURBRUpoUWtscG43eUpYdVJBVDE3S0xmbXRlaGJQNHBLM0hQNnVoZ3BFa2hZVHNHMlhweGg5VVluckQ0WTFSSzZXS2pPUDFWSzlWRU5FZ3FlMUhVTlZBbmQ3U3pUWVFnQWd6RHRuYWltNytYT1JNMEpVOFpwZWZBZ2Z6cTZtT1JIV2d6S252YTU4czVQeUYzSWk4dzN0ZmlCaEtZWjRFK3ZaVVF4NmFLUEgyNVp2U2ZYampvcDlXZ3hKN043d053WTVzZjlKd1UydnhHV29OdjVwbm84dlFKeXRkbzVzN1B3Z0NJd0FkZE5UbEZ1WEJVMEQ5aVphM2JUVUMyRHl1RFVkaGJqRkZPeldzQmR4R3h1eWYzbmtnZEhvTVBxREpYeStvKzRBU2MvZkRxbHFsaUJXa05TWmx5UWdieDJKY05BQzR0Zmw5OW9iNjl5Yyt3ZXVHU0p6YnEyM1U3Vk1HUUVObWxaR2Q5aHEwZVdCempRVVVWY3dzbHVLNk5uZTBoTk12djl2MUNoMW5sOXJna1I0bG5FWEUyQ1hhcW8xVWQ5bVdMVVJBUFZkdk9YWmNINHhiRzF2WEVPRUMxNVhBaTd0Ni9weEVkcGJLLy9rOWE2NFBld29KU0UxcUtSVGh6RmxBUTU4UzZPL21YNWJhdG9WbXo2ak02Zi9TbjFlQUVBdHFHdEFsY0lGZWNCejN2OGR6SE4wcGZrRk1lNnRLYjcybTZEYkpZVnA1blZYVjBKSjVkYjMxcXQrUVRkelp4eE5TWkJ3NUlGSXd3MXNtcnZpRmwrSUhQZlpMK1VNUUtZTk5seE8xRWxmcGFoTGpFMmtLSUQrYjkvWE9wZDI0UVA1YzZnTjJzNjNNZE92dVYwTzhqVGkwNXYxL1g0cGZ0aDYxNjl5bUZ4Q1Q1ODdMRzNscUw5T3QwbEVqODd3aEkxZ3gvTlNQcGRzWGZHSWRoZlY5MmtaT2cvQVhvZkJpb1NHQ0JYc3ZvZUdlMmFCdUZkSFFZUzJkcVl0d1Zta0F5OWhnUnA2N0paQ0FIRTJRaUIvdTRJQ0ovd3duNFduc0VBa2VIbmdlWDVZR0Y0azJwYmZqakVXbWxSWmFJVjVQSmFaU1B4TGpPWWdLb1pqZGx3L1B2cDdEWXRTVDdrTnNIRlU1WFgzNnFXS3Q2WEpaWGlDN0hJR2ZxQmtIdjhjWEdwYjExcitMNWJaZ2NvMWhzQ0ZlQ3JmamFWcnRhWDBtYjNOaUpsOGQ4ekhCcXRWSVRKMGtPNVQrU2JYZmVBa0NuQWFTUmp6QXNrdDd3b3kyY1BFVGk0eTlDQnlZYnZrWHJ5Q3RhQTgxNC9sY1FoWG9jQ1Fscmg2bVYxd0kxOS9VdFBsTnRQcUVyemtFK2QvT0tYWmtEUExydWJZQnhZQ21yb3VRVm9QMEwyQWc2TGErV2VVeTh4dm5mKzRrR2xnUittdDh6UjYrd1cvdWNvNkViK21RMVVTRTdiYVVDZXFsVjJNaVl1Yk4vMVp3MWNPNUFrcU05TlJaeG5RdUJWMXZiak5JbmhwNDgzTDJiOVAwa0RSNUJleFVDNnErL0E3NXpYNG5NUHY2VHhWWFRwV1Rrbzh2OXFQbWZQUkJhaGs2czdDbXY1M0ZkbE10RHI5cThheU9YRHBjbzRSbzlSMmpoWk9SOFM3bE0yRzNKZkxTYUpnUXBFa25kcnkrMjRBZVpFZXEwSGJoakNUVzM2TjJLeHdSMmxxbGg5Smk4WWJ5ZDd1eVlSenI0YmNmQ2lDZEVVSGhUSnJXM3BoLzU4VmVpTCtkcjV4eVdhVVBDMk5RTnFFcUlFa0w3Y1N6ZENkOUlwenY3N3ErMHdXT29xaGxua1dUK3dOeEZVaitidU03M1Z6aE4rb1poVlhZZHcxZitIZGtjY21IQXBWS2tXUVc3YUw4cllaelUycjdzQWlDMEsxNXg0WHZqdDV5ZEJhQS9HcU02MmhjZ1dXZlFmZW92MS9ZMmVFblA4Ykcycy9KZ0dlcEROcXJrQVpTR0J4NDlNUVdyYnNndEFLc3YvMGpHL2VUYXVmN3J2dDRMK29JOXFET25ZMTFzTnBSa3ByVDRKZCtlN3B4a0NlMmhoaWtsZWxZbXM2MDlXaDU0dWI4T0oyVGE1L0dnT3BKZHQ4Y1JrNXpKT1hLZGdGUDNTaWFVQjIzYXJ3TEEwS3U0cCtNUGNQOUdUeGdsSmxrK2N3QVRPVXRTL1pnYURQRnB1dTFBeXBKUi9RTUdMZ0JDbStxZkk5ZURqbWMxTXcyQXV0ajJ6Z2xrYnFtb1dadWdPTWdYZFZrSG5SU0ZOdmFqbUJId1lvc0dBWVZCQS8zM3o0QkFIZHp3aFZXbkZQUGF3b1FKcUJCaVVBeGFIOGxjeEpTVXdWUUMvaFoyQSt3TXNmdEtoOHpaeE43THJDakVEa2hON0l0NFFjcEVJYk50UG5oZ0JBWmR5TGo1YTFVUHRMY2llL3o2SGFLTmFxcUtvZjFTMll2RmlhemZLcjdnTGdkRXE0UHdKOFVrS2lIOER1TmthVWRNSDZ5cjd4Uis0SFVVRUhaRFpkbytLMWRxTnlGZWhJZ3dGM29BWE4wcFBQQ0ZjR0RYb20zWVkrWGw1RWRENTVDZEV3bUF6T3FCNDNCaTdtQm9kZmVTdXRKbG9YTlAreUpLSDJKemNuUE1wWDJpT2d5MmdwNGM0SThqdkJpSmp1bHJ4QTBsWW9aZTlXZUtkSVNQOHBkSzQxUFdwd2pkMnVPQ2VOcDZYaHNVbHZvWis0WHV4d2Q5MWRNZjJlQ2tJQUdMaHFsZUpIako3L2haa3JOTTVoYWJreGhnVGk5NVJkZ0Vzay9mR0lOS08yVTRKVXZPSkE2eUkra0QrOXRmbUxSVi9QeFhROGluZEtPWnZnYXk5MEErT3pVR1B2Mmc2UUJzSi9pR1laeVgwOE5QSFg4Wit4eGJRcTVnU0l6bkxkZ1ByTUZjWVZCeVl2Y3dMRytyTWZRcEt1eVV0Wk1WL0JCV3BXSFNxaE0rMkRwN0N2M3JYS3QzMjFnb09ZdzlPOEtzV3BhU29RSDJvU2c1Y2VBcW1TU1hUbGJyL1YramZEbjV0TldJVG9SZXpkVU5vQUVnbHJRbFJ4eHJlbklCN2xMUEFJVS9BbW1LYXVsb0V3dEl4eXJuRWRNL1A1QlJ1Q0JkTTZ1ZzNrOVJLYzlwNEVCcnZJempreEFsZzNva2VrQjhkWDh4UGhLZXd4dDBidm5yWWlsdXZSb01rSEpjYkJuelVvM2wvbjZBUmZhTUlnSDVRU1RhdFBiYmUvSlhrOHd1UG9iZVNocTFjT0ZOVFhET2VvRHNCbXNRS1RIeTlXZk0xano2R3FXYXk0VkFoYjh4NVllL1ByZ1VFVFZyeWFnRWNNUzZVdXJYODByUUZUTHltSSsweW9ORzIwcWxCVE12Ujl1dUwwVmVmYU9VaEI3NFFjS0lOajNMWFdRSytSTENpcmZMZFB4WGRhdz09PC94ZW5jOkNpcGhlclZhbHVlPjwveGVuYzpDaXBoZXJEYXRhPjwveGVuYzpFbmNyeXB0ZWREYXRhPjx4ZW5jOkVuY3J5cHRlZEtleSBJZD0iXzQzNDA4YjgwNGY4NzAxNmM3ZGU5OTIzOTgxZjU4NzI0IiB4bWxuczp4ZW5jPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzA0L3htbGVuYyMiPjx4ZW5jOkVuY3J5cHRpb25NZXRob2QgQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzA0L3htbGVuYyNyc2Etb2FlcC1tZ2YxcCIgeG1sbnM6eGVuYz0iaHR0cDovL3d3dy53My5vcmcvMjAwMS8wNC94bWxlbmMjIj48ZHM6RGlnZXN0TWV0aG9kIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMC8wOS94bWxkc2lnI3NoYTEiIHhtbG5zOmRzPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwLzA5L3htbGRzaWcjIi8+PC94ZW5jOkVuY3J5cHRpb25NZXRob2Q+PGRzOktleUluZm8geG1sbnM6ZHM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvMDkveG1sZHNpZyMiPjxkczpYNTA5RGF0YT48ZHM6WDUwOUNlcnRpZmljYXRlPk1JSUI3ekNDQVZnQ0NRREZ6YktJcDdiM01UQU5CZ2txaGtpRzl3MEJBUVVGQURBOE1Rc3dDUVlEVlFRR0V3SlZVekVMTUFrR0ExVUUKQ0F3Q1IwRXhEREFLQmdOVkJBb01BMlp2YnpFU01CQUdBMVVFQXd3SmJHOWpZV3hvYjNOME1CNFhEVEV6TVRBd01qQXdNRGcxTVZvWApEVEUwTVRBd01qQXdNRGcxTVZvd1BERUxNQWtHQTFVRUJoTUNWVk14Q3pBSkJnTlZCQWdNQWtkQk1Rd3dDZ1lEVlFRS0RBTm1iMjh4CkVqQVFCZ05WQkFNTUNXeHZZMkZzYUc5emREQ0JuekFOQmdrcWhraUc5dzBCQVFFRkFBT0JqUUF3Z1lrQ2dZRUExUE1IWW1oWmozMDgKa1dMaFpWVDR2T3VscXgvOWlibTVCODZmUFd3VUtLUTJpMTJNWXR6MDd0enVrUHltaXNURGhRYXF5SjhLcWIvNkpqaG1lTW5FT2RUdgpTUG1ITzhtMVpWdmVKVTZOb0tSbi9tUC9CRDdGVzUyV2hiclVYTFNlSFZTS2ZXa05rNlM0aGs5TVY5VHN3VHZ5UklLdlJzdzBYL2dmCm5xa3JvSmNDQXdFQUFUQU5CZ2txaGtpRzl3MEJBUVVGQUFPQmdRQ01NbElPK0dOY0dla2V2S2drYWtwTWRBcUpmczI0bWFHYjkwRHYKVExiUlpSRDdYdm4xTW5WQkJTOWh6bFhpRkxZT0luWEFDTVc1Z2NvUkZmZVRRTFNvdU1NOG81N2gwdUtqZlRtdW9XSExRTGk2aG5GKwpjdkNzRUZpSlo0QWJGK0RnbU82VGFySjhPMDV0OHp2bk93SmxOQ0FTUFpSSC9KbUY4dFgwaG9IdUFRPT08L2RzOlg1MDlDZXJ0aWZpY2F0ZT48L2RzOlg1MDlEYXRhPjwvZHM6S2V5SW5mbz48eGVuYzpDaXBoZXJEYXRhIHhtbG5zOnhlbmM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvMDQveG1sZW5jIyI+PHhlbmM6Q2lwaGVyVmFsdWU+Y1dtRmZHU1VOM08rTGJxd1ZpbnJvMWlmZEJYNTYxOFdtRlI4KzJKQjRqUTdYSndtb1NlNG8yNTQxbkkrUStaL2R6MmhOUFc4bmF5MzdvUkRDa1RocnY2Q0RjZlFhYjBmZnFWUFUreGVrVnd0ejRBSWc5bWt0UFdSempnaGdCdUliU055aDJDZU45dUlwOWJwbVpYY09ScEZoZzMyWVpxRWhEUWxmMWdTUEpNPTwveGVuYzpDaXBoZXJWYWx1ZT48L3hlbmM6Q2lwaGVyRGF0YT48eGVuYzpSZWZlcmVuY2VMaXN0Pjx4ZW5jOkRhdGFSZWZlcmVuY2UgVVJJPSIjXzJlNjIyNDFlZTUzODlmNzg5YzY2YjQ5NzU0N2ZkOTBiIi8+PC94ZW5jOlJlZmVyZW5jZUxpc3Q+PC94ZW5jOkVuY3J5cHRlZEtleT48L3NhbWwyOkVuY3J5cHRlZEFzc2VydGlvbj48L3NhbWwycDpSZXNwb25zZT4=saml-0.4.6/testdata/TestSPCanHandleOktaSignedResponseEncryptedAssertion_IDPMetadata000066400000000000000000000044141415467341100305350ustar00rootroot00000000000000 MIIDpDCCAoygAwIBAgIGAWW0dDUQMA0GCSqGSIb3DQEBCwUAMIGSMQswCQYDVQQGEwJVUzETMBEG A1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNU2FuIEZyYW5jaXNjbzENMAsGA1UECgwET2t0YTEU MBIGA1UECwwLU1NPUHJvdmlkZXIxEzARBgNVBAMMCmRldi01MTMzOTQxHDAaBgkqhkiG9w0BCQEW DWluZm9Ab2t0YS5jb20wHhcNMTgwOTA3MTQzMjU5WhcNMjgwOTA3MTQzMzU5WjCBkjELMAkGA1UE BhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExFjAUBgNVBAcMDVNhbiBGcmFuY2lzY28xDTALBgNV BAoMBE9rdGExFDASBgNVBAsMC1NTT1Byb3ZpZGVyMRMwEQYDVQQDDApkZXYtNTEzMzk0MRwwGgYJ KoZIhvcNAQkBFg1pbmZvQG9rdGEuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA oggcfiSRJ6PGoI8XHKUYd89/BPMmduzR365yUEKSK6TIOcA/jrnJzxWHT9PsvB4znaoEdg27dmX0 IZ2I0bjSoyvp4BT8ZtsuqpamsJOFDajfzrU/dMLIQCwY0+38F+x/gNNL+BhYb6zmrdvomb7yqI2E JuHMXMS786UY5GfD+/n0gRSvd+DpIW8ZlsZMG/llyxO1ZccuUqzkbiVV4w1y5PMvSBL7BAWsTn9G IckQsyF+fsG0bKlN3JQjHmjFUrT0cnWkAJjGIVmmrp9NUWyc/SI01i6WlwcQsKw4PB7EU3J8BINv 9mCGXpwp5vWXRdRGjTT4BmFm8lY0QXHqXa/2+QIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQBypFox /IaTXAKFsRQi6WUG0QiBLCR8eLhSUDF3xkgELkNYDErQKNyVaXrYoHwPoWYpok6MYddMkoo2YuPG W6V4zDa0k0ulbzKlvbbZQpkzIJEj4dr+PaqmtHAe7C7YNkj4jlfJP6QdqMK+rCBVU3kCX2c/ARun Vy/pIuLowXrQUCF0cccePD8jryej+cmm9jjHWmQNfHDMAv/vpGSXV2W3bzNALXxfCoKqU15ii6YQ hXU85OE5qXEY92ab3D67gppte7eNn/G7D7cuAZhkt7wfLsjoCVK4bZOwxqUw6mPoXXFpkTnlSo86 p7wkbeii7Epjm5HcXTPPC7jd7ZOu3Hsr urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress saml-0.4.6/testdata/TestSPCanHandleOktaSignedResponseEncryptedAssertion_response000066400000000000000000000236041415467341100303200ustar00rootroot00000000000000PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz48c2FtbDJwOlJlc3BvbnNlIERlc3RpbmF0aW9uPSJodHRwOi8vbG9jYWxob3N0OjgwMDAvc2FtbC9hY3MiIElEPSJpZDg0OTUyMTk5Njg5MDU3MzYxODk2OTM5MzMzIiBJblJlc3BvbnNlVG89ImlkLWE3MzY0ZDFlNDQzMmFhOTA4NWE3YThiZDgyNGVhMmZhOGZhOGY2ODQiIElzc3VlSW5zdGFudD0iMjAyMC0wMy0wM1QxOToyNDoyOS4yMTNaIiBWZXJzaW9uPSIyLjAiIHhtbG5zOnNhbWwycD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOnByb3RvY29sIj48c2FtbDI6SXNzdWVyIEZvcm1hdD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOm5hbWVpZC1mb3JtYXQ6ZW50aXR5IiB4bWxuczpzYW1sMj0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmFzc2VydGlvbiI+aHR0cDovL3d3dy5va3RhLmNvbS9leGtwcHNhMXF3dUZWNEQ3ejBoNzwvc2FtbDI6SXNzdWVyPjxkczpTaWduYXR1cmUgeG1sbnM6ZHM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvMDkveG1sZHNpZyMiPjxkczpTaWduZWRJbmZvPjxkczpDYW5vbmljYWxpemF0aW9uTWV0aG9kIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS8xMC94bWwtZXhjLWMxNG4jIi8+PGRzOlNpZ25hdHVyZU1ldGhvZCBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvMDQveG1sZHNpZy1tb3JlI3JzYS1zaGEyNTYiLz48ZHM6UmVmZXJlbmNlIFVSST0iI2lkODQ5NTIxOTk2ODkwNTczNjE4OTY5MzkzMzMiPjxkczpUcmFuc2Zvcm1zPjxkczpUcmFuc2Zvcm0gQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwLzA5L3htbGRzaWcjZW52ZWxvcGVkLXNpZ25hdHVyZSIvPjxkczpUcmFuc2Zvcm0gQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzEwL3htbC1leGMtYzE0biMiLz48L2RzOlRyYW5zZm9ybXM+PGRzOkRpZ2VzdE1ldGhvZCBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvMDQveG1sZW5jI3NoYTI1NiIvPjxkczpEaWdlc3RWYWx1ZT5mSmFzQXdHNHQrOTh3MGFDY1h1dy9VbEdqQkRRcWtxeWpYQjFIMWdtN09nPTwvZHM6RGlnZXN0VmFsdWU+PC9kczpSZWZlcmVuY2U+PC9kczpTaWduZWRJbmZvPjxkczpTaWduYXR1cmVWYWx1ZT5SR2RFaHJRRURiRHRxcHVrRlFveEtmVTl2cmJxNnNyTjJucHBSOG13bnhDL21VZG1kT0lTMlRwRFl6UjlPTlZVY3FzOUR5UWxwRzZhQWVvSFN0eVFDUnlYcEV1MjVUNmVLTHgyNnNGMjNsSXNmenRXWVZlaXRWVzJlaEttRXdoc3RxOUZlRmxPd2p2SkhGUWJKMHVJK2NpbjVFY1Nhc2FJV0Y4b2oySlJxcnl5cXRDbFl2WUNOd3JDL090TjVqcUg2aVNhaWVhUmM2c3hPQlR0amNGTkp2cnVKY29JaTFrV2lkaEVlWUdjVnJTT1dJVGJFWWl2UnNWczVGYUxIdTBNaUVSeG91ZG9GNEwrMDJnZWdoN21MOG1Na1RUTWdtSEd6NklJdk1JbEpoZkthRjJJNE1Na1F5c2pHQ3RBb201NG5Va0tKOXNVRDlxbFJpWStidjkvNUE9PTwvZHM6U2lnbmF0dXJlVmFsdWU+PGRzOktleUluZm8+PGRzOlg1MDlEYXRhPjxkczpYNTA5Q2VydGlmaWNhdGU+TUlJRHBEQ0NBb3lnQXdJQkFnSUdBV1cwZERVUU1BMEdDU3FHU0liM0RRRUJDd1VBTUlHU01Rc3dDUVlEVlFRR0V3SlZVekVUTUJFRwpBMVVFQ0F3S1EyRnNhV1p2Y201cFlURVdNQlFHQTFVRUJ3d05VMkZ1SUVaeVlXNWphWE5qYnpFTk1Bc0dBMVVFQ2d3RVQydDBZVEVVCk1CSUdBMVVFQ3d3TFUxTlBVSEp2ZG1sa1pYSXhFekFSQmdOVkJBTU1DbVJsZGkwMU1UTXpPVFF4SERBYUJna3Foa2lHOXcwQkNRRVcKRFdsdVptOUFiMnQwWVM1amIyMHdIaGNOTVRnd09UQTNNVFF6TWpVNVdoY05Namd3T1RBM01UUXpNelU1V2pDQmtqRUxNQWtHQTFVRQpCaE1DVlZNeEV6QVJCZ05WQkFnTUNrTmhiR2xtYjNKdWFXRXhGakFVQmdOVkJBY01EVk5oYmlCR2NtRnVZMmx6WTI4eERUQUxCZ05WCkJBb01CRTlyZEdFeEZEQVNCZ05WQkFzTUMxTlRUMUJ5YjNacFpHVnlNUk13RVFZRFZRUUREQXBrWlhZdE5URXpNemswTVJ3d0dnWUoKS29aSWh2Y05BUWtCRmcxcGJtWnZRRzlyZEdFdVkyOXRNSUlCSWpBTkJna3Foa2lHOXcwQkFRRUZBQU9DQVE4QU1JSUJDZ0tDQVFFQQpvZ2djZmlTUko2UEdvSThYSEtVWWQ4OS9CUE1tZHV6UjM2NXlVRUtTSzZUSU9jQS9qcm5KenhXSFQ5UHN2QjR6bmFvRWRnMjdkbVgwCklaMkkwYmpTb3l2cDRCVDhadHN1cXBhbXNKT0ZEYWpmenJVL2RNTElRQ3dZMCszOEYreC9nTk5MK0JoWWI2em1yZHZvbWI3eXFJMkUKSnVITVhNUzc4NlVZNUdmRCsvbjBnUlN2ZCtEcElXOFpsc1pNRy9sbHl4TzFaY2N1VXF6a2JpVlY0dzF5NVBNdlNCTDdCQVdzVG45RwpJY2tRc3lGK2ZzRzBiS2xOM0pRakhtakZVclQwY25Xa0FKakdJVm1tcnA5TlVXeWMvU0kwMWk2V2x3Y1FzS3c0UEI3RVUzSjhCSU52CjltQ0dYcHdwNXZXWFJkUkdqVFQ0Qm1GbThsWTBRWEhxWGEvMitRSURBUUFCTUEwR0NTcUdTSWIzRFFFQkN3VUFBNElCQVFCeXBGb3gKL0lhVFhBS0ZzUlFpNldVRzBRaUJMQ1I4ZUxoU1VERjN4a2dFTGtOWURFclFLTnlWYVhyWW9Id1BvV1lwb2s2TVlkZE1rb28yWXVQRwpXNlY0ekRhMGswdWxiektsdmJiWlFwa3pJSkVqNGRyK1BhcW10SEFlN0M3WU5rajRqbGZKUDZRZHFNSytyQ0JWVTNrQ1gyYy9BUnVuClZ5L3BJdUxvd1hyUVVDRjBjY2NlUEQ4anJ5ZWorY21tOWpqSFdtUU5mSERNQXYvdnBHU1hWMlczYnpOQUxYeGZDb0txVTE1aWk2WVEKaFhVODVPRTVxWEVZOTJhYjNENjdncHB0ZTdlTm4vRzdEN2N1QVpoa3Q3d2ZMc2pvQ1ZLNGJaT3d4cVV3Nm1Qb1hYRnBrVG5sU284NgpwN3drYmVpaTdFcGptNUhjWFRQUEM3amQ3Wk91M0hzcjwvZHM6WDUwOUNlcnRpZmljYXRlPjwvZHM6WDUwOURhdGE+PC9kczpLZXlJbmZvPjwvZHM6U2lnbmF0dXJlPjxzYW1sMnA6U3RhdHVzIHhtbG5zOnNhbWwycD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOnByb3RvY29sIj48c2FtbDJwOlN0YXR1c0NvZGUgVmFsdWU9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpzdGF0dXM6U3VjY2VzcyIvPjwvc2FtbDJwOlN0YXR1cz48c2FtbDI6RW5jcnlwdGVkQXNzZXJ0aW9uIHhtbG5zOnNhbWwyPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YXNzZXJ0aW9uIj48eGVuYzpFbmNyeXB0ZWREYXRhIElkPSJfM2I2MWMxYmI3YTQxOTUzNjE5YmRlZjQxMjM4ODFhMGIiIFR5cGU9Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvMDQveG1sZW5jI0VsZW1lbnQiIHhtbG5zOnhlbmM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvMDQveG1sZW5jIyI+PHhlbmM6RW5jcnlwdGlvbk1ldGhvZCBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvMDQveG1sZW5jI2FlczI1Ni1jYmMiIHhtbG5zOnhlbmM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvMDQveG1sZW5jIyIvPjxkczpLZXlJbmZvIHhtbG5zOmRzPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwLzA5L3htbGRzaWcjIj48ZHM6UmV0cmlldmFsTWV0aG9kIFR5cGU9Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvMDQveG1sZW5jI0VuY3J5cHRlZEtleSIgVVJJPSIjX2QxOWE1OGQ5NGMxODE1NDA2YmQwMGFlZmQxNzJiODVkIi8+PC9kczpLZXlJbmZvPjx4ZW5jOkNpcGhlckRhdGEgeG1sbnM6eGVuYz0iaHR0cDovL3d3dy53My5vcmcvMjAwMS8wNC94bWxlbmMjIj48eGVuYzpDaXBoZXJWYWx1ZT4zVmdtUUwzb0hWanZtR1J4WUZveFJKMlpqcks2MDRZaDZnQ2h6K2t2OGEvOFdPd1E5R1dzZkVoWWJsam1jblJFbDh3N25aejZIZW1YZmI2WFRlTWh6Q2JUaVFZSEFjRThBZlVLaHRISGVLZk1RTWV5eTlnZ1pEM3ljT2lWQ0RQKzYraVo4cjY1WUExK24wVnc1WTJUMVJFSXpZOG1FZUdIWkNUVERJb21oRmRPYUhWRkltM2ZSQmREaXFrM0xFRWVvRG11V25uWkJpSnJ1OFNkbDkwcC8xK0x3NXVrNFhDSWd3MDc3Q0Z0WHYxb2MxcnFSUkd5TmhFWHJpWGhLMVdCck5Jemt3dVZZTVRmR0lzalREbFRJVWZUOGNwckY1ZGtQMmFyb1FEMWFlWGtyY2E2VWpYVTV0MkZ2NEYwZ1lSems3TkR2WERaSmxtNTM0Z21MSnB2YWNpQUtKZ3g1VnBpd09FL1VwVFMzbzJzRzBNZnZYUnl2N1MwdkJBSlRKUDdVUmJOczVUaTRQeHN2STV6aUM3Um9RRkZYN3BaWWZxcllCanVrbm00eDNBOFdGYzVjZFdtZEV6eHorUmQwMEpFNXBVN3VJaVZpNlF6S2cramlPWTREUzdVVStGL2pZdEluS0VaSkVFb3AvOVZ0SFJxZ1lqN3pxcGRwZTJ3SjFqcklQY05qcUlXZFY1YUhlajVsazZjWkJIcjBlRnVEWks0WDNNaDVQUnQ5UkVSb1hkbWt1N1M2cG93b0g3bEtEUEpIVEFkUVhUSFZvYXR3ZGI4VTd0Qy9kYVlvbGFrRXk2NVJ2Y01halFtYVJTeDgrVWwvZDBxMGIvUUZoWUh2NVZkTXNYYWRBU0Q0U0lWd1Mwa2FFSloxNmVhQkpPRG1WV1M0Y1YrdUxjUE9VdTZyV0t1NjhpVG9ZUDhESjFZZFNjRjhjcE0xbUNGVGFPNlpjT3RhRUQvRXJMdkJCdnFPOWgwT1EvWVo4TjVSN0hKSnQ3UlRHanV4TjBQMkxpYVcvem1vdTE1b2pySmJyZFRDZWxFV3dyWG5QTzRweXVTc01sL0tETC9JSm9aY3YxTlQ4aStKa2NuOXBQdVZuQ3BXckZnWWRGOWZTZkwrWDNraXd0SjlybjNYaVNvUVh2RUo5SjU4b3dzTWg0bjNFUFVJcnpFT3E1VGVibjVpTmxvdVMyOUhKR3RNeHphOG03UllhcDdkZjExeDE5bStrTDVmc0tnNHRzM1V1TWhVdkovblM5S1VtWWtFOENKdDF1cndWNThEem9EZ3dNTkRmbFphWGRYUmQ1MWtCVVE3Q2lab1p2NFVudkpCQXJNUjhVMHpoOG16M2xqdHpEdGdsVzBFSGNJdFp4b2htUlpLVHZVMXdOa2FHcmtwV3V4aXczSWhKTnF3blZlTG5wWkFvbEt6b1RlWkpjUjVxOHBzNEsvVnhwajhQK3pOcXJJSWpCRUtJbFVyMHl3UjZFbUpLTjFHWXpTaXF3VHdQNGp5MzIrZThuOUVQck9ka0NuWEJhak1pMlV2Rk5mQm05TWVUenY4RGNxWVphZUFhMUp6ZUl4TTd0YVozb0UxSzNYTjBYQ0tPUmJGT01SY2wwSHhQRE9vdDVQb2lDZ0xoeDNxVGJGUHdCcUdkazBmQTZmUDFNWmNMUm55N2lkdVpOQjhINHFBSlo0UU9icHgzSWZNaWxrclpxY29yNXMrMGhHMDBaZFE4YUgwa1lLejJZNDFQaFk5dTRwUXo1NXlhZkJtTGdCamREWDJNNHBiOHRnRmRsMUVyWWtkbW1TeFphNmhWRXk3QUU5Wkh2ZGV5dXZ1cFo0c1NwY1hldm5vMk5SeUVDWmwybHZyZGhkc0N6amJqRFRVM0JIRWczUjFnTTlhOWdJZzg1RklGejJIL3FxcnU0Snppdk1VZmdLdjZHdnpnSXpzWVFkSFhrTklsQkdEMFhSSEZiWGFLWXVYY3MzTDhHNzMrRXhiN1dPYTkzekRKMVNNRCt5cFpNOVNwMVZRbk5Qa2lWeExiTDNxQ01KUUZOQy9jM2theTVINUR1cUt6QjZZbjNtV2FKZjY3YmIzeXNNd255MnBTRSt4eW1BN0V0UVQwbzROWE9hRFQxQVE2bDhxeDNNUW51RUZubWg0L0xZV3FIVUtmTzhETkllR1FZS3BGcHJ4SVp1bVhLQlEvalpUdUF6dUVLUEZRY1hoU2dTM1NNUnAzYXI2aGhPbEs1dTNha0dKUURhYnZsVlZEckt6cDJrVWxoRjlVUnFtQVU0SlNSQTYrY3M2TFQxc1J1czd5V0JleWtsTnlaMnByUUpBV1prVHlaZDVKKzlwb3dvcUhQdGdhN2tEMlFNY0lJTm90QW5IWktnM2Z3UVlhWnJRME5rbUhWREw3akE5SloxUDgvYzdDdzRRMDFnWUIvZmwwVzJKZ3FHZlpnR29kSTdrM3dySmxxYlNQeGt3cWdnVytVSm42d2VGTWV4R0d3dHJseGhid0NhVjVMTXpjNk96dkdkallFM3FKQlhFRkNCdUVJcEozUXRsM2hNcXoxUEZXRXpIcWl1TUlxOG9FM1UyYXAvV2FrOXBtRkNGTll3L1k1Ry9pTE04QUFXRWwrTml3ZFN2N2xVN0wrOHM2NFJKRXJRdHplVFdTR1lneTlaRit5T1E2NUNpMHNaNlJYbEhtZkJMRWo0MHhrY0hrc1NqeHBKc0lOS1Izd0VPcWliSTNKVE1NYUpYc0FCbkhNbWtWanNyR0FQMnBtOVFQempvQ01NY3J0NHM5WDB6T2hKWEhPK2xqTzRVeWpZQkJxVUV3em1tR2V3V01OWVBVQlFrQmJnZlFwYmFNeTR2eTl1SURVWDBOVGRUSGlseUtpR3NnQmtXOWlLcm0yNTl3Ujl2eks2WG55ODcrR1J1TkRIZitLMnFlcy9UVE0xc292aU5oWUJaclV0Q2cyZnovcz08L3hlbmM6Q2lwaGVyVmFsdWU+PC94ZW5jOkNpcGhlckRhdGE+PC94ZW5jOkVuY3J5cHRlZERhdGE+PHhlbmM6RW5jcnlwdGVkS2V5IElkPSJfZDE5YTU4ZDk0YzE4MTU0MDZiZDAwYWVmZDE3MmI4NWQiIHhtbG5zOnhlbmM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvMDQveG1sZW5jIyI+PHhlbmM6RW5jcnlwdGlvbk1ldGhvZCBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvMDQveG1sZW5jI3JzYS1vYWVwLW1nZjFwIiB4bWxuczp4ZW5jPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzA0L3htbGVuYyMiPjxkczpEaWdlc3RNZXRob2QgQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwLzA5L3htbGRzaWcjc2hhMSIgeG1sbnM6ZHM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvMDkveG1sZHNpZyMiLz48L3hlbmM6RW5jcnlwdGlvbk1ldGhvZD48ZHM6S2V5SW5mbyB4bWxuczpkcz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC8wOS94bWxkc2lnIyI+PGRzOlg1MDlEYXRhPjxkczpYNTA5Q2VydGlmaWNhdGU+TUlJQjd6Q0NBVmdDQ1FERnpiS0lwN2IzTVRBTkJna3Foa2lHOXcwQkFRVUZBREE4TVFzd0NRWURWUVFHRXdKVlV6RUxNQWtHQTFVRQpDQXdDUjBFeEREQUtCZ05WQkFvTUEyWnZiekVTTUJBR0ExVUVBd3dKYkc5allXeG9iM04wTUI0WERURXpNVEF3TWpBd01EZzFNVm9YCkRURTBNVEF3TWpBd01EZzFNVm93UERFTE1Ba0dBMVVFQmhNQ1ZWTXhDekFKQmdOVkJBZ01Ba2RCTVF3d0NnWURWUVFLREFObWIyOHgKRWpBUUJnTlZCQU1NQ1d4dlkyRnNhRzl6ZERDQm56QU5CZ2txaGtpRzl3MEJBUUVGQUFPQmpRQXdnWWtDZ1lFQTFQTUhZbWhaajMwOAprV0xoWlZUNHZPdWxxeC85aWJtNUI4NmZQV3dVS0tRMmkxMk1ZdHowN3R6dWtQeW1pc1REaFFhcXlKOEtxYi82SmpobWVNbkVPZFR2ClNQbUhPOG0xWlZ2ZUpVNk5vS1JuL21QL0JEN0ZXNTJXaGJyVVhMU2VIVlNLZldrTms2UzRoazlNVjlUc3dUdnlSSUt2UnN3MFgvZ2YKbnFrcm9KY0NBd0VBQVRBTkJna3Foa2lHOXcwQkFRVUZBQU9CZ1FDTU1sSU8rR05jR2VrZXZLZ2tha3BNZEFxSmZzMjRtYUdiOTBEdgpUTGJSWlJEN1h2bjFNblZCQlM5aHpsWGlGTFlPSW5YQUNNVzVnY29SRmZlVFFMU291TU04bzU3aDB1S2pmVG11b1dITFFMaTZobkYrCmN2Q3NFRmlKWjRBYkYrRGdtTzZUYXJKOE8wNXQ4enZuT3dKbE5DQVNQWlJIL0ptRjh0WDBob0h1QVE9PTwvZHM6WDUwOUNlcnRpZmljYXRlPjwvZHM6WDUwOURhdGE+PC9kczpLZXlJbmZvPjx4ZW5jOkNpcGhlckRhdGEgeG1sbnM6eGVuYz0iaHR0cDovL3d3dy53My5vcmcvMjAwMS8wNC94bWxlbmMjIj48eGVuYzpDaXBoZXJWYWx1ZT5yRDl5RUpXdDBRbSt4UURSZXFRVUNEZDN5dEpiWTVtb01zcG5iZzcrY0ZWY1VrN2hVaDRScFpIUldSM2FkWTBRK2drQU1JcUhXYjFaZEFQL2g5emV0RktYVnFiajl4Y2FkSUFnR0UvQWZBMUs5SndXZVZQbmdjeERCNlZ0RU1oZG01cVJEemVnMEJSUnMwTE4xMTRObEN4dHhEMkx5R1c5M0NkZ2t2VXBnYWM9PC94ZW5jOkNpcGhlclZhbHVlPjwveGVuYzpDaXBoZXJEYXRhPjx4ZW5jOlJlZmVyZW5jZUxpc3Q+PHhlbmM6RGF0YVJlZmVyZW5jZSBVUkk9IiNfM2I2MWMxYmI3YTQxOTUzNjE5YmRlZjQxMjM4ODFhMGIiLz48L3hlbmM6UmVmZXJlbmNlTGlzdD48L3hlbmM6RW5jcnlwdGVkS2V5Pjwvc2FtbDI6RW5jcnlwdGVkQXNzZXJ0aW9uPjwvc2FtbDJwOlJlc3BvbnNlPg==saml-0.4.6/testdata/TestSPCanHandleOneloginResponse_IDPMetadata000066400000000000000000000051221415467341100245060ustar00rootroot00000000000000 MIIECDCCAvCgAwIBAgIUXun08CslLRWSLqNnDE1NtGJefl0wDQYJKoZIhvcNAQEF BQAwUzELMAkGA1UEBhMCVVMxDDAKBgNVBAoMA2N0dTEVMBMGA1UECwwMT25lTG9n aW4gSWRQMR8wHQYDVQQDDBZPbmVMb2dpbiBBY2NvdW50IDMyNjE0MB4XDTEzMDkz MDE5MzU0NFoXDTE4MTAwMTE5MzU0NFowUzELMAkGA1UEBhMCVVMxDDAKBgNVBAoM A2N0dTEVMBMGA1UECwwMT25lTG9naW4gSWRQMR8wHQYDVQQDDBZPbmVMb2dpbiBB Y2NvdW50IDMyNjE0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0OG8 V8mhovkj4rhGhjrbExRYbzKV2ZxfvGfEGXGUvXc6DqejYEdhZ2mIfCDojhQjk0By wiirAKMOt1GNuH7aWIE47D0ewtK5ylEAm7eVmoY4kxLCaW5wYrC1SzMnpeitUxqv sbnKz3jUKYHRggpfvVj4siHDZeIZa9a5rUvpMnnbOoFiZCIENpq3TC33ivOSZhEN RTzmvnk5GDoLHw/8qAgQiyT3D1xCkSBb54PHgkQ5Rq1odLM/hJ+L0jzCUQH4gxpW lEAab4K9s8fpBUBBh5gmJCYi8UbIlhqO8N2mynum33BU/vJ3PnawT4YYkTwRUx6Y +3fpmRBHql4h83SMewIDAQABo4HTMIHQMAwGA1UdEwEB/wQCMAAwHQYDVR0OBBYE FOfFFjHFj9a6xpngb11rrhgMe9ArMIGQBgNVHSMEgYgwgYWAFOfFFjHFj9a6xpng b11rrhgMe9AroVekVTBTMQswCQYDVQQGEwJVUzEMMAoGA1UECgwDY3R1MRUwEwYD VQQLDAxPbmVMb2dpbiBJZFAxHzAdBgNVBAMMFk9uZUxvZ2luIEFjY291bnQgMzI2 MTSCFF7p9PArJS0Vki6jZwxNTbRiXn5dMA4GA1UdDwEB/wQEAwIHgDANBgkqhkiG 9w0BAQUFAAOCAQEAMgln4NPMQn8Gyvq8CTP+c2e6CUzcvREKnThjxT9WcvV1ZVXM BNPm4cTqT361EdLzY5yWLUWXd4AvFnciqB3MHYa2nqTmnvLgmhkWe+hdFoNe5+IA 8AxGn+nqUISmyBeCxuUUAbRMuowiArwHIpzpEyRIYdSZRNF0dvgiPYyr/MiPXIcz pH5nLkvbLpcAF+R8Zh9nwY0g1JVyc6AB6j7YexuUQZpHH4s0Vdx/nWmrcFeLZKCT xcahHvU50e1yKX5thfVaJqI8QQ7xZxyu0TTsiaX0uw51JPOzPuAPph0z6xoS9oYx uzZ1y9sNHH6kH8GFnvS2MqyHiNz0h0Sq/q6n+w== urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress Support support@onelogin.com saml-0.4.6/testdata/TestSPCanHandleOneloginResponse_response000066400000000000000000000163341415467341100242760ustar00rootroot00000000000000PHNhbWxwOlJlc3BvbnNlIHhtbG5zOnNhbWw9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphc3NlcnRpb24iIHhtbG5zOnNhbWxwPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6cHJvdG9jb2wiIElEPSJwZnhlZDg4YzQzZC02NTA0LWUxZjEtNWFmMC00MGJlN2YyNzlmYzUiIFZlcnNpb249IjIuMCIgSXNzdWVJbnN0YW50PSIyMDE2LTAxLTA1VDE3OjUzOjExWiIgRGVzdGluYXRpb249Imh0dHBzOi8vMjllZTZkMmUubmdyb2suaW8vc2FtbC9hY3MiIEluUmVzcG9uc2VUbz0iaWQtZDQwYzE1YzEwNGI1MjY5MWVjY2YwYTJhNWM4YTE1NTk1YmU3NTQyMyI+PHNhbWw6SXNzdWVyPmh0dHBzOi8vYXBwLm9uZWxvZ2luLmNvbS9zYW1sL21ldGFkYXRhLzUwMzk4Mzwvc2FtbDpJc3N1ZXI+PGRzOlNpZ25hdHVyZSB4bWxuczpkcz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC8wOS94bWxkc2lnIyI+PGRzOlNpZ25lZEluZm8+PGRzOkNhbm9uaWNhbGl6YXRpb25NZXRob2QgQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzEwL3htbC1leGMtYzE0biMiLz48ZHM6U2lnbmF0dXJlTWV0aG9kIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMC8wOS94bWxkc2lnI3JzYS1zaGExIi8+PGRzOlJlZmVyZW5jZSBVUkk9IiNwZnhlZDg4YzQzZC02NTA0LWUxZjEtNWFmMC00MGJlN2YyNzlmYzUiPjxkczpUcmFuc2Zvcm1zPjxkczpUcmFuc2Zvcm0gQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwLzA5L3htbGRzaWcjZW52ZWxvcGVkLXNpZ25hdHVyZSIvPjxkczpUcmFuc2Zvcm0gQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzEwL3htbC1leGMtYzE0biMiLz48L2RzOlRyYW5zZm9ybXM+PGRzOkRpZ2VzdE1ldGhvZCBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvMDkveG1sZHNpZyNzaGExIi8+PGRzOkRpZ2VzdFZhbHVlPlNWQWFRZzh2bW1TUUw2L1lCbVMyeWRLUlA3ST08L2RzOkRpZ2VzdFZhbHVlPjwvZHM6UmVmZXJlbmNlPjwvZHM6U2lnbmVkSW5mbz48ZHM6U2lnbmF0dXJlVmFsdWU+c0JlVFZQMGJab1BSK2JmeUFrVnY2STNDVjdZOFhxbkoycjhmMStXbXIyZ0ZnblJGODVOdnZTUCtyMUJvN250dU9zd080ZkI0Uks0SHlTYnlsZzRiS0hLSDE5WDkxaFZBekpTeXNmbVMvZDV3ZzFDZmlXV3Q1UzJIQTUwOHRoWHVabndHM1h6NktuV0s4a1JkeDFkYytZUldnYUZ5ZDRnTEc5YUJUc1hPWjd2eC83UDRicnpORW00d1A5LzB0dWZ4Rytuc1k2RHB3bkVHQ2psK1ZVS3BnekVxd05OalFxWUZZU0FYRWsrVnQrWDNjMmQwSElyWlF2WW5OaDAyS3h1d1ZCVGhuM01helFOYU54Qy9zeWYza0RRQ1JyWkNZbytZdER1ZHpKVTlwM0EwWVhIVFFjc2RldHNIWlhDTWozbXV2emMwbUVCbHc0TGJjaEttbmJ5Wm1nPT08L2RzOlNpZ25hdHVyZVZhbHVlPjxkczpLZXlJbmZvPjxkczpYNTA5RGF0YT48ZHM6WDUwOUNlcnRpZmljYXRlPk1JSUVDRENDQXZDZ0F3SUJBZ0lVWHVuMDhDc2xMUldTTHFObkRFMU50R0plZmwwd0RRWUpLb1pJaHZjTkFRRUZCUUF3VXpFTE1Ba0dBMVVFQmhNQ1ZWTXhEREFLQmdOVkJBb01BMk4wZFRFVk1CTUdBMVVFQ3d3TVQyNWxURzluYVc0Z1NXUlFNUjh3SFFZRFZRUUREQlpQYm1WTWIyZHBiaUJCWTJOdmRXNTBJRE15TmpFME1CNFhEVEV6TURrek1ERTVNelUwTkZvWERURTRNVEF3TVRFNU16VTBORm93VXpFTE1Ba0dBMVVFQmhNQ1ZWTXhEREFLQmdOVkJBb01BMk4wZFRFVk1CTUdBMVVFQ3d3TVQyNWxURzluYVc0Z1NXUlFNUjh3SFFZRFZRUUREQlpQYm1WTWIyZHBiaUJCWTJOdmRXNTBJRE15TmpFME1JSUJJakFOQmdrcWhraUc5dzBCQVFFRkFBT0NBUThBTUlJQkNnS0NBUUVBME9HOFY4bWhvdmtqNHJoR2hqcmJFeFJZYnpLVjJaeGZ2R2ZFR1hHVXZYYzZEcWVqWUVkaFoybUlmQ0RvamhRamswQnl3aWlyQUtNT3QxR051SDdhV0lFNDdEMGV3dEs1eWxFQW03ZVZtb1k0a3hMQ2FXNXdZckMxU3pNbnBlaXRVeHF2c2JuS3ozalVLWUhSZ2dwZnZWajRzaUhEWmVJWmE5YTVyVXZwTW5uYk9vRmlaQ0lFTnBxM1RDMzNpdk9TWmhFTlJUem12bms1R0RvTEh3LzhxQWdRaXlUM0QxeENrU0JiNTRQSGdrUTVScTFvZExNL2hKK0wwanpDVVFINGd4cFdsRUFhYjRLOXM4ZnBCVUJCaDVnbUpDWWk4VWJJbGhxTzhOMm15bnVtMzNCVS92SjNQbmF3VDRZWWtUd1JVeDZZKzNmcG1SQkhxbDRoODNTTWV3SURBUUFCbzRIVE1JSFFNQXdHQTFVZEV3RUIvd1FDTUFBd0hRWURWUjBPQkJZRUZPZkZGakhGajlhNnhwbmdiMTFycmhnTWU5QXJNSUdRQmdOVkhTTUVnWWd3Z1lXQUZPZkZGakhGajlhNnhwbmdiMTFycmhnTWU5QXJvVmVrVlRCVE1Rc3dDUVlEVlFRR0V3SlZVekVNTUFvR0ExVUVDZ3dEWTNSMU1SVXdFd1lEVlFRTERBeFBibVZNYjJkcGJpQkpaRkF4SHpBZEJnTlZCQU1NRms5dVpVeHZaMmx1SUVGalkyOTFiblFnTXpJMk1UU0NGRjdwOVBBckpTMFZraTZqWnd4TlRiUmlYbjVkTUE0R0ExVWREd0VCL3dRRUF3SUhnREFOQmdrcWhraUc5dzBCQVFVRkFBT0NBUUVBTWdsbjROUE1RbjhHeXZxOENUUCtjMmU2Q1V6Y3ZSRUtuVGhqeFQ5V2N2VjFaVlhNQk5QbTRjVHFUMzYxRWRMelk1eVdMVVdYZDRBdkZuY2lxQjNNSFlhMm5xVG1udkxnbWhrV2UraGRGb05lNStJQThBeEduK25xVUlTbXlCZUN4dVVVQWJSTXVvd2lBcndISXB6cEV5UklZZFNaUk5GMGR2Z2lQWXlyL01pUFhJY3pwSDVuTGt2YkxwY0FGK1I4Wmg5bndZMGcxSlZ5YzZBQjZqN1lleHVVUVpwSEg0czBWZHgvbldtcmNGZUxaS0NUeGNhaEh2VTUwZTF5S1g1dGhmVmFKcUk4UVE3eFp4eXUwVFRzaWFYMHV3NTFKUE96UHVBUHBoMHo2eG9TOW9ZeHV6WjF5OXNOSEg2a0g4R0ZudlMyTXF5SGlOejBoMFNxL3E2bit3PT08L2RzOlg1MDlDZXJ0aWZpY2F0ZT48L2RzOlg1MDlEYXRhPjwvZHM6S2V5SW5mbz48L2RzOlNpZ25hdHVyZT48c2FtbHA6U3RhdHVzPjxzYW1scDpTdGF0dXNDb2RlIFZhbHVlPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6c3RhdHVzOlN1Y2Nlc3MiLz48L3NhbWxwOlN0YXR1cz48c2FtbDpBc3NlcnRpb24geG1sbnM6c2FtbD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmFzc2VydGlvbiIgeG1sbnM6eHM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvWE1MU2NoZW1hIiB4bWxuczp4c2k9Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvWE1MU2NoZW1hLWluc3RhbmNlIiBWZXJzaW9uPSIyLjAiIElEPSJBZDk0NWFlZGEzOGE1MDhmOGZhYzliYzk2MTNkNTk2NDJjMGQyZDhjYiIgSXNzdWVJbnN0YW50PSIyMDE2LTAxLTA1VDE3OjUzOjExWiI+PHNhbWw6SXNzdWVyPmh0dHBzOi8vYXBwLm9uZWxvZ2luLmNvbS9zYW1sL21ldGFkYXRhLzUwMzk4Mzwvc2FtbDpJc3N1ZXI+PHNhbWw6U3ViamVjdD48c2FtbDpOYW1lSUQgRm9ybWF0PSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoxLjE6bmFtZWlkLWZvcm1hdDplbWFpbEFkZHJlc3MiPnJvc3NAa25kci5vcmc8L3NhbWw6TmFtZUlEPjxzYW1sOlN1YmplY3RDb25maXJtYXRpb24gTWV0aG9kPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6Y206YmVhcmVyIj48c2FtbDpTdWJqZWN0Q29uZmlybWF0aW9uRGF0YSBOb3RPbk9yQWZ0ZXI9IjIwMTYtMDEtMDVUMTc6NTY6MTFaIiBSZWNpcGllbnQ9Imh0dHBzOi8vMjllZTZkMmUubmdyb2suaW8vc2FtbC9hY3MiIEluUmVzcG9uc2VUbz0iaWQtZDQwYzE1YzEwNGI1MjY5MWVjY2YwYTJhNWM4YTE1NTk1YmU3NTQyMyIvPjwvc2FtbDpTdWJqZWN0Q29uZmlybWF0aW9uPjwvc2FtbDpTdWJqZWN0PjxzYW1sOkNvbmRpdGlvbnMgTm90QmVmb3JlPSIyMDE2LTAxLTA1VDE3OjUwOjExWiIgTm90T25PckFmdGVyPSIyMDE2LTAxLTA1VDE3OjU2OjExWiI+PHNhbWw6QXVkaWVuY2VSZXN0cmljdGlvbj48c2FtbDpBdWRpZW5jZT5odHRwczovLzI5ZWU2ZDJlLm5ncm9rLmlvL3NhbWwvbWV0YWRhdGE8L3NhbWw6QXVkaWVuY2U+PC9zYW1sOkF1ZGllbmNlUmVzdHJpY3Rpb24+PC9zYW1sOkNvbmRpdGlvbnM+PHNhbWw6QXV0aG5TdGF0ZW1lbnQgQXV0aG5JbnN0YW50PSIyMDE2LTAxLTA1VDE3OjUzOjEwWiIgU2Vzc2lvbk5vdE9uT3JBZnRlcj0iMjAxNi0wMS0wNlQxNzo1MzoxMVoiIFNlc3Npb25JbmRleD0iX2ViZGNiZTgwLTk1ZmYtMDEzMy1kODcxLTM4Y2EzYTY2MmYxYyI+PHNhbWw6QXV0aG5Db250ZXh0PjxzYW1sOkF1dGhuQ29udGV4dENsYXNzUmVmPnVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphYzpjbGFzc2VzOlBhc3N3b3JkUHJvdGVjdGVkVHJhbnNwb3J0PC9zYW1sOkF1dGhuQ29udGV4dENsYXNzUmVmPjwvc2FtbDpBdXRobkNvbnRleHQ+PC9zYW1sOkF1dGhuU3RhdGVtZW50PjxzYW1sOkF0dHJpYnV0ZVN0YXRlbWVudD48c2FtbDpBdHRyaWJ1dGUgTmFtZUZvcm1hdD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmF0dHJuYW1lLWZvcm1hdDpiYXNpYyIgTmFtZT0iVXNlci5lbWFpbCI+PHNhbWw6QXR0cmlidXRlVmFsdWUgeG1sbnM6eHNpPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxL1hNTFNjaGVtYS1pbnN0YW5jZSIgeHNpOnR5cGU9InhzOnN0cmluZyI+cm9zc0BrbmRyLm9yZzwvc2FtbDpBdHRyaWJ1dGVWYWx1ZT48L3NhbWw6QXR0cmlidXRlPjxzYW1sOkF0dHJpYnV0ZSBOYW1lRm9ybWF0PSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YXR0cm5hbWUtZm9ybWF0OmJhc2ljIiBOYW1lPSJtZW1iZXJPZiI+PHNhbWw6QXR0cmlidXRlVmFsdWUgeG1sbnM6eHNpPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxL1hNTFNjaGVtYS1pbnN0YW5jZSIgeHNpOnR5cGU9InhzOnN0cmluZyIvPjwvc2FtbDpBdHRyaWJ1dGU+PHNhbWw6QXR0cmlidXRlIE5hbWVGb3JtYXQ9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphdHRybmFtZS1mb3JtYXQ6YmFzaWMiIE5hbWU9IlVzZXIuTGFzdE5hbWUiPjxzYW1sOkF0dHJpYnV0ZVZhbHVlIHhtbG5zOnhzaT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEtaW5zdGFuY2UiIHhzaTp0eXBlPSJ4czpzdHJpbmciPktpbmRlcjwvc2FtbDpBdHRyaWJ1dGVWYWx1ZT48L3NhbWw6QXR0cmlidXRlPjxzYW1sOkF0dHJpYnV0ZSBOYW1lRm9ybWF0PSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YXR0cm5hbWUtZm9ybWF0OmJhc2ljIiBOYW1lPSJQZXJzb25JbW11dGFibGVJRCI+PHNhbWw6QXR0cmlidXRlVmFsdWUgeG1sbnM6eHNpPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxL1hNTFNjaGVtYS1pbnN0YW5jZSIgeHNpOnR5cGU9InhzOnN0cmluZyIvPjwvc2FtbDpBdHRyaWJ1dGU+PHNhbWw6QXR0cmlidXRlIE5hbWVGb3JtYXQ9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphdHRybmFtZS1mb3JtYXQ6YmFzaWMiIE5hbWU9IlVzZXIuRmlyc3ROYW1lIj48c2FtbDpBdHRyaWJ1dGVWYWx1ZSB4bWxuczp4c2k9Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvWE1MU2NoZW1hLWluc3RhbmNlIiB4c2k6dHlwZT0ieHM6c3RyaW5nIj5Sb3NzPC9zYW1sOkF0dHJpYnV0ZVZhbHVlPjwvc2FtbDpBdHRyaWJ1dGU+PC9zYW1sOkF0dHJpYnV0ZVN0YXRlbWVudD48L3NhbWw6QXNzZXJ0aW9uPjwvc2FtbHA6UmVzcG9uc2U+Cgo=saml-0.4.6/testdata/TestSPCanHandlePlaintextResponse_IDPMetadata000066400000000000000000000042771415467341100247160ustar00rootroot00000000000000 MIIDdDCCAlygAwIBAgIGAVISlIlYMA0GCSqGSIb3DQEBCwUAMHsxFDASBgNVBAoTC0dvb2dsZSBJ bmMuMRYwFAYDVQQHEw1Nb3VudGFpbiBWaWV3MQ8wDQYDVQQDEwZHb29nbGUxGDAWBgNVBAsTD0dv b2dsZSBGb3IgV29yazELMAkGA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWEwHhcNMTYwMTA1 MTYxNzQ5WhcNMjEwMTAzMTYxNzQ5WjB7MRQwEgYDVQQKEwtHb29nbGUgSW5jLjEWMBQGA1UEBxMN TW91bnRhaW4gVmlldzEPMA0GA1UEAxMGR29vZ2xlMRgwFgYDVQQLEw9Hb29nbGUgRm9yIFdvcmsx CzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A MIIBCgKCAQEAmUfMUPxHSY/ZYZ88fUGAlhUP4Ni7zj54vsrsPDA4UhQiReEDRunN1q3OHsShRong gd4LvA83/e/3pm/V60R6vyMfj3Z/IGWY+eZ97EJUvjktt+VRoAi26oeY9ZW6S85yapvA3iuhEwIQ OcuPm1OqRQ0yQ4sUD+WtL/QSmlYvDP5TK1d6whTisNsKSqeFZCb/s9OX01UexW1BuDOLeVt0rCW1 kRNcBBLDmd4hnDP0SVq7nLhNFYXj2Ea6WsyRAIvchaUGy+Ima2okXm95Ye9kn8e118i/5rReyKCm BlskMkNaA4KWKvIQm3DdjgONgEd0IvKExyLwY7a5/JIUvBhb9QIDAQABMA0GCSqGSIb3DQEBCwUA A4IBAQAUDLMnHpzfp4ShdBqCreW48f8rU94q2qMwrU+W6DkOrGJTASVGS9Rib/MKAiRYOmqlaqEY NP57pCrE/nRB5FVdE+AlSx/fR3khsQ3zf/4dYs21SvGf+Oas99XEbWfV0OmPMYm3IrSCOBEV31wh 41qRc5QLnR+XutNPbSBN+tn+giRCLGCBLe81oVw4fRGQbgkd87rfLOy3G630I6s/J5feFFUT8d7h 9mpOeOqLCPrKpq+wI3aD3lf4mXqKIDNiHHRoNl67ANPu/N3fNU1HplVtvroVpiNp87frgdlKTEcg PUkfbaYHQGP6IS0lzeCeDX0wab3qRoh7/jJt5/BR8Iwf urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress saml-0.4.6/testdata/TestSPCanHandlePlaintextResponse_response000066400000000000000000000143341415467341100244720ustar00rootroot00000000000000PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+PHNhbWwycDpSZXNwb25zZSB4bWxuczpzYW1sMnA9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpwcm90b2NvbCIgRGVzdGluYXRpb249Imh0dHBzOi8vMjllZTZkMmUubmdyb2suaW8vc2FtbC9hY3MiIElEPSJfZmMxNDFkYjI4NGViMzA5ODYwNTM1MWJkZTRkOWJlNTkiIEluUmVzcG9uc2VUbz0iaWQtZmQ0MTlhNWFiMDQ3MjY0NTQyN2Y4ZTA3ZDg3YTNhNWRkMGIyZTlhNiIgSXNzdWVJbnN0YW50PSIyMDE2LTAxLTA1VDE2OjU1OjM5LjM0OFoiIFZlcnNpb249IjIuMCI+PHNhbWwyOklzc3VlciB4bWxuczpzYW1sMj0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmFzc2VydGlvbiI+aHR0cHM6Ly9hY2NvdW50cy5nb29nbGUuY29tL28vc2FtbDI/aWRwaWQ9QzAyZGZsMXIxPC9zYW1sMjpJc3N1ZXI+PGRzOlNpZ25hdHVyZSB4bWxuczpkcz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC8wOS94bWxkc2lnIyI+PGRzOlNpZ25lZEluZm8+PGRzOkNhbm9uaWNhbGl6YXRpb25NZXRob2QgQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzEwL3htbC1leGMtYzE0biMiLz48ZHM6U2lnbmF0dXJlTWV0aG9kIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS8wNC94bWxkc2lnLW1vcmUjcnNhLXNoYTI1NiIvPjxkczpSZWZlcmVuY2UgVVJJPSIjX2ZjMTQxZGIyODRlYjMwOTg2MDUzNTFiZGU0ZDliZTU5Ij48ZHM6VHJhbnNmb3Jtcz48ZHM6VHJhbnNmb3JtIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMC8wOS94bWxkc2lnI2VudmVsb3BlZC1zaWduYXR1cmUiLz48ZHM6VHJhbnNmb3JtIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS8xMC94bWwtZXhjLWMxNG4jIi8+PC9kczpUcmFuc2Zvcm1zPjxkczpEaWdlc3RNZXRob2QgQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzA0L3htbGVuYyNzaGEyNTYiLz48ZHM6RGlnZXN0VmFsdWU+bHRNRUJLRzRZNVNLeERScUxHR2xFSGtPd3hla3dQOStybnA2WEtqdkJxVT08L2RzOkRpZ2VzdFZhbHVlPjwvZHM6UmVmZXJlbmNlPjwvZHM6U2lnbmVkSW5mbz48ZHM6U2lnbmF0dXJlVmFsdWU+SFBVV0pmYTlqdVdiKy9wZ0YrQklsc2pycE40NkE0RUNiT3hNdXhmWEFRUCtrMU5KMG9EdTJKYk1pZHpmclJBRkRHMjZaNjZWQWtkcwpBRmYwVFgzMWxvVjdaU0tGS0lVY0tuaFlXTHFuUTZLbmRydnJLbzF5UUhzUkdUNzJoVjl3SWdqTFRTZm5FV3QvOEMxaERQQi96R0txClhXZ3VvNFFHYlZUeVBoVVh3eEFzRmxBNjFDdkE5Q1pzU2xpeHBaY2pOVjUyQmMydzI5RUNRNStBcHZGWjVqRU1EN1JiQTVpMzdBbmgKUVBCeVYrZXo4ZU9Yc0hvQlhsR0drTjlDR201MFR6djZ3TW12WkdkT2pKWlhvRWZGUTA4UFJwbE9DQWpxSjM3QnhpWitLZWtUaE1KYgorelowcG1yeWR2V3lONEMzNWcycGVueGw2QUtxYnhMaXlJUkVaZz09PC9kczpTaWduYXR1cmVWYWx1ZT48ZHM6S2V5SW5mbz48ZHM6WDUwOURhdGE+PGRzOlg1MDlTdWJqZWN0TmFtZT5TVD1DYWxpZm9ybmlhLEM9VVMsT1U9R29vZ2xlIEZvciBXb3JrLENOPUdvb2dsZSxMPU1vdW50YWluIFZpZXcsTz1Hb29nbGUgSW5jLjwvZHM6WDUwOVN1YmplY3ROYW1lPjxkczpYNTA5Q2VydGlmaWNhdGU+TUlJRGREQ0NBbHlnQXdJQkFnSUdBVklTbElsWU1BMEdDU3FHU0liM0RRRUJDd1VBTUhzeEZEQVNCZ05WQkFvVEMwZHZiMmRzWlNCSgpibU11TVJZd0ZBWURWUVFIRXcxTmIzVnVkR0ZwYmlCV2FXVjNNUTh3RFFZRFZRUURFd1pIYjI5bmJHVXhHREFXQmdOVkJBc1REMGR2CmIyZHNaU0JHYjNJZ1YyOXlhekVMTUFrR0ExVUVCaE1DVlZNeEV6QVJCZ05WQkFnVENrTmhiR2xtYjNKdWFXRXdIaGNOTVRZd01UQTEKTVRZeE56UTVXaGNOTWpFd01UQXpNVFl4TnpRNVdqQjdNUlF3RWdZRFZRUUtFd3RIYjI5bmJHVWdTVzVqTGpFV01CUUdBMVVFQnhNTgpUVzkxYm5SaGFXNGdWbWxsZHpFUE1BMEdBMVVFQXhNR1IyOXZaMnhsTVJnd0ZnWURWUVFMRXc5SGIyOW5iR1VnUm05eUlGZHZjbXN4CkN6QUpCZ05WQkFZVEFsVlRNUk13RVFZRFZRUUlFd3BEWVd4cFptOXlibWxoTUlJQklqQU5CZ2txaGtpRzl3MEJBUUVGQUFPQ0FROEEKTUlJQkNnS0NBUUVBbVVmTVVQeEhTWS9aWVo4OGZVR0FsaFVQNE5pN3pqNTR2c3JzUERBNFVoUWlSZUVEUnVuTjFxM09Ic1NoUm9uZwpnZDRMdkE4My9lLzNwbS9WNjBSNnZ5TWZqM1ovSUdXWStlWjk3RUpVdmprdHQrVlJvQWkyNm9lWTlaVzZTODV5YXB2QTNpdWhFd0lRCk9jdVBtMU9xUlEweVE0c1VEK1d0TC9RU21sWXZEUDVUSzFkNndoVGlzTnNLU3FlRlpDYi9zOU9YMDFVZXhXMUJ1RE9MZVZ0MHJDVzEKa1JOY0JCTERtZDRobkRQMFNWcTduTGhORllYajJFYTZXc3lSQUl2Y2hhVUd5K0ltYTJva1htOTVZZTlrbjhlMTE4aS81clJleUtDbQpCbHNrTWtOYUE0S1dLdklRbTNEZGpnT05nRWQwSXZLRXh5THdZN2E1L0pJVXZCaGI5UUlEQVFBQk1BMEdDU3FHU0liM0RRRUJDd1VBCkE0SUJBUUFVRExNbkhwemZwNFNoZEJxQ3JlVzQ4ZjhyVTk0cTJxTXdyVStXNkRrT3JHSlRBU1ZHUzlSaWIvTUtBaVJZT21xbGFxRVkKTlA1N3BDckUvblJCNUZWZEUrQWxTeC9mUjNraHNRM3pmLzRkWXMyMVN2R2YrT2FzOTlYRWJXZlYwT21QTVltM0lyU0NPQkVWMzF3aAo0MXFSYzVRTG5SK1h1dE5QYlNCTit0bitnaVJDTEdDQkxlODFvVnc0ZlJHUWJna2Q4N3JmTE95M0c2MzBJNnMvSjVmZUZGVVQ4ZDdoCjltcE9lT3FMQ1ByS3BxK3dJM2FEM2xmNG1YcUtJRE5pSEhSb05sNjdBTlB1L04zZk5VMUhwbFZ0dnJvVnBpTnA4N2ZyZ2RsS1RFY2cKUFVrZmJhWUhRR1A2SVMwbHplQ2VEWDB3YWIzcVJvaDcvakp0NS9CUjhJd2Y8L2RzOlg1MDlDZXJ0aWZpY2F0ZT48L2RzOlg1MDlEYXRhPjwvZHM6S2V5SW5mbz48L2RzOlNpZ25hdHVyZT48c2FtbDJwOlN0YXR1cz48c2FtbDJwOlN0YXR1c0NvZGUgVmFsdWU9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpzdGF0dXM6U3VjY2VzcyIvPjwvc2FtbDJwOlN0YXR1cz48c2FtbDI6QXNzZXJ0aW9uIHhtbG5zOnNhbWwyPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YXNzZXJ0aW9uIiBJRD0iXzllNzY0OTUyZTZhMjYxZTE5NDA5YTM4MjU1ODEwMzNkIiBJc3N1ZUluc3RhbnQ9IjIwMTYtMDEtMDVUMTY6NTU6MzkuMzQ4WiIgVmVyc2lvbj0iMi4wIj48c2FtbDI6SXNzdWVyPmh0dHBzOi8vYWNjb3VudHMuZ29vZ2xlLmNvbS9vL3NhbWwyP2lkcGlkPUMwMmRmbDFyMTwvc2FtbDI6SXNzdWVyPjxzYW1sMjpTdWJqZWN0PjxzYW1sMjpOYW1lSUQ+cm9zc0BvY3RvbGFicy5pbzwvc2FtbDI6TmFtZUlEPjxzYW1sMjpTdWJqZWN0Q29uZmlybWF0aW9uIE1ldGhvZD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmNtOmJlYXJlciI+PHNhbWwyOlN1YmplY3RDb25maXJtYXRpb25EYXRhIEluUmVzcG9uc2VUbz0iaWQtZmQ0MTlhNWFiMDQ3MjY0NTQyN2Y4ZTA3ZDg3YTNhNWRkMGIyZTlhNiIgTm90T25PckFmdGVyPSIyMDE2LTAxLTA1VDE3OjAwOjM5LjM0OFoiIFJlY2lwaWVudD0iaHR0cHM6Ly8yOWVlNmQyZS5uZ3Jvay5pby9zYW1sL2FjcyIvPjwvc2FtbDI6U3ViamVjdENvbmZpcm1hdGlvbj48L3NhbWwyOlN1YmplY3Q+PHNhbWwyOkNvbmRpdGlvbnMgTm90QmVmb3JlPSIyMDE2LTAxLTA1VDE2OjUwOjM5LjM0OFoiIE5vdE9uT3JBZnRlcj0iMjAxNi0wMS0wNVQxNzowMDozOS4zNDhaIj48c2FtbDI6QXVkaWVuY2VSZXN0cmljdGlvbj48c2FtbDI6QXVkaWVuY2U+aHR0cHM6Ly8yOWVlNmQyZS5uZ3Jvay5pby9zYW1sL21ldGFkYXRhPC9zYW1sMjpBdWRpZW5jZT48L3NhbWwyOkF1ZGllbmNlUmVzdHJpY3Rpb24+PC9zYW1sMjpDb25kaXRpb25zPjxzYW1sMjpBdHRyaWJ1dGVTdGF0ZW1lbnQ+PHNhbWwyOkF0dHJpYnV0ZSBOYW1lPSJwaG9uZSIvPjxzYW1sMjpBdHRyaWJ1dGUgTmFtZT0iYWRkcmVzcyIvPjxzYW1sMjpBdHRyaWJ1dGUgTmFtZT0iam9iVGl0bGUiLz48c2FtbDI6QXR0cmlidXRlIE5hbWU9ImZpcnN0TmFtZSI+PHNhbWwyOkF0dHJpYnV0ZVZhbHVlIHhtbG5zOnhzPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxL1hNTFNjaGVtYSIgeG1sbnM6eHNpPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxL1hNTFNjaGVtYS1pbnN0YW5jZSIgeHNpOnR5cGU9InhzOmFueVR5cGUiPlJvc3M8L3NhbWwyOkF0dHJpYnV0ZVZhbHVlPjwvc2FtbDI6QXR0cmlidXRlPjxzYW1sMjpBdHRyaWJ1dGUgTmFtZT0ibGFzdE5hbWUiPjxzYW1sMjpBdHRyaWJ1dGVWYWx1ZSB4bWxuczp4cz0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEiIHhtbG5zOnhzaT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEtaW5zdGFuY2UiIHhzaTp0eXBlPSJ4czphbnlUeXBlIj5LaW5kZXI8L3NhbWwyOkF0dHJpYnV0ZVZhbHVlPjwvc2FtbDI6QXR0cmlidXRlPjwvc2FtbDI6QXR0cmlidXRlU3RhdGVtZW50PjxzYW1sMjpBdXRoblN0YXRlbWVudCBBdXRobkluc3RhbnQ9IjIwMTYtMDEtMDVUMTY6NTU6MzguMDAwWiIgU2Vzc2lvbkluZGV4PSJfOWU3NjQ5NTJlNmEyNjFlMTk0MDlhMzgyNTU4MTAzM2QiPjxzYW1sMjpBdXRobkNvbnRleHQ+PHNhbWwyOkF1dGhuQ29udGV4dENsYXNzUmVmPnVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphYzpjbGFzc2VzOnVuc3BlY2lmaWVkPC9zYW1sMjpBdXRobkNvbnRleHRDbGFzc1JlZj48L3NhbWwyOkF1dGhuQ29udGV4dD48L3NhbWwyOkF1dGhuU3RhdGVtZW50Pjwvc2FtbDI6QXNzZXJ0aW9uPjwvc2FtbDJwOlJlc3BvbnNlPg==saml-0.4.6/testdata/TestSPCanProduceMetadataWithBothCerts_metadata000066400000000000000000000063221415467341100253230ustar00rootroot00000000000000 MIIB7zCCAVgCCQDFzbKIp7b3MTANBgkqhkiG9w0BAQUFADA8MQswCQYDVQQGEwJVUzELMAkGA1UECAwCR0ExDDAKBgNVBAoMA2ZvbzESMBAGA1UEAwwJbG9jYWxob3N0MB4XDTEzMTAwMjAwMDg1MVoXDTE0MTAwMjAwMDg1MVowPDELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkdBMQwwCgYDVQQKDANmb28xEjAQBgNVBAMMCWxvY2FsaG9zdDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA1PMHYmhZj308kWLhZVT4vOulqx/9ibm5B86fPWwUKKQ2i12MYtz07tzukPymisTDhQaqyJ8Kqb/6JjhmeMnEOdTvSPmHO8m1ZVveJU6NoKRn/mP/BD7FW52WhbrUXLSeHVSKfWkNk6S4hk9MV9TswTvyRIKvRsw0X/gfnqkroJcCAwEAATANBgkqhkiG9w0BAQUFAAOBgQCMMlIO+GNcGekevKgkakpMdAqJfs24maGb90DvTLbRZRD7Xvn1MnVBBS9hzlXiFLYOInXACMW5gcoRFfeTQLSouMM8o57h0uKjfTmuoWHLQLi6hnF+cvCsEFiJZ4AbF+DgmO6TarJ8O05t8zvnOwJlNCASPZRH/JmF8tX0hoHuAQ== MIIB7zCCAVgCCQDFzbKIp7b3MTANBgkqhkiG9w0BAQUFADA8MQswCQYDVQQGEwJVUzELMAkGA1UECAwCR0ExDDAKBgNVBAoMA2ZvbzESMBAGA1UEAwwJbG9jYWxob3N0MB4XDTEzMTAwMjAwMDg1MVoXDTE0MTAwMjAwMDg1MVowPDELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkdBMQwwCgYDVQQKDANmb28xEjAQBgNVBAMMCWxvY2FsaG9zdDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA1PMHYmhZj308kWLhZVT4vOulqx/9ibm5B86fPWwUKKQ2i12MYtz07tzukPymisTDhQaqyJ8Kqb/6JjhmeMnEOdTvSPmHO8m1ZVveJU6NoKRn/mP/BD7FW52WhbrUXLSeHVSKfWkNk6S4hk9MV9TswTvyRIKvRsw0X/gfnqkroJcCAwEAATANBgkqhkiG9w0BAQUFAAOBgQCMMlIO+GNcGekevKgkakpMdAqJfs24maGb90DvTLbRZRD7Xvn1MnVBBS9hzlXiFLYOInXACMW5gcoRFfeTQLSouMM8o57h0uKjfTmuoWHLQLi6hnF+cvCsEFiJZ4AbF+DgmO6TarJ8O05t8zvnOwJlNCASPZRH/JmF8tX0hoHuAQ== saml-0.4.6/testdata/TestSPCanProduceMetadataWithEncryptionCert_metadata000066400000000000000000000044111415467341100263730ustar00rootroot00000000000000 MIIB7zCCAVgCCQDFzbKIp7b3MTANBgkqhkiG9w0BAQUFADA8MQswCQYDVQQGEwJVUzELMAkGA1UECAwCR0ExDDAKBgNVBAoMA2ZvbzESMBAGA1UEAwwJbG9jYWxob3N0MB4XDTEzMTAwMjAwMDg1MVoXDTE0MTAwMjAwMDg1MVowPDELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkdBMQwwCgYDVQQKDANmb28xEjAQBgNVBAMMCWxvY2FsaG9zdDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA1PMHYmhZj308kWLhZVT4vOulqx/9ibm5B86fPWwUKKQ2i12MYtz07tzukPymisTDhQaqyJ8Kqb/6JjhmeMnEOdTvSPmHO8m1ZVveJU6NoKRn/mP/BD7FW52WhbrUXLSeHVSKfWkNk6S4hk9MV9TswTvyRIKvRsw0X/gfnqkroJcCAwEAATANBgkqhkiG9w0BAQUFAAOBgQCMMlIO+GNcGekevKgkakpMdAqJfs24maGb90DvTLbRZRD7Xvn1MnVBBS9hzlXiFLYOInXACMW5gcoRFfeTQLSouMM8o57h0uKjfTmuoWHLQLi6hnF+cvCsEFiJZ4AbF+DgmO6TarJ8O05t8zvnOwJlNCASPZRH/JmF8tX0hoHuAQ== saml-0.4.6/testdata/TestSPCanProducePostLogoutRequest_form000066400000000000000000000024171415467341100240050ustar00rootroot00000000000000
saml-0.4.6/testdata/TestSPCanProducePostLogoutResponse_form000066400000000000000000000023061415467341100241500ustar00rootroot00000000000000
saml-0.4.6/testdata/TestSPCanProducePostRequest_form000066400000000000000000000024431415467341100226120ustar00rootroot00000000000000
saml-0.4.6/testdata/TestSPCanProduceRedirectLogoutRequest_decodedRequest000066400000000000000000000012261415467341100266130ustar00rootroot00000000000000https://15661444.ngrok.io/saml2/metadataross@octolabs.iosaml-0.4.6/testdata/TestSPCanProduceRedirectLogoutResponse_decodedResponse000066400000000000000000000011331415467341100271240ustar00rootroot00000000000000https://15661444.ngrok.io/saml2/metadatasaml-0.4.6/testdata/TestSPCanProduceRedirectRequest_decoded_request000066400000000000000000000012411415467341100256150ustar00rootroot00000000000000https://15661444.ngrok.io/saml2/metadatasaml-0.4.6/testdata/TestSPCanProduceSignedRequestPostBinding_decodedRequest000066400000000000000000000043641415467341100272400ustar00rootroot00000000000000https://15661444.ngrok.io/saml2/metadatahJD/5Zu9pV3hk8yrz7PF3aOAeb4=bAem8RP7VOdIl3m7TlVuh1mq2pymKHLzXRsrlrqum8tkYsXnvSDcUsn5/gCNUd+hbuA2mjp4xVAJbSDoXd6ePQDyCCsEwvY6tnb5aThIFI+FaM2h9UeoENn9cCuFZIp25tqUnvqg45S8kzJw3HZ17o2Qgu9Zsy89yU5kxwCOkEk=MIIB7zCCAVgCCQDFzbKIp7b3MTANBgkqhkiG9w0BAQUFADA8MQswCQYDVQQGEwJVUzELMAkGA1UECAwCR0ExDDAKBgNVBAoMA2ZvbzESMBAGA1UEAwwJbG9jYWxob3N0MB4XDTEzMTAwMjAwMDg1MVoXDTE0MTAwMjAwMDg1MVowPDELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkdBMQwwCgYDVQQKDANmb28xEjAQBgNVBAMMCWxvY2FsaG9zdDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA1PMHYmhZj308kWLhZVT4vOulqx/9ibm5B86fPWwUKKQ2i12MYtz07tzukPymisTDhQaqyJ8Kqb/6JjhmeMnEOdTvSPmHO8m1ZVveJU6NoKRn/mP/BD7FW52WhbrUXLSeHVSKfWkNk6S4hk9MV9TswTvyRIKvRsw0X/gfnqkroJcCAwEAATANBgkqhkiG9w0BAQUFAAOBgQCMMlIO+GNcGekevKgkakpMdAqJfs24maGb90DvTLbRZRD7Xvn1MnVBBS9hzlXiFLYOInXACMW5gcoRFfeTQLSouMM8o57h0uKjfTmuoWHLQLi6hnF+cvCsEFiJZ4AbF+DgmO6TarJ8O05t8zvnOwJlNCASPZRH/JmF8tX0hoHuAQ==saml-0.4.6/testdata/TestSPCanProduceSignedRequestRedirectBinding_decodedRequest000066400000000000000000000012411415467341100300430ustar00rootroot00000000000000https://15661444.ngrok.io/saml2/metadatasaml-0.4.6/testdata/TestSPCanProduceSignedRequestRedirectBinding_queryString000066400000000000000000000014651415467341100274470ustar00rootroot00000000000000SAMLRequest=nFJNb9swDP0rhu62RNU1CqE2kDUYFqBbgzjbYTfVZhNituSJ9Lb%2B%2B8Fph2WXDOiV4uP70LtlPw6TW81yDDv8PiNL9mscArvloVZzCi56JnbBj8hOOteuPt47WxjnmTEJxaDOINNlzJSixC4OKtusa0V9boyxpjSVuTHedAbBgIUSKrgBDx2gNdba0lYq%2B4KJKYZa2cKobMM84yaw%2BCC1sgauc7C5gb0BdwXOQgH26qvK1shCwcsJeRSZ2GlN%2FVQIsvCRHouYDstATyk%2B0YB60Wr1DntK2Ilu2weVrf5YvYuB5xFTi%2BkHdfh5d%2F%2F3KlxXFZRlWYRDit8KinoJxGrfscq2r8bfUegpHC6n9PiyxO7Dfr%2FNtw%2FtXjWnn3In2yl7H9Po5fKRZUJ9%2FnRadRiE5Fk1%2FxM7ovjei7%2FVZ3zNa00%2B%2BRE3620cqHt%2BgwZJPjBhEJWthiH%2BvEvoBWslaUalmxfKf8vY%2FA4AAP%2F%2F&RelayState=relayState&SigAlg=http%3A%2F%2Fwww.w3.org%2F2000%2F09%2Fxmldsig%23rsa-sha1&Signature=WqMc7vKRJVNXwNHJmTemdfw5OML2XkLntYw%2FzwKoLMfavV%2FYy6fBP0GeGYlJVMweZBvbpjwoe%2BgpRkUCHKDUgixCG7hPi41p6MpQC%2Fp7ExTW5plvlS97iVAOvaF5V1MjvQCgBNKYnKNnvwAuxK%2Bu3N4rZjwGM%2F4JGgjJ5pannFQ%3Dsaml-0.4.6/testdata/TestSPRealWorldAssertionSignedNotResponse_idp_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urn:oasis:names:tc:SAML:2.0:nameid-format:transientsaml-0.4.6/testdata/TestSPRealWorldAssertionSignedNotResponse_response000066400000000000000000000064141415467341100263620ustar00rootroot00000000000000https://idp.secureworks.com/SAML2Authentication success.https://idp.secureworks.com/SAML2BMN0lUblP0gYGcw2PCyhwFZzkxY=F/2aaOQ3J/S6ULUd+gAuIclVueHEC2UfmtO2eR2oYb/YXub9E22yZe7eQgj2wdhYOvacVXN28QJJJG+K3Njwvi6b7mqf+T8N1YwaJW1fYAm28ayg4dEOTjHnjbRMZ6L+3cZPmPcFyE+edhCHEMnTLSqSvBnSyc1cwGdO9PmfWmt6PzUwf2nr2P5577Yc1FEQ9OtTx7ugWN3iPmjtLeTcpZfIDQX9+gSsh0KT+t61uWaYz+PJhtKnZQFeyr3uIxBTxv4wQ90FnmE4PiDvMksin5CDMfiMwd7pn7rNbk4EVHiDgSMkY6P4h8eWQwiqglOrQSZZr4BJgCoUbcNfZCq/7A==zZlTNJ+QcTp2yGH1ECXO3ry4GHhcs1CW3I6GPiPvtO+P6lyWxYdQd2RK/Hk9Kap6qpm/qom0rTwb FU2I67Y2JdQ3T5QBJjGHbGHU1uMxVWkhJluoa0Lpm381zNCJTZp8PetoB8dnIGua9y1aL75v04CG TzJ14I9/sW+apTkWj7xVQXutvVKETdn4kAy+L33HpriZjQNlcuAbqQj6OWsN4tGkLvNFZT40jQzp /8/tOQE6n2+zn3I8hUePwjPQROUmCeK86CkF0yVCPQ/vOTsC00Uaeu/SPOUu5ot+/75NPyE8w5Ry DgefdDXhYNmeuQtwGtcu/FI66atQMNTDoChXJQ==AQABrkinder@secureworks.comhttps://preview.docrocket-ross.test.octolabs.io/saml/metadataurn:oasis:names:tc:SAML:2.0:ac:classes:unspecified saml-0.4.6/testdata/TestSPRealWorldKeyInfoHasRSAPublicKeyNotX509Cert_idp_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urn:oasis:names:tc:SAML:2.0:nameid-format:transientsaml-0.4.6/testdata/TestSPRealWorldKeyInfoHasRSAPublicKeyNotX509Cert_response000066400000000000000000000113701415467341100271210ustar00rootroot00000000000000https://idp.secureworks.com/SAML2/6iPSzUnncXDbwrXiqZZVSaHt/Q=hpJLvXp7DN5qhYkR0+TfvzAHDTIEmOnjA7QGKxbuqUcLxL+xpLqEiPiyCT3DZ5r4eoUlGSTS4tZ2c/A3wnvzEy+f0Pf5D2dUWCL5RfVp7Q6cndEpqlXjZ3lhymTA+go/SdY9VQFKOBsS6ElT56Pr/QRtqqRP2JQK6pP96voeYqWT0YKCdrBkYZ6fJGQ32AD+mQ62hiMzOu9PvriNJzw2no7xyK1U0+MBNPzCcJ6yOrGqX8/yVB8d1hL9IjstZRbMaszdJnnGGMN/JoOtcFxg6v+a5EFC63uXAUL/inxvdNreZMGnuPJJ7HnuDe8yY089Xzwisy6dts6YJ/doEPFOJQ==zZlTNJ+QcTp2yGH1ECXO3ry4GHhcs1CW3I6GPiPvtO+P6lyWxYdQd2RK/Hk9Kap6qpm/qom0rTwb FU2I67Y2JdQ3T5QBJjGHbGHU1uMxVWkhJluoa0Lpm381zNCJTZp8PetoB8dnIGua9y1aL75v04CG TzJ14I9/sW+apTkWj7xVQXutvVKETdn4kAy+L33HpriZjQNlcuAbqQj6OWsN4tGkLvNFZT40jQzp /8/tOQE6n2+zn3I8hUePwjPQROUmCeK86CkF0yVCPQ/vOTsC00Uaeu/SPOUu5ot+/75NPyE8w5Ry DgefdDXhYNmeuQtwGtcu/FI66atQMNTDoChXJQ==AQABAuthentication success.https://idp.secureworks.com/SAML2BMN0lUblP0gYGcw2PCyhwFZzkxY=F/2aaOQ3J/S6ULUd+gAuIclVueHEC2UfmtO2eR2oYb/YXub9E22yZe7eQgj2wdhYOvacVXN28QJJJG+K3Njwvi6b7mqf+T8N1YwaJW1fYAm28ayg4dEOTjHnjbRMZ6L+3cZPmPcFyE+edhCHEMnTLSqSvBnSyc1cwGdO9PmfWmt6PzUwf2nr2P5577Yc1FEQ9OtTx7ugWN3iPmjtLeTcpZfIDQX9+gSsh0KT+t61uWaYz+PJhtKnZQFeyr3uIxBTxv4wQ90FnmE4PiDvMksin5CDMfiMwd7pn7rNbk4EVHiDgSMkY6P4h8eWQwiqglOrQSZZr4BJgCoUbcNfZCq/7A==zZlTNJ+QcTp2yGH1ECXO3ry4GHhcs1CW3I6GPiPvtO+P6lyWxYdQd2RK/Hk9Kap6qpm/qom0rTwb FU2I67Y2JdQ3T5QBJjGHbGHU1uMxVWkhJluoa0Lpm381zNCJTZp8PetoB8dnIGua9y1aL75v04CG TzJ14I9/sW+apTkWj7xVQXutvVKETdn4kAy+L33HpriZjQNlcuAbqQj6OWsN4tGkLvNFZT40jQzp /8/tOQE6n2+zn3I8hUePwjPQROUmCeK86CkF0yVCPQ/vOTsC00Uaeu/SPOUu5ot+/75NPyE8w5Ry DgefdDXhYNmeuQtwGtcu/FI66atQMNTDoChXJQ==AQABrkinder@secureworks.comhttps://preview.docrocket-ross.test.octolabs.io/saml/metadataurn:oasis:names:tc:SAML:2.0:ac:classes:unspecified saml-0.4.6/testdata/TestSPRejectsInjectedComment_IDPMetadata000066400000000000000000000042771415467341100240610ustar00rootroot00000000000000 MIIDdDCCAlygAwIBAgIGAVISlIlYMA0GCSqGSIb3DQEBCwUAMHsxFDASBgNVBAoTC0dvb2dsZSBJ bmMuMRYwFAYDVQQHEw1Nb3VudGFpbiBWaWV3MQ8wDQYDVQQDEwZHb29nbGUxGDAWBgNVBAsTD0dv b2dsZSBGb3IgV29yazELMAkGA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWEwHhcNMTYwMTA1 MTYxNzQ5WhcNMjEwMTAzMTYxNzQ5WjB7MRQwEgYDVQQKEwtHb29nbGUgSW5jLjEWMBQGA1UEBxMN TW91bnRhaW4gVmlldzEPMA0GA1UEAxMGR29vZ2xlMRgwFgYDVQQLEw9Hb29nbGUgRm9yIFdvcmsx CzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A MIIBCgKCAQEAmUfMUPxHSY/ZYZ88fUGAlhUP4Ni7zj54vsrsPDA4UhQiReEDRunN1q3OHsShRong gd4LvA83/e/3pm/V60R6vyMfj3Z/IGWY+eZ97EJUvjktt+VRoAi26oeY9ZW6S85yapvA3iuhEwIQ OcuPm1OqRQ0yQ4sUD+WtL/QSmlYvDP5TK1d6whTisNsKSqeFZCb/s9OX01UexW1BuDOLeVt0rCW1 kRNcBBLDmd4hnDP0SVq7nLhNFYXj2Ea6WsyRAIvchaUGy+Ima2okXm95Ye9kn8e118i/5rReyKCm BlskMkNaA4KWKvIQm3DdjgONgEd0IvKExyLwY7a5/JIUvBhb9QIDAQABMA0GCSqGSIb3DQEBCwUA A4IBAQAUDLMnHpzfp4ShdBqCreW48f8rU94q2qMwrU+W6DkOrGJTASVGS9Rib/MKAiRYOmqlaqEY NP57pCrE/nRB5FVdE+AlSx/fR3khsQ3zf/4dYs21SvGf+Oas99XEbWfV0OmPMYm3IrSCOBEV31wh 41qRc5QLnR+XutNPbSBN+tn+giRCLGCBLe81oVw4fRGQbgkd87rfLOy3G630I6s/J5feFFUT8d7h 9mpOeOqLCPrKpq+wI3aD3lf4mXqKIDNiHHRoNl67ANPu/N3fNU1HplVtvroVpiNp87frgdlKTEcg PUkfbaYHQGP6IS0lzeCeDX0wab3qRoh7/jJt5/BR8Iwf urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress saml-0.4.6/testdata/TestSPRejectsInjectedComment_response000066400000000000000000000143341415467341100236350ustar00rootroot00000000000000PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+PHNhbWwycDpSZXNwb25zZSB4bWxuczpzYW1sMnA9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpwcm90b2NvbCIgRGVzdGluYXRpb249Imh0dHBzOi8vMjllZTZkMmUubmdyb2suaW8vc2FtbC9hY3MiIElEPSJfZmMxNDFkYjI4NGViMzA5ODYwNTM1MWJkZTRkOWJlNTkiIEluUmVzcG9uc2VUbz0iaWQtZmQ0MTlhNWFiMDQ3MjY0NTQyN2Y4ZTA3ZDg3YTNhNWRkMGIyZTlhNiIgSXNzdWVJbnN0YW50PSIyMDE2LTAxLTA1VDE2OjU1OjM5LjM0OFoiIFZlcnNpb249IjIuMCI+PHNhbWwyOklzc3VlciB4bWxuczpzYW1sMj0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmFzc2VydGlvbiI+aHR0cHM6Ly9hY2NvdW50cy5nb29nbGUuY29tL28vc2FtbDI/aWRwaWQ9QzAyZGZsMXIxPC9zYW1sMjpJc3N1ZXI+PGRzOlNpZ25hdHVyZSB4bWxuczpkcz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC8wOS94bWxkc2lnIyI+PGRzOlNpZ25lZEluZm8+PGRzOkNhbm9uaWNhbGl6YXRpb25NZXRob2QgQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzEwL3htbC1leGMtYzE0biMiLz48ZHM6U2lnbmF0dXJlTWV0aG9kIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS8wNC94bWxkc2lnLW1vcmUjcnNhLXNoYTI1NiIvPjxkczpSZWZlcmVuY2UgVVJJPSIjX2ZjMTQxZGIyODRlYjMwOTg2MDUzNTFiZGU0ZDliZTU5Ij48ZHM6VHJhbnNmb3Jtcz48ZHM6VHJhbnNmb3JtIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMC8wOS94bWxkc2lnI2VudmVsb3BlZC1zaWduYXR1cmUiLz48ZHM6VHJhbnNmb3JtIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS8xMC94bWwtZXhjLWMxNG4jIi8+PC9kczpUcmFuc2Zvcm1zPjxkczpEaWdlc3RNZXRob2QgQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzA0L3htbGVuYyNzaGEyNTYiLz48ZHM6RGlnZXN0VmFsdWU+bHRNRUJLRzRZNVNLeERScUxHR2xFSGtPd3hla3dQOStybnA2WEtqdkJxVT08L2RzOkRpZ2VzdFZhbHVlPjwvZHM6UmVmZXJlbmNlPjwvZHM6U2lnbmVkSW5mbz48ZHM6U2lnbmF0dXJlVmFsdWU+SFBVV0pmYTlqdVdiKy9wZ0YrQklsc2pycE40NkE0RUNiT3hNdXhmWEFRUCtrMU5KMG9EdTJKYk1pZHpmclJBRkRHMjZaNjZWQWtkcwpBRmYwVFgzMWxvVjdaU0tGS0lVY0tuaFlXTHFuUTZLbmRydnJLbzF5UUhzUkdUNzJoVjl3SWdqTFRTZm5FV3QvOEMxaERQQi96R0txClhXZ3VvNFFHYlZUeVBoVVh3eEFzRmxBNjFDdkE5Q1pzU2xpeHBaY2pOVjUyQmMydzI5RUNRNStBcHZGWjVqRU1EN1JiQTVpMzdBbmgKUVBCeVYrZXo4ZU9Yc0hvQlhsR0drTjlDR201MFR6djZ3TW12WkdkT2pKWlhvRWZGUTA4UFJwbE9DQWpxSjM3QnhpWitLZWtUaE1KYgorelowcG1yeWR2V3lONEMzNWcycGVueGw2QUtxYnhMaXlJUkVaZz09PC9kczpTaWduYXR1cmVWYWx1ZT48ZHM6S2V5SW5mbz48ZHM6WDUwOURhdGE+PGRzOlg1MDlTdWJqZWN0TmFtZT5TVD1DYWxpZm9ybmlhLEM9VVMsT1U9R29vZ2xlIEZvciBXb3JrLENOPUdvb2dsZSxMPU1vdW50YWluIFZpZXcsTz1Hb29nbGUgSW5jLjwvZHM6WDUwOVN1YmplY3ROYW1lPjxkczpYNTA5Q2VydGlmaWNhdGU+TUlJRGREQ0NBbHlnQXdJQkFnSUdBVklTbElsWU1BMEdDU3FHU0liM0RRRUJDd1VBTUhzeEZEQVNCZ05WQkFvVEMwZHZiMmRzWlNCSgpibU11TVJZd0ZBWURWUVFIRXcxTmIzVnVkR0ZwYmlCV2FXVjNNUTh3RFFZRFZRUURFd1pIYjI5bmJHVXhHREFXQmdOVkJBc1REMGR2CmIyZHNaU0JHYjNJZ1YyOXlhekVMTUFrR0ExVUVCaE1DVlZNeEV6QVJCZ05WQkFnVENrTmhiR2xtYjNKdWFXRXdIaGNOTVRZd01UQTEKTVRZeE56UTVXaGNOTWpFd01UQXpNVFl4TnpRNVdqQjdNUlF3RWdZRFZRUUtFd3RIYjI5bmJHVWdTVzVqTGpFV01CUUdBMVVFQnhNTgpUVzkxYm5SaGFXNGdWbWxsZHpFUE1BMEdBMVVFQXhNR1IyOXZaMnhsTVJnd0ZnWURWUVFMRXc5SGIyOW5iR1VnUm05eUlGZHZjbXN4CkN6QUpCZ05WQkFZVEFsVlRNUk13RVFZRFZRUUlFd3BEWVd4cFptOXlibWxoTUlJQklqQU5CZ2txaGtpRzl3MEJBUUVGQUFPQ0FROEEKTUlJQkNnS0NBUUVBbVVmTVVQeEhTWS9aWVo4OGZVR0FsaFVQNE5pN3pqNTR2c3JzUERBNFVoUWlSZUVEUnVuTjFxM09Ic1NoUm9uZwpnZDRMdkE4My9lLzNwbS9WNjBSNnZ5TWZqM1ovSUdXWStlWjk3RUpVdmprdHQrVlJvQWkyNm9lWTlaVzZTODV5YXB2QTNpdWhFd0lRCk9jdVBtMU9xUlEweVE0c1VEK1d0TC9RU21sWXZEUDVUSzFkNndoVGlzTnNLU3FlRlpDYi9zOU9YMDFVZXhXMUJ1RE9MZVZ0MHJDVzEKa1JOY0JCTERtZDRobkRQMFNWcTduTGhORllYajJFYTZXc3lSQUl2Y2hhVUd5K0ltYTJva1htOTVZZTlrbjhlMTE4aS81clJleUtDbQpCbHNrTWtOYUE0S1dLdklRbTNEZGpnT05nRWQwSXZLRXh5THdZN2E1L0pJVXZCaGI5UUlEQVFBQk1BMEdDU3FHU0liM0RRRUJDd1VBCkE0SUJBUUFVRExNbkhwemZwNFNoZEJxQ3JlVzQ4ZjhyVTk0cTJxTXdyVStXNkRrT3JHSlRBU1ZHUzlSaWIvTUtBaVJZT21xbGFxRVkKTlA1N3BDckUvblJCNUZWZEUrQWxTeC9mUjNraHNRM3pmLzRkWXMyMVN2R2YrT2FzOTlYRWJXZlYwT21QTVltM0lyU0NPQkVWMzF3aAo0MXFSYzVRTG5SK1h1dE5QYlNCTit0bitnaVJDTEdDQkxlODFvVnc0ZlJHUWJna2Q4N3JmTE95M0c2MzBJNnMvSjVmZUZGVVQ4ZDdoCjltcE9lT3FMQ1ByS3BxK3dJM2FEM2xmNG1YcUtJRE5pSEhSb05sNjdBTlB1L04zZk5VMUhwbFZ0dnJvVnBpTnA4N2ZyZ2RsS1RFY2cKUFVrZmJhWUhRR1A2SVMwbHplQ2VEWDB3YWIzcVJvaDcvakp0NS9CUjhJd2Y8L2RzOlg1MDlDZXJ0aWZpY2F0ZT48L2RzOlg1MDlEYXRhPjwvZHM6S2V5SW5mbz48L2RzOlNpZ25hdHVyZT48c2FtbDJwOlN0YXR1cz48c2FtbDJwOlN0YXR1c0NvZGUgVmFsdWU9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpzdGF0dXM6U3VjY2VzcyIvPjwvc2FtbDJwOlN0YXR1cz48c2FtbDI6QXNzZXJ0aW9uIHhtbG5zOnNhbWwyPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YXNzZXJ0aW9uIiBJRD0iXzllNzY0OTUyZTZhMjYxZTE5NDA5YTM4MjU1ODEwMzNkIiBJc3N1ZUluc3RhbnQ9IjIwMTYtMDEtMDVUMTY6NTU6MzkuMzQ4WiIgVmVyc2lvbj0iMi4wIj48c2FtbDI6SXNzdWVyPmh0dHBzOi8vYWNjb3VudHMuZ29vZ2xlLmNvbS9vL3NhbWwyP2lkcGlkPUMwMmRmbDFyMTwvc2FtbDI6SXNzdWVyPjxzYW1sMjpTdWJqZWN0PjxzYW1sMjpOYW1lSUQ+cm9zc0BvY3RvbGFicy5pbzwvc2FtbDI6TmFtZUlEPjxzYW1sMjpTdWJqZWN0Q29uZmlybWF0aW9uIE1ldGhvZD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmNtOmJlYXJlciI+PHNhbWwyOlN1YmplY3RDb25maXJtYXRpb25EYXRhIEluUmVzcG9uc2VUbz0iaWQtZmQ0MTlhNWFiMDQ3MjY0NTQyN2Y4ZTA3ZDg3YTNhNWRkMGIyZTlhNiIgTm90T25PckFmdGVyPSIyMDE2LTAxLTA1VDE3OjAwOjM5LjM0OFoiIFJlY2lwaWVudD0iaHR0cHM6Ly8yOWVlNmQyZS5uZ3Jvay5pby9zYW1sL2FjcyIvPjwvc2FtbDI6U3ViamVjdENvbmZpcm1hdGlvbj48L3NhbWwyOlN1YmplY3Q+PHNhbWwyOkNvbmRpdGlvbnMgTm90QmVmb3JlPSIyMDE2LTAxLTA1VDE2OjUwOjM5LjM0OFoiIE5vdE9uT3JBZnRlcj0iMjAxNi0wMS0wNVQxNzowMDozOS4zNDhaIj48c2FtbDI6QXVkaWVuY2VSZXN0cmljdGlvbj48c2FtbDI6QXVkaWVuY2U+aHR0cHM6Ly8yOWVlNmQyZS5uZ3Jvay5pby9zYW1sL21ldGFkYXRhPC9zYW1sMjpBdWRpZW5jZT48L3NhbWwyOkF1ZGllbmNlUmVzdHJpY3Rpb24+PC9zYW1sMjpDb25kaXRpb25zPjxzYW1sMjpBdHRyaWJ1dGVTdGF0ZW1lbnQ+PHNhbWwyOkF0dHJpYnV0ZSBOYW1lPSJwaG9uZSIvPjxzYW1sMjpBdHRyaWJ1dGUgTmFtZT0iYWRkcmVzcyIvPjxzYW1sMjpBdHRyaWJ1dGUgTmFtZT0iam9iVGl0bGUiLz48c2FtbDI6QXR0cmlidXRlIE5hbWU9ImZpcnN0TmFtZSI+PHNhbWwyOkF0dHJpYnV0ZVZhbHVlIHhtbG5zOnhzPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxL1hNTFNjaGVtYSIgeG1sbnM6eHNpPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxL1hNTFNjaGVtYS1pbnN0YW5jZSIgeHNpOnR5cGU9InhzOmFueVR5cGUiPlJvc3M8L3NhbWwyOkF0dHJpYnV0ZVZhbHVlPjwvc2FtbDI6QXR0cmlidXRlPjxzYW1sMjpBdHRyaWJ1dGUgTmFtZT0ibGFzdE5hbWUiPjxzYW1sMjpBdHRyaWJ1dGVWYWx1ZSB4bWxuczp4cz0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEiIHhtbG5zOnhzaT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEtaW5zdGFuY2UiIHhzaTp0eXBlPSJ4czphbnlUeXBlIj5LaW5kZXI8L3NhbWwyOkF0dHJpYnV0ZVZhbHVlPjwvc2FtbDI6QXR0cmlidXRlPjwvc2FtbDI6QXR0cmlidXRlU3RhdGVtZW50PjxzYW1sMjpBdXRoblN0YXRlbWVudCBBdXRobkluc3RhbnQ9IjIwMTYtMDEtMDVUMTY6NTU6MzguMDAwWiIgU2Vzc2lvbkluZGV4PSJfOWU3NjQ5NTJlNmEyNjFlMTk0MDlhMzgyNTU4MTAzM2QiPjxzYW1sMjpBdXRobkNvbnRleHQ+PHNhbWwyOkF1dGhuQ29udGV4dENsYXNzUmVmPnVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphYzpjbGFzc2VzOnVuc3BlY2lmaWVkPC9zYW1sMjpBdXRobkNvbnRleHRDbGFzc1JlZj48L3NhbWwyOkF1dGhuQ29udGV4dD48L3NhbWwyOkF1dGhuU3RhdGVtZW50Pjwvc2FtbDI6QXNzZXJ0aW9uPjwvc2FtbDJwOlJlc3BvbnNlPg==saml-0.4.6/testdata/TestSPRejectsMalformedResponse_IDPMetadata000066400000000000000000000042771415467341100244360ustar00rootroot00000000000000 MIIDdDCCAlygAwIBAgIGAVISlIlYMA0GCSqGSIb3DQEBCwUAMHsxFDASBgNVBAoTC0dvb2dsZSBJ bmMuMRYwFAYDVQQHEw1Nb3VudGFpbiBWaWV3MQ8wDQYDVQQDEwZHb29nbGUxGDAWBgNVBAsTD0dv b2dsZSBGb3IgV29yazELMAkGA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWEwHhcNMTYwMTA1 MTYxNzQ5WhcNMjEwMTAzMTYxNzQ5WjB7MRQwEgYDVQQKEwtHb29nbGUgSW5jLjEWMBQGA1UEBxMN TW91bnRhaW4gVmlldzEPMA0GA1UEAxMGR29vZ2xlMRgwFgYDVQQLEw9Hb29nbGUgRm9yIFdvcmsx CzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A MIIBCgKCAQEAmUfMUPxHSY/ZYZ88fUGAlhUP4Ni7zj54vsrsPDA4UhQiReEDRunN1q3OHsShRong gd4LvA83/e/3pm/V60R6vyMfj3Z/IGWY+eZ97EJUvjktt+VRoAi26oeY9ZW6S85yapvA3iuhEwIQ OcuPm1OqRQ0yQ4sUD+WtL/QSmlYvDP5TK1d6whTisNsKSqeFZCb/s9OX01UexW1BuDOLeVt0rCW1 kRNcBBLDmd4hnDP0SVq7nLhNFYXj2Ea6WsyRAIvchaUGy+Ima2okXm95Ye9kn8e118i/5rReyKCm BlskMkNaA4KWKvIQm3DdjgONgEd0IvKExyLwY7a5/JIUvBhb9QIDAQABMA0GCSqGSIb3DQEBCwUA A4IBAQAUDLMnHpzfp4ShdBqCreW48f8rU94q2qMwrU+W6DkOrGJTASVGS9Rib/MKAiRYOmqlaqEY NP57pCrE/nRB5FVdE+AlSx/fR3khsQ3zf/4dYs21SvGf+Oas99XEbWfV0OmPMYm3IrSCOBEV31wh 41qRc5QLnR+XutNPbSBN+tn+giRCLGCBLe81oVw4fRGQbgkd87rfLOy3G630I6s/J5feFFUT8d7h 9mpOeOqLCPrKpq+wI3aD3lf4mXqKIDNiHHRoNl67ANPu/N3fNU1HplVtvroVpiNp87frgdlKTEcg PUkfbaYHQGP6IS0lzeCeDX0wab3qRoh7/jJt5/BR8Iwf urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress saml-0.4.6/testdata/TestSPRejectsMalformedResponse_response000066400000000000000000000143341415467341100242120ustar00rootroot00000000000000PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+PHNhbWwycDpSZXNwb25zZSB4bWxuczpzYW1sMnA9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpwcm90b2NvbCIgRGVzdGluYXRpb249Imh0dHBzOi8vMjllZTZkMmUubmdyb2suaW8vc2FtbC9hY3MiIElEPSJfZmMxNDFkYjI4NGViMzA5ODYwNTM1MWJkZTRkOWJlNTkiIEluUmVzcG9uc2VUbz0iaWQtZmQ0MTlhNWFiMDQ3MjY0NTQyN2Y4ZTA3ZDg3YTNhNWRkMGIyZTlhNiIgSXNzdWVJbnN0YW50PSIyMDE2LTAxLTA1VDE2OjU1OjM5LjM0OFoiIFZlcnNpb249IjIuMCI+PHNhbWwyOklzc3VlciB4bWxuczpzYW1sMj0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmFzc2VydGlvbiI+aHR0cHM6Ly9hY2NvdW50cy5nb29nbGUuY29tL28vc2FtbDI/aWRwaWQ9QzAyZGZsMXIxPC9zYW1sMjpJc3N1ZXI+PGRzOlNpZ25hdHVyZSB4bWxuczpkcz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC8wOS94bWxkc2lnIyI+PGRzOlNpZ25lZEluZm8+PGRzOkNhbm9uaWNhbGl6YXRpb25NZXRob2QgQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzEwL3htbC1leGMtYzE0biMiLz48ZHM6U2lnbmF0dXJlTWV0aG9kIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS8wNC94bWxkc2lnLW1vcmUjcnNhLXNoYTI1NiIvPjxkczpSZWZlcmVuY2UgVVJJPSIjX2ZjMTQxZGIyODRlYjMwOTg2MDUzNTFiZGU0ZDliZTU5Ij48ZHM6VHJhbnNmb3Jtcz48ZHM6VHJhbnNmb3JtIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMC8wOS94bWxkc2lnI2VudmVsb3BlZC1zaWduYXR1cmUiLz48ZHM6VHJhbnNmb3JtIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS8xMC94bWwtZXhjLWMxNG4jIi8+PC9kczpUcmFuc2Zvcm1zPjxkczpEaWdlc3RNZXRob2QgQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzA0L3htbGVuYyNzaGEyNTYiLz48ZHM6RGlnZXN0VmFsdWU+bHRNRUJLRzRZNVNLeERScUxHR2xFSGtPd3hla3dQOStybnA2WEtqdkJxVT08L2RzOkRpZ2VzdFZhbHVlPjwvZHM6UmVmZXJlbmNlPjwvZHM6U2lnbmVkSW5mbz48ZHM6U2lnbmF0dXJlVmFsdWU+SFBVV0pmYTlqdVdiKy9wZ0YrQklsc2pycE40NkE0RUNiT3hNdXhmWEFRUCtrMU5KMG9EdTJKYk1pZHpmclJBRkRHMjZaNjZWQWtkcwpBRmYwVFgzMWxvVjdaU0tGS0lVY0tuaFlXTHFuUTZLbmRydnJLbzF5UUhzUkdUNzJoVjl3SWdqTFRTZm5FV3QvOEMxaERQQi96R0txClhXZ3VvNFFHYlZUeVBoVVh3eEFzRmxBNjFDdkE5Q1pzU2xpeHBaY2pOVjUyQmMydzI5RUNRNStBcHZGWjVqRU1EN1JiQTVpMzdBbmgKUVBCeVYrZXo4ZU9Yc0hvQlhsR0drTjlDR201MFR6djZ3TW12WkdkT2pKWlhvRWZGUTA4UFJwbE9DQWpxSjM3QnhpWitLZWtUaE1KYgorelowcG1yeWR2V3lONEMzNWcycGVueGw2QUtxYnhMaXlJUkVaZz09PC9kczpTaWduYXR1cmVWYWx1ZT48ZHM6S2V5SW5mbz48ZHM6WDUwOURhdGE+PGRzOlg1MDlTdWJqZWN0TmFtZT5TVD1DYWxpZm9ybmlhLEM9VVMsT1U9R29vZ2xlIEZvciBXb3JrLENOPUdvb2dsZSxMPU1vdW50YWluIFZpZXcsTz1Hb29nbGUgSW5jLjwvZHM6WDUwOVN1YmplY3ROYW1lPjxkczpYNTA5Q2VydGlmaWNhdGU+TUlJRGREQ0NBbHlnQXdJQkFnSUdBVklTbElsWU1BMEdDU3FHU0liM0RRRUJDd1VBTUhzeEZEQVNCZ05WQkFvVEMwZHZiMmRzWlNCSgpibU11TVJZd0ZBWURWUVFIRXcxTmIzVnVkR0ZwYmlCV2FXVjNNUTh3RFFZRFZRUURFd1pIYjI5bmJHVXhHREFXQmdOVkJBc1REMGR2CmIyZHNaU0JHYjNJZ1YyOXlhekVMTUFrR0ExVUVCaE1DVlZNeEV6QVJCZ05WQkFnVENrTmhiR2xtYjNKdWFXRXdIaGNOTVRZd01UQTEKTVRZeE56UTVXaGNOTWpFd01UQXpNVFl4TnpRNVdqQjdNUlF3RWdZRFZRUUtFd3RIYjI5bmJHVWdTVzVqTGpFV01CUUdBMVVFQnhNTgpUVzkxYm5SaGFXNGdWbWxsZHpFUE1BMEdBMVVFQXhNR1IyOXZaMnhsTVJnd0ZnWURWUVFMRXc5SGIyOW5iR1VnUm05eUlGZHZjbXN4CkN6QUpCZ05WQkFZVEFsVlRNUk13RVFZRFZRUUlFd3BEWVd4cFptOXlibWxoTUlJQklqQU5CZ2txaGtpRzl3MEJBUUVGQUFPQ0FROEEKTUlJQkNnS0NBUUVBbVVmTVVQeEhTWS9aWVo4OGZVR0FsaFVQNE5pN3pqNTR2c3JzUERBNFVoUWlSZUVEUnVuTjFxM09Ic1NoUm9uZwpnZDRMdkE4My9lLzNwbS9WNjBSNnZ5TWZqM1ovSUdXWStlWjk3RUpVdmprdHQrVlJvQWkyNm9lWTlaVzZTODV5YXB2QTNpdWhFd0lRCk9jdVBtMU9xUlEweVE0c1VEK1d0TC9RU21sWXZEUDVUSzFkNndoVGlzTnNLU3FlRlpDYi9zOU9YMDFVZXhXMUJ1RE9MZVZ0MHJDVzEKa1JOY0JCTERtZDRobkRQMFNWcTduTGhORllYajJFYTZXc3lSQUl2Y2hhVUd5K0ltYTJva1htOTVZZTlrbjhlMTE4aS81clJleUtDbQpCbHNrTWtOYUE0S1dLdklRbTNEZGpnT05nRWQwSXZLRXh5THdZN2E1L0pJVXZCaGI5UUlEQVFBQk1BMEdDU3FHU0liM0RRRUJDd1VBCkE0SUJBUUFVRExNbkhwemZwNFNoZEJxQ3JlVzQ4ZjhyVTk0cTJxTXdyVStXNkRrT3JHSlRBU1ZHUzlSaWIvTUtBaVJZT21xbGFxRVkKTlA1N3BDckUvblJCNUZWZEUrQWxTeC9mUjNraHNRM3pmLzRkWXMyMVN2R2YrT2FzOTlYRWJXZlYwT21QTVltM0lyU0NPQkVWMzF3aAo0MXFSYzVRTG5SK1h1dE5QYlNCTit0bitnaVJDTEdDQkxlODFvVnc0ZlJHUWJna2Q4N3JmTE95M0c2MzBJNnMvSjVmZUZGVVQ4ZDdoCjltcE9lT3FMQ1ByS3BxK3dJM2FEM2xmNG1YcUtJRE5pSEhSb05sNjdBTlB1L04zZk5VMUhwbFZ0dnJvVnBpTnA4N2ZyZ2RsS1RFY2cKUFVrZmJhWUhRR1A2SVMwbHplQ2VEWDB3YWIzcVJvaDcvakp0NS9CUjhJd2Y8L2RzOlg1MDlDZXJ0aWZpY2F0ZT48L2RzOlg1MDlEYXRhPjwvZHM6S2V5SW5mbz48L2RzOlNpZ25hdHVyZT48c2FtbDJwOlN0YXR1cz48c2FtbDJwOlN0YXR1c0NvZGUgVmFsdWU9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpzdGF0dXM6U3VjY2VzcyIvPjwvc2FtbDJwOlN0YXR1cz48c2FtbDI6QXNzZXJ0aW9uIHhtbG5zOnNhbWwyPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YXNzZXJ0aW9uIiBJRD0iXzllNzY0OTUyZTZhMjYxZTE5NDA5YTM4MjU1ODEwMzNkIiBJc3N1ZUluc3RhbnQ9IjIwMTYtMDEtMDVUMTY6NTU6MzkuMzQ4WiIgVmVyc2lvbj0iMi4wIj48c2FtbDI6SXNzdWVyPmh0dHBzOi8vYWNjb3VudHMuZ29vZ2xlLmNvbS9vL3NhbWwyP2lkcGlkPUMwMmRmbDFyMTwvc2FtbDI6SXNzdWVyPjxzYW1sMjpTdWJqZWN0PjxzYW1sMjpOYW1lSUQ+cm9zc0BvY3RvbGFicy5pbzwvc2FtbDI6TmFtZUlEPjxzYW1sMjpTdWJqZWN0Q29uZmlybWF0aW9uIE1ldGhvZD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmNtOmJlYXJlciI+PHNhbWwyOlN1YmplY3RDb25maXJtYXRpb25EYXRhIEluUmVzcG9uc2VUbz0iaWQtZmQ0MTlhNWFiMDQ3MjY0NTQyN2Y4ZTA3ZDg3YTNhNWRkMGIyZTlhNiIgTm90T25PckFmdGVyPSIyMDE2LTAxLTA1VDE3OjAwOjM5LjM0OFoiIFJlY2lwaWVudD0iaHR0cHM6Ly8yOWVlNmQyZS5uZ3Jvay5pby9zYW1sL2FjcyIvPjwvc2FtbDI6U3ViamVjdENvbmZpcm1hdGlvbj48L3NhbWwyOlN1YmplY3Q+PHNhbWwyOkNvbmRpdGlvbnMgTm90QmVmb3JlPSIyMDE2LTAxLTA1VDE2OjUwOjM5LjM0OFoiIE5vdE9uT3JBZnRlcj0iMjAxNi0wMS0wNVQxNzowMDozOS4zNDhaIj48c2FtbDI6QXVkaWVuY2VSZXN0cmljdGlvbj48c2FtbDI6QXVkaWVuY2U+aHR0cHM6Ly8yOWVlNmQyZS5uZ3Jvay5pby9zYW1sL21ldGFkYXRhPC9zYW1sMjpBdWRpZW5jZT48L3NhbWwyOkF1ZGllbmNlUmVzdHJpY3Rpb24+PC9zYW1sMjpDb25kaXRpb25zPjxzYW1sMjpBdHRyaWJ1dGVTdGF0ZW1lbnQ+PHNhbWwyOkF0dHJpYnV0ZSBOYW1lPSJwaG9uZSIvPjxzYW1sMjpBdHRyaWJ1dGUgTmFtZT0iYWRkcmVzcyIvPjxzYW1sMjpBdHRyaWJ1dGUgTmFtZT0iam9iVGl0bGUiLz48c2FtbDI6QXR0cmlidXRlIE5hbWU9ImZpcnN0TmFtZSI+PHNhbWwyOkF0dHJpYnV0ZVZhbHVlIHhtbG5zOnhzPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxL1hNTFNjaGVtYSIgeG1sbnM6eHNpPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxL1hNTFNjaGVtYS1pbnN0YW5jZSIgeHNpOnR5cGU9InhzOmFueVR5cGUiPlJvc3M8L3NhbWwyOkF0dHJpYnV0ZVZhbHVlPjwvc2FtbDI6QXR0cmlidXRlPjxzYW1sMjpBdHRyaWJ1dGUgTmFtZT0ibGFzdE5hbWUiPjxzYW1sMjpBdHRyaWJ1dGVWYWx1ZSB4bWxuczp4cz0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEiIHhtbG5zOnhzaT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEtaW5zdGFuY2UiIHhzaTp0eXBlPSJ4czphbnlUeXBlIj5LaW5kZXI8L3NhbWwyOkF0dHJpYnV0ZVZhbHVlPjwvc2FtbDI6QXR0cmlidXRlPjwvc2FtbDI6QXR0cmlidXRlU3RhdGVtZW50PjxzYW1sMjpBdXRoblN0YXRlbWVudCBBdXRobkluc3RhbnQ9IjIwMTYtMDEtMDVUMTY6NTU6MzguMDAwWiIgU2Vzc2lvbkluZGV4PSJfOWU3NjQ5NTJlNmEyNjFlMTk0MDlhMzgyNTU4MTAzM2QiPjxzYW1sMjpBdXRobkNvbnRleHQ+PHNhbWwyOkF1dGhuQ29udGV4dENsYXNzUmVmPnVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphYzpjbGFzc2VzOnVuc3BlY2lmaWVkPC9zYW1sMjpBdXRobkNvbnRleHRDbGFzc1JlZj48L3NhbWwyOkF1dGhuQ29udGV4dD48L3NhbWwyOkF1dGhuU3RhdGVtZW50Pjwvc2FtbDI6QXNzZXJ0aW9uPjwvc2FtbDJwOlJlc3BvbnNlPg==saml-0.4.6/testdata/TestSPResponseWithNoIssuer_response000066400000000000000000000307501415467341100233670ustar00rootroot00000000000000MIIB7zCCAVgCCQDFzbKIp7b3MTANBgkqhkiG9w0BAQUFADA8MQswCQYDVQQGEwJVUzELMAkGA1UE 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-0.4.6/testdata/TestServiceProviderCanHandleSignedAssertionsResponse_IDPMetadata000066400000000000000000000040141415467341100310100ustar00rootroot00000000000000 MIICajCCAdOgAwIBAgIBADANBgkqhkiG9w0BAQ0FADBSMQswCQYDVQQGEwJ1czETMBEGA1UECAwKQ2FsaWZvcm5pYTEVMBMGA1UECgwMT25lbG9naW4gSW5jMRcwFQYDVQQDDA5zcC5leGFtcGxlLmNvbTAeFw0xNDA3MTcxNDEyNTZaFw0xNTA3MTcxNDEyNTZaMFIxCzAJBgNVBAYTAnVzMRMwEQYDVQQIDApDYWxpZm9ybmlhMRUwEwYDVQQKDAxPbmVsb2dpbiBJbmMxFzAVBgNVBAMMDnNwLmV4YW1wbGUuY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDZx+ON4IUoIWxgukTb1tOiX3bMYzYQiwWPUNMp+Fq82xoNogso2bykZG0yiJm5o8zv/sd6pGouayMgkx/2FSOdc36T0jGbCHuRSbtia0PEzNIRtmViMrt3AeoWBidRXmZsxCNLwgIV6dn2WpuE5Az0bHgpZnQxTKFek0BMKU/d8wIDAQABo1AwTjAdBgNVHQ4EFgQUGHxYqZYyX7cTxKVODVgZwSTdCnwwHwYDVR0jBBgwFoAUGHxYqZYyX7cTxKVODVgZwSTdCnwwDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQ0FAAOBgQByFOl+hMFICbd3DJfnp2Rgd/dqttsZG/tyhILWvErbio/DEe98mXpowhTkC04ENprOyXi7ZbUqiicF89uAGyt1oqgTUCD1VsLahqIcmrzgumNyTwLGWo17WDAa1/usDhetWAMhgzF/Cnf5ek0nK00m0YZGyc4LzgD0CROMASTWNg== urn:oasis:names:tc:SAML:1.1:nameid-format:transient Support support@onelogin.com saml-0.4.6/testdata/TestServiceProviderCanHandleSignedAssertionsResponse_response000066400000000000000000000136741415467341100306050ustar00rootroot00000000000000PHNhbWxwOlJlc3BvbnNlIHhtbG5zOnNhbWxwPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6cHJvdG9jb2wiIHhtbG5zOnNhbWw9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphc3NlcnRpb24iIElEPSJfOGU4ZGM1ZjY5YTk4Y2M0YzFmZjM0MjdlNWNlMzQ2MDZmZDY3MmY5MWU2IiBWZXJzaW9uPSIyLjAiIElzc3VlSW5zdGFudD0iMjAxNC0wNy0xN1QwMTowMTo0OFoiIERlc3RpbmF0aW9uPSJodHRwOi8vc3AuZXhhbXBsZS5jb20vZGVtbzEvaW5kZXgucGhwP2FjcyIgSW5SZXNwb25zZVRvPSJPTkVMT0dJTl80ZmVlM2IwNDYzOTVjNGU3NTEwMTFlOTdmODkwMGI1MjczZDU2Njg1Ij4KICA8c2FtbDpJc3N1ZXI+aHR0cDovL2lkcC5leGFtcGxlLmNvbS9tZXRhZGF0YS5waHA8L3NhbWw6SXNzdWVyPgogIDxzYW1scDpTdGF0dXM+CiAgICA8c2FtbHA6U3RhdHVzQ29kZSBWYWx1ZT0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOnN0YXR1czpTdWNjZXNzIi8+CiAgPC9zYW1scDpTdGF0dXM+CiAgPHNhbWw6QXNzZXJ0aW9uIHhtbG5zOnhzaT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEtaW5zdGFuY2UiIHhtbG5zOnhzPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxL1hNTFNjaGVtYSIgSUQ9InBmeDA0NjkwMGM1LTA0MjMtMzVjYi0yYWRiLTcyMjgzYmE1ZDhjZCIgVmVyc2lvbj0iMi4wIiBJc3N1ZUluc3RhbnQ9IjIwMTQtMDctMTdUMDE6MDE6NDhaIj4KICAgIDxzYW1sOklzc3Vlcj5odHRwOi8vaWRwLmV4YW1wbGUuY29tL21ldGFkYXRhLnBocDwvc2FtbDpJc3N1ZXI+PGRzOlNpZ25hdHVyZSB4bWxuczpkcz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC8wOS94bWxkc2lnIyI+CiAgPGRzOlNpZ25lZEluZm8+PGRzOkNhbm9uaWNhbGl6YXRpb25NZXRob2QgQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzEwL3htbC1leGMtYzE0biMiLz4KICAgIDxkczpTaWduYXR1cmVNZXRob2QgQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwLzA5L3htbGRzaWcjcnNhLXNoYTEiLz4KICA8ZHM6UmVmZXJlbmNlIFVSST0iI3BmeDA0NjkwMGM1LTA0MjMtMzVjYi0yYWRiLTcyMjgzYmE1ZDhjZCI+PGRzOlRyYW5zZm9ybXM+PGRzOlRyYW5zZm9ybSBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvMDkveG1sZHNpZyNlbnZlbG9wZWQtc2lnbmF0dXJlIi8+PGRzOlRyYW5zZm9ybSBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvMTAveG1sLWV4Yy1jMTRuIyIvPjwvZHM6VHJhbnNmb3Jtcz48ZHM6RGlnZXN0TWV0aG9kIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMC8wOS94bWxkc2lnI3NoYTEiLz48ZHM6RGlnZXN0VmFsdWU+YmV5ZnFIOXMxUys2bDJHQkhiU2xXOFR4SzZFPTwvZHM6RGlnZXN0VmFsdWU+PC9kczpSZWZlcmVuY2U+PC9kczpTaWduZWRJbmZvPjxkczpTaWduYXR1cmVWYWx1ZT5DSkJMY0pVTm91Q0psY3d5YUtTb1RGdHJUYVJOUWJnWHJFUUdKTmZsdjJkakx0M3J0d2krRzZMd3VQZkQrckF5b3lIbXFyUXlTaVJaZ1lNeWN1bk8vNUQ2R2J5ZVhJVjNrc093Y0YrQXlWZGtrblVpcVN3SDcvOXJkdkVhZmtKcDQ3d1pYKzc4dlFGMDZNcjFnNEpsODByTmNEUncxeE9FdW9QN2pDMjVtMVE9PC9kczpTaWduYXR1cmVWYWx1ZT4KPGRzOktleUluZm8+PGRzOlg1MDlEYXRhPjxkczpYNTA5Q2VydGlmaWNhdGU+TUlJQ2FqQ0NBZE9nQXdJQkFnSUJBREFOQmdrcWhraUc5dzBCQVEwRkFEQlNNUXN3Q1FZRFZRUUdFd0oxY3pFVE1CRUdBMVVFQ0F3S1EyRnNhV1p2Y201cFlURVZNQk1HQTFVRUNnd01UMjVsYkc5bmFXNGdTVzVqTVJjd0ZRWURWUVFEREE1emNDNWxlR0Z0Y0d4bExtTnZiVEFlRncweE5EQTNNVGN4TkRFeU5UWmFGdzB4TlRBM01UY3hOREV5TlRaYU1GSXhDekFKQmdOVkJBWVRBblZ6TVJNd0VRWURWUVFJREFwRFlXeHBabTl5Ym1saE1SVXdFd1lEVlFRS0RBeFBibVZzYjJkcGJpQkpibU14RnpBVkJnTlZCQU1NRG5Od0xtVjRZVzF3YkdVdVkyOXRNSUdmTUEwR0NTcUdTSWIzRFFFQkFRVUFBNEdOQURDQmlRS0JnUURaeCtPTjRJVW9JV3hndWtUYjF0T2lYM2JNWXpZUWl3V1BVTk1wK0ZxODJ4b05vZ3NvMmJ5a1pHMHlpSm01bzh6di9zZDZwR291YXlNZ2t4LzJGU09kYzM2VDBqR2JDSHVSU2J0aWEwUEV6TklSdG1WaU1ydDNBZW9XQmlkUlhtWnN4Q05Md2dJVjZkbjJXcHVFNUF6MGJIZ3BablF4VEtGZWswQk1LVS9kOHdJREFRQUJvMUF3VGpBZEJnTlZIUTRFRmdRVUdIeFlxWll5WDdjVHhLVk9EVmdad1NUZENud3dId1lEVlIwakJCZ3dGb0FVR0h4WXFaWXlYN2NUeEtWT0RWZ1p3U1RkQ253d0RBWURWUjBUQkFVd0F3RUIvekFOQmdrcWhraUc5dzBCQVEwRkFBT0JnUUJ5Rk9sK2hNRklDYmQzREpmbnAyUmdkL2RxdHRzWkcvdHloSUxXdkVyYmlvL0RFZTk4bVhwb3doVGtDMDRFTnByT3lYaTdaYlVxaWljRjg5dUFHeXQxb3FnVFVDRDFWc0xhaHFJY21yemd1bU55VHdMR1dvMTdXREFhMS91c0RoZXRXQU1oZ3pGL0NuZjVlazBuSzAwbTBZWkd5YzRMemdEMENST01BU1RXTmc9PTwvZHM6WDUwOUNlcnRpZmljYXRlPjwvZHM6WDUwOURhdGE+PC9kczpLZXlJbmZvPjwvZHM6U2lnbmF0dXJlPgogICAgPHNhbWw6U3ViamVjdD4KICAgICAgPHNhbWw6TmFtZUlEIFNQTmFtZVF1YWxpZmllcj0iaHR0cDovL3NwLmV4YW1wbGUuY29tL2RlbW8xL21ldGFkYXRhLnBocCIgRm9ybWF0PSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6bmFtZWlkLWZvcm1hdDp0cmFuc2llbnQiPl9jZTNkMjk0OGI0Y2YyMDE0NmRlZTBhMGIzZGQ2ZjY5YjZjZjg2ZjYyZDc8L3NhbWw6TmFtZUlEPgogICAgICA8c2FtbDpTdWJqZWN0Q29uZmlybWF0aW9uIE1ldGhvZD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmNtOmJlYXJlciI+CiAgICAgICAgPHNhbWw6U3ViamVjdENvbmZpcm1hdGlvbkRhdGEgTm90T25PckFmdGVyPSIyMDI0LTAxLTE4VDA2OjIxOjQ4WiIgUmVjaXBpZW50PSJodHRwOi8vc3AuZXhhbXBsZS5jb20vZGVtbzEvaW5kZXgucGhwP2FjcyIgSW5SZXNwb25zZVRvPSJPTkVMT0dJTl80ZmVlM2IwNDYzOTVjNGU3NTEwMTFlOTdmODkwMGI1MjczZDU2Njg1Ii8+CiAgICAgIDwvc2FtbDpTdWJqZWN0Q29uZmlybWF0aW9uPgogICAgPC9zYW1sOlN1YmplY3Q+CiAgICA8c2FtbDpDb25kaXRpb25zIE5vdEJlZm9yZT0iMjAxNC0wNy0xN1QwMTowMToxOFoiIE5vdE9uT3JBZnRlcj0iMjAyNC0wMS0xOFQwNjoyMTo0OFoiPgogICAgICA8c2FtbDpBdWRpZW5jZVJlc3RyaWN0aW9uPgogICAgICAgIDxzYW1sOkF1ZGllbmNlPmh0dHA6Ly9zcC5leGFtcGxlLmNvbS9kZW1vMS9tZXRhZGF0YS5waHA8L3NhbWw6QXVkaWVuY2U+CiAgICAgIDwvc2FtbDpBdWRpZW5jZVJlc3RyaWN0aW9uPgogICAgPC9zYW1sOkNvbmRpdGlvbnM+CiAgICA8c2FtbDpBdXRoblN0YXRlbWVudCBBdXRobkluc3RhbnQ9IjIwMTQtMDctMTdUMDE6MDE6NDhaIiBTZXNzaW9uTm90T25PckFmdGVyPSIyMDI0LTA3LTE3VDA5OjAxOjQ4WiIgU2Vzc2lvbkluZGV4PSJfYmU5OTY3YWJkOTA0ZGRjYWUzYzBlYjQxODlhZGJlM2Y3MWUzMjdjZjkzIj4KICAgICAgPHNhbWw6QXV0aG5Db250ZXh0PgogICAgICAgIDxzYW1sOkF1dGhuQ29udGV4dENsYXNzUmVmPnVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphYzpjbGFzc2VzOlBhc3N3b3JkPC9zYW1sOkF1dGhuQ29udGV4dENsYXNzUmVmPgogICAgICA8L3NhbWw6QXV0aG5Db250ZXh0PgogICAgPC9zYW1sOkF1dGhuU3RhdGVtZW50PgogICAgPHNhbWw6QXR0cmlidXRlU3RhdGVtZW50PgogICAgICA8c2FtbDpBdHRyaWJ1dGUgTmFtZT0idWlkIiBOYW1lRm9ybWF0PSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YXR0cm5hbWUtZm9ybWF0OmJhc2ljIj4KICAgICAgICA8c2FtbDpBdHRyaWJ1dGVWYWx1ZSB4c2k6dHlwZT0ieHM6c3RyaW5nIj50ZXN0PC9zYW1sOkF0dHJpYnV0ZVZhbHVlPgogICAgICA8L3NhbWw6QXR0cmlidXRlPgogICAgICA8c2FtbDpBdHRyaWJ1dGUgTmFtZT0ibWFpbCIgTmFtZUZvcm1hdD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmF0dHJuYW1lLWZvcm1hdDpiYXNpYyI+CiAgICAgICAgPHNhbWw6QXR0cmlidXRlVmFsdWUgeHNpOnR5cGU9InhzOnN0cmluZyI+dGVzdEBleGFtcGxlLmNvbTwvc2FtbDpBdHRyaWJ1dGVWYWx1ZT4KICAgICAgPC9zYW1sOkF0dHJpYnV0ZT4KICAgICAgPHNhbWw6QXR0cmlidXRlIE5hbWU9ImVkdVBlcnNvbkFmZmlsaWF0aW9uIiBOYW1lRm9ybWF0PSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YXR0cm5hbWUtZm9ybWF0OmJhc2ljIj4KICAgICAgICA8c2FtbDpBdHRyaWJ1dGVWYWx1ZSB4c2k6dHlwZT0ieHM6c3RyaW5nIj51c2Vyczwvc2FtbDpBdHRyaWJ1dGVWYWx1ZT4KICAgICAgICA8c2FtbDpBdHRyaWJ1dGVWYWx1ZSB4c2k6dHlwZT0ieHM6c3RyaW5nIj5leGFtcGxlcm9sZTE8L3NhbWw6QXR0cmlidXRlVmFsdWU+CiAgICAgIDwvc2FtbDpBdHRyaWJ1dGU+CiAgICA8L3NhbWw6QXR0cmlidXRlU3RhdGVtZW50PgogIDwvc2FtbDpBc3NlcnRpb24+Cjwvc2FtbHA6UmVzcG9uc2U+saml-0.4.6/testdata/TestXswPermutationEightIsRejected_response000066400000000000000000000205401415467341100247320ustar00rootroot00000000000000PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPHNhbWxwOlJlc3BvbnNlIHhtbG5zOnNhbWw9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphc3NlcnRpb24iIHhtbG5zOnNhbWxwPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6cHJvdG9jb2wiIERlc3RpbmF0aW9uPSJodHRwOi8vc3AuZXhhbXBsZS5jb20vZGVtbzEvaW5kZXgucGhwP2FjcyIgSUQ9Il84ZThkYzVmNjlhOThjYzRjMWZmMzQyN2U1Y2UzNDYwNmZkNjcyZjkxZTYiIEluUmVzcG9uc2VUbz0iT05FTE9HSU5fNGZlZTNiMDQ2Mzk1YzRlNzUxMDExZTk3Zjg5MDBiNTI3M2Q1NjY4NSIgSXNzdWVJbnN0YW50PSIyMDE0LTA3LTE3VDAxOjAxOjQ4WiIgVmVyc2lvbj0iMi4wIj48c2FtbDpJc3N1ZXI+aHR0cDovL2lkcC5leGFtcGxlLmNvbS9tZXRhZGF0YS5waHA8L3NhbWw6SXNzdWVyPjxzYW1scDpTdGF0dXM+PHNhbWxwOlN0YXR1c0NvZGUgVmFsdWU9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpzdGF0dXM6U3VjY2VzcyIvPjwvc2FtbHA6U3RhdHVzPjxzYW1sOkFzc2VydGlvbiB4bWxuczp4cz0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEiIHhtbG5zOnhzaT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEtaW5zdGFuY2UiIElEPSJwZngwNDY5MDBjNS0wNDIzLTM1Y2ItMmFkYi03MjI4M2JhNWQ4Y2QiIElzc3VlSW5zdGFudD0iMjAxNC0wNy0xN1QwMTowMTo0OFoiIFZlcnNpb249IjIuMCI+PHNhbWw6SXNzdWVyPmh0dHA6Ly9pZHAuZXhhbXBsZS5jb20vbWV0YWRhdGEucGhwPC9zYW1sOklzc3Vlcj48ZHM6U2lnbmF0dXJlIHhtbG5zOmRzPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwLzA5L3htbGRzaWcjIj48ZHM6U2lnbmVkSW5mbz48ZHM6Q2Fub25pY2FsaXphdGlvbk1ldGhvZCBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvMTAveG1sLWV4Yy1jMTRuIyIvPjxkczpTaWduYXR1cmVNZXRob2QgQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwLzA5L3htbGRzaWcjcnNhLXNoYTEiLz48ZHM6UmVmZXJlbmNlIFVSST0iI3BmeDA0NjkwMGM1LTA0MjMtMzVjYi0yYWRiLTcyMjgzYmE1ZDhjZCI+PGRzOlRyYW5zZm9ybXM+PGRzOlRyYW5zZm9ybSBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvMDkveG1sZHNpZyNlbnZlbG9wZWQtc2lnbmF0dXJlIi8+PGRzOlRyYW5zZm9ybSBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvMTAveG1sLWV4Yy1jMTRuIyIvPjwvZHM6VHJhbnNmb3Jtcz48ZHM6RGlnZXN0TWV0aG9kIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMC8wOS94bWxkc2lnI3NoYTEiLz48ZHM6RGlnZXN0VmFsdWU+YmV5ZnFIOXMxUys2bDJHQkhiU2xXOFR4SzZFPTwvZHM6RGlnZXN0VmFsdWU+PC9kczpSZWZlcmVuY2U+PC9kczpTaWduZWRJbmZvPjxkczpTaWduYXR1cmVWYWx1ZT5DSkJMY0pVTm91Q0psY3d5YUtTb1RGdHJUYVJOUWJnWHJFUUdKTmZsdjJkakx0M3J0d2krRzZMd3VQZkQrckF5b3lIbXFyUXlTaVJaZ1lNeWN1bk8vNUQ2R2J5ZVhJVjNrc093Y0YrQXlWZGtrblVpcVN3SDcvOXJkdkVhZmtKcDQ3d1pYKzc4dlFGMDZNcjFnNEpsODByTmNEUncxeE9FdW9QN2pDMjVtMVE9PC9kczpTaWduYXR1cmVWYWx1ZT48ZHM6S2V5SW5mbz48ZHM6WDUwOURhdGE+PGRzOlg1MDlDZXJ0aWZpY2F0ZT5NSUlDYWpDQ0FkT2dBd0lCQWdJQkFEQU5CZ2txaGtpRzl3MEJBUTBGQURCU01Rc3dDUVlEVlFRR0V3SjFjekVUTUJFR0ExVUVDQXdLUTJGc2FXWnZjbTVwWVRFVk1CTUdBMVVFQ2d3TVQyNWxiRzluYVc0Z1NXNWpNUmN3RlFZRFZRUUREQTV6Y0M1bGVHRnRjR3hsTG1OdmJUQWVGdzB4TkRBM01UY3hOREV5TlRaYUZ3MHhOVEEzTVRjeE5ERXlOVFphTUZJeEN6QUpCZ05WQkFZVEFuVnpNUk13RVFZRFZRUUlEQXBEWVd4cFptOXlibWxoTVJVd0V3WURWUVFLREF4UGJtVnNiMmRwYmlCSmJtTXhGekFWQmdOVkJBTU1Ebk53TG1WNFlXMXdiR1V1WTI5dE1JR2ZNQTBHQ1NxR1NJYjNEUUVCQVFVQUE0R05BRENCaVFLQmdRRFp4K09ONElVb0lXeGd1a1RiMXRPaVgzYk1ZellRaXdXUFVOTXArRnE4MnhvTm9nc28yYnlrWkcweWlKbTVvOHp2L3NkNnBHb3VheU1na3gvMkZTT2RjMzZUMGpHYkNIdVJTYnRpYTBQRXpOSVJ0bVZpTXJ0M0Flb1dCaWRSWG1ac3hDTkx3Z0lWNmRuMldwdUU1QXowYkhncFpuUXhUS0ZlazBCTUtVL2Q4d0lEQVFBQm8xQXdUakFkQmdOVkhRNEVGZ1FVR0h4WXFaWXlYN2NUeEtWT0RWZ1p3U1RkQ253d0h3WURWUjBqQkJnd0ZvQVVHSHhZcVpZeVg3Y1R4S1ZPRFZnWndTVGRDbnd3REFZRFZSMFRCQVV3QXdFQi96QU5CZ2txaGtpRzl3MEJBUTBGQUFPQmdRQnlGT2wraE1GSUNiZDNESmZucDJSZ2QvZHF0dHNaRy90eWhJTFd2RXJiaW8vREVlOThtWHBvd2hUa0MwNEVOcHJPeVhpN1piVXFpaWNGODl1QUd5dDFvcWdUVUNEMVZzTGFocUljbXJ6Z3VtTnlUd0xHV28xN1dEQWExL3VzRGhldFdBTWhnekYvQ25mNWVrMG5LMDBtMFlaR3ljNEx6Z0QwQ1JPTUFTVFdOZz09PC9kczpYNTA5Q2VydGlmaWNhdGU+PC9kczpYNTA5RGF0YT48L2RzOktleUluZm8+PE9iamVjdD48c2FtbDpBc3NlcnRpb24geG1sbnM6eHM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvWE1MU2NoZW1hIiB4bWxuczp4c2k9Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvWE1MU2NoZW1hLWluc3RhbmNlIiBJRD0icGZ4MDQ2OTAwYzUtMDQyMy0zNWNiLTJhZGItNzIyODNiYTVkOGNkIiBJc3N1ZUluc3RhbnQ9IjIwMTQtMDctMTdUMDE6MDE6NDhaIiBWZXJzaW9uPSIyLjAiPjxzYW1sOklzc3Vlcj5odHRwOi8vaWRwLmV4YW1wbGUuY29tL21ldGFkYXRhLnBocDwvc2FtbDpJc3N1ZXI+PHNhbWw6U3ViamVjdD48c2FtbDpOYW1lSUQgRm9ybWF0PSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6bmFtZWlkLWZvcm1hdDp0cmFuc2llbnQiIFNQTmFtZVF1YWxpZmllcj0iaHR0cDovL3NwLmV4YW1wbGUuY29tL2RlbW8xL21ldGFkYXRhLnBocCI+X2NlM2QyOTQ4YjRjZjIwMTQ2ZGVlMGEwYjNkZDZmNjliNmNmODZmNjJkNzwvc2FtbDpOYW1lSUQ+PHNhbWw6U3ViamVjdENvbmZpcm1hdGlvbiBNZXRob2Q9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpjbTpiZWFyZXIiPjxzYW1sOlN1YmplY3RDb25maXJtYXRpb25EYXRhIEluUmVzcG9uc2VUbz0iT05FTE9HSU5fNGZlZTNiMDQ2Mzk1YzRlNzUxMDExZTk3Zjg5MDBiNTI3M2Q1NjY4NSIgTm90T25PckFmdGVyPSIyMDI0LTAxLTE4VDA2OjIxOjQ4WiIgUmVjaXBpZW50PSJodHRwOi8vc3AuZXhhbXBsZS5jb20vZGVtbzEvaW5kZXgucGhwP2FjcyIvPjwvc2FtbDpTdWJqZWN0Q29uZmlybWF0aW9uPjwvc2FtbDpTdWJqZWN0PjxzYW1sOkNvbmRpdGlvbnMgTm90QmVmb3JlPSIyMDE0LTA3LTE3VDAxOjAxOjE4WiIgTm90T25PckFmdGVyPSIyMDI0LTAxLTE4VDA2OjIxOjQ4WiI+PHNhbWw6QXVkaWVuY2VSZXN0cmljdGlvbj48c2FtbDpBdWRpZW5jZT5odHRwOi8vc3AuZXhhbXBsZS5jb20vZGVtbzEvbWV0YWRhdGEucGhwPC9zYW1sOkF1ZGllbmNlPjwvc2FtbDpBdWRpZW5jZVJlc3RyaWN0aW9uPjwvc2FtbDpDb25kaXRpb25zPjxzYW1sOkF1dGhuU3RhdGVtZW50IEF1dGhuSW5zdGFudD0iMjAxNC0wNy0xN1QwMTowMTo0OFoiIFNlc3Npb25JbmRleD0iX2JlOTk2N2FiZDkwNGRkY2FlM2MwZWI0MTg5YWRiZTNmNzFlMzI3Y2Y5MyIgU2Vzc2lvbk5vdE9uT3JBZnRlcj0iMjAyNC0wNy0xN1QwOTowMTo0OFoiPjxzYW1sOkF1dGhuQ29udGV4dD48c2FtbDpBdXRobkNvbnRleHRDbGFzc1JlZj51cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YWM6Y2xhc3NlczpQYXNzd29yZDwvc2FtbDpBdXRobkNvbnRleHRDbGFzc1JlZj48L3NhbWw6QXV0aG5Db250ZXh0Pjwvc2FtbDpBdXRoblN0YXRlbWVudD48c2FtbDpBdHRyaWJ1dGVTdGF0ZW1lbnQ+PHNhbWw6QXR0cmlidXRlIE5hbWU9InVpZCIgTmFtZUZvcm1hdD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmF0dHJuYW1lLWZvcm1hdDpiYXNpYyI+PHNhbWw6QXR0cmlidXRlVmFsdWUgeHNpOnR5cGU9InhzOnN0cmluZyI+dGVzdDwvc2FtbDpBdHRyaWJ1dGVWYWx1ZT48L3NhbWw6QXR0cmlidXRlPjxzYW1sOkF0dHJpYnV0ZSBOYW1lPSJtYWlsIiBOYW1lRm9ybWF0PSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YXR0cm5hbWUtZm9ybWF0OmJhc2ljIj48c2FtbDpBdHRyaWJ1dGVWYWx1ZSB4c2k6dHlwZT0ieHM6c3RyaW5nIj50ZXN0QGV4YW1wbGUuY29tPC9zYW1sOkF0dHJpYnV0ZVZhbHVlPjwvc2FtbDpBdHRyaWJ1dGU+PHNhbWw6QXR0cmlidXRlIE5hbWU9ImVkdVBlcnNvbkFmZmlsaWF0aW9uIiBOYW1lRm9ybWF0PSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YXR0cm5hbWUtZm9ybWF0OmJhc2ljIj48c2FtbDpBdHRyaWJ1dGVWYWx1ZSB4c2k6dHlwZT0ieHM6c3RyaW5nIj51c2Vyczwvc2FtbDpBdHRyaWJ1dGVWYWx1ZT48c2FtbDpBdHRyaWJ1dGVWYWx1ZSB4c2k6dHlwZT0ieHM6c3RyaW5nIj5leGFtcGxlcm9sZTE8L3NhbWw6QXR0cmlidXRlVmFsdWU+PC9zYW1sOkF0dHJpYnV0ZT48L3NhbWw6QXR0cmlidXRlU3RhdGVtZW50Pjwvc2FtbDpBc3NlcnRpb24+PC9PYmplY3Q+PC9kczpTaWduYXR1cmU+PHNhbWw6U3ViamVjdD48c2FtbDpOYW1lSUQgRm9ybWF0PSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6bmFtZWlkLWZvcm1hdDp0cmFuc2llbnQiIFNQTmFtZVF1YWxpZmllcj0iaHR0cDovL3NwLmV4YW1wbGUuY29tL2RlbW8xL21ldGFkYXRhLnBocCI+X2NlM2QyOTQ4YjRjZjIwMTQ2ZGVlMGEwYjNkZDZmNjliNmNmODZmNjJkNzwvc2FtbDpOYW1lSUQ+PHNhbWw6U3ViamVjdENvbmZpcm1hdGlvbiBNZXRob2Q9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpjbTpiZWFyZXIiPjxzYW1sOlN1YmplY3RDb25maXJtYXRpb25EYXRhIEluUmVzcG9uc2VUbz0iT05FTE9HSU5fNGZlZTNiMDQ2Mzk1YzRlNzUxMDExZTk3Zjg5MDBiNTI3M2Q1NjY4NSIgTm90T25PckFmdGVyPSIyMDI0LTAxLTE4VDA2OjIxOjQ4WiIgUmVjaXBpZW50PSJodHRwOi8vc3AuZXhhbXBsZS5jb20vZGVtbzEvaW5kZXgucGhwP2FjcyIvPjwvc2FtbDpTdWJqZWN0Q29uZmlybWF0aW9uPjwvc2FtbDpTdWJqZWN0PjxzYW1sOkNvbmRpdGlvbnMgTm90QmVmb3JlPSIyMDE0LTA3LTE3VDAxOjAxOjE4WiIgTm90T25PckFmdGVyPSIyMDI0LTAxLTE4VDA2OjIxOjQ4WiI+PHNhbWw6QXVkaWVuY2VSZXN0cmljdGlvbj48c2FtbDpBdWRpZW5jZT5odHRwOi8vc3AuZXhhbXBsZS5jb20vZGVtbzEvbWV0YWRhdGEucGhwPC9zYW1sOkF1ZGllbmNlPjwvc2FtbDpBdWRpZW5jZVJlc3RyaWN0aW9uPjwvc2FtbDpDb25kaXRpb25zPjxzYW1sOkF1dGhuU3RhdGVtZW50IEF1dGhuSW5zdGFudD0iMjAxNC0wNy0xN1QwMTowMTo0OFoiIFNlc3Npb25JbmRleD0iX2JlOTk2N2FiZDkwNGRkY2FlM2MwZWI0MTg5YWRiZTNmNzFlMzI3Y2Y5MyIgU2Vzc2lvbk5vdE9uT3JBZnRlcj0iMjAyNC0wNy0xN1QwOTowMTo0OFoiPjxzYW1sOkF1dGhuQ29udGV4dD48c2FtbDpBdXRobkNvbnRleHRDbGFzc1JlZj51cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YWM6Y2xhc3NlczpQYXNzd29yZDwvc2FtbDpBdXRobkNvbnRleHRDbGFzc1JlZj48L3NhbWw6QXV0aG5Db250ZXh0Pjwvc2FtbDpBdXRoblN0YXRlbWVudD48c2FtbDpBdHRyaWJ1dGVTdGF0ZW1lbnQ+PHNhbWw6QXR0cmlidXRlIE5hbWU9InVpZCIgTmFtZUZvcm1hdD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmF0dHJuYW1lLWZvcm1hdDpiYXNpYyI+PHNhbWw6QXR0cmlidXRlVmFsdWUgeHNpOnR5cGU9InhzOnN0cmluZyI+dGVzdDwvc2FtbDpBdHRyaWJ1dGVWYWx1ZT48L3NhbWw6QXR0cmlidXRlPjxzYW1sOkF0dHJpYnV0ZSBOYW1lPSJtYWlsIiBOYW1lRm9ybWF0PSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YXR0cm5hbWUtZm9ybWF0OmJhc2ljIj48c2FtbDpBdHRyaWJ1dGVWYWx1ZSB4c2k6dHlwZT0ieHM6c3RyaW5nIj50ZXN0QGV4YW1wbGUuY29tPC9zYW1sOkF0dHJpYnV0ZVZhbHVlPjwvc2FtbDpBdHRyaWJ1dGU+PHNhbWw6QXR0cmlidXRlIE5hbWU9ImVkdVBlcnNvbkFmZmlsaWF0aW9uIiBOYW1lRm9ybWF0PSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YXR0cm5hbWUtZm9ybWF0OmJhc2ljIj48c2FtbDpBdHRyaWJ1dGVWYWx1ZSB4c2k6dHlwZT0ieHM6c3RyaW5nIj51c2Vyczwvc2FtbDpBdHRyaWJ1dGVWYWx1ZT48c2FtbDpBdHRyaWJ1dGVWYWx1ZSB4c2k6dHlwZT0ieHM6c3RyaW5nIj5leGFtcGxlcm9sZTE8L3NhbWw6QXR0cmlidXRlVmFsdWU+PC9zYW1sOkF0dHJpYnV0ZT48L3NhbWw6QXR0cmlidXRlU3RhdGVtZW50Pjwvc2FtbDpBc3NlcnRpb24+PC9zYW1scDpSZXNwb25zZT4Ksaml-0.4.6/testdata/TestXswPermutationFiveIsRejected_response000066400000000000000000000204601415467341100245640ustar00rootroot00000000000000PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPHNhbWxwOlJlc3BvbnNlIHhtbG5zOnNhbWw9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphc3NlcnRpb24iIHhtbG5zOnNhbWxwPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6cHJvdG9jb2wiIERlc3RpbmF0aW9uPSJodHRwOi8vc3AuZXhhbXBsZS5jb20vZGVtbzEvaW5kZXgucGhwP2FjcyIgSUQ9Il84ZThkYzVmNjlhOThjYzRjMWZmMzQyN2U1Y2UzNDYwNmZkNjcyZjkxZTYiIEluUmVzcG9uc2VUbz0iT05FTE9HSU5fNGZlZTNiMDQ2Mzk1YzRlNzUxMDExZTk3Zjg5MDBiNTI3M2Q1NjY4NSIgSXNzdWVJbnN0YW50PSIyMDE0LTA3LTE3VDAxOjAxOjQ4WiIgVmVyc2lvbj0iMi4wIj48c2FtbDpJc3N1ZXI+aHR0cDovL2lkcC5leGFtcGxlLmNvbS9tZXRhZGF0YS5waHA8L3NhbWw6SXNzdWVyPjxzYW1scDpTdGF0dXM+PHNhbWxwOlN0YXR1c0NvZGUgVmFsdWU9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpzdGF0dXM6U3VjY2VzcyIvPjwvc2FtbHA6U3RhdHVzPjxzYW1sOkFzc2VydGlvbiB4bWxuczp4cz0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEiIHhtbG5zOnhzaT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEtaW5zdGFuY2UiIElEPSJfZXZpbF9hc3NlcnRpb25fSUQiIElzc3VlSW5zdGFudD0iMjAxNC0wNy0xN1QwMTowMTo0OFoiIFZlcnNpb249IjIuMCI+PHNhbWw6SXNzdWVyPmh0dHA6Ly9pZHAuZXhhbXBsZS5jb20vbWV0YWRhdGEucGhwPC9zYW1sOklzc3Vlcj48ZHM6U2lnbmF0dXJlIHhtbG5zOmRzPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwLzA5L3htbGRzaWcjIj48ZHM6U2lnbmVkSW5mbz48ZHM6Q2Fub25pY2FsaXphdGlvbk1ldGhvZCBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvMTAveG1sLWV4Yy1jMTRuIyIvPjxkczpTaWduYXR1cmVNZXRob2QgQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwLzA5L3htbGRzaWcjcnNhLXNoYTEiLz48ZHM6UmVmZXJlbmNlIFVSST0iI3BmeDA0NjkwMGM1LTA0MjMtMzVjYi0yYWRiLTcyMjgzYmE1ZDhjZCI+PGRzOlRyYW5zZm9ybXM+PGRzOlRyYW5zZm9ybSBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvMDkveG1sZHNpZyNlbnZlbG9wZWQtc2lnbmF0dXJlIi8+PGRzOlRyYW5zZm9ybSBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvMTAveG1sLWV4Yy1jMTRuIyIvPjwvZHM6VHJhbnNmb3Jtcz48ZHM6RGlnZXN0TWV0aG9kIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMC8wOS94bWxkc2lnI3NoYTEiLz48ZHM6RGlnZXN0VmFsdWU+YmV5ZnFIOXMxUys2bDJHQkhiU2xXOFR4SzZFPTwvZHM6RGlnZXN0VmFsdWU+PC9kczpSZWZlcmVuY2U+PC9kczpTaWduZWRJbmZvPjxkczpTaWduYXR1cmVWYWx1ZT5DSkJMY0pVTm91Q0psY3d5YUtTb1RGdHJUYVJOUWJnWHJFUUdKTmZsdjJkakx0M3J0d2krRzZMd3VQZkQrckF5b3lIbXFyUXlTaVJaZ1lNeWN1bk8vNUQ2R2J5ZVhJVjNrc093Y0YrQXlWZGtrblVpcVN3SDcvOXJkdkVhZmtKcDQ3d1pYKzc4dlFGMDZNcjFnNEpsODByTmNEUncxeE9FdW9QN2pDMjVtMVE9PC9kczpTaWduYXR1cmVWYWx1ZT48ZHM6S2V5SW5mbz48ZHM6WDUwOURhdGE+PGRzOlg1MDlDZXJ0aWZpY2F0ZT5NSUlDYWpDQ0FkT2dBd0lCQWdJQkFEQU5CZ2txaGtpRzl3MEJBUTBGQURCU01Rc3dDUVlEVlFRR0V3SjFjekVUTUJFR0ExVUVDQXdLUTJGc2FXWnZjbTVwWVRFVk1CTUdBMVVFQ2d3TVQyNWxiRzluYVc0Z1NXNWpNUmN3RlFZRFZRUUREQTV6Y0M1bGVHRnRjR3hsTG1OdmJUQWVGdzB4TkRBM01UY3hOREV5TlRaYUZ3MHhOVEEzTVRjeE5ERXlOVFphTUZJeEN6QUpCZ05WQkFZVEFuVnpNUk13RVFZRFZRUUlEQXBEWVd4cFptOXlibWxoTVJVd0V3WURWUVFLREF4UGJtVnNiMmRwYmlCSmJtTXhGekFWQmdOVkJBTU1Ebk53TG1WNFlXMXdiR1V1WTI5dE1JR2ZNQTBHQ1NxR1NJYjNEUUVCQVFVQUE0R05BRENCaVFLQmdRRFp4K09ONElVb0lXeGd1a1RiMXRPaVgzYk1ZellRaXdXUFVOTXArRnE4MnhvTm9nc28yYnlrWkcweWlKbTVvOHp2L3NkNnBHb3VheU1na3gvMkZTT2RjMzZUMGpHYkNIdVJTYnRpYTBQRXpOSVJ0bVZpTXJ0M0Flb1dCaWRSWG1ac3hDTkx3Z0lWNmRuMldwdUU1QXowYkhncFpuUXhUS0ZlazBCTUtVL2Q4d0lEQVFBQm8xQXdUakFkQmdOVkhRNEVGZ1FVR0h4WXFaWXlYN2NUeEtWT0RWZ1p3U1RkQ253d0h3WURWUjBqQkJnd0ZvQVVHSHhZcVpZeVg3Y1R4S1ZPRFZnWndTVGRDbnd3REFZRFZSMFRCQVV3QXdFQi96QU5CZ2txaGtpRzl3MEJBUTBGQUFPQmdRQnlGT2wraE1GSUNiZDNESmZucDJSZ2QvZHF0dHNaRy90eWhJTFd2RXJiaW8vREVlOThtWHBvd2hUa0MwNEVOcHJPeVhpN1piVXFpaWNGODl1QUd5dDFvcWdUVUNEMVZzTGFocUljbXJ6Z3VtTnlUd0xHV28xN1dEQWExL3VzRGhldFdBTWhnekYvQ25mNWVrMG5LMDBtMFlaR3ljNEx6Z0QwQ1JPTUFTVFdOZz09PC9kczpYNTA5Q2VydGlmaWNhdGU+PC9kczpYNTA5RGF0YT48L2RzOktleUluZm8+PC9kczpTaWduYXR1cmU+PHNhbWw6U3ViamVjdD48c2FtbDpOYW1lSUQgRm9ybWF0PSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6bmFtZWlkLWZvcm1hdDp0cmFuc2llbnQiIFNQTmFtZVF1YWxpZmllcj0iaHR0cDovL3NwLmV4YW1wbGUuY29tL2RlbW8xL21ldGFkYXRhLnBocCI+X2NlM2QyOTQ4YjRjZjIwMTQ2ZGVlMGEwYjNkZDZmNjliNmNmODZmNjJkNzwvc2FtbDpOYW1lSUQ+PHNhbWw6U3ViamVjdENvbmZpcm1hdGlvbiBNZXRob2Q9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpjbTpiZWFyZXIiPjxzYW1sOlN1YmplY3RDb25maXJtYXRpb25EYXRhIEluUmVzcG9uc2VUbz0iT05FTE9HSU5fNGZlZTNiMDQ2Mzk1YzRlNzUxMDExZTk3Zjg5MDBiNTI3M2Q1NjY4NSIgTm90T25PckFmdGVyPSIyMDI0LTAxLTE4VDA2OjIxOjQ4WiIgUmVjaXBpZW50PSJodHRwOi8vc3AuZXhhbXBsZS5jb20vZGVtbzEvaW5kZXgucGhwP2FjcyIvPjwvc2FtbDpTdWJqZWN0Q29uZmlybWF0aW9uPjwvc2FtbDpTdWJqZWN0PjxzYW1sOkNvbmRpdGlvbnMgTm90QmVmb3JlPSIyMDE0LTA3LTE3VDAxOjAxOjE4WiIgTm90T25PckFmdGVyPSIyMDI0LTAxLTE4VDA2OjIxOjQ4WiI+PHNhbWw6QXVkaWVuY2VSZXN0cmljdGlvbj48c2FtbDpBdWRpZW5jZT5odHRwOi8vc3AuZXhhbXBsZS5jb20vZGVtbzEvbWV0YWRhdGEucGhwPC9zYW1sOkF1ZGllbmNlPjwvc2FtbDpBdWRpZW5jZVJlc3RyaWN0aW9uPjwvc2FtbDpDb25kaXRpb25zPjxzYW1sOkF1dGhuU3RhdGVtZW50IEF1dGhuSW5zdGFudD0iMjAxNC0wNy0xN1QwMTowMTo0OFoiIFNlc3Npb25JbmRleD0iX2JlOTk2N2FiZDkwNGRkY2FlM2MwZWI0MTg5YWRiZTNmNzFlMzI3Y2Y5MyIgU2Vzc2lvbk5vdE9uT3JBZnRlcj0iMjAyNC0wNy0xN1QwOTowMTo0OFoiPjxzYW1sOkF1dGhuQ29udGV4dD48c2FtbDpBdXRobkNvbnRleHRDbGFzc1JlZj51cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YWM6Y2xhc3NlczpQYXNzd29yZDwvc2FtbDpBdXRobkNvbnRleHRDbGFzc1JlZj48L3NhbWw6QXV0aG5Db250ZXh0Pjwvc2FtbDpBdXRoblN0YXRlbWVudD48c2FtbDpBdHRyaWJ1dGVTdGF0ZW1lbnQ+PHNhbWw6QXR0cmlidXRlIE5hbWU9InVpZCIgTmFtZUZvcm1hdD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmF0dHJuYW1lLWZvcm1hdDpiYXNpYyI+PHNhbWw6QXR0cmlidXRlVmFsdWUgeHNpOnR5cGU9InhzOnN0cmluZyI+dGVzdDwvc2FtbDpBdHRyaWJ1dGVWYWx1ZT48L3NhbWw6QXR0cmlidXRlPjxzYW1sOkF0dHJpYnV0ZSBOYW1lPSJtYWlsIiBOYW1lRm9ybWF0PSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YXR0cm5hbWUtZm9ybWF0OmJhc2ljIj48c2FtbDpBdHRyaWJ1dGVWYWx1ZSB4c2k6dHlwZT0ieHM6c3RyaW5nIj50ZXN0QGV4YW1wbGUuY29tPC9zYW1sOkF0dHJpYnV0ZVZhbHVlPjwvc2FtbDpBdHRyaWJ1dGU+PHNhbWw6QXR0cmlidXRlIE5hbWU9ImVkdVBlcnNvbkFmZmlsaWF0aW9uIiBOYW1lRm9ybWF0PSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YXR0cm5hbWUtZm9ybWF0OmJhc2ljIj48c2FtbDpBdHRyaWJ1dGVWYWx1ZSB4c2k6dHlwZT0ieHM6c3RyaW5nIj51c2Vyczwvc2FtbDpBdHRyaWJ1dGVWYWx1ZT48c2FtbDpBdHRyaWJ1dGVWYWx1ZSB4c2k6dHlwZT0ieHM6c3RyaW5nIj5leGFtcGxlcm9sZTE8L3NhbWw6QXR0cmlidXRlVmFsdWU+PC9zYW1sOkF0dHJpYnV0ZT48L3NhbWw6QXR0cmlidXRlU3RhdGVtZW50Pjwvc2FtbDpBc3NlcnRpb24+PHNhbWw6QXNzZXJ0aW9uIHhtbG5zOnhzPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxL1hNTFNjaGVtYSIgeG1sbnM6eHNpPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxL1hNTFNjaGVtYS1pbnN0YW5jZSIgSUQ9InBmeDA0NjkwMGM1LTA0MjMtMzVjYi0yYWRiLTcyMjgzYmE1ZDhjZCIgSXNzdWVJbnN0YW50PSIyMDE0LTA3LTE3VDAxOjAxOjQ4WiIgVmVyc2lvbj0iMi4wIj48c2FtbDpJc3N1ZXI+aHR0cDovL2lkcC5leGFtcGxlLmNvbS9tZXRhZGF0YS5waHA8L3NhbWw6SXNzdWVyPjxzYW1sOlN1YmplY3Q+PHNhbWw6TmFtZUlEIEZvcm1hdD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOm5hbWVpZC1mb3JtYXQ6dHJhbnNpZW50IiBTUE5hbWVRdWFsaWZpZXI9Imh0dHA6Ly9zcC5leGFtcGxlLmNvbS9kZW1vMS9tZXRhZGF0YS5waHAiPl9jZTNkMjk0OGI0Y2YyMDE0NmRlZTBhMGIzZGQ2ZjY5YjZjZjg2ZjYyZDc8L3NhbWw6TmFtZUlEPjxzYW1sOlN1YmplY3RDb25maXJtYXRpb24gTWV0aG9kPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6Y206YmVhcmVyIj48c2FtbDpTdWJqZWN0Q29uZmlybWF0aW9uRGF0YSBJblJlc3BvbnNlVG89Ik9ORUxPR0lOXzRmZWUzYjA0NjM5NWM0ZTc1MTAxMWU5N2Y4OTAwYjUyNzNkNTY2ODUiIE5vdE9uT3JBZnRlcj0iMjAyNC0wMS0xOFQwNjoyMTo0OFoiIFJlY2lwaWVudD0iaHR0cDovL3NwLmV4YW1wbGUuY29tL2RlbW8xL2luZGV4LnBocD9hY3MiLz48L3NhbWw6U3ViamVjdENvbmZpcm1hdGlvbj48L3NhbWw6U3ViamVjdD48c2FtbDpDb25kaXRpb25zIE5vdEJlZm9yZT0iMjAxNC0wNy0xN1QwMTowMToxOFoiIE5vdE9uT3JBZnRlcj0iMjAyNC0wMS0xOFQwNjoyMTo0OFoiPjxzYW1sOkF1ZGllbmNlUmVzdHJpY3Rpb24+PHNhbWw6QXVkaWVuY2U+aHR0cDovL3NwLmV4YW1wbGUuY29tL2RlbW8xL21ldGFkYXRhLnBocDwvc2FtbDpBdWRpZW5jZT48L3NhbWw6QXVkaWVuY2VSZXN0cmljdGlvbj48L3NhbWw6Q29uZGl0aW9ucz48c2FtbDpBdXRoblN0YXRlbWVudCBBdXRobkluc3RhbnQ9IjIwMTQtMDctMTdUMDE6MDE6NDhaIiBTZXNzaW9uSW5kZXg9Il9iZTk5NjdhYmQ5MDRkZGNhZTNjMGViNDE4OWFkYmUzZjcxZTMyN2NmOTMiIFNlc3Npb25Ob3RPbk9yQWZ0ZXI9IjIwMjQtMDctMTdUMDk6MDE6NDhaIj48c2FtbDpBdXRobkNvbnRleHQ+PHNhbWw6QXV0aG5Db250ZXh0Q2xhc3NSZWY+dXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmFjOmNsYXNzZXM6UGFzc3dvcmQ8L3NhbWw6QXV0aG5Db250ZXh0Q2xhc3NSZWY+PC9zYW1sOkF1dGhuQ29udGV4dD48L3NhbWw6QXV0aG5TdGF0ZW1lbnQ+PHNhbWw6QXR0cmlidXRlU3RhdGVtZW50PjxzYW1sOkF0dHJpYnV0ZSBOYW1lPSJ1aWQiIE5hbWVGb3JtYXQ9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphdHRybmFtZS1mb3JtYXQ6YmFzaWMiPjxzYW1sOkF0dHJpYnV0ZVZhbHVlIHhzaTp0eXBlPSJ4czpzdHJpbmciPnRlc3Q8L3NhbWw6QXR0cmlidXRlVmFsdWU+PC9zYW1sOkF0dHJpYnV0ZT48c2FtbDpBdHRyaWJ1dGUgTmFtZT0ibWFpbCIgTmFtZUZvcm1hdD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmF0dHJuYW1lLWZvcm1hdDpiYXNpYyI+PHNhbWw6QXR0cmlidXRlVmFsdWUgeHNpOnR5cGU9InhzOnN0cmluZyI+dGVzdEBleGFtcGxlLmNvbTwvc2FtbDpBdHRyaWJ1dGVWYWx1ZT48L3NhbWw6QXR0cmlidXRlPjxzYW1sOkF0dHJpYnV0ZSBOYW1lPSJlZHVQZXJzb25BZmZpbGlhdGlvbiIgTmFtZUZvcm1hdD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmF0dHJuYW1lLWZvcm1hdDpiYXNpYyI+PHNhbWw6QXR0cmlidXRlVmFsdWUgeHNpOnR5cGU9InhzOnN0cmluZyI+dXNlcnM8L3NhbWw6QXR0cmlidXRlVmFsdWU+PHNhbWw6QXR0cmlidXRlVmFsdWUgeHNpOnR5cGU9InhzOnN0cmluZyI+ZXhhbXBsZXJvbGUxPC9zYW1sOkF0dHJpYnV0ZVZhbHVlPjwvc2FtbDpBdHRyaWJ1dGU+PC9zYW1sOkF0dHJpYnV0ZVN0YXRlbWVudD48L3NhbWw6QXNzZXJ0aW9uPjwvc2FtbHA6UmVzcG9uc2U+Cg==saml-0.4.6/testdata/TestXswPermutationFourIsRejected_response000066400000000000000000000204601415467341100246060ustar00rootroot00000000000000PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPHNhbWxwOlJlc3BvbnNlIHhtbG5zOnNhbWw9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphc3NlcnRpb24iIHhtbG5zOnNhbWxwPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6cHJvdG9jb2wiIERlc3RpbmF0aW9uPSJodHRwOi8vc3AuZXhhbXBsZS5jb20vZGVtbzEvaW5kZXgucGhwP2FjcyIgSUQ9Il84ZThkYzVmNjlhOThjYzRjMWZmMzQyN2U1Y2UzNDYwNmZkNjcyZjkxZTYiIEluUmVzcG9uc2VUbz0iT05FTE9HSU5fNGZlZTNiMDQ2Mzk1YzRlNzUxMDExZTk3Zjg5MDBiNTI3M2Q1NjY4NSIgSXNzdWVJbnN0YW50PSIyMDE0LTA3LTE3VDAxOjAxOjQ4WiIgVmVyc2lvbj0iMi4wIj48c2FtbDpJc3N1ZXI+aHR0cDovL2lkcC5leGFtcGxlLmNvbS9tZXRhZGF0YS5waHA8L3NhbWw6SXNzdWVyPjxzYW1scDpTdGF0dXM+PHNhbWxwOlN0YXR1c0NvZGUgVmFsdWU9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpzdGF0dXM6U3VjY2VzcyIvPjwvc2FtbHA6U3RhdHVzPjxzYW1sOkFzc2VydGlvbiB4bWxuczp4cz0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEiIHhtbG5zOnhzaT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEtaW5zdGFuY2UiIElEPSJfZXZpbF9hc3NlcnRpb25fSUQiIElzc3VlSW5zdGFudD0iMjAxNC0wNy0xN1QwMTowMTo0OFoiIFZlcnNpb249IjIuMCI+PHNhbWw6SXNzdWVyPmh0dHA6Ly9pZHAuZXhhbXBsZS5jb20vbWV0YWRhdGEucGhwPC9zYW1sOklzc3Vlcj48c2FtbDpTdWJqZWN0PjxzYW1sOk5hbWVJRCBGb3JtYXQ9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpuYW1laWQtZm9ybWF0OnRyYW5zaWVudCIgU1BOYW1lUXVhbGlmaWVyPSJodHRwOi8vc3AuZXhhbXBsZS5jb20vZGVtbzEvbWV0YWRhdGEucGhwIj5fY2UzZDI5NDhiNGNmMjAxNDZkZWUwYTBiM2RkNmY2OWI2Y2Y4NmY2MmQ3PC9zYW1sOk5hbWVJRD48c2FtbDpTdWJqZWN0Q29uZmlybWF0aW9uIE1ldGhvZD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmNtOmJlYXJlciI+PHNhbWw6U3ViamVjdENvbmZpcm1hdGlvbkRhdGEgSW5SZXNwb25zZVRvPSJPTkVMT0dJTl80ZmVlM2IwNDYzOTVjNGU3NTEwMTFlOTdmODkwMGI1MjczZDU2Njg1IiBOb3RPbk9yQWZ0ZXI9IjIwMjQtMDEtMThUMDY6MjE6NDhaIiBSZWNpcGllbnQ9Imh0dHA6Ly9zcC5leGFtcGxlLmNvbS9kZW1vMS9pbmRleC5waHA/YWNzIi8+PC9zYW1sOlN1YmplY3RDb25maXJtYXRpb24+PC9zYW1sOlN1YmplY3Q+PHNhbWw6Q29uZGl0aW9ucyBOb3RCZWZvcmU9IjIwMTQtMDctMTdUMDE6MDE6MThaIiBOb3RPbk9yQWZ0ZXI9IjIwMjQtMDEtMThUMDY6MjE6NDhaIj48c2FtbDpBdWRpZW5jZVJlc3RyaWN0aW9uPjxzYW1sOkF1ZGllbmNlPmh0dHA6Ly9zcC5leGFtcGxlLmNvbS9kZW1vMS9tZXRhZGF0YS5waHA8L3NhbWw6QXVkaWVuY2U+PC9zYW1sOkF1ZGllbmNlUmVzdHJpY3Rpb24+PC9zYW1sOkNvbmRpdGlvbnM+PHNhbWw6QXV0aG5TdGF0ZW1lbnQgQXV0aG5JbnN0YW50PSIyMDE0LTA3LTE3VDAxOjAxOjQ4WiIgU2Vzc2lvbkluZGV4PSJfYmU5OTY3YWJkOTA0ZGRjYWUzYzBlYjQxODlhZGJlM2Y3MWUzMjdjZjkzIiBTZXNzaW9uTm90T25PckFmdGVyPSIyMDI0LTA3LTE3VDA5OjAxOjQ4WiI+PHNhbWw6QXV0aG5Db250ZXh0PjxzYW1sOkF1dGhuQ29udGV4dENsYXNzUmVmPnVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphYzpjbGFzc2VzOlBhc3N3b3JkPC9zYW1sOkF1dGhuQ29udGV4dENsYXNzUmVmPjwvc2FtbDpBdXRobkNvbnRleHQ+PC9zYW1sOkF1dGhuU3RhdGVtZW50PjxzYW1sOkF0dHJpYnV0ZVN0YXRlbWVudD48c2FtbDpBdHRyaWJ1dGUgTmFtZT0idWlkIiBOYW1lRm9ybWF0PSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YXR0cm5hbWUtZm9ybWF0OmJhc2ljIj48c2FtbDpBdHRyaWJ1dGVWYWx1ZSB4c2k6dHlwZT0ieHM6c3RyaW5nIj50ZXN0PC9zYW1sOkF0dHJpYnV0ZVZhbHVlPjwvc2FtbDpBdHRyaWJ1dGU+PHNhbWw6QXR0cmlidXRlIE5hbWU9Im1haWwiIE5hbWVGb3JtYXQ9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphdHRybmFtZS1mb3JtYXQ6YmFzaWMiPjxzYW1sOkF0dHJpYnV0ZVZhbHVlIHhzaTp0eXBlPSJ4czpzdHJpbmciPnRlc3RAZXhhbXBsZS5jb208L3NhbWw6QXR0cmlidXRlVmFsdWU+PC9zYW1sOkF0dHJpYnV0ZT48c2FtbDpBdHRyaWJ1dGUgTmFtZT0iZWR1UGVyc29uQWZmaWxpYXRpb24iIE5hbWVGb3JtYXQ9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphdHRybmFtZS1mb3JtYXQ6YmFzaWMiPjxzYW1sOkF0dHJpYnV0ZVZhbHVlIHhzaTp0eXBlPSJ4czpzdHJpbmciPnVzZXJzPC9zYW1sOkF0dHJpYnV0ZVZhbHVlPjxzYW1sOkF0dHJpYnV0ZVZhbHVlIHhzaTp0eXBlPSJ4czpzdHJpbmciPmV4YW1wbGVyb2xlMTwvc2FtbDpBdHRyaWJ1dGVWYWx1ZT48L3NhbWw6QXR0cmlidXRlPjwvc2FtbDpBdHRyaWJ1dGVTdGF0ZW1lbnQ+PHNhbWw6QXNzZXJ0aW9uIHhtbG5zOnhzPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxL1hNTFNjaGVtYSIgeG1sbnM6eHNpPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxL1hNTFNjaGVtYS1pbnN0YW5jZSIgSUQ9InBmeDA0NjkwMGM1LTA0MjMtMzVjYi0yYWRiLTcyMjgzYmE1ZDhjZCIgSXNzdWVJbnN0YW50PSIyMDE0LTA3LTE3VDAxOjAxOjQ4WiIgVmVyc2lvbj0iMi4wIj48c2FtbDpJc3N1ZXI+aHR0cDovL2lkcC5leGFtcGxlLmNvbS9tZXRhZGF0YS5waHA8L3NhbWw6SXNzdWVyPjxkczpTaWduYXR1cmUgeG1sbnM6ZHM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvMDkveG1sZHNpZyMiPjxkczpTaWduZWRJbmZvPjxkczpDYW5vbmljYWxpemF0aW9uTWV0aG9kIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS8xMC94bWwtZXhjLWMxNG4jIi8+PGRzOlNpZ25hdHVyZU1ldGhvZCBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvMDkveG1sZHNpZyNyc2Etc2hhMSIvPjxkczpSZWZlcmVuY2UgVVJJPSIjcGZ4MDQ2OTAwYzUtMDQyMy0zNWNiLTJhZGItNzIyODNiYTVkOGNkIj48ZHM6VHJhbnNmb3Jtcz48ZHM6VHJhbnNmb3JtIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMC8wOS94bWxkc2lnI2VudmVsb3BlZC1zaWduYXR1cmUiLz48ZHM6VHJhbnNmb3JtIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS8xMC94bWwtZXhjLWMxNG4jIi8+PC9kczpUcmFuc2Zvcm1zPjxkczpEaWdlc3RNZXRob2QgQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwLzA5L3htbGRzaWcjc2hhMSIvPjxkczpEaWdlc3RWYWx1ZT5iZXlmcUg5czFTKzZsMkdCSGJTbFc4VHhLNkU9PC9kczpEaWdlc3RWYWx1ZT48L2RzOlJlZmVyZW5jZT48L2RzOlNpZ25lZEluZm8+PGRzOlNpZ25hdHVyZVZhbHVlPkNKQkxjSlVOb3VDSmxjd3lhS1NvVEZ0clRhUk5RYmdYckVRR0pOZmx2MmRqTHQzcnR3aStHNkx3dVBmRCtyQXlveUhtcXJReVNpUlpnWU15Y3VuTy81RDZHYnllWElWM2tzT3djRitBeVZka2tuVWlxU3dINy85cmR2RWFma0pwNDd3WlgrNzh2UUYwNk1yMWc0Smw4MHJOY0RSdzF4T0V1b1A3akMyNW0xUT08L2RzOlNpZ25hdHVyZVZhbHVlPjxkczpLZXlJbmZvPjxkczpYNTA5RGF0YT48ZHM6WDUwOUNlcnRpZmljYXRlPk1JSUNhakNDQWRPZ0F3SUJBZ0lCQURBTkJna3Foa2lHOXcwQkFRMEZBREJTTVFzd0NRWURWUVFHRXdKMWN6RVRNQkVHQTFVRUNBd0tRMkZzYVdadmNtNXBZVEVWTUJNR0ExVUVDZ3dNVDI1bGJHOW5hVzRnU1c1ak1SY3dGUVlEVlFRRERBNXpjQzVsZUdGdGNHeGxMbU52YlRBZUZ3MHhOREEzTVRjeE5ERXlOVFphRncweE5UQTNNVGN4TkRFeU5UWmFNRkl4Q3pBSkJnTlZCQVlUQW5Wek1STXdFUVlEVlFRSURBcERZV3hwWm05eWJtbGhNUlV3RXdZRFZRUUtEQXhQYm1Wc2IyZHBiaUJKYm1NeEZ6QVZCZ05WQkFNTURuTndMbVY0WVcxd2JHVXVZMjl0TUlHZk1BMEdDU3FHU0liM0RRRUJBUVVBQTRHTkFEQ0JpUUtCZ1FEWngrT040SVVvSVd4Z3VrVGIxdE9pWDNiTVl6WVFpd1dQVU5NcCtGcTgyeG9Ob2dzbzJieWtaRzB5aUptNW84enYvc2Q2cEdvdWF5TWdreC8yRlNPZGMzNlQwakdiQ0h1UlNidGlhMFBFek5JUnRtVmlNcnQzQWVvV0JpZFJYbVpzeENOTHdnSVY2ZG4yV3B1RTVBejBiSGdwWm5ReFRLRmVrMEJNS1UvZDh3SURBUUFCbzFBd1RqQWRCZ05WSFE0RUZnUVVHSHhZcVpZeVg3Y1R4S1ZPRFZnWndTVGRDbnd3SHdZRFZSMGpCQmd3Rm9BVUdIeFlxWll5WDdjVHhLVk9EVmdad1NUZENud3dEQVlEVlIwVEJBVXdBd0VCL3pBTkJna3Foa2lHOXcwQkFRMEZBQU9CZ1FCeUZPbCtoTUZJQ2JkM0RKZm5wMlJnZC9kcXR0c1pHL3R5aElMV3ZFcmJpby9ERWU5OG1YcG93aFRrQzA0RU5wck95WGk3WmJVcWlpY0Y4OXVBR3l0MW9xZ1RVQ0QxVnNMYWhxSWNtcnpndW1OeVR3TEdXbzE3V0RBYTEvdXNEaGV0V0FNaGd6Ri9DbmY1ZWswbkswMG0wWVpHeWM0THpnRDBDUk9NQVNUV05nPT08L2RzOlg1MDlDZXJ0aWZpY2F0ZT48L2RzOlg1MDlEYXRhPjwvZHM6S2V5SW5mbz48L2RzOlNpZ25hdHVyZT48c2FtbDpTdWJqZWN0PjxzYW1sOk5hbWVJRCBGb3JtYXQ9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpuYW1laWQtZm9ybWF0OnRyYW5zaWVudCIgU1BOYW1lUXVhbGlmaWVyPSJodHRwOi8vc3AuZXhhbXBsZS5jb20vZGVtbzEvbWV0YWRhdGEucGhwIj5fY2UzZDI5NDhiNGNmMjAxNDZkZWUwYTBiM2RkNmY2OWI2Y2Y4NmY2MmQ3PC9zYW1sOk5hbWVJRD48c2FtbDpTdWJqZWN0Q29uZmlybWF0aW9uIE1ldGhvZD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmNtOmJlYXJlciI+PHNhbWw6U3ViamVjdENvbmZpcm1hdGlvbkRhdGEgSW5SZXNwb25zZVRvPSJPTkVMT0dJTl80ZmVlM2IwNDYzOTVjNGU3NTEwMTFlOTdmODkwMGI1MjczZDU2Njg1IiBOb3RPbk9yQWZ0ZXI9IjIwMjQtMDEtMThUMDY6MjE6NDhaIiBSZWNpcGllbnQ9Imh0dHA6Ly9zcC5leGFtcGxlLmNvbS9kZW1vMS9pbmRleC5waHA/YWNzIi8+PC9zYW1sOlN1YmplY3RDb25maXJtYXRpb24+PC9zYW1sOlN1YmplY3Q+PHNhbWw6Q29uZGl0aW9ucyBOb3RCZWZvcmU9IjIwMTQtMDctMTdUMDE6MDE6MThaIiBOb3RPbk9yQWZ0ZXI9IjIwMjQtMDEtMThUMDY6MjE6NDhaIj48c2FtbDpBdWRpZW5jZVJlc3RyaWN0aW9uPjxzYW1sOkF1ZGllbmNlPmh0dHA6Ly9zcC5leGFtcGxlLmNvbS9kZW1vMS9tZXRhZGF0YS5waHA8L3NhbWw6QXVkaWVuY2U+PC9zYW1sOkF1ZGllbmNlUmVzdHJpY3Rpb24+PC9zYW1sOkNvbmRpdGlvbnM+PHNhbWw6QXV0aG5TdGF0ZW1lbnQgQXV0aG5JbnN0YW50PSIyMDE0LTA3LTE3VDAxOjAxOjQ4WiIgU2Vzc2lvbkluZGV4PSJfYmU5OTY3YWJkOTA0ZGRjYWUzYzBlYjQxODlhZGJlM2Y3MWUzMjdjZjkzIiBTZXNzaW9uTm90T25PckFmdGVyPSIyMDI0LTA3LTE3VDA5OjAxOjQ4WiI+PHNhbWw6QXV0aG5Db250ZXh0PjxzYW1sOkF1dGhuQ29udGV4dENsYXNzUmVmPnVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphYzpjbGFzc2VzOlBhc3N3b3JkPC9zYW1sOkF1dGhuQ29udGV4dENsYXNzUmVmPjwvc2FtbDpBdXRobkNvbnRleHQ+PC9zYW1sOkF1dGhuU3RhdGVtZW50PjxzYW1sOkF0dHJpYnV0ZVN0YXRlbWVudD48c2FtbDpBdHRyaWJ1dGUgTmFtZT0idWlkIiBOYW1lRm9ybWF0PSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YXR0cm5hbWUtZm9ybWF0OmJhc2ljIj48c2FtbDpBdHRyaWJ1dGVWYWx1ZSB4c2k6dHlwZT0ieHM6c3RyaW5nIj50ZXN0PC9zYW1sOkF0dHJpYnV0ZVZhbHVlPjwvc2FtbDpBdHRyaWJ1dGU+PHNhbWw6QXR0cmlidXRlIE5hbWU9Im1haWwiIE5hbWVGb3JtYXQ9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphdHRybmFtZS1mb3JtYXQ6YmFzaWMiPjxzYW1sOkF0dHJpYnV0ZVZhbHVlIHhzaTp0eXBlPSJ4czpzdHJpbmciPnRlc3RAZXhhbXBsZS5jb208L3NhbWw6QXR0cmlidXRlVmFsdWU+PC9zYW1sOkF0dHJpYnV0ZT48c2FtbDpBdHRyaWJ1dGUgTmFtZT0iZWR1UGVyc29uQWZmaWxpYXRpb24iIE5hbWVGb3JtYXQ9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphdHRybmFtZS1mb3JtYXQ6YmFzaWMiPjxzYW1sOkF0dHJpYnV0ZVZhbHVlIHhzaTp0eXBlPSJ4czpzdHJpbmciPnVzZXJzPC9zYW1sOkF0dHJpYnV0ZVZhbHVlPjxzYW1sOkF0dHJpYnV0ZVZhbHVlIHhzaTp0eXBlPSJ4czpzdHJpbmciPmV4YW1wbGVyb2xlMTwvc2FtbDpBdHRyaWJ1dGVWYWx1ZT48L3NhbWw6QXR0cmlidXRlPjwvc2FtbDpBdHRyaWJ1dGVTdGF0ZW1lbnQ+PC9zYW1sOkFzc2VydGlvbj48L3NhbWw6QXNzZXJ0aW9uPjwvc2FtbHA6UmVzcG9uc2U+Cg==saml-0.4.6/testdata/TestXswPermutationNineIsRejected_response000066400000000000000000000205401415467341100245630ustar00rootroot00000000000000PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPHNhbWxwOlJlc3BvbnNlIHhtbG5zOnNhbWw9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphc3NlcnRpb24iIHhtbG5zOnNhbWxwPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6cHJvdG9jb2wiIERlc3RpbmF0aW9uPSJodHRwOi8vc3AuZXhhbXBsZS5jb20vZGVtbzEvaW5kZXgucGhwP2FjcyIgSUQ9Il84ZThkYzVmNjlhOThjYzRjMWZmMzQyN2U1Y2UzNDYwNmZkNjcyZjkxZTYiIEluUmVzcG9uc2VUbz0iT05FTE9HSU5fNGZlZTNiMDQ2Mzk1YzRlNzUxMDExZTk3Zjg5MDBiNTI3M2Q1NjY4NSIgSXNzdWVJbnN0YW50PSIyMDE0LTA3LTE3VDAxOjAxOjQ4WiIgVmVyc2lvbj0iMi4wIj48c2FtbDpJc3N1ZXI+aHR0cDovL2lkcC5leGFtcGxlLmNvbS9tZXRhZGF0YS5waHA8L3NhbWw6SXNzdWVyPjxzYW1scDpTdGF0dXM+PHNhbWxwOlN0YXR1c0NvZGUgVmFsdWU9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpzdGF0dXM6U3VjY2VzcyIvPjwvc2FtbHA6U3RhdHVzPjxzYW1sOkFzc2VydGlvbiB4bWxuczp4cz0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEiIHhtbG5zOnhzaT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEtaW5zdGFuY2UiIElEPSJzcG9vZmVkX2Fzc2VydGlvbl9pZCIgSXNzdWVJbnN0YW50PSIyMDE0LTA3LTE3VDAxOjAxOjQ4WiIgVmVyc2lvbj0iMi4wIj48c2FtbDpJc3N1ZXI+aHR0cDovL2lkcC5leGFtcGxlLmNvbS9tZXRhZGF0YS5waHA8L3NhbWw6SXNzdWVyPjxkczpTaWduYXR1cmUgeG1sbnM6ZHM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvMDkveG1sZHNpZyMiPjxkczpTaWduZWRJbmZvPjxkczpDYW5vbmljYWxpemF0aW9uTWV0aG9kIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS8xMC94bWwtZXhjLWMxNG4jIi8+PGRzOlNpZ25hdHVyZU1ldGhvZCBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvMDkveG1sZHNpZyNyc2Etc2hhMSIvPjxkczpSZWZlcmVuY2UgVVJJPSIjcGZ4MDQ2OTAwYzUtMDQyMy0zNWNiLTJhZGItNzIyODNiYTVkOGNkIj48ZHM6VHJhbnNmb3Jtcz48ZHM6VHJhbnNmb3JtIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMC8wOS94bWxkc2lnI2VudmVsb3BlZC1zaWduYXR1cmUiLz48ZHM6VHJhbnNmb3JtIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS8xMC94bWwtZXhjLWMxNG4jIi8+PC9kczpUcmFuc2Zvcm1zPjxkczpEaWdlc3RNZXRob2QgQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwLzA5L3htbGRzaWcjc2hhMSIvPjxkczpEaWdlc3RWYWx1ZT5iZXlmcUg5czFTKzZsMkdCSGJTbFc4VHhLNkU9PC9kczpEaWdlc3RWYWx1ZT48L2RzOlJlZmVyZW5jZT48L2RzOlNpZ25lZEluZm8+PGRzOlNpZ25hdHVyZVZhbHVlPkNKQkxjSlVOb3VDSmxjd3lhS1NvVEZ0clRhUk5RYmdYckVRR0pOZmx2MmRqTHQzcnR3aStHNkx3dVBmRCtyQXlveUhtcXJReVNpUlpnWU15Y3VuTy81RDZHYnllWElWM2tzT3djRitBeVZka2tuVWlxU3dINy85cmR2RWFma0pwNDd3WlgrNzh2UUYwNk1yMWc0Smw4MHJOY0RSdzF4T0V1b1A3akMyNW0xUT08L2RzOlNpZ25hdHVyZVZhbHVlPjxkczpLZXlJbmZvPjxkczpYNTA5RGF0YT48ZHM6WDUwOUNlcnRpZmljYXRlPk1JSUNhakNDQWRPZ0F3SUJBZ0lCQURBTkJna3Foa2lHOXcwQkFRMEZBREJTTVFzd0NRWURWUVFHRXdKMWN6RVRNQkVHQTFVRUNBd0tRMkZzYVdadmNtNXBZVEVWTUJNR0ExVUVDZ3dNVDI1bGJHOW5hVzRnU1c1ak1SY3dGUVlEVlFRRERBNXpjQzVsZUdGdGNHeGxMbU52YlRBZUZ3MHhOREEzTVRjeE5ERXlOVFphRncweE5UQTNNVGN4TkRFeU5UWmFNRkl4Q3pBSkJnTlZCQVlUQW5Wek1STXdFUVlEVlFRSURBcERZV3hwWm05eWJtbGhNUlV3RXdZRFZRUUtEQXhQYm1Wc2IyZHBiaUJKYm1NeEZ6QVZCZ05WQkFNTURuTndMbVY0WVcxd2JHVXVZMjl0TUlHZk1BMEdDU3FHU0liM0RRRUJBUVVBQTRHTkFEQ0JpUUtCZ1FEWngrT040SVVvSVd4Z3VrVGIxdE9pWDNiTVl6WVFpd1dQVU5NcCtGcTgyeG9Ob2dzbzJieWtaRzB5aUptNW84enYvc2Q2cEdvdWF5TWdreC8yRlNPZGMzNlQwakdiQ0h1UlNidGlhMFBFek5JUnRtVmlNcnQzQWVvV0JpZFJYbVpzeENOTHdnSVY2ZG4yV3B1RTVBejBiSGdwWm5ReFRLRmVrMEJNS1UvZDh3SURBUUFCbzFBd1RqQWRCZ05WSFE0RUZnUVVHSHhZcVpZeVg3Y1R4S1ZPRFZnWndTVGRDbnd3SHdZRFZSMGpCQmd3Rm9BVUdIeFlxWll5WDdjVHhLVk9EVmdad1NUZENud3dEQVlEVlIwVEJBVXdBd0VCL3pBTkJna3Foa2lHOXcwQkFRMEZBQU9CZ1FCeUZPbCtoTUZJQ2JkM0RKZm5wMlJnZC9kcXR0c1pHL3R5aElMV3ZFcmJpby9ERWU5OG1YcG93aFRrQzA0RU5wck95WGk3WmJVcWlpY0Y4OXVBR3l0MW9xZ1RVQ0QxVnNMYWhxSWNtcnpndW1OeVR3TEdXbzE3V0RBYTEvdXNEaGV0V0FNaGd6Ri9DbmY1ZWswbkswMG0wWVpHeWM0THpnRDBDUk9NQVNUV05nPT08L2RzOlg1MDlDZXJ0aWZpY2F0ZT48L2RzOlg1MDlEYXRhPjwvZHM6S2V5SW5mbz48L2RzOlNpZ25hdHVyZT48c2FtbDpTdWJqZWN0PjxzYW1sOk5hbWVJRCBGb3JtYXQ9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpuYW1laWQtZm9ybWF0OnRyYW5zaWVudCIgU1BOYW1lUXVhbGlmaWVyPSJodHRwOi8vc3AuZXhhbXBsZS5jb20vZGVtbzEvbWV0YWRhdGEucGhwIj5fY2UzZDI5NDhiNGNmMjAxNDZkZWUwYTBiM2RkNmY2OWI2Y2Y4NmY2MmQ3PC9zYW1sOk5hbWVJRD48c2FtbDpTdWJqZWN0Q29uZmlybWF0aW9uIE1ldGhvZD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmNtOmJlYXJlciI+PHNhbWw6U3ViamVjdENvbmZpcm1hdGlvbkRhdGEgSW5SZXNwb25zZVRvPSJPTkVMT0dJTl80ZmVlM2IwNDYzOTVjNGU3NTEwMTFlOTdmODkwMGI1MjczZDU2Njg1IiBOb3RPbk9yQWZ0ZXI9IjIwMjQtMDEtMThUMDY6MjE6NDhaIiBSZWNpcGllbnQ9Imh0dHA6Ly9zcC5leGFtcGxlLmNvbS9kZW1vMS9pbmRleC5waHA/YWNzIi8+PC9zYW1sOlN1YmplY3RDb25maXJtYXRpb24+PC9zYW1sOlN1YmplY3Q+PHNhbWw6Q29uZGl0aW9ucyBOb3RCZWZvcmU9IjIwMTQtMDctMTdUMDE6MDE6MThaIiBOb3RPbk9yQWZ0ZXI9IjIwMjQtMDEtMThUMDY6MjE6NDhaIj48c2FtbDpBdWRpZW5jZVJlc3RyaWN0aW9uPjxzYW1sOkF1ZGllbmNlPmh0dHA6Ly9zcC5leGFtcGxlLmNvbS9kZW1vMS9tZXRhZGF0YS5waHA8L3NhbWw6QXVkaWVuY2U+PC9zYW1sOkF1ZGllbmNlUmVzdHJpY3Rpb24+PC9zYW1sOkNvbmRpdGlvbnM+PHNhbWw6QXV0aG5TdGF0ZW1lbnQgQXV0aG5JbnN0YW50PSIyMDE0LTA3LTE3VDAxOjAxOjQ4WiIgU2Vzc2lvbkluZGV4PSJfYmU5OTY3YWJkOTA0ZGRjYWUzYzBlYjQxODlhZGJlM2Y3MWUzMjdjZjkzIiBTZXNzaW9uTm90T25PckFmdGVyPSIyMDI0LTA3LTE3VDA5OjAxOjQ4WiI+PHNhbWw6QXV0aG5Db250ZXh0PjxzYW1sOkF1dGhuQ29udGV4dENsYXNzUmVmPnVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphYzpjbGFzc2VzOlBhc3N3b3JkPC9zYW1sOkF1dGhuQ29udGV4dENsYXNzUmVmPjwvc2FtbDpBdXRobkNvbnRleHQ+PC9zYW1sOkF1dGhuU3RhdGVtZW50PjxzYW1sOkF0dHJpYnV0ZVN0YXRlbWVudD48c2FtbDpBdHRyaWJ1dGUgTmFtZT0idWlkIiBOYW1lRm9ybWF0PSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YXR0cm5hbWUtZm9ybWF0OmJhc2ljIj48c2FtbDpBdHRyaWJ1dGVWYWx1ZSB4c2k6dHlwZT0ieHM6c3RyaW5nIj5hdHRhY2tlcjwvc2FtbDpBdHRyaWJ1dGVWYWx1ZT48L3NhbWw6QXR0cmlidXRlPjxzYW1sOkF0dHJpYnV0ZSBOYW1lPSJtYWlsIiBOYW1lRm9ybWF0PSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YXR0cm5hbWUtZm9ybWF0OmJhc2ljIj48c2FtbDpBdHRyaWJ1dGVWYWx1ZSB4c2k6dHlwZT0ieHM6c3RyaW5nIj5hdHRhY2tlckBleGFtcGxlLmNvbTwvc2FtbDpBdHRyaWJ1dGVWYWx1ZT48L3NhbWw6QXR0cmlidXRlPjxzYW1sOkF0dHJpYnV0ZSBOYW1lPSJlZHVQZXJzb25BZmZpbGlhdGlvbiIgTmFtZUZvcm1hdD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmF0dHJuYW1lLWZvcm1hdDpiYXNpYyI+PHNhbWw6QXR0cmlidXRlVmFsdWUgeHNpOnR5cGU9InhzOnN0cmluZyI+dXNlcnM8L3NhbWw6QXR0cmlidXRlVmFsdWU+PHNhbWw6QXR0cmlidXRlVmFsdWUgeHNpOnR5cGU9InhzOnN0cmluZyI+ZXhhbXBsZXJvbGUxPC9zYW1sOkF0dHJpYnV0ZVZhbHVlPjwvc2FtbDpBdHRyaWJ1dGU+PC9zYW1sOkF0dHJpYnV0ZVN0YXRlbWVudD48L3NhbWw6QXNzZXJ0aW9uPjx4c3dfd3JhcHBlcj48c2FtbDpBc3NlcnRpb24geG1sbnM6eHM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvWE1MU2NoZW1hIiB4bWxuczp4c2k9Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvWE1MU2NoZW1hLWluc3RhbmNlIiBJRD0icGZ4MDQ2OTAwYzUtMDQyMy0zNWNiLTJhZGItNzIyODNiYTVkOGNkIiBJc3N1ZUluc3RhbnQ9IjIwMTQtMDctMTdUMDE6MDE6NDhaIiBWZXJzaW9uPSIyLjAiPjxzYW1sOklzc3Vlcj5odHRwOi8vaWRwLmV4YW1wbGUuY29tL21ldGFkYXRhLnBocDwvc2FtbDpJc3N1ZXI+PHNhbWw6U3ViamVjdD48c2FtbDpOYW1lSUQgRm9ybWF0PSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6bmFtZWlkLWZvcm1hdDp0cmFuc2llbnQiIFNQTmFtZVF1YWxpZmllcj0iaHR0cDovL3NwLmV4YW1wbGUuY29tL2RlbW8xL21ldGFkYXRhLnBocCI+X2NlM2QyOTQ4YjRjZjIwMTQ2ZGVlMGEwYjNkZDZmNjliNmNmODZmNjJkNzwvc2FtbDpOYW1lSUQ+PHNhbWw6U3ViamVjdENvbmZpcm1hdGlvbiBNZXRob2Q9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpjbTpiZWFyZXIiPjxzYW1sOlN1YmplY3RDb25maXJtYXRpb25EYXRhIEluUmVzcG9uc2VUbz0iT05FTE9HSU5fNGZlZTNiMDQ2Mzk1YzRlNzUxMDExZTk3Zjg5MDBiNTI3M2Q1NjY4NSIgTm90T25PckFmdGVyPSIyMDI0LTAxLTE4VDA2OjIxOjQ4WiIgUmVjaXBpZW50PSJodHRwOi8vc3AuZXhhbXBsZS5jb20vZGVtbzEvaW5kZXgucGhwP2FjcyIvPjwvc2FtbDpTdWJqZWN0Q29uZmlybWF0aW9uPjwvc2FtbDpTdWJqZWN0PjxzYW1sOkNvbmRpdGlvbnMgTm90QmVmb3JlPSIyMDE0LTA3LTE3VDAxOjAxOjE4WiIgTm90T25PckFmdGVyPSIyMDI0LTAxLTE4VDA2OjIxOjQ4WiI+PHNhbWw6QXVkaWVuY2VSZXN0cmljdGlvbj48c2FtbDpBdWRpZW5jZT5odHRwOi8vc3AuZXhhbXBsZS5jb20vZGVtbzEvbWV0YWRhdGEucGhwPC9zYW1sOkF1ZGllbmNlPjwvc2FtbDpBdWRpZW5jZVJlc3RyaWN0aW9uPjwvc2FtbDpDb25kaXRpb25zPjxzYW1sOkF1dGhuU3RhdGVtZW50IEF1dGhuSW5zdGFudD0iMjAxNC0wNy0xN1QwMTowMTo0OFoiIFNlc3Npb25JbmRleD0iX2JlOTk2N2FiZDkwNGRkY2FlM2MwZWI0MTg5YWRiZTNmNzFlMzI3Y2Y5MyIgU2Vzc2lvbk5vdE9uT3JBZnRlcj0iMjAyNC0wNy0xN1QwOTowMTo0OFoiPjxzYW1sOkF1dGhuQ29udGV4dD48c2FtbDpBdXRobkNvbnRleHRDbGFzc1JlZj51cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YWM6Y2xhc3NlczpQYXNzd29yZDwvc2FtbDpBdXRobkNvbnRleHRDbGFzc1JlZj48L3NhbWw6QXV0aG5Db250ZXh0Pjwvc2FtbDpBdXRoblN0YXRlbWVudD48c2FtbDpBdHRyaWJ1dGVTdGF0ZW1lbnQ+PHNhbWw6QXR0cmlidXRlIE5hbWU9InVpZCIgTmFtZUZvcm1hdD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmF0dHJuYW1lLWZvcm1hdDpiYXNpYyI+PHNhbWw6QXR0cmlidXRlVmFsdWUgeHNpOnR5cGU9InhzOnN0cmluZyI+dGVzdDwvc2FtbDpBdHRyaWJ1dGVWYWx1ZT48L3NhbWw6QXR0cmlidXRlPjxzYW1sOkF0dHJpYnV0ZSBOYW1lPSJtYWlsIiBOYW1lRm9ybWF0PSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YXR0cm5hbWUtZm9ybWF0OmJhc2ljIj48c2FtbDpBdHRyaWJ1dGVWYWx1ZSB4c2k6dHlwZT0ieHM6c3RyaW5nIj50ZXN0QGV4YW1wbGUuY29tPC9zYW1sOkF0dHJpYnV0ZVZhbHVlPjwvc2FtbDpBdHRyaWJ1dGU+PHNhbWw6QXR0cmlidXRlIE5hbWU9ImVkdVBlcnNvbkFmZmlsaWF0aW9uIiBOYW1lRm9ybWF0PSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YXR0cm5hbWUtZm9ybWF0OmJhc2ljIj48c2FtbDpBdHRyaWJ1dGVWYWx1ZSB4c2k6dHlwZT0ieHM6c3RyaW5nIj51c2Vyczwvc2FtbDpBdHRyaWJ1dGVWYWx1ZT48c2FtbDpBdHRyaWJ1dGVWYWx1ZSB4c2k6dHlwZT0ieHM6c3RyaW5nIj5leGFtcGxlcm9sZTE8L3NhbWw6QXR0cmlidXRlVmFsdWU+PC9zYW1sOkF0dHJpYnV0ZT48L3NhbWw6QXR0cmlidXRlU3RhdGVtZW50Pjwvc2FtbDpBc3NlcnRpb24+PC94c3dfd3JhcHBlcj48L3NhbWxwOlJlc3BvbnNlPgo=saml-0.4.6/testdata/TestXswPermutationOneIsRejected_response000066400000000000000000000262741415467341100244250ustar00rootroot00000000000000PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPHNhbWxwOlJlc3BvbnNlIHhtbG5zOnNhbWw9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphc3NlcnRpb24iIHhtbG5zOnNhbWxwPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6cHJvdG9jb2wiIERlc3RpbmF0aW9uPSJodHRwczovLzI5ZWU2ZDJlLm5ncm9rLmlvL3NhbWwvYWNzIiBJRD0iX2V2aWxfcmVzcG9uc2VfSUQiIEluUmVzcG9uc2VUbz0iaWQtZDQwYzE1YzEwNGI1MjY5MWVjY2YwYTJhNWM4YTE1NTk1YmU3NTQyMyIgSXNzdWVJbnN0YW50PSIyMDE2LTAxLTA1VDE3OjUzOjExWiIgVmVyc2lvbj0iMi4wIj48c2FtbDpJc3N1ZXI+aHR0cHM6Ly9hcHAub25lbG9naW4uY29tL3NhbWwvbWV0YWRhdGEvNTAzOTgzPC9zYW1sOklzc3Vlcj48ZHM6U2lnbmF0dXJlIHhtbG5zOmRzPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwLzA5L3htbGRzaWcjIj48ZHM6U2lnbmVkSW5mbz48ZHM6Q2Fub25pY2FsaXphdGlvbk1ldGhvZCBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvMTAveG1sLWV4Yy1jMTRuIyIvPjxkczpTaWduYXR1cmVNZXRob2QgQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwLzA5L3htbGRzaWcjcnNhLXNoYTEiLz48ZHM6UmVmZXJlbmNlIFVSST0iI3BmeGVkODhjNDNkLTY1MDQtZTFmMS01YWYwLTQwYmU3ZjI3OWZjNSI+PGRzOlRyYW5zZm9ybXM+PGRzOlRyYW5zZm9ybSBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvMDkveG1sZHNpZyNlbnZlbG9wZWQtc2lnbmF0dXJlIi8+PGRzOlRyYW5zZm9ybSBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvMTAveG1sLWV4Yy1jMTRuIyIvPjwvZHM6VHJhbnNmb3Jtcz48ZHM6RGlnZXN0TWV0aG9kIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMC8wOS94bWxkc2lnI3NoYTEiLz48ZHM6RGlnZXN0VmFsdWU+U1ZBYVFnOHZtbVNRTDYvWUJtUzJ5ZEtSUDdJPTwvZHM6RGlnZXN0VmFsdWU+PC9kczpSZWZlcmVuY2U+PC9kczpTaWduZWRJbmZvPjxkczpTaWduYXR1cmVWYWx1ZT5zQmVUVlAwYlpvUFIrYmZ5QWtWdjZJM0NWN1k4WHFuSjJyOGYxK1dtcjJnRmduUkY4NU52dlNQK3IxQm83bnR1T3N3TzRmQjRSSzRIeVNieWxnNGJLSEtIMTlYOTFoVkF6SlN5c2ZtUy9kNXdnMUNmaVdXdDVTMkhBNTA4dGhYdVpud0czWHo2S25XSzhrUmR4MWRjK1lSV2dhRnlkNGdMRzlhQlRzWE9aN3Z4LzdQNGJyek5FbTR3UDkvMHR1ZnhHK25zWTZEcHduRUdDamwrVlVLcGd6RXF3Tk5qUXFZRllTQVhFaytWdCtYM2MyZDBISXJaUXZZbk5oMDJLeHV3VkJUaG4zTWF6UU5hTnhDL3N5ZjNrRFFDUnJaQ1lvK1l0RHVkekpVOXAzQTBZWEhUUWNzZGV0c0haWENNajNtdXZ6YzBtRUJsdzRMYmNoS21uYnlabWc9PTwvZHM6U2lnbmF0dXJlVmFsdWU+PGRzOktleUluZm8+PGRzOlg1MDlEYXRhPjxkczpYNTA5Q2VydGlmaWNhdGU+TUlJRUNEQ0NBdkNnQXdJQkFnSVVYdW4wOENzbExSV1NMcU5uREUxTnRHSmVmbDB3RFFZSktvWklodmNOQVFFRkJRQXdVekVMTUFrR0ExVUVCaE1DVlZNeEREQUtCZ05WQkFvTUEyTjBkVEVWTUJNR0ExVUVDd3dNVDI1bFRHOW5hVzRnU1dSUU1SOHdIUVlEVlFRRERCWlBibVZNYjJkcGJpQkJZMk52ZFc1MElETXlOakUwTUI0WERURXpNRGt6TURFNU16VTBORm9YRFRFNE1UQXdNVEU1TXpVME5Gb3dVekVMTUFrR0ExVUVCaE1DVlZNeEREQUtCZ05WQkFvTUEyTjBkVEVWTUJNR0ExVUVDd3dNVDI1bFRHOW5hVzRnU1dSUU1SOHdIUVlEVlFRRERCWlBibVZNYjJkcGJpQkJZMk52ZFc1MElETXlOakUwTUlJQklqQU5CZ2txaGtpRzl3MEJBUUVGQUFPQ0FROEFNSUlCQ2dLQ0FRRUEwT0c4VjhtaG92a2o0cmhHaGpyYkV4UlliektWMlp4ZnZHZkVHWEdVdlhjNkRxZWpZRWRoWjJtSWZDRG9qaFFqazBCeXdpaXJBS01PdDFHTnVIN2FXSUU0N0QwZXd0SzV5bEVBbTdlVm1vWTRreExDYVc1d1lyQzFTek1ucGVpdFV4cXZzYm5LejNqVUtZSFJnZ3BmdlZqNHNpSERaZUlaYTlhNXJVdnBNbm5iT29GaVpDSUVOcHEzVEMzM2l2T1NaaEVOUlR6bXZuazVHRG9MSHcvOHFBZ1FpeVQzRDF4Q2tTQmI1NFBIZ2tRNVJxMW9kTE0vaEorTDBqekNVUUg0Z3hwV2xFQWFiNEs5czhmcEJVQkJoNWdtSkNZaThVYklsaHFPOE4ybXludW0zM0JVL3ZKM1BuYXdUNFlZa1R3UlV4NlkrM2ZwbVJCSHFsNGg4M1NNZXdJREFRQUJvNEhUTUlIUU1Bd0dBMVVkRXdFQi93UUNNQUF3SFFZRFZSME9CQllFRk9mRkZqSEZqOWE2eHBuZ2IxMXJyaGdNZTlBck1JR1FCZ05WSFNNRWdZZ3dnWVdBRk9mRkZqSEZqOWE2eHBuZ2IxMXJyaGdNZTlBcm9WZWtWVEJUTVFzd0NRWURWUVFHRXdKVlV6RU1NQW9HQTFVRUNnd0RZM1IxTVJVd0V3WURWUVFMREF4UGJtVk1iMmRwYmlCSlpGQXhIekFkQmdOVkJBTU1Gazl1WlV4dloybHVJRUZqWTI5MWJuUWdNekkyTVRTQ0ZGN3A5UEFySlMwVmtpNmpad3hOVGJSaVhuNWRNQTRHQTFVZER3RUIvd1FFQXdJSGdEQU5CZ2txaGtpRzl3MEJBUVVGQUFPQ0FRRUFNZ2xuNE5QTVFuOEd5dnE4Q1RQK2MyZTZDVXpjdlJFS25UaGp4VDlXY3ZWMVpWWE1CTlBtNGNUcVQzNjFFZEx6WTV5V0xVV1hkNEF2Rm5jaXFCM01IWWEybnFUbW52TGdtaGtXZStoZEZvTmU1K0lBOEF4R24rbnFVSVNteUJlQ3h1VVVBYlJNdW93aUFyd0hJcHpwRXlSSVlkU1pSTkYwZHZnaVBZeXIvTWlQWEljenBINW5Ma3ZiTHBjQUYrUjhaaDlud1kwZzFKVnljNkFCNmo3WWV4dVVRWnBISDRzMFZkeC9uV21yY0ZlTFpLQ1R4Y2FoSHZVNTBlMXlLWDV0aGZWYUpxSThRUTd4Wnh5dTBUVHNpYVgwdXc1MUpQT3pQdUFQcGgwejZ4b1M5b1l4dXpaMXk5c05ISDZrSDhHRm52UzJNcXlIaU56MGgwU3EvcTZuK3c9PTwvZHM6WDUwOUNlcnRpZmljYXRlPjwvZHM6WDUwOURhdGE+PC9kczpLZXlJbmZvPjxzYW1scDpSZXNwb25zZSB4bWxuczpzYW1sPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YXNzZXJ0aW9uIiB4bWxuczpzYW1scD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOnByb3RvY29sIiBEZXN0aW5hdGlvbj0iaHR0cHM6Ly8yOWVlNmQyZS5uZ3Jvay5pby9zYW1sL2FjcyIgSUQ9InBmeGVkODhjNDNkLTY1MDQtZTFmMS01YWYwLTQwYmU3ZjI3OWZjNSIgSW5SZXNwb25zZVRvPSJpZC1kNDBjMTVjMTA0YjUyNjkxZWNjZjBhMmE1YzhhMTU1OTViZTc1NDIzIiBJc3N1ZUluc3RhbnQ9IjIwMTYtMDEtMDVUMTc6NTM6MTFaIiBWZXJzaW9uPSIyLjAiPjxzYW1sOklzc3Vlcj5odHRwczovL2FwcC5vbmVsb2dpbi5jb20vc2FtbC9tZXRhZGF0YS81MDM5ODM8L3NhbWw6SXNzdWVyPjxzYW1scDpTdGF0dXM+PHNhbWxwOlN0YXR1c0NvZGUgVmFsdWU9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpzdGF0dXM6U3VjY2VzcyIvPjwvc2FtbHA6U3RhdHVzPjxzYW1sOkFzc2VydGlvbiB4bWxuczpzYW1sPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YXNzZXJ0aW9uIiB4bWxuczp4cz0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEiIHhtbG5zOnhzaT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEtaW5zdGFuY2UiIElEPSJBZDk0NWFlZGEzOGE1MDhmOGZhYzliYzk2MTNkNTk2NDJjMGQyZDhjYiIgSXNzdWVJbnN0YW50PSIyMDE2LTAxLTA1VDE3OjUzOjExWiIgVmVyc2lvbj0iMi4wIj48c2FtbDpJc3N1ZXI+aHR0cHM6Ly9hcHAub25lbG9naW4uY29tL3NhbWwvbWV0YWRhdGEvNTAzOTgzPC9zYW1sOklzc3Vlcj48c2FtbDpTdWJqZWN0PjxzYW1sOk5hbWVJRCBGb3JtYXQ9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjEuMTpuYW1laWQtZm9ybWF0OmVtYWlsQWRkcmVzcyI+cm9zc0BrbmRyLm9yZzwvc2FtbDpOYW1lSUQ+PHNhbWw6U3ViamVjdENvbmZpcm1hdGlvbiBNZXRob2Q9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpjbTpiZWFyZXIiPjxzYW1sOlN1YmplY3RDb25maXJtYXRpb25EYXRhIEluUmVzcG9uc2VUbz0iaWQtZDQwYzE1YzEwNGI1MjY5MWVjY2YwYTJhNWM4YTE1NTk1YmU3NTQyMyIgTm90T25PckFmdGVyPSIyMDE2LTAxLTA1VDE3OjU2OjExWiIgUmVjaXBpZW50PSJodHRwczovLzI5ZWU2ZDJlLm5ncm9rLmlvL3NhbWwvYWNzIi8+PC9zYW1sOlN1YmplY3RDb25maXJtYXRpb24+PC9zYW1sOlN1YmplY3Q+PHNhbWw6Q29uZGl0aW9ucyBOb3RCZWZvcmU9IjIwMTYtMDEtMDVUMTc6NTA6MTFaIiBOb3RPbk9yQWZ0ZXI9IjIwMTYtMDEtMDVUMTc6NTY6MTFaIj48c2FtbDpBdWRpZW5jZVJlc3RyaWN0aW9uPjxzYW1sOkF1ZGllbmNlPmh0dHBzOi8vMjllZTZkMmUubmdyb2suaW8vc2FtbC9tZXRhZGF0YTwvc2FtbDpBdWRpZW5jZT48L3NhbWw6QXVkaWVuY2VSZXN0cmljdGlvbj48L3NhbWw6Q29uZGl0aW9ucz48c2FtbDpBdXRoblN0YXRlbWVudCBBdXRobkluc3RhbnQ9IjIwMTYtMDEtMDVUMTc6NTM6MTBaIiBTZXNzaW9uSW5kZXg9Il9lYmRjYmU4MC05NWZmLTAxMzMtZDg3MS0zOGNhM2E2NjJmMWMiIFNlc3Npb25Ob3RPbk9yQWZ0ZXI9IjIwMTYtMDEtMDZUMTc6NTM6MTFaIj48c2FtbDpBdXRobkNvbnRleHQ+PHNhbWw6QXV0aG5Db250ZXh0Q2xhc3NSZWY+dXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmFjOmNsYXNzZXM6UGFzc3dvcmRQcm90ZWN0ZWRUcmFuc3BvcnQ8L3NhbWw6QXV0aG5Db250ZXh0Q2xhc3NSZWY+PC9zYW1sOkF1dGhuQ29udGV4dD48L3NhbWw6QXV0aG5TdGF0ZW1lbnQ+PHNhbWw6QXR0cmlidXRlU3RhdGVtZW50PjxzYW1sOkF0dHJpYnV0ZSBOYW1lPSJVc2VyLmVtYWlsIiBOYW1lRm9ybWF0PSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YXR0cm5hbWUtZm9ybWF0OmJhc2ljIj48c2FtbDpBdHRyaWJ1dGVWYWx1ZSB4bWxuczp4c2k9Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvWE1MU2NoZW1hLWluc3RhbmNlIiB4c2k6dHlwZT0ieHM6c3RyaW5nIj5yb3NzQGtuZHIub3JnPC9zYW1sOkF0dHJpYnV0ZVZhbHVlPjwvc2FtbDpBdHRyaWJ1dGU+PHNhbWw6QXR0cmlidXRlIE5hbWU9Im1lbWJlck9mIiBOYW1lRm9ybWF0PSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YXR0cm5hbWUtZm9ybWF0OmJhc2ljIj48c2FtbDpBdHRyaWJ1dGVWYWx1ZSB4bWxuczp4c2k9Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvWE1MU2NoZW1hLWluc3RhbmNlIiB4c2k6dHlwZT0ieHM6c3RyaW5nIi8+PC9zYW1sOkF0dHJpYnV0ZT48c2FtbDpBdHRyaWJ1dGUgTmFtZT0iVXNlci5MYXN0TmFtZSIgTmFtZUZvcm1hdD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmF0dHJuYW1lLWZvcm1hdDpiYXNpYyI+PHNhbWw6QXR0cmlidXRlVmFsdWUgeG1sbnM6eHNpPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxL1hNTFNjaGVtYS1pbnN0YW5jZSIgeHNpOnR5cGU9InhzOnN0cmluZyI+S2luZGVyPC9zYW1sOkF0dHJpYnV0ZVZhbHVlPjwvc2FtbDpBdHRyaWJ1dGU+PHNhbWw6QXR0cmlidXRlIE5hbWU9IlBlcnNvbkltbXV0YWJsZUlEIiBOYW1lRm9ybWF0PSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YXR0cm5hbWUtZm9ybWF0OmJhc2ljIj48c2FtbDpBdHRyaWJ1dGVWYWx1ZSB4bWxuczp4c2k9Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvWE1MU2NoZW1hLWluc3RhbmNlIiB4c2k6dHlwZT0ieHM6c3RyaW5nIi8+PC9zYW1sOkF0dHJpYnV0ZT48c2FtbDpBdHRyaWJ1dGUgTmFtZT0iVXNlci5GaXJzdE5hbWUiIE5hbWVGb3JtYXQ9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphdHRybmFtZS1mb3JtYXQ6YmFzaWMiPjxzYW1sOkF0dHJpYnV0ZVZhbHVlIHhtbG5zOnhzaT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEtaW5zdGFuY2UiIHhzaTp0eXBlPSJ4czpzdHJpbmciPlJvc3M8L3NhbWw6QXR0cmlidXRlVmFsdWU+PC9zYW1sOkF0dHJpYnV0ZT48L3NhbWw6QXR0cmlidXRlU3RhdGVtZW50Pjwvc2FtbDpBc3NlcnRpb24+PC9zYW1scDpSZXNwb25zZT48L2RzOlNpZ25hdHVyZT48c2FtbHA6U3RhdHVzPjxzYW1scDpTdGF0dXNDb2RlIFZhbHVlPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6c3RhdHVzOlN1Y2Nlc3MiLz48L3NhbWxwOlN0YXR1cz48c2FtbDpBc3NlcnRpb24geG1sbnM6c2FtbD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmFzc2VydGlvbiIgeG1sbnM6eHM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvWE1MU2NoZW1hIiB4bWxuczp4c2k9Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvWE1MU2NoZW1hLWluc3RhbmNlIiBJRD0iQWQ5NDVhZWRhMzhhNTA4ZjhmYWM5YmM5NjEzZDU5NjQyYzBkMmQ4Y2IiIElzc3VlSW5zdGFudD0iMjAxNi0wMS0wNVQxNzo1MzoxMVoiIFZlcnNpb249IjIuMCI+PHNhbWw6SXNzdWVyPmh0dHBzOi8vYXBwLm9uZWxvZ2luLmNvbS9zYW1sL21ldGFkYXRhLzUwMzk4Mzwvc2FtbDpJc3N1ZXI+PHNhbWw6U3ViamVjdD48c2FtbDpOYW1lSUQgRm9ybWF0PSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoxLjE6bmFtZWlkLWZvcm1hdDplbWFpbEFkZHJlc3MiPnJvc3NAa25kci5vcmc8L3NhbWw6TmFtZUlEPjxzYW1sOlN1YmplY3RDb25maXJtYXRpb24gTWV0aG9kPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6Y206YmVhcmVyIj48c2FtbDpTdWJqZWN0Q29uZmlybWF0aW9uRGF0YSBJblJlc3BvbnNlVG89ImlkLWQ0MGMxNWMxMDRiNTI2OTFlY2NmMGEyYTVjOGExNTU5NWJlNzU0MjMiIE5vdE9uT3JBZnRlcj0iMjAxNi0wMS0wNVQxNzo1NjoxMVoiIFJlY2lwaWVudD0iaHR0cHM6Ly8yOWVlNmQyZS5uZ3Jvay5pby9zYW1sL2FjcyIvPjwvc2FtbDpTdWJqZWN0Q29uZmlybWF0aW9uPjwvc2FtbDpTdWJqZWN0PjxzYW1sOkNvbmRpdGlvbnMgTm90QmVmb3JlPSIyMDE2LTAxLTA1VDE3OjUwOjExWiIgTm90T25PckFmdGVyPSIyMDE2LTAxLTA1VDE3OjU2OjExWiI+PHNhbWw6QXVkaWVuY2VSZXN0cmljdGlvbj48c2FtbDpBdWRpZW5jZT5odHRwczovLzI5ZWU2ZDJlLm5ncm9rLmlvL3NhbWwvbWV0YWRhdGE8L3NhbWw6QXVkaWVuY2U+PC9zYW1sOkF1ZGllbmNlUmVzdHJpY3Rpb24+PC9zYW1sOkNvbmRpdGlvbnM+PHNhbWw6QXV0aG5TdGF0ZW1lbnQgQXV0aG5JbnN0YW50PSIyMDE2LTAxLTA1VDE3OjUzOjEwWiIgU2Vzc2lvbkluZGV4PSJfZWJkY2JlODAtOTVmZi0wMTMzLWQ4NzEtMzhjYTNhNjYyZjFjIiBTZXNzaW9uTm90T25PckFmdGVyPSIyMDE2LTAxLTA2VDE3OjUzOjExWiI+PHNhbWw6QXV0aG5Db250ZXh0PjxzYW1sOkF1dGhuQ29udGV4dENsYXNzUmVmPnVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphYzpjbGFzc2VzOlBhc3N3b3JkUHJvdGVjdGVkVHJhbnNwb3J0PC9zYW1sOkF1dGhuQ29udGV4dENsYXNzUmVmPjwvc2FtbDpBdXRobkNvbnRleHQ+PC9zYW1sOkF1dGhuU3RhdGVtZW50PjxzYW1sOkF0dHJpYnV0ZVN0YXRlbWVudD48c2FtbDpBdHRyaWJ1dGUgTmFtZT0iVXNlci5lbWFpbCIgTmFtZUZvcm1hdD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmF0dHJuYW1lLWZvcm1hdDpiYXNpYyI+PHNhbWw6QXR0cmlidXRlVmFsdWUgeG1sbnM6eHNpPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxL1hNTFNjaGVtYS1pbnN0YW5jZSIgeHNpOnR5cGU9InhzOnN0cmluZyI+cm9zc0BrbmRyLm9yZzwvc2FtbDpBdHRyaWJ1dGVWYWx1ZT48L3NhbWw6QXR0cmlidXRlPjxzYW1sOkF0dHJpYnV0ZSBOYW1lPSJtZW1iZXJPZiIgTmFtZUZvcm1hdD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmF0dHJuYW1lLWZvcm1hdDpiYXNpYyI+PHNhbWw6QXR0cmlidXRlVmFsdWUgeG1sbnM6eHNpPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxL1hNTFNjaGVtYS1pbnN0YW5jZSIgeHNpOnR5cGU9InhzOnN0cmluZyIvPjwvc2FtbDpBdHRyaWJ1dGU+PHNhbWw6QXR0cmlidXRlIE5hbWU9IlVzZXIuTGFzdE5hbWUiIE5hbWVGb3JtYXQ9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphdHRybmFtZS1mb3JtYXQ6YmFzaWMiPjxzYW1sOkF0dHJpYnV0ZVZhbHVlIHhtbG5zOnhzaT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEtaW5zdGFuY2UiIHhzaTp0eXBlPSJ4czpzdHJpbmciPktpbmRlcjwvc2FtbDpBdHRyaWJ1dGVWYWx1ZT48L3NhbWw6QXR0cmlidXRlPjxzYW1sOkF0dHJpYnV0ZSBOYW1lPSJQZXJzb25JbW11dGFibGVJRCIgTmFtZUZvcm1hdD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmF0dHJuYW1lLWZvcm1hdDpiYXNpYyI+PHNhbWw6QXR0cmlidXRlVmFsdWUgeG1sbnM6eHNpPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxL1hNTFNjaGVtYS1pbnN0YW5jZSIgeHNpOnR5cGU9InhzOnN0cmluZyIvPjwvc2FtbDpBdHRyaWJ1dGU+PHNhbWw6QXR0cmlidXRlIE5hbWU9IlVzZXIuRmlyc3ROYW1lIiBOYW1lRm9ybWF0PSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YXR0cm5hbWUtZm9ybWF0OmJhc2ljIj48c2FtbDpBdHRyaWJ1dGVWYWx1ZSB4bWxuczp4c2k9Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvWE1MU2NoZW1hLWluc3RhbmNlIiB4c2k6dHlwZT0ieHM6c3RyaW5nIj5Sb3NzPC9zYW1sOkF0dHJpYnV0ZVZhbHVlPjwvc2FtbDpBdHRyaWJ1dGU+PC9zYW1sOkF0dHJpYnV0ZVN0YXRlbWVudD48L3NhbWw6QXNzZXJ0aW9uPjwvc2FtbHA6UmVzcG9uc2U+Cg==saml-0.4.6/testdata/TestXswPermutationSevenIsRejected_response000066400000000000000000000205541415467341100247570ustar00rootroot00000000000000PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPHNhbWxwOlJlc3BvbnNlIHhtbG5zOnNhbWw9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphc3NlcnRpb24iIHhtbG5zOnNhbWxwPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6cHJvdG9jb2wiIERlc3RpbmF0aW9uPSJodHRwOi8vc3AuZXhhbXBsZS5jb20vZGVtbzEvaW5kZXgucGhwP2FjcyIgSUQ9Il84ZThkYzVmNjlhOThjYzRjMWZmMzQyN2U1Y2UzNDYwNmZkNjcyZjkxZTYiIEluUmVzcG9uc2VUbz0iT05FTE9HSU5fNGZlZTNiMDQ2Mzk1YzRlNzUxMDExZTk3Zjg5MDBiNTI3M2Q1NjY4NSIgSXNzdWVJbnN0YW50PSIyMDE0LTA3LTE3VDAxOjAxOjQ4WiIgVmVyc2lvbj0iMi4wIj48c2FtbDpJc3N1ZXI+aHR0cDovL2lkcC5leGFtcGxlLmNvbS9tZXRhZGF0YS5waHA8L3NhbWw6SXNzdWVyPjxzYW1scDpTdGF0dXM+PHNhbWxwOlN0YXR1c0NvZGUgVmFsdWU9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpzdGF0dXM6U3VjY2VzcyIvPjwvc2FtbHA6U3RhdHVzPjxFeHRlbnNpb25zPjxzYW1sOkFzc2VydGlvbiB4bWxuczp4cz0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEiIHhtbG5zOnhzaT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEtaW5zdGFuY2UiIElEPSJwZngwNDY5MDBjNS0wNDIzLTM1Y2ItMmFkYi03MjI4M2JhNWQ4Y2QiIElzc3VlSW5zdGFudD0iMjAxNC0wNy0xN1QwMTowMTo0OFoiIFZlcnNpb249IjIuMCI+PHNhbWw6SXNzdWVyPmh0dHA6Ly9pZHAuZXhhbXBsZS5jb20vbWV0YWRhdGEucGhwPC9zYW1sOklzc3Vlcj48c2FtbDpTdWJqZWN0PjxzYW1sOk5hbWVJRCBGb3JtYXQ9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpuYW1laWQtZm9ybWF0OnRyYW5zaWVudCIgU1BOYW1lUXVhbGlmaWVyPSJodHRwOi8vc3AuZXhhbXBsZS5jb20vZGVtbzEvbWV0YWRhdGEucGhwIj5fY2UzZDI5NDhiNGNmMjAxNDZkZWUwYTBiM2RkNmY2OWI2Y2Y4NmY2MmQ3PC9zYW1sOk5hbWVJRD48c2FtbDpTdWJqZWN0Q29uZmlybWF0aW9uIE1ldGhvZD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmNtOmJlYXJlciI+PHNhbWw6U3ViamVjdENvbmZpcm1hdGlvbkRhdGEgSW5SZXNwb25zZVRvPSJPTkVMT0dJTl80ZmVlM2IwNDYzOTVjNGU3NTEwMTFlOTdmODkwMGI1MjczZDU2Njg1IiBOb3RPbk9yQWZ0ZXI9IjIwMjQtMDEtMThUMDY6MjE6NDhaIiBSZWNpcGllbnQ9Imh0dHA6Ly9zcC5leGFtcGxlLmNvbS9kZW1vMS9pbmRleC5waHA/YWNzIi8+PC9zYW1sOlN1YmplY3RDb25maXJtYXRpb24+PC9zYW1sOlN1YmplY3Q+PHNhbWw6Q29uZGl0aW9ucyBOb3RCZWZvcmU9IjIwMTQtMDctMTdUMDE6MDE6MThaIiBOb3RPbk9yQWZ0ZXI9IjIwMjQtMDEtMThUMDY6MjE6NDhaIj48c2FtbDpBdWRpZW5jZVJlc3RyaWN0aW9uPjxzYW1sOkF1ZGllbmNlPmh0dHA6Ly9zcC5leGFtcGxlLmNvbS9kZW1vMS9tZXRhZGF0YS5waHA8L3NhbWw6QXVkaWVuY2U+PC9zYW1sOkF1ZGllbmNlUmVzdHJpY3Rpb24+PC9zYW1sOkNvbmRpdGlvbnM+PHNhbWw6QXV0aG5TdGF0ZW1lbnQgQXV0aG5JbnN0YW50PSIyMDE0LTA3LTE3VDAxOjAxOjQ4WiIgU2Vzc2lvbkluZGV4PSJfYmU5OTY3YWJkOTA0ZGRjYWUzYzBlYjQxODlhZGJlM2Y3MWUzMjdjZjkzIiBTZXNzaW9uTm90T25PckFmdGVyPSIyMDI0LTA3LTE3VDA5OjAxOjQ4WiI+PHNhbWw6QXV0aG5Db250ZXh0PjxzYW1sOkF1dGhuQ29udGV4dENsYXNzUmVmPnVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphYzpjbGFzc2VzOlBhc3N3b3JkPC9zYW1sOkF1dGhuQ29udGV4dENsYXNzUmVmPjwvc2FtbDpBdXRobkNvbnRleHQ+PC9zYW1sOkF1dGhuU3RhdGVtZW50PjxzYW1sOkF0dHJpYnV0ZVN0YXRlbWVudD48c2FtbDpBdHRyaWJ1dGUgTmFtZT0idWlkIiBOYW1lRm9ybWF0PSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YXR0cm5hbWUtZm9ybWF0OmJhc2ljIj48c2FtbDpBdHRyaWJ1dGVWYWx1ZSB4c2k6dHlwZT0ieHM6c3RyaW5nIj50ZXN0PC9zYW1sOkF0dHJpYnV0ZVZhbHVlPjwvc2FtbDpBdHRyaWJ1dGU+PHNhbWw6QXR0cmlidXRlIE5hbWU9Im1haWwiIE5hbWVGb3JtYXQ9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphdHRybmFtZS1mb3JtYXQ6YmFzaWMiPjxzYW1sOkF0dHJpYnV0ZVZhbHVlIHhzaTp0eXBlPSJ4czpzdHJpbmciPnRlc3RAZXhhbXBsZS5jb208L3NhbWw6QXR0cmlidXRlVmFsdWU+PC9zYW1sOkF0dHJpYnV0ZT48c2FtbDpBdHRyaWJ1dGUgTmFtZT0iZWR1UGVyc29uQWZmaWxpYXRpb24iIE5hbWVGb3JtYXQ9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphdHRybmFtZS1mb3JtYXQ6YmFzaWMiPjxzYW1sOkF0dHJpYnV0ZVZhbHVlIHhzaTp0eXBlPSJ4czpzdHJpbmciPnVzZXJzPC9zYW1sOkF0dHJpYnV0ZVZhbHVlPjxzYW1sOkF0dHJpYnV0ZVZhbHVlIHhzaTp0eXBlPSJ4czpzdHJpbmciPmV4YW1wbGVyb2xlMTwvc2FtbDpBdHRyaWJ1dGVWYWx1ZT48L3NhbWw6QXR0cmlidXRlPjwvc2FtbDpBdHRyaWJ1dGVTdGF0ZW1lbnQ+PC9zYW1sOkFzc2VydGlvbj48L0V4dGVuc2lvbnM+PHNhbWw6QXNzZXJ0aW9uIHhtbG5zOnhzPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxL1hNTFNjaGVtYSIgeG1sbnM6eHNpPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxL1hNTFNjaGVtYS1pbnN0YW5jZSIgSUQ9InBmeDA0NjkwMGM1LTA0MjMtMzVjYi0yYWRiLTcyMjgzYmE1ZDhjZCIgSXNzdWVJbnN0YW50PSIyMDE0LTA3LTE3VDAxOjAxOjQ4WiIgVmVyc2lvbj0iMi4wIj48c2FtbDpJc3N1ZXI+aHR0cDovL2lkcC5leGFtcGxlLmNvbS9tZXRhZGF0YS5waHA8L3NhbWw6SXNzdWVyPjxkczpTaWduYXR1cmUgeG1sbnM6ZHM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvMDkveG1sZHNpZyMiPjxkczpTaWduZWRJbmZvPjxkczpDYW5vbmljYWxpemF0aW9uTWV0aG9kIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS8xMC94bWwtZXhjLWMxNG4jIi8+PGRzOlNpZ25hdHVyZU1ldGhvZCBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvMDkveG1sZHNpZyNyc2Etc2hhMSIvPjxkczpSZWZlcmVuY2UgVVJJPSIjcGZ4MDQ2OTAwYzUtMDQyMy0zNWNiLTJhZGItNzIyODNiYTVkOGNkIj48ZHM6VHJhbnNmb3Jtcz48ZHM6VHJhbnNmb3JtIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMC8wOS94bWxkc2lnI2VudmVsb3BlZC1zaWduYXR1cmUiLz48ZHM6VHJhbnNmb3JtIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS8xMC94bWwtZXhjLWMxNG4jIi8+PC9kczpUcmFuc2Zvcm1zPjxkczpEaWdlc3RNZXRob2QgQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwLzA5L3htbGRzaWcjc2hhMSIvPjxkczpEaWdlc3RWYWx1ZT5iZXlmcUg5czFTKzZsMkdCSGJTbFc4VHhLNkU9PC9kczpEaWdlc3RWYWx1ZT48L2RzOlJlZmVyZW5jZT48L2RzOlNpZ25lZEluZm8+PGRzOlNpZ25hdHVyZVZhbHVlPkNKQkxjSlVOb3VDSmxjd3lhS1NvVEZ0clRhUk5RYmdYckVRR0pOZmx2MmRqTHQzcnR3aStHNkx3dVBmRCtyQXlveUhtcXJReVNpUlpnWU15Y3VuTy81RDZHYnllWElWM2tzT3djRitBeVZka2tuVWlxU3dINy85cmR2RWFma0pwNDd3WlgrNzh2UUYwNk1yMWc0Smw4MHJOY0RSdzF4T0V1b1A3akMyNW0xUT08L2RzOlNpZ25hdHVyZVZhbHVlPjxkczpLZXlJbmZvPjxkczpYNTA5RGF0YT48ZHM6WDUwOUNlcnRpZmljYXRlPk1JSUNhakNDQWRPZ0F3SUJBZ0lCQURBTkJna3Foa2lHOXcwQkFRMEZBREJTTVFzd0NRWURWUVFHRXdKMWN6RVRNQkVHQTFVRUNBd0tRMkZzYVdadmNtNXBZVEVWTUJNR0ExVUVDZ3dNVDI1bGJHOW5hVzRnU1c1ak1SY3dGUVlEVlFRRERBNXpjQzVsZUdGdGNHeGxMbU52YlRBZUZ3MHhOREEzTVRjeE5ERXlOVFphRncweE5UQTNNVGN4TkRFeU5UWmFNRkl4Q3pBSkJnTlZCQVlUQW5Wek1STXdFUVlEVlFRSURBcERZV3hwWm05eWJtbGhNUlV3RXdZRFZRUUtEQXhQYm1Wc2IyZHBiaUJKYm1NeEZ6QVZCZ05WQkFNTURuTndMbVY0WVcxd2JHVXVZMjl0TUlHZk1BMEdDU3FHU0liM0RRRUJBUVVBQTRHTkFEQ0JpUUtCZ1FEWngrT040SVVvSVd4Z3VrVGIxdE9pWDNiTVl6WVFpd1dQVU5NcCtGcTgyeG9Ob2dzbzJieWtaRzB5aUptNW84enYvc2Q2cEdvdWF5TWdreC8yRlNPZGMzNlQwakdiQ0h1UlNidGlhMFBFek5JUnRtVmlNcnQzQWVvV0JpZFJYbVpzeENOTHdnSVY2ZG4yV3B1RTVBejBiSGdwWm5ReFRLRmVrMEJNS1UvZDh3SURBUUFCbzFBd1RqQWRCZ05WSFE0RUZnUVVHSHhZcVpZeVg3Y1R4S1ZPRFZnWndTVGRDbnd3SHdZRFZSMGpCQmd3Rm9BVUdIeFlxWll5WDdjVHhLVk9EVmdad1NUZENud3dEQVlEVlIwVEJBVXdBd0VCL3pBTkJna3Foa2lHOXcwQkFRMEZBQU9CZ1FCeUZPbCtoTUZJQ2JkM0RKZm5wMlJnZC9kcXR0c1pHL3R5aElMV3ZFcmJpby9ERWU5OG1YcG93aFRrQzA0RU5wck95WGk3WmJVcWlpY0Y4OXVBR3l0MW9xZ1RVQ0QxVnNMYWhxSWNtcnpndW1OeVR3TEdXbzE3V0RBYTEvdXNEaGV0V0FNaGd6Ri9DbmY1ZWswbkswMG0wWVpHeWM0THpnRDBDUk9NQVNUV05nPT08L2RzOlg1MDlDZXJ0aWZpY2F0ZT48L2RzOlg1MDlEYXRhPjwvZHM6S2V5SW5mbz48L2RzOlNpZ25hdHVyZT48c2FtbDpTdWJqZWN0PjxzYW1sOk5hbWVJRCBGb3JtYXQ9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpuYW1laWQtZm9ybWF0OnRyYW5zaWVudCIgU1BOYW1lUXVhbGlmaWVyPSJodHRwOi8vc3AuZXhhbXBsZS5jb20vZGVtbzEvbWV0YWRhdGEucGhwIj5fY2UzZDI5NDhiNGNmMjAxNDZkZWUwYTBiM2RkNmY2OWI2Y2Y4NmY2MmQ3PC9zYW1sOk5hbWVJRD48c2FtbDpTdWJqZWN0Q29uZmlybWF0aW9uIE1ldGhvZD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmNtOmJlYXJlciI+PHNhbWw6U3ViamVjdENvbmZpcm1hdGlvbkRhdGEgSW5SZXNwb25zZVRvPSJPTkVMT0dJTl80ZmVlM2IwNDYzOTVjNGU3NTEwMTFlOTdmODkwMGI1MjczZDU2Njg1IiBOb3RPbk9yQWZ0ZXI9IjIwMjQtMDEtMThUMDY6MjE6NDhaIiBSZWNpcGllbnQ9Imh0dHA6Ly9zcC5leGFtcGxlLmNvbS9kZW1vMS9pbmRleC5waHA/YWNzIi8+PC9zYW1sOlN1YmplY3RDb25maXJtYXRpb24+PC9zYW1sOlN1YmplY3Q+PHNhbWw6Q29uZGl0aW9ucyBOb3RCZWZvcmU9IjIwMTQtMDctMTdUMDE6MDE6MThaIiBOb3RPbk9yQWZ0ZXI9IjIwMjQtMDEtMThUMDY6MjE6NDhaIj48c2FtbDpBdWRpZW5jZVJlc3RyaWN0aW9uPjxzYW1sOkF1ZGllbmNlPmh0dHA6Ly9zcC5leGFtcGxlLmNvbS9kZW1vMS9tZXRhZGF0YS5waHA8L3NhbWw6QXVkaWVuY2U+PC9zYW1sOkF1ZGllbmNlUmVzdHJpY3Rpb24+PC9zYW1sOkNvbmRpdGlvbnM+PHNhbWw6QXV0aG5TdGF0ZW1lbnQgQXV0aG5JbnN0YW50PSIyMDE0LTA3LTE3VDAxOjAxOjQ4WiIgU2Vzc2lvbkluZGV4PSJfYmU5OTY3YWJkOTA0ZGRjYWUzYzBlYjQxODlhZGJlM2Y3MWUzMjdjZjkzIiBTZXNzaW9uTm90T25PckFmdGVyPSIyMDI0LTA3LTE3VDA5OjAxOjQ4WiI+PHNhbWw6QXV0aG5Db250ZXh0PjxzYW1sOkF1dGhuQ29udGV4dENsYXNzUmVmPnVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphYzpjbGFzc2VzOlBhc3N3b3JkPC9zYW1sOkF1dGhuQ29udGV4dENsYXNzUmVmPjwvc2FtbDpBdXRobkNvbnRleHQ+PC9zYW1sOkF1dGhuU3RhdGVtZW50PjxzYW1sOkF0dHJpYnV0ZVN0YXRlbWVudD48c2FtbDpBdHRyaWJ1dGUgTmFtZT0idWlkIiBOYW1lRm9ybWF0PSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YXR0cm5hbWUtZm9ybWF0OmJhc2ljIj48c2FtbDpBdHRyaWJ1dGVWYWx1ZSB4c2k6dHlwZT0ieHM6c3RyaW5nIj50ZXN0PC9zYW1sOkF0dHJpYnV0ZVZhbHVlPjwvc2FtbDpBdHRyaWJ1dGU+PHNhbWw6QXR0cmlidXRlIE5hbWU9Im1haWwiIE5hbWVGb3JtYXQ9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphdHRybmFtZS1mb3JtYXQ6YmFzaWMiPjxzYW1sOkF0dHJpYnV0ZVZhbHVlIHhzaTp0eXBlPSJ4czpzdHJpbmciPnRlc3RAZXhhbXBsZS5jb208L3NhbWw6QXR0cmlidXRlVmFsdWU+PC9zYW1sOkF0dHJpYnV0ZT48c2FtbDpBdHRyaWJ1dGUgTmFtZT0iZWR1UGVyc29uQWZmaWxpYXRpb24iIE5hbWVGb3JtYXQ9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphdHRybmFtZS1mb3JtYXQ6YmFzaWMiPjxzYW1sOkF0dHJpYnV0ZVZhbHVlIHhzaTp0eXBlPSJ4czpzdHJpbmciPnVzZXJzPC9zYW1sOkF0dHJpYnV0ZVZhbHVlPjxzYW1sOkF0dHJpYnV0ZVZhbHVlIHhzaTp0eXBlPSJ4czpzdHJpbmciPmV4YW1wbGVyb2xlMTwvc2FtbDpBdHRyaWJ1dGVWYWx1ZT48L3NhbWw6QXR0cmlidXRlPjwvc2FtbDpBdHRyaWJ1dGVTdGF0ZW1lbnQ+PC9zYW1sOkFzc2VydGlvbj48L3NhbWxwOlJlc3BvbnNlPgo=saml-0.4.6/testdata/TestXswPermutationSixIsRejected_response000066400000000000000000000204601415467341100244360ustar00rootroot00000000000000PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPHNhbWxwOlJlc3BvbnNlIHhtbG5zOnNhbWw9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphc3NlcnRpb24iIHhtbG5zOnNhbWxwPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6cHJvdG9jb2wiIERlc3RpbmF0aW9uPSJodHRwOi8vc3AuZXhhbXBsZS5jb20vZGVtbzEvaW5kZXgucGhwP2FjcyIgSUQ9Il84ZThkYzVmNjlhOThjYzRjMWZmMzQyN2U1Y2UzNDYwNmZkNjcyZjkxZTYiIEluUmVzcG9uc2VUbz0iT05FTE9HSU5fNGZlZTNiMDQ2Mzk1YzRlNzUxMDExZTk3Zjg5MDBiNTI3M2Q1NjY4NSIgSXNzdWVJbnN0YW50PSIyMDE0LTA3LTE3VDAxOjAxOjQ4WiIgVmVyc2lvbj0iMi4wIj48c2FtbDpJc3N1ZXI+aHR0cDovL2lkcC5leGFtcGxlLmNvbS9tZXRhZGF0YS5waHA8L3NhbWw6SXNzdWVyPjxzYW1scDpTdGF0dXM+PHNhbWxwOlN0YXR1c0NvZGUgVmFsdWU9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpzdGF0dXM6U3VjY2VzcyIvPjwvc2FtbHA6U3RhdHVzPjxzYW1sOkFzc2VydGlvbiB4bWxuczp4cz0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEiIHhtbG5zOnhzaT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEtaW5zdGFuY2UiIElEPSJfZXZpbF9hc3NlcnRpb25fSUQiIElzc3VlSW5zdGFudD0iMjAxNC0wNy0xN1QwMTowMTo0OFoiIFZlcnNpb249IjIuMCI+PHNhbWw6SXNzdWVyPmh0dHA6Ly9pZHAuZXhhbXBsZS5jb20vbWV0YWRhdGEucGhwPC9zYW1sOklzc3Vlcj48ZHM6U2lnbmF0dXJlIHhtbG5zOmRzPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwLzA5L3htbGRzaWcjIj48ZHM6U2lnbmVkSW5mbz48ZHM6Q2Fub25pY2FsaXphdGlvbk1ldGhvZCBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvMTAveG1sLWV4Yy1jMTRuIyIvPjxkczpTaWduYXR1cmVNZXRob2QgQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwLzA5L3htbGRzaWcjcnNhLXNoYTEiLz48ZHM6UmVmZXJlbmNlIFVSST0iI3BmeDA0NjkwMGM1LTA0MjMtMzVjYi0yYWRiLTcyMjgzYmE1ZDhjZCI+PGRzOlRyYW5zZm9ybXM+PGRzOlRyYW5zZm9ybSBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvMDkveG1sZHNpZyNlbnZlbG9wZWQtc2lnbmF0dXJlIi8+PGRzOlRyYW5zZm9ybSBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvMTAveG1sLWV4Yy1jMTRuIyIvPjwvZHM6VHJhbnNmb3Jtcz48ZHM6RGlnZXN0TWV0aG9kIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMC8wOS94bWxkc2lnI3NoYTEiLz48ZHM6RGlnZXN0VmFsdWU+YmV5ZnFIOXMxUys2bDJHQkhiU2xXOFR4SzZFPTwvZHM6RGlnZXN0VmFsdWU+PC9kczpSZWZlcmVuY2U+PC9kczpTaWduZWRJbmZvPjxkczpTaWduYXR1cmVWYWx1ZT5DSkJMY0pVTm91Q0psY3d5YUtTb1RGdHJUYVJOUWJnWHJFUUdKTmZsdjJkakx0M3J0d2krRzZMd3VQZkQrckF5b3lIbXFyUXlTaVJaZ1lNeWN1bk8vNUQ2R2J5ZVhJVjNrc093Y0YrQXlWZGtrblVpcVN3SDcvOXJkdkVhZmtKcDQ3d1pYKzc4dlFGMDZNcjFnNEpsODByTmNEUncxeE9FdW9QN2pDMjVtMVE9PC9kczpTaWduYXR1cmVWYWx1ZT48ZHM6S2V5SW5mbz48ZHM6WDUwOURhdGE+PGRzOlg1MDlDZXJ0aWZpY2F0ZT5NSUlDYWpDQ0FkT2dBd0lCQWdJQkFEQU5CZ2txaGtpRzl3MEJBUTBGQURCU01Rc3dDUVlEVlFRR0V3SjFjekVUTUJFR0ExVUVDQXdLUTJGc2FXWnZjbTVwWVRFVk1CTUdBMVVFQ2d3TVQyNWxiRzluYVc0Z1NXNWpNUmN3RlFZRFZRUUREQTV6Y0M1bGVHRnRjR3hsTG1OdmJUQWVGdzB4TkRBM01UY3hOREV5TlRaYUZ3MHhOVEEzTVRjeE5ERXlOVFphTUZJeEN6QUpCZ05WQkFZVEFuVnpNUk13RVFZRFZRUUlEQXBEWVd4cFptOXlibWxoTVJVd0V3WURWUVFLREF4UGJtVnNiMmRwYmlCSmJtTXhGekFWQmdOVkJBTU1Ebk53TG1WNFlXMXdiR1V1WTI5dE1JR2ZNQTBHQ1NxR1NJYjNEUUVCQVFVQUE0R05BRENCaVFLQmdRRFp4K09ONElVb0lXeGd1a1RiMXRPaVgzYk1ZellRaXdXUFVOTXArRnE4MnhvTm9nc28yYnlrWkcweWlKbTVvOHp2L3NkNnBHb3VheU1na3gvMkZTT2RjMzZUMGpHYkNIdVJTYnRpYTBQRXpOSVJ0bVZpTXJ0M0Flb1dCaWRSWG1ac3hDTkx3Z0lWNmRuMldwdUU1QXowYkhncFpuUXhUS0ZlazBCTUtVL2Q4d0lEQVFBQm8xQXdUakFkQmdOVkhRNEVGZ1FVR0h4WXFaWXlYN2NUeEtWT0RWZ1p3U1RkQ253d0h3WURWUjBqQkJnd0ZvQVVHSHhZcVpZeVg3Y1R4S1ZPRFZnWndTVGRDbnd3REFZRFZSMFRCQVV3QXdFQi96QU5CZ2txaGtpRzl3MEJBUTBGQUFPQmdRQnlGT2wraE1GSUNiZDNESmZucDJSZ2QvZHF0dHNaRy90eWhJTFd2RXJiaW8vREVlOThtWHBvd2hUa0MwNEVOcHJPeVhpN1piVXFpaWNGODl1QUd5dDFvcWdUVUNEMVZzTGFocUljbXJ6Z3VtTnlUd0xHV28xN1dEQWExL3VzRGhldFdBTWhnekYvQ25mNWVrMG5LMDBtMFlaR3ljNEx6Z0QwQ1JPTUFTVFdOZz09PC9kczpYNTA5Q2VydGlmaWNhdGU+PC9kczpYNTA5RGF0YT48L2RzOktleUluZm8+PHNhbWw6QXNzZXJ0aW9uIHhtbG5zOnhzPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxL1hNTFNjaGVtYSIgeG1sbnM6eHNpPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxL1hNTFNjaGVtYS1pbnN0YW5jZSIgSUQ9InBmeDA0NjkwMGM1LTA0MjMtMzVjYi0yYWRiLTcyMjgzYmE1ZDhjZCIgSXNzdWVJbnN0YW50PSIyMDE0LTA3LTE3VDAxOjAxOjQ4WiIgVmVyc2lvbj0iMi4wIj48c2FtbDpJc3N1ZXI+aHR0cDovL2lkcC5leGFtcGxlLmNvbS9tZXRhZGF0YS5waHA8L3NhbWw6SXNzdWVyPjxzYW1sOlN1YmplY3Q+PHNhbWw6TmFtZUlEIEZvcm1hdD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOm5hbWVpZC1mb3JtYXQ6dHJhbnNpZW50IiBTUE5hbWVRdWFsaWZpZXI9Imh0dHA6Ly9zcC5leGFtcGxlLmNvbS9kZW1vMS9tZXRhZGF0YS5waHAiPl9jZTNkMjk0OGI0Y2YyMDE0NmRlZTBhMGIzZGQ2ZjY5YjZjZjg2ZjYyZDc8L3NhbWw6TmFtZUlEPjxzYW1sOlN1YmplY3RDb25maXJtYXRpb24gTWV0aG9kPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6Y206YmVhcmVyIj48c2FtbDpTdWJqZWN0Q29uZmlybWF0aW9uRGF0YSBJblJlc3BvbnNlVG89Ik9ORUxPR0lOXzRmZWUzYjA0NjM5NWM0ZTc1MTAxMWU5N2Y4OTAwYjUyNzNkNTY2ODUiIE5vdE9uT3JBZnRlcj0iMjAyNC0wMS0xOFQwNjoyMTo0OFoiIFJlY2lwaWVudD0iaHR0cDovL3NwLmV4YW1wbGUuY29tL2RlbW8xL2luZGV4LnBocD9hY3MiLz48L3NhbWw6U3ViamVjdENvbmZpcm1hdGlvbj48L3NhbWw6U3ViamVjdD48c2FtbDpDb25kaXRpb25zIE5vdEJlZm9yZT0iMjAxNC0wNy0xN1QwMTowMToxOFoiIE5vdE9uT3JBZnRlcj0iMjAyNC0wMS0xOFQwNjoyMTo0OFoiPjxzYW1sOkF1ZGllbmNlUmVzdHJpY3Rpb24+PHNhbWw6QXVkaWVuY2U+aHR0cDovL3NwLmV4YW1wbGUuY29tL2RlbW8xL21ldGFkYXRhLnBocDwvc2FtbDpBdWRpZW5jZT48L3NhbWw6QXVkaWVuY2VSZXN0cmljdGlvbj48L3NhbWw6Q29uZGl0aW9ucz48c2FtbDpBdXRoblN0YXRlbWVudCBBdXRobkluc3RhbnQ9IjIwMTQtMDctMTdUMDE6MDE6NDhaIiBTZXNzaW9uSW5kZXg9Il9iZTk5NjdhYmQ5MDRkZGNhZTNjMGViNDE4OWFkYmUzZjcxZTMyN2NmOTMiIFNlc3Npb25Ob3RPbk9yQWZ0ZXI9IjIwMjQtMDctMTdUMDk6MDE6NDhaIj48c2FtbDpBdXRobkNvbnRleHQ+PHNhbWw6QXV0aG5Db250ZXh0Q2xhc3NSZWY+dXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmFjOmNsYXNzZXM6UGFzc3dvcmQ8L3NhbWw6QXV0aG5Db250ZXh0Q2xhc3NSZWY+PC9zYW1sOkF1dGhuQ29udGV4dD48L3NhbWw6QXV0aG5TdGF0ZW1lbnQ+PHNhbWw6QXR0cmlidXRlU3RhdGVtZW50PjxzYW1sOkF0dHJpYnV0ZSBOYW1lPSJ1aWQiIE5hbWVGb3JtYXQ9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphdHRybmFtZS1mb3JtYXQ6YmFzaWMiPjxzYW1sOkF0dHJpYnV0ZVZhbHVlIHhzaTp0eXBlPSJ4czpzdHJpbmciPnRlc3Q8L3NhbWw6QXR0cmlidXRlVmFsdWU+PC9zYW1sOkF0dHJpYnV0ZT48c2FtbDpBdHRyaWJ1dGUgTmFtZT0ibWFpbCIgTmFtZUZvcm1hdD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmF0dHJuYW1lLWZvcm1hdDpiYXNpYyI+PHNhbWw6QXR0cmlidXRlVmFsdWUgeHNpOnR5cGU9InhzOnN0cmluZyI+dGVzdEBleGFtcGxlLmNvbTwvc2FtbDpBdHRyaWJ1dGVWYWx1ZT48L3NhbWw6QXR0cmlidXRlPjxzYW1sOkF0dHJpYnV0ZSBOYW1lPSJlZHVQZXJzb25BZmZpbGlhdGlvbiIgTmFtZUZvcm1hdD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmF0dHJuYW1lLWZvcm1hdDpiYXNpYyI+PHNhbWw6QXR0cmlidXRlVmFsdWUgeHNpOnR5cGU9InhzOnN0cmluZyI+dXNlcnM8L3NhbWw6QXR0cmlidXRlVmFsdWU+PHNhbWw6QXR0cmlidXRlVmFsdWUgeHNpOnR5cGU9InhzOnN0cmluZyI+ZXhhbXBsZXJvbGUxPC9zYW1sOkF0dHJpYnV0ZVZhbHVlPjwvc2FtbDpBdHRyaWJ1dGU+PC9zYW1sOkF0dHJpYnV0ZVN0YXRlbWVudD48L3NhbWw6QXNzZXJ0aW9uPjwvZHM6U2lnbmF0dXJlPjxzYW1sOlN1YmplY3Q+PHNhbWw6TmFtZUlEIEZvcm1hdD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOm5hbWVpZC1mb3JtYXQ6dHJhbnNpZW50IiBTUE5hbWVRdWFsaWZpZXI9Imh0dHA6Ly9zcC5leGFtcGxlLmNvbS9kZW1vMS9tZXRhZGF0YS5waHAiPl9jZTNkMjk0OGI0Y2YyMDE0NmRlZTBhMGIzZGQ2ZjY5YjZjZjg2ZjYyZDc8L3NhbWw6TmFtZUlEPjxzYW1sOlN1YmplY3RDb25maXJtYXRpb24gTWV0aG9kPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6Y206YmVhcmVyIj48c2FtbDpTdWJqZWN0Q29uZmlybWF0aW9uRGF0YSBJblJlc3BvbnNlVG89Ik9ORUxPR0lOXzRmZWUzYjA0NjM5NWM0ZTc1MTAxMWU5N2Y4OTAwYjUyNzNkNTY2ODUiIE5vdE9uT3JBZnRlcj0iMjAyNC0wMS0xOFQwNjoyMTo0OFoiIFJlY2lwaWVudD0iaHR0cDovL3NwLmV4YW1wbGUuY29tL2RlbW8xL2luZGV4LnBocD9hY3MiLz48L3NhbWw6U3ViamVjdENvbmZpcm1hdGlvbj48L3NhbWw6U3ViamVjdD48c2FtbDpDb25kaXRpb25zIE5vdEJlZm9yZT0iMjAxNC0wNy0xN1QwMTowMToxOFoiIE5vdE9uT3JBZnRlcj0iMjAyNC0wMS0xOFQwNjoyMTo0OFoiPjxzYW1sOkF1ZGllbmNlUmVzdHJpY3Rpb24+PHNhbWw6QXVkaWVuY2U+aHR0cDovL3NwLmV4YW1wbGUuY29tL2RlbW8xL21ldGFkYXRhLnBocDwvc2FtbDpBdWRpZW5jZT48L3NhbWw6QXVkaWVuY2VSZXN0cmljdGlvbj48L3NhbWw6Q29uZGl0aW9ucz48c2FtbDpBdXRoblN0YXRlbWVudCBBdXRobkluc3RhbnQ9IjIwMTQtMDctMTdUMDE6MDE6NDhaIiBTZXNzaW9uSW5kZXg9Il9iZTk5NjdhYmQ5MDRkZGNhZTNjMGViNDE4OWFkYmUzZjcxZTMyN2NmOTMiIFNlc3Npb25Ob3RPbk9yQWZ0ZXI9IjIwMjQtMDctMTdUMDk6MDE6NDhaIj48c2FtbDpBdXRobkNvbnRleHQ+PHNhbWw6QXV0aG5Db250ZXh0Q2xhc3NSZWY+dXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmFjOmNsYXNzZXM6UGFzc3dvcmQ8L3NhbWw6QXV0aG5Db250ZXh0Q2xhc3NSZWY+PC9zYW1sOkF1dGhuQ29udGV4dD48L3NhbWw6QXV0aG5TdGF0ZW1lbnQ+PHNhbWw6QXR0cmlidXRlU3RhdGVtZW50PjxzYW1sOkF0dHJpYnV0ZSBOYW1lPSJ1aWQiIE5hbWVGb3JtYXQ9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphdHRybmFtZS1mb3JtYXQ6YmFzaWMiPjxzYW1sOkF0dHJpYnV0ZVZhbHVlIHhzaTp0eXBlPSJ4czpzdHJpbmciPnRlc3Q8L3NhbWw6QXR0cmlidXRlVmFsdWU+PC9zYW1sOkF0dHJpYnV0ZT48c2FtbDpBdHRyaWJ1dGUgTmFtZT0ibWFpbCIgTmFtZUZvcm1hdD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmF0dHJuYW1lLWZvcm1hdDpiYXNpYyI+PHNhbWw6QXR0cmlidXRlVmFsdWUgeHNpOnR5cGU9InhzOnN0cmluZyI+dGVzdEBleGFtcGxlLmNvbTwvc2FtbDpBdHRyaWJ1dGVWYWx1ZT48L3NhbWw6QXR0cmlidXRlPjxzYW1sOkF0dHJpYnV0ZSBOYW1lPSJlZHVQZXJzb25BZmZpbGlhdGlvbiIgTmFtZUZvcm1hdD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmF0dHJuYW1lLWZvcm1hdDpiYXNpYyI+PHNhbWw6QXR0cmlidXRlVmFsdWUgeHNpOnR5cGU9InhzOnN0cmluZyI+dXNlcnM8L3NhbWw6QXR0cmlidXRlVmFsdWU+PHNhbWw6QXR0cmlidXRlVmFsdWUgeHNpOnR5cGU9InhzOnN0cmluZyI+ZXhhbXBsZXJvbGUxPC9zYW1sOkF0dHJpYnV0ZVZhbHVlPjwvc2FtbDpBdHRyaWJ1dGU+PC9zYW1sOkF0dHJpYnV0ZVN0YXRlbWVudD48L3NhbWw6QXNzZXJ0aW9uPjwvc2FtbHA6UmVzcG9uc2U+Cg==saml-0.4.6/testdata/TestXswPermutationThreeIsRejected_response000066400000000000000000000204601415467341100247420ustar00rootroot00000000000000PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPHNhbWxwOlJlc3BvbnNlIHhtbG5zOnNhbWw9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphc3NlcnRpb24iIHhtbG5zOnNhbWxwPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6cHJvdG9jb2wiIERlc3RpbmF0aW9uPSJodHRwOi8vc3AuZXhhbXBsZS5jb20vZGVtbzEvaW5kZXgucGhwP2FjcyIgSUQ9Il84ZThkYzVmNjlhOThjYzRjMWZmMzQyN2U1Y2UzNDYwNmZkNjcyZjkxZTYiIEluUmVzcG9uc2VUbz0iT05FTE9HSU5fNGZlZTNiMDQ2Mzk1YzRlNzUxMDExZTk3Zjg5MDBiNTI3M2Q1NjY4NSIgSXNzdWVJbnN0YW50PSIyMDE0LTA3LTE3VDAxOjAxOjQ4WiIgVmVyc2lvbj0iMi4wIj48c2FtbDpJc3N1ZXI+aHR0cDovL2lkcC5leGFtcGxlLmNvbS9tZXRhZGF0YS5waHA8L3NhbWw6SXNzdWVyPjxzYW1scDpTdGF0dXM+PHNhbWxwOlN0YXR1c0NvZGUgVmFsdWU9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpzdGF0dXM6U3VjY2VzcyIvPjwvc2FtbHA6U3RhdHVzPjxzYW1sOkFzc2VydGlvbiB4bWxuczp4cz0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEiIHhtbG5zOnhzaT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEtaW5zdGFuY2UiIElEPSJfZXZpbF9hc3NlcnRpb25fSUQiIElzc3VlSW5zdGFudD0iMjAxNC0wNy0xN1QwMTowMTo0OFoiIFZlcnNpb249IjIuMCI+PHNhbWw6SXNzdWVyPmh0dHA6Ly9pZHAuZXhhbXBsZS5jb20vbWV0YWRhdGEucGhwPC9zYW1sOklzc3Vlcj48c2FtbDpTdWJqZWN0PjxzYW1sOk5hbWVJRCBGb3JtYXQ9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpuYW1laWQtZm9ybWF0OnRyYW5zaWVudCIgU1BOYW1lUXVhbGlmaWVyPSJodHRwOi8vc3AuZXhhbXBsZS5jb20vZGVtbzEvbWV0YWRhdGEucGhwIj5fY2UzZDI5NDhiNGNmMjAxNDZkZWUwYTBiM2RkNmY2OWI2Y2Y4NmY2MmQ3PC9zYW1sOk5hbWVJRD48c2FtbDpTdWJqZWN0Q29uZmlybWF0aW9uIE1ldGhvZD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmNtOmJlYXJlciI+PHNhbWw6U3ViamVjdENvbmZpcm1hdGlvbkRhdGEgSW5SZXNwb25zZVRvPSJPTkVMT0dJTl80ZmVlM2IwNDYzOTVjNGU3NTEwMTFlOTdmODkwMGI1MjczZDU2Njg1IiBOb3RPbk9yQWZ0ZXI9IjIwMjQtMDEtMThUMDY6MjE6NDhaIiBSZWNpcGllbnQ9Imh0dHA6Ly9zcC5leGFtcGxlLmNvbS9kZW1vMS9pbmRleC5waHA/YWNzIi8+PC9zYW1sOlN1YmplY3RDb25maXJtYXRpb24+PC9zYW1sOlN1YmplY3Q+PHNhbWw6Q29uZGl0aW9ucyBOb3RCZWZvcmU9IjIwMTQtMDctMTdUMDE6MDE6MThaIiBOb3RPbk9yQWZ0ZXI9IjIwMjQtMDEtMThUMDY6MjE6NDhaIj48c2FtbDpBdWRpZW5jZVJlc3RyaWN0aW9uPjxzYW1sOkF1ZGllbmNlPmh0dHA6Ly9zcC5leGFtcGxlLmNvbS9kZW1vMS9tZXRhZGF0YS5waHA8L3NhbWw6QXVkaWVuY2U+PC9zYW1sOkF1ZGllbmNlUmVzdHJpY3Rpb24+PC9zYW1sOkNvbmRpdGlvbnM+PHNhbWw6QXV0aG5TdGF0ZW1lbnQgQXV0aG5JbnN0YW50PSIyMDE0LTA3LTE3VDAxOjAxOjQ4WiIgU2Vzc2lvbkluZGV4PSJfYmU5OTY3YWJkOTA0ZGRjYWUzYzBlYjQxODlhZGJlM2Y3MWUzMjdjZjkzIiBTZXNzaW9uTm90T25PckFmdGVyPSIyMDI0LTA3LTE3VDA5OjAxOjQ4WiI+PHNhbWw6QXV0aG5Db250ZXh0PjxzYW1sOkF1dGhuQ29udGV4dENsYXNzUmVmPnVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphYzpjbGFzc2VzOlBhc3N3b3JkPC9zYW1sOkF1dGhuQ29udGV4dENsYXNzUmVmPjwvc2FtbDpBdXRobkNvbnRleHQ+PC9zYW1sOkF1dGhuU3RhdGVtZW50PjxzYW1sOkF0dHJpYnV0ZVN0YXRlbWVudD48c2FtbDpBdHRyaWJ1dGUgTmFtZT0idWlkIiBOYW1lRm9ybWF0PSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YXR0cm5hbWUtZm9ybWF0OmJhc2ljIj48c2FtbDpBdHRyaWJ1dGVWYWx1ZSB4c2k6dHlwZT0ieHM6c3RyaW5nIj50ZXN0PC9zYW1sOkF0dHJpYnV0ZVZhbHVlPjwvc2FtbDpBdHRyaWJ1dGU+PHNhbWw6QXR0cmlidXRlIE5hbWU9Im1haWwiIE5hbWVGb3JtYXQ9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphdHRybmFtZS1mb3JtYXQ6YmFzaWMiPjxzYW1sOkF0dHJpYnV0ZVZhbHVlIHhzaTp0eXBlPSJ4czpzdHJpbmciPnRlc3RAZXhhbXBsZS5jb208L3NhbWw6QXR0cmlidXRlVmFsdWU+PC9zYW1sOkF0dHJpYnV0ZT48c2FtbDpBdHRyaWJ1dGUgTmFtZT0iZWR1UGVyc29uQWZmaWxpYXRpb24iIE5hbWVGb3JtYXQ9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphdHRybmFtZS1mb3JtYXQ6YmFzaWMiPjxzYW1sOkF0dHJpYnV0ZVZhbHVlIHhzaTp0eXBlPSJ4czpzdHJpbmciPnVzZXJzPC9zYW1sOkF0dHJpYnV0ZVZhbHVlPjxzYW1sOkF0dHJpYnV0ZVZhbHVlIHhzaTp0eXBlPSJ4czpzdHJpbmciPmV4YW1wbGVyb2xlMTwvc2FtbDpBdHRyaWJ1dGVWYWx1ZT48L3NhbWw6QXR0cmlidXRlPjwvc2FtbDpBdHRyaWJ1dGVTdGF0ZW1lbnQ+PC9zYW1sOkFzc2VydGlvbj48c2FtbDpBc3NlcnRpb24geG1sbnM6eHM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvWE1MU2NoZW1hIiB4bWxuczp4c2k9Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvWE1MU2NoZW1hLWluc3RhbmNlIiBJRD0icGZ4MDQ2OTAwYzUtMDQyMy0zNWNiLTJhZGItNzIyODNiYTVkOGNkIiBJc3N1ZUluc3RhbnQ9IjIwMTQtMDctMTdUMDE6MDE6NDhaIiBWZXJzaW9uPSIyLjAiPjxzYW1sOklzc3Vlcj5odHRwOi8vaWRwLmV4YW1wbGUuY29tL21ldGFkYXRhLnBocDwvc2FtbDpJc3N1ZXI+PGRzOlNpZ25hdHVyZSB4bWxuczpkcz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC8wOS94bWxkc2lnIyI+PGRzOlNpZ25lZEluZm8+PGRzOkNhbm9uaWNhbGl6YXRpb25NZXRob2QgQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzEwL3htbC1leGMtYzE0biMiLz48ZHM6U2lnbmF0dXJlTWV0aG9kIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMC8wOS94bWxkc2lnI3JzYS1zaGExIi8+PGRzOlJlZmVyZW5jZSBVUkk9IiNwZngwNDY5MDBjNS0wNDIzLTM1Y2ItMmFkYi03MjI4M2JhNWQ4Y2QiPjxkczpUcmFuc2Zvcm1zPjxkczpUcmFuc2Zvcm0gQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwLzA5L3htbGRzaWcjZW52ZWxvcGVkLXNpZ25hdHVyZSIvPjxkczpUcmFuc2Zvcm0gQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzEwL3htbC1leGMtYzE0biMiLz48L2RzOlRyYW5zZm9ybXM+PGRzOkRpZ2VzdE1ldGhvZCBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvMDkveG1sZHNpZyNzaGExIi8+PGRzOkRpZ2VzdFZhbHVlPmJleWZxSDlzMVMrNmwyR0JIYlNsVzhUeEs2RT08L2RzOkRpZ2VzdFZhbHVlPjwvZHM6UmVmZXJlbmNlPjwvZHM6U2lnbmVkSW5mbz48ZHM6U2lnbmF0dXJlVmFsdWU+Q0pCTGNKVU5vdUNKbGN3eWFLU29URnRyVGFSTlFiZ1hyRVFHSk5mbHYyZGpMdDNydHdpK0c2THd1UGZEK3JBeW95SG1xclF5U2lSWmdZTXljdW5PLzVENkdieWVYSVYza3NPd2NGK0F5VmRra25VaXFTd0g3LzlyZHZFYWZrSnA0N3daWCs3OHZRRjA2TXIxZzRKbDgwck5jRFJ3MXhPRXVvUDdqQzI1bTFRPTwvZHM6U2lnbmF0dXJlVmFsdWU+PGRzOktleUluZm8+PGRzOlg1MDlEYXRhPjxkczpYNTA5Q2VydGlmaWNhdGU+TUlJQ2FqQ0NBZE9nQXdJQkFnSUJBREFOQmdrcWhraUc5dzBCQVEwRkFEQlNNUXN3Q1FZRFZRUUdFd0oxY3pFVE1CRUdBMVVFQ0F3S1EyRnNhV1p2Y201cFlURVZNQk1HQTFVRUNnd01UMjVsYkc5bmFXNGdTVzVqTVJjd0ZRWURWUVFEREE1emNDNWxlR0Z0Y0d4bExtTnZiVEFlRncweE5EQTNNVGN4TkRFeU5UWmFGdzB4TlRBM01UY3hOREV5TlRaYU1GSXhDekFKQmdOVkJBWVRBblZ6TVJNd0VRWURWUVFJREFwRFlXeHBabTl5Ym1saE1SVXdFd1lEVlFRS0RBeFBibVZzYjJkcGJpQkpibU14RnpBVkJnTlZCQU1NRG5Od0xtVjRZVzF3YkdVdVkyOXRNSUdmTUEwR0NTcUdTSWIzRFFFQkFRVUFBNEdOQURDQmlRS0JnUURaeCtPTjRJVW9JV3hndWtUYjF0T2lYM2JNWXpZUWl3V1BVTk1wK0ZxODJ4b05vZ3NvMmJ5a1pHMHlpSm01bzh6di9zZDZwR291YXlNZ2t4LzJGU09kYzM2VDBqR2JDSHVSU2J0aWEwUEV6TklSdG1WaU1ydDNBZW9XQmlkUlhtWnN4Q05Md2dJVjZkbjJXcHVFNUF6MGJIZ3BablF4VEtGZWswQk1LVS9kOHdJREFRQUJvMUF3VGpBZEJnTlZIUTRFRmdRVUdIeFlxWll5WDdjVHhLVk9EVmdad1NUZENud3dId1lEVlIwakJCZ3dGb0FVR0h4WXFaWXlYN2NUeEtWT0RWZ1p3U1RkQ253d0RBWURWUjBUQkFVd0F3RUIvekFOQmdrcWhraUc5dzBCQVEwRkFBT0JnUUJ5Rk9sK2hNRklDYmQzREpmbnAyUmdkL2RxdHRzWkcvdHloSUxXdkVyYmlvL0RFZTk4bVhwb3doVGtDMDRFTnByT3lYaTdaYlVxaWljRjg5dUFHeXQxb3FnVFVDRDFWc0xhaHFJY21yemd1bU55VHdMR1dvMTdXREFhMS91c0RoZXRXQU1oZ3pGL0NuZjVlazBuSzAwbTBZWkd5YzRMemdEMENST01BU1RXTmc9PTwvZHM6WDUwOUNlcnRpZmljYXRlPjwvZHM6WDUwOURhdGE+PC9kczpLZXlJbmZvPjwvZHM6U2lnbmF0dXJlPjxzYW1sOlN1YmplY3Q+PHNhbWw6TmFtZUlEIEZvcm1hdD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOm5hbWVpZC1mb3JtYXQ6dHJhbnNpZW50IiBTUE5hbWVRdWFsaWZpZXI9Imh0dHA6Ly9zcC5leGFtcGxlLmNvbS9kZW1vMS9tZXRhZGF0YS5waHAiPl9jZTNkMjk0OGI0Y2YyMDE0NmRlZTBhMGIzZGQ2ZjY5YjZjZjg2ZjYyZDc8L3NhbWw6TmFtZUlEPjxzYW1sOlN1YmplY3RDb25maXJtYXRpb24gTWV0aG9kPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6Y206YmVhcmVyIj48c2FtbDpTdWJqZWN0Q29uZmlybWF0aW9uRGF0YSBJblJlc3BvbnNlVG89Ik9ORUxPR0lOXzRmZWUzYjA0NjM5NWM0ZTc1MTAxMWU5N2Y4OTAwYjUyNzNkNTY2ODUiIE5vdE9uT3JBZnRlcj0iMjAyNC0wMS0xOFQwNjoyMTo0OFoiIFJlY2lwaWVudD0iaHR0cDovL3NwLmV4YW1wbGUuY29tL2RlbW8xL2luZGV4LnBocD9hY3MiLz48L3NhbWw6U3ViamVjdENvbmZpcm1hdGlvbj48L3NhbWw6U3ViamVjdD48c2FtbDpDb25kaXRpb25zIE5vdEJlZm9yZT0iMjAxNC0wNy0xN1QwMTowMToxOFoiIE5vdE9uT3JBZnRlcj0iMjAyNC0wMS0xOFQwNjoyMTo0OFoiPjxzYW1sOkF1ZGllbmNlUmVzdHJpY3Rpb24+PHNhbWw6QXVkaWVuY2U+aHR0cDovL3NwLmV4YW1wbGUuY29tL2RlbW8xL21ldGFkYXRhLnBocDwvc2FtbDpBdWRpZW5jZT48L3NhbWw6QXVkaWVuY2VSZXN0cmljdGlvbj48L3NhbWw6Q29uZGl0aW9ucz48c2FtbDpBdXRoblN0YXRlbWVudCBBdXRobkluc3RhbnQ9IjIwMTQtMDctMTdUMDE6MDE6NDhaIiBTZXNzaW9uSW5kZXg9Il9iZTk5NjdhYmQ5MDRkZGNhZTNjMGViNDE4OWFkYmUzZjcxZTMyN2NmOTMiIFNlc3Npb25Ob3RPbk9yQWZ0ZXI9IjIwMjQtMDctMTdUMDk6MDE6NDhaIj48c2FtbDpBdXRobkNvbnRleHQ+PHNhbWw6QXV0aG5Db250ZXh0Q2xhc3NSZWY+dXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmFjOmNsYXNzZXM6UGFzc3dvcmQ8L3NhbWw6QXV0aG5Db250ZXh0Q2xhc3NSZWY+PC9zYW1sOkF1dGhuQ29udGV4dD48L3NhbWw6QXV0aG5TdGF0ZW1lbnQ+PHNhbWw6QXR0cmlidXRlU3RhdGVtZW50PjxzYW1sOkF0dHJpYnV0ZSBOYW1lPSJ1aWQiIE5hbWVGb3JtYXQ9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphdHRybmFtZS1mb3JtYXQ6YmFzaWMiPjxzYW1sOkF0dHJpYnV0ZVZhbHVlIHhzaTp0eXBlPSJ4czpzdHJpbmciPnRlc3Q8L3NhbWw6QXR0cmlidXRlVmFsdWU+PC9zYW1sOkF0dHJpYnV0ZT48c2FtbDpBdHRyaWJ1dGUgTmFtZT0ibWFpbCIgTmFtZUZvcm1hdD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmF0dHJuYW1lLWZvcm1hdDpiYXNpYyI+PHNhbWw6QXR0cmlidXRlVmFsdWUgeHNpOnR5cGU9InhzOnN0cmluZyI+dGVzdEBleGFtcGxlLmNvbTwvc2FtbDpBdHRyaWJ1dGVWYWx1ZT48L3NhbWw6QXR0cmlidXRlPjxzYW1sOkF0dHJpYnV0ZSBOYW1lPSJlZHVQZXJzb25BZmZpbGlhdGlvbiIgTmFtZUZvcm1hdD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmF0dHJuYW1lLWZvcm1hdDpiYXNpYyI+PHNhbWw6QXR0cmlidXRlVmFsdWUgeHNpOnR5cGU9InhzOnN0cmluZyI+dXNlcnM8L3NhbWw6QXR0cmlidXRlVmFsdWU+PHNhbWw6QXR0cmlidXRlVmFsdWUgeHNpOnR5cGU9InhzOnN0cmluZyI+ZXhhbXBsZXJvbGUxPC9zYW1sOkF0dHJpYnV0ZVZhbHVlPjwvc2FtbDpBdHRyaWJ1dGU+PC9zYW1sOkF0dHJpYnV0ZVN0YXRlbWVudD48L3NhbWw6QXNzZXJ0aW9uPjwvc2FtbHA6UmVzcG9uc2U+Cg==saml-0.4.6/testdata/TestXswPermutationTwoIsRejected_response000066400000000000000000000262741415467341100244550ustar00rootroot00000000000000PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPHNhbWxwOlJlc3BvbnNlIHhtbG5zOnNhbWw9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphc3NlcnRpb24iIHhtbG5zOnNhbWxwPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6cHJvdG9jb2wiIERlc3RpbmF0aW9uPSJodHRwczovLzI5ZWU2ZDJlLm5ncm9rLmlvL3NhbWwvYWNzIiBJRD0iX2V2aWxfcmVzcG9uc2VfSUQiIEluUmVzcG9uc2VUbz0iaWQtZDQwYzE1YzEwNGI1MjY5MWVjY2YwYTJhNWM4YTE1NTk1YmU3NTQyMyIgSXNzdWVJbnN0YW50PSIyMDE2LTAxLTA1VDE3OjUzOjExWiIgVmVyc2lvbj0iMi4wIj48c2FtbDpJc3N1ZXI+aHR0cHM6Ly9hcHAub25lbG9naW4uY29tL3NhbWwvbWV0YWRhdGEvNTAzOTgzPC9zYW1sOklzc3Vlcj48c2FtbHA6UmVzcG9uc2UgeG1sbnM6c2FtbD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmFzc2VydGlvbiIgeG1sbnM6c2FtbHA9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpwcm90b2NvbCIgRGVzdGluYXRpb249Imh0dHBzOi8vMjllZTZkMmUubmdyb2suaW8vc2FtbC9hY3MiIElEPSJwZnhlZDg4YzQzZC02NTA0LWUxZjEtNWFmMC00MGJlN2YyNzlmYzUiIEluUmVzcG9uc2VUbz0iaWQtZDQwYzE1YzEwNGI1MjY5MWVjY2YwYTJhNWM4YTE1NTk1YmU3NTQyMyIgSXNzdWVJbnN0YW50PSIyMDE2LTAxLTA1VDE3OjUzOjExWiIgVmVyc2lvbj0iMi4wIj48c2FtbDpJc3N1ZXI+aHR0cHM6Ly9hcHAub25lbG9naW4uY29tL3NhbWwvbWV0YWRhdGEvNTAzOTgzPC9zYW1sOklzc3Vlcj48c2FtbHA6U3RhdHVzPjxzYW1scDpTdGF0dXNDb2RlIFZhbHVlPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6c3RhdHVzOlN1Y2Nlc3MiLz48L3NhbWxwOlN0YXR1cz48c2FtbDpBc3NlcnRpb24geG1sbnM6c2FtbD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmFzc2VydGlvbiIgeG1sbnM6eHM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvWE1MU2NoZW1hIiB4bWxuczp4c2k9Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvWE1MU2NoZW1hLWluc3RhbmNlIiBJRD0iQWQ5NDVhZWRhMzhhNTA4ZjhmYWM5YmM5NjEzZDU5NjQyYzBkMmQ4Y2IiIElzc3VlSW5zdGFudD0iMjAxNi0wMS0wNVQxNzo1MzoxMVoiIFZlcnNpb249IjIuMCI+PHNhbWw6SXNzdWVyPmh0dHBzOi8vYXBwLm9uZWxvZ2luLmNvbS9zYW1sL21ldGFkYXRhLzUwMzk4Mzwvc2FtbDpJc3N1ZXI+PHNhbWw6U3ViamVjdD48c2FtbDpOYW1lSUQgRm9ybWF0PSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoxLjE6bmFtZWlkLWZvcm1hdDplbWFpbEFkZHJlc3MiPnJvc3NAa25kci5vcmc8L3NhbWw6TmFtZUlEPjxzYW1sOlN1YmplY3RDb25maXJtYXRpb24gTWV0aG9kPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6Y206YmVhcmVyIj48c2FtbDpTdWJqZWN0Q29uZmlybWF0aW9uRGF0YSBJblJlc3BvbnNlVG89ImlkLWQ0MGMxNWMxMDRiNTI2OTFlY2NmMGEyYTVjOGExNTU5NWJlNzU0MjMiIE5vdE9uT3JBZnRlcj0iMjAxNi0wMS0wNVQxNzo1NjoxMVoiIFJlY2lwaWVudD0iaHR0cHM6Ly8yOWVlNmQyZS5uZ3Jvay5pby9zYW1sL2FjcyIvPjwvc2FtbDpTdWJqZWN0Q29uZmlybWF0aW9uPjwvc2FtbDpTdWJqZWN0PjxzYW1sOkNvbmRpdGlvbnMgTm90QmVmb3JlPSIyMDE2LTAxLTA1VDE3OjUwOjExWiIgTm90T25PckFmdGVyPSIyMDE2LTAxLTA1VDE3OjU2OjExWiI+PHNhbWw6QXVkaWVuY2VSZXN0cmljdGlvbj48c2FtbDpBdWRpZW5jZT5odHRwczovLzI5ZWU2ZDJlLm5ncm9rLmlvL3NhbWwvbWV0YWRhdGE8L3NhbWw6QXVkaWVuY2U+PC9zYW1sOkF1ZGllbmNlUmVzdHJpY3Rpb24+PC9zYW1sOkNvbmRpdGlvbnM+PHNhbWw6QXV0aG5TdGF0ZW1lbnQgQXV0aG5JbnN0YW50PSIyMDE2LTAxLTA1VDE3OjUzOjEwWiIgU2Vzc2lvbkluZGV4PSJfZWJkY2JlODAtOTVmZi0wMTMzLWQ4NzEtMzhjYTNhNjYyZjFjIiBTZXNzaW9uTm90T25PckFmdGVyPSIyMDE2LTAxLTA2VDE3OjUzOjExWiI+PHNhbWw6QXV0aG5Db250ZXh0PjxzYW1sOkF1dGhuQ29udGV4dENsYXNzUmVmPnVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphYzpjbGFzc2VzOlBhc3N3b3JkUHJvdGVjdGVkVHJhbnNwb3J0PC9zYW1sOkF1dGhuQ29udGV4dENsYXNzUmVmPjwvc2FtbDpBdXRobkNvbnRleHQ+PC9zYW1sOkF1dGhuU3RhdGVtZW50PjxzYW1sOkF0dHJpYnV0ZVN0YXRlbWVudD48c2FtbDpBdHRyaWJ1dGUgTmFtZT0iVXNlci5lbWFpbCIgTmFtZUZvcm1hdD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmF0dHJuYW1lLWZvcm1hdDpiYXNpYyI+PHNhbWw6QXR0cmlidXRlVmFsdWUgeG1sbnM6eHNpPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxL1hNTFNjaGVtYS1pbnN0YW5jZSIgeHNpOnR5cGU9InhzOnN0cmluZyI+cm9zc0BrbmRyLm9yZzwvc2FtbDpBdHRyaWJ1dGVWYWx1ZT48L3NhbWw6QXR0cmlidXRlPjxzYW1sOkF0dHJpYnV0ZSBOYW1lPSJtZW1iZXJPZiIgTmFtZUZvcm1hdD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmF0dHJuYW1lLWZvcm1hdDpiYXNpYyI+PHNhbWw6QXR0cmlidXRlVmFsdWUgeG1sbnM6eHNpPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxL1hNTFNjaGVtYS1pbnN0YW5jZSIgeHNpOnR5cGU9InhzOnN0cmluZyIvPjwvc2FtbDpBdHRyaWJ1dGU+PHNhbWw6QXR0cmlidXRlIE5hbWU9IlVzZXIuTGFzdE5hbWUiIE5hbWVGb3JtYXQ9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphdHRybmFtZS1mb3JtYXQ6YmFzaWMiPjxzYW1sOkF0dHJpYnV0ZVZhbHVlIHhtbG5zOnhzaT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEtaW5zdGFuY2UiIHhzaTp0eXBlPSJ4czpzdHJpbmciPktpbmRlcjwvc2FtbDpBdHRyaWJ1dGVWYWx1ZT48L3NhbWw6QXR0cmlidXRlPjxzYW1sOkF0dHJpYnV0ZSBOYW1lPSJQZXJzb25JbW11dGFibGVJRCIgTmFtZUZvcm1hdD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmF0dHJuYW1lLWZvcm1hdDpiYXNpYyI+PHNhbWw6QXR0cmlidXRlVmFsdWUgeG1sbnM6eHNpPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxL1hNTFNjaGVtYS1pbnN0YW5jZSIgeHNpOnR5cGU9InhzOnN0cmluZyIvPjwvc2FtbDpBdHRyaWJ1dGU+PHNhbWw6QXR0cmlidXRlIE5hbWU9IlVzZXIuRmlyc3ROYW1lIiBOYW1lRm9ybWF0PSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YXR0cm5hbWUtZm9ybWF0OmJhc2ljIj48c2FtbDpBdHRyaWJ1dGVWYWx1ZSB4bWxuczp4c2k9Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvWE1MU2NoZW1hLWluc3RhbmNlIiB4c2k6dHlwZT0ieHM6c3RyaW5nIj5Sb3NzPC9zYW1sOkF0dHJpYnV0ZVZhbHVlPjwvc2FtbDpBdHRyaWJ1dGU+PC9zYW1sOkF0dHJpYnV0ZVN0YXRlbWVudD48L3NhbWw6QXNzZXJ0aW9uPjwvc2FtbHA6UmVzcG9uc2U+PGRzOlNpZ25hdHVyZSB4bWxuczpkcz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC8wOS94bWxkc2lnIyI+PGRzOlNpZ25lZEluZm8+PGRzOkNhbm9uaWNhbGl6YXRpb25NZXRob2QgQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzEwL3htbC1leGMtYzE0biMiLz48ZHM6U2lnbmF0dXJlTWV0aG9kIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMC8wOS94bWxkc2lnI3JzYS1zaGExIi8+PGRzOlJlZmVyZW5jZSBVUkk9IiNwZnhlZDg4YzQzZC02NTA0LWUxZjEtNWFmMC00MGJlN2YyNzlmYzUiPjxkczpUcmFuc2Zvcm1zPjxkczpUcmFuc2Zvcm0gQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwLzA5L3htbGRzaWcjZW52ZWxvcGVkLXNpZ25hdHVyZSIvPjxkczpUcmFuc2Zvcm0gQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzEwL3htbC1leGMtYzE0biMiLz48L2RzOlRyYW5zZm9ybXM+PGRzOkRpZ2VzdE1ldGhvZCBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvMDkveG1sZHNpZyNzaGExIi8+PGRzOkRpZ2VzdFZhbHVlPlNWQWFRZzh2bW1TUUw2L1lCbVMyeWRLUlA3ST08L2RzOkRpZ2VzdFZhbHVlPjwvZHM6UmVmZXJlbmNlPjwvZHM6U2lnbmVkSW5mbz48ZHM6U2lnbmF0dXJlVmFsdWU+c0JlVFZQMGJab1BSK2JmeUFrVnY2STNDVjdZOFhxbkoycjhmMStXbXIyZ0ZnblJGODVOdnZTUCtyMUJvN250dU9zd080ZkI0Uks0SHlTYnlsZzRiS0hLSDE5WDkxaFZBekpTeXNmbVMvZDV3ZzFDZmlXV3Q1UzJIQTUwOHRoWHVabndHM1h6NktuV0s4a1JkeDFkYytZUldnYUZ5ZDRnTEc5YUJUc1hPWjd2eC83UDRicnpORW00d1A5LzB0dWZ4Rytuc1k2RHB3bkVHQ2psK1ZVS3BnekVxd05OalFxWUZZU0FYRWsrVnQrWDNjMmQwSElyWlF2WW5OaDAyS3h1d1ZCVGhuM01helFOYU54Qy9zeWYza0RRQ1JyWkNZbytZdER1ZHpKVTlwM0EwWVhIVFFjc2RldHNIWlhDTWozbXV2emMwbUVCbHc0TGJjaEttbmJ5Wm1nPT08L2RzOlNpZ25hdHVyZVZhbHVlPjxkczpLZXlJbmZvPjxkczpYNTA5RGF0YT48ZHM6WDUwOUNlcnRpZmljYXRlPk1JSUVDRENDQXZDZ0F3SUJBZ0lVWHVuMDhDc2xMUldTTHFObkRFMU50R0plZmwwd0RRWUpLb1pJaHZjTkFRRUZCUUF3VXpFTE1Ba0dBMVVFQmhNQ1ZWTXhEREFLQmdOVkJBb01BMk4wZFRFVk1CTUdBMVVFQ3d3TVQyNWxURzluYVc0Z1NXUlFNUjh3SFFZRFZRUUREQlpQYm1WTWIyZHBiaUJCWTJOdmRXNTBJRE15TmpFME1CNFhEVEV6TURrek1ERTVNelUwTkZvWERURTRNVEF3TVRFNU16VTBORm93VXpFTE1Ba0dBMVVFQmhNQ1ZWTXhEREFLQmdOVkJBb01BMk4wZFRFVk1CTUdBMVVFQ3d3TVQyNWxURzluYVc0Z1NXUlFNUjh3SFFZRFZRUUREQlpQYm1WTWIyZHBiaUJCWTJOdmRXNTBJRE15TmpFME1JSUJJakFOQmdrcWhraUc5dzBCQVFFRkFBT0NBUThBTUlJQkNnS0NBUUVBME9HOFY4bWhvdmtqNHJoR2hqcmJFeFJZYnpLVjJaeGZ2R2ZFR1hHVXZYYzZEcWVqWUVkaFoybUlmQ0RvamhRamswQnl3aWlyQUtNT3QxR051SDdhV0lFNDdEMGV3dEs1eWxFQW03ZVZtb1k0a3hMQ2FXNXdZckMxU3pNbnBlaXRVeHF2c2JuS3ozalVLWUhSZ2dwZnZWajRzaUhEWmVJWmE5YTVyVXZwTW5uYk9vRmlaQ0lFTnBxM1RDMzNpdk9TWmhFTlJUem12bms1R0RvTEh3LzhxQWdRaXlUM0QxeENrU0JiNTRQSGdrUTVScTFvZExNL2hKK0wwanpDVVFINGd4cFdsRUFhYjRLOXM4ZnBCVUJCaDVnbUpDWWk4VWJJbGhxTzhOMm15bnVtMzNCVS92SjNQbmF3VDRZWWtUd1JVeDZZKzNmcG1SQkhxbDRoODNTTWV3SURBUUFCbzRIVE1JSFFNQXdHQTFVZEV3RUIvd1FDTUFBd0hRWURWUjBPQkJZRUZPZkZGakhGajlhNnhwbmdiMTFycmhnTWU5QXJNSUdRQmdOVkhTTUVnWWd3Z1lXQUZPZkZGakhGajlhNnhwbmdiMTFycmhnTWU5QXJvVmVrVlRCVE1Rc3dDUVlEVlFRR0V3SlZVekVNTUFvR0ExVUVDZ3dEWTNSMU1SVXdFd1lEVlFRTERBeFBibVZNYjJkcGJpQkpaRkF4SHpBZEJnTlZCQU1NRms5dVpVeHZaMmx1SUVGalkyOTFiblFnTXpJMk1UU0NGRjdwOVBBckpTMFZraTZqWnd4TlRiUmlYbjVkTUE0R0ExVWREd0VCL3dRRUF3SUhnREFOQmdrcWhraUc5dzBCQVFVRkFBT0NBUUVBTWdsbjROUE1RbjhHeXZxOENUUCtjMmU2Q1V6Y3ZSRUtuVGhqeFQ5V2N2VjFaVlhNQk5QbTRjVHFUMzYxRWRMelk1eVdMVVdYZDRBdkZuY2lxQjNNSFlhMm5xVG1udkxnbWhrV2UraGRGb05lNStJQThBeEduK25xVUlTbXlCZUN4dVVVQWJSTXVvd2lBcndISXB6cEV5UklZZFNaUk5GMGR2Z2lQWXlyL01pUFhJY3pwSDVuTGt2YkxwY0FGK1I4Wmg5bndZMGcxSlZ5YzZBQjZqN1lleHVVUVpwSEg0czBWZHgvbldtcmNGZUxaS0NUeGNhaEh2VTUwZTF5S1g1dGhmVmFKcUk4UVE3eFp4eXUwVFRzaWFYMHV3NTFKUE96UHVBUHBoMHo2eG9TOW9ZeHV6WjF5OXNOSEg2a0g4R0ZudlMyTXF5SGlOejBoMFNxL3E2bit3PT08L2RzOlg1MDlDZXJ0aWZpY2F0ZT48L2RzOlg1MDlEYXRhPjwvZHM6S2V5SW5mbz48L2RzOlNpZ25hdHVyZT48c2FtbHA6U3RhdHVzPjxzYW1scDpTdGF0dXNDb2RlIFZhbHVlPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6c3RhdHVzOlN1Y2Nlc3MiLz48L3NhbWxwOlN0YXR1cz48c2FtbDpBc3NlcnRpb24geG1sbnM6c2FtbD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmFzc2VydGlvbiIgeG1sbnM6eHM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvWE1MU2NoZW1hIiB4bWxuczp4c2k9Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvWE1MU2NoZW1hLWluc3RhbmNlIiBJRD0iQWQ5NDVhZWRhMzhhNTA4ZjhmYWM5YmM5NjEzZDU5NjQyYzBkMmQ4Y2IiIElzc3VlSW5zdGFudD0iMjAxNi0wMS0wNVQxNzo1MzoxMVoiIFZlcnNpb249IjIuMCI+PHNhbWw6SXNzdWVyPmh0dHBzOi8vYXBwLm9uZWxvZ2luLmNvbS9zYW1sL21ldGFkYXRhLzUwMzk4Mzwvc2FtbDpJc3N1ZXI+PHNhbWw6U3ViamVjdD48c2FtbDpOYW1lSUQgRm9ybWF0PSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoxLjE6bmFtZWlkLWZvcm1hdDplbWFpbEFkZHJlc3MiPnJvc3NAa25kci5vcmc8L3NhbWw6TmFtZUlEPjxzYW1sOlN1YmplY3RDb25maXJtYXRpb24gTWV0aG9kPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6Y206YmVhcmVyIj48c2FtbDpTdWJqZWN0Q29uZmlybWF0aW9uRGF0YSBJblJlc3BvbnNlVG89ImlkLWQ0MGMxNWMxMDRiNTI2OTFlY2NmMGEyYTVjOGExNTU5NWJlNzU0MjMiIE5vdE9uT3JBZnRlcj0iMjAxNi0wMS0wNVQxNzo1NjoxMVoiIFJlY2lwaWVudD0iaHR0cHM6Ly8yOWVlNmQyZS5uZ3Jvay5pby9zYW1sL2FjcyIvPjwvc2FtbDpTdWJqZWN0Q29uZmlybWF0aW9uPjwvc2FtbDpTdWJqZWN0PjxzYW1sOkNvbmRpdGlvbnMgTm90QmVmb3JlPSIyMDE2LTAxLTA1VDE3OjUwOjExWiIgTm90T25PckFmdGVyPSIyMDE2LTAxLTA1VDE3OjU2OjExWiI+PHNhbWw6QXVkaWVuY2VSZXN0cmljdGlvbj48c2FtbDpBdWRpZW5jZT5odHRwczovLzI5ZWU2ZDJlLm5ncm9rLmlvL3NhbWwvbWV0YWRhdGE8L3NhbWw6QXVkaWVuY2U+PC9zYW1sOkF1ZGllbmNlUmVzdHJpY3Rpb24+PC9zYW1sOkNvbmRpdGlvbnM+PHNhbWw6QXV0aG5TdGF0ZW1lbnQgQXV0aG5JbnN0YW50PSIyMDE2LTAxLTA1VDE3OjUzOjEwWiIgU2Vzc2lvbkluZGV4PSJfZWJkY2JlODAtOTVmZi0wMTMzLWQ4NzEtMzhjYTNhNjYyZjFjIiBTZXNzaW9uTm90T25PckFmdGVyPSIyMDE2LTAxLTA2VDE3OjUzOjExWiI+PHNhbWw6QXV0aG5Db250ZXh0PjxzYW1sOkF1dGhuQ29udGV4dENsYXNzUmVmPnVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphYzpjbGFzc2VzOlBhc3N3b3JkUHJvdGVjdGVkVHJhbnNwb3J0PC9zYW1sOkF1dGhuQ29udGV4dENsYXNzUmVmPjwvc2FtbDpBdXRobkNvbnRleHQ+PC9zYW1sOkF1dGhuU3RhdGVtZW50PjxzYW1sOkF0dHJpYnV0ZVN0YXRlbWVudD48c2FtbDpBdHRyaWJ1dGUgTmFtZT0iVXNlci5lbWFpbCIgTmFtZUZvcm1hdD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmF0dHJuYW1lLWZvcm1hdDpiYXNpYyI+PHNhbWw6QXR0cmlidXRlVmFsdWUgeG1sbnM6eHNpPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxL1hNTFNjaGVtYS1pbnN0YW5jZSIgeHNpOnR5cGU9InhzOnN0cmluZyI+cm9zc0BrbmRyLm9yZzwvc2FtbDpBdHRyaWJ1dGVWYWx1ZT48L3NhbWw6QXR0cmlidXRlPjxzYW1sOkF0dHJpYnV0ZSBOYW1lPSJtZW1iZXJPZiIgTmFtZUZvcm1hdD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmF0dHJuYW1lLWZvcm1hdDpiYXNpYyI+PHNhbWw6QXR0cmlidXRlVmFsdWUgeG1sbnM6eHNpPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxL1hNTFNjaGVtYS1pbnN0YW5jZSIgeHNpOnR5cGU9InhzOnN0cmluZyIvPjwvc2FtbDpBdHRyaWJ1dGU+PHNhbWw6QXR0cmlidXRlIE5hbWU9IlVzZXIuTGFzdE5hbWUiIE5hbWVGb3JtYXQ9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphdHRybmFtZS1mb3JtYXQ6YmFzaWMiPjxzYW1sOkF0dHJpYnV0ZVZhbHVlIHhtbG5zOnhzaT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEtaW5zdGFuY2UiIHhzaTp0eXBlPSJ4czpzdHJpbmciPktpbmRlcjwvc2FtbDpBdHRyaWJ1dGVWYWx1ZT48L3NhbWw6QXR0cmlidXRlPjxzYW1sOkF0dHJpYnV0ZSBOYW1lPSJQZXJzb25JbW11dGFibGVJRCIgTmFtZUZvcm1hdD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmF0dHJuYW1lLWZvcm1hdDpiYXNpYyI+PHNhbWw6QXR0cmlidXRlVmFsdWUgeG1sbnM6eHNpPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxL1hNTFNjaGVtYS1pbnN0YW5jZSIgeHNpOnR5cGU9InhzOnN0cmluZyIvPjwvc2FtbDpBdHRyaWJ1dGU+PHNhbWw6QXR0cmlidXRlIE5hbWU9IlVzZXIuRmlyc3ROYW1lIiBOYW1lRm9ybWF0PSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YXR0cm5hbWUtZm9ybWF0OmJhc2ljIj48c2FtbDpBdHRyaWJ1dGVWYWx1ZSB4bWxuczp4c2k9Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvWE1MU2NoZW1hLWluc3RhbmNlIiB4c2k6dHlwZT0ieHM6c3RyaW5nIj5Sb3NzPC9zYW1sOkF0dHJpYnV0ZVZhbHVlPjwvc2FtbDpBdHRyaWJ1dGU+PC9zYW1sOkF0dHJpYnV0ZVN0YXRlbWVudD48L3NhbWw6QXNzZXJ0aW9uPjwvc2FtbHA6UmVzcG9uc2U+Cg==saml-0.4.6/testdata/cert_2017.pem000066400000000000000000000022541415467341100164100ustar00rootroot00000000000000-----BEGIN CERTIFICATE----- MIIDRTCCAi2gAwIBAgIJANke+OUVRk19MA0GCSqGSIb3DQEBBQUAMCAxHjAcBgNV BAMTFW15c2VydmljZS5leGFtcGxlLmNvbTAeFw0xNzA0MTkxOTU2MTNaFw0xODA0 MTkxOTU2MTNaMCAxHjAcBgNVBAMTFW15c2VydmljZS5leGFtcGxlLmNvbTCCASIw DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJ7tsAJqJ4rUZFT/5SjvoA1mpQBC +KidcehMsTFW/9l73cPnYNhtnNe+9S8n76eF2ASuEwAKN5xoXDP297y4ZOORnkou P8jiYZjqNHTckE9LevM8YOpTPVojhq+rq7hiQyMF6pJkBPgduolcsnBg188jn/oN mdmzIaS33QiNOZ6FGXLKt3/Fapscea0gq8a46wKhFFQmW8i+MGqdSSi255c05ZZL 5H04plDNoGEvES5OsxSjbZ4+294K83nOXoGSEPfsO4XUTORlryis0p/f3aNUgoou eeTphD9Go2pGHYvahjusx9ONbZ7egc07dJoIAVjgaMSv+UwqfpxjAU7018sCAwEA AaOBgTB/MB0GA1UdDgQWBBSYE9Nwp/eUqfRQ11rqwoowNFHNyTBQBgNVHSMESTBH gBSYE9Nwp/eUqfRQ11rqwoowNFHNyaEkpCIwIDEeMBwGA1UEAxMVbXlzZXJ2aWNl LmV4YW1wbGUuY29tggkA2R745RVGTX0wDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0B AQUFAAOCAQEAVJmpMg1ZpDGweoCU4k66RVDpPzSuPJ+9H9L2jcaA38itDtXmG9Iz dbOLpNF9fDbU60P421SgS0nF/s7zkxkYJWOoZaced/vUO6H9TdWEZay+uywAjvoZ GwkZ9HxYMqKMVld4EwW/OwT67UVBdtgkSfI1O7ojqDOFx7U4+HJWxUEwGOc0pOPz NyLSYCsAkQt2CZU7dN72L96Ka8xxklNaVcUaUH+zOWF1JBamV9s6M2umcdBot8MO 3m1zQTkXzBKM3f+Yvk+dRjO4TSW90h2oQqot8xrkPhy+DgOqJj3/lKmZXjqE5mAE hpQB0uVPekPvKN89hCnkPo2EvXKPf7VZgg== -----END CERTIFICATE----- saml-0.4.6/testdata/idp_authn_request.xml000066400000000000000000000012001415467341100205320ustar00rootroot00000000000000https://sp.example.com/saml2/metadatasaml-0.4.6/testdata/idp_cert.pem000066400000000000000000000013351415467341100165720ustar00rootroot00000000000000-----BEGIN CERTIFICATE----- MIIB7zCCAVgCCQDFzbKIp7b3MTANBgkqhkiG9w0BAQUFADA8MQswCQYDVQQGEwJV UzELMAkGA1UECAwCR0ExDDAKBgNVBAoMA2ZvbzESMBAGA1UEAwwJbG9jYWxob3N0 MB4XDTEzMTAwMjAwMDg1MVoXDTE0MTAwMjAwMDg1MVowPDELMAkGA1UEBhMCVVMx CzAJBgNVBAgMAkdBMQwwCgYDVQQKDANmb28xEjAQBgNVBAMMCWxvY2FsaG9zdDCB nzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA1PMHYmhZj308kWLhZVT4vOulqx/9 ibm5B86fPWwUKKQ2i12MYtz07tzukPymisTDhQaqyJ8Kqb/6JjhmeMnEOdTvSPmH O8m1ZVveJU6NoKRn/mP/BD7FW52WhbrUXLSeHVSKfWkNk6S4hk9MV9TswTvyRIKv Rsw0X/gfnqkroJcCAwEAATANBgkqhkiG9w0BAQUFAAOBgQCMMlIO+GNcGekevKgk akpMdAqJfs24maGb90DvTLbRZRD7Xvn1MnVBBS9hzlXiFLYOInXACMW5gcoRFfeT QLSouMM8o57h0uKjfTmuoWHLQLi6hnF+cvCsEFiJZ4AbF+DgmO6TarJ8O05t8zvn OwJlNCASPZRH/JmF8tX0hoHuAQ== -----END CERTIFICATE----- saml-0.4.6/testdata/idp_key.pem000066400000000000000000000015731415467341100164310ustar00rootroot00000000000000-----BEGIN RSA PRIVATE KEY----- MIICXgIBAAKBgQDU8wdiaFmPfTyRYuFlVPi866WrH/2JubkHzp89bBQopDaLXYxi 3PTu3O6Q/KaKxMOFBqrInwqpv/omOGZ4ycQ51O9I+Yc7ybVlW94lTo2gpGf+Y/8E PsVbnZaFutRctJ4dVIp9aQ2TpLiGT0xX1OzBO/JEgq9GzDRf+B+eqSuglwIDAQAB AoGBAMuy1eN6cgFiCOgBsB3gVDdTKpww87Qk5ivjqEt28SmXO13A1KNVPS6oQ8SJ CT5Azc6X/BIAoJCURVL+LHdqebogKljhH/3yIel1kH19vr4E2kTM/tYH+qj8afUS JEmArUzsmmK8ccuNqBcllqdwCZjxL4CHDUmyRudFcHVX9oyhAkEA/OV1OkjM3CLU N3sqELdMmHq5QZCUihBmk3/N5OvGdqAFGBlEeewlepEVxkh7JnaNXAXrKHRVu/f/ fbCQxH+qrwJBANeQERF97b9Sibp9xgolb749UWNlAdqmEpmlvmS202TdcaaT1msU 4rRLiQN3X9O9mq4LZMSVethrQAdX1whawpkCQQDk1yGf7xZpMJ8F4U5sN+F4rLyM Rq8Sy8p2OBTwzCUXXK+fYeXjybsUUMr6VMYTRP2fQr/LKJIX+E5ZxvcIyFmDAkEA yfjNVUNVaIbQTzEbRlRvT6MqR+PTCefC072NF9aJWR93JimspGZMR7viY6IM4lrr vBkm0F5yXKaYtoiiDMzlOQJADqmEwXl0D72ZG/2KDg8b4QZEmC9i5gidpQwJXUc6 hU+IVQoLxRq0fBib/36K9tcrrO5Ba4iEvDcNY+D8yGbUtA== -----END RSA PRIVATE KEY----- saml-0.4.6/testdata/idp_new_session_response000066400000000000000000000012551415467341100213300ustar00rootroot00000000000000RelayState: ThisIsTheRelayState SAMLRequest: https://sp.example.com/saml2/metadatasaml-0.4.6/testdata/key_2017.pem000066400000000000000000000032161415467341100162420ustar00rootroot00000000000000-----BEGIN RSA PRIVATE KEY----- MIIEpAIBAAKCAQEAnu2wAmonitRkVP/lKO+gDWalAEL4qJ1x6EyxMVb/2Xvdw+dg 2G2c1771Lyfvp4XYBK4TAAo3nGhcM/b3vLhk45GeSi4/yOJhmOo0dNyQT0t68zxg 6lM9WiOGr6uruGJDIwXqkmQE+B26iVyycGDXzyOf+g2Z2bMhpLfdCI05noUZcsq3 f8Vqmxx5rSCrxrjrAqEUVCZbyL4wap1JKLbnlzTllkvkfTimUM2gYS8RLk6zFKNt nj7b3grzec5egZIQ9+w7hdRM5GWvKKzSn9/do1SCii555OmEP0ajakYdi9qGO6zH 041tnt6BzTt0mggBWOBoxK/5TCp+nGMBTvTXywIDAQABAoIBAGc853TqGD2qsnI0 uFvbLREHeG+vEXAWtoO8Le5rIU/ZkrlLeDGfIp9TQFodiyQ7YZPIsDb6bB2B/UMU TuGctozNbxGo8W5BAD0hBmpTTLr1wSx4MEyHPfdr1HYRAj+INSxvD22A42l5hk7s lE1D22yHK8h3RVWRc21YspB3jNJXhYq4qHU8ZIOK+I7wEy8AZYP5kI4ZUC/nXFik SwMeVN7U6TT+53Q6n08pos5Nupq0+3cAvZgneV2PP2fGCLIi9VU8Oh7N1WQMYzBt rYQwnWR6e2Mo0uH4eaaBlPW/3t4HXbS1b26RULC+i5MU3ZUy02w76liHGuuKAWOy 6p6CkoECgYEAzpnt/9S+HKknCFwKMkvMxZ7xI3TxyG5gd41NYm+w8hPk3QlXzcva RqM1p2nacok4t8HEeAotkz8sP28GmcB4Egh33fUdhKQAkFrQ/OncWcRfzrhnrAMa gaNT/DT1QTOvfmnle3QLnZhgEDk6plgujvPmJ104/4TjIUOqru0DebsCgYEAxO20 Biq5Bjfot8HxQE+Ur1VPEyZkYUQ9Xmx/exyMTBLy2bUMvZq40TMDEgGMzYvbOvgR d7/1X2SVvl3sl2mlRJ3nfSEO/Blq+EovdOi0liUYo5LT4IGx+uToBlfmTPNrTYBp P7JqH97DKYdM4eujwYkiPaAHkFYsnJi/jEU9cTECgYAssS3D9uB9ULYp38cw5CbS 5TQiyGx5QC9MDVwdHC4538XVbuz4js2UFEBKC+L+feKwFZGLqh/7x2GqAzl5TyJq PDy53glZpSSeFZc57tkE7i8Ph+KdWjqEqrFDUK1xQl4HSZ8j2pGcsNavC8I9M7w2 nlo+T7NByxxbGMk2d/0VewKBgQCJvr7qhV2wRNEqH6VxV3jn/2L1QSh7hLDsaDXv VjOoTqTBtUs5II1f/y+Jm73yVH4/TB9jxMiMNh4r7yS7cDEiwtSWCNajbeAN1k5F lzQhxcbrO5uqcO2eUhkdvsQfVTDcIBL+c/yZWEbouHQFnr6HdDWYJ2TDCBPiYVGy ewgUMQKBgQCIgGzau6+hH8z6aWTnozGA4m8sWFq+t+ug4Gq6v3IAxBOQ2NhmQMRQ L3BkCfCGAx5JckBROqEiAvPLftof0bVJoxBKfslDrhJocEUJwjXUxmD5RRLr7SXU P4hDC736Y0DH3nzRlUZ2IP4mhqSECOEYAuz2VuJBTCbd0VEzpnxVfg== -----END RSA PRIVATE KEY-----saml-0.4.6/testdata/sp_cert.pem000066400000000000000000000013351415467341100164400ustar00rootroot00000000000000-----BEGIN CERTIFICATE----- MIIB7zCCAVgCCQDFzbKIp7b3MTANBgkqhkiG9w0BAQUFADA8MQswCQYDVQQGEwJV UzELMAkGA1UECAwCR0ExDDAKBgNVBAoMA2ZvbzESMBAGA1UEAwwJbG9jYWxob3N0 MB4XDTEzMTAwMjAwMDg1MVoXDTE0MTAwMjAwMDg1MVowPDELMAkGA1UEBhMCVVMx CzAJBgNVBAgMAkdBMQwwCgYDVQQKDANmb28xEjAQBgNVBAMMCWxvY2FsaG9zdDCB nzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA1PMHYmhZj308kWLhZVT4vOulqx/9 ibm5B86fPWwUKKQ2i12MYtz07tzukPymisTDhQaqyJ8Kqb/6JjhmeMnEOdTvSPmH O8m1ZVveJU6NoKRn/mP/BD7FW52WhbrUXLSeHVSKfWkNk6S4hk9MV9TswTvyRIKv Rsw0X/gfnqkroJcCAwEAATANBgkqhkiG9w0BAQUFAAOBgQCMMlIO+GNcGekevKgk akpMdAqJfs24maGb90DvTLbRZRD7Xvn1MnVBBS9hzlXiFLYOInXACMW5gcoRFfeT QLSouMM8o57h0uKjfTmuoWHLQLi6hnF+cvCsEFiJZ4AbF+DgmO6TarJ8O05t8zvn OwJlNCASPZRH/JmF8tX0hoHuAQ== -----END CERTIFICATE----- saml-0.4.6/testdata/sp_key.pem000066400000000000000000000015731415467341100162770ustar00rootroot00000000000000-----BEGIN RSA PRIVATE KEY----- MIICXgIBAAKBgQDU8wdiaFmPfTyRYuFlVPi866WrH/2JubkHzp89bBQopDaLXYxi 3PTu3O6Q/KaKxMOFBqrInwqpv/omOGZ4ycQ51O9I+Yc7ybVlW94lTo2gpGf+Y/8E PsVbnZaFutRctJ4dVIp9aQ2TpLiGT0xX1OzBO/JEgq9GzDRf+B+eqSuglwIDAQAB AoGBAMuy1eN6cgFiCOgBsB3gVDdTKpww87Qk5ivjqEt28SmXO13A1KNVPS6oQ8SJ CT5Azc6X/BIAoJCURVL+LHdqebogKljhH/3yIel1kH19vr4E2kTM/tYH+qj8afUS JEmArUzsmmK8ccuNqBcllqdwCZjxL4CHDUmyRudFcHVX9oyhAkEA/OV1OkjM3CLU N3sqELdMmHq5QZCUihBmk3/N5OvGdqAFGBlEeewlepEVxkh7JnaNXAXrKHRVu/f/ fbCQxH+qrwJBANeQERF97b9Sibp9xgolb749UWNlAdqmEpmlvmS202TdcaaT1msU 4rRLiQN3X9O9mq4LZMSVethrQAdX1whawpkCQQDk1yGf7xZpMJ8F4U5sN+F4rLyM Rq8Sy8p2OBTwzCUXXK+fYeXjybsUUMr6VMYTRP2fQr/LKJIX+E5ZxvcIyFmDAkEA yfjNVUNVaIbQTzEbRlRvT6MqR+PTCefC072NF9aJWR93JimspGZMR7viY6IM4lrr vBkm0F5yXKaYtoiiDMzlOQJADqmEwXl0D72ZG/2KDg8b4QZEmC9i5gidpQwJXUc6 hU+IVQoLxRq0fBib/36K9tcrrO5Ba4iEvDcNY+D8yGbUtA== -----END RSA PRIVATE KEY----- saml-0.4.6/testsaml/000077500000000000000000000000001415467341100143175ustar00rootroot00000000000000saml-0.4.6/testsaml/parse.go000066400000000000000000000021231415467341100157560ustar00rootroot00000000000000package testsaml import ( "bytes" "compress/flate" "encoding/base64" "fmt" "io/ioutil" "net/url" ) // ParseRedirectRequest returns the decoded SAML AuthnRequest from an HTTP-Redirect URL func ParseRedirectRequest(u *url.URL) ([]byte, error) { compressedRequest, err := base64.StdEncoding.DecodeString(u.Query().Get("SAMLRequest")) if err != nil { return nil, fmt.Errorf("cannot decode request: %s", err) } buf, err := ioutil.ReadAll(flate.NewReader(bytes.NewReader(compressedRequest))) if err != nil { return nil, fmt.Errorf("cannot decompress request: %s", err) } return buf, nil } // ParseRedirectResponse returns the decoded SAML LogoutResponse from an HTTP-Redirect URL func ParseRedirectResponse(u *url.URL) ([]byte, error) { compressedResponse, err := base64.StdEncoding.DecodeString(u.Query().Get("SAMLResponse")) if err != nil { return nil, fmt.Errorf("cannot decode response: %s", err) } buf, err := ioutil.ReadAll(flate.NewReader(bytes.NewReader(compressedResponse))) if err != nil { return nil, fmt.Errorf("cannot decompress response: %s", err) } return buf, nil } saml-0.4.6/time.go000066400000000000000000000024051415467341100137510ustar00rootroot00000000000000package saml import "time" // RelaxedTime is a version of time.Time that supports the time format // found in SAML documents. type RelaxedTime time.Time const timeFormat = "2006-01-02T15:04:05.999Z07:00" // MarshalText implements encoding.TextMarshaler func (m RelaxedTime) MarshalText() ([]byte, error) { // According to section 1.2.2 of the OASIS SAML 1.1 spec, we can't trust // other applications to handle time resolution finer than a millisecond. // // The time MUST be expressed in UTC. return []byte(m.String()), nil } func (m RelaxedTime) String() string { return time.Time(m).Round(time.Millisecond).UTC().Format(timeFormat) } // UnmarshalText implements encoding.TextUnmarshaler func (m *RelaxedTime) UnmarshalText(text []byte) error { if len(text) == 0 { *m = RelaxedTime(time.Time{}) return nil } t, err1 := time.Parse(time.RFC3339, string(text)) if err1 == nil { t = t.Round(time.Millisecond) *m = RelaxedTime(t) return nil } t, err2 := time.Parse(time.RFC3339Nano, string(text)) if err2 == nil { t = t.Round(time.Millisecond) *m = RelaxedTime(t) return nil } t, err2 = time.Parse("2006-01-02T15:04:05.999999999", string(text)) if err2 == nil { t = t.Round(time.Millisecond) *m = RelaxedTime(t) return nil } return err1 } saml-0.4.6/time_test.go000066400000000000000000000033711415467341100150130ustar00rootroot00000000000000package saml import ( "testing" "time" "github.com/google/go-cmp/cmp" "gotest.tools/assert" is "gotest.tools/assert/cmp" ) func TestRelaxedTimeFormat(t *testing.T) { rt := time.Date(1981, 02, 03, 14, 15, 16, 17, time.UTC) assert.Check(t, is.Equal("1981-02-03T14:15:16Z", RelaxedTime(rt).String())) buf, err := RelaxedTime(rt).MarshalText() assert.Check(t, err) assert.Check(t, is.Equal("1981-02-03T14:15:16Z", string(buf))) loc, err := time.LoadLocation("America/New_York") assert.Check(t, err) rt = time.Date(1981, 02, 03, 9, 15, 16, 17, loc) assert.Check(t, is.Equal("1981-02-03T14:15:16Z", RelaxedTime(rt).String())) buf, err = RelaxedTime(rt).MarshalText() assert.Check(t, err) assert.Check(t, is.Equal("1981-02-03T14:15:16Z", string(buf))) } func TestRelaxedTimeParse(t *testing.T) { { var rt RelaxedTime err := rt.UnmarshalText([]byte("1981-02-03T14:15:16Z")) assert.Check(t, err) assert.Check(t, is.DeepEqual( RelaxedTime(time.Date(1981, 02, 03, 14, 15, 16, 0, time.UTC)), rt, cmp.AllowUnexported(RelaxedTime{}))) } { var rt RelaxedTime err := rt.UnmarshalText([]byte("1981-02-03T14:15:16.178901234Z")) assert.Check(t, err) assert.Check(t, is.DeepEqual(RelaxedTime(time.Date(1981, 02, 03, 14, 15, 16, 179000000, time.UTC)), rt, cmp.AllowUnexported(RelaxedTime{}))) } { var rt RelaxedTime err := rt.UnmarshalText([]byte("1981-02-03T14:15:16.1717Z")) assert.Check(t, err) assert.Check(t, is.DeepEqual(RelaxedTime(time.Date(1981, 02, 03, 14, 15, 16, 172000000, time.UTC)), rt, cmp.AllowUnexported(RelaxedTime{}))) } { var rt RelaxedTime err := rt.UnmarshalText([]byte("1981-02-03T14:15:16Z04:00")) assert.Check(t, is.Error(err, "parsing time \"1981-02-03T14:15:16Z04:00\": extra text: \"04:00\"")) } } saml-0.4.6/util.go000066400000000000000000000014231415467341100137670ustar00rootroot00000000000000package saml import ( "crypto/rand" "io" "time" dsig "github.com/russellhaering/goxmldsig" ) // TimeNow is a function that returns the current time. The default // value is time.Now, but it can be replaced for testing. var TimeNow = func() time.Time { return time.Now().UTC() } // Clock is assigned to dsig validation and signing contexts if it is // not nil, otherwise the default clock is used. var Clock *dsig.Clock // RandReader is the io.Reader that produces cryptographically random // bytes when they are need by the library. The default value is // rand.Reader, but it can be replaced for testing. var RandReader = rand.Reader func randomBytes(n int) []byte { rv := make([]byte, n) if _, err := io.ReadFull(RandReader, rv); err != nil { panic(err) } return rv } saml-0.4.6/xmlenc/000077500000000000000000000000001415467341100137515ustar00rootroot00000000000000saml-0.4.6/xmlenc/cbc.go000066400000000000000000000117721415467341100150370ustar00rootroot00000000000000package xmlenc import ( "crypto/aes" "crypto/cipher" "crypto/des" // nolint: gas "encoding/base64" "errors" "fmt" "github.com/beevik/etree" ) // CBC implements Decrypter and Encrypter for block ciphers in CBC mode type CBC struct { keySize int algorithm string cipher func([]byte) (cipher.Block, error) } // KeySize returns the length of the key required. func (e CBC) KeySize() int { return e.keySize } // Algorithm returns the name of the algorithm, as will be found // in an xenc:EncryptionMethod element. func (e CBC) Algorithm() string { return e.algorithm } // Encrypt encrypts plaintext with key, which should be a []byte of length KeySize(). // It returns an xenc:EncryptedData element. func (e CBC) Encrypt(key interface{}, plaintext []byte, nonce []byte) (*etree.Element, error) { keyBuf, ok := key.([]byte) if !ok { return nil, ErrIncorrectKeyType("[]byte") } if len(keyBuf) != e.keySize { return nil, ErrIncorrectKeyLength(e.keySize) } block, err := e.cipher(keyBuf) if err != nil { return nil, err } encryptedDataEl := etree.NewElement("xenc:EncryptedData") encryptedDataEl.CreateAttr("xmlns:xenc", "http://www.w3.org/2001/04/xmlenc#") { randBuf := make([]byte, 16) if _, err := RandReader.Read(randBuf); err != nil { return nil, err } encryptedDataEl.CreateAttr("Id", fmt.Sprintf("_%x", randBuf)) } em := encryptedDataEl.CreateElement("xenc:EncryptionMethod") em.CreateAttr("Algorithm", e.algorithm) em.CreateAttr("xmlns:xenc", "http://www.w3.org/2001/04/xmlenc#") plaintext = appendPadding(plaintext, block.BlockSize()) iv := make([]byte, block.BlockSize()) if _, err := RandReader.Read(iv); err != nil { return nil, err } mode := cipher.NewCBCEncrypter(block, iv) ciphertext := make([]byte, len(plaintext)) mode.CryptBlocks(ciphertext, plaintext) ciphertext = append(iv, ciphertext...) cd := encryptedDataEl.CreateElement("xenc:CipherData") cd.CreateAttr("xmlns:xenc", "http://www.w3.org/2001/04/xmlenc#") cd.CreateElement("xenc:CipherValue").SetText(base64.StdEncoding.EncodeToString(ciphertext)) return encryptedDataEl, nil } // Decrypt decrypts an encrypted element with key. If the ciphertext contains an // EncryptedKey element, then the type of `key` is determined by the registered // Decryptor for the EncryptedKey element. Otherwise, `key` must be a []byte of // length KeySize(). func (e CBC) Decrypt(key interface{}, ciphertextEl *etree.Element) ([]byte, error) { // If the key is encrypted, decrypt it. if encryptedKeyEl := ciphertextEl.FindElement("./KeyInfo/EncryptedKey"); encryptedKeyEl != nil { var err error key, err = Decrypt(key, encryptedKeyEl) if err != nil { return nil, err } } keyBuf, ok := key.([]byte) if !ok { return nil, ErrIncorrectKeyType("[]byte") } if len(keyBuf) != e.KeySize() { return nil, ErrIncorrectKeyLength(e.KeySize()) } block, err := e.cipher(keyBuf) if err != nil { return nil, err } ciphertext, err := getCiphertext(ciphertextEl) if err != nil { return nil, err } if len(ciphertext) < block.BlockSize() { return nil, errors.New("ciphertext too short") } iv := ciphertext[:aes.BlockSize] ciphertext = ciphertext[aes.BlockSize:] mode := cipher.NewCBCDecrypter(block, iv) plaintext := make([]byte, len(ciphertext)) mode.CryptBlocks(plaintext, ciphertext) // decrypt in place plaintext, err = stripPadding(plaintext) if err != nil { return nil, err } return plaintext, nil } var ( // AES128CBC implements AES128-CBC symetric key mode for encryption and decryption AES128CBC BlockCipher = CBC{ keySize: 16, algorithm: "http://www.w3.org/2001/04/xmlenc#aes128-cbc", cipher: aes.NewCipher, } // AES192CBC implements AES192-CBC symetric key mode for encryption and decryption AES192CBC BlockCipher = CBC{ keySize: 24, algorithm: "http://www.w3.org/2001/04/xmlenc#aes192-cbc", cipher: aes.NewCipher, } // AES256CBC implements AES256-CBC symetric key mode for encryption and decryption AES256CBC BlockCipher = CBC{ keySize: 32, algorithm: "http://www.w3.org/2001/04/xmlenc#aes256-cbc", cipher: aes.NewCipher, } // TripleDES implements 3DES in CBC mode for encryption and decryption TripleDES BlockCipher = CBC{ keySize: 8, algorithm: "http://www.w3.org/2001/04/xmlenc#tripledes-cbc", cipher: des.NewCipher, } ) func init() { RegisterDecrypter(AES128CBC) RegisterDecrypter(AES192CBC) RegisterDecrypter(AES256CBC) RegisterDecrypter(TripleDES) } func appendPadding(buf []byte, blockSize int) []byte { paddingBytes := blockSize - (len(buf) % blockSize) padding := make([]byte, paddingBytes) padding[len(padding)-1] = byte(paddingBytes) return append(buf, padding...) } func stripPadding(buf []byte) ([]byte, error) { if len(buf) < 1 { return nil, errors.New("buffer is too short for padding") } paddingBytes := int(buf[len(buf)-1]) if paddingBytes > len(buf)-1 { return nil, errors.New("buffer is too short for padding") } if paddingBytes < 1 { return nil, errors.New("padding must be at least one byte") } return buf[:len(buf)-paddingBytes], nil } saml-0.4.6/xmlenc/corpus/000077500000000000000000000000001415467341100152645ustar00rootroot00000000000000saml-0.4.6/xmlenc/corpus/bad-encrypt-content-aes128-cbc-kw-aes192.xml000066400000000000000000000031161415467341100250360ustar00rootroot00000000000000 spade shovel Dig PLC, 1 First Ave, Dublin 1, Ireland jeb JbjZH7Mq564oMybpvCHWYM/5ER3eFsAV YDYTxR+smxZDSVoXXEp3n6HzTgWqV7ZlG6I1lmEv7zLGZBF/o7eqe5QGT6L3DPNW geflA8vVJHxwliixWcvHCnNKQkx+Sw8YbIknCQyr4mqtXEmHhsie5XYTEyqgKLVP YdNXf56wLUTMEmBqq7cto9OrYcBWkrDcQQvHmDkHuG+Nom4m+623GsB0FNts6VyN sdGMwo4K0bEFReLL04l6It+cgLJ2q+LKdBoMQL59IAQmrwi0bkiqee2cLlDuGyQ1 KD9IQ1qtlJpvQujN4xNVWT00UjtWxmpSMID/Kue/AnXn7Cf8zw1ZZQitgh8uWOX2 uMy99F2YlxqIK1r+MeXHuZDNf75S8dFaKIKtHMf7ioA= saml-0.4.6/xmlenc/corpus/decryption-transform-except.xml000066400000000000000000000064511415467341100234730ustar00rootroot00000000000000 spade shovel Dig PLC, 1 First Ave, Dublin 1, Ireland jed cX6lnfgmvWuxyiQgNhzAq1lYggW2M5GziFgNBQju3xcnDqlzf5LSjeyBnbL0Q7ws 8XhySFCrdwIi5mVxyfdFkVrTlzQQ0viaqTDgi9PQRgZMOImGGWij3wbmf9XseHHt 6q8V7LPjMFQAnsLDQgKf4gzzOnhtKf15GfTEpGvUnNn2dLDxw+hDcD1N54/bjSQs uTiL7PgGQ5g4u4eaXRRLWeAGsIf5QgdQG3GLiOZIX1LJ5bREKgXeKrtJJI97xUX3 3vaF+tKRcSFBFIMjFrw271bFj4vvvQZfSS6xX+BKXHOUu8C4NH9Le8pA9o4NgCB8 tWA8W3iI5/BGEZve0Me9byvPHYjRXlbG+YqysVTmzfw= x3aR5pJ5pepFFH5ENv61pZG4pVwNKaM+H9oyY4qG6d8l/C0J1iGv6c8dyLp0YQ2k 5Oe9qba6preOZG1NZAYK2/6pu9RCon9vRJ9hVLDpeng= LuHrz9+WG7/c4Q81tFboNZg2cktWbZcRfp08XrmgKy1GDm9xSfTYCA==

imup6lmki4rAmUstKb/xdBRMWNtQ+pDN97ZnLA9X3lKbkEHtYFyjQ3uActgVSJ75 iVRuKxz4Cb5RzVm25EaKmKq8rif1MtBIi6jjDJxmIdNaEKG9zVTf9giJx1N9I0t3 oh1fAVZDSrzKzJGQ2WvDfIfFHdJMtB3C0VKGmLZR7Xk=

xDve3j7sEnh4rIzM5gK+5/gxxFU= NLugAf6IZJxo3BCOi5yrGEVwtlEzXcnndXhd0Tz38CnQKc4SEupm4PyP5TmLvK64 TDfOD7sno/W5oI1KZdimfW2c4r/6waNzZSvicMOWhLYY621Nn6njBc8VNwoxWpzC XhKm70b8+D4YZMn/eU5DN8dvhTv/bNK21FfJqjp033U= W7dOmH/vWqocVCiqaxj6soxVXfR8XpMdY2Zv4Amjr3n81geyOLb6IZ+l7MUbdp85 29DQzuoVTthVpB9X4JKCprZIzifOTM1PFflTBzjx7egJwJWAIVdWyiIPjke6Va+w uV2n4Rl/cgCvrXK5cTov5C/Bpaf6o+qrrDGFBLLZTF4=
saml-0.4.6/xmlenc/corpus/decryption-transform.xml000066400000000000000000000055431415467341100222060ustar00rootroot00000000000000 spade shovel Dig PLC, 1 First Ave, Dublin 1, Ireland jed SE3HkQevYxzuN9LoMH3QIYHK0X7DBlobhiTbRucgKcTKt9DsUJIcd6JZV6lrw/4x YICyq6YM73IWpibspxgz/0chhvWem9sYZvWTuTtZgHzeY0Uri6bpXqBEn1YT0K6B chwfv1myfp91EmdPHU+shH6ZEyYkHJUMss58iIawIuVsIfpCO7xDKgfs/glnN3os epY0KvAMZSnwUAf42fQ3TlahLTR+B52AmdodwaCwQlwQwrC7RH0FtNiiLQA9SA2t //StKWcyHjswUCejfKLdjv6bK+WmBxmnNWtmI9DYkjJ6V5pYU1MVw+JG410O+gaa fnNWxlWa+BGwcTaz+KNrP8bIqli8IoJJgxXIUqfb734= wSvPYqTcpLfX2mKXibtsmm7FDu8N+/BObM0+bGaeXhk= O0VYUdslJ8t2EURD0T/v2nNrFQMo42vzvfAhooZrDbkuLbCj6/Hxmw==

imup6lmki4rAmUstKb/xdBRMWNtQ+pDN97ZnLA9X3lKbkEHtYFyjQ3uActgVSJ75 iVRuKxz4Cb5RzVm25EaKmKq8rif1MtBIi6jjDJxmIdNaEKG9zVTf9giJx1N9I0t3 oh1fAVZDSrzKzJGQ2WvDfIfFHdJMtB3C0VKGmLZR7Xk=

xDve3j7sEnh4rIzM5gK+5/gxxFU= NLugAf6IZJxo3BCOi5yrGEVwtlEzXcnndXhd0Tz38CnQKc4SEupm4PyP5TmLvK64 TDfOD7sno/W5oI1KZdimfW2c4r/6waNzZSvicMOWhLYY621Nn6njBc8VNwoxWpzC XhKm70b8+D4YZMn/eU5DN8dvhTv/bNK21FfJqjp033U= W7dOmH/vWqocVCiqaxj6soxVXfR8XpMdY2Zv4Amjr3n81geyOLb6IZ+l7MUbdp85 29DQzuoVTthVpB9X4JKCprZIzifOTM1PFflTBzjx7egJwJWAIVdWyiIPjke6Va+w uV2n4Rl/cgCvrXK5cTov5C/Bpaf6o+qrrDGFBLLZTF4=
saml-0.4.6/xmlenc/corpus/encrypt-content-aes128-cbc-kw-aes192.xml000066400000000000000000000032101415467341100243050ustar00rootroot00000000000000 ]> spade shovel Dig PLC, 1 First Ave, Dublin 1, Ireland jeb IbjZH7Mq564oMybpvCHWYM/5ER3eFsAV YDYTxR+smxZDSVoXXEp3n6HzTgWqV7ZlG6I1lmEv7zLGZBF/o7eqe5QGT6L3DPNW geflA8vVJHxwliixWcvHCnNKQkx+Sw8YbIknCQyr4mqtXEmHhsie5XYTEyqgKLVP YdNXf56wLUTMEmBqq7cto9OrYcBWkrDcQQvHmDkHuG+Nom4m+623GsB0FNts6VyN sdGMwo4K0bEFReLL04l6It+cgLJ2q+LKdBoMQL59IAQmrwi0bkiqee2cLlDuGyQ1 KD9IQ1qtlJpvQujN4xNVWT00UjtWxmpSMID/Kue/AnXn7Cf8zw1ZZQitgh8uWOX2 uMy99F2YlxqIK1r+MeXHuZDNf75S8dFaKIKtHMf7ioA= saml-0.4.6/xmlenc/corpus/encrypt-content-aes192-cbc-dh-sha512.xml000066400000000000000000000144131415467341100242660ustar00rootroot00000000000000 spade shovel Dig PLC, 1 First Ave, Dublin 1, Ireland bm9uY2U=

plygl2uMNc+jYtAZeKCZxPsmqa2z8DrOUa7L455iszN4SdPnL+LsZD47VJayvQY8 6D1J5arkwrbUzmhMAjBZsENPBgffRwwEBTjoq+gjSyZNIbxqsqnJdEyUElzn4kGE whECkJGnOaScacpjZg11h+gd0iBfY091bGHrCZrvr/8=

9jJXQijNovoq6QUBFcEUYwUvyTM= PerUZgMEMDTegMdTBRG9DPY5EHmwDxwzladdRcfvfdfU/9wlPzz5BUotMm730J9d lF6avWr929fzYsnIOUDeUOJpltXmrTYnvz5Bi6yuUu6bVwSfv7u4S+I/EM9ZB+eY 3fdF5TAMHD4tK86lw5APDrN2QnO1UMCwIvjOFatSOI0= Ulu6B1lCwajtIBnolqqgU+R1oxfye63DnI/iLM/Oe+Y8I/LMMaEmo3LmCU30m82r NyOUqgfnm97S0bT8ZhI8gvw0EyQJ87vhlUz4WcmddU/YlTi3gJHUClr2olmBmRCt m2vKo/BpoLGJ0Wg1eyWfo54+gCqbeNez/DmBGcBEEhM=
MIIDvjCCA36gAwIBAgIGAOxN39MIMAkGByqGSM44BAMwbjELMAkGA1UEBhMCSUUx DzANBgNVBAgTBkR1YmxpbjEkMCIGA1UEChMbQmFsdGltb3JlIFRlY2hub2xvZ2ll cyBMdGQuMREwDwYDVQQLEwhYL1NlY3VyZTEVMBMGA1UEAxMMVHJhbnNpZW50IENB MB4XDTAyMDIyODE3NTMxNloXDTAzMDIyODE3NTI1NFowbzELMAkGA1UEBhMCSUUx DzANBgNVBAgTBkR1YmxpbjEkMCIGA1UEChMbQmFsdGltb3JlIFRlY2hub2xvZ2ll cyBMdGQuMREwDwYDVQQLEwhYL1NlY3VyZTEWMBQGA1UEAxMNTWVybGluIEh1Z2hl czCCAiUwggGaBgcqhkjOPgIBMIIBjQKBgQCmXKCXa4w1z6Ni0Bl4oJnE+yaprbPw Os5RrsvjnmKzM3hJ0+cv4uxkPjtUlrK9BjzoPUnlquTCttTOaEwCMFmwQ08GB99H DAQFOOir6CNLJk0hvGqyqcl0TJQSXOfiQYTCEQKQkac5pJxpymNmDXWH6B3SIF9j T3VsYesJmu+v/wKBgD3q1GYDBDA03oDHUwURvQz2ORB5sA8cM5WnXUXH733X1P/c JT88+QVKLTJu99CfXZRemr1q/dvX82LJyDlA3lDiaZbV5q02J78+QYusrlLum1cE n7+7uEviPxDPWQfnmN33ReUwDBw+LSvOpcOQDw6zdkJztVDAsCL4zhWrUjiNAhUA 9jJXQijNovoq6QUBFcEUYwUvyTMCbQCs/HkLusCqHmY71JxUOFzy5fuWkPpWXJzx qU3oz1BfMZtPUqjpBnqU97M7VUEg+5pRG2txaHP8XNmB1bY0DCE88riDmHP7HqZB Z2gbaH2LxXDQDayb5GcPfn38eDcWvVAaKP9fJ8wG5RUu3AoDgYQAAoGAUlu6B1lC wajtIBnolqqgU+R1oxfye63DnI/iLM/Oe+Y8I/LMMaEmo3LmCU30m82rNyOUqgfn m97S0bT8ZhI8gvw0EyQJ87vhlUz4WcmddU/YlTi3gJHUClr2olmBmRCtm2vKo/Bp oLGJ0Wg1eyWfo54+gCqbeNez/DmBGcBEEhOjOjA4MA4GA1UdDwEB/wQEAwIDCDAR BgNVHQ4ECgQIgUAwB+9f1oIwEwYDVR0jBAwwCoAIgjqisiZ1WVswCQYHKoZIzjgE AwMvADAsAhQ41mCUsFhmxI58tytV8XEVZOCuUwIUVMe/HbUAH5PJ7aRoCNqa3fCI cU0=
MIIDvjCCA36gAwIBAgIGAOxN3+EMMAkGByqGSM44BAMwbjELMAkGA1UEBhMCSUUx DzANBgNVBAgTBkR1YmxpbjEkMCIGA1UEChMbQmFsdGltb3JlIFRlY2hub2xvZ2ll cyBMdGQuMREwDwYDVQQLEwhYL1NlY3VyZTEVMBMGA1UEAxMMVHJhbnNpZW50IENB MB4XDTAyMDIyODE3NTMxOVoXDTAzMDIyODE3NTI1NFowbzELMAkGA1UEBhMCSUUx DzANBgNVBAgTBkR1YmxpbjEkMCIGA1UEChMbQmFsdGltb3JlIFRlY2hub2xvZ2ll cyBMdGQuMREwDwYDVQQLEwhYL1NlY3VyZTEWMBQGA1UEAxMNTmlscmVtIFNlaGd1 aDCCAiUwggGaBgcqhkjOPgIBMIIBjQKBgQCmXKCXa4w1z6Ni0Bl4oJnE+yaprbPw Os5RrsvjnmKzM3hJ0+cv4uxkPjtUlrK9BjzoPUnlquTCttTOaEwCMFmwQ08GB99H DAQFOOir6CNLJk0hvGqyqcl0TJQSXOfiQYTCEQKQkac5pJxpymNmDXWH6B3SIF9j T3VsYesJmu+v/wKBgD3q1GYDBDA03oDHUwURvQz2ORB5sA8cM5WnXUXH733X1P/c JT88+QVKLTJu99CfXZRemr1q/dvX82LJyDlA3lDiaZbV5q02J78+QYusrlLum1cE n7+7uEviPxDPWQfnmN33ReUwDBw+LSvOpcOQDw6zdkJztVDAsCL4zhWrUjiNAhUA 9jJXQijNovoq6QUBFcEUYwUvyTMCbQCs/HkLusCqHmY71JxUOFzy5fuWkPpWXJzx qU3oz1BfMZtPUqjpBnqU97M7VUEg+5pRG2txaHP8XNmB1bY0DCE88riDmHP7HqZB Z2gbaH2LxXDQDayb5GcPfn38eDcWvVAaKP9fJ8wG5RUu3AoDgYQAAoGAGSYT19Pb VCxMt06cAP7zQZ6AC5eXp3zeAweIevV96ryA1mB03qhB9X2lVowAUOFc24aVRTz7 wRoRjNQ20atzSy21C7yXDkvZ4uxfdrpIqpIVrI28e7XL+6CrhnAk621OvdeyEz5H orA21hPXoCNdnUPG5Ib20oopM87ptF5dwiWjOjA4MA4GA1UdDwEB/wQEAwIDCDAR BgNVHQ4ECgQIiDCSQ3FB/oEwEwYDVR0jBAwwCoAIgjqisiZ1WVswCQYHKoZIzjgE AwMvADAsAhQMtZ98TyqVkVqUJ3RJqaU7l2xqKgIUX997qRqeMjAkK88NHeNd95/2 Yos=
5jIlxXZGhx8vUNbL0ZvdRry6mPapX8qLYlDgy3tE6nRbnBRWACviYQAXBqvDfn1R TKmBWZ5NoJobM8lXWOk2nNQIuSQojcFYRuvcWU7DffDVX7dUCAVRJp6PS/5V1IHR JJ2WBagWSW1lFW9mqjfe0ZflEZGYI3/5kUYQIpbMvEuXoF8129VGiKalZsCVTRxd /IsdT8x/7L57GlGq0OzCMI5zG3QrBV7wUOoqBu5SxS8QUvUPucH8hsD4Bq4BwVEa GlUVAj7H3HYYo7fviTO4i2lTMunGW9rcJVnKXjDM/Mds3oM4zbBo/Ao3m3rmpUUz AwSe6ofh6ML418+cyCaRUoVQOlG+VwkHEKUiYYGhsKY=
saml-0.4.6/xmlenc/corpus/encrypt-content-aes256-cbc-prop.xml000066400000000000000000000030041415467341100236450ustar00rootroot00000000000000 ]> spade shovel Dig PLC, 1 First Ave, Dublin 1, Ireland jed H8n1OuEJFyUgUguDFF6ml8nRbA0IaDYgmtGelWT4V7CSz9q/AvtfxyMzUH+tQZ+F jyXh3otR1+V1+8EsevzEq5nUmNKl+wyxQmWaUvbvXpSwAJnlJdyvnP56JiXUBS+p C2KzlO9kk8l6awtuRd9Z6eVjngwTf7kNprmu5Bv0o+x7dcq96G8wGLvMThbs4uxk iIDK5+qGBzzIlFw3GG82MKmnVBveQw3LD52y76yBtoayuAJFJMnrXa0OEAaBRSI2 fjPNGJV3sCyKZDHqGlsQ4X+VvXzevLbBLkFy1xH9/zoUXo8cEaTvsIOBYu/Xn/CJ y/dpe/dvOqqji+9vFccAyrBHxHeYSonuFsxfpSDVC6Y= certifiable saml-0.4.6/xmlenc/corpus/encrypt-content-tripledes-cbc.xml000066400000000000000000000023521415467341100236620ustar00rootroot00000000000000 ]> spade shovel Dig PLC, 1 First Ave, Dublin 1, Ireland bob uchJT2QyzQe7BoBaDYKPR5BDgEW8jsJ3UOGEYz9EVrBKVztYfcu0xhif5Y9kqtyx DDa7woNcTyhwQDZh9jGr5hzkcjrsKfMjJw+PnKNZzc+KMW0z861L8sdhdl8TA+bt yudfaCEJaH4RdHABp+VMzL5CrXr5skvubolWs1KzUtqbRekkxucknzJmnqRY8yPp 4iBvVuvus+Bk0pj271NWu13CmHvdJRMMDSX30JMfsecW6mfdF5xjoFciL8VnemzJ qt0SUVjMzoeY0PnCdk09Ej2OZdj8AtkLPCEKeiBBD+coCf5F8WaLrPTRPgjoAtiN Wda+McaZPJje1IfoAKGTcg== saml-0.4.6/xmlenc/corpus/encrypt-data-aes128-cbc.xml000066400000000000000000000007011415467341100221250ustar00rootroot00000000000000 job QMpxhXq1DtBeyC9KfSaMQWrEtefe+e935gF/x62spvmL6IW0XeS0W4Kk31OgWzN0 saml-0.4.6/xmlenc/corpus/encrypt-data-aes192-cbc-kw-aes256.xml000066400000000000000000000014771415467341100235630ustar00rootroot00000000000000 jed 4AAgyi3M7xNdBimbQZKdGJLn3/cS4Yv8QKuA01+gUnY= 50lv94d/DFJirJXYOUXaBlrO+7gIXpx8cqH+G2xvE4mueoIxmGs8RH7FBXwjuMgf saml-0.4.6/xmlenc/corpus/encrypt-data-aes256-cbc-kw-tripledes.xml000066400000000000000000000015261415467341100245450ustar00rootroot00000000000000 bob ZyJbVsjRM4MEsswwwHz57aUz1eMqZHuEIoEPGS47CcmLvhuCtlzWZ9S/WcVJZIpz Lp2ZWyJERT05icmHvWWbEtCCfmB2jvSlSclhS0oj3A3PU90aE6v+bFFQxrHw7VUd saml-0.4.6/xmlenc/corpus/encrypt-data-tripledes-cbc-rsa-oaep-mgf1p-sha256.xml000066400000000000000000000045341415467341100266500ustar00rootroot00000000000000 MTIzNDU2Nzg= MIICkjCCAfugAwIBAgIGAOxN32E+MA0GCSqGSIb3DQEBBQUAMG4xCzAJBgNVBAYT AklFMQ8wDQYDVQQIEwZEdWJsaW4xJDAiBgNVBAoTG0JhbHRpbW9yZSBUZWNobm9s b2dpZXMgTHRkLjERMA8GA1UECxMIWC9TZWN1cmUxFTATBgNVBAMTDFRyYW5zaWVu dCBDQTAeFw0wMjAyMjgxNzUyNDZaFw0wMzAyMjgxNzUyNDBaMG8xCzAJBgNVBAYT AklFMQ8wDQYDVQQIEwZEdWJsaW4xJDAiBgNVBAoTG0JhbHRpbW9yZSBUZWNobm9s b2dpZXMgTHRkLjERMA8GA1UECxMIWC9TZWN1cmUxFjAUBgNVBAMTDU1lcmxpbiBI dWdoZXMwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAORdNSxbNFWlQeNsOlYJ 9gN9eZD+rguRqKhmhOm7i63VDd5ALm2APXhqAmGBPzLN5jlL9g2XALK5WSO4XKjJ McVfYg4+nPuOeHgqdD4HUgf19j/6SaTMcmDFJQMmx1Qw+Aakq3mGcSfvOJcBZctz a50VucfCGL1NdfBEcaL3BnhjAgMBAAGjOjA4MA4GA1UdDwEB/wQEAwIFoDARBgNV HQ4ECgQIjFG0ZGNyvNswEwYDVR0jBAwwCoAIhJXVlhr6O4wwDQYJKoZIhvcNAQEF BQADgYEAXzG7x5aCJYRusTbmuZqhidGM5iiA9+RmZ4JTPDEgbeiTiJROxpr+ZjnA TmsDKrCpqNUiHWjmsKEArYQp8R/KjdKl/pVe3jUvTxb0YZ+li/7k0GQ5LyRT/K4c 2SgyLlyBPhpMq+z3g4P2egVRaZbxsLuKQILf7MIV/X5iAEBzu1w= 1SVctZA/RB6vVjsu5NYTxowdvsViJJ1skDXX09RmNU3YlCuPpSqWWhCU5u5ILfr9 6AFcascXbdFyEZ9tjDhK8Nid2MEqkR/Mc9zFHf7mPMnO7C8bRggkjjdILSIF/Ft7 FXzm/DFP50IF3zPe/n5jy2Nk8uRvTmKUDcnoV6qnUgY= QOImekuU44UeCmVaMma9bCT5h5a6mWXDSndTB81jvHw= saml-0.4.6/xmlenc/corpus/encrypt-data-tripledes-cbc-rsa-oaep-mgf1p.xml000066400000000000000000000044311415467341100256360ustar00rootroot00000000000000 MIICkjCCAfugAwIBAgIGAOxN32E+MA0GCSqGSIb3DQEBBQUAMG4xCzAJBgNVBAYT AklFMQ8wDQYDVQQIEwZEdWJsaW4xJDAiBgNVBAoTG0JhbHRpbW9yZSBUZWNobm9s b2dpZXMgTHRkLjERMA8GA1UECxMIWC9TZWN1cmUxFTATBgNVBAMTDFRyYW5zaWVu dCBDQTAeFw0wMjAyMjgxNzUyNDZaFw0wMzAyMjgxNzUyNDBaMG8xCzAJBgNVBAYT AklFMQ8wDQYDVQQIEwZEdWJsaW4xJDAiBgNVBAoTG0JhbHRpbW9yZSBUZWNobm9s b2dpZXMgTHRkLjERMA8GA1UECxMIWC9TZWN1cmUxFjAUBgNVBAMTDU1lcmxpbiBI dWdoZXMwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAORdNSxbNFWlQeNsOlYJ 9gN9eZD+rguRqKhmhOm7i63VDd5ALm2APXhqAmGBPzLN5jlL9g2XALK5WSO4XKjJ McVfYg4+nPuOeHgqdD4HUgf19j/6SaTMcmDFJQMmx1Qw+Aakq3mGcSfvOJcBZctz a50VucfCGL1NdfBEcaL3BnhjAgMBAAGjOjA4MA4GA1UdDwEB/wQEAwIFoDARBgNV HQ4ECgQIjFG0ZGNyvNswEwYDVR0jBAwwCoAIhJXVlhr6O4wwDQYJKoZIhvcNAQEF BQADgYEAXzG7x5aCJYRusTbmuZqhidGM5iiA9+RmZ4JTPDEgbeiTiJROxpr+ZjnA TmsDKrCpqNUiHWjmsKEArYQp8R/KjdKl/pVe3jUvTxb0YZ+li/7k0GQ5LyRT/K4c 2SgyLlyBPhpMq+z3g4P2egVRaZbxsLuKQILf7MIV/X5iAEBzu1w= S5SqVG+QxxpCNWobuqQFAI6db1pTEpWNMQXQVJAPjlfmvnVmTtq5v6fgMA2l/r7M iX7gUPZthrKezkSavDfi057cK6YKpC5/KACXjNJvUoaVXj/aXpcoMOO+ZTPq36eo pyeW99DWYgCbY88Kf9R3r3QMx/ogwjScfRVJTRZL3Lo= HG02AxNyn4iA9NH5x+PQ9lgPNzTkljThotXWKz0UYrE= saml-0.4.6/xmlenc/corpus/encrypt-element-aes128-cbc-rsa-1_5.xml000066400000000000000000000057771415467341100240340ustar00rootroot00000000000000 ]> spade shovel Dig PLC, 1 First Ave, Dublin 1, Ireland MIICkjCCAfugAwIBAgIGAOxN32E+MA0GCSqGSIb3DQEBBQUAMG4xCzAJBgNVBAYT AklFMQ8wDQYDVQQIEwZEdWJsaW4xJDAiBgNVBAoTG0JhbHRpbW9yZSBUZWNobm9s b2dpZXMgTHRkLjERMA8GA1UECxMIWC9TZWN1cmUxFTATBgNVBAMTDFRyYW5zaWVu dCBDQTAeFw0wMjAyMjgxNzUyNDZaFw0wMzAyMjgxNzUyNDBaMG8xCzAJBgNVBAYT AklFMQ8wDQYDVQQIEwZEdWJsaW4xJDAiBgNVBAoTG0JhbHRpbW9yZSBUZWNobm9s b2dpZXMgTHRkLjERMA8GA1UECxMIWC9TZWN1cmUxFjAUBgNVBAMTDU1lcmxpbiBI dWdoZXMwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAORdNSxbNFWlQeNsOlYJ 9gN9eZD+rguRqKhmhOm7i63VDd5ALm2APXhqAmGBPzLN5jlL9g2XALK5WSO4XKjJ McVfYg4+nPuOeHgqdD4HUgf19j/6SaTMcmDFJQMmx1Qw+Aakq3mGcSfvOJcBZctz a50VucfCGL1NdfBEcaL3BnhjAgMBAAGjOjA4MA4GA1UdDwEB/wQEAwIFoDARBgNV HQ4ECgQIjFG0ZGNyvNswEwYDVR0jBAwwCoAIhJXVlhr6O4wwDQYJKoZIhvcNAQEF BQADgYEAXzG7x5aCJYRusTbmuZqhidGM5iiA9+RmZ4JTPDEgbeiTiJROxpr+ZjnA TmsDKrCpqNUiHWjmsKEArYQp8R/KjdKl/pVe3jUvTxb0YZ+li/7k0GQ5LyRT/K4c 2SgyLlyBPhpMq+z3g4P2egVRaZbxsLuKQILf7MIV/X5iAEBzu1w= heZshNX5m7arS3OmR72+8WNCMMpznxE41dLWkgd6XJpzl+IN2xuijAf4YPEjjJmZ nt9PlO3/hiHl0Cvpg5vMR6AhvL49BvCz9JCeMG6x3MHBiKbRNhyEq2rX7o1GdJhC 5cm35Q/ZDKV9DHG8jWmPcOb8yKU9NYo2LJKDb3YHOJY= 0wkECpTy60/FDwbVM4zgd9qJVjR4h0q4PLm5pyyIxAuhbEh0art03yEikmbWBt2H 7qOk2G9iufUdwwqNPuZV5Qw5Rg2FMvTx234lDERGn5p+hhjOTcss5JF9QDzgdiec KABX3vbCESi/f3uwQ8BYDT+6SnxTR+xtcNv5xhbUCIFk/TaenSWx6p6fntTwTl1e lpwnI0EtM1yf4a9tBiH9PNd36BUv2rvSi4cZvJqSB3ZKvGtuwwyRzOzlzl259d1u QuoYysTBEAHw/WIop8eAexU9PUv7UbTkQAQag1yStda+GepVdpXEpu4hcxXQcvfs 9AQgkAgh4JKrnY4Bhz2B/e4CHHfbEedDOi+FVYlZuLn0CzrKMnM+1nUmqxJVWHz7 hytidpuqNRw3gcMkYvgH6g== saml-0.4.6/xmlenc/corpus/encrypt-element-aes192-cbc-ref.xml000066400000000000000000000033411415467341100234230ustar00rootroot00000000000000 ]> spade shovel Dig PLC, 1 First Ave, Dublin 1, Ireland jeb self::text()[parent::rep:CipherValue[@Id="example1"]] zih1MFU6Px1m2U1lSEIV9LUIsnb3SIWBfRHlRrOWKFFFcVvXiE6z3nCbkNYMuy1T nPwXDd9/BkOGiPuFT2jixN7Zowe2ANK1dZXKVjZ1+ACx+Kg17U+EMPEuq481OW7e wm0vnbur0L2lCXb4DP7c6sotV89W53v2MlaYqWHhlBO/zasqwhl6q/c/L/GdPUHH ovKZ+24ZWYktxCLEXMslIAysQ0UFBLolrtC/7XDgYY9s4UvbedgeqbrdnxQ4LiRn L+aKN1bnKF3KlWKCJFvVrRESriGPBfpasWA/A1LOK333a8LaOlS7RFamflfICk+t VqCspVnIs6vBBtrGLI5SsJS+rh1r42jI/h/ivELUOmUq1sZCFQvEhx7AiHi4/9SY LWcR4w3ZH3aqFL/XtAzKYQ== saml-0.4.6/xmlenc/corpus/encrypt-element-aes256-cbc-carried-kw-aes256.xml000066400000000000000000000043011415467341100257000ustar00rootroot00000000000000 spade shovel Dig PLC, 1 First Ave, Dublin 1, Ireland Foo Key pdDtiyd7XQ/BFEEN0PMJuHnLUfCY+bJlsW+q04OiKSPnRd4/dS1tjaTfj5dPpGXe cY3fJvRsq9QP1CJiwyEC/EQ1zSLbzwOtZ+NtxtsFgYvPBJ9t86ZcXIjlErQ85z3L wnb8rSHpE9tu4tJ1rjgf2i6NCbdFnSMXLSDgLEs48+gkX0cJCmKxzRaSE4cV0OSl hBWND4EYzX1M679VlSYrI0de+lSPO3Vx+y/TuZ5Vo+uu9+YP+ce0LRkx2BicjjsP QO9sp+yjHPNDIV1Z7VHsDIWqqmBaNQo3GuzF5WzWgaXTKnPv/IgUQn+1t3EtgHyb JhnfR/1em16z/Zaf9Uy1Lfd//yfEJ9BCjqwe1UjwN6ytu1v2BHd+8bVjD2o+Dg8V 7ayOLlkWOTOLvtJMPOXPqw== ned EWlIkFPGrkeW4cyjWSznLVoClVh/OEC7Klya9d9o7R6wll6JswZb2w== Foo Key jed bsL63D0hPN6EOyzdgfEmKsAAvoJiGM+Wp9a9KZM92IKdl7s3YSntRg== Foo Key saml-0.4.6/xmlenc/corpus/encrypt-element-aes256-cbc-kw-aes256-dh-ripemd160.xml000066400000000000000000000155021415467341100263740ustar00rootroot00000000000000 spade shovel Dig PLC, 1 First Ave, Dublin 1, Ireland bm9uY2U=

plygl2uMNc+jYtAZeKCZxPsmqa2z8DrOUa7L455iszN4SdPnL+LsZD47VJayvQY8 6D1J5arkwrbUzmhMAjBZsENPBgffRwwEBTjoq+gjSyZNIbxqsqnJdEyUElzn4kGE whECkJGnOaScacpjZg11h+gd0iBfY091bGHrCZrvr/8=

9jJXQijNovoq6QUBFcEUYwUvyTM= PerUZgMEMDTegMdTBRG9DPY5EHmwDxwzladdRcfvfdfU/9wlPzz5BUotMm730J9d lF6avWr929fzYsnIOUDeUOJpltXmrTYnvz5Bi6yuUu6bVwSfv7u4S+I/EM9ZB+eY 3fdF5TAMHD4tK86lw5APDrN2QnO1UMCwIvjOFatSOI0= Ulu6B1lCwajtIBnolqqgU+R1oxfye63DnI/iLM/Oe+Y8I/LMMaEmo3LmCU30m82r NyOUqgfnm97S0bT8ZhI8gvw0EyQJ87vhlUz4WcmddU/YlTi3gJHUClr2olmBmRCt m2vKo/BpoLGJ0Wg1eyWfo54+gCqbeNez/DmBGcBEEhM=
MIIDvjCCA36gAwIBAgIGAOxN39MIMAkGByqGSM44BAMwbjELMAkGA1UEBhMCSUUx DzANBgNVBAgTBkR1YmxpbjEkMCIGA1UEChMbQmFsdGltb3JlIFRlY2hub2xvZ2ll cyBMdGQuMREwDwYDVQQLEwhYL1NlY3VyZTEVMBMGA1UEAxMMVHJhbnNpZW50IENB MB4XDTAyMDIyODE3NTMxNloXDTAzMDIyODE3NTI1NFowbzELMAkGA1UEBhMCSUUx DzANBgNVBAgTBkR1YmxpbjEkMCIGA1UEChMbQmFsdGltb3JlIFRlY2hub2xvZ2ll cyBMdGQuMREwDwYDVQQLEwhYL1NlY3VyZTEWMBQGA1UEAxMNTWVybGluIEh1Z2hl czCCAiUwggGaBgcqhkjOPgIBMIIBjQKBgQCmXKCXa4w1z6Ni0Bl4oJnE+yaprbPw Os5RrsvjnmKzM3hJ0+cv4uxkPjtUlrK9BjzoPUnlquTCttTOaEwCMFmwQ08GB99H DAQFOOir6CNLJk0hvGqyqcl0TJQSXOfiQYTCEQKQkac5pJxpymNmDXWH6B3SIF9j T3VsYesJmu+v/wKBgD3q1GYDBDA03oDHUwURvQz2ORB5sA8cM5WnXUXH733X1P/c JT88+QVKLTJu99CfXZRemr1q/dvX82LJyDlA3lDiaZbV5q02J78+QYusrlLum1cE n7+7uEviPxDPWQfnmN33ReUwDBw+LSvOpcOQDw6zdkJztVDAsCL4zhWrUjiNAhUA 9jJXQijNovoq6QUBFcEUYwUvyTMCbQCs/HkLusCqHmY71JxUOFzy5fuWkPpWXJzx qU3oz1BfMZtPUqjpBnqU97M7VUEg+5pRG2txaHP8XNmB1bY0DCE88riDmHP7HqZB Z2gbaH2LxXDQDayb5GcPfn38eDcWvVAaKP9fJ8wG5RUu3AoDgYQAAoGAUlu6B1lC wajtIBnolqqgU+R1oxfye63DnI/iLM/Oe+Y8I/LMMaEmo3LmCU30m82rNyOUqgfn m97S0bT8ZhI8gvw0EyQJ87vhlUz4WcmddU/YlTi3gJHUClr2olmBmRCtm2vKo/Bp oLGJ0Wg1eyWfo54+gCqbeNez/DmBGcBEEhOjOjA4MA4GA1UdDwEB/wQEAwIDCDAR BgNVHQ4ECgQIgUAwB+9f1oIwEwYDVR0jBAwwCoAIgjqisiZ1WVswCQYHKoZIzjgE AwMvADAsAhQ41mCUsFhmxI58tytV8XEVZOCuUwIUVMe/HbUAH5PJ7aRoCNqa3fCI cU0=
MIIDvjCCA36gAwIBAgIGAOxN3+EMMAkGByqGSM44BAMwbjELMAkGA1UEBhMCSUUx DzANBgNVBAgTBkR1YmxpbjEkMCIGA1UEChMbQmFsdGltb3JlIFRlY2hub2xvZ2ll cyBMdGQuMREwDwYDVQQLEwhYL1NlY3VyZTEVMBMGA1UEAxMMVHJhbnNpZW50IENB MB4XDTAyMDIyODE3NTMxOVoXDTAzMDIyODE3NTI1NFowbzELMAkGA1UEBhMCSUUx DzANBgNVBAgTBkR1YmxpbjEkMCIGA1UEChMbQmFsdGltb3JlIFRlY2hub2xvZ2ll cyBMdGQuMREwDwYDVQQLEwhYL1NlY3VyZTEWMBQGA1UEAxMNTmlscmVtIFNlaGd1 aDCCAiUwggGaBgcqhkjOPgIBMIIBjQKBgQCmXKCXa4w1z6Ni0Bl4oJnE+yaprbPw Os5RrsvjnmKzM3hJ0+cv4uxkPjtUlrK9BjzoPUnlquTCttTOaEwCMFmwQ08GB99H DAQFOOir6CNLJk0hvGqyqcl0TJQSXOfiQYTCEQKQkac5pJxpymNmDXWH6B3SIF9j T3VsYesJmu+v/wKBgD3q1GYDBDA03oDHUwURvQz2ORB5sA8cM5WnXUXH733X1P/c JT88+QVKLTJu99CfXZRemr1q/dvX82LJyDlA3lDiaZbV5q02J78+QYusrlLum1cE n7+7uEviPxDPWQfnmN33ReUwDBw+LSvOpcOQDw6zdkJztVDAsCL4zhWrUjiNAhUA 9jJXQijNovoq6QUBFcEUYwUvyTMCbQCs/HkLusCqHmY71JxUOFzy5fuWkPpWXJzx qU3oz1BfMZtPUqjpBnqU97M7VUEg+5pRG2txaHP8XNmB1bY0DCE88riDmHP7HqZB Z2gbaH2LxXDQDayb5GcPfn38eDcWvVAaKP9fJ8wG5RUu3AoDgYQAAoGAGSYT19Pb VCxMt06cAP7zQZ6AC5eXp3zeAweIevV96ryA1mB03qhB9X2lVowAUOFc24aVRTz7 wRoRjNQ20atzSy21C7yXDkvZ4uxfdrpIqpIVrI28e7XL+6CrhnAk621OvdeyEz5H orA21hPXoCNdnUPG5Ib20oopM87ptF5dwiWjOjA4MA4GA1UdDwEB/wQEAwIDCDAR BgNVHQ4ECgQIiDCSQ3FB/oEwEwYDVR0jBAwwCoAIgjqisiZ1WVswCQYHKoZIzjgE AwMvADAsAhQMtZ98TyqVkVqUJ3RJqaU7l2xqKgIUX997qRqeMjAkK88NHeNd95/2 Yos=
qKWnCxVIlNvPEqBMxhCaY6z9NK0ZFCmRef1U5wbIMPaR/g2Zdw7VZg==
betMfG/VMLdwNGdkspCrJSo092PltInklQisKd8ImQgeFMzjn73OpXhK0KJtB9IB 1xGjENZ8Yzu625ehhCZGGFK4mp8DkIE7Sfw7O+5UEqprE/cGrWL0bbcz0U7X2Evh 4/9va6h+DHAzmVYW7bqsa0WkiHkELRq44ORdSzyPUIwpGUCsOWyThsYfIn4uhIHQ NJVTKPRHTb5H5lsxNtobSeXACSYAHk/BmJM99h4IQ9Gh7bCkhkmZsIvo/lNOW+6r xtvLqHfYw9XhJe7hL0Q5EluMCBZQJ/Vx2r5lTXzBeonlurpzNdRa+ClKSVRUwKYH Vjemr/o+Y4e4r8gD3TVP3auVuUCmi3XLpj4WjOsPDcekzZUgXA/xuJ+7jHXjOEOK RViMiwIk0cqOa6s0Qg63EQ==
saml-0.4.6/xmlenc/corpus/encrypt-element-aes256-cbc-retrieved-kw-aes256.xml000066400000000000000000000034461415467341100262710ustar00rootroot00000000000000 ]> spade shovel Dig PLC, 1 First Ave, Dublin 1, Ireland cudR6Hg0xqhrOjbvQz4C/WOdHbcB7Duc+xFxbObkfkW6jXweDOf8Tq87FPbj5bby oCLbWqq3ap/zx/gN8Xv3Fj6fYUz3dIb1wzXy7B0/3me7i4fBHyGropflLi7iEag2 WU7aGJ0CA9/jQr6Td2qhH0CDU47QN9eK/PVMPPfLX1D1A90uK32wPn+SCysE58Q3 rCi7Jwo+OsrxT0qqjP82T3FjVi0i/dsnPb5GQWLE3/y7OsIuknuMRO4mWma+bO/m aAN9JNeom5Kn3IKHCK2+kyx+LsGo2daKxF7RF9QqlaA/imsMS4trRjZjYhgfgm96 kb1l4AI7VZcfRXwYdzLqKNHty6ZxbSQBMeEca0mEuIbor7IH34641a/BuFME/BLm MoVaLUCE0rg1e1U0S18UCg== jed bsL63D0hPN6EOyzdgfEmKsAAvoJiGM+Wp9a9KZM92IKdl7s3YSntRg== saml-0.4.6/xmlenc/corpus/encrypt-element-tripledes-cbc-kw-aes128.xml000066400000000000000000000031261415467341100252610ustar00rootroot00000000000000 ]> spade shovel Dig PLC, 1 First Ave, Dublin 1, Ireland job dV45TUpJbidb9iKa34xj1WVtTZ036cnqvym2TBJWR5c= bmExbDyrUQtsGjNBU7TRpMhOC76O/wBDWVMQML43lWNP0xp7QwVPce1XdbB4AVUn xxAuJh18jOd9UzPTzrJHrKWvsWP8Xp1m/HL3A1XhOUe+MEcFyJB9fXazhDmyaSYU SvieaPXcpzKWiHhZE8RKUyAYw9nU9wf2SEUgCVRuRPfsrXg4Uyr83VTn84LPe9sL dd2hMj4jhgHL86b7PTYBWdtrYXq0Jwzptuw+TZ1C706BAZDYNAiSTdx3J17Ey3ex IeIFBBIq8D8Gp7XiH4UxiDB6rtA2czox6+FCvaIsrGFaaw9XdzvhiZ3HxYROjprz qiXcJlZzG6j8yRdpHSjsDkN3w7XjEgRODieGx110rBytZcwtqb0zc6JTZH5DzoJy saml-0.4.6/xmlenc/corpus/encsig-hmac-sha256-dh.xml000066400000000000000000000126561415467341100215750ustar00rootroot00000000000000 60NvZvtdTB+7UnlLp/H24p7h4bs= 255LFQdP+eAK2aeuuLnz10pmaw4WEYb6TZa3B6H4z8c= bm9uY2U=

plygl2uMNc+jYtAZeKCZxPsmqa2z8DrOUa7L455iszN4SdPnL+LsZD47VJayvQY8 6D1J5arkwrbUzmhMAjBZsENPBgffRwwEBTjoq+gjSyZNIbxqsqnJdEyUElzn4kGE whECkJGnOaScacpjZg11h+gd0iBfY091bGHrCZrvr/8=

9jJXQijNovoq6QUBFcEUYwUvyTM= PerUZgMEMDTegMdTBRG9DPY5EHmwDxwzladdRcfvfdfU/9wlPzz5BUotMm730J9d lF6avWr929fzYsnIOUDeUOJpltXmrTYnvz5Bi6yuUu6bVwSfv7u4S+I/EM9ZB+eY 3fdF5TAMHD4tK86lw5APDrN2QnO1UMCwIvjOFatSOI0= Ulu6B1lCwajtIBnolqqgU+R1oxfye63DnI/iLM/Oe+Y8I/LMMaEmo3LmCU30m82r NyOUqgfnm97S0bT8ZhI8gvw0EyQJ87vhlUz4WcmddU/YlTi3gJHUClr2olmBmRCt m2vKo/BpoLGJ0Wg1eyWfo54+gCqbeNez/DmBGcBEEhM=
MIIDvjCCA36gAwIBAgIGAOxN39MIMAkGByqGSM44BAMwbjELMAkGA1UEBhMCSUUx DzANBgNVBAgTBkR1YmxpbjEkMCIGA1UEChMbQmFsdGltb3JlIFRlY2hub2xvZ2ll cyBMdGQuMREwDwYDVQQLEwhYL1NlY3VyZTEVMBMGA1UEAxMMVHJhbnNpZW50IENB MB4XDTAyMDIyODE3NTMxNloXDTAzMDIyODE3NTI1NFowbzELMAkGA1UEBhMCSUUx DzANBgNVBAgTBkR1YmxpbjEkMCIGA1UEChMbQmFsdGltb3JlIFRlY2hub2xvZ2ll cyBMdGQuMREwDwYDVQQLEwhYL1NlY3VyZTEWMBQGA1UEAxMNTWVybGluIEh1Z2hl czCCAiUwggGaBgcqhkjOPgIBMIIBjQKBgQCmXKCXa4w1z6Ni0Bl4oJnE+yaprbPw Os5RrsvjnmKzM3hJ0+cv4uxkPjtUlrK9BjzoPUnlquTCttTOaEwCMFmwQ08GB99H DAQFOOir6CNLJk0hvGqyqcl0TJQSXOfiQYTCEQKQkac5pJxpymNmDXWH6B3SIF9j T3VsYesJmu+v/wKBgD3q1GYDBDA03oDHUwURvQz2ORB5sA8cM5WnXUXH733X1P/c JT88+QVKLTJu99CfXZRemr1q/dvX82LJyDlA3lDiaZbV5q02J78+QYusrlLum1cE n7+7uEviPxDPWQfnmN33ReUwDBw+LSvOpcOQDw6zdkJztVDAsCL4zhWrUjiNAhUA 9jJXQijNovoq6QUBFcEUYwUvyTMCbQCs/HkLusCqHmY71JxUOFzy5fuWkPpWXJzx qU3oz1BfMZtPUqjpBnqU97M7VUEg+5pRG2txaHP8XNmB1bY0DCE88riDmHP7HqZB Z2gbaH2LxXDQDayb5GcPfn38eDcWvVAaKP9fJ8wG5RUu3AoDgYQAAoGAUlu6B1lC wajtIBnolqqgU+R1oxfye63DnI/iLM/Oe+Y8I/LMMaEmo3LmCU30m82rNyOUqgfn m97S0bT8ZhI8gvw0EyQJ87vhlUz4WcmddU/YlTi3gJHUClr2olmBmRCtm2vKo/Bp oLGJ0Wg1eyWfo54+gCqbeNez/DmBGcBEEhOjOjA4MA4GA1UdDwEB/wQEAwIDCDAR BgNVHQ4ECgQIgUAwB+9f1oIwEwYDVR0jBAwwCoAIgjqisiZ1WVswCQYHKoZIzjgE AwMvADAsAhQ41mCUsFhmxI58tytV8XEVZOCuUwIUVMe/HbUAH5PJ7aRoCNqa3fCI cU0=
MIIDvjCCA36gAwIBAgIGAOxN3+EMMAkGByqGSM44BAMwbjELMAkGA1UEBhMCSUUx DzANBgNVBAgTBkR1YmxpbjEkMCIGA1UEChMbQmFsdGltb3JlIFRlY2hub2xvZ2ll cyBMdGQuMREwDwYDVQQLEwhYL1NlY3VyZTEVMBMGA1UEAxMMVHJhbnNpZW50IENB MB4XDTAyMDIyODE3NTMxOVoXDTAzMDIyODE3NTI1NFowbzELMAkGA1UEBhMCSUUx DzANBgNVBAgTBkR1YmxpbjEkMCIGA1UEChMbQmFsdGltb3JlIFRlY2hub2xvZ2ll cyBMdGQuMREwDwYDVQQLEwhYL1NlY3VyZTEWMBQGA1UEAxMNTmlscmVtIFNlaGd1 aDCCAiUwggGaBgcqhkjOPgIBMIIBjQKBgQCmXKCXa4w1z6Ni0Bl4oJnE+yaprbPw Os5RrsvjnmKzM3hJ0+cv4uxkPjtUlrK9BjzoPUnlquTCttTOaEwCMFmwQ08GB99H DAQFOOir6CNLJk0hvGqyqcl0TJQSXOfiQYTCEQKQkac5pJxpymNmDXWH6B3SIF9j T3VsYesJmu+v/wKBgD3q1GYDBDA03oDHUwURvQz2ORB5sA8cM5WnXUXH733X1P/c JT88+QVKLTJu99CfXZRemr1q/dvX82LJyDlA3lDiaZbV5q02J78+QYusrlLum1cE n7+7uEviPxDPWQfnmN33ReUwDBw+LSvOpcOQDw6zdkJztVDAsCL4zhWrUjiNAhUA 9jJXQijNovoq6QUBFcEUYwUvyTMCbQCs/HkLusCqHmY71JxUOFzy5fuWkPpWXJzx qU3oz1BfMZtPUqjpBnqU97M7VUEg+5pRG2txaHP8XNmB1bY0DCE88riDmHP7HqZB Z2gbaH2LxXDQDayb5GcPfn38eDcWvVAaKP9fJ8wG5RUu3AoDgYQAAoGAGSYT19Pb VCxMt06cAP7zQZ6AC5eXp3zeAweIevV96ryA1mB03qhB9X2lVowAUOFc24aVRTz7 wRoRjNQ20atzSy21C7yXDkvZ4uxfdrpIqpIVrI28e7XL+6CrhnAk621OvdeyEz5H orA21hPXoCNdnUPG5Ib20oopM87ptF5dwiWjOjA4MA4GA1UdDwEB/wQEAwIDCDAR BgNVHQ4ECgQIiDCSQ3FB/oEwEwYDVR0jBAwwCoAIgjqisiZ1WVswCQYHKoZIzjgE AwMvADAsAhQMtZ98TyqVkVqUJ3RJqaU7l2xqKgIUX997qRqeMjAkK88NHeNd95/2 Yos=
saml-0.4.6/xmlenc/corpus/encsig-hmac-sha256-kw-tripledes-dh.xml000066400000000000000000000142071415467341100241770ustar00rootroot00000000000000 60NvZvtdTB+7UnlLp/H24p7h4bs= 9XBpYbFplNqqF7U/QtCHYE20U7oIxcyCr0L19MlenNo= bm9uY2U=

plygl2uMNc+jYtAZeKCZxPsmqa2z8DrOUa7L455iszN4SdPnL+LsZD47VJayvQY8 6D1J5arkwrbUzmhMAjBZsENPBgffRwwEBTjoq+gjSyZNIbxqsqnJdEyUElzn4kGE whECkJGnOaScacpjZg11h+gd0iBfY091bGHrCZrvr/8=

9jJXQijNovoq6QUBFcEUYwUvyTM= PerUZgMEMDTegMdTBRG9DPY5EHmwDxwzladdRcfvfdfU/9wlPzz5BUotMm730J9d lF6avWr929fzYsnIOUDeUOJpltXmrTYnvz5Bi6yuUu6bVwSfv7u4S+I/EM9ZB+eY 3fdF5TAMHD4tK86lw5APDrN2QnO1UMCwIvjOFatSOI0= Ulu6B1lCwajtIBnolqqgU+R1oxfye63DnI/iLM/Oe+Y8I/LMMaEmo3LmCU30m82r NyOUqgfnm97S0bT8ZhI8gvw0EyQJ87vhlUz4WcmddU/YlTi3gJHUClr2olmBmRCt m2vKo/BpoLGJ0Wg1eyWfo54+gCqbeNez/DmBGcBEEhM=
MIIDvjCCA36gAwIBAgIGAOxN39MIMAkGByqGSM44BAMwbjELMAkGA1UEBhMCSUUx DzANBgNVBAgTBkR1YmxpbjEkMCIGA1UEChMbQmFsdGltb3JlIFRlY2hub2xvZ2ll cyBMdGQuMREwDwYDVQQLEwhYL1NlY3VyZTEVMBMGA1UEAxMMVHJhbnNpZW50IENB MB4XDTAyMDIyODE3NTMxNloXDTAzMDIyODE3NTI1NFowbzELMAkGA1UEBhMCSUUx DzANBgNVBAgTBkR1YmxpbjEkMCIGA1UEChMbQmFsdGltb3JlIFRlY2hub2xvZ2ll cyBMdGQuMREwDwYDVQQLEwhYL1NlY3VyZTEWMBQGA1UEAxMNTWVybGluIEh1Z2hl czCCAiUwggGaBgcqhkjOPgIBMIIBjQKBgQCmXKCXa4w1z6Ni0Bl4oJnE+yaprbPw Os5RrsvjnmKzM3hJ0+cv4uxkPjtUlrK9BjzoPUnlquTCttTOaEwCMFmwQ08GB99H DAQFOOir6CNLJk0hvGqyqcl0TJQSXOfiQYTCEQKQkac5pJxpymNmDXWH6B3SIF9j T3VsYesJmu+v/wKBgD3q1GYDBDA03oDHUwURvQz2ORB5sA8cM5WnXUXH733X1P/c JT88+QVKLTJu99CfXZRemr1q/dvX82LJyDlA3lDiaZbV5q02J78+QYusrlLum1cE n7+7uEviPxDPWQfnmN33ReUwDBw+LSvOpcOQDw6zdkJztVDAsCL4zhWrUjiNAhUA 9jJXQijNovoq6QUBFcEUYwUvyTMCbQCs/HkLusCqHmY71JxUOFzy5fuWkPpWXJzx qU3oz1BfMZtPUqjpBnqU97M7VUEg+5pRG2txaHP8XNmB1bY0DCE88riDmHP7HqZB Z2gbaH2LxXDQDayb5GcPfn38eDcWvVAaKP9fJ8wG5RUu3AoDgYQAAoGAUlu6B1lC wajtIBnolqqgU+R1oxfye63DnI/iLM/Oe+Y8I/LMMaEmo3LmCU30m82rNyOUqgfn m97S0bT8ZhI8gvw0EyQJ87vhlUz4WcmddU/YlTi3gJHUClr2olmBmRCtm2vKo/Bp oLGJ0Wg1eyWfo54+gCqbeNez/DmBGcBEEhOjOjA4MA4GA1UdDwEB/wQEAwIDCDAR BgNVHQ4ECgQIgUAwB+9f1oIwEwYDVR0jBAwwCoAIgjqisiZ1WVswCQYHKoZIzjgE AwMvADAsAhQ41mCUsFhmxI58tytV8XEVZOCuUwIUVMe/HbUAH5PJ7aRoCNqa3fCI cU0=
MIIDvjCCA36gAwIBAgIGAOxN3+EMMAkGByqGSM44BAMwbjELMAkGA1UEBhMCSUUx DzANBgNVBAgTBkR1YmxpbjEkMCIGA1UEChMbQmFsdGltb3JlIFRlY2hub2xvZ2ll cyBMdGQuMREwDwYDVQQLEwhYL1NlY3VyZTEVMBMGA1UEAxMMVHJhbnNpZW50IENB MB4XDTAyMDIyODE3NTMxOVoXDTAzMDIyODE3NTI1NFowbzELMAkGA1UEBhMCSUUx DzANBgNVBAgTBkR1YmxpbjEkMCIGA1UEChMbQmFsdGltb3JlIFRlY2hub2xvZ2ll cyBMdGQuMREwDwYDVQQLEwhYL1NlY3VyZTEWMBQGA1UEAxMNTmlscmVtIFNlaGd1 aDCCAiUwggGaBgcqhkjOPgIBMIIBjQKBgQCmXKCXa4w1z6Ni0Bl4oJnE+yaprbPw Os5RrsvjnmKzM3hJ0+cv4uxkPjtUlrK9BjzoPUnlquTCttTOaEwCMFmwQ08GB99H DAQFOOir6CNLJk0hvGqyqcl0TJQSXOfiQYTCEQKQkac5pJxpymNmDXWH6B3SIF9j T3VsYesJmu+v/wKBgD3q1GYDBDA03oDHUwURvQz2ORB5sA8cM5WnXUXH733X1P/c JT88+QVKLTJu99CfXZRemr1q/dvX82LJyDlA3lDiaZbV5q02J78+QYusrlLum1cE n7+7uEviPxDPWQfnmN33ReUwDBw+LSvOpcOQDw6zdkJztVDAsCL4zhWrUjiNAhUA 9jJXQijNovoq6QUBFcEUYwUvyTMCbQCs/HkLusCqHmY71JxUOFzy5fuWkPpWXJzx qU3oz1BfMZtPUqjpBnqU97M7VUEg+5pRG2txaHP8XNmB1bY0DCE88riDmHP7HqZB Z2gbaH2LxXDQDayb5GcPfn38eDcWvVAaKP9fJ8wG5RUu3AoDgYQAAoGAGSYT19Pb VCxMt06cAP7zQZ6AC5eXp3zeAweIevV96ryA1mB03qhB9X2lVowAUOFc24aVRTz7 wRoRjNQ20atzSy21C7yXDkvZ4uxfdrpIqpIVrI28e7XL+6CrhnAk621OvdeyEz5H orA21hPXoCNdnUPG5Ib20oopM87ptF5dwiWjOjA4MA4GA1UdDwEB/wQEAwIDCDAR BgNVHQ4ECgQIiDCSQ3FB/oEwEwYDVR0jBAwwCoAIgjqisiZ1WVswCQYHKoZIzjgE AwMvADAsAhQMtZ98TyqVkVqUJ3RJqaU7l2xqKgIUX997qRqeMjAkK88NHeNd95/2 Yos=
2s+2ji8opL0SLKziiyNZ+mZ8Ibfu7cTwe4C0MmyarYDwGmsiRSqff8trHUwa+njZ
saml-0.4.6/xmlenc/corpus/encsig-hmac-sha256-rsa-1_5.xml000066400000000000000000000045561415467341100223510ustar00rootroot00000000000000 60NvZvtdTB+7UnlLp/H24p7h4bs= 9XBpYbFplNqqF7U/QtCHYE20U7oIxcyCr0L19MlenNo= MIICkjCCAfugAwIBAgIGAOxN32E+MA0GCSqGSIb3DQEBBQUAMG4xCzAJBgNVBAYT AklFMQ8wDQYDVQQIEwZEdWJsaW4xJDAiBgNVBAoTG0JhbHRpbW9yZSBUZWNobm9s b2dpZXMgTHRkLjERMA8GA1UECxMIWC9TZWN1cmUxFTATBgNVBAMTDFRyYW5zaWVu dCBDQTAeFw0wMjAyMjgxNzUyNDZaFw0wMzAyMjgxNzUyNDBaMG8xCzAJBgNVBAYT AklFMQ8wDQYDVQQIEwZEdWJsaW4xJDAiBgNVBAoTG0JhbHRpbW9yZSBUZWNobm9s b2dpZXMgTHRkLjERMA8GA1UECxMIWC9TZWN1cmUxFjAUBgNVBAMTDU1lcmxpbiBI dWdoZXMwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAORdNSxbNFWlQeNsOlYJ 9gN9eZD+rguRqKhmhOm7i63VDd5ALm2APXhqAmGBPzLN5jlL9g2XALK5WSO4XKjJ McVfYg4+nPuOeHgqdD4HUgf19j/6SaTMcmDFJQMmx1Qw+Aakq3mGcSfvOJcBZctz a50VucfCGL1NdfBEcaL3BnhjAgMBAAGjOjA4MA4GA1UdDwEB/wQEAwIFoDARBgNV HQ4ECgQIjFG0ZGNyvNswEwYDVR0jBAwwCoAIhJXVlhr6O4wwDQYJKoZIhvcNAQEF BQADgYEAXzG7x5aCJYRusTbmuZqhidGM5iiA9+RmZ4JTPDEgbeiTiJROxpr+ZjnA TmsDKrCpqNUiHWjmsKEArYQp8R/KjdKl/pVe3jUvTxb0YZ+li/7k0GQ5LyRT/K4c 2SgyLlyBPhpMq+z3g4P2egVRaZbxsLuKQILf7MIV/X5iAEBzu1w= BRhPOKN/KLCih2Q2RoxQiaV0s1FfpOM+kisl9MwRSPow5CyX91rBVfoWpP/Qq1T3 Rj/f0gVoJyE008uLic4X/S4spnudlOzTkVB6bUzoBt4j+z4hEq/cIfHqVdEJ+lN0 iu1sJk3k6ESl22OWEqQB7Rl5sAdhFPOqXsnLUNWmqA8= saml-0.4.6/xmlenc/corpus/encsig-hmac-sha256-rsa-oaep-mgf1p.xml000066400000000000000000000051061415467341100237110ustar00rootroot00000000000000 60NvZvtdTB+7UnlLp/H24p7h4bs= 9XBpYbFplNqqF7U/QtCHYE20U7oIxcyCr0L19MlenNo= MTIzNDU2Nzg= MIICkjCCAfugAwIBAgIGAOxN32E+MA0GCSqGSIb3DQEBBQUAMG4xCzAJBgNVBAYT AklFMQ8wDQYDVQQIEwZEdWJsaW4xJDAiBgNVBAoTG0JhbHRpbW9yZSBUZWNobm9s b2dpZXMgTHRkLjERMA8GA1UECxMIWC9TZWN1cmUxFTATBgNVBAMTDFRyYW5zaWVu dCBDQTAeFw0wMjAyMjgxNzUyNDZaFw0wMzAyMjgxNzUyNDBaMG8xCzAJBgNVBAYT AklFMQ8wDQYDVQQIEwZEdWJsaW4xJDAiBgNVBAoTG0JhbHRpbW9yZSBUZWNobm9s b2dpZXMgTHRkLjERMA8GA1UECxMIWC9TZWN1cmUxFjAUBgNVBAMTDU1lcmxpbiBI dWdoZXMwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAORdNSxbNFWlQeNsOlYJ 9gN9eZD+rguRqKhmhOm7i63VDd5ALm2APXhqAmGBPzLN5jlL9g2XALK5WSO4XKjJ McVfYg4+nPuOeHgqdD4HUgf19j/6SaTMcmDFJQMmx1Qw+Aakq3mGcSfvOJcBZctz a50VucfCGL1NdfBEcaL3BnhjAgMBAAGjOjA4MA4GA1UdDwEB/wQEAwIFoDARBgNV HQ4ECgQIjFG0ZGNyvNswEwYDVR0jBAwwCoAIhJXVlhr6O4wwDQYJKoZIhvcNAQEF BQADgYEAXzG7x5aCJYRusTbmuZqhidGM5iiA9+RmZ4JTPDEgbeiTiJROxpr+ZjnA TmsDKrCpqNUiHWjmsKEArYQp8R/KjdKl/pVe3jUvTxb0YZ+li/7k0GQ5LyRT/K4c 2SgyLlyBPhpMq+z3g4P2egVRaZbxsLuKQILf7MIV/X5iAEBzu1w= NGIOL9UzhGwPYvVzbBxOGzxXfTIkzIsmtNSkWA03p64aS41vVA0sKWvcr/79Nf7T 6RdA61TmwOKa5GDUYRumEadC7Z0zKFDKcuN78iJzlj2WwVqr5vBx14X2BSVW+de1 UTmXRZFRosFOk9etvD7Lm1V+kqIxqSrod68G8gJvGrY= saml-0.4.6/xmlenc/corpus/encsig-ripemd160-hmac-ripemd160-kw-tripledes.xml000066400000000000000000000020471415467341100260110ustar00rootroot00000000000000 ixv9ZpIiqEzBC3Uztm5Rl6tXd9Q= kwV4uELL96oFm8/+VGzq+xAOgUg= bob gHMpx5iF7+KXtNHLasZrkcLHn8Ti4rxUjCIRK+IcgbQir6FUsQ/uxQ3o8enEMWq1 saml-0.4.6/xmlenc/corpus/encsig-sha256-hmac-sha256-kw-aes128.xml000066400000000000000000000020661415467341100236240ustar00rootroot00000000000000 eI1OLVStn6Z4q7Byq8XGUJ4bce1LMSlanI6o+SvYzt0= cOQGJE3d3fXi1BIfdvr1v6tz/4lt9xGznfyDPXEvc4Q= job rPnY/XoSGCbuwy7vpslf29rs9dbvSCmGFOjEs3LT6g/qyZjfDA+2fQ== saml-0.4.6/xmlenc/corpus/encsig-sha384-hmac-sha384-kw-aes192.xml000066400000000000000000000021441415467341100236260ustar00rootroot00000000000000 bWetGDV3M5oEiecfEHILQxVQRa1XgdY37VH8eWi9yVVx7Rr7UNhk+v6Jk7sMNPoA iEjhOJoKiwsOBduxHj7bxILSsl6TLhNO3w/vlRcw9RZAe24HIxLRfhj4Xqsz1Orr jeb 19D633XVohP6UJvaVRAhJek+ahtM3gOiVs6nZyAasDEb+WCUQOcWZw== saml-0.4.6/xmlenc/corpus/encsig-sha512-hmac-sha512-kw-aes256.xml000066400000000000000000000022231415467341100236070ustar00rootroot00000000000000 c8+KT9+qCSbNpdZm7/dp9Mv/lgF51ATycY0Ttz/0bw2p5nvnmeEgQpIPw5HhVJ9Ku6dDf0RKVVR/CsYvPGfnEg== zB8ZUo9bQxzxnxW2aZ217eu//1e5xHB6RlfEOFOlx1l5PIhadKAlQo0z1D9B2HVU Kj4StSnlUsrvDo2BxgiAoA== jed tPCC89jQShB+WDINCdRfKgf8wTlAx8xRXD73RmEHPBfix8zS1N82KQ== saml-0.4.6/xmlenc/crashers/000077500000000000000000000000001415467341100155635ustar00rootroot00000000000000saml-0.4.6/xmlenc/crashers/255c4741516851ba13e8151340541d1d247a1d5f000066400000000000000000000004661415467341100224670ustar00rootroot000000000000000 saml-0.4.6/xmlenc/crashers/464766e5a802f545c15806de53b161b26f8548a4000066400000000000000000000001211415467341100225710ustar00rootroot00000000000000saml-0.4.6/xmlenc/crashers/4d7686c884eb4d4e41d69446018d7c3122c9bc11000066400000000000000000000003211415467341100227330ustar00rootroot000000000000000 saml-0.4.6/xmlenc/crashers/5837247e7ed40387b811a2a1be52cd91b873d349000066400000000000000000000022611415467341100227350ustar00rootroot00000000000000MIICkjCCAfugAwIBAgIGAOxN32E+MA0GCSqGSIb3DQEBBQUAMG4xCzAJBgNVBAYTAklFMQ8wDQYDVQQIEwZEdWJsaW4xJDAiBgNVBAoTG0JhbHRpbW9yZSBUZWNobm9sb2dpZXMgTHRkLjERMA8GA1UECxMIWC9TZWN1cmUxFTATBgNVBAMTDFRyYW5zaWVudCBDQTAeFw0wMjAyMjgxNzUyNDZaFw0wMzAyMjgxNzUyNDBaMG8xCzAJBgNVBAYTAklFMQ8wDQYDVQQIEwZEdWJsaW4xJDAiBgNVBAoTG0JhbHRpbW9yZSBUZWNobm9sb2dpZXMgTHRkLjERMA8GA1UECxMIWC9TZWN1cmUxFjAUBgNVBAMTDU1lcmxpbiBIdWdoZXMwgZ8wDQYGKoZIhvcNAQEBBQADgY0AMIGJAoGBAORdNSxbNFWlQeNsOlYJ9gN9eZD+rguRqKhmhOm7i63VDd5ALm2APXhqAmGBPzLN5jlL9g2XALK5WSO4XKjJMcVfYg4+nPuOeHgqdD4HUgf19j/6SaTMcmDFJQMmx1Qw+Aakq3mGcSfvOJcBZctza50VucfCGL1NdfBEcaL3BnhjAgMBAAGjOjA4MA4GA1UdDwEB/wQEAwIFoDARBgNVHQ4ECgQIjFG0ZGNyvNswEwYDVR0jBAwwCoAIhJXVlhr6O4wwDQYJKoZIhvcNAQEFBQADgYEAXzG7x5aCJYRusTbmuZqhidGM5iiA9+RmZ4JTPDEgbeiTiJROxpr+ZjnATmsDKrCpqNUiHWjmsKEArYQp8R/KjdKl/pVe3jUvTxb0YZ+li/7k0GQ5LyRT/K4c2SgyLlyBPhpMq+z3g4P2egVRaZbxsLuKQILf7MIV/X5iAEBzu1w= saml-0.4.6/xmlenc/crashers/655d116d6e8b8d9c1de179459ee71293a6151792000066400000000000000000000021071415467341100226770ustar00rootroot00000000000000MIICkjCCAfugAwIBAgIGAOxN32E+MA0GCSqGEIb3DQEBBQUAMG4xCzAJBgNVBAYTAklFMQ8wDQYDVQQIEwZEdWJsaW4xJDAiBgNVBAoTG0JhbHRpbW9yZSBUZWNobm9sb2dpZXMgTHRkLjERMA8GA1UECxMIWC9TZWN1cmUxFTATBgNVBAMTDFRyYW5zaWVudCBDQTAeFw0wMjAyMjgxNzUyNDZaFw0wMzAyMjgxNzUyNDBaMG8xCzAJBgNVBAYTAklFMQ8wDQYDVQQIEwZEdWJsaW4xJDAiBgNVBAoTG0JhbHRpbW9yZSBUZWNobm9sb2dpZXMgTHRkLjERMA8GA1UECxMIWC9TZWN1cmUxFjAUBgNVBAMTDU1lcmxpbiBIdWdoZXMwgZ8wDQYJKoZIBvcNAQEBBQADgY0ACIGJAoGBAORdNSxbNFWlQeNsOlYJ9gN9eZD+rguRqKhmhOm7i63VDd5ALm2APXhqAmGBPzLN5jlL9g2XALK5WSO4XKjJMcVfYg4+nPuOeHgqdD4HUgf19j/6SaTMcmDFJQMmx1Qw+Aakq3mGcSfvOJcBZctza50VucfCGL1NdfBEcaL3BnhjAgMBAAGjOjA4MA4GA1UdDwEB/wQEAwIFoDARBgNVHQ4ECgQIjFG0ZGNyvNswEwYDVR0jBAwwCoAIhJXVlhr6O4wwDQYJKoZIhvcNAQEFBQADgYEAXzG7x5aCJYRusTbmuZqhidGM5iiA9+RmZ4JTPDEgbeiTiJROxpr+ZjnATmsDKrCpqNUiHWjmsKEArYQp8R/KjdKl/pVe3jUvTxb0YZ+li/7k0GQ5LyRT/K4c2SgyLlyhPhpMq+z3g4P2egVRaZbxsLuKQILf7MIV/X5iAEBzu1w= saml-0.4.6/xmlenc/crashers/b74dc2d61db80b70f24a2ac8525a02d8d0282cbf000066400000000000000000000002661415467341100232620ustar00rootroot00000000000000saml-0.4.6/xmlenc/crashers/da39a3ee5e6b4b0d3255bfef95601890afd80709000066400000000000000000000000001415467341100232210ustar00rootroot00000000000000saml-0.4.6/xmlenc/decrypt.go000066400000000000000000000073501415467341100157570ustar00rootroot00000000000000package xmlenc import ( // nolint: gas "crypto/rsa" "crypto/x509" "encoding/base64" "encoding/pem" "errors" "fmt" "strings" "github.com/beevik/etree" ) // ErrAlgorithmNotImplemented is returned when encryption used is not // supported. type ErrAlgorithmNotImplemented string func (e ErrAlgorithmNotImplemented) Error() string { return "algorithm is not implemented: " + string(e) } // ErrCannotFindRequiredElement is returned by Decrypt when a required // element cannot be found. type ErrCannotFindRequiredElement string func (e ErrCannotFindRequiredElement) Error() string { return "cannot find required element: " + string(e) } // ErrIncorrectTag is returned when Decrypt is passed an element which // is neither an EncryptedType nor an EncryptedKey var ErrIncorrectTag = fmt.Errorf("tag must be an EncryptedType or EncryptedKey") // ErrIncorrectKeyLength is returned when the fixed length key is not // of the required length. type ErrIncorrectKeyLength int func (e ErrIncorrectKeyLength) Error() string { return fmt.Sprintf("expected key to be %d bytes", int(e)) } // ErrIncorrectKeyType is returned when the key is not the correct type type ErrIncorrectKeyType string func (e ErrIncorrectKeyType) Error() string { return fmt.Sprintf("expected key to be %s", string(e)) } // Decrypt decrypts the encrypted data using the provided key. If the // data are encrypted using AES or 3DEC, then the key should be a []byte. // If the data are encrypted with PKCS1v15 or RSA-OAEP-MGF1P then key should // be a *rsa.PrivateKey. func Decrypt(key interface{}, ciphertextEl *etree.Element) ([]byte, error) { encryptionMethodEl := ciphertextEl.FindElement("./EncryptionMethod") if encryptionMethodEl == nil { return nil, ErrCannotFindRequiredElement("EncryptionMethod") } algorithm := encryptionMethodEl.SelectAttrValue("Algorithm", "") decrypter, ok := decrypters[algorithm] if !ok { return nil, ErrAlgorithmNotImplemented(algorithm) } return decrypter.Decrypt(key, ciphertextEl) } func getCiphertext(encryptedKey *etree.Element) ([]byte, error) { ciphertextEl := encryptedKey.FindElement("./CipherData/CipherValue") if ciphertextEl == nil { return nil, fmt.Errorf("cannot find CipherData element containing a CipherValue element") } ciphertext, err := base64.StdEncoding.DecodeString(strings.TrimSpace(ciphertextEl.Text())) if err != nil { return nil, err } return ciphertext, nil } func validateRSAKeyIfPresent(key interface{}, encryptedKey *etree.Element) (*rsa.PrivateKey, error) { rsaKey, ok := key.(*rsa.PrivateKey) if !ok { return nil, errors.New("expected key to be a *rsa.PrivateKey") } // extract and verify that the public key matches the certificate // this section is included to either let the service know up front // if the key will work, or let the service provider know which key // to use to decrypt the message. Either way, verification is not // security-critical. if el := encryptedKey.FindElement("./KeyInfo/X509Data/X509Certificate"); el != nil { certPEMbuf := el.Text() certPEMbuf = "-----BEGIN CERTIFICATE-----\n" + certPEMbuf + "\n-----END CERTIFICATE-----\n" certPEM, _ := pem.Decode([]byte(certPEMbuf)) if certPEM == nil { return nil, fmt.Errorf("invalid certificate") } cert, err := x509.ParseCertificate(certPEM.Bytes) if err != nil { return nil, err } pubKey, ok := cert.PublicKey.(*rsa.PublicKey) if !ok { return nil, fmt.Errorf("expected certificate to be an *rsa.PublicKey") } if rsaKey.N.Cmp(pubKey.N) != 0 || rsaKey.E != pubKey.E { return nil, fmt.Errorf("certificate does not match provided key") } } else if el = encryptedKey.FindElement("./KeyInfo/X509Data/X509IssuerSerial"); el != nil { // TODO: determine how to validate the issuer serial information } return rsaKey, nil } saml-0.4.6/xmlenc/decrypt_test.go000066400000000000000000000071701415467341100170160ustar00rootroot00000000000000package xmlenc import ( "crypto/x509" "encoding/pem" "testing" "gotest.tools/assert" is "gotest.tools/assert/cmp" "gotest.tools/golden" "github.com/beevik/etree" ) func TestCanDecrypt(t *testing.T) { t.Run("CBC", func(t *testing.T) { doc := etree.NewDocument() err := doc.ReadFromBytes(golden.Get(t, "input.xml")) assert.Check(t, err) keyPEM := "-----BEGIN RSA PRIVATE KEY-----\nMIICXgIBAAKBgQDU8wdiaFmPfTyRYuFlVPi866WrH/2JubkHzp89bBQopDaLXYxi\n3PTu3O6Q/KaKxMOFBqrInwqpv/omOGZ4ycQ51O9I+Yc7ybVlW94lTo2gpGf+Y/8E\nPsVbnZaFutRctJ4dVIp9aQ2TpLiGT0xX1OzBO/JEgq9GzDRf+B+eqSuglwIDAQAB\nAoGBAMuy1eN6cgFiCOgBsB3gVDdTKpww87Qk5ivjqEt28SmXO13A1KNVPS6oQ8SJ\nCT5Azc6X/BIAoJCURVL+LHdqebogKljhH/3yIel1kH19vr4E2kTM/tYH+qj8afUS\nJEmArUzsmmK8ccuNqBcllqdwCZjxL4CHDUmyRudFcHVX9oyhAkEA/OV1OkjM3CLU\nN3sqELdMmHq5QZCUihBmk3/N5OvGdqAFGBlEeewlepEVxkh7JnaNXAXrKHRVu/f/\nfbCQxH+qrwJBANeQERF97b9Sibp9xgolb749UWNlAdqmEpmlvmS202TdcaaT1msU\n4rRLiQN3X9O9mq4LZMSVethrQAdX1whawpkCQQDk1yGf7xZpMJ8F4U5sN+F4rLyM\nRq8Sy8p2OBTwzCUXXK+fYeXjybsUUMr6VMYTRP2fQr/LKJIX+E5ZxvcIyFmDAkEA\nyfjNVUNVaIbQTzEbRlRvT6MqR+PTCefC072NF9aJWR93JimspGZMR7viY6IM4lrr\nvBkm0F5yXKaYtoiiDMzlOQJADqmEwXl0D72ZG/2KDg8b4QZEmC9i5gidpQwJXUc6\nhU+IVQoLxRq0fBib/36K9tcrrO5Ba4iEvDcNY+D8yGbUtA==\n-----END RSA PRIVATE KEY-----\n" b, _ := pem.Decode([]byte(keyPEM)) key, err := x509.ParsePKCS1PrivateKey(b.Bytes) assert.Check(t, err) el := doc.Root().FindElement("//EncryptedKey") buf, err := Decrypt(key, el) assert.Check(t, err) assert.Check(t, is.DeepEqual([]byte{0xc, 0x70, 0xa2, 0xc8, 0x15, 0x74, 0x89, 0x3f, 0x36, 0xd2, 0x7c, 0x14, 0x2a, 0x9b, 0xaa, 0xd9}, buf)) el = doc.Root().FindElement("//EncryptedData") buf, err = Decrypt(key, el) assert.Check(t, err) golden.Assert(t, string(buf), "plaintext.xml") }) t.Run("GCM", func(t *testing.T) { doc := etree.NewDocument() err := doc.ReadFromBytes(golden.Get(t, "input_gcm.xml")) assert.Check(t, err) keyPEM := golden.Get(t, "cert.key") b, _ := pem.Decode(keyPEM) key, err := x509.ParsePKCS8PrivateKey(b.Bytes) assert.Check(t, err) el := doc.Root().FindElement("//EncryptedKey") _, err = Decrypt(key, el) assert.Check(t, err) el = doc.Root().FindElement("//EncryptedData") _, err = Decrypt(key, el) assert.Check(t, err) }) } func TestCanDecryptWithoutCertificate(t *testing.T) { t.Run("CBC", func(t *testing.T) { doc := etree.NewDocument() err := doc.ReadFromBytes(golden.Get(t, "input.xml")) assert.Check(t, err) el := doc.FindElement("//ds:X509Certificate") el.Parent().RemoveChild(el) keyPEM := golden.Get(t, "key.pem") b, _ := pem.Decode(keyPEM) key, err := x509.ParsePKCS1PrivateKey(b.Bytes) assert.Check(t, err) el = doc.Root().FindElement("//EncryptedKey") buf, err := Decrypt(key, el) assert.Check(t, err) assert.Check(t, is.DeepEqual([]byte{0xc, 0x70, 0xa2, 0xc8, 0x15, 0x74, 0x89, 0x3f, 0x36, 0xd2, 0x7c, 0x14, 0x2a, 0x9b, 0xaa, 0xd9}, buf)) el = doc.Root().FindElement("//EncryptedData") buf, err = Decrypt(key, el) assert.Check(t, err) golden.Assert(t, string(buf), "plaintext.xml") }) t.Run("GCM", func(t *testing.T) { doc := etree.NewDocument() err := doc.ReadFromBytes(golden.Get(t, "input_gcm.xml")) assert.Check(t, err) el := doc.FindElement("//ds:X509Certificate") el.Parent().RemoveChild(el) keyPEM := golden.Get(t, "cert.key") b, _ := pem.Decode(keyPEM) key, err := x509.ParsePKCS8PrivateKey(b.Bytes) assert.Check(t, err) el = doc.Root().FindElement("//EncryptedKey") _, err = Decrypt(key, el) assert.Check(t, err) el = doc.Root().FindElement("//EncryptedData") _, err = Decrypt(key, el) assert.Check(t, err) //assertion.NotNil(t, plaintext) }) } saml-0.4.6/xmlenc/digest.go000066400000000000000000000022421415467341100155570ustar00rootroot00000000000000package xmlenc import ( "crypto/sha1" //nolint:gosec // required for protocol support "crypto/sha256" "crypto/sha512" "hash" "golang.org/x/crypto/ripemd160" ) type digestMethod struct { algorithm string hash func() hash.Hash } func (dm digestMethod) Algorithm() string { return dm.algorithm } func (dm digestMethod) Hash() hash.Hash { return dm.hash() } var ( // SHA1 implements the SHA-1 digest method (which is considered insecure) SHA1 = digestMethod{ algorithm: "http://www.w3.org/2000/09/xmldsig#sha1", hash: sha1.New, } // SHA256 implements the SHA-256 digest method SHA256 = digestMethod{ algorithm: "http://www.w3.org/2000/09/xmldsig#sha256", hash: sha256.New, } // SHA512 implements the SHA-512 digest method SHA512 = digestMethod{ algorithm: "http://www.w3.org/2000/09/xmldsig#sha512", hash: sha512.New, } // RIPEMD160 implements the RIPEMD160 digest method RIPEMD160 = digestMethod{ algorithm: "http://www.w3.org/2000/09/xmldsig#ripemd160", hash: ripemd160.New, } ) func init() { RegisterDigestMethod(SHA1) RegisterDigestMethod(SHA256) RegisterDigestMethod(SHA512) RegisterDigestMethod(RIPEMD160) } saml-0.4.6/xmlenc/encrypt_test.go000066400000000000000000000026201415467341100170230ustar00rootroot00000000000000package xmlenc import ( "crypto/x509" "encoding/pem" "math/rand" "testing" "github.com/beevik/etree" "gotest.tools/assert" "gotest.tools/golden" ) func TestCanEncryptOAEP(t *testing.T) { t.Run("CBC", func(t *testing.T) { RandReader = rand.New(rand.NewSource(0)) //nolint:gosec // deterministic random numbers for tests pemBlock, _ := pem.Decode(golden.Get(t, "cert.pem")) certificate, err := x509.ParseCertificate(pemBlock.Bytes) assert.Check(t, err) e := OAEP() e.BlockCipher = AES128CBC e.DigestMethod = &SHA1 el, err := e.Encrypt(certificate, golden.Get(t, "plaintext.xml"), nil) assert.Check(t, err) doc := etree.NewDocument() doc.SetRoot(el) doc.IndentTabs() ciphertext, _ := doc.WriteToString() golden.Assert(t, ciphertext, "ciphertext.xml") }) t.Run("GCM", func(t *testing.T) { RandReader = rand.New(rand.NewSource(0)) //nolint:gosec // deterministic random numbers for tests cert := golden.Get(t, "cert.cert") b, _ := pem.Decode(cert) certificate, err := x509.ParseCertificate(b.Bytes) assert.Check(t, err) e := OAEP() e.BlockCipher = AES128GCM e.DigestMethod = &SHA1 el, err := e.Encrypt(certificate, golden.Get(t, "plaintext_gcm.xml"), []byte("1234567890AZ")) assert.Check(t, err) doc := etree.NewDocument() doc.SetRoot(el) doc.Indent(4) ciphertext, _ := doc.WriteToString() golden.Assert(t, ciphertext, "ciphertext_gcm.xml") }) } saml-0.4.6/xmlenc/fuzz.go000066400000000000000000000026611415467341100153030ustar00rootroot00000000000000package xmlenc import ( "crypto/rsa" "crypto/x509" "encoding/pem" "github.com/beevik/etree" ) var testKey = func() *rsa.PrivateKey { const keyStr = `-----BEGIN RSA PRIVATE KEY----- MIICXQIBAAKBgQDkXTUsWzRVpUHjbDpWCfYDfXmQ/q4LkaioZoTpu4ut1Q3eQC5t gD14agJhgT8yzeY5S/YNlwCyuVkjuFyoyTHFX2IOPpz7jnh4KnQ+B1IH9fY/+kmk zHJgxSUDJsdUMPgGpKt5hnEn7ziXAWXLc2udFbnHwhi9TXXwRHGi9wZ4YwIDAQAB AoGBALNTnlXeqRI4W61DZ+v4ln/XIIeD9xiOoWrcVrNU2zL+g41ryQmkEqFkXcpD vGUg2xFTXTz+v0WZ1y39sIW6uKFRYUfaNsF6iVfGAyx1VWK/jgtPnCWDQy26Eby0 BqpbZRy1a6MLYVEG/5bvZE01CDV4XttpTrNX91WWcYGduJxBAkEA6ED1ZOqIzBpu c2KAo+bWmroCH8+cSDk0gVq6bnRB+EEhRCmo/VgvndWLxfexdGmDIOAIisB06N5a GzBSCaEY/QJBAPu2cNvuuBNLwrlxPCwOEpIHYT4gJq8UMtg6O6N+u++nYCGhK6uo VCmrKY+UewyNIcsLZF0jsNI2qJjiU1vQxN8CQQDfQJnigMQwlfO3/Ga1po6Buu2R 0IpkroB3G1R8GkrTrR+iGv2zUdKrwHsUOC2fPlFrB4+OeMOomRw6aG9jjDStAkB1 ztiZhuvuVAoKIv5HnDqC0CNqIUAZtzlozDB3f+xT6SFr+/Plfn4Nlod4JMVGhZNo ZaeOlBLBAEX+cAcVtOs/AkBicZOAPv84ABmFfyhXhYaAuacaJLq//jg+t+URUOg+ XZS9naRmawEQxOkZQVoMeKgvu05+V4MniFqdQBINIkr5 -----END RSA PRIVATE KEY-----` b, _ := pem.Decode([]byte(keyStr)) k, err := x509.ParsePKCS1PrivateKey(b.Bytes) if err != nil { panic(err) } return k }() // Fuzz is the go-fuzz fuzzing function func Fuzz(data []byte) int { doc := etree.NewDocument() if err := doc.ReadFromBytes(data); err != nil { return 0 } if doc.Root() == nil { return 0 } if _, err := Decrypt(testKey, doc.Root()); err != nil { return 0 } return 1 } saml-0.4.6/xmlenc/fuzz_test.go000066400000000000000000000010661415467341100163400ustar00rootroot00000000000000// +build gofuzz package xmlenc import ( "io/ioutil" "testing" "strings" ) func TestPastFuzzingFailures(t *testing.T) { entries, err := ioutil.ReadDir("crashers") if err != nil { t.Errorf("%s", err) return } for _, entry := range entries { if strings.HasSuffix(entry.Name(), ".output") { continue } if strings.HasSuffix(entry.Name(), ".quoted") { continue } t.Logf("%s", entry.Name()) data, err := ioutil.ReadFile("crashers/" + entry.Name()) if err != nil { t.Errorf("%s: %s", entry.Name(), err) return } Fuzz(data) } } saml-0.4.6/xmlenc/gcm.go000066400000000000000000000067051415467341100150560ustar00rootroot00000000000000package xmlenc import ( "crypto/aes" "crypto/cipher" "crypto/rand" "encoding/base64" "fmt" "io" "github.com/beevik/etree" ) // GCM implements Decrypter and Encrypter for block ciphers in struct mode type GCM struct { keySize int algorithm string cipher func([]byte) (cipher.Block, error) } // KeySize returns the length of the key required. func (e GCM) KeySize() int { return e.keySize } // Algorithm returns the name of the algorithm, as will be found // in an xenc:EncryptionMethod element. func (e GCM) Algorithm() string { return e.algorithm } // Encrypt encrypts plaintext with key and nonce func (e GCM) Encrypt(key interface{}, plaintext []byte, nonce []byte) (*etree.Element, error) { keyBuf, ok := key.([]byte) if !ok { return nil, ErrIncorrectKeyType("[]byte") } if len(keyBuf) != e.keySize { return nil, ErrIncorrectKeyLength(e.keySize) } block, err := e.cipher(keyBuf) if err != nil { return nil, err } encryptedDataEl := etree.NewElement("xenc:EncryptedData") encryptedDataEl.CreateAttr("xmlns:xenc", "http://www.w3.org/2001/04/xmlenc#") { randBuf := make([]byte, 16) if _, err := RandReader.Read(randBuf); err != nil { return nil, err } encryptedDataEl.CreateAttr("Id", fmt.Sprintf("_%x", randBuf)) } em := encryptedDataEl.CreateElement("xenc:EncryptionMethod") em.CreateAttr("Algorithm", e.algorithm) em.CreateAttr("xmlns:xenc", "http://www.w3.org/2001/04/xmlenc#") plaintext = appendPadding(plaintext, block.BlockSize()) aesgcm, err := cipher.NewGCM(block) if err != nil { return nil, err } if nonce == nil { // generate random nonce when it's nil nonce := make([]byte, aesgcm.NonceSize()) if _, err := io.ReadFull(rand.Reader, nonce); err != nil { panic(err.Error()) } } ciphertext := make([]byte, len(plaintext)) text := aesgcm.Seal(nil, nonce, ciphertext, nil) cd := encryptedDataEl.CreateElement("xenc:CipherData") cd.CreateAttr("xmlns:xenc", "http://www.w3.org/2001/04/xmlenc#") cd.CreateElement("xenc:CipherValue").SetText(base64.StdEncoding.EncodeToString(text)) return encryptedDataEl, nil } // Decrypt decrypts an encrypted element with key. If the ciphertext contains an // EncryptedKey element, then the type of `key` is determined by the registered // Decryptor for the EncryptedKey element. Otherwise, `key` must be a []byte of // length KeySize(). func (e GCM) Decrypt(key interface{}, ciphertextEl *etree.Element) ([]byte, error) { if encryptedKeyEl := ciphertextEl.FindElement("./KeyInfo/EncryptedKey"); encryptedKeyEl != nil { var err error key, err = Decrypt(key, encryptedKeyEl) if err != nil { return nil, err } } keyBuf, ok := key.([]byte) if !ok { return nil, ErrIncorrectKeyType("[]byte") } if len(keyBuf) != e.KeySize() { return nil, ErrIncorrectKeyLength(e.KeySize()) } block, err := e.cipher(keyBuf) if err != nil { return nil, err } aesgcm, err := cipher.NewGCM(block) if err != nil { return nil, err } ciphertext, err := getCiphertext(ciphertextEl) if err != nil { return nil, err } nonce := ciphertext[:aesgcm.NonceSize()] text := ciphertext[aesgcm.NonceSize():] plainText, err := aesgcm.Open(nil, nonce, text, nil) if err != nil { return nil, err } return plainText, nil } var ( // AES128GCM implements AES128-GCM mode for encryption and decryption AES128GCM BlockCipher = GCM{ keySize: 16, algorithm: "http://www.w3.org/2009/xmlenc11#aes128-gcm", cipher: aes.NewCipher, } ) func init() { RegisterDecrypter(AES128GCM) } saml-0.4.6/xmlenc/pubkey.go000066400000000000000000000121351415467341100156010ustar00rootroot00000000000000package xmlenc import ( "crypto/rsa" "crypto/x509" "encoding/base64" "fmt" "github.com/beevik/etree" ) // RSA implements Encrypter and Decrypter using RSA public key encryption. // // Use function like OAEP(), or PKCS1v15() to get an instance of this type ready // to use. type RSA struct { BlockCipher BlockCipher DigestMethod DigestMethod // only for OAEP algorithm string keyEncrypter func(e RSA, pubKey *rsa.PublicKey, plaintext []byte) ([]byte, error) keyDecrypter func(e RSA, privKey *rsa.PrivateKey, ciphertext []byte) ([]byte, error) } // Algorithm returns the name of the algorithm func (e RSA) Algorithm() string { return e.algorithm } // Encrypt implements encrypter. certificate must be a []byte containing the ASN.1 bytes // of certificate containing an RSA public key. func (e RSA) Encrypt(certificate interface{}, plaintext []byte, nonce []byte) (*etree.Element, error) { cert, ok := certificate.(*x509.Certificate) if !ok { return nil, ErrIncorrectKeyType("*x.509 certificate") } pubKey, ok := cert.PublicKey.(*rsa.PublicKey) if !ok { return nil, ErrIncorrectKeyType("x.509 certificate with an RSA public key") } // generate a key key := make([]byte, e.BlockCipher.KeySize()) if _, err := RandReader.Read(key); err != nil { return nil, err } keyInfoEl := etree.NewElement("ds:KeyInfo") keyInfoEl.CreateAttr("xmlns:ds", "http://www.w3.org/2000/09/xmldsig#") encryptedKey := keyInfoEl.CreateElement("xenc:EncryptedKey") { randBuf := make([]byte, 16) if _, err := RandReader.Read(randBuf); err != nil { return nil, err } encryptedKey.CreateAttr("Id", fmt.Sprintf("_%x", randBuf)) } encryptedKey.CreateAttr("xmlns:xenc", "http://www.w3.org/2001/04/xmlenc#") encryptionMethodEl := encryptedKey.CreateElement("xenc:EncryptionMethod") encryptionMethodEl.CreateAttr("Algorithm", e.algorithm) encryptionMethodEl.CreateAttr("xmlns:xenc", "http://www.w3.org/2001/04/xmlenc#") if e.DigestMethod != nil { dm := encryptionMethodEl.CreateElement("ds:DigestMethod") dm.CreateAttr("Algorithm", e.DigestMethod.Algorithm()) dm.CreateAttr("xmlns:ds", "http://www.w3.org/2000/09/xmldsig#") } { innerKeyInfoEl := encryptedKey.CreateElement("ds:KeyInfo") x509data := innerKeyInfoEl.CreateElement("ds:X509Data") x509data.CreateElement("ds:X509Certificate").SetText( base64.StdEncoding.EncodeToString(cert.Raw), ) } buf, err := e.keyEncrypter(e, pubKey, key) if err != nil { return nil, err } cd := encryptedKey.CreateElement("xenc:CipherData") cd.CreateAttr("xmlns:xenc", "http://www.w3.org/2001/04/xmlenc#") cd.CreateElement("xenc:CipherValue").SetText(base64.StdEncoding.EncodeToString(buf)) encryptedDataEl, err := e.BlockCipher.Encrypt(key, plaintext, nonce) if err != nil { return nil, err } encryptedDataEl.InsertChildAt(encryptedDataEl.FindElement("./CipherData").Index(), keyInfoEl) return encryptedDataEl, nil } // Decrypt implements Decryptor. `key` must be an *rsa.PrivateKey. func (e RSA) Decrypt(key interface{}, ciphertextEl *etree.Element) ([]byte, error) { rsaKey, err := validateRSAKeyIfPresent(key, ciphertextEl) if err != nil { return nil, err } ciphertext, err := getCiphertext(ciphertextEl) if err != nil { return nil, err } { digestMethodEl := ciphertextEl.FindElement("./EncryptionMethod/DigestMethod") if digestMethodEl == nil { e.DigestMethod = SHA1 } else { hashAlgorithmStr := digestMethodEl.SelectAttrValue("Algorithm", "") digestMethod, ok := digestMethods[hashAlgorithmStr] if !ok { return nil, ErrAlgorithmNotImplemented(hashAlgorithmStr) } e.DigestMethod = digestMethod } } return e.keyDecrypter(e, rsaKey, ciphertext) } // OAEP returns a version of RSA that implements RSA in OAEP-MGF1P mode. By default // the block cipher used is AES-256 CBC and the digest method is SHA-256. You can // specify other ciphers and digest methods by assigning to BlockCipher or // DigestMethod. func OAEP() RSA { return RSA{ BlockCipher: AES256CBC, DigestMethod: SHA256, algorithm: "http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p", keyEncrypter: func(e RSA, pubKey *rsa.PublicKey, plaintext []byte) ([]byte, error) { return rsa.EncryptOAEP(e.DigestMethod.Hash(), RandReader, pubKey, plaintext, nil) }, keyDecrypter: func(e RSA, privKey *rsa.PrivateKey, ciphertext []byte) ([]byte, error) { return rsa.DecryptOAEP(e.DigestMethod.Hash(), RandReader, privKey, ciphertext, nil) }, } } // PKCS1v15 returns a version of RSA that implements RSA in PKCS1v15 mode. By default // the block cipher used is AES-256 CBC. The DigestMethod field is ignored because PKCS1v15 // does not use a digest function. func PKCS1v15() RSA { return RSA{ BlockCipher: AES256CBC, DigestMethod: nil, algorithm: "http://www.w3.org/2001/04/xmlenc#rsa-1_5", keyEncrypter: func(e RSA, pubKey *rsa.PublicKey, plaintext []byte) ([]byte, error) { return rsa.EncryptPKCS1v15(RandReader, pubKey, plaintext) }, keyDecrypter: func(e RSA, privKey *rsa.PrivateKey, ciphertext []byte) ([]byte, error) { return rsa.DecryptPKCS1v15(RandReader, privKey, ciphertext) }, } } func init() { RegisterDecrypter(OAEP()) RegisterDecrypter(PKCS1v15()) } saml-0.4.6/xmlenc/testdata/000077500000000000000000000000001415467341100155625ustar00rootroot00000000000000saml-0.4.6/xmlenc/testdata/cert.cert000066400000000000000000000033001415467341100173720ustar00rootroot00000000000000-----BEGIN CERTIFICATE----- MIIE0DCCArgCCQDQ3vxsffYA7DANBgkqhkiG9w0BAQsFADAqMSgwJgYDVQQDDB9t YWluLmF1dGgtc3NvLmRlLnFhLm1lZGljdWphLmRlMB4XDTIxMDcxOTEzMzkxOFoX DTIyMDcxOTEzMzkxOFowKjEoMCYGA1UEAwwfbWFpbi5hdXRoLXNzby5kZS5xYS5t ZWRpY3VqYS5kZTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAJe50Ka+ qNwgmKeoLEDYTmIbLRAyVX6lgO2RKzVzDzf6LyrWtZL1SKbbA6VdmzPlo4kzA3di f4/XgjS6hWeXXKaqXV8g/wKSSIhFTkp/WIrZY7KHF9mTYXdOrPGkR9IENiMevsVg RfcBnnHTGgczeiAFZm1xnkHBmVPZZjfuoi3nbjrMCh8uR3EqDsLzIHJ9PB2F4KvW KDuDaqdNOocI3fxEZuy/Ahwpr5FORJYZDk24ZusTPniy/+xFREneHPZaB+tFCxtj 3PdmCpIF54tj9dVLOMkQzbtPiTCGx2own9yjwa7NYYV8y8fBUcpuexbk+576J/rL MIFvFusf8PaL0QfMnkCJGL1T5O8wTEdpZ6h17rVzJtaK4lLNsO9+7vtW2gYFgLGj nr/Zg6ZKfUfZLm6h3bfjtKRKnAzrqI4E6f1nvwyEglqE5BHEXriI2+tbsrMwHQoy XVray8KY1WVrCnFFQFtb22BPSQLHu/n4GIOz7k34h7mgWOPnlW2p625UXiuFbWJD 4sdNw7encs3gkILStbaRX1WZHwb2QjfFDvMS1k1KhdktELxmZKR8bDXRzymMYwls KInPC2s92u+L7wesuqQI2GKeMnH6jEdFdqPwXo8ObgI6GPOxzVS6Q3BnI4SXTxeC 4wQq6hQpqZS4rge0dfxHttwRVywdzjSKVw5ZAgMBAAEwDQYJKoZIhvcNAQELBQAD ggIBABVWyAQh+880dOqd/LEX7DR4mwVnaRnOn+Xl6gbdoKJK0KyoQWEigW+Rj4Rt uBdi56NvxbBQk8UIamO0zFJzCpJ936OhoMwE3ZZETto0YAVtGqPlZRBJxLQo56Wz Rtmpy3bHOpQsYZRTYOXjoFxG+pHhiOjJ5W1djRqyOPa3I8H3tsHixUOZcycHIIo6 gLRBvsVuDgrI4YDtX8mw695nmbFzwDbpkf0kPAb18eihrFqKlI4JNegn1RE/zkHe 4kC3xpaqt/XlsU39evgUb78S8ZsVgtyt+S5ywChcRunyyv5fyQm79ZRtVPVS7oPJ OHWG4EpzuU1XAqDu7mGfZW7Aks3j8OrGzmzt72CfcBuRaAomgdllMalO6iUkDVdh q2RmjBKaVbh+9aq4JB+cf88TfcrzuB4lBWeOdwOgQuhn8zB0qiuHJ9TgTUAuVeIX HI+/I5s0FFbqSCfOraFAwbfxecSs9rEaZJvLMQ5mB9g7LW3tRSYGNT2ujOQQygKI +RDNMDNLS5wO/M2DdSDJcyrrxZXiZp2/ob6ovRERmIIlxIoaQkON2pJ0eLnt0Qz5 wrqOhgbJSPtNUdMMD3GtGm3eZn+F0Gm0WzpMD3VVHpJYxcnvFe2pgQrS6DhRCjkr paYGxi0gsWxwWE+H5dFPayliO0EoLQIojSKTdRVIogvy62S9 -----END CERTIFICATE----- saml-0.4.6/xmlenc/testdata/cert.key000066400000000000000000000063101415467341100172310ustar00rootroot00000000000000-----BEGIN PRIVATE KEY----- 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 xESNSIqhp4ubdwD87Ec0oeEcIqHzwRM= -----END PRIVATE KEY----- saml-0.4.6/xmlenc/testdata/cert.pem000066400000000000000000000013351415467341100172240ustar00rootroot00000000000000-----BEGIN CERTIFICATE----- MIIB7zCCAVgCCQDFzbKIp7b3MTANBgkqhkiG9w0BAQUFADA8MQswCQYDVQQGEwJV UzELMAkGA1UECAwCR0ExDDAKBgNVBAoMA2ZvbzESMBAGA1UEAwwJbG9jYWxob3N0 MB4XDTEzMTAwMjAwMDg1MVoXDTE0MTAwMjAwMDg1MVowPDELMAkGA1UEBhMCVVMx CzAJBgNVBAgMAkdBMQwwCgYDVQQKDANmb28xEjAQBgNVBAMMCWxvY2FsaG9zdDCB nzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA1PMHYmhZj308kWLhZVT4vOulqx/9 ibm5B86fPWwUKKQ2i12MYtz07tzukPymisTDhQaqyJ8Kqb/6JjhmeMnEOdTvSPmH O8m1ZVveJU6NoKRn/mP/BD7FW52WhbrUXLSeHVSKfWkNk6S4hk9MV9TswTvyRIKv Rsw0X/gfnqkroJcCAwEAATANBgkqhkiG9w0BAQUFAAOBgQCMMlIO+GNcGekevKgk akpMdAqJfs24maGb90DvTLbRZRD7Xvn1MnVBBS9hzlXiFLYOInXACMW5gcoRFfeT QLSouMM8o57h0uKjfTmuoWHLQLi6hnF+cvCsEFiJZ4AbF+DgmO6TarJ8O05t8zvn OwJlNCASPZRH/JmF8tX0hoHuAQ== -----END CERTIFICATE----- saml-0.4.6/xmlenc/testdata/ciphertext.xml000066400000000000000000000277511415467341100204770ustar00rootroot00000000000000 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 saml-0.4.6/xmlenc/testdata/ciphertext_gcm.xml000066400000000000000000000354521415467341100213220ustar00rootroot00000000000000 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 saml-0.4.6/xmlenc/testdata/encrypt-data-aes128-cbc.data000066400000000000000000000000231415467341100225310ustar00rootroot00000000000000top secret message saml-0.4.6/xmlenc/testdata/encrypt-data-aes128-cbc.xml000066400000000000000000000007011415467341100224230ustar00rootroot00000000000000 job QMpxhXq1DtBeyC9KfSaMQWrEtefe+e935gF/x62spvmL6IW0XeS0W4Kk31OgWzN0 saml-0.4.6/xmlenc/testdata/input.xml000066400000000000000000000312321415467341100174440ustar00rootroot00000000000000https://idp.testshib.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-0.4.6/xmlenc/testdata/input_gcm.xml000066400000000000000000000510011415467341100202660ustar00rootroot00000000000000 https://testidp2.aai.dfn.de/idp/shibboleth 0FIhiuTU4G/2II+pBXGC81qI9Hev22WJV65BoRGsgJ0= leAla7fqpxY5xvsklsr+o6fS4KDVlL0Z3paXsKzoy+xTR7fFV9XK9SvAhzYZigjzXZEDQcQwjTfRqBbINufFZvarHho1QuORZP1H6brlswkWYM2LjsWuwuGJElepwUfARqVhbmIUuj5SV9ZuIfrayTCMaVOsoUZrxylhj8V94DZbvKfB415WDQkUzpbftlSxaBIRBGMnT2AupdZGkYLN/7XZ6oybI1kqPYZMEZd6tYUTWbMGc7DGBQfHFR4atBPtiw/lisLbLLc/9F2N+nC3ODaFfLCQwdBm/7NGPbSDZMOBsqfKy8zauFzNiR5yvFiOXo7kobacFqq6lIrNagFKUvRXgq7lUdlfWR2Ul1tJO1VaV+RgTo8jGsE3UBrIFetRo6blUnSFHG5MkPjClv2SuoTmwTtBmrnj/bWDLGvUxwxDusoKsj/d9OGv64rAGPTp5cPSNIfW3R3xrKsIBMl8lpfH2GzwWFvoySS5oFKX84YTmLQH2mESStdYlUJsMQ6Wq0ZssCTikpvtTo0Z1+NuVscba3vgAlVgBxaL+Kkx8+RWlbW+C9tU7XUVvabcGmeX95P7OYaOvROxq2y/uQKRzA1sz/L2d3vbUz5feiGPx+pEuX3MYQbfRRatlNan6nI4twzokOobPmmeWLUp+b6eMNkz2QC7JkTikYkzwSkhjxU= MIIJJzCCCA+gAwIBAgIMJSC7cHRrXZg60Eo/MA0GCSqGSIb3DQEBCwUAMIGNMQswCQYDVQQGEwJE RTFFMEMGA1UECgw8VmVyZWluIHp1ciBGb2VyZGVydW5nIGVpbmVzIERldXRzY2hlbiBGb3JzY2h1 bmdzbmV0emVzIGUuIFYuMRAwDgYDVQQLDAdERk4tUEtJMSUwIwYDVQQDDBxERk4tVmVyZWluIEds b2JhbCBJc3N1aW5nIENBMB4XDTIxMDcyODExMjIxMFoXDTIyMDgyODExMjIxMFowga8xCzAJBgNV BAYTAkRFMQ8wDQYDVQQIDAZCZXJsaW4xDzANBgNVBAcMBkJlcmxpbjFFMEMGA1UECgw8VmVyZWlu IHp1ciBGb2VyZGVydW5nIGVpbmVzIERldXRzY2hlbiBGb3JzY2h1bmdzbmV0emVzIGUuIFYuMRkw FwYDVQQLDBBHZXNjaGFlZnRzc3RlbGxlMRwwGgYDVQQDDBN0ZXN0aWRwMi5hYWkuZGZuLmRlMIIC IjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAvMXPQpcH57g+I5qLmSHTuGewKaqg/xHSkEza 7P3dAVg4sHslBdtPN5ngoA2D2x5/zz078BszczYSeVlXH5Jj8nJ5EXesEdBTlWTk1eq4tWy1X2fW CcALbs6RvCVAmweWyfNMGBTDdk8TG/Xn58HzXLgDlpBcoNmIiVgtYQ1z7vZyTkVhy7DhmOLDHZ0B IhWJnl3wsmBTLwkAG41vzlWqA/03R50TcTc1QKF1St5YX7AIjaruZZs2BOTKcQhk9/vqooD8aXZ0 O2+FAtiQivbxldZUuUuuenx2dwlMY2FxCSTwEFdyW8sAapF+9YhrRKzFEtcihAZxLR+ggqJch8Zi gAC1I/xuFH4KUXOuOdDF4mRVMRNDYw207h2s2ur9hBSw5yRgQG/oQVO6QFr8d6taf14QDcVF3ZC8 zxYsx0Az/HdRYPBV2urSsk+ln3vg7HOMFtUuAACU0ejeYriMpDgGzWEji4K3m9CaFkEMT4jo6zRk OeKXpNnZsXT8tQ1huvkNG4lqNHVGLN5NI3tYPMSkRhdI+tHgRcYEn+gnRoTHfoSJAsZv/UeLH0gZ LKDBDBmvdCADP2I4uLOEYqqh5MDtIOY5/vBN3CDw4wDO3lCzF6YhWJh336AT5baVmpZvlYe35w8u fdAbpcKzuuB9UcvYOsYUKDBw+FucMDlttFtA5l0CAwEAAaOCBGEwggRdMFcGA1UdIARQME4wCAYG Z4EMAQICMA0GCysGAQQBga0hgiweMA8GDSsGAQQBga0hgiwBAQQwEAYOKwYBBAGBrSGCLAEBBAkw EAYOKwYBBAGBrSGCLAIBBAkwCQYDVR0TBAIwADAOBgNVHQ8BAf8EBAMCBaAwHQYDVR0lBBYwFAYI KwYBBQUHAwIGCCsGAQUFBwMBMB0GA1UdDgQWBBTuOFXROs368znJJLquZbkABIi0mTAfBgNVHSME GDAWgBRrOpiL+fJTidrgrbIyHgkf6Ko7dDAeBgNVHREEFzAVghN0ZXN0aWRwMi5hYWkuZGZuLmRl MIGNBgNVHR8EgYUwgYIwP6A9oDuGOWh0dHA6Ly9jZHAxLnBjYS5kZm4uZGUvZGZuLWNhLWdsb2Jh bC1nMi9wdWIvY3JsL2NhY3JsLmNybDA/oD2gO4Y5aHR0cDovL2NkcDIucGNhLmRmbi5kZS9kZm4t Y2EtZ2xvYmFsLWcyL3B1Yi9jcmwvY2FjcmwuY3JsMIHbBggrBgEFBQcBAQSBzjCByzAzBggrBgEF BQcwAYYnaHR0cDovL29jc3AucGNhLmRmbi5kZS9PQ1NQLVNlcnZlci9PQ1NQMEkGCCsGAQUFBzAC hj1odHRwOi8vY2RwMS5wY2EuZGZuLmRlL2Rmbi1jYS1nbG9iYWwtZzIvcHViL2NhY2VydC9jYWNl cnQuY3J0MEkGCCsGAQUFBzAChj1odHRwOi8vY2RwMi5wY2EuZGZuLmRlL2Rmbi1jYS1nbG9iYWwt ZzIvcHViL2NhY2VydC9jYWNlcnQuY3J0MIIB+AYKKwYBBAHWeQIEAgSCAegEggHkAeIAdgBGpVXr dfqRIDC1oolp9PN9ESxBdL79SbiFq/L8cP5tRwAAAXrs2cfNAAAEAwBHMEUCIQDNfyPxXrQl7gIc Lw7wEH537JUD41i06NNZUTxBdn4iHwIgK990g8JF36529aiweqqQC59H8/T03I9yHi2N/lMthY8A dgApeb7wnjk5IfBWc59jpXflvld9nGAK+PlNXSZcJV3HhAAAAXrs2cz2AAAEAwBHMEUCIQCLlz4B upCeqi8KyO7T7jp8+GRlxRyWyO2C8vqbeiFD1gIgHanhzYpnfD5JwyATOH5/iCc6vqR9vJIW8ttj DADOqSkAdwBvU3asMfAxGdiZAKRRFf93FRwR2QLBACkGjbIImjfZEwAAAXrs2cgeAAAEAwBIMEYC IQD/h0+qUXYOK8sj+F+qoypjQ+uCHFu1b+wFJpnvQ00D/gIhAJFNPtbfAFl1m0m11u7kAuM2bPk3 LCx6471dRixZvrLpAHcAVYHUwhaQNgFK6gubVzxT8MDkOHhwJQgXL6OqHQcT0wwAAAF67NnJKgAA BAMASDBGAiEA/+o2fEeFg73eCZ2UawSnZcZIXycHs+9CXNRntbfRmIUCIQDPSvvsmphFvYPeQy7B QDG+3EyrvyKqichkwKLNjgIc9TANBgkqhkiG9w0BAQsFAAOCAQEAVg7v+aFqn5443l88dXR1JGeP 6qzL0jDB6EYREhWvxeb2JEl1kn7jvLPMF+LKatADykBWxV3L2IHxEcmtP9hDnv39t7P92FN9zssn hHs49LZPwl3gsoErdbB1jMCkVC+0qTA0JoeEbkixlZXwarUf6UF/17jBKSLdlA3CkTv51Td7dqsl FBihFzLxzTLpkuFYxtN8Ax5BfqbCPnNQ+XAlTenClyrgB7wzZ3qgoCS+saW7rn1MbdBcuOmUS8+A jQnr+mBWWZJPXpZnlR7FIo/krCmxhEWpwsBf5taIguDbZ3oE92oQOtYsJ561ATAtDpxZMr91ljmk hVoyt2aEjDtCgg== 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 saml-0.4.6/xmlenc/testdata/key.pem000066400000000000000000000015731415467341100170630ustar00rootroot00000000000000-----BEGIN RSA PRIVATE KEY----- MIICXgIBAAKBgQDU8wdiaFmPfTyRYuFlVPi866WrH/2JubkHzp89bBQopDaLXYxi 3PTu3O6Q/KaKxMOFBqrInwqpv/omOGZ4ycQ51O9I+Yc7ybVlW94lTo2gpGf+Y/8E PsVbnZaFutRctJ4dVIp9aQ2TpLiGT0xX1OzBO/JEgq9GzDRf+B+eqSuglwIDAQAB AoGBAMuy1eN6cgFiCOgBsB3gVDdTKpww87Qk5ivjqEt28SmXO13A1KNVPS6oQ8SJ CT5Azc6X/BIAoJCURVL+LHdqebogKljhH/3yIel1kH19vr4E2kTM/tYH+qj8afUS JEmArUzsmmK8ccuNqBcllqdwCZjxL4CHDUmyRudFcHVX9oyhAkEA/OV1OkjM3CLU N3sqELdMmHq5QZCUihBmk3/N5OvGdqAFGBlEeewlepEVxkh7JnaNXAXrKHRVu/f/ fbCQxH+qrwJBANeQERF97b9Sibp9xgolb749UWNlAdqmEpmlvmS202TdcaaT1msU 4rRLiQN3X9O9mq4LZMSVethrQAdX1whawpkCQQDk1yGf7xZpMJ8F4U5sN+F4rLyM Rq8Sy8p2OBTwzCUXXK+fYeXjybsUUMr6VMYTRP2fQr/LKJIX+E5ZxvcIyFmDAkEA yfjNVUNVaIbQTzEbRlRvT6MqR+PTCefC072NF9aJWR93JimspGZMR7viY6IM4lrr vBkm0F5yXKaYtoiiDMzlOQJADqmEwXl0D72ZG/2KDg8b4QZEmC9i5gidpQwJXUc6 hU+IVQoLxRq0fBib/36K9tcrrO5Ba4iEvDcNY+D8yGbUtA== -----END RSA PRIVATE KEY----- saml-0.4.6/xmlenc/testdata/plaintext.xml000066400000000000000000000170371415467341100203240ustar00rootroot00000000000000https://idp.testshib.org/idp/shibbolethcX9v4gtpluvP0P1TjYf+NJpli/PO5Abp/8bNxHFENGo=PmgDQrPcsntL0XsPnQXAXcdrjvD90hiTYWbj5+Bsn9WgI3HEDZh8RJAgei3LbFWDGOU6mBwYQU1PCxcAcXWkjamH4L0QDExA/5iHrhRXGQgwgEigKqLZH3h2EPVV1HYK5VLRs4ZI6uAgiY4wObHORxZevuM9NdgYRjKVUl0tSIXc6IfJqTNfCGfSQ9UIFNn8GI9GNaYBaHDhXyGJL1MnbHISQEJP7ingyHFKpc7UO0o8EGlgGJ4iVIe1ONBeSKs/d+dHwRKE1gUd4Ls3N7h8tBiCxr0ICSNANvLdAzGoefLCQe9UEh9QB3ZIQK1+sv9MzEFT+7bQY9GrZVszucBf4w==MIIEDjCCAvagAwIBAgIBADANBgkqhkiG9w0BAQUFADBnMQswCQYDVQQGEwJVUzEVMBMGA1UECBMM UGVubnN5bHZhbmlhMRMwEQYDVQQHEwpQaXR0c2J1cmdoMREwDwYDVQQKEwhUZXN0U2hpYjEZMBcG A1UEAxMQaWRwLnRlc3RzaGliLm9yZzAeFw0wNjA4MzAyMTEyMjVaFw0xNjA4MjcyMTEyMjVaMGcx CzAJBgNVBAYTAlVTMRUwEwYDVQQIEwxQZW5uc3lsdmFuaWExEzARBgNVBAcTClBpdHRzYnVyZ2gx ETAPBgNVBAoTCFRlc3RTaGliMRkwFwYDVQQDExBpZHAudGVzdHNoaWIub3JnMIIBIjANBgkqhkiG 9w0BAQEFAAOCAQ8AMIIBCgKCAQEArYkCGuTmJp9eAOSGHwRJo1SNatB5ZOKqDM9ysg7CyVTDClcp u93gSP10nH4gkCZOlnESNgttg0r+MqL8tfJC6ybddEFB3YBo8PZajKSe3OQ01Ow3yT4I+Wdg1tsT pSge9gEz7SrC07EkYmHuPtd71CHiUaCWDv+xVfUQX0aTNPFmDixzUjoYzbGDrtAyCqA8f9CN2txI fJnpHE6q6CmKcoLADS4UrNPlhHSzd614kR/JYiks0K4kbRqCQF0Dv0P5Di+rEfefC6glV8ysC8dB 5/9nb0yh/ojRuJGmgMWHgWk6h0ihjihqiu4jACovUZ7vVOCgSE5Ipn7OIwqd93zp2wIDAQABo4HE MIHBMB0GA1UdDgQWBBSsBQ869nh83KqZr5jArr4/7b+QazCBkQYDVR0jBIGJMIGGgBSsBQ869nh8 3KqZr5jArr4/7b+Qa6FrpGkwZzELMAkGA1UEBhMCVVMxFTATBgNVBAgTDFBlbm5zeWx2YW5pYTET MBEGA1UEBxMKUGl0dHNidXJnaDERMA8GA1UEChMIVGVzdFNoaWIxGTAXBgNVBAMTEGlkcC50ZXN0 c2hpYi5vcmeCAQAwDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQUFAAOCAQEAjR29PhrCbk8qLN5M FfSVk98t3CT9jHZoYxd8QMRLI4j7iYQxXiGJTT1FXs1nd4Rha9un+LqTfeMMYqISdDDI6tv8iNpk OAvZZUosVkUo93pv1T0RPz35hcHHYq2yee59HJOco2bFlcsH8JBXRSRrJ3Q7Eut+z9uo80JdGNJ4 /SJy5UorZ8KazGj16lfJhOBXldgrhppQBb0Nq6HKHguqmwRfJ+WkxemZXzhediAjGeka8nz8Jjwx pUjAiSWYKLtJhGEaTqCYxCCX2Dw+dOTqUzHOZ7WKv4JXPK5G/Uhr8K/qhmFT2nIQi538n6rVYLeW j8Bbnl+ev0peYzxFyF5sQA==_41bd295976dadd70e1480f318e772841https://15661444.ngrok.io/saml2/metadataurn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransportmyselfMemberStaffmyself@testshib.orgAnd IMember@testshib.orgStaff@testshib.orgMe Myselfurn:mace:dir:entitlement:common-lib-termsMe Myself And I8F+M9ovyaYNwCId0pVkVsnZYRDo=555-5555saml-0.4.6/xmlenc/testdata/plaintext_gcm.xml000066400000000000000000000206761415467341100211550ustar00rootroot00000000000000 https://testidp2.com/idp/shibboleth gYIc30qUhP+BV4KzOEZ4DBBvxc6ehHkzUgxe7RKo1L8= dPsW50R3xIlq7Sus7kFWTsmzKJ3MU5vN7SL6yACXvqt1s2bWPCf1HQuEEh1MKxsHC6fuknbdWKgHF4lhWFs35CGc6q4mA9wt4AC8XD7qN2Ps5oPmT7DNynB1G9y2p/yYIuspjjRE5yHNyuPEg6Qgi6OR22sp5bv5XOP+bslmdGGjC5xAYML7JG6iPW56fwxTXFmKwlnjWHZwYohNOzbaZ7k1GDvNNSjbJvv8+BcgsH7yJnmFS6IUlxcQs33q+XGRAki+Q8Cw0NFzCV4xLLIs8JIutXUjvI00vrHnJjEHi/6yaXITxpgRShkrn2/zbzYOCmy5JgRkVXjyccAWT0jWZSBJ20Rchov9q1PAbda6/FO1x9ln6EzqCWKEaEa0OLMiQei0P2vQ2ZoKdfm36fKKrYdQGxtHFAyWt4k7WKlw71fZFFRNLpfLuocH8pjRRfapNwfBWcfHuZshvqML/O0150+1GcUUYPZrkamPvrzekPVllL7XqKpmKe2BDGLH4t3iSwC1+NU14Hq/wlQi7HhEzxm7YVK6LQhMyH4GP9MJmoc6Yhnn4emAz25TPDj8VmhTmtXlrHcHPXCTW5axYDvXm0oQNISaAcTuLzyBF4eRiswQaEsymHQn6HDybADODKESjYMbxxD/zDy+FAMwszHaVe7bcUPa9MISgAcUV3dpLvk= MIIJJzCCCA+gAwIBAgIMJSC7cHRrXZg60Eo/MA0GCSqGSIb3DQEBCwUAMIGNMQswCQYDVQQGEwJE RTFFMEMGA1UECgw8VmVyZWluIHp1ciBGb2VyZGVydW5nIGVpbmVzIERldXRzY2hlbiBGb3JzY2h1 bmdzbmV0emVzIGUuIFYuMRAwDgYDVQQLDAdERk4tUEtJMSUwIwYDVQQDDBxERk4tVmVyZWluIEds b2JhbCBJc3N1aW5nIENBMB4XDTIxMDcyODExMjIxMFoXDTIyMDgyODExMjIxMFowga8xCzAJBgNV BAYTAkRFMQ8wDQYDVQQIDAZCZXJsaW4xDzANBgNVBAcMBkJlcmxpbjFFMEMGA1UECgw8VmVyZWlu IHp1ciBGb2VyZGVydW5nIGVpbmVzIERldXRzY2hlbiBGb3JzY2h1bmdzbmV0emVzIGUuIFYuMRkw FwYDVQQLDBBHZXNjaGFlZnRzc3RlbGxlMRwwGgYDVQQDDBN0ZXN0aWRwMi5hYWkuZGZuLmRlMIIC IjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAvMXPQpcH57g+I5qLmSHTuGewKaqg/xHSkEza 7P3dAVg4sHslBdtPN5ngoA2D2x5/zz078BszczYSeVlXH5Jj8nJ5EXesEdBTlWTk1eq4tWy1X2fW CcALbs6RvCVAmweWyfNMGBTDdk8TG/Xn58HzXLgDlpBcoNmIiVgtYQ1z7vZyTkVhy7DhmOLDHZ0B IhWJnl3wsmBTLwkAG41vzlWqA/03R50TcTc1QKF1St5YX7AIjaruZZs2BOTKcQhk9/vqooD8aXZ0 O2+FAtiQivbxldZUuUuuenx2dwlMY2FxCSTwEFdyW8sAapF+9YhrRKzFEtcihAZxLR+ggqJch8Zi gAC1I/xuFH4KUXOuOdDF4mRVMRNDYw207h2s2ur9hBSw5yRgQG/oQVO6QFr8d6taf14QDcVF3ZC8 zxYsx0Az/HdRYPBV2urSsk+ln3vg7HOMFtUuAACU0ejeYriMpDgGzWEji4K3m9CaFkEMT4jo6zRk OeKXpNnZsXT8tQ1huvkNG4lqNHVGLN5NI3tYPMSkRhdI+tHgRcYEn+gnRoTHfoSJAsZv/UeLH0gZ LKDBDBmvdCADP2I4uLOEYqqh5MDtIOY5/vBN3CDw4wDO3lCzF6YhWJh336AT5baVmpZvlYe35w8u fdAbpcKzuuB9UcvYOsYUKDBw+FucMDlttFtA5l0CAwEAAaOCBGEwggRdMFcGA1UdIARQME4wCAYG Z4EMAQICMA0GCysGAQQBga0hgiweMA8GDSsGAQQBga0hgiwBAQQwEAYOKwYBBAGBrSGCLAEBBAkw EAYOKwYBBAGBrSGCLAIBBAkwCQYDVR0TBAIwADAOBgNVHQ8BAf8EBAMCBaAwHQYDVR0lBBYwFAYI KwYBBQUHAwIGCCsGAQUFBwMBMB0GA1UdDgQWBBTuOFXROs368znJJLquZbkABIi0mTAfBgNVHSME GDAWgBRrOpiL+fJTidrgrbIyHgkf6Ko7dDAeBgNVHREEFzAVghN0ZXN0aWRwMi5hYWkuZGZuLmRl MIGNBgNVHR8EgYUwgYIwP6A9oDuGOWh0dHA6Ly9jZHAxLnBjYS5kZm4uZGUvZGZuLWNhLWdsb2Jh bC1nMi9wdWIvY3JsL2NhY3JsLmNybDA/oD2gO4Y5aHR0cDovL2NkcDIucGNhLmRmbi5kZS9kZm4t Y2EtZ2xvYmFsLWcyL3B1Yi9jcmwvY2FjcmwuY3JsMIHbBggrBgEFBQcBAQSBzjCByzAzBggrBgEF BQcwAYYnaHR0cDovL29jc3AucGNhLmRmbi5kZS9PQ1NQLVNlcnZlci9PQ1NQMEkGCCsGAQUFBzAC hj1odHRwOi8vY2RwMS5wY2EuZGZuLmRlL2Rmbi1jYS1nbG9iYWwtZzIvcHViL2NhY2VydC9jYWNl cnQuY3J0MEkGCCsGAQUFBzAChj1odHRwOi8vY2RwMi5wY2EuZGZuLmRlL2Rmbi1jYS1nbG9iYWwt ZzIvcHViL2NhY2VydC9jYWNlcnQuY3J0MIIB+AYKKwYBBAHWeQIEAgSCAegEggHkAeIAdgBGpVXr dfqRIDC1oolp9PN9ESxBdL79SbiFq/L8cP5tRwAAAXrs2cfNAAAEAwBHMEUCIQDNfyPxXrQl7gIc Lw7wEH537JUD41i06NNZUTxBdn4iHwIgK990g8JF36529aiweqqQC59H8/T03I9yHi2N/lMthY8A dgApeb7wnjk5IfBWc59jpXflvld9nGAK+PlNXSZcJV3HhAAAAXrs2cz2AAAEAwBHMEUCIQCLlz4B upCeqi8KyO7T7jp8+GRlxRyWyO2C8vqbeiFD1gIgHanhzYpnfD5JwyATOH5/iCc6vqR9vJIW8ttj DADOqSkAdwBvU3asMfAxGdiZAKRRFf93FRwR2QLBACkGjbIImjfZEwAAAXrs2cgeAAAEAwBIMEYC IQD/h0+qUXYOK8sj+F+qoypjQ+uCHFu1b+wFJpnvQ00D/gIhAJFNPtbfAFl1m0m11u7kAuM2bPk3 LCx6471dRixZvrLpAHcAVYHUwhaQNgFK6gubVzxT8MDkOHhwJQgXL6OqHQcT0wwAAAF67NnJKgAA BAMASDBGAiEA/+o2fEeFg73eCZ2UawSnZcZIXycHs+9CXNRntbfRmIUCIQDPSvvsmphFvYPeQy7B QDG+3EyrvyKqichkwKLNjgIc9TANBgkqhkiG9w0BAQsFAAOCAQEAVg7v+aFqn5443l88dXR1JGeP 6qzL0jDB6EYREhWvxeb2JEl1kn7jvLPMF+LKatADykBWxV3L2IHxEcmtP9hDnv39t7P92FN9zssn hHs49LZPwl3gsoErdbB1jMCkVC+0qTA0JoeEbkixlZXwarUf6UF/17jBKSLdlA3CkTv51Td7dqsl FBihFzLxzTLpkuFYxtN8Ax5BfqbCPnNQ+XAlTenClyrgB7wzZ3qgoCS+saW7rn1MbdBcuOmUS8+A jQnr+mBWWZJPXpZnlR7FIo/krCmxhEWpwsBf5taIguDbZ3oE92oQOtYsJ561ATAtDpxZMr91ljmk hVoyt2aEjDtCgg== AAdzZWNyZXQxm924IEWIZegn9l1NChK4GXWETDW/ca4xRwNHuV21SA25MzW2bWqqCudhmNUrUsXk+Ci8W5MrwFiLKqJkNm4NwmHFsnvpUMVHlH8raI+xLVwwa2lf/poCXml0kE8D6cbtEBBACazlvgYMHHLud5+6uSDbta1xlp8S2G6aDOzWWYJluw== https://example.com/saml/metadata urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport urn:mace:dir:entitlement:common-lib-terms member@testscope.aai.dfn.de saml-0.4.6/xmlenc/testdata/rsapriv.pem000066400000000000000000000015671415467341100177640ustar00rootroot00000000000000-----BEGIN RSA PRIVATE KEY----- MIICXQIBAAKBgQDkXTUsWzRVpUHjbDpWCfYDfXmQ/q4LkaioZoTpu4ut1Q3eQC5t gD14agJhgT8yzeY5S/YNlwCyuVkjuFyoyTHFX2IOPpz7jnh4KnQ+B1IH9fY/+kmk zHJgxSUDJsdUMPgGpKt5hnEn7ziXAWXLc2udFbnHwhi9TXXwRHGi9wZ4YwIDAQAB AoGBALNTnlXeqRI4W61DZ+v4ln/XIIeD9xiOoWrcVrNU2zL+g41ryQmkEqFkXcpD vGUg2xFTXTz+v0WZ1y39sIW6uKFRYUfaNsF6iVfGAyx1VWK/jgtPnCWDQy26Eby0 BqpbZRy1a6MLYVEG/5bvZE01CDV4XttpTrNX91WWcYGduJxBAkEA6ED1ZOqIzBpu c2KAo+bWmroCH8+cSDk0gVq6bnRB+EEhRCmo/VgvndWLxfexdGmDIOAIisB06N5a GzBSCaEY/QJBAPu2cNvuuBNLwrlxPCwOEpIHYT4gJq8UMtg6O6N+u++nYCGhK6uo VCmrKY+UewyNIcsLZF0jsNI2qJjiU1vQxN8CQQDfQJnigMQwlfO3/Ga1po6Buu2R 0IpkroB3G1R8GkrTrR+iGv2zUdKrwHsUOC2fPlFrB4+OeMOomRw6aG9jjDStAkB1 ztiZhuvuVAoKIv5HnDqC0CNqIUAZtzlozDB3f+xT6SFr+/Plfn4Nlod4JMVGhZNo ZaeOlBLBAEX+cAcVtOs/AkBicZOAPv84ABmFfyhXhYaAuacaJLq//jg+t+URUOg+ XZS9naRmawEQxOkZQVoMeKgvu05+V4MniFqdQBINIkr5 -----END RSA PRIVATE KEY----- saml-0.4.6/xmlenc/testdata/rsapub.pem000066400000000000000000000004201415467341100175550ustar00rootroot00000000000000-----BEGIN PUBLIC KEY----- MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDkXTUsWzRVpUHjbDpWCfYDfXmQ /q4LkaioZoTpu4ut1Q3eQC5tgD14agJhgT8yzeY5S/YNlwCyuVkjuFyoyTHFX2IO Ppz7jnh4KnQ+B1IH9fY/+kmkzHJgxSUDJsdUMPgGpKt5hnEn7ziXAWXLc2udFbnH whi9TXXwRHGi9wZ4YwIDAQAB -----END PUBLIC KEY----- saml-0.4.6/xmlenc/xmlenc.go000066400000000000000000000036601415467341100155730ustar00rootroot00000000000000// Package xmlenc is a partial implementation of the xmlenc standard // as described in https://www.w3.org/TR/2002/REC-xmlenc-core-20021210/Overview.html. // The purpose of this implementation is to support encrypted SAML assertions. package xmlenc import ( "crypto/rand" "hash" "github.com/beevik/etree" ) // RandReader is a thunk that allows test to replace the source of randomness used by // this package. By default it is Reader from crypto/rand. var RandReader = rand.Reader // Encrypter is an interface that encrypts things. Given a plaintext it returns an // XML EncryptedData or EncryptedKey element. The required type of `key` varies // depending on the implementation. type Encrypter interface { Encrypt(key interface{}, plaintext []byte, nonce []byte) (*etree.Element, error) } // Decrypter is an interface that decrypts things. The Decrypt() method returns the // plaintext version of the EncryptedData or EncryptedKey element passed. // // You probably don't have to use this interface directly, instead you may call // Decrypt() and it will examine the element to determine which Decrypter to use. type Decrypter interface { Algorithm() string Decrypt(key interface{}, ciphertextEl *etree.Element) ([]byte, error) } // DigestMethod represents a digest method such as SHA1, etc. type DigestMethod interface { Algorithm() string Hash() hash.Hash } var ( decrypters = map[string]Decrypter{} digestMethods = map[string]DigestMethod{} ) // RegisterDecrypter registers the specified decrypter to that it can be // used with Decrypt(). func RegisterDecrypter(d Decrypter) { decrypters[d.Algorithm()] = d } // RegisterDigestMethod registers the specified digest method to that it can be // used with Decrypt(). func RegisterDigestMethod(dm DigestMethod) { digestMethods[dm.Algorithm()] = dm } // BlockCipher implements a cipher with a fixed size key like AES or 3DES. type BlockCipher interface { Encrypter Decrypter KeySize() int } saml-0.4.6/xmlenc/xmlenc_test.go000066400000000000000000000047161415467341100166350ustar00rootroot00000000000000package xmlenc import ( "io/ioutil" "math/rand" "testing" "github.com/beevik/etree" "gotest.tools/assert" is "gotest.tools/assert/cmp" ) func TestDataAES128(t *testing.T) { t.Run("CBC", func(t *testing.T) { RandReader = rand.New(rand.NewSource(0)) //nolint:gosec // deterministic random numbers for tests plaintext, err := ioutil.ReadFile("testdata/encrypt-data-aes128-cbc.data") assert.Check(t, err) var ciphertext string { encrypter := AES128CBC cipherEl, encErr := encrypter.Encrypt([]byte("abcdefghijklmnop"), plaintext, nil) assert.Check(t, encErr) doc := etree.NewDocument() doc.SetRoot(cipherEl) doc.IndentTabs() ciphertext, err = doc.WriteToString() assert.Check(t, err) } { decrypter := AES128CBC doc := etree.NewDocument() err = doc.ReadFromString(ciphertext) assert.Check(t, err) actualPlaintext, err := decrypter.Decrypt( []byte("abcdefghijklmnop"), doc.Root()) assert.Check(t, err) assert.Check(t, is.DeepEqual(plaintext, actualPlaintext)) } { decrypter := AES128CBC doc := etree.NewDocument() err := doc.ReadFromFile("testdata/encrypt-data-aes128-cbc.xml") assert.Check(t, err) actualPlaintext, err := decrypter.Decrypt([]byte("abcdefghijklmnop"), doc.Root()) assert.Check(t, err) assert.Check(t, is.DeepEqual(plaintext, actualPlaintext)) } }) t.Run("GCM", func(t *testing.T) { RandReader = rand.New(rand.NewSource(0)) //nolint:gosec // deterministic random numbers for tests plaintext := "top secret message to use with gcm" { encrypter := AES128GCM cipherEl, encErr := encrypter.Encrypt([]byte("abcdefghijklmnop"), []byte(plaintext), []byte("1234567890AZ")) assert.Check(t, encErr) doc := etree.NewDocument() doc.SetRoot(cipherEl) doc.IndentTabs() _, err := doc.WriteToString() assert.Check(t, err) } }) } /* func TestAES256CBC(t *testing.T) { RandReader = rand.New(rand.NewSource(0)) // deterministic random numbers for tests doc := etree.NewDocument() err := doc.ReadFromFile("testdata/plaintext.xml") assert.NoError(t, err) el := doc.FindElement("//PaymentInfo") assert.NotNil(t, el) tmpDoc := etree.NewDocument() tmpDoc.SetRoot(el.Copy()) tmpBuf, _ := tmpDoc.WriteToString() encrypter := AES256CBC cipherEl, err := encrypter.Encrypt( []byte("abcdefghijklmnopqrstuvwxyz012345"), []byte(tmpBuf)) assert.NoError(t, err) el.Child = nil el.AddChild(cipherEl) doc.IndentTabs() s, _ := doc.WriteToString() fmt.Println(s) } */