aquaman-core 0.1.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 +21 -0
- package/README.md +51 -0
- package/dist/audit/index.d.ts +5 -0
- package/dist/audit/index.d.ts.map +1 -0
- package/dist/audit/index.js +5 -0
- package/dist/audit/index.js.map +1 -0
- package/dist/audit/logger.d.ts +48 -0
- package/dist/audit/logger.d.ts.map +1 -0
- package/dist/audit/logger.js +237 -0
- package/dist/audit/logger.js.map +1 -0
- package/dist/credentials/backends/onepassword.d.ts +38 -0
- package/dist/credentials/backends/onepassword.d.ts.map +1 -0
- package/dist/credentials/backends/onepassword.js +218 -0
- package/dist/credentials/backends/onepassword.js.map +1 -0
- package/dist/credentials/backends/vault.d.ts +56 -0
- package/dist/credentials/backends/vault.d.ts.map +1 -0
- package/dist/credentials/backends/vault.js +206 -0
- package/dist/credentials/backends/vault.js.map +1 -0
- package/dist/credentials/index.d.ts +7 -0
- package/dist/credentials/index.d.ts.map +1 -0
- package/dist/credentials/index.js +7 -0
- package/dist/credentials/index.js.map +1 -0
- package/dist/credentials/store.d.ts +88 -0
- package/dist/credentials/store.d.ts.map +1 -0
- package/dist/credentials/store.js +221 -0
- package/dist/credentials/store.js.map +1 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +18 -0
- package/dist/index.js.map +1 -0
- package/dist/types.d.ts +85 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +11 -0
- package/dist/types.js.map +1 -0
- package/dist/utils/config.d.ts +18 -0
- package/dist/utils/config.d.ts.map +1 -0
- package/dist/utils/config.js +115 -0
- package/dist/utils/config.js.map +1 -0
- package/dist/utils/hash.d.ts +27 -0
- package/dist/utils/hash.d.ts.map +1 -0
- package/dist/utils/hash.js +348 -0
- package/dist/utils/hash.js.map +1 -0
- package/dist/utils/index.d.ts +6 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +6 -0
- package/dist/utils/index.js.map +1 -0
- package/package.json +63 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* aquaman-core - Core credential storage, audit logging, and utilities
|
|
3
|
+
*
|
|
4
|
+
* This package provides the shared functionality for:
|
|
5
|
+
* - Credential storage backends (Keychain, 1Password, Vault, encrypted file)
|
|
6
|
+
* - Hash-chained tamper-evident audit logs
|
|
7
|
+
* - Cryptographic utilities
|
|
8
|
+
* - Configuration management
|
|
9
|
+
*/
|
|
10
|
+
export * from './types.js';
|
|
11
|
+
export { type Credential, type CredentialStore, type CredentialStoreOptions, KeychainStore, EncryptedFileStore, MemoryStore, createCredentialStore, type OnePasswordStoreOptions, OnePasswordStore, createOnePasswordStore, type VaultStoreOptions, VaultStore, createVaultStore } from './credentials/index.js';
|
|
12
|
+
export { type AuditLoggerOptions, AuditLogger, createAuditLogger } from './audit/index.js';
|
|
13
|
+
export { computeHash, computeChainedHash, generateId, generateNonce, generateSigningKeyPair, sign, verify, encryptWithPassword, decryptWithPassword, generateSelfSignedCert, type SigningKeyPair, type SelfSignedCert, getConfigDir, getConfigPath, expandPath, getDefaultConfig, loadConfig, ensureConfigDir, saveConfig } from './utils/index.js';
|
|
14
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAGH,cAAc,YAAY,CAAC;AAG3B,OAAO,EACL,KAAK,UAAU,EACf,KAAK,eAAe,EACpB,KAAK,sBAAsB,EAC3B,aAAa,EACb,kBAAkB,EAClB,WAAW,EACX,qBAAqB,EACrB,KAAK,uBAAuB,EAC5B,gBAAgB,EAChB,sBAAsB,EACtB,KAAK,iBAAiB,EACtB,UAAU,EACV,gBAAgB,EACjB,MAAM,wBAAwB,CAAC;AAGhC,OAAO,EACL,KAAK,kBAAkB,EACvB,WAAW,EACX,iBAAiB,EAClB,MAAM,kBAAkB,CAAC;AAG1B,OAAO,EACL,WAAW,EACX,kBAAkB,EAClB,UAAU,EACV,aAAa,EACb,sBAAsB,EACtB,IAAI,EACJ,MAAM,EACN,mBAAmB,EACnB,mBAAmB,EACnB,sBAAsB,EACtB,KAAK,cAAc,EACnB,KAAK,cAAc,EACnB,YAAY,EACZ,aAAa,EACb,UAAU,EACV,gBAAgB,EAChB,UAAU,EACV,eAAe,EACf,UAAU,EACX,MAAM,kBAAkB,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* aquaman-core - Core credential storage, audit logging, and utilities
|
|
3
|
+
*
|
|
4
|
+
* This package provides the shared functionality for:
|
|
5
|
+
* - Credential storage backends (Keychain, 1Password, Vault, encrypted file)
|
|
6
|
+
* - Hash-chained tamper-evident audit logs
|
|
7
|
+
* - Cryptographic utilities
|
|
8
|
+
* - Configuration management
|
|
9
|
+
*/
|
|
10
|
+
// Types
|
|
11
|
+
export * from './types.js';
|
|
12
|
+
// Credentials
|
|
13
|
+
export { KeychainStore, EncryptedFileStore, MemoryStore, createCredentialStore, OnePasswordStore, createOnePasswordStore, VaultStore, createVaultStore } from './credentials/index.js';
|
|
14
|
+
// Audit
|
|
15
|
+
export { AuditLogger, createAuditLogger } from './audit/index.js';
|
|
16
|
+
// Utils
|
|
17
|
+
export { computeHash, computeChainedHash, generateId, generateNonce, generateSigningKeyPair, sign, verify, encryptWithPassword, decryptWithPassword, generateSelfSignedCert, getConfigDir, getConfigPath, expandPath, getDefaultConfig, loadConfig, ensureConfigDir, saveConfig } from './utils/index.js';
|
|
18
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,QAAQ;AACR,cAAc,YAAY,CAAC;AAE3B,cAAc;AACd,OAAO,EAIL,aAAa,EACb,kBAAkB,EAClB,WAAW,EACX,qBAAqB,EAErB,gBAAgB,EAChB,sBAAsB,EAEtB,UAAU,EACV,gBAAgB,EACjB,MAAM,wBAAwB,CAAC;AAEhC,QAAQ;AACR,OAAO,EAEL,WAAW,EACX,iBAAiB,EAClB,MAAM,kBAAkB,CAAC;AAE1B,QAAQ;AACR,OAAO,EACL,WAAW,EACX,kBAAkB,EAClB,UAAU,EACV,aAAa,EACb,sBAAsB,EACtB,IAAI,EACJ,MAAM,EACN,mBAAmB,EACnB,mBAAmB,EACnB,sBAAsB,EAGtB,YAAY,EACZ,aAAa,EACb,UAAU,EACV,gBAAgB,EAChB,UAAU,EACV,eAAe,EACf,UAAU,EACX,MAAM,kBAAkB,CAAC"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Core types for aquaman credential isolation layer
|
|
3
|
+
*
|
|
4
|
+
* This module focuses on unique features NOT in OpenClaw:
|
|
5
|
+
* - Credential proxy with TLS
|
|
6
|
+
* - Enterprise backends (1Password, Vault)
|
|
7
|
+
* - Hash-chained tamper-evident audit logs
|
|
8
|
+
* - Dynamic service registry
|
|
9
|
+
*/
|
|
10
|
+
export interface ToolCall {
|
|
11
|
+
id: string;
|
|
12
|
+
sessionId: string;
|
|
13
|
+
agentId: string;
|
|
14
|
+
tool: string;
|
|
15
|
+
params: Record<string, unknown>;
|
|
16
|
+
timestamp: Date;
|
|
17
|
+
}
|
|
18
|
+
export interface ToolResult {
|
|
19
|
+
id: string;
|
|
20
|
+
toolCallId: string;
|
|
21
|
+
result: unknown;
|
|
22
|
+
error?: string;
|
|
23
|
+
timestamp: Date;
|
|
24
|
+
}
|
|
25
|
+
export interface AuditEntry {
|
|
26
|
+
id: string;
|
|
27
|
+
timestamp: Date;
|
|
28
|
+
type: 'tool_call' | 'tool_result' | 'credential_access';
|
|
29
|
+
sessionId: string;
|
|
30
|
+
agentId: string;
|
|
31
|
+
data: ToolCall | ToolResult | CredentialAccess;
|
|
32
|
+
previousHash: string;
|
|
33
|
+
hash: string;
|
|
34
|
+
}
|
|
35
|
+
export interface CredentialAccess {
|
|
36
|
+
service: string;
|
|
37
|
+
operation: 'read' | 'use' | 'rotate';
|
|
38
|
+
success: boolean;
|
|
39
|
+
error?: string;
|
|
40
|
+
}
|
|
41
|
+
export interface ServiceConfig {
|
|
42
|
+
name: string;
|
|
43
|
+
upstream: string;
|
|
44
|
+
authHeader: string;
|
|
45
|
+
authPrefix?: string;
|
|
46
|
+
credentialKey: string;
|
|
47
|
+
description?: string;
|
|
48
|
+
}
|
|
49
|
+
export interface CredentialsConfig {
|
|
50
|
+
backend: 'keychain' | '1password' | 'vault' | 'encrypted-file';
|
|
51
|
+
proxyPort: number;
|
|
52
|
+
proxiedServices: string[];
|
|
53
|
+
tls?: {
|
|
54
|
+
enabled: boolean;
|
|
55
|
+
certPath?: string;
|
|
56
|
+
keyPath?: string;
|
|
57
|
+
autoGenerate?: boolean;
|
|
58
|
+
};
|
|
59
|
+
onePasswordVault?: string;
|
|
60
|
+
onePasswordAccount?: string;
|
|
61
|
+
vaultAddress?: string;
|
|
62
|
+
vaultToken?: string;
|
|
63
|
+
vaultNamespace?: string;
|
|
64
|
+
vaultMountPath?: string;
|
|
65
|
+
}
|
|
66
|
+
export interface AuditConfig {
|
|
67
|
+
enabled: boolean;
|
|
68
|
+
logDir: string;
|
|
69
|
+
}
|
|
70
|
+
export interface ServicesConfig {
|
|
71
|
+
configPath: string;
|
|
72
|
+
}
|
|
73
|
+
export interface OpenClawConfig {
|
|
74
|
+
autoLaunch: boolean;
|
|
75
|
+
configMethod: 'env' | 'dotenv' | 'shell-rc';
|
|
76
|
+
binaryPath?: string;
|
|
77
|
+
}
|
|
78
|
+
export interface WrapperConfig {
|
|
79
|
+
credentials: CredentialsConfig;
|
|
80
|
+
audit: AuditConfig;
|
|
81
|
+
services: ServicesConfig;
|
|
82
|
+
openclaw: OpenClawConfig;
|
|
83
|
+
}
|
|
84
|
+
export type CredentialBackend = 'keychain' | '1password' | 'vault' | 'encrypted-file';
|
|
85
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,MAAM,WAAW,QAAQ;IACvB,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAChC,SAAS,EAAE,IAAI,CAAC;CACjB;AAED,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,OAAO,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,IAAI,CAAC;CACjB;AAED,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,IAAI,CAAC;IAChB,IAAI,EAAE,WAAW,GAAG,aAAa,GAAG,mBAAmB,CAAC;IACxD,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,QAAQ,GAAG,UAAU,GAAG,gBAAgB,CAAC;IAC/C,YAAY,EAAE,MAAM,CAAC;IACrB,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,GAAG,KAAK,GAAG,QAAQ,CAAC;IACrC,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,UAAU,GAAG,WAAW,GAAG,OAAO,GAAG,gBAAgB,CAAC;IAC/D,SAAS,EAAE,MAAM,CAAC;IAClB,eAAe,EAAE,MAAM,EAAE,CAAC;IAC1B,GAAG,CAAC,EAAE;QACJ,OAAO,EAAE,OAAO,CAAC;QACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,YAAY,CAAC,EAAE,OAAO,CAAC;KACxB,CAAC;IAEF,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAE5B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,cAAc;IAC7B,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,cAAc;IAC7B,UAAU,EAAE,OAAO,CAAC;IACpB,YAAY,EAAE,KAAK,GAAG,QAAQ,GAAG,UAAU,CAAC;IAC5C,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,aAAa;IAC5B,WAAW,EAAE,iBAAiB,CAAC;IAC/B,KAAK,EAAE,WAAW,CAAC;IACnB,QAAQ,EAAE,cAAc,CAAC;IACzB,QAAQ,EAAE,cAAc,CAAC;CAC1B;AAED,MAAM,MAAM,iBAAiB,GAAG,UAAU,GAAG,WAAW,GAAG,OAAO,GAAG,gBAAgB,CAAC"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Core types for aquaman credential isolation layer
|
|
3
|
+
*
|
|
4
|
+
* This module focuses on unique features NOT in OpenClaw:
|
|
5
|
+
* - Credential proxy with TLS
|
|
6
|
+
* - Enterprise backends (1Password, Vault)
|
|
7
|
+
* - Hash-chained tamper-evident audit logs
|
|
8
|
+
* - Dynamic service registry
|
|
9
|
+
*/
|
|
10
|
+
export {};
|
|
11
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Configuration loader and validator for aquaman
|
|
3
|
+
*
|
|
4
|
+
* Focused on credential isolation features:
|
|
5
|
+
* - Credential proxy settings
|
|
6
|
+
* - Enterprise backend configuration
|
|
7
|
+
* - Audit logging configuration
|
|
8
|
+
* - OpenClaw integration settings
|
|
9
|
+
*/
|
|
10
|
+
import type { WrapperConfig } from '../types.js';
|
|
11
|
+
export declare function getConfigDir(): string;
|
|
12
|
+
export declare function getConfigPath(): string;
|
|
13
|
+
export declare function expandPath(p: string): string;
|
|
14
|
+
export declare function getDefaultConfig(): WrapperConfig;
|
|
15
|
+
export declare function loadConfig(): WrapperConfig;
|
|
16
|
+
export declare function ensureConfigDir(): void;
|
|
17
|
+
export declare function saveConfig(config: WrapperConfig): void;
|
|
18
|
+
//# sourceMappingURL=config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/utils/config.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAMH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAKjD,wBAAgB,YAAY,IAAI,MAAM,CAErC;AAED,wBAAgB,aAAa,IAAI,MAAM,CAEtC;AAED,wBAAgB,UAAU,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAQ5C;AAED,wBAAgB,gBAAgB,IAAI,aAAa,CA0BhD;AAED,wBAAgB,UAAU,IAAI,aAAa,CAgB1C;AAqCD,wBAAgB,eAAe,IAAI,IAAI,CAKtC;AAED,wBAAgB,UAAU,CAAC,MAAM,EAAE,aAAa,GAAG,IAAI,CAItD"}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Configuration loader and validator for aquaman
|
|
3
|
+
*
|
|
4
|
+
* Focused on credential isolation features:
|
|
5
|
+
* - Credential proxy settings
|
|
6
|
+
* - Enterprise backend configuration
|
|
7
|
+
* - Audit logging configuration
|
|
8
|
+
* - OpenClaw integration settings
|
|
9
|
+
*/
|
|
10
|
+
import * as fs from 'node:fs';
|
|
11
|
+
import * as path from 'node:path';
|
|
12
|
+
import * as os from 'node:os';
|
|
13
|
+
import { parse as parseYaml, stringify as stringifyYaml } from 'yaml';
|
|
14
|
+
const DEFAULT_CONFIG_DIR = path.join(os.homedir(), '.aquaman');
|
|
15
|
+
const CONFIG_FILE = 'config.yaml';
|
|
16
|
+
export function getConfigDir() {
|
|
17
|
+
return process.env['AQUAMAN_CONFIG_DIR'] || DEFAULT_CONFIG_DIR;
|
|
18
|
+
}
|
|
19
|
+
export function getConfigPath() {
|
|
20
|
+
return path.join(getConfigDir(), CONFIG_FILE);
|
|
21
|
+
}
|
|
22
|
+
export function expandPath(p) {
|
|
23
|
+
if (p.startsWith('~')) {
|
|
24
|
+
return path.join(os.homedir(), p.slice(1));
|
|
25
|
+
}
|
|
26
|
+
if (p.includes('${HOME}')) {
|
|
27
|
+
return p.replace('${HOME}', os.homedir());
|
|
28
|
+
}
|
|
29
|
+
return p;
|
|
30
|
+
}
|
|
31
|
+
export function getDefaultConfig() {
|
|
32
|
+
return {
|
|
33
|
+
credentials: {
|
|
34
|
+
backend: 'keychain',
|
|
35
|
+
proxyPort: 8081,
|
|
36
|
+
proxiedServices: ['anthropic', 'openai', 'slack', 'discord', 'github'],
|
|
37
|
+
tls: {
|
|
38
|
+
enabled: true,
|
|
39
|
+
autoGenerate: true,
|
|
40
|
+
certPath: path.join(getConfigDir(), 'certs', 'proxy.crt'),
|
|
41
|
+
keyPath: path.join(getConfigDir(), 'certs', 'proxy.key')
|
|
42
|
+
},
|
|
43
|
+
vaultMountPath: 'secret'
|
|
44
|
+
},
|
|
45
|
+
audit: {
|
|
46
|
+
enabled: true,
|
|
47
|
+
logDir: path.join(getConfigDir(), 'audit')
|
|
48
|
+
},
|
|
49
|
+
services: {
|
|
50
|
+
configPath: path.join(getConfigDir(), 'services.yaml')
|
|
51
|
+
},
|
|
52
|
+
openclaw: {
|
|
53
|
+
autoLaunch: true,
|
|
54
|
+
configMethod: 'env'
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
export function loadConfig() {
|
|
59
|
+
const configPath = getConfigPath();
|
|
60
|
+
const defaultConfig = getDefaultConfig();
|
|
61
|
+
if (!fs.existsSync(configPath)) {
|
|
62
|
+
return defaultConfig;
|
|
63
|
+
}
|
|
64
|
+
try {
|
|
65
|
+
const content = fs.readFileSync(configPath, 'utf-8');
|
|
66
|
+
const userConfig = parseYaml(content);
|
|
67
|
+
return mergeConfig(defaultConfig, userConfig);
|
|
68
|
+
}
|
|
69
|
+
catch (error) {
|
|
70
|
+
console.error(`Warning: Failed to load config from ${configPath}, using defaults`);
|
|
71
|
+
return defaultConfig;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
function mergeConfig(base, override) {
|
|
75
|
+
// Merge TLS config, ensuring enabled has a value
|
|
76
|
+
const baseTls = base.credentials.tls;
|
|
77
|
+
const overrideTls = override.credentials?.tls;
|
|
78
|
+
const mergedTls = baseTls || overrideTls ? {
|
|
79
|
+
enabled: overrideTls?.enabled ?? baseTls?.enabled ?? true,
|
|
80
|
+
certPath: overrideTls?.certPath ?? baseTls?.certPath,
|
|
81
|
+
keyPath: overrideTls?.keyPath ?? baseTls?.keyPath,
|
|
82
|
+
autoGenerate: overrideTls?.autoGenerate ?? baseTls?.autoGenerate
|
|
83
|
+
} : undefined;
|
|
84
|
+
return {
|
|
85
|
+
credentials: {
|
|
86
|
+
...base.credentials,
|
|
87
|
+
...override.credentials,
|
|
88
|
+
tls: mergedTls
|
|
89
|
+
},
|
|
90
|
+
audit: {
|
|
91
|
+
...base.audit,
|
|
92
|
+
...override.audit
|
|
93
|
+
},
|
|
94
|
+
services: {
|
|
95
|
+
...base.services,
|
|
96
|
+
...override.services
|
|
97
|
+
},
|
|
98
|
+
openclaw: {
|
|
99
|
+
...base.openclaw,
|
|
100
|
+
...override.openclaw
|
|
101
|
+
}
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
export function ensureConfigDir() {
|
|
105
|
+
const configDir = getConfigDir();
|
|
106
|
+
if (!fs.existsSync(configDir)) {
|
|
107
|
+
fs.mkdirSync(configDir, { recursive: true });
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
export function saveConfig(config) {
|
|
111
|
+
ensureConfigDir();
|
|
112
|
+
const configPath = getConfigPath();
|
|
113
|
+
fs.writeFileSync(configPath, stringifyYaml(config), 'utf-8');
|
|
114
|
+
}
|
|
115
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/utils/config.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cryptographic utilities for hash chains and integrity verification
|
|
3
|
+
*/
|
|
4
|
+
export declare function computeHash(data: string): string;
|
|
5
|
+
export declare function computeChainedHash(data: string, previousHash: string): string;
|
|
6
|
+
export declare function generateId(): string;
|
|
7
|
+
export declare function generateNonce(): string;
|
|
8
|
+
export interface SigningKeyPair {
|
|
9
|
+
publicKey: string;
|
|
10
|
+
privateKey: string;
|
|
11
|
+
}
|
|
12
|
+
export declare function generateSigningKeyPair(): SigningKeyPair;
|
|
13
|
+
export declare function sign(data: string, privateKey: string): string;
|
|
14
|
+
export declare function verify(data: string, signature: string, publicKey: string): boolean;
|
|
15
|
+
export declare function encryptWithPassword(data: string, password: string): string;
|
|
16
|
+
export declare function decryptWithPassword(encryptedData: string, password: string): string;
|
|
17
|
+
export interface SelfSignedCert {
|
|
18
|
+
cert: string;
|
|
19
|
+
key: string;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Generate a self-signed TLS certificate.
|
|
23
|
+
* Prefers openssl CLI (correct DER encoding on all platforms),
|
|
24
|
+
* falls back to manual ASN.1 DER encoding if openssl is unavailable.
|
|
25
|
+
*/
|
|
26
|
+
export declare function generateSelfSignedCert(commonName: string, days?: number): SelfSignedCert;
|
|
27
|
+
//# sourceMappingURL=hash.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hash.d.ts","sourceRoot":"","sources":["../../src/utils/hash.ts"],"names":[],"mappings":"AAAA;;GAEG;AAUH,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEhD;AAED,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,MAAM,CAE7E;AAED,wBAAgB,UAAU,IAAI,MAAM,CAEnC;AAED,wBAAgB,aAAa,IAAI,MAAM,CAEtC;AAED,MAAM,WAAW,cAAc;IAC7B,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,wBAAgB,sBAAsB,IAAI,cAAc,CAMvD;AAED,wBAAgB,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,CAG7D;AAED,wBAAgB,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAWlF;AAED,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAgB1E;AAED,wBAAgB,mBAAmB,CAAC,aAAa,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAmBnF;AAED,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;CACb;AAED;;;;GAIG;AACH,wBAAgB,sBAAsB,CAAC,UAAU,EAAE,MAAM,EAAE,IAAI,SAAM,GAAG,cAAc,CA+BrF"}
|
|
@@ -0,0 +1,348 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cryptographic utilities for hash chains and integrity verification
|
|
3
|
+
*/
|
|
4
|
+
import * as crypto from 'node:crypto';
|
|
5
|
+
import { execFileSync } from 'node:child_process';
|
|
6
|
+
import * as fs from 'node:fs';
|
|
7
|
+
import * as path from 'node:path';
|
|
8
|
+
import * as os from 'node:os';
|
|
9
|
+
const HASH_ALGORITHM = 'sha256';
|
|
10
|
+
export function computeHash(data) {
|
|
11
|
+
return crypto.createHash(HASH_ALGORITHM).update(data).digest('hex');
|
|
12
|
+
}
|
|
13
|
+
export function computeChainedHash(data, previousHash) {
|
|
14
|
+
return computeHash(previousHash + data);
|
|
15
|
+
}
|
|
16
|
+
export function generateId() {
|
|
17
|
+
return crypto.randomUUID();
|
|
18
|
+
}
|
|
19
|
+
export function generateNonce() {
|
|
20
|
+
return crypto.randomBytes(16).toString('hex');
|
|
21
|
+
}
|
|
22
|
+
export function generateSigningKeyPair() {
|
|
23
|
+
const { publicKey, privateKey } = crypto.generateKeyPairSync('ed25519', {
|
|
24
|
+
publicKeyEncoding: { type: 'spki', format: 'pem' },
|
|
25
|
+
privateKeyEncoding: { type: 'pkcs8', format: 'pem' }
|
|
26
|
+
});
|
|
27
|
+
return { publicKey, privateKey };
|
|
28
|
+
}
|
|
29
|
+
export function sign(data, privateKey) {
|
|
30
|
+
const signature = crypto.sign(null, Buffer.from(data), privateKey);
|
|
31
|
+
return signature.toString('base64');
|
|
32
|
+
}
|
|
33
|
+
export function verify(data, signature, publicKey) {
|
|
34
|
+
try {
|
|
35
|
+
return crypto.verify(null, Buffer.from(data), publicKey, Buffer.from(signature, 'base64'));
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
return false;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
export function encryptWithPassword(data, password) {
|
|
42
|
+
const salt = crypto.randomBytes(16);
|
|
43
|
+
const key = crypto.pbkdf2Sync(password, salt, 600000, 32, 'sha256');
|
|
44
|
+
const iv = crypto.randomBytes(12);
|
|
45
|
+
const cipher = crypto.createCipheriv('aes-256-gcm', key, iv);
|
|
46
|
+
let encrypted = cipher.update(data, 'utf-8', 'base64');
|
|
47
|
+
encrypted += cipher.final('base64');
|
|
48
|
+
const authTag = cipher.getAuthTag();
|
|
49
|
+
return [
|
|
50
|
+
salt.toString('base64'),
|
|
51
|
+
iv.toString('base64'),
|
|
52
|
+
authTag.toString('base64'),
|
|
53
|
+
encrypted
|
|
54
|
+
].join(':');
|
|
55
|
+
}
|
|
56
|
+
export function decryptWithPassword(encryptedData, password) {
|
|
57
|
+
const [saltB64, ivB64, authTagB64, encrypted] = encryptedData.split(':');
|
|
58
|
+
if (!saltB64 || !ivB64 || !authTagB64 || !encrypted) {
|
|
59
|
+
throw new Error('Invalid encrypted data format');
|
|
60
|
+
}
|
|
61
|
+
const salt = Buffer.from(saltB64, 'base64');
|
|
62
|
+
const iv = Buffer.from(ivB64, 'base64');
|
|
63
|
+
const authTag = Buffer.from(authTagB64, 'base64');
|
|
64
|
+
const key = crypto.pbkdf2Sync(password, salt, 600000, 32, 'sha256');
|
|
65
|
+
const decipher = crypto.createDecipheriv('aes-256-gcm', key, iv);
|
|
66
|
+
decipher.setAuthTag(authTag);
|
|
67
|
+
let decrypted = decipher.update(encrypted, 'base64', 'utf-8');
|
|
68
|
+
decrypted += decipher.final('utf-8');
|
|
69
|
+
return decrypted;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Generate a self-signed TLS certificate.
|
|
73
|
+
* Prefers openssl CLI (correct DER encoding on all platforms),
|
|
74
|
+
* falls back to manual ASN.1 DER encoding if openssl is unavailable.
|
|
75
|
+
*/
|
|
76
|
+
export function generateSelfSignedCert(commonName, days = 365) {
|
|
77
|
+
// Generate RSA key pair using Node.js crypto (always reliable)
|
|
78
|
+
const { publicKey, privateKey } = crypto.generateKeyPairSync('rsa', {
|
|
79
|
+
modulusLength: 2048,
|
|
80
|
+
publicKeyEncoding: { type: 'spki', format: 'pem' },
|
|
81
|
+
privateKeyEncoding: { type: 'pkcs8', format: 'pem' }
|
|
82
|
+
});
|
|
83
|
+
// Try openssl CLI first — produces correct DER across all OpenSSL/LibreSSL versions
|
|
84
|
+
try {
|
|
85
|
+
const cert = generateCertWithOpenSSL(commonName, days, privateKey);
|
|
86
|
+
return { cert, key: privateKey };
|
|
87
|
+
}
|
|
88
|
+
catch {
|
|
89
|
+
// Fall back to manual ASN.1 DER encoding
|
|
90
|
+
}
|
|
91
|
+
const now = new Date();
|
|
92
|
+
const notBefore = now;
|
|
93
|
+
const notAfter = new Date(now.getTime() + days * 24 * 60 * 60 * 1000);
|
|
94
|
+
const serialNumber = crypto.randomBytes(16).toString('hex');
|
|
95
|
+
const cert = buildSelfSignedCert({
|
|
96
|
+
commonName,
|
|
97
|
+
publicKey,
|
|
98
|
+
privateKey,
|
|
99
|
+
notBefore,
|
|
100
|
+
notAfter,
|
|
101
|
+
serialNumber
|
|
102
|
+
});
|
|
103
|
+
return { cert, key: privateKey };
|
|
104
|
+
}
|
|
105
|
+
function generateCertWithOpenSSL(commonName, days, privateKey) {
|
|
106
|
+
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'aquaman-cert-'));
|
|
107
|
+
const keyFile = path.join(tmpDir, 'key.pem');
|
|
108
|
+
const certFile = path.join(tmpDir, 'cert.pem');
|
|
109
|
+
const configFile = path.join(tmpDir, 'openssl.cnf');
|
|
110
|
+
try {
|
|
111
|
+
fs.writeFileSync(keyFile, privateKey, { mode: 0o600 });
|
|
112
|
+
// Minimal openssl config with SAN extension
|
|
113
|
+
fs.writeFileSync(configFile, [
|
|
114
|
+
'[req]',
|
|
115
|
+
'distinguished_name = dn',
|
|
116
|
+
'x509_extensions = ext',
|
|
117
|
+
'prompt = no',
|
|
118
|
+
'[dn]',
|
|
119
|
+
`CN = ${commonName}`,
|
|
120
|
+
'[ext]',
|
|
121
|
+
`subjectAltName = DNS:${commonName}`,
|
|
122
|
+
'basicConstraints = critical, CA:FALSE',
|
|
123
|
+
'keyUsage = critical, digitalSignature, keyEncipherment',
|
|
124
|
+
].join('\n'));
|
|
125
|
+
execFileSync('openssl', [
|
|
126
|
+
'req', '-x509',
|
|
127
|
+
'-key', keyFile,
|
|
128
|
+
'-out', certFile,
|
|
129
|
+
'-days', String(days),
|
|
130
|
+
'-config', configFile,
|
|
131
|
+
], { stdio: 'pipe' });
|
|
132
|
+
return fs.readFileSync(certFile, 'utf-8');
|
|
133
|
+
}
|
|
134
|
+
finally {
|
|
135
|
+
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
function buildSelfSignedCert(params) {
|
|
139
|
+
// Extract the public key bytes from PEM
|
|
140
|
+
const pubKeyDer = pemToDer(params.publicKey, 'PUBLIC KEY');
|
|
141
|
+
// Build TBSCertificate (To Be Signed Certificate)
|
|
142
|
+
const tbsCert = buildTBSCertificate(params, pubKeyDer);
|
|
143
|
+
// Sign the TBSCertificate
|
|
144
|
+
const signature = crypto.sign('sha256', tbsCert, params.privateKey);
|
|
145
|
+
// Build the complete certificate
|
|
146
|
+
const cert = buildCertificate(tbsCert, signature);
|
|
147
|
+
// Convert to PEM
|
|
148
|
+
return derToPem(cert, 'CERTIFICATE');
|
|
149
|
+
}
|
|
150
|
+
function pemToDer(pem, label) {
|
|
151
|
+
const base64 = pem
|
|
152
|
+
.replace(`-----BEGIN ${label}-----`, '')
|
|
153
|
+
.replace(`-----END ${label}-----`, '')
|
|
154
|
+
.replace(/\s/g, '');
|
|
155
|
+
return Buffer.from(base64, 'base64');
|
|
156
|
+
}
|
|
157
|
+
function derToPem(der, label) {
|
|
158
|
+
const base64 = der.toString('base64');
|
|
159
|
+
const lines = [];
|
|
160
|
+
for (let i = 0; i < base64.length; i += 64) {
|
|
161
|
+
lines.push(base64.slice(i, i + 64));
|
|
162
|
+
}
|
|
163
|
+
return `-----BEGIN ${label}-----\n${lines.join('\n')}\n-----END ${label}-----\n`;
|
|
164
|
+
}
|
|
165
|
+
// ASN.1 DER encoding helpers
|
|
166
|
+
function encodeLength(length) {
|
|
167
|
+
if (length < 128) {
|
|
168
|
+
return Buffer.from([length]);
|
|
169
|
+
}
|
|
170
|
+
const bytes = [];
|
|
171
|
+
let temp = length;
|
|
172
|
+
while (temp > 0) {
|
|
173
|
+
bytes.unshift(temp & 0xff);
|
|
174
|
+
temp >>= 8;
|
|
175
|
+
}
|
|
176
|
+
return Buffer.from([0x80 | bytes.length, ...bytes]);
|
|
177
|
+
}
|
|
178
|
+
function encodeSequence(items) {
|
|
179
|
+
const content = Buffer.concat(items);
|
|
180
|
+
const lengthBytes = encodeLength(content.length);
|
|
181
|
+
return Buffer.concat([Buffer.from([0x30]), lengthBytes, content]);
|
|
182
|
+
}
|
|
183
|
+
function encodeSet(items) {
|
|
184
|
+
const content = Buffer.concat(items);
|
|
185
|
+
const lengthBytes = encodeLength(content.length);
|
|
186
|
+
return Buffer.concat([Buffer.from([0x31]), lengthBytes, content]);
|
|
187
|
+
}
|
|
188
|
+
function encodeInteger(value) {
|
|
189
|
+
let bytes;
|
|
190
|
+
if (typeof value === 'number') {
|
|
191
|
+
if (value === 0) {
|
|
192
|
+
bytes = Buffer.from([0]);
|
|
193
|
+
}
|
|
194
|
+
else {
|
|
195
|
+
const hex = value.toString(16);
|
|
196
|
+
bytes = Buffer.from(hex.length % 2 ? '0' + hex : hex, 'hex');
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
else {
|
|
200
|
+
bytes = value;
|
|
201
|
+
}
|
|
202
|
+
// Add leading zero if high bit is set (to ensure positive number)
|
|
203
|
+
if (bytes[0] & 0x80) {
|
|
204
|
+
bytes = Buffer.concat([Buffer.from([0]), bytes]);
|
|
205
|
+
}
|
|
206
|
+
const lengthBytes = encodeLength(bytes.length);
|
|
207
|
+
return Buffer.concat([Buffer.from([0x02]), lengthBytes, bytes]);
|
|
208
|
+
}
|
|
209
|
+
function encodeOID(oid) {
|
|
210
|
+
const parts = oid.split('.').map(Number);
|
|
211
|
+
const bytes = [];
|
|
212
|
+
// First two components are encoded specially
|
|
213
|
+
bytes.push(parts[0] * 40 + parts[1]);
|
|
214
|
+
// Remaining components use variable-length encoding
|
|
215
|
+
for (let i = 2; i < parts.length; i++) {
|
|
216
|
+
let n = parts[i];
|
|
217
|
+
if (n === 0) {
|
|
218
|
+
bytes.push(0);
|
|
219
|
+
}
|
|
220
|
+
else {
|
|
221
|
+
const octets = [];
|
|
222
|
+
while (n > 0) {
|
|
223
|
+
octets.unshift(n & 0x7f);
|
|
224
|
+
n >>= 7;
|
|
225
|
+
}
|
|
226
|
+
for (let j = 0; j < octets.length - 1; j++) {
|
|
227
|
+
octets[j] |= 0x80;
|
|
228
|
+
}
|
|
229
|
+
bytes.push(...octets);
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
const content = Buffer.from(bytes);
|
|
233
|
+
const lengthBytes = encodeLength(content.length);
|
|
234
|
+
return Buffer.concat([Buffer.from([0x06]), lengthBytes, content]);
|
|
235
|
+
}
|
|
236
|
+
function encodePrintableString(str) {
|
|
237
|
+
const content = Buffer.from(str, 'ascii');
|
|
238
|
+
const lengthBytes = encodeLength(content.length);
|
|
239
|
+
return Buffer.concat([Buffer.from([0x13]), lengthBytes, content]);
|
|
240
|
+
}
|
|
241
|
+
function encodeUTCTime(date) {
|
|
242
|
+
const year = date.getUTCFullYear() % 100;
|
|
243
|
+
const month = (date.getUTCMonth() + 1).toString().padStart(2, '0');
|
|
244
|
+
const day = date.getUTCDate().toString().padStart(2, '0');
|
|
245
|
+
const hours = date.getUTCHours().toString().padStart(2, '0');
|
|
246
|
+
const minutes = date.getUTCMinutes().toString().padStart(2, '0');
|
|
247
|
+
const seconds = date.getUTCSeconds().toString().padStart(2, '0');
|
|
248
|
+
const str = `${year.toString().padStart(2, '0')}${month}${day}${hours}${minutes}${seconds}Z`;
|
|
249
|
+
const content = Buffer.from(str, 'ascii');
|
|
250
|
+
const lengthBytes = encodeLength(content.length);
|
|
251
|
+
return Buffer.concat([Buffer.from([0x17]), lengthBytes, content]);
|
|
252
|
+
}
|
|
253
|
+
function encodeBitString(data) {
|
|
254
|
+
// Add leading byte for unused bits (0)
|
|
255
|
+
const content = Buffer.concat([Buffer.from([0]), data]);
|
|
256
|
+
const lengthBytes = encodeLength(content.length);
|
|
257
|
+
return Buffer.concat([Buffer.from([0x03]), lengthBytes, content]);
|
|
258
|
+
}
|
|
259
|
+
function encodeContextTag(tag, content) {
|
|
260
|
+
const lengthBytes = encodeLength(content.length);
|
|
261
|
+
return Buffer.concat([Buffer.from([0xa0 | tag]), lengthBytes, content]);
|
|
262
|
+
}
|
|
263
|
+
function buildTBSCertificate(params, pubKeyDer) {
|
|
264
|
+
// Version (v3 = 2)
|
|
265
|
+
const version = encodeContextTag(0, encodeInteger(2));
|
|
266
|
+
// Serial number
|
|
267
|
+
const serial = encodeInteger(Buffer.from(params.serialNumber, 'hex'));
|
|
268
|
+
// Signature algorithm (SHA256 with RSA)
|
|
269
|
+
const signatureAlgorithm = encodeSequence([
|
|
270
|
+
encodeOID('1.2.840.113549.1.1.11'), // sha256WithRSAEncryption
|
|
271
|
+
Buffer.from([0x05, 0x00]) // NULL
|
|
272
|
+
]);
|
|
273
|
+
// Issuer (same as subject for self-signed)
|
|
274
|
+
const issuer = buildName(params.commonName);
|
|
275
|
+
// Validity
|
|
276
|
+
const validity = encodeSequence([
|
|
277
|
+
encodeUTCTime(params.notBefore),
|
|
278
|
+
encodeUTCTime(params.notAfter)
|
|
279
|
+
]);
|
|
280
|
+
// Subject
|
|
281
|
+
const subject = buildName(params.commonName);
|
|
282
|
+
// Subject Public Key Info (already in DER format)
|
|
283
|
+
const subjectPublicKeyInfo = pubKeyDer;
|
|
284
|
+
// Extensions (v3)
|
|
285
|
+
const extensions = buildExtensions(params.commonName);
|
|
286
|
+
const extensionsTagged = encodeContextTag(3, extensions);
|
|
287
|
+
return encodeSequence([
|
|
288
|
+
version,
|
|
289
|
+
serial,
|
|
290
|
+
signatureAlgorithm,
|
|
291
|
+
issuer,
|
|
292
|
+
validity,
|
|
293
|
+
subject,
|
|
294
|
+
subjectPublicKeyInfo,
|
|
295
|
+
extensionsTagged
|
|
296
|
+
]);
|
|
297
|
+
}
|
|
298
|
+
function buildName(commonName) {
|
|
299
|
+
// Build RDN for CN (Common Name)
|
|
300
|
+
const cnOid = encodeOID('2.5.4.3'); // id-at-commonName
|
|
301
|
+
const cnValue = encodePrintableString(commonName);
|
|
302
|
+
const cnAttr = encodeSequence([cnOid, cnValue]);
|
|
303
|
+
const cnRdn = encodeSet([cnAttr]);
|
|
304
|
+
return encodeSequence([cnRdn]);
|
|
305
|
+
}
|
|
306
|
+
function buildExtensions(commonName) {
|
|
307
|
+
// Basic Constraints (CA: false)
|
|
308
|
+
const basicConstraints = encodeSequence([
|
|
309
|
+
encodeOID('2.5.29.19'), // id-ce-basicConstraints
|
|
310
|
+
Buffer.from([0x01, 0x01, 0xff]), // critical = true
|
|
311
|
+
Buffer.from([0x04, 0x02, 0x30, 0x00]) // OCTET STRING containing empty SEQUENCE
|
|
312
|
+
]);
|
|
313
|
+
// Key Usage (digitalSignature, keyEncipherment)
|
|
314
|
+
const keyUsage = encodeSequence([
|
|
315
|
+
encodeOID('2.5.29.15'), // id-ce-keyUsage
|
|
316
|
+
Buffer.from([0x01, 0x01, 0xff]), // critical = true
|
|
317
|
+
Buffer.from([0x04, 0x04, 0x03, 0x02, 0x05, 0xa0]) // OCTET STRING containing BIT STRING
|
|
318
|
+
]);
|
|
319
|
+
// Subject Alternative Name (DNS name)
|
|
320
|
+
const sanValue = encodeSequence([
|
|
321
|
+
Buffer.concat([
|
|
322
|
+
Buffer.from([0x82]), // context tag 2 (dNSName)
|
|
323
|
+
encodeLength(commonName.length),
|
|
324
|
+
Buffer.from(commonName, 'ascii')
|
|
325
|
+
])
|
|
326
|
+
]);
|
|
327
|
+
const sanOctet = Buffer.concat([
|
|
328
|
+
Buffer.from([0x04]),
|
|
329
|
+
encodeLength(sanValue.length),
|
|
330
|
+
sanValue
|
|
331
|
+
]);
|
|
332
|
+
const san = encodeSequence([
|
|
333
|
+
encodeOID('2.5.29.17'), // id-ce-subjectAltName
|
|
334
|
+
sanOctet
|
|
335
|
+
]);
|
|
336
|
+
return encodeSequence([basicConstraints, keyUsage, san]);
|
|
337
|
+
}
|
|
338
|
+
function buildCertificate(tbsCert, signature) {
|
|
339
|
+
// Signature algorithm
|
|
340
|
+
const signatureAlgorithm = encodeSequence([
|
|
341
|
+
encodeOID('1.2.840.113549.1.1.11'), // sha256WithRSAEncryption
|
|
342
|
+
Buffer.from([0x05, 0x00]) // NULL
|
|
343
|
+
]);
|
|
344
|
+
// Signature value
|
|
345
|
+
const signatureValue = encodeBitString(signature);
|
|
346
|
+
return encodeSequence([tbsCert, signatureAlgorithm, signatureValue]);
|
|
347
|
+
}
|
|
348
|
+
//# sourceMappingURL=hash.js.map
|