-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Modes for cipher will be added soon Signed-off-by: Sid Sun <[email protected]>
- Loading branch information
Showing
5 changed files
with
380 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,257 @@ | ||
package seaturtle | ||
|
||
import ( | ||
"encoding/binary" | ||
) | ||
|
||
// Encrypt one block from src into dst, using the subkeys. | ||
func encryptBlock(subkeys []uint32, dst, src []byte) { | ||
cryptBlock(subkeys, dst, src, false) | ||
} | ||
|
||
// Decrypt one block from src into dst, using the subkeys. | ||
func decryptBlock(subkeys []uint32, dst, src []byte) { | ||
cryptBlock(subkeys, dst, src, true) | ||
} | ||
|
||
func cryptBlock(subkeys []uint32, dst, src []byte, decrypt bool) { | ||
var t uint64 | ||
left := binary.BigEndian.Uint64(src[0:8]) | ||
right := binary.BigEndian.Uint64(src[8:16]) | ||
|
||
if !decrypt { | ||
// Input Whitening | ||
left = left ^ concatenate32(subkeys[0], subkeys[1]) | ||
right = right ^ concatenate32(subkeys[2], subkeys[3]) | ||
for i := 0; i < 16; i++ { | ||
t = left | ||
left_ := feistelFunction(left, subkeys[4+(i*3)]) ^ concatenate32(subkeys[5+(i*3)], subkeys[6+(i*3)]) | ||
left = left_ ^ right | ||
right = t | ||
} | ||
// Undo Last Swap | ||
t = left | ||
left = right | ||
right = t | ||
// Output Whitening | ||
left = left ^ concatenate32(subkeys[52], subkeys[53]) | ||
right = right ^ concatenate32(subkeys[54], subkeys[55]) | ||
} else { | ||
// Input Whitening | ||
left = left ^ concatenate32(subkeys[52], subkeys[53]) | ||
right = right ^ concatenate32(subkeys[54], subkeys[55]) | ||
|
||
// Perform 16 feistel rounds | ||
for i := 0; i < 16; i++ { | ||
t = left | ||
left_ := feistelFunction(left, subkeys[49 +(i*-3)]) ^ concatenate32(subkeys[50 +(i*-3)], subkeys[51 +(i*-3)]) | ||
left = left_ ^ right | ||
right = t | ||
} | ||
// Undo Last Swap | ||
t = left | ||
left = right | ||
right = t | ||
|
||
// Output Whitening | ||
left = left ^ concatenate32(subkeys[0], subkeys[1]) | ||
right = right ^ concatenate32(subkeys[2], subkeys[3]) | ||
} | ||
binary.BigEndian.PutUint64(dst[0:8], left) | ||
binary.BigEndian.PutUint64(dst[8:16], right) | ||
} | ||
|
||
func feistelFunction(input uint64, p uint32) uint64 { | ||
G1 := gFunction(input, p) | ||
// Initial PHT without schedule | ||
for x := 0; x < 4; x += 2 { | ||
G1[x], G1[x+1] = pht8(G1[x], G1[x+1]) | ||
G1[x+4], G1[x+5] = pht8(G1[x+4], G1[x+5]) | ||
} | ||
// PHT With Schedule | ||
numberOfScheduledPHTLayers := 2 | ||
var intermediate [8]uint8 | ||
for j := 0; j < numberOfScheduledPHTLayers; j++ { | ||
for x := 0; x < 4; x += 2 { | ||
intermediate[x], intermediate[x+1] = pht8(G1[x*2], G1[(x+1)*2]) | ||
intermediate[x+4], intermediate[x+5] = pht8(G1[1+(x*2)], G1[1+((x+1)*2)]) | ||
} | ||
G1 = intermediate | ||
} | ||
return binary.BigEndian.Uint64(G1[:]) | ||
} | ||
|
||
func gFunction(input uint64, p uint32) [8]uint8 { | ||
var s0, s1, s2, s3, s4, s5, s6, s7 uint16 | ||
// Split 64 bit input to four 16 bit blocks | ||
// s0, s2, s4, s6 have the original input and s1, s3, s5, s7 have derived input | ||
s0 = uint16(input >> (3 * 16)) | ||
s2 = uint16(input >> (2 * 16)) | ||
s4 = uint16(input >> (1 * 16)) | ||
s6 = uint16(input) | ||
// Rotate the split blocks as per data p | ||
s0 = rotate16byN(s0, shift32ToGet4(p, 1), false) | ||
s2 = rotate16byN(s2, shift32ToGet4(p, 2), false) | ||
s4 = rotate16byN(s4, shift32ToGet4(p, 3), false) | ||
s6 = rotate16byN(s6, shift32ToGet4(p, 4), false) | ||
// Do Expansion and then DDR with data p on four 16 bit blocks | ||
// Expansion is performed by taking 1 byte each from corresponding s0, s2, s4, s6 and concatenating | ||
// In case of s7, the second byte for concatenation is the first byte of s0 | ||
s1 = rotate16byN(concatenate8ToGet16(shift16ToGet8(s0, 2), shift16ToGet8(s2, 1)), shift32ToGet4(p, 5), true) | ||
s3 = rotate16byN(concatenate8ToGet16(shift16ToGet8(s2, 2), shift16ToGet8(s4, 1)), shift32ToGet4(p, 6), true) | ||
s5 = rotate16byN(concatenate8ToGet16(shift16ToGet8(s4, 2), shift16ToGet8(s6, 1)), shift32ToGet4(p, 7), true) | ||
s7 = rotate16byN(concatenate8ToGet16(shift16ToGet8(s6, 2), shift16ToGet8(s0, 1)), shift32ToGet4(p, 8), true) | ||
return [8]uint8{ | ||
// There are 8 16:8 APN S Boxes | ||
// For the corresponding array implementation, we need to split 16 bits into 8 bits | ||
sBoxes[0][shift16ToGet8(s0, 1)][shift16ToGet8(s0, 2)], | ||
sBoxes[1][shift16ToGet8(s1, 1)][shift16ToGet8(s1, 2)], | ||
sBoxes[2][shift16ToGet8(s2, 1)][shift16ToGet8(s2, 2)], | ||
sBoxes[3][shift16ToGet8(s3, 1)][shift16ToGet8(s3, 2)], | ||
sBoxes[4][shift16ToGet8(s4, 1)][shift16ToGet8(s4, 2)], | ||
sBoxes[5][shift16ToGet8(s5, 1)][shift16ToGet8(s5, 2)], | ||
sBoxes[6][shift16ToGet8(s6, 1)][shift16ToGet8(s6, 2)], | ||
sBoxes[7][shift16ToGet8(s7, 1)][shift16ToGet8(s7, 2)], | ||
} | ||
} | ||
|
||
func (s seaturtleCipher) generateSubKeys(key []byte) { | ||
numberOfWordsForInitialKey := len(key) / 4 // Number of 32 bit words needed for initial key (4,6 or 8) | ||
nextPiWord := 0 | ||
// Put the initial key in SubKeys after XOR with a word of PI | ||
for i := 0; i < numberOfWordsForInitialKey; i++ { | ||
s.subkeys[i] = binary.BigEndian.Uint32(key[i*4:(i*4)+4]) ^ pi[nextPiWord] | ||
nextPiWord++ | ||
} | ||
switch numberOfWordsForInitialKey { | ||
// TODO: Convert Different KeySchedules to a single, generic one | ||
case 4: | ||
// 128 Bit Key Schedule Rounds | ||
numberOfRounds := 13 | ||
for i := 0; i < numberOfRounds; i++ { | ||
G1 := gFunction(concatenate32(s.subkeys[i*4], s.subkeys[(i*4)+1]), pi[nextPiWord]) | ||
nextPiWord++ | ||
var pArray [8]uint16 | ||
// Initialize P Array | ||
pArray[0] = concatenate8ToGet16(G1[0], G1[1]) | ||
pArray[1] = concatenate8ToGet16(G1[2], G1[3]) | ||
pArray[2] = concatenate8ToGet16(G1[4], G1[5]) | ||
pArray[3] = concatenate8ToGet16(G1[6], G1[7]) | ||
pArray[4] = shift32ToGet16(s.subkeys[(i*4)+2], 1) | ||
pArray[5] = shift32ToGet16(s.subkeys[(i*4)+2], 2) | ||
pArray[6] = shift32ToGet16(s.subkeys[(i*4)+3], 1) | ||
pArray[7] = shift32ToGet16(s.subkeys[(i*4)+3], 2) | ||
// Initial PHT without schedule | ||
for x := 0; x < 8; x += 2 { | ||
pArray[x], pArray[x+1] = pht16(pArray[x], pArray[x+1]) | ||
} | ||
// PHT With Schedule | ||
numberOfScheduledPHTLayers := numberOfWordsForInitialKey / 2 | ||
var intermediate [8]uint16 | ||
for j := 0; j < numberOfScheduledPHTLayers; j++ { | ||
for x := 0; x < 4; x += 2 { | ||
intermediate[x], intermediate[x+1] = pht16(pArray[x*2], pArray[(x+1)*2]) | ||
intermediate[x+4], intermediate[x+5] = pht16(pArray[1+(x*2)], pArray[1+((x+1)*2)]) | ||
} | ||
pArray = intermediate | ||
} | ||
nextKeyWord := 4 + (i * 4) | ||
for i := 0; i < 8; i += 2 { | ||
s.subkeys[nextKeyWord] = concatenate16ToGet32(pArray[i], pArray[i+1]) | ||
nextKeyWord++ | ||
} | ||
} | ||
case 6: | ||
// 192 Bit Key Schedule Rounds | ||
numberOfRounds := 9 | ||
for i := 0; i < numberOfRounds; i++ { | ||
G1 := gFunction(concatenate32(s.subkeys[i*6], s.subkeys[(i*6)+1]), pi[nextPiWord]) | ||
nextPiWord++ | ||
G2 := gFunction(concatenate32(s.subkeys[4+(i*6)], s.subkeys[5+(i*6)]), pi[nextPiWord]) | ||
nextPiWord++ | ||
var pArray [12]uint16 | ||
// Initialize P Array | ||
pArray[0] = concatenate8ToGet16(G1[0], G1[1]) | ||
pArray[1] = concatenate8ToGet16(G1[2], G1[3]) | ||
pArray[2] = concatenate8ToGet16(G1[4], G1[5]) | ||
pArray[3] = concatenate8ToGet16(G1[6], G1[7]) | ||
pArray[4] = shift32ToGet16(s.subkeys[(i*6)+2], 1) | ||
pArray[5] = shift32ToGet16(s.subkeys[(i*6)+2], 2) | ||
pArray[6] = shift32ToGet16(s.subkeys[(i*6)+3], 1) | ||
pArray[7] = shift32ToGet16(s.subkeys[(i*6)+3], 2) | ||
pArray[8] = concatenate8ToGet16(G2[0], G2[1]) | ||
pArray[9] = concatenate8ToGet16(G2[2], G2[3]) | ||
pArray[10] = concatenate8ToGet16(G2[4], G2[5]) | ||
pArray[11] = concatenate8ToGet16(G2[6], G2[7]) | ||
// Initial PHT without schedule | ||
for x := 0; x < 12; x += 2 { | ||
pArray[x], pArray[x+1] = pht16(pArray[x], pArray[x+1]) | ||
} | ||
// PHT With Schedule | ||
numberOfScheduledPHTLayers := numberOfWordsForInitialKey / 2 | ||
var intermediate [12]uint16 | ||
for j := 0; j < numberOfScheduledPHTLayers; j++ { | ||
for x := 0; x < 6; x += 2 { | ||
intermediate[x], intermediate[x+1] = pht16(pArray[x*2], pArray[(x+1)*2]) | ||
intermediate[x+6], intermediate[x+7] = pht16(pArray[1+(x*2)], pArray[1+((x+1)*2)]) | ||
} | ||
pArray = intermediate | ||
} | ||
nextKeyWord := 6 + (i * 6) | ||
for i := 0; i < 12; i += 2 { | ||
if !(nextKeyWord > 56) { // 192 Bit KeySchedule generates more subkeys than necessary, ensure they are not added | ||
s.subkeys[nextKeyWord] = concatenate16ToGet32(pArray[i], pArray[i+1]) | ||
nextKeyWord++ | ||
} else { | ||
break | ||
} | ||
} | ||
} | ||
case 8: | ||
// 256 Bit Key Schedule Rounds | ||
numberOfRounds := 6 | ||
for i := 0; i < numberOfRounds; i++ { | ||
G1 := gFunction(concatenate32(s.subkeys[i*8], s.subkeys[(i*8)+1]), pi[nextPiWord]) | ||
nextPiWord++ | ||
G2 := gFunction(concatenate32(s.subkeys[4+(i*8)], s.subkeys[5+(i*8)]), pi[nextPiWord]) | ||
nextPiWord++ | ||
var pArray [16]uint16 | ||
// Initialize P Array | ||
pArray[0] = concatenate8ToGet16(G1[0], G1[1]) | ||
pArray[1] = concatenate8ToGet16(G1[2], G1[3]) | ||
pArray[2] = concatenate8ToGet16(G1[4], G1[5]) | ||
pArray[3] = concatenate8ToGet16(G1[6], G1[7]) | ||
pArray[4] = shift32ToGet16(s.subkeys[(i*8)+2], 1) | ||
pArray[5] = shift32ToGet16(s.subkeys[(i*8)+2], 2) | ||
pArray[6] = shift32ToGet16(s.subkeys[(i*8)+3], 1) | ||
pArray[7] = shift32ToGet16(s.subkeys[(i*8)+3], 2) | ||
pArray[8] = concatenate8ToGet16(G2[0], G2[1]) | ||
pArray[9] = concatenate8ToGet16(G2[2], G2[3]) | ||
pArray[10] = concatenate8ToGet16(G2[4], G2[5]) | ||
pArray[11] = concatenate8ToGet16(G2[6], G2[7]) | ||
pArray[12] = shift32ToGet16(s.subkeys[(i*8)+6], 1) | ||
pArray[13] = shift32ToGet16(s.subkeys[(i*8)+6], 2) | ||
pArray[14] = shift32ToGet16(s.subkeys[(i*8)+7], 1) | ||
pArray[15] = shift32ToGet16(s.subkeys[(i*8)+7], 2) | ||
// Initial PHT without schedule | ||
for x := 0; x < 16; x += 2 { | ||
pArray[x], pArray[x+1] = pht16(pArray[x], pArray[x+1]) | ||
} | ||
// PHT With Schedule | ||
numberOfScheduledPHTLayers := numberOfWordsForInitialKey / 2 | ||
var intermediate [16]uint16 | ||
for j := 0; j < numberOfScheduledPHTLayers; j++ { | ||
for x := 0; x < 8; x += 2 { | ||
intermediate[x], intermediate[x+1] = pht16(pArray[x*2], pArray[(x+1)*2]) | ||
intermediate[x+8], intermediate[x+9] = pht16(pArray[1+(x*2)], pArray[1+((x+1)*2)]) | ||
} | ||
pArray = intermediate | ||
} | ||
nextKeyWord := 8 + (i * 8) | ||
for i := 0; i < 16; i += 2 { | ||
s.subkeys[nextKeyWord] = concatenate16ToGet32(pArray[i], pArray[i+1]) | ||
nextKeyWord++ | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
package seaturtle | ||
|
||
import ( | ||
"crypto/cipher" | ||
"strconv" | ||
) | ||
|
||
type seaturtleCipher struct { | ||
subkeys [56]uint32 | ||
} | ||
|
||
const BlockSize = 16 | ||
|
||
type KeySizeError int | ||
|
||
func (k KeySizeError) Error() string { | ||
return "seaturtle: invalid key size " + strconv.Itoa(int(k)) | ||
} | ||
|
||
func NewCipher(key []byte) (cipher.Block, error) { | ||
|
||
switch len(key) { | ||
case 16, 24, 32: | ||
break | ||
default: | ||
return nil, KeySizeError(len(key)) | ||
} | ||
|
||
c := new(seaturtleCipher) | ||
c.generateSubKeys(key) | ||
|
||
return c, nil | ||
} | ||
|
||
func (s seaturtleCipher) BlockSize() int { | ||
return BlockSize | ||
} | ||
|
||
func (s seaturtleCipher) Encrypt(dst, src []byte) { | ||
if len(src) < BlockSize { | ||
panic("seaturtle: input not full block") | ||
} | ||
|
||
if len(dst) < BlockSize { | ||
panic("seaturtle: output not full block") | ||
} | ||
|
||
encryptBlock(s.subkeys[:], dst, src) | ||
} | ||
|
||
func (s seaturtleCipher) Decrypt(dst, src []byte) { | ||
if len(src) < BlockSize { | ||
panic("seaturtle: input not full block") | ||
} | ||
|
||
if len(dst) < BlockSize { | ||
panic("seaturtle: output not full block") | ||
} | ||
|
||
decryptBlock(s.subkeys[:], dst, src) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
package seaturtle | ||
|
||
var pi = [26]uint32{0x243F6A88, 0x85A308D3, 0x13198A2E, 0x03707344, 0xA4093822, 0x299F31D0, 0x082EFA98, 0xEC4E6C89, 0x452821E6, 0x38D01377, 0xBE5466CF, 0x34E90C6C, 0xC0AC29B7, 0xC97C50DD, 0x3F84D5B5, 0xB5470917, 0x9216D5D9, 0x8979FB1B, 0xD1310BA6, 0x98DFB5AC, 0x2FFD72DB, 0xD01ADFB7, 0xB8E1AFED, 0x6A267E96, 0xBA7C9045, 0xF12C7F99} |
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
package seaturtle | ||
|
||
func pht8(a byte, b byte) (byte, byte) { | ||
a = (a + b) % byte(255) | ||
b = (a + b) % byte(255) | ||
return a, b | ||
} | ||
|
||
func pht16(a uint16, b uint16) (uint16, uint16) { | ||
a = (a + b) % uint16(65535) | ||
b = (a + b) % uint16(65535) | ||
return a, b | ||
} | ||
|
||
func concatenate8ToGet16(a uint8, b uint8) uint16 { | ||
return (uint16(a) << 8) | uint16(b) | ||
} | ||
|
||
func concatenate16ToGet32(a, b uint16) uint32 { | ||
return (uint32(a) << 16) | uint32(b) | ||
} | ||
|
||
func concatenate32(a uint32, b uint32) uint64 { | ||
return (uint64(a) << 32) | uint64(b) | ||
} | ||
|
||
func rotate16byN(x uint16, N uint8, ShiftRight bool) uint16 { | ||
if ShiftRight { | ||
return x>>N | x<<(16-N) | ||
} else { | ||
return x<<N | x>>(16-N) | ||
} | ||
} | ||
|
||
func shift16ToGet8(x uint16, ind uint8) uint8 { | ||
// Indexing must start at 1 | ||
return uint8((x << ((ind - 1) * 8)) >> (8)) | ||
} | ||
|
||
func shift32ToGet4(x uint32, ind uint8) uint8 { | ||
// Indexing must start at 1 | ||
return uint8((x << ((ind - 1) * 4)) >> (7 * 4)) | ||
} | ||
func shift32ToGet16(x uint32, ind uint32) uint16 { | ||
// Indexing must start at 1 | ||
return uint16(x<<((ind-1)*16)) >> (16) | ||
} |