Simple and efficient encryption algorithm TEA

Cafly
8 min readFeb 14, 2022

--

1. What is TEA encryption algorithm

TEA (Tiny Encryption Algorithm) is a simple and efficient encryption algorithm, known for its fast encryption and decryption speed and simple and efficient implementation.

This article has a lot of code, if you are more concerned about practicality, please check the last chapter directly(Advantages and Use Cases of TEA Encryption Algorithms).

In the field of security, TEA (Tiny Encryption Algorithm) is a block encryption algorithm whose implementation is very simple, usually requiring only a few lines of code. The TEA algorithm was originally developed by David Wheeler and Roger Needham of the Cambridge Computer Laboratory. Designed in 1994.

The TEA algorithm uses 64-bit plaintext blocks and 128-bit keys. It uses the Feistel block encryption framework and requires 64 rounds of iterations, although the author believes that 32 rounds are sufficient. The algorithm uses a mysterious constant δ as a multiple, which is derived from It is based on the golden ratio to ensure that each round of encryption is different. But the exact value of δ does not seem to be important, here TEA defines it as δ=“(√5–1)231” (that is, 0×9E3779B9 in the program ).

1.1 Algorithm principle

A constant value is used for TEA encryption and decryption. This constant value is 0x9e3779b, which is an approximate golden ratio. Note that some programmers avoid directly appearing “mov variable, 0x9e3779b” in the program, so as not to be directly searched by crackers The constant 0x9e3779b is known to use the TEA algorithm, so sometimes “sub variable, 0x61C88647” is used instead of “mov variable, 0x9e3779b”, 0x61C88647=-(0x9e3779b). The TEA algorithm can operate 64bit (8byte) each time, using 128bit (16byte) As the key, the algorithm adopts the form of iteration, the recommended number of iteration rounds is 64 rounds, and the minimum round is 32 rounds.

2. XTEA(TEAN)Algorithm

XTEA — The first version of the successor to the block TEA,

Later, the TEA algorithm was found to be flawed, and in response, the designers proposed an upgraded version of TEA, — -XTEA (sometimes called “tean”). XTEA uses the same simple operations as TEA, but it uses a very different In order to prevent key table attacks, the four subkeys (in the encryption process, the original 128-bit key is split into four 32-bit subkeys) are mixed in a less formal way , but slower.

The third version of TEA, XXTEA, published in 1998, further improves the security of the TEA algorithm.

3. TEA algorithm Golang code

import (
"crypto/cipher"
"encoding/binary"
"errors"
)
const (
// TEA BlockSize byte
BlockSize = 8
// KeySize TEA Key's size.
KeySize = 16
// delta is the key schedule.
delta = 0x9e3779b9
// numRounds round.
numRounds = 64
)
//tea
type tea struct {
key [16]byte
rounds int
}
// Encryption algorithm constructor, using standard rounds, key length must be 16 bytes
func NewCipher(key []byte) (cipher.Block, error) {
return NewCipherWithRounds(key, numRounds)
}
// NewCipherWithRounds Encryption algorithm constructor, the key length must be 16 bytes
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
func (*tea) BlockSize() int {
return BlockSize
}
// Encrypt uses t.key to encrypt the content of the 8byte buffer of the src parameter, and the ciphertext is stored in dst.
// Note that the length of data is greater than the block, it is not safe to call encrypt on consecutive blocks, you should use CBC crypto/cipher/cbc.go to encrypt
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)
}
// Use t.key to decrypt the 8byte buffer content of the src parameter, and save the plaintext 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)
}

4. XTEA Algorithm Golang code

block.go

