Ver Fonte

x/crypto: add support for Tiny Encryption Algorithm (TEA)

TEA is still in use, particularly in old software.

The algorithm is described at,
http://www.cix.co.uk/~klockstone/tea.pdf
http://en.wikipedia.org/wiki/Tiny_Encryption_Algorithm#Reference_code

Reduced-round variations of TEA are annoyingly common, like,
http://daniel.haxx.se/sansa/mi4code.html

Change-Id: I28d7d584d398e3a96371f344624dc60dec75aea3
Reviewed-on: https://go-review.googlesource.com/10825
Reviewed-by: Adam Langley <agl@golang.org>
Dhiru Kholia há 10 anos atrás
pai
commit
e7913d6af1
2 ficheiros alterados com 202 adições e 0 exclusões
  1. 109 0
      tea/cipher.go
  2. 93 0
      tea/tea_test.go

+ 109 - 0
tea/cipher.go

@@ -0,0 +1,109 @@
+// Copyright 2015 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 tea implements the TEA algorithm, as defined in Needham and
+// Wheeler's 1994 technical report, “TEA, a Tiny Encryption Algorithm”. See
+// http://www.cix.co.uk/~klockstone/tea.pdf for details.
+
+package tea
+
+import (
+	"crypto/cipher"
+	"encoding/binary"
+	"errors"
+)
+
+const (
+	// BlockSize is the size of a TEA block, in bytes.
+	BlockSize = 8
+
+	// KeySize is the size of a TEA key, in bytes.
+	KeySize = 16
+
+	// delta is the TEA key schedule constant.
+	delta = 0x9e3779b9
+
+	// numRounds is the standard number of rounds in TEA.
+	numRounds = 64
+)
+
+// tea is an instance of the TEA cipher with a particular key.
+type tea struct {
+	key    [16]byte
+	rounds int
+}
+
+// NewCipher returns an instance of the TEA cipher with the standard number of
+// rounds. The key argument must be 16 bytes long.
+func NewCipher(key []byte) (cipher.Block, error) {
+	return NewCipherWithRounds(key, numRounds)
+}
+
+// NewCipherWithRounds returns an instance of the TEA cipher with a given
+// number of rounds, which must be even. The key argument must be 16 bytes
+// long.
+func NewCipherWithRounds(key []byte, rounds int) (cipher.Block, error) {
+	if len(key) != 16 {
+		return nil, errors.New("tea: incorrect key size")
+	}
+
+	if rounds&1 != 0 {
+		return nil, errors.New("tea: odd number of rounds specified")
+	}
+
+	c := &tea{
+		rounds: rounds,
+	}
+	copy(c.key[:], key)
+
+	return c, nil
+}
+
+// BlockSize returns the TEA block size, which is eight bytes. It is necessary
+// to satisfy the Block interface in the package "crypto/cipher".
+func (*tea) BlockSize() int {
+	return BlockSize
+}
+
+// Encrypt encrypts the 8 byte buffer src using the key in t and stores the
+// result in dst. Note that for amounts of data larger than a block, it is not
+// safe to just call Encrypt on successive blocks; instead, use an encryption
+// mode like CBC (see crypto/cipher/cbc.go).
+func (t *tea) Encrypt(dst, src []byte) {
+	e := binary.BigEndian
+	v0, v1 := e.Uint32(src), e.Uint32(src[4:])
+	k0, k1, k2, k3 := e.Uint32(t.key[0:]), e.Uint32(t.key[4:]), e.Uint32(t.key[8:]), e.Uint32(t.key[12:])
+
+	sum := uint32(0)
+	delta := uint32(delta)
+
+	for i := 0; i < t.rounds/2; i++ {
+		sum += delta
+		v0 += ((v1 << 4) + k0) ^ (v1 + sum) ^ ((v1 >> 5) + k1)
+		v1 += ((v0 << 4) + k2) ^ (v0 + sum) ^ ((v0 >> 5) + k3)
+	}
+
+	e.PutUint32(dst, v0)
+	e.PutUint32(dst[4:], v1)
+}
+
+// Decrypt decrypts the 8 byte buffer src using the key in t and stores the
+// result in dst.
+func (t *tea) Decrypt(dst, src []byte) {
+	e := binary.BigEndian
+	v0, v1 := e.Uint32(src), e.Uint32(src[4:])
+	k0, k1, k2, k3 := e.Uint32(t.key[0:]), e.Uint32(t.key[4:]), e.Uint32(t.key[8:]), e.Uint32(t.key[12:])
+
+	delta := uint32(delta)
+	sum := delta * uint32(t.rounds/2) // in general, sum = delta * n
+
+	for i := 0; i < t.rounds/2; i++ {
+		v1 -= ((v0 << 4) + k2) ^ (v0 + sum) ^ ((v0 >> 5) + k3)
+		v0 -= ((v1 << 4) + k0) ^ (v1 + sum) ^ ((v1 >> 5) + k1)
+		sum -= delta
+	}
+
+	e.PutUint32(dst, v0)
+	e.PutUint32(dst[4:], v1)
+}

