@wemush/wols 0.1.0

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 (87) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +294 -0
  3. package/dist/cjs/compact-url.js +272 -0
  4. package/dist/cjs/compact-url.js.map +1 -0
  5. package/dist/cjs/crypto/decrypt.js +125 -0
  6. package/dist/cjs/crypto/decrypt.js.map +1 -0
  7. package/dist/cjs/crypto/encrypt.js +151 -0
  8. package/dist/cjs/crypto/encrypt.js.map +1 -0
  9. package/dist/cjs/crypto/index.js +16 -0
  10. package/dist/cjs/crypto/index.js.map +1 -0
  11. package/dist/cjs/errors.js +188 -0
  12. package/dist/cjs/errors.js.map +1 -0
  13. package/dist/cjs/index.js +81 -0
  14. package/dist/cjs/index.js.map +1 -0
  15. package/dist/cjs/parser.js +148 -0
  16. package/dist/cjs/parser.js.map +1 -0
  17. package/dist/cjs/qr/generate.js +275 -0
  18. package/dist/cjs/qr/generate.js.map +1 -0
  19. package/dist/cjs/qr/index.js +18 -0
  20. package/dist/cjs/qr/index.js.map +1 -0
  21. package/dist/cjs/specimen.js +163 -0
  22. package/dist/cjs/specimen.js.map +1 -0
  23. package/dist/cjs/types.js +78 -0
  24. package/dist/cjs/types.js.map +1 -0
  25. package/dist/cjs/utils/cuid.js +150 -0
  26. package/dist/cjs/utils/cuid.js.map +1 -0
  27. package/dist/cjs/utils/iso8601.js +131 -0
  28. package/dist/cjs/utils/iso8601.js.map +1 -0
  29. package/dist/cjs/validator.js +307 -0
  30. package/dist/cjs/validator.js.map +1 -0
  31. package/dist/esm/compact-url.js +264 -0
  32. package/dist/esm/compact-url.js.map +1 -0
  33. package/dist/esm/crypto/decrypt.js +122 -0
  34. package/dist/esm/crypto/decrypt.js.map +1 -0
  35. package/dist/esm/crypto/encrypt.js +147 -0
  36. package/dist/esm/crypto/encrypt.js.map +1 -0
  37. package/dist/esm/crypto/index.js +10 -0
  38. package/dist/esm/crypto/index.js.map +1 -0
  39. package/dist/esm/errors.js +178 -0
  40. package/dist/esm/errors.js.map +1 -0
  41. package/dist/esm/index.js +47 -0
  42. package/dist/esm/index.js.map +1 -0
  43. package/dist/esm/parser.js +145 -0
  44. package/dist/esm/parser.js.map +1 -0
  45. package/dist/esm/qr/generate.js +235 -0
  46. package/dist/esm/qr/generate.js.map +1 -0
  47. package/dist/esm/qr/index.js +10 -0
  48. package/dist/esm/qr/index.js.map +1 -0
  49. package/dist/esm/specimen.js +159 -0
  50. package/dist/esm/specimen.js.map +1 -0
  51. package/dist/esm/types.js +71 -0
  52. package/dist/esm/types.js.map +1 -0
  53. package/dist/esm/utils/cuid.js +145 -0
  54. package/dist/esm/utils/cuid.js.map +1 -0
  55. package/dist/esm/utils/iso8601.js +122 -0
  56. package/dist/esm/utils/iso8601.js.map +1 -0
  57. package/dist/esm/validator.js +304 -0
  58. package/dist/esm/validator.js.map +1 -0
  59. package/dist/types/compact-url.d.ts +104 -0
  60. package/dist/types/compact-url.d.ts.map +1 -0
  61. package/dist/types/crypto/decrypt.d.ts +33 -0
  62. package/dist/types/crypto/decrypt.d.ts.map +1 -0
  63. package/dist/types/crypto/encrypt.d.ts +32 -0
  64. package/dist/types/crypto/encrypt.d.ts.map +1 -0
  65. package/dist/types/crypto/index.d.ts +10 -0
  66. package/dist/types/crypto/index.d.ts.map +1 -0
  67. package/dist/types/errors.d.ts +103 -0
  68. package/dist/types/errors.d.ts.map +1 -0
  69. package/dist/types/index.d.ts +21 -0
  70. package/dist/types/index.d.ts.map +1 -0
  71. package/dist/types/parser.d.ts +30 -0
  72. package/dist/types/parser.d.ts.map +1 -0
  73. package/dist/types/qr/generate.d.ts +62 -0
  74. package/dist/types/qr/generate.d.ts.map +1 -0
  75. package/dist/types/qr/index.d.ts +10 -0
  76. package/dist/types/qr/index.d.ts.map +1 -0
  77. package/dist/types/specimen.d.ts +71 -0
  78. package/dist/types/specimen.d.ts.map +1 -0
  79. package/dist/types/types.d.ts +269 -0
  80. package/dist/types/types.d.ts.map +1 -0
  81. package/dist/types/utils/cuid.d.ts +44 -0
  82. package/dist/types/utils/cuid.d.ts.map +1 -0
  83. package/dist/types/utils/iso8601.d.ts +79 -0
  84. package/dist/types/utils/iso8601.d.ts.map +1 -0
  85. package/dist/types/validator.d.ts +25 -0
  86. package/dist/types/validator.d.ts.map +1 -0
  87. package/package.json +95 -0
