45 lines
2.2 KiB
Python
45 lines
2.2 KiB
Python
"""Immutable signing/trust snapshot for the owned RNode process.
|
|
|
|
Upstream keeps its normal persistent firmware/cache HOME, but key reads use the
|
|
owner's sealed snapshot. Key generation/overwrite cannot replace retained keys.
|
|
The installer never receives Archy's node/wallet identity material.
|
|
"""
|
|
import builtins
|
|
import fcntl
|
|
import io
|
|
import json
|
|
import os
|
|
|
|
|
|
def install(upstream, fd=7):
|
|
seals = fcntl.F_SEAL_WRITE | fcntl.F_SEAL_GROW | fcntl.F_SEAL_SHRINK | fcntl.F_SEAL_SEAL
|
|
if fcntl.fcntl(fd, fcntl.F_GET_SEALS) & seals != seals:
|
|
raise ValueError('Sealed RNode key custody required')
|
|
raw = os.read(fd, 524289)
|
|
if len(raw) > 524288: raise ValueError('RNode key snapshot bound')
|
|
value = json.loads(raw)
|
|
required = {'firmware/signing.key', 'firmware/device.key'}
|
|
if not isinstance(value, dict) or not required <= value.keys() or len(value) > 32:
|
|
raise ValueError('Existing RNode signing identities required')
|
|
keys = {}
|
|
for name, encoded in value.items():
|
|
if name not in required and not (name.startswith('trusted_keys/') and name.endswith('.pubkey') and name.count('/') == 1):
|
|
raise ValueError('Unrecognized key custody name')
|
|
if '..' in name or not isinstance(encoded, str) or len(encoded) > 131072:
|
|
raise ValueError('Invalid key custody value')
|
|
keys[upstream.CNF_DIR+'/'+name] = bytes.fromhex(encoded)
|
|
original_open, original_isfile, original_listdir = builtins.open, os.path.isfile, os.listdir
|
|
|
|
def open_key(path, mode='r', *args, **kwargs):
|
|
name = os.fspath(path) if isinstance(path, (str, bytes, os.PathLike)) else None
|
|
if name in keys:
|
|
if mode != 'rb': raise ValueError('Retained RNode keys are read-only')
|
|
return io.BytesIO(keys[name])
|
|
return original_open(path, mode, *args, **kwargs)
|
|
|
|
builtins.open = open_key
|
|
name_of = lambda path: os.fspath(path) if isinstance(path, (str, bytes, os.PathLike)) else None
|
|
os.path.isfile = lambda path: name_of(path) in keys or original_isfile(path)
|
|
os.listdir = lambda path='.': ([name.rsplit('/',1)[1] for name in keys if name.startswith(upstream.TK_DIR+'/')]
|
|
if name_of(path) == upstream.TK_DIR else original_listdir(path))
|