mirror of
https://github.com/libp2p/go-openssl.git
synced 2025-04-24 17:50:13 +08:00
space monkey internal commit export
[katamari commit: 0a645f84cf1bd058f7533e8eaceba32c75fae90e]
This commit is contained in:
parent
e7e20b5376
commit
12a1657a95
465
ciphers.go
Normal file
465
ciphers.go
Normal file
@ -0,0 +1,465 @@
|
||||
// Copyright (C) 2014 Space Monkey, Inc.
|
||||
// +build cgo
|
||||
|
||||
package openssl
|
||||
|
||||
// #include <openssl/evp.h>
|
||||
//
|
||||
// int EVP_CIPHER_block_size_not_a_macro(EVP_CIPHER *c) {
|
||||
// return EVP_CIPHER_block_size(c);
|
||||
// }
|
||||
//
|
||||
// int EVP_CIPHER_key_length_not_a_macro(EVP_CIPHER *c) {
|
||||
// return EVP_CIPHER_key_length(c);
|
||||
// }
|
||||
//
|
||||
// int EVP_CIPHER_iv_length_not_a_macro(EVP_CIPHER *c) {
|
||||
// return EVP_CIPHER_iv_length(c);
|
||||
// }
|
||||
//
|
||||
// int EVP_CIPHER_nid_not_a_macro(EVP_CIPHER *c) {
|
||||
// return EVP_CIPHER_nid(c);
|
||||
// }
|
||||
//
|
||||
// int EVP_CIPHER_CTX_block_size_not_a_macro(EVP_CIPHER_CTX *ctx) {
|
||||
// return EVP_CIPHER_CTX_block_size(ctx);
|
||||
// }
|
||||
//
|
||||
// int EVP_CIPHER_CTX_key_length_not_a_macro(EVP_CIPHER_CTX *ctx) {
|
||||
// return EVP_CIPHER_CTX_key_length(ctx);
|
||||
// }
|
||||
//
|
||||
// int EVP_CIPHER_CTX_iv_length_not_a_macro(EVP_CIPHER_CTX *ctx) {
|
||||
// return EVP_CIPHER_CTX_iv_length(ctx);
|
||||
// }
|
||||
//
|
||||
// const EVP_CIPHER *EVP_CIPHER_CTX_cipher_not_a_macro(EVP_CIPHER_CTX *ctx) {
|
||||
// return EVP_CIPHER_CTX_cipher(ctx);
|
||||
// }
|
||||
import "C"
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"runtime"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
const (
|
||||
GCM_TAG_MAXLEN = 16
|
||||
)
|
||||
|
||||
type Cipher struct {
|
||||
ptr *C.EVP_CIPHER
|
||||
}
|
||||
|
||||
type cipherCtx struct {
|
||||
ctx *C.EVP_CIPHER_CTX
|
||||
}
|
||||
|
||||
type encryptionCipherCtx struct {
|
||||
cipherCtx
|
||||
}
|
||||
|
||||
type decryptionCipherCtx struct {
|
||||
cipherCtx
|
||||
}
|
||||
|
||||
type CipherCtx interface {
|
||||
Cipher() *Cipher
|
||||
BlockSize() int
|
||||
KeySize() int
|
||||
IVSize() int
|
||||
}
|
||||
|
||||
type EncryptionCipherCtx interface {
|
||||
CipherCtx
|
||||
|
||||
// pass in plaintext, get back ciphertext. can be called
|
||||
// multiple times as needed
|
||||
EncryptUpdate(input []byte) ([]byte, error)
|
||||
|
||||
// call after all plaintext has been passed in; may return
|
||||
// additional ciphertext if needed to finish off a block
|
||||
// or extra padding information
|
||||
EncryptFinal() ([]byte, error)
|
||||
}
|
||||
|
||||
type DecryptionCipherCtx interface {
|
||||
CipherCtx
|
||||
|
||||
// pass in ciphertext, get back plaintext. can be called
|
||||
// multiple times as needed
|
||||
DecryptUpdate(input []byte) ([]byte, error)
|
||||
|
||||
// call after all ciphertext has been passed in; may return
|
||||
// additional plaintext if needed to finish off a block
|
||||
DecryptFinal() ([]byte, error)
|
||||
}
|
||||
|
||||
type AuthenticatedEncryptionCipherCtx interface {
|
||||
EncryptionCipherCtx
|
||||
|
||||
// data passed in to ExtraData() is part of the final output; it is
|
||||
// not encrypted itself, but is part of the authenticated data. when
|
||||
// decrypting or authenticating, pass back with the decryption
|
||||
// context's ExtraData()
|
||||
ExtraData([]byte) error
|
||||
|
||||
// use after finalizing encryption to get the authenticating tag
|
||||
GetTag() ([]byte, error)
|
||||
}
|
||||
|
||||
type AuthenticatedDecryptionCipherCtx interface {
|
||||
DecryptionCipherCtx
|
||||
|
||||
// pass in any extra data that was added during encryption with the
|
||||
// encryption context's ExtraData()
|
||||
ExtraData([]byte) error
|
||||
|
||||
// use before finalizing decryption to tell the library what the
|
||||
// tag is expected to be
|
||||
SetTag([]byte) error
|
||||
}
|
||||
|
||||
func Nid2ShortName(nid int) (string, error) {
|
||||
sn := C.OBJ_nid2sn(C.int(nid))
|
||||
if sn == nil {
|
||||
return "", fmt.Errorf("NID %d not found", nid)
|
||||
}
|
||||
return C.GoString(sn), nil
|
||||
}
|
||||
|
||||
func GetCipherByName(name string) (*Cipher, error) {
|
||||
cname := C.CString(name)
|
||||
defer C.free(unsafe.Pointer(cname))
|
||||
p := C.EVP_get_cipherbyname(cname)
|
||||
if p == nil {
|
||||
return nil, fmt.Errorf("Cipher %v not found", name)
|
||||
}
|
||||
// we can consider ciphers to use static mem; don't need to free
|
||||
return &Cipher{ptr: p}, nil
|
||||
}
|
||||
|
||||
func GetCipherByNid(nid int) (*Cipher, error) {
|
||||
sn, err := Nid2ShortName(nid)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return GetCipherByName(sn)
|
||||
}
|
||||
|
||||
func (c Cipher) Nid() int {
|
||||
return int(C.EVP_CIPHER_nid_not_a_macro(c.ptr))
|
||||
}
|
||||
|
||||
func (c Cipher) ShortName() (string, error) {
|
||||
return Nid2ShortName(c.Nid())
|
||||
}
|
||||
|
||||
func (c Cipher) BlockSize() int {
|
||||
return int(C.EVP_CIPHER_block_size_not_a_macro(c.ptr))
|
||||
}
|
||||
|
||||
func (c Cipher) KeySize() int {
|
||||
return int(C.EVP_CIPHER_key_length_not_a_macro(c.ptr))
|
||||
}
|
||||
|
||||
func (c Cipher) IVSize() int {
|
||||
return int(C.EVP_CIPHER_iv_length_not_a_macro(c.ptr))
|
||||
}
|
||||
|
||||
func newCipherCtx() (*cipherCtx, error) {
|
||||
cctx := C.EVP_CIPHER_CTX_new()
|
||||
if cctx == nil {
|
||||
return nil, errors.New("failed to allocate cipher context")
|
||||
}
|
||||
ctx := &cipherCtx{cctx}
|
||||
runtime.SetFinalizer(ctx, func(ctx *cipherCtx) {
|
||||
C.EVP_CIPHER_CTX_free(ctx.ctx)
|
||||
})
|
||||
return ctx, nil
|
||||
}
|
||||
|
||||
func (ctx *cipherCtx) applyKeyAndIV(key, iv []byte) error {
|
||||
var kptr, iptr *C.uchar
|
||||
if key != nil {
|
||||
if len(key) != ctx.KeySize() {
|
||||
return fmt.Errorf("bad key size (%d bytes instead of %d)",
|
||||
len(key), ctx.KeySize())
|
||||
}
|
||||
kptr = (*C.uchar)(&key[0])
|
||||
}
|
||||
if iv != nil {
|
||||
if len(iv) != ctx.IVSize() {
|
||||
return fmt.Errorf("bad IV size (%d bytes instead of %d)",
|
||||
len(iv), ctx.IVSize())
|
||||
}
|
||||
iptr = (*C.uchar)(&iv[0])
|
||||
}
|
||||
if kptr != nil || iptr != nil {
|
||||
if 1 != C.EVP_EncryptInit_ex(ctx.ctx, nil, nil, kptr, iptr) {
|
||||
return errors.New("failed to apply key/IV")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func newEncryptionCipherCtx(c *Cipher, e *Engine, key, iv []byte) (
|
||||
*encryptionCipherCtx, error) {
|
||||
if c == nil {
|
||||
return nil, errors.New("null cipher not allowed")
|
||||
}
|
||||
ctx, err := newCipherCtx()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var eptr *C.ENGINE
|
||||
if e != nil {
|
||||
eptr = e.e
|
||||
}
|
||||
if 1 != C.EVP_EncryptInit_ex(ctx.ctx, c.ptr, eptr, nil, nil) {
|
||||
return nil, errors.New("failed to initialize cipher context")
|
||||
}
|
||||
err = ctx.applyKeyAndIV(key, iv)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &encryptionCipherCtx{*ctx}, nil
|
||||
}
|
||||
|
||||
func newDecryptionCipherCtx(c *Cipher, e *Engine, key, iv []byte) (
|
||||
*decryptionCipherCtx, error) {
|
||||
if c == nil {
|
||||
return nil, errors.New("null cipher not allowed")
|
||||
}
|
||||
ctx, err := newCipherCtx()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var eptr *C.ENGINE
|
||||
if e != nil {
|
||||
eptr = e.e
|
||||
}
|
||||
if 1 != C.EVP_DecryptInit_ex(ctx.ctx, c.ptr, eptr, nil, nil) {
|
||||
return nil, errors.New("failed to initialize cipher context")
|
||||
}
|
||||
err = ctx.applyKeyAndIV(key, iv)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &decryptionCipherCtx{*ctx}, nil
|
||||
}
|
||||
|
||||
func NewEncryptionCipherCtx(c *Cipher, e *Engine, key, iv []byte) (
|
||||
EncryptionCipherCtx, error) {
|
||||
return newEncryptionCipherCtx(c, e, key, iv)
|
||||
}
|
||||
|
||||
func NewDecryptionCipherCtx(c *Cipher, e *Engine, key, iv []byte) (
|
||||
DecryptionCipherCtx, error) {
|
||||
return newDecryptionCipherCtx(c, e, key, iv)
|
||||
}
|
||||
|
||||
func (ctx *cipherCtx) Cipher() *Cipher {
|
||||
return &Cipher{ptr: C.EVP_CIPHER_CTX_cipher_not_a_macro(ctx.ctx)}
|
||||
}
|
||||
|
||||
func (ctx *cipherCtx) BlockSize() int {
|
||||
return int(C.EVP_CIPHER_CTX_block_size_not_a_macro(ctx.ctx))
|
||||
}
|
||||
|
||||
func (ctx *cipherCtx) KeySize() int {
|
||||
return int(C.EVP_CIPHER_CTX_key_length_not_a_macro(ctx.ctx))
|
||||
}
|
||||
|
||||
func (ctx *cipherCtx) IVSize() int {
|
||||
return int(C.EVP_CIPHER_CTX_iv_length_not_a_macro(ctx.ctx))
|
||||
}
|
||||
|
||||
func (ctx *cipherCtx) setCtrl(code, arg int) error {
|
||||
res := C.EVP_CIPHER_CTX_ctrl(ctx.ctx, C.int(code), C.int(arg), nil)
|
||||
if res != 1 {
|
||||
return fmt.Errorf("failed to set code %d to %d [result %d]",
|
||||
code, arg, res)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ctx *cipherCtx) setCtrlBytes(code, arg int, value []byte) error {
|
||||
res := C.EVP_CIPHER_CTX_ctrl(ctx.ctx, C.int(code), C.int(arg),
|
||||
unsafe.Pointer(&value[0]))
|
||||
if res != 1 {
|
||||
return fmt.Errorf("failed to set code %d with arg %d to %x [result %d]",
|
||||
code, arg, value, res)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ctx *cipherCtx) getCtrlInt(code, arg int) (int, error) {
|
||||
var returnVal C.int
|
||||
res := C.EVP_CIPHER_CTX_ctrl(ctx.ctx, C.int(code), C.int(arg),
|
||||
unsafe.Pointer(&returnVal))
|
||||
if res != 1 {
|
||||
return 0, fmt.Errorf("failed to get code %d with arg %d [result %d]",
|
||||
code, arg, res)
|
||||
}
|
||||
return int(returnVal), nil
|
||||
}
|
||||
|
||||
func (ctx *cipherCtx) getCtrlBytes(code, arg, expectsize int) ([]byte, error) {
|
||||
returnVal := make([]byte, expectsize)
|
||||
res := C.EVP_CIPHER_CTX_ctrl(ctx.ctx, C.int(code), C.int(arg),
|
||||
unsafe.Pointer(&returnVal[0]))
|
||||
if res != 1 {
|
||||
return nil, fmt.Errorf("failed to get code %d with arg %d [result %d]",
|
||||
code, arg, res)
|
||||
}
|
||||
return returnVal, nil
|
||||
}
|
||||
|
||||
func (ctx *encryptionCipherCtx) EncryptUpdate(input []byte) ([]byte, error) {
|
||||
outbuf := make([]byte, len(input)+ctx.BlockSize())
|
||||
outlen := C.int(len(outbuf))
|
||||
res := C.EVP_EncryptUpdate(ctx.ctx, (*C.uchar)(&outbuf[0]), &outlen,
|
||||
(*C.uchar)(&input[0]), C.int(len(input)))
|
||||
if res != 1 {
|
||||
return nil, fmt.Errorf("failed to encrypt [result %d]", res)
|
||||
}
|
||||
return outbuf[:outlen], nil
|
||||
}
|
||||
|
||||
func (ctx *decryptionCipherCtx) DecryptUpdate(input []byte) ([]byte, error) {
|
||||
outbuf := make([]byte, len(input)+ctx.BlockSize())
|
||||
outlen := C.int(len(outbuf))
|
||||
res := C.EVP_DecryptUpdate(ctx.ctx, (*C.uchar)(&outbuf[0]), &outlen,
|
||||
(*C.uchar)(&input[0]), C.int(len(input)))
|
||||
if res != 1 {
|
||||
return nil, fmt.Errorf("failed to decrypt [result %d]", res)
|
||||
}
|
||||
return outbuf[:outlen], nil
|
||||
}
|
||||
|
||||
func (ctx *encryptionCipherCtx) EncryptFinal() ([]byte, error) {
|
||||
outbuf := make([]byte, ctx.BlockSize())
|
||||
var outlen C.int
|
||||
if 1 != C.EVP_EncryptFinal_ex(ctx.ctx, (*C.uchar)(&outbuf[0]), &outlen) {
|
||||
return nil, errors.New("encryption failed")
|
||||
}
|
||||
return outbuf[:outlen], nil
|
||||
}
|
||||
|
||||
func (ctx *decryptionCipherCtx) DecryptFinal() ([]byte, error) {
|
||||
outbuf := make([]byte, ctx.BlockSize())
|
||||
var outlen C.int
|
||||
if 1 != C.EVP_DecryptFinal_ex(ctx.ctx, (*C.uchar)(&outbuf[0]), &outlen) {
|
||||
// this may mean the tag failed to verify- all previous plaintext
|
||||
// returned must be considered faked and invalid
|
||||
return nil, errors.New("decryption failed")
|
||||
}
|
||||
return outbuf[:outlen], nil
|
||||
}
|
||||
|
||||
type authEncryptionCipherCtx struct {
|
||||
encryptionCipherCtx
|
||||
}
|
||||
|
||||
type authDecryptionCipherCtx struct {
|
||||
decryptionCipherCtx
|
||||
}
|
||||
|
||||
func getGCMCipher(blocksize int) (*Cipher, error) {
|
||||
var cipherptr *C.EVP_CIPHER
|
||||
switch blocksize {
|
||||
case 256:
|
||||
cipherptr = C.EVP_aes_256_gcm()
|
||||
case 192:
|
||||
cipherptr = C.EVP_aes_192_gcm()
|
||||
case 128:
|
||||
cipherptr = C.EVP_aes_128_gcm()
|
||||
default:
|
||||
return nil, fmt.Errorf("unknown block size %d", blocksize)
|
||||
}
|
||||
return &Cipher{ptr: cipherptr}, nil
|
||||
}
|
||||
|
||||
func NewGCMEncryptionCipherCtx(blocksize int, e *Engine, key, iv []byte) (
|
||||
AuthenticatedEncryptionCipherCtx, error) {
|
||||
cipher, err := getGCMCipher(blocksize)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ctx, err := newEncryptionCipherCtx(cipher, e, key, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if iv != nil {
|
||||
err := ctx.setCtrl(C.EVP_CTRL_GCM_SET_IVLEN, len(iv))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not set IV len to %d: %s",
|
||||
len(iv), err)
|
||||
}
|
||||
if 1 != C.EVP_EncryptInit_ex(ctx.ctx, nil, nil, nil,
|
||||
(*C.uchar)(&iv[0])) {
|
||||
return nil, errors.New("failed to apply IV")
|
||||
}
|
||||
}
|
||||
return &authEncryptionCipherCtx{*ctx}, nil
|
||||
}
|
||||
|
||||
func NewGCMDecryptionCipherCtx(blocksize int, e *Engine, key, iv []byte) (
|
||||
AuthenticatedDecryptionCipherCtx, error) {
|
||||
cipher, err := getGCMCipher(blocksize)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ctx, err := newDecryptionCipherCtx(cipher, e, key, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if iv != nil {
|
||||
err := ctx.setCtrl(C.EVP_CTRL_GCM_SET_IVLEN, len(iv))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not set IV len to %d: %s",
|
||||
len(iv), err)
|
||||
}
|
||||
if 1 != C.EVP_DecryptInit_ex(ctx.ctx, nil, nil, nil,
|
||||
(*C.uchar)(&iv[0])) {
|
||||
return nil, errors.New("failed to apply IV")
|
||||
}
|
||||
}
|
||||
return &authDecryptionCipherCtx{*ctx}, nil
|
||||
}
|
||||
|
||||
func (ctx *authEncryptionCipherCtx) ExtraData(aad []byte) error {
|
||||
if aad == nil {
|
||||
return nil
|
||||
}
|
||||
var outlen C.int
|
||||
if 1 != C.EVP_EncryptUpdate(ctx.ctx, nil, &outlen, (*C.uchar)(&aad[0]),
|
||||
C.int(len(aad))) {
|
||||
return errors.New("failed to add additional authenticated data")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ctx *authDecryptionCipherCtx) ExtraData(aad []byte) error {
|
||||
if aad == nil {
|
||||
return nil
|
||||
}
|
||||
var outlen C.int
|
||||
if 1 != C.EVP_DecryptUpdate(ctx.ctx, nil, &outlen, (*C.uchar)(&aad[0]),
|
||||
C.int(len(aad))) {
|
||||
return errors.New("failed to add additional authenticated data")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ctx *authEncryptionCipherCtx) GetTag() ([]byte, error) {
|
||||
return ctx.getCtrlBytes(C.EVP_CTRL_GCM_GET_TAG, GCM_TAG_MAXLEN,
|
||||
GCM_TAG_MAXLEN)
|
||||
}
|
||||
|
||||
func (ctx *authDecryptionCipherCtx) SetTag(tag []byte) error {
|
||||
return ctx.setCtrlBytes(C.EVP_CTRL_GCM_SET_TAG, len(tag), tag)
|
||||
}
|
239
ciphers_test.go
Normal file
239
ciphers_test.go
Normal file
@ -0,0 +1,239 @@
|
||||
// Copyright (C) 2014 Space Monkey, Inc.
|
||||
|
||||
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
|
||||
plaintext_out, err := doDecryption(key, iv, nil, ciphertext, tag, 128, 129)
|
||||
if 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
|
||||
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 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
|
||||
plaintext_out, err := doDecryption(key, iv, aad, ciphertext, tag, 192, 192)
|
||||
if 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
|
||||
plaintext_out, err = doDecryption(key, iv, aad, ciphertext, tag, 192, 192)
|
||||
if err != nil {
|
||||
t.Fatal("Decryption failure:", err)
|
||||
}
|
||||
checkEqual(t, plaintext_out, 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
|
||||
plaintext_out, err := doDecryption(key, iv, aad, ciphertext, tag, 256, 256)
|
||||
if 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
|
||||
plaintext_out, err = doDecryption(key, iv, aad, ciphertext, tag, 256, 256)
|
||||
if err != nil {
|
||||
t.Fatal("Decryption failure:", err)
|
||||
}
|
||||
checkEqual(t, plaintext_out, plaintext)
|
||||
}
|
Loading…
Reference in New Issue
Block a user