## Summary
Fixes#11029 - `crypto.verify()` now correctly handles null/undefined
algorithm parameter for RSA keys, matching Node.js behavior.
## Problem
When calling `crypto.verify()` with a null or undefined algorithm
parameter, Bun was throwing an error:
```
error: error:06000077:public key routines:OPENSSL_internal:NO_DEFAULT_DIGEST
```
## Root Cause
The issue stems from the difference between OpenSSL (used by Node.js)
and BoringSSL (used by Bun):
- **OpenSSL v3**: Automatically provides SHA256 as the default digest
for RSA keys when NULL is passed
- **BoringSSL**: Returns an error when NULL digest is passed for RSA
keys
## Solution
This fix explicitly sets SHA256 as the default digest for RSA keys when
no algorithm is specified, achieving OpenSSL-compatible behavior.
## OpenSSL v3 Source Code Analysis
I traced through the OpenSSL v3 source code to understand exactly how it
handles null digests:
### 1. Entry Point (`crypto/evp/m_sigver.c`)
When `EVP_DigestSignInit` or `EVP_DigestVerifyInit` is called with NULL
digest:
```c
// Lines 215-220 in do_sigver_init function
if (mdname == NULL && !reinit) {
if (evp_keymgmt_util_get_deflt_digest_name(tmp_keymgmt, provkey,
locmdname,
sizeof(locmdname)) > 0) {
mdname = canon_mdname(locmdname);
}
}
```
### 2. Default Digest Query (`crypto/evp/keymgmt_lib.c`)
```c
// Lines 533-571 in evp_keymgmt_util_get_deflt_digest_name
params[0] = OSSL_PARAM_construct_utf8_string(OSSL_PKEY_PARAM_DEFAULT_DIGEST,
mddefault, sizeof(mddefault));
if (!evp_keymgmt_get_params(keymgmt, keydata, params))
return 0;
```
### 3. RSA Provider Implementation
(`providers/implementations/keymgmt/rsa_kmgmt.c`)
```c
// Line 54: Define the default
#define RSA_DEFAULT_MD "SHA256"
// Lines 351-355: Return it for RSA keys
if ((p = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_DEFAULT_DIGEST)) != NULL
&& (rsa_type != RSA_FLAG_TYPE_RSASSAPSS
|| ossl_rsa_pss_params_30_is_unrestricted(pss_params))) {
if (!OSSL_PARAM_set_utf8_string(p, RSA_DEFAULT_MD))
return 0;
}
```
## Implementation Details
The fix includes extensive documentation in the source code explaining:
- The OpenSSL v3 mechanism with specific file paths and line numbers
- Why BoringSSL behaves differently
- Why Ed25519/Ed448 keys are handled differently (they don't need a
digest)
## Test Plan
✅ Added comprehensive regression test in
`test/regression/issue/11029-crypto-verify-null-algorithm.test.ts`
✅ Tests cover:
- RSA keys with null/undefined algorithm
- Ed25519 keys with null algorithm
- Cross-verification between null and explicit SHA256
- `createVerify()` compatibility
✅ All tests pass and behavior matches Node.js
## Verification
```bash
# Test with Bun
bun test test/regression/issue/11029-crypto-verify-null-algorithm.test.ts
# Compare with Node.js behavior
node -e "const crypto = require('crypto');
const {publicKey, privateKey} = crypto.generateKeyPairSync('rsa', {modulusLength: 2048});
const data = Buffer.from('test');
const sig = crypto.sign(null, data, privateKey);
console.log('Node.js verify with null:', crypto.verify(null, data, publicKey, sig));"
```
🤖 Generated with [Claude Code](https://claude.ai/code)
---------
Co-authored-by: Claude Bot <claude-bot@bun.sh>
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>