mirror of
https://github.com/libp2p/go-openssl.git
synced 2025-01-14 02:30:08 +08:00
597b8983b0
Fix `staticcheck` issues: - S1028 use `fmt.Errorf` to construct formatted errors - ST1017 yoda conditions - ST1005 error message capitalization - ST1006 avoid `self` as receiver name - S1030 use `buf.String` - S1011 avoid redundant loop when `append` suffices - SA4006 unused value - S1019 remove redundant capacity on `make` call - SA2002 `t.Fatal` called outside of test Exported error violates ST1012, which is ignored by this PR since rename may cause breaking changes. Remove redundant parentheses wrapping, and use CamelCase naming while at it.
303 lines
8.9 KiB
Go
303 lines
8.9 KiB
Go
// Copyright (C) 2017. See AUTHORS.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
package openssl
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
func expectError(t *testing.T, err error, msg string) {
|
|
if err == nil {
|
|
t.Fatalf("Expected error containing %#v, but got none", msg)
|
|
}
|
|
if !strings.Contains(err.Error(), msg) {
|
|
t.Fatalf("Expected error containing %#v, but got %s", msg, err)
|
|
}
|
|
}
|
|
|
|
func TestBadInputs(t *testing.T) {
|
|
_, err := NewGCMEncryptionCipherCtx(256, nil,
|
|
[]byte("abcdefghijklmnopqrstuvwxyz"), nil)
|
|
expectError(t, err, "bad key size")
|
|
_, err = NewGCMEncryptionCipherCtx(128, nil,
|
|
[]byte("abcdefghijklmnopqrstuvwxyz"), nil)
|
|
expectError(t, err, "bad key size")
|
|
_, err = NewGCMEncryptionCipherCtx(200, nil,
|
|
[]byte("abcdefghijklmnopqrstuvwxy"), nil)
|
|
expectError(t, err, "unknown block size")
|
|
c, err := GetCipherByName("AES-128-CBC")
|
|
if err != nil {
|
|
t.Fatal("Could not look up AES-128-CBC")
|
|
}
|
|
_, err = NewEncryptionCipherCtx(c, nil, []byte("abcdefghijklmnop"),
|
|
[]byte("abc"))
|
|
expectError(t, err, "bad IV size")
|
|
}
|
|
|
|
func doEncryption(key, iv, aad, plaintext []byte, blocksize, bufsize int) (
|
|
ciphertext, tag []byte, err error) {
|
|
ectx, err := NewGCMEncryptionCipherCtx(blocksize, nil, key, iv)
|
|
if err != nil {
|
|
return nil, nil, fmt.Errorf("Failed making GCM encryption ctx: %s", err)
|
|
}
|
|
err = ectx.ExtraData(aad)
|
|
if err != nil {
|
|
return nil, nil, fmt.Errorf("Failed to add authenticated data: %s",
|
|
err)
|
|
}
|
|
plainb := bytes.NewBuffer(plaintext)
|
|
cipherb := new(bytes.Buffer)
|
|
for plainb.Len() > 0 {
|
|
moar, err := ectx.EncryptUpdate(plainb.Next(bufsize))
|
|
if err != nil {
|
|
return nil, nil, fmt.Errorf("Failed to perform an encryption: %s",
|
|
err)
|
|
}
|
|
cipherb.Write(moar)
|
|
}
|
|
moar, err := ectx.EncryptFinal()
|
|
if err != nil {
|
|
return nil, nil, fmt.Errorf("Failed to finalize encryption: %s", err)
|
|
}
|
|
cipherb.Write(moar)
|
|
tag, err = ectx.GetTag()
|
|
if err != nil {
|
|
return nil, nil, fmt.Errorf("Failed to get GCM tag: %s", err)
|
|
}
|
|
return cipherb.Bytes(), tag, nil
|
|
}
|
|
|
|
func doDecryption(key, iv, aad, ciphertext, tag []byte, blocksize,
|
|
bufsize int) (plaintext []byte, err error) {
|
|
dctx, err := NewGCMDecryptionCipherCtx(blocksize, nil, key, iv)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("Failed making GCM decryption ctx: %s", err)
|
|
}
|
|
aadbuf := bytes.NewBuffer(aad)
|
|
for aadbuf.Len() > 0 {
|
|
err = dctx.ExtraData(aadbuf.Next(bufsize))
|
|
if err != nil {
|
|
return nil, fmt.Errorf("Failed to add authenticated data: %s", err)
|
|
}
|
|
}
|
|
plainb := new(bytes.Buffer)
|
|
cipherb := bytes.NewBuffer(ciphertext)
|
|
for cipherb.Len() > 0 {
|
|
moar, err := dctx.DecryptUpdate(cipherb.Next(bufsize))
|
|
if err != nil {
|
|
return nil, fmt.Errorf("Failed to perform a decryption: %s", err)
|
|
}
|
|
plainb.Write(moar)
|
|
}
|
|
err = dctx.SetTag(tag)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("Failed to set expected GCM tag: %s", err)
|
|
}
|
|
moar, err := dctx.DecryptFinal()
|
|
if err != nil {
|
|
return nil, fmt.Errorf("Failed to finalize decryption: %s", err)
|
|
}
|
|
plainb.Write(moar)
|
|
return plainb.Bytes(), nil
|
|
}
|
|
|
|
func checkEqual(t *testing.T, output []byte, original string) {
|
|
output_s := string(output)
|
|
if output_s != original {
|
|
t.Fatalf("output != original! %#v != %#v", output_s, original)
|
|
}
|
|
}
|
|
|
|
func TestGCM(t *testing.T) {
|
|
aad := []byte("foo bar baz")
|
|
key := []byte("nobody can guess this i'm sure..") // len=32
|
|
iv := []byte("just a bunch of bytes")
|
|
plaintext := "Long long ago, in a land far away..."
|
|
|
|
blocksizes_to_test := []int{256, 192, 128}
|
|
|
|
// best for this to have no common factors with blocksize, so that the
|
|
// buffering layer inside the CIPHER_CTX gets exercised
|
|
bufsize := 33
|
|
|
|
if len(plaintext)%8 == 0 {
|
|
plaintext += "!" // make sure padding is exercised
|
|
}
|
|
|
|
for _, bsize := range blocksizes_to_test {
|
|
subkey := key[:bsize/8]
|
|
ciphertext, tag, err := doEncryption(subkey, iv, aad, []byte(plaintext),
|
|
bsize, bufsize)
|
|
if err != nil {
|
|
t.Fatalf("Encryption with b=%d: %s", bsize, err)
|
|
}
|
|
plaintext_out, err := doDecryption(subkey, iv, aad, ciphertext, tag,
|
|
bsize, bufsize)
|
|
if err != nil {
|
|
t.Fatalf("Decryption with b=%d: %s", bsize, err)
|
|
}
|
|
checkEqual(t, plaintext_out, plaintext)
|
|
}
|
|
}
|
|
|
|
func TestGCMWithNoAAD(t *testing.T) {
|
|
key := []byte("0000111122223333")
|
|
iv := []byte("9999")
|
|
plaintext := "ABORT ABORT ABORT DANGAR"
|
|
|
|
ciphertext, tag, err := doEncryption(key, iv, nil, []byte(plaintext),
|
|
128, 32)
|
|
if err != nil {
|
|
t.Fatal("Encryption failure:", err)
|
|
}
|
|
plaintext_out, err := doDecryption(key, iv, nil, ciphertext, tag, 128, 129)
|
|
if err != nil {
|
|
t.Fatal("Decryption failure:", err)
|
|
}
|
|
checkEqual(t, plaintext_out, plaintext)
|
|
}
|
|
|
|
func TestBadTag(t *testing.T) {
|
|
key := []byte("abcdefghijklmnop")
|
|
iv := []byte("v7239qjfv3qr793fuaj")
|
|
plaintext := "The red rooster has flown the coop I REPEAT" +
|
|
"the red rooster has flown the coop!!1!"
|
|
|
|
ciphertext, tag, err := doEncryption(key, iv, nil, []byte(plaintext),
|
|
128, 32)
|
|
if err != nil {
|
|
t.Fatal("Encryption failure:", err)
|
|
}
|
|
// flip the last bit
|
|
tag[len(tag)-1] ^= 1
|
|
if _, err := doDecryption(key, iv, nil, ciphertext, tag, 128, 129); err == nil {
|
|
t.Fatal("Expected error for bad tag, but got none")
|
|
}
|
|
// flip it back, try again just to make sure
|
|
tag[len(tag)-1] ^= 1
|
|
plaintextOut, err := doDecryption(key, iv, nil, ciphertext, tag, 128, 129)
|
|
if err != nil {
|
|
t.Fatal("Decryption failure:", err)
|
|
}
|
|
checkEqual(t, plaintextOut, plaintext)
|
|
}
|
|
|
|
func TestBadCiphertext(t *testing.T) {
|
|
key := []byte("hard boiled eggs & bacon")
|
|
iv := []byte("x") // it's not a very /good/ IV, is it
|
|
aad := []byte("mu")
|
|
plaintext := "Roger roger bingo charlie, we have a niner fourteen tango"
|
|
|
|
ciphertext, tag, err := doEncryption(key, iv, aad, []byte(plaintext),
|
|
192, 1)
|
|
if err != nil {
|
|
t.Fatal("Encryption failure:", err)
|
|
}
|
|
// flip the last bit
|
|
ciphertext[len(ciphertext)-1] ^= 1
|
|
if _, err := doDecryption(key, iv, aad, ciphertext, tag, 192, 192); err == nil {
|
|
t.Fatal("Expected error for bad ciphertext, but got none")
|
|
}
|
|
// flip it back, try again just to make sure
|
|
ciphertext[len(ciphertext)-1] ^= 1
|
|
plaintextOut, err := doDecryption(key, iv, aad, ciphertext, tag, 192, 192)
|
|
if err != nil {
|
|
t.Fatal("Decryption failure:", err)
|
|
}
|
|
checkEqual(t, plaintextOut, plaintext)
|
|
}
|
|
|
|
func TestBadAAD(t *testing.T) {
|
|
key := []byte("Ive got a lovely buncha coconuts")
|
|
iv := []byte("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab")
|
|
aad := []byte("Hi i am a plain")
|
|
plaintext := "Whatever."
|
|
|
|
ciphertext, tag, err := doEncryption(key, iv, aad, []byte(plaintext),
|
|
256, 256)
|
|
if err != nil {
|
|
t.Fatal("Encryption failure:", err)
|
|
}
|
|
// flip the last bit
|
|
aad[len(aad)-1] ^= 1
|
|
if _, err := doDecryption(key, iv, aad, ciphertext, tag, 256, 256); err == nil {
|
|
t.Fatal("Expected error for bad AAD, but got none")
|
|
}
|
|
// flip it back, try again just to make sure
|
|
aad[len(aad)-1] ^= 1
|
|
plaintextOut, err := doDecryption(key, iv, aad, ciphertext, tag, 256, 256)
|
|
if err != nil {
|
|
t.Fatal("Decryption failure:", err)
|
|
}
|
|
checkEqual(t, plaintextOut, plaintext)
|
|
}
|
|
|
|
func TestNonAuthenticatedEncryption(t *testing.T) {
|
|
key := []byte("never gonna give you up, never g")
|
|
iv := []byte("onna let you dow")
|
|
plaintext1 := "n, never gonna run around"
|
|
plaintext2 := " and desert you"
|
|
|
|
cipher, err := GetCipherByName("aes-256-cbc")
|
|
if err != nil {
|
|
t.Fatal("Could not get cipher: ", err)
|
|
}
|
|
|
|
eCtx, err := NewEncryptionCipherCtx(cipher, nil, key, iv)
|
|
if err != nil {
|
|
t.Fatal("Could not create encryption context: ", err)
|
|
}
|
|
cipherbytes, err := eCtx.EncryptUpdate([]byte(plaintext1))
|
|
if err != nil {
|
|
t.Fatal("EncryptUpdate(plaintext1) failure: ", err)
|
|
}
|
|
ciphertext := string(cipherbytes)
|
|
cipherbytes, err = eCtx.EncryptUpdate([]byte(plaintext2))
|
|
if err != nil {
|
|
t.Fatal("EncryptUpdate(plaintext2) failure: ", err)
|
|
}
|
|
ciphertext += string(cipherbytes)
|
|
cipherbytes, err = eCtx.EncryptFinal()
|
|
if err != nil {
|
|
t.Fatal("EncryptFinal() failure: ", err)
|
|
}
|
|
ciphertext += string(cipherbytes)
|
|
|
|
dCtx, err := NewDecryptionCipherCtx(cipher, nil, key, iv)
|
|
if err != nil {
|
|
t.Fatal("Could not create decryption context: ", err)
|
|
}
|
|
plainbytes, err := dCtx.DecryptUpdate([]byte(ciphertext[:15]))
|
|
if err != nil {
|
|
t.Fatal("DecryptUpdate(ciphertext part 1) failure: ", err)
|
|
}
|
|
plainOutput := string(plainbytes)
|
|
plainbytes, err = dCtx.DecryptUpdate([]byte(ciphertext[15:]))
|
|
if err != nil {
|
|
t.Fatal("DecryptUpdate(ciphertext part 2) failure: ", err)
|
|
}
|
|
plainOutput += string(plainbytes)
|
|
plainbytes, err = dCtx.DecryptFinal()
|
|
if err != nil {
|
|
t.Fatal("DecryptFinal() failure: ", err)
|
|
}
|
|
plainOutput += string(plainbytes)
|
|
|
|
checkEqual(t, []byte(plainOutput), plaintext1+plaintext2)
|
|
}
|