From ca5e60aae5059748073796de3083e93669a9cc36 Mon Sep 17 00:00:00 2001 From: Geoffroy Couprie Date: Tue, 11 Oct 2022 21:08:14 +0200 Subject: [PATCH 01/22] add a signature type --- biscuit-auth/src/crypto/mod.rs | 38 ++++++++++++++++++++-------- biscuit-auth/src/format/mod.rs | 33 +++++++----------------- biscuit-auth/src/token/mod.rs | 11 +++----- biscuit-auth/src/token/unverified.rs | 8 ++---- 4 files changed, 42 insertions(+), 48 deletions(-) diff --git a/biscuit-auth/src/crypto/mod.rs b/biscuit-auth/src/crypto/mod.rs index 324e9ca2..5d694129 100644 --- a/biscuit-auth/src/crypto/mod.rs +++ b/biscuit-auth/src/crypto/mod.rs @@ -179,18 +179,38 @@ impl Hash for PublicKey { } } +#[derive(Clone, Debug)] +pub struct Signature(pub(crate) ed25519_dalek::Signature); + +impl Signature { + pub fn from_bytes(data: &[u8]) -> Result { + Ok(Signature( + ed25519_dalek::Signature::from_bytes(data).map_err(|e| { + error::Format::BlockSignatureDeserializationError(format!( + "block signature deserialization error: {:?}", + e + )) + })?, + )) + } + + pub fn to_bytes(&self) -> [u8; 64] { + self.0.to_bytes() + } +} + #[derive(Clone, Debug)] pub struct Block { pub(crate) data: Vec, pub(crate) next_key: PublicKey, - pub signature: ed25519_dalek::Signature, + pub signature: Signature, pub external_signature: Option, } #[derive(Clone, Debug)] pub struct ExternalSignature { pub(crate) public_key: PublicKey, - pub(crate) signature: ed25519_dalek::Signature, + pub(crate) signature: Signature, } #[derive(Clone, Debug)] @@ -203,7 +223,7 @@ pub struct Token { #[derive(Clone, Debug)] pub enum TokenNext { Secret(PrivateKey), - Seal(ed25519_dalek::Signature), + Seal(Signature), } pub fn sign( @@ -223,24 +243,22 @@ pub fn sign( .map_err(error::Signature::InvalidSignatureGeneration) .map_err(error::Format::Signature)?; - Ok(signature) + Ok(Signature(signature)) } pub fn verify_block_signature(block: &Block, public_key: &PublicKey) -> Result<(), error::Format> { - use ed25519_dalek::ed25519::signature::Signature; - //FIXME: replace with SHA512 hashing let mut to_verify = block.data.to_vec(); if let Some(signature) = block.external_signature.as_ref() { - to_verify.extend_from_slice(signature.signature.as_bytes()); + to_verify.extend_from_slice(&signature.signature.to_bytes()); } to_verify.extend(&(crate::format::schema::public_key::Algorithm::Ed25519 as i32).to_le_bytes()); to_verify.extend(&block.next_key.to_bytes()); public_key .0 - .verify_strict(&to_verify, &block.signature) + .verify_strict(&to_verify, &block.signature.0) .map_err(|s| s.to_string()) .map_err(error::Signature::InvalidSignature) .map_err(error::Format::Signature)?; @@ -254,7 +272,7 @@ pub fn verify_block_signature(block: &Block, public_key: &PublicKey) -> Result<( external_signature .public_key .0 - .verify_strict(&to_verify, &external_signature.signature) + .verify_strict(&to_verify, &external_signature.signature.0) .map_err(|s| s.to_string()) .map_err(error::Signature::InvalidSignature) .map_err(error::Format::Signature)?; @@ -347,7 +365,7 @@ impl Token { current_pub .0 - .verify_strict(&to_verify, signature) + .verify_strict(&to_verify, &signature.0) .map_err(|s| s.to_string()) .map_err(error::Signature::InvalidSignature) .map_err(error::Format::Signature)?; diff --git a/biscuit-auth/src/format/mod.rs b/biscuit-auth/src/format/mod.rs index 913b1ce7..36d5538a 100644 --- a/biscuit-auth/src/format/mod.rs +++ b/biscuit-auth/src/format/mod.rs @@ -6,12 +6,12 @@ //! - serialization of a wrapper structure containing serialized blocks and the signature use super::crypto::{self, KeyPair, PrivateKey, PublicKey, TokenNext}; -use ed25519_dalek::ed25519::signature::Signature; use prost::Message; use super::error; use super::token::Block; use crate::crypto::ExternalSignature; +use crate::crypto::Signature; use crate::datalog::SymbolTable; use crate::token::RootKeyProvider; use ed25519_dalek::Signer; @@ -63,12 +63,7 @@ impl SerializedBiscuit { .try_into() .map_err(|_| error::Format::InvalidSignatureSize(data.authority.signature.len()))?; - let signature = ed25519_dalek::Signature::from_bytes(&bytes).map_err(|e| { - error::Format::SignatureDeserializationError(format!( - "signature deserialization error: {:?}", - e - )) - })?; + let signature = Signature::from_bytes(&bytes)?; if data.authority.external_signature.is_some() { return Err(error::Format::DeserializationError( @@ -91,12 +86,7 @@ impl SerializedBiscuit { .try_into() .map_err(|_| error::Format::InvalidSignatureSize(block.signature.len()))?; - let signature = ed25519_dalek::Signature::from_bytes(&bytes).map_err(|e| { - error::Format::BlockSignatureDeserializationError(format!( - "block signature deserialization error: {:?}", - e - )) - })?; + let signature = Signature::from_bytes(&bytes)?; let external_signature = if let Some(ex) = block.external_signature.as_ref() { let public_key = PublicKey::from_proto(&ex.public_key)?; @@ -105,12 +95,7 @@ impl SerializedBiscuit { .try_into() .map_err(|_| error::Format::InvalidSignatureSize(ex.signature.len()))?; - let signature = ed25519_dalek::Signature::from_bytes(&bytes).map_err(|e| { - error::Format::BlockSignatureDeserializationError(format!( - "block external signature deserialization error: {:?}", - e - )) - })?; + let signature = Signature::from_bytes(&bytes)?; Some(ExternalSignature { public_key, @@ -141,7 +126,7 @@ impl SerializedBiscuit { let bytes: [u8; 64] = (&v[..]) .try_into() .map_err(|_| error::Format::InvalidSignatureSize(v.len()))?; - let signature = ed25519_dalek::Signature::from_bytes(&bytes).map_err(|e| { + let signature = Signature::from_bytes(&bytes).map_err(|e| { error::Format::SignatureDeserializationError(format!( "final signature deserialization error: {:?}", e @@ -336,7 +321,7 @@ impl SerializedBiscuit { error::Format::SerializationError(format!("serialization error: {:?}", e)) })?; if let Some(signature) = &external_signature { - v.extend_from_slice(signature.signature.as_bytes()); + v.extend_from_slice(&signature.signature.to_bytes()); } let signature = crypto::sign(&keypair, next_keypair, &v)?; @@ -369,7 +354,7 @@ impl SerializedBiscuit { let mut v = block.clone(); if let Some(signature) = &external_signature { - v.extend_from_slice(signature.signature.as_bytes()); + v.extend_from_slice(&signature.signature.to_bytes()); } let signature = crypto::sign(&keypair, next_keypair, &v)?; @@ -432,7 +417,7 @@ impl SerializedBiscuit { current_pub .0 - .verify_strict(&to_verify, signature) + .verify_strict(&to_verify, &signature.0) .map_err(|s| s.to_string()) .map_err(error::Signature::InvalidSignature) .map_err(error::Format::Signature)?; @@ -469,7 +454,7 @@ impl SerializedBiscuit { root_key_id: self.root_key_id, authority: self.authority.clone(), blocks: self.blocks.clone(), - proof: TokenNext::Seal(signature), + proof: TokenNext::Seal(Signature(signature)), }) } } diff --git a/biscuit-auth/src/token/mod.rs b/biscuit-auth/src/token/mod.rs index e47c1385..bdc0921f 100644 --- a/biscuit-auth/src/token/mod.rs +++ b/biscuit-auth/src/token/mod.rs @@ -4,7 +4,7 @@ use std::convert::TryInto; use self::public_keys::PublicKeys; -use super::crypto::{KeyPair, PublicKey}; +use super::crypto::{KeyPair, PublicKey, Signature}; use super::datalog::SymbolTable; use super::error; use super::format::SerializedBiscuit; @@ -423,12 +423,7 @@ impl Biscuit { .try_into() .map_err(|_| error::Format::InvalidSignatureSize(external_signature.signature.len()))?; - let signature = ed25519_dalek::Signature::from_bytes(&bytes).map_err(|e| { - error::Format::BlockSignatureDeserializationError(format!( - "block external signature deserialization error: {:?}", - e - )) - })?; + let signature = Signature::from_bytes(&bytes)?; let previous_key = self .container .blocks @@ -442,7 +437,7 @@ impl Biscuit { external_key .0 - .verify_strict(&to_verify, &signature) + .verify_strict(&to_verify, &signature.0) .map_err(|s| s.to_string()) .map_err(error::Signature::InvalidSignature) .map_err(error::Format::Signature)?; diff --git a/biscuit-auth/src/token/unverified.rs b/biscuit-auth/src/token/unverified.rs index 63040b21..f4c92f3a 100644 --- a/biscuit-auth/src/token/unverified.rs +++ b/biscuit-auth/src/token/unverified.rs @@ -6,6 +6,7 @@ use crate::{ builder::BlockBuilder, crypto, crypto::PublicKey, + crypto::Signature, datalog::SymbolTable, error, format::{convert::proto_block_to_token_block, schema, SerializedBiscuit}, @@ -296,12 +297,7 @@ impl UnverifiedBiscuit { .try_into() .map_err(|_| error::Format::InvalidSignatureSize(external_signature.signature.len()))?; - let signature = ed25519_dalek::Signature::from_bytes(&bytes).map_err(|e| { - error::Format::BlockSignatureDeserializationError(format!( - "block external signature deserialization error: {:?}", - e - )) - })?; + let signature = Signature::from_bytes(&bytes)?; let previous_key = self .container .blocks From 863c723ec880d5963e0cf29e150bfbc226d9a73b Mon Sep 17 00:00:00 2001 From: Geoffroy Couprie Date: Tue, 11 Oct 2022 21:10:53 +0200 Subject: [PATCH 02/22] helper methods --- biscuit-auth/src/crypto/mod.rs | 84 ++++++++++++++++++--------- biscuit-auth/src/format/mod.rs | 30 +++------- biscuit-auth/src/token/mod.rs | 19 +++--- biscuit-auth/src/token/third_party.rs | 10 +--- biscuit-auth/src/token/unverified.rs | 13 +---- 5 files changed, 75 insertions(+), 81 deletions(-) diff --git a/biscuit-auth/src/crypto/mod.rs b/biscuit-auth/src/crypto/mod.rs index 5d694129..8d9fcdd6 100644 --- a/biscuit-auth/src/crypto/mod.rs +++ b/biscuit-auth/src/crypto/mod.rs @@ -10,6 +10,7 @@ use crate::{error::Format, format::schema}; use super::error; +use ed25519_dalek::Signer; use ed25519_dalek::*; use rand_core::{CryptoRng, RngCore}; use std::{convert::TryInto, hash::Hash, ops::Drop}; @@ -18,7 +19,7 @@ use zeroize::Zeroize; /// pair of cryptographic keys used to sign a token's block #[derive(Debug)] pub struct KeyPair { - pub kp: ed25519_dalek::Keypair, + kp: ed25519_dalek::Keypair, } impl KeyPair { @@ -42,6 +43,18 @@ impl KeyPair { } } + pub fn sign(&self, data: &[u8]) -> Result { + Ok(Signature( + self.kp + .try_sign(&data) + .map_err(|s| s.to_string()) + .map_err(error::Signature::InvalidSignatureGeneration) + .map_err(error::Format::Signature)? + .to_bytes() + .to_vec(), + )) + } + pub fn private(&self) -> PrivateKey { let secret = SecretKey::from_bytes(&self.kp.secret.to_bytes()).unwrap(); PrivateKey(secret) @@ -50,6 +63,10 @@ impl KeyPair { pub fn public(&self) -> PublicKey { PublicKey(self.kp.public) } + + pub fn algorithm(&self) -> crate::format::schema::public_key::Algorithm { + crate::format::schema::public_key::Algorithm::Ed25519 + } } impl std::default::Default for KeyPair { @@ -66,7 +83,7 @@ impl Drop for KeyPair { /// the private part of a [KeyPair] #[derive(Debug)] -pub struct PrivateKey(pub(crate) ed25519_dalek::SecretKey); +pub struct PrivateKey(ed25519_dalek::SecretKey); impl PrivateKey { /// serializes to a byte array @@ -100,6 +117,10 @@ impl PrivateKey { pub fn public(&self) -> PublicKey { PublicKey((&self.0).into()) } + + pub fn algorithm(&self) -> crate::format::schema::public_key::Algorithm { + crate::format::schema::public_key::Algorithm::Ed25519 + } } impl std::clone::Clone for PrivateKey { @@ -116,7 +137,7 @@ impl Drop for PrivateKey { /// the public part of a [KeyPair] #[derive(Debug, Clone, Copy, Eq)] -pub struct PublicKey(pub(crate) ed25519_dalek::PublicKey); +pub struct PublicKey(ed25519_dalek::PublicKey); impl PublicKey { /// serializes to a byte array @@ -161,6 +182,29 @@ impl PublicKey { } } + pub fn verify_signature( + &self, + data: &[u8], + signature: &Signature, + ) -> Result<(), error::Format> { + let sig = ed25519_dalek::Signature::from_bytes(&signature.0).map_err(|e| { + error::Format::BlockSignatureDeserializationError(format!( + "block signature deserialization error: {:?}", + e + )) + })?; + + self.0 + .verify_strict(&data, &sig) + .map_err(|s| s.to_string()) + .map_err(error::Signature::InvalidSignature) + .map_err(error::Format::Signature) + } + + pub fn algorithm(&self) -> crate::format::schema::public_key::Algorithm { + crate::format::schema::public_key::Algorithm::Ed25519 + } + pub fn print(&self) -> String { format!("ed25519/{}", hex::encode(&self.to_bytes())) } @@ -180,22 +224,15 @@ impl Hash for PublicKey { } #[derive(Clone, Debug)] -pub struct Signature(pub(crate) ed25519_dalek::Signature); +pub struct Signature(pub(crate) Vec); impl Signature { pub fn from_bytes(data: &[u8]) -> Result { - Ok(Signature( - ed25519_dalek::Signature::from_bytes(data).map_err(|e| { - error::Format::BlockSignatureDeserializationError(format!( - "block signature deserialization error: {:?}", - e - )) - })?, - )) + Ok(Signature(data.to_owned())) } - pub fn to_bytes(&self) -> [u8; 64] { - self.0.to_bytes() + pub fn to_bytes(&self) -> &[u8] { + &self.0[..] } } @@ -243,7 +280,7 @@ pub fn sign( .map_err(error::Signature::InvalidSignatureGeneration) .map_err(error::Format::Signature)?; - Ok(Signature(signature)) + Ok(Signature(signature.to_bytes().to_vec())) } pub fn verify_block_signature(block: &Block, public_key: &PublicKey) -> Result<(), error::Format> { @@ -256,12 +293,7 @@ pub fn verify_block_signature(block: &Block, public_key: &PublicKey) -> Result<( to_verify.extend(&(crate::format::schema::public_key::Algorithm::Ed25519 as i32).to_le_bytes()); to_verify.extend(&block.next_key.to_bytes()); - public_key - .0 - .verify_strict(&to_verify, &block.signature.0) - .map_err(|s| s.to_string()) - .map_err(error::Signature::InvalidSignature) - .map_err(error::Format::Signature)?; + public_key.verify_signature(&to_verify, &block.signature)?; if let Some(external_signature) = block.external_signature.as_ref() { let mut to_verify = block.data.to_vec(); @@ -271,16 +303,12 @@ pub fn verify_block_signature(block: &Block, public_key: &PublicKey) -> Result<( external_signature .public_key - .0 - .verify_strict(&to_verify, &external_signature.signature.0) - .map_err(|s| s.to_string()) - .map_err(error::Signature::InvalidSignature) - .map_err(error::Format::Signature)?; + .verify_signature(&to_verify, &external_signature.signature)?; } Ok(()) } - +/* impl Token { #[allow(dead_code)] pub fn new( @@ -374,7 +402,7 @@ impl Token { Ok(()) } -} +}*/ impl TokenNext { pub fn keypair(&self) -> Result { diff --git a/biscuit-auth/src/format/mod.rs b/biscuit-auth/src/format/mod.rs index 36d5538a..8ef66639 100644 --- a/biscuit-auth/src/format/mod.rs +++ b/biscuit-auth/src/format/mod.rs @@ -14,7 +14,6 @@ use crate::crypto::ExternalSignature; use crate::crypto::Signature; use crate::datalog::SymbolTable; use crate::token::RootKeyProvider; -use ed25519_dalek::Signer; use std::collections::HashMap; use std::convert::TryInto; @@ -409,18 +408,11 @@ impl SerializedBiscuit { &self.blocks[self.blocks.len() - 1] }; to_verify.extend(&block.data); - to_verify.extend( - &(crate::format::schema::public_key::Algorithm::Ed25519 as i32).to_le_bytes(), - ); + to_verify.extend(&(current_pub.algorithm() as i32).to_le_bytes()); to_verify.extend(&block.next_key.to_bytes()); - to_verify.extend(&block.signature.to_bytes()); - - current_pub - .0 - .verify_strict(&to_verify, &signature.0) - .map_err(|s| s.to_string()) - .map_err(error::Signature::InvalidSignature) - .map_err(error::Format::Signature)?; + to_verify.extend(block.signature.to_bytes()); + + current_pub.verify_signature(&to_verify, &signature)?; } } @@ -438,23 +430,17 @@ impl SerializedBiscuit { &self.blocks[self.blocks.len() - 1] }; to_sign.extend(&block.data); - to_sign - .extend(&(crate::format::schema::public_key::Algorithm::Ed25519 as i32).to_le_bytes()); + to_sign.extend(&(keypair.algorithm() as i32).to_le_bytes()); to_sign.extend(&block.next_key.to_bytes()); - to_sign.extend(&block.signature.to_bytes()); + to_sign.extend(block.signature.to_bytes()); - let signature = keypair - .kp - .try_sign(&to_sign) - .map_err(|s| s.to_string()) - .map_err(error::Signature::InvalidSignatureGeneration) - .map_err(error::Format::Signature)?; + let signature = keypair.sign(&to_sign)?; Ok(SerializedBiscuit { root_key_id: self.root_key_id, authority: self.authority.clone(), blocks: self.blocks.clone(), - proof: TokenNext::Seal(Signature(signature)), + proof: TokenNext::Seal(signature), }) } } diff --git a/biscuit-auth/src/token/mod.rs b/biscuit-auth/src/token/mod.rs index bdc0921f..4b749692 100644 --- a/biscuit-auth/src/token/mod.rs +++ b/biscuit-auth/src/token/mod.rs @@ -410,15 +410,16 @@ impl Biscuit { external_signature, } = response.0; - if external_signature.public_key.algorithm != schema::public_key::Algorithm::Ed25519 as i32 - { + let provided_key = PublicKey::from_proto(&external_signature.public_key)?; + if external_key != provided_key { return Err(error::Token::Format(error::Format::DeserializationError( format!( - "deserialization error: unexpected key algorithm {}", - external_signature.public_key.algorithm + "deserialization error: unexpected key {}", + provided_key.print() ), ))); } + let bytes: [u8; 64] = (&external_signature.signature[..]) .try_into() .map_err(|_| error::Format::InvalidSignatureSize(external_signature.signature.len()))?; @@ -431,16 +432,10 @@ impl Biscuit { .unwrap_or(&self.container.authority) .next_key; let mut to_verify = payload.clone(); - to_verify - .extend(&(crate::format::schema::public_key::Algorithm::Ed25519 as i32).to_le_bytes()); + to_verify.extend(&(external_key.algorithm() as i32).to_le_bytes()); to_verify.extend(&previous_key.to_bytes()); - external_key - .0 - .verify_strict(&to_verify, &signature.0) - .map_err(|s| s.to_string()) - .map_err(error::Signature::InvalidSignature) - .map_err(error::Format::Signature)?; + external_key.verify_signature(&to_verify, &signature)?; let block = schema::Block::decode(&payload[..]).map_err(|e| { error::Token::Format(error::Format::DeserializationError(format!( diff --git a/biscuit-auth/src/token/third_party.rs b/biscuit-auth/src/token/third_party.rs index 26aedbed..1c8fba8d 100644 --- a/biscuit-auth/src/token/third_party.rs +++ b/biscuit-auth/src/token/third_party.rs @@ -1,4 +1,3 @@ -use ed25519_dalek::Signer; use prost::Message; use crate::{ @@ -133,16 +132,11 @@ impl ThirdPartyRequest { })?; let payload = v.clone(); - v.extend(&(crate::format::schema::public_key::Algorithm::Ed25519 as i32).to_le_bytes()); + v.extend(&(private_key.algorithm() as i32).to_le_bytes()); v.extend(self.previous_key.to_bytes()); let keypair = KeyPair::from(private_key); - let signature = keypair - .kp - .try_sign(&v) - .map_err(|s| s.to_string()) - .map_err(error::Signature::InvalidSignatureGeneration) - .map_err(error::Format::Signature)?; + let signature = keypair.sign(&v)?; let public_key = keypair.public(); let content = schema::ThirdPartyBlockContents { diff --git a/biscuit-auth/src/token/unverified.rs b/biscuit-auth/src/token/unverified.rs index f4c92f3a..b91a320f 100644 --- a/biscuit-auth/src/token/unverified.rs +++ b/biscuit-auth/src/token/unverified.rs @@ -276,15 +276,6 @@ impl UnverifiedBiscuit { error::Format::DeserializationError(format!("deserialization error: {:?}", e)) })?; - if external_signature.public_key.algorithm != schema::public_key::Algorithm::Ed25519 as i32 - { - return Err(error::Token::Format(error::Format::DeserializationError( - format!( - "deserialization error: unexpected key algorithm {}", - external_signature.public_key.algorithm - ), - ))); - } let external_key = PublicKey::from_bytes(&external_signature.public_key.key).map_err(|e| { error::Format::BlockSignatureDeserializationError(format!( @@ -305,9 +296,9 @@ impl UnverifiedBiscuit { .unwrap_or(&self.container.authority) .next_key; let mut to_verify = payload.clone(); - to_verify - .extend(&(crate::format::schema::public_key::Algorithm::Ed25519 as i32).to_le_bytes()); + to_verify.extend(&(external_key.algorithm() as i32).to_le_bytes()); to_verify.extend(&previous_key.to_bytes()); + external_key.verify_signature(&to_verify, &signature)?; let block = schema::Block::decode(&payload[..]).map_err(|e| { error::Token::Format(error::Format::DeserializationError(format!( From b242be8d500ec39dc3a4596f10c36ce2754beea9 Mon Sep 17 00:00:00 2001 From: Geoffroy Couprie Date: Tue, 11 Oct 2022 21:15:24 +0200 Subject: [PATCH 03/22] indicate the algorithm of the next private key --- biscuit-auth/src/crypto/mod.rs | 11 +++++++++++ biscuit-auth/src/format/mod.rs | 3 +++ biscuit-auth/src/format/schema.proto | 11 +++++++++++ 3 files changed, 25 insertions(+) diff --git a/biscuit-auth/src/crypto/mod.rs b/biscuit-auth/src/crypto/mod.rs index 8d9fcdd6..58716eb6 100644 --- a/biscuit-auth/src/crypto/mod.rs +++ b/biscuit-auth/src/crypto/mod.rs @@ -113,6 +113,17 @@ impl PrivateKey { Self::from_bytes(&bytes) } + pub fn from_proto(key: &schema::PrivateKey) -> Result { + if key.algorithm != schema::private_key::Algorithm::Ed25519 as i32 { + return Err(error::Format::DeserializationError(format!( + "deserialization error: unexpected key algorithm {}", + key.algorithm + ))); + } + + PrivateKey::from_bytes(&key.key) + } + /// returns the matching public key pub fn public(&self) -> PublicKey { PublicKey((&self.0).into()) diff --git a/biscuit-auth/src/format/mod.rs b/biscuit-auth/src/format/mod.rs index 8ef66639..4b13c110 100644 --- a/biscuit-auth/src/format/mod.rs +++ b/biscuit-auth/src/format/mod.rs @@ -121,6 +121,9 @@ impl SerializedBiscuit { Some(schema::proof::Content::NextSecret(v)) => { TokenNext::Secret(PrivateKey::from_bytes(&v)?) } + Some(schema::proof::Content::NextSecretv3(key)) => { + TokenNext::Secret(PrivateKey::from_proto(&key)?) + } Some(schema::proof::Content::FinalSignature(v)) => { let bytes: [u8; 64] = (&v[..]) .try_into() diff --git a/biscuit-auth/src/format/schema.proto b/biscuit-auth/src/format/schema.proto index 461075f5..be32c1de 100644 --- a/biscuit-auth/src/format/schema.proto +++ b/biscuit-auth/src/format/schema.proto @@ -31,11 +31,22 @@ message PublicKey { required bytes key = 2; } +message PrivateKey { + required Algorithm algorithm = 1; + + enum Algorithm { + Ed25519 = 0; + } + + required bytes key = 2; +} message Proof { oneof Content { + // deprecated bytes nextSecret = 1; bytes finalSignature = 2; + PrivateKey nextSecretv3 = 3; } } From 92da6e8d79275360213e89fd4ca5fa3515cba5fc Mon Sep 17 00:00:00 2001 From: Geoffroy Couprie Date: Tue, 11 Oct 2022 21:13:07 +0200 Subject: [PATCH 04/22] cleanup --- biscuit-auth/src/crypto/mod.rs | 198 +-------------------------------- 1 file changed, 3 insertions(+), 195 deletions(-) diff --git a/biscuit-auth/src/crypto/mod.rs b/biscuit-auth/src/crypto/mod.rs index 58716eb6..19aadafb 100644 --- a/biscuit-auth/src/crypto/mod.rs +++ b/biscuit-auth/src/crypto/mod.rs @@ -281,7 +281,7 @@ pub fn sign( ) -> Result { //FIXME: replace with SHA512 hashing let mut to_sign = message.to_vec(); - to_sign.extend(&(crate::format::schema::public_key::Algorithm::Ed25519 as i32).to_le_bytes()); + to_sign.extend(&(next_key.algorithm() as i32).to_le_bytes()); to_sign.extend(&next_key.public().to_bytes()); let signature = keypair @@ -301,15 +301,14 @@ pub fn verify_block_signature(block: &Block, public_key: &PublicKey) -> Result<( if let Some(signature) = block.external_signature.as_ref() { to_verify.extend_from_slice(&signature.signature.to_bytes()); } - to_verify.extend(&(crate::format::schema::public_key::Algorithm::Ed25519 as i32).to_le_bytes()); + to_verify.extend(&(block.next_key.algorithm() as i32).to_le_bytes()); to_verify.extend(&block.next_key.to_bytes()); public_key.verify_signature(&to_verify, &block.signature)?; if let Some(external_signature) = block.external_signature.as_ref() { let mut to_verify = block.data.to_vec(); - to_verify - .extend(&(crate::format::schema::public_key::Algorithm::Ed25519 as i32).to_le_bytes()); + to_verify.extend(&(public_key.algorithm() as i32).to_le_bytes()); to_verify.extend(&public_key.to_bytes()); external_signature @@ -319,101 +318,6 @@ pub fn verify_block_signature(block: &Block, public_key: &PublicKey) -> Result<( Ok(()) } -/* -impl Token { - #[allow(dead_code)] - pub fn new( - keypair: &KeyPair, - next_key: &KeyPair, - message: &[u8], - ) -> Result { - let signature = sign(keypair, next_key, message)?; - - let block = Block { - data: message.to_vec(), - next_key: next_key.public(), - signature, - external_signature: None, - }; - - Ok(Token { - root: keypair.public(), - blocks: vec![block], - next: TokenNext::Secret(next_key.private()), - }) - } - - #[allow(dead_code)] - pub fn append( - &self, - next_key: &KeyPair, - message: &[u8], - external_signature: Option, - ) -> Result { - let keypair = match self.next.keypair() { - Err(error::Token::AlreadySealed) => Err(error::Token::AppendOnSealed), - other => other, - }?; - - let signature = sign(&keypair, next_key, message)?; - - let block = Block { - data: message.to_vec(), - next_key: next_key.public(), - signature, - external_signature, - }; - - let mut t = Token { - root: self.root, - blocks: self.blocks.clone(), - next: TokenNext::Secret(next_key.private()), - }; - - t.blocks.push(block); - - Ok(t) - } - - #[allow(dead_code)] - pub fn verify(&self, root: PublicKey) -> Result<(), error::Token> { - //FIXME: try batched signature verification - let mut current_pub = root; - - for block in &self.blocks { - verify_block_signature(block, ¤t_pub)?; - current_pub = block.next_key; - } - - match &self.next { - TokenNext::Secret(private) => { - if current_pub != private.public() { - return Err(error::Format::Signature(error::Signature::InvalidSignature( - "the last public key does not match the private key".to_string(), - )) - .into()); - } - } - TokenNext::Seal(signature) => { - //FIXME: replace with SHA512 hashing - let mut to_verify = Vec::new(); - for block in &self.blocks { - to_verify.extend(&block.data); - to_verify.extend(&block.next_key.to_bytes()); - } - - current_pub - .0 - .verify_strict(&to_verify, &signature.0) - .map_err(|s| s.to_string()) - .map_err(error::Signature::InvalidSignature) - .map_err(error::Format::Signature)?; - } - } - - Ok(()) - } -}*/ impl TokenNext { pub fn keypair(&self) -> Result { @@ -430,99 +334,3 @@ impl TokenNext { } } } - -#[cfg(test)] -mod tests { - /* - use super::*; - use rand::prelude::*; - use rand_core::SeedableRng; - - #[test] - fn basic_signature() { - let mut rng: StdRng = SeedableRng::seed_from_u64(0); - - let message = b"hello world"; - let keypair = KeyPair::new_with_rng(&mut rng); - - let signature = keypair.sign(&mut rng, message); - - assert!(verify(&keypair.public, message, &signature)); - - assert!(!verify(&keypair.public, b"AAAA", &signature)); - } - - #[test] - fn three_messages() { - //let mut rng: OsRng = OsRng::new().unwrap(); - //keep the same values in tests - let mut rng: StdRng = SeedableRng::seed_from_u64(0); - - let message1 = b"hello"; - let keypair1 = KeyPair::new_with_rng(&mut rng); - - let token1 = Token::new(&mut rng, &keypair1, &message1[..]); - - assert_eq!(token1.verify(), Ok(()), "cannot verify first token"); - - println!("will derive a second token"); - - let message2 = b"world"; - let keypair2 = KeyPair::new_with_rng(&mut rng); - - let token2 = token1.append(&mut rng, &keypair2, &message2[..]); - - assert_eq!(token2.verify(), Ok(()), "cannot verify second token"); - - println!("will derive a third token"); - - let message3 = b"!!!"; - let keypair3 = KeyPair::new_with_rng(&mut rng); - - let token3 = token2.append(&mut rng, &keypair3, &message3[..]); - - assert_eq!(token3.verify(), Ok(()), "cannot verify third token"); - } - - #[test] - fn change_message() { - //let mut rng: OsRng = OsRng::new().unwrap(); - //keep the same values in tests - let mut rng: StdRng = SeedableRng::seed_from_u64(0); - - let message1 = b"hello"; - let keypair1 = KeyPair::new_with_rng(&mut rng); - - let token1 = Token::new(&mut rng, &keypair1, &message1[..]); - - assert_eq!(token1.verify(), Ok(()), "cannot verify first token"); - - println!("will derive a second token"); - - let message2 = b"world"; - let keypair2 = KeyPair::new_with_rng(&mut rng); - - let mut token2 = token1.append(&mut rng, &keypair2, &message2[..]); - - token2.messages[1] = Vec::from(&b"you"[..]); - - assert_eq!( - token2.verify(), - Err(error::Signature::InvalidSignature), - "second token should not be valid" - ); - - println!("will derive a third token"); - - let message3 = b"!!!"; - let keypair3 = KeyPair::new_with_rng(&mut rng); - - let token3 = token2.append(&mut rng, &keypair3, &message3[..]); - - assert_eq!( - token3.verify(), - Err(error::Signature::InvalidSignature), - "cannot verify third token" - ); - }*/ -} From 13581cfa162d1d0809f5a0db382aa97a354dbc4d Mon Sep 17 00:00:00 2001 From: Geoffroy Couprie Date: Tue, 11 Oct 2022 21:16:23 +0200 Subject: [PATCH 05/22] start the boilerplate around managing multiple types of keys --- biscuit-auth/src/crypto/mod.rs | 158 +++++++++++++-------------------- 1 file changed, 63 insertions(+), 95 deletions(-) diff --git a/biscuit-auth/src/crypto/mod.rs b/biscuit-auth/src/crypto/mod.rs index 19aadafb..bf6e3d6c 100644 --- a/biscuit-auth/src/crypto/mod.rs +++ b/biscuit-auth/src/crypto/mod.rs @@ -7,19 +7,17 @@ //! //! The implementation is based on [ed25519_dalek](https://github.com/dalek-cryptography/ed25519-dalek). #![allow(non_snake_case)] -use crate::{error::Format, format::schema}; +use crate::format::schema; use super::error; -use ed25519_dalek::Signer; -use ed25519_dalek::*; +mod ed25519; use rand_core::{CryptoRng, RngCore}; -use std::{convert::TryInto, hash::Hash, ops::Drop}; -use zeroize::Zeroize; +use std::hash::Hash; /// pair of cryptographic keys used to sign a token's block #[derive(Debug)] -pub struct KeyPair { - kp: ed25519_dalek::Keypair, +pub enum KeyPair { + Ed25519(ed25519::KeyPair), } impl KeyPair { @@ -28,44 +26,37 @@ impl KeyPair { } pub fn new_with_rng(rng: &mut T) -> Self { - let kp = ed25519_dalek::Keypair::generate(rng); - - KeyPair { kp } + KeyPair::Ed25519(ed25519::KeyPair::new_with_rng(rng)) } pub fn from(key: &PrivateKey) -> Self { - let secret = SecretKey::from_bytes(&key.0.to_bytes()).unwrap(); - - let public = (&key.0).into(); - - KeyPair { - kp: ed25519_dalek::Keypair { secret, public }, + match key { + PrivateKey::Ed25519(key) => KeyPair::Ed25519(ed25519::KeyPair::from(key)), } } pub fn sign(&self, data: &[u8]) -> Result { - Ok(Signature( - self.kp - .try_sign(&data) - .map_err(|s| s.to_string()) - .map_err(error::Signature::InvalidSignatureGeneration) - .map_err(error::Format::Signature)? - .to_bytes() - .to_vec(), - )) + match self { + KeyPair::Ed25519(key) => key.sign(data), + } } pub fn private(&self) -> PrivateKey { - let secret = SecretKey::from_bytes(&self.kp.secret.to_bytes()).unwrap(); - PrivateKey(secret) + match self { + KeyPair::Ed25519(key) => PrivateKey::Ed25519(key.private()), + } } pub fn public(&self) -> PublicKey { - PublicKey(self.kp.public) + match self { + KeyPair::Ed25519(key) => PublicKey::Ed25519(key.public()), + } } - pub fn algorithm(&self) -> crate::format::schema::public_key::Algorithm { - crate::format::schema::public_key::Algorithm::Ed25519 + pub fn algorithm(&self) -> crate::format::schema::private_key::Algorithm { + match self { + KeyPair::Ed25519(_) => crate::format::schema::private_key::Algorithm::Ed25519, + } } } @@ -75,20 +66,18 @@ impl std::default::Default for KeyPair { } } -impl Drop for KeyPair { - fn drop(&mut self) { - self.kp.secret.zeroize(); - } -} - /// the private part of a [KeyPair] #[derive(Debug)] -pub struct PrivateKey(ed25519_dalek::SecretKey); +pub enum PrivateKey { + Ed25519(ed25519::PrivateKey), +} impl PrivateKey { /// serializes to a byte array pub fn to_bytes(&self) -> [u8; 32] { - self.0.to_bytes() + match self { + PrivateKey::Ed25519(key) => key.to_bytes(), + } } /// serializes to an hex-encoded string @@ -98,13 +87,7 @@ impl PrivateKey { /// deserializes from a byte array pub fn from_bytes(bytes: &[u8]) -> Result { - let bytes: [u8; 32] = bytes - .try_into() - .map_err(|_| Format::InvalidKeySize(bytes.len()))?; - SecretKey::from_bytes(&bytes) - .map(PrivateKey) - .map_err(|s| s.to_string()) - .map_err(Format::InvalidKey) + Ok(PrivateKey::Ed25519(ed25519::PrivateKey::from_bytes(bytes)?)) } /// deserializes from an hex-encoded string @@ -121,16 +104,29 @@ impl PrivateKey { ))); } - PrivateKey::from_bytes(&key.key) + Ok(PrivateKey::Ed25519(ed25519::PrivateKey::from_bytes( + &key.key, + )?)) + } + + pub fn to_proto(&self) -> schema::PrivateKey { + schema::PrivateKey { + algorithm: schema::private_key::Algorithm::Ed25519 as i32, + key: self.to_bytes().to_vec(), + } } /// returns the matching public key pub fn public(&self) -> PublicKey { - PublicKey((&self.0).into()) + match self { + PrivateKey::Ed25519(key) => PublicKey::Ed25519(key.public()), + } } - pub fn algorithm(&self) -> crate::format::schema::public_key::Algorithm { - crate::format::schema::public_key::Algorithm::Ed25519 + pub fn algorithm(&self) -> crate::format::schema::private_key::Algorithm { + match self { + PrivateKey::Ed25519(_) => crate::format::schema::private_key::Algorithm::Ed25519, + } } } @@ -140,20 +136,18 @@ impl std::clone::Clone for PrivateKey { } } -impl Drop for PrivateKey { - fn drop(&mut self) { - self.0.zeroize(); - } -} - /// the public part of a [KeyPair] -#[derive(Debug, Clone, Copy, Eq)] -pub struct PublicKey(ed25519_dalek::PublicKey); +#[derive(Debug, Clone, Copy, PartialEq, Hash, Eq)] +pub enum PublicKey { + Ed25519(ed25519::PublicKey), +} impl PublicKey { /// serializes to a byte array pub fn to_bytes(&self) -> [u8; 32] { - self.0.to_bytes() + match self { + PublicKey::Ed25519(key) => key.to_bytes(), + } } /// serializes to an hex-encoded string @@ -163,10 +157,7 @@ impl PublicKey { /// deserializes from a byte array pub fn from_bytes(bytes: &[u8]) -> Result { - ed25519_dalek::PublicKey::from_bytes(bytes) - .map(PublicKey) - .map_err(|s| s.to_string()) - .map_err(Format::InvalidKey) + Ok(PublicKey::Ed25519(ed25519::PublicKey::from_bytes(bytes)?)) } /// deserializes from an hex-encoded string @@ -183,7 +174,9 @@ impl PublicKey { ))); } - PublicKey::from_bytes(&key.key) + Ok(PublicKey::Ed25519(ed25519::PublicKey::from_bytes( + &key.key, + )?)) } pub fn to_proto(&self) -> schema::PublicKey { @@ -198,22 +191,15 @@ impl PublicKey { data: &[u8], signature: &Signature, ) -> Result<(), error::Format> { - let sig = ed25519_dalek::Signature::from_bytes(&signature.0).map_err(|e| { - error::Format::BlockSignatureDeserializationError(format!( - "block signature deserialization error: {:?}", - e - )) - })?; - - self.0 - .verify_strict(&data, &sig) - .map_err(|s| s.to_string()) - .map_err(error::Signature::InvalidSignature) - .map_err(error::Format::Signature) + match self { + PublicKey::Ed25519(key) => key.verify_signature(data, signature), + } } pub fn algorithm(&self) -> crate::format::schema::public_key::Algorithm { - crate::format::schema::public_key::Algorithm::Ed25519 + match self { + PublicKey::Ed25519(_) => crate::format::schema::public_key::Algorithm::Ed25519, + } } pub fn print(&self) -> String { @@ -221,19 +207,6 @@ impl PublicKey { } } -impl PartialEq for PublicKey { - fn eq(&self, other: &Self) -> bool { - self.0.to_bytes() == other.0.to_bytes() - } -} - -impl Hash for PublicKey { - fn hash(&self, state: &mut H) { - (crate::format::schema::public_key::Algorithm::Ed25519 as i32).hash(state); - self.0.to_bytes().hash(state); - } -} - #[derive(Clone, Debug)] pub struct Signature(pub(crate) Vec); @@ -284,12 +257,7 @@ pub fn sign( to_sign.extend(&(next_key.algorithm() as i32).to_le_bytes()); to_sign.extend(&next_key.public().to_bytes()); - let signature = keypair - .kp - .try_sign(&to_sign) - .map_err(|s| s.to_string()) - .map_err(error::Signature::InvalidSignatureGeneration) - .map_err(error::Format::Signature)?; + let signature = keypair.sign(&to_sign)?; Ok(Signature(signature.to_bytes().to_vec())) } From be9e6a4414fb82fb0ae9ce64bf96fb83b5dfaa10 Mon Sep 17 00:00:00 2001 From: Geoffroy Couprie Date: Tue, 11 Oct 2022 21:17:12 +0200 Subject: [PATCH 06/22] start support for p256 signatures --- biscuit-auth/Cargo.toml | 2 + biscuit-auth/src/crypto/mod.rs | 115 ++++++++++++++++----------- biscuit-auth/src/format/mod.rs | 7 +- biscuit-auth/src/format/schema.proto | 13 +-- biscuit-auth/src/format/schema.rs | 1 + 5 files changed, 76 insertions(+), 62 deletions(-) diff --git a/biscuit-auth/Cargo.toml b/biscuit-auth/Cargo.toml index bfea503d..6e59efd5 100644 --- a/biscuit-auth/Cargo.toml +++ b/biscuit-auth/Cargo.toml @@ -42,6 +42,8 @@ getrandom = { version = "0.1.16" } time = {version = "0.3.7", features = ["formatting", "parsing"]} biscuit-parser = { version = "0.1.0-alpha2", path = "../biscuit-parser" } biscuit-quote = { version = "0.2.0-alpha2", optional = true, path = "../biscuit-quote" } +ecdsa = { version = "0.14.8", features = ["sign", "verify", "pem", "alloc", "pkcs8", "serde"] } +p256 = "0.11.1" [dev-dependencies] diff --git a/biscuit-auth/src/crypto/mod.rs b/biscuit-auth/src/crypto/mod.rs index bf6e3d6c..ab2ad59f 100644 --- a/biscuit-auth/src/crypto/mod.rs +++ b/biscuit-auth/src/crypto/mod.rs @@ -11,6 +11,7 @@ use crate::format::schema; use super::error; mod ed25519; +mod p256; use rand_core::{CryptoRng, RngCore}; use std::hash::Hash; @@ -18,6 +19,7 @@ use std::hash::Hash; #[derive(Debug)] pub enum KeyPair { Ed25519(ed25519::KeyPair), + P256(p256::KeyPair), } impl KeyPair { @@ -29,33 +31,57 @@ impl KeyPair { KeyPair::Ed25519(ed25519::KeyPair::new_with_rng(rng)) } + pub fn new_p256() -> Self { + KeyPair::P256(p256::KeyPair::new()) + } + pub fn from(key: &PrivateKey) -> Self { match key { PrivateKey::Ed25519(key) => KeyPair::Ed25519(ed25519::KeyPair::from(key)), + PrivateKey::P256(key) => KeyPair::P256(p256::KeyPair::from(key)), + } + } + + /// deserializes from a byte array + pub fn from_bytes( + bytes: &[u8], + algorithm: schema::public_key::Algorithm, + ) -> Result { + match algorithm { + schema::public_key::Algorithm::Ed25519 => { + Ok(KeyPair::Ed25519(ed25519::KeyPair::from_bytes(bytes)?)) + } + schema::public_key::Algorithm::P256 => { + Ok(KeyPair::P256(p256::KeyPair::from_bytes(bytes)?)) + } } } pub fn sign(&self, data: &[u8]) -> Result { match self { KeyPair::Ed25519(key) => key.sign(data), + KeyPair::P256(key) => key.sign(data), } } pub fn private(&self) -> PrivateKey { match self { KeyPair::Ed25519(key) => PrivateKey::Ed25519(key.private()), + KeyPair::P256(key) => PrivateKey::P256(key.private()), } } pub fn public(&self) -> PublicKey { match self { KeyPair::Ed25519(key) => PublicKey::Ed25519(key.public()), + KeyPair::P256(key) => PublicKey::P256(key.public()), } } - pub fn algorithm(&self) -> crate::format::schema::private_key::Algorithm { + pub fn algorithm(&self) -> crate::format::schema::public_key::Algorithm { match self { - KeyPair::Ed25519(_) => crate::format::schema::private_key::Algorithm::Ed25519, + KeyPair::Ed25519(_) => crate::format::schema::public_key::Algorithm::Ed25519, + KeyPair::P256(_) => crate::format::schema::public_key::Algorithm::P256, } } } @@ -67,9 +93,10 @@ impl std::default::Default for KeyPair { } /// the private part of a [KeyPair] -#[derive(Debug)] +#[derive(Debug, Clone)] pub enum PrivateKey { Ed25519(ed25519::PrivateKey), + P256(p256::PrivateKey), } impl PrivateKey { @@ -77,6 +104,7 @@ impl PrivateKey { pub fn to_bytes(&self) -> [u8; 32] { match self { PrivateKey::Ed25519(key) => key.to_bytes(), + PrivateKey::P256(key) => key.to_bytes(), } } @@ -86,67 +114,58 @@ impl PrivateKey { } /// deserializes from a byte array - pub fn from_bytes(bytes: &[u8]) -> Result { - Ok(PrivateKey::Ed25519(ed25519::PrivateKey::from_bytes(bytes)?)) + pub fn from_bytes( + bytes: &[u8], + algorithm: schema::public_key::Algorithm, + ) -> Result { + match algorithm { + schema::public_key::Algorithm::Ed25519 => { + Ok(PrivateKey::Ed25519(ed25519::PrivateKey::from_bytes(bytes)?)) + } + schema::public_key::Algorithm::P256 => { + Ok(PrivateKey::P256(p256::PrivateKey::from_bytes(bytes)?)) + } + } } /// deserializes from an hex-encoded string - pub fn from_bytes_hex(str: &str) -> Result { + pub fn from_bytes_hex( + str: &str, + algorithm: schema::public_key::Algorithm, + ) -> Result { let bytes = hex::decode(str).map_err(|e| error::Format::InvalidKey(e.to_string()))?; - Self::from_bytes(&bytes) - } - - pub fn from_proto(key: &schema::PrivateKey) -> Result { - if key.algorithm != schema::private_key::Algorithm::Ed25519 as i32 { - return Err(error::Format::DeserializationError(format!( - "deserialization error: unexpected key algorithm {}", - key.algorithm - ))); - } - - Ok(PrivateKey::Ed25519(ed25519::PrivateKey::from_bytes( - &key.key, - )?)) - } - - pub fn to_proto(&self) -> schema::PrivateKey { - schema::PrivateKey { - algorithm: schema::private_key::Algorithm::Ed25519 as i32, - key: self.to_bytes().to_vec(), - } + Self::from_bytes(&bytes, algorithm) } /// returns the matching public key pub fn public(&self) -> PublicKey { match self { PrivateKey::Ed25519(key) => PublicKey::Ed25519(key.public()), + PrivateKey::P256(key) => PublicKey::P256(key.public()), } } - pub fn algorithm(&self) -> crate::format::schema::private_key::Algorithm { + pub fn algorithm(&self) -> crate::format::schema::public_key::Algorithm { match self { - PrivateKey::Ed25519(_) => crate::format::schema::private_key::Algorithm::Ed25519, + PrivateKey::Ed25519(_) => crate::format::schema::public_key::Algorithm::Ed25519, + PrivateKey::P256(_) => crate::format::schema::public_key::Algorithm::P256, } } } -impl std::clone::Clone for PrivateKey { - fn clone(&self) -> Self { - PrivateKey::from_bytes(&self.to_bytes()).unwrap() - } -} - /// the public part of a [KeyPair] #[derive(Debug, Clone, Copy, PartialEq, Hash, Eq)] pub enum PublicKey { Ed25519(ed25519::PublicKey), + P256(p256::PublicKey), } impl PublicKey { /// serializes to a byte array - pub fn to_bytes(&self) -> [u8; 32] { + pub fn to_bytes(&self) -> Vec { match self { - PublicKey::Ed25519(key) => key.to_bytes(), + PublicKey::Ed25519(key) => key.to_bytes().into(), + PublicKey::P256(key) => key.to_bytes(), } } @@ -167,22 +186,24 @@ impl PublicKey { } pub fn from_proto(key: &schema::PublicKey) -> Result { - if key.algorithm != schema::public_key::Algorithm::Ed25519 as i32 { - return Err(error::Format::DeserializationError(format!( + if key.algorithm == schema::public_key::Algorithm::Ed25519 as i32 { + Ok(PublicKey::Ed25519(ed25519::PublicKey::from_bytes( + &key.key, + )?)) + } else if key.algorithm == schema::public_key::Algorithm::P256 as i32 { + Ok(PublicKey::P256(p256::PublicKey::from_bytes(&key.key)?)) + } else { + Err(error::Format::DeserializationError(format!( "deserialization error: unexpected key algorithm {}", key.algorithm - ))); + ))) } - - Ok(PublicKey::Ed25519(ed25519::PublicKey::from_bytes( - &key.key, - )?)) } pub fn to_proto(&self) -> schema::PublicKey { schema::PublicKey { - algorithm: schema::public_key::Algorithm::Ed25519 as i32, - key: self.to_bytes().to_vec(), + algorithm: self.algorithm() as i32, + key: self.to_bytes(), } } @@ -193,12 +214,14 @@ impl PublicKey { ) -> Result<(), error::Format> { match self { PublicKey::Ed25519(key) => key.verify_signature(data, signature), + PublicKey::P256(key) => key.verify_signature(data, signature), } } pub fn algorithm(&self) -> crate::format::schema::public_key::Algorithm { match self { PublicKey::Ed25519(_) => crate::format::schema::public_key::Algorithm::Ed25519, + PublicKey::P256(_) => crate::format::schema::public_key::Algorithm::P256, } } diff --git a/biscuit-auth/src/format/mod.rs b/biscuit-auth/src/format/mod.rs index 4b13c110..38113d4b 100644 --- a/biscuit-auth/src/format/mod.rs +++ b/biscuit-auth/src/format/mod.rs @@ -57,6 +57,7 @@ impl SerializedBiscuit { })?; let next_key = PublicKey::from_proto(&data.authority.next_key)?; + let mut next_key_algorithm = next_key.algorithm(); let bytes: [u8; 64] = (&data.authority.signature[..]) .try_into() @@ -80,6 +81,7 @@ impl SerializedBiscuit { let mut blocks = Vec::new(); for block in &data.blocks { let next_key = PublicKey::from_proto(&block.next_key)?; + next_key_algorithm = next_key.algorithm(); let bytes: [u8; 64] = (&block.signature[..]) .try_into() @@ -119,10 +121,7 @@ impl SerializedBiscuit { )) } Some(schema::proof::Content::NextSecret(v)) => { - TokenNext::Secret(PrivateKey::from_bytes(&v)?) - } - Some(schema::proof::Content::NextSecretv3(key)) => { - TokenNext::Secret(PrivateKey::from_proto(&key)?) + TokenNext::Secret(PrivateKey::from_bytes(&v, next_key_algorithm)?) } Some(schema::proof::Content::FinalSignature(v)) => { let bytes: [u8; 64] = (&v[..]) diff --git a/biscuit-auth/src/format/schema.proto b/biscuit-auth/src/format/schema.proto index be32c1de..548ea953 100644 --- a/biscuit-auth/src/format/schema.proto +++ b/biscuit-auth/src/format/schema.proto @@ -26,16 +26,7 @@ message PublicKey { enum Algorithm { Ed25519 = 0; - } - - required bytes key = 2; -} - -message PrivateKey { - required Algorithm algorithm = 1; - - enum Algorithm { - Ed25519 = 0; + P256 = 1; } required bytes key = 2; @@ -43,10 +34,8 @@ message PrivateKey { message Proof { oneof Content { - // deprecated bytes nextSecret = 1; bytes finalSignature = 2; - PrivateKey nextSecretv3 = 3; } } diff --git a/biscuit-auth/src/format/schema.rs b/biscuit-auth/src/format/schema.rs index 288e6f27..33548981 100644 --- a/biscuit-auth/src/format/schema.rs +++ b/biscuit-auth/src/format/schema.rs @@ -40,6 +40,7 @@ pub mod public_key { #[repr(i32)] pub enum Algorithm { Ed25519 = 0, + P256 = 1, } } #[derive(Clone, PartialEq, ::prost::Message)] From d0113b33aaf94099546eef63524b8bc769d06be5 Mon Sep 17 00:00:00 2001 From: Geoffroy Couprie Date: Sat, 15 Oct 2022 00:55:57 +0200 Subject: [PATCH 07/22] p256 support --- biscuit-auth/Cargo.toml | 1 + biscuit-auth/src/crypto/ed25519.rs | 238 +++++++++++++++++++++++++++++ biscuit-auth/src/crypto/mod.rs | 4 +- biscuit-auth/src/crypto/p256.rs | 237 ++++++++++++++++++++++++++++ 4 files changed, 478 insertions(+), 2 deletions(-) create mode 100644 biscuit-auth/src/crypto/ed25519.rs create mode 100644 biscuit-auth/src/crypto/p256.rs diff --git a/biscuit-auth/Cargo.toml b/biscuit-auth/Cargo.toml index 6e59efd5..fb176c8e 100644 --- a/biscuit-auth/Cargo.toml +++ b/biscuit-auth/Cargo.toml @@ -44,6 +44,7 @@ biscuit-parser = { version = "0.1.0-alpha2", path = "../biscuit-parser" } biscuit-quote = { version = "0.2.0-alpha2", optional = true, path = "../biscuit-quote" } ecdsa = { version = "0.14.8", features = ["sign", "verify", "pem", "alloc", "pkcs8", "serde"] } p256 = "0.11.1" +pkcs8 = "0.9.0" [dev-dependencies] diff --git a/biscuit-auth/src/crypto/ed25519.rs b/biscuit-auth/src/crypto/ed25519.rs new file mode 100644 index 00000000..c3aaa3f6 --- /dev/null +++ b/biscuit-auth/src/crypto/ed25519.rs @@ -0,0 +1,238 @@ +//! cryptographic operations +//! +//! Biscuit tokens are based on a chain of Ed25519 signatures. +//! This provides the fundamental operation for offline delegation: from a message +//! and a valid signature, it is possible to add a new message and produce a valid +//! signature for the whole. +//! +//! The implementation is based on [ed25519_dalek](https://github.com/dalek-cryptography/ed25519-dalek). +#![allow(non_snake_case)] +use crate::{error::Format, format::schema}; + +use super::error; +use super::Signature; +use ed25519_dalek::Signer; +use ed25519_dalek::*; +use rand_core::{CryptoRng, RngCore}; +use std::{convert::TryInto, hash::Hash, ops::Drop}; +use zeroize::Zeroize; + +/// pair of cryptographic keys used to sign a token's block +#[derive(Debug)] +pub struct KeyPair { + kp: ed25519_dalek::Keypair, +} + +impl KeyPair { + pub fn new() -> Self { + Self::new_with_rng(&mut rand::rngs::OsRng) + } + + pub fn new_with_rng(rng: &mut T) -> Self { + let kp = ed25519_dalek::Keypair::generate(rng); + + KeyPair { kp } + } + + pub fn from(key: &PrivateKey) -> Self { + let secret = SecretKey::from_bytes(&key.0.to_bytes()).unwrap(); + + let public = (&key.0).into(); + + KeyPair { + kp: ed25519_dalek::Keypair { secret, public }, + } + } + + /// deserializes from a byte array + pub fn from_bytes(bytes: &[u8]) -> Result { + let secret = SecretKey::from_bytes(bytes) + .map_err(|s| s.to_string()) + .map_err(Format::InvalidKey)?; + + let public = (&secret).into(); + + Ok(KeyPair { + kp: ed25519_dalek::Keypair { secret, public }, + }) + } + + pub fn sign(&self, data: &[u8]) -> Result { + Ok(Signature( + self.kp + .try_sign(&data) + .map_err(|s| s.to_string()) + .map_err(error::Signature::InvalidSignatureGeneration) + .map_err(error::Format::Signature)? + .to_bytes() + .to_vec(), + )) + } + + pub fn private(&self) -> PrivateKey { + let secret = SecretKey::from_bytes(&self.kp.secret.to_bytes()).unwrap(); + PrivateKey(secret) + } + + pub fn public(&self) -> PublicKey { + PublicKey(self.kp.public) + } + + pub fn algorithm(&self) -> crate::format::schema::public_key::Algorithm { + crate::format::schema::public_key::Algorithm::Ed25519 + } +} + +impl std::default::Default for KeyPair { + fn default() -> Self { + Self::new() + } +} + +impl Drop for KeyPair { + fn drop(&mut self) { + self.kp.secret.zeroize(); + } +} + +/// the private part of a [KeyPair] +#[derive(Debug)] +pub struct PrivateKey(ed25519_dalek::SecretKey); + +impl PrivateKey { + /// serializes to a byte array + pub fn to_bytes(&self) -> Vec { + self.0.to_bytes().to_vec() + } + + /// serializes to an hex-encoded string + pub fn to_bytes_hex(&self) -> String { + hex::encode(self.to_bytes()) + } + + /// deserializes from a byte array + pub fn from_bytes(bytes: &[u8]) -> Result { + let bytes: [u8; 32] = bytes + .try_into() + .map_err(|_| Format::InvalidKeySize(bytes.len()))?; + SecretKey::from_bytes(&bytes) + .map(PrivateKey) + .map_err(|s| s.to_string()) + .map_err(Format::InvalidKey) + } + + /// deserializes from an hex-encoded string + pub fn from_bytes_hex(str: &str) -> Result { + let bytes = hex::decode(str).map_err(|e| error::Format::InvalidKey(e.to_string()))?; + Self::from_bytes(&bytes) + } + + /// returns the matching public key + pub fn public(&self) -> PublicKey { + PublicKey((&self.0).into()) + } + + pub fn algorithm(&self) -> crate::format::schema::public_key::Algorithm { + crate::format::schema::public_key::Algorithm::Ed25519 + } +} + +impl std::clone::Clone for PrivateKey { + fn clone(&self) -> Self { + PrivateKey::from_bytes(&self.to_bytes()).unwrap() + } +} + +impl Drop for PrivateKey { + fn drop(&mut self) { + self.0.zeroize(); + } +} + +/// the public part of a [KeyPair] +#[derive(Debug, Clone, Copy, Eq)] +pub struct PublicKey(ed25519_dalek::PublicKey); + +impl PublicKey { + /// serializes to a byte array + pub fn to_bytes(&self) -> [u8; 32] { + self.0.to_bytes() + } + + /// serializes to an hex-encoded string + pub fn to_bytes_hex(&self) -> String { + hex::encode(self.to_bytes()) + } + + /// deserializes from a byte array + pub fn from_bytes(bytes: &[u8]) -> Result { + ed25519_dalek::PublicKey::from_bytes(bytes) + .map(PublicKey) + .map_err(|s| s.to_string()) + .map_err(Format::InvalidKey) + } + + /// deserializes from an hex-encoded string + pub fn from_bytes_hex(str: &str) -> Result { + let bytes = hex::decode(str).map_err(|e| error::Format::InvalidKey(e.to_string()))?; + Self::from_bytes(&bytes) + } + + pub fn from_proto(key: &schema::PublicKey) -> Result { + if key.algorithm != schema::public_key::Algorithm::Ed25519 as i32 { + return Err(error::Format::DeserializationError(format!( + "deserialization error: unexpected key algorithm {}", + key.algorithm + ))); + } + + PublicKey::from_bytes(&key.key) + } + + pub fn to_proto(&self) -> schema::PublicKey { + schema::PublicKey { + algorithm: schema::public_key::Algorithm::Ed25519 as i32, + key: self.to_bytes().to_vec(), + } + } + + pub fn verify_signature( + &self, + data: &[u8], + signature: &Signature, + ) -> Result<(), error::Format> { + let sig = ed25519_dalek::Signature::from_bytes(&signature.0).map_err(|e| { + error::Format::BlockSignatureDeserializationError(format!( + "block signature deserialization error: {:?}", + e + )) + })?; + + self.0 + .verify_strict(&data, &sig) + .map_err(|s| s.to_string()) + .map_err(error::Signature::InvalidSignature) + .map_err(error::Format::Signature) + } + + pub fn algorithm(&self) -> crate::format::schema::public_key::Algorithm { + crate::format::schema::public_key::Algorithm::Ed25519 + } + + pub fn print(&self) -> String { + format!("ed25519/{}", hex::encode(&self.to_bytes())) + } +} + +impl PartialEq for PublicKey { + fn eq(&self, other: &Self) -> bool { + self.0.to_bytes() == other.0.to_bytes() + } +} + +impl Hash for PublicKey { + fn hash(&self, state: &mut H) { + (crate::format::schema::public_key::Algorithm::Ed25519 as i32).hash(state); + self.0.to_bytes().hash(state); + } +} diff --git a/biscuit-auth/src/crypto/mod.rs b/biscuit-auth/src/crypto/mod.rs index ab2ad59f..49defb61 100644 --- a/biscuit-auth/src/crypto/mod.rs +++ b/biscuit-auth/src/crypto/mod.rs @@ -101,9 +101,9 @@ pub enum PrivateKey { impl PrivateKey { /// serializes to a byte array - pub fn to_bytes(&self) -> [u8; 32] { + pub fn to_bytes(&self) -> zeroize::Zeroizing> { match self { - PrivateKey::Ed25519(key) => key.to_bytes(), + PrivateKey::Ed25519(key) => zeroize::Zeroizing::new(key.to_bytes()), PrivateKey::P256(key) => key.to_bytes(), } } diff --git a/biscuit-auth/src/crypto/p256.rs b/biscuit-auth/src/crypto/p256.rs new file mode 100644 index 00000000..86ddb15b --- /dev/null +++ b/biscuit-auth/src/crypto/p256.rs @@ -0,0 +1,237 @@ +#![allow(non_snake_case)] +use crate::{error::Format, format::schema}; + +use super::error; +use super::Signature; + +use p256::ecdsa::{signature::Signer, signature::Verifier, SigningKey, VerifyingKey}; +use p256::elliptic_curve::rand_core::{CryptoRng, OsRng, RngCore}; +use p256::elliptic_curve::sec1::FromEncodedPoint; +use p256::pkcs8::{DecodePublicKey, EncodePrivateKey, EncodePublicKey}; +use pkcs8::DecodePrivateKey; +use std::convert::TryFrom; +use std::hash::Hash; + +/// pair of cryptographic keys used to sign a token's block +#[derive(Debug)] +pub struct KeyPair { + kp: SigningKey, +} + +impl KeyPair { + pub fn new() -> Self { + Self::new_with_rng(&mut OsRng) + } + + pub fn new_with_rng(rng: &mut T) -> Self { + let kp = SigningKey::random(rng); + + KeyPair { kp } + } + + pub fn from(key: &PrivateKey) -> Self { + let kp = SigningKey::from_pkcs8_der(&key.0.to_bytes()).unwrap(); + + KeyPair { kp } + } + + /// deserializes from a byte array + pub fn from_bytes(bytes: &[u8]) -> Result { + let kp = SigningKey::from_pkcs8_der(bytes) + .map_err(|s| s.to_string()) + .map_err(Format::InvalidKey)?; + + Ok(KeyPair { kp }) + } + + pub fn sign(&self, data: &[u8]) -> Result { + Ok(Signature( + self.kp + .try_sign(&data) + .map_err(|s| s.to_string()) + .map_err(error::Signature::InvalidSignatureGeneration) + .map_err(error::Format::Signature)? + .to_vec(), + )) + } + + pub fn private(&self) -> PrivateKey { + let secret = SigningKey::from_bytes(&self.kp.to_bytes()).unwrap(); + PrivateKey(secret) + } + + pub fn public(&self) -> PublicKey { + PublicKey(self.kp.verifying_key()) + } + + pub fn algorithm(&self) -> crate::format::schema::public_key::Algorithm { + crate::format::schema::public_key::Algorithm::P256 + } +} + +impl std::default::Default for KeyPair { + fn default() -> Self { + Self::new() + } +} + +/// the private part of a [KeyPair] +#[derive(Debug)] +pub struct PrivateKey(SigningKey); + +impl PrivateKey { + /// serializes to a byte array + pub fn to_bytes(&self) -> zeroize::Zeroizing> { + self.0.to_pkcs8_der().unwrap().to_bytes() + //self.0.to_bytes().into() + } + + /// serializes to an hex-encoded string + pub fn to_bytes_hex(&self) -> String { + hex::encode(self.to_bytes()) + } + + /// deserializes from a byte array + pub fn from_bytes(bytes: &[u8]) -> Result { + SigningKey::from_pkcs8_der(&bytes) + .map(PrivateKey) + .map_err(|s| s.to_string()) + .map_err(Format::InvalidKey) + } + + /// deserializes from an hex-encoded string + pub fn from_bytes_hex(str: &str) -> Result { + let bytes = hex::decode(str).map_err(|e| error::Format::InvalidKey(e.to_string()))?; + Self::from_bytes(&bytes) + } + + /// returns the matching public key + pub fn public(&self) -> PublicKey { + PublicKey((&self.0).verifying_key()) + } + + pub fn algorithm(&self) -> crate::format::schema::public_key::Algorithm { + crate::format::schema::public_key::Algorithm::Ed25519 + } +} + +impl std::clone::Clone for PrivateKey { + fn clone(&self) -> Self { + PrivateKey::from_bytes(&self.to_bytes()).unwrap() + } +} + +/// the public part of a [KeyPair] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct PublicKey(VerifyingKey); + +impl PublicKey { + /// serializes to a byte array + pub fn to_bytes(&self) -> Vec { + self.0.to_encoded_point(true).to_bytes().into() + } + + /// serializes to an hex-encoded string + pub fn to_bytes_hex(&self) -> String { + hex::encode(self.to_bytes()) + } + + /// deserializes from a byte array + pub fn from_bytes(bytes: &[u8]) -> Result { + let k = VerifyingKey::from_sec1_bytes(bytes) + .map_err(|s| s.to_string()) + .map_err(Format::InvalidKey)?; + + Ok(Self(k.into())) + } + + /// deserializes from an hex-encoded string + pub fn from_bytes_hex(str: &str) -> Result { + let bytes = hex::decode(str).map_err(|e| error::Format::InvalidKey(e.to_string()))?; + Self::from_bytes(&bytes) + } + + pub fn from_proto(key: &schema::PublicKey) -> Result { + if key.algorithm != schema::public_key::Algorithm::Ed25519 as i32 { + return Err(error::Format::DeserializationError(format!( + "deserialization error: unexpected key algorithm {}", + key.algorithm + ))); + } + + PublicKey::from_bytes(&key.key) + } + + pub fn to_proto(&self) -> schema::PublicKey { + schema::PublicKey { + algorithm: schema::public_key::Algorithm::Ed25519 as i32, + key: self.to_bytes().to_vec(), + } + } + + pub fn verify_signature( + &self, + data: &[u8], + signature: &Signature, + ) -> Result<(), error::Format> { + let sig = p256::ecdsa::Signature::try_from(&signature.0[..]).map_err(|e| { + error::Format::BlockSignatureDeserializationError(format!( + "block signature deserialization error: {:?}", + e + )) + })?; + + self.0 + .verify(&data, &sig) + .map_err(|s| s.to_string()) + .map_err(error::Signature::InvalidSignature) + .map_err(error::Format::Signature) + } + + pub fn algorithm(&self) -> crate::format::schema::public_key::Algorithm { + crate::format::schema::public_key::Algorithm::Ed25519 + } + + pub fn print(&self) -> String { + format!("ed25519/{}", hex::encode(&self.to_bytes())) + } +} + +impl Hash for PublicKey { + fn hash(&self, state: &mut H) { + (crate::format::schema::public_key::Algorithm::Ed25519 as i32).hash(state); + self.to_bytes().hash(state); + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn serialization() { + let kp = KeyPair::new(); + let private = kp.private(); + let public = kp.public(); + let private_hex = private.to_bytes_hex(); + let public_hex = public.to_bytes_hex(); + + println!("private: {private_hex}"); + println!("public: {public_hex}"); + + let message = "hello world"; + let signature = kp.sign(message.as_bytes()).unwrap(); + println!("signature: {}", hex::encode(&signature.0)); + + let deserialized_priv = PrivateKey::from_bytes_hex(&private_hex).unwrap(); + let deserialized_pub = PublicKey::from_bytes_hex(&public_hex).unwrap(); + + // assert_eq!(private, deserialized_priv); + assert_eq!(public, deserialized_pub); + + deserialized_pub + .verify_signature(message.as_bytes(), &signature) + .unwrap(); + panic!(); + } +} From ad56ade3284ad3059c80d1a3f453c491408b803d Mon Sep 17 00:00:00 2001 From: Geoffroy Couprie Date: Thu, 24 Aug 2023 22:16:11 +0200 Subject: [PATCH 08/22] reintroduce ed25519-dalek 2.0 --- biscuit-auth/Cargo.toml | 4 +-- biscuit-auth/src/crypto/ed25519.rs | 56 ++++++++++++------------------ biscuit-auth/src/crypto/mod.rs | 1 + biscuit-auth/src/crypto/p256.rs | 2 +- 4 files changed, 26 insertions(+), 37 deletions(-) diff --git a/biscuit-auth/Cargo.toml b/biscuit-auth/Cargo.toml index 6a8518f6..252dedc9 100644 --- a/biscuit-auth/Cargo.toml +++ b/biscuit-auth/Cargo.toml @@ -31,13 +31,13 @@ prost-types = "0.10" regex = { version = "1.5", default-features = false, features = ["std"] } nom = {version = "7", default-features = false, features = ["std"] } hex = "0.4" -zeroize = { version = "1", default-features = false } +zeroize = { version = "1.5", default-features = false } thiserror = "1" rand = { version = "0.8" } inline-c = { version = "0.1", optional = true } wasm-bindgen = { version = "0.2", optional = true } base64 = "0.13.0" -ed25519-dalek = { version = "2.0.0", features = ["rand_core", "zeroize"] } +ed25519-dalek = { version = "2.0.0", features = ["rand_core", "zeroize", "pkcs8"] } serde = { version = "1.0.132", optional = true, features = ["derive"] } getrandom = { version = "0.1.16" } time = { version = "0.3.7", features = ["formatting", "parsing"] } diff --git a/biscuit-auth/src/crypto/ed25519.rs b/biscuit-auth/src/crypto/ed25519.rs index c3aaa3f6..d6a56504 100644 --- a/biscuit-auth/src/crypto/ed25519.rs +++ b/biscuit-auth/src/crypto/ed25519.rs @@ -20,7 +20,7 @@ use zeroize::Zeroize; /// pair of cryptographic keys used to sign a token's block #[derive(Debug)] pub struct KeyPair { - kp: ed25519_dalek::Keypair, + kp: ed25519_dalek::SigningKey, } impl KeyPair { @@ -29,31 +29,24 @@ impl KeyPair { } pub fn new_with_rng(rng: &mut T) -> Self { - let kp = ed25519_dalek::Keypair::generate(rng); - + let kp = ed25519_dalek::SigningKey::generate(rng); KeyPair { kp } } pub fn from(key: &PrivateKey) -> Self { - let secret = SecretKey::from_bytes(&key.0.to_bytes()).unwrap(); - - let public = (&key.0).into(); - KeyPair { - kp: ed25519_dalek::Keypair { secret, public }, + kp: ed25519_dalek::SigningKey::from_bytes(&key.0), } } /// deserializes from a byte array pub fn from_bytes(bytes: &[u8]) -> Result { - let secret = SecretKey::from_bytes(bytes) - .map_err(|s| s.to_string()) - .map_err(Format::InvalidKey)?; - - let public = (&secret).into(); + let bytes: [u8; 32] = bytes + .try_into() + .map_err(|_| Format::InvalidKeySize(bytes.len()))?; Ok(KeyPair { - kp: ed25519_dalek::Keypair { secret, public }, + kp: ed25519_dalek::SigningKey::from_bytes(&bytes), }) } @@ -70,12 +63,11 @@ impl KeyPair { } pub fn private(&self) -> PrivateKey { - let secret = SecretKey::from_bytes(&self.kp.secret.to_bytes()).unwrap(); - PrivateKey(secret) + PrivateKey(self.kp.to_bytes()) } pub fn public(&self) -> PublicKey { - PublicKey(self.kp.public) + PublicKey(self.kp.verifying_key()) } pub fn algorithm(&self) -> crate::format::schema::public_key::Algorithm { @@ -89,25 +81,19 @@ impl std::default::Default for KeyPair { } } -impl Drop for KeyPair { - fn drop(&mut self) { - self.kp.secret.zeroize(); - } -} - /// the private part of a [KeyPair] #[derive(Debug)] -pub struct PrivateKey(ed25519_dalek::SecretKey); +pub struct PrivateKey(pub(crate) ed25519_dalek::SecretKey); impl PrivateKey { /// serializes to a byte array pub fn to_bytes(&self) -> Vec { - self.0.to_bytes().to_vec() + self.0.to_vec() } /// serializes to an hex-encoded string pub fn to_bytes_hex(&self) -> String { - hex::encode(self.to_bytes()) + hex::encode(self.0) } /// deserializes from a byte array @@ -115,10 +101,7 @@ impl PrivateKey { let bytes: [u8; 32] = bytes .try_into() .map_err(|_| Format::InvalidKeySize(bytes.len()))?; - SecretKey::from_bytes(&bytes) - .map(PrivateKey) - .map_err(|s| s.to_string()) - .map_err(Format::InvalidKey) + Ok(PrivateKey(bytes)) } /// deserializes from an hex-encoded string @@ -129,7 +112,7 @@ impl PrivateKey { /// returns the matching public key pub fn public(&self) -> PublicKey { - PublicKey((&self.0).into()) + PublicKey(SigningKey::from_bytes(&self.0).verifying_key()) } pub fn algorithm(&self) -> crate::format::schema::public_key::Algorithm { @@ -151,7 +134,7 @@ impl Drop for PrivateKey { /// the public part of a [KeyPair] #[derive(Debug, Clone, Copy, Eq)] -pub struct PublicKey(ed25519_dalek::PublicKey); +pub struct PublicKey(ed25519_dalek::VerifyingKey); impl PublicKey { /// serializes to a byte array @@ -166,7 +149,11 @@ impl PublicKey { /// deserializes from a byte array pub fn from_bytes(bytes: &[u8]) -> Result { - ed25519_dalek::PublicKey::from_bytes(bytes) + let bytes: [u8; 32] = bytes + .try_into() + .map_err(|_| Format::InvalidKeySize(bytes.len()))?; + + ed25519_dalek::VerifyingKey::from_bytes(&bytes) .map(PublicKey) .map_err(|s| s.to_string()) .map_err(Format::InvalidKey) @@ -201,12 +188,13 @@ impl PublicKey { data: &[u8], signature: &Signature, ) -> Result<(), error::Format> { - let sig = ed25519_dalek::Signature::from_bytes(&signature.0).map_err(|e| { + let signature_bytes: [u8; 64] = signature.0.clone().try_into().map_err(|e| { error::Format::BlockSignatureDeserializationError(format!( "block signature deserialization error: {:?}", e )) })?; + let sig = ed25519_dalek::Signature::from_bytes(&signature_bytes); self.0 .verify_strict(&data, &sig) diff --git a/biscuit-auth/src/crypto/mod.rs b/biscuit-auth/src/crypto/mod.rs index 93fd1410..52aa5cdc 100644 --- a/biscuit-auth/src/crypto/mod.rs +++ b/biscuit-auth/src/crypto/mod.rs @@ -12,6 +12,7 @@ use crate::format::schema; use super::error; mod ed25519; mod p256; +use nom::Finish; use rand_core::{CryptoRng, RngCore}; use std::{fmt::Display, hash::Hash, str::FromStr}; diff --git a/biscuit-auth/src/crypto/p256.rs b/biscuit-auth/src/crypto/p256.rs index 86ddb15b..8cd6da32 100644 --- a/biscuit-auth/src/crypto/p256.rs +++ b/biscuit-auth/src/crypto/p256.rs @@ -232,6 +232,6 @@ mod tests { deserialized_pub .verify_signature(message.as_bytes(), &signature) .unwrap(); - panic!(); + //panic!(); } } From 60514db2d3e2430517aee03fa78e65340a05530c Mon Sep 17 00:00:00 2001 From: Geoffroy Couprie Date: Tue, 26 Mar 2024 21:55:37 +0100 Subject: [PATCH 09/22] fix testcases --- biscuit-auth/examples/testcases.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/biscuit-auth/examples/testcases.rs b/biscuit-auth/examples/testcases.rs index 8f499201..ac3e9794 100644 --- a/biscuit-auth/examples/testcases.rs +++ b/biscuit-auth/examples/testcases.rs @@ -10,6 +10,7 @@ use biscuit::macros::*; use biscuit::Authorizer; use biscuit::{builder::*, builder_ext::*, Biscuit}; use biscuit::{KeyPair, PrivateKey, PublicKey}; +use biscuit_auth::format::schema::public_key::Algorithm; use prost::Message; use rand::prelude::*; use serde::Serialize; @@ -69,7 +70,7 @@ fn main() { }; let root = if let Some(key) = root_key { - KeyPair::from(&PrivateKey::from_bytes_hex(&key).unwrap()) + KeyPair::from(&PrivateKey::from_bytes_hex(&key, Algorithm::Ed25519).unwrap()) } else { let mut rng: StdRng = SeedableRng::seed_from_u64(1234); KeyPair::new_with_rng(&mut rng) @@ -1617,6 +1618,7 @@ fn third_party(target: &str, root: &KeyPair, test: bool) -> TestResult { let external = KeyPair::from( &PrivateKey::from_bytes_hex( "12aca40167fbdd1a11037e9fd440e3d510d9d9dea70a6646aa4aaf84d718d75a", + Algorithm::Ed25519, ) .unwrap(), ); @@ -1735,18 +1737,21 @@ fn public_keys_interning(target: &str, root: &KeyPair, test: bool) -> TestResult let external1 = KeyPair::from( &PrivateKey::from_bytes_hex( "12aca40167fbdd1a11037e9fd440e3d510d9d9dea70a6646aa4aaf84d718d75a", + Algorithm::Ed25519, ) .unwrap(), ); let external2 = KeyPair::from( &PrivateKey::from_bytes_hex( "018e3f6864a1c9ffc2e67939a835d41c808b0084b3d7babf9364f674db19eeb3", + Algorithm::Ed25519, ) .unwrap(), ); let external3 = KeyPair::from( &PrivateKey::from_bytes_hex( "88c637e4844fc3f52290889dc961cb15d809c994b5ef71990d6a2f989bd2f02c", + Algorithm::Ed25519, ) .unwrap(), ); From 92f88f88202f1f1c6d60fd965938f74abeb95d8e Mon Sep 17 00:00:00 2001 From: Geoffroy Couprie Date: Tue, 26 Mar 2024 22:00:13 +0100 Subject: [PATCH 10/22] move impl --- biscuit-auth/src/crypto/ed25519.rs | 16 ++++++++++++++++ biscuit-auth/src/crypto/mod.rs | 14 ++++++-------- 2 files changed, 22 insertions(+), 8 deletions(-) diff --git a/biscuit-auth/src/crypto/ed25519.rs b/biscuit-auth/src/crypto/ed25519.rs index 0686c3de..5b028640 100644 --- a/biscuit-auth/src/crypto/ed25519.rs +++ b/biscuit-auth/src/crypto/ed25519.rs @@ -11,6 +11,8 @@ use crate::{error::Format, format::schema}; use super::error; use super::Signature; +#[cfg(feature = "pem")] +use ed25519_dalek::pkcs8::DecodePrivateKey; use ed25519_dalek::Signer; use ed25519_dalek::*; use rand_core::{CryptoRng, RngCore}; @@ -73,6 +75,20 @@ impl KeyPair { pub fn algorithm(&self) -> crate::format::schema::public_key::Algorithm { crate::format::schema::public_key::Algorithm::Ed25519 } + + #[cfg(feature = "pem")] + pub fn from_private_key_der(bytes: &[u8]) -> Result { + let kp = SigningKey::from_pkcs8_der(bytes) + .map_err(|e| error::Format::InvalidKey(e.to_string()))?; + Ok(KeyPair { kp }) + } + + #[cfg(feature = "pem")] + pub fn from_private_key_pem(str: &str) -> Result { + let kp = SigningKey::from_pkcs8_pem(str) + .map_err(|e| error::Format::InvalidKey(e.to_string()))?; + Ok(KeyPair { kp }) + } } impl std::default::Default for KeyPair { diff --git a/biscuit-auth/src/crypto/mod.rs b/biscuit-auth/src/crypto/mod.rs index 05473dbd..19c61337 100644 --- a/biscuit-auth/src/crypto/mod.rs +++ b/biscuit-auth/src/crypto/mod.rs @@ -13,8 +13,6 @@ use super::error; mod ed25519; mod p256; -#[cfg(feature = "pem")] -use ed25519_dalek::pkcs8::DecodePrivateKey; use nom::Finish; use rand_core::{CryptoRng, RngCore}; use std::{fmt::Display, hash::Hash, str::FromStr}; @@ -70,16 +68,16 @@ impl KeyPair { #[cfg(feature = "pem")] pub fn from_private_key_der(bytes: &[u8]) -> Result { - let kp = ed25519_dalek::SigningKey::from_pkcs8_der(bytes) - .map_err(|e| error::Format::InvalidKey(e.to_string()))?; - Ok(KeyPair::Ed25519(ed25519::KeyPair { kp })) + Ok(KeyPair::Ed25519(ed25519::KeyPair::from_private_key_der( + bytes, + )?)) } #[cfg(feature = "pem")] pub fn from_private_key_pem(str: &str) -> Result { - let kp = ed25519_dalek::SigningKey::from_pkcs8_pem(str) - .map_err(|e| error::Format::InvalidKey(e.to_string()))?; - Ok(KeyPair::Ed25519(ed25519::KeyPair { kp })) + Ok(KeyPair::Ed25519(ed25519::KeyPair::from_private_key_pem( + str, + )?)) } pub fn private(&self) -> PrivateKey { From 9a8cd39f899160c5caef759cb0c5d604b8ca3f20 Mon Sep 17 00:00:00 2001 From: Geoffroy Couprie Date: Tue, 26 Mar 2024 22:05:14 +0100 Subject: [PATCH 11/22] PEM support --- biscuit-auth/src/crypto/mod.rs | 12 ++++++------ biscuit-auth/src/crypto/p256.rs | 14 ++++++++++++++ 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/biscuit-auth/src/crypto/mod.rs b/biscuit-auth/src/crypto/mod.rs index 19c61337..d421cf03 100644 --- a/biscuit-auth/src/crypto/mod.rs +++ b/biscuit-auth/src/crypto/mod.rs @@ -68,16 +68,16 @@ impl KeyPair { #[cfg(feature = "pem")] pub fn from_private_key_der(bytes: &[u8]) -> Result { - Ok(KeyPair::Ed25519(ed25519::KeyPair::from_private_key_der( - bytes, - )?)) + ed25519::KeyPair::from_private_key_der(bytes) + .map(KeyPair::Ed25519) + .or_else(|_| p256::KeyPair::from_private_key_der(bytes).map(KeyPair::P256)) } #[cfg(feature = "pem")] pub fn from_private_key_pem(str: &str) -> Result { - Ok(KeyPair::Ed25519(ed25519::KeyPair::from_private_key_pem( - str, - )?)) + ed25519::KeyPair::from_private_key_pem(str) + .map(KeyPair::Ed25519) + .or_else(|_| p256::KeyPair::from_private_key_pem(str).map(KeyPair::P256)) } pub fn private(&self) -> PrivateKey { diff --git a/biscuit-auth/src/crypto/p256.rs b/biscuit-auth/src/crypto/p256.rs index 8cd6da32..8b5c7280 100644 --- a/biscuit-auth/src/crypto/p256.rs +++ b/biscuit-auth/src/crypto/p256.rs @@ -67,6 +67,20 @@ impl KeyPair { pub fn algorithm(&self) -> crate::format::schema::public_key::Algorithm { crate::format::schema::public_key::Algorithm::P256 } + + #[cfg(feature = "pem")] + pub fn from_private_key_der(bytes: &[u8]) -> Result { + let kp = SigningKey::from_pkcs8_der(bytes) + .map_err(|e| error::Format::InvalidKey(e.to_string()))?; + Ok(KeyPair { kp }) + } + + #[cfg(feature = "pem")] + pub fn from_private_key_pem(str: &str) -> Result { + let kp = SigningKey::from_pkcs8_pem(str) + .map_err(|e| error::Format::InvalidKey(e.to_string()))?; + Ok(KeyPair { kp }) + } } impl std::default::Default for KeyPair { From 2985990052a2cc001cd9426ab6ee3ee4afeabb17 Mon Sep 17 00:00:00 2001 From: Geoffroy Couprie Date: Wed, 22 May 2024 13:34:29 +0200 Subject: [PATCH 12/22] wip --- biscuit-auth/Cargo.toml | 3 ++- biscuit-auth/src/crypto/p256.rs | 2 +- biscuit-auth/src/format/schema.proto | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/biscuit-auth/Cargo.toml b/biscuit-auth/Cargo.toml index e05e0c82..dcfa7d61 100644 --- a/biscuit-auth/Cargo.toml +++ b/biscuit-auth/Cargo.toml @@ -49,9 +49,10 @@ uuid = { version = "1", optional = true } biscuit-parser = { version = "0.1.2", path = "../biscuit-parser" } biscuit-quote = { version = "0.2.2", optional = true, path = "../biscuit-quote" } chrono = { version = "0.4.26", optional = true, default-features = false, features = ["serde"] } -ecdsa = { version = "0.14.8", features = ["sign", "verify", "pem", "alloc", "pkcs8", "serde"] } +ecdsa = { version = "0.16.9", features = ["signing", "verifying", "pem", "alloc", "pkcs8", "serde"] } p256 = "0.11.1" pkcs8 = "0.9.0" +elliptic-curve = { version = "0.13.8", features = ["pkcs8"] } [dev-dependencies] bencher = "0.1.5" diff --git a/biscuit-auth/src/crypto/p256.rs b/biscuit-auth/src/crypto/p256.rs index 8b5c7280..e19ffed3 100644 --- a/biscuit-auth/src/crypto/p256.rs +++ b/biscuit-auth/src/crypto/p256.rs @@ -96,7 +96,7 @@ pub struct PrivateKey(SigningKey); impl PrivateKey { /// serializes to a byte array pub fn to_bytes(&self) -> zeroize::Zeroizing> { - self.0.to_pkcs8_der().unwrap().to_bytes() + self.0.to_bytes().to_bytes() //self.0.to_bytes().into() } diff --git a/biscuit-auth/src/format/schema.proto b/biscuit-auth/src/format/schema.proto index cc7b45db..9ed7df3d 100644 --- a/biscuit-auth/src/format/schema.proto +++ b/biscuit-auth/src/format/schema.proto @@ -26,7 +26,7 @@ message PublicKey { enum Algorithm { Ed25519 = 0; - P256 = 1; + P256 = 1; //fixme: secp256r1? } required bytes key = 2; From 27da40e287f1b4625ff7ec17ad221b2ad4f88d43 Mon Sep 17 00:00:00 2001 From: Geoffroy Couprie Date: Thu, 23 May 2024 10:38:57 +0200 Subject: [PATCH 13/22] update protobuf enum --- biscuit-auth/src/crypto/mod.rs | 12 ++++++------ biscuit-auth/src/crypto/p256.rs | 2 +- biscuit-auth/src/format/schema.proto | 2 +- biscuit-auth/src/format/schema.rs | 2 +- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/biscuit-auth/src/crypto/mod.rs b/biscuit-auth/src/crypto/mod.rs index d421cf03..b37f58d0 100644 --- a/biscuit-auth/src/crypto/mod.rs +++ b/biscuit-auth/src/crypto/mod.rs @@ -53,7 +53,7 @@ impl KeyPair { schema::public_key::Algorithm::Ed25519 => { Ok(KeyPair::Ed25519(ed25519::KeyPair::from_bytes(bytes)?)) } - schema::public_key::Algorithm::P256 => { + schema::public_key::Algorithm::Secp256r1 => { Ok(KeyPair::P256(p256::KeyPair::from_bytes(bytes)?)) } } @@ -97,7 +97,7 @@ impl KeyPair { pub fn algorithm(&self) -> crate::format::schema::public_key::Algorithm { match self { KeyPair::Ed25519(_) => crate::format::schema::public_key::Algorithm::Ed25519, - KeyPair::P256(_) => crate::format::schema::public_key::Algorithm::P256, + KeyPair::P256(_) => crate::format::schema::public_key::Algorithm::Secp256r1, } } } @@ -138,7 +138,7 @@ impl PrivateKey { schema::public_key::Algorithm::Ed25519 => { Ok(PrivateKey::Ed25519(ed25519::PrivateKey::from_bytes(bytes)?)) } - schema::public_key::Algorithm::P256 => { + schema::public_key::Algorithm::Secp256r1 => { Ok(PrivateKey::P256(p256::PrivateKey::from_bytes(bytes)?)) } } @@ -164,7 +164,7 @@ impl PrivateKey { pub fn algorithm(&self) -> crate::format::schema::public_key::Algorithm { match self { PrivateKey::Ed25519(_) => crate::format::schema::public_key::Algorithm::Ed25519, - PrivateKey::P256(_) => crate::format::schema::public_key::Algorithm::P256, + PrivateKey::P256(_) => crate::format::schema::public_key::Algorithm::Secp256r1, } } } @@ -206,7 +206,7 @@ impl PublicKey { Ok(PublicKey::Ed25519(ed25519::PublicKey::from_bytes( &key.key, )?)) - } else if key.algorithm == schema::public_key::Algorithm::P256 as i32 { + } else if key.algorithm == schema::public_key::Algorithm::Secp256r1 as i32 { Ok(PublicKey::P256(p256::PublicKey::from_bytes(&key.key)?)) } else { Err(error::Format::DeserializationError(format!( @@ -237,7 +237,7 @@ impl PublicKey { pub fn algorithm(&self) -> crate::format::schema::public_key::Algorithm { match self { PublicKey::Ed25519(_) => crate::format::schema::public_key::Algorithm::Ed25519, - PublicKey::P256(_) => crate::format::schema::public_key::Algorithm::P256, + PublicKey::P256(_) => crate::format::schema::public_key::Algorithm::Secp256r1, } } diff --git a/biscuit-auth/src/crypto/p256.rs b/biscuit-auth/src/crypto/p256.rs index e19ffed3..fd1ce23c 100644 --- a/biscuit-auth/src/crypto/p256.rs +++ b/biscuit-auth/src/crypto/p256.rs @@ -65,7 +65,7 @@ impl KeyPair { } pub fn algorithm(&self) -> crate::format::schema::public_key::Algorithm { - crate::format::schema::public_key::Algorithm::P256 + crate::format::schema::public_key::Algorithm::Secp256r1 } #[cfg(feature = "pem")] diff --git a/biscuit-auth/src/format/schema.proto b/biscuit-auth/src/format/schema.proto index ea169d3d..ee3c63fc 100644 --- a/biscuit-auth/src/format/schema.proto +++ b/biscuit-auth/src/format/schema.proto @@ -26,7 +26,7 @@ message PublicKey { enum Algorithm { Ed25519 = 0; - P256 = 1; //fixme: secp256r1? + SECP256R1 = 1; } required bytes key = 2; diff --git a/biscuit-auth/src/format/schema.rs b/biscuit-auth/src/format/schema.rs index d33d7d47..63692c50 100644 --- a/biscuit-auth/src/format/schema.rs +++ b/biscuit-auth/src/format/schema.rs @@ -40,7 +40,7 @@ pub mod public_key { #[repr(i32)] pub enum Algorithm { Ed25519 = 0, - P256 = 1, + Secp256r1 = 1, } } #[derive(Clone, PartialEq, ::prost::Message)] From 93c2aaec750f5ff589b8658f26ac10b24119f46b Mon Sep 17 00:00:00 2001 From: Geoffroy Couprie Date: Thu, 23 May 2024 10:39:10 +0200 Subject: [PATCH 14/22] update serialization formats --- biscuit-auth/src/crypto/p256.rs | 33 ++++++++++++++------------------- 1 file changed, 14 insertions(+), 19 deletions(-) diff --git a/biscuit-auth/src/crypto/p256.rs b/biscuit-auth/src/crypto/p256.rs index fd1ce23c..f3fe0a5b 100644 --- a/biscuit-auth/src/crypto/p256.rs +++ b/biscuit-auth/src/crypto/p256.rs @@ -6,10 +6,6 @@ use super::Signature; use p256::ecdsa::{signature::Signer, signature::Verifier, SigningKey, VerifyingKey}; use p256::elliptic_curve::rand_core::{CryptoRng, OsRng, RngCore}; -use p256::elliptic_curve::sec1::FromEncodedPoint; -use p256::pkcs8::{DecodePublicKey, EncodePrivateKey, EncodePublicKey}; -use pkcs8::DecodePrivateKey; -use std::convert::TryFrom; use std::hash::Hash; /// pair of cryptographic keys used to sign a token's block @@ -30,14 +26,12 @@ impl KeyPair { } pub fn from(key: &PrivateKey) -> Self { - let kp = SigningKey::from_pkcs8_der(&key.0.to_bytes()).unwrap(); - - KeyPair { kp } + KeyPair { kp: key.0.clone() } } - /// deserializes from a byte array + /// deserializes from a big endian byte array pub fn from_bytes(bytes: &[u8]) -> Result { - let kp = SigningKey::from_pkcs8_der(bytes) + let kp = SigningKey::from_bytes(bytes) .map_err(|s| s.to_string()) .map_err(Format::InvalidKey)?; @@ -51,13 +45,14 @@ impl KeyPair { .map_err(|s| s.to_string()) .map_err(error::Signature::InvalidSignatureGeneration) .map_err(error::Format::Signature)? - .to_vec(), + .to_der() + .as_bytes() + .to_owned(), )) } pub fn private(&self) -> PrivateKey { - let secret = SigningKey::from_bytes(&self.kp.to_bytes()).unwrap(); - PrivateKey(secret) + PrivateKey(self.kp.clone()) } pub fn public(&self) -> PublicKey { @@ -94,10 +89,10 @@ impl std::default::Default for KeyPair { pub struct PrivateKey(SigningKey); impl PrivateKey { - /// serializes to a byte array + /// serializes to a big endian byte array pub fn to_bytes(&self) -> zeroize::Zeroizing> { - self.0.to_bytes().to_bytes() - //self.0.to_bytes().into() + let field_bytes = self.0.to_bytes(); + zeroize::Zeroizing::new(field_bytes.to_vec()) } /// serializes to an hex-encoded string @@ -105,9 +100,9 @@ impl PrivateKey { hex::encode(self.to_bytes()) } - /// deserializes from a byte array + /// deserializes from a big endian byte array pub fn from_bytes(bytes: &[u8]) -> Result { - SigningKey::from_pkcs8_der(&bytes) + SigningKey::from_bytes(bytes) .map(PrivateKey) .map_err(|s| s.to_string()) .map_err(Format::InvalidKey) @@ -188,7 +183,7 @@ impl PublicKey { data: &[u8], signature: &Signature, ) -> Result<(), error::Format> { - let sig = p256::ecdsa::Signature::try_from(&signature.0[..]).map_err(|e| { + let sig = p256::ecdsa::Signature::from_der(&signature.0).map_err(|e| { error::Format::BlockSignatureDeserializationError(format!( "block signature deserialization error: {:?}", e @@ -240,7 +235,7 @@ mod tests { let deserialized_priv = PrivateKey::from_bytes_hex(&private_hex).unwrap(); let deserialized_pub = PublicKey::from_bytes_hex(&public_hex).unwrap(); - // assert_eq!(private, deserialized_priv); + assert_eq!(private.0.to_bytes(), deserialized_priv.0.to_bytes()); assert_eq!(public, deserialized_pub); deserialized_pub From f104b656fbbd64c7fb8a3d5705a59a22550b32e7 Mon Sep 17 00:00:00 2001 From: Geoffroy Couprie Date: Thu, 23 May 2024 10:40:22 +0200 Subject: [PATCH 15/22] remove unuse code --- biscuit-auth/src/crypto/mod.rs | 7 ------- 1 file changed, 7 deletions(-) diff --git a/biscuit-auth/src/crypto/mod.rs b/biscuit-auth/src/crypto/mod.rs index b37f58d0..ef27b915 100644 --- a/biscuit-auth/src/crypto/mod.rs +++ b/biscuit-auth/src/crypto/mod.rs @@ -290,13 +290,6 @@ pub struct ExternalSignature { pub(crate) signature: Signature, } -#[derive(Clone, Debug)] -pub struct Token { - pub root: PublicKey, - pub blocks: Vec, - pub next: TokenNext, -} - #[derive(Clone, Debug)] pub enum TokenNext { Secret(PrivateKey), From 047ded5e25fdcdbda2899759503915d7954983e6 Mon Sep 17 00:00:00 2001 From: Geoffroy Couprie Date: Thu, 23 May 2024 11:05:06 +0200 Subject: [PATCH 16/22] add samples Since we can now have multiple algorithms, we cannot know during deserialization the right size of a signature, so that check is now removed. A signature with an invalid size will generate an error later during verification anyway --- biscuit-auth/examples/testcases.rs | 57 ++++++++++++++ biscuit-auth/samples/README.md | 87 +++++++++++++++++++++- biscuit-auth/samples/samples.json | 72 +++++++++++++++++- biscuit-auth/samples/test031_secp256r1.bc | Bin 0 -> 366 bytes biscuit-auth/src/crypto/mod.rs | 10 ++- biscuit-auth/src/format/mod.rs | 34 ++------- biscuit-auth/src/token/builder.rs | 10 +++ biscuit-auth/src/token/mod.rs | 39 ++++++---- biscuit-auth/src/token/unverified.rs | 10 +-- 9 files changed, 267 insertions(+), 52 deletions(-) create mode 100644 biscuit-auth/samples/test031_secp256r1.bc diff --git a/biscuit-auth/examples/testcases.rs b/biscuit-auth/examples/testcases.rs index 5112f374..c3694310 100644 --- a/biscuit-auth/examples/testcases.rs +++ b/biscuit-auth/examples/testcases.rs @@ -144,6 +144,8 @@ fn main() { add_test_result(&mut results, reject_if(&target, &root, test)); + add_test_result(&mut results, secp256r1(&target, &root, test)); + if json { let s = serde_json::to_string_pretty(&TestCases { root_private_key: hex::encode(root.private().to_bytes()), @@ -1991,6 +1993,61 @@ fn reject_if(target: &str, root: &KeyPair, test: bool) -> TestResult { } } +fn secp256r1(target: &str, root: &KeyPair, test: bool) -> TestResult { + let mut rng: StdRng = SeedableRng::seed_from_u64(1234); + let title = "ECDSA secp256r1 signatures".to_string(); + let filename = "test031_secp256r1".to_string(); + let token; + + let keypair2 = KeyPair::new_secp256r1_with_rng(&mut rng); + let biscuit1 = biscuit!( + r#" + right("file1", "read"); + right("file2", "read"); + right("file1", "write"); + "# + ) + .build_with_key_pair(&root, SymbolTable::default(), &keypair2) + .unwrap(); + + let keypair3 = KeyPair::new_secp256r1_with_rng(&mut rng); + let biscuit2 = biscuit1 + .append_with_keypair( + &keypair3, + block!( + r#" + check if resource($0), operation("read"), right($0, "read") + "# + ), + ) + .unwrap(); + + token = print_blocks(&biscuit2); + + let data = write_or_load_testcase(target, &filename, root, &biscuit2, test); + + let mut validations = BTreeMap::new(); + validations.insert( + "".to_string(), + validate_token( + root, + &data[..], + r#" + resource("file1"); + operation("read"); + allow if true; + "#, + ), + ); + + TestResult { + title, + filename, + token, + validations, + } +} + fn print_blocks(token: &Biscuit) -> Vec { let mut v = Vec::new(); diff --git a/biscuit-auth/samples/README.md b/biscuit-auth/samples/README.md index 0e429b2d..1151c221 100644 --- a/biscuit-auth/samples/README.md +++ b/biscuit-auth/samples/README.md @@ -141,7 +141,7 @@ check if resource($0), operation("read"), right($0, "read"); ### validation -result: `Err(Format(InvalidSignatureSize(16)))` +result: `Err(Format(BlockSignatureDeserializationError("block signature deserialization error: [117, 149, 161, 18, 161, 235, 91, 129, 166, 227, 152, 133, 46, 97, 24, 183]")))` ------------------------------ @@ -2463,3 +2463,88 @@ World { result: `Err(FailedLogic(Unauthorized { policy: Allow(0), checks: [Block(FailedBlockCheck { block_id: 0, check_id: 0, rule: "reject if test($test), $test" })] }))` + +------------------------------ + +## ECDSA secp256r1 signatures: test031_secp256r1.bc +### token + +authority: +symbols: ["file1", "file2"] + +public keys: [] + +``` +right("file1", "read"); +right("file2", "read"); +right("file1", "write"); +``` + +1: +symbols: ["0"] + +public keys: [] + +``` +check if resource($0), operation("read"), right($0, "read"); +``` + +### validation + +authorizer code: +``` +resource("file1"); +operation("read"); + +allow if true; +``` + +revocation ids: +- `760785de30d7348e9c847aab8b3bdad6a0d463f4f50ed9667aade563e9112ee6d2f589630dd7553c2eced2a57edf3636d5c874b35df15120c62fddcbdbd2de09` +- `30440220039667c7a4d964e4b449289dc8fd206d7aa0e77eb701a9253b3307d32c177fa8022023f7523c143c5fb55ee4cafe49804702ef05a70883ebf42185b54bd36a7e7cd4` + +authorizer world: +``` +World { + facts: [ + Facts { + origin: { + None, + }, + facts: [ + "operation(\"read\")", + "resource(\"file1\")", + ], + }, + Facts { + origin: { + Some( + 0, + ), + }, + facts: [ + "right(\"file1\", \"read\")", + "right(\"file1\", \"write\")", + "right(\"file2\", \"read\")", + ], + }, +] + rules: [] + checks: [ + Checks { + origin: Some( + 1, + ), + checks: [ + "check if resource($0), operation(\"read\"), right($0, \"read\")", + ], + }, +] + policies: [ + "allow if true", +] +} +``` + +result: `Ok(0)` + diff --git a/biscuit-auth/samples/samples.json b/biscuit-auth/samples/samples.json index 373a63c5..c5a218c2 100644 --- a/biscuit-auth/samples/samples.json +++ b/biscuit-auth/samples/samples.json @@ -154,7 +154,7 @@ "result": { "Err": { "Format": { - "InvalidSignatureSize": 16 + "BlockSignatureDeserializationError": "block signature deserialization error: [117, 149, 161, 18, 161, 235, 91, 129, 166, 227, 152, 133, 46, 97, 24, 183]" } } }, @@ -2276,6 +2276,76 @@ ] } } + }, + { + "title": "ECDSA secp256r1 signatures", + "filename": "test031_secp256r1.bc", + "token": [ + { + "symbols": [ + "file1", + "file2" + ], + "public_keys": [], + "external_key": null, + "code": "right(\"file1\", \"read\");\nright(\"file2\", \"read\");\nright(\"file1\", \"write\");\n" + }, + { + "symbols": [ + "0" + ], + "public_keys": [], + "external_key": null, + "code": "check if resource($0), operation(\"read\"), right($0, \"read\");\n" + } + ], + "validations": { + "": { + "world": { + "facts": [ + { + "origin": [ + null + ], + "facts": [ + "operation(\"read\")", + "resource(\"file1\")" + ] + }, + { + "origin": [ + 0 + ], + "facts": [ + "right(\"file1\", \"read\")", + "right(\"file1\", \"write\")", + "right(\"file2\", \"read\")" + ] + } + ], + "rules": [], + "checks": [ + { + "origin": 1, + "checks": [ + "check if resource($0), operation(\"read\"), right($0, \"read\")" + ] + } + ], + "policies": [ + "allow if true" + ] + }, + "result": { + "Ok": 0 + }, + "authorizer_code": "resource(\"file1\");\noperation(\"read\");\n\nallow if true;\n", + "revocation_ids": [ + "760785de30d7348e9c847aab8b3bdad6a0d463f4f50ed9667aade563e9112ee6d2f589630dd7553c2eced2a57edf3636d5c874b35df15120c62fddcbdbd2de09", + "30440220039667c7a4d964e4b449289dc8fd206d7aa0e77eb701a9253b3307d32c177fa8022023f7523c143c5fb55ee4cafe49804702ef05a70883ebf42185b54bd36a7e7cd4" + ] + } + } } ] } diff --git a/biscuit-auth/samples/test031_secp256r1.bc b/biscuit-auth/samples/test031_secp256r1.bc new file mode 100644 index 0000000000000000000000000000000000000000..15a8a429e52823e318d362c4264507f2a92fc49d GIT binary patch literal 366 zcmWey!N_IH#hR9xlWGW|j3k(qc)7SaScI4*8aRZQBpBe_MpQ0XmQhHRgHcG4DQ;r_ z6*mi`b&Hy8|4tWMob5YXxO{K$-KM)=4(us?o4sGkp^Uxtp22mKzBw&btGlglU0ZM^ z`O8RpC-Q))O&X6YiBa=^-vqVbC;IZ-8VD4dZJ`=?8iWbWBPYb-@bH@Q)&() zmo^upfdsRW3YQWW6Nj`AI|q{xGe;AL5E}Joi6Mq$Qs}?-3+s?RB z)!LZ-vW|HD3MK{R??E;qHt}2Io}BvU+2GFfo^?4#^Xo5)ty{e>XVukQQBvYkIOTY2 jL+gaMOPmb1{rR$6kf&8!<%7ZgnOU75bj}>D+xigzPX&iC literal 0 HcmV?d00001 diff --git a/biscuit-auth/src/crypto/mod.rs b/biscuit-auth/src/crypto/mod.rs index ef27b915..f2fa530f 100644 --- a/biscuit-auth/src/crypto/mod.rs +++ b/biscuit-auth/src/crypto/mod.rs @@ -33,10 +33,14 @@ impl KeyPair { KeyPair::Ed25519(ed25519::KeyPair::new_with_rng(rng)) } - pub fn new_p256() -> Self { + pub fn new_secp256r1() -> Self { KeyPair::P256(p256::KeyPair::new()) } + pub fn new_secp256r1_with_rng(rng: &mut T) -> Self { + KeyPair::P256(p256::KeyPair::new_with_rng(rng)) + } + pub fn from(key: &PrivateKey) -> Self { match key { PrivateKey::Ed25519(key) => KeyPair::Ed25519(ed25519::KeyPair::from(key)), @@ -254,6 +258,10 @@ impl Signature { Ok(Signature(data.to_owned())) } + pub(crate) fn from_vec(data: Vec) -> Self { + Signature(data) + } + pub fn to_bytes(&self) -> &[u8] { &self.0[..] } diff --git a/biscuit-auth/src/format/mod.rs b/biscuit-auth/src/format/mod.rs index b87a91f4..e0ded83b 100644 --- a/biscuit-auth/src/format/mod.rs +++ b/biscuit-auth/src/format/mod.rs @@ -15,7 +15,6 @@ use crate::crypto::Signature; use crate::datalog::SymbolTable; use crate::token::RootKeyProvider; use std::collections::HashMap; -use std::convert::TryInto; /// Structures generated from the Protobuf schema pub mod schema; /*{ @@ -59,11 +58,7 @@ impl SerializedBiscuit { let next_key = PublicKey::from_proto(&data.authority.next_key)?; let mut next_key_algorithm = next_key.algorithm(); - let bytes: [u8; 64] = (&data.authority.signature[..]) - .try_into() - .map_err(|_| error::Format::InvalidSignatureSize(data.authority.signature.len()))?; - - let signature = Signature::from_bytes(&bytes)?; + let signature = Signature::from_vec(data.authority.signature); if data.authority.external_signature.is_some() { return Err(error::Format::DeserializationError( @@ -79,24 +74,15 @@ impl SerializedBiscuit { }; let mut blocks = Vec::new(); - for block in &data.blocks { + for block in data.blocks { let next_key = PublicKey::from_proto(&block.next_key)?; next_key_algorithm = next_key.algorithm(); - let bytes: [u8; 64] = (&block.signature[..]) - .try_into() - .map_err(|_| error::Format::InvalidSignatureSize(block.signature.len()))?; - - let signature = Signature::from_bytes(&bytes)?; + let signature = Signature::from_vec(block.signature); - let external_signature = if let Some(ex) = block.external_signature.as_ref() { + let external_signature = if let Some(ex) = block.external_signature { let public_key = PublicKey::from_proto(&ex.public_key)?; - - let bytes: [u8; 64] = (&ex.signature[..]) - .try_into() - .map_err(|_| error::Format::InvalidSignatureSize(ex.signature.len()))?; - - let signature = Signature::from_bytes(&bytes)?; + let signature = Signature::from_vec(ex.signature); Some(ExternalSignature { public_key, @@ -124,15 +110,7 @@ impl SerializedBiscuit { TokenNext::Secret(PrivateKey::from_bytes(&v, next_key_algorithm)?) } Some(schema::proof::Content::FinalSignature(v)) => { - let bytes: [u8; 64] = (&v[..]) - .try_into() - .map_err(|_| error::Format::InvalidSignatureSize(v.len()))?; - let signature = Signature::from_bytes(&bytes).map_err(|e| { - error::Format::SignatureDeserializationError(format!( - "final signature deserialization error: {:?}", - e - )) - })?; + let signature = Signature::from_vec(v); TokenNext::Seal(signature) } diff --git a/biscuit-auth/src/token/builder.rs b/biscuit-auth/src/token/builder.rs index 9bd19f79..5ddd6951 100644 --- a/biscuit-auth/src/token/builder.rs +++ b/biscuit-auth/src/token/builder.rs @@ -404,6 +404,16 @@ impl BiscuitBuilder { let authority_block = self.inner.build(symbols.clone()); Biscuit::new_with_rng(rng, self.root_key_id, root, symbols, authority_block) } + + pub fn build_with_key_pair( + self, + root: &KeyPair, + symbols: SymbolTable, + next: &KeyPair, + ) -> Result { + let authority_block = self.inner.build(symbols.clone()); + Biscuit::new_with_key_pair(self.root_key_id, root, next, symbols, authority_block) + } } pub trait Convert: Sized { diff --git a/biscuit-auth/src/token/mod.rs b/biscuit-auth/src/token/mod.rs index 246dcb8a..836a791d 100644 --- a/biscuit-auth/src/token/mod.rs +++ b/biscuit-auth/src/token/mod.rs @@ -1,18 +1,16 @@ //! main structures to interact with Biscuit tokens use std::collections::HashMap; -use std::convert::TryInto; use std::fmt::Display; -use self::public_keys::PublicKeys; +use builder::{BiscuitBuilder, BlockBuilder}; +use prost::Message; +use rand_core::{CryptoRng, RngCore}; +use self::public_keys::PublicKeys; use super::crypto::{KeyPair, PublicKey, Signature}; use super::datalog::SymbolTable; use super::error; use super::format::SerializedBiscuit; -use builder::{BiscuitBuilder, BlockBuilder}; -use prost::Message; -use rand_core::{CryptoRng, RngCore}; - use crate::crypto::{self}; use crate::format::convert::proto_block_to_token_block; use crate::format::schema::{self, ThirdPartyBlockContents}; @@ -25,7 +23,6 @@ pub mod builder_ext; pub(crate) mod public_keys; pub(crate) mod third_party; pub mod unverified; - pub use block::Block; pub use third_party::*; @@ -232,6 +229,25 @@ impl Biscuit { rng: &mut T, root_key_id: Option, root: &KeyPair, + symbols: SymbolTable, + authority: Block, + ) -> Result { + Self::new_with_key_pair( + root_key_id, + root, + &KeyPair::new_with_rng(rng), + symbols, + authority, + ) + } + + /// creates a new token, using a provided CSPRNG + /// + /// the public part of the root keypair must be used for verification + pub(crate) fn new_with_key_pair( + root_key_id: Option, + root: &KeyPair, + next_keypair: &KeyPair, mut symbols: SymbolTable, authority: Block, ) -> Result { @@ -243,8 +259,7 @@ impl Biscuit { let blocks = vec![]; - let next_keypair = KeyPair::new_with_rng(rng); - let container = SerializedBiscuit::new(root_key_id, root, &next_keypair, &authority)?; + let container = SerializedBiscuit::new(root_key_id, root, next_keypair, &authority)?; symbols.public_keys.extend(&authority.public_keys)?; @@ -411,11 +426,7 @@ impl Biscuit { ))); } - let bytes: [u8; 64] = (&external_signature.signature[..]) - .try_into() - .map_err(|_| error::Format::InvalidSignatureSize(external_signature.signature.len()))?; - - let signature = Signature::from_bytes(&bytes)?; + let signature = Signature::from_vec(external_signature.signature); let previous_key = self .container diff --git a/biscuit-auth/src/token/unverified.rs b/biscuit-auth/src/token/unverified.rs index c14e01b5..1f60bcd9 100644 --- a/biscuit-auth/src/token/unverified.rs +++ b/biscuit-auth/src/token/unverified.rs @@ -1,5 +1,6 @@ use std::collections::HashMap; -use std::convert::TryInto; + +use prost::Message; use super::{default_symbol_table, Biscuit, Block}; use crate::{ @@ -13,7 +14,6 @@ use crate::{ token::{ThirdPartyBlockContents, ThirdPartyRequest}, KeyPair, RootKeyProvider, }; -use prost::Message; /// A token that was parsed without cryptographic signature verification /// @@ -303,11 +303,7 @@ impl UnverifiedBiscuit { )) })?; - let bytes: [u8; 64] = (&external_signature.signature[..]) - .try_into() - .map_err(|_| error::Format::InvalidSignatureSize(external_signature.signature.len()))?; - - let signature = Signature::from_bytes(&bytes)?; + let signature = Signature::from_vec(external_signature.signature); let previous_key = self .container From 3bd1d04028323af7a89e5cbc550d0726d2dc0e5c Mon Sep 17 00:00:00 2001 From: Geoffroy Couprie Date: Thu, 23 May 2024 14:11:49 +0200 Subject: [PATCH 17/22] parsing and printing --- biscuit-auth/Cargo.toml | 2 +- biscuit-auth/examples/verifying_printer.rs | 1 + biscuit-auth/src/crypto/mod.rs | 28 ++++++++++---- biscuit-auth/src/crypto/p256.rs | 2 +- biscuit-auth/src/datalog/symbol.rs | 2 +- biscuit-auth/src/token/authorizer.rs | 1 + biscuit-auth/src/token/builder.rs | 43 ++++++++++++++++++---- biscuit-auth/src/token/unverified.rs | 34 ++++++++++++----- biscuit-auth/tests/macros.rs | 4 ++ biscuit-parser/src/builder.rs | 25 ++++++++++--- biscuit-parser/src/parser.rs | 15 ++++++-- 11 files changed, 121 insertions(+), 36 deletions(-) diff --git a/biscuit-auth/Cargo.toml b/biscuit-auth/Cargo.toml index dcfa7d61..04688c62 100644 --- a/biscuit-auth/Cargo.toml +++ b/biscuit-auth/Cargo.toml @@ -41,7 +41,7 @@ rand = { version = "0.8" } inline-c = { version = "0.1", optional = true } wasm-bindgen = { version = "0.2", optional = true } base64 = "0.13.0" -ed25519-dalek = { version = "2.0.0", features = ["rand_core", "zeroize", "pkcs8"] } +ed25519-dalek = { version = "2.0.0", features = ["rand_core", "zeroize"] } serde = { version = "1.0.132", optional = true, features = ["derive"] } getrandom = { version = "0.1.16" } time = { version = "0.3.7", features = ["formatting", "parsing"] } diff --git a/biscuit-auth/examples/verifying_printer.rs b/biscuit-auth/examples/verifying_printer.rs index 9a15900d..fc0ecc18 100644 --- a/biscuit-auth/examples/verifying_printer.rs +++ b/biscuit-auth/examples/verifying_printer.rs @@ -14,6 +14,7 @@ fn main() { let data = std::fs::read(target).unwrap(); let root = PublicKey::from_bytes( &hex::decode("acdd6d5b53bfee478bf689f8e012fe7988bf755e3d7c5152947abc149bc20189").unwrap(), + biscuit_auth::builder::Algorithm::Ed25519, ) .unwrap(); let token = biscuit_auth::Biscuit::from(&data[..], &root).unwrap(); diff --git a/biscuit-auth/src/crypto/mod.rs b/biscuit-auth/src/crypto/mod.rs index f2fa530f..68c107db 100644 --- a/biscuit-auth/src/crypto/mod.rs +++ b/biscuit-auth/src/crypto/mod.rs @@ -7,7 +7,7 @@ //! //! The implementation is based on [ed25519_dalek](https://github.com/dalek-cryptography/ed25519-dalek). #![allow(non_snake_case)] -use crate::format::schema; +use crate::{builder::Algorithm, format::schema}; use super::error; mod ed25519; @@ -195,14 +195,17 @@ impl PublicKey { } /// deserializes from a byte array - pub fn from_bytes(bytes: &[u8]) -> Result { - Ok(PublicKey::Ed25519(ed25519::PublicKey::from_bytes(bytes)?)) + pub fn from_bytes(bytes: &[u8], algorithm: Algorithm) -> Result { + match algorithm { + Algorithm::Ed25519 => Ok(PublicKey::Ed25519(ed25519::PublicKey::from_bytes(bytes)?)), + Algorithm::Secp256r1 => Ok(PublicKey::P256(p256::PublicKey::from_bytes(bytes)?)), + } } /// deserializes from an hex-encoded string - pub fn from_bytes_hex(str: &str) -> Result { + pub fn from_bytes_hex(str: &str, algorithm: Algorithm) -> Result { let bytes = hex::decode(str).map_err(|e| error::Format::InvalidKey(e.to_string()))?; - Self::from_bytes(&bytes) + Self::from_bytes(&bytes, algorithm) } pub fn from_proto(key: &schema::PublicKey) -> Result { @@ -271,16 +274,25 @@ impl FromStr for PublicKey { type Err = error::Token; fn from_str(s: &str) -> Result { - let (_, bytes) = biscuit_parser::parser::public_key(s) + let (_, public_key) = biscuit_parser::parser::public_key(s) .finish() .map_err(biscuit_parser::error::LanguageError::from)?; - Ok(PublicKey::from_bytes(&bytes)?) + Ok(PublicKey::from_bytes( + &public_key.key, + match public_key.algorithm { + biscuit_parser::builder::Algorithm::Ed25519 => Algorithm::Ed25519, + biscuit_parser::builder::Algorithm::Secp256r1 => Algorithm::Secp256r1, + }, + )?) } } impl Display for PublicKey { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "ed25519/{}", hex::encode(&self.to_bytes())) + match self { + PublicKey::Ed25519(key) => write!(f, "ed25519/{}", hex::encode(key.to_bytes())), + PublicKey::P256(key) => write!(f, "secp256r1/{}", hex::encode(&key.to_bytes())), + } } } diff --git a/biscuit-auth/src/crypto/p256.rs b/biscuit-auth/src/crypto/p256.rs index f3fe0a5b..d54420e1 100644 --- a/biscuit-auth/src/crypto/p256.rs +++ b/biscuit-auth/src/crypto/p256.rs @@ -202,7 +202,7 @@ impl PublicKey { } pub fn print(&self) -> String { - format!("ed25519/{}", hex::encode(&self.to_bytes())) + format!("secp256r1/{}", hex::encode(&self.to_bytes())) } } diff --git a/biscuit-auth/src/datalog/symbol.rs b/biscuit-auth/src/datalog/symbol.rs index 5f1a616d..266938c3 100644 --- a/biscuit-auth/src/datalog/symbol.rs +++ b/biscuit-auth/src/datalog/symbol.rs @@ -256,7 +256,7 @@ impl SymbolTable { crate::token::Scope::Previous => "previous".to_string(), crate::token::Scope::PublicKey(key_id) => { match self.public_keys.get_key(*key_id) { - Some(key) => format!("ed25519/{}", hex::encode(key.to_bytes())), + Some(key) => key.to_string(), None => "".to_string(), } } diff --git a/biscuit-auth/src/token/authorizer.rs b/biscuit-auth/src/token/authorizer.rs index 2ed0da73..cc7f28b7 100644 --- a/biscuit-auth/src/token/authorizer.rs +++ b/biscuit-auth/src/token/authorizer.rs @@ -1396,6 +1396,7 @@ mod tests { PublicKey::from_bytes( &hex::decode("6e9e6d5a75cf0c0e87ec1256b4dfed0ca3ba452912d213fcc70f8516583db9db") .unwrap(), + crate::builder::Algorithm::Ed25519, ) .unwrap(), ); diff --git a/biscuit-auth/src/token/builder.rs b/biscuit-auth/src/token/builder.rs index 5ddd6951..1291c699 100644 --- a/biscuit-auth/src/token/builder.rs +++ b/biscuit-auth/src/token/builder.rs @@ -594,7 +594,10 @@ impl fmt::Display for Scope { match self { Scope::Authority => write!(f, "authority"), Scope::Previous => write!(f, "previous"), - Scope::PublicKey(pk) => write!(f, "ed25519/{}", hex::encode(pk.to_bytes())), + Scope::PublicKey(pk) => match pk { + PublicKey::Ed25519(key) => write!(f, "ed25519/{}", hex::encode(key.to_bytes())), + PublicKey::P256(key) => write!(f, "secp256r1/{}", hex::encode(key.to_bytes())), + }, Scope::Parameter(s) => { write!(f, "{{{}}}", s) } @@ -607,15 +610,28 @@ impl From for Scope { match scope { biscuit_parser::builder::Scope::Authority => Scope::Authority, biscuit_parser::builder::Scope::Previous => Scope::Previous, - biscuit_parser::builder::Scope::PublicKey(pk) => { - Scope::PublicKey(PublicKey::from_bytes(&pk).expect("invalid public key")) - } + biscuit_parser::builder::Scope::PublicKey(pk) => Scope::PublicKey( + PublicKey::from_bytes( + &pk.key, + match pk.algorithm { + biscuit_parser::builder::Algorithm::Ed25519 => Algorithm::Ed25519, + biscuit_parser::builder::Algorithm::Secp256r1 => Algorithm::Secp256r1, + }, + ) + .expect("invalid public key"), + ), biscuit_parser::builder::Scope::Parameter(s) => Scope::Parameter(s), } } } -/// Builder for a Datalog dicate, used in facts and rules +#[derive(Debug, Clone, PartialEq, Hash, Eq)] +pub enum Algorithm { + Ed25519, + Secp256r1, +} + +/// Builder for a Datalog predicate, used in facts and rules #[derive(Debug, Clone, PartialEq, Hash, Eq)] pub struct Predicate { pub name: String, @@ -1422,8 +1438,19 @@ impl From for Rule { .map(|(k, v)| { ( k, - v.map(|bytes| { - PublicKey::from_bytes(&bytes).expect("invalid public key") + v.map(|pk| { + PublicKey::from_bytes( + &pk.key, + match pk.algorithm { + biscuit_parser::builder::Algorithm::Ed25519 => { + Algorithm::Ed25519 + } + biscuit_parser::builder::Algorithm::Secp256r1 => { + Algorithm::Secp256r1 + } + }, + ) + .expect("invalid public key") }), ) }) @@ -2369,6 +2396,7 @@ mod tests { let pubkey = PublicKey::from_bytes( &hex::decode("6e9e6d5a75cf0c0e87ec1256b4dfed0ca3ba452912d213fcc70f8516583db9db") .unwrap(), + Algorithm::Ed25519, ) .unwrap(); let mut rule = Rule::try_from( @@ -2395,6 +2423,7 @@ mod tests { let pubkey = PublicKey::from_bytes( &hex::decode("6e9e6d5a75cf0c0e87ec1256b4dfed0ca3ba452912d213fcc70f8516583db9db") .unwrap(), + Algorithm::Ed25519, ) .unwrap(); let mut scope_params = HashMap::new(); diff --git a/biscuit-auth/src/token/unverified.rs b/biscuit-auth/src/token/unverified.rs index 1f60bcd9..ddf30c6e 100644 --- a/biscuit-auth/src/token/unverified.rs +++ b/biscuit-auth/src/token/unverified.rs @@ -5,12 +5,14 @@ use prost::Message; use super::{default_symbol_table, Biscuit, Block}; use crate::{ builder::BlockBuilder, - crypto, - crypto::PublicKey, - crypto::Signature, + crypto::{self, PublicKey, Signature}, datalog::SymbolTable, error, - format::{convert::proto_block_to_token_block, schema, SerializedBiscuit}, + format::{ + convert::proto_block_to_token_block, + schema::{self, public_key::Algorithm}, + SerializedBiscuit, + }, token::{ThirdPartyBlockContents, ThirdPartyRequest}, KeyPair, RootKeyProvider, }; @@ -295,13 +297,25 @@ impl UnverifiedBiscuit { error::Format::DeserializationError(format!("deserialization error: {:?}", e)) })?; - let external_key = - PublicKey::from_bytes(&external_signature.public_key.key).map_err(|e| { - error::Format::BlockSignatureDeserializationError(format!( - "block external public key deserialization error: {:?}", - e - )) + let algorithm = + Algorithm::from_i32(external_signature.public_key.algorithm).ok_or_else(|| { + error::Format::DeserializationError( + "deserialization error: invalid external key algorithm".to_string(), + ) })?; + let external_key = PublicKey::from_bytes( + &external_signature.public_key.key, + match algorithm { + Algorithm::Ed25519 => crate::builder::Algorithm::Ed25519, + Algorithm::Secp256r1 => crate::builder::Algorithm::Secp256r1, + }, + ) + .map_err(|e| { + error::Format::BlockSignatureDeserializationError(format!( + "block external public key deserialization error: {:?}", + e + )) + })?; let signature = Signature::from_vec(external_signature.signature); diff --git a/biscuit-auth/tests/macros.rs b/biscuit-auth/tests/macros.rs index 49244655..15b9ff85 100644 --- a/biscuit-auth/tests/macros.rs +++ b/biscuit-auth/tests/macros.rs @@ -92,6 +92,7 @@ fn biscuit_macro() { use biscuit_auth::PublicKey; let pubkey = PublicKey::from_bytes( &hex::decode("6e9e6d5a75cf0c0e87ec1256b4dfed0ca3ba452912d213fcc70f8516583db9db").unwrap(), + biscuit_auth::builder::Algorithm::Ed25519, ) .unwrap(); @@ -154,6 +155,7 @@ fn rule_macro() { use biscuit_auth::PublicKey; let pubkey = PublicKey::from_bytes( &hex::decode("6e9e6d5a75cf0c0e87ec1256b4dfed0ca3ba452912d213fcc70f8516583db9db").unwrap(), + biscuit_auth::builder::Algorithm::Ed25519, ) .unwrap(); let mut term_set = BTreeSet::new(); @@ -183,6 +185,7 @@ fn check_macro() { use biscuit_auth::PublicKey; let pubkey = PublicKey::from_bytes( &hex::decode("6e9e6d5a75cf0c0e87ec1256b4dfed0ca3ba452912d213fcc70f8516583db9db").unwrap(), + biscuit_auth::builder::Algorithm::Ed25519, ) .unwrap(); let mut term_set = BTreeSet::new(); @@ -203,6 +206,7 @@ fn policy_macro() { use biscuit_auth::PublicKey; let pubkey = PublicKey::from_bytes( &hex::decode("6e9e6d5a75cf0c0e87ec1256b4dfed0ca3ba452912d213fcc70f8516583db9db").unwrap(), + biscuit_auth::builder::Algorithm::Ed25519, ) .unwrap(); let mut term_set = BTreeSet::new(); diff --git a/biscuit-parser/src/builder.rs b/biscuit-parser/src/builder.rs index c5efe188..2491c9ae 100644 --- a/biscuit-parser/src/builder.rs +++ b/biscuit-parser/src/builder.rs @@ -77,10 +77,15 @@ impl ToTokens for Scope { Scope::Authority => quote! { ::biscuit_auth::builder::Scope::Authority}, Scope::Previous => quote! { ::biscuit_auth::builder::Scope::Previous}, Scope::PublicKey(pk) => { - let bytes = pk.iter(); - quote! { ::biscuit_auth::builder::Scope::PublicKey( - ::biscuit_auth::PublicKey::from_bytes(&[#(#bytes),*]).unwrap() - )} + let bytes = pk.key.iter(); + match pk.algorithm { + Algorithm::Ed25519 => quote! { ::biscuit_auth::builder::Scope::PublicKey( + ::biscuit_auth::PublicKey::from_bytes(&[#(#bytes),*], ::biscuit_auth::builder::Algorithm::Ed25519).unwrap() + )}, + Algorithm::Secp256r1 => quote! { ::biscuit_auth::builder::Scope::PublicKey( + ::biscuit_auth::PublicKey::from_bytes(&[#(#bytes),*], ::biscuit_auth::builder::Algorithm::Secp256r1).unwrap() + )}, + } } Scope::Parameter(v) => { quote! { ::biscuit_auth::builder::Scope::Parameter(#v.to_string())} @@ -266,7 +271,17 @@ impl ToTokens for Binary { } } -pub type PublicKey = Vec; +#[derive(Debug, Clone, Hash, PartialEq, Eq)] +pub struct PublicKey { + pub key: Vec, + pub algorithm: Algorithm, +} + +#[derive(Debug, Clone, Hash, PartialEq, Eq)] +pub enum Algorithm { + Ed25519, + Secp256r1, +} /// Builder for a Datalog rule #[derive(Debug, Clone, PartialEq, Eq)] diff --git a/biscuit-parser/src/parser.rs b/biscuit-parser/src/parser.rs index 66da7cea..144c9085 100644 --- a/biscuit-parser/src/parser.rs +++ b/biscuit-parser/src/parser.rs @@ -1,4 +1,4 @@ -use crate::builder::{self, CheckKind}; +use crate::builder::{self, CheckKind, PublicKey}; use nom::{ branch::alt, bytes::complete::{escaped_transform, tag, tag_no_case, take_until, take_while, take_while1}, @@ -10,7 +10,7 @@ use nom::{ error::{ErrorKind, FromExternalError, ParseError}, multi::{many0, separated_list0, separated_list1}, sequence::{delimited, pair, preceded, terminated, tuple}, - IResult, Offset, + IResult, Offset, Parser, }; use std::{collections::BTreeSet, convert::TryInto}; use thiserror::Error; @@ -376,7 +376,16 @@ fn scope(i: &str) -> IResult<&str, builder::Scope, Error> { } pub fn public_key(i: &str) -> IResult<&str, builder::PublicKey, Error> { - preceded(tag("ed25519/"), parse_hex)(i) + alt(( + preceded(tag("ed25519/"), parse_hex).map(|key| PublicKey { + key, + algorithm: builder::Algorithm::Ed25519, + }), + preceded(tag("secp256r1/"), parse_hex).map(|key| PublicKey { + key, + algorithm: builder::Algorithm::Secp256r1, + }), + ))(i) } #[derive(Debug, PartialEq)] From 5aeb701623252832cdbffd8e9607c019038318ed Mon Sep 17 00:00:00 2001 From: Geoffroy Couprie Date: Thu, 23 May 2024 14:35:18 +0200 Subject: [PATCH 18/22] fix sample file name --- biscuit-auth/examples/testcases.rs | 2 +- biscuit-auth/samples/README.md | 2 +- biscuit-auth/samples/samples.json | 2 +- biscuit-auth/samples/test30_null.bc | Bin 248 -> 0 bytes 4 files changed, 3 insertions(+), 3 deletions(-) delete mode 100644 biscuit-auth/samples/test30_null.bc diff --git a/biscuit-auth/examples/testcases.rs b/biscuit-auth/examples/testcases.rs index 01a10a70..ba08dcc7 100644 --- a/biscuit-auth/examples/testcases.rs +++ b/biscuit-auth/examples/testcases.rs @@ -1999,7 +1999,7 @@ fn reject_if(target: &str, root: &KeyPair, test: bool) -> TestResult { fn null(target: &str, root: &KeyPair, test: bool) -> TestResult { let mut rng: StdRng = SeedableRng::seed_from_u64(1234); let title = "test null".to_string(); - let filename = "test30_null".to_string(); + let filename = "test030_null".to_string(); let token; let biscuit = biscuit!( diff --git a/biscuit-auth/samples/README.md b/biscuit-auth/samples/README.md index bdcf8467..66077eb0 100644 --- a/biscuit-auth/samples/README.md +++ b/biscuit-auth/samples/README.md @@ -2466,7 +2466,7 @@ result: `Err(FailedLogic(Unauthorized { policy: Allow(0), checks: [Block(FailedB ------------------------------ -## test null: test30_null.bc +## test null: test030_null.bc ### token authority: diff --git a/biscuit-auth/samples/samples.json b/biscuit-auth/samples/samples.json index c576af86..e14abd81 100644 --- a/biscuit-auth/samples/samples.json +++ b/biscuit-auth/samples/samples.json @@ -2279,7 +2279,7 @@ }, { "title": "test null", - "filename": "test30_null.bc", + "filename": "test030_null.bc", "token": [ { "symbols": [ diff --git a/biscuit-auth/samples/test30_null.bc b/biscuit-auth/samples/test30_null.bc deleted file mode 100644 index 2337a1114ef4e0530bb0283a7e36ff07aa920989..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 248 zcmWf-$jFt>#gdkoT*Ad#mY7qTD#2=`$)(Q4#33!j!_mMY#N@;v#LUsiAtlVk$^~L@ zv2X#!xLBl^I9QCdiB&2hz$B!?!62j{5PCddPP$2UNR<`+DIN%IdD0A-g5zynHfbpoUMiR^0))% znoDzSVrjhE<0dI#*E#)giF@0N2h2)JTnaN^RP#-Y2$p-dmHEG@TAsG@p+y1J!aZ}P M<+Zj%+}BM60M+S53jhEB From ce02b6000eecbc66e75edf40183cc9ee2bf2c9b7 Mon Sep 17 00:00:00 2001 From: Geoffroy Couprie Date: Sat, 25 May 2024 15:27:00 +0200 Subject: [PATCH 19/22] specify the algorithm when creating a KeyPair --- biscuit-auth/benches/token.rs | 70 ++++++++++++++-------------- biscuit-auth/examples/third_party.rs | 8 ++-- biscuit-auth/src/crypto/mod.rs | 21 ++++----- biscuit-auth/src/lib.rs | 2 +- biscuit-auth/src/macros.rs | 9 ++-- biscuit-auth/src/token/authorizer.rs | 20 ++++---- biscuit-auth/src/token/mod.rs | 49 +++++++++---------- biscuit-auth/src/token/unverified.rs | 13 +++++- biscuit-auth/tests/rights.rs | 2 +- 9 files changed, 103 insertions(+), 91 deletions(-) diff --git a/biscuit-auth/benches/token.rs b/biscuit-auth/benches/token.rs index 814b6e79..4407c67c 100644 --- a/biscuit-auth/benches/token.rs +++ b/biscuit-auth/benches/token.rs @@ -11,7 +11,7 @@ use rand::rngs::OsRng; fn create_block_1(b: &mut Bencher) { let mut rng = OsRng; - let root = KeyPair::new_with_rng(&mut rng); + let root = KeyPair::new_with_rng(Algorithm::Ed25519, &mut rng); let mut builder = Biscuit::builder(); builder.add_fact(fact("right", &[string("file1"), string("read")])); @@ -40,8 +40,8 @@ fn create_block_1(b: &mut Bencher) { fn append_block_2(b: &mut Bencher) { let mut rng: OsRng = OsRng; - let root = KeyPair::new_with_rng(&mut rng); - let keypair2 = KeyPair::new_with_rng(&mut rng); + let root = KeyPair::new_with_rng(Algorithm::Ed25519, &mut rng); + let keypair2 = KeyPair::new_with_rng(Algorithm::Ed25519, &mut rng); let mut builder = Biscuit::builder(); builder.add_fact(fact("right", &[string("file1"), string("read")])); @@ -75,11 +75,11 @@ fn append_block_2(b: &mut Bencher) { fn append_block_5(b: &mut Bencher) { let mut rng: OsRng = OsRng; - let root = KeyPair::new_with_rng(&mut rng); - let keypair2 = KeyPair::new_with_rng(&mut rng); - let keypair3 = KeyPair::new_with_rng(&mut rng); - let keypair4 = KeyPair::new_with_rng(&mut rng); - let keypair5 = KeyPair::new_with_rng(&mut rng); + let root = KeyPair::new_with_rng(Algorithm::Ed25519, &mut rng); + let keypair2 = KeyPair::new_with_rng(Algorithm::Ed25519, &mut rng); + let keypair3 = KeyPair::new_with_rng(Algorithm::Ed25519, &mut rng); + let keypair4 = KeyPair::new_with_rng(Algorithm::Ed25519, &mut rng); + let keypair5 = KeyPair::new_with_rng(Algorithm::Ed25519, &mut rng); let mut builder = Biscuit::builder(); builder.add_fact(fact("right", &[string("file1"), string("read")])); @@ -129,8 +129,8 @@ fn append_block_5(b: &mut Bencher) { fn unverified_append_block_2(b: &mut Bencher) { let mut rng: OsRng = OsRng; - let root = KeyPair::new_with_rng(&mut rng); - let keypair2 = KeyPair::new_with_rng(&mut rng); + let root = KeyPair::new_with_rng(Algorithm::Ed25519, &mut rng); + let keypair2 = KeyPair::new_with_rng(Algorithm::Ed25519, &mut rng); let mut builder = Biscuit::builder(); builder.add_fact(fact("right", &[string("file1"), string("read")])); @@ -164,11 +164,11 @@ fn unverified_append_block_2(b: &mut Bencher) { fn unverified_append_block_5(b: &mut Bencher) { let mut rng: OsRng = OsRng; - let root = KeyPair::new_with_rng(&mut rng); - let keypair2 = KeyPair::new_with_rng(&mut rng); - let keypair3 = KeyPair::new_with_rng(&mut rng); - let keypair4 = KeyPair::new_with_rng(&mut rng); - let keypair5 = KeyPair::new_with_rng(&mut rng); + let root = KeyPair::new_with_rng(Algorithm::Ed25519, &mut rng); + let keypair2 = KeyPair::new_with_rng(Algorithm::Ed25519, &mut rng); + let keypair3 = KeyPair::new_with_rng(Algorithm::Ed25519, &mut rng); + let keypair4 = KeyPair::new_with_rng(Algorithm::Ed25519, &mut rng); + let keypair5 = KeyPair::new_with_rng(Algorithm::Ed25519, &mut rng); let mut builder = Biscuit::builder(); builder.add_fact(fact("right", &[string("file1"), string("read")])); @@ -218,8 +218,8 @@ fn unverified_append_block_5(b: &mut Bencher) { fn verify_block_2(b: &mut Bencher) { let mut rng: OsRng = OsRng; - let root = KeyPair::new_with_rng(&mut rng); - let keypair2 = KeyPair::new_with_rng(&mut rng); + let root = KeyPair::new_with_rng(Algorithm::Ed25519, &mut rng); + let keypair2 = KeyPair::new_with_rng(Algorithm::Ed25519, &mut rng); let data = { let mut builder = Biscuit::builder(); @@ -260,11 +260,11 @@ fn verify_block_2(b: &mut Bencher) { fn verify_block_5(b: &mut Bencher) { let mut rng: OsRng = OsRng; - let root = KeyPair::new_with_rng(&mut rng); - let keypair2 = KeyPair::new_with_rng(&mut rng); - let keypair3 = KeyPair::new_with_rng(&mut rng); - let keypair4 = KeyPair::new_with_rng(&mut rng); - let keypair5 = KeyPair::new_with_rng(&mut rng); + let root = KeyPair::new_with_rng(Algorithm::Ed25519, &mut rng); + let keypair2 = KeyPair::new_with_rng(Algorithm::Ed25519, &mut rng); + let keypair3 = KeyPair::new_with_rng(Algorithm::Ed25519, &mut rng); + let keypair4 = KeyPair::new_with_rng(Algorithm::Ed25519, &mut rng); + let keypair5 = KeyPair::new_with_rng(Algorithm::Ed25519, &mut rng); let data = { let mut builder = Biscuit::builder(); @@ -329,8 +329,8 @@ fn verify_block_5(b: &mut Bencher) { fn check_signature_2(b: &mut Bencher) { let mut rng: OsRng = OsRng; - let root = KeyPair::new_with_rng(&mut rng); - let keypair2 = KeyPair::new_with_rng(&mut rng); + let root = KeyPair::new_with_rng(Algorithm::Ed25519, &mut rng); + let keypair2 = KeyPair::new_with_rng(Algorithm::Ed25519, &mut rng); let data = { let mut builder = Biscuit::builder(); @@ -366,11 +366,11 @@ fn check_signature_2(b: &mut Bencher) { fn check_signature_5(b: &mut Bencher) { let mut rng: OsRng = OsRng; - let root = KeyPair::new_with_rng(&mut rng); - let keypair2 = KeyPair::new_with_rng(&mut rng); - let keypair3 = KeyPair::new_with_rng(&mut rng); - let keypair4 = KeyPair::new_with_rng(&mut rng); - let keypair5 = KeyPair::new_with_rng(&mut rng); + let root = KeyPair::new_with_rng(Algorithm::Ed25519, &mut rng); + let keypair2 = KeyPair::new_with_rng(Algorithm::Ed25519, &mut rng); + let keypair3 = KeyPair::new_with_rng(Algorithm::Ed25519, &mut rng); + let keypair4 = KeyPair::new_with_rng(Algorithm::Ed25519, &mut rng); + let keypair5 = KeyPair::new_with_rng(Algorithm::Ed25519, &mut rng); let data = { let mut builder = Biscuit::builder(); @@ -429,8 +429,8 @@ fn check_signature_5(b: &mut Bencher) { fn checks_block_2(b: &mut Bencher) { let mut rng: OsRng = OsRng; - let root = KeyPair::new_with_rng(&mut rng); - let keypair2 = KeyPair::new_with_rng(&mut rng); + let root = KeyPair::new_with_rng(Algorithm::Ed25519, &mut rng); + let keypair2 = KeyPair::new_with_rng(Algorithm::Ed25519, &mut rng); let data = { let mut builder = Biscuit::builder(); @@ -471,8 +471,8 @@ fn checks_block_2(b: &mut Bencher) { fn checks_block_create_verifier2(b: &mut Bencher) { let mut rng: OsRng = OsRng; - let root = KeyPair::new_with_rng(&mut rng); - let keypair2 = KeyPair::new_with_rng(&mut rng); + let root = KeyPair::new_with_rng(Algorithm::Ed25519, &mut rng); + let keypair2 = KeyPair::new_with_rng(Algorithm::Ed25519, &mut rng); let data = { let mut builder = Biscuit::builder(); @@ -509,8 +509,8 @@ fn checks_block_create_verifier2(b: &mut Bencher) { fn checks_block_verify_only2(b: &mut Bencher) { let mut rng: OsRng = OsRng; - let root = KeyPair::new_with_rng(&mut rng); - let keypair2 = KeyPair::new_with_rng(&mut rng); + let root = KeyPair::new_with_rng(Algorithm::Ed25519, &mut rng); + let keypair2 = KeyPair::new_with_rng(Algorithm::Ed25519, &mut rng); let data = { let mut builder = Biscuit::builder(); diff --git a/biscuit-auth/examples/third_party.rs b/biscuit-auth/examples/third_party.rs index 072f435f..67fd6d99 100644 --- a/biscuit-auth/examples/third_party.rs +++ b/biscuit-auth/examples/third_party.rs @@ -1,10 +1,12 @@ -use biscuit_auth::{builder::BlockBuilder, datalog::SymbolTable, Biscuit, KeyPair}; +use biscuit_auth::{ + builder::Algorithm, builder::BlockBuilder, datalog::SymbolTable, Biscuit, KeyPair, +}; use rand::{prelude::StdRng, SeedableRng}; fn main() { let mut rng: StdRng = SeedableRng::seed_from_u64(0); - let root = KeyPair::new_with_rng(&mut rng); - let external = KeyPair::new_with_rng(&mut rng); + let root = KeyPair::new_with_rng(Algorithm::Ed25519, &mut rng); + let external = KeyPair::new_with_rng(Algorithm::Ed25519, &mut rng); let mut builder = Biscuit::builder(); diff --git a/biscuit-auth/src/crypto/mod.rs b/biscuit-auth/src/crypto/mod.rs index 68c107db..1b893117 100644 --- a/biscuit-auth/src/crypto/mod.rs +++ b/biscuit-auth/src/crypto/mod.rs @@ -25,20 +25,15 @@ pub enum KeyPair { } impl KeyPair { - pub fn new() -> Self { - Self::new_with_rng(&mut rand::rngs::OsRng) + pub fn new(algorithm: Algorithm) -> Self { + Self::new_with_rng(algorithm, &mut rand::rngs::OsRng) } - pub fn new_with_rng(rng: &mut T) -> Self { - KeyPair::Ed25519(ed25519::KeyPair::new_with_rng(rng)) - } - - pub fn new_secp256r1() -> Self { - KeyPair::P256(p256::KeyPair::new()) - } - - pub fn new_secp256r1_with_rng(rng: &mut T) -> Self { - KeyPair::P256(p256::KeyPair::new_with_rng(rng)) + pub fn new_with_rng(algorithm: Algorithm, rng: &mut T) -> Self { + match algorithm { + Algorithm::Ed25519 => KeyPair::Ed25519(ed25519::KeyPair::new_with_rng(rng)), + Algorithm::Secp256r1 => KeyPair::P256(p256::KeyPair::new_with_rng(rng)), + } } pub fn from(key: &PrivateKey) -> Self { @@ -108,7 +103,7 @@ impl KeyPair { impl std::default::Default for KeyPair { fn default() -> Self { - Self::new() + Self::new(Algorithm::Ed25519) } } diff --git a/biscuit-auth/src/lib.rs b/biscuit-auth/src/lib.rs index a3596cf4..7fb8bc19 100644 --- a/biscuit-auth/src/lib.rs +++ b/biscuit-auth/src/lib.rs @@ -32,7 +32,7 @@ //! fn main() -> Result<(), error::Token> { //! // let's generate the root key pair. The root public key will be necessary //! // to verify the token -//! let root = KeyPair::new(); +//! let root = KeyPair::new(Algorithm::Ed25519); //! let public_key = root.public(); //! //! // creating a first token diff --git a/biscuit-auth/src/macros.rs b/biscuit-auth/src/macros.rs index dfab972e..cbd47669 100644 --- a/biscuit-auth/src/macros.rs +++ b/biscuit-auth/src/macros.rs @@ -2,10 +2,11 @@ //! //! ```rust //! use biscuit_auth::KeyPair; +//! use biscuit_auth::builder::Algorithm; //! use biscuit_auth::macros::{authorizer, biscuit, block}; //! use std::time::{Duration, SystemTime}; //! -//! let root = KeyPair::new(); +//! let root = KeyPair::new(Algorithm::Ed25519); //! //! let user_id = "1234"; //! let biscuit = biscuit!( @@ -92,10 +93,11 @@ pub use biscuit_quote::authorizer_merge; /// /// ```rust /// use biscuit_auth::{Biscuit, KeyPair}; +/// use biscuit_auth::builder::Algorithm; /// use biscuit_auth::macros::biscuit; /// use std::time::{SystemTime, Duration}; /// -/// let root = KeyPair::new(); +/// let root = KeyPair::new(Algorithm::Ed25519); /// let biscuit = biscuit!( /// r#" /// user({user_id}); @@ -113,10 +115,11 @@ pub use biscuit_quote::biscuit; /// /// ```rust /// use biscuit_auth::{Biscuit, KeyPair}; +/// use biscuit_auth::builder::Algorithm; /// use biscuit_auth::macros::{biscuit, biscuit_merge}; /// use std::time::{SystemTime, Duration}; /// -/// let root = KeyPair::new(); +/// let root = KeyPair::new(Algorithm::Ed25519); /// /// let mut b = biscuit!( /// r#" diff --git a/biscuit-auth/src/token/authorizer.rs b/biscuit-auth/src/token/authorizer.rs index cc7f28b7..c4357429 100644 --- a/biscuit-auth/src/token/authorizer.rs +++ b/biscuit-auth/src/token/authorizer.rs @@ -415,7 +415,8 @@ impl Authorizer { /// ```rust /// # use biscuit_auth::KeyPair; /// # use biscuit_auth::Biscuit; - /// let keypair = KeyPair::new(); + /// # use biscuit_auth::builder::Algorithm; + /// let keypair = KeyPair::new(Algorithm::Ed25519); /// let mut builder = Biscuit::builder(); /// builder.add_fact("user(\"John Doe\", 42)"); /// @@ -505,7 +506,8 @@ impl Authorizer { /// ```rust /// # use biscuit_auth::KeyPair; /// # use biscuit_auth::Biscuit; - /// let keypair = KeyPair::new(); + /// # use biscuit_auth::builder::Algorithm; + /// let keypair = KeyPair::new(Algorithm::Ed25519,); /// let mut builder = Biscuit::builder(); /// builder.add_fact("user(\"John Doe\", 42)"); /// @@ -1370,7 +1372,7 @@ mod tests { use std::time::Duration; use crate::{ - builder::{BiscuitBuilder, BlockBuilder}, + builder::{Algorithm, BiscuitBuilder, BlockBuilder}, KeyPair, }; @@ -1504,7 +1506,7 @@ mod tests { fn query_authorizer_from_token_tuple() { use crate::Biscuit; use crate::KeyPair; - let keypair = KeyPair::new(); + let keypair = KeyPair::new(Algorithm::Ed25519); let mut builder = Biscuit::builder(); builder.add_fact("user(\"John Doe\", 42)").unwrap(); @@ -1524,7 +1526,7 @@ mod tests { fn query_authorizer_from_token_string() { use crate::Biscuit; use crate::KeyPair; - let keypair = KeyPair::new(); + let keypair = KeyPair::new(Algorithm::Ed25519); let mut builder = Biscuit::builder(); builder.add_fact("user(\"John Doe\")").unwrap(); @@ -1539,8 +1541,8 @@ mod tests { #[test] fn authorizer_with_scopes() { - let root = KeyPair::new(); - let external = KeyPair::new(); + let root = KeyPair::new(Algorithm::Ed25519); + let external = KeyPair::new(Algorithm::Ed25519); let mut builder = Biscuit::builder(); let mut scope_params = HashMap::new(); @@ -1571,7 +1573,7 @@ mod tests { let biscuit2 = biscuit1.append_third_party(external.public(), res).unwrap(); let mut authorizer = Authorizer::new(); - let external2 = KeyPair::new(); + let external2 = KeyPair::new(Algorithm::Ed25519); let mut scope_params = HashMap::new(); scope_params.insert("external".to_string(), external.public()); @@ -1673,7 +1675,7 @@ mod tests { #[test] fn authorizer_display_before_and_after_authorization() { - let root = KeyPair::new(); + let root = KeyPair::new(Algorithm::Ed25519); let mut token_builder = BiscuitBuilder::new(); token_builder diff --git a/biscuit-auth/src/token/mod.rs b/biscuit-auth/src/token/mod.rs index 836a791d..0769aedf 100644 --- a/biscuit-auth/src/token/mod.rs +++ b/biscuit-auth/src/token/mod.rs @@ -50,7 +50,7 @@ pub fn default_symbol_table() -> SymbolTable { /// use biscuit::{KeyPair, Biscuit, builder::*, builder_ext::*}; /// /// fn main() { -/// let root = KeyPair::new(); +/// let root = KeyPair::new(Algorithm::Ed25519); /// /// // first we define the authority block for global data, /// // like access rights @@ -153,7 +153,7 @@ impl Biscuit { /// since the public key is integrated into the token, the keypair can be /// discarded right after calling this function pub fn append(&self, block_builder: BlockBuilder) -> Result { - let keypair = KeyPair::new_with_rng(&mut rand::rngs::OsRng); + let keypair = KeyPair::new_with_rng(builder::Algorithm::Ed25519, &mut rand::rngs::OsRng); self.append_with_keypair(&keypair, block_builder) } @@ -235,7 +235,7 @@ impl Biscuit { Self::new_with_key_pair( root_key_id, root, - &KeyPair::new_with_rng(rng), + &KeyPair::new_with_rng(builder::Algorithm::Ed25519, rng), symbols, authority, ) @@ -401,7 +401,8 @@ impl Biscuit { external_key: PublicKey, response: ThirdPartyBlock, ) -> Result { - let next_keypair = KeyPair::new_with_rng(&mut rand::rngs::OsRng); + let next_keypair = + KeyPair::new_with_rng(builder::Algorithm::Ed25519, &mut rand::rngs::OsRng); self.append_third_party_with_keypair(external_key, response, next_keypair) } @@ -718,7 +719,7 @@ mod tests { #[test] fn basic() { let mut rng: StdRng = SeedableRng::seed_from_u64(0); - let root = KeyPair::new_with_rng(&mut rng); + let root = KeyPair::new_with_rng(builder::Algorithm::Ed25519, &mut rng); let serialized1 = { let mut builder = Biscuit::builder(); @@ -787,7 +788,7 @@ mod tests { )) .unwrap(); - let keypair2 = KeyPair::new_with_rng(&mut rng); + let keypair2 = KeyPair::new_with_rng(builder::Algorithm::Ed25519, &mut rng); let biscuit2 = biscuit1_deser .append_with_keypair(&keypair2, block2) .unwrap(); @@ -814,7 +815,7 @@ mod tests { )) .unwrap(); - let keypair3 = KeyPair::new_with_rng(&mut rng); + let keypair3 = KeyPair::new_with_rng(builder::Algorithm::Ed25519, &mut rng); let biscuit3 = biscuit2_deser .append_with_keypair(&keypair3, block3) .unwrap(); @@ -878,7 +879,7 @@ mod tests { #[test] fn folders() { let mut rng: StdRng = SeedableRng::seed_from_u64(0); - let root = KeyPair::new_with_rng(&mut rng); + let root = KeyPair::new_with_rng(builder::Algorithm::Ed25519, &mut rng); let mut builder = Biscuit::builder(); @@ -899,7 +900,7 @@ mod tests { block2.check_resource_prefix("/folder1/"); block2.check_right("read"); - let keypair2 = KeyPair::new_with_rng(&mut rng); + let keypair2 = KeyPair::new_with_rng(builder::Algorithm::Ed25519, &mut rng); let biscuit2 = biscuit1.append_with_keypair(&keypair2, block2).unwrap(); { @@ -956,7 +957,7 @@ mod tests { #[test] fn constraints() { let mut rng: StdRng = SeedableRng::seed_from_u64(0); - let root = KeyPair::new_with_rng(&mut rng); + let root = KeyPair::new_with_rng(builder::Algorithm::Ed25519, &mut rng); let mut builder = Biscuit::builder(); @@ -974,7 +975,7 @@ mod tests { block2.check_expiration_date(SystemTime::now() + Duration::from_secs(30)); block2.add_fact("key(1234)").unwrap(); - let keypair2 = KeyPair::new_with_rng(&mut rng); + let keypair2 = KeyPair::new_with_rng(builder::Algorithm::Ed25519, &mut rng); let biscuit2 = biscuit1.append_with_keypair(&keypair2, block2).unwrap(); { @@ -1009,7 +1010,7 @@ mod tests { #[test] fn sealed_token() { let mut rng: StdRng = SeedableRng::seed_from_u64(0); - let root = KeyPair::new_with_rng(&mut rng); + let root = KeyPair::new_with_rng(builder::Algorithm::Ed25519, &mut rng); let mut builder = Biscuit::builder(); builder.add_right("/folder1/file1", "read"); @@ -1029,7 +1030,7 @@ mod tests { block2.check_resource_prefix("/folder1/"); block2.check_right("read"); - let keypair2 = KeyPair::new_with_rng(&mut rng); + let keypair2 = KeyPair::new_with_rng(builder::Algorithm::Ed25519, &mut rng); let biscuit2 = biscuit1.append_with_keypair(&keypair2, block2).unwrap(); //println!("biscuit2:\n{:#?}", biscuit2); @@ -1070,7 +1071,7 @@ mod tests { use crate::token::builder::*; let mut rng: StdRng = SeedableRng::seed_from_u64(1234); - let root = KeyPair::new_with_rng(&mut rng); + let root = KeyPair::new_with_rng(builder::Algorithm::Ed25519, &mut rng); let mut builder = Biscuit::builder(); @@ -1115,7 +1116,7 @@ mod tests { #[test] fn authorizer_queries() { let mut rng: StdRng = SeedableRng::seed_from_u64(0); - let root = KeyPair::new_with_rng(&mut rng); + let root = KeyPair::new_with_rng(builder::Algorithm::Ed25519, &mut rng); let mut builder = Biscuit::builder(); @@ -1134,7 +1135,7 @@ mod tests { block2.check_expiration_date(SystemTime::now() + Duration::from_secs(30)); block2.add_fact("key(1234)").unwrap(); - let keypair2 = KeyPair::new_with_rng(&mut rng); + let keypair2 = KeyPair::new_with_rng(builder::Algorithm::Ed25519, &mut rng); let biscuit2 = biscuit1.append_with_keypair(&keypair2, block2).unwrap(); let mut block3 = BlockBuilder::new(); @@ -1142,7 +1143,7 @@ mod tests { block3.check_expiration_date(SystemTime::now() + Duration::from_secs(10)); block3.add_fact("key(5678)").unwrap(); - let keypair3 = KeyPair::new_with_rng(&mut rng); + let keypair3 = KeyPair::new_with_rng(builder::Algorithm::Ed25519, &mut rng); let biscuit3 = biscuit2.append_with_keypair(&keypair3, block3).unwrap(); { println!("biscuit3: {}", biscuit3); @@ -1193,7 +1194,7 @@ mod tests { #[test] fn check_head_name() { let mut rng: StdRng = SeedableRng::seed_from_u64(0); - let root = KeyPair::new_with_rng(&mut rng); + let root = KeyPair::new_with_rng(builder::Algorithm::Ed25519, &mut rng); let mut builder = Biscuit::builder(); @@ -1214,7 +1215,7 @@ mod tests { let mut block2 = BlockBuilder::new(); block2.add_fact(fact("check1", &[string("test")])).unwrap(); - let keypair2 = KeyPair::new_with_rng(&mut rng); + let keypair2 = KeyPair::new_with_rng(builder::Algorithm::Ed25519, &mut rng); let biscuit2 = biscuit1.append_with_keypair(&keypair2, block2).unwrap(); println!("biscuit2: {}", biscuit2); @@ -1294,7 +1295,7 @@ mod tests { #[test] fn bytes_constraints() { let mut rng: StdRng = SeedableRng::seed_from_u64(0); - let root = KeyPair::new_with_rng(&mut rng); + let root = KeyPair::new_with_rng(builder::Algorithm::Ed25519, &mut rng); let mut builder = Biscuit::builder(); builder.add_fact("bytes(hex:0102AB)").unwrap(); @@ -1308,7 +1309,7 @@ mod tests { block2 .add_rule("has_bytes($0) <- bytes($0), [ hex:00000000, hex:0102AB ].contains($0)") .unwrap(); - let keypair2 = KeyPair::new_with_rng(&mut rng); + let keypair2 = KeyPair::new_with_rng(builder::Algorithm::Ed25519, &mut rng); let biscuit2 = biscuit1.append_with_keypair(&keypair2, block2).unwrap(); let mut authorizer = biscuit2.authorizer().unwrap(); @@ -1329,7 +1330,7 @@ mod tests { #[test] fn block1_generates_authority_or_ambient() { let mut rng: StdRng = SeedableRng::seed_from_u64(0); - let root = KeyPair::new_with_rng(&mut rng); + let root = KeyPair::new_with_rng(builder::Algorithm::Ed25519, &mut rng); let serialized1 = { let mut builder = Biscuit::builder(); @@ -1378,7 +1379,7 @@ mod tests { block2.add_rule("right($file, $right) <- right($any1, $any2), resource($file), operation($right)") .unwrap(); - let keypair2 = KeyPair::new_with_rng(&mut rng); + let keypair2 = KeyPair::new_with_rng(builder::Algorithm::Ed25519, &mut rng); let biscuit2 = biscuit1_deser .append_with_keypair(&keypair2, block2) .unwrap(); @@ -1415,7 +1416,7 @@ mod tests { #[test] fn check_all() { let mut rng: StdRng = SeedableRng::seed_from_u64(0); - let root = KeyPair::new_with_rng(&mut rng); + let root = KeyPair::new_with_rng(builder::Algorithm::Ed25519, &mut rng); let mut builder = Biscuit::builder(); diff --git a/biscuit-auth/src/token/unverified.rs b/biscuit-auth/src/token/unverified.rs index ddf30c6e..1fde7b7c 100644 --- a/biscuit-auth/src/token/unverified.rs +++ b/biscuit-auth/src/token/unverified.rs @@ -82,7 +82,8 @@ impl UnverifiedBiscuit { /// since the public key is integrated into the token, the keypair can be /// discarded right after calling this function pub fn append(&self, block_builder: BlockBuilder) -> Result { - let keypair = KeyPair::new_with_rng(&mut rand::rngs::OsRng); + let keypair = + KeyPair::new_with_rng(super::builder::Algorithm::Ed25519, &mut rand::rngs::OsRng); self.append_with_keypair(&keypair, block_builder) } @@ -288,8 +289,16 @@ impl UnverifiedBiscuit { } pub fn append_third_party(&self, slice: &[u8]) -> Result { - let next_keypair = KeyPair::new_with_rng(&mut rand::rngs::OsRng); + let next_keypair = + KeyPair::new_with_rng(super::builder::Algorithm::Ed25519, &mut rand::rngs::OsRng); + self.append_third_party_with_keypair(slice, next_keypair) + } + pub fn append_third_party_with_keypair( + &self, + slice: &[u8], + next_keypair: KeyPair, + ) -> Result { let ThirdPartyBlockContents { payload, external_signature, diff --git a/biscuit-auth/tests/rights.rs b/biscuit-auth/tests/rights.rs index 39950811..3741abad 100644 --- a/biscuit-auth/tests/rights.rs +++ b/biscuit-auth/tests/rights.rs @@ -9,7 +9,7 @@ use rand::{prelude::StdRng, SeedableRng}; fn main() { let mut rng: StdRng = SeedableRng::seed_from_u64(1234); - let root = KeyPair::new_with_rng(&mut rng); + let root = KeyPair::new_with_rng(builder::Algorithm::Ed25519, &mut rng); let mut builder = Biscuit::builder(); From 81084204ee5a5ae02514e104099d20bb958f94f2 Mon Sep 17 00:00:00 2001 From: Geoffroy Couprie Date: Sat, 25 May 2024 15:36:09 +0200 Subject: [PATCH 20/22] capi fixes --- biscuit-auth/src/capi.rs | 36 +++++++++++++++++++++++++++++++----- 1 file changed, 31 insertions(+), 5 deletions(-) diff --git a/biscuit-auth/src/capi.rs b/biscuit-auth/src/capi.rs index 3a80cbba..c33d1a48 100644 --- a/biscuit-auth/src/capi.rs +++ b/biscuit-auth/src/capi.rs @@ -295,10 +295,17 @@ pub struct BiscuitBuilder(crate::token::builder::BiscuitBuilder); pub struct BlockBuilder(crate::token::builder::BlockBuilder); pub struct Authorizer(crate::token::authorizer::Authorizer); +#[repr(C)] +pub enum SignatureAlgorithm { + Ed25519, + Secp256r1, +} + #[no_mangle] pub unsafe extern "C" fn key_pair_new<'a>( seed_ptr: *const u8, seed_len: usize, + algorithm: SignatureAlgorithm, ) -> Option> { let slice = std::slice::from_raw_parts(seed_ptr, seed_len); if slice.len() != 32 { @@ -310,9 +317,13 @@ pub unsafe extern "C" fn key_pair_new<'a>( seed.copy_from_slice(slice); let mut rng: StdRng = SeedableRng::from_seed(seed); + let algorithm = match algorithm { + SignatureAlgorithm::Ed25519 => crate::builder::Algorithm::Ed25519, + SignatureAlgorithm::Secp256r1 => crate::builder::Algorithm::Secp256r1, + }; Some(Box::new(KeyPair(crate::crypto::KeyPair::new_with_rng( - &mut rng, + algorithm, &mut rng, )))) } @@ -343,10 +354,18 @@ pub unsafe extern "C" fn key_pair_serialize(kp: Option<&KeyPair>, buffer_ptr: *m /// expects a 32 byte buffer #[no_mangle] -pub unsafe extern "C" fn key_pair_deserialize(buffer_ptr: *mut u8) -> Option> { +pub unsafe extern "C" fn key_pair_deserialize( + buffer_ptr: *mut u8, + algorithm: SignatureAlgorithm, +) -> Option> { let input_slice = std::slice::from_raw_parts_mut(buffer_ptr, 32); - match crate::crypto::PrivateKey::from_bytes(input_slice).ok() { + let algorithm = match algorithm { + SignatureAlgorithm::Ed25519 => crate::format::schema::public_key::Algorithm::Ed25519, + SignatureAlgorithm::Secp256r1 => crate::format::schema::public_key::Algorithm::Secp256r1, + }; + + match crate::crypto::PrivateKey::from_bytes(input_slice, algorithm).ok() { None => { update_last_error(Error::InvalidArgument); None @@ -378,10 +397,17 @@ pub unsafe extern "C" fn public_key_serialize( /// expects a 32 byte buffer #[no_mangle] -pub unsafe extern "C" fn public_key_deserialize(buffer_ptr: *mut u8) -> Option> { +pub unsafe extern "C" fn public_key_deserialize( + buffer_ptr: *mut u8, + algorithm: SignatureAlgorithm, +) -> Option> { let input_slice = std::slice::from_raw_parts_mut(buffer_ptr, 32); + let algorithm = match algorithm { + SignatureAlgorithm::Ed25519 => crate::token::builder::Algorithm::Ed25519, + SignatureAlgorithm::Secp256r1 => crate::token::builder::Algorithm::Secp256r1, + }; - match crate::crypto::PublicKey::from_bytes(input_slice).ok() { + match crate::crypto::PublicKey::from_bytes(input_slice, algorithm).ok() { None => { update_last_error(Error::InvalidArgument); None From c88fff0007d646440a540d647268a508ad65beec Mon Sep 17 00:00:00 2001 From: Geoffroy Couprie Date: Sun, 26 May 2024 22:31:25 +0200 Subject: [PATCH 21/22] fix samples build --- biscuit-auth/examples/testcases.rs | 80 +- biscuit-auth/samples/README.md | 2852 +++++++++++++++++++++ biscuit-auth/samples/samples.json | 2 +- biscuit-auth/samples/test032_secp256r1.bc | Bin 0 -> 366 bytes biscuit-auth/src/crypto/mod.rs | 18 +- biscuit-auth/src/format/mod.rs | 6 + 6 files changed, 2909 insertions(+), 49 deletions(-) create mode 100644 biscuit-auth/samples/test032_secp256r1.bc diff --git a/biscuit-auth/examples/testcases.rs b/biscuit-auth/examples/testcases.rs index 7f53b655..91d9457a 100644 --- a/biscuit-auth/examples/testcases.rs +++ b/biscuit-auth/examples/testcases.rs @@ -10,7 +10,7 @@ use biscuit::macros::*; use biscuit::Authorizer; use biscuit::{builder::*, builder_ext::*, Biscuit}; use biscuit::{KeyPair, PrivateKey, PublicKey}; -use biscuit_auth::format::schema::public_key::Algorithm; +use biscuit_auth::builder::Algorithm; use prost::Message; use rand::prelude::*; use serde::Serialize; @@ -77,7 +77,7 @@ fn run(target: String, root_key: Option, test: bool, json: bool) { KeyPair::from(&PrivateKey::from_bytes_hex(&key, Algorithm::Ed25519).unwrap()) } else { let mut rng: StdRng = SeedableRng::seed_from_u64(1234); - KeyPair::new_with_rng(&mut rng) + KeyPair::new_with_rng(Algorithm::Ed25519, &mut rng) }; let mut results = Vec::new(); @@ -507,7 +507,7 @@ fn basic_token(target: &str, root: &KeyPair, test: bool) -> TestResult { .build_with_rng(&root, SymbolTable::default(), &mut rng) .unwrap(); - let keypair2 = KeyPair::new_with_rng(&mut rng); + let keypair2 = KeyPair::new_with_rng(Algorithm::Ed25519, &mut rng); let biscuit2 = biscuit1 .append_with_keypair( &keypair2, @@ -551,7 +551,7 @@ fn different_root_key(target: &str, root: &KeyPair, test: bool) -> TestResult { let filename = "test002_different_root_key".to_string(); let token; - let root2 = KeyPair::new_with_rng(&mut rng); + let root2 = KeyPair::new_with_rng(Algorithm::Ed25519, &mut rng); let biscuit1 = biscuit!( r#" @@ -561,7 +561,7 @@ fn different_root_key(target: &str, root: &KeyPair, test: bool) -> TestResult { .build_with_rng(&root2, SymbolTable::default(), &mut rng) .unwrap(); - let keypair2 = KeyPair::new_with_rng(&mut rng); + let keypair2 = KeyPair::new_with_rng(Algorithm::Ed25519, &mut rng); let biscuit2 = biscuit1 .append_with_keypair( &keypair2, @@ -614,7 +614,7 @@ fn invalid_signature_format(target: &str, root: &KeyPair, test: bool) -> TestRes .build_with_rng(&root, SymbolTable::default(), &mut rng) .unwrap(); - let keypair2 = KeyPair::new_with_rng(&mut rng); + let keypair2 = KeyPair::new_with_rng(Algorithm::Ed25519, &mut rng); let biscuit2 = biscuit1 .append_with_keypair( &keypair2, @@ -666,7 +666,7 @@ fn random_block(target: &str, root: &KeyPair, test: bool) -> TestResult { .build_with_rng(&root, SymbolTable::default(), &mut rng) .unwrap(); - let keypair2 = KeyPair::new_with_rng(&mut rng); + let keypair2 = KeyPair::new_with_rng(Algorithm::Ed25519, &mut rng); let biscuit2 = biscuit1 .append_with_keypair( &keypair2, @@ -721,7 +721,7 @@ fn invalid_signature(target: &str, root: &KeyPair, test: bool) -> TestResult { .build_with_rng(&root, SymbolTable::default(), &mut rng) .unwrap(); - let keypair2 = KeyPair::new_with_rng(&mut rng); + let keypair2 = KeyPair::new_with_rng(Algorithm::Ed25519, &mut rng); let biscuit2 = biscuit1 .append_with_keypair( &keypair2, @@ -775,7 +775,7 @@ fn reordered_blocks(target: &str, root: &KeyPair, test: bool) -> TestResult { .build_with_rng(&root, SymbolTable::default(), &mut rng) .unwrap(); - let keypair2 = KeyPair::new_with_rng(&mut rng); + let keypair2 = KeyPair::new_with_rng(Algorithm::Ed25519, &mut rng); let biscuit2 = biscuit1 .append_with_keypair( &keypair2, @@ -783,7 +783,7 @@ fn reordered_blocks(target: &str, root: &KeyPair, test: bool) -> TestResult { ) .unwrap(); - let keypair3 = KeyPair::new_with_rng(&mut rng); + let keypair3 = KeyPair::new_with_rng(Algorithm::Ed25519, &mut rng); let biscuit3 = biscuit2 .append_with_keypair(&keypair3, block!(r#"check if resource("file1")"#)) .unwrap(); @@ -833,7 +833,7 @@ fn scoped_rules(target: &str, root: &KeyPair, test: bool) -> TestResult { .build_with_rng(&root, SymbolTable::default(), &mut rng) .unwrap(); - let keypair2 = KeyPair::new_with_rng(&mut rng); + let keypair2 = KeyPair::new_with_rng(Algorithm::Ed25519, &mut rng); let biscuit2 = biscuit1 .append_with_keypair( &keypair2, @@ -850,7 +850,7 @@ fn scoped_rules(target: &str, root: &KeyPair, test: bool) -> TestResult { block3.add_fact(r#"owner("alice", "file2")"#).unwrap(); - let keypair3 = KeyPair::new_with_rng(&mut rng); + let keypair3 = KeyPair::new_with_rng(Algorithm::Ed25519, &mut rng); let biscuit3 = biscuit2.append_with_keypair(&keypair3, block3).unwrap(); token = print_blocks(&biscuit3); @@ -892,7 +892,7 @@ fn scoped_checks(target: &str, root: &KeyPair, test: bool) -> TestResult { .build_with_rng(&root, SymbolTable::default(), &mut rng) .unwrap(); - let keypair2 = KeyPair::new_with_rng(&mut rng); + let keypair2 = KeyPair::new_with_rng(Algorithm::Ed25519, &mut rng); let biscuit2 = biscuit1 .append_with_keypair( &keypair2, @@ -900,7 +900,7 @@ fn scoped_checks(target: &str, root: &KeyPair, test: bool) -> TestResult { ) .unwrap(); - let keypair3 = KeyPair::new_with_rng(&mut rng); + let keypair3 = KeyPair::new_with_rng(Algorithm::Ed25519, &mut rng); let biscuit3 = biscuit2 .append_with_keypair(&keypair3, block!(r#"right("file2", "read")"#)) .unwrap(); @@ -950,7 +950,7 @@ fn expired_token(target: &str, root: &KeyPair, test: bool) -> TestResult { .unwrap(), ); - let keypair2 = KeyPair::new_with_rng(&mut rng); + let keypair2 = KeyPair::new_with_rng(Algorithm::Ed25519, &mut rng); let biscuit2 = biscuit1.append_with_keypair(&keypair2, block2).unwrap(); token = print_blocks(&biscuit2); @@ -993,7 +993,7 @@ fn authorizer_scope(target: &str, root: &KeyPair, test: bool) -> TestResult { .build_with_rng(&root, SymbolTable::default(), &mut rng) .unwrap(); - let keypair2 = KeyPair::new_with_rng(&mut rng); + let keypair2 = KeyPair::new_with_rng(Algorithm::Ed25519, &mut rng); let biscuit2 = biscuit1 .append_with_keypair(&keypair2, block!(r#"right("file2", "read")"#)) .unwrap(); @@ -1127,7 +1127,7 @@ fn block_rules(target: &str, root: &KeyPair, test: bool) -> TestResult { .build_with_rng(&root, SymbolTable::default(), &mut rng) .unwrap(); - let keypair2 = KeyPair::new_with_rng(&mut rng); + let keypair2 = KeyPair::new_with_rng(Algorithm::Ed25519, &mut rng); let biscuit2 = biscuit1.append_with_keypair(&keypair2, block!(r#" // generate valid_date("file1") if before Thursday, December 31, 2030 12:59:59 PM UTC valid_date("file1") <- time($0), resource("file1"), $0 <= 2030-12-31T12:59:59Z; @@ -1248,7 +1248,7 @@ fn check_head_name(target: &str, root: &KeyPair, test: bool) -> TestResult { .build_with_rng(&root, SymbolTable::default(), &mut rng) .unwrap(); - let keypair2 = KeyPair::new_with_rng(&mut rng); + let keypair2 = KeyPair::new_with_rng(Algorithm::Ed25519, &mut rng); let biscuit2 = biscuit1 .append_with_keypair(&keypair2, block!(r#"query("test")"#)) .unwrap(); @@ -1395,7 +1395,7 @@ fn unbound_variables_in_rule(target: &str, root: &KeyPair, test: bool) -> TestRe )) .unwrap(); - let keypair2 = KeyPair::new_with_rng(&mut rng); + let keypair2 = KeyPair::new_with_rng(Algorithm::Ed25519, &mut rng); let biscuit2 = biscuit1.append_with_keypair(&keypair2, block2).unwrap(); token = print_blocks(&biscuit2); @@ -1425,7 +1425,7 @@ fn generating_ambient_from_variables(target: &str, root: &KeyPair, test: bool) - .build_with_rng(&root, SymbolTable::default(), &mut rng) .unwrap(); - let keypair2 = KeyPair::new_with_rng(&mut rng); + let keypair2 = KeyPair::new_with_rng(Algorithm::Ed25519, &mut rng); let biscuit2 = biscuit1 .append_with_keypair(&keypair2, block!(r#"operation("read") <- operation($any)"#)) .unwrap(); @@ -1462,7 +1462,7 @@ fn sealed_token(target: &str, root: &KeyPair, test: bool) -> TestResult { .build_with_rng(&root, SymbolTable::default(), &mut rng) .unwrap(); - let keypair2 = KeyPair::new_with_rng(&mut rng); + let keypair2 = KeyPair::new_with_rng(Algorithm::Ed25519, &mut rng); let biscuit2 = biscuit1 .append_with_keypair( &keypair2, @@ -1589,12 +1589,12 @@ fn execution_scope(target: &str, root: &KeyPair, test: bool) -> TestResult { .build_with_rng(&root, SymbolTable::default(), &mut rng) .unwrap(); - let keypair2 = KeyPair::new_with_rng(&mut rng); + let keypair2 = KeyPair::new_with_rng(Algorithm::Ed25519, &mut rng); let biscuit2 = biscuit1 .append_with_keypair(&keypair2, block!("block1_fact(1)")) .unwrap(); - let keypair3 = KeyPair::new_with_rng(&mut rng); + let keypair3 = KeyPair::new_with_rng(Algorithm::Ed25519, &mut rng); let biscuit3 = biscuit2 .append_with_keypair( &keypair3, @@ -1631,7 +1631,7 @@ fn third_party(target: &str, root: &KeyPair, test: bool) -> TestResult { let token; // keep this to conserve the same RNG state - let _ = KeyPair::new_with_rng(&mut rng); + let _ = KeyPair::new_with_rng(Algorithm::Ed25519, &mut rng); let external = KeyPair::from( &PrivateKey::from_bytes_hex( "12aca40167fbdd1a11037e9fd440e3d510d9d9dea70a6646aa4aaf84d718d75a", @@ -1663,7 +1663,7 @@ fn third_party(target: &str, root: &KeyPair, test: bool) -> TestResult { ), ) .unwrap(); - let keypair2 = KeyPair::new_with_rng(&mut rng); + let keypair2 = KeyPair::new_with_rng(Algorithm::Ed25519, &mut rng); let biscuit2 = biscuit1 .append_third_party_with_keypair(external.public(), res, keypair2) .unwrap(); @@ -1747,9 +1747,9 @@ fn public_keys_interning(target: &str, root: &KeyPair, test: bool) -> TestResult let token; // keep this to conserve the same RNG state - let _ = KeyPair::new_with_rng(&mut rng); - let _ = KeyPair::new_with_rng(&mut rng); - let _ = KeyPair::new_with_rng(&mut rng); + let _ = KeyPair::new_with_rng(Algorithm::Ed25519, &mut rng); + let _ = KeyPair::new_with_rng(Algorithm::Ed25519, &mut rng); + let _ = KeyPair::new_with_rng(Algorithm::Ed25519, &mut rng); let external1 = KeyPair::from( &PrivateKey::from_bytes_hex( @@ -1802,7 +1802,11 @@ fn public_keys_interning(target: &str, root: &KeyPair, test: bool) -> TestResult .unwrap(); let biscuit2 = biscuit1 - .append_third_party_with_keypair(external1.public(), res1, KeyPair::new_with_rng(&mut rng)) + .append_third_party_with_keypair( + external1.public(), + res1, + KeyPair::new_with_rng(Algorithm::Ed25519, &mut rng), + ) .unwrap(); let req2 = biscuit2.third_party_request().unwrap(); @@ -1822,7 +1826,11 @@ fn public_keys_interning(target: &str, root: &KeyPair, test: bool) -> TestResult .unwrap(); let biscuit3 = biscuit2 - .append_third_party_with_keypair(external2.public(), res2, KeyPair::new_with_rng(&mut rng)) + .append_third_party_with_keypair( + external2.public(), + res2, + KeyPair::new_with_rng(Algorithm::Ed25519, &mut rng), + ) .unwrap(); let req3 = biscuit3.third_party_request().unwrap(); @@ -1842,12 +1850,16 @@ fn public_keys_interning(target: &str, root: &KeyPair, test: bool) -> TestResult .unwrap(); let biscuit4 = biscuit3 - .append_third_party_with_keypair(external2.public(), res3, KeyPair::new_with_rng(&mut rng)) + .append_third_party_with_keypair( + external2.public(), + res3, + KeyPair::new_with_rng(Algorithm::Ed25519, &mut rng), + ) .unwrap(); let biscuit5 = biscuit4 .append_with_keypair( - &KeyPair::new_with_rng(&mut rng), + &KeyPair::new_with_rng(Algorithm::Ed25519, &mut rng), block!( r#" query(4); @@ -2092,7 +2104,7 @@ fn secp256r1(target: &str, root: &KeyPair, test: bool) -> TestResult { let filename = "test032_secp256r1".to_string(); let token; - let keypair2 = KeyPair::new_secp256r1_with_rng(&mut rng); + let keypair2 = KeyPair::new_with_rng(Algorithm::Secp256r1, &mut rng); let biscuit1 = biscuit!( r#" right("file1", "read"); @@ -2103,7 +2115,7 @@ fn secp256r1(target: &str, root: &KeyPair, test: bool) -> TestResult { .build_with_key_pair(&root, SymbolTable::default(), &keypair2) .unwrap(); - let keypair3 = KeyPair::new_secp256r1_with_rng(&mut rng); + let keypair3 = KeyPair::new_with_rng(Algorithm::Secp256r1, &mut rng); let biscuit2 = biscuit1 .append_with_keypair( &keypair3, diff --git a/biscuit-auth/samples/README.md b/biscuit-auth/samples/README.md index e69de29b..cc0dbd5a 100644 --- a/biscuit-auth/samples/README.md +++ b/biscuit-auth/samples/README.md @@ -0,0 +1,2852 @@ +# Biscuit samples and expected results + +root secret key: 99e87b0e9158531eeeb503ff15266e2b23c2a2507b138c9d1b1f2ab458df2d61 +root public key: 1055c750b1a1505937af1537c626ba3263995c33a64758aaafb1275b0312e284 + +------------------------------ + +## basic token: test001_basic.bc +### token + +authority: +symbols: ["file1", "file2"] + +public keys: [] + +``` +right("file1", "read"); +right("file2", "read"); +right("file1", "write"); +``` + +1: +symbols: ["0"] + +public keys: [] + +``` +check if resource($0), operation("read"), right($0, "read"); +``` + +### validation + +authorizer code: +``` +resource("file1"); + +allow if true; +``` + +revocation ids: +- `7595a112a1eb5b81a6e398852e6118b7f5b8cbbff452778e655100e5fb4faa8d3a2af52fe2c4f9524879605675fae26adbc4783e0cafc43522fa82385f396c03` +- `45f4c14f9d9e8fa044d68be7a2ec8cddb835f575c7b913ec59bd636c70acae9a90db9064ba0b3084290ed0c422bbb7170092a884f5e0202b31e9235bbcc1650d` + +authorizer world: +``` +World { + facts: [ + Facts { + origin: { + None, + }, + facts: [ + "resource(\"file1\")", + ], + }, + Facts { + origin: { + Some( + 0, + ), + }, + facts: [ + "right(\"file1\", \"read\")", + "right(\"file1\", \"write\")", + "right(\"file2\", \"read\")", + ], + }, +] + rules: [] + checks: [ + Checks { + origin: Some( + 1, + ), + checks: [ + "check if resource($0), operation(\"read\"), right($0, \"read\")", + ], + }, +] + policies: [ + "allow if true", +] +} +``` + +result: `Err(FailedLogic(Unauthorized { policy: Allow(0), checks: [Block(FailedBlockCheck { block_id: 1, check_id: 0, rule: "check if resource($0), operation(\"read\"), right($0, \"read\")" })] }))` + + +------------------------------ + +## different root key: test002_different_root_key.bc +### token + +authority: +symbols: ["file1"] + +public keys: [] + +``` +right("file1", "read"); +``` + +1: +symbols: ["0"] + +public keys: [] + +``` +check if resource($0), operation("read"), right($0, "read"); +``` + +### validation + +result: `Err(Format(Signature(InvalidSignature("signature error: Verification equation was not satisfied"))))` + + +------------------------------ + +## invalid signature format: test003_invalid_signature_format.bc +### token + +authority: +symbols: ["file1", "file2"] + +public keys: [] + +``` +right("file1", "read"); +right("file2", "read"); +right("file1", "write"); +``` + +1: +symbols: ["0"] + +public keys: [] + +``` +check if resource($0), operation("read"), right($0, "read"); +``` + +### validation + +result: `Err(Format(BlockSignatureDeserializationError("block signature deserialization error: [117, 149, 161, 18, 161, 235, 91, 129, 166, 227, 152, 133, 46, 97, 24, 183]")))` + + +------------------------------ + +## random block: test004_random_block.bc +### token + +authority: +symbols: ["file1", "file2"] + +public keys: [] + +``` +right("file1", "read"); +right("file2", "read"); +right("file1", "write"); +``` + +1: +symbols: ["0"] + +public keys: [] + +``` +check if resource($0), operation("read"), right($0, "read"); +``` + +### validation + +result: `Err(Format(Signature(InvalidSignature("signature error: Verification equation was not satisfied"))))` + + +------------------------------ + +## invalid signature: test005_invalid_signature.bc +### token + +authority: +symbols: ["file1", "file2"] + +public keys: [] + +``` +right("file1", "read"); +right("file2", "read"); +right("file1", "write"); +``` + +1: +symbols: ["0"] + +public keys: [] + +``` +check if resource($0), operation("read"), right($0, "read"); +``` + +### validation + +result: `Err(Format(Signature(InvalidSignature("signature error: Verification equation was not satisfied"))))` + + +------------------------------ + +## reordered blocks: test006_reordered_blocks.bc +### token + +authority: +symbols: ["file1", "file2"] + +public keys: [] + +``` +right("file1", "read"); +right("file2", "read"); +right("file1", "write"); +``` + +1: +symbols: ["0"] + +public keys: [] + +``` +check if resource($0), operation("read"), right($0, "read"); +``` + +2: +symbols: [] + +public keys: [] + +``` +check if resource("file1"); +``` + +### validation + +result: `Err(Format(Signature(InvalidSignature("signature error: Verification equation was not satisfied"))))` + + +------------------------------ + +## scoped rules: test007_scoped_rules.bc +### token + +authority: +symbols: ["user_id", "alice", "file1"] + +public keys: [] + +``` +user_id("alice"); +owner("alice", "file1"); +``` + +1: +symbols: ["0", "1"] + +public keys: [] + +``` +right($0, "read") <- resource($0), user_id($1), owner($1, $0); +check if resource($0), operation("read"), right($0, "read"); +``` + +2: +symbols: ["file2"] + +public keys: [] + +``` +owner("alice", "file2"); +``` + +### validation + +authorizer code: +``` +resource("file2"); +operation("read"); + +allow if true; +``` + +revocation ids: +- `4d86c9af808dc2e0583f47282e6f5df3e09dc264d5231ec360b4519e15ddaeec60b25a9bbcb22e8d192f4d36a0da3f9243711e30535b00ee55c53cb1395f230a` +- `63208c668c66f3ba6927140ba37533593b25e03459447805d4b2a8b75adeef45794c3d7249afe506ed77ccee276160bb4052a4009302bd34871a440f070b4509` +- `d8da982888eae8c038e4894a8c06fc57d8e5f06ad2e972b9cf4bde49ad60804558a0d1938192596c702d8e4f7f12ec19201d7c33d0cd77774a0d879a33880d02` + +authorizer world: +``` +World { + facts: [ + Facts { + origin: { + None, + }, + facts: [ + "operation(\"read\")", + "resource(\"file2\")", + ], + }, + Facts { + origin: { + Some( + 0, + ), + }, + facts: [ + "owner(\"alice\", \"file1\")", + "user_id(\"alice\")", + ], + }, + Facts { + origin: { + Some( + 2, + ), + }, + facts: [ + "owner(\"alice\", \"file2\")", + ], + }, +] + rules: [ + Rules { + origin: Some( + 1, + ), + rules: [ + "right($0, \"read\") <- resource($0), user_id($1), owner($1, $0)", + ], + }, +] + checks: [ + Checks { + origin: Some( + 1, + ), + checks: [ + "check if resource($0), operation(\"read\"), right($0, \"read\")", + ], + }, +] + policies: [ + "allow if true", +] +} +``` + +result: `Err(FailedLogic(Unauthorized { policy: Allow(0), checks: [Block(FailedBlockCheck { block_id: 1, check_id: 0, rule: "check if resource($0), operation(\"read\"), right($0, \"read\")" })] }))` + + +------------------------------ + +## scoped checks: test008_scoped_checks.bc +### token + +authority: +symbols: ["file1"] + +public keys: [] + +``` +right("file1", "read"); +``` + +1: +symbols: ["0"] + +public keys: [] + +``` +check if resource($0), operation("read"), right($0, "read"); +``` + +2: +symbols: ["file2"] + +public keys: [] + +``` +right("file2", "read"); +``` + +### validation + +authorizer code: +``` +resource("file2"); +operation("read"); + +allow if true; +``` + +revocation ids: +- `a80c985ddef895518c216f64c65dcd50a5d97d012a94453d79159aed2981654b1fe9748c686c5667604026a94fb8db8a1d02de747df61e99fa9a63ff2878ad00` +- `77df45442be86a416aa02fd9d98d6d4703c634a9e3b1d293b41f5dc97849afbe7faeec8c22a210574888acc008fb64fe691ec9e8d2655586f970d9a6b6577000` +- `b31398aefe97d3db41ebc445760f216fb3aa7bf7439adcfc3a07489bfcc163970af3f4e20f5460aa24cf841101a5ab114d21acc0ee8d442bae7793b121284900` + +authorizer world: +``` +World { + facts: [ + Facts { + origin: { + None, + }, + facts: [ + "operation(\"read\")", + "resource(\"file2\")", + ], + }, + Facts { + origin: { + Some( + 0, + ), + }, + facts: [ + "right(\"file1\", \"read\")", + ], + }, + Facts { + origin: { + Some( + 2, + ), + }, + facts: [ + "right(\"file2\", \"read\")", + ], + }, +] + rules: [] + checks: [ + Checks { + origin: Some( + 1, + ), + checks: [ + "check if resource($0), operation(\"read\"), right($0, \"read\")", + ], + }, +] + policies: [ + "allow if true", +] +} +``` + +result: `Err(FailedLogic(Unauthorized { policy: Allow(0), checks: [Block(FailedBlockCheck { block_id: 1, check_id: 0, rule: "check if resource($0), operation(\"read\"), right($0, \"read\")" })] }))` + + +------------------------------ + +## expired token: test009_expired_token.bc +### token + +authority: +symbols: [] + +public keys: [] + +``` +``` + +1: +symbols: ["file1"] + +public keys: [] + +``` +check if resource("file1"); +check if time($time), $time <= 2018-12-20T00:00:00Z; +``` + +### validation + +authorizer code: +``` +resource("file1"); +operation("read"); +time(2020-12-21T09:23:12Z); + +allow if true; +``` + +revocation ids: +- `c248907bb6e5f433bbb5edf6367b399ebefca0d321d0b2ea9fc67f66dc1064ce926adb0c05d90c3e8a2833328b3578f79c4e1bca43583d9bcfb2ba6c37303d00` +- `a4edf7aaea8658bb9ae19b3ffe2adcc77cc9f16c249aeb0a85a584b5362f89f27f7c67ac0af16d7170673d6d1fb1563d1934b25ec5a461f6c01fa49805cd5e07` + +authorizer world: +``` +World { + facts: [ + Facts { + origin: { + None, + }, + facts: [ + "operation(\"read\")", + "resource(\"file1\")", + "time(2020-12-21T09:23:12Z)", + ], + }, +] + rules: [] + checks: [ + Checks { + origin: Some( + 1, + ), + checks: [ + "check if resource(\"file1\")", + "check if time($time), $time <= 2018-12-20T00:00:00Z", + ], + }, +] + policies: [ + "allow if true", +] +} +``` + +result: `Err(FailedLogic(Unauthorized { policy: Allow(0), checks: [Block(FailedBlockCheck { block_id: 1, check_id: 1, rule: "check if time($time), $time <= 2018-12-20T00:00:00Z" })] }))` + + +------------------------------ + +## authorizer scope: test010_authorizer_scope.bc +### token + +authority: +symbols: ["file1"] + +public keys: [] + +``` +right("file1", "read"); +``` + +1: +symbols: ["file2"] + +public keys: [] + +``` +right("file2", "read"); +``` + +### validation + +authorizer code: +``` +resource("file2"); +operation("read"); + +check if right($0, $1), resource($0), operation($1); + +allow if true; +``` + +revocation ids: +- `a80c985ddef895518c216f64c65dcd50a5d97d012a94453d79159aed2981654b1fe9748c686c5667604026a94fb8db8a1d02de747df61e99fa9a63ff2878ad00` +- `966eceb2aa937c41b25368808bab6e0698c02a4038de669d007c9c3d43602638a640083558d1576ac80cf3eb2ac6a7585527e0f6c1a65402f0935cf7f4df8005` + +authorizer world: +``` +World { + facts: [ + Facts { + origin: { + None, + }, + facts: [ + "operation(\"read\")", + "resource(\"file2\")", + ], + }, + Facts { + origin: { + Some( + 0, + ), + }, + facts: [ + "right(\"file1\", \"read\")", + ], + }, + Facts { + origin: { + Some( + 1, + ), + }, + facts: [ + "right(\"file2\", \"read\")", + ], + }, +] + rules: [] + checks: [ + Checks { + origin: Some( + 18446744073709551615, + ), + checks: [ + "check if right($0, $1), resource($0), operation($1)", + ], + }, +] + policies: [ + "allow if true", +] +} +``` + +result: `Err(FailedLogic(Unauthorized { policy: Allow(0), checks: [Authorizer(FailedAuthorizerCheck { check_id: 0, rule: "check if right($0, $1), resource($0), operation($1)" })] }))` + + +------------------------------ + +## authorizer authority checks: test011_authorizer_authority_caveats.bc +### token + +authority: +symbols: ["file1"] + +public keys: [] + +``` +right("file1", "read"); +``` + +### validation + +authorizer code: +``` +resource("file2"); +operation("read"); + +check if right($0, $1), resource($0), operation($1); + +allow if true; +``` + +revocation ids: +- `a80c985ddef895518c216f64c65dcd50a5d97d012a94453d79159aed2981654b1fe9748c686c5667604026a94fb8db8a1d02de747df61e99fa9a63ff2878ad00` + +authorizer world: +``` +World { + facts: [ + Facts { + origin: { + None, + }, + facts: [ + "operation(\"read\")", + "resource(\"file2\")", + ], + }, + Facts { + origin: { + Some( + 0, + ), + }, + facts: [ + "right(\"file1\", \"read\")", + ], + }, +] + rules: [] + checks: [ + Checks { + origin: Some( + 18446744073709551615, + ), + checks: [ + "check if right($0, $1), resource($0), operation($1)", + ], + }, +] + policies: [ + "allow if true", +] +} +``` + +result: `Err(FailedLogic(Unauthorized { policy: Allow(0), checks: [Authorizer(FailedAuthorizerCheck { check_id: 0, rule: "check if right($0, $1), resource($0), operation($1)" })] }))` + + +------------------------------ + +## authority checks: test012_authority_caveats.bc +### token + +authority: +symbols: ["file1"] + +public keys: [] + +``` +check if resource("file1"); +``` + +### validation for "file1" + +authorizer code: +``` +resource("file1"); +operation("read"); + +allow if true; +``` + +revocation ids: +- `6a8f90dad67ae2ac188460463914ae7326fda431c80785755f4edcc15f1a53911f7366e606ad80cbbeba94672e42713e88632a932128f1d796ce9ba7d7a0b80a` + +authorizer world: +``` +World { + facts: [ + Facts { + origin: { + None, + }, + facts: [ + "operation(\"read\")", + "resource(\"file1\")", + ], + }, +] + rules: [] + checks: [ + Checks { + origin: Some( + 0, + ), + checks: [ + "check if resource(\"file1\")", + ], + }, +] + policies: [ + "allow if true", +] +} +``` + +result: `Ok(0)` +### validation for "file2" + +authorizer code: +``` +resource("file2"); +operation("read"); + +allow if true; +``` + +revocation ids: +- `6a8f90dad67ae2ac188460463914ae7326fda431c80785755f4edcc15f1a53911f7366e606ad80cbbeba94672e42713e88632a932128f1d796ce9ba7d7a0b80a` + +authorizer world: +``` +World { + facts: [ + Facts { + origin: { + None, + }, + facts: [ + "operation(\"read\")", + "resource(\"file2\")", + ], + }, +] + rules: [] + checks: [ + Checks { + origin: Some( + 0, + ), + checks: [ + "check if resource(\"file1\")", + ], + }, +] + policies: [ + "allow if true", +] +} +``` + +result: `Err(FailedLogic(Unauthorized { policy: Allow(0), checks: [Block(FailedBlockCheck { block_id: 0, check_id: 0, rule: "check if resource(\"file1\")" })] }))` + + +------------------------------ + +## block rules: test013_block_rules.bc +### token + +authority: +symbols: ["file1", "file2"] + +public keys: [] + +``` +right("file1", "read"); +right("file2", "read"); +``` + +1: +symbols: ["valid_date", "0", "1"] + +public keys: [] + +``` +valid_date("file1") <- time($0), resource("file1"), $0 <= 2030-12-31T12:59:59Z; +valid_date($1) <- time($0), resource($1), $0 <= 1999-12-31T12:59:59Z, !["file1"].contains($1); +check if valid_date($0), resource($0); +``` + +### validation for "file1" + +authorizer code: +``` +resource("file1"); +time(2020-12-21T09:23:12Z); + +allow if true; +``` + +revocation ids: +- `c46d071ff3f33434223c8305fdad529f62bf78bb5d9cbfc2a345d4bca6bf314014840e18ba353f86fdb9073d58b12b8c872ac1f8e593c2e9064b90f6c2ede006` +- `a0c4c163a0b3ca406df4ece3d1371356190df04208eccef72f77e875ed0531b5d37e243d6f388b1967776a5dfd16ef228f19c5bdd6d2820f145c5ed3c3dcdc00` + +authorizer world: +``` +World { + facts: [ + Facts { + origin: { + None, + }, + facts: [ + "resource(\"file1\")", + "time(2020-12-21T09:23:12Z)", + ], + }, + Facts { + origin: { + None, + Some( + 1, + ), + }, + facts: [ + "valid_date(\"file1\")", + ], + }, + Facts { + origin: { + Some( + 0, + ), + }, + facts: [ + "right(\"file1\", \"read\")", + "right(\"file2\", \"read\")", + ], + }, +] + rules: [ + Rules { + origin: Some( + 1, + ), + rules: [ + "valid_date(\"file1\") <- time($0), resource(\"file1\"), $0 <= 2030-12-31T12:59:59Z", + "valid_date($1) <- time($0), resource($1), $0 <= 1999-12-31T12:59:59Z, ![\"file1\"].contains($1)", + ], + }, +] + checks: [ + Checks { + origin: Some( + 1, + ), + checks: [ + "check if valid_date($0), resource($0)", + ], + }, +] + policies: [ + "allow if true", +] +} +``` + +result: `Ok(0)` +### validation for "file2" + +authorizer code: +``` +resource("file2"); +time(2020-12-21T09:23:12Z); + +allow if true; +``` + +revocation ids: +- `c46d071ff3f33434223c8305fdad529f62bf78bb5d9cbfc2a345d4bca6bf314014840e18ba353f86fdb9073d58b12b8c872ac1f8e593c2e9064b90f6c2ede006` +- `a0c4c163a0b3ca406df4ece3d1371356190df04208eccef72f77e875ed0531b5d37e243d6f388b1967776a5dfd16ef228f19c5bdd6d2820f145c5ed3c3dcdc00` + +authorizer world: +``` +World { + facts: [ + Facts { + origin: { + None, + }, + facts: [ + "resource(\"file2\")", + "time(2020-12-21T09:23:12Z)", + ], + }, + Facts { + origin: { + Some( + 0, + ), + }, + facts: [ + "right(\"file1\", \"read\")", + "right(\"file2\", \"read\")", + ], + }, +] + rules: [ + Rules { + origin: Some( + 1, + ), + rules: [ + "valid_date(\"file1\") <- time($0), resource(\"file1\"), $0 <= 2030-12-31T12:59:59Z", + "valid_date($1) <- time($0), resource($1), $0 <= 1999-12-31T12:59:59Z, ![\"file1\"].contains($1)", + ], + }, +] + checks: [ + Checks { + origin: Some( + 1, + ), + checks: [ + "check if valid_date($0), resource($0)", + ], + }, +] + policies: [ + "allow if true", +] +} +``` + +result: `Err(FailedLogic(Unauthorized { policy: Allow(0), checks: [Block(FailedBlockCheck { block_id: 1, check_id: 0, rule: "check if valid_date($0), resource($0)" })] }))` + + +------------------------------ + +## regex_constraint: test014_regex_constraint.bc +### token + +authority: +symbols: ["0", "file[0-9]+.txt"] + +public keys: [] + +``` +check if resource($0), $0.matches("file[0-9]+.txt"); +``` + +### validation for "file1" + +authorizer code: +``` +resource("file1"); + +allow if true; +``` + +revocation ids: +- `da42718ad2631c12d3a44b7710dcc76c6c7809c6bc3a2d7eb0378c4154eae10e0884a8d54a2cd25ca3dfe01091d816ebbb9d246227baf7a359a787cb2344ad07` + +authorizer world: +``` +World { + facts: [ + Facts { + origin: { + None, + }, + facts: [ + "resource(\"file1\")", + ], + }, +] + rules: [] + checks: [ + Checks { + origin: Some( + 0, + ), + checks: [ + "check if resource($0), $0.matches(\"file[0-9]+.txt\")", + ], + }, +] + policies: [ + "allow if true", +] +} +``` + +result: `Err(FailedLogic(Unauthorized { policy: Allow(0), checks: [Block(FailedBlockCheck { block_id: 0, check_id: 0, rule: "check if resource($0), $0.matches(\"file[0-9]+.txt\")" })] }))` +### validation for "file123" + +authorizer code: +``` +resource("file123.txt"); + +allow if true; +``` + +revocation ids: +- `da42718ad2631c12d3a44b7710dcc76c6c7809c6bc3a2d7eb0378c4154eae10e0884a8d54a2cd25ca3dfe01091d816ebbb9d246227baf7a359a787cb2344ad07` + +authorizer world: +``` +World { + facts: [ + Facts { + origin: { + None, + }, + facts: [ + "resource(\"file123.txt\")", + ], + }, +] + rules: [] + checks: [ + Checks { + origin: Some( + 0, + ), + checks: [ + "check if resource($0), $0.matches(\"file[0-9]+.txt\")", + ], + }, +] + policies: [ + "allow if true", +] +} +``` + +result: `Ok(0)` + + +------------------------------ + +## multi queries checks: test015_multi_queries_caveats.bc +### token + +authority: +symbols: ["must_be_present", "hello"] + +public keys: [] + +``` +must_be_present("hello"); +``` + +### validation + +authorizer code: +``` +check if must_be_present($0) or must_be_present($0); + +allow if true; +``` + +revocation ids: +- `b0d466d31e015fa85a075fa875f7e1c9017edd503fee9f62a5f033e1fcfa811074b6e39dfe5af2f452043db97a3f98650592a370f5685b62c5d6abf9dd10b603` + +authorizer world: +``` +World { + facts: [ + Facts { + origin: { + Some( + 0, + ), + }, + facts: [ + "must_be_present(\"hello\")", + ], + }, +] + rules: [] + checks: [ + Checks { + origin: Some( + 18446744073709551615, + ), + checks: [ + "check if must_be_present($0) or must_be_present($0)", + ], + }, +] + policies: [ + "allow if true", +] +} +``` + +result: `Ok(0)` + + +------------------------------ + +## check head name should be independent from fact names: test016_caveat_head_name.bc +### token + +authority: +symbols: ["hello"] + +public keys: [] + +``` +check if resource("hello"); +``` + +1: +symbols: ["test"] + +public keys: [] + +``` +query("test"); +``` + +### validation + +authorizer code: +``` +allow if true; +``` + +revocation ids: +- `ce6f804f4390e693a8853d9a4a10bd4f3c94b86b7c6d671993a6e19346bc4d20bbb52cc945e5d0d02e4e75fa5da2caa99764050190353564a0a0b4b276809402` +- `916d566cc724e0773046fc5266e9d0d804311435b8d6955b332f823ab296be9a78dfea190447732ac9f6217234cf5726becf88f65169c6de56a766af55451b0f` + +authorizer world: +``` +World { + facts: [ + Facts { + origin: { + Some( + 1, + ), + }, + facts: [ + "query(\"test\")", + ], + }, +] + rules: [] + checks: [ + Checks { + origin: Some( + 0, + ), + checks: [ + "check if resource(\"hello\")", + ], + }, +] + policies: [ + "allow if true", +] +} +``` + +result: `Err(FailedLogic(Unauthorized { policy: Allow(0), checks: [Block(FailedBlockCheck { block_id: 0, check_id: 0, rule: "check if resource(\"hello\")" })] }))` + + +------------------------------ + +## test expression syntax and all available operations: test017_expressions.bc +### token + +authority: +symbols: ["hello world", "hello", "world", "aaabde", "a*c?.e", "abd", "aaa", "b", "de", "abcD12", "é", "abc", "def"] + +public keys: [] + +``` +check if true; +check if !false; +check if !false && true; +check if false || true; +check if (true || false) && true; +check if true === true; +check if false === false; +check if 1 < 2; +check if 2 > 1; +check if 1 <= 2; +check if 1 <= 1; +check if 2 >= 1; +check if 2 >= 2; +check if 3 === 3; +check if 1 + 2 * 3 - 4 / 2 === 5; +check if "hello world".starts_with("hello") && "hello world".ends_with("world"); +check if "aaabde".matches("a*c?.e"); +check if "aaabde".contains("abd"); +check if "aaabde" === "aaa" + "b" + "de"; +check if "abcD12" === "abcD12"; +check if "abcD12".length() === 6; +check if "é".length() === 2; +check if 2019-12-04T09:46:41Z < 2020-12-04T09:46:41Z; +check if 2020-12-04T09:46:41Z > 2019-12-04T09:46:41Z; +check if 2019-12-04T09:46:41Z <= 2020-12-04T09:46:41Z; +check if 2020-12-04T09:46:41Z >= 2020-12-04T09:46:41Z; +check if 2020-12-04T09:46:41Z >= 2019-12-04T09:46:41Z; +check if 2020-12-04T09:46:41Z >= 2020-12-04T09:46:41Z; +check if 2020-12-04T09:46:41Z === 2020-12-04T09:46:41Z; +check if hex:12ab === hex:12ab; +check if [1, 2].contains(2); +check if [2019-12-04T09:46:41Z, 2020-12-04T09:46:41Z].contains(2020-12-04T09:46:41Z); +check if [false, true].contains(true); +check if ["abc", "def"].contains("abc"); +check if [hex:12ab, hex:34de].contains(hex:34de); +check if [1, 2].contains([2]); +check if [1, 2] === [1, 2]; +check if [1, 2].intersection([2, 3]) === [2]; +check if [1, 2].union([2, 3]) === [1, 2, 3]; +check if [1, 2, 3].intersection([1, 2]).contains(1); +check if [1, 2, 3].intersection([1, 2]).length() === 2; +``` + +### validation + +authorizer code: +``` +allow if true; +``` + +revocation ids: +- `3d5b23b502b3dd920bfb68b9039164d1563bb8927210166fa5c17f41b76b31bb957bc2ed3318452958f658baa2d398fe4cf25c58a27e6c8bc42c9702c8aa1b0c` + +authorizer world: +``` +World { + facts: [] + rules: [] + checks: [ + Checks { + origin: Some( + 0, + ), + checks: [ + "check if !false", + "check if !false && true", + "check if \"aaabde\" === \"aaa\" + \"b\" + \"de\"", + "check if \"aaabde\".contains(\"abd\")", + "check if \"aaabde\".matches(\"a*c?.e\")", + "check if \"abcD12\" === \"abcD12\"", + "check if \"abcD12\".length() === 6", + "check if \"hello world\".starts_with(\"hello\") && \"hello world\".ends_with(\"world\")", + "check if \"é\".length() === 2", + "check if (true || false) && true", + "check if 1 + 2 * 3 - 4 / 2 === 5", + "check if 1 < 2", + "check if 1 <= 1", + "check if 1 <= 2", + "check if 2 > 1", + "check if 2 >= 1", + "check if 2 >= 2", + "check if 2019-12-04T09:46:41Z < 2020-12-04T09:46:41Z", + "check if 2019-12-04T09:46:41Z <= 2020-12-04T09:46:41Z", + "check if 2020-12-04T09:46:41Z === 2020-12-04T09:46:41Z", + "check if 2020-12-04T09:46:41Z > 2019-12-04T09:46:41Z", + "check if 2020-12-04T09:46:41Z >= 2019-12-04T09:46:41Z", + "check if 2020-12-04T09:46:41Z >= 2020-12-04T09:46:41Z", + "check if 2020-12-04T09:46:41Z >= 2020-12-04T09:46:41Z", + "check if 3 === 3", + "check if [\"abc\", \"def\"].contains(\"abc\")", + "check if [1, 2, 3].intersection([1, 2]).contains(1)", + "check if [1, 2, 3].intersection([1, 2]).length() === 2", + "check if [1, 2] === [1, 2]", + "check if [1, 2].contains(2)", + "check if [1, 2].contains([2])", + "check if [1, 2].intersection([2, 3]) === [2]", + "check if [1, 2].union([2, 3]) === [1, 2, 3]", + "check if [2019-12-04T09:46:41Z, 2020-12-04T09:46:41Z].contains(2020-12-04T09:46:41Z)", + "check if [false, true].contains(true)", + "check if [hex:12ab, hex:34de].contains(hex:34de)", + "check if false === false", + "check if false || true", + "check if hex:12ab === hex:12ab", + "check if true", + "check if true === true", + ], + }, +] + policies: [ + "allow if true", +] +} +``` + +result: `Ok(0)` + + +------------------------------ + +## invalid block rule with unbound_variables: test018_unbound_variables_in_rule.bc +### token + +authority: +symbols: [] + +public keys: [] + +``` +check if operation("read"); +``` + +1: +symbols: ["unbound", "any1", "any2"] + +public keys: [] + +``` +operation($unbound, "read") <- operation($any1, $any2); +``` + +### validation + +result: `Err(FailedLogic(InvalidBlockRule(0, "operation($unbound, \"read\") <- operation($any1, $any2)")))` + + +------------------------------ + +## invalid block rule generating an #authority or #ambient symbol with a variable: test019_generating_ambient_from_variables.bc +### token + +authority: +symbols: [] + +public keys: [] + +``` +check if operation("read"); +``` + +1: +symbols: ["any"] + +public keys: [] + +``` +operation("read") <- operation($any); +``` + +### validation + +authorizer code: +``` +operation("write"); + +allow if true; +``` + +revocation ids: +- `a44210c6a01e55eadefc7d8540c2e6eff80ab6eeedde4751de734f9d780435780680d3f42d826b7e0f0dcf4a5ba303fd4c116984bb30978813d46ed867924307` +- `d3f8822a9b9bc0ee3933283c493ca9e711be5dd8339b5fe2eba1de3805aad4e84d3e2fb4affb4a743f1289915c167582b9425343635e45b70573ea1ee7a1ea03` + +authorizer world: +``` +World { + facts: [ + Facts { + origin: { + None, + }, + facts: [ + "operation(\"write\")", + ], + }, + Facts { + origin: { + None, + Some( + 1, + ), + }, + facts: [ + "operation(\"read\")", + ], + }, +] + rules: [ + Rules { + origin: Some( + 1, + ), + rules: [ + "operation(\"read\") <- operation($any)", + ], + }, +] + checks: [ + Checks { + origin: Some( + 0, + ), + checks: [ + "check if operation(\"read\")", + ], + }, +] + policies: [ + "allow if true", +] +} +``` + +result: `Err(FailedLogic(Unauthorized { policy: Allow(0), checks: [Block(FailedBlockCheck { block_id: 0, check_id: 0, rule: "check if operation(\"read\")" })] }))` + + +------------------------------ + +## sealed token: test020_sealed.bc +### token + +authority: +symbols: ["file1", "file2"] + +public keys: [] + +``` +right("file1", "read"); +right("file2", "read"); +right("file1", "write"); +``` + +1: +symbols: ["0"] + +public keys: [] + +``` +check if resource($0), operation("read"), right($0, "read"); +``` + +### validation + +authorizer code: +``` +resource("file1"); +operation("read"); + +allow if true; +``` + +revocation ids: +- `7595a112a1eb5b81a6e398852e6118b7f5b8cbbff452778e655100e5fb4faa8d3a2af52fe2c4f9524879605675fae26adbc4783e0cafc43522fa82385f396c03` +- `45f4c14f9d9e8fa044d68be7a2ec8cddb835f575c7b913ec59bd636c70acae9a90db9064ba0b3084290ed0c422bbb7170092a884f5e0202b31e9235bbcc1650d` + +authorizer world: +``` +World { + facts: [ + Facts { + origin: { + None, + }, + facts: [ + "operation(\"read\")", + "resource(\"file1\")", + ], + }, + Facts { + origin: { + Some( + 0, + ), + }, + facts: [ + "right(\"file1\", \"read\")", + "right(\"file1\", \"write\")", + "right(\"file2\", \"read\")", + ], + }, +] + rules: [] + checks: [ + Checks { + origin: Some( + 1, + ), + checks: [ + "check if resource($0), operation(\"read\"), right($0, \"read\")", + ], + }, +] + policies: [ + "allow if true", +] +} +``` + +result: `Ok(0)` + + +------------------------------ + +## parsing: test021_parsing.bc +### token + +authority: +symbols: ["ns::fact_123", "hello é\t😁"] + +public keys: [] + +``` +ns::fact_123("hello é 😁"); +``` + +### validation + +authorizer code: +``` +check if ns::fact_123("hello é 😁"); + +allow if true; +``` + +revocation ids: +- `d4b2f417b6e906434fdf5058afcabfcb98d3628f814f1c9dd7e64250d9beec4465aff51bd0cb2e85d0e67dc9f613c2a42af6158c678bc6f8b4684cd3a2d0d302` + +authorizer world: +``` +World { + facts: [ + Facts { + origin: { + Some( + 0, + ), + }, + facts: [ + "ns::fact_123(\"hello é\t😁\")", + ], + }, +] + rules: [] + checks: [ + Checks { + origin: Some( + 18446744073709551615, + ), + checks: [ + "check if ns::fact_123(\"hello é\t😁\")", + ], + }, +] + policies: [ + "allow if true", +] +} +``` + +result: `Ok(0)` + + +------------------------------ + +## default_symbols: test022_default_symbols.bc +### token + +authority: +symbols: [] + +public keys: [] + +``` +read(0); +write(1); +resource(2); +operation(3); +right(4); +time(5); +role(6); +owner(7); +tenant(8); +namespace(9); +user(10); +team(11); +service(12); +admin(13); +email(14); +group(15); +member(16); +ip_address(17); +client(18); +client_ip(19); +domain(20); +path(21); +version(22); +cluster(23); +node(24); +hostname(25); +nonce(26); +query(27); +``` + +### validation + +authorizer code: +``` +check if read(0), write(1), resource(2), operation(3), right(4), time(5), role(6), owner(7), tenant(8), namespace(9), user(10), team(11), service(12), admin(13), email(14), group(15), member(16), ip_address(17), client(18), client_ip(19), domain(20), path(21), version(22), cluster(23), node(24), hostname(25), nonce(26), query(27); + +allow if true; +``` + +revocation ids: +- `75ce48d496fd28f99905901783a1ba46d7ff8d69f9d364d1546fd73006026eae51849ad1190a4ae521a0a1269f9c6951e226afba8fcd24fa50f679162439ae09` + +authorizer world: +``` +World { + facts: [ + Facts { + origin: { + Some( + 0, + ), + }, + facts: [ + "admin(13)", + "client(18)", + "client_ip(19)", + "cluster(23)", + "domain(20)", + "email(14)", + "group(15)", + "hostname(25)", + "ip_address(17)", + "member(16)", + "namespace(9)", + "node(24)", + "nonce(26)", + "operation(3)", + "owner(7)", + "path(21)", + "query(27)", + "read(0)", + "resource(2)", + "right(4)", + "role(6)", + "service(12)", + "team(11)", + "tenant(8)", + "time(5)", + "user(10)", + "version(22)", + "write(1)", + ], + }, +] + rules: [] + checks: [ + Checks { + origin: Some( + 18446744073709551615, + ), + checks: [ + "check if read(0), write(1), resource(2), operation(3), right(4), time(5), role(6), owner(7), tenant(8), namespace(9), user(10), team(11), service(12), admin(13), email(14), group(15), member(16), ip_address(17), client(18), client_ip(19), domain(20), path(21), version(22), cluster(23), node(24), hostname(25), nonce(26), query(27)", + ], + }, +] + policies: [ + "allow if true", +] +} +``` + +result: `Ok(0)` + + +------------------------------ + +## execution scope: test023_execution_scope.bc +### token + +authority: +symbols: ["authority_fact"] + +public keys: [] + +``` +authority_fact(1); +``` + +1: +symbols: ["block1_fact"] + +public keys: [] + +``` +block1_fact(1); +``` + +2: +symbols: ["var"] + +public keys: [] + +``` +check if authority_fact($var); +check if block1_fact($var); +``` + +### validation + +authorizer code: +``` +allow if true; +``` + +revocation ids: +- `f9b49866caef5ece7be14ec5a9b36d98ca81d06b306eb0b4c57cd7436af176f40ee972f40903f87ec4460ab8b1adfcbfa9b19b20a6955a1e8dae7d88b2076005` +- `889054b9119e4440e54da1b63266a98d0f6646cde195fef206efd8b133cfb2ee7be49b32a9a5925ece452e64f9e6f6d80dab422e916c599675dd68cdea053802` +- `0a85ffbf27e08aa23665ba0d96a985b274d747556c9f016fd7f590c641ed0e4133291521aa442b320ee9ce80f5ad701b914a0c87b3dfa0cc92629dce94201806` + +authorizer world: +``` +World { + facts: [ + Facts { + origin: { + Some( + 0, + ), + }, + facts: [ + "authority_fact(1)", + ], + }, + Facts { + origin: { + Some( + 1, + ), + }, + facts: [ + "block1_fact(1)", + ], + }, +] + rules: [] + checks: [ + Checks { + origin: Some( + 2, + ), + checks: [ + "check if authority_fact($var)", + "check if block1_fact($var)", + ], + }, +] + policies: [ + "allow if true", +] +} +``` + +result: `Err(FailedLogic(Unauthorized { policy: Allow(0), checks: [Block(FailedBlockCheck { block_id: 2, check_id: 1, rule: "check if block1_fact($var)" })] }))` + + +------------------------------ + +## third party: test024_third_party.bc +### token + +authority: +symbols: [] + +public keys: ["ed25519/acdd6d5b53bfee478bf689f8e012fe7988bf755e3d7c5152947abc149bc20189"] + +``` +right("read"); +check if group("admin") trusting ed25519/acdd6d5b53bfee478bf689f8e012fe7988bf755e3d7c5152947abc149bc20189; +``` + +1: +symbols: [] + +public keys: [] + +external signature by: "ed25519/acdd6d5b53bfee478bf689f8e012fe7988bf755e3d7c5152947abc149bc20189" + +``` +group("admin"); +check if right("read"); +``` + +### validation + +authorizer code: +``` +allow if true; +``` + +revocation ids: +- `470e4bf7aa2a01ab39c98150bd06aa15b4aa5d86509044a8809a8634cd8cf2b42269a51a774b65d10bac9369d013070b00187925196a8e680108473f11cf8f03` +- `93a7315ab1272da9eeef015f6fecbc9ac96fe4660e6204bf64ea2105ebe309e9c9cadc0a26c5604f13910fae3f2cd0800756afb6b6b208bf77adeb1ab2f42405` + +authorizer world: +``` +World { + facts: [ + Facts { + origin: { + Some( + 0, + ), + }, + facts: [ + "right(\"read\")", + ], + }, + Facts { + origin: { + Some( + 1, + ), + }, + facts: [ + "group(\"admin\")", + ], + }, +] + rules: [] + checks: [ + Checks { + origin: Some( + 0, + ), + checks: [ + "check if group(\"admin\") trusting ed25519/acdd6d5b53bfee478bf689f8e012fe7988bf755e3d7c5152947abc149bc20189", + ], + }, + Checks { + origin: Some( + 1, + ), + checks: [ + "check if right(\"read\")", + ], + }, +] + policies: [ + "allow if true", +] +} +``` + +result: `Ok(0)` + + +------------------------------ + +## block rules: test025_check_all.bc +### token + +authority: +symbols: ["allowed_operations", "A", "B", "op", "allowed"] + +public keys: [] + +``` +allowed_operations(["A", "B"]); +check all operation($op), allowed_operations($allowed), $allowed.contains($op); +``` + +### validation for "A, B" + +authorizer code: +``` +operation("A"); +operation("B"); + +allow if true; +``` + +revocation ids: +- `c456817012e1d523c6d145b6d6a3475d9f7dd4383c535454ff3f745ecf4234984ce09b9dec0551f3d783abe850f826ce43b12f1fd91999a4753a56ecf4c56d0d` + +authorizer world: +``` +World { + facts: [ + Facts { + origin: { + None, + }, + facts: [ + "operation(\"A\")", + "operation(\"B\")", + ], + }, + Facts { + origin: { + Some( + 0, + ), + }, + facts: [ + "allowed_operations([\"A\", \"B\"])", + ], + }, +] + rules: [] + checks: [ + Checks { + origin: Some( + 0, + ), + checks: [ + "check all operation($op), allowed_operations($allowed), $allowed.contains($op)", + ], + }, +] + policies: [ + "allow if true", +] +} +``` + +result: `Ok(0)` +### validation for "A, invalid" + +authorizer code: +``` +operation("A"); +operation("invalid"); + +allow if true; +``` + +revocation ids: +- `c456817012e1d523c6d145b6d6a3475d9f7dd4383c535454ff3f745ecf4234984ce09b9dec0551f3d783abe850f826ce43b12f1fd91999a4753a56ecf4c56d0d` + +authorizer world: +``` +World { + facts: [ + Facts { + origin: { + None, + }, + facts: [ + "operation(\"A\")", + "operation(\"invalid\")", + ], + }, + Facts { + origin: { + Some( + 0, + ), + }, + facts: [ + "allowed_operations([\"A\", \"B\"])", + ], + }, +] + rules: [] + checks: [ + Checks { + origin: Some( + 0, + ), + checks: [ + "check all operation($op), allowed_operations($allowed), $allowed.contains($op)", + ], + }, +] + policies: [ + "allow if true", +] +} +``` + +result: `Err(FailedLogic(Unauthorized { policy: Allow(0), checks: [Block(FailedBlockCheck { block_id: 0, check_id: 0, rule: "check all operation($op), allowed_operations($allowed), $allowed.contains($op)" })] }))` + + +------------------------------ + +## public keys interning: test026_public_keys_interning.bc +### token + +authority: +symbols: [] + +public keys: ["ed25519/acdd6d5b53bfee478bf689f8e012fe7988bf755e3d7c5152947abc149bc20189"] + +``` +query(0); +check if true trusting previous, ed25519/acdd6d5b53bfee478bf689f8e012fe7988bf755e3d7c5152947abc149bc20189; +``` + +1: +symbols: [] + +public keys: ["ed25519/a060270db7e9c9f06e8f9cc33a64e99f6596af12cb01c4b638df8afc7b642463"] + +external signature by: "ed25519/acdd6d5b53bfee478bf689f8e012fe7988bf755e3d7c5152947abc149bc20189" + +``` +query(1); +query(1, 2) <- query(1), query(2) trusting ed25519/a060270db7e9c9f06e8f9cc33a64e99f6596af12cb01c4b638df8afc7b642463; +check if query(2), query(3) trusting ed25519/a060270db7e9c9f06e8f9cc33a64e99f6596af12cb01c4b638df8afc7b642463; +check if query(1) trusting ed25519/acdd6d5b53bfee478bf689f8e012fe7988bf755e3d7c5152947abc149bc20189; +``` + +2: +symbols: [] + +public keys: [] + +external signature by: "ed25519/a060270db7e9c9f06e8f9cc33a64e99f6596af12cb01c4b638df8afc7b642463" + +``` +query(2); +check if query(2), query(3) trusting ed25519/a060270db7e9c9f06e8f9cc33a64e99f6596af12cb01c4b638df8afc7b642463; +check if query(1) trusting ed25519/acdd6d5b53bfee478bf689f8e012fe7988bf755e3d7c5152947abc149bc20189; +``` + +3: +symbols: [] + +public keys: [] + +external signature by: "ed25519/a060270db7e9c9f06e8f9cc33a64e99f6596af12cb01c4b638df8afc7b642463" + +``` +query(3); +check if query(2), query(3) trusting ed25519/a060270db7e9c9f06e8f9cc33a64e99f6596af12cb01c4b638df8afc7b642463; +check if query(1) trusting ed25519/acdd6d5b53bfee478bf689f8e012fe7988bf755e3d7c5152947abc149bc20189; +``` + +4: +symbols: [] + +public keys: ["ed25519/f98da8c1cf907856431bfc3dc87531e0eaadba90f919edc232405b85877ef136"] + +``` +query(4); +check if query(2) trusting ed25519/a060270db7e9c9f06e8f9cc33a64e99f6596af12cb01c4b638df8afc7b642463; +check if query(4) trusting ed25519/f98da8c1cf907856431bfc3dc87531e0eaadba90f919edc232405b85877ef136; +``` + +### validation + +authorizer code: +``` +check if query(1, 2) trusting ed25519/acdd6d5b53bfee478bf689f8e012fe7988bf755e3d7c5152947abc149bc20189, ed25519/a060270db7e9c9f06e8f9cc33a64e99f6596af12cb01c4b638df8afc7b642463; + +deny if query(3); +deny if query(1, 2); +deny if query(0) trusting ed25519/acdd6d5b53bfee478bf689f8e012fe7988bf755e3d7c5152947abc149bc20189; +allow if true; +``` + +revocation ids: +- `3771cefe71beb21ead35a59c8116ee82627a5717c0295f35980662abccb159fe1b37848cb1818e548656bd4fd882d0094a2daab631c76b2b72e3a093914bfe04` +- `45133b90f228a81fe4d3042a79f6c6b7608e656e903d6b1f4db32cd774b09b8315af360879a5f210ad7be37ff55e3eb34f237bcc9711407b6329ac6018bfb400` +- `179f054f3c572646aba5013159ae192ac42f5666dbdd984129955f4652b6829e59f54aa251e451f96329d42a2524ce569c3e1ec52e708b642dd8994af51dd703` +- `edab54789d6656936fcd28200b9c61643434842d531f09f209fad555e11ff53174db174dafba126e6de448983a56f78d2042bc5782d71a45799c022fe69fb30d` +- `6a62306831e9dbe83e7b33db96b758c77dd690930f2d2d87e239b210b1944c5582bf6d7e1bfea8e7f928c27f2fff0e2ee2e0adc41e11e0c3abe8d7b96b9ede07` + +authorizer world: +``` +World { + facts: [ + Facts { + origin: { + Some( + 0, + ), + }, + facts: [ + "query(0)", + ], + }, + Facts { + origin: { + Some( + 1, + ), + }, + facts: [ + "query(1)", + ], + }, + Facts { + origin: { + Some( + 1, + ), + Some( + 2, + ), + }, + facts: [ + "query(1, 2)", + ], + }, + Facts { + origin: { + Some( + 2, + ), + }, + facts: [ + "query(2)", + ], + }, + Facts { + origin: { + Some( + 3, + ), + }, + facts: [ + "query(3)", + ], + }, + Facts { + origin: { + Some( + 4, + ), + }, + facts: [ + "query(4)", + ], + }, +] + rules: [ + Rules { + origin: Some( + 1, + ), + rules: [ + "query(1, 2) <- query(1), query(2) trusting ed25519/a060270db7e9c9f06e8f9cc33a64e99f6596af12cb01c4b638df8afc7b642463", + ], + }, +] + checks: [ + Checks { + origin: Some( + 0, + ), + checks: [ + "check if true trusting previous, ed25519/acdd6d5b53bfee478bf689f8e012fe7988bf755e3d7c5152947abc149bc20189", + ], + }, + Checks { + origin: Some( + 1, + ), + checks: [ + "check if query(1) trusting ed25519/acdd6d5b53bfee478bf689f8e012fe7988bf755e3d7c5152947abc149bc20189", + "check if query(2), query(3) trusting ed25519/a060270db7e9c9f06e8f9cc33a64e99f6596af12cb01c4b638df8afc7b642463", + ], + }, + Checks { + origin: Some( + 2, + ), + checks: [ + "check if query(1) trusting ed25519/acdd6d5b53bfee478bf689f8e012fe7988bf755e3d7c5152947abc149bc20189", + "check if query(2), query(3) trusting ed25519/a060270db7e9c9f06e8f9cc33a64e99f6596af12cb01c4b638df8afc7b642463", + ], + }, + Checks { + origin: Some( + 3, + ), + checks: [ + "check if query(1) trusting ed25519/acdd6d5b53bfee478bf689f8e012fe7988bf755e3d7c5152947abc149bc20189", + "check if query(2), query(3) trusting ed25519/a060270db7e9c9f06e8f9cc33a64e99f6596af12cb01c4b638df8afc7b642463", + ], + }, + Checks { + origin: Some( + 4, + ), + checks: [ + "check if query(2) trusting ed25519/a060270db7e9c9f06e8f9cc33a64e99f6596af12cb01c4b638df8afc7b642463", + "check if query(4) trusting ed25519/f98da8c1cf907856431bfc3dc87531e0eaadba90f919edc232405b85877ef136", + ], + }, + Checks { + origin: Some( + 18446744073709551615, + ), + checks: [ + "check if query(1, 2) trusting ed25519/acdd6d5b53bfee478bf689f8e012fe7988bf755e3d7c5152947abc149bc20189, ed25519/a060270db7e9c9f06e8f9cc33a64e99f6596af12cb01c4b638df8afc7b642463", + ], + }, +] + policies: [ + "deny if query(3)", + "deny if query(1, 2)", + "deny if query(0) trusting ed25519/acdd6d5b53bfee478bf689f8e012fe7988bf755e3d7c5152947abc149bc20189", + "allow if true", +] +} +``` + +result: `Ok(3)` + + +------------------------------ + +## integer wraparound: test027_integer_wraparound.bc +### token + +authority: +symbols: [] + +public keys: [] + +``` +check if true || 10000000000 * 10000000000 != 0; +check if true || 9223372036854775807 + 1 != 0; +check if true || -9223372036854775808 - 1 != 0; +``` + +### validation + +authorizer code: +``` +allow if true; +``` + +revocation ids: +- `a57be539aae237040fe6c2c28c4263516147c9f0d1d7ba88a385f1574f504c544164a2c747efd8b30eaab9d351c383cc1875642f173546d5f4b53b2220c87a0a` + +authorizer world: +``` +World { + facts: [] + rules: [] + checks: [ + Checks { + origin: Some( + 0, + ), + checks: [ + "check if true || -9223372036854775808 - 1 != 0", + "check if true || 10000000000 * 10000000000 != 0", + "check if true || 9223372036854775807 + 1 != 0", + ], + }, +] + policies: [ + "allow if true", +] +} +``` + +result: `Err(Execution(Overflow))` + + +------------------------------ + +## test expression syntax and all available operations (v4 blocks): test028_expressions_v4.bc +### token + +authority: +symbols: ["abcD12x", "abcD12"] + +public keys: [] + +``` +check if 1 !== 3; +check if 1 | 2 ^ 3 === 0; +check if "abcD12x" !== "abcD12"; +check if 2022-12-04T09:46:41Z !== 2020-12-04T09:46:41Z; +check if hex:12abcd !== hex:12ab; +check if [1, 4] !== [1, 2]; +``` + +### validation + +authorizer code: +``` +allow if true; +``` + +revocation ids: +- `117fa653744c859561555e6a6f5990e3a8e7817f91b87aa6991b6d64297158b4e884c92d10f49f74c96069df722aa676839b72751ca9d1fe83a7025b591de00b` + +authorizer world: +``` +World { + facts: [] + rules: [] + checks: [ + Checks { + origin: Some( + 0, + ), + checks: [ + "check if \"abcD12x\" !== \"abcD12\"", + "check if 1 !== 3", + "check if 1 | 2 ^ 3 === 0", + "check if 2022-12-04T09:46:41Z !== 2020-12-04T09:46:41Z", + "check if [1, 4] !== [1, 2]", + "check if hex:12abcd !== hex:12ab", + ], + }, +] + policies: [ + "allow if true", +] +} +``` + +result: `Ok(0)` + + +------------------------------ + +## test reject if: test029_reject_if.bc +### token + +authority: +symbols: ["test"] + +public keys: [] + +``` +reject if test($test), $test; +``` + +### validation + +authorizer code: +``` +test(false); + +allow if true; +``` + +revocation ids: +- `2060031eb9968b492123440fa9cbc781f18be812961e765a34a8702d3eee0ed54910710efbb41b3141f60748a815012fe0e703a5b5604f4262d1ac7e79766b07` + +authorizer world: +``` +World { + facts: [ + Facts { + origin: { + None, + }, + facts: [ + "test(false)", + ], + }, +] + rules: [] + checks: [ + Checks { + origin: Some( + 0, + ), + checks: [ + "reject if test($test), $test", + ], + }, +] + policies: [ + "allow if true", +] +} +``` + +result: `Ok(0)` +### validation for "rejection" + +authorizer code: +``` +test(true); + +allow if true; +``` + +revocation ids: +- `2060031eb9968b492123440fa9cbc781f18be812961e765a34a8702d3eee0ed54910710efbb41b3141f60748a815012fe0e703a5b5604f4262d1ac7e79766b07` + +authorizer world: +``` +World { + facts: [ + Facts { + origin: { + None, + }, + facts: [ + "test(true)", + ], + }, +] + rules: [] + checks: [ + Checks { + origin: Some( + 0, + ), + checks: [ + "reject if test($test), $test", + ], + }, +] + policies: [ + "allow if true", +] +} +``` + +result: `Err(FailedLogic(Unauthorized { policy: Allow(0), checks: [Block(FailedBlockCheck { block_id: 0, check_id: 0, rule: "reject if test($test), $test" })] }))` + + +------------------------------ + +## test null: test030_null.bc +### token + +authority: +symbols: ["fact", "value"] + +public keys: [] + +``` +check if fact(null, $value), $value == null; +reject if fact(null, $value), $value != null; +``` + +### validation + +authorizer code: +``` +fact(null, null); + +allow if true; +``` + +revocation ids: +- `35d99762ee4343b245d66b719f7ad6180c76dd899c39e4072cf61dcf8673e7510374922457ce260b8c576431e894e38c7c0bacd3e5cae2bfc63e3286d2078d02` + +authorizer world: +``` +World { + facts: [ + Facts { + origin: { + None, + }, + facts: [ + "fact(null, null)", + ], + }, +] + rules: [] + checks: [ + Checks { + origin: Some( + 0, + ), + checks: [ + "check if fact(null, $value), $value == null", + "reject if fact(null, $value), $value != null", + ], + }, +] + policies: [ + "allow if true", +] +} +``` + +result: `Ok(0)` +### validation for "rejection1" + +authorizer code: +``` +fact(null, 1); + +allow if true; +``` + +revocation ids: +- `35d99762ee4343b245d66b719f7ad6180c76dd899c39e4072cf61dcf8673e7510374922457ce260b8c576431e894e38c7c0bacd3e5cae2bfc63e3286d2078d02` + +authorizer world: +``` +World { + facts: [ + Facts { + origin: { + None, + }, + facts: [ + "fact(null, 1)", + ], + }, +] + rules: [] + checks: [ + Checks { + origin: Some( + 0, + ), + checks: [ + "check if fact(null, $value), $value == null", + "reject if fact(null, $value), $value != null", + ], + }, +] + policies: [ + "allow if true", +] +} +``` + +result: `Err(FailedLogic(Unauthorized { policy: Allow(0), checks: [Block(FailedBlockCheck { block_id: 0, check_id: 0, rule: "check if fact(null, $value), $value == null" }), Block(FailedBlockCheck { block_id: 0, check_id: 1, rule: "reject if fact(null, $value), $value != null" })] }))` +### validation for "rejection2" + +authorizer code: +``` +fact(null, true); + +allow if true; +``` + +revocation ids: +- `35d99762ee4343b245d66b719f7ad6180c76dd899c39e4072cf61dcf8673e7510374922457ce260b8c576431e894e38c7c0bacd3e5cae2bfc63e3286d2078d02` + +authorizer world: +``` +World { + facts: [ + Facts { + origin: { + None, + }, + facts: [ + "fact(null, true)", + ], + }, +] + rules: [] + checks: [ + Checks { + origin: Some( + 0, + ), + checks: [ + "check if fact(null, $value), $value == null", + "reject if fact(null, $value), $value != null", + ], + }, +] + policies: [ + "allow if true", +] +} +``` + +result: `Err(FailedLogic(Unauthorized { policy: Allow(0), checks: [Block(FailedBlockCheck { block_id: 0, check_id: 0, rule: "check if fact(null, $value), $value == null" }), Block(FailedBlockCheck { block_id: 0, check_id: 1, rule: "reject if fact(null, $value), $value != null" })] }))` +### validation for "rejection3" + +authorizer code: +``` +fact(null, "abcd"); + +allow if true; +``` + +revocation ids: +- `35d99762ee4343b245d66b719f7ad6180c76dd899c39e4072cf61dcf8673e7510374922457ce260b8c576431e894e38c7c0bacd3e5cae2bfc63e3286d2078d02` + +authorizer world: +``` +World { + facts: [ + Facts { + origin: { + None, + }, + facts: [ + "fact(null, \"abcd\")", + ], + }, +] + rules: [] + checks: [ + Checks { + origin: Some( + 0, + ), + checks: [ + "check if fact(null, $value), $value == null", + "reject if fact(null, $value), $value != null", + ], + }, +] + policies: [ + "allow if true", +] +} +``` + +result: `Err(FailedLogic(Unauthorized { policy: Allow(0), checks: [Block(FailedBlockCheck { block_id: 0, check_id: 0, rule: "check if fact(null, $value), $value == null" }), Block(FailedBlockCheck { block_id: 0, check_id: 1, rule: "reject if fact(null, $value), $value != null" })] }))` + + +------------------------------ + +## test heterogeneous equal: test031_heterogeneous_equal.bc +### token + +authority: +symbols: ["fact", "value", "fact2"] + +public keys: [] + +``` +check if fact(1, $value), 1 == $value; +check if fact2(1, $value), 1 != $value; +``` + +### validation for "authorized same type" + +authorizer code: +``` +fact(1, 1); +fact2(1, 2); + +allow if true; +``` + +revocation ids: +- `4af31ee86f7afbf20c7fe7e664943b2b3895693b85b3c580f3accc3bbdf8d4b1909f84787e1189c0711fd850a7cc330021e8bbe127ae3a4b36624ff7e487170e` + +authorizer world: +``` +World { + facts: [ + Facts { + origin: { + None, + }, + facts: [ + "fact(1, 1)", + "fact2(1, 2)", + ], + }, +] + rules: [] + checks: [ + Checks { + origin: Some( + 0, + ), + checks: [ + "check if fact(1, $value), 1 == $value", + "check if fact2(1, $value), 1 != $value", + ], + }, +] + policies: [ + "allow if true", +] +} +``` + +result: `Ok(0)` +### validation for "unauthorized failed logic different type" + +authorizer code: +``` +fact(1, true); +fact2(1, false); + +allow if true; +``` + +revocation ids: +- `4af31ee86f7afbf20c7fe7e664943b2b3895693b85b3c580f3accc3bbdf8d4b1909f84787e1189c0711fd850a7cc330021e8bbe127ae3a4b36624ff7e487170e` + +authorizer world: +``` +World { + facts: [ + Facts { + origin: { + None, + }, + facts: [ + "fact(1, true)", + "fact2(1, false)", + ], + }, +] + rules: [] + checks: [ + Checks { + origin: Some( + 0, + ), + checks: [ + "check if fact(1, $value), 1 == $value", + "check if fact2(1, $value), 1 != $value", + ], + }, +] + policies: [ + "allow if true", +] +} +``` + +result: `Err(FailedLogic(Unauthorized { policy: Allow(0), checks: [Block(FailedBlockCheck { block_id: 0, check_id: 0, rule: "check if fact(1, $value), 1 == $value" })] }))` + + +------------------------------ + +## ECDSA secp256r1 signatures: test032_secp256r1.bc +### token + +authority: +symbols: ["file1", "file2"] + +public keys: [] + +``` +right("file1", "read"); +right("file2", "read"); +right("file1", "write"); +``` + +1: +symbols: ["0"] + +public keys: [] + +``` +check if resource($0), operation("read"), right($0, "read"); +``` + +### validation + +authorizer code: +``` +resource("file1"); +operation("read"); + +allow if true; +``` + +revocation ids: +- `760785de30d7348e9c847aab8b3bdad6a0d463f4f50ed9667aade563e9112ee6d2f589630dd7553c2eced2a57edf3636d5c874b35df15120c62fddcbdbd2de09` +- `30440220039667c7a4d964e4b449289dc8fd206d7aa0e77eb701a9253b3307d32c177fa8022023f7523c143c5fb55ee4cafe49804702ef05a70883ebf42185b54bd36a7e7cd4` + +authorizer world: +``` +World { + facts: [ + Facts { + origin: { + None, + }, + facts: [ + "operation(\"read\")", + "resource(\"file1\")", + ], + }, + Facts { + origin: { + Some( + 0, + ), + }, + facts: [ + "right(\"file1\", \"read\")", + "right(\"file1\", \"write\")", + "right(\"file2\", \"read\")", + ], + }, +] + rules: [] + checks: [ + Checks { + origin: Some( + 1, + ), + checks: [ + "check if resource($0), operation(\"read\"), right($0, \"read\")", + ], + }, +] + policies: [ + "allow if true", +] +} +``` + +result: `Ok(0)` + diff --git a/biscuit-auth/samples/samples.json b/biscuit-auth/samples/samples.json index 1a8b4bd0..a5676d3f 100644 --- a/biscuit-auth/samples/samples.json +++ b/biscuit-auth/samples/samples.json @@ -2609,7 +2609,7 @@ }, { "title": "ECDSA secp256r1 signatures", - "filename": "test031_secp256r1.bc", + "filename": "test032_secp256r1.bc", "token": [ { "symbols": [ diff --git a/biscuit-auth/samples/test032_secp256r1.bc b/biscuit-auth/samples/test032_secp256r1.bc new file mode 100644 index 0000000000000000000000000000000000000000..15a8a429e52823e318d362c4264507f2a92fc49d GIT binary patch literal 366 zcmWey!N_IH#hR9xlWGW|j3k(qc)7SaScI4*8aRZQBpBe_MpQ0XmQhHRgHcG4DQ;r_ z6*mi`b&Hy8|4tWMob5YXxO{K$-KM)=4(us?o4sGkp^Uxtp22mKzBw&btGlglU0ZM^ z`O8RpC-Q))O&X6YiBa=^-vqVbC;IZ-8VD4dZJ`=?8iWbWBPYb-@bH@Q)&() zmo^upfdsRW3YQWW6Nj`AI|q{xGe;AL5E}Joi6Mq$Qs}?-3+s?RB z)!LZ-vW|HD3MK{R??E;qHt}2Io}BvU+2GFfo^?4#^Xo5)ty{e>XVukQQBvYkIOTY2 jL+gaMOPmb1{rR$6kf&8!<%7ZgnOU75bj}>D+xigzPX&iC literal 0 HcmV?d00001 diff --git a/biscuit-auth/src/crypto/mod.rs b/biscuit-auth/src/crypto/mod.rs index 1b893117..558cc894 100644 --- a/biscuit-auth/src/crypto/mod.rs +++ b/biscuit-auth/src/crypto/mod.rs @@ -129,25 +129,15 @@ impl PrivateKey { } /// deserializes from a byte array - pub fn from_bytes( - bytes: &[u8], - algorithm: schema::public_key::Algorithm, - ) -> Result { + pub fn from_bytes(bytes: &[u8], algorithm: Algorithm) -> Result { match algorithm { - schema::public_key::Algorithm::Ed25519 => { - Ok(PrivateKey::Ed25519(ed25519::PrivateKey::from_bytes(bytes)?)) - } - schema::public_key::Algorithm::Secp256r1 => { - Ok(PrivateKey::P256(p256::PrivateKey::from_bytes(bytes)?)) - } + Algorithm::Ed25519 => Ok(PrivateKey::Ed25519(ed25519::PrivateKey::from_bytes(bytes)?)), + Algorithm::Secp256r1 => Ok(PrivateKey::P256(p256::PrivateKey::from_bytes(bytes)?)), } } /// deserializes from an hex-encoded string - pub fn from_bytes_hex( - str: &str, - algorithm: schema::public_key::Algorithm, - ) -> Result { + pub fn from_bytes_hex(str: &str, algorithm: Algorithm) -> Result { let bytes = hex::decode(str).map_err(|e| error::Format::InvalidKey(e.to_string()))?; Self::from_bytes(&bytes, algorithm) } diff --git a/biscuit-auth/src/format/mod.rs b/biscuit-auth/src/format/mod.rs index 3f64b21c..e6c70835 100644 --- a/biscuit-auth/src/format/mod.rs +++ b/biscuit-auth/src/format/mod.rs @@ -107,6 +107,12 @@ impl SerializedBiscuit { )) } Some(schema::proof::Content::NextSecret(v)) => { + let next_key_algorithm = match next_key_algorithm { + schema::public_key::Algorithm::Ed25519 => crate::builder::Algorithm::Ed25519, + schema::public_key::Algorithm::Secp256r1 => { + crate::builder::Algorithm::Secp256r1 + } + }; TokenNext::Secret(PrivateKey::from_bytes(&v, next_key_algorithm)?) } Some(schema::proof::Content::FinalSignature(v)) => { From 346d4de030368bedfad37d0597e64f1cb7130412 Mon Sep 17 00:00:00 2001 From: Geoffroy Couprie Date: Sat, 15 Jun 2024 15:58:42 +0200 Subject: [PATCH 22/22] make the algorithm Copy --- biscuit-auth/src/token/builder.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/biscuit-auth/src/token/builder.rs b/biscuit-auth/src/token/builder.rs index c6459040..abf18a8a 100644 --- a/biscuit-auth/src/token/builder.rs +++ b/biscuit-auth/src/token/builder.rs @@ -631,7 +631,7 @@ impl From for Scope { } } -#[derive(Debug, Clone, PartialEq, Hash, Eq)] +#[derive(Debug, Copy, Clone, PartialEq, Hash, Eq)] pub enum Algorithm { Ed25519, Secp256r1,