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",
|
||||
"branch": "master"
|
||||
},
|
||||
{
|
||||
"importpath": "github.com/go-macaroon/macaroon",
|
||||
"repository": "https://github.com/go-macaroon/macaroon",
|
||||
"revision": "bed2a428da6e56d950bed5b41fcbae3141e5b0d0",
|
||||
"branch": "HEAD"
|
||||
},
|
||||
{
|
||||
"importpath": "github.com/golang/protobuf/proto",
|
||||
"repository": "https://github.com/golang/protobuf",
|
||||
|
@ -375,27 +369,6 @@
|
|||
"branch": "master",
|
||||
"path": "/ed25519"
|
||||
},
|
||||
{
|
||||
"importpath": "golang.org/x/crypto/nacl/secretbox",
|
||||
"repository": "https://go.googlesource.com/crypto",
|
||||
"revision": "d6449816ce06963d9d136eee5a56fca5b0616e7e",
|
||||
"branch": "master",
|
||||
"path": "/nacl/secretbox"
|
||||
},
|
||||
{
|
||||
"importpath": "golang.org/x/crypto/poly1305",
|
||||
"repository": "https://go.googlesource.com/crypto",
|
||||
"revision": "d6449816ce06963d9d136eee5a56fca5b0616e7e",
|
||||
"branch": "master",
|
||||
"path": "/poly1305"
|
||||
},
|
||||
{
|
||||
"importpath": "golang.org/x/crypto/salsa20/salsa",
|
||||
"repository": "https://go.googlesource.com/crypto",
|
||||
"revision": "d6449816ce06963d9d136eee5a56fca5b0616e7e",
|
||||
"branch": "master",
|
||||
"path": "/salsa20/salsa"
|
||||
},
|
||||
{
|
||||
"importpath": "golang.org/x/crypto/ssh",
|
||||
"repository": "https://go.googlesource.com/crypto",
|
||||
|
|
|
@ -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