git.delta.rocks / jrsonnet / refs/commits / 453e81eb3dff

difftreelog

feat hex secret encoding

Yaroslav Bolyukin2024-07-02parent: #1389456.patch.diff
in: trunk

3 files changed

modifiedCargo.lockdiffbeforeafterboth
812 "clap",812 "clap",
813 "ed25519-dalek",813 "ed25519-dalek",
814 "fleet-shared",814 "fleet-shared",
815 "hex",
815 "rand",816 "rand",
816 "x25519-dalek",817 "x25519-dalek",
817]818]
1048source = "registry+https://github.com/rust-lang/crates.io-index"1049source = "registry+https://github.com/rust-lang/crates.io-index"
1049checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024"1050checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024"
1051
1052[[package]]
1053name = "hex"
1054version = "0.4.3"
1055source = "registry+https://github.com/rust-lang/crates.io-index"
1056checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
10501057
1051[[package]]1058[[package]]
1052name = "hkdf"1059name = "hkdf"
modifiedcmds/generator-helper/Cargo.tomldiffbeforeafterboth
--- a/cmds/generator-helper/Cargo.toml
+++ b/cmds/generator-helper/Cargo.toml
@@ -10,5 +10,6 @@
 clap.workspace = true
 ed25519-dalek = { version = "2.1", features = ["rand_core"] }
 fleet-shared.workspace = true
+hex = "0.4.3"
 rand = "0.8.5"
 x25519-dalek = "2.0.1"
modifiedcmds/generator-helper/src/main.rsdiffbeforeafterboth
--- a/cmds/generator-helper/src/main.rs
+++ b/cmds/generator-helper/src/main.rs
@@ -1,7 +1,7 @@
 use std::{
 	env,
 	fs::{File, OpenOptions},
-	io::{copy, Read, Write},
+	io::{self, copy, stdin, stdout, Read, Write},
 	str::FromStr,
 };
 
@@ -106,10 +106,29 @@
 	match encoding {
 		OutputEncoding::Raw => coerce(w),
 		OutputEncoding::Base64 => {
-			use base64::engine::general_purpose::STANDARD;
-			let writer = base64::write::EncoderWriter::new(w, &STANDARD);
+			use base64::{engine::general_purpose::STANDARD, write::EncoderWriter};
+
+			let writer = EncoderWriter::new(w, &STANDARD);
 			coerce(writer)
 		}
+		OutputEncoding::Hex => {
+			struct HexWriter<W>(W);
+			impl<W> Write for HexWriter<W>
+			where
+				W: Write,
+			{
+				fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
+					let encoded = hex::encode(buf);
+					self.0.write_all(encoded.as_bytes())?;
+					Ok(buf.len())
+				}
+
+				fn flush(&mut self) -> io::Result<()> {
+					self.0.flush()
+				}
+			}
+			coerce(HexWriter(w))
+		}
 	}
 }
 
@@ -120,6 +139,8 @@
 	Raw,
 	/// Encode as base64 (with padding).
 	Base64,
+	/// Encode as hex (without leading 0x)
+	Hex,
 }
 
 #[derive(Parser)]
@@ -175,6 +196,14 @@
 		#[arg(long, short = 'e', value_enum, default_value_t)]
 		encoding: OutputEncoding,
 	},
+	/// Sometimes you also need to reencode secret, this command allows you to get raw data from
+	/// secret encoded using `gh public`... I would like if I knew a better design for some sort of
+	/// such thing. Ideally there should be no need to decode secrets back, but garage wants both
+	/// raw pubkey and raw secret key, yet also requires node id which is hex-reencoded public key.
+	Decode {
+		#[arg(long, short = 'i')]
+		input: String,
+	},
 	/// Generate keys in well-known schemas.
 	///
 	/// Note that this command is only intended to be used in fleet secret generator,
@@ -190,11 +219,11 @@
 
 	match opts {
 		Opts::Public { output, encoding } => {
-			write_public(&output, std::io::stdin(), encoding)?;
+			write_public(&output, stdin(), encoding)?;
 		}
 		Opts::Private { output, encoding } => {
 			let recipients = load_identities()?;
-			write_private(&recipients, &output, std::io::stdin(), encoding)?;
+			write_private(&recipients, &output, stdin(), encoding)?;
 		}
 		Opts::Generate(gen) => {
 			match gen {
@@ -204,8 +233,10 @@
 					no_embed_public,
 					encoding,
 				} => {
+					use ed25519_dalek::SigningKey;
+
 					let recipients = load_identities()?;
-					let key = ed25519_dalek::SigningKey::generate(&mut rng).to_keypair_bytes();
+					let key = SigningKey::generate(&mut rng).to_keypair_bytes();
 					write_public(&public, &key[32..], encoding)?;
 					write_private(
 						&recipients,
@@ -225,9 +256,11 @@
 					private,
 					encoding,
 				} => {
+					use x25519_dalek::{PublicKey, StaticSecret};
+
 					let recipients = load_identities()?;
-					let key = x25519_dalek::StaticSecret::random_from_rng(rng);
-					let public_key: x25519_dalek::PublicKey = (&key).into();
+					let key = StaticSecret::random_from_rng(rng);
+					let public_key: PublicKey = (&key).into();
 					write_public(&public, public_key.as_bytes().as_slice(), encoding)?;
 					write_private(&recipients, &private, key.as_bytes().as_slice(), encoding)?;
 				}
@@ -257,6 +290,20 @@
 				}
 			}
 		}
+		Opts::Decode { input } => {
+			let mut data = Vec::new();
+			File::open(input)?.read_to_end(&mut data)?;
+			let data = String::from_utf8(data).context(
+				"encoded data is always utf-8, you are trying to use decode the wrong way.",
+			)?;
+			let data =
+				SecretData::from_str(&data).map_err(|e| anyhow!("failed to decode data: {e}"))?;
+			ensure!(
+				!data.encrypted,
+				"you can not decrypt secret data, only decode public."
+			);
+			stdout().write_all(&data.data)?;
+		}
 	}
 	Ok(())
 }