@push.rocks/smartsecret 1.0.2 → 1.2.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.
@@ -0,0 +1,57 @@
1
+ import * as plugins from './smartsecret.plugins.js';
2
+ export declare const SMARTSECRET_KEYRING_SCHEMA_VERSION: 1;
3
+ export declare const SMARTSECRET_KEYRING_PROFILE: "smartsecret-aes-256-gcm-dek-kek-v1";
4
+ export type TSmartSecretKeyringProfile = typeof SMARTSECRET_KEYRING_PROFILE;
5
+ export declare const SMARTSECRET_KEYRING_MAX_PAYLOAD_BYTES = 524288;
6
+ export declare const SMARTSECRET_KEYRING_MIN_CONTEXT_BYTES = 1;
7
+ export declare const SMARTSECRET_KEYRING_MAX_CONTEXT_BYTES = 65536;
8
+ export declare const SMARTSECRET_KEYRING_MAX_IDENTIFIER_BYTES = 200;
9
+ export declare const SMARTSECRET_KEYRING_MAX_KEK_VERSION = 2147483647;
10
+ export interface ISmartSecretEnvelopeCiphertext {
11
+ nonce: string;
12
+ ciphertext: string;
13
+ tag: string;
14
+ }
15
+ export interface ISmartSecretEnvelopeV1 {
16
+ schemaVersion: 1;
17
+ profile: TSmartSecretKeyringProfile;
18
+ envelopeId: string;
19
+ createdAt: number;
20
+ contextDigest: string;
21
+ kek: {
22
+ keyringId: string;
23
+ version: number;
24
+ fingerprint: `sha256:${string}`;
25
+ };
26
+ payload: ISmartSecretEnvelopeCiphertext;
27
+ wrappedDek: ISmartSecretEnvelopeCiphertext;
28
+ }
29
+ export interface IParsedSmartSecretEnvelopeV1 {
30
+ envelope: ISmartSecretEnvelopeV1;
31
+ contextDigest: Uint8Array;
32
+ kekFingerprint: Uint8Array;
33
+ payload: plugins.smartcrypto.IAesGcmCiphertext;
34
+ wrappedDek: plugins.smartcrypto.IAesGcmCiphertext;
35
+ }
36
+ export interface ISmartSecretWrapAadFields {
37
+ envelopeId: string;
38
+ createdAt: number;
39
+ contextDigest: Uint8Array;
40
+ payloadDigest: Uint8Array;
41
+ keyringId: string;
42
+ kekVersion: number;
43
+ kekFingerprint: Uint8Array;
44
+ }
45
+ export declare const requireSmartSecretIdentifier: (valueArg: unknown) => string;
46
+ export declare const requireEnvelopeIdentifier: (valueArg: unknown) => string;
47
+ export declare const requireKekVersion: (valueArg: unknown, envelopeArg?: boolean) => number;
48
+ export declare const requireEpochMilliseconds: (valueArg: unknown, envelopeArg?: boolean) => number;
49
+ export declare const requireFingerprint: (valueArg: unknown, envelopeArg?: boolean) => `sha256:${string}`;
50
+ export declare const fingerprintToBytes: (valueArg: `sha256:${string}`) => Uint8Array;
51
+ export declare const encodeCanonicalBase64Url: (valueArg: Uint8Array) => string;
52
+ export declare const parseSmartSecretEnvelope: (valueArg: unknown) => IParsedSmartSecretEnvelopeV1;
53
+ export declare const sha256Bytes: (...valuesArg: Uint8Array[]) => Uint8Array;
54
+ export declare const createSmartSecretPayloadDigest: (payloadArg: plugins.smartcrypto.IAesGcmCiphertext) => Uint8Array;
55
+ export declare const createSmartSecretWrapAad: (fieldsArg: ISmartSecretWrapAadFields) => Uint8Array;
56
+ export declare const wipeCiphertext: (valueArg: plugins.smartcrypto.IAesGcmCiphertext | undefined) => void;
57
+ export declare const timingSafeEqual: (leftArg: Uint8Array, rightArg: Uint8Array) => boolean;
@@ -0,0 +1,290 @@
1
+ import * as plugins from './smartsecret.plugins.js';
2
+ import { SmartSecretKeyringError, createSmartSecretKeyringError, } from './smartsecret.keyring.error.js';
3
+ export const SMARTSECRET_KEYRING_SCHEMA_VERSION = 1;
4
+ export const SMARTSECRET_KEYRING_PROFILE = 'smartsecret-aes-256-gcm-dek-kek-v1';
5
+ export const SMARTSECRET_KEYRING_MAX_PAYLOAD_BYTES = 524_288;
6
+ export const SMARTSECRET_KEYRING_MIN_CONTEXT_BYTES = 1;
7
+ export const SMARTSECRET_KEYRING_MAX_CONTEXT_BYTES = 65_536;
8
+ export const SMARTSECRET_KEYRING_MAX_IDENTIFIER_BYTES = 200;
9
+ export const SMARTSECRET_KEYRING_MAX_KEK_VERSION = 2_147_483_647;
10
+ const identifierPattern = /^[A-Za-z0-9][A-Za-z0-9:._-]{0,199}$/;
11
+ const fingerprintPattern = /^sha256:[0-9a-f]{64}$/;
12
+ const base64UrlPattern = /^[A-Za-z0-9_-]*$/;
13
+ const wrapAadMagic = Buffer.from('smartsecret-envelope-wrap-aad-v1', 'ascii');
14
+ const payloadDigestMagic = Buffer.from('smartsecret-envelope-payload-digest-v1', 'ascii');
15
+ const requirePlainObject = (valueArg, expectedKeysArg) => {
16
+ try {
17
+ if (valueArg === null || typeof valueArg !== 'object' || Array.isArray(valueArg)) {
18
+ throw createSmartSecretKeyringError('ENVELOPE_INVALID');
19
+ }
20
+ const prototype = Object.getPrototypeOf(valueArg);
21
+ if (prototype !== Object.prototype && prototype !== null) {
22
+ throw createSmartSecretKeyringError('ENVELOPE_INVALID');
23
+ }
24
+ const ownKeys = Reflect.ownKeys(valueArg);
25
+ if (ownKeys.length !== expectedKeysArg.length
26
+ || ownKeys.some((keyArg) => typeof keyArg !== 'string')) {
27
+ throw createSmartSecretKeyringError('ENVELOPE_INVALID');
28
+ }
29
+ const actualKeys = ownKeys;
30
+ if (expectedKeysArg.some((keyArg) => !actualKeys.includes(keyArg))) {
31
+ throw createSmartSecretKeyringError('ENVELOPE_INVALID');
32
+ }
33
+ const result = Object.create(null);
34
+ for (const key of actualKeys) {
35
+ const descriptor = Object.getOwnPropertyDescriptor(valueArg, key);
36
+ if (!descriptor?.enumerable || !('value' in descriptor)) {
37
+ throw createSmartSecretKeyringError('ENVELOPE_INVALID');
38
+ }
39
+ result[key] = descriptor.value;
40
+ }
41
+ return result;
42
+ }
43
+ catch (error) {
44
+ if (error instanceof SmartSecretKeyringError) {
45
+ throw error;
46
+ }
47
+ throw createSmartSecretKeyringError('ENVELOPE_INVALID');
48
+ }
49
+ };
50
+ export const requireSmartSecretIdentifier = (valueArg) => {
51
+ if (typeof valueArg !== 'string'
52
+ || !identifierPattern.test(valueArg)
53
+ || Buffer.byteLength(valueArg, 'utf8') < 1
54
+ || Buffer.byteLength(valueArg, 'utf8') > SMARTSECRET_KEYRING_MAX_IDENTIFIER_BYTES) {
55
+ throw createSmartSecretKeyringError('CONFIG_INVALID');
56
+ }
57
+ return valueArg;
58
+ };
59
+ export const requireEnvelopeIdentifier = (valueArg) => {
60
+ try {
61
+ return requireSmartSecretIdentifier(valueArg);
62
+ }
63
+ catch {
64
+ throw createSmartSecretKeyringError('ENVELOPE_INVALID');
65
+ }
66
+ };
67
+ export const requireKekVersion = (valueArg, envelopeArg = false) => {
68
+ if (!Number.isSafeInteger(valueArg)
69
+ || valueArg < 1
70
+ || valueArg > SMARTSECRET_KEYRING_MAX_KEK_VERSION) {
71
+ throw createSmartSecretKeyringError(envelopeArg ? 'ENVELOPE_INVALID' : 'CONFIG_INVALID');
72
+ }
73
+ return valueArg;
74
+ };
75
+ export const requireEpochMilliseconds = (valueArg, envelopeArg = false) => {
76
+ if (!Number.isSafeInteger(valueArg) || valueArg < 0) {
77
+ throw createSmartSecretKeyringError(envelopeArg ? 'ENVELOPE_INVALID' : 'CONFIG_INVALID');
78
+ }
79
+ return valueArg;
80
+ };
81
+ export const requireFingerprint = (valueArg, envelopeArg = false) => {
82
+ if (typeof valueArg !== 'string' || !fingerprintPattern.test(valueArg)) {
83
+ throw createSmartSecretKeyringError(envelopeArg ? 'ENVELOPE_INVALID' : 'CONFIG_INVALID');
84
+ }
85
+ return valueArg;
86
+ };
87
+ export const fingerprintToBytes = (valueArg) => new Uint8Array(Buffer.from(valueArg.slice('sha256:'.length), 'hex'));
88
+ export const encodeCanonicalBase64Url = (valueArg) => Buffer.from(valueArg).toString('base64url');
89
+ const decodeCanonicalBase64Url = (valueArg, maximumBytesArg, exactBytesArg) => {
90
+ if (typeof valueArg !== 'string') {
91
+ throw createSmartSecretKeyringError('ENVELOPE_INVALID');
92
+ }
93
+ const maximumEncodedLength = Math.ceil(maximumBytesArg * 4 / 3);
94
+ if (valueArg.length > maximumEncodedLength) {
95
+ throw createSmartSecretKeyringError('SIZE_LIMIT_EXCEEDED');
96
+ }
97
+ if (!base64UrlPattern.test(valueArg) || valueArg.length % 4 === 1) {
98
+ throw createSmartSecretKeyringError('ENVELOPE_INVALID');
99
+ }
100
+ let result;
101
+ let decodedBuffer;
102
+ let didSucceed = false;
103
+ try {
104
+ decodedBuffer = Buffer.from(valueArg, 'base64url');
105
+ result = new Uint8Array(decodedBuffer);
106
+ if (result.byteLength > maximumBytesArg) {
107
+ throw createSmartSecretKeyringError('SIZE_LIMIT_EXCEEDED');
108
+ }
109
+ if (exactBytesArg !== undefined && result.byteLength !== exactBytesArg) {
110
+ throw createSmartSecretKeyringError('ENVELOPE_INVALID');
111
+ }
112
+ if (encodeCanonicalBase64Url(result) !== valueArg) {
113
+ throw createSmartSecretKeyringError('ENVELOPE_INVALID');
114
+ }
115
+ didSucceed = true;
116
+ return result;
117
+ }
118
+ catch (error) {
119
+ if (error instanceof SmartSecretKeyringError) {
120
+ throw error;
121
+ }
122
+ throw createSmartSecretKeyringError('ENVELOPE_INVALID');
123
+ }
124
+ finally {
125
+ decodedBuffer?.fill(0);
126
+ if (!didSucceed) {
127
+ result?.fill(0);
128
+ }
129
+ }
130
+ };
131
+ const parseCiphertext = (valueArg, maximumCiphertextBytesArg, exactCiphertextBytesArg) => {
132
+ const value = requirePlainObject(valueArg, ['nonce', 'ciphertext', 'tag']);
133
+ let nonce;
134
+ let ciphertext;
135
+ let tag;
136
+ let didSucceed = false;
137
+ try {
138
+ nonce = decodeCanonicalBase64Url(value.nonce, 12, 12);
139
+ ciphertext = decodeCanonicalBase64Url(value.ciphertext, maximumCiphertextBytesArg, exactCiphertextBytesArg);
140
+ tag = decodeCanonicalBase64Url(value.tag, 16, 16);
141
+ const result = {
142
+ encoded: {
143
+ nonce: value.nonce,
144
+ ciphertext: value.ciphertext,
145
+ tag: value.tag,
146
+ },
147
+ raw: { nonce, ciphertext, tag },
148
+ };
149
+ didSucceed = true;
150
+ return result;
151
+ }
152
+ finally {
153
+ if (!didSucceed) {
154
+ nonce?.fill(0);
155
+ ciphertext?.fill(0);
156
+ tag?.fill(0);
157
+ }
158
+ }
159
+ };
160
+ export const parseSmartSecretEnvelope = (valueArg) => {
161
+ const value = requirePlainObject(valueArg, [
162
+ 'schemaVersion',
163
+ 'profile',
164
+ 'envelopeId',
165
+ 'createdAt',
166
+ 'contextDigest',
167
+ 'kek',
168
+ 'payload',
169
+ 'wrappedDek',
170
+ ]);
171
+ if (value.schemaVersion !== SMARTSECRET_KEYRING_SCHEMA_VERSION) {
172
+ throw createSmartSecretKeyringError('ENVELOPE_INVALID');
173
+ }
174
+ if (value.profile !== SMARTSECRET_KEYRING_PROFILE) {
175
+ throw createSmartSecretKeyringError('ENVELOPE_INVALID');
176
+ }
177
+ let contextDigest;
178
+ let kekFingerprint;
179
+ let payload;
180
+ let wrappedDek;
181
+ let didSucceed = false;
182
+ try {
183
+ const envelopeId = requireEnvelopeIdentifier(value.envelopeId);
184
+ const createdAt = requireEpochMilliseconds(value.createdAt, true);
185
+ contextDigest = decodeCanonicalBase64Url(value.contextDigest, 32, 32);
186
+ const kekValue = requirePlainObject(value.kek, ['keyringId', 'version', 'fingerprint']);
187
+ const keyringId = requireEnvelopeIdentifier(kekValue.keyringId);
188
+ const version = requireKekVersion(kekValue.version, true);
189
+ const fingerprint = requireFingerprint(kekValue.fingerprint, true);
190
+ kekFingerprint = fingerprintToBytes(fingerprint);
191
+ payload = parseCiphertext(value.payload, SMARTSECRET_KEYRING_MAX_PAYLOAD_BYTES);
192
+ wrappedDek = parseCiphertext(value.wrappedDek, 32, 32);
193
+ const result = {
194
+ envelope: {
195
+ schemaVersion: SMARTSECRET_KEYRING_SCHEMA_VERSION,
196
+ profile: SMARTSECRET_KEYRING_PROFILE,
197
+ envelopeId,
198
+ createdAt,
199
+ contextDigest: value.contextDigest,
200
+ kek: { keyringId, version, fingerprint },
201
+ payload: payload.encoded,
202
+ wrappedDek: wrappedDek.encoded,
203
+ },
204
+ contextDigest,
205
+ kekFingerprint,
206
+ payload: payload.raw,
207
+ wrappedDek: wrappedDek.raw,
208
+ };
209
+ didSucceed = true;
210
+ return result;
211
+ }
212
+ finally {
213
+ if (!didSucceed) {
214
+ contextDigest?.fill(0);
215
+ kekFingerprint?.fill(0);
216
+ wipeCiphertext(payload?.raw);
217
+ wipeCiphertext(wrappedDek?.raw);
218
+ }
219
+ }
220
+ };
221
+ export const sha256Bytes = (...valuesArg) => {
222
+ const hash = plugins.crypto.createHash('sha256');
223
+ for (const value of valuesArg) {
224
+ hash.update(value);
225
+ }
226
+ return new Uint8Array(hash.digest());
227
+ };
228
+ const uint32Bytes = (valueArg) => {
229
+ const result = Buffer.allocUnsafe(4);
230
+ result.writeUInt32BE(valueArg, 0);
231
+ return result;
232
+ };
233
+ const uint64Bytes = (valueArg) => {
234
+ const result = Buffer.allocUnsafe(8);
235
+ result.writeBigUInt64BE(BigInt(valueArg), 0);
236
+ return result;
237
+ };
238
+ const lengthPrefixed = (valueArg) => {
239
+ if (valueArg.byteLength > 0xffff_ffff) {
240
+ throw createSmartSecretKeyringError('SIZE_LIMIT_EXCEEDED');
241
+ }
242
+ return Buffer.concat([uint32Bytes(valueArg.byteLength), valueArg]);
243
+ };
244
+ export const createSmartSecretPayloadDigest = (payloadArg) => {
245
+ const hash = plugins.crypto.createHash('sha256');
246
+ hash.update(payloadDigestMagic);
247
+ for (const value of [payloadArg.nonce, payloadArg.ciphertext, payloadArg.tag]) {
248
+ const length = uint32Bytes(value.byteLength);
249
+ try {
250
+ hash.update(length);
251
+ hash.update(value);
252
+ }
253
+ finally {
254
+ length.fill(0);
255
+ }
256
+ }
257
+ return new Uint8Array(hash.digest());
258
+ };
259
+ export const createSmartSecretWrapAad = (fieldsArg) => {
260
+ if (fieldsArg.contextDigest.byteLength !== 32
261
+ || fieldsArg.payloadDigest.byteLength !== 32
262
+ || fieldsArg.kekFingerprint.byteLength !== 32) {
263
+ throw createSmartSecretKeyringError('ENVELOPE_INVALID');
264
+ }
265
+ const profile = Buffer.from(SMARTSECRET_KEYRING_PROFILE, 'utf8');
266
+ const envelopeId = Buffer.from(requireEnvelopeIdentifier(fieldsArg.envelopeId), 'utf8');
267
+ const keyringId = Buffer.from(requireEnvelopeIdentifier(fieldsArg.keyringId), 'utf8');
268
+ const createdAt = requireEpochMilliseconds(fieldsArg.createdAt, true);
269
+ const kekVersion = requireKekVersion(fieldsArg.kekVersion, true);
270
+ return Buffer.concat([
271
+ wrapAadMagic,
272
+ uint64Bytes(SMARTSECRET_KEYRING_SCHEMA_VERSION),
273
+ lengthPrefixed(profile),
274
+ lengthPrefixed(envelopeId),
275
+ uint64Bytes(createdAt),
276
+ fieldsArg.contextDigest,
277
+ fieldsArg.payloadDigest,
278
+ lengthPrefixed(keyringId),
279
+ uint64Bytes(kekVersion),
280
+ fieldsArg.kekFingerprint,
281
+ ]);
282
+ };
283
+ export const wipeCiphertext = (valueArg) => {
284
+ valueArg?.nonce.fill(0);
285
+ valueArg?.ciphertext.fill(0);
286
+ valueArg?.tag.fill(0);
287
+ };
288
+ export const timingSafeEqual = (leftArg, rightArg) => leftArg.byteLength === rightArg.byteLength
289
+ && plugins.crypto.timingSafeEqual(leftArg, rightArg);
290
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic21hcnRzZWNyZXQua2V5cmluZy5wcm90b2NvbC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3RzL3NtYXJ0c2VjcmV0LmtleXJpbmcucHJvdG9jb2wudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxLQUFLLE9BQU8sTUFBTSwwQkFBMEIsQ0FBQztBQUNwRCxPQUFPLEVBQ0wsdUJBQXVCLEVBQ3ZCLDZCQUE2QixHQUM5QixNQUFNLGdDQUFnQyxDQUFDO0FBRXhDLE1BQU0sQ0FBQyxNQUFNLGtDQUFrQyxHQUFHLENBQVUsQ0FBQztBQUM3RCxNQUFNLENBQUMsTUFBTSwyQkFBMkIsR0FBRyxvQ0FBNkMsQ0FBQztBQUd6RixNQUFNLENBQUMsTUFBTSxxQ0FBcUMsR0FBRyxPQUFPLENBQUM7QUFDN0QsTUFBTSxDQUFDLE1BQU0scUNBQXFDLEdBQUcsQ0FBQyxDQUFDO0FBQ3ZELE1BQU0sQ0FBQyxNQUFNLHFDQUFxQyxHQUFHLE1BQU0sQ0FBQztBQUM1RCxNQUFNLENBQUMsTUFBTSx3Q0FBd0MsR0FBRyxHQUFHLENBQUM7QUFDNUQsTUFBTSxDQUFDLE1BQU0sbUNBQW1DLEdBQUcsYUFBYSxDQUFDO0FBRWpFLE1BQU0saUJBQWlCLEdBQUcscUNBQXFDLENBQUM7QUFDaEUsTUFBTSxrQkFBa0IsR0FBRyx1QkFBdUIsQ0FBQztBQUNuRCxNQUFNLGdCQUFnQixHQUFHLGtCQUFrQixDQUFDO0FBQzVDLE1BQU0sWUFBWSxHQUFHLE1BQU0sQ0FBQyxJQUFJLENBQUMsa0NBQWtDLEVBQUUsT0FBTyxDQUFDLENBQUM7QUFDOUUsTUFBTSxrQkFBa0IsR0FBRyxNQUFNLENBQUMsSUFBSSxDQUFDLHdDQUF3QyxFQUFFLE9BQU8sQ0FBQyxDQUFDO0FBeUMxRixNQUFNLGtCQUFrQixHQUFHLENBQ3pCLFFBQWlCLEVBQ2pCLGVBQWtDLEVBQ1QsRUFBRTtJQUMzQixJQUFJLENBQUM7UUFDSCxJQUFJLFFBQVEsS0FBSyxJQUFJLElBQUksT0FBTyxRQUFR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
@@ -3,4 +3,8 @@ import * as crypto from 'crypto';
3
3
  import * as fs from 'fs';
4
4
  import * as os from 'os';
5
5
  import * as path from 'path';
6
- export { childProcess, crypto, fs, os, path };
6
+ import * as url from 'url';
7
+ import * as smartcrypto from '@push.rocks/smartcrypto';
8
+ import * as smartipc from '@push.rocks/smartipc';
9
+ import * as smartrust from '@push.rocks/smartrust';
10
+ export { childProcess, crypto, fs, os, path, url, smartcrypto, smartipc, smartrust };
@@ -4,5 +4,10 @@ import * as crypto from 'crypto';
4
4
  import * as fs from 'fs';
5
5
  import * as os from 'os';
6
6
  import * as path from 'path';
7
- export { childProcess, crypto, fs, os, path };
8
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic21hcnRzZWNyZXQucGx1Z2lucy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3RzL3NtYXJ0c2VjcmV0LnBsdWdpbnMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsb0JBQW9CO0FBQ3BCLE9BQU8sS0FBSyxZQUFZLE1BQU0sZUFBZSxDQUFDO0FBQzlDLE9BQU8sS0FBSyxNQUFNLE1BQU0sUUFBUSxDQUFDO0FBQ2pDLE9BQU8sS0FBSyxFQUFFLE1BQU0sSUFBSSxDQUFDO0FBQ3pCLE9BQU8sS0FBSyxFQUFFLE1BQU0sSUFBSSxDQUFDO0FBQ3pCLE9BQU8sS0FBSyxJQUFJLE1BQU0sTUFBTSxDQUFDO0FBRTdCLE9BQU8sRUFBRSxZQUFZLEVBQUUsTUFBTSxFQUFFLEVBQUUsRUFBRSxFQUFFLEVBQUUsSUFBSSxFQUFFLENBQUMifQ==
7
+ import * as url from 'url';
8
+ // @push.rocks scope
9
+ import * as smartcrypto from '@push.rocks/smartcrypto';
10
+ import * as smartipc from '@push.rocks/smartipc';
11
+ import * as smartrust from '@push.rocks/smartrust';
12
+ export { childProcess, crypto, fs, os, path, url, smartcrypto, smartipc, smartrust };
13
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic21hcnRzZWNyZXQucGx1Z2lucy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3RzL3NtYXJ0c2VjcmV0LnBsdWdpbnMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsb0JBQW9CO0FBQ3BCLE9BQU8sS0FBSyxZQUFZLE1BQU0sZUFBZSxDQUFDO0FBQzlDLE9BQU8sS0FBSyxNQUFNLE1BQU0sUUFBUSxDQUFDO0FBQ2pDLE9BQU8sS0FBSyxFQUFFLE1BQU0sSUFBSSxDQUFDO0FBQ3pCLE9BQU8sS0FBSyxFQUFFLE1BQU0sSUFBSSxDQUFDO0FBQ3pCLE9BQU8sS0FBSyxJQUFJLE1BQU0sTUFBTSxDQUFDO0FBQzdCLE9BQU8sS0FBSyxHQUFHLE1BQU0sS0FBSyxDQUFDO0FBRTNCLG9CQUFvQjtBQUNwQixPQUFPLEtBQUssV0FBVyxNQUFNLHlCQUF5QixDQUFDO0FBQ3ZELE9BQU8sS0FBSyxRQUFRLE1BQU0sc0JBQXNCLENBQUM7QUFDakQsT0FBTyxLQUFLLFNBQVMsTUFBTSx1QkFBdUIsQ0FBQztBQUVuRCxPQUFPLEVBQUUsWUFBWSxFQUFFLE1BQU0sRUFBRSxFQUFFLEVBQUUsRUFBRSxFQUFFLElBQUksRUFBRSxHQUFHLEVBQUUsV0FBVyxFQUFFLFFBQVEsRUFBRSxTQUFTLEVBQUUsQ0FBQyJ9
package/package.json CHANGED
@@ -1,16 +1,11 @@
1
1
  {
2
2
  "name": "@push.rocks/smartsecret",
3
- "version": "1.0.2",
3
+ "version": "1.2.0",
4
4
  "private": false,
5
- "description": "OS keychain-based secret storage with encrypted-file fallback for Node.js.",
5
+ "description": "OS-backed secret storage plus strict Linux kernel-keyring and envelope APIs for Node.js.",
6
6
  "main": "dist_ts/index.js",
7
7
  "typings": "dist_ts/index.d.ts",
8
8
  "type": "module",
9
- "scripts": {
10
- "test": "(tstest test/ --verbose)",
11
- "build": "(tsbuild)",
12
- "buildDocs": "tsdoc"
13
- },
14
9
  "repository": {
15
10
  "type": "git",
16
11
  "url": "https://code.foss.global/push.rocks/smartsecret.git"
@@ -34,10 +29,11 @@
34
29
  },
35
30
  "homepage": "https://code.foss.global/push.rocks/smartsecret",
36
31
  "devDependencies": {
37
- "@git.zone/tsbuild": "^4.1.2",
38
- "@git.zone/tsrun": "^2.0.1",
39
- "@git.zone/tstest": "^3.1.8",
40
- "@types/node": "^22.15.0"
32
+ "@git.zone/tsbuild": "^4.4.2",
33
+ "@git.zone/tsrun": "^2.0.6",
34
+ "@git.zone/tsrust": "^1.7.0",
35
+ "@git.zone/tstest": "^3.6.7",
36
+ "@types/node": "^25.9.5"
41
37
  },
42
38
  "files": [
43
39
  "ts/**/*",
@@ -48,10 +44,22 @@
48
44
  "dist_ts_web/**/*",
49
45
  "assets/**/*",
50
46
  "cli.js",
51
- "npmextra.json",
52
- "readme.md"
47
+ ".smartconfig.json",
48
+ "readme.md",
49
+ "third-party-notices.md"
53
50
  ],
54
51
  "browserslist": [
55
52
  "last 1 chrome versions"
56
- ]
57
- }
53
+ ],
54
+ "dependencies": {
55
+ "@push.rocks/smartcrypto": "^2.2.0",
56
+ "@push.rocks/smartipc": "^2.4.0",
57
+ "@push.rocks/smartrust": "^1.6.0"
58
+ },
59
+ "scripts": {
60
+ "test": "(tstest test/ --verbose)",
61
+ "test:before": "(tsrust) && (cargo test --manifest-path rust/Cargo.toml --target-dir .nogit/cargo-test-target)",
62
+ "build": "(tsbuild) && (tsrust)",
63
+ "buildDocs": "tsdoc"
64
+ }
65
+ }
package/readme.hints.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  ## Architecture
4
4
  - 3-tier backend: macOS Keychain → Linux secret-tool → encrypted file vault
