diff --git a/aes.go b/aes.go index 7ec81ee..830686f 100644 --- a/aes.go +++ b/aes.go @@ -115,6 +115,17 @@ package wolfSSL // return -174; // } // #endif +// /* unlock/op/lock must stay inside one cgo call; see fips.go */ +// static int wc_PBKDF2_Unlocked(byte* output, const byte* passwd, int pLen, +// const byte* salt, int sLen, int iterations, +// int kLen, int typeH) +// { +// int ret; +// PRIVATE_KEY_UNLOCK(); +// ret = wc_PBKDF2(output, passwd, pLen, salt, sLen, iterations, kLen, typeH); +// PRIVATE_KEY_LOCK(); +// return ret; +// } import "C" import ( "unsafe" @@ -274,10 +285,7 @@ func Wc_PBKDF2(out []byte, pwd []byte, pLen int, salt []byte, saltLen int, iter if len(salt) > 0 { saltPtr = (*C.uchar)(unsafe.Pointer(&salt[0])) } - PRIVATE_KEY_UNLOCK() - ret := int(C.wc_PBKDF2(outPtr, pwdPtr, C.int(pLen), + return int(C.wc_PBKDF2_Unlocked(outPtr, pwdPtr, C.int(pLen), saltPtr, C.int(saltLen), C.int(iter), C.int(kLen), C.int(typeH))) - PRIVATE_KEY_LOCK() - return ret } diff --git a/ecc.go b/ecc.go index 755ce59..3762482 100644 --- a/ecc.go +++ b/ecc.go @@ -129,6 +129,35 @@ package wolfSSL // (void)key; return -174; // } // #endif +// /* unlock/op/lock must stay inside one cgo call; see fips.go */ +// static int wc_ecc_export_private_only_Unlocked(ecc_key* key, byte* out, +// word32* outLen) +// { +// int ret; +// PRIVATE_KEY_UNLOCK(); +// ret = wc_ecc_export_private_only(key, out, outLen); +// PRIVATE_KEY_LOCK(); +// return ret; +// } +// static int wc_ecc_export_x963_ex_Unlocked(ecc_key* key, byte* out, +// word32* outLen, int compressed) +// { +// int ret; +// PRIVATE_KEY_UNLOCK(); +// ret = wc_ecc_export_x963_ex(key, out, outLen, compressed); +// PRIVATE_KEY_LOCK(); +// return ret; +// } +// static int wc_ecc_shared_secret_Unlocked(ecc_key* private_key, +// ecc_key* public_key, +// byte* out, word32* outLen) +// { +// int ret; +// PRIVATE_KEY_UNLOCK(); +// ret = wc_ecc_shared_secret(private_key, public_key, out, outLen); +// PRIVATE_KEY_LOCK(); +// return ret; +// } import "C" import ( "unsafe" @@ -165,10 +194,8 @@ func Wc_ecc_export_private_only(key *C.struct_ecc_key, out []byte, outLen *int) return BAD_FUNC_ARG } cOutLen := C.word32(*outLen) - PRIVATE_KEY_UNLOCK() - ret := int(C.wc_ecc_export_private_only(key, (*C.byte)(unsafe.Pointer(&out[0])), &cOutLen)) + ret := int(C.wc_ecc_export_private_only_Unlocked(key, (*C.byte)(unsafe.Pointer(&out[0])), &cOutLen)) *outLen = int(cOutLen) - PRIVATE_KEY_LOCK() return ret } @@ -177,10 +204,8 @@ func Wc_ecc_export_x963_ex(key *C.struct_ecc_key, out []byte, outLen *int, compr return BAD_FUNC_ARG } cOutLen := C.word32(*outLen) - PRIVATE_KEY_UNLOCK() - ret := int(C.wc_ecc_export_x963_ex(key, (*C.byte)(unsafe.Pointer(&out[0])), &cOutLen, C.int(compressed))) + ret := int(C.wc_ecc_export_x963_ex_Unlocked(key, (*C.byte)(unsafe.Pointer(&out[0])), &cOutLen, C.int(compressed))) *outLen = int(cOutLen) - PRIVATE_KEY_LOCK() return ret } @@ -254,10 +279,8 @@ func Wc_ecc_shared_secret(privKey, pubKey *C.struct_ecc_key, out []byte, outLen return BAD_FUNC_ARG } cOutLen := C.word32(*outLen) - PRIVATE_KEY_UNLOCK() - ret := int(C.wc_ecc_shared_secret(privKey, pubKey, (*C.uchar)(unsafe.Pointer(&out[0])), &cOutLen)) + ret := int(C.wc_ecc_shared_secret_Unlocked(privKey, pubKey, (*C.uchar)(unsafe.Pointer(&out[0])), &cOutLen)) *outLen = int(cOutLen) - PRIVATE_KEY_LOCK() return ret } diff --git a/fips.go b/fips.go index dcb6a5b..b3f8133 100644 --- a/fips.go +++ b/fips.go @@ -30,7 +30,6 @@ package wolfSSL // return -174; // } // #endif -// #define WC_SPKRE_F(x,y) wolfCrypt_SetPrivateKeyReadEnable_fips((x),(y)) // #ifdef HAVE_FIPS // int WC_PRIVATE_KEY_LOCK(void) { // return WC_SPKRE_F(0,WC_KEYTYPE_ALL); @@ -76,10 +75,32 @@ func Wc_SetDefaultSeed_Cb() int { return int(C.wc_SetSeed_Cb((C.wc_RngSeed_Cb)(C.wc_GenerateSeed))) } +// FIPS private-key gate. +// +// wolfCrypt's FIPS APIs refuse to read private-key material unless the +// calling thread has enabled it. In userspace builds that enable flag is +// per-OS-thread instead of process-wide. +// +// A goroutine is pinned to its OS thread only for the duration of a single +// cgo call. Calling PRIVATE_KEY_UNLOCK, then the wolfCrypt operation, then +// PRIVATE_KEY_LOCK as three separate cgo calls therefore leaves a window in +// which the scheduler can move the goroutine to another thread. The +// operation then runs on a thread whose counter was never incremented and +// fails with FIPS_PRIVATE_KEY_LOCKED_E (-287), while the thread that took +// the unlock is left permanently unlocked. +// +// The Wc_* wrappers in this go-wolfssl avoid that by performing unlock, +// operation and lock inside one static C helper (wc_HKDF_Unlocked, +// wc_PBKDF2_Unlocked, ...). +// +// PRIVATE_KEY_LOCK and PRIVATE_KEY_UNLOCK are exported but they act on the +// calling OS thread's counter, so any direct callers must hold +// runtime.LockOSThread() across the entire unlock/operation/lock sequence. func PRIVATE_KEY_LOCK() int { return int(C.WC_PRIVATE_KEY_LOCK()) } +// See PRIVATE_KEY_LOCK for the threading requirements. func PRIVATE_KEY_UNLOCK() int { return int(C.WC_PRIVATE_KEY_UNLOCK()) } diff --git a/hmac.go b/hmac.go index a803673..a9986e5 100644 --- a/hmac.go +++ b/hmac.go @@ -45,6 +45,18 @@ package wolfSSL // free(ptr); // } // #endif +// /* unlock/op/lock must stay inside one cgo call; see fips.go */ +// static int wc_HKDF_Unlocked(int type, const byte* inKey, word32 inKeySz, +// const byte* salt, word32 saltSz, +// const byte* info, word32 infoSz, +// byte* out, word32 outSz) +// { +// int ret; +// PRIVATE_KEY_UNLOCK(); +// ret = wc_HKDF(type, inKey, inKeySz, salt, saltSz, info, infoSz, out, outSz); +// PRIVATE_KEY_LOCK(); +// return ret; +// } import "C" import ( "unsafe" @@ -119,12 +131,9 @@ func Wc_HKDF(hashType int, inputKey []byte, inputKeySz int, salt []byte, if len(info) > 0 { infoPtr = (*C.uchar)(unsafe.Pointer(&info[0])) } - PRIVATE_KEY_UNLOCK() - ret := int(C.wc_HKDF(C.int(hashType), ikmPtr, + return int(C.wc_HKDF_Unlocked(C.int(hashType), ikmPtr, C.word32(inputKeySz), saltPtr, C.word32(saltSz), infoPtr, C.word32(infoSz), (*C.uchar)(unsafe.Pointer(&out[0])), C.word32(outSz))) - PRIVATE_KEY_LOCK() - return ret }