Bump dependencies (#592)

* Bump dependencies

* Fix missing dependencies that were not previously fetched
This commit is contained in:
Brendan Abolivier 2018-11-12 10:21:15 +00:00 committed by GitHub
parent 8b0f60a470
commit 2133e6bf59
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
44 changed files with 10555 additions and 2 deletions

38
vendor/manifest vendored
View File

@ -148,8 +148,8 @@
{ {
"importpath": "github.com/matrix-org/gomatrixserverlib", "importpath": "github.com/matrix-org/gomatrixserverlib",
"repository": "https://github.com/matrix-org/gomatrixserverlib", "repository": "https://github.com/matrix-org/gomatrixserverlib",
"revision": "677bbe93ffc9ad9ba5de615cd81185d0493f5d25", "revision": "1c2cbc0872f0b2f19929dd70d22f77486078641e",
"branch": "master" "branch": "HEAD"
}, },
{ {
"importpath": "github.com/matrix-org/naffka", "importpath": "github.com/matrix-org/naffka",
@ -369,6 +369,34 @@
"branch": "master", "branch": "master",
"path": "/ed25519" "path": "/ed25519"
}, },
{
"importpath": "golang.org/x/crypto/internal/subtle",
"repository": "https://go.googlesource.com/crypto",
"revision": "e4dc69e5b2fd71dcaf8bd5d054eb936deb78d1fa",
"branch": "master",
"path": "/internal/subtle"
},
{
"importpath": "golang.org/x/crypto/nacl/secretbox",
"repository": "https://go.googlesource.com/crypto",
"revision": "e4dc69e5b2fd71dcaf8bd5d054eb936deb78d1fa",
"branch": "master",
"path": "/nacl/secretbox"
},
{
"importpath": "golang.org/x/crypto/poly1305",
"repository": "https://go.googlesource.com/crypto",
"revision": "e4dc69e5b2fd71dcaf8bd5d054eb936deb78d1fa",
"branch": "master",
"path": "/poly1305"
},
{
"importpath": "golang.org/x/crypto/salsa20/salsa",
"repository": "https://go.googlesource.com/crypto",
"revision": "e4dc69e5b2fd71dcaf8bd5d054eb936deb78d1fa",
"branch": "master",
"path": "/salsa20/salsa"
},
{ {
"importpath": "golang.org/x/crypto/ssh", "importpath": "golang.org/x/crypto/ssh",
"repository": "https://go.googlesource.com/crypto", "repository": "https://go.googlesource.com/crypto",
@ -455,6 +483,12 @@
"revision": "02e621739c77c791d8c153f240b7a1f75b07816f", "revision": "02e621739c77c791d8c153f240b7a1f75b07816f",
"branch": "master" "branch": "master"
}, },
{
"importpath": "gopkg.in/macaroon.v2",
"repository": "https://gopkg.in/macaroon.v2",
"revision": "bed2a428da6e56d950bed5b41fcbae3141e5b0d0",
"branch": "master"
},
{ {
"importpath": "gopkg.in/yaml.v2", "importpath": "gopkg.in/yaml.v2",
"repository": "https://gopkg.in/yaml.v2", "repository": "https://gopkg.in/yaml.v2",

View File

@ -3,6 +3,7 @@ package gomatrixserverlib
import ( import (
"context" "context"
"net/url" "net/url"
"strconv"
"golang.org/x/crypto/ed25519" "golang.org/x/crypto/ed25519"
) )
@ -170,3 +171,33 @@ func (ac *FederationClient) LookupRoomAlias(
err = ac.doRequest(ctx, req, &res) err = ac.doRequest(ctx, req, &res)
return return
} }
// Backfill asks a homeserver for events early enough for them to not be in the
// local database.
// See https://matrix.org/docs/spec/server_server/unstable.html#get-matrix-federation-v1-backfill-roomid
func (ac *FederationClient) Backfill(
ctx context.Context, s ServerName, roomID string, limit int, eventIDs []string,
) (res Transaction, err error) {
// Encode the room ID so it won't interfer with the path.
roomID = url.PathEscape(roomID)
// Parse the limit into a string so that we can include it in the URL's query.
limitStr := strconv.Itoa(limit)
// Define the URL's query.
query := url.Values{}
query["v"] = eventIDs
query.Set("limit", limitStr)
// Use the url.URL structure to easily generate the request's URI (path?query).
u := url.URL{
Path: "/_matrix/federation/v1/backfill/" + roomID + "/",
RawQuery: query.Encode(),
}
path := u.RequestURI()
// Send the request.
req := NewFederationRequest("GET", s, path)
err = ac.doRequest(ctx, req, &res)
return
}

View File

@ -0,0 +1,32 @@
// Copyright 2018 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 !appengine
// Package subtle implements functions that are often useful in cryptographic
// code but require careful thought to use correctly.
package subtle // import "golang.org/x/crypto/internal/subtle"
import "unsafe"
// AnyOverlap reports whether x and y share memory at any (not necessarily
// corresponding) index. The memory beyond the slice length is ignored.
func AnyOverlap(x, y []byte) bool {
return len(x) > 0 && len(y) > 0 &&
uintptr(unsafe.Pointer(&x[0])) <= uintptr(unsafe.Pointer(&y[len(y)-1])) &&
uintptr(unsafe.Pointer(&y[0])) <= uintptr(unsafe.Pointer(&x[len(x)-1]))
}
// InexactOverlap reports whether x and y share memory at any non-corresponding
// index. The memory beyond the slice length is ignored. Note that x and y can
// have different lengths and still not have any inexact overlap.
//
// InexactOverlap can be used to implement the requirements of the crypto/cipher
// AEAD, Block, BlockMode and Stream interfaces.
func InexactOverlap(x, y []byte) bool {
if len(x) == 0 || len(y) == 0 || &x[0] == &y[0] {
return false
}
return AnyOverlap(x, y)
}

View File

@ -0,0 +1,35 @@
// Copyright 2018 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 appengine
// Package subtle implements functions that are often useful in cryptographic
// code but require careful thought to use correctly.
package subtle // import "golang.org/x/crypto/internal/subtle"
// This is the Google App Engine standard variant based on reflect
// because the unsafe package and cgo are disallowed.
import "reflect"
// AnyOverlap reports whether x and y share memory at any (not necessarily
// corresponding) index. The memory beyond the slice length is ignored.
func AnyOverlap(x, y []byte) bool {
return len(x) > 0 && len(y) > 0 &&
reflect.ValueOf(&x[0]).Pointer() <= reflect.ValueOf(&y[len(y)-1]).Pointer() &&
reflect.ValueOf(&y[0]).Pointer() <= reflect.ValueOf(&x[len(x)-1]).Pointer()
}
// InexactOverlap reports whether x and y share memory at any non-corresponding
// index. The memory beyond the slice length is ignored. Note that x and y can
// have different lengths and still not have any inexact overlap.
//
// InexactOverlap can be used to implement the requirements of the crypto/cipher
// AEAD, Block, BlockMode and Stream interfaces.
func InexactOverlap(x, y []byte) bool {
if len(x) == 0 || len(y) == 0 || &x[0] == &y[0] {
return false
}
return AnyOverlap(x, y)
}

View File

@ -0,0 +1,50 @@
// Copyright 2018 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 subtle_test
import (
"testing"
"golang.org/x/crypto/internal/subtle"
)
var a, b [100]byte
var aliasingTests = []struct {
x, y []byte
anyOverlap, inexactOverlap bool
}{
{a[:], b[:], false, false},
{a[:], b[:0], false, false},
{a[:], b[:50], false, false},
{a[40:50], a[50:60], false, false},
{a[40:50], a[60:70], false, false},
{a[:51], a[50:], true, true},
{a[:], a[:], true, false},
{a[:50], a[:60], true, false},
{a[:], nil, false, false},
{nil, nil, false, false},
{a[:], a[:0], false, false},
{a[:10], a[:10:20], true, false},
{a[:10], a[5:10:20], true, true},
}
func testAliasing(t *testing.T, i int, x, y []byte, anyOverlap, inexactOverlap bool) {
any := subtle.AnyOverlap(x, y)
if any != anyOverlap {
t.Errorf("%d: wrong AnyOverlap result, expected %v, got %v", i, anyOverlap, any)
}
inexact := subtle.InexactOverlap(x, y)
if inexact != inexactOverlap {
t.Errorf("%d: wrong InexactOverlap result, expected %v, got %v", i, inexactOverlap, any)
}
}
func TestAliasing(t *testing.T) {
for i, tt := range aliasingTests {
testAliasing(t, i, tt.x, tt.y, tt.anyOverlap, tt.inexactOverlap)
testAliasing(t, i, tt.y, tt.x, tt.anyOverlap, tt.inexactOverlap)
}
}

View File

@ -0,0 +1,53 @@
// Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package secretbox_test
import (
"crypto/rand"
"encoding/hex"
"fmt"
"io"
"golang.org/x/crypto/nacl/secretbox"
)
func Example() {
// Load your secret key from a safe place and reuse it across multiple
// Seal calls. (Obviously don't use this example key for anything
// real.) If you want to convert a passphrase to a key, use a suitable
// package like bcrypt or scrypt.
secretKeyBytes, err := hex.DecodeString("6368616e676520746869732070617373776f726420746f206120736563726574")
if err != nil {
panic(err)
}
var secretKey [32]byte
copy(secretKey[:], secretKeyBytes)
// You must use a different nonce for each message you encrypt with the
// same key. Since the nonce here is 192 bits long, a random value
// provides a sufficiently small probability of repeats.
var nonce [24]byte
if _, err := io.ReadFull(rand.Reader, nonce[:]); err != nil {
panic(err)
}
// This encrypts "hello world" and appends the result to the nonce.
encrypted := secretbox.Seal(nonce[:], []byte("hello world"), &nonce, &secretKey)
// When you decrypt, you must use the same nonce and key you used to
// encrypt the message. One way to achieve this is to store the nonce
// alongside the encrypted message. Above, we stored the nonce in the first
// 24 bytes of the encrypted text.
var decryptNonce [24]byte
copy(decryptNonce[:], encrypted[:24])
decrypted, ok := secretbox.Open(nil, encrypted[24:], &decryptNonce, &secretKey)
if !ok {
panic("decryption error")
}
fmt.Println(string(decrypted))
// Output: hello world
}

View File

@ -0,0 +1,173 @@
// 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 noncesfor
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/internal/subtle"
"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)
if subtle.AnyOverlap(out, message) {
panic("nacl: invalid buffer overlap")
}
// 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, 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)
if subtle.AnyOverlap(out, box) {
panic("nacl: invalid buffer overlap")
}
// 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
}

View File

@ -0,0 +1,154 @@
// Copyright 2012 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package secretbox
import (
"bytes"
"crypto/rand"
"encoding/hex"
"testing"
)
func TestSealOpen(t *testing.T) {
var key [32]byte
var nonce [24]byte
rand.Reader.Read(key[:])
rand.Reader.Read(nonce[:])
var box, opened []byte
for msgLen := 0; msgLen < 128; msgLen += 17 {
message := make([]byte, msgLen)
rand.Reader.Read(message)
box = Seal(box[:0], message, &nonce, &key)
var ok bool
opened, ok = Open(opened[:0], box, &nonce, &key)
if !ok {
t.Errorf("%d: failed to open box", msgLen)
continue
}
if !bytes.Equal(opened, message) {
t.Errorf("%d: got %x, expected %x", msgLen, opened, message)
continue
}
}
for i := range box {
box[i] ^= 0x20
_, ok := Open(opened[:0], box, &nonce, &key)
if ok {
t.Errorf("box was opened after corrupting byte %d", i)
}
box[i] ^= 0x20
}
}
func TestSecretBox(t *testing.T) {
var key [32]byte
var nonce [24]byte
var message [64]byte
for i := range key[:] {
key[i] = 1
}
for i := range nonce[:] {
nonce[i] = 2
}
for i := range message[:] {
message[i] = 3
}
box := Seal(nil, message[:], &nonce, &key)
// expected was generated using the C implementation of NaCl.
expected, _ := hex.DecodeString("8442bc313f4626f1359e3b50122b6ce6fe66ddfe7d39d14e637eb4fd5b45beadab55198df6ab5368439792a23c87db70acb6156dc5ef957ac04f6276cf6093b84be77ff0849cc33e34b7254d5a8f65ad")
if !bytes.Equal(box, expected) {
t.Fatalf("box didn't match, got\n%x\n, expected\n%x", box, expected)
}
}
func TestAppend(t *testing.T) {
var key [32]byte
var nonce [24]byte
var message [8]byte
out := make([]byte, 4)
box := Seal(out, message[:], &nonce, &key)
if !bytes.Equal(box[:4], out[:4]) {
t.Fatalf("Seal didn't correctly append")
}
out = make([]byte, 4, 100)
box = Seal(out, message[:], &nonce, &key)
if !bytes.Equal(box[:4], out[:4]) {
t.Fatalf("Seal didn't correctly append with sufficient capacity.")
}
}
func benchmarkSealSize(b *testing.B, size int) {
message := make([]byte, size)
out := make([]byte, size+Overhead)
var nonce [24]byte
var key [32]byte
b.SetBytes(int64(size))
b.ResetTimer()
for i := 0; i < b.N; i++ {
out = Seal(out[:0], message, &nonce, &key)
}
}
func BenchmarkSeal8Bytes(b *testing.B) {
benchmarkSealSize(b, 8)
}
func BenchmarkSeal100Bytes(b *testing.B) {
benchmarkSealSize(b, 100)
}
func BenchmarkSeal1K(b *testing.B) {
benchmarkSealSize(b, 1024)
}
func BenchmarkSeal8K(b *testing.B) {
benchmarkSealSize(b, 8192)
}
func benchmarkOpenSize(b *testing.B, size int) {
msg := make([]byte, size)
result := make([]byte, size)
var nonce [24]byte
var key [32]byte
box := Seal(nil, msg, &nonce, &key)
b.SetBytes(int64(size))
b.ResetTimer()
for i := 0; i < b.N; i++ {
if _, ok := Open(result[:0], box, &nonce, &key); !ok {
panic("Open failed")
}
}
}
func BenchmarkOpen8Bytes(b *testing.B) {
benchmarkOpenSize(b, 8)
}
func BenchmarkOpen100Bytes(b *testing.B) {
benchmarkOpenSize(b, 100)
}
func BenchmarkOpen1K(b *testing.B) {
benchmarkOpenSize(b, 1024)
}
func BenchmarkOpen8K(b *testing.B) {
benchmarkOpenSize(b, 8192)
}

View File

@ -0,0 +1,33 @@
// Copyright 2012 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
/*
Package poly1305 implements Poly1305 one-time message authentication code as
specified in https://cr.yp.to/mac/poly1305-20050329.pdf.
Poly1305 is a fast, one-time authentication function. It is infeasible for an
attacker to generate an authenticator for a message without the key. However, a
key must only be used for a single message. Authenticating two different
messages with the same key allows an attacker to forge authenticators for other
messages with the same key.
Poly1305 was originally coupled with AES in order to make Poly1305-AES. AES was
used with a fixed key in order to generate one-time keys from an nonce.
However, in this package AES isn't used and the one-time key is specified
directly.
*/
package poly1305 // import "golang.org/x/crypto/poly1305"
import "crypto/subtle"
// TagSize is the size, in bytes, of a poly1305 authenticator.
const TagSize = 16
// Verify returns true if mac is a valid authenticator for m with the given
// key.
func Verify(mac *[16]byte, m []byte, key *[32]byte) bool {
var tmp [16]byte
Sum(&tmp, m, key)
return subtle.ConstantTimeCompare(tmp[:], mac[:]) == 1
}

View File

@ -0,0 +1,132 @@
// 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 (
"encoding/hex"
"flag"
"testing"
"unsafe"
)
var stressFlag = flag.Bool("stress", false, "run slow stress tests")
type test struct {
in string
key string
tag string
}
func (t *test) Input() []byte {
in, err := hex.DecodeString(t.in)
if err != nil {
panic(err)
}
return in
}
func (t *test) Key() [32]byte {
buf, err := hex.DecodeString(t.key)
if err != nil {
panic(err)
}
var key [32]byte
copy(key[:], buf[:32])
return key
}
func (t *test) Tag() [16]byte {
buf, err := hex.DecodeString(t.tag)
if err != nil {
panic(err)
}
var tag [16]byte
copy(tag[:], buf[:16])
return tag
}
func testSum(t *testing.T, unaligned bool, sumImpl func(tag *[TagSize]byte, msg []byte, key *[32]byte)) {
var tag [16]byte
for i, v := range testData {
in := v.Input()
if unaligned {
in = unalignBytes(in)
}
key := v.Key()
sumImpl(&tag, in, &key)
if tag != v.Tag() {
t.Errorf("%d: expected %x, got %x", i, v.Tag(), tag[:])
}
}
}
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, Sum) }
func TestSumUnaligned(t *testing.T) { testSum(t, true, Sum) }
func TestSumGeneric(t *testing.T) { testSum(t, false, sumGeneric) }
func TestSumGenericUnaligned(t *testing.T) { testSum(t, true, sumGeneric) }
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 Benchmark2M(b *testing.B) { benchmark(b, 2097152, 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
}

View File

@ -0,0 +1,22 @@
// Copyright 2012 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build amd64,!gccgo,!appengine
package poly1305
// This function is implemented in sum_amd64.s
//go:noescape
func poly1305(out *[16]byte, m *byte, mlen uint64, key *[32]byte)
// Sum generates an authenticator for m using a one-time key and puts the
// 16-byte result into out. Authenticating two different messages with the same
// key allows an attacker to forge messages at will.
func Sum(out *[16]byte, m []byte, key *[32]byte) {
var mPtr *byte
if len(m) > 0 {
mPtr = &m[0]
}
poly1305(out, mPtr, uint64(len(m)), key)
}

View File

@ -0,0 +1,125 @@
// Copyright 2012 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build amd64,!gccgo,!appengine
#include "textflag.h"
#define POLY1305_ADD(msg, h0, h1, h2) \
ADDQ 0(msg), h0; \
ADCQ 8(msg), h1; \
ADCQ $1, h2; \
LEAQ 16(msg), msg
#define POLY1305_MUL(h0, h1, h2, r0, r1, t0, t1, t2, t3) \
MOVQ r0, AX; \
MULQ h0; \
MOVQ AX, t0; \
MOVQ DX, t1; \
MOVQ r0, AX; \
MULQ h1; \
ADDQ AX, t1; \
ADCQ $0, DX; \
MOVQ r0, t2; \
IMULQ h2, t2; \
ADDQ DX, t2; \
\
MOVQ r1, AX; \
MULQ h0; \
ADDQ AX, t1; \
ADCQ $0, DX; \
MOVQ DX, h0; \
MOVQ r1, t3; \
IMULQ h2, t3; \
MOVQ r1, AX; \
MULQ h1; \
ADDQ AX, t2; \
ADCQ DX, t3; \
ADDQ h0, t2; \
ADCQ $0, t3; \
\
MOVQ t0, h0; \
MOVQ t1, h1; \
MOVQ t2, h2; \
ANDQ $3, h2; \
MOVQ t2, t0; \
ANDQ $0xFFFFFFFFFFFFFFFC, t0; \
ADDQ t0, h0; \
ADCQ t3, h1; \
ADCQ $0, h2; \
SHRQ $2, t3, t2; \
SHRQ $2, t3; \
ADDQ t2, h0; \
ADCQ t3, h1; \
ADCQ $0, h2
DATA ·poly1305Mask<>+0x00(SB)/8, $0x0FFFFFFC0FFFFFFF
DATA ·poly1305Mask<>+0x08(SB)/8, $0x0FFFFFFC0FFFFFFC
GLOBL ·poly1305Mask<>(SB), RODATA, $16
// func poly1305(out *[16]byte, m *byte, mlen uint64, key *[32]key)
TEXT ·poly1305(SB), $0-32
MOVQ out+0(FP), DI
MOVQ m+8(FP), SI
MOVQ mlen+16(FP), R15
MOVQ key+24(FP), AX
MOVQ 0(AX), R11
MOVQ 8(AX), R12
ANDQ ·poly1305Mask<>(SB), R11 // r0
ANDQ ·poly1305Mask<>+8(SB), R12 // r1
XORQ R8, R8 // h0
XORQ R9, R9 // h1
XORQ R10, R10 // h2
CMPQ R15, $16
JB bytes_between_0_and_15
loop:
POLY1305_ADD(SI, R8, R9, R10)
multiply:
POLY1305_MUL(R8, R9, R10, R11, R12, BX, CX, R13, R14)
SUBQ $16, R15
CMPQ R15, $16
JAE loop
bytes_between_0_and_15:
TESTQ R15, R15
JZ done
MOVQ $1, BX
XORQ CX, CX
XORQ R13, R13
ADDQ R15, SI
flush_buffer:
SHLQ $8, BX, CX
SHLQ $8, BX
MOVB -1(SI), R13
XORQ R13, BX
DECQ SI
DECQ R15
JNZ flush_buffer
ADDQ BX, R8
ADCQ CX, R9
ADCQ $0, R10
MOVQ $16, R15
JMP multiply
done:
MOVQ R8, AX
MOVQ R9, BX
SUBQ $0xFFFFFFFFFFFFFFFB, AX
SBBQ $0xFFFFFFFFFFFFFFFF, BX
SBBQ $3, R10
CMOVQCS R8, AX
CMOVQCS R9, BX
MOVQ key+24(FP), R8
ADDQ 16(R8), AX
ADCQ 24(R8), BX
MOVQ AX, 0(DI)
MOVQ BX, 8(DI)
RET

View File

@ -0,0 +1,22 @@
// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build arm,!gccgo,!appengine,!nacl
package poly1305
// This function is implemented in sum_arm.s
//go:noescape
func poly1305_auth_armv6(out *[16]byte, m *byte, mlen uint32, key *[32]byte)
// Sum generates an authenticator for m using a one-time key and puts the
// 16-byte result into out. Authenticating two different messages with the same
// key allows an attacker to forge messages at will.
func Sum(out *[16]byte, m []byte, key *[32]byte) {
var mPtr *byte
if len(m) > 0 {
mPtr = &m[0]
}
poly1305_auth_armv6(out, mPtr, uint32(len(m)), key)
}

View File

@ -0,0 +1,427 @@
// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build arm,!gccgo,!appengine,!nacl
#include "textflag.h"
// This code was translated into a form compatible with 5a from the public
// domain source by Andrew Moon: github.com/floodyberry/poly1305-opt/blob/master/app/extensions/poly1305.
DATA ·poly1305_init_constants_armv6<>+0x00(SB)/4, $0x3ffffff
DATA ·poly1305_init_constants_armv6<>+0x04(SB)/4, $0x3ffff03
DATA ·poly1305_init_constants_armv6<>+0x08(SB)/4, $0x3ffc0ff
DATA ·poly1305_init_constants_armv6<>+0x0c(SB)/4, $0x3f03fff
DATA ·poly1305_init_constants_armv6<>+0x10(SB)/4, $0x00fffff
GLOBL ·poly1305_init_constants_armv6<>(SB), 8, $20
// Warning: the linker may use R11 to synthesize certain instructions. Please
// take care and verify that no synthetic instructions use it.
TEXT poly1305_init_ext_armv6<>(SB), NOSPLIT, $0
// Needs 16 bytes of stack and 64 bytes of space pointed to by R0. (It
// might look like it's only 60 bytes of space but the final four bytes
// will be written by another function.) We need to skip over four
// bytes of stack because that's saving the value of 'g'.
ADD $4, R13, R8
MOVM.IB [R4-R7], (R8)
MOVM.IA.W (R1), [R2-R5]
MOVW $·poly1305_init_constants_armv6<>(SB), R7
MOVW R2, R8
MOVW R2>>26, R9
MOVW R3>>20, g
MOVW R4>>14, R11
MOVW R5>>8, R12
ORR R3<<6, R9, R9
ORR R4<<12, g, g
ORR R5<<18, R11, R11
MOVM.IA (R7), [R2-R6]
AND R8, R2, R2
AND R9, R3, R3
AND g, R4, R4
AND R11, R5, R5
AND R12, R6, R6
MOVM.IA.W [R2-R6], (R0)
EOR R2, R2, R2
EOR R3, R3, R3
EOR R4, R4, R4
EOR R5, R5, R5
EOR R6, R6, R6
MOVM.IA.W [R2-R6], (R0)
MOVM.IA.W (R1), [R2-R5]
MOVM.IA [R2-R6], (R0)
ADD $20, R13, R0
MOVM.DA (R0), [R4-R7]
RET
#define MOVW_UNALIGNED(Rsrc, Rdst, Rtmp, offset) \
MOVBU (offset+0)(Rsrc), Rtmp; \
MOVBU Rtmp, (offset+0)(Rdst); \
MOVBU (offset+1)(Rsrc), Rtmp; \
MOVBU Rtmp, (offset+1)(Rdst); \
MOVBU (offset+2)(Rsrc), Rtmp; \
MOVBU Rtmp, (offset+2)(Rdst); \
MOVBU (offset+3)(Rsrc), Rtmp; \
MOVBU Rtmp, (offset+3)(Rdst)
TEXT poly1305_blocks_armv6<>(SB), NOSPLIT, $0
// Needs 24 bytes of stack for saved registers and then 88 bytes of
// scratch space after that. We assume that 24 bytes at (R13) have
// already been used: four bytes for the link register saved in the
// prelude of poly1305_auth_armv6, four bytes for saving the value of g
// in that function and 16 bytes of scratch space used around
// poly1305_finish_ext_armv6_skip1.
ADD $24, R13, R12
MOVM.IB [R4-R8, R14], (R12)
MOVW R0, 88(R13)
MOVW R1, 92(R13)
MOVW R2, 96(R13)
MOVW R1, R14
MOVW R2, R12
MOVW 56(R0), R8
WORD $0xe1180008 // TST R8, R8 not working see issue 5921
EOR R6, R6, R6
MOVW.EQ $(1<<24), R6
MOVW R6, 84(R13)
ADD $116, R13, g
MOVM.IA (R0), [R0-R9]
MOVM.IA [R0-R4], (g)
CMP $16, R12
BLO poly1305_blocks_armv6_done
poly1305_blocks_armv6_mainloop:
WORD $0xe31e0003 // TST R14, #3 not working see issue 5921
BEQ poly1305_blocks_armv6_mainloop_aligned
ADD $100, R13, g
MOVW_UNALIGNED(R14, g, R0, 0)
MOVW_UNALIGNED(R14, g, R0, 4)
MOVW_UNALIGNED(R14, g, R0, 8)
MOVW_UNALIGNED(R14, g, R0, 12)
MOVM.IA (g), [R0-R3]
ADD $16, R14
B poly1305_blocks_armv6_mainloop_loaded
poly1305_blocks_armv6_mainloop_aligned:
MOVM.IA.W (R14), [R0-R3]
poly1305_blocks_armv6_mainloop_loaded:
MOVW R0>>26, g
MOVW R1>>20, R11
MOVW R2>>14, R12
MOVW R14, 92(R13)
MOVW R3>>8, R4
ORR R1<<6, g, g
ORR R2<<12, R11, R11
ORR R3<<18, R12, R12
BIC $0xfc000000, R0, R0
BIC $0xfc000000, g, g
MOVW 84(R13), R3
BIC $0xfc000000, R11, R11
BIC $0xfc000000, R12, R12
ADD R0, R5, R5
ADD g, R6, R6
ORR R3, R4, R4
ADD R11, R7, R7
ADD $116, R13, R14
ADD R12, R8, R8
ADD R4, R9, R9
MOVM.IA (R14), [R0-R4]
MULLU R4, R5, (R11, g)
MULLU R3, R5, (R14, R12)
MULALU R3, R6, (R11, g)
MULALU R2, R6, (R14, R12)
MULALU R2, R7, (R11, g)
MULALU R1, R7, (R14, R12)
ADD R4<<2, R4, R4
ADD R3<<2, R3, R3
MULALU R1, R8, (R11, g)
MULALU R0, R8, (R14, R12)
MULALU R0, R9, (R11, g)
MULALU R4, R9, (R14, R12)
MOVW g, 76(R13)
MOVW R11, 80(R13)
MOVW R12, 68(R13)
MOVW R14, 72(R13)
MULLU R2, R5, (R11, g)
MULLU R1, R5, (R14, R12)
MULALU R1, R6, (R11, g)
MULALU R0, R6, (R14, R12)
MULALU R0, R7, (R11, g)
MULALU R4, R7, (R14, R12)
ADD R2<<2, R2, R2
ADD R1<<2, R1, R1
MULALU R4, R8, (R11, g)
MULALU R3, R8, (R14, R12)
MULALU R3, R9, (R11, g)
MULALU R2, R9, (R14, R12)
MOVW g, 60(R13)
MOVW R11, 64(R13)
MOVW R12, 52(R13)
MOVW R14, 56(R13)
MULLU R0, R5, (R11, g)
MULALU R4, R6, (R11, g)
MULALU R3, R7, (R11, g)
MULALU R2, R8, (R11, g)
MULALU R1, R9, (R11, g)
ADD $52, R13, R0
MOVM.IA (R0), [R0-R7]
MOVW g>>26, R12
MOVW R4>>26, R14
ORR R11<<6, R12, R12
ORR R5<<6, R14, R14
BIC $0xfc000000, g, g
BIC $0xfc000000, R4, R4
ADD.S R12, R0, R0
ADC $0, R1, R1
ADD.S R14, R6, R6
ADC $0, R7, R7
MOVW R0>>26, R12
MOVW R6>>26, R14
ORR R1<<6, R12, R12
ORR R7<<6, R14, R14
BIC $0xfc000000, R0, R0
BIC $0xfc000000, R6, R6
ADD R14<<2, R14, R14
ADD.S R12, R2, R2
ADC $0, R3, R3
ADD R14, g, g
MOVW R2>>26, R12
MOVW g>>26, R14
ORR R3<<6, R12, R12
BIC $0xfc000000, g, R5
BIC $0xfc000000, R2, R7
ADD R12, R4, R4
ADD R14, R0, R0
MOVW R4>>26, R12
BIC $0xfc000000, R4, R8
ADD R12, R6, R9
MOVW 96(R13), R12
MOVW 92(R13), R14
MOVW R0, R6
CMP $32, R12
SUB $16, R12, R12
MOVW R12, 96(R13)
BHS poly1305_blocks_armv6_mainloop
poly1305_blocks_armv6_done:
MOVW 88(R13), R12
MOVW R5, 20(R12)
MOVW R6, 24(R12)
MOVW R7, 28(R12)
MOVW R8, 32(R12)
MOVW R9, 36(R12)
ADD $48, R13, R0
MOVM.DA (R0), [R4-R8, R14]
RET
#define MOVHUP_UNALIGNED(Rsrc, Rdst, Rtmp) \
MOVBU.P 1(Rsrc), Rtmp; \
MOVBU.P Rtmp, 1(Rdst); \
MOVBU.P 1(Rsrc), Rtmp; \
MOVBU.P Rtmp, 1(Rdst)
#define MOVWP_UNALIGNED(Rsrc, Rdst, Rtmp) \
MOVHUP_UNALIGNED(Rsrc, Rdst, Rtmp); \
MOVHUP_UNALIGNED(Rsrc, Rdst, Rtmp)
// func poly1305_auth_armv6(out *[16]byte, m *byte, mlen uint32, key *[32]key)
TEXT ·poly1305_auth_armv6(SB), $196-16
// The value 196, just above, is the sum of 64 (the size of the context
// structure) and 132 (the amount of stack needed).
//
// At this point, the stack pointer (R13) has been moved down. It
// points to the saved link register and there's 196 bytes of free
// space above it.
//
// The stack for this function looks like:
//
// +---------------------
// |
// | 64 bytes of context structure
// |
// +---------------------
// |
// | 112 bytes for poly1305_blocks_armv6
// |
// +---------------------
// | 16 bytes of final block, constructed at
// | poly1305_finish_ext_armv6_skip8
// +---------------------
// | four bytes of saved 'g'
// +---------------------
// | lr, saved by prelude <- R13 points here
// +---------------------
MOVW g, 4(R13)
MOVW out+0(FP), R4
MOVW m+4(FP), R5
MOVW mlen+8(FP), R6
MOVW key+12(FP), R7
ADD $136, R13, R0 // 136 = 4 + 4 + 16 + 112
MOVW R7, R1
// poly1305_init_ext_armv6 will write to the stack from R13+4, but
// that's ok because none of the other values have been written yet.
BL poly1305_init_ext_armv6<>(SB)
BIC.S $15, R6, R2
BEQ poly1305_auth_armv6_noblocks
ADD $136, R13, R0
MOVW R5, R1
ADD R2, R5, R5
SUB R2, R6, R6
BL poly1305_blocks_armv6<>(SB)
poly1305_auth_armv6_noblocks:
ADD $136, R13, R0
MOVW R5, R1
MOVW R6, R2
MOVW R4, R3
MOVW R0, R5
MOVW R1, R6
MOVW R2, R7
MOVW R3, R8
AND.S R2, R2, R2
BEQ poly1305_finish_ext_armv6_noremaining
EOR R0, R0
ADD $8, R13, R9 // 8 = offset to 16 byte scratch space
MOVW R0, (R9)
MOVW R0, 4(R9)
MOVW R0, 8(R9)
MOVW R0, 12(R9)
WORD $0xe3110003 // TST R1, #3 not working see issue 5921
BEQ poly1305_finish_ext_armv6_aligned
WORD $0xe3120008 // TST R2, #8 not working see issue 5921
BEQ poly1305_finish_ext_armv6_skip8
MOVWP_UNALIGNED(R1, R9, g)
MOVWP_UNALIGNED(R1, R9, g)
poly1305_finish_ext_armv6_skip8:
WORD $0xe3120004 // TST $4, R2 not working see issue 5921
BEQ poly1305_finish_ext_armv6_skip4
MOVWP_UNALIGNED(R1, R9, g)
poly1305_finish_ext_armv6_skip4:
WORD $0xe3120002 // TST $2, R2 not working see issue 5921
BEQ poly1305_finish_ext_armv6_skip2
MOVHUP_UNALIGNED(R1, R9, g)
B poly1305_finish_ext_armv6_skip2
poly1305_finish_ext_armv6_aligned:
WORD $0xe3120008 // TST R2, #8 not working see issue 5921
BEQ poly1305_finish_ext_armv6_skip8_aligned
MOVM.IA.W (R1), [g-R11]
MOVM.IA.W [g-R11], (R9)
poly1305_finish_ext_armv6_skip8_aligned:
WORD $0xe3120004 // TST $4, R2 not working see issue 5921
BEQ poly1305_finish_ext_armv6_skip4_aligned
MOVW.P 4(R1), g
MOVW.P g, 4(R9)
poly1305_finish_ext_armv6_skip4_aligned:
WORD $0xe3120002 // TST $2, R2 not working see issue 5921
BEQ poly1305_finish_ext_armv6_skip2
MOVHU.P 2(R1), g
MOVH.P g, 2(R9)
poly1305_finish_ext_armv6_skip2:
WORD $0xe3120001 // TST $1, R2 not working see issue 5921
BEQ poly1305_finish_ext_armv6_skip1
MOVBU.P 1(R1), g
MOVBU.P g, 1(R9)
poly1305_finish_ext_armv6_skip1:
MOVW $1, R11
MOVBU R11, 0(R9)
MOVW R11, 56(R5)
MOVW R5, R0
ADD $8, R13, R1
MOVW $16, R2
BL poly1305_blocks_armv6<>(SB)
poly1305_finish_ext_armv6_noremaining:
MOVW 20(R5), R0
MOVW 24(R5), R1
MOVW 28(R5), R2
MOVW 32(R5), R3
MOVW 36(R5), R4
MOVW R4>>26, R12
BIC $0xfc000000, R4, R4
ADD R12<<2, R12, R12
ADD R12, R0, R0
MOVW R0>>26, R12
BIC $0xfc000000, R0, R0
ADD R12, R1, R1
MOVW R1>>26, R12
BIC $0xfc000000, R1, R1
ADD R12, R2, R2
MOVW R2>>26, R12
BIC $0xfc000000, R2, R2
ADD R12, R3, R3
MOVW R3>>26, R12
BIC $0xfc000000, R3, R3
ADD R12, R4, R4
ADD $5, R0, R6
MOVW R6>>26, R12
BIC $0xfc000000, R6, R6
ADD R12, R1, R7
MOVW R7>>26, R12
BIC $0xfc000000, R7, R7
ADD R12, R2, g
MOVW g>>26, R12
BIC $0xfc000000, g, g
ADD R12, R3, R11
MOVW $-(1<<26), R12
ADD R11>>26, R12, R12
BIC $0xfc000000, R11, R11
ADD R12, R4, R9
MOVW R9>>31, R12
SUB $1, R12
AND R12, R6, R6
AND R12, R7, R7
AND R12, g, g
AND R12, R11, R11
AND R12, R9, R9
MVN R12, R12
AND R12, R0, R0
AND R12, R1, R1
AND R12, R2, R2
AND R12, R3, R3
AND R12, R4, R4
ORR R6, R0, R0
ORR R7, R1, R1
ORR g, R2, R2
ORR R11, R3, R3
ORR R9, R4, R4
ORR R1<<26, R0, R0
MOVW R1>>6, R1
ORR R2<<20, R1, R1
MOVW R2>>12, R2
ORR R3<<14, R2, R2
MOVW R3>>18, R3
ORR R4<<8, R3, R3
MOVW 40(R5), R6
MOVW 44(R5), R7
MOVW 48(R5), g
MOVW 52(R5), R11
ADD.S R6, R0, R0
ADC.S R7, R1, R1
ADC.S g, R2, R2
ADC.S R11, R3, R3
MOVM.IA [R0-R3], (R8)
MOVW R5, R12
EOR R0, R0, R0
EOR R1, R1, R1
EOR R2, R2, R2
EOR R3, R3, R3
EOR R4, R4, R4
EOR R5, R5, R5
EOR R6, R6, R6
EOR R7, R7, R7
MOVM.IA.W [R0-R7], (R12)
MOVM.IA [R0-R7], (R12)
MOVW 4(R13), g
RET

View File

@ -0,0 +1,14 @@
// Copyright 2018 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 s390x,!go1.11 !arm,!amd64,!s390x gccgo appengine nacl
package poly1305
// 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) {
sumGeneric(out, msg, key)
}

