]> Cypherpunks repositories - gostls13.git/commitdiff
crypto/cipher: add package
authorAdam Langley <agl@golang.org>
Fri, 19 Nov 2010 19:12:07 +0000 (14:12 -0500)
committerAdam Langley <agl@golang.org>
Fri, 19 Nov 2010 19:12:07 +0000 (14:12 -0500)
cipher is intended to replace crypto/block over time. This
change only adds basic parts: CBC and CTR mode and doesn't add
the package to the top-level Makefile.

R=r, rsc
CC=golang-dev
https://golang.org/cl/3069041

src/pkg/crypto/cipher/Makefile [new file with mode: 0644]
src/pkg/crypto/cipher/cbc.go [new file with mode: 0644]
src/pkg/crypto/cipher/cbc_aes_test.go [new file with mode: 0644]
src/pkg/crypto/cipher/cipher.go [new file with mode: 0644]
src/pkg/crypto/cipher/common_test.go [new file with mode: 0644]
src/pkg/crypto/cipher/ctr.go [new file with mode: 0644]
src/pkg/crypto/cipher/ctr_aes_test.go [new file with mode: 0644]
src/pkg/crypto/cipher/io.go [new file with mode: 0644]

diff --git a/src/pkg/crypto/cipher/Makefile b/src/pkg/crypto/cipher/Makefile
new file mode 100644 (file)
index 0000000..6ebfe79
--- /dev/null
@@ -0,0 +1,14 @@
+# Copyright 2010 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.
+
+include ../../../Make.inc
+
+TARG=crypto/cipher
+GOFILES=\
+       cbc.go\
+       cipher.go\
+       ctr.go\
+       io.go
+
+include ../../../Make.pkg
diff --git a/src/pkg/crypto/cipher/cbc.go b/src/pkg/crypto/cipher/cbc.go
new file mode 100644 (file)
index 0000000..3efc863
--- /dev/null
@@ -0,0 +1,78 @@
+// Copyright 2009 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.
+
+// Cipher block chaining (CBC) mode.
+
+// CBC provides confidentiality by xoring (chaining) each plaintext block
+// with the previous ciphertext block before applying the block cipher.
+
+// See NIST SP 800-38A, pp 10-11
+
+package cipher
+
+type cbc struct {
+       b         Block
+       blockSize int
+       iv        []byte
+       tmp       []byte
+}
+
+func newCBC(b Block, iv []byte) *cbc {
+       return &cbc{
+               b:         b,
+               blockSize: b.BlockSize(),
+               iv:        dup(iv),
+               tmp:       make([]byte, b.BlockSize()),
+       }
+}
+
+type cbcEncrypter cbc
+
+// NewCBCEncrypter returns a BlockMode which encrypts in cipher block chaining
+// mode, using the given Block. The length of iv must be the same as the
+// Block's block size.
+func NewCBCEncrypter(b Block, iv []byte) BlockMode {
+       return (*cbcEncrypter)(newCBC(b, iv))
+}
+
+func (x *cbcEncrypter) BlockSize() int { return x.blockSize }
+
+func (x *cbcEncrypter) CryptBlocks(dst, src []byte) {
+       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]
+               }
+               src = src[x.blockSize:]
+               dst = dst[x.blockSize:]
+       }
+}
+
+type cbcDecrypter cbc
+
+// NewCBCDecrypter returns a BlockMode which decrypts in cipher block chaining
+// mode, using the given Block. The length of iv must be the same as the
+// Block's block size as must match the iv used to encrypt the data.
+func NewCBCDecrypter(b Block, iv []byte) *cbcDecrypter {
+       return (*cbcDecrypter)(newCBC(b, iv))
+}
+
+func (x *cbcDecrypter) BlockSize() int { return x.blockSize }
+
+func (x *cbcDecrypter) CryptBlocks(dst, src []byte) {
+       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]
+               }
+
+               src = src[x.blockSize:]
+               dst = dst[x.blockSize:]
+       }
+}
diff --git a/src/pkg/crypto/cipher/cbc_aes_test.go b/src/pkg/crypto/cipher/cbc_aes_test.go
new file mode 100644 (file)
index 0000000..944ca1b
--- /dev/null
@@ -0,0 +1,89 @@
+// Copyright 2009 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.
+
+// CBC AES test vectors.
+
+// See U.S. National Institute of Standards and Technology (NIST)
+// Special Publication 800-38A, ``Recommendation for Block Cipher
+// Modes of Operation,'' 2001 Edition, pp. 24-29.
+
+package cipher
+
+import (
+       "bytes"
+       "crypto/aes"
+       "testing"
+)
+
+var cbcAESTests = []struct {
+       name string
+       key  []byte
+       iv   []byte
+       in   []byte
+       out  []byte
+}{
+       // NIST SP 800-38A pp 27-29
+       {
+               "CBC-AES128",
+               commonKey128,
+               commonIV,
+               commonInput,
+               []byte{
+                       0x76, 0x49, 0xab, 0xac, 0x81, 0x19, 0xb2, 0x46, 0xce, 0xe9, 0x8e, 0x9b, 0x12, 0xe9, 0x19, 0x7d,
+                       0x50, 0x86, 0xcb, 0x9b, 0x50, 0x72, 0x19, 0xee, 0x95, 0xdb, 0x11, 0x3a, 0x91, 0x76, 0x78, 0xb2,
+                       0x73, 0xbe, 0xd6, 0xb8, 0xe3, 0xc1, 0x74, 0x3b, 0x71, 0x16, 0xe6, 0x9e, 0x22, 0x22, 0x95, 0x16,
+                       0x3f, 0xf1, 0xca, 0xa1, 0x68, 0x1f, 0xac, 0x09, 0x12, 0x0e, 0xca, 0x30, 0x75, 0x86, 0xe1, 0xa7,
+               },
+       },
+       {
+               "CBC-AES192",
+               commonKey192,
+               commonIV,
+               commonInput,
+               []byte{
+                       0x4f, 0x02, 0x1d, 0xb2, 0x43, 0xbc, 0x63, 0x3d, 0x71, 0x78, 0x18, 0x3a, 0x9f, 0xa0, 0x71, 0xe8,
+                       0xb4, 0xd9, 0xad, 0xa9, 0xad, 0x7d, 0xed, 0xf4, 0xe5, 0xe7, 0x38, 0x76, 0x3f, 0x69, 0x14, 0x5a,
+                       0x57, 0x1b, 0x24, 0x20, 0x12, 0xfb, 0x7a, 0xe0, 0x7f, 0xa9, 0xba, 0xac, 0x3d, 0xf1, 0x02, 0xe0,
+                       0x08, 0xb0, 0xe2, 0x79, 0x88, 0x59, 0x88, 0x81, 0xd9, 0x20, 0xa9, 0xe6, 0x4f, 0x56, 0x15, 0xcd,
+               },
+       },
+       {
+               "CBC-AES256",
+               commonKey256,
+               commonIV,
+               commonInput,
+               []byte{
+                       0xf5, 0x8c, 0x4c, 0x04, 0xd6, 0xe5, 0xf1, 0xba, 0x77, 0x9e, 0xab, 0xfb, 0x5f, 0x7b, 0xfb, 0xd6,
+                       0x9c, 0xfc, 0x4e, 0x96, 0x7e, 0xdb, 0x80, 0x8d, 0x67, 0x9f, 0x77, 0x7b, 0xc6, 0x70, 0x2c, 0x7d,
+                       0x39, 0xf2, 0x33, 0x69, 0xa9, 0xd9, 0xba, 0xcf, 0xa5, 0x30, 0xe2, 0x63, 0x04, 0x23, 0x14, 0x61,
+                       0xb2, 0xeb, 0x05, 0xe2, 0xc3, 0x9b, 0xe9, 0xfc, 0xda, 0x6c, 0x19, 0x07, 0x8c, 0x6a, 0x9d, 0x1b,
+               },
+       },
+}
+
+func TestCBC_AES(t *testing.T) {
+       for _, tt := range cbcAESTests {
+               test := tt.name
+
+               c, err := aes.NewCipher(tt.key)
+               if err != nil {
+                       t.Errorf("%s: NewCipher(%d bytes) = %s", test, len(tt.key), err)
+                       continue
+               }
+
+               encrypter := 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)
+               }
+
+               decrypter := 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)
+               }
+       }
+}
diff --git a/src/pkg/crypto/cipher/cipher.go b/src/pkg/crypto/cipher/cipher.go
new file mode 100644 (file)
index 0000000..50516b2
--- /dev/null
@@ -0,0 +1,63 @@
+// Copyright 2010 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.
+
+// The cipher package implements standard block cipher modes
+// that can be wrapped around low-level block cipher implementations.
+// See http://csrc.nist.gov/groups/ST/toolkit/BCM/current_modes.html
+// and NIST Special Publication 800-38A.
+package cipher
+
+// A Block represents an implementation of block cipher
+// using a given key.  It provides the capability to encrypt
+// or decrypt individual blocks.  The mode implementations
+// extend that capability to streams of blocks.
+type Block interface {
+       // BlockSize returns the cipher's block size.
+       BlockSize() int
+
+       // Encrypt encrypts the first block in src into dst.
+       // Dst and src may point at the same memory.
+       Encrypt(dst, src []byte)
+
+       // Decrypt decrypts the first block in src into dst.
+       // Dst and src may point at the same memory.
+       Decrypt(dst, src []byte)
+}
+
+// A Stream represents a stream cipher.
+type Stream interface {
+       // XORKeyStream XORs each byte in the given slice with a byte from the
+       // cipher's key stream. Dst and src may point to the same memory.
+       XORKeyStream(dst, src []byte)
+}
+
+// A BlockMode represents a block cipher running in a block-based mode (CBC,
+// ECB etc).
+type BlockMode interface {
+       // BlockSize returns the mode's block size.
+       BlockSize() int
+
+       // CryptBlocks encrypts or decrypts a number of blocks. The length of
+       // src must be a multiple of the block size. Dst and src may point to
+       // the same memory.
+       CryptBlocks(dst, src []byte)
+}
+
+// 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)
+       return q
+}
diff --git a/src/pkg/crypto/cipher/common_test.go b/src/pkg/crypto/cipher/common_test.go
new file mode 100644 (file)
index 0000000..fb75575
--- /dev/null
@@ -0,0 +1,28 @@
+// Copyright 2009 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
+
+// Common values for tests.
+
+var commonInput = []byte{
+       0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a,
+       0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c, 0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51,
+       0x30, 0xc8, 0x1c, 0x46, 0xa3, 0x5c, 0xe4, 0x11, 0xe5, 0xfb, 0xc1, 0x19, 0x1a, 0x0a, 0x52, 0xef,
+       0xf6, 0x9f, 0x24, 0x45, 0xdf, 0x4f, 0x9b, 0x17, 0xad, 0x2b, 0x41, 0x7b, 0xe6, 0x6c, 0x37, 0x10,
+}
+
+var commonKey128 = []byte{0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c}
+
+var commonKey192 = []byte{
+       0x8e, 0x73, 0xb0, 0xf7, 0xda, 0x0e, 0x64, 0x52, 0xc8, 0x10, 0xf3, 0x2b, 0x80, 0x90, 0x79, 0xe5,
+       0x62, 0xf8, 0xea, 0xd2, 0x52, 0x2c, 0x6b, 0x7b,
+}
+
+var commonKey256 = []byte{
+       0x60, 0x3d, 0xeb, 0x10, 0x15, 0xca, 0x71, 0xbe, 0x2b, 0x73, 0xae, 0xf0, 0x85, 0x7d, 0x77, 0x81,
+       0x1f, 0x35, 0x2c, 0x07, 0x3b, 0x61, 0x08, 0xd7, 0x2d, 0x98, 0x10, 0xa3, 0x09, 0x14, 0xdf, 0xf4,
+}
+
+var commonIV = []byte{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f}
diff --git a/src/pkg/crypto/cipher/ctr.go b/src/pkg/crypto/cipher/ctr.go
new file mode 100644 (file)
index 0000000..04436ec
--- /dev/null
@@ -0,0 +1,51 @@
+// Copyright 2009 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.
+
+// Counter (CTR) mode.
+
+// CTR converts a block cipher into a stream cipher by
+// repeatedly encrypting an incrementing counter and
+// xoring the resulting stream of data with the input.
+
+// See NIST SP 800-38A, pp 13-15
+
+package cipher
+
+type ctr struct {
+       b       Block
+       ctr     []byte
+       out     []byte
+       outUsed int
+}
+
+// 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 {
+       return &ctr{
+               b:       block,
+               ctr:     dup(iv),
+               out:     make([]byte, len(iv)),
+               outUsed: len(iv),
+       }
+}
+
+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
+                               }
+                       }
+               }
+
+               dst[i] = src[i] ^ x.out[x.outUsed]
+               x.outUsed++
+       }
+}
diff --git a/src/pkg/crypto/cipher/ctr_aes_test.go b/src/pkg/crypto/cipher/ctr_aes_test.go
new file mode 100644 (file)
index 0000000..8dca996
--- /dev/null
@@ -0,0 +1,101 @@
+// Copyright 2009 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.
+
+// CTR AES test vectors.
+
+// See U.S. National Institute of Standards and Technology (NIST)
+// Special Publication 800-38A, ``Recommendation for Block Cipher
+// Modes of Operation,'' 2001 Edition, pp. 55-58.
+
+package cipher
+
+import (
+       "bytes"
+       "crypto/aes"
+       "testing"
+)
+
+var commonCounter = []byte{0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff}
+
+var ctrAESTests = []struct {
+       name string
+       key  []byte
+       iv   []byte
+       in   []byte
+       out  []byte
+}{
+       // NIST SP 800-38A pp 55-58
+       {
+               "CTR-AES128",
+               commonKey128,
+               commonCounter,
+               commonInput,
+               []byte{
+                       0x87, 0x4d, 0x61, 0x91, 0xb6, 0x20, 0xe3, 0x26, 0x1b, 0xef, 0x68, 0x64, 0x99, 0x0d, 0xb6, 0xce,
+                       0x98, 0x06, 0xf6, 0x6b, 0x79, 0x70, 0xfd, 0xff, 0x86, 0x17, 0x18, 0x7b, 0xb9, 0xff, 0xfd, 0xff,
+                       0x5a, 0xe4, 0xdf, 0x3e, 0xdb, 0xd5, 0xd3, 0x5e, 0x5b, 0x4f, 0x09, 0x02, 0x0d, 0xb0, 0x3e, 0xab,
+                       0x1e, 0x03, 0x1d, 0xda, 0x2f, 0xbe, 0x03, 0xd1, 0x79, 0x21, 0x70, 0xa0, 0xf3, 0x00, 0x9c, 0xee,
+               },
+       },
+       {
+               "CTR-AES192",
+               commonKey192,
+               commonCounter,
+               commonInput,
+               []byte{
+                       0x1a, 0xbc, 0x93, 0x24, 0x17, 0x52, 0x1c, 0xa2, 0x4f, 0x2b, 0x04, 0x59, 0xfe, 0x7e, 0x6e, 0x0b,
+                       0x09, 0x03, 0x39, 0xec, 0x0a, 0xa6, 0xfa, 0xef, 0xd5, 0xcc, 0xc2, 0xc6, 0xf4, 0xce, 0x8e, 0x94,
+                       0x1e, 0x36, 0xb2, 0x6b, 0xd1, 0xeb, 0xc6, 0x70, 0xd1, 0xbd, 0x1d, 0x66, 0x56, 0x20, 0xab, 0xf7,
+                       0x4f, 0x78, 0xa7, 0xf6, 0xd2, 0x98, 0x09, 0x58, 0x5a, 0x97, 0xda, 0xec, 0x58, 0xc6, 0xb0, 0x50,
+               },
+       },
+       {
+               "CTR-AES256",
+               commonKey256,
+               commonCounter,
+               commonInput,
+               []byte{
+                       0x60, 0x1e, 0xc3, 0x13, 0x77, 0x57, 0x89, 0xa5, 0xb7, 0xa7, 0xf5, 0x04, 0xbb, 0xf3, 0xd2, 0x28,
+                       0xf4, 0x43, 0xe3, 0xca, 0x4d, 0x62, 0xb5, 0x9a, 0xca, 0x84, 0xe9, 0x90, 0xca, 0xca, 0xf5, 0xc5,
+                       0x2b, 0x09, 0x30, 0xda, 0xa2, 0x3d, 0xe9, 0x4c, 0xe8, 0x70, 0x17, 0xba, 0x2d, 0x84, 0x98, 0x8d,
+                       0xdf, 0xc9, 0xc5, 0x8d, 0xb6, 0x7a, 0xad, 0xa6, 0x13, 0xc2, 0xdd, 0x08, 0x45, 0x79, 0x41, 0xa6,
+               },
+       },
+}
+
+func TestCTR_AES(t *testing.T) {
+       for _, tt := range ctrAESTests {
+               test := tt.name
+
+               c, err := aes.NewCipher(tt.key)
+               if err != nil {
+                       t.Errorf("%s: NewCipher(%d bytes) = %s", test, len(tt.key), err)
+                       continue
+               }
+
+               for j := 0; j <= 5; j += 5 {
+                       in := tt.in[0 : len(tt.in)-j]
+                       ctr := NewCTR(c, tt.iv)
+                       encrypted := make([]byte, len(in))
+                       ctr.XORKeyStream(encrypted, in)
+                       if out := tt.out[0:len(in)]; !bytes.Equal(out, encrypted) {
+                               t.Errorf("%s/%d: CTR\ninpt %x\nhave %x\nwant %x", test, len(in), in, encrypted, out)
+                       }
+               }
+
+               for j := 0; j <= 7; j += 7 {
+                       in := tt.out[0 : len(tt.out)-j]
+                       ctr := NewCTR(c, tt.iv)
+                       plain := make([]byte, len(in))
+                       ctr.XORKeyStream(plain, in)
+                       if out := tt.in[0:len(in)]; !bytes.Equal(out, plain) {
+                               t.Errorf("%s/%d: CTRReader\nhave %x\nwant %x", test, len(out), plain, out)
+                       }
+               }
+
+               if t.Failed() {
+                       break
+               }
+       }
+}
diff --git a/src/pkg/crypto/cipher/io.go b/src/pkg/crypto/cipher/io.go
new file mode 100644 (file)
index 0000000..97f40b8
--- /dev/null
@@ -0,0 +1,57 @@
+// Copyright 2010 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 (
+       "os"
+       "io"
+)
+
+// The Stream* objects are so simple that all their members are public. Users
+// can create them themselves.
+
+// StreamReader wraps a Stream into an io.Reader. It simply calls XORKeyStream
+// to process each slice of data which passes through.
+type StreamReader struct {
+       S Stream
+       R io.Reader
+}
+
+func (r StreamReader) Read(dst []byte) (n int, err os.Error) {
+       n, err = r.R.Read(dst)
+       r.S.XORKeyStream(dst[:n], dst[:n])
+       return
+}
+
+// StreamWriter wraps a Stream into an io.Writer. It simply calls XORKeyStream
+// to process each slice of data which passes through. If any Write call
+// returns short then the StreamWriter is out of sync and must be discarded.
+type StreamWriter struct {
+       S   Stream
+       W   io.Writer
+       Err os.Error
+}
+
+func (w StreamWriter) Write(src []byte) (n int, err os.Error) {
+       if w.Err != nil {
+               return 0, w.Err
+       }
+       c := make([]byte, len(src))
+       w.S.XORKeyStream(c, src)
+       n, err = w.W.Write(c)
+       if n != len(src) {
+               if err == nil { // should never happen
+                       err = io.ErrShortWrite
+               }
+               w.Err = err
+       }
+       return
+}
+
+func (w StreamWriter) Close() os.Error {
+       // This saves us from either requiring a WriteCloser or having a
+       // StreamWriterCloser.
+       return w.W.(io.Closer).Close()
+}