@tnid/encryption 0.0.4 → 0.0.6

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/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # @tnid/encryption
2
2
 
3
- Format-preserving encryption for TNIDs - hide timestamp information by encrypting V0 TNIDs to V1.
3
+ Encrypt V0 TNIDs to V1 to hide timestamp information.
4
4
 
5
5
  ## Why Encrypt TNIDs?
6
6
 
@@ -62,13 +62,7 @@ const decrypted = await decryptV1ToV0(v1, key);
62
62
 
63
63
  ## How It Works
64
64
 
65
- The encryption uses Format-Preserving Encryption (FPE) with AES-128 in FF1 mode (NIST SP 800-38G). This encrypts the 100 Payload bits while preserving:
66
-
67
- - The TNID name (unchanged)
68
- - The UUID version/variant bits (valid UUIDv8)
69
- - The overall 128-bit structure
70
-
71
- The TNID variant changes from V0 to V1, making the encrypted ID indistinguishable from a randomly generated V1 TNID.
65
+ The encryption converts the 100 payload bits while preserving the TNID structure. The result is a valid V1 TNID that is indistinguishable from a randomly generated one.
72
66
 
73
67
  ## API Reference
74
68
 
@@ -94,26 +88,27 @@ const bytes: Uint8Array = key.asBytes();
94
88
  Encrypts a V0 TNID to V1, hiding timestamp information.
95
89
 
96
90
  ```typescript
97
- const v1 = await encryptV0ToV1("user.Br2flcNDfF6LYICnT", key);
91
+ const v0: UserId = UserId.new_v0();
92
+ const v1: UserId = await encryptV0ToV1(v0, key); // Type preserved!
98
93
  ```
99
94
 
100
- - **Input**: V0 TNID string
101
- - **Output**: V1 TNID string (same name, encrypted payload)
95
+ - **Input**: V0 TNID (any typed TNID or `DynamicTnid`)
96
+ - **Output**: V1 TNID (same type as input)
102
97
  - **Idempotent**: If input is already V1, returns it unchanged
103
- - **Throws**: `EncryptionError` if input is invalid or unsupported variant
98
+ - **Throws**: `EncryptionError` if variant is unsupported (v2/v3)
104
99
 
105
100
  ### `decryptV1ToV0(tnid, key)`
106
101
 
107
102
  Decrypts a V1 TNID back to V0, recovering timestamp information.
108
103
 
109
104
  ```typescript
110
- const v0 = await decryptV1ToV0("user.X3Wxwp0wOy4OZp_rP", key);
105
+ const decrypted: UserId = await decryptV1ToV0(v1, key); // Type preserved!
111
106
  ```
112
107
 
113
- - **Input**: V1 TNID string (encrypted)
114
- - **Output**: V0 TNID string (original with timestamp)
108
+ - **Input**: V1 TNID (any typed TNID or `DynamicTnid`)
109
+ - **Output**: V0 TNID (same type as input)
115
110
  - **Idempotent**: If input is already V0, returns it unchanged
116
- - **Throws**: `EncryptionError` if input is invalid or unsupported variant
111
+ - **Throws**: `EncryptionError` if variant is unsupported (v2/v3)
117
112
 
118
113
  ### Error Classes
119
114
 
@@ -128,17 +123,12 @@ try {
128
123
  console.log("Invalid key:", e.message);
129
124
  }
130
125
  }
