1
0
mirror of https://github.com/golang/go synced 2024-11-21 17:54:39 -07:00

crypto/cipher: add CFB and OCFB mode.

(Files which I left out of the initial commit to keep it small.)

R=rsc
CC=golang-dev
https://golang.org/cl/3183043
This commit is contained in:
Adam Langley 2010-11-19 16:17:58 -05:00
parent b51f0c5cca
commit b84b20b820
5 changed files with 232 additions and 1 deletions

View File

@ -9,6 +9,8 @@ GOFILES=\
cbc.go\
cipher.go\
ctr.go\
io.go
io.go\
ocfb.go\
cfb.go
include ../../../Make.pkg

View File

@ -0,0 +1,64 @@
// 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.
// CFB (Cipher Feedback) Mode.
package cipher
type cfb struct {
b Block
out []byte
outUsed int
decrypt bool
}
// 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 {
return newCFB(block, iv, false)
}
// NewCFBDecrypter returns a Stream which decrypts with cipher feedback mode,
// using the given Block. The iv must be the same length as the Block's block
// size.
func NewCFBDecrypter(block Block, iv []byte) Stream {
return newCFB(block, iv, true)
}
func newCFB(block Block, iv []byte, decrypt bool) Stream {
blockSize := block.BlockSize()
if len(iv) != blockSize {
return nil
}
x := &cfb{
b: block,
out: make([]byte, blockSize),
outUsed: 0,
decrypt: decrypt,
}
block.Encrypt(x.out, 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++
}
}

View File

@ -0,0 +1,35 @@
// 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 (
"bytes"
"crypto/aes"
"crypto/rand"
"testing"
)
func TestCFB(t *testing.T) {
block, err := aes.NewCipher(commonKey128)
if err != nil {
t.Error(err)
return
}
plaintext := []byte("this is the plaintext")
iv := make([]byte, block.BlockSize())
rand.Reader.Read(iv)
cfb := NewCFBEncrypter(block, iv)
ciphertext := make([]byte, len(plaintext))
cfb.XORKeyStream(ciphertext, plaintext)
cfbdec := NewCFBDecrypter(block, iv)
plaintextCopy := make([]byte, len(plaintext))
cfbdec.XORKeyStream(plaintextCopy, ciphertext)
if !bytes.Equal(plaintextCopy, plaintext) {
t.Errorf("got: %x, want: %x", plaintextCopy, plaintext)
}
}

View File

@ -0,0 +1,91 @@
// 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.
// OpenPGP CFB Mode. http://tools.ietf.org/html/rfc4880#section-13.9
package cipher
type ocfb struct {
b Block
fre []byte
outUsed int
}
// NewOCFBEncrypter returns a Stream which encrypts data with OpenPGP's cipher
// feedback mode using the given Block, and an initial amount of ciphertext.
// randData must be random bytes and be the same length as the Block's block
// size.
func NewOCFBEncrypter(block Block, randData []byte) (Stream, []byte) {
blockSize := block.BlockSize()
if len(randData) != blockSize {
return nil, nil
}
x := &ocfb{
b: block,
fre: make([]byte, blockSize),
outUsed: 0,
}
prefix := make([]byte, blockSize+2)
block.Encrypt(x.fre, x.fre)
for i := 0; i < blockSize; i++ {
prefix[i] = randData[i] ^ x.fre[i]
}
block.Encrypt(x.fre, prefix[:blockSize])
prefix[blockSize] = x.fre[0] ^ randData[blockSize-2]
prefix[blockSize+1] = x.fre[1] ^ randData[blockSize-1]
block.Encrypt(x.fre, prefix[2:])
return x, prefix
}
// NewOCFBDecrypter returns a Stream which decrypts data with OpenPGP's cipher
// feedback mode using the given Block. Prefix must be the first blockSize + 2
// bytes of the ciphertext, where blockSize is the Block's block size. If an
// incorrect key is detected then nil is returned.
func NewOCFBDecrypter(block Block, prefix []byte) Stream {
blockSize := block.BlockSize()
if len(prefix) != blockSize+2 {
return nil
}
x := &ocfb{
b: block,
fre: make([]byte, blockSize),
outUsed: 0,
}
prefixCopy := make([]byte, len(prefix))
copy(prefixCopy, prefix)
block.Encrypt(x.fre, x.fre)
for i := 0; i < blockSize; i++ {
prefixCopy[i] ^= x.fre[i]
}
block.Encrypt(x.fre, prefix[:blockSize])
prefixCopy[blockSize] ^= x.fre[0]
prefixCopy[blockSize+1] ^= x.fre[1]
if prefixCopy[blockSize-2] != prefixCopy[blockSize] ||
prefixCopy[blockSize-1] != prefixCopy[blockSize+1] {
return nil
}
block.Encrypt(x.fre, prefix[2:])
return x
}
func (x *ocfb) XORKeyStream(dst, src []byte) {
for i := 0; i < len(src); i++ {
if x.outUsed == len(x.fre) {
x.b.Encrypt(x.fre, x.fre)
x.outUsed = 0
}
dst[i] = x.fre[x.outUsed] ^ src[i]
x.outUsed++
}
}

View File

@ -0,0 +1,39 @@
// 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 (
"bytes"
"crypto/aes"
"crypto/rand"
"testing"
)
func TestOCFB(t *testing.T) {
block, err := aes.NewCipher(commonKey128)
if err != nil {
t.Error(err)
return
}
plaintext := []byte("this is the plaintext")
randData := make([]byte, block.BlockSize())
rand.Reader.Read(randData)
ocfb, prefix := NewOCFBEncrypter(block, randData)
ciphertext := make([]byte, len(plaintext))
ocfb.XORKeyStream(ciphertext, plaintext)
ocfbdec := NewOCFBDecrypter(block, prefix)
if ocfbdec == nil {
t.Error("NewOCFBDecrypter failed")
return
}
plaintextCopy := make([]byte, len(plaintext))
ocfbdec.XORKeyStream(plaintextCopy, ciphertext)
if !bytes.Equal(plaintextCopy, plaintext) {
t.Errorf("got: %x, want: %x", plaintextCopy, plaintext)
}
}