1
0
Fork 0
mirror of https://github.com/Luzifer/go-openssl.git synced 2024-10-18 06:14:21 +00:00
go-openssl/openssl_test.go
Owen McGill 9b5d475a0b Add encrypt/decrypt without base64 encoding
The DecryptBinaryBytes, EncryptBinaryBytes* methods allow the data
being decrypted/encrypted to be in a binary form, not base64.

Addresses issue #14.

Signed-off-by: Owen McGill <mcgillo.rnx@gmail.com>
2019-04-29 15:38:49 +02:00

442 lines
11 KiB
Go

package openssl
import (
"bytes"
"fmt"
"os/exec"
"testing"
)
func TestDecryptFromStringMD5(t *testing.T) {
// > echo -n "hallowelt" | openssl aes-256-cbc -pass pass:z4yH36a6zerhfE5427ZV -md md5 -a -salt
// U2FsdGVkX19ZM5qQJGe/d5A/4pccgH+arBGTp+QnWPU=
opensslEncrypted := "U2FsdGVkX19ZM5qQJGe/d5A/4pccgH+arBGTp+QnWPU="
passphrase := "z4yH36a6zerhfE5427ZV"
o := New()
data, err := o.DecryptBytes(passphrase, []byte(opensslEncrypted), DigestMD5Sum)
if err != nil {
t.Fatalf("Test errored: %s", err)
}
if string(data) != "hallowelt" {
t.Errorf("Decryption output did not equal expected output.")
}
}
func TestDecryptFromStringSHA1(t *testing.T) {
// > echo -n "hallowelt" | openssl aes-256-cbc -pass pass:z4yH36a6zerhfE5427ZV -md sha1 -a -salt
// U2FsdGVkX1/Yy9kegseq2Ewd4UvjFYCpIEA1cltTA1Q=
opensslEncrypted := "U2FsdGVkX1/Yy9kegseq2Ewd4UvjFYCpIEA1cltTA1Q="
passphrase := "z4yH36a6zerhfE5427ZV"
o := New()
data, err := o.DecryptBytes(passphrase, []byte(opensslEncrypted), DigestSHA1Sum)
if err != nil {
t.Fatalf("Test errored: %s", err)
}
if string(data) != "hallowelt" {
t.Errorf("Decryption output did not equal expected output.")
}
}
func TestDecryptFromStringSHA256(t *testing.T) {
// > echo -n "hallowelt" | openssl aes-256-cbc -pass pass:z4yH36a6zerhfE5427ZV -md sha256 -a -salt
// U2FsdGVkX1+O68d7BO9ibP8nB5+xtb/27IHlyjJWpl8=
opensslEncrypted := "U2FsdGVkX1+O68d7BO9ibP8nB5+xtb/27IHlyjJWpl8="
passphrase := "z4yH36a6zerhfE5427ZV"
o := New()
data, err := o.DecryptBytes(passphrase, []byte(opensslEncrypted), DigestSHA256Sum)
if err != nil {
t.Fatalf("Test errored: %s", err)
}
if string(data) != "hallowelt" {
t.Errorf("Decryption output did not equal expected output.")
}
}
func TestDecryptBinaryFromString(t *testing.T) {
plaintext := "hallowelt"
passphrase := "z4yH36a6zerhfE5427ZV"
testtable :=
[]struct {
tname string
tMdParam string
tMdFunc DigestFunc
}{
{
tname: "MD5",
tMdParam: "md5",
tMdFunc: DigestMD5Sum,
},
{
tname: "SHA1",
tMdParam: "sha1",
tMdFunc: DigestSHA1Sum,
},
{
tname: "SHA256",
tMdParam: "sha256",
tMdFunc: DigestSHA256Sum,
},
}
o := New()
for _, tc := range testtable {
t.Run(tc.tname, func(t *testing.T) {
cmd := exec.Command("/bin/bash", "-c", fmt.Sprintf("echo -n \"%s\" | openssl aes-256-cbc -pass pass:%s -md %s", plaintext, passphrase, tc.tMdParam))
var out bytes.Buffer
cmd.Stdout = &out
err := cmd.Run()
if err != nil {
t.Fatalf("Running openssl CLI failed: %v", err)
}
data, err := o.DecryptBinaryBytes(passphrase, out.Bytes(), tc.tMdFunc)
if err != nil {
t.Fatalf("Decryption failed: %v", err)
}
if string(data) != plaintext {
t.Logf("Data: %s\nPlaintext: %s", string(data), plaintext)
t.Errorf("Decryption output did not equal expected output.")
}
})
}
}
func TestEncryptToDecrypt(t *testing.T) {
plaintext := "hallowelt"
passphrase := "z4yH36a6zerhfE5427ZV"
o := New()
enc, err := o.EncryptBytes(passphrase, []byte(plaintext), DigestSHA256Sum)
if err != nil {
t.Fatalf("Test errored at encrypt: %s", err)
}
dec, err := o.DecryptBytes(passphrase, enc, DigestSHA256Sum)
if err != nil {
t.Fatalf("Test errored at decrypt: %s", err)
}
if string(dec) != plaintext {
t.Errorf("Decrypted text did not match input.")
}
}
func TestBinaryEncryptToDecrypt(t *testing.T) {
plaintext := "hallowelt"
passphrase := "z4yH36a6zerhfE5427ZV"
o := New()
enc, err := o.EncryptBinaryBytes(passphrase, []byte(plaintext), DigestSHA256Sum)
if err != nil {
t.Fatalf("Test errored at encrypt: %s", err)
}
dec, err := o.DecryptBinaryBytes(passphrase, enc, DigestSHA256Sum)
if err != nil {
t.Fatalf("Test errored at decrypt: %s", err)
}
if string(dec) != plaintext {
t.Errorf("Decrypted text did not match input.")
}
}
func TestEncryptToDecryptWithCustomSalt(t *testing.T) {
plaintext := "hallowelt"
passphrase := "z4yH36a6zerhfE5427ZV"
salt := []byte("saltsalt")
o := New()
enc, err := o.EncryptBytesWithSaltAndDigestFunc(passphrase, salt, []byte(plaintext), DigestSHA256Sum)
if err != nil {
t.Fatalf("Test errored at encrypt: %s", err)
}
dec, err := o.DecryptBytes(passphrase, enc, DigestSHA256Sum)
if err != nil {
t.Fatalf("Test errored at decrypt: %s", err)
}
if string(dec) != plaintext {
t.Errorf("Decrypted text did not match input.")
}
}
func TestBinaryEncryptToDecryptWithCustomSalt(t *testing.T) {
plaintext := "hallowelt"
passphrase := "z4yH36a6zerhfE5427ZV"
salt := []byte("saltsalt")
o := New()
enc, err := o.EncryptBinaryBytesWithSaltAndDigestFunc(passphrase, salt, []byte(plaintext), DigestSHA256Sum)
if err != nil {
t.Fatalf("Test errored at encrypt: %s", err)
}
dec, err := o.DecryptBinaryBytes(passphrase, enc, DigestSHA256Sum)
if err != nil {
t.Fatalf("Test errored at decrypt: %s", err)
}
if string(dec) != plaintext {
t.Errorf("Decrypted text did not match input.")
}
}
func TestEncryptWithSaltShouldHaveSameOutput(t *testing.T) {
plaintext := "outputshouldbesame"
passphrase := "passphrasesupersecure"
salt := []byte("saltsalt")
o := New()
enc1, err := o.EncryptBytesWithSaltAndDigestFunc(passphrase, salt, []byte(plaintext), DigestSHA256Sum)
if err != nil {
t.Fatalf("Test errored at encrypt: %s", err)
}
enc2, err := o.EncryptBytesWithSaltAndDigestFunc(passphrase, salt, []byte(plaintext), DigestSHA256Sum)
if err != nil {
t.Fatalf("Test errored at encrypt: %s", err)
}
if string(enc1) != string(enc2) {
t.Errorf("Encrypted outputs are not same.")
}
}
func TestBinaryEncryptWithSaltShouldHaveSameOutput(t *testing.T) {
plaintext := "outputshouldbesame"
passphrase := "passphrasesupersecure"
salt := []byte("saltsalt")
o := New()
enc1, err := o.EncryptBinaryBytesWithSaltAndDigestFunc(passphrase, salt, []byte(plaintext), DigestSHA256Sum)
if err != nil {
t.Fatalf("Test errored at encrypt: %s", err)
}
enc2, err := o.EncryptBinaryBytesWithSaltAndDigestFunc(passphrase, salt, []byte(plaintext), DigestSHA256Sum)
if err != nil {
t.Fatalf("Test errored at encrypt: %s", err)
}
if string(enc1) != string(enc2) {
t.Errorf("Encrypted outputs are not same.")
}
}
func TestEncryptToOpenSSL(t *testing.T) {
plaintext := "hallowelt"
passphrase := "z4yH36a6zerhfE5427ZV"
matrix := map[string]DigestFunc{
"md5": DigestMD5Sum,
"sha1": DigestSHA1Sum,
"sha256": DigestSHA256Sum,
}
for mdParam, hashFunc := range matrix {
o := New()
salt, err := o.GenerateSalt()
if err != nil {
t.Fatalf("Failed to generate salt: %s", err)
}
enc, err := o.EncryptBytesWithSaltAndDigestFunc(passphrase, salt, []byte(plaintext), hashFunc)
if err != nil {
t.Fatalf("Test errored at encrypt (%s): %s", mdParam, err)
}
// WTF? Without "echo" openssl tells us "error reading input file"
cmd := exec.Command("/bin/bash", "-c", fmt.Sprintf("echo \"%s\" | openssl aes-256-cbc -k %s -md %s -d -a", string(enc), passphrase, mdParam))
var out bytes.Buffer
cmd.Stdout = &out
err = cmd.Run()
if err != nil {
t.Errorf("OpenSSL errored (%s): %s", mdParam, err)
}
if out.String() != plaintext {
t.Errorf("OpenSSL output did not match input.\nOutput was (%s): %s", mdParam, out.String())
}
}
}
func TestBinaryEncryptToOpenSSL(t *testing.T) {
plaintext := "hallowelt"
passphrase := "z4yH36a6zerhfE5427ZV"
testtable :=
[]struct {
tname string
tMdParam string
tMdFunc DigestFunc
}{
{
tname: "MD5",
tMdParam: "md5",
tMdFunc: DigestMD5Sum,
},
{
tname: "SHA1",
tMdParam: "sha1",
tMdFunc: DigestSHA1Sum,
},
{
tname: "SHA256",
tMdParam: "sha256",
tMdFunc: DigestSHA256Sum,
},
}
o := New()
for _, tc := range testtable {
t.Run(tc.tname, func(t *testing.T) {
salt, err := o.GenerateSalt()
if err != nil {
t.Fatalf("Failed to generate salt: %v", err)
}
enc, err := o.EncryptBinaryBytesWithSaltAndDigestFunc(passphrase, salt, []byte(plaintext), tc.tMdFunc)
if err != nil {
t.Fatalf("Test errored at encrypt: %v", err)
}
// Need to specify /dev/stdin as file so that we can pass in binary
// data to openssl without creating a file
cmd := exec.Command("/bin/bash", "-c", fmt.Sprintf("openssl aes-256-cbc -pass pass:%s -md %s -d -in /dev/stdin", passphrase, tc.tMdParam))
var out bytes.Buffer
cmd.Stdout = &out
cmd.Stdin = bytes.NewBuffer(enc)
err = cmd.Run()
if err != nil {
t.Errorf("OpenSSL errored: %v", err)
}
if out.String() != plaintext {
t.Errorf("OpenSSL output did not match input.\nOutput was: %s", out.String())
}
})
}
}
func TestGenerateSalt(t *testing.T) {
knownSalts := [][]byte{}
o := New()
for i := 0; i < 10; i++ {
salt, err := o.GenerateSalt()
if err != nil {
t.Fatalf("Failed to generate salt: %s", err)
}
for _, ks := range knownSalts {
if bytes.Equal(ks, salt) {
t.Errorf("Duplicate salt detected")
}
knownSalts = append(knownSalts, salt)
}
}
}
func TestSaltValidation(t *testing.T) {
plaintext := "hallowelt"
passphrase := "z4yH36a6zerhfE5427ZV"
o := New()
if _, err := o.EncryptBytesWithSaltAndDigestFunc(passphrase, []byte("12345"), []byte(plaintext), DigestSHA256Sum); err != ErrInvalidSalt {
t.Errorf("5-character salt was accepted, needs to have 8 character")
}
if _, err := o.EncryptBytesWithSaltAndDigestFunc(passphrase, []byte("1234567890"), []byte(plaintext), DigestSHA256Sum); err != ErrInvalidSalt {
t.Errorf("10-character salt was accepted, needs to have 8 character")
}
if _, err := o.EncryptBytesWithSaltAndDigestFunc(passphrase, []byte{0xcb, 0xd5, 0x1a, 0x3, 0x84, 0xba, 0xa8, 0xc8}, []byte(plaintext), DigestSHA256Sum); err == ErrInvalidSalt {
t.Errorf("Salt with 8 byte unprintable characters was not accepted")
}
}
func benchmarkDecrypt(ciphertext []byte, kdf DigestFunc, b *testing.B) {
passphrase := "z4yH36a6zerhfE5427ZV"
o := New()
for n := 0; n < b.N; n++ {
o.DecryptBytes(passphrase, ciphertext, kdf)
}
}
func BenchmarkDecryptMD5(b *testing.B) {
benchmarkDecrypt([]byte("U2FsdGVkX19ZM5qQJGe/d5A/4pccgH+arBGTp+QnWPU="), DigestMD5Sum, b)
}
func BenchmarkDecryptSHA1(b *testing.B) {
benchmarkDecrypt([]byte("U2FsdGVkX1/Yy9kegseq2Ewd4UvjFYCpIEA1cltTA1Q="), DigestSHA1Sum, b)
}
func BenchmarkDecryptSHA256(b *testing.B) {
benchmarkDecrypt([]byte("U2FsdGVkX1+O68d7BO9ibP8nB5+xtb/27IHlyjJWpl8="), DigestSHA256Sum, b)
}
func benchmarkEncrypt(plaintext string, hashFunc DigestFunc, b *testing.B) {
passphrase := "z4yH36a6zerhfE5427ZV"
o := New()
salt, _ := o.GenerateSalt()
for n := 0; n < b.N; n++ {
o.EncryptBytesWithSaltAndDigestFunc(passphrase, salt, []byte(plaintext), hashFunc)
}
}
func BenchmarkEncryptMD5(b *testing.B) {
benchmarkEncrypt("hallowelt", DigestMD5Sum, b)
}
func BenchmarkEncryptSHA1(b *testing.B) {
benchmarkEncrypt("hallowelt", DigestSHA1Sum, b)
}
func BenchmarkEncryptSHA256(b *testing.B) {
benchmarkEncrypt("hallowelt", DigestSHA256Sum, b)
}
func BenchmarkGenerateSalt(b *testing.B) {
o := New()
for n := 0; n < b.N; n++ {
o.GenerateSalt()
}
}