From afeab7b2d4cc70abd8e3911f3cecdb13d9699e5a Mon Sep 17 00:00:00 2001 From: Anant Prakash Date: Tue, 22 May 2018 14:43:58 +0530 Subject: [PATCH] Add token generation using go macaroon (#437) * Add Go macaroon library Signed-off-by: Anant Prakash * Add macaroon generation and serialization, for login token. Signed-off-by: Anant Prakash * Remove copyright, trim empty lines * Make Serialize functions private * Fix typos --- .../dendrite/clientapi/auth/tokens/tokens.go | 132 +++ .../clientapi/auth/tokens/tokens_test.go | 80 ++ vendor/manifest | 27 + .../github.com/go-macaroon/macaroon/LICENSE | 26 + .../github.com/go-macaroon/macaroon/README.md | 159 +++ .../src/github.com/go-macaroon/macaroon/TODO | 4 + .../go-macaroon/macaroon/bench_test.go | 109 +++ .../github.com/go-macaroon/macaroon/crypto.go | 91 ++ .../go-macaroon/macaroon/crypto_test.go | 66 ++ .../go-macaroon/macaroon/export_test.go | 14 + .../go-macaroon/macaroon/macaroon.go | 366 +++++++ .../go-macaroon/macaroon/macaroon_test.go | 914 ++++++++++++++++++ .../go-macaroon/macaroon/marshal-v1.go | 190 ++++ .../go-macaroon/macaroon/marshal-v2.go | 253 +++++ .../go-macaroon/macaroon/marshal.go | 239 +++++ .../go-macaroon/macaroon/marshal_test.go | 203 ++++ .../go-macaroon/macaroon/packet-v1.go | 133 +++ .../go-macaroon/macaroon/packet-v1_test.go | 98 ++ .../go-macaroon/macaroon/packet-v2.go | 117 +++ .../go-macaroon/macaroon/packet-v2_test.go | 126 +++ .../github.com/go-macaroon/macaroon/trace.go | 102 ++ .../x/crypto/nacl/secretbox/example_test.go | 53 + .../x/crypto/nacl/secretbox/secretbox.go | 166 ++++ .../x/crypto/nacl/secretbox/secretbox_test.go | 154 +++ .../golang.org/x/crypto/poly1305/poly1305.go | 33 + .../x/crypto/poly1305/poly1305_test.go | 159 +++ .../golang.org/x/crypto/poly1305/sum_amd64.go | 22 + .../golang.org/x/crypto/poly1305/sum_amd64.s | 125 +++ .../golang.org/x/crypto/poly1305/sum_arm.go | 22 + .../golang.org/x/crypto/poly1305/sum_arm.s | 427 ++++++++ .../golang.org/x/crypto/poly1305/sum_ref.go | 141 +++ .../x/crypto/salsa20/salsa/hsalsa20.go | 144 +++ .../x/crypto/salsa20/salsa/salsa2020_amd64.s | 889 +++++++++++++++++ .../x/crypto/salsa20/salsa/salsa208.go | 199 ++++ .../x/crypto/salsa20/salsa/salsa20_amd64.go | 24 + .../x/crypto/salsa20/salsa/salsa20_ref.go | 234 +++++ .../x/crypto/salsa20/salsa/salsa_test.go | 54 ++ 37 files changed, 6295 insertions(+) create mode 100644 src/github.com/matrix-org/dendrite/clientapi/auth/tokens/tokens.go create mode 100644 src/github.com/matrix-org/dendrite/clientapi/auth/tokens/tokens_test.go create mode 100644 vendor/src/github.com/go-macaroon/macaroon/LICENSE create mode 100644 vendor/src/github.com/go-macaroon/macaroon/README.md create mode 100644 vendor/src/github.com/go-macaroon/macaroon/TODO create mode 100644 vendor/src/github.com/go-macaroon/macaroon/bench_test.go create mode 100644 vendor/src/github.com/go-macaroon/macaroon/crypto.go create mode 100644 vendor/src/github.com/go-macaroon/macaroon/crypto_test.go create mode 100644 vendor/src/github.com/go-macaroon/macaroon/export_test.go create mode 100644 vendor/src/github.com/go-macaroon/macaroon/macaroon.go create mode 100644 vendor/src/github.com/go-macaroon/macaroon/macaroon_test.go create mode 100644 vendor/src/github.com/go-macaroon/macaroon/marshal-v1.go create mode 100644 vendor/src/github.com/go-macaroon/macaroon/marshal-v2.go create mode 100644 vendor/src/github.com/go-macaroon/macaroon/marshal.go create mode 100644 vendor/src/github.com/go-macaroon/macaroon/marshal_test.go create mode 100644 vendor/src/github.com/go-macaroon/macaroon/packet-v1.go create mode 100644 vendor/src/github.com/go-macaroon/macaroon/packet-v1_test.go create mode 100644 vendor/src/github.com/go-macaroon/macaroon/packet-v2.go create mode 100644 vendor/src/github.com/go-macaroon/macaroon/packet-v2_test.go create mode 100644 vendor/src/github.com/go-macaroon/macaroon/trace.go create mode 100644 vendor/src/golang.org/x/crypto/nacl/secretbox/example_test.go create mode 100644 vendor/src/golang.org/x/crypto/nacl/secretbox/secretbox.go create mode 100644 vendor/src/golang.org/x/crypto/nacl/secretbox/secretbox_test.go create mode 100644 vendor/src/golang.org/x/crypto/poly1305/poly1305.go create mode 100644 vendor/src/golang.org/x/crypto/poly1305/poly1305_test.go create mode 100644 vendor/src/golang.org/x/crypto/poly1305/sum_amd64.go create mode 100644 vendor/src/golang.org/x/crypto/poly1305/sum_amd64.s create mode 100644 vendor/src/golang.org/x/crypto/poly1305/sum_arm.go create mode 100644 vendor/src/golang.org/x/crypto/poly1305/sum_arm.s create mode 100644 vendor/src/golang.org/x/crypto/poly1305/sum_ref.go create mode 100644 vendor/src/golang.org/x/crypto/salsa20/salsa/hsalsa20.go create mode 100644 vendor/src/golang.org/x/crypto/salsa20/salsa/salsa2020_amd64.s create mode 100644 vendor/src/golang.org/x/crypto/salsa20/salsa/salsa208.go create mode 100644 vendor/src/golang.org/x/crypto/salsa20/salsa/salsa20_amd64.go create mode 100644 vendor/src/golang.org/x/crypto/salsa20/salsa/salsa20_ref.go create mode 100644 vendor/src/golang.org/x/crypto/salsa20/salsa/salsa_test.go diff --git a/src/github.com/matrix-org/dendrite/clientapi/auth/tokens/tokens.go b/src/github.com/matrix-org/dendrite/clientapi/auth/tokens/tokens.go new file mode 100644 index 00000000..431d29e1 --- /dev/null +++ b/src/github.com/matrix-org/dendrite/clientapi/auth/tokens/tokens.go @@ -0,0 +1,132 @@ +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package tokens + +import ( + "encoding/base64" + "errors" + "fmt" + "strconv" + "time" + + "github.com/go-macaroon/macaroon" +) + +const ( + macaroonVersion = macaroon.V2 + defaultDuration = 2 * 60 + // UserPrefix is a common prefix for every user_id caveat + UserPrefix = "user_id = " + // TimePrefix is a common prefix for every expiry caveat + TimePrefix = "time < " + // Gen is a common caveat for every token + Gen = "gen = 1" +) + +// TokenOptions represent parameters of Token +type TokenOptions struct { + ServerPrivateKey []byte `yaml:"private_key"` + ServerName string `yaml:"server_name"` + UserID string `json:"user_id"` + Duration int // optional +} + +// GenerateLoginToken generates a short term login token to be used as +// token authentication ("m.login.token") +func GenerateLoginToken(op TokenOptions) (string, error) { + if !isValidTokenOptions(op) { + return "", errors.New("The given TokenOptions is invalid") + } + + mac, err := generateBaseMacaroon(op.ServerPrivateKey, op.ServerName, op.UserID) + if err != nil { + return "", err + } + + if op.Duration == 0 { + op.Duration = defaultDuration + } + now := time.Now().Second() + expiryCaveat := TimePrefix + strconv.Itoa(now+op.Duration) + err = mac.AddFirstPartyCaveat([]byte(expiryCaveat)) + if err != nil { + return "", macaroonError(err) + } + + urlSafeEncode, err := serializeMacaroon(*mac) + if err != nil { + return "", macaroonError(err) + } + return urlSafeEncode, nil +} + +// isValidTokenOptions checks for required fields in a TokenOptions +func isValidTokenOptions(op TokenOptions) bool { + if op.ServerPrivateKey == nil || op.ServerName == "" || op.UserID == "" { + return false + } + return true +} + +// generateBaseMacaroon generates a base macaroon common for accessToken & loginToken. +// Returns a macaroon tied with userID, +// returns an error if something goes wrong. +func generateBaseMacaroon( + secret []byte, ServerName string, userID string, +) (*macaroon.Macaroon, error) { + mac, err := macaroon.New(secret, []byte(userID), ServerName, macaroonVersion) + if err != nil { + return nil, macaroonError(err) + } + + err = mac.AddFirstPartyCaveat([]byte(Gen)) + if err != nil { + return nil, macaroonError(err) + } + + err = mac.AddFirstPartyCaveat([]byte(UserPrefix + userID)) + if err != nil { + return nil, macaroonError(err) + } + + return mac, nil +} + +func macaroonError(err error) error { + return fmt.Errorf("Macaroon creation failed: %s", err.Error()) +} + +// serializeMacaroon takes a macaroon to be serialized. +// returns its base64 encoded string, URL safe, which can be sent via web, email, etc. +func serializeMacaroon(m macaroon.Macaroon) (string, error) { + bin, err := m.MarshalBinary() + if err != nil { + return "", err + } + + urlSafeEncode := base64.RawURLEncoding.EncodeToString(bin) + return urlSafeEncode, nil +} + +// deSerializeMacaroon takes a base64 encoded string of a macaroon to be de-serialized. +// Returns a macaroon. On failure returns error with description. +func deSerializeMacaroon(urlSafeEncode string) (macaroon.Macaroon, error) { + var mac macaroon.Macaroon + bin, err := base64.RawURLEncoding.DecodeString(urlSafeEncode) + if err != nil { + return mac, err + } + + err = mac.UnmarshalBinary(bin) + return mac, err +} diff --git a/src/github.com/matrix-org/dendrite/clientapi/auth/tokens/tokens_test.go b/src/github.com/matrix-org/dendrite/clientapi/auth/tokens/tokens_test.go new file mode 100644 index 00000000..7ffe9809 --- /dev/null +++ b/src/github.com/matrix-org/dendrite/clientapi/auth/tokens/tokens_test.go @@ -0,0 +1,80 @@ +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package tokens + +import ( + "testing" +) + +var ( + validTokenOp = TokenOptions{ + ServerPrivateKey: []byte("aSecretKey"), + ServerName: "aRandomServerName", + UserID: "aRandomUserID", + } + invalidTokenOps = map[string]TokenOptions{ + "ServerPrivateKey": { + ServerName: "aRandomServerName", + UserID: "aRandomUserID", + }, + "ServerName": { + ServerPrivateKey: []byte("aSecretKey"), + UserID: "aRandomUserID", + }, + "UserID": { + ServerPrivateKey: []byte("aSecretKey"), + ServerName: "aRandomServerName", + }, + } +) + +func TestGenerateLoginToken(t *testing.T) { + // Test valid + _, err := GenerateLoginToken(validTokenOp) + if err != nil { + t.Errorf("Token generation failed for valid TokenOptions with err: %s", err.Error()) + } + + // Test invalids + for missing, invalidTokenOp := range invalidTokenOps { + _, err := GenerateLoginToken(invalidTokenOp) + if err == nil { + t.Errorf("Token generation should fail for TokenOptions with missing %s", missing) + } + } +} + +func serializationTestError(err error) string { + return "Token Serialization test failed with err: " + err.Error() +} + +func TestSerialization(t *testing.T) { + fakeToken, err := GenerateLoginToken(validTokenOp) + if err != nil { + t.Errorf(serializationTestError(err)) + } + + fakeMacaroon, err := deSerializeMacaroon(fakeToken) + if err != nil { + t.Errorf(serializationTestError(err)) + } + + sameFakeToken, err := serializeMacaroon(fakeMacaroon) + if err != nil { + t.Errorf(serializationTestError(err)) + } + + if sameFakeToken != fakeToken { + t.Errorf("Token Serialization mismatch") + } +} diff --git a/vendor/manifest b/vendor/manifest index c17b5cf8..659081be 100644 --- a/vendor/manifest +++ b/vendor/manifest @@ -77,6 +77,12 @@ "revision": "44cc805cf13205b55f69e14bcb69867d1ae92f98", "branch": "master" }, + { + "importpath": "github.com/go-macaroon/macaroon", + "repository": "https://github.com/go-macaroon/macaroon", + "revision": "bed2a428da6e56d950bed5b41fcbae3141e5b0d0", + "branch": "HEAD" + }, { "importpath": "github.com/golang/protobuf/proto", "repository": "https://github.com/golang/protobuf", @@ -369,6 +375,27 @@ "branch": "master", "path": "/ed25519" }, + { + "importpath": "golang.org/x/crypto/nacl/secretbox", + "repository": "https://go.googlesource.com/crypto", + "revision": "d6449816ce06963d9d136eee5a56fca5b0616e7e", + "branch": "master", + "path": "/nacl/secretbox" + }, + { + "importpath": "golang.org/x/crypto/poly1305", + "repository": "https://go.googlesource.com/crypto", + "revision": "d6449816ce06963d9d136eee5a56fca5b0616e7e", + "branch": "master", + "path": "/poly1305" + }, + { + "importpath": "golang.org/x/crypto/salsa20/salsa", + "repository": "https://go.googlesource.com/crypto", + "revision": "d6449816ce06963d9d136eee5a56fca5b0616e7e", + "branch": "master", + "path": "/salsa20/salsa" + }, { "importpath": "golang.org/x/crypto/ssh", "repository": "https://go.googlesource.com/crypto", diff --git a/vendor/src/github.com/go-macaroon/macaroon/LICENSE b/vendor/src/github.com/go-macaroon/macaroon/LICENSE new file mode 100644 index 00000000..9525fc82 --- /dev/null +++ b/vendor/src/github.com/go-macaroon/macaroon/LICENSE @@ -0,0 +1,26 @@ +Copyright © 2014, Roger Peppe +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + * Neither the name of this project nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED +TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/vendor/src/github.com/go-macaroon/macaroon/README.md b/vendor/src/github.com/go-macaroon/macaroon/README.md new file mode 100644 index 00000000..2de9436b --- /dev/null +++ b/vendor/src/github.com/go-macaroon/macaroon/README.md @@ -0,0 +1,159 @@ +# macaroon +-- + import "gopkg.in/macaroon.v1" + +The macaroon package implements macaroons as described in the paper "Macaroons: +Cookies with Contextual Caveats for Decentralized Authorization in the Cloud" +(http://theory.stanford.edu/~ataly/Papers/macaroons.pdf) + +See the macaroon bakery packages at http://godoc.org/gopkg.in/macaroon-bakery.v0 +for higher level services and operations that use macaroons. + +## Usage + +#### type Caveat + +```go +type Caveat struct { + Id string + Location string +} +``` + + +#### type Macaroon + +```go +type Macaroon struct { +} +``` + +Macaroon holds a macaroon. See Fig. 7 of +http://theory.stanford.edu/~ataly/Papers/macaroons.pdf for a description of the +data contained within. Macaroons are mutable objects - use Clone as appropriate +to avoid unwanted mutation. + +#### func New + +```go +func New(rootKey []byte, id, loc string) (*Macaroon, error) +``` +New returns a new macaroon with the given root key, identifier and location. + +#### func (*Macaroon) AddFirstPartyCaveat + +```go +func (m *Macaroon) AddFirstPartyCaveat(caveatId string) error +``` +AddFirstPartyCaveat adds a caveat that will be verified by the target service. + +#### func (*Macaroon) AddThirdPartyCaveat + +```go +func (m *Macaroon) AddThirdPartyCaveat(rootKey []byte, caveatId string, loc string) error +``` +AddThirdPartyCaveat adds a third-party caveat to the macaroon, using the given +shared root key, caveat id and location hint. The caveat id should encode the +root key in some way, either by encrypting it with a key known to the third +party or by holding a reference to it stored in the third party's storage. + +#### func (*Macaroon) Bind + +```go +func (m *Macaroon) Bind(sig []byte) +``` +Bind prepares the macaroon for being used to discharge the macaroon with the +given signature sig. This must be used before it is used in the discharges +argument to Verify. + +#### func (*Macaroon) Caveats + +```go +func (m *Macaroon) Caveats() []Caveat +``` +Caveats returns the macaroon's caveats. This method will probably change, and +it's important not to change the returned caveat. + +#### func (*Macaroon) Clone + +```go +func (m *Macaroon) Clone() *Macaroon +``` +Clone returns a copy of the receiving macaroon. + +#### func (*Macaroon) Id + +```go +func (m *Macaroon) Id() string +``` +Id returns the id of the macaroon. This can hold arbitrary information. + +#### func (*Macaroon) Location + +```go +func (m *Macaroon) Location() string +``` +Location returns the macaroon's location hint. This is not verified as part of +the macaroon. + +#### func (*Macaroon) MarshalBinary + +```go +func (m *Macaroon) MarshalBinary() ([]byte, error) +``` +MarshalBinary implements encoding.BinaryMarshaler. + +#### func (*Macaroon) MarshalJSON + +```go +func (m *Macaroon) MarshalJSON() ([]byte, error) +``` +MarshalJSON implements json.Marshaler. + +#### func (*Macaroon) Signature + +```go +func (m *Macaroon) Signature() []byte +``` +Signature returns the macaroon's signature. + +#### func (*Macaroon) UnmarshalBinary + +```go +func (m *Macaroon) UnmarshalBinary(data []byte) error +``` +UnmarshalBinary implements encoding.BinaryUnmarshaler. + +#### func (*Macaroon) UnmarshalJSON + +```go +func (m *Macaroon) UnmarshalJSON(jsonData []byte) error +``` +UnmarshalJSON implements json.Unmarshaler. + +#### func (*Macaroon) Verify + +```go +func (m *Macaroon) Verify(rootKey []byte, check func(caveat string) error, discharges []*Macaroon) error +``` +Verify verifies that the receiving macaroon is valid. The root key must be the +same that the macaroon was originally minted with. The check function is called +to verify each first-party caveat - it should return an error if the condition +is not met. + +The discharge macaroons should be provided in discharges. + +Verify returns true if the verification succeeds; if returns (false, nil) if the +verification fails, and (false, err) if the verification cannot be asserted (but +may not be false). + +TODO(rog) is there a possible DOS attack that can cause this function to +infinitely recurse? + +#### type Verifier + +```go +type Verifier interface { + Verify(m *Macaroon, rootKey []byte) (bool, error) +} +``` diff --git a/vendor/src/github.com/go-macaroon/macaroon/TODO b/vendor/src/github.com/go-macaroon/macaroon/TODO new file mode 100644 index 00000000..f272ce3e --- /dev/null +++ b/vendor/src/github.com/go-macaroon/macaroon/TODO @@ -0,0 +1,4 @@ +macaroon: + + - verify that all signature calculations to correspond exactly + with libmacaroons. diff --git a/vendor/src/github.com/go-macaroon/macaroon/bench_test.go b/vendor/src/github.com/go-macaroon/macaroon/bench_test.go new file mode 100644 index 00000000..f56bd06e --- /dev/null +++ b/vendor/src/github.com/go-macaroon/macaroon/bench_test.go @@ -0,0 +1,109 @@ +package macaroon_test + +import ( + "crypto/rand" + "encoding/base64" + "testing" + + "gopkg.in/macaroon.v2" +) + +func randomBytes(n int) []byte { + b := make([]byte, n) + _, err := rand.Read(b) + if err != nil { + panic(err) + } + return b +} + +func BenchmarkNew(b *testing.B) { + rootKey := randomBytes(24) + id := []byte(base64.StdEncoding.EncodeToString(randomBytes(100))) + loc := base64.StdEncoding.EncodeToString(randomBytes(40)) + b.ResetTimer() + for i := b.N - 1; i >= 0; i-- { + MustNew(rootKey, id, loc, macaroon.LatestVersion) + } +} + +func BenchmarkAddCaveat(b *testing.B) { + rootKey := randomBytes(24) + id := []byte(base64.StdEncoding.EncodeToString(randomBytes(100))) + loc := base64.StdEncoding.EncodeToString(randomBytes(40)) + b.ResetTimer() + for i := b.N - 1; i >= 0; i-- { + b.StopTimer() + m := MustNew(rootKey, id, loc, macaroon.LatestVersion) + b.StartTimer() + m.AddFirstPartyCaveat([]byte("some caveat stuff")) + } +} + +func benchmarkVerify(b *testing.B, mspecs []macaroonSpec) { + rootKey, macaroons := makeMacaroons(mspecs) + check := func(string) error { + return nil + } + b.ResetTimer() + for i := b.N - 1; i >= 0; i-- { + err := macaroons[0].Verify(rootKey, check, macaroons[1:]) + if err != nil { + b.Fatalf("verification failed: %v", err) + } + } +} + +func BenchmarkVerifyLarge(b *testing.B) { + benchmarkVerify(b, multilevelThirdPartyCaveatMacaroons) +} + +func BenchmarkVerifySmall(b *testing.B) { + benchmarkVerify(b, []macaroonSpec{{ + rootKey: "root-key", + id: "root-id", + caveats: []caveat{{ + condition: "wonderful", + }}, + }}) +} + +func BenchmarkMarshalJSON(b *testing.B) { + rootKey := randomBytes(24) + id := []byte(base64.StdEncoding.EncodeToString(randomBytes(100))) + loc := base64.StdEncoding.EncodeToString(randomBytes(40)) + m := MustNew(rootKey, id, loc, macaroon.LatestVersion) + b.ResetTimer() + for i := b.N - 1; i >= 0; i-- { + _, err := m.MarshalJSON() + if err != nil { + b.Fatalf("cannot marshal JSON: %v", err) + } + } +} + +func MustNew(rootKey, id []byte, loc string, vers macaroon.Version) *macaroon.Macaroon { + m, err := macaroon.New(rootKey, id, loc, vers) + if err != nil { + panic(err) + } + return m +} + +func BenchmarkUnmarshalJSON(b *testing.B) { + rootKey := randomBytes(24) + id := []byte(base64.StdEncoding.EncodeToString(randomBytes(100))) + loc := base64.StdEncoding.EncodeToString(randomBytes(40)) + m := MustNew(rootKey, id, loc, macaroon.LatestVersion) + data, err := m.MarshalJSON() + if err != nil { + b.Fatalf("cannot marshal JSON: %v", err) + } + for i := b.N - 1; i >= 0; i-- { + var m macaroon.Macaroon + err := m.UnmarshalJSON(data) + if err != nil { + b.Fatalf("cannot unmarshal JSON: %v", err) + } + } +} diff --git a/vendor/src/github.com/go-macaroon/macaroon/crypto.go b/vendor/src/github.com/go-macaroon/macaroon/crypto.go new file mode 100644 index 00000000..c7e98e51 --- /dev/null +++ b/vendor/src/github.com/go-macaroon/macaroon/crypto.go @@ -0,0 +1,91 @@ +package macaroon + +import ( + "crypto/hmac" + "crypto/sha256" + "fmt" + "hash" + "io" + + "golang.org/x/crypto/nacl/secretbox" +) + +func keyedHash(key *[hashLen]byte, text []byte) *[hashLen]byte { + h := keyedHasher(key) + h.Write([]byte(text)) + var sum [hashLen]byte + hashSum(h, &sum) + return &sum +} + +func keyedHasher(key *[hashLen]byte) hash.Hash { + return hmac.New(sha256.New, key[:]) +} + +var keyGen = []byte("macaroons-key-generator") + +// makeKey derives a fixed length key from a variable +// length key. The keyGen constant is the same +// as that used in libmacaroons. +func makeKey(variableKey []byte) *[keyLen]byte { + h := hmac.New(sha256.New, keyGen) + h.Write(variableKey) + var key [keyLen]byte + hashSum(h, &key) + return &key +} + +// hashSum calls h.Sum to put the sum into +// the given destination. It also sanity +// checks that the result really is the expected +// size. +func hashSum(h hash.Hash, dest *[hashLen]byte) { + r := h.Sum(dest[:0]) + if len(r) != len(dest) { + panic("hash size inconsistency") + } +} + +const ( + keyLen = 32 + nonceLen = 24 + hashLen = sha256.Size +) + +func newNonce(r io.Reader) (*[nonceLen]byte, error) { + var nonce [nonceLen]byte + _, err := r.Read(nonce[:]) + if err != nil { + return nil, fmt.Errorf("cannot generate random bytes: %v", err) + } + return &nonce, nil +} + +func encrypt(key *[keyLen]byte, text *[hashLen]byte, r io.Reader) ([]byte, error) { + nonce, err := newNonce(r) + if err != nil { + return nil, err + } + out := make([]byte, 0, len(nonce)+secretbox.Overhead+len(text)) + out = append(out, nonce[:]...) + return secretbox.Seal(out, text[:], nonce, key), nil +} + +func decrypt(key *[keyLen]byte, ciphertext []byte) (*[hashLen]byte, error) { + if len(ciphertext) < nonceLen+secretbox.Overhead { + return nil, fmt.Errorf("message too short") + } + var nonce [nonceLen]byte + copy(nonce[:], ciphertext) + ciphertext = ciphertext[nonceLen:] + text, ok := secretbox.Open(nil, ciphertext, &nonce, key) + if !ok { + return nil, fmt.Errorf("decryption failure") + } + if len(text) != hashLen { + return nil, fmt.Errorf("decrypted text is wrong length") + } + var rtext [hashLen]byte + copy(rtext[:], text) + return &rtext, nil +} diff --git a/vendor/src/github.com/go-macaroon/macaroon/crypto_test.go b/vendor/src/github.com/go-macaroon/macaroon/crypto_test.go new file mode 100644 index 00000000..49c30dbc --- /dev/null +++ b/vendor/src/github.com/go-macaroon/macaroon/crypto_test.go @@ -0,0 +1,66 @@ +package macaroon + +import ( + "crypto/rand" + "fmt" + + "golang.org/x/crypto/nacl/secretbox" + gc "gopkg.in/check.v1" +) + +type cryptoSuite struct{} + +var _ = gc.Suite(&cryptoSuite{}) + +var testCryptKey = &[hashLen]byte{'k', 'e', 'y'} +var testCryptText = &[hashLen]byte{'t', 'e', 'x', 't'} + +func (*cryptoSuite) TestEncDec(c *gc.C) { + b, err := encrypt(testCryptKey, testCryptText, rand.Reader) + c.Assert(err, gc.IsNil) + t, err := decrypt(testCryptKey, b) + c.Assert(err, gc.IsNil) + c.Assert(string(t[:]), gc.Equals, string(testCryptText[:])) +} + +func (*cryptoSuite) TestUniqueNonces(c *gc.C) { + nonces := make(map[string]struct{}) + for i := 0; i < 100; i++ { + nonce, err := newNonce(rand.Reader) + c.Assert(err, gc.IsNil) + nonces[string(nonce[:])] = struct{}{} + } + c.Assert(nonces, gc.HasLen, 100, gc.Commentf("duplicate nonce detected")) +} + +type ErrorReader struct{} + +func (*ErrorReader) Read([]byte) (int, error) { + return 0, fmt.Errorf("fail") +} + +func (*cryptoSuite) TestBadRandom(c *gc.C) { + _, err := newNonce(&ErrorReader{}) + c.Assert(err, gc.ErrorMatches, "^cannot generate random bytes:.*") + + _, err = encrypt(testCryptKey, testCryptText, &ErrorReader{}) + c.Assert(err, gc.ErrorMatches, "^cannot generate random bytes:.*") +} + +func (*cryptoSuite) TestBadCiphertext(c *gc.C) { + buf := randomBytes(nonceLen + secretbox.Overhead) + for i := range buf { + _, err := decrypt(testCryptKey, buf[0:i]) + c.Assert(err, gc.ErrorMatches, "message too short") + } + _, err := decrypt(testCryptKey, buf) + c.Assert(err, gc.ErrorMatches, "decryption failure") +} + +func randomBytes(n int) []byte { + buf := make([]byte, n) + if _, err := rand.Reader.Read(buf); err != nil { + panic(err) + } + return buf +} diff --git a/vendor/src/github.com/go-macaroon/macaroon/export_test.go b/vendor/src/github.com/go-macaroon/macaroon/export_test.go new file mode 100644 index 00000000..6cb3ac4d --- /dev/null +++ b/vendor/src/github.com/go-macaroon/macaroon/export_test.go @@ -0,0 +1,14 @@ +package macaroon + +var ( + AddThirdPartyCaveatWithRand = (*Macaroon).addThirdPartyCaveatWithRand +) + +type MacaroonJSONV2 macaroonJSONV2 + +// SetVersion sets the version field of m to v; +// usually so that we can compare it for deep equality with +// another differently unmarshaled macaroon. +func (m *Macaroon) SetVersion(v Version) { + m.version = v +} diff --git a/vendor/src/github.com/go-macaroon/macaroon/macaroon.go b/vendor/src/github.com/go-macaroon/macaroon/macaroon.go new file mode 100644 index 00000000..6e99fd04 --- /dev/null +++ b/vendor/src/github.com/go-macaroon/macaroon/macaroon.go @@ -0,0 +1,366 @@ +// The macaroon package implements macaroons as described in +// the paper "Macaroons: Cookies with Contextual Caveats for +// Decentralized Authorization in the Cloud" +// (http://theory.stanford.edu/~ataly/Papers/macaroons.pdf) +// +// See the macaroon bakery packages at http://godoc.org/gopkg.in/macaroon-bakery.v1 +// for higher level services and operations that use macaroons. +package macaroon + +import ( + "bytes" + "crypto/hmac" + "crypto/rand" + "fmt" + "io" + "unicode/utf8" +) + +// Macaroon holds a macaroon. +// See Fig. 7 of http://theory.stanford.edu/~ataly/Papers/macaroons.pdf +// for a description of the data contained within. +// Macaroons are mutable objects - use Clone as appropriate +// to avoid unwanted mutation. +type Macaroon struct { + location string + id []byte + caveats []Caveat + sig [hashLen]byte + version Version +} + +// Caveat holds a first person or third party caveat. +type Caveat struct { + // Id holds the id of the caveat. For first + // party caveats this holds the condition; + // for third party caveats this holds the encrypted + // third party caveat. + Id []byte + + // VerificationId holds the verification id. If this is + // non-empty, it's a third party caveat. + VerificationId []byte + + // For third-party caveats, Location holds the + // ocation hint. Note that this is not signature checked + // as part of the caveat, so should only + // be used as a hint. + Location string +} + +// isThirdParty reports whether the caveat must be satisfied +// by some third party (if not, it's a first person caveat). +func (cav *Caveat) isThirdParty() bool { + return len(cav.VerificationId) > 0 +} + +// New returns a new macaroon with the given root key, +// identifier, location and version. +func New(rootKey, id []byte, loc string, version Version) (*Macaroon, error) { + var m Macaroon + if version < V2 { + if !utf8.Valid(id) { + return nil, fmt.Errorf("invalid id for %v macaroon", id) + } + // TODO check id length too. + } + if version < V1 || version > LatestVersion { + return nil, fmt.Errorf("invalid version %v", version) + } + m.version = version + m.init(append([]byte(nil), id...), loc, version) + derivedKey := makeKey(rootKey) + m.sig = *keyedHash(derivedKey, m.id) + return &m, nil +} + +// init initializes the macaroon. It retains a reference to id. +func (m *Macaroon) init(id []byte, loc string, vers Version) { + m.location = loc + m.id = append([]byte(nil), id...) + m.version = vers +} + +// SetLocation sets the location associated with the macaroon. +// Note that the location is not included in the macaroon's +// hash chain, so this does not change the signature. +func (m *Macaroon) SetLocation(loc string) { + m.location = loc +} + +// Clone returns a copy of the receiving macaroon. +func (m *Macaroon) Clone() *Macaroon { + m1 := *m + // Ensure that if any caveats are appended to the new + // macaroon, it will copy the caveats. + m1.caveats = m1.caveats[0:len(m1.caveats):len(m1.caveats)] + return &m1 +} + +// Location returns the macaroon's location hint. This is +// not verified as part of the macaroon. +func (m *Macaroon) Location() string { + return m.location +} + +// Id returns the id of the macaroon. This can hold +// arbitrary information. +func (m *Macaroon) Id() []byte { + return append([]byte(nil), m.id...) +} + +// Signature returns the macaroon's signature. +func (m *Macaroon) Signature() []byte { + // sig := m.sig + // return sig[:] + // Work around https://github.com/golang/go/issues/9537 + sig := new([hashLen]byte) + *sig = m.sig + return sig[:] +} + +// Caveats returns the macaroon's caveats. +// This method will probably change, and it's important not to change the returned caveat. +func (m *Macaroon) Caveats() []Caveat { + return m.caveats[0:len(m.caveats):len(m.caveats)] +} + +// appendCaveat appends a caveat without modifying the macaroon's signature. +func (m *Macaroon) appendCaveat(caveatId, verificationId []byte, loc string) { + m.caveats = append(m.caveats, Caveat{ + Id: caveatId, + VerificationId: verificationId, + Location: loc, + }) +} + +func (m *Macaroon) addCaveat(caveatId, verificationId []byte, loc string) error { + if m.version < V2 { + if !utf8.Valid(caveatId) { + return fmt.Errorf("invalid caveat id for %v macaroon", m.version) + } + // TODO check caveat length too. + } + m.appendCaveat(caveatId, verificationId, loc) + if len(verificationId) == 0 { + m.sig = *keyedHash(&m.sig, caveatId) + } else { + m.sig = *keyedHash2(&m.sig, verificationId, caveatId) + } + return nil +} + +func keyedHash2(key *[keyLen]byte, d1, d2 []byte) *[hashLen]byte { + var data [hashLen * 2]byte + copy(data[0:], keyedHash(key, d1)[:]) + copy(data[hashLen:], keyedHash(key, d2)[:]) + return keyedHash(key, data[:]) +} + +// Bind prepares the macaroon for being used to discharge the +// macaroon with the given signature sig. This must be +// used before it is used in the discharges argument to Verify. +func (m *Macaroon) Bind(sig []byte) { + m.sig = *bindForRequest(sig, &m.sig) +} + +// AddFirstPartyCaveat adds a caveat that will be verified +// by the target service. +func (m *Macaroon) AddFirstPartyCaveat(condition []byte) error { + m.addCaveat(condition, nil, "") + return nil +} + +// AddThirdPartyCaveat adds a third-party caveat to the macaroon, +// using the given shared root key, caveat id and location hint. +// The caveat id should encode the root key in some +// way, either by encrypting it with a key known to the third party +// or by holding a reference to it stored in the third party's +// storage. +func (m *Macaroon) AddThirdPartyCaveat(rootKey, caveatId []byte, loc string) error { + return m.addThirdPartyCaveatWithRand(rootKey, caveatId, loc, rand.Reader) +} + +// addThirdPartyCaveatWithRand adds a third-party caveat to the macaroon, using +// the given source of randomness for encrypting the caveat id. +func (m *Macaroon) addThirdPartyCaveatWithRand(rootKey, caveatId []byte, loc string, r io.Reader) error { + derivedKey := makeKey(rootKey) + verificationId, err := encrypt(&m.sig, derivedKey, r) + if err != nil { + return err + } + m.addCaveat(caveatId, verificationId, loc) + return nil +} + +var zeroKey [hashLen]byte + +// bindForRequest binds the given macaroon +// to the given signature of its parent macaroon. +func bindForRequest(rootSig []byte, dischargeSig *[hashLen]byte) *[hashLen]byte { + if bytes.Equal(rootSig, dischargeSig[:]) { + return dischargeSig + } + return keyedHash2(&zeroKey, rootSig, dischargeSig[:]) +} + +// Verify verifies that the receiving macaroon is valid. +// The root key must be the same that the macaroon was originally +// minted with. The check function is called to verify each +// first-party caveat - it should return an error if the +// condition is not met. +// +// The discharge macaroons should be provided in discharges. +// +// Verify returns nil if the verification succeeds. +func (m *Macaroon) Verify(rootKey []byte, check func(caveat string) error, discharges []*Macaroon) error { + var vctx verificationContext + vctx.init(rootKey, m, discharges, check) + return vctx.verify(m, rootKey) +} + +// VerifySignature verifies the signature of the given macaroon with respect +// to the root key, but it does not validate any first-party caveats. Instead +// it returns all the applicable first party caveats on success. +// +// The caller is responsible for checking the returned first party caveat +// conditions. +func (m *Macaroon) VerifySignature(rootKey []byte, discharges []*Macaroon) ([]string, error) { + n := len(m.caveats) + for _, dm := range discharges { + n += len(dm.caveats) + } + conds := make([]string, 0, n) + var vctx verificationContext + vctx.init(rootKey, m, discharges, func(cond string) error { + conds = append(conds, cond) + return nil + }) + err := vctx.verify(m, rootKey) + if err != nil { + return nil, err + } + return conds, nil +} + +// TraceVerify verifies the signature of the macaroon without checking +// any of the first party caveats, and returns a slice of Traces holding +// the operations used when verifying the macaroons. +// +// Each element in the returned slice corresponds to the +// operation for one of the argument macaroons, with m at index 0, +// and discharges at 1 onwards. +func (m *Macaroon) TraceVerify(rootKey []byte, discharges []*Macaroon) ([]Trace, error) { + var vctx verificationContext + vctx.init(rootKey, m, discharges, func(string) error { return nil }) + vctx.traces = make([]Trace, len(discharges)+1) + err := vctx.verify(m, rootKey) + return vctx.traces, err +} + +type verificationContext struct { + used []bool + discharges []*Macaroon + rootSig *[hashLen]byte + traces []Trace + check func(caveat string) error +} + +func (vctx *verificationContext) init(rootKey []byte, root *Macaroon, discharges []*Macaroon, check func(caveat string) error) { + *vctx = verificationContext{ + discharges: discharges, + used: make([]bool, len(discharges)), + rootSig: &root.sig, + check: check, + } +} + +func (vctx *verificationContext) verify(root *Macaroon, rootKey []byte) error { + vctx.traceRootKey(0, rootKey) + vctx.trace(0, TraceMakeKey, rootKey, nil) + derivedKey := makeKey(rootKey) + if err := vctx.verify0(root, 0, derivedKey); err != nil { + vctx.trace(0, TraceFail, nil, nil) + return err + } + for i, wasUsed := range vctx.used { + if !wasUsed { + vctx.trace(i+1, TraceFail, nil, nil) + return fmt.Errorf("discharge macaroon %q was not used", vctx.discharges[i].Id()) + } + } + return nil +} + +func (vctx *verificationContext) verify0(m *Macaroon, index int, rootKey *[hashLen]byte) error { + vctx.trace(index, TraceHash, m.id, nil) + caveatSig := keyedHash(rootKey, m.id) + for i, cav := range m.caveats { + if cav.isThirdParty() { + cavKey, err := decrypt(caveatSig, cav.VerificationId) + if err != nil { + return fmt.Errorf("failed to decrypt caveat %d signature: %v", i, err) + } + dm, di, err := vctx.findDischarge(cav.Id) + if err != nil { + return err + } + vctx.traceRootKey(di+1, cavKey[:]) + if err := vctx.verify0(dm, di+1, cavKey); err != nil { + vctx.trace(di+1, TraceFail, nil, nil) + return err + } + vctx.trace(index, TraceHash, cav.VerificationId, cav.Id) + caveatSig = keyedHash2(caveatSig, cav.VerificationId, cav.Id) + } else { + vctx.trace(index, TraceHash, cav.Id, nil) + caveatSig = keyedHash(caveatSig, cav.Id) + if err := vctx.check(string(cav.Id)); err != nil { + return err + } + } + } + if index > 0 { + vctx.trace(index, TraceBind, vctx.rootSig[:], caveatSig[:]) + caveatSig = bindForRequest(vctx.rootSig[:], caveatSig) + } + // TODO perhaps we should actually do this check before doing + // all the potentially expensive caveat checks. + if !hmac.Equal(caveatSig[:], m.sig[:]) { + return fmt.Errorf("signature mismatch after caveat verification") + } + return nil +} + +func (vctx *verificationContext) findDischarge(id []byte) (dm *Macaroon, index int, err error) { + for di, dm := range vctx.discharges { + if !bytes.Equal(dm.id, id) { + continue + } + // Don't use a discharge macaroon more than once. + // It's important that we do this check here rather than after + // verify as it prevents potentially infinite recursion. + if vctx.used[di] { + return nil, 0, fmt.Errorf("discharge macaroon %q was used more than once", dm.Id()) + } + vctx.used[di] = true + return dm, di, nil + } + return nil, 0, fmt.Errorf("cannot find discharge macaroon for caveat %x", id) +} + +func (vctx *verificationContext) trace(index int, op TraceOpKind, data1, data2 []byte) { + if vctx.traces != nil { + vctx.traces[index].Ops = append(vctx.traces[index].Ops, TraceOp{ + Kind: op, + Data1: data1, + Data2: data2, + }) + } +} + +func (vctx *verificationContext) traceRootKey(index int, rootKey []byte) { + if vctx.traces != nil { + vctx.traces[index].RootKey = rootKey[:] + } +} diff --git a/vendor/src/github.com/go-macaroon/macaroon/macaroon_test.go b/vendor/src/github.com/go-macaroon/macaroon/macaroon_test.go new file mode 100644 index 00000000..bffe97af --- /dev/null +++ b/vendor/src/github.com/go-macaroon/macaroon/macaroon_test.go @@ -0,0 +1,914 @@ +package macaroon_test + +import ( + "encoding/base64" + "encoding/hex" + "encoding/json" + "fmt" + "testing" + + jc "github.com/juju/testing/checkers" + gc "gopkg.in/check.v1" + + "gopkg.in/macaroon.v2" +) + +func TestPackage(t *testing.T) { + gc.TestingT(t) +} + +type macaroonSuite struct{} + +var _ = gc.Suite(&macaroonSuite{}) + +func never(string) error { + return fmt.Errorf("condition is never true") +} + +func (*macaroonSuite) TestNoCaveats(c *gc.C) { + rootKey := []byte("secret") + m := MustNew(rootKey, []byte("some id"), "a location", macaroon.LatestVersion) + c.Assert(m.Location(), gc.Equals, "a location") + c.Assert(m.Id(), gc.DeepEquals, []byte("some id")) + + err := m.Verify(rootKey, never, nil) + c.Assert(err, gc.IsNil) +} + +func (*macaroonSuite) TestFirstPartyCaveat(c *gc.C) { + rootKey := []byte("secret") + m := MustNew(rootKey, []byte("some id"), "a location", macaroon.LatestVersion) + + caveats := map[string]bool{ + "a caveat": true, + "another caveat": true, + } + tested := make(map[string]bool) + + for cav := range caveats { + m.AddFirstPartyCaveat([]byte(cav)) + } + expectErr := fmt.Errorf("condition not met") + check := func(cav string) error { + tested[cav] = true + if caveats[cav] { + return nil + } + return expectErr + } + err := m.Verify(rootKey, check, nil) + c.Assert(err, gc.IsNil) + + c.Assert(tested, gc.DeepEquals, caveats) + + m.AddFirstPartyCaveat([]byte("not met")) + err = m.Verify(rootKey, check, nil) + c.Assert(err, gc.Equals, expectErr) + + c.Assert(tested["not met"], gc.Equals, true) +} + +func (*macaroonSuite) TestThirdPartyCaveat(c *gc.C) { + rootKey := []byte("secret") + m := MustNew(rootKey, []byte("some id"), "a location", macaroon.LatestVersion) + + dischargeRootKey := []byte("shared root key") + thirdPartyCaveatId := []byte("3rd party caveat") + err := m.AddThirdPartyCaveat(dischargeRootKey, thirdPartyCaveatId, "remote.com") + c.Assert(err, gc.IsNil) + + dm := MustNew(dischargeRootKey, thirdPartyCaveatId, "remote location", macaroon.LatestVersion) + dm.Bind(m.Signature()) + err = m.Verify(rootKey, never, []*macaroon.Macaroon{dm}) + c.Assert(err, gc.IsNil) +} + +func (*macaroonSuite) TestThirdPartyCaveatBadRandom(c *gc.C) { + rootKey := []byte("secret") + m := MustNew(rootKey, []byte("some id"), "a location", macaroon.LatestVersion) + dischargeRootKey := []byte("shared root key") + thirdPartyCaveatId := []byte("3rd party caveat") + + err := macaroon.AddThirdPartyCaveatWithRand(m, dischargeRootKey, thirdPartyCaveatId, "remote.com", &macaroon.ErrorReader{}) + c.Assert(err, gc.ErrorMatches, "cannot generate random bytes: fail") +} + +func (*macaroonSuite) TestSetLocation(c *gc.C) { + rootKey := []byte("secret") + m := MustNew(rootKey, []byte("some id"), "a location", macaroon.LatestVersion) + c.Assert(m.Location(), gc.Equals, "a location") + m.SetLocation("another location") + c.Assert(m.Location(), gc.Equals, "another location") +} + +type conditionTest struct { + conditions map[string]bool + expectErr string +} + +var verifyTests = []struct { + about string + macaroons []macaroonSpec + conditions []conditionTest +}{{ + about: "single third party caveat without discharge", + macaroons: []macaroonSpec{{ + rootKey: "root-key", + id: "root-id", + caveats: []caveat{{ + condition: "wonderful", + }, { + condition: "bob-is-great", + location: "bob", + rootKey: "bob-caveat-root-key", + }}, + }}, + conditions: []conditionTest{{ + conditions: map[string]bool{ + "wonderful": true, + }, + expectErr: fmt.Sprintf(`cannot find discharge macaroon for caveat %x`, "bob-is-great"), + }}, +}, { + about: "single third party caveat with discharge", + macaroons: []macaroonSpec{{ + rootKey: "root-key", + id: "root-id", + caveats: []caveat{{ + condition: "wonderful", + }, { + condition: "bob-is-great", + location: "bob", + rootKey: "bob-caveat-root-key", + }}, + }, { + location: "bob", + rootKey: "bob-caveat-root-key", + id: "bob-is-great", + }}, + conditions: []conditionTest{{ + conditions: map[string]bool{ + "wonderful": true, + }, + }, { + conditions: map[string]bool{ + "wonderful": false, + }, + expectErr: `condition "wonderful" not met`, + }}, +}, { + about: "single third party caveat with discharge with mismatching root key", + macaroons: []macaroonSpec{{ + rootKey: "root-key", + id: "root-id", + caveats: []caveat{{ + condition: "wonderful", + }, { + condition: "bob-is-great", + location: "bob", + rootKey: "bob-caveat-root-key", + }}, + }, { + location: "bob", + rootKey: "bob-caveat-root-key-wrong", + id: "bob-is-great", + }}, + conditions: []conditionTest{{ + conditions: map[string]bool{ + "wonderful": true, + }, + expectErr: `signature mismatch after caveat verification`, + }}, +}, { + about: "single third party caveat with two discharges", + macaroons: []macaroonSpec{{ + rootKey: "root-key", + id: "root-id", + caveats: []caveat{{ + condition: "wonderful", + }, { + condition: "bob-is-great", + location: "bob", + rootKey: "bob-caveat-root-key", + }}, + }, { + location: "bob", + rootKey: "bob-caveat-root-key", + id: "bob-is-great", + caveats: []caveat{{ + condition: "splendid", + }}, + }, { + location: "bob", + rootKey: "bob-caveat-root-key", + id: "bob-is-great", + caveats: []caveat{{ + condition: "top of the world", + }}, + }}, + conditions: []conditionTest{{ + conditions: map[string]bool{ + "wonderful": true, + }, + expectErr: `condition "splendid" not met`, + }, { + conditions: map[string]bool{ + "wonderful": true, + "splendid": true, + "top of the world": true, + }, + expectErr: `discharge macaroon "bob-is-great" was not used`, + }, { + conditions: map[string]bool{ + "wonderful": true, + "splendid": false, + "top of the world": true, + }, + expectErr: `condition "splendid" not met`, + }, { + conditions: map[string]bool{ + "wonderful": true, + "splendid": true, + "top of the world": false, + }, + expectErr: `discharge macaroon "bob-is-great" was not used`, + }}, +}, { + about: "one discharge used for two macaroons", + macaroons: []macaroonSpec{{ + rootKey: "root-key", + id: "root-id", + caveats: []caveat{{ + condition: "somewhere else", + location: "bob", + rootKey: "bob-caveat-root-key", + }, { + condition: "bob-is-great", + location: "charlie", + rootKey: "bob-caveat-root-key", + }}, + }, { + location: "bob", + rootKey: "bob-caveat-root-key", + id: "somewhere else", + caveats: []caveat{{ + condition: "bob-is-great", + location: "charlie", + rootKey: "bob-caveat-root-key", + }}, + }, { + location: "bob", + rootKey: "bob-caveat-root-key", + id: "bob-is-great", + }}, + conditions: []conditionTest{{ + expectErr: `discharge macaroon "bob-is-great" was used more than once`, + }}, +}, { + about: "recursive third party caveat", + macaroons: []macaroonSpec{{ + rootKey: "root-key", + id: "root-id", + caveats: []caveat{{ + condition: "bob-is-great", + location: "bob", + rootKey: "bob-caveat-root-key", + }}, + }, { + location: "bob", + rootKey: "bob-caveat-root-key", + id: "bob-is-great", + caveats: []caveat{{ + condition: "bob-is-great", + location: "charlie", + rootKey: "bob-caveat-root-key", + }}, + }}, + conditions: []conditionTest{{ + expectErr: `discharge macaroon "bob-is-great" was used more than once`, + }}, +}, { + about: "two third party caveats", + macaroons: []macaroonSpec{{ + rootKey: "root-key", + id: "root-id", + caveats: []caveat{{ + condition: "wonderful", + }, { + condition: "bob-is-great", + location: "bob", + rootKey: "bob-caveat-root-key", + }, { + condition: "charlie-is-great", + location: "charlie", + rootKey: "charlie-caveat-root-key", + }}, + }, { + location: "bob", + rootKey: "bob-caveat-root-key", + id: "bob-is-great", + caveats: []caveat{{ + condition: "splendid", + }}, + }, { + location: "charlie", + rootKey: "charlie-caveat-root-key", + id: "charlie-is-great", + caveats: []caveat{{ + condition: "top of the world", + }}, + }}, + conditions: []conditionTest{{ + conditions: map[string]bool{ + "wonderful": true, + "splendid": true, + "top of the world": true, + }, + }, { + conditions: map[string]bool{ + "wonderful": true, + "splendid": false, + "top of the world": true, + }, + expectErr: `condition "splendid" not met`, + }, { + conditions: map[string]bool{ + "wonderful": true, + "splendid": true, + "top of the world": false, + }, + expectErr: `condition "top of the world" not met`, + }}, +}, { + about: "third party caveat with undischarged third party caveat", + macaroons: []macaroonSpec{{ + rootKey: "root-key", + id: "root-id", + caveats: []caveat{{ + condition: "wonderful", + }, { + condition: "bob-is-great", + location: "bob", + rootKey: "bob-caveat-root-key", + }}, + }, { + location: "bob", + rootKey: "bob-caveat-root-key", + id: "bob-is-great", + caveats: []caveat{{ + condition: "splendid", + }, { + condition: "barbara-is-great", + location: "barbara", + rootKey: "barbara-caveat-root-key", + }}, + }}, + conditions: []conditionTest{{ + conditions: map[string]bool{ + "wonderful": true, + "splendid": true, + }, + expectErr: fmt.Sprintf(`cannot find discharge macaroon for caveat %x`, "barbara-is-great"), + }}, +}, { + about: "multilevel third party caveats", + macaroons: multilevelThirdPartyCaveatMacaroons, + conditions: []conditionTest{{ + conditions: map[string]bool{ + "wonderful": true, + "splendid": true, + "high-fiving": true, + "spiffing": true, + }, + }, { + conditions: map[string]bool{ + "wonderful": true, + "splendid": true, + "high-fiving": false, + "spiffing": true, + }, + expectErr: `condition "high-fiving" not met`, + }}, +}, { + about: "unused discharge", + macaroons: []macaroonSpec{{ + rootKey: "root-key", + id: "root-id", + }, { + rootKey: "other-key", + id: "unused", + }}, + conditions: []conditionTest{{ + expectErr: `discharge macaroon "unused" was not used`, + }}, +}} + +var multilevelThirdPartyCaveatMacaroons = []macaroonSpec{{ + rootKey: "root-key", + id: "root-id", + caveats: []caveat{{ + condition: "wonderful", + }, { + condition: "bob-is-great", + location: "bob", + rootKey: "bob-caveat-root-key", + }, { + condition: "charlie-is-great", + location: "charlie", + rootKey: "charlie-caveat-root-key", + }}, +}, { + location: "bob", + rootKey: "bob-caveat-root-key", + id: "bob-is-great", + caveats: []caveat{{ + condition: "splendid", + }, { + condition: "barbara-is-great", + location: "barbara", + rootKey: "barbara-caveat-root-key", + }}, +}, { + location: "charlie", + rootKey: "charlie-caveat-root-key", + id: "charlie-is-great", + caveats: []caveat{{ + condition: "splendid", + }, { + condition: "celine-is-great", + location: "celine", + rootKey: "celine-caveat-root-key", + }}, +}, { + location: "barbara", + rootKey: "barbara-caveat-root-key", + id: "barbara-is-great", + caveats: []caveat{{ + condition: "spiffing", + }, { + condition: "ben-is-great", + location: "ben", + rootKey: "ben-caveat-root-key", + }}, +}, { + location: "ben", + rootKey: "ben-caveat-root-key", + id: "ben-is-great", +}, { + location: "celine", + rootKey: "celine-caveat-root-key", + id: "celine-is-great", + caveats: []caveat{{ + condition: "high-fiving", + }}, +}} + +func (*macaroonSuite) TestVerify(c *gc.C) { + for i, test := range verifyTests { + c.Logf("test %d: %s", i, test.about) + rootKey, macaroons := makeMacaroons(test.macaroons) + for _, cond := range test.conditions { + c.Logf("conditions %#v", cond.conditions) + check := func(cav string) error { + if cond.conditions[cav] { + return nil + } + return fmt.Errorf("condition %q not met", cav) + } + err := macaroons[0].Verify( + rootKey, + check, + macaroons[1:], + ) + if cond.expectErr != "" { + c.Assert(err, gc.ErrorMatches, cond.expectErr) + } else { + c.Assert(err, gc.IsNil) + } + + // Cloned macaroon should have same verify result. + cloneErr := macaroons[0].Clone().Verify(rootKey, check, macaroons[1:]) + c.Assert(cloneErr, gc.DeepEquals, err) + } + } +} + +func (*macaroonSuite) TestTraceVerify(c *gc.C) { + rootKey, macaroons := makeMacaroons(multilevelThirdPartyCaveatMacaroons) + traces, err := macaroons[0].TraceVerify(rootKey, macaroons[1:]) + c.Assert(err, gc.Equals, nil) + c.Assert(traces, gc.HasLen, len(macaroons)) + // Check that we can run through the resulting operations and + // arrive at the same signature. + for i, m := range macaroons { + r := traces[i].Results() + c.Assert(b64str(r[len(r)-1]), gc.Equals, b64str(m.Signature()), gc.Commentf("macaroon %d", i)) + } +} + +func (*macaroonSuite) TestTraceVerifyFailure(c *gc.C) { + rootKey, macaroons := makeMacaroons([]macaroonSpec{{ + rootKey: "xxx", + id: "hello", + caveats: []caveat{{ + condition: "cond1", + }, { + condition: "cond2", + }, { + condition: "cond3", + }}, + }}) + // Marshal the macaroon, corrupt a condition, then unmarshal + // it and check we see the expected trace failure. + data, err := json.Marshal(macaroons[0]) + c.Assert(err, gc.Equals, nil) + var jm macaroon.MacaroonJSONV2 + err = json.Unmarshal(data, &jm) + c.Assert(err, gc.Equals, nil) + jm.Caveats[1].CID = "cond2 corrupted" + data, err = json.Marshal(jm) + c.Assert(err, gc.Equals, nil) + + var corruptm *macaroon.Macaroon + err = json.Unmarshal(data, &corruptm) + c.Assert(err, gc.Equals, nil) + + traces, err := corruptm.TraceVerify(rootKey, nil) + c.Assert(err, gc.ErrorMatches, `signature mismatch after caveat verification`) + c.Assert(traces, gc.HasLen, 1) + var kinds []macaroon.TraceOpKind + for _, op := range traces[0].Ops { + kinds = append(kinds, op.Kind) + } + c.Assert(kinds, gc.DeepEquals, []macaroon.TraceOpKind{ + macaroon.TraceMakeKey, + macaroon.TraceHash, // id + macaroon.TraceHash, // cond1 + macaroon.TraceHash, // cond2 + macaroon.TraceHash, // cond3 + macaroon.TraceFail, // sig mismatch + }) +} + +func b64str(b []byte) string { + return base64.StdEncoding.EncodeToString(b) +} + +func (*macaroonSuite) TestVerifySignature(c *gc.C) { + rootKey, macaroons := makeMacaroons([]macaroonSpec{{ + rootKey: "xxx", + id: "hello", + caveats: []caveat{{ + rootKey: "y", + condition: "something", + location: "somewhere", + }, { + condition: "cond1", + }, { + condition: "cond2", + }}, + }, { + rootKey: "y", + id: "something", + caveats: []caveat{{ + condition: "cond3", + }, { + condition: "cond4", + }}, + }}) + conds, err := macaroons[0].VerifySignature(rootKey, macaroons[1:]) + c.Assert(err, gc.IsNil) + c.Assert(conds, jc.DeepEquals, []string{"cond3", "cond4", "cond1", "cond2"}) + + conds, err = macaroons[0].VerifySignature(nil, macaroons[1:]) + c.Assert(err, gc.ErrorMatches, `failed to decrypt caveat 0 signature: decryption failure`) + c.Assert(conds, gc.IsNil) +} + +// TODO(rog) move the following JSON-marshal tests into marshal_test.go. + +// jsonTestVersions holds the various possible ways of marshaling a macaroon +// to JSON. +var jsonTestVersions = []macaroon.Version{ + macaroon.V1, + macaroon.V2, +} + +func (s *macaroonSuite) TestMarshalJSON(c *gc.C) { + for i, vers := range jsonTestVersions { + c.Logf("test %d: %v", i, vers) + s.testMarshalJSONWithVersion(c, vers) + } +} + +func (*macaroonSuite) testMarshalJSONWithVersion(c *gc.C, vers macaroon.Version) { + rootKey := []byte("secret") + m0 := MustNew(rootKey, []byte("some id"), "a location", vers) + m0.AddFirstPartyCaveat([]byte("account = 3735928559")) + m0JSON, err := json.Marshal(m0) + c.Assert(err, gc.IsNil) + var m1 macaroon.Macaroon + err = json.Unmarshal(m0JSON, &m1) + c.Assert(err, gc.IsNil) + c.Assert(m0.Location(), gc.Equals, m1.Location()) + c.Assert(string(m0.Id()), gc.Equals, string(m1.Id())) + c.Assert( + hex.EncodeToString(m0.Signature()), + gc.Equals, + hex.EncodeToString(m1.Signature())) + c.Assert(m1.Version(), gc.Equals, vers) +} + +var jsonRoundTripTests = []struct { + about string + // data holds the marshaled data. All the data values hold + // different encodings of the same macaroon - the same as produced + // from the second example in libmacaroons + // example README with the following libmacaroons code: + // + // secret = 'this is a different super-secret key; never use the same secret twice' + // public = 'we used our other secret key' + // location = 'http://mybank/' + // M = macaroons.create(location, secret, public) + // M = M.add_first_party_caveat('account = 3735928559') + // caveat_key = '4; guaranteed random by a fair toss of the dice' + // predicate = 'user = Alice' + // identifier = 'this was how we remind auth of key/pred' + // M = M.add_third_party_caveat('http://auth.mybank/', caveat_key, identifier) + // m.serialize_json() + data string + expectExactRoundTrip bool + expectVers macaroon.Version +}{{ + about: "exact JSON as produced by libmacaroons", + data: `{"caveats":[{"cid":"account = 3735928559"},{"cid":"this was how we remind auth of key\/pred","vid":"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA027FAuBYhtHwJ58FX6UlVNFtFsGxQHS7uD_w_dedwv4Jjw7UorCREw5rXbRqIKhr","cl":"http:\/\/auth.mybank\/"}],"location":"http:\/\/mybank\/","identifier":"we used our other secret key","signature":"d27db2fd1f22760e4c3dae8137e2d8fc1df6c0741c18aed4b97256bf78d1f55c"}`, + expectVers: macaroon.V1, + expectExactRoundTrip: true, +}, { + about: "V2 object with std base-64 binary values", + data: `{"c":[{"i64":"YWNjb3VudCA9IDM3MzU5Mjg1NTk="},{"i64":"dGhpcyB3YXMgaG93IHdlIHJlbWluZCBhdXRoIG9mIGtleS9wcmVk","v64":"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA027FAuBYhtHwJ58FX6UlVNFtFsGxQHS7uD/w/dedwv4Jjw7UorCREw5rXbRqIKhr","l":"http://auth.mybank/"}],"l":"http://mybank/","i64":"d2UgdXNlZCBvdXIgb3RoZXIgc2VjcmV0IGtleQ==","s64":"0n2y/R8idg5MPa6BN+LY/B32wHQcGK7UuXJWv3jR9Vw="}`, + expectVers: macaroon.V2, +}, { + about: "V2 object with URL base-64 binary values", + data: `{"c":[{"i64":"YWNjb3VudCA9IDM3MzU5Mjg1NTk"},{"i64":"dGhpcyB3YXMgaG93IHdlIHJlbWluZCBhdXRoIG9mIGtleS9wcmVk","v64":"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA027FAuBYhtHwJ58FX6UlVNFtFsGxQHS7uD_w_dedwv4Jjw7UorCREw5rXbRqIKhr","l":"http://auth.mybank/"}],"l":"http://mybank/","i64":"d2UgdXNlZCBvdXIgb3RoZXIgc2VjcmV0IGtleQ","s64":"0n2y_R8idg5MPa6BN-LY_B32wHQcGK7UuXJWv3jR9Vw"}`, + expectVers: macaroon.V2, +}, { + about: "V2 object with URL base-64 binary values and strings for ASCII", + data: `{"c":[{"i":"account = 3735928559"},{"i":"this was how we remind auth of key/pred","v64":"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA027FAuBYhtHwJ58FX6UlVNFtFsGxQHS7uD_w_dedwv4Jjw7UorCREw5rXbRqIKhr","l":"http://auth.mybank/"}],"l":"http://mybank/","i":"we used our other secret key","s64":"0n2y_R8idg5MPa6BN-LY_B32wHQcGK7UuXJWv3jR9Vw"}`, + expectVers: macaroon.V2, + expectExactRoundTrip: true, +}, { + about: "V2 base64 encoded binary", + data: `"` + + base64.StdEncoding.EncodeToString([]byte( + "\x02"+ + "\x01\x0ehttp://mybank/"+ + "\x02\x1cwe used our other secret key"+ + "\x00"+ + "\x02\x14account = 3735928559"+ + "\x00"+ + "\x01\x13http://auth.mybank/"+ + "\x02'this was how we remind auth of key/pred"+ + "\x04\x48\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xd3\x6e\xc5\x02\xe0\x58\x86\xd1\xf0\x27\x9f\x05\x5f\xa5\x25\x54\xd1\x6d\x16\xc1\xb1\x40\x74\xbb\xb8\x3f\xf0\xfd\xd7\x9d\xc2\xfe\x09\x8f\x0e\xd4\xa2\xb0\x91\x13\x0e\x6b\x5d\xb4\x6a\x20\xa8\x6b"+ + "\x00"+ + "\x00"+ + "\x06\x20\xd2\x7d\xb2\xfd\x1f\x22\x76\x0e\x4c\x3d\xae\x81\x37\xe2\xd8\xfc\x1d\xf6\xc0\x74\x1c\x18\xae\xd4\xb9\x72\x56\xbf\x78\xd1\xf5\x5c", + )) + `"`, + expectVers: macaroon.V2, +}} + +func (s *macaroonSuite) TestJSONRoundTrip(c *gc.C) { + for i, test := range jsonRoundTripTests { + c.Logf("test %d (%v) %s", i, test.expectVers, test.about) + s.testJSONRoundTripWithVersion(c, test.data, test.expectVers, test.expectExactRoundTrip) + } +} + +func (*macaroonSuite) testJSONRoundTripWithVersion(c *gc.C, jsonData string, vers macaroon.Version, expectExactRoundTrip bool) { + var m macaroon.Macaroon + err := json.Unmarshal([]byte(jsonData), &m) + c.Assert(err, gc.IsNil) + assertLibMacaroonsMacaroon(c, &m) + c.Assert(m.Version(), gc.Equals, vers) + + data, err := m.MarshalJSON() + c.Assert(err, gc.IsNil) + + if expectExactRoundTrip { + // The data is in canonical form, so we can check that + // the round-tripped data is the same as the original + // data when unmarshalled into an interface{}. + var got interface{} + err = json.Unmarshal(data, &got) + c.Assert(err, gc.IsNil) + + var original interface{} + err = json.Unmarshal([]byte(jsonData), &original) + c.Assert(err, gc.IsNil) + + c.Assert(got, jc.DeepEquals, original, gc.Commentf("data: %s", data)) + } + // Check that we can unmarshal the marshaled data anyway + // and the macaroon still looks the same. + var m1 macaroon.Macaroon + err = m1.UnmarshalJSON(data) + c.Assert(err, gc.IsNil) + assertLibMacaroonsMacaroon(c, &m1) + c.Assert(m.Version(), gc.Equals, vers) +} + +// assertLibMacaroonsMacaroon asserts that m looks like the macaroon +// created in the README of the libmacaroons documentation. +// In particular, the signature is the same one reported there. +func assertLibMacaroonsMacaroon(c *gc.C, m *macaroon.Macaroon) { + c.Assert(fmt.Sprintf("%x", m.Signature()), gc.Equals, + "d27db2fd1f22760e4c3dae8137e2d8fc1df6c0741c18aed4b97256bf78d1f55c") + c.Assert(m.Location(), gc.Equals, "http://mybank/") + c.Assert(string(m.Id()), gc.Equals, "we used our other secret key") + c.Assert(m.Caveats(), jc.DeepEquals, []macaroon.Caveat{{ + Id: []byte("account = 3735928559"), + }, { + Id: []byte("this was how we remind auth of key/pred"), + VerificationId: decodeB64("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA027FAuBYhtHwJ58FX6UlVNFtFsGxQHS7uD_w_dedwv4Jjw7UorCREw5rXbRqIKhr"), + Location: "http://auth.mybank/", + }}) +} + +var jsonDecodeErrorTests = []struct { + about string + data string + expectError string +}{{ + about: "ambiguous id #1", + data: `{"i": "hello", "i64": "abcd", "s64": "ZDI3ZGIyZmQxZjIyNzYwZTRjM2RhZTgxMzdlMmQ4ZmMK"}`, + expectError: "invalid identifier: ambiguous field encoding", +}, { + about: "ambiguous signature", + data: `{"i": "hello", "s": "345", "s64": "543467"}`, + expectError: "invalid signature: ambiguous field encoding", +}, { + about: "signature too short", + data: `{"i": "hello", "s64": "0n2y/R8idg5MPa6BN+LY/B32wHQcGK7UuXJWv3jR9Q"}`, + expectError: "signature has unexpected length 31", +}, { + about: "signature too long", + data: `{"i": "hello", "s64": "0n2y/R8idg5MPa6BN+LY/B32wHQcGK7UuXJWv3jR9dP1"}`, + expectError: "signature has unexpected length 33", +}, { + about: "invalid caveat id", + data: `{"i": "hello", "s64": "0n2y/R8idg5MPa6BN+LY/B32wHQcGK7UuXJWv3jR9Vw", "c": [{"i": "hello", "i64": "00"}]}`, + expectError: "invalid cid in caveat: ambiguous field encoding", +}, { + about: "invalid caveat vid", + data: `{"i": "hello", "s64": "0n2y/R8idg5MPa6BN+LY/B32wHQcGK7UuXJWv3jR9Vw", "c": [{"i": "hello", "v": "hello", "v64": "00"}]}`, + expectError: "invalid vid in caveat: ambiguous field encoding", +}} + +func (*macaroonSuite) TestJSONDecodeError(c *gc.C) { + for i, test := range jsonDecodeErrorTests { + c.Logf("test %d: %v", i, test.about) + var m macaroon.Macaroon + err := json.Unmarshal([]byte(test.data), &m) + c.Assert(err, gc.ErrorMatches, test.expectError) + } +} + +func (*macaroonSuite) TestFirstPartyCaveatWithInvalidUTF8(c *gc.C) { + rootKey := []byte("secret") + badString := "foo\xff" + + m0 := MustNew(rootKey, []byte("some id"), "a location", macaroon.LatestVersion) + err := m0.AddFirstPartyCaveat([]byte(badString)) + c.Assert(err, gc.Equals, nil) +} + +func decodeB64(s string) []byte { + data, err := base64.RawURLEncoding.DecodeString(s) + if err != nil { + panic(err) + } + return data +} + +type caveat struct { + rootKey string + location string + condition string +} + +type macaroonSpec struct { + rootKey string + id string + caveats []caveat + location string +} + +func makeMacaroons(mspecs []macaroonSpec) (rootKey []byte, macaroons macaroon.Slice) { + for _, mspec := range mspecs { + macaroons = append(macaroons, makeMacaroon(mspec)) + } + primary := macaroons[0] + for _, m := range macaroons[1:] { + m.Bind(primary.Signature()) + } + return []byte(mspecs[0].rootKey), macaroons +} + +func makeMacaroon(mspec macaroonSpec) *macaroon.Macaroon { + m := MustNew([]byte(mspec.rootKey), []byte(mspec.id), mspec.location, macaroon.LatestVersion) + for _, cav := range mspec.caveats { + if cav.location != "" { + err := m.AddThirdPartyCaveat([]byte(cav.rootKey), []byte(cav.condition), cav.location) + if err != nil { + panic(err) + } + } else { + m.AddFirstPartyCaveat([]byte(cav.condition)) + } + } + return m +} + +func assertEqualMacaroons(c *gc.C, m0, m1 *macaroon.Macaroon) { + m0json, err := m0.MarshalJSON() + c.Assert(err, gc.IsNil) + m1json, err := m1.MarshalJSON() + var m0val, m1val interface{} + err = json.Unmarshal(m0json, &m0val) + c.Assert(err, gc.IsNil) + err = json.Unmarshal(m1json, &m1val) + c.Assert(err, gc.IsNil) + c.Assert(m0val, gc.DeepEquals, m1val) +} + +func (*macaroonSuite) TestBinaryRoundTrip(c *gc.C) { + // Test the binary marshalling and unmarshalling of a macaroon with + // first and third party caveats. + rootKey := []byte("secret") + m0 := MustNew(rootKey, []byte("some id"), "a location", macaroon.LatestVersion) + err := m0.AddFirstPartyCaveat([]byte("first caveat")) + c.Assert(err, gc.IsNil) + err = m0.AddFirstPartyCaveat([]byte("second caveat")) + c.Assert(err, gc.IsNil) + err = m0.AddThirdPartyCaveat([]byte("shared root key"), []byte("3rd party caveat"), "remote.com") + c.Assert(err, gc.IsNil) + data, err := m0.MarshalBinary() + c.Assert(err, gc.IsNil) + var m1 macaroon.Macaroon + err = m1.UnmarshalBinary(data) + c.Assert(err, gc.IsNil) + assertEqualMacaroons(c, m0, &m1) +} + +func (*macaroonSuite) TestBinaryMarshalingAgainstLibmacaroon(c *gc.C) { + // Test that a libmacaroon marshalled macaroon can be correctly unmarshaled + data, err := base64.RawURLEncoding.DecodeString( + "MDAxY2xvY2F0aW9uIGh0dHA6Ly9teWJhbmsvCjAwMmNpZGVudGlmaWVyIHdlIHVzZWQgb3VyIG90aGVyIHNlY3JldCBrZXkKMDAxZGNpZCBhY2NvdW50ID0gMzczNTkyODU1OQowMDMwY2lkIHRoaXMgd2FzIGhvdyB3ZSByZW1pbmQgYXV0aCBvZiBrZXkvcHJlZAowMDUxdmlkIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAANNuxQLgWIbR8CefBV-lJVTRbRbBsUB0u7g_8P3XncL-CY8O1KKwkRMOa120aiCoawowMDFiY2wgaHR0cDovL2F1dGgubXliYW5rLwowMDJmc2lnbmF0dXJlINJ9sv0fInYOTD2ugTfi2Pwd9sB0HBiu1LlyVr940fVcCg") + c.Assert(err, gc.IsNil) + var m0 macaroon.Macaroon + err = m0.UnmarshalBinary(data) + c.Assert(err, gc.IsNil) + jsonData := []byte(`{"caveats":[{"cid":"account = 3735928559"},{"cid":"this was how we remind auth of key\/pred","vid":"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA027FAuBYhtHwJ58FX6UlVNFtFsGxQHS7uD_w_dedwv4Jjw7UorCREw5rXbRqIKhr","cl":"http:\/\/auth.mybank\/"}],"location":"http:\/\/mybank\/","identifier":"we used our other secret key","signature":"d27db2fd1f22760e4c3dae8137e2d8fc1df6c0741c18aed4b97256bf78d1f55c"}`) + var m1 macaroon.Macaroon + err = m1.UnmarshalJSON(jsonData) + c.Assert(err, gc.IsNil) + assertEqualMacaroons(c, &m0, &m1) +} + +var binaryFieldBase64ChoiceTests = []struct { + id string + expectBase64 bool +}{ + {"x", false}, + {"\x00", true}, + {"\x03\x00", true}, + {"a longer id with more stuff", false}, + {"a longer id with more stuff and one invalid \xff", true}, + {"a longer id with more stuff and one encoded \x00", false}, +} + +func (*macaroonSuite) TestBinaryFieldBase64Choice(c *gc.C) { + for i, test := range binaryFieldBase64ChoiceTests { + c.Logf("test %d: %q", i, test.id) + m := MustNew([]byte{0}, []byte(test.id), "", macaroon.LatestVersion) + data, err := json.Marshal(m) + c.Assert(err, gc.Equals, nil) + var x struct { + Id *string `json:"i"` + Id64 *string `json:"i64"` + } + err = json.Unmarshal(data, &x) + c.Assert(err, gc.Equals, nil) + if test.expectBase64 { + c.Assert(x.Id64, gc.NotNil) + c.Assert(x.Id, gc.IsNil) + idDec, err := base64.RawURLEncoding.DecodeString(*x.Id64) + c.Assert(err, gc.Equals, nil) + c.Assert(string(idDec), gc.Equals, test.id) + } else { + c.Assert(x.Id64, gc.IsNil) + c.Assert(x.Id, gc.NotNil) + c.Assert(*x.Id, gc.Equals, test.id) + } + } +} diff --git a/vendor/src/github.com/go-macaroon/macaroon/marshal-v1.go b/vendor/src/github.com/go-macaroon/macaroon/marshal-v1.go new file mode 100644 index 00000000..da20db37 --- /dev/null +++ b/vendor/src/github.com/go-macaroon/macaroon/marshal-v1.go @@ -0,0 +1,190 @@ +package macaroon + +import ( + "encoding/base64" + "encoding/hex" + "encoding/json" + "fmt" + "unicode/utf8" +) + +// macaroonJSONV1 defines the V1 JSON format for macaroons. +type macaroonJSONV1 struct { + Caveats []caveatJSONV1 `json:"caveats"` + Location string `json:"location"` + Identifier string `json:"identifier"` + Signature string `json:"signature"` // hex-encoded +} + +// caveatJSONV1 defines the V1 JSON format for caveats within a macaroon. +type caveatJSONV1 struct { + CID string `json:"cid"` + VID string `json:"vid,omitempty"` + Location string `json:"cl,omitempty"` +} + +// marshalJSONV1 marshals the macaroon to the V1 JSON format. +func (m *Macaroon) marshalJSONV1() ([]byte, error) { + if !utf8.Valid(m.id) { + return nil, fmt.Errorf("macaroon id is not valid UTF-8") + } + mjson := macaroonJSONV1{ + Location: m.location, + Identifier: string(m.id), + Signature: hex.EncodeToString(m.sig[:]), + Caveats: make([]caveatJSONV1, len(m.caveats)), + } + for i, cav := range m.caveats { + if !utf8.Valid(cav.Id) { + return nil, fmt.Errorf("caveat id is not valid UTF-8") + } + mjson.Caveats[i] = caveatJSONV1{ + Location: cav.Location, + CID: string(cav.Id), + VID: base64.RawURLEncoding.EncodeToString(cav.VerificationId), + } + } + data, err := json.Marshal(mjson) + if err != nil { + return nil, fmt.Errorf("cannot marshal json data: %v", err) + } + return data, nil +} + +// initJSONV1 initializes m from the JSON-unmarshaled data +// held in mjson. +func (m *Macaroon) initJSONV1(mjson *macaroonJSONV1) error { + m.init([]byte(mjson.Identifier), mjson.Location, V1) + sig, err := hex.DecodeString(mjson.Signature) + if err != nil { + return fmt.Errorf("cannot decode macaroon signature %q: %v", m.sig, err) + } + if len(sig) != hashLen { + return fmt.Errorf("signature has unexpected length %d", len(sig)) + } + copy(m.sig[:], sig) + m.caveats = m.caveats[:0] + for _, cav := range mjson.Caveats { + vid, err := Base64Decode([]byte(cav.VID)) + if err != nil { + return fmt.Errorf("cannot decode verification id %q: %v", cav.VID, err) + } + m.appendCaveat([]byte(cav.CID), vid, cav.Location) + } + return nil +} + +// The original (v1) binary format of a macaroon is as follows. +// Each identifier represents a v1 packet. +// +// location +// identifier +// ( +// caveatId? +// verificationId? +// caveatLocation? +// )* +// signature + +// parseBinaryV1 parses the given data in V1 format into the macaroon. The macaroon's +// internal data structures will retain references to the data. It +// returns the data after the end of the macaroon. +func (m *Macaroon) parseBinaryV1(data []byte) ([]byte, error) { + var err error + + loc, err := expectPacketV1(data, fieldNameLocation) + if err != nil { + return nil, err + } + data = data[loc.totalLen:] + id, err := expectPacketV1(data, fieldNameIdentifier) + if err != nil { + return nil, err + } + data = data[id.totalLen:] + m.init(id.data, string(loc.data), V1) + var cav Caveat + for { + p, err := parsePacketV1(data) + if err != nil { + return nil, err + } + data = data[p.totalLen:] + switch field := string(p.fieldName); field { + case fieldNameSignature: + // At the end of the caveats we find the signature. + if cav.Id != nil { + m.caveats = append(m.caveats, cav) + } + if len(p.data) != hashLen { + return nil, fmt.Errorf("signature has unexpected length %d", len(p.data)) + } + copy(m.sig[:], p.data) + return data, nil + case fieldNameCaveatId: + if cav.Id != nil { + m.caveats = append(m.caveats, cav) + cav = Caveat{} + } + cav.Id = p.data + case fieldNameVerificationId: + if cav.VerificationId != nil { + return nil, fmt.Errorf("repeated field %q in caveat", fieldNameVerificationId) + } + cav.VerificationId = p.data + case fieldNameCaveatLocation: + if cav.Location != "" { + return nil, fmt.Errorf("repeated field %q in caveat", fieldNameLocation) + } + cav.Location = string(p.data) + default: + return nil, fmt.Errorf("unexpected field %q", field) + } + } +} + +func expectPacketV1(data []byte, kind string) (packetV1, error) { + p, err := parsePacketV1(data) + if err != nil { + return packetV1{}, err + } + if field := string(p.fieldName); field != kind { + return packetV1{}, fmt.Errorf("unexpected field %q; expected %s", field, kind) + } + return p, nil +} + +// appendBinaryV1 appends the binary encoding of m to data. +func (m *Macaroon) appendBinaryV1(data []byte) ([]byte, error) { + var ok bool + data, ok = appendPacketV1(data, fieldNameLocation, []byte(m.location)) + if !ok { + return nil, fmt.Errorf("failed to append location to macaroon, packet is too long") + } + data, ok = appendPacketV1(data, fieldNameIdentifier, m.id) + if !ok { + return nil, fmt.Errorf("failed to append identifier to macaroon, packet is too long") + } + for _, cav := range m.caveats { + data, ok = appendPacketV1(data, fieldNameCaveatId, cav.Id) + if !ok { + return nil, fmt.Errorf("failed to append caveat id to macaroon, packet is too long") + } + if cav.VerificationId == nil { + continue + } + data, ok = appendPacketV1(data, fieldNameVerificationId, cav.VerificationId) + if !ok { + return nil, fmt.Errorf("failed to append verification id to macaroon, packet is too long") + } + data, ok = appendPacketV1(data, fieldNameCaveatLocation, []byte(cav.Location)) + if !ok { + return nil, fmt.Errorf("failed to append verification id to macaroon, packet is too long") + } + } + data, ok = appendPacketV1(data, fieldNameSignature, m.sig[:]) + if !ok { + return nil, fmt.Errorf("failed to append signature to macaroon, packet is too long") + } + return data, nil +} diff --git a/vendor/src/github.com/go-macaroon/macaroon/marshal-v2.go b/vendor/src/github.com/go-macaroon/macaroon/marshal-v2.go new file mode 100644 index 00000000..e02131f2 --- /dev/null +++ b/vendor/src/github.com/go-macaroon/macaroon/marshal-v2.go @@ -0,0 +1,253 @@ +package macaroon + +import ( + "encoding/base64" + "encoding/json" + "fmt" + "unicode/utf8" +) + +// macaroonJSONV2 defines the V2 JSON format for macaroons. +type macaroonJSONV2 struct { + Caveats []caveatJSONV2 `json:"c,omitempty"` + Location string `json:"l,omitempty"` + Identifier string `json:"i,omitempty"` + Identifier64 string `json:"i64,omitempty"` + Signature string `json:"s,omitempty"` + Signature64 string `json:"s64,omitempty"` +} + +// caveatJSONV2 defines the V2 JSON format for caveats within a macaroon. +type caveatJSONV2 struct { + CID string `json:"i,omitempty"` + CID64 string `json:"i64,omitempty"` + VID string `json:"v,omitempty"` + VID64 string `json:"v64,omitempty"` + Location string `json:"l,omitempty"` +} + +func (m *Macaroon) marshalJSONV2() ([]byte, error) { + mjson := macaroonJSONV2{ + Location: m.location, + Caveats: make([]caveatJSONV2, len(m.caveats)), + } + putJSONBinaryField(m.id, &mjson.Identifier, &mjson.Identifier64) + putJSONBinaryField(m.sig[:], &mjson.Signature, &mjson.Signature64) + for i, cav := range m.caveats { + cavjson := caveatJSONV2{ + Location: cav.Location, + } + putJSONBinaryField(cav.Id, &cavjson.CID, &cavjson.CID64) + putJSONBinaryField(cav.VerificationId, &cavjson.VID, &cavjson.VID64) + mjson.Caveats[i] = cavjson + } + data, err := json.Marshal(mjson) + if err != nil { + return nil, fmt.Errorf("cannot marshal json data: %v", err) + } + return data, nil +} + +// initJSONV2 initializes m from the JSON-unmarshaled data +// held in mjson. +func (m *Macaroon) initJSONV2(mjson *macaroonJSONV2) error { + id, err := jsonBinaryField(mjson.Identifier, mjson.Identifier64) + if err != nil { + return fmt.Errorf("invalid identifier: %v", err) + } + m.init(id, mjson.Location, V2) + sig, err := jsonBinaryField(mjson.Signature, mjson.Signature64) + if err != nil { + return fmt.Errorf("invalid signature: %v", err) + } + if len(sig) != hashLen { + return fmt.Errorf("signature has unexpected length %d", len(sig)) + } + copy(m.sig[:], sig) + m.caveats = make([]Caveat, 0, len(mjson.Caveats)) + for _, cav := range mjson.Caveats { + cid, err := jsonBinaryField(cav.CID, cav.CID64) + if err != nil { + return fmt.Errorf("invalid cid in caveat: %v", err) + } + vid, err := jsonBinaryField(cav.VID, cav.VID64) + if err != nil { + return fmt.Errorf("invalid vid in caveat: %v", err) + } + m.appendCaveat(cid, vid, cav.Location) + } + return nil +} + +// putJSONBinaryField puts the value of x into one +// of the appropriate fields depending on its value. +func putJSONBinaryField(x []byte, s, sb64 *string) { + if !utf8.Valid(x) { + *sb64 = base64.RawURLEncoding.EncodeToString(x) + return + } + // We could use either string or base64 encoding; + // choose the most compact of the two possibilities. + b64len := base64.RawURLEncoding.EncodedLen(len(x)) + sx := string(x) + if jsonEnc, _ := json.Marshal(sx); len(jsonEnc)-2 <= b64len+2 { + // The JSON encoding is smaller than the base 64 encoding. + // NB marshaling a string can never return an error; + // it always includes the two quote characters; + // but using base64 also uses two extra characters for the + // "64" suffix on the field name. If all is equal, prefer string + // encoding because it's more readable. + *s = sx + return + } + *sb64 = base64.RawURLEncoding.EncodeToString(x) +} + +// jsonBinaryField returns the value of a JSON field that may +// be string, hex or base64-encoded. +func jsonBinaryField(s, sb64 string) ([]byte, error) { + switch { + case s != "": + if sb64 != "" { + return nil, fmt.Errorf("ambiguous field encoding") + } + return []byte(s), nil + case sb64 != "": + return Base64Decode([]byte(sb64)) + } + return []byte{}, nil +} + +// The v2 binary format of a macaroon is as follows. +// All entries other than the version are packets as +// parsed by parsePacketV2. +// +// version [1 byte] +// location? +// identifier +// eos +// ( +// location? +// identifier +// verificationId? +// eos +// )* +// eos +// signature +// +// See also https://github.com/rescrv/libmacaroons/blob/master/doc/format.txt + +// parseBinaryV2 parses the given data in V2 format into the macaroon. The macaroon's +// internal data structures will retain references to the data. It +// returns the data after the end of the macaroon. +func (m *Macaroon) parseBinaryV2(data []byte) ([]byte, error) { + // The version has already been checked, so + // skip it. + data = data[1:] + + data, section, err := parseSectionV2(data) + if err != nil { + return nil, err + } + var loc string + if len(section) > 0 && section[0].fieldType == fieldLocation { + loc = string(section[0].data) + section = section[1:] + } + if len(section) != 1 || section[0].fieldType != fieldIdentifier { + return nil, fmt.Errorf("invalid macaroon header") + } + id := section[0].data + m.init(id, loc, V2) + for { + rest, section, err := parseSectionV2(data) + if err != nil { + return nil, err + } + data = rest + if len(section) == 0 { + break + } + var cav Caveat + if len(section) > 0 && section[0].fieldType == fieldLocation { + cav.Location = string(section[0].data) + section = section[1:] + } + if len(section) == 0 || section[0].fieldType != fieldIdentifier { + return nil, fmt.Errorf("no identifier in caveat") + } + cav.Id = section[0].data + section = section[1:] + if len(section) == 0 { + // First party caveat. + if cav.Location != "" { + return nil, fmt.Errorf("location not allowed in first party caveat") + } + m.caveats = append(m.caveats, cav) + continue + } + if len(section) != 1 { + return nil, fmt.Errorf("extra fields found in caveat") + } + if section[0].fieldType != fieldVerificationId { + return nil, fmt.Errorf("invalid field found in caveat") + } + cav.VerificationId = section[0].data + m.caveats = append(m.caveats, cav) + } + data, sig, err := parsePacketV2(data) + if err != nil { + return nil, err + } + if sig.fieldType != fieldSignature { + return nil, fmt.Errorf("unexpected field found instead of signature") + } + if len(sig.data) != hashLen { + return nil, fmt.Errorf("signature has unexpected length") + } + copy(m.sig[:], sig.data) + return data, nil +} + +// appendBinaryV2 appends the binary-encoded macaroon +// in v2 format to data. +func (m *Macaroon) appendBinaryV2(data []byte) []byte { + // Version byte. + data = append(data, 2) + if len(m.location) > 0 { + data = appendPacketV2(data, packetV2{ + fieldType: fieldLocation, + data: []byte(m.location), + }) + } + data = appendPacketV2(data, packetV2{ + fieldType: fieldIdentifier, + data: m.id, + }) + data = appendEOSV2(data) + for _, cav := range m.caveats { + if len(cav.Location) > 0 { + data = appendPacketV2(data, packetV2{ + fieldType: fieldLocation, + data: []byte(cav.Location), + }) + } + data = appendPacketV2(data, packetV2{ + fieldType: fieldIdentifier, + data: cav.Id, + }) + if len(cav.VerificationId) > 0 { + data = appendPacketV2(data, packetV2{ + fieldType: fieldVerificationId, + data: []byte(cav.VerificationId), + }) + } + data = appendEOSV2(data) + } + data = appendEOSV2(data) + data = appendPacketV2(data, packetV2{ + fieldType: fieldSignature, + data: m.sig[:], + }) + return data +} diff --git a/vendor/src/github.com/go-macaroon/macaroon/marshal.go b/vendor/src/github.com/go-macaroon/macaroon/marshal.go new file mode 100644 index 00000000..7bfb99bb --- /dev/null +++ b/vendor/src/github.com/go-macaroon/macaroon/marshal.go @@ -0,0 +1,239 @@ +package macaroon + +import ( + "encoding/base64" + "encoding/json" + "fmt" +) + +// Version specifies the version of a macaroon. +// In version 1, the macaroon id and all caveats +// must be UTF-8-compatible strings, and the +// size of any part of the macaroon may not exceed +// approximately 64K. In version 2, +// all field may be arbitrary binary blobs. +type Version uint16 + +const ( + // V1 specifies version 1 macaroons. + V1 Version = 1 + + // V2 specifies version 2 macaroons. + V2 Version = 2 + + // LatestVersion holds the latest supported version. + LatestVersion = V2 +) + +// String returns a string representation of the version; +// for example V1 formats as "v1". +func (v Version) String() string { + return fmt.Sprintf("v%d", v) +} + +// Version returns the version of the macaroon. +func (m *Macaroon) Version() Version { + return m.version +} + +// MarshalJSON implements json.Marshaler by marshaling the +// macaroon in JSON format. The serialisation format is determined +// by the macaroon's version. +func (m *Macaroon) MarshalJSON() ([]byte, error) { + switch m.version { + case V1: + return m.marshalJSONV1() + case V2: + return m.marshalJSONV2() + default: + return nil, fmt.Errorf("unknown version %v", m.version) + } +} + +// UnmarshalJSON implements json.Unmarshaller by unmarshaling +// the given macaroon in JSON format. It accepts both V1 and V2 +// forms encoded forms, and also a base64-encoded JSON string +// containing the binary-marshaled macaroon. +// +// After unmarshaling, the macaroon's version will reflect +// the version that it was unmarshaled as. +func (m *Macaroon) UnmarshalJSON(data []byte) error { + if data[0] == '"' { + // It's a string, so it must be a base64-encoded binary form. + var s string + if err := json.Unmarshal(data, &s); err != nil { + return err + } + data, err := Base64Decode([]byte(s)) + if err != nil { + return err + } + if err := m.UnmarshalBinary(data); err != nil { + return err + } + return nil + } + // Not a string; try to unmarshal into both kinds of macaroon object. + // This assumes that neither format has any fields in common. + // For subsequent versions we may need to change this approach. + type MacaroonJSONV1 macaroonJSONV1 + type MacaroonJSONV2 macaroonJSONV2 + var both struct { + *MacaroonJSONV1 + *MacaroonJSONV2 + } + if err := json.Unmarshal(data, &both); err != nil { + return err + } + switch { + case both.MacaroonJSONV1 != nil && both.MacaroonJSONV2 != nil: + return fmt.Errorf("cannot determine macaroon encoding version") + case both.MacaroonJSONV1 != nil: + if err := m.initJSONV1((*macaroonJSONV1)(both.MacaroonJSONV1)); err != nil { + return err + } + m.version = V1 + case both.MacaroonJSONV2 != nil: + if err := m.initJSONV2((*macaroonJSONV2)(both.MacaroonJSONV2)); err != nil { + return err + } + m.version = V2 + default: + return fmt.Errorf("invalid JSON macaroon encoding") + } + return nil +} + +// UnmarshalBinary implements encoding.BinaryUnmarshaler. +// It accepts both V1 and V2 binary encodings. +func (m *Macaroon) UnmarshalBinary(data []byte) error { + // Copy the data to avoid retaining references to it + // in the internal data structures. + data = append([]byte(nil), data...) + _, err := m.parseBinary(data) + return err +} + +// parseBinary parses the macaroon in binary format +// from the given data and returns where the parsed data ends. +// +// It retains references to data. +func (m *Macaroon) parseBinary(data []byte) ([]byte, error) { + if len(data) == 0 { + return nil, fmt.Errorf("empty macaroon data") + } + v := data[0] + if v == 2 { + // Version 2 binary format. + data, err := m.parseBinaryV2(data) + if err != nil { + return nil, fmt.Errorf("unmarshal v2: %v", err) + } + m.version = V2 + return data, nil + } + if isASCIIHex(v) { + // It's a hex digit - version 1 binary format + data, err := m.parseBinaryV1(data) + if err != nil { + return nil, fmt.Errorf("unmarshal v1: %v", err) + } + m.version = V1 + return data, nil + } + return nil, fmt.Errorf("cannot determine data format of binary-encoded macaroon") +} + +// MarshalBinary implements encoding.BinaryMarshaler by +// formatting the macaroon according to the version specified +// by MarshalAs. +func (m *Macaroon) MarshalBinary() ([]byte, error) { + return m.appendBinary(nil) +} + +// appendBinary appends the binary-formatted macaroon to +// the given data, formatting it according to the macaroon's +// version. +func (m *Macaroon) appendBinary(data []byte) ([]byte, error) { + switch m.version { + case V1: + return m.appendBinaryV1(data) + case V2: + return m.appendBinaryV2(data), nil + default: + return nil, fmt.Errorf("bad macaroon version %v", m.version) + } +} + +// Slice defines a collection of macaroons. By convention, the +// first macaroon in the slice is a primary macaroon and the rest +// are discharges for its third party caveats. +type Slice []*Macaroon + +// MarshalBinary implements encoding.BinaryMarshaler. +func (s Slice) MarshalBinary() ([]byte, error) { + var data []byte + var err error + for _, m := range s { + data, err = m.appendBinary(data) + if err != nil { + return nil, fmt.Errorf("failed to marshal macaroon %q: %v", m.Id(), err) + } + } + return data, nil +} + +// UnmarshalBinary implements encoding.BinaryUnmarshaler. +// It accepts all known binary encodings for the data - all the +// embedded macaroons need not be encoded in the same format. +func (s *Slice) UnmarshalBinary(data []byte) error { + // Prevent the internal data structures from holding onto the + // slice by copying it first. + data = append([]byte(nil), data...) + *s = (*s)[:0] + for len(data) > 0 { + var m Macaroon + rest, err := m.parseBinary(data) + if err != nil { + return fmt.Errorf("cannot unmarshal macaroon: %v", err) + } + *s = append(*s, &m) + data = rest + } + return nil +} + +const ( + padded = 1 << iota + stdEncoding +) + +var codecs = [4]*base64.Encoding{ + 0: base64.RawURLEncoding, + padded: base64.URLEncoding, + stdEncoding: base64.RawStdEncoding, + stdEncoding | padded: base64.StdEncoding, +} + +// Base64Decode base64-decodes the given data. +// It accepts both standard and URL encodings, both +// padded and unpadded. +func Base64Decode(data []byte) ([]byte, error) { + encoding := 0 + if len(data) > 0 && data[len(data)-1] == '=' { + encoding |= padded + } + for _, b := range data { + if b == '/' || b == '+' { + encoding |= stdEncoding + break + } + } + codec := codecs[encoding] + buf := make([]byte, codec.DecodedLen(len(data))) + n, err := codec.Decode(buf, data) + if err == nil { + return buf[0:n], nil + } + return nil, err +} diff --git a/vendor/src/github.com/go-macaroon/macaroon/marshal_test.go b/vendor/src/github.com/go-macaroon/macaroon/marshal_test.go new file mode 100644 index 00000000..b8f4a969 --- /dev/null +++ b/vendor/src/github.com/go-macaroon/macaroon/marshal_test.go @@ -0,0 +1,203 @@ +package macaroon_test + +import ( + jc "github.com/juju/testing/checkers" + gc "gopkg.in/check.v1" + + "gopkg.in/macaroon.v2" +) + +type marshalSuite struct{} + +var _ = gc.Suite(&marshalSuite{}) + +func (s *marshalSuite) TestMarshalUnmarshalMacaroonV1(c *gc.C) { + s.testMarshalUnmarshalWithVersion(c, macaroon.V1) +} + +func (s *marshalSuite) TestMarshalUnmarshalMacaroonV2(c *gc.C) { + s.testMarshalUnmarshalWithVersion(c, macaroon.V2) +} + +func (*marshalSuite) testMarshalUnmarshalWithVersion(c *gc.C, vers macaroon.Version) { + rootKey := []byte("secret") + m := MustNew(rootKey, []byte("some id"), "a location", vers) + + // Adding the third party caveat before the first party caveat + // tests a former bug where the caveat wasn't zeroed + // before moving to the next caveat. + err := m.AddThirdPartyCaveat([]byte("shared root key"), []byte("3rd party caveat"), "remote.com") + c.Assert(err, gc.IsNil) + + err = m.AddFirstPartyCaveat([]byte("a caveat")) + c.Assert(err, gc.IsNil) + + b, err := m.MarshalBinary() + c.Assert(err, gc.IsNil) + + var um macaroon.Macaroon + err = um.UnmarshalBinary(b) + c.Assert(err, gc.IsNil) + + c.Assert(um.Location(), gc.Equals, m.Location()) + c.Assert(string(um.Id()), gc.Equals, string(m.Id())) + c.Assert(um.Signature(), jc.DeepEquals, m.Signature()) + c.Assert(um.Caveats(), jc.DeepEquals, m.Caveats()) + c.Assert(um.Version(), gc.Equals, vers) + um.SetVersion(m.Version()) + c.Assert(m, jc.DeepEquals, &um) +} + +func (s *marshalSuite) TestMarshalBinaryRoundTrip(c *gc.C) { + // This data holds the V2 binary encoding of + data := []byte( + "\x02" + + "\x01\x0ehttp://mybank/" + + "\x02\x1cwe used our other secret key" + + "\x00" + + "\x02\x14account = 3735928559" + + "\x00" + + "\x01\x13http://auth.mybank/" + + "\x02'this was how we remind auth of key/pred" + + "\x04\x48\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xd3\x6e\xc5\x02\xe0\x58\x86\xd1\xf0\x27\x9f\x05\x5f\xa5\x25\x54\xd1\x6d\x16\xc1\xb1\x40\x74\xbb\xb8\x3f\xf0\xfd\xd7\x9d\xc2\xfe\x09\x8f\x0e\xd4\xa2\xb0\x91\x13\x0e\x6b\x5d\xb4\x6a\x20\xa8\x6b" + + "\x00" + + "\x00" + + "\x06\x20\xd2\x7d\xb2\xfd\x1f\x22\x76\x0e\x4c\x3d\xae\x81\x37\xe2\xd8\xfc\x1d\xf6\xc0\x74\x1c\x18\xae\xd4\xb9\x72\x56\xbf\x78\xd1\xf5\x5c", + ) + var m macaroon.Macaroon + err := m.UnmarshalBinary(data) + c.Assert(err, gc.Equals, nil) + assertLibMacaroonsMacaroon(c, &m) + c.Assert(m.Version(), gc.Equals, macaroon.V2) + + data1, err := m.MarshalBinary() + c.Assert(err, gc.Equals, nil) + c.Assert(data1, jc.DeepEquals, data) +} + +func (s *marshalSuite) TestMarshalUnmarshalSliceV1(c *gc.C) { + s.testMarshalUnmarshalSliceWithVersion(c, macaroon.V1) +} + +func (s *marshalSuite) TestMarshalUnmarshalSliceV2(c *gc.C) { + s.testMarshalUnmarshalSliceWithVersion(c, macaroon.V2) +} + +func (*marshalSuite) testMarshalUnmarshalSliceWithVersion(c *gc.C, vers macaroon.Version) { + rootKey := []byte("secret") + m1 := MustNew(rootKey, []byte("some id"), "a location", vers) + m2 := MustNew(rootKey, []byte("some other id"), "another location", vers) + + err := m1.AddFirstPartyCaveat([]byte("a caveat")) + c.Assert(err, gc.IsNil) + err = m2.AddFirstPartyCaveat([]byte("another caveat")) + c.Assert(err, gc.IsNil) + + macaroons := macaroon.Slice{m1, m2} + + b, err := macaroons.MarshalBinary() + c.Assert(err, gc.IsNil) + + var unmarshaledMacs macaroon.Slice + err = unmarshaledMacs.UnmarshalBinary(b) + c.Assert(err, gc.IsNil) + + c.Assert(unmarshaledMacs, gc.HasLen, len(macaroons)) + for i, m := range macaroons { + um := unmarshaledMacs[i] + c.Assert(um.Location(), gc.Equals, m.Location()) + c.Assert(string(um.Id()), gc.Equals, string(m.Id())) + c.Assert(um.Signature(), jc.DeepEquals, m.Signature()) + c.Assert(um.Caveats(), jc.DeepEquals, m.Caveats()) + c.Assert(um.Version(), gc.Equals, vers) + um.SetVersion(m.Version()) + } + c.Assert(macaroons, jc.DeepEquals, unmarshaledMacs) + + // Check that appending a caveat to the first does not + // affect the second. + for i := 0; i < 10; i++ { + err = unmarshaledMacs[0].AddFirstPartyCaveat([]byte("caveat")) + c.Assert(err, gc.IsNil) + } + unmarshaledMacs[1].SetVersion(macaroons[1].Version()) + c.Assert(unmarshaledMacs[1], jc.DeepEquals, macaroons[1]) + c.Assert(err, gc.IsNil) +} + +func (s *marshalSuite) TestSliceRoundTripV1(c *gc.C) { + s.testSliceRoundTripWithVersion(c, macaroon.V1) +} + +func (s *marshalSuite) TestSliceRoundTripV2(c *gc.C) { + s.testSliceRoundTripWithVersion(c, macaroon.V2) +} + +func (*marshalSuite) testSliceRoundTripWithVersion(c *gc.C, vers macaroon.Version) { + rootKey := []byte("secret") + m1 := MustNew(rootKey, []byte("some id"), "a location", vers) + m2 := MustNew(rootKey, []byte("some other id"), "another location", vers) + + err := m1.AddFirstPartyCaveat([]byte("a caveat")) + c.Assert(err, gc.IsNil) + err = m2.AddFirstPartyCaveat([]byte("another caveat")) + c.Assert(err, gc.IsNil) + + macaroons := macaroon.Slice{m1, m2} + + b, err := macaroons.MarshalBinary() + c.Assert(err, gc.IsNil) + + var unmarshaledMacs macaroon.Slice + err = unmarshaledMacs.UnmarshalBinary(b) + c.Assert(err, gc.IsNil) + + marshaledMacs, err := unmarshaledMacs.MarshalBinary() + c.Assert(err, gc.IsNil) + + c.Assert(b, jc.DeepEquals, marshaledMacs) +} + +var base64DecodeTests = []struct { + about string + input string + expect string + expectError string +}{{ + about: "empty string", + input: "", + expect: "", +}, { + about: "standard encoding, padded", + input: "Z29+IQ==", + expect: "go~!", +}, { + about: "URL encoding, padded", + input: "Z29-IQ==", + expect: "go~!", +}, { + about: "standard encoding, not padded", + input: "Z29+IQ", + expect: "go~!", +}, { + about: "URL encoding, not padded", + input: "Z29-IQ", + expect: "go~!", +}, { + about: "standard encoding, too much padding", + input: "Z29+IQ===", + expectError: `illegal base64 data at input byte 8`, +}} + +func (*marshalSuite) TestBase64Decode(c *gc.C) { + for i, test := range base64DecodeTests { + c.Logf("test %d: %s", i, test.about) + out, err := macaroon.Base64Decode([]byte(test.input)) + if test.expectError != "" { + c.Assert(err, gc.ErrorMatches, test.expectError) + } else { + c.Assert(err, gc.Equals, nil) + c.Assert(string(out), gc.Equals, test.expect) + } + } +} diff --git a/vendor/src/github.com/go-macaroon/macaroon/packet-v1.go b/vendor/src/github.com/go-macaroon/macaroon/packet-v1.go new file mode 100644 index 00000000..10e9f7b7 --- /dev/null +++ b/vendor/src/github.com/go-macaroon/macaroon/packet-v1.go @@ -0,0 +1,133 @@ +package macaroon + +import ( + "bytes" + "fmt" +) + +// field names, as defined in libmacaroons +const ( + fieldNameLocation = "location" + fieldNameIdentifier = "identifier" + fieldNameSignature = "signature" + fieldNameCaveatId = "cid" + fieldNameVerificationId = "vid" + fieldNameCaveatLocation = "cl" +) + +// maxPacketV1Len is the maximum allowed length of a packet in the v1 macaroon +// serialization format. +const maxPacketV1Len = 0xffff + +// The original macaroon binary encoding is made from a sequence +// of "packets", each of which has a field name and some data. +// The encoding is: +// +// - four ascii hex digits holding the entire packet size (including +// the digits themselves). +// +// - the field name, followed by an ascii space. +// +// - the raw data +// +// - a newline (\n) character +// +// The packet struct below holds a reference into Macaroon.data. +type packetV1 struct { + // ftype holds the field name of the packet. + fieldName []byte + + // data holds the packet's data. + data []byte + + // len holds the total length in bytes + // of the packet, including any header. + totalLen int +} + +// parsePacket parses the packet at the start of the +// given data. +func parsePacketV1(data []byte) (packetV1, error) { + if len(data) < 6 { + return packetV1{}, fmt.Errorf("packet too short") + } + plen, ok := parseSizeV1(data) + if !ok { + return packetV1{}, fmt.Errorf("cannot parse size") + } + if plen > len(data) { + return packetV1{}, fmt.Errorf("packet size too big") + } + if plen < 4 { + return packetV1{}, fmt.Errorf("packet size too small") + } + data = data[4:plen] + i := bytes.IndexByte(data, ' ') + if i <= 0 { + return packetV1{}, fmt.Errorf("cannot parse field name") + } + fieldName := data[0:i] + if data[len(data)-1] != '\n' { + return packetV1{}, fmt.Errorf("no terminating newline found") + } + return packetV1{ + fieldName: fieldName, + data: data[i+1 : len(data)-1], + totalLen: plen, + }, nil +} + +// appendPacketV1 appends a packet with the given field name +// and data to the given buffer. If the field and data were +// too long to be encoded, it returns nil, false; otherwise +// it returns the appended buffer. +func appendPacketV1(buf []byte, field string, data []byte) ([]byte, bool) { + plen := packetV1Size(field, data) + if plen > maxPacketV1Len { + return nil, false + } + buf = appendSizeV1(buf, plen) + buf = append(buf, field...) + buf = append(buf, ' ') + buf = append(buf, data...) + buf = append(buf, '\n') + return buf, true +} + +func packetV1Size(field string, data []byte) int { + return 4 + len(field) + 1 + len(data) + 1 +} + +var hexDigits = []byte("0123456789abcdef") + +func appendSizeV1(data []byte, size int) []byte { + return append(data, + hexDigits[size>>12], + hexDigits[(size>>8)&0xf], + hexDigits[(size>>4)&0xf], + hexDigits[size&0xf], + ) +} + +func parseSizeV1(data []byte) (int, bool) { + d0, ok0 := asciiHex(data[0]) + d1, ok1 := asciiHex(data[1]) + d2, ok2 := asciiHex(data[2]) + d3, ok3 := asciiHex(data[3]) + return d0<<12 + d1<<8 + d2<<4 + d3, ok0 && ok1 && ok2 && ok3 +} + +func asciiHex(b byte) (int, bool) { + switch { + case b >= '0' && b <= '9': + return int(b) - '0', true + case b >= 'a' && b <= 'f': + return int(b) - 'a' + 0xa, true + } + return 0, false +} + +func isASCIIHex(b byte) bool { + _, ok := asciiHex(b) + return ok +} diff --git a/vendor/src/github.com/go-macaroon/macaroon/packet-v1_test.go b/vendor/src/github.com/go-macaroon/macaroon/packet-v1_test.go new file mode 100644 index 00000000..776164c6 --- /dev/null +++ b/vendor/src/github.com/go-macaroon/macaroon/packet-v1_test.go @@ -0,0 +1,98 @@ +package macaroon + +import ( + "strconv" + "strings" + "unicode" + + gc "gopkg.in/check.v1" +) + +type packetV1Suite struct{} + +var _ = gc.Suite(&packetV1Suite{}) + +func (*packetV1Suite) TestAppendPacket(c *gc.C) { + data, ok := appendPacketV1(nil, "field", []byte("some data")) + c.Assert(ok, gc.Equals, true) + c.Assert(string(data), gc.Equals, "0014field some data\n") + + data, ok = appendPacketV1(data, "otherfield", []byte("more and more data")) + c.Assert(ok, gc.Equals, true) + c.Assert(string(data), gc.Equals, "0014field some data\n0022otherfield more and more data\n") +} + +func (*packetV1Suite) TestAppendPacketTooBig(c *gc.C) { + data, ok := appendPacketV1(nil, "field", make([]byte, 65532)) + c.Assert(ok, gc.Equals, false) + c.Assert(data, gc.IsNil) +} + +var parsePacketV1Tests = []struct { + data string + expect packetV1 + expectErr string +}{{ + expectErr: "packet too short", +}, { + data: "0014field some data\n", + expect: packetV1{ + fieldName: []byte("field"), + data: []byte("some data"), + totalLen: 20, + }, +}, { + data: "0015field some data\n", + expectErr: "packet size too big", +}, { + data: "0003a\n", + expectErr: "packet size too small", +}, { + data: "0014fieldwithoutanyspaceordata\n", + expectErr: "cannot parse field name", +}, { + data: "fedcsomefield " + strings.Repeat("x", 0xfedc-len("0000somefield \n")) + "\n", + expect: packetV1{ + fieldName: []byte("somefield"), + data: []byte(strings.Repeat("x", 0xfedc-len("0000somefield \n"))), + totalLen: 0xfedc, + }, +}, { + data: "zzzzbadpacketsizenomacaroon", + expectErr: "cannot parse size", +}} + +func (*packetV1Suite) TestParsePacketV1(c *gc.C) { + for i, test := range parsePacketV1Tests { + c.Logf("test %d: %q", i, truncate(test.data)) + p, err := parsePacketV1([]byte(test.data)) + if test.expectErr != "" { + c.Assert(err, gc.ErrorMatches, test.expectErr) + c.Assert(p, gc.DeepEquals, packetV1{}) + continue + } + c.Assert(err, gc.IsNil) + c.Assert(p, gc.DeepEquals, test.expect) + } +} + +func truncate(d string) string { + if len(d) > 50 { + return d[0:50] + "..." + } + return d +} + +func (*packetV1Suite) TestAsciiHex(c *gc.C) { + for b := 0; b < 256; b++ { + n, err := strconv.ParseInt(string(b), 16, 8) + value, ok := asciiHex(byte(b)) + if err != nil || unicode.IsUpper(rune(b)) { + c.Assert(ok, gc.Equals, false) + c.Assert(value, gc.Equals, 0) + } else { + c.Assert(ok, gc.Equals, true) + c.Assert(value, gc.Equals, int(n)) + } + } +} diff --git a/vendor/src/github.com/go-macaroon/macaroon/packet-v2.go b/vendor/src/github.com/go-macaroon/macaroon/packet-v2.go new file mode 100644 index 00000000..9725dc38 --- /dev/null +++ b/vendor/src/github.com/go-macaroon/macaroon/packet-v2.go @@ -0,0 +1,117 @@ +package macaroon + +import ( + "encoding/binary" + "fmt" +) + +type fieldType int + +// Field constants as used in the binary encoding. +const ( + fieldEOS fieldType = 0 + fieldLocation fieldType = 1 + fieldIdentifier fieldType = 2 + fieldVerificationId fieldType = 4 + fieldSignature fieldType = 6 +) + +type packetV2 struct { + // fieldType holds the type of the field. + fieldType fieldType + + // data holds the packet's data. + data []byte +} + +// parseSectionV2 parses a sequence of packets +// in data. The sequence is terminated by a packet +// with a field type of fieldEOS. +func parseSectionV2(data []byte) ([]byte, []packetV2, error) { + prevFieldType := fieldType(-1) + var packets []packetV2 + for { + if len(data) == 0 { + return nil, nil, fmt.Errorf("section extends past end of buffer") + } + rest, p, err := parsePacketV2(data) + if err != nil { + return nil, nil, err + } + if p.fieldType == fieldEOS { + return rest, packets, nil + } + if p.fieldType <= prevFieldType { + return nil, nil, fmt.Errorf("fields out of order") + } + packets = append(packets, p) + prevFieldType = p.fieldType + data = rest + } +} + +// parsePacketV2 parses a V2 data package at the start +// of the given data. +// The format of a packet is as follows: +// +// fieldType(varint) payloadLen(varint) data[payloadLen bytes] +// +// apart from fieldEOS which has no payloadLen or data (it's +// a single zero byte). +func parsePacketV2(data []byte) ([]byte, packetV2, error) { + data, ft, err := parseVarint(data) + if err != nil { + return nil, packetV2{}, err + } + p := packetV2{ + fieldType: fieldType(ft), + } + if p.fieldType == fieldEOS { + return data, p, nil + } + data, payloadLen, err := parseVarint(data) + if err != nil { + return nil, packetV2{}, err + } + if payloadLen > len(data) { + return nil, packetV2{}, fmt.Errorf("field data extends past end of buffer") + } + p.data = data[0:payloadLen] + return data[payloadLen:], p, nil +} + +// parseVarint parses the variable-length integer +// at the start of the given data and returns rest +// of the buffer and the number. +func parseVarint(data []byte) ([]byte, int, error) { + val, n := binary.Uvarint(data) + if n > 0 { + if val > 0x7fffffff { + return nil, 0, fmt.Errorf("varint value out of range") + } + return data[n:], int(val), nil + } + if n == 0 { + return nil, 0, fmt.Errorf("varint value extends past end of buffer") + } + return nil, 0, fmt.Errorf("varint value out of range") +} + +func appendPacketV2(data []byte, p packetV2) []byte { + data = appendVarint(data, int(p.fieldType)) + if p.fieldType != fieldEOS { + data = appendVarint(data, len(p.data)) + data = append(data, p.data...) + } + return data +} + +func appendEOSV2(data []byte) []byte { + return append(data, 0) +} + +func appendVarint(data []byte, x int) []byte { + var buf [binary.MaxVarintLen32]byte + n := binary.PutUvarint(buf[:], uint64(x)) + return append(data, buf[:n]...) +} diff --git a/vendor/src/github.com/go-macaroon/macaroon/packet-v2_test.go b/vendor/src/github.com/go-macaroon/macaroon/packet-v2_test.go new file mode 100644 index 00000000..1ad883f6 --- /dev/null +++ b/vendor/src/github.com/go-macaroon/macaroon/packet-v2_test.go @@ -0,0 +1,126 @@ +package macaroon + +import ( + jc "github.com/juju/testing/checkers" + gc "gopkg.in/check.v1" +) + +type packetV2Suite struct{} + +var _ = gc.Suite(&packetV2Suite{}) + +var parsePacketV2Tests = []struct { + about string + data string + expectPacket packetV2 + expectData string + expectError string +}{{ + about: "EOS packet", + data: "\x00", + expectPacket: packetV2{ + fieldType: fieldEOS, + }, +}, { + about: "simple field", + data: "\x02\x03xyz", + expectPacket: packetV2{ + fieldType: 2, + data: []byte("xyz"), + }, +}, { + about: "empty buffer", + data: "", + expectError: "varint value extends past end of buffer", +}, { + about: "varint out of range", + data: "\xff\xff\xff\xff\xff\xff\x7f", + expectError: "varint value out of range", +}, { + about: "varint way out of range", + data: "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x7f", + expectError: "varint value out of range", +}, { + about: "unterminated varint", + data: "\x80", + expectError: "varint value extends past end of buffer", +}, { + about: "field data too long", + data: "\x01\x02a", + expectError: "field data extends past end of buffer", +}, { + about: "bad data length varint", + data: "\x01\xff", + expectError: "varint value extends past end of buffer", +}} + +func (*packetV2Suite) TestParsePacketV2(c *gc.C) { + for i, test := range parsePacketV2Tests { + c.Logf("test %d: %v", i, test.about) + data, p, err := parsePacketV2([]byte(test.data)) + if test.expectError != "" { + c.Assert(err, gc.ErrorMatches, test.expectError) + c.Assert(data, gc.IsNil) + c.Assert(p, gc.DeepEquals, packetV2{}) + } else { + c.Assert(err, gc.IsNil) + c.Assert(p, jc.DeepEquals, test.expectPacket) + } + } +} + +var parseSectionV2Tests = []struct { + about string + data string + + expectData string + expectPackets []packetV2 + expectError string +}{{ + about: "no packets", + data: "\x00", +}, { + about: "one packet", + data: "\x02\x03xyz\x00", + expectPackets: []packetV2{{ + fieldType: 2, + data: []byte("xyz"), + }}, +}, { + about: "two packets", + data: "\x02\x03xyz\x07\x05abcde\x00", + expectPackets: []packetV2{{ + fieldType: 2, + data: []byte("xyz"), + }, { + fieldType: 7, + data: []byte("abcde"), + }}, +}, { + about: "unterminated section", + data: "\x02\x03xyz\x07\x05abcde", + expectError: "section extends past end of buffer", +}, { + about: "out of order fields", + data: "\x07\x05abcde\x02\x03xyz\x00", + expectError: "fields out of order", +}, { + about: "bad packet", + data: "\x07\x05abcde\xff", + expectError: "varint value extends past end of buffer", +}} + +func (*packetV2Suite) TestParseSectionV2(c *gc.C) { + for i, test := range parseSectionV2Tests { + c.Logf("test %d: %v", i, test.about) + data, ps, err := parseSectionV2([]byte(test.data)) + if test.expectError != "" { + c.Assert(err, gc.ErrorMatches, test.expectError) + c.Assert(data, gc.IsNil) + c.Assert(ps, gc.IsNil) + } else { + c.Assert(err, gc.IsNil) + c.Assert(ps, jc.DeepEquals, test.expectPackets) + } + } +} diff --git a/vendor/src/github.com/go-macaroon/macaroon/trace.go b/vendor/src/github.com/go-macaroon/macaroon/trace.go new file mode 100644 index 00000000..581a998b --- /dev/null +++ b/vendor/src/github.com/go-macaroon/macaroon/trace.go @@ -0,0 +1,102 @@ +package macaroon + +import ( + "fmt" +) + +// Trace holds all toperations involved in verifying a macaroon, +// and the root key used as the initial verification key. +// This can be useful for debugging macaroon implementations. +type Trace struct { + RootKey []byte + Ops []TraceOp +} + +// Results returns the output from all operations in the Trace. +// The result from ts.Ops[i] will be in the i'th element of the +// returned slice. +// When a trace has resulted in a failure, the +// last element will be nil. +func (t Trace) Results() [][]byte { + r := make([][]byte, len(t.Ops)) + input := t.RootKey + for i, op := range t.Ops { + input = op.Result(input) + r[i] = input + } + return r +} + +// TraceOp holds one possible operation when verifying a macaroon. +type TraceOp struct { + Kind TraceOpKind `json:"kind"` + Data1 []byte `json:"data1,omitempty"` + Data2 []byte `json:"data2,omitempty"` +} + +// Result returns the result of computing the given +// operation with the given input data. +// If op is TraceFail, it returns nil. +func (op TraceOp) Result(input []byte) []byte { + switch op.Kind { + case TraceMakeKey: + return makeKey(input)[:] + case TraceHash: + if len(op.Data2) == 0 { + return keyedHash(bytesToKey(input), op.Data1)[:] + } + return keyedHash2(bytesToKey(input), op.Data1, op.Data2)[:] + case TraceBind: + return bindForRequest(op.Data1, bytesToKey(input))[:] + case TraceFail: + return nil + default: + panic(fmt.Errorf("unknown trace operation kind %d", op.Kind)) + } +} + +func bytesToKey(data []byte) *[keyLen]byte { + var key [keyLen]byte + if len(data) != keyLen { + panic(fmt.Errorf("unexpected input key length; got %d want %d", len(data), keyLen)) + } + copy(key[:], data) + return &key +} + +// TraceOpKind represents the kind of a macaroon verification operation. +type TraceOpKind int + +const ( + TraceInvalid = TraceOpKind(iota) + + // TraceMakeKey represents the operation of calculating a + // fixed length root key from the variable length input key. + TraceMakeKey + + // TraceHash represents a keyed hash operation with one + // or two values. If there is only one value, it will be in Data1. + TraceHash + + // TraceBind represents the operation of binding a discharge macaroon + // to its primary macaroon. Data1 holds the signature of the primary + // macaroon. + TraceBind + + // TraceFail represents a verification failure. If present, this will always + // be the last operation in a trace. + TraceFail +) + +var traceOps = []string{ + TraceInvalid: "invalid", + TraceMakeKey: "makekey", + TraceHash: "hash", + TraceBind: "bind", + TraceFail: "fail", +} + +// String returns a string representation of the operation. +func (k TraceOpKind) String() string { + return traceOps[k] +} diff --git a/vendor/src/golang.org/x/crypto/nacl/secretbox/example_test.go b/vendor/src/golang.org/x/crypto/nacl/secretbox/example_test.go new file mode 100644 index 00000000..789f4ff0 --- /dev/null +++ b/vendor/src/golang.org/x/crypto/nacl/secretbox/example_test.go @@ -0,0 +1,53 @@ +// Copyright 2016 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 secretbox_test + +import ( + "crypto/rand" + "encoding/hex" + "fmt" + "io" + + "golang.org/x/crypto/nacl/secretbox" +) + +func Example() { + // Load your secret key from a safe place and reuse it across multiple + // Seal calls. (Obviously don't use this example key for anything + // real.) If you want to convert a passphrase to a key, use a suitable + // package like bcrypt or scrypt. + secretKeyBytes, err := hex.DecodeString("6368616e676520746869732070617373776f726420746f206120736563726574") + if err != nil { + panic(err) + } + + var secretKey [32]byte + copy(secretKey[:], secretKeyBytes) + + // You must use a different nonce for each message you encrypt with the + // same key. Since the nonce here is 192 bits long, a random value + // provides a sufficiently small probability of repeats. + var nonce [24]byte + if _, err := io.ReadFull(rand.Reader, nonce[:]); err != nil { + panic(err) + } + + // This encrypts "hello world" and appends the result to the nonce. + encrypted := secretbox.Seal(nonce[:], []byte("hello world"), &nonce, &secretKey) + + // When you decrypt, you must use the same nonce and key you used to + // encrypt the message. One way to achieve this is to store the nonce + // alongside the encrypted message. Above, we stored the nonce in the first + // 24 bytes of the encrypted text. + var decryptNonce [24]byte + copy(decryptNonce[:], encrypted[:24]) + decrypted, ok := secretbox.Open(nil, encrypted[24:], &decryptNonce, &secretKey) + if !ok { + panic("decryption error") + } + + fmt.Println(string(decrypted)) + // Output: hello world +} diff --git a/vendor/src/golang.org/x/crypto/nacl/secretbox/secretbox.go b/vendor/src/golang.org/x/crypto/nacl/secretbox/secretbox.go new file mode 100644 index 00000000..53ee83cf --- /dev/null +++ b/vendor/src/golang.org/x/crypto/nacl/secretbox/secretbox.go @@ -0,0 +1,166 @@ +// Copyright 2012 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 secretbox encrypts and authenticates small messages. + +Secretbox uses XSalsa20 and Poly1305 to encrypt and authenticate messages with +secret-key cryptography. The length of messages is not hidden. + +It is the caller's responsibility to ensure the uniqueness of nonces—for +example, by using nonce 1 for the first message, nonce 2 for the second +message, etc. Nonces are long enough that randomly generated nonces have +negligible risk of collision. + +Messages should be small because: + +1. The whole message needs to be held in memory to be processed. + +2. Using large messages pressures implementations on small machines to decrypt +and process plaintext before authenticating it. This is very dangerous, and +this API does not allow it, but a protocol that uses excessive message sizes +might present some implementations with no other choice. + +3. Fixed overheads will be sufficiently amortised by messages as small as 8KB. + +4. Performance may be improved by working with messages that fit into data caches. + +Thus large amounts of data should be chunked so that each message is small. +(Each message still needs a unique nonce.) If in doubt, 16KB is a reasonable +chunk size. + +This package is interoperable with NaCl: https://nacl.cr.yp.to/secretbox.html. +*/ +package secretbox // import "golang.org/x/crypto/nacl/secretbox" + +import ( + "golang.org/x/crypto/poly1305" + "golang.org/x/crypto/salsa20/salsa" +) + +// Overhead is the number of bytes of overhead when boxing a message. +const Overhead = poly1305.TagSize + +// setup produces a sub-key and Salsa20 counter given a nonce and key. +func setup(subKey *[32]byte, counter *[16]byte, nonce *[24]byte, key *[32]byte) { + // We use XSalsa20 for encryption so first we need to generate a + // key and nonce with HSalsa20. + var hNonce [16]byte + copy(hNonce[:], nonce[:]) + salsa.HSalsa20(subKey, &hNonce, key, &salsa.Sigma) + + // The final 8 bytes of the original nonce form the new nonce. + copy(counter[:], nonce[16:]) +} + +// sliceForAppend takes a slice and a requested number of bytes. It returns a +// slice with the contents of the given slice followed by that many bytes and a +// second slice that aliases into it and contains only the extra bytes. If the +// original slice has sufficient capacity then no allocation is performed. +func sliceForAppend(in []byte, n int) (head, tail []byte) { + if total := len(in) + n; cap(in) >= total { + head = in[:total] + } else { + head = make([]byte, total) + copy(head, in) + } + tail = head[len(in):] + return +} + +// Seal appends an encrypted and authenticated copy of message to out, which +// must not overlap message. The key and nonce pair must be unique for each +// distinct message and the output will be Overhead bytes longer than message. +func Seal(out, message []byte, nonce *[24]byte, key *[32]byte) []byte { + var subKey [32]byte + var counter [16]byte + setup(&subKey, &counter, nonce, key) + + // The Poly1305 key is generated by encrypting 32 bytes of zeros. Since + // Salsa20 works with 64-byte blocks, we also generate 32 bytes of + // keystream as a side effect. + var firstBlock [64]byte + salsa.XORKeyStream(firstBlock[:], firstBlock[:], &counter, &subKey) + + var poly1305Key [32]byte + copy(poly1305Key[:], firstBlock[:]) + + ret, out := sliceForAppend(out, len(message)+poly1305.TagSize) + + // We XOR up to 32 bytes of message with the keystream generated from + // the first block. + firstMessageBlock := message + if len(firstMessageBlock) > 32 { + firstMessageBlock = firstMessageBlock[:32] + } + + tagOut := out + out = out[poly1305.TagSize:] + for i, x := range firstMessageBlock { + out[i] = firstBlock[32+i] ^ x + } + message = message[len(firstMessageBlock):] + ciphertext := out + out = out[len(firstMessageBlock):] + + // Now encrypt the rest. + counter[8] = 1 + salsa.XORKeyStream(out, message, &counter, &subKey) + + var tag [poly1305.TagSize]byte + poly1305.Sum(&tag, ciphertext, &poly1305Key) + copy(tagOut, tag[:]) + + return ret +} + +// Open authenticates and decrypts a box produced by Seal and appends the +// message to out, which must not overlap box. The output will be Overhead +// bytes smaller than box. +func Open(out []byte, box []byte, nonce *[24]byte, key *[32]byte) ([]byte, bool) { + if len(box) < Overhead { + return nil, false + } + + var subKey [32]byte + var counter [16]byte + setup(&subKey, &counter, nonce, key) + + // The Poly1305 key is generated by encrypting 32 bytes of zeros. Since + // Salsa20 works with 64-byte blocks, we also generate 32 bytes of + // keystream as a side effect. + var firstBlock [64]byte + salsa.XORKeyStream(firstBlock[:], firstBlock[:], &counter, &subKey) + + var poly1305Key [32]byte + copy(poly1305Key[:], firstBlock[:]) + var tag [poly1305.TagSize]byte + copy(tag[:], box) + + if !poly1305.Verify(&tag, box[poly1305.TagSize:], &poly1305Key) { + return nil, false + } + + ret, out := sliceForAppend(out, len(box)-Overhead) + + // We XOR up to 32 bytes of box with the keystream generated from + // the first block. + box = box[Overhead:] + firstMessageBlock := box + if len(firstMessageBlock) > 32 { + firstMessageBlock = firstMessageBlock[:32] + } + for i, x := range firstMessageBlock { + out[i] = firstBlock[32+i] ^ x + } + + box = box[len(firstMessageBlock):] + out = out[len(firstMessageBlock):] + + // Now decrypt the rest. + counter[8] = 1 + salsa.XORKeyStream(out, box, &counter, &subKey) + + return ret, true +} diff --git a/vendor/src/golang.org/x/crypto/nacl/secretbox/secretbox_test.go b/vendor/src/golang.org/x/crypto/nacl/secretbox/secretbox_test.go new file mode 100644 index 00000000..3c70b0f4 --- /dev/null +++ b/vendor/src/golang.org/x/crypto/nacl/secretbox/secretbox_test.go @@ -0,0 +1,154 @@ +// Copyright 2012 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 secretbox + +import ( + "bytes" + "crypto/rand" + "encoding/hex" + "testing" +) + +func TestSealOpen(t *testing.T) { + var key [32]byte + var nonce [24]byte + + rand.Reader.Read(key[:]) + rand.Reader.Read(nonce[:]) + + var box, opened []byte + + for msgLen := 0; msgLen < 128; msgLen += 17 { + message := make([]byte, msgLen) + rand.Reader.Read(message) + + box = Seal(box[:0], message, &nonce, &key) + var ok bool + opened, ok = Open(opened[:0], box, &nonce, &key) + if !ok { + t.Errorf("%d: failed to open box", msgLen) + continue + } + + if !bytes.Equal(opened, message) { + t.Errorf("%d: got %x, expected %x", msgLen, opened, message) + continue + } + } + + for i := range box { + box[i] ^= 0x20 + _, ok := Open(opened[:0], box, &nonce, &key) + if ok { + t.Errorf("box was opened after corrupting byte %d", i) + } + box[i] ^= 0x20 + } +} + +func TestSecretBox(t *testing.T) { + var key [32]byte + var nonce [24]byte + var message [64]byte + + for i := range key[:] { + key[i] = 1 + } + for i := range nonce[:] { + nonce[i] = 2 + } + for i := range message[:] { + message[i] = 3 + } + + box := Seal(nil, message[:], &nonce, &key) + // expected was generated using the C implementation of NaCl. + expected, _ := hex.DecodeString("8442bc313f4626f1359e3b50122b6ce6fe66ddfe7d39d14e637eb4fd5b45beadab55198df6ab5368439792a23c87db70acb6156dc5ef957ac04f6276cf6093b84be77ff0849cc33e34b7254d5a8f65ad") + + if !bytes.Equal(box, expected) { + t.Fatalf("box didn't match, got\n%x\n, expected\n%x", box, expected) + } +} + +func TestAppend(t *testing.T) { + var key [32]byte + var nonce [24]byte + var message [8]byte + + out := make([]byte, 4) + box := Seal(out, message[:], &nonce, &key) + if !bytes.Equal(box[:4], out[:4]) { + t.Fatalf("Seal didn't correctly append") + } + + out = make([]byte, 4, 100) + box = Seal(out, message[:], &nonce, &key) + if !bytes.Equal(box[:4], out[:4]) { + t.Fatalf("Seal didn't correctly append with sufficient capacity.") + } +} + +func benchmarkSealSize(b *testing.B, size int) { + message := make([]byte, size) + out := make([]byte, size+Overhead) + var nonce [24]byte + var key [32]byte + + b.SetBytes(int64(size)) + b.ResetTimer() + + for i := 0; i < b.N; i++ { + out = Seal(out[:0], message, &nonce, &key) + } +} + +func BenchmarkSeal8Bytes(b *testing.B) { + benchmarkSealSize(b, 8) +} + +func BenchmarkSeal100Bytes(b *testing.B) { + benchmarkSealSize(b, 100) +} + +func BenchmarkSeal1K(b *testing.B) { + benchmarkSealSize(b, 1024) +} + +func BenchmarkSeal8K(b *testing.B) { + benchmarkSealSize(b, 8192) +} + +func benchmarkOpenSize(b *testing.B, size int) { + msg := make([]byte, size) + result := make([]byte, size) + var nonce [24]byte + var key [32]byte + box := Seal(nil, msg, &nonce, &key) + + b.SetBytes(int64(size)) + b.ResetTimer() + + for i := 0; i < b.N; i++ { + if _, ok := Open(result[:0], box, &nonce, &key); !ok { + panic("Open failed") + } + } +} + +func BenchmarkOpen8Bytes(b *testing.B) { + benchmarkOpenSize(b, 8) +} + +func BenchmarkOpen100Bytes(b *testing.B) { + benchmarkOpenSize(b, 100) +} + +func BenchmarkOpen1K(b *testing.B) { + benchmarkOpenSize(b, 1024) +} + +func BenchmarkOpen8K(b *testing.B) { + benchmarkOpenSize(b, 8192) +} diff --git a/vendor/src/golang.org/x/crypto/poly1305/poly1305.go b/vendor/src/golang.org/x/crypto/poly1305/poly1305.go new file mode 100644 index 00000000..f562fa57 --- /dev/null +++ b/vendor/src/golang.org/x/crypto/poly1305/poly1305.go @@ -0,0 +1,33 @@ +// Copyright 2012 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 poly1305 implements Poly1305 one-time message authentication code as +specified in https://cr.yp.to/mac/poly1305-20050329.pdf. + +Poly1305 is a fast, one-time authentication function. It is infeasible for an +attacker to generate an authenticator for a message without the key. However, a +key must only be used for a single message. Authenticating two different +messages with the same key allows an attacker to forge authenticators for other +messages with the same key. + +Poly1305 was originally coupled with AES in order to make Poly1305-AES. AES was +used with a fixed key in order to generate one-time keys from an nonce. +However, in this package AES isn't used and the one-time key is specified +directly. +*/ +package poly1305 // import "golang.org/x/crypto/poly1305" + +import "crypto/subtle" + +// TagSize is the size, in bytes, of a poly1305 authenticator. +const TagSize = 16 + +// Verify returns true if mac is a valid authenticator for m with the given +// key. +func Verify(mac *[16]byte, m []byte, key *[32]byte) bool { + var tmp [16]byte + Sum(&tmp, m, key) + return subtle.ConstantTimeCompare(tmp[:], mac[:]) == 1 +} diff --git a/vendor/src/golang.org/x/crypto/poly1305/poly1305_test.go b/vendor/src/golang.org/x/crypto/poly1305/poly1305_test.go new file mode 100644 index 00000000..017027fe --- /dev/null +++ b/vendor/src/golang.org/x/crypto/poly1305/poly1305_test.go @@ -0,0 +1,159 @@ +// Copyright 2012 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 poly1305 + +import ( + "bytes" + "encoding/hex" + "flag" + "testing" + "unsafe" +) + +var stressFlag = flag.Bool("stress", false, "run slow stress tests") + +var testData = []struct { + in, k, correct []byte +}{ + { + []byte("Hello world!"), + []byte("this is 32-byte key for Poly1305"), + []byte{0xa6, 0xf7, 0x45, 0x00, 0x8f, 0x81, 0xc9, 0x16, 0xa2, 0x0d, 0xcc, 0x74, 0xee, 0xf2, 0xb2, 0xf0}, + }, + { + make([]byte, 32), + []byte("this is 32-byte key for Poly1305"), + []byte{0x49, 0xec, 0x78, 0x09, 0x0e, 0x48, 0x1e, 0xc6, 0xc2, 0x6b, 0x33, 0xb9, 0x1c, 0xcc, 0x03, 0x07}, + }, + { + make([]byte, 2007), + []byte("this is 32-byte key for Poly1305"), + []byte{0xda, 0x84, 0xbc, 0xab, 0x02, 0x67, 0x6c, 0x38, 0xcd, 0xb0, 0x15, 0x60, 0x42, 0x74, 0xc2, 0xaa}, + }, + { + make([]byte, 2007), + make([]byte, 32), + make([]byte, 16), + }, + { + // This test triggers an edge-case. See https://go-review.googlesource.com/#/c/30101/. + []byte{0x81, 0xd8, 0xb2, 0xe4, 0x6a, 0x25, 0x21, 0x3b, 0x58, 0xfe, 0xe4, 0x21, 0x3a, 0x2a, 0x28, 0xe9, 0x21, 0xc1, 0x2a, 0x96, 0x32, 0x51, 0x6d, 0x3b, 0x73, 0x27, 0x27, 0x27, 0xbe, 0xcf, 0x21, 0x29}, + []byte{0x3b, 0x3a, 0x29, 0xe9, 0x3b, 0x21, 0x3a, 0x5c, 0x5c, 0x3b, 0x3b, 0x05, 0x3a, 0x3a, 0x8c, 0x0d}, + []byte{0x6d, 0xc1, 0x8b, 0x8c, 0x34, 0x4c, 0xd7, 0x99, 0x27, 0x11, 0x8b, 0xbe, 0x84, 0xb7, 0xf3, 0x14}, + }, + { + // This test generates a result of (2^130-1) % (2^130-5). + []byte{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + }, + []byte{1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + []byte{4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + }, + { + // This test generates a result of (2^130-6) % (2^130-5). + []byte{ + 0xfa, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + }, + []byte{1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + []byte{0xfa, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, + }, + { + // This test generates a result of (2^130-5) % (2^130-5). + []byte{ + 0xfb, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + }, + []byte{1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + }, +} + +func testSum(t *testing.T, unaligned bool) { + var out [16]byte + var key [32]byte + + for i, v := range testData { + in := v.in + if unaligned { + in = unalignBytes(in) + } + copy(key[:], v.k) + Sum(&out, in, &key) + if !bytes.Equal(out[:], v.correct) { + t.Errorf("%d: expected %x, got %x", i, v.correct, out[:]) + } + } +} + +func TestBurnin(t *testing.T) { + // This test can be used to sanity-check significant changes. It can + // take about many minutes to run, even on fast machines. It's disabled + // by default. + if !*stressFlag { + t.Skip("skipping without -stress") + } + + var key [32]byte + var input [25]byte + var output [16]byte + + for i := range key { + key[i] = 1 + } + for i := range input { + input[i] = 2 + } + + for i := uint64(0); i < 1e10; i++ { + Sum(&output, input[:], &key) + copy(key[0:], output[:]) + copy(key[16:], output[:]) + copy(input[:], output[:]) + copy(input[16:], output[:]) + } + + const expected = "5e3b866aea0b636d240c83c428f84bfa" + if got := hex.EncodeToString(output[:]); got != expected { + t.Errorf("expected %s, got %s", expected, got) + } +} + +func TestSum(t *testing.T) { testSum(t, false) } +func TestSumUnaligned(t *testing.T) { testSum(t, true) } + +func benchmark(b *testing.B, size int, unaligned bool) { + var out [16]byte + var key [32]byte + in := make([]byte, size) + if unaligned { + in = unalignBytes(in) + } + b.SetBytes(int64(len(in))) + b.ResetTimer() + for i := 0; i < b.N; i++ { + Sum(&out, in, &key) + } +} + +func Benchmark64(b *testing.B) { benchmark(b, 64, false) } +func Benchmark1K(b *testing.B) { benchmark(b, 1024, false) } +func Benchmark64Unaligned(b *testing.B) { benchmark(b, 64, true) } +func Benchmark1KUnaligned(b *testing.B) { benchmark(b, 1024, true) } + +func unalignBytes(in []byte) []byte { + out := make([]byte, len(in)+1) + if uintptr(unsafe.Pointer(&out[0]))&(unsafe.Alignof(uint32(0))-1) == 0 { + out = out[1:] + } else { + out = out[:len(in)] + } + copy(out, in) + return out +} diff --git a/vendor/src/golang.org/x/crypto/poly1305/sum_amd64.go b/vendor/src/golang.org/x/crypto/poly1305/sum_amd64.go new file mode 100644 index 00000000..4dd72fe7 --- /dev/null +++ b/vendor/src/golang.org/x/crypto/poly1305/sum_amd64.go @@ -0,0 +1,22 @@ +// Copyright 2012 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. + +// +build amd64,!gccgo,!appengine + +package poly1305 + +// This function is implemented in sum_amd64.s +//go:noescape +func poly1305(out *[16]byte, m *byte, mlen uint64, key *[32]byte) + +// Sum generates an authenticator for m using a one-time key and puts the +// 16-byte result into out. Authenticating two different messages with the same +// key allows an attacker to forge messages at will. +func Sum(out *[16]byte, m []byte, key *[32]byte) { + var mPtr *byte + if len(m) > 0 { + mPtr = &m[0] + } + poly1305(out, mPtr, uint64(len(m)), key) +} diff --git a/vendor/src/golang.org/x/crypto/poly1305/sum_amd64.s b/vendor/src/golang.org/x/crypto/poly1305/sum_amd64.s new file mode 100644 index 00000000..2edae638 --- /dev/null +++ b/vendor/src/golang.org/x/crypto/poly1305/sum_amd64.s @@ -0,0 +1,125 @@ +// Copyright 2012 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. + +// +build amd64,!gccgo,!appengine + +#include "textflag.h" + +#define POLY1305_ADD(msg, h0, h1, h2) \ + ADDQ 0(msg), h0; \ + ADCQ 8(msg), h1; \ + ADCQ $1, h2; \ + LEAQ 16(msg), msg + +#define POLY1305_MUL(h0, h1, h2, r0, r1, t0, t1, t2, t3) \ + MOVQ r0, AX; \ + MULQ h0; \ + MOVQ AX, t0; \ + MOVQ DX, t1; \ + MOVQ r0, AX; \ + MULQ h1; \ + ADDQ AX, t1; \ + ADCQ $0, DX; \ + MOVQ r0, t2; \ + IMULQ h2, t2; \ + ADDQ DX, t2; \ + \ + MOVQ r1, AX; \ + MULQ h0; \ + ADDQ AX, t1; \ + ADCQ $0, DX; \ + MOVQ DX, h0; \ + MOVQ r1, t3; \ + IMULQ h2, t3; \ + MOVQ r1, AX; \ + MULQ h1; \ + ADDQ AX, t2; \ + ADCQ DX, t3; \ + ADDQ h0, t2; \ + ADCQ $0, t3; \ + \ + MOVQ t0, h0; \ + MOVQ t1, h1; \ + MOVQ t2, h2; \ + ANDQ $3, h2; \ + MOVQ t2, t0; \ + ANDQ $0xFFFFFFFFFFFFFFFC, t0; \ + ADDQ t0, h0; \ + ADCQ t3, h1; \ + ADCQ $0, h2; \ + SHRQ $2, t3, t2; \ + SHRQ $2, t3; \ + ADDQ t2, h0; \ + ADCQ t3, h1; \ + ADCQ $0, h2 + +DATA ·poly1305Mask<>+0x00(SB)/8, $0x0FFFFFFC0FFFFFFF +DATA ·poly1305Mask<>+0x08(SB)/8, $0x0FFFFFFC0FFFFFFC +GLOBL ·poly1305Mask<>(SB), RODATA, $16 + +// func poly1305(out *[16]byte, m *byte, mlen uint64, key *[32]key) +TEXT ·poly1305(SB), $0-32 + MOVQ out+0(FP), DI + MOVQ m+8(FP), SI + MOVQ mlen+16(FP), R15 + MOVQ key+24(FP), AX + + MOVQ 0(AX), R11 + MOVQ 8(AX), R12 + ANDQ ·poly1305Mask<>(SB), R11 // r0 + ANDQ ·poly1305Mask<>+8(SB), R12 // r1 + XORQ R8, R8 // h0 + XORQ R9, R9 // h1 + XORQ R10, R10 // h2 + + CMPQ R15, $16 + JB bytes_between_0_and_15 + +loop: + POLY1305_ADD(SI, R8, R9, R10) + +multiply: + POLY1305_MUL(R8, R9, R10, R11, R12, BX, CX, R13, R14) + SUBQ $16, R15 + CMPQ R15, $16 + JAE loop + +bytes_between_0_and_15: + TESTQ R15, R15 + JZ done + MOVQ $1, BX + XORQ CX, CX + XORQ R13, R13 + ADDQ R15, SI + +flush_buffer: + SHLQ $8, BX, CX + SHLQ $8, BX + MOVB -1(SI), R13 + XORQ R13, BX + DECQ SI + DECQ R15 + JNZ flush_buffer + + ADDQ BX, R8 + ADCQ CX, R9 + ADCQ $0, R10 + MOVQ $16, R15 + JMP multiply + +done: + MOVQ R8, AX + MOVQ R9, BX + SUBQ $0xFFFFFFFFFFFFFFFB, AX + SBBQ $0xFFFFFFFFFFFFFFFF, BX + SBBQ $3, R10 + CMOVQCS R8, AX + CMOVQCS R9, BX + MOVQ key+24(FP), R8 + ADDQ 16(R8), AX + ADCQ 24(R8), BX + + MOVQ AX, 0(DI) + MOVQ BX, 8(DI) + RET diff --git a/vendor/src/golang.org/x/crypto/poly1305/sum_arm.go b/vendor/src/golang.org/x/crypto/poly1305/sum_arm.go new file mode 100644 index 00000000..5dc321c2 --- /dev/null +++ b/vendor/src/golang.org/x/crypto/poly1305/sum_arm.go @@ -0,0 +1,22 @@ +// Copyright 2015 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. + +// +build arm,!gccgo,!appengine,!nacl + +package poly1305 + +// This function is implemented in sum_arm.s +//go:noescape +func poly1305_auth_armv6(out *[16]byte, m *byte, mlen uint32, key *[32]byte) + +// Sum generates an authenticator for m using a one-time key and puts the +// 16-byte result into out. Authenticating two different messages with the same +// key allows an attacker to forge messages at will. +func Sum(out *[16]byte, m []byte, key *[32]byte) { + var mPtr *byte + if len(m) > 0 { + mPtr = &m[0] + } + poly1305_auth_armv6(out, mPtr, uint32(len(m)), key) +} diff --git a/vendor/src/golang.org/x/crypto/poly1305/sum_arm.s b/vendor/src/golang.org/x/crypto/poly1305/sum_arm.s new file mode 100644 index 00000000..f70b4ac4 --- /dev/null +++ b/vendor/src/golang.org/x/crypto/poly1305/sum_arm.s @@ -0,0 +1,427 @@ +// Copyright 2015 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. + +// +build arm,!gccgo,!appengine,!nacl + +#include "textflag.h" + +// This code was translated into a form compatible with 5a from the public +// domain source by Andrew Moon: github.com/floodyberry/poly1305-opt/blob/master/app/extensions/poly1305. + +DATA ·poly1305_init_constants_armv6<>+0x00(SB)/4, $0x3ffffff +DATA ·poly1305_init_constants_armv6<>+0x04(SB)/4, $0x3ffff03 +DATA ·poly1305_init_constants_armv6<>+0x08(SB)/4, $0x3ffc0ff +DATA ·poly1305_init_constants_armv6<>+0x0c(SB)/4, $0x3f03fff +DATA ·poly1305_init_constants_armv6<>+0x10(SB)/4, $0x00fffff +GLOBL ·poly1305_init_constants_armv6<>(SB), 8, $20 + +// Warning: the linker may use R11 to synthesize certain instructions. Please +// take care and verify that no synthetic instructions use it. + +TEXT poly1305_init_ext_armv6<>(SB), NOSPLIT, $0 + // Needs 16 bytes of stack and 64 bytes of space pointed to by R0. (It + // might look like it's only 60 bytes of space but the final four bytes + // will be written by another function.) We need to skip over four + // bytes of stack because that's saving the value of 'g'. + ADD $4, R13, R8 + MOVM.IB [R4-R7], (R8) + MOVM.IA.W (R1), [R2-R5] + MOVW $·poly1305_init_constants_armv6<>(SB), R7 + MOVW R2, R8 + MOVW R2>>26, R9 + MOVW R3>>20, g + MOVW R4>>14, R11 + MOVW R5>>8, R12 + ORR R3<<6, R9, R9 + ORR R4<<12, g, g + ORR R5<<18, R11, R11 + MOVM.IA (R7), [R2-R6] + AND R8, R2, R2 + AND R9, R3, R3 + AND g, R4, R4 + AND R11, R5, R5 + AND R12, R6, R6 + MOVM.IA.W [R2-R6], (R0) + EOR R2, R2, R2 + EOR R3, R3, R3 + EOR R4, R4, R4 + EOR R5, R5, R5 + EOR R6, R6, R6 + MOVM.IA.W [R2-R6], (R0) + MOVM.IA.W (R1), [R2-R5] + MOVM.IA [R2-R6], (R0) + ADD $20, R13, R0 + MOVM.DA (R0), [R4-R7] + RET + +#define MOVW_UNALIGNED(Rsrc, Rdst, Rtmp, offset) \ + MOVBU (offset+0)(Rsrc), Rtmp; \ + MOVBU Rtmp, (offset+0)(Rdst); \ + MOVBU (offset+1)(Rsrc), Rtmp; \ + MOVBU Rtmp, (offset+1)(Rdst); \ + MOVBU (offset+2)(Rsrc), Rtmp; \ + MOVBU Rtmp, (offset+2)(Rdst); \ + MOVBU (offset+3)(Rsrc), Rtmp; \ + MOVBU Rtmp, (offset+3)(Rdst) + +TEXT poly1305_blocks_armv6<>(SB), NOSPLIT, $0 + // Needs 24 bytes of stack for saved registers and then 88 bytes of + // scratch space after that. We assume that 24 bytes at (R13) have + // already been used: four bytes for the link register saved in the + // prelude of poly1305_auth_armv6, four bytes for saving the value of g + // in that function and 16 bytes of scratch space used around + // poly1305_finish_ext_armv6_skip1. + ADD $24, R13, R12 + MOVM.IB [R4-R8, R14], (R12) + MOVW R0, 88(R13) + MOVW R1, 92(R13) + MOVW R2, 96(R13) + MOVW R1, R14 + MOVW R2, R12 + MOVW 56(R0), R8 + WORD $0xe1180008 // TST R8, R8 not working see issue 5921 + EOR R6, R6, R6 + MOVW.EQ $(1<<24), R6 + MOVW R6, 84(R13) + ADD $116, R13, g + MOVM.IA (R0), [R0-R9] + MOVM.IA [R0-R4], (g) + CMP $16, R12 + BLO poly1305_blocks_armv6_done + +poly1305_blocks_armv6_mainloop: + WORD $0xe31e0003 // TST R14, #3 not working see issue 5921 + BEQ poly1305_blocks_armv6_mainloop_aligned + ADD $100, R13, g + MOVW_UNALIGNED(R14, g, R0, 0) + MOVW_UNALIGNED(R14, g, R0, 4) + MOVW_UNALIGNED(R14, g, R0, 8) + MOVW_UNALIGNED(R14, g, R0, 12) + MOVM.IA (g), [R0-R3] + ADD $16, R14 + B poly1305_blocks_armv6_mainloop_loaded + +poly1305_blocks_armv6_mainloop_aligned: + MOVM.IA.W (R14), [R0-R3] + +poly1305_blocks_armv6_mainloop_loaded: + MOVW R0>>26, g + MOVW R1>>20, R11 + MOVW R2>>14, R12 + MOVW R14, 92(R13) + MOVW R3>>8, R4 + ORR R1<<6, g, g + ORR R2<<12, R11, R11 + ORR R3<<18, R12, R12 + BIC $0xfc000000, R0, R0 + BIC $0xfc000000, g, g + MOVW 84(R13), R3 + BIC $0xfc000000, R11, R11 + BIC $0xfc000000, R12, R12 + ADD R0, R5, R5 + ADD g, R6, R6 + ORR R3, R4, R4 + ADD R11, R7, R7 + ADD $116, R13, R14 + ADD R12, R8, R8 + ADD R4, R9, R9 + MOVM.IA (R14), [R0-R4] + MULLU R4, R5, (R11, g) + MULLU R3, R5, (R14, R12) + MULALU R3, R6, (R11, g) + MULALU R2, R6, (R14, R12) + MULALU R2, R7, (R11, g) + MULALU R1, R7, (R14, R12) + ADD R4<<2, R4, R4 + ADD R3<<2, R3, R3 + MULALU R1, R8, (R11, g) + MULALU R0, R8, (R14, R12) + MULALU R0, R9, (R11, g) + MULALU R4, R9, (R14, R12) + MOVW g, 76(R13) + MOVW R11, 80(R13) + MOVW R12, 68(R13) + MOVW R14, 72(R13) + MULLU R2, R5, (R11, g) + MULLU R1, R5, (R14, R12) + MULALU R1, R6, (R11, g) + MULALU R0, R6, (R14, R12) + MULALU R0, R7, (R11, g) + MULALU R4, R7, (R14, R12) + ADD R2<<2, R2, R2 + ADD R1<<2, R1, R1 + MULALU R4, R8, (R11, g) + MULALU R3, R8, (R14, R12) + MULALU R3, R9, (R11, g) + MULALU R2, R9, (R14, R12) + MOVW g, 60(R13) + MOVW R11, 64(R13) + MOVW R12, 52(R13) + MOVW R14, 56(R13) + MULLU R0, R5, (R11, g) + MULALU R4, R6, (R11, g) + MULALU R3, R7, (R11, g) + MULALU R2, R8, (R11, g) + MULALU R1, R9, (R11, g) + ADD $52, R13, R0 + MOVM.IA (R0), [R0-R7] + MOVW g>>26, R12 + MOVW R4>>26, R14 + ORR R11<<6, R12, R12 + ORR R5<<6, R14, R14 + BIC $0xfc000000, g, g + BIC $0xfc000000, R4, R4 + ADD.S R12, R0, R0 + ADC $0, R1, R1 + ADD.S R14, R6, R6 + ADC $0, R7, R7 + MOVW R0>>26, R12 + MOVW R6>>26, R14 + ORR R1<<6, R12, R12 + ORR R7<<6, R14, R14 + BIC $0xfc000000, R0, R0 + BIC $0xfc000000, R6, R6 + ADD R14<<2, R14, R14 + ADD.S R12, R2, R2 + ADC $0, R3, R3 + ADD R14, g, g + MOVW R2>>26, R12 + MOVW g>>26, R14 + ORR R3<<6, R12, R12 + BIC $0xfc000000, g, R5 + BIC $0xfc000000, R2, R7 + ADD R12, R4, R4 + ADD R14, R0, R0 + MOVW R4>>26, R12 + BIC $0xfc000000, R4, R8 + ADD R12, R6, R9 + MOVW 96(R13), R12 + MOVW 92(R13), R14 + MOVW R0, R6 + CMP $32, R12 + SUB $16, R12, R12 + MOVW R12, 96(R13) + BHS poly1305_blocks_armv6_mainloop + +poly1305_blocks_armv6_done: + MOVW 88(R13), R12 + MOVW R5, 20(R12) + MOVW R6, 24(R12) + MOVW R7, 28(R12) + MOVW R8, 32(R12) + MOVW R9, 36(R12) + ADD $48, R13, R0 + MOVM.DA (R0), [R4-R8, R14] + RET + +#define MOVHUP_UNALIGNED(Rsrc, Rdst, Rtmp) \ + MOVBU.P 1(Rsrc), Rtmp; \ + MOVBU.P Rtmp, 1(Rdst); \ + MOVBU.P 1(Rsrc), Rtmp; \ + MOVBU.P Rtmp, 1(Rdst) + +#define MOVWP_UNALIGNED(Rsrc, Rdst, Rtmp) \ + MOVHUP_UNALIGNED(Rsrc, Rdst, Rtmp); \ + MOVHUP_UNALIGNED(Rsrc, Rdst, Rtmp) + +// func poly1305_auth_armv6(out *[16]byte, m *byte, mlen uint32, key *[32]key) +TEXT ·poly1305_auth_armv6(SB), $196-16 + // The value 196, just above, is the sum of 64 (the size of the context + // structure) and 132 (the amount of stack needed). + // + // At this point, the stack pointer (R13) has been moved down. It + // points to the saved link register and there's 196 bytes of free + // space above it. + // + // The stack for this function looks like: + // + // +--------------------- + // | + // | 64 bytes of context structure + // | + // +--------------------- + // | + // | 112 bytes for poly1305_blocks_armv6 + // | + // +--------------------- + // | 16 bytes of final block, constructed at + // | poly1305_finish_ext_armv6_skip8 + // +--------------------- + // | four bytes of saved 'g' + // +--------------------- + // | lr, saved by prelude <- R13 points here + // +--------------------- + MOVW g, 4(R13) + + MOVW out+0(FP), R4 + MOVW m+4(FP), R5 + MOVW mlen+8(FP), R6 + MOVW key+12(FP), R7 + + ADD $136, R13, R0 // 136 = 4 + 4 + 16 + 112 + MOVW R7, R1 + + // poly1305_init_ext_armv6 will write to the stack from R13+4, but + // that's ok because none of the other values have been written yet. + BL poly1305_init_ext_armv6<>(SB) + BIC.S $15, R6, R2 + BEQ poly1305_auth_armv6_noblocks + ADD $136, R13, R0 + MOVW R5, R1 + ADD R2, R5, R5 + SUB R2, R6, R6 + BL poly1305_blocks_armv6<>(SB) + +poly1305_auth_armv6_noblocks: + ADD $136, R13, R0 + MOVW R5, R1 + MOVW R6, R2 + MOVW R4, R3 + + MOVW R0, R5 + MOVW R1, R6 + MOVW R2, R7 + MOVW R3, R8 + AND.S R2, R2, R2 + BEQ poly1305_finish_ext_armv6_noremaining + EOR R0, R0 + ADD $8, R13, R9 // 8 = offset to 16 byte scratch space + MOVW R0, (R9) + MOVW R0, 4(R9) + MOVW R0, 8(R9) + MOVW R0, 12(R9) + WORD $0xe3110003 // TST R1, #3 not working see issue 5921 + BEQ poly1305_finish_ext_armv6_aligned + WORD $0xe3120008 // TST R2, #8 not working see issue 5921 + BEQ poly1305_finish_ext_armv6_skip8 + MOVWP_UNALIGNED(R1, R9, g) + MOVWP_UNALIGNED(R1, R9, g) + +poly1305_finish_ext_armv6_skip8: + WORD $0xe3120004 // TST $4, R2 not working see issue 5921 + BEQ poly1305_finish_ext_armv6_skip4 + MOVWP_UNALIGNED(R1, R9, g) + +poly1305_finish_ext_armv6_skip4: + WORD $0xe3120002 // TST $2, R2 not working see issue 5921 + BEQ poly1305_finish_ext_armv6_skip2 + MOVHUP_UNALIGNED(R1, R9, g) + B poly1305_finish_ext_armv6_skip2 + +poly1305_finish_ext_armv6_aligned: + WORD $0xe3120008 // TST R2, #8 not working see issue 5921 + BEQ poly1305_finish_ext_armv6_skip8_aligned + MOVM.IA.W (R1), [g-R11] + MOVM.IA.W [g-R11], (R9) + +poly1305_finish_ext_armv6_skip8_aligned: + WORD $0xe3120004 // TST $4, R2 not working see issue 5921 + BEQ poly1305_finish_ext_armv6_skip4_aligned + MOVW.P 4(R1), g + MOVW.P g, 4(R9) + +poly1305_finish_ext_armv6_skip4_aligned: + WORD $0xe3120002 // TST $2, R2 not working see issue 5921 + BEQ poly1305_finish_ext_armv6_skip2 + MOVHU.P 2(R1), g + MOVH.P g, 2(R9) + +poly1305_finish_ext_armv6_skip2: + WORD $0xe3120001 // TST $1, R2 not working see issue 5921 + BEQ poly1305_finish_ext_armv6_skip1 + MOVBU.P 1(R1), g + MOVBU.P g, 1(R9) + +poly1305_finish_ext_armv6_skip1: + MOVW $1, R11 + MOVBU R11, 0(R9) + MOVW R11, 56(R5) + MOVW R5, R0 + ADD $8, R13, R1 + MOVW $16, R2 + BL poly1305_blocks_armv6<>(SB) + +poly1305_finish_ext_armv6_noremaining: + MOVW 20(R5), R0 + MOVW 24(R5), R1 + MOVW 28(R5), R2 + MOVW 32(R5), R3 + MOVW 36(R5), R4 + MOVW R4>>26, R12 + BIC $0xfc000000, R4, R4 + ADD R12<<2, R12, R12 + ADD R12, R0, R0 + MOVW R0>>26, R12 + BIC $0xfc000000, R0, R0 + ADD R12, R1, R1 + MOVW R1>>26, R12 + BIC $0xfc000000, R1, R1 + ADD R12, R2, R2 + MOVW R2>>26, R12 + BIC $0xfc000000, R2, R2 + ADD R12, R3, R3 + MOVW R3>>26, R12 + BIC $0xfc000000, R3, R3 + ADD R12, R4, R4 + ADD $5, R0, R6 + MOVW R6>>26, R12 + BIC $0xfc000000, R6, R6 + ADD R12, R1, R7 + MOVW R7>>26, R12 + BIC $0xfc000000, R7, R7 + ADD R12, R2, g + MOVW g>>26, R12 + BIC $0xfc000000, g, g + ADD R12, R3, R11 + MOVW $-(1<<26), R12 + ADD R11>>26, R12, R12 + BIC $0xfc000000, R11, R11 + ADD R12, R4, R9 + MOVW R9>>31, R12 + SUB $1, R12 + AND R12, R6, R6 + AND R12, R7, R7 + AND R12, g, g + AND R12, R11, R11 + AND R12, R9, R9 + MVN R12, R12 + AND R12, R0, R0 + AND R12, R1, R1 + AND R12, R2, R2 + AND R12, R3, R3 + AND R12, R4, R4 + ORR R6, R0, R0 + ORR R7, R1, R1 + ORR g, R2, R2 + ORR R11, R3, R3 + ORR R9, R4, R4 + ORR R1<<26, R0, R0 + MOVW R1>>6, R1 + ORR R2<<20, R1, R1 + MOVW R2>>12, R2 + ORR R3<<14, R2, R2 + MOVW R3>>18, R3 + ORR R4<<8, R3, R3 + MOVW 40(R5), R6 + MOVW 44(R5), R7 + MOVW 48(R5), g + MOVW 52(R5), R11 + ADD.S R6, R0, R0 + ADC.S R7, R1, R1 + ADC.S g, R2, R2 + ADC.S R11, R3, R3 + MOVM.IA [R0-R3], (R8) + MOVW R5, R12 + EOR R0, R0, R0 + EOR R1, R1, R1 + EOR R2, R2, R2 + EOR R3, R3, R3 + EOR R4, R4, R4 + EOR R5, R5, R5 + EOR R6, R6, R6 + EOR R7, R7, R7 + MOVM.IA.W [R0-R7], (R12) + MOVM.IA [R0-R7], (R12) + MOVW 4(R13), g + RET diff --git a/vendor/src/golang.org/x/crypto/poly1305/sum_ref.go b/vendor/src/golang.org/x/crypto/poly1305/sum_ref.go new file mode 100644 index 00000000..b2805a5c --- /dev/null +++ b/vendor/src/golang.org/x/crypto/poly1305/sum_ref.go @@ -0,0 +1,141 @@ +// Copyright 2012 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. + +// +build !amd64,!arm gccgo appengine nacl + +package poly1305 + +import "encoding/binary" + +// Sum generates an authenticator for msg using a one-time key and puts the +// 16-byte result into out. Authenticating two different messages with the same +// key allows an attacker to forge messages at will. +func Sum(out *[TagSize]byte, msg []byte, key *[32]byte) { + var ( + h0, h1, h2, h3, h4 uint32 // the hash accumulators + r0, r1, r2, r3, r4 uint64 // the r part of the key + ) + + r0 = uint64(binary.LittleEndian.Uint32(key[0:]) & 0x3ffffff) + r1 = uint64((binary.LittleEndian.Uint32(key[3:]) >> 2) & 0x3ffff03) + r2 = uint64((binary.LittleEndian.Uint32(key[6:]) >> 4) & 0x3ffc0ff) + r3 = uint64((binary.LittleEndian.Uint32(key[9:]) >> 6) & 0x3f03fff) + r4 = uint64((binary.LittleEndian.Uint32(key[12:]) >> 8) & 0x00fffff) + + R1, R2, R3, R4 := r1*5, r2*5, r3*5, r4*5 + + for len(msg) >= TagSize { + // h += msg + h0 += binary.LittleEndian.Uint32(msg[0:]) & 0x3ffffff + h1 += (binary.LittleEndian.Uint32(msg[3:]) >> 2) & 0x3ffffff + h2 += (binary.LittleEndian.Uint32(msg[6:]) >> 4) & 0x3ffffff + h3 += (binary.LittleEndian.Uint32(msg[9:]) >> 6) & 0x3ffffff + h4 += (binary.LittleEndian.Uint32(msg[12:]) >> 8) | (1 << 24) + + // h *= r + d0 := (uint64(h0) * r0) + (uint64(h1) * R4) + (uint64(h2) * R3) + (uint64(h3) * R2) + (uint64(h4) * R1) + d1 := (d0 >> 26) + (uint64(h0) * r1) + (uint64(h1) * r0) + (uint64(h2) * R4) + (uint64(h3) * R3) + (uint64(h4) * R2) + d2 := (d1 >> 26) + (uint64(h0) * r2) + (uint64(h1) * r1) + (uint64(h2) * r0) + (uint64(h3) * R4) + (uint64(h4) * R3) + d3 := (d2 >> 26) + (uint64(h0) * r3) + (uint64(h1) * r2) + (uint64(h2) * r1) + (uint64(h3) * r0) + (uint64(h4) * R4) + d4 := (d3 >> 26) + (uint64(h0) * r4) + (uint64(h1) * r3) + (uint64(h2) * r2) + (uint64(h3) * r1) + (uint64(h4) * r0) + + // h %= p + h0 = uint32(d0) & 0x3ffffff + h1 = uint32(d1) & 0x3ffffff + h2 = uint32(d2) & 0x3ffffff + h3 = uint32(d3) & 0x3ffffff + h4 = uint32(d4) & 0x3ffffff + + h0 += uint32(d4>>26) * 5 + h1 += h0 >> 26 + h0 = h0 & 0x3ffffff + + msg = msg[TagSize:] + } + + if len(msg) > 0 { + var block [TagSize]byte + off := copy(block[:], msg) + block[off] = 0x01 + + // h += msg + h0 += binary.LittleEndian.Uint32(block[0:]) & 0x3ffffff + h1 += (binary.LittleEndian.Uint32(block[3:]) >> 2) & 0x3ffffff + h2 += (binary.LittleEndian.Uint32(block[6:]) >> 4) & 0x3ffffff + h3 += (binary.LittleEndian.Uint32(block[9:]) >> 6) & 0x3ffffff + h4 += (binary.LittleEndian.Uint32(block[12:]) >> 8) + + // h *= r + d0 := (uint64(h0) * r0) + (uint64(h1) * R4) + (uint64(h2) * R3) + (uint64(h3) * R2) + (uint64(h4) * R1) + d1 := (d0 >> 26) + (uint64(h0) * r1) + (uint64(h1) * r0) + (uint64(h2) * R4) + (uint64(h3) * R3) + (uint64(h4) * R2) + d2 := (d1 >> 26) + (uint64(h0) * r2) + (uint64(h1) * r1) + (uint64(h2) * r0) + (uint64(h3) * R4) + (uint64(h4) * R3) + d3 := (d2 >> 26) + (uint64(h0) * r3) + (uint64(h1) * r2) + (uint64(h2) * r1) + (uint64(h3) * r0) + (uint64(h4) * R4) + d4 := (d3 >> 26) + (uint64(h0) * r4) + (uint64(h1) * r3) + (uint64(h2) * r2) + (uint64(h3) * r1) + (uint64(h4) * r0) + + // h %= p + h0 = uint32(d0) & 0x3ffffff + h1 = uint32(d1) & 0x3ffffff + h2 = uint32(d2) & 0x3ffffff + h3 = uint32(d3) & 0x3ffffff + h4 = uint32(d4) & 0x3ffffff + + h0 += uint32(d4>>26) * 5 + h1 += h0 >> 26 + h0 = h0 & 0x3ffffff + } + + // h %= p reduction + h2 += h1 >> 26 + h1 &= 0x3ffffff + h3 += h2 >> 26 + h2 &= 0x3ffffff + h4 += h3 >> 26 + h3 &= 0x3ffffff + h0 += 5 * (h4 >> 26) + h4 &= 0x3ffffff + h1 += h0 >> 26 + h0 &= 0x3ffffff + + // h - p + t0 := h0 + 5 + t1 := h1 + (t0 >> 26) + t2 := h2 + (t1 >> 26) + t3 := h3 + (t2 >> 26) + t4 := h4 + (t3 >> 26) - (1 << 26) + t0 &= 0x3ffffff + t1 &= 0x3ffffff + t2 &= 0x3ffffff + t3 &= 0x3ffffff + + // select h if h < p else h - p + t_mask := (t4 >> 31) - 1 + h_mask := ^t_mask + h0 = (h0 & h_mask) | (t0 & t_mask) + h1 = (h1 & h_mask) | (t1 & t_mask) + h2 = (h2 & h_mask) | (t2 & t_mask) + h3 = (h3 & h_mask) | (t3 & t_mask) + h4 = (h4 & h_mask) | (t4 & t_mask) + + // h %= 2^128 + h0 |= h1 << 26 + h1 = ((h1 >> 6) | (h2 << 20)) + h2 = ((h2 >> 12) | (h3 << 14)) + h3 = ((h3 >> 18) | (h4 << 8)) + + // s: the s part of the key + // tag = (h + s) % (2^128) + t := uint64(h0) + uint64(binary.LittleEndian.Uint32(key[16:])) + h0 = uint32(t) + t = uint64(h1) + uint64(binary.LittleEndian.Uint32(key[20:])) + (t >> 32) + h1 = uint32(t) + t = uint64(h2) + uint64(binary.LittleEndian.Uint32(key[24:])) + (t >> 32) + h2 = uint32(t) + t = uint64(h3) + uint64(binary.LittleEndian.Uint32(key[28:])) + (t >> 32) + h3 = uint32(t) + + binary.LittleEndian.PutUint32(out[0:], h0) + binary.LittleEndian.PutUint32(out[4:], h1) + binary.LittleEndian.PutUint32(out[8:], h2) + binary.LittleEndian.PutUint32(out[12:], h3) +} diff --git a/vendor/src/golang.org/x/crypto/salsa20/salsa/hsalsa20.go b/vendor/src/golang.org/x/crypto/salsa20/salsa/hsalsa20.go new file mode 100644 index 00000000..4c96147c --- /dev/null +++ b/vendor/src/golang.org/x/crypto/salsa20/salsa/hsalsa20.go @@ -0,0 +1,144 @@ +// Copyright 2012 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 salsa provides low-level access to functions in the Salsa family. +package salsa // import "golang.org/x/crypto/salsa20/salsa" + +// Sigma is the Salsa20 constant for 256-bit keys. +var Sigma = [16]byte{'e', 'x', 'p', 'a', 'n', 'd', ' ', '3', '2', '-', 'b', 'y', 't', 'e', ' ', 'k'} + +// HSalsa20 applies the HSalsa20 core function to a 16-byte input in, 32-byte +// key k, and 16-byte constant c, and puts the result into the 32-byte array +// out. +func HSalsa20(out *[32]byte, in *[16]byte, k *[32]byte, c *[16]byte) { + x0 := uint32(c[0]) | uint32(c[1])<<8 | uint32(c[2])<<16 | uint32(c[3])<<24 + x1 := uint32(k[0]) | uint32(k[1])<<8 | uint32(k[2])<<16 | uint32(k[3])<<24 + x2 := uint32(k[4]) | uint32(k[5])<<8 | uint32(k[6])<<16 | uint32(k[7])<<24 + x3 := uint32(k[8]) | uint32(k[9])<<8 | uint32(k[10])<<16 | uint32(k[11])<<24 + x4 := uint32(k[12]) | uint32(k[13])<<8 | uint32(k[14])<<16 | uint32(k[15])<<24 + x5 := uint32(c[4]) | uint32(c[5])<<8 | uint32(c[6])<<16 | uint32(c[7])<<24 + x6 := uint32(in[0]) | uint32(in[1])<<8 | uint32(in[2])<<16 | uint32(in[3])<<24 + x7 := uint32(in[4]) | uint32(in[5])<<8 | uint32(in[6])<<16 | uint32(in[7])<<24 + x8 := uint32(in[8]) | uint32(in[9])<<8 | uint32(in[10])<<16 | uint32(in[11])<<24 + x9 := uint32(in[12]) | uint32(in[13])<<8 | uint32(in[14])<<16 | uint32(in[15])<<24 + x10 := uint32(c[8]) | uint32(c[9])<<8 | uint32(c[10])<<16 | uint32(c[11])<<24 + x11 := uint32(k[16]) | uint32(k[17])<<8 | uint32(k[18])<<16 | uint32(k[19])<<24 + x12 := uint32(k[20]) | uint32(k[21])<<8 | uint32(k[22])<<16 | uint32(k[23])<<24 + x13 := uint32(k[24]) | uint32(k[25])<<8 | uint32(k[26])<<16 | uint32(k[27])<<24 + x14 := uint32(k[28]) | uint32(k[29])<<8 | uint32(k[30])<<16 | uint32(k[31])<<24 + x15 := uint32(c[12]) | uint32(c[13])<<8 | uint32(c[14])<<16 | uint32(c[15])<<24 + + for i := 0; i < 20; i += 2 { + u := x0 + x12 + x4 ^= u<<7 | u>>(32-7) + u = x4 + x0 + x8 ^= u<<9 | u>>(32-9) + u = x8 + x4 + x12 ^= u<<13 | u>>(32-13) + u = x12 + x8 + x0 ^= u<<18 | u>>(32-18) + + u = x5 + x1 + x9 ^= u<<7 | u>>(32-7) + u = x9 + x5 + x13 ^= u<<9 | u>>(32-9) + u = x13 + x9 + x1 ^= u<<13 | u>>(32-13) + u = x1 + x13 + x5 ^= u<<18 | u>>(32-18) + + u = x10 + x6 + x14 ^= u<<7 | u>>(32-7) + u = x14 + x10 + x2 ^= u<<9 | u>>(32-9) + u = x2 + x14 + x6 ^= u<<13 | u>>(32-13) + u = x6 + x2 + x10 ^= u<<18 | u>>(32-18) + + u = x15 + x11 + x3 ^= u<<7 | u>>(32-7) + u = x3 + x15 + x7 ^= u<<9 | u>>(32-9) + u = x7 + x3 + x11 ^= u<<13 | u>>(32-13) + u = x11 + x7 + x15 ^= u<<18 | u>>(32-18) + + u = x0 + x3 + x1 ^= u<<7 | u>>(32-7) + u = x1 + x0 + x2 ^= u<<9 | u>>(32-9) + u = x2 + x1 + x3 ^= u<<13 | u>>(32-13) + u = x3 + x2 + x0 ^= u<<18 | u>>(32-18) + + u = x5 + x4 + x6 ^= u<<7 | u>>(32-7) + u = x6 + x5 + x7 ^= u<<9 | u>>(32-9) + u = x7 + x6 + x4 ^= u<<13 | u>>(32-13) + u = x4 + x7 + x5 ^= u<<18 | u>>(32-18) + + u = x10 + x9 + x11 ^= u<<7 | u>>(32-7) + u = x11 + x10 + x8 ^= u<<9 | u>>(32-9) + u = x8 + x11 + x9 ^= u<<13 | u>>(32-13) + u = x9 + x8 + x10 ^= u<<18 | u>>(32-18) + + u = x15 + x14 + x12 ^= u<<7 | u>>(32-7) + u = x12 + x15 + x13 ^= u<<9 | u>>(32-9) + u = x13 + x12 + x14 ^= u<<13 | u>>(32-13) + u = x14 + x13 + x15 ^= u<<18 | u>>(32-18) + } + out[0] = byte(x0) + out[1] = byte(x0 >> 8) + out[2] = byte(x0 >> 16) + out[3] = byte(x0 >> 24) + + out[4] = byte(x5) + out[5] = byte(x5 >> 8) + out[6] = byte(x5 >> 16) + out[7] = byte(x5 >> 24) + + out[8] = byte(x10) + out[9] = byte(x10 >> 8) + out[10] = byte(x10 >> 16) + out[11] = byte(x10 >> 24) + + out[12] = byte(x15) + out[13] = byte(x15 >> 8) + out[14] = byte(x15 >> 16) + out[15] = byte(x15 >> 24) + + out[16] = byte(x6) + out[17] = byte(x6 >> 8) + out[18] = byte(x6 >> 16) + out[19] = byte(x6 >> 24) + + out[20] = byte(x7) + out[21] = byte(x7 >> 8) + out[22] = byte(x7 >> 16) + out[23] = byte(x7 >> 24) + + out[24] = byte(x8) + out[25] = byte(x8 >> 8) + out[26] = byte(x8 >> 16) + out[27] = byte(x8 >> 24) + + out[28] = byte(x9) + out[29] = byte(x9 >> 8) + out[30] = byte(x9 >> 16) + out[31] = byte(x9 >> 24) +} diff --git a/vendor/src/golang.org/x/crypto/salsa20/salsa/salsa2020_amd64.s b/vendor/src/golang.org/x/crypto/salsa20/salsa/salsa2020_amd64.s new file mode 100644 index 00000000..22afbdca --- /dev/null +++ b/vendor/src/golang.org/x/crypto/salsa20/salsa/salsa2020_amd64.s @@ -0,0 +1,889 @@ +// Copyright 2012 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. + +// +build amd64,!appengine,!gccgo + +// This code was translated into a form compatible with 6a from the public +// domain sources in SUPERCOP: https://bench.cr.yp.to/supercop.html + +// func salsa2020XORKeyStream(out, in *byte, n uint64, nonce, key *byte) +// This needs up to 64 bytes at 360(SP); hence the non-obvious frame size. +TEXT ·salsa2020XORKeyStream(SB),0,$456-40 // frame = 424 + 32 byte alignment + MOVQ out+0(FP),DI + MOVQ in+8(FP),SI + MOVQ n+16(FP),DX + MOVQ nonce+24(FP),CX + MOVQ key+32(FP),R8 + + MOVQ SP,R12 + MOVQ SP,R9 + ADDQ $31, R9 + ANDQ $~31, R9 + MOVQ R9, SP + + MOVQ DX,R9 + MOVQ CX,DX + MOVQ R8,R10 + CMPQ R9,$0 + JBE DONE + START: + MOVL 20(R10),CX + MOVL 0(R10),R8 + MOVL 0(DX),AX + MOVL 16(R10),R11 + MOVL CX,0(SP) + MOVL R8, 4 (SP) + MOVL AX, 8 (SP) + MOVL R11, 12 (SP) + MOVL 8(DX),CX + MOVL 24(R10),R8 + MOVL 4(R10),AX + MOVL 4(DX),R11 + MOVL CX,16(SP) + MOVL R8, 20 (SP) + MOVL AX, 24 (SP) + MOVL R11, 28 (SP) + MOVL 12(DX),CX + MOVL 12(R10),DX + MOVL 28(R10),R8 + MOVL 8(R10),AX + MOVL DX,32(SP) + MOVL CX, 36 (SP) + MOVL R8, 40 (SP) + MOVL AX, 44 (SP) + MOVQ $1634760805,DX + MOVQ $857760878,CX + MOVQ $2036477234,R8 + MOVQ $1797285236,AX + MOVL DX,48(SP) + MOVL CX, 52 (SP) + MOVL R8, 56 (SP) + MOVL AX, 60 (SP) + CMPQ R9,$256 + JB BYTESBETWEEN1AND255 + MOVOA 48(SP),X0 + PSHUFL $0X55,X0,X1 + PSHUFL $0XAA,X0,X2 + PSHUFL $0XFF,X0,X3 + PSHUFL $0X00,X0,X0 + MOVOA X1,64(SP) + MOVOA X2,80(SP) + MOVOA X3,96(SP) + MOVOA X0,112(SP) + MOVOA 0(SP),X0 + PSHUFL $0XAA,X0,X1 + PSHUFL $0XFF,X0,X2 + PSHUFL $0X00,X0,X3 + PSHUFL $0X55,X0,X0 + MOVOA X1,128(SP) + MOVOA X2,144(SP) + MOVOA X3,160(SP) + MOVOA X0,176(SP) + MOVOA 16(SP),X0 + PSHUFL $0XFF,X0,X1 + PSHUFL $0X55,X0,X2 + PSHUFL $0XAA,X0,X0 + MOVOA X1,192(SP) + MOVOA X2,208(SP) + MOVOA X0,224(SP) + MOVOA 32(SP),X0 + PSHUFL $0X00,X0,X1 + PSHUFL $0XAA,X0,X2 + PSHUFL $0XFF,X0,X0 + MOVOA X1,240(SP) + MOVOA X2,256(SP) + MOVOA X0,272(SP) + BYTESATLEAST256: + MOVL 16(SP),DX + MOVL 36 (SP),CX + MOVL DX,288(SP) + MOVL CX,304(SP) + ADDQ $1,DX + SHLQ $32,CX + ADDQ CX,DX + MOVQ DX,CX + SHRQ $32,CX + MOVL DX, 292 (SP) + MOVL CX, 308 (SP) + ADDQ $1,DX + SHLQ $32,CX + ADDQ CX,DX + MOVQ DX,CX + SHRQ $32,CX + MOVL DX, 296 (SP) + MOVL CX, 312 (SP) + ADDQ $1,DX + SHLQ $32,CX + ADDQ CX,DX + MOVQ DX,CX + SHRQ $32,CX + MOVL DX, 300 (SP) + MOVL CX, 316 (SP) + ADDQ $1,DX + SHLQ $32,CX + ADDQ CX,DX + MOVQ DX,CX + SHRQ $32,CX + MOVL DX,16(SP) + MOVL CX, 36 (SP) + MOVQ R9,352(SP) + MOVQ $20,DX + MOVOA 64(SP),X0 + MOVOA 80(SP),X1 + MOVOA 96(SP),X2 + MOVOA 256(SP),X3 + MOVOA 272(SP),X4 + MOVOA 128(SP),X5 + MOVOA 144(SP),X6 + MOVOA 176(SP),X7 + MOVOA 192(SP),X8 + MOVOA 208(SP),X9 + MOVOA 224(SP),X10 + MOVOA 304(SP),X11 + MOVOA 112(SP),X12 + MOVOA 160(SP),X13 + MOVOA 240(SP),X14 + MOVOA 288(SP),X15 + MAINLOOP1: + MOVOA X1,320(SP) + MOVOA X2,336(SP) + MOVOA X13,X1 + PADDL X12,X1 + MOVOA X1,X2 + PSLLL $7,X1 + PXOR X1,X14 + PSRLL $25,X2 + PXOR X2,X14 + MOVOA X7,X1 + PADDL X0,X1 + MOVOA X1,X2 + PSLLL $7,X1 + PXOR X1,X11 + PSRLL $25,X2 + PXOR X2,X11 + MOVOA X12,X1 + PADDL X14,X1 + MOVOA X1,X2 + PSLLL $9,X1 + PXOR X1,X15 + PSRLL $23,X2 + PXOR X2,X15 + MOVOA X0,X1 + PADDL X11,X1 + MOVOA X1,X2 + PSLLL $9,X1 + PXOR X1,X9 + PSRLL $23,X2 + PXOR X2,X9 + MOVOA X14,X1 + PADDL X15,X1 + MOVOA X1,X2 + PSLLL $13,X1 + PXOR X1,X13 + PSRLL $19,X2 + PXOR X2,X13 + MOVOA X11,X1 + PADDL X9,X1 + MOVOA X1,X2 + PSLLL $13,X1 + PXOR X1,X7 + PSRLL $19,X2 + PXOR X2,X7 + MOVOA X15,X1 + PADDL X13,X1 + MOVOA X1,X2 + PSLLL $18,X1 + PXOR X1,X12 + PSRLL $14,X2 + PXOR X2,X12 + MOVOA 320(SP),X1 + MOVOA X12,320(SP) + MOVOA X9,X2 + PADDL X7,X2 + MOVOA X2,X12 + PSLLL $18,X2 + PXOR X2,X0 + PSRLL $14,X12 + PXOR X12,X0 + MOVOA X5,X2 + PADDL X1,X2 + MOVOA X2,X12 + PSLLL $7,X2 + PXOR X2,X3 + PSRLL $25,X12 + PXOR X12,X3 + MOVOA 336(SP),X2 + MOVOA X0,336(SP) + MOVOA X6,X0 + PADDL X2,X0 + MOVOA X0,X12 + PSLLL $7,X0 + PXOR X0,X4 + PSRLL $25,X12 + PXOR X12,X4 + MOVOA X1,X0 + PADDL X3,X0 + MOVOA X0,X12 + PSLLL $9,X0 + PXOR X0,X10 + PSRLL $23,X12 + PXOR X12,X10 + MOVOA X2,X0 + PADDL X4,X0 + MOVOA X0,X12 + PSLLL $9,X0 + PXOR X0,X8 + PSRLL $23,X12 + PXOR X12,X8 + MOVOA X3,X0 + PADDL X10,X0 + MOVOA X0,X12 + PSLLL $13,X0 + PXOR X0,X5 + PSRLL $19,X12 + PXOR X12,X5 + MOVOA X4,X0 + PADDL X8,X0 + MOVOA X0,X12 + PSLLL $13,X0 + PXOR X0,X6 + PSRLL $19,X12 + PXOR X12,X6 + MOVOA X10,X0 + PADDL X5,X0 + MOVOA X0,X12 + PSLLL $18,X0 + PXOR X0,X1 + PSRLL $14,X12 + PXOR X12,X1 + MOVOA 320(SP),X0 + MOVOA X1,320(SP) + MOVOA X4,X1 + PADDL X0,X1 + MOVOA X1,X12 + PSLLL $7,X1 + PXOR X1,X7 + PSRLL $25,X12 + PXOR X12,X7 + MOVOA X8,X1 + PADDL X6,X1 + MOVOA X1,X12 + PSLLL $18,X1 + PXOR X1,X2 + PSRLL $14,X12 + PXOR X12,X2 + MOVOA 336(SP),X12 + MOVOA X2,336(SP) + MOVOA X14,X1 + PADDL X12,X1 + MOVOA X1,X2 + PSLLL $7,X1 + PXOR X1,X5 + PSRLL $25,X2 + PXOR X2,X5 + MOVOA X0,X1 + PADDL X7,X1 + MOVOA X1,X2 + PSLLL $9,X1 + PXOR X1,X10 + PSRLL $23,X2 + PXOR X2,X10 + MOVOA X12,X1 + PADDL X5,X1 + MOVOA X1,X2 + PSLLL $9,X1 + PXOR X1,X8 + PSRLL $23,X2 + PXOR X2,X8 + MOVOA X7,X1 + PADDL X10,X1 + MOVOA X1,X2 + PSLLL $13,X1 + PXOR X1,X4 + PSRLL $19,X2 + PXOR X2,X4 + MOVOA X5,X1 + PADDL X8,X1 + MOVOA X1,X2 + PSLLL $13,X1 + PXOR X1,X14 + PSRLL $19,X2 + PXOR X2,X14 + MOVOA X10,X1 + PADDL X4,X1 + MOVOA X1,X2 + PSLLL $18,X1 + PXOR X1,X0 + PSRLL $14,X2 + PXOR X2,X0 + MOVOA 320(SP),X1 + MOVOA X0,320(SP) + MOVOA X8,X0 + PADDL X14,X0 + MOVOA X0,X2 + PSLLL $18,X0 + PXOR X0,X12 + PSRLL $14,X2 + PXOR X2,X12 + MOVOA X11,X0 + PADDL X1,X0 + MOVOA X0,X2 + PSLLL $7,X0 + PXOR X0,X6 + PSRLL $25,X2 + PXOR X2,X6 + MOVOA 336(SP),X2 + MOVOA X12,336(SP) + MOVOA X3,X0 + PADDL X2,X0 + MOVOA X0,X12 + PSLLL $7,X0 + PXOR X0,X13 + PSRLL $25,X12 + PXOR X12,X13 + MOVOA X1,X0 + PADDL X6,X0 + MOVOA X0,X12 + PSLLL $9,X0 + PXOR X0,X15 + PSRLL $23,X12 + PXOR X12,X15 + MOVOA X2,X0 + PADDL X13,X0 + MOVOA X0,X12 + PSLLL $9,X0 + PXOR X0,X9 + PSRLL $23,X12 + PXOR X12,X9 + MOVOA X6,X0 + PADDL X15,X0 + MOVOA X0,X12 + PSLLL $13,X0 + PXOR X0,X11 + PSRLL $19,X12 + PXOR X12,X11 + MOVOA X13,X0 + PADDL X9,X0 + MOVOA X0,X12 + PSLLL $13,X0 + PXOR X0,X3 + PSRLL $19,X12 + PXOR X12,X3 + MOVOA X15,X0 + PADDL X11,X0 + MOVOA X0,X12 + PSLLL $18,X0 + PXOR X0,X1 + PSRLL $14,X12 + PXOR X12,X1 + MOVOA X9,X0 + PADDL X3,X0 + MOVOA X0,X12 + PSLLL $18,X0 + PXOR X0,X2 + PSRLL $14,X12 + PXOR X12,X2 + MOVOA 320(SP),X12 + MOVOA 336(SP),X0 + SUBQ $2,DX + JA MAINLOOP1 + PADDL 112(SP),X12 + PADDL 176(SP),X7 + PADDL 224(SP),X10 + PADDL 272(SP),X4 + MOVD X12,DX + MOVD X7,CX + MOVD X10,R8 + MOVD X4,R9 + PSHUFL $0X39,X12,X12 + PSHUFL $0X39,X7,X7 + PSHUFL $0X39,X10,X10 + PSHUFL $0X39,X4,X4 + XORL 0(SI),DX + XORL 4(SI),CX + XORL 8(SI),R8 + XORL 12(SI),R9 + MOVL DX,0(DI) + MOVL CX,4(DI) + MOVL R8,8(DI) + MOVL R9,12(DI) + MOVD X12,DX + MOVD X7,CX + MOVD X10,R8 + MOVD X4,R9 + PSHUFL $0X39,X12,X12 + PSHUFL $0X39,X7,X7 + PSHUFL $0X39,X10,X10 + PSHUFL $0X39,X4,X4 + XORL 64(SI),DX + XORL 68(SI),CX + XORL 72(SI),R8 + XORL 76(SI),R9 + MOVL DX,64(DI) + MOVL CX,68(DI) + MOVL R8,72(DI) + MOVL R9,76(DI) + MOVD X12,DX + MOVD X7,CX + MOVD X10,R8 + MOVD X4,R9 + PSHUFL $0X39,X12,X12 + PSHUFL $0X39,X7,X7 + PSHUFL $0X39,X10,X10 + PSHUFL $0X39,X4,X4 + XORL 128(SI),DX + XORL 132(SI),CX + XORL 136(SI),R8 + XORL 140(SI),R9 + MOVL DX,128(DI) + MOVL CX,132(DI) + MOVL R8,136(DI) + MOVL R9,140(DI) + MOVD X12,DX + MOVD X7,CX + MOVD X10,R8 + MOVD X4,R9 + XORL 192(SI),DX + XORL 196(SI),CX + XORL 200(SI),R8 + XORL 204(SI),R9 + MOVL DX,192(DI) + MOVL CX,196(DI) + MOVL R8,200(DI) + MOVL R9,204(DI) + PADDL 240(SP),X14 + PADDL 64(SP),X0 + PADDL 128(SP),X5 + PADDL 192(SP),X8 + MOVD X14,DX + MOVD X0,CX + MOVD X5,R8 + MOVD X8,R9 + PSHUFL $0X39,X14,X14 + PSHUFL $0X39,X0,X0 + PSHUFL $0X39,X5,X5 + PSHUFL $0X39,X8,X8 + XORL 16(SI),DX + XORL 20(SI),CX + XORL 24(SI),R8 + XORL 28(SI),R9 + MOVL DX,16(DI) + MOVL CX,20(DI) + MOVL R8,24(DI) + MOVL R9,28(DI) + MOVD X14,DX + MOVD X0,CX + MOVD X5,R8 + MOVD X8,R9 + PSHUFL $0X39,X14,X14 + PSHUFL $0X39,X0,X0 + PSHUFL $0X39,X5,X5 + PSHUFL $0X39,X8,X8 + XORL 80(SI),DX + XORL 84(SI),CX + XORL 88(SI),R8 + XORL 92(SI),R9 + MOVL DX,80(DI) + MOVL CX,84(DI) + MOVL R8,88(DI) + MOVL R9,92(DI) + MOVD X14,DX + MOVD X0,CX + MOVD X5,R8 + MOVD X8,R9 + PSHUFL $0X39,X14,X14 + PSHUFL $0X39,X0,X0 + PSHUFL $0X39,X5,X5 + PSHUFL $0X39,X8,X8 + XORL 144(SI),DX + XORL 148(SI),CX + XORL 152(SI),R8 + XORL 156(SI),R9 + MOVL DX,144(DI) + MOVL CX,148(DI) + MOVL R8,152(DI) + MOVL R9,156(DI) + MOVD X14,DX + MOVD X0,CX + MOVD X5,R8 + MOVD X8,R9 + XORL 208(SI),DX + XORL 212(SI),CX + XORL 216(SI),R8 + XORL 220(SI),R9 + MOVL DX,208(DI) + MOVL CX,212(DI) + MOVL R8,216(DI) + MOVL R9,220(DI) + PADDL 288(SP),X15 + PADDL 304(SP),X11 + PADDL 80(SP),X1 + PADDL 144(SP),X6 + MOVD X15,DX + MOVD X11,CX + MOVD X1,R8 + MOVD X6,R9 + PSHUFL $0X39,X15,X15 + PSHUFL $0X39,X11,X11 + PSHUFL $0X39,X1,X1 + PSHUFL $0X39,X6,X6 + XORL 32(SI),DX + XORL 36(SI),CX + XORL 40(SI),R8 + XORL 44(SI),R9 + MOVL DX,32(DI) + MOVL CX,36(DI) + MOVL R8,40(DI) + MOVL R9,44(DI) + MOVD X15,DX + MOVD X11,CX + MOVD X1,R8 + MOVD X6,R9 + PSHUFL $0X39,X15,X15 + PSHUFL $0X39,X11,X11 + PSHUFL $0X39,X1,X1 + PSHUFL $0X39,X6,X6 + XORL 96(SI),DX + XORL 100(SI),CX + XORL 104(SI),R8 + XORL 108(SI),R9 + MOVL DX,96(DI) + MOVL CX,100(DI) + MOVL R8,104(DI) + MOVL R9,108(DI) + MOVD X15,DX + MOVD X11,CX + MOVD X1,R8 + MOVD X6,R9 + PSHUFL $0X39,X15,X15 + PSHUFL $0X39,X11,X11 + PSHUFL $0X39,X1,X1 + PSHUFL $0X39,X6,X6 + XORL 160(SI),DX + XORL 164(SI),CX + XORL 168(SI),R8 + XORL 172(SI),R9 + MOVL DX,160(DI) + MOVL CX,164(DI) + MOVL R8,168(DI) + MOVL R9,172(DI) + MOVD X15,DX + MOVD X11,CX + MOVD X1,R8 + MOVD X6,R9 + XORL 224(SI),DX + XORL 228(SI),CX + XORL 232(SI),R8 + XORL 236(SI),R9 + MOVL DX,224(DI) + MOVL CX,228(DI) + MOVL R8,232(DI) + MOVL R9,236(DI) + PADDL 160(SP),X13 + PADDL 208(SP),X9 + PADDL 256(SP),X3 + PADDL 96(SP),X2 + MOVD X13,DX + MOVD X9,CX + MOVD X3,R8 + MOVD X2,R9 + PSHUFL $0X39,X13,X13 + PSHUFL $0X39,X9,X9 + PSHUFL $0X39,X3,X3 + PSHUFL $0X39,X2,X2 + XORL 48(SI),DX + XORL 52(SI),CX + XORL 56(SI),R8 + XORL 60(SI),R9 + MOVL DX,48(DI) + MOVL CX,52(DI) + MOVL R8,56(DI) + MOVL R9,60(DI) + MOVD X13,DX + MOVD X9,CX + MOVD X3,R8 + MOVD X2,R9 + PSHUFL $0X39,X13,X13 + PSHUFL $0X39,X9,X9 + PSHUFL $0X39,X3,X3 + PSHUFL $0X39,X2,X2 + XORL 112(SI),DX + XORL 116(SI),CX + XORL 120(SI),R8 + XORL 124(SI),R9 + MOVL DX,112(DI) + MOVL CX,116(DI) + MOVL R8,120(DI) + MOVL R9,124(DI) + MOVD X13,DX + MOVD X9,CX + MOVD X3,R8 + MOVD X2,R9 + PSHUFL $0X39,X13,X13 + PSHUFL $0X39,X9,X9 + PSHUFL $0X39,X3,X3 + PSHUFL $0X39,X2,X2 + XORL 176(SI),DX + XORL 180(SI),CX + XORL 184(SI),R8 + XORL 188(SI),R9 + MOVL DX,176(DI) + MOVL CX,180(DI) + MOVL R8,184(DI) + MOVL R9,188(DI) + MOVD X13,DX + MOVD X9,CX + MOVD X3,R8 + MOVD X2,R9 + XORL 240(SI),DX + XORL 244(SI),CX + XORL 248(SI),R8 + XORL 252(SI),R9 + MOVL DX,240(DI) + MOVL CX,244(DI) + MOVL R8,248(DI) + MOVL R9,252(DI) + MOVQ 352(SP),R9 + SUBQ $256,R9 + ADDQ $256,SI + ADDQ $256,DI + CMPQ R9,$256 + JAE BYTESATLEAST256 + CMPQ R9,$0 + JBE DONE + BYTESBETWEEN1AND255: + CMPQ R9,$64 + JAE NOCOPY + MOVQ DI,DX + LEAQ 360(SP),DI + MOVQ R9,CX + REP; MOVSB + LEAQ 360(SP),DI + LEAQ 360(SP),SI + NOCOPY: + MOVQ R9,352(SP) + MOVOA 48(SP),X0 + MOVOA 0(SP),X1 + MOVOA 16(SP),X2 + MOVOA 32(SP),X3 + MOVOA X1,X4 + MOVQ $20,CX + MAINLOOP2: + PADDL X0,X4 + MOVOA X0,X5 + MOVOA X4,X6 + PSLLL $7,X4 + PSRLL $25,X6 + PXOR X4,X3 + PXOR X6,X3 + PADDL X3,X5 + MOVOA X3,X4 + MOVOA X5,X6 + PSLLL $9,X5 + PSRLL $23,X6 + PXOR X5,X2 + PSHUFL $0X93,X3,X3 + PXOR X6,X2 + PADDL X2,X4 + MOVOA X2,X5 + MOVOA X4,X6 + PSLLL $13,X4 + PSRLL $19,X6 + PXOR X4,X1 + PSHUFL $0X4E,X2,X2 + PXOR X6,X1 + PADDL X1,X5 + MOVOA X3,X4 + MOVOA X5,X6 + PSLLL $18,X5 + PSRLL $14,X6 + PXOR X5,X0 + PSHUFL $0X39,X1,X1 + PXOR X6,X0 + PADDL X0,X4 + MOVOA X0,X5 + MOVOA X4,X6 + PSLLL $7,X4 + PSRLL $25,X6 + PXOR X4,X1 + PXOR X6,X1 + PADDL X1,X5 + MOVOA X1,X4 + MOVOA X5,X6 + PSLLL $9,X5 + PSRLL $23,X6 + PXOR X5,X2 + PSHUFL $0X93,X1,X1 + PXOR X6,X2 + PADDL X2,X4 + MOVOA X2,X5 + MOVOA X4,X6 + PSLLL $13,X4 + PSRLL $19,X6 + PXOR X4,X3 + PSHUFL $0X4E,X2,X2 + PXOR X6,X3 + PADDL X3,X5 + MOVOA X1,X4 + MOVOA X5,X6 + PSLLL $18,X5 + PSRLL $14,X6 + PXOR X5,X0 + PSHUFL $0X39,X3,X3 + PXOR X6,X0 + PADDL X0,X4 + MOVOA X0,X5 + MOVOA X4,X6 + PSLLL $7,X4 + PSRLL $25,X6 + PXOR X4,X3 + PXOR X6,X3 + PADDL X3,X5 + MOVOA X3,X4 + MOVOA X5,X6 + PSLLL $9,X5 + PSRLL $23,X6 + PXOR X5,X2 + PSHUFL $0X93,X3,X3 + PXOR X6,X2 + PADDL X2,X4 + MOVOA X2,X5 + MOVOA X4,X6 + PSLLL $13,X4 + PSRLL $19,X6 + PXOR X4,X1 + PSHUFL $0X4E,X2,X2 + PXOR X6,X1 + PADDL X1,X5 + MOVOA X3,X4 + MOVOA X5,X6 + PSLLL $18,X5 + PSRLL $14,X6 + PXOR X5,X0 + PSHUFL $0X39,X1,X1 + PXOR X6,X0 + PADDL X0,X4 + MOVOA X0,X5 + MOVOA X4,X6 + PSLLL $7,X4 + PSRLL $25,X6 + PXOR X4,X1 + PXOR X6,X1 + PADDL X1,X5 + MOVOA X1,X4 + MOVOA X5,X6 + PSLLL $9,X5 + PSRLL $23,X6 + PXOR X5,X2 + PSHUFL $0X93,X1,X1 + PXOR X6,X2 + PADDL X2,X4 + MOVOA X2,X5 + MOVOA X4,X6 + PSLLL $13,X4 + PSRLL $19,X6 + PXOR X4,X3 + PSHUFL $0X4E,X2,X2 + PXOR X6,X3 + SUBQ $4,CX + PADDL X3,X5 + MOVOA X1,X4 + MOVOA X5,X6 + PSLLL $18,X5 + PXOR X7,X7 + PSRLL $14,X6 + PXOR X5,X0 + PSHUFL $0X39,X3,X3 + PXOR X6,X0 + JA MAINLOOP2 + PADDL 48(SP),X0 + PADDL 0(SP),X1 + PADDL 16(SP),X2 + PADDL 32(SP),X3 + MOVD X0,CX + MOVD X1,R8 + MOVD X2,R9 + MOVD X3,AX + PSHUFL $0X39,X0,X0 + PSHUFL $0X39,X1,X1 + PSHUFL $0X39,X2,X2 + PSHUFL $0X39,X3,X3 + XORL 0(SI),CX + XORL 48(SI),R8 + XORL 32(SI),R9 + XORL 16(SI),AX + MOVL CX,0(DI) + MOVL R8,48(DI) + MOVL R9,32(DI) + MOVL AX,16(DI) + MOVD X0,CX + MOVD X1,R8 + MOVD X2,R9 + MOVD X3,AX + PSHUFL $0X39,X0,X0 + PSHUFL $0X39,X1,X1 + PSHUFL $0X39,X2,X2 + PSHUFL $0X39,X3,X3 + XORL 20(SI),CX + XORL 4(SI),R8 + XORL 52(SI),R9 + XORL 36(SI),AX + MOVL CX,20(DI) + MOVL R8,4(DI) + MOVL R9,52(DI) + MOVL AX,36(DI) + MOVD X0,CX + MOVD X1,R8 + MOVD X2,R9 + MOVD X3,AX + PSHUFL $0X39,X0,X0 + PSHUFL $0X39,X1,X1 + PSHUFL $0X39,X2,X2 + PSHUFL $0X39,X3,X3 + XORL 40(SI),CX + XORL 24(SI),R8 + XORL 8(SI),R9 + XORL 56(SI),AX + MOVL CX,40(DI) + MOVL R8,24(DI) + MOVL R9,8(DI) + MOVL AX,56(DI) + MOVD X0,CX + MOVD X1,R8 + MOVD X2,R9 + MOVD X3,AX + XORL 60(SI),CX + XORL 44(SI),R8 + XORL 28(SI),R9 + XORL 12(SI),AX + MOVL CX,60(DI) + MOVL R8,44(DI) + MOVL R9,28(DI) + MOVL AX,12(DI) + MOVQ 352(SP),R9 + MOVL 16(SP),CX + MOVL 36 (SP),R8 + ADDQ $1,CX + SHLQ $32,R8 + ADDQ R8,CX + MOVQ CX,R8 + SHRQ $32,R8 + MOVL CX,16(SP) + MOVL R8, 36 (SP) + CMPQ R9,$64 + JA BYTESATLEAST65 + JAE BYTESATLEAST64 + MOVQ DI,SI + MOVQ DX,DI + MOVQ R9,CX + REP; MOVSB + BYTESATLEAST64: + DONE: + MOVQ R12,SP + RET + BYTESATLEAST65: + SUBQ $64,R9 + ADDQ $64,DI + ADDQ $64,SI + JMP BYTESBETWEEN1AND255 diff --git a/vendor/src/golang.org/x/crypto/salsa20/salsa/salsa208.go b/vendor/src/golang.org/x/crypto/salsa20/salsa/salsa208.go new file mode 100644 index 00000000..9bfc0927 --- /dev/null +++ b/vendor/src/golang.org/x/crypto/salsa20/salsa/salsa208.go @@ -0,0 +1,199 @@ +// Copyright 2012 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 salsa + +// Core208 applies the Salsa20/8 core function to the 64-byte array in and puts +// the result into the 64-byte array out. The input and output may be the same array. +func Core208(out *[64]byte, in *[64]byte) { + j0 := uint32(in[0]) | uint32(in[1])<<8 | uint32(in[2])<<16 | uint32(in[3])<<24 + j1 := uint32(in[4]) | uint32(in[5])<<8 | uint32(in[6])<<16 | uint32(in[7])<<24 + j2 := uint32(in[8]) | uint32(in[9])<<8 | uint32(in[10])<<16 | uint32(in[11])<<24 + j3 := uint32(in[12]) | uint32(in[13])<<8 | uint32(in[14])<<16 | uint32(in[15])<<24 + j4 := uint32(in[16]) | uint32(in[17])<<8 | uint32(in[18])<<16 | uint32(in[19])<<24 + j5 := uint32(in[20]) | uint32(in[21])<<8 | uint32(in[22])<<16 | uint32(in[23])<<24 + j6 := uint32(in[24]) | uint32(in[25])<<8 | uint32(in[26])<<16 | uint32(in[27])<<24 + j7 := uint32(in[28]) | uint32(in[29])<<8 | uint32(in[30])<<16 | uint32(in[31])<<24 + j8 := uint32(in[32]) | uint32(in[33])<<8 | uint32(in[34])<<16 | uint32(in[35])<<24 + j9 := uint32(in[36]) | uint32(in[37])<<8 | uint32(in[38])<<16 | uint32(in[39])<<24 + j10 := uint32(in[40]) | uint32(in[41])<<8 | uint32(in[42])<<16 | uint32(in[43])<<24 + j11 := uint32(in[44]) | uint32(in[45])<<8 | uint32(in[46])<<16 | uint32(in[47])<<24 + j12 := uint32(in[48]) | uint32(in[49])<<8 | uint32(in[50])<<16 | uint32(in[51])<<24 + j13 := uint32(in[52]) | uint32(in[53])<<8 | uint32(in[54])<<16 | uint32(in[55])<<24 + j14 := uint32(in[56]) | uint32(in[57])<<8 | uint32(in[58])<<16 | uint32(in[59])<<24 + j15 := uint32(in[60]) | uint32(in[61])<<8 | uint32(in[62])<<16 | uint32(in[63])<<24 + + x0, x1, x2, x3, x4, x5, x6, x7, x8 := j0, j1, j2, j3, j4, j5, j6, j7, j8 + x9, x10, x11, x12, x13, x14, x15 := j9, j10, j11, j12, j13, j14, j15 + + for i := 0; i < 8; i += 2 { + u := x0 + x12 + x4 ^= u<<7 | u>>(32-7) + u = x4 + x0 + x8 ^= u<<9 | u>>(32-9) + u = x8 + x4 + x12 ^= u<<13 | u>>(32-13) + u = x12 + x8 + x0 ^= u<<18 | u>>(32-18) + + u = x5 + x1 + x9 ^= u<<7 | u>>(32-7) + u = x9 + x5 + x13 ^= u<<9 | u>>(32-9) + u = x13 + x9 + x1 ^= u<<13 | u>>(32-13) + u = x1 + x13 + x5 ^= u<<18 | u>>(32-18) + + u = x10 + x6 + x14 ^= u<<7 | u>>(32-7) + u = x14 + x10 + x2 ^= u<<9 | u>>(32-9) + u = x2 + x14 + x6 ^= u<<13 | u>>(32-13) + u = x6 + x2 + x10 ^= u<<18 | u>>(32-18) + + u = x15 + x11 + x3 ^= u<<7 | u>>(32-7) + u = x3 + x15 + x7 ^= u<<9 | u>>(32-9) + u = x7 + x3 + x11 ^= u<<13 | u>>(32-13) + u = x11 + x7 + x15 ^= u<<18 | u>>(32-18) + + u = x0 + x3 + x1 ^= u<<7 | u>>(32-7) + u = x1 + x0 + x2 ^= u<<9 | u>>(32-9) + u = x2 + x1 + x3 ^= u<<13 | u>>(32-13) + u = x3 + x2 + x0 ^= u<<18 | u>>(32-18) + + u = x5 + x4 + x6 ^= u<<7 | u>>(32-7) + u = x6 + x5 + x7 ^= u<<9 | u>>(32-9) + u = x7 + x6 + x4 ^= u<<13 | u>>(32-13) + u = x4 + x7 + x5 ^= u<<18 | u>>(32-18) + + u = x10 + x9 + x11 ^= u<<7 | u>>(32-7) + u = x11 + x10 + x8 ^= u<<9 | u>>(32-9) + u = x8 + x11 + x9 ^= u<<13 | u>>(32-13) + u = x9 + x8 + x10 ^= u<<18 | u>>(32-18) + + u = x15 + x14 + x12 ^= u<<7 | u>>(32-7) + u = x12 + x15 + x13 ^= u<<9 | u>>(32-9) + u = x13 + x12 + x14 ^= u<<13 | u>>(32-13) + u = x14 + x13 + x15 ^= u<<18 | u>>(32-18) + } + x0 += j0 + x1 += j1 + x2 += j2 + x3 += j3 + x4 += j4 + x5 += j5 + x6 += j6 + x7 += j7 + x8 += j8 + x9 += j9 + x10 += j10 + x11 += j11 + x12 += j12 + x13 += j13 + x14 += j14 + x15 += j15 + + out[0] = byte(x0) + out[1] = byte(x0 >> 8) + out[2] = byte(x0 >> 16) + out[3] = byte(x0 >> 24) + + out[4] = byte(x1) + out[5] = byte(x1 >> 8) + out[6] = byte(x1 >> 16) + out[7] = byte(x1 >> 24) + + out[8] = byte(x2) + out[9] = byte(x2 >> 8) + out[10] = byte(x2 >> 16) + out[11] = byte(x2 >> 24) + + out[12] = byte(x3) + out[13] = byte(x3 >> 8) + out[14] = byte(x3 >> 16) + out[15] = byte(x3 >> 24) + + out[16] = byte(x4) + out[17] = byte(x4 >> 8) + out[18] = byte(x4 >> 16) + out[19] = byte(x4 >> 24) + + out[20] = byte(x5) + out[21] = byte(x5 >> 8) + out[22] = byte(x5 >> 16) + out[23] = byte(x5 >> 24) + + out[24] = byte(x6) + out[25] = byte(x6 >> 8) + out[26] = byte(x6 >> 16) + out[27] = byte(x6 >> 24) + + out[28] = byte(x7) + out[29] = byte(x7 >> 8) + out[30] = byte(x7 >> 16) + out[31] = byte(x7 >> 24) + + out[32] = byte(x8) + out[33] = byte(x8 >> 8) + out[34] = byte(x8 >> 16) + out[35] = byte(x8 >> 24) + + out[36] = byte(x9) + out[37] = byte(x9 >> 8) + out[38] = byte(x9 >> 16) + out[39] = byte(x9 >> 24) + + out[40] = byte(x10) + out[41] = byte(x10 >> 8) + out[42] = byte(x10 >> 16) + out[43] = byte(x10 >> 24) + + out[44] = byte(x11) + out[45] = byte(x11 >> 8) + out[46] = byte(x11 >> 16) + out[47] = byte(x11 >> 24) + + out[48] = byte(x12) + out[49] = byte(x12 >> 8) + out[50] = byte(x12 >> 16) + out[51] = byte(x12 >> 24) + + out[52] = byte(x13) + out[53] = byte(x13 >> 8) + out[54] = byte(x13 >> 16) + out[55] = byte(x13 >> 24) + + out[56] = byte(x14) + out[57] = byte(x14 >> 8) + out[58] = byte(x14 >> 16) + out[59] = byte(x14 >> 24) + + out[60] = byte(x15) + out[61] = byte(x15 >> 8) + out[62] = byte(x15 >> 16) + out[63] = byte(x15 >> 24) +} diff --git a/vendor/src/golang.org/x/crypto/salsa20/salsa/salsa20_amd64.go b/vendor/src/golang.org/x/crypto/salsa20/salsa/salsa20_amd64.go new file mode 100644 index 00000000..f9269c38 --- /dev/null +++ b/vendor/src/golang.org/x/crypto/salsa20/salsa/salsa20_amd64.go @@ -0,0 +1,24 @@ +// Copyright 2012 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. + +// +build amd64,!appengine,!gccgo + +package salsa + +// This function is implemented in salsa2020_amd64.s. + +//go:noescape + +func salsa2020XORKeyStream(out, in *byte, n uint64, nonce, key *byte) + +// XORKeyStream crypts bytes from in to out using the given key and counters. +// In and out must overlap entirely or not at all. Counter +// contains the raw salsa20 counter bytes (both nonce and block counter). +func XORKeyStream(out, in []byte, counter *[16]byte, key *[32]byte) { + if len(in) == 0 { + return + } + _ = out[len(in)-1] + salsa2020XORKeyStream(&out[0], &in[0], uint64(len(in)), &counter[0], &key[0]) +} diff --git a/vendor/src/golang.org/x/crypto/salsa20/salsa/salsa20_ref.go b/vendor/src/golang.org/x/crypto/salsa20/salsa/salsa20_ref.go new file mode 100644 index 00000000..22126d17 --- /dev/null +++ b/vendor/src/golang.org/x/crypto/salsa20/salsa/salsa20_ref.go @@ -0,0 +1,234 @@ +// Copyright 2012 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. + +// +build !amd64 appengine gccgo + +package salsa + +const rounds = 20 + +// core applies the Salsa20 core function to 16-byte input in, 32-byte key k, +// and 16-byte constant c, and puts the result into 64-byte array out. +func core(out *[64]byte, in *[16]byte, k *[32]byte, c *[16]byte) { + j0 := uint32(c[0]) | uint32(c[1])<<8 | uint32(c[2])<<16 | uint32(c[3])<<24 + j1 := uint32(k[0]) | uint32(k[1])<<8 | uint32(k[2])<<16 | uint32(k[3])<<24 + j2 := uint32(k[4]) | uint32(k[5])<<8 | uint32(k[6])<<16 | uint32(k[7])<<24 + j3 := uint32(k[8]) | uint32(k[9])<<8 | uint32(k[10])<<16 | uint32(k[11])<<24 + j4 := uint32(k[12]) | uint32(k[13])<<8 | uint32(k[14])<<16 | uint32(k[15])<<24 + j5 := uint32(c[4]) | uint32(c[5])<<8 | uint32(c[6])<<16 | uint32(c[7])<<24 + j6 := uint32(in[0]) | uint32(in[1])<<8 | uint32(in[2])<<16 | uint32(in[3])<<24 + j7 := uint32(in[4]) | uint32(in[5])<<8 | uint32(in[6])<<16 | uint32(in[7])<<24 + j8 := uint32(in[8]) | uint32(in[9])<<8 | uint32(in[10])<<16 | uint32(in[11])<<24 + j9 := uint32(in[12]) | uint32(in[13])<<8 | uint32(in[14])<<16 | uint32(in[15])<<24 + j10 := uint32(c[8]) | uint32(c[9])<<8 | uint32(c[10])<<16 | uint32(c[11])<<24 + j11 := uint32(k[16]) | uint32(k[17])<<8 | uint32(k[18])<<16 | uint32(k[19])<<24 + j12 := uint32(k[20]) | uint32(k[21])<<8 | uint32(k[22])<<16 | uint32(k[23])<<24 + j13 := uint32(k[24]) | uint32(k[25])<<8 | uint32(k[26])<<16 | uint32(k[27])<<24 + j14 := uint32(k[28]) | uint32(k[29])<<8 | uint32(k[30])<<16 | uint32(k[31])<<24 + j15 := uint32(c[12]) | uint32(c[13])<<8 | uint32(c[14])<<16 | uint32(c[15])<<24 + + x0, x1, x2, x3, x4, x5, x6, x7, x8 := j0, j1, j2, j3, j4, j5, j6, j7, j8 + x9, x10, x11, x12, x13, x14, x15 := j9, j10, j11, j12, j13, j14, j15 + + for i := 0; i < rounds; i += 2 { + u := x0 + x12 + x4 ^= u<<7 | u>>(32-7) + u = x4 + x0 + x8 ^= u<<9 | u>>(32-9) + u = x8 + x4 + x12 ^= u<<13 | u>>(32-13) + u = x12 + x8 + x0 ^= u<<18 | u>>(32-18) + + u = x5 + x1 + x9 ^= u<<7 | u>>(32-7) + u = x9 + x5 + x13 ^= u<<9 | u>>(32-9) + u = x13 + x9 + x1 ^= u<<13 | u>>(32-13) + u = x1 + x13 + x5 ^= u<<18 | u>>(32-18) + + u = x10 + x6 + x14 ^= u<<7 | u>>(32-7) + u = x14 + x10 + x2 ^= u<<9 | u>>(32-9) + u = x2 + x14 + x6 ^= u<<13 | u>>(32-13) + u = x6 + x2 + x10 ^= u<<18 | u>>(32-18) + + u = x15 + x11 + x3 ^= u<<7 | u>>(32-7) + u = x3 + x15 + x7 ^= u<<9 | u>>(32-9) + u = x7 + x3 + x11 ^= u<<13 | u>>(32-13) + u = x11 + x7 + x15 ^= u<<18 | u>>(32-18) + + u = x0 + x3 + x1 ^= u<<7 | u>>(32-7) + u = x1 + x0 + x2 ^= u<<9 | u>>(32-9) + u = x2 + x1 + x3 ^= u<<13 | u>>(32-13) + u = x3 + x2 + x0 ^= u<<18 | u>>(32-18) + + u = x5 + x4 + x6 ^= u<<7 | u>>(32-7) + u = x6 + x5 + x7 ^= u<<9 | u>>(32-9) + u = x7 + x6 + x4 ^= u<<13 | u>>(32-13) + u = x4 + x7 + x5 ^= u<<18 | u>>(32-18) + + u = x10 + x9 + x11 ^= u<<7 | u>>(32-7) + u = x11 + x10 + x8 ^= u<<9 | u>>(32-9) + u = x8 + x11 + x9 ^= u<<13 | u>>(32-13) + u = x9 + x8 + x10 ^= u<<18 | u>>(32-18) + + u = x15 + x14 + x12 ^= u<<7 | u>>(32-7) + u = x12 + x15 + x13 ^= u<<9 | u>>(32-9) + u = x13 + x12 + x14 ^= u<<13 | u>>(32-13) + u = x14 + x13 + x15 ^= u<<18 | u>>(32-18) + } + x0 += j0 + x1 += j1 + x2 += j2 + x3 += j3 + x4 += j4 + x5 += j5 + x6 += j6 + x7 += j7 + x8 += j8 + x9 += j9 + x10 += j10 + x11 += j11 + x12 += j12 + x13 += j13 + x14 += j14 + x15 += j15 + + out[0] = byte(x0) + out[1] = byte(x0 >> 8) + out[2] = byte(x0 >> 16) + out[3] = byte(x0 >> 24) + + out[4] = byte(x1) + out[5] = byte(x1 >> 8) + out[6] = byte(x1 >> 16) + out[7] = byte(x1 >> 24) + + out[8] = byte(x2) + out[9] = byte(x2 >> 8) + out[10] = byte(x2 >> 16) + out[11] = byte(x2 >> 24) + + out[12] = byte(x3) + out[13] = byte(x3 >> 8) + out[14] = byte(x3 >> 16) + out[15] = byte(x3 >> 24) + + out[16] = byte(x4) + out[17] = byte(x4 >> 8) + out[18] = byte(x4 >> 16) + out[19] = byte(x4 >> 24) + + out[20] = byte(x5) + out[21] = byte(x5 >> 8) + out[22] = byte(x5 >> 16) + out[23] = byte(x5 >> 24) + + out[24] = byte(x6) + out[25] = byte(x6 >> 8) + out[26] = byte(x6 >> 16) + out[27] = byte(x6 >> 24) + + out[28] = byte(x7) + out[29] = byte(x7 >> 8) + out[30] = byte(x7 >> 16) + out[31] = byte(x7 >> 24) + + out[32] = byte(x8) + out[33] = byte(x8 >> 8) + out[34] = byte(x8 >> 16) + out[35] = byte(x8 >> 24) + + out[36] = byte(x9) + out[37] = byte(x9 >> 8) + out[38] = byte(x9 >> 16) + out[39] = byte(x9 >> 24) + + out[40] = byte(x10) + out[41] = byte(x10 >> 8) + out[42] = byte(x10 >> 16) + out[43] = byte(x10 >> 24) + + out[44] = byte(x11) + out[45] = byte(x11 >> 8) + out[46] = byte(x11 >> 16) + out[47] = byte(x11 >> 24) + + out[48] = byte(x12) + out[49] = byte(x12 >> 8) + out[50] = byte(x12 >> 16) + out[51] = byte(x12 >> 24) + + out[52] = byte(x13) + out[53] = byte(x13 >> 8) + out[54] = byte(x13 >> 16) + out[55] = byte(x13 >> 24) + + out[56] = byte(x14) + out[57] = byte(x14 >> 8) + out[58] = byte(x14 >> 16) + out[59] = byte(x14 >> 24) + + out[60] = byte(x15) + out[61] = byte(x15 >> 8) + out[62] = byte(x15 >> 16) + out[63] = byte(x15 >> 24) +} + +// XORKeyStream crypts bytes from in to out using the given key and counters. +// In and out must overlap entirely or not at all. Counter +// contains the raw salsa20 counter bytes (both nonce and block counter). +func XORKeyStream(out, in []byte, counter *[16]byte, key *[32]byte) { + var block [64]byte + var counterCopy [16]byte + copy(counterCopy[:], counter[:]) + + for len(in) >= 64 { + core(&block, &counterCopy, key, &Sigma) + for i, x := range block { + out[i] = in[i] ^ x + } + u := uint32(1) + for i := 8; i < 16; i++ { + u += uint32(counterCopy[i]) + counterCopy[i] = byte(u) + u >>= 8 + } + in = in[64:] + out = out[64:] + } + + if len(in) > 0 { + core(&block, &counterCopy, key, &Sigma) + for i, v := range in { + out[i] = v ^ block[i] + } + } +} diff --git a/vendor/src/golang.org/x/crypto/salsa20/salsa/salsa_test.go b/vendor/src/golang.org/x/crypto/salsa20/salsa/salsa_test.go new file mode 100644 index 00000000..f67e94eb --- /dev/null +++ b/vendor/src/golang.org/x/crypto/salsa20/salsa/salsa_test.go @@ -0,0 +1,54 @@ +// Copyright 2012 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 salsa + +import "testing" + +func TestCore208(t *testing.T) { + in := [64]byte{ + 0x7e, 0x87, 0x9a, 0x21, 0x4f, 0x3e, 0xc9, 0x86, + 0x7c, 0xa9, 0x40, 0xe6, 0x41, 0x71, 0x8f, 0x26, + 0xba, 0xee, 0x55, 0x5b, 0x8c, 0x61, 0xc1, 0xb5, + 0x0d, 0xf8, 0x46, 0x11, 0x6d, 0xcd, 0x3b, 0x1d, + 0xee, 0x24, 0xf3, 0x19, 0xdf, 0x9b, 0x3d, 0x85, + 0x14, 0x12, 0x1e, 0x4b, 0x5a, 0xc5, 0xaa, 0x32, + 0x76, 0x02, 0x1d, 0x29, 0x09, 0xc7, 0x48, 0x29, + 0xed, 0xeb, 0xc6, 0x8d, 0xb8, 0xb8, 0xc2, 0x5e} + + out := [64]byte{ + 0xa4, 0x1f, 0x85, 0x9c, 0x66, 0x08, 0xcc, 0x99, + 0x3b, 0x81, 0xca, 0xcb, 0x02, 0x0c, 0xef, 0x05, + 0x04, 0x4b, 0x21, 0x81, 0xa2, 0xfd, 0x33, 0x7d, + 0xfd, 0x7b, 0x1c, 0x63, 0x96, 0x68, 0x2f, 0x29, + 0xb4, 0x39, 0x31, 0x68, 0xe3, 0xc9, 0xe6, 0xbc, + 0xfe, 0x6b, 0xc5, 0xb7, 0xa0, 0x6d, 0x96, 0xba, + 0xe4, 0x24, 0xcc, 0x10, 0x2c, 0x91, 0x74, 0x5c, + 0x24, 0xad, 0x67, 0x3d, 0xc7, 0x61, 0x8f, 0x81, + } + + Core208(&in, &in) + if in != out { + t.Errorf("expected %x, got %x", out, in) + } +} + +func TestOutOfBoundsWrite(t *testing.T) { + // encrypted "0123456789" + cipherText := []byte{170, 166, 196, 104, 175, 121, 68, 44, 174, 51} + var counter [16]byte + var key [32]byte + want := "abcdefghij" + plainText := []byte(want) + defer func() { + err := recover() + if err == nil { + t.Error("XORKeyStream expected to panic on len(dst) < len(src), but didn't") + } + if plainText[3] == '3' { + t.Errorf("XORKeyStream did out of bounds write, want %v, got %v", want, string(plainText)) + } + }() + XORKeyStream(plainText[:3], cipherText, &counter, &key) +}