View File

@ -0,0 +1,139 @@
// 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 "encoding/binary"
// sumGeneric generates an authenticator for msg using a one-time key and
// puts the 16-byte result into out. This is the generic implementation of
// Sum and should be called if no assembly implementation is available.
func sumGeneric(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)
}

View File

@ -0,0 +1,49 @@
// Copyright 2018 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 s390x,go1.11,!gccgo,!appengine
package poly1305
// hasVectorFacility reports whether the machine supports
// the vector facility (vx).
func hasVectorFacility() bool
// hasVMSLFacility reports whether the machine supports
// Vector Multiply Sum Logical (VMSL).
func hasVMSLFacility() bool
var hasVX = hasVectorFacility()
var hasVMSL = hasVMSLFacility()
// poly1305vx is an assembly implementation of Poly1305 that uses vector
// instructions. It must only be called if the vector facility (vx) is
// available.
//go:noescape
func poly1305vx(out *[16]byte, m *byte, mlen uint64, key *[32]byte)
// poly1305vmsl is an assembly implementation of Poly1305 that uses vector
// instructions, including VMSL. It must only be called if the vector facility (vx) is
// available and if VMSL is supported.
//go:noescape
func poly1305vmsl(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) {
if hasVX {
var mPtr *byte
if len(m) > 0 {
mPtr = &m[0]
}
if hasVMSL && len(m) > 256 {
poly1305vmsl(out, mPtr, uint64(len(m)), key)
} else {
poly1305vx(out, mPtr, uint64(len(m)), key)
}
} else {
sumGeneric(out, m, key)
}
}

View File

@ -0,0 +1,400 @@
// Copyright 2018 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 s390x,go1.11,!gccgo,!appengine
#include "textflag.h"
// Implementation of Poly1305 using the vector facility (vx).
// constants
#define MOD26 V0
#define EX0 V1
#define EX1 V2
#define EX2 V3
// temporaries
#define T_0 V4
#define T_1 V5
#define T_2 V6
#define T_3 V7
#define T_4 V8
// key (r)
#define R_0 V9
#define R_1 V10
#define R_2 V11
#define R_3 V12
#define R_4 V13
#define R5_1 V14
#define R5_2 V15
#define R5_3 V16
#define R5_4 V17
#define RSAVE_0 R5
#define RSAVE_1 R6
#define RSAVE_2 R7
#define RSAVE_3 R8
#define RSAVE_4 R9
#define R5SAVE_1 V28
#define R5SAVE_2 V29
#define R5SAVE_3 V30
#define R5SAVE_4 V31
// message block
#define F_0 V18
#define F_1 V19
#define F_2 V20
#define F_3 V21
#define F_4 V22
// accumulator
#define H_0 V23
#define H_1 V24
#define H_2 V25
#define H_3 V26
#define H_4 V27
GLOBL ·keyMask<>(SB), RODATA, $16
DATA ·keyMask<>+0(SB)/8, $0xffffff0ffcffff0f
DATA ·keyMask<>+8(SB)/8, $0xfcffff0ffcffff0f
GLOBL ·bswapMask<>(SB), RODATA, $16
DATA ·bswapMask<>+0(SB)/8, $0x0f0e0d0c0b0a0908
DATA ·bswapMask<>+8(SB)/8, $0x0706050403020100
GLOBL ·constants<>(SB), RODATA, $64
// MOD26
DATA ·constants<>+0(SB)/8, $0x3ffffff
DATA ·constants<>+8(SB)/8, $0x3ffffff
// EX0
DATA ·constants<>+16(SB)/8, $0x0006050403020100
DATA ·constants<>+24(SB)/8, $0x1016151413121110
// EX1
DATA ·constants<>+32(SB)/8, $0x060c0b0a09080706
DATA ·constants<>+40(SB)/8, $0x161c1b1a19181716
// EX2
DATA ·constants<>+48(SB)/8, $0x0d0d0d0d0d0f0e0d
DATA ·constants<>+56(SB)/8, $0x1d1d1d1d1d1f1e1d
// h = (f*g) % (2**130-5) [partial reduction]
#define MULTIPLY(f0, f1, f2, f3, f4, g0, g1, g2, g3, g4, g51, g52, g53, g54, h0, h1, h2, h3, h4) \
VMLOF f0, g0, h0 \
VMLOF f0, g1, h1 \
VMLOF f0, g2, h2 \
VMLOF f0, g3, h3 \
VMLOF f0, g4, h4 \
VMLOF f1, g54, T_0 \
VMLOF f1, g0, T_1 \
VMLOF f1, g1, T_2 \
VMLOF f1, g2, T_3 \
VMLOF f1, g3, T_4 \
VMALOF f2, g53, h0, h0 \
VMALOF f2, g54, h1, h1 \
VMALOF f2, g0, h2, h2 \
VMALOF f2, g1, h3, h3 \
VMALOF f2, g2, h4, h4 \
VMALOF f3, g52, T_0, T_0 \
VMALOF f3, g53, T_1, T_1 \
VMALOF f3, g54, T_2, T_2 \
VMALOF f3, g0, T_3, T_3 \
VMALOF f3, g1, T_4, T_4 \
VMALOF f4, g51, h0, h0 \
VMALOF f4, g52, h1, h1 \
VMALOF f4, g53, h2, h2 \
VMALOF f4, g54, h3, h3 \
VMALOF f4, g0, h4, h4 \
VAG T_0, h0, h0 \
VAG T_1, h1, h1 \
VAG T_2, h2, h2 \
VAG T_3, h3, h3 \
VAG T_4, h4, h4
// carry h0->h1 h3->h4, h1->h2 h4->h0, h0->h1 h2->h3, h3->h4
#define REDUCE(h0, h1, h2, h3, h4) \
VESRLG $26, h0, T_0 \
VESRLG $26, h3, T_1 \
VN MOD26, h0, h0 \
VN MOD26, h3, h3 \
VAG T_0, h1, h1 \
VAG T_1, h4, h4 \
VESRLG $26, h1, T_2 \
VESRLG $26, h4, T_3 \
VN MOD26, h1, h1 \
VN MOD26, h4, h4 \
VESLG $2, T_3, T_4 \
VAG T_3, T_4, T_4 \
VAG T_2, h2, h2 \
VAG T_4, h0, h0 \
VESRLG $26, h2, T_0 \
VESRLG $26, h0, T_1 \
VN MOD26, h2, h2 \
VN MOD26, h0, h0 \
VAG T_0, h3, h3 \
VAG T_1, h1, h1 \
VESRLG $26, h3, T_2 \
VN MOD26, h3, h3 \
VAG T_2, h4, h4
// expand in0 into d[0] and in1 into d[1]
#define EXPAND(in0, in1, d0, d1, d2, d3, d4) \
VGBM $0x0707, d1 \ // d1=tmp
VPERM in0, in1, EX2, d4 \
VPERM in0, in1, EX0, d0 \
VPERM in0, in1, EX1, d2 \
VN d1, d4, d4 \
VESRLG $26, d0, d1 \
VESRLG $30, d2, d3 \
VESRLG $4, d2, d2 \
VN MOD26, d0, d0 \
VN MOD26, d1, d1 \
VN MOD26, d2, d2 \
VN MOD26, d3, d3
// pack h4:h0 into h1:h0 (no carry)
#define PACK(h0, h1, h2, h3, h4) \
VESLG $26, h1, h1 \
VESLG $26, h3, h3 \
VO h0, h1, h0 \
VO h2, h3, h2 \
VESLG $4, h2, h2 \
VLEIB $7, $48, h1 \
VSLB h1, h2, h2 \
VO h0, h2, h0 \
VLEIB $7, $104, h1 \
VSLB h1, h4, h3 \
VO h3, h0, h0 \
VLEIB $7, $24, h1 \
VSRLB h1, h4, h1
// if h > 2**130-5 then h -= 2**130-5
#define MOD(h0, h1, t0, t1, t2) \
VZERO t0 \
VLEIG $1, $5, t0 \
VACCQ h0, t0, t1 \
VAQ h0, t0, t0 \
VONE t2 \
VLEIG $1, $-4, t2 \
VAQ t2, t1, t1 \
VACCQ h1, t1, t1 \
VONE t2 \
VAQ t2, t1, t1 \
VN h0, t1, t2 \
VNC t0, t1, t1 \
VO t1, t2, h0
// func poly1305vx(out *[16]byte, m *byte, mlen uint64, key *[32]key)
TEXT ·poly1305vx(SB), $0-32
// This code processes up to 2 blocks (32 bytes) per iteration
// using the algorithm described in:
// NEON crypto, Daniel J. Bernstein & Peter Schwabe
// https://cryptojedi.org/papers/neoncrypto-20120320.pdf
LMG out+0(FP), R1, R4 // R1=out, R2=m, R3=mlen, R4=key
// load MOD26, EX0, EX1 and EX2
MOVD $·constants<>(SB), R5
VLM (R5), MOD26, EX2
// setup r
VL (R4), T_0
MOVD $·keyMask<>(SB), R6
VL (R6), T_1
VN T_0, T_1, T_0
EXPAND(T_0, T_0, R_0, R_1, R_2, R_3, R_4)
// setup r*5
VLEIG $0, $5, T_0
VLEIG $1, $5, T_0
// store r (for final block)
VMLOF T_0, R_1, R5SAVE_1
VMLOF T_0, R_2, R5SAVE_2
VMLOF T_0, R_3, R5SAVE_3
VMLOF T_0, R_4, R5SAVE_4
VLGVG $0, R_0, RSAVE_0
VLGVG $0, R_1, RSAVE_1
VLGVG $0, R_2, RSAVE_2
VLGVG $0, R_3, RSAVE_3
VLGVG $0, R_4, RSAVE_4
// skip r**2 calculation
CMPBLE R3, $16, skip
// calculate r**2
MULTIPLY(R_0, R_1, R_2, R_3, R_4, R_0, R_1, R_2, R_3, R_4, R5SAVE_1, R5SAVE_2, R5SAVE_3, R5SAVE_4, H_0, H_1, H_2, H_3, H_4)
REDUCE(H_0, H_1, H_2, H_3, H_4)
VLEIG $0, $5, T_0
VLEIG $1, $5, T_0
VMLOF T_0, H_1, R5_1
VMLOF T_0, H_2, R5_2
VMLOF T_0, H_3, R5_3
VMLOF T_0, H_4, R5_4
VLR H_0, R_0
VLR H_1, R_1
VLR H_2, R_2
VLR H_3, R_3
VLR H_4, R_4
// initialize h
VZERO H_0
VZERO H_1
VZERO H_2
VZERO H_3
VZERO H_4
loop:
CMPBLE R3, $32, b2
VLM (R2), T_0, T_1
SUB $32, R3
MOVD $32(R2), R2
EXPAND(T_0, T_1, F_0, F_1, F_2, F_3, F_4)
VLEIB $4, $1, F_4
VLEIB $12, $1, F_4
multiply:
VAG H_0, F_0, F_0
VAG H_1, F_1, F_1
VAG H_2, F_2, F_2
VAG H_3, F_3, F_3
VAG H_4, F_4, F_4
MULTIPLY(F_0, F_1, F_2, F_3, F_4, R_0, R_1, R_2, R_3, R_4, R5_1, R5_2, R5_3, R5_4, H_0, H_1, H_2, H_3, H_4)
REDUCE(H_0, H_1, H_2, H_3, H_4)
CMPBNE R3, $0, loop
finish:
// sum vectors
VZERO T_0
VSUMQG H_0, T_0, H_0
VSUMQG H_1, T_0, H_1
VSUMQG H_2, T_0, H_2
VSUMQG H_3, T_0, H_3
VSUMQG H_4, T_0, H_4
// h may be >= 2*(2**130-5) so we need to reduce it again
REDUCE(H_0, H_1, H_2, H_3, H_4)
// carry h1->h4
VESRLG $26, H_1, T_1
VN MOD26, H_1, H_1
VAQ T_1, H_2, H_2
VESRLG $26, H_2, T_2
VN MOD26, H_2, H_2
VAQ T_2, H_3, H_3
VESRLG $26, H_3, T_3
VN MOD26, H_3, H_3
VAQ T_3, H_4, H_4
// h is now < 2*(2**130-5)
// pack h into h1 (hi) and h0 (lo)
PACK(H_0, H_1, H_2, H_3, H_4)
// if h > 2**130-5 then h -= 2**130-5
MOD(H_0, H_1, T_0, T_1, T_2)
// h += s
MOVD $·bswapMask<>(SB), R5
VL (R5), T_1
VL 16(R4), T_0
VPERM T_0, T_0, T_1, T_0 // reverse bytes (to big)
VAQ T_0, H_0, H_0
VPERM H_0, H_0, T_1, H_0 // reverse bytes (to little)
VST H_0, (R1)
RET
b2:
CMPBLE R3, $16, b1
// 2 blocks remaining
SUB $17, R3
VL (R2), T_0
VLL R3, 16(R2), T_1
ADD $1, R3
MOVBZ $1, R0
CMPBEQ R3, $16, 2(PC)
VLVGB R3, R0, T_1
EXPAND(T_0, T_1, F_0, F_1, F_2, F_3, F_4)
CMPBNE R3, $16, 2(PC)
VLEIB $12, $1, F_4
VLEIB $4, $1, F_4
// setup [r²,r]
VLVGG $1, RSAVE_0, R_0
VLVGG $1, RSAVE_1, R_1
VLVGG $1, RSAVE_2, R_2
VLVGG $1, RSAVE_3, R_3
VLVGG $1, RSAVE_4, R_4
VPDI $0, R5_1, R5SAVE_1, R5_1
VPDI $0, R5_2, R5SAVE_2, R5_2
VPDI $0, R5_3, R5SAVE_3, R5_3
VPDI $0, R5_4, R5SAVE_4, R5_4
MOVD $0, R3
BR multiply
skip:
VZERO H_0
VZERO H_1
VZERO H_2
VZERO H_3
VZERO H_4
CMPBEQ R3, $0, finish
b1:
// 1 block remaining
SUB $1, R3
VLL R3, (R2), T_0
ADD $1, R3
MOVBZ $1, R0
CMPBEQ R3, $16, 2(PC)
VLVGB R3, R0, T_0
VZERO T_1
EXPAND(T_0, T_1, F_0, F_1, F_2, F_3, F_4)
CMPBNE R3, $16, 2(PC)
VLEIB $4, $1, F_4
VLEIG $1, $1, R_0
VZERO R_1
VZERO R_2
VZERO R_3
VZERO R_4
VZERO R5_1
VZERO R5_2
VZERO R5_3
VZERO R5_4
// setup [r, 1]
VLVGG $0, RSAVE_0, R_0
VLVGG $0, RSAVE_1, R_1
VLVGG $0, RSAVE_2, R_2
VLVGG $0, RSAVE_3, R_3
VLVGG $0, RSAVE_4, R_4
VPDI $0, R5SAVE_1, R5_1, R5_1
VPDI $0, R5SAVE_2, R5_2, R5_2
VPDI $0, R5SAVE_3, R5_3, R5_3
VPDI $0, R5SAVE_4, R5_4, R5_4
MOVD $0, R3
BR multiply
TEXT ·hasVectorFacility(SB), NOSPLIT, $24-1
MOVD $x-24(SP), R1
XC $24, 0(R1), 0(R1) // clear the storage
MOVD $2, R0 // R0 is the number of double words stored -1
WORD $0xB2B01000 // STFLE 0(R1)
XOR R0, R0 // reset the value of R0
MOVBZ z-8(SP), R1
AND $0x40, R1
BEQ novector
vectorinstalled:
// check if the vector instruction has been enabled
VLEIB $0, $0xF, V16
VLGVB $0, V16, R1
CMPBNE R1, $0xF, novector
MOVB $1, ret+0(FP) // have vx
RET
novector:
MOVB $0, ret+0(FP) // no vx
RET

View File

