From 9721a2abbae8f9cfaf4c1b76ee2a0a60d1478614 Mon Sep 17 00:00:00 2001 From: Jeremiah Mackey Date: Fri, 8 May 2026 15:46:14 +0000 Subject: [PATCH 1/4] Fix ChaCha20Poly1305 and AES-GCM appended-tag oversized outCipher --- aes.go | 18 +++++++++++++----- chacha_poly.go | 18 +++++++++++++----- 2 files changed, 26 insertions(+), 10 deletions(-) diff --git a/aes.go b/aes.go index b268544..c5663af 100644 --- a/aes.go +++ b/aes.go @@ -232,18 +232,26 @@ func Wc_AesGcmDecrypt(aes *C.struct_Aes, outPlain, inCipher, inIv, inAuthTag, in } +// Wc_AesGcm_Appended_Tag_Encrypt encrypts inPlain and appends the GCM tag, +// returning a slice of length exactly len(inPlain)+AES_BLOCK_SIZE. If +// outCipher is large enough it is used as backing storage; otherwise a new +// slice is allocated. func Wc_AesGcm_Appended_Tag_Encrypt(aes *C.struct_Aes, outCipher, inPlain, inIv, inAAD []byte) ([]byte, int) { var outAuthTag [AES_BLOCK_SIZE]byte var longOutCipher []byte - if len(outCipher) < (len(inPlain) + AES_BLOCK_SIZE) { - longOutCipher = make([]byte, len(inPlain) + AES_BLOCK_SIZE) + need := len(inPlain) + AES_BLOCK_SIZE + if len(outCipher) < need { + longOutCipher = make([]byte, need) } else { - longOutCipher = outCipher + // Reslice to `need` so the returned cipher||tag has no gap when + // outCipher is oversized; reverting this re-introduces uninitialized + // bytes between ciphertext and tag. + longOutCipher = outCipher[:need] } - ret := Wc_AesGcmEncrypt(aes, longOutCipher[:(len(longOutCipher)-AES_BLOCK_SIZE)], inPlain, inIv, outAuthTag[:], inAAD) - copy(longOutCipher[(len(longOutCipher)-AES_BLOCK_SIZE):], outAuthTag[:]) + ret := Wc_AesGcmEncrypt(aes, longOutCipher[:len(inPlain)], inPlain, inIv, outAuthTag[:], inAAD) + copy(longOutCipher[len(inPlain):], outAuthTag[:]) return longOutCipher, ret } diff --git a/chacha_poly.go b/chacha_poly.go index 3631b1e..1cfccac 100644 --- a/chacha_poly.go +++ b/chacha_poly.go @@ -121,18 +121,26 @@ func Wc_ChaCha20Poly1305_Decrypt(inKey, inIv, inAAD, inCipher, inAuthTag , outPl (*C.uchar)(unsafe.Pointer(&inAuthTag[0])), sanOutPlain)) } +// Wc_ChaCha20Poly1305_Appended_Tag_Encrypt encrypts inPlain and appends the +// 16-byte tag, returning a slice of length exactly +// len(inPlain)+CHACHA20_POLY1305_AEAD_AUTHTAG_SIZE. If outCipher is large +// enough it is used as backing storage; otherwise a new slice is allocated. func Wc_ChaCha20Poly1305_Appended_Tag_Encrypt(inKey, inIv, inAAD, inPlain, outCipher []byte) ([]byte, int) { var outAuthTag [CHACHA20_POLY1305_AEAD_AUTHTAG_SIZE]byte var longOutCipher []byte - if len(outCipher) < (len(inPlain) + CHACHA20_POLY1305_AEAD_AUTHTAG_SIZE) { - longOutCipher = make([]byte, len(inPlain) + CHACHA20_POLY1305_AEAD_AUTHTAG_SIZE) + need := len(inPlain) + CHACHA20_POLY1305_AEAD_AUTHTAG_SIZE + if len(outCipher) < need { + longOutCipher = make([]byte, need) } else { - longOutCipher = outCipher + // Reslice to `need` so the returned cipher||tag has no gap when + // outCipher is oversized; reverting this re-introduces uninitialized + // bytes between ciphertext and tag. + longOutCipher = outCipher[:need] } - ret := Wc_ChaCha20Poly1305_Encrypt(inKey, inIv, inAAD, inPlain, longOutCipher[:(len(longOutCipher)-CHACHA20_POLY1305_AEAD_AUTHTAG_SIZE)], outAuthTag[:]) - copy(longOutCipher[(len(longOutCipher)-CHACHA20_POLY1305_AEAD_AUTHTAG_SIZE):], outAuthTag[:]) + ret := Wc_ChaCha20Poly1305_Encrypt(inKey, inIv, inAAD, inPlain, longOutCipher[:len(inPlain)], outAuthTag[:]) + copy(longOutCipher[len(inPlain):], outAuthTag[:]) return longOutCipher, ret } From 1a5baa6987bbd2ca32dd9d4debe5020de2be35e6 Mon Sep 17 00:00:00 2001 From: Jeremiah Mackey Date: Fri, 8 May 2026 15:16:10 +0000 Subject: [PATCH 2/4] Add cgo stubs for NO_HMAC and NO_PWDBASED builds --- aes.go | 10 ++++++++++ hmac.go | 34 +++++++++++++++++++++++++++++++++- 2 files changed, 43 insertions(+), 1 deletion(-) diff --git a/aes.go b/aes.go index c5663af..1bf308a 100644 --- a/aes.go +++ b/aes.go @@ -116,6 +116,16 @@ package wolfSSL // return -174; // } // #endif +// #ifdef NO_PWDBASED +// int wc_PBKDF2(byte* output, const byte* passwd, int pLen, +// const byte* salt, int sLen, int iterations, +// int kLen, int typeH) { +// (void)output; (void)passwd; (void)pLen; +// (void)salt; (void)sLen; (void)iterations; +// (void)kLen; (void)typeH; +// return -174; +// } +// #endif import "C" import ( "unsafe" diff --git a/hmac.go b/hmac.go index 23b7320..19dae43 100644 --- a/hmac.go +++ b/hmac.go @@ -23,7 +23,28 @@ package wolfSSL // #include // #include -// #ifdef _WIN32 +// #ifdef NO_HMAC +// typedef struct Hmac {} Hmac; +// int wc_HmacInit(Hmac* hmac, void* heap, int devId) { +// (void)hmac; (void)heap; (void)devId; +// return -174; +// } +// void wc_HmacFree(Hmac* hmac) { (void)hmac; } +// int wc_HmacSetKey(Hmac* hmac, int type, const byte* key, word32 keySz) { +// (void)hmac; (void)type; (void)key; (void)keySz; +// return -174; +// } +// int wc_HmacUpdate(Hmac* hmac, const byte* msg, word32 length) { +// (void)hmac; (void)msg; (void)length; +// return -174; +// } +// int wc_HmacFinal(Hmac* hmac, byte* hash) { +// (void)hmac; (void)hash; +// return -174; +// } +// Hmac* wc_HmacAllocAligned(void) { return NULL; } +// void wc_HmacFreeAllocAligned(Hmac* ptr) { (void)ptr; } +// #elif defined(_WIN32) // #include // #include // Hmac* wc_HmacAllocAligned(void) { @@ -46,6 +67,17 @@ package wolfSSL // free(ptr); // } // #endif +// #if defined(NO_HMAC) || !defined(HAVE_HKDF) +// int wc_HKDF(int type, const byte* inKey, word32 inKeySz, +// const byte* salt, word32 saltSz, +// const byte* info, word32 infoSz, +// byte* out, word32 outSz) { +// (void)type; (void)inKey; (void)inKeySz; +// (void)salt; (void)saltSz; (void)info; (void)infoSz; +// (void)out; (void)outSz; +// return -174; +// } +// #endif import "C" import ( "unsafe" From 96e639fb983739f6901ecb0eefba313eec361b9a Mon Sep 17 00:00:00 2001 From: Jeremiah Mackey Date: Fri, 8 May 2026 15:47:00 +0000 Subject: [PATCH 3/4] Cap 64-bit lengths at MaxInt32 in cgo boundaries --- curve25519.go | 5 +++-- ssl.go | 5 +++-- x509.go | 10 +++++++--- 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/curve25519.go b/curve25519.go index 7266247..c5a1f76 100644 --- a/curve25519.go +++ b/curve25519.go @@ -67,6 +67,7 @@ package wolfSSL // #endif import "C" import ( + "math" "unsafe" ) @@ -85,13 +86,13 @@ func Wc_curve25519_make_key(rng *C.struct_WC_RNG, keySize int, key *C.struct_cur } func Wc_curve25519_make_pub(pub, priv []byte) int { - if len(pub) == 0 || len(priv) == 0 { return BAD_FUNC_ARG } + if len(pub) == 0 || len(priv) == 0 || len(pub) > math.MaxInt32 || len(priv) > math.MaxInt32 { return BAD_FUNC_ARG } return int(C.wc_curve25519_make_pub(C.int(len(pub)),(*C.uchar)(unsafe.Pointer(&pub[0])), C.int(len(priv)), (*C.uchar)(unsafe.Pointer(&priv[0])))) } func Wc_curve25519_make_priv(rng *C.struct_WC_RNG, priv []byte) int { - if len(priv) == 0 { return BAD_FUNC_ARG } + if len(priv) == 0 || len(priv) > math.MaxInt32 { return BAD_FUNC_ARG } return int(C.wc_curve25519_make_priv(rng, C.int(len(priv)), (*C.uchar)(unsafe.Pointer(&priv[0])))) } diff --git a/ssl.go b/ssl.go index bc7959e..5e84e10 100644 --- a/ssl.go +++ b/ssl.go @@ -112,6 +112,7 @@ package wolfSSL // #endif import "C" import ( + "math" "unsafe" ) @@ -265,7 +266,7 @@ func WolfSSL_accept(ssl *C.struct_WOLFSSL) int { func WolfSSL_read(ssl *C.struct_WOLFSSL, data []byte, sz uintptr) int { if sz == 0 { return 0 } - if len(data) == 0 || sz > uintptr(len(data)) { + if len(data) == 0 || sz > uintptr(len(data)) || sz > math.MaxInt32 { return BAD_FUNC_ARG } return int(C.wolfSSL_read(ssl, unsafe.Pointer(&data[0]), C.int(sz))) @@ -273,7 +274,7 @@ func WolfSSL_read(ssl *C.struct_WOLFSSL, data []byte, sz uintptr) int { func WolfSSL_write(ssl *C.struct_WOLFSSL, data []byte, sz uintptr) int { if sz == 0 { return 0 } - if len(data) == 0 || sz > uintptr(len(data)) { + if len(data) == 0 || sz > uintptr(len(data)) || sz > math.MaxInt32 { return BAD_FUNC_ARG } return int(C.wolfSSL_write(ssl, unsafe.Pointer(&data[0]), C.int(sz))) diff --git a/x509.go b/x509.go index c535b15..5b8f38c 100644 --- a/x509.go +++ b/x509.go @@ -128,6 +128,7 @@ package wolfSSL // #include import "C" import ( + "math" "sync" "unsafe" ) @@ -192,13 +193,14 @@ func WolfSSL_X509_verify_cert(ctx *C.WOLFSSL_X509_STORE_CTX) int { } func WolfSSL_X509_load_certificate_buffer(buff []byte, buffSz int, certType int) *C.WOLFSSL_X509 { - if buffSz < 0 || buffSz > len(buff) || len(buff) == 0 { return nil } + if buffSz < 0 || buffSz > len(buff) || len(buff) == 0 || buffSz > math.MaxInt32 { return nil } return C.wolfSSL_X509_load_certificate_buffer((*C.byte)(unsafe.Pointer(&buff[0])), C.int(buffSz), C.int(certType)) } func WolfSSL_X509_get_pubkey_buffer(cert *WOLFSSL_X509, out []byte, outLen *int) int { if outLen == nil { return BAD_FUNC_ARG } - if len(out) > 0 && (*outLen < 0 || *outLen > len(out)) { return BAD_FUNC_ARG } + if *outLen < 0 || *outLen > math.MaxInt32 { return BAD_FUNC_ARG } + if len(out) > 0 && *outLen > len(out) { return BAD_FUNC_ARG } var outPtr *C.uchar if len(out) > 0 { outPtr = (*C.uchar)(unsafe.Pointer(&out[0])) @@ -210,7 +212,9 @@ func WolfSSL_X509_get_pubkey_buffer(cert *WOLFSSL_X509, out []byte, outLen *int) } func WolfSSL_BIO_new_mem_buf(buf []byte, bufLen int) *WOLFSSL_BIO { - if bufLen <= 0 || bufLen > len(buf) { + // MaxInt32 cap: casting > MaxInt32 to C.int can wrap to -1, which OpenSSL + // interprets as a strlen sentinel and would scan past cBuf. + if bufLen <= 0 || bufLen > len(buf) || bufLen > math.MaxInt32 { return nil } cBuf := C.CBytes(buf[:bufLen]) From 5a243d594699aaa1ff7724726c1c04b063abcc5f Mon Sep 17 00:00:00 2001 From: Jeremiah Mackey Date: Fri, 8 May 2026 15:48:07 +0000 Subject: [PATCH 4/4] Validate PBKDF2 iter and curve25519 buffer sizes --- aes.go | 7 +++++++ curve25519.go | 13 +++++++++---- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/aes.go b/aes.go index 1bf308a..a4729d5 100644 --- a/aes.go +++ b/aes.go @@ -128,6 +128,7 @@ package wolfSSL // #endif import "C" import ( + "math" "unsafe" ) @@ -281,6 +282,12 @@ func Wc_PBKDF2(out []byte, pwd []byte, pLen int, salt []byte, saltLen int, iter pLen > len(pwd) || saltLen > len(salt) || kLen > len(out) { return BAD_FUNC_ARG } + if iter <= 0 || iter > math.MaxInt32 { + return BAD_FUNC_ARG + } + if pLen > math.MaxInt32 || saltLen > math.MaxInt32 || kLen > math.MaxInt32 { + return BAD_FUNC_ARG + } var outPtr *C.uchar if len(out) > 0 { outPtr = (*C.uchar)(unsafe.Pointer(&out[0])) diff --git a/curve25519.go b/curve25519.go index c5a1f76..7da97d8 100644 --- a/curve25519.go +++ b/curve25519.go @@ -27,6 +27,7 @@ package wolfSSL // #ifndef HAVE_CURVE25519 // typedef struct curve25519_key {} curve25519_key; // #define EC25519_LITTLE_ENDIAN 1 +// #define CURVE25519_KEYSIZE 32 // int wc_curve25519_init(curve25519_key* key) { // return -174; // } @@ -73,6 +74,8 @@ import ( type Curve25519_key = C.struct_curve25519_key +const CURVE25519_KEYSIZE = int(C.CURVE25519_KEYSIZE) + func Wc_curve25519_init(key *C.struct_curve25519_key) int { return int(C.wc_curve25519_init(key)) } @@ -110,14 +113,16 @@ func Wc_curve25519_import_public(pub []byte, key *C.struct_curve25519_key) int { } func Wc_curve25519_export_private_raw(key *C.struct_curve25519_key, priv []byte) int { - if len(priv) == 0 { return BAD_FUNC_ARG } - cOutLen := C.word32(len(priv)) + if len(priv) < CURVE25519_KEYSIZE { return BAD_FUNC_ARG } + // Pin cOutLen to KEYSIZE so C writes exactly 32 bytes regardless of buffer size. + cOutLen := C.word32(CURVE25519_KEYSIZE) return int(C.wc_curve25519_export_private_raw(key, (*C.uchar)(unsafe.Pointer(&priv[0])), &cOutLen)) } func Wc_curve25519_shared_secret(privKey, pubKey *C.struct_curve25519_key, out []byte) int { - if len(out) == 0 { return BAD_FUNC_ARG } - cOutLen := C.word32(len(out)) + if len(out) < CURVE25519_KEYSIZE { return BAD_FUNC_ARG } + // Pin cOutLen to KEYSIZE so C writes exactly 32 bytes regardless of buffer size. + cOutLen := C.word32(CURVE25519_KEYSIZE) return int(C.wc_curve25519_shared_secret_ex(privKey, pubKey, (*C.uchar)(unsafe.Pointer(&out[0])), &cOutLen, C.EC25519_LITTLE_ENDIAN)) }