|
15 | 15 |
|
16 | 16 | #include "include/proxy-wasm/v8.h"
|
17 | 17 |
|
| 18 | +#include <array> |
18 | 19 | #include <cassert>
|
19 | 20 | #include <iomanip>
|
20 | 21 | #include <memory>
|
|
25 | 26 | #include <utility>
|
26 | 27 | #include <vector>
|
27 | 28 |
|
| 29 | +#include <openssl/curve25519.h> |
| 30 | +#include <openssl/sha.h> |
| 31 | + |
28 | 32 | #include "v8.h"
|
29 | 33 | #include "v8-version.h"
|
30 | 34 | #include "wasm-api/wasm.hh"
|
31 | 35 |
|
32 | 36 | namespace proxy_wasm {
|
| 37 | + |
| 38 | +#ifdef PROXY_WASM_VERIFY_WITH_ED25519_PUBKEY |
| 39 | + |
| 40 | +static uint8_t hex2dec(const unsigned char c) { |
| 41 | + if (c >= '0' && c <= '9') { |
| 42 | + return c - '0'; |
| 43 | + } else if (c >= 'a' && c <= 'f') { |
| 44 | + return c - 'a' + 10; |
| 45 | + } else if (c >= 'A' && c <= 'F') { |
| 46 | + return c - 'A' + 10; |
| 47 | + } else { |
| 48 | + throw std::logic_error{"invalid hex character"}; |
| 49 | + } |
| 50 | +} |
| 51 | + |
| 52 | +template <size_t N> constexpr std::array<uint8_t, N> hex2pubkey(const char (&hex)[2 * N + 1]) { |
| 53 | + std::array<uint8_t, N> pubkey; |
| 54 | + for (size_t i = 0; i < pubkey.size(); i++) { |
| 55 | + pubkey[i] = hex2dec(hex[2 * i]) << 4 | hex2dec(hex[2 * i + 1]); |
| 56 | + } |
| 57 | + return pubkey; |
| 58 | +} |
| 59 | + |
| 60 | +#endif |
| 61 | + |
33 | 62 | namespace {
|
34 | 63 |
|
35 | 64 | wasm::Engine *engine() {
|
@@ -94,6 +123,7 @@ class V8 : public WasmVm {
|
94 | 123 | #undef _GET_MODULE_FUNCTION
|
95 | 124 |
|
96 | 125 | private:
|
| 126 | + bool verifySignature(); |
97 | 127 | void buildFunctionNameIndex();
|
98 | 128 | wasm::vec<byte_t> getStrippedSource();
|
99 | 129 | std::string getFailMessage(std::string_view function_name, wasm::own<wasm::Trap> trap);
|
@@ -279,6 +309,8 @@ bool V8::load(const std::string &code, bool allow_precompiled) {
|
279 | 309 | source_ = wasm::vec<byte_t>::make_uninitialized(code.size());
|
280 | 310 | ::memcpy(source_.get(), code.data(), code.size());
|
281 | 311 |
|
| 312 | + verifySignature(); |
| 313 | + |
282 | 314 | if (allow_precompiled) {
|
283 | 315 | const auto section_name = getPrecompiledSectionName();
|
284 | 316 | if (!section_name.empty()) {
|
@@ -312,6 +344,66 @@ bool V8::load(const std::string &code, bool allow_precompiled) {
|
312 | 344 | return module_ != nullptr;
|
313 | 345 | }
|
314 | 346 |
|
| 347 | +bool V8::verifySignature() { |
| 348 | +#ifdef PROXY_WASM_VERIFY_WITH_ED25519_PUBKEY |
| 349 | + /* |
| 350 | + * Ed25519 signature generated using https://github.com/jedisct1/wasmsign |
| 351 | + */ |
| 352 | + |
| 353 | + auto payload = getCustomSection("signature_wasmsign"); |
| 354 | + if (payload.empty()) { |
| 355 | + fail(FailState::UnableToInitializeCode, "Custom Section \"signature_wasmsign\" not found"); |
| 356 | + return false; |
| 357 | + } |
| 358 | + |
| 359 | + if (source_.get() + source_.size() != payload.data() + payload.size()) { |
| 360 | + fail(FailState::UnableToInitializeCode, |
| 361 | + "Custom Section \"signature_wasmsign\" not at the end of Wasm module"); |
| 362 | + return false; |
| 363 | + } |
| 364 | + |
| 365 | + if (payload.size() != 68) { |
| 366 | + fail(FailState::UnableToInitializeCode, |
| 367 | + "Custom Section \"signature_wasmsign\" contains paylod of a wrong size " |
| 368 | + "(want: 68, is: " + |
| 369 | + std::to_string(payload.size()) + ")"); |
| 370 | + return false; |
| 371 | + } |
| 372 | + |
| 373 | + uint32_t alg_id; |
| 374 | + std::memcpy(&alg_id, payload.data(), sizeof(uint32_t)); |
| 375 | + |
| 376 | + if (alg_id != 2) { |
| 377 | + fail(FailState::UnableToInitializeCode, |
| 378 | + "Signature has a wrong alg_id (want: 2, is: " + std::to_string(alg_id) + ")"); |
| 379 | + return false; |
| 380 | + } |
| 381 | + |
| 382 | + const auto *signature = reinterpret_cast<const uint8_t *>(payload.data()) + sizeof(uint32_t); |
| 383 | + |
| 384 | + SHA512_CTX ctx; |
| 385 | + SHA512_Init(&ctx); |
| 386 | + SHA512_Update(&ctx, "WasmSignature", sizeof("WasmSignature") - 1); |
| 387 | + const uint32_t ad_len = 0; |
| 388 | + SHA512_Update(&ctx, &ad_len, sizeof(uint32_t)); |
| 389 | + const size_t section_len = 3 + sizeof("signature_wasmsign") - 1 + 68; |
| 390 | + SHA512_Update(&ctx, source_.get(), source_.size() - section_len); |
| 391 | + uint8_t hash[SHA512_DIGEST_LENGTH]; |
| 392 | + SHA512_Final(hash, &ctx); |
| 393 | + |
| 394 | + static const auto ed25519_pubkey = hex2pubkey<32>(PROXY_WASM_VERIFY_WITH_ED25519_PUBKEY); |
| 395 | + |
| 396 | + if (!ED25519_verify(hash, sizeof(hash), signature, ed25519_pubkey.data())) { |
| 397 | + fail(FailState::UnableToInitializeCode, "Signature mismatch"); |
| 398 | + return false; |
| 399 | + } |
| 400 | + |
| 401 | + integration()->trace("Wasm signature OK (Ed25519)"); |
| 402 | +#endif |
| 403 | + |
| 404 | + return true; |
| 405 | +} |
| 406 | + |
315 | 407 | void V8::buildFunctionNameIndex() {
|
316 | 408 | // build function index -> function name map for backtrace
|
317 | 409 | // https://webassembly.github.io/spec/core/appendix/custom.html#binary-namesubsection
|
|
0 commit comments