@vex-chat/spire 4.1.1 → 5.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/README.md +8 -11
- package/dist/Database.js +2 -6
- package/dist/Database.js.map +1 -1
- package/dist/Spire.d.ts +2 -10
- package/dist/Spire.js +24 -60
- package/dist/Spire.js.map +1 -1
- package/dist/run.js +7 -12
- package/dist/run.js.map +1 -1
- package/dist/spireListenPort.d.ts +1 -2
- package/dist/spireListenPort.js +1 -2
- package/dist/spireListenPort.js.map +1 -1
- package/dist/utils/loadEnv.d.ts +0 -1
- package/dist/utils/loadEnv.js +4 -14
- package/dist/utils/loadEnv.js.map +1 -1
- package/dist/utils/spireXSignOpenAsync.d.ts +1 -3
- package/dist/utils/spireXSignOpenAsync.js +4 -9
- package/dist/utils/spireXSignOpenAsync.js.map +1 -1
- package/package.json +3 -4
- package/src/Database.ts +2 -11
- package/src/Spire.ts +31 -80
- package/src/__tests__/loadEnv.spec.ts +2 -26
- package/src/__tests__/preKeySignature.spec.ts +1 -6
- package/src/__tests__/spireListenPort.spec.ts +1 -1
- package/src/run.ts +6 -13
- package/src/spireListenPort.ts +1 -2
- package/src/utils/loadEnv.ts +4 -16
- package/src/utils/spireXSignOpenAsync.ts +4 -9
|
@@ -7,13 +7,11 @@
|
|
|
7
7
|
import { describe, expect, it } from "vitest";
|
|
8
8
|
|
|
9
9
|
import {
|
|
10
|
-
isSpireFipsEnabled,
|
|
11
10
|
normalizeEnvValue,
|
|
12
11
|
validateSpireRuntimeEnv,
|
|
13
12
|
} from "../utils/loadEnv.ts";
|
|
14
13
|
|
|
15
14
|
const TWEETNACL_SPK = "ab".repeat(64);
|
|
16
|
-
const FIPS_SPK = "cd".repeat(90);
|
|
17
15
|
const JWT_SECRET = "ef".repeat(32);
|
|
18
16
|
|
|
19
17
|
describe("normalizeEnvValue", () => {
|
|
@@ -30,22 +28,11 @@ describe("normalizeEnvValue", () => {
|
|
|
30
28
|
});
|
|
31
29
|
});
|
|
32
30
|
|
|
33
|
-
describe("isSpireFipsEnabled", () => {
|
|
34
|
-
it("accepts true and 1 only", () => {
|
|
35
|
-
expect(isSpireFipsEnabled("true")).toBe(true);
|
|
36
|
-
expect(isSpireFipsEnabled('"true"')).toBe(true);
|
|
37
|
-
expect(isSpireFipsEnabled("1")).toBe(true);
|
|
38
|
-
expect(isSpireFipsEnabled("false")).toBe(false);
|
|
39
|
-
expect(isSpireFipsEnabled(undefined)).toBe(false);
|
|
40
|
-
});
|
|
41
|
-
});
|
|
42
|
-
|
|
43
31
|
describe("validateSpireRuntimeEnv", () => {
|
|
44
32
|
it("accepts quoted compose-safe tweetnacl keys", () => {
|
|
45
33
|
expect(() => {
|
|
46
34
|
validateSpireRuntimeEnv({
|
|
47
35
|
JWT_SECRET: `"${JWT_SECRET}"`,
|
|
48
|
-
SPIRE_FIPS: "false",
|
|
49
36
|
SPK: `"${TWEETNACL_SPK}"`,
|
|
50
37
|
});
|
|
51
38
|
}).not.toThrow();
|
|
@@ -60,22 +47,11 @@ describe("validateSpireRuntimeEnv", () => {
|
|
|
60
47
|
}).toThrow(/SPK must be an even-length hex string/);
|
|
61
48
|
});
|
|
62
49
|
|
|
63
|
-
it("rejects
|
|
64
|
-
expect(() => {
|
|
65
|
-
validateSpireRuntimeEnv({
|
|
66
|
-
JWT_SECRET,
|
|
67
|
-
SPIRE_FIPS: "true",
|
|
68
|
-
SPK: TWEETNACL_SPK,
|
|
69
|
-
});
|
|
70
|
-
}).toThrow(/requires an SPK from .*gen-spk-fips/);
|
|
71
|
-
});
|
|
72
|
-
|
|
73
|
-
it("rejects a FIPS-sized SPK without the FIPS flag", () => {
|
|
50
|
+
it("rejects an SPK with the wrong length", () => {
|
|
74
51
|
expect(() => {
|
|
75
52
|
validateSpireRuntimeEnv({
|
|
76
53
|
JWT_SECRET,
|
|
77
|
-
|
|
78
|
-
SPK: FIPS_SPK,
|
|
54
|
+
SPK: "cd".repeat(90),
|
|
79
55
|
});
|
|
80
56
|
}).toThrow(/SPK must be 128 hex characters/);
|
|
81
57
|
});
|
|
@@ -5,7 +5,6 @@
|
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
import {
|
|
8
|
-
setCryptoProfile,
|
|
9
8
|
xBoxKeyPairAsync,
|
|
10
9
|
xPreKeySignaturePayload,
|
|
11
10
|
xSignAsync,
|
|
@@ -13,7 +12,7 @@ import {
|
|
|
13
12
|
XUtils,
|
|
14
13
|
} from "@vex-chat/crypto";
|
|
15
14
|
|
|
16
|
-
import {
|
|
15
|
+
import { describe, expect, it } from "vitest";
|
|
17
16
|
|
|
18
17
|
import {
|
|
19
18
|
verifyDevicePayloadPreKeySignature,
|
|
@@ -31,10 +30,6 @@ async function makeSignedPreKey() {
|
|
|
31
30
|
}
|
|
32
31
|
|
|
33
32
|
describe("prekey signature validation", () => {
|
|
34
|
-
beforeEach(() => {
|
|
35
|
-
setCryptoProfile("tweetnacl");
|
|
36
|
-
});
|
|
37
|
-
|
|
38
33
|
it("accepts a prekey signed by the device signing key", async () => {
|
|
39
34
|
const { preKey, signature, signKeys } = await makeSignedPreKey();
|
|
40
35
|
|
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
} from "../spireListenPort.ts";
|
|
13
13
|
|
|
14
14
|
describe("resolveSpireListenPort", () => {
|
|
15
|
-
it("uses default
|
|
15
|
+
it("uses the default when no explicit port is provided", () => {
|
|
16
16
|
expect(resolveSpireListenPort(undefined)).toBe(DEFAULT_SPIRE_API_PORT);
|
|
17
17
|
});
|
|
18
18
|
|
package/src/run.ts
CHANGED
|
@@ -9,7 +9,7 @@ import type { SpireOptions } from "./Spire.ts";
|
|
|
9
9
|
import { Spire } from "./Spire.ts";
|
|
10
10
|
import { loadEnv } from "./utils/loadEnv.ts";
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
function main() {
|
|
13
13
|
// load the environment variables — loadEnv() exits if required vars are missing
|
|
14
14
|
loadEnv();
|
|
15
15
|
|
|
@@ -29,21 +29,12 @@ async function main() {
|
|
|
29
29
|
}
|
|
30
30
|
}
|
|
31
31
|
const dbType = parseDbType(process.env["DB_TYPE"]);
|
|
32
|
-
const fips =
|
|
33
|
-
process.env["SPIRE_FIPS"] === "1" ||
|
|
34
|
-
process.env["SPIRE_FIPS"] === "true";
|
|
35
|
-
|
|
36
32
|
const options: SpireOptions = {
|
|
37
33
|
...(apiPort !== undefined ? { apiPort } : {}),
|
|
38
34
|
...(dbType !== undefined ? { dbType } : {}),
|
|
39
|
-
...(fips ? { cryptoProfile: "fips" } : {}),
|
|
40
35
|
};
|
|
41
36
|
|
|
42
|
-
|
|
43
|
-
await Spire.createAsync(spk, options);
|
|
44
|
-
} else {
|
|
45
|
-
new Spire(spk, options);
|
|
46
|
-
}
|
|
37
|
+
new Spire(spk, options);
|
|
47
38
|
}
|
|
48
39
|
|
|
49
40
|
function parseDbType(value: string | undefined): SpireOptions["dbType"] {
|
|
@@ -58,7 +49,9 @@ function parseDbType(value: string | undefined): SpireOptions["dbType"] {
|
|
|
58
49
|
}
|
|
59
50
|
}
|
|
60
51
|
|
|
61
|
-
|
|
52
|
+
try {
|
|
53
|
+
main();
|
|
54
|
+
} catch (err: unknown) {
|
|
62
55
|
console.error(err);
|
|
63
56
|
process.exit(1);
|
|
64
|
-
}
|
|
57
|
+
}
|
package/src/spireListenPort.ts
CHANGED
|
@@ -5,8 +5,7 @@
|
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
|
-
* Default HTTP/WS port for the Spire API
|
|
9
|
-
* `API_PORT` / `apiPort` is set. Clients can tell profiles apart via `GET /status`.
|
|
8
|
+
* Default HTTP/WS port for the Spire API unless `API_PORT` / `apiPort` is set.
|
|
10
9
|
*/
|
|
11
10
|
export const DEFAULT_SPIRE_API_PORT = 16777;
|
|
12
11
|
|
package/src/utils/loadEnv.ts
CHANGED
|
@@ -11,7 +11,6 @@ const NORMALIZED_ENV_VARS = [
|
|
|
11
11
|
...REQUIRED_ENV_VARS,
|
|
12
12
|
"API_PORT",
|
|
13
13
|
"SPIRE_TRUST_PROXY_HOPS",
|
|
14
|
-
"SPIRE_FIPS",
|
|
15
14
|
"SPIRE_PASSKEY_RP_ID",
|
|
16
15
|
"SPIRE_PASSKEY_RP_NAME",
|
|
17
16
|
"SPIRE_PASSKEY_ORIGINS",
|
|
@@ -22,11 +21,6 @@ const NORMALIZED_ENV_VARS = [
|
|
|
22
21
|
const HEX_BYTES_RE = /^(?:[0-9a-fA-F]{2})+$/;
|
|
23
22
|
const TWEETNACL_SPK_HEX_LENGTH = 128;
|
|
24
23
|
|
|
25
|
-
export function isSpireFipsEnabled(value: string | undefined): boolean {
|
|
26
|
-
const normalized = value == null ? "" : normalizeEnvValue(value);
|
|
27
|
-
return normalized === "1" || normalized === "true";
|
|
28
|
-
}
|
|
29
|
-
|
|
30
24
|
/* Populate process.env with vars from .env and verify required vars are present. */
|
|
31
25
|
export function loadEnv(): void {
|
|
32
26
|
config();
|
|
@@ -62,25 +56,19 @@ export function validateSpireRuntimeEnv(
|
|
|
62
56
|
const jwtSecret = normalizeEnvValue(env["JWT_SECRET"] ?? "");
|
|
63
57
|
if (!HEX_BYTES_RE.test(spk)) {
|
|
64
58
|
throw new Error(
|
|
65
|
-
"SPK must be an even-length hex string. Generate one with `pnpm --filter @vex-chat/spire gen-spk
|
|
59
|
+
"SPK must be an even-length hex string. Generate one with `pnpm --filter @vex-chat/spire gen-spk`.",
|
|
66
60
|
);
|
|
67
61
|
}
|
|
68
62
|
|
|
69
|
-
if (
|
|
70
|
-
if (spk.length === TWEETNACL_SPK_HEX_LENGTH) {
|
|
71
|
-
throw new Error(
|
|
72
|
-
"SPIRE_FIPS=true requires an SPK from `pnpm --filter @vex-chat/spire gen-spk-fips`; the configured SPK looks like a tweetnacl key.",
|
|
73
|
-
);
|
|
74
|
-
}
|
|
75
|
-
} else if (spk.length !== TWEETNACL_SPK_HEX_LENGTH) {
|
|
63
|
+
if (spk.length !== TWEETNACL_SPK_HEX_LENGTH) {
|
|
76
64
|
throw new Error(
|
|
77
|
-
"
|
|
65
|
+
"SPK must be 128 hex characters from `pnpm --filter @vex-chat/spire gen-spk`.",
|
|
78
66
|
);
|
|
79
67
|
}
|
|
80
68
|
|
|
81
69
|
if (jwtSecret.length === 0) {
|
|
82
70
|
throw new Error(
|
|
83
|
-
"JWT_SECRET must be set. Generate one with `pnpm --filter @vex-chat/spire gen-spk
|
|
71
|
+
"JWT_SECRET must be set. Generate one with `pnpm --filter @vex-chat/spire gen-spk`.",
|
|
84
72
|
);
|
|
85
73
|
}
|
|
86
74
|
if (jwtSecret.length < 32) {
|
|
@@ -4,17 +4,12 @@
|
|
|
4
4
|
* Commercial licenses available at vex.wtf
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
import {
|
|
7
|
+
import { xSignOpen } from "@vex-chat/crypto";
|
|
8
8
|
|
|
9
|
-
/**
|
|
10
|
-
|
|
11
|
-
*/
|
|
12
|
-
export async function spireXSignOpenAsync(
|
|
9
|
+
/** Ed25519 detached-verify open. */
|
|
10
|
+
export function spireXSignOpenAsync(
|
|
13
11
|
signedMessage: Uint8Array,
|
|
14
12
|
publicKey: Uint8Array,
|
|
15
13
|
): Promise<null | Uint8Array> {
|
|
16
|
-
|
|
17
|
-
return xSignOpenAsync(signedMessage, publicKey);
|
|
18
|
-
}
|
|
19
|
-
return xSignOpen(signedMessage, publicKey);
|
|
14
|
+
return Promise.resolve(xSignOpen(signedMessage, publicKey));
|
|
20
15
|
}
|