@ -0,0 +1,931 @@
// Copyright 2018 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 s390x,go1.11,!gccgo,!appengine
#include "textflag.h"
// Implementation of Poly1305 using the vector facility (vx) and the VMSL instruction.
// constants
#define EX0 V1
#define EX1 V2
#define EX2 V3
// temporaries
#define T_0 V4
#define T_1 V5
#define T_2 V6
#define T_3 V7
#define T_4 V8
#define T_5 V9
#define T_6 V10
#define T_7 V11
#define T_8 V12
#define T_9 V13
#define T_10 V14
// r**2 & r**4
#define R_0 V15
#define R_1 V16
#define R_2 V17
#define R5_1 V18
#define R5_2 V19
// key (r)
#define RSAVE_0 R7
#define RSAVE_1 R8
#define RSAVE_2 R9
#define R5SAVE_1 R10
#define R5SAVE_2 R11
// message block
#define M0 V20
#define M1 V21
#define M2 V22
#define M3 V23
#define M4 V24
#define M5 V25
// accumulator
#define H0_0 V26
#define H1_0 V27
#define H2_0 V28
#define H0_1 V29
#define H1_1 V30
#define H2_1 V31
GLOBL ·keyMask<>(SB), RODATA, $16
DATA ·keyMask<>+0(SB)/8, $0xffffff0ffcffff0f
DATA ·keyMask<>+8(SB)/8, $0xfcffff0ffcffff0f
GLOBL ·bswapMask<>(SB), RODATA, $16
DATA ·bswapMask<>+0(SB)/8, $0x0f0e0d0c0b0a0908
DATA ·bswapMask<>+8(SB)/8, $0x0706050403020100
GLOBL ·constants<>(SB), RODATA, $48
// EX0
DATA ·constants<>+0(SB)/8, $0x18191a1b1c1d1e1f
DATA ·constants<>+8(SB)/8, $0x0000050403020100
// EX1
DATA ·constants<>+16(SB)/8, $0x18191a1b1c1d1e1f
DATA ·constants<>+24(SB)/8, $0x00000a0908070605
// EX2
DATA ·constants<>+32(SB)/8, $0x18191a1b1c1d1e1f
DATA ·constants<>+40(SB)/8, $0x0000000f0e0d0c0b
GLOBL ·c<>(SB), RODATA, $48
// EX0
DATA ·c<>+0(SB)/8, $0x0000050403020100
DATA ·c<>+8(SB)/8, $0x0000151413121110
// EX1
DATA ·c<>+16(SB)/8, $0x00000a0908070605
DATA ·c<>+24(SB)/8, $0x00001a1918171615
// EX2
DATA ·c<>+32(SB)/8, $0x0000000f0e0d0c0b
DATA ·c<>+40(SB)/8, $0x0000001f1e1d1c1b
GLOBL ·reduce<>(SB), RODATA, $32
// 44 bit
DATA ·reduce<>+0(SB)/8, $0x0
DATA ·reduce<>+8(SB)/8, $0xfffffffffff
// 42 bit
DATA ·reduce<>+16(SB)/8, $0x0
DATA ·reduce<>+24(SB)/8, $0x3ffffffffff
// h = (f*g) % (2**130-5) [partial reduction]
// uses T_0...T_9 temporary registers
// input: m02_0, m02_1, m02_2, m13_0, m13_1, m13_2, r_0, r_1, r_2, r5_1, r5_2, m4_0, m4_1, m4_2, m5_0, m5_1, m5_2
// temp: t0, t1, t2, t3, t4, t5, t6, t7, t8, t9
// output: m02_0, m02_1, m02_2, m13_0, m13_1, m13_2
#define MULTIPLY(m02_0, m02_1, m02_2, m13_0, m13_1, m13_2, r_0, r_1, r_2, r5_1, r5_2, m4_0, m4_1, m4_2, m5_0, m5_1, m5_2, t0, t1, t2, t3, t4, t5, t6, t7, t8, t9) \
\ // Eliminate the dependency for the last 2 VMSLs
VMSLG m02_0, r_2, m4_2, m4_2 \
VMSLG m13_0, r_2, m5_2, m5_2 \ // 8 VMSLs pipelined
VMSLG m02_0, r_0, m4_0, m4_0 \
VMSLG m02_1, r5_2, V0, T_0 \
VMSLG m02_0, r_1, m4_1, m4_1 \
VMSLG m02_1, r_0, V0, T_1 \
VMSLG m02_1, r_1, V0, T_2 \
VMSLG m02_2, r5_1, V0, T_3 \
VMSLG m02_2, r5_2, V0, T_4 \
VMSLG m13_0, r_0, m5_0, m5_0 \
VMSLG m13_1, r5_2, V0, T_5 \
VMSLG m13_0, r_1, m5_1, m5_1 \
VMSLG m13_1, r_0, V0, T_6 \
VMSLG m13_1, r_1, V0, T_7 \
VMSLG m13_2, r5_1, V0, T_8 \
VMSLG m13_2, r5_2, V0, T_9 \
VMSLG m02_2, r_0, m4_2, m4_2 \
VMSLG m13_2, r_0, m5_2, m5_2 \
VAQ m4_0, T_0, m02_0 \
VAQ m4_1, T_1, m02_1 \
VAQ m5_0, T_5, m13_0 \
VAQ m5_1, T_6, m13_1 \
VAQ m02_0, T_3, m02_0 \
VAQ m02_1, T_4, m02_1 \
VAQ m13_0, T_8, m13_0 \
VAQ m13_1, T_9, m13_1 \
VAQ m4_2, T_2, m02_2 \
VAQ m5_2, T_7, m13_2 \
// SQUARE uses three limbs of r and r_2*5 to output square of r
// uses T_1, T_5 and T_7 temporary registers
// input: r_0, r_1, r_2, r5_2
// temp: TEMP0, TEMP1, TEMP2
// output: p0, p1, p2
#define SQUARE(r_0, r_1, r_2, r5_2, p0, p1, p2, TEMP0, TEMP1, TEMP2) \
VMSLG r_0, r_0, p0, p0 \
VMSLG r_1, r5_2, V0, TEMP0 \
VMSLG r_2, r5_2, p1, p1 \
VMSLG r_0, r_1, V0, TEMP1 \
VMSLG r_1, r_1, p2, p2 \
VMSLG r_0, r_2, V0, TEMP2 \
VAQ TEMP0, p0, p0 \
VAQ TEMP1, p1, p1 \
VAQ TEMP2, p2, p2 \
VAQ TEMP0, p0, p0 \
VAQ TEMP1, p1, p1 \
VAQ TEMP2, p2, p2 \
// carry h0->h1->h2->h0 || h3->h4->h5->h3
// uses T_2, T_4, T_5, T_7, T_8, T_9
// t6, t7, t8, t9, t10, t11
// input: h0, h1, h2, h3, h4, h5
// temp: t0, t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11
// output: h0, h1, h2, h3, h4, h5
#define REDUCE(h0, h1, h2, h3, h4, h5, t0, t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11) \
VLM (R12), t6, t7 \ // 44 and 42 bit clear mask
VLEIB $7, $0x28, t10 \ // 5 byte shift mask
VREPIB $4, t8 \ // 4 bit shift mask
VREPIB $2, t11 \ // 2 bit shift mask
VSRLB t10, h0, t0 \ // h0 byte shift
VSRLB t10, h1, t1 \ // h1 byte shift
VSRLB t10, h2, t2 \ // h2 byte shift
VSRLB t10, h3, t3 \ // h3 byte shift
VSRLB t10, h4, t4 \ // h4 byte shift
VSRLB t10, h5, t5 \ // h5 byte shift
VSRL t8, t0, t0 \ // h0 bit shift
VSRL t8, t1, t1 \ // h2 bit shift
VSRL t11, t2, t2 \ // h2 bit shift
VSRL t8, t3, t3 \ // h3 bit shift
VSRL t8, t4, t4 \ // h4 bit shift
VESLG $2, t2, t9 \ // h2 carry x5
VSRL t11, t5, t5 \ // h5 bit shift
VN t6, h0, h0 \ // h0 clear carry
VAQ t2, t9, t2 \ // h2 carry x5
VESLG $2, t5, t9 \ // h5 carry x5
VN t6, h1, h1 \ // h1 clear carry
VN t7, h2, h2 \ // h2 clear carry
VAQ t5, t9, t5 \ // h5 carry x5
VN t6, h3, h3 \ // h3 clear carry
VN t6, h4, h4 \ // h4 clear carry
VN t7, h5, h5 \ // h5 clear carry
VAQ t0, h1, h1 \ // h0->h1
VAQ t3, h4, h4 \ // h3->h4
VAQ t1, h2, h2 \ // h1->h2
VAQ t4, h5, h5 \ // h4->h5
VAQ t2, h0, h0 \ // h2->h0
VAQ t5, h3, h3 \ // h5->h3
VREPG $1, t6, t6 \ // 44 and 42 bit masks across both halves
VREPG $1, t7, t7 \
VSLDB $8, h0, h0, h0 \ // set up [h0/1/2, h3/4/5]
VSLDB $8, h1, h1, h1 \
VSLDB $8, h2, h2, h2 \
VO h0, h3, h3 \
VO h1, h4, h4 \
VO h2, h5, h5 \
VESRLG $44, h3, t0 \ // 44 bit shift right
VESRLG $44, h4, t1 \
VESRLG $42, h5, t2 \
VN t6, h3, h3 \ // clear carry bits
VN t6, h4, h4 \
VN t7, h5, h5 \
VESLG $2, t2, t9 \ // multiply carry by 5
VAQ t9, t2, t2 \
VAQ t0, h4, h4 \
VAQ t1, h5, h5 \
VAQ t2, h3, h3 \
// carry h0->h1->h2->h0
// input: h0, h1, h2
// temp: t0, t1, t2, t3, t4, t5, t6, t7, t8
// output: h0, h1, h2
#define REDUCE2(h0, h1, h2, t0, t1, t2, t3, t4, t5, t6, t7, t8) \
VLEIB $7, $0x28, t3 \ // 5 byte shift mask
VREPIB $4, t4 \ // 4 bit shift mask
VREPIB $2, t7 \ // 2 bit shift mask
VGBM $0x003F, t5 \ // mask to clear carry bits
VSRLB t3, h0, t0 \
VSRLB t3, h1, t1 \
VSRLB t3, h2, t2 \
VESRLG $4, t5, t5 \ // 44 bit clear mask
VSRL t4, t0, t0 \
VSRL t4, t1, t1 \
VSRL t7, t2, t2 \
VESRLG $2, t5, t6 \ // 42 bit clear mask
VESLG $2, t2, t8 \
VAQ t8, t2, t2 \
VN t5, h0, h0 \
VN t5, h1, h1 \
VN t6, h2, h2 \
VAQ t0, h1, h1 \
VAQ t1, h2, h2 \
VAQ t2, h0, h0 \
VSRLB t3, h0, t0 \
VSRLB t3, h1, t1 \
VSRLB t3, h2, t2 \
VSRL t4, t0, t0 \
VSRL t4, t1, t1 \
VSRL t7, t2, t2 \
VN t5, h0, h0 \
VN t5, h1, h1 \
VESLG $2, t2, t8 \
VN t6, h2, h2 \
VAQ t0, h1, h1 \
VAQ t8, t2, t2 \
VAQ t1, h2, h2 \
VAQ t2, h0, h0 \
// expands two message blocks into the lower halfs of the d registers
// moves the contents of the d registers into upper halfs
// input: in1, in2, d0, d1, d2, d3, d4, d5
// temp: TEMP0, TEMP1, TEMP2, TEMP3
// output: d0, d1, d2, d3, d4, d5
#define EXPACC(in1, in2, d0, d1, d2, d3, d4, d5, TEMP0, TEMP1, TEMP2, TEMP3) \
VGBM $0xff3f, TEMP0 \
VGBM $0xff1f, TEMP1 \
VESLG $4, d1, TEMP2 \
VESLG $4, d4, TEMP3 \
VESRLG $4, TEMP0, TEMP0 \
VPERM in1, d0, EX0, d0 \
VPERM in2, d3, EX0, d3 \
VPERM in1, d2, EX2, d2 \
VPERM in2, d5, EX2, d5 \
VPERM in1, TEMP2, EX1, d1 \
VPERM in2, TEMP3, EX1, d4 \
VN TEMP0, d0, d0 \
VN TEMP0, d3, d3 \
VESRLG $4, d1, d1 \
VESRLG $4, d4, d4 \
VN TEMP1, d2, d2 \
VN TEMP1, d5, d5 \
VN TEMP0, d1, d1 \
VN TEMP0, d4, d4 \
// expands one message block into the lower halfs of the d registers
// moves the contents of the d registers into upper halfs
// input: in, d0, d1, d2
// temp: TEMP0, TEMP1, TEMP2
// output: d0, d1, d2
#define EXPACC2(in, d0, d1, d2, TEMP0, TEMP1, TEMP2) \
VGBM $0xff3f, TEMP0 \
VESLG $4, d1, TEMP2 \
VGBM $0xff1f, TEMP1 \
VPERM in, d0, EX0, d0 \
VESRLG $4, TEMP0, TEMP0 \
VPERM in, d2, EX2, d2 \
VPERM in, TEMP2, EX1, d1 \
VN TEMP0, d0, d0 \
VN TEMP1, d2, d2 \
VESRLG $4, d1, d1 \
VN TEMP0, d1, d1 \
// pack h2:h0 into h1:h0 (no carry)
// input: h0, h1, h2
// output: h0, h1, h2
#define PACK(h0, h1, h2) \
VMRLG h1, h2, h2 \ // copy h1 to upper half h2
VESLG $44, h1, h1 \ // shift limb 1 44 bits, leaving 20
VO h0, h1, h0 \ // combine h0 with 20 bits from limb 1
VESRLG $20, h2, h1 \ // put top 24 bits of limb 1 into h1
VLEIG $1, $0, h1 \ // clear h2 stuff from lower half of h1
VO h0, h1, h0 \ // h0 now has 88 bits (limb 0 and 1)
VLEIG $0, $0, h2 \ // clear upper half of h2
VESRLG $40, h2, h1 \ // h1 now has upper two bits of result
VLEIB $7, $88, h1 \ // for byte shift (11 bytes)
VSLB h1, h2, h2 \ // shift h2 11 bytes to the left
VO h0, h2, h0 \ // combine h0 with 20 bits from limb 1
VLEIG $0, $0, h1 \ // clear upper half of h1
// if h > 2**130-5 then h -= 2**130-5
// input: h0, h1
// temp: t0, t1, t2
// output: h0
#define MOD(h0, h1, t0, t1, t2) \
VZERO t0 \
VLEIG $1, $5, t0 \
VACCQ h0, t0, t1 \
VAQ h0, t0, t0 \
VONE t2 \
VLEIG $1, $-4, t2 \
VAQ t2, t1, t1 \
VACCQ h1, t1, t1 \
VONE t2 \
VAQ t2, t1, t1 \
VN h0, t1, t2 \
VNC t0, t1, t1 \
VO t1, t2, h0 \
// func poly1305vmsl(out *[16]byte, m *byte, mlen uint64, key *[32]key)
TEXT ·poly1305vmsl(SB), $0-32
// This code processes 6 + up to 4 blocks (32 bytes) per iteration
// using the algorithm described in:
// NEON crypto, Daniel J. Bernstein & Peter Schwabe
// https://cryptojedi.org/papers/neoncrypto-20120320.pdf
// And as moddified for VMSL as described in
// Accelerating Poly1305 Cryptographic Message Authentication on the z14
// O'Farrell et al, CASCON 2017, p48-55
// https://ibm.ent.box.com/s/jf9gedj0e9d2vjctfyh186shaztavnht
LMG out+0(FP), R1, R4 // R1=out, R2=m, R3=mlen, R4=key
VZERO V0 // c
// load EX0, EX1 and EX2
MOVD $·constants<>(SB), R5
VLM (R5), EX0, EX2 // c
// setup r
VL (R4), T_0
MOVD $·keyMask<>(SB), R6
VL (R6), T_1
VN T_0, T_1, T_0
VZERO T_2 // limbs for r
VZERO T_3
VZERO T_4
EXPACC2(T_0, T_2, T_3, T_4, T_1, T_5, T_7)
// T_2, T_3, T_4: [0, r]
// setup r*20
VLEIG $0, $0, T_0
VLEIG $1, $20, T_0 // T_0: [0, 20]
VZERO T_5
VZERO T_6
VMSLG T_0, T_3, T_5, T_5
VMSLG T_0, T_4, T_6, T_6
// store r for final block in GR
VLGVG $1, T_2, RSAVE_0 // c
VLGVG $1, T_3, RSAVE_1 // c
VLGVG $1, T_4, RSAVE_2 // c
VLGVG $1, T_5, R5SAVE_1 // c
VLGVG $1, T_6, R5SAVE_2 // c
// initialize h
VZERO H0_0
VZERO H1_0
VZERO H2_0
VZERO H0_1
VZERO H1_1
VZERO H2_1
// initialize pointer for reduce constants
MOVD $·reduce<>(SB), R12
// calculate r**2 and 20*(r**2)
VZERO R_0
VZERO R_1
VZERO R_2
SQUARE(T_2, T_3, T_4, T_6, R_0, R_1, R_2, T_1, T_5, T_7)
REDUCE2(R_0, R_1, R_2, M0, M1, M2, M3, M4, R5_1, R5_2, M5, T_1)
VZERO R5_1
VZERO R5_2
VMSLG T_0, R_1, R5_1, R5_1
VMSLG T_0, R_2, R5_2, R5_2
// skip r**4 calculation if 3 blocks or less
CMPBLE R3, $48, b4
// calculate r**4 and 20*(r**4)
VZERO T_8
VZERO T_9
VZERO T_10
SQUARE(R_0, R_1, R_2, R5_2, T_8, T_9, T_10, T_1, T_5, T_7)
REDUCE2(T_8, T_9, T_10, M0, M1, M2, M3, M4, T_2, T_3, M5, T_1)
VZERO T_2
VZERO T_3
VMSLG T_0, T_9, T_2, T_2
VMSLG T_0, T_10, T_3, T_3
// put r**2 to the right and r**4 to the left of R_0, R_1, R_2
VSLDB $8, T_8, T_8, T_8
VSLDB $8, T_9, T_9, T_9
VSLDB $8, T_10, T_10, T_10
VSLDB $8, T_2, T_2, T_2
VSLDB $8, T_3, T_3, T_3
VO T_8, R_0, R_0
VO T_9, R_1, R_1
VO T_10, R_2, R_2
VO T_2, R5_1, R5_1
VO T_3, R5_2, R5_2
CMPBLE R3, $80, load // less than or equal to 5 blocks in message
// 6(or 5+1) blocks
SUB $81, R3
VLM (R2), M0, M4
VLL R3, 80(R2), M5
ADD $1, R3
MOVBZ $1, R0
CMPBGE R3, $16, 2(PC)
VLVGB R3, R0, M5
MOVD $96(R2), R2
EXPACC(M0, M1, H0_0, H1_0, H2_0, H0_1, H1_1, H2_1, T_0, T_1, T_2, T_3)
EXPACC(M2, M3, H0_0, H1_0, H2_0, H0_1, H1_1, H2_1, T_0, T_1, T_2, T_3)
VLEIB $2, $1, H2_0
VLEIB $2, $1, H2_1
VLEIB $10, $1, H2_0
VLEIB $10, $1, H2_1
VZERO M0
VZERO M1
VZERO M2
VZERO M3
VZERO T_4
VZERO T_10
EXPACC(M4, M5, M0, M1, M2, M3, T_4, T_10, T_0, T_1, T_2, T_3)
VLR T_4, M4
VLEIB $10, $1, M2
CMPBLT R3, $16, 2(PC)
VLEIB $10, $1, T_10
MULTIPLY(H0_0, H1_0, H2_0, H0_1, H1_1, H2_1, R_0, R_1, R_2, R5_1, R5_2, M0, M1, M2, M3, M4, T_10, T_0, T_1, T_2, T_3, T_4, T_5, T_6, T_7, T_8, T_9)
REDUCE(H0_0, H1_0, H2_0, H0_1, H1_1, H2_1, T_10, M0, M1, M2, M3, M4, T_4, T_5, T_2, T_7, T_8, T_9)
VMRHG V0, H0_1, H0_0
VMRHG V0, H1_1, H1_0
VMRHG V0, H2_1, H2_0
VMRLG V0, H0_1, H0_1
VMRLG V0, H1_1, H1_1
VMRLG V0, H2_1, H2_1
SUB $16, R3
CMPBLE R3, $0, square
load:
// load EX0, EX1 and EX2
MOVD $·c<>(SB), R5
VLM (R5), EX0, EX2
loop:
CMPBLE R3, $64, add // b4 // last 4 or less blocks left
// next 4 full blocks
VLM (R2), M2, M5
SUB $64, R3
MOVD $64(R2), R2
REDUCE(H0_0, H1_0, H2_0, H0_1, H1_1, H2_1, T_10, M0, M1, T_0, T_1, T_3, T_4, T_5, T_2, T_7, T_8, T_9)
// expacc in-lined to create [m2, m3] limbs
VGBM $0x3f3f, T_0 // 44 bit clear mask
VGBM $0x1f1f, T_1 // 40 bit clear mask
VPERM M2, M3, EX0, T_3
VESRLG $4, T_0, T_0 // 44 bit clear mask ready
VPERM M2, M3, EX1, T_4
VPERM M2, M3, EX2, T_5
VN T_0, T_3, T_3
VESRLG $4, T_4, T_4
VN T_1, T_5, T_5
VN T_0, T_4, T_4
VMRHG H0_1, T_3, H0_0
VMRHG H1_1, T_4, H1_0
VMRHG H2_1, T_5, H2_0
VMRLG H0_1, T_3, H0_1
VMRLG H1_1, T_4, H1_1
VMRLG H2_1, T_5, H2_1
VLEIB $10, $1, H2_0
VLEIB $10, $1, H2_1
VPERM M4, M5, EX0, T_3
VPERM M4, M5, EX1, T_4
VPERM M4, M5, EX2, T_5
VN T_0, T_3, T_3
VESRLG $4, T_4, T_4
VN T_1, T_5, T_5
VN T_0, T_4, T_4
VMRHG V0, T_3, M0
VMRHG V0, T_4, M1
VMRHG V0, T_5, M2
VMRLG V0, T_3, M3
VMRLG V0, T_4, M4
VMRLG V0, T_5, M5
VLEIB $10, $1, M2
VLEIB $10, $1, M5
MULTIPLY(H0_0, H1_0, H2_0, H0_1, H1_1, H2_1, R_0, R_1, R_2, R5_1, R5_2, M0, M1, M2, M3, M4, M5, T_0, T_1, T_2, T_3, T_4, T_5, T_6, T_7, T_8, T_9)
CMPBNE R3, $0, loop
REDUCE(H0_0, H1_0, H2_0, H0_1, H1_1, H2_1, T_10, M0, M1, M3, M4, M5, T_4, T_5, T_2, T_7, T_8, T_9)
VMRHG V0, H0_1, H0_0
VMRHG V0, H1_1, H1_0
VMRHG V0, H2_1, H2_0
VMRLG V0, H0_1, H0_1
VMRLG V0, H1_1, H1_1
VMRLG V0, H2_1, H2_1
// load EX0, EX1, EX2
MOVD $·constants<>(SB), R5
VLM (R5), EX0, EX2
// sum vectors
VAQ H0_0, H0_1, H0_0
VAQ H1_0, H1_1, H1_0
VAQ H2_0, H2_1, H2_0
// h may be >= 2*(2**130-5) so we need to reduce it again
// M0...M4 are used as temps here
REDUCE2(H0_0, H1_0, H2_0, M0, M1, M2, M3, M4, T_9, T_10, H0_1, M5)
next: // carry h1->h2
VLEIB $7, $0x28, T_1
VREPIB $4, T_2
VGBM $0x003F, T_3
VESRLG $4, T_3
// byte shift
VSRLB T_1, H1_0, T_4
// bit shift
VSRL T_2, T_4, T_4
// clear h1 carry bits
VN T_3, H1_0, H1_0
// add carry
VAQ T_4, H2_0, H2_0
// h is now < 2*(2**130-5)
// pack h into h1 (hi) and h0 (lo)
PACK(H0_0, H1_0, H2_0)
// if h > 2**130-5 then h -= 2**130-5
MOD(H0_0, H1_0, T_0, T_1, T_2)
// h += s
MOVD $·bswapMask<>(SB), R5
VL (R5), T_1
VL 16(R4), T_0
VPERM T_0, T_0, T_1, T_0 // reverse bytes (to big)
VAQ T_0, H0_0, H0_0
VPERM H0_0, H0_0, T_1, H0_0 // reverse bytes (to little)
VST H0_0, (R1)
RET
add:
// load EX0, EX1, EX2
MOVD $·constants<>(SB), R5
VLM (R5), EX0, EX2
REDUCE(H0_0, H1_0, H2_0, H0_1, H1_1, H2_1, T_10, M0, M1, M3, M4, M5, T_4, T_5, T_2, T_7, T_8, T_9)
VMRHG V0, H0_1, H0_0
VMRHG V0, H1_1, H1_0
VMRHG V0, H2_1, H2_0
VMRLG V0, H0_1, H0_1
VMRLG V0, H1_1, H1_1
VMRLG V0, H2_1, H2_1
CMPBLE R3, $64, b4
b4:
CMPBLE R3, $48, b3 // 3 blocks or less
// 4(3+1) blocks remaining
SUB $49, R3
VLM (R2), M0, M2
VLL R3, 48(R2), M3
ADD $1, R3
MOVBZ $1, R0
CMPBEQ R3, $16, 2(PC)
VLVGB R3, R0, M3
MOVD $64(R2), R2
EXPACC(M0, M1, H0_0, H1_0, H2_0, H0_1, H1_1, H2_1, T_0, T_1, T_2, T_3)
VLEIB $10, $1, H2_0
VLEIB $10, $1, H2_1
VZERO M0
VZERO M1
VZERO M4
VZERO M5
VZERO T_4
VZERO T_10
EXPACC(M2, M3, M0, M1, M4, M5, T_4, T_10, T_0, T_1, T_2, T_3)
VLR T_4, M2
VLEIB $10, $1, M4
CMPBNE R3, $16, 2(PC)
VLEIB $10, $1, T_10
MULTIPLY(H0_0, H1_0, H2_0, H0_1, H1_1, H2_1, R_0, R_1, R_2, R5_1, R5_2, M0, M1, M4, M5, M2, T_10, T_0, T_1, T_2, T_3, T_4, T_5, T_6, T_7, T_8, T_9)
REDUCE(H0_0, H1_0, H2_0, H0_1, H1_1, H2_1, T_10, M0, M1, M3, M4, M5, T_4, T_5, T_2, T_7, T_8, T_9)
VMRHG V0, H0_1, H0_0
VMRHG V0, H1_1, H1_0
VMRHG V0, H2_1, H2_0
VMRLG V0, H0_1, H0_1
VMRLG V0, H1_1, H1_1
VMRLG V0, H2_1, H2_1
SUB $16, R3
CMPBLE R3, $0, square // this condition must always hold true!
b3:
CMPBLE R3, $32, b2
// 3 blocks remaining
// setup [r²,r]
VSLDB $8, R_0, R_0, R_0
VSLDB $8, R_1, R_1, R_1
VSLDB $8, R_2, R_2, R_2
VSLDB $8, R5_1, R5_1, R5_1
VSLDB $8, R5_2, R5_2, R5_2
VLVGG $1, RSAVE_0, R_0
VLVGG $1, RSAVE_1, R_1
VLVGG $1, RSAVE_2, R_2
VLVGG $1, R5SAVE_1, R5_1
VLVGG $1, R5SAVE_2, R5_2
// setup [h0, h1]
VSLDB $8, H0_0, H0_0, H0_0
VSLDB $8, H1_0, H1_0, H1_0
VSLDB $8, H2_0, H2_0, H2_0
VO H0_1, H0_0, H0_0
VO H1_1, H1_0, H1_0
VO H2_1, H2_0, H2_0
VZERO H0_1
VZERO H1_1
VZERO H2_1
VZERO M0
VZERO M1
VZERO M2
VZERO M3
VZERO M4
VZERO M5
// H*[r**2, r]
MULTIPLY(H0_0, H1_0, H2_0, H0_1, H1_1, H2_1, R_0, R_1, R_2, R5_1, R5_2, M0, M1, M2, M3, M4, M5, T_0, T_1, T_2, T_3, T_4, T_5, T_6, T_7, T_8, T_9)
REDUCE2(H0_0, H1_0, H2_0, M0, M1, M2, M3, M4, H0_1, H1_1, T_10, M5)
SUB $33, R3
VLM (R2), M0, M1
VLL R3, 32(R2), M2
ADD $1, R3
MOVBZ $1, R0
CMPBEQ R3, $16, 2(PC)
VLVGB R3, R0, M2
// H += m0
VZERO T_1
VZERO T_2
VZERO T_3
EXPACC2(M0, T_1, T_2, T_3, T_4, T_5, T_6)
VLEIB $10, $1, T_3
VAG H0_0, T_1, H0_0
VAG H1_0, T_2, H1_0
VAG H2_0, T_3, H2_0
VZERO M0
VZERO M3
VZERO M4
VZERO M5
VZERO T_10
// (H+m0)*r
MULTIPLY(H0_0, H1_0, H2_0, H0_1, H1_1, H2_1, R_0, R_1, R_2, R5_1, R5_2, M0, M3, M4, M5, V0, T_10, T_0, T_1, T_2, T_3, T_4, T_5, T_6, T_7, T_8, T_9)
REDUCE2(H0_0, H1_0, H2_0, M0, M3, M4, M5, T_10, H0_1, H1_1, H2_1, T_9)
// H += m1
VZERO V0
VZERO T_1
VZERO T_2
VZERO T_3
EXPACC2(M1, T_1, T_2, T_3, T_4, T_5, T_6)
VLEIB $10, $1, T_3
VAQ H0_0, T_1, H0_0
VAQ H1_0, T_2, H1_0
VAQ H2_0, T_3, H2_0
REDUCE2(H0_0, H1_0, H2_0, M0, M3, M4, M5, T_9, H0_1, H1_1, H2_1, T_10)
// [H, m2] * [r**2, r]
EXPACC2(M2, H0_0, H1_0, H2_0, T_1, T_2, T_3)
CMPBNE R3, $16, 2(PC)
VLEIB $10, $1, H2_0
VZERO M0
VZERO M1
VZERO M2
VZERO M3
VZERO M4
VZERO M5
MULTIPLY(H0_0, H1_0, H2_0, H0_1, H1_1, H2_1, R_0, R_1, R_2, R5_1, R5_2, M0, M1, M2, M3, M4, M5, T_0, T_1, T_2, T_3, T_4, T_5, T_6, T_7, T_8, T_9)
REDUCE2(H0_0, H1_0, H2_0, M0, M1, M2, M3, M4, H0_1, H1_1, M5, T_10)
SUB $16, R3
CMPBLE R3, $0, next // this condition must always hold true!
b2:
CMPBLE R3, $16, b1
// 2 blocks remaining
// setup [r²,r]
VSLDB $8, R_0, R_0, R_0
VSLDB $8, R_1, R_1, R_1
VSLDB $8, R_2, R_2, R_2
VSLDB $8, R5_1, R5_1, R5_1
VSLDB $8, R5_2, R5_2, R5_2
VLVGG $1, RSAVE_0, R_0
VLVGG $1, RSAVE_1, R_1
VLVGG $1, RSAVE_2, R_2
VLVGG $1, R5SAVE_1, R5_1
VLVGG $1, R5SAVE_2, R5_2
// setup [h0, h1]
VSLDB $8, H0_0, H0_0, H0_0
VSLDB $8, H1_0, H1_0, H1_0
VSLDB $8, H2_0, H2_0, H2_0
VO H0_1, H0_0, H0_0
VO H1_1, H1_0, H1_0
VO H2_1, H2_0, H2_0
VZERO H0_1
VZERO H1_1
VZERO H2_1
VZERO M0
VZERO M1
VZERO M2
VZERO M3
VZERO M4
VZERO M5
// H*[r**2, r]
MULTIPLY(H0_0, H1_0, H2_0, H0_1, H1_1, H2_1, R_0, R_1, R_2, R5_1, R5_2, M0, M1, M2, M3, M4, M5, T_0, T_1, T_2, T_3, T_4, T_5, T_6, T_7, T_8, T_9)
REDUCE(H0_0, H1_0, H2_0, H0_1, H1_1, H2_1, T_10, M0, M1, M2, M3, M4, T_4, T_5, T_2, T_7, T_8, T_9)
VMRHG V0, H0_1, H0_0
VMRHG V0, H1_1, H1_0
VMRHG V0, H2_1, H2_0
VMRLG V0, H0_1, H0_1
VMRLG V0, H1_1, H1_1
VMRLG V0, H2_1, H2_1
// move h to the left and 0s at the right
VSLDB $8, H0_0, H0_0, H0_0
VSLDB $8, H1_0, H1_0, H1_0
VSLDB $8, H2_0, H2_0, H2_0
// get message blocks and append 1 to start
SUB $17, R3
VL (R2), M0
VLL R3, 16(R2), M1
ADD $1, R3
MOVBZ $1, R0
CMPBEQ R3, $16, 2(PC)
VLVGB R3, R0, M1
VZERO T_6
VZERO T_7
VZERO T_8
EXPACC2(M0, T_6, T_7, T_8, T_1, T_2, T_3)
EXPACC2(M1, T_6, T_7, T_8, T_1, T_2, T_3)
VLEIB $2, $1, T_8
CMPBNE R3, $16, 2(PC)
VLEIB $10, $1, T_8
// add [m0, m1] to h
VAG H0_0, T_6, H0_0
VAG H1_0, T_7, H1_0
VAG H2_0, T_8, H2_0
VZERO M2
VZERO M3
VZERO M4
VZERO M5
VZERO T_10
VZERO M0
// at this point R_0 .. R5_2 look like [r**2, r]
MULTIPLY(H0_0, H1_0, H2_0, H0_1, H1_1, H2_1, R_0, R_1, R_2, R5_1, R5_2, M2, M3, M4, M5, T_10, M0, T_0, T_1, T_2, T_3, T_4, T_5, T_6, T_7, T_8, T_9)
REDUCE2(H0_0, H1_0, H2_0, M2, M3, M4, M5, T_9, H0_1, H1_1, H2_1, T_10)
SUB $16, R3, R3
CMPBLE R3, $0, next
b1:
CMPBLE R3, $0, next
// 1 block remaining
// setup [r²,r]
VSLDB $8, R_0, R_0, R_0
VSLDB $8, R_1, R_1, R_1
VSLDB $8, R_2, R_2, R_2
VSLDB $8, R5_1, R5_1, R5_1
VSLDB $8, R5_2, R5_2, R5_2
VLVGG $1, RSAVE_0, R_0
VLVGG $1, RSAVE_1, R_1
VLVGG $1, RSAVE_2, R_2
VLVGG $1, R5SAVE_1, R5_1
VLVGG $1, R5SAVE_2, R5_2
// setup [h0, h1]
VSLDB $8, H0_0, H0_0, H0_0
VSLDB $8, H1_0, H1_0, H1_0
VSLDB $8, H2_0, H2_0, H2_0
VO H0_1, H0_0, H0_0
VO H1_1, H1_0, H1_0
VO H2_1, H2_0, H2_0
VZERO H0_1
VZERO H1_1
VZERO H2_1
VZERO M0
VZERO M1
VZERO M2
VZERO M3
VZERO M4
VZERO M5
// H*[r**2, r]
MULTIPLY(H0_0, H1_0, H2_0, H0_1, H1_1, H2_1, R_0, R_1, R_2, R5_1, R5_2, M0, M1, M2, M3, M4, M5, T_0, T_1, T_2, T_3, T_4, T_5, T_6, T_7, T_8, T_9)
REDUCE2(H0_0, H1_0, H2_0, M0, M1, M2, M3, M4, T_9, T_10, H0_1, M5)
// set up [0, m0] limbs
SUB $1, R3
VLL R3, (R2), M0
ADD $1, R3
MOVBZ $1, R0
CMPBEQ R3, $16, 2(PC)
VLVGB R3, R0, M0
VZERO T_1
VZERO T_2
VZERO T_3
EXPACC2(M0, T_1, T_2, T_3, T_4, T_5, T_6)// limbs: [0, m]
CMPBNE R3, $16, 2(PC)
VLEIB $10, $1, T_3
// h+m0
VAQ H0_0, T_1, H0_0
VAQ H1_0, T_2, H1_0
VAQ H2_0, T_3, H2_0
VZERO M0
VZERO M1
VZERO M2
VZERO M3
VZERO M4
VZERO M5
MULTIPLY(H0_0, H1_0, H2_0, H0_1, H1_1, H2_1, R_0, R_1, R_2, R5_1, R5_2, M0, M1, M2, M3, M4, M5, T_0, T_1, T_2, T_3, T_4, T_5, T_6, T_7, T_8, T_9)
REDUCE2(H0_0, H1_0, H2_0, M0, M1, M2, M3, M4, T_9, T_10, H0_1, M5)
BR next
square:
// setup [r²,r]
VSLDB $8, R_0, R_0, R_0
VSLDB $8, R_1, R_1, R_1
VSLDB $8, R_2, R_2, R_2
VSLDB $8, R5_1, R5_1, R5_1
VSLDB $8, R5_2, R5_2, R5_2
VLVGG $1, RSAVE_0, R_0
VLVGG $1, RSAVE_1, R_1
VLVGG $1, RSAVE_2, R_2
VLVGG $1, R5SAVE_1, R5_1
VLVGG $1, R5SAVE_2, R5_2
// setup [h0, h1]
VSLDB $8, H0_0, H0_0, H0_0
VSLDB $8, H1_0, H1_0, H1_0
VSLDB $8, H2_0, H2_0, H2_0
VO H0_1, H0_0, H0_0
VO H1_1, H1_0, H1_0
VO H2_1, H2_0, H2_0
VZERO H0_1
VZERO H1_1
VZERO H2_1
VZERO M0
VZERO M1
VZERO M2
VZERO M3
VZERO M4
VZERO M5
// (h0*r**2) + (h1*r)
MULTIPLY(H0_0, H1_0, H2_0, H0_1, H1_1, H2_1, R_0, R_1, R_2, R5_1, R5_2, M0, M1, M2, M3, M4, M5, T_0, T_1, T_2, T_3, T_4, T_5, T_6, T_7, T_8, T_9)
REDUCE2(H0_0, H1_0, H2_0, M0, M1, M2, M3, M4, T_9, T_10, H0_1, M5)
BR next
TEXT ·hasVMSLFacility(SB), NOSPLIT, $24-1
MOVD $x-24(SP), R1
XC $24, 0(R1), 0(R1) // clear the storage
MOVD $2, R0 // R0 is the number of double words stored -1
WORD $0xB2B01000 // STFLE 0(R1)
XOR R0, R0 // reset the value of R0
MOVBZ z-8(SP), R1
AND $0x01, R1
BEQ novmsl
vectorinstalled:
// check if the vector instruction has been enabled
VLEIB $0, $0xF, V16
VLGVB $0, V16, R1
CMPBNE R1, $0xF, novmsl
MOVB $1, ret+0(FP) // have vx
RET
novmsl:
MOVB $0, ret+0(FP) // no vx
RET

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,144 @@
// Copyright 2012 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package salsa provides low-level access to functions in the Salsa family.
package salsa // import "golang.org/x/crypto/salsa20/salsa"
// Sigma is the Salsa20 constant for 256-bit keys.
var Sigma = [16]byte{'e', 'x', 'p', 'a', 'n', 'd', ' ', '3', '2', '-', 'b', 'y', 't', 'e', ' ', 'k'}
// HSalsa20 applies the HSalsa20 core function to a 16-byte input in, 32-byte
// key k, and 16-byte constant c, and puts the result into the 32-byte array
// out.
func HSalsa20(out *[32]byte, in *[16]byte, k *[32]byte, c *[16]byte) {
x0 := uint32(c[0]) | uint32(c[1])<<8 | uint32(c[2])<<16 | uint32(c[3])<<24
x1 := uint32(k[0]) | uint32(k[1])<<8 | uint32(k[2])<<16 | uint32(k[3])<<24
x2 := uint32(k[4]) | uint32(k[5])<<8 | uint32(k[6])<<16 | uint32(k[7])<<24
x3 := uint32(k[8]) | uint32(k[9])<<8 | uint32(k[10])<<16 | uint32(k[11])<<24
x4 := uint32(k[12]) | uint32(k[13])<<8 | uint32(k[14])<<16 | uint32(k[15])<<24
x5 := uint32(c[4]) | uint32(c[5])<<8 | uint32(c[6])<<16 | uint32(c[7])<<24
x6 := uint32(in[0]) | uint32(in[1])<<8 | uint32(in[2])<<16 | uint32(in[3])<<24
x7 := uint32(in[4]) | uint32(in[5])<<8 | uint32(in[6])<<16 | uint32(in[7])<<24
x8 := uint32(in[8]) | uint32(in[9])<<8 | uint32(in[10])<<16 | uint32(in[11])<<24
x9 := uint32(in[12]) | uint32(in[13])<<8 | uint32(in[14])<<16 | uint32(in[15])<<24
x10 := uint32(c[8]) | uint32(c[9])<<8 | uint32(c[10])<<16 | uint32(c[11])<<24
x11 := uint32(k[16]) | uint32(k[17])<<8 | uint32(k[18])<<16 | uint32(k[19])<<24
x12 := uint32(k[20]) | uint32(k[21])<<8 | uint32(k[22])<<16 | uint32(k[23])<<24
x13 := uint32(k[24]) | uint32(k[25])<<8 | uint32(k[26])<<16 | uint32(k[27])<<24
x14 := uint32(k[28]) | uint32(k[29])<<8 | uint32(k[30])<<16 | uint32(k[31])<<24
x15 := uint32(c[12]) | uint32(c[13])<<8 | uint32(c[14])<<16 | uint32(c[15])<<24
for i := 0; i < 20; i += 2 {
u := x0 + x12
x4 ^= u<<7 | u>>(32-7)
u = x4 + x0
x8 ^= u<<9 | u>>(32-9)
u = x8 + x4
x12 ^= u<<13 | u>>(32-13)
u = x12 + x8
x0 ^= u<<18 | u>>(32-18)
u = x5 + x1
x9 ^= u<<7 | u>>(32-7)
u = x9 + x5
x13 ^= u<<9 | u>>(32-9)
u = x13 + x9
x1 ^= u<<13 | u>>(32-13)
u = x1 + x13
x5 ^= u<<18 | u>>(32-18)
u = x10 + x6
x14 ^= u<<7 | u>>(32-7)
u = x14 + x10
x2 ^= u<<9 | u>>(32-9)
u = x2 + x14
x6 ^= u<<13 | u>>(32-13)
u = x6 + x2
x10 ^= u<<18 | u>>(32-18)
u = x15 + x11
x3 ^= u<<7 | u>>(32-7)
u = x3 + x15
x7 ^= u<<9 | u>>(32-9)
u = x7 + x3
x11 ^= u<<13 | u>>(32-13)
u = x11 + x7
x15 ^= u<<18 | u>>(32-18)
u = x0 + x3
x1 ^= u<<7 | u>>(32-7)
u = x1 + x0
x2 ^= u<<9 | u>>(32-9)
u = x2 + x1
x3 ^= u<<13 | u>>(32-13)
u = x3 + x2
x0 ^= u<<18 | u>>(32-18)
u = x5 + x4
x6 ^= u<<7 | u>>(32-7)
u = x6 + x5
x7 ^= u<<9 | u>>(32-9)
u = x7 + x6
x4 ^= u<<13 | u>>(32-13)
u = x4 + x7
x5 ^= u<<18 | u>>(32-18)
u = x10 + x9
x11 ^= u<<7 | u>>(32-7)
u = x11 + x10
x8 ^= u<<9 | u>>(32-9)
u = x8 + x11
x9 ^= u<<13 | u>>(32-13)
u = x9 + x8
x10 ^= u<<18 | u>>(32-18)
u = x15 + x14
x12 ^= u<<7 | u>>(32-7)
u = x12 + x15
x13 ^= u<<9 | u>>(32-9)
u = x13 + x12
x14 ^= u<<13 | u>>(32-13)
u = x14 + x13
x15 ^= u<<18 | u>>(32-18)
}
out[0] = byte(x0)
out[1] = byte(x0 >> 8)
out[2] = byte(x0 >> 16)
out[3] = byte(x0 >> 24)
out[4] = byte(x5)
out[5] = byte(x5 >> 8)
out[6] = byte(x5 >> 16)
out[7] = byte(x5 >> 24)
out[8] = byte(x10)
out[9] = byte(x10 >> 8)
out[10] = byte(x10 >> 16)
out[11] = byte(x10 >> 24)
out[12] = byte(x15)
out[13] = byte(x15 >> 8)
out[14] = byte(x15 >> 16)
out[15] = byte(x15 >> 24)
out[16] = byte(x6)
out[17] = byte(x6 >> 8)
out[18] = byte(x6 >> 16)
out[19] = byte(x6 >> 24)
out[20] = byte(x7)
out[21] = byte(x7 >> 8)
out[22] = byte(x7 >> 16)
out[23] = byte(x7 >> 24)
out[24] = byte(x8)
out[25] = byte(x8 >> 8)
out[26] = byte(x8 >> 16)
out[27] = byte(x8 >> 24)
out[28] = byte(x9)
out[29] = byte(x9 >> 8)
out[30] = byte(x9 >> 16)
out[31] = byte(x9 >> 24)
}

