Skip to content

Commit 9375316

Browse files
committed
Add hmac/signMessage/verifyMessage functions
1 parent c951659 commit 9375316

3 files changed

Lines changed: 191 additions & 0 deletions

File tree

src/playground/bindings/global_callbacks.cc

Lines changed: 183 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,12 @@
44

55
#include "bindings/global_callbacks.h"
66

7+
#include <iomanip>
78
#include <openssl/bio.h>
89
#include <openssl/evp.h>
10+
#include <openssl/hmac.h>
11+
#include <openssl/pem.h>
12+
#include <openssl/rsa.h>
913

1014
#include "base/file_path.h"
1115
#include "base/file_search.h"
@@ -368,6 +372,50 @@ void HighResolutionTimeCallback(const v8::FunctionCallbackInfo<v8::Value>& argum
368372
arguments.GetReturnValue().Set(global->HighResolutionTime());
369373
}
370374

375+
// string hmac(string privateKey, string message);
376+
void HmacCallback(const v8::FunctionCallbackInfo<v8::Value>& arguments) {
377+
if (arguments.Length() < 2) {
378+
ThrowException("unable to execute hmac(): 2 argument required, but only " +
379+
std::to_string(arguments.Length()) + " provided.");
380+
return;
381+
}
382+
383+
if (!arguments[0]->IsString()) {
384+
ThrowException("unable to execute hmac(): expected a string for argument 1.");
385+
return;
386+
}
387+
388+
if (!arguments[1]->IsString()) {
389+
ThrowException("unable to execute hmac(): expected a string for argument 2.");
390+
return;
391+
}
392+
393+
const std::string private_key = toString(arguments[0]);
394+
const std::string message = toString(arguments[1]);
395+
396+
unsigned int signature_length = 32;
397+
unsigned char signature[32];
398+
399+
// (1) Compute the signature to apply for the given message.
400+
HMAC_CTX* context = HMAC_CTX_new();
401+
HMAC_Init_ex(context, private_key.c_str(), private_key.size(), EVP_sha256(), nullptr);
402+
HMAC_Update(context, (const unsigned char*)message.c_str(), message.length());
403+
HMAC_Final(context, signature, &signature_length);
404+
HMAC_CTX_free(context);
405+
406+
// (2) Convert the signature to a string that can be encoded.
407+
std::stringstream stream;
408+
stream << std::setfill('0');
409+
410+
for (size_t index = 0; index < signature_length; ++index)
411+
stream << signature[index];
412+
413+
// (3) Encode the string to base64
414+
const std::string encoded_string = Base64Transform(stream.str(), /* encode= */ true);
415+
416+
arguments.GetReturnValue().Set(v8String(encoded_string));
417+
}
418+
371419
// bool isPlayerMinimized(playerId [, currentTime]);
372420
void IsPlayerMinimizedCallback(const v8::FunctionCallbackInfo<v8::Value>& arguments) {
373421
auto runtime = Runtime::FromIsolate(arguments.GetIsolate());
@@ -524,6 +572,74 @@ void KillServerCallback(const v8::FunctionCallbackInfo<v8::Value>& arguments) {
524572
#endif
525573
}
526574

575+
// string signMessage(string privateKey, string plaintext);
576+
void SignMessageCallback(const v8::FunctionCallbackInfo<v8::Value>& arguments) {
577+
if (arguments.Length() < 2) {
578+
ThrowException("unable to execute signMessage(): 2 arguments required, but only " +
579+
std::to_string(arguments.Length()) + " provided.");
580+
return;
581+
}
582+
583+
if (!arguments[0]->IsString()) {
584+
ThrowException("unable to execute signMessage(): expected a string for argument 1.");
585+
return;
586+
}
587+
588+
if (!arguments[1]->IsString()) {
589+
ThrowException("unable to execute signMessage(): expected a string for argument 2.");
590+
return;
591+
}
592+
593+
arguments.GetReturnValue().SetNull();
594+
595+
const std::string private_key = toString(arguments[0]);
596+
const std::string plaintext = toString(arguments[1]);
597+
598+
RSA* cipher = nullptr;
599+
BIO* key = BIO_new_mem_buf((void*)private_key.data(), -1);
600+
601+
if (!PEM_read_bio_RSAPrivateKey(key, &cipher, nullptr, nullptr)) {
602+
ThrowException("unable to execute signMessage(): unable to decode the private key.");
603+
return;
604+
}
605+
606+
EVP_MD_CTX* sign_context = EVP_MD_CTX_create();
607+
608+
EVP_PKEY* pkey = EVP_PKEY_new();
609+
EVP_PKEY_assign_RSA(pkey, cipher);
610+
611+
std::string signature;
612+
size_t signature_length = 0;
613+
614+
// (1) Create the binary signature, and write it to |signature|.
615+
if (EVP_DigestSignInit(sign_context, nullptr, EVP_sha256(), nullptr, pkey) > 0) {
616+
if (EVP_DigestSignUpdate(sign_context, plaintext.data(), plaintext.size()) > 0) {
617+
if (EVP_DigestSignFinal(sign_context, nullptr, &signature_length) > 0) {
618+
signature.resize(signature_length);
619+
if (EVP_DigestSignFinal(sign_context, (unsigned char*)signature.data(), &signature_length) <= 0)
620+
ThrowException("unable to execute signMessage(): unable to write the finalized digest.");
621+
622+
} else {
623+
ThrowException("unable to execute signMessage(): unable to finalize the digest.");
624+
}
625+
} else {
626+
ThrowException("unable to execute signMessage(): unable to update the digest.");
627+
}
628+
} else {
629+
ThrowException("unable to execute signMessage(): unable to initialize the digest.");
630+
}
631+
632+
EVP_MD_CTX_free(sign_context);
633+
634+
// (2) Encode the binary signature with base64.
635+
if (signature.size() > 0) {
636+
const std::string encoded_signature = Base64Transform(signature, /* encode= */ true);
637+
638+
// (3) Return the newly encoded signature as a string.
639+
arguments.GetReturnValue().Set(v8String(encoded_signature));
640+
}
641+
}
642+
527643
// void startTrace();
528644
void StartTraceCallback(const v8::FunctionCallbackInfo<v8::Value>& arguments) {
529645
LOG(INFO) << "[TraceManager] Started capturing traces.";
@@ -560,6 +676,73 @@ void ToggleMemoryLoggingCallback(const v8::FunctionCallbackInfo<v8::Value>& argu
560676
base::g_debugMemoryAllocations = !base::g_debugMemoryAllocations;
561677
}
562678

679+
// bool verifyMessage(string publicKey, string signature, string message);
680+
void VerifyMessageCallback(const v8::FunctionCallbackInfo<v8::Value>& arguments) {
681+
if (arguments.Length() < 3) {
682+
ThrowException("unable to execute verifyMessage(): 3 arguments required, but only " +
683+
std::to_string(arguments.Length()) + " provided.");
684+
return;
685+
}
686+
687+
if (!arguments[0]->IsString()) {
688+
ThrowException("unable to execute verifyMessage(): expected a string for argument 1.");
689+
return;
690+
}
691+
692+
if (!arguments[1]->IsString()) {
693+
ThrowException("unable to execute verifyMessage(): expected a string for argument 2.");
694+
return;
695+
}
696+
697+
if (!arguments[2]->IsString()) {
698+
ThrowException("unable to execute verifyMessage(): expected a string for argument 3.");
699+
return;
700+
}
701+
702+
const std::string public_key = toString(arguments[0]);
703+
const std::string signature = toString(arguments[1]);
704+
const std::string plaintext = toString(arguments[2]);
705+
706+
RSA* cipher = nullptr;
707+
BIO* key = BIO_new_mem_buf((void*)public_key.data(), -1);
708+
709+
if (!PEM_read_bio_RSA_PUBKEY(key, &cipher, nullptr, nullptr)) {
710+
ThrowException("unable to execute verifyMessage(): unable to decode the public key.");
711+
return;
712+
}
713+
714+
EVP_MD_CTX* verify_context = EVP_MD_CTX_create();
715+
716+
EVP_PKEY* pkey = EVP_PKEY_new();
717+
EVP_PKEY_assign_RSA(pkey, cipher);
718+
719+
int status = -1;
720+
721+
// (1) Decode the signature, which is base64 encoded.
722+
const std::string decoded_signature = Base64Transform(signature, /* encode= */ false);
723+
724+
// (2) Verify the signature against the given |plaintext|.
725+
if (EVP_DigestVerifyInit(verify_context, nullptr, EVP_sha256(), nullptr, pkey) > 0) {
726+
if (EVP_DigestVerifyUpdate(verify_context, plaintext.c_str(), plaintext.size()) > 0) {
727+
status = EVP_DigestVerifyFinal(
728+
verify_context, (const unsigned char*)decoded_signature.c_str(), decoded_signature.size());
729+
730+
if (status != /* unauthentic */ 0 && status != 1 /* authentic */)
731+
ThrowException("unable to execute verifyMessage(): unable to finalize the digest.");
732+
733+
} else {
734+
ThrowException("unable to execute verifyMessage(): unable to update the digest.");
735+
}
736+
} else {
737+
ThrowException("unable to execute verifyMessage(): unable to initialize the digest.");
738+
}
739+
740+
EVP_MD_CTX_free(verify_context);
741+
742+
// (3) Return whether the |status| was set to 1 (authenticated).
743+
arguments.GetReturnValue().Set(status == 1);
744+
}
745+
563746
// Promise<void> wait(unsigned long time);
564747
void WaitCallback(const v8::FunctionCallbackInfo<v8::Value>& arguments) {
565748
std::shared_ptr<Runtime> runtime = Runtime::FromIsolate(arguments.GetIsolate());

src/playground/bindings/global_callbacks.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ void GetRuntimeStatisticsCallback(const v8::FunctionCallbackInfo<v8::Value>& arg
2525
void GlobCallback(const v8::FunctionCallbackInfo<v8::Value>& arguments);
2626
void HasEventListenersCallback(const v8::FunctionCallbackInfo<v8::Value>& arguments);
2727
void HighResolutionTimeCallback(const v8::FunctionCallbackInfo<v8::Value>& arguments);
28+
void HmacCallback(const v8::FunctionCallbackInfo<v8::Value>& arguments);
2829
void IsPlayerMinimizedCallback(const v8::FunctionCallbackInfo<v8::Value>& arguments);
2930
void NotifyReadyCallback(const v8::FunctionCallbackInfo<v8::Value>& arguments);
3031
void KillServerCallback(const v8::FunctionCallbackInfo<v8::Value>& arguments);
@@ -33,9 +34,11 @@ void ProvideNativeCallback(const v8::FunctionCallbackInfo<v8::Value>& arguments)
3334
void ReadFileCallback(const v8::FunctionCallbackInfo<v8::Value>& arguments);
3435
void RemoveEventListenerCallback(const v8::FunctionCallbackInfo<v8::Value>& arguments);
3536
void ReportTestsFinishedCallback(const v8::FunctionCallbackInfo<v8::Value>& arguments);
37+
void SignMessageCallback(const v8::FunctionCallbackInfo<v8::Value>& arguments);
3638
void StartTraceCallback(const v8::FunctionCallbackInfo<v8::Value>& arguments);
3739
void StopTraceCallback(const v8::FunctionCallbackInfo<v8::Value>& arguments);
3840
void ToggleMemoryLoggingCallback(const v8::FunctionCallbackInfo<v8::Value>& arguments);
41+
void VerifyMessageCallback(const v8::FunctionCallbackInfo<v8::Value>& arguments);
3942
void WaitCallback(const v8::FunctionCallbackInfo<v8::Value>& arguments);
4043

4144
} // namespace

src/playground/bindings/global_scope.cc

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,11 @@ void GlobalScope::InstallPrototypes(v8::Local<v8::ObjectTemplate> global) {
7272
InstallFunction(global, "atob", Base64DecodeCallback);
7373
InstallFunction(global, "btoa", Base64EncodeCallback);
7474

75+
// JavaScript methods for signing or verifying RSA signatures.
76+
InstallFunction(global, "hmac", HmacCallback);
77+
InstallFunction(global, "signMessage", SignMessageCallback);
78+
InstallFunction(global, "verifyMessage", VerifyMessageCallback);
79+
7580
// Fast-path since idle checks generally are expensive.
7681
InstallFunction(global, "isPlayerMinimized", IsPlayerMinimizedCallback);
7782

0 commit comments

Comments
 (0)