Skip to content

Commit 2d18eea

Browse files
committed
Add support for loading and verifying signed Wasm modules.
Signed-off-by: Piotr Sikora <[email protected]>
1 parent 1d4bdb9 commit 2d18eea

File tree

1 file changed

+92
-0
lines changed

1 file changed

+92
-0
lines changed

src/v8/v8.cc

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515

1616
#include "include/proxy-wasm/v8.h"
1717

18+
#include <array>
1819
#include <cassert>
1920
#include <iomanip>
2021
#include <memory>
@@ -25,11 +26,39 @@
2526
#include <utility>
2627
#include <vector>
2728

29+
#include <openssl/curve25519.h>
30+
#include <openssl/sha.h>
31+
2832
#include "v8.h"
2933
#include "v8-version.h"
3034
#include "wasm-api/wasm.hh"
3135

3236
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+
3362
namespace {
3463

3564
wasm::Engine *engine() {
@@ -94,6 +123,7 @@ class V8 : public WasmVm {
94123
#undef _GET_MODULE_FUNCTION
95124

96125
private:
126+
bool verifySignature();
97127
void buildFunctionNameIndex();
98128
wasm::vec<byte_t> getStrippedSource();
99129
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) {
279309
source_ = wasm::vec<byte_t>::make_uninitialized(code.size());
280310
::memcpy(source_.get(), code.data(), code.size());
281311

312+
verifySignature();
313+
282314
if (allow_precompiled) {
283315
const auto section_name = getPrecompiledSectionName();
284316
if (!section_name.empty()) {
@@ -312,6 +344,66 @@ bool V8::load(const std::string &code, bool allow_precompiled) {
312344
return module_ != nullptr;
313345
}
314346

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+
315407
void V8::buildFunctionNameIndex() {
316408
// build function index -> function name map for backtrace
317409
// https://webassembly.github.io/spec/core/appendix/custom.html#binary-namesubsection

0 commit comments

Comments
 (0)