@veridjs/core 1.0.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/LICENSE +1 -0
- package/README.md +86 -0
- package/dist/adapters/mongo/VIDMongoAdapter.d.ts +22 -0
- package/dist/adapters/mongo/VIDMongoAdapter.js +36 -0
- package/dist/adapters/postgres/VIDPostgresAdapter.d.ts +35 -0
- package/dist/adapters/postgres/VIDPostgresAdapter.js +49 -0
- package/dist/core/VID.d.ts +103 -0
- package/dist/core/VID.js +171 -0
- package/dist/core/VIDGenerator.d.ts +53 -0
- package/dist/core/VIDGenerator.js +140 -0
- package/dist/core/VIDParser.d.ts +49 -0
- package/dist/core/VIDParser.js +136 -0
- package/dist/core/VIDValue.d.ts +25 -0
- package/dist/core/VIDValue.js +38 -0
- package/dist/core/VIDVerifier.d.ts +44 -0
- package/dist/core/VIDVerifier.js +99 -0
- package/dist/crypto/HMACSigner.d.ts +5 -0
- package/dist/crypto/HMACSigner.js +34 -0
- package/dist/encoding/Base32Decoder.d.ts +3 -0
- package/dist/encoding/Base32Decoder.js +24 -0
- package/dist/encoding/Base32Encoder.d.ts +3 -0
- package/dist/encoding/Base32Encoder.js +24 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +5 -0
- package/dist/types/index.d.ts +13 -0
- package/dist/types/index.js +2 -0
- package/dist/utils/ByteUtils.d.ts +3 -0
- package/dist/utils/ByteUtils.js +12 -0
- package/dist/utils/TimeUtils.d.ts +3 -0
- package/dist/utils/TimeUtils.js +9 -0
- package/package.json +35 -0
package/LICENSE
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
MIT License
|
package/README.md
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
# VID — Verifiable Identifier
|
|
2
|
+
|
|
3
|
+
VID is a cryptographically verifiable, globally unique identifier designed for distributed systems.
|
|
4
|
+
|
|
5
|
+
Unlike UUIDv4 or UUIDv7, VID includes built-in authenticity verification using HMAC signatures.
|
|
6
|
+
|
|
7
|
+
This prevents forged IDs and enables trustless validation.
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## Features
|
|
12
|
+
|
|
13
|
+
• Globally unique
|
|
14
|
+
• Cryptographically verifiable
|
|
15
|
+
• Database efficient (18 bytes binary)
|
|
16
|
+
• Supports MongoDB, PostgreSQL, Redis
|
|
17
|
+
• Time-sortable
|
|
18
|
+
• Zero dependencies
|
|
19
|
+
• High performance
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## Installation
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
npm install @kishan/vid
|
|
27
|
+
Quick Start
|
|
28
|
+
import { VID } from "@kishan/vid"
|
|
29
|
+
|
|
30
|
+
const vid = VID.initialize({
|
|
31
|
+
secret: "your-secret-key",
|
|
32
|
+
keyVersion: 1
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
const id = vid.generate()
|
|
36
|
+
|
|
37
|
+
console.log(id.toString())
|
|
38
|
+
|
|
39
|
+
console.log(
|
|
40
|
+
vid.verify(id.toString())
|
|
41
|
+
)
|
|
42
|
+
Storage Options
|
|
43
|
+
String storage (recommended default):
|
|
44
|
+
|
|
45
|
+
id.toString()
|
|
46
|
+
Binary storage (optimized):
|
|
47
|
+
|
|
48
|
+
id.toBinary()
|
|
49
|
+
MongoDB Example
|
|
50
|
+
collection.insertOne({
|
|
51
|
+
_id: id.toBinary()
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
vid.verify(doc._id)
|
|
55
|
+
PostgreSQL Example
|
|
56
|
+
CREATE TABLE users (
|
|
57
|
+
id BYTEA PRIMARY KEY
|
|
58
|
+
);
|
|
59
|
+
await db.query(
|
|
60
|
+
"INSERT INTO users (id) VALUES ($1)",
|
|
61
|
+
[id.toBinary()]
|
|
62
|
+
)
|
|
63
|
+
Verification
|
|
64
|
+
vid.verify(id)
|
|
65
|
+
Returns true if valid.
|
|
66
|
+
|
|
67
|
+
False if tampered or forged.
|
|
68
|
+
|
|
69
|
+
Parsing
|
|
70
|
+
const metadata = vid.parse(id)
|
|
71
|
+
|
|
72
|
+
console.log(metadata.timestamp)
|
|
73
|
+
console.log(metadata.nodeId)
|
|
74
|
+
Security
|
|
75
|
+
VID uses HMAC-SHA256 signature truncated to 56 bits.
|
|
76
|
+
|
|
77
|
+
Verification uses constant-time comparison to prevent timing attacks.
|
|
78
|
+
|
|
79
|
+
Secret key never exposed.
|
|
80
|
+
|
|
81
|
+
Performance
|
|
82
|
+
Binary size: 18 bytes
|
|
83
|
+
String size: 26 chars
|
|
84
|
+
|
|
85
|
+
Index size smaller than UUIDv7.
|
|
86
|
+
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MongoDB adapter for VID.
|
|
3
|
+
*
|
|
4
|
+
* Converts between VID binary format and MongoDB BSON Binary type.
|
|
5
|
+
*
|
|
6
|
+
* Does NOT require VIDValue instance.
|
|
7
|
+
* Works with Uint8Array directly.
|
|
8
|
+
*/
|
|
9
|
+
export declare class VIDMongoAdapter {
|
|
10
|
+
/**
|
|
11
|
+
* Convert VID binary to MongoDB Binary.
|
|
12
|
+
*
|
|
13
|
+
* Accepts Uint8Array (from id.toBinary())
|
|
14
|
+
*/
|
|
15
|
+
static toDatabase(binary: Uint8Array): Buffer;
|
|
16
|
+
/**
|
|
17
|
+
* Convert MongoDB Binary to Uint8Array.
|
|
18
|
+
*
|
|
19
|
+
* Output can be passed directly to vid.verify()
|
|
20
|
+
*/
|
|
21
|
+
static fromDatabase(value: Buffer): Uint8Array;
|
|
22
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.VIDMongoAdapter = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* MongoDB adapter for VID.
|
|
6
|
+
*
|
|
7
|
+
* Converts between VID binary format and MongoDB BSON Binary type.
|
|
8
|
+
*
|
|
9
|
+
* Does NOT require VIDValue instance.
|
|
10
|
+
* Works with Uint8Array directly.
|
|
11
|
+
*/
|
|
12
|
+
class VIDMongoAdapter {
|
|
13
|
+
/**
|
|
14
|
+
* Convert VID binary to MongoDB Binary.
|
|
15
|
+
*
|
|
16
|
+
* Accepts Uint8Array (from id.toBinary())
|
|
17
|
+
*/
|
|
18
|
+
static toDatabase(binary) {
|
|
19
|
+
if (!(binary instanceof Uint8Array)) {
|
|
20
|
+
throw new Error("Expected Uint8Array");
|
|
21
|
+
}
|
|
22
|
+
return Buffer.from(binary);
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Convert MongoDB Binary to Uint8Array.
|
|
26
|
+
*
|
|
27
|
+
* Output can be passed directly to vid.verify()
|
|
28
|
+
*/
|
|
29
|
+
static fromDatabase(value) {
|
|
30
|
+
if (!(value instanceof Buffer)) {
|
|
31
|
+
throw new Error("Expected MongoDB Binary");
|
|
32
|
+
}
|
|
33
|
+
return new Uint8Array(value);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
exports.VIDMongoAdapter = VIDMongoAdapter;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PostgreSQL adapter for VID.
|
|
3
|
+
*
|
|
4
|
+
* Converts between VID binary format and PostgreSQL BYTEA format.
|
|
5
|
+
*
|
|
6
|
+
* PostgreSQL BYTEA maps to Node.js Buffer.
|
|
7
|
+
*
|
|
8
|
+
* This adapter does NOT depend on VIDValue.
|
|
9
|
+
* Works only with Uint8Array and Buffer.
|
|
10
|
+
*/
|
|
11
|
+
export declare class VIDPostgresAdapter {
|
|
12
|
+
/**
|
|
13
|
+
* Convert VID binary (Uint8Array) to PostgreSQL Buffer.
|
|
14
|
+
*
|
|
15
|
+
* Usage:
|
|
16
|
+
*
|
|
17
|
+
* await db.query(
|
|
18
|
+
* "INSERT INTO users (id) VALUES ($1)",
|
|
19
|
+
* [VIDPostgresAdapter.toDatabase(id.toBinary())]
|
|
20
|
+
* )
|
|
21
|
+
*/
|
|
22
|
+
static toDatabase(binary: Uint8Array): Buffer;
|
|
23
|
+
/**
|
|
24
|
+
* Convert PostgreSQL BYTEA (Buffer) to Uint8Array.
|
|
25
|
+
*
|
|
26
|
+
* Usage:
|
|
27
|
+
*
|
|
28
|
+
* const result = await db.query(...)
|
|
29
|
+
*
|
|
30
|
+
* vid.verify(
|
|
31
|
+
* VIDPostgresAdapter.fromDatabase(result.rows[0].id)
|
|
32
|
+
* )
|
|
33
|
+
*/
|
|
34
|
+
static fromDatabase(buffer: Buffer): Uint8Array;
|
|
35
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.VIDPostgresAdapter = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* PostgreSQL adapter for VID.
|
|
6
|
+
*
|
|
7
|
+
* Converts between VID binary format and PostgreSQL BYTEA format.
|
|
8
|
+
*
|
|
9
|
+
* PostgreSQL BYTEA maps to Node.js Buffer.
|
|
10
|
+
*
|
|
11
|
+
* This adapter does NOT depend on VIDValue.
|
|
12
|
+
* Works only with Uint8Array and Buffer.
|
|
13
|
+
*/
|
|
14
|
+
class VIDPostgresAdapter {
|
|
15
|
+
/**
|
|
16
|
+
* Convert VID binary (Uint8Array) to PostgreSQL Buffer.
|
|
17
|
+
*
|
|
18
|
+
* Usage:
|
|
19
|
+
*
|
|
20
|
+
* await db.query(
|
|
21
|
+
* "INSERT INTO users (id) VALUES ($1)",
|
|
22
|
+
* [VIDPostgresAdapter.toDatabase(id.toBinary())]
|
|
23
|
+
* )
|
|
24
|
+
*/
|
|
25
|
+
static toDatabase(binary) {
|
|
26
|
+
if (!(binary instanceof Uint8Array)) {
|
|
27
|
+
throw new Error("VIDPostgresAdapter.toDatabase: expected Uint8Array");
|
|
28
|
+
}
|
|
29
|
+
return Buffer.from(binary);
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Convert PostgreSQL BYTEA (Buffer) to Uint8Array.
|
|
33
|
+
*
|
|
34
|
+
* Usage:
|
|
35
|
+
*
|
|
36
|
+
* const result = await db.query(...)
|
|
37
|
+
*
|
|
38
|
+
* vid.verify(
|
|
39
|
+
* VIDPostgresAdapter.fromDatabase(result.rows[0].id)
|
|
40
|
+
* )
|
|
41
|
+
*/
|
|
42
|
+
static fromDatabase(buffer) {
|
|
43
|
+
if (!Buffer.isBuffer(buffer)) {
|
|
44
|
+
throw new Error("VIDPostgresAdapter.fromDatabase: expected Buffer");
|
|
45
|
+
}
|
|
46
|
+
return new Uint8Array(buffer);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
exports.VIDPostgresAdapter = VIDPostgresAdapter;
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { VIDOptions } from "../types";
|
|
2
|
+
import { VIDValue } from "./VIDValue";
|
|
3
|
+
/**
|
|
4
|
+
* VID — Verifiable Identifier Engine
|
|
5
|
+
*
|
|
6
|
+
* This class is the main entry point for generating, verifying,
|
|
7
|
+
* and parsing VIDs.
|
|
8
|
+
*
|
|
9
|
+
* It encapsulates all configuration required for identifier generation,
|
|
10
|
+
* including cryptographic secret, key version, and node identity.
|
|
11
|
+
*
|
|
12
|
+
* This class is stateless beyond its configuration and safe to use
|
|
13
|
+
* in distributed environments.
|
|
14
|
+
*
|
|
15
|
+
* Example usage:
|
|
16
|
+
*
|
|
17
|
+
* ```ts
|
|
18
|
+
* const vid = VID.initialize({
|
|
19
|
+
* secret: process.env.VID_SECRET!,
|
|
20
|
+
* keyVersion: 1,
|
|
21
|
+
* nodeId: 42
|
|
22
|
+
* })
|
|
23
|
+
*
|
|
24
|
+
* const id = vid.generate()
|
|
25
|
+
*
|
|
26
|
+
* console.log(id.toString())
|
|
27
|
+
*
|
|
28
|
+
* const valid = vid.verify(id)
|
|
29
|
+
*
|
|
30
|
+
* const metadata = vid.parse(id)
|
|
31
|
+
* ```
|
|
32
|
+
*
|
|
33
|
+
* Configuration must be initialized once per service instance.
|
|
34
|
+
*/
|
|
35
|
+
export declare class VID {
|
|
36
|
+
/**
|
|
37
|
+
* Cryptographic secret used for HMAC signing.
|
|
38
|
+
*
|
|
39
|
+
* Stored internally as byte array for deterministic cryptographic behavior.
|
|
40
|
+
*/
|
|
41
|
+
private readonly secret;
|
|
42
|
+
/**
|
|
43
|
+
* Secret version identifier.
|
|
44
|
+
*
|
|
45
|
+
* Embedded in VID binary structure.
|
|
46
|
+
* Enables secret rotation.
|
|
47
|
+
*/
|
|
48
|
+
private readonly keyVersion;
|
|
49
|
+
/**
|
|
50
|
+
* Unique identifier for this node.
|
|
51
|
+
*
|
|
52
|
+
* Prevents collisions in distributed systems.
|
|
53
|
+
*/
|
|
54
|
+
private readonly nodeId;
|
|
55
|
+
/**
|
|
56
|
+
* Private constructor.
|
|
57
|
+
*
|
|
58
|
+
* Use VID.initialize() instead.
|
|
59
|
+
*/
|
|
60
|
+
private constructor();
|
|
61
|
+
/**
|
|
62
|
+
* Initializes VID engine instance.
|
|
63
|
+
*
|
|
64
|
+
* This must be called before generating identifiers.
|
|
65
|
+
*
|
|
66
|
+
* @param options.secret Cryptographic secret string (required)
|
|
67
|
+
* @param options.keyVersion Secret version number (required)
|
|
68
|
+
* @param options.nodeId Optional node identifier
|
|
69
|
+
*
|
|
70
|
+
* @returns VID instance
|
|
71
|
+
*
|
|
72
|
+
* Throws error if configuration invalid.
|
|
73
|
+
*/
|
|
74
|
+
static initialize(options: VIDOptions): VID;
|
|
75
|
+
/**
|
|
76
|
+
* Generates a new VID.
|
|
77
|
+
*
|
|
78
|
+
* @returns VIDValue object
|
|
79
|
+
*
|
|
80
|
+
* Thread-safe within Node.js event loop.
|
|
81
|
+
*/
|
|
82
|
+
generate(): VIDValue;
|
|
83
|
+
/**
|
|
84
|
+
* Verifies VID authenticity.
|
|
85
|
+
*
|
|
86
|
+
* Ensures identifier was generated using correct secret.
|
|
87
|
+
*
|
|
88
|
+
* @param id VIDValue instance
|
|
89
|
+
*
|
|
90
|
+
* @returns true if valid, false otherwise
|
|
91
|
+
*/
|
|
92
|
+
verify(input: string | Uint8Array | Buffer | ArrayBuffer): boolean;
|
|
93
|
+
/**
|
|
94
|
+
* Parses VID metadata.
|
|
95
|
+
*
|
|
96
|
+
* Extracts timestamp, nodeId, sequence, and keyVersion.
|
|
97
|
+
*
|
|
98
|
+
* @param id VIDValue instance
|
|
99
|
+
*
|
|
100
|
+
* @returns VIDMetadata
|
|
101
|
+
*/
|
|
102
|
+
parse(input: string | Uint8Array | Buffer | ArrayBuffer): import("../types").VIDMetadata;
|
|
103
|
+
}
|
package/dist/core/VID.js
ADDED
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.VID = void 0;
|
|
4
|
+
const VIDGenerator_1 = require("./VIDGenerator");
|
|
5
|
+
const VIDVerifier_1 = require("./VIDVerifier");
|
|
6
|
+
const VIDParser_1 = require("./VIDParser");
|
|
7
|
+
/**
|
|
8
|
+
* VID — Verifiable Identifier Engine
|
|
9
|
+
*
|
|
10
|
+
* This class is the main entry point for generating, verifying,
|
|
11
|
+
* and parsing VIDs.
|
|
12
|
+
*
|
|
13
|
+
* It encapsulates all configuration required for identifier generation,
|
|
14
|
+
* including cryptographic secret, key version, and node identity.
|
|
15
|
+
*
|
|
16
|
+
* This class is stateless beyond its configuration and safe to use
|
|
17
|
+
* in distributed environments.
|
|
18
|
+
*
|
|
19
|
+
* Example usage:
|
|
20
|
+
*
|
|
21
|
+
* ```ts
|
|
22
|
+
* const vid = VID.initialize({
|
|
23
|
+
* secret: process.env.VID_SECRET!,
|
|
24
|
+
* keyVersion: 1,
|
|
25
|
+
* nodeId: 42
|
|
26
|
+
* })
|
|
27
|
+
*
|
|
28
|
+
* const id = vid.generate()
|
|
29
|
+
*
|
|
30
|
+
* console.log(id.toString())
|
|
31
|
+
*
|
|
32
|
+
* const valid = vid.verify(id)
|
|
33
|
+
*
|
|
34
|
+
* const metadata = vid.parse(id)
|
|
35
|
+
* ```
|
|
36
|
+
*
|
|
37
|
+
* Configuration must be initialized once per service instance.
|
|
38
|
+
*/
|
|
39
|
+
class VID {
|
|
40
|
+
/**
|
|
41
|
+
* Private constructor.
|
|
42
|
+
*
|
|
43
|
+
* Use VID.initialize() instead.
|
|
44
|
+
*/
|
|
45
|
+
constructor(options) {
|
|
46
|
+
// Validate options object
|
|
47
|
+
if (!options) {
|
|
48
|
+
throw new Error("VID initialization failed: options object is required");
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Validate secret presence
|
|
52
|
+
*/
|
|
53
|
+
if (options.secret === undefined || options.secret === null) {
|
|
54
|
+
throw new Error("VID initialization failed: secret is required");
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Validate secret type
|
|
58
|
+
*/
|
|
59
|
+
if (typeof options.secret !== "string") {
|
|
60
|
+
throw new Error("VID initialization failed: secret must be a string");
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Validate secret length
|
|
64
|
+
*/
|
|
65
|
+
if (options.secret.trim().length === 0) {
|
|
66
|
+
throw new Error("VID initialization failed: secret must not be empty");
|
|
67
|
+
}
|
|
68
|
+
if (options.secret.length < 8) {
|
|
69
|
+
throw new Error("VID initialization failed: secret must be at least 8 characters");
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Validate keyVersion presence
|
|
73
|
+
*/
|
|
74
|
+
if (options.keyVersion === undefined || options.keyVersion === null) {
|
|
75
|
+
throw new Error("VID initialization failed: keyVersion is required");
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Validate keyVersion type
|
|
79
|
+
*/
|
|
80
|
+
if (!Number.isInteger(options.keyVersion)) {
|
|
81
|
+
throw new Error("VID initialization failed: keyVersion must be an integer");
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Validate keyVersion range
|
|
85
|
+
*/
|
|
86
|
+
if (options.keyVersion < 0 || options.keyVersion > 255) {
|
|
87
|
+
throw new Error("VID initialization failed: keyVersion must be between 0 and 255");
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Validate nodeId if provided
|
|
91
|
+
*/
|
|
92
|
+
if (options.nodeId !== undefined) {
|
|
93
|
+
if (!Number.isInteger(options.nodeId)) {
|
|
94
|
+
throw new Error("VID initialization failed: nodeId must be an integer");
|
|
95
|
+
}
|
|
96
|
+
if (options.nodeId < 0 || options.nodeId > 65535) {
|
|
97
|
+
throw new Error("VID initialization failed: nodeId must be between 0 and 65535");
|
|
98
|
+
}
|
|
99
|
+
this.nodeId = options.nodeId;
|
|
100
|
+
}
|
|
101
|
+
else {
|
|
102
|
+
/**
|
|
103
|
+
* Auto-generate nodeId safely
|
|
104
|
+
*
|
|
105
|
+
* Uses process and time entropy to reduce collision risk.
|
|
106
|
+
*/
|
|
107
|
+
this.nodeId = ((process.pid ^ Date.now()) & 0xffff);
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Convert secret string to byte array
|
|
111
|
+
*/
|
|
112
|
+
this.secret =
|
|
113
|
+
new TextEncoder().encode(options.secret);
|
|
114
|
+
this.keyVersion = options.keyVersion;
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Initializes VID engine instance.
|
|
118
|
+
*
|
|
119
|
+
* This must be called before generating identifiers.
|
|
120
|
+
*
|
|
121
|
+
* @param options.secret Cryptographic secret string (required)
|
|
122
|
+
* @param options.keyVersion Secret version number (required)
|
|
123
|
+
* @param options.nodeId Optional node identifier
|
|
124
|
+
*
|
|
125
|
+
* @returns VID instance
|
|
126
|
+
*
|
|
127
|
+
* Throws error if configuration invalid.
|
|
128
|
+
*/
|
|
129
|
+
static initialize(options) {
|
|
130
|
+
return new VID(options);
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Generates a new VID.
|
|
134
|
+
*
|
|
135
|
+
* @returns VIDValue object
|
|
136
|
+
*
|
|
137
|
+
* Thread-safe within Node.js event loop.
|
|
138
|
+
*/
|
|
139
|
+
generate() {
|
|
140
|
+
return VIDGenerator_1.VIDGenerator.generate({
|
|
141
|
+
secret: this.secret,
|
|
142
|
+
keyVersion: this.keyVersion,
|
|
143
|
+
nodeId: this.nodeId
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Verifies VID authenticity.
|
|
148
|
+
*
|
|
149
|
+
* Ensures identifier was generated using correct secret.
|
|
150
|
+
*
|
|
151
|
+
* @param id VIDValue instance
|
|
152
|
+
*
|
|
153
|
+
* @returns true if valid, false otherwise
|
|
154
|
+
*/
|
|
155
|
+
verify(input) {
|
|
156
|
+
return VIDVerifier_1.VIDVerifier.verify(input, this.secret);
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* Parses VID metadata.
|
|
160
|
+
*
|
|
161
|
+
* Extracts timestamp, nodeId, sequence, and keyVersion.
|
|
162
|
+
*
|
|
163
|
+
* @param id VIDValue instance
|
|
164
|
+
*
|
|
165
|
+
* @returns VIDMetadata
|
|
166
|
+
*/
|
|
167
|
+
parse(input) {
|
|
168
|
+
return VIDParser_1.VIDParser.parse(input);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
exports.VID = VID;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { VIDValue } from "./VIDValue";
|
|
2
|
+
/**
|
|
3
|
+
* VIDGenerator is responsible for constructing VID binary identifiers.
|
|
4
|
+
*
|
|
5
|
+
* This class is INTERNAL and should never be used directly by library consumers.
|
|
6
|
+
*
|
|
7
|
+
* It assumes all inputs have already been validated by the VID class.
|
|
8
|
+
*
|
|
9
|
+
* Responsibilities:
|
|
10
|
+
*
|
|
11
|
+
* - Construct VID payload
|
|
12
|
+
* - Ensure per-millisecond uniqueness using sequence counter
|
|
13
|
+
* - Generate cryptographic signature
|
|
14
|
+
* - Combine payload and signature into final VID binary
|
|
15
|
+
*
|
|
16
|
+
* VID binary layout (18 bytes total):
|
|
17
|
+
*
|
|
18
|
+
* Byte 0 → keyVersion (1 byte)
|
|
19
|
+
*
|
|
20
|
+
* Bytes 1–6 → timestamp (6 bytes, milliseconds)
|
|
21
|
+
*
|
|
22
|
+
* Bytes 7–8 → nodeId (2 bytes)
|
|
23
|
+
*
|
|
24
|
+
* Bytes 9–10 → sequence (2 bytes)
|
|
25
|
+
*
|
|
26
|
+
* Bytes 11–17 → signature (7 bytes, HMAC truncated)
|
|
27
|
+
*/
|
|
28
|
+
export declare class VIDGenerator {
|
|
29
|
+
/**
|
|
30
|
+
* Generates a new VIDValue instance.
|
|
31
|
+
*
|
|
32
|
+
* This method is called internally by VID.generate().
|
|
33
|
+
*
|
|
34
|
+
* @param config.secret Secret key (Uint8Array)
|
|
35
|
+
* @param config.keyVersion Secret version identifier
|
|
36
|
+
* @param config.nodeId Unique node identifier
|
|
37
|
+
*
|
|
38
|
+
* @returns VIDValue instance containing binary identifier
|
|
39
|
+
*
|
|
40
|
+
* Internal assumptions:
|
|
41
|
+
*
|
|
42
|
+
* - secret is valid Uint8Array
|
|
43
|
+
* - keyVersion is valid uint8
|
|
44
|
+
* - nodeId is valid uint16
|
|
45
|
+
*
|
|
46
|
+
* These guarantees are enforced by VID.initialize().
|
|
47
|
+
*/
|
|
48
|
+
static generate(config: {
|
|
49
|
+
secret: Uint8Array;
|
|
50
|
+
keyVersion: number;
|
|
51
|
+
nodeId: number;
|
|
52
|
+
}): VIDValue;
|
|
53
|
+
}
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.VIDGenerator = void 0;
|
|
4
|
+
const ByteUtils_1 = require("../utils/ByteUtils");
|
|
5
|
+
const HMACSigner_1 = require("../crypto/HMACSigner");
|
|
6
|
+
const VIDValue_1 = require("./VIDValue");
|
|
7
|
+
/**
|
|
8
|
+
* Internal timestamp tracker.
|
|
9
|
+
*
|
|
10
|
+
* Used to detect multiple ID generation events within the same millisecond.
|
|
11
|
+
*
|
|
12
|
+
* Ensures monotonic uniqueness when generating high volumes of IDs.
|
|
13
|
+
*/
|
|
14
|
+
let lastTimestamp = 0;
|
|
15
|
+
/**
|
|
16
|
+
* Per-millisecond sequence counter.
|
|
17
|
+
*
|
|
18
|
+
* Prevents collisions when multiple identifiers are generated within the same millisecond.
|
|
19
|
+
*
|
|
20
|
+
* Range: 0–65535 (uint16)
|
|
21
|
+
*/
|
|
22
|
+
let sequence = 0;
|
|
23
|
+
/**
|
|
24
|
+
* VIDGenerator is responsible for constructing VID binary identifiers.
|
|
25
|
+
*
|
|
26
|
+
* This class is INTERNAL and should never be used directly by library consumers.
|
|
27
|
+
*
|
|
28
|
+
* It assumes all inputs have already been validated by the VID class.
|
|
29
|
+
*
|
|
30
|
+
* Responsibilities:
|
|
31
|
+
*
|
|
32
|
+
* - Construct VID payload
|
|
33
|
+
* - Ensure per-millisecond uniqueness using sequence counter
|
|
34
|
+
* - Generate cryptographic signature
|
|
35
|
+
* - Combine payload and signature into final VID binary
|
|
36
|
+
*
|
|
37
|
+
* VID binary layout (18 bytes total):
|
|
38
|
+
*
|
|
39
|
+
* Byte 0 → keyVersion (1 byte)
|
|
40
|
+
*
|
|
41
|
+
* Bytes 1–6 → timestamp (6 bytes, milliseconds)
|
|
42
|
+
*
|
|
43
|
+
* Bytes 7–8 → nodeId (2 bytes)
|
|
44
|
+
*
|
|
45
|
+
* Bytes 9–10 → sequence (2 bytes)
|
|
46
|
+
*
|
|
47
|
+
* Bytes 11–17 → signature (7 bytes, HMAC truncated)
|
|
48
|
+
*/
|
|
49
|
+
class VIDGenerator {
|
|
50
|
+
/**
|
|
51
|
+
* Generates a new VIDValue instance.
|
|
52
|
+
*
|
|
53
|
+
* This method is called internally by VID.generate().
|
|
54
|
+
*
|
|
55
|
+
* @param config.secret Secret key (Uint8Array)
|
|
56
|
+
* @param config.keyVersion Secret version identifier
|
|
57
|
+
* @param config.nodeId Unique node identifier
|
|
58
|
+
*
|
|
59
|
+
* @returns VIDValue instance containing binary identifier
|
|
60
|
+
*
|
|
61
|
+
* Internal assumptions:
|
|
62
|
+
*
|
|
63
|
+
* - secret is valid Uint8Array
|
|
64
|
+
* - keyVersion is valid uint8
|
|
65
|
+
* - nodeId is valid uint16
|
|
66
|
+
*
|
|
67
|
+
* These guarantees are enforced by VID.initialize().
|
|
68
|
+
*/
|
|
69
|
+
static generate(config) {
|
|
70
|
+
/**
|
|
71
|
+
* Get current timestamp in milliseconds.
|
|
72
|
+
*/
|
|
73
|
+
let timestamp = Date.now();
|
|
74
|
+
/**
|
|
75
|
+
* Handle same-millisecond generation.
|
|
76
|
+
*
|
|
77
|
+
* If generating multiple IDs within same millisecond,
|
|
78
|
+
* increment sequence counter.
|
|
79
|
+
*/
|
|
80
|
+
if (timestamp === lastTimestamp) {
|
|
81
|
+
sequence++;
|
|
82
|
+
/**
|
|
83
|
+
* Prevent uint16 overflow.
|
|
84
|
+
*
|
|
85
|
+
* More than 65,536 IDs per millisecond is extremely unlikely.
|
|
86
|
+
*/
|
|
87
|
+
if (sequence > 65535) {
|
|
88
|
+
throw new Error("VID generation overflow: too many IDs generated in same millisecond");
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
else {
|
|
92
|
+
sequence = 0;
|
|
93
|
+
lastTimestamp = timestamp;
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Construct payload buffer (11 bytes).
|
|
97
|
+
*
|
|
98
|
+
* Payload contains core identifier data excluding signature.
|
|
99
|
+
*/
|
|
100
|
+
const payload = new Uint8Array(11);
|
|
101
|
+
const view = new DataView(payload.buffer);
|
|
102
|
+
/**
|
|
103
|
+
* Byte 0 → keyVersion
|
|
104
|
+
*/
|
|
105
|
+
view.setUint8(0, config.keyVersion);
|
|
106
|
+
/**
|
|
107
|
+
* Bytes 1–6 → timestamp (48-bit)
|
|
108
|
+
*
|
|
109
|
+
* Split into:
|
|
110
|
+
*
|
|
111
|
+
* high 32 bits → bytes 1–4
|
|
112
|
+
* low 16 bits → bytes 5–6
|
|
113
|
+
*/
|
|
114
|
+
view.setUint32(1, Math.floor(timestamp / 65536));
|
|
115
|
+
view.setUint16(5, timestamp % 65536);
|
|
116
|
+
/**
|
|
117
|
+
* Bytes 7–8 → nodeId
|
|
118
|
+
*/
|
|
119
|
+
view.setUint16(7, config.nodeId);
|
|
120
|
+
/**
|
|
121
|
+
* Bytes 9–10 → sequence
|
|
122
|
+
*/
|
|
123
|
+
view.setUint16(9, sequence);
|
|
124
|
+
/**
|
|
125
|
+
* Generate cryptographic signature.
|
|
126
|
+
*
|
|
127
|
+
* Signature ensures identifier authenticity.
|
|
128
|
+
*/
|
|
129
|
+
const signature = HMACSigner_1.HMACSigner.sign(payload, config.secret);
|
|
130
|
+
/**
|
|
131
|
+
* Combine payload and signature into final binary identifier.
|
|
132
|
+
*/
|
|
133
|
+
const binary = ByteUtils_1.ByteUtils.concat(payload, signature);
|
|
134
|
+
/**
|
|
135
|
+
* Return VIDValue instance.
|
|
136
|
+
*/
|
|
137
|
+
return new VIDValue_1.VIDValue(binary);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
exports.VIDGenerator = VIDGenerator;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { VIDMetadata } from "../types";
|
|
2
|
+
/**
|
|
3
|
+
* VIDParser
|
|
4
|
+
*
|
|
5
|
+
* Responsible for extracting metadata from a VID identifier.
|
|
6
|
+
*
|
|
7
|
+
* This class does NOT perform cryptographic verification.
|
|
8
|
+
* It only decodes structural fields embedded in the VID.
|
|
9
|
+
*
|
|
10
|
+
* Supported input formats:
|
|
11
|
+
*
|
|
12
|
+
* - Base32 string
|
|
13
|
+
* - Uint8Array
|
|
14
|
+
* - Buffer (Node.js)
|
|
15
|
+
* - ArrayBuffer
|
|
16
|
+
*
|
|
17
|
+
* VID binary layout (18 bytes total):
|
|
18
|
+
*
|
|
19
|
+
* Byte 0 → keyVersion (1 byte)
|
|
20
|
+
* Bytes 1–6 → timestamp (6 bytes, 48-bit)
|
|
21
|
+
* Bytes 7–8 → nodeId (2 bytes)
|
|
22
|
+
* Bytes 9–10 → sequence (2 bytes)
|
|
23
|
+
* Bytes 11–17 → signature (7 bytes)
|
|
24
|
+
*
|
|
25
|
+
* Returned metadata does NOT include signature.
|
|
26
|
+
*/
|
|
27
|
+
export declare class VIDParser {
|
|
28
|
+
/**
|
|
29
|
+
* Parses VID and extracts metadata fields.
|
|
30
|
+
*
|
|
31
|
+
* @param input VID in string or binary format
|
|
32
|
+
*
|
|
33
|
+
* @returns VIDMetadata object containing:
|
|
34
|
+
*
|
|
35
|
+
* - keyVersion → secret version used
|
|
36
|
+
* - timestamp → creation timestamp (ms)
|
|
37
|
+
* - nodeId → generator node identifier
|
|
38
|
+
* - sequence → per-ms sequence counter
|
|
39
|
+
*
|
|
40
|
+
* Throws error for invalid input structure.
|
|
41
|
+
*/
|
|
42
|
+
static parse(input: string | Uint8Array | Buffer | ArrayBuffer): VIDMetadata;
|
|
43
|
+
/**
|
|
44
|
+
* Normalizes input into canonical Uint8Array.
|
|
45
|
+
*
|
|
46
|
+
* Accepts string, Buffer, Uint8Array, ArrayBuffer.
|
|
47
|
+
*/
|
|
48
|
+
private static normalizeToBinary;
|
|
49
|
+
}
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.VIDParser = void 0;
|
|
4
|
+
const Base32Decoder_1 = require("../encoding/Base32Decoder");
|
|
5
|
+
/**
|
|
6
|
+
* VIDParser
|
|
7
|
+
*
|
|
8
|
+
* Responsible for extracting metadata from a VID identifier.
|
|
9
|
+
*
|
|
10
|
+
* This class does NOT perform cryptographic verification.
|
|
11
|
+
* It only decodes structural fields embedded in the VID.
|
|
12
|
+
*
|
|
13
|
+
* Supported input formats:
|
|
14
|
+
*
|
|
15
|
+
* - Base32 string
|
|
16
|
+
* - Uint8Array
|
|
17
|
+
* - Buffer (Node.js)
|
|
18
|
+
* - ArrayBuffer
|
|
19
|
+
*
|
|
20
|
+
* VID binary layout (18 bytes total):
|
|
21
|
+
*
|
|
22
|
+
* Byte 0 → keyVersion (1 byte)
|
|
23
|
+
* Bytes 1–6 → timestamp (6 bytes, 48-bit)
|
|
24
|
+
* Bytes 7–8 → nodeId (2 bytes)
|
|
25
|
+
* Bytes 9–10 → sequence (2 bytes)
|
|
26
|
+
* Bytes 11–17 → signature (7 bytes)
|
|
27
|
+
*
|
|
28
|
+
* Returned metadata does NOT include signature.
|
|
29
|
+
*/
|
|
30
|
+
class VIDParser {
|
|
31
|
+
/**
|
|
32
|
+
* Parses VID and extracts metadata fields.
|
|
33
|
+
*
|
|
34
|
+
* @param input VID in string or binary format
|
|
35
|
+
*
|
|
36
|
+
* @returns VIDMetadata object containing:
|
|
37
|
+
*
|
|
38
|
+
* - keyVersion → secret version used
|
|
39
|
+
* - timestamp → creation timestamp (ms)
|
|
40
|
+
* - nodeId → generator node identifier
|
|
41
|
+
* - sequence → per-ms sequence counter
|
|
42
|
+
*
|
|
43
|
+
* Throws error for invalid input structure.
|
|
44
|
+
*/
|
|
45
|
+
static parse(input) {
|
|
46
|
+
const binary = this.normalizeToBinary(input);
|
|
47
|
+
if (binary.length !== 18) {
|
|
48
|
+
throw new Error("VID parse failed: invalid VID length");
|
|
49
|
+
}
|
|
50
|
+
const view = new DataView(binary.buffer, binary.byteOffset, binary.byteLength);
|
|
51
|
+
/**
|
|
52
|
+
* Extract keyVersion (1 byte)
|
|
53
|
+
*/
|
|
54
|
+
const keyVersion = view.getUint8(0);
|
|
55
|
+
/**
|
|
56
|
+
* Extract timestamp (48-bit)
|
|
57
|
+
*
|
|
58
|
+
* Recombine:
|
|
59
|
+
*
|
|
60
|
+
* high 32 bits → bytes 1–4
|
|
61
|
+
* low 16 bits → bytes 5–6
|
|
62
|
+
*/
|
|
63
|
+
const timestamp = view.getUint32(1) * 65536 +
|
|
64
|
+
view.getUint16(5);
|
|
65
|
+
/**
|
|
66
|
+
* Extract nodeId (2 bytes)
|
|
67
|
+
*/
|
|
68
|
+
const nodeId = view.getUint16(7);
|
|
69
|
+
/**
|
|
70
|
+
* Extract sequence (2 bytes)
|
|
71
|
+
*/
|
|
72
|
+
const sequence = view.getUint16(9);
|
|
73
|
+
/**
|
|
74
|
+
* Optional derived fields for usability
|
|
75
|
+
*/
|
|
76
|
+
const date = new Date(timestamp);
|
|
77
|
+
const iso = date.toISOString();
|
|
78
|
+
return {
|
|
79
|
+
keyVersion,
|
|
80
|
+
timestamp,
|
|
81
|
+
nodeId,
|
|
82
|
+
sequence,
|
|
83
|
+
/**
|
|
84
|
+
* Helpful derived values
|
|
85
|
+
*/
|
|
86
|
+
date,
|
|
87
|
+
iso
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Normalizes input into canonical Uint8Array.
|
|
92
|
+
*
|
|
93
|
+
* Accepts string, Buffer, Uint8Array, ArrayBuffer.
|
|
94
|
+
*/
|
|
95
|
+
static normalizeToBinary(input) {
|
|
96
|
+
if (input === null || input === undefined) {
|
|
97
|
+
throw new Error("VID parse failed: input is required");
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Handle Base32 string
|
|
101
|
+
*/
|
|
102
|
+
if (typeof input === "string") {
|
|
103
|
+
const trimmed = input.trim();
|
|
104
|
+
if (trimmed.length === 0) {
|
|
105
|
+
throw new Error("VID parse failed: empty string");
|
|
106
|
+
}
|
|
107
|
+
try {
|
|
108
|
+
return Base32Decoder_1.Base32Decoder.decode(trimmed);
|
|
109
|
+
}
|
|
110
|
+
catch {
|
|
111
|
+
throw new Error("VID parse failed: invalid string encoding");
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Handle Uint8Array
|
|
116
|
+
*/
|
|
117
|
+
if (input instanceof Uint8Array) {
|
|
118
|
+
return input;
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Handle Node.js Buffer
|
|
122
|
+
*/
|
|
123
|
+
if (typeof Buffer !== "undefined" &&
|
|
124
|
+
Buffer.isBuffer(input)) {
|
|
125
|
+
return new Uint8Array(input);
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Handle ArrayBuffer
|
|
129
|
+
*/
|
|
130
|
+
if (input instanceof ArrayBuffer) {
|
|
131
|
+
return new Uint8Array(input);
|
|
132
|
+
}
|
|
133
|
+
throw new Error("VID parse failed: unsupported input type");
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
exports.VIDParser = VIDParser;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export declare class VIDValue {
|
|
2
|
+
private readonly binary;
|
|
3
|
+
constructor(binary: Uint8Array);
|
|
4
|
+
/**
|
|
5
|
+
* Returns Base32 string representation.
|
|
6
|
+
*
|
|
7
|
+
* Recommended for:
|
|
8
|
+
* - APIs
|
|
9
|
+
* - logging
|
|
10
|
+
* - debugging
|
|
11
|
+
* - external communication
|
|
12
|
+
*/
|
|
13
|
+
toString(): string;
|
|
14
|
+
/**
|
|
15
|
+
* Returns binary representation.
|
|
16
|
+
*
|
|
17
|
+
* Recommended for:
|
|
18
|
+
* - MongoDB Binary
|
|
19
|
+
* - PostgreSQL BYTEA
|
|
20
|
+
* - Redis
|
|
21
|
+
*
|
|
22
|
+
* Saves database space and improves index performance.
|
|
23
|
+
*/
|
|
24
|
+
toBinary(): Uint8Array;
|
|
25
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.VIDValue = void 0;
|
|
4
|
+
const Base32Encoder_1 = require("../encoding/Base32Encoder");
|
|
5
|
+
class VIDValue {
|
|
6
|
+
constructor(binary) {
|
|
7
|
+
if (!binary || binary.length !== 18) {
|
|
8
|
+
throw new Error("Invalid VID binary");
|
|
9
|
+
}
|
|
10
|
+
this.binary = binary;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Returns Base32 string representation.
|
|
14
|
+
*
|
|
15
|
+
* Recommended for:
|
|
16
|
+
* - APIs
|
|
17
|
+
* - logging
|
|
18
|
+
* - debugging
|
|
19
|
+
* - external communication
|
|
20
|
+
*/
|
|
21
|
+
toString() {
|
|
22
|
+
return Base32Encoder_1.Base32Encoder.encode(this.binary);
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Returns binary representation.
|
|
26
|
+
*
|
|
27
|
+
* Recommended for:
|
|
28
|
+
* - MongoDB Binary
|
|
29
|
+
* - PostgreSQL BYTEA
|
|
30
|
+
* - Redis
|
|
31
|
+
*
|
|
32
|
+
* Saves database space and improves index performance.
|
|
33
|
+
*/
|
|
34
|
+
toBinary() {
|
|
35
|
+
return this.binary;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
exports.VIDValue = VIDValue;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* VIDVerifier
|
|
3
|
+
*
|
|
4
|
+
* Central verification engine for VID identifiers.
|
|
5
|
+
*
|
|
6
|
+
* Responsibilities:
|
|
7
|
+
*
|
|
8
|
+
* - Accept user input in multiple formats
|
|
9
|
+
* - Normalize input into canonical binary form
|
|
10
|
+
* - Validate VID structure
|
|
11
|
+
* - Verify cryptographic authenticity
|
|
12
|
+
*
|
|
13
|
+
* Supported input formats:
|
|
14
|
+
*
|
|
15
|
+
* - Base32 string
|
|
16
|
+
* - Uint8Array
|
|
17
|
+
* - Node.js Buffer
|
|
18
|
+
* - ArrayBuffer
|
|
19
|
+
*
|
|
20
|
+
* VID binary layout (18 bytes):
|
|
21
|
+
*
|
|
22
|
+
* Bytes 0–10 → payload
|
|
23
|
+
* Bytes 11–17 → signature
|
|
24
|
+
*/
|
|
25
|
+
export declare class VIDVerifier {
|
|
26
|
+
/**
|
|
27
|
+
* Public verification method.
|
|
28
|
+
*
|
|
29
|
+
* Accepts flexible input formats and verifies VID authenticity.
|
|
30
|
+
*
|
|
31
|
+
* @param input VID in string or binary format
|
|
32
|
+
* @param secret secret key used for signing
|
|
33
|
+
*
|
|
34
|
+
* @returns true if valid
|
|
35
|
+
* @returns false if invalid or tampered
|
|
36
|
+
*/
|
|
37
|
+
static verify(input: string | Uint8Array | Buffer | ArrayBuffer, secret: Uint8Array): boolean;
|
|
38
|
+
/**
|
|
39
|
+
* Converts user input into Uint8Array.
|
|
40
|
+
*
|
|
41
|
+
* Handles all supported transport formats.
|
|
42
|
+
*/
|
|
43
|
+
private static normalizeToBinary;
|
|
44
|
+
}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.VIDVerifier = void 0;
|
|
4
|
+
const HMACSigner_1 = require("../crypto/HMACSigner");
|
|
5
|
+
const Base32Decoder_1 = require("../encoding/Base32Decoder");
|
|
6
|
+
/**
|
|
7
|
+
* VIDVerifier
|
|
8
|
+
*
|
|
9
|
+
* Central verification engine for VID identifiers.
|
|
10
|
+
*
|
|
11
|
+
* Responsibilities:
|
|
12
|
+
*
|
|
13
|
+
* - Accept user input in multiple formats
|
|
14
|
+
* - Normalize input into canonical binary form
|
|
15
|
+
* - Validate VID structure
|
|
16
|
+
* - Verify cryptographic authenticity
|
|
17
|
+
*
|
|
18
|
+
* Supported input formats:
|
|
19
|
+
*
|
|
20
|
+
* - Base32 string
|
|
21
|
+
* - Uint8Array
|
|
22
|
+
* - Node.js Buffer
|
|
23
|
+
* - ArrayBuffer
|
|
24
|
+
*
|
|
25
|
+
* VID binary layout (18 bytes):
|
|
26
|
+
*
|
|
27
|
+
* Bytes 0–10 → payload
|
|
28
|
+
* Bytes 11–17 → signature
|
|
29
|
+
*/
|
|
30
|
+
class VIDVerifier {
|
|
31
|
+
/**
|
|
32
|
+
* Public verification method.
|
|
33
|
+
*
|
|
34
|
+
* Accepts flexible input formats and verifies VID authenticity.
|
|
35
|
+
*
|
|
36
|
+
* @param input VID in string or binary format
|
|
37
|
+
* @param secret secret key used for signing
|
|
38
|
+
*
|
|
39
|
+
* @returns true if valid
|
|
40
|
+
* @returns false if invalid or tampered
|
|
41
|
+
*/
|
|
42
|
+
static verify(input, secret) {
|
|
43
|
+
if (!secret || !(secret instanceof Uint8Array)) {
|
|
44
|
+
throw new Error("VID verification failed: valid secret is required");
|
|
45
|
+
}
|
|
46
|
+
const binary = this.normalizeToBinary(input);
|
|
47
|
+
if (binary.length !== 18) {
|
|
48
|
+
return false;
|
|
49
|
+
}
|
|
50
|
+
const payload = binary.slice(0, 11);
|
|
51
|
+
const signature = binary.slice(11, 18);
|
|
52
|
+
return HMACSigner_1.HMACSigner.verify(payload, signature, secret);
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Converts user input into Uint8Array.
|
|
56
|
+
*
|
|
57
|
+
* Handles all supported transport formats.
|
|
58
|
+
*/
|
|
59
|
+
static normalizeToBinary(input) {
|
|
60
|
+
if (input === null || input === undefined) {
|
|
61
|
+
throw new Error("VID verification failed: input is required");
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* String input (Base32)
|
|
65
|
+
*/
|
|
66
|
+
if (typeof input === "string") {
|
|
67
|
+
const trimmed = input.trim();
|
|
68
|
+
if (trimmed.length === 0) {
|
|
69
|
+
throw new Error("VID verification failed: empty string");
|
|
70
|
+
}
|
|
71
|
+
try {
|
|
72
|
+
return Base32Decoder_1.Base32Decoder.decode(trimmed);
|
|
73
|
+
}
|
|
74
|
+
catch {
|
|
75
|
+
throw new Error("VID verification failed: invalid string encoding");
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Uint8Array input
|
|
80
|
+
*/
|
|
81
|
+
if (input instanceof Uint8Array) {
|
|
82
|
+
return input;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Buffer input
|
|
86
|
+
*/
|
|
87
|
+
if (typeof Buffer !== "undefined" && Buffer.isBuffer(input)) {
|
|
88
|
+
return new Uint8Array(input);
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* ArrayBuffer input
|
|
92
|
+
*/
|
|
93
|
+
if (input instanceof ArrayBuffer) {
|
|
94
|
+
return new Uint8Array(input);
|
|
95
|
+
}
|
|
96
|
+
throw new Error("VID verification failed: unsupported input type");
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
exports.VIDVerifier = VIDVerifier;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.HMACSigner = void 0;
|
|
7
|
+
const crypto_1 = __importDefault(require("crypto"));
|
|
8
|
+
class HMACSigner {
|
|
9
|
+
static sign(payload, secret) {
|
|
10
|
+
const hmac = crypto_1.default.createHmac("sha256", secret);
|
|
11
|
+
hmac.update(payload);
|
|
12
|
+
const fullSignature = hmac.digest();
|
|
13
|
+
return new Uint8Array(fullSignature.slice(0, this.SIGNATURE_LENGTH));
|
|
14
|
+
}
|
|
15
|
+
static verify(payload, signature, secret) {
|
|
16
|
+
if (!payload ||
|
|
17
|
+
!signature ||
|
|
18
|
+
!secret) {
|
|
19
|
+
return false;
|
|
20
|
+
}
|
|
21
|
+
if (signature.length !== this.SIGNATURE_LENGTH) {
|
|
22
|
+
return false;
|
|
23
|
+
}
|
|
24
|
+
const expected = this.sign(payload, secret);
|
|
25
|
+
const expectedBuffer = Buffer.from(expected);
|
|
26
|
+
const signatureBuffer = Buffer.from(signature);
|
|
27
|
+
if (expectedBuffer.length !== signatureBuffer.length) {
|
|
28
|
+
return false;
|
|
29
|
+
}
|
|
30
|
+
return crypto_1.default.timingSafeEqual(expectedBuffer, signatureBuffer);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
exports.HMACSigner = HMACSigner;
|
|
34
|
+
HMACSigner.SIGNATURE_LENGTH = 7;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Base32Decoder = void 0;
|
|
4
|
+
const alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
|
|
5
|
+
class Base32Decoder {
|
|
6
|
+
static decode(str) {
|
|
7
|
+
let bits = 0;
|
|
8
|
+
let value = 0;
|
|
9
|
+
const out = [];
|
|
10
|
+
for (const c of str) {
|
|
11
|
+
const idx = alphabet.indexOf(c);
|
|
12
|
+
if (idx === -1)
|
|
13
|
+
throw new Error("Invalid Base32");
|
|
14
|
+
value = (value << 5) | idx;
|
|
15
|
+
bits += 5;
|
|
16
|
+
if (bits >= 8) {
|
|
17
|
+
out.push((value >>> (bits - 8)) & 255);
|
|
18
|
+
bits -= 8;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
return new Uint8Array(out);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
exports.Base32Decoder = Base32Decoder;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Base32Encoder = void 0;
|
|
4
|
+
const alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
|
|
5
|
+
class Base32Encoder {
|
|
6
|
+
static encode(data) {
|
|
7
|
+
let bits = 0;
|
|
8
|
+
let value = 0;
|
|
9
|
+
let output = "";
|
|
10
|
+
for (const byte of data) {
|
|
11
|
+
value = (value << 8) | byte;
|
|
12
|
+
bits += 8;
|
|
13
|
+
while (bits >= 5) {
|
|
14
|
+
output += alphabet[(value >>> (bits - 5)) & 31];
|
|
15
|
+
bits -= 5;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
if (bits > 0) {
|
|
19
|
+
output += alphabet[(value << (5 - bits)) & 31];
|
|
20
|
+
}
|
|
21
|
+
return output;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
exports.Base32Encoder = Base32Encoder;
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { VID } from "./core/VID";
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ByteUtils = void 0;
|
|
4
|
+
class ByteUtils {
|
|
5
|
+
static concat(a, b) {
|
|
6
|
+
const out = new Uint8Array(a.length + b.length);
|
|
7
|
+
out.set(a, 0);
|
|
8
|
+
out.set(b, a.length);
|
|
9
|
+
return out;
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
exports.ByteUtils = ByteUtils;
|
package/package.json
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@veridjs/core",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Verifiable Identifier for distributed systems",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": "./dist/index.js",
|
|
9
|
+
"./postgres": "./dist/adapters/VIDPostgresAdapter.js",
|
|
10
|
+
"./mongo": "./dist/adapters/VIDMongoAdapter.js"
|
|
11
|
+
},
|
|
12
|
+
"files": [
|
|
13
|
+
"dist",
|
|
14
|
+
"README.md",
|
|
15
|
+
"LICENSE"
|
|
16
|
+
],
|
|
17
|
+
"scripts": {
|
|
18
|
+
"build": "tsc"
|
|
19
|
+
},
|
|
20
|
+
"keywords": [
|
|
21
|
+
"identifier",
|
|
22
|
+
"uuid",
|
|
23
|
+
"vid",
|
|
24
|
+
"distributed"
|
|
25
|
+
],
|
|
26
|
+
"author": "Kishan Kumar",
|
|
27
|
+
"license": "MIT",
|
|
28
|
+
"devDependencies": {
|
|
29
|
+
"@types/node": "^25.2.3",
|
|
30
|
+
"@types/pg": "^8.16.0",
|
|
31
|
+
"ts-node": "^10.9.2",
|
|
32
|
+
"typescript": "^5.9.3"
|
|
33
|
+
},
|
|
34
|
+
"dependencies": {}
|
|
35
|
+
}
|