Skip to content

Commit 37a17fe

Browse files
committed
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 <[email protected]>
1 parent e6b1200 commit 37a17fe

File tree

9 files changed

+128
-1
lines changed

9 files changed

+128
-1
lines changed

chacha20poly1305/chacha20poly1305_amd64.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ package chacha20poly1305
99
import (
1010
"encoding/binary"
1111

12+
"golang.org/x/crypto/internal/subtle"
1213
"golang.org/x/sys/cpu"
1314
)
1415

@@ -55,6 +56,9 @@ func (c *chacha20poly1305) seal(dst, nonce, plaintext, additionalData []byte) []
5556
setupState(&state, &c.key, nonce)
5657

5758
ret, out := sliceForAppend(dst, len(plaintext)+16)
59+
if subtle.InexactOverlap(out, plaintext) {
60+
panic("chacha20poly1305: invalid buffer overlap")
61+
}
5862
chacha20Poly1305Seal(out[:], state[:], plaintext, additionalData)
5963
return ret
6064
}
@@ -69,6 +73,9 @@ func (c *chacha20poly1305) open(dst, nonce, ciphertext, additionalData []byte) (
6973

7074
ciphertext = ciphertext[:len(ciphertext)-16]
7175
ret, out := sliceForAppend(dst, len(ciphertext))
76+
if subtle.InexactOverlap(out, ciphertext) {
77+
panic("chacha20poly1305: invalid buffer overlap")
78+
}
7279
if !chacha20Poly1305Open(out, state[:], ciphertext, additionalData) {
7380
for i := range out {
7481
out[i] = 0

chacha20poly1305/chacha20poly1305_generic.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"encoding/binary"
99

1010
"golang.org/x/crypto/internal/chacha20"
11+
"golang.org/x/crypto/internal/subtle"
1112
"golang.org/x/crypto/poly1305"
1213
)
1314

@@ -17,6 +18,9 @@ func roundTo16(n int) int {
1718

1819
func (c *chacha20poly1305) sealGeneric(dst, nonce, plaintext, additionalData []byte) []byte {
1920
ret, out := sliceForAppend(dst, len(plaintext)+poly1305.TagSize)
21+
if subtle.InexactOverlap(out, plaintext) {
22+
panic("chacha20poly1305: invalid buffer overlap")
23+
}
2024

2125
var polyKey [32]byte
2226
s := chacha20.New(c.key, [3]uint32{
@@ -62,6 +66,9 @@ func (c *chacha20poly1305) openGeneric(dst, nonce, ciphertext, additionalData []
6266
binary.LittleEndian.PutUint64(polyInput[len(polyInput)-8:], uint64(len(ciphertext)))
6367

6468
ret, out := sliceForAppend(dst, len(ciphertext))
69+
if subtle.InexactOverlap(out, ciphertext) {
70+
panic("chacha20poly1305: invalid buffer overlap")
71+
}
6572
if !poly1305.Verify(&tag, polyInput, &polyKey) {
6673
for i := range out {
6774
out[i] = 0

internal/chacha20/chacha_generic.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ package chacha20
99
import (
1010
"crypto/cipher"
1111
"encoding/binary"
12+
13+
"golang.org/x/crypto/internal/subtle"
1214
)
1315

1416
// assert that *Cipher implements cipher.Stream
@@ -41,6 +43,13 @@ func New(key [8]uint32, nonce [3]uint32) *Cipher {
4143
// the src buffers was passed in a single run. That is, Cipher
4244
// maintains state and does not reset at each XORKeyStream call.
4345
func (s *Cipher) XORKeyStream(dst, src []byte) {
46+
if len(dst) < len(src) {
47+
panic("chacha20: output smaller than input")
48+
}
49+
if subtle.InexactOverlap(dst[:len(src)], src) {
50+
panic("chacha20: invalid buffer overlap")
51+
}
52+
4453
// xor src with buffered keystream first
4554
if s.len != 0 {
4655
buf := s.buf[len(s.buf)-s.len:]

internal/subtle/aliasing.go

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
// Copyright 2018 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
// Package subtle implements functions that are often useful in cryptographic
6+
// code but require careful thought to use correctly.
7+
package subtle // import "golang.org/x/crypto/internal/subtle"
8+
9+
import "unsafe"
10+
11+
// AnyOverlap reports whether x and y share memory at any (not necessarily
12+
// corresponding) index. The memory beyond the slice length is ignored.
13+
func AnyOverlap(x, y []byte) bool {
14+
return len(x) > 0 && len(y) > 0 &&
15+
uintptr(unsafe.Pointer(&x[0])) <= uintptr(unsafe.Pointer(&y[len(y)-1])) &&
16+
uintptr(unsafe.Pointer(&y[0])) <= uintptr(unsafe.Pointer(&x[len(x)-1]))
17+
}
18+
19+
// InexactOverlap reports whether x and y share memory at any non-corresponding
20+
// index. The memory beyond the slice length is ignored. Note that x and y can
21+
// have different lengths and still not have any inexact overlap.
22+
//
23+
// InexactOverlap can be used to implement the requirements of the crypto/cipher
24+
// AEAD, Block, BlockMode and Stream interfaces.
25+
func InexactOverlap(x, y []byte) bool {
26+
if len(x) == 0 || len(y) == 0 || &x[0] == &y[0] {
27+
return false
28+
}
29+
return AnyOverlap(x, y)
30+
}

internal/subtle/aliasing_test.go

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
// Copyright 2018 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
package subtle_test
6+
7+
import (
8+
"testing"
9+
10+
"golang.org/x/crypto/internal/subtle"
11+
)
12+
13+
var a, b [100]byte
14+
15+
var aliasingTests = []struct {
16+
x, y []byte
17+
anyOverlap, inexactOverlap bool
18+
}{
19+
{a[:], b[:], false, false},
20+
{a[:], b[:0], false, false},
21+
{a[:], b[:50], false, false},
22+
{a[40:50], a[50:60], false, false},
23+
{a[40:50], a[60:70], false, false},
24+
{a[:51], a[50:], true, true},
25+
{a[:], a[:], true, false},
26+
{a[:50], a[:60], true, false},
27+
{a[:], nil, false, false},
28+
{nil, nil, false, false},
29+
{a[:], a[:0], false, false},
30+
}
31+
32+
func testAliasing(t *testing.T, i int, x, y []byte, anyOverlap, inexactOverlap bool) {
33+
any := subtle.AnyOverlap(x, y)
34+
if any != anyOverlap {
35+
t.Errorf("%d: wrong AnyOverlap result, expected %v, got %v", i, anyOverlap, any)
36+
}
37+
inexact := subtle.InexactOverlap(x, y)
38+
if inexact != inexactOverlap {
39+
t.Errorf("%d: wrong InexactOverlap result, expected %v, got %v", i, inexactOverlap, any)
40+
}
41+
}
42+
43+
func TestAliasing(t *testing.T) {
44+
for i, tt := range aliasingTests {
45+
testAliasing(t, i, tt.x, tt.y, tt.anyOverlap, tt.inexactOverlap)
46+
testAliasing(t, i, tt.y, tt.x, tt.anyOverlap, tt.inexactOverlap)
47+
}
48+
}

nacl/secretbox/secretbox.go

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ This package is interoperable with NaCl: https://nacl.cr.yp.to/secretbox.html.
3535
package secretbox // import "golang.org/x/crypto/nacl/secretbox"
3636

3737
import (
38+
"golang.org/x/crypto/internal/subtle"
3839
"golang.org/x/crypto/poly1305"
3940
"golang.org/x/crypto/salsa20/salsa"
4041
)
@@ -87,6 +88,9 @@ func Seal(out, message []byte, nonce *[24]byte, key *[32]byte) []byte {
8788
copy(poly1305Key[:], firstBlock[:])
8889

8990
ret, out := sliceForAppend(out, len(message)+poly1305.TagSize)
91+
if subtle.AnyOverlap(out, message) {
92+
panic("nacl: invalid buffer overlap")
93+
}
9094

9195
// We XOR up to 32 bytes of message with the keystream generated from
9296
// the first block.
@@ -118,7 +122,7 @@ func Seal(out, message []byte, nonce *[24]byte, key *[32]byte) []byte {
118122
// Open authenticates and decrypts a box produced by Seal and appends the
119123
// message to out, which must not overlap box. The output will be Overhead
120124
// bytes smaller than box.
121-
func Open(out []byte, box []byte, nonce *[24]byte, key *[32]byte) ([]byte, bool) {
125+
func Open(out, box []byte, nonce *[24]byte, key *[32]byte) ([]byte, bool) {
122126
if len(box) < Overhead {
123127
return nil, false
124128
}
@@ -143,6 +147,9 @@ func Open(out []byte, box []byte, nonce *[24]byte, key *[32]byte) ([]byte, bool)
143147
}
144148

145149
ret, out := sliceForAppend(out, len(box)-Overhead)
150+
if subtle.AnyOverlap(out, box) {
151+
panic("nacl: invalid buffer overlap")
152+
}
146153

147154
// We XOR up to 32 bytes of box with the keystream generated from
148155
// the first block.

nacl/sign/sign.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import (
2424
"io"
2525

2626
"golang.org/x/crypto/ed25519"
27+
"golang.org/x/crypto/internal/subtle"
2728
)
2829

2930
// Overhead is the number of bytes of overhead when signing a message.
@@ -47,6 +48,9 @@ func GenerateKey(rand io.Reader) (publicKey *[32]byte, privateKey *[64]byte, err
4748
func Sign(out, message []byte, privateKey *[64]byte) []byte {
4849
sig := ed25519.Sign(ed25519.PrivateKey((*privateKey)[:]), message)
4950
ret, out := sliceForAppend(out, Overhead+len(message))
51+
if subtle.AnyOverlap(out, message) {
52+
panic("nacl: invalid buffer overlap")
53+
}
5054
copy(out, sig)
5155
copy(out[Overhead:], message)
5256
return ret
@@ -63,6 +67,9 @@ func Open(out, signedMessage []byte, publicKey *[32]byte) ([]byte, bool) {
6367
return nil, false
6468
}
6569
ret, out := sliceForAppend(out, len(signedMessage)-Overhead)
70+
if subtle.AnyOverlap(out, signedMessage) {
71+
panic("nacl: invalid buffer overlap")
72+
}
6673
copy(out, signedMessage[Overhead:])
6774
return ret, true
6875
}

salsa20/salsa20.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ package salsa20 // import "golang.org/x/crypto/salsa20"
2424
// TODO(agl): implement XORKeyStream12 and XORKeyStream8 - the reduced round variants of Salsa20.
2525

2626
import (
27+
"golang.org/x/crypto/internal/subtle"
2728
"golang.org/x/crypto/salsa20/salsa"
2829
)
2930

@@ -34,6 +35,9 @@ func XORKeyStream(out, in []byte, nonce []byte, key *[32]byte) {
3435
if len(out) < len(in) {
3536
panic("salsa20: output smaller than input")
3637
}
38+
if subtle.InexactOverlap(out[:len(in)], in) {
39+
panic("salsa20: invalid buffer overlap")
40+
}
3741

3842
var subNonce [16]byte
3943

xts/xts.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ import (
2525
"crypto/cipher"
2626
"encoding/binary"
2727
"errors"
28+
29+
"golang.org/x/crypto/internal/subtle"
2830
)
2931

3032
// Cipher contains an expanded key structure. It doesn't contain mutable state
@@ -64,6 +66,9 @@ func (c *Cipher) Encrypt(ciphertext, plaintext []byte, sectorNum uint64) {
6466
if len(plaintext)%blockSize != 0 {
6567
panic("xts: plaintext is not a multiple of the block size")
6668
}
69+
if subtle.InexactOverlap(ciphertext[:len(plaintext)], plaintext) {
70+
panic("xts: invalid buffer overlap")
71+
}
6772

6873
var tweak [blockSize]byte
6974
binary.LittleEndian.PutUint64(tweak[:8], sectorNum)
@@ -95,6 +100,9 @@ func (c *Cipher) Decrypt(plaintext, ciphertext []byte, sectorNum uint64) {
95100
if len(ciphertext)%blockSize != 0 {
96101
panic("xts: ciphertext is not a multiple of the block size")
97102
}
103+
if subtle.InexactOverlap(plaintext[:len(ciphertext)], ciphertext) {
104+
panic("xts: invalid buffer overlap")
105+
}
98106

99107
var tweak [blockSize]byte
100108
binary.LittleEndian.PutUint64(tweak[:8], sectorNum)

0 commit comments

Comments
 (0)