View File

@ -0,0 +1,889 @@
// Copyright 2012 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build amd64,!appengine,!gccgo
// This code was translated into a form compatible with 6a from the public
// domain sources in SUPERCOP: https://bench.cr.yp.to/supercop.html
// func salsa2020XORKeyStream(out, in *byte, n uint64, nonce, key *byte)
// This needs up to 64 bytes at 360(SP); hence the non-obvious frame size.
TEXT ·salsa2020XORKeyStream(SB),0,$456-40 // frame = 424 + 32 byte alignment
MOVQ out+0(FP),DI
MOVQ in+8(FP),SI
MOVQ n+16(FP),DX
MOVQ nonce+24(FP),CX
MOVQ key+32(FP),R8
MOVQ SP,R12
MOVQ SP,R9
ADDQ $31, R9
ANDQ $~31, R9
MOVQ R9, SP
MOVQ DX,R9
MOVQ CX,DX
MOVQ R8,R10
CMPQ R9,$0
JBE DONE
START:
MOVL 20(R10),CX
MOVL 0(R10),R8
MOVL 0(DX),AX
MOVL 16(R10),R11
MOVL CX,0(SP)
MOVL R8, 4 (SP)
MOVL AX, 8 (SP)
MOVL R11, 12 (SP)
MOVL 8(DX),CX
MOVL 24(R10),R8
MOVL 4(R10),AX
MOVL 4(DX),R11
MOVL CX,16(SP)
MOVL R8, 20 (SP)
MOVL AX, 24 (SP)
MOVL R11, 28 (SP)
MOVL 12(DX),CX
MOVL 12(R10),DX
MOVL 28(R10),R8
MOVL 8(R10),AX
MOVL DX,32(SP)
MOVL CX, 36 (SP)
MOVL R8, 40 (SP)
MOVL AX, 44 (SP)
MOVQ $1634760805,DX
MOVQ $857760878,CX
MOVQ $2036477234,R8
MOVQ $1797285236,AX
MOVL DX,48(SP)
MOVL CX, 52 (SP)
MOVL R8, 56 (SP)
MOVL AX, 60 (SP)
CMPQ R9,$256
JB BYTESBETWEEN1AND255
MOVOA 48(SP),X0
PSHUFL $0X55,X0,X1
PSHUFL $0XAA,X0,X2
PSHUFL $0XFF,X0,X3
PSHUFL $0X00,X0,X0
MOVOA X1,64(SP)
MOVOA X2,80(SP)
MOVOA X3,96(SP)
MOVOA X0,112(SP)
MOVOA 0(SP),X0
PSHUFL $0XAA,X0,X1
PSHUFL $0XFF,X0,X2
PSHUFL $0X00,X0,X3
PSHUFL $0X55,X0,X0
MOVOA X1,128(SP)
MOVOA X2,144(SP)
MOVOA X3,160(SP)
MOVOA X0,176(SP)
MOVOA 16(SP),X0
PSHUFL $0XFF,X0,X1
PSHUFL $0X55,X0,X2
PSHUFL $0XAA,X0,X0
MOVOA X1,192(SP)
MOVOA X2,208(SP)
MOVOA X0,224(SP)
MOVOA 32(SP),X0
PSHUFL $0X00,X0,X1
PSHUFL $0XAA,X0,X2
PSHUFL $0XFF,X0,X0
MOVOA X1,240(SP)
MOVOA X2,256(SP)
MOVOA X0,272(SP)
BYTESATLEAST256:
MOVL 16(SP),DX
MOVL 36 (SP),CX
MOVL DX,288(SP)
MOVL CX,304(SP)
ADDQ $1,DX
SHLQ $32,CX
ADDQ CX,DX
MOVQ DX,CX
SHRQ $32,CX
MOVL DX, 292 (SP)
MOVL CX, 308 (SP)
ADDQ $1,DX
SHLQ $32,CX
ADDQ CX,DX
MOVQ DX,CX
SHRQ $32,CX
MOVL DX, 296 (SP)
MOVL CX, 312 (SP)
ADDQ $1,DX
SHLQ $32,CX
ADDQ CX,DX
MOVQ DX,CX
SHRQ $32,CX
MOVL DX, 300 (SP)
MOVL CX, 316 (SP)
ADDQ $1,DX
SHLQ $32,CX
ADDQ CX,DX
MOVQ DX,CX
SHRQ $32,CX
MOVL DX,16(SP)
MOVL CX, 36 (SP)
MOVQ R9,352(SP)
MOVQ $20,DX
MOVOA 64(SP),X0
MOVOA 80(SP),X1
MOVOA 96(SP),X2
MOVOA 256(SP),X3
MOVOA 272(SP),X4
MOVOA 128(SP),X5
MOVOA 144(SP),X6
MOVOA 176(SP),X7
MOVOA 192(SP),X8
MOVOA 208(SP),X9
MOVOA 224(SP),X10
MOVOA 304(SP),X11
MOVOA 112(SP),X12
MOVOA 160(SP),X13
MOVOA 240(SP),X14
MOVOA 288(SP),X15
MAINLOOP1:
MOVOA X1,320(SP)
MOVOA X2,336(SP)
MOVOA X13,X1
PADDL X12,X1
MOVOA X1,X2
PSLLL $7,X1
PXOR X1,X14
PSRLL $25,X2
PXOR X2,X14
MOVOA X7,X1
PADDL X0,X1
MOVOA X1,X2
PSLLL $7,X1
PXOR X1,X11
PSRLL $25,X2
PXOR X2,X11
MOVOA X12,X1
PADDL X14,X1
MOVOA X1,X2
PSLLL $9,X1
PXOR X1,X15
PSRLL $23,X2
PXOR X2,X15
MOVOA X0,X1
PADDL X11,X1
MOVOA X1,X2
PSLLL $9,X1
PXOR X1,X9
PSRLL $23,X2
PXOR X2,X9
MOVOA X14,X1
PADDL X15,X1
MOVOA X1,X2
PSLLL $13,X1
PXOR X1,X13
PSRLL $19,X2
PXOR X2,X13
MOVOA X11,X1
PADDL X9,X1
MOVOA X1,X2
PSLLL $13,X1
PXOR X1,X7
PSRLL $19,X2
PXOR X2,X7
MOVOA X15,X1
PADDL X13,X1
MOVOA X1,X2
PSLLL $18,X1
PXOR X1,X12
PSRLL $14,X2
PXOR X2,X12
MOVOA 320(SP),X1
MOVOA X12,320(SP)
MOVOA X9,X2
PADDL X7,X2
MOVOA X2,X12
PSLLL $18,X2
PXOR X2,X0
PSRLL $14,X12
PXOR X12,X0
MOVOA X5,X2
PADDL X1,X2
MOVOA X2,X12
PSLLL $7,X2
PXOR X2,X3
PSRLL $25,X12
PXOR X12,X3
MOVOA 336(SP),X2
MOVOA X0,336(SP)
MOVOA X6,X0
PADDL X2,X0
MOVOA X0,X12
PSLLL $7,X0
PXOR X0,X4
PSRLL $25,X12
PXOR X12,X4
MOVOA X1,X0
PADDL X3,X0
MOVOA X0,X12
PSLLL $9,X0
PXOR X0,X10
PSRLL $23,X12
PXOR X12,X10
MOVOA X2,X0
PADDL X4,X0
MOVOA X0,X12
PSLLL $9,X0
PXOR X0,X8
PSRLL $23,X12
PXOR X12,X8
MOVOA X3,X0
PADDL X10,X0
MOVOA X0,X12
PSLLL $13,X0
PXOR X0,X5
PSRLL $19,X12
PXOR X12,X5
MOVOA X4,X0
PADDL X8,X0
MOVOA X0,X12
PSLLL $13,X0
PXOR X0,X6
PSRLL $19,X12
PXOR X12,X6
MOVOA X10,X0
PADDL X5,X0
MOVOA X0,X12
PSLLL $18,X0
PXOR X0,X1
PSRLL $14,X12
PXOR X12,X1
MOVOA 320(SP),X0
MOVOA X1,320(SP)
MOVOA X4,X1
PADDL X0,X1
MOVOA X1,X12
PSLLL $7,X1
PXOR X1,X7
PSRLL $25,X12
PXOR X12,X7
MOVOA X8,X1
PADDL X6,X1
MOVOA X1,X12
PSLLL $18,X1
PXOR X1,X2
PSRLL $14,X12
PXOR X12,X2
MOVOA 336(SP),X12
MOVOA X2,336(SP)
MOVOA X14,X1
PADDL X12,X1
MOVOA X1,X2
PSLLL $7,X1
PXOR X1,X5
PSRLL $25,X2
PXOR X2,X5
MOVOA X0,X1
PADDL X7,X1
MOVOA X1,X2
PSLLL $9,X1
PXOR X1,X10
PSRLL $23,X2
PXOR X2,X10
MOVOA X12,X1
PADDL X5,X1
MOVOA X1,X2
PSLLL $9,X1
PXOR X1,X8
PSRLL $23,X2
PXOR X2,X8
MOVOA X7,X1
PADDL X10,X1
MOVOA X1,X2
PSLLL $13,X1
PXOR X1,X4
PSRLL $19,X2
PXOR X2,X4
MOVOA X5,X1
PADDL X8,X1
MOVOA X1,X2
PSLLL $13,X1
PXOR X1,X14
PSRLL $19,X2
PXOR X2,X14
MOVOA X10,X1
PADDL X4,X1
MOVOA X1,X2
PSLLL $18,X1
PXOR X1,X0
PSRLL $14,X2
PXOR X2,X0
MOVOA 320(SP),X1
MOVOA X0,320(SP)
MOVOA X8,X0
PADDL X14,X0
MOVOA X0,X2
PSLLL $18,X0
PXOR X0,X12
PSRLL $14,X2
PXOR X2,X12
MOVOA X11,X0
PADDL X1,X0
MOVOA X0,X2
PSLLL $7,X0
PXOR X0,X6
PSRLL $25,X2
PXOR X2,X6
MOVOA 336(SP),X2
MOVOA X12,336(SP)
MOVOA X3,X0
PADDL X2,X0
MOVOA X0,X12
PSLLL $7,X0
PXOR X0,X13
PSRLL $25,X12
PXOR X12,X13
MOVOA X1,X0
PADDL X6,X0
MOVOA X0,X12
PSLLL $9,X0
PXOR X0,X15
PSRLL $23,X12
PXOR X12,X15
MOVOA X2,X0
PADDL X13,X0
MOVOA X0,X12
PSLLL $9,X0
PXOR X0,X9
PSRLL $23,X12
PXOR X12,X9
MOVOA X6,X0
PADDL X15,X0
MOVOA X0,X12
PSLLL $13,X0
PXOR X0,X11
PSRLL $19,X12
PXOR X12,X11
MOVOA X13,X0
PADDL X9,X0
MOVOA X0,X12
PSLLL $13,X0
PXOR X0,X3
PSRLL $19,X12
PXOR X12,X3
MOVOA X15,X0
PADDL X11,X0
MOVOA X0,X12
PSLLL $18,X0
PXOR X0,X1
PSRLL $14,X12
PXOR X12,X1
MOVOA X9,X0
PADDL X3,X0
MOVOA X0,X12
PSLLL $18,X0
PXOR X0,X2
PSRLL $14,X12
PXOR X12,X2
MOVOA 320(SP),X12
MOVOA 336(SP),X0
SUBQ $2,DX
JA MAINLOOP1
PADDL 112(SP),X12
PADDL 176(SP),X7
PADDL 224(SP),X10
PADDL 272(SP),X4
MOVD X12,DX
MOVD X7,CX
MOVD X10,R8
MOVD X4,R9
PSHUFL $0X39,X12,X12
PSHUFL $0X39,X7,X7
PSHUFL $0X39,X10,X10
PSHUFL $0X39,X4,X4
XORL 0(SI),DX
XORL 4(SI),CX
XORL 8(SI),R8
XORL 12(SI),R9
MOVL DX,0(DI)
MOVL CX,4(DI)
MOVL R8,8(DI)
MOVL R9,12(DI)
MOVD X12,DX
MOVD X7,CX
MOVD X10,R8
MOVD X4,R9
PSHUFL $0X39,X12,X12
PSHUFL $0X39,X7,X7
PSHUFL $0X39,X10,X10
PSHUFL $0X39,X4,X4
XORL 64(SI),DX
XORL 68(SI),CX
XORL 72(SI),R8
XORL 76(SI),R9
MOVL DX,64(DI)
MOVL CX,68(DI)
MOVL R8,72(DI)
MOVL R9,76(DI)
MOVD X12,DX
MOVD X7,CX
MOVD X10,R8
MOVD X4,R9
PSHUFL $0X39,X12,X12
PSHUFL $0X39,X7,X7
PSHUFL $0X39,X10,X10
PSHUFL $0X39,X4,X4
XORL 128(SI),DX
XORL 132(SI),CX
XORL 136(SI),R8
XORL 140(SI),R9
MOVL DX,128(DI)
MOVL CX,132(DI)
MOVL R8,136(DI)
MOVL R9,140(DI)
MOVD X12,DX
MOVD X7,CX
MOVD X10,R8
MOVD X4,R9
XORL 192(SI),DX
XORL 196(SI),CX
XORL 200(SI),R8
XORL 204(SI),R9
MOVL DX,192(DI)
MOVL CX,196(DI)
MOVL R8,200(DI)
MOVL R9,204(DI)
PADDL 240(SP),X14
PADDL 64(SP),X0
PADDL 128(SP),X5
PADDL 192(SP),X8
MOVD X14,DX
MOVD X0,CX
MOVD X5,R8
MOVD X8,R9
PSHUFL $0X39,X14,X14
PSHUFL $0X39,X0,X0
PSHUFL $0X39,X5,X5
PSHUFL $0X39,X8,X8
XORL 16(SI),DX
XORL 20(SI),CX
XORL 24(SI),R8
XORL 28(SI),R9
MOVL DX,16(DI)
MOVL CX,20(DI)
MOVL R8,24(DI)
MOVL R9,28(DI)
MOVD X14,DX
MOVD X0,CX
MOVD X5,R8
MOVD X8,R9
PSHUFL $0X39,X14,X14
PSHUFL $0X39,X0,X0
PSHUFL $0X39,X5,X5
PSHUFL $0X39,X8,X8
XORL 80(SI),DX
XORL 84(SI),CX
XORL 88(SI),R8
XORL 92(SI),R9
MOVL DX,80(DI)
MOVL CX,84(DI)
MOVL R8,88(DI)
MOVL R9,92(DI)
MOVD X14,DX
MOVD X0,CX
MOVD X5,R8
MOVD X8,R9
PSHUFL $0X39,X14,X14
PSHUFL $0X39,X0,X0
PSHUFL $0X39,X5,X5
PSHUFL $0X39,X8,X8
XORL 144(SI),DX
XORL 148(SI),CX
XORL 152(SI),R8
XORL 156(SI),R9
MOVL DX,144(DI)
MOVL CX,148(DI)
MOVL R8,152(DI)
MOVL R9,156(DI)
MOVD X14,DX
MOVD X0,CX
MOVD X5,R8
MOVD X8,R9
XORL 208(SI),DX
XORL 212(SI),CX
XORL 216(SI),R8
XORL 220(SI),R9
MOVL DX,208(DI)
MOVL CX,212(DI)
MOVL R8,216(DI)
MOVL R9,220(DI)
PADDL 288(SP),X15
PADDL 304(SP),X11
PADDL 80(SP),X1
PADDL 144(SP),X6
MOVD X15,DX
MOVD X11,CX
MOVD X1,R8
MOVD X6,R9
PSHUFL $0X39,X15,X15
PSHUFL $0X39,X11,X11
PSHUFL $0X39,X1,X1
PSHUFL $0X39,X6,X6
XORL 32(SI),DX
XORL 36(SI),CX
XORL 40(SI),R8
XORL 44(SI),R9
MOVL DX,32(DI)
MOVL CX,36(DI)
MOVL R8,40(DI)
MOVL R9,44(DI)
MOVD X15,DX
MOVD X11,CX
MOVD X1,R8
MOVD X6,R9
PSHUFL $0X39,X15,X15
PSHUFL $0X39,X11,X11
PSHUFL $0X39,X1,X1
PSHUFL $0X39,X6,X6
XORL 96(SI),DX
XORL 100(SI),CX
XORL 104(SI),R8
XORL 108(SI),R9
MOVL DX,96(DI)
MOVL CX,100(DI)
MOVL R8,104(DI)
MOVL R9,108(DI)
MOVD X15,DX
MOVD X11,CX
MOVD X1,R8
MOVD X6,R9
PSHUFL $0X39,X15,X15
PSHUFL $0X39,X11,X11
PSHUFL $0X39,X1,X1
PSHUFL $0X39,X6,X6
XORL 160(SI),DX
XORL 164(SI),CX
XORL 168(SI),R8
XORL 172(SI),R9
MOVL DX,160(DI)
MOVL CX,164(DI)
MOVL R8,168(DI)
MOVL R9,172(DI)
MOVD X15,DX
MOVD X11,CX
MOVD X1,R8
MOVD X6,R9
XORL 224(SI),DX
XORL 228(SI),CX
XORL 232(SI),R8
XORL 236(SI),R9
MOVL DX,224(DI)
MOVL CX,228(DI)
MOVL R8,232(DI)
MOVL R9,236(DI)
PADDL 160(SP),X13
PADDL 208(SP),X9
PADDL 256(SP),X3
PADDL 96(SP),X2
MOVD X13,DX
MOVD X9,CX
MOVD X3,R8
MOVD X2,R9
PSHUFL $0X39,X13,X13
PSHUFL $0X39,X9,X9
PSHUFL $0X39,X3,X3
PSHUFL $0X39,X2,X2
XORL 48(SI),DX
XORL 52(SI),CX
XORL 56(SI),R8
XORL 60(SI),R9
MOVL DX,48(DI)
MOVL CX,52(DI)
MOVL R8,56(DI)
MOVL R9,60(DI)
MOVD X13,DX
MOVD X9,CX
MOVD X3,R8
MOVD X2,R9
PSHUFL $0X39,X13,X13
PSHUFL $0X39,X9,X9
PSHUFL $0X39,X3,X3
PSHUFL $0X39,X2,X2
XORL 112(SI),DX
XORL 116(SI),CX
XORL 120(SI),R8
XORL 124(SI),R9
MOVL DX,112(DI)
MOVL CX,116(DI)
MOVL R8,120(DI)
MOVL R9,124(DI)
MOVD X13,DX
MOVD X9,CX
MOVD X3,R8
MOVD X2,R9
PSHUFL $0X39,X13,X13
PSHUFL $0X39,X9,X9
PSHUFL $0X39,X3,X3
PSHUFL $0X39,X2,X2
XORL 176(SI),DX
XORL 180(SI),CX
XORL 184(SI),R8
XORL 188(SI),R9
MOVL DX,176(DI)
MOVL CX,180(DI)
MOVL R8,184(DI)
MOVL R9,188(DI)
MOVD X13,DX
MOVD X9,CX
MOVD X3,R8
MOVD X2,R9
XORL 240(SI),DX
XORL 244(SI),CX
XORL 248(SI),R8
XORL 252(SI),R9
MOVL DX,240(DI)
MOVL CX,244(DI)
MOVL R8,248(DI)
MOVL R9,252(DI)
MOVQ 352(SP),R9
SUBQ $256,R9
ADDQ $256,SI
ADDQ $256,DI
CMPQ R9,$256
JAE BYTESATLEAST256
CMPQ R9,$0
JBE DONE
BYTESBETWEEN1AND255:
CMPQ R9,$64
JAE NOCOPY
MOVQ DI,DX
LEAQ 360(SP),DI
MOVQ R9,CX
REP; MOVSB
LEAQ 360(SP),DI
LEAQ 360(SP),SI
NOCOPY:
MOVQ R9,352(SP)
MOVOA 48(SP),X0
MOVOA 0(SP),X1
MOVOA 16(SP),X2
MOVOA 32(SP),X3
MOVOA X1,X4
MOVQ $20,CX
MAINLOOP2:
PADDL X0,X4
MOVOA X0,X5
MOVOA X4,X6
PSLLL $7,X4
PSRLL $25,X6
PXOR X4,X3
PXOR X6,X3
PADDL X3,X5
MOVOA X3,X4
MOVOA X5,X6
PSLLL $9,X5
PSRLL $23,X6
PXOR X5,X2
PSHUFL $0X93,X3,X3
PXOR X6,X2
PADDL X2,X4
MOVOA X2,X5
MOVOA X4,X6
PSLLL $13,X4
PSRLL $19,X6
PXOR X4,X1
PSHUFL $0X4E,X2,X2
PXOR X6,X1
PADDL X1,X5
MOVOA X3,X4
MOVOA X5,X6
PSLLL $18,X5
PSRLL $14,X6
PXOR X5,X0
PSHUFL $0X39,X1,X1
PXOR X6,X0
PADDL X0,X4
MOVOA X0,X5
MOVOA X4,X6
PSLLL $7,X4
PSRLL $25,X6
PXOR X4,X1
PXOR X6,X1
PADDL X1,X5
MOVOA X1,X4
MOVOA X5,X6
PSLLL $9,X5
PSRLL $23,X6
PXOR X5,X2
PSHUFL $0X93,X1,X1
PXOR X6,X2
PADDL X2,X4
MOVOA X2,X5
MOVOA X4,X6
PSLLL $13,X4
PSRLL $19,X6
PXOR X4,X3
PSHUFL $0X4E,X2,X2
PXOR X6,X3
PADDL X3,X5
MOVOA X1,X4
MOVOA X5,X6
PSLLL $18,X5
PSRLL $14,X6
PXOR X5,X0
PSHUFL $0X39,X3,X3
PXOR X6,X0
PADDL X0,X4
MOVOA X0,X5
MOVOA X4,X6
PSLLL $7,X4
PSRLL $25,X6
PXOR X4,X3
PXOR X6,X3
PADDL X3,X5
MOVOA X3,X4
MOVOA X5,X6
PSLLL $9,X5
PSRLL $23,X6
PXOR X5,X2
PSHUFL $0X93,X3,X3
PXOR X6,X2
PADDL X2,X4
MOVOA X2,X5
MOVOA X4,X6
PSLLL $13,X4
PSRLL $19,X6
PXOR X4,X1
PSHUFL $0X4E,X2,X2
PXOR X6,X1
PADDL X1,X5
MOVOA X3,X4
MOVOA X5,X6
PSLLL $18,X5
PSRLL $14,X6
PXOR X5,X0
PSHUFL $0X39,X1,X1
PXOR X6,X0
PADDL X0,X4
MOVOA X0,X5
MOVOA X4,X6
PSLLL $7,X4
PSRLL $25,X6
PXOR X4,X1
PXOR X6,X1
PADDL X1,X5
MOVOA X1,X4
MOVOA X5,X6
PSLLL $9,X5
PSRLL $23,X6
PXOR X5,X2
PSHUFL $0X93,X1,X1
PXOR X6,X2
PADDL X2,X4
MOVOA X2,X5
MOVOA X4,X6
PSLLL $13,X4
PSRLL $19,X6
PXOR X4,X3
PSHUFL $0X4E,X2,X2
PXOR X6,X3
SUBQ $4,CX
PADDL X3,X5
MOVOA X1,X4
MOVOA X5,X6
PSLLL $18,X5
PXOR X7,X7
PSRLL $14,X6
PXOR X5,X0
PSHUFL $0X39,X3,X3
PXOR X6,X0
JA MAINLOOP2
PADDL 48(SP),X0
PADDL 0(SP),X1
PADDL 16(SP),X2
PADDL 32(SP),X3
MOVD X0,CX
MOVD X1,R8
MOVD X2,R9
MOVD X3,AX
PSHUFL $0X39,X0,X0
PSHUFL $0X39,X1,X1
PSHUFL $0X39,X2,X2
PSHUFL $0X39,X3,X3
XORL 0(SI),CX
XORL 48(SI),R8
XORL 32(SI),R9
XORL 16(SI),AX
MOVL CX,0(DI)
MOVL R8,48(DI)
MOVL R9,32(DI)
MOVL AX,16(DI)
MOVD X0,CX
MOVD X1,R8
MOVD X2,R9
MOVD X3,AX
PSHUFL $0X39,X0,X0
PSHUFL $0X39,X1,X1
PSHUFL $0X39,X2,X2
PSHUFL $0X39,X3,X3
XORL 20(SI),CX
XORL 4(SI),R8
XORL 52(SI),R9
XORL 36(SI),AX
MOVL CX,20(DI)
MOVL R8,4(DI)
MOVL R9,52(DI)
MOVL AX,36(DI)
MOVD X0,CX
MOVD X1,R8
MOVD X2,R9
MOVD X3,AX
PSHUFL $0X39,X0,X0
PSHUFL $0X39,X1,X1
PSHUFL $0X39,X2,X2
PSHUFL $0X39,X3,X3
XORL 40(SI),CX
XORL 24(SI),R8
XORL 8(SI),R9
XORL 56(SI),AX
MOVL CX,40(DI)
MOVL R8,24(DI)
MOVL R9,8(DI)
MOVL AX,56(DI)
MOVD X0,CX
MOVD X1,R8
MOVD X2,R9
MOVD X3,AX
XORL 60(SI),CX
XORL 44(SI),R8
XORL 28(SI),R9
XORL 12(SI),AX
MOVL CX,60(DI)
MOVL R8,44(DI)
MOVL R9,28(DI)
MOVL AX,12(DI)
MOVQ 352(SP),R9
MOVL 16(SP),CX
MOVL 36 (SP),R8
ADDQ $1,CX
SHLQ $32,R8
ADDQ R8,CX
MOVQ CX,R8
SHRQ $32,R8
MOVL CX,16(SP)
MOVL R8, 36 (SP)
CMPQ R9,$64
JA BYTESATLEAST65
JAE BYTESATLEAST64
MOVQ DI,SI
MOVQ DX,DI
MOVQ R9,CX
REP; MOVSB
BYTESATLEAST64:
DONE:
MOVQ R12,SP
RET
BYTESATLEAST65:
SUBQ $64,R9
ADDQ $64,DI
ADDQ $64,SI
JMP BYTESBETWEEN1AND255

