1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
5  * You can obtain one at http://mozilla.org/MPL/2.0/. */
6 
7 #include "nsIIdentityCryptoService.h"
8 #include "nsServiceManagerUtils.h"
9 #include "nsIThread.h"
10 #include "nsThreadUtils.h"
11 #include "nsCOMPtr.h"
12 #include "nsProxyRelease.h"
13 #include "nsString.h"
14 #include "mozilla/ArrayUtils.h"  // ArrayLength
15 #include "mozilla/Base64.h"
16 #include "mozilla/Components.h"
17 #include "ScopedNSSTypes.h"
18 #include "NSSErrorsService.h"
19 
20 #include "nss.h"
21 #include "pk11pub.h"
22 #include "secmod.h"
23 #include "secerr.h"
24 #include "keyhi.h"
25 #include "cryptohi.h"
26 
27 #include <limits.h>
28 
29 using namespace mozilla;
30 
31 namespace {
32 
HexEncode(const SECItem * it,nsACString & result)33 void HexEncode(const SECItem* it, nsACString& result) {
34   static const char digits[] = "0123456789ABCDEF";
35   result.SetLength(it->len * 2);
36   char* p = result.BeginWriting();
37   for (unsigned int i = 0; i < it->len; ++i) {
38     *p++ = digits[it->data[i] >> 4];
39     *p++ = digits[it->data[i] & 0x0f];
40   }
41 }
42 
43 #define DSA_KEY_TYPE_STRING (NS_LITERAL_CSTRING("DS160"))
44 #define RSA_KEY_TYPE_STRING (NS_LITERAL_CSTRING("RS256"))
45 
46 class KeyPair : public nsIIdentityKeyPair {
47  public:
48   NS_DECL_THREADSAFE_ISUPPORTS
49   NS_DECL_NSIIDENTITYKEYPAIR
50 
51   KeyPair(SECKEYPrivateKey* aPrivateKey, SECKEYPublicKey* aPublicKey,
52           nsIEventTarget* aOperationThread);
53 
54  private:
~KeyPair()55   virtual ~KeyPair() {
56     if (mPrivateKey) {
57       SECKEY_DestroyPrivateKey(mPrivateKey);
58     }
59     if (mPublicKey) {
60       SECKEY_DestroyPublicKey(mPublicKey);
61     }
62   }
63 
64   SECKEYPrivateKey* mPrivateKey;
65   SECKEYPublicKey* mPublicKey;
66   nsCOMPtr<nsIEventTarget> mThread;
67 
68   KeyPair(const KeyPair&) = delete;
69   void operator=(const KeyPair&) = delete;
70 };
71 
72 NS_IMPL_ISUPPORTS(KeyPair, nsIIdentityKeyPair)
73 
74 class KeyGenRunnable : public Runnable {
75  public:
76   NS_DECL_NSIRUNNABLE
77 
78   KeyGenRunnable(KeyType keyType, nsIIdentityKeyGenCallback* aCallback,
79                  nsIEventTarget* aOperationThread);
80 
81  private:
82   const KeyType mKeyType;                                      // in
83   nsMainThreadPtrHandle<nsIIdentityKeyGenCallback> mCallback;  // in
84   nsresult mRv;                                                // out
85   nsCOMPtr<nsIIdentityKeyPair> mKeyPair;                       // out
86   nsCOMPtr<nsIEventTarget> mThread;
87 
88   KeyGenRunnable(const KeyGenRunnable&) = delete;
89   void operator=(const KeyGenRunnable&) = delete;
90 };
91 
92 class SignRunnable : public Runnable {
93  public:
94   NS_DECL_NSIRUNNABLE
95 
96   SignRunnable(const nsACString& textToSign, SECKEYPrivateKey* privateKey,
97                nsIIdentitySignCallback* aCallback);
98 
99  private:
~SignRunnable()100   ~SignRunnable() override {
101     if (mPrivateKey) {
102       SECKEY_DestroyPrivateKey(mPrivateKey);
103     }
104   }
105 
106   const nsCString mTextToSign;                               // in
107   SECKEYPrivateKey* mPrivateKey;                             // in
108   nsMainThreadPtrHandle<nsIIdentitySignCallback> mCallback;  // in
109   nsresult mRv;                                              // out
110   nsCString mSignature;                                      // out
111 
112   SignRunnable(const SignRunnable&) = delete;
113   void operator=(const SignRunnable&) = delete;
114 };
115 
116 class IdentityCryptoService final : public nsIIdentityCryptoService {
117  public:
118   NS_DECL_THREADSAFE_ISUPPORTS
119   NS_DECL_NSIIDENTITYCRYPTOSERVICE
120 
121   IdentityCryptoService() = default;
Init()122   nsresult Init() {
123     nsresult rv;
124     nsCOMPtr<nsISupports> dummyUsedToEnsureNSSIsInitialized =
125         do_GetService("@mozilla.org/psm;1", &rv);
126     NS_ENSURE_SUCCESS(rv, rv);
127 
128     nsCOMPtr<nsIThread> thread;
129     rv = NS_NewNamedThread("IdentityCrypto", getter_AddRefs(thread));
130     NS_ENSURE_SUCCESS(rv, rv);
131 
132     mThread = std::move(thread);
133 
134     return NS_OK;
135   }
136 
137  private:
138   ~IdentityCryptoService() = default;
139   IdentityCryptoService(const KeyPair&) = delete;
140   void operator=(const IdentityCryptoService&) = delete;
141 
142   nsCOMPtr<nsIEventTarget> mThread;
143 };
144 
NS_IMPL_ISUPPORTS(IdentityCryptoService,nsIIdentityCryptoService)145 NS_IMPL_ISUPPORTS(IdentityCryptoService, nsIIdentityCryptoService)
146 
147 NS_IMETHODIMP
148 IdentityCryptoService::GenerateKeyPair(const nsACString& keyTypeString,
149                                        nsIIdentityKeyGenCallback* callback) {
150   KeyType keyType;
151   if (keyTypeString.Equals(RSA_KEY_TYPE_STRING)) {
152     keyType = rsaKey;
153   } else if (keyTypeString.Equals(DSA_KEY_TYPE_STRING)) {
154     keyType = dsaKey;
155   } else {
156     return NS_ERROR_UNEXPECTED;
157   }
158 
159   nsCOMPtr<nsIRunnable> r = new KeyGenRunnable(keyType, callback, mThread);
160   nsresult rv = mThread->Dispatch(r.forget(), NS_DISPATCH_NORMAL);
161   NS_ENSURE_SUCCESS(rv, rv);
162 
163   return NS_OK;
164 }
165 
166 NS_IMETHODIMP
Base64UrlEncode(const nsACString & utf8Input,nsACString & result)167 IdentityCryptoService::Base64UrlEncode(const nsACString& utf8Input,
168                                        nsACString& result) {
169   return Base64URLEncode(
170       utf8Input.Length(),
171       reinterpret_cast<const uint8_t*>(utf8Input.BeginReading()),
172       Base64URLEncodePaddingPolicy::Include, result);
173 }
174 
KeyPair(SECKEYPrivateKey * privateKey,SECKEYPublicKey * publicKey,nsIEventTarget * operationThread)175 KeyPair::KeyPair(SECKEYPrivateKey* privateKey, SECKEYPublicKey* publicKey,
176                  nsIEventTarget* operationThread)
177     : mPrivateKey(privateKey), mPublicKey(publicKey), mThread(operationThread) {
178   MOZ_ASSERT(!NS_IsMainThread());
179 }
180 
181 NS_IMETHODIMP
GetHexRSAPublicKeyExponent(nsACString & result)182 KeyPair::GetHexRSAPublicKeyExponent(nsACString& result) {
183   MOZ_ASSERT(NS_IsMainThread());
184   NS_ENSURE_TRUE(mPublicKey, NS_ERROR_NOT_AVAILABLE);
185   NS_ENSURE_TRUE(mPublicKey->keyType == rsaKey, NS_ERROR_NOT_AVAILABLE);
186   HexEncode(&mPublicKey->u.rsa.publicExponent, result);
187   return NS_OK;
188 }
189 
190 NS_IMETHODIMP
GetHexRSAPublicKeyModulus(nsACString & result)191 KeyPair::GetHexRSAPublicKeyModulus(nsACString& result) {
192   MOZ_ASSERT(NS_IsMainThread());
193   NS_ENSURE_TRUE(mPublicKey, NS_ERROR_NOT_AVAILABLE);
194   NS_ENSURE_TRUE(mPublicKey->keyType == rsaKey, NS_ERROR_NOT_AVAILABLE);
195   HexEncode(&mPublicKey->u.rsa.modulus, result);
196   return NS_OK;
197 }
198 
199 NS_IMETHODIMP
GetHexDSAPrime(nsACString & result)200 KeyPair::GetHexDSAPrime(nsACString& result) {
201   MOZ_ASSERT(NS_IsMainThread());
202   NS_ENSURE_TRUE(mPublicKey, NS_ERROR_NOT_AVAILABLE);
203   NS_ENSURE_TRUE(mPublicKey->keyType == dsaKey, NS_ERROR_NOT_AVAILABLE);
204   HexEncode(&mPublicKey->u.dsa.params.prime, result);
205   return NS_OK;
206 }
207 
208 NS_IMETHODIMP
GetHexDSASubPrime(nsACString & result)209 KeyPair::GetHexDSASubPrime(nsACString& result) {
210   MOZ_ASSERT(NS_IsMainThread());
211   NS_ENSURE_TRUE(mPublicKey, NS_ERROR_NOT_AVAILABLE);
212   NS_ENSURE_TRUE(mPublicKey->keyType == dsaKey, NS_ERROR_NOT_AVAILABLE);
213   HexEncode(&mPublicKey->u.dsa.params.subPrime, result);
214   return NS_OK;
215 }
216 
217 NS_IMETHODIMP
GetHexDSAGenerator(nsACString & result)218 KeyPair::GetHexDSAGenerator(nsACString& result) {
219   MOZ_ASSERT(NS_IsMainThread());
220   NS_ENSURE_TRUE(mPublicKey, NS_ERROR_NOT_AVAILABLE);
221   NS_ENSURE_TRUE(mPublicKey->keyType == dsaKey, NS_ERROR_NOT_AVAILABLE);
222   HexEncode(&mPublicKey->u.dsa.params.base, result);
223   return NS_OK;
224 }
225 
226 NS_IMETHODIMP
GetHexDSAPublicValue(nsACString & result)227 KeyPair::GetHexDSAPublicValue(nsACString& result) {
228   MOZ_ASSERT(NS_IsMainThread());
229   NS_ENSURE_TRUE(mPublicKey, NS_ERROR_NOT_AVAILABLE);
230   NS_ENSURE_TRUE(mPublicKey->keyType == dsaKey, NS_ERROR_NOT_AVAILABLE);
231   HexEncode(&mPublicKey->u.dsa.publicValue, result);
232   return NS_OK;
233 }
234 
235 NS_IMETHODIMP
GetKeyType(nsACString & result)236 KeyPair::GetKeyType(nsACString& result) {
237   MOZ_ASSERT(NS_IsMainThread());
238   NS_ENSURE_TRUE(mPublicKey, NS_ERROR_NOT_AVAILABLE);
239 
240   switch (mPublicKey->keyType) {
241     case rsaKey:
242       result = RSA_KEY_TYPE_STRING;
243       return NS_OK;
244     case dsaKey:
245       result = DSA_KEY_TYPE_STRING;
246       return NS_OK;
247     default:
248       return NS_ERROR_UNEXPECTED;
249   }
250 }
251 
252 NS_IMETHODIMP
Sign(const nsACString & textToSign,nsIIdentitySignCallback * callback)253 KeyPair::Sign(const nsACString& textToSign, nsIIdentitySignCallback* callback) {
254   MOZ_ASSERT(NS_IsMainThread());
255   nsCOMPtr<nsIRunnable> r = new SignRunnable(textToSign, mPrivateKey, callback);
256 
257   return mThread->Dispatch(r, NS_DISPATCH_NORMAL);
258 }
259 
KeyGenRunnable(KeyType keyType,nsIIdentityKeyGenCallback * callback,nsIEventTarget * operationThread)260 KeyGenRunnable::KeyGenRunnable(KeyType keyType,
261                                nsIIdentityKeyGenCallback* callback,
262                                nsIEventTarget* operationThread)
263     : mozilla::Runnable("KeyGenRunnable"),
264       mKeyType(keyType),
265       mCallback(new nsMainThreadPtrHolder<nsIIdentityKeyGenCallback>(
266           "KeyGenRunnable::mCallback", callback)),
267       mRv(NS_ERROR_NOT_INITIALIZED),
268       mThread(operationThread) {}
269 
GenerateKeyPair(PK11SlotInfo * slot,SECKEYPrivateKey ** privateKey,SECKEYPublicKey ** publicKey,CK_MECHANISM_TYPE mechanism,void * params)270 MOZ_MUST_USE nsresult GenerateKeyPair(PK11SlotInfo* slot,
271                                       SECKEYPrivateKey** privateKey,
272                                       SECKEYPublicKey** publicKey,
273                                       CK_MECHANISM_TYPE mechanism,
274                                       void* params) {
275   *publicKey = nullptr;
276   *privateKey = PK11_GenerateKeyPair(
277       slot, mechanism, params, publicKey, PR_FALSE /*isPerm*/,
278       PR_TRUE /*isSensitive*/, nullptr /*&pwdata*/);
279   if (!*privateKey) {
280     MOZ_ASSERT(!*publicKey);
281     return mozilla::psm::GetXPCOMFromNSSError(PR_GetError());
282   }
283   if (!*publicKey) {
284     SECKEY_DestroyPrivateKey(*privateKey);
285     *privateKey = nullptr;
286     MOZ_CRASH("PK11_GnerateKeyPair returned private key without public key");
287   }
288 
289   return NS_OK;
290 }
291 
GenerateRSAKeyPair(PK11SlotInfo * slot,SECKEYPrivateKey ** privateKey,SECKEYPublicKey ** publicKey)292 MOZ_MUST_USE nsresult GenerateRSAKeyPair(PK11SlotInfo* slot,
293                                          SECKEYPrivateKey** privateKey,
294                                          SECKEYPublicKey** publicKey) {
295   MOZ_ASSERT(!NS_IsMainThread());
296 
297   PK11RSAGenParams rsaParams;
298   rsaParams.keySizeInBits = 2048;
299   rsaParams.pe = 0x10001;
300   return GenerateKeyPair(slot, privateKey, publicKey, CKM_RSA_PKCS_KEY_PAIR_GEN,
301                          &rsaParams);
302 }
303 
GenerateDSAKeyPair(PK11SlotInfo * slot,SECKEYPrivateKey ** privateKey,SECKEYPublicKey ** publicKey)304 MOZ_MUST_USE nsresult GenerateDSAKeyPair(PK11SlotInfo* slot,
305                                          SECKEYPrivateKey** privateKey,
306                                          SECKEYPublicKey** publicKey) {
307   MOZ_ASSERT(!NS_IsMainThread());
308 
309   // XXX: These could probably be static const arrays, but this way we avoid
310   // compiler warnings and also we avoid having to worry much about whether the
311   // functions that take these inputs will (unexpectedly) modify them.
312 
313   // Using NIST parameters. Some other BrowserID components require that these
314   // exact parameters are used.
315   uint8_t P[] = {
316       0xFF, 0x60, 0x04, 0x83, 0xDB, 0x6A, 0xBF, 0xC5, 0xB4, 0x5E, 0xAB, 0x78,
317       0x59, 0x4B, 0x35, 0x33, 0xD5, 0x50, 0xD9, 0xF1, 0xBF, 0x2A, 0x99, 0x2A,
318       0x7A, 0x8D, 0xAA, 0x6D, 0xC3, 0x4F, 0x80, 0x45, 0xAD, 0x4E, 0x6E, 0x0C,
319       0x42, 0x9D, 0x33, 0x4E, 0xEE, 0xAA, 0xEF, 0xD7, 0xE2, 0x3D, 0x48, 0x10,
320       0xBE, 0x00, 0xE4, 0xCC, 0x14, 0x92, 0xCB, 0xA3, 0x25, 0xBA, 0x81, 0xFF,
321       0x2D, 0x5A, 0x5B, 0x30, 0x5A, 0x8D, 0x17, 0xEB, 0x3B, 0xF4, 0xA0, 0x6A,
322       0x34, 0x9D, 0x39, 0x2E, 0x00, 0xD3, 0x29, 0x74, 0x4A, 0x51, 0x79, 0x38,
323       0x03, 0x44, 0xE8, 0x2A, 0x18, 0xC4, 0x79, 0x33, 0x43, 0x8F, 0x89, 0x1E,
324       0x22, 0xAE, 0xEF, 0x81, 0x2D, 0x69, 0xC8, 0xF7, 0x5E, 0x32, 0x6C, 0xB7,
325       0x0E, 0xA0, 0x00, 0xC3, 0xF7, 0x76, 0xDF, 0xDB, 0xD6, 0x04, 0x63, 0x8C,
326       0x2E, 0xF7, 0x17, 0xFC, 0x26, 0xD0, 0x2E, 0x17};
327 
328   uint8_t Q[] = {0xE2, 0x1E, 0x04, 0xF9, 0x11, 0xD1, 0xED, 0x79, 0x91, 0x00,
329                  0x8E, 0xCA, 0xAB, 0x3B, 0xF7, 0x75, 0x98, 0x43, 0x09, 0xC3};
330 
331   uint8_t G[] = {
332       0xC5, 0x2A, 0x4A, 0x0F, 0xF3, 0xB7, 0xE6, 0x1F, 0xDF, 0x18, 0x67, 0xCE,
333       0x84, 0x13, 0x83, 0x69, 0xA6, 0x15, 0x4F, 0x4A, 0xFA, 0x92, 0x96, 0x6E,
334       0x3C, 0x82, 0x7E, 0x25, 0xCF, 0xA6, 0xCF, 0x50, 0x8B, 0x90, 0xE5, 0xDE,
335       0x41, 0x9E, 0x13, 0x37, 0xE0, 0x7A, 0x2E, 0x9E, 0x2A, 0x3C, 0xD5, 0xDE,
336       0xA7, 0x04, 0xD1, 0x75, 0xF8, 0xEB, 0xF6, 0xAF, 0x39, 0x7D, 0x69, 0xE1,
337       0x10, 0xB9, 0x6A, 0xFB, 0x17, 0xC7, 0xA0, 0x32, 0x59, 0x32, 0x9E, 0x48,
338       0x29, 0xB0, 0xD0, 0x3B, 0xBC, 0x78, 0x96, 0xB1, 0x5B, 0x4A, 0xDE, 0x53,
339       0xE1, 0x30, 0x85, 0x8C, 0xC3, 0x4D, 0x96, 0x26, 0x9A, 0xA8, 0x90, 0x41,
340       0xF4, 0x09, 0x13, 0x6C, 0x72, 0x42, 0xA3, 0x88, 0x95, 0xC9, 0xD5, 0xBC,
341       0xCA, 0xD4, 0xF3, 0x89, 0xAF, 0x1D, 0x7A, 0x4B, 0xD1, 0x39, 0x8B, 0xD0,
342       0x72, 0xDF, 0xFA, 0x89, 0x62, 0x33, 0x39, 0x7A};
343 
344   static_assert(MOZ_ARRAY_LENGTH(P) == 1024 / CHAR_BIT, "bad DSA P");
345   static_assert(MOZ_ARRAY_LENGTH(Q) == 160 / CHAR_BIT, "bad DSA Q");
346   static_assert(MOZ_ARRAY_LENGTH(G) == 1024 / CHAR_BIT, "bad DSA G");
347 
348   PQGParams pqgParams = {
349       nullptr /*arena*/,
350       {siBuffer, P, static_cast<unsigned int>(mozilla::ArrayLength(P))},
351       {siBuffer, Q, static_cast<unsigned int>(mozilla::ArrayLength(Q))},
352       {siBuffer, G, static_cast<unsigned int>(mozilla::ArrayLength(G))}};
353 
354   return GenerateKeyPair(slot, privateKey, publicKey, CKM_DSA_KEY_PAIR_GEN,
355                          &pqgParams);
356 }
357 
358 NS_IMETHODIMP
Run()359 KeyGenRunnable::Run() {
360   if (!NS_IsMainThread()) {
361     // We always want to use the internal slot for BrowserID; in particular,
362     // we want to avoid smartcard slots.
363     PK11SlotInfo* slot = PK11_GetInternalSlot();
364     if (!slot) {
365       mRv = NS_ERROR_UNEXPECTED;
366     } else {
367       SECKEYPrivateKey* privk = nullptr;
368       SECKEYPublicKey* pubk = nullptr;
369 
370       switch (mKeyType) {
371         case rsaKey:
372           mRv = GenerateRSAKeyPair(slot, &privk, &pubk);
373           break;
374         case dsaKey:
375           mRv = GenerateDSAKeyPair(slot, &privk, &pubk);
376           break;
377         default:
378           MOZ_CRASH("unknown key type");
379       }
380 
381       PK11_FreeSlot(slot);
382 
383       if (NS_SUCCEEDED(mRv)) {
384         MOZ_ASSERT(privk);
385         MOZ_ASSERT(pubk);
386         // mKeyPair will take over ownership of privk and pubk
387         mKeyPair = new KeyPair(privk, pubk, mThread);
388       }
389     }
390 
391     NS_DispatchToMainThread(this);
392   } else {
393     // Back on Main Thread
394     (void)mCallback->GenerateKeyPairFinished(mRv, mKeyPair);
395   }
396   return NS_OK;
397 }
398 
SignRunnable(const nsACString & aText,SECKEYPrivateKey * privateKey,nsIIdentitySignCallback * aCallback)399 SignRunnable::SignRunnable(const nsACString& aText,
400                            SECKEYPrivateKey* privateKey,
401                            nsIIdentitySignCallback* aCallback)
402     : mozilla::Runnable("SignRunnable"),
403       mTextToSign(aText),
404       mPrivateKey(SECKEY_CopyPrivateKey(privateKey)),
405       mCallback(new nsMainThreadPtrHolder<nsIIdentitySignCallback>(
406           "SignRunnable::mCallback", aCallback)),
407       mRv(NS_ERROR_NOT_INITIALIZED) {}
408 
409 NS_IMETHODIMP
Run()410 SignRunnable::Run() {
411   if (!NS_IsMainThread()) {
412     // We need the output in PKCS#11 format, not DER encoding, so we must use
413     // PK11_HashBuf and PK11_Sign instead of SEC_SignData.
414 
415     SECItem sig = {siBuffer, nullptr, 0};
416     int sigLength = PK11_SignatureLen(mPrivateKey);
417     if (sigLength <= 0) {
418       mRv = mozilla::psm::GetXPCOMFromNSSError(PR_GetError());
419     } else if (!SECITEM_AllocItem(nullptr, &sig, sigLength)) {
420       mRv = mozilla::psm::GetXPCOMFromNSSError(PR_GetError());
421     } else {
422       uint8_t hash[32];  // big enough for SHA-1 or SHA-256
423       SECOidTag hashAlg =
424           mPrivateKey->keyType == dsaKey ? SEC_OID_SHA1 : SEC_OID_SHA256;
425       SECItem hashItem = {siBuffer, hash, hashAlg == SEC_OID_SHA1 ? 20u : 32u};
426 
427       mRv = MapSECStatus(
428           PK11_HashBuf(hashAlg, hash,
429                        const_cast<uint8_t*>(
430                            reinterpret_cast<const uint8_t*>(mTextToSign.get())),
431                        mTextToSign.Length()));
432       if (NS_SUCCEEDED(mRv)) {
433         mRv = MapSECStatus(PK11_Sign(mPrivateKey, &sig, &hashItem));
434       }
435       if (NS_SUCCEEDED(mRv)) {
436         mRv =
437             Base64URLEncode(sig.len, sig.data,
438                             Base64URLEncodePaddingPolicy::Include, mSignature);
439       }
440       SECITEM_FreeItem(&sig, false);
441     }
442 
443     NS_DispatchToMainThread(this);
444   } else {
445     // Back on Main Thread
446     (void)mCallback->SignFinished(mRv, mSignature);
447   }
448 
449   return NS_OK;
450 }
451 }  // unnamed namespace
452 
453 // XPCOM module registration
454 
NS_IMPL_COMPONENT_FACTORY(nsIIdentityCryptoService)455 NS_IMPL_COMPONENT_FACTORY(nsIIdentityCryptoService) {
456   auto inst = MakeRefPtr<IdentityCryptoService>();
457   if (NS_SUCCEEDED(inst->Init())) {
458     return inst.forget().downcast<nsIIdentityCryptoService>();
459   }
460   return nullptr;
461 }
462