index.ts 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103
  1. "use strict";
  2. import { EC } from "./elliptic";
  3. import { arrayify, BytesLike, hexlify, hexZeroPad, Signature, SignatureLike, splitSignature } from "@ethersproject/bytes";
  4. import { defineReadOnly } from "@ethersproject/properties";
  5. import { Logger } from "@ethersproject/logger";
  6. import { version } from "./_version";
  7. const logger = new Logger(version);
  8. let _curve: EC = null
  9. function getCurve() {
  10. if (!_curve) {
  11. _curve = new EC("secp256k1");
  12. }
  13. return _curve;
  14. }
  15. export class SigningKey {
  16. readonly curve: string;
  17. readonly privateKey: string;
  18. readonly publicKey: string;
  19. readonly compressedPublicKey: string;
  20. //readonly address: string;
  21. readonly _isSigningKey: boolean;
  22. constructor(privateKey: BytesLike) {
  23. defineReadOnly(this, "curve", "secp256k1");
  24. defineReadOnly(this, "privateKey", hexlify(privateKey));
  25. const keyPair = getCurve().keyFromPrivate(arrayify(this.privateKey));
  26. defineReadOnly(this, "publicKey", "0x" + keyPair.getPublic(false, "hex"));
  27. defineReadOnly(this, "compressedPublicKey", "0x" + keyPair.getPublic(true, "hex"));
  28. defineReadOnly(this, "_isSigningKey", true);
  29. }
  30. _addPoint(other: BytesLike): string {
  31. const p0 = getCurve().keyFromPublic(arrayify(this.publicKey));
  32. const p1 = getCurve().keyFromPublic(arrayify(other));
  33. return "0x" + p0.pub.add(p1.pub).encodeCompressed("hex");
  34. }
  35. signDigest(digest: BytesLike): Signature {
  36. const keyPair = getCurve().keyFromPrivate(arrayify(this.privateKey));
  37. const digestBytes = arrayify(digest);
  38. if (digestBytes.length !== 32) {
  39. logger.throwArgumentError("bad digest length", "digest", digest);
  40. }
  41. const signature = keyPair.sign(digestBytes, { canonical: true });
  42. return splitSignature({
  43. recoveryParam: signature.recoveryParam,
  44. r: hexZeroPad("0x" + signature.r.toString(16), 32),
  45. s: hexZeroPad("0x" + signature.s.toString(16), 32),
  46. })
  47. }
  48. computeSharedSecret(otherKey: BytesLike): string {
  49. const keyPair = getCurve().keyFromPrivate(arrayify(this.privateKey));
  50. const otherKeyPair = getCurve().keyFromPublic(arrayify(computePublicKey(otherKey)));
  51. return hexZeroPad("0x" + keyPair.derive(otherKeyPair.getPublic()).toString(16), 32);
  52. }
  53. static isSigningKey(value: any): value is SigningKey {
  54. return !!(value && value._isSigningKey);
  55. }
  56. }
  57. export function recoverPublicKey(digest: BytesLike, signature: SignatureLike): string {
  58. const sig = splitSignature(signature);
  59. const rs = { r: arrayify(sig.r), s: arrayify(sig.s) };
  60. return "0x" + getCurve().recoverPubKey(arrayify(digest), rs, sig.recoveryParam).encode("hex", false);
  61. }
  62. export function computePublicKey(key: BytesLike, compressed?: boolean): string {
  63. const bytes = arrayify(key);
  64. if (bytes.length === 32) {
  65. const signingKey = new SigningKey(bytes);
  66. if (compressed) {
  67. return "0x" + getCurve().keyFromPrivate(bytes).getPublic(true, "hex");
  68. }
  69. return signingKey.publicKey;
  70. } else if (bytes.length === 33) {
  71. if (compressed) { return hexlify(bytes); }
  72. return "0x" + getCurve().keyFromPublic(bytes).getPublic(false, "hex");
  73. } else if (bytes.length === 65) {
  74. if (!compressed) { return hexlify(bytes); }
  75. return "0x" + getCurve().keyFromPublic(bytes).getPublic(true, "hex");
  76. }
  77. return logger.throwArgumentError("invalid public or private key", "key", "[REDACTED]");
  78. }