in src/apps/js_v8/tmpl/ccf_global.cpp [433:594]
static void js_wrap_key(const v8::FunctionCallbackInfo<v8::Value>& info)
{
v8::Isolate* isolate = info.GetIsolate();
v8::Local<v8::Context> context = isolate->GetCurrentContext();
if (info.Length() != 3)
{
v8_util::throw_type_error(
isolate,
fmt::format("Passed {} arguments, but expected 3", info.Length()));
return;
}
// API loosely modeled after
// https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/wrapKey.
v8::Local<v8::Value> arg_key = info[0];
if (!arg_key->IsArrayBuffer())
{
v8_util::throw_type_error(isolate, "Argument 1 must be an ArrayBuffer");
return;
}
auto key = v8_util::get_array_buffer_data(arg_key.As<v8::ArrayBuffer>());
v8::Local<v8::Value> arg_wrapping_key = info[1];
if (!arg_wrapping_key->IsArrayBuffer())
{
v8_util::throw_type_error(isolate, "Argument 2 must be an ArrayBuffer");
return;
}
auto wrapping_key =
v8_util::get_array_buffer_data(arg_wrapping_key.As<v8::ArrayBuffer>());
v8::Local<v8::Value> arg_parameters = info[2];
if (!arg_parameters->IsObject())
{
v8_util::throw_type_error(isolate, "Argument 3 must be an object");
return;
}
v8::Local<v8::Object> parameters_obj = arg_parameters.As<v8::Object>();
v8::Local<v8::Value> algo_name_val;
if (
!parameters_obj->Get(context, v8_util::to_v8_istr(isolate, "name"))
.ToLocal(&algo_name_val) ||
!algo_name_val->IsString())
{
v8_util::throw_type_error(
isolate, "Argument 3 must have a 'name' property that is a string");
return;
}
auto algo_name = v8_util::to_str(isolate, algo_name_val.As<v8::String>());
try
{
if (algo_name == "RSA-OAEP")
{
// key can in principle be arbitrary data (see note on maximum size
// in rsa_key_pair.h). wrapping_key is a public RSA key.
v8::Local<v8::Value> label_val;
Buffer label;
if (parameters_obj->Get(context, v8_util::to_v8_istr(isolate, "label"))
.ToLocal(&label_val))
{
if (!label_val->IsArrayBuffer())
{
v8_util::throw_type_error(
isolate,
"'label' property of argument 3, if existing, must be an "
"ArrayBuffer");
return;
}
label =
v8_util::get_array_buffer_data(label_val.As<v8::ArrayBuffer>());
}
std::optional<std::vector<uint8_t>> label_opt = std::nullopt;
if (label.n > 0)
{
label_opt = {label.p, label.p + label.n};
}
auto wrapped_key = crypto::ckm_rsa_pkcs_oaep_wrap(
crypto::Pem(wrapping_key), {key.p, key.p + key.n}, label_opt);
info.GetReturnValue().Set(v8_util::to_v8_array_buffer_copy(
isolate, wrapped_key.data(), wrapped_key.size()));
return;
}
else if (algo_name == "AES-KWP")
{
std::vector<uint8_t> wrapped_key = crypto::ckm_aes_key_wrap_pad(
{wrapping_key.p, wrapping_key.p + wrapping_key.n},
{key.p, key.p + key.n});
info.GetReturnValue().Set(v8_util::to_v8_array_buffer_copy(
isolate, wrapped_key.data(), wrapped_key.size()));
return;
}
else if (algo_name == "RSA-OAEP-AES-KWP")
{
v8::Local<v8::Value> aes_key_size_val;
int32_t aes_key_size;
if (
!parameters_obj
->Get(context, v8_util::to_v8_istr(isolate, "aesKeySize"))
.ToLocal(&aes_key_size_val) ||
!aes_key_size_val->Int32Value(context).To(&aes_key_size))
{
v8_util::throw_type_error(
isolate,
"Argument 3 must have an 'aesKeySize' property that is a number");
return;
}
v8::Local<v8::Value> label_val;
Buffer label;
if (parameters_obj->Get(context, v8_util::to_v8_istr(isolate, "label"))
.ToLocal(&label_val))
{
if (!label_val->IsArrayBuffer())
{
v8_util::throw_type_error(
isolate,
"'label' property of argument 3, if existing, must be an "
"ArrayBuffer");
return;
}
label =
v8_util::get_array_buffer_data(label_val.As<v8::ArrayBuffer>());
}
std::optional<std::vector<uint8_t>> label_opt = std::nullopt;
if (label.n > 0)
{
label_opt = {label.p, label.p + label.n};
}
auto wrapped_key = crypto::ckm_rsa_aes_key_wrap(
aes_key_size,
crypto::Pem(wrapping_key),
{key.p, key.p + key.n},
label_opt);
info.GetReturnValue().Set(v8_util::to_v8_array_buffer_copy(
isolate, wrapped_key.data(), wrapped_key.size()));
}
else
{
v8_util::throw_range_error(
isolate,
"Argument 3 must have a 'name' property that is one of 'RSA-OAEP', "
"'AES-KWP', or 'RSA-OAEP-AES-KWP'");
return;
}
}
catch (std::exception& ex)
{
v8_util::throw_range_error(isolate, ex.what());
}
}