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 @@ - - MIIB7zCCAVgCCQDFzbKIp7b3MTANBgkqhkiG9w0BAQUFADA8MQswCQYDVQQGEwJVUzELMAkGA1UECAwCR0ExDDAKBgNVBAoMA2ZvbzESMBAGA1UEAwwJbG9jYWxob3N0MB4XDTEzMTAwMjAwMDg1MVoXDTE0MTAwMjAwMDg1MVowPDELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkdBMQwwCgYDVQQKDANmb28xEjAQBgNVBAMMCWxvY2FsaG9zdDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA1PMHYmhZj308kWLhZVT4vOulqx/9ibm5B86fPWwUKKQ2i12MYtz07tzukPymisTDhQaqyJ8Kqb/6JjhmeMnEOdTvSPmHO8m1ZVveJU6NoKRn/mP/BD7FW52WhbrUXLSeHVSKfWkNk6S4hk9MV9TswTvyRIKvRsw0X/gfnqkroJcCAwEAATANBgkqhkiG9w0BAQUFAAOBgQCMMlIO+GNcGekevKgkakpMdAqJfs24maGb90DvTLbRZRD7Xvn1MnVBBS9hzlXiFLYOInXACMW5gcoRFfeTQLSouMM8o57h0uKjfTmuoWHLQLi6hnF+cvCsEFiJZ4AbF+DgmO6TarJ8O05t8zvnOwJlNCASPZRH/JmF8tX0hoHuAQ== + + MIIB7zCCAVgCCQDFzbKIp7b3MTANBgkqhkiG9w0BAQUFADA8MQswCQYDVQQGEwJVUzELMAkGA1UECAwCR0ExDDAKBgNVBAoMA2ZvbzESMBAGA1UEAwwJbG9jYWxob3N0MB4XDTEzMTAwMjAwMDg1MVoXDTE0MTAwMjAwMDg1MVowPDELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkdBMQwwCgYDVQQKDANmb28xEjAQBgNVBAMMCWxvY2FsaG9zdDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA1PMHYmhZj308kWLhZVT4vOulqx/9ibm5B86fPWwUKKQ2i12MYtz07tzukPymisTDhQaqyJ8Kqb/6JjhmeMnEOdTvSPmHO8m1ZVveJU6NoKRn/mP/BD7FW52WhbrUXLSeHVSKfWkNk6S4hk9MV9TswTvyRIKvRsw0X/gfnqkroJcCAwEAATANBgkqhkiG9w0BAQUFAAOBgQCMMlIO+GNcGekevKgkakpMdAqJfs24maGb90DvTLbRZRD7Xvn1MnVBBS9hzlXiFLYOInXACMW5gcoRFfeTQLSouMM8o57h0uKjfTmuoWHLQLi6hnF+cvCsEFiJZ4AbF+DgmO6TarJ8O05t8zvnOwJlNCASPZRH/JmF8tX0hoHuAQ== - - MIIB7zCCAVgCCQDFzbKIp7b3MTANBgkqhkiG9w0BAQUFADA8MQswCQYDVQQGEwJVUzELMAkGA1UECAwCR0ExDDAKBgNVBAoMA2ZvbzESMBAGA1UEAwwJbG9jYWxob3N0MB4XDTEzMTAwMjAwMDg1MVoXDTE0MTAwMjAwMDg1MVowPDELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkdBMQwwCgYDVQQKDANmb28xEjAQBgNVBAMMCWxvY2FsaG9zdDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA1PMHYmhZj308kWLhZVT4vOulqx/9ibm5B86fPWwUKKQ2i12MYtz07tzukPymisTDhQaqyJ8Kqb/6JjhmeMnEOdTvSPmHO8m1ZVveJU6NoKRn/mP/BD7FW52WhbrUXLSeHVSKfWkNk6S4hk9MV9TswTvyRIKvRsw0X/gfnqkroJcCAwEAATANBgkqhkiG9w0BAQUFAAOBgQCMMlIO+GNcGekevKgkakpMdAqJfs24maGb90DvTLbRZRD7Xvn1MnVBBS9hzlXiFLYOInXACMW5gcoRFfeTQLSouMM8o57h0uKjfTmuoWHLQLi6hnF+cvCsEFiJZ4AbF+DgmO6TarJ8O05t8zvnOwJlNCASPZRH/JmF8tX0hoHuAQ== + + MIIB7zCCAVgCCQDFzbKIp7b3MTANBgkqhkiG9w0BAQUFADA8MQswCQYDVQQGEwJVUzELMAkGA1UECAwCR0ExDDAKBgNVBAoMA2ZvbzESMBAGA1UEAwwJbG9jYWxob3N0MB4XDTEzMTAwMjAwMDg1MVoXDTE0MTAwMjAwMDg1MVowPDELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkdBMQwwCgYDVQQKDANmb28xEjAQBgNVBAMMCWxvY2FsaG9zdDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA1PMHYmhZj308kWLhZVT4vOulqx/9ibm5B86fPWwUKKQ2i12MYtz07tzukPymisTDhQaqyJ8Kqb/6JjhmeMnEOdTvSPmHO8m1ZVveJU6NoKRn/mP/BD7FW52WhbrUXLSeHVSKfWkNk6S4hk9MV9TswTvyRIKvRsw0X/gfnqkroJcCAwEAATANBgkqhkiG9w0BAQUFAAOBgQCMMlIO+GNcGekevKgkakpMdAqJfs24maGb90DvTLbRZRD7Xvn1MnVBBS9hzlXiFLYOInXACMW5gcoRFfeTQLSouMM8o57h0uKjfTmuoWHLQLi6hnF+cvCsEFiJZ4AbF+DgmO6TarJ8O05t8zvnOwJlNCASPZRH/JmF8tX0hoHuAQ== @@ -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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 - \ 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.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 \ 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 -===================== - -[![Build Status](https://travis-ci.org/dchest/uniuri.svg)](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 - -[![Build Status](https://travis-ci.org/russellhaering/goxmldsig.svg?branch=master)](https://travis-ci.org/russellhaering/goxmldsig) -[![GoDoc](https://godoc.org/github.com/russellhaering/goxmldsig?status.svg)](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 -==== - -[![GoDoc](https://godoc.org/github.com/zenazn/goji/web?status.svg)](https://godoc.org/github.com/zenazn/goji/web) [![Build Status](https://travis-ci.org/zenazn/goji.svg?branch=master)](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) + } + }) } /*