package xtea// XTEA is based on 64 rounds.
const numRounds = 64
// blockToUint32
func blockToUint32(src []byte) (uint32, uint32) {
r0 := uint32(src[0])<<24 | uint32(src[1])<<16 | uint32(src[2])<<8 | uint32(src[3])
r1 := uint32(src[4])<<24 | uint32(src[5])<<16 | uint32(src[6])<<8 | uint32(src[7])
return r0, r1
}
//uint32ToBlock
func uint32ToBlock(v0, v1 uint32, dst []byte) {
dst[0] = byte(v0 >> 24)
dst[1] = byte(v0 >> 16)
dst[2] = byte(v0 >> 8)
dst[3] = byte(v0)
dst[4] = byte(v1 >> 24)
dst[5] = byte(v1 >> 16)
dst[6] = byte(v1 >> 8)
dst[7] = byte(v1 >> 0)
}
// encryptBlock
func encryptBlock(c *Cipher, dst, src []byte) {
v0, v1 := blockToUint32(src)
// Two rounds of XTEA applied per loop
for i := 0; i < numRounds; {
v0 += ((v1<<4 ^ v1>>5) + v1) ^ c.table[i]
i++
v1 += ((v0<<4 ^ v0>>5) + v0) ^ c.table[i]
i++
}
uint32ToBlock(v0, v1, dst)
}
// decryptBlock
func decryptBlock(c *Cipher, dst, src []byte) {
v0, v1 := blockToUint32(src)
// Two rounds of XTEA applied per loop
for i := numRounds; i > 0; {
i--
v1 -= ((v0<<4 ^ v0>>5) + v0) ^ c.table[i]
i--
v0 -= ((v1<<4 ^ v1>>5) + v1) ^ c.table[i]
}
uint32ToBlock(v0, v1, dst)
}

cipher.go

package xtea import "strconv"// XTEA block size in bytes.
const BlockSize = 8
// A Cipher is an instance of an XTEA cipher using a particular key.
type Cipher struct {
// table contains a series of precalculated values that are used each round.
table [64]uint32
}
// KeySizeError
type KeySizeError int
// Error .
func (k KeySizeError) Error() string {
return "crypto/xtea: invalid key size " + strconv.Itoa(int(k))
}
// The key can only be 16 bytes.
func NewCipher(key []byte) (*Cipher, error) {
k := len(key)
switch k {
default:
return nil, KeySizeError(k)
case 16:
break
}
c := new(Cipher)
initCipher(c, key)
return c, nil
}
//BlockSize
func (c *Cipher) BlockSize() int { return BlockSize }
// Encrypt encrypts the 8byte buffer content of the src parameter, and the plaintext is stored in dst.
func (c *Cipher) Encrypt(dst, src []byte) { encryptBlock(c, dst, src) }
// Decrypt decrypts the 8 byte buffer src using the key and stores the result in dst.
func (c *Cipher) Decrypt(dst, src []byte) { decryptBlock(c, dst, src) }
// initCipher
func initCipher(c *Cipher, key []byte) {
// Load the key into four uint32s
var k [4]uint32
for i := 0; i < len(k); i++ {
j := i << 2 // Multiply by 4
k[i] = uint32(key[j+0])<<24 | uint32(key[j+1])<<16 | uint32(key[j+2])<<8 | uint32(key[j+3])
}
// Precalculate the table
const delta = 0x9E3779B9
var sum uint32
// Two rounds of XTEA applied per loop
for i := 0; i < numRounds; {
c.table[i] = sum + k[sum&3]
i++
sum += delta
c.table[i] = sum + k[(sum>>11)&3]
i++
}
}

5. Advantages and Use Cases of TEA Encryption Algorithms

TEA (Tiny Encryption Algorithm) is a simple and efficient encryption algorithm, known for its fast encryption and decryption speed and simple and efficient implementation. The algorithm is really simple, the TEA algorithm can operate 64-bit (8-byte) each time, using 128- bit (16-byte) is used as the key, and the algorithm adopts the form of iteration. The recommended number of iteration rounds is 64 rounds, with a minimum of 32 rounds.

5.1 basic unit test

import (
"bytes"
"crypto/cipher"
"crypto/rand"
"encoding/base64"
"errors"
"fmt"
"golang.org/x/crypto/tea"
"golang.org/x/crypto/xtea"
"io"
"testing"
)
func TestTeaDemo(t *testing.T) {
key := []byte("mojotv.cn.=.good")//16 bytes
c, err := tea.NewCipherWithRounds(key, 8)
if err != nil {
t.Fatal(err)
}
raw := []byte("mojotvcn")//8 bytes
dst := make([]byte,8)//8bytes
c.Encrypt(dst,raw)
raw2 := make([]byte,8)//8bytes
c.Decrypt(raw2,dst[:])
if !bytes.Equal(raw,raw2){
t.Error("failed")
}
t.Log("Succeed")
}
func TestXteaDemo(t *testing.T) {
key := []byte("mojotv.cn.=.good")//16bytes
c, err := xtea.NewCipher(key)
if err != nil {
t.Fatal(err)
}
raw := []byte("mojotvcn")//8bytes
dst := make([]byte,8)//8bytes
c.Encrypt(dst,raw)
raw2 := make([]byte,8)//8bytes
c.Decrypt(raw2,dst[:])
if !bytes.Equal(raw,raw2){
t.Error("failed")
}
t.Log("xtea succeed")
}

