diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml
new file mode 100644
index 00000000..69f6627a
--- /dev/null
+++ b/.github/workflows/lint.yml
@@ -0,0 +1,18 @@
+name: 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
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
new file mode 100644
index 00000000..2e18bb6e
--- /dev/null
+++ b/.github/workflows/test.yml
@@ -0,0 +1,24 @@
+name: 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: find . -name go.mod -execdir go test -v ./... \;
diff --git a/example/go.mod b/example/go.mod
new file mode 100644
index 00000000..69b7b2e4
--- /dev/null
+++ b/example/go.mod
@@ -0,0 +1,17 @@
+module github.com/crewjam/saml/example
+
+replace github.com/crewjam/saml => ../
+
+replace github.com/crewjam/saml/samlidp => ../samlidp
+
+go 1.13
+
+require (
+ github.com/crewjam/saml v0.0.0-00010101000000-000000000000
+ github.com/dchest/uniuri v0.0.0-20200228104902-7aecb25e1fe5
+ github.com/kr/pretty v0.3.0
+ github.com/zenazn/goji v1.0.1
+ golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3
+)
+
+require github.com/crewjam/saml/samlidp v0.0.0-00010101000000-000000000000
diff --git a/example/go.sum b/example/go.sum
new file mode 100644
index 00000000..4603a9ec
--- /dev/null
+++ b/example/go.sum
@@ -0,0 +1,66 @@
+github.com/beevik/etree v1.1.0 h1:T0xke/WvNtMoCqgzPhkX2r4rjY3GDZFi+FjpRZY2Jbs=
+github.com/beevik/etree v1.1.0/go.mod h1:r8Aw8JqVegEf0w2fDnATrX9VpkMcyFeM0FhwO62wh+A=
+github.com/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.2.0 h1:besgBTC8w8HjP6NzQdxwKH9Z5oQMZ24ThTrHp3cZ8eU=
+github.com/golang-jwt/jwt/v4 v4.2.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-20211215153901-e495a2d5b3d3 h1:0es+/5331RGQPcXlMfP+WrnIIS6dNnNRe0WB02W0F4M=
+golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
+golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
+golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
+golang.org/x/text v0.3.6/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=
diff --git a/example/saml-test-matt.cobb-lightstep.shared.xml b/example/saml-test-matt.cobb-lightstep.shared.xml
new file mode 100644
index 00000000..b29bba1b
--- /dev/null
+++ b/example/saml-test-matt.cobb-lightstep.shared.xml
@@ -0,0 +1,24 @@
+
+
+
+
+
+ MIICvDCCAaQCCQCS7D7HtAusiDANBgkqhkiG9w0BAQsFADAgMR4wHAYDVQQDDBVteXNlcnZpY2UuZXhhbXBsZS5jb20wHhcNMjExMTE5MDAyMzIwWhcNMjIxMTE5MDAyMzIwWjAgMR4wHAYDVQQDDBVteXNlcnZpY2UuZXhhbXBsZS5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC5U0mD0s+OymB4RbCdOXtdtRjZBnNJmb1II8j0PUzY9sNNMuB0yQytkZQy9s3aPwUPaA4yWQdaxn+bPZ4vEDuy5AVjDTH4ncpaiRI8g/6JWpeHcj/CNar5SPt0Mp9j/ommvtuhO0w+VAspKIEyhGI7a6QW0lsI8SfE+ye26n7gLXadABSA8BTH0XgrQ3MmECe3v7YUucSBo+j+MTWe+6CvtLN8a7nsOn5CzoQGE0mpZZ72Qooh0sgcia1RlTA3rt/xqR1rRJcpLTQcu3yFBBvg4Y5EqvmyMV0IzD8QZmS6+KIclrX08bIVrdj2BpFkCUaRD9/9noKwsJ+/lgL0IWozAgMBAAEwDQYJKoZIhvcNAQELBQADggEBAG4NsbuDCNbv/RqdKv3S/dxPz5eF6aBsWH9OXCzQS9gl7j98CFAC2P8x/SOwwY/BY1+/CRydnQJMlQUj220pHZLTwIQM351enYfmQ3sjo3gclYnUnrAO5fF/u5MG1yw6PdfsYI+1tvWgAVKnh8FpSJ3ZHDQtI8Cf1mPFD0QdZ+F0yLkakVbskFlfRL+WjcJN4bXGlueOdK/uixvs6cKjI8jw1ATfOkzfKCqXKj/33OwJJXaiyq6hibULkSrjdOB8qDdcOesXTybaUooGsH3Qd+FgTsdqGgo0ZeRrg7NGOgtD3GpvV0l/Fhi0I0zjOfq7zLGBsnz+UjHAqNAclJyDI78=
+
+
+
+
+
+
+
+
+
+
+ MIICvDCCAaQCCQCS7D7HtAusiDANBgkqhkiG9w0BAQsFADAgMR4wHAYDVQQDDBVteXNlcnZpY2UuZXhhbXBsZS5jb20wHhcNMjExMTE5MDAyMzIwWhcNMjIxMTE5MDAyMzIwWjAgMR4wHAYDVQQDDBVteXNlcnZpY2UuZXhhbXBsZS5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC5U0mD0s+OymB4RbCdOXtdtRjZBnNJmb1II8j0PUzY9sNNMuB0yQytkZQy9s3aPwUPaA4yWQdaxn+bPZ4vEDuy5AVjDTH4ncpaiRI8g/6JWpeHcj/CNar5SPt0Mp9j/ommvtuhO0w+VAspKIEyhGI7a6QW0lsI8SfE+ye26n7gLXadABSA8BTH0XgrQ3MmECe3v7YUucSBo+j+MTWe+6CvtLN8a7nsOn5CzoQGE0mpZZ72Qooh0sgcia1RlTA3rt/xqR1rRJcpLTQcu3yFBBvg4Y5EqvmyMV0IzD8QZmS6+KIclrX08bIVrdj2BpFkCUaRD9/9noKwsJ+/lgL0IWozAgMBAAEwDQYJKoZIhvcNAQELBQADggEBAG4NsbuDCNbv/RqdKv3S/dxPz5eF6aBsWH9OXCzQS9gl7j98CFAC2P8x/SOwwY/BY1+/CRydnQJMlQUj220pHZLTwIQM351enYfmQ3sjo3gclYnUnrAO5fF/u5MG1yw6PdfsYI+1tvWgAVKnh8FpSJ3ZHDQtI8Cf1mPFD0QdZ+F0yLkakVbskFlfRL+WjcJN4bXGlueOdK/uixvs6cKjI8jw1ATfOkzfKCqXKj/33OwJJXaiyq6hibULkSrjdOB8qDdcOesXTybaUooGsH3Qd+FgTsdqGgo0ZeRrg7NGOgtD3GpvV0l/Fhi0I0zjOfq7zLGBsnz+UjHAqNAclJyDI78=
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/example/trivial/myservice.cert b/example/trivial/myservice.cert
new file mode 100644
index 00000000..1112a58e
--- /dev/null
+++ b/example/trivial/myservice.cert
@@ -0,0 +1,17 @@
+-----BEGIN CERTIFICATE-----
+MIICvDCCAaQCCQCS7D7HtAusiDANBgkqhkiG9w0BAQsFADAgMR4wHAYDVQQDDBVt
+eXNlcnZpY2UuZXhhbXBsZS5jb20wHhcNMjExMTE5MDAyMzIwWhcNMjIxMTE5MDAy
+MzIwWjAgMR4wHAYDVQQDDBVteXNlcnZpY2UuZXhhbXBsZS5jb20wggEiMA0GCSqG
+SIb3DQEBAQUAA4IBDwAwggEKAoIBAQC5U0mD0s+OymB4RbCdOXtdtRjZBnNJmb1I
+I8j0PUzY9sNNMuB0yQytkZQy9s3aPwUPaA4yWQdaxn+bPZ4vEDuy5AVjDTH4ncpa
+iRI8g/6JWpeHcj/CNar5SPt0Mp9j/ommvtuhO0w+VAspKIEyhGI7a6QW0lsI8SfE
++ye26n7gLXadABSA8BTH0XgrQ3MmECe3v7YUucSBo+j+MTWe+6CvtLN8a7nsOn5C
+zoQGE0mpZZ72Qooh0sgcia1RlTA3rt/xqR1rRJcpLTQcu3yFBBvg4Y5EqvmyMV0I
+zD8QZmS6+KIclrX08bIVrdj2BpFkCUaRD9/9noKwsJ+/lgL0IWozAgMBAAEwDQYJ
+KoZIhvcNAQELBQADggEBAG4NsbuDCNbv/RqdKv3S/dxPz5eF6aBsWH9OXCzQS9gl
+7j98CFAC2P8x/SOwwY/BY1+/CRydnQJMlQUj220pHZLTwIQM351enYfmQ3sjo3gc
+lYnUnrAO5fF/u5MG1yw6PdfsYI+1tvWgAVKnh8FpSJ3ZHDQtI8Cf1mPFD0QdZ+F0
+yLkakVbskFlfRL+WjcJN4bXGlueOdK/uixvs6cKjI8jw1ATfOkzfKCqXKj/33OwJ
+JXaiyq6hibULkSrjdOB8qDdcOesXTybaUooGsH3Qd+FgTsdqGgo0ZeRrg7NGOgtD
+3GpvV0l/Fhi0I0zjOfq7zLGBsnz+UjHAqNAclJyDI78=
+-----END CERTIFICATE-----
diff --git a/example/trivial/myservice.key b/example/trivial/myservice.key
new file mode 100644
index 00000000..0c2962aa
--- /dev/null
+++ b/example/trivial/myservice.key
@@ -0,0 +1,28 @@
+-----BEGIN PRIVATE KEY-----
+MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC5U0mD0s+OymB4
+RbCdOXtdtRjZBnNJmb1II8j0PUzY9sNNMuB0yQytkZQy9s3aPwUPaA4yWQdaxn+b
+PZ4vEDuy5AVjDTH4ncpaiRI8g/6JWpeHcj/CNar5SPt0Mp9j/ommvtuhO0w+VAsp
+KIEyhGI7a6QW0lsI8SfE+ye26n7gLXadABSA8BTH0XgrQ3MmECe3v7YUucSBo+j+
+MTWe+6CvtLN8a7nsOn5CzoQGE0mpZZ72Qooh0sgcia1RlTA3rt/xqR1rRJcpLTQc
+u3yFBBvg4Y5EqvmyMV0IzD8QZmS6+KIclrX08bIVrdj2BpFkCUaRD9/9noKwsJ+/
+lgL0IWozAgMBAAECggEAf7TF/wIBJ0uMCLU1TFK1EVDfC4ZnYXuAtLp8Gto3H3eE
+A6mghLmac+zSq//owJEWwpQY3Fb/O213t5lRbOsJyG9ayvPn0IOjUpoMKAlpGGuF
+6b8v8nubEudY56yOq5BWeMIihMtg8b5mZnlT9mTFcF7vlw5jzxinKBVOO70BqyZc
+eNTtoH8v96939jGra2YLBacI58b+aA1uxMV42IYyMPNziw+ACevDEhHrdhlD8ZQd
+o0fEfSf/M5pmZKQANrJQG7AvZvfBst6UIWrJmC1XFw5w1C+InSXDPk9XCgJarxUt
+oEDKxV1bUFWcyAu5Gwe85cEVAJE2pU0zAZTPQuCPuQKBgQDlCBStAIg1GmWhinvM
+e2x0MkxoGfZSoMNlaE9J6fTh/zIIW3JlHye/+yhDAGoBdBltGDvvxJJEJQ7O+zYk
+RNmN6xmvcjuCJyN19PanE9A1hmrP6khlPYHNSqpoX6camp64D03x2DoXE1Rz3I2v
+IdKKjjlePI5drRq4x068MTPGFQKBgQDPJbo/y9bHBQtK6+Qanhea2m2dpyQGiZNg
+aR27fW8fYMt5KDlOfVEpDgLWGzzOrgb9v3PjrrZ7nQuVGUdI4dSEPmrFKpy6f9nq
+sB3KiTcfrYGFLn2xqYsdpQFvVSyfKhLcI4C4g2izRzqXTuyf5cykI0YYQ2rKQyjt
+Wpttg4mJJwKBgGgRguuVaU7P3sxHU7d3gbFOxZ8r5NxoxkCG5DhOwYILZl2bx+Ah
+BvvaENJZFtHwQjlrIJjzNg8W06LDTTevvTcaU8J/4oBibl+D3qvOefZITLKK3zh5
+eUiiG8pvZEvXZDV4Zs90k/i2RNKcrhidAsEInRKv63SvWowJrEIhDUh9AoGBALrB
+C8pqbsddH9I7G5p3v1ZH6R2CS7NtIyZ/fmPDcYS7kpY3knrV9m7qB9qaEddQBFOO
+4N97a1/EtWWFtcPafeuADmiVw1lM3N1+TE2GKeFwXt4AjSe4cAiY89M42hLhLsGz
+bGBtcN54sDUr3Z9G4Zl1h8NmPIf/xo0+6/Pu6/dvAoGBAL6njcZmgaWeq29pQkL2
+FXxtGC6joBZWLd/gdUXD+56834DzrqgupKb0Ee+KlgbgEZKeHXWh+Z5JjG1PJ9Hs
+mCIoyO8ZqXA2zS7rkOnl5NsTSfMdJoCHkXqth+x18lJCuRVIEyFaDurPckakrdvG
+NDcat1Rk+KIk0hp65ll+Amx2
+-----END PRIVATE KEY-----
diff --git a/example/trivial/trivial.go b/example/trivial/trivial.go
index 50ca554b..cf65a22e 100644
--- a/example/trivial/trivial.go
+++ b/example/trivial/trivial.go
@@ -12,8 +12,26 @@ import (
"github.com/lightstep/saml/samlsp"
)
+var samlMiddleware *samlsp.Middleware
+
func hello(w http.ResponseWriter, r *http.Request) {
- fmt.Fprintf(w, "Hello, %s!", samlsp.AttributeFromContext(r.Context(), "cn"))
+ 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() {
@@ -26,12 +44,11 @@ func main() {
panic(err) // TODO handle error
}
- rootURL, _ := url.Parse("http://localhost:8000")
- idpMetadataURL, _ := url.Parse("https://samltest.id/saml/idp")
-
- idpMetadata, err := samlsp.FetchMetadata(
- context.Background(),
- http.DefaultClient,
+ 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
@@ -48,8 +65,17 @@ func main() {
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)
- http.Handle("/hello", samlSP.RequireAccount(app))
- http.Handle("/saml/", samlSP)
+ slo := http.HandlerFunc(logout)
+ http.Handle("/hello", samlMiddleware.RequireAccount(app))
+ http.Handle("/saml/", samlMiddleware)
+ http.Handle("/logout", slo)
http.ListenAndServe(":8000", nil)
}
diff --git a/go.mod b/go.mod
index c4dad961..548464ab 100644
--- a/go.mod
+++ b/go.mod
@@ -5,21 +5,17 @@ go 1.17
require (
github.com/beevik/etree v1.1.0
github.com/crewjam/httperr v0.2.0
- github.com/dchest/uniuri v0.0.0-20200228104902-7aecb25e1fe5
- github.com/form3tech-oss/jwt-go v3.2.2+incompatible
- github.com/google/go-cmp v0.5.5
- github.com/kr/pretty v0.2.1
+ github.com/golang-jwt/jwt/v4 v4.4.1
+ github.com/google/go-cmp v0.5.7
github.com/mattermost/xml-roundtrip-validator v0.1.0
- github.com/russellhaering/goxmldsig v1.1.0
- github.com/zenazn/goji v1.0.1
- golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2
+ github.com/russellhaering/goxmldsig v1.1.1
+ golang.org/x/crypto v0.0.0-20220128200615-198e4374d7ed
gotest.tools v2.2.0+incompatible
)
require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/jonboulle/clockwork v0.2.2 // indirect
- github.com/kr/text v0.2.0 // indirect
github.com/pkg/errors v0.9.1 // indirect
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
)
diff --git a/go.sum b/go.sum
index a45b921f..34e5b9e1 100644
--- a/go.sum
+++ b/go.sum
@@ -6,49 +6,53 @@ github.com/crewjam/httperr v0.2.0/go.mod h1:Jlz+Sg/XqBQhyMjdDiC+GNNRzZTD7x39Gu3p
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/form3tech-oss/jwt-go v3.2.2+incompatible h1:TcekIExNqud5crz4xD2pavyTgWiPvpYe4Xau31I0PRk=
-github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k=
-github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
-github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
-github.com/jonboulle/clockwork v0.2.0/go.mod h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUBGnn1kMkgxc8=
+github.com/golang-jwt/jwt/v4 v4.4.1 h1:pC5DB52sCeK48Wlb9oPcdhnjkz1TKt1D/P7WKJ0kUcQ=
+github.com/golang-jwt/jwt/v4 v4.4.1/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
+github.com/google/go-cmp v0.5.7 h1:81/ik6ipDQS2aGcBfIN5dHDB36BwrStyeAQquSYCV4o=
+github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE=
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.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI=
+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/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/russellhaering/goxmldsig v1.1.0 h1:lK/zeJie2sqG52ZAlPNn1oBBqsIsEKypUUBGpYYF6lk=
-github.com/russellhaering/goxmldsig v1.1.0/go.mod h1:QK8GhXPB3+AfuCrfo0oRISa9NfzeCpWmxeGnqEpDF9o=
+github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
+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/crypto v0.0.0-20220128200615-198e4374d7ed h1:YoWVYYAfvQ4ddHv3OKmIvX7NCAhFGTj62VP2l2kfBbA=
+golang.org/x/crypto v0.0.0-20220128200615-198e4374d7ed/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
+golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
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/text v0.3.6/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/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 h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
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=
diff --git a/identity_provider.go b/identity_provider.go
index d08803f8..6d38f66f 100644
--- a/identity_provider.go
+++ b/identity_provider.go
@@ -120,7 +120,7 @@ func (idp *IdentityProvider) Metadata() *EntityDescriptor {
ed := &EntityDescriptor{
EntityID: idp.MetadataURL.String(),
- ValidUntil: &validUntil,
+ ValidUntil: TimeNow().Add(validDuration),
CacheDuration: validDuration,
IDPSSODescriptors: []IDPSSODescriptor{
{
@@ -131,13 +131,21 @@ func (idp *IdentityProvider) Metadata() *EntityDescriptor {
{
Use: "signing",
KeyInfo: KeyInfo{
- Certificate: certStr,
+ X509Data: X509Data{
+ X509Certificates: []X509Certificate{
+ {Data: certStr},
+ },
+ },
},
},
{
Use: "encryption",
KeyInfo: KeyInfo{
- Certificate: certStr,
+ X509Data: X509Data{
+ X509Certificates: []X509Certificate{
+ {Data: certStr},
+ },
+ },
},
EncryptionMethods: []EncryptionMethod{
{Algorithm: "http://www.w3.org/2001/04/xmlenc#aes128-cbc"},
@@ -418,7 +426,7 @@ func (req *IdpAuthnRequest) Validate() error {
}
}
- if req.Request.IssueInstant.Add(MaxIssueDelay).Before(TimeNow()) {
+ if req.Request.IssueInstant.Add(MaxIssueDelay).Before(req.Now) {
return fmt.Errorf("request expired at %s",
req.Request.IssueInstant.Add(MaxIssueDelay))
}
@@ -728,8 +736,8 @@ func (DefaultAssertionMaker) MakeAssertion(req *IdpAuthnRequest, session *Sessio
// allow for some clock skew in the validity period using the
// issuer's apparent clock.
- notBefore := TimeNow().Add(-1 * MaxClockSkew)
- notOnOrAfterAfter := notBefore.Add(MaxClockSkew).Add(MaxIssueDelay)
+ 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)
@@ -756,7 +764,7 @@ func (DefaultAssertionMaker) MakeAssertion(req *IdpAuthnRequest, session *Sessio
SubjectConfirmationData: &SubjectConfirmationData{
Address: req.HTTPRequest.RemoteAddr,
InResponseTo: req.Request.ID,
- NotOnOrAfter: TimeNow().Add(MaxIssueDelay),
+ NotOnOrAfter: req.Now.Add(MaxIssueDelay),
Recipient: req.ACSEndpoint.Location,
},
},
@@ -854,7 +862,7 @@ func (req *IdpAuthnRequest) MakeAssertionEl() error {
encryptor := xmlenc.OAEP()
encryptor.BlockCipher = xmlenc.AES128CBC
encryptor.DigestMethod = &xmlenc.SHA1
- encryptedDataEl, err := encryptor.Encrypt(certBuf, signedAssertionBuf)
+ encryptedDataEl, err := encryptor.Encrypt(certBuf, signedAssertionBuf, nil)
if err != nil {
return err
}
@@ -927,7 +935,7 @@ func (req *IdpAuthnRequest) getSPEncryptionCert() (*x509.Certificate, error) {
certStr := ""
for _, keyDescriptor := range req.SPSSODescriptor.KeyDescriptors {
if keyDescriptor.Use == "encryption" {
- certStr = keyDescriptor.KeyInfo.Certificate
+ certStr = keyDescriptor.KeyInfo.X509Data.X509Certificates[0].Data
break
}
}
@@ -936,8 +944,8 @@ func (req *IdpAuthnRequest) getSPEncryptionCert() (*x509.Certificate, error) {
// non-empty cert we find.
if certStr == "" {
for _, keyDescriptor := range req.SPSSODescriptor.KeyDescriptors {
- if keyDescriptor.Use == "" && keyDescriptor.KeyInfo.Certificate != "" {
- certStr = keyDescriptor.KeyInfo.Certificate
+ if keyDescriptor.Use == "" && len(keyDescriptor.KeyInfo.X509Data.X509Certificates) != 0 && keyDescriptor.KeyInfo.X509Data.X509Certificates[0].Data != "" {
+ certStr = keyDescriptor.KeyInfo.X509Data.X509Certificates[0].Data
break
}
}
@@ -987,7 +995,7 @@ func (req *IdpAuthnRequest) MakeResponse() error {
Destination: req.ACSEndpoint.Location,
ID: fmt.Sprintf("id-%x", randomBytes(20)),
InResponseTo: req.Request.ID,
- IssueInstant: TimeNow(),
+ IssueInstant: req.Now,
Version: "2.0",
Issuer: &Issuer{
Format: "urn:oasis:names:tc:SAML:2.0:nameid-format:entity",
diff --git a/identity_provider_go116_test.go b/identity_provider_go116_test.go
new file mode 100644
index 00000000..ead0a780
--- /dev/null
+++ b/identity_provider_go116_test.go
@@ -0,0 +1,57 @@
+//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))
+ }
+}
diff --git a/identity_provider_test.go b/identity_provider_test.go
index 15bb6689..0d2d2de7 100644
--- a/identity_provider_test.go
+++ b/identity_provider_test.go
@@ -25,7 +25,7 @@ import (
"gotest.tools/golden"
"github.com/beevik/etree"
- "github.com/form3tech-oss/jwt-go"
+ "github.com/golang-jwt/jwt/v4"
"github.com/lightstep/saml/logger"
"github.com/lightstep/saml/testsaml"
@@ -141,11 +141,11 @@ func (mspp *mockServiceProviderProvider) GetServiceProvider(r *http.Request, ser
}
func TestIDPCanProduceMetadata(t *testing.T) {
- // TODO: LightStep had: c.Skip("broken")
+ // TODO: LightStep had:
+ // validUntil := time.Date(2015, time.December, 3, 1, 57, 9, 000000000, time.UTC)
test := NewIdentifyProviderTest(t)
- validUntil := time.Date(2015, time.December, 3, 1, 57, 9, 000000000, time.UTC)
expected := &EntityDescriptor{
- ValidUntil: &validUntil,
+ ValidUntil: TimeNow().Add(DefaultValidDuration),
CacheDuration: DefaultValidDuration,
EntityID: "https://idp.example.com/saml/metadata",
IDPSSODescriptors: []IDPSSODescriptor{
@@ -157,16 +157,24 @@ func TestIDPCanProduceMetadata(t *testing.T) {
{
Use: "signing",
KeyInfo: KeyInfo{
- XMLName: xml.Name{},
- Certificate: "MIIB7zCCAVgCCQDFzbKIp7b3MTANBgkqhkiG9w0BAQUFADA8MQswCQYDVQQGEwJVUzELMAkGA1UECAwCR0ExDDAKBgNVBAoMA2ZvbzESMBAGA1UEAwwJbG9jYWxob3N0MB4XDTEzMTAwMjAwMDg1MVoXDTE0MTAwMjAwMDg1MVowPDELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkdBMQwwCgYDVQQKDANmb28xEjAQBgNVBAMMCWxvY2FsaG9zdDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA1PMHYmhZj308kWLhZVT4vOulqx/9ibm5B86fPWwUKKQ2i12MYtz07tzukPymisTDhQaqyJ8Kqb/6JjhmeMnEOdTvSPmHO8m1ZVveJU6NoKRn/mP/BD7FW52WhbrUXLSeHVSKfWkNk6S4hk9MV9TswTvyRIKvRsw0X/gfnqkroJcCAwEAATANBgkqhkiG9w0BAQUFAAOBgQCMMlIO+GNcGekevKgkakpMdAqJfs24maGb90DvTLbRZRD7Xvn1MnVBBS9hzlXiFLYOInXACMW5gcoRFfeTQLSouMM8o57h0uKjfTmuoWHLQLi6hnF+cvCsEFiJZ4AbF+DgmO6TarJ8O05t8zvnOwJlNCASPZRH/JmF8tX0hoHuAQ==",
+ 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{},
- Certificate: "MIIB7zCCAVgCCQDFzbKIp7b3MTANBgkqhkiG9w0BAQUFADA8MQswCQYDVQQGEwJVUzELMAkGA1UECAwCR0ExDDAKBgNVBAoMA2ZvbzESMBAGA1UEAwwJbG9jYWxob3N0MB4XDTEzMTAwMjAwMDg1MVoXDTE0MTAwMjAwMDg1MVowPDELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkdBMQwwCgYDVQQKDANmb28xEjAQBgNVBAMMCWxvY2FsaG9zdDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA1PMHYmhZj308kWLhZVT4vOulqx/9ibm5B86fPWwUKKQ2i12MYtz07tzukPymisTDhQaqyJ8Kqb/6JjhmeMnEOdTvSPmHO8m1ZVveJU6NoKRn/mP/BD7FW52WhbrUXLSeHVSKfWkNk6S4hk9MV9TswTvyRIKvRsw0X/gfnqkroJcCAwEAATANBgkqhkiG9w0BAQUFAAOBgQCMMlIO+GNcGekevKgkakpMdAqJfs24maGb90DvTLbRZRD7Xvn1MnVBBS9hzlXiFLYOInXACMW5gcoRFfeTQLSouMM8o57h0uKjfTmuoWHLQLi6hnF+cvCsEFiJZ4AbF+DgmO6TarJ8O05t8zvnOwJlNCASPZRH/JmF8tX0hoHuAQ==",
+ 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"},
@@ -202,48 +210,8 @@ func TestIDPHTTPCanHandleMetadataRequest(t *testing.T) {
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(w.Body.String(), "X509Certificate"`
+ 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.
@@ -209,6 +225,7 @@ type IDPSSODescriptor struct {
WantAuthnRequestsSigned *bool `xml:",attr"`
SingleSignOnServices []Endpoint `xml:"SingleSignOnService"`
+ ArtifactResolutionServices []Endpoint `xml:"ArtifactResolutionService"`
NameIDMappingServices []Endpoint `xml:"NameIDMappingService"`
AssertionIDRequestServices []Endpoint `xml:"AssertionIDRequestService"`
AttributeProfiles []string `xml:"AttributeProfile"`
diff --git a/metadata_test.go b/metadata_test.go
index 92ee157e..51f5bd8b 100644
--- a/metadata_test.go
+++ b/metadata_test.go
@@ -19,7 +19,9 @@ func TestCanParseMetadata(t *testing.T) {
var False = false
var True = true
- validUntil := time.Date(2001, time.February, 3, 4, 5, 6, 789000000, time.UTC)
+ // Lightstep fix:
+ validUntil := time.Date(2001, time.February, 3, 4, 5, 6, 789000000, time.UTC)
+
expected := EntityDescriptor{
EntityID: "https://dev.aa.kndr.org/users/auth/saml/metadata",
ID: "_af805d1c-c2e3-444e-9cf5-efc664eeace6",
@@ -88,7 +90,6 @@ func TestCanParseMetadata(t *testing.T) {
}
func TestCanProduceSPMetadata(t *testing.T) {
- // TODO: LightStep - still broken? t.Skip("broken")
validUntil, _ := time.Parse("2006-02-01T15:04:05.000000", "2013-10-03T00:32:19.104000")
AuthnRequestsSigned := true
WantAssertionsSigned := true
@@ -107,7 +108,10 @@ func TestCanProduceSPMetadata(t *testing.T) {
{
Use: "encryption",
KeyInfo: KeyInfo{
- Certificate: `MIIB7zCCAVgCCQDFzbKIp7b3MTANBgkqhkiG9w0BAQUFADA8MQswCQYDVQQGEwJVUzELMAkGA1UE
+ X509Data: X509Data{
+ X509Certificates: []X509Certificate{
+ {
+ Data: `MIIB7zCCAVgCCQDFzbKIp7b3MTANBgkqhkiG9w0BAQUFADA8MQswCQYDVQQGEwJVUzELMAkGA1UE
CAwCR0ExDDAKBgNVBAoMA2ZvbzESMBAGA1UEAwwJbG9jYWxob3N0MB4XDTEzMTAwMjAwMDg1MVoX
DTE0MTAwMjAwMDg1MVowPDELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkdBMQwwCgYDVQQKDANmb28x
EjAQBgNVBAMMCWxvY2FsaG9zdDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA1PMHYmhZj308
@@ -116,12 +120,18 @@ SPmHO8m1ZVveJU6NoKRn/mP/BD7FW52WhbrUXLSeHVSKfWkNk6S4hk9MV9TswTvyRIKvRsw0X/gf
nqkroJcCAwEAATANBgkqhkiG9w0BAQUFAAOBgQCMMlIO+GNcGekevKgkakpMdAqJfs24maGb90Dv
TLbRZRD7Xvn1MnVBBS9hzlXiFLYOInXACMW5gcoRFfeTQLSouMM8o57h0uKjfTmuoWHLQLi6hnF+
cvCsEFiJZ4AbF+DgmO6TarJ8O05t8zvnOwJlNCASPZRH/JmF8tX0hoHuAQ==`,
+ },
+ },
+ },
},
},
{
Use: "signing",
KeyInfo: KeyInfo{
- Certificate: `MIIB7zCCAVgCCQDFzbKIp7b3MTANBgkqhkiG9w0BAQUFADA8MQswCQYDVQQGEwJVUzELMAkGA1UE
+ X509Data: X509Data{
+ X509Certificates: []X509Certificate{
+ {
+ Data: `MIIB7zCCAVgCCQDFzbKIp7b3MTANBgkqhkiG9w0BAQUFADA8MQswCQYDVQQGEwJVUzELMAkGA1UE
CAwCR0ExDDAKBgNVBAoMA2ZvbzESMBAGA1UEAwwJbG9jYWxob3N0MB4XDTEzMTAwMjAwMDg1MVoX
DTE0MTAwMjAwMDg1MVowPDELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkdBMQwwCgYDVQQKDANmb28x
EjAQBgNVBAMMCWxvY2FsaG9zdDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA1PMHYmhZj308
@@ -130,6 +140,9 @@ SPmHO8m1ZVveJU6NoKRn/mP/BD7FW52WhbrUXLSeHVSKfWkNk6S4hk9MV9TswTvyRIKvRsw0X/gf
nqkroJcCAwEAATANBgkqhkiG9w0BAQUFAAOBgQCMMlIO+GNcGekevKgkakpMdAqJfs24maGb90Dv
TLbRZRD7Xvn1MnVBBS9hzlXiFLYOInXACMW5gcoRFfeTQLSouMM8o57h0uKjfTmuoWHLQLi6hnF+
cvCsEFiJZ4AbF+DgmO6TarJ8O05t8zvnOwJlNCASPZRH/JmF8tX0hoHuAQ==`,
+ },
+ },
+ },
},
},
},
diff --git a/samlidp/go.mod b/samlidp/go.mod
new file mode 100644
index 00000000..a53ba2cc
--- /dev/null
+++ b/samlidp/go.mod
@@ -0,0 +1,14 @@
+module github.com/crewjam/saml/samlidp
+
+replace github.com/crewjam/saml => ../
+
+go 1.13
+
+require (
+ github.com/crewjam/saml v0.0.0-00010101000000-000000000000
+ github.com/golang-jwt/jwt/v4 v4.2.0
+ github.com/mattermost/xml-roundtrip-validator v0.1.0
+ github.com/zenazn/goji v1.0.1
+ golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3
+ gotest.tools v2.2.0+incompatible
+)
diff --git a/samlidp/go.sum b/samlidp/go.sum
new file mode 100644
index 00000000..a653625b
--- /dev/null
+++ b/samlidp/go.sum
@@ -0,0 +1,63 @@
+github.com/beevik/etree v1.1.0 h1:T0xke/WvNtMoCqgzPhkX2r4rjY3GDZFi+FjpRZY2Jbs=
+github.com/beevik/etree v1.1.0/go.mod h1:r8Aw8JqVegEf0w2fDnATrX9VpkMcyFeM0FhwO62wh+A=
+github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
+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/golang-jwt/jwt/v4 v4.2.0 h1:besgBTC8w8HjP6NzQdxwKH9Z5oQMZ24ThTrHp3cZ8eU=
+github.com/golang-jwt/jwt/v4 v4.2.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-20211215153901-e495a2d5b3d3 h1:0es+/5331RGQPcXlMfP+WrnIIS6dNnNRe0WB02W0F4M=
+golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
+golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
+golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
+golang.org/x/text v0.3.6/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=
diff --git a/samlidp/samlidp_test.go b/samlidp/samlidp_test.go
index 00c0d730..12e4cc81 100644
--- a/samlidp/samlidp_test.go
+++ b/samlidp/samlidp_test.go
@@ -16,7 +16,7 @@ import (
is "gotest.tools/assert/cmp"
"gotest.tools/golden"
- "github.com/form3tech-oss/jwt-go"
+ "github.com/golang-jwt/jwt/v4"
"github.com/lightstep/saml"
"github.com/lightstep/saml/logger"
diff --git a/samlidp/session.go b/samlidp/session.go
index 10ed05d1..0d2514d3 100644
--- a/samlidp/session.go
+++ b/samlidp/session.go
@@ -13,6 +13,7 @@ import (
"golang.org/x/crypto/bcrypt"
"github.com/lightstep/saml"
+ "github.com/zenazn/goji/web"
)
var sessionMaxAge = time.Hour
diff --git a/samlidp/testdata/http_metadata_response.html b/samlidp/testdata/http_metadata_response.html
index e204fd3e..2090d9e6 100644
--- a/samlidp/testdata/http_metadata_response.html
+++ b/samlidp/testdata/http_metadata_response.html
@@ -2,15 +2,15 @@
-
- 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
+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
-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
+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
@@ -22,4 +22,4 @@
-
\ No newline at end of file
+
diff --git a/samlidp/testdata/sp_metadata.xml b/samlidp/testdata/sp_metadata.xml
index f5cef423..3ba9fcc5 100644
--- a/samlidp/testdata/sp_metadata.xml
+++ b/samlidp/testdata/sp_metadata.xml
@@ -1 +1 @@
-MIIB7zCCAVgCCQDFzbKIp7b3MTANBgkqhkiG9w0BAQUFADA8MQswCQYDVQQGEwJVUzELMAkGA1UECAwCR0ExDDAKBgNVBAoMA2ZvbzESMBAGA1UEAwwJbG9jYWxob3N0MB4XDTEzMTAwMjAwMDg1MVoXDTE0MTAwMjAwMDg1MVowPDELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkdBMQwwCgYDVQQKDANmb28xEjAQBgNVBAMMCWxvY2FsaG9zdDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA1PMHYmhZj308kWLhZVT4vOulqx/9ibm5B86fPWwUKKQ2i12MYtz07tzukPymisTDhQaqyJ8Kqb/6JjhmeMnEOdTvSPmHO8m1ZVveJU6NoKRn/mP/BD7FW52WhbrUXLSeHVSKfWkNk6S4hk9MV9TswTvyRIKvRsw0X/gfnqkroJcCAwEAATANBgkqhkiG9w0BAQUFAAOBgQCMMlIO+GNcGekevKgkakpMdAqJfs24maGb90DvTLbRZRD7Xvn1MnVBBS9hzlXiFLYOInXACMW5gcoRFfeTQLSouMM8o57h0uKjfTmuoWHLQLi6hnF+cvCsEFiJZ4AbF+DgmO6TarJ8O05t8zvnOwJlNCASPZRH/JmF8tX0hoHuAQ==MIIB7zCCAVgCCQDFzbKIp7b3MTANBgkqhkiG9w0BAQUFADA8MQswCQYDVQQGEwJVUzELMAkGA1UECAwCR0ExDDAKBgNVBAoMA2ZvbzESMBAGA1UEAwwJbG9jYWxob3N0MB4XDTEzMTAwMjAwMDg1MVoXDTE0MTAwMjAwMDg1MVowPDELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkdBMQwwCgYDVQQKDANmb28xEjAQBgNVBAMMCWxvY2FsaG9zdDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA1PMHYmhZj308kWLhZVT4vOulqx/9ibm5B86fPWwUKKQ2i12MYtz07tzukPymisTDhQaqyJ8Kqb/6JjhmeMnEOdTvSPmHO8m1ZVveJU6NoKRn/mP/BD7FW52WhbrUXLSeHVSKfWkNk6S4hk9MV9TswTvyRIKvRsw0X/gfnqkroJcCAwEAATANBgkqhkiG9w0BAQUFAAOBgQCMMlIO+GNcGekevKgkakpMdAqJfs24maGb90DvTLbRZRD7Xvn1MnVBBS9hzlXiFLYOInXACMW5gcoRFfeTQLSouMM8o57h0uKjfTmuoWHLQLi6hnF+cvCsEFiJZ4AbF+DgmO6TarJ8O05t8zvnOwJlNCASPZRH/JmF8tX0hoHuAQ==
\ No newline at end of file
+MIIB7zCCAVgCCQDFzbKIp7b3MTANBgkqhkiG9w0BAQUFADA8MQswCQYDVQQGEwJVUzELMAkGA1UECAwCR0ExDDAKBgNVBAoMA2ZvbzESMBAGA1UEAwwJbG9jYWxob3N0MB4XDTEzMTAwMjAwMDg1MVoXDTE0MTAwMjAwMDg1MVowPDELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkdBMQwwCgYDVQQKDANmb28xEjAQBgNVBAMMCWxvY2FsaG9zdDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA1PMHYmhZj308kWLhZVT4vOulqx/9ibm5B86fPWwUKKQ2i12MYtz07tzukPymisTDhQaqyJ8Kqb/6JjhmeMnEOdTvSPmHO8m1ZVveJU6NoKRn/mP/BD7FW52WhbrUXLSeHVSKfWkNk6S4hk9MV9TswTvyRIKvRsw0X/gfnqkroJcCAwEAATANBgkqhkiG9w0BAQUFAAOBgQCMMlIO+GNcGekevKgkakpMdAqJfs24maGb90DvTLbRZRD7Xvn1MnVBBS9hzlXiFLYOInXACMW5gcoRFfeTQLSouMM8o57h0uKjfTmuoWHLQLi6hnF+cvCsEFiJZ4AbF+DgmO6TarJ8O05t8zvnOwJlNCASPZRH/JmF8tX0hoHuAQ==MIIB7zCCAVgCCQDFzbKIp7b3MTANBgkqhkiG9w0BAQUFADA8MQswCQYDVQQGEwJVUzELMAkGA1UECAwCR0ExDDAKBgNVBAoMA2ZvbzESMBAGA1UEAwwJbG9jYWxob3N0MB4XDTEzMTAwMjAwMDg1MVoXDTE0MTAwMjAwMDg1MVowPDELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkdBMQwwCgYDVQQKDANmb28xEjAQBgNVBAMMCWxvY2FsaG9zdDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA1PMHYmhZj308kWLhZVT4vOulqx/9ibm5B86fPWwUKKQ2i12MYtz07tzukPymisTDhQaqyJ8Kqb/6JjhmeMnEOdTvSPmHO8m1ZVveJU6NoKRn/mP/BD7FW52WhbrUXLSeHVSKfWkNk6S4hk9MV9TswTvyRIKvRsw0X/gfnqkroJcCAwEAATANBgkqhkiG9w0BAQUFAAOBgQCMMlIO+GNcGekevKgkakpMdAqJfs24maGb90DvTLbRZRD7Xvn1MnVBBS9hzlXiFLYOInXACMW5gcoRFfeTQLSouMM8o57h0uKjfTmuoWHLQLi6hnF+cvCsEFiJZ4AbF+DgmO6TarJ8O05t8zvnOwJlNCASPZRH/JmF8tX0hoHuAQ==
diff --git a/samlidp/util.go b/samlidp/util.go
index 3352ecdb..c625acec 100644
--- a/samlidp/util.go
+++ b/samlidp/util.go
@@ -5,6 +5,7 @@ import (
"encoding/xml"
"errors"
"io"
+ xrv "github.com/mattermost/xml-roundtrip-validator"
xrv "github.com/mattermost/xml-roundtrip-validator"
diff --git a/samlidp/util_go116_test.go b/samlidp/util_go116_test.go
new file mode 100644
index 00000000..16f54740
--- /dev/null
+++ b/samlidp/util_go116_test.go
@@ -0,0 +1,26 @@
+//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/}]}"))
+}
diff --git a/samlidp/util_go117_test.go b/samlidp/util_go117_test.go
new file mode 100644
index 00000000..2c273bc1
--- /dev/null
+++ b/samlidp/util_go117_test.go
@@ -0,0 +1,26 @@
+//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"))
+}
diff --git a/samlsp/fetch_metadata_go116_test.go b/samlsp/fetch_metadata_go116_test.go
new file mode 100644
index 00000000..91c3aa69
--- /dev/null
+++ b/samlsp/fetch_metadata_go116_test.go
@@ -0,0 +1,34 @@
+//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))
+}
diff --git a/samlsp/middleware.go b/samlsp/middleware.go
index 022726e3..c6e58432 100644
--- a/samlsp/middleware.go
+++ b/samlsp/middleware.go
@@ -42,6 +42,7 @@ 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
}
@@ -91,7 +92,7 @@ func (m *Middleware) ServeACS(w http.ResponseWriter, r *http.Request) {
return
}
- m.CreateSessionFromAssertion(w, r, assertion)
+ m.CreateSessionFromAssertion(w, r, assertion, m.ServiceProvider.DefaultRedirectURI)
return
}
@@ -140,7 +141,7 @@ func (m *Middleware) HandleStartAuthFlow(w http.ResponseWriter, r *http.Request)
}
}
- authReq, err := m.ServiceProvider.MakeAuthenticationRequest(bindingLocation, binding)
+ authReq, err := m.ServiceProvider.MakeAuthenticationRequest(bindingLocation, binding, m.ResponseBinding)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
@@ -181,8 +182,7 @@ func (m *Middleware) HandleStartAuthFlow(w http.ResponseWriter, r *http.Request)
}
// 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 := "/"
+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 {
diff --git a/samlsp/middleware_test.go b/samlsp/middleware_test.go
index eca377ec..56f8f8dd 100644
--- a/samlsp/middleware_test.go
+++ b/samlsp/middleware_test.go
@@ -17,7 +17,7 @@ import (
"testing"
"time"
- "github.com/form3tech-oss/jwt-go"
+ "github.com/golang-jwt/jwt/v4"
dsig "github.com/russellhaering/goxmldsig"
"gotest.tools/assert"
is "gotest.tools/assert/cmp"
@@ -123,7 +123,6 @@ func (test *MiddlewareTest) makeTrackedRequest(id string) string {
}
func TestMiddlewareCanProduceMetadata(t *testing.T) {
- // TODO: Lightstep had: t.Skip("broken")
test := NewMiddlewareTest(t)
req, _ := http.NewRequest("GET", "/saml2/metadata", nil)
diff --git a/samlsp/new.go b/samlsp/new.go
index 93eaca28..2dbe4e5f 100644
--- a/samlsp/new.go
+++ b/samlsp/new.go
@@ -14,17 +14,20 @@ import (
// 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
- IDPMetadata *saml.EntityDescriptor
- SignRequest bool
- ForceAuthn bool // TODO(ross): this should be *bool
- CookieSameSite http.SameSite
- RelayStateFunc func(w http.ResponseWriter, r *http.Request) string
+ EntityID string
+ URL url.URL
+ Key *rsa.PrivateKey
+ Certificate *x509.Certificate
+ Intermediates []*x509.Certificate
+ HTTPClient *http.Client
+ 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,
@@ -94,18 +97,24 @@ func DefaultServiceProvider(opts Options) saml.ServiceProvider {
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,
+ EntityID: opts.EntityID,
+ Key: opts.Key,
+ Certificate: opts.Certificate,
+ HTTPClient: opts.HTTPClient,
+ Intermediates: opts.Intermediates,
+ MetadataURL: *metadataURL,
+ AcsURL: *acsURL,
+ SloURL: *sloURL,
+ IDPMetadata: opts.IDPMetadata,
+ ForceAuthn: forceAuthn,
+ SignatureMethod: signatureMethod,
+ AllowIDPInitiated: opts.AllowIDPInitiated,
+ DefaultRedirectURI: opts.DefaultRedirectURI,
}
}
@@ -119,10 +128,14 @@ 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
}
diff --git a/samlsp/request_tracker_jwt.go b/samlsp/request_tracker_jwt.go
index ca9df342..0e835307 100644
--- a/samlsp/request_tracker_jwt.go
+++ b/samlsp/request_tracker_jwt.go
@@ -5,7 +5,7 @@ import (
"fmt"
"time"
- "github.com/form3tech-oss/jwt-go"
+ github.com/golang-jwt/jwt/v4"
"github.com/lightstep/saml"
)
@@ -35,7 +35,7 @@ func (s JWTTrackedRequestCodec) Encode(value TrackedRequest) (string, error) {
now := saml.TimeNow()
claims := JWTTrackedRequestClaims{
StandardClaims: jwt.StandardClaims{
- Audience: []string{s.Audience},
+ Audience: []string{s.Audience},
ExpiresAt: now.Add(s.MaxAge).Unix(),
IssuedAt: now.Unix(),
Issuer: s.Issuer,
diff --git a/samlsp/session_jwt.go b/samlsp/session_jwt.go
index 3b9bdf9a..bcb0f27a 100644
--- a/samlsp/session_jwt.go
+++ b/samlsp/session_jwt.go
@@ -6,7 +6,7 @@ import (
"fmt"
"time"
- "github.com/form3tech-oss/jwt-go"
+ "github.com/golang-jwt/jwt/v4"
"github.com/lightstep/saml"
)
@@ -35,6 +35,7 @@ func (c JWTSessionCodec) New(assertion *saml.Assertion) (Session, error) {
now := saml.TimeNow()
claims := JWTSessionClaims{}
claims.SAMLSession = true
+ // Per spec, aud is an array. keep:
claims.Audience = []string{c.Audience}
claims.Issuer = c.Issuer
claims.IssuedAt = now.Unix()
diff --git a/samlsp/testdata/expected_middleware_metadata.xml b/samlsp/testdata/expected_middleware_metadata.xml
index 77a0ea58..74a51b1f 100644
--- a/samlsp/testdata/expected_middleware_metadata.xml
+++ b/samlsp/testdata/expected_middleware_metadata.xml
@@ -2,8 +2,8 @@
-
- MIIB7zCCAVgCCQDFzbKIp7b3MTANBgkqhkiG9w0BAQUFADA8MQswCQYDVQQGEwJVUzELMAkGA1UECAwCR0ExDDAKBgNVBAoMA2ZvbzESMBAGA1UEAwwJbG9jYWxob3N0MB4XDTEzMTAwMjAwMDg1MVoXDTE0MTAwMjAwMDg1MVowPDELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkdBMQwwCgYDVQQKDANmb28xEjAQBgNVBAMMCWxvY2FsaG9zdDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA1PMHYmhZj308kWLhZVT4vOulqx/9ibm5B86fPWwUKKQ2i12MYtz07tzukPymisTDhQaqyJ8Kqb/6JjhmeMnEOdTvSPmHO8m1ZVveJU6NoKRn/mP/BD7FW52WhbrUXLSeHVSKfWkNk6S4hk9MV9TswTvyRIKvRsw0X/gfnqkroJcCAwEAATANBgkqhkiG9w0BAQUFAAOBgQCMMlIO+GNcGekevKgkakpMdAqJfs24maGb90DvTLbRZRD7Xvn1MnVBBS9hzlXiFLYOInXACMW5gcoRFfeTQLSouMM8o57h0uKjfTmuoWHLQLi6hnF+cvCsEFiJZ4AbF+DgmO6TarJ8O05t8zvnOwJlNCASPZRH/JmF8tX0hoHuAQ==
+
+ MIIB7zCCAVgCCQDFzbKIp7b3MTANBgkqhkiG9w0BAQUFADA8MQswCQYDVQQGEwJVUzELMAkGA1UECAwCR0ExDDAKBgNVBAoMA2ZvbzESMBAGA1UEAwwJbG9jYWxob3N0MB4XDTEzMTAwMjAwMDg1MVoXDTE0MTAwMjAwMDg1MVowPDELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkdBMQwwCgYDVQQKDANmb28xEjAQBgNVBAMMCWxvY2FsaG9zdDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA1PMHYmhZj308kWLhZVT4vOulqx/9ibm5B86fPWwUKKQ2i12MYtz07tzukPymisTDhQaqyJ8Kqb/6JjhmeMnEOdTvSPmHO8m1ZVveJU6NoKRn/mP/BD7FW52WhbrUXLSeHVSKfWkNk6S4hk9MV9TswTvyRIKvRsw0X/gfnqkroJcCAwEAATANBgkqhkiG9w0BAQUFAAOBgQCMMlIO+GNcGekevKgkakpMdAqJfs24maGb90DvTLbRZRD7Xvn1MnVBBS9hzlXiFLYOInXACMW5gcoRFfeTQLSouMM8o57h0uKjfTmuoWHLQLi6hnF+cvCsEFiJZ4AbF+DgmO6TarJ8O05t8zvnOwJlNCASPZRH/JmF8tX0hoHuAQ==
@@ -13,5 +13,6 @@
+
-
\ No newline at end of file
+
diff --git a/samlsp/testdata/token.json b/samlsp/testdata/token.json
index 8187becd..b4681f23 100644
--- a/samlsp/testdata/token.json
+++ b/samlsp/testdata/token.json
@@ -45,4 +45,4 @@
]
},
"saml-session": true
-}
\ No newline at end of file
+}
diff --git a/schema.go b/schema.go
index 3c271138..70178981 100644
--- a/schema.go
+++ b/schema.go
@@ -48,11 +48,12 @@ type AuthnRequest struct {
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"`
- Destination string `xml:",attr"`
- Issuer *Issuer `xml:"urn:oasis:names:tc:SAML:2.0:assertion Issuer"`
+ 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
@@ -67,6 +68,9 @@ func (r *LogoutRequest) Element() *etree.Element {
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)
}
@@ -89,10 +93,12 @@ func (r *LogoutRequest) Element() *etree.Element {
func (r *LogoutRequest) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
type Alias LogoutRequest
aux := &struct {
- IssueInstant RelaxedTime `xml:",attr"`
+ IssueInstant RelaxedTime `xml:",attr"`
+ NotOnOrAfter *RelaxedTime `xml:",attr"`
*Alias
}{
IssueInstant: RelaxedTime(r.IssueInstant),
+ NotOnOrAfter: (*RelaxedTime)(r.NotOnOrAfter),
Alias: (*Alias)(r),
}
return e.Encode(aux)
@@ -102,7 +108,8 @@ func (r *LogoutRequest) MarshalXML(e *xml.Encoder, start xml.StartElement) error
func (r *LogoutRequest) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
type Alias LogoutRequest
aux := &struct {
- IssueInstant RelaxedTime `xml:",attr"`
+ IssueInstant RelaxedTime `xml:",attr"`
+ NotOnOrAfter *RelaxedTime `xml:",attr"`
*Alias
}{
Alias: (*Alias)(r),
@@ -111,6 +118,7 @@ func (r *LogoutRequest) UnmarshalXML(d *xml.Decoder, start xml.StartElement) err
return err
}
r.IssueInstant = time.Time(aux.IssueInstant)
+ r.NotOnOrAfter = (*time.Time)(aux.NotOnOrAfter)
return nil
}
@@ -296,6 +304,161 @@ func (a *NameIDPolicy) Element() *etree.Element {
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 ArtifactResolve"`
+ 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
@@ -515,7 +678,8 @@ const (
//
// See http://docs.oasis-open.org/security/saml/v2.0/saml-core-2.0-os.pdf ยง3.2.2.3
type StatusMessage struct {
- Value string
+ 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.
@@ -583,7 +747,7 @@ func (a *Assertion) Element() *etree.Element {
for _, attributeStatement := range a.AttributeStatements {
el.AddChild(attributeStatement.Element())
}
- err := etreeutils.TransformExcC14n(el, canonicalizerPrefixList)
+ err := etreeutils.TransformExcC14n(el, canonicalizerPrefixList, false)
if err != nil {
panic(err)
}
diff --git a/schema_test.go b/schema_test.go
index 22e58793..c5ccb20e 100644
--- a/schema_test.go
+++ b/schema_test.go
@@ -94,3 +94,173 @@ func TestAuthnStatementMarshalWithoutSessionNotOnOrAfter(t *testing.T) {
assert.Check(t, err)
assert.Check(t, is.DeepEqual(expected, actual))
}
+
+func TestArtifactResolveElement(t *testing.T) {
+ issueInstant := time.Date(2020, 7, 21, 12, 30, 45, 0, time.UTC)
+ expected := ArtifactResolve{
+ ID: "index",
+ Version: "version",
+ IssueInstant: issueInstant,
+ // Signature *etree.Element
+ }
+
+ doc := etree.NewDocument()
+ doc.SetRoot(expected.Element())
+ x, err := doc.WriteToBytes()
+ assert.Check(t, err)
+ assert.Check(t, is.Equal(``,
+ string(x)))
+
+ var actual ArtifactResolve
+ 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 TestArtifactResolveSoapRequest(t *testing.T) {
+ issueInstant := time.Date(2020, 7, 21, 12, 30, 45, 0, time.UTC)
+ expected := ArtifactResolve{
+ ID: "index",
+ Version: "version",
+ IssueInstant: issueInstant,
+ // Signature *etree.Element
+ }
+
+ doc := etree.NewDocument()
+ doc.SetRoot(expected.SoapRequest())
+ x, err := doc.WriteToBytes()
+ assert.Check(t, err)
+ assert.Check(t, is.Equal(``,
+ string(x)))
+}
+
+func TestArtifactResponseElement(t *testing.T) {
+ issueInstant := time.Date(2020, 7, 21, 12, 30, 45, 0, time.UTC)
+ status := Status{
+ XMLName: xml.Name{
+ Space: "urn:oasis:names:tc:SAML:2.0:protocol",
+ Local: "Status",
+ },
+ StatusCode: StatusCode{
+ XMLName: xml.Name{
+ Space: "urn:oasis:names:tc:SAML:2.0:protocol",
+ Local: "StatusCode",
+ },
+ Value: "value",
+ },
+ }
+ expected := ArtifactResponse{
+ ID: "index",
+ InResponseTo: "ID",
+ Version: "version",
+ IssueInstant: issueInstant,
+ Status: status,
+ Response: Response{
+ ID: "index",
+ InResponseTo: "ID",
+ Version: "version",
+ Destination: "destination",
+ Consent: "consent",
+ Status: status,
+ IssueInstant: issueInstant,
+ },
+ // Signature *etree.Element
+ }
+
+ doc := etree.NewDocument()
+ doc.SetRoot(expected.Element())
+ x, err := doc.WriteToBytes()
+ assert.Check(t, err)
+ assert.Check(t, is.Equal(``,
+ string(x)))
+
+ var actual ArtifactResponse
+ 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 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))
+}
diff --git a/service_provider.go b/service_provider.go
index cc973075..d34f25e8 100644
--- a/service_provider.go
+++ b/service_provider.go
@@ -74,6 +74,9 @@ type ServiceProvider struct {
Certificate *x509.Certificate
Intermediates []*x509.Certificate
+ // HTTPClient to use during SAML artifact resolution
+ HTTPClient *http.Client
+
// MetadataURL is the full URL to the metadata endpoint on this host,
// i.e. https://example.com/saml/metadata
MetadataURL url.URL
@@ -104,6 +107,9 @@ type ServiceProvider struct {
// 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
@@ -134,11 +140,12 @@ func (sp *ServiceProvider) Metadata() *EntityDescriptor {
if sp.MetadataValidDuration > 0 {
validDuration = sp.MetadataValidDuration
}
- validUntil := TimeNow().Add(validDuration)
authnRequestsSigned := len(sp.SignatureMethod) > 0
wantAssertionsSigned := true
- keyDescriptors := []KeyDescriptor{}
+ validUntil := TimeNow().Add(validDuration)
+
+ var keyDescriptors []KeyDescriptor
if sp.Certificate != nil {
certBytes := sp.Certificate.Raw
for _, intermediate := range sp.Intermediates {
@@ -148,7 +155,11 @@ func (sp *ServiceProvider) Metadata() *EntityDescriptor {
{
Use: "encryption",
KeyInfo: KeyInfo{
- Certificate: base64.StdEncoding.EncodeToString(certBytes),
+ X509Data: X509Data{
+ X509Certificates: []X509Certificate{
+ {Data: base64.StdEncoding.EncodeToString(certBytes)},
+ },
+ },
},
EncryptionMethods: []EncryptionMethod{
{Algorithm: "http://www.w3.org/2001/04/xmlenc#aes128-cbc"},
@@ -162,7 +173,11 @@ func (sp *ServiceProvider) Metadata() *EntityDescriptor {
keyDescriptors = append(keyDescriptors, KeyDescriptor{
Use: "signing",
KeyInfo: KeyInfo{
- Certificate: base64.StdEncoding.EncodeToString(certBytes),
+ X509Data: X509Data{
+ X509Certificates: []X509Certificate{
+ {Data: base64.StdEncoding.EncodeToString(certBytes)},
+ },
+ },
},
})
}
@@ -170,7 +185,8 @@ func (sp *ServiceProvider) Metadata() *EntityDescriptor {
return &EntityDescriptor{
EntityID: firstSet(sp.EntityID, sp.MetadataURL.String()),
- ValidUntil: &validUntil,
+ // Lightstep had: ValidUntil: &validUntil,
+ ValidUntil: validUntil,
SPSSODescriptors: []SPSSODescriptor{
{
@@ -197,6 +213,11 @@ func (sp *ServiceProvider) Metadata() *EntityDescriptor {
Location: sp.AcsURL.String(),
Index: 1,
},
+ {
+ Binding: HTTPArtifactBinding,
+ Location: sp.AcsURL.String(),
+ Index: 2,
+ },
},
},
},
@@ -207,7 +228,7 @@ func (sp *ServiceProvider) Metadata() *EntityDescriptor {
// 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)
+ req, err := sp.MakeAuthenticationRequest(sp.GetSSOBindingLocation(HTTPRedirectBinding), HTTPRedirectBinding, HTTPPostBinding)
if err != nil {
return nil, err
}
@@ -272,6 +293,19 @@ func (sp *ServiceProvider) GetSSOBindingLocation(binding string) string {
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 {
@@ -294,10 +328,12 @@ func (sp *ServiceProvider) getIDPSigningCerts() ([]*x509.Certificate, error) {
// either set to "signing" or is missing
for _, idpSSODescriptor := range sp.IDPMetadata.IDPSSODescriptors {
for _, keyDescriptor := range idpSSODescriptor.KeyDescriptors {
- if keyDescriptor.KeyInfo.Certificate != "" {
+ if len(keyDescriptor.KeyInfo.X509Data.X509Certificates) != 0 {
switch keyDescriptor.Use {
case "", "signing":
- certStrs = append(certStrs, keyDescriptor.KeyInfo.Certificate)
+ for _, certificate := range keyDescriptor.KeyInfo.X509Data.X509Certificates {
+ certStrs = append(certStrs, certificate.Data)
+ }
}
}
}
@@ -328,16 +364,38 @@ func (sp *ServiceProvider) getIDPSigningCerts() ([]*x509.Certificate, error) {
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) (*AuthnRequest, error) {
+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: HTTPPostBinding, // default binding for the response
+ ProtocolBinding: resultBinding, // default binding for the response
ID: fmt.Sprintf("id-%x", randomBytes(20)),
IssueInstant: TimeNow(),
Version: "2.0",
@@ -391,6 +449,24 @@ func GetSigningContext(sp *ServiceProvider) (*dsig.SigningContext, error) {
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 {
@@ -506,8 +582,8 @@ func (e ErrBadStatus) Error() string {
return e.Status
}
-func responseIsSigned(response *etree.Document) (bool, error) {
- signatureElement, err := findChild(response.Root(), "http://www.w3.org/2000/09/xmldsig#", "Signature")
+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
}
@@ -516,14 +592,8 @@ func responseIsSigned(response *etree.Document) (bool, error) {
// validateDestination validates the Destination attribute.
// If the response is signed, the Destination is required to be present.
-func (sp *ServiceProvider) validateDestination(response []byte, responseDom *Response) error {
- responseXML := etree.NewDocument()
- err := responseXML.ReadFromBytes(response)
- if err != nil {
- return err
- }
-
- signed, err := responseIsSigned(responseXML)
+func (sp *ServiceProvider) validateDestination(response *etree.Element, responseDom *Response) error {
+ signed, err := responseIsSigned(response)
if err != nil {
return err
}
@@ -531,8 +601,9 @@ func (sp *ServiceProvider) validateDestination(response []byte, responseDom *Res
// 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 != "" {
- // Compare everything except the port number, and allow parameters to appear in any order
- if !urlsMatchModuloPortNumbers(responseDom.Destination, &sp.AcsURL) {
+ // Lightstep had: // Compare everything except the port number, and allow parameters to appear in any order
+ // Lightstep had: if !urlsMatchModuloPortNumbers(responseDom.Destination, &sp.AcsURL) {
+ if responseDom.Destination != sp.AcsURL.String() {
return fmt.Errorf("`Destination` does not match AcsURL (expected %q, actual %q)", sp.AcsURL.String(), responseDom.Destination)
}
}
@@ -540,31 +611,166 @@ func (sp *ServiceProvider) validateDestination(response []byte, responseDom *Res
return nil
}
-// ParseResponse extracts the SAML IDP response received in req, validates
-// it, and returns the verified assertion.
+// 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"),
}
- rawResponseBuf, err := base64.StdEncoding.DecodeString(req.PostForm.Get("SAMLResponse"))
- if err != nil {
- retErr.PrivateErr = fmt.Errorf("cannot parse base64: %s", err)
+ 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)
+ client := sp.HTTPClient
+ if client == nil {
+ client = http.DefaultClient
+ }
+ response, err := client.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
}
- retErr.Response = string(rawResponseBuf)
- assertion, err := sp.ParseXMLResponse(rawResponseBuf, possibleRequestIDs)
+ 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 {
- return nil, err
+ retErr.PrivateErr = err
+ if updatedResponse != nil {
+ retErr.Response = *updatedResponse
+ }
+ return nil, retErr
}
return assertion, nil
-
}
-// ParseXMLResponse validates the SAML IDP response and
+// ParseXMLResponse parses and validates the SAML IDP response and
// returns the verified assertion.
//
// This function handles decrypting the message, verifying the digital
@@ -596,11 +802,37 @@ func (sp *ServiceProvider) ParseXMLResponse(decodedResponseXML []byte, possibleR
return nil, retErr
}
- if err := sp.validateDestination(decodedResponseXML, &resp); err != nil {
+ 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 {
@@ -614,42 +846,28 @@ func (sp *ServiceProvider) ParseXMLResponse(decodedResponseXML []byte, possibleR
}
if !requestIDvalid {
- retErr.PrivateErr = fmt.Errorf("`InResponseTo` does not match any of the possible request IDs (expected %v)", possibleRequestIDs)
- return nil, retErr
+ 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) {
- retErr.PrivateErr = fmt.Errorf("response IssueInstant expired at %s", resp.IssueInstant.Add(MaxIssueDelay))
- return nil, retErr
+ return nil, updatedResponse, fmt.Errorf("response IssueInstant expired at %s", resp.IssueInstant.Add(MaxIssueDelay))
}
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
+ return nil, updatedResponse, fmt.Errorf("response Issuer does not match the IDP metadata (expected %q)", sp.IDPMetadata.EntityID)
}
if resp.Status.StatusCode.Value != StatusSuccess {
- retErr.PrivateErr = ErrBadStatus{Status: resp.Status.StatusCode.Value}
- return nil, retErr
+ return nil, updatedResponse, ErrBadStatus{Status: resp.Status.StatusCode.Value}
}
var assertion *Assertion
if resp.EncryptedAssertion == nil {
-
- doc := etree.NewDocument()
- if err := doc.ReadFromBytes(decodedResponseXML); err != nil {
- retErr.PrivateErr = err
- return nil, retErr
- }
-
// TODO(ross): verify that the namespace is urn:oasis:names:tc:SAML:2.0:protocol
- responseEl := doc.Root()
if responseEl.Tag != "Response" {
- retErr.PrivateErr = fmt.Errorf("expected to find a response object, not %s", doc.Root().Tag)
- return nil, retErr
+ return nil, updatedResponse, fmt.Errorf("expected to find a response object, not %s", responseEl.Tag)
}
- if err = sp.validateSigned(responseEl); err != nil {
- retErr.PrivateErr = err
- return nil, retErr
+ 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
@@ -657,93 +875,64 @@ func (sp *ServiceProvider) ParseXMLResponse(decodedResponseXML []byte, possibleR
// decrypt the response
if resp.EncryptedAssertion != nil {
- doc := etree.NewDocument()
- if err := doc.ReadFromBytes(decodedResponseXML); err != nil {
- retErr.PrivateErr = err
- return nil, retErr
- }
-
// encrypted assertions are part of the signature
// before decrypting the response verify that
- responseSigned, err := responseIsSigned(doc)
+ responseSigned, err := responseIsSigned(responseEl)
if err != nil {
- retErr.PrivateErr = err
- return nil, retErr
+ return nil, updatedResponse, err
}
if responseSigned {
- if err := sp.validateSigned(doc.Root()); err != nil {
- retErr.PrivateErr = err
- return nil, retErr
+ if err := sp.validateSigned(responseEl); err != nil {
+ return nil, updatedResponse, err
}
}
var key interface{} = sp.Key
- keyEl := doc.FindElement("//EncryptedAssertion/EncryptedKey")
+ keyEl := responseEl.FindElement("//EncryptedAssertion/EncryptedKey")
if keyEl != nil {
key, err = xmlenc.Decrypt(sp.Key, keyEl)
if err != nil {
- retErr.PrivateErr = fmt.Errorf("failed to decrypt key from response: %s", err)
- return nil, retErr
+ return nil, updatedResponse, fmt.Errorf("failed to decrypt key from response: %s", err)
}
}
- el := doc.FindElement("//EncryptedAssertion/EncryptedData")
+ el := responseEl.FindElement("//EncryptedAssertion/EncryptedData")
plaintextAssertion, err := xmlenc.Decrypt(key, el)
if err != nil {
- retErr.PrivateErr = fmt.Errorf("failed to decrypt response: %s", err)
- return nil, retErr
+ return nil, updatedResponse, fmt.Errorf("failed to decrypt response: %s", err)
}
- retErr.Response = string(plaintextAssertion)
+ updatedResponse = new(string)
+ *updatedResponse = string(plaintextAssertion)
// TODO(ross): add test case for this
if err := xrv.Validate(bytes.NewReader(plaintextAssertion)); err != nil {
- retErr.PrivateErr = fmt.Errorf("plaintext response contains invalid XML: %s", err)
- return nil, retErr
+ return nil, updatedResponse, fmt.Errorf("plaintext response contains invalid XML: %s", err)
}
- doc = etree.NewDocument()
+ doc := etree.NewDocument()
if err := doc.ReadFromBytes(plaintextAssertion); err != nil {
- retErr.PrivateErr = fmt.Errorf("cannot parse plaintext response %v", err)
- return nil, retErr
+ 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 {
- retErr.PrivateErr = err
- return nil, retErr
+ 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 {
- retErr.PrivateErr = err
- return nil, retErr
+ return nil, updatedResponse, err
}
}
if err := sp.validateAssertion(assertion, possibleRequestIDs, now); err != nil {
- retErr.PrivateErr = fmt.Errorf("assertion invalid: %s", err)
- return nil, retErr
+ return nil, updatedResponse, fmt.Errorf("assertion invalid: %s", err)
}
- return assertion, nil
-}
-
-// urlsMatchModuloPortNumbers returns true if the URLs are equivalent, ignoring port numbers and parameter order
-func urlsMatchModuloPortNumbers(s string, b *url.URL) bool {
- a, err := url.Parse(s)
- if err != nil {
- return false
- }
- if a == nil && b == nil {
- return true
- }
- if a == nil || b == nil {
- return false
- }
- return a.Hostname() == b.Hostname() && a.Path == b.Path && reflect.DeepEqual(a.Query(), b.Query())
+ return assertion, updatedResponse, nil
}
// validateAssertion checks that the conditions specified in assertion match
@@ -788,8 +977,8 @@ func (sp *ServiceProvider) validateAssertion(assertion *Assertion, possibleReque
return fmt.Errorf("assertion SubjectConfirmation one of the possible request IDs (%v)", possibleRequestIDs)
}
}
- if !urlsMatchModuloPortNumbers(subjectConfirmation.SubjectConfirmationData.Recipient, &sp.AcsURL) {
- return fmt.Errorf("SubjectConfirmation Recipient is not %s", sp.AcsURL.String())
+ 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")
@@ -805,7 +994,8 @@ func (sp *ServiceProvider) validateAssertion(assertion *Assertion, possibleReque
audienceRestrictionsValid := len(assertion.Conditions.AudienceRestrictions) == 0
audience := firstSet(sp.EntityID, sp.MetadataURL.String())
for _, audienceRestriction := range assertion.Conditions.AudienceRestrictions {
- if urlsMatchModuloPortNumbers(audienceRestriction.Audience.Value, &sp.MetadataURL) {
+ // Lightstep had: if urlsMatchModuloPortNumbers(audienceRestriction.Audience.Value, &sp.MetadataURL) {
+ if audienceRestriction.Audience.Value == audience {
audienceRestrictionsValid = true
}
}
@@ -843,6 +1033,42 @@ func findChild(parentEl *etree.Element, childNS string, childTag string) (*etree
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 {
@@ -1289,11 +1515,7 @@ func (sp *ServiceProvider) ValidateLogoutResponseForm(postFormData string) error
}
responseEl := doc.Root()
- if err = sp.validateSigned(responseEl); err != nil {
- return err
- }
-
- return nil
+ return sp.validateSigned(responseEl)
}
// ValidateLogoutResponseRedirect returns a nil error if the logout response is valid.
@@ -1334,11 +1556,7 @@ func (sp *ServiceProvider) ValidateLogoutResponseRedirect(queryParameterData str
}
responseEl := doc.Root()
- if err = sp.validateSigned(responseEl); err != nil {
- return err
- }
-
- return nil
+ return sp.validateSigned(responseEl)
}
// validateLogoutResponse validates the LogoutResponse fields. Returns a nil error if the LogoutResponse is valid.
diff --git a/service_provider_go116_test.go b/service_provider_go116_test.go
new file mode 100644
index 00000000..77395e01
--- /dev/null
+++ b/service_provider_go116_test.go
@@ -0,0 +1,136 @@
+//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"))
+}
diff --git a/service_provider_go117_test.go b/service_provider_go117_test.go
new file mode 100644
index 00000000..1f5a07d5
--- /dev/null
+++ b/service_provider_go117_test.go
@@ -0,0 +1,136 @@
+//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"))
+}
diff --git a/service_provider_test.go b/service_provider_test.go
index d2a9d520..1561a8f4 100644
--- a/service_provider_test.go
+++ b/service_provider_test.go
@@ -14,13 +14,14 @@ import (
"testing"
"time"
- "github.com/lightstep/saml/testsaml"
"gotest.tools/assert"
is "gotest.tools/assert/cmp"
"gotest.tools/golden"
"github.com/beevik/etree"
dsig "github.com/russellhaering/goxmldsig"
+
+ "github.com/lightstep/saml/testsaml"
)
type ServiceProviderTest struct {
@@ -80,7 +81,7 @@ func TestSPCanSetAuthenticationNameIDFormat(t *testing.T) {
}
// defaults to "transient"
- req, err := s.MakeAuthenticationRequest("", HTTPRedirectBinding)
+ req, err := s.MakeAuthenticationRequest("", HTTPRedirectBinding, HTTPPostBinding)
assert.Check(t, err)
assert.Check(t, is.Equal(string(TransientNameIDFormat), *req.NameIDPolicy.Format))
@@ -92,13 +93,13 @@ func TestSPCanSetAuthenticationNameIDFormat(t *testing.T) {
// explicitly set to "unspecified"
s.AuthnNameIDFormat = UnspecifiedNameIDFormat
- req, err = s.MakeAuthenticationRequest("", HTTPRedirectBinding)
+ 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)
+ req, err = s.MakeAuthenticationRequest("", HTTPRedirectBinding, HTTPPostBinding)
assert.Check(t, err)
assert.Check(t, is.Equal(string(EmailAddressNameIDFormat), *req.NameIDPolicy.Format))
}
@@ -157,6 +158,58 @@ func TestCanProduceMetadataNoCerts(t *testing.T) {
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"),
+ 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{
@@ -307,7 +360,7 @@ func TestSPFailToProduceSignedRequestWithBogusSignatureMethod(t *testing.T) {
assert.Check(t, err)
_, err = s.MakeRedirectAuthenticationRequest("relayState")
- assert.Check(t, is.ErrorContains(err, ""), "invalid signing method bogus")
+ assert.Check(t, is.ErrorContains(err, "invalid signing method bogus"))
}
func TestSPCanProducePostLogoutRequest(t *testing.T) {
@@ -742,51 +795,6 @@ func TestSPRejectsInjectedComment(t *testing.T) {
}
}
-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.Certificate = "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.Certificate = "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"))
-}
-
func TestSPInvalidAssertions(t *testing.T) {
test := NewServiceProviderTest(t)
s := ServiceProvider{
@@ -1162,7 +1099,7 @@ func TestSPInvalidAssertions(t *testing.T) {
req := http.Request{PostForm: url.Values{}}
req.PostForm.Set("SAMLResponse", base64.StdEncoding.EncodeToString(test.SamlResponse))
- s.IDPMetadata.IDPSSODescriptors[0].KeyDescriptors[0].KeyInfo.Certificate = "invalid"
+ s.IDPMetadata.IDPSSODescriptors[0].KeyDescriptors[0].KeyInfo.X509Data.X509Certificates[0].Data = "invalid"
_, err = s.ParseResponse(&req, []string{"id-9e61753d64e928af5a7a341a97f420c9"})
assertionBuf := []byte(err.(*InvalidResponseError).Response)
@@ -1647,3 +1584,201 @@ func TestSPResponseWithNoIssuer(t *testing.T) {
_, err = s.ParseResponse(&req, []string{"id-9e61753d64e928af5a7a341a97f420c9"})
assert.Check(t, err)
}
+
+func TestGetArtifactBindingLocation(t *testing.T) {
+ test := NewServiceProviderTest(t)
+ test.IDPMetadata = golden.Get(t, "TestGetArtifactBindingLocation_IDPMetadata")
+
+ sp := ServiceProvider{
+ Key: test.Key,
+ Certificate: test.Certificate,
+ MetadataURL: mustParseURL("https://example.com/saml2/metadata"),
+ AcsURL: mustParseURL("https://example.com/saml2/acs"),
+ IDPMetadata: &EntityDescriptor{},
+ }
+
+ location := sp.GetArtifactBindingLocation(SOAPBinding)
+ assert.Check(t, is.Equal(location, ""))
+
+ err := xml.Unmarshal(test.IDPMetadata, &sp.IDPMetadata)
+ assert.Check(t, err)
+
+ location = sp.GetArtifactBindingLocation(SOAPBinding)
+ assert.Check(t, is.Equal(location, "https://samltest.id/idp/profile/SAML2/SOAP/ArtifactResolution"))
+}
+
+func TestMakeArtifactResolveRequest(t *testing.T) {
+ test := NewServiceProviderTest(t)
+
+ sp := ServiceProvider{
+ Key: test.Key,
+ Certificate: test.Certificate,
+ MetadataURL: mustParseURL("https://example.com/saml2/metadata"),
+ AcsURL: mustParseURL("https://example.com/saml2/acs"),
+ IDPMetadata: &EntityDescriptor{},
+ }
+
+ req, err := sp.MakeArtifactResolveRequest("artifactId")
+ assert.Check(t, err)
+
+ x, err := xml.Marshal(req)
+ assert.Check(t, err)
+ golden.Assert(t, string(x), t.Name())
+}
+
+func TestMakeSignedArtifactResolveRequest(t *testing.T) {
+ test := NewServiceProviderTest(t)
+
+ sp := ServiceProvider{
+ Key: test.Key,
+ Certificate: test.Certificate,
+ MetadataURL: mustParseURL("https://example.com/saml2/metadata"),
+ AcsURL: mustParseURL("https://example.com/saml2/acs"),
+ IDPMetadata: &EntityDescriptor{},
+ SignatureMethod: dsig.RSASHA1SignatureMethod,
+ }
+
+ req, err := sp.MakeArtifactResolveRequest("artifactId")
+ assert.Check(t, err)
+
+ x, err := xml.Marshal(req)
+ assert.Check(t, err)
+ golden.Assert(t, string(x), t.Name())
+}
+
+func TestMakeSignedArtifactResolveRequestWithBogusSignatureMethod(t *testing.T) {
+ test := NewServiceProviderTest(t)
+
+ sp := ServiceProvider{
+ Key: test.Key,
+ Certificate: test.Certificate,
+ MetadataURL: mustParseURL("https://example.com/saml2/metadata"),
+ AcsURL: mustParseURL("https://example.com/saml2/acs"),
+ IDPMetadata: &EntityDescriptor{},
+ SignatureMethod: "bogus",
+ }
+
+ _, err := sp.MakeArtifactResolveRequest("artifactId")
+ assert.Check(t, is.ErrorContains(err, "invalid signing method bogus"))
+
+}
+
+func TestParseXMLArtifactResponse(t *testing.T) {
+ test := NewServiceProviderTest(t)
+ TimeNow = func() time.Time {
+ rv, _ := time.Parse(timeFormat, "2021-08-17T10:26:57Z")
+ return rv
+ }
+ Clock = dsig.NewFakeClockAt(TimeNow())
+
+ // an actual response from samltest.id
+ samlResponse := golden.Get(t, "TestParseXMLArtifactResponse_response")
+ test.IDPMetadata = golden.Get(t, "TestGetArtifactBindingLocation_IDPMetadata")
+
+ sp := ServiceProvider{
+ Key: test.Key,
+ Certificate: test.Certificate,
+ MetadataURL: mustParseURL("http://localhost:8000/saml/metadata"),
+ AcsURL: mustParseURL("http://localhost:8000/saml/acs"),
+ IDPMetadata: &EntityDescriptor{},
+ }
+
+ err := xml.Unmarshal(test.IDPMetadata, &sp.IDPMetadata)
+ assert.Check(t, err)
+
+ possibleReqIDs := []string{"id-f3c7bc7d626a4ededa6028b718e5252c6e770b94"}
+ reqID := "id-218eb155248f7db7c85fe4e2709a3f17a70d09c7"
+
+ assertion, err := sp.ParseXMLArtifactResponse(samlResponse, possibleReqIDs, reqID)
+ assert.Check(t, err)
+
+ x, err := xml.Marshal(assertion)
+ assert.Check(t, err)
+
+ golden.Assert(t, string(x), t.Name()+"_assertion")
+}
+
+func TestParseBadXMLArtifactResponse(t *testing.T) {
+ test := NewServiceProviderTest(t)
+ TimeNow = func() time.Time {
+ rv, _ := time.Parse(timeFormat, "2021-08-17T10:26:57Z")
+ return rv
+ }
+ Clock = dsig.NewFakeClockAt(TimeNow())
+
+ // an actual response from samltest.id
+ samlResponse := golden.Get(t, "TestParseXMLArtifactResponse_response")
+ test.IDPMetadata = golden.Get(t, "TestGetArtifactBindingLocation_IDPMetadata")
+
+ possibleReqIDs := []string{"id-f3c7bc7d626a4ededa6028b718e5252c6e770b94"}
+ reqID := "id-218eb155248f7db7c85fe4e2709a3f17a70d09c7"
+
+ sp := ServiceProvider{
+ Key: test.Key,
+ Certificate: test.Certificate,
+ MetadataURL: mustParseURL("http://localhost:8000/saml/metadata"),
+ AcsURL: mustParseURL("https://example.com/saml2/acs"),
+ IDPMetadata: &EntityDescriptor{},
+ }
+
+ assertion, err := sp.ParseXMLArtifactResponse(samlResponse, possibleReqIDs, reqID)
+ assert.Check(t, is.Error(err.(*InvalidResponseError).PrivateErr,
+ "response Issuer does not match the IDP metadata (expected \"\")"))
+ assert.Check(t, is.Nil(assertion))
+
+ err = xml.Unmarshal(test.IDPMetadata, &sp.IDPMetadata)
+ assert.Check(t, err)
+
+ assertion, err = sp.ParseXMLArtifactResponse(samlResponse, possibleReqIDs, reqID)
+ assert.Check(t, is.Error(err.(*InvalidResponseError).PrivateErr,
+ "`Destination` does not match AcsURL (expected \"https://example.com/saml2/acs\", actual \"http://localhost:8000/saml/acs\")"))
+ assert.Check(t, is.Nil(assertion))
+
+ sp.AcsURL = mustParseURL("http://localhost:8000/saml/acs")
+
+ // TimeNow is used to verify the response time
+ TimeNow = func() time.Time {
+ rv, _ := time.Parse(timeFormat, "2022-08-17T10:26:57Z")
+ return rv
+ }
+
+ assertion, err = sp.ParseXMLArtifactResponse(samlResponse, possibleReqIDs, reqID)
+ assert.Check(t, is.Error(err.(*InvalidResponseError).PrivateErr,
+ "response IssueInstant expired at 2021-08-17 10:28:50.146 +0000 UTC"))
+ assert.Check(t, is.Nil(assertion))
+
+ // Clock is used to verify the certificate
+ Clock = dsig.NewFakeClockAt(func() time.Time {
+ rv, _ := time.Parse(timeFormat, "2039-08-17T10:26:57Z")
+ return rv
+ }())
+ TimeNow = func() time.Time {
+ rv, _ := time.Parse(timeFormat, "2021-08-17T10:26:57Z")
+ return rv
+ }
+
+ assertion, err = sp.ParseXMLArtifactResponse(samlResponse, possibleReqIDs, reqID)
+ assert.Check(t, is.Error(err.(*InvalidResponseError).PrivateErr,
+ "cannot validate signature on Response: Cert is not valid at this time"))
+ assert.Check(t, is.Nil(assertion))
+ Clock = dsig.NewFakeClockAt(TimeNow())
+
+ wrongReqID := "id-218eb155248f7db7c85fe4e2709a3f17a70d09c8"
+ assertion, err = sp.ParseXMLArtifactResponse(samlResponse, possibleReqIDs, wrongReqID)
+ assert.Check(t, is.Error(err.(*InvalidResponseError).PrivateErr,
+ "`InResponseTo` does not match the artifact request ID (expected id-218eb155248f7db7c85fe4e2709a3f17a70d09c8)"))
+ assert.Check(t, is.Nil(assertion))
+
+ wrongPossibleReqIDs := []string{"id-f3c7bc7d626a4ededa6028b718e5252c6e770b95"}
+ assertion, err = sp.ParseXMLArtifactResponse(samlResponse, wrongPossibleReqIDs, reqID)
+ assert.Check(t, is.Error(err.(*InvalidResponseError).PrivateErr,
+ "`InResponseTo` does not match any of the possible request IDs (expected [id-f3c7bc7d626a4ededa6028b718e5252c6e770b95])"))
+ assert.Check(t, is.Nil(assertion))
+
+ // random other key
+ sp.Key = mustParsePrivateKey(golden.Get(t, "key_2017.pem")).(*rsa.PrivateKey)
+ assertion, err = sp.ParseXMLArtifactResponse(samlResponse, possibleReqIDs, reqID)
+ assert.Check(t, is.Error(err.(*InvalidResponseError).PrivateErr,
+ "failed to decrypt response: certificate does not match provided key"))
+ assert.Check(t, is.Nil(assertion))
+}
diff --git a/testdata/SP_IDPMetadata b/testdata/SP_IDPMetadata
index a79b4f19..4f9b9362 100644
--- a/testdata/SP_IDPMetadata
+++ b/testdata/SP_IDPMetadata
@@ -1,5 +1,5 @@
-
+
@@ -10,7 +10,7 @@
-
+
testshib.org
diff --git a/testdata/TestCanProduceMetadataEntityID_metadata b/testdata/TestCanProduceMetadataEntityID_metadata
index 79686c48..90d837d1 100644
--- a/testdata/TestCanProduceMetadataEntityID_metadata
+++ b/testdata/TestCanProduceMetadataEntityID_metadata
@@ -2,5 +2,6 @@
+
-
\ No newline at end of file
+
diff --git a/testdata/TestCanProduceMetadataNoCerts_metadata b/testdata/TestCanProduceMetadataNoCerts_metadata
index 3e802f77..5b294d08 100644
--- a/testdata/TestCanProduceMetadataNoCerts_metadata
+++ b/testdata/TestCanProduceMetadataNoCerts_metadata
@@ -2,5 +2,6 @@
+
-
\ No newline at end of file
+
diff --git a/testdata/TestCanProduceSPMetadata_expected b/testdata/TestCanProduceSPMetadata_expected
index 9250ba1a..628d094b 100644
--- a/testdata/TestCanProduceSPMetadata_expected
+++ b/testdata/TestCanProduceSPMetadata_expected
@@ -2,19 +2,19 @@
-
- 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
-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
-
\ No newline at end of file
+
diff --git a/testdata/TestGetArtifactBindingLocation_IDPMetadata b/testdata/TestGetArtifactBindingLocation_IDPMetadata
new file mode 100644
index 00000000..f0f8b54c
--- /dev/null
+++ b/testdata/TestGetArtifactBindingLocation_IDPMetadata
@@ -0,0 +1,122 @@
+
+
+
+
+
+
+
+
+ samltest.id
+
+
+
+ SAMLtest IdP
+ A free and basic IdP for testing SAML deployments
+ https://samltest.id/saml/logo.png
+
+
+
+
+
+
+
+MIIDETCCAfmgAwIBAgIUZRpDhkNKl5eWtJqk0Bu1BgTTargwDQYJKoZIhvcNAQEL
+BQAwFjEUMBIGA1UEAwwLc2FtbHRlc3QuaWQwHhcNMTgwODI0MjExNDEwWhcNMzgw
+ODI0MjExNDEwWjAWMRQwEgYDVQQDDAtzYW1sdGVzdC5pZDCCASIwDQYJKoZIhvcN
+AQEBBQADggEPADCCAQoCggEBAJrh9/PcDsiv3UeL8Iv9rf4WfLPxuOm9W6aCntEA
+8l6c1LQ1Zyrz+Xa/40ZgP29ENf3oKKbPCzDcc6zooHMji2fBmgXp6Li3fQUzu7yd
++nIC2teejijVtrNLjn1WUTwmqjLtuzrKC/ePoZyIRjpoUxyEMJopAd4dJmAcCq/K
+k2eYX9GYRlqvIjLFoGNgy2R4dWwAKwljyh6pdnPUgyO/WjRDrqUBRFrLQJorR2kD
+c4seZUbmpZZfp4MjmWMDgyGM1ZnR0XvNLtYeWAyt0KkSvFoOMjZUeVK/4xR74F8e
+8ToPqLmZEg9ZUx+4z2KjVK00LpdRkH9Uxhh03RQ0FabHW6UCAwEAAaNXMFUwHQYD
+VR0OBBYEFJDbe6uSmYQScxpVJhmt7PsCG4IeMDQGA1UdEQQtMCuCC3NhbWx0ZXN0
+LmlkhhxodHRwczovL3NhbWx0ZXN0LmlkL3NhbWwvaWRwMA0GCSqGSIb3DQEBCwUA
+A4IBAQBNcF3zkw/g51q26uxgyuy4gQwnSr01Mhvix3Dj/Gak4tc4XwvxUdLQq+jC
+cxr2Pie96klWhY/v/JiHDU2FJo9/VWxmc/YOk83whvNd7mWaNMUsX3xGv6AlZtCO
+L3JhCpHjiN+kBcMgS5jrtGgV1Lz3/1zpGxykdvS0B4sPnFOcaCwHe2B9SOCWbDAN
+JXpTjz1DmJO4ImyWPJpN1xsYKtm67Pefxmn0ax0uE2uuzq25h0xbTkqIQgJzyoE/
+DPkBFK1vDkMfAW11dQ0BXatEnW7Gtkc0lh2/PIbHWj4AzxYMyBf5Gy6HSVOftwjC
+voQR2qr2xJBixsg+MIORKtmKHLfU
+
+
+
+
+
+
+
+
+
+MIIDEjCCAfqgAwIBAgIVAMECQ1tjghafm5OxWDh9hwZfxthWMA0GCSqGSIb3DQEB
+CwUAMBYxFDASBgNVBAMMC3NhbWx0ZXN0LmlkMB4XDTE4MDgyNDIxMTQwOVoXDTM4
+MDgyNDIxMTQwOVowFjEUMBIGA1UEAwwLc2FtbHRlc3QuaWQwggEiMA0GCSqGSIb3
+DQEBAQUAA4IBDwAwggEKAoIBAQC0Z4QX1NFKs71ufbQwoQoW7qkNAJRIANGA4iM0
+ThYghul3pC+FwrGv37aTxWXfA1UG9njKbbDreiDAZKngCgyjxj0uJ4lArgkr4AOE
+jj5zXA81uGHARfUBctvQcsZpBIxDOvUUImAl+3NqLgMGF2fktxMG7kX3GEVNc1kl
+bN3dfYsaw5dUrw25DheL9np7G/+28GwHPvLb4aptOiONbCaVvh9UMHEA9F7c0zfF
+/cL5fOpdVa54wTI0u12CsFKt78h6lEGG5jUs/qX9clZncJM7EFkN3imPPy+0HC8n
+spXiH/MZW8o2cqWRkrw3MzBZW3Ojk5nQj40V6NUbjb7kfejzAgMBAAGjVzBVMB0G
+A1UdDgQWBBQT6Y9J3Tw/hOGc8PNV7JEE4k2ZNTA0BgNVHREELTArggtzYW1sdGVz
+dC5pZIYcaHR0cHM6Ly9zYW1sdGVzdC5pZC9zYW1sL2lkcDANBgkqhkiG9w0BAQsF
+AAOCAQEASk3guKfTkVhEaIVvxEPNR2w3vWt3fwmwJCccW98XXLWgNbu3YaMb2RSn
+7Th4p3h+mfyk2don6au7Uyzc1Jd39RNv80TG5iQoxfCgphy1FYmmdaSfO8wvDtHT
+TNiLArAxOYtzfYbzb5QrNNH/gQEN8RJaEf/g/1GTw9x/103dSMK0RXtl+fRs2nbl
+D1JJKSQ3AdhxK/weP3aUPtLxVVJ9wMOQOfcy02l+hHMb6uAjsPOpOVKqi3M8XmcU
+ZOpx4swtgGdeoSpeRyrtMvRwdcciNBp9UZome44qZAYH1iqrpmmjsfI9pJItsgWu
+3kXPjhSfj1AJGR1l9JGvJrHki1iHTA==
+
+
+
+
+
+
+
+
+
+MIIDEjCCAfqgAwIBAgIVAPVbodo8Su7/BaHXUHykx0Pi5CFaMA0GCSqGSIb3DQEB
+CwUAMBYxFDASBgNVBAMMC3NhbWx0ZXN0LmlkMB4XDTE4MDgyNDIxMTQwOVoXDTM4
+MDgyNDIxMTQwOVowFjEUMBIGA1UEAwwLc2FtbHRlc3QuaWQwggEiMA0GCSqGSIb3
+DQEBAQUAA4IBDwAwggEKAoIBAQCQb+1a7uDdTTBBFfwOUun3IQ9nEuKM98SmJDWa
+MwM877elswKUTIBVh5gB2RIXAPZt7J/KGqypmgw9UNXFnoslpeZbA9fcAqqu28Z4
+sSb2YSajV1ZgEYPUKvXwQEmLWN6aDhkn8HnEZNrmeXihTFdyr7wjsLj0JpQ+VUlc
+4/J+hNuU7rGYZ1rKY8AA34qDVd4DiJ+DXW2PESfOu8lJSOteEaNtbmnvH8KlwkDs
+1NvPTsI0W/m4SK0UdXo6LLaV8saIpJfnkVC/FwpBolBrRC/Em64UlBsRZm2T89ca
+uzDee2yPUvbBd5kLErw+sC7i4xXa2rGmsQLYcBPhsRwnmBmlAgMBAAGjVzBVMB0G
+A1UdDgQWBBRZ3exEu6rCwRe5C7f5QrPcAKRPUjA0BgNVHREELTArggtzYW1sdGVz
+dC5pZIYcaHR0cHM6Ly9zYW1sdGVzdC5pZC9zYW1sL2lkcDANBgkqhkiG9w0BAQsF
+AAOCAQEABZDFRNtcbvIRmblnZItoWCFhVUlq81ceSQddLYs8DqK340//hWNAbYdj
+WcP85HhIZnrw6NGCO4bUipxZXhiqTA/A9d1BUll0vYB8qckYDEdPDduYCOYemKkD
+dmnHMQWs9Y6zWiYuNKEJ9mf3+1N8knN/PK0TYVjVjXAf2CnOETDbLtlj6Nqb8La3
+sQkYmU+aUdopbjd5JFFwbZRaj6KiHXHtnIRgu8sUXNPrgipUgZUOVhP0C0N5OfE4
+JW8ZBrKgQC/6vJ2rSa9TlzI6JAa5Ww7gMXMP9M+cJUNQklcq+SBnTK8G+uBHgPKR
+zBDsMIEzRtQZm4GIoHJae4zmnCekkQ==
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/testdata/TestMakeArtifactResolveRequest b/testdata/TestMakeArtifactResolveRequest
new file mode 100644
index 00000000..fcc54fcd
--- /dev/null
+++ b/testdata/TestMakeArtifactResolveRequest
@@ -0,0 +1 @@
+https://example.com/saml2/metadataartifactId
\ No newline at end of file
diff --git a/testdata/TestMakeSignedArtifactResolveRequest b/testdata/TestMakeSignedArtifactResolveRequest
new file mode 100644
index 00000000..8c1b614e
--- /dev/null
+++ b/testdata/TestMakeSignedArtifactResolveRequest
@@ -0,0 +1 @@
+https://example.com/saml2/metadatadsSignaturexmlnsdshttp://www.w3.org/2000/09/xmldsig#dsSignedInfodsCanonicalizationMethodAlgorithmhttp://www.w3.org/2001/10/xml-exc-c14n#dsSignatureMethodAlgorithmhttp://www.w3.org/2000/09/xmldsig#rsa-sha1dsReferenceURI#id-00020406080a0c0e10121416181a1c1e20222426dsTransformsdsTransformAlgorithmhttp://www.w3.org/2000/09/xmldsig#enveloped-signaturedsTransformAlgorithmhttp://www.w3.org/2001/10/xml-exc-c14n#dsDigestMethodAlgorithmhttp://www.w3.org/2000/09/xmldsig#sha1dsDigestValueOZX5MUcmjNTL4/ULK2e2UgRiTxw=dsSignatureValuewWSt0RdNbeUfNXo6dWRO9Jdt4qyy2NXVxntlvyOi8mcgm8mHPqPC86cHCggx/DM/WKlTGOP3bvYgJYSj14GSrfAB3WIVsECOHI00juy5kRnMpFWRcsqMGij+gVX5a6WhAVoJWZox5N1avqJbw0T5bi+2rMG8pzHwfNtfSAQ8OkI=dsKeyInfodsX509DatadsX509CertificateMIIB7zCCAVgCCQDFzbKIp7b3MTANBgkqhkiG9w0BAQUFADA8MQswCQYDVQQGEwJVUzELMAkGA1UECAwCR0ExDDAKBgNVBAoMA2ZvbzESMBAGA1UEAwwJbG9jYWxob3N0MB4XDTEzMTAwMjAwMDg1MVoXDTE0MTAwMjAwMDg1MVowPDELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkdBMQwwCgYDVQQKDANmb28xEjAQBgNVBAMMCWxvY2FsaG9zdDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA1PMHYmhZj308kWLhZVT4vOulqx/9ibm5B86fPWwUKKQ2i12MYtz07tzukPymisTDhQaqyJ8Kqb/6JjhmeMnEOdTvSPmHO8m1ZVveJU6NoKRn/mP/BD7FW52WhbrUXLSeHVSKfWkNk6S4hk9MV9TswTvyRIKvRsw0X/gfnqkroJcCAwEAATANBgkqhkiG9w0BAQUFAAOBgQCMMlIO+GNcGekevKgkakpMdAqJfs24maGb90DvTLbRZRD7Xvn1MnVBBS9hzlXiFLYOInXACMW5gcoRFfeTQLSouMM8o57h0uKjfTmuoWHLQLi6hnF+cvCsEFiJZ4AbF+DgmO6TarJ8O05t8zvnOwJlNCASPZRH/JmF8tX0hoHuAQ==artifactId
\ No newline at end of file
diff --git a/testdata/TestParseXMLArtifactResponse_assertion b/testdata/TestParseXMLArtifactResponse_assertion
new file mode 100644
index 00000000..f7b0ac21
--- /dev/null
+++ b/testdata/TestParseXMLArtifactResponse_assertion
@@ -0,0 +1 @@
+https://samltest.id/saml/idpAAdzZWNyZXQxmdGfdGiCl5GfFqdXr4fFg22uNwB1bPW0DpwXVsA8ZTM9Mm4WZbdwL2HGSb16cikyIjqUeddVshrsVM6DMD/iVagheXMVMIp8Y1JOQsPr6eLL4K7B9u5BIoE6FduV3W60g77uBwM2http://localhost:8000/saml/metadataurn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransporturn:mace:dir:entitlement:common-lib-termsrickrsanchez@samltest.idmanager@samltest.id+1-555-555-5515rsanchez@samltest.idSanchezRick SanchezRick
\ No newline at end of file
diff --git a/testdata/TestParseXMLArtifactResponse_response b/testdata/TestParseXMLArtifactResponse_response
new file mode 100644
index 00000000..71f01dca
--- /dev/null
+++ b/testdata/TestParseXMLArtifactResponse_response
@@ -0,0 +1,36 @@
+
+https://samltest.id/saml/idplFfEmKnbMD3hy2rIOa9KSlQWiRbNd5mn8MelKB2TPgo=jl6a7xNieATxehNBKJpKIWvjbp4LjYd2i/9nCk5pKKKyvdUchaz74nILrg6en0iR3HKZl0sGaXmIEzkmpFNRqiam5I0lX1OMzi8HQU99UcbyDtoArLM65uHIExrd5n/W0hnWXcqAjeucNdtWVygx3ptXl36ivh9i2QXM/xi/x4QwBcNZJcbguGYy/0pSmzVcDrmvYvLq2/Sg3M9pDXh1LR5hW5ui/heJFcnDbS2O6Iu0lTbFpVDoeRZz2PTURQqjP2WnsQOfxS0822H2ldFKdXVQdQ10MIGTpq6ckhMFahwbAYophLwLyZCsrl8SWjgovYk+LvumrG/TtQlqz51Iwg==MIIDEjCCAfqgAwIBAgIVAMECQ1tjghafm5OxWDh9hwZfxthWMA0GCSqGSIb3DQEBCwUAMBYxFDAS
+BgNVBAMMC3NhbWx0ZXN0LmlkMB4XDTE4MDgyNDIxMTQwOVoXDTM4MDgyNDIxMTQwOVowFjEUMBIG
+A1UEAwwLc2FtbHRlc3QuaWQwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC0Z4QX1NFK
+s71ufbQwoQoW7qkNAJRIANGA4iM0ThYghul3pC+FwrGv37aTxWXfA1UG9njKbbDreiDAZKngCgyj
+xj0uJ4lArgkr4AOEjj5zXA81uGHARfUBctvQcsZpBIxDOvUUImAl+3NqLgMGF2fktxMG7kX3GEVN
+c1klbN3dfYsaw5dUrw25DheL9np7G/+28GwHPvLb4aptOiONbCaVvh9UMHEA9F7c0zfF/cL5fOpd
+Va54wTI0u12CsFKt78h6lEGG5jUs/qX9clZncJM7EFkN3imPPy+0HC8nspXiH/MZW8o2cqWRkrw3
+MzBZW3Ojk5nQj40V6NUbjb7kfejzAgMBAAGjVzBVMB0GA1UdDgQWBBQT6Y9J3Tw/hOGc8PNV7JEE
+4k2ZNTA0BgNVHREELTArggtzYW1sdGVzdC5pZIYcaHR0cHM6Ly9zYW1sdGVzdC5pZC9zYW1sL2lk
+cDANBgkqhkiG9w0BAQsFAAOCAQEASk3guKfTkVhEaIVvxEPNR2w3vWt3fwmwJCccW98XXLWgNbu3
+YaMb2RSn7Th4p3h+mfyk2don6au7Uyzc1Jd39RNv80TG5iQoxfCgphy1FYmmdaSfO8wvDtHTTNiL
+ArAxOYtzfYbzb5QrNNH/gQEN8RJaEf/g/1GTw9x/103dSMK0RXtl+fRs2nblD1JJKSQ3AdhxK/we
+P3aUPtLxVVJ9wMOQOfcy02l+hHMb6uAjsPOpOVKqi3M8XmcUZOpx4swtgGdeoSpeRyrtMvRwdcci
+NBp9UZome44qZAYH1iqrpmmjsfI9pJItsgWu3kXPjhSfj1AJGR1l9JGvJrHki1iHTA==https://samltest.id/saml/idpKYwjbB62U5e5h6/C2RRnzQeGbWmUzeyAtJJOwgvEjD8=o+lyCiKpMZ+Yr4s4DVHVNi4iLumaLooQ8DUX2gFvNRdVeeFb5KqLdm3Fr3O74fApzJSNssLgrvyt3AOx+YRXRRUdjK+Y8l9s+lTA6v9Xk/DCvyFg1gZx6mdFz6IPcoFO8m7C0xs09mVnrGlZPTr7NfWgiPiMuNaTbrbuUkMwf1xLEqhNOjkI6sQL0e6HoAvnpNw9uThBTQLEgzb9+ikao4vvTg9XkNexo6+dCd3RH1Gg3Gwf79Vi2b7jtzLjftqMeYLqh3TpQEAu/hyiatO9MYG2hDaEiBrzNVrDJp2sKyVq9+Z+y5x0RjSZcjm6pj9mOKLc+H8Q2evg4naOL0cnRQ==MIIDEjCCAfqgAwIBAgIVAMECQ1tjghafm5OxWDh9hwZfxthWMA0GCSqGSIb3DQEBCwUAMBYxFDAS
+BgNVBAMMC3NhbWx0ZXN0LmlkMB4XDTE4MDgyNDIxMTQwOVoXDTM4MDgyNDIxMTQwOVowFjEUMBIG
+A1UEAwwLc2FtbHRlc3QuaWQwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC0Z4QX1NFK
+s71ufbQwoQoW7qkNAJRIANGA4iM0ThYghul3pC+FwrGv37aTxWXfA1UG9njKbbDreiDAZKngCgyj
+xj0uJ4lArgkr4AOEjj5zXA81uGHARfUBctvQcsZpBIxDOvUUImAl+3NqLgMGF2fktxMG7kX3GEVN
+c1klbN3dfYsaw5dUrw25DheL9np7G/+28GwHPvLb4aptOiONbCaVvh9UMHEA9F7c0zfF/cL5fOpd
+Va54wTI0u12CsFKt78h6lEGG5jUs/qX9clZncJM7EFkN3imPPy+0HC8nspXiH/MZW8o2cqWRkrw3
+MzBZW3Ojk5nQj40V6NUbjb7kfejzAgMBAAGjVzBVMB0GA1UdDgQWBBQT6Y9J3Tw/hOGc8PNV7JEE
+4k2ZNTA0BgNVHREELTArggtzYW1sdGVzdC5pZIYcaHR0cHM6Ly9zYW1sdGVzdC5pZC9zYW1sL2lk
+cDANBgkqhkiG9w0BAQsFAAOCAQEASk3guKfTkVhEaIVvxEPNR2w3vWt3fwmwJCccW98XXLWgNbu3
+YaMb2RSn7Th4p3h+mfyk2don6au7Uyzc1Jd39RNv80TG5iQoxfCgphy1FYmmdaSfO8wvDtHTTNiL
+ArAxOYtzfYbzb5QrNNH/gQEN8RJaEf/g/1GTw9x/103dSMK0RXtl+fRs2nblD1JJKSQ3AdhxK/we
+P3aUPtLxVVJ9wMOQOfcy02l+hHMb6uAjsPOpOVKqi3M8XmcUZOpx4swtgGdeoSpeRyrtMvRwdcci
+NBp9UZome44qZAYH1iqrpmmjsfI9pJItsgWu3kXPjhSfj1AJGR1l9JGvJrHki1iHTA==MIIB7zCCAVgCCQDFzbKIp7b3MTANBgkqhkiG9w0BAQUFADA8MQswCQYDVQQGEwJVUzELMAkGA1UE
+CAwCR0ExDDAKBgNVBAoMA2ZvbzESMBAGA1UEAwwJbG9jYWxob3N0MB4XDTEzMTAwMjAwMDg1MVoX
+DTE0MTAwMjAwMDg1MVowPDELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkdBMQwwCgYDVQQKDANmb28x
+EjAQBgNVBAMMCWxvY2FsaG9zdDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA1PMHYmhZj308
+kWLhZVT4vOulqx/9ibm5B86fPWwUKKQ2i12MYtz07tzukPymisTDhQaqyJ8Kqb/6JjhmeMnEOdTv
+SPmHO8m1ZVveJU6NoKRn/mP/BD7FW52WhbrUXLSeHVSKfWkNk6S4hk9MV9TswTvyRIKvRsw0X/gf
+nqkroJcCAwEAATANBgkqhkiG9w0BAQUFAAOBgQCMMlIO+GNcGekevKgkakpMdAqJfs24maGb90Dv
+TLbRZRD7Xvn1MnVBBS9hzlXiFLYOInXACMW5gcoRFfeTQLSouMM8o57h0uKjfTmuoWHLQLi6hnF+
+cvCsEFiJZ4AbF+DgmO6TarJ8O05t8zvnOwJlNCASPZRH/JmF8tX0hoHuAQ==VdyZ46hXopHanbCvmLQc0w+eU0WZAoX1/QXeE5TsXIvTqGs2b5mxRKtKasbnKuxG6X8Ph8FEExUGCCkJrwAfXoLCrquza195G27hXtQRoCi+B6mVNqVnZYXVOc/BEO/OAEoy9ig7GqGqzTh4iLEaELaIZTLmpe72eOfVnMa91bc=fmaEdPEGisiSvEyCtQG6YCkeJ1RPYH26n6yRZmS/7quukIvCfTnG2wFigHpwjAffukNHxlBZp7T5xEJKT6ivzpDOjZEIxzO66k5UcsqwDZynC94vLuFyDl08Dk/4abNP28CrSi8x1OuYjeM6fpy+cWD5GLwGQO6/1kOjJBJRdg3q4YW7cCJKzctsjhjxmN8tvavZqJ9BsbziPNBqD3DlXTypc0Lste2ZU3TVs99teR8R08PgEVXoOkshtKgzA+dJ8BSr4QrsTHVRX1fE3Ktl75wrXAAhptrsHksICEHgkTmMkRLm69zIZE+UP78CZb21w2UXqTkfwG4iPIGcUYTx1VBaITzgs4O0yN99QIXkqxRfZg0/PKE12VfGbSUS+X8KllGxa+w2YqQefdfzZefriimlzPKCwjPqCxts7ImsaVRYw8gShen3jKwLM8xLztT9tRZWlAsGJTKtG3qsuvcMFBPyUp3/7MxO87nmTk/G1rtb0uJLZv2Azvj2B9Aexg9AZXGnoCfPbdMnfqOznVHo9hWgSLfKTeUDlgRcM0yOrk0skVtpq0cy/7DCwzGt1ZENJev6Rpg5PB54+/0r9Wv5RJWWuCXwdW5+ypgPTAPcwp1DeS7HTDmUZgENqGk2jSz44k2PqCOrhpJ3o8pGAPy2xPphu6op66sCBHwdgT9WdTkBOKo/2It0ljwFNocAxNeUsHidtX6EmiaN2DJFjrayq/BIL8jF75fa1e4jDm03VlF3VLmPuz4LZ9ukIQtRShKeH3ZwnxIJdQLn8Li1P9shcBJ8xXU87efKyczXzUa7MS5kkrfCpxeD184GfnKKRMbUHujqbjSdbyEKkwNz+U0bPHESw0A7c03iQBjCnTZVPkdZXzqfmqiDYqou64b+No7+uJOvwPpQgBuTGnEAnlcdtGMRzd1gxHAw02HZuu7sY04r0lcH9VspSRS9lP3um5/DpS29CaMJn1MjuR/r1aJMhvjadN5iMcKxaz8BM+6TJ9UIWOvVDWWnmL8G46KAUzA/w0jLk6IpvaQB3J2us24JMDU+XNBn3S+7t5Px0oOekgQ381/sUJwqhaa+0IEQNSyPQODO8Ew7P9cA4Mea8V1NSfOMwtUk1gtatO252DT/Y6nPQGP4p3OHmMvFnlmi3MnQcdlBAL51PP2MLgOjE96WCxfDr5oqivvocDbAUACj+SZX6uy0bMmY8DwGxYZ4+H45jgaG3HR0pdX/N5XK/HL0aiLO6WLVmeefIO5+eW+es5DS9hAwVxd9Olpor50MLIbgFXmVJUqbdNj4mRP8qLcqOeq+lKKUoVWuj1KG87MZrby91o8j5dpZ2YD1KfOFrl77QcJDXM/FkClovypvKQBuMYvo5gPxDfPR0GaZ85uj+iIoqUpyao+HCa4lNOfStACYXM31lHQ/eA3hBbnFxrfJJBTYJ7VP+K/jM7Wd4xd9h7FYCF579KhbZPh1o2/xslYHGwjfHbXrGfQrp6SzjqXitpK1j+zOZqaj0jcaZcc2E5+zd66OQ27NMdQcdlNPUU9W7L0f507DvN6moxtGAmreqafSt3m/1bU5qiqG5mTzw88TI4dWW7GnptDNdcq8Hj8pdACHBcFhrNiXf4LBspIyMyTjkQg7lyPG/Dbh8O2+U94TL1sMKSY06vGfbHyqpec8s1S9zsiYs2asnhnh6eUB9HFR38781ERlxWfxJ5LsRR8ZGU3qpX9CvJYZYx3ko9a7+uubiyMGlIOmaMVN5ZdY4w2buLNd4zdSDtutDnwEDvBfLvfESghPU6eVfzp3AI0mBU9LvgkDHczaiJpLjLCSvzYUvu5Eu0/a6q3fm4njjEP2K2FCJix6cieI6xRDla3SaxkuptVoCxhnawvX/lpimkQc+6h8j2Fb3xVFj5b2FRGa1ITYcOAKSA9ja7Po3S0etykizXwfhP2f3LSpCLaXptzaAr5nAmHP2hPtQqer63FcXq9LlDpy1ecN+aCvnULtlRFGTkwikSSWZ+obIXArp1HfnClUOMYYBRNlZRXLe9osQ+hmbaHwsPkZEXpkFGH6dwOIW0bB+Zro5xIlGVz6x8v6Uc6I5GugHBCZYD54CI1UHTPkVhEMk91kOni11K/3t5v+bKBl3ZlRSHU9XXfmYO8/ViunTQ1jUZ/CDuvS3IwBQgegJT/Z7BIvl7sJgGRjnNVgCvPE22wC/EQSYQt06hcUOKg7RatLNfPKTum6DGh+ifMUtIgjohZxMEI7hKFbghtY6lNy0qCJ9sdZBQSN0EQvCEE11aRCWxvfxh6K8pnRuULIwZ8vAoz/qBoJ6fManqed9MLGVnepDbtlbYPh2IFlzkxRdfYiobtj+NqS11/scYTP8dNBD3dWeKXXgZf0Haoomeyj0mhBfHdT3usmeWeszZLZviZXNq+Za6KFISDhY4owIyvBpilIUAwI+xGQyYuhjAaoE+020M9a0/YuR6lEP0LYNvc4nnlHbd5qeIbzqmdJFMXG9MiBRxqmY7F3BFCgIzcQo3qCMASh9Se0mvbmilg4rxJdgsOq07Z4ssiBYCJySNemKLkYcn8HBDPEYySGclDpDN2QtKXNhYYOhIypo/yPxwc9VjfgIRboAC8uatX/BCzp4ws0ZWgvLvu3BOOMXHbwsQi+Lubo8tCXEqYu3zK33S0vjvsu8fcQ94JcbY9AZHrUywAqQ5Gx3incGghwVn42Bq9RwDl7JXvW2e1Z2T+hEdatbzq1QFNMFF181uArYa3G8RkNJSvaFyQ+kFnjlMkXhiapzlPgmSUgUz3q5gfD5UNxQ3iJGPehAruBdPONXRNRsLvq/kcFikp7iAViYqrT5Th7e0Ws9tE5IvMvUfcE+8OiiUH+61NgC5PHHrZ5E5wPaPjrYEnrb7+dBoApq3mqKOjZdgQ4lboIO7JvknBibKFLGd655leWCOXyRQsnBn+s17EYQrR4aiMXYwIqBrwktiH1Mg/nryVHuELhaV9DXN/hfMWU8uo8NVzA7vBk7mrlvZPbcsPBYnBnNOBeAEMRTH+8nKzM08/yvOv2DwUVFSbwlICaEC2PNoBSXK4BSI6ZS44FqAlbt0DY4D3eyWes9siCoWtJKVJ8X5TKc+ffDk2q73tlltK/ZfpxQpwDShanxmBLTdySM2MkE2w467piVbamNUPS862e6NMDfhwcqOVKVw4KwhHPqOxUQFg9zeCVDQ22WaGm6N+PzMgU5c1LGqBZQ0kTSiFVTTGogpdSxJKcOaNPWYCFp8KoZCVcM+nFpbZD1OOq2DmyB5KsSIpQe3tJMq+1zXLcStVi51Xe7ci9WgOvLerfycN7/9sRamisb8/ng9HpPuMnARASW/f+LK8xFiFK4tylNgH7eP7F/1g8AVnzxr9kpmF/6HkBR/xBLlv+T3bRnPGHl2YIHWRvPLIe3QWGa3ZQgX1MWmkHLCuuR8YnSP1PqJAlkV7o/KT3SyDxlCkb2v2nhq8ztMm/w+kMD9GU9LYMETlWh0OM5raNcLfZs73nmYZ3JOUD8x5cqefkmv9qSRdkXoPArTiwozc/xyueViyJI8EDgAihtGcjye+G8FUvV42UyJQR9Blh3/dtbz6G4T/6L+S0mN0Np09Njw7KWgw492dR3lqZwS0exxN8w0GSsJ6YtoifFziiu3UbdEcbkhbiq9/fnPFSYxVHRd+fcxsUhNKYmVmHsKRs0rYbvbqh0LZMnSu5kAfa8jmf8DB+tnsSYoazk4uiod5cyzMXOduqi09mTu4lyyxX22/RAIYVaTiceQKgXi/Jesf+K3+t2kXZpqHc1vRcVoPmz/BReygO28xNQa0eDYwhUJZE5nFa7yDu2PIcM3Xbj7cHJRGAGN4+nlEAFhU1f3UPDzySliJEE1ePx6S1Ly6hgRlgie1zFngDzpkF1N4Q6zFVLdwbgLVfPzMwYb9bCWoPpfSV5ziUo1AyDHe2W0f/JBETe0DGLrMpEGQT3W+4tMmgv6ayB2n6ceSTsjNxguO9I063Wn8JV9bIPLYg1sz1kxaX8gyYCFxLvDiIPgH9M0u7ZN0eUg1zT2rKrE/YQ9kn/X/Tw5UOHCdcvq6oje33J+niPxjl6b27u3+jnN/uBsQHD4IlgUO+qihuPaX1f4KSn//N3MAig80OT79uSq4LS5RIOjREqNQXSvkSG6RtktHiqC6NFd8JwwZFBW7+jaAdpsalq6A6eyaOs4aAgqkXJQ+FvtA//qEZjzC88ZR/MN31gHoiSUan6HHJoFuzNbcdJUOsFf9TSs7gJOtjx7nuEV3fVnIMIfFCdegYKyMkhQJ08XzJsoU0y6Ov/NdxWLXZZIca3arh1bEBCexcE51SCt3W6ZMwrVKXwXfvBlRp0Oz+HcJULhiVaR3x3OBqZ2kU0QFKnsqEpolnF4rA7qMAgdap9FsEVz4/3PR9Fu3R7rxTetpRVQsjxf4HE3g6qTP2uXafMKYrxqsQlbyzB/Hqi6+Y/UTvuY79CMjIBsTFb/NqbIcM+XRmYczPMj/41k41drZm3jb1VPgUylmUcsdty0fL2U5d0Nbw2hdu+tlsosIzr6ZpfYZpnrPe1eK0zVVX/1X6UeAn1VhIwSD6fQhO0KlLwq4DzUMiuKoOm61Cz9yPlqjQ5uNNgRgKjOQWuCocYjtYWv5SwyoD+LVhMd8Zn3eCLIbp/aX4sQBP+tS01FsueD/vKTV36u4E1JAc5B6B33J5yiLiH9ZPzMcY8JfzZuJyZNmFFC3jVWzSH0YnFQNwmLlqCmE65i9hdPaKk7sgdJub8PGdvkun154TLcUwZAFQdLbK7Qv2lBO8ynGDMpvMvFZARSisTgZbAkzrEwOmu7t3RCp6d/LbJiWSRjJJ/yYZ4rDlIw+i3kitQV8mfWpls5fpqPbDtsME+FqXEdHnAyHLtRWiPq/lpAqNrzfvxQoeFphkmmCKJM7a1B/oy4DEGIDZFtZreviY1i9pFStMea2SBSOJrMPQnH3fe4/KDnk30wornL0YSkmnHP5RZbJUhgnSc3uXcKnGEt4/rF++9+U85jme/iLRz0/TUrr/F+GK6ECX3PzRHYK30wiWG852BWrPu9Xkhtl1XcNBBfdqCGR6GXxpTD//6YhKzXj14Tlne1vOnD21J5fJmnNhta2AYc7qEY2fGUKCH0z9OH2G02p57nWMIKdpuzNNrjdaUZWZGFMsIRBz5mMYlr0hmZr5Y0gDNJmvEZGn38PunT0zzSqkqPcsLCniwZnV8CGSqo/IahCdGfFwhNMt5aT9FHsLJ/O3dN8IFWznUNV5xonbZmk2uPCSZrB6l4Eme9dYbioSDP3XBr0ntxIASoUrmASjHawxAvYboi0+HAQzycLd12zKoyAXvyrEzb68mXOXTAQy6wjCFoVO6oDTVvCOGGqIPsF3Ynt4E8tKg/AX/VHID9p+llnYsM9uFtUVfRn9nkmMy793X0goxrVJcIikU3+kqI8FHT+9u/spzWbjvtdIqOD46ZsvEOXmQpsh6B84k1soDmLJdEfrHKi2Hjj7fFX31jS1/z+KyTxxxaviL0M8AQ86I7UmT455PRyZCPzMCFtVVDu60eMbz8svsBX3ydPfUX2iNnBD0mZW8wdvwuSepabt9l4IonZjL5byljLjux1/MqclOlzgZLQhJ1NqLzTyS8hrasQ9fBVkW6Qxxfw3lEKnFaVkyt4/T44yNZ/smU2v1qQQa2GF7iuz52Zp90cPJtDflRZzBYeDd3x47a5S0YjhtToaUsw78KpZKQbDanOwD++OOx3D2tjMTofaR+jFsypl0T94rh746qo3hBFJGK+/bjw7u28g2TLcBm9bWG7g81v9xurjTabbH80vOrFPzWLvLftJTfT23SXE/8yBWKhH+zTDyHmZBgYdacN8Zm8m6fjT7NiT+lYa/V0/07gmuHsNWLah2yFluR110W/rOGMCpGdtKOpRgtrmkyPnR+dpOlI6tcAEFSf6JaKzdlH4njpjDFaeJs/VCKNxHXumpOz8t5LfsuxrSOUUX4lvO/kM6hvQGikHdl7k8Chji5uLjg5G/b3gVhfbL6LT4hblgPpj6YaiuDak1cwhAaGFZth9Xu+Dk6t+zsIZEOj1YUQwwA96lcU3nGVyrkRAFnm/G3Wosp2H8+lNmwYJJvf3ZKlAE8Mee/czgxgMbZ72IfDy0SrCLuEl9scwFGMl9Tyg/kVTS2T6g6//dNg3DXZLv8cV7brVYSVQqji/NVxiQc/CQBBN2+SriH4bAyZ6YnWwMhoOTc4cYpf0ghZrxOAPrcMTIhm1IGVRPubahvn/6Z2nz+S8WuqdZ1Ge43EilrsqrCKH9mduxASZT+M5yPwiWDptc4W0d/kM2dc4pZWSei7Wcjx3Yq6cPaJNqgbnOVZCjBb0+YoegxL4Lb67NYzuH2s4ZMP5SEtqOMtlXcwStLXywuRzI6aGEGLk1J+zfgTVlavNtlUKSDmkAc3sSZYL1ZMsi1fjkZ5jvsHLwr7X+T8ESKfXKy34oIMmDCXOiNmKgmeOPHsDHa3kJbYsoYsyNwrji5Q9DiwkCCRFSoBIWR16eRhCNXEBnqkuUUnTuBhd733XF/15RwFzhRdsJ3DweIy96m25ZhGkryD98VHDp458an8MDR8eQC8LwBxf7LPXDPeJ30TjP8TEI5IZUbkijZyzU+E+CVYlKFR4lbia1ndoHPj5PnYeOP4y+87yeuHMhH92OUL2CZ5fH4e6stw69vzuA1RttC+V1ifvpKE0iiEkVrnhbAxDOQZs1inUZPRW8raTriUW0gfBJ3lH21YLuSk91JxcFntXkg1EWNKinzkUEgb1P4YqwDww3SK76Gqz8XlN0R1i3fJBJbicQV6K/mMEvGN3PDKzLe1cp8wUAqRGyJrutBhVuoDZCoChkoIB3x5eJONZ/ZngdTVmwSQ6k9gnCj7u18v3fJwDFlD7pFt7pEvit6q3gbhF298FV3zv4rSYXm9Gl8Sip90Rh/UVd02c/KBil7EGmZE0er0VqraRT7N8TZye7GMwwNkuFhvE199dUGwq8soO+DzC1h2t/0RnLI0gIkO+X2Qov5gWFyG0XBSWBQaUH27b4+DNzT6Tpz2yJhBYu+jUq+lEOfHDT7mRncuoDIg6v3F51/akqMFL4zufF+MvUWGkPKIGNSThyJ+YakMWUkIXOwuS9fpA8CKiBqpCoz9xlYuXonjEDEO/CmHzMl5SDP29dvvWl2yzJKozBTo1IYGZfKe7Crhby5kFhq72raQ3Kz30Q0732MZP9rFXVlLtTERBPMzN9lCCNPqLC9zWnzQAU3C2eZYQDApLUeUlagbUYe8FuSu/yAT5Ncz9gL1TH2pljW6hgWOQD1RgHi2yn+8Lx3LCgtuy+QMoyKHULAQdYdzYdd6CBq1q1jX9I+zqqQjPVcDZQwFt5Yo8u41G9tFXqvYoKyCEld7TfwrnZWRto/3eqjsIjkVUUcRP7jCk6wG4i3IlahBMrXBEiq/JUXHvaaDl9hsciDsYphmQCkbyK2Zk3sNWSbLYnXHgmZyv5TmNhsVXX9dohWG+3qOc+gTUgBaZcY9u8OMXDh7ax11wEAoEPv/GA0n1eyacEv2PhEXxxo9bbeJz/P08UQRUsHZvbY7RpvXvmaIfy2yulxgh66Zd+52yIOS36vUp97Z2WUjRExQGNAcyaaHrKLZywspeUAueTxVXZbNUQXhgNZ0UK2D7FlPqJVI+TYzaiBkqNj2vw2oIMJIrVZhkrjV4S1FNPdi6b0Ou6en62meC5xWSwae+r+unL970isdfJFcaoElCTCuFajxdjixozdmby6xHzLikf203SB1Zw0Uk3uUdE4UBkzzKugtRMwq3K7lbn8IThh+v7tksRwxrlnff2dbnjRfMWcUQFNU9m/nySq0I46aJVjy6q0XMkDUSHJOszMcT69ccb0eqPfZjAVM2kC7p1u7SyTccdTIXiehYbRznhy17O1wIa0WzbWUOS114twMaYdRFmrOFgOkc1mCn+CwmvN+/wprX3WT2jizyJacFhKTKVTRX/oSf7pxXhor6ZIJMC6FFpbHTlOMlbcuAK5JpDaftEv7K9TnHP3ksWaQc/UkXm/zm53p/r6nPN+wILU6b+cbv69ZvSThhUzjC40IiPJnkQgujvYz8HbKtUBNYPHFsR0S1iNDUB1hWysGfkTLjCTgtZEjjEXO701oIFrK1iUWtYQKyyHj5LTDKW2RsKVJuWO+5YS3GlTBbLazsS7Qiv3SA05k/OSr8N9M8GTxyLSbv2CV49izseJCnBuf1wXvA+Qjtwl9etT8toMuHoE/ksNkqqO+nzIk//NzIdJjjiVx1zBhCCDcegxQxcj0=
\ No newline at end of file
diff --git a/testdata/TestSPCanProduceMetadataWithBothCerts_metadata b/testdata/TestSPCanProduceMetadataWithBothCerts_metadata
index 428cc13b..2c5fc76f 100644
--- a/testdata/TestSPCanProduceMetadataWithBothCerts_metadata
+++ b/testdata/TestSPCanProduceMetadataWithBothCerts_metadata
@@ -2,8 +2,8 @@
-
- MIIB7zCCAVgCCQDFzbKIp7b3MTANBgkqhkiG9w0BAQUFADA8MQswCQYDVQQGEwJVUzELMAkGA1UECAwCR0ExDDAKBgNVBAoMA2ZvbzESMBAGA1UEAwwJbG9jYWxob3N0MB4XDTEzMTAwMjAwMDg1MVoXDTE0MTAwMjAwMDg1MVowPDELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkdBMQwwCgYDVQQKDANmb28xEjAQBgNVBAMMCWxvY2FsaG9zdDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA1PMHYmhZj308kWLhZVT4vOulqx/9ibm5B86fPWwUKKQ2i12MYtz07tzukPymisTDhQaqyJ8Kqb/6JjhmeMnEOdTvSPmHO8m1ZVveJU6NoKRn/mP/BD7FW52WhbrUXLSeHVSKfWkNk6S4hk9MV9TswTvyRIKvRsw0X/gfnqkroJcCAwEAATANBgkqhkiG9w0BAQUFAAOBgQCMMlIO+GNcGekevKgkakpMdAqJfs24maGb90DvTLbRZRD7Xvn1MnVBBS9hzlXiFLYOInXACMW5gcoRFfeTQLSouMM8o57h0uKjfTmuoWHLQLi6hnF+cvCsEFiJZ4AbF+DgmO6TarJ8O05t8zvnOwJlNCASPZRH/JmF8tX0hoHuAQ==
+
+ MIIB7zCCAVgCCQDFzbKIp7b3MTANBgkqhkiG9w0BAQUFADA8MQswCQYDVQQGEwJVUzELMAkGA1UECAwCR0ExDDAKBgNVBAoMA2ZvbzESMBAGA1UEAwwJbG9jYWxob3N0MB4XDTEzMTAwMjAwMDg1MVoXDTE0MTAwMjAwMDg1MVowPDELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkdBMQwwCgYDVQQKDANmb28xEjAQBgNVBAMMCWxvY2FsaG9zdDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA1PMHYmhZj308kWLhZVT4vOulqx/9ibm5B86fPWwUKKQ2i12MYtz07tzukPymisTDhQaqyJ8Kqb/6JjhmeMnEOdTvSPmHO8m1ZVveJU6NoKRn/mP/BD7FW52WhbrUXLSeHVSKfWkNk6S4hk9MV9TswTvyRIKvRsw0X/gfnqkroJcCAwEAATANBgkqhkiG9w0BAQUFAAOBgQCMMlIO+GNcGekevKgkakpMdAqJfs24maGb90DvTLbRZRD7Xvn1MnVBBS9hzlXiFLYOInXACMW5gcoRFfeTQLSouMM8o57h0uKjfTmuoWHLQLi6hnF+cvCsEFiJZ4AbF+DgmO6TarJ8O05t8zvnOwJlNCASPZRH/JmF8tX0hoHuAQ==
@@ -13,12 +13,13 @@
-
- MIIB7zCCAVgCCQDFzbKIp7b3MTANBgkqhkiG9w0BAQUFADA8MQswCQYDVQQGEwJVUzELMAkGA1UECAwCR0ExDDAKBgNVBAoMA2ZvbzESMBAGA1UEAwwJbG9jYWxob3N0MB4XDTEzMTAwMjAwMDg1MVoXDTE0MTAwMjAwMDg1MVowPDELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkdBMQwwCgYDVQQKDANmb28xEjAQBgNVBAMMCWxvY2FsaG9zdDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA1PMHYmhZj308kWLhZVT4vOulqx/9ibm5B86fPWwUKKQ2i12MYtz07tzukPymisTDhQaqyJ8Kqb/6JjhmeMnEOdTvSPmHO8m1ZVveJU6NoKRn/mP/BD7FW52WhbrUXLSeHVSKfWkNk6S4hk9MV9TswTvyRIKvRsw0X/gfnqkroJcCAwEAATANBgkqhkiG9w0BAQUFAAOBgQCMMlIO+GNcGekevKgkakpMdAqJfs24maGb90DvTLbRZRD7Xvn1MnVBBS9hzlXiFLYOInXACMW5gcoRFfeTQLSouMM8o57h0uKjfTmuoWHLQLi6hnF+cvCsEFiJZ4AbF+DgmO6TarJ8O05t8zvnOwJlNCASPZRH/JmF8tX0hoHuAQ==
+
+ MIIB7zCCAVgCCQDFzbKIp7b3MTANBgkqhkiG9w0BAQUFADA8MQswCQYDVQQGEwJVUzELMAkGA1UECAwCR0ExDDAKBgNVBAoMA2ZvbzESMBAGA1UEAwwJbG9jYWxob3N0MB4XDTEzMTAwMjAwMDg1MVoXDTE0MTAwMjAwMDg1MVowPDELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkdBMQwwCgYDVQQKDANmb28xEjAQBgNVBAMMCWxvY2FsaG9zdDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA1PMHYmhZj308kWLhZVT4vOulqx/9ibm5B86fPWwUKKQ2i12MYtz07tzukPymisTDhQaqyJ8Kqb/6JjhmeMnEOdTvSPmHO8m1ZVveJU6NoKRn/mP/BD7FW52WhbrUXLSeHVSKfWkNk6S4hk9MV9TswTvyRIKvRsw0X/gfnqkroJcCAwEAATANBgkqhkiG9w0BAQUFAAOBgQCMMlIO+GNcGekevKgkakpMdAqJfs24maGb90DvTLbRZRD7Xvn1MnVBBS9hzlXiFLYOInXACMW5gcoRFfeTQLSouMM8o57h0uKjfTmuoWHLQLi6hnF+cvCsEFiJZ4AbF+DgmO6TarJ8O05t8zvnOwJlNCASPZRH/JmF8tX0hoHuAQ==
+
-
\ No newline at end of file
+
diff --git a/testdata/TestSPCanProduceMetadataWithEncryptionCert_metadata b/testdata/TestSPCanProduceMetadataWithEncryptionCert_metadata
index 02d7aeea..e188d2ca 100644
--- a/testdata/TestSPCanProduceMetadataWithEncryptionCert_metadata
+++ b/testdata/TestSPCanProduceMetadataWithEncryptionCert_metadata
@@ -2,8 +2,8 @@
-
- MIIB7zCCAVgCCQDFzbKIp7b3MTANBgkqhkiG9w0BAQUFADA8MQswCQYDVQQGEwJVUzELMAkGA1UECAwCR0ExDDAKBgNVBAoMA2ZvbzESMBAGA1UEAwwJbG9jYWxob3N0MB4XDTEzMTAwMjAwMDg1MVoXDTE0MTAwMjAwMDg1MVowPDELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkdBMQwwCgYDVQQKDANmb28xEjAQBgNVBAMMCWxvY2FsaG9zdDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA1PMHYmhZj308kWLhZVT4vOulqx/9ibm5B86fPWwUKKQ2i12MYtz07tzukPymisTDhQaqyJ8Kqb/6JjhmeMnEOdTvSPmHO8m1ZVveJU6NoKRn/mP/BD7FW52WhbrUXLSeHVSKfWkNk6S4hk9MV9TswTvyRIKvRsw0X/gfnqkroJcCAwEAATANBgkqhkiG9w0BAQUFAAOBgQCMMlIO+GNcGekevKgkakpMdAqJfs24maGb90DvTLbRZRD7Xvn1MnVBBS9hzlXiFLYOInXACMW5gcoRFfeTQLSouMM8o57h0uKjfTmuoWHLQLi6hnF+cvCsEFiJZ4AbF+DgmO6TarJ8O05t8zvnOwJlNCASPZRH/JmF8tX0hoHuAQ==
+
+ MIIB7zCCAVgCCQDFzbKIp7b3MTANBgkqhkiG9w0BAQUFADA8MQswCQYDVQQGEwJVUzELMAkGA1UECAwCR0ExDDAKBgNVBAoMA2ZvbzESMBAGA1UEAwwJbG9jYWxob3N0MB4XDTEzMTAwMjAwMDg1MVoXDTE0MTAwMjAwMDg1MVowPDELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkdBMQwwCgYDVQQKDANmb28xEjAQBgNVBAMMCWxvY2FsaG9zdDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA1PMHYmhZj308kWLhZVT4vOulqx/9ibm5B86fPWwUKKQ2i12MYtz07tzukPymisTDhQaqyJ8Kqb/6JjhmeMnEOdTvSPmHO8m1ZVveJU6NoKRn/mP/BD7FW52WhbrUXLSeHVSKfWkNk6S4hk9MV9TswTvyRIKvRsw0X/gfnqkroJcCAwEAATANBgkqhkiG9w0BAQUFAAOBgQCMMlIO+GNcGekevKgkakpMdAqJfs24maGb90DvTLbRZRD7Xvn1MnVBBS9hzlXiFLYOInXACMW5gcoRFfeTQLSouMM8o57h0uKjfTmuoWHLQLi6hnF+cvCsEFiJZ4AbF+DgmO6TarJ8O05t8zvnOwJlNCASPZRH/JmF8tX0hoHuAQ==
@@ -13,5 +13,6 @@
+
-
\ No newline at end of file
+
diff --git a/vendor/github.com/dchest/uniuri/.travis.yml b/vendor/github.com/dchest/uniuri/.travis.yml
deleted file mode 100644
index 245a2f51..00000000
--- a/vendor/github.com/dchest/uniuri/.travis.yml
+++ /dev/null
@@ -1,6 +0,0 @@
-language: go
-
-go:
- - 1.3
- - 1.4
- - tip
diff --git a/vendor/github.com/dchest/uniuri/README.md b/vendor/github.com/dchest/uniuri/README.md
deleted file mode 100644
index b321a5fa..00000000
--- a/vendor/github.com/dchest/uniuri/README.md
+++ /dev/null
@@ -1,97 +0,0 @@
-Package uniuri
-=====================
-
-[](https://travis-ci.org/dchest/uniuri)
-
-```go
-import "github.com/dchest/uniuri"
-```
-
-Package uniuri generates random strings good for use in URIs to identify
-unique objects.
-
-Example usage:
-
-```go
-s := uniuri.New() // s is now "apHCJBl7L1OmC57n"
-```
-
-A standard string created by New() is 16 bytes in length and consists of
-Latin upper and lowercase letters, and numbers (from the set of 62 allowed
-characters), which means that it has ~95 bits of entropy. To get more
-entropy, you can use NewLen(UUIDLen), which returns 20-byte string, giving
-~119 bits of entropy, or any other desired length.
-
-Functions read from crypto/rand random source, and panic if they fail to
-read from it.
-
-
-Constants
----------
-
-```go
-const (
- // StdLen is a standard length of uniuri string to achive ~95 bits of entropy.
- StdLen = 16
- // UUIDLen is a length of uniuri string to achive ~119 bits of entropy, closest
- // to what can be losslessly converted to UUIDv4 (122 bits).
- UUIDLen = 20
-)
-
-```
-
-
-
-Variables
----------
-
-```go
-var StdChars = []byte("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789")
-```
-
-
-StdChars is a set of standard characters allowed in uniuri string.
-
-
-Functions
----------
-
-### func New
-
-```go
-func New() string
-```
-
-New returns a new random string of the standard length, consisting of
-standard characters.
-
-### func NewLen
-
-```go
-func NewLen(length int) string
-```
-
-NewLen returns a new random string of the provided length, consisting of
-standard characters.
-
-### func NewLenChars
-
-```go
-func NewLenChars(length int, chars []byte) string
-```
-
-NewLenChars returns a new random string of the provided length, consisting
-of the provided byte slice of allowed characters (maximum 256).
-
-
-
-Public domain dedication
-------------------------
-
-Written in 2011-2014 by Dmitry Chestnykh
-
-The author(s) have dedicated all copyright and related and
-neighboring rights to this software to the public domain
-worldwide. Distributed without any warranty.
-http://creativecommons.org/publicdomain/zero/1.0/
-
diff --git a/vendor/github.com/dchest/uniuri/uniuri.go b/vendor/github.com/dchest/uniuri/uniuri.go
deleted file mode 100644
index 6393446c..00000000
--- a/vendor/github.com/dchest/uniuri/uniuri.go
+++ /dev/null
@@ -1,81 +0,0 @@
-// Written in 2011-2014 by Dmitry Chestnykh
-//
-// The author(s) have dedicated all copyright and related and
-// neighboring rights to this software to the public domain
-// worldwide. Distributed without any warranty.
-// http://creativecommons.org/publicdomain/zero/1.0/
-
-// Package uniuri generates random strings good for use in URIs to identify
-// unique objects.
-//
-// Example usage:
-//
-// s := uniuri.New() // s is now "apHCJBl7L1OmC57n"
-//
-// A standard string created by New() is 16 bytes in length and consists of
-// Latin upper and lowercase letters, and numbers (from the set of 62 allowed
-// characters), which means that it has ~95 bits of entropy. To get more
-// entropy, you can use NewLen(UUIDLen), which returns 20-byte string, giving
-// ~119 bits of entropy, or any other desired length.
-//
-// Functions read from crypto/rand random source, and panic if they fail to
-// read from it.
-package uniuri
-
-import "crypto/rand"
-
-const (
- // StdLen is a standard length of uniuri string to achive ~95 bits of entropy.
- StdLen = 16
- // UUIDLen is a length of uniuri string to achive ~119 bits of entropy, closest
- // to what can be losslessly converted to UUIDv4 (122 bits).
- UUIDLen = 20
-)
-
-// StdChars is a set of standard characters allowed in uniuri string.
-var StdChars = []byte("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789")
-
-// New returns a new random string of the standard length, consisting of
-// standard characters.
-func New() string {
- return NewLenChars(StdLen, StdChars)
-}
-
-// NewLen returns a new random string of the provided length, consisting of
-// standard characters.
-func NewLen(length int) string {
- return NewLenChars(length, StdChars)
-}
-
-// NewLenChars returns a new random string of the provided length, consisting
-// of the provided byte slice of allowed characters (maximum 256).
-func NewLenChars(length int, chars []byte) string {
- if length == 0 {
- return ""
- }
- clen := len(chars)
- if clen < 2 || clen > 256 {
- panic("uniuri: wrong charset length for NewLenChars")
- }
- maxrb := 255 - (256 % clen)
- b := make([]byte, length)
- r := make([]byte, length+(length/4)) // storage for random bytes.
- i := 0
- for {
- if _, err := rand.Read(r); err != nil {
- panic("uniuri: error reading random bytes: " + err.Error())
- }
- for _, rb := range r {
- c := int(rb)
- if c > maxrb {
- // Skip this number to avoid modulo bias.
- continue
- }
- b[i] = chars[c%clen]
- i++
- if i == length {
- return string(b)
- }
- }
- }
-}
diff --git a/vendor/github.com/kr/pretty/.gitignore b/vendor/github.com/kr/pretty/.gitignore
deleted file mode 100644
index 1f0a99f2..00000000
--- a/vendor/github.com/kr/pretty/.gitignore
+++ /dev/null
@@ -1,4 +0,0 @@
-[568].out
-_go*
-_test*
-_obj
diff --git a/vendor/github.com/kr/pretty/License b/vendor/github.com/kr/pretty/License
deleted file mode 100644
index 480a3280..00000000
--- a/vendor/github.com/kr/pretty/License
+++ /dev/null
@@ -1,19 +0,0 @@
-Copyright 2012 Keith Rarick
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in
-all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-THE SOFTWARE.
diff --git a/vendor/github.com/kr/pretty/Readme b/vendor/github.com/kr/pretty/Readme
deleted file mode 100644
index c589fc62..00000000
--- a/vendor/github.com/kr/pretty/Readme
+++ /dev/null
@@ -1,9 +0,0 @@
-package pretty
-
- import "github.com/kr/pretty"
-
- Package pretty provides pretty-printing for Go values.
-
-Documentation
-
- http://godoc.org/github.com/kr/pretty
diff --git a/vendor/github.com/kr/pretty/diff.go b/vendor/github.com/kr/pretty/diff.go
deleted file mode 100644
index 6aa7f743..00000000
--- a/vendor/github.com/kr/pretty/diff.go
+++ /dev/null
@@ -1,265 +0,0 @@
-package pretty
-
-import (
- "fmt"
- "io"
- "reflect"
-)
-
-type sbuf []string
-
-func (p *sbuf) Printf(format string, a ...interface{}) {
- s := fmt.Sprintf(format, a...)
- *p = append(*p, s)
-}
-
-// Diff returns a slice where each element describes
-// a difference between a and b.
-func Diff(a, b interface{}) (desc []string) {
- Pdiff((*sbuf)(&desc), a, b)
- return desc
-}
-
-// wprintfer calls Fprintf on w for each Printf call
-// with a trailing newline.
-type wprintfer struct{ w io.Writer }
-
-func (p *wprintfer) Printf(format string, a ...interface{}) {
- fmt.Fprintf(p.w, format+"\n", a...)
-}
-
-// Fdiff writes to w a description of the differences between a and b.
-func Fdiff(w io.Writer, a, b interface{}) {
- Pdiff(&wprintfer{w}, a, b)
-}
-
-type Printfer interface {
- Printf(format string, a ...interface{})
-}
-
-// Pdiff prints to p a description of the differences between a and b.
-// It calls Printf once for each difference, with no trailing newline.
-// The standard library log.Logger is a Printfer.
-func Pdiff(p Printfer, a, b interface{}) {
- diffPrinter{w: p}.diff(reflect.ValueOf(a), reflect.ValueOf(b))
-}
-
-type Logfer interface {
- Logf(format string, a ...interface{})
-}
-
-// logprintfer calls Fprintf on w for each Printf call
-// with a trailing newline.
-type logprintfer struct{ l Logfer }
-
-func (p *logprintfer) Printf(format string, a ...interface{}) {
- p.l.Logf(format, a...)
-}
-
-// Ldiff prints to l a description of the differences between a and b.
-// It calls Logf once for each difference, with no trailing newline.
-// The standard library testing.T and testing.B are Logfers.
-func Ldiff(l Logfer, a, b interface{}) {
- Pdiff(&logprintfer{l}, a, b)
-}
-
-type diffPrinter struct {
- w Printfer
- l string // label
-}
-
-func (w diffPrinter) printf(f string, a ...interface{}) {
- var l string
- if w.l != "" {
- l = w.l + ": "
- }
- w.w.Printf(l+f, a...)
-}
-
-func (w diffPrinter) diff(av, bv reflect.Value) {
- if !av.IsValid() && bv.IsValid() {
- w.printf("nil != %# v", formatter{v: bv, quote: true})
- return
- }
- if av.IsValid() && !bv.IsValid() {
- w.printf("%# v != nil", formatter{v: av, quote: true})
- return
- }
- if !av.IsValid() && !bv.IsValid() {
- return
- }
-
- at := av.Type()
- bt := bv.Type()
- if at != bt {
- w.printf("%v != %v", at, bt)
- return
- }
-
- switch kind := at.Kind(); kind {
- case reflect.Bool:
- if a, b := av.Bool(), bv.Bool(); a != b {
- w.printf("%v != %v", a, b)
- }
- case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
- if a, b := av.Int(), bv.Int(); a != b {
- w.printf("%d != %d", a, b)
- }
- case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
- if a, b := av.Uint(), bv.Uint(); a != b {
- w.printf("%d != %d", a, b)
- }
- case reflect.Float32, reflect.Float64:
- if a, b := av.Float(), bv.Float(); a != b {
- w.printf("%v != %v", a, b)
- }
- case reflect.Complex64, reflect.Complex128:
- if a, b := av.Complex(), bv.Complex(); a != b {
- w.printf("%v != %v", a, b)
- }
- case reflect.Array:
- n := av.Len()
- for i := 0; i < n; i++ {
- w.relabel(fmt.Sprintf("[%d]", i)).diff(av.Index(i), bv.Index(i))
- }
- case reflect.Chan, reflect.Func, reflect.UnsafePointer:
- if a, b := av.Pointer(), bv.Pointer(); a != b {
- w.printf("%#x != %#x", a, b)
- }
- case reflect.Interface:
- w.diff(av.Elem(), bv.Elem())
- case reflect.Map:
- ak, both, bk := keyDiff(av.MapKeys(), bv.MapKeys())
- for _, k := range ak {
- w := w.relabel(fmt.Sprintf("[%#v]", k))
- w.printf("%q != (missing)", av.MapIndex(k))
- }
- for _, k := range both {
- w := w.relabel(fmt.Sprintf("[%#v]", k))
- w.diff(av.MapIndex(k), bv.MapIndex(k))
- }
- for _, k := range bk {
- w := w.relabel(fmt.Sprintf("[%#v]", k))
- w.printf("(missing) != %q", bv.MapIndex(k))
- }
- case reflect.Ptr:
- switch {
- case av.IsNil() && !bv.IsNil():
- w.printf("nil != %# v", formatter{v: bv, quote: true})
- case !av.IsNil() && bv.IsNil():
- w.printf("%# v != nil", formatter{v: av, quote: true})
- case !av.IsNil() && !bv.IsNil():
- w.diff(av.Elem(), bv.Elem())
- }
- case reflect.Slice:
- lenA := av.Len()
- lenB := bv.Len()
- if lenA != lenB {
- w.printf("%s[%d] != %s[%d]", av.Type(), lenA, bv.Type(), lenB)
- break
- }
- for i := 0; i < lenA; i++ {
- w.relabel(fmt.Sprintf("[%d]", i)).diff(av.Index(i), bv.Index(i))
- }
- case reflect.String:
- if a, b := av.String(), bv.String(); a != b {
- w.printf("%q != %q", a, b)
- }
- case reflect.Struct:
- for i := 0; i < av.NumField(); i++ {
- w.relabel(at.Field(i).Name).diff(av.Field(i), bv.Field(i))
- }
- default:
- panic("unknown reflect Kind: " + kind.String())
- }
-}
-
-func (d diffPrinter) relabel(name string) (d1 diffPrinter) {
- d1 = d
- if d.l != "" && name[0] != '[' {
- d1.l += "."
- }
- d1.l += name
- return d1
-}
-
-// keyEqual compares a and b for equality.
-// Both a and b must be valid map keys.
-func keyEqual(av, bv reflect.Value) bool {
- if !av.IsValid() && !bv.IsValid() {
- return true
- }
- if !av.IsValid() || !bv.IsValid() || av.Type() != bv.Type() {
- return false
- }
- switch kind := av.Kind(); kind {
- case reflect.Bool:
- a, b := av.Bool(), bv.Bool()
- return a == b
- case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
- a, b := av.Int(), bv.Int()
- return a == b
- case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
- a, b := av.Uint(), bv.Uint()
- return a == b
- case reflect.Float32, reflect.Float64:
- a, b := av.Float(), bv.Float()
- return a == b
- case reflect.Complex64, reflect.Complex128:
- a, b := av.Complex(), bv.Complex()
- return a == b
- case reflect.Array:
- for i := 0; i < av.Len(); i++ {
- if !keyEqual(av.Index(i), bv.Index(i)) {
- return false
- }
- }
- return true
- case reflect.Chan, reflect.UnsafePointer, reflect.Ptr:
- a, b := av.Pointer(), bv.Pointer()
- return a == b
- case reflect.Interface:
- return keyEqual(av.Elem(), bv.Elem())
- case reflect.String:
- a, b := av.String(), bv.String()
- return a == b
- case reflect.Struct:
- for i := 0; i < av.NumField(); i++ {
- if !keyEqual(av.Field(i), bv.Field(i)) {
- return false
- }
- }
- return true
- default:
- panic("invalid map key type " + av.Type().String())
- }
-}
-
-func keyDiff(a, b []reflect.Value) (ak, both, bk []reflect.Value) {
- for _, av := range a {
- inBoth := false
- for _, bv := range b {
- if keyEqual(av, bv) {
- inBoth = true
- both = append(both, av)
- break
- }
- }
- if !inBoth {
- ak = append(ak, av)
- }
- }
- for _, bv := range b {
- inBoth := false
- for _, av := range a {
- if keyEqual(av, bv) {
- inBoth = true
- break
- }
- }
- if !inBoth {
- bk = append(bk, bv)
- }
- }
- return
-}
diff --git a/vendor/github.com/kr/pretty/formatter.go b/vendor/github.com/kr/pretty/formatter.go
deleted file mode 100644
index bf4b598d..00000000
--- a/vendor/github.com/kr/pretty/formatter.go
+++ /dev/null
@@ -1,327 +0,0 @@
-package pretty
-
-import (
- "fmt"
- "io"
- "reflect"
- "strconv"
- "text/tabwriter"
-
- "github.com/kr/text"
-)
-
-type formatter struct {
- v reflect.Value
- force bool
- quote bool
-}
-
-// Formatter makes a wrapper, f, that will format x as go source with line
-// breaks and tabs. Object f responds to the "%v" formatting verb when both the
-// "#" and " " (space) flags are set, for example:
-//
-// fmt.Sprintf("%# v", Formatter(x))
-//
-// If one of these two flags is not set, or any other verb is used, f will
-// format x according to the usual rules of package fmt.
-// In particular, if x satisfies fmt.Formatter, then x.Format will be called.
-func Formatter(x interface{}) (f fmt.Formatter) {
- return formatter{v: reflect.ValueOf(x), quote: true}
-}
-
-func (fo formatter) String() string {
- return fmt.Sprint(fo.v.Interface()) // unwrap it
-}
-
-func (fo formatter) passThrough(f fmt.State, c rune) {
- s := "%"
- for i := 0; i < 128; i++ {
- if f.Flag(i) {
- s += string(rune(i))
- }
- }
- if w, ok := f.Width(); ok {
- s += fmt.Sprintf("%d", w)
- }
- if p, ok := f.Precision(); ok {
- s += fmt.Sprintf(".%d", p)
- }
- s += string(c)
- fmt.Fprintf(f, s, fo.v.Interface())
-}
-
-func (fo formatter) Format(f fmt.State, c rune) {
- if fo.force || c == 'v' && f.Flag('#') && f.Flag(' ') {
- w := tabwriter.NewWriter(f, 4, 4, 1, ' ', 0)
- p := &printer{tw: w, Writer: w, visited: make(map[visit]int)}
- p.printValue(fo.v, true, fo.quote)
- w.Flush()
- return
- }
- fo.passThrough(f, c)
-}
-
-type printer struct {
- io.Writer
- tw *tabwriter.Writer
- visited map[visit]int
- depth int
-}
-
-func (p *printer) indent() *printer {
- q := *p
- q.tw = tabwriter.NewWriter(p.Writer, 4, 4, 1, ' ', 0)
- q.Writer = text.NewIndentWriter(q.tw, []byte{'\t'})
- return &q
-}
-
-func (p *printer) printInline(v reflect.Value, x interface{}, showType bool) {
- if showType {
- io.WriteString(p, v.Type().String())
- fmt.Fprintf(p, "(%#v)", x)
- } else {
- fmt.Fprintf(p, "%#v", x)
- }
-}
-
-// printValue must keep track of already-printed pointer values to avoid
-// infinite recursion.
-type visit struct {
- v uintptr
- typ reflect.Type
-}
-
-func (p *printer) printValue(v reflect.Value, showType, quote bool) {
- if p.depth > 10 {
- io.WriteString(p, "!%v(DEPTH EXCEEDED)")
- return
- }
-
- switch v.Kind() {
- case reflect.Bool:
- p.printInline(v, v.Bool(), showType)
- case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
- p.printInline(v, v.Int(), showType)
- case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
- p.printInline(v, v.Uint(), showType)
- case reflect.Float32, reflect.Float64:
- p.printInline(v, v.Float(), showType)
- case reflect.Complex64, reflect.Complex128:
- fmt.Fprintf(p, "%#v", v.Complex())
- case reflect.String:
- p.fmtString(v.String(), quote)
- case reflect.Map:
- t := v.Type()
- if showType {
- io.WriteString(p, t.String())
- }
- writeByte(p, '{')
- if nonzero(v) {
- expand := !canInline(v.Type())
- pp := p
- if expand {
- writeByte(p, '\n')
- pp = p.indent()
- }
- keys := v.MapKeys()
- for i := 0; i < v.Len(); i++ {
- k := keys[i]
- mv := v.MapIndex(k)
- pp.printValue(k, false, true)
- writeByte(pp, ':')
- if expand {
- writeByte(pp, '\t')
- }
- showTypeInStruct := t.Elem().Kind() == reflect.Interface
- pp.printValue(mv, showTypeInStruct, true)
- if expand {
- io.WriteString(pp, ",\n")
- } else if i < v.Len()-1 {
- io.WriteString(pp, ", ")
- }
- }
- if expand {
- pp.tw.Flush()
- }
- }
- writeByte(p, '}')
- case reflect.Struct:
- t := v.Type()
- if v.CanAddr() {
- addr := v.UnsafeAddr()
- vis := visit{addr, t}
- if vd, ok := p.visited[vis]; ok && vd < p.depth {
- p.fmtString(t.String()+"{(CYCLIC REFERENCE)}", false)
- break // don't print v again
- }
- p.visited[vis] = p.depth
- }
-
- if showType {
- io.WriteString(p, t.String())
- }
- writeByte(p, '{')
- if nonzero(v) {
- expand := !canInline(v.Type())
- pp := p
- if expand {
- writeByte(p, '\n')
- pp = p.indent()
- }
- for i := 0; i < v.NumField(); i++ {
- showTypeInStruct := true
- if f := t.Field(i); f.Name != "" {
- io.WriteString(pp, f.Name)
- writeByte(pp, ':')
- if expand {
- writeByte(pp, '\t')
- }
- showTypeInStruct = labelType(f.Type)
- }
- pp.printValue(getField(v, i), showTypeInStruct, true)
- if expand {
- io.WriteString(pp, ",\n")
- } else if i < v.NumField()-1 {
- io.WriteString(pp, ", ")
- }
- }
- if expand {
- pp.tw.Flush()
- }
- }
- writeByte(p, '}')
- case reflect.Interface:
- switch e := v.Elem(); {
- case e.Kind() == reflect.Invalid:
- io.WriteString(p, "nil")
- case e.IsValid():
- pp := *p
- pp.depth++
- pp.printValue(e, showType, true)
- default:
- io.WriteString(p, v.Type().String())
- io.WriteString(p, "(nil)")
- }
- case reflect.Array, reflect.Slice:
- t := v.Type()
- if showType {
- io.WriteString(p, t.String())
- }
- if v.Kind() == reflect.Slice && v.IsNil() && showType {
- io.WriteString(p, "(nil)")
- break
- }
- if v.Kind() == reflect.Slice && v.IsNil() {
- io.WriteString(p, "nil")
- break
- }
- writeByte(p, '{')
- expand := !canInline(v.Type())
- pp := p
- if expand {
- writeByte(p, '\n')
- pp = p.indent()
- }
- for i := 0; i < v.Len(); i++ {
- showTypeInSlice := t.Elem().Kind() == reflect.Interface
- pp.printValue(v.Index(i), showTypeInSlice, true)
- if expand {
- io.WriteString(pp, ",\n")
- } else if i < v.Len()-1 {
- io.WriteString(pp, ", ")
- }
- }
- if expand {
- pp.tw.Flush()
- }
- writeByte(p, '}')
- case reflect.Ptr:
- e := v.Elem()
- if !e.IsValid() {
- writeByte(p, '(')
- io.WriteString(p, v.Type().String())
- io.WriteString(p, ")(nil)")
- } else {
- pp := *p
- pp.depth++
- writeByte(pp, '&')
- pp.printValue(e, true, true)
- }
- case reflect.Chan:
- x := v.Pointer()
- if showType {
- writeByte(p, '(')
- io.WriteString(p, v.Type().String())
- fmt.Fprintf(p, ")(%#v)", x)
- } else {
- fmt.Fprintf(p, "%#v", x)
- }
- case reflect.Func:
- io.WriteString(p, v.Type().String())
- io.WriteString(p, " {...}")
- case reflect.UnsafePointer:
- p.printInline(v, v.Pointer(), showType)
- case reflect.Invalid:
- io.WriteString(p, "nil")
- }
-}
-
-func canInline(t reflect.Type) bool {
- switch t.Kind() {
- case reflect.Map:
- return !canExpand(t.Elem())
- case reflect.Struct:
- for i := 0; i < t.NumField(); i++ {
- if canExpand(t.Field(i).Type) {
- return false
- }
- }
- return true
- case reflect.Interface:
- return false
- case reflect.Array, reflect.Slice:
- return !canExpand(t.Elem())
- case reflect.Ptr:
- return false
- case reflect.Chan, reflect.Func, reflect.UnsafePointer:
- return false
- }
- return true
-}
-
-func canExpand(t reflect.Type) bool {
- switch t.Kind() {
- case reflect.Map, reflect.Struct,
- reflect.Interface, reflect.Array, reflect.Slice,
- reflect.Ptr:
- return true
- }
- return false
-}
-
-func labelType(t reflect.Type) bool {
- switch t.Kind() {
- case reflect.Interface, reflect.Struct:
- return true
- }
- return false
-}
-
-func (p *printer) fmtString(s string, quote bool) {
- if quote {
- s = strconv.Quote(s)
- }
- io.WriteString(p, s)
-}
-
-func writeByte(w io.Writer, b byte) {
- w.Write([]byte{b})
-}
-
-func getField(v reflect.Value, i int) reflect.Value {
- val := v.Field(i)
- if val.Kind() == reflect.Interface && !val.IsNil() {
- val = val.Elem()
- }
- return val
-}
diff --git a/vendor/github.com/kr/pretty/pretty.go b/vendor/github.com/kr/pretty/pretty.go
deleted file mode 100644
index b4ca583c..00000000
--- a/vendor/github.com/kr/pretty/pretty.go
+++ /dev/null
@@ -1,108 +0,0 @@
-// Package pretty provides pretty-printing for Go values. This is
-// useful during debugging, to avoid wrapping long output lines in
-// the terminal.
-//
-// It provides a function, Formatter, that can be used with any
-// function that accepts a format string. It also provides
-// convenience wrappers for functions in packages fmt and log.
-package pretty
-
-import (
- "fmt"
- "io"
- "log"
- "reflect"
-)
-
-// Errorf is a convenience wrapper for fmt.Errorf.
-//
-// Calling Errorf(f, x, y) is equivalent to
-// fmt.Errorf(f, Formatter(x), Formatter(y)).
-func Errorf(format string, a ...interface{}) error {
- return fmt.Errorf(format, wrap(a, false)...)
-}
-
-// Fprintf is a convenience wrapper for fmt.Fprintf.
-//
-// Calling Fprintf(w, f, x, y) is equivalent to
-// fmt.Fprintf(w, f, Formatter(x), Formatter(y)).
-func Fprintf(w io.Writer, format string, a ...interface{}) (n int, error error) {
- return fmt.Fprintf(w, format, wrap(a, false)...)
-}
-
-// Log is a convenience wrapper for log.Printf.
-//
-// Calling Log(x, y) is equivalent to
-// log.Print(Formatter(x), Formatter(y)), but each operand is
-// formatted with "%# v".
-func Log(a ...interface{}) {
- log.Print(wrap(a, true)...)
-}
-
-// Logf is a convenience wrapper for log.Printf.
-//
-// Calling Logf(f, x, y) is equivalent to
-// log.Printf(f, Formatter(x), Formatter(y)).
-func Logf(format string, a ...interface{}) {
- log.Printf(format, wrap(a, false)...)
-}
-
-// Logln is a convenience wrapper for log.Printf.
-//
-// Calling Logln(x, y) is equivalent to
-// log.Println(Formatter(x), Formatter(y)), but each operand is
-// formatted with "%# v".
-func Logln(a ...interface{}) {
- log.Println(wrap(a, true)...)
-}
-
-// Print pretty-prints its operands and writes to standard output.
-//
-// Calling Print(x, y) is equivalent to
-// fmt.Print(Formatter(x), Formatter(y)), but each operand is
-// formatted with "%# v".
-func Print(a ...interface{}) (n int, errno error) {
- return fmt.Print(wrap(a, true)...)
-}
-
-// Printf is a convenience wrapper for fmt.Printf.
-//
-// Calling Printf(f, x, y) is equivalent to
-// fmt.Printf(f, Formatter(x), Formatter(y)).
-func Printf(format string, a ...interface{}) (n int, errno error) {
- return fmt.Printf(format, wrap(a, false)...)
-}
-
-// Println pretty-prints its operands and writes to standard output.
-//
-// Calling Println(x, y) is equivalent to
-// fmt.Println(Formatter(x), Formatter(y)), but each operand is
-// formatted with "%# v".
-func Println(a ...interface{}) (n int, errno error) {
- return fmt.Println(wrap(a, true)...)
-}
-
-// Sprint is a convenience wrapper for fmt.Sprintf.
-//
-// Calling Sprint(x, y) is equivalent to
-// fmt.Sprint(Formatter(x), Formatter(y)), but each operand is
-// formatted with "%# v".
-func Sprint(a ...interface{}) string {
- return fmt.Sprint(wrap(a, true)...)
-}
-
-// Sprintf is a convenience wrapper for fmt.Sprintf.
-//
-// Calling Sprintf(f, x, y) is equivalent to
-// fmt.Sprintf(f, Formatter(x), Formatter(y)).
-func Sprintf(format string, a ...interface{}) string {
- return fmt.Sprintf(format, wrap(a, false)...)
-}
-
-func wrap(a []interface{}, force bool) []interface{} {
- w := make([]interface{}, len(a))
- for i, x := range a {
- w[i] = formatter{v: reflect.ValueOf(x), force: force}
- }
- return w
-}
diff --git a/vendor/github.com/kr/pretty/zero.go b/vendor/github.com/kr/pretty/zero.go
deleted file mode 100644
index abb5b6fc..00000000
--- a/vendor/github.com/kr/pretty/zero.go
+++ /dev/null
@@ -1,41 +0,0 @@
-package pretty
-
-import (
- "reflect"
-)
-
-func nonzero(v reflect.Value) bool {
- switch v.Kind() {
- case reflect.Bool:
- return v.Bool()
- case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
- return v.Int() != 0
- case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
- return v.Uint() != 0
- case reflect.Float32, reflect.Float64:
- return v.Float() != 0
- case reflect.Complex64, reflect.Complex128:
- return v.Complex() != complex(0, 0)
- case reflect.String:
- return v.String() != ""
- case reflect.Struct:
- for i := 0; i < v.NumField(); i++ {
- if nonzero(getField(v, i)) {
- return true
- }
- }
- return false
- case reflect.Array:
- for i := 0; i < v.Len(); i++ {
- if nonzero(v.Index(i)) {
- return true
- }
- }
- return false
- case reflect.Map, reflect.Interface, reflect.Slice, reflect.Ptr, reflect.Chan, reflect.Func:
- return !v.IsNil()
- case reflect.UnsafePointer:
- return v.Pointer() != 0
- }
- return true
-}
diff --git a/vendor/github.com/kr/text/License b/vendor/github.com/kr/text/License
deleted file mode 100644
index 480a3280..00000000
--- a/vendor/github.com/kr/text/License
+++ /dev/null
@@ -1,19 +0,0 @@
-Copyright 2012 Keith Rarick
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in
-all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-THE SOFTWARE.
diff --git a/vendor/github.com/kr/text/Readme b/vendor/github.com/kr/text/Readme
deleted file mode 100644
index 7e6e7c06..00000000
--- a/vendor/github.com/kr/text/Readme
+++ /dev/null
@@ -1,3 +0,0 @@
-This is a Go package for manipulating paragraphs of text.
-
-See http://go.pkgdoc.org/github.com/kr/text for full documentation.
diff --git a/vendor/github.com/kr/text/doc.go b/vendor/github.com/kr/text/doc.go
deleted file mode 100644
index cf4c198f..00000000
--- a/vendor/github.com/kr/text/doc.go
+++ /dev/null
@@ -1,3 +0,0 @@
-// Package text provides rudimentary functions for manipulating text in
-// paragraphs.
-package text
diff --git a/vendor/github.com/kr/text/indent.go b/vendor/github.com/kr/text/indent.go
deleted file mode 100644
index 4ebac45c..00000000
--- a/vendor/github.com/kr/text/indent.go
+++ /dev/null
@@ -1,74 +0,0 @@
-package text
-
-import (
- "io"
-)
-
-// Indent inserts prefix at the beginning of each non-empty line of s. The
-// end-of-line marker is NL.
-func Indent(s, prefix string) string {
- return string(IndentBytes([]byte(s), []byte(prefix)))
-}
-
-// IndentBytes inserts prefix at the beginning of each non-empty line of b.
-// The end-of-line marker is NL.
-func IndentBytes(b, prefix []byte) []byte {
- var res []byte
- bol := true
- for _, c := range b {
- if bol && c != '\n' {
- res = append(res, prefix...)
- }
- res = append(res, c)
- bol = c == '\n'
- }
- return res
-}
-
-// Writer indents each line of its input.
-type indentWriter struct {
- w io.Writer
- bol bool
- pre [][]byte
- sel int
- off int
-}
-
-// NewIndentWriter makes a new write filter that indents the input
-// lines. Each line is prefixed in order with the corresponding
-// element of pre. If there are more lines than elements, the last
-// element of pre is repeated for each subsequent line.
-func NewIndentWriter(w io.Writer, pre ...[]byte) io.Writer {
- return &indentWriter{
- w: w,
- pre: pre,
- bol: true,
- }
-}
-
-// The only errors returned are from the underlying indentWriter.
-func (w *indentWriter) Write(p []byte) (n int, err error) {
- for _, c := range p {
- if w.bol {
- var i int
- i, err = w.w.Write(w.pre[w.sel][w.off:])
- w.off += i
- if err != nil {
- return n, err
- }
- }
- _, err = w.w.Write([]byte{c})
- if err != nil {
- return n, err
- }
- n++
- w.bol = c == '\n'
- if w.bol {
- w.off = 0
- if w.sel < len(w.pre)-1 {
- w.sel++
- }
- }
- }
- return n, nil
-}
diff --git a/vendor/github.com/kr/text/wrap.go b/vendor/github.com/kr/text/wrap.go
deleted file mode 100644
index b09bb037..00000000
--- a/vendor/github.com/kr/text/wrap.go
+++ /dev/null
@@ -1,86 +0,0 @@
-package text
-
-import (
- "bytes"
- "math"
-)
-
-var (
- nl = []byte{'\n'}
- sp = []byte{' '}
-)
-
-const defaultPenalty = 1e5
-
-// Wrap wraps s into a paragraph of lines of length lim, with minimal
-// raggedness.
-func Wrap(s string, lim int) string {
- return string(WrapBytes([]byte(s), lim))
-}
-
-// WrapBytes wraps b into a paragraph of lines of length lim, with minimal
-// raggedness.
-func WrapBytes(b []byte, lim int) []byte {
- words := bytes.Split(bytes.Replace(bytes.TrimSpace(b), nl, sp, -1), sp)
- var lines [][]byte
- for _, line := range WrapWords(words, 1, lim, defaultPenalty) {
- lines = append(lines, bytes.Join(line, sp))
- }
- return bytes.Join(lines, nl)
-}
-
-// WrapWords is the low-level line-breaking algorithm, useful if you need more
-// control over the details of the text wrapping process. For most uses, either
-// Wrap or WrapBytes will be sufficient and more convenient.
-//
-// WrapWords splits a list of words into lines with minimal "raggedness",
-// treating each byte as one unit, accounting for spc units between adjacent
-// words on each line, and attempting to limit lines to lim units. Raggedness
-// is the total error over all lines, where error is the square of the
-// difference of the length of the line and lim. Too-long lines (which only
-// happen when a single word is longer than lim units) have pen penalty units
-// added to the error.
-func WrapWords(words [][]byte, spc, lim, pen int) [][][]byte {
- n := len(words)
-
- length := make([][]int, n)
- for i := 0; i < n; i++ {
- length[i] = make([]int, n)
- length[i][i] = len(words[i])
- for j := i + 1; j < n; j++ {
- length[i][j] = length[i][j-1] + spc + len(words[j])
- }
- }
-
- nbrk := make([]int, n)
- cost := make([]int, n)
- for i := range cost {
- cost[i] = math.MaxInt32
- }
- for i := n - 1; i >= 0; i-- {
- if length[i][n-1] <= lim || i == n-1 {
- cost[i] = 0
- nbrk[i] = n
- } else {
- for j := i + 1; j < n; j++ {
- d := lim - length[i][j-1]
- c := d*d + cost[j]
- if length[i][j-1] > lim {
- c += pen // too-long lines get a worse penalty
- }
- if c < cost[i] {
- cost[i] = c
- nbrk[i] = j
- }
- }
- }
- }
-
- var lines [][][]byte
- i := 0
- for i < n {
- lines = append(lines, words[i:nbrk[i]])
- i = nbrk[i]
- }
- return lines
-}
diff --git a/vendor/github.com/russellhaering/goxmldsig/.gitignore b/vendor/github.com/russellhaering/goxmldsig/.gitignore
deleted file mode 100644
index 9ed3b07c..00000000
--- a/vendor/github.com/russellhaering/goxmldsig/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-*.test
diff --git a/vendor/github.com/russellhaering/goxmldsig/.travis.yml b/vendor/github.com/russellhaering/goxmldsig/.travis.yml
deleted file mode 100644
index 4f020af5..00000000
--- a/vendor/github.com/russellhaering/goxmldsig/.travis.yml
+++ /dev/null
@@ -1,7 +0,0 @@
-language: go
-
-go:
- - "1.13.x"
- - "1.14.x"
- - "1.15.x"
- - master
diff --git a/vendor/github.com/russellhaering/goxmldsig/LICENSE b/vendor/github.com/russellhaering/goxmldsig/LICENSE
deleted file mode 100644
index 67db8588..00000000
--- a/vendor/github.com/russellhaering/goxmldsig/LICENSE
+++ /dev/null
@@ -1,175 +0,0 @@
-
- Apache License
- Version 2.0, January 2004
- http://www.apache.org/licenses/
-
- TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
-
- 1. Definitions.
-
- "License" shall mean the terms and conditions for use, reproduction,
- and distribution as defined by Sections 1 through 9 of this document.
-
- "Licensor" shall mean the copyright owner or entity authorized by
- the copyright owner that is granting the License.
-
- "Legal Entity" shall mean the union of the acting entity and all
- other entities that control, are controlled by, or are under common
- control with that entity. For the purposes of this definition,
- "control" means (i) the power, direct or indirect, to cause the
- direction or management of such entity, whether by contract or
- otherwise, or (ii) ownership of fifty percent (50%) or more of the
- outstanding shares, or (iii) beneficial ownership of such entity.
-
- "You" (or "Your") shall mean an individual or Legal Entity
- exercising permissions granted by this License.
-
- "Source" form shall mean the preferred form for making modifications,
- including but not limited to software source code, documentation
- source, and configuration files.
-
- "Object" form shall mean any form resulting from mechanical
- transformation or translation of a Source form, including but
- not limited to compiled object code, generated documentation,
- and conversions to other media types.
-
- "Work" shall mean the work of authorship, whether in Source or
- Object form, made available under the License, as indicated by a
- copyright notice that is included in or attached to the work
- (an example is provided in the Appendix below).
-
- "Derivative Works" shall mean any work, whether in Source or Object
- form, that is based on (or derived from) the Work and for which the
- editorial revisions, annotations, elaborations, or other modifications
- represent, as a whole, an original work of authorship. For the purposes
- of this License, Derivative Works shall not include works that remain
- separable from, or merely link (or bind by name) to the interfaces of,
- the Work and Derivative Works thereof.
-
- "Contribution" shall mean any work of authorship, including
- the original version of the Work and any modifications or additions
- to that Work or Derivative Works thereof, that is intentionally
- submitted to Licensor for inclusion in the Work by the copyright owner
- or by an individual or Legal Entity authorized to submit on behalf of
- the copyright owner. For the purposes of this definition, "submitted"
- means any form of electronic, verbal, or written communication sent
- to the Licensor or its representatives, including but not limited to
- communication on electronic mailing lists, source code control systems,
- and issue tracking systems that are managed by, or on behalf of, the
- Licensor for the purpose of discussing and improving the Work, but
- excluding communication that is conspicuously marked or otherwise
- designated in writing by the copyright owner as "Not a Contribution."
-
- "Contributor" shall mean Licensor and any individual or Legal Entity
- on behalf of whom a Contribution has been received by Licensor and
- subsequently incorporated within the Work.
-
- 2. Grant of Copyright License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- copyright license to reproduce, prepare Derivative Works of,
- publicly display, publicly perform, sublicense, and distribute the
- Work and such Derivative Works in Source or Object form.
-
- 3. Grant of Patent License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- (except as stated in this section) patent license to make, have made,
- use, offer to sell, sell, import, and otherwise transfer the Work,
- where such license applies only to those patent claims licensable
- by such Contributor that are necessarily infringed by their
- Contribution(s) alone or by combination of their Contribution(s)
- with the Work to which such Contribution(s) was submitted. If You
- institute patent litigation against any entity (including a
- cross-claim or counterclaim in a lawsuit) alleging that the Work
- or a Contribution incorporated within the Work constitutes direct
- or contributory patent infringement, then any patent licenses
- granted to You under this License for that Work shall terminate
- as of the date such litigation is filed.
-
- 4. Redistribution. You may reproduce and distribute copies of the
- Work or Derivative Works thereof in any medium, with or without
- modifications, and in Source or Object form, provided that You
- meet the following conditions:
-
- (a) You must give any other recipients of the Work or
- Derivative Works a copy of this License; and
-
- (b) You must cause any modified files to carry prominent notices
- stating that You changed the files; and
-
- (c) You must retain, in the Source form of any Derivative Works
- that You distribute, all copyright, patent, trademark, and
- attribution notices from the Source form of the Work,
- excluding those notices that do not pertain to any part of
- the Derivative Works; and
-
- (d) If the Work includes a "NOTICE" text file as part of its
- distribution, then any Derivative Works that You distribute must
- include a readable copy of the attribution notices contained
- within such NOTICE file, excluding those notices that do not
- pertain to any part of the Derivative Works, in at least one
- of the following places: within a NOTICE text file distributed
- as part of the Derivative Works; within the Source form or
- documentation, if provided along with the Derivative Works; or,
- within a display generated by the Derivative Works, if and
- wherever such third-party notices normally appear. The contents
- of the NOTICE file are for informational purposes only and
- do not modify the License. You may add Your own attribution
- notices within Derivative Works that You distribute, alongside
- or as an addendum to the NOTICE text from the Work, provided
- that such additional attribution notices cannot be construed
- as modifying the License.
-
- You may add Your own copyright statement to Your modifications and
- may provide additional or different license terms and conditions
- for use, reproduction, or distribution of Your modifications, or
- for any such Derivative Works as a whole, provided Your use,
- reproduction, and distribution of the Work otherwise complies with
- the conditions stated in this License.
-
- 5. Submission of Contributions. Unless You explicitly state otherwise,
- any Contribution intentionally submitted for inclusion in the Work
- by You to the Licensor shall be under the terms and conditions of
- this License, without any additional terms or conditions.
- Notwithstanding the above, nothing herein shall supersede or modify
- the terms of any separate license agreement you may have executed
- with Licensor regarding such Contributions.
-
- 6. Trademarks. This License does not grant permission to use the trade
- names, trademarks, service marks, or product names of the Licensor,
- except as required for reasonable and customary use in describing the
- origin of the Work and reproducing the content of the NOTICE file.
-
- 7. Disclaimer of Warranty. Unless required by applicable law or
- agreed to in writing, Licensor provides the Work (and each
- Contributor provides its Contributions) on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
- implied, including, without limitation, any warranties or conditions
- of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
- PARTICULAR PURPOSE. You are solely responsible for determining the
- appropriateness of using or redistributing the Work and assume any
- risks associated with Your exercise of permissions under this License.
-
- 8. Limitation of Liability. In no event and under no legal theory,
- whether in tort (including negligence), contract, or otherwise,
- unless required by applicable law (such as deliberate and grossly
- negligent acts) or agreed to in writing, shall any Contributor be
- liable to You for damages, including any direct, indirect, special,
- incidental, or consequential damages of any character arising as a
- result of this License or out of the use or inability to use the
- Work (including but not limited to damages for loss of goodwill,
- work stoppage, computer failure or malfunction, or any and all
- other commercial damages or losses), even if such Contributor
- has been advised of the possibility of such damages.
-
- 9. Accepting Warranty or Additional Liability. While redistributing
- the Work or Derivative Works thereof, You may choose to offer,
- and charge a fee for, acceptance of support, warranty, indemnity,
- or other liability obligations and/or rights consistent with this
- License. However, in accepting such obligations, You may act only
- on Your own behalf and on Your sole responsibility, not on behalf
- of any other Contributor, and only if You agree to indemnify,
- defend, and hold each Contributor harmless for any liability
- incurred by, or claims asserted against, such Contributor by reason
- of your accepting any such warranty or additional liability.
diff --git a/vendor/github.com/russellhaering/goxmldsig/README.md b/vendor/github.com/russellhaering/goxmldsig/README.md
deleted file mode 100644
index 9464e61e..00000000
--- a/vendor/github.com/russellhaering/goxmldsig/README.md
+++ /dev/null
@@ -1,90 +0,0 @@
-# goxmldsig
-
-[](https://travis-ci.org/russellhaering/goxmldsig)
-[](https://godoc.org/github.com/russellhaering/goxmldsig)
-
-XML Digital Signatures implemented in pure Go.
-
-## Installation
-
-Install `goxmldsig` using `go get`:
-
-```
-$ go get github.com/russellhaering/goxmldsig
-```
-
-## Usage
-
-### Signing
-
-```go
-package main
-
-import (
- "github.com/beevik/etree"
- "github.com/russellhaering/goxmldsig"
-)
-
-func main() {
- // Generate a key and self-signed certificate for signing
- randomKeyStore := dsig.RandomKeyStoreForTest()
- ctx := dsig.NewDefaultSigningContext(randomKeyStore)
- elementToSign := &etree.Element{
- Tag: "ExampleElement",
- }
- elementToSign.CreateAttr("ID", "id1234")
-
- // Sign the element
- signedElement, err := ctx.SignEnveloped(elementToSign)
- if err != nil {
- panic(err)
- }
-
- // Serialize the signed element. It is important not to modify the element
- // after it has been signed - even pretty-printing the XML will invalidate
- // the signature.
- doc := etree.NewDocument()
- doc.SetRoot(signedElement)
- str, err := doc.WriteToString()
- if err != nil {
- panic(err)
- }
-
- println(str)
-}
-```
-
-### Signature Validation
-
-```go
-// Validate an element against a root certificate
-func validate(root *x509.Certificate, el *etree.Element) {
- // Construct a signing context with one or more roots of trust.
- ctx := dsig.NewDefaultValidationContext(&dsig.MemoryX509CertificateStore{
- Roots: []*x509.Certificate{root},
- })
-
- // It is important to only use the returned validated element.
- // See: https://www.w3.org/TR/xmldsig-bestpractices/#check-what-is-signed
- validated, err := ctx.Validate(el)
- if err != nil {
- panic(err)
- }
-
- doc := etree.NewDocument()
- doc.SetRoot(validated)
- str, err := doc.WriteToString()
- if err != nil {
- panic(err)
- }
-
- println(str)
-}
-```
-
-## Limitations
-
-This library was created in order to [implement SAML 2.0](https://github.com/russellhaering/gosaml2)
-without needing to execute a command line tool to create and validate signatures. It currently
-only implements the subset of relevant standards needed to support that implementation, but
-I hope to make it more complete over time. Contributions are welcome.
diff --git a/vendor/github.com/russellhaering/goxmldsig/canonicalize.go b/vendor/github.com/russellhaering/goxmldsig/canonicalize.go
deleted file mode 100644
index 55101919..00000000
--- a/vendor/github.com/russellhaering/goxmldsig/canonicalize.go
+++ /dev/null
@@ -1,162 +0,0 @@
-package dsig
-
-import (
- "sort"
-
- "github.com/beevik/etree"
- "github.com/russellhaering/goxmldsig/etreeutils"
-)
-
-// Canonicalizer is an implementation of a canonicalization algorithm.
-type Canonicalizer interface {
- Canonicalize(el *etree.Element) ([]byte, error)
- Algorithm() AlgorithmID
-}
-
-type c14N10ExclusiveCanonicalizer struct {
- prefixList string
-}
-
-// MakeC14N10ExclusiveCanonicalizerWithPrefixList constructs an exclusive Canonicalizer
-// from a PrefixList in NMTOKENS format (a white space separated list).
-func MakeC14N10ExclusiveCanonicalizerWithPrefixList(prefixList string) Canonicalizer {
- return &c14N10ExclusiveCanonicalizer{
- prefixList: prefixList,
- }
-}
-
-// Canonicalize transforms the input Element into a serialized XML document in canonical form.
-func (c *c14N10ExclusiveCanonicalizer) Canonicalize(el *etree.Element) ([]byte, error) {
- err := etreeutils.TransformExcC14n(el, c.prefixList)
- if err != nil {
- return nil, err
- }
-
- return canonicalSerialize(el)
-}
-
-func (c *c14N10ExclusiveCanonicalizer) Algorithm() AlgorithmID {
- return CanonicalXML10ExclusiveAlgorithmId
-}
-
-type c14N11Canonicalizer struct{}
-
-// MakeC14N11Canonicalizer constructs an inclusive canonicalizer.
-func MakeC14N11Canonicalizer() Canonicalizer {
- return &c14N11Canonicalizer{}
-}
-
-// Canonicalize transforms the input Element into a serialized XML document in canonical form.
-func (c *c14N11Canonicalizer) Canonicalize(el *etree.Element) ([]byte, error) {
- scope := make(map[string]struct{})
- return canonicalSerialize(canonicalPrep(el, scope))
-}
-
-func (c *c14N11Canonicalizer) Algorithm() AlgorithmID {
- return CanonicalXML11AlgorithmId
-}
-
-type c14N10RecCanonicalizer struct{}
-
-// MakeC14N10RecCanonicalizer constructs an inclusive canonicalizer.
-func MakeC14N10RecCanonicalizer() Canonicalizer {
- return &c14N10RecCanonicalizer{}
-}
-
-// Canonicalize transforms the input Element into a serialized XML document in canonical form.
-func (c *c14N10RecCanonicalizer) Canonicalize(el *etree.Element) ([]byte, error) {
- scope := make(map[string]struct{})
- return canonicalSerialize(canonicalPrep(el, scope))
-}
-
-func (c *c14N10RecCanonicalizer) Algorithm() AlgorithmID {
- return CanonicalXML10RecAlgorithmId
-}
-
-type c14N10CommentCanonicalizer struct{}
-
-// MakeC14N10CommentCanonicalizer constructs an inclusive canonicalizer.
-func MakeC14N10CommentCanonicalizer() Canonicalizer {
- return &c14N10CommentCanonicalizer{}
-}
-
-// Canonicalize transforms the input Element into a serialized XML document in canonical form.
-func (c *c14N10CommentCanonicalizer) Canonicalize(el *etree.Element) ([]byte, error) {
- scope := make(map[string]struct{})
- return canonicalSerialize(canonicalPrep(el, scope))
-}
-
-func (c *c14N10CommentCanonicalizer) Algorithm() AlgorithmID {
- return CanonicalXML10CommentAlgorithmId
-}
-
-func composeAttr(space, key string) string {
- if space != "" {
- return space + ":" + key
- }
-
- return key
-}
-
-type c14nSpace struct {
- a etree.Attr
- used bool
-}
-
-const nsSpace = "xmlns"
-
-// canonicalPrep accepts an *etree.Element and transforms it into one which is ready
-// for serialization into inclusive canonical form. Specifically this
-// entails:
-//
-// 1. Stripping re-declarations of namespaces
-// 2. Sorting attributes into canonical order
-//
-// Inclusive canonicalization does not strip unused namespaces.
-//
-// TODO(russell_h): This is very similar to excCanonicalPrep - perhaps they should
-// be unified into one parameterized function?
-func canonicalPrep(el *etree.Element, seenSoFar map[string]struct{}) *etree.Element {
- _seenSoFar := make(map[string]struct{})
- for k, v := range seenSoFar {
- _seenSoFar[k] = v
- }
-
- ne := el.Copy()
- sort.Sort(etreeutils.SortedAttrs(ne.Attr))
- if len(ne.Attr) != 0 {
- for _, attr := range ne.Attr {
- if attr.Space != nsSpace {
- continue
- }
- key := attr.Space + ":" + attr.Key
- if _, seen := _seenSoFar[key]; seen {
- ne.RemoveAttr(attr.Space + ":" + attr.Key)
- } else {
- _seenSoFar[key] = struct{}{}
- }
- }
- }
-
- for i, token := range ne.Child {
- childElement, ok := token.(*etree.Element)
- if ok {
- ne.Child[i] = canonicalPrep(childElement, _seenSoFar)
- }
- }
-
- return ne
-}
-
-func canonicalSerialize(el *etree.Element) ([]byte, error) {
- doc := etree.NewDocument()
- doc.SetRoot(el.Copy())
-
- doc.WriteSettings = etree.WriteSettings{
- CanonicalAttrVal: true,
- CanonicalEndTags: true,
- CanonicalText: true,
- }
-
- return doc.WriteToBytes()
-}
diff --git a/vendor/github.com/russellhaering/goxmldsig/clock.go b/vendor/github.com/russellhaering/goxmldsig/clock.go
deleted file mode 100644
index cceaaa54..00000000
--- a/vendor/github.com/russellhaering/goxmldsig/clock.go
+++ /dev/null
@@ -1,55 +0,0 @@
-package dsig
-
-import (
- "time"
-
- "github.com/jonboulle/clockwork"
-)
-
-// Clock wraps a clockwork.Clock (which could be real or fake) in order
-// to default to a real clock when a nil *Clock is used. In other words,
-// if you attempt to use a nil *Clock it will defer to the real system
-// clock. This allows Clock to be easily added to structs with methods
-// that currently reference the time package, without requiring every
-// instantiation of that struct to be updated.
-type Clock struct {
- wrapped clockwork.Clock
-}
-
-func (c *Clock) getWrapped() clockwork.Clock {
- if c == nil {
- return clockwork.NewRealClock()
- }
-
- return c.wrapped
-}
-
-func (c *Clock) After(d time.Duration) <-chan time.Time {
- return c.getWrapped().After(d)
-}
-
-func (c *Clock) Sleep(d time.Duration) {
- c.getWrapped().Sleep(d)
-}
-
-func (c *Clock) Now() time.Time {
- return c.getWrapped().Now()
-}
-
-func NewRealClock() *Clock {
- return &Clock{
- wrapped: clockwork.NewRealClock(),
- }
-}
-
-func NewFakeClock(wrapped clockwork.Clock) *Clock {
- return &Clock{
- wrapped: wrapped,
- }
-}
-
-func NewFakeClockAt(t time.Time) *Clock {
- return &Clock{
- wrapped: clockwork.NewFakeClockAt(t),
- }
-}
diff --git a/vendor/github.com/russellhaering/goxmldsig/etreeutils/canonicalize.go b/vendor/github.com/russellhaering/goxmldsig/etreeutils/canonicalize.go
deleted file mode 100644
index e9f8deb1..00000000
--- a/vendor/github.com/russellhaering/goxmldsig/etreeutils/canonicalize.go
+++ /dev/null
@@ -1,98 +0,0 @@
-package etreeutils
-
-import (
- "sort"
- "strings"
-
- "github.com/beevik/etree"
-)
-
-// TransformExcC14n transforms the passed element into xml-exc-c14n form.
-func TransformExcC14n(el *etree.Element, inclusiveNamespacesPrefixList string) error {
- prefixes := strings.Fields(inclusiveNamespacesPrefixList)
- prefixSet := make(map[string]struct{}, len(prefixes))
-
- for _, prefix := range prefixes {
- prefixSet[prefix] = struct{}{}
- }
-
- err := transformExcC14n(DefaultNSContext, DefaultNSContext, el, prefixSet)
- if err != nil {
- return err
- }
-
- return nil
-}
-
-func transformExcC14n(ctx, declared NSContext, el *etree.Element, inclusiveNamespaces map[string]struct{}) error {
- scope, err := ctx.SubContext(el)
- if err != nil {
- return err
- }
-
- visiblyUtilizedPrefixes := map[string]struct{}{
- el.Space: struct{}{},
- }
-
- filteredAttrs := []etree.Attr{}
-
- // Filter out all namespace declarations
- for _, attr := range el.Attr {
- switch {
- case attr.Space == xmlnsPrefix:
- if _, ok := inclusiveNamespaces[attr.Key]; ok {
- visiblyUtilizedPrefixes[attr.Key] = struct{}{}
- }
-
- case attr.Space == defaultPrefix && attr.Key == xmlnsPrefix:
- if _, ok := inclusiveNamespaces[defaultPrefix]; ok {
- visiblyUtilizedPrefixes[defaultPrefix] = struct{}{}
- }
-
- default:
- if attr.Space != defaultPrefix {
- visiblyUtilizedPrefixes[attr.Space] = struct{}{}
- }
-
- filteredAttrs = append(filteredAttrs, attr)
- }
- }
-
- el.Attr = filteredAttrs
-
- declared = declared.Copy()
-
- // Declare all visibly utilized prefixes that are in-scope but haven't
- // been declared in the canonicalized form yet. These might have been
- // declared on this element but then filtered out above, or they might
- // have been declared on an ancestor (before canonicalization) which
- // didn't visibly utilize and thus had them removed.
- for prefix := range visiblyUtilizedPrefixes {
- // Skip redundant declarations - they have to already have the same
- // value.
- if declaredNamespace, ok := declared.prefixes[prefix]; ok {
- if value, ok := scope.prefixes[prefix]; ok && declaredNamespace == value {
- continue
- }
- }
-
- namespace, err := scope.LookupPrefix(prefix)
- if err != nil {
- return err
- }
-
- el.Attr = append(el.Attr, declared.declare(prefix, namespace))
- }
-
- sort.Sort(SortedAttrs(el.Attr))
-
- // Transform child elements
- for _, child := range el.ChildElements() {
- err := transformExcC14n(scope, declared, child, inclusiveNamespaces)
- if err != nil {
- return err
- }
- }
-
- return nil
-}
diff --git a/vendor/github.com/russellhaering/goxmldsig/etreeutils/namespace.go b/vendor/github.com/russellhaering/goxmldsig/etreeutils/namespace.go
deleted file mode 100644
index baf1124f..00000000
--- a/vendor/github.com/russellhaering/goxmldsig/etreeutils/namespace.go
+++ /dev/null
@@ -1,407 +0,0 @@
-package etreeutils
-
-import (
- "errors"
-
- "fmt"
-
- "sort"
-
- "github.com/beevik/etree"
-)
-
-const (
- defaultPrefix = ""
- xmlnsPrefix = "xmlns"
- xmlPrefix = "xml"
-
- XMLNamespace = "http://www.w3.org/XML/1998/namespace"
- XMLNSNamespace = "http://www.w3.org/2000/xmlns/"
-)
-
-var (
- DefaultNSContext = NSContext{
- prefixes: map[string]string{
- defaultPrefix: XMLNamespace,
- xmlPrefix: XMLNamespace,
- xmlnsPrefix: XMLNSNamespace,
- },
- }
-
- EmptyNSContext = NSContext{}
-
- ErrReservedNamespace = errors.New("disallowed declaration of reserved namespace")
- ErrInvalidDefaultNamespace = errors.New("invalid default namespace declaration")
- ErrTraversalHalted = errors.New("traversal halted")
-)
-
-type ErrUndeclaredNSPrefix struct {
- Prefix string
-}
-
-func (e ErrUndeclaredNSPrefix) Error() string {
- return fmt.Sprintf("undeclared namespace prefix: '%s'", e.Prefix)
-}
-
-type NSContext struct {
- prefixes map[string]string
-}
-
-func (ctx NSContext) Copy() NSContext {
- prefixes := make(map[string]string, len(ctx.prefixes)+4)
- for k, v := range ctx.prefixes {
- prefixes[k] = v
- }
-
- return NSContext{prefixes: prefixes}
-}
-
-func (ctx NSContext) declare(prefix, namespace string) etree.Attr {
- ctx.prefixes[prefix] = namespace
-
- switch prefix {
- case defaultPrefix:
- return etree.Attr{
- Key: xmlnsPrefix,
- Value: namespace,
- }
-
- default:
- return etree.Attr{
- Space: xmlnsPrefix,
- Key: prefix,
- Value: namespace,
- }
- }
-}
-
-func (ctx NSContext) SubContext(el *etree.Element) (NSContext, error) {
- // The subcontext should inherit existing declared prefixes
- newCtx := ctx.Copy()
-
- // Merge new namespace declarations on top of existing ones.
- for _, attr := range el.Attr {
- if attr.Space == xmlnsPrefix {
- // This attribute is a namespace declaration of the form "xmlns:"
-
- // The 'xml' namespace may only be re-declared with the name 'http://www.w3.org/XML/1998/namespace'
- if attr.Key == xmlPrefix && attr.Value != XMLNamespace {
- return ctx, ErrReservedNamespace
- }
-
- // The 'xmlns' namespace may not be re-declared
- if attr.Key == xmlnsPrefix {
- return ctx, ErrReservedNamespace
- }
-
- newCtx.declare(attr.Key, attr.Value)
- } else if attr.Space == defaultPrefix && attr.Key == xmlnsPrefix {
- // This attribute is a default namespace declaration
-
- // The xmlns namespace value may not be declared as the default namespace
- if attr.Value == XMLNSNamespace {
- return ctx, ErrInvalidDefaultNamespace
- }
-
- newCtx.declare(defaultPrefix, attr.Value)
- }
- }
-
- return newCtx, nil
-}
-
-// Prefixes returns a copy of this context's prefix map.
-func (ctx NSContext) Prefixes() map[string]string {
- prefixes := make(map[string]string, len(ctx.prefixes))
- for k, v := range ctx.prefixes {
- prefixes[k] = v
- }
-
- return prefixes
-}
-
-// LookupPrefix attempts to find a declared namespace for the specified prefix. If the prefix
-// is an empty string this will be the default namespace for this context. If the prefix is
-// undeclared in this context an ErrUndeclaredNSPrefix will be returned.
-func (ctx NSContext) LookupPrefix(prefix string) (string, error) {
- if namespace, ok := ctx.prefixes[prefix]; ok {
- return namespace, nil
- }
-
- return "", ErrUndeclaredNSPrefix{
- Prefix: prefix,
- }
-}
-
-// NSIterHandler is a function which is invoked with a element and its surrounding
-// NSContext during traversals.
-type NSIterHandler func(NSContext, *etree.Element) error
-
-// NSTraverse traverses an element tree, invoking the passed handler for each element
-// in the tree.
-func NSTraverse(ctx NSContext, el *etree.Element, handle NSIterHandler) error {
- ctx, err := ctx.SubContext(el)
- if err != nil {
- return err
- }
-
- err = handle(ctx, el)
- if err != nil {
- return err
- }
-
- // Recursively traverse child elements.
- for _, child := range el.ChildElements() {
- err := NSTraverse(ctx, child, handle)
- if err != nil {
- return err
- }
- }
-
- return nil
-}
-
-// NSDetatch makes a copy of the passed element, and declares any namespaces in
-// the passed context onto the new element before returning it.
-func NSDetatch(ctx NSContext, el *etree.Element) (*etree.Element, error) {
- ctx, err := ctx.SubContext(el)
- if err != nil {
- return nil, err
- }
-
- el = el.Copy()
-
- // Build a new attribute list
- attrs := make([]etree.Attr, 0, len(el.Attr))
-
- // First copy over anything that isn't a namespace declaration
- for _, attr := range el.Attr {
- if attr.Space == xmlnsPrefix {
- continue
- }
-
- if attr.Space == defaultPrefix && attr.Key == xmlnsPrefix {
- continue
- }
-
- attrs = append(attrs, attr)
- }
-
- // Append all in-context namespace declarations
- for prefix, namespace := range ctx.prefixes {
- // Skip the implicit "xml" and "xmlns" prefix declarations
- if prefix == xmlnsPrefix || prefix == xmlPrefix {
- continue
- }
-
- // Also skip declararing the default namespace as XMLNamespace
- if prefix == defaultPrefix && namespace == XMLNamespace {
- continue
- }
-
- if prefix != defaultPrefix {
- attrs = append(attrs, etree.Attr{
- Space: xmlnsPrefix,
- Key: prefix,
- Value: namespace,
- })
- } else {
- attrs = append(attrs, etree.Attr{
- Key: xmlnsPrefix,
- Value: namespace,
- })
- }
- }
-
- sort.Sort(SortedAttrs(attrs))
-
- el.Attr = attrs
-
- return el, nil
-}
-
-// NSSelectOne behaves identically to NSSelectOneCtx, but uses DefaultNSContext as the
-// surrounding context.
-func NSSelectOne(el *etree.Element, namespace, tag string) (*etree.Element, error) {
- return NSSelectOneCtx(DefaultNSContext, el, namespace, tag)
-}
-
-// NSSelectOneCtx conducts a depth-first search for an element with the specified namespace
-// and tag. If such an element is found, a new *etree.Element is returned which is a
-// copy of the found element, but with all in-context namespace declarations attached
-// to the element as attributes.
-func NSSelectOneCtx(ctx NSContext, el *etree.Element, namespace, tag string) (*etree.Element, error) {
- var found *etree.Element
-
- err := NSFindIterateCtx(ctx, el, namespace, tag, func(ctx NSContext, el *etree.Element) error {
- var err error
-
- found, err = NSDetatch(ctx, el)
- if err != nil {
- return err
- }
-
- return ErrTraversalHalted
- })
-
- if err != nil {
- return nil, err
- }
-
- return found, nil
-}
-
-// NSFindIterate behaves identically to NSFindIterateCtx, but uses DefaultNSContext
-// as the surrounding context.
-func NSFindIterate(el *etree.Element, namespace, tag string, handle NSIterHandler) error {
- return NSFindIterateCtx(DefaultNSContext, el, namespace, tag, handle)
-}
-
-// NSFindIterateCtx conducts a depth-first traversal searching for elements with the
-// specified tag in the specified namespace. It uses the passed NSContext for prefix
-// lookups. For each such element, the passed handler function is invoked. If the
-// handler function returns an error traversal is immediately halted. If the error
-// returned by the handler is ErrTraversalHalted then nil will be returned by
-// NSFindIterate. If any other error is returned by the handler, that error will be
-// returned by NSFindIterate.
-func NSFindIterateCtx(ctx NSContext, el *etree.Element, namespace, tag string, handle NSIterHandler) error {
- err := NSTraverse(ctx, el, func(ctx NSContext, el *etree.Element) error {
- _ctx, err := ctx.SubContext(el)
- if err != nil {
- return err
- }
-
- currentNS, err := _ctx.LookupPrefix(el.Space)
- if err != nil {
- return err
- }
-
- // Base case, el is the sought after element.
- if currentNS == namespace && el.Tag == tag {
- return handle(ctx, el)
- }
-
- return nil
- })
-
- if err != nil && err != ErrTraversalHalted {
- return err
- }
-
- return nil
-}
-
-// NSFindOne behaves identically to NSFindOneCtx, but uses DefaultNSContext for
-// context.
-func NSFindOne(el *etree.Element, namespace, tag string) (*etree.Element, error) {
- return NSFindOneCtx(DefaultNSContext, el, namespace, tag)
-}
-
-// NSFindOneCtx conducts a depth-first search for the specified element. If such an element
-// is found a reference to it is returned.
-func NSFindOneCtx(ctx NSContext, el *etree.Element, namespace, tag string) (*etree.Element, error) {
- var found *etree.Element
-
- err := NSFindIterateCtx(ctx, el, namespace, tag, func(ctx NSContext, el *etree.Element) error {
- found = el
- return ErrTraversalHalted
- })
-
- if err != nil {
- return nil, err
- }
-
- return found, nil
-}
-
-// NSIterateChildren iterates the children of an element, invoking the passed
-// handler with each direct child of the element, and the context surrounding
-// that child.
-func NSIterateChildren(ctx NSContext, el *etree.Element, handle NSIterHandler) error {
- ctx, err := ctx.SubContext(el)
- if err != nil {
- return err
- }
-
- // Iterate the child elements.
- for _, child := range el.ChildElements() {
- err = handle(ctx, child)
- if err != nil {
- return err
- }
- }
-
- return nil
-}
-
-// NSFindIterateChildrenCtx takes an element and its surrounding context, and iterates
-// the children of that element searching for an element matching the passed namespace
-// and tag. For each such element that is found, handle is invoked with the matched
-// element and its own surrounding context.
-func NSFindChildrenIterateCtx(ctx NSContext, el *etree.Element, namespace, tag string, handle NSIterHandler) error {
- err := NSIterateChildren(ctx, el, func(ctx NSContext, el *etree.Element) error {
- _ctx, err := ctx.SubContext(el)
- if err != nil {
- return err
- }
-
- currentNS, err := _ctx.LookupPrefix(el.Space)
- if err != nil {
- return err
- }
-
- // Base case, el is the sought after element.
- if currentNS == namespace && el.Tag == tag {
- return handle(ctx, el)
- }
-
- return nil
- })
-
- if err != nil && err != ErrTraversalHalted {
- return err
- }
-
- return nil
-}
-
-// NSFindOneChild behaves identically to NSFindOneChildCtx, but uses
-// DefaultNSContext for context.
-func NSFindOneChild(el *etree.Element, namespace, tag string) (*etree.Element, error) {
- return NSFindOneChildCtx(DefaultNSContext, el, namespace, tag)
-}
-
-// NSFindOneCtx conducts a depth-first search for the specified element. If such an
-// element is found a reference to it is returned.
-func NSFindOneChildCtx(ctx NSContext, el *etree.Element, namespace, tag string) (*etree.Element, error) {
- var found *etree.Element
-
- err := NSFindChildrenIterateCtx(ctx, el, namespace, tag, func(ctx NSContext, el *etree.Element) error {
- found = el
- return ErrTraversalHalted
- })
-
- if err != nil && err != ErrTraversalHalted {
- return nil, err
- }
-
- return found, nil
-}
-
-// NSBuildParentContext recurses upward from an element in order to build an NSContext
-// for its immediate parent. If the element has no parent DefaultNSContext
-// is returned.
-func NSBuildParentContext(el *etree.Element) (NSContext, error) {
- parent := el.Parent()
- if parent == nil {
- return DefaultNSContext, nil
- }
-
- ctx, err := NSBuildParentContext(parent)
-
- if err != nil {
- return ctx, err
- }
-
- return ctx.SubContext(parent)
-}
diff --git a/vendor/github.com/russellhaering/goxmldsig/etreeutils/sort.go b/vendor/github.com/russellhaering/goxmldsig/etreeutils/sort.go
deleted file mode 100644
index 5871a391..00000000
--- a/vendor/github.com/russellhaering/goxmldsig/etreeutils/sort.go
+++ /dev/null
@@ -1,66 +0,0 @@
-package etreeutils
-
-import "github.com/beevik/etree"
-
-// SortedAttrs provides sorting capabilities, compatible with XML C14N, on top
-// of an []etree.Attr
-type SortedAttrs []etree.Attr
-
-func (a SortedAttrs) Len() int {
- return len(a)
-}
-
-func (a SortedAttrs) Swap(i, j int) {
- a[i], a[j] = a[j], a[i]
-}
-
-func (a SortedAttrs) Less(i, j int) bool {
- // This is the best reference I've found on sort order:
- // http://dst.lbl.gov/~ksb/Scratch/XMLC14N.html
-
- // If attr j is a default namespace declaration, attr i may
- // not be strictly "less" than it.
- if a[j].Space == defaultPrefix && a[j].Key == xmlnsPrefix {
- return false
- }
-
- // Otherwise, if attr i is a default namespace declaration, it
- // must be less than anything else.
- if a[i].Space == defaultPrefix && a[i].Key == xmlnsPrefix {
- return true
- }
-
- // Next, namespace prefix declarations, sorted by prefix, come before
- // anythign else.
- if a[i].Space == xmlnsPrefix {
- if a[j].Space == xmlnsPrefix {
- return a[i].Key < a[j].Key
- }
- return true
- }
-
- if a[j].Space == xmlnsPrefix {
- return false
- }
-
- // Then come unprefixed attributes, sorted by key.
- if a[i].Space == defaultPrefix {
- if a[j].Space == defaultPrefix {
- return a[i].Key < a[j].Key
- }
- return true
- }
-
- if a[j].Space == defaultPrefix {
- return false
- }
-
- // Wow. We're still going. Finally, attributes in the same namespace should be
- // sorted by key. Attributes in different namespaces should be sorted by the
- // actual namespace (_not_ the prefix). For now just use the prefix.
- if a[i].Space == a[j].Space {
- return a[i].Key < a[j].Key
- }
-
- return a[i].Space < a[j].Space
-}
diff --git a/vendor/github.com/russellhaering/goxmldsig/etreeutils/unmarshal.go b/vendor/github.com/russellhaering/goxmldsig/etreeutils/unmarshal.go
deleted file mode 100644
index b1fecf85..00000000
--- a/vendor/github.com/russellhaering/goxmldsig/etreeutils/unmarshal.go
+++ /dev/null
@@ -1,43 +0,0 @@
-package etreeutils
-
-import (
- "encoding/xml"
-
- "github.com/beevik/etree"
-)
-
-// NSUnmarshalElement unmarshals the passed etree Element into the value pointed to by
-// v using encoding/xml in the context of the passed NSContext. If v implements
-// ElementKeeper, SetUnderlyingElement will be called on v with a reference to el.
-func NSUnmarshalElement(ctx NSContext, el *etree.Element, v interface{}) error {
- detatched, err := NSDetatch(ctx, el)
- if err != nil {
- return err
- }
-
- doc := etree.NewDocument()
- doc.AddChild(detatched)
- data, err := doc.WriteToBytes()
- if err != nil {
- return err
- }
-
- err = xml.Unmarshal(data, v)
- if err != nil {
- return err
- }
-
- switch v := v.(type) {
- case ElementKeeper:
- v.SetUnderlyingElement(el)
- }
-
- return nil
-}
-
-// ElementKeeper should be implemented by types which will be passed to
-// UnmarshalElement, but wish to keep a reference
-type ElementKeeper interface {
- SetUnderlyingElement(*etree.Element)
- UnderlyingElement() *etree.Element
-}
diff --git a/vendor/github.com/russellhaering/goxmldsig/keystore.go b/vendor/github.com/russellhaering/goxmldsig/keystore.go
deleted file mode 100644
index 313d9b6a..00000000
--- a/vendor/github.com/russellhaering/goxmldsig/keystore.go
+++ /dev/null
@@ -1,67 +0,0 @@
-package dsig
-
-import (
- "crypto/rand"
- "crypto/rsa"
- "crypto/x509"
- "math/big"
- "time"
-)
-
-type X509KeyStore interface {
- GetKeyPair() (privateKey *rsa.PrivateKey, cert []byte, err error)
-}
-
-type X509ChainStore interface {
- GetChain() (certs [][]byte, err error)
-}
-
-type X509CertificateStore interface {
- Certificates() (roots []*x509.Certificate, err error)
-}
-
-type MemoryX509CertificateStore struct {
- Roots []*x509.Certificate
-}
-
-func (mX509cs *MemoryX509CertificateStore) Certificates() ([]*x509.Certificate, error) {
- return mX509cs.Roots, nil
-}
-
-type MemoryX509KeyStore struct {
- privateKey *rsa.PrivateKey
- cert []byte
-}
-
-func (ks *MemoryX509KeyStore) GetKeyPair() (*rsa.PrivateKey, []byte, error) {
- return ks.privateKey, ks.cert, nil
-}
-
-func RandomKeyStoreForTest() X509KeyStore {
- key, err := rsa.GenerateKey(rand.Reader, 1024)
- if err != nil {
- panic(err)
- }
-
- now := time.Now()
-
- template := &x509.Certificate{
- SerialNumber: big.NewInt(0),
- NotBefore: now.Add(-5 * time.Minute),
- NotAfter: now.Add(365 * 24 * time.Hour),
-
- KeyUsage: x509.KeyUsageDigitalSignature,
- ExtKeyUsage: []x509.ExtKeyUsage{},
- BasicConstraintsValid: true,
- }
-
- cert, err := x509.CreateCertificate(rand.Reader, template, template, &key.PublicKey, key)
- if err != nil {
- panic(err)
- }
-
- return &MemoryX509KeyStore{
- privateKey: key,
- cert: cert,
- }
-}
diff --git a/vendor/github.com/russellhaering/goxmldsig/run_test.sh b/vendor/github.com/russellhaering/goxmldsig/run_test.sh
deleted file mode 100644
index cfe5b2ea..00000000
--- a/vendor/github.com/russellhaering/goxmldsig/run_test.sh
+++ /dev/null
@@ -1,12 +0,0 @@
-#!/bin/bash
-cd `dirname $0`
-DIRS=`git grep -l 'func Test' | xargs dirname | sort -u`
-for DIR in $DIRS
-do
- echo
- echo "dir: $DIR"
- echo "======================================"
- pushd $DIR >/dev/null
- go test -v || exit 1
- popd >/dev/null
-done
diff --git a/vendor/github.com/russellhaering/goxmldsig/sign.go b/vendor/github.com/russellhaering/goxmldsig/sign.go
deleted file mode 100644
index e2c2852e..00000000
--- a/vendor/github.com/russellhaering/goxmldsig/sign.go
+++ /dev/null
@@ -1,256 +0,0 @@
-package dsig
-
-import (
- "crypto"
- "crypto/rand"
- "crypto/rsa"
- _ "crypto/sha1"
- _ "crypto/sha256"
- "encoding/base64"
- "errors"
- "fmt"
-
- "github.com/beevik/etree"
- "github.com/russellhaering/goxmldsig/etreeutils"
-)
-
-type SigningContext struct {
- Hash crypto.Hash
- KeyStore X509KeyStore
- IdAttribute string
- Prefix string
- Canonicalizer Canonicalizer
-}
-
-func NewDefaultSigningContext(ks X509KeyStore) *SigningContext {
- return &SigningContext{
- Hash: crypto.SHA256,
- KeyStore: ks,
- IdAttribute: DefaultIdAttr,
- Prefix: DefaultPrefix,
- Canonicalizer: MakeC14N11Canonicalizer(),
- }
-}
-
-func (ctx *SigningContext) SetSignatureMethod(algorithmID string) error {
- hash, ok := signatureMethodsByIdentifier[algorithmID]
- if !ok {
- return fmt.Errorf("Unknown SignatureMethod: %s", algorithmID)
- }
-
- ctx.Hash = hash
-
- return nil
-}
-
-func (ctx *SigningContext) digest(el *etree.Element) ([]byte, error) {
- canonical, err := ctx.Canonicalizer.Canonicalize(el)
- if err != nil {
- return nil, err
- }
-
- hash := ctx.Hash.New()
- _, err = hash.Write(canonical)
- if err != nil {
- return nil, err
- }
-
- return hash.Sum(nil), nil
-}
-
-func (ctx *SigningContext) constructSignedInfo(el *etree.Element, enveloped bool) (*etree.Element, error) {
- digestAlgorithmIdentifier := ctx.GetDigestAlgorithmIdentifier()
- if digestAlgorithmIdentifier == "" {
- return nil, errors.New("unsupported hash mechanism")
- }
-
- signatureMethodIdentifier := ctx.GetSignatureMethodIdentifier()
- if signatureMethodIdentifier == "" {
- return nil, errors.New("unsupported signature method")
- }
-
- digest, err := ctx.digest(el)
- if err != nil {
- return nil, err
- }
-
- signedInfo := &etree.Element{
- Tag: SignedInfoTag,
- Space: ctx.Prefix,
- }
-
- // /SignedInfo/CanonicalizationMethod
- canonicalizationMethod := ctx.createNamespacedElement(signedInfo, CanonicalizationMethodTag)
- canonicalizationMethod.CreateAttr(AlgorithmAttr, string(ctx.Canonicalizer.Algorithm()))
-
- // /SignedInfo/SignatureMethod
- signatureMethod := ctx.createNamespacedElement(signedInfo, SignatureMethodTag)
- signatureMethod.CreateAttr(AlgorithmAttr, signatureMethodIdentifier)
-
- // /SignedInfo/Reference
- reference := ctx.createNamespacedElement(signedInfo, ReferenceTag)
-
- dataId := el.SelectAttrValue(ctx.IdAttribute, "")
- if dataId == "" {
- return nil, errors.New("Missing data ID")
- }
-
- reference.CreateAttr(URIAttr, "#"+dataId)
-
- // /SignedInfo/Reference/Transforms
- transforms := ctx.createNamespacedElement(reference, TransformsTag)
- if enveloped {
- envelopedTransform := ctx.createNamespacedElement(transforms, TransformTag)
- envelopedTransform.CreateAttr(AlgorithmAttr, EnvelopedSignatureAltorithmId.String())
- }
- canonicalizationAlgorithm := ctx.createNamespacedElement(transforms, TransformTag)
- canonicalizationAlgorithm.CreateAttr(AlgorithmAttr, string(ctx.Canonicalizer.Algorithm()))
-
- // /SignedInfo/Reference/DigestMethod
- digestMethod := ctx.createNamespacedElement(reference, DigestMethodTag)
- digestMethod.CreateAttr(AlgorithmAttr, digestAlgorithmIdentifier)
-
- // /SignedInfo/Reference/DigestValue
- digestValue := ctx.createNamespacedElement(reference, DigestValueTag)
- digestValue.SetText(base64.StdEncoding.EncodeToString(digest))
-
- return signedInfo, nil
-}
-
-func (ctx *SigningContext) ConstructSignature(el *etree.Element, enveloped bool) (*etree.Element, error) {
- signedInfo, err := ctx.constructSignedInfo(el, enveloped)
- if err != nil {
- return nil, err
- }
-
- sig := &etree.Element{
- Tag: SignatureTag,
- Space: ctx.Prefix,
- }
-
- xmlns := "xmlns"
- if ctx.Prefix != "" {
- xmlns += ":" + ctx.Prefix
- }
-
- sig.CreateAttr(xmlns, Namespace)
- sig.AddChild(signedInfo)
-
- // When using xml-c14n11 (ie, non-exclusive canonicalization) the canonical form
- // of the SignedInfo must declare all namespaces that are in scope at it's final
- // enveloped location in the document. In order to do that, we're going to construct
- // a series of cascading NSContexts to capture namespace declarations:
-
- // First get the context surrounding the element we are signing.
- rootNSCtx, err := etreeutils.NSBuildParentContext(el)
- if err != nil {
- return nil, err
- }
-
- // Then capture any declarations on the element itself.
- elNSCtx, err := rootNSCtx.SubContext(el)
- if err != nil {
- return nil, err
- }
-
- // Followed by declarations on the Signature (which we just added above)
- sigNSCtx, err := elNSCtx.SubContext(sig)
- if err != nil {
- return nil, err
- }
-
- // Finally detatch the SignedInfo in order to capture all of the namespace
- // declarations in the scope we've constructed.
- detatchedSignedInfo, err := etreeutils.NSDetatch(sigNSCtx, signedInfo)
- if err != nil {
- return nil, err
- }
-
- digest, err := ctx.digest(detatchedSignedInfo)
- if err != nil {
- return nil, err
- }
-
- key, cert, err := ctx.KeyStore.GetKeyPair()
- if err != nil {
- return nil, err
- }
-
- certs := [][]byte{cert}
- if cs, ok := ctx.KeyStore.(X509ChainStore); ok {
- certs, err = cs.GetChain()
- if err != nil {
- return nil, err
- }
- }
-
- rawSignature, err := rsa.SignPKCS1v15(rand.Reader, key, ctx.Hash, digest)
- if err != nil {
- return nil, err
- }
-
- signatureValue := ctx.createNamespacedElement(sig, SignatureValueTag)
- signatureValue.SetText(base64.StdEncoding.EncodeToString(rawSignature))
-
- keyInfo := ctx.createNamespacedElement(sig, KeyInfoTag)
- x509Data := ctx.createNamespacedElement(keyInfo, X509DataTag)
- for _, cert := range certs {
- x509Certificate := ctx.createNamespacedElement(x509Data, X509CertificateTag)
- x509Certificate.SetText(base64.StdEncoding.EncodeToString(cert))
- }
-
- return sig, nil
-}
-
-func (ctx *SigningContext) createNamespacedElement(el *etree.Element, tag string) *etree.Element {
- child := el.CreateElement(tag)
- child.Space = ctx.Prefix
- return child
-}
-
-func (ctx *SigningContext) SignEnveloped(el *etree.Element) (*etree.Element, error) {
- sig, err := ctx.ConstructSignature(el, true)
- if err != nil {
- return nil, err
- }
-
- ret := el.Copy()
- ret.Child = append(ret.Child, sig)
-
- return ret, nil
-}
-
-func (ctx *SigningContext) GetSignatureMethodIdentifier() string {
- if ident, ok := signatureMethodIdentifiers[ctx.Hash]; ok {
- return ident
- }
- return ""
-}
-
-func (ctx *SigningContext) GetDigestAlgorithmIdentifier() string {
- if ident, ok := digestAlgorithmIdentifiers[ctx.Hash]; ok {
- return ident
- }
- return ""
-}
-
-// Useful for signing query string (including DEFLATED AuthnRequest) when
-// using HTTP-Redirect to make a signed request.
-// See 3.4.4.1 DEFLATE Encoding of https://docs.oasis-open.org/security/saml/v2.0/saml-bindings-2.0-os.pdf
-func (ctx *SigningContext) SignString(content string) ([]byte, error) {
- hash := ctx.Hash.New()
- if ln, err := hash.Write([]byte(content)); err != nil {
- return nil, fmt.Errorf("error calculating hash: %v", err)
- } else if ln < 1 {
- return nil, fmt.Errorf("zero length hash")
- }
- digest := hash.Sum(nil)
-
- var signature []byte
- if key, _, err := ctx.KeyStore.GetKeyPair(); err != nil {
- return nil, fmt.Errorf("unable to fetch key for signing: %v", err)
- } else if signature, err = rsa.SignPKCS1v15(rand.Reader, key, ctx.Hash, digest); err != nil {
- return nil, fmt.Errorf("error signing: %v", err)
- }
- return signature, nil
-}
diff --git a/vendor/github.com/russellhaering/goxmldsig/tls_keystore.go b/vendor/github.com/russellhaering/goxmldsig/tls_keystore.go
deleted file mode 100644
index 79a1a4d8..00000000
--- a/vendor/github.com/russellhaering/goxmldsig/tls_keystore.go
+++ /dev/null
@@ -1,39 +0,0 @@
-package dsig
-
-import (
- "crypto/rsa"
- "crypto/tls"
- "fmt"
-)
-
-//Well-known errors
-var (
- ErrNonRSAKey = fmt.Errorf("Private key was not RSA")
- ErrMissingCertificates = fmt.Errorf("No public certificates provided")
-)
-
-//TLSCertKeyStore wraps the stdlib tls.Certificate to return its contained key
-//and certs.
-type TLSCertKeyStore tls.Certificate
-
-//GetKeyPair implements X509KeyStore using the underlying tls.Certificate
-func (d TLSCertKeyStore) GetKeyPair() (*rsa.PrivateKey, []byte, error) {
- pk, ok := d.PrivateKey.(*rsa.PrivateKey)
-
- if !ok {
- return nil, nil, ErrNonRSAKey
- }
-
- if len(d.Certificate) < 1 {
- return nil, nil, ErrMissingCertificates
- }
-
- crt := d.Certificate[0]
-
- return pk, crt, nil
-}
-
-//GetChain impliments X509ChainStore using the underlying tls.Certificate
-func (d TLSCertKeyStore) GetChain() ([][]byte, error) {
- return d.Certificate, nil
-}
diff --git a/vendor/github.com/russellhaering/goxmldsig/types/signature.go b/vendor/github.com/russellhaering/goxmldsig/types/signature.go
deleted file mode 100644
index 17fd3d72..00000000
--- a/vendor/github.com/russellhaering/goxmldsig/types/signature.go
+++ /dev/null
@@ -1,93 +0,0 @@
-package types
-
-import (
- "encoding/xml"
-
- "github.com/beevik/etree"
-)
-
-type InclusiveNamespaces struct {
- XMLName xml.Name `xml:"http://www.w3.org/2001/10/xml-exc-c14n# InclusiveNamespaces"`
- PrefixList string `xml:"PrefixList,attr"`
-}
-
-type Transform struct {
- XMLName xml.Name `xml:"http://www.w3.org/2000/09/xmldsig# Transform"`
- Algorithm string `xml:"Algorithm,attr"`
- InclusiveNamespaces *InclusiveNamespaces `xml:"InclusiveNamespaces"`
-}
-
-type Transforms struct {
- XMLName xml.Name `xml:"http://www.w3.org/2000/09/xmldsig# Transforms"`
- Transforms []Transform `xml:"Transform"`
-}
-
-type DigestMethod struct {
- XMLName xml.Name `xml:"http://www.w3.org/2000/09/xmldsig# DigestMethod"`
- Algorithm string `xml:"Algorithm,attr"`
-}
-
-type Reference struct {
- XMLName xml.Name `xml:"http://www.w3.org/2000/09/xmldsig# Reference"`
- URI string `xml:"URI,attr"`
- DigestValue string `xml:"DigestValue"`
- DigestAlgo DigestMethod `xml:"DigestMethod"`
- Transforms Transforms `xml:"Transforms"`
-}
-
-type CanonicalizationMethod struct {
- XMLName xml.Name `xml:"http://www.w3.org/2000/09/xmldsig# CanonicalizationMethod"`
- Algorithm string `xml:"Algorithm,attr"`
-}
-
-type SignatureMethod struct {
- XMLName xml.Name `xml:"http://www.w3.org/2000/09/xmldsig# SignatureMethod"`
- Algorithm string `xml:"Algorithm,attr"`
-}
-
-type SignedInfo struct {
- XMLName xml.Name `xml:"http://www.w3.org/2000/09/xmldsig# SignedInfo"`
- CanonicalizationMethod CanonicalizationMethod `xml:"CanonicalizationMethod"`
- SignatureMethod SignatureMethod `xml:"SignatureMethod"`
- References []Reference `xml:"Reference"`
-}
-
-type SignatureValue struct {
- XMLName xml.Name `xml:"http://www.w3.org/2000/09/xmldsig# SignatureValue"`
- Data string `xml:",chardata"`
-}
-
-type KeyInfo struct {
- XMLName xml.Name `xml:"http://www.w3.org/2000/09/xmldsig# KeyInfo"`
- X509Data X509Data `xml:"X509Data"`
-}
-
-type X509Data struct {
- XMLName xml.Name `xml:"http://www.w3.org/2000/09/xmldsig# X509Data"`
- X509Certificates []X509Certificate `xml:"X509Certificate"`
-}
-
-type X509Certificate struct {
- XMLName xml.Name `xml:"http://www.w3.org/2000/09/xmldsig# X509Certificate"`
- Data string `xml:",chardata"`
-}
-
-type Signature struct {
- XMLName xml.Name `xml:"http://www.w3.org/2000/09/xmldsig# Signature"`
- SignedInfo *SignedInfo `xml:"SignedInfo"`
- SignatureValue *SignatureValue `xml:"SignatureValue"`
- KeyInfo *KeyInfo `xml:"KeyInfo"`
- el *etree.Element
-}
-
-// SetUnderlyingElement will be called with a reference to the Element this Signature
-// was unmarshaled from.
-func (s *Signature) SetUnderlyingElement(el *etree.Element) {
- s.el = el
-}
-
-// UnderlyingElement returns a reference to the Element this signature was unmarshaled
-// from, where applicable.
-func (s *Signature) UnderlyingElement() *etree.Element {
- return s.el
-}
diff --git a/vendor/github.com/russellhaering/goxmldsig/validate.go b/vendor/github.com/russellhaering/goxmldsig/validate.go
deleted file mode 100644
index 3fac6991..00000000
--- a/vendor/github.com/russellhaering/goxmldsig/validate.go
+++ /dev/null
@@ -1,490 +0,0 @@
-package dsig
-
-import (
- "bytes"
- "crypto/rsa"
- "crypto/x509"
- "encoding/base64"
- "errors"
- "fmt"
- "regexp"
-
- "github.com/beevik/etree"
- "github.com/russellhaering/goxmldsig/etreeutils"
- "github.com/russellhaering/goxmldsig/types"
-)
-
-var uriRegexp = regexp.MustCompile("^#[a-zA-Z_][\\w.-]*$")
-var whiteSpace = regexp.MustCompile("\\s+")
-
-var (
- // ErrMissingSignature indicates that no enveloped signature was found referencing
- // the top level element passed for signature verification.
- ErrMissingSignature = errors.New("Missing signature referencing the top-level element")
- ErrInvalidSignature = errors.New( "Invalid Signature")
-)
-
-type ValidationContext struct {
- CertificateStore X509CertificateStore
- IdAttribute string
- Clock *Clock
-}
-
-func NewDefaultValidationContext(certificateStore X509CertificateStore) *ValidationContext {
- return &ValidationContext{
- CertificateStore: certificateStore,
- IdAttribute: DefaultIdAttr,
- }
-}
-
-// TODO(russell_h): More flexible namespace support. This might barely work.
-func inNamespace(el *etree.Element, ns string) bool {
- for _, attr := range el.Attr {
- if attr.Value == ns {
- if attr.Space == "" && attr.Key == "xmlns" {
- return el.Space == ""
- } else if attr.Space == "xmlns" {
- return el.Space == attr.Key
- }
- }
- }
-
- return false
-}
-
-func childPath(space, tag string) string {
- if space == "" {
- return "./" + tag
- } else {
- return "./" + space + ":" + tag
- }
-}
-
-func mapPathToElement(tree, el *etree.Element) []int {
- for i, child := range tree.Child {
- if child == el {
- return []int{i}
- }
- }
-
- for i, child := range tree.Child {
- if childElement, ok := child.(*etree.Element); ok {
- childPath := mapPathToElement(childElement, el)
- if childElement != nil {
- return append([]int{i}, childPath...)
- }
- }
- }
-
- return nil
-}
-
-func removeElementAtPath(el *etree.Element, path []int) bool {
- if len(path) == 0 {
- return false
- }
-
- if len(el.Child) <= path[0] {
- return false
- }
-
- childElement, ok := el.Child[path[0]].(*etree.Element)
- if !ok {
- return false
- }
-
- if len(path) == 1 {
- el.RemoveChild(childElement)
- return true
- }
-
- return removeElementAtPath(childElement, path[1:])
-}
-
-// Transform returns a new element equivalent to the passed root el, but with
-// the set of transformations described by the ref applied.
-//
-// The functionality of transform is currently very limited and purpose-specific.
-func (ctx *ValidationContext) transform(
- el *etree.Element,
- sig *types.Signature,
- ref *types.Reference) (*etree.Element, Canonicalizer, error) {
- transforms := ref.Transforms.Transforms
-
- if len(transforms) != 2 {
- return nil, nil, errors.New("Expected Enveloped and C14N transforms")
- }
-
- // map the path to the passed signature relative to the passed root, in
- // order to enable removal of the signature by an enveloped signature
- // transform
- signaturePath := mapPathToElement(el, sig.UnderlyingElement())
-
- // make a copy of the passed root
- el = el.Copy()
-
- var canonicalizer Canonicalizer
-
- for _, transform := range transforms {
- algo := transform.Algorithm
-
- switch AlgorithmID(algo) {
- case EnvelopedSignatureAltorithmId:
- if !removeElementAtPath(el, signaturePath) {
- return nil, nil, errors.New("Error applying canonicalization transform: Signature not found")
- }
-
- case CanonicalXML10ExclusiveAlgorithmId:
- var prefixList string
- if transform.InclusiveNamespaces != nil {
- prefixList = transform.InclusiveNamespaces.PrefixList
- }
-
- canonicalizer = MakeC14N10ExclusiveCanonicalizerWithPrefixList(prefixList)
-
- case CanonicalXML11AlgorithmId:
- canonicalizer = MakeC14N11Canonicalizer()
-
- case CanonicalXML10RecAlgorithmId:
- canonicalizer = MakeC14N10RecCanonicalizer()
-
- case CanonicalXML10CommentAlgorithmId:
- canonicalizer = MakeC14N10CommentCanonicalizer()
-
- default:
- return nil, nil, errors.New("Unknown Transform Algorithm: " + algo)
- }
- }
-
- if canonicalizer == nil {
- return nil, nil, errors.New("Expected canonicalization transform")
- }
-
- return el, canonicalizer, nil
-}
-
-func (ctx *ValidationContext) digest(el *etree.Element, digestAlgorithmId string, canonicalizer Canonicalizer) ([]byte, error) {
- data, err := canonicalizer.Canonicalize(el)
- if err != nil {
- return nil, err
- }
-
- digestAlgorithm, ok := digestAlgorithmsByIdentifier[digestAlgorithmId]
- if !ok {
- return nil, errors.New("Unknown digest algorithm: " + digestAlgorithmId)
- }
-
- hash := digestAlgorithm.New()
- _, err = hash.Write(data)
- if err != nil {
- return nil, err
- }
-
- return hash.Sum(nil), nil
-}
-
-func (ctx *ValidationContext) verifySignedInfo(sig *types.Signature, canonicalizer Canonicalizer, signatureMethodId string, cert *x509.Certificate, decodedSignature []byte) error {
- signatureElement := sig.UnderlyingElement()
-
- nsCtx, err := etreeutils.NSBuildParentContext(signatureElement)
- if err != nil {
- return err
- }
-
- signedInfo, err := etreeutils.NSFindOneChildCtx(nsCtx, signatureElement, Namespace, SignedInfoTag)
- if err != nil {
- return err
- }
-
- if signedInfo == nil {
- return errors.New("Missing SignedInfo")
- }
-
- // Canonicalize the xml
- canonical, err := canonicalSerialize(signedInfo)
- if err != nil {
- return err
- }
-
- signatureAlgorithm, ok := signatureMethodsByIdentifier[signatureMethodId]
- if !ok {
- return errors.New("Unknown signature method: " + signatureMethodId)
- }
-
- hash := signatureAlgorithm.New()
- _, err = hash.Write(canonical)
- if err != nil {
- return err
- }
-
- hashed := hash.Sum(nil)
-
- pubKey, ok := cert.PublicKey.(*rsa.PublicKey)
- if !ok {
- return errors.New("Invalid public key")
- }
-
- // Verify that the private key matching the public key from the cert was what was used to sign the 'SignedInfo' and produce the 'SignatureValue'
- err = rsa.VerifyPKCS1v15(pubKey, signatureAlgorithm, hashed[:], decodedSignature)
- if err != nil {
- return err
- }
-
- return nil
-}
-
-func (ctx *ValidationContext) validateSignature(el *etree.Element, sig *types.Signature, cert *x509.Certificate) (*etree.Element, error) {
- idAttr := el.SelectAttr(ctx.IdAttribute)
- if idAttr == nil || idAttr.Value == "" {
- return nil, errors.New("Missing ID attribute")
- }
-
- var ref *types.Reference
-
- // Find the first reference which references the top-level element
- for _, _ref := range sig.SignedInfo.References {
- if _ref.URI == "" || _ref.URI[1:] == idAttr.Value {
- ref = &_ref
- }
- }
-
- // Perform all transformations listed in the 'SignedInfo'
- // Basically, this means removing the 'SignedInfo'
- transformed, canonicalizer, err := ctx.transform(el, sig, ref)
- if err != nil {
- return nil, err
- }
-
- digestAlgorithm := ref.DigestAlgo.Algorithm
-
- // Digest the transformed XML and compare it to the 'DigestValue' from the 'SignedInfo'
- digest, err := ctx.digest(transformed, digestAlgorithm, canonicalizer)
- if err != nil {
- return nil, err
- }
-
- decodedDigestValue, err := base64.StdEncoding.DecodeString(ref.DigestValue)
- if err != nil {
- return nil, err
- }
-
- if !bytes.Equal(digest, decodedDigestValue) {
- return nil, errors.New("Signature could not be verified")
- }
-
- // Decode the 'SignatureValue' so we can compare against it
- decodedSignature, err := base64.StdEncoding.DecodeString(sig.SignatureValue.Data)
- if err != nil {
- return nil, errors.New("Could not decode signature")
- }
-
- // Actually verify the 'SignedInfo' was signed by a trusted source
- signatureMethod := sig.SignedInfo.SignatureMethod.Algorithm
- err = ctx.verifySignedInfo(sig, canonicalizer, signatureMethod, cert, decodedSignature)
- if err != nil {
- return nil, err
- }
-
- return transformed, nil
-}
-
-func contains(roots []*x509.Certificate, cert *x509.Certificate) bool {
- for _, root := range roots {
- if root.Equal(cert) {
- return true
- }
- }
- return false
-}
-
-// In most places, we use etree Elements, but while deserializing the Signature, we use
-// encoding/xml unmarshal directly to convert to a convenient go struct. This presents a problem in some cases because
-// when an xml element repeats under the parent, the last element will win and/or be appended. We need to assert that
-// the Signature object matches the expected shape of a Signature object.
-func validateShape(signatureEl *etree.Element) error {
- children := signatureEl.ChildElements()
-
- childCounts := map[string]int{}
- for _, child := range children {
- childCounts[child.Tag]++
- }
-
- validateCount := childCounts[SignedInfoTag] == 1 && childCounts[KeyInfoTag] <= 1 && childCounts[SignatureValueTag] == 1
- if !validateCount {
- return ErrInvalidSignature
- }
- return nil
-}
-
-// findSignature searches for a Signature element referencing the passed root element.
-func (ctx *ValidationContext) findSignature(root *etree.Element) (*types.Signature, error) {
- idAttr := root.SelectAttr(ctx.IdAttribute)
- if idAttr == nil || idAttr.Value == "" {
- return nil, errors.New("Missing ID attribute")
- }
-
- var sig *types.Signature
-
- // Traverse the tree looking for a Signature element
- err := etreeutils.NSFindIterate(root, Namespace, SignatureTag, func(ctx etreeutils.NSContext, signatureEl *etree.Element) error {
- err := validateShape(signatureEl)
- if err != nil {
- return err
- }
- found := false
- err = etreeutils.NSFindChildrenIterateCtx(ctx, signatureEl, Namespace, SignedInfoTag,
- func(ctx etreeutils.NSContext, signedInfo *etree.Element) error {
- detachedSignedInfo, err := etreeutils.NSDetatch(ctx, signedInfo)
- if err != nil {
- return err
- }
-
- c14NMethod, err := etreeutils.NSFindOneChildCtx(ctx, detachedSignedInfo, Namespace, CanonicalizationMethodTag)
- if err != nil {
- return err
- }
-
- if c14NMethod == nil {
- return errors.New("missing CanonicalizationMethod on Signature")
- }
-
- c14NAlgorithm := c14NMethod.SelectAttrValue(AlgorithmAttr, "")
-
- var canonicalSignedInfo *etree.Element
-
- switch AlgorithmID(c14NAlgorithm) {
- case CanonicalXML10ExclusiveAlgorithmId:
- err := etreeutils.TransformExcC14n(detachedSignedInfo, "")
- if err != nil {
- return err
- }
-
- // NOTE: TransformExcC14n transforms the element in-place,
- // while canonicalPrep isn't meant to. Once we standardize
- // this behavior we can drop this, as well as the adding and
- // removing of elements below.
- canonicalSignedInfo = detachedSignedInfo
-
- case CanonicalXML11AlgorithmId:
- canonicalSignedInfo = canonicalPrep(detachedSignedInfo, map[string]struct{}{})
-
- case CanonicalXML10RecAlgorithmId:
- canonicalSignedInfo = canonicalPrep(detachedSignedInfo, map[string]struct{}{})
-
- case CanonicalXML10CommentAlgorithmId:
- canonicalSignedInfo = canonicalPrep(detachedSignedInfo, map[string]struct{}{})
-
- default:
- return fmt.Errorf("invalid CanonicalizationMethod on Signature: %s", c14NAlgorithm)
- }
-
- signatureEl.RemoveChild(signedInfo)
- signatureEl.AddChild(canonicalSignedInfo)
-
- found = true
-
- return etreeutils.ErrTraversalHalted
- })
- if err != nil {
- return err
- }
-
- if !found {
- return errors.New("Missing SignedInfo")
- }
-
- // Unmarshal the signature into a structured Signature type
- _sig := &types.Signature{}
- err = etreeutils.NSUnmarshalElement(ctx, signatureEl, _sig)
- if err != nil {
- return err
- }
-
- // Traverse references in the signature to determine whether it has at least
- // one reference to the top level element. If so, conclude the search.
- for _, ref := range _sig.SignedInfo.References {
- if ref.URI == "" || ref.URI[1:] == idAttr.Value {
- sig = _sig
- return etreeutils.ErrTraversalHalted
- }
- }
-
- return nil
- })
-
- if err != nil {
- return nil, err
- }
-
- if sig == nil {
- return nil, ErrMissingSignature
- }
-
- return sig, nil
-}
-
-func (ctx *ValidationContext) verifyCertificate(sig *types.Signature) (*x509.Certificate, error) {
- now := ctx.Clock.Now()
-
- roots, err := ctx.CertificateStore.Certificates()
- if err != nil {
- return nil, err
- }
-
- var cert *x509.Certificate
-
- if sig.KeyInfo != nil {
- // If the Signature includes KeyInfo, extract the certificate from there
- if len(sig.KeyInfo.X509Data.X509Certificates) == 0 || sig.KeyInfo.X509Data.X509Certificates[0].Data == "" {
- return nil, errors.New("missing X509Certificate within KeyInfo")
- }
-
- certData, err := base64.StdEncoding.DecodeString(
- whiteSpace.ReplaceAllString(sig.KeyInfo.X509Data.X509Certificates[0].Data, ""))
- if err != nil {
- return nil, errors.New("Failed to parse certificate")
- }
-
- cert, err = x509.ParseCertificate(certData)
- if err != nil {
- return nil, err
- }
- } else {
- // If the Signature doesn't have KeyInfo, Use the root certificate if there is only one
- if len(roots) == 1 {
- cert = roots[0]
- } else {
- return nil, errors.New("Missing x509 Element")
- }
- }
-
- // Verify that the certificate is one we trust
- if !contains(roots, cert) {
- return nil, errors.New("Could not verify certificate against trusted certs")
- }
-
- if now.Before(cert.NotBefore) || now.After(cert.NotAfter) {
- return nil, errors.New("Cert is not valid at this time")
- }
-
- return cert, nil
-}
-
-// Validate verifies that the passed element contains a valid enveloped signature
-// matching a currently-valid certificate in the context's CertificateStore.
-func (ctx *ValidationContext) Validate(el *etree.Element) (*etree.Element, error) {
- // Make a copy of the element to avoid mutating the one we were passed.
- el = el.Copy()
-
- sig, err := ctx.findSignature(el)
- if err != nil {
- return nil, err
- }
-
- cert, err := ctx.verifyCertificate(sig)
- if err != nil {
- return nil, err
- }
-
- return ctx.validateSignature(el, sig, cert)
-}
diff --git a/vendor/github.com/russellhaering/goxmldsig/xml_constants.go b/vendor/github.com/russellhaering/goxmldsig/xml_constants.go
deleted file mode 100644
index c4b815b2..00000000
--- a/vendor/github.com/russellhaering/goxmldsig/xml_constants.go
+++ /dev/null
@@ -1,81 +0,0 @@
-package dsig
-
-import "crypto"
-
-const (
- DefaultPrefix = "ds"
- Namespace = "http://www.w3.org/2000/09/xmldsig#"
-)
-
-// Tags
-const (
- SignatureTag = "Signature"
- SignedInfoTag = "SignedInfo"
- CanonicalizationMethodTag = "CanonicalizationMethod"
- SignatureMethodTag = "SignatureMethod"
- ReferenceTag = "Reference"
- TransformsTag = "Transforms"
- TransformTag = "Transform"
- DigestMethodTag = "DigestMethod"
- DigestValueTag = "DigestValue"
- SignatureValueTag = "SignatureValue"
- KeyInfoTag = "KeyInfo"
- X509DataTag = "X509Data"
- X509CertificateTag = "X509Certificate"
- InclusiveNamespacesTag = "InclusiveNamespaces"
-)
-
-const (
- AlgorithmAttr = "Algorithm"
- URIAttr = "URI"
- DefaultIdAttr = "ID"
- PrefixListAttr = "PrefixList"
-)
-
-type AlgorithmID string
-
-func (id AlgorithmID) String() string {
- return string(id)
-}
-
-const (
- RSASHA1SignatureMethod = "http://www.w3.org/2000/09/xmldsig#rsa-sha1"
- RSASHA256SignatureMethod = "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"
- RSASHA512SignatureMethod = "http://www.w3.org/2001/04/xmldsig-more#rsa-sha512"
-)
-
-//Well-known signature algorithms
-const (
- // Supported canonicalization algorithms
- CanonicalXML10ExclusiveAlgorithmId AlgorithmID = "http://www.w3.org/2001/10/xml-exc-c14n#"
- CanonicalXML11AlgorithmId AlgorithmID = "http://www.w3.org/2006/12/xml-c14n11"
-
- CanonicalXML10RecAlgorithmId AlgorithmID = "http://www.w3.org/TR/2001/REC-xml-c14n-20010315"
- CanonicalXML10CommentAlgorithmId AlgorithmID = "http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments"
-
- EnvelopedSignatureAltorithmId AlgorithmID = "http://www.w3.org/2000/09/xmldsig#enveloped-signature"
-)
-
-var digestAlgorithmIdentifiers = map[crypto.Hash]string{
- crypto.SHA1: "http://www.w3.org/2000/09/xmldsig#sha1",
- crypto.SHA256: "http://www.w3.org/2001/04/xmlenc#sha256",
- crypto.SHA512: "http://www.w3.org/2001/04/xmlenc#sha512",
-}
-
-var digestAlgorithmsByIdentifier = map[string]crypto.Hash{}
-var signatureMethodsByIdentifier = map[string]crypto.Hash{}
-
-func init() {
- for hash, id := range digestAlgorithmIdentifiers {
- digestAlgorithmsByIdentifier[id] = hash
- }
- for hash, id := range signatureMethodIdentifiers {
- signatureMethodsByIdentifier[id] = hash
- }
-}
-
-var signatureMethodIdentifiers = map[crypto.Hash]string{
- crypto.SHA1: RSASHA1SignatureMethod,
- crypto.SHA256: RSASHA256SignatureMethod,
- crypto.SHA512: RSASHA512SignatureMethod,
-}
diff --git a/vendor/github.com/zenazn/goji/.travis.yml b/vendor/github.com/zenazn/goji/.travis.yml
deleted file mode 100644
index 1db3926c..00000000
--- a/vendor/github.com/zenazn/goji/.travis.yml
+++ /dev/null
@@ -1,17 +0,0 @@
-language: go
-
-go:
- - "1.5.x"
- - "1.6.x"
- - "1.7.x"
- - "1.8.x"
- - "1.9.x"
- - "1.10.x"
- - "1.11.x"
-
-install:
- - go list -f '{{range .Imports}}{{.}} {{end}}' ./... | xargs go get -v
- - go list -f '{{range .TestImports}}{{.}} {{end}}' ./... | xargs go get -v
-
-script: go test -cover ./...
-
diff --git a/vendor/github.com/zenazn/goji/LICENSE b/vendor/github.com/zenazn/goji/LICENSE
deleted file mode 100644
index 446aba06..00000000
--- a/vendor/github.com/zenazn/goji/LICENSE
+++ /dev/null
@@ -1,20 +0,0 @@
-Copyright (c) 2014, 2015, 2016 Carl Jackson (carl@avtok.com)
-
-MIT License
-
-Permission is hereby granted, free of charge, to any person obtaining a copy of
-this software and associated documentation files (the "Software"), to deal in
-the Software without restriction, including without limitation the rights to
-use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
-the Software, and to permit persons to whom the Software is furnished to do so,
-subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
-FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
-COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
-IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
-CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/vendor/github.com/zenazn/goji/README.md b/vendor/github.com/zenazn/goji/README.md
deleted file mode 100644
index 82df6fee..00000000
--- a/vendor/github.com/zenazn/goji/README.md
+++ /dev/null
@@ -1,176 +0,0 @@
-Goji
-====
-
-[](https://godoc.org/github.com/zenazn/goji/web) [](https://travis-ci.org/zenazn/goji)
-
-Goji is a minimalistic web framework that values composability and simplicity.
-
-This project has been superseded by a [new version of Goji][goji2] by the same
-author, which has very similar primitives and semantics, but has been updated to
-reflect several years of experience with this library and the surrounding Go
-ecosystem. This project is still well-loved and well-maintained, and will be for
-the foreseeable future, but new projects are encouraged to use `goji.io`
-instead.
-
-[goji2]: https://goji.io
-
-Example
--------
-
-```go
-package main
-
-import (
- "fmt"
- "net/http"
-
- "github.com/zenazn/goji"
- "github.com/zenazn/goji/web"
-)
-
-func hello(c web.C, w http.ResponseWriter, r *http.Request) {
- fmt.Fprintf(w, "Hello, %s!", c.URLParams["name"])
-}
-
-func main() {
- goji.Get("/hello/:name", hello)
- goji.Serve()
-}
-```
-
-Goji also includes a [sample application][sample] in the `example` folder which
-was artificially constructed to show off all of Goji's features. Check it out!
-
-[sample]: https://github.com/zenazn/goji/tree/master/example
-
-
-Features
---------
-
-* Compatible with `net/http`
-* URL patterns (both Sinatra style `/foo/:bar` patterns and regular expressions,
- as well as [custom patterns][pattern])
-* Reconfigurable middleware stack
-* Context/environment object threaded through middleware and handlers
-* Automatic support for [Einhorn][einhorn], systemd, and [more][bind]
-* [Graceful shutdown][graceful], and zero-downtime graceful reload when combined
- with Einhorn.
-* High in antioxidants
-
-[einhorn]: https://github.com/stripe/einhorn
-[bind]: http://godoc.org/github.com/zenazn/goji/bind
-[graceful]: http://godoc.org/github.com/zenazn/goji/graceful
-[pattern]: https://godoc.org/github.com/zenazn/goji/web#Pattern
-
-
-Stability
----------
-
-Goji's API is essentially frozen, and guarantees to never break compatibility
-with existing code (under similar rules to the Go project's
-[guidelines][compat]). Goji is suitable for use in production, and has served
-billions of requests across several companies.
-
-[compat]: https://golang.org/doc/go1compat
-
-
-Is it any good?
----------------
-
-Maybe!
-
-There are [plenty][revel] of [other][gorilla] [good][pat] [Go][martini]
-[web][gocraft] [frameworks][tiger] out there. Goji is by no means especially
-novel, nor is it uniquely good. The primary difference between Goji and other
-frameworksโand the primary reason I think Goji is any goodโis its philosophy:
-
-Goji first of all attempts to be simple. It is of the Sinatra and Flask school
-of web framework design, and not the Rails/Django one. If you want me to tell
-you what directory you should put your models in, or if you want built-in flash
-sessions, you won't have a good time with Goji.
-
-Secondly, Goji attempts to be composable. It is fully composable with net/http,
-and can be used as a `http.Handler`, or can serve arbitrary `http.Handler`s. At
-least a few HTTP frameworks share this property, and is not particularly novel.
-The more interesting property in my mind is that Goji is fully composable with
-itself: it defines an interface (`web.Handler`) which is both fully compatible
-with `http.Handler` and allows Goji to perform a "protocol upgrade" of sorts
-when it detects that it is talking to itself (or another `web.Handler`
-compatible component). `web.Handler` is at the core of Goji's interfaces and is
-what allows it to share request contexts across unrelated objects.
-
-Third, Goji is not magic. One of my favorite existing frameworks is
-[Martini][martini], but I rejected it in favor of building Goji because I
-thought it was too magical. Goji's web package does not use reflection at all,
-which is not in itself a sign of API quality, but to me at least seems to
-suggest it.
-
-Finally, Goji gives you enough rope to hang yourself with. One of my other
-favorite libraries, [pat][pat], implements Sinatra-like routing in a
-particularly elegant way, but because of its reliance on net/http's interfaces,
-doesn't allow programmers to thread their own state through the request handling
-process. Implementing arbitrary context objects was one of the primary
-motivations behind abandoning pat to write Goji.
-
-[revel]: http://revel.github.io/
-[gorilla]: http://www.gorillatoolkit.org/
-[pat]: https://github.com/bmizerany/pat
-[martini]: http://martini.codegangsta.io/
-[gocraft]: https://github.com/gocraft/web
-[tiger]: https://github.com/rcrowley/go-tigertonic
-
-
-Is it fast?
------------
-
-[Yeah][bench1], [it is][bench2]. Goji is among the fastest HTTP routers out
-there, and is very gentle on the garbage collector.
-
-But that's sort of missing the point. Almost all Go routers are fast enough for
-almost all purposes. In my opinion, what matters more is how simple and flexible
-the routing semantics are.
-
-Goji provides results indistinguishable from naively trying routes one after
-another. This means that a route added before another route will be attempted
-before that route as well. This is perhaps the most simple and most intuitive
-interface a router can provide, and makes routes very easy to understand and
-debug.
-
-Goji's router is also very flexible: in addition to the standard Sinatra-style
-patterns and regular expression patterns, you can define [custom
-patterns][pattern] to perform whatever custom matching logic you desire. Custom
-patterns of course are fully compatible with the routing semantics above.
-
-It's easy (and quite a bit of fun!) to get carried away by microbenchmarks, but
-at the end of the day you're not going to miss those extra hundred nanoseconds
-on a request. What matters is that you aren't compromising on the API for a
-handful of CPU cycles.
-
-[bench1]: https://gist.github.com/zenazn/c5c8528efe1a00634096
-[bench2]: https://github.com/julienschmidt/go-http-routing-benchmark
-
-
-Third-Party Libraries
----------------------
-
-Goji is already compatible with a great many third-party libraries that are
-themselves compatible with `net/http`, however some library authors have gone
-out of their way to include Goji compatibility specifically, perhaps by
-integrating more tightly with Goji's `web.C` or by providing a custom pattern
-type. An informal list of such libraries is maintained [on the wiki][third];
-feel free to add to it as you see fit.
-
-[third]: https://github.com/zenazn/goji/wiki/Third-Party-Libraries
-
-
-Contributing
-------------
-
-Please do! I love pull requests, and I love pull requests that include tests
-even more. Goji's core packages have pretty good code coverage (yay code
-coverage gamification!), and if you have the time to write tests I'd like to
-keep it that way.
-
-In addition to contributing code, I'd love to know what you think about Goji.
-Please open an issue or send me an email with your thoughts; it'd mean a lot to
-me.
diff --git a/vendor/github.com/zenazn/goji/bind/bind.go b/vendor/github.com/zenazn/goji/bind/bind.go
deleted file mode 100644
index 43d11dd3..00000000
--- a/vendor/github.com/zenazn/goji/bind/bind.go
+++ /dev/null
@@ -1,155 +0,0 @@
-/*
-Package bind provides a convenient way to bind to sockets. It exposes a flag in
-the default flag set named "bind" which provides syntax to bind TCP and UNIX
-sockets. It also supports binding to arbitrary file descriptors passed by a
-parent (for instance, systemd), and for binding to Einhorn sockets (including
-Einhorn ACK support).
-
-If the value passed to bind contains a colon, as in ":8000" or "127.0.0.1:9001",
-it will be treated as a TCP address. If it begins with a "/" or a ".", it will
-be treated as a path to a UNIX socket. If it begins with the string "fd@", as in
-"fd@3", it will be treated as a file descriptor (useful for use with systemd,
-for instance). If it begins with the string "einhorn@", as in "einhorn@0", the
-corresponding einhorn socket will be used.
-
-If an option is not explicitly passed, the implementation will automatically
-select between using "einhorn@0", "fd@3", and ":8000", depending on whether
-Einhorn or systemd (or neither) is detected.
-
-This package is a teensy bit magical, and goes out of its way to Do The Right
-Thing in many situations, including in both development and production. If
-you're looking for something less magical, you'd probably be better off just
-calling net.Listen() the old-fashioned way.
-*/
-package bind
-
-import (
- "flag"
- "fmt"
- "log"
- "net"
- "os"
- "strconv"
- "strings"
- "sync"
-)
-
-var bind string
-
-func init() {
- einhornInit()
- systemdInit()
-}
-
-// DefaultBind specifies the fallback used for WithFlag() if it is
-// unable to discover an environment hint (after checking $GOJI_BIND,
-// Einhorn, systemd, and $PORT).
-//
-// If DefaultBind is overridden, it must be set before calling
-// WithFlag().
-var DefaultBind = ":8000"
-
-// WithFlag adds a standard flag to the global flag instance that
-// allows configuration of the default socket. Users who call
-// Default() must call this function before flags are parsed, for
-// example in an init() block.
-//
-// When selecting the default bind string, this function will examine
-// its environment for hints about what port to bind to, selecting the
-// GOJI_BIND environment variable, Einhorn, systemd, the PORT
-// environment variable, and the value of DefaultBind, in order. In
-// most cases, this means that the default behavior of the default
-// socket will be reasonable for use in your circumstance.
-func WithFlag() {
- s := Sniff()
- if s == "" {
- s = DefaultBind
- }
- flag.StringVar(&bind, "bind", s,
- `Address to bind on. If this value has a colon, as in ":8000" or
- "127.0.0.1:9001", it will be treated as a TCP address. If it
- begins with a "/" or a ".", it will be treated as a path to a
- UNIX socket. If it begins with the string "fd@", as in "fd@3",
- it will be treated as a file descriptor (useful for use with
- systemd, for instance). If it begins with the string "einhorn@",
- as in "einhorn@0", the corresponding einhorn socket will be
- used. If an option is not explicitly passed, the implementation
- will automatically select among "einhorn@0" (Einhorn), "fd@3"
- (systemd), and ":8000" (fallback) based on its environment.`)
-}
-
-// Sniff attempts to select a sensible default bind string by examining its
-// environment. It examines the GOJI_BIND environment variable, Einhorn,
-// systemd, and the PORT environment variable, in that order, selecting the
-// first plausible option. It returns the empty string if no sensible default
-// could be extracted from the environment.
-func Sniff() string {
- if bind := os.Getenv("GOJI_BIND"); bind != "" {
- return bind
- } else if usingEinhorn() {
- return "einhorn@0"
- } else if usingSystemd() {
- return "fd@3"
- } else if port := os.Getenv("PORT"); port != "" {
- return ":" + port
- }
- return ""
-}
-
-func listenTo(bind string) (net.Listener, error) {
- if strings.Contains(bind, ":") {
- return net.Listen("tcp", bind)
- } else if strings.HasPrefix(bind, ".") || strings.HasPrefix(bind, "/") {
- return net.Listen("unix", bind)
- } else if strings.HasPrefix(bind, "fd@") {
- fd, err := strconv.Atoi(bind[3:])
- if err != nil {
- return nil, fmt.Errorf("error while parsing fd %v: %v",
- bind, err)
- }
- f := os.NewFile(uintptr(fd), bind)
- defer f.Close()
- return net.FileListener(f)
- } else if strings.HasPrefix(bind, "einhorn@") {
- fd, err := strconv.Atoi(bind[8:])
- if err != nil {
- return nil, fmt.Errorf(
- "error while parsing einhorn %v: %v", bind, err)
- }
- return einhornBind(fd)
- }
-
- return nil, fmt.Errorf("error while parsing bind arg %v", bind)
-}
-
-// Socket parses and binds to the specified address. If Socket encounters an
-// error while parsing or binding to the given socket it will exit by calling
-// log.Fatal.
-func Socket(bind string) net.Listener {
- l, err := listenTo(bind)
- if err != nil {
- log.Fatal(err)
- }
- return l
-}
-
-// Default parses and binds to the default socket as given to us by the flag
-// module. If there was an error parsing or binding to that socket, Default will
-// exit by calling `log.Fatal`.
-func Default() net.Listener {
- return Socket(bind)
-}
-
-// I'm not sure why you'd ever want to call Ready() more than once, but we may
-// as well be safe against it...
-var ready sync.Once
-
-// Ready notifies the environment (for now, just Einhorn) that the process is
-// ready to receive traffic. Should be called at the last possible moment to
-// maximize the chances that a faulty process exits before signaling that it's
-// ready.
-func Ready() {
- ready.Do(func() {
- einhornAck()
- })
-}
diff --git a/vendor/github.com/zenazn/goji/bind/einhorn.go b/vendor/github.com/zenazn/goji/bind/einhorn.go
deleted file mode 100644
index e695c0e9..00000000
--- a/vendor/github.com/zenazn/goji/bind/einhorn.go
+++ /dev/null
@@ -1,91 +0,0 @@
-// +build !windows
-
-package bind
-
-import (
- "fmt"
- "log"
- "net"
- "os"
- "strconv"
- "syscall"
-)
-
-const tooBigErr = "bind: einhorn@%d not found (einhorn only passed %d fds)"
-const bindErr = "bind: could not bind einhorn@%d: not running under einhorn"
-const einhornErr = "bind: einhorn environment initialization error"
-const ackErr = "bind: error ACKing to einhorn: %v"
-
-var einhornNumFds int
-
-func envInt(val string) (int, error) {
- return strconv.Atoi(os.Getenv(val))
-}
-
-// Unfortunately this can't be a normal init function, because their execution
-// order is undefined, and we need to run before the init() in bind.go.
-func einhornInit() {
- mpid, err := envInt("EINHORN_MASTER_PID")
- if err != nil || mpid != os.Getppid() {
- return
- }
-
- einhornNumFds, err = envInt("EINHORN_FD_COUNT")
- if err != nil {
- einhornNumFds = 0
- return
- }
-
- // Prevent einhorn's fds from leaking to our children
- for i := 0; i < einhornNumFds; i++ {
- syscall.CloseOnExec(einhornFdMap(i))
- }
-}
-
-func usingEinhorn() bool {
- return einhornNumFds > 0
-}
-
-func einhornFdMap(n int) int {
- name := fmt.Sprintf("EINHORN_FD_%d", n)
- fno, err := envInt(name)
- if err != nil {
- log.Fatal(einhornErr)
- }
- return fno
-}
-
-func einhornBind(n int) (net.Listener, error) {
- if !usingEinhorn() {
- return nil, fmt.Errorf(bindErr, n)
- }
- if n >= einhornNumFds || n < 0 {
- return nil, fmt.Errorf(tooBigErr, n, einhornNumFds)
- }
-
- fno := einhornFdMap(n)
- f := os.NewFile(uintptr(fno), fmt.Sprintf("einhorn@%d", n))
- defer f.Close()
- return net.FileListener(f)
-}
-
-// Fun story: this is actually YAML, not JSON.
-const ackMsg = `{"command": "worker:ack", "pid": %d}` + "\n"
-
-func einhornAck() {
- if !usingEinhorn() {
- return
- }
- log.Print("bind: ACKing to einhorn")
-
- ctl, err := net.Dial("unix", os.Getenv("EINHORN_SOCK_PATH"))
- if err != nil {
- log.Fatalf(ackErr, err)
- }
- defer ctl.Close()
-
- _, err = fmt.Fprintf(ctl, ackMsg, os.Getpid())
- if err != nil {
- log.Fatalf(ackErr, err)
- }
-}
diff --git a/vendor/github.com/zenazn/goji/bind/einhorn_stub.go b/vendor/github.com/zenazn/goji/bind/einhorn_stub.go
deleted file mode 100644
index 093707fb..00000000
--- a/vendor/github.com/zenazn/goji/bind/einhorn_stub.go
+++ /dev/null
@@ -1,12 +0,0 @@
-// +build windows
-
-package bind
-
-import (
- "net"
-)
-
-func einhornInit() {}
-func einhornAck() {}
-func einhornBind(fd int) (net.Listener, error) { return nil, nil }
-func usingEinhorn() bool { return false }
diff --git a/vendor/github.com/zenazn/goji/bind/systemd.go b/vendor/github.com/zenazn/goji/bind/systemd.go
deleted file mode 100644
index e7cd8e41..00000000
--- a/vendor/github.com/zenazn/goji/bind/systemd.go
+++ /dev/null
@@ -1,36 +0,0 @@
-// +build !windows
-
-package bind
-
-import (
- "os"
- "syscall"
-)
-
-const systemdMinFd = 3
-
-var systemdNumFds int
-
-// Unfortunately this can't be a normal init function, because their execution
-// order is undefined, and we need to run before the init() in bind.go.
-func systemdInit() {
- pid, err := envInt("LISTEN_PID")
- if err != nil || pid != os.Getpid() {
- return
- }
-
- systemdNumFds, err = envInt("LISTEN_FDS")
- if err != nil {
- systemdNumFds = 0
- return
- }
-
- // Prevent fds from leaking to our children
- for i := 0; i < systemdNumFds; i++ {
- syscall.CloseOnExec(systemdMinFd + i)
- }
-}
-
-func usingSystemd() bool {
- return systemdNumFds > 0
-}
diff --git a/vendor/github.com/zenazn/goji/bind/systemd_stub.go b/vendor/github.com/zenazn/goji/bind/systemd_stub.go
deleted file mode 100644
index 4ad4d209..00000000
--- a/vendor/github.com/zenazn/goji/bind/systemd_stub.go
+++ /dev/null
@@ -1,6 +0,0 @@
-// +build windows
-
-package bind
-
-func systemdInit() {}
-func usingSystemd() bool { return false }
diff --git a/vendor/github.com/zenazn/goji/default.go b/vendor/github.com/zenazn/goji/default.go
deleted file mode 100644
index 540e7924..00000000
--- a/vendor/github.com/zenazn/goji/default.go
+++ /dev/null
@@ -1,102 +0,0 @@
-package goji
-
-import (
- "github.com/zenazn/goji/web"
- "github.com/zenazn/goji/web/middleware"
-)
-
-// The default web.Mux.
-var DefaultMux *web.Mux
-
-func init() {
- DefaultMux = web.New()
-
- DefaultMux.Use(middleware.RequestID)
- DefaultMux.Use(middleware.Logger)
- DefaultMux.Use(middleware.Recoverer)
- DefaultMux.Use(middleware.AutomaticOptions)
-}
-
-// Use appends the given middleware to the default Mux's middleware stack. See
-// the documentation for web.Mux.Use for more information.
-func Use(middleware web.MiddlewareType) {
- DefaultMux.Use(middleware)
-}
-
-// Insert the given middleware into the default Mux's middleware stack. See the
-// documentation for web.Mux.Insert for more information.
-func Insert(middleware, before web.MiddlewareType) error {
- return DefaultMux.Insert(middleware, before)
-}
-
-// Abandon removes the given middleware from the default Mux's middleware stack.
-// See the documentation for web.Mux.Abandon for more information.
-func Abandon(middleware web.MiddlewareType) error {
- return DefaultMux.Abandon(middleware)
-}
-
-// Handle adds a route to the default Mux. See the documentation for web.Mux for
-// more information about what types this function accepts.
-func Handle(pattern web.PatternType, handler web.HandlerType) {
- DefaultMux.Handle(pattern, handler)
-}
-
-// Connect adds a CONNECT route to the default Mux. See the documentation for
-// web.Mux for more information about what types this function accepts.
-func Connect(pattern web.PatternType, handler web.HandlerType) {
- DefaultMux.Connect(pattern, handler)
-}
-
-// Delete adds a DELETE route to the default Mux. See the documentation for
-// web.Mux for more information about what types this function accepts.
-func Delete(pattern web.PatternType, handler web.HandlerType) {
- DefaultMux.Delete(pattern, handler)
-}
-
-// Get adds a GET route to the default Mux. See the documentation for web.Mux for
-// more information about what types this function accepts.
-func Get(pattern web.PatternType, handler web.HandlerType) {
- DefaultMux.Get(pattern, handler)
-}
-
-// Head adds a HEAD route to the default Mux. See the documentation for web.Mux
-// for more information about what types this function accepts.
-func Head(pattern web.PatternType, handler web.HandlerType) {
- DefaultMux.Head(pattern, handler)
-}
-
-// Options adds a OPTIONS route to the default Mux. See the documentation for
-// web.Mux for more information about what types this function accepts.
-func Options(pattern web.PatternType, handler web.HandlerType) {
- DefaultMux.Options(pattern, handler)
-}
-
-// Patch adds a PATCH route to the default Mux. See the documentation for web.Mux
-// for more information about what types this function accepts.
-func Patch(pattern web.PatternType, handler web.HandlerType) {
- DefaultMux.Patch(pattern, handler)
-}
-
-// Post adds a POST route to the default Mux. See the documentation for web.Mux
-// for more information about what types this function accepts.
-func Post(pattern web.PatternType, handler web.HandlerType) {
- DefaultMux.Post(pattern, handler)
-}
-
-// Put adds a PUT route to the default Mux. See the documentation for web.Mux for
-// more information about what types this function accepts.
-func Put(pattern web.PatternType, handler web.HandlerType) {
- DefaultMux.Put(pattern, handler)
-}
-
-// Trace adds a TRACE route to the default Mux. See the documentation for
-// web.Mux for more information about what types this function accepts.
-func Trace(pattern web.PatternType, handler web.HandlerType) {
- DefaultMux.Trace(pattern, handler)
-}
-
-// NotFound sets the NotFound handler for the default Mux. See the documentation
-// for web.Mux.NotFound for more information.
-func NotFound(handler web.HandlerType) {
- DefaultMux.NotFound(handler)
-}
diff --git a/vendor/github.com/zenazn/goji/goji.go b/vendor/github.com/zenazn/goji/goji.go
deleted file mode 100644
index ab278cd1..00000000
--- a/vendor/github.com/zenazn/goji/goji.go
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
-Package goji provides an out-of-box web server with reasonable defaults.
-
-Example:
- package main
-
- import (
- "fmt"
- "net/http"
-
- "github.com/zenazn/goji"
- "github.com/zenazn/goji/web"
- )
-
- func hello(c web.C, w http.ResponseWriter, r *http.Request) {
- fmt.Fprintf(w, "Hello, %s!", c.URLParams["name"])
- }
-
- func main() {
- goji.Get("/hello/:name", hello)
- goji.Serve()
- }
-
-This package exists purely as a convenience to programmers who want to get
-started as quickly as possible. It draws almost all of its code from goji's
-subpackages, the most interesting of which is goji/web, and where most of the
-documentation for the web framework lives.
-
-A side effect of this package's ease-of-use is the fact that it is opinionated.
-If you don't like (or have outgrown) its opinions, it should be straightforward
-to use the APIs of goji's subpackages to reimplement things to your liking. Both
-methods of using this library are equally well supported.
-
-Goji requires Go 1.2 or newer.
-*/
-package goji
diff --git a/vendor/github.com/zenazn/goji/graceful/clone.go b/vendor/github.com/zenazn/goji/graceful/clone.go
deleted file mode 100644
index a9027e57..00000000
--- a/vendor/github.com/zenazn/goji/graceful/clone.go
+++ /dev/null
@@ -1,11 +0,0 @@
-// +build !go1.6
-
-package graceful
-
-import "crypto/tls"
-
-// see clone16.go
-func cloneTLSConfig(cfg *tls.Config) *tls.Config {
- c := *cfg
- return &c
-}
diff --git a/vendor/github.com/zenazn/goji/graceful/clone16.go b/vendor/github.com/zenazn/goji/graceful/clone16.go
deleted file mode 100644
index 810b3a2c..00000000
--- a/vendor/github.com/zenazn/goji/graceful/clone16.go
+++ /dev/null
@@ -1,34 +0,0 @@
-// +build go1.6
-
-package graceful
-
-import "crypto/tls"
-
-// cloneTLSConfig was taken from the Go standard library's net/http package. We
-// need it because tls.Config objects now contain a sync.Once.
-func cloneTLSConfig(cfg *tls.Config) *tls.Config {
- if cfg == nil {
- return &tls.Config{}
- }
- return &tls.Config{
- Rand: cfg.Rand,
- Time: cfg.Time,
- Certificates: cfg.Certificates,
- NameToCertificate: cfg.NameToCertificate,
- GetCertificate: cfg.GetCertificate,
- RootCAs: cfg.RootCAs,
- NextProtos: cfg.NextProtos,
- ServerName: cfg.ServerName,
- ClientAuth: cfg.ClientAuth,
- ClientCAs: cfg.ClientCAs,
- InsecureSkipVerify: cfg.InsecureSkipVerify,
- CipherSuites: cfg.CipherSuites,
- PreferServerCipherSuites: cfg.PreferServerCipherSuites,
- SessionTicketsDisabled: cfg.SessionTicketsDisabled,
- SessionTicketKey: cfg.SessionTicketKey,
- ClientSessionCache: cfg.ClientSessionCache,
- MinVersion: cfg.MinVersion,
- MaxVersion: cfg.MaxVersion,
- CurvePreferences: cfg.CurvePreferences,
- }
-}
diff --git a/vendor/github.com/zenazn/goji/graceful/einhorn.go b/vendor/github.com/zenazn/goji/graceful/einhorn.go
deleted file mode 100644
index 082d1c48..00000000
--- a/vendor/github.com/zenazn/goji/graceful/einhorn.go
+++ /dev/null
@@ -1,21 +0,0 @@
-// +build !windows
-
-package graceful
-
-import (
- "os"
- "strconv"
- "syscall"
-)
-
-func init() {
- // This is a little unfortunate: goji/bind already knows whether we're
- // running under einhorn, but we don't want to introduce a dependency
- // between the two packages. Since the check is short enough, inlining
- // it here seems "fine."
- mpid, err := strconv.Atoi(os.Getenv("EINHORN_MASTER_PID"))
- if err != nil || mpid != os.Getppid() {
- return
- }
- stdSignals = append(stdSignals, syscall.SIGUSR2)
-}
diff --git a/vendor/github.com/zenazn/goji/graceful/graceful.go b/vendor/github.com/zenazn/goji/graceful/graceful.go
deleted file mode 100644
index 19490e05..00000000
--- a/vendor/github.com/zenazn/goji/graceful/graceful.go
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
-Package graceful implements graceful shutdown for HTTP servers by closing idle
-connections after receiving a signal. By default, this package listens for
-interrupts (i.e., SIGINT), but when it detects that it is running under Einhorn
-it will additionally listen for SIGUSR2 as well, giving your application
-automatic support for graceful restarts/code upgrades.
-*/
-package graceful
-
-import (
- "net"
- "sync/atomic"
-
- "github.com/zenazn/goji/graceful/listener"
-)
-
-// WrapListener wraps an arbitrary net.Listener for use with graceful shutdowns.
-// In the background, it uses the listener sub-package to Wrap the listener in
-// Deadline mode. If another mode of operation is desired, you should call
-// listener.Wrap yourself: this function is smart enough to not double-wrap
-// listeners.
-func WrapListener(l net.Listener) net.Listener {
- if lt, ok := l.(*listener.T); ok {
- appendListener(lt)
- return lt
- }
-
- lt := listener.Wrap(l, listener.Deadline)
- appendListener(lt)
- return lt
-}
-
-func appendListener(l *listener.T) {
- mu.Lock()
- defer mu.Unlock()
-
- listeners = append(listeners, l)
-}
-
-const errClosing = "use of closed network connection"
-
-// During graceful shutdown, calls to Accept will start returning errors. This
-// is inconvenient, since we know these sorts of errors are peaceful, so we
-// silently swallow them.
-func peacefulError(err error) error {
- if atomic.LoadInt32(&closing) == 0 {
- return err
- }
- // Unfortunately Go doesn't really give us a better way to select errors
- // than this, so *shrug*.
- if oe, ok := err.(*net.OpError); ok {
- switch oe.Op {
- // backward compatibility: older golang returns AcceptEx on Windows.
- // Current golang returns "accept" consistently. It's platform independent.
- // See https://github.com/golang/go/commit/b0f4ee533a875c258ac1030ee382f0ffe2de304b
- case "AcceptEx":
- fallthrough
- case "accept":
- if oe.Err.Error() == errClosing {
- return nil
- }
- }
- }
- return err
-}
diff --git a/vendor/github.com/zenazn/goji/graceful/listener/conn.go b/vendor/github.com/zenazn/goji/graceful/listener/conn.go
deleted file mode 100644
index 7b34b477..00000000
--- a/vendor/github.com/zenazn/goji/graceful/listener/conn.go
+++ /dev/null
@@ -1,151 +0,0 @@
-package listener
-
-import (
- "errors"
- "io"
- "net"
- "sync"
- "time"
-)
-
-type conn struct {
- net.Conn
-
- shard *shard
- mode mode
-
- mu sync.Mutex // Protects the state machine below
- busy bool // connection is in use (i.e., not idle)
- closed bool // connection is closed
- disowned bool // if true, this connection is no longer under our management
-}
-
-// This intentionally looks a lot like the one in package net.
-var errClosing = errors.New("use of closed network connection")
-
-func (c *conn) init() error {
- c.shard.wg.Add(1)
- if shouldExit := c.shard.track(c); shouldExit {
- c.Close()
- return errClosing
- }
- return nil
-}
-
-func (c *conn) Read(b []byte) (n int, err error) {
- defer func() {
- c.mu.Lock()
- defer c.mu.Unlock()
-
- if c.disowned {
- return
- }
-
- // This protects against a Close/Read race. We're not really
- // concerned about the general case (it's fundamentally racy),
- // but are mostly trying to prevent a race between a new request
- // getting read off the wire in one thread while the connection
- // is being gracefully shut down in another.
- if c.closed && err == nil {
- n = 0
- err = errClosing
- return
- }
-
- if c.mode != Manual && !c.busy && !c.closed {
- c.busy = true
- c.shard.markInUse(c)
- }
- }()
-
- return c.Conn.Read(b)
-}
-
-func (c *conn) Close() error {
- c.mu.Lock()
- defer c.mu.Unlock()
-
- if c.disowned {
- return c.Conn.Close()
- } else if c.closed {
- return errClosing
- }
-
- c.closed = true
- c.shard.disown(c)
- defer c.shard.wg.Done()
-
- return c.Conn.Close()
-}
-
-func (c *conn) SetReadDeadline(t time.Time) error {
- c.mu.Lock()
- if !c.disowned && c.mode == Deadline {
- defer c.markIdle()
- }
- c.mu.Unlock()
- return c.Conn.SetReadDeadline(t)
-}
-
-func (c *conn) ReadFrom(r io.Reader) (int64, error) {
- return io.Copy(c.Conn, r)
-}
-
-func (c *conn) markIdle() {
- c.mu.Lock()
- defer c.mu.Unlock()
-
- if !c.busy {
- return
- }
- c.busy = false
-
- if exit := c.shard.markIdle(c); exit && !c.closed && !c.disowned {
- c.closed = true
- c.shard.disown(c)
- defer c.shard.wg.Done()
- c.Conn.Close()
- return
- }
-}
-
-func (c *conn) markInUse() {
- c.mu.Lock()
- defer c.mu.Unlock()
-
- if !c.busy && !c.closed && !c.disowned {
- c.busy = true
- c.shard.markInUse(c)
- }
-}
-
-func (c *conn) closeIfIdle() error {
- c.mu.Lock()
- defer c.mu.Unlock()
-
- if !c.busy && !c.closed && !c.disowned {
- c.closed = true
- c.shard.disown(c)
- defer c.shard.wg.Done()
- return c.Conn.Close()
- }
-
- return nil
-}
-
-var errAlreadyDisowned = errors.New("listener: conn already disowned")
-
-func (c *conn) disown() error {
- c.mu.Lock()
- defer c.mu.Unlock()
-
- if c.disowned {
- return errAlreadyDisowned
- }
-
- c.shard.disown(c)
- c.disowned = true
- c.shard.wg.Done()
-
- return nil
-}
diff --git a/vendor/github.com/zenazn/goji/graceful/listener/listener.go b/vendor/github.com/zenazn/goji/graceful/listener/listener.go
deleted file mode 100644
index 6c9c477f..00000000
--- a/vendor/github.com/zenazn/goji/graceful/listener/listener.go
+++ /dev/null
@@ -1,178 +0,0 @@
-/*
-Package listener provides a way to incorporate graceful shutdown to any
-net.Listener.
-
-This package provides low-level primitives, not a high-level API. If you're
-looking for a package that provides graceful shutdown for HTTP servers, I
-recommend this package's parent package, github.com/zenazn/goji/graceful.
-*/
-package listener
-
-import (
- "errors"
- "net"
- "runtime"
- "sync"
- "sync/atomic"
-)
-
-type mode int8
-
-const (
- // Manual mode is completely manual: users must use use MarkIdle and
- // MarkInUse to indicate when connections are busy servicing requests or
- // are eligible for termination.
- Manual mode = iota
- // Automatic mode is what most users probably want: calling Read on a
- // connection will mark it as in use, but users must manually call
- // MarkIdle to indicate when connections may be safely closed.
- Automatic
- // Deadline mode is like automatic mode, except that calling
- // SetReadDeadline on a connection will also mark it as being idle. This
- // is useful for many servers like net/http, where SetReadDeadline is
- // used to implement read timeouts on new requests.
- Deadline
-)
-
-// Wrap a net.Listener, returning a net.Listener which supports idle connection
-// tracking and shutdown. Listeners can be placed in to one of three modes,
-// exported as variables from this package: most users will probably want the
-// "Automatic" mode.
-func Wrap(l net.Listener, m mode) *T {
- t := &T{
- l: l,
- mode: m,
- // To keep the expected contention rate constant we'd have to
- // grow this as numcpu**2. In practice, CPU counts don't
- // generally grow without bound, and contention is probably
- // going to be small enough that nobody cares anyways.
- shards: make([]shard, 2*runtime.NumCPU()),
- }
- for i := range t.shards {
- t.shards[i].init(t)
- }
- return t
-}
-
-// T is the type of this package's graceful listeners.
-type T struct {
- mu sync.Mutex
- l net.Listener
-
- // TODO(carl): a count of currently outstanding connections.
- connCount uint64
- shards []shard
-
- mode mode
-}
-
-var _ net.Listener = &T{}
-
-// Accept waits for and returns the next connection to the listener. The
-// returned net.Conn's idleness is tracked, and idle connections can be closed
-// from the associated T.
-func (t *T) Accept() (net.Conn, error) {
- c, err := t.l.Accept()
- if err != nil {
- return nil, err
- }
-
- connID := atomic.AddUint64(&t.connCount, 1)
- shard := &t.shards[int(connID)%len(t.shards)]
- wc := &conn{
- Conn: c,
- shard: shard,
- mode: t.mode,
- }
-
- if err = wc.init(); err != nil {
- return nil, err
- }
- return wc, nil
-}
-
-// Addr returns the wrapped listener's network address.
-func (t *T) Addr() net.Addr {
- return t.l.Addr()
-}
-
-// Close closes the wrapped listener.
-func (t *T) Close() error {
- return t.l.Close()
-}
-
-// CloseIdle closes all connections that are currently marked as being idle. It,
-// however, makes no attempt to wait for in-use connections to die, or to close
-// connections which become idle in the future. Call this function if you're
-// interested in shedding useless connections, but otherwise wish to continue
-// serving requests.
-func (t *T) CloseIdle() error {
- for i := range t.shards {
- t.shards[i].closeConns(false, false)
- }
- // Not sure if returning errors is actually useful here :/
- return nil
-}
-
-// Drain immediately closes all idle connections, prevents new connections from
-// being accepted, and waits for all outstanding connections to finish.
-//
-// Once a listener has been drained, there is no way to re-enable it. You
-// probably want to Close the listener before draining it, otherwise new
-// connections will be accepted and immediately closed.
-func (t *T) Drain() error {
- for i := range t.shards {
- t.shards[i].closeConns(false, true)
- }
- for i := range t.shards {
- t.shards[i].wait()
- }
- return nil
-}
-
-// DrainAll closes all connections currently tracked by this listener (both idle
-// and in-use connections), and prevents new connections from being accepted.
-// Disowned connections are not closed.
-func (t *T) DrainAll() error {
- for i := range t.shards {
- t.shards[i].closeConns(true, true)
- }
- for i := range t.shards {
- t.shards[i].wait()
- }
- return nil
-}
-
-var errNotManaged = errors.New("listener: passed net.Conn is not managed by this package")
-
-// Disown causes a connection to no longer be tracked by the listener. The
-// passed connection must have been returned by a call to Accept from this
-// listener.
-func Disown(c net.Conn) error {
- if cn, ok := c.(*conn); ok {
- return cn.disown()
- }
- return errNotManaged
-}
-
-// MarkIdle marks the given connection as being idle, and therefore eligible for
-// closing at any time. The passed connection must have been returned by a call
-// to Accept from this listener.
-func MarkIdle(c net.Conn) error {
- if cn, ok := c.(*conn); ok {
- cn.markIdle()
- return nil
- }
- return errNotManaged
-}
-
-// MarkInUse marks this connection as being in use, removing it from the set of
-// connections which are eligible for closing. The passed connection must have
-// been returned by a call to Accept from this listener.
-func MarkInUse(c net.Conn) error {
- if cn, ok := c.(*conn); ok {
- cn.markInUse()
- return nil
- }
- return errNotManaged
-}
diff --git a/vendor/github.com/zenazn/goji/graceful/listener/shard.go b/vendor/github.com/zenazn/goji/graceful/listener/shard.go
deleted file mode 100644
index a9addad1..00000000
--- a/vendor/github.com/zenazn/goji/graceful/listener/shard.go
+++ /dev/null
@@ -1,98 +0,0 @@
-package listener
-
-import "sync"
-
-type shard struct {
- l *T
-
- mu sync.Mutex
- idle map[*conn]struct{}
- all map[*conn]struct{}
- wg sync.WaitGroup
- drain bool
-}
-
-// We pretty aggressively preallocate set entries in the hopes that we never
-// have to allocate memory with the lock held. This is definitely a premature
-// optimization and is probably misguided, but luckily it costs us essentially
-// nothing.
-const prealloc = 2048
-
-func (s *shard) init(l *T) {
- s.l = l
- s.idle = make(map[*conn]struct{}, prealloc)
- s.all = make(map[*conn]struct{}, prealloc)
-}
-
-func (s *shard) track(c *conn) (shouldClose bool) {
- s.mu.Lock()
- if s.drain {
- s.mu.Unlock()
- return true
- }
- s.all[c] = struct{}{}
- s.idle[c] = struct{}{}
- s.mu.Unlock()
- return false
-}
-
-func (s *shard) disown(c *conn) {
- s.mu.Lock()
- delete(s.all, c)
- delete(s.idle, c)
- s.mu.Unlock()
-}
-
-func (s *shard) markIdle(c *conn) (shouldClose bool) {
- s.mu.Lock()
- if s.drain {
- s.mu.Unlock()
- return true
- }
- s.idle[c] = struct{}{}
- s.mu.Unlock()
- return false
-}
-
-func (s *shard) markInUse(c *conn) {
- s.mu.Lock()
- delete(s.idle, c)
- s.mu.Unlock()
-}
-
-func (s *shard) closeConns(all, drain bool) {
- s.mu.Lock()
- if drain {
- s.drain = true
- }
- set := make(map[*conn]struct{}, len(s.all))
- if all {
- for c := range s.all {
- set[c] = struct{}{}
- }
- } else {
- for c := range s.idle {
- set[c] = struct{}{}
- }
- }
- // We have to drop the shard lock here to avoid deadlock: we cannot
- // acquire the shard lock after the connection lock, and the closeIfIdle
- // call below will grab a connection lock.
- s.mu.Unlock()
-
- for c := range set {
- // This might return an error (from Close), but I don't think we
- // can do anything about it, so let's just pretend it didn't
- // happen. (I also expect that most errors returned in this way
- // are going to be pretty boring)
- if all {
- c.Close()
- } else {
- c.closeIfIdle()
- }
- }
-}
-
-func (s *shard) wait() {
- s.wg.Wait()
-}
diff --git a/vendor/github.com/zenazn/goji/graceful/middleware.go b/vendor/github.com/zenazn/goji/graceful/middleware.go
deleted file mode 100644
index 94edfe31..00000000
--- a/vendor/github.com/zenazn/goji/graceful/middleware.go
+++ /dev/null
@@ -1,103 +0,0 @@
-// +build !go1.3
-
-package graceful
-
-import (
- "bufio"
- "io"
- "net"
- "net/http"
- "sync/atomic"
-
- "github.com/zenazn/goji/graceful/listener"
-)
-
-// Middleware provides functionality similar to net/http.Server's
-// SetKeepAlivesEnabled in Go 1.3, but in Go 1.2.
-func middleware(h http.Handler) http.Handler {
- if h == nil {
- return nil
- }
- return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- _, cn := w.(http.CloseNotifier)
- _, fl := w.(http.Flusher)
- _, hj := w.(http.Hijacker)
- _, rf := w.(io.ReaderFrom)
-
- bw := basicWriter{ResponseWriter: w}
-
- if cn && fl && hj && rf {
- h.ServeHTTP(&fancyWriter{bw}, r)
- } else {
- h.ServeHTTP(&bw, r)
- }
- if !bw.headerWritten {
- bw.maybeClose()
- }
- })
-}
-
-type basicWriter struct {
- http.ResponseWriter
- headerWritten bool
-}
-
-func (b *basicWriter) maybeClose() {
- b.headerWritten = true
- if atomic.LoadInt32(&closing) != 0 {
- b.ResponseWriter.Header().Set("Connection", "close")
- }
-}
-
-func (b *basicWriter) WriteHeader(code int) {
- b.maybeClose()
- b.ResponseWriter.WriteHeader(code)
-}
-
-func (b *basicWriter) Write(buf []byte) (int, error) {
- if !b.headerWritten {
- b.maybeClose()
- }
- return b.ResponseWriter.Write(buf)
-}
-
-func (b *basicWriter) Unwrap() http.ResponseWriter {
- return b.ResponseWriter
-}
-
-// Optimize for the common case of a ResponseWriter that supports all three of
-// CloseNotifier, Flusher, and Hijacker.
-type fancyWriter struct {
- basicWriter
-}
-
-func (f *fancyWriter) CloseNotify() <-chan bool {
- cn := f.basicWriter.ResponseWriter.(http.CloseNotifier)
- return cn.CloseNotify()
-}
-func (f *fancyWriter) Flush() {
- fl := f.basicWriter.ResponseWriter.(http.Flusher)
- fl.Flush()
-}
-func (f *fancyWriter) Hijack() (c net.Conn, b *bufio.ReadWriter, e error) {
- hj := f.basicWriter.ResponseWriter.(http.Hijacker)
- c, b, e = hj.Hijack()
-
- if e == nil {
- e = listener.Disown(c)
- }
-
- return
-}
-func (f *fancyWriter) ReadFrom(r io.Reader) (int64, error) {
- rf := f.basicWriter.ResponseWriter.(io.ReaderFrom)
- if !f.basicWriter.headerWritten {
- f.basicWriter.maybeClose()
- }
- return rf.ReadFrom(r)
-}
-
-var _ http.CloseNotifier = &fancyWriter{}
-var _ http.Flusher = &fancyWriter{}
-var _ http.Hijacker = &fancyWriter{}
-var _ io.ReaderFrom = &fancyWriter{}
diff --git a/vendor/github.com/zenazn/goji/graceful/serve.go b/vendor/github.com/zenazn/goji/graceful/serve.go
deleted file mode 100644
index edb2a536..00000000
--- a/vendor/github.com/zenazn/goji/graceful/serve.go
+++ /dev/null
@@ -1,33 +0,0 @@
-// +build !go1.3
-
-package graceful
-
-import (
- "net"
- "net/http"
- "time"
-
- "github.com/zenazn/goji/graceful/listener"
-)
-
-// About 200 years, also known as "forever"
-const forever time.Duration = 200 * 365 * 24 * time.Hour
-
-// Serve behaves like the method on net/http.Server with the same name.
-func (srv *Server) Serve(l net.Listener) error {
- // Spawn a shadow http.Server to do the actual servering. We do this
- // because we need to sketch on some of the parameters you passed in,
- // and it's nice to keep our sketching to ourselves.
- shadow := *(*http.Server)(srv)
-
- if shadow.ReadTimeout == 0 {
- shadow.ReadTimeout = forever
- }
- shadow.Handler = middleware(shadow.Handler)
-
- wrap := listener.Wrap(l, listener.Deadline)
- appendListener(wrap)
-
- err := shadow.Serve(wrap)
- return peacefulError(err)
-}
diff --git a/vendor/github.com/zenazn/goji/graceful/serve13.go b/vendor/github.com/zenazn/goji/graceful/serve13.go
deleted file mode 100644
index 68cac049..00000000
--- a/vendor/github.com/zenazn/goji/graceful/serve13.go
+++ /dev/null
@@ -1,76 +0,0 @@
-// +build go1.3
-
-package graceful
-
-import (
- "log"
- "net"
- "net/http"
-
- "github.com/zenazn/goji/graceful/listener"
-)
-
-// This is a slightly hacky shim to disable keepalives when shutting a server
-// down. We could have added extra functionality in listener or signal.go to
-// deal with this case, but this seems simpler.
-type gracefulServer struct {
- net.Listener
- s *http.Server
-}
-
-func (g gracefulServer) Close() error {
- g.s.SetKeepAlivesEnabled(false)
- return g.Listener.Close()
-}
-
-// A chaining http.ConnState wrapper
-type connState func(net.Conn, http.ConnState)
-
-func (c connState) Wrap(nc net.Conn, s http.ConnState) {
- // There are a few other states defined, most notably StateActive.
- // Unfortunately it doesn't look like it's possible to make use of
- // StateActive to implement graceful shutdown, since StateActive is set
- // after a complete request has been read off the wire with an intent to
- // process it. If we were to race a graceful shutdown against a
- // connection that was just read off the wire (but not yet in
- // StateActive), we would accidentally close the connection out from
- // underneath an active request.
- //
- // We already needed to work around this for Go 1.2 by shimming out a
- // full net.Conn object, so we can just fall back to the old behavior
- // there.
- //
- // I started a golang-nuts thread about this here:
- // https://groups.google.com/forum/#!topic/golang-nuts/Xi8yjBGWfCQ
- // I'd be very eager to find a better way to do this, so reach out to me
- // if you have any ideas.
- switch s {
- case http.StateIdle:
- if err := listener.MarkIdle(nc); err != nil {
- log.Printf("error marking conn as idle: %v", err)
- }
- case http.StateHijacked:
- if err := listener.Disown(nc); err != nil {
- log.Printf("error disowning hijacked conn: %v", err)
- }
- }
- if c != nil {
- c(nc, s)
- }
-}
-
-// Serve behaves like the method on net/http.Server with the same name.
-func (srv *Server) Serve(l net.Listener) error {
- // Spawn a shadow http.Server to do the actual servering. We do this
- // because we need to sketch on some of the parameters you passed in,
- // and it's nice to keep our sketching to ourselves.
- shadow := *(*http.Server)(srv)
- shadow.ConnState = connState(shadow.ConnState).Wrap
-
- l = gracefulServer{l, &shadow}
- wrap := listener.Wrap(l, listener.Automatic)
- appendListener(wrap)
-
- err := shadow.Serve(wrap)
- return peacefulError(err)
-}
diff --git a/vendor/github.com/zenazn/goji/graceful/server.go b/vendor/github.com/zenazn/goji/graceful/server.go
deleted file mode 100644
index ae9a5fbf..00000000
--- a/vendor/github.com/zenazn/goji/graceful/server.go
+++ /dev/null
@@ -1,108 +0,0 @@
-package graceful
-
-import (
- "crypto/tls"
- "net"
- "net/http"
- "time"
-)
-
-// Most of the code here is lifted straight from net/http
-
-// tcpKeepAliveListener sets TCP keep-alive timeouts on accepted
-// connections. It's used by ListenAndServe and ListenAndServeTLS so
-// dead TCP connections (e.g. closing laptop mid-download) eventually
-// go away.
-type tcpKeepAliveListener struct {
- *net.TCPListener
-}
-
-func (ln tcpKeepAliveListener) Accept() (c net.Conn, err error) {
- tc, err := ln.AcceptTCP()
- if err != nil {
- return
- }
- tc.SetKeepAlive(true)
- tc.SetKeepAlivePeriod(3 * time.Minute)
- return tc, nil
-}
-
-// A Server is exactly the same as an http.Server, but provides more graceful
-// implementations of its methods.
-type Server http.Server
-
-// ListenAndServe behaves like the method on net/http.Server with the same name.
-func (srv *Server) ListenAndServe() error {
- addr := srv.Addr
- if addr == "" {
- addr = ":http"
- }
- ln, err := net.Listen("tcp", addr)
- if err != nil {
- return err
- }
- return srv.Serve(tcpKeepAliveListener{ln.(*net.TCPListener)})
-}
-
-// ListenAndServeTLS behaves like the method on net/http.Server with the same
-// name. Unlike the method of the same name on http.Server, this function
-// defaults to enforcing TLS 1.0 or higher in order to address the POODLE
-// vulnerability. Users who wish to enable SSLv3 must do so by supplying a
-// TLSConfig explicitly.
-func (srv *Server) ListenAndServeTLS(certFile, keyFile string) error {
- addr := srv.Addr
- if addr == "" {
- addr = ":https"
- }
- config := &tls.Config{
- MinVersion: tls.VersionTLS10,
- }
- if srv.TLSConfig != nil {
- config = cloneTLSConfig(srv.TLSConfig)
- }
- if config.NextProtos == nil {
- config.NextProtos = []string{"http/1.1"}
- }
-
- var err error
- config.Certificates = make([]tls.Certificate, 1)
- config.Certificates[0], err = tls.LoadX509KeyPair(certFile, keyFile)
- if err != nil {
- return err
- }
-
- ln, err := net.Listen("tcp", addr)
- if err != nil {
- return err
- }
-
- tlsListener := tls.NewListener(tcpKeepAliveListener{ln.(*net.TCPListener)}, config)
- return srv.Serve(tlsListener)
-}
-
-// ListenAndServe behaves exactly like the net/http function of the same name.
-func ListenAndServe(addr string, handler http.Handler) error {
- server := &Server{Addr: addr, Handler: handler}
- return server.ListenAndServe()
-}
-
-// ListenAndServeTLS behaves almost exactly like the net/http function of the
-// same name. Unlike net/http, however, this function defaults to enforcing TLS
-// 1.0 or higher in order to address the POODLE vulnerability. Users who wish to
-// enable SSLv3 must do so by explicitly instantiating a server with an
-// appropriately configured TLSConfig property.
-func ListenAndServeTLS(addr, certfile, keyfile string, handler http.Handler) error {
- server := &Server{Addr: addr, Handler: handler}
- return server.ListenAndServeTLS(certfile, keyfile)
-}
-
-// Serve mostly behaves like the net/http function of the same name, except that
-// if the passed listener is a net.TCPListener, TCP keep-alives are enabled on
-// accepted connections.
-func Serve(l net.Listener, handler http.Handler) error {
- if tl, ok := l.(*net.TCPListener); ok {
- l = tcpKeepAliveListener{tl}
- }
- server := &Server{Handler: handler}
- return server.Serve(l)
-}
diff --git a/vendor/github.com/zenazn/goji/graceful/signal.go b/vendor/github.com/zenazn/goji/graceful/signal.go
deleted file mode 100644
index 55061a59..00000000
--- a/vendor/github.com/zenazn/goji/graceful/signal.go
+++ /dev/null
@@ -1,223 +0,0 @@
-package graceful
-
-import (
- "os"
- "os/signal"
- "sync"
- "sync/atomic"
- "time"
-
- "github.com/zenazn/goji/graceful/listener"
-)
-
-var mu sync.Mutex // protects everything that follows
-var listeners = make([]*listener.T, 0)
-var prehooks = make([]func(os.Signal), 0)
-var posthooks = make([]func(os.Signal), 0)
-var closing int32
-var doubleKick, timeout time.Duration
-
-var wait = make(chan struct{})
-var stdSignals = []os.Signal{os.Interrupt}
-var sigchan = make(chan os.Signal, 1)
-
-// HandleSignals installs signal handlers for a set of standard signals. By
-// default, this set only includes keyboard interrupts, however when the package
-// detects that it is running under Einhorn, a SIGUSR2 handler is installed as
-// well.
-func HandleSignals() {
- AddSignal(stdSignals...)
-}
-
-// AddSignal adds the given signal to the set of signals that trigger a graceful
-// shutdown.
-func AddSignal(sig ...os.Signal) {
- signal.Notify(sigchan, sig...)
-}
-
-// ResetSignals resets the list of signals that trigger a graceful shutdown.
-func ResetSignals() {
- signal.Stop(sigchan)
-}
-
-// PreHookWithSignal registers a function to be called before any of this
-// package's normal shutdown actions, which recieves the signal that caused the
-// shutdown (or nil for manual shutdowns). All listeners will be called in the
-// order they were added, from a single goroutine.
-func PreHookWithSignal(f func(os.Signal)) {
- mu.Lock()
- defer mu.Unlock()
-
- prehooks = append(prehooks, f)
-}
-
-// PreHook registers a function to be called before any of this package's normal
-// shutdown actions. All listeners will be called in the order they were added,
-// from a single goroutine.
-func PreHook(f func()) {
- PreHookWithSignal(func(_ os.Signal) {
- f()
- })
-}
-
-// PostHookWithSignal registers a function to be called after all of this
-// package's normal shutdown actions, which receives the signal that caused the
-// shutdown (or nil for manual shutdowns). All listeners will be called in the
-// order they were added, from a single goroutine, and are guaranteed to be
-// called after all listening connections have been closed, but before Wait()
-// returns.
-//
-// If you've Hijacked any connections that must be gracefully shut down in some
-// other way (since this library disowns all hijacked connections), it's
-// reasonable to use a PostHook to signal and wait for them.
-func PostHookWithSignal(f func(os.Signal)) {
- mu.Lock()
- defer mu.Unlock()
-
- posthooks = append(posthooks, f)
-}
-
-// PostHook registers a function to be called after all of this package's normal
-// shutdown actions. All listeners will be called in the order they were added,
-// from a single goroutine, and are guaranteed to be called after all listening
-// connections have been closed, but before Wait() returns.
-//
-// If you've Hijacked any connections that must be gracefully shut down in some
-// other way (since this library disowns all hijacked connections), it's
-// reasonable to use a PostHook to signal and wait for them.
-func PostHook(f func()) {
- PostHookWithSignal(func(_ os.Signal) {
- f()
- })
-}
-
-// Shutdown manually triggers a shutdown from your application. Like Wait,
-// blocks until all connections have gracefully shut down.
-func Shutdown() {
- shutdown(false, nil)
-}
-
-// ShutdownNow triggers an immediate shutdown from your application. All
-// connections (not just those that are idle) are immediately closed, even if
-// they are in the middle of serving a request.
-func ShutdownNow() {
- shutdown(true, nil)
-}
-
-// DoubleKickWindow sets the length of the window during which two back-to-back
-// signals are treated as an especially urgent or forceful request to exit
-// (i.e., ShutdownNow instead of Shutdown). Signals delivered more than this
-// duration apart are treated as separate requests to exit gracefully as usual.
-//
-// Setting DoubleKickWindow to 0 disables the feature.
-func DoubleKickWindow(d time.Duration) {
- if d < 0 {
- return
- }
- mu.Lock()
- defer mu.Unlock()
-
- doubleKick = d
-}
-
-// Timeout sets the maximum amount of time package graceful will wait for
-// connections to gracefully shut down after receiving a signal. After this
-// timeout, connections will be forcefully shut down (similar to calling
-// ShutdownNow).
-//
-// Setting Timeout to 0 disables the feature.
-func Timeout(d time.Duration) {
- if d < 0 {
- return
- }
- mu.Lock()
- defer mu.Unlock()
-
- timeout = d
-}
-
-// Wait for all connections to gracefully shut down. This is commonly called at
-// the bottom of the main() function to prevent the program from exiting
-// prematurely.
-func Wait() {
- <-wait
-}
-
-func init() {
- go sigLoop()
-}
-func sigLoop() {
- var last time.Time
- for {
- sig := <-sigchan
- now := time.Now()
- mu.Lock()
- force := doubleKick != 0 && now.Sub(last) < doubleKick
- if t := timeout; t != 0 && !force {
- go func() {
- time.Sleep(t)
- shutdown(true, sig)
- }()
- }
- mu.Unlock()
- go shutdown(force, sig)
- last = now
- }
-}
-
-var preOnce, closeOnce, forceOnce, postOnce, notifyOnce sync.Once
-
-func shutdown(force bool, sig os.Signal) {
- preOnce.Do(func() {
- mu.Lock()
- defer mu.Unlock()
- for _, f := range prehooks {
- f(sig)
- }
- })
-
- if force {
- forceOnce.Do(func() {
- closeListeners(force)
- })
- } else {
- closeOnce.Do(func() {
- closeListeners(force)
- })
- }
-
- postOnce.Do(func() {
- mu.Lock()
- defer mu.Unlock()
- for _, f := range posthooks {
- f(sig)
- }
- })
-
- notifyOnce.Do(func() {
- close(wait)
- })
-}
-
-func closeListeners(force bool) {
- atomic.StoreInt32(&closing, 1)
-
- var wg sync.WaitGroup
- defer wg.Wait()
-
- mu.Lock()
- defer mu.Unlock()
- wg.Add(len(listeners))
-
- for _, l := range listeners {
- go func(l *listener.T) {
- defer wg.Done()
- l.Close()
- if force {
- l.DrainAll()
- } else {
- l.Drain()
- }
- }(l)
- }
-}
diff --git a/vendor/github.com/zenazn/goji/serve.go b/vendor/github.com/zenazn/goji/serve.go
deleted file mode 100644
index da73a9bf..00000000
--- a/vendor/github.com/zenazn/goji/serve.go
+++ /dev/null
@@ -1,64 +0,0 @@
-// +build !appengine
-
-package goji
-
-import (
- "crypto/tls"
- "flag"
- "log"
- "net"
- "net/http"
- "time"
-
- "github.com/zenazn/goji/bind"
- "github.com/zenazn/goji/graceful"
-)
-
-func init() {
- bind.WithFlag()
- if fl := log.Flags(); fl&log.Ltime != 0 {
- log.SetFlags(fl | log.Lmicroseconds)
- }
- graceful.DoubleKickWindow(2 * time.Second)
-}
-
-// Serve starts Goji using reasonable defaults.
-func Serve() {
- if !flag.Parsed() {
- flag.Parse()
- }
-
- ServeListener(bind.Default())
-}
-
-// Like Serve, but enables TLS using the given config.
-func ServeTLS(config *tls.Config) {
- if !flag.Parsed() {
- flag.Parse()
- }
-
- ServeListener(tls.NewListener(bind.Default(), config))
-}
-
-// Like Serve, but runs Goji on top of an arbitrary net.Listener.
-func ServeListener(listener net.Listener) {
- DefaultMux.Compile()
- // Install our handler at the root of the standard net/http default mux.
- // This allows packages like expvar to continue working as expected.
- http.Handle("/", DefaultMux)
-
- log.Println("Starting Goji on", listener.Addr())
-
- graceful.HandleSignals()
- bind.Ready()
- graceful.PreHook(func() { log.Printf("Goji received signal, gracefully stopping") })
- graceful.PostHook(func() { log.Printf("Goji stopped") })
-
- err := graceful.Serve(listener, http.DefaultServeMux)
-
- if err != nil {
- log.Fatal(err)
- }
-
- graceful.Wait()
-}
diff --git a/vendor/github.com/zenazn/goji/serve_appengine.go b/vendor/github.com/zenazn/goji/serve_appengine.go
deleted file mode 100644
index 88dc7a88..00000000
--- a/vendor/github.com/zenazn/goji/serve_appengine.go
+++ /dev/null
@@ -1,23 +0,0 @@
-// +build appengine
-
-package goji
-
-import (
- "log"
- "net/http"
-)
-
-func init() {
- if fl := log.Flags(); fl&log.Ltime != 0 {
- log.SetFlags(fl | log.Lmicroseconds)
- }
-}
-
-// Serve starts Goji using reasonable defaults.
-func Serve() {
- DefaultMux.Compile()
- // Install our handler at the root of the standard net/http default mux.
- // This is required for App Engine, and also allows packages like expvar
- // to continue working as expected.
- http.Handle("/", DefaultMux)
-}
diff --git a/vendor/github.com/zenazn/goji/web/atomic.go b/vendor/github.com/zenazn/goji/web/atomic.go
deleted file mode 100644
index 795d8e52..00000000
--- a/vendor/github.com/zenazn/goji/web/atomic.go
+++ /dev/null
@@ -1,18 +0,0 @@
-// +build !appengine
-
-package web
-
-import (
- "sync/atomic"
- "unsafe"
-)
-
-func (rt *router) getMachine() *routeMachine {
- ptr := (*unsafe.Pointer)(unsafe.Pointer(&rt.machine))
- sm := (*routeMachine)(atomic.LoadPointer(ptr))
- return sm
-}
-func (rt *router) setMachine(m *routeMachine) {
- ptr := (*unsafe.Pointer)(unsafe.Pointer(&rt.machine))
- atomic.StorePointer(ptr, unsafe.Pointer(m))
-}
diff --git a/vendor/github.com/zenazn/goji/web/atomic_appengine.go b/vendor/github.com/zenazn/goji/web/atomic_appengine.go
deleted file mode 100644
index 027127a4..00000000
--- a/vendor/github.com/zenazn/goji/web/atomic_appengine.go
+++ /dev/null
@@ -1,14 +0,0 @@
-// +build appengine
-
-package web
-
-func (rt *router) getMachine() *routeMachine {
- rt.lock.Lock()
- defer rt.lock.Unlock()
- return rt.machine
-}
-
-// We always hold the lock when calling setMachine.
-func (rt *router) setMachine(m *routeMachine) {
- rt.machine = m
-}
diff --git a/vendor/github.com/zenazn/goji/web/bytecode_compiler.go b/vendor/github.com/zenazn/goji/web/bytecode_compiler.go
deleted file mode 100644
index b6f52b12..00000000
--- a/vendor/github.com/zenazn/goji/web/bytecode_compiler.go
+++ /dev/null
@@ -1,265 +0,0 @@
-package web
-
-/*
-This file implements a fast router by encoding a list of routes first into a
-pseudo-trie, then encoding that pseudo-trie into a state machine realized as
-a routing bytecode.
-
-The most interesting part of this router is not its speed (it is quite fast),
-but the guarantees it provides. In a naive router, routes are examined one after
-another until a match is found, and this is the programming model we want to
-support. For any given request ("GET /hello/carl"), there is a list of
-"plausible" routes: routes which match the method ("GET"), and which have a
-prefix that is a prefix of the requested path ("/" and "/hello/", for instance,
-but not "/foobar"). Patterns also have some amount of arbitrary code associated
-with them, which tells us whether or not the route matched. Just like the naive
-router, our goal is to call each plausible pattern, in the order they were
-added, until we find one that matches. The "fast" part here is being smart about
-which non-plausible routes we can skip.
-
-First, we sort routes using a pairwise comparison function: sorting occurs as
-normal on the prefixes, with the caveat that a route may not be moved past a
-route that might also match the same string. Among other things, this means
-we're forced to use particularly dumb sorting algorithms, but it only has to
-happen once, and there probably aren't even that many routes to begin with. This
-logic appears inline in the router's handle() function.
-
-We then build a pseudo-trie from the sorted list of routes. It's not quite a
-normal trie because there are certain routes we cannot reorder around other
-routes (since we're providing identical semantics to the naive router), but it's
-close enough and the basic idea is the same.
-
-Finally, we lower this psuedo-trie from its tree representation to a state
-machine bytecode. The bytecode is pretty simple: it contains up to three bytes,
-a choice of a bunch of flags, and an index. The state machine is pretty simple:
-if the bytes match the next few bytes after the cursor, the instruction matches,
-and the state machine advances to the next instruction. If it does not match, it
-jumps to the instruction at the index. Various flags modify this basic behavior,
-the documentation for which can be found below.
-
-The thing we're optimizing for here over pretty much everything else is memory
-locality. We make an effort to lay out both the trie child selection logic and
-the matching of long strings consecutively in memory, making both operations
-very cheap. In fact, our matching logic isn't particularly asymptotically good,
-but in practice the benefits of memory locality outweigh just about everything
-else.
-
-Unfortunately, the code implementing all of this is pretty bad (both inefficient
-and hard to read). Maybe someday I'll come and take a second pass at it.
-*/
-type state struct {
- mode smMode
- bs [3]byte
- i int32
-}
-type stateMachine []state
-
-type smMode uint8
-
-// Many combinations of smModes don't make sense, but since this is interal to
-// the library I don't feel like documenting them.
-const (
- // The two low bits of the mode are used as a length of how many bytes
- // of bs are used. If the length is 0, the node is treated as a
- // wildcard.
- smLengthMask smMode = 3
-)
-
-const (
- // Jump to the given index on a match. Ordinarily, the state machine
- // will jump to the state given by the index if the characters do not
- // match.
- smJumpOnMatch smMode = 4 << iota
- // The index is the index of a route to try. If running the route fails,
- // the state machine advances by one.
- smRoute
- // Reset the state machine's cursor into the input string to the state's
- // index value.
- smSetCursor
- // If this bit is set, the machine transitions into a non-accepting
- // state if it matches.
- smFail
-)
-
-type trie struct {
- prefix string
- children []trieSegment
-}
-
-// A trie segment is a route matching this point (or -1), combined with a list
-// of trie children that follow that route.
-type trieSegment struct {
- route int
- children []trie
-}
-
-func buildTrie(routes []route, dp, dr int) trie {
- var t trie
- ts := trieSegment{-1, nil}
- for i, r := range routes {
- if len(r.prefix) != dp {
- continue
- }
-
- if i == 0 {
- ts.route = 0
- } else {
- subroutes := routes[ts.route+1 : i]
- ts.children = buildTrieSegment(subroutes, dp, dr+ts.route+1)
- t.children = append(t.children, ts)
- ts = trieSegment{i, nil}
- }
- }
-
- // This could be a little DRYer...
- subroutes := routes[ts.route+1:]
- ts.children = buildTrieSegment(subroutes, dp, dr+ts.route+1)
- t.children = append(t.children, ts)
-
- for i := range t.children {
- if t.children[i].route != -1 {
- t.children[i].route += dr
- }
- }
-
- return t
-}
-
-func commonPrefix(s1, s2 string) string {
- if len(s1) > len(s2) {
- return commonPrefix(s2, s1)
- }
- for i := 0; i < len(s1); i++ {
- if s1[i] != s2[i] {
- return s1[:i]
- }
- }
- return s1
-}
-
-func buildTrieSegment(routes []route, dp, dr int) []trie {
- if len(routes) == 0 {
- return nil
- }
- var tries []trie
-
- start := 0
- p := routes[0].prefix[dp:]
- for i := 1; i < len(routes); i++ {
- ip := routes[i].prefix[dp:]
- cp := commonPrefix(p, ip)
- if len(cp) == 0 {
- t := buildTrie(routes[start:i], dp+len(p), dr+start)
- t.prefix = p
- tries = append(tries, t)
- start = i
- p = ip
- } else {
- p = cp
- }
- }
-
- t := buildTrie(routes[start:], dp+len(p), dr+start)
- t.prefix = p
- return append(tries, t)
-}
-
-// This is a bit confusing, since the encode method on a trie deals exclusively
-// with trieSegments (i.e., its children), and vice versa.
-//
-// These methods are also hideously inefficient, both in terms of memory usage
-// and algorithmic complexity. If it ever becomes a problem, maybe we can do
-// something smarter than stupid O(N^2) appends, but to be honest, I bet N is
-// small (it almost always is :P) and we only do it once at boot anyways.
-
-func (t trie) encode(dp, off int) stateMachine {
- ms := make([]stateMachine, len(t.children))
- subs := make([]stateMachine, len(t.children))
- var l, msl, subl int
-
- for i, ts := range t.children {
- ms[i], subs[i] = ts.encode(dp, 0)
- msl += len(ms[i])
- l += len(ms[i]) + len(subs[i])
- }
-
- l++
-
- m := make(stateMachine, 0, l)
- for i, mm := range ms {
- for j := range mm {
- if mm[j].mode&(smRoute|smSetCursor) != 0 {
- continue
- }
-
- mm[j].i += int32(off + msl + subl + 1)
- }
- m = append(m, mm...)
- subl += len(subs[i])
- }
-
- m = append(m, state{mode: smJumpOnMatch, i: -1})
-
- msl = 0
- for i, sub := range subs {
- msl += len(ms[i])
- for j := range sub {
- if sub[j].mode&(smRoute|smSetCursor) != 0 {
- continue
- }
- if sub[j].i == -1 {
- sub[j].i = int32(off + msl)
- } else {
- sub[j].i += int32(off + len(m))
- }
- }
- m = append(m, sub...)
- }
-
- return m
-}
-
-func (ts trieSegment) encode(dp, off int) (me stateMachine, sub stateMachine) {
- o := 1
- if ts.route != -1 {
- o++
- }
- me = make(stateMachine, len(ts.children)+o)
-
- me[0] = state{mode: smSetCursor, i: int32(dp)}
- if ts.route != -1 {
- me[1] = state{mode: smRoute, i: int32(ts.route)}
- }
-
- for i, t := range ts.children {
- p := t.prefix
-
- bc := copy(me[i+o].bs[:], p)
- me[i+o].mode = smMode(bc) | smJumpOnMatch
- me[i+o].i = int32(off + len(sub))
-
- for len(p) > bc {
- var bs [3]byte
- p = p[bc:]
- bc = copy(bs[:], p)
- sub = append(sub, state{bs: bs, mode: smMode(bc), i: -1})
- }
-
- sub = append(sub, t.encode(dp+len(t.prefix), off+len(sub))...)
- }
- return
-}
-
-func compile(routes []route) stateMachine {
- if len(routes) == 0 {
- return nil
- }
- t := buildTrie(routes, 0, 0)
- m := t.encode(0, 0)
- for i := range m {
- if m[i].i == -1 {
- m[i].mode = m[i].mode | smFail
- }
- }
- return m
-}
diff --git a/vendor/github.com/zenazn/goji/web/bytecode_runner.go b/vendor/github.com/zenazn/goji/web/bytecode_runner.go
deleted file mode 100644
index c32b16a2..00000000
--- a/vendor/github.com/zenazn/goji/web/bytecode_runner.go
+++ /dev/null
@@ -1,83 +0,0 @@
-package web
-
-import "net/http"
-
-type routeMachine struct {
- sm stateMachine
- routes []route
-}
-
-func matchRoute(route route, m method, ms *method, r *http.Request, c *C) bool {
- if !route.pattern.Match(r, c) {
- return false
- }
- *ms |= route.method
-
- if route.method&m != 0 {
- route.pattern.Run(r, c)
- return true
- }
- return false
-}
-
-func (rm routeMachine) route(c *C, w http.ResponseWriter, r *http.Request) (method, *route) {
- m := httpMethod(r.Method)
- var methods method
- p := r.URL.Path
-
- if len(rm.sm) == 0 {
- return methods, nil
- }
-
- var i int
- for {
- sm := rm.sm[i].mode
- if sm&smSetCursor != 0 {
- si := rm.sm[i].i
- p = r.URL.Path[si:]
- i++
- continue
- }
-
- length := int(sm & smLengthMask)
- match := false
- if length <= len(p) {
- bs := rm.sm[i].bs
- switch length {
- case 3:
- if p[2] != bs[2] {
- break
- }
- fallthrough
- case 2:
- if p[1] != bs[1] {
- break
- }
- fallthrough
- case 1:
- if p[0] != bs[0] {
- break
- }
- fallthrough
- case 0:
- p = p[length:]
- match = true
- }
- }
-
- if match && sm&smRoute != 0 {
- si := rm.sm[i].i
- if matchRoute(rm.routes[si], m, &methods, r, c) {
- return 0, &rm.routes[si]
- }
- i++
- } else if match != (sm&smJumpOnMatch == 0) {
- if sm&smFail != 0 {
- return methods, nil
- }
- i = int(rm.sm[i].i)
- } else {
- i++
- }
- }
-}
diff --git a/vendor/github.com/zenazn/goji/web/chanpool.go b/vendor/github.com/zenazn/goji/web/chanpool.go
deleted file mode 100644
index 6c53c74f..00000000
--- a/vendor/github.com/zenazn/goji/web/chanpool.go
+++ /dev/null
@@ -1,31 +0,0 @@
-// +build !go1.3
-
-package web
-
-// This is an alternate implementation of Go 1.3's sync.Pool.
-
-// Maximum size of the pool of spare middleware stacks
-const cPoolSize = 32
-
-type cPool chan *cStack
-
-func makeCPool() *cPool {
- p := make(cPool, cPoolSize)
- return &p
-}
-
-func (c cPool) alloc() *cStack {
- select {
- case cs := <-c:
- return cs
- default:
- return nil
- }
-}
-
-func (c cPool) release(cs *cStack) {
- select {
- case c <- cs:
- default:
- }
-}
diff --git a/vendor/github.com/zenazn/goji/web/cpool.go b/vendor/github.com/zenazn/goji/web/cpool.go
deleted file mode 100644
index 59f8764a..00000000
--- a/vendor/github.com/zenazn/goji/web/cpool.go
+++ /dev/null
@@ -1,23 +0,0 @@
-// +build go1.3
-
-package web
-
-import "sync"
-
-type cPool sync.Pool
-
-func makeCPool() *cPool {
- return &cPool{}
-}
-
-func (c *cPool) alloc() *cStack {
- cs := (*sync.Pool)(c).Get()
- if cs == nil {
- return nil
- }
- return cs.(*cStack)
-}
-
-func (c *cPool) release(cs *cStack) {
- (*sync.Pool)(c).Put(cs)
-}
diff --git a/vendor/github.com/zenazn/goji/web/func_equal.go b/vendor/github.com/zenazn/goji/web/func_equal.go
deleted file mode 100644
index 9c8f7cb6..00000000
--- a/vendor/github.com/zenazn/goji/web/func_equal.go
+++ /dev/null
@@ -1,32 +0,0 @@
-package web
-
-import (
- "reflect"
-)
-
-/*
-This is more than a little sketchtacular. Go's rules for function pointer
-equality are pretty restrictive: nil function pointers always compare equal, and
-all other pointer types never do. However, this is pretty limiting: it means
-that we can't let people reference the middleware they've given us since we have
-no idea which function they're referring to.
-
-To get better data out of Go, we sketch on the representation of interfaces. We
-happen to know that interfaces are pairs of pointers: one to the real data, one
-to data about the type. Therefore, two interfaces, including two function
-interface{}'s, point to exactly the same objects iff their interface
-representations are identical. And it turns out this is sufficient for our
-purposes.
-
-If you're curious, you can read more about the representation of functions here:
-http://golang.org/s/go11func
-We're in effect comparing the pointers of the indirect layer.
-
-This function also works on non-function values.
-*/
-func funcEqual(a, b interface{}) bool {
- av := reflect.ValueOf(&a).Elem()
- bv := reflect.ValueOf(&b).Elem()
-
- return av.InterfaceData() == bv.InterfaceData()
-}
diff --git a/vendor/github.com/zenazn/goji/web/handler.go b/vendor/github.com/zenazn/goji/web/handler.go
deleted file mode 100644
index 746c9f03..00000000
--- a/vendor/github.com/zenazn/goji/web/handler.go
+++ /dev/null
@@ -1,42 +0,0 @@
-package web
-
-import (
- "log"
- "net/http"
-)
-
-const unknownHandler = `Unknown handler type %T. See http://godoc.org/github.com/zenazn/goji/web#HandlerType for a list of acceptable types.`
-
-type netHTTPHandlerWrap struct{ http.Handler }
-type netHTTPHandlerFuncWrap struct {
- fn func(http.ResponseWriter, *http.Request)
-}
-type handlerFuncWrap struct {
- fn func(C, http.ResponseWriter, *http.Request)
-}
-
-func (h netHTTPHandlerWrap) ServeHTTPC(c C, w http.ResponseWriter, r *http.Request) {
- h.Handler.ServeHTTP(w, r)
-}
-func (h netHTTPHandlerFuncWrap) ServeHTTPC(c C, w http.ResponseWriter, r *http.Request) {
- h.fn(w, r)
-}
-func (h handlerFuncWrap) ServeHTTPC(c C, w http.ResponseWriter, r *http.Request) {
- h.fn(c, w, r)
-}
-
-func parseHandler(h HandlerType) Handler {
- switch f := h.(type) {
- case func(c C, w http.ResponseWriter, r *http.Request):
- return handlerFuncWrap{f}
- case func(w http.ResponseWriter, r *http.Request):
- return netHTTPHandlerFuncWrap{f}
- case Handler:
- return f
- case http.Handler:
- return netHTTPHandlerWrap{f}
- default:
- log.Fatalf(unknownHandler, h)
- panic("log.Fatalf does not return")
- }
-}
diff --git a/vendor/github.com/zenazn/goji/web/match.go b/vendor/github.com/zenazn/goji/web/match.go
deleted file mode 100644
index 1a44144b..00000000
--- a/vendor/github.com/zenazn/goji/web/match.go
+++ /dev/null
@@ -1,66 +0,0 @@
-package web
-
-// The key used to store route Matches in the Goji environment. If this key is
-// present in the environment and contains a value of type Match, routing will
-// not be performed, and the Match's Handler will be used instead.
-const MatchKey = "goji.web.Match"
-
-// Match is the type of routing matches. It is inserted into C.Env under
-// MatchKey when the Mux.Router middleware is invoked. If MatchKey is present at
-// route dispatch time, the Handler of the corresponding Match will be called
-// instead of performing routing as usual.
-//
-// By computing a Match and inserting it into the Goji environment as part of a
-// middleware stack (see Mux.Router, for instance), it is possible to customize
-// Goji's routing behavior or replace it entirely.
-type Match struct {
- // Pattern is the Pattern that matched during routing. Will be nil if no
- // route matched (Handler will be set to the Mux's NotFound handler)
- Pattern Pattern
- // The Handler corresponding to the matched pattern.
- Handler Handler
-}
-
-// GetMatch returns the Match stored in the Goji environment, or an empty Match
-// if none exists (valid Matches always have a Handler property).
-func GetMatch(c C) Match {
- if c.Env == nil {
- return Match{}
- }
- mi, ok := c.Env[MatchKey]
- if !ok {
- return Match{}
- }
- if m, ok := mi.(Match); ok {
- return m
- }
- return Match{}
-}
-
-// RawPattern returns the PatternType that was originally passed to ParsePattern
-// or any of the HTTP method functions (Get, Post, etc.).
-func (m Match) RawPattern() PatternType {
- switch v := m.Pattern.(type) {
- case regexpPattern:
- return v.re
- case stringPattern:
- return v.raw
- default:
- return v
- }
-}
-
-// RawHandler returns the HandlerType that was originally passed to the HTTP
-// method functions (Get, Post, etc.).
-func (m Match) RawHandler() HandlerType {
- switch v := m.Handler.(type) {
- case netHTTPHandlerWrap:
- return v.Handler
- case handlerFuncWrap:
- return v.fn
- case netHTTPHandlerFuncWrap:
- return v.fn
- default:
- return v
- }
-}
diff --git a/vendor/github.com/zenazn/goji/web/middleware.go b/vendor/github.com/zenazn/goji/web/middleware.go
deleted file mode 100644
index 985a6c9e..00000000
--- a/vendor/github.com/zenazn/goji/web/middleware.go
+++ /dev/null
@@ -1,155 +0,0 @@
-package web
-
-import (
- "fmt"
- "log"
- "net/http"
- "sync"
-)
-
-// mLayer is a single middleware stack layer. It contains a canonicalized
-// middleware representation, as well as the original function as passed to us.
-type mLayer struct {
- fn func(*C, http.Handler) http.Handler
- orig interface{}
-}
-
-// mStack is an entire middleware stack. It contains a slice of middleware
-// layers (outermost first) protected by a mutex, a cache of pre-built stack
-// instances, and a final routing function.
-type mStack struct {
- lock sync.Mutex
- stack []mLayer
- pool *cPool
- router internalRouter
-}
-
-type internalRouter interface {
- route(*C, http.ResponseWriter, *http.Request)
-}
-
-/*
-cStack is a cached middleware stack instance. Constructing a middleware stack
-involves a lot of allocations: at the very least each layer will have to close
-over the layer after (inside) it and a stack N levels deep will incur at least N
-separate allocations. Instead of doing this on every request, we keep a pool of
-pre-built stacks around for reuse.
-*/
-type cStack struct {
- C
- m http.Handler
- pool *cPool
-}
-
-func (s *cStack) ServeHTTP(w http.ResponseWriter, r *http.Request) {
- s.C = C{}
- s.m.ServeHTTP(w, r)
-}
-func (s *cStack) ServeHTTPC(c C, w http.ResponseWriter, r *http.Request) {
- s.C = c
- s.m.ServeHTTP(w, r)
-}
-
-const unknownMiddleware = `Unknown middleware type %T. See http://godoc.org/github.com/zenazn/goji/web#MiddlewareType for a list of acceptable types.`
-
-func (m *mStack) appendLayer(fn interface{}) {
- ml := mLayer{orig: fn}
- switch f := fn.(type) {
- case func(http.Handler) http.Handler:
- ml.fn = func(c *C, h http.Handler) http.Handler {
- return f(h)
- }
- case func(*C, http.Handler) http.Handler:
- ml.fn = f
- default:
- log.Fatalf(unknownMiddleware, fn)
- }
- m.stack = append(m.stack, ml)
-}
-
-func (m *mStack) findLayer(l interface{}) int {
- for i, middleware := range m.stack {
- if funcEqual(l, middleware.orig) {
- return i
- }
- }
- return -1
-}
-
-func (m *mStack) invalidate() {
- m.pool = makeCPool()
-}
-
-func (m *mStack) newStack() *cStack {
- cs := cStack{}
- router := m.router
-
- cs.m = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- router.route(&cs.C, w, r)
- })
- for i := len(m.stack) - 1; i >= 0; i-- {
- cs.m = m.stack[i].fn(&cs.C, cs.m)
- }
-
- return &cs
-}
-
-func (m *mStack) alloc() *cStack {
- p := m.pool
- cs := p.alloc()
- if cs == nil {
- cs = m.newStack()
- }
-
- cs.pool = p
- return cs
-}
-
-func (m *mStack) release(cs *cStack) {
- cs.C = C{}
- if cs.pool != m.pool {
- return
- }
- p := cs.pool
- cs.pool = nil
- p.release(cs)
-}
-
-func (m *mStack) Use(middleware interface{}) {
- m.lock.Lock()
- defer m.lock.Unlock()
- m.appendLayer(middleware)
- m.invalidate()
-}
-
-func (m *mStack) Insert(middleware, before interface{}) error {
- m.lock.Lock()
- defer m.lock.Unlock()
- i := m.findLayer(before)
- if i < 0 {
- return fmt.Errorf("web: unknown middleware %v", before)
- }
-
- m.appendLayer(middleware)
- inserted := m.stack[len(m.stack)-1]
- copy(m.stack[i+1:], m.stack[i:])
- m.stack[i] = inserted
-
- m.invalidate()
- return nil
-}
-
-func (m *mStack) Abandon(middleware interface{}) error {
- m.lock.Lock()
- defer m.lock.Unlock()
- i := m.findLayer(middleware)
- if i < 0 {
- return fmt.Errorf("web: unknown middleware %v", middleware)
- }
-
- copy(m.stack[i:], m.stack[i+1:])
- m.stack = m.stack[: len(m.stack)-1 : len(m.stack)]
-
- m.invalidate()
- return nil
-}
diff --git a/vendor/github.com/zenazn/goji/web/middleware/envinit.go b/vendor/github.com/zenazn/goji/web/middleware/envinit.go
deleted file mode 100644
index ae3b683a..00000000
--- a/vendor/github.com/zenazn/goji/web/middleware/envinit.go
+++ /dev/null
@@ -1,27 +0,0 @@
-package middleware
-
-import (
- "net/http"
-
- "github.com/zenazn/goji/web"
-)
-
-type envInit struct {
- c *web.C
- h http.Handler
-}
-
-func (e envInit) ServeHTTP(w http.ResponseWriter, r *http.Request) {
- if e.c.Env == nil {
- e.c.Env = make(map[interface{}]interface{})
- }
- e.h.ServeHTTP(w, r)
-}
-
-// EnvInit is a middleware that allocates an environment map if it is nil. While
-// it's impossible in general to ensure that Env is never nil in a middleware
-// stack, in most common cases placing this middleware at the top of the stack
-// will eliminate the need for repetative nil checks.
-func EnvInit(c *web.C, h http.Handler) http.Handler {
- return envInit{c, h}
-}
diff --git a/vendor/github.com/zenazn/goji/web/middleware/logger.go b/vendor/github.com/zenazn/goji/web/middleware/logger.go
deleted file mode 100644
index 8bbcac8c..00000000
--- a/vendor/github.com/zenazn/goji/web/middleware/logger.go
+++ /dev/null
@@ -1,92 +0,0 @@
-package middleware
-
-import (
- "bytes"
- "log"
- "net/http"
- "time"
-
- "github.com/zenazn/goji/web"
- "github.com/zenazn/goji/web/mutil"
-)
-
-// Logger is a middleware that logs the start and end of each request, along
-// with some useful data about what was requested, what the response status was,
-// and how long it took to return. When standard output is a TTY, Logger will
-// print in color, otherwise it will print in black and white.
-//
-// Logger prints a request ID if one is provided.
-//
-// Logger has been designed explicitly to be Good Enough for use in small
-// applications and for people just getting started with Goji. It is expected
-// that applications will eventually outgrow this middleware and replace it with
-// a custom request logger, such as one that produces machine-parseable output,
-// outputs logs to a different service (e.g., syslog), or formats lines like
-// those printed elsewhere in the application.
-func Logger(c *web.C, h http.Handler) http.Handler {
- fn := func(w http.ResponseWriter, r *http.Request) {
- reqID := GetReqID(*c)
-
- printStart(reqID, r)
-
- lw := mutil.WrapWriter(w)
-
- t1 := time.Now()
- h.ServeHTTP(lw, r)
-
- if lw.Status() == 0 {
- lw.WriteHeader(http.StatusOK)
- }
- t2 := time.Now()
-
- printEnd(reqID, lw, t2.Sub(t1))
- }
-
- return http.HandlerFunc(fn)
-}
-
-func printStart(reqID string, r *http.Request) {
- var buf bytes.Buffer
-
- if reqID != "" {
- cW(&buf, bBlack, "[%s] ", reqID)
- }
- buf.WriteString("Started ")
- cW(&buf, bMagenta, "%s ", r.Method)
- cW(&buf, nBlue, "%q ", r.URL.String())
- buf.WriteString("from ")
- buf.WriteString(r.RemoteAddr)
-
- log.Print(buf.String())
-}
-
-func printEnd(reqID string, w mutil.WriterProxy, dt time.Duration) {
- var buf bytes.Buffer
-
- if reqID != "" {
- cW(&buf, bBlack, "[%s] ", reqID)
- }
- buf.WriteString("Returning ")
- status := w.Status()
- if status < 200 {
- cW(&buf, bBlue, "%03d", status)
- } else if status < 300 {
- cW(&buf, bGreen, "%03d", status)
- } else if status < 400 {
- cW(&buf, bCyan, "%03d", status)
- } else if status < 500 {
- cW(&buf, bYellow, "%03d", status)
- } else {
- cW(&buf, bRed, "%03d", status)
- }
- buf.WriteString(" in ")
- if dt < 500*time.Millisecond {
- cW(&buf, nGreen, "%s", dt)
- } else if dt < 5*time.Second {
- cW(&buf, nYellow, "%s", dt)
- } else {
- cW(&buf, nRed, "%s", dt)
- }
-
- log.Print(buf.String())
-}
diff --git a/vendor/github.com/zenazn/goji/web/middleware/middleware.go b/vendor/github.com/zenazn/goji/web/middleware/middleware.go
deleted file mode 100644
index 23cfde29..00000000
--- a/vendor/github.com/zenazn/goji/web/middleware/middleware.go
+++ /dev/null
@@ -1,4 +0,0 @@
-/*
-Package middleware provides several standard middleware implementations.
-*/
-package middleware
diff --git a/vendor/github.com/zenazn/goji/web/middleware/nocache.go b/vendor/github.com/zenazn/goji/web/middleware/nocache.go
deleted file mode 100644
index ae3d2609..00000000
--- a/vendor/github.com/zenazn/goji/web/middleware/nocache.go
+++ /dev/null
@@ -1,55 +0,0 @@
-package middleware
-
-import (
- "net/http"
- "time"
-)
-
-// Unix epoch time
-var epoch = time.Unix(0, 0).Format(time.RFC1123)
-
-// Taken from https://github.com/mytrile/nocache
-var noCacheHeaders = map[string]string{
- "Expires": epoch,
- "Cache-Control": "no-cache, private, max-age=0",
- "Pragma": "no-cache",
- "X-Accel-Expires": "0",
-}
-
-var etagHeaders = []string{
- "ETag",
- "If-Modified-Since",
- "If-Match",
- "If-None-Match",
- "If-Range",
- "If-Unmodified-Since",
-}
-
-// NoCache is a simple piece of middleware that sets a number of HTTP headers to prevent
-// a router (or subrouter) from being cached by an upstream proxy and/or client.
-//
-// As per http://wiki.nginx.org/HttpProxyModule - NoCache sets:
-// Expires: Thu, 01 Jan 1970 00:00:00 UTC
-// Cache-Control: no-cache, private, max-age=0
-// X-Accel-Expires: 0
-// Pragma: no-cache (for HTTP/1.0 proxies/clients)
-func NoCache(h http.Handler) http.Handler {
- fn := func(w http.ResponseWriter, r *http.Request) {
-
- // Delete any ETag headers that may have been set
- for _, v := range etagHeaders {
- if r.Header.Get(v) != "" {
- r.Header.Del(v)
- }
- }
-
- // Set our NoCache headers
- for k, v := range noCacheHeaders {
- w.Header().Set(k, v)
- }
-
- h.ServeHTTP(w, r)
- }
-
- return http.HandlerFunc(fn)
-}
diff --git a/vendor/github.com/zenazn/goji/web/middleware/options.go b/vendor/github.com/zenazn/goji/web/middleware/options.go
deleted file mode 100644
index 4bdce5f7..00000000
--- a/vendor/github.com/zenazn/goji/web/middleware/options.go
+++ /dev/null
@@ -1,97 +0,0 @@
-package middleware
-
-import (
- "net/http"
- "strings"
-
- "github.com/zenazn/goji/web"
-)
-
-type autoOptionsState int
-
-const (
- aosInit autoOptionsState = iota
- aosHeaderWritten
- aosProxying
-)
-
-// I originally used an httptest.ResponseRecorder here, but package httptest
-// adds a flag which I'm not particularly eager to expose. This is essentially a
-// ResponseRecorder that has been specialized for the purpose at hand to avoid
-// the httptest dependency.
-type autoOptionsProxy struct {
- w http.ResponseWriter
- c *web.C
- state autoOptionsState
-}
-
-func (p *autoOptionsProxy) Header() http.Header {
- return p.w.Header()
-}
-
-func (p *autoOptionsProxy) Write(buf []byte) (int, error) {
- switch p.state {
- case aosInit:
- p.state = aosHeaderWritten
- case aosProxying:
- return len(buf), nil
- }
- return p.w.Write(buf)
-}
-
-func (p *autoOptionsProxy) WriteHeader(code int) {
- methods := getValidMethods(*p.c)
- switch p.state {
- case aosInit:
- if methods != nil && code == http.StatusNotFound {
- p.state = aosProxying
- break
- }
- p.state = aosHeaderWritten
- fallthrough
- default:
- p.w.WriteHeader(code)
- return
- }
-
- methods = addMethod(methods, "OPTIONS")
- p.w.Header().Set("Allow", strings.Join(methods, ", "))
- p.w.WriteHeader(http.StatusOK)
-}
-
-// AutomaticOptions automatically return an appropriate "Allow" header when the
-// request method is OPTIONS and the request would have otherwise been 404'd.
-func AutomaticOptions(c *web.C, h http.Handler) http.Handler {
- fn := func(w http.ResponseWriter, r *http.Request) {
- if r.Method == "OPTIONS" {
- w = &autoOptionsProxy{c: c, w: w}
- }
-
- h.ServeHTTP(w, r)
- }
-
- return http.HandlerFunc(fn)
-}
-
-func getValidMethods(c web.C) []string {
- if c.Env == nil {
- return nil
- }
- v, ok := c.Env[web.ValidMethodsKey]
- if !ok {
- return nil
- }
- if methods, ok := v.([]string); ok {
- return methods
- }
- return nil
-}
-
-func addMethod(methods []string, method string) []string {
- for _, m := range methods {
- if m == method {
- return methods
- }
- }
- return append(methods, method)
-}
diff --git a/vendor/github.com/zenazn/goji/web/middleware/realip.go b/vendor/github.com/zenazn/goji/web/middleware/realip.go
deleted file mode 100644
index ae5599f2..00000000
--- a/vendor/github.com/zenazn/goji/web/middleware/realip.go
+++ /dev/null
@@ -1,51 +0,0 @@
-package middleware
-
-import (
- "net/http"
- "strings"
-)
-
-var xForwardedFor = http.CanonicalHeaderKey("X-Forwarded-For")
-var xRealIP = http.CanonicalHeaderKey("X-Real-IP")
-
-// RealIP is a middleware that sets a http.Request's RemoteAddr to the results
-// of parsing either the X-Forwarded-For header or the X-Real-IP header (in that
-// order).
-//
-// This middleware should be inserted fairly early in the middleware stack to
-// ensure that subsequent layers (e.g., request loggers) which examine the
-// RemoteAddr will see the intended value.
-//
-// You should only use this middleware if you can trust the headers passed to
-// you (in particular, the two headers this middleware uses), for example
-// because you have placed a reverse proxy like HAProxy or nginx in front of
-// Goji. If your reverse proxies are configured to pass along arbitrary header
-// values from the client, or if you use this middleware without a reverse
-// proxy, malicious clients will be able to make you very sad (or, depending on
-// how you're using RemoteAddr, vulnerable to an attack of some sort).
-func RealIP(h http.Handler) http.Handler {
- fn := func(w http.ResponseWriter, r *http.Request) {
- if rip := realIP(r); rip != "" {
- r.RemoteAddr = rip
- }
- h.ServeHTTP(w, r)
- }
-
- return http.HandlerFunc(fn)
-}
-
-func realIP(r *http.Request) string {
- var ip string
-
- if xff := r.Header.Get(xForwardedFor); xff != "" {
- i := strings.Index(xff, ", ")
- if i == -1 {
- i = len(xff)
- }
- ip = xff[:i]
- } else if xrip := r.Header.Get(xRealIP); xrip != "" {
- ip = xrip
- }
-
- return ip
-}
diff --git a/vendor/github.com/zenazn/goji/web/middleware/recoverer.go b/vendor/github.com/zenazn/goji/web/middleware/recoverer.go
deleted file mode 100644
index 43ad648d..00000000
--- a/vendor/github.com/zenazn/goji/web/middleware/recoverer.go
+++ /dev/null
@@ -1,44 +0,0 @@
-package middleware
-
-import (
- "bytes"
- "log"
- "net/http"
- "runtime/debug"
-
- "github.com/zenazn/goji/web"
-)
-
-// Recoverer is a middleware that recovers from panics, logs the panic (and a
-// backtrace), and returns a HTTP 500 (Internal Server Error) status if
-// possible.
-//
-// Recoverer prints a request ID if one is provided.
-func Recoverer(c *web.C, h http.Handler) http.Handler {
- fn := func(w http.ResponseWriter, r *http.Request) {
- reqID := GetReqID(*c)
-
- defer func() {
- if err := recover(); err != nil {
- printPanic(reqID, err)
- debug.PrintStack()
- http.Error(w, http.StatusText(500), 500)
- }
- }()
-
- h.ServeHTTP(w, r)
- }
-
- return http.HandlerFunc(fn)
-}
-
-func printPanic(reqID string, err interface{}) {
- var buf bytes.Buffer
-
- if reqID != "" {
- cW(&buf, bBlack, "[%s] ", reqID)
- }
- cW(&buf, bRed, "panic: %+v", err)
-
- log.Print(buf.String())
-}
diff --git a/vendor/github.com/zenazn/goji/web/middleware/request_id.go b/vendor/github.com/zenazn/goji/web/middleware/request_id.go
deleted file mode 100644
index 834d8e35..00000000
--- a/vendor/github.com/zenazn/goji/web/middleware/request_id.go
+++ /dev/null
@@ -1,88 +0,0 @@
-package middleware
-
-import (
- "crypto/rand"
- "encoding/base64"
- "fmt"
- "net/http"
- "os"
- "strings"
- "sync/atomic"
-
- "github.com/zenazn/goji/web"
-)
-
-// Key to use when setting the request ID.
-const RequestIDKey = "reqID"
-
-var prefix string
-var reqid uint64
-
-/*
-A quick note on the statistics here: we're trying to calculate the chance that
-two randomly generated base62 prefixes will collide. We use the formula from
-http://en.wikipedia.org/wiki/Birthday_problem
-
-P[m, n] \approx 1 - e^{-m^2/2n}
-
-We ballpark an upper bound for $m$ by imagining (for whatever reason) a server
-that restarts every second over 10 years, for $m = 86400 * 365 * 10 = 315360000$
-
-For a $k$ character base-62 identifier, we have $n(k) = 62^k$
-
-Plugging this in, we find $P[m, n(10)] \approx 5.75%$, which is good enough for
-our purposes, and is surely more than anyone would ever need in practice -- a
-process that is rebooted a handful of times a day for a hundred years has less
-than a millionth of a percent chance of generating two colliding IDs.
-*/
-
-func init() {
- hostname, err := os.Hostname()
- if hostname == "" || err != nil {
- hostname = "localhost"
- }
- var buf [12]byte
- var b64 string
- for len(b64) < 10 {
- rand.Read(buf[:])
- b64 = base64.StdEncoding.EncodeToString(buf[:])
- b64 = strings.NewReplacer("+", "", "/", "").Replace(b64)
- }
-
- prefix = fmt.Sprintf("%s/%s", hostname, b64[0:10])
-}
-
-// RequestID is a middleware that injects a request ID into the context of each
-// request. A request ID is a string of the form "host.example.com/random-0001",
-// where "random" is a base62 random string that uniquely identifies this go
-// process, and where the last number is an atomically incremented request
-// counter.
-func RequestID(c *web.C, h http.Handler) http.Handler {
- fn := func(w http.ResponseWriter, r *http.Request) {
- if c.Env == nil {
- c.Env = make(map[interface{}]interface{})
- }
- myid := atomic.AddUint64(&reqid, 1)
- c.Env[RequestIDKey] = fmt.Sprintf("%s-%06d", prefix, myid)
-
- h.ServeHTTP(w, r)
- }
-
- return http.HandlerFunc(fn)
-}
-
-// GetReqID returns a request ID from the given context if one is present.
-// Returns the empty string if a request ID cannot be found.
-func GetReqID(c web.C) string {
- if c.Env == nil {
- return ""
- }
- v, ok := c.Env[RequestIDKey]
- if !ok {
- return ""
- }
- if reqID, ok := v.(string); ok {
- return reqID
- }
- return ""
-}
diff --git a/vendor/github.com/zenazn/goji/web/middleware/subrouter.go b/vendor/github.com/zenazn/goji/web/middleware/subrouter.go
deleted file mode 100644
index e5b0921e..00000000
--- a/vendor/github.com/zenazn/goji/web/middleware/subrouter.go
+++ /dev/null
@@ -1,65 +0,0 @@
-package middleware
-
-import (
- "net/http"
-
- "github.com/zenazn/goji/web"
-)
-
-type subrouter struct {
- c *web.C
- h http.Handler
-}
-
-func (s subrouter) ServeHTTP(w http.ResponseWriter, r *http.Request) {
- if s.c.URLParams != nil {
- path, ok := s.c.URLParams["*"]
- if !ok {
- path, ok = s.c.URLParams["_"]
- }
- if ok {
- oldpath := r.URL.Path
- oldmatch := web.GetMatch(*s.c)
- r.URL.Path = path
- if oldmatch.Handler != nil {
- delete(s.c.Env, web.MatchKey)
- }
-
- defer func() {
- r.URL.Path = oldpath
-
- if s.c.Env == nil {
- return
- }
- if oldmatch.Handler != nil {
- s.c.Env[web.MatchKey] = oldmatch
- } else {
- delete(s.c.Env, web.MatchKey)
- }
- }()
- }
- }
- s.h.ServeHTTP(w, r)
-}
-
-/*
-SubRouter is a helper middleware that makes writing sub-routers easier.
-
-If you register a sub-router under a key like "/admin/*", Goji's router will
-automatically set c.URLParams["*"] to the unmatched path suffix. This middleware
-will help you set the request URL's Path to this unmatched suffix, allowing you
-to write sub-routers with no knowledge of what routes the parent router matches.
-
-Since Go's regular expressions do not allow you to create a capturing group
-named "*", SubRouter also accepts the string "_". For instance, to duplicate the
-semantics of the string pattern "/foo/*", you might use the regular expression
-"^/foo(?P<_>/.*)$".
-
-This middleware is Match-aware: it will un-set any explicit routing information
-contained in the Goji context in order to prevent routing loops when using
-explicit routing with sub-routers. See the documentation for Mux.Router for
-more.
-*/
-func SubRouter(c *web.C, h http.Handler) http.Handler {
- return subrouter{c, h}
-}
diff --git a/vendor/github.com/zenazn/goji/web/middleware/terminal.go b/vendor/github.com/zenazn/goji/web/middleware/terminal.go
deleted file mode 100644
index db029170..00000000
--- a/vendor/github.com/zenazn/goji/web/middleware/terminal.go
+++ /dev/null
@@ -1,60 +0,0 @@
-package middleware
-
-import (
- "bytes"
- "fmt"
- "os"
-)
-
-var (
- // Normal colors
- nBlack = []byte{'\033', '[', '3', '0', 'm'}
- nRed = []byte{'\033', '[', '3', '1', 'm'}
- nGreen = []byte{'\033', '[', '3', '2', 'm'}
- nYellow = []byte{'\033', '[', '3', '3', 'm'}
- nBlue = []byte{'\033', '[', '3', '4', 'm'}
- nMagenta = []byte{'\033', '[', '3', '5', 'm'}
- nCyan = []byte{'\033', '[', '3', '6', 'm'}
- nWhite = []byte{'\033', '[', '3', '7', 'm'}
- // Bright colors
- bBlack = []byte{'\033', '[', '3', '0', ';', '1', 'm'}
- bRed = []byte{'\033', '[', '3', '1', ';', '1', 'm'}
- bGreen = []byte{'\033', '[', '3', '2', ';', '1', 'm'}
- bYellow = []byte{'\033', '[', '3', '3', ';', '1', 'm'}
- bBlue = []byte{'\033', '[', '3', '4', ';', '1', 'm'}
- bMagenta = []byte{'\033', '[', '3', '5', ';', '1', 'm'}
- bCyan = []byte{'\033', '[', '3', '6', ';', '1', 'm'}
- bWhite = []byte{'\033', '[', '3', '7', ';', '1', 'm'}
-
- reset = []byte{'\033', '[', '0', 'm'}
-)
-
-var isTTY bool
-
-func init() {
- // This is sort of cheating: if stdout is a character device, we assume
- // that means it's a TTY. Unfortunately, there are many non-TTY
- // character devices, but fortunately stdout is rarely set to any of
- // them.
- //
- // We could solve this properly by pulling in a dependency on
- // code.google.com/p/go.crypto/ssh/terminal, for instance, but as a
- // heuristic for whether to print in color or in black-and-white, I'd
- // really rather not.
- fi, err := os.Stdout.Stat()
- if err == nil {
- m := os.ModeDevice | os.ModeCharDevice
- isTTY = fi.Mode()&m == m
- }
-}
-
-// colorWrite
-func cW(buf *bytes.Buffer, color []byte, s string, args ...interface{}) {
- if isTTY {
- buf.Write(color)
- }
- fmt.Fprintf(buf, s, args...)
- if isTTY {
- buf.Write(reset)
- }
-}
diff --git a/vendor/github.com/zenazn/goji/web/middleware/urlquery.go b/vendor/github.com/zenazn/goji/web/middleware/urlquery.go
deleted file mode 100644
index 36c8820b..00000000
--- a/vendor/github.com/zenazn/goji/web/middleware/urlquery.go
+++ /dev/null
@@ -1,24 +0,0 @@
-package middleware
-
-import (
- "github.com/zenazn/goji/web"
- "net/http"
-)
-
-// URLQueryKey is the context key for the URL Query
-const URLQueryKey string = "urlquery"
-
-// URLQuery is a middleware to parse the URL Query parameters just once,
-// and store the resulting url.Values in the context.
-func URLQuery(c *web.C, h http.Handler) http.Handler {
- fn := func(w http.ResponseWriter, r *http.Request) {
- if c.Env == nil {
- c.Env = make(map[interface{}]interface{})
- }
- c.Env[URLQueryKey] = r.URL.Query()
-
- h.ServeHTTP(w, r)
- }
-
- return http.HandlerFunc(fn)
-}
diff --git a/vendor/github.com/zenazn/goji/web/mutil/mutil.go b/vendor/github.com/zenazn/goji/web/mutil/mutil.go
deleted file mode 100644
index e8d5b28d..00000000
--- a/vendor/github.com/zenazn/goji/web/mutil/mutil.go
+++ /dev/null
@@ -1,3 +0,0 @@
-// Package mutil contains various functions that are helpful when writing http
-// middleware.
-package mutil
diff --git a/vendor/github.com/zenazn/goji/web/mutil/writer_proxy.go b/vendor/github.com/zenazn/goji/web/mutil/writer_proxy.go
deleted file mode 100644
index fb4a08be..00000000
--- a/vendor/github.com/zenazn/goji/web/mutil/writer_proxy.go
+++ /dev/null
@@ -1,151 +0,0 @@
-// +build !go1.8
-
-package mutil
-
-import (
- "bufio"
- "io"
- "net"
- "net/http"
-)
-
-// WriterProxy is a proxy around an http.ResponseWriter that allows you to hook
-// into various parts of the response process.
-type WriterProxy interface {
- http.ResponseWriter
- // Status returns the HTTP status of the request, or 0 if one has not
- // yet been sent.
- Status() int
- // BytesWritten returns the total number of bytes sent to the client.
- BytesWritten() int
- // Tee causes the response body to be written to the given io.Writer in
- // addition to proxying the writes through. Only one io.Writer can be
- // tee'd to at once: setting a second one will overwrite the first.
- // Writes will be sent to the proxy before being written to this
- // io.Writer. It is illegal for the tee'd writer to be modified
- // concurrently with writes.
- Tee(io.Writer)
- // Unwrap returns the original proxied target.
- Unwrap() http.ResponseWriter
-}
-
-// WrapWriter wraps an http.ResponseWriter, returning a proxy that allows you to
-// hook into various parts of the response process.
-func WrapWriter(w http.ResponseWriter) WriterProxy {
- _, cn := w.(http.CloseNotifier)
- _, fl := w.(http.Flusher)
- _, hj := w.(http.Hijacker)
- _, rf := w.(io.ReaderFrom)
-
- bw := basicWriter{ResponseWriter: w}
- if cn && fl && hj && rf {
- return &fancyWriter{bw}
- }
- if fl {
- return &flushWriter{bw}
- }
- return &bw
-}
-
-// basicWriter wraps a http.ResponseWriter that implements the minimal
-// http.ResponseWriter interface.
-type basicWriter struct {
- http.ResponseWriter
- wroteHeader bool
- code int
- bytes int
- tee io.Writer
-}
-
-func (b *basicWriter) WriteHeader(code int) {
- if !b.wroteHeader {
- b.code = code
- b.wroteHeader = true
- b.ResponseWriter.WriteHeader(code)
- }
-}
-
-func (b *basicWriter) Write(buf []byte) (int, error) {
- b.WriteHeader(http.StatusOK)
- n, err := b.ResponseWriter.Write(buf)
- if b.tee != nil {
- _, err2 := b.tee.Write(buf[:n])
- // Prefer errors generated by the proxied writer.
- if err == nil {
- err = err2
- }
- }
- b.bytes += n
- return n, err
-}
-
-func (b *basicWriter) maybeWriteHeader() {
- if !b.wroteHeader {
- b.WriteHeader(http.StatusOK)
- }
-}
-
-func (b *basicWriter) Status() int {
- return b.code
-}
-
-func (b *basicWriter) BytesWritten() int {
- return b.bytes
-}
-
-func (b *basicWriter) Tee(w io.Writer) {
- b.tee = w
-}
-
-func (b *basicWriter) Unwrap() http.ResponseWriter {
- return b.ResponseWriter
-}
-
-// fancyWriter is a writer that additionally satisfies http.CloseNotifier,
-// http.Flusher, http.Hijacker, and io.ReaderFrom. It exists for the common case
-// of wrapping the http.ResponseWriter that package http gives you, in order to
-// make the proxied object support the full method set of the proxied object.
-type fancyWriter struct {
- basicWriter
-}
-
-func (f *fancyWriter) CloseNotify() <-chan bool {
- cn := f.basicWriter.ResponseWriter.(http.CloseNotifier)
- return cn.CloseNotify()
-}
-
-func (f *fancyWriter) Flush() {
- fl := f.basicWriter.ResponseWriter.(http.Flusher)
- fl.Flush()
-}
-
-func (f *fancyWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) {
- hj := f.basicWriter.ResponseWriter.(http.Hijacker)
- return hj.Hijack()
-}
-
-func (f *fancyWriter) ReadFrom(r io.Reader) (int64, error) {
- if f.basicWriter.tee != nil {
- return io.Copy(&f.basicWriter, r)
- }
- rf := f.basicWriter.ResponseWriter.(io.ReaderFrom)
- f.basicWriter.maybeWriteHeader()
- return rf.ReadFrom(r)
-}
-
-type flushWriter struct {
- basicWriter
-}
-
-func (f *flushWriter) Flush() {
- fl := f.basicWriter.ResponseWriter.(http.Flusher)
- fl.Flush()
-}
-
-var (
- _ http.CloseNotifier = &fancyWriter{}
- _ http.Flusher = &fancyWriter{}
- _ http.Hijacker = &fancyWriter{}
- _ io.ReaderFrom = &fancyWriter{}
- _ http.Flusher = &flushWriter{}
-)
diff --git a/vendor/github.com/zenazn/goji/web/mux.go b/vendor/github.com/zenazn/goji/web/mux.go
deleted file mode 100644
index 18b99919..00000000
--- a/vendor/github.com/zenazn/goji/web/mux.go
+++ /dev/null
@@ -1,213 +0,0 @@
-package web
-
-import (
- "net/http"
-)
-
-/*
-Mux is an HTTP multiplexer, much like net/http's ServeMux. It functions as both
-a middleware stack and as an HTTP router.
-
-Middleware provide a great abstraction for actions that must be performed on
-every request, such as request logging and authentication. To append, insert,
-and remove middleware, you can call the Use, Insert, and Abandon functions
-respectively.
-
-Routes may be added using any of the HTTP verb functions (Get, Post, etc.), or
-through the generic Handle function. Goji's routing algorithm is very simple:
-routes are processed in the order they are added, and the first matching route
-will be executed. Routes match if their HTTP method and Pattern both match.
-*/
-type Mux struct {
- ms mStack
- rt router
-}
-
-// New creates a new Mux without any routes or middleware.
-func New() *Mux {
- mux := Mux{
- ms: mStack{
- stack: make([]mLayer, 0),
- pool: makeCPool(),
- },
- rt: router{
- routes: make([]route, 0),
- notFound: parseHandler(http.NotFound),
- },
- }
- mux.ms.router = &mux.rt
- return &mux
-}
-
-// ServeHTTP processes HTTP requests. Satisfies net/http.Handler.
-func (m *Mux) ServeHTTP(w http.ResponseWriter, r *http.Request) {
- stack := m.ms.alloc()
- stack.ServeHTTP(w, r)
- m.ms.release(stack)
-}
-
-// ServeHTTPC creates a context dependent request with the given Mux. Satisfies
-// the Handler interface.
-func (m *Mux) ServeHTTPC(c C, w http.ResponseWriter, r *http.Request) {
- stack := m.ms.alloc()
- stack.ServeHTTPC(c, w, r)
- m.ms.release(stack)
-}
-
-// Middleware Stack functions
-
-// Use appends the given middleware to the middleware stack.
-//
-// No attempt is made to enforce the uniqueness of middlewares. It is illegal to
-// call this function concurrently with active requests.
-func (m *Mux) Use(middleware MiddlewareType) {
- m.ms.Use(middleware)
-}
-
-// Insert inserts the given middleware immediately before a given existing
-// middleware in the stack. Returns an error if "before" cannot be found in the
-// current stack.
-//
-// No attempt is made to enforce the uniqueness of middlewares. If the insertion
-// point is ambiguous, the first (outermost) one is chosen. It is illegal to
-// call this function concurrently with active requests.
-func (m *Mux) Insert(middleware, before MiddlewareType) error {
- return m.ms.Insert(middleware, before)
-}
-
-// Abandon removes the given middleware from the middleware stack. Returns an
-// error if no such middleware can be found.
-//
-// If the name of the middleware to delete is ambiguous, the first (outermost)
-// one is chosen. It is illegal to call this function concurrently with active
-// requests.
-func (m *Mux) Abandon(middleware MiddlewareType) error {
- return m.ms.Abandon(middleware)
-}
-
-// Router functions
-
-type routerMiddleware struct {
- m *Mux
- c *C
- h http.Handler
-}
-
-func (rm routerMiddleware) ServeHTTP(w http.ResponseWriter, r *http.Request) {
- if rm.c.Env == nil {
- rm.c.Env = make(map[interface{}]interface{}, 1)
- }
- rm.c.Env[MatchKey] = rm.m.rt.getMatch(rm.c, w, r)
- rm.h.ServeHTTP(w, r)
-}
-
-/*
-Router is a middleware that performs routing and stores the resulting Match in
-Goji's environment. If a routing Match is present at the end of the middleware
-stack, that Match is used instead of re-routing.
-
-This middleware is especially useful to create post-routing middleware, e.g. a
-request logger which prints which pattern or handler was selected, or an
-authentication middleware which only applies to certain routes.
-
-If you use nested Muxes with explicit routing, you should be aware that the
-explicit routing information set by an outer Mux can be picked up by an inner
-Mux, inadvertently causing an infinite routing loop. If you use both explicit
-routing and nested Muxes, you should be sure to unset MatchKey before the inner
-Mux performs routing (or attach a Router to the inner Mux as well).
-*/
-func (m *Mux) Router(c *C, h http.Handler) http.Handler {
- return routerMiddleware{m, c, h}
-}
-
-/*
-Handle dispatches to the given handler when the pattern matches, regardless of
-HTTP method.
-
-This method is commonly used to implement sub-routing: an admin application, for
-instance, can expose a single handler that is attached to the main Mux by
-calling Handle("/admin/*", adminHandler) or similar. Note that this function
-doesn't strip this prefix from the path before forwarding it on (e.g., the
-handler will see the full path, including the "/admin/" part), but this
-functionality can easily be performed by an extra middleware layer.
-*/
-func (m *Mux) Handle(pattern PatternType, handler HandlerType) {
- m.rt.handleUntyped(pattern, mALL, handler)
-}
-
-// Connect dispatches to the given handler when the pattern matches and the HTTP
-// method is CONNECT.
-func (m *Mux) Connect(pattern PatternType, handler HandlerType) {
- m.rt.handleUntyped(pattern, mCONNECT, handler)
-}
-
-// Delete dispatches to the given handler when the pattern matches and the HTTP
-// method is DELETE.
-func (m *Mux) Delete(pattern PatternType, handler HandlerType) {
- m.rt.handleUntyped(pattern, mDELETE, handler)
-}
-
-// Get dispatches to the given handler when the pattern matches and the HTTP
-// method is GET.
-//
-// All GET handlers also transparently serve HEAD requests, since net/http will
-// take care of all the fiddly bits for you. If you wish to provide an alternate
-// implementation of HEAD, you should add a handler explicitly and place it
-// above your GET handler.
-func (m *Mux) Get(pattern PatternType, handler HandlerType) {
- m.rt.handleUntyped(pattern, mGET|mHEAD, handler)
-}
-
-// Head dispatches to the given handler when the pattern matches and the HTTP
-// method is HEAD.
-func (m *Mux) Head(pattern PatternType, handler HandlerType) {
- m.rt.handleUntyped(pattern, mHEAD, handler)
-}
-
-// Options dispatches to the given handler when the pattern matches and the HTTP
-// method is OPTIONS.
-func (m *Mux) Options(pattern PatternType, handler HandlerType) {
- m.rt.handleUntyped(pattern, mOPTIONS, handler)
-}
-
-// Patch dispatches to the given handler when the pattern matches and the HTTP
-// method is PATCH.
-func (m *Mux) Patch(pattern PatternType, handler HandlerType) {
- m.rt.handleUntyped(pattern, mPATCH, handler)
-}
-
-// Post dispatches to the given handler when the pattern matches and the HTTP
-// method is POST.
-func (m *Mux) Post(pattern PatternType, handler HandlerType) {
- m.rt.handleUntyped(pattern, mPOST, handler)
-}
-
-// Put dispatches to the given handler when the pattern matches and the HTTP
-// method is PUT.
-func (m *Mux) Put(pattern PatternType, handler HandlerType) {
- m.rt.handleUntyped(pattern, mPUT, handler)
-}
-
-// Trace dispatches to the given handler when the pattern matches and the HTTP
-// method is TRACE.
-func (m *Mux) Trace(pattern PatternType, handler HandlerType) {
- m.rt.handleUntyped(pattern, mTRACE, handler)
-}
-
-// NotFound sets the fallback (i.e., 404) handler for this mux.
-//
-// As a convenience, the context environment variable "goji.web.validMethods"
-// (also available as the constant ValidMethodsKey) will be set to the list of
-// HTTP methods that could have been routed had they been provided on an
-// otherwise identical request.
-func (m *Mux) NotFound(handler HandlerType) {
- m.rt.notFound = parseHandler(handler)
-}
-
-// Compile compiles the list of routes into bytecode. This only needs to be done
-// once after all the routes have been added, and will be called automatically
-// for you (at some performance cost on the first request) if you do not call it
-// explicitly.
-func (m *Mux) Compile() {
- m.rt.compile()
-}
diff --git a/vendor/github.com/zenazn/goji/web/pattern.go b/vendor/github.com/zenazn/goji/web/pattern.go
deleted file mode 100644
index 9f9fc857..00000000
--- a/vendor/github.com/zenazn/goji/web/pattern.go
+++ /dev/null
@@ -1,58 +0,0 @@
-package web
-
-import (
- "log"
- "net/http"
- "regexp"
-)
-
-// A Pattern determines whether or not a given request matches some criteria.
-// They are often used in routes, which are essentially (pattern, methodSet,
-// handler) tuples. If the method and pattern match, the given handler is used.
-//
-// Built-in implementations of this interface are used to implement regular
-// expression and string matching.
-type Pattern interface {
- // In practice, most real-world routes have a string prefix that can be
- // used to quickly determine if a pattern is an eligible match. The
- // router uses the result of this function to optimize away calls to the
- // full Match function, which is likely much more expensive to compute.
- // If your Pattern does not support prefixes, this function should
- // return the empty string.
- Prefix() string
- // Returns true if the request satisfies the pattern. This function is
- // free to examine both the request and the context to make this
- // decision. Match should not modify either argument, and since it will
- // potentially be called several times over the course of matching a
- // request, it should be reasonably efficient.
- Match(r *http.Request, c *C) bool
- // Run the pattern on the request and context, modifying the context as
- // necessary to bind URL parameters or other parsed state.
- Run(r *http.Request, c *C)
-}
-
-const unknownPattern = `Unknown pattern type %T. See http://godoc.org/github.com/zenazn/goji/web#PatternType for a list of acceptable types.`
-
-/*
-ParsePattern is used internally by Goji to parse route patterns. It is exposed
-publicly to make it easier to write thin wrappers around the built-in Pattern
-implementations.
-
-ParsePattern fatally exits (using log.Fatalf) if it is passed a value of an
-unexpected type (see the documentation for PatternType for a list of which types
-are accepted). It is the caller's responsibility to ensure that ParsePattern is
-called in a type-safe manner.
-*/
-func ParsePattern(raw PatternType) Pattern {
- switch v := raw.(type) {
- case Pattern:
- return v
- case *regexp.Regexp:
- return parseRegexpPattern(v)
- case string:
- return parseStringPattern(v)
- default:
- log.Fatalf(unknownPattern, v)
- panic("log.Fatalf does not return")
- }
-}
diff --git a/vendor/github.com/zenazn/goji/web/regexp_pattern.go b/vendor/github.com/zenazn/goji/web/regexp_pattern.go
deleted file mode 100644
index 95e7e097..00000000
--- a/vendor/github.com/zenazn/goji/web/regexp_pattern.go
+++ /dev/null
@@ -1,149 +0,0 @@
-package web
-
-import (
- "bytes"
- "fmt"
- "log"
- "net/http"
- "regexp"
- "regexp/syntax"
-)
-
-type regexpPattern struct {
- re *regexp.Regexp
- prefix string
- names []string
-}
-
-func (p regexpPattern) Prefix() string {
- return p.prefix
-}
-func (p regexpPattern) Match(r *http.Request, c *C) bool {
- return p.match(r, c, false)
-}
-func (p regexpPattern) Run(r *http.Request, c *C) {
- p.match(r, c, false)
-}
-
-func (p regexpPattern) match(r *http.Request, c *C, dryrun bool) bool {
- matches := p.re.FindStringSubmatch(r.URL.Path)
- if matches == nil || len(matches) == 0 {
- return false
- }
-
- if c == nil || dryrun || len(matches) == 1 {
- return true
- }
-
- if c.URLParams == nil {
- c.URLParams = make(map[string]string, len(matches)-1)
- }
- for i := 1; i < len(matches); i++ {
- c.URLParams[p.names[i]] = matches[i]
- }
- return true
-}
-
-func (p regexpPattern) String() string {
- return fmt.Sprintf("regexpPattern(%v)", p.re)
-}
-
-func (p regexpPattern) Raw() *regexp.Regexp {
- return p.re
-}
-
-/*
-I'm sorry, dear reader. I really am.
-
-The problem here is to take an arbitrary regular expression and:
-1. return a regular expression that is just like it, but left-anchored,
- preferring to return the original if possible.
-2. determine a string literal prefix that all matches of this regular expression
- have, much like regexp.Regexp.Prefix(). Unfortunately, Prefix() does not work
- in the presence of anchors, so we need to write it ourselves.
-
-What this actually means is that we need to sketch on the internals of the
-standard regexp library to forcefully extract the information we want.
-
-Unfortunately, regexp.Regexp hides a lot of its state, so our abstraction is
-going to be pretty leaky. The biggest leak is that we blindly assume that all
-regular expressions are perl-style, not POSIX. This is probably Mostly True, and
-I think most users of the library probably won't be able to notice.
-*/
-func sketchOnRegex(re *regexp.Regexp) (*regexp.Regexp, string) {
- rawRe := re.String()
- sRe, err := syntax.Parse(rawRe, syntax.Perl)
- if err != nil {
- log.Printf("WARN(web): unable to parse regexp %v as perl. "+
- "This route might behave unexpectedly.", re)
- return re, ""
- }
- sRe = sRe.Simplify()
- p, err := syntax.Compile(sRe)
- if err != nil {
- log.Printf("WARN(web): unable to compile regexp %v. This "+
- "route might behave unexpectedly.", re)
- return re, ""
- }
- if p.StartCond()&syntax.EmptyBeginText == 0 {
- // I hope doing this is always legal...
- newRe, err := regexp.Compile(`\A` + rawRe)
- if err != nil {
- log.Printf("WARN(web): unable to create a left-"+
- "anchored regexp from %v. This route might "+
- "behave unexpectedly", re)
- return re, ""
- }
- re = newRe
- }
-
- // Run the regular expression more or less by hand :(
- pc := uint32(p.Start)
- atStart := true
- i := &p.Inst[pc]
- var buf bytes.Buffer
-Sadness:
- for {
- switch i.Op {
- case syntax.InstEmptyWidth:
- if !atStart {
- break Sadness
- }
- case syntax.InstCapture, syntax.InstNop:
- // nop!
- case syntax.InstRune, syntax.InstRune1, syntax.InstRuneAny,
- syntax.InstRuneAnyNotNL:
-
- atStart = false
- if len(i.Rune) != 1 ||
- syntax.Flags(i.Arg)&syntax.FoldCase != 0 {
- break Sadness
- }
- buf.WriteRune(i.Rune[0])
- default:
- break Sadness
- }
- pc = i.Out
- i = &p.Inst[pc]
- }
- return re, buf.String()
-}
-
-func parseRegexpPattern(re *regexp.Regexp) regexpPattern {
- re, prefix := sketchOnRegex(re)
- rnames := re.SubexpNames()
- // We have to make our own copy since package regexp forbids us
- // from scribbling over the slice returned by SubexpNames().
- names := make([]string, len(rnames))
- for i, rname := range rnames {
- if rname == "" {
- rname = fmt.Sprintf("$%d", i)
- }
- names[i] = rname
- }
- return regexpPattern{
- re: re,
- prefix: prefix,
- names: names,
- }
-}
diff --git a/vendor/github.com/zenazn/goji/web/router.go b/vendor/github.com/zenazn/goji/web/router.go
deleted file mode 100644
index 1fbc41ff..00000000
--- a/vendor/github.com/zenazn/goji/web/router.go
+++ /dev/null
@@ -1,154 +0,0 @@
-package web
-
-import (
- "net/http"
- "sort"
- "strings"
- "sync"
-)
-
-type method int
-
-const (
- mCONNECT method = 1 << iota
- mDELETE
- mGET
- mHEAD
- mOPTIONS
- mPATCH
- mPOST
- mPUT
- mTRACE
- // We only natively support the methods above, but we pass through other
- // methods. This constant pretty much only exists for the sake of mALL.
- mIDK
-
- mALL method = mCONNECT | mDELETE | mGET | mHEAD | mOPTIONS | mPATCH |
- mPOST | mPUT | mTRACE | mIDK
-)
-
-// The key used to communicate to the NotFound handler what methods would have
-// been allowed if they'd been provided.
-const ValidMethodsKey = "goji.web.ValidMethods"
-
-var validMethodsMap = map[string]method{
- "CONNECT": mCONNECT,
- "DELETE": mDELETE,
- "GET": mGET,
- "HEAD": mHEAD,
- "OPTIONS": mOPTIONS,
- "PATCH": mPATCH,
- "POST": mPOST,
- "PUT": mPUT,
- "TRACE": mTRACE,
-}
-
-type route struct {
- prefix string
- method method
- pattern Pattern
- handler Handler
-}
-
-type router struct {
- lock sync.Mutex
- routes []route
- notFound Handler
- machine *routeMachine
-}
-
-func httpMethod(mname string) method {
- if method, ok := validMethodsMap[mname]; ok {
- return method
- }
- return mIDK
-}
-
-func (rt *router) compile() *routeMachine {
- rt.lock.Lock()
- defer rt.lock.Unlock()
- sm := routeMachine{
- sm: compile(rt.routes),
- routes: rt.routes,
- }
- rt.setMachine(&sm)
- return &sm
-}
-
-func (rt *router) getMatch(c *C, w http.ResponseWriter, r *http.Request) Match {
- rm := rt.getMachine()
- if rm == nil {
- rm = rt.compile()
- }
-
- methods, route := rm.route(c, w, r)
- if route != nil {
- return Match{
- Pattern: route.pattern,
- Handler: route.handler,
- }
- }
-
- if methods == 0 {
- return Match{Handler: rt.notFound}
- }
-
- var methodsList = make([]string, 0)
- for mname, meth := range validMethodsMap {
- if methods&meth != 0 {
- methodsList = append(methodsList, mname)
- }
- }
- sort.Strings(methodsList)
-
- if c.Env == nil {
- c.Env = map[interface{}]interface{}{
- ValidMethodsKey: methodsList,
- }
- } else {
- c.Env[ValidMethodsKey] = methodsList
- }
- return Match{Handler: rt.notFound}
-}
-
-func (rt *router) route(c *C, w http.ResponseWriter, r *http.Request) {
- match := GetMatch(*c)
- if match.Handler == nil {
- match = rt.getMatch(c, w, r)
- }
- match.Handler.ServeHTTPC(*c, w, r)
-}
-
-func (rt *router) handleUntyped(p PatternType, m method, h HandlerType) {
- rt.handle(ParsePattern(p), m, parseHandler(h))
-}
-
-func (rt *router) handle(p Pattern, m method, h Handler) {
- rt.lock.Lock()
- defer rt.lock.Unlock()
-
- // Calculate the sorted insertion point, because there's no reason to do
- // swapping hijinks if we're already making a copy. We need to use
- // bubble sort because we can only compare adjacent elements.
- pp := p.Prefix()
- var i int
- for i = len(rt.routes); i > 0; i-- {
- rip := rt.routes[i-1].prefix
- if rip <= pp || strings.HasPrefix(rip, pp) {
- break
- }
- }
-
- newRoutes := make([]route, len(rt.routes)+1)
- copy(newRoutes, rt.routes[:i])
- newRoutes[i] = route{
- prefix: pp,
- method: m,
- pattern: p,
- handler: h,
- }
- copy(newRoutes[i+1:], rt.routes[i:])
-
- rt.setMachine(nil)
- rt.routes = newRoutes
-}
diff --git a/vendor/github.com/zenazn/goji/web/string_pattern.go b/vendor/github.com/zenazn/goji/web/string_pattern.go
deleted file mode 100644
index aa9b33a0..00000000
--- a/vendor/github.com/zenazn/goji/web/string_pattern.go
+++ /dev/null
@@ -1,137 +0,0 @@
-package web
-
-import (
- "fmt"
- "net/http"
- "regexp"
- "strings"
-)
-
-// stringPattern is a struct describing
-type stringPattern struct {
- raw string
- pats []string
- breaks []byte
- literals []string
- wildcard bool
-}
-
-func (s stringPattern) Prefix() string {
- return s.literals[0]
-}
-func (s stringPattern) Match(r *http.Request, c *C) bool {
- return s.match(r, c, true)
-}
-func (s stringPattern) Run(r *http.Request, c *C) {
- s.match(r, c, false)
-}
-func (s stringPattern) match(r *http.Request, c *C, dryrun bool) bool {
- path := r.URL.Path
- var matches map[string]string
- if !dryrun {
- if s.wildcard {
- matches = make(map[string]string, len(s.pats)+1)
- } else if len(s.pats) != 0 {
- matches = make(map[string]string, len(s.pats))
- }
- }
- for i, pat := range s.pats {
- sli := s.literals[i]
- if !strings.HasPrefix(path, sli) {
- return false
- }
- path = path[len(sli):]
-
- m := 0
- bc := s.breaks[i]
- for ; m < len(path); m++ {
- if path[m] == bc || path[m] == '/' {
- break
- }
- }
- if m == 0 {
- // Empty strings are not matches, otherwise routes like
- // "/:foo" would match the path "/"
- return false
- }
- if !dryrun {
- matches[pat] = path[:m]
- }
- path = path[m:]
- }
- // There's exactly one more literal than pat.
- tail := s.literals[len(s.pats)]
- if s.wildcard {
- if !strings.HasPrefix(path, tail) {
- return false
- }
- if !dryrun {
- matches["*"] = path[len(tail)-1:]
- }
- } else if path != tail {
- return false
- }
-
- if c == nil || dryrun {
- return true
- }
-
- if c.URLParams == nil {
- c.URLParams = matches
- } else {
- for k, v := range matches {
- c.URLParams[k] = v
- }
- }
- return true
-}
-
-func (s stringPattern) String() string {
- return fmt.Sprintf("stringPattern(%q)", s.raw)
-}
-
-func (s stringPattern) Raw() string {
- return s.raw
-}
-
-// "Break characters" are characters that can end patterns. They are not allowed
-// to appear in pattern names. "/" was chosen because it is the standard path
-// separator, and "." was chosen because it often delimits file extensions. ";"
-// and "," were chosen because Section 3.3 of RFC 3986 suggests their use.
-const bc = "/.;,"
-
-var patternRe = regexp.MustCompile(`[` + bc + `]:([^` + bc + `]+)`)
-
-func parseStringPattern(s string) stringPattern {
- raw := s
- var wildcard bool
- if strings.HasSuffix(s, "/*") {
- s = s[:len(s)-1]
- wildcard = true
- }
-
- matches := patternRe.FindAllStringSubmatchIndex(s, -1)
- pats := make([]string, len(matches))
- breaks := make([]byte, len(matches))
- literals := make([]string, len(matches)+1)
- n := 0
- for i, match := range matches {
- a, b := match[2], match[3]
- literals[i] = s[n : a-1] // Need to leave off the colon
- pats[i] = s[a:b]
- if b == len(s) {
- breaks[i] = '/'
- } else {
- breaks[i] = s[b]
- }
- n = b
- }
- literals[len(matches)] = s[n:]
- return stringPattern{
- raw: raw,
- pats: pats,
- breaks: breaks,
- literals: literals,
- wildcard: wildcard,
- }
-}
diff --git a/vendor/github.com/zenazn/goji/web/web.go b/vendor/github.com/zenazn/goji/web/web.go
deleted file mode 100644
index 21f6fcc4..00000000
--- a/vendor/github.com/zenazn/goji/web/web.go
+++ /dev/null
@@ -1,112 +0,0 @@
-/*
-Package web provides a fast and flexible middleware stack and mux.
-
-This package attempts to solve three problems that net/http does not. First, it
-allows you to specify flexible patterns, including routes with named parameters
-and regular expressions. Second, it allows you to write reconfigurable
-middleware stacks. And finally, it allows you to attach additional context to
-requests, in a manner that can be manipulated by both compliant middleware and
-handlers.
-*/
-package web
-
-import (
- "net/http"
-)
-
-/*
-C is a request-local context object which is threaded through all compliant
-middleware layers and given to the final request handler.
-*/
-type C struct {
- // URLParams is a map of variables extracted from the URL (typically
- // from the path portion) during routing. See the documentation for the
- // URL Pattern you are using (or the documentation for PatternType for
- // the case of standard pattern types) for more information about how
- // variables are extracted and named.
- URLParams map[string]string
- // Env is a free-form environment for storing request-local data. Keys
- // may be arbitrary types that support equality, however package-private
- // types with type-safe accessors provide a convenient way for packages
- // to mediate access to their request-local data.
- Env map[interface{}]interface{}
-}
-
-// Handler is similar to net/http's http.Handler, but also accepts a Goji
-// context object.
-type Handler interface {
- ServeHTTPC(C, http.ResponseWriter, *http.Request)
-}
-
-// HandlerFunc is similar to net/http's http.HandlerFunc, but supports a context
-// object. Implements both http.Handler and Handler.
-type HandlerFunc func(C, http.ResponseWriter, *http.Request)
-
-// ServeHTTP implements http.Handler, allowing HandlerFunc's to be used with
-// net/http and other compliant routers. When used in this way, the underlying
-// function will be passed an empty context.
-func (h HandlerFunc) ServeHTTP(w http.ResponseWriter, r *http.Request) {
- h(C{}, w, r)
-}
-
-// ServeHTTPC implements Handler.
-func (h HandlerFunc) ServeHTTPC(c C, w http.ResponseWriter, r *http.Request) {
- h(c, w, r)
-}
-
-/*
-PatternType is the type denoting Patterns and types that Goji internally
-converts to Pattern (via the ParsePattern function). In order to provide an
-expressive API, this type is an alias for interface{} that is named for the
-purposes of documentation, however only the following concrete types are
-accepted:
- - types that implement Pattern
- - string, which is interpreted as a Sinatra-like URL pattern. In
- particular, the following syntax is recognized:
- - a path segment starting with a colon will match any
- string placed at that position. e.g., "/:name" will match
- "/carl", binding "name" to "carl".
- - a pattern ending with "/*" will match any route with that
- prefix. For instance, the pattern "/u/:name/*" will match
- "/u/carl/" and "/u/carl/projects/123", but not "/u/carl"
- (because there is no trailing slash). In addition to any names
- bound in the pattern, the special key "*" is bound to the
- unmatched tail of the match, but including the leading "/". So
- for the two matching examples above, "*" would be bound to "/"
- and "/projects/123" respectively.
- Unlike http.ServeMux's patterns, string patterns support neither the
- "rooted subtree" behavior nor Host-specific routes. Users who require
- either of these features are encouraged to compose package http's mux
- with the mux provided by this package.
- - regexp.Regexp, which is assumed to be a Perl-style regular expression
- that is anchored on the left (i.e., the beginning of the string). If
- your regular expression is not anchored on the left, a
- hopefully-identical left-anchored regular expression will be created
- and used instead.
-
- Capturing groups will be converted into bound URL parameters in
- URLParams. If the capturing group is named, that name will be used;
- otherwise the special identifiers "$1", "$2", etc. will be used.
-*/
-type PatternType interface{}
-
-/*
-HandlerType is the type of Handlers and types that Goji internally converts to
-Handler. In order to provide an expressive API, this type is an alias for
-interface{} that is named for the purposes of documentation, however only the
-following concrete types are accepted:
- - types that implement http.Handler
- - types that implement Handler
- - func(http.ResponseWriter, *http.Request)
- - func(web.C, http.ResponseWriter, *http.Request)
-*/
-type HandlerType interface{}
-
-/*
-MiddlewareType is the type of Goji middleware. In order to provide an expressive
-API, this type is an alias for interface{} that is named for the purposes of
-documentation, however only the following concrete types are accepted:
- - func(http.Handler) http.Handler
- - func(*web.C, http.Handler) http.Handler
-*/
-type MiddlewareType interface{}
diff --git a/vendor/golang.org/x/crypto/bcrypt/base64.go b/vendor/golang.org/x/crypto/bcrypt/base64.go
deleted file mode 100644
index fc311609..00000000
--- a/vendor/golang.org/x/crypto/bcrypt/base64.go
+++ /dev/null
@@ -1,35 +0,0 @@
-// Copyright 2011 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package bcrypt
-
-import "encoding/base64"
-
-const alphabet = "./ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
-
-var bcEncoding = base64.NewEncoding(alphabet)
-
-func base64Encode(src []byte) []byte {
- n := bcEncoding.EncodedLen(len(src))
- dst := make([]byte, n)
- bcEncoding.Encode(dst, src)
- for dst[n-1] == '=' {
- n--
- }
- return dst[:n]
-}
-
-func base64Decode(src []byte) ([]byte, error) {
- numOfEquals := 4 - (len(src) % 4)
- for i := 0; i < numOfEquals; i++ {
- src = append(src, '=')
- }
-
- dst := make([]byte, bcEncoding.DecodedLen(len(src)))
- n, err := bcEncoding.Decode(dst, src)
- if err != nil {
- return nil, err
- }
- return dst[:n], nil
-}
diff --git a/vendor/golang.org/x/crypto/bcrypt/bcrypt.go b/vendor/golang.org/x/crypto/bcrypt/bcrypt.go
deleted file mode 100644
index aeb73f81..00000000
--- a/vendor/golang.org/x/crypto/bcrypt/bcrypt.go
+++ /dev/null
@@ -1,295 +0,0 @@
-// Copyright 2011 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// Package bcrypt implements Provos and Maziรจres's bcrypt adaptive hashing
-// algorithm. See http://www.usenix.org/event/usenix99/provos/provos.pdf
-package bcrypt // import "golang.org/x/crypto/bcrypt"
-
-// The code is a port of Provos and Maziรจres's C implementation.
-import (
- "crypto/rand"
- "crypto/subtle"
- "errors"
- "fmt"
- "io"
- "strconv"
-
- "golang.org/x/crypto/blowfish"
-)
-
-const (
- MinCost int = 4 // the minimum allowable cost as passed in to GenerateFromPassword
- MaxCost int = 31 // the maximum allowable cost as passed in to GenerateFromPassword
- DefaultCost int = 10 // the cost that will actually be set if a cost below MinCost is passed into GenerateFromPassword
-)
-
-// The error returned from CompareHashAndPassword when a password and hash do
-// not match.
-var ErrMismatchedHashAndPassword = errors.New("crypto/bcrypt: hashedPassword is not the hash of the given password")
-
-// The error returned from CompareHashAndPassword when a hash is too short to
-// be a bcrypt hash.
-var ErrHashTooShort = errors.New("crypto/bcrypt: hashedSecret too short to be a bcrypted password")
-
-// The error returned from CompareHashAndPassword when a hash was created with
-// a bcrypt algorithm newer than this implementation.
-type HashVersionTooNewError byte
-
-func (hv HashVersionTooNewError) Error() string {
- return fmt.Sprintf("crypto/bcrypt: bcrypt algorithm version '%c' requested is newer than current version '%c'", byte(hv), majorVersion)
-}
-
-// The error returned from CompareHashAndPassword when a hash starts with something other than '$'
-type InvalidHashPrefixError byte
-
-func (ih InvalidHashPrefixError) Error() string {
- return fmt.Sprintf("crypto/bcrypt: bcrypt hashes must start with '$', but hashedSecret started with '%c'", byte(ih))
-}
-
-type InvalidCostError int
-
-func (ic InvalidCostError) Error() string {
- return fmt.Sprintf("crypto/bcrypt: cost %d is outside allowed range (%d,%d)", int(ic), int(MinCost), int(MaxCost))
-}
-
-const (
- majorVersion = '2'
- minorVersion = 'a'
- maxSaltSize = 16
- maxCryptedHashSize = 23
- encodedSaltSize = 22
- encodedHashSize = 31
- minHashSize = 59
-)
-
-// magicCipherData is an IV for the 64 Blowfish encryption calls in
-// bcrypt(). It's the string "OrpheanBeholderScryDoubt" in big-endian bytes.
-var magicCipherData = []byte{
- 0x4f, 0x72, 0x70, 0x68,
- 0x65, 0x61, 0x6e, 0x42,
- 0x65, 0x68, 0x6f, 0x6c,
- 0x64, 0x65, 0x72, 0x53,
- 0x63, 0x72, 0x79, 0x44,
- 0x6f, 0x75, 0x62, 0x74,
-}
-
-type hashed struct {
- hash []byte
- salt []byte
- cost int // allowed range is MinCost to MaxCost
- major byte
- minor byte
-}
-
-// GenerateFromPassword returns the bcrypt hash of the password at the given
-// cost. If the cost given is less than MinCost, the cost will be set to
-// DefaultCost, instead. Use CompareHashAndPassword, as defined in this package,
-// to compare the returned hashed password with its cleartext version.
-func GenerateFromPassword(password []byte, cost int) ([]byte, error) {
- p, err := newFromPassword(password, cost)
- if err != nil {
- return nil, err
- }
- return p.Hash(), nil
-}
-
-// CompareHashAndPassword compares a bcrypt hashed password with its possible
-// plaintext equivalent. Returns nil on success, or an error on failure.
-func CompareHashAndPassword(hashedPassword, password []byte) error {
- p, err := newFromHash(hashedPassword)
- if err != nil {
- return err
- }
-
- otherHash, err := bcrypt(password, p.cost, p.salt)
- if err != nil {
- return err
- }
-
- otherP := &hashed{otherHash, p.salt, p.cost, p.major, p.minor}
- if subtle.ConstantTimeCompare(p.Hash(), otherP.Hash()) == 1 {
- return nil
- }
-
- return ErrMismatchedHashAndPassword
-}
-
-// Cost returns the hashing cost used to create the given hashed
-// password. When, in the future, the hashing cost of a password system needs
-// to be increased in order to adjust for greater computational power, this
-// function allows one to establish which passwords need to be updated.
-func Cost(hashedPassword []byte) (int, error) {
- p, err := newFromHash(hashedPassword)
- if err != nil {
- return 0, err
- }
- return p.cost, nil
-}
-
-func newFromPassword(password []byte, cost int) (*hashed, error) {
- if cost < MinCost {
- cost = DefaultCost
- }
- p := new(hashed)
- p.major = majorVersion
- p.minor = minorVersion
-
- err := checkCost(cost)
- if err != nil {
- return nil, err
- }
- p.cost = cost
-
- unencodedSalt := make([]byte, maxSaltSize)
- _, err = io.ReadFull(rand.Reader, unencodedSalt)
- if err != nil {
- return nil, err
- }
-
- p.salt = base64Encode(unencodedSalt)
- hash, err := bcrypt(password, p.cost, p.salt)
- if err != nil {
- return nil, err
- }
- p.hash = hash
- return p, err
-}
-
-func newFromHash(hashedSecret []byte) (*hashed, error) {
- if len(hashedSecret) < minHashSize {
- return nil, ErrHashTooShort
- }
- p := new(hashed)
- n, err := p.decodeVersion(hashedSecret)
- if err != nil {
- return nil, err
- }
- hashedSecret = hashedSecret[n:]
- n, err = p.decodeCost(hashedSecret)
- if err != nil {
- return nil, err
- }
- hashedSecret = hashedSecret[n:]
-
- // The "+2" is here because we'll have to append at most 2 '=' to the salt
- // when base64 decoding it in expensiveBlowfishSetup().
- p.salt = make([]byte, encodedSaltSize, encodedSaltSize+2)
- copy(p.salt, hashedSecret[:encodedSaltSize])
-
- hashedSecret = hashedSecret[encodedSaltSize:]
- p.hash = make([]byte, len(hashedSecret))
- copy(p.hash, hashedSecret)
-
- return p, nil
-}
-
-func bcrypt(password []byte, cost int, salt []byte) ([]byte, error) {
- cipherData := make([]byte, len(magicCipherData))
- copy(cipherData, magicCipherData)
-
- c, err := expensiveBlowfishSetup(password, uint32(cost), salt)
- if err != nil {
- return nil, err
- }
-
- for i := 0; i < 24; i += 8 {
- for j := 0; j < 64; j++ {
- c.Encrypt(cipherData[i:i+8], cipherData[i:i+8])
- }
- }
-
- // Bug compatibility with C bcrypt implementations. We only encode 23 of
- // the 24 bytes encrypted.
- hsh := base64Encode(cipherData[:maxCryptedHashSize])
- return hsh, nil
-}
-
-func expensiveBlowfishSetup(key []byte, cost uint32, salt []byte) (*blowfish.Cipher, error) {
- csalt, err := base64Decode(salt)
- if err != nil {
- return nil, err
- }
-
- // Bug compatibility with C bcrypt implementations. They use the trailing
- // NULL in the key string during expansion.
- // We copy the key to prevent changing the underlying array.
- ckey := append(key[:len(key):len(key)], 0)
-
- c, err := blowfish.NewSaltedCipher(ckey, csalt)
- if err != nil {
- return nil, err
- }
-
- var i, rounds uint64
- rounds = 1 << cost
- for i = 0; i < rounds; i++ {
- blowfish.ExpandKey(ckey, c)
- blowfish.ExpandKey(csalt, c)
- }
-
- return c, nil
-}
-
-func (p *hashed) Hash() []byte {
- arr := make([]byte, 60)
- arr[0] = '$'
- arr[1] = p.major
- n := 2
- if p.minor != 0 {
- arr[2] = p.minor
- n = 3
- }
- arr[n] = '$'
- n++
- copy(arr[n:], []byte(fmt.Sprintf("%02d", p.cost)))
- n += 2
- arr[n] = '$'
- n++
- copy(arr[n:], p.salt)
- n += encodedSaltSize
- copy(arr[n:], p.hash)
- n += encodedHashSize
- return arr[:n]
-}
-
-func (p *hashed) decodeVersion(sbytes []byte) (int, error) {
- if sbytes[0] != '$' {
- return -1, InvalidHashPrefixError(sbytes[0])
- }
- if sbytes[1] > majorVersion {
- return -1, HashVersionTooNewError(sbytes[1])
- }
- p.major = sbytes[1]
- n := 3
- if sbytes[2] != '$' {
- p.minor = sbytes[2]
- n++
- }
- return n, nil
-}
-
-// sbytes should begin where decodeVersion left off.
-func (p *hashed) decodeCost(sbytes []byte) (int, error) {
- cost, err := strconv.Atoi(string(sbytes[0:2]))
- if err != nil {
- return -1, err
- }
- err = checkCost(cost)
- if err != nil {
- return -1, err
- }
- p.cost = cost
- return 3, nil
-}
-
-func (p *hashed) String() string {
- return fmt.Sprintf("&{hash: %#v, salt: %#v, cost: %d, major: %c, minor: %c}", string(p.hash), p.salt, p.cost, p.major, p.minor)
-}
-
-func checkCost(cost int) error {
- if cost < MinCost || cost > MaxCost {
- return InvalidCostError(cost)
- }
- return nil
-}
diff --git a/vendor/golang.org/x/crypto/blowfish/block.go b/vendor/golang.org/x/crypto/blowfish/block.go
deleted file mode 100644
index 9d80f195..00000000
--- a/vendor/golang.org/x/crypto/blowfish/block.go
+++ /dev/null
@@ -1,159 +0,0 @@
-// Copyright 2010 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package blowfish
-
-// getNextWord returns the next big-endian uint32 value from the byte slice
-// at the given position in a circular manner, updating the position.
-func getNextWord(b []byte, pos *int) uint32 {
- var w uint32
- j := *pos
- for i := 0; i < 4; i++ {
- w = w<<8 | uint32(b[j])
- j++
- if j >= len(b) {
- j = 0
- }
- }
- *pos = j
- return w
-}
-
-// ExpandKey performs a key expansion on the given *Cipher. Specifically, it
-// performs the Blowfish algorithm's key schedule which sets up the *Cipher's
-// pi and substitution tables for calls to Encrypt. This is used, primarily,
-// by the bcrypt package to reuse the Blowfish key schedule during its
-// set up. It's unlikely that you need to use this directly.
-func ExpandKey(key []byte, c *Cipher) {
- j := 0
- for i := 0; i < 18; i++ {
- // Using inlined getNextWord for performance.
- var d uint32
- for k := 0; k < 4; k++ {
- d = d<<8 | uint32(key[j])
- j++
- if j >= len(key) {
- j = 0
- }
- }
- c.p[i] ^= d
- }
-
- var l, r uint32
- for i := 0; i < 18; i += 2 {
- l, r = encryptBlock(l, r, c)
- c.p[i], c.p[i+1] = l, r
- }
-
- for i := 0; i < 256; i += 2 {
- l, r = encryptBlock(l, r, c)
- c.s0[i], c.s0[i+1] = l, r
- }
- for i := 0; i < 256; i += 2 {
- l, r = encryptBlock(l, r, c)
- c.s1[i], c.s1[i+1] = l, r
- }
- for i := 0; i < 256; i += 2 {
- l, r = encryptBlock(l, r, c)
- c.s2[i], c.s2[i+1] = l, r
- }
- for i := 0; i < 256; i += 2 {
- l, r = encryptBlock(l, r, c)
- c.s3[i], c.s3[i+1] = l, r
- }
-}
-
-// This is similar to ExpandKey, but folds the salt during the key
-// schedule. While ExpandKey is essentially expandKeyWithSalt with an all-zero
-// salt passed in, reusing ExpandKey turns out to be a place of inefficiency
-// and specializing it here is useful.
-func expandKeyWithSalt(key []byte, salt []byte, c *Cipher) {
- j := 0
- for i := 0; i < 18; i++ {
- c.p[i] ^= getNextWord(key, &j)
- }
-
- j = 0
- var l, r uint32
- for i := 0; i < 18; i += 2 {
- l ^= getNextWord(salt, &j)
- r ^= getNextWord(salt, &j)
- l, r = encryptBlock(l, r, c)
- c.p[i], c.p[i+1] = l, r
- }
-
- for i := 0; i < 256; i += 2 {
- l ^= getNextWord(salt, &j)
- r ^= getNextWord(salt, &j)
- l, r = encryptBlock(l, r, c)
- c.s0[i], c.s0[i+1] = l, r
- }
-
- for i := 0; i < 256; i += 2 {
- l ^= getNextWord(salt, &j)
- r ^= getNextWord(salt, &j)
- l, r = encryptBlock(l, r, c)
- c.s1[i], c.s1[i+1] = l, r
- }
-
- for i := 0; i < 256; i += 2 {
- l ^= getNextWord(salt, &j)
- r ^= getNextWord(salt, &j)
- l, r = encryptBlock(l, r, c)
- c.s2[i], c.s2[i+1] = l, r
- }
-
- for i := 0; i < 256; i += 2 {
- l ^= getNextWord(salt, &j)
- r ^= getNextWord(salt, &j)
- l, r = encryptBlock(l, r, c)
- c.s3[i], c.s3[i+1] = l, r
- }
-}
-
-func encryptBlock(l, r uint32, c *Cipher) (uint32, uint32) {
- xl, xr := l, r
- xl ^= c.p[0]
- xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[1]
- xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[2]
- xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[3]
- xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[4]
- xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[5]
- xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[6]
- xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[7]
- xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[8]
- xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[9]
- xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[10]
- xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[11]
- xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[12]
- xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[13]
- xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[14]
- xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[15]
- xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[16]
- xr ^= c.p[17]
- return xr, xl
-}
-
-func decryptBlock(l, r uint32, c *Cipher) (uint32, uint32) {
- xl, xr := l, r
- xl ^= c.p[17]
- xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[16]
- xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[15]
- xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[14]
- xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[13]
- xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[12]
- xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[11]
- xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[10]
- xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[9]
- xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[8]
- xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[7]
- xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[6]
- xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[5]
- xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[4]
- xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[3]
- xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[2]
- xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[1]
- xr ^= c.p[0]
- return xr, xl
-}
diff --git a/vendor/golang.org/x/crypto/blowfish/cipher.go b/vendor/golang.org/x/crypto/blowfish/cipher.go
deleted file mode 100644
index 213bf204..00000000
--- a/vendor/golang.org/x/crypto/blowfish/cipher.go
+++ /dev/null
@@ -1,99 +0,0 @@
-// Copyright 2010 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// Package blowfish implements Bruce Schneier's Blowfish encryption algorithm.
-//
-// Blowfish is a legacy cipher and its short block size makes it vulnerable to
-// birthday bound attacks (see https://sweet32.info). It should only be used
-// where compatibility with legacy systems, not security, is the goal.
-//
-// Deprecated: any new system should use AES (from crypto/aes, if necessary in
-// an AEAD mode like crypto/cipher.NewGCM) or XChaCha20-Poly1305 (from
-// golang.org/x/crypto/chacha20poly1305).
-package blowfish // import "golang.org/x/crypto/blowfish"
-
-// The code is a port of Bruce Schneier's C implementation.
-// See https://www.schneier.com/blowfish.html.
-
-import "strconv"
-
-// The Blowfish block size in bytes.
-const BlockSize = 8
-
-// A Cipher is an instance of Blowfish encryption using a particular key.
-type Cipher struct {
- p [18]uint32
- s0, s1, s2, s3 [256]uint32
-}
-
-type KeySizeError int
-
-func (k KeySizeError) Error() string {
- return "crypto/blowfish: invalid key size " + strconv.Itoa(int(k))
-}
-
-// NewCipher creates and returns a Cipher.
-// The key argument should be the Blowfish key, from 1 to 56 bytes.
-func NewCipher(key []byte) (*Cipher, error) {
- var result Cipher
- if k := len(key); k < 1 || k > 56 {
- return nil, KeySizeError(k)
- }
- initCipher(&result)
- ExpandKey(key, &result)
- return &result, nil
-}
-
-// NewSaltedCipher creates a returns a Cipher that folds a salt into its key
-// schedule. For most purposes, NewCipher, instead of NewSaltedCipher, is
-// sufficient and desirable. For bcrypt compatibility, the key can be over 56
-// bytes.
-func NewSaltedCipher(key, salt []byte) (*Cipher, error) {
- if len(salt) == 0 {
- return NewCipher(key)
- }
- var result Cipher
- if k := len(key); k < 1 {
- return nil, KeySizeError(k)
- }
- initCipher(&result)
- expandKeyWithSalt(key, salt, &result)
- return &result, nil
-}
-
-// BlockSize returns the Blowfish block size, 8 bytes.
-// It is necessary to satisfy the Block interface in the
-// package "crypto/cipher".
-func (c *Cipher) BlockSize() int { return BlockSize }
-
-// Encrypt encrypts the 8-byte buffer src using the key k
-// and stores the result in dst.
-// Note that for amounts of data larger than a block,
-// it is not safe to just call Encrypt on successive blocks;
-// instead, use an encryption mode like CBC (see crypto/cipher/cbc.go).
-func (c *Cipher) Encrypt(dst, src []byte) {
- l := uint32(src[0])<<24 | uint32(src[1])<<16 | uint32(src[2])<<8 | uint32(src[3])
- r := uint32(src[4])<<24 | uint32(src[5])<<16 | uint32(src[6])<<8 | uint32(src[7])
- l, r = encryptBlock(l, r, c)
- dst[0], dst[1], dst[2], dst[3] = byte(l>>24), byte(l>>16), byte(l>>8), byte(l)
- dst[4], dst[5], dst[6], dst[7] = byte(r>>24), byte(r>>16), byte(r>>8), byte(r)
-}
-
-// Decrypt decrypts the 8-byte buffer src using the key k
-// and stores the result in dst.
-func (c *Cipher) Decrypt(dst, src []byte) {
- l := uint32(src[0])<<24 | uint32(src[1])<<16 | uint32(src[2])<<8 | uint32(src[3])
- r := uint32(src[4])<<24 | uint32(src[5])<<16 | uint32(src[6])<<8 | uint32(src[7])
- l, r = decryptBlock(l, r, c)
- dst[0], dst[1], dst[2], dst[3] = byte(l>>24), byte(l>>16), byte(l>>8), byte(l)
- dst[4], dst[5], dst[6], dst[7] = byte(r>>24), byte(r>>16), byte(r>>8), byte(r)
-}
-
-func initCipher(c *Cipher) {
- copy(c.p[0:], p[0:])
- copy(c.s0[0:], s0[0:])
- copy(c.s1[0:], s1[0:])
- copy(c.s2[0:], s2[0:])
- copy(c.s3[0:], s3[0:])
-}
diff --git a/vendor/golang.org/x/crypto/blowfish/const.go b/vendor/golang.org/x/crypto/blowfish/const.go
deleted file mode 100644
index d0407759..00000000
--- a/vendor/golang.org/x/crypto/blowfish/const.go
+++ /dev/null
@@ -1,199 +0,0 @@
-// Copyright 2010 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// The startup permutation array and substitution boxes.
-// They are the hexadecimal digits of PI; see:
-// https://www.schneier.com/code/constants.txt.
-
-package blowfish
-
-var s0 = [256]uint32{
- 0xd1310ba6, 0x98dfb5ac, 0x2ffd72db, 0xd01adfb7, 0xb8e1afed, 0x6a267e96,
- 0xba7c9045, 0xf12c7f99, 0x24a19947, 0xb3916cf7, 0x0801f2e2, 0x858efc16,
- 0x636920d8, 0x71574e69, 0xa458fea3, 0xf4933d7e, 0x0d95748f, 0x728eb658,
- 0x718bcd58, 0x82154aee, 0x7b54a41d, 0xc25a59b5, 0x9c30d539, 0x2af26013,
- 0xc5d1b023, 0x286085f0, 0xca417918, 0xb8db38ef, 0x8e79dcb0, 0x603a180e,
- 0x6c9e0e8b, 0xb01e8a3e, 0xd71577c1, 0xbd314b27, 0x78af2fda, 0x55605c60,
- 0xe65525f3, 0xaa55ab94, 0x57489862, 0x63e81440, 0x55ca396a, 0x2aab10b6,
- 0xb4cc5c34, 0x1141e8ce, 0xa15486af, 0x7c72e993, 0xb3ee1411, 0x636fbc2a,
- 0x2ba9c55d, 0x741831f6, 0xce5c3e16, 0x9b87931e, 0xafd6ba33, 0x6c24cf5c,
- 0x7a325381, 0x28958677, 0x3b8f4898, 0x6b4bb9af, 0xc4bfe81b, 0x66282193,
- 0x61d809cc, 0xfb21a991, 0x487cac60, 0x5dec8032, 0xef845d5d, 0xe98575b1,
- 0xdc262302, 0xeb651b88, 0x23893e81, 0xd396acc5, 0x0f6d6ff3, 0x83f44239,
- 0x2e0b4482, 0xa4842004, 0x69c8f04a, 0x9e1f9b5e, 0x21c66842, 0xf6e96c9a,
- 0x670c9c61, 0xabd388f0, 0x6a51a0d2, 0xd8542f68, 0x960fa728, 0xab5133a3,
- 0x6eef0b6c, 0x137a3be4, 0xba3bf050, 0x7efb2a98, 0xa1f1651d, 0x39af0176,
- 0x66ca593e, 0x82430e88, 0x8cee8619, 0x456f9fb4, 0x7d84a5c3, 0x3b8b5ebe,
- 0xe06f75d8, 0x85c12073, 0x401a449f, 0x56c16aa6, 0x4ed3aa62, 0x363f7706,
- 0x1bfedf72, 0x429b023d, 0x37d0d724, 0xd00a1248, 0xdb0fead3, 0x49f1c09b,
- 0x075372c9, 0x80991b7b, 0x25d479d8, 0xf6e8def7, 0xe3fe501a, 0xb6794c3b,
- 0x976ce0bd, 0x04c006ba, 0xc1a94fb6, 0x409f60c4, 0x5e5c9ec2, 0x196a2463,
- 0x68fb6faf, 0x3e6c53b5, 0x1339b2eb, 0x3b52ec6f, 0x6dfc511f, 0x9b30952c,
- 0xcc814544, 0xaf5ebd09, 0xbee3d004, 0xde334afd, 0x660f2807, 0x192e4bb3,
- 0xc0cba857, 0x45c8740f, 0xd20b5f39, 0xb9d3fbdb, 0x5579c0bd, 0x1a60320a,
- 0xd6a100c6, 0x402c7279, 0x679f25fe, 0xfb1fa3cc, 0x8ea5e9f8, 0xdb3222f8,
- 0x3c7516df, 0xfd616b15, 0x2f501ec8, 0xad0552ab, 0x323db5fa, 0xfd238760,
- 0x53317b48, 0x3e00df82, 0x9e5c57bb, 0xca6f8ca0, 0x1a87562e, 0xdf1769db,
- 0xd542a8f6, 0x287effc3, 0xac6732c6, 0x8c4f5573, 0x695b27b0, 0xbbca58c8,
- 0xe1ffa35d, 0xb8f011a0, 0x10fa3d98, 0xfd2183b8, 0x4afcb56c, 0x2dd1d35b,
- 0x9a53e479, 0xb6f84565, 0xd28e49bc, 0x4bfb9790, 0xe1ddf2da, 0xa4cb7e33,
- 0x62fb1341, 0xcee4c6e8, 0xef20cada, 0x36774c01, 0xd07e9efe, 0x2bf11fb4,
- 0x95dbda4d, 0xae909198, 0xeaad8e71, 0x6b93d5a0, 0xd08ed1d0, 0xafc725e0,
- 0x8e3c5b2f, 0x8e7594b7, 0x8ff6e2fb, 0xf2122b64, 0x8888b812, 0x900df01c,
- 0x4fad5ea0, 0x688fc31c, 0xd1cff191, 0xb3a8c1ad, 0x2f2f2218, 0xbe0e1777,
- 0xea752dfe, 0x8b021fa1, 0xe5a0cc0f, 0xb56f74e8, 0x18acf3d6, 0xce89e299,
- 0xb4a84fe0, 0xfd13e0b7, 0x7cc43b81, 0xd2ada8d9, 0x165fa266, 0x80957705,
- 0x93cc7314, 0x211a1477, 0xe6ad2065, 0x77b5fa86, 0xc75442f5, 0xfb9d35cf,
- 0xebcdaf0c, 0x7b3e89a0, 0xd6411bd3, 0xae1e7e49, 0x00250e2d, 0x2071b35e,
- 0x226800bb, 0x57b8e0af, 0x2464369b, 0xf009b91e, 0x5563911d, 0x59dfa6aa,
- 0x78c14389, 0xd95a537f, 0x207d5ba2, 0x02e5b9c5, 0x83260376, 0x6295cfa9,
- 0x11c81968, 0x4e734a41, 0xb3472dca, 0x7b14a94a, 0x1b510052, 0x9a532915,
- 0xd60f573f, 0xbc9bc6e4, 0x2b60a476, 0x81e67400, 0x08ba6fb5, 0x571be91f,
- 0xf296ec6b, 0x2a0dd915, 0xb6636521, 0xe7b9f9b6, 0xff34052e, 0xc5855664,
- 0x53b02d5d, 0xa99f8fa1, 0x08ba4799, 0x6e85076a,
-}
-
-var s1 = [256]uint32{
- 0x4b7a70e9, 0xb5b32944, 0xdb75092e, 0xc4192623, 0xad6ea6b0, 0x49a7df7d,
- 0x9cee60b8, 0x8fedb266, 0xecaa8c71, 0x699a17ff, 0x5664526c, 0xc2b19ee1,
- 0x193602a5, 0x75094c29, 0xa0591340, 0xe4183a3e, 0x3f54989a, 0x5b429d65,
- 0x6b8fe4d6, 0x99f73fd6, 0xa1d29c07, 0xefe830f5, 0x4d2d38e6, 0xf0255dc1,
- 0x4cdd2086, 0x8470eb26, 0x6382e9c6, 0x021ecc5e, 0x09686b3f, 0x3ebaefc9,
- 0x3c971814, 0x6b6a70a1, 0x687f3584, 0x52a0e286, 0xb79c5305, 0xaa500737,
- 0x3e07841c, 0x7fdeae5c, 0x8e7d44ec, 0x5716f2b8, 0xb03ada37, 0xf0500c0d,
- 0xf01c1f04, 0x0200b3ff, 0xae0cf51a, 0x3cb574b2, 0x25837a58, 0xdc0921bd,
- 0xd19113f9, 0x7ca92ff6, 0x94324773, 0x22f54701, 0x3ae5e581, 0x37c2dadc,
- 0xc8b57634, 0x9af3dda7, 0xa9446146, 0x0fd0030e, 0xecc8c73e, 0xa4751e41,
- 0xe238cd99, 0x3bea0e2f, 0x3280bba1, 0x183eb331, 0x4e548b38, 0x4f6db908,
- 0x6f420d03, 0xf60a04bf, 0x2cb81290, 0x24977c79, 0x5679b072, 0xbcaf89af,
- 0xde9a771f, 0xd9930810, 0xb38bae12, 0xdccf3f2e, 0x5512721f, 0x2e6b7124,
- 0x501adde6, 0x9f84cd87, 0x7a584718, 0x7408da17, 0xbc9f9abc, 0xe94b7d8c,
- 0xec7aec3a, 0xdb851dfa, 0x63094366, 0xc464c3d2, 0xef1c1847, 0x3215d908,
- 0xdd433b37, 0x24c2ba16, 0x12a14d43, 0x2a65c451, 0x50940002, 0x133ae4dd,
- 0x71dff89e, 0x10314e55, 0x81ac77d6, 0x5f11199b, 0x043556f1, 0xd7a3c76b,
- 0x3c11183b, 0x5924a509, 0xf28fe6ed, 0x97f1fbfa, 0x9ebabf2c, 0x1e153c6e,
- 0x86e34570, 0xeae96fb1, 0x860e5e0a, 0x5a3e2ab3, 0x771fe71c, 0x4e3d06fa,
- 0x2965dcb9, 0x99e71d0f, 0x803e89d6, 0x5266c825, 0x2e4cc978, 0x9c10b36a,
- 0xc6150eba, 0x94e2ea78, 0xa5fc3c53, 0x1e0a2df4, 0xf2f74ea7, 0x361d2b3d,
- 0x1939260f, 0x19c27960, 0x5223a708, 0xf71312b6, 0xebadfe6e, 0xeac31f66,
- 0xe3bc4595, 0xa67bc883, 0xb17f37d1, 0x018cff28, 0xc332ddef, 0xbe6c5aa5,
- 0x65582185, 0x68ab9802, 0xeecea50f, 0xdb2f953b, 0x2aef7dad, 0x5b6e2f84,
- 0x1521b628, 0x29076170, 0xecdd4775, 0x619f1510, 0x13cca830, 0xeb61bd96,
- 0x0334fe1e, 0xaa0363cf, 0xb5735c90, 0x4c70a239, 0xd59e9e0b, 0xcbaade14,
- 0xeecc86bc, 0x60622ca7, 0x9cab5cab, 0xb2f3846e, 0x648b1eaf, 0x19bdf0ca,
- 0xa02369b9, 0x655abb50, 0x40685a32, 0x3c2ab4b3, 0x319ee9d5, 0xc021b8f7,
- 0x9b540b19, 0x875fa099, 0x95f7997e, 0x623d7da8, 0xf837889a, 0x97e32d77,
- 0x11ed935f, 0x16681281, 0x0e358829, 0xc7e61fd6, 0x96dedfa1, 0x7858ba99,
- 0x57f584a5, 0x1b227263, 0x9b83c3ff, 0x1ac24696, 0xcdb30aeb, 0x532e3054,
- 0x8fd948e4, 0x6dbc3128, 0x58ebf2ef, 0x34c6ffea, 0xfe28ed61, 0xee7c3c73,
- 0x5d4a14d9, 0xe864b7e3, 0x42105d14, 0x203e13e0, 0x45eee2b6, 0xa3aaabea,
- 0xdb6c4f15, 0xfacb4fd0, 0xc742f442, 0xef6abbb5, 0x654f3b1d, 0x41cd2105,
- 0xd81e799e, 0x86854dc7, 0xe44b476a, 0x3d816250, 0xcf62a1f2, 0x5b8d2646,
- 0xfc8883a0, 0xc1c7b6a3, 0x7f1524c3, 0x69cb7492, 0x47848a0b, 0x5692b285,
- 0x095bbf00, 0xad19489d, 0x1462b174, 0x23820e00, 0x58428d2a, 0x0c55f5ea,
- 0x1dadf43e, 0x233f7061, 0x3372f092, 0x8d937e41, 0xd65fecf1, 0x6c223bdb,
- 0x7cde3759, 0xcbee7460, 0x4085f2a7, 0xce77326e, 0xa6078084, 0x19f8509e,
- 0xe8efd855, 0x61d99735, 0xa969a7aa, 0xc50c06c2, 0x5a04abfc, 0x800bcadc,
- 0x9e447a2e, 0xc3453484, 0xfdd56705, 0x0e1e9ec9, 0xdb73dbd3, 0x105588cd,
- 0x675fda79, 0xe3674340, 0xc5c43465, 0x713e38d8, 0x3d28f89e, 0xf16dff20,
- 0x153e21e7, 0x8fb03d4a, 0xe6e39f2b, 0xdb83adf7,
-}
-
-var s2 = [256]uint32{
- 0xe93d5a68, 0x948140f7, 0xf64c261c, 0x94692934, 0x411520f7, 0x7602d4f7,
- 0xbcf46b2e, 0xd4a20068, 0xd4082471, 0x3320f46a, 0x43b7d4b7, 0x500061af,
- 0x1e39f62e, 0x97244546, 0x14214f74, 0xbf8b8840, 0x4d95fc1d, 0x96b591af,
- 0x70f4ddd3, 0x66a02f45, 0xbfbc09ec, 0x03bd9785, 0x7fac6dd0, 0x31cb8504,
- 0x96eb27b3, 0x55fd3941, 0xda2547e6, 0xabca0a9a, 0x28507825, 0x530429f4,
- 0x0a2c86da, 0xe9b66dfb, 0x68dc1462, 0xd7486900, 0x680ec0a4, 0x27a18dee,
- 0x4f3ffea2, 0xe887ad8c, 0xb58ce006, 0x7af4d6b6, 0xaace1e7c, 0xd3375fec,
- 0xce78a399, 0x406b2a42, 0x20fe9e35, 0xd9f385b9, 0xee39d7ab, 0x3b124e8b,
- 0x1dc9faf7, 0x4b6d1856, 0x26a36631, 0xeae397b2, 0x3a6efa74, 0xdd5b4332,
- 0x6841e7f7, 0xca7820fb, 0xfb0af54e, 0xd8feb397, 0x454056ac, 0xba489527,
- 0x55533a3a, 0x20838d87, 0xfe6ba9b7, 0xd096954b, 0x55a867bc, 0xa1159a58,
- 0xcca92963, 0x99e1db33, 0xa62a4a56, 0x3f3125f9, 0x5ef47e1c, 0x9029317c,
- 0xfdf8e802, 0x04272f70, 0x80bb155c, 0x05282ce3, 0x95c11548, 0xe4c66d22,
- 0x48c1133f, 0xc70f86dc, 0x07f9c9ee, 0x41041f0f, 0x404779a4, 0x5d886e17,
- 0x325f51eb, 0xd59bc0d1, 0xf2bcc18f, 0x41113564, 0x257b7834, 0x602a9c60,
- 0xdff8e8a3, 0x1f636c1b, 0x0e12b4c2, 0x02e1329e, 0xaf664fd1, 0xcad18115,
- 0x6b2395e0, 0x333e92e1, 0x3b240b62, 0xeebeb922, 0x85b2a20e, 0xe6ba0d99,
- 0xde720c8c, 0x2da2f728, 0xd0127845, 0x95b794fd, 0x647d0862, 0xe7ccf5f0,
- 0x5449a36f, 0x877d48fa, 0xc39dfd27, 0xf33e8d1e, 0x0a476341, 0x992eff74,
- 0x3a6f6eab, 0xf4f8fd37, 0xa812dc60, 0xa1ebddf8, 0x991be14c, 0xdb6e6b0d,
- 0xc67b5510, 0x6d672c37, 0x2765d43b, 0xdcd0e804, 0xf1290dc7, 0xcc00ffa3,
- 0xb5390f92, 0x690fed0b, 0x667b9ffb, 0xcedb7d9c, 0xa091cf0b, 0xd9155ea3,
- 0xbb132f88, 0x515bad24, 0x7b9479bf, 0x763bd6eb, 0x37392eb3, 0xcc115979,
- 0x8026e297, 0xf42e312d, 0x6842ada7, 0xc66a2b3b, 0x12754ccc, 0x782ef11c,
- 0x6a124237, 0xb79251e7, 0x06a1bbe6, 0x4bfb6350, 0x1a6b1018, 0x11caedfa,
- 0x3d25bdd8, 0xe2e1c3c9, 0x44421659, 0x0a121386, 0xd90cec6e, 0xd5abea2a,
- 0x64af674e, 0xda86a85f, 0xbebfe988, 0x64e4c3fe, 0x9dbc8057, 0xf0f7c086,
- 0x60787bf8, 0x6003604d, 0xd1fd8346, 0xf6381fb0, 0x7745ae04, 0xd736fccc,
- 0x83426b33, 0xf01eab71, 0xb0804187, 0x3c005e5f, 0x77a057be, 0xbde8ae24,
- 0x55464299, 0xbf582e61, 0x4e58f48f, 0xf2ddfda2, 0xf474ef38, 0x8789bdc2,
- 0x5366f9c3, 0xc8b38e74, 0xb475f255, 0x46fcd9b9, 0x7aeb2661, 0x8b1ddf84,
- 0x846a0e79, 0x915f95e2, 0x466e598e, 0x20b45770, 0x8cd55591, 0xc902de4c,
- 0xb90bace1, 0xbb8205d0, 0x11a86248, 0x7574a99e, 0xb77f19b6, 0xe0a9dc09,
- 0x662d09a1, 0xc4324633, 0xe85a1f02, 0x09f0be8c, 0x4a99a025, 0x1d6efe10,
- 0x1ab93d1d, 0x0ba5a4df, 0xa186f20f, 0x2868f169, 0xdcb7da83, 0x573906fe,
- 0xa1e2ce9b, 0x4fcd7f52, 0x50115e01, 0xa70683fa, 0xa002b5c4, 0x0de6d027,
- 0x9af88c27, 0x773f8641, 0xc3604c06, 0x61a806b5, 0xf0177a28, 0xc0f586e0,
- 0x006058aa, 0x30dc7d62, 0x11e69ed7, 0x2338ea63, 0x53c2dd94, 0xc2c21634,
- 0xbbcbee56, 0x90bcb6de, 0xebfc7da1, 0xce591d76, 0x6f05e409, 0x4b7c0188,
- 0x39720a3d, 0x7c927c24, 0x86e3725f, 0x724d9db9, 0x1ac15bb4, 0xd39eb8fc,
- 0xed545578, 0x08fca5b5, 0xd83d7cd3, 0x4dad0fc4, 0x1e50ef5e, 0xb161e6f8,
- 0xa28514d9, 0x6c51133c, 0x6fd5c7e7, 0x56e14ec4, 0x362abfce, 0xddc6c837,
- 0xd79a3234, 0x92638212, 0x670efa8e, 0x406000e0,
-}
-
-var s3 = [256]uint32{
- 0x3a39ce37, 0xd3faf5cf, 0xabc27737, 0x5ac52d1b, 0x5cb0679e, 0x4fa33742,
- 0xd3822740, 0x99bc9bbe, 0xd5118e9d, 0xbf0f7315, 0xd62d1c7e, 0xc700c47b,
- 0xb78c1b6b, 0x21a19045, 0xb26eb1be, 0x6a366eb4, 0x5748ab2f, 0xbc946e79,
- 0xc6a376d2, 0x6549c2c8, 0x530ff8ee, 0x468dde7d, 0xd5730a1d, 0x4cd04dc6,
- 0x2939bbdb, 0xa9ba4650, 0xac9526e8, 0xbe5ee304, 0xa1fad5f0, 0x6a2d519a,
- 0x63ef8ce2, 0x9a86ee22, 0xc089c2b8, 0x43242ef6, 0xa51e03aa, 0x9cf2d0a4,
- 0x83c061ba, 0x9be96a4d, 0x8fe51550, 0xba645bd6, 0x2826a2f9, 0xa73a3ae1,
- 0x4ba99586, 0xef5562e9, 0xc72fefd3, 0xf752f7da, 0x3f046f69, 0x77fa0a59,
- 0x80e4a915, 0x87b08601, 0x9b09e6ad, 0x3b3ee593, 0xe990fd5a, 0x9e34d797,
- 0x2cf0b7d9, 0x022b8b51, 0x96d5ac3a, 0x017da67d, 0xd1cf3ed6, 0x7c7d2d28,
- 0x1f9f25cf, 0xadf2b89b, 0x5ad6b472, 0x5a88f54c, 0xe029ac71, 0xe019a5e6,
- 0x47b0acfd, 0xed93fa9b, 0xe8d3c48d, 0x283b57cc, 0xf8d56629, 0x79132e28,
- 0x785f0191, 0xed756055, 0xf7960e44, 0xe3d35e8c, 0x15056dd4, 0x88f46dba,
- 0x03a16125, 0x0564f0bd, 0xc3eb9e15, 0x3c9057a2, 0x97271aec, 0xa93a072a,
- 0x1b3f6d9b, 0x1e6321f5, 0xf59c66fb, 0x26dcf319, 0x7533d928, 0xb155fdf5,
- 0x03563482, 0x8aba3cbb, 0x28517711, 0xc20ad9f8, 0xabcc5167, 0xccad925f,
- 0x4de81751, 0x3830dc8e, 0x379d5862, 0x9320f991, 0xea7a90c2, 0xfb3e7bce,
- 0x5121ce64, 0x774fbe32, 0xa8b6e37e, 0xc3293d46, 0x48de5369, 0x6413e680,
- 0xa2ae0810, 0xdd6db224, 0x69852dfd, 0x09072166, 0xb39a460a, 0x6445c0dd,
- 0x586cdecf, 0x1c20c8ae, 0x5bbef7dd, 0x1b588d40, 0xccd2017f, 0x6bb4e3bb,
- 0xdda26a7e, 0x3a59ff45, 0x3e350a44, 0xbcb4cdd5, 0x72eacea8, 0xfa6484bb,
- 0x8d6612ae, 0xbf3c6f47, 0xd29be463, 0x542f5d9e, 0xaec2771b, 0xf64e6370,
- 0x740e0d8d, 0xe75b1357, 0xf8721671, 0xaf537d5d, 0x4040cb08, 0x4eb4e2cc,
- 0x34d2466a, 0x0115af84, 0xe1b00428, 0x95983a1d, 0x06b89fb4, 0xce6ea048,
- 0x6f3f3b82, 0x3520ab82, 0x011a1d4b, 0x277227f8, 0x611560b1, 0xe7933fdc,
- 0xbb3a792b, 0x344525bd, 0xa08839e1, 0x51ce794b, 0x2f32c9b7, 0xa01fbac9,
- 0xe01cc87e, 0xbcc7d1f6, 0xcf0111c3, 0xa1e8aac7, 0x1a908749, 0xd44fbd9a,
- 0xd0dadecb, 0xd50ada38, 0x0339c32a, 0xc6913667, 0x8df9317c, 0xe0b12b4f,
- 0xf79e59b7, 0x43f5bb3a, 0xf2d519ff, 0x27d9459c, 0xbf97222c, 0x15e6fc2a,
- 0x0f91fc71, 0x9b941525, 0xfae59361, 0xceb69ceb, 0xc2a86459, 0x12baa8d1,
- 0xb6c1075e, 0xe3056a0c, 0x10d25065, 0xcb03a442, 0xe0ec6e0e, 0x1698db3b,
- 0x4c98a0be, 0x3278e964, 0x9f1f9532, 0xe0d392df, 0xd3a0342b, 0x8971f21e,
- 0x1b0a7441, 0x4ba3348c, 0xc5be7120, 0xc37632d8, 0xdf359f8d, 0x9b992f2e,
- 0xe60b6f47, 0x0fe3f11d, 0xe54cda54, 0x1edad891, 0xce6279cf, 0xcd3e7e6f,
- 0x1618b166, 0xfd2c1d05, 0x848fd2c5, 0xf6fb2299, 0xf523f357, 0xa6327623,
- 0x93a83531, 0x56cccd02, 0xacf08162, 0x5a75ebb5, 0x6e163697, 0x88d273cc,
- 0xde966292, 0x81b949d0, 0x4c50901b, 0x71c65614, 0xe6c6c7bd, 0x327a140a,
- 0x45e1d006, 0xc3f27b9a, 0xc9aa53fd, 0x62a80f00, 0xbb25bfe2, 0x35bdd2f6,
- 0x71126905, 0xb2040222, 0xb6cbcf7c, 0xcd769c2b, 0x53113ec0, 0x1640e3d3,
- 0x38abbd60, 0x2547adf0, 0xba38209c, 0xf746ce76, 0x77afa1c5, 0x20756060,
- 0x85cbfe4e, 0x8ae88dd8, 0x7aaaf9b0, 0x4cf9aa7e, 0x1948c25c, 0x02fb8a8c,
- 0x01c36ae4, 0xd6ebe1f9, 0x90d4f869, 0xa65cdea0, 0x3f09252d, 0xc208e69f,
- 0xb74e6132, 0xce77e25b, 0x578fdfe3, 0x3ac372e6,
-}
-
-var p = [18]uint32{
- 0x243f6a88, 0x85a308d3, 0x13198a2e, 0x03707344, 0xa4093822, 0x299f31d0,
- 0x082efa98, 0xec4e6c89, 0x452821e6, 0x38d01377, 0xbe5466cf, 0x34e90c6c,
- 0xc0ac29b7, 0xc97c50dd, 0x3f84d5b5, 0xb5470917, 0x9216d5d9, 0x8979fb1b,
-}
diff --git a/xmlenc/cbc.go b/xmlenc/cbc.go
index bf6a14f7..d60f7e30 100644
--- a/xmlenc/cbc.go
+++ b/xmlenc/cbc.go
@@ -32,7 +32,7 @@ func (e CBC) Algorithm() string {
// 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) (*etree.Element, error) {
+func (e CBC) Encrypt(key interface{}, plaintext []byte, nonce []byte) (*etree.Element, error) {
keyBuf, ok := key.([]byte)
if !ok {
return nil, ErrIncorrectKeyType("[]byte")
diff --git a/xmlenc/corpus/encrypt-content-aes192-cbc-dh-sha512.xml b/xmlenc/corpus/encrypt-content-aes192-cbc-dh-sha512.xml
index d1242784..2c8069d3 100644
--- a/xmlenc/corpus/encrypt-content-aes192-cbc-dh-sha512.xml
+++ b/xmlenc/corpus/encrypt-content-aes192-cbc-dh-sha512.xml
@@ -44,7 +44,7 @@
-
+
MIIDvjCCA36gAwIBAgIGAOxN39MIMAkGByqGSM44BAMwbjELMAkGA1UEBhMCSUUx
DzANBgNVBAgTBkR1YmxpbjEkMCIGA1UEChMbQmFsdGltb3JlIFRlY2hub2xvZ2ll
cyBMdGQuMREwDwYDVQQLEwhYL1NlY3VyZTEVMBMGA1UEAxMMVHJhbnNpZW50IENB
@@ -71,7 +71,7 @@
-
+
MIIDvjCCA36gAwIBAgIGAOxN3+EMMAkGByqGSM44BAMwbjELMAkGA1UEBhMCSUUx
DzANBgNVBAgTBkR1YmxpbjEkMCIGA1UEChMbQmFsdGltb3JlIFRlY2hub2xvZ2ll
cyBMdGQuMREwDwYDVQQLEwhYL1NlY3VyZTEVMBMGA1UEAxMMVHJhbnNpZW50IENB
diff --git a/xmlenc/corpus/encrypt-data-tripledes-cbc-rsa-oaep-mgf1p-sha256.xml b/xmlenc/corpus/encrypt-data-tripledes-cbc-rsa-oaep-mgf1p-sha256.xml
index c9c30e09..562b8f87 100644
--- a/xmlenc/corpus/encrypt-data-tripledes-cbc-rsa-oaep-mgf1p-sha256.xml
+++ b/xmlenc/corpus/encrypt-data-tripledes-cbc-rsa-oaep-mgf1p-sha256.xml
@@ -10,8 +10,8 @@
-
-
+
+
MIICkjCCAfugAwIBAgIGAOxN32E+MA0GCSqGSIb3DQEBBQUAMG4xCzAJBgNVBAYT
AklFMQ8wDQYDVQQIEwZEdWJsaW4xJDAiBgNVBAoTG0JhbHRpbW9yZSBUZWNobm9s
b2dpZXMgTHRkLjERMA8GA1UECxMIWC9TZWN1cmUxFTATBgNVBAMTDFRyYW5zaWVu
diff --git a/xmlenc/corpus/encrypt-data-tripledes-cbc-rsa-oaep-mgf1p.xml b/xmlenc/corpus/encrypt-data-tripledes-cbc-rsa-oaep-mgf1p.xml
index 29daa4ea..fef209eb 100644
--- a/xmlenc/corpus/encrypt-data-tripledes-cbc-rsa-oaep-mgf1p.xml
+++ b/xmlenc/corpus/encrypt-data-tripledes-cbc-rsa-oaep-mgf1p.xml
@@ -7,8 +7,8 @@
-
-
+
+
MIICkjCCAfugAwIBAgIGAOxN32E+MA0GCSqGSIb3DQEBBQUAMG4xCzAJBgNVBAYT
AklFMQ8wDQYDVQQIEwZEdWJsaW4xJDAiBgNVBAoTG0JhbHRpbW9yZSBUZWNobm9s
b2dpZXMgTHRkLjERMA8GA1UECxMIWC9TZWN1cmUxFTATBgNVBAMTDFRyYW5zaWVu
diff --git a/xmlenc/corpus/encrypt-element-aes128-cbc-rsa-1_5.xml b/xmlenc/corpus/encrypt-element-aes128-cbc-rsa-1_5.xml
index 9d74e16c..eebd0b59 100644
--- a/xmlenc/corpus/encrypt-element-aes128-cbc-rsa-1_5.xml
+++ b/xmlenc/corpus/encrypt-element-aes128-cbc-rsa-1_5.xml
@@ -20,8 +20,8 @@
-
-
+
+
MIICkjCCAfugAwIBAgIGAOxN32E+MA0GCSqGSIb3DQEBBQUAMG4xCzAJBgNVBAYT
AklFMQ8wDQYDVQQIEwZEdWJsaW4xJDAiBgNVBAoTG0JhbHRpbW9yZSBUZWNobm9s
b2dpZXMgTHRkLjERMA8GA1UECxMIWC9TZWN1cmUxFTATBgNVBAMTDFRyYW5zaWVu
diff --git a/xmlenc/corpus/encrypt-element-aes256-cbc-kw-aes256-dh-ripemd160.xml b/xmlenc/corpus/encrypt-element-aes256-cbc-kw-aes256-dh-ripemd160.xml
index 5fb336ac..71b77885 100644
--- a/xmlenc/corpus/encrypt-element-aes256-cbc-kw-aes256-dh-ripemd160.xml
+++ b/xmlenc/corpus/encrypt-element-aes256-cbc-kw-aes256-dh-ripemd160.xml
@@ -46,7 +46,7 @@
-
+
MIIDvjCCA36gAwIBAgIGAOxN39MIMAkGByqGSM44BAMwbjELMAkGA1UEBhMCSUUx
DzANBgNVBAgTBkR1YmxpbjEkMCIGA1UEChMbQmFsdGltb3JlIFRlY2hub2xvZ2ll
cyBMdGQuMREwDwYDVQQLEwhYL1NlY3VyZTEVMBMGA1UEAxMMVHJhbnNpZW50IENB
@@ -73,7 +73,7 @@
-
+
MIIDvjCCA36gAwIBAgIGAOxN3+EMMAkGByqGSM44BAMwbjELMAkGA1UEBhMCSUUx
DzANBgNVBAgTBkR1YmxpbjEkMCIGA1UEChMbQmFsdGltb3JlIFRlY2hub2xvZ2ll
cyBMdGQuMREwDwYDVQQLEwhYL1NlY3VyZTEVMBMGA1UEAxMMVHJhbnNpZW50IENB
diff --git a/xmlenc/corpus/encsig-hmac-sha256-dh.xml b/xmlenc/corpus/encsig-hmac-sha256-dh.xml
index a69d9361..46af3fb2 100644
--- a/xmlenc/corpus/encsig-hmac-sha256-dh.xml
+++ b/xmlenc/corpus/encsig-hmac-sha256-dh.xml
@@ -41,7 +41,7 @@
-
+
MIIDvjCCA36gAwIBAgIGAOxN39MIMAkGByqGSM44BAMwbjELMAkGA1UEBhMCSUUx
DzANBgNVBAgTBkR1YmxpbjEkMCIGA1UEChMbQmFsdGltb3JlIFRlY2hub2xvZ2ll
cyBMdGQuMREwDwYDVQQLEwhYL1NlY3VyZTEVMBMGA1UEAxMMVHJhbnNpZW50IENB
@@ -68,7 +68,7 @@
-
+
MIIDvjCCA36gAwIBAgIGAOxN3+EMMAkGByqGSM44BAMwbjELMAkGA1UEBhMCSUUx
DzANBgNVBAgTBkR1YmxpbjEkMCIGA1UEChMbQmFsdGltb3JlIFRlY2hub2xvZ2ll
cyBMdGQuMREwDwYDVQQLEwhYL1NlY3VyZTEVMBMGA1UEAxMMVHJhbnNpZW50IENB
diff --git a/xmlenc/corpus/encsig-hmac-sha256-kw-tripledes-dh.xml b/xmlenc/corpus/encsig-hmac-sha256-kw-tripledes-dh.xml
index 79ef3f12..f6cf2ac7 100644
--- a/xmlenc/corpus/encsig-hmac-sha256-kw-tripledes-dh.xml
+++ b/xmlenc/corpus/encsig-hmac-sha256-kw-tripledes-dh.xml
@@ -44,7 +44,7 @@
-
+
MIIDvjCCA36gAwIBAgIGAOxN39MIMAkGByqGSM44BAMwbjELMAkGA1UEBhMCSUUx
DzANBgNVBAgTBkR1YmxpbjEkMCIGA1UEChMbQmFsdGltb3JlIFRlY2hub2xvZ2ll
cyBMdGQuMREwDwYDVQQLEwhYL1NlY3VyZTEVMBMGA1UEAxMMVHJhbnNpZW50IENB
@@ -71,7 +71,7 @@
-
+
MIIDvjCCA36gAwIBAgIGAOxN3+EMMAkGByqGSM44BAMwbjELMAkGA1UEBhMCSUUx
DzANBgNVBAgTBkR1YmxpbjEkMCIGA1UEChMbQmFsdGltb3JlIFRlY2hub2xvZ2ll
cyBMdGQuMREwDwYDVQQLEwhYL1NlY3VyZTEVMBMGA1UEAxMMVHJhbnNpZW50IENB
diff --git a/xmlenc/corpus/encsig-hmac-sha256-rsa-1_5.xml b/xmlenc/corpus/encsig-hmac-sha256-rsa-1_5.xml
index ecc29878..948cad30 100644
--- a/xmlenc/corpus/encsig-hmac-sha256-rsa-1_5.xml
+++ b/xmlenc/corpus/encsig-hmac-sha256-rsa-1_5.xml
@@ -15,8 +15,8 @@
-
-
+
+
MIICkjCCAfugAwIBAgIGAOxN32E+MA0GCSqGSIb3DQEBBQUAMG4xCzAJBgNVBAYT
AklFMQ8wDQYDVQQIEwZEdWJsaW4xJDAiBgNVBAoTG0JhbHRpbW9yZSBUZWNobm9s
b2dpZXMgTHRkLjERMA8GA1UECxMIWC9TZWN1cmUxFTATBgNVBAMTDFRyYW5zaWVu
diff --git a/xmlenc/corpus/encsig-hmac-sha256-rsa-oaep-mgf1p.xml b/xmlenc/corpus/encsig-hmac-sha256-rsa-oaep-mgf1p.xml
index 1779093a..e088df0f 100644
--- a/xmlenc/corpus/encsig-hmac-sha256-rsa-oaep-mgf1p.xml
+++ b/xmlenc/corpus/encsig-hmac-sha256-rsa-oaep-mgf1p.xml
@@ -20,8 +20,8 @@
-
-
+
+
MIICkjCCAfugAwIBAgIGAOxN32E+MA0GCSqGSIb3DQEBBQUAMG4xCzAJBgNVBAYT
AklFMQ8wDQYDVQQIEwZEdWJsaW4xJDAiBgNVBAoTG0JhbHRpbW9yZSBUZWNobm9s
b2dpZXMgTHRkLjERMA8GA1UECxMIWC9TZWN1cmUxFTATBgNVBAMTDFRyYW5zaWVu
diff --git a/xmlenc/crashers/255c4741516851ba13e8151340541d1d247a1d5f b/xmlenc/crashers/255c4741516851ba13e8151340541d1d247a1d5f
index 69762d9c..c32edc58 100644
--- a/xmlenc/crashers/255c4741516851ba13e8151340541d1d247a1d5f
+++ b/xmlenc/crashers/255c4741516851ba13e8151340541d1d247a1d5f
@@ -1 +1 @@
-0
\ No newline at end of file
+0
diff --git a/xmlenc/crashers/4d7686c884eb4d4e41d69446018d7c3122c9bc11 b/xmlenc/crashers/4d7686c884eb4d4e41d69446018d7c3122c9bc11
index fd608dc1..99b5da31 100644
--- a/xmlenc/crashers/4d7686c884eb4d4e41d69446018d7c3122c9bc11
+++ b/xmlenc/crashers/4d7686c884eb4d4e41d69446018d7c3122c9bc11
@@ -1 +1 @@
-0
\ No newline at end of file
+0
diff --git a/xmlenc/crashers/5837247e7ed40387b811a2a1be52cd91b873d349 b/xmlenc/crashers/5837247e7ed40387b811a2a1be52cd91b873d349
index bda37610..3b8110e6 100644
--- a/xmlenc/crashers/5837247e7ed40387b811a2a1be52cd91b873d349
+++ b/xmlenc/crashers/5837247e7ed40387b811a2a1be52cd91b873d349
@@ -1 +1 @@
-MIICkjCCAfugAwIBAgIGAOxN32E+MA0GCSqGSIb3DQEBBQUAMG4xCzAJBgNVBAYTAklFMQ8wDQYDVQQIEwZEdWJsaW4xJDAiBgNVBAoTG0JhbHRpbW9yZSBUZWNobm9sb2dpZXMgTHRkLjERMA8GA1UECxMIWC9TZWN1cmUxFTATBgNVBAMTDFRyYW5zaWVudCBDQTAeFw0wMjAyMjgxNzUyNDZaFw0wMzAyMjgxNzUyNDBaMG8xCzAJBgNVBAYTAklFMQ8wDQYDVQQIEwZEdWJsaW4xJDAiBgNVBAoTG0JhbHRpbW9yZSBUZWNobm9sb2dpZXMgTHRkLjERMA8GA1UECxMIWC9TZWN1cmUxFjAUBgNVBAMTDU1lcmxpbiBIdWdoZXMwgZ8wDQYGKoZIhvcNAQEBBQADgY0AMIGJAoGBAORdNSxbNFWlQeNsOlYJ9gN9eZD+rguRqKhmhOm7i63VDd5ALm2APXhqAmGBPzLN5jlL9g2XALK5WSO4XKjJMcVfYg4+nPuOeHgqdD4HUgf19j/6SaTMcmDFJQMmx1Qw+Aakq3mGcSfvOJcBZctza50VucfCGL1NdfBEcaL3BnhjAgMBAAGjOjA4MA4GA1UdDwEB/wQEAwIFoDARBgNVHQ4ECgQIjFG0ZGNyvNswEwYDVR0jBAwwCoAIhJXVlhr6O4wwDQYJKoZIhvcNAQEFBQADgYEAXzG7x5aCJYRusTbmuZqhidGM5iiA9+RmZ4JTPDEgbeiTiJROxpr+ZjnATmsDKrCpqNUiHWjmsKEArYQp8R/KjdKl/pVe3jUvTxb0YZ+li/7k0GQ5LyRT/K4c2SgyLlyBPhpMq+z3g4P2egVRaZbxsLuKQILf7MIV/X5iAEBzu1w=
\ No newline at end of file
+MIICkjCCAfugAwIBAgIGAOxN32E+MA0GCSqGSIb3DQEBBQUAMG4xCzAJBgNVBAYTAklFMQ8wDQYDVQQIEwZEdWJsaW4xJDAiBgNVBAoTG0JhbHRpbW9yZSBUZWNobm9sb2dpZXMgTHRkLjERMA8GA1UECxMIWC9TZWN1cmUxFTATBgNVBAMTDFRyYW5zaWVudCBDQTAeFw0wMjAyMjgxNzUyNDZaFw0wMzAyMjgxNzUyNDBaMG8xCzAJBgNVBAYTAklFMQ8wDQYDVQQIEwZEdWJsaW4xJDAiBgNVBAoTG0JhbHRpbW9yZSBUZWNobm9sb2dpZXMgTHRkLjERMA8GA1UECxMIWC9TZWN1cmUxFjAUBgNVBAMTDU1lcmxpbiBIdWdoZXMwgZ8wDQYGKoZIhvcNAQEBBQADgY0AMIGJAoGBAORdNSxbNFWlQeNsOlYJ9gN9eZD+rguRqKhmhOm7i63VDd5ALm2APXhqAmGBPzLN5jlL9g2XALK5WSO4XKjJMcVfYg4+nPuOeHgqdD4HUgf19j/6SaTMcmDFJQMmx1Qw+Aakq3mGcSfvOJcBZctza50VucfCGL1NdfBEcaL3BnhjAgMBAAGjOjA4MA4GA1UdDwEB/wQEAwIFoDARBgNVHQ4ECgQIjFG0ZGNyvNswEwYDVR0jBAwwCoAIhJXVlhr6O4wwDQYJKoZIhvcNAQEFBQADgYEAXzG7x5aCJYRusTbmuZqhidGM5iiA9+RmZ4JTPDEgbeiTiJROxpr+ZjnATmsDKrCpqNUiHWjmsKEArYQp8R/KjdKl/pVe3jUvTxb0YZ+li/7k0GQ5LyRT/K4c2SgyLlyBPhpMq+z3g4P2egVRaZbxsLuKQILf7MIV/X5iAEBzu1w=
diff --git a/xmlenc/crashers/655d116d6e8b8d9c1de179459ee71293a6151792 b/xmlenc/crashers/655d116d6e8b8d9c1de179459ee71293a6151792
index 35ac2fc8..b290dff3 100644
--- a/xmlenc/crashers/655d116d6e8b8d9c1de179459ee71293a6151792
+++ b/xmlenc/crashers/655d116d6e8b8d9c1de179459ee71293a6151792
@@ -1 +1 @@
-MIICkjCCAfugAwIBAgIGAOxN32E+MA0GCSqGEIb3DQEBBQUAMG4xCzAJBgNVBAYTAklFMQ8wDQYDVQQIEwZEdWJsaW4xJDAiBgNVBAoTG0JhbHRpbW9yZSBUZWNobm9sb2dpZXMgTHRkLjERMA8GA1UECxMIWC9TZWN1cmUxFTATBgNVBAMTDFRyYW5zaWVudCBDQTAeFw0wMjAyMjgxNzUyNDZaFw0wMzAyMjgxNzUyNDBaMG8xCzAJBgNVBAYTAklFMQ8wDQYDVQQIEwZEdWJsaW4xJDAiBgNVBAoTG0JhbHRpbW9yZSBUZWNobm9sb2dpZXMgTHRkLjERMA8GA1UECxMIWC9TZWN1cmUxFjAUBgNVBAMTDU1lcmxpbiBIdWdoZXMwgZ8wDQYJKoZIBvcNAQEBBQADgY0ACIGJAoGBAORdNSxbNFWlQeNsOlYJ9gN9eZD+rguRqKhmhOm7i63VDd5ALm2APXhqAmGBPzLN5jlL9g2XALK5WSO4XKjJMcVfYg4+nPuOeHgqdD4HUgf19j/6SaTMcmDFJQMmx1Qw+Aakq3mGcSfvOJcBZctza50VucfCGL1NdfBEcaL3BnhjAgMBAAGjOjA4MA4GA1UdDwEB/wQEAwIFoDARBgNVHQ4ECgQIjFG0ZGNyvNswEwYDVR0jBAwwCoAIhJXVlhr6O4wwDQYJKoZIhvcNAQEFBQADgYEAXzG7x5aCJYRusTbmuZqhidGM5iiA9+RmZ4JTPDEgbeiTiJROxpr+ZjnATmsDKrCpqNUiHWjmsKEArYQp8R/KjdKl/pVe3jUvTxb0YZ+li/7k0GQ5LyRT/K4c2SgyLlyhPhpMq+z3g4P2egVRaZbxsLuKQILf7MIV/X5iAEBzu1w=
\ No newline at end of file
+MIICkjCCAfugAwIBAgIGAOxN32E+MA0GCSqGEIb3DQEBBQUAMG4xCzAJBgNVBAYTAklFMQ8wDQYDVQQIEwZEdWJsaW4xJDAiBgNVBAoTG0JhbHRpbW9yZSBUZWNobm9sb2dpZXMgTHRkLjERMA8GA1UECxMIWC9TZWN1cmUxFTATBgNVBAMTDFRyYW5zaWVudCBDQTAeFw0wMjAyMjgxNzUyNDZaFw0wMzAyMjgxNzUyNDBaMG8xCzAJBgNVBAYTAklFMQ8wDQYDVQQIEwZEdWJsaW4xJDAiBgNVBAoTG0JhbHRpbW9yZSBUZWNobm9sb2dpZXMgTHRkLjERMA8GA1UECxMIWC9TZWN1cmUxFjAUBgNVBAMTDU1lcmxpbiBIdWdoZXMwgZ8wDQYJKoZIBvcNAQEBBQADgY0ACIGJAoGBAORdNSxbNFWlQeNsOlYJ9gN9eZD+rguRqKhmhOm7i63VDd5ALm2APXhqAmGBPzLN5jlL9g2XALK5WSO4XKjJMcVfYg4+nPuOeHgqdD4HUgf19j/6SaTMcmDFJQMmx1Qw+Aakq3mGcSfvOJcBZctza50VucfCGL1NdfBEcaL3BnhjAgMBAAGjOjA4MA4GA1UdDwEB/wQEAwIFoDARBgNVHQ4ECgQIjFG0ZGNyvNswEwYDVR0jBAwwCoAIhJXVlhr6O4wwDQYJKoZIhvcNAQEFBQADgYEAXzG7x5aCJYRusTbmuZqhidGM5iiA9+RmZ4JTPDEgbeiTiJROxpr+ZjnATmsDKrCpqNUiHWjmsKEArYQp8R/KjdKl/pVe3jUvTxb0YZ+li/7k0GQ5LyRT/K4c2SgyLlyhPhpMq+z3g4P2egVRaZbxsLuKQILf7MIV/X5iAEBzu1w=
diff --git a/xmlenc/decrypt_test.go b/xmlenc/decrypt_test.go
index ecf46dda..2e23f8f6 100644
--- a/xmlenc/decrypt_test.go
+++ b/xmlenc/decrypt_test.go
@@ -13,47 +13,93 @@ import (
)
func TestCanDecrypt(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("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) {
- 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("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)
+ })
}
diff --git a/xmlenc/encrypt_test.go b/xmlenc/encrypt_test.go
index cca38604..56b41255 100644
--- a/xmlenc/encrypt_test.go
+++ b/xmlenc/encrypt_test.go
@@ -5,31 +5,54 @@ import (
"encoding/pem"
"math/rand"
"testing"
-
+ "github.com/beevik/etree"
"gotest.tools/assert"
"gotest.tools/golden"
-
- "github.com/beevik/etree"
)
func TestCanEncryptOAEP(t *testing.T) {
- RandReader = rand.New(rand.NewSource(0)) //nolint:gosec // deterministic random numbers for tests
+ 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")
+ })
- pemBlock, _ := pem.Decode(golden.Get(t, "cert.pem"))
- certificate, err := x509.ParseCertificate(pemBlock.Bytes)
- assert.Check(t, err)
+ t.Run("GCM", func(t *testing.T) {
+ RandReader = rand.New(rand.NewSource(0)) //nolint:gosec // deterministic random numbers for tests
- e := OAEP()
- e.BlockCipher = AES128CBC
- e.DigestMethod = &SHA1
+ cert := golden.Get(t, "cert.cert")
+ b, _ := pem.Decode(cert)
+ certificate, err := x509.ParseCertificate(b.Bytes)
+ assert.Check(t, err)
- el, err := e.Encrypt(certificate, golden.Get(t, "plaintext.xml"))
- assert.Check(t, err)
+ e := OAEP()
+ e.BlockCipher = AES128GCM
+ e.DigestMethod = &SHA1
- doc := etree.NewDocument()
- doc.SetRoot(el)
- doc.IndentTabs()
- ciphertext, _ := doc.WriteToString()
+ el, err := e.Encrypt(certificate, golden.Get(t, "plaintext_gcm.xml"), []byte("1234567890AZ"))
+ assert.Check(t, err)
- golden.Assert(t, ciphertext, "ciphertext.xml")
+ doc := etree.NewDocument()
+ doc.SetRoot(el)
+ doc.Indent(4)
+ ciphertext, _ := doc.WriteToString()
+ golden.Assert(t, ciphertext, "ciphertext_gcm.xml")
+ })
}
diff --git a/xmlenc/gcm.go b/xmlenc/gcm.go
new file mode 100644
index 00000000..91911319
--- /dev/null
+++ b/xmlenc/gcm.go
@@ -0,0 +1,143 @@
+package 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)
+}
diff --git a/xmlenc/pubkey.go b/xmlenc/pubkey.go
index 72286863..13d4d9e7 100644
--- a/xmlenc/pubkey.go
+++ b/xmlenc/pubkey.go
@@ -29,7 +29,7 @@ func (e RSA) Algorithm() string {
// 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) (*etree.Element, error) {
+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")
@@ -83,11 +83,11 @@ func (e RSA) Encrypt(certificate interface{}, plaintext []byte) (*etree.Element,
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)
+ encryptedDataEl, err := e.BlockCipher.Encrypt(key, plaintext, nonce)
if err != nil {
return nil, err
}
- encryptedDataEl.InsertChild(encryptedDataEl.FindElement("./CipherData"), keyInfoEl)
+ encryptedDataEl.InsertChildAt(encryptedDataEl.FindElement("./CipherData").Index(), keyInfoEl)
return encryptedDataEl, nil
}
diff --git a/xmlenc/testdata/cert.cert b/xmlenc/testdata/cert.cert
new file mode 100644
index 00000000..fea41ca9
--- /dev/null
+++ b/xmlenc/testdata/cert.cert
@@ -0,0 +1,28 @@
+-----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-----
diff --git a/xmlenc/testdata/cert.key b/xmlenc/testdata/cert.key
new file mode 100644
index 00000000..c9bdc52e
--- /dev/null
+++ b/xmlenc/testdata/cert.key
@@ -0,0 +1,52 @@
+-----BEGIN PRIVATE KEY-----
+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
+xESNSIqhp4ubdwD87Ec0oeEcIqHzwRM=
+-----END PRIVATE KEY-----
diff --git a/xmlenc/testdata/ciphertext_gcm.xml b/xmlenc/testdata/ciphertext_gcm.xml
new file mode 100644
index 00000000..f722e6fc
--- /dev/null
+++ b/xmlenc/testdata/ciphertext_gcm.xml
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
+
+
+ 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
+
+
diff --git a/xmlenc/testdata/input_gcm.xml b/xmlenc/testdata/input_gcm.xml
new file mode 100644
index 00000000..71e0dc9c
--- /dev/null
+++ b/xmlenc/testdata/input_gcm.xml
@@ -0,0 +1,131 @@
+
+
+ https://testidp2.aai.dfn.de/idp/shibboleth
+
+
+
+
+
+
+
+
+
+
+
+ 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
+ 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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
+
+
+
+
+
\ No newline at end of file
diff --git a/xmlenc/testdata/plaintext_gcm.xml b/xmlenc/testdata/plaintext_gcm.xml
new file mode 100644
index 00000000..a3747e57
--- /dev/null
+++ b/xmlenc/testdata/plaintext_gcm.xml
@@ -0,0 +1,104 @@
+
+ https://testidp2.com/idp/shibboleth
+
+
+
+
+
+
+
+
+
+
+ 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
+ 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
+
+
+
\ No newline at end of file
diff --git a/xmlenc/xmlenc.go b/xmlenc/xmlenc.go
index b0ed5bf1..719c523f 100644
--- a/xmlenc/xmlenc.go
+++ b/xmlenc/xmlenc.go
@@ -18,7 +18,7 @@ var RandReader = rand.Reader
// XML EncryptedData or EncryptedKey element. The required type of `key` varies
// depending on the implementation.
type Encrypter interface {
- Encrypt(key interface{}, plaintext []byte) (*etree.Element, error)
+ Encrypt(key interface{}, plaintext []byte, nonce []byte) (*etree.Element, error)
}
// Decrypter is an interface that decrypts things. The Decrypt() method returns the
diff --git a/xmlenc/xmlenc_test.go b/xmlenc/xmlenc_test.go
index 65683dfe..4e17db15 100644
--- a/xmlenc/xmlenc_test.go
+++ b/xmlenc/xmlenc_test.go
@@ -2,7 +2,6 @@ package xmlenc
import (
"math/rand"
- "os"
"testing"
"github.com/beevik/etree"
@@ -10,46 +9,65 @@ import (
is "gotest.tools/assert/cmp"
)
-func TestDataAES128CBC(t *testing.T) {
- RandReader = rand.New(rand.NewSource(0)) //nolint:gosec // deterministic random numbers for tests
- plaintext, err := os.ReadFile("testdata/encrypt-data-aes128-cbc.data")
- assert.Check(t, err)
-
- var ciphertext string
- {
- encrypter := AES128CBC
- cipherEl, encErr := encrypter.Encrypt([]byte("abcdefghijklmnop"), plaintext)
- assert.Check(t, encErr)
-
- doc := etree.NewDocument()
- doc.SetRoot(cipherEl)
- doc.IndentTabs()
- ciphertext, err = doc.WriteToString()
+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)
- }
- {
- decrypter := AES128CBC
- doc := etree.NewDocument()
- err = doc.ReadFromString(ciphertext)
- assert.Check(t, err)
+ var ciphertext string
+ {
+ encrypter := AES128CBC
+ cipherEl, encErr := encrypter.Encrypt([]byte("abcdefghijklmnop"), plaintext, nil)
+ assert.Check(t, encErr)
- actualPlaintext, err := decrypter.Decrypt(
- []byte("abcdefghijklmnop"), doc.Root())
- assert.Check(t, err)
- assert.Check(t, is.DeepEqual(plaintext, actualPlaintext))
- }
+ doc := etree.NewDocument()
+ doc.SetRoot(cipherEl)
+ doc.IndentTabs()
+ ciphertext, err = doc.WriteToString()
+ assert.Check(t, err)
+ }
- {
- decrypter := AES128CBC
- doc := etree.NewDocument()
- err := doc.ReadFromFile("testdata/encrypt-data-aes128-cbc.xml")
- 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))
- }
+ 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)
+ }
+ })
}
/*