Last active
October 30, 2018 12:40
-
-
Save slifer2015/0d182d99280758a154e3fe12e48664d4 to your computer and use it in GitHub Desktop.
AES 256 CBC 32byte block size IV support golang implementation
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
package encryption | |
import ( | |
"crypto/cipher" | |
"runtime" | |
"unsafe" | |
) | |
func dup(p []byte) []byte { | |
q := make([]byte, len(p)) | |
copy(q, p) | |
return q | |
} | |
type cbc struct { | |
b cipher.Block | |
blockSize int | |
iv []byte | |
tmp []byte | |
} | |
func newCBC(b cipher.Block, iv []byte) *cbc { | |
return &cbc{ | |
b: b, | |
blockSize: b.BlockSize(), | |
iv: dup(iv), | |
tmp: make([]byte, b.BlockSize()), | |
} | |
} | |
type cbcEncrypter cbc | |
// cbcEncAble is an interface implemented by ciphers that have a specific | |
// optimized implementation of CBC encryption, like crypto/aes. | |
// NewCBCEncrypter will check for this interface and return the specific | |
// BlockMode if found. | |
type cbcEncAble interface { | |
NewCBCEncrypter(iv []byte) cipher.BlockMode | |
} | |
// NewCBCEncrypter returns a BlockMode which encrypts in cipher block chaining | |
// mode, using the given Block. The length of iv must be the same as the | |
// Block's block size. | |
func NewCBCEncrypter(b cipher.Block, iv []byte) cipher.BlockMode { | |
if len(iv) != b.BlockSize() { | |
panic("cipher.NewCBCEncrypter: IV length must equal block size") | |
} | |
if cbc, ok := b.(cbcEncAble); ok { | |
return cbc.NewCBCEncrypter(iv) | |
} | |
return (*cbcEncrypter)(newCBC(b, iv)) | |
} | |
func (x *cbcEncrypter) BlockSize() int { return x.blockSize } | |
func (x *cbcEncrypter) CryptBlocks(dst, src []byte) { | |
if len(src)%x.blockSize != 0 { | |
panic("crypto/cipher: input not full blocks") | |
} | |
if len(dst) < len(src) { | |
panic("crypto/cipher: output smaller than input") | |
} | |
iv := x.iv | |
for len(src) > 0 { | |
// Write the xor to dst, then encrypt in place. | |
xorBytes(dst[:x.blockSize], src[:x.blockSize], iv) | |
x.b.Encrypt(dst[:x.blockSize], dst[:x.blockSize]) | |
// Move to the next block with this block as the next iv. | |
iv = dst[:x.blockSize] | |
src = src[x.blockSize:] | |
dst = dst[x.blockSize:] | |
} | |
// Save the iv for the next CryptBlocks call. | |
copy(x.iv, iv) | |
} | |
func (x *cbcEncrypter) SetIV(iv []byte) { | |
if len(iv) != len(x.iv) { | |
panic("cipher: incorrect length IV") | |
} | |
copy(x.iv, iv) | |
} | |
type cbcDecrypter cbc | |
// cbcDecAble is an interface implemented by ciphers that have a specific | |
// optimized implementation of CBC decryption, like crypto/aes. | |
// NewCBCDecrypter will check for this interface and return the specific | |
// BlockMode if found. | |
type cbcDecAble interface { | |
NewCBCDecrypter(iv []byte) cipher.BlockMode | |
} | |
// NewCBCDecrypter returns a BlockMode which decrypts in cipher block chaining | |
// mode, using the given Block. The length of iv must be the same as the | |
// Block's block size and must match the iv used to encrypt the data. | |
func NewCBCDecrypter(b cipher.Block, iv []byte) cipher.BlockMode { | |
if len(iv) != b.BlockSize() { | |
panic("cipher.NewCBCDecrypter: IV length must equal block size") | |
} | |
if cbc, ok := b.(cbcDecAble); ok { | |
return cbc.NewCBCDecrypter(iv) | |
} | |
return (*cbcDecrypter)(newCBC(b, iv)) | |
} | |
func (x *cbcDecrypter) BlockSize() int { return x.blockSize } | |
func (x *cbcDecrypter) CryptBlocks(dst, src []byte) { | |
if len(src)%x.blockSize != 0 { | |
panic("crypto/cipher: input not full blocks") | |
} | |
if len(dst) < len(src) { | |
panic("crypto/cipher: output smaller than input") | |
} | |
if len(src) == 0 { | |
return | |
} | |
// For each block, we need to xor the decrypted data with the previous block's ciphertext (the iv). | |
// To avoid making a copy each time, we loop over the blocks BACKWARDS. | |
end := len(src) | |
start := end - x.blockSize | |
prev := start - x.blockSize | |
// Copy the last block of ciphertext in preparation as the new iv. | |
copy(x.tmp, src[start:end]) | |
// Loop over all but the first block. | |
for start > 0 { | |
x.b.Decrypt(dst[start:end], src[start:end]) | |
xorBytes(dst[start:end], dst[start:end], src[prev:start]) | |
end = start | |
start = prev | |
prev -= x.blockSize | |
} | |
// The first block is special because it uses the saved iv. | |
x.b.Decrypt(dst[start:end], src[start:end]) | |
xorBytes(dst[start:end], dst[start:end], x.iv) | |
// Set the new iv to the first block we copied earlier. | |
x.iv, x.tmp = x.tmp, x.iv | |
} | |
func (x *cbcDecrypter) SetIV(iv []byte) { | |
if len(iv) != len(x.iv) { | |
panic("cipher: incorrect length IV") | |
} | |
copy(x.iv, iv) | |
} | |
const wordSize = int(unsafe.Sizeof(uintptr(0))) | |
const supportsUnaligned = runtime.GOARCH == "386" || runtime.GOARCH == "amd64" || runtime.GOARCH == "ppc64" || runtime.GOARCH == "ppc64le" || runtime.GOARCH == "s390x" | |
// fastXORBytes xors in bulk. It only works on architectures that | |
// support unaligned read/writes. | |
func fastXORBytes(dst, a, b []byte) int { | |
n := len(a) | |
if len(b) < n { | |
n = len(b) | |
} | |
if n == 0 { | |
return 0 | |
} | |
// Assert dst has enough space | |
_ = dst[n-1] | |
w := n / wordSize | |
if w > 0 { | |
dw := *(*[]uintptr)(unsafe.Pointer(&dst)) | |
aw := *(*[]uintptr)(unsafe.Pointer(&a)) | |
bw := *(*[]uintptr)(unsafe.Pointer(&b)) | |
for i := 0; i < w; i++ { | |
dw[i] = aw[i] ^ bw[i] | |
} | |
} | |
for i := (n - n%wordSize); i < n; i++ { | |
dst[i] = a[i] ^ b[i] | |
} | |
return n | |
} | |
func safeXORBytes(dst, a, b []byte) int { | |
n := len(a) | |
if len(b) < n { | |
n = len(b) | |
} | |
for i := 0; i < n; i++ { | |
dst[i] = a[i] ^ b[i] | |
} | |
return n | |
} | |
// xorBytes xors the bytes in a and b. The destination should have enough | |
// space, otherwise xorBytes will panic. Returns the number of bytes xor'd. | |
func xorBytes(dst, a, b []byte) int { | |
if supportsUnaligned { | |
return fastXORBytes(dst, a, b) | |
} else { | |
// TODO(hanwen): if (dst, a, b) have common alignment | |
// we could still try fastXORBytes. It is not clear | |
// how often this happens, and it's only worth it if | |
// the block encryption itself is hardware | |
// accelerated. | |
return safeXORBytes(dst, a, b) | |
} | |
} | |
// fastXORWords XORs multiples of 4 or 8 bytes (depending on architecture.) | |
// The arguments are assumed to be of equal length. | |
func fastXORWords(dst, a, b []byte) { | |
dw := *(*[]uintptr)(unsafe.Pointer(&dst)) | |
aw := *(*[]uintptr)(unsafe.Pointer(&a)) | |
bw := *(*[]uintptr)(unsafe.Pointer(&b)) | |
n := len(b) / wordSize | |
for i := 0; i < n; i++ { | |
dw[i] = aw[i] ^ bw[i] | |
} | |
} | |
func xorWords(dst, a, b []byte) { | |
if supportsUnaligned { | |
fastXORWords(dst, a, b) | |
} else { | |
safeXORBytes(dst, a, b) | |
} | |
} |
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
package encryption | |
import ( | |
"bytes" | |
"encoding/base64" | |
"fmt" | |
) | |
func Rijndael256CBCEncrypt32(key,iv []byte,src string)(string,error){ | |
ciph,err:=NewCipher(key) | |
if err!=nil{ | |
return "",err | |
} | |
origData:=addPadding([]byte(src),ciph.BlockSize()) | |
blockMode:=NewCBCEncrypter(ciph,iv) | |
crypted := make([]byte, len(origData)) | |
blockMode.CryptBlocks(crypted, origData) | |
finalResult:=base64.StdEncoding.EncodeToString(crypted) | |
return finalResult,nil | |
} | |
func Rijndael256CBCDecrypt32(key,iv []byte,cipherText string)(string,error){ | |
ciph,err:=NewCipher(key) | |
if err!=nil{ | |
return "",err | |
} | |
cipherTextFinal,err:=base64.StdEncoding.DecodeString(cipherText) | |
if err!=nil{ | |
return "",err | |
} | |
blockMode:=NewCBCDecrypter(ciph,iv) | |
origData := make([]byte, len(cipherTextFinal)) | |
fmt.Println(len(origData)) | |
blockMode.CryptBlocks(origData, cipherTextFinal) | |
origData, err = unpad(origData, ciph.BlockSize()) | |
return string(origData),nil | |
} | |
func addPadding(src []byte, blockLen int)[]byte{ | |
padding := blockLen - len(src)%blockLen | |
padtext := bytes.Repeat([]byte{byte(padding)}, padding) | |
return append(src, padtext...) | |
} | |
func unpad(data []byte, blockLen int) ([]byte, error) { | |
if blockLen <= 0 { | |
return nil, fmt.Errorf("invalid blockLen %d", blockLen) | |
} | |
if len(data)%blockLen != 0 || len(data) == 0 { | |
return nil, fmt.Errorf("invalid data len %d", len(data)) | |
} | |
padlen := int(data[len(data)-1]) | |
if padlen > blockLen || padlen == 0 { | |
return nil, fmt.Errorf("invalid padding") | |
} | |
// check padding | |
pad := data[len(data)-padlen:] | |
for i := 0; i < padlen; i++ { | |
if pad[i] != byte(padlen) { | |
return nil, fmt.Errorf("invalid padding") | |
} | |
} | |
return data[:len(data)-padlen], nil | |
} | |
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
package encryption | |
import ( | |
"crypto/cipher" | |
"encoding/binary" | |
) | |
const rounds = 14 | |
const keyWords = 8 | |
const BlockSize = 32 | |
const blockWords = BlockSize/4 | |
type Cipher struct { | |
key [blockWords * (rounds + 1)]uint32 | |
} | |
func NewCipher(key []byte) (cipher.Block, error) { | |
c := new(Cipher) | |
for i := 0; i < keyWords; i++ { | |
c.key[i] = binary.BigEndian.Uint32(key[4*i:]) | |
} | |
roundConstant := byte(1) | |
for i := keyWords; i < blockWords * (rounds + 1); i++ { | |
temp := c.key[i-1] | |
if i % keyWords == 0 { | |
temp = subByte(rotByte(temp)) ^ (uint32(roundConstant) << 24) | |
roundConstant = xtime(roundConstant) | |
} else if i % keyWords == 4 { | |
temp = subByte(temp) | |
} | |
c.key[i] = c.key[i - keyWords] ^ temp | |
} | |
return c, nil | |
} | |
func (c *Cipher) BlockSize() int { | |
return BlockSize; | |
} | |
func (c *Cipher) Encrypt(dst, src []byte) { | |
var state [4*blockWords]byte | |
copy(state[:], src[:]) | |
addKey(&state, c.key[:]) | |
for round := 1; round < rounds; round++ { | |
// ByteSub | |
for i := range state { | |
state[i] = sbox[state[i]] | |
} | |
// ShiftRow | |
if blockWords == 32/4 { | |
shiftRow(&state, 1, 1) | |
shiftRow(&state, 2, 3) | |
shiftRow(&state, 3, 4) | |
} else { | |
shiftRow(&state, 1, 1) | |
shiftRow(&state, 2, 2) | |
shiftRow(&state, 3, 3) | |
} | |
// MixColumns | |
for i := 0; i < blockWords; i++ { | |
mixColumn(state[4*i:]) | |
} | |
addKey(&state, c.key[blockWords*round:]) | |
} | |
// ByteSub | |
for i := range state { | |
state[i] = sbox[state[i]] | |
} | |
// ShiftRow | |
if blockWords == 32/4 { | |
shiftRow(&state, 1, 1) | |
shiftRow(&state, 2, 3) | |
shiftRow(&state, 3, 4) | |
} else { | |
shiftRow(&state, 1, 1) | |
shiftRow(&state, 2, 2) | |
shiftRow(&state, 3, 3) | |
} | |
addKey(&state, c.key[blockWords*rounds:]) | |
copy(dst[:], state[:]) | |
} | |
func (c *Cipher) Decrypt(dst, src []byte) { | |
var state [4*blockWords]byte | |
copy(state[:], src[:]) | |
addKey(&state, c.key[blockWords*rounds:]) | |
// ShiftRow | |
if blockWords == 32/4 { | |
shiftRow(&state, 1, -1) | |
shiftRow(&state, 2, -3) | |
shiftRow(&state, 3, -4) | |
} else { | |
shiftRow(&state, 1, -1) | |
shiftRow(&state, 2, -2) | |
shiftRow(&state, 3, -3) | |
} | |
// ByteSub | |
for i := range state { | |
state[i] = sboxInv[state[i]] | |
} | |
for round := rounds - 1; round >= 1; round-- { | |
addKey(&state, c.key[blockWords*round:]) | |
// MixColumns | |
for i := 0; i < blockWords; i++ { | |
mixColumnInv(state[4*i:]) | |
} | |
// ShiftRow | |
if blockWords == 32/4 { | |
shiftRow(&state, 1, -1) | |
shiftRow(&state, 2, -3) | |
shiftRow(&state, 3, -4) | |
} else { | |
shiftRow(&state, 1, -1) | |
shiftRow(&state, 2, -2) | |
shiftRow(&state, 3, -3) | |
} | |
// ByteSub | |
for i := range state { | |
state[i] = sboxInv[state[i]] | |
} | |
} | |
addKey(&state, c.key[:]) | |
copy(dst[:], state[:]) | |
} | |
func subByte(a uint32) uint32 { | |
ret := uint32(0) | |
for i := 0; i < 4; i++ { | |
b := byte(a) | |
ret >>= 8 | |
ret |= uint32(sbox[b]) << 24 | |
a >>= 8 | |
} | |
return ret | |
} | |
func rotByte(a uint32) uint32 { | |
return (a >> 24) | (a << 8) | |
} | |
func addKey(state *[4*blockWords]byte, key []uint32) { | |
for i := 0; i < blockWords; i++ { | |
t := binary.BigEndian.Uint32(state[4*i:]) | |
t ^= key[i] | |
binary.BigEndian.PutUint32(state[4*i:], t) | |
} | |
} | |
func shiftRow(state *[4*blockWords]byte, row, shift int) { | |
var input [blockWords]byte | |
for i := range input { | |
input[i] = state[row + 4*i] | |
} | |
src := shift | |
if src < 0 { | |
src += blockWords | |
} | |
dst := row | |
for i := 0; i < blockWords; i++ { | |
state[dst] = input[src] | |
dst += 4 | |
dst %= blockWords*4 | |
src++ | |
src %= blockWords | |
} | |
} | |
func mixColumn(col []byte) { | |
a := col[0] | |
b := col[1] | |
c := col[2] | |
d := col[3] | |
e := a ^ b ^ c ^ d | |
col[0] ^= e ^ xtime(a^b) | |
col[1] ^= e ^ xtime(b^c) | |
col[2] ^= e ^ xtime(c^d) | |
col[3] ^= e ^ xtime(d^a) | |
} | |
func mixColumnInv(col []byte) { | |
a := col[0] | |
b := col[1] | |
c := col[2] | |
d := col[3] | |
e := a ^ b ^ c ^ d | |
z := xtime(e) | |
x := e ^ xtime(xtime(z^a^c)) | |
y := e ^ xtime(xtime(z^b^d)) | |
col[0] ^= x ^ xtime(a^b) | |
col[1] ^= y ^ xtime(b^c) | |
col[2] ^= x ^ xtime(c^d) | |
col[3] ^= y ^ xtime(d^a) | |
} | |
func xtime(b byte) byte { | |
c := uint32(b) | |
c <<= 1 | |
c ^= ((c >> 8) & 1) * 0x1b | |
return byte(c) | |
} | |
// FIPS-197 Figure 7. S-box substitution values in hexadecimal format. | |
var sbox = [256]byte{ | |
0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76, | |
0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0, | |
0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15, | |
0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75, | |
0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84, | |
0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf, | |
0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8, | |
0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2, | |
0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73, | |
0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb, | |
0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79, | |
0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08, | |
0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a, | |
0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e, | |
0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf, | |
0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16, | |
} | |
// FIPS-197 Figure 14. Inverse S-box substitution values in hexadecimal format. | |
var sboxInv = [256]byte{ | |
0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb, | |
0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87, 0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb, | |
0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d, 0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e, | |
0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2, 0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25, | |
0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92, | |
0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda, 0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84, | |
0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a, 0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06, | |
0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02, 0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b, | |
0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea, 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73, | |
0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85, 0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e, | |
0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89, 0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b, | |
0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20, 0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4, | |
0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31, 0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f, | |
0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d, 0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef, | |
0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0, 0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61, | |
0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, 0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d, | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment