@aikdna/kdna-core 0.7.2 → 0.9.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.
- package/package.json +5 -1
- package/src/asset-reader.js +81 -31
- package/src/crypto-profile.js +185 -0
- package/src/index.js +2 -0
- package/src/lint-pure.js +2 -2
- package/src/types.d.ts +1 -1
- package/src/workpack-pure.js +254 -0
- package/schema/Composition_Policy.schema.json +0 -130
- package/schema/KDNA_Cases.schema.json +0 -77
- package/schema/KDNA_Cluster.schema.json +0 -132
- package/schema/KDNA_Core.schema.json +0 -286
- package/schema/KDNA_Core.strict.schema.json +0 -290
- package/schema/KDNA_Evolution.schema.json +0 -129
- package/schema/KDNA_Patterns.schema.json +0 -338
- package/schema/KDNA_Patterns.strict.schema.json +0 -342
- package/schema/KDNA_Reasoning.schema.json +0 -76
- package/schema/KDNA_Scenarios.schema.json +0 -112
- package/schema/KDNA_Scenarios.strict.schema.json +0 -101
- package/schema/eval.schema.json +0 -58
- package/schema/kdna-file.schema.json +0 -272
- package/schema/kdna-manifest-v1rc.json +0 -346
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aikdna/kdna-core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.0",
|
|
4
4
|
"description": "KDNA core library — pure logic for loading, validating, linting, and rendering KDNA domain cognition packages. Zero Node.js dependencies.",
|
|
5
5
|
"type": "commonjs",
|
|
6
6
|
"main": "src/index.js",
|
|
@@ -47,5 +47,9 @@
|
|
|
47
47
|
"ajv-formats": {
|
|
48
48
|
"optional": true
|
|
49
49
|
}
|
|
50
|
+
},
|
|
51
|
+
"dependencies": {
|
|
52
|
+
"@noble/hashes": "^2.2.0",
|
|
53
|
+
"cbor-x": "^1.6.4"
|
|
50
54
|
}
|
|
51
55
|
}
|
package/src/asset-reader.js
CHANGED
|
@@ -1,27 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* KDNA Asset Reader — direct .kdna container access.
|
|
3
|
-
*
|
|
4
|
-
* This module intentionally uses only Node.js built-ins. It reads ZIP central
|
|
5
|
-
* directory records directly so runtimes can inspect, verify, and load .kdna
|
|
6
|
-
* assets without persistent extraction to a domain directory.
|
|
7
3
|
*/
|
|
8
4
|
|
|
9
5
|
const fs = require('fs');
|
|
10
6
|
const crypto = require('crypto');
|
|
11
7
|
const zlib = require('zlib');
|
|
8
|
+
const cbor = require('cbor-x');
|
|
12
9
|
const { loadDomainFromFiles, formatContext } = require('./loader');
|
|
13
10
|
|
|
14
|
-
const STANDARD_ENTRIES = [
|
|
15
|
-
'kdna.json',
|
|
16
|
-
'KDNA_Core.json',
|
|
17
|
-
'KDNA_Patterns.json',
|
|
18
|
-
'KDNA_Scenarios.json',
|
|
19
|
-
'KDNA_Cases.json',
|
|
20
|
-
'KDNA_Reasoning.json',
|
|
21
|
-
'KDNA_Evolution.json',
|
|
22
|
-
];
|
|
23
|
-
|
|
24
|
-
const JSON_ENTRY_RE = /\.json$/i;
|
|
25
11
|
const KDNA_MEDIA_TYPE = 'application/vnd.aikdna.kdna+zip';
|
|
26
12
|
|
|
27
13
|
function sha256Hex(buf) {
|
|
@@ -274,6 +260,53 @@ function verifyMediaType(asset, errors) {
|
|
|
274
260
|
}
|
|
275
261
|
}
|
|
276
262
|
|
|
263
|
+
/**
|
|
264
|
+
* Verify Human Lock signatures on locked axiom cards.
|
|
265
|
+
* Reconstructs signing payload (cardId|statement|fingerprint) and verifies
|
|
266
|
+
* against the creator's public key from kdna.json manifest.author.
|
|
267
|
+
*/
|
|
268
|
+
function verifyHumanLockSignatures(coreData, manifest, errors, warnings) {
|
|
269
|
+
const publicKeyPEM = manifest?.author?.public_key_pem;
|
|
270
|
+
if (!publicKeyPEM) {
|
|
271
|
+
// Skip if no public key available (unsigned assets are valid)
|
|
272
|
+
return;
|
|
273
|
+
}
|
|
274
|
+
const axioms = coreData?.axioms || [];
|
|
275
|
+
if (!Array.isArray(axioms) || axioms.length === 0) return;
|
|
276
|
+
|
|
277
|
+
let verified = 0, missing = 0, invalid = 0;
|
|
278
|
+
for (const ax of axioms) {
|
|
279
|
+
const hl = ax.human_lock;
|
|
280
|
+
if (!hl || !hl.signature) {
|
|
281
|
+
// Only warn for locked cards without signature
|
|
282
|
+
if (ax.status === 'locked' || ax.status === 'tested' || ax.status === 'published') {
|
|
283
|
+
missing++;
|
|
284
|
+
}
|
|
285
|
+
continue;
|
|
286
|
+
}
|
|
287
|
+
try {
|
|
288
|
+
const payload = [ax.id, hl.statement || '', hl.judgment_fingerprint || ''].join('\n');
|
|
289
|
+
const sig = Buffer.from(String(hl.signature).replace(/^ed25519:/, ''), 'hex');
|
|
290
|
+
const key = crypto.createPublicKey(publicKeyPEM);
|
|
291
|
+
const ok = crypto.verify(null, Buffer.from(payload), key, sig);
|
|
292
|
+
if (ok) { verified++; }
|
|
293
|
+
else { invalid++; }
|
|
294
|
+
} catch {
|
|
295
|
+
invalid++;
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
if (invalid > 0) {
|
|
300
|
+
errors.push(`${invalid} Human Lock signature(s) failed verification — judgment may have been altered`);
|
|
301
|
+
}
|
|
302
|
+
if (missing > 0) {
|
|
303
|
+
warnings.push(`${missing} locked card(s) have no Human Lock signature`);
|
|
304
|
+
}
|
|
305
|
+
if (verified > 0 && invalid === 0) {
|
|
306
|
+
// All signed locks verified — good
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
|
|
277
310
|
function validateManifestIdentity(manifest, errors, _warnings) {
|
|
278
311
|
if (manifest.kdna_spec) {
|
|
279
312
|
errors.push('kdna.json: kdna_spec is not allowed. Use spec_version.');
|
|
@@ -281,9 +314,9 @@ function validateManifestIdentity(manifest, errors, _warnings) {
|
|
|
281
314
|
if (manifest.format && manifest.format !== 'kdna') {
|
|
282
315
|
errors.push(`kdna.json.format: invalid value "${manifest.format}". Expected "kdna".`);
|
|
283
316
|
}
|
|
284
|
-
if (manifest.format_version && manifest.format_version !== '
|
|
317
|
+
if (manifest.format_version && manifest.format_version !== '2.0') {
|
|
285
318
|
errors.push(
|
|
286
|
-
`kdna.json.format_version: invalid value "${manifest.format_version}". Expected "
|
|
319
|
+
`kdna.json.format_version: invalid value "${manifest.format_version}". Expected "2.0".`,
|
|
287
320
|
);
|
|
288
321
|
}
|
|
289
322
|
if (!manifest.spec_version) errors.push('kdna.json: missing required field "spec_version"');
|
|
@@ -323,19 +356,30 @@ function readManifest(asset) {
|
|
|
323
356
|
return parseJson(asset.readEntry('kdna.json'), 'kdna.json');
|
|
324
357
|
}
|
|
325
358
|
|
|
326
|
-
function readDataMapSync(asset,
|
|
359
|
+
function readDataMapSync(asset, options = {}) {
|
|
327
360
|
const dataMap = {};
|
|
328
361
|
const manifest = readManifest(asset);
|
|
329
|
-
|
|
330
|
-
if (
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
362
|
+
|
|
363
|
+
if (asset.entries.has('payload.kdnab')) {
|
|
364
|
+
const payloadBuf = asset.readEntry('payload.kdnab');
|
|
365
|
+
const payload = cbor.decode(payloadBuf);
|
|
366
|
+
if (payload && payload.judgment) {
|
|
367
|
+
if (payload.judgment.core) dataMap['KDNA_Core.json'] = payload.judgment.core;
|
|
368
|
+
if (payload.judgment.patterns) dataMap['KDNA_Patterns.json'] = payload.judgment.patterns;
|
|
369
|
+
if (payload.judgment.scenarios) dataMap['KDNA_Scenarios.json'] = payload.judgment.scenarios;
|
|
370
|
+
if (payload.judgment.cases) dataMap['KDNA_Cases.json'] = payload.judgment.cases;
|
|
371
|
+
if (payload.judgment.reasoning) dataMap['KDNA_Reasoning.json'] = payload.judgment.reasoning;
|
|
372
|
+
if (payload.judgment.evolution) dataMap['KDNA_Evolution.json'] = payload.judgment.evolution;
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
const encrypted = encryptedEntries(manifest);
|
|
376
|
+
if (encrypted.length && typeof options.decryptEntry !== 'function') {
|
|
377
|
+
throw new Error(`encrypted entries require decryptEntry hook: ${encrypted.join(', ')}`);
|
|
378
|
+
}
|
|
379
|
+
return dataMap;
|
|
337
380
|
}
|
|
338
|
-
|
|
381
|
+
|
|
382
|
+
throw new Error('Not a KDNA asset: missing payload.kdnab');
|
|
339
383
|
}
|
|
340
384
|
|
|
341
385
|
function verifySync(asset, options = {}) {
|
|
@@ -345,10 +389,7 @@ function verifySync(asset, options = {}) {
|
|
|
345
389
|
|
|
346
390
|
if (!asset.entries.has('kdna.json')) errors.push('required entry missing: kdna.json');
|
|
347
391
|
verifyMediaType(asset, errors);
|
|
348
|
-
if (!asset.entries.has('
|
|
349
|
-
if (!asset.entries.has('KDNA_Patterns.json')) {
|
|
350
|
-
errors.push('required entry missing: KDNA_Patterns.json');
|
|
351
|
-
}
|
|
392
|
+
if (!asset.entries.has('payload.kdnab')) errors.push('required entry missing: payload.kdnab');
|
|
352
393
|
|
|
353
394
|
const content_digest = buildContentDigest(asset);
|
|
354
395
|
const asset_digest = asset.asset_digest;
|
|
@@ -395,6 +436,15 @@ function verifySync(asset, options = {}) {
|
|
|
395
436
|
if (options.requireSignature || manifest.signature) {
|
|
396
437
|
signature_valid = verifySignature(asset, manifest, errors, warnings);
|
|
397
438
|
}
|
|
439
|
+
// ── Human Lock signature verification ──────────────────
|
|
440
|
+
if (asset.entries.has('KDNA_Core.json')) {
|
|
441
|
+
try {
|
|
442
|
+
const coreData = parseJson(asset.readEntry('KDNA_Core.json'), 'KDNA_Core.json');
|
|
443
|
+
verifyHumanLockSignatures(coreData, manifest, errors, warnings);
|
|
444
|
+
} catch (e) {
|
|
445
|
+
warnings.push(`Human Lock signature check skipped: ${e.message}`);
|
|
446
|
+
}
|
|
447
|
+
}
|
|
398
448
|
} catch (e) {
|
|
399
449
|
errors.push(e.message);
|
|
400
450
|
}
|
package/src/crypto-profile.js
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
const crypto = require('crypto');
|
|
2
2
|
|
|
3
|
+
let argon2id;
|
|
4
|
+
try {
|
|
5
|
+
({ argon2id } = require('@noble/hashes/argon2.js'));
|
|
6
|
+
} catch {
|
|
7
|
+
// Optional: password-protected assets require @noble/hashes
|
|
8
|
+
}
|
|
9
|
+
|
|
3
10
|
// ── Profile constants ──────────────────────────────────────────────
|
|
4
11
|
|
|
5
12
|
/**
|
|
@@ -18,9 +25,19 @@ const LICENSED_ENTRY_PROFILE = 'kdna-licensed-entry-v1';
|
|
|
18
25
|
*/
|
|
19
26
|
const LICENSED_EXPERIMENTAL_PROFILE = 'kdna-licensed-entry-experimental';
|
|
20
27
|
|
|
28
|
+
/**
|
|
29
|
+
* RFC-0009 compliant profile.
|
|
30
|
+
* - Argon2id password-based key derivation
|
|
31
|
+
* - AES-256-KW content encryption key wrapping
|
|
32
|
+
* - AES-256-GCM content encryption
|
|
33
|
+
* - Dual key slots: password + recovery
|
|
34
|
+
*/
|
|
35
|
+
const PASSWORD_PROTECTED_PROFILE = 'kdna-password-protected-v1';
|
|
36
|
+
|
|
21
37
|
const RFC_KDF = 'HKDF-SHA256';
|
|
22
38
|
const RFC_KEY_WRAPPING = 'AES-256-KW';
|
|
23
39
|
const LEGACY_KDF = 'scrypt-sha256';
|
|
40
|
+
const PASSWORD_KDF = 'Argon2id';
|
|
24
41
|
const ALG = 'AES-256-GCM';
|
|
25
42
|
|
|
26
43
|
// ── Helpers ───────────────────────────────────────────────────────
|
|
@@ -246,6 +263,163 @@ function decryptLicensedEntryV1(envelopeValue, options = {}) {
|
|
|
246
263
|
]);
|
|
247
264
|
}
|
|
248
265
|
|
|
266
|
+
// ── RFC-0009: Password-protected encryption ───────────────────────
|
|
267
|
+
|
|
268
|
+
function ensureArgon2id() {
|
|
269
|
+
if (!argon2id) {
|
|
270
|
+
throw new Error(
|
|
271
|
+
'password-protected assets require @noble/hashes. Install: npm install @noble/hashes',
|
|
272
|
+
);
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
function derivePasswordKey(password, params = {}) {
|
|
277
|
+
ensureArgon2id();
|
|
278
|
+
const {
|
|
279
|
+
salt,
|
|
280
|
+
memory_kib = 65536,
|
|
281
|
+
iterations = 3,
|
|
282
|
+
parallelism = 4,
|
|
283
|
+
} = params;
|
|
284
|
+
if (!salt) throw new Error('salt is required for Argon2id');
|
|
285
|
+
const saltBuf = decodeBase64(salt, 'salt');
|
|
286
|
+
const passwordBuf = toBuffer(password, 'password');
|
|
287
|
+
const key = argon2id(passwordBuf, saltBuf, {
|
|
288
|
+
t: iterations,
|
|
289
|
+
m: memory_kib,
|
|
290
|
+
p: parallelism,
|
|
291
|
+
dkLen: 32,
|
|
292
|
+
});
|
|
293
|
+
return Buffer.from(key);
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
function generateRecoveryCode() {
|
|
297
|
+
const raw = crypto.randomBytes(32); // 256 bits
|
|
298
|
+
const hex = raw.toString('hex').toUpperCase();
|
|
299
|
+
const groups = hex.match(/.{4}/g);
|
|
300
|
+
return `kdna-recover-${groups.join('-')}`;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
function decodeRecoveryCode(code) {
|
|
304
|
+
if (typeof code !== 'string' || !code.startsWith('kdna-recover-')) {
|
|
305
|
+
throw new Error('recovery code must start with "kdna-recover-"');
|
|
306
|
+
}
|
|
307
|
+
const hex = code.slice('kdna-recover-'.length).replace(/-/g, '');
|
|
308
|
+
if (!/^[0-9A-Fa-f]{64}$/.test(hex)) {
|
|
309
|
+
throw new Error('recovery code format is invalid');
|
|
310
|
+
}
|
|
311
|
+
return Buffer.from(hex, 'hex');
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
function encryptProtectedEntry(plaintext, options = {}) {
|
|
315
|
+
const { entryName, manifest = {}, password, includeRecovery = true, recoveryCode } = options;
|
|
316
|
+
if (!entryName) throw new Error('entryName is required');
|
|
317
|
+
if (!password) throw new Error('password is required for protected encryption');
|
|
318
|
+
|
|
319
|
+
const cek = generateCEK();
|
|
320
|
+
|
|
321
|
+
// Password slot
|
|
322
|
+
const salt = crypto.randomBytes(16);
|
|
323
|
+
const passwordKdf = {
|
|
324
|
+
name: PASSWORD_KDF,
|
|
325
|
+
salt: salt.toString('base64'),
|
|
326
|
+
memory_kib: 65536,
|
|
327
|
+
iterations: 3,
|
|
328
|
+
parallelism: 4,
|
|
329
|
+
};
|
|
330
|
+
const passwordKey = derivePasswordKey(password, passwordKdf);
|
|
331
|
+
const passwordWrappedKey = wrapCEK(cek, passwordKey);
|
|
332
|
+
|
|
333
|
+
const keySlots = [
|
|
334
|
+
{
|
|
335
|
+
slot: 'password',
|
|
336
|
+
wrap: RFC_KEY_WRAPPING,
|
|
337
|
+
wrapped_key: passwordWrappedKey.toString('base64'),
|
|
338
|
+
},
|
|
339
|
+
];
|
|
340
|
+
|
|
341
|
+
// Recovery slot
|
|
342
|
+
if (includeRecovery) {
|
|
343
|
+
const recoveryKey = recoveryCode ? decodeRecoveryCode(recoveryCode) : crypto.randomBytes(32);
|
|
344
|
+
const recoveryWrappedKey = wrapCEK(cek, recoveryKey);
|
|
345
|
+
keySlots.push({
|
|
346
|
+
slot: 'recovery',
|
|
347
|
+
wrap: RFC_KEY_WRAPPING,
|
|
348
|
+
wrapped_key: recoveryWrappedKey.toString('base64'),
|
|
349
|
+
});
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
const iv = crypto.randomBytes(12);
|
|
353
|
+
const cipher = crypto.createCipheriv('aes-256-gcm', cek, iv);
|
|
354
|
+
cipher.setAAD(encryptedEntryAad(entryName, manifest, PASSWORD_PROTECTED_PROFILE));
|
|
355
|
+
const ciphertext = Buffer.concat([cipher.update(toBuffer(plaintext, 'plaintext')), cipher.final()]);
|
|
356
|
+
|
|
357
|
+
return {
|
|
358
|
+
profile: PASSWORD_PROTECTED_PROFILE,
|
|
359
|
+
alg: ALG,
|
|
360
|
+
kdf: PASSWORD_KDF,
|
|
361
|
+
key_wrapping: RFC_KEY_WRAPPING,
|
|
362
|
+
password_kdf: passwordKdf,
|
|
363
|
+
key_slots: keySlots,
|
|
364
|
+
iv: iv.toString('base64'),
|
|
365
|
+
tag: cipher.getAuthTag().toString('base64'),
|
|
366
|
+
ciphertext: ciphertext.toString('base64'),
|
|
367
|
+
};
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
function decryptProtectedEntry(envelopeValue, options = {}) {
|
|
371
|
+
const { entryName, manifest = {}, password, recoveryCode } = options;
|
|
372
|
+
if (!entryName) throw new Error('entryName is required');
|
|
373
|
+
if (!password && !recoveryCode) {
|
|
374
|
+
throw new Error('password or recoveryCode is required for protected decryption');
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
const envelope = normalizeEnvelope(envelopeValue);
|
|
378
|
+
if (envelope.profile !== PASSWORD_PROTECTED_PROFILE) {
|
|
379
|
+
throw new Error(
|
|
380
|
+
`unsupported encrypted entry profile: ${envelope.profile || 'unknown'} (expected ${PASSWORD_PROTECTED_PROFILE})`,
|
|
381
|
+
);
|
|
382
|
+
}
|
|
383
|
+
if (envelope.alg !== ALG) throw new Error(`unsupported encrypted entry alg: ${envelope.alg}`);
|
|
384
|
+
if (envelope.kdf !== PASSWORD_KDF) throw new Error(`unsupported encrypted entry kdf: ${envelope.kdf}`);
|
|
385
|
+
if (envelope.key_wrapping !== RFC_KEY_WRAPPING) {
|
|
386
|
+
throw new Error(`unsupported encrypted entry key_wrapping: ${envelope.key_wrapping}`);
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
let cek;
|
|
390
|
+
if (password) {
|
|
391
|
+
const passwordKey = derivePasswordKey(password, envelope.password_kdf);
|
|
392
|
+
const passwordSlot = envelope.key_slots.find((s) => s.slot === 'password');
|
|
393
|
+
if (!passwordSlot) throw new Error('password slot missing from envelope');
|
|
394
|
+
cek = unwrapCEK(decodeBase64(passwordSlot.wrapped_key, 'wrapped_key'), passwordKey);
|
|
395
|
+
} else {
|
|
396
|
+
const recoveryKey = decodeRecoveryCode(recoveryCode);
|
|
397
|
+
const recoverySlot = envelope.key_slots.find((s) => s.slot === 'recovery');
|
|
398
|
+
if (!recoverySlot) throw new Error('recovery slot missing from envelope');
|
|
399
|
+
cek = unwrapCEK(decodeBase64(recoverySlot.wrapped_key, 'wrapped_key'), recoveryKey);
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
const decipher = crypto.createDecipheriv('aes-256-gcm', cek, decodeBase64(envelope.iv, 'iv'));
|
|
403
|
+
decipher.setAAD(encryptedEntryAad(entryName, manifest, PASSWORD_PROTECTED_PROFILE));
|
|
404
|
+
decipher.setAuthTag(decodeBase64(envelope.tag, 'tag'));
|
|
405
|
+
return Buffer.concat([
|
|
406
|
+
decipher.update(decodeBase64(envelope.ciphertext, 'ciphertext')),
|
|
407
|
+
decipher.final(),
|
|
408
|
+
]);
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
function createPasswordDecryptEntry(options = {}) {
|
|
412
|
+
const { password } = options;
|
|
413
|
+
return ({ entryName, ciphertext, manifest }) =>
|
|
414
|
+
decryptProtectedEntry(ciphertext, { entryName, manifest, password });
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
function createRecoveryDecryptEntry(options = {}) {
|
|
418
|
+
const { recoveryCode } = options;
|
|
419
|
+
return ({ entryName, ciphertext, manifest }) =>
|
|
420
|
+
decryptProtectedEntry(ciphertext, { entryName, manifest, recoveryCode });
|
|
421
|
+
}
|
|
422
|
+
|
|
249
423
|
// ── Legacy / experimental profile (pre-RFC, backward compat) ───────
|
|
250
424
|
|
|
251
425
|
function deriveLicensedEntryKeyLegacy(options = {}) {
|
|
@@ -329,10 +503,12 @@ module.exports = {
|
|
|
329
503
|
// Profiles
|
|
330
504
|
LICENSED_ENTRY_PROFILE,
|
|
331
505
|
LICENSED_EXPERIMENTAL_PROFILE,
|
|
506
|
+
PASSWORD_PROTECTED_PROFILE,
|
|
332
507
|
ALG,
|
|
333
508
|
RFC_KDF,
|
|
334
509
|
RFC_KEY_WRAPPING,
|
|
335
510
|
LEGACY_KDF,
|
|
511
|
+
PASSWORD_KDF,
|
|
336
512
|
|
|
337
513
|
// RFC-0008 compliant
|
|
338
514
|
deriveWrappingKey,
|
|
@@ -342,6 +518,15 @@ module.exports = {
|
|
|
342
518
|
encryptLicensedEntryV1,
|
|
343
519
|
decryptLicensedEntryV1,
|
|
344
520
|
|
|
521
|
+
// RFC-0009 compliant
|
|
522
|
+
derivePasswordKey,
|
|
523
|
+
generateRecoveryCode,
|
|
524
|
+
decodeRecoveryCode,
|
|
525
|
+
encryptProtectedEntry,
|
|
526
|
+
decryptProtectedEntry,
|
|
527
|
+
createPasswordDecryptEntry,
|
|
528
|
+
createRecoveryDecryptEntry,
|
|
529
|
+
|
|
345
530
|
// Legacy
|
|
346
531
|
deriveLicensedEntryKey: deriveLicensedEntryKeyLegacy,
|
|
347
532
|
encryptLicensedEntryLegacy,
|
package/src/index.js
CHANGED
|
@@ -9,6 +9,7 @@ const compose = require('./compose');
|
|
|
9
9
|
const assetReader = require('./asset-reader');
|
|
10
10
|
const cryptoProfile = require('./crypto-profile');
|
|
11
11
|
const publicApi = require('./public-api');
|
|
12
|
+
const workpackPure = require('./workpack-pure');
|
|
12
13
|
|
|
13
14
|
module.exports = {
|
|
14
15
|
...publicApi,
|
|
@@ -19,4 +20,5 @@ module.exports = {
|
|
|
19
20
|
...compose,
|
|
20
21
|
...assetReader,
|
|
21
22
|
...cryptoProfile,
|
|
23
|
+
...workpackPure,
|
|
22
24
|
};
|
package/src/lint-pure.js
CHANGED
|
@@ -318,9 +318,9 @@ function validateManifest(manifest) {
|
|
|
318
318
|
if (manifest.format && manifest.format !== 'kdna') {
|
|
319
319
|
errors.push(`kdna.json.format: invalid value "${manifest.format}". Expected "kdna".`);
|
|
320
320
|
}
|
|
321
|
-
if (manifest.format_version && manifest.format_version !== '
|
|
321
|
+
if (manifest.format_version && manifest.format_version !== '2.0') {
|
|
322
322
|
errors.push(
|
|
323
|
-
`kdna.json.format_version: invalid value "${manifest.format_version}". Expected "
|
|
323
|
+
`kdna.json.format_version: invalid value "${manifest.format_version}". Expected "2.0".`,
|
|
324
324
|
);
|
|
325
325
|
}
|
|
326
326
|
if (manifest.status && !VALID_STATUS.has(manifest.status)) {
|