@peerbit/crypto 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (83) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +3 -0
  3. package/lib/esm/bytes.d.ts +5 -0
  4. package/lib/esm/bytes.js +15 -0
  5. package/lib/esm/bytes.js.map +1 -0
  6. package/lib/esm/ed25519-sign-browser.d.ts +5 -0
  7. package/lib/esm/ed25519-sign-browser.js +27 -0
  8. package/lib/esm/ed25519-sign-browser.js.map +1 -0
  9. package/lib/esm/ed25519-sign.d.ts +5 -0
  10. package/lib/esm/ed25519-sign.js +64 -0
  11. package/lib/esm/ed25519-sign.js.map +1 -0
  12. package/lib/esm/ed25519.d.ts +41 -0
  13. package/lib/esm/ed25519.js +181 -0
  14. package/lib/esm/ed25519.js.map +1 -0
  15. package/lib/esm/encryption.d.ts +76 -0
  16. package/lib/esm/encryption.js +350 -0
  17. package/lib/esm/encryption.js.map +1 -0
  18. package/lib/esm/errors.d.ts +3 -0
  19. package/lib/esm/errors.js +6 -0
  20. package/lib/esm/errors.js.map +1 -0
  21. package/lib/esm/hash-browser.d.ts +4 -0
  22. package/lib/esm/hash-browser.js +7 -0
  23. package/lib/esm/hash-browser.js.map +1 -0
  24. package/lib/esm/hash.d.ts +4 -0
  25. package/lib/esm/hash.js +6 -0
  26. package/lib/esm/hash.js.map +1 -0
  27. package/lib/esm/index.d.ts +14 -0
  28. package/lib/esm/index.js +15 -0
  29. package/lib/esm/index.js.map +1 -0
  30. package/lib/esm/key.d.ts +44 -0
  31. package/lib/esm/key.js +51 -0
  32. package/lib/esm/key.js.map +1 -0
  33. package/lib/esm/keychain.d.ts +30 -0
  34. package/lib/esm/keychain.js +142 -0
  35. package/lib/esm/keychain.js.map +1 -0
  36. package/lib/esm/libp2p.d.ts +5 -0
  37. package/lib/esm/libp2p.js +21 -0
  38. package/lib/esm/libp2p.js.map +1 -0
  39. package/lib/esm/prehash.d.ts +6 -0
  40. package/lib/esm/prehash.js +32 -0
  41. package/lib/esm/prehash.js.map +1 -0
  42. package/lib/esm/random-browser.d.ts +1 -0
  43. package/lib/esm/random-browser.js +2 -0
  44. package/lib/esm/random-browser.js.map +1 -0
  45. package/lib/esm/random.d.ts +2 -0
  46. package/lib/esm/random.js +3 -0
  47. package/lib/esm/random.js.map +1 -0
  48. package/lib/esm/sepc256k1.d.ts +42 -0
  49. package/lib/esm/sepc256k1.js +194 -0
  50. package/lib/esm/sepc256k1.js.map +1 -0
  51. package/lib/esm/signature.d.ts +34 -0
  52. package/lib/esm/signature.js +283 -0
  53. package/lib/esm/signature.js.map +1 -0
  54. package/lib/esm/signer.d.ts +10 -0
  55. package/lib/esm/signer.js +2 -0
  56. package/lib/esm/signer.js.map +1 -0
  57. package/lib/esm/utils.d.ts +4 -0
  58. package/lib/esm/utils.js +10 -0
  59. package/lib/esm/utils.js.map +1 -0
  60. package/lib/esm/x25519.d.ts +38 -0
  61. package/lib/esm/x25519.js +158 -0
  62. package/lib/esm/x25519.js.map +1 -0
  63. package/package.json +55 -0
  64. package/src/bytes.ts +12 -0
  65. package/src/ed25519-sign-browser.ts +43 -0
  66. package/src/ed25519-sign.ts +83 -0
  67. package/src/ed25519.ts +194 -0
  68. package/src/encryption.ts +376 -0
  69. package/src/errors.ts +5 -0
  70. package/src/hash-browser.ts +10 -0
  71. package/src/hash.ts +10 -0
  72. package/src/index.ts +14 -0
  73. package/src/key.ts +80 -0
  74. package/src/keychain.ts +197 -0
  75. package/src/libp2p.ts +27 -0
  76. package/src/prehash.ts +42 -0
  77. package/src/random-browser.ts +2 -0
  78. package/src/random.ts +2 -0
  79. package/src/sepc256k1.ts +229 -0
  80. package/src/signature.ts +284 -0
  81. package/src/signer.ts +18 -0
  82. package/src/utils.ts +15 -0
  83. package/src/x25519.ts +167 -0