5
- - Zero runtime dependencies (Node.js built-ins only)
5
+ - The legacy SmartSecret backends use Node.js built-ins only
6
6
  - All OS interactions via child_process.execFile (no shell injection)
7
7
 
8
8
  ## File Backend
@@ -10,3 +10,21 @@
10
10
  - Keyfile: auto-generated 32 random bytes at ~/.config/smartsecret/.keyfile (mode 0600)
11
11
  - Key derivation: PBKDF2 (SHA-512, 100k iterations, service-name salt)
12
12
  - Atomic writes: write .tmp then rename()
13
+
14
+ ## SmartSecret Keyring
15
+ - Separate Linux-only API; it does not alter legacy SmartSecret backend selection or behavior
16
+ - Uses @push.rocks/smartcrypto for AES-GCM payload encryption and DEK wrap/unwrap/rewrap
17
+ - Loads exact 32-byte KEKs from validated absolute credential paths into immutable per-instance snapshots
18
+ - Consumers own envelope persistence and compare-and-swap coordination
19
+ - JavaScript key and plaintext zeroization is best effort
20
+
21
+ ## SmartSecret Kernel Store
22
+ - Additive strict API; legacy SmartSecret backend selection remains unchanged
23
+ - Requires Node.js 25, Linux x64, Linux keyutils, and the exact package-owned static Rust worker
24
+ - No shell, PATH lookup, environment override, Secret Service, or filesystem secret fallback
25
+ - Generic values are framed and limited to 0..1024 bytes; service/account names are valid scalar UTF-8 strings of 1..1024 bytes
26
+ - Persistent-root service rings are revalidated on every operation; process keyring ownership is staging-only
27
+ - Service-wide SmartIPC NamedMutex plus a single monotonic 1..60000 ms deadline bounds cooperating cross-process access
28
+ - Any post-dispatch mutation ambiguity or worker integrity failure terminates and poisons the store
29
+ - DevIdP v1 migration is opaque, receipt-bound, keyutils-only, and requires a quiescent legacy writer
30
+ - Same-UID readability and kernel expiry/reboot behavior are part of the security model