Add Speck-128 implementation
This commit is contained in:
20
cipher/cipher.go
Normal file
20
cipher/cipher.go
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
package cipher
|
||||||
|
|
||||||
|
import "errors"
|
||||||
|
|
||||||
|
var ErrInvalidKeyLength = errors.New("Invalid key length")
|
||||||
|
|
||||||
|
// A Block represents an implementation of a block cipher using block cipher
|
||||||
|
// specific parameters.
|
||||||
|
type Block interface {
|
||||||
|
// Encrypt a source block into the destination. dst and src must be exactly
|
||||||
|
// block sized. Panics if blocks are not sized correctly.
|
||||||
|
Encrypt(dst, src []byte)
|
||||||
|
// Decrypt a source block into the destination. dst and src must be exactly
|
||||||
|
// block sized. Panics if blocks are not sized correctly.
|
||||||
|
Decrypt(dst, src []byte)
|
||||||
|
// BlockSize returns the blocksize in bytes
|
||||||
|
BlockSize() int
|
||||||
|
// Algorithm returns the name of the algorithm
|
||||||
|
Algorithm() string
|
||||||
|
}
|
122
cipher/speck/impl/speck128.go
Normal file
122
cipher/speck/impl/speck128.go
Normal file
@ -0,0 +1,122 @@
|
|||||||
|
// impl implements the Speck algorithm. This implementation should not be used
|
||||||
|
// and instead the parent package should be used. The implementations exposes
|
||||||
|
// all the internal details for testing and analysis.
|
||||||
|
package impl
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/binary"
|
||||||
|
"math/bits"
|
||||||
|
|
||||||
|
"git.omicron.one/playground/cryptography/cipher"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
BlockSize128 = 128 / 8
|
||||||
|
KeySize128128 = 128 / 8
|
||||||
|
KeySize128192 = 192 / 8
|
||||||
|
KeySize128256 = 256 / 8
|
||||||
|
Rounds128128 = 32
|
||||||
|
Rounds128192 = 33
|
||||||
|
Rounds128256 = 34
|
||||||
|
)
|
||||||
|
|
||||||
|
type Speck128 struct {
|
||||||
|
Keys []uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
func New128(key []byte) (*Speck128, error) {
|
||||||
|
var k [4]uint64
|
||||||
|
var m int
|
||||||
|
var rounds int
|
||||||
|
switch len(key) {
|
||||||
|
case KeySize128128:
|
||||||
|
rounds = Rounds128128
|
||||||
|
m = 1
|
||||||
|
k[0] = binary.BigEndian.Uint64(key[:8])
|
||||||
|
k[1] = binary.BigEndian.Uint64(key[8:])
|
||||||
|
case KeySize128192:
|
||||||
|
rounds = Rounds128192
|
||||||
|
m = 2
|
||||||
|
k[0] = binary.BigEndian.Uint64(key[8:])
|
||||||
|
k[1] = binary.BigEndian.Uint64(key[:8])
|
||||||
|
k[2] = binary.BigEndian.Uint64(key[16:24])
|
||||||
|
case KeySize128256:
|
||||||
|
rounds = Rounds128256
|
||||||
|
m = 3
|
||||||
|
// Note that k0, k1 and k2 are l[2], l[1] and l[0] in the spec. The
|
||||||
|
// order is reversed to make the key schedule loop below easy. The same
|
||||||
|
// is true for the 192 bit case above but with 1 less value.
|
||||||
|
k[0] = binary.BigEndian.Uint64(key[16:24])
|
||||||
|
k[1] = binary.BigEndian.Uint64(key[8:])
|
||||||
|
k[2] = binary.BigEndian.Uint64(key[:8])
|
||||||
|
k[3] = binary.BigEndian.Uint64(key[24:])
|
||||||
|
default:
|
||||||
|
return nil, cipher.ErrInvalidKeyLength
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx := &Speck128{
|
||||||
|
Keys: make([]uint64, rounds),
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.Keys[0] = k[m]
|
||||||
|
for i := 0; i < rounds-1; i++ {
|
||||||
|
k[i%m], k[m] = Round128(uint64(i), k[i%m], k[m])
|
||||||
|
ctx.Keys[i+1] = k[m]
|
||||||
|
}
|
||||||
|
return ctx, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func Round128(k, x1, x2 uint64) (uint64, uint64) {
|
||||||
|
x1 = (bits.RotateLeft64(x1, 64-8) + x2) ^ k
|
||||||
|
x2 = bits.RotateLeft64(x2, 3) ^ x1
|
||||||
|
return x1, x2
|
||||||
|
}
|
||||||
|
|
||||||
|
func InverseRound128(k, x1, x2 uint64) (uint64, uint64) {
|
||||||
|
x2 = bits.RotateLeft64(x2^x1, 64-3)
|
||||||
|
x1 = bits.RotateLeft64((x1^k)-x2, 8)
|
||||||
|
return x1, x2
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ctx *Speck128) Encrypt(dst, src []byte) {
|
||||||
|
if len(dst) != BlockSize128 || len(src) != BlockSize128 {
|
||||||
|
panic("Incorrect blocksize, expected 128 bits")
|
||||||
|
}
|
||||||
|
|
||||||
|
x1 := binary.BigEndian.Uint64(src[:8])
|
||||||
|
x2 := binary.BigEndian.Uint64(src[8:])
|
||||||
|
for _, k := range ctx.Keys {
|
||||||
|
x1, x2 = Round128(k, x1, x2)
|
||||||
|
}
|
||||||
|
binary.BigEndian.PutUint64(dst[:8], x1)
|
||||||
|
binary.BigEndian.PutUint64(dst[8:], x2)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ctx *Speck128) Decrypt(dst, src []byte) {
|
||||||
|
if len(dst) != BlockSize128 || len(src) != BlockSize128 {
|
||||||
|
panic("Incorrect blocksize, expected 128 bits")
|
||||||
|
}
|
||||||
|
x1 := binary.BigEndian.Uint64(src[:8])
|
||||||
|
x2 := binary.BigEndian.Uint64(src[8:])
|
||||||
|
for i := len(ctx.Keys) - 1; i >= 0; i-- {
|
||||||
|
x1, x2 = InverseRound128(ctx.Keys[i], x1, x2)
|
||||||
|
}
|
||||||
|
binary.BigEndian.PutUint64(dst[:8], x1)
|
||||||
|
binary.BigEndian.PutUint64(dst[8:], x2)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ctx *Speck128) BlockSize() int {
|
||||||
|
return BlockSize128
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ctx *Speck128) Algorithm() string {
|
||||||
|
switch len(ctx.Keys) {
|
||||||
|
case Rounds128128:
|
||||||
|
return "Speck128/128"
|
||||||
|
case Rounds128192:
|
||||||
|
return "Speck128/192"
|
||||||
|
case Rounds128256:
|
||||||
|
return "Speck126/256"
|
||||||
|
}
|
||||||
|
panic("unreachable")
|
||||||
|
}
|
64
cipher/speck/speck.go
Normal file
64
cipher/speck/speck.go
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
// Package speck implements (parts of) the Speck block cipher as defined in
|
||||||
|
// https://eprint.iacr.org/2013/404.pdf.
|
||||||
|
package speck
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"git.omicron.one/playground/cryptography/cipher"
|
||||||
|
"git.omicron.one/playground/cryptography/cipher/speck/impl"
|
||||||
|
)
|
||||||
|
|
||||||
|
type SpeckParameters int
|
||||||
|
|
||||||
|
const (
|
||||||
|
Speck3264 = iota + 1
|
||||||
|
Speck4872
|
||||||
|
Speck4896
|
||||||
|
Speck6496
|
||||||
|
Speck64128
|
||||||
|
Speck9696
|
||||||
|
Speck96144
|
||||||
|
Speck128128
|
||||||
|
Speck128192
|
||||||
|
Speck128256
|
||||||
|
)
|
||||||
|
|
||||||
|
var keySizes = []int{
|
||||||
|
0, // unused
|
||||||
|
8, // Speck3264
|
||||||
|
9, // Speck4872
|
||||||
|
12, // Speck4896
|
||||||
|
12, // Speck6496
|
||||||
|
16, // Speck64128
|
||||||
|
12, // Speck9696
|
||||||
|
18, // Speck96144
|
||||||
|
16, // Speck128128
|
||||||
|
24, // Speck128192
|
||||||
|
32, // Speck128256
|
||||||
|
}
|
||||||
|
|
||||||
|
// New creates a new speck block cipher context.
|
||||||
|
// Returns the created block cipher or an error.
|
||||||
|
func New(key []byte, param SpeckParameters) (cipher.Block, error) {
|
||||||
|
if param == 0 || int(param) > len(keySizes) {
|
||||||
|
panic("Invalid parameters")
|
||||||
|
}
|
||||||
|
keySize := keySizes[param]
|
||||||
|
if len(key) != keySize {
|
||||||
|
return nil, cipher.ErrInvalidKeyLength
|
||||||
|
}
|
||||||
|
switch param {
|
||||||
|
case Speck3264:
|
||||||
|
return nil, fmt.Errorf("Not implemented")
|
||||||
|
case Speck4872, Speck4896:
|
||||||
|
return nil, fmt.Errorf("Not implemented")
|
||||||
|
case Speck6496, Speck64128:
|
||||||
|
return nil, fmt.Errorf("Not implemented")
|
||||||
|
case Speck9696, Speck96144:
|
||||||
|
return nil, fmt.Errorf("Not implemented")
|
||||||
|
case Speck128128, Speck128192, Speck128256:
|
||||||
|
return impl.New128(key)
|
||||||
|
}
|
||||||
|
panic("unreachable")
|
||||||
|
}
|
75
cipher/speck/speck_test.go
Normal file
75
cipher/speck/speck_test.go
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
package speck_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/hex"
|
||||||
|
"slices"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"git.omicron.one/playground/cryptography/cipher/speck"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func DeHex(s string) []byte {
|
||||||
|
decoded, err := hex.DecodeString(s)
|
||||||
|
if err != nil {
|
||||||
|
panic("invalid hex string")
|
||||||
|
}
|
||||||
|
return decoded
|
||||||
|
}
|
||||||
|
|
||||||
|
type TestVector struct {
|
||||||
|
Key []byte
|
||||||
|
Plaintext []byte
|
||||||
|
Ciphertext []byte
|
||||||
|
Param speck.SpeckParameters
|
||||||
|
}
|
||||||
|
|
||||||
|
var vectors []TestVector = []TestVector{
|
||||||
|
// Speck128/128 test vector
|
||||||
|
{
|
||||||
|
Key: DeHex("0f0e0d0c0b0a09080706050403020100"),
|
||||||
|
Plaintext: DeHex("6c617669757165207469206564616d20"),
|
||||||
|
Ciphertext: DeHex("a65d9851797832657860fedf5c570d18"),
|
||||||
|
Param: speck.Speck128128,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Key: DeHex("17161514131211100f0e0d0c0b0a09080706050403020100"),
|
||||||
|
Plaintext: DeHex("726148206665696843206f7420746e65"),
|
||||||
|
Ciphertext: DeHex("1be4cf3a13135566f9bc185de03c1886"),
|
||||||
|
Param: speck.Speck128192,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Key: DeHex("1f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100"),
|
||||||
|
Plaintext: DeHex("65736f6874206e49202e72656e6f6f70"),
|
||||||
|
Ciphertext: DeHex("4109010405c0f53e4eeeb48d9c188f43"),
|
||||||
|
Param: speck.Speck128256,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestVectors(t *testing.T) {
|
||||||
|
for _, vector := range vectors {
|
||||||
|
ctx, err := speck.New(vector.Key, vector.Param)
|
||||||
|
assert.NotNil(t, ctx)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
|
||||||
|
// Test in place
|
||||||
|
buffer := slices.Clone(vector.Plaintext)
|
||||||
|
ctx.Encrypt(buffer, buffer)
|
||||||
|
assert.Equal(t, vector.Ciphertext, buffer, ctx.Algorithm())
|
||||||
|
ctx.Decrypt(buffer, buffer)
|
||||||
|
assert.Equal(t, vector.Plaintext, buffer, ctx.Algorithm())
|
||||||
|
|
||||||
|
// Test two buffers
|
||||||
|
dst := make([]byte, len(vector.Ciphertext))
|
||||||
|
src := slices.Clone(vector.Plaintext)
|
||||||
|
ctx.Encrypt(dst, src)
|
||||||
|
assert.Equal(t, vector.Plaintext, src, ctx.Algorithm())
|
||||||
|
assert.Equal(t, vector.Ciphertext, dst, ctx.Algorithm())
|
||||||
|
|
||||||
|
dst = make([]byte, len(vector.Plaintext))
|
||||||
|
src = slices.Clone(vector.Ciphertext)
|
||||||
|
ctx.Decrypt(dst, src)
|
||||||
|
assert.Equal(t, vector.Ciphertext, src, ctx.Algorithm())
|
||||||
|
assert.Equal(t, vector.Plaintext, dst, ctx.Algorithm())
|
||||||
|
}
|
||||||
|
}
|
Reference in New Issue
Block a user