signers.js 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147
  1. const {
  2. AbstractSigner,
  3. Signature,
  4. TypedDataEncoder,
  5. assert,
  6. assertArgument,
  7. concat,
  8. dataLength,
  9. decodeBase64,
  10. getBytes,
  11. getBytesCopy,
  12. hashMessage,
  13. hexlify,
  14. sha256,
  15. toBeHex,
  16. } = require('ethers');
  17. const { secp256r1 } = require('@noble/curves/p256');
  18. const { generateKeyPairSync, privateEncrypt } = require('crypto');
  19. // Lightweight version of BaseWallet
  20. class NonNativeSigner extends AbstractSigner {
  21. #signingKey;
  22. constructor(privateKey, provider) {
  23. super(provider);
  24. assertArgument(
  25. privateKey && typeof privateKey.sign === 'function',
  26. 'invalid private key',
  27. 'privateKey',
  28. '[ REDACTED ]',
  29. );
  30. this.#signingKey = privateKey;
  31. }
  32. get signingKey() {
  33. return this.#signingKey;
  34. }
  35. get privateKey() {
  36. return this.signingKey.privateKey;
  37. }
  38. async getAddress() {
  39. throw new Error("NonNativeSigner doesn't have an address");
  40. }
  41. connect(provider) {
  42. return new NonNativeSigner(this.#signingKey, provider);
  43. }
  44. async signTransaction(/*tx: TransactionRequest*/) {
  45. throw new Error('NonNativeSigner cannot send transactions');
  46. }
  47. async signMessage(message /*: string | Uint8Array*/) /*: Promise<string>*/ {
  48. return this.signingKey.sign(hashMessage(message)).serialized;
  49. }
  50. async signTypedData(
  51. domain /*: TypedDataDomain*/,
  52. types /*: Record<string, Array<TypedDataField>>*/,
  53. value /*: Record<string, any>*/,
  54. ) /*: Promise<string>*/ {
  55. // Populate any ENS names
  56. const populated = await TypedDataEncoder.resolveNames(domain, types, value, async name => {
  57. assert(this.provider != null, 'cannot resolve ENS names without a provider', 'UNSUPPORTED_OPERATION', {
  58. operation: 'resolveName',
  59. info: { name },
  60. });
  61. const address = await this.provider.resolveName(name);
  62. assert(address != null, 'unconfigured ENS name', 'UNCONFIGURED_NAME', { value: name });
  63. return address;
  64. });
  65. return this.signingKey.sign(TypedDataEncoder.hash(populated.domain, types, populated.value)).serialized;
  66. }
  67. }
  68. class P256SigningKey {
  69. #privateKey;
  70. constructor(privateKey) {
  71. this.#privateKey = getBytes(privateKey);
  72. }
  73. static random() {
  74. return new this(secp256r1.utils.randomPrivateKey());
  75. }
  76. get privateKey() {
  77. return hexlify(this.#privateKey);
  78. }
  79. get publicKey() {
  80. const publicKeyBytes = secp256r1.getPublicKey(this.#privateKey, false);
  81. return { qx: hexlify(publicKeyBytes.slice(0x01, 0x21)), qy: hexlify(publicKeyBytes.slice(0x21, 0x41)) };
  82. }
  83. sign(digest /*: BytesLike*/) /*: Signature*/ {
  84. assertArgument(dataLength(digest) === 32, 'invalid digest length', 'digest', digest);
  85. const sig = secp256r1.sign(getBytesCopy(digest), getBytesCopy(this.#privateKey), { lowS: true });
  86. return Signature.from({ r: toBeHex(sig.r, 32), s: toBeHex(sig.s, 32), v: sig.recovery ? 0x1c : 0x1b });
  87. }
  88. }
  89. class RSASigningKey {
  90. #privateKey;
  91. #publicKey;
  92. constructor(keyPair) {
  93. const jwk = keyPair.publicKey.export({ format: 'jwk' });
  94. this.#privateKey = keyPair.privateKey;
  95. this.#publicKey = { e: decodeBase64(jwk.e), n: decodeBase64(jwk.n) };
  96. }
  97. static random(modulusLength = 2048) {
  98. return new this(generateKeyPairSync('rsa', { modulusLength }));
  99. }
  100. get privateKey() {
  101. return hexlify(this.#privateKey);
  102. }
  103. get publicKey() {
  104. return { e: hexlify(this.#publicKey.e), n: hexlify(this.#publicKey.n) };
  105. }
  106. sign(digest /*: BytesLike*/) /*: Signature*/ {
  107. assertArgument(dataLength(digest) === 32, 'invalid digest length', 'digest', digest);
  108. // SHA256 OID = 608648016503040201 (9 bytes) | NULL = 0500 (2 bytes) (explicit) | OCTET_STRING length (0x20) = 0420 (2 bytes)
  109. return {
  110. serialized: hexlify(
  111. privateEncrypt(this.#privateKey, getBytes(concat(['0x3031300d060960864801650304020105000420', digest]))),
  112. ),
  113. };
  114. }
  115. }
  116. class RSASHA256SigningKey extends RSASigningKey {
  117. sign(digest /*: BytesLike*/) /*: Signature*/ {
  118. assertArgument(dataLength(digest) === 32, 'invalid digest length', 'digest', digest);
  119. return super.sign(sha256(getBytes(digest)));
  120. }
  121. }
  122. module.exports = { NonNativeSigner, P256SigningKey, RSASigningKey, RSASHA256SigningKey };