redesign sshsigchain authority protocol
This commit is contained in:
parent
73500e1944
commit
492a37c020
5 changed files with 1542 additions and 867 deletions
|
|
@ -4109,20 +4109,22 @@ fn print_response(response: ControlResponse, output: OutputMode) -> Result<()> {
|
||||||
chain_id,
|
chain_id,
|
||||||
records,
|
records,
|
||||||
head,
|
head,
|
||||||
active_admin_keys,
|
active_keys,
|
||||||
users,
|
|
||||||
devices,
|
devices,
|
||||||
nodes,
|
disclosed_profiles,
|
||||||
|
incomplete_profiles,
|
||||||
|
anchor_threshold,
|
||||||
note,
|
note,
|
||||||
} => {
|
} => {
|
||||||
println!("sigchain: {}", input.display());
|
println!("sigchain: {}", input.display());
|
||||||
println!("chain_id: {chain_id}");
|
println!("chain_id: {chain_id}");
|
||||||
println!("records: {records}");
|
println!("records: {records}");
|
||||||
println!("head: {head}");
|
println!("head: {head}");
|
||||||
println!("active_admin_keys: {active_admin_keys}");
|
println!("active_keys: {active_keys}");
|
||||||
println!("users: {users}");
|
|
||||||
println!("devices: {devices}");
|
println!("devices: {devices}");
|
||||||
println!("nodes: {nodes}");
|
println!("disclosed_profiles: {disclosed_profiles}");
|
||||||
|
println!("incomplete_profiles: {incomplete_profiles}");
|
||||||
|
println!("anchor_threshold: {anchor_threshold}");
|
||||||
eprintln!("note: {note}");
|
eprintln!("note: {note}");
|
||||||
}
|
}
|
||||||
ControlResponse::KeychainExplained { subject, lines } => {
|
ControlResponse::KeychainExplained { subject, lines } => {
|
||||||
|
|
|
||||||
|
|
@ -746,10 +746,11 @@ pub enum ControlResponse {
|
||||||
chain_id: String,
|
chain_id: String,
|
||||||
records: usize,
|
records: usize,
|
||||||
head: String,
|
head: String,
|
||||||
active_admin_keys: usize,
|
active_keys: usize,
|
||||||
users: usize,
|
|
||||||
devices: usize,
|
devices: usize,
|
||||||
nodes: usize,
|
disclosed_profiles: usize,
|
||||||
|
incomplete_profiles: usize,
|
||||||
|
anchor_threshold: u16,
|
||||||
note: String,
|
note: String,
|
||||||
},
|
},
|
||||||
KeychainExplained {
|
KeychainExplained {
|
||||||
|
|
|
||||||
|
|
@ -13,15 +13,16 @@
|
||||||
mod sshsigchain;
|
mod sshsigchain;
|
||||||
|
|
||||||
pub use sshsigchain::{
|
pub use sshsigchain::{
|
||||||
ChainId as SshSigchainChainId, Digest as SshSigchainDigest,
|
AnchorBackendPolicy, AnchorPolicy, AnchorReceipt, AnchorReceiptVerifier, AnchoredHistory,
|
||||||
KEYCHAIN_SSH_SIGCHAIN_PAYLOAD_VERSION, KEYCHAIN_SSH_SIGCHAIN_PROFILE,
|
AuthorityDeviceState, AuthorityKey, AuthorityKeyId, AuthorityKeyState, AuthorityState,
|
||||||
KeychainSshSigchainPolicy, KeychainSshSigchainState, KeychainSshSigchainVerification,
|
AuthorityTransition, ChainId as SshSigchainChainId, Digest as SshSigchainDigest, HeadClaim,
|
||||||
MAX_JSONL_BYTES, MAX_JSONL_LINE_BYTES, MAX_NAMESPACE_BYTES, SSH_SIGCHAIN_NAMESPACE,
|
MAX_JSONL_BYTES, MAX_JSONL_LINE_BYTES, MAX_NAMESPACE_BYTES, Permission, ProfileDisclosure,
|
||||||
SSH_SIGCHAIN_VERIFIER_PRINCIPAL, SSH_SIGCHAIN_VERSION, SshSigchainError, SshSigchainPolicy,
|
ProfileExtension, SSH_SIGCHAIN_ANCHOR_NAMESPACE, SSH_SIGCHAIN_KEY_PROOF_NAMESPACE,
|
||||||
SshSigchainRecord, SshSigchainTrust, SshSigchainVerification, SshSigchainVerifier,
|
SSH_SIGCHAIN_NAMESPACE, SSH_SIGCHAIN_VERIFIER_PRINCIPAL, SSH_SIGCHAIN_VERSION,
|
||||||
decode_keychain_sshsigchain_payload, decode_sshsigchain_jsonl, encode_sshsigchain_jsonl,
|
SshSigchainError, SshSigchainRecord, SshSigchainTrust, SshSigchainVerification,
|
||||||
keychain_sshsigchain_payload, keychain_sshsigchain_unsigned_record,
|
SshSigchainVerifier, authority_key_id, decode_sshsigchain_jsonl, encode_sshsigchain_jsonl,
|
||||||
verify_keychain_sshsigchain, verify_sshsigchain,
|
key_proof_signing_bytes, profile_payload_commitment, select_anchored_head,
|
||||||
|
verify_anchor_receipts, verify_head_claim, verify_sshsigchain,
|
||||||
};
|
};
|
||||||
|
|
||||||
use geth_types::{
|
use geth_types::{
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -6505,7 +6505,6 @@ pub fn handle_request(
|
||||||
let records = read_sshsigchain_jsonl_file(&input)?;
|
let records = read_sshsigchain_jsonl_file(&input)?;
|
||||||
let trust = geth_keychain::SshSigchainTrust::new(
|
let trust = geth_keychain::SshSigchainTrust::new(
|
||||||
geth_keychain::SshSigchainChainId::from_hex(&chain_id)?,
|
geth_keychain::SshSigchainChainId::from_hex(&chain_id)?,
|
||||||
geth_keychain::KEYCHAIN_SSH_SIGCHAIN_PROFILE,
|
|
||||||
namespace.unwrap_or_else(|| geth_keychain::SSH_SIGCHAIN_NAMESPACE.to_owned()),
|
namespace.unwrap_or_else(|| geth_keychain::SSH_SIGCHAIN_NAMESPACE.to_owned()),
|
||||||
std::fs::read_to_string(root_key_path)?,
|
std::fs::read_to_string(root_key_path)?,
|
||||||
)?;
|
)?;
|
||||||
|
|
@ -6515,12 +6514,13 @@ pub fn handle_request(
|
||||||
chain_id: trust.chain_id.to_hex(),
|
chain_id: trust.chain_id.to_hex(),
|
||||||
records: verified.records,
|
records: verified.records,
|
||||||
head: verified.head.to_hex(),
|
head: verified.head.to_hex(),
|
||||||
active_admin_keys: verified.view.admin_keys.len(),
|
active_keys: verified.state.active_key_count(),
|
||||||
users: verified.view.users.len(),
|
devices: verified.state.devices.values().filter(|device| device.active).count(),
|
||||||
devices: verified.view.devices.len(),
|
disclosed_profiles: verified.state.disclosed_profiles.len(),
|
||||||
nodes: verified.view.nodes.len(),
|
incomplete_profiles: verified.state.incomplete_profiles.len(),
|
||||||
|
anchor_threshold: verified.state.anchor_policy.threshold,
|
||||||
note:
|
note:
|
||||||
"verified linked SSHSIGCHAIN v1 records against the explicitly pinned root key"
|
"verified sequence-free SSHSIGCHAIN v1 authority records and disclosed profile commitments against the explicitly pinned root key"
|
||||||
.to_owned(),
|
.to_owned(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
@ -9545,7 +9545,7 @@ fn verify_keychain_sigchain_entries_with_ssh(
|
||||||
fn verify_keychain_sshsigchain_with_ssh(
|
fn verify_keychain_sshsigchain_with_ssh(
|
||||||
records: &[geth_keychain::SshSigchainRecord],
|
records: &[geth_keychain::SshSigchainRecord],
|
||||||
trust: &geth_keychain::SshSigchainTrust,
|
trust: &geth_keychain::SshSigchainTrust,
|
||||||
) -> Result<geth_keychain::KeychainSshSigchainVerification, NodeError> {
|
) -> Result<geth_keychain::SshSigchainVerification, NodeError> {
|
||||||
geth_ssh_identity::ensure_ssh_keygen_available()?;
|
geth_ssh_identity::ensure_ssh_keygen_available()?;
|
||||||
let verify_dir = tempfile::tempdir()?;
|
let verify_dir = tempfile::tempdir()?;
|
||||||
|
|
||||||
|
|
@ -9601,7 +9601,7 @@ fn verify_keychain_sshsigchain_with_ssh(
|
||||||
let verifier = SshSigchainOpenSshVerifier {
|
let verifier = SshSigchainOpenSshVerifier {
|
||||||
verify_dir: verify_dir.path().to_path_buf(),
|
verify_dir: verify_dir.path().to_path_buf(),
|
||||||
};
|
};
|
||||||
Ok(geth_keychain::verify_keychain_sshsigchain(
|
Ok(geth_keychain::verify_sshsigchain(
|
||||||
records, trust, &verifier,
|
records, trust, &verifier,
|
||||||
)?)
|
)?)
|
||||||
}
|
}
|
||||||
|
|
@ -11045,14 +11045,13 @@ mod tests {
|
||||||
);
|
);
|
||||||
let trust = geth_keychain::SshSigchainTrust::new(
|
let trust = geth_keychain::SshSigchainTrust::new(
|
||||||
geth_keychain::SshSigchainChainId([0x42; 32]),
|
geth_keychain::SshSigchainChainId([0x42; 32]),
|
||||||
geth_keychain::KEYCHAIN_SSH_SIGCHAIN_PROFILE,
|
|
||||||
geth_keychain::SSH_SIGCHAIN_NAMESPACE,
|
geth_keychain::SSH_SIGCHAIN_NAMESPACE,
|
||||||
&root_public_key,
|
&root_public_key,
|
||||||
)
|
)
|
||||||
.expect("trust tuple");
|
.expect("trust tuple");
|
||||||
|
|
||||||
let sign = |record: geth_keychain::SshSigchainRecord| {
|
let sign = |record: geth_keychain::SshSigchainRecord| {
|
||||||
let payload = dir.path().join(format!("record-{}", record.sequence));
|
let payload = dir.path().join("record");
|
||||||
std::fs::write(&payload, record.signing_bytes().expect("signing bytes"))
|
std::fs::write(&payload, record.signing_bytes().expect("signing bytes"))
|
||||||
.expect("write signing payload");
|
.expect("write signing payload");
|
||||||
let output = geth_ssh_identity::sign_command(
|
let output = geth_ssh_identity::sign_command(
|
||||||
|
|
@ -11072,50 +11071,32 @@ mod tests {
|
||||||
record.with_signature(signature).expect("signed record")
|
record.with_signature(signature).expect("signed record")
|
||||||
};
|
};
|
||||||
|
|
||||||
let init_op = KeychainOp {
|
|
||||||
id: AuthOpId::new("auth-op:sshsigchain-init"),
|
|
||||||
created_at: UnixMillis(1),
|
|
||||||
kind: KeychainOpKind::KeychainInit,
|
|
||||||
};
|
|
||||||
let init = sign(
|
let init = sign(
|
||||||
geth_keychain::keychain_sshsigchain_unsigned_record(
|
geth_keychain::SshSigchainRecord::unsigned(
|
||||||
&trust,
|
trust.chain_id,
|
||||||
0,
|
|
||||||
None,
|
None,
|
||||||
&init_op,
|
|
||||||
&root_public_key,
|
&root_public_key,
|
||||||
|
geth_keychain::AuthorityTransition::Genesis {
|
||||||
|
device_id: "device:root".to_owned(),
|
||||||
|
root_key: geth_keychain::AuthorityKey {
|
||||||
|
public_key: root_public_key.clone(),
|
||||||
|
permissions: vec![geth_keychain::Permission::All],
|
||||||
|
delegable_permissions: vec![geth_keychain::Permission::All],
|
||||||
|
},
|
||||||
|
anchor_policy: geth_keychain::AnchorPolicy::default(),
|
||||||
|
},
|
||||||
|
vec![],
|
||||||
)
|
)
|
||||||
.expect("unsigned init"),
|
.expect("unsigned init"),
|
||||||
);
|
);
|
||||||
let root_add_op = KeychainOp {
|
|
||||||
id: AuthOpId::new("auth-op:sshsigchain-root"),
|
|
||||||
created_at: UnixMillis(2),
|
|
||||||
kind: KeychainOpKind::AdminKeyAdd {
|
|
||||||
key: KeyId::new(geth_keychain::admin_key_fingerprint(&root_public_key)),
|
|
||||||
public_key: Some(root_public_key.clone()),
|
|
||||||
principal: Some("root".to_owned()),
|
|
||||||
valid_after_ms: None,
|
|
||||||
valid_before_ms: None,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
let root_add = sign(
|
|
||||||
geth_keychain::keychain_sshsigchain_unsigned_record(
|
|
||||||
&trust,
|
|
||||||
1,
|
|
||||||
Some(init.record_hash().expect("init hash")),
|
|
||||||
&root_add_op,
|
|
||||||
&root_public_key,
|
|
||||||
)
|
|
||||||
.expect("unsigned root add"),
|
|
||||||
);
|
|
||||||
|
|
||||||
let verified = verify_keychain_sshsigchain_with_ssh(&[init.clone(), root_add], &trust)
|
let verified = verify_keychain_sshsigchain_with_ssh(&[init.clone()], &trust)
|
||||||
.expect("verify real OpenSSH SSHSIG chain");
|
.expect("verify real OpenSSH SSHSIG chain");
|
||||||
assert_eq!(verified.records, 2);
|
assert_eq!(verified.records, 1);
|
||||||
assert_eq!(verified.view.admin_keys.len(), 1);
|
assert_eq!(verified.state.active_key_count(), 1);
|
||||||
|
|
||||||
let mut tampered = init;
|
let mut tampered = init;
|
||||||
tampered.payload.push(0);
|
tampered.chain_id = geth_keychain::SshSigchainChainId([0x43; 32]);
|
||||||
assert!(verify_keychain_sshsigchain_with_ssh(&[tampered], &trust).is_err());
|
assert!(verify_keychain_sshsigchain_with_ssh(&[tampered], &trust).is_err());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue