space-data-module-sdk 0.4.1 → 0.5.4

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.
@@ -6,6 +6,8 @@ import {
6
6
  toUint8Array,
7
7
  } from "../utils/encoding.js";
8
8
  import {
9
+ aesCtrDecrypt,
10
+ aesCtrEncrypt,
9
11
  aesGcmDecrypt,
10
12
  aesGcmEncrypt,
11
13
  hkdfBytes,
@@ -13,6 +15,13 @@ import {
13
15
  x25519PublicKey,
14
16
  x25519SharedSecret,
15
17
  } from "../utils/wasmCrypto.js";
18
+ import {
19
+ appendPublicationRecordCollection,
20
+ createEncryptedEnvelopePayload,
21
+ decodeEncRecord,
22
+ encodePublicationRecordCollection,
23
+ extractPublicationRecordCollection,
24
+ } from "./records.js";
16
25
 
17
26
  function normalizePublicKey(value) {
18
27
  if (typeof value === "string") {
@@ -53,15 +62,12 @@ export async function generateX25519Keypair() {
53
62
  };
54
63
  }
55
64
 
56
- export async function encryptBytesForRecipient({
65
+ async function encryptBytesLegacy({
57
66
  plaintext,
58
67
  recipientPublicKey,
59
68
  context = "space-data-module-sdk/package",
60
69
  senderKeyPair = null,
61
70
  } = {}) {
62
- if (!recipientPublicKey) {
63
- throw new Error("encryptBytesForRecipient requires recipientPublicKey.");
64
- }
65
71
  const sender = senderKeyPair ?? (await generateX25519Keypair());
66
72
  const salt = await randomBytes(32);
67
73
  const iv = await randomBytes(12);
@@ -89,6 +95,77 @@ export async function encryptBytesForRecipient({
89
95
  };
90
96
  }
91
97
 
98
+ export async function decryptProtectedBytes({
99
+ protectedBytes,
100
+ recipientPrivateKey,
101
+ } = {}) {
102
+ const parsed = extractPublicationRecordCollection(protectedBytes);
103
+ if (!parsed?.enc) {
104
+ return toUint8Array(protectedBytes);
105
+ }
106
+ const sharedSecret = await deriveSharedSecret(
107
+ recipientPrivateKey,
108
+ parsed.enc.ephemeralPublicKey,
109
+ );
110
+ const aesKey = await deriveAesKey(
111
+ sharedSecret,
112
+ new Uint8Array(0),
113
+ parsed.enc.context ?? "",
114
+ );
115
+ return aesCtrDecrypt(aesKey, parsed.payloadBytes, parsed.enc.nonceStart);
116
+ }
117
+
118
+ export async function encryptBytesForRecipient({
119
+ plaintext,
120
+ recipientPublicKey,
121
+ context = "space-data-module-sdk/package",
122
+ senderKeyPair = null,
123
+ recipientKeyId = null,
124
+ schemaHash = null,
125
+ rootType = null,
126
+ } = {}) {
127
+ if (!recipientPublicKey) {
128
+ throw new Error("encryptBytesForRecipient requires recipientPublicKey.");
129
+ }
130
+ const sender = senderKeyPair ?? (await generateX25519Keypair());
131
+ const nonceStart = await randomBytes(12);
132
+ const sharedSecret = await deriveSharedSecret(
133
+ sender.privateKey,
134
+ recipientPublicKey,
135
+ );
136
+ const aesKey = await deriveAesKey(sharedSecret, new Uint8Array(0), context);
137
+ const ciphertext = await aesCtrEncrypt(aesKey, toUint8Array(plaintext), nonceStart);
138
+ const enc = {
139
+ version: 1,
140
+ keyExchange: "X25519",
141
+ symmetric: "AES_256_CTR",
142
+ keyDerivation: "HKDF_SHA256",
143
+ ephemeralPublicKey: sender.publicKey,
144
+ nonceStart,
145
+ recipientKeyId,
146
+ context,
147
+ schemaHash,
148
+ rootType,
149
+ timestamp: Date.now(),
150
+ };
151
+ const recordCollectionBytes = encodePublicationRecordCollection({ enc });
152
+ const protectedBlobBytes = appendPublicationRecordCollection(
153
+ ciphertext,
154
+ recordCollectionBytes,
155
+ );
156
+ return createEncryptedEnvelopePayload({
157
+ protectedBlobBytes,
158
+ parsedProtectedBlob: {
159
+ payloadBytes: ciphertext,
160
+ recordCollectionBytes,
161
+ enc,
162
+ pnm: null,
163
+ },
164
+ enc,
165
+ context,
166
+ });
167
+ }
168
+
92
169
  export async function decryptBytesFromEnvelope({
93
170
  envelope,
94
171
  recipientPrivateKey,
@@ -98,6 +175,29 @@ export async function decryptBytesFromEnvelope({
98
175
  "decryptBytesFromEnvelope requires envelope and recipientPrivateKey.",
99
176
  );
100
177
  }
178
+ if (envelope.protectedBlobBase64) {
179
+ return decryptProtectedBytes({
180
+ protectedBytes: base64ToBytes(envelope.protectedBlobBase64),
181
+ recipientPrivateKey,
182
+ });
183
+ }
184
+ if (envelope.ciphertextBase64 && envelope.encRecordBase64) {
185
+ const enc = decodeEncRecord(base64ToBytes(envelope.encRecordBase64));
186
+ const sharedSecret = await deriveSharedSecret(
187
+ recipientPrivateKey,
188
+ enc.ephemeralPublicKey,
189
+ );
190
+ const aesKey = await deriveAesKey(
191
+ sharedSecret,
192
+ new Uint8Array(0),
193
+ enc.context ?? envelope.context ?? "",
194
+ );
195
+ return aesCtrDecrypt(
196
+ aesKey,
197
+ base64ToBytes(envelope.ciphertextBase64),
198
+ enc.nonceStart,
199
+ );
200
+ }
101
201
  const sharedSecret = await deriveSharedSecret(
102
202
  recipientPrivateKey,
103
203
  base64ToBytes(envelope.senderPublicKeyBase64),
@@ -133,3 +233,24 @@ export async function decryptJsonFromEnvelope(options = {}) {
133
233
  return JSON.parse(new TextDecoder().decode(bytes));
134
234
  }
135
235
 
236
+ export async function decryptPublicationRecordCollection({
237
+ protectedBytes,
238
+ recipientPrivateKey,
239
+ } = {}) {
240
+ const parsed = extractPublicationRecordCollection(protectedBytes);
241
+ if (!parsed) {
242
+ return {
243
+ payloadBytes: toUint8Array(protectedBytes),
244
+ decryptedBytes: toUint8Array(protectedBytes),
245
+ publication: null,
246
+ };
247
+ }
248
+ const decryptedBytes = parsed.enc
249
+ ? await decryptProtectedBytes({ protectedBytes, recipientPrivateKey })
250
+ : parsed.payloadBytes;
251
+ return {
252
+ payloadBytes: parsed.payloadBytes,
253
+ decryptedBytes,
254
+ publication: parsed,
255
+ };
256
+ }