package/src/key.ts ADDED
@@ -0,0 +1,80 @@
1
+ import { serialize } from "@dao-xyz/borsh";
2
+ import { sha256Base64Sync } from "./hash.js";
3
+ import { PeerId } from "@libp2p/interface-peer-id";
4
+
5
+ interface Key {
6
+ equals(other: Key): boolean;
7
+ get bytes(): Uint8Array;
8
+ hashcode(): string;
9
+ toString(): string;
10
+ _hashcode: string;
11
+ }
12
+
13
+ export abstract class Keypair {
14
+ abstract get publicKey(): PublicSignKey | PublicKeyEncryptionKey;
15
+
16
+ equals(other: Keypair): boolean {
17
+ throw new Error("Not implemented");
18
+ }
19
+
20
+ toPeerId(): Promise<PeerId> {
21
+ throw new Error("Not implemented");
22
+ }
23
+ }
24
+
25
+ // ---- SIGNATURE KEYS -----
26
+ export interface PublicSignKey extends Key {}
27
+ export abstract class PublicSignKey implements Key {
28
+ get bytes(): Uint8Array {
29
+ return serialize(this);
30
+ }
31
+
32
+ hashcode(): string {
33
+ return this._hashcode || (this._hashcode = sha256Base64Sync(this.bytes));
34
+ }
35
+ }
36
+
37
+ export interface PrivateSignKey extends Key {}
38
+ export abstract class PrivateSignKey implements Key {
39
+ get bytes(): Uint8Array {
40
+ return serialize(this);
41
+ }
42
+
43
+ hashcode(): string {
44
+ return this._hashcode || (this._hashcode = sha256Base64Sync(this.bytes));
45
+ }
46
+ }
47
+
48
+ // ---- PUBLIC KEY ENCRYPTION -----
49
+ export interface PublicKeyEncryptionKey extends Key {}
50
+ export abstract class PublicKeyEncryptionKey implements Key {
51
+ get bytes(): Uint8Array {
52
+ return serialize(this);
53
+ }
54
+
55
+ hashcode(): string {
56
+ return this._hashcode || (this._hashcode = sha256Base64Sync(this.bytes));
57
+ }
58
+ }
59
+ export interface PrivateEncryptionKey extends Key {}
60
+ export abstract class PrivateEncryptionKey implements Key {
61
+ get bytes(): Uint8Array {
62
+ return serialize(this);
63
+ }
64
+
65
+ hashcode(): string {
66
+ return this._hashcode || (this._hashcode = sha256Base64Sync(this.bytes));
67
+ }
68
+ }
69
+
70
+ // ---- OTHER KEYS ----
71
+ export interface PlainKey extends Key {}
72
+ export abstract class PlainKey implements Key {
73
+ get bytes(): Uint8Array {
74
+ return serialize(this);
75
+ }
76
+
77
+ hashcode(): string {
78
+ return this._hashcode || (this._hashcode = sha256Base64Sync(this.bytes));
79
+ }
80
+ }
@@ -0,0 +1,197 @@
1
+ import { KeyChain as InternalKeychain } from "@libp2p/interface-keychain";
2
+ import { keysPBM } from "@libp2p/crypto/keys";
3
+ import { identity } from "multiformats/hashes/identity";
4
+ import { base58btc } from "multiformats/bases/base58";
5
+ import { Cache } from "@peerbit/cache";
6
+ import { Ed25519Keypair, Ed25519PublicKey } from "./ed25519.js";
7
+ import { Keypair, PublicSignKey } from "./key.js";
8
+
9
+ import { KeyInfo } from "@libp2p/interface-keychain";
10
+ import { AccessError, X25519Keypair, X25519PublicKey } from "./x25519.js";
11
+
12
+ export type KeypairFromPublicKey<T> = T extends X25519PublicKey
13
+ ? X25519PublicKey extends T
14
+ ? X25519Keypair
15
+ : Ed25519Keypair
16
+ : Ed25519Keypair;
17
+
18
+ export interface Keychain {
19
+ import(keypair: Ed25519Keypair, id: Uint8Array): Promise<void>;
20
+
21
+ exportByKey<
22
+ T extends Ed25519PublicKey | X25519PublicKey,
23
+ Q = KeypairFromPublicKey<T>
24
+ >(
25
+ publicKey: T
26
+ ): Promise<Q | undefined>;
27
+
28
+ exportById<
29
+ T = "ed25519" | "x25519",
30
+ Q = T extends "ed25519" ? Ed25519Keypair : X25519Keypair
31
+ >(
32
+ id: Uint8Array,
33
+ type: T
34
+ ): Promise<Q | undefined>;
35
+ }
36
+
37
+ export class Libp2pKeychain implements Keychain {
38
+ constructor(
39
+ readonly keychain: InternalKeychain,
40
+ readonly options?: { cache?: Cache<X25519Keypair | Ed25519Keypair | null> }
41
+ ) {}
42
+
43
+ keychainKeyIdFromPublicKey(publicKey: X25519PublicKey) {
44
+ const bytes = keysPBM.PublicKey.encode({
45
+ Type: keysPBM.KeyType.Ed25519,
46
+ Data: publicKey.publicKey,
47
+ }).subarray();
48
+
49
+ const encoding = identity.digest(bytes);
50
+ return base58btc.encode(encoding.bytes).substring(1);
51
+ }
52
+
53
+ private cacheKey(key: Ed25519Keypair | X25519Keypair, id?: Uint8Array) {
54
+ this.options?.cache?.add(base58btc.encode(key.publicKey.bytes), key);
55
+ id && this.options?.cache?.add(base58btc.encode(id), key);
56
+ }
57
+
58
+ private getCachedById(id: Uint8Array): Ed25519Keypair | null | undefined {
59
+ const key = base58btc.encode(id instanceof PublicSignKey ? id.bytes : id);
60
+ const cached = this.options?.cache?.get(key);
61
+ if (cached === null) {
62
+ return null;
63
+ } else if (!cached) {
64
+ return undefined;
65
+ } else if (cached instanceof Ed25519Keypair) {
66
+ return cached;
67
+ }
68
+ throw new Error("Unexpected cached keypair type: " + key?.constructor.name);
69
+ }
70
+
71
+ private getCachedByKey<
72
+ T extends X25519PublicKey | Ed25519PublicKey,
73
+ Q = KeypairFromPublicKey<T>
74
+ >(publicKey: T): Q | null | undefined {
75
+ const key = base58btc.encode(publicKey.bytes);
76
+ const cached = this.options?.cache?.get(key);
77
+ if (cached === null) {
78
+ return null;
79
+ } else if (!cached) {
80
+ return undefined;
81
+ } else if (cached instanceof Keypair) {
82
+ return cached as Q;
83
+ }
84
+ throw new Error("Unexpected cached keypair type: " + key?.constructor.name);
85
+ }
86
+
87
+ exportByKey = async <
88
+ T extends X25519PublicKey | Ed25519PublicKey,
89
+ Q = KeypairFromPublicKey<T>
90
+ >(
91
+ publicKey: T
92
+ ): Promise<Q | undefined> => {
93
+ const cached = this.getCachedByKey<T, Q>(publicKey);
94
+ if (cached !== undefined) {
95
+ // if null, means key is deleted
96
+ return cached ? cached : undefined;
97
+ }
98
+
99
+ let keyInfo: KeyInfo | undefined = undefined;
100
+ if (publicKey instanceof Ed25519PublicKey) {
101
+ try {
102
+ keyInfo = await this.keychain.findKeyById(
103
+ (await publicKey.toPeerId()).toString()
104
+ );
105
+ } catch (e: any) {
106
+ if (e.code !== "ERR_KEY_NOT_FOUND") {
107
+ throw e;
108
+ }
109
+ }
110
+ }
111
+
112
+ try {
113
+ keyInfo = await this.keychain.findKeyByName(
114
+ base58btc.encode(publicKey.bytes)
115
+ );
116
+ } catch (e: any) {
117
+ if (e.code !== "ERR_KEY_NOT_FOUND") {
118
+ throw e;
119
+ }
120
+ }
121
+
122
+ if (!keyInfo) {
123
+ return undefined;
124
+ }
125
+
126
+ const peerId = await this.keychain.exportPeerId(keyInfo.name);
127
+
128
+ return (
129
+ publicKey instanceof X25519PublicKey
130
+ ? X25519Keypair.fromPeerId(peerId)
131
+ : Ed25519Keypair.fromPeerId(peerId)
132
+ ) as Q;
133
+ };
134
+
135
+ async exportById<
136
+ T = "ed25519" | "x25519",
137
+ Q = T extends "ed25519" ? Ed25519Keypair : X25519Keypair
138
+ >(id: Uint8Array, type: T): Promise<Q | undefined> {
139
+ const cached = this.getCachedById(id) as Ed25519Keypair | undefined | null;
140
+ if (cached !== undefined) {
141
+ // if null, means key is deleted
142
+ if (type === "x25519" && cached instanceof Ed25519Keypair) {
143
+ return X25519Keypair.from(cached) as Q; // TODO perf, don't do this all the time
144
+ }
145
+ return cached ? (cached as Q) : undefined;
146
+ }
147
+ try {
148
+ const keyInfo = await this.keychain.findKeyByName(base58btc.encode(id));
149
+ const peerId = await this.keychain.exportPeerId(keyInfo.name);
150
+ if (type === "x25519") {
151
+ return X25519Keypair.fromPeerId(peerId) as Q;
152
+ }
153
+ return Ed25519Keypair.fromPeerId(peerId) as Q;
154
+ } catch (e: any) {
155
+ if (e.code !== "ERR_KEY_NOT_FOUND") {
156
+ throw e;
157
+ }
158
+ }
159
+ }
160
+
161
+ import = async (keypair: Ed25519Keypair, id: Uint8Array) => {
162
+ const receiverKeyPeerId = await keypair.toPeerId();
163
+ this.cacheKey(keypair, id);
164
+
165
+ // import as ed
166
+ await this.keychain.importPeer(base58btc.encode(id), receiverKeyPeerId);
167
+
168
+ // import as x so we can decrypt messages with this public key (if recieved any)
169
+ const xKeypair = await X25519Keypair.from(keypair);
170
+ this.cacheKey(xKeypair);
171
+ await this.keychain.importPeer(
172
+ base58btc.encode(xKeypair.publicKey.bytes),
173
+ receiverKeyPeerId
174
+ );
175
+ };
176
+
177
+ // Arrow function is used so we can reference this function and use 'this' without .bind(self)
178
+ getAnyKeypair = async (publicKeys) => {
179
+ for (let i = 0; i < publicKeys.length; i++) {
180
+ try {
181
+ const key = await this.exportByKey(publicKeys[i]);
182
+ if (key && key instanceof X25519Keypair) {
183
+ return {
184
+ index: i,
185
+ keypair: key as X25519Keypair,
186
+ };
187
+ }
188
+ } catch (error: any) {
189
+ // Key missing
190
+ if (error.code !== "ERR_NOT_FOUND") {
191
+ throw error;
192
+ }
193
+ }
194
+ }
195
+ throw new AccessError("Failed to access key");
196
+ };
197
+ }
package/src/libp2p.ts ADDED
@@ -0,0 +1,27 @@
1
+ import { PeerId } from "@libp2p/interface-peer-id";
2
+ import { Ed25519Keypair, Ed25519PublicKey } from "./ed25519.js";
3
+ import { Secp256k1Keypair, Secp256k1PublicKey } from "./sepc256k1.js";
4
+
5
+ export const getKeypairFromPeerId = (
6
+ peerId: PeerId
7
+ ): Ed25519Keypair | Secp256k1Keypair => {
8
+ if (peerId.type === "Ed25519") {
9
+ return Ed25519Keypair.fromPeerId(peerId);
10
+ }
11
+ if (peerId.type === "secp256k1") {
12
+ return Secp256k1Keypair.fromPeerId(peerId);
13
+ }
14
+ throw new Error("Unsupported key type");
15
+ };
16
+
17
+ export const getPublicKeyFromPeerId = (
18
+ peerId: PeerId
19
+ ): Ed25519PublicKey | Secp256k1PublicKey => {
20
+ if (peerId.type === "Ed25519") {
21
+ return Ed25519PublicKey.fromPeerId(peerId);
22
+ }
23
+ if (peerId.type === "secp256k1") {
24
+ return Secp256k1PublicKey.from(peerId);
25
+ }
26
+ throw new Error("Unsupported key type");
27
+ };
package/src/prehash.ts ADDED
@@ -0,0 +1,42 @@
1
+ import { sha256 } from "./hash.js";
2
+ import sha3 from "js-sha3";
3
+ import { toUtf8Bytes } from "@ethersproject/strings";
4
+ import { concat } from "uint8arrays";
5
+
6
+ const messagePrefix = "\x19Ethereum Signed Message:\n";
7
+ const ethKeccak256Hash = (message: Uint8Array) =>
8
+ new Uint8Array(
9
+ sha3.keccak256
10
+ .update(
11
+ concat([
12
+ toUtf8Bytes(messagePrefix),
13
+ toUtf8Bytes(String(message.length)),
14
+ message,
15
+ ])
16
+ )
17
+ .arrayBuffer()
18
+ );
19
+
20
+ export enum PreHash {
21
+ NONE = 0,
22
+ SHA_256 = 1,
23
+ //BLAKE3 = 2,
24
+ ETH_KECCAK_256 = 3,
25
+ }
26
+
27
+ export const prehashFn = (
28
+ data: Uint8Array,
29
+ prehash: PreHash
30
+ ): Promise<Uint8Array> | Uint8Array => {
31
+ if (prehash === PreHash.NONE) {
32
+ return data;
33
+ }
34
+ if (prehash === PreHash.SHA_256) {
35
+ return sha256(data);
36
+ }
37
+ if (prehash === PreHash.ETH_KECCAK_256) {
38
+ return ethKeccak256Hash(data);
39
+ }
40
+
41
+ throw new Error("Unsupported");
42
+ };
@@ -0,0 +1,2 @@
1
+ export const randomBytes = (len: number) =>
2
+ globalThis.crypto.getRandomValues(new Uint8Array(len));
package/src/random.ts ADDED
@@ -0,0 +1,2 @@
1
+ import crypto from "crypto";
2
+ export const randomBytes = (len: number) => crypto.randomBytes(len);
@@ -0,0 +1,229 @@
1
+ import { field, fixedArray, variant, vec } from "@dao-xyz/borsh";
2
+ import { Keypair, PrivateSignKey, PublicSignKey } from "./key.js";
3
+ import { Wallet } from "@ethersproject/wallet";
4
+ import { arrayify } from "@ethersproject/bytes";
5
+ import { joinSignature } from "@ethersproject/bytes";
6
+ import { SignatureLike, splitSignature } from "@ethersproject/bytes";
7
+ import _ec from "elliptic";
8
+ import EC = _ec.ec;
9
+ let _curve: EC;
10
+
11
+ import { equals } from "@peerbit/uint8arrays";
12
+ import { toHexString } from "./utils.js";
13
+ import { PeerId } from "@libp2p/interface-peer-id";
14
+ import { Identity, Signer } from "./signer.js";
15
+ import { coerce } from "./bytes.js";
16
+ import { generateKeyPair, supportedKeys } from "@libp2p/crypto/keys";
17
+ import utf8 from "@protobufjs/utf8";
18
+ import { SignatureWithKey } from "./signature.js";
19
+ import { PreHash, prehashFn } from "./prehash.js";
20
+ import { peerIdFromKeys } from "@libp2p/peer-id";
21
+
22
+ @variant(1)
23
+ export class Secp256k1PublicKey extends PublicSignKey {
24
+ @field({ type: fixedArray("u8", 33) })
25
+ publicKey: Uint8Array;
26
+
27
+ constructor(properties: { publicKey: Uint8Array }) {
28
+ super();
29
+ if (properties.publicKey.length !== 33) {
30
+ throw new Error("Expecting key to have length 33");
31
+ }
32
+ this.publicKey = properties.publicKey;
33
+ }
34
+
35
+ static async recover(wallet: Wallet) {
36
+ // Signa message
37
+ const toSign = new Uint8Array([0]);
38
+ const signature = await wallet.signMessage(toSign);
39
+
40
+ // So we can recover the public key
41
+ const publicKey = recoverPublicKeyFromSignature(
42
+ await prehashFn(toSign, PreHash.ETH_KECCAK_256),
43
+ signature
44
+ );
45
+
46
+ return new Secp256k1PublicKey({ publicKey });
47
+ }
48
+
49
+ equals(other: PublicSignKey): boolean {
50
+ if (other instanceof Secp256k1PublicKey) {
51
+ return equals(this.publicKey, other.publicKey);
52
+ }
53
+ return false;
54
+ }
55
+
56
+ toString(): string {
57
+ return "sepc256k1/" + toHexString(this.publicKey);
58
+ }
59
+
60
+ static from(id: PeerId) {
61
+ if (!id.publicKey) {
62
+ throw new Error("Missing public key");
63
+ }
64
+ if (id.type === "secp256k1") {
65
+ return new Secp256k1PublicKey({
66
+ publicKey: id.publicKey.slice(4), // computeAddress(!.slice(4)),
67
+ });
68
+ }
69
+ throw new Error("Unsupported key type: " + id.type);
70
+ }
71
+ }
72
+
73
+ @variant(1)
74
+ export class Secp256k1PrivateKey extends PrivateSignKey {
75
+ @field({ type: Uint8Array })
76
+ privateKey: Uint8Array;
77
+
78
+ constructor(properties: { privateKey: Uint8Array }) {
79
+ super();
80
+ if (properties.privateKey.length !== 32) {
81
+ throw new Error("Expecting key to have length 32");
82
+ }
83
+
84
+ this.privateKey = properties.privateKey;
85
+ }
86
+
87
+ equals(other: Secp256k1PrivateKey): boolean {
88
+ if (other instanceof Secp256k1PrivateKey) {
89
+ return equals(this.privateKey, other.privateKey);
90
+ }
91
+ return false;
92
+ }
93
+
94
+ toString(): string {
95
+ return "secp256k1s/" + toHexString(this.privateKey);
96
+ }
97
+
98
+ static from(id: PeerId) {
99
+ if (!id.privateKey) {
100
+ throw new Error("Missing privateKey key");
101
+ }
102
+ if (id.type === "secp256k1") {
103
+ return new Secp256k1PrivateKey({
104
+ privateKey: coerce(id.privateKey!.slice(4)),
105
+ });
106
+ }
107
+ throw new Error("Unsupported key type: " + id.type);
108
+ }
109
+ }
110
+
111
+ @variant(2)
112
+ export class Secp256k1Keypair extends Keypair implements Identity {
113
+ @field({ type: Secp256k1PublicKey })
114
+ publicKey: Secp256k1PublicKey;
115
+
116
+ @field({ type: Secp256k1PrivateKey })
117
+ privateKey: Secp256k1PrivateKey;
118
+
119
+ _wallet: Wallet;
120
+ constructor(properties: {
121
+ publicKey: Secp256k1PublicKey;
122
+ privateKey: Secp256k1PrivateKey;
123
+ }) {
124
+ super();
125
+ this.privateKey = properties.privateKey;
126
+ this.publicKey = properties.publicKey;
127
+ }
128
+
129
+ static async create(): Promise<Secp256k1Keypair> {
130
+ const generated = await generateKeyPair("secp256k1");
131
+ const kp = new Secp256k1Keypair({
132
+ publicKey: new Secp256k1PublicKey({
133
+ publicKey: generated.public.marshal(),
134
+ }),
135
+ privateKey: new Secp256k1PrivateKey({
136
+ privateKey: generated.marshal(),
137
+ }),
138
+ });
139
+
140
+ return kp;
141
+ }
142
+
143
+ async sign(
144
+ data: Uint8Array,
145
+ prehash: PreHash = PreHash.ETH_KECCAK_256
146
+ ): Promise<SignatureWithKey> {
147
+ const maybeHashed = await prehashFn(data, prehash);
148
+
149
+ const signature = joinSignature(
150
+ (this._wallet || (this._wallet = new Wallet(this.privateKey.privateKey)))
151
+ ._signingKey()
152
+ .signDigest(maybeHashed)
153
+ );
154
+ const signatureBytes = new Uint8Array(utf8.length(signature)); // TODO utilize Buffer allocUnsafe
155
+ utf8.write(signature, signatureBytes, 0);
156
+
157
+ return new SignatureWithKey({
158
+ prehash,
159
+ publicKey: this.publicKey,
160
+ signature: signatureBytes,
161
+ });
162
+ }
163
+
164
+ equals(other: Keypair) {
165
+ if (other instanceof Secp256k1Keypair) {
166
+ return (
167
+ this.publicKey.equals(other.publicKey) &&
168
+ this.privateKey.equals(other.privateKey)
169
+ );
170
+ }
171
+ return false;
172
+ }
173
+
174
+ static fromPeerId(peerId: PeerId) {
175
+ return new Secp256k1Keypair({
176
+ privateKey: Secp256k1PrivateKey.from(peerId),
177
+ publicKey: Secp256k1PublicKey.from(peerId),
178
+ });
179
+ }
180
+
181
+ toPeerId(): Promise<PeerId> {
182
+ return peerIdFromKeys(
183
+ new supportedKeys["secp256k1"].Secp256k1PublicKey(
184
+ this.publicKey.publicKey
185
+ ).bytes,
186
+ new supportedKeys["secp256k1"].Secp256k1PrivateKey(
187
+ this.privateKey.privateKey,
188
+ this.publicKey.publicKey
189
+ ).bytes
190
+ );
191
+ }
192
+ }
193
+
194
+ const decoder = new TextDecoder();
195
+
196
+ function getCurve() {
197
+ if (!_curve) {
198
+ _curve = new EC("secp256k1");
199
+ }
200
+ return _curve;
201
+ }
202
+
203
+ export const recoverPublicKeyFromSignature = (
204
+ digest: Uint8Array,
205
+ signature: SignatureLike
206
+ ): Uint8Array => {
207
+ const sig = splitSignature(signature);
208
+ const rs = { r: arrayify(sig.r), s: arrayify(sig.s) };
209
+ return new Uint8Array(
210
+ getCurve()
211
+ .recoverPubKey(arrayify(digest), rs, sig.recoveryParam)
212
+ .encodeCompressed()
213
+ );
214
+ };
215
+
216
+ export const verifySignatureSecp256k1 = async (
217
+ signature: SignatureWithKey,
218
+ data: Uint8Array
219
+ ): Promise<boolean> => {
220
+ const hashedData = await prehashFn(data, signature.prehash);
221
+ const signerKey = recoverPublicKeyFromSignature(
222
+ arrayify(hashedData),
223
+ decoder.decode(signature.signature)
224
+ );
225
+ return equals(
226
+ signerKey,
227
+ (signature.publicKey as Secp256k1PublicKey).publicKey
228
+ );
229
+ };