mirror of
https://github.com/matter-labs/teepot.git
synced 2025-07-22 15:34:48 +02:00
feat: initial commit
Signed-off-by: Harald Hoyer <harald@matterlabs.dev>
This commit is contained in:
parent
aff4dd30bd
commit
89ffbd35a8
123 changed files with 16508 additions and 0 deletions
22
bin/vault-unseal/Cargo.toml
Normal file
22
bin/vault-unseal/Cargo.toml
Normal file
|
@ -0,0 +1,22 @@
|
|||
[package]
|
||||
name = "vault-unseal"
|
||||
version.workspace = true
|
||||
edition.workspace = true
|
||||
authors.workspace = true
|
||||
license.workspace = true
|
||||
repository.workspace = true
|
||||
|
||||
[dependencies]
|
||||
actix-web.workspace = true
|
||||
anyhow.workspace = true
|
||||
awc.workspace = true
|
||||
base64.workspace = true
|
||||
clap.workspace = true
|
||||
hex.workspace = true
|
||||
serde.workspace = true
|
||||
serde_json.workspace = true
|
||||
sha2.workspace = true
|
||||
teepot.workspace = true
|
||||
tracing-log.workspace = true
|
||||
tracing-subscriber.workspace = true
|
||||
tracing.workspace = true
|
220
bin/vault-unseal/src/main.rs
Normal file
220
bin/vault-unseal/src/main.rs
Normal file
|
@ -0,0 +1,220 @@
|
|||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright (c) 2023 Matter Labs
|
||||
|
||||
use anyhow::{anyhow, bail, Context, Result};
|
||||
use base64::{engine::general_purpose, Engine as _};
|
||||
use clap::{Args, Parser, Subcommand};
|
||||
use serde_json::Value;
|
||||
use std::fs::File;
|
||||
use std::io::Read;
|
||||
use teepot::client::{AttestationArgs, TeeConnection};
|
||||
use teepot::json::http::{Init, InitResponse, Unseal, ATTESTATION_URL};
|
||||
use tracing::{error, info, trace, warn};
|
||||
use tracing_log::LogTracer;
|
||||
use tracing_subscriber::Registry;
|
||||
use tracing_subscriber::{fmt, prelude::*, EnvFilter};
|
||||
|
||||
#[derive(Args, Debug)]
|
||||
pub struct InitArgs {
|
||||
/// admin threshold
|
||||
#[arg(long)]
|
||||
admin_threshold: usize,
|
||||
/// PGP keys to sign commands for the admin tee
|
||||
#[arg(short, long)]
|
||||
admin_pgp_key_file: Vec<String>,
|
||||
/// admin TEE mrenclave
|
||||
#[arg(long)]
|
||||
admin_tee_mrenclave: String,
|
||||
/// secret threshold
|
||||
#[arg(long)]
|
||||
unseal_threshold: usize,
|
||||
/// PGP keys to encrypt the unseal keys with
|
||||
#[arg(short, long)]
|
||||
unseal_pgp_key_file: Vec<String>,
|
||||
}
|
||||
|
||||
/// subcommands and their options/arguments.
|
||||
#[derive(Subcommand, Debug)]
|
||||
enum SubCommands {
|
||||
Init(InitArgs),
|
||||
Unseal,
|
||||
}
|
||||
|
||||
#[derive(Parser, Debug)]
|
||||
#[command(author, version, about, long_about = None)]
|
||||
struct Arguments {
|
||||
#[clap(flatten)]
|
||||
pub attestation: AttestationArgs,
|
||||
/// Subcommands (with their own options)
|
||||
#[clap(subcommand)]
|
||||
cmd: SubCommands,
|
||||
}
|
||||
|
||||
#[actix_web::main]
|
||||
async fn main() -> Result<()> {
|
||||
LogTracer::init().context("Failed to set logger")?;
|
||||
|
||||
let subscriber = Registry::default()
|
||||
.with(EnvFilter::from_default_env())
|
||||
.with(fmt::layer().with_writer(std::io::stderr));
|
||||
tracing::subscriber::set_global_default(subscriber).unwrap();
|
||||
|
||||
let args = Arguments::parse();
|
||||
|
||||
match args.cmd {
|
||||
SubCommands::Init(_) => init(args).await?,
|
||||
SubCommands::Unseal => unseal(args).await?,
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn init(args: Arguments) -> Result<()> {
|
||||
let conn = TeeConnection::new(&args.attestation, ATTESTATION_URL).await?;
|
||||
|
||||
info!("Quote verified! Connection secure!");
|
||||
|
||||
let SubCommands::Init(init_args) = args.cmd else {
|
||||
unreachable!()
|
||||
};
|
||||
|
||||
if init_args.admin_threshold == 0 {
|
||||
bail!("admin threshold must be greater than 0");
|
||||
}
|
||||
|
||||
if init_args.unseal_threshold == 0 {
|
||||
bail!("unseal threshold must be greater than 0");
|
||||
}
|
||||
|
||||
if init_args.admin_threshold > init_args.admin_pgp_key_file.len() {
|
||||
bail!("admin threshold must be less than or equal to the number of admin pgp keys");
|
||||
}
|
||||
|
||||
if init_args.unseal_threshold > init_args.unseal_pgp_key_file.len() {
|
||||
bail!("unseal threshold must be less than or equal to the number of unseal pgp keys");
|
||||
}
|
||||
|
||||
let mut pgp_keys = Vec::new();
|
||||
|
||||
for filename in init_args.unseal_pgp_key_file {
|
||||
let mut file =
|
||||
File::open(&filename).context(format!("Failed to open pgp key file {}", &filename))?;
|
||||
let mut buf = Vec::new();
|
||||
file.read_to_end(&mut buf)?;
|
||||
let key = std::str::from_utf8(&buf)?.trim().to_string();
|
||||
pgp_keys.push(key);
|
||||
}
|
||||
|
||||
let mut admin_pgp_keys = Vec::new();
|
||||
|
||||
for filename in init_args.admin_pgp_key_file {
|
||||
let mut file =
|
||||
File::open(&filename).context(format!("Failed to open pgp key file {}", &filename))?;
|
||||
// read all lines from file and concatenate them
|
||||
let mut key = String::new();
|
||||
file.read_to_string(&mut key)
|
||||
.context(format!("Failed to read pgp key file {}", &filename))?;
|
||||
key.retain(|c| !c.is_ascii_whitespace());
|
||||
|
||||
let bytes = general_purpose::STANDARD.decode(key).context(format!(
|
||||
"Failed to base64 decode pgp key file {}",
|
||||
&filename
|
||||
))?;
|
||||
admin_pgp_keys.push(bytes.into_boxed_slice());
|
||||
}
|
||||
|
||||
let init = Init {
|
||||
secret_shares: pgp_keys.len() as _,
|
||||
secret_threshold: init_args.unseal_threshold,
|
||||
admin_threshold: init_args.admin_threshold,
|
||||
admin_tee_mrenclave: init_args.admin_tee_mrenclave,
|
||||
admin_pgp_keys: admin_pgp_keys.into_boxed_slice(),
|
||||
pgp_keys,
|
||||
};
|
||||
|
||||
info!("Inititalizing vault");
|
||||
|
||||
let mut response = conn
|
||||
.client()
|
||||
.post(&format!(
|
||||
"{server}{url}",
|
||||
server = conn.server(),
|
||||
url = Init::URL
|
||||
))
|
||||
.send_json(&init)
|
||||
.await
|
||||
.map_err(|e| anyhow!("Error sending init request: {}", e))?;
|
||||
|
||||
let status_code = response.status();
|
||||
if !status_code.is_success() {
|
||||
error!("Failed to init vault: {}", status_code);
|
||||
if let Ok(r) = response.json::<Value>().await {
|
||||
eprintln!("Failed to init vault: {}", r);
|
||||
}
|
||||
bail!("failed to init vault: {}", status_code);
|
||||
}
|
||||
|
||||
let init_response: Value = response.json().await.context("failed to init vault")?;
|
||||
|
||||
info!("Got Response: {}", init_response.to_string());
|
||||
|
||||
let resp: InitResponse =
|
||||
serde_json::from_value(init_response).context("Failed to parse init response")?;
|
||||
println!("{}", serde_json::to_string(&resp).unwrap());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn unseal(args: Arguments) -> Result<()> {
|
||||
info!("Reading unencrypted key from stdin");
|
||||
|
||||
// read all bytes from stdin
|
||||
let mut stdin = std::io::stdin();
|
||||
let mut buf = Vec::new();
|
||||
stdin.read_to_end(&mut buf)?;
|
||||
let key = std::str::from_utf8(&buf)?.trim().to_string();
|
||||
|
||||
if key.is_empty() {
|
||||
bail!("Error reading key from stdin");
|
||||
}
|
||||
|
||||
let conn = TeeConnection::new(&args.attestation, ATTESTATION_URL).await?;
|
||||
|
||||
info!("Quote verified! Connection secure!");
|
||||
|
||||
info!("Unsealing vault");
|
||||
|
||||
let unseal_data = Unseal { key };
|
||||
|
||||
let mut response = conn
|
||||
.client()
|
||||
.post(&format!(
|
||||
"{server}{url}",
|
||||
server = conn.server(),
|
||||
url = Unseal::URL
|
||||
))
|
||||
.send_json(&unseal_data)
|
||||
.await
|
||||
.map_err(|e| anyhow!("Error sending unseal request: {}", e))?;
|
||||
|
||||
let status_code = response.status();
|
||||
if !status_code.is_success() {
|
||||
error!("Failed to unseal vault: {}", status_code);
|
||||
if let Ok(r) = response.json::<Value>().await {
|
||||
eprintln!("Failed to unseal vault: {}", r);
|
||||
}
|
||||
bail!("failed to unseal vault: {}", status_code);
|
||||
}
|
||||
|
||||
let unseal_response: Value = response.json().await.context("failed to unseal vault")?;
|
||||
|
||||
trace!("Got Response: {}", unseal_response.to_string());
|
||||
|
||||
if matches!(unseal_response["sealed"].as_bool(), Some(true)) {
|
||||
warn!("Vault is still sealed!");
|
||||
println!("Vault is still sealed!");
|
||||
} else {
|
||||
info!("Vault is unsealed!");
|
||||
println!("Vault is unsealed!");
|
||||
}
|
||||
Ok(())
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue