Implement fail-closed node keystore bridge
This commit is contained in:
parent
c0e3c67fe4
commit
b4d6e7a4f9
5 changed files with 345 additions and 5 deletions
|
|
@ -22,6 +22,7 @@ import { DomainAccessOrchestrator } from '../../guanghu-knowledge-base/server/do
|
|||
import { TrustedSignerSnapshotLoader } from '../../guanghu-knowledge-base/server/trusted-signer-snapshot.js';
|
||||
import { NodeRegistrationSnapshotLoader } from '../../guanghu-knowledge-base/server/node-registration-snapshot.js';
|
||||
import { HttpNodeRegistrationClaimSource } from '../../guanghu-knowledge-base/server/node-registration-client.js';
|
||||
import { EncryptedNodeKeyStoreProofSource } from '../../guanghu-knowledge-base/server/node-keystore-bridge.js';
|
||||
import { DomainRuntimeHandoffSnapshotLoader } from '../../guanghu-knowledge-base/server/domain-runtime-handoff-snapshot.js';
|
||||
import {
|
||||
AnchoredDomainRuntimeHandoffSource,
|
||||
|
|
@ -40,6 +41,7 @@ const KB_REPO_PATH = path.join(DATA_DIR, 'knowledge-base');
|
|||
const MODEL_CONFIG_PATH = path.join(app.getPath('userData'), 'model-config.json');
|
||||
const SERVER_AUTH_PATH = path.join(app.getPath('userData'), 'server-auth.json');
|
||||
const SERVER_PROFILES_PATH = path.join(app.getPath('userData'), 'server-profiles.json');
|
||||
const NODE_KEYSTORE_PATH = path.join(app.getPath('userData'), 'node-keystore.json');
|
||||
const AGENT_STATE_PATH = path.join(app.getPath('userData'), 'agent-conversations.json');
|
||||
const GIT_ASKPASS_PATH = path.join(app.getPath('userData'), 'hololake-git-askpass.sh');
|
||||
interface ServerProfileDefinition {
|
||||
|
|
@ -366,6 +368,37 @@ const domainRuntimeHandoffs = new AnchoredDomainRuntimeHandoffSource(
|
|||
new HttpDomainRuntimeHandoffTransport(),
|
||||
);
|
||||
|
||||
const nodeKeyStoreProofs = new EncryptedNodeKeyStoreProofSource({
|
||||
isAvailable() {
|
||||
return safeStorage.isEncryptionAvailable();
|
||||
},
|
||||
async readRecord() {
|
||||
try {
|
||||
const stat = fs.statSync(NODE_KEYSTORE_PATH);
|
||||
if (!stat.isFile() || (stat.mode & 0o077) !== 0) return null;
|
||||
return JSON.parse(fs.readFileSync(NODE_KEYSTORE_PATH, 'utf8')) as unknown;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
},
|
||||
async decryptPrivateKeyPkcs8(encrypted) {
|
||||
try {
|
||||
if (!safeStorage.isEncryptionAvailable()) return null;
|
||||
const ciphertext = Buffer.from(encrypted, 'base64');
|
||||
if (ciphertext.length === 0 || ciphertext.toString('base64') !== encrypted) return null;
|
||||
const plaintextBase64 = safeStorage.decryptString(ciphertext);
|
||||
const plaintext = Buffer.from(plaintextBase64, 'base64');
|
||||
if (plaintext.length === 0 || plaintext.toString('base64') !== plaintextBase64) {
|
||||
plaintext.fill(0);
|
||||
return null;
|
||||
}
|
||||
return plaintext;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
const domainAccessOrchestrator = new DomainAccessOrchestrator(
|
||||
trustedSignerSnapshots,
|
||||
nodeRegistrationSnapshots,
|
||||
|
|
@ -388,11 +421,7 @@ const domainAccessOrchestrator = new DomainAccessOrchestrator(
|
|||
},
|
||||
},
|
||||
nodeRegistrationClaims,
|
||||
{
|
||||
// The production node-keystore bridge is not registered yet. Returning no
|
||||
// proof keeps the domain gate closed even if a future signed claim appears.
|
||||
async prove() { return null; },
|
||||
},
|
||||
nodeKeyStoreProofs,
|
||||
domainRuntimeHandoffs,
|
||||
);
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue