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 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 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{decryptionCipherCtx: 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 } }
// Open decrypts "in" using "iv" and "authData" and append the result to "dst" func (g stupidGCM) Open(dst, iv, in, authData []byte) ([]byte, error) { if len(iv) != ivLen { log.Panicf("Only %d-byte IVs are supported", ivLen) } if len(in) <= tagLen { log.Panic("Input data too short") } buf := make([]byte, len(in)-tagLen) ciphertext := in[:len(in)-tagLen] tag := in[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_DecryptInit_ex(ctx, C.EVP_aes_256_gcm(), nil, nil, nil) != 1 { log.Panic("EVP_DecryptInit_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_DecryptInit_ex(ctx, nil, nil, (*C.uchar)(&g.key[0]), (*C.uchar)(&iv[0])) != 1 { log.Panic("EVP_DecryptInit_ex II failed") } // Set expected GMAC tag if C.EVP_CIPHER_CTX_ctrl(ctx, C.EVP_CTRL_GCM_SET_TAG, tagLen, (unsafe.Pointer)(&tag[0])) != 1 { log.Panic("EVP_CIPHER_CTX_ctrl failed") } // Provide authentication data var resultLen C.int if C.EVP_DecryptUpdate(ctx, nil, &resultLen, (*C.uchar)(&authData[0]), C.int(len(authData))) != 1 { log.Panic("EVP_DecryptUpdate authData failed") } if int(resultLen) != len(authData) { log.Panicf("Unexpected length %d", resultLen) } // Decrypt "ciphertext" into "buf" if C.EVP_DecryptUpdate(ctx, (*C.uchar)(&buf[0]), &resultLen, (*C.uchar)(&ciphertext[0]), C.int(len(ciphertext))) != 1 { log.Panic("EVP_DecryptUpdate failed") } if int(resultLen) != len(ciphertext) { log.Panicf("Unexpected length %d", resultLen) } // Check GMAC dummy := make([]byte, 16) res := C.EVP_DecryptFinal_ex(ctx, (*C.uchar)(&dummy[0]), &resultLen) if resultLen != 0 { log.Panicf("Unexpected length %d", resultLen) } // Free scratch space C.EVP_CIPHER_CTX_free(ctx) if res != 1 { return nil, fmt.Errorf("stupidgcm: message authentication failed") } return append(dst, buf...), nil }