View File

@ -0,0 +1,199 @@
// Copyright 2012 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package salsa
// Core208 applies the Salsa20/8 core function to the 64-byte array in and puts
// the result into the 64-byte array out. The input and output may be the same array.
func Core208(out *[64]byte, in *[64]byte) {
j0 := uint32(in[0]) | uint32(in[1])<<8 | uint32(in[2])<<16 | uint32(in[3])<<24
j1 := uint32(in[4]) | uint32(in[5])<<8 | uint32(in[6])<<16 | uint32(in[7])<<24
j2 := uint32(in[8]) | uint32(in[9])<<8 | uint32(in[10])<<16 | uint32(in[11])<<24
j3 := uint32(in[12]) | uint32(in[13])<<8 | uint32(in[14])<<16 | uint32(in[15])<<24
j4 := uint32(in[16]) | uint32(in[17])<<8 | uint32(in[18])<<16 | uint32(in[19])<<24
j5 := uint32(in[20]) | uint32(in[21])<<8 | uint32(in[22])<<16 | uint32(in[23])<<24
j6 := uint32(in[24]) | uint32(in[25])<<8 | uint32(in[26])<<16 | uint32(in[27])<<24
j7 := uint32(in[28]) | uint32(in[29])<<8 | uint32(in[30])<<16 | uint32(in[31])<<24
j8 := uint32(in[32]) | uint32(in[33])<<8 | uint32(in[34])<<16 | uint32(in[35])<<24
j9 := uint32(in[36]) | uint32(in[37])<<8 | uint32(in[38])<<16 | uint32(in[39])<<24
j10 := uint32(in[40]) | uint32(in[41])<<8 | uint32(in[42])<<16 | uint32(in[43])<<24
j11 := uint32(in[44]) | uint32(in[45])<<8 | uint32(in[46])<<16 | uint32(in[47])<<24
j12 := uint32(in[48]) | uint32(in[49])<<8 | uint32(in[50])<<16 | uint32(in[51])<<24
j13 := uint32(in[52]) | uint32(in[53])<<8 | uint32(in[54])<<16 | uint32(in[55])<<24
j14 := uint32(in[56]) | uint32(in[57])<<8 | uint32(in[58])<<16 | uint32(in[59])<<24
j15 := uint32(in[60]) | uint32(in[61])<<8 | uint32(in[62])<<16 | uint32(in[63])<<24
x0, x1, x2, x3, x4, x5, x6, x7, x8 := j0, j1, j2, j3, j4, j5, j6, j7, j8
x9, x10, x11, x12, x13, x14, x15 := j9, j10, j11, j12, j13, j14, j15
for i := 0; i < 8; i += 2 {
u := x0 + x12
x4 ^= u<<7 | u>>(32-7)
u = x4 + x0
x8 ^= u<<9 | u>>(32-9)
u = x8 + x4
x12 ^= u<<13 | u>>(32-13)
u = x12 + x8
x0 ^= u<<18 | u>>(32-18)
u = x5 + x1
x9 ^= u<<7 | u>>(32-7)
u = x9 + x5
x13 ^= u<<9 | u>>(32-9)
u = x13 + x9
x1 ^= u<<13 | u>>(32-13)
u = x1 + x13
x5 ^= u<<18 | u>>(32-18)
u = x10 + x6
x14 ^= u<<7 | u>>(32-7)
u = x14 + x10
x2 ^= u<<9 | u>>(32-9)
u = x2 + x14
x6 ^= u<<13 | u>>(32-13)
u = x6 + x2
x10 ^= u<<18 | u>>(32-18)
u = x15 + x11
x3 ^= u<<7 | u>>(32-7)
u = x3 + x15
x7 ^= u<<9 | u>>(32-9)
u = x7 + x3
x11 ^= u<<13 | u>>(32-13)
u = x11 + x7
x15 ^= u<<18 | u>>(32-18)
u = x0 + x3
x1 ^= u<<7 | u>>(32-7)
u = x1 + x0
x2 ^= u<<9 | u>>(32-9)
u = x2 + x1
x3 ^= u<<13 | u>>(32-13)
u = x3 + x2
x0 ^= u<<18 | u>>(32-18)
u = x5 + x4
x6 ^= u<<7 | u>>(32-7)
u = x6 + x5
x7 ^= u<<9 | u>>(32-9)
u = x7 + x6
x4 ^= u<<13 | u>>(32-13)
u = x4 + x7
x5 ^= u<<18 | u>>(32-18)
u = x10 + x9
x11 ^= u<<7 | u>>(32-7)
u = x11 + x10
x8 ^= u<<9 | u>>(32-9)
u = x8 + x11
x9 ^= u<<13 | u>>(32-13)
u = x9 + x8
x10 ^= u<<18 | u>>(32-18)
u = x15 + x14
x12 ^= u<<7 | u>>(32-7)
u = x12 + x15
x13 ^= u<<9 | u>>(32-9)
u = x13 + x12
x14 ^= u<<13 | u>>(32-13)
u = x14 + x13
x15 ^= u<<18 | u>>(32-18)
}
x0 += j0
x1 += j1
x2 += j2
x3 += j3
x4 += j4
x5 += j5
x6 += j6
x7 += j7
x8 += j8
x9 += j9
x10 += j10
x11 += j11
x12 += j12
x13 += j13
x14 += j14
x15 += j15
out[0] = byte(x0)
out[1] = byte(x0 >> 8)
out[2] = byte(x0 >> 16)
out[3] = byte(x0 >> 24)
out[4] = byte(x1)
out[5] = byte(x1 >> 8)
out[6] = byte(x1 >> 16)
out[7] = byte(x1 >> 24)
out[8] = byte(x2)
out[9] = byte(x2 >> 8)
out[10] = byte(x2 >> 16)
out[11] = byte(x2 >> 24)
out[12] = byte(x3)
out[13] = byte(x3 >> 8)
out[14] = byte(x3 >> 16)
out[15] = byte(x3 >> 24)
out[16] = byte(x4)
out[17] = byte(x4 >> 8)
out[18] = byte(x4 >> 16)
out[19] = byte(x4 >> 24)
out[20] = byte(x5)
out[21] = byte(x5 >> 8)
out[22] = byte(x5 >> 16)
out[23] = byte(x5 >> 24)
out[24] = byte(x6)
out[25] = byte(x6 >> 8)
out[26] = byte(x6 >> 16)
out[27] = byte(x6 >> 24)
out[28] = byte(x7)
out[29] = byte(x7 >> 8)
out[30] = byte(x7 >> 16)
out[31] = byte(x7 >> 24)
out[32] = byte(x8)
out[33] = byte(x8 >> 8)
out[34] = byte(x8 >> 16)
out[35] = byte(x8 >> 24)
out[36] = byte(x9)
out[37] = byte(x9 >> 8)
out[38] = byte(x9 >> 16)
out[39] = byte(x9 >> 24)
out[40] = byte(x10)
out[41] = byte(x10 >> 8)
out[42] = byte(x10 >> 16)
out[43] = byte(x10 >> 24)
out[44] = byte(x11)
out[45] = byte(x11 >> 8)
out[46] = byte(x11 >> 16)
out[47] = byte(x11 >> 24)
out[48] = byte(x12)
out[49] = byte(x12 >> 8)
out[50] = byte(x12 >> 16)
out[51] = byte(x12 >> 24)
out[52] = byte(x13)
out[53] = byte(x13 >> 8)
out[54] = byte(x13 >> 16)
out[55] = byte(x13 >> 24)
out[56] = byte(x14)
out[57] = byte(x14 >> 8)
out[58] = byte(x14 >> 16)
out[59] = byte(x14 >> 24)
out[60] = byte(x15)
out[61] = byte(x15 >> 8)
out[62] = byte(x15 >> 16)
out[63] = byte(x15 >> 24)
}

View File

@ -0,0 +1,24 @@
// Copyright 2012 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build amd64,!appengine,!gccgo
package salsa
// This function is implemented in salsa2020_amd64.s.
//go:noescape
func salsa2020XORKeyStream(out, in *byte, n uint64, nonce, key *byte)
// XORKeyStream crypts bytes from in to out using the given key and counters.
// In and out must overlap entirely or not at all. Counter
// contains the raw salsa20 counter bytes (both nonce and block counter).
func XORKeyStream(out, in []byte, counter *[16]byte, key *[32]byte) {
if len(in) == 0 {
return
}
_ = out[len(in)-1]
salsa2020XORKeyStream(&out[0], &in[0], uint64(len(in)), &counter[0], &key[0])
}

View File

@ -0,0 +1,234 @@
// Copyright 2012 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build !amd64 appengine gccgo
package salsa
const rounds = 20
// core applies the Salsa20 core function to 16-byte input in, 32-byte key k,
// and 16-byte constant c, and puts the result into 64-byte array out.
func core(out *[64]byte, in *[16]byte, k *[32]byte, c *[16]byte) {
j0 := uint32(c[0]) | uint32(c[1])<<8 | uint32(c[2])<<16 | uint32(c[3])<<24
j1 := uint32(k[0]) | uint32(k[1])<<8 | uint32(k[2])<<16 | uint32(k[3])<<24
j2 := uint32(k[4]) | uint32(k[5])<<8 | uint32(k[6])<<16 | uint32(k[7])<<24
j3 := uint32(k[8]) | uint32(k[9])<<8 | uint32(k[10])<<16 | uint32(k[11])<<24
j4 := uint32(k[12]) | uint32(k[13])<<8 | uint32(k[14])<<16 | uint32(k[15])<<24
j5 := uint32(c[4]) | uint32(c[5])<<8 | uint32(c[6])<<16 | uint32(c[7])<<24
j6 := uint32(in[0]) | uint32(in[1])<<8 | uint32(in[2])<<16 | uint32(in[3])<<24
j7 := uint32(in[4]) | uint32(in[5])<<8 | uint32(in[6])<<16 | uint32(in[7])<<24
j8 := uint32(in[8]) | uint32(in[9])<<8 | uint32(in[10])<<16 | uint32(in[11])<<24
j9 := uint32(in[12]) | uint32(in[13])<<8 | uint32(in[14])<<16 | uint32(in[15])<<24
j10 := uint32(c[8]) | uint32(c[9])<<8 | uint32(c[10])<<16 | uint32(c[11])<<24
j11 := uint32(k[16]) | uint32(k[17])<<8 | uint32(k[18])<<16 | uint32(k[19])<<24
j12 := uint32(k[20]) | uint32(k[21])<<8 | uint32(k[22])<<16 | uint32(k[23])<<24
j13 := uint32(k[24]) | uint32(k[25])<<8 | uint32(k[26])<<16 | uint32(k[27])<<24
j14 := uint32(k[28]) | uint32(k[29])<<8 | uint32(k[30])<<16 | uint32(k[31])<<24
j15 := uint32(c[12]) | uint32(c[13])<<8 | uint32(c[14])<<16 | uint32(c[15])<<24
x0, x1, x2, x3, x4, x5, x6, x7, x8 := j0, j1, j2, j3, j4, j5, j6, j7, j8
x9, x10, x11, x12, x13, x14, x15 := j9, j10, j11, j12, j13, j14, j15
for i := 0; i < rounds; i += 2 {
u := x0 + x12
x4 ^= u<<7 | u>>(32-7)
u = x4 + x0
x8 ^= u<<9 | u>>(32-9)
u = x8 + x4
x12 ^= u<<13 | u>>(32-13)
u = x12 + x8
x0 ^= u<<18 | u>>(32-18)
u = x5 + x1
x9 ^= u<<7 | u>>(32-7)
u = x9 + x5
x13 ^= u<<9 | u>>(32-9)
u = x13 + x9
x1 ^= u<<13 | u>>(32-13)
u = x1 + x13
x5 ^= u<<18 | u>>(32-18)
u = x10 + x6
x14 ^= u<<7 | u>>(32-7)
u = x14 + x10
x2 ^= u<<9 | u>>(32-9)
u = x2 + x14
x6 ^= u<<13 | u>>(32-13)
u = x6 + x2
x10 ^= u<<18 | u>>(32-18)
u = x15 + x11
x3 ^= u<<7 | u>>(32-7)
u = x3 + x15
x7 ^= u<<9 | u>>(32-9)
u = x7 + x3
x11 ^= u<<13 | u>>(32-13)
u = x11 + x7
x15 ^= u<<18 | u>>(32-18)
u = x0 + x3
x1 ^= u<<7 | u>>(32-7)
u = x1 + x0
x2 ^= u<<9 | u>>(32-9)
u = x2 + x1
x3 ^= u<<13 | u>>(32-13)
u = x3 + x2
x0 ^= u<<18 | u>>(32-18)
u = x5 + x4
x6 ^= u<<7 | u>>(32-7)
u = x6 + x5
x7 ^= u<<9 | u>>(32-9)
u = x7 + x6
x4 ^= u<<13 | u>>(32-13)
u = x4 + x7
x5 ^= u<<18 | u>>(32-18)
u = x10 + x9
x11 ^= u<<7 | u>>(32-7)
u = x11 + x10
x8 ^= u<<9 | u>>(32-9)
u = x8 + x11
x9 ^= u<<13 | u>>(32-13)
u = x9 + x8
x10 ^= u<<18 | u>>(32-18)
u = x15 + x14
x12 ^= u<<7 | u>>(32-7)
u = x12 + x15
x13 ^= u<<9 | u>>(32-9)
u = x13 + x12
x14 ^= u<<13 | u>>(32-13)
u = x14 + x13
x15 ^= u<<18 | u>>(32-18)
}
x0 += j0
x1 += j1
x2 += j2
x3 += j3
x4 += j4
x5 += j5
x6 += j6
x7 += j7
x8 += j8
x9 += j9
x10 += j10
x11 += j11
x12 += j12
x13 += j13
x14 += j14
x15 += j15
out[0] = byte(x0)
out[1] = byte(x0 >> 8)
out[2] = byte(x0 >> 16)
out[3] = byte(x0 >> 24)
out[4] = byte(x1)
out[5] = byte(x1 >> 8)
out[6] = byte(x1 >> 16)
out[7] = byte(x1 >> 24)
out[8] = byte(x2)
out[9] = byte(x2 >> 8)
out[10] = byte(x2 >> 16)
out[11] = byte(x2 >> 24)
out[12] = byte(x3)
out[13] = byte(x3 >> 8)
out[14] = byte(x3 >> 16)
out[15] = byte(x3 >> 24)
out[16] = byte(x4)
out[17] = byte(x4 >> 8)
out[18] = byte(x4 >> 16)
out[19] = byte(x4 >> 24)
out[20] = byte(x5)
out[21] = byte(x5 >> 8)
out[22] = byte(x5 >> 16)
out[23] = byte(x5 >> 24)
out[24] = byte(x6)
out[25] = byte(x6 >> 8)
out[26] = byte(x6 >> 16)
out[27] = byte(x6 >> 24)
out[28] = byte(x7)
out[29] = byte(x7 >> 8)
out[30] = byte(x7 >> 16)
out[31] = byte(x7 >> 24)
out[32] = byte(x8)
out[33] = byte(x8 >> 8)
out[34] = byte(x8 >> 16)
out[35] = byte(x8 >> 24)
out[36] = byte(x9)
out[37] = byte(x9 >> 8)
out[38] = byte(x9 >> 16)
out[39] = byte(x9 >> 24)
out[40] = byte(x10)
out[41] = byte(x10 >> 8)
out[42] = byte(x10 >> 16)
out[43] = byte(x10 >> 24)
out[44] = byte(x11)
out[45] = byte(x11 >> 8)
out[46] = byte(x11 >> 16)
out[47] = byte(x11 >> 24)
out[48] = byte(x12)
out[49] = byte(x12 >> 8)
out[50] = byte(x12 >> 16)
out[51] = byte(x12 >> 24)
out[52] = byte(x13)
out[53] = byte(x13 >> 8)
out[54] = byte(x13 >> 16)
out[55] = byte(x13 >> 24)
out[56] = byte(x14)
out[57] = byte(x14 >> 8)
out[58] = byte(x14 >> 16)
out[59] = byte(x14 >> 24)
out[60] = byte(x15)
out[61] = byte(x15 >> 8)
out[62] = byte(x15 >> 16)
out[63] = byte(x15 >> 24)
}
// XORKeyStream crypts bytes from in to out using the given key and counters.
// In and out must overlap entirely or not at all. Counter
// contains the raw salsa20 counter bytes (both nonce and block counter).
func XORKeyStream(out, in []byte, counter *[16]byte, key *[32]byte) {
var block [64]byte
var counterCopy [16]byte
copy(counterCopy[:], counter[:])
for len(in) >= 64 {
core(&block, &counterCopy, key, &Sigma)
for i, x := range block {
out[i] = in[i] ^ x
}
u := uint32(1)
for i := 8; i < 16; i++ {
u += uint32(counterCopy[i])
counterCopy[i] = byte(u)
u >>= 8
}
in = in[64:]
out = out[64:]
}
if len(in) > 0 {
core(&block, &counterCopy, key, &Sigma)
for i, v := range in {
out[i] = v ^ block[i]
}
}
}

View File

@ -0,0 +1,54 @@
// Copyright 2012 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package salsa
import "testing"
func TestCore208(t *testing.T) {
in := [64]byte{
0x7e, 0x87, 0x9a, 0x21, 0x4f, 0x3e, 0xc9, 0x86,
0x7c, 0xa9, 0x40, 0xe6, 0x41, 0x71, 0x8f, 0x26,
0xba, 0xee, 0x55, 0x5b, 0x8c, 0x61, 0xc1, 0xb5,
0x0d, 0xf8, 0x46, 0x11, 0x6d, 0xcd, 0x3b, 0x1d,
0xee, 0x24, 0xf3, 0x19, 0xdf, 0x9b, 0x3d, 0x85,
0x14, 0x12, 0x1e, 0x4b, 0x5a, 0xc5, 0xaa, 0x32,
0x76, 0x02, 0x1d, 0x29, 0x09, 0xc7, 0x48, 0x29,
0xed, 0xeb, 0xc6, 0x8d, 0xb8, 0xb8, 0xc2, 0x5e}
out := [64]byte{
0xa4, 0x1f, 0x85, 0x9c, 0x66, 0x08, 0xcc, 0x99,
0x3b, 0x81, 0xca, 0xcb, 0x02, 0x0c, 0xef, 0x05,
0x04, 0x4b, 0x21, 0x81, 0xa2, 0xfd, 0x33, 0x7d,
0xfd, 0x7b, 0x1c, 0x63, 0x96, 0x68, 0x2f, 0x29,
0xb4, 0x39, 0x31, 0x68, 0xe3, 0xc9, 0xe6, 0xbc,
0xfe, 0x6b, 0xc5, 0xb7, 0xa0, 0x6d, 0x96, 0xba,
0xe4, 0x24, 0xcc, 0x10, 0x2c, 0x91, 0x74, 0x5c,
0x24, 0xad, 0x67, 0x3d, 0xc7, 0x61, 0x8f, 0x81,
}
Core208(&in, &in)
if in != out {
t.Errorf("expected %x, got %x", out, in)
}
}
func TestOutOfBoundsWrite(t *testing.T) {
// encrypted "0123456789"
cipherText := []byte{170, 166, 196, 104, 175, 121, 68, 44, 174, 51}
var counter [16]byte
var key [32]byte
want := "abcdefghij"
plainText := []byte(want)
defer func() {
err := recover()
if err == nil {
t.Error("XORKeyStream expected to panic on len(dst) < len(src), but didn't")
}
if plainText[3] == '3' {
t.Errorf("XORKeyStream did out of bounds write, want %v, got %v", want, string(plainText))
}
}()
XORKeyStream(plainText[:3], cipherText, &counter, &key)
}

26
vendor/src/gopkg.in/macaroon.v2/LICENSE vendored Normal file
View File

