From 56b174d983219ad360d24f1f76405701f8e21d5e Mon Sep 17 00:00:00 2001 From: Mikkel Krautz Date: Sat, 8 Dec 2012 23:34:42 +0100 Subject: [PATCH] pkg/cryptstate: make cryptstate independent of the chosen crypto mode. --- pkg/cryptstate/cryptstate.go | 46 +++++++++++++----------- pkg/cryptstate/mode_ocb2.go | 68 ++++++++++++++++++++++++++++++++++++ pkg/cryptstate/ocb2/ocb2.go | 7 ++-- 3 files changed, 97 insertions(+), 24 deletions(-) create mode 100644 pkg/cryptstate/mode_ocb2.go diff --git a/pkg/cryptstate/cryptstate.go b/pkg/cryptstate/cryptstate.go index 2fb4434..03afd80 100644 --- a/pkg/cryptstate/cryptstate.go +++ b/pkg/cryptstate/cryptstate.go @@ -6,7 +6,6 @@ package cryptstate import ( "crypto/aes" - "crypto/cipher" "crypto/rand" "errors" "io" @@ -16,6 +15,16 @@ import ( const DecryptHistorySize = 0x100 +type CryptoMode interface { + NonceSize() int + KeySize() int + Overhead() int + + SetKey([]byte) + Encrypt(dst []byte, src []byte, nonce []byte) + Decrypt(dst []byte, src []byte, nonce []byte) bool +} + type CryptState struct { Key []byte EncryptIV []byte @@ -33,7 +42,7 @@ type CryptState struct { RemoteResync uint32 decryptHistory [DecryptHistorySize]byte - cipher cipher.Block + mode CryptoMode } // SupportedModes returns the list of supported CryptoModes. @@ -41,12 +50,23 @@ func SupportedModes() []string { return []string{"OCB2-AES128"} } +// createMode creates the CryptoMode with the given mode name. +func createMode(mode string) CryptoMode { + switch mode { + case "OCB2-AES128": + return &ocb2Mode{} + } + panic("cryptstate: no such CryptoMode") +} + func (cs *CryptState) GenerateKey() error { - cs.Key = make([]byte, aes.BlockSize) + cs.mode = createMode("OCB2-AES128") + cs.Key = make([]byte, cs.mode.KeySize()) _, err := io.ReadFull(rand.Reader, cs.Key) if err != nil { return err } + cs.mode.SetKey(cs.Key) cs.EncryptIV = make([]byte, ocb2.NonceSize) _, err = io.ReadFull(rand.Reader, cs.EncryptIV) @@ -60,11 +80,6 @@ func (cs *CryptState) GenerateKey() error { return err } - cs.cipher, err = aes.NewCipher(cs.Key) - if err != nil { - return err - } - return nil } @@ -77,8 +92,8 @@ func (cs *CryptState) SetKey(key []byte, eiv []byte, div []byte) error { if err != nil { return err } - cs.cipher = cipher + cs.mode = &ocb2Mode{cipher: cipher} return nil } @@ -93,7 +108,6 @@ func (cs *CryptState) Decrypt(dst, src []byte) error { } ivbyte := src[0] - tag := src[1:4] restore := false lost := 0 late := 0 @@ -167,7 +181,7 @@ func (cs *CryptState) Decrypt(dst, src []byte) error { } } - ok := ocb2.Decrypt(cs.cipher, dst, src[4:], cs.DecryptIV, tag[:]) + ok := cs.mode.Decrypt(dst, src[1:], cs.DecryptIV) if !ok { cs.DecryptIV = saveiv return errors.New("cryptstate: tag mismatch") @@ -197,8 +211,6 @@ func (cs *CryptState) Decrypt(dst, src []byte) error { } func (cs *CryptState) Encrypt(dst, src []byte) { - var tag [ocb2.TagSize]byte - // First, increase our IV for i := range cs.EncryptIV { cs.EncryptIV[i] += 1 @@ -207,12 +219,6 @@ func (cs *CryptState) Encrypt(dst, src []byte) { } } - ocb2.Encrypt(cs.cipher, dst[4:], src, cs.EncryptIV, tag[:]) - dst[0] = cs.EncryptIV[0] - dst[1] = tag[0] - dst[2] = tag[1] - dst[3] = tag[2] - - return + cs.mode.Encrypt(dst[1:], src, cs.EncryptIV) } diff --git a/pkg/cryptstate/mode_ocb2.go b/pkg/cryptstate/mode_ocb2.go new file mode 100644 index 0000000..223845c --- /dev/null +++ b/pkg/cryptstate/mode_ocb2.go @@ -0,0 +1,68 @@ +// Copyright (c) 2012 The Grumble Authors +// The use of this source code is goverened by a BSD-style +// license that can be found in the LICENSE-file. + +package cryptstate + +import ( + "crypto/aes" + "crypto/cipher" + "mumbleapp.com/grumble/pkg/cryptstate/ocb2" +) + +// ocb2Mode implements the OCB2-AES128 CryptoMode +type ocb2Mode struct { + cipher cipher.Block +} + +// NonceSize returns the nonce size to be used with OCB2-AES128. +func (ocb *ocb2Mode) NonceSize() int { + return ocb2.NonceSize +} + +// KeySize returns the key size to be used with OCB2-AES128. +func (ocb *ocb2Mode) KeySize() int { + return aes.BlockSize +} + +// Overhead returns the overhead that a ciphertext has over a plaintext. +// In the case of OCB2-AES128, the overhead is the authentication tag. +func (ocb *ocb2Mode) Overhead() int { + return 3 +} + +// SetKey sets a new key. The key must have a length equal to KeySize(). +func (ocb *ocb2Mode) SetKey(key []byte) { + if len(key) != ocb.KeySize() { + panic("cryptstate: invalid key length") + } + + cipher, err := aes.NewCipher(key) + if err != nil { + panic("cryptstate: NewCipher returned unexpected " + err.Error()) + } + ocb.cipher = cipher +} + +// Encrypt encrypts a message using OCB2-AES128 and outputs it to dst. +func (ocb *ocb2Mode) Encrypt(dst []byte, src []byte, nonce []byte) { + if len(dst) <= ocb.Overhead() { + panic("cryptstate: bad dst") + } + + tag := dst[0:3] + dst = dst[3:] + ocb2.Encrypt(ocb.cipher, dst, src, nonce, tag) +} + +// Decrypt decrypts a message using OCB2-AES128 and outputs it to dst. +// Returns false if decryption failed (authentication tag mismatch). +func (ocb *ocb2Mode) Decrypt(dst []byte, src []byte, nonce []byte) bool { + if len(src) <= ocb.Overhead() { + panic("cryptstate: bad src") + } + + tag := src[0:3] + src = src[3:] + return ocb2.Decrypt(ocb.cipher, dst, src, nonce, tag) +} diff --git a/pkg/cryptstate/ocb2/ocb2.go b/pkg/cryptstate/ocb2/ocb2.go index 55c7438..5c67bb3 100644 --- a/pkg/cryptstate/ocb2/ocb2.go +++ b/pkg/cryptstate/ocb2/ocb2.go @@ -119,15 +119,13 @@ func Encrypt(cipher cipher.Block, dst []byte, src []byte, nonce []byte, tag []by if len(nonce) != NonceSize { panic("ocb2: nonce length is not equal to ocb2.NonceSize") } - if len(tag) != TagSize { - panic("ocb2: tag length is not equal to ocb2.TagSize") - } var ( checksum [BlockSize]byte delta [BlockSize]byte tmp [BlockSize]byte pad [BlockSize]byte + calcTag [NonceSize]byte off int ) @@ -167,7 +165,8 @@ func Encrypt(cipher cipher.Block, dst []byte, src []byte, nonce []byte, tag []by times3(delta[0:]) xor(tmp[0:], delta[0:], checksum[0:]) - cipher.Encrypt(tag[0:], tmp[0:]) + cipher.Encrypt(calcTag[0:], tmp[0:]) + copy(tag, calcTag[:]) } // Decrypt takes a ciphertext, a nonce, and a tag as its input and outputs a decrypted