Commit 4a842f25 authored by Michael Munday's avatar Michael Munday

crypto/{aes,cipher,rand}: use binary.{Big,Little}Endian methods

Use the binary.{Big,Little}Endian integer encoding methods rather
than unsafe or local implementations. These methods are tested to
ensure they inline correctly and don't add unnecessary bounds checks,
so it seems better to use them wherever possible.

This introduces a dependency on encoding/binary to crypto/cipher. I
think this is OK because other "L3" packages already import
encoding/binary.

Change-Id: I5cf01800d08554ca364e46cfc1d9445cf3c711a0
Reviewed-on: https://go-review.googlesource.com/115555
Run-TryBot: Michael Munday <mike.munday@ibm.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: default avatarBrad Fitzpatrick <bradfitz@golang.org>
parent 3649fe29
...@@ -36,14 +36,17 @@ ...@@ -36,14 +36,17 @@
package aes package aes
import (
"encoding/binary"
)
// Encrypt one block from src into dst, using the expanded key xk. // Encrypt one block from src into dst, using the expanded key xk.
func encryptBlockGo(xk []uint32, dst, src []byte) { func encryptBlockGo(xk []uint32, dst, src []byte) {
var s0, s1, s2, s3, t0, t1, t2, t3 uint32 _ = src[15] // early bounds check
s0 := binary.BigEndian.Uint32(src[0:4])
s0 = uint32(src[0])<<24 | uint32(src[1])<<16 | uint32(src[2])<<8 | uint32(src[3]) s1 := binary.BigEndian.Uint32(src[4:8])
s1 = uint32(src[4])<<24 | uint32(src[5])<<16 | uint32(src[6])<<8 | uint32(src[7]) s2 := binary.BigEndian.Uint32(src[8:12])
s2 = uint32(src[8])<<24 | uint32(src[9])<<16 | uint32(src[10])<<8 | uint32(src[11]) s3 := binary.BigEndian.Uint32(src[12:16])
s3 = uint32(src[12])<<24 | uint32(src[13])<<16 | uint32(src[14])<<8 | uint32(src[15])
// First round just XORs input with key. // First round just XORs input with key.
s0 ^= xk[0] s0 ^= xk[0]
...@@ -55,6 +58,7 @@ func encryptBlockGo(xk []uint32, dst, src []byte) { ...@@ -55,6 +58,7 @@ func encryptBlockGo(xk []uint32, dst, src []byte) {
// Number of rounds is set by length of expanded key. // Number of rounds is set by length of expanded key.
nr := len(xk)/4 - 2 // - 2: one above, one more below nr := len(xk)/4 - 2 // - 2: one above, one more below
k := 4 k := 4
var t0, t1, t2, t3 uint32
for r := 0; r < nr; r++ { for r := 0; r < nr; r++ {
t0 = xk[k+0] ^ te0[uint8(s0>>24)] ^ te1[uint8(s1>>16)] ^ te2[uint8(s2>>8)] ^ te3[uint8(s3)] t0 = xk[k+0] ^ te0[uint8(s0>>24)] ^ te1[uint8(s1>>16)] ^ te2[uint8(s2>>8)] ^ te3[uint8(s3)]
t1 = xk[k+1] ^ te0[uint8(s1>>24)] ^ te1[uint8(s2>>16)] ^ te2[uint8(s3>>8)] ^ te3[uint8(s0)] t1 = xk[k+1] ^ te0[uint8(s1>>24)] ^ te1[uint8(s2>>16)] ^ te2[uint8(s3>>8)] ^ te3[uint8(s0)]
...@@ -75,20 +79,20 @@ func encryptBlockGo(xk []uint32, dst, src []byte) { ...@@ -75,20 +79,20 @@ func encryptBlockGo(xk []uint32, dst, src []byte) {
s2 ^= xk[k+2] s2 ^= xk[k+2]
s3 ^= xk[k+3] s3 ^= xk[k+3]
dst[0], dst[1], dst[2], dst[3] = byte(s0>>24), byte(s0>>16), byte(s0>>8), byte(s0) _ = dst[15] // early bounds check
dst[4], dst[5], dst[6], dst[7] = byte(s1>>24), byte(s1>>16), byte(s1>>8), byte(s1) binary.BigEndian.PutUint32(dst[0:4], s0)
dst[8], dst[9], dst[10], dst[11] = byte(s2>>24), byte(s2>>16), byte(s2>>8), byte(s2) binary.BigEndian.PutUint32(dst[4:8], s1)
dst[12], dst[13], dst[14], dst[15] = byte(s3>>24), byte(s3>>16), byte(s3>>8), byte(s3) binary.BigEndian.PutUint32(dst[8:12], s2)
binary.BigEndian.PutUint32(dst[12:16], s3)
} }
// Decrypt one block from src into dst, using the expanded key xk. // Decrypt one block from src into dst, using the expanded key xk.
func decryptBlockGo(xk []uint32, dst, src []byte) { func decryptBlockGo(xk []uint32, dst, src []byte) {
var s0, s1, s2, s3, t0, t1, t2, t3 uint32 _ = src[15] // early bounds check
s0 := binary.BigEndian.Uint32(src[0:4])
s0 = uint32(src[0])<<24 | uint32(src[1])<<16 | uint32(src[2])<<8 | uint32(src[3]) s1 := binary.BigEndian.Uint32(src[4:8])
s1 = uint32(src[4])<<24 | uint32(src[5])<<16 | uint32(src[6])<<8 | uint32(src[7]) s2 := binary.BigEndian.Uint32(src[8:12])
s2 = uint32(src[8])<<24 | uint32(src[9])<<16 | uint32(src[10])<<8 | uint32(src[11]) s3 := binary.BigEndian.Uint32(src[12:16])
s3 = uint32(src[12])<<24 | uint32(src[13])<<16 | uint32(src[14])<<8 | uint32(src[15])
// First round just XORs input with key. // First round just XORs input with key.
s0 ^= xk[0] s0 ^= xk[0]
...@@ -100,6 +104,7 @@ func decryptBlockGo(xk []uint32, dst, src []byte) { ...@@ -100,6 +104,7 @@ func decryptBlockGo(xk []uint32, dst, src []byte) {
// Number of rounds is set by length of expanded key. // Number of rounds is set by length of expanded key.
nr := len(xk)/4 - 2 // - 2: one above, one more below nr := len(xk)/4 - 2 // - 2: one above, one more below
k := 4 k := 4
var t0, t1, t2, t3 uint32
for r := 0; r < nr; r++ { for r := 0; r < nr; r++ {
t0 = xk[k+0] ^ td0[uint8(s0>>24)] ^ td1[uint8(s3>>16)] ^ td2[uint8(s2>>8)] ^ td3[uint8(s1)] t0 = xk[k+0] ^ td0[uint8(s0>>24)] ^ td1[uint8(s3>>16)] ^ td2[uint8(s2>>8)] ^ td3[uint8(s1)]
t1 = xk[k+1] ^ td0[uint8(s1>>24)] ^ td1[uint8(s0>>16)] ^ td2[uint8(s3>>8)] ^ td3[uint8(s2)] t1 = xk[k+1] ^ td0[uint8(s1>>24)] ^ td1[uint8(s0>>16)] ^ td2[uint8(s3>>8)] ^ td3[uint8(s2)]
...@@ -120,10 +125,11 @@ func decryptBlockGo(xk []uint32, dst, src []byte) { ...@@ -120,10 +125,11 @@ func decryptBlockGo(xk []uint32, dst, src []byte) {
s2 ^= xk[k+2] s2 ^= xk[k+2]
s3 ^= xk[k+3] s3 ^= xk[k+3]
dst[0], dst[1], dst[2], dst[3] = byte(s0>>24), byte(s0>>16), byte(s0>>8), byte(s0) _ = dst[15] // early bounds check
dst[4], dst[5], dst[6], dst[7] = byte(s1>>24), byte(s1>>16), byte(s1>>8), byte(s1) binary.BigEndian.PutUint32(dst[0:4], s0)
dst[8], dst[9], dst[10], dst[11] = byte(s2>>24), byte(s2>>16), byte(s2>>8), byte(s2) binary.BigEndian.PutUint32(dst[4:8], s1)
dst[12], dst[13], dst[14], dst[15] = byte(s3>>24), byte(s3>>16), byte(s3>>8), byte(s3) binary.BigEndian.PutUint32(dst[8:12], s2)
binary.BigEndian.PutUint32(dst[12:16], s3)
} }
// Apply sbox0 to each byte in w. // Apply sbox0 to each byte in w.
...@@ -144,7 +150,7 @@ func expandKeyGo(key []byte, enc, dec []uint32) { ...@@ -144,7 +150,7 @@ func expandKeyGo(key []byte, enc, dec []uint32) {
var i int var i int
nk := len(key) / 4 nk := len(key) / 4
for i = 0; i < nk; i++ { for i = 0; i < nk; i++ {
enc[i] = uint32(key[4*i])<<24 | uint32(key[4*i+1])<<16 | uint32(key[4*i+2])<<8 | uint32(key[4*i+3]) enc[i] = binary.BigEndian.Uint32(key[4*i:])
} }
for ; i < len(enc); i++ { for ; i < len(enc); i++ {
t := enc[i-1] t := enc[i-1]
......
...@@ -7,7 +7,7 @@ package aes ...@@ -7,7 +7,7 @@ package aes
import ( import (
"crypto/cipher" "crypto/cipher"
"crypto/internal/subtle" "crypto/internal/subtle"
"unsafe" "encoding/binary"
) )
// Assert that aesCipherAsm implements the ctrAble interface. // Assert that aesCipherAsm implements the ctrAble interface.
...@@ -38,8 +38,8 @@ func (c *aesCipherAsm) NewCTR(iv []byte) cipher.Stream { ...@@ -38,8 +38,8 @@ func (c *aesCipherAsm) NewCTR(iv []byte) cipher.Stream {
} }
var ac aesctr var ac aesctr
ac.block = c ac.block = c
ac.ctr[0] = *(*uint64)(unsafe.Pointer((&iv[0]))) // high bits ac.ctr[0] = binary.BigEndian.Uint64(iv[0:]) // high bits
ac.ctr[1] = *(*uint64)(unsafe.Pointer((&iv[8]))) // low bits ac.ctr[1] = binary.BigEndian.Uint64(iv[8:]) // low bits
ac.buffer = ac.storage[:0] ac.buffer = ac.storage[:0]
return &ac return &ac
} }
...@@ -48,10 +48,10 @@ func (c *aesctr) refill() { ...@@ -48,10 +48,10 @@ func (c *aesctr) refill() {
// Fill up the buffer with an incrementing count. // Fill up the buffer with an incrementing count.
c.buffer = c.storage[:streamBufferSize] c.buffer = c.storage[:streamBufferSize]
c0, c1 := c.ctr[0], c.ctr[1] c0, c1 := c.ctr[0], c.ctr[1]
for i := 0; i < streamBufferSize; i += BlockSize { for i := 0; i < streamBufferSize; i += 16 {
b0 := (*uint64)(unsafe.Pointer(&c.buffer[i])) binary.BigEndian.PutUint64(c.buffer[i+0:], c0)
b1 := (*uint64)(unsafe.Pointer(&c.buffer[i+BlockSize/2])) binary.BigEndian.PutUint64(c.buffer[i+8:], c1)
*b0, *b1 = c0, c1
// Increment in big endian: c0 is high, c1 is low. // Increment in big endian: c0 is high, c1 is low.
c1++ c1++
if c1 == 0 { if c1 == 0 {
......
...@@ -8,6 +8,7 @@ import ( ...@@ -8,6 +8,7 @@ import (
"crypto/cipher" "crypto/cipher"
subtleoverlap "crypto/internal/subtle" subtleoverlap "crypto/internal/subtle"
"crypto/subtle" "crypto/subtle"
"encoding/binary"
"errors" "errors"
"internal/cpu" "internal/cpu"
) )
...@@ -22,35 +23,15 @@ type gcmCount [16]byte ...@@ -22,35 +23,15 @@ type gcmCount [16]byte
// inc increments the rightmost 32-bits of the count value by 1. // inc increments the rightmost 32-bits of the count value by 1.
func (x *gcmCount) inc() { func (x *gcmCount) inc() {
// The compiler should optimize this to a 32-bit addition. binary.BigEndian.PutUint32(x[len(x)-4:], binary.BigEndian.Uint32(x[len(x)-4:])+1)
n := uint32(x[15]) | uint32(x[14])<<8 | uint32(x[13])<<16 | uint32(x[12])<<24
n += 1
x[12] = byte(n >> 24)
x[13] = byte(n >> 16)
x[14] = byte(n >> 8)
x[15] = byte(n)
} }
// gcmLengths writes len0 || len1 as big-endian values to a 16-byte array. // gcmLengths writes len0 || len1 as big-endian values to a 16-byte array.
func gcmLengths(len0, len1 uint64) [16]byte { func gcmLengths(len0, len1 uint64) [16]byte {
return [16]byte{ v := [16]byte{}
byte(len0 >> 56), binary.BigEndian.PutUint64(v[0:], len0)
byte(len0 >> 48), binary.BigEndian.PutUint64(v[8:], len1)
byte(len0 >> 40), return v
byte(len0 >> 32),
byte(len0 >> 24),
byte(len0 >> 16),
byte(len0 >> 8),
byte(len0),
byte(len1 >> 56),
byte(len1 >> 48),
byte(len1 >> 40),
byte(len1 >> 32),
byte(len1 >> 24),
byte(len1 >> 16),
byte(len1 >> 8),
byte(len1),
}
} }
// gcmHashKey represents the 16-byte hash key required by the GHASH algorithm. // gcmHashKey represents the 16-byte hash key required by the GHASH algorithm.
......
...@@ -7,6 +7,7 @@ package cipher ...@@ -7,6 +7,7 @@ package cipher
import ( import (
subtleoverlap "crypto/internal/subtle" subtleoverlap "crypto/internal/subtle"
"crypto/subtle" "crypto/subtle"
"encoding/binary"
"errors" "errors"
) )
...@@ -53,8 +54,8 @@ type gcmAble interface { ...@@ -53,8 +54,8 @@ type gcmAble interface {
} }
// gcmFieldElement represents a value in GF(2¹²⁸). In order to reflect the GCM // gcmFieldElement represents a value in GF(2¹²⁸). In order to reflect the GCM
// standard and make getUint64 suitable for marshaling these values, the bits // standard and make binary.BigEndian suitable for marshaling these values, the
// are stored backwards. For example: // bits are stored in big endian order. For example:
// the coefficient of x⁰ can be obtained by v.low >> 63. // the coefficient of x⁰ can be obtained by v.low >> 63.
// the coefficient of x⁶³ can be obtained by v.low & 1. // the coefficient of x⁶³ can be obtained by v.low & 1.
// the coefficient of x⁶⁴ can be obtained by v.high >> 63. // the coefficient of x⁶⁴ can be obtained by v.high >> 63.
...@@ -130,8 +131,8 @@ func newGCMWithNonceAndTagSize(cipher Block, nonceSize, tagSize int) (AEAD, erro ...@@ -130,8 +131,8 @@ func newGCMWithNonceAndTagSize(cipher Block, nonceSize, tagSize int) (AEAD, erro
// would expect, say, 4*key to be in index 4 of the table but due to // would expect, say, 4*key to be in index 4 of the table but due to
// this bit ordering it will actually be in index 0010 (base 2) = 2. // this bit ordering it will actually be in index 0010 (base 2) = 2.
x := gcmFieldElement{ x := gcmFieldElement{
getUint64(key[:8]), binary.BigEndian.Uint64(key[:8]),
getUint64(key[8:]), binary.BigEndian.Uint64(key[8:]),
} }
g.productTable[reverseBits(1)] = x g.productTable[reverseBits(1)] = x
...@@ -316,8 +317,8 @@ func (g *gcm) mul(y *gcmFieldElement) { ...@@ -316,8 +317,8 @@ func (g *gcm) mul(y *gcmFieldElement) {
// Horner's rule. There must be a multiple of gcmBlockSize bytes in blocks. // Horner's rule. There must be a multiple of gcmBlockSize bytes in blocks.
func (g *gcm) updateBlocks(y *gcmFieldElement, blocks []byte) { func (g *gcm) updateBlocks(y *gcmFieldElement, blocks []byte) {
for len(blocks) > 0 { for len(blocks) > 0 {
y.low ^= getUint64(blocks) y.low ^= binary.BigEndian.Uint64(blocks)
y.high ^= getUint64(blocks[8:]) y.high ^= binary.BigEndian.Uint64(blocks[8:])
g.mul(y) g.mul(y)
blocks = blocks[gcmBlockSize:] blocks = blocks[gcmBlockSize:]
} }
...@@ -339,12 +340,8 @@ func (g *gcm) update(y *gcmFieldElement, data []byte) { ...@@ -339,12 +340,8 @@ func (g *gcm) update(y *gcmFieldElement, data []byte) {
// gcmInc32 treats the final four bytes of counterBlock as a big-endian value // gcmInc32 treats the final four bytes of counterBlock as a big-endian value
// and increments it. // and increments it.
func gcmInc32(counterBlock *[16]byte) { func gcmInc32(counterBlock *[16]byte) {
for i := gcmBlockSize - 1; i >= gcmBlockSize-4; i-- { ctr := counterBlock[len(counterBlock)-4:]
counterBlock[i]++ binary.BigEndian.PutUint32(ctr, binary.BigEndian.Uint32(ctr)+1)
if counterBlock[i] != 0 {
break
}
}
} }
// sliceForAppend takes a slice and a requested number of bytes. It returns a // sliceForAppend takes a slice and a requested number of bytes. It returns a
...@@ -400,8 +397,8 @@ func (g *gcm) deriveCounter(counter *[gcmBlockSize]byte, nonce []byte) { ...@@ -400,8 +397,8 @@ func (g *gcm) deriveCounter(counter *[gcmBlockSize]byte, nonce []byte) {
g.update(&y, nonce) g.update(&y, nonce)
y.high ^= uint64(len(nonce)) * 8 y.high ^= uint64(len(nonce)) * 8
g.mul(&y) g.mul(&y)
putUint64(counter[:8], y.low) binary.BigEndian.PutUint64(counter[:8], y.low)
putUint64(counter[8:], y.high) binary.BigEndian.PutUint64(counter[8:], y.high)
} }
} }
...@@ -417,33 +414,8 @@ func (g *gcm) auth(out, ciphertext, additionalData []byte, tagMask *[gcmTagSize] ...@@ -417,33 +414,8 @@ func (g *gcm) auth(out, ciphertext, additionalData []byte, tagMask *[gcmTagSize]
g.mul(&y) g.mul(&y)
putUint64(out, y.low) binary.BigEndian.PutUint64(out, y.low)
putUint64(out[8:], y.high) binary.BigEndian.PutUint64(out[8:], y.high)
xorWords(out, out, tagMask[:]) xorWords(out, out, tagMask[:])
} }
func getUint64(data []byte) uint64 {
_ = data[7] // bounds check hint to compiler; see golang.org/issue/14808
r := uint64(data[0])<<56 |
uint64(data[1])<<48 |
uint64(data[2])<<40 |
uint64(data[3])<<32 |
uint64(data[4])<<24 |
uint64(data[5])<<16 |
uint64(data[6])<<8 |
uint64(data[7])
return r
}
func putUint64(out []byte, v uint64) {
_ = out[7] // bounds check hint to compiler; see golang.org/issue/14808
out[0] = byte(v >> 56)
out[1] = byte(v >> 48)
out[2] = byte(v >> 40)
out[3] = byte(v >> 32)
out[4] = byte(v >> 24)
out[5] = byte(v >> 16)
out[6] = byte(v >> 8)
out[7] = byte(v)
}
...@@ -13,6 +13,7 @@ import ( ...@@ -13,6 +13,7 @@ import (
"bufio" "bufio"
"crypto/aes" "crypto/aes"
"crypto/cipher" "crypto/cipher"
"encoding/binary"
"io" "io"
"os" "os"
"runtime" "runtime"
...@@ -137,14 +138,7 @@ func (r *reader) Read(b []byte) (n int, err error) { ...@@ -137,14 +138,7 @@ func (r *reader) Read(b []byte) (n int, err error) {
// dst = encrypt(t^seed) // dst = encrypt(t^seed)
// seed = encrypt(t^dst) // seed = encrypt(t^dst)
ns := time.Now().UnixNano() ns := time.Now().UnixNano()
r.time[0] = byte(ns >> 56) binary.BigEndian.PutUint64(r.time[:], uint64(ns))
r.time[1] = byte(ns >> 48)
r.time[2] = byte(ns >> 40)
r.time[3] = byte(ns >> 32)
r.time[4] = byte(ns >> 24)
r.time[5] = byte(ns >> 16)
r.time[6] = byte(ns >> 8)
r.time[7] = byte(ns)
r.cipher.Encrypt(r.time[0:], r.time[0:]) r.cipher.Encrypt(r.time[0:], r.time[0:])
for i := 0; i < aes.BlockSize; i++ { for i := 0; i < aes.BlockSize; i++ {
r.dst[i] = r.time[i] ^ r.seed[i] r.dst[i] = r.time[i] ^ r.seed[i]
......
...@@ -100,7 +100,7 @@ var pkgDeps = map[string][]string{ ...@@ -100,7 +100,7 @@ var pkgDeps = map[string][]string{
// and interface definitions, but nothing that makes // and interface definitions, but nothing that makes
// system calls. // system calls.
"crypto": {"L2", "hash"}, // interfaces "crypto": {"L2", "hash"}, // interfaces
"crypto/cipher": {"L2", "crypto/subtle", "crypto/internal/subtle"}, "crypto/cipher": {"L2", "crypto/subtle", "crypto/internal/subtle", "encoding/binary"},
"crypto/internal/subtle": {"unsafe", "reflect"}, // reflect behind a appengine tag "crypto/internal/subtle": {"unsafe", "reflect"}, // reflect behind a appengine tag
"crypto/subtle": {}, "crypto/subtle": {},
"encoding/base32": {"L2"}, "encoding/base32": {"L2"},
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment