|
4 | 4 |
|
5 | 5 | #include "bindings/global_callbacks.h" |
6 | 6 |
|
| 7 | +#include <iomanip> |
7 | 8 | #include <openssl/bio.h> |
8 | 9 | #include <openssl/evp.h> |
| 10 | +#include <openssl/hmac.h> |
| 11 | +#include <openssl/pem.h> |
| 12 | +#include <openssl/rsa.h> |
9 | 13 |
|
10 | 14 | #include "base/file_path.h" |
11 | 15 | #include "base/file_search.h" |
@@ -368,6 +372,50 @@ void HighResolutionTimeCallback(const v8::FunctionCallbackInfo<v8::Value>& argum |
368 | 372 | arguments.GetReturnValue().Set(global->HighResolutionTime()); |
369 | 373 | } |
370 | 374 |
|
| 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 | + |
371 | 419 | // bool isPlayerMinimized(playerId [, currentTime]); |
372 | 420 | void IsPlayerMinimizedCallback(const v8::FunctionCallbackInfo<v8::Value>& arguments) { |
373 | 421 | auto runtime = Runtime::FromIsolate(arguments.GetIsolate()); |
@@ -524,6 +572,74 @@ void KillServerCallback(const v8::FunctionCallbackInfo<v8::Value>& arguments) { |
524 | 572 | #endif |
525 | 573 | } |
526 | 574 |
|
| 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 | + |
527 | 643 | // void startTrace(); |
528 | 644 | void StartTraceCallback(const v8::FunctionCallbackInfo<v8::Value>& arguments) { |
529 | 645 | LOG(INFO) << "[TraceManager] Started capturing traces."; |
@@ -560,6 +676,73 @@ void ToggleMemoryLoggingCallback(const v8::FunctionCallbackInfo<v8::Value>& argu |
560 | 676 | base::g_debugMemoryAllocations = !base::g_debugMemoryAllocations; |
561 | 677 | } |
562 | 678 |
|
| 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 | + |
563 | 746 | // Promise<void> wait(unsigned long time); |
564 | 747 | void WaitCallback(const v8::FunctionCallbackInfo<v8::Value>& arguments) { |
565 | 748 | std::shared_ptr<Runtime> runtime = Runtime::FromIsolate(arguments.GetIsolate()); |
|
0 commit comments