131
-
132
- // EncryptionError - encryption/decryption failed
133
- try {
134
- await encryptV0ToV1("invalid-tnid", key);
135
- } catch (e) {
136
- if (e instanceof EncryptionError) {
137
- console.log("Encryption failed:", e.message);
138
- }
139
- }
140
126
  ```
141
127
 
128
+ ## Implementation Details
129
+
130
+ Uses FF1 format-preserving encryption (NIST SP 800-38G) with AES-128, which allows encrypting the 100 payload bits while maintaining the exact same bit length. This implementation is bit-compatible with the Rust TNID library.
131
+
142
132
  ## Note
143
133
 
144
134
  The encryption functionality is not part of the TNID specification. Encrypted TNIDs are standard V1 TNIDs and remain fully compatible with any TNID implementation.
package/esm/bits.js CHANGED
@@ -4,7 +4,7 @@
4
4
  * These functions extract and expand the 100 Payload bits that are
5
5
  * encrypted/decrypted, matching the Rust implementation exactly.
6
6
  */
7
- // Mask for the right-most Payload bits section (bits 0-51, 52 bits)
7
+ // Mask for the right-most Payload bits section (bits 0-59, 60 bits)
8
8
  export const RIGHT_SECRET_DATA_SECTION_MASK = 0x00000000000000000fffffffffffffffn;
9
9
  // Mask for the middle Payload bits section (bits 64-75, 12 bits)
10
10
  export const MIDDLE_SECRET_DATA_SECTION_MASK = 0x0000000000000fff0000000000000000n;
@@ -4,6 +4,7 @@
4
4
  * Provides functions to convert V0 (time-ordered) TNIDs to V1 (random-looking)
5
5
  * TNIDs and vice versa, hiding timestamp information while remaining reversible.
6
6
  */
7
+ import { DynamicTnid } from "@tnid/core";
7
8
  /**
8
9
  * Error when creating an EncryptionKey.
9
10
  */
@@ -38,19 +39,19 @@ export declare class EncryptionKey {
38
39
  /**
39
40
  * Encrypts a V0 TNID to V1, hiding timestamp information.
40
41
  *
41
- * @param tnid The V0 TNID string to encrypt
42
+ * @param tnid The V0 TNID to encrypt
42
43
  * @param key The encryption key
43
- * @returns The encrypted V1 TNID string
44
+ * @returns The encrypted V1 TNID (same type as input)
44
45
  * @throws EncryptionError if the TNID is not V0 or is invalid
45
46
  */
46
- export declare function encryptV0ToV1(tnid: string, key: EncryptionKey): Promise<string>;
47
+ export declare function encryptV0ToV1<T extends DynamicTnid>(tnid: T, key: EncryptionKey): Promise<T>;
47
48
  /**
48
49
  * Decrypts a V1 TNID back to V0, recovering timestamp information.
49
50
  *
50
- * @param tnid The V1 TNID string to decrypt
51
+ * @param tnid The V1 TNID to decrypt
51
52
  * @param key The encryption key (must match the one used for encryption)
52
- * @returns The decrypted V0 TNID string
53
+ * @returns The decrypted V0 TNID (same type as input)
53
54
  * @throws EncryptionError if the TNID is not V1 or is invalid
54
55
  */
55
- export declare function decryptV1ToV0(tnid: string, key: EncryptionKey): Promise<string>;
56
+ export declare function decryptV1ToV0<T extends DynamicTnid>(tnid: T, key: EncryptionKey): Promise<T>;
56
57
  //# sourceMappingURL=encryption.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"encryption.d.ts","sourceRoot":"","sources":["../src/encryption.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAmBH;;GAEG;AACH,qBAAa,kBAAmB,SAAQ,KAAK;gBAC/B,OAAO,EAAE,MAAM;CAI5B;AAED;;GAEG;AACH,qBAAa,eAAgB,SAAQ,KAAK;gBAC5B,OAAO,EAAE,MAAM;CAI5B;AAED;;GAEG;AACH,qBAAa,aAAa;IACxB,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAa;IAEnC,OAAO;IAIP;;OAEG;IACH,MAAM,CAAC,SAAS,CAAC,KAAK,EAAE,UAAU,GAAG,aAAa;IASlD;;OAEG;IACH,MAAM,CAAC,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,aAAa;IAsB1C;;OAEG;IACH,OAAO,IAAI,UAAU;CAGtB;AA6DD;;;;;;;GAOG;AACH,wBAAsB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,aAAa,GAAG,OAAO,CAAC,MAAM,CAAC,CAmCrF;AAED;;;;;;;GAOG;AACH,wBAAsB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,aAAa,GAAG,OAAO,CAAC,MAAM,CAAC,CAmCrF"}
1
+ {"version":3,"file":"encryption.d.ts","sourceRoot":"","sources":["../src/encryption.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAiBzC;;GAEG;AACH,qBAAa,kBAAmB,SAAQ,KAAK;gBAC/B,OAAO,EAAE,MAAM;CAI5B;AAED;;GAEG;AACH,qBAAa,eAAgB,SAAQ,KAAK;gBAC5B,OAAO,EAAE,MAAM;CAI5B;AAED;;GAEG;AACH,qBAAa,aAAa;IACxB,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAa;IAEnC,OAAO;IAIP;;OAEG;IACH,MAAM,CAAC,SAAS,CAAC,KAAK,EAAE,UAAU,GAAG,aAAa;IASlD;;OAEG;IACH,MAAM,CAAC,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,aAAa;IAsB1C;;OAEG;IACH,OAAO,IAAI,UAAU;CAGtB;AA2DD;;;;;;;GAOG;AACH,wBAAsB,aAAa,CAAC,CAAC,SAAS,WAAW,EAAE,IAAI,EAAE,CAAC,EAAE,GAAG,EAAE,aAAa,GAAG,OAAO,CAAC,CAAC,CAAC,CA8BlG;AAED;;;;;;;GAOG;AACH,wBAAsB,aAAa,CAAC,CAAC,SAAS,WAAW,EAAE,IAAI,EAAE,CAAC,EAAE,GAAG,EAAE,aAAa,GAAG,OAAO,CAAC,CAAC,CAAC,CA8BlG"}
package/esm/encryption.js CHANGED
@@ -74,16 +74,14 @@ export class EncryptionKey {
74
74
  }
75
75
  }
76
76
  /**
77
- * Convert TNID string to 128-bit value.
77
+ * Convert TNID to 128-bit value.
78
78
  */
79
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);
80
+ const uuid = DynamicTnid.toUuidString(tnid);
83
81
  return parseUuidStringToValue(uuid);
84
82
  }
85
83
  /**
86
- * Convert 128-bit value back to TNID string.
84
+ * Convert 128-bit value back to TNID.
87
85
  */
88
86
  function valueToTnid(value) {
89
87
  return valueToTnidString(value);
@@ -123,19 +121,13 @@ async function decryptPayload(payload, key) {
123
121
  /**
124
122
  * Encrypts a V0 TNID to V1, hiding timestamp information.
125
123
  *
126
- * @param tnid The V0 TNID string to encrypt
124
+ * @param tnid The V0 TNID to encrypt
127
125
  * @param key The encryption key
128
- * @returns The encrypted V1 TNID string
126
+ * @returns The encrypted V1 TNID (same type as input)
129
127
  * @throws EncryptionError if the TNID is not V0 or is invalid
130
128
  */
131
129
  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
- }
130
+ const value = tnidToValue(tnid);
139
131
  const variant = extractVariantFromValue(value);
140
132
  if (variant === "v1") {
141
133
  // Already V1, return unchanged
@@ -159,19 +151,13 @@ export async function encryptV0ToV1(tnid, key) {
159
151
  /**
160
152
  * Decrypts a V1 TNID back to V0, recovering timestamp information.
161
153
  *
162
- * @param tnid The V1 TNID string to decrypt
154
+ * @param tnid The V1 TNID to decrypt
163
155
  * @param key The encryption key (must match the one used for encryption)
164
- * @returns The decrypted V0 TNID string
156
+ * @returns The decrypted V0 TNID (same type as input)
165
157
  * @throws EncryptionError if the TNID is not V1 or is invalid
166
158
  */
167
159
  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
- }
160
+ const value = tnidToValue(tnid);
175
161
  const variant = extractVariantFromValue(value);
176
162
  if (variant === "v0") {
177
163
  // Already V0, return unchanged
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tnid/encryption",
3
- "version": "0.0.4",
3
+ "version": "0.0.6",
4
4
  "description": "Format-preserving encryption for TNIDs - convert time-ordered IDs to random-looking IDs",
5
5
  "keywords": [
6
6
  "uuid",
@@ -34,7 +34,7 @@
34
34
  "node": ">=20"
35
35
  },
36
36
  "dependencies": {
37
- "@tnid/core": "^0.0.4"
37
+ "@tnid/core": "^0.0.6"
38
38
  },
39
39
  "_generatedBy": "dnt@dev"
40
40
  }
package/script/bits.js CHANGED
@@ -13,7 +13,7 @@ exports.toHexDigits = toHexDigits;
13
13
  exports.fromHexDigits = fromHexDigits;
14
14
  exports.getVariant = getVariant;
15
15
  exports.setVariant = setVariant;
16
- // Mask for the right-most Payload bits section (bits 0-51, 52 bits)
16
+ // Mask for the right-most Payload bits section (bits 0-59, 60 bits)
17
17
  exports.RIGHT_SECRET_DATA_SECTION_MASK = 0x00000000000000000fffffffffffffffn;
18
18
  // Mask for the middle Payload bits section (bits 64-75, 12 bits)
19
19
  exports.MIDDLE_SECRET_DATA_SECTION_MASK = 0x0000000000000fff0000000000000000n;
@@ -4,6 +4,7 @@
4
4
  * Provides functions to convert V0 (time-ordered) TNIDs to V1 (random-looking)
5
5
  * TNIDs and vice versa, hiding timestamp information while remaining reversible.
6
6
  */
7
+ import { DynamicTnid } from "@tnid/core";
7
8
  /**
8
9
  * Error when creating an EncryptionKey.
9
10
  */
@@ -38,19 +39,19 @@ export declare class EncryptionKey {
38
39
  /**
39
40
  * Encrypts a V0 TNID to V1, hiding timestamp information.
40
41
  *
41
- * @param tnid The V0 TNID string to encrypt
42
+ * @param tnid The V0 TNID to encrypt
42
43
  * @param key The encryption key
43
- * @returns The encrypted V1 TNID string
44
+ * @returns The encrypted V1 TNID (same type as input)
44
45
  * @throws EncryptionError if the TNID is not V0 or is invalid
45
46
  */
46
- export declare function encryptV0ToV1(tnid: string, key: EncryptionKey): Promise<string>;
47
+ export declare function encryptV0ToV1<T extends DynamicTnid>(tnid: T, key: EncryptionKey): Promise<T>;
47
48
  /**
48
49
  * Decrypts a V1 TNID back to V0, recovering timestamp information.
49
50
  *
50
- * @param tnid The V1 TNID string to decrypt
51
+ * @param tnid The V1 TNID to decrypt
51
52
  * @param key The encryption key (must match the one used for encryption)
52
- * @returns The decrypted V0 TNID string
53
+ * @returns The decrypted V0 TNID (same type as input)
53
54
  * @throws EncryptionError if the TNID is not V1 or is invalid
54
55
  */
55
- export declare function decryptV1ToV0(tnid: string, key: EncryptionKey): Promise<string>;
56
+ export declare function decryptV1ToV0<T extends DynamicTnid>(tnid: T, key: EncryptionKey): Promise<T>;
56
57
  //# sourceMappingURL=encryption.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"encryption.d.ts","sourceRoot":"","sources":["../src/encryption.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAmBH;;GAEG;AACH,qBAAa,kBAAmB,SAAQ,KAAK;gBAC/B,OAAO,EAAE,MAAM;CAI5B;AAED;;GAEG;AACH,qBAAa,eAAgB,SAAQ,KAAK;gBAC5B,OAAO,EAAE,MAAM;CAI5B;AAED;;GAEG;AACH,qBAAa,aAAa;IACxB,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAa;IAEnC,OAAO;IAIP;;OAEG;IACH,MAAM,CAAC,SAAS,CAAC,KAAK,EAAE,UAAU,GAAG,aAAa;IASlD;;OAEG;IACH,MAAM,CAAC,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,aAAa;IAsB1C;;OAEG;IACH,OAAO,IAAI,UAAU;CAGtB;AA6DD;;;;;;;GAOG;AACH,wBAAsB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,aAAa,GAAG,OAAO,CAAC,MAAM,CAAC,CAmCrF;AAED;;;;;;;GAOG;AACH,wBAAsB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,aAAa,GAAG,OAAO,CAAC,MAAM,CAAC,CAmCrF"}
1
+ {"version":3,"file":"encryption.d.ts","sourceRoot":"","sources":["../src/encryption.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAiBzC;;GAEG;AACH,qBAAa,kBAAmB,SAAQ,KAAK;gBAC/B,OAAO,EAAE,MAAM;CAI5B;AAED;;GAEG;AACH,qBAAa,eAAgB,SAAQ,KAAK;gBAC5B,OAAO,EAAE,MAAM;CAI5B;AAED;;GAEG;AACH,qBAAa,aAAa;IACxB,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAa;IAEnC,OAAO;IAIP;;OAEG;IACH,MAAM,CAAC,SAAS,CAAC,KAAK,EAAE,UAAU,GAAG,aAAa;IASlD;;OAEG;IACH,MAAM,CAAC,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,aAAa;IAsB1C;;OAEG;IACH,OAAO,IAAI,UAAU;CAGtB;AA2DD;;;;;;;GAOG;AACH,wBAAsB,aAAa,CAAC,CAAC,SAAS,WAAW,EAAE,IAAI,EAAE,CAAC,EAAE,GAAG,EAAE,aAAa,GAAG,OAAO,CAAC,CAAC,CAAC,CA8BlG;AAED;;;;;;;GAOG;AACH,wBAAsB,aAAa,CAAC,CAAC,SAAS,WAAW,EAAE,IAAI,EAAE,CAAC,EAAE,GAAG,EAAE,aAAa,GAAG,OAAO,CAAC,CAAC,CAAC,CA8BlG"}
@@ -82,16 +82,14 @@ class EncryptionKey {
82
82
  }
83
83
  exports.EncryptionKey = EncryptionKey;
84
84
  /**
85
- * Convert TNID string to 128-bit value.
85
+ * Convert TNID to 128-bit value.
86
86
  */
87
87
  function tnidToValue(tnid) {
88
- // Use @tnid/core to convert to UUID, then parse UUID to value
89
- const parsed = core_1.DynamicTnid.parse(tnid);
90
- const uuid = core_1.DynamicTnid.toUuidString(parsed);
88
+ const uuid = core_1.DynamicTnid.toUuidString(tnid);
91
89
  return (0, uuid_1.parseUuidStringToValue)(uuid);
92
90
  }
93
91
  /**
94
- * Convert 128-bit value back to TNID string.
92
+ * Convert 128-bit value back to TNID.
95
93
  */
96
94
  function valueToTnid(value) {
97
95
  return (0, uuid_1.valueToTnidString)(value);
@@ -131,19 +129,13 @@ async function decryptPayload(payload, key) {
131
129
  /**
132
130
  * Encrypts a V0 TNID to V1, hiding timestamp information.
133
131
  *
134
- * @param tnid The V0 TNID string to encrypt
132
+ * @param tnid The V0 TNID to encrypt
135
133
  * @param key The encryption key
136
- * @returns The encrypted V1 TNID string
134
+ * @returns The encrypted V1 TNID (same type as input)
137
135
  * @throws EncryptionError if the TNID is not V0 or is invalid
138
136
  */
139
137
  async function encryptV0ToV1(tnid, key) {
140
- let value;
141
- try {
142
- value = tnidToValue(tnid);
143
- }
144
- catch (e) {
145
- throw new EncryptionError(`Invalid TNID: ${e.message}`);
146
- }
138
+ const value = tnidToValue(tnid);
147
139
  const variant = (0, uuid_1.extractVariantFromValue)(value);
148
140
  if (variant === "v1") {
149
141
  // Already V1, return unchanged
@@ -167,19 +159,13 @@ async function encryptV0ToV1(tnid, key) {
167
159
  /**
168
160
  * Decrypts a V1 TNID back to V0, recovering timestamp information.
169
161
  *
170
- * @param tnid The V1 TNID string to decrypt
162
+ * @param tnid The V1 TNID to decrypt
171
163
  * @param key The encryption key (must match the one used for encryption)
172
- * @returns The decrypted V0 TNID string
164
+ * @returns The decrypted V0 TNID (same type as input)
173
165
  * @throws EncryptionError if the TNID is not V1 or is invalid
174
166
  */
175
167
  async function decryptV1ToV0(tnid, key) {
176
- let value;
177
- try {
178
- value = tnidToValue(tnid);
179
- }
180
- catch (e) {
181
- throw new EncryptionError(`Invalid TNID: ${e.message}`);
182
- }
168
+ const value = tnidToValue(tnid);
183
169
  const variant = (0, uuid_1.extractVariantFromValue)(value);
184
170
  if (variant === "v0") {
185
171
  // Already V0, return unchanged