Bump dependencies (#592)
* Bump dependencies * Fix missing dependencies that were not previously fetchedmain
parent
8b0f60a470
commit
2133e6bf59
|
@ -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",
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
||||||
|
|
|
@ -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)
|
||||||
|
}
|
|
@ -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)
|
||||||
|
}
|
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
|
@ -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 nonces—for
|
||||||
|
example, by using nonce 1 for the first message, nonce 2 for the second
|
||||||
|
message, etc. Nonces are long enough that randomly generated nonces have
|
||||||
|
negligible risk of collision.
|
||||||
|
|
||||||
|
Messages should be small because:
|
||||||
|
|
||||||
|
1. The whole message needs to be held in memory to be processed.
|
||||||
|
|
||||||
|
2. Using large messages pressures implementations on small machines to decrypt
|
||||||
|
and process plaintext before authenticating it. This is very dangerous, and
|
||||||
|
this API does not allow it, but a protocol that uses excessive message sizes
|
||||||
|
might present some implementations with no other choice.
|
||||||
|
|
||||||
|
3. Fixed overheads will be sufficiently amortised by messages as small as 8KB.
|
||||||
|
|
||||||
|
4. Performance may be improved by working with messages that fit into data caches.
|
||||||
|
|
||||||
|
Thus large amounts of data should be chunked so that each message is small.
|
||||||
|
(Each message still needs a unique nonce.) If in doubt, 16KB is a reasonable
|
||||||
|
chunk size.
|
||||||
|
|
||||||
|
This package is interoperable with NaCl: https://nacl.cr.yp.to/secretbox.html.
|
||||||
|
*/
|
||||||
|
package secretbox // import "golang.org/x/crypto/nacl/secretbox"
|
||||||
|
|
||||||
|
import (
|
||||||
|
"golang.org/x/crypto/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
|
||||||
|
}
|
|
@ -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)
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
|
@ -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)
|
||||||
|
}
|
|
@ -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
|
|
@ -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)
|
||||||
|
}
|
|
@ -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
|
|
@ -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)
|
||||||
|
}
|
|
@ -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)
|
||||||
|
}
|
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
|
@ -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
|
|
@ -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
|
@ -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)
|
||||||
|
}
|
|
@ -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
|
|
@ -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)
|
||||||
|
}
|
|
@ -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])
|
||||||
|
}
|
|
@ -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]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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)
|
||||||
|
}
|
|
@ -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.
|
|
@ -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)
|
||||||
|
}
|
||||||
|
```
|
|
@ -0,0 +1,4 @@
|
||||||
|
macaroon:
|
||||||
|
|
||||||
|
- verify that all signature calculations to correspond exactly
|
||||||
|
with libmacaroons.
|
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
|
@ -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[:]
|
||||||
|
}
|
||||||
|
}
|
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
|
@ -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))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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]...)
|
||||||
|
}
|
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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]
|
||||||
|
}
|
Loading…
Reference in New Issue