@@ -212,6 +212,113 @@ static int NoPasswordCallback(char* buf, int size, int rwflag, void* u) {
212212}
213213
214214
215+ // namespace node::crypto::error
216+ namespace error {
217+ void Decorate (Environment* env, Local<Object> obj,
218+ unsigned long err) { // NOLINT(runtime/int)
219+ if (err == 0 ) return ; // No decoration possible.
220+
221+ const char * ls = ERR_lib_error_string (err);
222+ const char * fs = ERR_func_error_string (err);
223+ const char * rs = ERR_reason_error_string (err);
224+
225+ Isolate* isolate = env->isolate ();
226+ Local<Context> context = isolate->GetCurrentContext ();
227+
228+ if (ls != nullptr ) {
229+ if (obj->Set (context, env->library_string (),
230+ OneByteString (isolate, ls)).IsNothing ()) {
231+ return ;
232+ }
233+ }
234+ if (fs != nullptr ) {
235+ if (obj->Set (context, env->function_string (),
236+ OneByteString (isolate, fs)).IsNothing ()) {
237+ return ;
238+ }
239+ }
240+ if (rs != nullptr ) {
241+ if (obj->Set (context, env->reason_string (),
242+ OneByteString (isolate, rs)).IsNothing ()) {
243+ return ;
244+ }
245+
246+ // SSL has no API to recover the error name from the number, so we
247+ // transform reason strings like "this error" to "ERR_SSL_THIS_ERROR",
248+ // which ends up being close to the original error macro name.
249+ std::string reason (rs);
250+
251+ for (auto & c : reason) {
252+ if (c == ' ' )
253+ c = ' _' ;
254+ else
255+ c = ToUpper (c);
256+ }
257+
258+ #define OSSL_ERROR_CODES_MAP (V ) \
259+ V (SYS) \
260+ V (BN) \
261+ V (RSA) \
262+ V (DH) \
263+ V (EVP) \
264+ V (BUF) \
265+ V (OBJ) \
266+ V (PEM) \
267+ V (DSA) \
268+ V (X509) \
269+ V (ASN1) \
270+ V (CONF) \
271+ V (CRYPTO) \
272+ V (EC) \
273+ V (SSL) \
274+ V (BIO) \
275+ V (PKCS7) \
276+ V (X509V3) \
277+ V (PKCS12) \
278+ V (RAND) \
279+ V (DSO) \
280+ V (ENGINE) \
281+ V (OCSP) \
282+ V (UI) \
283+ V (COMP) \
284+ V (ECDSA) \
285+ V (ECDH) \
286+ V (OSSL_STORE) \
287+ V (FIPS) \
288+ V (CMS) \
289+ V (TS) \
290+ V (HMAC) \
291+ V (CT) \
292+ V (ASYNC) \
293+ V (KDF) \
294+ V (SM2) \
295+ V (USER) \
296+
297+ #define V (name ) case ERR_LIB_##name: lib = #name " _" ; break ;
298+ const char * lib = " " ;
299+ const char * prefix = " OSSL_" ;
300+ switch (ERR_GET_LIB (err)) { OSSL_ERROR_CODES_MAP (V) }
301+ #undef V
302+ #undef OSSL_ERROR_CODES_MAP
303+ // Don't generate codes like "ERR_OSSL_SSL_".
304+ if (lib && strcmp (lib, " SSL_" ) == 0 )
305+ prefix = " " ;
306+
307+ // All OpenSSL reason strings fit in a single 80-column macro definition,
308+ // all prefix lengths are <= 10, and ERR_OSSL_ is 9, so 128 is more than
309+ // sufficient.
310+ char code[128 ];
311+ snprintf (code, sizeof (code), " ERR_%s%s%s" , prefix, lib, reason.c_str ());
312+
313+ if (obj->Set (env->isolate ()->GetCurrentContext (),
314+ env->code_string (),
315+ OneByteString (env->isolate (), code)).IsNothing ())
316+ return ;
317+ }
318+ }
319+ } // namespace error
320+
321+
215322struct CryptoErrorVector : public std ::vector<std::string> {
216323 inline void Capture () {
217324 clear ();
@@ -258,6 +365,8 @@ struct CryptoErrorVector : public std::vector<std::string> {
258365
259366void ThrowCryptoError (Environment* env,
260367 unsigned long err, // NOLINT(runtime/int)
368+ // Default, only used if there is no SSL `err` which can
369+ // be used to create a long-style message string.
261370 const char * message = nullptr ) {
262371 char message_buffer[128 ] = {0 };
263372 if (err != 0 || message == nullptr ) {
@@ -270,7 +379,11 @@ void ThrowCryptoError(Environment* env,
270379 .ToLocalChecked ();
271380 CryptoErrorVector errors;
272381 errors.Capture ();
273- auto exception = errors.ToException (env, exception_string);
382+ Local<Value> exception = errors.ToException (env, exception_string);
383+ Local<Object> obj;
384+ if (!exception->ToObject (env->context ()).ToLocal (&obj))
385+ return ;
386+ error::Decorate (env, obj, err);
274387 env->isolate ()->ThrowException (exception);
275388}
276389
0 commit comments