internal/subtle: add Any/InexactOverlap (new package) and apply them across packages
AnyOverlap and InexactOverlap implement checks for the aliasing
requirements defined by the crypto/cipher interfaces. Apply them to all
implementations as the actual requirement could be architecture-dependent
and user code should not rely on undefined behavior.
Updates golang/go#21624
Change-Id: I465de02fb3fec4e0c6f1fdee1ef6ae7ed5abff10
Reviewed-on: https://go-review.googlesource.com/112236
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
diff --git a/chacha20poly1305/chacha20poly1305_amd64.go b/chacha20poly1305/chacha20poly1305_amd64.go
index 07d18a3..ec13d13 100644
--- a/chacha20poly1305/chacha20poly1305_amd64.go
+++ b/chacha20poly1305/chacha20poly1305_amd64.go
@@ -9,6 +9,7 @@
import (
"encoding/binary"
+ "golang.org/x/crypto/internal/subtle"
"golang.org/x/sys/cpu"
)
@@ -55,6 +56,9 @@
setupState(&state, &c.key, nonce)
ret, out := sliceForAppend(dst, len(plaintext)+16)
+ if subtle.InexactOverlap(out, plaintext) {
+ panic("chacha20poly1305: invalid buffer overlap")
+ }
chacha20Poly1305Seal(out[:], state[:], plaintext, additionalData)
return ret
}
@@ -69,6 +73,9 @@
ciphertext = ciphertext[:len(ciphertext)-16]
ret, out := sliceForAppend(dst, len(ciphertext))
+ if subtle.InexactOverlap(out, ciphertext) {
+ panic("chacha20poly1305: invalid buffer overlap")
+ }
if !chacha20Poly1305Open(out, state[:], ciphertext, additionalData) {
for i := range out {
out[i] = 0
diff --git a/chacha20poly1305/chacha20poly1305_generic.go b/chacha20poly1305/chacha20poly1305_generic.go
index 8d28ce2..c279712 100644
--- a/chacha20poly1305/chacha20poly1305_generic.go
+++ b/chacha20poly1305/chacha20poly1305_generic.go
@@ -8,6 +8,7 @@
"encoding/binary"
"golang.org/x/crypto/internal/chacha20"
+ "golang.org/x/crypto/internal/subtle"
"golang.org/x/crypto/poly1305"
)
@@ -17,6 +18,9 @@
func (c *chacha20poly1305) sealGeneric(dst, nonce, plaintext, additionalData []byte) []byte {
ret, out := sliceForAppend(dst, len(plaintext)+poly1305.TagSize)
+ if subtle.InexactOverlap(out, plaintext) {
+ panic("chacha20poly1305: invalid buffer overlap")
+ }
var polyKey [32]byte
s := chacha20.New(c.key, [3]uint32{
@@ -62,6 +66,9 @@
binary.LittleEndian.PutUint64(polyInput[len(polyInput)-8:], uint64(len(ciphertext)))
ret, out := sliceForAppend(dst, len(ciphertext))
+ if subtle.InexactOverlap(out, ciphertext) {
+ panic("chacha20poly1305: invalid buffer overlap")
+ }
if !poly1305.Verify(&tag, polyInput, &polyKey) {
for i := range out {
out[i] = 0
diff --git a/internal/chacha20/chacha_generic.go b/internal/chacha20/chacha_generic.go
index 7ed1cd9..523751f 100644
--- a/internal/chacha20/chacha_generic.go
+++ b/internal/chacha20/chacha_generic.go
@@ -9,6 +9,8 @@
import (
"crypto/cipher"
"encoding/binary"
+
+ "golang.org/x/crypto/internal/subtle"
)
// assert that *Cipher implements cipher.Stream
@@ -41,6 +43,13 @@
// the src buffers was passed in a single run. That is, Cipher
// maintains state and does not reset at each XORKeyStream call.
func (s *Cipher) XORKeyStream(dst, src []byte) {
+ if len(dst) < len(src) {
+ panic("chacha20: output smaller than input")
+ }
+ if subtle.InexactOverlap(dst[:len(src)], src) {
+ panic("chacha20: invalid buffer overlap")
+ }
+
// xor src with buffered keystream first
if s.len != 0 {
buf := s.buf[len(s.buf)-s.len:]
diff --git a/internal/subtle/aliasing.go b/internal/subtle/aliasing.go
new file mode 100644
index 0000000..61b67bf
--- /dev/null
+++ b/internal/subtle/aliasing.go
@@ -0,0 +1,30 @@
+// 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 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)
+}
diff --git a/internal/subtle/aliasing_test.go b/internal/subtle/aliasing_test.go
new file mode 100644
index 0000000..dfa7368
--- /dev/null
+++ b/internal/subtle/aliasing_test.go
@@ -0,0 +1,48 @@
+// 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},
+}
+
+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)
+ }
+}
diff --git a/nacl/secretbox/secretbox.go b/nacl/secretbox/secretbox.go
index 53ee83c..a98d1bd 100644
--- a/nacl/secretbox/secretbox.go
+++ b/nacl/secretbox/secretbox.go
@@ -35,6 +35,7 @@
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"
)
@@ -87,6 +88,9 @@
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.
@@ -118,7 +122,7 @@
// Open authenticates and decrypts a box produced by Seal and appends the
// message to out, which must not overlap box. The output will be Overhead
// bytes smaller than box.
-func Open(out []byte, box []byte, nonce *[24]byte, key *[32]byte) ([]byte, bool) {
+func Open(out, box []byte, nonce *[24]byte, key *[32]byte) ([]byte, bool) {
if len(box) < Overhead {
return nil, false
}
@@ -143,6 +147,9 @@
}
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.
diff --git a/nacl/sign/sign.go b/nacl/sign/sign.go
index a9ac0a7..d076270 100644
--- a/nacl/sign/sign.go
+++ b/nacl/sign/sign.go
@@ -24,6 +24,7 @@
"io"
"golang.org/x/crypto/ed25519"
+ "golang.org/x/crypto/internal/subtle"
)
// Overhead is the number of bytes of overhead when signing a message.
@@ -47,6 +48,9 @@
func Sign(out, message []byte, privateKey *[64]byte) []byte {
sig := ed25519.Sign(ed25519.PrivateKey((*privateKey)[:]), message)
ret, out := sliceForAppend(out, Overhead+len(message))
+ if subtle.AnyOverlap(out, message) {
+ panic("nacl: invalid buffer overlap")
+ }
copy(out, sig)
copy(out[Overhead:], message)
return ret
@@ -63,6 +67,9 @@
return nil, false
}
ret, out := sliceForAppend(out, len(signedMessage)-Overhead)
+ if subtle.AnyOverlap(out, signedMessage) {
+ panic("nacl: invalid buffer overlap")
+ }
copy(out, signedMessage[Overhead:])
return ret, true
}
diff --git a/salsa20/salsa20.go b/salsa20/salsa20.go
index 3ca6748..6f9bb10 100644
--- a/salsa20/salsa20.go
+++ b/salsa20/salsa20.go
@@ -24,6 +24,7 @@
// TODO(agl): implement XORKeyStream12 and XORKeyStream8 - the reduced round variants of Salsa20.
import (
+ "golang.org/x/crypto/internal/subtle"
"golang.org/x/crypto/salsa20/salsa"
)
@@ -34,6 +35,9 @@
if len(out) < len(in) {
panic("salsa20: output smaller than input")
}
+ if subtle.InexactOverlap(out[:len(in)], in) {
+ panic("salsa20: invalid buffer overlap")
+ }
var subNonce [16]byte
diff --git a/xts/xts.go b/xts/xts.go
index 92cbce9..9654e1f 100644
--- a/xts/xts.go
+++ b/xts/xts.go
@@ -25,6 +25,8 @@
"crypto/cipher"
"encoding/binary"
"errors"
+
+ "golang.org/x/crypto/internal/subtle"
)
// Cipher contains an expanded key structure. It doesn't contain mutable state
@@ -64,6 +66,9 @@
if len(plaintext)%blockSize != 0 {
panic("xts: plaintext is not a multiple of the block size")
}
+ if subtle.InexactOverlap(ciphertext[:len(plaintext)], plaintext) {
+ panic("xts: invalid buffer overlap")
+ }
var tweak [blockSize]byte
binary.LittleEndian.PutUint64(tweak[:8], sectorNum)
@@ -95,6 +100,9 @@
if len(ciphertext)%blockSize != 0 {
panic("xts: ciphertext is not a multiple of the block size")
}
+ if subtle.InexactOverlap(plaintext[:len(ciphertext)], ciphertext) {
+ panic("xts: invalid buffer overlap")
+ }
var tweak [blockSize]byte
binary.LittleEndian.PutUint64(tweak[:8], sectorNum)