diff options
Diffstat (limited to 'src/pkg/crypto/cipher')
| -rw-r--r-- | src/pkg/crypto/cipher/benchmark_test.go | 139 | ||||
| -rw-r--r-- | src/pkg/crypto/cipher/cbc.go | 55 | ||||
| -rw-r--r-- | src/pkg/crypto/cipher/cbc_aes_test.go | 46 | ||||
| -rw-r--r-- | src/pkg/crypto/cipher/cfb.go | 60 | ||||
| -rw-r--r-- | src/pkg/crypto/cipher/cfb_test.go | 8 | ||||
| -rw-r--r-- | src/pkg/crypto/cipher/cipher.go | 10 | ||||
| -rw-r--r-- | src/pkg/crypto/cipher/ctr.go | 55 | ||||
| -rw-r--r-- | src/pkg/crypto/cipher/gcm.go | 27 | ||||
| -rw-r--r-- | src/pkg/crypto/cipher/gcm_test.go | 16 | ||||
| -rw-r--r-- | src/pkg/crypto/cipher/ofb.go | 42 | ||||
| -rw-r--r-- | src/pkg/crypto/cipher/xor.go | 84 | ||||
| -rw-r--r-- | src/pkg/crypto/cipher/xor_test.go | 28 | 
12 files changed, 436 insertions, 134 deletions
| diff --git a/src/pkg/crypto/cipher/benchmark_test.go b/src/pkg/crypto/cipher/benchmark_test.go new file mode 100644 index 000000000..027b24851 --- /dev/null +++ b/src/pkg/crypto/cipher/benchmark_test.go @@ -0,0 +1,139 @@ +// Copyright 2013 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 cipher_test + +import ( +	"crypto/aes" +	"crypto/cipher" +	"testing" +) + +func BenchmarkAESGCMSeal1K(b *testing.B) { +	buf := make([]byte, 1024) +	b.SetBytes(int64(len(buf))) + +	var key [16]byte +	var nonce [12]byte +	aes, _ := aes.NewCipher(key[:]) +	aesgcm, _ := cipher.NewGCM(aes) +	var out []byte + +	b.ResetTimer() +	for i := 0; i < b.N; i++ { +		out = aesgcm.Seal(out[:0], nonce[:], buf, nonce[:]) +	} +} + +func BenchmarkAESGCMOpen1K(b *testing.B) { +	buf := make([]byte, 1024) +	b.SetBytes(int64(len(buf))) + +	var key [16]byte +	var nonce [12]byte +	aes, _ := aes.NewCipher(key[:]) +	aesgcm, _ := cipher.NewGCM(aes) +	var out []byte +	out = aesgcm.Seal(out[:0], nonce[:], buf, nonce[:]) + +	b.ResetTimer() +	for i := 0; i < b.N; i++ { +		_, err := aesgcm.Open(buf[:0], nonce[:], out, nonce[:]) +		if err != nil { +			b.Errorf("Open: %v", err) +		} +	} +} + +// If we test exactly 1K blocks, we would generate exact multiples of +// the cipher's block size, and the cipher stream fragments would +// always be wordsize aligned, whereas non-aligned is a more typical +// use-case. +const almost1K = 1024 - 5 + +func BenchmarkAESCFBEncrypt1K(b *testing.B) { +	buf := make([]byte, almost1K) +	b.SetBytes(int64(len(buf))) + +	var key [16]byte +	var iv [16]byte +	aes, _ := aes.NewCipher(key[:]) +	ctr := cipher.NewCFBEncrypter(aes, iv[:]) + +	b.ResetTimer() +	for i := 0; i < b.N; i++ { +		ctr.XORKeyStream(buf, buf) +	} +} + +func BenchmarkAESCFBDecrypt1K(b *testing.B) { +	buf := make([]byte, almost1K) +	b.SetBytes(int64(len(buf))) + +	var key [16]byte +	var iv [16]byte +	aes, _ := aes.NewCipher(key[:]) +	ctr := cipher.NewCFBDecrypter(aes, iv[:]) + +	b.ResetTimer() +	for i := 0; i < b.N; i++ { +		ctr.XORKeyStream(buf, buf) +	} +} + +func BenchmarkAESOFB1K(b *testing.B) { +	buf := make([]byte, almost1K) +	b.SetBytes(int64(len(buf))) + +	var key [16]byte +	var iv [16]byte +	aes, _ := aes.NewCipher(key[:]) +	ctr := cipher.NewOFB(aes, iv[:]) + +	b.ResetTimer() +	for i := 0; i < b.N; i++ { +		ctr.XORKeyStream(buf, buf) +	} +} + +func BenchmarkAESCTR1K(b *testing.B) { +	buf := make([]byte, almost1K) +	b.SetBytes(int64(len(buf))) + +	var key [16]byte +	var iv [16]byte +	aes, _ := aes.NewCipher(key[:]) +	ctr := cipher.NewCTR(aes, iv[:]) + +	b.ResetTimer() +	for i := 0; i < b.N; i++ { +		ctr.XORKeyStream(buf, buf) +	} +} + +func BenchmarkAESCBCEncrypt1K(b *testing.B) { +	buf := make([]byte, 1024) +	b.SetBytes(int64(len(buf))) + +	var key [16]byte +	var iv [16]byte +	aes, _ := aes.NewCipher(key[:]) +	cbc := cipher.NewCBCEncrypter(aes, iv[:]) +	for i := 0; i < b.N; i++ { +		cbc.CryptBlocks(buf, buf) +	} +} + +func BenchmarkAESCBCDecrypt1K(b *testing.B) { +	buf := make([]byte, 1024) +	b.SetBytes(int64(len(buf))) + +	var key [16]byte +	var iv [16]byte +	aes, _ := aes.NewCipher(key[:]) +	cbc := cipher.NewCBCDecrypter(aes, iv[:]) +	for i := 0; i < b.N; i++ { +		cbc.CryptBlocks(buf, buf) +	} +} diff --git a/src/pkg/crypto/cipher/cbc.go b/src/pkg/crypto/cipher/cbc.go index 4189677e3..241e122ee 100644 --- a/src/pkg/crypto/cipher/cbc.go +++ b/src/pkg/crypto/cipher/cbc.go @@ -48,17 +48,22 @@ func (x *cbcEncrypter) CryptBlocks(dst, src []byte) {  	if len(dst) < len(src) {  		panic("crypto/cipher: output smaller than input")  	} + +	iv := x.iv +  	for len(src) > 0 { -		for i := 0; i < x.blockSize; i++ { -			x.iv[i] ^= src[i] -		} -		x.b.Encrypt(x.iv, x.iv) -		for i := 0; i < x.blockSize; i++ { -			dst[i] = x.iv[i] -		} +		// Write the xor to dst, then encrypt in place. +		xorBytes(dst[:x.blockSize], src[:x.blockSize], iv) +		x.b.Encrypt(dst[:x.blockSize], dst[:x.blockSize]) + +		// Move to the next block with this block as the next iv. +		iv = dst[:x.blockSize]  		src = src[x.blockSize:]  		dst = dst[x.blockSize:]  	} + +	// Save the iv for the next CryptBlocks call. +	copy(x.iv, iv)  }  func (x *cbcEncrypter) SetIV(iv []byte) { @@ -89,17 +94,35 @@ func (x *cbcDecrypter) CryptBlocks(dst, src []byte) {  	if len(dst) < len(src) {  		panic("crypto/cipher: output smaller than input")  	} -	for len(src) > 0 { -		x.b.Decrypt(x.tmp, src[:x.blockSize]) -		for i := 0; i < x.blockSize; i++ { -			x.tmp[i] ^= x.iv[i] -			x.iv[i] = src[i] -			dst[i] = x.tmp[i] -		} +	if len(src) == 0 { +		return +	} -		src = src[x.blockSize:] -		dst = dst[x.blockSize:] +	// For each block, we need to xor the decrypted data with the previous block's ciphertext (the iv). +	// To avoid making a copy each time, we loop over the blocks BACKWARDS. +	end := len(src) +	start := end - x.blockSize +	prev := start - x.blockSize + +	// Copy the last block of ciphertext in preparation as the new iv. +	copy(x.tmp, src[start:end]) + +	// Loop over all but the first block. +	for start > 0 { +		x.b.Decrypt(dst[start:end], src[start:end]) +		xorBytes(dst[start:end], dst[start:end], src[prev:start]) + +		end = start +		start = prev +		prev -= x.blockSize  	} + +	// The first block is special because it uses the saved iv. +	x.b.Decrypt(dst[start:end], src[start:end]) +	xorBytes(dst[start:end], dst[start:end], x.iv) + +	// Set the new iv to the first block we copied earlier. +	x.iv, x.tmp = x.tmp, x.iv  }  func (x *cbcDecrypter) SetIV(iv []byte) { diff --git a/src/pkg/crypto/cipher/cbc_aes_test.go b/src/pkg/crypto/cipher/cbc_aes_test.go index cee3a784b..bf9e7ad70 100644 --- a/src/pkg/crypto/cipher/cbc_aes_test.go +++ b/src/pkg/crypto/cipher/cbc_aes_test.go @@ -63,28 +63,42 @@ var cbcAESTests = []struct {  	},  } -func TestCBC_AES(t *testing.T) { -	for _, tt := range cbcAESTests { -		test := tt.name - -		c, err := aes.NewCipher(tt.key) +func TestCBCEncrypterAES(t *testing.T) { +	for _, test := range cbcAESTests { +		c, err := aes.NewCipher(test.key)  		if err != nil { -			t.Errorf("%s: NewCipher(%d bytes) = %s", test, len(tt.key), err) +			t.Errorf("%s: NewCipher(%d bytes) = %s", test.name, len(test.key), err)  			continue  		} -		encrypter := cipher.NewCBCEncrypter(c, tt.iv) -		d := make([]byte, len(tt.in)) -		encrypter.CryptBlocks(d, tt.in) -		if !bytes.Equal(tt.out, d) { -			t.Errorf("%s: CBCEncrypter\nhave %x\nwant %x", test, d, tt.out) +		encrypter := cipher.NewCBCEncrypter(c, test.iv) + +		data := make([]byte, len(test.in)) +		copy(data, test.in) + +		encrypter.CryptBlocks(data, data) +		if !bytes.Equal(test.out, data) { +			t.Errorf("%s: CBCEncrypter\nhave %x\nwant %x", test.name, data, test.out)  		} +	} +} + +func TestCBCDecrypterAES(t *testing.T) { +	for _, test := range cbcAESTests { +		c, err := aes.NewCipher(test.key) +		if err != nil { +			t.Errorf("%s: NewCipher(%d bytes) = %s", test.name, len(test.key), err) +			continue +		} + +		decrypter := cipher.NewCBCDecrypter(c, test.iv) + +		data := make([]byte, len(test.out)) +		copy(data, test.out) -		decrypter := cipher.NewCBCDecrypter(c, tt.iv) -		p := make([]byte, len(d)) -		decrypter.CryptBlocks(p, d) -		if !bytes.Equal(tt.in, p) { -			t.Errorf("%s: CBCDecrypter\nhave %x\nwant %x", test, d, tt.in) +		decrypter.CryptBlocks(data, data) +		if !bytes.Equal(test.in, data) { +			t.Errorf("%s: CBCDecrypter\nhave %x\nwant %x", test.name, data, test.in)  		}  	}  } diff --git a/src/pkg/crypto/cipher/cfb.go b/src/pkg/crypto/cipher/cfb.go index 99006b546..9b4eebf5b 100644 --- a/src/pkg/crypto/cipher/cfb.go +++ b/src/pkg/crypto/cipher/cfb.go @@ -8,18 +8,41 @@ package cipher  type cfb struct {  	b       Block +	next    []byte  	out     []byte  	outUsed int +  	decrypt bool  } +func (x *cfb) XORKeyStream(dst, src []byte) { +	for len(src) > 0 { +		if x.outUsed == len(x.out) { +			x.b.Encrypt(x.out, x.next) +			x.outUsed = 0 +		} + +		if x.decrypt { +			// We can precompute a larger segment of the +			// keystream on decryption. This will allow +			// larger batches for xor, and we should be +			// able to match CTR/OFB performance. +			copy(x.next[x.outUsed:], src) +		} +		n := xorBytes(dst, src, x.out[x.outUsed:]) +		if !x.decrypt { +			copy(x.next[x.outUsed:], dst) +		} +		dst = dst[n:] +		src = src[n:] +		x.outUsed += n +	} +} +  // NewCFBEncrypter returns a Stream which encrypts with cipher feedback mode,  // using the given Block. The iv must be the same length as the Block's block  // size.  func NewCFBEncrypter(block Block, iv []byte) Stream { -	if len(iv) != block.BlockSize() { -		panic("cipher.NewCBFEncrypter: IV length must equal block size") -	}  	return newCFB(block, iv, false)  } @@ -27,44 +50,23 @@ func NewCFBEncrypter(block Block, iv []byte) Stream {  // using the given Block. The iv must be the same length as the Block's block  // size.  func NewCFBDecrypter(block Block, iv []byte) Stream { -	if len(iv) != block.BlockSize() { -		panic("cipher.NewCBFEncrypter: IV length must equal block size") -	}  	return newCFB(block, iv, true)  }  func newCFB(block Block, iv []byte, decrypt bool) Stream {  	blockSize := block.BlockSize()  	if len(iv) != blockSize { -		return nil +		// stack trace will indicate whether it was de or encryption +		panic("cipher.newCFB: IV length must equal block size")  	} -  	x := &cfb{  		b:       block,  		out:     make([]byte, blockSize), -		outUsed: 0, +		next:    make([]byte, blockSize), +		outUsed: blockSize,  		decrypt: decrypt,  	} -	block.Encrypt(x.out, iv) +	copy(x.next, iv)  	return x  } - -func (x *cfb) XORKeyStream(dst, src []byte) { -	for i := 0; i < len(src); i++ { -		if x.outUsed == len(x.out) { -			x.b.Encrypt(x.out, x.out) -			x.outUsed = 0 -		} - -		if x.decrypt { -			t := src[i] -			dst[i] = src[i] ^ x.out[x.outUsed] -			x.out[x.outUsed] = t -		} else { -			x.out[x.outUsed] ^= src[i] -			dst[i] = x.out[x.outUsed] -		} -		x.outUsed++ -	} -} diff --git a/src/pkg/crypto/cipher/cfb_test.go b/src/pkg/crypto/cipher/cfb_test.go index f704b337e..ec708ab2b 100644 --- a/src/pkg/crypto/cipher/cfb_test.go +++ b/src/pkg/crypto/cipher/cfb_test.go @@ -19,16 +19,18 @@ func TestCFB(t *testing.T) {  		return  	} -	plaintext := []byte("this is the plaintext") +	plaintext := []byte("this is the plaintext. this is the plaintext.")  	iv := make([]byte, block.BlockSize())  	rand.Reader.Read(iv)  	cfb := cipher.NewCFBEncrypter(block, iv)  	ciphertext := make([]byte, len(plaintext)) -	cfb.XORKeyStream(ciphertext, plaintext) +	copy(ciphertext, plaintext) +	cfb.XORKeyStream(ciphertext, ciphertext)  	cfbdec := cipher.NewCFBDecrypter(block, iv)  	plaintextCopy := make([]byte, len(plaintext)) -	cfbdec.XORKeyStream(plaintextCopy, ciphertext) +	copy(plaintextCopy, ciphertext) +	cfbdec.XORKeyStream(plaintextCopy, plaintextCopy)  	if !bytes.Equal(plaintextCopy, plaintext) {  		t.Errorf("got: %x, want: %x", plaintextCopy, plaintext) diff --git a/src/pkg/crypto/cipher/cipher.go b/src/pkg/crypto/cipher/cipher.go index 1ffaa8c2c..67afdb1e0 100644 --- a/src/pkg/crypto/cipher/cipher.go +++ b/src/pkg/crypto/cipher/cipher.go @@ -46,16 +46,6 @@ type BlockMode interface {  // Utility routines -func shift1(dst, src []byte) byte { -	var b byte -	for i := len(src) - 1; i >= 0; i-- { -		bb := src[i] >> 7 -		dst[i] = src[i]<<1 | b -		b = bb -	} -	return b -} -  func dup(p []byte) []byte {  	q := make([]byte, len(p))  	copy(q, p) diff --git a/src/pkg/crypto/cipher/ctr.go b/src/pkg/crypto/cipher/ctr.go index d9ee9d827..70ac40f6a 100644 --- a/src/pkg/crypto/cipher/ctr.go +++ b/src/pkg/crypto/cipher/ctr.go @@ -19,37 +19,58 @@ type ctr struct {  	outUsed int  } +const streamBufferSize = 512 +  // NewCTR returns a Stream which encrypts/decrypts using the given Block in  // counter mode. The length of iv must be the same as the Block's block size.  func NewCTR(block Block, iv []byte) Stream {  	if len(iv) != block.BlockSize() {  		panic("cipher.NewCTR: IV length must equal block size")  	} - +	bufSize := streamBufferSize +	if bufSize < block.BlockSize() { +		bufSize = block.BlockSize() +	}  	return &ctr{  		b:       block,  		ctr:     dup(iv), -		out:     make([]byte, len(iv)), -		outUsed: len(iv), +		out:     make([]byte, 0, bufSize), +		outUsed: 0,  	}  } -func (x *ctr) XORKeyStream(dst, src []byte) { -	for i := 0; i < len(src); i++ { -		if x.outUsed == len(x.ctr) { -			x.b.Encrypt(x.out, x.ctr) -			x.outUsed = 0 - -			// Increment counter -			for i := len(x.ctr) - 1; i >= 0; i-- { -				x.ctr[i]++ -				if x.ctr[i] != 0 { -					break -				} +func (x *ctr) refill() { +	remain := len(x.out) - x.outUsed +	if remain > x.outUsed { +		return +	} +	copy(x.out, x.out[x.outUsed:]) +	x.out = x.out[:cap(x.out)] +	bs := x.b.BlockSize() +	for remain < len(x.out)-bs { +		x.b.Encrypt(x.out[remain:], x.ctr) +		remain += bs + +		// Increment counter +		for i := len(x.ctr) - 1; i >= 0; i-- { +			x.ctr[i]++ +			if x.ctr[i] != 0 { +				break  			}  		} +	} +	x.out = x.out[:remain] +	x.outUsed = 0 +} -		dst[i] = src[i] ^ x.out[x.outUsed] -		x.outUsed++ +func (x *ctr) XORKeyStream(dst, src []byte) { +	for len(src) > 0 { +		if x.outUsed >= len(x.out)-x.b.BlockSize() { +			x.refill() +		} +		n := xorBytes(dst, src, x.out[x.outUsed:]) +		dst = dst[n:] +		src = src[n:] +		x.outUsed += n  	}  } diff --git a/src/pkg/crypto/cipher/gcm.go b/src/pkg/crypto/cipher/gcm.go index 2bcb46985..bdafd85fc 100644 --- a/src/pkg/crypto/cipher/gcm.go +++ b/src/pkg/crypto/cipher/gcm.go @@ -30,9 +30,9 @@ type AEAD interface {  	// Open decrypts and authenticates ciphertext, authenticates the  	// additional data and, if successful, appends the resulting plaintext -	// to dst, returning the updated slice and true. On error, nil and -	// false is returned. The nonce must be NonceSize() bytes long and both -	// it and the additional data must match the value passed to Seal. +	// to dst, returning the updated slice. The nonce must be NonceSize() +	// bytes long and both it and the additional data must match the +	// value passed to Seal.  	//  	// The ciphertext and dst may alias exactly or not at all.  	Open(dst, nonce, ciphertext, data []byte) ([]byte, error) @@ -258,11 +258,11 @@ func (g *gcm) update(y *gcmFieldElement, data []byte) {  // gcmInc32 treats the final four bytes of counterBlock as a big-endian value  // and increments it.  func gcmInc32(counterBlock *[16]byte) { -	c := 1  	for i := gcmBlockSize - 1; i >= gcmBlockSize-4; i-- { -		c += int(counterBlock[i]) -		counterBlock[i] = byte(c) -		c >>= 8 +		counterBlock[i]++ +		if counterBlock[i] != 0 { +			break +		}  	}  } @@ -289,9 +289,7 @@ func (g *gcm) counterCrypt(out, in []byte, counter *[gcmBlockSize]byte) {  		g.cipher.Encrypt(mask[:], counter[:])  		gcmInc32(counter) -		for i := range mask { -			out[i] = in[i] ^ mask[i] -		} +		xorWords(out, in, mask[:])  		out = out[gcmBlockSize:]  		in = in[gcmBlockSize:]  	} @@ -299,10 +297,7 @@ func (g *gcm) counterCrypt(out, in []byte, counter *[gcmBlockSize]byte) {  	if len(in) > 0 {  		g.cipher.Encrypt(mask[:], counter[:])  		gcmInc32(counter) - -		for i := range in { -			out[i] = in[i] ^ mask[i] -		} +		xorBytes(out, in, mask[:])  	}  } @@ -321,9 +316,7 @@ func (g *gcm) auth(out, ciphertext, additionalData []byte, tagMask *[gcmTagSize]  	putUint64(out, y.low)  	putUint64(out[8:], y.high) -	for i := range tagMask { -		out[i] ^= tagMask[i] -	} +	xorWords(out, out, tagMask[:])  }  func getUint64(data []byte) uint64 { diff --git a/src/pkg/crypto/cipher/gcm_test.go b/src/pkg/crypto/cipher/gcm_test.go index 02d421590..0c502ce40 100644 --- a/src/pkg/crypto/cipher/gcm_test.go +++ b/src/pkg/crypto/cipher/gcm_test.go @@ -157,19 +157,3 @@ func TestAESGCM(t *testing.T) {  		ct[0] ^= 0x80  	}  } - -func BenchmarkAESGCM(b *testing.B) { -	buf := make([]byte, 1024) -	b.SetBytes(int64(len(buf))) - -	var key [16]byte -	var nonce [12]byte -	aes, _ := aes.NewCipher(key[:]) -	aesgcm, _ := cipher.NewGCM(aes) -	var out []byte - -	b.ResetTimer() -	for i := 0; i < b.N; i++ { -		out = aesgcm.Seal(out[:0], nonce[:], buf, nonce[:]) -	} -} diff --git a/src/pkg/crypto/cipher/ofb.go b/src/pkg/crypto/cipher/ofb.go index 85e5f02b0..e86ebcb23 100644 --- a/src/pkg/crypto/cipher/ofb.go +++ b/src/pkg/crypto/cipher/ofb.go @@ -8,6 +8,7 @@ package cipher  type ofb struct {  	b       Block +	cipher  []byte  	out     []byte  	outUsed int  } @@ -20,25 +21,46 @@ func NewOFB(b Block, iv []byte) Stream {  	if len(iv) != blockSize {  		return nil  	} - +	bufSize := streamBufferSize +	if bufSize < blockSize { +		bufSize = blockSize +	}  	x := &ofb{  		b:       b, -		out:     make([]byte, blockSize), +		cipher:  make([]byte, blockSize), +		out:     make([]byte, 0, bufSize),  		outUsed: 0,  	} -	b.Encrypt(x.out, iv) +	copy(x.cipher, iv)  	return x  } +func (x *ofb) refill() { +	bs := x.b.BlockSize() +	remain := len(x.out) - x.outUsed +	if remain > x.outUsed { +		return +	} +	copy(x.out, x.out[x.outUsed:]) +	x.out = x.out[:cap(x.out)] +	for remain < len(x.out)-bs { +		x.b.Encrypt(x.cipher, x.cipher) +		copy(x.out[remain:], x.cipher) +		remain += bs +	} +	x.out = x.out[:remain] +	x.outUsed = 0 +} +  func (x *ofb) XORKeyStream(dst, src []byte) { -	for i, s := range src { -		if x.outUsed == len(x.out) { -			x.b.Encrypt(x.out, x.out) -			x.outUsed = 0 +	for len(src) > 0 { +		if x.outUsed >= len(x.out)-x.b.BlockSize() { +			x.refill()  		} - -		dst[i] = s ^ x.out[x.outUsed] -		x.outUsed++ +		n := xorBytes(dst, src, x.out[x.outUsed:]) +		dst = dst[n:] +		src = src[n:] +		x.outUsed += n  	}  } diff --git a/src/pkg/crypto/cipher/xor.go b/src/pkg/crypto/cipher/xor.go new file mode 100644 index 000000000..f88dc8914 --- /dev/null +++ b/src/pkg/crypto/cipher/xor.go @@ -0,0 +1,84 @@ +// Copyright 2013 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 cipher + +import ( +	"runtime" +	"unsafe" +) + +const wordSize = int(unsafe.Sizeof(uintptr(0))) +const supportsUnaligned = runtime.GOARCH == "386" || runtime.GOARCH == "amd64" + +// fastXORBytes xors in bulk. It only works on architectures that +// support unaligned read/writes. +func fastXORBytes(dst, a, b []byte) int { +	n := len(a) +	if len(b) < n { +		n = len(b) +	} + +	w := n / wordSize +	if w > 0 { +		dw := *(*[]uintptr)(unsafe.Pointer(&dst)) +		aw := *(*[]uintptr)(unsafe.Pointer(&a)) +		bw := *(*[]uintptr)(unsafe.Pointer(&b)) +		for i := 0; i < w; i++ { +			dw[i] = aw[i] ^ bw[i] +		} +	} + +	for i := (n - n%wordSize); i < n; i++ { +		dst[i] = a[i] ^ b[i] +	} + +	return n +} + +func safeXORBytes(dst, a, b []byte) int { +	n := len(a) +	if len(b) < n { +		n = len(b) +	} +	for i := 0; i < n; i++ { +		dst[i] = a[i] ^ b[i] +	} +	return n +} + +// xorBytes xors the bytes in a and b. The destination is assumed to have enough +// space. Returns the number of bytes xor'd. +func xorBytes(dst, a, b []byte) int { +	if supportsUnaligned { +		return fastXORBytes(dst, a, b) +	} else { +		// TODO(hanwen): if (dst, a, b) have common alignment +		// we could still try fastXORBytes. It is not clear +		// how often this happens, and it's only worth it if +		// the block encryption itself is hardware +		// accelerated. +		return safeXORBytes(dst, a, b) +	} +} + +// fastXORWords XORs multiples of 4 or 8 bytes (depending on architecture.) +// The arguments are assumed to be of equal length. +func fastXORWords(dst, a, b []byte) { +	dw := *(*[]uintptr)(unsafe.Pointer(&dst)) +	aw := *(*[]uintptr)(unsafe.Pointer(&a)) +	bw := *(*[]uintptr)(unsafe.Pointer(&b)) +	n := len(b) / wordSize +	for i := 0; i < n; i++ { +		dw[i] = aw[i] ^ bw[i] +	} +} + +func xorWords(dst, a, b []byte) { +	if supportsUnaligned { +		fastXORWords(dst, a, b) +	} else { +		safeXORBytes(dst, a, b) +	} +} diff --git a/src/pkg/crypto/cipher/xor_test.go b/src/pkg/crypto/cipher/xor_test.go new file mode 100644 index 000000000..cc1c9d72d --- /dev/null +++ b/src/pkg/crypto/cipher/xor_test.go @@ -0,0 +1,28 @@ +// Copyright 2013 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 cipher + +import ( +	"bytes" +	"testing" +) + +func TestXOR(t *testing.T) { +	for alignP := 0; alignP < 2; alignP++ { +		for alignQ := 0; alignQ < 2; alignQ++ { +			for alignD := 0; alignD < 2; alignD++ { +				p := make([]byte, 1024)[alignP:] +				q := make([]byte, 1024)[alignQ:] +				d1 := make([]byte, 1024+alignD)[alignD:] +				d2 := make([]byte, 1024+alignD)[alignD:] +				xorBytes(d1, p, q) +				safeXORBytes(d2, p, q) +				if bytes.Compare(d1, d2) != 0 { +					t.Error("not equal") +				} +			} +		} +	} +} | 
