@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
@@ -0,0 +1,83 @@
1
+ import { Ed25519Keypair, Ed25519PublicKey } from "./ed25519.js";
2
+ import sodium from "libsodium-wrappers";
3
+ import crypto from "crypto";
4
+ import { SignatureWithKey } from "./signature.js";
5
+ import { PreHash, prehashFn } from "./prehash.js";
6
+
7
+ export const sign = async (
8
+ data: Uint8Array,
9
+ keypair: Ed25519Keypair,
10
+ prehash: PreHash
11
+ ) => {
12
+ const hashedData = await prehashFn(data, prehash);
13
+
14
+ if (!keypair.privateKey.keyObject) {
15
+ keypair.privateKey.keyObject = crypto.createPrivateKey({
16
+ format: "der",
17
+ type: "pkcs8",
18
+ key: toDER(keypair.privateKeyPublicKey, true),
19
+ });
20
+ }
21
+ return new SignatureWithKey({
22
+ prehash,
23
+ publicKey: keypair.publicKey,
24
+ signature: crypto.sign(null, hashedData, keypair.privateKey.keyObject),
25
+ });
26
+ };
27
+
28
+ export const verifySignatureEd25519 = async (
29
+ signature: SignatureWithKey,
30
+ data: Uint8Array
31
+ ) => {
32
+ let res = false;
33
+ try {
34
+ const hashedData = await prehashFn(data, signature.prehash);
35
+
36
+ /* return crypto.verify(null, hashedData, publicKey.keyObject, signature); */ // Sodium seems faster
37
+ const verified = sodium.crypto_sign_verify_detached(
38
+ signature.signature,
39
+ hashedData,
40
+ (signature.publicKey as Ed25519PublicKey).publicKey
41
+ );
42
+ res = verified;
43
+ } catch (error) {
44
+ return false;
45
+ }
46
+ return res;
47
+ };
48
+
49
+ const DER_PREFIX = Buffer.from([
50
+ 48, 46, 2, 1, 0, 48, 5, 6, 3, 43, 101, 112, 4, 34, 4, 32,
51
+ ]);
52
+ const ED25519_OID = Buffer.from([0x06, 0x03, 0x2b, 0x65, 0x70]);
53
+ const SEQUENCE_TAG = Buffer.from([0x30]); // Sequence tag
54
+ const BIT_TAG = Buffer.from([0x03]); // Bit tag
55
+ const ZERO_BIT_TAG = Buffer.from([0x00]); // Zero bit
56
+ function toDER(key: Uint8Array, p = false) {
57
+ if (p) {
58
+ return Buffer.concat([DER_PREFIX, key]);
59
+ }
60
+
61
+ // Ed25519's OID
62
+ const oid = ED25519_OID;
63
+
64
+ // Create a byte sequence containing the OID and key
65
+ const elements = Buffer.concat([
66
+ SEQUENCE_TAG,
67
+ Buffer.from([oid.length]),
68
+ oid,
69
+ BIT_TAG,
70
+ Buffer.from([key.length + 1]),
71
+ ZERO_BIT_TAG,
72
+ key,
73
+ ]);
74
+
75
+ // Wrap up by creating a sequence of elements
76
+ const der = Buffer.concat([
77
+ SEQUENCE_TAG,
78
+ Buffer.from([elements.length]),
79
+ elements,
80
+ ]);
81
+
82
+ return der;
83
+ }
package/src/ed25519.ts ADDED
@@ -0,0 +1,194 @@
1
+ import { field, fixedArray, variant } from "@dao-xyz/borsh";
2
+ import { PrivateSignKey, PublicSignKey, Keypair } from "./key.js";
3
+ import { equals } from "@peerbit/uint8arrays";
4
+ import { Identity, Signer, SignWithKey } from "./signer.js";
5
+ import { SignatureWithKey } from "./signature.js";
6
+ import { toHexString } from "./utils.js";
7
+ import { peerIdFromKeys } from "@libp2p/peer-id";
8
+ import { supportedKeys } from "@libp2p/crypto/keys";
9
+ import { coerce } from "./bytes.js";
10
+ import sodium from "libsodium-wrappers";
11
+ import type { Ed25519PeerId, PeerId } from "@libp2p/interface-peer-id";
12
+ import { sign } from "./ed25519-sign.js";
13
+ import { PreHash } from "./prehash.js";
14
+ import { concat } from "uint8arrays";
15
+
16
+ @variant(0)
17
+ export class Ed25519PublicKey extends PublicSignKey {
18
+ @field({ type: fixedArray("u8", 32) })
19
+ publicKey: Uint8Array;
20
+
21
+ constructor(properties: { publicKey: Uint8Array }) {
22
+ super();
23
+ this.publicKey = properties.publicKey;
24
+ if (properties.publicKey.length !== 32) {
25
+ throw new Error("Expecting key to have length 32");
26
+ }
27
+ }
28
+
29
+ equals(other: PublicSignKey): boolean {
30
+ if (other instanceof Ed25519PublicKey) {
31
+ return equals(this.publicKey, other.publicKey);
32
+ }
33
+ return false;
34
+ }
35
+ toString(): string {
36
+ return "ed25119p/" + toHexString(this.publicKey);
37
+ }
38
+
39
+ toPeerId(): Promise<PeerId> {
40
+ return peerIdFromKeys(
41
+ new supportedKeys["ed25519"].Ed25519PublicKey(this.publicKey).bytes
42
+ );
43
+ }
44
+
45
+ /* Don't use keyobject for publicKeys becuse it takes longer time to derive it compare to verifying with sodium
46
+ private keyObject: any;
47
+ get keyObject() {
48
+ return (
49
+ this._keyObject ||
50
+ (this._keyObject = crypto.createPublicKey({
51
+ format: "der",
52
+ type: "spki",
53
+ key: toDER(this.publicKey),
54
+ }))
55
+ );
56
+ } */
57
+ static fromPeerId(id: PeerId) {
58
+ if (!id.publicKey) {
59
+ throw new Error("Missing public key");
60
+ }
61
+ if (id.type === "Ed25519") {
62
+ return new Ed25519PublicKey({
63
+ publicKey: coerce(id.publicKey!.slice(4)),
64
+ });
65
+ }
66
+ throw new Error("Unsupported key type: " + id.type);
67
+ }
68
+ }
69
+
70
+ @variant(0)
71
+ export class Ed25519PrivateKey extends PrivateSignKey {
72
+ @field({ type: fixedArray("u8", 32) })
73
+ privateKey: Uint8Array;
74
+
75
+ constructor(properties: { privateKey: Uint8Array }) {
76
+ super();
77
+
78
+ if (properties.privateKey.length !== 32) {
79
+ throw new Error("Expecting key to have length 32");
80
+ }
81
+
82
+ this.privateKey = properties.privateKey;
83
+ }
84
+
85
+ equals(other: Ed25519PrivateKey): boolean {
86
+ if (other instanceof Ed25519PrivateKey) {
87
+ return equals(this.privateKey, other.privateKey);
88
+ }
89
+ return false;
90
+ }
91
+
92
+ toString(): string {
93
+ return "ed25119s/" + toHexString(this.privateKey);
94
+ }
95
+
96
+ keyObject: any; // crypto.KeyObject;
97
+
98
+ static fromPeerID(id: PeerId) {
99
+ if (!id.privateKey) {
100
+ throw new Error("Missing privateKey key");
101
+ }
102
+ if (id.type === "Ed25519") {
103
+ return new Ed25519PrivateKey({
104
+ privateKey: coerce(id.privateKey!.slice(4, 36)),
105
+ });
106
+ }
107
+ throw new Error("Unsupported key type: " + id.type);
108
+ }
109
+ }
110
+
111
+ @variant(0)
112
+ export class Ed25519Keypair extends Keypair implements Identity {
113
+ @field({ type: Ed25519PublicKey })
114
+ publicKey: Ed25519PublicKey;
115
+
116
+ @field({ type: Ed25519PrivateKey })
117
+ privateKey: Ed25519PrivateKey;
118
+
119
+ constructor(properties: {
120
+ publicKey: Ed25519PublicKey;
121
+ privateKey: Ed25519PrivateKey;
122
+ }) {
123
+ super();
124
+ this.privateKey = properties.privateKey;
125
+ this.publicKey = properties.publicKey;
126
+ }
127
+
128
+ static async create(): Promise<Ed25519Keypair> {
129
+ await sodium.ready;
130
+ const generated = sodium.crypto_sign_keypair();
131
+ const kp = new Ed25519Keypair({
132
+ publicKey: new Ed25519PublicKey({
133
+ publicKey: generated.publicKey,
134
+ }),
135
+ privateKey: new Ed25519PrivateKey({
136
+ privateKey: generated.privateKey.slice(0, 32), // Only the private key part (?)
137
+ }),
138
+ });
139
+
140
+ return kp;
141
+ }
142
+
143
+ sign(
144
+ data: Uint8Array,
145
+ prehash: PreHash = PreHash.NONE
146
+ ): Promise<SignatureWithKey> {
147
+ return sign(data, this, prehash);
148
+ }
149
+
150
+ signer(prehash: PreHash): SignWithKey {
151
+ return async (data: Uint8Array) => {
152
+ return this.sign(data, prehash);
153
+ };
154
+ }
155
+
156
+ equals(other: Keypair) {
157
+ if (other instanceof Ed25519Keypair) {
158
+ return (
159
+ this.publicKey.equals(other.publicKey) &&
160
+ this.privateKey.equals(other.privateKey)
161
+ );
162
+ }
163
+ return false;
164
+ }
165
+
166
+ static fromPeerId(peerId: PeerId | Ed25519PeerId) {
167
+ return new Ed25519Keypair({
168
+ privateKey: Ed25519PrivateKey.fromPeerID(peerId),
169
+ publicKey: Ed25519PublicKey.fromPeerId(peerId),
170
+ });
171
+ }
172
+
173
+ _privateKeyPublicKey: Uint8Array; // length 64
174
+ get privateKeyPublicKey(): Uint8Array {
175
+ return (
176
+ this._privateKeyPublicKey ||
177
+ (this._privateKeyPublicKey = concat([
178
+ this.privateKey.privateKey,
179
+ this.publicKey.publicKey,
180
+ ]))
181
+ );
182
+ }
183
+
184
+ toPeerId(): Promise<PeerId> {
185
+ return peerIdFromKeys(
186
+ new supportedKeys["ed25519"].Ed25519PublicKey(this.publicKey.publicKey)
187
+ .bytes,
188
+ new supportedKeys["ed25519"].Ed25519PrivateKey(
189
+ this.privateKeyPublicKey,
190
+ this.publicKey.publicKey
191
+ ).bytes
192
+ );
193
+ }
194
+ }
@@ -0,0 +1,376 @@
1
+ export * from "./errors.js";
2
+ import {
3
+ AbstractType,
4
+ deserialize,
5
+ field,
6
+ serialize,
7
+ variant,
8
+ vec,
9
+ } from "@dao-xyz/borsh";
10
+ import { equals } from "@peerbit/uint8arrays";
11
+ import { AccessError } from "./errors.js";
12
+ import sodium from "libsodium-wrappers";
13
+ import { X25519Keypair, X25519PublicKey, X25519SecretKey } from "./x25519.js";
14
+ import { Ed25519Keypair, Ed25519PublicKey } from "./ed25519.js";
15
+ import { randomBytes } from "./random.js";
16
+ import { Keychain } from "./keychain.js";
17
+
18
+ const NONCE_LENGTH = 24;
19
+
20
+ @variant(0)
21
+ export abstract class MaybeEncrypted<T> {
22
+ /**
23
+ * Will throw error if not decrypted
24
+ */
25
+ get decrypted(): DecryptedThing<T> {
26
+ throw new Error("Not implented");
27
+ }
28
+
29
+ decrypt(
30
+ keyOrKeychain?: Keychain | X25519Keypair
31
+ ): Promise<DecryptedThing<T>> | DecryptedThing<T> {
32
+ throw new Error("Not implemented");
33
+ }
34
+ equals(other: MaybeEncrypted<T>): boolean {
35
+ throw new Error("Not implemented");
36
+ }
37
+
38
+ /**
39
+ * Clear cached data
40
+ */
41
+ clear() {
42
+ throw new Error("Not implemented");
43
+ }
44
+
45
+ abstract get byteLength(): number;
46
+ }
47
+
48
+ @variant(0)
49
+ export class DecryptedThing<T> extends MaybeEncrypted<T> {
50
+ @field({ type: Uint8Array })
51
+ _data?: Uint8Array;
52
+
53
+ constructor(props?: { data?: Uint8Array; value?: T }) {
54
+ super();
55
+ if (props) {
56
+ this._data = props.data;
57
+ this._value = props.value;
58
+ }
59
+ }
60
+
61
+ _value?: T;
62
+ getValue(clazz: AbstractType<T>): T {
63
+ if (this._value) {
64
+ return this._value;
65
+ }
66
+ if (!this._data) {
67
+ throw new Error("Missing data");
68
+ }
69
+ return deserialize(this._data, clazz);
70
+ }
71
+
72
+ async encrypt(
73
+ x25519Keypair: X25519Keypair,
74
+ ...recieverPublicKeys: (X25519PublicKey | Ed25519PublicKey)[]
75
+ ): Promise<EncryptedThing<T>> {
76
+ const bytes = serialize(this);
77
+ const epheremalKey = sodium.crypto_secretbox_keygen();
78
+ const nonce = randomBytes(NONCE_LENGTH); // crypto random is faster than sodim random
79
+ const cipher = sodium.crypto_secretbox_easy(bytes, nonce, epheremalKey);
80
+
81
+ const recieverX25519PublicKeys = await Promise.all(
82
+ recieverPublicKeys.map((key) => {
83
+ if (key instanceof Ed25519PublicKey) {
84
+ return X25519PublicKey.from(key);
85
+ }
86
+ return key;
87
+ })
88
+ );
89
+
90
+ const ks = recieverX25519PublicKeys.map((recieverPublicKey) => {
91
+ const kNonce = randomBytes(NONCE_LENGTH); // crypto random is faster than sodium random
92
+ return new K({
93
+ encryptedKey: new CipherWithNonce({
94
+ cipher: sodium.crypto_box_easy(
95
+ epheremalKey,
96
+ kNonce,
97
+ recieverPublicKey.publicKey,
98
+ x25519Keypair.secretKey.secretKey
99
+ ),
100
+ nonce: kNonce,
101
+ }),
102
+ recieverPublicKey,
103
+ });
104
+ });
105
+
106
+ const enc = new EncryptedThing<T>({
107
+ encrypted: new Uint8Array(cipher),
108
+ nonce,
109
+ envelope: new Envelope({
110
+ senderPublicKey: x25519Keypair.publicKey,
111
+ ks,
112
+ }),
113
+ });
114
+ enc._decrypted = this;
115
+ return enc;
116
+ }
117
+
118
+ get decrypted(): DecryptedThing<T> {
119
+ return this;
120
+ }
121
+
122
+ decrypt(): DecryptedThing<T> {
123
+ return this;
124
+ }
125
+
126
+ equals(other: MaybeEncrypted<T>) {
127
+ if (other instanceof DecryptedThing) {
128
+ return equals(this._data, other._data);
129
+ } else {
130
+ return false;
131
+ }
132
+ }
133
+
134
+ clear() {
135
+ this._value = undefined;
136
+ }
137
+
138
+ get byteLength() {
139
+ return this._data!.byteLength;
140
+ }
141
+ }
142
+
143
+ @variant(0)
144
+ export class CipherWithNonce {
145
+ @field({ type: Uint8Array })
146
+ nonce: Uint8Array;
147
+
148
+ @field({ type: Uint8Array })
149
+ cipher: Uint8Array;
150
+
151
+ constructor(props?: { nonce: Uint8Array; cipher: Uint8Array }) {
152
+ if (props) {
153
+ this.nonce = props.nonce;
154
+ this.cipher = props.cipher;
155
+ }
156
+ }
157
+
158
+ equals(other: CipherWithNonce): boolean {
159
+ if (other instanceof CipherWithNonce) {
160
+ return (
161
+ equals(this.nonce, other.nonce) && equals(this.cipher, other.cipher)
162
+ );
163
+ } else {
164
+ return false;
165
+ }
166
+ }
167
+ }
168
+
169
+ @variant(0)
170
+ export class K {
171
+ @field({ type: CipherWithNonce })
172
+ _encryptedKey: CipherWithNonce;
173
+
174
+ @field({ type: X25519PublicKey })
175
+ _recieverPublicKey: X25519PublicKey;
176
+
177
+ constructor(props?: {
178
+ encryptedKey: CipherWithNonce;
179
+ recieverPublicKey: X25519PublicKey;
180
+ }) {
181
+ if (props) {
182
+ this._encryptedKey = props.encryptedKey;
183
+ this._recieverPublicKey = props.recieverPublicKey;
184
+ }
185
+ }
186
+
187
+ equals(other: K): boolean {
188
+ if (other instanceof K) {
189
+ return (
190
+ this._encryptedKey.equals(other._encryptedKey) &&
191
+ this._recieverPublicKey.equals(other._recieverPublicKey)
192
+ );
193
+ } else {
194
+ return false;
195
+ }
196
+ }
197
+ }
198
+
199
+ @variant(0)
200
+ export class Envelope {
201
+ @field({ type: X25519PublicKey })
202
+ _senderPublicKey: X25519PublicKey;
203
+
204
+ @field({ type: vec(K) })
205
+ _ks: K[];
206
+
207
+ constructor(props?: { senderPublicKey: X25519PublicKey; ks: K[] }) {
208
+ if (props) {
209
+ this._senderPublicKey = props.senderPublicKey;
210
+ this._ks = props.ks;
211
+ }
212
+ }
213
+
214
+ equals(other: Envelope): boolean {
215
+ if (other instanceof Envelope) {
216
+ if (!this._senderPublicKey.equals(other._senderPublicKey)) {
217
+ return false;
218
+ }
219
+
220
+ if (this._ks.length !== other._ks.length) {
221
+ return false;
222
+ }
223
+ for (let i = 0; i < this._ks.length; i++) {
224
+ if (!this._ks[i].equals(other._ks[i])) {
225
+ return false;
226
+ }
227
+ }
228
+ return true;
229
+ } else {
230
+ return false;
231
+ }
232
+ }
233
+ }
234
+
235
+ @variant(1)
236
+ export class EncryptedThing<T> extends MaybeEncrypted<T> {
237
+ @field({ type: Uint8Array })
238
+ _encrypted: Uint8Array;
239
+
240
+ @field({ type: Uint8Array })
241
+ _nonce: Uint8Array;
242
+
243
+ @field({ type: Envelope })
244
+ _envelope: Envelope;
245
+
246
+ constructor(props?: {
247
+ encrypted: Uint8Array;
248
+ nonce: Uint8Array;
249
+ envelope: Envelope;
250
+ }) {
251
+ super();
252
+ if (props) {
253
+ this._encrypted = props.encrypted;
254
+ this._nonce = props.nonce;
255
+ this._envelope = props.envelope;
256
+ }
257
+ }
258
+
259
+ _decrypted?: DecryptedThing<T>;
260
+ get decrypted(): DecryptedThing<T> {
261
+ if (!this._decrypted) {
262
+ throw new Error(
263
+ "Entry has not been decrypted, invoke decrypt method before"
264
+ );
265
+ }
266
+ return this._decrypted;
267
+ }
268
+
269
+ async decrypt(
270
+ keyResolver?: Keychain | X25519Keypair
271
+ ): Promise<DecryptedThing<T>> {
272
+ if (this._decrypted) {
273
+ return this._decrypted;
274
+ }
275
+
276
+ if (!keyResolver) {
277
+ throw new AccessError("Expecting key resolver");
278
+ }
279
+
280
+ // We only need to open with one of the keys
281
+ let key: { index: number; keypair: X25519Keypair } | undefined;
282
+ if (keyResolver instanceof X25519Keypair) {
283
+ for (const [i, k] of this._envelope._ks.entries()) {
284
+ if (k._recieverPublicKey.equals(keyResolver.publicKey)) {
285
+ key = {
286
+ index: i,
287
+ keypair: keyResolver,
288
+ };
289
+ }
290
+ }
291
+ } else {
292
+ for (const [i, k] of this._envelope._ks.entries()) {
293
+ const exported = await keyResolver.exportByKey(k._recieverPublicKey);
294
+ if (exported) {
295
+ key = {
296
+ index: i,
297
+ keypair: exported,
298
+ };
299
+ break;
300
+ }
301
+ }
302
+ }
303
+
304
+ if (key) {
305
+ const k = this._envelope._ks[key.index];
306
+ let secretKey: X25519SecretKey = undefined as any;
307
+ if (key.keypair instanceof X25519Keypair) {
308
+ secretKey = key.keypair.secretKey;
309
+ } else {
310
+ secretKey = await X25519SecretKey.from(key.keypair);
311
+ }
312
+ let epheremalKey: Uint8Array;
313
+ try {
314
+ epheremalKey = sodium.crypto_box_open_easy(
315
+ k._encryptedKey.cipher,
316
+ k._encryptedKey.nonce,
317
+ this._envelope._senderPublicKey.publicKey,
318
+ secretKey.secretKey
319
+ );
320
+ } catch (error) {
321
+ throw new AccessError("Failed to decrypt");
322
+ }
323
+
324
+ // TODO: is nested decryption necessary?
325
+ /* let der: any = this;
326
+ let counter = 0;
327
+ while (der instanceof EncryptedThing) {
328
+ const decrypted = await sodium.crypto_secretbox_open_easy(this._encrypted, this._nonce, epheremalKey);
329
+ der = deserialize(decrypted, DecryptedThing)
330
+ counter += 1;
331
+ if (counter >= 10) {
332
+ throw new Error("Unexpected decryption behaviour, data seems to always be in encrypted state")
333
+ }
334
+ } */
335
+
336
+ const der = deserialize(
337
+ sodium.crypto_secretbox_open_easy(
338
+ this._encrypted,
339
+ this._nonce,
340
+ epheremalKey
341
+ ),
342
+ DecryptedThing
343
+ );
344
+ this._decrypted = der as DecryptedThing<T>;
345
+ } else {
346
+ throw new AccessError("Failed to resolve decryption key");
347
+ }
348
+ return this._decrypted;
349
+ }
350
+
351
+ equals(other: MaybeEncrypted<T>): boolean {
352
+ if (other instanceof EncryptedThing) {
353
+ if (!equals(this._encrypted, other._encrypted)) {
354
+ return false;
355
+ }
356
+ if (!equals(this._nonce, other._nonce)) {
357
+ return false;
358
+ }
359
+
360
+ if (!this._envelope.equals(other._envelope)) {
361
+ return false;
362
+ }
363
+ return true;
364
+ } else {
365
+ return false;
366
+ }
367
+ }
368
+
369
+ clear() {
370
+ this._decrypted = undefined;
371
+ }
372
+
373
+ get byteLength() {
374
+ return this._encrypted.byteLength; // ignore other metdata for now in the size calculation
375
+ }
376
+ }
package/src/errors.ts ADDED
@@ -0,0 +1,5 @@
1
+ export class AccessError extends Error {
2
+ constructor(message?: string) {
3
+ super(message);
4
+ }
5
+ }
@@ -0,0 +1,10 @@
1
+ import { toBase64 } from "./utils";
2
+ import { SHA256 } from "@stablelib/sha256";
3
+ export const sha256Base64 = async (bytes: Uint8Array): Promise<string> =>
4
+ toBase64(await sha256(bytes));
5
+ export const sha256Base64Sync = (bytes: Uint8Array): string =>
6
+ toBase64(new SHA256().update(bytes).digest());
7
+ export const sha256 = async (bytes: Uint8Array): Promise<Uint8Array> =>
8
+ new Uint8Array(await globalThis.crypto.subtle.digest("SHA-256", bytes));
9
+ export const sha256Sync = (bytes: Uint8Array): Uint8Array =>
10
+ new SHA256().update(bytes).digest();
package/src/hash.ts ADDED
@@ -0,0 +1,10 @@
1
+ import crypto from "crypto";
2
+
3
+ export const sha256Base64Sync = (bytes: Uint8Array): string =>
4
+ crypto.createHash("sha256").update(bytes).digest("base64");
5
+ export const sha256Base64 = async (bytes: Uint8Array): Promise<string> =>
6
+ crypto.createHash("sha256").update(bytes).digest("base64");
7
+ export const sha256 = async (bytes: Uint8Array): Promise<Uint8Array> =>
8
+ crypto.createHash("sha256").update(bytes).digest();
9
+ export const sha256Sync = (bytes: Uint8Array): Uint8Array =>
10
+ crypto.createHash("sha256").update(bytes).digest();
package/src/index.ts ADDED
@@ -0,0 +1,14 @@
1
+ export * from "./key.js";
2
+ export * from "./ed25519.js";
3
+ export * from "./signature.js";
4
+ export * from "./key.js";
5
+ export * from "./sepc256k1.js";
6
+ export * from "./x25519.js";
7
+ export * from "./encryption.js";
8
+ export * from "./libp2p.js";
9
+ export * from "./utils.js";
10
+ export * from "./hash.js";
11
+ export * from "./random.js";
12
+ export * from "./prehash.js";
13
+ export * from "./signer.js";
14
+ export * from "./keychain.js";