diff --git a/scripts/check-diff.sh b/scripts/check-diff.sh
new file mode 100755
index 00000000..7d1f007b
--- /dev/null
+++ b/scripts/check-diff.sh
@@ -0,0 +1,15 @@
+#!/bin/bash
+
+trap cleanup EXIT
+
+function cleanup() {
+    rm scripts/generated-diffcheck.txt
+}
+
+diff openssl v2/openssl > scripts/generated-diffcheck.txt
+
+DIFF_OUTPUT=$(diff scripts/generated-diff.txt scripts/generated-diffcheck.txt)
+if [ "$DIFF_OUTPUT" ]; then
+    echo "Modifications have been made to the generated diff. Please review and update the diff."
+    exit 1
+fi
\ No newline at end of file
diff --git a/scripts/create-diff.sh b/scripts/create-diff.sh
new file mode 100755
index 00000000..1d8778b1
--- /dev/null
+++ b/scripts/create-diff.sh
@@ -0,0 +1,5 @@
+#!/bin/bash
+
+set -ex
+
+diff openssl v2/openssl > scripts/generated-diff.txt
\ No newline at end of file
diff --git a/scripts/generated-diff.txt b/scripts/generated-diff.txt
new file mode 100644
index 00000000..6afd670c
--- /dev/null
+++ b/scripts/generated-diff.txt
@@ -0,0 +1,189 @@
+diff openssl/aes.go v2/openssl/aes.go
+365a366,371
+> // NewGCMTLS returns a GCM cipher specific to TLS
+> // and should not be used for non-TLS purposes.
+> func NewGCMTLS(c cipher.Block) (cipher.AEAD, error) {
+> 	return c.(*aesCipher).NewGCMTLS()
+> }
+> 
+Only in v2/openssl: bbig
+diff openssl/ecdh_test.go v2/openssl/ecdh_test.go
+1c1
+< package openssl
+---
+> package openssl_test
+4a5,6
+> 	"github.com/golang-fips/openssl-fips/v2/openssl"
+> 	"github.com/golang-fips/openssl-fips/v2/openssl/bbig"
+10c12
+< 	if !Enabled() {
+---
+> 	if !openssl.Enabled() {
+56c58,60
+< 	priv, err := NewPrivateKeyECDH("P-256", x, y, k)
+---
+> 	bx, by, bk := bbig.Enc(x), bbig.Enc(y), bbig.Enc(k)
+> 
+> 	priv, err := openssl.NewPrivateKeyECDH("P-256", bx, by, bk)
+61c65
+< 	derived, err := SharedKeyECDH(priv, peerPublicKey)
+---
+> 	derived, err := openssl.SharedKeyECDH(priv, peerPublicKey)
+diff openssl/ecdsa.go v2/openssl/ecdsa.go
+13,14d12
+< 	"crypto"
+< 	"encoding/asn1"
+16d13
+< 	"math/big"
+22c19
+< 	R, S *big.Int
+---
+> 	R, S BigInt
+61c58
+< func NewPublicKeyECDSA(curve string, X, Y *big.Int) (*PublicKeyECDSA, error) {
+---
+> func NewPublicKeyECDSA(curve string, X, Y BigInt) (*PublicKeyECDSA, error) {
+75c72
+< func newECKey(curve string, X, Y *big.Int) (*C.GO_EC_KEY, error) {
+---
+> func newECKey(curve string, X, Y BigInt) (*C.GO_EC_KEY, error) {
+108c105
+< func NewPrivateKeyECDSA(curve string, X, Y *big.Int, D *big.Int) (*PrivateKeyECDSA, error) {
+---
+> func NewPrivateKeyECDSA(curve string, X, Y BigInt, D BigInt) (*PrivateKeyECDSA, error) {
+131,147c128
+< func SignECDSA(priv *PrivateKeyECDSA, hash []byte, h crypto.Hash) (r, s *big.Int, err error) {
+< 	// We could use ECDSA_do_sign instead but would need to convert
+< 	// the resulting BIGNUMs to *big.Int form. If we're going to do a
+< 	// conversion, converting the ASN.1 form is more convenient and
+< 	// likely not much more expensive.
+< 	sig, err := SignMarshalECDSA(priv, hash, h)
+< 	if err != nil {
+< 		return nil, nil, err
+< 	}
+< 	var esig ecdsaSignature
+< 	if _, err := asn1.Unmarshal(sig, &esig); err != nil {
+< 		return nil, nil, err
+< 	}
+< 	return esig.R, esig.S, nil
+< }
+< 
+< func SignMarshalECDSA(priv *PrivateKeyECDSA, hash []byte, h crypto.Hash) ([]byte, error) {
+---
+> func SignMarshalECDSA(priv *PrivateKeyECDSA, hash []byte) ([]byte, error) {
+151,163c132,134
+< 	if h == crypto.Hash(0) {
+< 		ok := C._goboringcrypto_internal_ECDSA_sign(0, base(hash), C.size_t(len(hash)), (*C.uint8_t)(unsafe.Pointer(&sig[0])), &sigLen, priv.key) > 0
+< 		if !ok {
+< 			return nil, NewOpenSSLError(("ECDSA_sign failed"))
+< 		}
+< 	} else {
+< 		md := cryptoHashToMD(h)
+< 		if md == nil {
+< 			panic("boring: invalid hash")
+< 		}
+< 		if C._goboringcrypto_ECDSA_sign(md, base(hash), C.size_t(len(hash)), (*C.uint8_t)(unsafe.Pointer(&sig[0])), &sigLen, priv.key) == 0 {
+< 			return nil, NewOpenSSLError("ECDSA_sign failed")
+< 		}
+---
+> 	ok := C._goboringcrypto_internal_ECDSA_sign(0, base(hash), C.size_t(len(hash)), (*C.uint8_t)(unsafe.Pointer(&sig[0])), &sigLen, priv.key) > 0
+> 	if !ok {
+> 		return nil, NewOpenSSLError(("ECDSA_sign failed"))
+164a136
+> 
+168,186c140,141
+< 
+< func VerifyECDSA(pub *PublicKeyECDSA, msg []byte, r, s *big.Int, h crypto.Hash) bool {
+< 	// We could use ECDSA_do_verify instead but would need to convert
+< 	// r and s to BIGNUM form. If we're going to do a conversion, marshaling
+< 	// to ASN.1 is more convenient and likely not much more expensive.
+< 	sig, err := asn1.Marshal(ecdsaSignature{r, s})
+< 	if err != nil {
+< 		return false
+< 	}
+< 	if h == crypto.Hash(0) {
+< 		ok := C._goboringcrypto_internal_ECDSA_verify(0, base(msg), C.size_t(len(msg)), (*C.uint8_t)(unsafe.Pointer(&sig[0])), C.uint(len(sig)), pub.key) > 0
+< 		runtime.KeepAlive(pub)
+< 		return ok
+< 	}
+< 	md := cryptoHashToMD(h)
+< 	if md == nil {
+< 		panic("boring: invalid hash")
+< 	}
+< 	ok := C._goboringcrypto_ECDSA_verify(md, base(msg), C.size_t(len(msg)), (*C.uint8_t)(unsafe.Pointer(&sig[0])), C.uint(len(sig)), pub.key) > 0
+---
+> func VerifyECDSA(pub *PublicKeyECDSA, hash []byte, sig []byte) bool {
+> 	ok := C._goboringcrypto_internal_ECDSA_verify(0, base(hash), C.size_t(len(hash)), (*C.uint8_t)(unsafe.Pointer(&sig[0])), C.uint(len(sig)), pub.key) > 0
+191c146
+< func GenerateKeyECDSA(curve string) (X, Y, D *big.Int, err error) {
+---
+> func GenerateKeyECDSA(curve string) (X, Y, D BigInt, err error) {
+diff openssl/goopenssl.h v2/openssl/goopenssl.h
+371a372,373
+> DEFINEFUNC(int, BN_bn2le_padded, (uint8_t *out, size_t len, const BIGNUM *in), (out, len, in))
+> DEFINEFUNC(GO_BIGNUM *, BN_le2bn, (const uint8_t *in, size_t len, BIGNUM *ret), (in, len, ret))
+diff openssl/openssl.go v2/openssl/openssl.go
+19c19
+< 	"math/big"
+---
+> 	"math/bits"
+39a40,44
+> // A BigInt is the raw words from a BigInt.
+> // This definition allows us to avoid importing math/big.
+> // Conversion between BigInt and *big.Int is in crypto/internal/boring/bbig.
+> type BigInt []uint
+> 
+186,188c191,201
+< func bigToBN(x *big.Int) *C.GO_BIGNUM {
+< 	raw := x.Bytes()
+< 	return C._goboringcrypto_BN_bin2bn(base(raw), C.size_t(len(raw)), nil)
+---
+> func wbase(b BigInt) *C.uint8_t {
+> 	if len(b) == 0 {
+> 		return nil
+> 	}
+> 	return (*C.uint8_t)(unsafe.Pointer(&b[0]))
+> }
+> 
+> const wordBytes = bits.UintSize / 8
+> 
+> func bigToBN(x BigInt) *C.GO_BIGNUM {
+> 	return C._goboringcrypto_BN_le2bn(wbase(x), C.size_t(len(x)*wordBytes), nil)
+191,194c204,209
+< func bnToBig(bn *C.GO_BIGNUM) *big.Int {
+< 	raw := make([]byte, C._goboringcrypto_BN_num_bytes(bn))
+< 	n := C._goboringcrypto_BN_bn2bin(bn, base(raw))
+< 	return new(big.Int).SetBytes(raw[:n])
+---
+> func bnToBig(bn *C.GO_BIGNUM) BigInt {
+> 	x := make(BigInt, (C._goboringcrypto_BN_num_bytes(bn)+wordBytes-1)/wordBytes)
+> 	if C._goboringcrypto_BN_bn2le_padded(wbase(x), C.size_t(len(x)*wordBytes), bn) == 0 {
+> 		panic("boringcrypto: bignum conversion failed")
+> 	}
+> 	return x
+197c212
+< func bigToBn(bnp **C.GO_BIGNUM, b *big.Int) bool {
+---
+> func bigToBn(bnp **C.GO_BIGNUM, b BigInt) bool {
+205,206c220
+< 	raw := b.Bytes()
+< 	bn := C._goboringcrypto_BN_bin2bn(base(raw), C.size_t(len(raw)), nil)
+---
+> 	bn := bigToBN(b)
+diff openssl/rsa.go v2/openssl/rsa.go
+16d15
+< 	"math/big"
+22,23c21,22
+< func GenerateKeyRSA(bits int) (N, E, D, P, Q, Dp, Dq, Qinv *big.Int, err error) {
+< 	bad := func(e error) (N, E, D, P, Q, Dp, Dq, Qinv *big.Int, err error) {
+---
+> func GenerateKeyRSA(bits int) (N, E, D, P, Q, Dp, Dq, Qinv BigInt, err error) {
+> 	bad := func(e error) (N, E, D, P, Q, Dp, Dq, Qinv BigInt, err error) {
+49c48
+< func NewPublicKeyRSA(N, E *big.Int) (*PublicKeyRSA, error) {
+---
+> func NewPublicKeyRSA(N, E BigInt) (*PublicKeyRSA, error) {
+80c79
+< func NewPrivateKeyRSA(N, E, D, P, Q, Dp, Dq, Qinv *big.Int) (*PrivateKeyRSA, error) {
+---
+> func NewPrivateKeyRSA(N, E, D, P, Q, Dp, Dq, Qinv BigInt) (*PrivateKeyRSA, error) {
diff --git a/v2/go.mod b/v2/go.mod
new file mode 100644
index 00000000..00516c65
--- /dev/null
+++ b/v2/go.mod
@@ -0,0 +1,3 @@
+module github.com/golang-fips/openssl-fips/v2
+
+go 1.18
diff --git a/v2/openssl/aes.go b/v2/openssl/aes.go
new file mode 100644
index 00000000..079fc3c4
--- /dev/null
+++ b/v2/openssl/aes.go
@@ -0,0 +1,516 @@
+// Copyright 2017 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+//go:build linux && !android && !cmd_go_bootstrap && !msan && !no_openssl
+// +build linux,!android,!cmd_go_bootstrap,!msan,!no_openssl
+
+package openssl
+
+// #include "goopenssl.h"
+import "C"
+import (
+	"crypto/cipher"
+	"errors"
+	"runtime"
+	"strconv"
+	"unsafe"
+)
+
+type aesKeySizeError int
+
+func (k aesKeySizeError) Error() string {
+	return "crypto/aes: invalid key size " + strconv.Itoa(int(k))
+}
+
+const aesBlockSize = 16
+
+type aesCipher struct {
+	key     []byte
+	enc_ctx *C.EVP_CIPHER_CTX
+	dec_ctx *C.EVP_CIPHER_CTX
+	cipher  *C.EVP_CIPHER
+}
+
+type extraModes interface {
+	// Copied out of crypto/aes/modes.go.
+	NewCBCEncrypter(iv []byte) cipher.BlockMode
+	NewCBCDecrypter(iv []byte) cipher.BlockMode
+	NewCTR(iv []byte) cipher.Stream
+	NewGCM(nonceSize, tagSize int) (cipher.AEAD, error)
+
+	// Invented for BoringCrypto.
+	NewGCMTLS() (cipher.AEAD, error)
+}
+
+var _ extraModes = (*aesCipher)(nil)
+
+func NewAESCipher(key []byte) (cipher.Block, error) {
+	c := &aesCipher{key: make([]byte, len(key))}
+	copy(c.key, key)
+
+	switch len(c.key) * 8 {
+	case 128:
+		c.cipher = C._goboringcrypto_EVP_aes_128_ecb()
+	case 192:
+		c.cipher = C._goboringcrypto_EVP_aes_192_ecb()
+	case 256:
+		c.cipher = C._goboringcrypto_EVP_aes_256_ecb()
+	default:
+		return nil, errors.New("crypto/cipher: Invalid key size")
+	}
+
+	runtime.SetFinalizer(c, (*aesCipher).finalize)
+
+	return c, nil
+}
+
+func (c *aesCipher) finalize() {
+	if c.enc_ctx != nil {
+		C._goboringcrypto_EVP_CIPHER_CTX_free(c.enc_ctx)
+	}
+	if c.dec_ctx != nil {
+		C._goboringcrypto_EVP_CIPHER_CTX_free(c.dec_ctx)
+	}
+}
+
+func (c *aesCipher) BlockSize() int { return aesBlockSize }
+
+func (c *aesCipher) Encrypt(dst, src []byte) {
+	if inexactOverlap(dst, src) {
+		panic("crypto/cipher: invalid buffer overlap")
+	}
+	if len(src) < aesBlockSize {
+		panic("crypto/aes: input not full block")
+	}
+	if len(dst) < aesBlockSize {
+		panic("crypto/aes: output not full block")
+	}
+
+	if c.enc_ctx == nil {
+		c.enc_ctx = C._goboringcrypto_EVP_CIPHER_CTX_new()
+		if c.enc_ctx == nil {
+			panic("cipher: unable to create EVP cipher ctx")
+		}
+
+		k := (*C.uchar)(unsafe.Pointer(&c.key[0]))
+
+		if C.int(1) != C._goboringcrypto_EVP_CipherInit_ex(c.enc_ctx, c.cipher, nil, k, nil, C.GO_AES_ENCRYPT) {
+			panic("cipher: unable to initialize EVP cipher ctx")
+		}
+	}
+
+	outlen := C.int(0)
+	C._goboringcrypto_EVP_CipherUpdate(c.enc_ctx, (*C.uchar)(unsafe.Pointer(&dst[0])), &outlen, (*C.uchar)(unsafe.Pointer(&src[0])), C.int(aesBlockSize))
+	runtime.KeepAlive(c)
+}
+
+func (c *aesCipher) Decrypt(dst, src []byte) {
+	if inexactOverlap(dst, src) {
+		panic("crypto/cipher: invalid buffer overlap")
+	}
+	if len(src) < aesBlockSize {
+		panic("crypto/aes: input not full block")
+	}
+	if len(dst) < aesBlockSize {
+		panic("crypto/aes: output not full block")
+	}
+	if c.dec_ctx == nil {
+		c.dec_ctx = C._goboringcrypto_EVP_CIPHER_CTX_new()
+		if c.dec_ctx == nil {
+			panic("cipher: unable to create EVP cipher ctx")
+		}
+
+		k := (*C.uchar)(unsafe.Pointer(&c.key[0]))
+
+		if C.int(1) != C._goboringcrypto_EVP_CipherInit_ex(c.dec_ctx, c.cipher, nil, k, nil, C.GO_AES_DECRYPT) {
+			panic("cipher: unable to initialize EVP cipher ctx")
+		}
+	}
+	// Workaround - padding detection is broken but we don't need it
+	// since we check for full blocks
+	if C._goboringcrypto_EVP_CIPHER_CTX_set_padding(c.dec_ctx, 0) != 1 {
+		panic("crypto/cipher: could not disable cipher padding")
+	}
+	outlen := C.int(0)
+	C._goboringcrypto_EVP_CipherUpdate(c.dec_ctx, (*C.uchar)(unsafe.Pointer(&dst[0])), &outlen, (*C.uchar)(unsafe.Pointer(&src[0])), C.int(aesBlockSize))
+	runtime.KeepAlive(c)
+}
+
+type aesCBC struct {
+	key  []byte
+	mode C.int
+	iv   [aesBlockSize]byte
+	ctx  *C.EVP_CIPHER_CTX
+}
+
+func (x *aesCBC) BlockSize() int { return aesBlockSize }
+
+func (x *aesCBC) CryptBlocks(dst, src []byte) {
+	if inexactOverlap(dst, src) {
+		panic("crypto/cipher: invalid buffer overlap")
+	}
+	if len(src)%aesBlockSize != 0 {
+		panic("crypto/cipher: input not full blocks")
+	}
+	if len(dst) < len(src) {
+		panic("crypto/cipher: output smaller than input")
+	}
+	if len(src) > 0 {
+		outlen := C.int(0)
+		// Workaround - padding detection is broken but we don't need it
+		// since we check for full blocks
+		if C._goboringcrypto_EVP_CIPHER_CTX_set_padding(x.ctx, 0) != 1 {
+			panic("crypto/cipher: could not disable cipher padding")
+		}
+		if C._goboringcrypto_EVP_CipherUpdate(
+			x.ctx,
+			base(dst), &outlen,
+			base(src), C.int(len(src)),
+		) != 1 {
+			panic("crypto/cipher: CipherUpdate failed")
+		}
+		runtime.KeepAlive(x)
+	}
+}
+
+func (x *aesCBC) SetIV(iv []byte) {
+	if len(iv) != aesBlockSize {
+		panic("cipher: incorrect length IV")
+	}
+	copy(x.iv[:], iv)
+	if C.int(1) != C._goboringcrypto_EVP_CipherInit_ex(x.ctx, nil, nil, nil, (*C.uchar)(unsafe.Pointer(&x.iv[0])), -1) {
+		panic("cipher: unable to initialize EVP cipher ctx")
+	}
+}
+
+func (c *aesCipher) NewCBCEncrypter(iv []byte) cipher.BlockMode {
+	x := &aesCBC{key: c.key, mode: C.GO_AES_ENCRYPT}
+	copy(x.iv[:], iv)
+
+	x.ctx = C._goboringcrypto_EVP_CIPHER_CTX_new()
+	if x.ctx == nil {
+		panic("cipher: unable to create EVP cipher ctx")
+	}
+
+	k := (*C.uchar)(unsafe.Pointer(&x.key[0]))
+	vec := (*C.uchar)(unsafe.Pointer(&x.iv[0]))
+
+	var cipher *C.EVP_CIPHER
+	switch len(c.key) * 8 {
+	case 128:
+		cipher = C._goboringcrypto_EVP_aes_128_cbc()
+	case 192:
+		cipher = C._goboringcrypto_EVP_aes_192_cbc()
+	case 256:
+		cipher = C._goboringcrypto_EVP_aes_256_cbc()
+	default:
+		panic("crypto/boring: unsupported key length")
+	}
+	if C.int(1) != C._goboringcrypto_EVP_CipherInit_ex(x.ctx, cipher, nil, k, vec, x.mode) {
+		panic("cipher: unable to initialize EVP cipher ctx")
+	}
+
+	runtime.SetFinalizer(x, (*aesCBC).finalize)
+
+	return x
+}
+
+func (c *aesCBC) finalize() {
+	C._goboringcrypto_EVP_CIPHER_CTX_free(c.ctx)
+}
+
+func (c *aesCipher) NewCBCDecrypter(iv []byte) cipher.BlockMode {
+	x := &aesCBC{key: c.key, mode: C.GO_AES_DECRYPT}
+	copy(x.iv[:], iv)
+
+	x.ctx = C._goboringcrypto_EVP_CIPHER_CTX_new()
+	if x.ctx == nil {
+		panic("cipher: unable to create EVP cipher ctx")
+	}
+
+	k := (*C.uchar)(unsafe.Pointer(&x.key[0]))
+	vec := (*C.uchar)(unsafe.Pointer(&x.iv[0]))
+
+	var cipher *C.EVP_CIPHER
+	switch len(c.key) * 8 {
+	case 128:
+		cipher = C._goboringcrypto_EVP_aes_128_cbc()
+	case 192:
+		cipher = C._goboringcrypto_EVP_aes_192_cbc()
+	case 256:
+		cipher = C._goboringcrypto_EVP_aes_256_cbc()
+	default:
+		panic("crypto/boring: unsupported key length")
+	}
+	if C.int(1) != C._goboringcrypto_EVP_CipherInit_ex(x.ctx, cipher, nil, k, vec, x.mode) {
+		panic("cipher: unable to initialize EVP cipher ctx")
+	}
+	if C.int(1) != C._goboringcrypto_EVP_CIPHER_CTX_set_padding(x.ctx, 0) {
+		panic("cipher: unable to set padding")
+	}
+
+	runtime.SetFinalizer(x, (*aesCBC).finalize)
+	return x
+}
+
+type aesCTR struct {
+	key        []byte
+	iv         [aesBlockSize]byte
+	ctx        *C.EVP_CIPHER_CTX
+	num        C.uint
+	ecount_buf [16]C.uint8_t
+}
+
+func (x *aesCTR) XORKeyStream(dst, src []byte) {
+	if inexactOverlap(dst, src) {
+		panic("crypto/cipher: invalid buffer overlap")
+	}
+	if len(dst) < len(src) {
+		panic("crypto/cipher: output smaller than input")
+	}
+	if len(src) == 0 {
+		return
+	}
+	C._goboringcrypto_EVP_AES_ctr128_enc(
+		x.ctx,
+		(*C.uint8_t)(unsafe.Pointer(&src[0])),
+		(*C.uint8_t)(unsafe.Pointer(&dst[0])),
+		C.size_t(len(src)))
+	runtime.KeepAlive(x)
+}
+
+func (c *aesCipher) NewCTR(iv []byte) cipher.Stream {
+	x := &aesCTR{key: c.key}
+	copy(x.iv[:], iv)
+
+	x.ctx = C._goboringcrypto_EVP_CIPHER_CTX_new()
+	if x.ctx == nil {
+		panic("cipher: unable to create EVP cipher ctx")
+	}
+
+	k := (*C.uchar)(unsafe.Pointer(&x.key[0]))
+	vec := (*C.uchar)(unsafe.Pointer(&x.iv[0]))
+
+	switch len(c.key) * 8 {
+	case 128:
+		if C.int(1) != C._goboringcrypto_EVP_EncryptInit_ex(x.ctx, C._goboringcrypto_EVP_aes_128_ctr(), nil, k, vec) {
+			panic("cipher: unable to initialize EVP cipher ctx")
+		}
+	case 192:
+		if C.int(1) != C._goboringcrypto_EVP_EncryptInit_ex(x.ctx, C._goboringcrypto_EVP_aes_192_ctr(), nil, k, vec) {
+			panic("cipher: unable to initialize EVP cipher ctx")
+		}
+	case 256:
+		if C.int(1) != C._goboringcrypto_EVP_EncryptInit_ex(x.ctx, C._goboringcrypto_EVP_aes_256_ctr(), nil, k, vec) {
+			panic("cipher: unable to initialize EVP cipher ctx")
+		}
+	}
+
+	runtime.SetFinalizer(x, (*aesCTR).finalize)
+
+	return x
+}
+
+func (c *aesCTR) finalize() {
+	C._goboringcrypto_EVP_CIPHER_CTX_free(c.ctx)
+}
+
+type aesGCM struct {
+	key []byte
+	tls bool
+}
+
+const (
+	gcmBlockSize         = 16
+	gcmTagSize           = 16
+	gcmStandardNonceSize = 12
+)
+
+type aesNonceSizeError int
+
+func (n aesNonceSizeError) Error() string {
+	return "crypto/aes: invalid GCM nonce size " + strconv.Itoa(int(n))
+}
+
+type noGCM struct {
+	cipher.Block
+}
+
+func (c *aesCipher) NewGCM(nonceSize, tagSize int) (cipher.AEAD, error) {
+	if !ExecutingTest() || IsStrictFips() {
+		if nonceSize != gcmStandardNonceSize {
+			return nil, errors.New("crypto/aes: GCM nonce size can't be non-standard")
+		}
+		if tagSize != gcmTagSize {
+			return nil, errors.New("crypto/aes: GCM tag size can't be non-standard")
+		}
+	} else {
+		// Be more lenient if we're running via a test binary so that
+		// we don't have to be as invasive with skipping tests in the standard
+		// library.
+		if nonceSize != gcmStandardNonceSize && tagSize != gcmTagSize {
+			return nil, errors.New("crypto/aes: GCM tag and nonce sizes can't be non-standard at the same time")
+		}
+		// Fall back to standard library for GCM with non-standard nonce or tag size.
+		if nonceSize != gcmStandardNonceSize {
+			return cipher.NewGCMWithNonceSize(&noGCM{c}, nonceSize)
+		}
+		if tagSize != gcmTagSize {
+			return cipher.NewGCMWithTagSize(&noGCM{c}, tagSize)
+		}
+	}
+	return c.newGCM(false)
+}
+
+// NewGCMTLS returns a GCM cipher specific to TLS
+// and should not be used for non-TLS purposes.
+func NewGCMTLS(c cipher.Block) (cipher.AEAD, error) {
+	return c.(*aesCipher).NewGCMTLS()
+}
+
+func (c *aesCipher) NewGCMTLS() (cipher.AEAD, error) {
+	return c.newGCM(true)
+}
+
+func (c *aesCipher) newGCM(tls bool) (cipher.AEAD, error) {
+	keyLen := len(c.key) * 8
+
+	if keyLen != 128 && keyLen != 256 {
+		if ExecutingTest() {
+			// Fall back to standard library for GCM with non-standard key size.
+			return cipher.NewGCMWithNonceSize(&noGCM{c}, gcmStandardNonceSize)
+		}
+		// Return error for GCM with non-standard key size.
+		return nil, fail("GCM invoked with non-standard key size")
+	}
+
+	g := &aesGCM{key: c.key, tls: tls}
+	if g.NonceSize() != gcmStandardNonceSize {
+		panic("boringcrypto: internal confusion about nonce size")
+	}
+	if g.Overhead() != gcmTagSize {
+		panic("boringcrypto: internal confusion about tag size")
+	}
+
+	return g, nil
+}
+
+func (g *aesGCM) NonceSize() int {
+	return gcmStandardNonceSize
+}
+
+func (g *aesGCM) Overhead() int {
+	return gcmTagSize
+}
+
+// base returns the address of the underlying array in b,
+// being careful not to panic when b has zero length.
+func base(b []byte) *C.uint8_t {
+	if len(b) == 0 {
+		return nil
+	}
+	return (*C.uint8_t)(unsafe.Pointer(&b[0]))
+}
+
+func (g *aesGCM) Seal(dst, nonce, plaintext, additionalData []byte) []byte {
+	if len(nonce) != gcmStandardNonceSize {
+		panic("cipher: incorrect nonce length given to GCM")
+	}
+	if uint64(len(plaintext)) > ((1<<32)-2)*aesBlockSize || len(plaintext)+gcmTagSize < len(plaintext) {
+		panic("cipher: message too large for GCM")
+	}
+	if len(dst)+len(plaintext)+gcmTagSize < len(dst) {
+		panic("cipher: message too large for buffer")
+	}
+
+	// Make room in dst to append plaintext+overhead.
+	n := len(dst)
+	for cap(dst) < n+len(plaintext)+gcmTagSize {
+		dst = append(dst[:cap(dst)], 0)
+	}
+	dst = dst[:n+len(plaintext)+gcmTagSize]
+
+	// Check delayed until now to make sure len(dst) is accurate.
+	if inexactOverlap(dst[n:], plaintext) {
+		panic("cipher: invalid buffer overlap")
+	}
+
+	var ciphertextLen C.size_t
+
+	if ok := C._goboringcrypto_EVP_CIPHER_CTX_seal(
+		(*C.uint8_t)(unsafe.Pointer(&dst[n])),
+		base(nonce), base(additionalData), C.size_t(len(additionalData)),
+		base(plaintext), C.size_t(len(plaintext)), &ciphertextLen,
+		base(g.key), C.int(len(g.key)*8)); ok != 1 {
+		panic("boringcrypto: EVP_CIPHER_CTX_seal fail")
+	}
+	runtime.KeepAlive(g)
+
+	if ciphertextLen != C.size_t(len(plaintext)+gcmTagSize) {
+		panic("boringcrypto: [seal] internal confusion about GCM tag size")
+	}
+	return dst[:n+int(ciphertextLen)]
+}
+
+var errOpen = errors.New("cipher: message authentication failed")
+
+func (g *aesGCM) Open(dst, nonce, ciphertext, additionalData []byte) ([]byte, error) {
+	if len(nonce) != gcmStandardNonceSize {
+		panic("cipher: incorrect nonce length given to GCM")
+	}
+	if len(ciphertext) < gcmTagSize {
+		return nil, errOpen
+	}
+	if uint64(len(ciphertext)) > ((1<<32)-2)*aesBlockSize+gcmTagSize {
+		return nil, errOpen
+	}
+
+	// Make room in dst to append ciphertext without tag.
+	n := len(dst)
+	for cap(dst) < n+len(ciphertext)-gcmTagSize {
+		dst = append(dst[:cap(dst)], 0)
+	}
+	dst = dst[:n+len(ciphertext)-gcmTagSize]
+
+	// Check delayed until now to make sure len(dst) is accurate.
+	if inexactOverlap(dst[n:], ciphertext) {
+		panic("cipher: invalid buffer overlap")
+	}
+
+	tag := ciphertext[len(ciphertext)-gcmTagSize:]
+
+	var outLen C.size_t
+
+	ok := C._goboringcrypto_EVP_CIPHER_CTX_open(
+		base(ciphertext), C.int(len(ciphertext)-gcmTagSize),
+		base(additionalData), C.int(len(additionalData)),
+		base(tag), base(g.key), C.int(len(g.key)*8),
+		base(nonce), C.int(len(nonce)),
+		base(dst[n:]), &outLen)
+	runtime.KeepAlive(g)
+	if ok == 0 {
+		// Zero output buffer on error.
+		for i := range dst {
+			dst[i] = 0
+		}
+		return nil, errOpen
+	}
+	if outLen != C.size_t(len(ciphertext)-gcmTagSize) {
+		panic("boringcrypto: [open] internal confusion about GCM tag size")
+	}
+	return dst[:n+int(outLen)], nil
+}
+
+func anyOverlap(x, y []byte) bool {
+	return len(x) > 0 && len(y) > 0 &&
+		uintptr(unsafe.Pointer(&x[0])) <= uintptr(unsafe.Pointer(&y[len(y)-1])) &&
+		uintptr(unsafe.Pointer(&y[0])) <= uintptr(unsafe.Pointer(&x[len(x)-1]))
+}
+
+func inexactOverlap(x, y []byte) bool {
+	if len(x) == 0 || len(y) == 0 || &x[0] == &y[0] {
+		return false
+	}
+	return anyOverlap(x, y)
+}
diff --git a/v2/openssl/aes_test.go b/v2/openssl/aes_test.go
new file mode 100644
index 00000000..a13054a7
--- /dev/null
+++ b/v2/openssl/aes_test.go
@@ -0,0 +1,180 @@
+//go:build linux && !android && !no_openssl && !cmd_go_bootstrap && !msan && cgo
+// +build linux,!android,!no_openssl,!cmd_go_bootstrap,!msan,cgo
+
+package openssl
+
+import (
+	"bytes"
+	"crypto/cipher"
+	"os"
+	"testing"
+)
+
+func TestNewGCMNonce(t *testing.T) {
+	origStrictFIPS := os.Getenv(GoStrictFipsEnv)
+	os.Setenv(GoStrictFipsEnv, "1")
+	defer os.Setenv(GoStrictFipsEnv, origStrictFIPS)
+
+	// Should return an error for non-standard nonce size.
+	key := []byte("D249BF6DEC97B1EBD69BC4D6B3A3C49D")
+	ci, err := NewAESCipher(key)
+	if err != nil {
+		t.Fatal(err)
+	}
+	c := ci.(*aesCipher)
+	_, err = c.NewGCM(gcmStandardNonceSize-1, gcmTagSize)
+	if err == nil {
+		t.Error("expected error for non-standard nonce size, got none")
+	}
+	_, err = c.NewGCM(gcmStandardNonceSize, gcmTagSize-1)
+	if err == nil {
+		t.Error("expected error for non-standard tag size, got none")
+	}
+	_, err = c.NewGCM(gcmStandardNonceSize, gcmTagSize)
+	if err != nil {
+		t.Errorf("expected no error for standard tag / nonce size, got: %#v", err)
+	}
+}
+
+func TestBlobEncryptBasicBlockEncryption(t *testing.T) {
+	key := []byte{0x24, 0xcd, 0x8b, 0x13, 0x37, 0xc5, 0xc1, 0xb1, 0x0, 0xbb, 0x27, 0x40, 0x4f, 0xab, 0x5f, 0x7b, 0x2d, 0x0, 0x20, 0xf5, 0x1, 0x84, 0x4, 0xbf, 0xe3, 0xbd, 0xa1, 0xc4, 0xbf, 0x61, 0x2f, 0xc5}
+	iv := []byte{0x91, 0xc7, 0xa7, 0x54, 0x52, 0xef, 0x10, 0xdb, 0x91, 0xa8, 0x6c, 0xf9, 0x79, 0xd5, 0xac, 0x74}
+
+	block, err := NewAESCipher(key)
+	if err != nil {
+		t.Errorf("expected no error for aes.NewCipher, got: %s", err)
+	}
+
+	blockSize := block.BlockSize()
+	if blockSize != 16 {
+		t.Errorf("unexpected block size, expected 16 got: %d", blockSize)
+	}
+	var encryptor cipher.BlockMode
+	if c, ok := block.(*aesCipher); ok {
+		encryptor = c.NewCBCEncrypter(iv)
+		if encryptor == nil {
+			t.Error("unable to create new CBC encrypter")
+		}
+	}
+
+	encrypted := make([]byte, 32)
+
+	// First block. 16 bytes.
+	srcBlock1 := bytes.Repeat([]byte{0x01}, 16)
+	encryptor.CryptBlocks(encrypted, srcBlock1)
+	if !bytes.Equal([]byte{
+		0x14, 0xb7, 0x3e, 0x2f, 0xd9, 0xe7, 0x69, 0x7e, 0xb7, 0xd2, 0xc3, 0x5b, 0x31, 0x9c, 0xf0, 0x59,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	}, encrypted) {
+		t.Error("unexpected CryptBlocks result for first block")
+	}
+
+	// Second block. 16 bytes.
+	srcBlock2 := bytes.Repeat([]byte{0x02}, 16)
+	encryptor.CryptBlocks(encrypted[16:], srcBlock2)
+	if !bytes.Equal([]byte{
+		0x14, 0xb7, 0x3e, 0x2f, 0xd9, 0xe7, 0x69, 0x7e, 0xb7, 0xd2, 0xc3, 0x5b, 0x31, 0x9c, 0xf0, 0x59,
+		0xbb, 0xd4, 0x95, 0x25, 0x21, 0x56, 0x87, 0x3b, 0xe6, 0x22, 0xe8, 0xd0, 0x19, 0xa8, 0xed, 0xcd,
+	}, encrypted) {
+		t.Error("unexpected CryptBlocks result for second block")
+	}
+
+	var decrypter cipher.BlockMode
+	if c, ok := block.(*aesCipher); ok {
+		decrypter = c.NewCBCDecrypter(iv)
+		if decrypter == nil {
+			t.Error("unable to create new CBC decrypter")
+		}
+	}
+	plainText := append(srcBlock1, srcBlock2...)
+	decrypted := make([]byte, len(plainText))
+	decrypter.CryptBlocks(decrypted, encrypted[:16])
+	decrypter.CryptBlocks(decrypted[16:], encrypted[16:])
+	if !bytes.Equal(decrypted, plainText) {
+		t.Errorf("unexpected decrypted result\ngot: %#v\nexp: %#v", decrypted, plainText)
+	}
+}
+
+func TestDecryptSimple(t *testing.T) {
+	key := []byte{
+		0x24, 0xcd, 0x8b, 0x13, 0x37, 0xc5, 0xc1, 0xb1,
+		0x0, 0xbb, 0x27, 0x40, 0x4f, 0xab, 0x5f, 0x7b,
+		0x2d, 0x0, 0x20, 0xf5, 0x1, 0x84, 0x4, 0xbf,
+		0xe3, 0xbd, 0xa1, 0xc4, 0xbf, 0x61, 0x2f, 0xc5,
+	}
+	block, err := NewAESCipher(key)
+	if err != nil {
+		panic(err)
+	}
+	iv := []byte{
+		0x91, 0xc7, 0xa7, 0x54, 0x52, 0xef, 0x10, 0xdb,
+		0x91, 0xa8, 0x6c, 0xf9, 0x79, 0xd5, 0xac, 0x74,
+	}
+
+	plainText := []byte{
+		0x54, 0x68, 0x65, 0x72, 0x65, 0x20, 0x69, 0x73,
+		0x20, 0x6f, 0x6e, 0x6c, 0x79, 0x20, 0x6f, 0x6e,
+		0x65, 0x20, 0x4c, 0x6f, 0x72, 0x64, 0x20, 0x6f,
+		0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x52, 0x69,
+		0x6e, 0x67, 0x2c, 0x20, 0x6f, 0x6e, 0x6c, 0x79,
+		0x20, 0x6f, 0x6e, 0x65, 0x20, 0x77, 0x68, 0x6f,
+		0x20, 0x63, 0x61, 0x6e, 0x20, 0x62, 0x65, 0x6e,
+		0x64, 0x20, 0x69, 0x74, 0x20, 0x74, 0x6f, 0x20,
+		0x68, 0x69, 0x73, 0x20, 0x77, 0x69, 0x6c, 0x6c,
+		0x2e, 0x20, 0x41, 0x6e, 0x64, 0x20, 0x68, 0x65,
+		0x20, 0x64, 0x6f, 0x65, 0x73, 0x20, 0x6e, 0x6f,
+		0x74, 0x20, 0x73, 0x68, 0x61, 0x72, 0x65, 0x20,
+		0x70, 0x6f, 0x77, 0x65, 0x72, 0x2e, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	}
+	cipherText := make([]byte, len(plainText))
+	var encrypter cipher.BlockMode
+	if c, ok := block.(*aesCipher); ok {
+		encrypter = c.NewCBCEncrypter(iv)
+		if encrypter == nil {
+			t.Error("unable to create new CBC encrypter")
+		}
+	}
+	encrypter.CryptBlocks(cipherText, plainText[:64])
+	encrypter.CryptBlocks(cipherText[64:], plainText[64:])
+
+	expectedCipherText := []byte{
+		23, 51, 192, 210, 170, 124, 30, 218,
+		176, 54, 70, 132, 141, 124, 3, 152,
+		47, 3, 37, 81, 187, 101, 197, 94,
+		11, 38, 128, 60, 112, 20, 235, 130,
+		111, 236, 176, 99, 121, 6, 221, 181,
+		190, 228, 150, 177, 218, 3, 196, 0,
+		5, 141, 169, 151, 3, 161, 64, 244,
+		231, 237, 252, 143, 111, 37, 68, 70,
+		11, 137, 220, 243, 195, 90, 182, 83,
+		96, 80, 122, 14, 93, 178, 62, 159,
+		25, 162, 200, 155, 21, 150, 6, 101,
+		21, 234, 12, 74, 190, 213, 159, 220,
+		111, 184, 94, 169, 188, 93, 38, 150,
+		3, 208, 185, 201, 212, 246, 238, 181,
+	}
+	if bytes.Compare(expectedCipherText, cipherText) != 0 {
+		t.Fail()
+	}
+
+	var decrypter cipher.BlockMode
+	if c, ok := block.(*aesCipher); ok {
+		decrypter = c.NewCBCDecrypter(iv)
+		if decrypter == nil {
+			t.Error("unable to create new CBC decrypter")
+		}
+	}
+	decrypted := make([]byte, len(plainText))
+
+	decrypter.CryptBlocks(decrypted, cipherText[:64])
+	decrypter.CryptBlocks(decrypted[64:], cipherText[64:])
+
+	if len(decrypted) != len(plainText) {
+		t.Fail()
+	}
+
+	if bytes.Compare(plainText, decrypted) != 0 {
+		t.Errorf("decryption incorrect\nexp %v, got %v\n", plainText, decrypted)
+	}
+}
diff --git a/v2/openssl/bbig/big.go b/v2/openssl/bbig/big.go
new file mode 100644
index 00000000..8ef6ec34
--- /dev/null
+++ b/v2/openssl/bbig/big.go
@@ -0,0 +1,38 @@
+// Copyright 2022 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This is a mirror of crypto/internal/boring/bbig/big.go.
+
+package bbig
+
+import (
+	"math/big"
+	"unsafe"
+
+	"github.com/golang-fips/openssl-fips/v2/openssl"
+)
+
+func Enc(b *big.Int) openssl.BigInt {
+	if b == nil {
+		return nil
+	}
+	x := b.Bits()
+	if len(x) == 0 {
+		return openssl.BigInt{}
+	}
+	// TODO: Use unsafe.Slice((*uint)(&x[0]), len(x)) once go1.16 is no longer supported.
+	return (*(*[]uint)(unsafe.Pointer(&x)))[:len(x)]
+}
+
+func Dec(b openssl.BigInt) *big.Int {
+	if b == nil {
+		return nil
+	}
+	if len(b) == 0 {
+		return new(big.Int)
+	}
+	// TODO: Use unsafe.Slice((*uint)(&b[0]), len(b)) once go1.16 is no longer supported.
+	x := (*(*[]big.Word)(unsafe.Pointer(&b)))[:len(b)]
+	return new(big.Int).SetBits(x)
+}
diff --git a/v2/openssl/doc.go b/v2/openssl/doc.go
new file mode 100644
index 00000000..cdc7f6a4
--- /dev/null
+++ b/v2/openssl/doc.go
@@ -0,0 +1,17 @@
+// Copyright 2017 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// package openssl provides access to OpenSSL implementation functions.
+package openssl
+
+// Enabled returns whether or not the boring package is enabled. When
+// the boring package is enabled that means FIPS mode is enabled.
+func Enabled() bool {
+	return enabled
+}
+
+// A BigInt is the raw words from a BigInt.
+// This definition allows us to avoid importing math/big.
+// Conversion between BigInt and *big.Int is in crypto/internal/boring/bbig.
+type BigInt []uint
diff --git a/v2/openssl/ecdh.go b/v2/openssl/ecdh.go
new file mode 100644
index 00000000..0b61e79b
--- /dev/null
+++ b/v2/openssl/ecdh.go
@@ -0,0 +1,108 @@
+// Copyright 2017 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+//go:build linux && !android && !cmd_go_bootstrap && !msan && !no_openssl
+// +build linux,!android,!cmd_go_bootstrap,!msan,!no_openssl
+
+package openssl
+
+// #include "goopenssl.h"
+import "C"
+import "runtime"
+
+// ECDH keys are compatible with ECDSA
+type PublicKeyECDH = PublicKeyECDSA
+type PrivateKeyECDH = PrivateKeyECDSA
+
+var NewPublicKeyECDH = NewPublicKeyECDSA
+var NewPrivateKeyECDH = NewPrivateKeyECDSA
+var GenerateKeyECDH = GenerateKeyECDSA
+
+func (k *PrivateKeyECDH) withKey(f func(*C.GO_EC_KEY) C.int) C.int {
+	// Because of the finalizer, any time _key is passed to cgo, that call must
+	// be followed by a call to runtime.KeepAlive, to make sure k is not
+	// collected (and finalized) before the cgo call returns.
+	defer runtime.KeepAlive(k)
+	return f(k.key)
+}
+
+func (k *PublicKeyECDH) withKey(f func(*C.GO_EC_KEY) C.int) C.int {
+	// Because of the finalizer, any time _key is passed to cgo, that call must
+	// be followed by a call to runtime.KeepAlive, to make sure k is not
+	// collected (and finalized) before the cgo call returns.
+	defer runtime.KeepAlive(k)
+	return f(k.key)
+}
+
+func getPeerKey(priv *PrivateKeyECDH, pubBytes []byte) (*PublicKeyECDH, error) {
+	eckey := C._goboringcrypto_EC_KEY_new()
+	if priv.withKey(func(key *C.GO_EC_KEY) C.int {
+		group := C._goboringcrypto_EC_KEY_get0_group(key)
+		return C._goboringcrypto_EC_KEY_set_group(eckey, group)
+	}) != 1 {
+		return nil, NewOpenSSLError("EC_KEY_set_group")
+	}
+	if C._goboringcrypto_EC_KEY_oct2key(eckey,
+		base(pubBytes), C.size_t(len(pubBytes)),
+		nil) != 1 {
+		return nil, NewOpenSSLError("EC_KEY_oct2key")
+	}
+	k := &PublicKeyECDSA{eckey}
+	// Note: Because of the finalizer, any time k.key is passed to cgo,
+	// that call must be followed by a call to runtime.KeepAlive(k),
+	// to make sure k is not collected (and finalized) before the cgo
+	// call returns.
+	runtime.SetFinalizer(k, (*PublicKeyECDH).finalize)
+	return k, nil
+}
+
+func SharedKeyECDH(priv *PrivateKeyECDH, peerPublicKey []byte) ([]byte, error) {
+	pkeyOurs := C._goboringcrypto_EVP_PKEY_new()
+	if pkeyOurs == nil {
+		return nil, NewOpenSSLError("EVP_PKEY_new failed")
+	}
+	defer C._goboringcrypto_EVP_PKEY_free(pkeyOurs)
+	if priv.withKey(func(key *C.GO_EC_KEY) C.int {
+		return C._goboringcrypto_EVP_PKEY_set1_EC_KEY(pkeyOurs, key)
+	}) != 1 {
+		return nil, NewOpenSSLError("EVP_PKEY_set1_EC_KEY")
+	}
+
+	pub, err := getPeerKey(priv, peerPublicKey)
+	if err != nil {
+		return nil, err
+	}
+
+	pkeyPeers := C._goboringcrypto_EVP_PKEY_new()
+	if pkeyPeers == nil {
+		return nil, NewOpenSSLError("EVP_PKEY_new failed")
+	}
+	defer C._goboringcrypto_EVP_PKEY_free(pkeyPeers)
+	if pub.withKey(func(key *C.GO_EC_KEY) C.int {
+		return C._goboringcrypto_EVP_PKEY_set1_EC_KEY(pkeyPeers, key)
+	}) != 1 {
+		return nil, NewOpenSSLError("EVP_PKEY_set1_EC_KEY")
+	}
+
+	ctx := C._goboringcrypto_EVP_PKEY_CTX_new(pkeyOurs, nil)
+	if ctx == nil {
+		return nil, NewOpenSSLError("EVP_PKEY_CTX_new failed")
+	}
+	defer C._goboringcrypto_EVP_PKEY_CTX_free(ctx)
+	if C._goboringcrypto_EVP_PKEY_derive_init(ctx) != 1 {
+		return nil, NewOpenSSLError("EVP_PKEY_derive_init failed")
+	}
+	if C._goboringcrypto_EVP_PKEY_derive_set_peer_ex(ctx, pkeyPeers, 1) != 1 {
+		return nil, NewOpenSSLError("EVP_PKEY_derive_set_peer_ex failed")
+	}
+	var outLen C.size_t
+	if C._goboringcrypto_EVP_PKEY_derive(ctx, nil, &outLen) != 1 {
+		return nil, NewOpenSSLError("EVP_PKEY_derive_init failed")
+	}
+	out := make([]byte, outLen)
+	if C._goboringcrypto_EVP_PKEY_derive(ctx, base(out), &outLen) != 1 {
+		return nil, NewOpenSSLError("EVP_PKEY_derive_init failed")
+	}
+	return out[:outLen], nil
+}
diff --git a/v2/openssl/ecdh_test.go b/v2/openssl/ecdh_test.go
new file mode 100644
index 00000000..f0857484
--- /dev/null
+++ b/v2/openssl/ecdh_test.go
@@ -0,0 +1,74 @@
+package openssl_test
+
+import (
+	"bytes"
+	"github.com/golang-fips/openssl-fips/v2/openssl"
+	"github.com/golang-fips/openssl-fips/v2/openssl/bbig"
+	"math/big"
+	"testing"
+)
+
+func TestSharedKeyECDH(t *testing.T) {
+	if !openssl.Enabled() {
+		t.Skip("boringcrypto: skipping test, FIPS not enabled")
+	}
+	// Test vector from CAVS
+	x, ok := new(big.Int).SetString(
+		"ead218590119e8876b29146ff89ca61770c4edbbf97d38ce385ed281d8a6b230",
+		16,
+	)
+	if !ok {
+		panic("bad hex")
+	}
+	y, ok := new(big.Int).SetString(
+		"28af61281fd35e2fa7002523acc85a429cb06ee6648325389f59edfce1405141",
+		16,
+	)
+	if !ok {
+		panic("bad hex")
+	}
+	k, ok := new(big.Int).SetString(
+		"7d7dc5f71eb29ddaf80d6214632eeae03d9058af1fb6d22ed80badb62bc1a534",
+		16,
+	)
+	if !ok {
+		panic("bad hex")
+	}
+	peerPublicKey := []byte{
+		// uncompressed
+		0x04,
+		// X
+		0x70, 0x0c, 0x48, 0xf7, 0x7f, 0x56, 0x58, 0x4c,
+		0x5c, 0xc6, 0x32, 0xca, 0x65, 0x64, 0x0d, 0xb9,
+		0x1b, 0x6b, 0xac, 0xce, 0x3a, 0x4d, 0xf6, 0xb4,
+		0x2c, 0xe7, 0xcc, 0x83, 0x88, 0x33, 0xd2, 0x87,
+		// Y
+		0xdb, 0x71, 0xe5, 0x09, 0xe3, 0xfd, 0x9b, 0x06,
+		0x0d, 0xdb, 0x20, 0xba, 0x5c, 0x51, 0xdc, 0xc5,
+		0x94, 0x8d, 0x46, 0xfb, 0xf6, 0x40, 0xdf, 0xe0,
+		0x44, 0x17, 0x82, 0xca, 0xb8, 0x5f, 0xa4, 0xac,
+	}
+	expected := []byte{
+		0x46, 0xfc, 0x62, 0x10, 0x64, 0x20, 0xff, 0x01,
+		0x2e, 0x54, 0xa4, 0x34, 0xfb, 0xdd, 0x2d, 0x25,
+		0xcc, 0xc5, 0x85, 0x20, 0x60, 0x56, 0x1e, 0x68,
+		0x04, 0x0d, 0xd7, 0x77, 0x89, 0x97, 0xbd, 0x7b,
+	}
+
+	bx, by, bk := bbig.Enc(x), bbig.Enc(y), bbig.Enc(k)
+
+	priv, err := openssl.NewPrivateKeyECDH("P-256", bx, by, bk)
+	if err != nil {
+		t.Log("NewPrivateKeyECDH failed", err)
+		t.Fail()
+	}
+	derived, err := openssl.SharedKeyECDH(priv, peerPublicKey)
+	if err != nil {
+		t.Log("SharedKeyECDH failed", err)
+		t.Fail()
+	}
+	if !bytes.Equal(derived, expected) {
+		t.Log("derived shared secret doesn't match")
+		t.Fail()
+	}
+}
diff --git a/v2/openssl/ecdsa.go b/v2/openssl/ecdsa.go
new file mode 100644
index 00000000..eb635079
--- /dev/null
+++ b/v2/openssl/ecdsa.go
@@ -0,0 +1,179 @@
+// Copyright 2017 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+//go:build linux && !android && !cmd_go_bootstrap && !msan && !no_openssl
+// +build linux,!android,!cmd_go_bootstrap,!msan,!no_openssl
+
+package openssl
+
+// #include "goopenssl.h"
+import "C"
+import (
+	"errors"
+	"runtime"
+	"unsafe"
+)
+
+type ecdsaSignature struct {
+	R, S BigInt
+}
+
+type PrivateKeyECDSA struct {
+	key *C.GO_EC_KEY
+}
+
+func (k *PrivateKeyECDSA) finalize() {
+	C._goboringcrypto_EC_KEY_free(k.key)
+}
+
+type PublicKeyECDSA struct {
+	key *C.GO_EC_KEY
+}
+
+func (k *PublicKeyECDSA) finalize() {
+	C._goboringcrypto_EC_KEY_free(k.key)
+}
+
+var errUnknownCurve = errors.New("boringcrypto: unknown elliptic curve")
+var errUnsupportedCurve = errors.New("boringcrypto: unsupported elliptic curve")
+
+func curveNID(curve string) (C.int, error) {
+	switch curve {
+	case "P-224":
+		if ExecutingTest() {
+			return C.GO_NID_secp224r1, nil
+		}
+		return 0, errUnsupportedCurve
+	case "P-256":
+		return C.GO_NID_X9_62_prime256v1, nil
+	case "P-384":
+		return C.GO_NID_secp384r1, nil
+	case "P-521":
+		return C.GO_NID_secp521r1, nil
+	}
+	return 0, errUnknownCurve
+}
+
+func NewPublicKeyECDSA(curve string, X, Y BigInt) (*PublicKeyECDSA, error) {
+	key, err := newECKey(curve, X, Y)
+	if err != nil {
+		return nil, err
+	}
+	k := &PublicKeyECDSA{key}
+	// Note: Because of the finalizer, any time k.key is passed to cgo,
+	// that call must be followed by a call to runtime.KeepAlive(k),
+	// to make sure k is not collected (and finalized) before the cgo
+	// call returns.
+	runtime.SetFinalizer(k, (*PublicKeyECDSA).finalize)
+	return k, nil
+}
+
+func newECKey(curve string, X, Y BigInt) (*C.GO_EC_KEY, error) {
+	nid, err := curveNID(curve)
+	if err != nil {
+		return nil, err
+	}
+	key := C._goboringcrypto_EC_KEY_new_by_curve_name(nid)
+	if key == nil {
+		return nil, NewOpenSSLError("EC_KEY_new_by_curve_name failed")
+	}
+	group := C._goboringcrypto_EC_KEY_get0_group(key)
+	pt := C._goboringcrypto_EC_POINT_new(group)
+	if pt == nil {
+		C._goboringcrypto_EC_KEY_free(key)
+		return nil, NewOpenSSLError("EC_POINT_new failed")
+	}
+	bx := bigToBN(X)
+	by := bigToBN(Y)
+	ok := bx != nil && by != nil && C._goboringcrypto_EC_POINT_set_affine_coordinates_GFp(group, pt, bx, by, nil) != 0 &&
+		C._goboringcrypto_EC_KEY_set_public_key(key, pt) != 0
+	if bx != nil {
+		C._goboringcrypto_BN_free(bx)
+	}
+	if by != nil {
+		C._goboringcrypto_BN_free(by)
+	}
+	C._goboringcrypto_EC_POINT_free(pt)
+	if !ok {
+		C._goboringcrypto_EC_KEY_free(key)
+		return nil, NewOpenSSLError("EC_POINT_free failed")
+	}
+	return key, nil
+}
+
+func NewPrivateKeyECDSA(curve string, X, Y BigInt, D BigInt) (*PrivateKeyECDSA, error) {
+	key, err := newECKey(curve, X, Y)
+	if err != nil {
+		return nil, err
+	}
+	bd := bigToBN(D)
+	ok := bd != nil && C._goboringcrypto_EC_KEY_set_private_key(key, bd) != 0
+	if bd != nil {
+		C._goboringcrypto_BN_free(bd)
+	}
+	if !ok {
+		C._goboringcrypto_EC_KEY_free(key)
+		return nil, NewOpenSSLError("EC_KEY_set_private_key failed")
+	}
+	k := &PrivateKeyECDSA{key}
+	// Note: Because of the finalizer, any time k.key is passed to cgo,
+	// that call must be followed by a call to runtime.KeepAlive(k),
+	// to make sure k is not collected (and finalized) before the cgo
+	// call returns.
+	runtime.SetFinalizer(k, (*PrivateKeyECDSA).finalize)
+	return k, nil
+}
+
+func SignMarshalECDSA(priv *PrivateKeyECDSA, hash []byte) ([]byte, error) {
+	size := C._goboringcrypto_ECDSA_size(priv.key)
+	sig := make([]byte, size)
+	var sigLen C.uint
+	ok := C._goboringcrypto_internal_ECDSA_sign(0, base(hash), C.size_t(len(hash)), (*C.uint8_t)(unsafe.Pointer(&sig[0])), &sigLen, priv.key) > 0
+	if !ok {
+		return nil, NewOpenSSLError(("ECDSA_sign failed"))
+	}
+
+	runtime.KeepAlive(priv)
+	return sig[:sigLen], nil
+}
+func VerifyECDSA(pub *PublicKeyECDSA, hash []byte, sig []byte) bool {
+	ok := C._goboringcrypto_internal_ECDSA_verify(0, base(hash), C.size_t(len(hash)), (*C.uint8_t)(unsafe.Pointer(&sig[0])), C.uint(len(sig)), pub.key) > 0
+	runtime.KeepAlive(pub)
+	return ok
+}
+
+func GenerateKeyECDSA(curve string) (X, Y, D BigInt, err error) {
+	nid, err := curveNID(curve)
+	if err != nil {
+		return nil, nil, nil, err
+	}
+	key := C._goboringcrypto_EC_KEY_new_by_curve_name(nid)
+	if key == nil {
+		return nil, nil, nil, NewOpenSSLError("EC_KEY_new_by_curve_name failed")
+	}
+	defer C._goboringcrypto_EC_KEY_free(key)
+	if C._goboringcrypto_EC_KEY_generate_key(key) == 0 {
+		return nil, nil, nil, NewOpenSSLError("EC_KEY_generate_key failed")
+	}
+	group := C._goboringcrypto_EC_KEY_get0_group(key)
+	pt := C._goboringcrypto_EC_KEY_get0_public_key(key)
+	bd := C._goboringcrypto_EC_KEY_get0_private_key(key)
+	if pt == nil || bd == nil {
+		return nil, nil, nil, NewOpenSSLError("EC_KEY_get0_private_key failed")
+	}
+	bx := C._goboringcrypto_BN_new()
+	if bx == nil {
+		return nil, nil, nil, NewOpenSSLError("BN_new failed")
+	}
+	defer C._goboringcrypto_BN_free(bx)
+	by := C._goboringcrypto_BN_new()
+	if by == nil {
+		return nil, nil, nil, NewOpenSSLError("BN_new failed")
+	}
+	defer C._goboringcrypto_BN_free(by)
+	if C._goboringcrypto_EC_POINT_get_affine_coordinates_GFp(group, pt, bx, by, nil) == 0 {
+		return nil, nil, nil, NewOpenSSLError("EC_POINT_get_affine_coordinates_GFp failed")
+	}
+	return bnToBig(bx), bnToBig(by), bnToBig(bd), nil
+}
diff --git a/v2/openssl/goopenssl.h b/v2/openssl/goopenssl.h
new file mode 100644
index 00000000..fae69930
--- /dev/null
+++ b/v2/openssl/goopenssl.h
@@ -0,0 +1,849 @@
+// Copyright 2017 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+// +build linux
+// +build !android
+// +build !no_openssl
+// +build !cmd_go_bootstrap
+// +build !msan
+
+// This header file describes the OpenSSL ABI as built for use in Go.
+
+#include <stdlib.h> // size_t
+#include <stdint.h> // uint8_t
+
+#include <openssl/ossl_typ.h>
+
+#if OPENSSL_VERSION_NUMBER < 0x30000000
+#define OPENSSL_DLSYM_CALL(handle, func) dlsym(handle, func)
+#else
+#define __USE_GNU
+#define OPENSSL_DLSYM_CALL(handle, func) dlvsym(handle, func, "OPENSSL_3.0.0")
+#endif
+
+#include <dlfcn.h>
+
+#define unlikely(x) __builtin_expect(!!(x), 0)
+#define DEFINEFUNC(ret, func, args, argscall)        \
+	typedef ret(*_goboringcrypto_PTR_##func) args;   \
+	static _goboringcrypto_PTR_##func _g_##func = 0; \
+	static inline ret _goboringcrypto_##func args    \
+	{                                                \
+		if (unlikely(!_g_##func))                    \
+		{                                            \
+			_g_##func = OPENSSL_DLSYM_CALL(handle, #func); \
+		}                                            \
+		return _g_##func argscall;                   \
+	}
+
+#define DEFINEFUNCINTERNAL(ret, func, args, argscall)        \
+	typedef ret(*_goboringcrypto_internal_PTR_##func) args;   \
+	static _goboringcrypto_internal_PTR_##func _g_internal_##func = 0; \
+	static inline ret _goboringcrypto_internal_##func args    \
+	{                                                \
+		if (unlikely(!_g_internal_##func))                    \
+		{                                            \
+			_g_internal_##func = OPENSSL_DLSYM_CALL(handle, #func); \
+		}                                            \
+		return _g_internal_##func argscall;                   \
+	}
+
+#define DEFINEMACRO(ret, func, args, argscall)    \
+	static inline ret _goboringcrypto_##func args \
+	{                                             \
+		return func argscall;                     \
+	}
+
+
+static void* handle;
+static void*
+_goboringcrypto_DLOPEN_OPENSSL(void)
+{
+	if (handle)
+	{
+		return handle;
+	}
+#if OPENSSL_VERSION_NUMBER < 0x10100000L
+	handle = dlopen("libcrypto.so.10", RTLD_NOW | RTLD_GLOBAL);
+#elif OPENSSL_VERSION_NUMBER < 0x30000000L
+	handle = dlopen("libcrypto.so.1.1", RTLD_NOW | RTLD_GLOBAL);
+#else
+	handle = dlopen("libcrypto.so.3", RTLD_NOW | RTLD_GLOBAL);
+#endif
+	return handle;
+}
+
+#include <openssl/opensslv.h>
+#include <openssl/ssl.h>
+
+DEFINEFUNCINTERNAL(void, OPENSSL_init, (void), ())
+
+static unsigned long _goboringcrypto_internal_OPENSSL_VERSION_NUMBER(void) {
+	return OPENSSL_VERSION_NUMBER;
+}
+
+static void
+_goboringcrypto_OPENSSL_setup(void) {
+	_goboringcrypto_internal_OPENSSL_init();
+}
+
+#include <openssl/err.h>
+DEFINEFUNCINTERNAL(void, ERR_print_errors_fp, (FILE* fp), (fp))
+#if OPENSSL_VERSION_NUMBER < 0x30000000
+DEFINEFUNCINTERNAL(unsigned long, ERR_get_error_line_data,
+		   (const char **file, int *line, const char **data, int *flags),
+		   (file, line, data, flags))
+static inline unsigned long
+_goboringcrypto_internal_ERR_get_error_all(const char **file, int *line, const char **func, const char **data, int *flags)
+{
+	unsigned long e = _goboringcrypto_internal_ERR_get_error_line_data(file, line, data, flags);
+	if (e == 0 && func != NULL) {
+		*func = "unknown";
+	}
+	return e;
+}
+#else
+DEFINEFUNCINTERNAL(unsigned long, ERR_get_error_all,
+		(const char **file, int *line, const char **func, const char **data, int *flags),
+		(file, line, func, data, flags))
+#endif
+DEFINEFUNCINTERNAL(void, ERR_error_string_n, (unsigned long e, unsigned char *buf, size_t len), (e, buf, len))
+
+#include <openssl/crypto.h>
+
+#if OPENSSL_VERSION_NUMBER < 0x10100000L
+DEFINEFUNC(int, CRYPTO_num_locks, (void), ())
+#else
+static inline int
+_goboringcrypto_CRYPTO_num_locks(void) {
+	return CRYPTO_num_locks(); /* defined as macro */
+}
+#endif
+#if OPENSSL_VERSION_NUMBER < 0x10100000L
+DEFINEFUNC(void, CRYPTO_set_id_callback, (unsigned long (*id_function)(void)), (id_function))
+#else
+static inline void
+_goboringcrypto_CRYPTO_set_id_callback(unsigned long (*id_function)(void)) {
+	CRYPTO_set_id_callback(id_function); /* defined as macro */
+}
+#endif
+#if OPENSSL_VERSION_NUMBER < 0x10100000L
+DEFINEFUNC(void, CRYPTO_set_locking_callback,
+	(void (*locking_function)(int mode, int n, const char *file, int line)), 
+	(locking_function))
+#else
+static inline void
+_goboringcrypto_CRYPTO_set_locking_callback(void (*locking_function)(int mode, int n, const char *file, int line)) {
+	CRYPTO_set_locking_callback(locking_function); /* defined as macro */
+}
+#endif
+
+int _goboringcrypto_OPENSSL_thread_setup(void);
+
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
+DEFINEFUNC(int, FIPS_mode, (void), ())
+DEFINEFUNC(int, FIPS_mode_set, (int r), (r))
+#else
+DEFINEFUNC(int, EVP_default_properties_is_fips_enabled, (OSSL_LIB_CTX *libctx), (libctx))
+static inline int _goboringcrypto_FIPS_mode(void) {
+	return _goboringcrypto_EVP_default_properties_is_fips_enabled(NULL);
+}
+#endif
+
+#include <openssl/rand.h>
+
+DEFINEFUNC(int, RAND_set_rand_method, (const RAND_METHOD *rand), (rand))
+DEFINEFUNC(const RAND_METHOD*, RAND_get_rand_method, (void), ())
+DEFINEFUNC(int, RAND_bytes, (uint8_t * arg0, size_t arg1), (arg0, arg1))
+
+int _goboringcrypto_stub_openssl_rand(void);
+int _goboringcrypto_restore_openssl_rand(void);
+int fbytes(unsigned char *buf, int num);
+
+
+#include <openssl/obj_mac.h>
+
+enum
+{
+	GO_NID_md5_sha1 = NID_md5_sha1,
+
+	GO_NID_secp224r1 = NID_secp224r1,
+	GO_NID_X9_62_prime256v1 = NID_X9_62_prime256v1,
+	GO_NID_secp384r1 = NID_secp384r1,
+	GO_NID_secp521r1 = NID_secp521r1,
+
+	GO_NID_sha224 = NID_sha224,
+	GO_NID_sha256 = NID_sha256,
+	GO_NID_sha384 = NID_sha384,
+	GO_NID_sha512 = NID_sha512,
+};
+
+#include <openssl/sha.h>
+
+typedef SHA_CTX GO_SHA_CTX;
+
+DEFINEFUNC(int, SHA1_Init, (GO_SHA_CTX * arg0), (arg0))
+DEFINEFUNC(int, SHA1_Update, (GO_SHA_CTX * arg0, const void *arg1, size_t arg2), (arg0, arg1, arg2))
+DEFINEFUNC(int, SHA1_Final, (uint8_t * arg0, GO_SHA_CTX *arg1), (arg0, arg1))
+
+typedef SHA256_CTX GO_SHA256_CTX;
+
+DEFINEFUNC(int, SHA224_Init, (GO_SHA256_CTX * arg0), (arg0))
+DEFINEFUNC(int, SHA224_Update, (GO_SHA256_CTX * arg0, const void *arg1, size_t arg2), (arg0, arg1, arg2))
+DEFINEFUNC(int, SHA224_Final, (uint8_t * arg0, GO_SHA256_CTX *arg1), (arg0, arg1))
+
+DEFINEFUNC(int, SHA256_Init, (GO_SHA256_CTX * arg0), (arg0))
+DEFINEFUNC(int, SHA256_Update, (GO_SHA256_CTX * arg0, const void *arg1, size_t arg2), (arg0, arg1, arg2))
+DEFINEFUNC(int, SHA256_Final, (uint8_t * arg0, GO_SHA256_CTX *arg1), (arg0, arg1))
+
+typedef SHA512_CTX GO_SHA512_CTX;
+DEFINEFUNC(int, SHA384_Init, (GO_SHA512_CTX * arg0), (arg0))
+DEFINEFUNC(int, SHA384_Update, (GO_SHA512_CTX * arg0, const void *arg1, size_t arg2), (arg0, arg1, arg2))
+DEFINEFUNC(int, SHA384_Final, (uint8_t * arg0, GO_SHA512_CTX *arg1), (arg0, arg1))
+
+DEFINEFUNC(int, SHA512_Init, (GO_SHA512_CTX * arg0), (arg0))
+DEFINEFUNC(int, SHA512_Update, (GO_SHA512_CTX * arg0, const void *arg1, size_t arg2), (arg0, arg1, arg2))
+DEFINEFUNC(int, SHA512_Final, (uint8_t * arg0, GO_SHA512_CTX *arg1), (arg0, arg1))
+
+#include <openssl/evp.h>
+
+typedef EVP_MD GO_EVP_MD;
+DEFINEFUNC(const GO_EVP_MD *, EVP_md4, (void), ())
+DEFINEFUNC(const GO_EVP_MD *, EVP_md5, (void), ())
+DEFINEFUNC(const GO_EVP_MD *, EVP_sha1, (void), ())
+DEFINEFUNC(const GO_EVP_MD *, EVP_sha224, (void), ())
+DEFINEFUNC(const GO_EVP_MD *, EVP_sha256, (void), ())
+DEFINEFUNC(const GO_EVP_MD *, EVP_sha384, (void), ())
+DEFINEFUNC(const GO_EVP_MD *, EVP_sha512, (void), ())
+DEFINEFUNC(const GO_EVP_MD *, EVP_md_null, (void), ())
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
+DEFINEFUNCINTERNAL(int, EVP_MD_type, (const GO_EVP_MD *arg0), (arg0))
+#else
+DEFINEFUNCINTERNAL(int, EVP_MD_get_type, (const GO_EVP_MD *arg0), (arg0))
+#endif
+DEFINEFUNCINTERNAL(size_t, EVP_MD_size, (const GO_EVP_MD *arg0), (arg0))
+DEFINEFUNCINTERNAL(const GO_EVP_MD*, EVP_md5_sha1, (void), ())
+
+# include <openssl/md5.h>
+DEFINEFUNCINTERNAL(int, MD5_Init, (MD5_CTX *c), (c))
+DEFINEFUNCINTERNAL(int, MD5_Update, (MD5_CTX *c, const void *data, size_t len), (c, data, len))
+DEFINEFUNCINTERNAL(int, MD5_Final, (unsigned char *md, MD5_CTX *c), (md, c))
+
+static inline int
+_goboringcrypto_EVP_MD_type(const GO_EVP_MD *md) {
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
+	return _goboringcrypto_internal_EVP_MD_type(md);
+#else
+	return _goboringcrypto_internal_EVP_MD_get_type(md);
+#endif
+}
+
+const GO_EVP_MD* _goboringcrypto_backport_EVP_md5_sha1(void);
+static inline const GO_EVP_MD*
+_goboringcrypto_EVP_md5_sha1(void) {
+#if OPENSSL_VERSION_NUMBER < 0x10100000L
+	return _goboringcrypto_backport_EVP_md5_sha1();
+#else
+	return _goboringcrypto_internal_EVP_md5_sha1();
+#endif
+}
+
+#include <openssl/hmac.h>
+
+typedef HMAC_CTX GO_HMAC_CTX;
+
+DEFINEFUNC(int, HMAC_Init_ex,
+		   (GO_HMAC_CTX * arg0, const void *arg1, int arg2, const GO_EVP_MD *arg3, ENGINE *arg4),
+		   (arg0, arg1, arg2, arg3, arg4))
+DEFINEFUNC(int, HMAC_Update, (GO_HMAC_CTX * arg0, const uint8_t *arg1, size_t arg2), (arg0, arg1, arg2))
+DEFINEFUNC(int, HMAC_Final, (GO_HMAC_CTX * arg0, uint8_t *arg1, unsigned int *arg2), (arg0, arg1, arg2))
+DEFINEFUNC(size_t, HMAC_CTX_copy, (GO_HMAC_CTX *dest, GO_HMAC_CTX *src), (dest, src))
+
+#if OPENSSL_VERSION_NUMBER < 0x10100000L
+DEFINEFUNCINTERNAL(void, HMAC_CTX_cleanup, (GO_HMAC_CTX * arg0), (arg0))
+static inline void
+_goboringcrypto_HMAC_CTX_free(HMAC_CTX *ctx) {
+   if (ctx != NULL) {
+       _goboringcrypto_HMAC_CTX_cleanup(ctx);
+       free(ctx);
+   }
+}
+#else
+DEFINEFUNC(void, HMAC_CTX_free, (GO_HMAC_CTX * arg0), (arg0))
+#endif
+
+#if OPENSSL_VERSION_NUMBER < 0x10100000L
+static inline size_t
+_goboringcrypto_HMAC_size(const GO_HMAC_CTX* arg0) {
+	return _goboringcrypto_internal_EVP_MD_size(arg0->md);
+}
+#else
+DEFINEFUNCINTERNAL(const EVP_MD*, HMAC_CTX_get_md, (const GO_HMAC_CTX* ctx), (ctx))
+# if OPENSSL_VERSION_NUMBER < 0x30000000L
+static inline size_t
+_goboringcrypto_HMAC_size(const GO_HMAC_CTX* arg0) {
+	const EVP_MD* md;
+	md = _goboringcrypto_internal_HMAC_CTX_get_md(arg0);
+	return _goboringcrypto_internal_EVP_MD_size(md);
+}
+# else
+DEFINEFUNCINTERNAL(size_t, EVP_MD_get_size, (const GO_EVP_MD *arg0), (arg0))
+static inline size_t
+_goboringcrypto_HMAC_size(const GO_HMAC_CTX* arg0) {
+	const EVP_MD* md;
+	md = _goboringcrypto_internal_HMAC_CTX_get_md(arg0);
+	return _goboringcrypto_internal_EVP_MD_get_size(md);
+}
+# endif
+#endif
+
+#if OPENSSL_VERSION_NUMBER < 0x10100000L
+DEFINEFUNCINTERNAL(void, HMAC_CTX_init, (GO_HMAC_CTX * arg0), (arg0))
+static inline GO_HMAC_CTX*
+_goboringcrypto_HMAC_CTX_new(void) {
+	GO_HMAC_CTX* ctx = malloc(sizeof(GO_HMAC_CTX));
+	if (ctx != NULL)
+		_goboringcrypto_internal_HMAC_CTX_init(ctx);
+	return ctx;
+}
+#else
+DEFINEFUNC(GO_HMAC_CTX*, HMAC_CTX_new, (void), ())
+#endif
+
+#if OPENSSL_VERSION_NUMBER < 0x10100000L
+static inline int
+_goboringcrypto_HMAC_CTX_reset(GO_HMAC_CTX* ctx) {
+	_goboringcrypto_HMAC_CTX_cleanup(ctx);
+	_goboringcrypto_HMAC_CTX_init(ctx);
+	return 0;
+}
+#else
+DEFINEFUNC(int, HMAC_CTX_reset, (GO_HMAC_CTX * arg0), (arg0))
+#endif
+
+int _goboringcrypto_HMAC_CTX_copy_ex(GO_HMAC_CTX *dest, const GO_HMAC_CTX *src);
+
+#include <openssl/evp.h>
+#include <openssl/aes.h>
+
+DEFINEFUNC(EVP_CIPHER_CTX *, EVP_CIPHER_CTX_new, (void), ())
+DEFINEFUNC(int, EVP_CIPHER_CTX_set_padding, (EVP_CIPHER_CTX *x, int padding), (x, padding))
+DEFINEFUNC(int, EVP_CipherInit_ex,
+		   (EVP_CIPHER_CTX * ctx, const EVP_CIPHER *type, ENGINE *impl, const unsigned char *key, const unsigned char *iv, int enc),
+		   (ctx, type, impl, key, iv, enc))
+DEFINEFUNC(int, EVP_CipherUpdate,
+		   (EVP_CIPHER_CTX * ctx, unsigned char *out, int *outl, const unsigned char *in, int inl),
+		   (ctx, out, outl, in, inl))
+
+void _goboringcrypto_EVP_AES_ctr128_enc(EVP_CIPHER_CTX *ctx, const uint8_t *in, uint8_t *out, size_t len);
+
+int _goboringcrypto_EVP_AES_encrypt(EVP_CIPHER_CTX *ctx, const uint8_t *in, size_t in_len, uint8_t *out);
+
+enum
+{
+	GO_AES_ENCRYPT = 1,
+	GO_AES_DECRYPT = 0
+};
+void _goboringcrypto_EVP_AES_cbc_encrypt(EVP_CIPHER_CTX *ctx, const uint8_t *arg0, uint8_t *arg1, size_t arg2, const uint8_t *a, const int arg5);
+DEFINEFUNC(void, AES_cbc_encrypt,
+		   (const unsigned char *in, unsigned char *out,
+                     size_t length, const AES_KEY *key,
+                     unsigned char *ivec, const int enc),
+		   (in, out, length, key, ivec, enc))
+
+void EVP_AES_cbc_enc(EVP_CIPHER_CTX *ctx, const uint8_t *in, uint8_t *out, size_t len);
+
+void EVP_AES_cbc_dec(EVP_CIPHER_CTX *ctx, const uint8_t *in, uint8_t *out, size_t len);
+
+typedef ENGINE GO_ENGINE;
+
+#include <openssl/bn.h>
+
+typedef BN_CTX GO_BN_CTX;
+typedef BIGNUM GO_BIGNUM;
+
+DEFINEFUNC(GO_BIGNUM *, BN_new, (void), ())
+DEFINEFUNC(void, BN_free, (GO_BIGNUM * arg0), (arg0))
+DEFINEFUNC(void, BN_clear_free, (GO_BIGNUM * arg0), (arg0))
+DEFINEFUNC(int, BN_set_word, (BIGNUM *a, BN_ULONG w), (a, w))
+DEFINEFUNC(unsigned int, BN_num_bits, (const GO_BIGNUM *arg0), (arg0))
+DEFINEFUNC(int, BN_is_negative, (const GO_BIGNUM *arg0), (arg0))
+DEFINEFUNC(GO_BIGNUM *, BN_bin2bn, (const uint8_t *arg0, size_t arg1, GO_BIGNUM *arg2), (arg0, arg1, arg2))
+DEFINEFUNC(int, BN_bn2le_padded, (uint8_t *out, size_t len, const BIGNUM *in), (out, len, in))
+DEFINEFUNC(GO_BIGNUM *, BN_le2bn, (const uint8_t *in, size_t len, BIGNUM *ret), (in, len, ret))
+DEFINEFUNC(size_t, BN_bn2bin, (const GO_BIGNUM *arg0, uint8_t *arg1), (arg0, arg1))
+
+static inline unsigned int
+_goboringcrypto_BN_num_bytes(const GO_BIGNUM* a) {
+	return ((_goboringcrypto_BN_num_bits(a)+7)/8);
+}
+
+#include <openssl/ec.h>
+
+typedef EC_GROUP GO_EC_GROUP;
+
+DEFINEFUNC(GO_EC_GROUP *, EC_GROUP_new_by_curve_name, (int arg0), (arg0))
+DEFINEFUNC(void, EC_GROUP_free, (GO_EC_GROUP * arg0), (arg0))
+
+typedef EC_POINT GO_EC_POINT;
+
+DEFINEFUNC(GO_EC_POINT *, EC_POINT_new, (const GO_EC_GROUP *arg0), (arg0))
+DEFINEFUNC(void, EC_POINT_free, (GO_EC_POINT * arg0), (arg0))
+DEFINEFUNC(int, EC_POINT_get_affine_coordinates_GFp,
+		   (const GO_EC_GROUP *arg0, const GO_EC_POINT *arg1, GO_BIGNUM *arg2, GO_BIGNUM *arg3, GO_BN_CTX *arg4),
+		   (arg0, arg1, arg2, arg3, arg4))
+DEFINEFUNC(int, EC_POINT_set_affine_coordinates_GFp,
+		   (const GO_EC_GROUP *arg0, GO_EC_POINT *arg1, const GO_BIGNUM *arg2, const GO_BIGNUM *arg3, GO_BN_CTX *arg4),
+		   (arg0, arg1, arg2, arg3, arg4))
+
+typedef EC_KEY GO_EC_KEY;
+
+DEFINEFUNC(GO_EC_KEY *, EC_KEY_new, (void), ())
+DEFINEFUNC(GO_EC_KEY *, EC_KEY_new_by_curve_name, (int arg0), (arg0))
+DEFINEFUNC(int, EC_KEY_oct2key, (GO_EC_KEY *arg0, const unsigned char *arg1, size_t arg2, BN_CTX *arg3), (arg0, arg1, arg2, arg3))
+DEFINEFUNC(void, EC_KEY_free, (GO_EC_KEY * arg0), (arg0))
+DEFINEFUNC(const GO_EC_GROUP *, EC_KEY_get0_group, (const GO_EC_KEY *arg0), (arg0))
+DEFINEFUNC(int, EC_KEY_set_group, (GO_EC_KEY *arg0, const EC_GROUP *arg1), (arg0, arg1))
+DEFINEFUNC(int, EC_KEY_generate_key, (GO_EC_KEY * arg0), (arg0))
+DEFINEFUNC(int, EC_KEY_set_private_key, (GO_EC_KEY * arg0, const GO_BIGNUM *arg1), (arg0, arg1))
+DEFINEFUNC(int, EC_KEY_set_public_key, (GO_EC_KEY * arg0, const GO_EC_POINT *arg1), (arg0, arg1))
+DEFINEFUNC(const GO_BIGNUM *, EC_KEY_get0_private_key, (const GO_EC_KEY *arg0), (arg0))
+DEFINEFUNC(const GO_EC_POINT *, EC_KEY_get0_public_key, (const GO_EC_KEY *arg0), (arg0))
+
+// TODO: EC_KEY_check_fips?
+
+#include <openssl/ecdsa.h>
+
+typedef ECDSA_SIG GO_ECDSA_SIG;
+
+DEFINEFUNC(GO_ECDSA_SIG *, ECDSA_SIG_new, (void), ())
+DEFINEFUNC(void, ECDSA_SIG_free, (GO_ECDSA_SIG * arg0), (arg0))
+DEFINEFUNC(GO_ECDSA_SIG *, ECDSA_do_sign, (const uint8_t *arg0, size_t arg1, const GO_EC_KEY *arg2), (arg0, arg1, arg2))
+DEFINEFUNC(int, ECDSA_do_verify, (const uint8_t *arg0, size_t arg1, const GO_ECDSA_SIG *arg2, const GO_EC_KEY *arg3), (arg0, arg1, arg2, arg3))
+DEFINEFUNC(size_t, ECDSA_size, (const GO_EC_KEY *arg0), (arg0))
+
+DEFINEFUNCINTERNAL(int, ECDSA_sign, 
+	(int type, const unsigned char *dgst, size_t dgstlen, unsigned char *sig, unsigned int *siglen, EC_KEY *eckey),
+	(type, dgst, dgstlen, sig, siglen, eckey))
+
+DEFINEFUNCINTERNAL(int, ECDSA_verify, 
+	(int type, const unsigned char *dgst, size_t dgstlen, const unsigned char *sig, unsigned int siglen, EC_KEY *eckey),
+	(type, dgst, dgstlen, sig, siglen, eckey))
+
+#if OPENSSL_VERSION_NUMBER < 0x10100000L
+DEFINEFUNC(EVP_MD_CTX*, EVP_MD_CTX_create, (void), ())
+#else
+DEFINEFUNCINTERNAL(EVP_MD_CTX*, EVP_MD_CTX_new, (void), ())
+static inline EVP_MD_CTX* _goboringcrypto_EVP_MD_CTX_create(void) {
+	return _goboringcrypto_internal_EVP_MD_CTX_new();
+}
+#endif
+
+DEFINEFUNCINTERNAL(int, EVP_PKEY_assign,
+	(EVP_PKEY *pkey, int type, void *eckey),
+	(pkey, type, eckey))
+
+static inline int
+_goboringcrypto_EVP_PKEY_assign_EC_KEY(EVP_PKEY *pkey, GO_EC_KEY *eckey) {
+	return _goboringcrypto_internal_EVP_PKEY_assign(pkey, EVP_PKEY_EC, (char *)(eckey));
+}
+
+static inline int
+_goboringcrypto_EVP_PKEY_assign_RSA(EVP_PKEY *pkey, RSA *rsa) {
+	return _goboringcrypto_internal_EVP_PKEY_assign(pkey, EVP_PKEY_RSA, (char *)(rsa));
+}
+
+DEFINEFUNC(int, EVP_DigestSignInit,
+	(EVP_MD_CTX* ctx, EVP_PKEY_CTX **pctx, const EVP_MD *type, ENGINE *e, const EVP_PKEY *pkey),
+	(ctx, pctx, type, e, pkey))
+
+DEFINEFUNC(int, EVP_DigestUpdate,
+	(EVP_MD_CTX* ctx, const void *d, size_t cnt),
+	(ctx, d, cnt))
+DEFINEFUNC(int, EVP_DigestSignFinal,
+	(EVP_MD_CTX* ctx, unsigned char *sig, unsigned int *siglen),
+	(ctx, sig, siglen))
+
+DEFINEFUNC(int, EVP_DigestVerifyInit,
+	(EVP_MD_CTX* ctx, EVP_PKEY_CTX **pctx, const EVP_MD *type, ENGINE *e, const EVP_PKEY *pkey),
+	(ctx, pctx, type, e, pkey))
+DEFINEFUNC(int, EVP_DigestVerifyFinal,
+	(EVP_MD_CTX* ctx, const uint8_t *sig, unsigned int siglen),
+	(ctx, sig, siglen))
+
+typedef RSA GO_RSA;
+int _goboringcrypto_EVP_sign(EVP_MD* md, EVP_PKEY_CTX *ctx, const uint8_t *msg, size_t msgLen, uint8_t *sig, unsigned int *slen, EVP_PKEY *eckey);
+int _goboringcrypto_EVP_sign_raw(EVP_MD *md, EVP_PKEY_CTX *ctx, const uint8_t *msg,
+                                                              size_t msgLen, uint8_t *sig, size_t *slen,
+                                                              GO_RSA *key);
+
+int _goboringcrypto_EVP_verify(EVP_MD* md, EVP_PKEY_CTX *ctx, const uint8_t *msg, size_t msgLen, const uint8_t *sig, unsigned int slen, EVP_PKEY *key);
+int _goboringcrypto_EVP_verify_raw(const uint8_t *msg, size_t msgLen,
+                               const uint8_t *sig, unsigned int slen,
+                               GO_RSA *key);
+
+#if OPENSSL_VERSION_NUMBER < 0x10100000L
+DEFINEFUNC(void, EVP_MD_CTX_destroy, (EVP_MD_CTX *ctx), (ctx))
+#else
+DEFINEFUNCINTERNAL(void, EVP_MD_CTX_free, (EVP_MD_CTX *ctx), (ctx))
+static inline void _goboringcrypto_EVP_MD_CTX_free(EVP_MD_CTX *ctx) {
+	return _goboringcrypto_internal_EVP_MD_CTX_free(ctx);
+}
+#endif
+
+int _goboringcrypto_ECDSA_sign(EVP_MD *md, const uint8_t *arg1, size_t arg2, uint8_t *arg3, unsigned int *arg4, GO_EC_KEY *arg5);
+int _goboringcrypto_ECDSA_verify(EVP_MD *md, const uint8_t *arg1, size_t arg2, const uint8_t *arg3, unsigned int arg4, GO_EC_KEY *arg5);
+
+#include <openssl/rsa.h>
+
+// Note: order of struct fields here is unchecked.
+typedef BN_GENCB GO_BN_GENCB;
+
+int _goboringcrypto_EVP_RSA_sign(EVP_MD* md, const uint8_t *msg, unsigned int msgLen, uint8_t *sig, unsigned int *slen, RSA *rsa);
+int _goboringcrypto_EVP_RSA_verify(EVP_MD* md, const uint8_t *msg, unsigned int msgLen, const uint8_t *sig, unsigned int slen, GO_RSA *rsa);
+
+DEFINEFUNC(GO_RSA *, RSA_new, (void), ())
+DEFINEFUNC(void, RSA_free, (GO_RSA * arg0), (arg0))
+DEFINEFUNC(int, RSA_private_encrypt,
+	(int flen, const unsigned char *from, unsigned char *to, RSA *rsa, int padding),
+	(flen, from, to, rsa, padding))
+DEFINEFUNC(int, RSA_public_decrypt,
+	(int flen, const unsigned char *from, unsigned char *to, RSA *rsa, int padding),
+	(flen, from, to, rsa, padding))
+DEFINEFUNC(int, RSA_sign,
+	(int arg0, const uint8_t *arg1, unsigned int arg2, uint8_t *arg3, unsigned int *arg4, GO_RSA *arg5),
+	(arg0, arg1, arg2, arg3, arg4, arg5))
+DEFINEFUNC(int, RSA_verify,
+	(int arg0, const uint8_t *arg1, unsigned int arg2, const uint8_t *arg3, unsigned int arg4, GO_RSA *arg5),
+	(arg0, arg1, arg2, arg3, arg4, arg5))
+DEFINEFUNC(int, RSA_generate_key_ex,
+	(GO_RSA * arg0, int arg1, GO_BIGNUM *arg2, GO_BN_GENCB *arg3),
+	(arg0, arg1, arg2, arg3))
+
+DEFINEFUNCINTERNAL(int, RSA_set0_factors,
+	(GO_RSA * rsa, GO_BIGNUM *p, GO_BIGNUM *q),
+	(rsa, p, q))
+
+static inline int
+_goboringcrypto_RSA_set0_factors(GO_RSA * r, GO_BIGNUM *p, GO_BIGNUM *q) {
+#if OPENSSL_VERSION_NUMBER < 0x10100000L
+    /* If the fields p and q in r are NULL, the corresponding input
+     * parameters MUST be non-NULL.
+     */
+    if ((r->p == NULL && p == NULL)
+        || (r->q == NULL && q == NULL))
+        return 0;
+
+    if (p != NULL) {
+        _goboringcrypto_BN_clear_free(r->p);
+        r->p = p;
+    }
+    if (q != NULL) {
+        _goboringcrypto_BN_clear_free(r->q);
+        r->q = q;
+    }
+
+    return 1;
+#else
+	return _goboringcrypto_internal_RSA_set0_factors(r, p, q);
+#endif
+}
+
+DEFINEFUNCINTERNAL(int, RSA_set0_crt_params,
+		   (GO_RSA * rsa, GO_BIGNUM *dmp1, GO_BIGNUM *dmp2, GO_BIGNUM *iqmp),
+		   (rsa, dmp1, dmp2, iqmp))
+
+static inline int
+_goboringcrypto_RSA_set0_crt_params(GO_RSA * r, GO_BIGNUM *dmp1, GO_BIGNUM *dmq1, GO_BIGNUM *iqmp) {
+#if OPENSSL_VERSION_NUMBER < 0x10100000L
+    /* If the fields dmp1, dmq1 and iqmp in r are NULL, the corresponding input
+     * parameters MUST be non-NULL.
+     */
+    if ((r->dmp1 == NULL && dmp1 == NULL)
+        || (r->dmq1 == NULL && dmq1 == NULL)
+        || (r->iqmp == NULL && iqmp == NULL))
+        return 0;
+
+    if (dmp1 != NULL) {
+        _goboringcrypto_BN_clear_free(r->dmp1);
+        r->dmp1 = dmp1;
+    }
+    if (dmq1 != NULL) {
+        _goboringcrypto_BN_clear_free(r->dmq1);
+        r->dmq1 = dmq1;
+    }
+    if (iqmp != NULL) {
+        _goboringcrypto_BN_clear_free(r->iqmp);
+        r->iqmp = iqmp;
+    }
+
+    return 1;
+#else
+	return _goboringcrypto_internal_RSA_set0_crt_params(r, dmp1, dmq1, iqmp);
+#endif
+}
+
+DEFINEFUNCINTERNAL(void, RSA_get0_crt_params,
+		   (const GO_RSA *r, const GO_BIGNUM **dmp1, const GO_BIGNUM **dmq1, const GO_BIGNUM **iqmp),
+		   (r, dmp1, dmq1, iqmp))
+static inline void
+_goboringcrypto_RSA_get0_crt_params(const GO_RSA *r, const GO_BIGNUM **dmp1, const GO_BIGNUM **dmq1, const GO_BIGNUM **iqmp) {
+#if OPENSSL_VERSION_NUMBER < 0x10100000L
+    if (dmp1 != NULL)
+        *dmp1 = r->dmp1;
+    if (dmq1 != NULL)
+        *dmq1 = r->dmq1;
+    if (iqmp != NULL)
+        *iqmp = r->iqmp;
+#else
+	_goboringcrypto_internal_RSA_get0_crt_params(r, dmp1, dmq1, iqmp);
+#endif
+}
+
+
+DEFINEFUNCINTERNAL(int, RSA_set0_key,
+		   (GO_RSA * r, GO_BIGNUM *n, GO_BIGNUM *e, GO_BIGNUM *d),
+		   (r, n, e, d))
+static inline int
+_goboringcrypto_RSA_set0_key(GO_RSA * r, GO_BIGNUM *n, GO_BIGNUM *e, GO_BIGNUM *d) {
+#if OPENSSL_VERSION_NUMBER < 0x10100000L
+    /* If the fields n and e in r are NULL, the corresponding input
+     * parameters MUST be non-NULL for n and e.  d may be
+     * left NULL (in case only the public key is used).
+     */
+    if ((r->n == NULL && n == NULL)
+        || (r->e == NULL && e == NULL))
+        return 0;
+
+    if (n != NULL) {
+        _goboringcrypto_BN_free(r->n);
+        r->n = n;
+    }
+    if (e != NULL) {
+        _goboringcrypto_BN_free(r->e);
+        r->e = e;
+    }
+    if (d != NULL) {
+        _goboringcrypto_BN_clear_free(r->d);
+        r->d = d;
+    }
+
+    return 1;
+#else
+	return _goboringcrypto_internal_RSA_set0_key(r, n, e, d);
+#endif
+}
+
+DEFINEFUNCINTERNAL(void, RSA_get0_factors,
+		   (const GO_RSA *rsa, const GO_BIGNUM **p, const GO_BIGNUM **q),
+		   (rsa, p, q))
+static inline void 
+_goboringcrypto_RSA_get0_factors(const GO_RSA *rsa, const GO_BIGNUM **p, const GO_BIGNUM **q) {
+#if OPENSSL_VERSION_NUMBER < 0x10100000L
+	if (p)
+		*p = rsa->p;
+	if (q)
+		*q = rsa->q;
+#else
+	_goboringcrypto_internal_RSA_get0_factors(rsa, p, q);
+#endif
+}
+
+DEFINEFUNCINTERNAL(void, RSA_get0_key,
+		   (const GO_RSA *rsa, const GO_BIGNUM **n, const GO_BIGNUM **e, const GO_BIGNUM **d),
+		   (rsa, n, e, d))
+static inline void 
+_goboringcrypto_RSA_get0_key(const GO_RSA *rsa, const GO_BIGNUM **n, const GO_BIGNUM **e, const GO_BIGNUM **d) {
+#if OPENSSL_VERSION_NUMBER < 0x10100000L
+	if (n)
+		*n = rsa->n;
+	if (e)
+		*e = rsa->e;
+	if (d)
+		*d = rsa->d;
+#else
+	_goboringcrypto_internal_RSA_get0_key(rsa, n, e, d);
+#endif
+}
+
+int _goboringcrypto_RSA_generate_key_fips(GO_RSA *, int, GO_BN_GENCB *);
+enum
+{
+	GO_RSA_PKCS1_PADDING = 1,
+	GO_RSA_NO_PADDING = 3,
+	GO_RSA_PKCS1_OAEP_PADDING = 4,
+	GO_RSA_PKCS1_PSS_PADDING = 6,
+};
+
+int _goboringcrypto_RSA_sign_pss_mgf1(GO_RSA *, unsigned int *out_len, uint8_t *out, unsigned int max_out, const uint8_t *in, unsigned int in_len, GO_EVP_MD *md, const GO_EVP_MD *mgf1_md, int salt_len);
+
+int _goboringcrypto_RSA_verify_pss_mgf1(GO_RSA *, const uint8_t *msg, unsigned int msg_len, GO_EVP_MD *md, const GO_EVP_MD *mgf1_md, int salt_len, const uint8_t *sig, unsigned int sig_len);
+
+DEFINEFUNC(unsigned int, RSA_size, (const GO_RSA *arg0), (arg0))
+DEFINEFUNC(int, RSA_check_key, (const GO_RSA *arg0), (arg0))
+
+DEFINEFUNC(int, EVP_EncryptInit_ex,
+	(EVP_CIPHER_CTX *ctx, const EVP_CIPHER *type, ENGINE *impl, const unsigned char *key, const unsigned char *iv),
+	(ctx, type, impl, key, iv))
+DEFINEFUNC(int, EVP_EncryptUpdate,
+	(EVP_CIPHER_CTX *ctx, unsigned char *out, int *outl, const unsigned char *in, int inl),
+	(ctx, out, outl, in, inl))
+DEFINEFUNC(int, EVP_EncryptFinal_ex,
+	(EVP_CIPHER_CTX *ctx, unsigned char *out, int *outl),
+	(ctx, out, outl))
+
+DEFINEFUNC(int, EVP_DecryptInit_ex,
+	(EVP_CIPHER_CTX *ctx, const EVP_CIPHER *type, ENGINE *impl, const unsigned char *key, const unsigned char *iv),
+	(ctx, type, impl, key, iv))
+DEFINEFUNC(int, EVP_DecryptUpdate,
+	(EVP_CIPHER_CTX *ctx, unsigned char *out, int *outl, const unsigned char *in, int inl),
+	(ctx, out, outl, in, inl))
+DEFINEFUNC(int, EVP_DecryptFinal_ex,
+	(EVP_CIPHER_CTX *ctx, unsigned char *outm, int *outl),
+	(ctx, outm, outl))
+
+DEFINEFUNC(const EVP_CIPHER*, EVP_aes_128_gcm, (void), ())
+DEFINEFUNC(const EVP_CIPHER*, EVP_aes_128_cbc, (void), ())
+DEFINEFUNC(const EVP_CIPHER*, EVP_aes_128_ctr, (void), ())
+DEFINEFUNC(const EVP_CIPHER*, EVP_aes_128_ecb, (void), ())
+DEFINEFUNC(const EVP_CIPHER*, EVP_aes_192_cbc, (void), ())
+DEFINEFUNC(const EVP_CIPHER*, EVP_aes_192_ctr, (void), ())
+DEFINEFUNC(const EVP_CIPHER*, EVP_aes_192_ecb, (void), ())
+DEFINEFUNC(const EVP_CIPHER*, EVP_aes_192_gcm, (void), ())
+DEFINEFUNC(const EVP_CIPHER*, EVP_aes_256_cbc, (void), ())
+DEFINEFUNC(const EVP_CIPHER*, EVP_aes_256_ctr, (void), ())
+DEFINEFUNC(const EVP_CIPHER*, EVP_aes_256_ecb, (void), ())
+DEFINEFUNC(const EVP_CIPHER*, EVP_aes_256_gcm, (void), ())
+
+DEFINEFUNC(void, EVP_CIPHER_CTX_free, (EVP_CIPHER_CTX* arg0), (arg0))
+DEFINEFUNC(int, EVP_CIPHER_CTX_ctrl, (EVP_CIPHER_CTX *ctx, int type, int arg, void *ptr), (ctx, type, arg, ptr))
+
+int _goboringcrypto_EVP_CIPHER_CTX_seal(
+	uint8_t *out, uint8_t *nonce,
+	uint8_t *aad, size_t aad_len,
+	uint8_t *plaintext, size_t plaintext_len,
+	size_t *ciphertext_len, uint8_t *key, int key_size);
+
+int _goboringcrypto_EVP_CIPHER_CTX_open(
+	uint8_t *ciphertext, int ciphertext_len,
+	uint8_t *aad, int aad_len,
+	uint8_t *tag, uint8_t *key, int key_size,
+	uint8_t *nonce, int nonce_len,
+	uint8_t *plaintext, size_t *plaintext_len);
+
+typedef EVP_PKEY GO_EVP_PKEY;
+
+DEFINEFUNC(GO_EVP_PKEY *, EVP_PKEY_new, (void), ())
+DEFINEFUNC(void, EVP_PKEY_free, (GO_EVP_PKEY * arg0), (arg0))
+DEFINEFUNC(int, EVP_PKEY_set1_RSA, (GO_EVP_PKEY * arg0, GO_RSA *arg1), (arg0, arg1))
+DEFINEFUNC(int, EVP_PKEY_set1_EC_KEY, (GO_EVP_PKEY * arg0, GO_EC_KEY *arg1), (arg0, arg1))
+DEFINEFUNC(int, EVP_PKEY_verify,
+	(EVP_PKEY_CTX *ctx, const unsigned char *sig, unsigned int siglen, const unsigned char *tbs, size_t tbslen),
+	(ctx, sig, siglen, tbs, tbslen))
+
+typedef EVP_PKEY_CTX GO_EVP_PKEY_CTX;
+
+DEFINEFUNC(GO_EVP_PKEY_CTX *, EVP_PKEY_CTX_new, (GO_EVP_PKEY * arg0, ENGINE *arg1), (arg0, arg1))
+DEFINEFUNC(void, EVP_PKEY_CTX_free, (GO_EVP_PKEY_CTX * arg0), (arg0))
+DEFINEFUNC(int, EVP_PKEY_CTX_ctrl,
+		   (EVP_PKEY_CTX * ctx, int keytype, int optype, int cmd, int p1, void *p2),
+		   (ctx, keytype, optype, cmd, p1, p2))
+DEFINEFUNCINTERNAL(int, RSA_pkey_ctx_ctrl,
+		   (EVP_PKEY_CTX *ctx, int optype, int cmd, int p1, void *p2),
+		   (ctx, optype, cmd, p1, p2))
+
+static inline int
+_goboringcrypto_EVP_PKEY_CTX_set_rsa_padding(GO_EVP_PKEY_CTX* ctx, int pad) {
+#if OPENSSL_VERSION_NUMBER < 0x10100000L
+	return _goboringcrypto_EVP_PKEY_CTX_ctrl(ctx, EVP_PKEY_RSA, -1, EVP_PKEY_CTRL_RSA_PADDING, pad, NULL);
+#else
+    return _goboringcrypto_internal_RSA_pkey_ctx_ctrl(ctx, -1, EVP_PKEY_CTRL_RSA_PADDING, pad, NULL);
+#endif
+}
+
+#if OPENSSL_VERSION_NUMBER < 0x30000000
+static inline int
+_goboringcrypto_EVP_PKEY_CTX_set0_rsa_oaep_label(GO_EVP_PKEY_CTX *ctx, uint8_t *l, int llen)
+{
+       return _goboringcrypto_EVP_PKEY_CTX_ctrl(ctx, EVP_PKEY_RSA, EVP_PKEY_OP_TYPE_CRYPT, EVP_PKEY_CTRL_RSA_OAEP_LABEL, llen, (void *)l);
+}
+#else
+DEFINEFUNC(int, EVP_PKEY_CTX_set0_rsa_oaep_label,
+		(GO_EVP_PKEY_CTX *ctx, uint8_t *l, int llen),
+		(ctx, l, llen))
+#endif
+
+static inline int
+_goboringcrypto_EVP_PKEY_CTX_set_rsa_oaep_md(GO_EVP_PKEY_CTX *ctx, const GO_EVP_MD *md)
+{
+	return _goboringcrypto_EVP_PKEY_CTX_ctrl(ctx, EVP_PKEY_RSA, EVP_PKEY_OP_TYPE_CRYPT, EVP_PKEY_CTRL_RSA_OAEP_MD, 0, (void *)md);
+}
+
+static inline int
+_goboringcrypto_EVP_PKEY_CTX_set_rsa_pss_saltlen(GO_EVP_PKEY_CTX * arg0, int arg1) {
+	return _goboringcrypto_EVP_PKEY_CTX_ctrl(arg0, EVP_PKEY_RSA, 
+		(EVP_PKEY_OP_SIGN|EVP_PKEY_OP_VERIFY), 
+		EVP_PKEY_CTRL_RSA_PSS_SALTLEN, 
+		arg1, NULL);
+}
+
+static inline int
+_goboringcrypto_EVP_PKEY_CTX_set_signature_md(EVP_PKEY_CTX *ctx, const EVP_MD *md) {
+	return _goboringcrypto_EVP_PKEY_CTX_ctrl(ctx, -1, EVP_PKEY_OP_TYPE_SIG, EVP_PKEY_CTRL_MD, 0, (void *)md);
+}
+
+static inline int
+_goboringcrypto_EVP_PKEY_CTX_set_rsa_mgf1_md(GO_EVP_PKEY_CTX * ctx, const GO_EVP_MD *md) {
+	return _goboringcrypto_EVP_PKEY_CTX_ctrl(ctx, EVP_PKEY_RSA,
+                        EVP_PKEY_OP_TYPE_SIG | EVP_PKEY_OP_TYPE_CRYPT,
+                                EVP_PKEY_CTRL_RSA_MGF1_MD, 0, (void *)md);
+}
+
+DEFINEFUNC(int, EVP_PKEY_decrypt,
+		   (GO_EVP_PKEY_CTX * arg0, uint8_t *arg1, unsigned int *arg2, const uint8_t *arg3, unsigned int arg4),
+		   (arg0, arg1, arg2, arg3, arg4))
+DEFINEFUNC(int, EVP_PKEY_encrypt,
+		   (GO_EVP_PKEY_CTX * arg0, uint8_t *arg1, unsigned int *arg2, const uint8_t *arg3, unsigned int arg4),
+		   (arg0, arg1, arg2, arg3, arg4))
+DEFINEFUNC(int, EVP_PKEY_decrypt_init, (GO_EVP_PKEY_CTX * arg0), (arg0))
+DEFINEFUNC(int, EVP_PKEY_encrypt_init, (GO_EVP_PKEY_CTX * arg0), (arg0))
+DEFINEFUNC(int, EVP_PKEY_sign_init, (GO_EVP_PKEY_CTX * arg0), (arg0))
+DEFINEFUNC(int, EVP_PKEY_verify_init, (GO_EVP_PKEY_CTX * arg0), (arg0))
+DEFINEFUNC(int, EVP_PKEY_sign,
+		   (GO_EVP_PKEY_CTX * arg0, uint8_t *arg1, size_t *arg2, const uint8_t *arg3, size_t arg4),
+		   (arg0, arg1, arg2, arg3, arg4))
+
+DEFINEFUNC(int, EVP_PKEY_derive_init, (GO_EVP_PKEY_CTX *arg0), (arg0))
+DEFINEFUNC(int, EVP_PKEY_derive, (GO_EVP_PKEY_CTX *arg0, unsigned char *arg1, size_t *arg2), (arg0, arg1, arg2))
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+DEFINEFUNC(int, EVP_PKEY_derive_set_peer_ex, (GO_EVP_PKEY_CTX *arg0, GO_EVP_PKEY *arg1, int arg2), (arg0, arg1, arg2))
+#else
+DEFINEFUNC(int, EVP_PKEY_derive_set_peer, (GO_EVP_PKEY_CTX *arg0, GO_EVP_PKEY *arg1), (arg0, arg1))
+
+# if OPENSSL_VERSION_NUMBER >= 0x10100000L
+DEFINEFUNC(int, EVP_PKEY_public_check, (EVP_PKEY_CTX *arg0), (arg0))
+
+static inline int
+_goboringcrypto_EVP_PKEY_derive_set_peer_ex(GO_EVP_PKEY_CTX *ctx, GO_EVP_PKEY *key, int validate)
+{
+        EVP_PKEY_CTX *check_ctx = _goboringcrypto_EVP_PKEY_CTX_new(key, NULL);
+        if (check_ctx == NULL) {
+		return -1;
+	}
+        int ok = _goboringcrypto_EVP_PKEY_public_check(check_ctx);
+        _goboringcrypto_EVP_PKEY_CTX_free(check_ctx);
+        if (ok != 1) {
+		return -1;
+	}
+	return _goboringcrypto_EVP_PKEY_derive_set_peer(ctx, key);
+}
+# else
+static inline int
+_goboringcrypto_EVP_PKEY_derive_set_peer_ex(GO_EVP_PKEY_CTX *ctx, GO_EVP_PKEY *key, int validate)
+{
+	/* No way to validate public key in OpenSSL 1.0.2 */
+	(void)validate;
+	return _goboringcrypto_EVP_PKEY_derive_set_peer(ctx, key);
+}
+# endif
+#endif
diff --git a/v2/openssl/hmac.go b/v2/openssl/hmac.go
new file mode 100644
index 00000000..6f001777
--- /dev/null
+++ b/v2/openssl/hmac.go
@@ -0,0 +1,163 @@
+// Copyright 2017 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+//go:build linux && !android && !cmd_go_bootstrap && !msan && !no_openssl
+// +build linux,!android,!cmd_go_bootstrap,!msan,!no_openssl
+
+package openssl
+
+// #include "goopenssl.h"
+import "C"
+import (
+	"crypto"
+	"hash"
+	"runtime"
+	"unsafe"
+)
+
+// hashToMD converts a hash.Hash implementation from this package
+// to a BoringCrypto *C.GO_EVP_MD.
+func hashToMD(h hash.Hash) *C.GO_EVP_MD {
+	switch h.(type) {
+	case *sha1Hash:
+		return C._goboringcrypto_EVP_sha1()
+	case *sha224Hash:
+		return C._goboringcrypto_EVP_sha224()
+	case *sha256Hash:
+		return C._goboringcrypto_EVP_sha256()
+	case *sha384Hash:
+		return C._goboringcrypto_EVP_sha384()
+	case *sha512Hash:
+		return C._goboringcrypto_EVP_sha512()
+	}
+	return nil
+}
+
+// cryptoHashToMD converts a crypto.Hash
+// to a BoringCrypto *C.GO_EVP_MD.
+func cryptoHashToMD(ch crypto.Hash) *C.GO_EVP_MD {
+	switch ch {
+	case crypto.MD5:
+		return C._goboringcrypto_EVP_md5()
+	case crypto.MD5SHA1:
+		return C._goboringcrypto_EVP_md5_sha1()
+	case crypto.SHA1:
+		return C._goboringcrypto_EVP_sha1()
+	case crypto.SHA224:
+		return C._goboringcrypto_EVP_sha224()
+	case crypto.SHA256:
+		return C._goboringcrypto_EVP_sha256()
+	case crypto.SHA384:
+		return C._goboringcrypto_EVP_sha384()
+	case crypto.SHA512:
+		return C._goboringcrypto_EVP_sha512()
+	}
+	return nil
+}
+
+// NewHMAC returns a new HMAC using BoringCrypto.
+// The function h must return a hash implemented by
+// BoringCrypto (for example, h could be boring.NewSHA256).
+// If h is not recognized, NewHMAC returns nil.
+func NewHMAC(h func() hash.Hash, key []byte) hash.Hash {
+	ch := h()
+	md := hashToMD(ch)
+	if md == nil {
+		return nil
+	}
+
+	var hkey []byte
+	if key != nil && len(key) > 0 {
+		// Note: Could hash down long keys here using EVP_Digest.
+		hkey = make([]byte, len(key))
+		copy(hkey, key)
+	} else {
+		// This is supported in BoringSSL/Standard lib and as such
+		// we must support it here. When using HMAC with a null key
+		// HMAC_Init will try and reuse the key from the ctx. This is
+		// not the bahavior previously implemented, so as a workaround
+		// we pass an "empty" key.
+		hkey = make([]byte, C.EVP_MAX_MD_SIZE)
+	}
+	hmac := &boringHMAC{
+		md:        md,
+		size:      ch.Size(),
+		blockSize: ch.BlockSize(),
+		key:       hkey,
+		ctx:       C._goboringcrypto_HMAC_CTX_new(),
+	}
+	hmac.Reset()
+	return hmac
+}
+
+type boringHMAC struct {
+	md          *C.GO_EVP_MD
+	ctx         *C.GO_HMAC_CTX
+	ctx2        *C.GO_HMAC_CTX
+	size        int
+	blockSize   int
+	key         []byte
+	sum         []byte
+	needCleanup bool
+}
+
+func (h *boringHMAC) Reset() {
+	if !h.needCleanup {
+		h.needCleanup = true
+		// Note: Because of the finalizer, any time h.ctx is passed to cgo,
+		// that call must be followed by a call to runtime.KeepAlive(h),
+		// to make sure h is not collected (and finalized) before the cgo
+		// call returns.
+		runtime.SetFinalizer(h, (*boringHMAC).finalize)
+	}
+	C._goboringcrypto_HMAC_CTX_reset(h.ctx)
+
+	if C._goboringcrypto_HMAC_Init_ex(h.ctx, unsafe.Pointer(base(h.key)), C.int(len(h.key)), h.md, nil) == 0 {
+		panic("boringcrypto: HMAC_Init failed")
+	}
+	if int(C._goboringcrypto_HMAC_size(h.ctx)) != h.size {
+		println("boringcrypto: HMAC size:", C._goboringcrypto_HMAC_size(h.ctx), "!=", h.size)
+		panic("boringcrypto: HMAC size mismatch")
+	}
+	runtime.KeepAlive(h) // Next line will keep h alive too; just making doubly sure.
+	h.sum = nil
+}
+
+func (h *boringHMAC) finalize() {
+	C._goboringcrypto_HMAC_CTX_free(h.ctx)
+}
+
+func (h *boringHMAC) Write(p []byte) (int, error) {
+	if len(p) > 0 {
+		C._goboringcrypto_HMAC_Update(h.ctx, (*C.uint8_t)(unsafe.Pointer(&p[0])), C.size_t(len(p)))
+	}
+	runtime.KeepAlive(h)
+	return len(p), nil
+}
+
+func (h *boringHMAC) Size() int {
+	return h.size
+}
+
+func (h *boringHMAC) BlockSize() int {
+	return h.blockSize
+}
+
+func (h *boringHMAC) Sum(in []byte) []byte {
+	if h.sum == nil {
+		size := h.Size()
+		h.sum = make([]byte, size)
+	}
+	// Make copy of context because Go hash.Hash mandates
+	// that Sum has no effect on the underlying stream.
+	// In particular it is OK to Sum, then Write more, then Sum again,
+	// and the second Sum acts as if the first didn't happen.
+	h.ctx2 = C._goboringcrypto_HMAC_CTX_new()
+	if C._goboringcrypto_HMAC_CTX_copy_ex(h.ctx2, h.ctx) == 0 {
+		panic("boringcrypto: HMAC_CTX_copy_ex failed")
+	}
+	C._goboringcrypto_HMAC_Final(h.ctx2, (*C.uint8_t)(unsafe.Pointer(&h.sum[0])), nil)
+	C._goboringcrypto_HMAC_CTX_free(h.ctx2)
+	return append(in, h.sum...)
+}
diff --git a/v2/openssl/hmac_test.go b/v2/openssl/hmac_test.go
new file mode 100644
index 00000000..cb88f691
--- /dev/null
+++ b/v2/openssl/hmac_test.go
@@ -0,0 +1,17 @@
+package openssl
+
+import (
+	"testing"
+)
+
+// Just tests that we can create an HMAC instance.
+// Previously would cause panic because of incorrect
+// stack allocation of opaque OpenSSL type.
+func TestNewHMAC(t *testing.T) {
+	if !Enabled() {
+		t.Skip("boringcrypto: skipping test, FIPS not enabled")
+	}
+	mac := NewHMAC(NewSHA256, nil)
+	mac.Write([]byte("foo"))
+	t.Logf("%x\n", mac.Sum(nil))
+}
diff --git a/v2/openssl/notboring.go b/v2/openssl/notboring.go
new file mode 100644
index 00000000..ec095b4d
--- /dev/null
+++ b/v2/openssl/notboring.go
@@ -0,0 +1,130 @@
+// Copyright 2017 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+//go:build !linux || !cgo || android || cmd_go_bootstrap || msan || no_openssl
+// +build !linux !cgo android cmd_go_bootstrap msan no_openssl
+
+package openssl
+
+import (
+	"crypto"
+	"crypto/cipher"
+	"crypto/internal/boring/sig"
+	"hash"
+)
+
+var enabled = false
+
+// Unreachable marks code that should be unreachable
+// when BoringCrypto is in use. It is a no-op without BoringCrypto.
+func Unreachable() {
+	// Code that's unreachable when using BoringCrypto
+	// is exactly the code we want to detect for reporting
+	// standard Go crypto.
+	sig.StandardCrypto()
+}
+
+// UnreachableExceptTests marks code that should be unreachable
+// when BoringCrypto is in use. It is a no-op without BoringCrypto.
+func UnreachableExceptTests() {}
+
+func ExecutingTest() bool { return false }
+
+// This is a noop withotu BoringCrytpo.
+func PanicIfStrictFIPS(v interface{}) {}
+
+type randReader int
+
+func (randReader) Read(b []byte) (int, error) { panic("boringcrypto: not available") }
+
+const RandReader = randReader(0)
+
+func NewSHA1() hash.Hash   { panic("boringcrypto: not available") }
+func NewSHA224() hash.Hash { panic("boringcrypto: not available") }
+func NewSHA256() hash.Hash { panic("boringcrypto: not available") }
+func NewSHA384() hash.Hash { panic("boringcrypto: not available") }
+func NewSHA512() hash.Hash { panic("boringcrypto: not available") }
+
+func NewHMAC(h func() hash.Hash, key []byte) hash.Hash { panic("boringcrypto: not available") }
+
+func NewAESCipher(key []byte) (cipher.Block, error) { panic("boringcrypto: not available") }
+
+type PublicKeyECDSA struct{ _ int }
+type PrivateKeyECDSA struct{ _ int }
+
+func GenerateKeyECDSA(curve string) (X, Y, D BigInt, err error) {
+	panic("boringcrypto: not available")
+}
+func NewPrivateKeyECDSA(curve string, X, Y, D BigInt) (*PrivateKeyECDSA, error) {
+	panic("boringcrypto: not available")
+}
+func NewPublicKeyECDSA(curve string, X, Y BigInt) (*PublicKeyECDSA, error) {
+	panic("boringcrypto: not available")
+}
+func SignECDSA(priv *PrivateKeyECDSA, hash []byte, h crypto.Hash) (r, s BigInt, err error) {
+	panic("boringcrypto: not available")
+}
+func SignMarshalECDSA(priv *PrivateKeyECDSA, hash []byte, h crypto.Hash) ([]byte, error) {
+	panic("boringcrypto: not available")
+}
+func VerifyECDSA(pub *PublicKeyECDSA, hash []byte, r, s BigInt, h crypto.Hash) bool {
+	panic("boringcrypto: not available")
+}
+
+type PublicKeyECDH struct{ _ int }
+type PrivateKeyECDH struct{ _ int }
+
+func GenerateKeyECDH(curve string) (X, Y, D BigInt, err error) {
+	panic("boringcrypto: not available")
+}
+func NewPrivateKeyECDH(curve string, X, Y, D BigInt) (*PrivateKeyECDH, error) {
+	panic("boringcrypto: not available")
+}
+func NewPublicKeyECDH(curve string, X, Y BigInt) (*PublicKeyECDH, error) {
+	panic("boringcrypto: not available")
+}
+func SharedKeyECDH(priv *PrivateKeyECDH, peerPublicKey []byte) ([]byte, error) {
+	panic("boringcrypto: not available")
+}
+
+type PublicKeyRSA struct{ _ int }
+type PrivateKeyRSA struct{ _ int }
+
+func DecryptRSAOAEP(h hash.Hash, priv *PrivateKeyRSA, ciphertext, label []byte) ([]byte, error) {
+	panic("boringcrypto: not available")
+}
+func DecryptRSAPKCS1(priv *PrivateKeyRSA, ciphertext []byte) ([]byte, error) {
+	panic("boringcrypto: not available")
+}
+func DecryptRSANoPadding(priv *PrivateKeyRSA, ciphertext []byte) ([]byte, error) {
+	panic("boringcrypto: not available")
+}
+func EncryptRSAOAEP(h hash.Hash, pub *PublicKeyRSA, msg, label []byte) ([]byte, error) {
+	panic("boringcrypto: not available")
+}
+func EncryptRSAPKCS1(pub *PublicKeyRSA, msg []byte) ([]byte, error) {
+	panic("boringcrypto: not available")
+}
+func EncryptRSANoPadding(pub *PublicKeyRSA, msg []byte) ([]byte, error) {
+	panic("boringcrypto: not available")
+}
+func GenerateKeyRSA(bits int) (N, E, D, P, Q, Dp, Dq, Qinv BigInt, err error) {
+	panic("boringcrypto: not available")
+}
+func NewPrivateKeyRSA(N, E, D, P, Q, Dp, Dq, Qinv BigInt) (*PrivateKeyRSA, error) {
+	panic("boringcrypto: not available")
+}
+func NewPublicKeyRSA(N, E BigInt) (*PublicKeyRSA, error) { panic("boringcrypto: not available") }
+func SignRSAPKCS1v15(priv *PrivateKeyRSA, h crypto.Hash, hashed []byte, msgHashed bool) ([]byte, error) {
+	panic("boringcrypto: not available")
+}
+func SignRSAPSS(priv *PrivateKeyRSA, h crypto.Hash, hashed []byte, saltLen int) ([]byte, error) {
+	panic("boringcrypto: not available")
+}
+func VerifyRSAPKCS1v15(pub *PublicKeyRSA, h crypto.Hash, hashed, sig []byte, msgHashed bool) error {
+	panic("boringcrypto: not available")
+}
+func VerifyRSAPSS(pub *PublicKeyRSA, h crypto.Hash, hashed, sig []byte, saltLen int) error {
+	panic("boringcrypto: not available")
+}
diff --git a/v2/openssl/openssl.go b/v2/openssl/openssl.go
new file mode 100644
index 00000000..58cade7c
--- /dev/null
+++ b/v2/openssl/openssl.go
@@ -0,0 +1,247 @@
+// Copyright 2017 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+//go:build linux && !android && !cmd_go_bootstrap && !msan && !no_openssl
+// +build linux,!android,!cmd_go_bootstrap,!msan,!no_openssl
+
+package openssl
+
+/*
+#cgo LDFLAGS: -ldl
+
+#include "goopenssl.h"
+*/
+import "C"
+import (
+	"errors"
+	"fmt"
+	"math/bits"
+	"os"
+	"runtime"
+	"unsafe"
+)
+
+const (
+	fipsOn  = C.int(1)
+	fipsOff = C.int(0)
+)
+
+const GoStrictFipsEnv = "GOLANG_STRICT_FIPS"
+
+const (
+	OPENSSL_VERSION_1_1_0 = uint64(C.ulong(0x10100000))
+	OPENSSL_VERSION_3_0_0 = uint64(C.ulong(0x30000000))
+)
+
+// Enabled controls whether FIPS crypto is enabled.
+var enabled = false
+
+// When this variable is true, the go crypto API will panic when a caller
+// tries to use the API in a non-compliant manner.  When this is false, the
+// go crypto API will allow existing go crypto APIs to be used even
+// if they aren't FIPS compliant.  However, all the underlying crypto operations
+// will still be done by OpenSSL.
+var strictFIPS = false
+
+func init() {
+	runtime.LockOSThread()
+	defer runtime.UnlockOSThread()
+
+	// Check if we can `dlopen` OpenSSL
+	if C._goboringcrypto_DLOPEN_OPENSSL() == C.NULL {
+		return
+	}
+
+	// Initialize the OpenSSL library.
+	C._goboringcrypto_OPENSSL_setup()
+
+	// Check to see if the system is running in FIPS mode, if so
+	// enable "boring" mode to call into OpenSSL for FIPS compliance.
+	if fipsModeEnabled() {
+		enableBoringFIPSMode()
+	}
+}
+
+func openSSLVersion() uint64 {
+	return uint64(C._goboringcrypto_internal_OPENSSL_VERSION_NUMBER())
+}
+
+func enableBoringFIPSMode() {
+	enabled = true
+
+	if os.Getenv(GoStrictFipsEnv) == "1" {
+		strictFIPS = true
+	}
+
+	if C._goboringcrypto_OPENSSL_thread_setup() != 1 {
+		panic("boringcrypto: OpenSSL thread setup failed")
+	}
+}
+
+func fipsModeEnabled() bool {
+	// Due to the way providers work in openssl 3, the FIPS methods are not
+	// necessarily going to be available for us to load based on the GOLANG_FIPS
+	// environment variable alone. For now, we must rely on the config to tell
+	// us if the provider is configured and active.
+	fipsConfigured := C._goboringcrypto_FIPS_mode() == fipsOn
+	openSSLVersion := openSSLVersion()
+	if openSSLVersion >= OPENSSL_VERSION_3_0_0 {
+		if !fipsConfigured && os.Getenv("GOLANG_FIPS") == "1" {
+			panic("GOLANG_FIPS=1 specified but OpenSSL FIPS provider is not configured")
+		}
+		return fipsConfigured
+
+	} else {
+		return os.Getenv("GOLANG_FIPS") == "1" || fipsConfigured
+	}
+}
+
+var randstub bool
+
+func RandStubbed() bool {
+	return randstub
+}
+
+func StubOpenSSLRand() {
+	if !randstub {
+		randstub = true
+		C._goboringcrypto_stub_openssl_rand()
+	}
+}
+
+func RestoreOpenSSLRand() {
+	if randstub {
+		randstub = false
+		C._goboringcrypto_restore_openssl_rand()
+	}
+}
+
+func hasSuffix(s, t string) bool {
+	return len(s) > len(t) && s[len(s)-len(t):] == t
+}
+
+func PanicIfStrictFIPS(msg string) {
+	if IsStrictFips() {
+		panic(msg)
+	}
+}
+
+func IsStrictFips() bool {
+	return os.Getenv(GoStrictFipsEnv) == "1" || strictFIPS
+}
+
+func NewOpenSSLError(msg string) error {
+	var e C.ulong
+	message := fmt.Sprintf("\n%v\nopenssl error(s):", msg)
+	for {
+		var buf [256]C.char
+		var file, fnc, data *C.char
+		var line, flags C.int
+		e = C._goboringcrypto_internal_ERR_get_error_all(&file, &line, &fnc, &data, &flags)
+		if e == 0 {
+			break
+		}
+
+		C._goboringcrypto_internal_ERR_error_string_n(e, (*C.uchar)(unsafe.Pointer(&buf[0])), C.size_t(len(buf)))
+		message = fmt.Sprintf(
+			"%v\nfile: %v\nline: %v\nfunction: %v\nflags: %v\nerror string: %s\n",
+			message, C.GoString(file), line, C.GoString(fnc), flags, C.GoString(&(buf[0])))
+
+	}
+	return errors.New(message)
+}
+
+// Unreachable marks code that should be unreachable
+// when FIPS mode. It panics only when
+// the system is in FIPS mode.
+func Unreachable() {
+	if Enabled() {
+		panic("openssl: invalid code execution")
+	}
+}
+
+// UnreachableExceptTests marks code that should be unreachable
+// when FIPS mode is active. It panics only when the system is in FIPS mode
+// and not executing under tests.
+func UnreachableExceptTests() {
+	name := os.Args[0]
+	if Enabled() && !ExecutingTest() {
+		println("openssl: unexpected code execution in", name)
+		panic("openssl: invalid code execution")
+	}
+}
+
+// ExecutingTest returns a boolean indicating if we're
+// executing under a test binary or not.
+func ExecutingTest() bool {
+	name := os.Args[0]
+	return hasSuffix(name, "_test") || hasSuffix(name, ".test")
+}
+
+type fail string
+
+func (e fail) Error() string { return "boringcrypto: " + string(e) + " failed" }
+
+func wbase(b BigInt) *C.uint8_t {
+	if len(b) == 0 {
+		return nil
+	}
+	return (*C.uint8_t)(unsafe.Pointer(&b[0]))
+}
+
+const wordBytes = bits.UintSize / 8
+
+func bigToBN(x BigInt) *C.GO_BIGNUM {
+	return C._goboringcrypto_BN_le2bn(wbase(x), C.size_t(len(x)*wordBytes), nil)
+}
+
+func bnToBig(bn *C.GO_BIGNUM) BigInt {
+	x := make(BigInt, (C._goboringcrypto_BN_num_bytes(bn)+wordBytes-1)/wordBytes)
+	if C._goboringcrypto_BN_bn2le_padded(wbase(x), C.size_t(len(x)*wordBytes), bn) == 0 {
+		panic("boringcrypto: bignum conversion failed")
+	}
+	return x
+}
+
+func bigToBn(bnp **C.GO_BIGNUM, b BigInt) bool {
+	if *bnp != nil {
+		C._goboringcrypto_BN_free(*bnp)
+		*bnp = nil
+	}
+	if b == nil {
+		return true
+	}
+	bn := bigToBN(b)
+	if bn == nil {
+		return false
+	}
+	*bnp = bn
+	return true
+}
+
+// noescape hides a pointer from escape analysis.  noescape is
+// the identity function but escape analysis doesn't think the
+// output depends on the input.  noescape is inlined and currently
+// compiles down to zero instructions.
+// USE CAREFULLY!
+//
+//go:nosplit
+func noescape(p unsafe.Pointer) unsafe.Pointer {
+	x := uintptr(p)
+	return unsafe.Pointer(x ^ 0)
+}
+
+var zero byte
+
+// addr converts p to its base addr, including a noescape along the way.
+// If p is nil, addr returns a non-nil pointer, so that the result can always
+// be dereferenced.
+//
+//go:nosplit
+func addr(p []byte) *byte {
+	if len(p) == 0 {
+		return &zero
+	}
+	return (*byte)(noescape(unsafe.Pointer(&p[0])))
+}
diff --git a/v2/openssl/openssl_ecdsa_signature.c b/v2/openssl/openssl_ecdsa_signature.c
new file mode 100644
index 00000000..2349db1f
--- /dev/null
+++ b/v2/openssl/openssl_ecdsa_signature.c
@@ -0,0 +1,46 @@
+// +build linux
+// +build !android
+// +build !no_openssl
+// +build !cmd_go_bootstrap
+// +build !msan
+
+#include "goopenssl.h"
+
+int _goboringcrypto_ECDSA_sign(EVP_MD *md, const uint8_t *msg, size_t msgLen,
+                               uint8_t *sig, unsigned int *slen,
+                               GO_EC_KEY *eckey) {
+  int result;
+  EVP_PKEY *key = _goboringcrypto_EVP_PKEY_new();
+  if (!key) {
+    return 0;
+  }
+  if (!_goboringcrypto_EVP_PKEY_set1_EC_KEY(key, eckey)) {
+    result = 0;
+    goto err;
+  }
+  result = _goboringcrypto_EVP_sign(md, NULL, msg, msgLen, sig, slen, key);
+err:
+  _goboringcrypto_EVP_PKEY_free(key);
+  return result;
+}
+
+int _goboringcrypto_ECDSA_verify(EVP_MD *md, const uint8_t *msg, size_t msgLen,
+                                 const uint8_t *sig, unsigned int slen,
+                                 GO_EC_KEY *eckey) {
+
+  int result;
+  EVP_PKEY *key = _goboringcrypto_EVP_PKEY_new();
+  if (!key) {
+    return 0;
+  }
+  if (!_goboringcrypto_EVP_PKEY_set1_EC_KEY(key, eckey)) {
+    result = 0;
+    goto err;
+  }
+
+  result = _goboringcrypto_EVP_verify(md, NULL, msg, msgLen, sig, slen, key);
+
+err:
+  _goboringcrypto_EVP_PKEY_free(key);
+  return result;
+}
diff --git a/v2/openssl/openssl_evp.c b/v2/openssl/openssl_evp.c
new file mode 100644
index 00000000..43790198
--- /dev/null
+++ b/v2/openssl/openssl_evp.c
@@ -0,0 +1,128 @@
+// +build linux
+// +build !android
+// +build !no_openssl
+// +build !cmd_go_bootstrap
+// +build !msan
+
+#include "goopenssl.h"
+
+int _goboringcrypto_EVP_sign(EVP_MD *md, EVP_PKEY_CTX *ctx, const uint8_t *msg,
+                             size_t msgLen, uint8_t *sig, unsigned int *slen,
+                             EVP_PKEY *key) {
+  EVP_MD_CTX *mdctx = NULL;
+  int ret = 0;
+
+  if (!(mdctx = _goboringcrypto_EVP_MD_CTX_create()))
+    goto err;
+
+  if (1 != _goboringcrypto_EVP_DigestSignInit(mdctx, &ctx, md, NULL, key))
+    goto err;
+
+  if (1 != _goboringcrypto_EVP_DigestUpdate(mdctx, msg, msgLen))
+    goto err;
+
+  /* Obtain the signature length */
+  if (1 != _goboringcrypto_EVP_DigestSignFinal(mdctx, NULL, slen))
+    goto err;
+  /* Obtain the signature */
+  if (1 != _goboringcrypto_EVP_DigestSignFinal(mdctx, sig, slen))
+    goto err;
+
+  /* Success */
+  ret = 1;
+
+err:
+  if (mdctx)
+    _goboringcrypto_EVP_MD_CTX_free(mdctx);
+
+  return ret;
+}
+
+int _goboringcrypto_EVP_sign_raw(EVP_MD *md, EVP_PKEY_CTX *ctx, const uint8_t *msg,
+                             size_t msgLen, uint8_t *sig, size_t *slen,
+                             GO_RSA *rsa_key) {
+  int ret = 0;
+  GO_EVP_PKEY *pk = _goboringcrypto_EVP_PKEY_new();
+  _goboringcrypto_EVP_PKEY_assign_RSA(pk, rsa_key);
+
+  if (!ctx && !(ctx = _goboringcrypto_EVP_PKEY_CTX_new(pk, NULL)))
+    goto err;
+
+  if (1 != _goboringcrypto_EVP_PKEY_sign_init(ctx))
+    goto err;
+
+  if (_goboringcrypto_EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_PADDING) <= 0)
+    goto err;
+
+  if (1 != _goboringcrypto_EVP_PKEY_sign(ctx, sig, slen, msg, msgLen))
+    goto err;
+
+  /* Success */
+  ret = 1;
+
+err:
+  if (ctx)
+    _goboringcrypto_EVP_PKEY_CTX_free(ctx);
+
+  return ret;
+}
+
+int _goboringcrypto_EVP_verify(EVP_MD *md, EVP_PKEY_CTX *ctx,
+                               const uint8_t *msg, size_t msgLen,
+                               const uint8_t *sig, unsigned int slen,
+                               EVP_PKEY *key) {
+  EVP_MD_CTX *mdctx = NULL;
+  int ret = 0;
+
+  if (!(mdctx = _goboringcrypto_EVP_MD_CTX_create()))
+    goto err;
+  if (1 != _goboringcrypto_EVP_DigestVerifyInit(mdctx, &ctx, md, NULL, key))
+    goto err;
+
+  if (1 != _goboringcrypto_EVP_DigestUpdate(mdctx, msg, msgLen))
+    goto err;
+
+  if (1 != _goboringcrypto_EVP_DigestVerifyFinal(mdctx, sig, slen)) {
+    goto err;
+  }
+
+  /* Success */
+  ret = 1;
+
+err:
+  if (mdctx)
+    _goboringcrypto_EVP_MD_CTX_free(mdctx);
+
+  return ret;
+}
+
+int _goboringcrypto_EVP_verify_raw(const uint8_t *msg, size_t msgLen,
+                               const uint8_t *sig, unsigned int slen,
+                               GO_RSA *rsa_key) {
+
+  int ret = 0;
+  EVP_PKEY_CTX *ctx;
+  GO_EVP_PKEY *pk = _goboringcrypto_EVP_PKEY_new();
+  _goboringcrypto_EVP_PKEY_assign_RSA(pk, rsa_key);
+
+  if (!(ctx = _goboringcrypto_EVP_PKEY_CTX_new(pk, NULL)))
+    goto err;
+
+  if (1 != _goboringcrypto_EVP_PKEY_verify_init(ctx))
+    goto err;
+
+  if (_goboringcrypto_EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_PADDING) <= 0)
+    goto err;
+
+  if (1 != _goboringcrypto_EVP_PKEY_verify(ctx, sig, slen, msg, msgLen))
+    goto err;
+
+  /* Success */
+  ret = 1;
+
+err:
+  if (ctx)
+    _goboringcrypto_EVP_PKEY_CTX_free(ctx);
+
+  return ret;
+}
diff --git a/v2/openssl/openssl_lock_setup.c b/v2/openssl/openssl_lock_setup.c
new file mode 100644
index 00000000..49d40a74
--- /dev/null
+++ b/v2/openssl/openssl_lock_setup.c
@@ -0,0 +1,49 @@
+// +build linux
+// +build !android
+// +build !no_openssl
+// +build !cmd_go_bootstrap
+// +build !msan
+
+#include "goopenssl.h"
+#include <openssl/err.h>
+#include <pthread.h>
+#include <stdio.h>
+#include <sys/syscall.h>
+#include <sys/types.h>
+
+#define _GNU_SOURCE
+#include <unistd.h>
+
+#define MUTEX_TYPE pthread_mutex_t
+#define MUTEX_SETUP(x) pthread_mutex_init(&(x), NULL)
+#define MUTEX_CLEANUP(x) pthread_mutex_destroy(&(x))
+#define MUTEX_LOCK(x) pthread_mutex_lock(&(x))
+#define MUTEX_UNLOCK(x) pthread_mutex_unlock(&(x))
+#define THREAD_ID pthread_self()
+
+/* This array will store all of the mutexes available to OpenSSL. */
+static MUTEX_TYPE *mutex_buf = NULL;
+
+static void locking_function(int mode, int n, const char *file, int line) {
+  if (mode & CRYPTO_LOCK)
+    MUTEX_LOCK(mutex_buf[n]);
+  else
+    MUTEX_UNLOCK(mutex_buf[n]);
+}
+
+static unsigned long id_function(void) {
+  return ((unsigned long)syscall(__NR_gettid));
+}
+
+int _goboringcrypto_OPENSSL_thread_setup(void) {
+  int i;
+
+  mutex_buf = malloc(_goboringcrypto_CRYPTO_num_locks() * sizeof(MUTEX_TYPE));
+  if (!mutex_buf)
+    return 0;
+  for (i = 0; i < _goboringcrypto_CRYPTO_num_locks(); i++)
+    MUTEX_SETUP(mutex_buf[i]);
+  _goboringcrypto_CRYPTO_set_id_callback(id_function);
+  _goboringcrypto_CRYPTO_set_locking_callback(locking_function);
+  return 1;
+}
diff --git a/v2/openssl/openssl_port_aead_gcm.c b/v2/openssl/openssl_port_aead_gcm.c
new file mode 100644
index 00000000..7eb645ef
--- /dev/null
+++ b/v2/openssl/openssl_port_aead_gcm.c
@@ -0,0 +1,171 @@
+// This file contains a port of the BoringSSL AEAD interface.
+// +build linux
+// +build !android
+// +build !no_openssl
+// +build !cmd_go_bootstrap
+// +build !msan
+
+#include "goopenssl.h"
+#include <openssl/err.h>
+
+int _goboringcrypto_EVP_CIPHER_CTX_seal(uint8_t *out, uint8_t *iv, uint8_t *aad,
+                                        size_t aad_len, uint8_t *plaintext,
+                                        size_t plaintext_len,
+                                        size_t *ciphertext_len, uint8_t *key,
+                                        int key_size) {
+
+  EVP_CIPHER_CTX *ctx;
+  int len;
+  int ret;
+
+  if (plaintext_len == 0) {
+    plaintext = "";
+  }
+
+  if (aad_len == 0) {
+    aad = "";
+  }
+
+  // Create and initialise the context.
+  if (!(ctx = _goboringcrypto_EVP_CIPHER_CTX_new())) {
+    goto err;
+  }
+
+  switch (key_size) {
+  case 128:
+    if (!_goboringcrypto_EVP_EncryptInit_ex(
+            ctx, _goboringcrypto_EVP_aes_128_gcm(), NULL, NULL, NULL)) {
+      goto err;
+    }
+    break;
+  case 256:
+    if (!_goboringcrypto_EVP_EncryptInit_ex(
+            ctx, _goboringcrypto_EVP_aes_256_gcm(), NULL, NULL, NULL)) {
+      goto err;
+    }
+    break;
+  default:
+    goto err;
+  }
+
+  // Initialize IV.
+  if (!_goboringcrypto_EVP_EncryptInit_ex(ctx, NULL, NULL, key, NULL)) {
+    goto err;
+  }
+  if (!_goboringcrypto_EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_IVLEN, 12,
+                                           0)) {
+    goto err;
+  }
+  if (!_goboringcrypto_EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_IV_FIXED, -1,
+                                           iv)) {
+    goto err;
+  }
+  if (!_goboringcrypto_EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_IV_GEN, 0, iv)) {
+    goto err;
+  }
+
+  // Provide AAD data.
+  if (!_goboringcrypto_EVP_EncryptUpdate(ctx, NULL, &len, aad, aad_len)) {
+    goto err;
+  }
+
+  if (!_goboringcrypto_EVP_EncryptUpdate(ctx, out, &len, plaintext,
+                                         plaintext_len)) {
+    goto err;
+  }
+  *ciphertext_len = len;
+
+  if (!_goboringcrypto_EVP_EncryptFinal_ex(ctx, out + len, &len)) {
+    goto err;
+  }
+  *ciphertext_len += len;
+
+  if (!_goboringcrypto_EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_GET_TAG, 16,
+                                           out + (*ciphertext_len))) {
+    goto err;
+  }
+  *ciphertext_len += 16;
+  ret = 1;
+
+err:
+  _goboringcrypto_EVP_CIPHER_CTX_free(ctx);
+
+  if (ret > 0) {
+    return ret;
+  } else {
+    return 0;
+  }
+}
+
+int _goboringcrypto_EVP_CIPHER_CTX_open(uint8_t *ciphertext, int ciphertext_len,
+                                        uint8_t *aad, int aad_len, uint8_t *tag,
+                                        uint8_t *key, int key_size, uint8_t *iv,
+                                        int iv_len, uint8_t *plaintext,
+                                        size_t *plaintext_len) {
+
+  EVP_CIPHER_CTX *ctx;
+  int len;
+  int ret;
+
+  if (aad_len == 0) {
+    aad = "";
+  }
+
+  // Create and initialise the context.
+  if (!(ctx = _goboringcrypto_EVP_CIPHER_CTX_new()))
+    return 0;
+
+  switch (key_size) {
+  case 128:
+    if (!_goboringcrypto_EVP_DecryptInit_ex(
+            ctx, _goboringcrypto_EVP_aes_128_gcm(), NULL, NULL, NULL)) {
+      goto err;
+    }
+    break;
+  case 256:
+    if (!_goboringcrypto_EVP_DecryptInit_ex(
+            ctx, _goboringcrypto_EVP_aes_256_gcm(), NULL, NULL, NULL)) {
+      goto err;
+    }
+    break;
+  }
+
+  // Initialize key and nonce.
+  if (!_goboringcrypto_EVP_DecryptInit_ex(ctx, NULL, NULL, key, iv)) {
+    goto err;
+  }
+
+  // Provide any AAD data.
+  if (!_goboringcrypto_EVP_DecryptUpdate(ctx, NULL, &len, aad, aad_len)) {
+    goto err;
+  }
+
+  // Provide the message to be decrypted, and obtain the plaintext output.
+  if (!_goboringcrypto_EVP_DecryptUpdate(ctx, plaintext, &len, ciphertext,
+                                         ciphertext_len)) {
+    goto err;
+  }
+  *plaintext_len = len;
+
+  // Set expected tag value. Works in OpenSSL 1.0.1d and later.
+  if (!_goboringcrypto_EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_TAG, 16,
+                                           tag)) {
+    goto err;
+  }
+
+  // Finalise the decryption. A positive return value indicates success,
+  // anything else is a failure - the plaintext is not trustworthy.
+  ret = _goboringcrypto_EVP_DecryptFinal_ex(ctx, plaintext + len, &len);
+
+err:
+  _goboringcrypto_EVP_CIPHER_CTX_free(ctx);
+
+  if (ret > 0) {
+    // Success
+    *plaintext_len += len;
+    return ret;
+  } else {
+    // Verify failed
+    return 0;
+  }
+}
diff --git a/v2/openssl/openssl_port_ctr128.c b/v2/openssl/openssl_port_ctr128.c
new file mode 100644
index 00000000..df4ebe32
--- /dev/null
+++ b/v2/openssl/openssl_port_ctr128.c
@@ -0,0 +1,13 @@
+// +build linux
+// +build !android
+// +build !no_openssl
+// +build !cmd_go_bootstrap
+// +build !msan
+
+#include "goopenssl.h"
+
+void _goboringcrypto_EVP_AES_ctr128_enc(EVP_CIPHER_CTX *ctx, const uint8_t *in,
+                                        uint8_t *out, size_t in_len) {
+  int len;
+  _goboringcrypto_EVP_EncryptUpdate(ctx, out, &len, in, in_len);
+}
diff --git a/v2/openssl/openssl_port_evp_md5_sha1.c b/v2/openssl/openssl_port_evp_md5_sha1.c
new file mode 100644
index 00000000..2eedd5b8
--- /dev/null
+++ b/v2/openssl/openssl_port_evp_md5_sha1.c
@@ -0,0 +1,90 @@
+// This file contains a backport of the EVP_md5_sha1 method.
+// +build linux
+// +build !android
+// +build !no_openssl
+// +build !cmd_go_bootstrap
+// +build !msan
+
+// The following is a partial backport of crypto/evp/m_md5_sha1.c,
+// commit cbc8a839959418d8a2c2e3ec6bdf394852c9501e on the
+// OpenSSL_1_1_0-stable branch.  The ctrl function has been removed.
+
+#include "goopenssl.h"
+
+#if OPENSSL_VERSION_NUMBER < 0x10100000L
+// New in OpenSSL 1.1.
+static inline void *
+_goboringcrypto_internal_EVP_MD_CTX_md_data(EVP_MD_CTX *ctx) {
+  return ctx->md_data;
+}
+
+/*
+ * Copyright 2015-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#if !defined(OPENSSL_NO_MD5)
+
+#include <openssl/evp.h>
+#include <openssl/md5.h>
+#include <openssl/objects.h>
+#include <openssl/rsa.h>
+#include <openssl/sha.h>
+#include <openssl/x509.h>
+
+struct md5_sha1_ctx {
+  MD5_CTX md5;
+  SHA_CTX sha1;
+};
+
+static int _goboringcrypto_internal_init(EVP_MD_CTX *ctx) {
+  struct md5_sha1_ctx *mctx = _goboringcrypto_internal_EVP_MD_CTX_md_data(ctx);
+  if (!_goboringcrypto_internal_MD5_Init(&mctx->md5))
+    return 0;
+  return _goboringcrypto_SHA1_Init(&mctx->sha1);
+}
+
+static int _goboringcrypto_internal_update(EVP_MD_CTX *ctx, const void *data,
+                                           size_t count) {
+  struct md5_sha1_ctx *mctx = _goboringcrypto_internal_EVP_MD_CTX_md_data(ctx);
+  if (!_goboringcrypto_internal_MD5_Update(&mctx->md5, data, count))
+    return 0;
+  return _goboringcrypto_SHA1_Update(&mctx->sha1, data, count);
+}
+
+static int _goboringcrypto_internal_final(EVP_MD_CTX *ctx, unsigned char *md) {
+  struct md5_sha1_ctx *mctx = _goboringcrypto_internal_EVP_MD_CTX_md_data(ctx);
+  if (!_goboringcrypto_internal_MD5_Final(md, &mctx->md5))
+    return 0;
+  return _goboringcrypto_SHA1_Final(md + MD5_DIGEST_LENGTH, &mctx->sha1);
+}
+
+// Change: Removed:
+// static int ctrl(EVP_MD_CTX *ctx, int cmd, int mslen, void *ms)
+
+static const EVP_MD md5_sha1_md = {
+    NID_md5_sha1,
+    NID_md5_sha1,
+    MD5_DIGEST_LENGTH + SHA_DIGEST_LENGTH,
+    0,
+    _goboringcrypto_internal_init,
+    _goboringcrypto_internal_update,
+    _goboringcrypto_internal_final,
+    NULL,
+    NULL,
+    EVP_PKEY_NULL_method, // Change: inserted
+    MD5_CBLOCK,
+    sizeof(EVP_MD *) + sizeof(struct md5_sha1_ctx),
+    NULL, // Change: was ctrl
+};
+
+// Change: Apply name mangling.
+const GO_EVP_MD *_goboringcrypto_backport_EVP_md5_sha1(void) {
+  return &md5_sha1_md;
+}
+#endif
+#endif
diff --git a/v2/openssl/openssl_port_hmac.c b/v2/openssl/openssl_port_hmac.c
new file mode 100644
index 00000000..362d9e53
--- /dev/null
+++ b/v2/openssl/openssl_port_hmac.c
@@ -0,0 +1,16 @@
+// This file contains HMAC portability wrappers.
+// +build linux
+// +build !android
+// +build !no_openssl
+// +build !cmd_go_bootstrap
+// +build !msan
+
+#include "goopenssl.h"
+
+// Not in OpenSSL 1.1.  However, HMAC_CTX_copy expects an initialized
+// target in OpenSSL 1.1.
+int _goboringcrypto_HMAC_CTX_copy_ex(GO_HMAC_CTX *dest,
+                                     const GO_HMAC_CTX *src) {
+  // HMAC_CTX_copy lacks the const qualifier for the second parameter.
+  return _goboringcrypto_HMAC_CTX_copy(dest, (GO_HMAC_CTX *)src);
+}
diff --git a/v2/openssl/openssl_port_rsa.c b/v2/openssl/openssl_port_rsa.c
new file mode 100644
index 00000000..28241470
--- /dev/null
+++ b/v2/openssl/openssl_port_rsa.c
@@ -0,0 +1,219 @@
+// This file contains RSA portability wrappers.
+// +build linux
+// +build !android
+// +build !no_openssl
+// +build !cmd_go_bootstrap
+// +build !msan
+
+#include "goopenssl.h"
+
+// Only in BoringSSL.
+int _goboringcrypto_RSA_generate_key_fips(GO_RSA *rsa, int size,
+                                          GO_BN_GENCB *cb) {
+  // BoringSSL's RSA_generate_key_fips hard-codes e to 65537.
+  BIGNUM *e = _goboringcrypto_BN_new();
+  if (e == NULL)
+    return 0;
+  int ret = _goboringcrypto_BN_set_word(e, RSA_F4) &&
+            _goboringcrypto_RSA_generate_key_ex(rsa, size, e, cb);
+  _goboringcrypto_BN_free(e);
+  return ret;
+}
+
+int _goboringcrypto_RSA_digest_and_sign_pss_mgf1(
+    GO_RSA *rsa, unsigned int *out_len, uint8_t *out, size_t max_out,
+    const uint8_t *in, size_t in_len, EVP_MD *md, const EVP_MD *mgf1_md,
+    int salt_len) {
+  EVP_PKEY_CTX *ctx;
+  unsigned int siglen;
+
+  int ret = 0;
+  EVP_PKEY *key = _goboringcrypto_EVP_PKEY_new();
+  if (!key) {
+    goto err;
+  }
+  if (!_goboringcrypto_EVP_PKEY_set1_RSA(key, rsa))
+    goto err;
+  ctx = _goboringcrypto_EVP_PKEY_CTX_new(key, NULL /* no engine */);
+  if (!ctx)
+    goto err;
+
+  EVP_MD_CTX *mdctx = NULL;
+  if (!(mdctx = _goboringcrypto_EVP_MD_CTX_create()))
+    goto err;
+
+  if (1 != _goboringcrypto_EVP_DigestSignInit(mdctx, &ctx, md, NULL, key))
+    goto err;
+
+  if (_goboringcrypto_EVP_PKEY_sign_init(ctx) <= 0)
+    goto err;
+  if (_goboringcrypto_EVP_PKEY_CTX_set_signature_md(ctx, md) <= 0)
+    goto err;
+  if (_goboringcrypto_EVP_PKEY_CTX_set_rsa_padding(ctx,
+                                                   RSA_PKCS1_PSS_PADDING) <= 0)
+    goto err;
+  if (_goboringcrypto_EVP_PKEY_CTX_set_rsa_pss_saltlen(ctx, salt_len) <= 0)
+    goto err;
+  if (mgf1_md)
+    if (_goboringcrypto_EVP_PKEY_CTX_set_rsa_mgf1_md(ctx, mgf1_md) <= 0)
+      goto err;
+  if (1 != _goboringcrypto_EVP_DigestUpdate(mdctx, in, in_len))
+    goto err;
+
+  /* Obtain the signature length */
+  if (1 != _goboringcrypto_EVP_DigestSignFinal(mdctx, NULL, out_len))
+    goto err;
+  /* Obtain the signature */
+  if (1 != _goboringcrypto_EVP_DigestSignFinal(mdctx, out, out_len))
+    goto err;
+
+  ret = 1;
+
+err:
+  if (mdctx)
+    _goboringcrypto_EVP_MD_CTX_free(mdctx);
+  if (ctx)
+    _goboringcrypto_EVP_PKEY_CTX_free(ctx);
+  if (key)
+    _goboringcrypto_EVP_PKEY_free(key);
+
+  return ret;
+}
+
+int _goboringcrypto_RSA_sign_pss_mgf1(GO_RSA *rsa, unsigned int *out_len,
+                                      uint8_t *out, unsigned int max_out,
+                                      const uint8_t *in, unsigned int in_len,
+                                      EVP_MD *md, const EVP_MD *mgf1_md,
+                                      int salt_len) {
+  EVP_PKEY_CTX *ctx;
+  EVP_PKEY *pkey;
+  size_t siglen;
+
+  int ret = 0;
+  pkey = _goboringcrypto_EVP_PKEY_new();
+  if (!pkey)
+    goto err;
+
+  if (_goboringcrypto_EVP_PKEY_set1_RSA(pkey, rsa) <= 0)
+    goto err;
+
+  ctx = _goboringcrypto_EVP_PKEY_CTX_new(pkey, NULL /* no engine */);
+  if (!ctx)
+    goto err;
+
+  if (_goboringcrypto_EVP_PKEY_sign_init(ctx) <= 0)
+    goto err;
+  if (_goboringcrypto_EVP_PKEY_CTX_set_signature_md(ctx, md) <= 0)
+    goto err;
+  if (_goboringcrypto_EVP_PKEY_CTX_set_rsa_padding(ctx,
+                                                   RSA_PKCS1_PSS_PADDING) <= 0)
+    goto err;
+  if (_goboringcrypto_EVP_PKEY_CTX_set_rsa_pss_saltlen(ctx, salt_len) <= 0)
+    goto err;
+  if (mgf1_md)
+    if (_goboringcrypto_EVP_PKEY_CTX_set_rsa_mgf1_md(ctx, mgf1_md) <= 0)
+      goto err;
+  /* Determine buffer length */
+  if (_goboringcrypto_EVP_PKEY_sign(ctx, NULL, &siglen, in, in_len) <= 0)
+    goto err;
+
+  if (max_out < siglen)
+    goto err;
+
+  if (_goboringcrypto_EVP_PKEY_sign(ctx, out, &siglen, in, in_len) <= 0)
+    goto err;
+
+  *out_len = siglen;
+  ret = 1;
+
+err:
+  if (ctx)
+    _goboringcrypto_EVP_PKEY_CTX_free(ctx);
+  if (pkey)
+    _goboringcrypto_EVP_PKEY_free(pkey);
+
+  return ret;
+}
+
+int _goboringcrypto_RSA_verify_pss_mgf1(RSA *rsa, const uint8_t *msg,
+                                        unsigned int msg_len, EVP_MD *md,
+                                        const EVP_MD *mgf1_md, int salt_len,
+                                        const uint8_t *sig,
+                                        unsigned int sig_len) {
+  EVP_PKEY_CTX *ctx;
+  EVP_PKEY *pkey;
+
+  int ret = 0;
+
+  pkey = _goboringcrypto_EVP_PKEY_new();
+  if (!pkey)
+    goto err;
+
+  if (_goboringcrypto_EVP_PKEY_set1_RSA(pkey, rsa) <= 0)
+    goto err;
+
+  ctx = _goboringcrypto_EVP_PKEY_CTX_new(pkey, NULL /* no engine */);
+  if (!ctx)
+    goto err;
+
+  if (_goboringcrypto_EVP_PKEY_verify_init(ctx) <= 0)
+    goto err;
+  if (_goboringcrypto_EVP_PKEY_CTX_set_rsa_padding(ctx,
+                                                   RSA_PKCS1_PSS_PADDING) <= 0)
+    goto err;
+  if (_goboringcrypto_EVP_PKEY_CTX_set_rsa_pss_saltlen(ctx, salt_len) <= 0)
+    goto err;
+  if (_goboringcrypto_EVP_PKEY_CTX_set_signature_md(ctx, md) <= 0)
+    goto err;
+  if (mgf1_md)
+    if (_goboringcrypto_EVP_PKEY_CTX_set_rsa_mgf1_md(ctx, mgf1_md) <= 0)
+      goto err;
+  if (_goboringcrypto_EVP_PKEY_verify(ctx, sig, sig_len, msg, msg_len) <= 0)
+    goto err;
+
+  ret = 1;
+
+err:
+  if (ctx)
+    _goboringcrypto_EVP_PKEY_CTX_free(ctx);
+  if (pkey)
+    _goboringcrypto_EVP_PKEY_free(pkey);
+
+  return ret;
+}
+
+int _goboringcrypto_EVP_RSA_sign(EVP_MD *md, const uint8_t *msg,
+                                 unsigned int msgLen, uint8_t *sig,
+                                 unsigned int *slen, RSA *rsa) {
+  int result;
+  EVP_PKEY *key = _goboringcrypto_EVP_PKEY_new();
+  if (!key) {
+    return 0;
+  }
+  if (!_goboringcrypto_EVP_PKEY_set1_RSA(key, rsa)) {
+    result = 0;
+    goto err;
+  }
+  result = _goboringcrypto_EVP_sign(md, NULL, msg, msgLen, sig, slen, key);
+err:
+  _goboringcrypto_EVP_PKEY_free(key);
+  return result;
+}
+
+int _goboringcrypto_EVP_RSA_verify(EVP_MD *md, const uint8_t *msg,
+                                   unsigned int msgLen, const uint8_t *sig,
+                                   unsigned int slen, GO_RSA *rsa) {
+  int result;
+  EVP_PKEY *key = _goboringcrypto_EVP_PKEY_new();
+  if (!key) {
+    return 0;
+  }
+  if (!_goboringcrypto_EVP_PKEY_set1_RSA(key, rsa)) {
+    result = 0;
+    goto err;
+  }
+  result = _goboringcrypto_EVP_verify(md, NULL, msg, msgLen, sig, slen, key);
+err:
+  _goboringcrypto_EVP_PKEY_free(key);
+  return result;
+}
\ No newline at end of file
diff --git a/v2/openssl/openssl_stub_rand.c b/v2/openssl/openssl_stub_rand.c
new file mode 100644
index 00000000..22bd865a
--- /dev/null
+++ b/v2/openssl/openssl_stub_rand.c
@@ -0,0 +1,45 @@
+// +build linux
+// +build !android
+// +build !no_openssl
+// +build !cmd_go_bootstrap
+// +build !msan
+
+#include "goopenssl.h"
+#include <openssl/rand.h>
+
+static RAND_METHOD fake_rand;
+static const RAND_METHOD *old_rand;
+
+int _goboringcrypto_stub_openssl_rand(void) {
+  /* save old rand method */
+  if ((old_rand = _goboringcrypto_RAND_get_rand_method()) == NULL)
+    return 0;
+
+  fake_rand.seed = old_rand->seed;
+  fake_rand.cleanup = old_rand->cleanup;
+  fake_rand.add = old_rand->add;
+  fake_rand.status = old_rand->status;
+  /* use own random function */
+  fake_rand.bytes = fbytes;
+  fake_rand.pseudorand = old_rand->bytes;
+  /* set new RAND_METHOD */
+  if (!_goboringcrypto_RAND_set_rand_method(&fake_rand))
+    return 0;
+  return 1;
+}
+
+int _goboringcrypto_restore_openssl_rand(void) {
+  if (!_goboringcrypto_RAND_set_rand_method(old_rand))
+    return 0;
+  else
+    return 1;
+}
+
+int fbytes(unsigned char *buf, int num) {
+  // return old_rand->bytes(buf, num);
+  int i;
+  for (i = 0; i < num; i++) {
+    buf[i] = 1;
+  }
+  return 1;
+}
diff --git a/v2/openssl/rand.go b/v2/openssl/rand.go
new file mode 100644
index 00000000..b3668b88
--- /dev/null
+++ b/v2/openssl/rand.go
@@ -0,0 +1,25 @@
+// Copyright 2017 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+//go:build linux && !android && !cmd_go_bootstrap && !msan && !no_openssl
+// +build linux,!android,!cmd_go_bootstrap,!msan,!no_openssl
+
+package openssl
+
+// #include "goopenssl.h"
+import "C"
+import "unsafe"
+
+type randReader int
+
+func (randReader) Read(b []byte) (int, error) {
+	// Note: RAND_bytes should never fail; the return value exists only for historical reasons.
+	// We check it even so.
+	if len(b) > 0 && C._goboringcrypto_RAND_bytes((*C.uint8_t)(unsafe.Pointer(&b[0])), C.size_t(len(b))) == 0 {
+		return 0, NewOpenSSLError("RAND_bytes")
+	}
+	return len(b), nil
+}
+
+const RandReader = randReader(0)
diff --git a/v2/openssl/rsa.go b/v2/openssl/rsa.go
new file mode 100644
index 00000000..915c8408
--- /dev/null
+++ b/v2/openssl/rsa.go
@@ -0,0 +1,411 @@
+// Copyright 2017 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+//go:build linux && !android && !cmd_go_bootstrap && !msan && !no_openssl
+// +build linux,!android,!cmd_go_bootstrap,!msan,!no_openssl
+
+package openssl
+
+// #include "goopenssl.h"
+import "C"
+import (
+	"crypto"
+	"errors"
+	"hash"
+	"runtime"
+	"strconv"
+	"unsafe"
+)
+
+func GenerateKeyRSA(bits int) (N, E, D, P, Q, Dp, Dq, Qinv BigInt, err error) {
+	bad := func(e error) (N, E, D, P, Q, Dp, Dq, Qinv BigInt, err error) {
+		return nil, nil, nil, nil, nil, nil, nil, nil, e
+	}
+
+	key := C._goboringcrypto_RSA_new()
+	if key == nil {
+		return bad(NewOpenSSLError("RSA_new failed"))
+	}
+	defer C._goboringcrypto_RSA_free(key)
+
+	if C._goboringcrypto_RSA_generate_key_fips(key, C.int(bits), nil) == 0 {
+		return bad(NewOpenSSLError("RSA_generate_key_fips failed"))
+	}
+
+	var n, e, d, p, q, dp, dq, qinv *C.GO_BIGNUM
+	C._goboringcrypto_RSA_get0_key(key, &n, &e, &d)
+	C._goboringcrypto_RSA_get0_factors(key, &p, &q)
+	C._goboringcrypto_RSA_get0_crt_params(key, &dp, &dq, &qinv)
+	return bnToBig(n), bnToBig(e), bnToBig(d), bnToBig(p), bnToBig(q), bnToBig(dp), bnToBig(dq), bnToBig(qinv), nil
+}
+
+type PublicKeyRSA struct {
+	// _key MUST NOT be accessed directly. Instead, use the withKey method.
+	_key *C.GO_RSA
+}
+
+func NewPublicKeyRSA(N, E BigInt) (*PublicKeyRSA, error) {
+	key := C._goboringcrypto_RSA_new()
+	if key == nil {
+		return nil, NewOpenSSLError("RSA_new failed")
+	}
+	var n, e *C.GO_BIGNUM
+	n = bigToBN(N)
+	e = bigToBN(E)
+	C._goboringcrypto_RSA_set0_key(key, n, e, nil)
+	k := &PublicKeyRSA{_key: key}
+	runtime.SetFinalizer(k, (*PublicKeyRSA).finalize)
+	return k, nil
+}
+
+func (k *PublicKeyRSA) finalize() {
+	C._goboringcrypto_RSA_free(k._key)
+}
+
+func (k *PublicKeyRSA) withKey(f func(*C.GO_RSA) C.int) C.int {
+	// Because of the finalizer, any time _key is passed to cgo, that call must
+	// be followed by a call to runtime.KeepAlive, to make sure k is not
+	// collected (and finalized) before the cgo call returns.
+	defer runtime.KeepAlive(k)
+	return f(k._key)
+}
+
+type PrivateKeyRSA struct {
+	// _key MUST NOT be accessed directly. Instead, use the withKey method.
+	_key *C.GO_RSA
+}
+
+func NewPrivateKeyRSA(N, E, D, P, Q, Dp, Dq, Qinv BigInt) (*PrivateKeyRSA, error) {
+	key := C._goboringcrypto_RSA_new()
+	if key == nil {
+		return nil, NewOpenSSLError("RSA_new failed")
+	}
+	var n, e, d, p, q, dp, dq, qinv *C.GO_BIGNUM
+	n = bigToBN(N)
+	e = bigToBN(E)
+	d = bigToBN(D)
+	C._goboringcrypto_RSA_set0_key(key, n, e, d)
+	if P != nil && Q != nil {
+		p = bigToBN(P)
+		q = bigToBN(Q)
+		C._goboringcrypto_RSA_set0_factors(key, p, q)
+	}
+	if Dp != nil && Dq != nil && Qinv != nil {
+		dp = bigToBN(Dp)
+		dq = bigToBN(Dq)
+		qinv = bigToBN(Qinv)
+		C._goboringcrypto_RSA_set0_crt_params(key, dp, dq, qinv)
+	}
+	k := &PrivateKeyRSA{_key: key}
+	runtime.SetFinalizer(k, (*PrivateKeyRSA).finalize)
+	return k, nil
+}
+
+func (k *PrivateKeyRSA) finalize() {
+	C._goboringcrypto_RSA_free(k._key)
+}
+
+func (k *PrivateKeyRSA) withKey(f func(*C.GO_RSA) C.int) C.int {
+	// Because of the finalizer, any time _key is passed to cgo, that call must
+	// be followed by a call to runtime.KeepAlive, to make sure k is not
+	// collected (and finalized) before the cgo call returns.
+	defer runtime.KeepAlive(k)
+	return f(k._key)
+}
+
+func setupRSA(withKey func(func(*C.GO_RSA) C.int) C.int,
+	padding C.int, h hash.Hash, label []byte, saltLen int, ch crypto.Hash,
+	init func(*C.GO_EVP_PKEY_CTX) C.int) (pkey *C.GO_EVP_PKEY, ctx *C.GO_EVP_PKEY_CTX, err error) {
+	defer func() {
+		if err != nil {
+			if pkey != nil {
+				C._goboringcrypto_EVP_PKEY_free(pkey)
+				pkey = nil
+			}
+			if ctx != nil {
+				C._goboringcrypto_EVP_PKEY_CTX_free(ctx)
+				ctx = nil
+			}
+		}
+	}()
+
+	pkey = C._goboringcrypto_EVP_PKEY_new()
+	if pkey == nil {
+		return nil, nil, NewOpenSSLError("EVP_PKEY_new failed")
+	}
+	if withKey(func(key *C.GO_RSA) C.int {
+		return C._goboringcrypto_EVP_PKEY_set1_RSA(pkey, key)
+	}) == 0 {
+		return nil, nil, fail("EVP_PKEY_set1_RSA")
+	}
+	ctx = C._goboringcrypto_EVP_PKEY_CTX_new(pkey, nil)
+	if ctx == nil {
+		return nil, nil, NewOpenSSLError("EVP_PKEY_CTX_new failed")
+	}
+	if init(ctx) == 0 {
+		return nil, nil, NewOpenSSLError("EVP_PKEY_operation_init failed")
+	}
+	if C._goboringcrypto_EVP_PKEY_CTX_set_rsa_padding(ctx, padding) == 0 {
+		return nil, nil, NewOpenSSLError("EVP_PKEY_CTX_set_rsa_padding failed")
+	}
+	if padding == C.GO_RSA_PKCS1_OAEP_PADDING {
+		md := hashToMD(h)
+		if md == nil {
+			return nil, nil, errors.New("crypto/rsa: unsupported hash function")
+		}
+		if C._goboringcrypto_EVP_PKEY_CTX_set_rsa_oaep_md(ctx, md) == 0 {
+			return nil, nil, NewOpenSSLError("EVP_PKEY_set_rsa_oaep_md failed")
+		}
+		// ctx takes ownership of label, so malloc a copy for BoringCrypto to free.
+		var clabel *C.uint8_t
+		clabel = nil
+		// OpenSSL 1.1.1 does not take ownership of the label if the length is zero.
+		// Depending on the malloc implementation, if clabel is allocated with malloc(0),
+		// metadata for the size-zero allocation is never cleaned up, which is a memory leak.
+		// As such, we must only allocate clabel if the label is of non zero length.
+		if (len(label) > 0) || (openSSLVersion() > OPENSSL_VERSION_3_0_0) {
+			clabel = (*C.uint8_t)(C.malloc(C.size_t(len(label))))
+			if clabel == nil {
+				return nil, nil, fail("OPENSSL_malloc")
+			}
+			copy((*[1 << 30]byte)(unsafe.Pointer(clabel))[:len(label)], label)
+		}
+		if C._goboringcrypto_EVP_PKEY_CTX_set0_rsa_oaep_label(ctx, clabel, C.int(len(label))) != 1 {
+			if clabel != nil {
+				C.free(unsafe.Pointer(clabel))
+			}
+			return nil, nil, NewOpenSSLError("EVP_PKEY_CTX_set0_rsa_oaep_label failed")
+		}
+	}
+	if padding == C.GO_RSA_PKCS1_PSS_PADDING {
+		if saltLen != 0 {
+			if C._goboringcrypto_EVP_PKEY_CTX_set_rsa_pss_saltlen(ctx, C.int(saltLen)) == 0 {
+				return nil, nil, NewOpenSSLError("EVP_PKEY_set_rsa_pss_saltlen failed")
+			}
+		}
+		md := cryptoHashToMD(ch)
+		if md == nil {
+			return nil, nil, errors.New("crypto/rsa: unsupported hash function")
+		}
+		if C._goboringcrypto_EVP_PKEY_CTX_set_rsa_mgf1_md(ctx, md) == 0 {
+			return nil, nil, NewOpenSSLError("EVP_PKEY_set_rsa_mgf1_md failed")
+		}
+	}
+
+	return pkey, ctx, nil
+}
+
+func cryptRSA(withKey func(func(*C.GO_RSA) C.int) C.int,
+	padding C.int, h hash.Hash, label []byte, saltLen int, ch crypto.Hash,
+	init func(*C.GO_EVP_PKEY_CTX) C.int,
+	crypt func(*C.GO_EVP_PKEY_CTX, *C.uint8_t, *C.uint, *C.uint8_t, C.uint) C.int,
+	in []byte) ([]byte, error) {
+
+	pkey, ctx, err := setupRSA(withKey, padding, h, label, saltLen, ch, init)
+	if err != nil {
+		return nil, err
+	}
+	defer C._goboringcrypto_EVP_PKEY_free(pkey)
+	defer C._goboringcrypto_EVP_PKEY_CTX_free(ctx)
+
+	var outLen C.uint
+	if crypt(ctx, nil, &outLen, base(in), C.uint(len(in))) == 0 {
+		return nil, NewOpenSSLError("EVP_PKEY_decrypt/encrypt failed")
+	}
+	out := make([]byte, outLen)
+	if crypt(ctx, base(out), &outLen, base(in), C.uint(len(in))) <= 0 {
+		return nil, NewOpenSSLError("EVP_PKEY_decrypt/encrypt failed")
+	}
+	return out[:outLen], nil
+}
+
+func DecryptRSAOAEP(h hash.Hash, priv *PrivateKeyRSA, ciphertext, label []byte) ([]byte, error) {
+	return cryptRSA(priv.withKey, C.GO_RSA_PKCS1_OAEP_PADDING, h, label, 0, 0, decryptInit, decrypt, ciphertext)
+}
+
+func EncryptRSAOAEP(h hash.Hash, pub *PublicKeyRSA, msg, label []byte) ([]byte, error) {
+	return cryptRSA(pub.withKey, C.GO_RSA_PKCS1_OAEP_PADDING, h, label, 0, 0, encryptInit, encrypt, msg)
+}
+
+func DecryptRSAPKCS1(priv *PrivateKeyRSA, ciphertext []byte) ([]byte, error) {
+	return cryptRSA(priv.withKey, C.GO_RSA_PKCS1_PADDING, nil, nil, 0, 0, decryptInit, decrypt, ciphertext)
+}
+
+func EncryptRSAPKCS1(pub *PublicKeyRSA, msg []byte) ([]byte, error) {
+	return cryptRSA(pub.withKey, C.GO_RSA_PKCS1_PADDING, nil, nil, 0, 0, encryptInit, encrypt, msg)
+}
+
+func DecryptRSANoPadding(priv *PrivateKeyRSA, ciphertext []byte) ([]byte, error) {
+	return cryptRSA(priv.withKey, C.GO_RSA_NO_PADDING, nil, nil, 0, 0, decryptInit, decrypt, ciphertext)
+}
+
+func EncryptRSANoPadding(pub *PublicKeyRSA, msg []byte) ([]byte, error) {
+	return cryptRSA(pub.withKey, C.GO_RSA_NO_PADDING, nil, nil, 0, 0, encryptInit, encrypt, msg)
+}
+
+// These dumb wrappers work around the fact that cgo functions cannot be used as values directly.
+
+func decryptInit(ctx *C.GO_EVP_PKEY_CTX) C.int {
+	return C._goboringcrypto_EVP_PKEY_decrypt_init(ctx)
+}
+
+func decrypt(ctx *C.GO_EVP_PKEY_CTX, out *C.uint8_t, outLen *C.uint, in *C.uint8_t, inLen C.uint) C.int {
+	return C._goboringcrypto_EVP_PKEY_decrypt(ctx, out, outLen, in, inLen)
+}
+
+func encryptInit(ctx *C.GO_EVP_PKEY_CTX) C.int {
+	return C._goboringcrypto_EVP_PKEY_encrypt_init(ctx)
+}
+
+func encrypt(ctx *C.GO_EVP_PKEY_CTX, out *C.uint8_t, outLen *C.uint, in *C.uint8_t, inLen C.uint) C.int {
+	return C._goboringcrypto_EVP_PKEY_encrypt(ctx, out, outLen, in, inLen)
+}
+
+func SignRSAPSS(priv *PrivateKeyRSA, h crypto.Hash, hashed []byte, saltLen int) ([]byte, error) {
+	md := cryptoHashToMD(h)
+	if md == nil {
+		return nil, errors.New("crypto/rsa: unsupported hash function")
+	}
+	if saltLen == 0 {
+		saltLen = -1
+	}
+	var out []byte
+	var outLen C.uint
+	if priv.withKey(func(key *C.GO_RSA) C.int {
+		out = make([]byte, C._goboringcrypto_RSA_size(key))
+		return C._goboringcrypto_RSA_sign_pss_mgf1(key, &outLen, base(out), C.uint(len(out)),
+			base(hashed), C.uint(len(hashed)), md, nil, C.int(saltLen))
+	}) == 0 {
+		return nil, fail("RSA_sign_pss_mgf1")
+	}
+
+	return out[:outLen], nil
+}
+
+func VerifyRSAPSS(pub *PublicKeyRSA, h crypto.Hash, hashed, sig []byte, saltLen int) error {
+	md := cryptoHashToMD(h)
+	if md == nil {
+		return errors.New("crypto/rsa: unsupported hash function")
+	}
+	if saltLen == 0 {
+		saltLen = -2 // auto-recover
+	}
+	if pub.withKey(func(key *C.GO_RSA) C.int {
+		return C._goboringcrypto_RSA_verify_pss_mgf1(key, base(hashed), C.uint(len(hashed)),
+			md, nil, C.int(saltLen), base(sig), C.uint(len(sig)))
+	}) == 0 {
+		return fail("RSA_verify_pss_mgf1")
+	}
+	return nil
+}
+
+func SignRSAPKCS1v15(priv *PrivateKeyRSA, h crypto.Hash, msg []byte, msgIsHashed bool) ([]byte, error) {
+	if h == 0 && ExecutingTest() {
+		return signRSAPKCS1v15Raw(priv, msg, C._goboringcrypto_EVP_md_null())
+	}
+
+	md := cryptoHashToMD(h)
+	if md == nil {
+		return nil, errors.New("crypto/rsa: unsupported hash function: " + strconv.Itoa(int(h)))
+	}
+
+	if msgIsHashed {
+		var out []byte
+		var outLen C.uint
+		PanicIfStrictFIPS("You must provide a raw unhashed message for PKCS1v15 signing and use HashSignPKCS1v15 instead of SignPKCS1v15")
+		nid := C._goboringcrypto_EVP_MD_type(md)
+		if priv.withKey(func(key *C.GO_RSA) C.int {
+			out = make([]byte, C._goboringcrypto_RSA_size(key))
+			return C._goboringcrypto_RSA_sign(nid, base(msg), C.uint(len(msg)), base(out), &outLen, key)
+		}) == 0 {
+			return nil, NewOpenSSLError("RSA_sign")
+		}
+		runtime.KeepAlive(priv)
+		return out[:outLen], nil
+	}
+
+	var out []byte
+	var outLen C.uint
+
+	if priv.withKey(func(key *C.GO_RSA) C.int {
+		return C._goboringcrypto_EVP_RSA_sign(md, base(msg), C.uint(len(msg)), base(out), &outLen, key)
+	}) == 0 {
+		return nil, NewOpenSSLError("RSA_sign")
+	}
+	return out[:outLen], nil
+}
+
+func signRSAPKCS1v15Raw(priv *PrivateKeyRSA, msg []byte, md *C.GO_EVP_MD) ([]byte, error) {
+	var out []byte
+	var outLen C.size_t
+	PanicIfStrictFIPS("You must provide a raw unhashed message for PKCS1v15 signing and use HashSignPKCS1v15 instead of SignPKCS1v15")
+
+	if priv.withKey(func(key *C.GO_RSA) C.int {
+		out = make([]byte, C._goboringcrypto_RSA_size(key))
+		outLen = C.size_t(len(out))
+		return C._goboringcrypto_EVP_sign_raw(md, nil, base(msg),
+			C.size_t(len(msg)), base(out), &outLen, key)
+	}) == 0 {
+		return nil, NewOpenSSLError("RSA_sign")
+	}
+	runtime.KeepAlive(priv)
+	return out[:outLen], nil
+}
+
+func VerifyRSAPKCS1v15(pub *PublicKeyRSA, h crypto.Hash, msg, sig []byte, msgIsHashed bool) error {
+	if h == 0 && ExecutingTest() {
+		return verifyRSAPKCS1v15Raw(pub, msg, sig)
+	}
+
+	md := cryptoHashToMD(h)
+	if md == nil {
+		return errors.New("crypto/rsa: unsupported hash function")
+	}
+
+	if pub.withKey(func(key *C.GO_RSA) C.int {
+		size := int(C._goboringcrypto_RSA_size(key))
+		if len(sig) < size {
+			return 0
+		}
+		return 1
+	}) == 0 {
+		return errors.New("crypto/rsa: verification error")
+	}
+
+	if msgIsHashed {
+		PanicIfStrictFIPS("You must provide a raw unhashed message for PKCS1v15 verification and use HashVerifyPKCS1v15 instead of VerifyPKCS1v15")
+		nid := C._goboringcrypto_EVP_MD_type(md)
+		if pub.withKey(func(key *C.GO_RSA) C.int {
+			return C._goboringcrypto_RSA_verify(nid, base(msg), C.uint(len(msg)), base(sig), C.uint(len(sig)), key)
+		}) == 0 {
+			return NewOpenSSLError("RSA_verify failed")
+		}
+		return nil
+	}
+
+	if pub.withKey(func(key *C.GO_RSA) C.int {
+		return C._goboringcrypto_EVP_RSA_verify(md, base(msg), C.uint(len(msg)), base(sig), C.uint(len(sig)), key)
+	}) == 0 {
+		return NewOpenSSLError("RSA_verify failed")
+	}
+	return nil
+}
+
+func verifyRSAPKCS1v15Raw(pub *PublicKeyRSA, msg, sig []byte) error {
+	if pub.withKey(func(key *C.GO_RSA) C.int {
+		size := int(C._goboringcrypto_RSA_size(key))
+		if len(sig) < size {
+			return 0
+		}
+		return 1
+	}) == 0 {
+		return errors.New("crypto/rsa: verification error")
+	}
+	if pub.withKey(func(key *C.GO_RSA) C.int {
+		return C._goboringcrypto_EVP_verify_raw(base(msg), C.size_t(len(msg)), base(sig), C.uint(len(sig)), key)
+	}) == 0 {
+		return NewOpenSSLError("RSA_verify failed")
+	}
+	return nil
+}
diff --git a/v2/openssl/sha.go b/v2/openssl/sha.go
new file mode 100644
index 00000000..0b55cedc
--- /dev/null
+++ b/v2/openssl/sha.go
@@ -0,0 +1,564 @@
+// Copyright 2017 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+//go:build linux && !android && !cmd_go_bootstrap && !msan && !no_openssl
+// +build linux,!android,!cmd_go_bootstrap,!msan,!no_openssl
+
+package openssl
+
+/*
+#include "goopenssl.h"
+
+int
+_goboringcrypto_gosha1(void *p, size_t n, void *out)
+{
+	GO_SHA_CTX ctx;
+	_goboringcrypto_SHA1_Init(&ctx);
+	return _goboringcrypto_SHA1_Update(&ctx, p, n) &&
+		_goboringcrypto_SHA1_Final(out, &ctx);
+}
+int
+_goboringcrypto_gosha224(void *p, size_t n, void *out)
+{
+	GO_SHA256_CTX ctx;
+	_goboringcrypto_SHA224_Init(&ctx);
+	return _goboringcrypto_SHA224_Update(&ctx, p, n) &&
+		_goboringcrypto_SHA224_Final(out, &ctx);
+}
+int
+_goboringcrypto_gosha256(void *p, size_t n, void *out)
+{
+	GO_SHA256_CTX ctx;
+	_goboringcrypto_SHA256_Init(&ctx);
+	return _goboringcrypto_SHA256_Update(&ctx, p, n) &&
+		_goboringcrypto_SHA256_Final(out, &ctx);
+}
+int
+_goboringcrypto_gosha384(void *p, size_t n, void *out)
+{
+	GO_SHA512_CTX ctx;
+	_goboringcrypto_SHA384_Init(&ctx);
+	return _goboringcrypto_SHA384_Update(&ctx, p, n) &&
+		_goboringcrypto_SHA384_Final(out, &ctx);
+}
+int
+_goboringcrypto_gosha512(void *p, size_t n, void *out)
+{
+	GO_SHA512_CTX ctx;
+	_goboringcrypto_SHA512_Init(&ctx);
+	return _goboringcrypto_SHA512_Update(&ctx, p, n) &&
+		_goboringcrypto_SHA512_Final(out, &ctx);
+}
+*/
+import "C"
+import (
+	"errors"
+	"hash"
+	"unsafe"
+)
+
+// NOTE: The cgo calls in this file are arranged to avoid marking the parameters as escaping.
+// To do that, we call noescape (including via addr).
+// We must also make sure that the data pointer arguments have the form unsafe.Pointer(&...)
+// so that cgo does not annotate them with cgoCheckPointer calls. If it did that, it might look
+// beyond the byte slice and find Go pointers in unprocessed parts of a larger allocation.
+// To do both of these simultaneously, the idiom is unsafe.Pointer(&*addr(p)),
+// where addr returns the base pointer of p, substituting a non-nil pointer for nil,
+// and applying a noescape along the way.
+// This is all to preserve compatibility with the allocation behavior of the non-boring implementations.
+
+func SHA1(p []byte) (sum [20]byte) {
+	if C._goboringcrypto_gosha1(unsafe.Pointer(&*addr(p)), C.size_t(len(p)), unsafe.Pointer(&*addr(sum[:]))) == 0 {
+		panic("boringcrypto: SHA1 failed")
+	}
+	return
+}
+
+func SHA224(p []byte) (sum [28]byte) {
+	if C._goboringcrypto_gosha224(unsafe.Pointer(&*addr(p)), C.size_t(len(p)), unsafe.Pointer(&*addr(sum[:]))) == 0 {
+		panic("boringcrypto: SHA224 failed")
+	}
+	return
+}
+
+func SHA256(p []byte) (sum [32]byte) {
+	if C._goboringcrypto_gosha256(unsafe.Pointer(&*addr(p)), C.size_t(len(p)), unsafe.Pointer(&*addr(sum[:]))) == 0 {
+		panic("boringcrypto: SHA256 failed")
+	}
+	return
+}
+
+func SHA384(p []byte) (sum [48]byte) {
+	if C._goboringcrypto_gosha384(unsafe.Pointer(&*addr(p)), C.size_t(len(p)), unsafe.Pointer(&*addr(sum[:]))) == 0 {
+		panic("boringcrypto: SHA384 failed")
+	}
+	return
+}
+
+func SHA512(p []byte) (sum [64]byte) {
+	if C._goboringcrypto_gosha512(unsafe.Pointer(&*addr(p)), C.size_t(len(p)), unsafe.Pointer(&*addr(sum[:]))) == 0 {
+		panic("boringcrypto: SHA512 failed")
+	}
+	return
+}
+
+// NewSHA1 returns a new SHA1 hash.
+func NewSHA1() hash.Hash {
+	h := new(sha1Hash)
+	h.Reset()
+	return h
+}
+
+type sha1Hash struct {
+	ctx C.GO_SHA_CTX
+	out [20]byte
+}
+
+type sha1Ctx struct {
+	h      [5]uint32
+	nl, nh uint32
+	x      [64]byte
+	nx     uint32
+}
+
+func (h *sha1Hash) Reset()               { C._goboringcrypto_SHA1_Init(&h.ctx) }
+func (h *sha1Hash) Size() int            { return 20 }
+func (h *sha1Hash) BlockSize() int       { return 64 }
+func (h *sha1Hash) Sum(in []byte) []byte { return append(in, h.sum()...) }
+
+func (h *sha1Hash) Write(p []byte) (int, error) {
+	if len(p) > 0 && C._goboringcrypto_SHA1_Update(&h.ctx, unsafe.Pointer(&p[0]), C.size_t(len(p))) == 0 {
+		panic("boringcrypto: SHA1_Update failed")
+	}
+	return len(p), nil
+}
+
+func (h0 *sha1Hash) sum() []byte {
+	h := *h0 // make copy so future Write+Sum is valid
+	if C._goboringcrypto_SHA1_Final((*C.uint8_t)(unsafe.Pointer(&h.out[0])), &h.ctx) == 0 {
+		panic("boringcrypto: SHA1_Final failed")
+	}
+	return h.out[:]
+}
+
+const (
+	sha1Magic         = "sha\x01"
+	sha1MarshaledSize = len(sha1Magic) + 5*4 + 64 + 8
+)
+
+func (h *sha1Hash) MarshalBinary() ([]byte, error) {
+	d := (*sha1Ctx)(unsafe.Pointer(&h.ctx))
+	b := make([]byte, 0, sha1MarshaledSize)
+	b = append(b, sha1Magic...)
+	b = appendUint32(b, d.h[0])
+	b = appendUint32(b, d.h[1])
+	b = appendUint32(b, d.h[2])
+	b = appendUint32(b, d.h[3])
+	b = appendUint32(b, d.h[4])
+	b = append(b, d.x[:d.nx]...)
+	b = b[:len(b)+len(d.x)-int(d.nx)] // already zero
+	b = appendUint64(b, uint64(d.nl)>>3|uint64(d.nh)<<29)
+	return b, nil
+}
+
+func (h *sha1Hash) UnmarshalBinary(b []byte) error {
+	if len(b) < len(sha1Magic) || string(b[:len(sha1Magic)]) != sha1Magic {
+		return errors.New("crypto/sha1: invalid hash state identifier")
+	}
+	if len(b) != sha1MarshaledSize {
+		return errors.New("crypto/sha1: invalid hash state size")
+	}
+	d := (*sha1Ctx)(unsafe.Pointer(&h.ctx))
+	b = b[len(sha1Magic):]
+	b, d.h[0] = consumeUint32(b)
+	b, d.h[1] = consumeUint32(b)
+	b, d.h[2] = consumeUint32(b)
+	b, d.h[3] = consumeUint32(b)
+	b, d.h[4] = consumeUint32(b)
+	b = b[copy(d.x[:], b):]
+	b, n := consumeUint64(b)
+	d.nl = uint32(n << 3)
+	d.nh = uint32(n >> 29)
+	d.nx = uint32(n) % 64
+	return nil
+}
+
+// NewSHA224 returns a new SHA224 hash.
+func NewSHA224() hash.Hash {
+	h := new(sha224Hash)
+	h.Reset()
+	return h
+}
+
+type sha224Hash struct {
+	ctx C.GO_SHA256_CTX
+	out [224 / 8]byte
+}
+
+func (h *sha224Hash) Reset()               { C._goboringcrypto_SHA224_Init(&h.ctx) }
+func (h *sha224Hash) Size() int            { return 224 / 8 }
+func (h *sha224Hash) BlockSize() int       { return 64 }
+func (h *sha224Hash) Sum(in []byte) []byte { return append(in, h.sum()...) }
+
+func (h *sha224Hash) Write(p []byte) (int, error) {
+	if len(p) > 0 && C._goboringcrypto_SHA224_Update(&h.ctx, unsafe.Pointer(&p[0]), C.size_t(len(p))) == 0 {
+		panic("boringcrypto: SHA224_Update failed")
+	}
+	return len(p), nil
+}
+
+func (h0 *sha224Hash) sum() []byte {
+	h := *h0 // make copy so future Write+Sum is valid
+	if C._goboringcrypto_SHA224_Final((*C.uint8_t)(unsafe.Pointer(&h.out[0])), &h.ctx) == 0 {
+		panic("boringcrypto: SHA224_Final failed")
+	}
+	return h.out[:]
+}
+
+// NewSHA256 returns a new SHA256 hash.
+func NewSHA256() hash.Hash {
+	h := new(sha256Hash)
+	h.Reset()
+	return h
+}
+
+type sha256Hash struct {
+	ctx C.GO_SHA256_CTX
+	out [256 / 8]byte
+}
+
+func (h *sha256Hash) Reset()               { C._goboringcrypto_SHA256_Init(&h.ctx) }
+func (h *sha256Hash) Size() int            { return 256 / 8 }
+func (h *sha256Hash) BlockSize() int       { return 64 }
+func (h *sha256Hash) Sum(in []byte) []byte { return append(in, h.sum()...) }
+
+func (h *sha256Hash) Write(p []byte) (int, error) {
+	if len(p) > 0 && C._goboringcrypto_SHA256_Update(&h.ctx, unsafe.Pointer(&p[0]), C.size_t(len(p))) == 0 {
+		panic("boringcrypto: SHA256_Update failed")
+	}
+	return len(p), nil
+}
+
+func (h0 *sha256Hash) sum() []byte {
+	h := *h0 // make copy so future Write+Sum is valid
+	if C._goboringcrypto_SHA256_Final((*C.uint8_t)(unsafe.Pointer(&h.out[0])), &h.ctx) == 0 {
+		panic("boringcrypto: SHA256_Final failed")
+	}
+	return h.out[:]
+}
+
+const (
+	magic224         = "sha\x02"
+	magic256         = "sha\x03"
+	marshaledSize256 = len(magic256) + 8*4 + 64 + 8
+)
+
+type sha256Ctx struct {
+	h      [8]uint32
+	nl, nh uint32
+	x      [64]byte
+	nx     uint32
+}
+
+func (h *sha224Hash) MarshalBinary() ([]byte, error) {
+	d := (*sha256Ctx)(unsafe.Pointer(&h.ctx))
+	b := make([]byte, 0, marshaledSize256)
+	b = append(b, magic224...)
+	b = appendUint32(b, d.h[0])
+	b = appendUint32(b, d.h[1])
+	b = appendUint32(b, d.h[2])
+	b = appendUint32(b, d.h[3])
+	b = appendUint32(b, d.h[4])
+	b = appendUint32(b, d.h[5])
+	b = appendUint32(b, d.h[6])
+	b = appendUint32(b, d.h[7])
+	b = append(b, d.x[:d.nx]...)
+	b = b[:len(b)+len(d.x)-int(d.nx)] // already zero
+	b = appendUint64(b, uint64(d.nl)>>3|uint64(d.nh)<<29)
+	return b, nil
+}
+
+func (h *sha256Hash) MarshalBinary() ([]byte, error) {
+	d := (*sha256Ctx)(unsafe.Pointer(&h.ctx))
+	b := make([]byte, 0, marshaledSize256)
+	b = append(b, magic256...)
+	b = appendUint32(b, d.h[0])
+	b = appendUint32(b, d.h[1])
+	b = appendUint32(b, d.h[2])
+	b = appendUint32(b, d.h[3])
+	b = appendUint32(b, d.h[4])
+	b = appendUint32(b, d.h[5])
+	b = appendUint32(b, d.h[6])
+	b = appendUint32(b, d.h[7])
+	b = append(b, d.x[:d.nx]...)
+	b = b[:len(b)+len(d.x)-int(d.nx)] // already zero
+	b = appendUint64(b, uint64(d.nl)>>3|uint64(d.nh)<<29)
+	return b, nil
+}
+
+func (h *sha224Hash) UnmarshalBinary(b []byte) error {
+	if len(b) < len(magic224) || string(b[:len(magic224)]) != magic224 {
+		return errors.New("crypto/sha256: invalid hash state identifier")
+	}
+	if len(b) != marshaledSize256 {
+		return errors.New("crypto/sha256: invalid hash state size")
+	}
+	d := (*sha256Ctx)(unsafe.Pointer(&h.ctx))
+	b = b[len(magic224):]
+	b, d.h[0] = consumeUint32(b)
+	b, d.h[1] = consumeUint32(b)
+	b, d.h[2] = consumeUint32(b)
+	b, d.h[3] = consumeUint32(b)
+	b, d.h[4] = consumeUint32(b)
+	b, d.h[5] = consumeUint32(b)
+	b, d.h[6] = consumeUint32(b)
+	b, d.h[7] = consumeUint32(b)
+	b = b[copy(d.x[:], b):]
+	b, n := consumeUint64(b)
+	d.nl = uint32(n << 3)
+	d.nh = uint32(n >> 29)
+	d.nx = uint32(n) % 64
+	return nil
+}
+
+func (h *sha256Hash) UnmarshalBinary(b []byte) error {
+	if len(b) < len(magic256) || string(b[:len(magic256)]) != magic256 {
+		return errors.New("crypto/sha256: invalid hash state identifier")
+	}
+	if len(b) != marshaledSize256 {
+		return errors.New("crypto/sha256: invalid hash state size")
+	}
+	d := (*sha256Ctx)(unsafe.Pointer(&h.ctx))
+	b = b[len(magic256):]
+	b, d.h[0] = consumeUint32(b)
+	b, d.h[1] = consumeUint32(b)
+	b, d.h[2] = consumeUint32(b)
+	b, d.h[3] = consumeUint32(b)
+	b, d.h[4] = consumeUint32(b)
+	b, d.h[5] = consumeUint32(b)
+	b, d.h[6] = consumeUint32(b)
+	b, d.h[7] = consumeUint32(b)
+	b = b[copy(d.x[:], b):]
+	b, n := consumeUint64(b)
+	d.nl = uint32(n << 3)
+	d.nh = uint32(n >> 29)
+	d.nx = uint32(n) % 64
+	return nil
+}
+
+// NewSHA384 returns a new SHA384 hash.
+func NewSHA384() hash.Hash {
+	h := new(sha384Hash)
+	h.Reset()
+	return h
+}
+
+type sha384Hash struct {
+	ctx C.GO_SHA512_CTX
+	out [384 / 8]byte
+}
+
+func (h *sha384Hash) Reset()               { C._goboringcrypto_SHA384_Init(&h.ctx) }
+func (h *sha384Hash) Size() int            { return 384 / 8 }
+func (h *sha384Hash) BlockSize() int       { return 128 }
+func (h *sha384Hash) Sum(in []byte) []byte { return append(in, h.sum()...) }
+
+func (h *sha384Hash) Write(p []byte) (int, error) {
+	if len(p) > 0 && C._goboringcrypto_SHA384_Update(&h.ctx, unsafe.Pointer(&p[0]), C.size_t(len(p))) == 0 {
+		panic("boringcrypto: SHA384_Update failed")
+	}
+	return len(p), nil
+}
+
+func (h0 *sha384Hash) sum() []byte {
+	h := *h0 // make copy so future Write+Sum is valid
+	if C._goboringcrypto_SHA384_Final((*C.uint8_t)(unsafe.Pointer(&h.out[0])), &h.ctx) == 0 {
+		panic("boringcrypto: SHA384_Final failed")
+	}
+	return h.out[:]
+}
+
+// NewSHA512 returns a new SHA512 hash.
+func NewSHA512() hash.Hash {
+	h := new(sha512Hash)
+	h.Reset()
+	return h
+}
+
+type sha512Hash struct {
+	ctx C.GO_SHA512_CTX
+	out [512 / 8]byte
+}
+
+func (h *sha512Hash) Reset()               { C._goboringcrypto_SHA512_Init(&h.ctx) }
+func (h *sha512Hash) Size() int            { return 512 / 8 }
+func (h *sha512Hash) BlockSize() int       { return 128 }
+func (h *sha512Hash) Sum(in []byte) []byte { return append(in, h.sum()...) }
+
+func (h *sha512Hash) Write(p []byte) (int, error) {
+	if len(p) > 0 && C._goboringcrypto_SHA512_Update(&h.ctx, unsafe.Pointer(&p[0]), C.size_t(len(p))) == 0 {
+		panic("boringcrypto: SHA512_Update failed")
+	}
+	return len(p), nil
+}
+
+func (h0 *sha512Hash) sum() []byte {
+	h := *h0 // make copy so future Write+Sum is valid
+	if C._goboringcrypto_SHA512_Final((*C.uint8_t)(unsafe.Pointer(&h.out[0])), &h.ctx) == 0 {
+		panic("boringcrypto: SHA512_Final failed")
+	}
+	return h.out[:]
+}
+
+type sha512Ctx struct {
+	h      [8]uint64
+	nl, nh uint64
+	x      [128]byte
+	nx     uint32
+}
+
+const (
+	magic384         = "sha\x04"
+	magic512_224     = "sha\x05"
+	magic512_256     = "sha\x06"
+	magic512         = "sha\x07"
+	marshaledSize512 = len(magic512) + 8*8 + 128 + 8
+)
+
+func (h *sha384Hash) MarshalBinary() ([]byte, error) {
+	d := (*sha512Ctx)(unsafe.Pointer(&h.ctx))
+	b := make([]byte, 0, marshaledSize512)
+	b = append(b, magic384...)
+	b = appendUint64(b, d.h[0])
+	b = appendUint64(b, d.h[1])
+	b = appendUint64(b, d.h[2])
+	b = appendUint64(b, d.h[3])
+	b = appendUint64(b, d.h[4])
+	b = appendUint64(b, d.h[5])
+	b = appendUint64(b, d.h[6])
+	b = appendUint64(b, d.h[7])
+	b = append(b, d.x[:d.nx]...)
+	b = b[:len(b)+len(d.x)-int(d.nx)] // already zero
+	b = appendUint64(b, d.nl>>3|d.nh<<61)
+	return b, nil
+}
+
+func (h *sha512Hash) MarshalBinary() ([]byte, error) {
+	d := (*sha512Ctx)(unsafe.Pointer(&h.ctx))
+	b := make([]byte, 0, marshaledSize512)
+	b = append(b, magic512...)
+	b = appendUint64(b, d.h[0])
+	b = appendUint64(b, d.h[1])
+	b = appendUint64(b, d.h[2])
+	b = appendUint64(b, d.h[3])
+	b = appendUint64(b, d.h[4])
+	b = appendUint64(b, d.h[5])
+	b = appendUint64(b, d.h[6])
+	b = appendUint64(b, d.h[7])
+	b = append(b, d.x[:d.nx]...)
+	b = b[:len(b)+len(d.x)-int(d.nx)] // already zero
+	b = appendUint64(b, d.nl>>3|d.nh<<61)
+	return b, nil
+}
+
+func (h *sha384Hash) UnmarshalBinary(b []byte) error {
+	if len(b) < len(magic512) {
+		return errors.New("crypto/sha512: invalid hash state identifier")
+	}
+	if string(b[:len(magic384)]) != magic384 {
+		return errors.New("crypto/sha512: invalid hash state identifier")
+	}
+	if len(b) != marshaledSize512 {
+		return errors.New("crypto/sha512: invalid hash state size")
+	}
+	d := (*sha512Ctx)(unsafe.Pointer(&h.ctx))
+	b = b[len(magic512):]
+	b, d.h[0] = consumeUint64(b)
+	b, d.h[1] = consumeUint64(b)
+	b, d.h[2] = consumeUint64(b)
+	b, d.h[3] = consumeUint64(b)
+	b, d.h[4] = consumeUint64(b)
+	b, d.h[5] = consumeUint64(b)
+	b, d.h[6] = consumeUint64(b)
+	b, d.h[7] = consumeUint64(b)
+	b = b[copy(d.x[:], b):]
+	b, n := consumeUint64(b)
+	d.nl = n << 3
+	d.nh = n >> 61
+	d.nx = uint32(n) % 128
+	return nil
+}
+
+func (h *sha512Hash) UnmarshalBinary(b []byte) error {
+	if len(b) < len(magic512) {
+		return errors.New("crypto/sha512: invalid hash state identifier")
+	}
+	if string(b[:len(magic512)]) != magic512 {
+		return errors.New("crypto/sha512: invalid hash state identifier")
+	}
+	if len(b) != marshaledSize512 {
+		return errors.New("crypto/sha512: invalid hash state size")
+	}
+	d := (*sha512Ctx)(unsafe.Pointer(&h.ctx))
+	b = b[len(magic512):]
+	b, d.h[0] = consumeUint64(b)
+	b, d.h[1] = consumeUint64(b)
+	b, d.h[2] = consumeUint64(b)
+	b, d.h[3] = consumeUint64(b)
+	b, d.h[4] = consumeUint64(b)
+	b, d.h[5] = consumeUint64(b)
+	b, d.h[6] = consumeUint64(b)
+	b, d.h[7] = consumeUint64(b)
+	b = b[copy(d.x[:], b):]
+	b, n := consumeUint64(b)
+	d.nl = n << 3
+	d.nh = n >> 61
+	d.nx = uint32(n) % 128
+	return nil
+}
+
+func appendUint64(b []byte, x uint64) []byte {
+	var a [8]byte
+	putUint64(a[:], x)
+	return append(b, a[:]...)
+}
+
+func appendUint32(b []byte, x uint32) []byte {
+	var a [4]byte
+	putUint32(a[:], x)
+	return append(b, a[:]...)
+}
+
+func consumeUint64(b []byte) ([]byte, uint64) {
+	_ = b[7]
+	x := uint64(b[7]) | uint64(b[6])<<8 | uint64(b[5])<<16 | uint64(b[4])<<24 |
+		uint64(b[3])<<32 | uint64(b[2])<<40 | uint64(b[1])<<48 | uint64(b[0])<<56
+	return b[8:], x
+}
+
+func consumeUint32(b []byte) ([]byte, uint32) {
+	_ = b[3]
+	x := uint32(b[3]) | uint32(b[2])<<8 | uint32(b[1])<<16 | uint32(b[0])<<24
+	return b[4:], x
+}
+
+func putUint64(x []byte, s uint64) {
+	_ = x[7]
+	x[0] = byte(s >> 56)
+	x[1] = byte(s >> 48)
+	x[2] = byte(s >> 40)
+	x[3] = byte(s >> 32)
+	x[4] = byte(s >> 24)
+	x[5] = byte(s >> 16)
+	x[6] = byte(s >> 8)
+	x[7] = byte(s)
+}
+
+func putUint32(x []byte, s uint32) {
+	_ = x[3]
+	x[0] = byte(s >> 24)
+	x[1] = byte(s >> 16)
+	x[2] = byte(s >> 8)
+	x[3] = byte(s)
+}