@ -0,0 +1,26 @@
Copyright © 2014, Roger Peppe
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of this project nor the names of its contributors
may be used to endorse or promote products derived from this software
without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@ -0,0 +1,159 @@
# macaroon
--
import "gopkg.in/macaroon.v1"
The macaroon package implements macaroons as described in the paper "Macaroons:
Cookies with Contextual Caveats for Decentralized Authorization in the Cloud"
(http://theory.stanford.edu/~ataly/Papers/macaroons.pdf)
See the macaroon bakery packages at http://godoc.org/gopkg.in/macaroon-bakery.v0
for higher level services and operations that use macaroons.
## Usage
#### type Caveat
```go
type Caveat struct {
Id string
Location string
}
```
#### type Macaroon
```go
type Macaroon struct {
}
```
Macaroon holds a macaroon. See Fig. 7 of
http://theory.stanford.edu/~ataly/Papers/macaroons.pdf for a description of the
data contained within. Macaroons are mutable objects - use Clone as appropriate
to avoid unwanted mutation.
#### func New
```go
func New(rootKey []byte, id, loc string) (*Macaroon, error)
```
New returns a new macaroon with the given root key, identifier and location.
#### func (*Macaroon) AddFirstPartyCaveat
```go
func (m *Macaroon) AddFirstPartyCaveat(caveatId string) error
```
AddFirstPartyCaveat adds a caveat that will be verified by the target service.
#### func (*Macaroon) AddThirdPartyCaveat
```go
func (m *Macaroon) AddThirdPartyCaveat(rootKey []byte, caveatId string, loc string) error
```
AddThirdPartyCaveat adds a third-party caveat to the macaroon, using the given
shared root key, caveat id and location hint. The caveat id should encode the
root key in some way, either by encrypting it with a key known to the third
party or by holding a reference to it stored in the third party's storage.
#### func (*Macaroon) Bind
```go
func (m *Macaroon) Bind(sig []byte)
```
Bind prepares the macaroon for being used to discharge the macaroon with the
given signature sig. This must be used before it is used in the discharges
argument to Verify.
#### func (*Macaroon) Caveats
```go
func (m *Macaroon) Caveats() []Caveat
```
Caveats returns the macaroon's caveats. This method will probably change, and
it's important not to change the returned caveat.
#### func (*Macaroon) Clone
```go
func (m *Macaroon) Clone() *Macaroon
```
Clone returns a copy of the receiving macaroon.
#### func (*Macaroon) Id
```go
func (m *Macaroon) Id() string
```
Id returns the id of the macaroon. This can hold arbitrary information.
#### func (*Macaroon) Location
```go
func (m *Macaroon) Location() string
```
Location returns the macaroon's location hint. This is not verified as part of
the macaroon.
#### func (*Macaroon) MarshalBinary
```go
func (m *Macaroon) MarshalBinary() ([]byte, error)
```
MarshalBinary implements encoding.BinaryMarshaler.
#### func (*Macaroon) MarshalJSON
```go
func (m *Macaroon) MarshalJSON() ([]byte, error)
```
MarshalJSON implements json.Marshaler.
#### func (*Macaroon) Signature
```go
func (m *Macaroon) Signature() []byte
```
Signature returns the macaroon's signature.
#### func (*Macaroon) UnmarshalBinary
```go
func (m *Macaroon) UnmarshalBinary(data []byte) error
```
UnmarshalBinary implements encoding.BinaryUnmarshaler.
#### func (*Macaroon) UnmarshalJSON
```go
func (m *Macaroon) UnmarshalJSON(jsonData []byte) error
```
UnmarshalJSON implements json.Unmarshaler.
#### func (*Macaroon) Verify
```go
func (m *Macaroon) Verify(rootKey []byte, check func(caveat string) error, discharges []*Macaroon) error
```
Verify verifies that the receiving macaroon is valid. The root key must be the
same that the macaroon was originally minted with. The check function is called
to verify each first-party caveat - it should return an error if the condition
is not met.
The discharge macaroons should be provided in discharges.
Verify returns true if the verification succeeds; if returns (false, nil) if the
verification fails, and (false, err) if the verification cannot be asserted (but
may not be false).
TODO(rog) is there a possible DOS attack that can cause this function to
infinitely recurse?
#### type Verifier
```go
type Verifier interface {
Verify(m *Macaroon, rootKey []byte) (bool, error)
}
```

4
vendor/src/gopkg.in/macaroon.v2/TODO vendored Normal file
View File

@ -0,0 +1,4 @@
macaroon:
- verify that all signature calculations to correspond exactly
with libmacaroons.

View File

@ -0,0 +1,109 @@
package macaroon_test
import (
"crypto/rand"
"encoding/base64"
"testing"
"gopkg.in/macaroon.v2"
)
func randomBytes(n int) []byte {
b := make([]byte, n)
_, err := rand.Read(b)
if err != nil {
panic(err)
}
return b
}
func BenchmarkNew(b *testing.B) {
rootKey := randomBytes(24)
id := []byte(base64.StdEncoding.EncodeToString(randomBytes(100)))
loc := base64.StdEncoding.EncodeToString(randomBytes(40))
b.ResetTimer()
for i := b.N - 1; i >= 0; i-- {
MustNew(rootKey, id, loc, macaroon.LatestVersion)
}
}
func BenchmarkAddCaveat(b *testing.B) {
rootKey := randomBytes(24)
id := []byte(base64.StdEncoding.EncodeToString(randomBytes(100)))
loc := base64.StdEncoding.EncodeToString(randomBytes(40))
b.ResetTimer()
for i := b.N - 1; i >= 0; i-- {
b.StopTimer()
m := MustNew(rootKey, id, loc, macaroon.LatestVersion)
b.StartTimer()
m.AddFirstPartyCaveat([]byte("some caveat stuff"))
}
}
func benchmarkVerify(b *testing.B, mspecs []macaroonSpec) {
rootKey, macaroons := makeMacaroons(mspecs)
check := func(string) error {
return nil
}
b.ResetTimer()
for i := b.N - 1; i >= 0; i-- {
err := macaroons[0].Verify(rootKey, check, macaroons[1:])
if err != nil {
b.Fatalf("verification failed: %v", err)
}
}
}
func BenchmarkVerifyLarge(b *testing.B) {
benchmarkVerify(b, multilevelThirdPartyCaveatMacaroons)
}
func BenchmarkVerifySmall(b *testing.B) {
benchmarkVerify(b, []macaroonSpec{{
rootKey: "root-key",
id: "root-id",
caveats: []caveat{{
condition: "wonderful",
}},
}})
}
func BenchmarkMarshalJSON(b *testing.B) {
rootKey := randomBytes(24)
id := []byte(base64.StdEncoding.EncodeToString(randomBytes(100)))
loc := base64.StdEncoding.EncodeToString(randomBytes(40))
m := MustNew(rootKey, id, loc, macaroon.LatestVersion)
b.ResetTimer()
for i := b.N - 1; i >= 0; i-- {
_, err := m.MarshalJSON()
if err != nil {
b.Fatalf("cannot marshal JSON: %v", err)
}
}
}
func MustNew(rootKey, id []byte, loc string, vers macaroon.Version) *macaroon.Macaroon {
m, err := macaroon.New(rootKey, id, loc, vers)
if err != nil {
panic(err)
}
return m
}
func BenchmarkUnmarshalJSON(b *testing.B) {
rootKey := randomBytes(24)
id := []byte(base64.StdEncoding.EncodeToString(randomBytes(100)))
loc := base64.StdEncoding.EncodeToString(randomBytes(40))
m := MustNew(rootKey, id, loc, macaroon.LatestVersion)
data, err := m.MarshalJSON()
if err != nil {
b.Fatalf("cannot marshal JSON: %v", err)
}
for i := b.N - 1; i >= 0; i-- {
var m macaroon.Macaroon
err := m.UnmarshalJSON(data)
if err != nil {
b.Fatalf("cannot unmarshal JSON: %v", err)
}
}
}

View File

@ -0,0 +1,91 @@
package macaroon
import (
"crypto/hmac"
"crypto/sha256"
"fmt"
"hash"
"io"
"golang.org/x/crypto/nacl/secretbox"
)
func keyedHash(key *[hashLen]byte, text []byte) *[hashLen]byte {
h := keyedHasher(key)
h.Write([]byte(text))
var sum [hashLen]byte
hashSum(h, &sum)
return &sum
}
func keyedHasher(key *[hashLen]byte) hash.Hash {
return hmac.New(sha256.New, key[:])
}
var keyGen = []byte("macaroons-key-generator")
// makeKey derives a fixed length key from a variable
// length key. The keyGen constant is the same
// as that used in libmacaroons.
func makeKey(variableKey []byte) *[keyLen]byte {
h := hmac.New(sha256.New, keyGen)
h.Write(variableKey)
var key [keyLen]byte
hashSum(h, &key)
return &key
}
// hashSum calls h.Sum to put the sum into
// the given destination. It also sanity
// checks that the result really is the expected
// size.
func hashSum(h hash.Hash, dest *[hashLen]byte) {
r := h.Sum(dest[:0])
if len(r) != len(dest) {
panic("hash size inconsistency")
}
}
const (
keyLen = 32
nonceLen = 24
hashLen = sha256.Size
)
func newNonce(r io.Reader) (*[nonceLen]byte, error) {
var nonce [nonceLen]byte
_, err := r.Read(nonce[:])
if err != nil {
return nil, fmt.Errorf("cannot generate random bytes: %v", err)
}
return &nonce, nil
}
func encrypt(key *[keyLen]byte, text *[hashLen]byte, r io.Reader) ([]byte, error) {
nonce, err := newNonce(r)
if err != nil {
return nil, err
}
out := make([]byte, 0, len(nonce)+secretbox.Overhead+len(text))
out = append(out, nonce[:]...)
return secretbox.Seal(out, text[:], nonce, key), nil
}
func decrypt(key *[keyLen]byte, ciphertext []byte) (*[hashLen]byte, error) {
if len(ciphertext) < nonceLen+secretbox.Overhead {
return nil, fmt.Errorf("message too short")
}
var nonce [nonceLen]byte
copy(nonce[:], ciphertext)
ciphertext = ciphertext[nonceLen:]
text, ok := secretbox.Open(nil, ciphertext, &nonce, key)
if !ok {
return nil, fmt.Errorf("decryption failure")
}
if len(text) != hashLen {
return nil, fmt.Errorf("decrypted text is wrong length")
}
var rtext [hashLen]byte
copy(rtext[:], text)
return &rtext, nil
}

View File

@ -0,0 +1,66 @@
package macaroon
import (
"crypto/rand"
"fmt"
"golang.org/x/crypto/nacl/secretbox"
gc "gopkg.in/check.v1"
)
type cryptoSuite struct{}
var _ = gc.Suite(&cryptoSuite{})
var testCryptKey = &[hashLen]byte{'k', 'e', 'y'}
var testCryptText = &[hashLen]byte{'t', 'e', 'x', 't'}
func (*cryptoSuite) TestEncDec(c *gc.C) {
b, err := encrypt(testCryptKey, testCryptText, rand.Reader)
c.Assert(err, gc.IsNil)
t, err := decrypt(testCryptKey, b)
c.Assert(err, gc.IsNil)
c.Assert(string(t[:]), gc.Equals, string(testCryptText[:]))
}
func (*cryptoSuite) TestUniqueNonces(c *gc.C) {
nonces := make(map[string]struct{})
for i := 0; i < 100; i++ {
nonce, err := newNonce(rand.Reader)
c.Assert(err, gc.IsNil)
nonces[string(nonce[:])] = struct{}{}
}
c.Assert(nonces, gc.HasLen, 100, gc.Commentf("duplicate nonce detected"))
}
type ErrorReader struct{}
func (*ErrorReader) Read([]byte) (int, error) {
return 0, fmt.Errorf("fail")
}
func (*cryptoSuite) TestBadRandom(c *gc.C) {
_, err := newNonce(&ErrorReader{})
c.Assert(err, gc.ErrorMatches, "^cannot generate random bytes:.*")
_, err = encrypt(testCryptKey, testCryptText, &ErrorReader{})
c.Assert(err, gc.ErrorMatches, "^cannot generate random bytes:.*")
}
func (*cryptoSuite) TestBadCiphertext(c *gc.C) {
buf := randomBytes(nonceLen + secretbox.Overhead)
for i := range buf {
_, err := decrypt(testCryptKey, buf[0:i])
c.Assert(err, gc.ErrorMatches, "message too short")
}
_, err := decrypt(testCryptKey, buf)
c.Assert(err, gc.ErrorMatches, "decryption failure")
}
func randomBytes(n int) []byte {
buf := make([]byte, n)
if _, err := rand.Reader.Read(buf); err != nil {
panic(err)
}
return buf
}

View File

@ -0,0 +1,14 @@
package macaroon
var (
AddThirdPartyCaveatWithRand = (*Macaroon).addThirdPartyCaveatWithRand
)
type MacaroonJSONV2 macaroonJSONV2
// SetVersion sets the version field of m to v;
// usually so that we can compare it for deep equality with
// another differently unmarshaled macaroon.
func (m *Macaroon) SetVersion(v Version) {
m.version = v
}

View File

@ -0,0 +1,366 @@
// The macaroon package implements macaroons as described in
// the paper "Macaroons: Cookies with Contextual Caveats for
// Decentralized Authorization in the Cloud"
// (http://theory.stanford.edu/~ataly/Papers/macaroons.pdf)
//
// See the macaroon bakery packages at http://godoc.org/gopkg.in/macaroon-bakery.v1
// for higher level services and operations that use macaroons.
package macaroon
import (
"bytes"
"crypto/hmac"
"crypto/rand"
"fmt"
"io"
"unicode/utf8"
)
// Macaroon holds a macaroon.
// See Fig. 7 of http://theory.stanford.edu/~ataly/Papers/macaroons.pdf
// for a description of the data contained within.
// Macaroons are mutable objects - use Clone as appropriate
// to avoid unwanted mutation.
type Macaroon struct {
location string
id []byte
caveats []Caveat
sig [hashLen]byte
version Version
}
// Caveat holds a first person or third party caveat.
type Caveat struct {
// Id holds the id of the caveat. For first
// party caveats this holds the condition;
// for third party caveats this holds the encrypted
// third party caveat.
Id []byte
// VerificationId holds the verification id. If this is
// non-empty, it's a third party caveat.
VerificationId []byte
// For third-party caveats, Location holds the
// ocation hint. Note that this is not signature checked
// as part of the caveat, so should only
// be used as a hint.
Location string
}
// isThirdParty reports whether the caveat must be satisfied
// by some third party (if not, it's a first person caveat).
func (cav *Caveat) isThirdParty() bool {
return len(cav.VerificationId) > 0
}
// New returns a new macaroon with the given root key,
// identifier, location and version.
func New(rootKey, id []byte, loc string, version Version) (*Macaroon, error) {
var m Macaroon
if version < V2 {
if !utf8.Valid(id) {
return nil, fmt.Errorf("invalid id for %v macaroon", id)
}
// TODO check id length too.
}
if version < V1 || version > LatestVersion {
return nil, fmt.Errorf("invalid version %v", version)
}
m.version = version
m.init(append([]byte(nil), id...), loc, version)
derivedKey := makeKey(rootKey)
m.sig = *keyedHash(derivedKey, m.id)
return &m, nil
}
// init initializes the macaroon. It retains a reference to id.
func (m *Macaroon) init(id []byte, loc string, vers Version) {
m.location = loc
m.id = append([]byte(nil), id...)
m.version = vers
}
// SetLocation sets the location associated with the macaroon.
// Note that the location is not included in the macaroon's
// hash chain, so this does not change the signature.
func (m *Macaroon) SetLocation(loc string) {
m.location = loc
}
// Clone returns a copy of the receiving macaroon.
func (m *Macaroon) Clone() *Macaroon {
m1 := *m
// Ensure that if any caveats are appended to the new
// macaroon, it will copy the caveats.
m1.caveats = m1.caveats[0:len(m1.caveats):len(m1.caveats)]
return &m1
}
// Location returns the macaroon's location hint. This is
// not verified as part of the macaroon.
func (m *Macaroon) Location() string {
return m.location
}
// Id returns the id of the macaroon. This can hold
// arbitrary information.
func (m *Macaroon) Id() []byte {
return append([]byte(nil), m.id...)
}
// Signature returns the macaroon's signature.
func (m *Macaroon) Signature() []byte {
// sig := m.sig
// return sig[:]
// Work around https://github.com/golang/go/issues/9537
sig := new([hashLen]byte)
*sig = m.sig
return sig[:]
}
// Caveats returns the macaroon's caveats.
// This method will probably change, and it's important not to change the returned caveat.
func (m *Macaroon) Caveats() []Caveat {
return m.caveats[0:len(m.caveats):len(m.caveats)]
}
// appendCaveat appends a caveat without modifying the macaroon's signature.
func (m *Macaroon) appendCaveat(caveatId, verificationId []byte, loc string) {
m.caveats = append(m.caveats, Caveat{
Id: caveatId,
VerificationId: verificationId,
Location: loc,
})
}
func (m *Macaroon) addCaveat(caveatId, verificationId []byte, loc string) error {
if m.version < V2 {
if !utf8.Valid(caveatId) {
return fmt.Errorf("invalid caveat id for %v macaroon", m.version)
}
// TODO check caveat length too.
}
m.appendCaveat(caveatId, verificationId, loc)
if len(verificationId) == 0 {
m.sig = *keyedHash(&m.sig, caveatId)
} else {
m.sig = *keyedHash2(&m.sig, verificationId, caveatId)
}
return nil
}
func keyedHash2(key *[keyLen]byte, d1, d2 []byte) *[hashLen]byte {
var data [hashLen * 2]byte
copy(data[0:], keyedHash(key, d1)[:])
copy(data[hashLen:], keyedHash(key, d2)[:])
return keyedHash(key, data[:])
}
// Bind prepares the macaroon for being used to discharge the
// macaroon with the given signature sig. This must be
// used before it is used in the discharges argument to Verify.
func (m *Macaroon) Bind(sig []byte) {
m.sig = *bindForRequest(sig, &m.sig)
}
// AddFirstPartyCaveat adds a caveat that will be verified
// by the target service.
func (m *Macaroon) AddFirstPartyCaveat(condition []byte) error {
m.addCaveat(condition, nil, "")
return nil
}
// AddThirdPartyCaveat adds a third-party caveat to the macaroon,
// using the given shared root key, caveat id and location hint.
// The caveat id should encode the root key in some
// way, either by encrypting it with a key known to the third party
// or by holding a reference to it stored in the third party's
// storage.
func (m *Macaroon) AddThirdPartyCaveat(rootKey, caveatId []byte, loc string) error {
return m.addThirdPartyCaveatWithRand(rootKey, caveatId, loc, rand.Reader)
}
// addThirdPartyCaveatWithRand adds a third-party caveat to the macaroon, using
// the given source of randomness for encrypting the caveat id.
func (m *Macaroon) addThirdPartyCaveatWithRand(rootKey, caveatId []byte, loc string, r io.Reader) error {
derivedKey := makeKey(rootKey)
verificationId, err := encrypt(&m.sig, derivedKey, r)
if err != nil {
return err
}
m.addCaveat(caveatId, verificationId, loc)
return nil
}
var zeroKey [hashLen]byte
// bindForRequest binds the given macaroon
// to the given signature of its parent macaroon.
func bindForRequest(rootSig []byte, dischargeSig *[hashLen]byte) *[hashLen]byte {
if bytes.Equal(rootSig, dischargeSig[:]) {
return dischargeSig
}
return keyedHash2(&zeroKey, rootSig, dischargeSig[:])
}
// Verify verifies that the receiving macaroon is valid.
// The root key must be the same that the macaroon was originally
// minted with. The check function is called to verify each
// first-party caveat - it should return an error if the
// condition is not met.
//
// The discharge macaroons should be provided in discharges.
//
// Verify returns nil if the verification succeeds.
func (m *Macaroon) Verify(rootKey []byte, check func(caveat string) error, discharges []*Macaroon) error {
var vctx verificationContext
vctx.init(rootKey, m, discharges, check)
return vctx.verify(m, rootKey)
}
// VerifySignature verifies the signature of the given macaroon with respect
// to the root key, but it does not validate any first-party caveats. Instead
// it returns all the applicable first party caveats on success.
//
// The caller is responsible for checking the returned first party caveat
// conditions.
func (m *Macaroon) VerifySignature(rootKey []byte, discharges []*Macaroon) ([]string, error) {
n := len(m.caveats)
for _, dm := range discharges {
n += len(dm.caveats)
}
conds := make([]string, 0, n)
var vctx verificationContext
vctx.init(rootKey, m, discharges, func(cond string) error {
conds = append(conds, cond)
return nil
})
err := vctx.verify(m, rootKey)
if err != nil {
return nil, err
}
return conds, nil
}
// TraceVerify verifies the signature of the macaroon without checking
// any of the first party caveats, and returns a slice of Traces holding
// the operations used when verifying the macaroons.
//
// Each element in the returned slice corresponds to the
// operation for one of the argument macaroons, with m at index 0,
// and discharges at 1 onwards.
func (m *Macaroon) TraceVerify(rootKey []byte, discharges []*Macaroon) ([]Trace, error) {
var vctx verificationContext
vctx.init(rootKey, m, discharges, func(string) error { return nil })
vctx.traces = make([]Trace, len(discharges)+1)
err := vctx.verify(m, rootKey)
return vctx.traces, err
}
type verificationContext struct {
used []bool
discharges []*Macaroon
rootSig *[hashLen]byte
traces []Trace
check func(caveat string) error
}
func (vctx *verificationContext) init(rootKey []byte, root *Macaroon, discharges []*Macaroon, check func(caveat string) error) {
*vctx = verificationContext{
discharges: discharges,
used: make([]bool, len(discharges)),
rootSig: &root.sig,
check: check,
}
}
func (vctx *verificationContext) verify(root *Macaroon, rootKey []byte) error {
vctx.traceRootKey(0, rootKey)
vctx.trace(0, TraceMakeKey, rootKey, nil)
derivedKey := makeKey(rootKey)
if err := vctx.verify0(root, 0, derivedKey); err != nil {
vctx.trace(0, TraceFail, nil, nil)
return err
}
for i, wasUsed := range vctx.used {
if !wasUsed {
vctx.trace(i+1, TraceFail, nil, nil)
return fmt.Errorf("discharge macaroon %q was not used", vctx.discharges[i].Id())
}
}
return nil
}
func (vctx *verificationContext) verify0(m *Macaroon, index int, rootKey *[hashLen]byte) error {
vctx.trace(index, TraceHash, m.id, nil)
caveatSig := keyedHash(rootKey, m.id)
for i, cav := range m.caveats {
if cav.isThirdParty() {
cavKey, err := decrypt(caveatSig, cav.VerificationId)
if err != nil {
return fmt.Errorf("failed to decrypt caveat %d signature: %v", i, err)
}
dm, di, err := vctx.findDischarge(cav.Id)
if err != nil {
return err
}
vctx.traceRootKey(di+1, cavKey[:])
if err := vctx.verify0(dm, di+1, cavKey); err != nil {
vctx.trace(di+1, TraceFail, nil, nil)
return err
}
vctx.trace(index, TraceHash, cav.VerificationId, cav.Id)
caveatSig = keyedHash2(caveatSig, cav.VerificationId, cav.Id)
} else {
vctx.trace(index, TraceHash, cav.Id, nil)
caveatSig = keyedHash(caveatSig, cav.Id)
if err := vctx.check(string(cav.Id)); err != nil {
return err
}
}
}
if index > 0 {
vctx.trace(index, TraceBind, vctx.rootSig[:], caveatSig[:])
caveatSig = bindForRequest(vctx.rootSig[:], caveatSig)
}
// TODO perhaps we should actually do this check before doing
// all the potentially expensive caveat checks.
if !hmac.Equal(caveatSig[:], m.sig[:]) {
return fmt.Errorf("signature mismatch after caveat verification")
}
return nil
}
func (vctx *verificationContext) findDischarge(id []byte) (dm *Macaroon, index int, err error) {
for di, dm := range vctx.discharges {
if !bytes.Equal(dm.id, id) {
continue
}
// Don't use a discharge macaroon more than once.
// It's important that we do this check here rather than after
// verify as it prevents potentially infinite recursion.
if vctx.used[di] {
return nil, 0, fmt.Errorf("discharge macaroon %q was used more than once", dm.Id())
}
vctx.used[di] = true
return dm, di, nil
}
return nil, 0, fmt.Errorf("cannot find discharge macaroon for caveat %x", id)
}
func (vctx *verificationContext) trace(index int, op TraceOpKind, data1, data2 []byte) {
if vctx.traces != nil {
vctx.traces[index].Ops = append(vctx.traces[index].Ops, TraceOp{
Kind: op,
Data1: data1,
Data2: data2,
})
}
}
func (vctx *verificationContext) traceRootKey(index int, rootKey []byte) {
if vctx.traces != nil {
vctx.traces[index].RootKey = rootKey[:]
}
}

View File

@ -0,0 +1,914 @@
package macaroon_test
import (
"encoding/base64"
"encoding/hex"
"encoding/json"
"fmt"
"testing"
jc "github.com/juju/testing/checkers"
gc "gopkg.in/check.v1"
"gopkg.in/macaroon.v2"
)
func TestPackage(t *testing.T) {
gc.TestingT(t)
}
type macaroonSuite struct{}
var _ = gc.Suite(&macaroonSuite{})
func never(string) error {
return fmt.Errorf("condition is never true")
}
func (*macaroonSuite) TestNoCaveats(c *gc.C) {
rootKey := []byte("secret")
m := MustNew(rootKey, []byte("some id"), "a location", macaroon.LatestVersion)
c.Assert(m.Location(), gc.Equals, "a location")
c.Assert(m.Id(), gc.DeepEquals, []byte("some id"))
err := m.Verify(rootKey, never, nil)
c.Assert(err, gc.IsNil)
}
func (*macaroonSuite) TestFirstPartyCaveat(c *gc.C) {
rootKey := []byte("secret")
m := MustNew(rootKey, []byte("some id"), "a location", macaroon.LatestVersion)
caveats := map[string]bool{
"a caveat": true,
"another caveat": true,
}
tested := make(map[string]bool)
for cav := range caveats {
m.AddFirstPartyCaveat([]byte(cav))
}
expectErr := fmt.Errorf("condition not met")
check := func(cav string) error {
tested[cav] = true
if caveats[cav] {
return nil
}
return expectErr
}
err := m.Verify(rootKey, check, nil)
c.Assert(err, gc.IsNil)
c.Assert(tested, gc.DeepEquals, caveats)
m.AddFirstPartyCaveat([]byte("not met"))
err = m.Verify(rootKey, check, nil)
c.Assert(err, gc.Equals, expectErr)
c.Assert(tested["not met"], gc.Equals, true)
}
func (*macaroonSuite) TestThirdPartyCaveat(c *gc.C) {
rootKey := []byte("secret")
m := MustNew(rootKey, []byte("some id"), "a location", macaroon.LatestVersion)
dischargeRootKey := []byte("shared root key")
thirdPartyCaveatId := []byte("3rd party caveat")
err := m.AddThirdPartyCaveat(dischargeRootKey, thirdPartyCaveatId, "remote.com")
c.Assert(err, gc.IsNil)
dm := MustNew(dischargeRootKey, thirdPartyCaveatId, "remote location", macaroon.LatestVersion)
dm.Bind(m.Signature())
err = m.Verify(rootKey, never, []*macaroon.Macaroon{dm})
c.Assert(err, gc.IsNil)
}
func (*macaroonSuite) TestThirdPartyCaveatBadRandom(c *gc.C) {
rootKey := []byte("secret")
m := MustNew(rootKey, []byte("some id"), "a location", macaroon.LatestVersion)
dischargeRootKey := []byte("shared root key")
thirdPartyCaveatId := []byte("3rd party caveat")
err := macaroon.AddThirdPartyCaveatWithRand(m, dischargeRootKey, thirdPartyCaveatId, "remote.com", &macaroon.ErrorReader{})
c.Assert(err, gc.ErrorMatches, "cannot generate random bytes: fail")
}
func (*macaroonSuite) TestSetLocation(c *gc.C) {
rootKey := []byte("secret")
m := MustNew(rootKey, []byte("some id"), "a location", macaroon.LatestVersion)
c.Assert(m.Location(), gc.Equals, "a location")
m.SetLocation("another location")
c.Assert(m.Location(), gc.Equals, "another location")
}
type conditionTest struct {
conditions map[string]bool
expectErr string
}
var verifyTests = []struct {
about string
macaroons []macaroonSpec
conditions []conditionTest
}{{
about: "single third party caveat without discharge",
macaroons: []macaroonSpec{{
rootKey: "root-key",
id: "root-id",
caveats: []caveat{{
condition: "wonderful",
}, {
condition: "bob-is-great",
location: "bob",
rootKey: "bob-caveat-root-key",
}},
}},
conditions: []conditionTest{{
conditions: map[string]bool{
"wonderful": true,
},
expectErr: fmt.Sprintf(`cannot find discharge macaroon for caveat %x`, "bob-is-great"),
}},
}, {
about: "single third party caveat with discharge",
macaroons: []macaroonSpec{{
rootKey: "root-key",
id: "root-id",
caveats: []caveat{{
condition: "wonderful",
}, {
condition: "bob-is-great",
location: "bob",
rootKey: "bob-caveat-root-key",
}},
}, {
location: "bob",
rootKey: "bob-caveat-root-key",
id: "bob-is-great",
}},
conditions: []conditionTest{{
conditions: map[string]bool{
"wonderful": true,
},
}, {
conditions: map[string]bool{
"wonderful": false,
},
expectErr: `condition "wonderful" not met`,
}},
}, {
about: "single third party caveat with discharge with mismatching root key",
macaroons: []macaroonSpec{{
rootKey: "root-key",
id: "root-id",
caveats: []caveat{{
condition: "wonderful",
}, {
condition: "bob-is-great",
location: "bob",
rootKey: "bob-caveat-root-key",
}},
}, {
location: "bob",
rootKey: "bob-caveat-root-key-wrong",
id: "bob-is-great",
}},
conditions: []conditionTest{{
conditions: map[string]bool{
"wonderful": true,
},
expectErr: `signature mismatch after caveat verification`,
}},
}, {
about: "single third party caveat with two discharges",
macaroons: []macaroonSpec{{
rootKey: "root-key",
id: "root-id",
caveats: []caveat{{
condition: "wonderful",
}, {
condition: "bob-is-great",
location: "bob",
rootKey: "bob-caveat-root-key",
}},
}, {
location: "bob",
rootKey: "bob-caveat-root-key",
id: "bob-is-great",
caveats: []caveat{{
condition: "splendid",
}},
}, {
location: "bob",
rootKey: "bob-caveat-root-key",
id: "bob-is-great",
caveats: []caveat{{
condition: "top of the world",
}},
}},
conditions: []conditionTest{{
conditions: map[string]bool{
"wonderful": true,
},
expectErr: `condition "splendid" not met`,
}, {
conditions: map[string]bool{
"wonderful": true,
"splendid": true,
"top of the world": true,
},
expectErr: `discharge macaroon "bob-is-great" was not used`,
}, {
conditions: map[string]bool{
"wonderful": true,
"splendid": false,
"top of the world": true,
},
expectErr: `condition "splendid" not met`,
}, {
conditions: map[string]bool{
"wonderful": true,
"splendid": true,
"top of the world": false,
},
expectErr: `discharge macaroon "bob-is-great" was not used`,
}},
}, {
about: "one discharge used for two macaroons",
macaroons: []macaroonSpec{{
rootKey: "root-key",
id: "root-id",
caveats: []caveat{{
condition: "somewhere else",
location: "bob",
rootKey: "bob-caveat-root-key",
}, {
condition: "bob-is-great",
location: "charlie",
rootKey: "bob-caveat-root-key",
}},
}, {
location: "bob",
rootKey: "bob-caveat-root-key",
id: "somewhere else",
caveats: []caveat{{
condition: "bob-is-great",
location: "charlie",
rootKey: "bob-caveat-root-key",
}},
}, {
location: "bob",
rootKey: "bob-caveat-root-key",
id: "bob-is-great",
}},
conditions: []conditionTest{{
expectErr: `discharge macaroon "bob-is-great" was used more than once`,
}},
}, {
about: "recursive third party caveat",
macaroons: []macaroonSpec{{
rootKey: "root-key",
id: "root-id",
caveats: []caveat{{
condition: "bob-is-great",
location: "bob",
rootKey: "bob-caveat-root-key",
}},
}, {
location: "bob",
rootKey: "bob-caveat-root-key",
id: "bob-is-great",
caveats: []caveat{{
condition: "bob-is-great",
location: "charlie",
rootKey: "bob-caveat-root-key",
}},
}},
conditions: []conditionTest{{
expectErr: `discharge macaroon "bob-is-great" was used more than once`,
}},
}, {
about: "two third party caveats",
macaroons: []macaroonSpec{{
rootKey: "root-key",
id: "root-id",
caveats: []caveat{{
condition: "wonderful",
}, {
condition: "bob-is-great",
location: "bob",
rootKey: "bob-caveat-root-key",
}, {
condition: "charlie-is-great",
location: "charlie",
rootKey: "charlie-caveat-root-key",
}},
}, {
location: "bob",
rootKey: "bob-caveat-root-key",
id: "bob-is-great",
caveats: []caveat{{
condition: "splendid",
}},
}, {
location: "charlie",
rootKey: "charlie-caveat-root-key",
id: "charlie-is-great",
caveats: []caveat{{
condition: "top of the world",
}},
}},
conditions: []conditionTest{{
conditions: map[string]bool{
"wonderful": true,
"splendid": true,
"top of the world": true,
},
}, {
conditions: map[string]bool{
"wonderful": true,
"splendid": false,
"top of the world": true,
},
expectErr: `condition "splendid" not met`,
}, {
conditions: map[string]bool{
"wonderful": true,
"splendid": true,
"top of the world": false,
},
expectErr: `condition "top of the world" not met`,
}},
}, {
about: "third party caveat with undischarged third party caveat",
macaroons: []macaroonSpec{{
rootKey: "root-key",
id: "root-id",
caveats: []caveat{{
condition: "wonderful",
}, {
condition: "bob-is-great",
location: "bob",
rootKey: "bob-caveat-root-key",
}},
}, {
location: "bob",
rootKey: "bob-caveat-root-key",
id: "bob-is-great",
caveats: []caveat{{
condition: "splendid",
}, {
condition: "barbara-is-great",
location: "barbara",
rootKey: "barbara-caveat-root-key",
}},
}},
conditions: []conditionTest{{
conditions: map[string]bool{
"wonderful": true,
"splendid": true,
},
expectErr: fmt.Sprintf(`cannot find discharge macaroon for caveat %x`, "barbara-is-great"),
}},
}, {
about: "multilevel third party caveats",
macaroons: multilevelThirdPartyCaveatMacaroons,
conditions: []conditionTest{{
conditions: map[string]bool{
"wonderful": true,
"splendid": true,
"high-fiving": true,
"spiffing": true,
},
}, {
conditions: map[string]bool{
"wonderful": true,
"splendid": true,
"high-fiving": false,
"spiffing": true,
},
expectErr: `condition "high-fiving" not met`,
}},
}, {
about: "unused discharge",
macaroons: []macaroonSpec{{
rootKey: "root-key",
id: "root-id",
}, {
rootKey: "other-key",
id: "unused",
}},
conditions: []conditionTest{{
expectErr: `discharge macaroon "unused" was not used`,
}},
}}
var multilevelThirdPartyCaveatMacaroons = []macaroonSpec{{
rootKey: "root-key",
id: "root-id",
caveats: []caveat{{
condition: "wonderful",
}, {
condition: "bob-is-great",
location: "bob",
rootKey: "bob-caveat-root-key",
}, {
condition: "charlie-is-great",
location: "charlie",
rootKey: "charlie-caveat-root-key",
}},
}, {
location: "bob",
rootKey: "bob-caveat-root-key",
id: "bob-is-great",
caveats: []caveat{{
condition: "splendid",
}, {
condition: "barbara-is-great",
location: "barbara",
rootKey: "barbara-caveat-root-key",
}},
}, {
location: "charlie",
rootKey: "charlie-caveat-root-key",
id: "charlie-is-great",
caveats: []caveat{{
condition: "splendid",
}, {
condition: "celine-is-great",
location: "celine",
rootKey: "celine-caveat-root-key",
}},
}, {
location: "barbara",
rootKey: "barbara-caveat-root-key",
id: "barbara-is-great",
caveats: []caveat{{
condition: "spiffing",
}, {
condition: "ben-is-great",
location: "ben",
rootKey: "ben-caveat-root-key",
}},
}, {
location: "ben",
rootKey: "ben-caveat-root-key",
id: "ben-is-great",
}, {
location: "celine",
rootKey: "celine-caveat-root-key",
id: "celine-is-great",
caveats: []caveat{{
condition: "high-fiving",
}},
}}
func (*macaroonSuite) TestVerify(c *gc.C) {
for i, test := range verifyTests {
c.Logf("test %d: %s", i, test.about)
rootKey, macaroons := makeMacaroons(test.macaroons)
for _, cond := range test.conditions {
c.Logf("conditions %#v", cond.conditions)
check := func(cav string) error {
if cond.conditions[cav] {
return nil
}
return fmt.Errorf("condition %q not met", cav)
}
err := macaroons[0].Verify(
rootKey,
check,
macaroons[1:],
)
if cond.expectErr != "" {
c.Assert(err, gc.ErrorMatches, cond.expectErr)
} else {
c.Assert(err, gc.IsNil)
}
// Cloned macaroon should have same verify result.
cloneErr := macaroons[0].Clone().Verify(rootKey, check, macaroons[1:])
c.Assert(cloneErr, gc.DeepEquals, err)
}
}
}
func (*macaroonSuite) TestTraceVerify(c *gc.C) {
rootKey, macaroons := makeMacaroons(multilevelThirdPartyCaveatMacaroons)
traces, err := macaroons[0].TraceVerify(rootKey, macaroons[1:])
c.Assert(err, gc.Equals, nil)
c.Assert(traces, gc.HasLen, len(macaroons))
// Check that we can run through the resulting operations and
// arrive at the same signature.
for i, m := range macaroons {
r := traces[i].Results()
c.Assert(b64str(r[len(r)-1]), gc.Equals, b64str(m.Signature()), gc.Commentf("macaroon %d", i))
}
}
func (*macaroonSuite) TestTraceVerifyFailure(c *gc.C) {
rootKey, macaroons := makeMacaroons([]macaroonSpec{{
rootKey: "xxx",
id: "hello",
caveats: []caveat{{
condition: "cond1",
}, {
condition: "cond2",
}, {
condition: "cond3",
}},
}})
// Marshal the macaroon, corrupt a condition, then unmarshal
// it and check we see the expected trace failure.
data, err := json.Marshal(macaroons[0])
c.Assert(err, gc.Equals, nil)
var jm macaroon.MacaroonJSONV2
err = json.Unmarshal(data, &jm)
c.Assert(err, gc.Equals, nil)
jm.Caveats[1].CID = "cond2 corrupted"
data, err = json.Marshal(jm)
c.Assert(err, gc.Equals, nil)
var corruptm *macaroon.Macaroon
err = json.Unmarshal(data, &corruptm)
c.Assert(err, gc.Equals, nil)
traces, err := corruptm.TraceVerify(rootKey, nil)
c.Assert(err, gc.ErrorMatches, `signature mismatch after caveat verification`)
c.Assert(traces, gc.HasLen, 1)
var kinds []macaroon.TraceOpKind
for _, op := range traces[0].Ops {
kinds = append(kinds, op.Kind)
}
c.Assert(kinds, gc.DeepEquals, []macaroon.TraceOpKind{
macaroon.TraceMakeKey,
macaroon.TraceHash, // id
macaroon.TraceHash, // cond1
macaroon.TraceHash, // cond2
macaroon.TraceHash, // cond3
macaroon.TraceFail, // sig mismatch
})
}
func b64str(b []byte) string {
return base64.StdEncoding.EncodeToString(b)
}
func (*macaroonSuite) TestVerifySignature(c *gc.C) {
rootKey, macaroons := makeMacaroons([]macaroonSpec{{
rootKey: "xxx",
id: "hello",
caveats: []caveat{{
rootKey: "y",
condition: "something",
location: "somewhere",
}, {
condition: "cond1",
}, {
condition: "cond2",
}},
}, {
rootKey: "y",
id: "something",
caveats: []caveat{{
condition: "cond3",
}, {
condition: "cond4",
}},
}})
conds, err := macaroons[0].VerifySignature(rootKey, macaroons[1:])
c.Assert(err, gc.IsNil)
c.Assert(conds, jc.DeepEquals, []string{"cond3", "cond4", "cond1", "cond2"})
conds, err = macaroons[0].VerifySignature(nil, macaroons[1:])
c.Assert(err, gc.ErrorMatches, `failed to decrypt caveat 0 signature: decryption failure`)
c.Assert(conds, gc.IsNil)
}
// TODO(rog) move the following JSON-marshal tests into marshal_test.go.
// jsonTestVersions holds the various possible ways of marshaling a macaroon
// to JSON.
var jsonTestVersions = []macaroon.Version{
macaroon.V1,
macaroon.V2,
}
func (s *macaroonSuite) TestMarshalJSON(c *gc.C) {
for i, vers := range jsonTestVersions {
c.Logf("test %d: %v", i, vers)
s.testMarshalJSONWithVersion(c, vers)
}
}
func (*macaroonSuite) testMarshalJSONWithVersion(c *gc.C, vers macaroon.Version) {
rootKey := []byte("secret")
m0 := MustNew(rootKey, []byte("some id"), "a location", vers)
m0.AddFirstPartyCaveat([]byte("account = 3735928559"))
m0JSON, err := json.Marshal(m0)
c.Assert(err, gc.IsNil)
var m1 macaroon.Macaroon
err = json.Unmarshal(m0JSON, &m1)
c.Assert(err, gc.IsNil)
c.Assert(m0.Location(), gc.Equals, m1.Location())
c.Assert(string(m0.Id()), gc.Equals, string(m1.Id()))
c.Assert(
hex.EncodeToString(m0.Signature()),
gc.Equals,
hex.EncodeToString(m1.Signature()))
c.Assert(m1.Version(), gc.Equals, vers)
}
var jsonRoundTripTests = []struct {
about string
// data holds the marshaled data. All the data values hold
// different encodings of the same macaroon - the same as produced
// from the second example in libmacaroons
// example README with the following libmacaroons code:
//
// secret = 'this is a different super-secret key; never use the same secret twice'
// public = 'we used our other secret key'
// location = 'http://mybank/'
// M = macaroons.create(location, secret, public)
// M = M.add_first_party_caveat('account = 3735928559')
// caveat_key = '4; guaranteed random by a fair toss of the dice'
// predicate = 'user = Alice'
// identifier = 'this was how we remind auth of key/pred'
// M = M.add_third_party_caveat('http://auth.mybank/', caveat_key, identifier)
// m.serialize_json()
data string
expectExactRoundTrip bool
expectVers macaroon.Version
}{{
about: "exact JSON as produced by libmacaroons",
data: `{"caveats":[{"cid":"account = 3735928559"},{"cid":"this was how we remind auth of key\/pred","vid":"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA027FAuBYhtHwJ58FX6UlVNFtFsGxQHS7uD_w_dedwv4Jjw7UorCREw5rXbRqIKhr","cl":"http:\/\/auth.mybank\/"}],"location":"http:\/\/mybank\/","identifier":"we used our other secret key","signature":"d27db2fd1f22760e4c3dae8137e2d8fc1df6c0741c18aed4b97256bf78d1f55c"}`,
expectVers: macaroon.V1,
expectExactRoundTrip: true,
}, {
about: "V2 object with std base-64 binary values",
data: `{"c":[{"i64":"YWNjb3VudCA9IDM3MzU5Mjg1NTk="},{"i64":"dGhpcyB3YXMgaG93IHdlIHJlbWluZCBhdXRoIG9mIGtleS9wcmVk","v64":"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA027FAuBYhtHwJ58FX6UlVNFtFsGxQHS7uD/w/dedwv4Jjw7UorCREw5rXbRqIKhr","l":"http://auth.mybank/"}],"l":"http://mybank/","i64":"d2UgdXNlZCBvdXIgb3RoZXIgc2VjcmV0IGtleQ==","s64":"0n2y/R8idg5MPa6BN+LY/B32wHQcGK7UuXJWv3jR9Vw="}`,
expectVers: macaroon.V2,
}, {
about: "V2 object with URL base-64 binary values",
data: `{"c":[{"i64":"YWNjb3VudCA9IDM3MzU5Mjg1NTk"},{"i64":"dGhpcyB3YXMgaG93IHdlIHJlbWluZCBhdXRoIG9mIGtleS9wcmVk","v64":"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA027FAuBYhtHwJ58FX6UlVNFtFsGxQHS7uD_w_dedwv4Jjw7UorCREw5rXbRqIKhr","l":"http://auth.mybank/"}],"l":"http://mybank/","i64":"d2UgdXNlZCBvdXIgb3RoZXIgc2VjcmV0IGtleQ","s64":"0n2y_R8idg5MPa6BN-LY_B32wHQcGK7UuXJWv3jR9Vw"}`,
expectVers: macaroon.V2,
}, {
about: "V2 object with URL base-64 binary values and strings for ASCII",
data: `{"c":[{"i":"account = 3735928559"},{"i":"this was how we remind auth of key/pred","v64":"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA027FAuBYhtHwJ58FX6UlVNFtFsGxQHS7uD_w_dedwv4Jjw7UorCREw5rXbRqIKhr","l":"http://auth.mybank/"}],"l":"http://mybank/","i":"we used our other secret key","s64":"0n2y_R8idg5MPa6BN-LY_B32wHQcGK7UuXJWv3jR9Vw"}`,
expectVers: macaroon.V2,
expectExactRoundTrip: true,
}, {
about: "V2 base64 encoded binary",
data: `"` +
base64.StdEncoding.EncodeToString([]byte(
"\x02"+
"\x01\x0ehttp://mybank/"+
"\x02\x1cwe used our other secret key"+
"\x00"+
"\x02\x14account = 3735928559"+
"\x00"+
"\x01\x13http://auth.mybank/"+
"\x02'this was how we remind auth of key/pred"+
"\x04\x48\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xd3\x6e\xc5\x02\xe0\x58\x86\xd1\xf0\x27\x9f\x05\x5f\xa5\x25\x54\xd1\x6d\x16\xc1\xb1\x40\x74\xbb\xb8\x3f\xf0\xfd\xd7\x9d\xc2\xfe\x09\x8f\x0e\xd4\xa2\xb0\x91\x13\x0e\x6b\x5d\xb4\x6a\x20\xa8\x6b"+
"\x00"+
"\x00"+
"\x06\x20\xd2\x7d\xb2\xfd\x1f\x22\x76\x0e\x4c\x3d\xae\x81\x37\xe2\xd8\xfc\x1d\xf6\xc0\x74\x1c\x18\xae\xd4\xb9\x72\x56\xbf\x78\xd1\xf5\x5c",
)) + `"`,
expectVers: macaroon.V2,
}}
func (s *macaroonSuite) TestJSONRoundTrip(c *gc.C) {
for i, test := range jsonRoundTripTests {
c.Logf("test %d (%v) %s", i, test.expectVers, test.about)
s.testJSONRoundTripWithVersion(c, test.data, test.expectVers, test.expectExactRoundTrip)
}
}
func (*macaroonSuite) testJSONRoundTripWithVersion(c *gc.C, jsonData string, vers macaroon.Version, expectExactRoundTrip bool) {
var m macaroon.Macaroon
err := json.Unmarshal([]byte(jsonData), &m)
c.Assert(err, gc.IsNil)
assertLibMacaroonsMacaroon(c, &m)
c.Assert(m.Version(), gc.Equals, vers)
data, err := m.MarshalJSON()
c.Assert(err, gc.IsNil)
if expectExactRoundTrip {
// The data is in canonical form, so we can check that
// the round-tripped data is the same as the original
// data when unmarshalled into an interface{}.
var got interface{}
err = json.Unmarshal(data, &got)
c.Assert(err, gc.IsNil)
var original interface{}
err = json.Unmarshal([]byte(jsonData), &original)
c.Assert(err, gc.IsNil)
c.Assert(got, jc.DeepEquals, original, gc.Commentf("data: %s", data))
}
// Check that we can unmarshal the marshaled data anyway
// and the macaroon still looks the same.
var m1 macaroon.Macaroon
err = m1.UnmarshalJSON(data)
c.Assert(err, gc.IsNil)
assertLibMacaroonsMacaroon(c, &m1)
c.Assert(m.Version(), gc.Equals, vers)
}
// assertLibMacaroonsMacaroon asserts that m looks like the macaroon
// created in the README of the libmacaroons documentation.
// In particular, the signature is the same one reported there.
func assertLibMacaroonsMacaroon(c *gc.C, m *macaroon.Macaroon) {
c.Assert(fmt.Sprintf("%x", m.Signature()), gc.Equals,
"d27db2fd1f22760e4c3dae8137e2d8fc1df6c0741c18aed4b97256bf78d1f55c")
c.Assert(m.Location(), gc.Equals, "http://mybank/")
c.Assert(string(m.Id()), gc.Equals, "we used our other secret key")
c.Assert(m.Caveats(), jc.DeepEquals, []macaroon.Caveat{{
Id: []byte("account = 3735928559"),
}, {
Id: []byte("this was how we remind auth of key/pred"),
VerificationId: decodeB64("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA027FAuBYhtHwJ58FX6UlVNFtFsGxQHS7uD_w_dedwv4Jjw7UorCREw5rXbRqIKhr"),
Location: "http://auth.mybank/",
}})
}
var jsonDecodeErrorTests = []struct {
about string
data string
expectError string
}{{
about: "ambiguous id #1",
data: `{"i": "hello", "i64": "abcd", "s64": "ZDI3ZGIyZmQxZjIyNzYwZTRjM2RhZTgxMzdlMmQ4ZmMK"}`,
expectError: "invalid identifier: ambiguous field encoding",
}, {
about: "ambiguous signature",
data: `{"i": "hello", "s": "345", "s64": "543467"}`,
expectError: "invalid signature: ambiguous field encoding",
}, {
about: "signature too short",
data: `{"i": "hello", "s64": "0n2y/R8idg5MPa6BN+LY/B32wHQcGK7UuXJWv3jR9Q"}`,
expectError: "signature has unexpected length 31",
}, {
about: "signature too long",
data: `{"i": "hello", "s64": "0n2y/R8idg5MPa6BN+LY/B32wHQcGK7UuXJWv3jR9dP1"}`,
expectError: "signature has unexpected length 33",
}, {
about: "invalid caveat id",
data: `{"i": "hello", "s64": "0n2y/R8idg5MPa6BN+LY/B32wHQcGK7UuXJWv3jR9Vw", "c": [{"i": "hello", "i64": "00"}]}`,
expectError: "invalid cid in caveat: ambiguous field encoding",
}, {
about: "invalid caveat vid",
data: `{"i": "hello", "s64": "0n2y/R8idg5MPa6BN+LY/B32wHQcGK7UuXJWv3jR9Vw", "c": [{"i": "hello", "v": "hello", "v64": "00"}]}`,
expectError: "invalid vid in caveat: ambiguous field encoding",
}}
func (*macaroonSuite) TestJSONDecodeError(c *gc.C) {
for i, test := range jsonDecodeErrorTests {
c.Logf("test %d: %v", i, test.about)
var m macaroon.Macaroon
err := json.Unmarshal([]byte(test.data), &m)
c.Assert(err, gc.ErrorMatches, test.expectError)
}
}
func (*macaroonSuite) TestFirstPartyCaveatWithInvalidUTF8(c *gc.C) {
rootKey := []byte("secret")
badString := "foo\xff"
m0 := MustNew(rootKey, []byte("some id"), "a location", macaroon.LatestVersion)
err := m0.AddFirstPartyCaveat([]byte(badString))
c.Assert(err, gc.Equals, nil)
}
func decodeB64(s string) []byte {
data, err := base64.RawURLEncoding.DecodeString(s)
if err != nil {
panic(err)
}
return data
}
type caveat struct {
rootKey string
location string
condition string
}
type macaroonSpec struct {
rootKey string
id string
caveats []caveat
location string
}
func makeMacaroons(mspecs []macaroonSpec) (rootKey []byte, macaroons macaroon.Slice) {
for _, mspec := range mspecs {
macaroons = append(macaroons, makeMacaroon(mspec))
}
primary := macaroons[0]
for _, m := range macaroons[1:] {
m.Bind(primary.Signature())
}
return []byte(mspecs[0].rootKey), macaroons
}
func makeMacaroon(mspec macaroonSpec) *macaroon.Macaroon {
m := MustNew([]byte(mspec.rootKey), []byte(mspec.id), mspec.location, macaroon.LatestVersion)
for _, cav := range mspec.caveats {
if cav.location != "" {
err := m.AddThirdPartyCaveat([]byte(cav.rootKey), []byte(cav.condition), cav.location)
if err != nil {
panic(err)
}
} else {
m.AddFirstPartyCaveat([]byte(cav.condition))
}
}
return m
}
func assertEqualMacaroons(c *gc.C, m0, m1 *macaroon.Macaroon) {
m0json, err := m0.MarshalJSON()
c.Assert(err, gc.IsNil)
m1json, err := m1.MarshalJSON()
var m0val, m1val interface{}
err = json.Unmarshal(m0json, &m0val)
c.Assert(err, gc.IsNil)
err = json.Unmarshal(m1json, &m1val)
c.Assert(err, gc.IsNil)
c.Assert(m0val, gc.DeepEquals, m1val)
}
func (*macaroonSuite) TestBinaryRoundTrip(c *gc.C) {
// Test the binary marshalling and unmarshalling of a macaroon with
// first and third party caveats.
rootKey := []byte("secret")
m0 := MustNew(rootKey, []byte("some id"), "a location", macaroon.LatestVersion)
err := m0.AddFirstPartyCaveat([]byte("first caveat"))
c.Assert(err, gc.IsNil)
err = m0.AddFirstPartyCaveat([]byte("second caveat"))
c.Assert(err, gc.IsNil)
err = m0.AddThirdPartyCaveat([]byte("shared root key"), []byte("3rd party caveat"), "remote.com")
c.Assert(err, gc.IsNil)
data, err := m0.MarshalBinary()
c.Assert(err, gc.IsNil)
var m1 macaroon.Macaroon
err = m1.UnmarshalBinary(data)
c.Assert(err, gc.IsNil)
assertEqualMacaroons(c, m0, &m1)
}
func (*macaroonSuite) TestBinaryMarshalingAgainstLibmacaroon(c *gc.C) {
// Test that a libmacaroon marshalled macaroon can be correctly unmarshaled
data, err := base64.RawURLEncoding.DecodeString(
"MDAxY2xvY2F0aW9uIGh0dHA6Ly9teWJhbmsvCjAwMmNpZGVudGlmaWVyIHdlIHVzZWQgb3VyIG90aGVyIHNlY3JldCBrZXkKMDAxZGNpZCBhY2NvdW50ID0gMzczNTkyODU1OQowMDMwY2lkIHRoaXMgd2FzIGhvdyB3ZSByZW1pbmQgYXV0aCBvZiBrZXkvcHJlZAowMDUxdmlkIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAANNuxQLgWIbR8CefBV-lJVTRbRbBsUB0u7g_8P3XncL-CY8O1KKwkRMOa120aiCoawowMDFiY2wgaHR0cDovL2F1dGgubXliYW5rLwowMDJmc2lnbmF0dXJlINJ9sv0fInYOTD2ugTfi2Pwd9sB0HBiu1LlyVr940fVcCg")
c.Assert(err, gc.IsNil)
var m0 macaroon.Macaroon
err = m0.UnmarshalBinary(data)
c.Assert(err, gc.IsNil)
jsonData := []byte(`{"caveats":[{"cid":"account = 3735928559"},{"cid":"this was how we remind auth of key\/pred","vid":"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA027FAuBYhtHwJ58FX6UlVNFtFsGxQHS7uD_w_dedwv4Jjw7UorCREw5rXbRqIKhr","cl":"http:\/\/auth.mybank\/"}],"location":"http:\/\/mybank\/","identifier":"we used our other secret key","signature":"d27db2fd1f22760e4c3dae8137e2d8fc1df6c0741c18aed4b97256bf78d1f55c"}`)
var m1 macaroon.Macaroon
err = m1.UnmarshalJSON(jsonData)
c.Assert(err, gc.IsNil)
assertEqualMacaroons(c, &m0, &m1)
}
var binaryFieldBase64ChoiceTests = []struct {
id string
expectBase64 bool
}{
{"x", false},
{"\x00", true},
{"\x03\x00", true},
{"a longer id with more stuff", false},
{"a longer id with more stuff and one invalid \xff", true},
{"a longer id with more stuff and one encoded \x00", false},
}
func (*macaroonSuite) TestBinaryFieldBase64Choice(c *gc.C) {
for i, test := range binaryFieldBase64ChoiceTests {
c.Logf("test %d: %q", i, test.id)
m := MustNew([]byte{0}, []byte(test.id), "", macaroon.LatestVersion)
data, err := json.Marshal(m)
c.Assert(err, gc.Equals, nil)
var x struct {
Id *string `json:"i"`
Id64 *string `json:"i64"`
}
err = json.Unmarshal(data, &x)
c.Assert(err, gc.Equals, nil)
if test.expectBase64 {
c.Assert(x.Id64, gc.NotNil)
c.Assert(x.Id, gc.IsNil)
idDec, err := base64.RawURLEncoding.DecodeString(*x.Id64)
c.Assert(err, gc.Equals, nil)
c.Assert(string(idDec), gc.Equals, test.id)
} else {
c.Assert(x.Id64, gc.IsNil)
c.Assert(x.Id, gc.NotNil)
c.Assert(*x.Id, gc.Equals, test.id)
}
}
}

View File

@ -0,0 +1,190 @@
package macaroon
import (
"encoding/base64"
"encoding/hex"
"encoding/json"
"fmt"
"unicode/utf8"
)
// macaroonJSONV1 defines the V1 JSON format for macaroons.
type macaroonJSONV1 struct {
Caveats []caveatJSONV1 `json:"caveats"`
Location string `json:"location"`
Identifier string `json:"identifier"`
Signature string `json:"signature"` // hex-encoded
}
// caveatJSONV1 defines the V1 JSON format for caveats within a macaroon.
type caveatJSONV1 struct {
CID string `json:"cid"`
VID string `json:"vid,omitempty"`
Location string `json:"cl,omitempty"`
}
// marshalJSONV1 marshals the macaroon to the V1 JSON format.
func (m *Macaroon) marshalJSONV1() ([]byte, error) {
if !utf8.Valid(m.id) {
return nil, fmt.Errorf("macaroon id is not valid UTF-8")
}
mjson := macaroonJSONV1{
Location: m.location,
Identifier: string(m.id),
Signature: hex.EncodeToString(m.sig[:]),
Caveats: make([]caveatJSONV1, len(m.caveats)),
}
for i, cav := range m.caveats {
if !utf8.Valid(cav.Id) {
return nil, fmt.Errorf("caveat id is not valid UTF-8")
}
mjson.Caveats[i] = caveatJSONV1{
Location: cav.Location,
CID: string(cav.Id),
VID: base64.RawURLEncoding.EncodeToString(cav.VerificationId),
}
}
data, err := json.Marshal(mjson)
if err != nil {
return nil, fmt.Errorf("cannot marshal json data: %v", err)
}
return data, nil
}
// initJSONV1 initializes m from the JSON-unmarshaled data
// held in mjson.
func (m *Macaroon) initJSONV1(mjson *macaroonJSONV1) error {
m.init([]byte(mjson.Identifier), mjson.Location, V1)
sig, err := hex.DecodeString(mjson.Signature)
if err != nil {
return fmt.Errorf("cannot decode macaroon signature %q: %v", m.sig, err)
}
if len(sig) != hashLen {
return fmt.Errorf("signature has unexpected length %d", len(sig))
}
copy(m.sig[:], sig)
m.caveats = m.caveats[:0]
for _, cav := range mjson.Caveats {
vid, err := Base64Decode([]byte(cav.VID))
if err != nil {
return fmt.Errorf("cannot decode verification id %q: %v", cav.VID, err)
}
m.appendCaveat([]byte(cav.CID), vid, cav.Location)
}
return nil
}
// The original (v1) binary format of a macaroon is as follows.
// Each identifier represents a v1 packet.
//
// location
// identifier
// (
// caveatId?
// verificationId?
// caveatLocation?
// )*
// signature
// parseBinaryV1 parses the given data in V1 format into the macaroon. The macaroon's
// internal data structures will retain references to the data. It
// returns the data after the end of the macaroon.
func (m *Macaroon) parseBinaryV1(data []byte) ([]byte, error) {
var err error
loc, err := expectPacketV1(data, fieldNameLocation)
if err != nil {
return nil, err
}
data = data[loc.totalLen:]
id, err := expectPacketV1(data, fieldNameIdentifier)
if err != nil {
return nil, err
}
data = data[id.totalLen:]
m.init(id.data, string(loc.data), V1)
var cav Caveat
for {
p, err := parsePacketV1(data)
if err != nil {
return nil, err
}
data = data[p.totalLen:]
switch field := string(p.fieldName); field {
case fieldNameSignature:
// At the end of the caveats we find the signature.
if cav.Id != nil {
m.caveats = append(m.caveats, cav)
}
if len(p.data) != hashLen {
return nil, fmt.Errorf("signature has unexpected length %d", len(p.data))
}
copy(m.sig[:], p.data)
return data, nil
case fieldNameCaveatId:
if cav.Id != nil {
m.caveats = append(m.caveats, cav)
cav = Caveat{}
}
cav.Id = p.data
case fieldNameVerificationId:
if cav.VerificationId != nil {
return nil, fmt.Errorf("repeated field %q in caveat", fieldNameVerificationId)
}
cav.VerificationId = p.data
case fieldNameCaveatLocation:
if cav.Location != "" {
return nil, fmt.Errorf("repeated field %q in caveat", fieldNameLocation)
}
cav.Location = string(p.data)
default:
return nil, fmt.Errorf("unexpected field %q", field)
}
}
}
func expectPacketV1(data []byte, kind string) (packetV1, error) {
p, err := parsePacketV1(data)
if err != nil {
return packetV1{}, err
}
if field := string(p.fieldName); field != kind {
return packetV1{}, fmt.Errorf("unexpected field %q; expected %s", field, kind)
}
return p, nil
}
// appendBinaryV1 appends the binary encoding of m to data.
func (m *Macaroon) appendBinaryV1(data []byte) ([]byte, error) {
var ok bool
data, ok = appendPacketV1(data, fieldNameLocation, []byte(m.location))
if !ok {
return nil, fmt.Errorf("failed to append location to macaroon, packet is too long")
}
data, ok = appendPacketV1(data, fieldNameIdentifier, m.id)
if !ok {
return nil, fmt.Errorf("failed to append identifier to macaroon, packet is too long")
}
for _, cav := range m.caveats {
data, ok = appendPacketV1(data, fieldNameCaveatId, cav.Id)
if !ok {
return nil, fmt.Errorf("failed to append caveat id to macaroon, packet is too long")
}
if cav.VerificationId == nil {
continue
}
data, ok = appendPacketV1(data, fieldNameVerificationId, cav.VerificationId)
if !ok {
return nil, fmt.Errorf("failed to append verification id to macaroon, packet is too long")
}
data, ok = appendPacketV1(data, fieldNameCaveatLocation, []byte(cav.Location))
if !ok {
return nil, fmt.Errorf("failed to append verification id to macaroon, packet is too long")
}
}
data, ok = appendPacketV1(data, fieldNameSignature, m.sig[:])
if !ok {
return nil, fmt.Errorf("failed to append signature to macaroon, packet is too long")
}
return data, nil
}

View File

@ -0,0 +1,253 @@
package macaroon
import (
"encoding/base64"
"encoding/json"
"fmt"
"unicode/utf8"
)
// macaroonJSONV2 defines the V2 JSON format for macaroons.
type macaroonJSONV2 struct {
Caveats []caveatJSONV2 `json:"c,omitempty"`
Location string `json:"l,omitempty"`
Identifier string `json:"i,omitempty"`
Identifier64 string `json:"i64,omitempty"`
Signature string `json:"s,omitempty"`
Signature64 string `json:"s64,omitempty"`
}
// caveatJSONV2 defines the V2 JSON format for caveats within a macaroon.
type caveatJSONV2 struct {
CID string `json:"i,omitempty"`
CID64 string `json:"i64,omitempty"`
VID string `json:"v,omitempty"`
VID64 string `json:"v64,omitempty"`
Location string `json:"l,omitempty"`
}
func (m *Macaroon) marshalJSONV2() ([]byte, error) {
mjson := macaroonJSONV2{
Location: m.location,
Caveats: make([]caveatJSONV2, len(m.caveats)),
}
putJSONBinaryField(m.id, &mjson.Identifier, &mjson.Identifier64)
putJSONBinaryField(m.sig[:], &mjson.Signature, &mjson.Signature64)
for i, cav := range m.caveats {
cavjson := caveatJSONV2{
Location: cav.Location,
}
putJSONBinaryField(cav.Id, &cavjson.CID, &cavjson.CID64)
putJSONBinaryField(cav.VerificationId, &cavjson.VID, &cavjson.VID64)
mjson.Caveats[i] = cavjson
}
data, err := json.Marshal(mjson)
if err != nil {
return nil, fmt.Errorf("cannot marshal json data: %v", err)
}
return data, nil
}
// initJSONV2 initializes m from the JSON-unmarshaled data
// held in mjson.
func (m *Macaroon) initJSONV2(mjson *macaroonJSONV2) error {
id, err := jsonBinaryField(mjson.Identifier, mjson.Identifier64)
if err != nil {
return fmt.Errorf("invalid identifier: %v", err)
}
m.init(id, mjson.Location, V2)
sig, err := jsonBinaryField(mjson.Signature, mjson.Signature64)
if err != nil {
return fmt.Errorf("invalid signature: %v", err)
}
if len(sig) != hashLen {
return fmt.Errorf("signature has unexpected length %d", len(sig))
}
copy(m.sig[:], sig)
m.caveats = make([]Caveat, 0, len(mjson.Caveats))
for _, cav := range mjson.Caveats {
cid, err := jsonBinaryField(cav.CID, cav.CID64)
if err != nil {
return fmt.Errorf("invalid cid in caveat: %v", err)
}
vid, err := jsonBinaryField(cav.VID, cav.VID64)
if err != nil {
return fmt.Errorf("invalid vid in caveat: %v", err)
}
m.appendCaveat(cid, vid, cav.Location)
}
return nil
}
// putJSONBinaryField puts the value of x into one
// of the appropriate fields depending on its value.
func putJSONBinaryField(x []byte, s, sb64 *string) {
if !utf8.Valid(x) {
*sb64 = base64.RawURLEncoding.EncodeToString(x)
return
}
// We could use either string or base64 encoding;
// choose the most compact of the two possibilities.
b64len := base64.RawURLEncoding.EncodedLen(len(x))
sx := string(x)
if jsonEnc, _ := json.Marshal(sx); len(jsonEnc)-2 <= b64len+2 {
// The JSON encoding is smaller than the base 64 encoding.
// NB marshaling a string can never return an error;
// it always includes the two quote characters;
// but using base64 also uses two extra characters for the
// "64" suffix on the field name. If all is equal, prefer string
// encoding because it's more readable.
*s = sx
return
}
*sb64 = base64.RawURLEncoding.EncodeToString(x)
}
// jsonBinaryField returns the value of a JSON field that may
// be string, hex or base64-encoded.
func jsonBinaryField(s, sb64 string) ([]byte, error) {
switch {
case s != "":
if sb64 != "" {
return nil, fmt.Errorf("ambiguous field encoding")
}
return []byte(s), nil
case sb64 != "":
return Base64Decode([]byte(sb64))
}
return []byte{}, nil
}
// The v2 binary format of a macaroon is as follows.
// All entries other than the version are packets as
// parsed by parsePacketV2.
//
// version [1 byte]
// location?
// identifier
// eos
// (
// location?
// identifier
// verificationId?
// eos
// )*
// eos
// signature
//
// See also https://github.com/rescrv/libmacaroons/blob/master/doc/format.txt
// parseBinaryV2 parses the given data in V2 format into the macaroon. The macaroon's
// internal data structures will retain references to the data. It
// returns the data after the end of the macaroon.
func (m *Macaroon) parseBinaryV2(data []byte) ([]byte, error) {
// The version has already been checked, so
// skip it.
data = data[1:]
data, section, err := parseSectionV2(data)
if err != nil {
return nil, err
}
var loc string
if len(section) > 0 && section[0].fieldType == fieldLocation {
loc = string(section[0].data)
section = section[1:]
}
if len(section) != 1 || section[0].fieldType != fieldIdentifier {
return nil, fmt.Errorf("invalid macaroon header")
}
id := section[0].data
m.init(id, loc, V2)
for {
rest, section, err := parseSectionV2(data)
if err != nil {
return nil, err
}
data = rest
if len(section) == 0 {
break
}
var cav Caveat
if len(section) > 0 && section[0].fieldType == fieldLocation {
cav.Location = string(section[0].data)
section = section[1:]
}
if len(section) == 0 || section[0].fieldType != fieldIdentifier {
return nil, fmt.Errorf("no identifier in caveat")
}
cav.Id = section[0].data
section = section[1:]
if len(section) == 0 {
// First party caveat.
if cav.Location != "" {
return nil, fmt.Errorf("location not allowed in first party caveat")
}
m.caveats = append(m.caveats, cav)
continue
}
if len(section) != 1 {
return nil, fmt.Errorf("extra fields found in caveat")
}
if section[0].fieldType != fieldVerificationId {
return nil, fmt.Errorf("invalid field found in caveat")
}
cav.VerificationId = section[0].data
m.caveats = append(m.caveats, cav)
}
data, sig, err := parsePacketV2(data)
if err != nil {
return nil, err
}
if sig.fieldType != fieldSignature {
return nil, fmt.Errorf("unexpected field found instead of signature")
}
if len(sig.data) != hashLen {
return nil, fmt.Errorf("signature has unexpected length")
}
copy(m.sig[:], sig.data)
return data, nil
}
// appendBinaryV2 appends the binary-encoded macaroon
// in v2 format to data.
func (m *Macaroon) appendBinaryV2(data []byte) []byte {
// Version byte.
data = append(data, 2)
if len(m.location) > 0 {
data = appendPacketV2(data, packetV2{
fieldType: fieldLocation,
data: []byte(m.location),
})
}
data = appendPacketV2(data, packetV2{
fieldType: fieldIdentifier,
data: m.id,
})
data = appendEOSV2(data)
for _, cav := range m.caveats {
if len(cav.Location) > 0 {
data = appendPacketV2(data, packetV2{
fieldType: fieldLocation,
data: []byte(cav.Location),
})
}
data = appendPacketV2(data, packetV2{
fieldType: fieldIdentifier,
data: cav.Id,
})
if len(cav.VerificationId) > 0 {
data = appendPacketV2(data, packetV2{
fieldType: fieldVerificationId,
data: []byte(cav.VerificationId),
})
}
data = appendEOSV2(data)
}
data = appendEOSV2(data)
data = appendPacketV2(data, packetV2{
fieldType: fieldSignature,
data: m.sig[:],
})
return data
}

View File

@ -0,0 +1,239 @@
package macaroon
import (
"encoding/base64"
"encoding/json"
"fmt"
)
// Version specifies the version of a macaroon.
// In version 1, the macaroon id and all caveats
// must be UTF-8-compatible strings, and the
// size of any part of the macaroon may not exceed
// approximately 64K. In version 2,
// all field may be arbitrary binary blobs.
type Version uint16
const (
// V1 specifies version 1 macaroons.
V1 Version = 1
// V2 specifies version 2 macaroons.
V2 Version = 2
// LatestVersion holds the latest supported version.
LatestVersion = V2
)
// String returns a string representation of the version;
// for example V1 formats as "v1".
func (v Version) String() string {
return fmt.Sprintf("v%d", v)
}
// Version returns the version of the macaroon.
func (m *Macaroon) Version() Version {
return m.version
}
// MarshalJSON implements json.Marshaler by marshaling the
// macaroon in JSON format. The serialisation format is determined
// by the macaroon's version.
func (m *Macaroon) MarshalJSON() ([]byte, error) {
switch m.version {
case V1:
return m.marshalJSONV1()
case V2:
return m.marshalJSONV2()
default:
return nil, fmt.Errorf("unknown version %v", m.version)
}
}
// UnmarshalJSON implements json.Unmarshaller by unmarshaling
// the given macaroon in JSON format. It accepts both V1 and V2
// forms encoded forms, and also a base64-encoded JSON string
// containing the binary-marshaled macaroon.
//
// After unmarshaling, the macaroon's version will reflect
// the version that it was unmarshaled as.
func (m *Macaroon) UnmarshalJSON(data []byte) error {
if data[0] == '"' {
// It's a string, so it must be a base64-encoded binary form.
var s string
if err := json.Unmarshal(data, &s); err != nil {
return err
}
data, err := Base64Decode([]byte(s))
if err != nil {
return err
}
if err := m.UnmarshalBinary(data); err != nil {
return err
}
return nil
}
// Not a string; try to unmarshal into both kinds of macaroon object.
// This assumes that neither format has any fields in common.
// For subsequent versions we may need to change this approach.
type MacaroonJSONV1 macaroonJSONV1
type MacaroonJSONV2 macaroonJSONV2
var both struct {
*MacaroonJSONV1
*MacaroonJSONV2
}
if err := json.Unmarshal(data, &both); err != nil {
return err
}
switch {
case both.MacaroonJSONV1 != nil && both.MacaroonJSONV2 != nil:
return fmt.Errorf("cannot determine macaroon encoding version")
case both.MacaroonJSONV1 != nil:
if err := m.initJSONV1((*macaroonJSONV1)(both.MacaroonJSONV1)); err != nil {
return err
}
m.version = V1
case both.MacaroonJSONV2 != nil:
if err := m.initJSONV2((*macaroonJSONV2)(both.MacaroonJSONV2)); err != nil {
return err
}
m.version = V2
default:
return fmt.Errorf("invalid JSON macaroon encoding")
}
return nil
}
// UnmarshalBinary implements encoding.BinaryUnmarshaler.
// It accepts both V1 and V2 binary encodings.
func (m *Macaroon) UnmarshalBinary(data []byte) error {
// Copy the data to avoid retaining references to it
// in the internal data structures.
data = append([]byte(nil), data...)
_, err := m.parseBinary(data)
return err
}
// parseBinary parses the macaroon in binary format
// from the given data and returns where the parsed data ends.
//
// It retains references to data.
func (m *Macaroon) parseBinary(data []byte) ([]byte, error) {
if len(data) == 0 {
return nil, fmt.Errorf("empty macaroon data")
}
v := data[0]
if v == 2 {
// Version 2 binary format.
data, err := m.parseBinaryV2(data)
if err != nil {
return nil, fmt.Errorf("unmarshal v2: %v", err)
}
m.version = V2
return data, nil
}
if isASCIIHex(v) {
// It's a hex digit - version 1 binary format
data, err := m.parseBinaryV1(data)
if err != nil {
return nil, fmt.Errorf("unmarshal v1: %v", err)
}
m.version = V1
return data, nil
}
return nil, fmt.Errorf("cannot determine data format of binary-encoded macaroon")
}
// MarshalBinary implements encoding.BinaryMarshaler by
// formatting the macaroon according to the version specified
// by MarshalAs.
func (m *Macaroon) MarshalBinary() ([]byte, error) {
return m.appendBinary(nil)
}
// appendBinary appends the binary-formatted macaroon to
// the given data, formatting it according to the macaroon's
// version.
func (m *Macaroon) appendBinary(data []byte) ([]byte, error) {
switch m.version {
case V1:
return m.appendBinaryV1(data)
case V2:
return m.appendBinaryV2(data), nil
default:
return nil, fmt.Errorf("bad macaroon version %v", m.version)
}
}
// Slice defines a collection of macaroons. By convention, the
// first macaroon in the slice is a primary macaroon and the rest
// are discharges for its third party caveats.
type Slice []*Macaroon
// MarshalBinary implements encoding.BinaryMarshaler.
func (s Slice) MarshalBinary() ([]byte, error) {
var data []byte
var err error
for _, m := range s {
data, err = m.appendBinary(data)
if err != nil {
return nil, fmt.Errorf("failed to marshal macaroon %q: %v", m.Id(), err)
}
}
return data, nil
}
// UnmarshalBinary implements encoding.BinaryUnmarshaler.
// It accepts all known binary encodings for the data - all the
// embedded macaroons need not be encoded in the same format.
func (s *Slice) UnmarshalBinary(data []byte) error {
// Prevent the internal data structures from holding onto the
// slice by copying it first.
data = append([]byte(nil), data...)
*s = (*s)[:0]
for len(data) > 0 {
var m Macaroon
rest, err := m.parseBinary(data)
if err != nil {
return fmt.Errorf("cannot unmarshal macaroon: %v", err)
}
*s = append(*s, &m)
data = rest
}
return nil
}
const (
padded = 1 << iota
stdEncoding
)
var codecs = [4]*base64.Encoding{
0: base64.RawURLEncoding,
padded: base64.URLEncoding,
stdEncoding: base64.RawStdEncoding,
stdEncoding | padded: base64.StdEncoding,
}
// Base64Decode base64-decodes the given data.
// It accepts both standard and URL encodings, both
// padded and unpadded.
func Base64Decode(data []byte) ([]byte, error) {
encoding := 0
if len(data) > 0 && data[len(data)-1] == '=' {
encoding |= padded
}
for _, b := range data {
if b == '/' || b == '+' {
encoding |= stdEncoding
break
}
}
codec := codecs[encoding]
buf := make([]byte, codec.DecodedLen(len(data)))
n, err := codec.Decode(buf, data)
if err == nil {
return buf[0:n], nil
}
return nil, err
}

View File

@ -0,0 +1,203 @@
package macaroon_test
import (
jc "github.com/juju/testing/checkers"
gc "gopkg.in/check.v1"
"gopkg.in/macaroon.v2"
)
type marshalSuite struct{}
var _ = gc.Suite(&marshalSuite{})
func (s *marshalSuite) TestMarshalUnmarshalMacaroonV1(c *gc.C) {
s.testMarshalUnmarshalWithVersion(c, macaroon.V1)
}
func (s *marshalSuite) TestMarshalUnmarshalMacaroonV2(c *gc.C) {
s.testMarshalUnmarshalWithVersion(c, macaroon.V2)
}
func (*marshalSuite) testMarshalUnmarshalWithVersion(c *gc.C, vers macaroon.Version) {
rootKey := []byte("secret")
m := MustNew(rootKey, []byte("some id"), "a location", vers)
// Adding the third party caveat before the first party caveat
// tests a former bug where the caveat wasn't zeroed
// before moving to the next caveat.
err := m.AddThirdPartyCaveat([]byte("shared root key"), []byte("3rd party caveat"), "remote.com")
c.Assert(err, gc.IsNil)
err = m.AddFirstPartyCaveat([]byte("a caveat"))
c.Assert(err, gc.IsNil)
b, err := m.MarshalBinary()
c.Assert(err, gc.IsNil)
var um macaroon.Macaroon
err = um.UnmarshalBinary(b)
c.Assert(err, gc.IsNil)
c.Assert(um.Location(), gc.Equals, m.Location())
c.Assert(string(um.Id()), gc.Equals, string(m.Id()))
c.Assert(um.Signature(), jc.DeepEquals, m.Signature())
c.Assert(um.Caveats(), jc.DeepEquals, m.Caveats())
c.Assert(um.Version(), gc.Equals, vers)
um.SetVersion(m.Version())
c.Assert(m, jc.DeepEquals, &um)
}
func (s *marshalSuite) TestMarshalBinaryRoundTrip(c *gc.C) {
// This data holds the V2 binary encoding of
data := []byte(
"\x02" +
"\x01\x0ehttp://mybank/" +
"\x02\x1cwe used our other secret key" +
"\x00" +
"\x02\x14account = 3735928559" +
"\x00" +
"\x01\x13http://auth.mybank/" +
"\x02'this was how we remind auth of key/pred" +
"\x04\x48\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xd3\x6e\xc5\x02\xe0\x58\x86\xd1\xf0\x27\x9f\x05\x5f\xa5\x25\x54\xd1\x6d\x16\xc1\xb1\x40\x74\xbb\xb8\x3f\xf0\xfd\xd7\x9d\xc2\xfe\x09\x8f\x0e\xd4\xa2\xb0\x91\x13\x0e\x6b\x5d\xb4\x6a\x20\xa8\x6b" +
"\x00" +
"\x00" +
"\x06\x20\xd2\x7d\xb2\xfd\x1f\x22\x76\x0e\x4c\x3d\xae\x81\x37\xe2\xd8\xfc\x1d\xf6\xc0\x74\x1c\x18\xae\xd4\xb9\x72\x56\xbf\x78\xd1\xf5\x5c",
)
var m macaroon.Macaroon
err := m.UnmarshalBinary(data)
c.Assert(err, gc.Equals, nil)
assertLibMacaroonsMacaroon(c, &m)
c.Assert(m.Version(), gc.Equals, macaroon.V2)
data1, err := m.MarshalBinary()
c.Assert(err, gc.Equals, nil)
c.Assert(data1, jc.DeepEquals, data)
}
func (s *marshalSuite) TestMarshalUnmarshalSliceV1(c *gc.C) {
s.testMarshalUnmarshalSliceWithVersion(c, macaroon.V1)
}
func (s *marshalSuite) TestMarshalUnmarshalSliceV2(c *gc.C) {
s.testMarshalUnmarshalSliceWithVersion(c, macaroon.V2)
}
func (*marshalSuite) testMarshalUnmarshalSliceWithVersion(c *gc.C, vers macaroon.Version) {
rootKey := []byte("secret")
m1 := MustNew(rootKey, []byte("some id"), "a location", vers)
m2 := MustNew(rootKey, []byte("some other id"), "another location", vers)
err := m1.AddFirstPartyCaveat([]byte("a caveat"))
c.Assert(err, gc.IsNil)
err = m2.AddFirstPartyCaveat([]byte("another caveat"))
c.Assert(err, gc.IsNil)
macaroons := macaroon.Slice{m1, m2}
b, err := macaroons.MarshalBinary()
c.Assert(err, gc.IsNil)
var unmarshaledMacs macaroon.Slice
err = unmarshaledMacs.UnmarshalBinary(b)
c.Assert(err, gc.IsNil)
c.Assert(unmarshaledMacs, gc.HasLen, len(macaroons))
for i, m := range macaroons {
um := unmarshaledMacs[i]
c.Assert(um.Location(), gc.Equals, m.Location())
c.Assert(string(um.Id()), gc.Equals, string(m.Id()))
c.Assert(um.Signature(), jc.DeepEquals, m.Signature())
c.Assert(um.Caveats(), jc.DeepEquals, m.Caveats())
c.Assert(um.Version(), gc.Equals, vers)
um.SetVersion(m.Version())
}
c.Assert(macaroons, jc.DeepEquals, unmarshaledMacs)
// Check that appending a caveat to the first does not
// affect the second.
for i := 0; i < 10; i++ {
err = unmarshaledMacs[0].AddFirstPartyCaveat([]byte("caveat"))
c.Assert(err, gc.IsNil)
}
unmarshaledMacs[1].SetVersion(macaroons[1].Version())
c.Assert(unmarshaledMacs[1], jc.DeepEquals, macaroons[1])
c.Assert(err, gc.IsNil)
}
func (s *marshalSuite) TestSliceRoundTripV1(c *gc.C) {
s.testSliceRoundTripWithVersion(c, macaroon.V1)
}
func (s *marshalSuite) TestSliceRoundTripV2(c *gc.C) {
s.testSliceRoundTripWithVersion(c, macaroon.V2)
}
func (*marshalSuite) testSliceRoundTripWithVersion(c *gc.C, vers macaroon.Version) {
rootKey := []byte("secret")
m1 := MustNew(rootKey, []byte("some id"), "a location", vers)
m2 := MustNew(rootKey, []byte("some other id"), "another location", vers)
err := m1.AddFirstPartyCaveat([]byte("a caveat"))
c.Assert(err, gc.IsNil)
err = m2.AddFirstPartyCaveat([]byte("another caveat"))
c.Assert(err, gc.IsNil)
macaroons := macaroon.Slice{m1, m2}
b, err := macaroons.MarshalBinary()
c.Assert(err, gc.IsNil)
var unmarshaledMacs macaroon.Slice
err = unmarshaledMacs.UnmarshalBinary(b)
c.Assert(err, gc.IsNil)
marshaledMacs, err := unmarshaledMacs.MarshalBinary()
c.Assert(err, gc.IsNil)
c.Assert(b, jc.DeepEquals, marshaledMacs)
}
var base64DecodeTests = []struct {
about string
input string
expect string
expectError string
}{{
about: "empty string",
input: "",
expect: "",
}, {
about: "standard encoding, padded",
input: "Z29+IQ==",
expect: "go~!",
}, {
about: "URL encoding, padded",
input: "Z29-IQ==",
expect: "go~!",
}, {
about: "standard encoding, not padded",
input: "Z29+IQ",
expect: "go~!",
}, {
about: "URL encoding, not padded",
input: "Z29-IQ",
expect: "go~!",
}, {
about: "standard encoding, too much padding",
input: "Z29+IQ===",
expectError: `illegal base64 data at input byte 8`,
}}
func (*marshalSuite) TestBase64Decode(c *gc.C) {
for i, test := range base64DecodeTests {
c.Logf("test %d: %s", i, test.about)
out, err := macaroon.Base64Decode([]byte(test.input))
if test.expectError != "" {
c.Assert(err, gc.ErrorMatches, test.expectError)
} else {
c.Assert(err, gc.Equals, nil)
c.Assert(string(out), gc.Equals, test.expect)
}
}
}

View File

@ -0,0 +1,133 @@
package macaroon
import (
"bytes"
"fmt"
)
// field names, as defined in libmacaroons
const (
fieldNameLocation = "location"
fieldNameIdentifier = "identifier"
fieldNameSignature = "signature"
fieldNameCaveatId = "cid"
fieldNameVerificationId = "vid"
fieldNameCaveatLocation = "cl"
)
// maxPacketV1Len is the maximum allowed length of a packet in the v1 macaroon
// serialization format.
const maxPacketV1Len = 0xffff
// The original macaroon binary encoding is made from a sequence
// of "packets", each of which has a field name and some data.
// The encoding is:
//
// - four ascii hex digits holding the entire packet size (including
// the digits themselves).
//
// - the field name, followed by an ascii space.
//
// - the raw data
//
// - a newline (\n) character
//
// The packet struct below holds a reference into Macaroon.data.
type packetV1 struct {
// ftype holds the field name of the packet.
fieldName []byte
// data holds the packet's data.
data []byte
// len holds the total length in bytes
// of the packet, including any header.
totalLen int
}
// parsePacket parses the packet at the start of the
// given data.
func parsePacketV1(data []byte) (packetV1, error) {
if len(data) < 6 {
return packetV1{}, fmt.Errorf("packet too short")
}
plen, ok := parseSizeV1(data)
if !ok {
return packetV1{}, fmt.Errorf("cannot parse size")
}
if plen > len(data) {
return packetV1{}, fmt.Errorf("packet size too big")
}
if plen < 4 {
return packetV1{}, fmt.Errorf("packet size too small")
}
data = data[4:plen]
i := bytes.IndexByte(data, ' ')
if i <= 0 {
return packetV1{}, fmt.Errorf("cannot parse field name")
}
fieldName := data[0:i]
if data[len(data)-1] != '\n' {
return packetV1{}, fmt.Errorf("no terminating newline found")
}
return packetV1{
fieldName: fieldName,
data: data[i+1 : len(data)-1],
totalLen: plen,
}, nil
}
// appendPacketV1 appends a packet with the given field name
// and data to the given buffer. If the field and data were
// too long to be encoded, it returns nil, false; otherwise
// it returns the appended buffer.
func appendPacketV1(buf []byte, field string, data []byte) ([]byte, bool) {
plen := packetV1Size(field, data)
if plen > maxPacketV1Len {
return nil, false
}
buf = appendSizeV1(buf, plen)
buf = append(buf, field...)
buf = append(buf, ' ')
buf = append(buf, data...)
buf = append(buf, '\n')
return buf, true
}
func packetV1Size(field string, data []byte) int {
return 4 + len(field) + 1 + len(data) + 1
}
var hexDigits = []byte("0123456789abcdef")
func appendSizeV1(data []byte, size int) []byte {
return append(data,
hexDigits[size>>12],
hexDigits[(size>>8)&0xf],
hexDigits[(size>>4)&0xf],
hexDigits[size&0xf],
)
}
func parseSizeV1(data []byte) (int, bool) {
d0, ok0 := asciiHex(data[0])
d1, ok1 := asciiHex(data[1])
d2, ok2 := asciiHex(data[2])
d3, ok3 := asciiHex(data[3])
return d0<<12 + d1<<8 + d2<<4 + d3, ok0 && ok1 && ok2 && ok3
}
func asciiHex(b byte) (int, bool) {
switch {
case b >= '0' && b <= '9':
return int(b) - '0', true
case b >= 'a' && b <= 'f':
return int(b) - 'a' + 0xa, true
}
return 0, false
}
func isASCIIHex(b byte) bool {
_, ok := asciiHex(b)
return ok
}

View File

@ -0,0 +1,98 @@
package macaroon
import (
"strconv"
"strings"
"unicode"
gc "gopkg.in/check.v1"
)
type packetV1Suite struct{}
var _ = gc.Suite(&packetV1Suite{})
func (*packetV1Suite) TestAppendPacket(c *gc.C) {
data, ok := appendPacketV1(nil, "field", []byte("some data"))
c.Assert(ok, gc.Equals, true)
c.Assert(string(data), gc.Equals, "0014field some data\n")
data, ok = appendPacketV1(data, "otherfield", []byte("more and more data"))
c.Assert(ok, gc.Equals, true)
c.Assert(string(data), gc.Equals, "0014field some data\n0022otherfield more and more data\n")
}
func (*packetV1Suite) TestAppendPacketTooBig(c *gc.C) {
data, ok := appendPacketV1(nil, "field", make([]byte, 65532))
c.Assert(ok, gc.Equals, false)
c.Assert(data, gc.IsNil)
}
var parsePacketV1Tests = []struct {
data string
expect packetV1
expectErr string
}{{
expectErr: "packet too short",
}, {
data: "0014field some data\n",
expect: packetV1{
fieldName: []byte("field"),
data: []byte("some data"),
totalLen: 20,
},
}, {
data: "0015field some data\n",
expectErr: "packet size too big",
}, {
data: "0003a\n",
expectErr: "packet size too small",
}, {
data: "0014fieldwithoutanyspaceordata\n",
expectErr: "cannot parse field name",
}, {
data: "fedcsomefield " + strings.Repeat("x", 0xfedc-len("0000somefield \n")) + "\n",
expect: packetV1{
fieldName: []byte("somefield"),
data: []byte(strings.Repeat("x", 0xfedc-len("0000somefield \n"))),
totalLen: 0xfedc,
},
}, {
data: "zzzzbadpacketsizenomacaroon",
expectErr: "cannot parse size",
}}
func (*packetV1Suite) TestParsePacketV1(c *gc.C) {
for i, test := range parsePacketV1Tests {
c.Logf("test %d: %q", i, truncate(test.data))
p, err := parsePacketV1([]byte(test.data))
if test.expectErr != "" {
c.Assert(err, gc.ErrorMatches, test.expectErr)
c.Assert(p, gc.DeepEquals, packetV1{})
continue
}
c.Assert(err, gc.IsNil)
c.Assert(p, gc.DeepEquals, test.expect)
}
}
func truncate(d string) string {
if len(d) > 50 {
return d[0:50] + "..."
}
return d
}
func (*packetV1Suite) TestAsciiHex(c *gc.C) {
for b := 0; b < 256; b++ {
n, err := strconv.ParseInt(string(b), 16, 8)
value, ok := asciiHex(byte(b))
if err != nil || unicode.IsUpper(rune(b)) {
c.Assert(ok, gc.Equals, false)
c.Assert(value, gc.Equals, 0)
} else {
c.Assert(ok, gc.Equals, true)
c.Assert(value, gc.Equals, int(n))
}
}
}

View File

@ -0,0 +1,117 @@
package macaroon
import (
"encoding/binary"
"fmt"
)
type fieldType int
// Field constants as used in the binary encoding.
const (
fieldEOS fieldType = 0
fieldLocation fieldType = 1
fieldIdentifier fieldType = 2
fieldVerificationId fieldType = 4
fieldSignature fieldType = 6
)
type packetV2 struct {
// fieldType holds the type of the field.
fieldType fieldType
// data holds the packet's data.
data []byte
}
// parseSectionV2 parses a sequence of packets
// in data. The sequence is terminated by a packet
// with a field type of fieldEOS.
func parseSectionV2(data []byte) ([]byte, []packetV2, error) {
prevFieldType := fieldType(-1)
var packets []packetV2
for {
if len(data) == 0 {
return nil, nil, fmt.Errorf("section extends past end of buffer")
}
rest, p, err := parsePacketV2(data)
if err != nil {
return nil, nil, err
}
if p.fieldType == fieldEOS {
return rest, packets, nil
}
if p.fieldType <= prevFieldType {
return nil, nil, fmt.Errorf("fields out of order")
}
packets = append(packets, p)
prevFieldType = p.fieldType
data = rest
}
}
// parsePacketV2 parses a V2 data package at the start
// of the given data.
// The format of a packet is as follows:
//
// fieldType(varint) payloadLen(varint) data[payloadLen bytes]
//
// apart from fieldEOS which has no payloadLen or data (it's
// a single zero byte).
func parsePacketV2(data []byte) ([]byte, packetV2, error) {
data, ft, err := parseVarint(data)
if err != nil {
return nil, packetV2{}, err
}
p := packetV2{
fieldType: fieldType(ft),
}
if p.fieldType == fieldEOS {
return data, p, nil
}
data, payloadLen, err := parseVarint(data)
if err != nil {
return nil, packetV2{}, err
}
if payloadLen > len(data) {
return nil, packetV2{}, fmt.Errorf("field data extends past end of buffer")
}
p.data = data[0:payloadLen]
return data[payloadLen:], p, nil
}
// parseVarint parses the variable-length integer
// at the start of the given data and returns rest
// of the buffer and the number.
func parseVarint(data []byte) ([]byte, int, error) {
val, n := binary.Uvarint(data)
if n > 0 {
if val > 0x7fffffff {
return nil, 0, fmt.Errorf("varint value out of range")
}
return data[n:], int(val), nil
}
if n == 0 {
return nil, 0, fmt.Errorf("varint value extends past end of buffer")
}
return nil, 0, fmt.Errorf("varint value out of range")
}
func appendPacketV2(data []byte, p packetV2) []byte {
data = appendVarint(data, int(p.fieldType))
if p.fieldType != fieldEOS {
data = appendVarint(data, len(p.data))
data = append(data, p.data...)
}
return data
}
func appendEOSV2(data []byte) []byte {
return append(data, 0)
}
func appendVarint(data []byte, x int) []byte {
var buf [binary.MaxVarintLen32]byte
n := binary.PutUvarint(buf[:], uint64(x))
return append(data, buf[:n]...)
}

View File

@ -0,0 +1,126 @@
package macaroon
import (
jc "github.com/juju/testing/checkers"
gc "gopkg.in/check.v1"
)
type packetV2Suite struct{}
var _ = gc.Suite(&packetV2Suite{})
var parsePacketV2Tests = []struct {
about string
data string
expectPacket packetV2
expectData string
expectError string
}{{
about: "EOS packet",
data: "\x00",
expectPacket: packetV2{
fieldType: fieldEOS,
},
}, {
about: "simple field",
data: "\x02\x03xyz",
expectPacket: packetV2{
fieldType: 2,
data: []byte("xyz"),
},
}, {
about: "empty buffer",
data: "",
expectError: "varint value extends past end of buffer",
}, {
about: "varint out of range",
data: "\xff\xff\xff\xff\xff\xff\x7f",
expectError: "varint value out of range",
}, {
about: "varint way out of range",
data: "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x7f",
expectError: "varint value out of range",
}, {
about: "unterminated varint",
data: "\x80",
expectError: "varint value extends past end of buffer",
}, {
about: "field data too long",
data: "\x01\x02a",
expectError: "field data extends past end of buffer",
}, {
about: "bad data length varint",
data: "\x01\xff",
expectError: "varint value extends past end of buffer",
}}
func (*packetV2Suite) TestParsePacketV2(c *gc.C) {
for i, test := range parsePacketV2Tests {
c.Logf("test %d: %v", i, test.about)
data, p, err := parsePacketV2([]byte(test.data))
if test.expectError != "" {
c.Assert(err, gc.ErrorMatches, test.expectError)
c.Assert(data, gc.IsNil)
c.Assert(p, gc.DeepEquals, packetV2{})
} else {
c.Assert(err, gc.IsNil)
c.Assert(p, jc.DeepEquals, test.expectPacket)
}
}
}
var parseSectionV2Tests = []struct {
about string
data string
expectData string
expectPackets []packetV2
expectError string
}{{
about: "no packets",
data: "\x00",
}, {
about: "one packet",
data: "\x02\x03xyz\x00",
expectPackets: []packetV2{{
fieldType: 2,
data: []byte("xyz"),
}},
}, {
about: "two packets",
data: "\x02\x03xyz\x07\x05abcde\x00",
expectPackets: []packetV2{{
fieldType: 2,
data: []byte("xyz"),
}, {
fieldType: 7,
data: []byte("abcde"),
}},
}, {
about: "unterminated section",
data: "\x02\x03xyz\x07\x05abcde",
expectError: "section extends past end of buffer",
}, {
about: "out of order fields",
data: "\x07\x05abcde\x02\x03xyz\x00",
expectError: "fields out of order",
}, {
about: "bad packet",
data: "\x07\x05abcde\xff",
expectError: "varint value extends past end of buffer",
}}
func (*packetV2Suite) TestParseSectionV2(c *gc.C) {
for i, test := range parseSectionV2Tests {
c.Logf("test %d: %v", i, test.about)
data, ps, err := parseSectionV2([]byte(test.data))
if test.expectError != "" {
c.Assert(err, gc.ErrorMatches, test.expectError)
c.Assert(data, gc.IsNil)
c.Assert(ps, gc.IsNil)
} else {
c.Assert(err, gc.IsNil)
c.Assert(ps, jc.DeepEquals, test.expectPackets)
}
}
}

102
vendor/src/gopkg.in/macaroon.v2/trace.go vendored Normal file
View File

@ -0,0 +1,102 @@
package macaroon
import (
"fmt"
)
// Trace holds all toperations involved in verifying a macaroon,
// and the root key used as the initial verification key.
// This can be useful for debugging macaroon implementations.
type Trace struct {
RootKey []byte
Ops []TraceOp
}
// Results returns the output from all operations in the Trace.
// The result from ts.Ops[i] will be in the i'th element of the
// returned slice.
// When a trace has resulted in a failure, the
// last element will be nil.
func (t Trace) Results() [][]byte {
r := make([][]byte, len(t.Ops))
input := t.RootKey
for i, op := range t.Ops {
input = op.Result(input)
r[i] = input
}
return r
}
// TraceOp holds one possible operation when verifying a macaroon.
type TraceOp struct {
Kind TraceOpKind `json:"kind"`
Data1 []byte `json:"data1,omitempty"`
Data2 []byte `json:"data2,omitempty"`
}
// Result returns the result of computing the given
// operation with the given input data.
// If op is TraceFail, it returns nil.
func (op TraceOp) Result(input []byte) []byte {
switch op.Kind {
case TraceMakeKey:
return makeKey(input)[:]
case TraceHash:
if len(op.Data2) == 0 {
return keyedHash(bytesToKey(input), op.Data1)[:]
}
return keyedHash2(bytesToKey(input), op.Data1, op.Data2)[:]
case TraceBind:
return bindForRequest(op.Data1, bytesToKey(input))[:]
case TraceFail:
return nil
default:
panic(fmt.Errorf("unknown trace operation kind %d", op.Kind))
}
}
func bytesToKey(data []byte) *[keyLen]byte {
var key [keyLen]byte
if len(data) != keyLen {
panic(fmt.Errorf("unexpected input key length; got %d want %d", len(data), keyLen))
}
copy(key[:], data)
return &key
}
// TraceOpKind represents the kind of a macaroon verification operation.
type TraceOpKind int
const (
TraceInvalid = TraceOpKind(iota)
// TraceMakeKey represents the operation of calculating a
// fixed length root key from the variable length input key.
TraceMakeKey
// TraceHash represents a keyed hash operation with one
// or two values. If there is only one value, it will be in Data1.
TraceHash
// TraceBind represents the operation of binding a discharge macaroon
// to its primary macaroon. Data1 holds the signature of the primary
// macaroon.
TraceBind
// TraceFail represents a verification failure. If present, this will always
// be the last operation in a trace.
TraceFail
)
var traceOps = []string{
TraceInvalid: "invalid",
TraceMakeKey: "makekey",
TraceHash: "hash",
TraceBind: "bind",
TraceFail: "fail",
}
// String returns a string representation of the operation.
func (k TraceOpKind) String() string {
return traceOps[k]
}