summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-16 17:19:37 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-16 17:19:37 +0000
commit72f4e700f3d71b1f6ba93da8db8d8f107a862977 (patch)
tree0931138bacb78b17e12907360bc2982ce526b9b8
parentInitial commit. (diff)
downloadgolang-github-aead-serpent-72f4e700f3d71b1f6ba93da8db8d8f107a862977.tar.xz
golang-github-aead-serpent-72f4e700f3d71b1f6ba93da8db8d8f107a862977.zip
Adding upstream version 0.1.upstream/0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
-rw-r--r--.gitignore25
-rw-r--r--LICENSE21
-rw-r--r--README.md9
-rw-r--r--sbox_ref.go316
-rw-r--r--serpent.go119
-rw-r--r--serpent_ref.go276
-rw-r--r--serpent_test.go296
-rw-r--r--vectors_test.go85
8 files changed, 1147 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..9d3d843
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,25 @@
+# Compiled Object files, Static and Dynamic libs (Shared Objects)
+*.o
+*.a
+*.so
+
+# Folders
+_obj
+_test
+.vscode
+
+# Architecture specific extensions/prefixes
+*.[568vq]
+[568vq].out
+
+*.cgo1.go
+*.cgo2.c
+_cgo_defun.c
+_cgo_gotypes.go
+_cgo_export.*
+
+_testmain.go
+
+*.exe
+*.test
+*.prof
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..b6a9210
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2016 Andreas Auernhammer
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..6dbceee
--- /dev/null
+++ b/README.md
@@ -0,0 +1,9 @@
+[![Godoc Reference](https://godoc.org/github.com/aead/serpent?status.svg)](https://godoc.org/github.com/aead/serpent)
+
+## The Serpent block cipher
+
+Serpent is a symmetric key block cipher that was a finalist in the Advanced Encryption Standard (AES) contest,
+where it was ranked second to Rijndael. Serpent was designed by Ross Anderson, Eli Biham, and Lars Knudsen.
+
+### Installation
+Install in your GOPATH: `go get -u github.com/aead/serpent`
diff --git a/sbox_ref.go b/sbox_ref.go
new file mode 100644
index 0000000..515afc6
--- /dev/null
+++ b/sbox_ref.go
@@ -0,0 +1,316 @@
+// Copyright (c) 2016 Andreas Auernhammer. All rights reserved.
+// Use of this source code is governed by a license that can be
+// found in the LICENSE file.
+
+package serpent
+
+// The linear transformation of serpent
+// This version, tries not to minimize the
+// number of registers, but maximize parallism.
+func linear(v0, v1, v2, v3 *uint32) {
+ t0 := ((*v0 << 13) | (*v0 >> (32 - 13)))
+ t2 := ((*v2 << 3) | (*v2 >> (32 - 3)))
+ t1 := *v1 ^ t0 ^ t2
+ t3 := *v3 ^ t2 ^ (t0 << 3)
+ *v1 = (t1 << 1) | (t1 >> (32 - 1))
+ *v3 = (t3 << 7) | (t3 >> (32 - 7))
+ t0 ^= *v1 ^ *v3
+ t2 ^= *v3 ^ (*v1 << 7)
+ *v0 = (t0 << 5) | (t0 >> (32 - 5))
+ *v2 = (t2 << 22) | (t2 >> (32 - 22))
+}
+
+// The inverse linear transformation of serpent
+// This version, tries not to minimize the
+// number of registers, but maximize parallism.
+func linearInv(v0, v1, v2, v3 *uint32) {
+ t2 := (*v2 >> 22) | (*v2 << (32 - 22))
+ t0 := (*v0 >> 5) | (*v0 << (32 - 5))
+ t2 ^= *v3 ^ (*v1 << 7)
+ t0 ^= *v1 ^ *v3
+ t3 := (*v3 >> 7) | (*v3 << (32 - 7))
+ t1 := (*v1 >> 1) | (*v1 << (32 - 1))
+ *v3 = t3 ^ t2 ^ (t0 << 3)
+ *v1 = t1 ^ t0 ^ t2
+ *v2 = (t2 >> 3) | (t2 << (32 - 3))
+ *v0 = (t0 >> 13) | (t0 << (32 - 13))
+}
+
+// The following functions sb0,sb1, ..., sb7 represent the 8 Serpent S-Boxes.
+// sb0Inv til sb7Inv are the inverse functions (e.g. sb0Inv is the Inverse to sb0
+// and vice versa).
+// The S-Boxes differ from the original Serpent definitions. This is for
+// optimisation. The functions use the Serpent S-Box improvements for (non x86)
+// from Dr. B. R. Gladman and Sam Simpson.
+
+// S-Box 0
+func sb0(r0, r1, r2, r3 *uint32) {
+ t0 := *r0 ^ *r3
+ t1 := *r2 ^ t0
+ t2 := *r1 ^ t1
+ *r3 = (*r0 & *r3) ^ t2
+ t3 := *r0 ^ (*r1 & t0)
+ *r2 = t2 ^ (*r2 | t3)
+ t4 := *r3 & (t1 ^ t3)
+ *r1 = (^t1) ^ t4
+ *r0 = t4 ^ (^t3)
+}
+
+// Inverse S-Box 0
+func sb0Inv(r0, r1, r2, r3 *uint32) {
+ t0 := ^(*r0)
+ t1 := *r0 ^ *r1
+ t2 := *r3 ^ (t0 | t1)
+ t3 := *r2 ^ t2
+ *r2 = t1 ^ t3
+ t4 := t0 ^ (*r3 & t1)
+ *r1 = t2 ^ (*r2 & t4)
+ *r3 = (*r0 & t2) ^ (t3 | *r1)
+ *r0 = *r3 ^ (t3 ^ t4)
+}
+
+// S-Box 1
+func sb1(r0, r1, r2, r3 *uint32) {
+ t0 := *r1 ^ (^(*r0))
+ t1 := *r2 ^ (*r0 | t0)
+ *r2 = *r3 ^ t1
+ t2 := *r1 ^ (*r3 | t0)
+ t3 := t0 ^ *r2
+ *r3 = t3 ^ (t1 & t2)
+ t4 := t1 ^ t2
+ *r1 = *r3 ^ t4
+ *r0 = t1 ^ (t3 & t4)
+}
+
+// Inverse S-Box 1
+func sb1Inv(r0, r1, r2, r3 *uint32) {
+ t0 := *r1 ^ *r3
+ t1 := *r0 ^ (*r1 & t0)
+ t2 := t0 ^ t1
+ *r3 = *r2 ^ t2
+ t3 := *r1 ^ (t0 & t1)
+ t4 := *r3 | t3
+ *r1 = t1 ^ t4
+ t5 := ^(*r1)
+ t6 := *r3 ^ t3
+ *r0 = t5 ^ t6
+ *r2 = t2 ^ (t5 | t6)
+}
+
+// S-Box 2
+func sb2(r0, r1, r2, r3 *uint32) {
+ v0 := *r0 // save r0
+ v3 := *r3 // save r3
+ t0 := ^v0
+ t1 := *r1 ^ v3
+ t2 := *r2 & t0
+ *r0 = t1 ^ t2
+ t3 := *r2 ^ t0
+ t4 := *r2 ^ *r0
+ t5 := *r1 & t4
+ *r3 = t3 ^ t5
+ *r2 = v0 ^ ((v3 | t5) & (*r0 | t3))
+ *r1 = (t1 ^ *r3) ^ (*r2 ^ (v3 | t0))
+}
+
+// Inverse S-Box 2
+func sb2Inv(r0, r1, r2, r3 *uint32) {
+ v0 := *r0 // save r0
+ v3 := *r3 // save r3
+ t0 := *r1 ^ v3
+ t1 := ^t0
+ t2 := v0 ^ *r2
+ t3 := *r2 ^ t0
+ t4 := *r1 & t3
+ *r0 = t2 ^ t4
+ t5 := v0 | t1
+ t6 := v3 ^ t5
+ t7 := t2 | t6
+ *r3 = t0 ^ t7
+ t8 := ^t3
+ t9 := *r0 | *r3
+ *r1 = t8 ^ t9
+ *r2 = (v3 & t8) ^ (t2 ^ t9)
+}
+
+// S-Box 3
+func sb3(r0, r1, r2, r3 *uint32) {
+ v1 := *r1 // save r1
+ v3 := *r3 // save r3
+ t0 := *r0 ^ *r1
+ t1 := *r0 & *r2
+ t2 := *r0 | *r3
+ t3 := *r2 ^ *r3
+ t4 := t0 & t2
+ t5 := t1 | t4
+ *r2 = t3 ^ t5
+ t6 := *r1 ^ t2
+ t7 := t5 ^ t6
+ t8 := t3 & t7
+ *r0 = t0 ^ t8
+ t9 := *r2 & *r0
+ *r1 = t7 ^ t9
+ *r3 = (v1 | v3) ^ (t3 ^ t9)
+}
+
+// Inverse S-Box 3
+func sb3Inv(r0, r1, r2, r3 *uint32) {
+ t0 := *r0 | *r1
+ t1 := *r1 ^ *r2
+ t2 := *r1 & t1
+ t3 := *r0 ^ t2
+ t4 := *r2 ^ t3
+ t5 := *r3 | t3
+ *r0 = t1 ^ t5
+ t6 := t1 | t5
+ t7 := *r3 ^ t6
+ *r2 = t4 ^ t7
+ t8 := t0 ^ t7
+ t9 := *r0 & t8
+ *r3 = t3 ^ t9
+ *r1 = *r3 ^ (*r0 ^ t8)
+}
+
+// S-Box 4
+func sb4(r0, r1, r2, r3 *uint32) {
+ v0 := *r0 // save r0
+ t0 := v0 ^ *r3
+ t1 := *r3 & t0
+ t2 := *r2 ^ t1
+ t3 := *r1 | t2
+ *r3 = t0 ^ t3
+ t4 := ^(*r1)
+ t5 := t0 | t4
+ *r0 = t2 ^ t5
+ t6 := v0 & *r0
+ t7 := t0 ^ t4
+ t8 := t3 & t7
+ *r2 = t6 ^ t8
+ *r1 = (v0 ^ t2) ^ (t7 & *r2)
+}
+
+// Inverse S-Box 4
+func sb4Inv(r0, r1, r2, r3 *uint32) {
+ v3 := *r3 // save r3
+ t0 := *r2 | v3
+ t1 := *r0 & t0
+ t2 := *r1 ^ t1
+ t3 := *r0 & t2
+ t4 := *r2 ^ t3
+ *r1 = v3 ^ t4
+ t5 := ^(*r0)
+ t6 := t4 & *r1
+ *r3 = t2 ^ t6
+ t7 := *r1 | t5
+ t8 := v3 ^ t7
+ *r0 = *r3 ^ t8
+ *r2 = (t2 & t8) ^ (*r1 ^ t5)
+}
+
+// S-Box 5
+func sb5(r0, r1, r2, r3 *uint32) {
+ v1 := *r1 // save r1
+ t0 := ^(*r0)
+ t1 := *r0 ^ v1
+ t2 := *r0 ^ *r3
+ t3 := *r2 ^ t0
+ t4 := t1 | t2
+ *r0 = t3 ^ t4
+ t5 := *r3 & *r0
+ t6 := t1 ^ *r0
+ *r1 = t5 ^ t6
+ t7 := t0 | *r0
+ t8 := t1 | t5
+ t9 := t2 ^ t7
+ *r2 = t8 ^ t9
+ *r3 = (v1 ^ t5) ^ (*r1 & t9)
+}
+
+// Inverse S-Box 5
+func sb5Inv(r0, r1, r2, r3 *uint32) {
+ v0 := *r0 // save r0
+ v1 := *r1 // save r1
+ v3 := *r3 // save r3
+ t0 := ^(*r2)
+ t1 := v1 & t0
+ t2 := v3 ^ t1
+ t3 := v0 & t2
+ t4 := v1 ^ t0
+ *r3 = t3 ^ t4
+ t5 := v1 | *r3
+ t6 := v0 & t5
+ *r1 = t2 ^ t6
+ t7 := v0 | v3
+ t8 := t0 ^ t5
+ *r0 = t7 ^ t8
+ *r2 = (v1 & t7) ^ (t3 | (v0 ^ *r2))
+}
+
+// S-Box 6
+func sb6(r0, r1, r2, r3 *uint32) {
+ t0 := ^(*r0)
+ t1 := *r0 ^ *r3
+ t2 := *r1 ^ t1
+ t3 := t0 | t1
+ t4 := *r2 ^ t3
+ *r1 = *r1 ^ t4
+ t5 := t1 | *r1
+ t6 := *r3 ^ t5
+ t7 := t4 & t6
+ *r2 = t2 ^ t7
+ t8 := t4 ^ t6
+ *r0 = *r2 ^ t8
+ *r3 = (^t4) ^ (t2 & t8)
+}
+
+// Inverse S-Box 6
+func sb6Inv(r0, r1, r2, r3 *uint32) {
+ v1 := *r1 // save r1
+ v3 := *r3 // save r3
+ t0 := ^(*r0)
+ t1 := *r0 ^ v1
+ t2 := *r2 ^ t1
+ t3 := *r2 | t0
+ t4 := v3 ^ t3
+ *r1 = t2 ^ t4
+ t5 := t2 & t4
+ t6 := t1 ^ t5
+ t7 := v1 | t6
+ *r3 = t4 ^ t7
+ t8 := v1 | *r3
+ *r0 = t6 ^ t8
+ *r2 = (v3 & t0) ^ (t2 ^ t8)
+}
+
+// S-Box 7
+func sb7(r0, r1, r2, r3 *uint32) {
+ t0 := *r1 ^ *r2
+ t1 := *r2 & t0
+ t2 := *r3 ^ t1
+ t3 := *r0 ^ t2
+ t4 := *r3 | t0
+ t5 := t3 & t4
+ *r1 = *r1 ^ t5
+ t6 := t2 | *r1
+ t7 := *r0 & t3
+ *r3 = t0 ^ t7
+ t8 := t3 ^ t6
+ t9 := *r3 & t8
+ *r2 = t2 ^ t9
+ *r0 = (^t8) ^ (*r3 & *r2)
+}
+
+// Inverse S-Box 7
+func sb7Inv(r0, r1, r2, r3 *uint32) {
+ v0 := *r0 // save r0
+ v3 := *r3 // save r3
+ t0 := *r2 | (v0 & *r1)
+ t1 := v3 & (v0 | *r1)
+ *r3 = t0 ^ t1
+ t2 := ^v3
+ t3 := *r1 ^ t1
+ t4 := t3 | (*r3 ^ t2)
+ *r1 = v0 ^ t4
+ *r0 = (*r2 ^ t3) ^ (v3 | *r1)
+ *r2 = (t0 ^ *r1) ^ (*r0 ^ (v0 & *r3))
+}
diff --git a/serpent.go b/serpent.go
new file mode 100644
index 0000000..b3fb811
--- /dev/null
+++ b/serpent.go
@@ -0,0 +1,119 @@
+// Copyright (c) 2016 Andreas Auernhammer. All rights reserved.
+// Use of this source code is governed by a license that can be
+// found in the LICENSE file.
+
+// Package serpent implements the Serpent block cipher
+// submitted to the AES challenge. Serpent was designed by
+// Ross Anderson, Eli Biham und Lars Knudsen.
+// The block cipher takes a 128, 192 or 256 bit key and
+// has a block size of 128 bit.
+package serpent // import "github.com/aead/serpent"
+
+import (
+ "crypto/cipher"
+ "errors"
+)
+
+// BlockSize is the serpent block size in bytes.
+const BlockSize = 16
+
+const phi = 0x9e3779b9 // The Serpent phi constant (sqrt(5) - 1) * 2**31
+
+var errKeySize = errors.New("invalid key size")
+
+// NewCipher returns a new cipher.Block implementing the serpent block cipher.
+// The key argument must be 128, 192 or 256 bit (16, 24, 32 byte).
+func NewCipher(key []byte) (cipher.Block, error) {
+ if k := len(key); k != 16 && k != 24 && k != 32 {
+ return nil, errKeySize
+ }
+ s := &subkeys{}
+ s.keySchedule(key)
+ return s, nil
+}
+
+// The 132 32 bit subkeys of serpent
+type subkeys [132]uint32
+
+func (s *subkeys) BlockSize() int { return BlockSize }
+
+func (s *subkeys) Encrypt(dst, src []byte) {
+ if len(src) < BlockSize {
+ panic("src buffer to small")
+ }
+ if len(dst) < BlockSize {
+ panic("dst buffer to small")
+ }
+ encryptBlock(dst, src, s)
+}
+
+func (s *subkeys) Decrypt(dst, src []byte) {
+ if len(src) < BlockSize {
+ panic("src buffer to small")
+ }
+ if len(dst) < BlockSize {
+ panic("dst buffer to small")
+ }
+ decryptBlock(dst, src, s)
+}
+
+// The key schedule of serpent.
+func (s *subkeys) keySchedule(key []byte) {
+ var k [16]uint32
+ j := 0
+ for i := 0; i+4 <= len(key); i += 4 {
+ k[j] = uint32(key[i]) | uint32(key[i+1])<<8 | uint32(key[i+2])<<16 | uint32(key[i+3])<<24
+ j++
+ }
+ if j < 8 {
+ k[j] = 1
+ }
+
+ for i := 8; i < 16; i++ {
+ x := k[i-8] ^ k[i-5] ^ k[i-3] ^ k[i-1] ^ phi ^ uint32(i-8)
+ k[i] = (x << 11) | (x >> 21)
+ s[i-8] = k[i]
+ }
+ for i := 8; i < 132; i++ {
+ x := s[i-8] ^ s[i-5] ^ s[i-3] ^ s[i-1] ^ phi ^ uint32(i)
+ s[i] = (x << 11) | (x >> 21)
+ }
+
+ sb3(&s[0], &s[1], &s[2], &s[3])
+ sb2(&s[4], &s[5], &s[6], &s[7])
+ sb1(&s[8], &s[9], &s[10], &s[11])
+ sb0(&s[12], &s[13], &s[14], &s[15])
+ sb7(&s[16], &s[17], &s[18], &s[19])
+ sb6(&s[20], &s[21], &s[22], &s[23])
+ sb5(&s[24], &s[25], &s[26], &s[27])
+ sb4(&s[28], &s[29], &s[30], &s[31])
+
+ sb3(&s[32], &s[33], &s[34], &s[35])
+ sb2(&s[36], &s[37], &s[38], &s[39])
+ sb1(&s[40], &s[41], &s[42], &s[43])
+ sb0(&s[44], &s[45], &s[46], &s[47])
+ sb7(&s[48], &s[49], &s[50], &s[51])
+ sb6(&s[52], &s[53], &s[54], &s[55])
+ sb5(&s[56], &s[57], &s[58], &s[59])
+ sb4(&s[60], &s[61], &s[62], &s[63])
+
+ sb3(&s[64], &s[65], &s[66], &s[67])
+ sb2(&s[68], &s[69], &s[70], &s[71])
+ sb1(&s[72], &s[73], &s[74], &s[75])
+ sb0(&s[76], &s[77], &s[78], &s[79])
+ sb7(&s[80], &s[81], &s[82], &s[83])
+ sb6(&s[84], &s[85], &s[86], &s[87])
+ sb5(&s[88], &s[89], &s[90], &s[91])
+ sb4(&s[92], &s[93], &s[94], &s[95])
+
+ sb3(&s[96], &s[97], &s[98], &s[99])
+ sb2(&s[100], &s[101], &s[102], &s[103])
+ sb1(&s[104], &s[105], &s[106], &s[107])
+ sb0(&s[108], &s[109], &s[110], &s[111])
+ sb7(&s[112], &s[113], &s[114], &s[115])
+ sb6(&s[116], &s[117], &s[118], &s[119])
+ sb5(&s[120], &s[121], &s[122], &s[123])
+ sb4(&s[124], &s[125], &s[126], &s[127])
+
+ sb3(&s[128], &s[129], &s[130], &s[131])
+}
diff --git a/serpent_ref.go b/serpent_ref.go
new file mode 100644
index 0000000..2d3ff02
--- /dev/null
+++ b/serpent_ref.go
@@ -0,0 +1,276 @@
+// Copyright (c) 2016 Andreas Auernhammer. All rights reserved.
+// Use of this source code is governed by a license that can be
+// found in the LICENSE file.
+
+package serpent
+
+// Encrypts one block with the given 132 sub-keys sk.
+func encryptBlock(dst, src []byte, sk *subkeys) {
+ // Transform the input block to 4 x 32 bit registers
+ r0 := uint32(src[0]) | uint32(src[1])<<8 | uint32(src[2])<<16 | uint32(src[3])<<24
+ r1 := uint32(src[4]) | uint32(src[5])<<8 | uint32(src[6])<<16 | uint32(src[7])<<24
+ r2 := uint32(src[8]) | uint32(src[9])<<8 | uint32(src[10])<<16 | uint32(src[11])<<24
+ r3 := uint32(src[12]) | uint32(src[13])<<8 | uint32(src[14])<<16 | uint32(src[15])<<24
+
+ // Encrypt the block with the 132 sub-keys and 8 S-Boxes
+ r0, r1, r2, r3 = r0^sk[0], r1^sk[1], r2^sk[2], r3^sk[3]
+ sb0(&r0, &r1, &r2, &r3)
+ linear(&r0, &r1, &r2, &r3)
+ r0, r1, r2, r3 = r0^sk[4], r1^sk[5], r2^sk[6], r3^sk[7]
+ sb1(&r0, &r1, &r2, &r3)
+ linear(&r0, &r1, &r2, &r3)
+ r0, r1, r2, r3 = r0^sk[8], r1^sk[9], r2^sk[10], r3^sk[11]
+ sb2(&r0, &r1, &r2, &r3)
+ linear(&r0, &r1, &r2, &r3)
+ r0, r1, r2, r3 = r0^sk[12], r1^sk[13], r2^sk[14], r3^sk[15]
+ sb3(&r0, &r1, &r2, &r3)
+ linear(&r0, &r1, &r2, &r3)
+ r0, r1, r2, r3 = r0^sk[16], r1^sk[17], r2^sk[18], r3^sk[19]
+ sb4(&r0, &r1, &r2, &r3)
+ linear(&r0, &r1, &r2, &r3)
+ r0, r1, r2, r3 = r0^sk[20], r1^sk[21], r2^sk[22], r3^sk[23]
+ sb5(&r0, &r1, &r2, &r3)
+ linear(&r0, &r1, &r2, &r3)
+ r0, r1, r2, r3 = r0^sk[24], r1^sk[25], r2^sk[26], r3^sk[27]
+ sb6(&r0, &r1, &r2, &r3)
+ linear(&r0, &r1, &r2, &r3)
+ r0, r1, r2, r3 = r0^sk[28], r1^sk[29], r2^sk[30], r3^sk[31]
+ sb7(&r0, &r1, &r2, &r3)
+ linear(&r0, &r1, &r2, &r3)
+
+ r0, r1, r2, r3 = r0^sk[32], r1^sk[33], r2^sk[34], r3^sk[35]
+ sb0(&r0, &r1, &r2, &r3)
+ linear(&r0, &r1, &r2, &r3)
+ r0, r1, r2, r3 = r0^sk[36], r1^sk[37], r2^sk[38], r3^sk[39]
+ sb1(&r0, &r1, &r2, &r3)
+ linear(&r0, &r1, &r2, &r3)
+ r0, r1, r2, r3 = r0^sk[40], r1^sk[41], r2^sk[42], r3^sk[43]
+ sb2(&r0, &r1, &r2, &r3)
+ linear(&r0, &r1, &r2, &r3)
+ r0, r1, r2, r3 = r0^sk[44], r1^sk[45], r2^sk[46], r3^sk[47]
+ sb3(&r0, &r1, &r2, &r3)
+ linear(&r0, &r1, &r2, &r3)
+ r0, r1, r2, r3 = r0^sk[48], r1^sk[49], r2^sk[50], r3^sk[51]
+ sb4(&r0, &r1, &r2, &r3)
+ linear(&r0, &r1, &r2, &r3)
+ r0, r1, r2, r3 = r0^sk[52], r1^sk[53], r2^sk[54], r3^sk[55]
+ sb5(&r0, &r1, &r2, &r3)
+ linear(&r0, &r1, &r2, &r3)
+ r0, r1, r2, r3 = r0^sk[56], r1^sk[57], r2^sk[58], r3^sk[59]
+ sb6(&r0, &r1, &r2, &r3)
+ linear(&r0, &r1, &r2, &r3)
+ r0, r1, r2, r3 = r0^sk[60], r1^sk[61], r2^sk[62], r3^sk[63]
+ sb7(&r0, &r1, &r2, &r3)
+ linear(&r0, &r1, &r2, &r3)
+
+ r0, r1, r2, r3 = r0^sk[64], r1^sk[65], r2^sk[66], r3^sk[67]
+ sb0(&r0, &r1, &r2, &r3)
+ linear(&r0, &r1, &r2, &r3)
+ r0, r1, r2, r3 = r0^sk[68], r1^sk[69], r2^sk[70], r3^sk[71]
+ sb1(&r0, &r1, &r2, &r3)
+ linear(&r0, &r1, &r2, &r3)
+ r0, r1, r2, r3 = r0^sk[72], r1^sk[73], r2^sk[74], r3^sk[75]
+ sb2(&r0, &r1, &r2, &r3)
+ linear(&r0, &r1, &r2, &r3)
+ r0, r1, r2, r3 = r0^sk[76], r1^sk[77], r2^sk[78], r3^sk[79]
+ sb3(&r0, &r1, &r2, &r3)
+ linear(&r0, &r1, &r2, &r3)
+ r0, r1, r2, r3 = r0^sk[80], r1^sk[81], r2^sk[82], r3^sk[83]
+ sb4(&r0, &r1, &r2, &r3)
+ linear(&r0, &r1, &r2, &r3)
+ r0, r1, r2, r3 = r0^sk[84], r1^sk[85], r2^sk[86], r3^sk[87]
+ sb5(&r0, &r1, &r2, &r3)
+ linear(&r0, &r1, &r2, &r3)
+ r0, r1, r2, r3 = r0^sk[88], r1^sk[89], r2^sk[90], r3^sk[91]
+ sb6(&r0, &r1, &r2, &r3)
+ linear(&r0, &r1, &r2, &r3)
+ r0, r1, r2, r3 = r0^sk[92], r1^sk[93], r2^sk[94], r3^sk[95]
+ sb7(&r0, &r1, &r2, &r3)
+ linear(&r0, &r1, &r2, &r3)
+
+ r0, r1, r2, r3 = r0^sk[96], r1^sk[97], r2^sk[98], r3^sk[99]
+ sb0(&r0, &r1, &r2, &r3)
+ linear(&r0, &r1, &r2, &r3)
+ r0, r1, r2, r3 = r0^sk[100], r1^sk[101], r2^sk[102], r3^sk[103]
+ sb1(&r0, &r1, &r2, &r3)
+ linear(&r0, &r1, &r2, &r3)
+ r0, r1, r2, r3 = r0^sk[104], r1^sk[105], r2^sk[106], r3^sk[107]
+ sb2(&r0, &r1, &r2, &r3)
+ linear(&r0, &r1, &r2, &r3)
+ r0, r1, r2, r3 = r0^sk[108], r1^sk[109], r2^sk[110], r3^sk[111]
+ sb3(&r0, &r1, &r2, &r3)
+ linear(&r0, &r1, &r2, &r3)
+ r0, r1, r2, r3 = r0^sk[112], r1^sk[113], r2^sk[114], r3^sk[115]
+ sb4(&r0, &r1, &r2, &r3)
+ linear(&r0, &r1, &r2, &r3)
+ r0, r1, r2, r3 = r0^sk[116], r1^sk[117], r2^sk[118], r3^sk[119]
+ sb5(&r0, &r1, &r2, &r3)
+ linear(&r0, &r1, &r2, &r3)
+ r0, r1, r2, r3 = r0^sk[120], r1^sk[121], r2^sk[122], r3^sk[123]
+ sb6(&r0, &r1, &r2, &r3)
+ linear(&r0, &r1, &r2, &r3)
+ r0, r1, r2, r3 = r0^sk[124], r1^sk[125], r2^sk[126], r3^sk[127]
+ sb7(&r0, &r1, &r2, &r3)
+
+ // whitening
+ r0 ^= sk[128]
+ r1 ^= sk[129]
+ r2 ^= sk[130]
+ r3 ^= sk[131]
+
+ // write the encrypted block to the output
+
+ dst[0] = byte(r0)
+ dst[1] = byte(r0 >> 8)
+ dst[2] = byte(r0 >> 16)
+ dst[3] = byte(r0 >> 24)
+ dst[4] = byte(r1)
+ dst[5] = byte(r1 >> 8)
+ dst[6] = byte(r1 >> 16)
+ dst[7] = byte(r1 >> 24)
+ dst[8] = byte(r2)
+ dst[9] = byte(r2 >> 8)
+ dst[10] = byte(r2 >> 16)
+ dst[11] = byte(r2 >> 24)
+ dst[12] = byte(r3)
+ dst[13] = byte(r3 >> 8)
+ dst[14] = byte(r3 >> 16)
+ dst[15] = byte(r3 >> 24)
+}
+
+// Decrypts one block with the given 132 sub-keys sk.
+func decryptBlock(dst, src []byte, sk *subkeys) {
+ // Transform the input block to 4 x 32 bit registers
+ r0 := uint32(src[0]) | uint32(src[1])<<8 | uint32(src[2])<<16 | uint32(src[3])<<24
+ r1 := uint32(src[4]) | uint32(src[5])<<8 | uint32(src[6])<<16 | uint32(src[7])<<24
+ r2 := uint32(src[8]) | uint32(src[9])<<8 | uint32(src[10])<<16 | uint32(src[11])<<24
+ r3 := uint32(src[12]) | uint32(src[13])<<8 | uint32(src[14])<<16 | uint32(src[15])<<24
+
+ // undo whitening
+ r0 ^= sk[128]
+ r1 ^= sk[129]
+ r2 ^= sk[130]
+ r3 ^= sk[131]
+
+ // Decrypt the block with the 132 sub-keys and 8 S-Boxes
+ sb7Inv(&r0, &r1, &r2, &r3)
+ r0, r1, r2, r3 = r0^sk[124], r1^sk[125], r2^sk[126], r3^sk[127]
+ linearInv(&r0, &r1, &r2, &r3)
+ sb6Inv(&r0, &r1, &r2, &r3)
+ r0, r1, r2, r3 = r0^sk[120], r1^sk[121], r2^sk[122], r3^sk[123]
+ linearInv(&r0, &r1, &r2, &r3)
+ sb5Inv(&r0, &r1, &r2, &r3)
+ r0, r1, r2, r3 = r0^sk[116], r1^sk[117], r2^sk[118], r3^sk[119]
+ linearInv(&r0, &r1, &r2, &r3)
+ sb4Inv(&r0, &r1, &r2, &r3)
+ r0, r1, r2, r3 = r0^sk[112], r1^sk[113], r2^sk[114], r3^sk[115]
+ linearInv(&r0, &r1, &r2, &r3)
+ sb3Inv(&r0, &r1, &r2, &r3)
+ r0, r1, r2, r3 = r0^sk[108], r1^sk[109], r2^sk[110], r3^sk[111]
+ linearInv(&r0, &r1, &r2, &r3)
+ sb2Inv(&r0, &r1, &r2, &r3)
+ r0, r1, r2, r3 = r0^sk[104], r1^sk[105], r2^sk[106], r3^sk[107]
+ linearInv(&r0, &r1, &r2, &r3)
+ sb1Inv(&r0, &r1, &r2, &r3)
+ r0, r1, r2, r3 = r0^sk[100], r1^sk[101], r2^sk[102], r3^sk[103]
+ linearInv(&r0, &r1, &r2, &r3)
+ sb0Inv(&r0, &r1, &r2, &r3)
+ r0, r1, r2, r3 = r0^sk[96], r1^sk[97], r2^sk[98], r3^sk[99]
+ linearInv(&r0, &r1, &r2, &r3)
+
+ sb7Inv(&r0, &r1, &r2, &r3)
+ r0, r1, r2, r3 = r0^sk[92], r1^sk[93], r2^sk[94], r3^sk[95]
+ linearInv(&r0, &r1, &r2, &r3)
+ sb6Inv(&r0, &r1, &r2, &r3)
+ r0, r1, r2, r3 = r0^sk[88], r1^sk[89], r2^sk[90], r3^sk[91]
+ linearInv(&r0, &r1, &r2, &r3)
+ sb5Inv(&r0, &r1, &r2, &r3)
+ r0, r1, r2, r3 = r0^sk[84], r1^sk[85], r2^sk[86], r3^sk[87]
+ linearInv(&r0, &r1, &r2, &r3)
+ sb4Inv(&r0, &r1, &r2, &r3)
+ r0, r1, r2, r3 = r0^sk[80], r1^sk[81], r2^sk[82], r3^sk[83]
+ linearInv(&r0, &r1, &r2, &r3)
+ sb3Inv(&r0, &r1, &r2, &r3)
+ r0, r1, r2, r3 = r0^sk[76], r1^sk[77], r2^sk[78], r3^sk[79]
+ linearInv(&r0, &r1, &r2, &r3)
+ sb2Inv(&r0, &r1, &r2, &r3)
+ r0, r1, r2, r3 = r0^sk[72], r1^sk[73], r2^sk[74], r3^sk[75]
+ linearInv(&r0, &r1, &r2, &r3)
+ sb1Inv(&r0, &r1, &r2, &r3)
+ r0, r1, r2, r3 = r0^sk[68], r1^sk[69], r2^sk[70], r3^sk[71]
+ linearInv(&r0, &r1, &r2, &r3)
+ sb0Inv(&r0, &r1, &r2, &r3)
+ r0, r1, r2, r3 = r0^sk[64], r1^sk[65], r2^sk[66], r3^sk[67]
+ linearInv(&r0, &r1, &r2, &r3)
+
+ sb7Inv(&r0, &r1, &r2, &r3)
+ r0, r1, r2, r3 = r0^sk[60], r1^sk[61], r2^sk[62], r3^sk[63]
+ linearInv(&r0, &r1, &r2, &r3)
+ sb6Inv(&r0, &r1, &r2, &r3)
+ r0, r1, r2, r3 = r0^sk[56], r1^sk[57], r2^sk[58], r3^sk[59]
+ linearInv(&r0, &r1, &r2, &r3)
+ sb5Inv(&r0, &r1, &r2, &r3)
+ r0, r1, r2, r3 = r0^sk[52], r1^sk[53], r2^sk[54], r3^sk[55]
+ linearInv(&r0, &r1, &r2, &r3)
+ sb4Inv(&r0, &r1, &r2, &r3)
+ r0, r1, r2, r3 = r0^sk[48], r1^sk[49], r2^sk[50], r3^sk[51]
+ linearInv(&r0, &r1, &r2, &r3)
+ sb3Inv(&r0, &r1, &r2, &r3)
+ r0, r1, r2, r3 = r0^sk[44], r1^sk[45], r2^sk[46], r3^sk[47]
+ linearInv(&r0, &r1, &r2, &r3)
+ sb2Inv(&r0, &r1, &r2, &r3)
+ r0, r1, r2, r3 = r0^sk[40], r1^sk[41], r2^sk[42], r3^sk[43]
+ linearInv(&r0, &r1, &r2, &r3)
+ sb1Inv(&r0, &r1, &r2, &r3)
+ r0, r1, r2, r3 = r0^sk[36], r1^sk[37], r2^sk[38], r3^sk[39]
+ linearInv(&r0, &r1, &r2, &r3)
+ sb0Inv(&r0, &r1, &r2, &r3)
+ r0, r1, r2, r3 = r0^sk[32], r1^sk[33], r2^sk[34], r3^sk[35]
+ linearInv(&r0, &r1, &r2, &r3)
+
+ sb7Inv(&r0, &r1, &r2, &r3)
+ r0, r1, r2, r3 = r0^sk[28], r1^sk[29], r2^sk[30], r3^sk[31]
+ linearInv(&r0, &r1, &r2, &r3)
+ sb6Inv(&r0, &r1, &r2, &r3)
+ r0, r1, r2, r3 = r0^sk[24], r1^sk[25], r2^sk[26], r3^sk[27]
+ linearInv(&r0, &r1, &r2, &r3)
+ sb5Inv(&r0, &r1, &r2, &r3)
+ r0, r1, r2, r3 = r0^sk[20], r1^sk[21], r2^sk[22], r3^sk[23]
+ linearInv(&r0, &r1, &r2, &r3)
+ sb4Inv(&r0, &r1, &r2, &r3)
+ r0, r1, r2, r3 = r0^sk[16], r1^sk[17], r2^sk[18], r3^sk[19]
+ linearInv(&r0, &r1, &r2, &r3)
+ sb3Inv(&r0, &r1, &r2, &r3)
+ r0, r1, r2, r3 = r0^sk[12], r1^sk[13], r2^sk[14], r3^sk[15]
+ linearInv(&r0, &r1, &r2, &r3)
+ sb2Inv(&r0, &r1, &r2, &r3)
+ r0, r1, r2, r3 = r0^sk[8], r1^sk[9], r2^sk[10], r3^sk[11]
+ linearInv(&r0, &r1, &r2, &r3)
+ sb1Inv(&r0, &r1, &r2, &r3)
+ r0, r1, r2, r3 = r0^sk[4], r1^sk[5], r2^sk[6], r3^sk[7]
+ linearInv(&r0, &r1, &r2, &r3)
+ sb0Inv(&r0, &r1, &r2, &r3)
+
+ r0 ^= sk[0]
+ r1 ^= sk[1]
+ r2 ^= sk[2]
+ r3 ^= sk[3]
+
+ // write the decrypted block to the output
+ dst[0] = byte(r0)
+ dst[1] = byte(r0 >> 8)
+ dst[2] = byte(r0 >> 16)
+ dst[3] = byte(r0 >> 24)
+ dst[4] = byte(r1)
+ dst[5] = byte(r1 >> 8)
+ dst[6] = byte(r1 >> 16)
+ dst[7] = byte(r1 >> 24)
+ dst[8] = byte(r2)
+ dst[9] = byte(r2 >> 8)
+ dst[10] = byte(r2 >> 16)
+ dst[11] = byte(r2 >> 24)
+ dst[12] = byte(r3)
+ dst[13] = byte(r3 >> 8)
+ dst[14] = byte(r3 >> 16)
+ dst[15] = byte(r3 >> 24)
+}
diff --git a/serpent_test.go b/serpent_test.go
new file mode 100644
index 0000000..919e4df
--- /dev/null
+++ b/serpent_test.go
@@ -0,0 +1,296 @@
+// Copyright (c) 2016 Andreas Auernhammer. All rights reserved.
+// Use of this source code is governed by a license that can be
+// found in the LICENSE file.
+
+package serpent
+
+import (
+ "bytes"
+ "crypto/cipher"
+ "encoding/hex"
+ "testing"
+)
+
+var recoverFail = func(t *testing.T) {
+ if err := recover(); err == nil {
+ t.Fatal("Recover expected error, but no one occured")
+ }
+}
+
+var badKeys = [][]byte{
+ make([]byte, 15),
+ make([]byte, 17),
+ make([]byte, 23),
+ make([]byte, 25),
+ make([]byte, 31),
+ make([]byte, 33),
+}
+
+func TestEncrypt(t *testing.T) {
+ encFail := func(t *testing.T, c cipher.Block, srcLen, dstLen int) {
+ defer recoverFail(t)
+ src := make([]byte, srcLen)
+ dst := make([]byte, dstLen)
+ c.Encrypt(dst, src)
+ }
+
+ c, err := NewCipher(make([]byte, 16))
+ if err != nil {
+ t.Fatalf("Failed to create serpent cipher: %s", err)
+ }
+ encFail(t, c, BlockSize-1, BlockSize)
+ encFail(t, c, BlockSize, BlockSize-1)
+}
+
+func TestDecrypt(t *testing.T) {
+ decFail := func(t *testing.T, c cipher.Block, srcLen, dstLen int) {
+ defer recoverFail(t)
+ src := make([]byte, srcLen)
+ dst := make([]byte, dstLen)
+ c.Decrypt(dst, src)
+ }
+
+ c, err := NewCipher(make([]byte, 16))
+ if err != nil {
+ t.Fatalf("Failed to create serpent cipher: %s", err)
+ }
+ decFail(t, c, BlockSize-1, BlockSize)
+ decFail(t, c, BlockSize, BlockSize-1)
+}
+
+func TestEncryptDecrypt(t *testing.T) {
+ c, err := NewCipher(make([]byte, 16))
+ if err != nil {
+ t.Fatalf("Failed to create serpent cipher: %s", err)
+ }
+
+ src := make([]byte, 32)
+ dst := make([]byte, 32)
+
+ c.Encrypt(dst, src)
+ c.Encrypt(dst[16:], src[:16])
+ c.Decrypt(dst, dst)
+ c.Decrypt(dst[16:], dst[16:])
+
+ if !bytes.Equal(src, dst) {
+ t.Fatalf("En / decryption sequence failed\nFound: %s\nExpected: %s", hex.EncodeToString(dst), hex.EncodeToString(src))
+ }
+}
+
+func TestNewCipher(t *testing.T) {
+ var (
+ key128 [16]byte
+ key192 [24]byte
+ key256 [32]byte
+ )
+ _, err := NewCipher(key128[:])
+ if err != nil {
+ t.Fatalf("NewCipher rejected valid key with length: %d", len(key128))
+ }
+ _, err = NewCipher(key192[:])
+ if err != nil {
+ t.Fatalf("NewCipher rejected valid key with length: %d", len(key192))
+ }
+ _, err = NewCipher(key256[:])
+ if err != nil {
+ t.Fatalf("NewCipher rejected valid key with length: %d", len(key256))
+ }
+
+ for i, v := range badKeys {
+ _, err := NewCipher(v)
+ if err == nil {
+ t.Fatalf("NewCipher accpeted bad key %d with length: %d", i, len(v))
+ }
+ }
+}
+
+func TestBlockSize(t *testing.T) {
+ s := new(subkeys)
+ if bs := s.BlockSize(); bs != BlockSize {
+ t.Fatalf("BlockSize() returned unexpected value: %d", bs)
+ }
+}
+
+// Tests the S-Box 0 and its inverse.
+func TestSBox0(t *testing.T) {
+ v0, v1, v2, v3 := uint32(0), uint32(0), uint32(0), uint32(0)
+ for i := 0; i < 16; i++ {
+ v0, v1, v2, v3 = v3+v0+uint32(i), v0+v1, v1+v2, v2+v3
+
+ i0, i1, i2, i3 := v0, v1, v2, v3
+ sb0(&v0, &v1, &v2, &v3)
+ sb0Inv(&v0, &v1, &v2, &v3)
+
+ if i0 != v0 || i1 != v1 || i2 != v2 || i3 != v3 {
+ t.Fatal("Sbox 0 failed")
+ }
+ }
+}
+
+// Tests the S-Box 1 and its inverse.
+func TestSBox1(t *testing.T) {
+ v0, v1, v2, v3 := uint32(0), uint32(0), uint32(0), uint32(0)
+ for i := 0; i < 16; i++ {
+ v0, v1, v2, v3 = v3+v0+uint32(i), v0+v1, v1+v2, v2+v3
+
+ i0, i1, i2, i3 := v0, v1, v2, v3
+ sb1(&v0, &v1, &v2, &v3)
+ sb1Inv(&v0, &v1, &v2, &v3)
+
+ if i0 != v0 || i1 != v1 || i2 != v2 || i3 != v3 {
+ t.Fatal("sbox 1 failed")
+ }
+ }
+}
+
+// Tests the S-Box 2 and its inverse.
+func TestSBox2(t *testing.T) {
+ v0, v1, v2, v3 := uint32(0), uint32(0), uint32(0), uint32(0)
+ for i := 0; i < 16; i++ {
+ v0, v1, v2, v3 = v3+v0+uint32(i), v0+v1, v1+v2, v2+v3
+
+ i0, i1, i2, i3 := v0, v1, v2, v3
+ sb2(&v0, &v1, &v2, &v3)
+ sb2Inv(&v0, &v1, &v2, &v3)
+
+ if i0 != v0 || i1 != v1 || i2 != v2 || i3 != v3 {
+ t.Fatal("sbox 2 failed")
+ }
+ }
+}
+
+// Tests the S-Box 3 and its inverse.
+func TestSBox3(t *testing.T) {
+ v0, v1, v2, v3 := uint32(0), uint32(0), uint32(0), uint32(0)
+ for i := 0; i < 16; i++ {
+ v0, v1, v2, v3 = v3+v0+uint32(i), v0+v1, v1+v2, v2+v3
+
+ i0, i1, i2, i3 := v0, v1, v2, v3
+ sb3(&v0, &v1, &v2, &v3)
+ sb3Inv(&v0, &v1, &v2, &v3)
+
+ if i0 != v0 || i1 != v1 || i2 != v2 || i3 != v3 {
+ t.Fatal("sbox 3 failed")
+ }
+ }
+}
+
+// Tests the S-Box 4 and its inverse.
+func TestSBox4(t *testing.T) {
+ v0, v1, v2, v3 := uint32(0), uint32(0), uint32(0), uint32(0)
+ for i := 0; i < 16; i++ {
+ v0, v1, v2, v3 = v3+v0+uint32(i), v0+v1, v1+v2, v2+v3
+
+ i0, i1, i2, i3 := v0, v1, v2, v3
+ sb4(&v0, &v1, &v2, &v3)
+ sb4Inv(&v0, &v1, &v2, &v3)
+
+ if i0 != v0 || i1 != v1 || i2 != v2 || i3 != v3 {
+ t.Fatal("sbox 4 failed")
+ }
+ }
+}
+
+// Tests the S-Box 5 and its inverse.
+func TestSBox5(t *testing.T) {
+ v0, v1, v2, v3 := uint32(0), uint32(0), uint32(0), uint32(0)
+ for i := 0; i < 16; i++ {
+ v0, v1, v2, v3 = v3+v0+uint32(i), v0+v1, v1+v2, v2+v3
+
+ i0, i1, i2, i3 := v0, v1, v2, v3
+ sb5(&v0, &v1, &v2, &v3)
+ sb5Inv(&v0, &v1, &v2, &v3)
+
+ if i0 != v0 || i1 != v1 || i2 != v2 || i3 != v3 {
+ t.Fatal("sbox 5 failed")
+ }
+ }
+}
+
+// Tests the S-Box 6 and its inverse.
+func TestSBox6(t *testing.T) {
+ v0, v1, v2, v3 := uint32(0), uint32(0), uint32(0), uint32(0)
+ for i := 0; i < 16; i++ {
+ v0, v1, v2, v3 = v3+v0+uint32(i), v0+v1, v1+v2, v2+v3
+
+ i0, i1, i2, i3 := v0, v1, v2, v3
+ sb6(&v0, &v1, &v2, &v3)
+ sb6Inv(&v0, &v1, &v2, &v3)
+
+ if i0 != v0 || i1 != v1 || i2 != v2 || i3 != v3 {
+ t.Fatal("sbox 6 failed")
+ }
+ }
+}
+
+// Tests the S-Box 7 and its inverse.
+func TestSBox7(t *testing.T) {
+ v0, v1, v2, v3 := uint32(0), uint32(0), uint32(0), uint32(0)
+ for i := 0; i < 16; i++ {
+ v0, v1, v2, v3 = v3+v0+uint32(i), v0+v1, v1+v2, v2+v3
+
+ i0, i1, i2, i3 := v0, v1, v2, v3
+ sb7(&v0, &v1, &v2, &v3)
+ sb7Inv(&v0, &v1, &v2, &v3)
+
+ if i0 != v0 || i1 != v1 || i2 != v2 || i3 != v3 {
+ t.Fatal("sbox 7 failed")
+ }
+ }
+}
+
+// Tests the linear transformation and its inverse
+func TestLinear(t *testing.T) {
+ v0, v1, v2, v3 := uint32(0), uint32(0), uint32(0), uint32(0)
+ for i := 0; i < 16; i++ {
+ v0, v1, v2, v3 = v3+v0+uint32(i), v0+v1, v1+v2, v2+v3
+ i0, i1, i2, i3 := v0, v1, v2, v3
+ linear(&v0, &v1, &v2, &v3)
+ linearInv(&v0, &v1, &v2, &v3)
+
+ if i0 != v0 || i1 != v1 || i2 != v2 || i3 != v3 {
+ t.Fatal("linear function failed")
+ }
+ }
+}
+
+// Benchmarks
+
+func BenchmarkEncrypt_16(b *testing.B) { benchmarkEncrypt(b, 16) }
+func BenchmarkDecrypt_16(b *testing.B) { benchmarkDecrypt(b, 16) }
+func BenchmarkEncrypt_1K(b *testing.B) { benchmarkEncrypt(b, 1024) }
+func BenchmarkDecrypt_1K(b *testing.B) { benchmarkDecrypt(b, 1024) }
+
+func benchmarkEncrypt(b *testing.B, size int) {
+ c, err := NewCipher(make([]byte, 16))
+ if err != nil {
+ b.Fatalf("Failed to create Serpent instance: %s", err)
+ }
+ buf := make([]byte, c.BlockSize())
+ b.SetBytes(int64(size - (size % c.BlockSize())))
+
+ n := size / c.BlockSize()
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ for j := 0; j < n; j++ {
+ c.Encrypt(buf, buf)
+ }
+ }
+}
+
+func benchmarkDecrypt(b *testing.B, size int) {
+ c, err := NewCipher(make([]byte, 16))
+ if err != nil {
+ b.Fatalf("Failed to create Serpent instance: %s", err)
+ }
+ buf := make([]byte, c.BlockSize())
+ b.SetBytes(int64(size - (size % c.BlockSize())))
+
+ n := size / c.BlockSize()
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ for j := 0; j < n; j++ {
+ c.Decrypt(buf, buf)
+ }
+ }
+}
diff --git a/vectors_test.go b/vectors_test.go
new file mode 100644
index 0000000..bb1db26
--- /dev/null
+++ b/vectors_test.go
@@ -0,0 +1,85 @@
+// Copyright (c) 2016 Andreas Auernhammer. All rights reserved.
+// Use of this source code is governed by a license that can be
+// found in the LICENSE file.
+
+package serpent
+
+import (
+ "bytes"
+ "encoding/hex"
+ "testing"
+)
+
+func fromHex(s string) []byte {
+ b, err := hex.DecodeString(s)
+ if err != nil {
+ panic(err)
+ }
+ return b
+}
+
+// Test vectors for serpent
+var vectors = []struct {
+ key, plaintext, ciphertext string
+}{
+ // test vectors for 128 bit key from
+ // http://www.cs.technion.ac.il/~biham/Reports/Serpent/Serpent-128-128.verified.test-vectors
+ { // Set 1, vector# 0
+ key: "80000000000000000000000000000000",
+ plaintext: "00000000000000000000000000000000",
+ ciphertext: "264E5481EFF42A4606ABDA06C0BFDA3D",
+ },
+ { // Set 1, vector# 1
+ key: "40000000000000000000000000000000",
+ plaintext: "00000000000000000000000000000000",
+ ciphertext: "4A231B3BC727993407AC6EC8350E8524",
+ },
+ // test vectors for 192 bit key from
+ // http://www.cs.technion.ac.il/~biham/Reports/Serpent/Serpent-192-128.verified.test-vectors
+ { // Set 1, vector# 0
+ key: "800000000000000000000000000000000000000000000000",
+ plaintext: "00000000000000000000000000000000",
+ ciphertext: "9E274EAD9B737BB21EFCFCA548602689",
+ },
+ { // Set 1, vector# 3
+ key: "100000000000000000000000000000000000000000000000",
+ plaintext: "00000000000000000000000000000000",
+ ciphertext: "BEC1E37824CF721E5D87F6CB4EBFB9BE",
+ },
+ // test vectors for 256 bit key from
+ // http://www.cs.technion.ac.il/~biham/Reports/Serpent/Serpent-256-128.verified.test-vectors
+ { // Set 3, vector# 1
+ key: "0101010101010101010101010101010101010101010101010101010101010101",
+ plaintext: "01010101010101010101010101010101",
+ ciphertext: "EC9723B15B2A6489F84C4524FFFC2748",
+ },
+ { // Set 3, vector# 2
+ key: "0202020202020202020202020202020202020202020202020202020202020202",
+ plaintext: "02020202020202020202020202020202",
+ ciphertext: "1187F485538514476184E567DA0421C7",
+ },
+}
+
+// Tests all serpent test vectors.
+func TestVectors(t *testing.T) {
+ for i, v := range vectors {
+ key := fromHex(v.key)
+ plaintext := fromHex(v.plaintext)
+ ciphertext := fromHex(v.ciphertext)
+ c, err := NewCipher(key)
+ if err != nil {
+ t.Fatalf("Test vector %d: Failed to create cipher instance: %s", i, err)
+ }
+
+ buf := make([]byte, BlockSize)
+
+ c.Encrypt(buf, plaintext)
+ if !bytes.Equal(ciphertext, buf) {
+ t.Fatalf("Test vector %d:\nEncryption failed\nFound: %s\nExpected: %s", i, hex.EncodeToString(buf), hex.EncodeToString(ciphertext))
+ }
+ c.Decrypt(buf, buf)
+ if !bytes.Equal(plaintext, buf) {
+ t.Fatalf("Test vector %d:\nDecryption failed\nFound: %s\nExpected: %s", i, hex.EncodeToString(buf), hex.EncodeToString(plaintext))
+ }
+ }
+}