Skip to content

Commit

Permalink
Impl Ed25519VerificationKey2020 and Multikey.
Browse files Browse the repository at this point in the history
  • Loading branch information
timothee-haudebourg committed Jul 19, 2023
1 parent 3cbe443 commit 551370e
Show file tree
Hide file tree
Showing 17 changed files with 918 additions and 116 deletions.
14 changes: 7 additions & 7 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ members = [
# "vc-test",
# "did-test",
# "ssi-caips",
"ssi-multicodec",
"ssi-jwk",
# "ssi-core",
"ssi-crypto",
Expand All @@ -101,10 +102,11 @@ members = [

[workspace.dependencies]
ssi-core = { path = "./ssi-core", version = "0.1" }
ssi-multicodec = { path = "./ssi-multicodec", version = "0.1" }
ssi-crypto = { path = "./ssi-crypto", version = "0.1" }
ssi-verification-methods = { path = "./ssi-verification-methods", version = "0.1" }
ssi-jwk = { path = "./ssi-jwk", version = "0.1" }
ssi-jws = { path = "./ssi-jws", version = "0.1" }
ssi-jwk = { path = "./ssi-jwk", version = "0.1", default-features = false }
ssi-jws = { path = "./ssi-jws", version = "0.1", default-features = false }
ssi-jwt = { path = "./ssi-jwt", version = "0.1" }
ssi-rdf = { path = "./ssi-rdf", version = "0.1" }
ssi-dids = { path = "./ssi-dids", version = "0.1" }
Expand All @@ -123,13 +125,11 @@ multibase = "0.9.1"
json-ld = "0.15"
serde = "1.0"
serde_json = "1.0"
# treeldr-rust-macros = { git = "https://github.com/spruceid/treeldr" }
# treeldr-rust-prelude = { git = "https://github.com/spruceid/treeldr" }
treeldr-rust-macros = { path = "/home/timothee/Projets/spruceid/treeldr/modules/rust/macros" }
treeldr-rust-prelude = { path = "/home/timothee/Projets/spruceid/treeldr/modules/rust/prelude" }
treeldr-rust-macros = { git = "https://github.com/spruceid/treeldr", branch = "rdf-types-v15.3" }
treeldr-rust-prelude = { git = "https://github.com/spruceid/treeldr", branch = "rdf-types-v15.3" }

[patch.crates-io]
rdf-types = { path = "/home/timothee/Projets/utils/rdf/rdf-types" }
rdf-types = { git = "https://github.com/timothee-haudebourg/rdf-types.git", rev = "55a0826" }

# [dev-dependencies]
# async-std = { version = "1.9", features = ["attributes"] }
Expand Down
2 changes: 1 addition & 1 deletion ssi-jwk/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -58,9 +58,9 @@ snarkvm-utilities = { version = "0.7.9", optional = true }
snarkvm-parameters = { version = "0.7.9", optional = true }
blake2b_simd = { version = "0.5", optional = true }
multibase = "0.9.1"
unsigned-varint = "0.7.1"
num-traits = "0.2"
num-derive = "0.3"
ssi-multicodec.workspace = true

[target.'cfg(target_arch = "wasm32")'.dependencies]
getrandom = { version = "0.2", features = ["js"], optional = true }
Expand Down
80 changes: 21 additions & 59 deletions ssi-jwk/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

use num_bigint::{BigInt, Sign};
use simple_asn1::{ASN1Block, ASN1Class, ToASN1};
use ssi_multicodec::MultiEncoded;
use std::convert::TryFrom;
use std::result::Result;
use zeroize::Zeroize;
Expand All @@ -22,8 +23,6 @@ pub mod blakesig;

pub mod der;

mod multicodec;

use der::{
BitString, Ed25519PrivateKey, Ed25519PublicKey, Integer, OctetString, RSAPrivateKey,
RSAPublicKey, RSAPublicKeyFromASN1Error,
Expand Down Expand Up @@ -473,64 +472,27 @@ impl JWK {
Ok(thumbprint)
}

pub fn from_vm_type(type_: &str, pk_bytes: Vec<u8>) -> Result<Self, Error> {
match type_ {
// TODO: check against IRIs when in JSON-LD
#[cfg(feature = "ed25519")]
"Ed25519VerificationKey2018" => ed25519_parse(&pk_bytes),
#[cfg(feature = "ed25519")]
"Ed25519VerificationKey2020" => match multicodec::decode(&pk_bytes) {
Ok((codec, pk)) => match codec {
multicodec::Codec::Ed25519Pub => ed25519_parse(&pk),
_ => Err(Error::MultibaseKeyPrefix),
},
Err(_) => Err(Error::MultibaseKeyPrefix),
},
pub fn from_multicodec(multicodec: &MultiEncoded) -> Result<Self, Error> {
#[allow(unused_variables)]
let (codec, k) = multicodec.parts();
match codec {
#[cfg(any(feature = "ed25519"))]
ssi_multicodec::ED25519_PUB => ed25519_parse(k),
#[cfg(any(feature = "ed25519"))]
ssi_multicodec::ED25519_PRIV => ed25519_parse_private(k),
#[cfg(feature = "secp256k1")]
"EcdsaSecp256k1VerificationKey2019" | "EcdsaSecp256k1RecoveryMethod2020" => {
secp256k1_parse(&pk_bytes)
}
"Multikey" => match multicodec::decode(&pk_bytes) {
Ok((codec, pk)) => match codec {
#[cfg(any(feature = "ed25519"))]
multicodec::Codec::Ed25519Pub => ed25519_parse(&pk),
#[cfg(feature = "secp256k1")]
multicodec::Codec::Secp256k1Pub => secp256k1_parse(&pk),
#[cfg(feature = "secp256r1")]
multicodec::Codec::P256Pub => p256_parse(&pk),
#[cfg(feature = "secp384r1")]
multicodec::Codec::P384Pub => p384_parse(&pk),
_ => Err(Error::MultibaseKeyPrefix),
},
Err(_) => Err(Error::MultibaseKeyPrefix),
},
_ => Err(Error::UnsupportedKeyType),
}
}

pub fn from_multicodec(multicodec: &str) -> Result<Self, Error> {
let bytes = multibase::decode(multicodec)?.1;
match multicodec::decode(&bytes) {
Ok((codec, k)) => match codec {
#[cfg(any(feature = "ed25519"))]
multicodec::Codec::Ed25519Pub => ed25519_parse(&k),
#[cfg(any(feature = "ed25519"))]
multicodec::Codec::Ed25519Priv => ed25519_parse_private(&k),
#[cfg(feature = "secp256k1")]
multicodec::Codec::Secp256k1Pub => secp256k1_parse(&k),
#[cfg(feature = "secp256k1")]
multicodec::Codec::Secp256k1Priv => secp256k1_parse_private(&k),
#[cfg(feature = "secp256r1")]
multicodec::Codec::P256Pub => p256_parse(&k),
#[cfg(feature = "secp256r1")]
multicodec::Codec::P256Priv => p256_parse_private(&k),
#[cfg(feature = "secp384r1")]
multicodec::Codec::P384Pub => p384_parse(&k),
#[cfg(feature = "secp384r1")]
multicodec::Codec::P384Priv => p384_parse_private(&k),
_ => Err(Error::MultibaseKeyPrefix),
},
Err(_) => Err(Error::MultibaseKeyPrefix),
ssi_multicodec::SECP256K1_PUB => secp256k1_parse(k),
#[cfg(feature = "secp256k1")]
ssi_multicodec::SECP256K1_PRIV => secp256k1_parse_private(k),
#[cfg(feature = "secp256r1")]
ssi_multicodec::P256_PUB => p256_parse(k),
#[cfg(feature = "secp256r1")]
ssi_multicodec::P256_PRIV => p256_parse_private(k),
#[cfg(feature = "secp384r1")]
ssi_multicodec::P384_PUB => p384_parse(k),
#[cfg(feature = "secp384r1")]
ssi_multicodec::P384_PRIV => p384_parse_private(k),
_ => Err(Error::MultibaseKeyPrefix),
}
}
}
Expand Down
30 changes: 0 additions & 30 deletions ssi-jwk/src/multicodec.rs

This file was deleted.

4 changes: 2 additions & 2 deletions ssi-jws/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ use serde::{Deserialize, Serialize};
use ssi_jwk::{Algorithm, Base64urlUInt, Params as JWKParams, JWK};
use std::borrow::Cow;
use std::collections::BTreeMap;
use std::convert::TryFrom;
use std::ops::Deref;

pub type VerificationWarnings = Vec<String>;
Expand Down Expand Up @@ -322,6 +321,7 @@ fn base64_encode_json<T: Serialize>(object: &T) -> Result<String, Error> {
Ok(base64::encode_config(json, base64::URL_SAFE_NO_PAD))
}

#[allow(unreachable_code, unused_variables)]
pub fn sign_bytes(algorithm: Algorithm, data: &[u8], key: &JWK) -> Result<Vec<u8>, Error> {
let signature = match &key.params {
#[cfg(feature = "ring")]
Expand Down Expand Up @@ -487,13 +487,13 @@ pub fn sign_bytes_b64(algorithm: Algorithm, data: &[u8], key: &JWK) -> Result<St
Ok(sig_b64)
}

#[allow(unreachable_code, unused_variables, unused_mut)]
pub fn verify_bytes_warnable(
algorithm: Algorithm,
data: &[u8],
key: &JWK,
signature: &[u8],
) -> Result<VerificationWarnings, Error> {
#[allow(unused_mut)]
let mut warnings = VerificationWarnings::default();
if let Some(key_algorithm) = key.algorithm {
if key_algorithm != algorithm
Expand Down
4 changes: 2 additions & 2 deletions ssi-jwt/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
json-syntax = { workspace = true, features = ["serde"] }
ssi-core.workspace = true
ssi-jwk = { workspace = true, default-features = false }
ssi-jws = { workspace = true, default-features = false }
ssi-jwk.workspace = true
ssi-jws.workspace = true
serde_with = "2.3.2"

[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
Expand Down
1 change: 1 addition & 0 deletions ssi-multicodec/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
src/table.rs
16 changes: 16 additions & 0 deletions ssi-multicodec/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
[package]
name = "ssi-multicodec"
version = "0.1.0"
edition = "2021"
authors = ["Spruce Systems, Inc."]
license = "Apache-2.0"
description = "Implementation of the Multicodec specification for the ssi library"
repository = "https://github.com/spruceid/ssi/"
documentation = "https://docs.rs/ssi-multicodec/"

[dependencies]
unsigned-varint = "0.7.1"

[build-dependencies]
csv = "1.2.2"
thiserror.workspace = true
105 changes: 105 additions & 0 deletions ssi-multicodec/build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
use std::{fs, io, io::Write, path::Path};

fn main() {
println!("cargo:rerun-if-changed=src/table.csv");
if let Err(e) = generate_codecs("src/table.csv", "src/table.rs") {
eprintln!("unable to generate codecs: {e}");
std::process::exit(1);
}
}

#[derive(Debug, thiserror::Error)]
enum Error {
#[error(transparent)]
IO(#[from] io::Error),

#[error(transparent)]
Csv(#[from] csv::Error),

#[error("missing name field")]
MissingNameField,

#[error("missing tag field")]
MissingTagField,

#[error("missing code field")]
MissingCodeField,

#[error("invalid code `{0}`")]
InvalidCode(String),

#[error("missing status field")]
MissingStatusField,

#[error("invalid status `{0}`")]
InvalidStatus(String),

#[error("missing description field")]
MissingDescriptionField,
}

fn generate_codecs(input: impl AsRef<Path>, output: impl AsRef<Path>) -> Result<(), Error> {
let mut reader = csv::Reader::from_path(input)?;
let mut f = io::BufWriter::new(fs::File::create(output)?);

for record in reader.records() {
let record = record?;
let mut fields = record.iter();

let name = fields.next().ok_or(Error::MissingNameField)?.trim();
let _tag = fields.next().ok_or(Error::MissingTagField)?.trim();
let code = parse_code(fields.next().ok_or(Error::MissingCodeField)?.trim())?;
let status = parse_status(fields.next().ok_or(Error::MissingStatusField)?.trim())?;
let description = fields.next().ok_or(Error::MissingDescriptionField)?.trim();

let const_name = all_caps(name);

writeln!(f, "#[doc = \"{description}\"]")?;

if matches!(status, Status::Deprecated) {
writeln!(f, "#[deprecated]")?
}

writeln!(f, "pub const {const_name}: u64 = {code:#x}u64;")?;
}

Ok(())
}

fn parse_code(s: &str) -> Result<u64, Error> {
u64::from_str_radix(
s.strip_prefix("0x")
.ok_or_else(|| Error::InvalidCode(s.to_owned()))?,
16,
)
.map_err(|_| Error::InvalidCode(s.to_owned()))
}

fn parse_status(s: &str) -> Result<Status, Error> {
match s {
"draft" => Ok(Status::Draft),
"permanent" => Ok(Status::Permanent),
"deprecated" => Ok(Status::Deprecated),
_ => Err(Error::InvalidStatus(s.to_owned())),
}
}

enum Status {
Draft,
Permanent,
Deprecated,
}

fn all_caps(s: &str) -> String {
let mut result = String::new();

for c in s.chars() {
if c == '-' {
result.push('_');
} else {
result.push(c.to_uppercase().next().unwrap());
}
}

result
}
Loading

0 comments on commit 551370e

Please sign in to comment.