+ 93 - 0
tea/tea_test.go

@@ -0,0 +1,93 @@
+// Copyright 2015 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 tea
+
+import (
+	"bytes"
+	"testing"
+)
+
+// A sample test key for when we just want to initialize a cipher
+var testKey = []byte{0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF}
+
+// Test that the block size for tea is correct
+func TestBlocksize(t *testing.T) {
+	c, err := NewCipher(testKey)
+	if err != nil {
+		t.Fatalf("NewCipher returned error: %s", err)
+	}
+
+	if result := c.BlockSize(); result != BlockSize {
+		t.Errorf("cipher.BlockSize returned %d, but expected %d", result, BlockSize)
+	}
+}
+
+// Test that invalid key sizes return an error
+func TestInvalidKeySize(t *testing.T) {
+	var key [KeySize + 1]byte
+
+	if _, err := NewCipher(key[:]); err == nil {
+		t.Errorf("invalid key size %d didn't result in an error.", len(key))
+	}
+
+	if _, err := NewCipher(key[:KeySize-1]); err == nil {
+		t.Errorf("invalid key size %d didn't result in an error.", KeySize-1)
+	}
+}
+
+// Test Vectors
+type teaTest struct {
+	rounds     int
+	key        []byte
+	plaintext  []byte
+	ciphertext []byte
+}
+
+var teaTests = []teaTest{
+	// These were sourced from https://github.com/froydnj/ironclad/blob/master/testing/test-vectors/tea.testvec
+	{
+		numRounds,
+		[]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+		[]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+		[]byte{0x41, 0xea, 0x3a, 0x0a, 0x94, 0xba, 0xa9, 0x40},
+	},
+	{
+		numRounds,
+		[]byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
+		[]byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
+		[]byte{0x31, 0x9b, 0xbe, 0xfb, 0x01, 0x6a, 0xbd, 0xb2},
+	},
+	{
+		16,
+		[]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+		[]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+		[]byte{0xed, 0x28, 0x5d, 0xa1, 0x45, 0x5b, 0x33, 0xc1},
+	},
+}
+
+// Test encryption
+func TestCipherEncrypt(t *testing.T) {
+	// Test encryption with standard 64 rounds
+	for i, test := range teaTests {
+		c, err := NewCipherWithRounds(test.key, test.rounds)
+		if err != nil {
+			t.Fatalf("#%d: NewCipher returned error: %s", i, err)
+		}
+
+		var ciphertext [BlockSize]byte
+		c.Encrypt(ciphertext[:], test.plaintext)
+
+		if !bytes.Equal(ciphertext[:], test.ciphertext) {
+			t.Errorf("#%d: incorrect ciphertext. Got %x, wanted %x", i, ciphertext, test.ciphertext)
+		}
+
+		var plaintext2 [BlockSize]byte
+		c.Decrypt(plaintext2[:], ciphertext[:])
+
+		if !bytes.Equal(plaintext2[:], test.plaintext) {
+			t.Errorf("#%d: incorrect plaintext. Got %x, wanted %x", i, plaintext2, test.plaintext)
+		}
+	}
+}