@tnid/encryption 0.0.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.
@@ -0,0 +1,194 @@
1
+ /**
2
+ * TNID encryption using FF1 Format-Preserving Encryption.
3
+ *
4
+ * Provides functions to convert V0 (time-ordered) TNIDs to V1 (random-looking)
5
+ * TNIDs and vice versa, hiding timestamp information while remaining reversible.
6
+ */
7
+ import { DynamicTnid } from "@tnid/core";
8
+ import { parseUuidStringToValue, valueToTnidString, extractVariantFromValue, } from "@tnid/core/uuid";
9
+ import { FF1 } from "./ff1.js";
10
+ import { COMPLETE_SECRET_DATA_MASK, expandSecretDataBits, extractSecretDataBits, fromHexDigits, setVariant, toHexDigits, } from "./bits.js";
11
+ /**
12
+ * Error when creating an EncryptionKey.
13
+ */
14
+ export class EncryptionKeyError extends Error {
15
+ constructor(message) {
16
+ super(message);
17
+ this.name = "EncryptionKeyError";
18
+ }
19
+ }
20
+ /**
21
+ * Error when encrypting or decrypting a TNID.
22
+ */
23
+ export class EncryptionError extends Error {
24
+ constructor(message) {
25
+ super(message);
26
+ this.name = "EncryptionError";
27
+ }
28
+ }
29
+ /**
30
+ * A 128-bit (16 byte) encryption key for TNID encryption.
31
+ */
32
+ export class EncryptionKey {
33
+ constructor(bytes) {
34
+ Object.defineProperty(this, "bytes", {
35
+ enumerable: true,
36
+ configurable: true,
37
+ writable: true,
38
+ value: void 0
39
+ });
40
+ this.bytes = bytes;
41
+ }
42
+ /**
43
+ * Creates a new encryption key from raw bytes.
44
+ */
45
+ static fromBytes(bytes) {
46
+ if (bytes.length !== 16) {
47
+ throw new EncryptionKeyError(`Encryption key must be 16 bytes, got ${bytes.length}`);
48
+ }
49
+ return new EncryptionKey(new Uint8Array(bytes));
50
+ }
51
+ /**
52
+ * Creates an encryption key from a 32-character hex string.
53
+ */
54
+ static fromHex(hex) {
55
+ if (hex.length !== 32) {
56
+ throw new EncryptionKeyError(`Encryption key hex string must be 32 characters, got ${hex.length}`);
57
+ }
58
+ const bytes = new Uint8Array(16);
59
+ for (let i = 0; i < 16; i++) {
60
+ const hexByte = hex.slice(i * 2, i * 2 + 2);
61
+ const value = parseInt(hexByte, 16);
62
+ if (isNaN(value)) {
63
+ throw new EncryptionKeyError(`Invalid hex character at position ${i * 2}`);
64
+ }
65
+ bytes[i] = value;
66
+ }
67
+ return new EncryptionKey(bytes);
68
+ }
69
+ /**
70
+ * Returns the key as a byte array.
71
+ */
72
+ asBytes() {
73
+ return new Uint8Array(this.bytes);
74
+ }
75
+ }
76
+ /**
77
+ * Convert TNID string to 128-bit value.
78
+ */
79
+ function tnidToValue(tnid) {
80
+ // Use @tnid/core to convert to UUID, then parse UUID to value
81
+ const parsed = DynamicTnid.parse(tnid);
82
+ const uuid = DynamicTnid.toUuidString(parsed);
83
+ return parseUuidStringToValue(uuid);
84
+ }
85
+ /**
86
+ * Convert 128-bit value back to TNID string.
87
+ */
88
+ function valueToTnid(value) {
89
+ return valueToTnidString(value);
90
+ }
91
+ /**
92
+ * Encrypts the 100-bit Payload using FF1.
93
+ */
94
+ async function encryptPayload(payload, key) {
95
+ // Mask to 100 bits
96
+ const mask = (1n << 100n) - 1n;
97
+ const data = payload & mask;
98
+ // Convert to hex digits
99
+ const hexDigits = toHexDigits(data);
100
+ // Create FF1 cipher with radix 16
101
+ const ff1 = new FF1(key.asBytes(), 16);
102
+ // Encrypt with empty tweak
103
+ const encrypted = await ff1.encrypt(new Uint8Array(0), hexDigits);
104
+ // Convert back to bigint
105
+ return fromHexDigits(encrypted);
106
+ }
107
+ /**
108
+ * Decrypts the 100-bit Payload using FF1.
109
+ */
110
+ async function decryptPayload(payload, key) {
111
+ // Mask to 100 bits
112
+ const mask = (1n << 100n) - 1n;
113
+ const data = payload & mask;
114
+ // Convert to hex digits
115
+ const hexDigits = toHexDigits(data);
116
+ // Create FF1 cipher with radix 16
117
+ const ff1 = new FF1(key.asBytes(), 16);
118
+ // Decrypt with empty tweak
119
+ const decrypted = await ff1.decrypt(new Uint8Array(0), hexDigits);
120
+ // Convert back to bigint
121
+ return fromHexDigits(decrypted);
122
+ }
123
+ /**
124
+ * Encrypts a V0 TNID to V1, hiding timestamp information.
125
+ *
126
+ * @param tnid The V0 TNID string to encrypt
127
+ * @param key The encryption key
128
+ * @returns The encrypted V1 TNID string
129
+ * @throws EncryptionError if the TNID is not V0 or is invalid
130
+ */
131
+ export async function encryptV0ToV1(tnid, key) {
132
+ let value;
133
+ try {
134
+ value = tnidToValue(tnid);
135
+ }
136
+ catch (e) {
137
+ throw new EncryptionError(`Invalid TNID: ${e.message}`);
138
+ }
139
+ const variant = extractVariantFromValue(value);
140
+ if (variant === "v1") {
141
+ // Already V1, return unchanged
142
+ return tnid;
143
+ }
144
+ if (variant !== "v0") {
145
+ throw new EncryptionError(`TNID variant ${variant} is not supported for encryption`);
146
+ }
147
+ // Extract the 100 Payload bits
148
+ const secretData = extractSecretDataBits(value);
149
+ // Encrypt the Payload
150
+ const encryptedData = await encryptPayload(secretData, key);
151
+ // Expand back to proper bit positions
152
+ const expanded = expandSecretDataBits(encryptedData);
153
+ // Preserve Name bits and UUID-specific bits, replace Payload bits
154
+ let result = (value & ~COMPLETE_SECRET_DATA_MASK) | expanded;
155
+ // Change variant from V0 to V1
156
+ result = setVariant(result, "v1");
157
+ return valueToTnid(result);
158
+ }
159
+ /**
160
+ * Decrypts a V1 TNID back to V0, recovering timestamp information.
161
+ *
162
+ * @param tnid The V1 TNID string to decrypt
163
+ * @param key The encryption key (must match the one used for encryption)
164
+ * @returns The decrypted V0 TNID string
165
+ * @throws EncryptionError if the TNID is not V1 or is invalid
166
+ */
167
+ export async function decryptV1ToV0(tnid, key) {
168
+ let value;
169
+ try {
170
+ value = tnidToValue(tnid);
171
+ }
172
+ catch (e) {
173
+ throw new EncryptionError(`Invalid TNID: ${e.message}`);
174
+ }
175
+ const variant = extractVariantFromValue(value);
176
+ if (variant === "v0") {
177
+ // Already V0, return unchanged
178
+ return tnid;
179
+ }
180
+ if (variant !== "v1") {
181
+ throw new EncryptionError(`TNID variant ${variant} is not supported for decryption`);
182
+ }
183
+ // Extract the 100 Payload bits
184
+ const encryptedData = extractSecretDataBits(value);
185
+ // Decrypt the Payload
186
+ const decryptedData = await decryptPayload(encryptedData, key);
187
+ // Expand back to proper bit positions
188
+ const expanded = expandSecretDataBits(decryptedData);
189
+ // Preserve Name bits and UUID-specific bits, replace Payload bits
190
+ let result = (value & ~COMPLETE_SECRET_DATA_MASK) | expanded;
191
+ // Change variant from V1 to V0
192
+ result = setVariant(result, "v0");
193
+ return valueToTnid(result);
194
+ }
package/esm/ff1.d.ts ADDED
@@ -0,0 +1,45 @@
1
+ /**
2
+ * FF1 Format-Preserving Encryption (NIST SP 800-38G).
3
+ *
4
+ * This implementation uses AES-128 as the underlying cipher.
5
+ * FF1 is a Feistel cipher that encrypts strings of numerals
6
+ * while preserving their format (length and radix).
7
+ */
8
+ /**
9
+ * FF1 Format-Preserving Encryption cipher.
10
+ *
11
+ * Encrypts/decrypts a string of numerals (digits in base `radix`)
12
+ * while preserving the format.
13
+ */
14
+ export declare class FF1 {
15
+ private aes;
16
+ private radix;
17
+ /**
18
+ * Create an FF1 cipher with the given key and radix.
19
+ *
20
+ * @param key 16-byte AES key
21
+ * @param radix Base of the numeral system (2-65536)
22
+ */
23
+ constructor(key: Uint8Array, radix: number);
24
+ /**
25
+ * FF1 encryption.
26
+ *
27
+ * @param tweak Additional data (can be empty)
28
+ * @param plaintext Array of numerals (each in range [0, radix))
29
+ * @returns Encrypted numeral array of same length
30
+ */
31
+ encrypt(tweak: Uint8Array, plaintext: number[]): Promise<number[]>;
32
+ /**
33
+ * FF1 decryption.
34
+ *
35
+ * @param tweak Additional data (must match encryption)
36
+ * @param ciphertext Array of numerals
37
+ * @returns Decrypted numeral array
38
+ */
39
+ decrypt(tweak: Uint8Array, ciphertext: number[]): Promise<number[]>;
40
+ /**
41
+ * Core FF1 Feistel cipher (10 rounds).
42
+ */
43
+ private cipher;
44
+ }
45
+ //# sourceMappingURL=ff1.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ff1.d.ts","sourceRoot":"","sources":["../src/ff1.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AA+EH;;;;;GAKG;AACH,qBAAa,GAAG;IACd,OAAO,CAAC,GAAG,CAAS;IACpB,OAAO,CAAC,KAAK,CAAS;IAEtB;;;;;OAKG;gBACS,GAAG,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM;IAQ1C;;;;;;OAMG;IACH,OAAO,CAAC,KAAK,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;IAIlE;;;;;;OAMG;IACH,OAAO,CAAC,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;IAInE;;OAEG;YACW,MAAM;CAsIrB"}
package/esm/ff1.js ADDED
@@ -0,0 +1,240 @@
1
+ /**
2
+ * FF1 Format-Preserving Encryption (NIST SP 800-38G).
3
+ *
4
+ * This implementation uses AES-128 as the underlying cipher.
5
+ * FF1 is a Feistel cipher that encrypts strings of numerals
6
+ * while preserving their format (length and radix).
7
+ */
8
+ import { Aes128 } from "./aes.js";
9
+ /**
10
+ * Ceiling division: ceil(a / b)
11
+ */
12
+ function ceilDiv(a, b) {
13
+ return Math.ceil(a / b);
14
+ }
15
+ /**
16
+ * Convert a number array (base radix) to a bigint.
17
+ * Most significant digit first.
18
+ */
19
+ function numArrayToBigInt(arr, radix) {
20
+ let result = 0n;
21
+ const radixBig = BigInt(radix);
22
+ for (const digit of arr) {
23
+ result = result * radixBig + BigInt(digit);
24
+ }
25
+ return result;
26
+ }
27
+ /**
28
+ * Convert a bigint to a number array (base radix) with specified length.
29
+ * Most significant digit first. Pads with zeros if needed.
30
+ */
31
+ function bigIntToNumArray(value, radix, length) {
32
+ const result = new Array(length).fill(0);
33
+ const radixBig = BigInt(radix);
34
+ let v = value;
35
+ for (let i = length - 1; i >= 0 && v > 0n; i--) {
36
+ result[i] = Number(v % radixBig);
37
+ v = v / radixBig;
38
+ }
39
+ return result;
40
+ }
41
+ /**
42
+ * Convert bigint to big-endian byte array with specified length.
43
+ */
44
+ function bigIntToBytes(value, length) {
45
+ const bytes = new Uint8Array(length);
46
+ let v = value;
47
+ for (let i = length - 1; i >= 0 && v > 0n; i--) {
48
+ bytes[i] = Number(v & 0xffn);
49
+ v >>= 8n;
50
+ }
51
+ return bytes;
52
+ }
53
+ /**
54
+ * Convert byte array to bigint (big-endian).
55
+ */
56
+ function bytesToBigInt(bytes) {
57
+ let result = 0n;
58
+ for (const byte of bytes) {
59
+ result = (result << 8n) | BigInt(byte);
60
+ }
61
+ return result;
62
+ }
63
+ /**
64
+ * Concatenate multiple Uint8Arrays.
65
+ */
66
+ function concat(...arrays) {
67
+ const totalLength = arrays.reduce((sum, arr) => sum + arr.length, 0);
68
+ const result = new Uint8Array(totalLength);
69
+ let offset = 0;
70
+ for (const arr of arrays) {
71
+ result.set(arr, offset);
72
+ offset += arr.length;
73
+ }
74
+ return result;
75
+ }
76
+ /**
77
+ * FF1 Format-Preserving Encryption cipher.
78
+ *
79
+ * Encrypts/decrypts a string of numerals (digits in base `radix`)
80
+ * while preserving the format.
81
+ */
82
+ export class FF1 {
83
+ /**
84
+ * Create an FF1 cipher with the given key and radix.
85
+ *
86
+ * @param key 16-byte AES key
87
+ * @param radix Base of the numeral system (2-65536)
88
+ */
89
+ constructor(key, radix) {
90
+ Object.defineProperty(this, "aes", {
91
+ enumerable: true,
92
+ configurable: true,
93
+ writable: true,
94
+ value: void 0
95
+ });
96
+ Object.defineProperty(this, "radix", {
97
+ enumerable: true,
98
+ configurable: true,
99
+ writable: true,
100
+ value: void 0
101
+ });
102
+ if (radix < 2 || radix > 65536) {
103
+ throw new Error(`Radix must be in range [2, 65536], got ${radix}`);
104
+ }
105
+ this.aes = new Aes128(key);
106
+ this.radix = radix;
107
+ }
108
+ /**
109
+ * FF1 encryption.
110
+ *
111
+ * @param tweak Additional data (can be empty)
112
+ * @param plaintext Array of numerals (each in range [0, radix))
113
+ * @returns Encrypted numeral array of same length
114
+ */
115
+ encrypt(tweak, plaintext) {
116
+ return this.cipher(tweak, plaintext, true);
117
+ }
118
+ /**
119
+ * FF1 decryption.
120
+ *
121
+ * @param tweak Additional data (must match encryption)
122
+ * @param ciphertext Array of numerals
123
+ * @returns Decrypted numeral array
124
+ */
125
+ decrypt(tweak, ciphertext) {
126
+ return this.cipher(tweak, ciphertext, false);
127
+ }
128
+ /**
129
+ * Core FF1 Feistel cipher (10 rounds).
130
+ */
131
+ async cipher(tweak, input, encrypting) {
132
+ const n = input.length;
133
+ const t = tweak.length;
134
+ const radix = this.radix;
135
+ // Split into two halves
136
+ const u = Math.floor(n / 2);
137
+ const v = n - u;
138
+ // A gets first u elements, B gets last v elements
139
+ let A = input.slice(0, u);
140
+ let B = input.slice(u);
141
+ // Precompute constants per spec
142
+ // b = ceil(ceil(v * log2(radix)) / 8) - number of bytes to represent NUM_radix(B)
143
+ const b = Math.ceil(Math.ceil(v * Math.log2(radix)) / 8);
144
+ // d = 4 * ceil(b/4) + 4 - length of S (multiple of 4, at least 4 more than b)
145
+ const d = 4 * ceilDiv(b, 4) + 4;
146
+ // P = [1, 2, 1, radix (3 bytes), 10, u mod 256, n (4 bytes), t (4 bytes)]
147
+ const P = new Uint8Array(16);
148
+ P[0] = 1;
149
+ P[1] = 2;
150
+ P[2] = 1;
151
+ // radix as 3 bytes (big-endian, upper 8 bits then lower 16 bits)
152
+ P[3] = (radix >> 16) & 0xff;
153
+ P[4] = (radix >> 8) & 0xff;
154
+ P[5] = radix & 0xff;
155
+ P[6] = 10; // Number of rounds
156
+ P[7] = u & 0xff;
157
+ // n as 4 bytes big-endian
158
+ P[8] = (n >> 24) & 0xff;
159
+ P[9] = (n >> 16) & 0xff;
160
+ P[10] = (n >> 8) & 0xff;
161
+ P[11] = n & 0xff;
162
+ // t as 4 bytes big-endian
163
+ P[12] = (t >> 24) & 0xff;
164
+ P[13] = (t >> 16) & 0xff;
165
+ P[14] = (t >> 8) & 0xff;
166
+ P[15] = t & 0xff;
167
+ // 10 Feistel rounds
168
+ const rounds = encrypting ? [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] : [9, 8, 7, 6, 5, 4, 3, 2, 1, 0];
169
+ for (const i of rounds) {
170
+ // Determine m based on round parity
171
+ const m = (i % 2 === 0) ? u : v;
172
+ // Build Q: tweak || zeros || round || numB
173
+ // Q length must be multiple of 16
174
+ const numB = numArrayToBigInt(encrypting ? B : A, radix);
175
+ const numBBytes = bigIntToBytes(numB, b);
176
+ // Q = tweak || 0^(-t-b-1 mod 16) || i || [NUM_radix(B)]
177
+ const padLen = (16 - ((t + b + 1) % 16)) % 16;
178
+ const Q = concat(tweak, new Uint8Array(padLen), new Uint8Array([i]), numBBytes);
179
+ // R = PRF(P || Q)
180
+ // PRF uses AES-CBC-MAC over (P || Q) with blocks of 16 bytes
181
+ const pq = concat(P, Q);
182
+ const numBlocks = pq.length / 16;
183
+ const blocks = [];
184
+ for (let j = 0; j < numBlocks; j++) {
185
+ blocks.push(pq.slice(j * 16, (j + 1) * 16));
186
+ }
187
+ const R = await this.aes.cbcMac(blocks);
188
+ // S = first d bytes of R || CIPH(R ⊕ [1]) || CIPH(R ⊕ [2]) || ...
189
+ const S = new Uint8Array(d);
190
+ const rCopyLen = Math.min(16, d);
191
+ S.set(R.slice(0, rCopyLen), 0);
192
+ let sOffset = rCopyLen;
193
+ let counter = 1;
194
+ while (sOffset < d) {
195
+ // R XOR counter (counter as 16-byte big-endian)
196
+ const counterBlock = new Uint8Array(16);
197
+ counterBlock[15] = counter & 0xff;
198
+ counterBlock[14] = (counter >> 8) & 0xff;
199
+ counterBlock[13] = (counter >> 16) & 0xff;
200
+ counterBlock[12] = (counter >> 24) & 0xff;
201
+ const xored = new Uint8Array(16);
202
+ for (let k = 0; k < 16; k++) {
203
+ xored[k] = R[k] ^ counterBlock[k];
204
+ }
205
+ const block = await this.aes.encryptBlock(xored);
206
+ const copyLen = Math.min(16, d - sOffset);
207
+ S.set(block.slice(0, copyLen), sOffset);
208
+ sOffset += copyLen;
209
+ counter++;
210
+ }
211
+ // y = NUM(S) - interpret S as big-endian number
212
+ const y = bytesToBigInt(S);
213
+ // c = (NUM_radix(A/B) + y) mod radix^m (encrypt)
214
+ // c = (NUM_radix(A/B) - y) mod radix^m (decrypt)
215
+ const radixPowM = BigInt(radix) ** BigInt(m);
216
+ const numSrc = numArrayToBigInt(encrypting ? A : B, radix);
217
+ let c;
218
+ if (encrypting) {
219
+ c = (numSrc + y) % radixPowM;
220
+ }
221
+ else {
222
+ // For modular subtraction, add radixPowM to handle negative
223
+ c = ((numSrc - y) % radixPowM + radixPowM) % radixPowM;
224
+ }
225
+ // C = STR_m_radix(c)
226
+ const C = bigIntToNumArray(c, radix, m);
227
+ // Swap: A = B, B = C (encrypt) or B = A, A = C (decrypt)
228
+ if (encrypting) {
229
+ A = B;
230
+ B = C;
231
+ }
232
+ else {
233
+ B = A;
234
+ A = C;
235
+ }
236
+ }
237
+ // Return A || B
238
+ return [...A, ...B];
239
+ }
240
+ }
package/esm/index.d.ts ADDED
@@ -0,0 +1,27 @@
1
+ /**
2
+ * @tnid/encryption - Format-preserving encryption for TNIDs
3
+ *
4
+ * Provides FF1 (NIST SP 800-38G) encryption to convert time-ordered V0 TNIDs
5
+ * to random-looking V1 TNIDs, hiding timestamp information while remaining
6
+ * reversible with the secret key.
7
+ *
8
+ * @example
9
+ * ```typescript
10
+ * import { EncryptionKey, encryptV0ToV1, decryptV1ToV0 } from "@tnid/encryption";
11
+ *
12
+ * // Create a key from hex string
13
+ * const key = EncryptionKey.fromHex("0102030405060708090a0b0c0d0e0f10");
14
+ *
15
+ * // Encrypt a V0 TNID to V1
16
+ * const encrypted = await encryptV0ToV1("user.Br2flcNDfF6LYICnT", key);
17
+ * // Returns a V1 TNID that looks random
18
+ *
19
+ * // Decrypt back to V0
20
+ * const decrypted = await decryptV1ToV0(encrypted, key);
21
+ * // decrypted === "user.Br2flcNDfF6LYICnT"
22
+ * ```
23
+ *
24
+ * @module
25
+ */
26
+ export { EncryptionKey, EncryptionKeyError, EncryptionError, encryptV0ToV1, decryptV1ToV0, } from "./encryption.js";
27
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH,OAAO,EACL,aAAa,EACb,kBAAkB,EAClB,eAAe,EACf,aAAa,EACb,aAAa,GACd,MAAM,iBAAiB,CAAC"}
package/esm/index.js ADDED
@@ -0,0 +1,26 @@
1
+ /**
2
+ * @tnid/encryption - Format-preserving encryption for TNIDs
3
+ *
4
+ * Provides FF1 (NIST SP 800-38G) encryption to convert time-ordered V0 TNIDs
5
+ * to random-looking V1 TNIDs, hiding timestamp information while remaining
6
+ * reversible with the secret key.
7
+ *
8
+ * @example
9
+ * ```typescript
10
+ * import { EncryptionKey, encryptV0ToV1, decryptV1ToV0 } from "@tnid/encryption";
11
+ *
12
+ * // Create a key from hex string
13
+ * const key = EncryptionKey.fromHex("0102030405060708090a0b0c0d0e0f10");
14
+ *
15
+ * // Encrypt a V0 TNID to V1
16
+ * const encrypted = await encryptV0ToV1("user.Br2flcNDfF6LYICnT", key);
17
+ * // Returns a V1 TNID that looks random
18
+ *
19
+ * // Decrypt back to V0
20
+ * const decrypted = await decryptV1ToV0(encrypted, key);
21
+ * // decrypted === "user.Br2flcNDfF6LYICnT"
22
+ * ```
23
+ *
24
+ * @module
25
+ */
26
+ export { EncryptionKey, EncryptionKeyError, EncryptionError, encryptV0ToV1, decryptV1ToV0, } from "./encryption.js";
@@ -0,0 +1,3 @@
1
+ {
2
+ "type": "module"
3
+ }
package/package.json ADDED
@@ -0,0 +1,40 @@
1
+ {
2
+ "name": "@tnid/encryption",
3
+ "version": "0.0.4",
4
+ "description": "Format-preserving encryption for TNIDs - convert time-ordered IDs to random-looking IDs",
5
+ "keywords": [
6
+ "uuid",
7
+ "id",
8
+ "identifier",
9
+ "tnid",
10
+ "typed",
11
+ "type-safe"
12
+ ],
13
+ "repository": {
14
+ "type": "git",
15
+ "url": "git+https://github.com/tnid/tnid-typescript.git"
16
+ },
17
+ "license": "MIT",
18
+ "bugs": {
19
+ "url": "https://github.com/tnid/tnid-typescript/issues"
20
+ },
21
+ "main": "./script/index.js",
22
+ "module": "./esm/index.js",
23
+ "exports": {
24
+ ".": {
25
+ "import": "./esm/index.js",
26
+ "require": "./script/index.js"
27
+ }
28
+ },
29
+ "scripts": {},
30
+ "publishConfig": {
31
+ "access": "public"
32
+ },
33
+ "engines": {
34
+ "node": ">=20"
35
+ },
36
+ "dependencies": {
37
+ "@tnid/core": "^0.0.4"
38
+ },
39
+ "_generatedBy": "dnt@dev"
40
+ }
@@ -0,0 +1,2 @@
1
+ export declare const dntGlobalThis: Omit<typeof globalThis, never>;
2
+ //# sourceMappingURL=_dnt.shims.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"_dnt.shims.d.ts","sourceRoot":"","sources":["../src/_dnt.shims.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,aAAa,gCAA2C,CAAC"}
@@ -0,0 +1,60 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.dntGlobalThis = void 0;
4
+ const dntGlobals = {};
5
+ exports.dntGlobalThis = createMergeProxy(globalThis, dntGlobals);
6
+ function createMergeProxy(baseObj, extObj) {
7
+ return new Proxy(baseObj, {
8
+ get(_target, prop, _receiver) {
9
+ if (prop in extObj) {
10
+ return extObj[prop];
11
+ }
12
+ else {
13
+ return baseObj[prop];
14
+ }
15
+ },
16
+ set(_target, prop, value) {
17
+ if (prop in extObj) {
18
+ delete extObj[prop];
19
+ }
20
+ baseObj[prop] = value;
21
+ return true;
22
+ },
23
+ deleteProperty(_target, prop) {
24
+ let success = false;
25
+ if (prop in extObj) {
26
+ delete extObj[prop];
27
+ success = true;
28
+ }
29
+ if (prop in baseObj) {
30
+ delete baseObj[prop];
31
+ success = true;
32
+ }
33
+ return success;
34
+ },
35
+ ownKeys(_target) {
36
+ const baseKeys = Reflect.ownKeys(baseObj);
37
+ const extKeys = Reflect.ownKeys(extObj);
38
+ const extKeysSet = new Set(extKeys);
39
+ return [...baseKeys.filter((k) => !extKeysSet.has(k)), ...extKeys];
40
+ },
41
+ defineProperty(_target, prop, desc) {
42
+ if (prop in extObj) {
43
+ delete extObj[prop];
44
+ }
45
+ Reflect.defineProperty(baseObj, prop, desc);
46
+ return true;
47
+ },
48
+ getOwnPropertyDescriptor(_target, prop) {
49
+ if (prop in extObj) {
50
+ return Reflect.getOwnPropertyDescriptor(extObj, prop);
51
+ }
52
+ else {
53
+ return Reflect.getOwnPropertyDescriptor(baseObj, prop);
54
+ }
55
+ },
56
+ has(_target, prop) {
57
+ return prop in extObj || prop in baseObj;
58
+ },
59
+ });
60
+ }
@@ -0,0 +1,25 @@
1
+ /**
2
+ * AES-128 block cipher wrapper using Web Crypto API.
3
+ *
4
+ * FF1 requires raw AES block cipher (AES-ECB), which Web Crypto doesn't expose.
5
+ * We simulate AES-ECB using AES-CBC with a zero IV for single-block operations.
6
+ */
7
+ /**
8
+ * AES-128 block cipher for FF1.
9
+ * Caches the imported CryptoKey for efficiency.
10
+ */
11
+ export declare class Aes128 {
12
+ private keyPromise;
13
+ constructor(keyBytes: Uint8Array);
14
+ /**
15
+ * Encrypts a single 16-byte block using AES-128.
16
+ * Uses AES-CBC with zero IV, which is equivalent to AES-ECB for single blocks.
17
+ */
18
+ encryptBlock(block: Uint8Array): Promise<Uint8Array>;
19
+ /**
20
+ * AES-CBC-MAC: Chain multiple blocks using CBC mode.
21
+ * Returns the final encrypted block (the MAC).
22
+ */
23
+ cbcMac(blocks: Uint8Array[]): Promise<Uint8Array>;
24
+ }
25
+ //# sourceMappingURL=aes.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"aes.d.ts","sourceRoot":"","sources":["../src/aes.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAeH;;;GAGG;AACH,qBAAa,MAAM;IACjB,OAAO,CAAC,UAAU,CAAqB;gBAE3B,QAAQ,EAAE,UAAU;IAchC;;;OAGG;IACG,YAAY,CAAC,KAAK,EAAE,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC;IAmB1D;;;OAGG;IACG,MAAM,CAAC,MAAM,EAAE,UAAU,EAAE,GAAG,OAAO,CAAC,UAAU,CAAC;CAgBxD"}