#include "crypto/crypto_sig.h" #include "async_wrap-inl.h" #include "base_object-inl.h" #include "crypto/crypto_ec.h" #include "crypto/crypto_keys.h" #include "crypto/crypto_util.h" #include "env-inl.h" #include "memory_tracker-inl.h" #include "openssl/ec.h" #include "threadpoolwork-inl.h" #include "v8.h" namespace node { using ncrypto::BignumPointer; using ncrypto::ClearErrorOnReturn; using ncrypto::DataPointer; using ncrypto::Digest; using ncrypto::ECDSASigPointer; using ncrypto::EVPKeyCtxPointer; using ncrypto::EVPKeyPointer; using ncrypto::EVPMDCtxPointer; using v8::ArrayBuffer; using v8::BackingStore; using v8::BackingStoreInitializationMode; using v8::Boolean; using v8::FunctionCallbackInfo; using v8::FunctionTemplate; using v8::HandleScope; using v8::Int32; using v8::Isolate; using v8::JustVoid; using v8::Local; using v8::Maybe; using v8::MaybeLocal; using v8::Nothing; using v8::Object; using v8::Uint32; using v8::Value; namespace crypto { namespace { int GetPaddingFromJS(const EVPKeyPointer& key, Local val) { int padding = key.getDefaultSignPadding(); if (!val->IsUndefined()) [[likely]] { CHECK(val->IsInt32()); padding = val.As()->Value(); } return padding; } std::optional GetSaltLenFromJS(Local val) { std::optional salt_len; if (!val->IsUndefined()) [[likely]] { CHECK(val->IsInt32()); salt_len = val.As()->Value(); } return salt_len; } DSASigEnc GetDSASigEncFromJS(Local val) { CHECK(val->IsInt32()); int i = val.As()->Value(); if (i < 0 || i >= static_cast(DSASigEnc::Invalid)) [[unlikely]] { return DSASigEnc::Invalid; } return static_cast(val.As()->Value()); } bool ApplyRSAOptions(const EVPKeyPointer& pkey, EVP_PKEY_CTX* pkctx, int padding, std::optional salt_len) { if (pkey.isRsaVariant()) { return EVPKeyCtxPointer::setRsaPadding(pkctx, padding, salt_len); } return true; } std::unique_ptr Node_SignFinal(Environment* env, EVPMDCtxPointer&& mdctx, const EVPKeyPointer& pkey, int padding, std::optional pss_salt_len) { auto data = mdctx.digestFinal(mdctx.getExpectedSize()); if (!data) [[unlikely]] return nullptr; auto sig = ArrayBuffer::NewBackingStore(env->isolate(), pkey.size()); ncrypto::Buffer sig_buf{ .data = static_cast(sig->Data()), .len = pkey.size(), }; EVPKeyCtxPointer pkctx = pkey.newCtx(); if (pkctx.initForSign() > 0 && ApplyRSAOptions(pkey, pkctx.get(), padding, pss_salt_len) && pkctx.setSignatureMd(mdctx) && pkctx.signInto(data, &sig_buf)) [[likely]] { CHECK_LE(sig_buf.len, sig->ByteLength()); if (sig_buf.len < sig->ByteLength()) { auto new_sig = ArrayBuffer::NewBackingStore( env->isolate(), sig_buf.len, BackingStoreInitializationMode::kUninitialized); if (sig_buf.len > 0) [[likely]] { memcpy(new_sig->Data(), sig->Data(), sig_buf.len); } sig = std::move(new_sig); } return sig; } return nullptr; } // Returns the maximum size of each of the integers (r, s) of the DSA signature. std::unique_ptr ConvertSignatureToP1363( Environment* env, const EVPKeyPointer& pkey, std::unique_ptr&& signature) { uint32_t n = pkey.getBytesOfRS().value_or(kNoDsaSignature); if (n == kNoDsaSignature) return std::move(signature); auto buf = ArrayBuffer::NewBackingStore( env->isolate(), 2 * n, BackingStoreInitializationMode::kUninitialized); ncrypto::Buffer sig_buffer{ .data = static_cast(signature->Data()), .len = signature->ByteLength(), }; if (!ncrypto::extractP1363( sig_buffer, static_cast(buf->Data()), n)) { return std::move(signature); } return buf; } // Returns the maximum size of each of the integers (r, s) of the DSA signature. ByteSource ConvertSignatureToP1363(Environment* env, const EVPKeyPointer& pkey, const ByteSource& signature) { unsigned int n = pkey.getBytesOfRS().value_or(kNoDsaSignature); if (n == kNoDsaSignature) [[unlikely]] return {}; auto data = DataPointer::Alloc(n * 2); if (!data) [[unlikely]] return {}; unsigned char* out = static_cast(data.get()); // Extracting the signature may not actually use all of the allocated space. // We need to ensure that the buffer is zeroed out before use. data.zero(); if (!ncrypto::extractP1363(signature, out, n)) [[unlikely]] { return {}; } return ByteSource::Allocated(data.release()); } ByteSource ConvertSignatureToDER(const EVPKeyPointer& pkey, ByteSource&& out) { unsigned int n = pkey.getBytesOfRS().value_or(kNoDsaSignature); if (n == kNoDsaSignature) return std::move(out); const unsigned char* sig_data = out.data(); if (out.size() != 2 * n) return {}; auto asn1_sig = ECDSASigPointer::New(); CHECK(asn1_sig); BignumPointer r(sig_data, n); CHECK(r); BignumPointer s(sig_data + n, n); CHECK(s); CHECK(asn1_sig.setParams(std::move(r), std::move(s))); auto buf = asn1_sig.encode(); if (buf.len <= 0) [[unlikely]] return {}; CHECK_NOT_NULL(buf.data); return ByteSource::Allocated(buf); } void CheckThrow(Environment* env, SignBase::Error error) { HandleScope scope(env->isolate()); switch (error) { case SignBase::Error::UnknownDigest: return THROW_ERR_CRYPTO_INVALID_DIGEST(env); case SignBase::Error::NotInitialised: return THROW_ERR_CRYPTO_INVALID_STATE(env, "Not initialised"); case SignBase::Error::MalformedSignature: return THROW_ERR_CRYPTO_OPERATION_FAILED(env, "Malformed signature"); case SignBase::Error::ContextUnsupported: return THROW_ERR_CRYPTO_OPERATION_FAILED( env, "Context parameter is unsupported"); case SignBase::Error::Init: case SignBase::Error::Update: case SignBase::Error::PrivateKey: case SignBase::Error::PublicKey: { unsigned long err = ERR_get_error(); // NOLINT(runtime/int) if (err) return ThrowCryptoError(env, err); switch (error) { case SignBase::Error::Init: return THROW_ERR_CRYPTO_OPERATION_FAILED(env, "EVP_SignInit_ex failed"); case SignBase::Error::Update: return THROW_ERR_CRYPTO_OPERATION_FAILED(env, "EVP_SignUpdate failed"); case SignBase::Error::PrivateKey: return THROW_ERR_CRYPTO_OPERATION_FAILED( env, "PEM_read_bio_PrivateKey failed"); case SignBase::Error::PublicKey: return THROW_ERR_CRYPTO_OPERATION_FAILED( env, "PEM_read_bio_PUBKEY failed"); default: ABORT(); } } case SignBase::Error::Ok: return; } } bool UseP1363Encoding(const EVPKeyPointer& key, const DSASigEnc dsa_encoding) { return key.isSigVariant() && dsa_encoding == DSASigEnc::P1363; } bool SupportsContextString(const EVPKeyPointer& key) { #if OPENSSL_VERSION_NUMBER < 0x3020000fL return false; #else switch (key.id()) { case EVP_PKEY_ED448: #if OPENSSL_WITH_PQC case EVP_PKEY_ML_DSA_44: case EVP_PKEY_ML_DSA_65: case EVP_PKEY_ML_DSA_87: case EVP_PKEY_SLH_DSA_SHA2_128F: case EVP_PKEY_SLH_DSA_SHA2_128S: case EVP_PKEY_SLH_DSA_SHA2_192F: case EVP_PKEY_SLH_DSA_SHA2_192S: case EVP_PKEY_SLH_DSA_SHA2_256F: case EVP_PKEY_SLH_DSA_SHA2_256S: case EVP_PKEY_SLH_DSA_SHAKE_128F: case EVP_PKEY_SLH_DSA_SHAKE_128S: case EVP_PKEY_SLH_DSA_SHAKE_192F: case EVP_PKEY_SLH_DSA_SHAKE_192S: case EVP_PKEY_SLH_DSA_SHAKE_256F: case EVP_PKEY_SLH_DSA_SHAKE_256S: #endif return true; default: return false; } #endif } } // namespace SignBase::Error SignBase::Init(const char* digest) { CHECK_NULL(mdctx_); auto md = Digest::FromName(digest); if (!md) [[unlikely]] return Error::UnknownDigest; mdctx_ = EVPMDCtxPointer::New(); if (!mdctx_.digestInit(md)) [[unlikely]] { mdctx_.reset(); return Error::Init; } return Error::Ok; } SignBase::Error SignBase::Update(const char* data, size_t len) { if (mdctx_ == nullptr) [[unlikely]] return Error::NotInitialised; ncrypto::Buffer buf{ .data = data, .len = len, }; return mdctx_.digestUpdate(buf) ? Error::Ok : Error::Update; } SignBase::SignBase(Environment* env, Local wrap) : BaseObject(env, wrap) { MakeWeak(); } void SignBase::MemoryInfo(MemoryTracker* tracker) const { tracker->TrackFieldWithSize("mdctx", mdctx_ ? kSizeOf_EVP_MD_CTX : 0); } Sign::Sign(Environment* env, Local wrap) : SignBase(env, wrap) {} void Sign::Initialize(Environment* env, Local target) { Isolate* isolate = env->isolate(); Local t = NewFunctionTemplate(isolate, New); t->InstanceTemplate()->SetInternalFieldCount(SignBase::kInternalFieldCount); SetProtoMethod(isolate, t, "init", SignInit); SetProtoMethod(isolate, t, "update", SignUpdate); SetProtoMethod(isolate, t, "sign", SignFinal); SetConstructorFunction(env->context(), target, "Sign", t); SignJob::Initialize(env, target); constexpr int kSignJobModeSign = static_cast(SignConfiguration::Mode::Sign); constexpr int kSignJobModeVerify = static_cast(SignConfiguration::Mode::Verify); constexpr auto kSigEncDER = DSASigEnc::DER; constexpr auto kSigEncP1363 = DSASigEnc::P1363; NODE_DEFINE_CONSTANT(target, kSignJobModeSign); NODE_DEFINE_CONSTANT(target, kSignJobModeVerify); NODE_DEFINE_CONSTANT(target, kSigEncDER); NODE_DEFINE_CONSTANT(target, kSigEncP1363); NODE_DEFINE_CONSTANT(target, RSA_PKCS1_PSS_PADDING); } void Sign::RegisterExternalReferences(ExternalReferenceRegistry* registry) { registry->Register(New); registry->Register(SignInit); registry->Register(SignUpdate); registry->Register(SignFinal); SignJob::RegisterExternalReferences(registry); } void Sign::New(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); new Sign(env, args.This()); } void Sign::SignInit(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); Sign* sign; ASSIGN_OR_RETURN_UNWRAP(&sign, args.This()); const node::Utf8Value sign_type(env->isolate(), args[0]); crypto::CheckThrow(env, sign->Init(*sign_type)); } void Sign::SignUpdate(const FunctionCallbackInfo& args) { Decode(args, [](Sign* sign, const FunctionCallbackInfo& args, const char* data, size_t size) { Environment* env = Environment::GetCurrent(args); if (size > INT_MAX) [[unlikely]] return THROW_ERR_OUT_OF_RANGE(env, "data is too long"); Error err = sign->Update(data, size); crypto::CheckThrow(sign->env(), err); }); } Sign::SignResult Sign::SignFinal(const EVPKeyPointer& pkey, int padding, std::optional salt_len, DSASigEnc dsa_sig_enc) { if (!mdctx_) [[unlikely]] { return SignResult(Error::NotInitialised); } EVPMDCtxPointer mdctx = std::move(mdctx_); if (!pkey.validateDsaParameters()) { return SignResult(Error::PrivateKey); } auto buffer = Node_SignFinal(env(), std::move(mdctx), pkey, padding, salt_len); Error error = buffer ? Error::Ok : Error::PrivateKey; if (error == Error::Ok && dsa_sig_enc == DSASigEnc::P1363) { buffer = ConvertSignatureToP1363(env(), pkey, std::move(buffer)); CHECK_NOT_NULL(buffer->Data()); } return SignResult(error, std::move(buffer)); } void Sign::SignFinal(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); Sign* sign; ASSIGN_OR_RETURN_UNWRAP(&sign, args.This()); ClearErrorOnReturn clear_error_on_return; unsigned int offset = 0; auto data = KeyObjectData::GetPrivateKeyFromJs(args, &offset, true); if (!data) [[unlikely]] return; const auto& key = data.GetAsymmetricKey(); if (!key) [[unlikely]] return; if (key.isOneShotVariant()) [[unlikely]] { THROW_ERR_CRYPTO_UNSUPPORTED_OPERATION(env); return; } int padding = GetPaddingFromJS(key, args[offset]); std::optional salt_len = GetSaltLenFromJS(args[offset + 1]); DSASigEnc dsa_sig_enc = GetDSASigEncFromJS(args[offset + 2]); if (dsa_sig_enc == DSASigEnc::Invalid) [[unlikely]] { THROW_ERR_OUT_OF_RANGE(env, "invalid signature encoding"); return; } SignResult ret = sign->SignFinal(key, padding, salt_len, dsa_sig_enc); if (ret.error != Error::Ok) [[unlikely]] { return crypto::CheckThrow(env, ret.error); } auto ab = ArrayBuffer::New(env->isolate(), std::move(ret.signature)); args.GetReturnValue().Set( Buffer::New(env, ab, 0, ab->ByteLength()).FromMaybe(Local())); } Verify::Verify(Environment* env, Local wrap) : SignBase(env, wrap) {} void Verify::Initialize(Environment* env, Local target) { Isolate* isolate = env->isolate(); Local t = NewFunctionTemplate(isolate, New); t->InstanceTemplate()->SetInternalFieldCount(SignBase::kInternalFieldCount); SetProtoMethod(isolate, t, "init", VerifyInit); SetProtoMethod(isolate, t, "update", VerifyUpdate); SetProtoMethod(isolate, t, "verify", VerifyFinal); SetConstructorFunction(env->context(), target, "Verify", t); } void Verify::RegisterExternalReferences(ExternalReferenceRegistry* registry) { registry->Register(New); registry->Register(VerifyInit); registry->Register(VerifyUpdate); registry->Register(VerifyFinal); } void Verify::New(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); new Verify(env, args.This()); } void Verify::VerifyInit(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); Verify* verify; ASSIGN_OR_RETURN_UNWRAP(&verify, args.This()); const node::Utf8Value verify_type(env->isolate(), args[0]); crypto::CheckThrow(env, verify->Init(*verify_type)); } void Verify::VerifyUpdate(const FunctionCallbackInfo& args) { Decode(args, [](Verify* verify, const FunctionCallbackInfo& args, const char* data, size_t size) { Environment* env = Environment::GetCurrent(args); if (size > INT_MAX) [[unlikely]] { return THROW_ERR_OUT_OF_RANGE(env, "data is too long"); } Error err = verify->Update(data, size); crypto::CheckThrow(verify->env(), err); }); } SignBase::Error Verify::VerifyFinal(const EVPKeyPointer& pkey, const ByteSource& sig, int padding, std::optional saltlen, bool* verify_result) { if (!mdctx_) [[unlikely]] return Error::NotInitialised; *verify_result = false; EVPMDCtxPointer mdctx = std::move(mdctx_); auto data = mdctx.digestFinal(mdctx.getExpectedSize()); if (!data) [[unlikely]] return Error::PublicKey; EVPKeyCtxPointer pkctx = pkey.newCtx(); if (pkctx) [[likely]] { const int init_ret = pkctx.initForVerify(); if (init_ret == -2) [[unlikely]] return Error::PublicKey; if (init_ret > 0 && ApplyRSAOptions(pkey, pkctx.get(), padding, saltlen) && pkctx.setSignatureMd(mdctx)) { *verify_result = pkctx.verify(sig, data); } } return Error::Ok; } void Verify::VerifyFinal(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); ClearErrorOnReturn clear_error_on_return; Verify* verify; ASSIGN_OR_RETURN_UNWRAP(&verify, args.This()); unsigned int offset = 0; auto data = KeyObjectData::GetPublicOrPrivateKeyFromJs(args, &offset); if (!data) [[unlikely]] return; const auto& key = data.GetAsymmetricKey(); if (!key) [[unlikely]] return; if (key.isOneShotVariant()) [[unlikely]] { THROW_ERR_CRYPTO_UNSUPPORTED_OPERATION(env); return; } ArrayBufferOrViewContents hbuf(args[offset]); if (!hbuf.CheckSizeInt32()) [[unlikely]] { return THROW_ERR_OUT_OF_RANGE(env, "buffer is too big"); } int padding = GetPaddingFromJS(key, args[offset + 1]); std::optional salt_len = GetSaltLenFromJS(args[offset + 2]); DSASigEnc dsa_sig_enc = GetDSASigEncFromJS(args[offset + 3]); if (dsa_sig_enc == DSASigEnc::Invalid) [[unlikely]] { THROW_ERR_OUT_OF_RANGE(env, "invalid signature encoding"); return; } ByteSource signature = hbuf.ToByteSource(); if (dsa_sig_enc == DSASigEnc::P1363) { signature = ConvertSignatureToDER(key, hbuf.ToByteSource()); if (signature.data() == nullptr) [[unlikely]] { return crypto::CheckThrow(env, Error::MalformedSignature); } } bool verify_result; Error err = verify->VerifyFinal(key, signature, padding, salt_len, &verify_result); if (err != Error::Ok) [[unlikely]] return crypto::CheckThrow(env, err); args.GetReturnValue().Set(verify_result); } SignConfiguration::SignConfiguration(SignConfiguration&& other) noexcept : job_mode(other.job_mode), mode(other.mode), key(std::move(other.key)), data(std::move(other.data)), signature(std::move(other.signature)), digest(other.digest), flags(other.flags), padding(other.padding), salt_length(other.salt_length), dsa_encoding(other.dsa_encoding), context_string(std::move(other.context_string)) {} SignConfiguration& SignConfiguration::operator=( SignConfiguration&& other) noexcept { if (&other == this) return *this; this->~SignConfiguration(); return *new (this) SignConfiguration(std::move(other)); } void SignConfiguration::MemoryInfo(MemoryTracker* tracker) const { tracker->TrackField("key", key); if (job_mode == kCryptoJobAsync) { tracker->TrackFieldWithSize("data", data.size()); tracker->TrackFieldWithSize("signature", signature.size()); tracker->TrackFieldWithSize("context_string", context_string.size()); } } Maybe SignTraits::AdditionalConfig( CryptoJobMode mode, const FunctionCallbackInfo& args, unsigned int offset, SignConfiguration* params) { ClearErrorOnReturn clear_error_on_return; Environment* env = Environment::GetCurrent(args); params->job_mode = mode; CHECK(args[offset]->IsUint32()); // Sign Mode params->mode = static_cast(args[offset].As()->Value()); unsigned int keyParamOffset = offset + 1; if (params->mode == SignConfiguration::Mode::Verify) { auto data = KeyObjectData::GetPublicOrPrivateKeyFromJs(args, &keyParamOffset); if (!data) return Nothing(); params->key = std::move(data); } else { auto data = KeyObjectData::GetPrivateKeyFromJs(args, &keyParamOffset, true); if (!data) return Nothing(); params->key = std::move(data); } ArrayBufferOrViewContents data(args[offset + 5]); if (!data.CheckSizeInt32()) [[unlikely]] { THROW_ERR_OUT_OF_RANGE(env, "data is too big"); return Nothing(); } params->data = mode == kCryptoJobAsync ? data.ToCopy() : data.ToByteSource(); if (args[offset + 6]->IsString()) { Utf8Value digest(env->isolate(), args[offset + 6]); params->digest = Digest::FromName(*digest); if (!params->digest) [[unlikely]] { THROW_ERR_CRYPTO_INVALID_DIGEST(env, "Invalid digest: %s", digest); return Nothing(); } } if (args[offset + 7]->IsInt32()) { // Salt length params->flags |= SignConfiguration::kHasSaltLength; params->salt_length = GetSaltLenFromJS(args[offset + 7]).value_or(params->salt_length); } if (args[offset + 8]->IsUint32()) { // Padding params->flags |= SignConfiguration::kHasPadding; params->padding = GetPaddingFromJS(params->key.GetAsymmetricKey(), args[offset + 8]); } if (args[offset + 9]->IsUint32()) { // DSA Encoding params->dsa_encoding = GetDSASigEncFromJS(args[offset + 9]); if (params->dsa_encoding == DSASigEnc::Invalid) [[unlikely]] { THROW_ERR_OUT_OF_RANGE(env, "invalid signature encoding"); return Nothing(); } } if (!args[offset + 10]->IsUndefined()) { // Context string ArrayBufferOrViewContents context_string(args[offset + 10]); if (context_string.size() > 255) [[unlikely]] { THROW_ERR_OUT_OF_RANGE(env, "context string must be at most 255 bytes"); return Nothing(); } params->flags |= SignConfiguration::kHasContextString; params->context_string = mode == kCryptoJobAsync ? context_string.ToCopy() : context_string.ToByteSource(); } if (params->mode == SignConfiguration::Mode::Verify) { ArrayBufferOrViewContents signature(args[offset + 11]); if (!signature.CheckSizeInt32()) [[unlikely]] { THROW_ERR_OUT_OF_RANGE(env, "signature is too big"); return Nothing(); } // If this is an EC key (assuming ECDSA) we need to convert the // the signature from WebCrypto format into DER format... Mutex::ScopedLock lock(params->key.mutex()); const auto& akey = params->key.GetAsymmetricKey(); if (UseP1363Encoding(akey, params->dsa_encoding)) { params->signature = ConvertSignatureToDER(akey, signature.ToByteSource()); } else { params->signature = mode == kCryptoJobAsync ? signature.ToCopy() : signature.ToByteSource(); } } return JustVoid(); } bool SignTraits::DeriveBits(Environment* env, const SignConfiguration& params, ByteSource* out, CryptoJobMode mode) { bool can_throw = mode == CryptoJobMode::kCryptoJobSync; auto context = EVPMDCtxPointer::New(); if (!context) [[unlikely]] return false; const auto& key = params.key.GetAsymmetricKey(); bool has_context = (params.flags & SignConfiguration::kHasContextString && params.context_string.size() > 0); if (has_context && !SupportsContextString(key)) { if (can_throw) crypto::CheckThrow(env, SignBase::Error::ContextUnsupported); return false; } auto ctx = ([&] { if (has_context) { ncrypto::Buffer context_buf{ .data = params.context_string.data(), .len = params.context_string.size(), }; switch (params.mode) { case SignConfiguration::Mode::Sign: return context.signInitWithContext(key, params.digest, context_buf); case SignConfiguration::Mode::Verify: return context.verifyInitWithContext(key, params.digest, context_buf); } } else { switch (params.mode) { case SignConfiguration::Mode::Sign: return context.signInit(key, params.digest); case SignConfiguration::Mode::Verify: return context.verifyInit(key, params.digest); } } UNREACHABLE(); })(); if (!ctx.has_value()) [[unlikely]] { if (can_throw) crypto::CheckThrow(env, SignBase::Error::Init); return false; } int padding = params.flags & SignConfiguration::kHasPadding ? params.padding : key.getDefaultSignPadding(); std::optional salt_length = params.flags & SignConfiguration::kHasSaltLength ? std::optional(params.salt_length) : std::nullopt; if (!ApplyRSAOptions(key, *ctx, padding, salt_length)) { if (can_throw) crypto::CheckThrow(env, SignBase::Error::PrivateKey); return false; } switch (params.mode) { case SignConfiguration::Mode::Sign: { if (key.isOneShotVariant()) { auto data = context.signOneShot(params.data); if (!data) [[unlikely]] { if (can_throw) crypto::CheckThrow(env, SignBase::Error::PrivateKey); return false; } DCHECK(!data.isSecure()); *out = ByteSource::Allocated(data.release()); } else { auto data = context.sign(params.data); if (!data) [[unlikely]] { if (can_throw) crypto::CheckThrow(env, SignBase::Error::PrivateKey); return false; } DCHECK(!data.isSecure()); auto bs = ByteSource::Allocated(data.release()); if (UseP1363Encoding(key, params.dsa_encoding)) { *out = ConvertSignatureToP1363(env, key, std::move(bs)); } else { *out = std::move(bs); } } break; } case SignConfiguration::Mode::Verify: { auto buf = DataPointer::Alloc(1); static_cast(buf.get())[0] = 0; if (context.verify(params.data, params.signature)) { static_cast(buf.get())[0] = 1; } *out = ByteSource::Allocated(buf.release()); } } return true; } MaybeLocal SignTraits::EncodeOutput(Environment* env, const SignConfiguration& params, ByteSource* out) { switch (params.mode) { case SignConfiguration::Mode::Sign: return out->ToArrayBuffer(env); case SignConfiguration::Mode::Verify: return Boolean::New(env->isolate(), out->data()[0] == 1); } UNREACHABLE(); } } // namespace crypto } // namespace node