Update codebase to use new identity management

finchie
Aug 16, 2022, 9:00 AM
DWSAYGVEOR4D2EKIICEZUWCRGJTUXQQLOUWMYIFV7XN62K44F4FAC

Dependencies

  • [2] MU6P2JXG SSH: return with an error if the host key is wrong, rather than denying authentication
  • [3] C267PHOH Tags: dropping useless Hashes in favour of Merkles
  • [4] NAUECZW3 Fixing the map between keys and identities
  • [5] SXEYMYF7 Fixing the bad changes in history (unfortunately, by rebooting).
  • [6] E7UUQQCC Apply changes with prefixes in .pijul/changes
  • [7] 6F6AAHK4 Simplifying pijul::commands::log, and fixing Broken Pipe errors
  • [8] FDEVV5NG Tag check
  • [9] SZWBLWZ4 Reading ~/.ssh/config
  • [10] 3KRGVQFU Do not update the mtime of unmodified files
  • [11] RUBBHYZ7 Removing unnecessary async/await
  • [12] TFPETWTV Add config options for patch message templates
  • [13] L4JXJHWX pijul/*: reorganize imports and remove extern crate
  • [14] U6TQX5Z2 pager function respects cli option and user config files, PAGER env var
  • [15] F6V27C3M Fixing the "old file optimisation" in record, after the move to parallelisable records
  • [16] NS36CJCO Validating timestamps in record
  • [17] EEBKW7VT Keys and identities
  • [18] LJFJEX43 Fixing newline issues in the protocol over OpenSSH
  • [19] TEQS7C6C Fixing filters in `pijul log`
  • [20] IIV3EL2X Cleanup, formatting, and fixing the Git feature
  • [21] QL6K2ZM3 Tags
  • [22] RRCSHAYZ Formatting
  • [23] EUZFFJSO Updating Pijul with the latest changes in Libpijul
  • [24] OU6JOR3C Add path filtering for log, add json output for log
  • [25] OU243LAB Support for staging
  • [26] HKA66XOQ Updating Thrussh version for long or massive connections
  • [27] 2RXOCWUW Making libpijul deterministic (and getting rid of `rand`)
  • [28] 6U42MTEZ Fixing log -- filters, along with performance improvements
  • [29] ZDK3GNDB Tag transactions (including a massive refactoring of errors)
  • [30] CCLLB7OI Upgrading to Sanakirja 0.15 + version bump
  • [31] G6S6PWZE Do not touch the channel if this is a partial record
  • [32] I24UEJQL Various post-fire fixes
  • [33] UDHP4ZVB Fixing SSH asynchronicity issues
  • [34] AAXP2534 Tags: completing the subcommand
  • [35] SLJ3OHD4 unrecord: show list of changes if none were given as arguments
  • [36] TPEH2XNB 1.0.0-alpha.28, with Tokio 1.0
  • [37] I52XSRUH Massive cleanup, and simplification
  • [38] LGEJSLTY Fixing output (including its uses in reset and pull)
  • [39] SMMBFECL Converting to the new patch format "online"
  • [40] GUL4M5FI Cleanup and formatting
  • [41] I6DVZEFU Do not ask for user input if the SSH channel is already closed
  • [42] A3RM526Y Integrating identity malleability
  • [43] MU5GSJAW Partial push and pull (WARNING: breaks the existing protocol)

Change contents

  • replacement in pijul/src/commands/tag.rs at line 86
    [5.1480][5.1081:1133]()
    pub fn run(self) -> Result<(), anyhow::Error> {
    [5.1480]
    [5.1538]
    pub async fn run(self) -> Result<(), anyhow::Error> {
  • replacement in pijul/src/commands/tag.rs at line 126
    [5.2976][5.2976:3053]()
    let header = header(author.as_deref(), message, timestamp)?;
    [5.2976]
    [3.74]
    let header = header(author.as_deref(), message, timestamp).await?;
  • replacement in pijul/src/commands/tag.rs at line 270
    [5.4613][5.4613:4624]()
    fn header(
    [5.4613]
    [5.4624]
    async fn header(
  • replacement in pijul/src/commands/tag.rs at line 280
    [5.44][5.757:1002](),[5.13668][5.757:1002](),[5.757][5.757:1002](),[5.1002][5.13669:13820](),[5.84][5.1034:1044](),[5.13820][5.1034:1044](),[5.1034][5.1034:1044]()
    } else if let Some(mut dir) = crate::config::global_config_dir() {
    dir.push("publickey.json");
    if let Ok(key) = std::fs::File::open(&dir) {
    let k: libpijul::key::PublicKey = serde_json::from_reader(key).unwrap();
    b.insert("key".to_string(), k.key);
    } else {
    bail!("No identity configured yet. Please use `pijul key` to create one")
    }
    [5.13668]
    [5.1044]
    } else if let Some(_dir) = crate::config::global_config_dir() {
    let k = crate::identity::public_key(&crate::identity::choose_identity_name(false).await?)?;
    b.insert("key".to_string(), k.key);
  • edit in pijul/src/commands/record.rs at line 50
    [5.102757]
    [5.102757]
    /// Identity to sign changes with
    #[clap(long = "identity")]
    pub identity: Option<String>,
  • replacement in pijul/src/commands/record.rs at line 65
    [5.102774][5.1370:1422]()
    pub fn run(self) -> Result<(), anyhow::Error> {
    [5.102774]
    [5.13911]
    pub async fn run(self) -> Result<(), anyhow::Error> {
  • replacement in pijul/src/commands/record.rs at line 132
    [5.104017][5.203:230]()
    self.header()?
    [5.104017]
    [5.104043]
    self.header().await?
  • replacement in pijul/src/commands/record.rs at line 145
    [5.1069][4.0:43]()
    let (_, key) = super::load_key()?;
    [5.1069]
    [5.1910]
    let (_, key) = crate::identity::decrypt_secret_key(
    &crate::identity::choose_identity_name(false).await?,
    None,
    )?;
  • edit in pijul/src/commands/record.rs at line 176
    [5.14174][5.14174:14266]()
    path.push("publickey.json");
    std::fs::File::create(&path)?;
  • replacement in pijul/src/commands/record.rs at line 210
    [5.105380][5.231:293]()
    fn header(&self) -> Result<ChangeHeader, anyhow::Error> {
    [5.105380]
    [5.293]
    async fn header(&self) -> Result<ChangeHeader, anyhow::Error> {
  • replacement in pijul/src/commands/record.rs at line 216
    [5.129][5.2177:2438](),[5.14377][5.2177:2438](),[5.2177][5.2177:2438](),[5.2438][5.14378:14430](),[5.14430][5.173:284](),[5.173][5.173:284](),[5.284][5.2474:2488](),[5.2474][5.2474:2488]()
    } else if let Some(mut dir) = crate::config::global_config_dir() {
    dir.push("publickey.json");
    if let Ok(key) = std::fs::File::open(&dir) {
    let k: libpijul::key::PublicKey = serde_json::from_reader(key).unwrap();
    b.insert("key".to_string(), k.key);
    } else {
    bail!("No identity configured yet. Please use `pijul key` to create one")
    }
    [5.14377]
    [5.2488]
    } else {
    let public_key = crate::identity::public_key(
    &self
    .identity
    .clone()
    .unwrap_or(crate::identity::choose_identity_name(false).await?),
    );
    b.insert("key".to_string(), public_key?.key);
  • edit in pijul/src/commands/record.rs at line 225
    [5.2498]
    [5.14431]
  • replacement in pijul/src/commands/protocol.rs at line 12
    [5.3078][5.1962:1987](),[5.15149][5.1962:1987](),[5.123476][5.1962:1987]()
    use log::{debug, error};
    [5.15149]
    [5.123476]
    use log::{debug, error, warn};
  • edit in pijul/src/commands/protocol.rs at line 374
    [5.359][5.359:752]()
    fn get_public_key() -> Result<libpijul::key::PublicKey, anyhow::Error> {
    if let Some(mut dir) = crate::config::global_config_dir() {
    dir.push("publickey.json");
    if let Ok(mut pkf) = std::fs::File::open(&dir) {
    if let Ok(pkf) = serde_json::from_reader(&mut pkf) {
    return Ok(pkf);
    }
    }
    }
    bail!("No public key found")
    }
  • replacement in pijul/src/commands/protocol.rs at line 391
    [5.1259][5.1259:1864](),[5.1864][5.1864:1915](),[5.1915][5.1915:2178]()
    let public_key: libpijul::key::PublicKey = if let Ok(pk) = get_public_key() {
    pk
    } else {
    return Ok(());
    };
    if !done.insert(public_key.key.clone()) {
    return Ok(());
    }
    if let Ok((config, last_modified)) = crate::config::Global::load() {
    serde_json::to_writer(
    &mut o,
    &crate::Identity {
    public_key,
    email: config.author.email,
    name: config.author.full_name,
    login: config.author.name,
    origin: String::new(),
    last_modified,
    },
    )
    .unwrap();
    writeln!(o)?;
    } else {
    debug!("no global config");
    }
    [5.1259]
    [5.2178]
    warn!("Skipping serializing old public key format.");
    return Ok(());
  • replacement in pijul/src/commands/mod.rs at line 59
    [5.91][5.2522:2547]()
    mod key;
    pub use key::*;
    [5.91]
    [5.2547]
    mod identity;
    pub use identity::*;
  • replacement in pijul/src/commands/mod.rs at line 280
    [5.357][5.357:398]()
    #[derive(Debug, Serialize, Deserialize)]
    [5.357]
    [5.398]
    #[derive(Clone, Debug, Serialize, Deserialize)]
  • edit in pijul/src/commands/mod.rs at line 282
    [5.420][5.19978:20024]()
    pub public_key: libpijul::key::PublicKey,
  • edit in pijul/src/commands/mod.rs at line 291
    [5.20225]
    [5.20225]
    pub public_key: libpijul::key::PublicKey,
  • edit in pijul/src/commands/mod.rs at line 294
    [5.20228][4.67:157](),[4.157][5.20290:20787](),[5.20290][5.20290:20787](),[5.20787][4.158:229](),[4.229][5.20828:21042](),[5.20828][5.20828:21042](),[5.21042][5.60:69]()
    fn load_key() -> Result<(libpijul::key::SecretKey, libpijul::key::SKey), anyhow::Error> {
    if let Some(mut dir) = crate::config::global_config_dir() {
    dir.push("secretkey.json");
    if let Ok(key) = std::fs::File::open(&dir) {
    let k: libpijul::key::SecretKey = serde_json::from_reader(key)?;
    let pass = if k.encryption.is_some() {
    Some(rpassword::read_password_from_tty(Some(&format!(
    "Password for {:?}: ",
    dir
    )))?)
    } else {
    None
    };
    let sk = k.load(pass.as_deref())?;
    Ok((k, sk))
    } else {
    bail!("Secret key not found, please use `pijul key generate` and try again")
    }
    } else {
    bail!("Secret key not found, please use `pijul key generate` and try again")
    }
    }
  • edit in pijul/src/commands/log.rs at line 84
    [4.244][4.244:466]()
    let mut global_id_path = crate::config::global_config_dir();
    if let Some(ref mut gl) = global_id_path {
    gl.push("identities")
    }
    debug!("global_id_path = {:?}", global_id_path);
  • edit in pijul/src/commands/log.rs at line 89
    [5.1608][4.467:495]()
    global_id_path,
  • edit in pijul/src/commands/log.rs at line 267
    [5.7422][4.496:533]()
    global_id_path: Option<PathBuf>,
  • edit in pijul/src/commands/log.rs at line 316
    [5.10433][4.536:598]()
    let mut global_id_path = self.global_id_path.clone();
  • replacement in pijul/src/commands/log.rs at line 336
    [5.1350][4.599:871]()
    let entry = self.mk_log_entry(
    &mut authors,
    &mut id_path,
    &mut global_id_path,
    h.into(),
    Some(mrk.into()),
    )?;
    [5.1350]
    [5.1482]
    let entry =
    self.mk_log_entry(&mut authors, &mut id_path, h.into(), Some(mrk.into()))?;
  • edit in pijul/src/commands/log.rs at line 359
    [5.12405][4.872:918]()
    global_id_path: &mut Option<PathBuf>,
  • replacement in pijul/src/commands/log.rs at line 382
    [5.13299][4.995:1745]()
    debug!("{:?} {:?}", global_id_path, id);
    if let Some(ref mut global_id_path) = global_id_path {
    if id.is_none() {
    global_id_path.push(e.key());
    debug!("{:?}", global_id_path);
    if let Ok(f) = std::fs::File::open(&global_id_path) {
    if let Ok(id_) = serde_json::from_reader(f) {
    id = Some(id_)
    } else {
    debug!("wrong identity for {:?}", e.key());
    }
    [5.13299]
    [4.1745]
    debug!("{:?}", id);
    if let Ok(identities) = crate::identity::Complete::load_all() {
    for identity in identities {
    if &identity.identity.public_key.key == e.key() {
    id = Some(identity.identity);
  • edit in pijul/src/commands/log.rs at line 388
    [4.1783][4.1783:1841]()
    global_id_path.pop();
  • replacement in pijul/src/remote/ssh.rs at line 3
    [5.25667][5.25667:25699]()
    use std::path::{Path, PathBuf};
    [5.25667]
    [5.0]
    use std::path::PathBuf;
  • replacement in pijul/src/remote/ssh.rs at line 123
    [2.138][2.138:233]()
    self.auth_pk(&mut h, &mut key_path).await || self.auth_password(&mut h).await?
    [2.138]
    [2.233]
    let mut stderr = std::io::stderr();
    writeln!(stderr, "Warning: Unable to automatically authenticate with server. Please make sure your SSH keys have been uploaded to the Nest.")?;
    writeln!(stderr, "For more information, please visit https://pijul.org/manual/the_nest/public_keys.html#ssh-public-keys")?;
    self.auth_password(&mut h).await?
  • replacement in pijul/src/remote/ssh.rs at line 133
    [5.28303][5.7903:7942]()
    bail!("Not authenticated")
    [5.28303]
    [5.28359]
    bail!("Not authenticated. Please check your credentials and try again.");
  • edit in pijul/src/remote/ssh.rs at line 211
    [5.30396][5.30396:30403](),[5.30403][5.344:481](),[5.481][5.0:63](),[5.63][5.30498:30537](),[5.481][5.30498:30537](),[5.30498][5.30498:30537](),[5.30537][5.1618:1878](),[5.1878][5.30582:30825](),[5.30582][5.30582:30825](),[5.30825][5.1879:1951](),[5.1951][5.30898:31112](),[5.30898][5.30898:31112]()
    }
    async fn auth_pk(
    &self,
    h: &mut thrussh::client::Handle<SshClient>,
    key_path: &mut PathBuf,
    ) -> bool {
    if h.is_closed() {
    return false;
    }
    let mut authenticated = false;
    let mut keys = Vec::new();
    if let Some(ref file) = self.config.identity_file {
    keys.push(file.as_str())
    } else {
    keys.push("id_ed25519");
    keys.push("id_rsa");
    }
    for k in keys.iter() {
    key_path.push(k);
    let k = if let Some(k) = load_secret_key(&key_path, k) {
    k
    } else {
    key_path.pop();
    continue;
    };
    if let Ok(auth) = h
    .authenticate_publickey(&self.config.user, Arc::new(k))
    .await
    {
    authenticated = auth
    }
    key_path.pop();
    if authenticated {
    return true;
    }
    }
    false
  • edit in pijul/src/remote/ssh.rs at line 220
    [5.131][5.31219:31323](),[5.617][5.31219:31323](),[5.31219][5.31219:31323](),[5.31323][5.1952:2004](),[5.2004][5.31356:31370](),[5.31356][5.31356:31370](),[5.31370][5.2005:2093](),[5.2093][5.31438:31446](),[5.31438][5.31438:31446]()
    let pass = rpassword::read_password_from_tty(Some(&format!(
    "Password for {}@{}: ",
    self.config.user, self.config.host_name
    )))?;
    h.authenticate_password(self.config.user.to_string(), &pass)
    .await
    }
    }
  • replacement in pijul/src/remote/ssh.rs at line 221
    [5.31447][5.238:406](),[5.406][5.31595:31641](),[5.31595][5.31595:31641](),[5.31641][5.618:679](),[5.679][5.31717:32070](),[5.31717][5.31717:32070](),[5.32070][5.0:87](),[5.87][5.32167:32203](),[5.32167][5.32167:32203]()
    pub fn load_secret_key<P: AsRef<Path>>(key_path: P, k: &str) -> Option<thrussh_keys::key::KeyPair> {
    match thrussh_keys::load_secret_key(key_path.as_ref(), None) {
    Ok(k) => Some(k),
    Err(e) => {
    if let thrussh_keys::Error::KeyIsEncrypted = e {
    let pass = if let Ok(pass) =
    rpassword::read_password_from_tty(Some(&format!("Password for key {:?}: ", k)))
    {
    pass
    } else {
    return None;
    };
    if pass.is_empty() {
    return None;
    }
    if let Ok(k) = thrussh_keys::load_secret_key(&key_path, Some(&pass)) {
    return Some(k);
    [5.31447]
    [5.32203]
    // Authentication can be attempted multiple times
    let mut authenticated = false;
    let username = format!("{}@{}", self.config.user, self.config.host_name);
    // Try authenticate using the user's keyring
    if let Ok(password) = keyring::Entry::new("pijul", &username).get_password() {
    authenticated = h
    .authenticate_password(self.config.user.to_string(), &password)
    .await?;
    }
    // Try authenticate using user's password
    if !authenticated {
    let password = dialoguer::Password::with_theme(
    crate::config::load_theme()
    .expect("Could not load config")
    .as_ref(),
    )
    .with_prompt(format!("Password for {username}"))
    .allow_empty_password(true)
    .interact()
    .unwrap();
    authenticated = h
    .authenticate_password(self.config.user.to_string(), &password)
    .await?;
    // If the new password is valid, update the keyring to match
    if authenticated {
    match keyring::Entry::new("pijul", &username).set_password(&password) {
    Err(e) => writeln!(
    std::io::stderr(),
    "Warning: could not write new password to keychain: {e}"
    )
    .unwrap(),
    _ => (),
  • edit in pijul/src/remote/ssh.rs at line 259
    [5.32235][5.32235:32252]()
    None
  • edit in pijul/src/remote/ssh.rs at line 260
    [5.32262]
    [5.32262]
    Ok(authenticated)
  • replacement in libpijul/src/key.rs at line 19
    [5.5695][5.5695:5729]()
    #[derive(Serialize, Deserialize)]
    [5.5695]
    [5.5729]
    #[derive(Clone, Debug, Serialize, Deserialize)]
  • replacement in libpijul/src/key.rs at line 319
    [5.40384][5.14834:14868](),[5.14834][5.14834:14868]()
    #[derive(Serialize, Deserialize)]
    [5.40384]
    [5.14868]
    #[derive(Clone, Debug, Serialize, Deserialize)]
  • replacement in libpijul/src/key.rs at line 324
    [5.14910][5.14910:14944]()
    #[derive(Serialize, Deserialize)]
    [5.14910]
    [5.14944]
    #[derive(Clone, Debug, Serialize, Deserialize)]