1// Copyright 2015 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5// Package tea implements the TEA algorithm, as defined in Needham and
6// Wheeler's 1994 technical report, “TEA, a Tiny Encryption Algorithm”. See
7// http://www.cix.co.uk/~klockstone/tea.pdf for details.
8
9package tea
10
11import (
12	"crypto/cipher"
13	"encoding/binary"
14	"errors"
15)
16
17const (
18	// BlockSize is the size of a TEA block, in bytes.
19	BlockSize = 8
20
21	// KeySize is the size of a TEA key, in bytes.
22	KeySize = 16
23
24	// delta is the TEA key schedule constant.
25	delta = 0x9e3779b9
26
27	// numRounds is the standard number of rounds in TEA.
28	numRounds = 64
29)
30
31// tea is an instance of the TEA cipher with a particular key.
32type tea struct {
33	key    [16]byte
34	rounds int
35}
36
37// NewCipher returns an instance of the TEA cipher with the standard number of
38// rounds. The key argument must be 16 bytes long.
39func NewCipher(key []byte) (cipher.Block, error) {
40	return NewCipherWithRounds(key, numRounds)
41}
42
43// NewCipherWithRounds returns an instance of the TEA cipher with a given
44// number of rounds, which must be even. The key argument must be 16 bytes
45// long.
46func NewCipherWithRounds(key []byte, rounds int) (cipher.Block, error) {
47	if len(key) != 16 {
48		return nil, errors.New("tea: incorrect key size")
49	}
50
51	if rounds&1 != 0 {
52		return nil, errors.New("tea: odd number of rounds specified")
53	}
54
55	c := &tea{
56		rounds: rounds,
57	}
58	copy(c.key[:], key)
59
60	return c, nil
61}
62
63// BlockSize returns the TEA block size, which is eight bytes. It is necessary
64// to satisfy the Block interface in the package "crypto/cipher".
65func (*tea) BlockSize() int {
66	return BlockSize
67}
68
69// Encrypt encrypts the 8 byte buffer src using the key in t and stores the
70// result in dst. Note that for amounts of data larger than a block, it is not
71// safe to just call Encrypt on successive blocks; instead, use an encryption
72// mode like CBC (see crypto/cipher/cbc.go).
73func (t *tea) Encrypt(dst, src []byte) {
74	e := binary.BigEndian
75	v0, v1 := e.Uint32(src), e.Uint32(src[4:])
76	k0, k1, k2, k3 := e.Uint32(t.key[0:]), e.Uint32(t.key[4:]), e.Uint32(t.key[8:]), e.Uint32(t.key[12:])
77
78	sum := uint32(0)
79	delta := uint32(delta)
80
81	for i := 0; i < t.rounds/2; i++ {
82		sum += delta
83		v0 += ((v1 << 4) + k0) ^ (v1 + sum) ^ ((v1 >> 5) + k1)
84		v1 += ((v0 << 4) + k2) ^ (v0 + sum) ^ ((v0 >> 5) + k3)
85	}
86
87	e.PutUint32(dst, v0)
88	e.PutUint32(dst[4:], v1)
89}
90
91// Decrypt decrypts the 8 byte buffer src using the key in t and stores the
92// result in dst.
93func (t *tea) Decrypt(dst, src []byte) {
94	e := binary.BigEndian
95	v0, v1 := e.Uint32(src), e.Uint32(src[4:])
96	k0, k1, k2, k3 := e.Uint32(t.key[0:]), e.Uint32(t.key[4:]), e.Uint32(t.key[8:]), e.Uint32(t.key[12:])
97
98	delta := uint32(delta)
99	sum := delta * uint32(t.rounds/2) // in general, sum = delta * n
100
101	for i := 0; i < t.rounds/2; i++ {
102		v1 -= ((v0 << 4) + k2) ^ (v0 + sum) ^ ((v0 >> 5) + k3)
103		v0 -= ((v1 << 4) + k0) ^ (v1 + sum) ^ ((v1 >> 5) + k1)
104		sum -= delta
105	}
106
107	e.PutUint32(dst, v0)
108	e.PutUint32(dst[4:], v1)
109}
110