Initial commit
This commit is contained in:
parent
082cfcc470
commit
45c6031836
3 changed files with 413 additions and 0 deletions
7
README.md
Normal file
7
README.md
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
# tblyler/go-mcrypt
|
||||||
|
|
||||||
|
This is a work in progress and currently just implements an encrypt and decrypt function that is compliant with PHP's rijndael AES 256 specification.
|
||||||
|
As one might guess, this was to replace existing PHP code that relied on rijndael AES 256.
|
||||||
|
|
||||||
|
## Requirements
|
||||||
|
* libmcrypt
|
253
mcrypt.go
Normal file
253
mcrypt.go
Normal file
|
@ -0,0 +1,253 @@
|
||||||
|
package mcrypt
|
||||||
|
|
||||||
|
/*
|
||||||
|
#cgo LDFLAGS: -lmcrypt
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <mcrypt.h>
|
||||||
|
|
||||||
|
#define FAILED_MCRYPT_MODULE 1
|
||||||
|
#define INVALID_KEY_LENGTH 2
|
||||||
|
#define INVALID_IV_LENGTH 3
|
||||||
|
#define FAILED_TO_ENCRYPT_DATA 4
|
||||||
|
#define FAILED_TO_DECRYPT_DATA 5
|
||||||
|
#define INVALID_DATA_LENGTH 6
|
||||||
|
|
||||||
|
// getError convert a given error code to its string representation
|
||||||
|
const char* getError(int err) {
|
||||||
|
switch (err) {
|
||||||
|
case FAILED_MCRYPT_MODULE:
|
||||||
|
return "Failed to open mcrypt module";
|
||||||
|
|
||||||
|
case INVALID_KEY_LENGTH:
|
||||||
|
return "Invalid key length";
|
||||||
|
|
||||||
|
case INVALID_IV_LENGTH:
|
||||||
|
return "Invalid iv length";
|
||||||
|
|
||||||
|
case FAILED_TO_ENCRYPT_DATA:
|
||||||
|
return "Failed to encrypt data";
|
||||||
|
|
||||||
|
case FAILED_TO_DECRYPT_DATA:
|
||||||
|
return "Failed to decrypt data";
|
||||||
|
|
||||||
|
case INVALID_DATA_LENGTH:
|
||||||
|
return "Invalid data length";
|
||||||
|
}
|
||||||
|
|
||||||
|
return mcrypt_strerror(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
// encrypt encrypt a given data set with rijndael 256
|
||||||
|
char* encrypt(void* key, int keyLength, void* iv, int ivLength, char* data, int* length, int* err) {
|
||||||
|
if (*length <= 0) {
|
||||||
|
*err = INVALID_DATA_LENGTH;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
int i;
|
||||||
|
|
||||||
|
MCRYPT td = mcrypt_module_open("rijndael-256", NULL, "cbc", NULL);
|
||||||
|
if (td == MCRYPT_FAILED) {
|
||||||
|
*err = FAILED_MCRYPT_MODULE;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
int requiredKeySize = mcrypt_enc_get_key_size(td);
|
||||||
|
int requiredIvSize = mcrypt_enc_get_iv_size(td);
|
||||||
|
|
||||||
|
// make sure the key and iv are the correct sizes
|
||||||
|
if (keyLength != requiredKeySize) {
|
||||||
|
*err = INVALID_KEY_LENGTH;
|
||||||
|
mcrypt_module_close(td);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ivLength != requiredIvSize) {
|
||||||
|
*err = INVALID_IV_LENGTH;
|
||||||
|
mcrypt_module_close(td);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
*err = mcrypt_generic_init(td, key, keyLength, iv);
|
||||||
|
if (*err) {
|
||||||
|
mcrypt_generic_deinit(td);
|
||||||
|
mcrypt_module_close(td);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// get the block size
|
||||||
|
int blockSize = mcrypt_enc_get_block_size(td);
|
||||||
|
|
||||||
|
// determine the new length if needed
|
||||||
|
int newLength = 0;
|
||||||
|
|
||||||
|
// if blockSize is greater than length, expand length to the blockSize
|
||||||
|
if (blockSize > *length) {
|
||||||
|
newLength = blockSize;
|
||||||
|
} else {
|
||||||
|
int lengthBlockMod = *length % blockSize;
|
||||||
|
if (lengthBlockMod) {
|
||||||
|
// if length is not multiple of blockSize, make it the next highest blockSize
|
||||||
|
newLength = *length - lengthBlockMod + blockSize;
|
||||||
|
} else {
|
||||||
|
// we do not need to change the length
|
||||||
|
newLength = *length;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// allocate and copy the data to the output value
|
||||||
|
char* output = malloc(sizeof *output * newLength);
|
||||||
|
// append byte zeroes to the output array if needed
|
||||||
|
for (i = *length; i < newLength; ++i) {
|
||||||
|
output[i] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(output, data, *length);
|
||||||
|
|
||||||
|
// update the length to the reallocated length
|
||||||
|
*length = newLength;
|
||||||
|
|
||||||
|
// loop through the output data by blockSize at a time
|
||||||
|
for (i = 0; i < *length; i += blockSize) {
|
||||||
|
// encrypt the block of output[i] plus blockSize
|
||||||
|
if (mcrypt_generic(td, output+i, blockSize)) {
|
||||||
|
*err = FAILED_TO_ENCRYPT_DATA;
|
||||||
|
mcrypt_generic_deinit(td);
|
||||||
|
mcrypt_module_close(td);
|
||||||
|
free(output);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// finish up mcrypt
|
||||||
|
mcrypt_generic_deinit(td);
|
||||||
|
mcrypt_module_close(td);
|
||||||
|
|
||||||
|
// return the encrypted data
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
// decrypt decrypt a given data set with rijndael 256
|
||||||
|
char* decrypt(void* key, int keyLength, void* iv, int ivLength, char* data, int* length, int* err) {
|
||||||
|
int i;
|
||||||
|
|
||||||
|
MCRYPT td = mcrypt_module_open("rijndael-256", NULL, "cbc", NULL);
|
||||||
|
if (td == MCRYPT_FAILED) {
|
||||||
|
*err = FAILED_MCRYPT_MODULE;
|
||||||
|
mcrypt_module_close(td);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
int requiredKeySize = mcrypt_enc_get_key_size(td);
|
||||||
|
int requiredIvSize = mcrypt_enc_get_iv_size(td);
|
||||||
|
|
||||||
|
// make sure the key and iv are the correct sizes
|
||||||
|
if (keyLength != requiredKeySize) {
|
||||||
|
*err = INVALID_KEY_LENGTH;
|
||||||
|
mcrypt_module_close(td);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ivLength != requiredIvSize) {
|
||||||
|
*err = INVALID_IV_LENGTH;
|
||||||
|
mcrypt_module_close(td);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
*err = mcrypt_generic_init(td, key, keyLength, iv);
|
||||||
|
if (*err) {
|
||||||
|
mcrypt_generic_deinit(td);
|
||||||
|
mcrypt_module_close(td);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// get the block size
|
||||||
|
int blockSize = mcrypt_enc_get_block_size(td);
|
||||||
|
|
||||||
|
if (*length < blockSize || *length % blockSize) {
|
||||||
|
*err = INVALID_DATA_LENGTH;
|
||||||
|
mcrypt_generic_deinit(td);
|
||||||
|
mcrypt_module_close(td);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// allocate and copy the data to the output value
|
||||||
|
char* output = malloc(sizeof *output * *length);
|
||||||
|
|
||||||
|
memcpy(output, data, *length);
|
||||||
|
|
||||||
|
// loop through the output data by blockSize at a time
|
||||||
|
for (i = 0; i < *length; i += blockSize) {
|
||||||
|
// decrypt the block of output[i] plus blockSize
|
||||||
|
if (mdecrypt_generic(td, output+i, blockSize)) {
|
||||||
|
*err = FAILED_TO_DECRYPT_DATA;
|
||||||
|
mcrypt_generic_deinit(td);
|
||||||
|
mcrypt_module_close(td);
|
||||||
|
free(output);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// finish up mcrypt
|
||||||
|
mcrypt_generic_deinit(td);
|
||||||
|
mcrypt_module_close(td);
|
||||||
|
|
||||||
|
// return the decrypted data
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
import "C"
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Encrypt encrypt something with mcrypt rijndael-256 PHP-style
|
||||||
|
func Encrypt(key []byte, iv []byte, data []byte) ([]byte, error) {
|
||||||
|
// keep track of the size of the input data
|
||||||
|
length := C.int(len(data))
|
||||||
|
if length == 0 {
|
||||||
|
return nil, errors.New("Invalid data size of 0")
|
||||||
|
}
|
||||||
|
// keep track of any errors that occur on encryption
|
||||||
|
err := C.int(0)
|
||||||
|
// encrypt the data
|
||||||
|
encryptedData := C.encrypt(unsafe.Pointer(&key[0]), C.int(len(key)), unsafe.Pointer(&iv[0]), C.int(len(iv)), (*C.char)(unsafe.Pointer(&data[0])), (*C.int)(unsafe.Pointer(&length)), (*C.int)(unsafe.Pointer(&err)))
|
||||||
|
|
||||||
|
// if err is not 0, there is an error
|
||||||
|
if int(err) != 0 {
|
||||||
|
return nil, errors.New(C.GoString(C.getError(err)))
|
||||||
|
}
|
||||||
|
|
||||||
|
// ensure that memory is freed on the encrypted data after it is converted to Go bytes
|
||||||
|
defer C.free(unsafe.Pointer(encryptedData))
|
||||||
|
|
||||||
|
// return the Go bytes of the encrypted data
|
||||||
|
return C.GoBytes(unsafe.Pointer(encryptedData), length), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decrypt decrypt something with mcrypt rijndael-256 PHP-style
|
||||||
|
func Decrypt(key []byte, iv []byte, data []byte) ([]byte, error) {
|
||||||
|
// keep track of the size of the input data
|
||||||
|
length := C.int(len(data))
|
||||||
|
if length == 0 {
|
||||||
|
return nil, errors.New("Invalid data size of 0")
|
||||||
|
}
|
||||||
|
// keep track of any errors that occur on decryption
|
||||||
|
err := C.int(0)
|
||||||
|
// decrypt the data
|
||||||
|
decryptedData := C.decrypt(unsafe.Pointer(&key[0]), C.int(len(key)), unsafe.Pointer(&iv[0]), C.int(len(iv)), (*C.char)(unsafe.Pointer(&data[0])), (*C.int)(unsafe.Pointer(&length)), (*C.int)(unsafe.Pointer(&err)))
|
||||||
|
|
||||||
|
// if err is not 0, there is an error
|
||||||
|
if int(err) != 0 {
|
||||||
|
return nil, errors.New(C.GoString(C.getError(err)))
|
||||||
|
}
|
||||||
|
|
||||||
|
// ensure that memory is freed on the decrypted data after it is converted to Go bytes
|
||||||
|
defer C.free(unsafe.Pointer(decryptedData))
|
||||||
|
|
||||||
|
// return the Go bytes of the decrypted data
|
||||||
|
return C.GoBytes(unsafe.Pointer(decryptedData), length), nil
|
||||||
|
}
|
153
mcrypt_test.go
Normal file
153
mcrypt_test.go
Normal file
|
@ -0,0 +1,153 @@
|
||||||
|
package mcrypt
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"crypto/rand"
|
||||||
|
mrand "math/rand"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestEncrypt(t *testing.T) {
|
||||||
|
dataSizes := []int{8, 13, 16, 32, 64, 1024, 1048576, 4194304, (mrand.Int() % 26214400) + 1}
|
||||||
|
|
||||||
|
for _, dataSize := range dataSizes {
|
||||||
|
key := make([]byte, 32)
|
||||||
|
_, err := rand.Read(key)
|
||||||
|
if err != nil {
|
||||||
|
t.Error("Failed to get random data from crypto/rand")
|
||||||
|
}
|
||||||
|
iv := make([]byte, 32)
|
||||||
|
_, err = rand.Read(iv)
|
||||||
|
if err != nil {
|
||||||
|
t.Error("Failed to get random data from crypto/rand")
|
||||||
|
}
|
||||||
|
data := make([]byte, dataSize)
|
||||||
|
_, err = rand.Read(data)
|
||||||
|
if err != nil {
|
||||||
|
t.Error("Failed to get random data from crypto/rand")
|
||||||
|
}
|
||||||
|
|
||||||
|
encrypted, err := Encrypt(key, iv, data)
|
||||||
|
if err != nil {
|
||||||
|
t.Error("Failed Encrypt with error: " + err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
if bytes.Equal(encrypted, data) {
|
||||||
|
t.Error("Failed Encrypt: Encrypted data was the same as input data")
|
||||||
|
}
|
||||||
|
|
||||||
|
decrypted, err := Decrypt(key, iv, encrypted)
|
||||||
|
if err != nil {
|
||||||
|
t.Error("Failed Decrypt with error: " + err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
cryptLen := len(decrypted)
|
||||||
|
for i := 0; i < cryptLen; i++ {
|
||||||
|
if i >= dataSize {
|
||||||
|
if decrypted[i] != 0 {
|
||||||
|
t.Error("Failed encryption/decryption: invalid padding")
|
||||||
|
}
|
||||||
|
} else if decrypted[i] != data[i] {
|
||||||
|
t.Error("Failed encryption/decryption: invalid data")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
key := make([]byte, 31)
|
||||||
|
iv := make([]byte, 32)
|
||||||
|
data := make([]byte, 32)
|
||||||
|
|
||||||
|
_, err := Encrypt(key, iv, data)
|
||||||
|
if err == nil {
|
||||||
|
t.Error("Failed to receive error for invalid key size")
|
||||||
|
}
|
||||||
|
|
||||||
|
key = make([]byte, 32)
|
||||||
|
iv = make([]byte, 31)
|
||||||
|
_, err = Encrypt(key, iv, data)
|
||||||
|
if err == nil {
|
||||||
|
t.Error("Failed to receive error for invalid iv size")
|
||||||
|
}
|
||||||
|
|
||||||
|
key = make([]byte, 32)
|
||||||
|
iv = make([]byte, 32)
|
||||||
|
_, err = Encrypt(key, iv, []byte{})
|
||||||
|
if err == nil {
|
||||||
|
t.Error("Failed to receive error for 0 byte data size")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDecrypt(t *testing.T) {
|
||||||
|
dataSizes := []int{8, 13, 16, 32, 64, 1024, 1048576, 4194304, (mrand.Int() % 26214400) + 1}
|
||||||
|
|
||||||
|
for _, dataSize := range dataSizes {
|
||||||
|
key := make([]byte, 32)
|
||||||
|
_, err := rand.Read(key)
|
||||||
|
if err != nil {
|
||||||
|
t.Error("Failed to get random data from crypto/rand")
|
||||||
|
}
|
||||||
|
iv := make([]byte, 32)
|
||||||
|
_, err = rand.Read(iv)
|
||||||
|
if err != nil {
|
||||||
|
t.Error("Failed to get random data from crypto/rand")
|
||||||
|
}
|
||||||
|
data := make([]byte, dataSize)
|
||||||
|
_, err = rand.Read(data)
|
||||||
|
if err != nil {
|
||||||
|
t.Error("Failed to get random data from crypto/rand")
|
||||||
|
}
|
||||||
|
|
||||||
|
encrypted, err := Encrypt(key, iv, data)
|
||||||
|
if err != nil {
|
||||||
|
t.Error("Failed Encrypt with error: " + err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
if bytes.Equal(encrypted, data) {
|
||||||
|
t.Error("Failed Encrypt: Encrypted data was the same as input data")
|
||||||
|
}
|
||||||
|
|
||||||
|
decrypted, err := Decrypt(key, iv, encrypted)
|
||||||
|
if err != nil {
|
||||||
|
t.Error("Failed Decrypt with error: " + err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
cryptLen := len(decrypted)
|
||||||
|
for i := 0; i < cryptLen; i++ {
|
||||||
|
if i >= dataSize {
|
||||||
|
if decrypted[i] != 0 {
|
||||||
|
t.Error("Failed encryption/decryption: invalid padding")
|
||||||
|
}
|
||||||
|
} else if decrypted[i] != data[i] {
|
||||||
|
t.Error("Failed encryption/decryption: invalid data")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
key := make([]byte, 31)
|
||||||
|
iv := make([]byte, 32)
|
||||||
|
data := make([]byte, 32)
|
||||||
|
|
||||||
|
_, err := Decrypt(key, iv, data)
|
||||||
|
if err == nil {
|
||||||
|
t.Error("Failed to receive error for invalid key size")
|
||||||
|
}
|
||||||
|
|
||||||
|
key = make([]byte, 32)
|
||||||
|
iv = make([]byte, 31)
|
||||||
|
_, err = Decrypt(key, iv, data)
|
||||||
|
if err == nil {
|
||||||
|
t.Error("Failed to receive error for invalid iv size")
|
||||||
|
}
|
||||||
|
|
||||||
|
key = make([]byte, 32)
|
||||||
|
iv = make([]byte, 32)
|
||||||
|
data = make([]byte, 31)
|
||||||
|
if err == nil {
|
||||||
|
t.Error("Failed to receive error for invalid data size")
|
||||||
|
}
|
||||||
|
|
||||||
|
data = make([]byte, 0)
|
||||||
|
if err == nil {
|
||||||
|
t.Error("Failed to receive error for 0 byte data size")
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue