Skip to content

Commit

Permalink
Data Integrity bbs-2023 Cryptosuite (#550)
Browse files Browse the repository at this point in the history
* Add bbs2023 suite.
* Factorize tests, ensure JSON canonicalization.
* Upgrade `uuid`to 1.9
* Lazy `Multikey` decoding.
* Unification of the signature options and verification options interface.
* Add `SelectiveCryptographicSuite` trait, providing a nice interface to SD suites.
* Add `DataIntegrity::select` method.
* Add `Bbs2023` variant to `AnySuite`.
* Upgrade `json-ld` to version 0.21.1.
* Remove feature gates on `Multikey`.

---------

Co-authored-by: Simon Bihel <[email protected]>
  • Loading branch information
timothee-haudebourg and sbihel authored Jul 10, 2024
1 parent dbd3c5c commit 0e87bfe
Show file tree
Hide file tree
Showing 138 changed files with 6,842 additions and 1,308 deletions.
12 changes: 10 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ ssi-ucan = { path = "./crates/ucan", version = "0.2", default-features = false }
ssi-zcap-ld = { path = "./crates/zcap-ld", version = "0.2", default-features = false }
ssi-ssh = { path = "./crates/ssh", version = "0.2", default-features = false }
ssi-status = { path = "./crates/status", version = "0.1", default-features = false }
ssi-bbs = { path = "./crates/bbs", version = "0.1", default-features = false }

# Verifiable Claims
ssi-claims-core = { path = "./crates/claims/core", version = "0.1", default-features = false }
Expand All @@ -48,6 +49,7 @@ ssi-jwt = { path = "./crates/claims/crates/jwt", version = "0.2", default-featur
ssi-sd-jwt = { path = "./crates/claims/crates/sd-jwt", version = "0.2", default-features = false }
ssi-vc = { path = "./crates/claims/crates/vc", version = "0.3", default-features = false }
ssi-data-integrity-core = { path = "./crates/claims/crates/data-integrity/core", version = "0.1", default-features = false }
ssi-di-sd-primitives = { path = "./crates/claims/crates/data-integrity/sd-primitives", version = "0.1", default-features = false }
ssi-data-integrity-suites = { path = "./crates/claims/crates/data-integrity/suites", version = "0.1", default-features = false }
ssi-data-integrity = { path = "./crates/claims/crates/data-integrity", version = "0.1", default-features = false }
ssi-claims = { path = "./crates/claims", version = "0.1", default-features = false }
Expand All @@ -64,6 +66,7 @@ did-web = { path = "./crates/dids/methods/web", version = "0.3", default-feature
ssi-dids = { path = "./crates/dids", version = "0.2", default-features = false }

# crypto
digest = "0.10"
k256 = "0.13.1"
p256 = "0.13.2"
p384 = "0.13.0"
Expand All @@ -72,6 +75,7 @@ blake2 = "0.10"
sha2 = "0.10.0"
sha3 = "0.10.8"
rsa = "0.6"
zkryptium = "0.2.2" # BBS

# other common dependencies
log = "0.4.21"
Expand Down Expand Up @@ -100,8 +104,11 @@ futures = "0.3.28"
linked-data = "0.1.2"
rand = "0.8"
rand_chacha = "0.3.1"
getrandom = "0.2"
contextual = "0.1.6"
lazy_static = "1.4.0"
indexmap = "2.0.0"
uuid = "1.9"

[features]
default = ["w3c", "rsa", "ed25519", "secp256k1", "secp256r1", "ripemd-160", "eip712"]
Expand Down Expand Up @@ -192,7 +199,7 @@ ethereum = ["ssi-claims/ethereum", "ssi-jwk/eip", "ssi-dids/eip"]
ripemd-160 = ["ssi-jwk/ripemd-160", "ssi-dids/ripemd-160"]

## Enable bbs.
bbs = ["ssi-crypto/bbs"]
bbs = ["ssi-crypto/bbs", "ssi-claims/bbs", "ssi-bbs"]

## Use the Ring crate for crypto operations
ring = ["ssi-jwk/ring", "ssi-jws/ring", "ssi-crypto/ring"]
Expand Down Expand Up @@ -224,12 +231,13 @@ ssi-ucan.workspace = true
ssi-zcap-ld.workspace = true
ssi-multicodec.workspace = true
ssi-ssh.workspace = true
ssi-bbs = { workspace = true, optional = true }
xsd-types.workspace = true # public reexport
document-features = "0.2"

[dev-dependencies]
async-std = { workspace = true, features = ["attributes"] }
uuid = { version = "0.8", features = ["v4", "serde"] }
uuid = { workspace = true, features = ["v4", "serde"] }
iref.workspace = true
static-iref.workspace = true
serde = { workspace = true, features = ["derive"] }
Expand Down
15 changes: 15 additions & 0 deletions crates/bbs/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
[package]
name = "ssi-bbs"
version = "0.1.0"
edition = "2021"
authors = ["Spruce Systems, Inc."]
license = "Apache-2.0"
description = "The BBS Signature Scheme implementation for SSI"
repository = "https://github.com/spruceid/ssi/"
documentation = "https://docs.rs/ssi-bbs/"

[dependencies]
ssi-crypto.workspace = true
ssi-claims-core.workspace = true
zkryptium = "0.2.2"
rand.workspace = true
112 changes: 112 additions & 0 deletions crates/bbs/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
use ssi_claims_core::{InvalidProof, MessageSignatureError, ProofValidationError, ProofValidity};
use ssi_crypto::algorithm::BbsParameters;
pub use zkryptium::{
bbsplus::keys::{BBSplusPublicKey, BBSplusSecretKey},
errors::Error,
};
use zkryptium::{
bbsplus::{
ciphersuites::{BbsCiphersuite, Bls12381Sha256},
commitment::BlindFactor,
},
keys::pair::KeyPair,
schemes::{
algorithms::BBSplus,
generics::{BlindSignature, Signature},
},
};

pub use ssi_crypto::algorithm::Bbs;

#[derive(Debug)]
pub struct ProofGenFailed;

pub fn proof_gen(
pk: &BBSplusPublicKey,
signature: &[u8],
header: &[u8],
ph: Option<&[u8]>,
messages: &[Vec<u8>],
disclosed_indexes: &[usize],
) -> Result<Vec<u8>, ProofGenFailed> {
Ok(
zkryptium::schemes::generics::PoKSignature::<BBSplus<Bls12381Sha256>>::proof_gen(
pk,
signature,
Some(header),
ph,
Some(messages),
Some(disclosed_indexes),
)
.map_err(|_| ProofGenFailed)?
.to_bytes(),
)
}

pub fn proof_verify(
pk: &BBSplusPublicKey,
signature: &[u8],
header: &[u8],
ph: Option<&[u8]>,
disclosed_messages: &[Vec<u8>],
disclosed_indexes: &[usize],
) -> Result<ProofValidity, ProofValidationError> {
let signature =
zkryptium::schemes::generics::PoKSignature::<BBSplus<Bls12381Sha256>>::from_bytes(
signature,
)
.map_err(|_| ProofValidationError::InvalidSignature)?;

Ok(signature
.proof_verify(
pk,
Some(disclosed_messages),
Some(disclosed_indexes),
Some(header),
ph,
)
.map_err(|_| InvalidProof::Signature))
}

pub fn sign(
params: BbsParameters,
sk: &BBSplusSecretKey,
pk: &BBSplusPublicKey,
messages: &[Vec<u8>],
) -> Result<Vec<u8>, MessageSignatureError> {
match params {
BbsParameters::Baseline { header } => {
Ok(
Signature::<BBSplus<Bls12381Sha256>>::sign(Some(messages), sk, pk, Some(&header))
.map_err(MessageSignatureError::signature_failed)?
.to_bytes()
.to_vec(),
)
}
BbsParameters::Blind {
header,
commitment_with_proof,
signer_blind,
} => {
let signer_blind = signer_blind.map(|b| BlindFactor::from_bytes(&b).unwrap());
Ok(BlindSignature::<BBSplus<Bls12381Sha256>>::blind_sign(
sk,
pk,
commitment_with_proof.as_deref(),
Some(&header),
Some(messages),
signer_blind.as_ref(),
)
.map_err(MessageSignatureError::signature_failed)?
.to_bytes()
.to_vec())
}
}
}

pub fn generate_secret_key(rng: &mut impl rand::RngCore) -> BBSplusSecretKey {
let mut key_material = [0; Bls12381Sha256::IKM_LEN];
rng.fill_bytes(&mut key_material);
let pair = KeyPair::<BBSplus<Bls12381Sha256>>::generate(&key_material, None, None).unwrap();
pair.into_parts().0
}
3 changes: 3 additions & 0 deletions crates/claims/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,9 @@ solana = ["ssi-data-integrity/solana", "ssi-verification-methods/solana"]
# Enables `EthereumPersonalSignature2021`
ethereum = ["ssi-jws/eip", "ssi-data-integrity/ethereum"]

# Enables `Bbs2023`
bbs = ["ssi-data-integrity/bbs"]

[dependencies]
ssi-core.workspace = true
ssi-crypto.workspace = true
Expand Down
1 change: 1 addition & 0 deletions crates/claims/core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ linked-data = ["dep:linked-data"]

[dependencies]
ssi-core.workspace = true
ssi-crypto.workspace = true
educe.workspace = true
thiserror.workspace = true
chrono.workspace = true
Expand Down
75 changes: 75 additions & 0 deletions crates/claims/core/src/signature.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use core::fmt;

use ssi_crypto::algorithm::{AlgorithmError, UnsupportedAlgorithm};
use ssi_eip712::Eip712TypesLoaderProvider;
use ssi_json_ld::JsonLdLoaderProvider;

Expand All @@ -26,6 +27,9 @@ pub enum SignatureError {
#[error("claims: {0}")]
Claims(String),

#[error("missing required option `{0}`")]
MissingRequiredOption(String),

#[error("missing signer")]
MissingSigner,

Expand All @@ -43,6 +47,10 @@ impl SignatureError {
pub fn other(e: impl fmt::Display) -> Self {
Self::Other(e.to_string())
}

pub fn missing_required_option(name: &str) -> Self {
Self::MissingRequiredOption(name.to_string())
}
}

impl From<std::convert::Infallible> for SignatureError {
Expand All @@ -51,6 +59,73 @@ impl From<std::convert::Infallible> for SignatureError {
}
}

#[derive(Debug, thiserror::Error)]
pub enum MessageSignatureError {
#[error("0")]
SignatureFailed(String),

#[error("invalid signature client query")]
InvalidQuery,

#[error("invalid signer response")]
InvalidResponse,

#[error("invalid public key")]
InvalidPublicKey,

#[error("invalid secret key")]
InvalidSecretKey,

#[error("missing signature algorithm")]
MissingAlgorithm,

#[error("unsupported signature algorithm `{0}`")]
UnsupportedAlgorithm(String),

#[error("unsupported verification method `{0}`")]
UnsupportedVerificationMethod(String),

/// Signature algorithm does not support multi-message signing.
#[error("too many messages")]
TooManyMessages,

/// Signature algorithm requires at least one message.
#[error("missing message")]
MissingMessage,
}

impl MessageSignatureError {
pub fn signature_failed(e: impl ToString) -> Self {
Self::SignatureFailed(e.to_string())
}
}

impl From<MessageSignatureError> for SignatureError {
fn from(value: MessageSignatureError) -> Self {
match value {
MessageSignatureError::MissingAlgorithm => Self::MissingAlgorithm,
MessageSignatureError::UnsupportedAlgorithm(name) => Self::UnsupportedAlgorithm(name),
MessageSignatureError::InvalidSecretKey => Self::InvalidSecretKey,
other => Self::other(other),
}
}
}

impl From<AlgorithmError> for MessageSignatureError {
fn from(value: AlgorithmError) -> Self {
match value {
AlgorithmError::Missing => Self::MissingAlgorithm,
AlgorithmError::Unsupported(a) => Self::UnsupportedAlgorithm(a.to_string()),
}
}
}

impl From<UnsupportedAlgorithm> for MessageSignatureError {
fn from(value: UnsupportedAlgorithm) -> Self {
Self::UnsupportedAlgorithm(value.0.to_string())
}
}

/// Signature environment.
///
/// This is a common environment implementation expected to work with most
Expand Down
13 changes: 13 additions & 0 deletions crates/claims/core/src/verification/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,19 @@ pub enum Invalid {
Proof(#[from] InvalidProof),
}

/// Arbitrary resource provider.
pub trait ResourceProvider<T> {
/// Returns a reference to the resource of type `T`.
fn get_resource(&self) -> &T;
}

/// Anything can return the unit resource.
impl<T> ResourceProvider<()> for T {
fn get_resource(&self) -> &() {
&()
}
}

/// Type that provides a public key resolver.
pub trait ResolverProvider {
/// Public key resolver.
Expand Down
5 changes: 5 additions & 0 deletions crates/claims/crates/data-integrity/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,9 @@ solana = ["ssi-data-integrity-suites/solana"]
## - `Eip712Signature2021` (requires `eip712`)
ethereum = ["ssi-data-integrity-suites/ethereum"]

## BBS cryptographic suites.
bbs = ["ssi-bbs", "ssi-data-integrity-suites/bbs"]

[dependencies]
ssi-data-integrity-core.workspace = true
ssi-data-integrity-suites.workspace = true
Expand All @@ -106,6 +109,8 @@ ssi-json-ld.workspace = true
ssi-verification-methods.workspace = true
ssi-eip712.workspace = true
ssi-claims-core.workspace = true
ssi-di-sd-primitives.workspace = true
ssi-bbs = { workspace = true, optional = true }
iref.workspace = true
rdf-types.workspace = true
linked-data.workspace = true
Expand Down
2 changes: 1 addition & 1 deletion crates/claims/crates/data-integrity/core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ futures.workspace = true
self_cell = "1.0.1"
contextual.workspace = true
multibase.workspace = true
digest = "0.10"
digest.workspace = true

[package.metadata.docs.rs]
all-features = true
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,10 @@ use ssi_rdf::{AnyLdEnvironment, LdEnvironment};

use crate::{
hashing::ConcatOutputSize,
suite::standard::{self, HashingAlgorithm, TransformationAlgorithm, TransformationError},
suite::{
standard::{self, HashingAlgorithm, TransformationAlgorithm, TransformationError},
TransformationOptions,
},
CryptographicSuite, ProofConfigurationRef, SerializeCryptographicSuite,
StandardCryptographicSuite,
};
Expand Down Expand Up @@ -36,6 +39,7 @@ where
context: &C,
data: &T,
proof_configuration: ProofConfigurationRef<'_, S>,
_transformation_options: TransformationOptions<S>,
) -> Result<Self::Output, TransformationError> {
let mut ld = LdEnvironment::default();

Expand Down
Loading

0 comments on commit 0e87bfe

Please sign in to comment.