// 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 // #include "shim.h" import "C" import ( "errors" "fmt" "runtime" "unsafe" ) const ( GCM_TAG_MAXLEN = 16 ) type CipherCtx interface { Cipher() *Cipher BlockSize() int KeySize() int IVSize() int } type Cipher struct { ptr *C.EVP_CIPHER } func (c *Cipher) Nid() NID { return NID(C.X_EVP_CIPHER_nid(c.ptr)) } func (c *Cipher) ShortName() (string, error) { return Nid2ShortName(c.Nid()) } func (c *Cipher) BlockSize() int { return int(C.X_EVP_CIPHER_block_size(c.ptr)) } func (c *Cipher) KeySize() int { return int(C.X_EVP_CIPHER_key_length(c.ptr)) } func (c *Cipher) IVSize() int { return int(C.X_EVP_CIPHER_iv_length(c.ptr)) } func Nid2ShortName(nid NID) (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 NID) (*Cipher, error) { sn, err := Nid2ShortName(nid) if err != nil { return nil, err } return GetCipherByName(sn) } type cipherCtx struct { ctx *C.EVP_CIPHER_CTX } 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 { var res C.int if C.X_EVP_CIPHER_CTX_encrypting(ctx.ctx) != 0 { res = C.EVP_EncryptInit_ex(ctx.ctx, nil, nil, kptr, iptr) } else { res = C.EVP_DecryptInit_ex(ctx.ctx, nil, nil, kptr, iptr) } if 1 != res { return errors.New("failed to apply key/IV") } } return nil } func (ctx *cipherCtx) Cipher() *Cipher { return &Cipher{ptr: C.X_EVP_CIPHER_CTX_cipher(ctx.ctx)} } func (ctx *cipherCtx) BlockSize() int { return int(C.X_EVP_CIPHER_CTX_block_size(ctx.ctx)) } func (ctx *cipherCtx) KeySize() int { return int(C.X_EVP_CIPHER_CTX_key_length(ctx.ctx)) } func (ctx *cipherCtx) IVSize() int { return int(C.X_EVP_CIPHER_CTX_iv_length(ctx.ctx)) } func (ctx *cipherCtx) SetPadding(pad bool) { if pad { C.X_EVP_CIPHER_CTX_set_padding(ctx.ctx, 1) } else { C.X_EVP_CIPHER_CTX_set_padding(ctx.ctx, 0) } } 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 } 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 encryptionCipherCtx struct { *cipherCtx } type decryptionCipherCtx struct { *cipherCtx } 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{cipherCtx: 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{cipherCtx: 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 *encryptionCipherCtx) EncryptUpdate(input []byte) ([]byte, error) { if len(input) == 0 { return nil, nil } 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) { if len(input) == 0 { return nil, nil } 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 }