From 15f4a4c5a3f81d9655c2cc4a2f3556d2001ac806 Mon Sep 17 00:00:00 2001 From: mycognosist Date: Wed, 15 Dec 2021 15:29:11 +0200 Subject: [PATCH] add go-sbot keystore functionality --- src/keystore/gosbot.rs | 98 ++++++++++++++++++++++++++++++++++++++++++ src/keystore/mod.rs | 4 +- 2 files changed, 101 insertions(+), 1 deletion(-) create mode 100644 src/keystore/gosbot.rs diff --git a/src/keystore/gosbot.rs b/src/keystore/gosbot.rs new file mode 100644 index 0000000..a04c745 --- /dev/null +++ b/src/keystore/gosbot.rs @@ -0,0 +1,98 @@ +//! Read and write a go-sbot secret (identity) file. + +#![warn(missing_docs)] + +use async_std::{ + io::{Read, Write}, + prelude::*, +}; + +use std::string::ToString; + +use super::{ + error::{Error, Result}, + CURVE_ED25519, + OwnedIdentity, + JsonSSBSecret, +}; +use crate::crypto::{ToSodiumObject, ToSsbId}; + +fn to_io_error(err: T) -> async_std::io::Error { + async_std::io::Error::new(std::io::ErrorKind::Other, err.to_string()) +} + +/// Return an `OwnedIdentity` from the local go-sbot secret file. +pub async fn from_gosbot_local() -> Result { + let home_dir = dirs::home_dir().ok_or(Error::HomeNotFound)?; + let local_key_file = format!("{}/.ssb-go/secret", home_dir.to_string_lossy()); + let mut file = async_std::fs::File::open(local_key_file).await?; + read_gosbot_config(&mut file).await +} + +/// Read the contents of the go-sbot secret file, deserialize into a +/// `JsonSSBSecret` and return an `OwnedIdentity`. +pub async fn read_gosbot_config(reader: &mut R) -> Result { + let mut buf = String::new(); + reader.read_to_string(&mut buf).await?; + + // parse json + let secret: JsonSSBSecret = serde_json::from_str(buf.as_ref()).map_err(to_io_error)?; + + if secret.curve != CURVE_ED25519 { + return Err(Error::InvalidConfig); + } + + Ok(OwnedIdentity { + id: secret.id, + pk: secret.public.to_ed25519_pk()?, + sk: secret.private.to_ed25519_sk()?, + }) +} + +/// Write an `OwnedIdentity`. +pub async fn write_gosbot_config( + id: &OwnedIdentity, + writer: &mut W, +) -> Result<()> { + let json = JsonSSBSecret { + curve: CURVE_ED25519.to_owned(), + id: id.id.clone(), + private: id.sk.to_ssb_id(), + public: id.pk.to_ssb_id(), + }; + let encoded = serde_json::to_vec(&json)?; + Ok(writer.write_all(&encoded).await?) +} + +#[cfg(test)] +mod test { + use async_std::io::Cursor; + use super::*; + + // ssb secret file contents, as formatted by go-sbot + const SECRET: &str = r#"{"curve":"ed25519","id":"@1vxS6DMi7z9uJIQG33W7mlsv21GZIbOpmWE1QEcn9oY=.ed25519","private":"F9bw6dPLaHR89hg6Q2dRmoNHHjm+COI53L0kdV3Y4w3W/FLoMyLvP24khAbfdbuaWy/bUZkhs6mZYTVARyf2hg==.ed25519","public":"1vxS6DMi7z9uJIQG33W7mlsv21GZIbOpmWE1QEcn9oY=.ed25519"}"#; + + #[async_std::test] + async fn test_gosbot_secret() -> Result<()> { + let mut secret_bytes = SECRET.as_bytes(); + let read_secret_output = read_gosbot_config(&mut secret_bytes).await?; + let expected = OwnedIdentity { + id: "@1vxS6DMi7z9uJIQG33W7mlsv21GZIbOpmWE1QEcn9oY=.ed25519".to_owned(), + sk: "F9bw6dPLaHR89hg6Q2dRmoNHHjm+COI53L0kdV3Y4w3W/FLoMyLvP24khAbfdbuaWy/bUZkhs6mZYTVARyf2hg==.ed25519".to_ed25519_sk()?, + pk: "1vxS6DMi7z9uJIQG33W7mlsv21GZIbOpmWE1QEcn9oY=.ed25519".to_ed25519_pk()? + }; + assert_eq!(expected, read_secret_output); + + // create a Cursor which wraps an in-memory buffer (implements `Write`) + let mut secret_buffer = Cursor::new(Vec::new()); + // write the `OwnedIdentity` from `read_gosbot_config()` to the buffer + write_gosbot_config(&read_secret_output, &mut secret_buffer).await?; + // retrieve the value from inside the Cursor + let secret_vector = secret_buffer.into_inner(); + // convert the byte slice to a string slice + let write_secret_output = std::str::from_utf8(&secret_vector).unwrap(); + assert_eq!(SECRET, write_secret_output); + + Ok(()) + } +} diff --git a/src/keystore/mod.rs b/src/keystore/mod.rs index 82269ee..5e5cb3f 100644 --- a/src/keystore/mod.rs +++ b/src/keystore/mod.rs @@ -1,6 +1,8 @@ mod error; +pub mod gosbot; mod identity; pub mod patchwork; -pub use identity::OwnedIdentity; +pub use gosbot::{from_gosbot_local, read_gosbot_config, write_gosbot_config}; +pub use identity::{JsonSSBSecret, OwnedIdentity, CURVE_ED25519}; pub use patchwork::{from_patchwork_local, read_patchwork_config, write_patchwork_config};