@@ -0,0 +1,264 @@
1
+ /**
2
+ * Compact URL Format Functions
3
+ *
4
+ * This module provides functions to generate and parse compact URL format
5
+ * for WOLS specimens. The compact format is designed for small QR codes
6
+ * (petri dishes, culture tubes) where space is limited.
7
+ *
8
+ * Format: wemush://v1/{id}?s={species_code}&st={stage}&t={timestamp}
9
+ *
10
+ * @module compact-url
11
+ */
12
+ import { WolsErrorCode, WolsParseError } from './errors.js';
13
+ /**
14
+ * Standard species codes from the WOLS specification
15
+ *
16
+ * @see https://github.com/wemush/open-standard for additional codes
17
+ */
18
+ export const SPECIES_CODES = {
19
+ POSTR: 'Pleurotus ostreatus',
20
+ PCITRI: 'Pleurotus citrinopileatus',
21
+ PDJAR: 'Pleurotus djamor',
22
+ PERYN: 'Pleurotus eryngii',
23
+ HERIN: 'Hericium erinaceus',
24
+ GLUCI: 'Ganoderma lucidum',
25
+ LPUDE: 'Laetiporus pudens',
26
+ LLENC: 'Lentinula edodes',
27
+ APOLY: 'Agrocybe polyphylla',
28
+ STRUG: 'Stropharia rugosoannulata',
29
+ };
30
+ /**
31
+ * Reverse lookup map: species name → code
32
+ */
33
+ const SPECIES_TO_CODE = {};
34
+ // Initialize reverse lookup
35
+ for (const [code, species] of Object.entries(SPECIES_CODES)) {
36
+ SPECIES_TO_CODE[species] = code;
37
+ }
38
+ /**
39
+ * Get the full species name from a species code
40
+ *
41
+ * @param code - Species code (e.g., "POSTR")
42
+ * @returns Species name or undefined if code is unknown
43
+ *
44
+ * @example
45
+ * ```typescript
46
+ * getSpeciesFromCode('POSTR') // Returns "Pleurotus ostreatus"
47
+ * getSpeciesFromCode('XXXXX') // Returns undefined
48
+ * ```
49
+ */
50
+ export function getSpeciesFromCode(code) {
51
+ return SPECIES_CODES[code];
52
+ }
53
+ /**
54
+ * Get the species code from a species name
55
+ *
56
+ * @param species - Full species name (e.g., "Pleurotus ostreatus")
57
+ * @returns Species code or undefined if species is not registered
58
+ *
59
+ * @example
60
+ * ```typescript
61
+ * getCodeFromSpecies('Pleurotus ostreatus') // Returns "POSTR"
62
+ * getCodeFromSpecies('Unknown species') // Returns undefined
63
+ * ```
64
+ */
65
+ export function getCodeFromSpecies(species) {
66
+ return SPECIES_TO_CODE[species];
67
+ }
68
+ /**
69
+ * Register a custom species code
70
+ *
71
+ * Use this to add species codes that are not in the standard WOLS list.
72
+ *
73
+ * @param code - Species code (uppercase, 5 characters recommended)
74
+ * @param species - Full species name
75
+ *
76
+ * @example
77
+ * ```typescript
78
+ * registerSpeciesCode('CCOMA', 'Coprinus comatus');
79
+ * getSpeciesFromCode('CCOMA') // Returns "Coprinus comatus"
80
+ * ```
81
+ */
82
+ export function registerSpeciesCode(code, species) {
83
+ // Remove old reverse lookup if code is being overwritten
84
+ const oldSpecies = SPECIES_CODES[code];
85
+ if (oldSpecies !== undefined) {
86
+ delete SPECIES_TO_CODE[oldSpecies];
87
+ }
88
+ SPECIES_CODES[code] = species;
89
+ SPECIES_TO_CODE[species] = code;
90
+ }
91
+ /**
92
+ * Generate a compact URL from a specimen
93
+ *
94
+ * The compact URL format is designed for small QR codes:
95
+ * ```
96
+ * wemush://v1/{id}?s={species_code}&st={stage}&t={timestamp}
97
+ * ```
98
+ *
99
+ * @param specimen - The specimen to encode
100
+ * @returns Compact URL string
101
+ *
102
+ * @example
103
+ * ```typescript
104
+ * const specimen = createSpecimen({
105
+ * type: 'CULTURE',
106
+ * species: 'Pleurotus ostreatus',
107
+ * stage: 'COLONIZATION'
108
+ * });
109
+ *
110
+ * const url = toCompactUrl(specimen);
111
+ * // wemush://v1/clx1a2b3c4?s=POSTR&st=COLONIZATION&ty=CULTURE&t=1734307200
112
+ * ```
113
+ */
114
+ export function toCompactUrl(specimen) {
115
+ // Extract ID without prefix
116
+ const idWithoutPrefix = specimen.id.replace(/^wemush:/, '');
117
+ // Start building URL
118
+ let url = `wemush://v1/${idWithoutPrefix}`;
119
+ // Build query parameters
120
+ const params = new URLSearchParams();
121
+ // Species: use code if known, otherwise full name
122
+ const speciesCode = getCodeFromSpecies(specimen.species);
123
+ if (speciesCode !== undefined) {
124
+ params.set('s', speciesCode);
125
+ }
126
+ else {
127
+ params.set('s', specimen.species);
128
+ }
129
+ // Type
130
+ params.set('ty', specimen.type);
131
+ // Stage (if present)
132
+ if (specimen.stage !== undefined) {
133
+ params.set('st', specimen.stage);
134
+ }
135
+ // Timestamp: convert ISO date to Unix timestamp
136
+ if (specimen.created !== undefined) {
137
+ const timestamp = Math.floor(new Date(specimen.created).getTime() / 1000);
138
+ params.set('t', timestamp.toString());
139
+ }
140
+ // Optional: Batch
141
+ if (specimen.batch !== undefined) {
142
+ params.set('b', specimen.batch);
143
+ }
144
+ // Optional: Strain name
145
+ if (specimen.strain?.name !== undefined) {
146
+ params.set('sn', specimen.strain.name);
147
+ }
148
+ // Optional: Strain generation
149
+ if (specimen.strain?.generation !== undefined) {
150
+ params.set('sg', specimen.strain.generation);
151
+ }
152
+ // Append query string
153
+ const queryString = params.toString();
154
+ if (queryString) {
155
+ url += `?${queryString}`;
156
+ }
157
+ return url;
158
+ }
159
+ /**
160
+ * Parse a compact URL into a SpecimenRef
161
+ *
162
+ * Returns a reference object containing the minimal information
163
+ * encoded in the compact URL. Use the id to fetch full specimen
164
+ * data from the API if needed.
165
+ *
166
+ * @param url - Compact URL to parse (wemush://v1/...)
167
+ * @returns ParseResult with SpecimenRef on success, error on failure
168
+ *
169
+ * @example
170
+ * ```typescript
171
+ * const result = parseCompactUrl('wemush://v1/clx1a2b3c4?s=POSTR&st=COLONIZATION');
172
+ *
173
+ * if (result.success) {
174
+ * console.log(result.data.species); // "Pleurotus ostreatus"
175
+ * }
176
+ * ```
177
+ */
178
+ export function parseCompactUrl(url) {
179
+ // Validate URL scheme
180
+ if (!url.startsWith('wemush://')) {
181
+ return {
182
+ success: false,
183
+ error: new WolsParseError(WolsErrorCode.INVALID_URL, 'URL must start with wemush://', {
184
+ url,
185
+ }),
186
+ };
187
+ }
188
+ // Parse URL parts
189
+ const withoutScheme = url.slice('wemush://'.length);
190
+ const [pathPart, queryPart] = withoutScheme.split('?');
191
+ if (!pathPart) {
192
+ return {
193
+ success: false,
194
+ error: new WolsParseError(WolsErrorCode.INVALID_URL, 'Invalid compact URL format: missing path', { url }),
195
+ };
196
+ }
197
+ // Parse path: v1/{id}
198
+ const pathSegments = pathPart.split('/');
199
+ if (pathSegments.length < 2 || !pathSegments[1]) {
200
+ return {
201
+ success: false,
202
+ error: new WolsParseError(WolsErrorCode.INVALID_URL, 'Invalid compact URL format: missing specimen ID', { url }),
203
+ };
204
+ }
205
+ const version = pathSegments[0] === 'v1' ? '1.1.0' : '1.0.0';
206
+ const idWithoutPrefix = pathSegments[1];
207
+ // Parse query parameters
208
+ const params = new URLSearchParams(queryPart ?? '');
209
+ // Extract species (resolve code if known)
210
+ const speciesParam = params.get('s');
211
+ let species;
212
+ if (speciesParam !== null) {
213
+ const resolvedSpecies = getSpeciesFromCode(speciesParam);
214
+ species = resolvedSpecies ?? speciesParam;
215
+ }
216
+ else {
217
+ return {
218
+ success: false,
219
+ error: new WolsParseError(WolsErrorCode.INVALID_URL, 'Invalid compact URL format: missing species parameter', { url }),
220
+ };
221
+ }
222
+ // Build result object
223
+ const result = {
224
+ id: `wemush:${idWithoutPrefix}`,
225
+ species,
226
+ version,
227
+ };
228
+ // Extract type
229
+ const typeParam = params.get('ty');
230
+ if (typeParam !== null) {
231
+ result.type = typeParam;
232
+ }
233
+ // Extract stage
234
+ const stageParam = params.get('st');
235
+ if (stageParam !== null) {
236
+ result.stage = stageParam;
237
+ }
238
+ // Extract timestamp
239
+ const timestampParam = params.get('t');
240
+ if (timestampParam !== null) {
241
+ result.timestamp = parseInt(timestampParam, 10);
242
+ }
243
+ // Extract batch
244
+ const batchParam = params.get('b');
245
+ if (batchParam !== null) {
246
+ result.batch = batchParam;
247
+ }
248
+ // Extract strain info
249
+ const strainNameParam = params.get('sn');
250
+ const strainGenParam = params.get('sg');
251
+ if (strainNameParam !== null) {
252
+ result.strain = {
253
+ name: strainNameParam,
254
+ };
255
+ if (strainGenParam !== null) {
256
+ result.strain.generation = strainGenParam;
257
+ }
258
+ }
259
+ return {
260
+ success: true,
261
+ data: result,
262
+ };
263
+ }
264
+ //# sourceMappingURL=compact-url.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"compact-url.js","sourceRoot":"","sources":["../../src/compact-url.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,122 @@
1
+ /**
2
+ * WOLS Decryption Module
3
+ *
4
+ * Provides AES-256-GCM decryption for encrypted specimens using Web Crypto API.
5
+ * Supports both password-based (PBKDF2) and raw CryptoKey decryption.
6
+ *
7
+ * @module crypto/decrypt
8
+ */
9
+ import { WolsEncryptionError, WolsErrorCode } from '../errors.js';
10
+ import { parseSpecimen } from '../parser.js';
11
+ // =============================================================================
12
+ // CONSTANTS
13
+ // =============================================================================
14
+ /** Salt for PBKDF2 key derivation (must match encrypt.ts) */
15
+ const PBKDF2_SALT = new TextEncoder().encode('@wemush/wols/crypto/v1');
16
+ /** Number of PBKDF2 iterations (must match encrypt.ts) */
17
+ const PBKDF2_ITERATIONS = 100_000;
18
+ // =============================================================================
19
+ // UTILITY FUNCTIONS
20
+ // =============================================================================
21
+ /**
22
+ * Convert base64url string to ArrayBuffer
23
+ */
24
+ function fromBase64Url(base64url) {
25
+ // Convert base64url to base64
26
+ const base64 = base64url
27
+ .replace(/-/g, '+')
28
+ .replace(/_/g, '/')
29
+ .padEnd(base64url.length + ((4 - (base64url.length % 4)) % 4), '=');
30
+ // Decode base64 to binary string
31
+ const binaryString = atob(base64);
32
+ // Convert to ArrayBuffer
33
+ const bytes = new Uint8Array(binaryString.length);
34
+ for (let i = 0; i < binaryString.length; i++) {
35
+ bytes[i] = binaryString.charCodeAt(i);
36
+ }
37
+ return bytes.buffer;
38
+ }
39
+ /**
40
+ * Derive a CryptoKey from a password using PBKDF2
41
+ */
42
+ async function deriveKey(password) {
43
+ const encoder = new TextEncoder();
44
+ const passwordBytes = encoder.encode(password);
45
+ // Import password as key material
46
+ const keyMaterial = await crypto.subtle.importKey('raw', passwordBytes, 'PBKDF2', false, [
47
+ 'deriveKey',
48
+ ]);
49
+ // Derive AES-256-GCM key
50
+ return crypto.subtle.deriveKey({
51
+ name: 'PBKDF2',
52
+ salt: PBKDF2_SALT,
53
+ iterations: PBKDF2_ITERATIONS,
54
+ hash: 'SHA-256',
55
+ }, keyMaterial, { name: 'AES-GCM', length: 256 }, false, ['decrypt']);
56
+ }
57
+ // =============================================================================
58
+ // DECRYPTION FUNCTIONS
59
+ // =============================================================================
60
+ /**
61
+ * Decrypt an encrypted specimen using AES-256-GCM
62
+ *
63
+ * @param encrypted - The encrypted specimen wrapper
64
+ * @param options - Decryption options (key required)
65
+ * @returns Promise resolving to ParseResult with decrypted specimen or error
66
+ *
67
+ * @example
68
+ * ```typescript
69
+ * import { decryptSpecimen } from '@wemush/wols/crypto';
70
+ *
71
+ * const result = await decryptSpecimen(encrypted, {
72
+ * key: 'my-secret-password',
73
+ * });
74
+ *
75
+ * if (result.success) {
76
+ * console.log(result.data);
77
+ * } else {
78
+ * console.error(result.error);
79
+ * }
80
+ * ```
81
+ */
82
+ export async function decryptSpecimen(encrypted, options) {
83
+ const { key } = options;
84
+ // Validate encrypted specimen structure
85
+ if (!encrypted.payload) {
86
+ return {
87
+ success: false,
88
+ error: new WolsEncryptionError(WolsErrorCode.DECRYPTION_ERROR, 'Missing encrypted payload', {
89
+ path: ['payload'],
90
+ }),
91
+ };
92
+ }
93
+ if (!encrypted.iv) {
94
+ return {
95
+ success: false,
96
+ error: new WolsEncryptionError(WolsErrorCode.DECRYPTION_ERROR, 'Missing initialization vector (IV)', { path: ['iv'] }),
97
+ };
98
+ }
99
+ try {
100
+ // Get or derive the decryption key
101
+ const cryptoKey = typeof key === 'string' ? await deriveKey(key) : key;
102
+ // Decode payload and IV from base64url
103
+ const ciphertext = fromBase64Url(encrypted.payload);
104
+ const ivBuffer = fromBase64Url(encrypted.iv);
105
+ const iv = new Uint8Array(ivBuffer);
106
+ // Decrypt
107
+ const plaintext = await crypto.subtle.decrypt({ name: 'AES-GCM', iv }, cryptoKey, ciphertext);
108
+ // Decode plaintext to string
109
+ const json = new TextDecoder().decode(plaintext);
110
+ // Parse the decrypted JSON back into a specimen
111
+ return parseSpecimen(json);
112
+ }
113
+ catch (error) {
114
+ // Handle decryption errors (wrong key, tampered data, etc.)
115
+ const errorMessage = error instanceof Error ? error.message : 'Unknown decryption error';
116
+ return {
117
+ success: false,
118
+ error: new WolsEncryptionError(WolsErrorCode.DECRYPTION_ERROR, `Decryption failed: ${errorMessage}`),
119
+ };
120
+ }
121
+ }
122
+ //# sourceMappingURL=decrypt.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"decrypt.js","sourceRoot":"","sources":["../../../src/crypto/decrypt.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,mBAAmB,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAClE,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAG7C,gFAAgF;AAChF,YAAY;AACZ,gFAAgF;AAEhF,6DAA6D;AAC7D,MAAM,WAAW,GAAG,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,wBAAwB,CAAC,CAAC;AAEvE,0DAA0D;AAC1D,MAAM,iBAAiB,GAAG,OAAO,CAAC;AAElC,gFAAgF;AAChF,oBAAoB;AACpB,gFAAgF;AAEhF;;GAEG;AACH,SAAS,aAAa,CAAC,SAAiB;IACtC,8BAA8B;IAC9B,MAAM,MAAM,GAAG,SAAS;SACrB,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC;SAClB,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC;SAClB,MAAM,CAAC,SAAS,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;IAEtE,iCAAiC;IACjC,MAAM,YAAY,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC;IAElC,yBAAyB;IACzB,MAAM,KAAK,GAAG,IAAI,UAAU,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC;IAClD,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,YAAY,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QAC7C,KAAK,CAAC,CAAC,CAAC,GAAG,YAAY,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;IACxC,CAAC;IAED,OAAO,KAAK,CAAC,MAAM,CAAC;AACtB,CAAC;AAED;;GAEG;AACH,KAAK,UAAU,SAAS,CAAC,QAAgB;IACvC,MAAM,OAAO,GAAG,IAAI,WAAW,EAAE,CAAC;IAClC,MAAM,aAAa,GAAG,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;IAE/C,kCAAkC;IAClC,MAAM,WAAW,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,KAAK,EAAE,aAAa,EAAE,QAAQ,EAAE,KAAK,EAAE;QACvF,WAAW;KACZ,CAAC,CAAC;IAEH,yBAAyB;IACzB,OAAO,MAAM,CAAC,MAAM,CAAC,SAAS,CAC5B;QACE,IAAI,EAAE,QAAQ;QACd,IAAI,EAAE,WAAW;QACjB,UAAU,EAAE,iBAAiB;QAC7B,IAAI,EAAE,SAAS;KAChB,EACD,WAAW,EACX,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE,EAChC,KAAK,EACL,CAAC,SAAS,CAAC,CACZ,CAAC;AACJ,CAAC;AAED,gFAAgF;AAChF,uBAAuB;AACvB,gFAAgF;AAEhF;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe,CACnC,SAA4B,EAC5B,OAA0B;IAE1B,MAAM,EAAE,GAAG,EAAE,GAAG,OAAO,CAAC;IAExB,wCAAwC;IACxC,IAAI,CAAC,SAAS,CAAC,OAAO,EAAE,CAAC;QACvB,OAAO;YACL,OAAO,EAAE,KAAK;YACd,KAAK,EAAE,IAAI,mBAAmB,CAAC,aAAa,CAAC,gBAAgB,EAAE,2BAA2B,EAAE;gBAC1F,IAAI,EAAE,CAAC,SAAS,CAAC;aAClB,CAAC;SACH,CAAC;IACJ,CAAC;IAED,IAAI,CAAC,SAAS,CAAC,EAAE,EAAE,CAAC;QAClB,OAAO;YACL,OAAO,EAAE,KAAK;YACd,KAAK,EAAE,IAAI,mBAAmB,CAC5B,aAAa,CAAC,gBAAgB,EAC9B,oCAAoC,EACpC,EAAE,IAAI,EAAE,CAAC,IAAI,CAAC,EAAE,CACjB;SACF,CAAC;IACJ,CAAC;IAED,IAAI,CAAC;QACH,mCAAmC;QACnC,MAAM,SAAS,GAAG,OAAO,GAAG,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC;QAEvE,uCAAuC;QACvC,MAAM,UAAU,GAAG,aAAa,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;QACpD,MAAM,QAAQ,GAAG,aAAa,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC;QAC7C,MAAM,EAAE,GAAG,IAAI,UAAU,CAAC,QAAQ,CAAC,CAAC;QAEpC,UAAU;QACV,MAAM,SAAS,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,EAAE,EAAE,EAAE,SAAS,EAAE,UAAU,CAAC,CAAC;QAE9F,6BAA6B;QAC7B,MAAM,IAAI,GAAG,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;QAEjD,gDAAgD;QAChD,OAAO,aAAa,CAAC,IAAI,CAAC,CAAC;IAC7B,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,4DAA4D;QAC5D,MAAM,YAAY,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,0BAA0B,CAAC;QAEzF,OAAO;YACL,OAAO,EAAE,KAAK;YACd,KAAK,EAAE,IAAI,mBAAmB,CAC5B,aAAa,CAAC,gBAAgB,EAC9B,sBAAsB,YAAY,EAAE,CACrC;SACF,CAAC;IACJ,CAAC;AACH,CAAC"}
@@ -0,0 +1,147 @@
1
+ /**
2
+ * WOLS Encryption Module
3
+ *
4
+ * Provides AES-256-GCM encryption for specimens using Web Crypto API.
5
+ * Supports both password-based (PBKDF2) and raw CryptoKey encryption.
6
+ *
7
+ * @module crypto/encrypt
8
+ */
9
+ import { serializeSpecimen } from '../specimen.js';
10
+ // =============================================================================
11
+ // CONSTANTS
12
+ // =============================================================================
13
+ /** Salt for PBKDF2 key derivation (fixed for deterministic key from same password) */
14
+ const PBKDF2_SALT = new TextEncoder().encode('@wemush/wols/crypto/v1');
15
+ /** Number of PBKDF2 iterations */
16
+ const PBKDF2_ITERATIONS = 100_000;
17
+ /** IV length in bytes (96 bits = 12 bytes recommended for GCM) */
18
+ const IV_LENGTH = 12;
19
+ // =============================================================================
20
+ // UTILITY FUNCTIONS
21
+ // =============================================================================
22
+ /**
23
+ * Convert ArrayBuffer to base64url string
24
+ */
25
+ function toBase64Url(buffer) {
26
+ const bytes = new Uint8Array(buffer);
27
+ let binary = '';
28
+ for (const byte of bytes) {
29
+ binary += String.fromCharCode(byte);
30
+ }
31
+ return btoa(binary).replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
32
+ }
33
+ /**
34
+ * Derive a CryptoKey from a password using PBKDF2
35
+ */
36
+ async function deriveKey(password) {
37
+ const encoder = new TextEncoder();
38
+ const passwordBytes = encoder.encode(password);
39
+ // Import password as key material
40
+ const keyMaterial = await crypto.subtle.importKey('raw', passwordBytes, 'PBKDF2', false, [
41
+ 'deriveKey',
42
+ ]);
43
+ // Derive AES-256-GCM key
44
+ return crypto.subtle.deriveKey({
45
+ name: 'PBKDF2',
46
+ salt: PBKDF2_SALT,
47
+ iterations: PBKDF2_ITERATIONS,
48
+ hash: 'SHA-256',
49
+ }, keyMaterial, { name: 'AES-GCM', length: 256 }, false, ['encrypt']);
50
+ }
51
+ /**
52
+ * Generate a random initialization vector
53
+ */
54
+ function generateIV() {
55
+ const buffer = new ArrayBuffer(IV_LENGTH);
56
+ const iv = new Uint8Array(buffer);
57
+ crypto.getRandomValues(iv);
58
+ return { iv, ivBuffer: buffer };
59
+ }
60
+ // =============================================================================
61
+ // ENCRYPTION FUNCTIONS
62
+ // =============================================================================
63
+ /**
64
+ * Encrypt a specimen using AES-256-GCM
65
+ *
66
+ * @param specimen - The specimen to encrypt
67
+ * @param options - Encryption options (key required)
68
+ * @returns Promise resolving to encrypted specimen wrapper
69
+ *
70
+ * @example
71
+ * ```typescript
72
+ * import { encryptSpecimen } from '@wemush/wols/crypto';
73
+ *
74
+ * const encrypted = await encryptSpecimen(specimen, {
75
+ * key: 'my-secret-password',
76
+ * });
77
+ * // Returns: { encrypted: true, payload: "...", iv: "...", algorithm: "AES-256-GCM" }
78
+ * ```
79
+ */
80
+ export async function encryptSpecimen(specimen, options) {
81
+ const { key, fields } = options;
82
+ // If partial encryption requested, handle separately
83
+ if (fields !== undefined && fields.length > 0) {
84
+ return encryptPartial(specimen, options);
85
+ }
86
+ // Get or derive the encryption key
87
+ const cryptoKey = typeof key === 'string' ? await deriveKey(key) : key;
88
+ // Serialize the specimen to JSON
89
+ const plaintext = serializeSpecimen(specimen);
90
+ const plaintextBytes = new TextEncoder().encode(plaintext);
91
+ // Generate random IV
92
+ const { iv, ivBuffer } = generateIV();
93
+ // Encrypt
94
+ const ciphertext = await crypto.subtle.encrypt({ name: 'AES-GCM', iv }, cryptoKey, plaintextBytes);
95
+ // Return encrypted specimen
96
+ return {
97
+ encrypted: true,
98
+ payload: toBase64Url(ciphertext),
99
+ iv: toBase64Url(ivBuffer),
100
+ algorithm: 'AES-256-GCM',
101
+ };
102
+ }
103
+ /**
104
+ * Encrypt only specific fields of a specimen
105
+ *
106
+ * Returns a specimen-like object with specified fields encrypted.
107
+ */
108
+ async function encryptPartial(specimen, options) {
109
+ const { key, fields = [] } = options;
110
+ // Get or derive the encryption key
111
+ const cryptoKey = typeof key === 'string' ? await deriveKey(key) : key;
112
+ // Clone the specimen
113
+ const result = { ...specimen };
114
+ // Encrypt each specified field
115
+ for (const field of fields) {
116
+ const value = result[field];
117
+ if (value !== undefined) {
118
+ const plaintext = JSON.stringify(value);
119
+ const plaintextBytes = new TextEncoder().encode(plaintext);
120
+ const { iv, ivBuffer } = generateIV();
121
+ const ciphertext = await crypto.subtle.encrypt({ name: 'AES-GCM', iv }, cryptoKey, plaintextBytes);
122
+ result[field] = {
123
+ encrypted: true,
124
+ payload: toBase64Url(ciphertext),
125
+ iv: toBase64Url(ivBuffer),
126
+ algorithm: 'AES-256-GCM',
127
+ };
128
+ }
129
+ }
130
+ // Cast to EncryptedSpecimen for return type consistency
131
+ // In partial mode, this is actually a hybrid object
132
+ return result;
133
+ }
134
+ /**
135
+ * Type guard to check if an object is an encrypted specimen
136
+ */
137
+ export function isEncrypted(value) {
138
+ if (value === null || value === undefined) {
139
+ return false;
140
+ }
141
+ if (typeof value !== 'object') {
142
+ return false;
143
+ }
144
+ const obj = value;
145
+ return obj['encrypted'] === true;
146
+ }
147
+ //# sourceMappingURL=encrypt.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"encrypt.js","sourceRoot":"","sources":["../../../src/crypto/encrypt.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ /**
2
+ * WOLS Crypto Module
3
+ *
4
+ * Provides AES-256-GCM encryption and decryption for specimens.
5
+ *
6
+ * @module crypto
7
+ */
8
+ export { encryptSpecimen, isEncrypted } from './encrypt.js';
9
+ export { decryptSpecimen } from './decrypt.js';
10
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/crypto/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,eAAe,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAC5D,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC"}