5.2 XTea is used with CBC Base64Url encoding

The TEA encryption algorithm only needs to modify the following code slightly.

//Padding with PKCS7
func pKCS7Padding(ciphertext []byte, blockSize int) []byte {
padding := blockSize - len(ciphertext) % blockSize
padtext := bytes.Repeat([]byte{byte(padding)}, padding)
return append(ciphertext, padtext...)
}
//UnPadding
func pKCS7UnPadding(origData []byte) []byte {
length := len(origData)
unpadding := int(origData[length-1])
return origData[:(length - unpadding)]
}
//XteaCbcEncrypt key 16 bytes
func XteaCbcEncrypt(rawData,key []byte) ([]byte, error) {
block, err := xtea.NewCipher(key)
if err != nil {
return nil,fmt.Errorf("key 只能为16bytes, %v",err)
}
//
blockSize := block.BlockSize()
rawData = pKCS7Padding(rawData, blockSize)
//The initial vector IV must be unique, but does not need to be kept secret
cipherText := make([]byte,blockSize+len(rawData))
//block 16 bytes
iv := cipherText[:blockSize]
if _, err := io.ReadFull(rand.Reader,iv); err != nil {
panic(err)
}
//The block size and the initial vector size must be the same
mode := cipher.NewCBCEncrypter(block,iv)
mode.CryptBlocks(cipherText[blockSize:],rawData)
return cipherText, nil
}
//XteaCbcDecrypt key 16 bytes
func XteaCbcDecrypt(encryptData, key []byte) ([]byte,error) {
block, err := xtea.NewCipher(key)
if err != nil {
return nil,fmt.Errorf("key 16bytes, %v",err)
}
blockSize := block.BlockSize() if len(encryptData) < blockSize {
return nil,errors.New("ciphertext too short")
}
iv := encryptData[:blockSize]
encryptData = encryptData[blockSize:]
// CBC mode always works in whole blocks.
if len(encryptData)%blockSize != 0 {
return nil,errors.New("ciphertext is not a multiple of the block size")
}
mode := cipher.NewCBCDecrypter(block, iv) // CryptBlocks can work in-place if the two arguments are the same.
mode.CryptBlocks(encryptData, encryptData)
encryptData = pKCS7UnPadding(encryptData)
return encryptData,nil
}
func XteaB64urlEncrypt(rawData,key []byte) (string,error) {
data, err:= XteaCbcEncrypt(rawData,key)
if err != nil {
return "",err
}
return base64.RawURLEncoding.EncodeToString(data),nil
}
func XteaB64urlDecrypt(rawData string,key []byte) (string,error) {
data,err := base64.RawURLEncoding.DecodeString(rawData)
if err != nil {
return "",err
}
dnData,err := XteaCbcDecrypt(data,key)
if err != nil {
return "",err
}
return string(dnData),nil
}
func TestXteaCbcB64url(t *testing.T) {
key := []byte("mojotv.cn.=.good")//16bytes
raw := "mojotv.cn and golang are great friends"
ciper,err := XteaB64urlEncrypt([]byte(raw),key)
if err != nil {
t.Error("xtea cbc base64 url failed",err)
return
}
decrypt, err := XteaB64urlDecrypt(ciper, key)
if err != nil {
t.Error("xtea cbc base64 url failed",err)
return
}
if decrypt != raw {
t.Error("Decryption result is incorrect")
}
}

unit test execution result

=== RUN   TestTeaDemo
tea_test.go:32: Success
--- PASS: TestTeaDemo (0.00s)
=== RUN TestXteaDemo
tea_test.go:50: xtea success
--- PASS: TestXteaDemo (0.00s)
=== RUN TestXteaCbcB64url
--- PASS: TestXteaCbcB64url (0.00s)
PASS
ok mojotv.cn/flash 0.005s

The End

--

--