signing-key.js 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166
  1. /**
  2. * Add details about signing here.
  3. *
  4. * @_subsection: api/crypto:Signing [about-signing]
  5. */
  6. import { secp256k1 } from "@noble/curves/secp256k1";
  7. import { concat, dataLength, getBytes, getBytesCopy, hexlify, toBeHex, assertArgument } from "../utils/index.js";
  8. import { Signature } from "./signature.js";
  9. /**
  10. * A **SigningKey** provides high-level access to the elliptic curve
  11. * cryptography (ECC) operations and key management.
  12. */
  13. export class SigningKey {
  14. #privateKey;
  15. /**
  16. * Creates a new **SigningKey** for %%privateKey%%.
  17. */
  18. constructor(privateKey) {
  19. assertArgument(dataLength(privateKey) === 32, "invalid private key", "privateKey", "[REDACTED]");
  20. this.#privateKey = hexlify(privateKey);
  21. }
  22. /**
  23. * The private key.
  24. */
  25. get privateKey() { return this.#privateKey; }
  26. /**
  27. * The uncompressed public key.
  28. *
  29. * This will always begin with the prefix ``0x04`` and be 132
  30. * characters long (the ``0x`` prefix and 130 hexadecimal nibbles).
  31. */
  32. get publicKey() { return SigningKey.computePublicKey(this.#privateKey); }
  33. /**
  34. * The compressed public key.
  35. *
  36. * This will always begin with either the prefix ``0x02`` or ``0x03``
  37. * and be 68 characters long (the ``0x`` prefix and 33 hexadecimal
  38. * nibbles)
  39. */
  40. get compressedPublicKey() { return SigningKey.computePublicKey(this.#privateKey, true); }
  41. /**
  42. * Return the signature of the signed %%digest%%.
  43. */
  44. sign(digest) {
  45. assertArgument(dataLength(digest) === 32, "invalid digest length", "digest", digest);
  46. const sig = secp256k1.sign(getBytesCopy(digest), getBytesCopy(this.#privateKey), {
  47. lowS: true
  48. });
  49. return Signature.from({
  50. r: toBeHex(sig.r, 32),
  51. s: toBeHex(sig.s, 32),
  52. v: (sig.recovery ? 0x1c : 0x1b)
  53. });
  54. }
  55. /**
  56. * Returns the [[link-wiki-ecdh]] shared secret between this
  57. * private key and the %%other%% key.
  58. *
  59. * The %%other%% key may be any type of key, a raw public key,
  60. * a compressed/uncompressed pubic key or aprivate key.
  61. *
  62. * Best practice is usually to use a cryptographic hash on the
  63. * returned value before using it as a symetric secret.
  64. *
  65. * @example:
  66. * sign1 = new SigningKey(id("some-secret-1"))
  67. * sign2 = new SigningKey(id("some-secret-2"))
  68. *
  69. * // Notice that privA.computeSharedSecret(pubB)...
  70. * sign1.computeSharedSecret(sign2.publicKey)
  71. * //_result:
  72. *
  73. * // ...is equal to privB.computeSharedSecret(pubA).
  74. * sign2.computeSharedSecret(sign1.publicKey)
  75. * //_result:
  76. */
  77. computeSharedSecret(other) {
  78. const pubKey = SigningKey.computePublicKey(other);
  79. return hexlify(secp256k1.getSharedSecret(getBytesCopy(this.#privateKey), getBytes(pubKey), false));
  80. }
  81. /**
  82. * Compute the public key for %%key%%, optionally %%compressed%%.
  83. *
  84. * The %%key%% may be any type of key, a raw public key, a
  85. * compressed/uncompressed public key or private key.
  86. *
  87. * @example:
  88. * sign = new SigningKey(id("some-secret"));
  89. *
  90. * // Compute the uncompressed public key for a private key
  91. * SigningKey.computePublicKey(sign.privateKey)
  92. * //_result:
  93. *
  94. * // Compute the compressed public key for a private key
  95. * SigningKey.computePublicKey(sign.privateKey, true)
  96. * //_result:
  97. *
  98. * // Compute the uncompressed public key
  99. * SigningKey.computePublicKey(sign.publicKey, false);
  100. * //_result:
  101. *
  102. * // Compute the Compressed a public key
  103. * SigningKey.computePublicKey(sign.publicKey, true);
  104. * //_result:
  105. */
  106. static computePublicKey(key, compressed) {
  107. let bytes = getBytes(key, "key");
  108. // private key
  109. if (bytes.length === 32) {
  110. const pubKey = secp256k1.getPublicKey(bytes, !!compressed);
  111. return hexlify(pubKey);
  112. }
  113. // raw public key; use uncompressed key with 0x04 prefix
  114. if (bytes.length === 64) {
  115. const pub = new Uint8Array(65);
  116. pub[0] = 0x04;
  117. pub.set(bytes, 1);
  118. bytes = pub;
  119. }
  120. const point = secp256k1.ProjectivePoint.fromHex(bytes);
  121. return hexlify(point.toRawBytes(compressed));
  122. }
  123. /**
  124. * Returns the public key for the private key which produced the
  125. * %%signature%% for the given %%digest%%.
  126. *
  127. * @example:
  128. * key = new SigningKey(id("some-secret"))
  129. * digest = id("hello world")
  130. * sig = key.sign(digest)
  131. *
  132. * // Notice the signer public key...
  133. * key.publicKey
  134. * //_result:
  135. *
  136. * // ...is equal to the recovered public key
  137. * SigningKey.recoverPublicKey(digest, sig)
  138. * //_result:
  139. *
  140. */
  141. static recoverPublicKey(digest, signature) {
  142. assertArgument(dataLength(digest) === 32, "invalid digest length", "digest", digest);
  143. const sig = Signature.from(signature);
  144. let secpSig = secp256k1.Signature.fromCompact(getBytesCopy(concat([sig.r, sig.s])));
  145. secpSig = secpSig.addRecoveryBit(sig.yParity);
  146. const pubKey = secpSig.recoverPublicKey(getBytesCopy(digest));
  147. assertArgument(pubKey != null, "invalid signautre for digest", "signature", signature);
  148. return "0x" + pubKey.toHex(false);
  149. }
  150. /**
  151. * Returns the point resulting from adding the ellipic curve points
  152. * %%p0%% and %%p1%%.
  153. *
  154. * This is not a common function most developers should require, but
  155. * can be useful for certain privacy-specific techniques.
  156. *
  157. * For example, it is used by [[HDNodeWallet]] to compute child
  158. * addresses from parent public keys and chain codes.
  159. */
  160. static addPoints(p0, p1, compressed) {
  161. const pub0 = secp256k1.ProjectivePoint.fromHex(SigningKey.computePublicKey(p0).substring(2));
  162. const pub1 = secp256k1.ProjectivePoint.fromHex(SigningKey.computePublicKey(p1).substring(2));
  163. return "0x" + pub0.add(pub1).toHex(!!compressed);
  164. }
  165. }
  166. //# sourceMappingURL=signing-key.js.map