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 ctx.ctx.encrypt != 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 New(key, iv []byte) Cipher { c := &cipher{} cgolock.Lock() defer cgolock.Unlock() C.EVP_CIPHER_CTX_init(&c.evp) runtime.SetFinalizer(c, func(c *cipher) { C.EVP_CIPHER_CTX_cleanup(&c.evp) }) C.EVP_EncryptInit_ex(&c.evp, C.EVP_aes_128_ctr(), nil, (*C.uchar)(&key[0]), (*C.uchar)(&iv[0])) return c }
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 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{encryptionCipherCtx: ctx}, nil }
//export ticket_key_cb_thunk func ticket_key_cb_thunk(p unsafe.Pointer, s *C.SSL, key_name *C.uchar, iv *C.uchar, cctx *C.EVP_CIPHER_CTX, hctx *C.HMAC_CTX, enc C.int) C.int { // no panic's allowed. it's super hard to guarantee any state at this point // so just abort everything. defer func() { if err := recover(); err != nil { logger.Critf("openssl: ticket key callback panic'd: %v", err) os.Exit(1) } }() ctx := (*Ctx)(p) store := ctx.ticket_store if store == nil { // TODO(jeff): should this be an error condition? it doesn't make sense // to be called if we don't have a store I believe, but that's probably // not worth aborting the handshake which is what I believe returning // an error would do. return ticket_resp_requireHandshake } ctx.ticket_store_mu.Lock() defer ctx.ticket_store_mu.Unlock() switch enc { case ticket_req_newSession: key := store.Keys.Current() if key == nil { key = store.Keys.New() if key == nil { return ticket_resp_requireHandshake } } C.memcpy( unsafe.Pointer(key_name), unsafe.Pointer(&key.Name[0]), KeyNameSize) C.EVP_EncryptInit_ex( cctx, store.CipherCtx.Cipher.ptr, store.cipherEngine(), (*C.uchar)(&key.CipherKey[0]), (*C.uchar)(&key.IV[0])) C.HMAC_Init_ex( hctx, unsafe.Pointer(&key.HMACKey[0]), C.int(len(key.HMACKey)), store.DigestCtx.Digest.ptr, store.digestEngine()) return ticket_resp_sessionOk case ticket_req_lookupSession: var name TicketName C.memcpy( unsafe.Pointer(&name[0]), unsafe.Pointer(key_name), KeyNameSize) key := store.Keys.Lookup(name) if key == nil { return ticket_resp_requireHandshake } if store.Keys.Expired(name) { return ticket_resp_requireHandshake } C.EVP_DecryptInit_ex( cctx, store.CipherCtx.Cipher.ptr, store.cipherEngine(), (*C.uchar)(&key.CipherKey[0]), (*C.uchar)(&key.IV[0])) C.HMAC_Init_ex( hctx, unsafe.Pointer(&key.HMACKey[0]), C.int(len(key.HMACKey)), store.DigestCtx.Digest.ptr, store.digestEngine()) if store.Keys.ShouldRenew(name) { return ticket_resp_renewSession } return ticket_resp_sessionOk default: return ticket_resp_error } }
// Seal encrypts "in" using "iv" and "authData" and append the result to "dst" func (g stupidGCM) Seal(dst, iv, in, authData []byte) []byte { if len(iv) != ivLen { log.Panicf("Only %d-byte IVs are supported", ivLen) } if len(in) == 0 { log.Panic("Zero-length input data is not supported") } buf := make([]byte, len(in)+tagLen) // https://wiki.openssl.org/index.php/EVP_Authenticated_Encryption_and_Decryption#Authenticated_Encryption_using_GCM_mode // Create scratch space "context" ctx := C.EVP_CIPHER_CTX_new() if ctx == nil { log.Panic("EVP_CIPHER_CTX_new failed") } // Set cipher to AES-256 if C.EVP_EncryptInit_ex(ctx, C.EVP_aes_256_gcm(), nil, nil, nil) != 1 { log.Panic("EVP_EncryptInit_ex I failed") } // Use 16-byte IV if C.EVP_CIPHER_CTX_ctrl(ctx, C.EVP_CTRL_GCM_SET_IVLEN, ivLen, nil) != 1 { log.Panic("EVP_CIPHER_CTX_ctrl EVP_CTRL_GCM_SET_IVLEN failed") } // Set key and IV if C.EVP_EncryptInit_ex(ctx, nil, nil, (*C.uchar)(&g.key[0]), (*C.uchar)(&iv[0])) != 1 { log.Panic("EVP_EncryptInit_ex II failed") } // Provide authentication data var resultLen C.int if C.EVP_EncryptUpdate(ctx, nil, &resultLen, (*C.uchar)(&authData[0]), C.int(len(authData))) != 1 { log.Panic("EVP_EncryptUpdate authData failed") } if int(resultLen) != len(authData) { log.Panicf("Unexpected length %d", resultLen) } // Encrypt "in" into "buf" if C.EVP_EncryptUpdate(ctx, (*C.uchar)(&buf[0]), &resultLen, (*C.uchar)(&in[0]), C.int(len(in))) != 1 { log.Panic("EVP_EncryptUpdate failed") } if int(resultLen) != len(in) { log.Panicf("Unexpected length %d", resultLen) } // Finalise encryption // Because GCM is a stream encryption, this will not write out any data. dummy := make([]byte, 16) if C.EVP_EncryptFinal_ex(ctx, (*C.uchar)(&dummy[0]), &resultLen) != 1 { log.Panic("EVP_EncryptFinal_ex failed") } if resultLen != 0 { log.Panicf("Unexpected length %d", resultLen) } // Get GMAC tag and append it to the ciphertext in "buf" if C.EVP_CIPHER_CTX_ctrl(ctx, C.EVP_CTRL_GCM_GET_TAG, tagLen, (unsafe.Pointer)(&buf[len(in)])) != 1 { log.Panic("EVP_CIPHER_CTX_ctrl EVP_CTRL_GCM_GET_TAG failed") } // Free scratch space C.EVP_CIPHER_CTX_free(ctx) return append(dst, buf...) }