This reverts commit afeab7b2d4
.
Signed-off-by: Andrew Morgan <andrewm@matrix.org>
main
parent
afeab7b2d4
commit
f5cc5bd9bb
|
@ -1,132 +0,0 @@
|
||||||
// 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
|
|
||||||
}
|
|
|
@ -1,80 +0,0 @@
|
||||||
// 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")
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -77,12 +77,6 @@
|
||||||
"revision": "44cc805cf13205b55f69e14bcb69867d1ae92f98",
|
"revision": "44cc805cf13205b55f69e14bcb69867d1ae92f98",
|
||||||
"branch": "master"
|
"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",
|
"importpath": "github.com/golang/protobuf/proto",
|
||||||
"repository": "https://github.com/golang/protobuf",
|
"repository": "https://github.com/golang/protobuf",
|
||||||
|
@ -375,27 +369,6 @@
|
||||||
"branch": "master",
|
"branch": "master",
|
||||||
"path": "/ed25519"
|
"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",
|
"importpath": "golang.org/x/crypto/ssh",
|
||||||
"repository": "https://go.googlesource.com/crypto",
|
"repository": "https://go.googlesource.com/crypto",
|
||||||
|
|
|
@ -1,26 +0,0 @@
|
||||||
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.
|
|
|
@ -1,159 +0,0 @@
|
||||||
# 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)
|
|
||||||
}
|
|
||||||
```
|
|
|
@ -1,4 +0,0 @@
|
||||||
macaroon:
|
|
||||||
|
|
||||||
- verify that all signature calculations to correspond exactly
|
|
||||||
with libmacaroons.
|
|
|
@ -1,109 +0,0 @@
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,91 +0,0 @@
|
||||||
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
|
|
||||||
}
|
|
|
@ -1,66 +0,0 @@
|
||||||
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
|
|
||||||
}
|
|
|
@ -1,14 +0,0 @@
|
||||||
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
|
|
||||||
}
|
|
|
@ -1,366 +0,0 @@
|
||||||
// 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[:]
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,914 +0,0 @@
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,190 +0,0 @@
|
||||||
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
|
|
||||||
}
|
|
|
@ -1,253 +0,0 @@
|
||||||
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
|
|
||||||
}
|
|
|
@ -1,239 +0,0 @@
|
||||||
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
|
|
||||||
}
|
|
|
@ -1,203 +0,0 @@
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,133 +0,0 @@
|
||||||
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
|
|
||||||
}
|
|
|
@ -1,98 +0,0 @@
|
||||||
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))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,117 +0,0 @@
|
||||||
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]...)
|
|
||||||
}
|
|
|
@ -1,126 +0,0 @@
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,102 +0,0 @@
|
||||||
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]
|
|
||||||
}
|
|
|
@ -1,53 +0,0 @@
|
||||||
// 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
|
|
||||||
}
|
|
|
@ -1,166 +0,0 @@
|
||||||
// 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
|
|
||||||
}
|
|
|
@ -1,154 +0,0 @@
|
||||||
// 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)
|
|
||||||
}
|
|
|
@ -1,33 +0,0 @@
|
||||||
// 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
|
|
||||||
}
|
|
|
@ -1,159 +0,0 @@
|
||||||
// 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
|
|
||||||
}
|
|
|
@ -1,22 +0,0 @@
|
||||||
// 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)
|
|
||||||
}
|
|
|
@ -1,125 +0,0 @@
|
||||||
// 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
|
|
|
@ -1,22 +0,0 @@
|
||||||
// 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)
|
|
||||||
}
|
|
|
@ -1,427 +0,0 @@
|
||||||
// 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
|
|
|
@ -1,141 +0,0 @@
|
||||||
// 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)
|
|
||||||
}
|
|
|
@ -1,144 +0,0 @@
|
||||||
// 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)
|
|
||||||
}
|
|
|
@ -1,889 +0,0 @@
|
||||||
// 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
|
|
|
@ -1,199 +0,0 @@
|
||||||
// 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)
|
|
||||||
}
|
|
|
@ -1,24 +0,0 @@
|
||||||
// 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])
|
|
||||||
}
|
|
|
@ -1,234 +0,0 @@
|
||||||
// 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]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,54 +0,0 @@
|
||||||
// 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)
|
|
||||||
}
|
|
Loading…
Reference in New Issue