diff options
author | Mikkel Krautz <mikkel@krautz.dk> | 2012-12-09 01:56:05 +0400 |
---|---|---|
committer | Mikkel Krautz <mikkel@krautz.dk> | 2012-12-09 01:56:05 +0400 |
commit | 154b7938d3c68cb395401eb72cab00a33ccd30fd (patch) | |
tree | 10ed954476f8fad75e3595013c9163edbb05fc4a /pkg | |
parent | 2b12adc014f3d1e4342f648584390a515f0114c7 (diff) |
pkg/cryptstate, pkg/cryptstate/ocb2: move OCB2 tag verification into ocb2.Decrypt.
Diffstat (limited to 'pkg')
-rw-r--r-- | pkg/cryptstate/cryptstate.go | 13 | ||||
-rw-r--r-- | pkg/cryptstate/ocb2/ocb2.go | 484 | ||||
-rw-r--r-- | pkg/cryptstate/ocb2/ocb2_test.go | 406 |
3 files changed, 453 insertions, 450 deletions
diff --git a/pkg/cryptstate/cryptstate.go b/pkg/cryptstate/cryptstate.go index c4bca38..2fb4434 100644 --- a/pkg/cryptstate/cryptstate.go +++ b/pkg/cryptstate/cryptstate.go @@ -92,8 +92,8 @@ func (cs *CryptState) Decrypt(dst, src []byte) error { return errors.New("cryptstate: plain_len and src len mismatch") } - var tag [ocb2.TagSize]byte ivbyte := src[0] + tag := src[1:4] restore := false lost := 0 late := 0 @@ -167,13 +167,10 @@ func (cs *CryptState) Decrypt(dst, src []byte) error { } } - ocb2.Decrypt(cs.cipher, dst, src[4:], cs.DecryptIV, tag[:]) - - for i := 0; i < 3; i++ { - if tag[i] != src[i+1] { - cs.DecryptIV = saveiv - return errors.New("tag mismatch") - } + ok := ocb2.Decrypt(cs.cipher, dst, src[4:], cs.DecryptIV, tag[:]) + if !ok { + cs.DecryptIV = saveiv + return errors.New("cryptstate: tag mismatch") } cs.decryptHistory[cs.DecryptIV[0]] = cs.DecryptIV[0] diff --git a/pkg/cryptstate/ocb2/ocb2.go b/pkg/cryptstate/ocb2/ocb2.go index 55e45a0..55c7438 100644 --- a/pkg/cryptstate/ocb2/ocb2.go +++ b/pkg/cryptstate/ocb2/ocb2.go @@ -1,237 +1,247 @@ -// Copyright (c) 2010-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 ocb2 implements the version 2 of the OCB authenticated-encryption algorithm.
-// OCB2 is specified in http://www.cs.ucdavis.edu/~rogaway/papers/draft-krovetz-ocb-00.txt.
-//
-// Note that this implementation is limited to block ciphers with a block size of 128 bits.
-//
-// It should also be noted that OCB's author, Phil Rogaway <rogaway@cs.ucdavis.edu>, holds
-// several US patents on the algorithm. This should be considered before using this code
-// in your own projects. See OCB's FAQ for more info:
-// http://www.cs.ucdavis.edu/~rogaway/ocb/ocb-faq.htm#patent:phil
-//
-// The Mumble Project has a license to use OCB mode in its BSD licensed code on a royalty
-// free basis.
-package ocb2
-
-import "crypto/cipher"
-
-const (
- // BlockSize defines the block size that this particular implementation
- // of OCB2 is made to work on.
- BlockSize = 16
- // TagSize specifies the length in bytes of a full OCB2 tag.
- // As per the specification, applications may truncate their
- // tags to a given length, but advocates that typical applications
- // should use a tag length of at least 8 bytes (64 bits).
- TagSize = BlockSize
- // NonceSize specifies the length in bytes of an OCB2 nonce.
- NonceSize = BlockSize
-)
-
-// zeros fills block with zero bytes.
-func zeros(block []byte) {
- for i := range block {
- block[i] = 0
- }
-}
-
-// xor outputs the bitwise exclusive-or of a and b to dst.
-func xor(dst []byte, a []byte, b []byte) {
- for i := 0; i < BlockSize; i++ {
- dst[i] = a[i] ^ b[i]
- }
-}
-
-// times2 performs the times2 operation, defined as:
-//
-// times2(S)
-// S << 1 if S[1] = 0, and (S << 1) xor const(bitlength(S)) if S[1] = 1.
-//
-// where const(n) is defined as
-//
-// const(n)
-// The lexicographically first n-bit string C among all
-// strings that have a minimal possible number of "1"
-// bits and which name a polynomial x^n + C[1] *
-// x^{n-1} + ... + C[n-1] * x^1 + C[n] * x^0 that is
-// irreducible over the field with two elements. In
-// particular, const(128) = num2str(135, 128). For
-// other values of n, refer to a standard table of
-// irreducible polynomials [G. Seroussi,
-// "Table of low-weight binary irreducible polynomials",
-// HP Labs Technical Report HPL-98-135, 1998.].
-//
-// and num2str(x, n) is defined as
-//
-// num2str(x, n)
-// The n-bit binary representation of the integer x.
-// More formally, the n-bit string S where x = S[1] *
-// 2^{n-1} + S[2] * 2^{n-2} + ... + S[n] * 2^{0}. Only
-// used when 0 <= x < 2^n.
-//
-// For our 128-bit block size implementation, this means that
-// the xor with const(bitlength(S)) if S[1] = 1 is implemented
-// by simply xor'ing the last byte with the number 135 when
-// S[1] = 1.
-func times2(block []byte) {
- carry := (block[0] >> 7) & 0x1
- for i := 0; i < BlockSize-1; i++ {
- block[i] = (block[i] << 1) | ((block[i+1] >> 7) & 0x1)
- }
- block[BlockSize-1] = (block[BlockSize-1] << 1) ^ (carry * 135)
-}
-
-// times3 performs the times3 operation, defined as:
-//
-// times3(S)
-// times2(S) xor S
-func times3(block []byte) {
- carry := (block[0] >> 7) & 0x1
- for i := 0; i < BlockSize-1; i++ {
- block[i] ^= (block[i] << 1) | ((block[i+1] >> 7) & 0x1)
- }
- block[BlockSize-1] ^= ((block[BlockSize-1] << 1) ^ (carry * 135))
-}
-
-// Encrypt encrypts the plaintext src and outputs the corresponding ciphertext into dst.
-// Besides outputting a ciphertext into dst, Encrypt also outputs an authentication tag
-// of ocb2.TagSize bytes into tag, which should be used to verify the authenticity of the
-// message on the receiving side.
-//
-// To ensure both authenticity and secrecy of messages, each invocation to this function must
-// be given an unique nonce of ocb2.NonceSize bytes. The nonce need not be secret (it can be
-// a counter), but it needs to be unique.
-//
-// The block cipher used in function must work on a block size equal to ocb2.BlockSize.
-// The tag slice used in this function must have a length equal to ocb2.TagSize.
-// The nonce slice used in this function must have a length equal to ocb2.NonceSize.
-// If any of the above are violated, Encrypt will panic.
-func Encrypt(cipher cipher.Block, dst []byte, src []byte, nonce []byte, tag []byte) {
- if cipher.BlockSize() != BlockSize {
- panic("ocb2: cipher blocksize is not equal to ocb2.BlockSize")
- }
- 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
- off int
- )
-
- cipher.Encrypt(delta[0:], nonce[0:])
- zeros(checksum[0:])
-
- remain := len(src)
- for remain > BlockSize {
- times2(delta[0:])
- xor(tmp[0:], delta[0:], src[off:off+BlockSize])
- cipher.Encrypt(tmp[0:], tmp[0:])
- xor(dst[off:off+BlockSize], delta[0:], tmp[0:])
- xor(checksum[0:], checksum[0:], src[off:off+BlockSize])
- remain -= BlockSize
- off += BlockSize
- }
-
- times2(delta[0:])
- zeros(tmp[0:])
- num := remain * 8
- tmp[BlockSize-2] = uint8((uint32(num) >> 8) & 0xff)
- tmp[BlockSize-1] = uint8(num & 0xff)
- xor(tmp[0:], tmp[0:], delta[0:])
- cipher.Encrypt(pad[0:], tmp[0:])
- copied := copy(tmp[0:], src[off:])
- if copied != remain {
- panic("ocb2: copy failed")
- }
- if copy(tmp[copied:], pad[copied:]) != (BlockSize - remain) {
- panic("ocb2: copy failed")
- }
- xor(checksum[0:], checksum[0:], tmp[0:])
- xor(tmp[0:], pad[0:], tmp[0:])
- if copy(dst[off:], tmp[0:]) != remain {
- panic("ocb2: copy failed")
- }
-
- times3(delta[0:])
- xor(tmp[0:], delta[0:], checksum[0:])
- cipher.Encrypt(tag[0:], tmp[0:])
-}
-
-// Decrypt takes a ciphertext and a nonce as its input and outputs a decrypted plaintext
-// and corresponding authentication tag.
-//
-// Before using the decrpyted plaintext, the application
-// should verify that the computed authentication tag matches the tag that was produced when
-// encrypting the message (taking into consideration that OCB tags are allowed to be truncated
-// to a length less than ocb.TagSize).
-//
-// The block cipher used in function must work on a block size equal to ocb2.BlockSize.
-// The tag slice used in this function must have a length equal to ocb2.TagSize.
-// The nonce slice used in this function must have a length equal to ocb2.NonceSize.
-// If any of the above are violated, Encrypt will panic.
-func Decrypt(cipher cipher.Block, plain []byte, encrypted []byte, nonce []byte, tag []byte) {
- if cipher.BlockSize() != BlockSize {
- panic("ocb2: cipher blocksize is not equal to ocb2.BlockSize")
- }
- 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
- off int
- )
-
- cipher.Encrypt(delta[0:], nonce[0:])
- zeros(checksum[0:])
-
- remain := len(encrypted)
- for remain > BlockSize {
- times2(delta[0:])
- xor(tmp[0:], delta[0:], encrypted[off:off+BlockSize])
- cipher.Decrypt(tmp[0:], tmp[0:])
- xor(plain[off:off+BlockSize], delta[0:], tmp[0:])
- xor(checksum[0:], checksum[0:], plain[off:off+BlockSize])
- off += BlockSize
- remain -= BlockSize
- }
-
- times2(delta[0:])
- zeros(tmp[0:])
- num := remain * 8
- tmp[BlockSize-2] = uint8((uint32(num) >> 8) & 0xff)
- tmp[BlockSize-1] = uint8(num & 0xff)
- xor(tmp[0:], tmp[0:], delta[0:])
- cipher.Encrypt(pad[0:], tmp[0:])
- zeros(tmp[0:])
- copied := copy(tmp[0:remain], encrypted[off:off+remain])
- if copied != remain {
- panic("ocb2: copy failed")
- }
- xor(tmp[0:], tmp[0:], pad[0:])
- xor(checksum[0:], checksum[0:], tmp[0:])
- copied = copy(plain[off:off+remain], tmp[0:remain])
- if copied != remain {
- panic("ocb2: copy failed")
- }
-
- times3(delta[0:])
- xor(tmp[0:], delta[0:], checksum[0:])
- cipher.Encrypt(tag[0:], tmp[0:])
-}
\ No newline at end of file +// Copyright (c) 2010-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 ocb2 implements the version 2 of the OCB authenticated-encryption algorithm. +// OCB2 is specified in http://www.cs.ucdavis.edu/~rogaway/papers/draft-krovetz-ocb-00.txt. +// +// Note that this implementation is limited to block ciphers with a block size of 128 bits. +// +// It should also be noted that OCB's author, Phil Rogaway <rogaway@cs.ucdavis.edu>, holds +// several US patents on the algorithm. This should be considered before using this code +// in your own projects. See OCB's FAQ for more info: +// http://www.cs.ucdavis.edu/~rogaway/ocb/ocb-faq.htm#patent:phil +// +// The Mumble Project has a license to use OCB mode in its BSD licensed code on a royalty +// free basis. +package ocb2 + +import ( + "crypto/cipher" + "crypto/subtle" +) + +const ( + // BlockSize defines the block size that this particular implementation + // of OCB2 is made to work on. + BlockSize = 16 + // TagSize specifies the length in bytes of a full OCB2 tag. + // As per the specification, applications may truncate their + // tags to a given length, but advocates that typical applications + // should use a tag length of at least 8 bytes (64 bits). + TagSize = BlockSize + // NonceSize specifies the length in bytes of an OCB2 nonce. + NonceSize = BlockSize +) + +// zeros fills block with zero bytes. +func zeros(block []byte) { + for i := range block { + block[i] = 0 + } +} + +// xor outputs the bitwise exclusive-or of a and b to dst. +func xor(dst []byte, a []byte, b []byte) { + for i := 0; i < BlockSize; i++ { + dst[i] = a[i] ^ b[i] + } +} + +// times2 performs the times2 operation, defined as: +// +// times2(S) +// S << 1 if S[1] = 0, and (S << 1) xor const(bitlength(S)) if S[1] = 1. +// +// where const(n) is defined as +// +// const(n) +// The lexicographically first n-bit string C among all +// strings that have a minimal possible number of "1" +// bits and which name a polynomial x^n + C[1] * +// x^{n-1} + ... + C[n-1] * x^1 + C[n] * x^0 that is +// irreducible over the field with two elements. In +// particular, const(128) = num2str(135, 128). For +// other values of n, refer to a standard table of +// irreducible polynomials [G. Seroussi, +// "Table of low-weight binary irreducible polynomials", +// HP Labs Technical Report HPL-98-135, 1998.]. +// +// and num2str(x, n) is defined as +// +// num2str(x, n) +// The n-bit binary representation of the integer x. +// More formally, the n-bit string S where x = S[1] * +// 2^{n-1} + S[2] * 2^{n-2} + ... + S[n] * 2^{0}. Only +// used when 0 <= x < 2^n. +// +// For our 128-bit block size implementation, this means that +// the xor with const(bitlength(S)) if S[1] = 1 is implemented +// by simply xor'ing the last byte with the number 135 when +// S[1] = 1. +func times2(block []byte) { + carry := (block[0] >> 7) & 0x1 + for i := 0; i < BlockSize-1; i++ { + block[i] = (block[i] << 1) | ((block[i+1] >> 7) & 0x1) + } + block[BlockSize-1] = (block[BlockSize-1] << 1) ^ (carry * 135) +} + +// times3 performs the times3 operation, defined as: +// +// times3(S) +// times2(S) xor S +func times3(block []byte) { + carry := (block[0] >> 7) & 0x1 + for i := 0; i < BlockSize-1; i++ { + block[i] ^= (block[i] << 1) | ((block[i+1] >> 7) & 0x1) + } + block[BlockSize-1] ^= ((block[BlockSize-1] << 1) ^ (carry * 135)) +} + +// Encrypt encrypts the plaintext src and outputs the corresponding ciphertext into dst. +// Besides outputting a ciphertext into dst, Encrypt also outputs an authentication tag +// of ocb2.TagSize bytes into tag, which should be used to verify the authenticity of the +// message on the receiving side. +// +// To ensure both authenticity and secrecy of messages, each invocation to this function must +// be given an unique nonce of ocb2.NonceSize bytes. The nonce need not be secret (it can be +// a counter), but it needs to be unique. +// +// The block cipher used in function must work on a block size equal to ocb2.BlockSize. +// The tag slice used in this function must have a length equal to ocb2.TagSize. +// The nonce slice used in this function must have a length equal to ocb2.NonceSize. +// If any of the above are violated, Encrypt will panic. +func Encrypt(cipher cipher.Block, dst []byte, src []byte, nonce []byte, tag []byte) { + if cipher.BlockSize() != BlockSize { + panic("ocb2: cipher blocksize is not equal to ocb2.BlockSize") + } + 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 + off int + ) + + cipher.Encrypt(delta[0:], nonce[0:]) + zeros(checksum[0:]) + + remain := len(src) + for remain > BlockSize { + times2(delta[0:]) + xor(tmp[0:], delta[0:], src[off:off+BlockSize]) + cipher.Encrypt(tmp[0:], tmp[0:]) + xor(dst[off:off+BlockSize], delta[0:], tmp[0:]) + xor(checksum[0:], checksum[0:], src[off:off+BlockSize]) + remain -= BlockSize + off += BlockSize + } + + times2(delta[0:]) + zeros(tmp[0:]) + num := remain * 8 + tmp[BlockSize-2] = uint8((uint32(num) >> 8) & 0xff) + tmp[BlockSize-1] = uint8(num & 0xff) + xor(tmp[0:], tmp[0:], delta[0:]) + cipher.Encrypt(pad[0:], tmp[0:]) + copied := copy(tmp[0:], src[off:]) + if copied != remain { + panic("ocb2: copy failed") + } + if copy(tmp[copied:], pad[copied:]) != (BlockSize - remain) { + panic("ocb2: copy failed") + } + xor(checksum[0:], checksum[0:], tmp[0:]) + xor(tmp[0:], pad[0:], tmp[0:]) + if copy(dst[off:], tmp[0:]) != remain { + panic("ocb2: copy failed") + } + + times3(delta[0:]) + xor(tmp[0:], delta[0:], checksum[0:]) + cipher.Encrypt(tag[0:], tmp[0:]) +} + +// Decrypt takes a ciphertext, a nonce, and a tag as its input and outputs a decrypted +// plaintext (if successful) and a boolean flag that determines whether the function +// successfully decrypted the given ciphertext. +// +// Before using the decrpyted plaintext, the application +// should verify that the computed authentication tag matches the tag that was produced when +// encrypting the message (taking into consideration that OCB tags are allowed to be truncated +// to a length less than ocb.TagSize). +// +// The block cipher used in function must work on a block size equal to ocb2.BlockSize. +// The tag slice used in this function must have a length equal to ocb2.TagSize. +// The nonce slice used in this function must have a length equal to ocb2.NonceSize. +// If any of the above are violated, Encrypt will panic. +func Decrypt(cipher cipher.Block, plain []byte, encrypted []byte, nonce []byte, tag []byte) bool { + if cipher.BlockSize() != BlockSize { + panic("ocb2: cipher blocksize is not equal to ocb2.BlockSize") + } + if len(nonce) != NonceSize { + panic("ocb2: nonce length is not equal to ocb2.NonceSize") + } + + var ( + checksum [BlockSize]byte + delta [BlockSize]byte + tmp [BlockSize]byte + pad [BlockSize]byte + calcTag [NonceSize]byte + off int + ) + + cipher.Encrypt(delta[0:], nonce[0:]) + zeros(checksum[0:]) + + remain := len(encrypted) + for remain > BlockSize { + times2(delta[0:]) + xor(tmp[0:], delta[0:], encrypted[off:off+BlockSize]) + cipher.Decrypt(tmp[0:], tmp[0:]) + xor(plain[off:off+BlockSize], delta[0:], tmp[0:]) + xor(checksum[0:], checksum[0:], plain[off:off+BlockSize]) + off += BlockSize + remain -= BlockSize + } + + times2(delta[0:]) + zeros(tmp[0:]) + num := remain * 8 + tmp[BlockSize-2] = uint8((uint32(num) >> 8) & 0xff) + tmp[BlockSize-1] = uint8(num & 0xff) + xor(tmp[0:], tmp[0:], delta[0:]) + cipher.Encrypt(pad[0:], tmp[0:]) + zeros(tmp[0:]) + copied := copy(tmp[0:remain], encrypted[off:off+remain]) + if copied != remain { + panic("ocb2: copy failed") + } + xor(tmp[0:], tmp[0:], pad[0:]) + xor(checksum[0:], checksum[0:], tmp[0:]) + copied = copy(plain[off:off+remain], tmp[0:remain]) + if copied != remain { + panic("ocb2: copy failed") + } + + times3(delta[0:]) + xor(tmp[0:], delta[0:], checksum[0:]) + cipher.Encrypt(calcTag[0:], tmp[0:]) + + // Compare the calculated tag with the expected tag. Truncate + // the computed tag if necessary. + if subtle.ConstantTimeCompare(calcTag[:len(tag)], tag) != 1 { + return false + } + + return true +} diff --git a/pkg/cryptstate/ocb2/ocb2_test.go b/pkg/cryptstate/ocb2/ocb2_test.go index d72d762..0c9bfcc 100644 --- a/pkg/cryptstate/ocb2/ocb2_test.go +++ b/pkg/cryptstate/ocb2/ocb2_test.go @@ -1,205 +1,201 @@ -// Copyright (c) 2010-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 ocb2
-
-import (
- "bytes"
- "crypto/aes"
- "encoding/hex"
- "testing"
-)
-
-func MustDecodeHex(s string) []byte {
- buf, err := hex.DecodeString(s)
- if err != nil {
- panic("MustDecodeHex: " + err.Error())
- }
- return buf
-}
-
-type ocbVector struct {
- Name string
- Key string
- Nonce string
- Header string
- PlainText string
- CipherText string
- Tag string
-}
-
-func (v ocbVector) KeyBytes() []byte {
- return MustDecodeHex(v.Key)
-}
-
-func (v ocbVector) NonceBytes() []byte {
- return MustDecodeHex(v.Nonce)
-}
-
-func (v ocbVector) PlainTextBytes() []byte {
- return MustDecodeHex(v.PlainText)
-}
-
-func (v ocbVector) CipherTextBytes() []byte {
- return MustDecodeHex(v.CipherText)
-}
-
-func (v ocbVector) TagBytes() []byte {
- return MustDecodeHex(v.Tag)
-}
-
-// ocb128Vectors are the test vectors for OCB-AES128 from
-// http://www.cs.ucdavis.edu/~rogaway/papers/draft-krovetz-ocb-00.txt
-//
-// Note: currently, the vectors with headers are not included in this list
-// as this implementation does not implement header authentication.
-var ocb128Vectors = []ocbVector{
- {
- Name: "OCB2-AES-128-001",
- Key: "000102030405060708090A0B0C0D0E0F",
- Nonce: "000102030405060708090A0B0C0D0E0F",
- PlainText: "",
- CipherText: "",
- Tag: "BF3108130773AD5EC70EC69E7875A7B0",
- },
- {
- Name: "OCB2-AES-128-002",
- Key: "000102030405060708090A0B0C0D0E0F",
- Nonce: "000102030405060708090A0B0C0D0E0F",
- PlainText: "0001020304050607",
- CipherText: "C636B3A868F429BB",
- Tag: "A45F5FDEA5C088D1D7C8BE37CABC8C5C",
- },
- {
- Name: "OCB2-AES-128-003",
- Key: "000102030405060708090A0B0C0D0E0F",
- Nonce: "000102030405060708090A0B0C0D0E0F",
- PlainText: "000102030405060708090A0B0C0D0E0F",
- CipherText: "52E48F5D19FE2D9869F0C4A4B3D2BE57",
- Tag: "F7EE49AE7AA5B5E6645DB6B3966136F9",
- },
- {
- Name: "OCB2-AES-128-003",
- Key: "000102030405060708090A0B0C0D0E0F",
- Nonce: "000102030405060708090A0B0C0D0E0F",
- PlainText: "000102030405060708090A0B0C0D0E0F1011121314151617",
- CipherText: "F75D6BC8B4DC8D66B836A2B08B32A636CC579E145D323BEB",
- Tag: "A1A50F822819D6E0A216784AC24AC84C",
- },
- {
- Name: "OCB2-AES-128-004",
- Key: "000102030405060708090A0B0C0D0E0F",
- Nonce: "000102030405060708090A0B0C0D0E0F",
- PlainText: "000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F",
- CipherText: "F75D6BC8B4DC8D66B836A2B08B32A636CEC3C555037571709DA25E1BB0421A27",
- Tag: "09CA6C73F0B5C6C5FD587122D75F2AA3",
- },
- {
- Name: "OCB2-AES-128-005",
- Key: "000102030405060708090A0B0C0D0E0F",
- Nonce: "000102030405060708090A0B0C0D0E0F",
- PlainText: "000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F2021222324252627",
- CipherText: "F75D6BC8B4DC8D66B836A2B08B32A6369F1CD3C5228D79FD6C267F5F6AA7B231C7DFB9D59951AE9C",
- Tag: "9DB0CDF880F73E3E10D4EB3217766688",
- },
-}
-
-func TestTimes2(t *testing.T) {
- msg := [aes.BlockSize]byte{
- 0x80, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe,
- }
- expected := [aes.BlockSize]byte{
- 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7b,
- }
-
- times2(msg[0:])
- if !bytes.Equal(msg[0:], expected[0:]) {
- t.Fatalf("times2 produces invalid output: %v, expected: %v", msg, expected)
- }
-}
-
-func TestTimes3(t *testing.T) {
- msg := [aes.BlockSize]byte{
- 0x80, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe,
- }
- expected := [aes.BlockSize]byte{
- 0x81, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x85,
- }
-
- times3(msg[0:])
- if !bytes.Equal(msg[0:], expected[0:]) {
- t.Errorf("times3 produces invalid output: %v, expected: %v", msg, expected)
- }
-}
-
-func TestZeros(t *testing.T) {
- var msg [aes.BlockSize]byte
- zeros(msg[0:])
- for i := 0; i < len(msg); i++ {
- if msg[i] != 0 {
- t.Fatalf("zeros does not zero slice.")
- }
- }
-}
-
-func TestXor(t *testing.T) {
- msg := [aes.BlockSize]byte{
- 0x80, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe,
- }
- var out [aes.BlockSize]byte
- xor(out[0:], msg[0:], msg[0:])
- for i := 0; i < len(out); i++ {
- if out[i] != 0 {
- t.Fatalf("XOR broken")
- }
- }
-}
-
-func TestEncryptOCBAES128Vectors(t *testing.T) {
- for _, vector := range ocb128Vectors {
- cipher, err := aes.NewCipher(vector.KeyBytes())
- if err != nil {
- t.Fatalf("%v", err)
- }
-
- plainText := vector.PlainTextBytes()
- cipherText := make([]byte, len(plainText))
- tag := make([]byte, TagSize)
- Encrypt(cipher, cipherText, plainText, vector.NonceBytes(), tag)
-
- expectedCipherText := vector.CipherTextBytes()
- if !bytes.Equal(cipherText, expectedCipherText) {
- t.Fatalf("expected CipherText %#v, got %#v", expectedCipherText, cipherText)
- }
-
- expectedTag := vector.TagBytes()
- if !bytes.Equal(tag, expectedTag) {
- t.Fatalf("expected tag %#v, got %#v", expectedTag, tag)
- }
- }
-}
-
-func TestDecryptOCBAES128Vectors(t *testing.T) {
- for _, vector := range ocb128Vectors {
- cipher, err := aes.NewCipher(vector.KeyBytes())
- if err != nil {
- t.Fatalf("%v", err)
- }
-
- cipherText := vector.CipherTextBytes()
- plainText := make([]byte, len(cipherText))
- tag := make([]byte, TagSize)
- Decrypt(cipher, plainText, cipherText, vector.NonceBytes(), tag)
-
- expectedPlainText := vector.PlainTextBytes()
- if !bytes.Equal(plainText, expectedPlainText) {
- t.Fatalf("expected PlainText %#v, got %#v", expectedPlainText, plainText)
- }
-
- expectedTag := vector.TagBytes()
- if !bytes.Equal(tag, expectedTag) {
- t.Fatalf("expected tag %#v, got %#v", expectedTag, tag)
- }
- }
-}
\ No newline at end of file +// Copyright (c) 2010-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 ocb2 + +import ( + "bytes" + "crypto/aes" + "encoding/hex" + "testing" +) + +func MustDecodeHex(s string) []byte { + buf, err := hex.DecodeString(s) + if err != nil { + panic("MustDecodeHex: " + err.Error()) + } + return buf +} + +type ocbVector struct { + Name string + Key string + Nonce string + Header string + PlainText string + CipherText string + Tag string +} + +func (v ocbVector) KeyBytes() []byte { + return MustDecodeHex(v.Key) +} + +func (v ocbVector) NonceBytes() []byte { + return MustDecodeHex(v.Nonce) +} + +func (v ocbVector) PlainTextBytes() []byte { + return MustDecodeHex(v.PlainText) +} + +func (v ocbVector) CipherTextBytes() []byte { + return MustDecodeHex(v.CipherText) +} + +func (v ocbVector) TagBytes() []byte { + return MustDecodeHex(v.Tag) +} + +// ocb128Vectors are the test vectors for OCB-AES128 from +// http://www.cs.ucdavis.edu/~rogaway/papers/draft-krovetz-ocb-00.txt +// +// Note: currently, the vectors with headers are not included in this list +// as this implementation does not implement header authentication. +var ocb128Vectors = []ocbVector{ + { + Name: "OCB2-AES-128-001", + Key: "000102030405060708090A0B0C0D0E0F", + Nonce: "000102030405060708090A0B0C0D0E0F", + PlainText: "", + CipherText: "", + Tag: "BF3108130773AD5EC70EC69E7875A7B0", + }, + { + Name: "OCB2-AES-128-002", + Key: "000102030405060708090A0B0C0D0E0F", + Nonce: "000102030405060708090A0B0C0D0E0F", + PlainText: "0001020304050607", + CipherText: "C636B3A868F429BB", + Tag: "A45F5FDEA5C088D1D7C8BE37CABC8C5C", + }, + { + Name: "OCB2-AES-128-003", + Key: "000102030405060708090A0B0C0D0E0F", + Nonce: "000102030405060708090A0B0C0D0E0F", + PlainText: "000102030405060708090A0B0C0D0E0F", + CipherText: "52E48F5D19FE2D9869F0C4A4B3D2BE57", + Tag: "F7EE49AE7AA5B5E6645DB6B3966136F9", + }, + { + Name: "OCB2-AES-128-003", + Key: "000102030405060708090A0B0C0D0E0F", + Nonce: "000102030405060708090A0B0C0D0E0F", + PlainText: "000102030405060708090A0B0C0D0E0F1011121314151617", + CipherText: "F75D6BC8B4DC8D66B836A2B08B32A636CC579E145D323BEB", + Tag: "A1A50F822819D6E0A216784AC24AC84C", + }, + { + Name: "OCB2-AES-128-004", + Key: "000102030405060708090A0B0C0D0E0F", + Nonce: "000102030405060708090A0B0C0D0E0F", + PlainText: "000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F", + CipherText: "F75D6BC8B4DC8D66B836A2B08B32A636CEC3C555037571709DA25E1BB0421A27", + Tag: "09CA6C73F0B5C6C5FD587122D75F2AA3", + }, + { + Name: "OCB2-AES-128-005", + Key: "000102030405060708090A0B0C0D0E0F", + Nonce: "000102030405060708090A0B0C0D0E0F", + PlainText: "000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F2021222324252627", + CipherText: "F75D6BC8B4DC8D66B836A2B08B32A6369F1CD3C5228D79FD6C267F5F6AA7B231C7DFB9D59951AE9C", + Tag: "9DB0CDF880F73E3E10D4EB3217766688", + }, +} + +func TestTimes2(t *testing.T) { + msg := [aes.BlockSize]byte{ + 0x80, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, + } + expected := [aes.BlockSize]byte{ + 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7b, + } + + times2(msg[0:]) + if !bytes.Equal(msg[0:], expected[0:]) { + t.Fatalf("times2 produces invalid output: %v, expected: %v", msg, expected) + } +} + +func TestTimes3(t *testing.T) { + msg := [aes.BlockSize]byte{ + 0x80, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, + } + expected := [aes.BlockSize]byte{ + 0x81, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x85, + } + + times3(msg[0:]) + if !bytes.Equal(msg[0:], expected[0:]) { + t.Errorf("times3 produces invalid output: %v, expected: %v", msg, expected) + } +} + +func TestZeros(t *testing.T) { + var msg [aes.BlockSize]byte + zeros(msg[0:]) + for i := 0; i < len(msg); i++ { + if msg[i] != 0 { + t.Fatalf("zeros does not zero slice.") + } + } +} + +func TestXor(t *testing.T) { + msg := [aes.BlockSize]byte{ + 0x80, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, + } + var out [aes.BlockSize]byte + xor(out[0:], msg[0:], msg[0:]) + for i := 0; i < len(out); i++ { + if out[i] != 0 { + t.Fatalf("XOR broken") + } + } +} + +func TestEncryptOCBAES128Vectors(t *testing.T) { + for _, vector := range ocb128Vectors { + cipher, err := aes.NewCipher(vector.KeyBytes()) + if err != nil { + t.Fatalf("%v", err) + } + + plainText := vector.PlainTextBytes() + cipherText := make([]byte, len(plainText)) + tag := make([]byte, TagSize) + Encrypt(cipher, cipherText, plainText, vector.NonceBytes(), tag) + + expectedCipherText := vector.CipherTextBytes() + if !bytes.Equal(cipherText, expectedCipherText) { + t.Fatalf("expected CipherText %#v, got %#v", expectedCipherText, cipherText) + } + + expectedTag := vector.TagBytes() + if !bytes.Equal(tag, expectedTag) { + t.Fatalf("expected tag %#v, got %#v", expectedTag, tag) + } + } +} + +func TestDecryptOCBAES128Vectors(t *testing.T) { + for _, vector := range ocb128Vectors { + cipher, err := aes.NewCipher(vector.KeyBytes()) + if err != nil { + t.Fatalf("%v", err) + } + + cipherText := vector.CipherTextBytes() + plainText := make([]byte, len(cipherText)) + if Decrypt(cipher, plainText, cipherText, vector.NonceBytes(), vector.TagBytes()) == false { + t.Fatalf("expected decrypt success; got failure. tag mismatch?") + } + + expectedPlainText := vector.PlainTextBytes() + if !bytes.Equal(plainText, expectedPlainText) { + t.Fatalf("expected PlainText %#v, got %#v", expectedPlainText, plainText) + } + } +} |