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") + } + } + } + } +} |