131 lines
3.1 KiB
Go
131 lines
3.1 KiB
Go
// 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
|
|
|
|
// Note that the layout of the k array is slightly different from the spec.
|
|
// Compared to the spec it is laid out like this:
|
|
// k[0] = l_0
|
|
// k[1] = l_1
|
|
// ...
|
|
// k[m] = k_0
|
|
//
|
|
// This allows the key schedule loop to easily access (and overwrite)
|
|
// k[i % m] and k[m].
|
|
|
|
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
|
|
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 "Speck128/256"
|
|
}
|
|
panic("unreachable")
|
|
}
|