From a8a3d4e11a8716406e6c1096ee595417df1aa7da Mon Sep 17 00:00:00 2001 From: Harald Hoyer Date: Tue, 6 Aug 2024 09:37:30 +0200 Subject: [PATCH] feat: WIP new multisig threshold scheme with a tree like structure allowing nested m of n schemes. Signed-off-by: Harald Hoyer --- Cargo.lock | 108 ++- Cargo.toml | 1 + bin/tee-vault-admin/Cargo.toml | 3 + bin/tee-vault-admin/src/command.rs | 8 +- bin/tee-vault-admin/src/main.rs | 1 + bin/tee-vault-admin/src/sign.rs | 7 +- bin/tee-vault-unseal/src/init.rs | 26 +- bin/vault-unseal/Cargo.toml | 1 - bin/vault-unseal/src/main.rs | 37 +- crates/teepot/Cargo.toml | 2 +- crates/teepot/src/json/http.rs | 8 +- crates/teepot/src/json/secrets.rs | 23 +- crates/teepot/src/server/signatures.rs | 284 +++++-- crates/teepot/src/sgx/sign.rs | 3 +- ...3D64824AC0B6B8009E50504BC0896FB5693595.asc | 734 ++++++++++++++++++ ...A312C59D679D930FA9E8B06D728F29A2DBABF8.b64 | 16 - crates/teepot/tests/data/test.json.asc | 34 +- crates/teepot/tests/data/test.json_2.asc | 16 + crates/teepot/tests/sgx_quote_verification.rs | 1 + 19 files changed, 1139 insertions(+), 174 deletions(-) create mode 100644 crates/teepot/tests/data/pub-7F3D64824AC0B6B8009E50504BC0896FB5693595.asc delete mode 100644 crates/teepot/tests/data/pub-81A312C59D679D930FA9E8B06D728F29A2DBABF8.b64 create mode 100644 crates/teepot/tests/data/test.json_2.asc diff --git a/Cargo.lock b/Cargo.lock index 9921303..d22324a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -310,12 +310,55 @@ dependencies = [ "libc", ] +[[package]] +name = "anstream" +version = "0.6.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64e15c1ab1f89faffbf04a634d5e1962e9074f2741eef6d97f3c4e322426d526" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + [[package]] name = "anstyle" version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1" +[[package]] +name = "anstyle-parse" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb47de1e80c2b463c735db5b217a0ddc39d612e7ac9e2e96a5aed1f57616c1cb" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d36fc52c7f6c869915e99412912f22093507da8d9e942ceaf66fe4b7c14422a" +dependencies = [ + "windows-sys 0.52.0", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5bf74e1b6e971609db8ca7a9ce79fd5768ab6ae46441c572e46cf596f59e57f8" +dependencies = [ + "anstyle", + "windows-sys 0.52.0", +] + [[package]] name = "anyhow" version = "1.0.86" @@ -735,6 +778,12 @@ dependencies = [ "digest", ] +[[package]] +name = "colorchoice" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0" + [[package]] name = "const-oid" version = "0.9.6" @@ -1156,6 +1205,27 @@ dependencies = [ "syn 2.0.72", ] +[[package]] +name = "env_filter" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f2c92ceda6ceec50f43169f9ee8424fe2db276791afde7b2cd8bc084cb376ab" +dependencies = [ + "log", +] + +[[package]] +name = "env_logger" +version = "0.11.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13fa619b91fb2381732789fc5de83b45675e882f66623b7d8cb4f643017018d" +dependencies = [ + "anstream", + "anstyle", + "env_filter", + "log", +] + [[package]] name = "equivalent" version = "1.0.1" @@ -1615,6 +1685,12 @@ dependencies = [ "bindgen", ] +[[package]] +name = "is_terminal_polyfill" +version = "1.70.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" + [[package]] name = "iter-read" version = "1.0.1" @@ -2926,6 +3002,7 @@ dependencies = [ "serde_json", "sha2", "teepot", + "test-log", "tracing", "tracing-actix-web", "tracing-log", @@ -2956,7 +3033,6 @@ dependencies = [ "actix-web", "anyhow", "awc", - "base64", "bytemuck", "bytes", "clap", @@ -2979,6 +3055,7 @@ dependencies = [ "sha2", "signature", "teepot-tee-quote-verification-rs", + "test-log", "testaso", "thiserror", "tracing", @@ -3035,6 +3112,28 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "test-log" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3dffced63c2b5c7be278154d76b479f9f9920ed34e7574201407f0b14e2bbb93" +dependencies = [ + "env_logger", + "test-log-macros", + "tracing-subscriber", +] + +[[package]] +name = "test-log-macros" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5999e24eaa32083191ba4e425deb75cdf25efefabe5aaccb7446dd0d4122a3f5" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.72", +] + [[package]] name = "testaso" version = "0.1.0" @@ -3356,6 +3455,12 @@ dependencies = [ "serde", ] +[[package]] +name = "utf8parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + [[package]] name = "uuid" version = "1.10.0" @@ -3394,7 +3499,6 @@ version = "0.1.2-alpha.1" dependencies = [ "actix-web", "anyhow", - "base64", "clap", "serde_json", "teepot", diff --git a/Cargo.toml b/Cargo.toml index 6ba37b6..1a36b2d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -54,6 +54,7 @@ sha2 = "0.10.8" signature = "2.2.0" teepot = { path = "crates/teepot" } testaso = "0.1.0" +test-log = "0.2.16" thiserror = "1.0.59" tokio = { version = "1", features = ["sync", "macros", "rt-multi-thread", "fs", "time"] } tracing = "0.1" diff --git a/bin/tee-vault-admin/Cargo.toml b/bin/tee-vault-admin/Cargo.toml index 7233679..e06392e 100644 --- a/bin/tee-vault-admin/Cargo.toml +++ b/bin/tee-vault-admin/Cargo.toml @@ -21,3 +21,6 @@ tracing.workspace = true tracing-actix-web.workspace = true tracing-log.workspace = true tracing-subscriber.workspace = true + +[dev-dependencies] +test-log.workspace = true diff --git a/bin/tee-vault-admin/src/command.rs b/bin/tee-vault-admin/src/command.rs index 607bca3..3bdd4b4 100644 --- a/bin/tee-vault-admin/src/command.rs +++ b/bin/tee-vault-admin/src/command.rs @@ -1,5 +1,5 @@ // SPDX-License-Identifier: Apache-2.0 -// Copyright (c) 2023 Matter Labs +// Copyright (c) 2023-2024 Matter Labs //! post commands @@ -14,7 +14,7 @@ use teepot::json::http::{ VaultCommandRequest, VaultCommandResponse, VaultCommands, VaultCommandsResponse, }; use teepot::json::secrets::{AdminConfig, AdminState}; -use teepot::server::{signatures::VerifySig, HttpResponseError, Status}; +use teepot::server::{HttpResponseError, Status}; use tracing::instrument; /// Post command @@ -52,7 +52,9 @@ pub async fn post_command( .await? .context("empty admin config") .status(StatusCode::BAD_GATEWAY)?; - admin_config.check_sigs(&item.signatures, item.commands.as_bytes())?; + admin_config + .policy + .check_sigs(&item.signatures, item.commands.as_bytes())?; let mut hasher = Sha256::new(); hasher.update(item.commands.as_bytes()); diff --git a/bin/tee-vault-admin/src/main.rs b/bin/tee-vault-admin/src/main.rs index 9cd54f8..d31288a 100644 --- a/bin/tee-vault-admin/src/main.rs +++ b/bin/tee-vault-admin/src/main.rs @@ -116,6 +116,7 @@ async fn main() -> Result<()> { mod tests { use serde_json::json; use teepot::json::http::{VaultCommand, VaultCommands}; + use test_log::test; const TEST_DATA: &str = include_str!("../../../crates/teepot/tests/data/test.json"); diff --git a/bin/tee-vault-admin/src/sign.rs b/bin/tee-vault-admin/src/sign.rs index 973df93..accd443 100644 --- a/bin/tee-vault-admin/src/sign.rs +++ b/bin/tee-vault-admin/src/sign.rs @@ -1,5 +1,5 @@ // SPDX-License-Identifier: Apache-2.0 -// Copyright (c) 2023 Matter Labs +// Copyright (c) 2023-2024 Matter Labs //! post signing request @@ -12,7 +12,6 @@ use std::sync::Arc; use teepot::client::vault::VaultConnection; use teepot::json::http::{SignRequest, SignRequestData, SignResponse}; use teepot::json::secrets::{AdminConfig, AdminState, SGXSigningKey}; -use teepot::server::signatures::VerifySig as _; use teepot::server::{HttpResponseError, Status}; use teepot::sgx::sign::PrivateKey as _; use teepot::sgx::sign::{Author, Signature}; @@ -76,7 +75,9 @@ pub async fn post_sign( .await? .context("empty admin config") .status(StatusCode::BAD_GATEWAY)?; - admin_config.check_sigs(&item.signatures, item.sign_request_data.as_bytes())?; + admin_config + .policy + .check_sigs(&item.signatures, item.sign_request_data.as_bytes())?; let mut hasher = Sha256::new(); hasher.update(item.sign_request_data.as_bytes()); diff --git a/bin/tee-vault-unseal/src/init.rs b/bin/tee-vault-unseal/src/init.rs index 302c5fa..2de42e9 100644 --- a/bin/tee-vault-unseal/src/init.rs +++ b/bin/tee-vault-unseal/src/init.rs @@ -2,14 +2,11 @@ // Copyright (c) 2023-2024 Matter Labs use crate::{get_vault_status, UnsealServerState, Worker}; -use actix_web::error::ErrorBadRequest; use actix_web::{web, HttpResponse}; use anyhow::{anyhow, Context, Result}; use awc::http::StatusCode; -use serde_json::json; use teepot::client::TeeConnection; use teepot::json::http::{Init, InitResponse, VaultInitRequest}; -use teepot::json::secrets::AdminConfig; use teepot::server::{HttpResponseError, Status}; use tracing::{debug, error, info, instrument, trace}; @@ -22,8 +19,7 @@ pub async fn post_init( pgp_keys, secret_shares, secret_threshold, - admin_pgp_keys, - admin_threshold, + admin_config, admin_tee_mrenclave, } = init.into_inner(); let conn = TeeConnection::new(&worker.vault_attestation); @@ -36,17 +32,10 @@ pub async fn post_init( secret_threshold, }; - if admin_threshold < 1 { - return Ok(HttpResponse::from_error(ErrorBadRequest( - json!({"error": "admin_threshold must be at least 1"}), - ))); - } - - if admin_threshold > admin_pgp_keys.len() { - return Ok(HttpResponse::from_error(ErrorBadRequest( - json!({"error": "admin_threshold must be less than or equal to the number of admin_pgp_keys"}), - ))); - } + admin_config + .validate() + .context("Invalid admin config") + .status(StatusCode::BAD_REQUEST)?; loop { let current_state = worker.state.read().unwrap().clone(); @@ -123,10 +112,7 @@ pub async fn post_init( */ *worker.state.write().unwrap() = UnsealServerState::VaultInitialized { - admin_config: AdminConfig { - admin_pgp_keys, - admin_threshold, - }, + admin_config, admin_tee_mrenclave, root_token, }; diff --git a/bin/vault-unseal/Cargo.toml b/bin/vault-unseal/Cargo.toml index 03eafc7..8f6ab53 100644 --- a/bin/vault-unseal/Cargo.toml +++ b/bin/vault-unseal/Cargo.toml @@ -9,7 +9,6 @@ repository.workspace = true [dependencies] actix-web.workspace = true anyhow.workspace = true -base64.workspace = true clap.workspace = true serde_json.workspace = true teepot.workspace = true diff --git a/bin/vault-unseal/src/main.rs b/bin/vault-unseal/src/main.rs index 29e2bc7..6183cf2 100644 --- a/bin/vault-unseal/src/main.rs +++ b/bin/vault-unseal/src/main.rs @@ -2,13 +2,13 @@ // Copyright (c) 2023-2024 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}; +use teepot::json::secrets::AdminConfig; use tracing::{error, info, trace, warn}; use tracing_log::LogTracer; use tracing_subscriber::Registry; @@ -16,12 +16,9 @@ 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, + admin_config_json: String, /// admin TEE mrenclave #[arg(long)] admin_tee_mrenclave: String, @@ -78,17 +75,12 @@ async fn init(args: Arguments) -> Result<()> { 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"); - } + let admin_config: AdminConfig = serde_json::from_str(&init_args.admin_config_json) + .context("failed to parse admin config")?; 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"); @@ -105,30 +97,11 @@ async fn init(args: Arguments) -> Result<()> { 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(), + admin_config, pgp_keys, }; diff --git a/crates/teepot/Cargo.toml b/crates/teepot/Cargo.toml index a2c48bf..dbec3c5 100644 --- a/crates/teepot/Cargo.toml +++ b/crates/teepot/Cargo.toml @@ -45,6 +45,6 @@ zeroize.workspace = true [dev-dependencies] anyhow.workspace = true -base64.workspace = true hex.workspace = true +test-log.workspace = true testaso.workspace = true diff --git a/crates/teepot/src/json/http.rs b/crates/teepot/src/json/http.rs index d450906..4717dfe 100644 --- a/crates/teepot/src/json/http.rs +++ b/crates/teepot/src/json/http.rs @@ -3,6 +3,7 @@ //! Common types for the teepot http JSON API +use crate::json::secrets::AdminConfig; use crate::sgx::Collateral; use serde::{Deserialize, Serialize}; use serde_json::Value; @@ -42,11 +43,8 @@ pub struct Init { pub secret_shares: usize, /// secret threshold pub secret_threshold: usize, - /// PGP keys to sign commands for the admin tee - #[serde_as(as = "Box<[Base64]>")] - pub admin_pgp_keys: Box<[Box<[u8]>]>, - /// admin threshold - pub admin_threshold: usize, + /// The m of n admin config + pub admin_config: AdminConfig, /// admin TEE mrenclave pub admin_tee_mrenclave: String, } diff --git a/crates/teepot/src/json/secrets.rs b/crates/teepot/src/json/secrets.rs index dd1fae5..c0db49c 100644 --- a/crates/teepot/src/json/secrets.rs +++ b/crates/teepot/src/json/secrets.rs @@ -1,22 +1,25 @@ // SPDX-License-Identifier: Apache-2.0 -// Copyright (c) 2023 Matter Labs +// Copyright (c) 2023-2024 Matter Labs //! Common types for the teepot secrets JSON API +use crate::server::signatures::MultiSigPolicy; use crate::sgx::sign::Zeroizing; +use anyhow::Result; use serde::{Deserialize, Serialize}; -use serde_with::base64::Base64; -use serde_with::serde_as; /// Configuration for the admin tee -#[serde_as] -#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize)] pub struct AdminConfig { - /// PGP keys to sign commands for the admin tee - #[serde_as(as = "Box<[Base64]>")] - pub admin_pgp_keys: Box<[Box<[u8]>]>, - /// admin threshold - pub admin_threshold: usize, + /// admin signature policy + pub policy: MultiSigPolicy, +} + +impl AdminConfig { + /// validate the configuration + pub fn validate(&self) -> Result<()> { + self.policy.validate() + } } /// Configuration for the admin tee diff --git a/crates/teepot/src/server/signatures.rs b/crates/teepot/src/server/signatures.rs index e91aa93..380fec5 100644 --- a/crates/teepot/src/server/signatures.rs +++ b/crates/teepot/src/server/signatures.rs @@ -1,15 +1,61 @@ // SPDX-License-Identifier: Apache-2.0 -// Copyright (c) 2023 Matter Labs +// Copyright (c) 2023-2024 Matter Labs //! Signature checking utilities -use crate::json::secrets::AdminConfig; use crate::server::{HttpResponseError, Status as _}; use actix_web::http::StatusCode; use anyhow::{anyhow, bail, Context, Result}; use pgp::types::KeyTrait; use pgp::{Deserializable, SignedPublicKey, StandaloneSignature}; -use tracing::debug; +use serde::{Deserialize, Serialize}; +use tracing::{error, trace}; + +impl MultiSigPolicy { + /// validate the policy + pub fn validate(&self) -> Result<()> { + if self.threshold == 0 { + bail!("admin_threshold must be greater than 0"); + } + if self.members.is_empty() { + bail!("validation elements must not be empty"); + } + if self.threshold > self.members.len() { + bail!("threshold must be smaller than number of elements"); + } + for ele in self.members.iter() { + match ele { + KeyOrChilds::Key(key) => { + SignedPublicKey::from_string(key)?; + } + KeyOrChilds::Child(child) => { + child.validate()?; + } + } + } + Ok(()) + } +} + +/// M of N Signature Policy +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct MultiSigPolicy { + /// Name of this level + pub name: String, + /// Array of PGP key for validation or Self + pub members: Box<[KeyOrChilds]>, + /// Threshold for validation + pub threshold: usize, +} + +/// A m of n child can be a m of n KeyOrChilds or a key +#[derive(Debug, Clone, Serialize, Deserialize)] +pub enum KeyOrChilds { + /// a key + Key(String), + /// a sub m of n + Child(MultiSigPolicy), +} /// Verify a pgp signature for some message given some public keys pub fn verify_sig(sig: &str, msg: &[u8], keys: &[SignedPublicKey]) -> anyhow::Result { @@ -20,90 +66,116 @@ pub fn verify_sig(sig: &str, msg: &[u8], keys: &[SignedPublicKey]) -> anyhow::Re let signature = match signature { Ok(s) => s, Err(e) => { - debug!("Failed to parse signature: {}", e); + error!("Failed to parse signature: {}", e); continue; } }; for (pos, key) in keys.iter().enumerate() { let actual_key = &key.primary_key; - if actual_key.is_signing_key() && signature.verify(&actual_key, msg).is_ok() { - return Ok(pos); - } - for sub_key in &key.public_subkeys { - if sub_key.is_signing_key() && signature.verify(sub_key, msg).is_ok() { + if actual_key.is_signing_key() { + trace!( + "Checking with key {}", + hex::encode(actual_key.fingerprint()) + ); + if signature.verify(&actual_key, msg).is_ok() { + trace!( + "Verified with key {}", + hex::encode(actual_key.fingerprint()) + ); return Ok(pos); } } + for actual_key in &key.public_subkeys { + if actual_key.is_signing_key() { + trace!( + "Checking with subkey {}", + hex::encode(actual_key.fingerprint()) + ); + if signature.verify(actual_key, msg).is_ok() { + trace!( + "Verified with key {}", + hex::encode(actual_key.fingerprint()) + ); + return Ok(pos); + } + } + } } } - eprintln!("Failed to verify signature for `{sig}`"); + trace!("Failed to verify signature for `{sig}`"); bail!("Failed to verify signature for `{sig}`"); } -/// Verify pgp signatures for a message with some threshold -pub fn check_sigs( - pgp_keys: &[Box<[u8]>], - threshold: usize, - signatures: &[String], - msg: &[u8], -) -> Result<(), HttpResponseError> { - let mut keys = Vec::new(); - - for bytes in pgp_keys { - let key = SignedPublicKey::from_bytes(bytes.as_ref()) - .context("parsing public key") - .status(StatusCode::INTERNAL_SERVER_ERROR)?; - keys.push(key); - } +impl MultiSigPolicy { + /// Verify pgp signatures for a message with the current policy + pub fn check_sigs(&self, signatures: &[String], msg: &[u8]) -> Result<(), HttpResponseError> { + fn inner_check_sigs( + admin_config: &MultiSigPolicy, + signatures: &[String], + msg: &[u8], + ) -> Result<(), HttpResponseError> { + let mut keys = Vec::new(); + let mut verified: usize = 0; - let mut verified: usize = 0; - - for sig in signatures { - if let Ok(pos) = verify_sig(sig, msg, &keys) { - keys.remove(pos); - verified += 1; - } - if verified >= threshold { - break; - } - } + for key_or_child in admin_config.members.as_ref() { + match key_or_child { + KeyOrChilds::Key(key) => { + // This is not a performance critical path, so we can import from bytes every time + let (key, _) = SignedPublicKey::from_string(key) + .context("parsing public key") + .status(StatusCode::INTERNAL_SERVER_ERROR)?; + keys.push(key); + } + KeyOrChilds::Child(child) => { + if inner_check_sigs(child, signatures, msg).is_ok() { + verified += 1; + } + } + } + } - if verified < threshold { - return Err(anyhow!("not enough valid signatures")).status(StatusCode::BAD_REQUEST); - } - Ok(()) -} + if verified < admin_config.threshold { + for sig in signatures { + if let Ok(pos) = verify_sig(sig, msg, &keys) { + keys.remove(pos); + verified += 1; + } + if verified >= admin_config.threshold { + break; + } + } + } -/// Verify pgp signatures for a message -pub trait VerifySig { - /// Verify pgp signatures for a message - fn check_sigs(&self, signatures: &[String], msg: &[u8]) -> Result<(), HttpResponseError>; -} + if verified < admin_config.threshold { + return Err(anyhow!("not enough valid signatures")).status(StatusCode::BAD_REQUEST); + } + Ok(()) + } -impl VerifySig for AdminConfig { - fn check_sigs(&self, signatures: &[String], msg: &[u8]) -> Result<(), HttpResponseError> { - check_sigs(&self.admin_pgp_keys, self.admin_threshold, signatures, msg) + inner_check_sigs(self, signatures, msg) } } #[cfg(test)] mod tests { - use super::verify_sig; - use base64::{engine::general_purpose, Engine as _}; + use super::*; use pgp::{Deserializable, SignedPublicKey}; + use test_log::test; const TEST_DATA: &str = include_str!("../../tests/data/test.json"); // gpg --armor --local-user test@example.com --detach-sign bin/tee-vault-admin/tests/data/test.json const TEST_SIG: &str = include_str!("../../tests/data/test.json.asc"); + const TEST_SIG_2: &str = include_str!("../../tests/data/test.json_2.asc"); + // gpg --armor --export 81A312C59D679D930FA9E8B06D728F29A2DBABF8 > bin/tee-vault-admin/tests/data/pub-81A312C59D679D930FA9E8B06D728F29A2DBABF8.asc const TEST_KEY: &str = include_str!("../../tests/data/pub-81A312C59D679D930FA9E8B06D728F29A2DBABF8.asc"); - const TEST_KEY_BASE64: &str = - include_str!("../../tests/data/pub-81A312C59D679D930FA9E8B06D728F29A2DBABF8.b64"); + const TEST_KEY_2: &str = + include_str!("../../tests/data/pub-7F3D64824AC0B6B8009E50504BC0896FB5693595.asc"); #[test] fn test_sig() { @@ -112,9 +184,107 @@ mod tests { } #[test] - fn test_key_import() { - let str = TEST_KEY_BASE64.lines().collect::(); - let bytes = general_purpose::STANDARD.decode(str).unwrap(); - let _ = SignedPublicKey::from_bytes(bytes.as_slice()).unwrap(); + fn test_multisig() { + assert!(MultiSigPolicy { + name: "test".to_string(), + members: vec![KeyOrChilds::Key("test".into())].into_boxed_slice(), + threshold: 0, + } + .validate() + .is_err()); + + assert!(MultiSigPolicy { + name: "test".to_string(), + members: vec![KeyOrChilds::Key("test".into())].into_boxed_slice(), + threshold: 2, + } + .validate() + .is_err()); + + assert!(MultiSigPolicy { + name: "test".to_string(), + members: vec![KeyOrChilds::Key("test".into()),].into_boxed_slice(), + threshold: 1, + } + .validate() + .is_err()); + + let policy = MultiSigPolicy { + name: "test".to_string(), + members: vec![ + KeyOrChilds::Key(TEST_KEY_2.into()), + KeyOrChilds::Key(TEST_KEY.into()), + ] + .into_boxed_slice(), + threshold: 1, + }; + + assert!(policy.validate().is_ok()); + + policy + .check_sigs(&[TEST_SIG.into()], TEST_DATA.as_bytes()) + .unwrap(); + + policy + .check_sigs(&[TEST_SIG_2.into()], TEST_DATA.as_bytes()) + .unwrap(); + + policy + .check_sigs(&[TEST_SIG.into(), TEST_SIG_2.into()], TEST_DATA.as_bytes()) + .unwrap(); + + let policy = MultiSigPolicy { + name: "test".to_string(), + members: vec![ + KeyOrChilds::Key(TEST_KEY_2.into()), + KeyOrChilds::Key(TEST_KEY.into()), + ] + .into_boxed_slice(), + threshold: 2, + }; + + assert!(policy.validate().is_ok()); + + assert!(policy + .check_sigs(&[TEST_SIG.into()], TEST_DATA.as_bytes()) + .is_err()); + + assert!(policy + .check_sigs(&[TEST_SIG_2.into()], TEST_DATA.as_bytes()) + .is_err()); + + policy + .check_sigs(&[TEST_SIG.into(), TEST_SIG_2.into()], TEST_DATA.as_bytes()) + .unwrap(); + + let policy = MultiSigPolicy { + name: "test".to_string(), + members: vec![ + KeyOrChilds::Child(MultiSigPolicy { + name: "teamA".to_string(), + members: vec![KeyOrChilds::Key(TEST_KEY.into())].into_boxed_slice(), + threshold: 1, + }), + KeyOrChilds::Child(MultiSigPolicy { + name: "teamB".to_string(), + members: vec![KeyOrChilds::Key(TEST_KEY_2.into())].into_boxed_slice(), + threshold: 1, + }), + ] + .into_boxed_slice(), + threshold: 2, + }; + + assert!(policy.validate().is_ok()); + + assert!(policy + .check_sigs(&[TEST_SIG.into()], TEST_DATA.as_bytes()) + .is_err()); + + policy + .check_sigs(&[TEST_SIG.into(), TEST_SIG_2.into()], TEST_DATA.as_bytes()) + .unwrap(); + + assert!(policy.validate().is_ok()); } } diff --git a/crates/teepot/src/sgx/sign.rs b/crates/teepot/src/sgx/sign.rs index 7b58f43..7da5d12 100644 --- a/crates/teepot/src/sgx/sign.rs +++ b/crates/teepot/src/sgx/sign.rs @@ -316,8 +316,9 @@ impl PrivateKey for RS256PrivateKey { } #[cfg(test)] -mod test { +mod tests { use super::{Author, Body, Signature}; + use test_log::test; use testaso::testaso; testaso! { diff --git a/crates/teepot/tests/data/pub-7F3D64824AC0B6B8009E50504BC0896FB5693595.asc b/crates/teepot/tests/data/pub-7F3D64824AC0B6B8009E50504BC0896FB5693595.asc new file mode 100644 index 0000000..4a8d00f --- /dev/null +++ b/crates/teepot/tests/data/pub-7F3D64824AC0B6B8009E50504BC0896FB5693595.asc @@ -0,0 +1,734 @@ +-----BEGIN PGP PUBLIC KEY BLOCK----- + +mQINBF586L8BEADxCazcu1Aetijsryp7+BDHMe2CipKcLk9h/DGxW1Bu+jLxJkDN +PPIS5v8AZbQbRzqfaiK0rnpxuhk4iEAKQUxuh6wzgzqshMgB2n+wqobpYqtSn6Um +WQHqspydRj5w1rxV5ikxL/3MCAhZ/FAz6pMgH1j1mtzth1mdb9yyVTEFSLslapCf +bCXVlKSZeH8Hb3t3nL3Uzv925+MkIsq8qrNxe6HUiscm0aKm3jTCACfsqSo33q/W +TSrrDGeNvr9Ke9kJJczVb4pr66M0KVEGSz5aoFGZxAKHMXNJUu/f0H3s8RBXMQ/M +bcwngNpc1f4RVnO2zeCz/GeSwf+00nouD/xjuy2SlpUPMhs4Qp8r7/Vgi2kaO095 +SOKc/hCoKspFY0vX3KVsHVIxyPMFTik4dmatQo4cJy4FW/dp3/2kyXf1HAnf0ZnK +2q8/iebylYxVio5X9UQ4hKIWgLUHu29crr515VPfGIidP+KNq6Lq49WAKPyVH1ex +hJSBoRiiSrNpAmYCubvHzZsIbC9UTCl2my1JahnHnSM4u18qwFki1n1Dj0BXof/Q +FjNIJEjN7b5hMaT0/+kIQN4kbMUhN+xR8HqGkQHrYC312ezKL+J7wI3+mIVSlr6I +88n6N8ZDNOPNHAUFBi9/7KiLGFdydAZT/l4gBvop/DTAzmPL9+RfxP3jNwARAQAB +tB9IYXJhbGQgSG95ZXIgPGhhcmFsZEBob3llci54eXo+iQNVBBMBCAE/AhsDAhkB +QBSAAAAAABAAJ3Byb29mQGFyaWFkbmUuaWRodHRwczovL2dpdGxhYi5jb20vaGFy +YWxkaC9naXRsYWJfcHJvb2ZZFIAAAAAAEABAcHJvb2ZAYXJpYWRuZS5pZGh0dHBz +Oi8vZ2lzdC5naXRodWIuY29tL2hhcmFsZGgvOWVjZjYzNGQ2NzQyZWFlZTRkOGM5 +YjZjZDgyZTM0ODA4FIAAAAAAEAAfcHJvb2ZAYXJpYWRuZS5pZGh0dHBzOi8vZmxv +c3Muc29jaWFsL0BiYWNrc2xhc2gvFIAAAAAAEAAWcHJvb2ZAYXJpYWRuZS5pZGRu +czpob3llci54eXo/dHlwZT1UWFQCF4AWIQR/PWSCSsC2uACeUFBLwIlvtWk1lQUC +ZON15QULCQgHAgYVCgkICwIEFgIDAQIeBQAKCRBLwIlvtWk1lcagEADM/a0kcv0l +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 +=hAu3 +-----END PGP PUBLIC KEY BLOCK----- diff --git a/crates/teepot/tests/data/pub-81A312C59D679D930FA9E8B06D728F29A2DBABF8.b64 b/crates/teepot/tests/data/pub-81A312C59D679D930FA9E8B06D728F29A2DBABF8.b64 deleted file mode 100644 index befdcaf..0000000 --- a/crates/teepot/tests/data/pub-81A312C59D679D930FA9E8B06D728F29A2DBABF8.b64 +++ /dev/null @@ -1,16 +0,0 @@ -mQGNBGTBKKQBDACo28S5h7rvfcj89OIv66evSsOExJJ363oYWBfs0GPkgPG/bS2pgIFe5+yqvW5q -2tnDYHz0WJkYQcwTqQQpt/Ma+UV+uaHc2pJfKvsmpI9o3xf+eV3C9HrU+6lwr8efkBkjLrfzkroQ -f0II/eX9omePt4qXNMX07UKI1ZrmXWmn5BlXiwkQl0M9XWwS3PZ2aiM2MMUjfmDAdi5pc10UgZdd -lbInjK8guXj9/HQt23l3W1df83QNlxC+sDmlekwugeg0HckcgTjGICvML+gwsX/GVDMJe/O8D7Sy -7KKrO23xwus7p8At4C5+0WCqARBKQbTE0IZTyUZUgt6xnn1BhFHBCblpI2KPkRXSUWBkb/Npr40Y -2uMiBZNk2Iwdyuim8+Zs5pHCISvR09COswFgM+K7ikv7EMMr1YsEcFGH4sE3GQXVw4qONdITNBAz -/5+cPWAOUOske6BeUjLaC4XaG4wY2Rg7RVoDCqhKav+VYE2B/X0pNVSfPyZKirkMllHVwA8AEQEA -AbQXdGVzdCA8dGVzdEBleGFtcGxlLmNvbT6JAdEEEwEIADsWIQSBoxLFnWedkw+p6LBtco8potur -+AUCZMEopAIbAwULCQgHAgIiAgYVCgkICwIEFgIDAQIeBwIXgAAKCRBtco8potur+Cb2C/9sc2VY -pi8eYnPrclU/28SoecG9cyPAZ5D+AQfAnVPLDdeBgLLr4FrMkRLvFNEoNwSFWAHNde77ChKFVPjq -s1ubR+P6RJ8YjeuF5l5objn/xXd+fe79gjBfWRVUR36+q9TvJwr51bL/cFNv0eGpvxV4OhiXImF9 -9Ik3HtXX2w5b8rkH2DcW39HlQvxqOY3OTskmVn0nv5l6Q9CHrIXsaug11Bp/EQPyMG/E0Qa1SWCI -yDwphEQzIUFIBg0zlAUC2Tlh9PYrgZJ5GjNY18a0ZglwTp48IpwOUXwT9IzOKyrM1jA9lg8P5ih/ -MLZKIMvroKrry+IZXQAujnvH0HwMehDuepR+liOx1byI2VitOkl7GpAZ03t+ae/KrsjJ+Dx/oFFk -DVQAF2dDJHiFzZD8DHucIngY4b8Wq7N+8hzyk8AgKFK9rq3HqPH5L+NsJEzS1xOQeaGlI1H1knnA -gRuIBZdZewrR/UosYei3mZtEclHU+YqeeBAsVoKKSVi2ryu8RCk= diff --git a/crates/teepot/tests/data/test.json.asc b/crates/teepot/tests/data/test.json.asc index de8708e..ed87504 100644 --- a/crates/teepot/tests/data/test.json.asc +++ b/crates/teepot/tests/data/test.json.asc @@ -1,26 +1,14 @@ -----BEGIN PGP SIGNATURE----- -iQIzBAABCAAdFiEEC0Pk344t4+guABNk9RmhFDs/vjIFAmT5sBwACgkQ9RmhFDs/ -vjIJ2g/9E8kRdvz8hhxOyJRPpNZ9bcGJ+FvMjG7geEdixqu1Bwpfyj+UhGRY7Dgq -4T46w/Hmr8YBZTI3xpafUSldyEvZnFXRuIQoBR+JQYNu8s9Jm9yDIyLLA86quiY8 -nU+x6x89sSOOvmTpRUBi6htTC4h0zZHHfAcmu0YS2pMmxXYJX7Rw3T7AJE0Uc4O/ -+0Ho1PMFB4BRmnNGlqBFc2u/yXEy38AWrmcheoPNtbdWmI/3leKikW5l2cXfPRuT -tazc92NiIK6qu209jlusMBpADdu8FSAI9ax4dKL1uE8KQyUIKQVQq3sBqQsPTgiW -XT6XznFdWawtW1y2jzt0DbdCt2osSwV7rPYbn6yxEpzQWuPL75JiDmJMktgBV38t -FKBpQl1ZDF9wARwsxBvNRaL0XPSurTtf/x4olue91D3I2fNeymS6P1DNXvLGXPD5 -46C2TvS1305wPjCpFAEgS58WpiiJppCYDsU0A7DEHbaIgk5mQ/iEdBOBvrn0xton -Rni5mXt0Vi0WfE8GriR25YajtAOI2/rxTSZjiSJI51WmdnV/lkJiY1YF0ws+KUXG -r2E+Bea0kwcnCMYFzraxwLwiS4mgamdQmp8DNALYZe8m/k+dNKI6tDEdWFMTiwda -PJMDc3+lUOcZCN6+umLUOVvNWQZ2QtZ4RSTzbJyDww4ysgTpFHOJAcUEAAEIAC8W -IQSBoxLFnWedkw+p6LBtco8potur+AUCZPmwIREcdGVzdEBleGFtcGxlLmNvbQAK -CRBtco8potur+CMVDACm+OMPck8lmOnNGphP7kKtMcyMyIvsI2Ik/fB3A2z+iXnX -TQljubYTqN4Hzv5MECoUhm3GiWJOYjGoXnpec1zAk4VAewin1814Dldq/9CQ+YlN -W7HKjCiCBSdk2tPEBUK6gfV+OU9N2mNp/+biuwNTVHpPokIiwBE3MKjtUc0W0xLU -N+S/mb1l0MsA91PPRlZqovEFs6214zo8e31pXcWbLbfhbVY32pqDk4eG1JAFjjGM -oBASk7z1TH8Ealfj3xr8/HCbfyoaMcSmQzYdEr4E7XOp955IjRU91RrHxYjcxQ9f -0VUwak08k6hNBD502jstv5ePJBWoFXYXKuBlaH224Xvhe+9GnaUXsU8rI+OMHJes -g4/98+bparaQ8pjhLeh5BoIhojg0y4qNaFmbJraZxG3uhyH0nDVfepOsUj4QOadS -VbuULwAy97q+UFwUWuVn/XVzrb3B35TpIJWpvJLF6w5f3pyy58XsVOfVmNyyGuyU -jMWUGxIGAsXZusCmsNs= -=qVV5 +iQHFBAABCAAvFiEEgaMSxZ1nnZMPqeiwbXKPKaLbq/gFAmaw3l4RHHRlc3RAZXhh +bXBsZS5jb20ACgkQbXKPKaLbq/jgCwwAnry/QNtlp5gagPAjcweL1J3K1vIsAMsN +GI9VUcM+1QtAlz3vLKfXKJXo4SpLS4TBPw/A7A4IATdEdYirp7dG3K6MtWnVl4P5 +kNs39SUUlVC2RlfiMOS0NHCMz9lazJsmVjYT1qwdGiO/9BS1Yy1VVbWJuRsPPBRH +kF4WbGiMkW4x0H+8s0tFCMoq9hgqqqcyxMbE6GLHa8u57fhn1TRVGpClRZ/G86xw +1N9I3nKvRvX64AjG3SOslBKDeKM01EIICG81FOTj54pIAiyKY82nRlx5OsRxsSpV +7GvIqE47e+qCmL4WOMykq+B+DHdalH4/5+AWaGfKcZCibXOxHvimfOfBaM/nah9W +wFOGDRihW759yHPq/ZP7JCYqlMRrNoAjoBORJEn/HHYYskYy4KeZSLFUm1eKAxFi +mg2oL6LFZn5O466JrZ4c2b0hOpKBUdC/NkXSgv8O6SuxnFmxGkx8fsdtm+d0T4Ff +PBat77Pnv5bDJ1dOJ5HQlvivKKPU6SK1 +=zQsO -----END PGP SIGNATURE----- diff --git a/crates/teepot/tests/data/test.json_2.asc b/crates/teepot/tests/data/test.json_2.asc new file mode 100644 index 0000000..25111d2 --- /dev/null +++ b/crates/teepot/tests/data/test.json_2.asc @@ -0,0 +1,16 @@ +-----BEGIN PGP SIGNATURE----- + +iQIzBAABCAAdFiEEC0Pk344t4+guABNk9RmhFDs/vjIFAmaw1WoACgkQ9RmhFDs/ +vjK4rRAArFtDoNwQsFVhFCIE3c4RNwfsgqZQp3RhdRBxw6DqCPjv0z41R2pQ5//r +TtKxtfqJqP5wdOHyk18ITd3AB49aRAIcgf2qq9t9Nygt0AR09IXKSFkozi+dtbIH +MR36zxa5DKejF6v6YiCrw6L2QGfDRYg/1IvLxdwyjGy4jwuNsRZJf4JSC8vWnm+n +EIVy4Fqcmd/kikLm1lhHaugIQ8u5HpHOP0uszhGAZHB5WPEHVOlnMKSODWZYSrvi +vhzQZEuvCWeFXH9qKmlC9DyJzUnnhCmnOkBZoSdOZIgnZfH2+pbyfRQ+7Hn8gbHB +VPCPOjRjvmR5EhHmuCdPTLZRl75mPlfQKVvBg5FMgMqXJYPlWSgJndPjWLtbiopD +7yTtrfesxXbJl2N/rSDWw3e3B7liWNgNOZGYY/xrKk/shlT+J2LJThQYvTcWXzdk +qHKGObiiDbZzwKDuTQ2NeI9JnbVk51JjqerhE6XkdGoCiGRyV7hUzF69m/vNnjD3 ++saZRANRzkyxxGUvDZbuSNIXAtxcc57to593z38XRIJAaS3/S7+cbBqc0daPJJKH +06gYG2Ak/jE99kZ2P/ULp0I58My14keMBlCRhcl2Y4sq0KL67pBtsqvFh2Vm1hKl +7siJFYSWXkyFit4pVCH2SlJf7s0AB1OZsOATG6Oil3gHpny5ybo= +=X256 +-----END PGP SIGNATURE----- diff --git a/crates/teepot/tests/sgx_quote_verification.rs b/crates/teepot/tests/sgx_quote_verification.rs index 4f04d2f..80368d0 100644 --- a/crates/teepot/tests/sgx_quote_verification.rs +++ b/crates/teepot/tests/sgx_quote_verification.rs @@ -6,6 +6,7 @@ mod sgx { use intel_tee_quote_verification_rs::{sgx_ql_qv_result_t, Collateral}; use std::time::{Duration, UNIX_EPOCH}; use teepot::sgx::{verify_quote_with_collateral, QuoteVerificationResult}; + use test_log::test; fn check_quote( quote: &[u8],