@@ -1788,6 +1788,313 @@ std::unique_ptr<worker::TransferData> NativeKeyObject::CloneForMessaging()
17881788 return std::make_unique<KeyObjectTransferData>(handle_data_);
17891789}
17901790
1791+ void NativeCryptoKey::Initialize (Environment* env, Local<Object> target) {
1792+ SetMethod (env->context (),
1793+ target,
1794+ " createCryptoKeyClass" ,
1795+ NativeCryptoKey::CreateCryptoKeyClass);
1796+ SetMethod (env->context (),
1797+ target,
1798+ " getCryptoKeyHandle" ,
1799+ NativeCryptoKey::GetKeyHandle);
1800+ SetMethod (
1801+ env->context (), target, " getCryptoKeySlots" , NativeCryptoKey::GetSlots);
1802+ }
1803+
1804+ void NativeCryptoKey::RegisterExternalReferences (
1805+ ExternalReferenceRegistry* registry) {
1806+ registry->Register (NativeCryptoKey::CreateCryptoKeyClass);
1807+ registry->Register (NativeCryptoKey::GetKeyHandle);
1808+ registry->Register (NativeCryptoKey::GetSlots);
1809+ registry->Register (NativeCryptoKey::New);
1810+ }
1811+
1812+ namespace {
1813+ // Brand check: every NativeCryptoKey stores this pointer in its
1814+ // kClassTagField slot. Nothing else in the binary can produce the
1815+ // same pointer, so HasInstance() can use it to recognize a real
1816+ // NativeCryptoKey.
1817+ constexpr int kNativeCryptoKeyClassTag = 0 ;
1818+ const void * class_tag () {
1819+ return &kNativeCryptoKeyClassTag ;
1820+ }
1821+ } // namespace
1822+
1823+ bool NativeCryptoKey::HasInstance (Local<Value> value) {
1824+ if (!value->IsObject ()) return false ;
1825+ Local<Object> obj = value.As <Object>();
1826+ if (obj->InternalFieldCount () < NativeCryptoKey::kInternalFieldCount ) {
1827+ return false ;
1828+ }
1829+ return obj->GetAlignedPointerFromInternalField (
1830+ NativeCryptoKey::kClassTagField , EmbedderDataTag::kDefault ) ==
1831+ class_tag ();
1832+ }
1833+
1834+ void NativeCryptoKey::New (const FunctionCallbackInfo<Value>& args) {
1835+ Environment* env = Environment::GetCurrent (args);
1836+ CHECK_EQ (args.Length (), 4 );
1837+ // args[0] is a KeyObjectHandle; we keep its KeyObjectData directly.
1838+ // args[1] is the algorithm dictionary object.
1839+ // args[2] is the usages array of strings.
1840+ // args[3] is the extractable boolean.
1841+ //
1842+ // args[1] is undefined only when called from
1843+ // CryptoKeyTransferData::Deserialize for a partially-initialized
1844+ // CryptoKey: algorithm/usages/extractable get filled in afterwards
1845+ // by FinalizeTransferRead before any JS can see the object.
1846+ //
1847+ // This constructor is not exposed to user JS - the public CryptoKey
1848+ // class throws from its constructor and InternalCryptoKey is kept
1849+ // in a module-closure.
1850+ CHECK (KeyObjectHandle::HasInstance (env, args[0 ]));
1851+ KeyObjectHandle* handle = Unwrap<KeyObjectHandle>(args[0 ].As <Object>());
1852+ CHECK_NOT_NULL (handle);
1853+
1854+ auto * native = new NativeCryptoKey (env, args.This (), handle->Data ());
1855+
1856+ // Brand-check tag for HasInstance().
1857+ args.This ()->SetAlignedPointerInInternalField (kClassTagField ,
1858+ const_cast <void *>(class_tag ()),
1859+ EmbedderDataTag::kDefault );
1860+
1861+ if (!args[1 ]->IsUndefined ()) {
1862+ CHECK (args[1 ]->IsObject ());
1863+ CHECK (args[2 ]->IsArray ());
1864+ CHECK (args[3 ]->IsBoolean ());
1865+ native->algorithm_ .Reset (env->isolate (), args[1 ].As <Object>());
1866+ native->usages_ .Reset (env->isolate (), args[2 ].As <Array>());
1867+ native->extractable_ = args[3 ]->IsTrue ();
1868+ }
1869+ }
1870+
1871+ void NativeCryptoKey::CreateCryptoKeyClass (
1872+ const FunctionCallbackInfo<Value>& args) {
1873+ Environment* env = Environment::GetCurrent (args);
1874+ Isolate* isolate = env->isolate ();
1875+
1876+ CHECK_EQ (args.Length (), 1 );
1877+ Local<Value> callback = args[0 ];
1878+ CHECK (callback->IsFunction ());
1879+
1880+ Local<FunctionTemplate> t =
1881+ NewFunctionTemplate (isolate, NativeCryptoKey::New);
1882+ t->InstanceTemplate ()->SetInternalFieldCount (
1883+ NativeCryptoKey::kInternalFieldCount );
1884+
1885+ Local<Value> ctor;
1886+ if (!t->GetFunction (env->context ()).ToLocal (&ctor)) return ;
1887+
1888+ Local<Value> recv = Undefined (env->isolate ());
1889+ Local<Value> ret_v;
1890+ if (!callback.As <Function>()
1891+ ->Call (env->context (), recv, 1 , &ctor)
1892+ .ToLocal (&ret_v)) {
1893+ return ;
1894+ }
1895+ Local<Array> ret = ret_v.As <Array>();
1896+ Local<Value> internal_ctor_v;
1897+ if (!ret->Get (env->context (), 1 ).ToLocal (&internal_ctor_v)) return ;
1898+ env->set_crypto_internal_cryptokey_constructor (
1899+ internal_ctor_v.As <Function>());
1900+ args.GetReturnValue ().Set (ret);
1901+ }
1902+
1903+ void NativeCryptoKey::GetKeyHandle (const FunctionCallbackInfo<Value>& args) {
1904+ Environment* env = Environment::GetCurrent (args);
1905+ CHECK_EQ (args.Length (), 1 );
1906+ CHECK (HasInstance (args[0 ]));
1907+ NativeCryptoKey* native = Unwrap<NativeCryptoKey>(args[0 ].As <Object>());
1908+ Local<Object> handle;
1909+ if (!KeyObjectHandle::Create (env, native->handle_data_ ).ToLocal (&handle)) {
1910+ return ;
1911+ }
1912+ args.GetReturnValue ().Set (handle);
1913+ }
1914+
1915+ // Returns all of the key's internal slot values as a single Array:
1916+ // [type, extractable, algorithm, usages, handle]. JS-side helpers
1917+ // call this once per key to prime a per-instance cache, so subsequent
1918+ // reads don't need to cross into C++ at all.
1919+ void NativeCryptoKey::GetSlots (const FunctionCallbackInfo<Value>& args) {
1920+ Environment* env = Environment::GetCurrent (args);
1921+ CHECK_EQ (args.Length (), 1 );
1922+ if (!HasInstance (args[0 ])) {
1923+ THROW_ERR_INVALID_THIS (env, " Value of \" this\" must be of type CryptoKey" );
1924+ return ;
1925+ }
1926+ Isolate* isolate = env->isolate ();
1927+ NativeCryptoKey* native = Unwrap<NativeCryptoKey>(args[0 ].As <Object>());
1928+
1929+ const char * type_str;
1930+ switch (native->handle_data_ .GetKeyType ()) {
1931+ case kKeyTypeSecret :
1932+ type_str = " secret" ;
1933+ break ;
1934+ case kKeyTypePublic :
1935+ type_str = " public" ;
1936+ break ;
1937+ case kKeyTypePrivate :
1938+ type_str = " private" ;
1939+ break ;
1940+ default :
1941+ UNREACHABLE ();
1942+ }
1943+
1944+ Local<Object> handle;
1945+ if (!KeyObjectHandle::Create (env, native->handle_data_ ).ToLocal (&handle)) {
1946+ return ;
1947+ }
1948+
1949+ CHECK (!native->algorithm_ .IsEmpty ());
1950+ CHECK (!native->usages_ .IsEmpty ());
1951+ Local<Value> slots[] = {
1952+ OneByteString (isolate, type_str),
1953+ v8::Boolean::New (isolate, native->extractable_ ),
1954+ PersistentToLocal::Strong (native->algorithm_ ),
1955+ PersistentToLocal::Strong (native->usages_ ),
1956+ handle,
1957+ };
1958+ args.GetReturnValue ().Set (Array::New (isolate, slots, arraysize (slots)));
1959+ }
1960+
1961+ BaseObject::TransferMode NativeCryptoKey::GetTransferMode () const {
1962+ return BaseObject::TransferMode::kCloneable ;
1963+ }
1964+
1965+ std::unique_ptr<worker::TransferData> NativeCryptoKey::CloneForMessaging ()
1966+ const {
1967+ Isolate* isolate = env ()->isolate ();
1968+ CHECK (!algorithm_.IsEmpty ());
1969+ CHECK (!usages_.IsEmpty ());
1970+ v8::Global<Object> algorithm_copy (isolate,
1971+ PersistentToLocal::Strong (algorithm_));
1972+ v8::Global<Array> usages_copy (isolate, PersistentToLocal::Strong (usages_));
1973+ return std::make_unique<CryptoKeyTransferData>(handle_data_,
1974+ std::move (algorithm_copy),
1975+ std::move (usages_copy),
1976+ extractable_);
1977+ }
1978+
1979+ Maybe<void > NativeCryptoKey::FinalizeTransferRead (
1980+ Local<Context> context, v8::ValueDeserializer* deserializer) {
1981+ Local<Value> bundle_v;
1982+ if (!deserializer->ReadValue (context).ToLocal (&bundle_v)) {
1983+ return Nothing<void >();
1984+ }
1985+ CHECK (bundle_v->IsObject ());
1986+ Local<Object> bundle = bundle_v.As <Object>();
1987+ Isolate* isolate = env ()->isolate ();
1988+
1989+ Local<Value> algorithm_v;
1990+ if (!bundle->Get (context, FIXED_ONE_BYTE_STRING (isolate, " algorithm" ))
1991+ .ToLocal (&algorithm_v)) {
1992+ return Nothing<void >();
1993+ }
1994+ CHECK (algorithm_v->IsObject ());
1995+ algorithm_.Reset (isolate, algorithm_v.As <Object>());
1996+
1997+ Local<Value> usages_v;
1998+ if (!bundle->Get (context, FIXED_ONE_BYTE_STRING (isolate, " usages" ))
1999+ .ToLocal (&usages_v)) {
2000+ return Nothing<void >();
2001+ }
2002+ CHECK (usages_v->IsArray ());
2003+ usages_.Reset (isolate, usages_v.As <Array>());
2004+
2005+ Local<Value> extractable_v;
2006+ if (!bundle->Get (context, FIXED_ONE_BYTE_STRING (isolate, " extractable" ))
2007+ .ToLocal (&extractable_v)) {
2008+ return Nothing<void >();
2009+ }
2010+ CHECK (extractable_v->IsBoolean ());
2011+ extractable_ = extractable_v->IsTrue ();
2012+
2013+ return v8::JustVoid ();
2014+ }
2015+
2016+ Maybe<bool > NativeCryptoKey::CryptoKeyTransferData::FinalizeTransferWrite (
2017+ Local<Context> context, v8::ValueSerializer* serializer) {
2018+ Isolate* isolate = Isolate::GetCurrent ();
2019+ CHECK (!algorithm_.IsEmpty ());
2020+ CHECK (!usages_.IsEmpty ());
2021+ Local<Object> bundle = Object::New (isolate);
2022+ Local<Value> algorithm_v = PersistentToLocal::Strong (algorithm_);
2023+ Local<Value> usages_v = PersistentToLocal::Strong (usages_);
2024+ if (bundle
2025+ ->Set (
2026+ context, FIXED_ONE_BYTE_STRING (isolate, " algorithm" ), algorithm_v)
2027+ .IsNothing () ||
2028+ bundle->Set (context, FIXED_ONE_BYTE_STRING (isolate, " usages" ), usages_v)
2029+ .IsNothing () ||
2030+ bundle
2031+ ->Set (context,
2032+ FIXED_ONE_BYTE_STRING (isolate, " extractable" ),
2033+ v8::Boolean::New (isolate, extractable_))
2034+ .IsNothing ()) {
2035+ return Nothing<bool >();
2036+ }
2037+ auto ret = serializer->WriteValue (context, bundle);
2038+ algorithm_.Reset ();
2039+ usages_.Reset ();
2040+ return ret;
2041+ }
2042+
2043+ BaseObjectPtr<BaseObject> NativeCryptoKey::CryptoKeyTransferData::Deserialize (
2044+ Environment* env,
2045+ Local<Context> context,
2046+ std::unique_ptr<worker::TransferData> self) {
2047+ if (context != env->context ()) {
2048+ THROW_ERR_MESSAGE_TARGET_CONTEXT_UNAVAILABLE (env);
2049+ return {};
2050+ }
2051+
2052+ // Reconstruct the KeyObjectHandle for the transferred KeyObjectData.
2053+ Local<Object> handle;
2054+ if (!KeyObjectHandle::Create (env, data_).ToLocal (&handle)) return {};
2055+
2056+ // Make sure internal/crypto/keys has been loaded so that the
2057+ // CryptoKey constructor is registered with the Environment.
2058+ Local<Value> arg =
2059+ FIXED_ONE_BYTE_STRING (env->isolate (), " internal/crypto/keys" );
2060+ if (env->builtin_module_require ()
2061+ ->Call (context, Null (env->isolate ()), 1 , &arg)
2062+ .IsEmpty ()) {
2063+ return {};
2064+ }
2065+
2066+ // Construct a partially-initialized InternalCryptoKey; algorithm,
2067+ // usages and extractable are filled in via FinalizeTransferRead.
2068+ Local<Function> cryptokey_ctor = env->crypto_internal_cryptokey_constructor ();
2069+ CHECK (!cryptokey_ctor.IsEmpty ());
2070+ Local<Value> ctor_args[] = {
2071+ handle,
2072+ Undefined (env->isolate ()),
2073+ Undefined (env->isolate ()),
2074+ Undefined (env->isolate ()),
2075+ };
2076+ Local<Value> cryptokey;
2077+ if (!cryptokey_ctor->NewInstance (context, 4 , ctor_args).ToLocal (&cryptokey)) {
2078+ return {};
2079+ }
2080+
2081+ return BaseObjectPtr<BaseObject>(
2082+ Unwrap<NativeCryptoKey>(cryptokey.As <Object>()));
2083+ }
2084+
2085+ void NativeCryptoKey::MemoryInfo (MemoryTracker* tracker) const {
2086+ tracker->TrackField (" handle_data" , handle_data_);
2087+ tracker->TrackField (" algorithm" , algorithm_);
2088+ tracker->TrackField (" usages" , usages_);
2089+ }
2090+
2091+ void NativeCryptoKey::CryptoKeyTransferData::MemoryInfo (
2092+ MemoryTracker* tracker) const {
2093+ tracker->TrackField (" data" , data_);
2094+ tracker->TrackField (" algorithm" , algorithm_);
2095+ tracker->TrackField (" usages" , usages_);
2096+ }
2097+
17912098namespace Keys {
17922099void Initialize (Environment* env, Local<Object> target) {
17932100 target->Set (env->context (),
0 commit comments