From 714e40f71745dea7dc57e6c072a99cc0920cde30 Mon Sep 17 00:00:00 2001 From: chunningham Date: Wed, 15 Feb 2023 16:43:53 +0100 Subject: [PATCH 01/70] move cacao v1 to it's own module --- src/lib.rs | 171 +----------------------------------- src/v1/mod.rs | 172 +++++++++++++++++++++++++++++++++++++ src/{ => v1}/siwe_cacao.rs | 0 3 files changed, 173 insertions(+), 170 deletions(-) create mode 100644 src/v1/mod.rs rename src/{ => v1}/siwe_cacao.rs (100%) diff --git a/src/lib.rs b/src/lib.rs index adde5a4..d3d7902 100755 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,172 +1,3 @@ -use std::fmt::Debug; - -use async_trait::async_trait; -use libipld::{cbor::DagCbor, DagCbor}; pub use siwe; -pub mod siwe_cacao; - -#[derive(DagCbor, Debug, Clone, PartialEq)] -pub struct CACAO -where - S: SignatureScheme, - S::Signature: DagCbor, - T: Representation, -{ - h: T::Header, - p: T::Payload, - s: S::Signature, -} - -impl CACAO -where - S: SignatureScheme, - S::Signature: DagCbor, - R: Representation, -{ - pub fn new(p: R::Payload, s: S::Signature, h: Option) -> Self { - Self { - h: h.unwrap_or_else(R::header), - p, - s, - } - } - - pub fn header(&self) -> &R::Header { - &self.h - } - - pub fn payload(&self) -> &R::Payload { - &self.p - } - - pub fn signature(&self) -> &S::Signature { - &self.s - } - - pub async fn verify(&self) -> Result<(), S::Err> - where - S: Send + Sync, - S::Signature: Send + Sync, - R::Payload: Send + Sync + Debug, - R::Header: Send + Sync + Debug, - { - S::verify_cacao(self).await - } -} - -pub trait Representation { - type Payload: DagCbor; - type Header: DagCbor; - fn header() -> Self::Header; -} - -#[async_trait] -pub trait SignatureScheme: Debug -where - T: Representation, -{ - type Signature: Debug; - type Err; - async fn verify(payload: &T::Payload, sig: &Self::Signature) -> Result<(), Self::Err> - where - Self::Signature: Send + Sync; - - async fn verify_cacao(cacao: &CACAO) -> Result<(), Self::Err> - where - Self: Sized, - Self::Signature: Send + Sync + Debug + DagCbor, - T::Payload: Send + Sync + Debug, - T::Header: Send + Sync + Debug, - { - Self::verify(cacao.payload(), cacao.signature()).await - } -} - -#[cfg(test)] -pub mod tests { - use super::*; - use libipld::{ - cbor::DagCborCodec, - codec::{assert_roundtrip, Decode}, - ipld, - }; - use siwe_cacao::SiweCacao; - use std::io::Cursor; - #[test] - fn test_ipld() { - let cacao = SiweCacao::decode( - DagCborCodec, - &mut Cursor::new([ - 163u8, 97u8, 104u8, 161u8, 97u8, 116u8, 103u8, 101u8, 105u8, 112u8, 52u8, 51u8, - 54u8, 49u8, 97u8, 112u8, 168u8, 99u8, 97u8, 117u8, 100u8, 120u8, 56u8, 100u8, - 105u8, 100u8, 58u8, 107u8, 101u8, 121u8, 58u8, 122u8, 54u8, 77u8, 107u8, 114u8, - 66u8, 100u8, 78u8, 100u8, 119u8, 85u8, 80u8, 110u8, 88u8, 68u8, 86u8, 68u8, 49u8, - 68u8, 67u8, 120u8, 101u8, 100u8, 122u8, 86u8, 86u8, 66u8, 112u8, 97u8, 71u8, 105u8, - 56u8, 97u8, 83u8, 109u8, 111u8, 88u8, 70u8, 65u8, 101u8, 75u8, 78u8, 103u8, 116u8, - 65u8, 101u8, 114u8, 56u8, 99u8, 105u8, 97u8, 116u8, 120u8, 24u8, 50u8, 48u8, 50u8, - 49u8, 45u8, 48u8, 57u8, 45u8, 51u8, 48u8, 84u8, 49u8, 54u8, 58u8, 50u8, 53u8, 58u8, - 50u8, 52u8, 46u8, 48u8, 48u8, 48u8, 90u8, 99u8, 105u8, 115u8, 115u8, 120u8, 59u8, - 100u8, 105u8, 100u8, 58u8, 112u8, 107u8, 104u8, 58u8, 101u8, 105u8, 112u8, 49u8, - 53u8, 53u8, 58u8, 49u8, 58u8, 48u8, 120u8, 66u8, 100u8, 57u8, 68u8, 57u8, 99u8, - 55u8, 68u8, 67u8, 51u8, 56u8, 57u8, 55u8, 49u8, 53u8, 97u8, 56u8, 57u8, 102u8, - 67u8, 56u8, 49u8, 52u8, 57u8, 69u8, 52u8, 97u8, 53u8, 66u8, 101u8, 57u8, 49u8, - 51u8, 51u8, 54u8, 66u8, 50u8, 55u8, 57u8, 54u8, 101u8, 110u8, 111u8, 110u8, 99u8, - 101u8, 104u8, 51u8, 50u8, 56u8, 57u8, 49u8, 55u8, 53u8, 55u8, 102u8, 100u8, 111u8, - 109u8, 97u8, 105u8, 110u8, 107u8, 115u8, 101u8, 114u8, 118u8, 105u8, 99u8, 101u8, - 46u8, 111u8, 114u8, 103u8, 103u8, 118u8, 101u8, 114u8, 115u8, 105u8, 111u8, 110u8, - 97u8, 49u8, 105u8, 114u8, 101u8, 115u8, 111u8, 117u8, 114u8, 99u8, 101u8, 115u8, - 130u8, 120u8, 53u8, 105u8, 112u8, 102u8, 115u8, 58u8, 47u8, 47u8, 81u8, 109u8, - 101u8, 55u8, 115u8, 115u8, 51u8, 65u8, 82u8, 86u8, 103u8, 120u8, 118u8, 54u8, - 114u8, 88u8, 113u8, 86u8, 80u8, 105u8, 105u8, 107u8, 77u8, 74u8, 56u8, 117u8, 50u8, - 78u8, 76u8, 103u8, 109u8, 103u8, 115u8, 122u8, 103u8, 49u8, 51u8, 112u8, 89u8, - 114u8, 68u8, 75u8, 69u8, 111u8, 105u8, 117u8, 120u8, 38u8, 104u8, 116u8, 116u8, - 112u8, 115u8, 58u8, 47u8, 47u8, 101u8, 120u8, 97u8, 109u8, 112u8, 108u8, 101u8, - 46u8, 99u8, 111u8, 109u8, 47u8, 109u8, 121u8, 45u8, 119u8, 101u8, 98u8, 50u8, 45u8, - 99u8, 108u8, 97u8, 105u8, 109u8, 46u8, 106u8, 115u8, 111u8, 110u8, 105u8, 115u8, - 116u8, 97u8, 116u8, 101u8, 109u8, 101u8, 110u8, 116u8, 120u8, 65u8, 73u8, 32u8, - 97u8, 99u8, 99u8, 101u8, 112u8, 116u8, 32u8, 116u8, 104u8, 101u8, 32u8, 83u8, - 101u8, 114u8, 118u8, 105u8, 99u8, 101u8, 79u8, 114u8, 103u8, 32u8, 84u8, 101u8, - 114u8, 109u8, 115u8, 32u8, 111u8, 102u8, 32u8, 83u8, 101u8, 114u8, 118u8, 105u8, - 99u8, 101u8, 58u8, 32u8, 104u8, 116u8, 116u8, 112u8, 115u8, 58u8, 47u8, 47u8, - 115u8, 101u8, 114u8, 118u8, 105u8, 99u8, 101u8, 46u8, 111u8, 114u8, 103u8, 47u8, - 116u8, 111u8, 115u8, 97u8, 115u8, 162u8, 97u8, 115u8, 152u8, 65u8, 16u8, 24u8, - 147u8, 19u8, 24u8, 231u8, 24u8, 82u8, 24u8, 93u8, 24u8, 234u8, 24u8, 85u8, 24u8, - 236u8, 24u8, 154u8, 24u8, 60u8, 24u8, 203u8, 24u8, 182u8, 24u8, 62u8, 24u8, 168u8, - 24u8, 214u8, 24u8, 132u8, 6u8, 24u8, 54u8, 24u8, 98u8, 24u8, 80u8, 24u8, 207u8, - 8u8, 24u8, 128u8, 24u8, 214u8, 24u8, 112u8, 24u8, 50u8, 24u8, 180u8, 24u8, 87u8, - 24u8, 171u8, 24u8, 51u8, 24u8, 201u8, 24u8, 38u8, 24u8, 198u8, 24u8, 127u8, 24u8, - 243u8, 24u8, 252u8, 24u8, 198u8, 24u8, 106u8, 24u8, 195u8, 24u8, 27u8, 24u8, 170u8, - 24u8, 104u8, 24u8, 104u8, 24u8, 168u8, 10u8, 18u8, 24u8, 251u8, 24u8, 230u8, 24u8, - 183u8, 24u8, 99u8, 24u8, 138u8, 24u8, 137u8, 24u8, 244u8, 24u8, 246u8, 24u8, 213u8, - 24u8, 26u8, 2u8, 24u8, 41u8, 24u8, 89u8, 12u8, 24u8, 246u8, 24u8, 103u8, 24u8, - 111u8, 24u8, 28u8, 97u8, 116u8, 102u8, 101u8, 105u8, 112u8, 49u8, 57u8, 49u8, - ]), - ) - .unwrap(); - - let ipld = ipld!({ - "h": { - "t": "eip4361", - }, - "p": { - "aud": "did:key:z6MkrBdNdwUPnXDVD1DCxedzVVBpaGi8aSmoXFAeKNgtAer8", - "domain": "service.org", - "iat": "2021-09-30T16:25:24.000Z", - "iss": "did:pkh:eip155:1:0xBd9D9c7DC389715a89fC8149E4a5Be91336B2796", - "nonce": "32891757", - "resources": [ - "ipfs://Qme7ss3ARVgxv6rXqVPiikMJ8u2NLgmgszg13pYrDKEoiu", - "https://example.com/my-web2-claim.json", - ], - "statement": "I accept the ServiceOrg Terms of Service: https://service.org/tos", - "version": "1", - }, - "s": { - "s": [0x10, 0x93, 0x13, 0xe7, 0x52, 0x5d, 0xea, 0x55, 0xec, 0x9a, 0x3c, 0xcb, 0xb6, 0x3e, 0xa8, 0xd6, 0x84, 0x06, 0x36, 0x62, 0x50, 0xcf, 0x08, 0x80, 0xd6, 0x70, 0x32, 0xb4, 0x57, 0xab, 0x33, 0xc9, 0x26, 0xc6, 0x7f, 0xf3, 0xfc, 0xc6, 0x6a, 0xc3, 0x1b, 0xaa, 0x68, 0x68, 0xa8, 0x0a, 0x12, 0xfb, 0xe6, 0xb7, 0x63, 0x8a, 0x89, 0xf4, 0xf6, 0xd5, 0x1a, 0x02, 0x29, 0x59, 0x0c, 0xf6, 0x67, 0x6f, 0x1c], - "t": "eip191", - }, - }); - assert_roundtrip(DagCborCodec, &cacao, &ipld); - } -} +pub mod v1; diff --git a/src/v1/mod.rs b/src/v1/mod.rs new file mode 100644 index 0000000..adde5a4 --- /dev/null +++ b/src/v1/mod.rs @@ -0,0 +1,172 @@ +use std::fmt::Debug; + +use async_trait::async_trait; +use libipld::{cbor::DagCbor, DagCbor}; +pub use siwe; + +pub mod siwe_cacao; + +#[derive(DagCbor, Debug, Clone, PartialEq)] +pub struct CACAO +where + S: SignatureScheme, + S::Signature: DagCbor, + T: Representation, +{ + h: T::Header, + p: T::Payload, + s: S::Signature, +} + +impl CACAO +where + S: SignatureScheme, + S::Signature: DagCbor, + R: Representation, +{ + pub fn new(p: R::Payload, s: S::Signature, h: Option) -> Self { + Self { + h: h.unwrap_or_else(R::header), + p, + s, + } + } + + pub fn header(&self) -> &R::Header { + &self.h + } + + pub fn payload(&self) -> &R::Payload { + &self.p + } + + pub fn signature(&self) -> &S::Signature { + &self.s + } + + pub async fn verify(&self) -> Result<(), S::Err> + where + S: Send + Sync, + S::Signature: Send + Sync, + R::Payload: Send + Sync + Debug, + R::Header: Send + Sync + Debug, + { + S::verify_cacao(self).await + } +} + +pub trait Representation { + type Payload: DagCbor; + type Header: DagCbor; + fn header() -> Self::Header; +} + +#[async_trait] +pub trait SignatureScheme: Debug +where + T: Representation, +{ + type Signature: Debug; + type Err; + async fn verify(payload: &T::Payload, sig: &Self::Signature) -> Result<(), Self::Err> + where + Self::Signature: Send + Sync; + + async fn verify_cacao(cacao: &CACAO) -> Result<(), Self::Err> + where + Self: Sized, + Self::Signature: Send + Sync + Debug + DagCbor, + T::Payload: Send + Sync + Debug, + T::Header: Send + Sync + Debug, + { + Self::verify(cacao.payload(), cacao.signature()).await + } +} + +#[cfg(test)] +pub mod tests { + use super::*; + use libipld::{ + cbor::DagCborCodec, + codec::{assert_roundtrip, Decode}, + ipld, + }; + use siwe_cacao::SiweCacao; + use std::io::Cursor; + #[test] + fn test_ipld() { + let cacao = SiweCacao::decode( + DagCborCodec, + &mut Cursor::new([ + 163u8, 97u8, 104u8, 161u8, 97u8, 116u8, 103u8, 101u8, 105u8, 112u8, 52u8, 51u8, + 54u8, 49u8, 97u8, 112u8, 168u8, 99u8, 97u8, 117u8, 100u8, 120u8, 56u8, 100u8, + 105u8, 100u8, 58u8, 107u8, 101u8, 121u8, 58u8, 122u8, 54u8, 77u8, 107u8, 114u8, + 66u8, 100u8, 78u8, 100u8, 119u8, 85u8, 80u8, 110u8, 88u8, 68u8, 86u8, 68u8, 49u8, + 68u8, 67u8, 120u8, 101u8, 100u8, 122u8, 86u8, 86u8, 66u8, 112u8, 97u8, 71u8, 105u8, + 56u8, 97u8, 83u8, 109u8, 111u8, 88u8, 70u8, 65u8, 101u8, 75u8, 78u8, 103u8, 116u8, + 65u8, 101u8, 114u8, 56u8, 99u8, 105u8, 97u8, 116u8, 120u8, 24u8, 50u8, 48u8, 50u8, + 49u8, 45u8, 48u8, 57u8, 45u8, 51u8, 48u8, 84u8, 49u8, 54u8, 58u8, 50u8, 53u8, 58u8, + 50u8, 52u8, 46u8, 48u8, 48u8, 48u8, 90u8, 99u8, 105u8, 115u8, 115u8, 120u8, 59u8, + 100u8, 105u8, 100u8, 58u8, 112u8, 107u8, 104u8, 58u8, 101u8, 105u8, 112u8, 49u8, + 53u8, 53u8, 58u8, 49u8, 58u8, 48u8, 120u8, 66u8, 100u8, 57u8, 68u8, 57u8, 99u8, + 55u8, 68u8, 67u8, 51u8, 56u8, 57u8, 55u8, 49u8, 53u8, 97u8, 56u8, 57u8, 102u8, + 67u8, 56u8, 49u8, 52u8, 57u8, 69u8, 52u8, 97u8, 53u8, 66u8, 101u8, 57u8, 49u8, + 51u8, 51u8, 54u8, 66u8, 50u8, 55u8, 57u8, 54u8, 101u8, 110u8, 111u8, 110u8, 99u8, + 101u8, 104u8, 51u8, 50u8, 56u8, 57u8, 49u8, 55u8, 53u8, 55u8, 102u8, 100u8, 111u8, + 109u8, 97u8, 105u8, 110u8, 107u8, 115u8, 101u8, 114u8, 118u8, 105u8, 99u8, 101u8, + 46u8, 111u8, 114u8, 103u8, 103u8, 118u8, 101u8, 114u8, 115u8, 105u8, 111u8, 110u8, + 97u8, 49u8, 105u8, 114u8, 101u8, 115u8, 111u8, 117u8, 114u8, 99u8, 101u8, 115u8, + 130u8, 120u8, 53u8, 105u8, 112u8, 102u8, 115u8, 58u8, 47u8, 47u8, 81u8, 109u8, + 101u8, 55u8, 115u8, 115u8, 51u8, 65u8, 82u8, 86u8, 103u8, 120u8, 118u8, 54u8, + 114u8, 88u8, 113u8, 86u8, 80u8, 105u8, 105u8, 107u8, 77u8, 74u8, 56u8, 117u8, 50u8, + 78u8, 76u8, 103u8, 109u8, 103u8, 115u8, 122u8, 103u8, 49u8, 51u8, 112u8, 89u8, + 114u8, 68u8, 75u8, 69u8, 111u8, 105u8, 117u8, 120u8, 38u8, 104u8, 116u8, 116u8, + 112u8, 115u8, 58u8, 47u8, 47u8, 101u8, 120u8, 97u8, 109u8, 112u8, 108u8, 101u8, + 46u8, 99u8, 111u8, 109u8, 47u8, 109u8, 121u8, 45u8, 119u8, 101u8, 98u8, 50u8, 45u8, + 99u8, 108u8, 97u8, 105u8, 109u8, 46u8, 106u8, 115u8, 111u8, 110u8, 105u8, 115u8, + 116u8, 97u8, 116u8, 101u8, 109u8, 101u8, 110u8, 116u8, 120u8, 65u8, 73u8, 32u8, + 97u8, 99u8, 99u8, 101u8, 112u8, 116u8, 32u8, 116u8, 104u8, 101u8, 32u8, 83u8, + 101u8, 114u8, 118u8, 105u8, 99u8, 101u8, 79u8, 114u8, 103u8, 32u8, 84u8, 101u8, + 114u8, 109u8, 115u8, 32u8, 111u8, 102u8, 32u8, 83u8, 101u8, 114u8, 118u8, 105u8, + 99u8, 101u8, 58u8, 32u8, 104u8, 116u8, 116u8, 112u8, 115u8, 58u8, 47u8, 47u8, + 115u8, 101u8, 114u8, 118u8, 105u8, 99u8, 101u8, 46u8, 111u8, 114u8, 103u8, 47u8, + 116u8, 111u8, 115u8, 97u8, 115u8, 162u8, 97u8, 115u8, 152u8, 65u8, 16u8, 24u8, + 147u8, 19u8, 24u8, 231u8, 24u8, 82u8, 24u8, 93u8, 24u8, 234u8, 24u8, 85u8, 24u8, + 236u8, 24u8, 154u8, 24u8, 60u8, 24u8, 203u8, 24u8, 182u8, 24u8, 62u8, 24u8, 168u8, + 24u8, 214u8, 24u8, 132u8, 6u8, 24u8, 54u8, 24u8, 98u8, 24u8, 80u8, 24u8, 207u8, + 8u8, 24u8, 128u8, 24u8, 214u8, 24u8, 112u8, 24u8, 50u8, 24u8, 180u8, 24u8, 87u8, + 24u8, 171u8, 24u8, 51u8, 24u8, 201u8, 24u8, 38u8, 24u8, 198u8, 24u8, 127u8, 24u8, + 243u8, 24u8, 252u8, 24u8, 198u8, 24u8, 106u8, 24u8, 195u8, 24u8, 27u8, 24u8, 170u8, + 24u8, 104u8, 24u8, 104u8, 24u8, 168u8, 10u8, 18u8, 24u8, 251u8, 24u8, 230u8, 24u8, + 183u8, 24u8, 99u8, 24u8, 138u8, 24u8, 137u8, 24u8, 244u8, 24u8, 246u8, 24u8, 213u8, + 24u8, 26u8, 2u8, 24u8, 41u8, 24u8, 89u8, 12u8, 24u8, 246u8, 24u8, 103u8, 24u8, + 111u8, 24u8, 28u8, 97u8, 116u8, 102u8, 101u8, 105u8, 112u8, 49u8, 57u8, 49u8, + ]), + ) + .unwrap(); + + let ipld = ipld!({ + "h": { + "t": "eip4361", + }, + "p": { + "aud": "did:key:z6MkrBdNdwUPnXDVD1DCxedzVVBpaGi8aSmoXFAeKNgtAer8", + "domain": "service.org", + "iat": "2021-09-30T16:25:24.000Z", + "iss": "did:pkh:eip155:1:0xBd9D9c7DC389715a89fC8149E4a5Be91336B2796", + "nonce": "32891757", + "resources": [ + "ipfs://Qme7ss3ARVgxv6rXqVPiikMJ8u2NLgmgszg13pYrDKEoiu", + "https://example.com/my-web2-claim.json", + ], + "statement": "I accept the ServiceOrg Terms of Service: https://service.org/tos", + "version": "1", + }, + "s": { + "s": [0x10, 0x93, 0x13, 0xe7, 0x52, 0x5d, 0xea, 0x55, 0xec, 0x9a, 0x3c, 0xcb, 0xb6, 0x3e, 0xa8, 0xd6, 0x84, 0x06, 0x36, 0x62, 0x50, 0xcf, 0x08, 0x80, 0xd6, 0x70, 0x32, 0xb4, 0x57, 0xab, 0x33, 0xc9, 0x26, 0xc6, 0x7f, 0xf3, 0xfc, 0xc6, 0x6a, 0xc3, 0x1b, 0xaa, 0x68, 0x68, 0xa8, 0x0a, 0x12, 0xfb, 0xe6, 0xb7, 0x63, 0x8a, 0x89, 0xf4, 0xf6, 0xd5, 0x1a, 0x02, 0x29, 0x59, 0x0c, 0xf6, 0x67, 0x6f, 0x1c], + "t": "eip191", + }, + }); + assert_roundtrip(DagCborCodec, &cacao, &ipld); + } +} diff --git a/src/siwe_cacao.rs b/src/v1/siwe_cacao.rs similarity index 100% rename from src/siwe_cacao.rs rename to src/v1/siwe_cacao.rs From 6164e1eaece9ca2d1593b84afd8e55c812f5dfbb Mon Sep 17 00:00:00 2001 From: chunningham Date: Wed, 15 Feb 2023 16:44:28 +0100 Subject: [PATCH 02/70] scaffold v2 mod, add multidid and varsig subcrates --- Cargo.toml | 10 ++++--- multidid/Cargo.toml | 8 ++++++ multidid/src/lib.rs | 12 +++++++++ src/lib.rs | 1 + src/v2/mod.rs | 48 ++++++++++++++++++++++++++++++++++ src/v2/recap_cacao.rs | 28 ++++++++++++++++++++ src/v2/version.rs | 25 ++++++++++++++++++ varsig/Cargo.toml | 8 ++++++ varsig/src/lib.rs | 61 +++++++++++++++++++++++++++++++++++++++++++ 9 files changed, 198 insertions(+), 3 deletions(-) create mode 100644 multidid/Cargo.toml create mode 100644 multidid/src/lib.rs create mode 100644 src/v2/mod.rs create mode 100644 src/v2/recap_cacao.rs create mode 100644 src/v2/version.rs create mode 100644 varsig/Cargo.toml create mode 100644 varsig/src/lib.rs diff --git a/Cargo.toml b/Cargo.toml index feec1bb..dfd0308 100755 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,6 +7,8 @@ license = "Apache-2.0" description = "Core library for CACAO traits and data structures" repository = "https://github.com/spruceid/cacao-rs/" +[workspace] +members = ["varsig", "multidid"] # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [features] @@ -19,12 +21,14 @@ iri-string = { version = "0.6", features = ["serde"] } thiserror = "1.0" url = "2.2" async-trait = "0.1" -serde = "1.0" -libipld = { version = "0.14", default-features = false, features = ["dag-cbor", "derive"]} -serde_with = "2.0" +serde = "1" +serde_json = "1" +libipld = { version = "0.16", default-features = false, features = ["derive", "serde-codec"]} +serde_with = "2" time = { version = "0.3", features = ["parsing", "formatting"] } http = "0.2.5" hex = { version = "0.4", optional = true } +leb128 = "0.2" [dev-dependencies] async-std = { version = "1.10", features = ["attributes"] } diff --git a/multidid/Cargo.toml b/multidid/Cargo.toml new file mode 100644 index 0000000..c1b7a54 --- /dev/null +++ b/multidid/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "multidid" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/multidid/src/lib.rs b/multidid/src/lib.rs new file mode 100644 index 0000000..805be7f --- /dev/null +++ b/multidid/src/lib.rs @@ -0,0 +1,12 @@ +use serde::{Deserialize, Serialize}; + +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +pub struct MultiDID(String); + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn it_works() {} +} diff --git a/src/lib.rs b/src/lib.rs index d3d7902..dc106f2 100755 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,3 +1,4 @@ pub use siwe; pub mod v1; +pub mod v2; diff --git a/src/v2/mod.rs b/src/v2/mod.rs new file mode 100644 index 0000000..0dda037 --- /dev/null +++ b/src/v2/mod.rs @@ -0,0 +1,48 @@ +use std::fmt::Debug; + +use async_trait::async_trait; +use iri_string::types::UriString; +use libipld::cid::Cid; +use serde::{Deserialize, Serialize}; +use serde_json::Value; +use serde_with::{serde_as, DeserializeFromStr, SerializeDisplay}; +use std::collections::BTreeMap; + +pub mod multidid; +pub mod recap_cacao; +pub mod varsig; +pub mod version; + +use multidid::MultiDID; +use varsig::VarSig; + +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +pub struct CACAO, NB = Value> { + #[serde(rename = "iss")] + issuer: MultiDID, + #[serde(rename = "aud")] + audience: MultiDID, + #[serde(rename = "s")] + signature: VarSig, + #[serde(rename = "v")] + version: String, + #[serde(rename = "att")] + attenuations: BTreeMap>>>, + #[serde(rename = "nnc")] + nonce: String, + #[serde(rename = "prf", skip_serializing_if = "Vec::is_empty", default)] + proof: Vec, + #[serde(rename = "iat")] + issued_at: Option, + #[serde(rename = "nbf")] + not_before: Option, + #[serde(rename = "exp")] + expiration: Option, + #[serde(rename = "fct")] + facts: F, +} + +#[cfg(test)] +pub mod tests { + use super::*; +} diff --git a/src/v2/recap_cacao.rs b/src/v2/recap_cacao.rs new file mode 100644 index 0000000..714e16e --- /dev/null +++ b/src/v2/recap_cacao.rs @@ -0,0 +1,28 @@ +use super::CACAO; +use http::uri::Authority; +use iri_string::types::UriString; +use serde::{Deserialize, Serialize}; +use serde_json::Value; +pub use siwe; +use siwe::{eip55, Message, TimeStamp, VerificationError as SVE, Version as SVersion}; +use std::fmt::Debug; +use std::io::{Read, Seek, Write}; +use thiserror::Error; +use time::OffsetDateTime; + +pub type RecapCacao = CACAO; + +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +pub struct RecapFacts { + #[serde(rename = "iat-z")] + iat_time_zone: Option, + #[serde(rename = "nbf-z")] + nbf_time_zone: Option, + #[serde(rename = "nbf-z")] + exp_time_zone: Option, + domain: Authority, + statement: Option, + #[serde(rename = "request-id")] + request_id: Option, + resources: Vec, +} diff --git a/src/v2/version.rs b/src/v2/version.rs new file mode 100644 index 0000000..d488267 --- /dev/null +++ b/src/v2/version.rs @@ -0,0 +1,25 @@ +use serde_with::{DeserializeFromStr, SerializeDisplay}; +use thiserror::Error; + +#[derive(Debug, Clone, PartialEq, SerializeDisplay, DeserializeFromStr)] +pub struct Version2; + +impl std::fmt::Display for Version2 { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> { + write!(f, "2") + } +} + +#[derive(Error, Debug, Copy, PartialEq)] +pub struct VersionErr; + +impl std::str::FromStr for Version2 { + type Err = VersionErr; + fn from_str(s: &str) -> Result { + if s == "2" { + Ok(Self) + } else { + Err(VersionErr) + } + } +} diff --git a/varsig/Cargo.toml b/varsig/Cargo.toml new file mode 100644 index 0000000..48e9582 --- /dev/null +++ b/varsig/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "varsig" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/varsig/src/lib.rs b/varsig/src/lib.rs new file mode 100644 index 0000000..0b40e7c --- /dev/null +++ b/varsig/src/lib.rs @@ -0,0 +1,61 @@ +use leb128::read::Error as Leb128Error; +use std::io::{Error as IoError, Read, Write}; + +const VARSIG_VARINT_PREFIX: u8 = 0x68; + +#[derive(Debug, Clone, PartialEq)] +pub struct VarSig { + codec: u64, + hash: u64, + key_type: u64, + signature: Vec, +} + +impl VarSig { + pub fn new(codec: u64, hash: u64, key_type: u64, signature: Vec) -> Self { + Self { + codec, + hash, + key_type, + signature, + } + } + + pub fn codec(&self) -> u64 { + self.codec + } + + pub fn hash(&self) -> u64 { + self.hash + } + + pub fn key_type(&self) -> u64 { + self.key_type + } + + pub fn signature(&self) -> &[u8] { + &self.signature + } + + pub fn from_reader(mut reader: &R) -> Result + where + R: ?Sized + Read, + { + todo!() + } + + pub fn to_writer(&self, mut writer: &W) -> Result + where + W: ?Sized + Write, + { + todo!() + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn it_works() {} +} From 6547936b2d945a5416ae575444ef2751ba9fc3de Mon Sep 17 00:00:00 2001 From: chunningham Date: Fri, 17 Feb 2023 15:54:24 +0100 Subject: [PATCH 03/70] impl varsig parsing, not exactly correct --- Cargo.toml | 1 - varsig/Cargo.toml | 2 + varsig/src/lib.rs | 98 +++++++++++++++++++++++++++++++++++++++++++---- 3 files changed, 93 insertions(+), 8 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index dfd0308..d5df4fb 100755 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,7 +28,6 @@ serde_with = "2" time = { version = "0.3", features = ["parsing", "formatting"] } http = "0.2.5" hex = { version = "0.4", optional = true } -leb128 = "0.2" [dev-dependencies] async-std = { version = "1.10", features = ["attributes"] } diff --git a/varsig/Cargo.toml b/varsig/Cargo.toml index 48e9582..4b4323d 100644 --- a/varsig/Cargo.toml +++ b/varsig/Cargo.toml @@ -6,3 +6,5 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +unsigned-varint = { version = "0.7", features = ["std"] } +thiserror = "1" diff --git a/varsig/src/lib.rs b/varsig/src/lib.rs index 0b40e7c..f78d0f2 100644 --- a/varsig/src/lib.rs +++ b/varsig/src/lib.rs @@ -1,5 +1,8 @@ -use leb128::read::Error as Leb128Error; use std::io::{Error as IoError, Read, Write}; +use unsigned_varint::{ + encode::{u64 as write_u64, u64_buffer}, + io::read_u64, +}; const VARSIG_VARINT_PREFIX: u8 = 0x68; @@ -11,6 +14,16 @@ pub struct VarSig { signature: Vec, } +#[derive(thiserror::Error, Debug)] +pub enum Error { + #[error(transparent)] + Varint(#[from] unsigned_varint::io::ReadError), + #[error(transparent)] + Io(#[from] std::io::Error), + #[error("Invalid varsig prefix, expected 0x68, recieved {0:x}")] + InvalidPrefix(u8), +} + impl VarSig { pub fn new(codec: u64, hash: u64, key_type: u64, signature: Vec) -> Self { Self { @@ -37,18 +50,56 @@ impl VarSig { &self.signature } - pub fn from_reader(mut reader: &R) -> Result + pub fn to_vec(&self) -> Result, IoError> { + let mut buf = Vec::new(); + self.to_writer(&mut buf)?; + Ok(buf) + } + + pub fn from_bytes(bytes: &[u8]) -> Result { + Self::from_reader(&mut bytes.as_ref()) + } + + pub fn from_reader(reader: &mut R) -> Result where - R: ?Sized + Read, + R: Read, { - todo!() + let mut tag = [0u8; 1]; + reader.read_exact(&mut tag)?; + + if tag[0] != VARSIG_VARINT_PREFIX { + return Err(Error::InvalidPrefix(tag[0])); + }; + + println!("tag: {:#x?}", tag); + let codec = read_u64(reader.by_ref())?; + println!("codec: {:#x?}", codec); + let hash = read_u64(reader.by_ref())?; + println!("hash: {:#x?}", hash); + let key_type = read_u64(reader.by_ref())?; + println!("key_type: {:#x?}", key_type); + + let sig_len = read_u64(reader.by_ref())?; + println!("sig_len: {:#x?}", sig_len); + let mut signature = vec![0; sig_len as usize]; + reader.read_exact(&mut signature)?; + println!("signature: {:#x?}", signature); + + Ok(Self::new(codec, hash, key_type, signature)) } - pub fn to_writer(&self, mut writer: &W) -> Result + pub fn to_writer(&self, writer: &mut W) -> Result<(), IoError> where W: ?Sized + Write, { - todo!() + writer.write(&[VARSIG_VARINT_PREFIX; 1])?; + let mut buf = u64_buffer(); + writer.write(write_u64(self.codec, &mut buf))?; + writer.write(write_u64(self.hash, &mut buf))?; + writer.write(write_u64(self.key_type, &mut buf))?; + writer.write(write_u64(self.signature.len() as u64, &mut buf))?; + writer.write(&self.signature)?; + Ok(()) } } @@ -56,6 +107,39 @@ impl VarSig { mod tests { use super::*; + const EXAMPLE: [u8; 15] = [ + 0x68, 0xd2, 0x04, 0xda, 0x03, 0x24, 0xde, 0xb7, 0xde, 0x9a, 0xf1, 0xd9, 0xa2, 0xa3, 0x02, + ]; + #[test] - fn it_works() {} + fn basic_roundtrip() { + let varsig = VarSig::from_reader(&mut EXAMPLE.as_ref()).unwrap(); + assert_eq!(varsig.codec(), 0x04d2); + assert_eq!(varsig.hash(), 0x03da); + assert_eq!(varsig.key_type(), 0x24); + assert_eq!(varsig.signature(), &EXAMPLE[6..]); + + assert_eq!(&varsig.to_vec().unwrap(), &EXAMPLE); + } + + #[test] + fn reverse_roundtrip() { + let varsig = VarSig::new(0x0129, 0x12, 0xed, EXAMPLE[6..].to_vec()); + let encoded = varsig.to_vec().unwrap(); + let decoded = VarSig::from_bytes(&encoded.as_ref()).unwrap(); + assert_eq!(varsig, decoded); + } + + #[test] + fn basic_ser() { + let varsig = VarSig::new(0x0129, 0x12, 0xed, EXAMPLE[6..].to_vec()); + assert_eq!(&varsig.to_vec().unwrap(), &EXAMPLE); + } + + #[test] + fn rsad() { + let b = [0xd2, 0x04]; + println!("{:#x?}", read_u64(&mut b.as_ref())); + None::.unwrap(); + } } From 45d203eeaf3dc2a2492235455a34080b0b571977 Mon Sep 17 00:00:00 2001 From: chunningham Date: Fri, 17 Feb 2023 15:54:42 +0100 Subject: [PATCH 04/70] impl multidid further, not fully correct --- multidid/Cargo.toml | 4 + multidid/src/lib.rs | 297 +++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 299 insertions(+), 2 deletions(-) diff --git a/multidid/Cargo.toml b/multidid/Cargo.toml index c1b7a54..0a8acba 100644 --- a/multidid/Cargo.toml +++ b/multidid/Cargo.toml @@ -6,3 +6,7 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +serde = "1" +iri-string = { version = "0.6", features = ["serde"] } +unsigned-varint = { version = "0.7", features = ["std"] } +thiserror = "1" diff --git a/multidid/src/lib.rs b/multidid/src/lib.rs index 805be7f..6dab01e 100644 --- a/multidid/src/lib.rs +++ b/multidid/src/lib.rs @@ -1,7 +1,300 @@ +use iri_string::types::{UriFragmentString, UriQueryString, UriRelativeString}; use serde::{Deserialize, Serialize}; +use std::io::{Error as IoError, Read, Write}; +use unsigned_varint::{ + encode::{u64 as write_u64, u64_buffer}, + io::read_u64, +}; -#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] -pub struct MultiDID(String); +const RAW_CODEC: u64 = 0x55; +const PKH_CODEC: u64 = 0xca; +const SECP256K1_CODEC: u64 = 0xe7; +const BLS12_381_G1_CODEC: u64 = 0xea; +const BLS12_381_G2_CODEC: u64 = 0xeb; +const X25519_CODEC: u64 = 0xec; +const ED25519_CODEC: u64 = 0xed; +const P256_CODEC: u64 = 0x1200; +const P384_CODEC: u64 = 0x1201; +const P521_CODEC: u64 = 0x1202; +// const RSA_CODEC: u64 = 0x1205; + +const MULTIDID_VARINT_TAG: u16 = 0x9d1a; + +#[derive(Debug, Clone, PartialEq)] +pub struct MultiDid { + method: Method, + fragment: Option, + query: Option, +} + +impl MultiDid { + pub fn new( + method: Method, + fragment: Option, + query: Option, + ) -> Self { + Self { + method, + fragment, + query, + } + } + + pub fn method(&self) -> &Method { + &self.method + } + + pub fn fragment(&self) -> Option<&UriFragmentString> { + self.fragment.as_ref() + } + + pub fn query(&self) -> Option<&UriQueryString> { + self.query.as_ref() + } + + pub fn to_vec(&self) -> Result, Error> { + let mut buf = Vec::new(); + self.to_writer(&mut buf)?; + Ok(buf) + } + + pub fn from_bytes(b: &[u8]) -> Result { + Self::from_reader(&mut b.as_ref()) + } + + pub fn from_reader(reader: &mut R) -> Result { + let mut tag = [0u8; 2]; + reader.read_exact(&mut tag)?; + let tag = u16::from_be_bytes(tag); + + if tag != MULTIDID_VARINT_TAG { + return Err(Error::InvalidPrefix(tag as u64)); + } + + let method = Method::from_reader(reader)?; + + let param_len = read_u64(reader.by_ref())?; + + let (fragment, query) = if param_len > 0 { + let mut param_buf = vec![0; param_len as usize]; + reader.read_exact(&mut param_buf)?; + let r = UriRelativeString::try_from(param_buf.as_slice())?; + ( + r.fragment().map(|f| f.to_owned()), + r.query().map(|q| q.to_owned()), + ) + } else { + (None, None) + }; + + Ok(Self::new(method, fragment, query)) + } + + pub fn to_writer(&self, writer: &mut W) -> Result<(), Error> { + self.method.to_writer(writer)?; + let mut buf = u64_buffer(); + match (&self.fragment, &self.query) { + (Some(fragment), Some(query)) => { + writer.write(write_u64( + (fragment.as_str().len() + query.as_str().len() + 2) as u64, + &mut buf, + ))?; + writer.write_all(b"#")?; + writer.write_all(fragment.as_str().as_bytes())?; + writer.write_all(b"?")?; + writer.write_all(query.as_str().as_bytes())?; + } + (Some(fragment), None) => { + writer.write(write_u64((fragment.as_str().len() + 1) as u64, &mut buf))?; + writer.write_all(b"#")?; + writer.write_all(fragment.as_str().as_bytes())?; + } + (None, Some(query)) => { + writer.write(write_u64((query.as_str().len() + 1) as u64, &mut buf))?; + writer.write_all(b"?")?; + writer.write_all(query.as_str().as_bytes())?; + } + (None, None) => { + writer.write(write_u64(0, &mut buf))?; + } + }; + Ok(()) + } +} + +#[derive(Debug, Clone, PartialEq)] +pub enum Method { + Pkh(Vec), + Key(DidKeyTypes), + Raw(String), +} + +#[derive(Debug, thiserror::Error)] +pub enum Error { + #[error(transparent)] + Varint(#[from] unsigned_varint::io::ReadError), + #[error(transparent)] + Io(#[from] IoError), + #[error("Invalid multidid varint prefix, expected 0x9d1a, recieved {0:x}")] + InvalidPrefix(u64), + #[error(transparent)] + Parameter(#[from] iri_string::validate::Error), + #[error(transparent)] + DidParse(#[from] std::string::FromUtf8Error), +} + +impl Method { + pub fn codec(&self) -> u64 { + use Method::*; + match self { + Raw(_) => RAW_CODEC, + Pkh(_) => PKH_CODEC, + Key(k) => k.codec(), + } + } + + fn from_reader(reader: &mut R) -> Result { + let codec = read_u64(reader.by_ref())?; + match codec { + RAW_CODEC => { + let len = read_u64(reader.by_ref())?; + let mut buf = vec![0; len as usize]; + reader.read_exact(&mut buf)?; + Ok(Self::Raw(String::from_utf8(buf)?)) + } + PKH_CODEC => { + let len = read_u64(reader.by_ref())?; + let mut buf = vec![0; len as usize]; + reader.read_exact(&mut buf)?; + Ok(Self::Pkh(buf)) + } + _ => { + let key = DidKeyTypes::from_reader(reader)?; + Ok(Self::Key(key)) + } + } + } + + fn to_writer(&self, writer: &mut W) -> Result<(), IoError> { + let mut vi_buf = u64_buffer(); + writer.write(write_u64(self.codec(), &mut vi_buf))?; + match self { + Self::Raw(buf) => { + writer.write(write_u64(buf.as_bytes().len() as u64, &mut vi_buf))?; + writer.write_all(buf.as_bytes())?; + } + Self::Pkh(buf) => { + writer.write(write_u64(buf.len() as u64, &mut vi_buf))?; + writer.write_all(buf)?; + } + Self::Key(key) => { + key.to_writer(writer)?; + } + } + Ok(()) + } +} + +#[derive(Debug, Clone, PartialEq)] +pub enum DidKeyTypes { + Secp256k1([u8; 33]), + Bls12_381G1([u8; 64]), + Bls12_381G2([u8; 96]), + X25519([u8; 32]), + Ed25519([u8; 32]), + P256([u8; 33]), + P384([u8; 49]), + P521([u8; 67]), + // RSA([u8; ??]), +} + +impl DidKeyTypes { + pub fn codec(&self) -> u64 { + use DidKeyTypes::*; + match self { + Secp256k1(_) => SECP256K1_CODEC, + Bls12_381G1(_) => BLS12_381_G1_CODEC, + Bls12_381G2(_) => BLS12_381_G2_CODEC, + X25519(_) => X25519_CODEC, + Ed25519(_) => ED25519_CODEC, + P256(_) => P256_CODEC, + P384(_) => P384_CODEC, + P521(_) => P521_CODEC, + } + } + + fn from_reader(reader: &mut R) -> Result + where + R: Read, + { + let codec = read_u64(reader.by_ref())?; + match codec { + SECP256K1_CODEC => { + let mut buf = [0; 33]; + reader.read_exact(&mut buf)?; + Ok(Self::Secp256k1(buf)) + } + BLS12_381_G1_CODEC => { + let mut buf = [0; 64]; + reader.read_exact(&mut buf)?; + Ok(Self::Bls12_381G1(buf)) + } + BLS12_381_G2_CODEC => { + let mut buf = [0; 96]; + reader.read_exact(&mut buf)?; + Ok(Self::Bls12_381G2(buf)) + } + X25519_CODEC => { + let mut buf = [0; 32]; + reader.read_exact(&mut buf)?; + Ok(Self::X25519(buf)) + } + ED25519_CODEC => { + let mut buf = [0; 32]; + reader.read_exact(&mut buf)?; + Ok(Self::Ed25519(buf)) + } + P256_CODEC => { + let mut buf = [0; 33]; + reader.read_exact(&mut buf)?; + Ok(Self::P256(buf)) + } + P384_CODEC => { + let mut buf = [0; 49]; + reader.read_exact(&mut buf)?; + Ok(Self::P384(buf)) + } + P521_CODEC => { + let mut buf = [0; 67]; + reader.read_exact(&mut buf)?; + Ok(Self::P521(buf)) + } + _ => Err(Error::InvalidPrefix(codec)), + } + } + + fn to_writer(&self, writer: &mut W) -> Result<(), IoError> + where + W: ?Sized + Write, + { + let mut buf = u64_buffer(); + writer.write(write_u64(self.codec(), &mut buf))?; + writer.write_all(&self.bytes()) + } + + fn bytes(&self) -> &[u8] { + match self { + Self::Secp256k1(key) => key, + Self::Bls12_381G1(key) => key, + Self::Bls12_381G2(key) => key, + Self::X25519(key) => key, + Self::Ed25519(key) => key, + Self::P256(key) => key, + Self::P384(key) => key, + Self::P521(key) => key, + } + } +} #[cfg(test)] mod tests { From ff4aff7bf724b65efa3340803c05098af6dd22c4 Mon Sep 17 00:00:00 2001 From: chunningham Date: Fri, 3 Mar 2023 16:21:25 +0100 Subject: [PATCH 05/70] fix multidid parsing and writing --- multidid/src/lib.rs | 89 ++++++++++++++++++++++++++++++--------------- 1 file changed, 60 insertions(+), 29 deletions(-) diff --git a/multidid/src/lib.rs b/multidid/src/lib.rs index 6dab01e..4ab0474 100644 --- a/multidid/src/lib.rs +++ b/multidid/src/lib.rs @@ -1,4 +1,4 @@ -use iri_string::types::{UriFragmentString, UriQueryString, UriRelativeString}; +use iri_string::types::{UriFragmentString, UriQueryString, UriReferenceString, UriRelativeString}; use serde::{Deserialize, Serialize}; use std::io::{Error as IoError, Read, Write}; use unsigned_varint::{ @@ -73,50 +73,69 @@ impl MultiDid { let method = Method::from_reader(reader)?; - let param_len = read_u64(reader.by_ref())?; - - let (fragment, query) = if param_len > 0 { - let mut param_buf = vec![0; param_len as usize]; - reader.read_exact(&mut param_buf)?; - let r = UriRelativeString::try_from(param_buf.as_slice())?; - ( - r.fragment().map(|f| f.to_owned()), - r.query().map(|q| q.to_owned()), - ) - } else { - (None, None) - }; + Ok(match method { + Method::Raw(raw) => { + let r = UriReferenceString::try_from(raw.as_bytes())?; + Self::new( + Method::Raw(r.path_str().to_string()), + r.fragment().map(|f| f.to_owned()), + r.query().map(|q| q.to_owned()), + ) + } + Method::Pkh(_) | Method::Key(_) => { + let param_len = read_u64(reader.by_ref())?; - Ok(Self::new(method, fragment, query)) + let (fragment, query) = if param_len > 0 { + let mut param_buf = vec![0; param_len as usize]; + reader.read_exact(&mut param_buf)?; + let r = UriRelativeString::try_from(param_buf.as_slice())?; + ( + r.fragment().map(|f| f.to_owned()), + r.query().map(|q| q.to_owned()), + ) + } else { + (None, None) + }; + Self::new(method, fragment, query) + } + }) } pub fn to_writer(&self, writer: &mut W) -> Result<(), Error> { self.method.to_writer(writer)?; - let mut buf = u64_buffer(); + match self.method { + Method::Pkh(_) | Method::Key(_) => { + let mut buf = u64_buffer(); + let len: u64 = (self + .fragment + .as_ref() + .map(|f| f.as_str().len() + 1) + .unwrap_or(0) + + self + .query + .as_ref() + .map(|q| q.as_str().len() + 1) + .unwrap_or(0)) as u64; + writer.write_all(write_u64(len, &mut buf))?; + } + _ => {} + }; match (&self.fragment, &self.query) { (Some(fragment), Some(query)) => { - writer.write(write_u64( - (fragment.as_str().len() + query.as_str().len() + 2) as u64, - &mut buf, - ))?; writer.write_all(b"#")?; writer.write_all(fragment.as_str().as_bytes())?; writer.write_all(b"?")?; writer.write_all(query.as_str().as_bytes())?; } (Some(fragment), None) => { - writer.write(write_u64((fragment.as_str().len() + 1) as u64, &mut buf))?; writer.write_all(b"#")?; writer.write_all(fragment.as_str().as_bytes())?; } (None, Some(query)) => { - writer.write(write_u64((query.as_str().len() + 1) as u64, &mut buf))?; writer.write_all(b"?")?; writer.write_all(query.as_str().as_bytes())?; } - (None, None) => { - writer.write(write_u64(0, &mut buf))?; - } + (None, None) => {} }; Ok(()) } @@ -180,11 +199,11 @@ impl Method { writer.write(write_u64(self.codec(), &mut vi_buf))?; match self { Self::Raw(buf) => { - writer.write(write_u64(buf.as_bytes().len() as u64, &mut vi_buf))?; + writer.write_all(write_u64(buf.as_bytes().len() as u64, &mut vi_buf))?; writer.write_all(buf.as_bytes())?; } Self::Pkh(buf) => { - writer.write(write_u64(buf.len() as u64, &mut vi_buf))?; + writer.write_all(write_u64(buf.len() as u64, &mut vi_buf))?; writer.write_all(buf)?; } Self::Key(key) => { @@ -278,7 +297,7 @@ impl DidKeyTypes { W: ?Sized + Write, { let mut buf = u64_buffer(); - writer.write(write_u64(self.codec(), &mut buf))?; + writer.write_all(write_u64(self.codec(), &mut buf))?; writer.write_all(&self.bytes()) } @@ -300,6 +319,18 @@ impl DidKeyTypes { mod tests { use super::*; + const VALID_TESTS: [(&str, &str); 1] = [( + r"9d1a550e6578616d706c653a313233343536", + r"did:example:123456", + )]; + + const VALID: [u8; 18] = [ + 0x9d, 0x1a, 0x55, 0x0e, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x3a, 0x31, 0x32, 0x33, + 0x34, 0x35, 0x36, + ]; + #[test] - fn it_works() {} + fn it_works() { + let did = MultiDid::from_reader(&mut &VALID[..]).unwrap(); + } } From f7f0849642d2fcb9caa538cd0afb2443042df5d9 Mon Sep 17 00:00:00 2001 From: chunningham Date: Fri, 3 Mar 2023 17:33:20 +0100 Subject: [PATCH 06/70] fix multidid codec writing for raw and key --- multidid/Cargo.toml | 6 +++- multidid/src/lib.rs | 73 +++++++++++++++++++++++++++------------ multidid/tests/valid.json | 23 ++++++++++++ 3 files changed, 78 insertions(+), 24 deletions(-) create mode 100644 multidid/tests/valid.json diff --git a/multidid/Cargo.toml b/multidid/Cargo.toml index 0a8acba..2db09f0 100644 --- a/multidid/Cargo.toml +++ b/multidid/Cargo.toml @@ -6,7 +6,11 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -serde = "1" iri-string = { version = "0.6", features = ["serde"] } unsigned-varint = { version = "0.7", features = ["std"] } thiserror = "1" + +[dev-dependencies] +serde_json = "1" +serde = "1" +serde_with = { version = "2", features = ["hex"] } diff --git a/multidid/src/lib.rs b/multidid/src/lib.rs index 4ab0474..472b586 100644 --- a/multidid/src/lib.rs +++ b/multidid/src/lib.rs @@ -1,5 +1,4 @@ use iri_string::types::{UriFragmentString, UriQueryString, UriReferenceString, UriRelativeString}; -use serde::{Deserialize, Serialize}; use std::io::{Error as IoError, Read, Write}; use unsigned_varint::{ encode::{u64 as write_u64, u64_buffer}, @@ -77,7 +76,10 @@ impl MultiDid { Method::Raw(raw) => { let r = UriReferenceString::try_from(raw.as_bytes())?; Self::new( - Method::Raw(r.path_str().to_string()), + Method::Raw(match r.scheme_str() { + Some(s) => format!("{}:{}", s, r.path_str()), + None => r.path_str().to_string(), + }), r.fragment().map(|f| f.to_owned()), r.query().map(|q| q.to_owned()), ) @@ -102,10 +104,14 @@ impl MultiDid { } pub fn to_writer(&self, writer: &mut W) -> Result<(), Error> { - self.method.to_writer(writer)?; + writer.write_all(&MULTIDID_VARINT_TAG.to_be_bytes())?; + // write codec + let mut buf = u64_buffer(); + writer.write_all(write_u64(self.method.codec(), &mut buf))?; + match self.method { Method::Pkh(_) | Method::Key(_) => { - let mut buf = u64_buffer(); + self.method.to_writer(writer)?; let len: u64 = (self .fragment .as_ref() @@ -118,7 +124,21 @@ impl MultiDid { .unwrap_or(0)) as u64; writer.write_all(write_u64(len, &mut buf))?; } - _ => {} + Method::Raw(ref raw) => { + let len: u64 = (self + .fragment + .as_ref() + .map(|f| f.as_str().len() + 1) + .unwrap_or(0) + + self + .query + .as_ref() + .map(|q| q.as_str().len() + 1) + .unwrap_or(0) + + raw.len()) as u64; + writer.write_all(write_u64(len, &mut buf))?; + writer.write_all(raw.as_bytes())?; + } }; match (&self.fragment, &self.query) { (Some(fragment), Some(query)) => { @@ -187,22 +207,20 @@ impl Method { reader.read_exact(&mut buf)?; Ok(Self::Pkh(buf)) } - _ => { - let key = DidKeyTypes::from_reader(reader)?; + codec => { + let key = DidKeyTypes::from_reader(reader, codec)?; Ok(Self::Key(key)) } } } fn to_writer(&self, writer: &mut W) -> Result<(), IoError> { - let mut vi_buf = u64_buffer(); - writer.write(write_u64(self.codec(), &mut vi_buf))?; match self { Self::Raw(buf) => { - writer.write_all(write_u64(buf.as_bytes().len() as u64, &mut vi_buf))?; writer.write_all(buf.as_bytes())?; } Self::Pkh(buf) => { + let mut vi_buf = u64_buffer(); writer.write_all(write_u64(buf.len() as u64, &mut vi_buf))?; writer.write_all(buf)?; } @@ -242,11 +260,10 @@ impl DidKeyTypes { } } - fn from_reader(reader: &mut R) -> Result + fn from_reader(reader: &mut R, codec: u64) -> Result where R: Read, { - let codec = read_u64(reader.by_ref())?; match codec { SECP256K1_CODEC => { let mut buf = [0; 33]; @@ -296,8 +313,6 @@ impl DidKeyTypes { where W: ?Sized + Write, { - let mut buf = u64_buffer(); - writer.write_all(write_u64(self.codec(), &mut buf))?; writer.write_all(&self.bytes()) } @@ -318,19 +333,31 @@ impl DidKeyTypes { #[cfg(test)] mod tests { use super::*; + use serde::{Deserialize, Serialize}; + use serde_with::{hex::Hex, serde_as}; - const VALID_TESTS: [(&str, &str); 1] = [( - r"9d1a550e6578616d706c653a313233343536", - r"did:example:123456", - )]; + #[serde_as] + #[derive(Serialize, Deserialize)] + struct ValidTest { + #[serde_as(as = "Hex")] + encoded: Vec, + decoded: String, + method: String, + query: Option, + fragment: Option, + } - const VALID: [u8; 18] = [ - 0x9d, 0x1a, 0x55, 0x0e, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x3a, 0x31, 0x32, 0x33, - 0x34, 0x35, 0x36, - ]; + const VALID_JSON: &str = include_str!("../tests/valid.json"); #[test] fn it_works() { - let did = MultiDid::from_reader(&mut &VALID[..]).unwrap(); + let valid: Vec = serde_json::from_str(VALID_JSON).unwrap(); + for test in valid { + println!("{:?}", test.decoded); + let did = MultiDid::from_reader(&mut test.encoded.as_slice()).unwrap(); + assert_eq!(did.query, test.query); + assert_eq!(did.fragment, test.fragment); + assert_eq!(did.to_vec().unwrap(), test.encoded); + } } } diff --git a/multidid/tests/valid.json b/multidid/tests/valid.json new file mode 100644 index 0000000..1512d2a --- /dev/null +++ b/multidid/tests/valid.json @@ -0,0 +1,23 @@ +[{ + "encoded": "9d1a550e6578616d706c653a313233343536", + "decoded": "did:example:123456", + "method": "raw" +}, { + "encoded": "9d1a551a6578616d706c653a3132333435363f76657273696f6e49643d31", + "decoded": "did:example:123456?versionId=1", + "method": "raw", + "query": "versionId=1" +}, { + "encoded": "9d1aed013b6a27bcceb6a42d62a3a8d02a6f0d73653215771de243a63ac048a18b59da2900", + "decoded": "did:key:z6MkiTBz1ymuepAQ4HEHYSF1H8quG5GLVVQR3djdX3mDooWp", + "method": "key" +}, { + "encoded": "9d1aed013b6a27bcceb6a42d62a3a8d02a6f0d73653215771de243a63ac048a18b59da2931237a364d6b6954427a31796d75657041513448454859534631483871754735474c5656515233646a6458336d446f6f5770", + "decoded": "did:key:z6MkiTBz1ymuepAQ4HEHYSF1H8quG5GLVVQR3djdX3mDooWp#z6MkiTBz1ymuepAQ4HEHYSF1H8quG5GLVVQR3djdX3mDooWp", + "method": "key", + "fragment": "z6MkiTBz1ymuepAQ4HEHYSF1H8quG5GLVVQR3djdX3mDooWp" +}, { + "encoded": "9d1ae70103874c15c7fda20e539c6e5ba573c139884c351188799f5458b4b41f7924f235cd00", + "decoded": "did:key:zQ3shokFTS3brHcDQrn82RUDfCZESWL1ZdCEJwekUDPQiYBme", + "method": "key" +}] From d69cd0bca2ced3acd2a0dd72d057287456f32aa2 Mon Sep 17 00:00:00 2001 From: chunningham Date: Fri, 3 Mar 2023 17:37:13 +0100 Subject: [PATCH 07/70] expand test --- multidid/src/lib.rs | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/multidid/src/lib.rs b/multidid/src/lib.rs index 472b586..fdc43fe 100644 --- a/multidid/src/lib.rs +++ b/multidid/src/lib.rs @@ -333,11 +333,11 @@ impl DidKeyTypes { #[cfg(test)] mod tests { use super::*; - use serde::{Deserialize, Serialize}; + use serde::Deserialize; use serde_with::{hex::Hex, serde_as}; #[serde_as] - #[derive(Serialize, Deserialize)] + #[derive(Deserialize)] struct ValidTest { #[serde_as(as = "Hex")] encoded: Vec, @@ -353,11 +353,16 @@ mod tests { fn it_works() { let valid: Vec = serde_json::from_str(VALID_JSON).unwrap(); for test in valid { - println!("{:?}", test.decoded); let did = MultiDid::from_reader(&mut test.encoded.as_slice()).unwrap(); assert_eq!(did.query, test.query); assert_eq!(did.fragment, test.fragment); assert_eq!(did.to_vec().unwrap(), test.encoded); + assert!(match (did.method(), test.method.as_str()) { + (Method::Key(_), "key") => true, + (Method::Pkh(_), "pkh") => true, + (Method::Raw(_), "raw") => true, + _ => false, + }); } } } From 664239b4cae6b4fa5ee3cd359989be1213014944 Mon Sep 17 00:00:00 2001 From: chunningham Date: Fri, 3 Mar 2023 17:59:22 +0100 Subject: [PATCH 08/70] break multidid into modules --- multidid/src/error.rs | 13 +++ multidid/src/key.rs | 110 ++++++++++++++++++++++++ multidid/src/lib.rs | 189 ++--------------------------------------- multidid/src/method.rs | 66 ++++++++++++++ 4 files changed, 197 insertions(+), 181 deletions(-) create mode 100644 multidid/src/error.rs create mode 100644 multidid/src/key.rs create mode 100644 multidid/src/method.rs diff --git a/multidid/src/error.rs b/multidid/src/error.rs new file mode 100644 index 0000000..d152115 --- /dev/null +++ b/multidid/src/error.rs @@ -0,0 +1,13 @@ +#[derive(Debug, thiserror::Error)] +pub enum Error { + #[error(transparent)] + Varint(#[from] unsigned_varint::io::ReadError), + #[error(transparent)] + Io(#[from] std::io::Error), + #[error("Invalid multidid varint prefix, expected 0x9d1a, recieved {0:x}")] + InvalidPrefix(u64), + #[error(transparent)] + Parameter(#[from] iri_string::validate::Error), + #[error(transparent)] + DidParse(#[from] std::string::FromUtf8Error), +} diff --git a/multidid/src/key.rs b/multidid/src/key.rs new file mode 100644 index 0000000..66301c7 --- /dev/null +++ b/multidid/src/key.rs @@ -0,0 +1,110 @@ +use crate::Error; +use std::io::{Error as IoError, Read, Write}; + +const SECP256K1_CODEC: u64 = 0xe7; +const BLS12_381_G1_CODEC: u64 = 0xea; +const BLS12_381_G2_CODEC: u64 = 0xeb; +const X25519_CODEC: u64 = 0xec; +const ED25519_CODEC: u64 = 0xed; +const P256_CODEC: u64 = 0x1200; +const P384_CODEC: u64 = 0x1201; +const P521_CODEC: u64 = 0x1202; +// const RSA_CODEC: u64 = 0x1205; + +#[derive(Debug, Clone, PartialEq)] +pub enum DidKeyTypes { + Secp256k1([u8; 33]), + Bls12_381G1([u8; 64]), + Bls12_381G2([u8; 96]), + X25519([u8; 32]), + Ed25519([u8; 32]), + P256([u8; 33]), + P384([u8; 49]), + P521([u8; 67]), + // RSA([u8; ??]), +} + +impl DidKeyTypes { + pub fn codec(&self) -> u64 { + use DidKeyTypes::*; + match self { + Secp256k1(_) => SECP256K1_CODEC, + Bls12_381G1(_) => BLS12_381_G1_CODEC, + Bls12_381G2(_) => BLS12_381_G2_CODEC, + X25519(_) => X25519_CODEC, + Ed25519(_) => ED25519_CODEC, + P256(_) => P256_CODEC, + P384(_) => P384_CODEC, + P521(_) => P521_CODEC, + } + } + + pub(crate) fn from_reader(reader: &mut R, codec: u64) -> Result + where + R: Read, + { + match codec { + SECP256K1_CODEC => { + let mut buf = [0; 33]; + reader.read_exact(&mut buf)?; + Ok(Self::Secp256k1(buf)) + } + BLS12_381_G1_CODEC => { + let mut buf = [0; 64]; + reader.read_exact(&mut buf)?; + Ok(Self::Bls12_381G1(buf)) + } + BLS12_381_G2_CODEC => { + let mut buf = [0; 96]; + reader.read_exact(&mut buf)?; + Ok(Self::Bls12_381G2(buf)) + } + X25519_CODEC => { + let mut buf = [0; 32]; + reader.read_exact(&mut buf)?; + Ok(Self::X25519(buf)) + } + ED25519_CODEC => { + let mut buf = [0; 32]; + reader.read_exact(&mut buf)?; + Ok(Self::Ed25519(buf)) + } + P256_CODEC => { + let mut buf = [0; 33]; + reader.read_exact(&mut buf)?; + Ok(Self::P256(buf)) + } + P384_CODEC => { + let mut buf = [0; 49]; + reader.read_exact(&mut buf)?; + Ok(Self::P384(buf)) + } + P521_CODEC => { + let mut buf = [0; 67]; + reader.read_exact(&mut buf)?; + Ok(Self::P521(buf)) + } + _ => Err(Error::InvalidPrefix(codec)), + } + } + + pub(crate) fn to_writer(&self, writer: &mut W) -> Result<(), IoError> + where + W: ?Sized + Write, + { + writer.write_all(&self.bytes()) + } + + pub fn bytes(&self) -> &[u8] { + match self { + Self::Secp256k1(key) => key, + Self::Bls12_381G1(key) => key, + Self::Bls12_381G2(key) => key, + Self::X25519(key) => key, + Self::Ed25519(key) => key, + Self::P256(key) => key, + Self::P384(key) => key, + Self::P521(key) => key, + } + } +} diff --git a/multidid/src/lib.rs b/multidid/src/lib.rs index fdc43fe..4971896 100644 --- a/multidid/src/lib.rs +++ b/multidid/src/lib.rs @@ -5,17 +5,13 @@ use unsigned_varint::{ io::read_u64, }; -const RAW_CODEC: u64 = 0x55; -const PKH_CODEC: u64 = 0xca; -const SECP256K1_CODEC: u64 = 0xe7; -const BLS12_381_G1_CODEC: u64 = 0xea; -const BLS12_381_G2_CODEC: u64 = 0xeb; -const X25519_CODEC: u64 = 0xec; -const ED25519_CODEC: u64 = 0xed; -const P256_CODEC: u64 = 0x1200; -const P384_CODEC: u64 = 0x1201; -const P521_CODEC: u64 = 0x1202; -// const RSA_CODEC: u64 = 0x1205; +mod error; +mod key; +mod method; + +pub use error::Error; +pub use key::DidKeyTypes; +pub use method::Method; const MULTIDID_VARINT_TAG: u16 = 0x9d1a; @@ -103,7 +99,7 @@ impl MultiDid { }) } - pub fn to_writer(&self, writer: &mut W) -> Result<(), Error> { + pub fn to_writer(&self, writer: &mut W) -> Result<(), IoError> { writer.write_all(&MULTIDID_VARINT_TAG.to_be_bytes())?; // write codec let mut buf = u64_buffer(); @@ -161,175 +157,6 @@ impl MultiDid { } } -#[derive(Debug, Clone, PartialEq)] -pub enum Method { - Pkh(Vec), - Key(DidKeyTypes), - Raw(String), -} - -#[derive(Debug, thiserror::Error)] -pub enum Error { - #[error(transparent)] - Varint(#[from] unsigned_varint::io::ReadError), - #[error(transparent)] - Io(#[from] IoError), - #[error("Invalid multidid varint prefix, expected 0x9d1a, recieved {0:x}")] - InvalidPrefix(u64), - #[error(transparent)] - Parameter(#[from] iri_string::validate::Error), - #[error(transparent)] - DidParse(#[from] std::string::FromUtf8Error), -} - -impl Method { - pub fn codec(&self) -> u64 { - use Method::*; - match self { - Raw(_) => RAW_CODEC, - Pkh(_) => PKH_CODEC, - Key(k) => k.codec(), - } - } - - fn from_reader(reader: &mut R) -> Result { - let codec = read_u64(reader.by_ref())?; - match codec { - RAW_CODEC => { - let len = read_u64(reader.by_ref())?; - let mut buf = vec![0; len as usize]; - reader.read_exact(&mut buf)?; - Ok(Self::Raw(String::from_utf8(buf)?)) - } - PKH_CODEC => { - let len = read_u64(reader.by_ref())?; - let mut buf = vec![0; len as usize]; - reader.read_exact(&mut buf)?; - Ok(Self::Pkh(buf)) - } - codec => { - let key = DidKeyTypes::from_reader(reader, codec)?; - Ok(Self::Key(key)) - } - } - } - - fn to_writer(&self, writer: &mut W) -> Result<(), IoError> { - match self { - Self::Raw(buf) => { - writer.write_all(buf.as_bytes())?; - } - Self::Pkh(buf) => { - let mut vi_buf = u64_buffer(); - writer.write_all(write_u64(buf.len() as u64, &mut vi_buf))?; - writer.write_all(buf)?; - } - Self::Key(key) => { - key.to_writer(writer)?; - } - } - Ok(()) - } -} - -#[derive(Debug, Clone, PartialEq)] -pub enum DidKeyTypes { - Secp256k1([u8; 33]), - Bls12_381G1([u8; 64]), - Bls12_381G2([u8; 96]), - X25519([u8; 32]), - Ed25519([u8; 32]), - P256([u8; 33]), - P384([u8; 49]), - P521([u8; 67]), - // RSA([u8; ??]), -} - -impl DidKeyTypes { - pub fn codec(&self) -> u64 { - use DidKeyTypes::*; - match self { - Secp256k1(_) => SECP256K1_CODEC, - Bls12_381G1(_) => BLS12_381_G1_CODEC, - Bls12_381G2(_) => BLS12_381_G2_CODEC, - X25519(_) => X25519_CODEC, - Ed25519(_) => ED25519_CODEC, - P256(_) => P256_CODEC, - P384(_) => P384_CODEC, - P521(_) => P521_CODEC, - } - } - - fn from_reader(reader: &mut R, codec: u64) -> Result - where - R: Read, - { - match codec { - SECP256K1_CODEC => { - let mut buf = [0; 33]; - reader.read_exact(&mut buf)?; - Ok(Self::Secp256k1(buf)) - } - BLS12_381_G1_CODEC => { - let mut buf = [0; 64]; - reader.read_exact(&mut buf)?; - Ok(Self::Bls12_381G1(buf)) - } - BLS12_381_G2_CODEC => { - let mut buf = [0; 96]; - reader.read_exact(&mut buf)?; - Ok(Self::Bls12_381G2(buf)) - } - X25519_CODEC => { - let mut buf = [0; 32]; - reader.read_exact(&mut buf)?; - Ok(Self::X25519(buf)) - } - ED25519_CODEC => { - let mut buf = [0; 32]; - reader.read_exact(&mut buf)?; - Ok(Self::Ed25519(buf)) - } - P256_CODEC => { - let mut buf = [0; 33]; - reader.read_exact(&mut buf)?; - Ok(Self::P256(buf)) - } - P384_CODEC => { - let mut buf = [0; 49]; - reader.read_exact(&mut buf)?; - Ok(Self::P384(buf)) - } - P521_CODEC => { - let mut buf = [0; 67]; - reader.read_exact(&mut buf)?; - Ok(Self::P521(buf)) - } - _ => Err(Error::InvalidPrefix(codec)), - } - } - - fn to_writer(&self, writer: &mut W) -> Result<(), IoError> - where - W: ?Sized + Write, - { - writer.write_all(&self.bytes()) - } - - fn bytes(&self) -> &[u8] { - match self { - Self::Secp256k1(key) => key, - Self::Bls12_381G1(key) => key, - Self::Bls12_381G2(key) => key, - Self::X25519(key) => key, - Self::Ed25519(key) => key, - Self::P256(key) => key, - Self::P384(key) => key, - Self::P521(key) => key, - } - } -} - #[cfg(test)] mod tests { use super::*; diff --git a/multidid/src/method.rs b/multidid/src/method.rs new file mode 100644 index 0000000..f5130ec --- /dev/null +++ b/multidid/src/method.rs @@ -0,0 +1,66 @@ +use crate::{DidKeyTypes, Error}; +use std::io::{Error as IoError, Read, Write}; +use unsigned_varint::{ + encode::{u64 as write_u64, u64_buffer}, + io::read_u64, +}; + +const RAW_CODEC: u64 = 0x55; +const PKH_CODEC: u64 = 0xca; + +#[derive(Debug, Clone, PartialEq)] +pub enum Method { + Pkh(Vec), + Key(DidKeyTypes), + Raw(String), +} + +impl Method { + pub fn codec(&self) -> u64 { + use Method::*; + match self { + Raw(_) => RAW_CODEC, + Pkh(_) => PKH_CODEC, + Key(k) => k.codec(), + } + } + + pub(crate) fn from_reader(reader: &mut R) -> Result { + let codec = read_u64(reader.by_ref())?; + match codec { + RAW_CODEC => { + let len = read_u64(reader.by_ref())?; + let mut buf = vec![0; len as usize]; + reader.read_exact(&mut buf)?; + Ok(Self::Raw(String::from_utf8(buf)?)) + } + PKH_CODEC => { + let len = read_u64(reader.by_ref())?; + let mut buf = vec![0; len as usize]; + reader.read_exact(&mut buf)?; + Ok(Self::Pkh(buf)) + } + codec => { + let key = DidKeyTypes::from_reader(reader, codec)?; + Ok(Self::Key(key)) + } + } + } + + pub(crate) fn to_writer(&self, writer: &mut W) -> Result<(), IoError> { + match self { + Self::Raw(buf) => { + writer.write_all(buf.as_bytes())?; + } + Self::Pkh(buf) => { + let mut vi_buf = u64_buffer(); + writer.write_all(write_u64(buf.len() as u64, &mut vi_buf))?; + writer.write_all(buf)?; + } + Self::Key(key) => { + key.to_writer(writer)?; + } + } + Ok(()) + } +} From dbf1aed0eed9e2933d452d401136137275df240a Mon Sep 17 00:00:00 2001 From: chunningham Date: Tue, 7 Mar 2023 14:44:00 +0100 Subject: [PATCH 09/70] stub out pkh multidid and stub Display impls --- multidid/src/key.rs | 6 ++++++ multidid/src/lib.rs | 15 +++++++++++++++ multidid/src/method.rs | 35 +++++++++++++++++------------------ multidid/src/pkh.rs | 39 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 77 insertions(+), 18 deletions(-) create mode 100644 multidid/src/pkh.rs diff --git a/multidid/src/key.rs b/multidid/src/key.rs index 66301c7..7b15fa1 100644 --- a/multidid/src/key.rs +++ b/multidid/src/key.rs @@ -24,6 +24,12 @@ pub enum DidKeyTypes { // RSA([u8; ??]), } +impl std::fmt::Display for DidKeyTypes { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + todo!() + } +} + impl DidKeyTypes { pub fn codec(&self) -> u64 { use DidKeyTypes::*; diff --git a/multidid/src/lib.rs b/multidid/src/lib.rs index 4971896..ce349af 100644 --- a/multidid/src/lib.rs +++ b/multidid/src/lib.rs @@ -8,9 +8,11 @@ use unsigned_varint::{ mod error; mod key; mod method; +mod pkh; pub use error::Error; pub use key::DidKeyTypes; +pub use pkh::DidPkhTypes; pub use method::Method; const MULTIDID_VARINT_TAG: u16 = 0x9d1a; @@ -22,6 +24,19 @@ pub struct MultiDid { query: Option, } +impl std::fmt::Display for MultiDid { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.method)?; + if let Some(fragment) = &self.fragment { + write!(f, "#{}", fragment)?; + } + if let Some(query) = &self.query { + write!(f, "?{}", query)?; + } + Ok(()) + } +} + impl MultiDid { pub fn new( method: Method, diff --git a/multidid/src/method.rs b/multidid/src/method.rs index f5130ec..6b00aae 100644 --- a/multidid/src/method.rs +++ b/multidid/src/method.rs @@ -1,26 +1,32 @@ -use crate::{DidKeyTypes, Error}; +use crate::{pkh::PKH_CODEC, DidKeyTypes, DidPkhTypes, Error}; use std::io::{Error as IoError, Read, Write}; -use unsigned_varint::{ - encode::{u64 as write_u64, u64_buffer}, - io::read_u64, -}; +use unsigned_varint::io::read_u64; const RAW_CODEC: u64 = 0x55; -const PKH_CODEC: u64 = 0xca; #[derive(Debug, Clone, PartialEq)] pub enum Method { - Pkh(Vec), + Pkh(DidPkhTypes), Key(DidKeyTypes), Raw(String), } +impl std::fmt::Display for Method { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Method::Raw(raw) => write!(f, "did:{}", raw), + Method::Key(key) => write!(f, "did:key:{}", key), + Method::Pkh(pkh) => write!(f, "did:pkh:{}", pkh), + } + } +} + impl Method { pub fn codec(&self) -> u64 { use Method::*; match self { Raw(_) => RAW_CODEC, - Pkh(_) => PKH_CODEC, + Pkh(h) => h.codec(), Key(k) => k.codec(), } } @@ -34,12 +40,7 @@ impl Method { reader.read_exact(&mut buf)?; Ok(Self::Raw(String::from_utf8(buf)?)) } - PKH_CODEC => { - let len = read_u64(reader.by_ref())?; - let mut buf = vec![0; len as usize]; - reader.read_exact(&mut buf)?; - Ok(Self::Pkh(buf)) - } + PKH_CODEC => Ok(Self::Pkh(DidPkhTypes::from_reader(reader)?)), codec => { let key = DidKeyTypes::from_reader(reader, codec)?; Ok(Self::Key(key)) @@ -52,10 +53,8 @@ impl Method { Self::Raw(buf) => { writer.write_all(buf.as_bytes())?; } - Self::Pkh(buf) => { - let mut vi_buf = u64_buffer(); - writer.write_all(write_u64(buf.len() as u64, &mut vi_buf))?; - writer.write_all(buf)?; + Self::Pkh(pkh) => { + pkh.to_writer(writer)?; } Self::Key(key) => { key.to_writer(writer)?; diff --git a/multidid/src/pkh.rs b/multidid/src/pkh.rs new file mode 100644 index 0000000..ca1b85a --- /dev/null +++ b/multidid/src/pkh.rs @@ -0,0 +1,39 @@ +use crate::Error; +use std::io::{Error as IoError, Read, Write}; + +pub(crate) const PKH_CODEC: u64 = 0xca; + +#[derive(Debug, Clone, PartialEq)] +pub enum DidPkhTypes { + Eip { chain_id: u64, address: [u8; 20] }, +} + +impl std::fmt::Display for DidPkhTypes { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + // TODO correct address formatting + Self::Eip { chain_id, address } => { + todo!() + } + } + } +} + +impl DidPkhTypes { + pub fn codec(&self) -> u64 { + PKH_CODEC + } + pub(crate) fn from_reader(reader: &mut R) -> Result + where + R: Read, + { + todo!() + } + + pub(crate) fn to_writer(&self, writer: &mut W) -> Result<(), IoError> + where + W: ?Sized + Write, + { + todo!() + } +} From 382f91d2102379100f4198dcd388d770469266c1 Mon Sep 17 00:00:00 2001 From: chunningham Date: Tue, 7 Mar 2023 15:58:59 +0100 Subject: [PATCH 10/70] varsig sig type trait --- varsig/src/lib.rs | 67 +++++++++++++++++++++++++---------------------- 1 file changed, 36 insertions(+), 31 deletions(-) diff --git a/varsig/src/lib.rs b/varsig/src/lib.rs index f78d0f2..ecbbf0f 100644 --- a/varsig/src/lib.rs +++ b/varsig/src/lib.rs @@ -4,32 +4,46 @@ use unsigned_varint::{ io::read_u64, }; -const VARSIG_VARINT_PREFIX: u8 = 0x68; +const VARSIG_VARINT_PREFIX: u8 = 0x34; #[derive(Debug, Clone, PartialEq)] -pub struct VarSig { +pub struct VarSig { codec: u64, hash: u64, - key_type: u64, - signature: Vec, + signature: H, +} + +pub trait SignatureHeader { + type SerError: std::error::Error + std::fmt::Debug; + type DeserError: std::error::Error + std::fmt::Debug; + fn from_reader(reader: &mut R) -> Result + where + Self: Sized, + R: Read; + fn to_writer(&self, writer: &mut W) -> Result<(), Self::SerError> + where + W: ?Sized + Write; } #[derive(thiserror::Error, Debug)] -pub enum Error { +pub enum Error { #[error(transparent)] Varint(#[from] unsigned_varint::io::ReadError), #[error(transparent)] Io(#[from] std::io::Error), #[error("Invalid varsig prefix, expected 0x68, recieved {0:x}")] InvalidPrefix(u8), + #[error(transparent)] + SignatureDeser(S::DeserError), + #[error(transparent)] + SignatureSer(S::SerError), } -impl VarSig { - pub fn new(codec: u64, hash: u64, key_type: u64, signature: Vec) -> Self { +impl VarSig { + pub fn new(codec: u64, hash: u64, signature: H) -> Self { Self { codec, hash, - key_type, signature, } } @@ -42,25 +56,26 @@ impl VarSig { self.hash } - pub fn key_type(&self) -> u64 { - self.key_type - } - - pub fn signature(&self) -> &[u8] { + pub fn signature(&self) -> &H { &self.signature } +} +impl VarSig +where + H: SignatureHeader, +{ pub fn to_vec(&self) -> Result, IoError> { let mut buf = Vec::new(); self.to_writer(&mut buf)?; Ok(buf) } - pub fn from_bytes(bytes: &[u8]) -> Result { + pub fn from_bytes(bytes: &[u8]) -> Result> { Self::from_reader(&mut bytes.as_ref()) } - pub fn from_reader(reader: &mut R) -> Result + pub fn from_reader(reader: &mut R) -> Result> where R: Read, { @@ -71,24 +86,14 @@ impl VarSig { return Err(Error::InvalidPrefix(tag[0])); }; - println!("tag: {:#x?}", tag); let codec = read_u64(reader.by_ref())?; - println!("codec: {:#x?}", codec); let hash = read_u64(reader.by_ref())?; - println!("hash: {:#x?}", hash); - let key_type = read_u64(reader.by_ref())?; - println!("key_type: {:#x?}", key_type); - - let sig_len = read_u64(reader.by_ref())?; - println!("sig_len: {:#x?}", sig_len); - let mut signature = vec![0; sig_len as usize]; - reader.read_exact(&mut signature)?; - println!("signature: {:#x?}", signature); + let signature = H::from_reader(reader.by_ref()).map_err(Error::SignatureDeser)?; - Ok(Self::new(codec, hash, key_type, signature)) + Ok(Self::new(codec, hash, signature)) } - pub fn to_writer(&self, writer: &mut W) -> Result<(), IoError> + pub fn to_writer(&self, writer: &mut W) -> Result<(), Error> where W: ?Sized + Write, { @@ -96,9 +101,9 @@ impl VarSig { let mut buf = u64_buffer(); writer.write(write_u64(self.codec, &mut buf))?; writer.write(write_u64(self.hash, &mut buf))?; - writer.write(write_u64(self.key_type, &mut buf))?; - writer.write(write_u64(self.signature.len() as u64, &mut buf))?; - writer.write(&self.signature)?; + self.signature + .to_writer(writer) + .map_err(Error::SignatureSer)?; Ok(()) } } From 192ebc5d666cfe0fd7773f676c44d2d10c21803a Mon Sep 17 00:00:00 2001 From: chunningham Date: Thu, 9 Mar 2023 17:15:56 +0100 Subject: [PATCH 11/70] wip --- Cargo.toml | 4 +++- multidid/src/lib.rs | 13 ++++++++----- src/v2/mod.rs | 32 +++++++++++++++++++++----------- src/v2/recap_cacao.rs | 3 +++ varsig/src/eip191.rs | 3 +++ varsig/src/either.rs | 41 +++++++++++++++++++++++++++++++++++++++++ varsig/src/lib.rs | 10 +++++++--- 7 files changed, 86 insertions(+), 20 deletions(-) create mode 100644 varsig/src/eip191.rs create mode 100644 varsig/src/either.rs diff --git a/Cargo.toml b/Cargo.toml index d5df4fb..63d53d9 100755 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,11 +23,13 @@ url = "2.2" async-trait = "0.1" serde = "1" serde_json = "1" -libipld = { version = "0.16", default-features = false, features = ["derive", "serde-codec"]} +libipld = { version = "0.16", default-features = false, features = ["derive", "serde-codec", "dag-cbor"]} serde_with = "2" time = { version = "0.3", features = ["parsing", "formatting"] } http = "0.2.5" hex = { version = "0.4", optional = true } +varsig = { version = "0.1", path = "./varsig" } +multidid = { version = "0.1", path = "./multidid" } [dev-dependencies] async-std = { version = "1.10", features = ["attributes"] } diff --git a/multidid/src/lib.rs b/multidid/src/lib.rs index ce349af..43dd258 100644 --- a/multidid/src/lib.rs +++ b/multidid/src/lib.rs @@ -12,8 +12,8 @@ mod pkh; pub use error::Error; pub use key::DidKeyTypes; -pub use pkh::DidPkhTypes; pub use method::Method; +pub use pkh::DidPkhTypes; const MULTIDID_VARINT_TAG: u16 = 0x9d1a; @@ -62,10 +62,13 @@ impl MultiDid { self.query.as_ref() } - pub fn to_vec(&self) -> Result, Error> { - let mut buf = Vec::new(); - self.to_writer(&mut buf)?; - Ok(buf) + pub fn to_vec(&self) -> Vec { + match (self.query, self.fragment) { + (Some(q), Some(f)) => {} + (None, Some(f)) => {} + (Some(q), None) => {} + (None, None) => self.method.to_vec(), + } } pub fn from_bytes(b: &[u8]) -> Result { diff --git a/src/v2/mod.rs b/src/v2/mod.rs index 0dda037..f65b88b 100644 --- a/src/v2/mod.rs +++ b/src/v2/mod.rs @@ -5,25 +5,23 @@ use iri_string::types::UriString; use libipld::cid::Cid; use serde::{Deserialize, Serialize}; use serde_json::Value; -use serde_with::{serde_as, DeserializeFromStr, SerializeDisplay}; +use serde_with::{serde_as, serde_conv, DeserializeFromStr, SerializeDisplay}; use std::collections::BTreeMap; -pub mod multidid; pub mod recap_cacao; -pub mod varsig; -pub mod version; -use multidid::MultiDID; -use varsig::VarSig; +use multidid::MultiDid; +use varsig::{SignatureHeader, VarSig}; +#[serde_as] #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] -pub struct CACAO, NB = Value> { +pub struct CACAO { #[serde(rename = "iss")] - issuer: MultiDID, + issuer: MultiDid, #[serde(rename = "aud")] - audience: MultiDID, + audience: MultiDid, #[serde(rename = "s")] - signature: VarSig, + signature: VarSig, #[serde(rename = "v")] version: String, #[serde(rename = "att")] @@ -39,9 +37,21 @@ pub struct CACAO, NB = Value> { #[serde(rename = "exp")] expiration: Option, #[serde(rename = "fct")] - facts: F, + facts: P::Facts, } +pub trait CacaoProfile { + type Signature: SignatureHeader; + type Facts: for<'d> Deserialize<'d> + Serialize + Clone + Debug; +} + +serde_conv!( + MultiDidToBytes, + MultiDid, + |did: &MultiDid| did.to_vec(), + |value: &[u8]| MultiDid::from_bytes(value) +); + #[cfg(test)] pub mod tests { use super::*; diff --git a/src/v2/recap_cacao.rs b/src/v2/recap_cacao.rs index 714e16e..c6d25fb 100644 --- a/src/v2/recap_cacao.rs +++ b/src/v2/recap_cacao.rs @@ -3,6 +3,7 @@ use http::uri::Authority; use iri_string::types::UriString; use serde::{Deserialize, Serialize}; use serde_json::Value; +use serde_with::{serde_as, DeserializeFromStr, DisplayFromStr, SerializeDisplay}; pub use siwe; use siwe::{eip55, Message, TimeStamp, VerificationError as SVE, Version as SVersion}; use std::fmt::Debug; @@ -12,6 +13,7 @@ use time::OffsetDateTime; pub type RecapCacao = CACAO; +#[serde_as] #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub struct RecapFacts { #[serde(rename = "iat-z")] @@ -20,6 +22,7 @@ pub struct RecapFacts { nbf_time_zone: Option, #[serde(rename = "nbf-z")] exp_time_zone: Option, + #[serde_as(as = "DisplayFromStr")] domain: Authority, statement: Option, #[serde(rename = "request-id")] diff --git a/varsig/src/eip191.rs b/varsig/src/eip191.rs new file mode 100644 index 0000000..9619557 --- /dev/null +++ b/varsig/src/eip191.rs @@ -0,0 +1,3 @@ +use crate::{Error, SignatureHeader}; + +pub struct Eip191Signature([u8; 65]); diff --git a/varsig/src/either.rs b/varsig/src/either.rs new file mode 100644 index 0000000..c4de52e --- /dev/null +++ b/varsig/src/either.rs @@ -0,0 +1,41 @@ +use crate::{Error, SignatureHeader}; +use std::io::{Error as IoError, Read, Write}; + +pub enum EitherSignature { + A(A), + B(B), +} + +#[derive(thiserror::Error, Debug)] +pub enum EitherError { + #[error(transparent)] + A(A), + #[error(transparent)] + B(B), +} + +impl SignatureHeader for EitherSignature +where + A: SignatureHeader, + B: SignatureHeader, +{ + type SerError = EitherError; + type DeserError = EitherError; + fn from_reader(reader: &mut R) -> Result + where + R: Read, + Self: Sized, + { + todo!() + } + + fn to_writer(&self, writer: &mut W) -> Result<(), Self::SerError> + where + W: ?Sized + Write, + { + match self { + Self::A(a) => a.to_writer(writer).map_err(EitherError::A), + Self::B(b) => b.to_writer(writer).map_err(EitherError::B), + } + } +} diff --git a/varsig/src/lib.rs b/varsig/src/lib.rs index ecbbf0f..ccac61e 100644 --- a/varsig/src/lib.rs +++ b/varsig/src/lib.rs @@ -1,8 +1,12 @@ -use std::io::{Error as IoError, Read, Write}; +use std::io::{Read, Write}; use unsigned_varint::{ encode::{u64 as write_u64, u64_buffer}, io::read_u64, }; +pub mod eip191; +pub mod either; + +pub use either::EitherSignature; const VARSIG_VARINT_PREFIX: u8 = 0x34; @@ -31,7 +35,7 @@ pub enum Error { Varint(#[from] unsigned_varint::io::ReadError), #[error(transparent)] Io(#[from] std::io::Error), - #[error("Invalid varsig prefix, expected 0x68, recieved {0:x}")] + #[error("Invalid varsig prefix, expected 0x34, recieved {0:x}")] InvalidPrefix(u8), #[error(transparent)] SignatureDeser(S::DeserError), @@ -65,7 +69,7 @@ impl VarSig where H: SignatureHeader, { - pub fn to_vec(&self) -> Result, IoError> { + pub fn to_vec(&self) -> Result, Error> { let mut buf = Vec::new(); self.to_writer(&mut buf)?; Ok(buf) From efbfd82ae2a56d125136635134b43faffc0a0a2e Mon Sep 17 00:00:00 2001 From: chunningham Date: Thu, 13 Apr 2023 22:11:28 +1000 Subject: [PATCH 12/70] move varsig trait to /traits, impl wrapper --- varsig/src/either.rs | 54 ++++++++--- varsig/src/lib.rs | 210 ++++++++++++++++++------------------------- varsig/src/traits.rs | 65 ++++++++++++++ 3 files changed, 197 insertions(+), 132 deletions(-) create mode 100644 varsig/src/traits.rs diff --git a/varsig/src/either.rs b/varsig/src/either.rs index c4de52e..b94f376 100644 --- a/varsig/src/either.rs +++ b/varsig/src/either.rs @@ -1,5 +1,5 @@ -use crate::{Error, SignatureHeader}; -use std::io::{Error as IoError, Read, Write}; +use crate::{DeserError, SerError, VarSigTrait}; +use std::io::{Read, Write}; pub enum EitherSignature { A(A), @@ -14,28 +14,62 @@ pub enum EitherError { B(B), } -impl SignatureHeader for EitherSignature +impl VarSigTrait for EitherSignature where - A: SignatureHeader, - B: SignatureHeader, + A: VarSigTrait, + B: VarSigTrait, { type SerError = EitherError; type DeserError = EitherError; - fn from_reader(reader: &mut R) -> Result + fn valid_header(bytes: &[u8]) -> bool { + A::valid_header(bytes) || B::valid_header(bytes) + } + + fn from_reader(reader: &mut R) -> Result> where R: Read, Self: Sized, { - todo!() + // check the header to discern the sig type + let mut buf = [0u8; 4]; + reader.read_exact(&mut buf)?; + + if A::valid_header(&buf) { + let chain = &mut buf.chain(reader); + A::from_reader(chain) + .map_err(|e| match e { + DeserError::Io(e) => DeserError::Io(e), + DeserError::InvalidHeader => DeserError::InvalidHeader, + DeserError::Format(e) => DeserError::Format(EitherError::A(e)), + }) + .map(EitherSignature::A) + } else if B::valid_header(&buf) { + let chain = &mut buf.chain(reader); + B::from_reader(chain) + .map_err(|e| match e { + DeserError::Io(e) => DeserError::Io(e), + DeserError::InvalidHeader => DeserError::InvalidHeader, + DeserError::Format(e) => DeserError::Format(EitherError::B(e)), + }) + .map(EitherSignature::B) + } else { + Err(DeserError::InvalidHeader) + } } - fn to_writer(&self, writer: &mut W) -> Result<(), Self::SerError> + fn to_writer(&self, writer: &mut W) -> Result<(), SerError> where W: ?Sized + Write, { match self { - Self::A(a) => a.to_writer(writer).map_err(EitherError::A), - Self::B(b) => b.to_writer(writer).map_err(EitherError::B), + Self::A(a) => a.to_writer(writer).map_err(|e| match e { + SerError::Io(e) => SerError::Io(e), + SerError::Format(e) => SerError::Format(EitherError::A(e)), + }), + Self::B(b) => b.to_writer(writer).map_err(|e| match e { + SerError::Io(e) => SerError::Io(e), + SerError::Format(e) => SerError::Format(EitherError::B(e)), + }), } } } diff --git a/varsig/src/lib.rs b/varsig/src/lib.rs index ccac61e..be6ba5f 100644 --- a/varsig/src/lib.rs +++ b/varsig/src/lib.rs @@ -1,154 +1,120 @@ use std::io::{Read, Write}; -use unsigned_varint::{ - encode::{u64 as write_u64, u64_buffer}, - io::read_u64, -}; + pub mod eip191; pub mod either; +pub mod traits; pub use either::EitherSignature; +pub use traits::{DeserError, SerError, VarSigTrait}; const VARSIG_VARINT_PREFIX: u8 = 0x34; -#[derive(Debug, Clone, PartialEq)] -pub struct VarSig { - codec: u64, - hash: u64, - signature: H, -} - -pub trait SignatureHeader { - type SerError: std::error::Error + std::fmt::Debug; - type DeserError: std::error::Error + std::fmt::Debug; - fn from_reader(reader: &mut R) -> Result - where - Self: Sized, - R: Read; - fn to_writer(&self, writer: &mut W) -> Result<(), Self::SerError> - where - W: ?Sized + Write; -} +pub struct VarSig(S); -#[derive(thiserror::Error, Debug)] -pub enum Error { - #[error(transparent)] - Varint(#[from] unsigned_varint::io::ReadError), - #[error(transparent)] - Io(#[from] std::io::Error), - #[error("Invalid varsig prefix, expected 0x34, recieved {0:x}")] - InvalidPrefix(u8), - #[error(transparent)] - SignatureDeser(S::DeserError), - #[error(transparent)] - SignatureSer(S::SerError), -} - -impl VarSig { - pub fn new(codec: u64, hash: u64, signature: H) -> Self { - Self { - codec, - hash, - signature, - } +impl VarSig { + pub fn new(s: S) -> Self { + Self(s) } - pub fn codec(&self) -> u64 { - self.codec + pub fn sig(&self) -> &S { + &self.0 } - pub fn hash(&self) -> u64 { - self.hash + pub fn from_reader(reader: &mut R) -> Result> + where + Self: Sized, + R: Read, + { + let mut buf = [0u8; 1]; + reader.read_exact(&mut buf)?; + if buf[0] != VARSIG_VARINT_PREFIX { + return Err(Error::InvalidPrefix(buf[0])); + } + Ok(Self::new(S::from_reader(reader)?)) } - pub fn signature(&self) -> &H { - &self.signature + pub fn to_writer(&self, writer: &mut W) -> Result<(), SerError> + where + W: ?Sized + Write, + { + writer.write_all(&[VARSIG_VARINT_PREFIX])?; + self.0.to_writer(writer) } -} -impl VarSig -where - H: SignatureHeader, -{ - pub fn to_vec(&self) -> Result, Error> { + pub fn to_vec(&self) -> Result, SerError> { let mut buf = Vec::new(); self.to_writer(&mut buf)?; Ok(buf) } - pub fn from_bytes(bytes: &[u8]) -> Result> { - Self::from_reader(&mut bytes.as_ref()) - } - - pub fn from_reader(reader: &mut R) -> Result> + pub fn from_bytes(bytes: &[u8]) -> Result> where - R: Read, + Self: Sized, { - let mut tag = [0u8; 1]; - reader.read_exact(&mut tag)?; - - if tag[0] != VARSIG_VARINT_PREFIX { - return Err(Error::InvalidPrefix(tag[0])); - }; - - let codec = read_u64(reader.by_ref())?; - let hash = read_u64(reader.by_ref())?; - let signature = H::from_reader(reader.by_ref()).map_err(Error::SignatureDeser)?; - - Ok(Self::new(codec, hash, signature)) + let mut reader = bytes; + Self::from_reader(&mut reader) } +} - pub fn to_writer(&self, writer: &mut W) -> Result<(), Error> - where - W: ?Sized + Write, - { - writer.write(&[VARSIG_VARINT_PREFIX; 1])?; - let mut buf = u64_buffer(); - writer.write(write_u64(self.codec, &mut buf))?; - writer.write(write_u64(self.hash, &mut buf))?; - self.signature - .to_writer(writer) - .map_err(Error::SignatureSer)?; - Ok(()) +#[derive(thiserror::Error, Debug)] +pub enum Error { + #[error(transparent)] + Format(D), + #[error("Invalid Prefix, expected 0x34, got 0x{0:x}")] + InvalidPrefix(u8), + #[error("Invalid header")] + InvalidHeader, + #[error(transparent)] + IoError(#[from] std::io::Error), +} + +impl From> for Error { + fn from(e: DeserError) -> Self { + match e { + DeserError::Format(e) => Error::Format(e), + DeserError::InvalidHeader => Error::InvalidHeader, + DeserError::Io(e) => Error::IoError(e), + } } } #[cfg(test)] mod tests { - use super::*; - - const EXAMPLE: [u8; 15] = [ - 0x68, 0xd2, 0x04, 0xda, 0x03, 0x24, 0xde, 0xb7, 0xde, 0x9a, 0xf1, 0xd9, 0xa2, 0xa3, 0x02, - ]; - - #[test] - fn basic_roundtrip() { - let varsig = VarSig::from_reader(&mut EXAMPLE.as_ref()).unwrap(); - assert_eq!(varsig.codec(), 0x04d2); - assert_eq!(varsig.hash(), 0x03da); - assert_eq!(varsig.key_type(), 0x24); - assert_eq!(varsig.signature(), &EXAMPLE[6..]); - - assert_eq!(&varsig.to_vec().unwrap(), &EXAMPLE); - } - - #[test] - fn reverse_roundtrip() { - let varsig = VarSig::new(0x0129, 0x12, 0xed, EXAMPLE[6..].to_vec()); - let encoded = varsig.to_vec().unwrap(); - let decoded = VarSig::from_bytes(&encoded.as_ref()).unwrap(); - assert_eq!(varsig, decoded); - } - - #[test] - fn basic_ser() { - let varsig = VarSig::new(0x0129, 0x12, 0xed, EXAMPLE[6..].to_vec()); - assert_eq!(&varsig.to_vec().unwrap(), &EXAMPLE); - } - - #[test] - fn rsad() { - let b = [0xd2, 0x04]; - println!("{:#x?}", read_u64(&mut b.as_ref())); - None::.unwrap(); - } + // use super::*; + + // const EXAMPLE: [u8; 15] = [ + // 0x68, 0xd2, 0x04, 0xda, 0x03, 0x24, 0xde, 0xb7, 0xde, 0x9a, 0xf1, 0xd9, 0xa2, 0xa3, 0x02, + // ]; + + // #[test] + // fn basic_roundtrip() { + // let varsig = VarSigTrait::from_reader(&mut EXAMPLE.as_ref()).unwrap(); + // assert_eq!(varsig.codec(), 0x04d2); + // assert_eq!(varsig.hash(), 0x03da); + // assert_eq!(varsig.key_type(), 0x24); + // assert_eq!(varsig.signature(), &EXAMPLE[6..]); + + // assert_eq!(&varsig.to_vec().unwrap(), &EXAMPLE); + // } + + // #[test] + // fn reverse_roundtrip() { + // let varsig = VarSigTrait::new(0x0129, 0x12, 0xed, EXAMPLE[6..].to_vec()); + // let encoded = varsig.to_vec().unwrap(); + // let decoded = VarSigTrait::from_bytes(&encoded.as_ref()).unwrap(); + // assert_eq!(varsig, decoded); + // } + + // #[test] + // fn basic_ser() { + // let varsig = VarSigTrait::new(0x0129, 0x12, 0xed, EXAMPLE[6..].to_vec()); + // assert_eq!(&varsig.to_vec().unwrap(), &EXAMPLE); + // } + + // #[test] + // fn rsad() { + // let b = [0xd2, 0x04]; + // println!("{:#x?}", read_u64(&mut b.as_ref())); + // None::.unwrap(); + // } } diff --git a/varsig/src/traits.rs b/varsig/src/traits.rs new file mode 100644 index 0000000..60a8025 --- /dev/null +++ b/varsig/src/traits.rs @@ -0,0 +1,65 @@ +use std::io::{Read, Write}; + +pub trait VarSigTrait { + type SerError: std::error::Error + std::fmt::Debug; + type DeserError: std::error::Error + std::fmt::Debug; + + fn valid_header(bytes: &[u8]) -> bool; + + fn from_reader(reader: &mut R) -> Result> + where + Self: Sized, + R: Read; + + fn to_writer(&self, writer: &mut W) -> Result<(), SerError> + where + W: ?Sized + Write; + + fn to_vec(&self) -> Result, SerError> { + let mut buf = Vec::new(); + self.to_writer(&mut buf)?; + Ok(buf) + } + + fn from_bytes(bytes: &[u8]) -> Result> + where + Self: Sized, + { + if !Self::valid_header(bytes) { + return Err(DeserError::InvalidHeader); + }; + let mut reader = std::io::Cursor::new(bytes); + Self::from_reader(&mut reader) + } +} + +#[derive(thiserror::Error, Debug)] +pub enum DeserError { + #[error("Invalid header")] + InvalidHeader, + #[error(transparent)] + Io(#[from] std::io::Error), + #[error(transparent)] + Format(E), +} + +#[derive(thiserror::Error, Debug)] +pub enum SerError { + #[error(transparent)] + Io(#[from] std::io::Error), + #[error(transparent)] + Format(E), +} + +impl From for DeserError +where + E: From, +{ + fn from(e: unsigned_varint::io::ReadError) -> Self { + match e { + unsigned_varint::io::ReadError::Io(e) => DeserError::Io(e), + unsigned_varint::io::ReadError::Decode(e) => DeserError::Format(e.into()), + _ => DeserError::Io(e.into()), + } + } +} From 8a26fcf6e5b658a7cf9cb852494238c303f5671e Mon Sep 17 00:00:00 2001 From: chunningham Date: Thu, 13 Apr 2023 22:12:12 +1000 Subject: [PATCH 13/70] impl common varsig types as in the spec --- varsig/src/common/ecdsa.rs | 95 ++++++++++++++++++++++++++++ varsig/src/common/ed25519.rs | 118 +++++++++++++++++++++++++++++++++++ varsig/src/common/mod.rs | 10 +++ varsig/src/common/rsa.rs | 93 +++++++++++++++++++++++++++ varsig/src/lib.rs | 1 + 5 files changed, 317 insertions(+) create mode 100644 varsig/src/common/ecdsa.rs create mode 100644 varsig/src/common/ed25519.rs create mode 100644 varsig/src/common/mod.rs create mode 100644 varsig/src/common/rsa.rs diff --git a/varsig/src/common/ecdsa.rs b/varsig/src/common/ecdsa.rs new file mode 100644 index 0000000..7a23a12 --- /dev/null +++ b/varsig/src/common/ecdsa.rs @@ -0,0 +1,95 @@ +use super::{SHA256, SHA512}; +use crate::{DeserError, SerError, VarSigTrait}; +use std::io::{Read, Write}; +use unsigned_varint::{ + decode::Error as VarintError, + encode::{u64 as write_u64, u64_buffer}, + io::read_u64, +}; + +pub const P256: u16 = 0x1200; +pub const K256: u16 = 0xe7; +pub const P521: u16 = 0x1202; + +pub struct Ecdsa { + encoding: u64, + bytes: [u8; LEN], +} + +impl Ecdsa { + pub fn new(encoding: u64, bytes: [u8; LEN]) -> Self { + Self { encoding, bytes } + } + pub fn encoding(&self) -> u64 { + self.encoding + } + pub fn bytes(&self) -> &[u8; LEN] { + &self.bytes + } +} + +pub type Es256 = Ecdsa<{ P256 as u64 }, { SHA256 as u64 }, 64>; +pub type Es256K = Ecdsa<{ K256 as u64 }, { SHA256 as u64 }, 64>; +pub type Es512 = Ecdsa<{ P521 as u64 }, { SHA512 as u64 }, 128>; + +#[derive(thiserror::Error, Debug)] +pub enum EcdsaError { + #[error(transparent)] + Varint(#[from] VarintError), + #[error("Incorrect hash code, expected {0}, got {1}")] + IncorrectHash(u64, u64), +} + +impl VarSigTrait + for Ecdsa +{ + type SerError = std::convert::Infallible; + type DeserError = EcdsaError; + + fn valid_header(bytes: &[u8]) -> bool { + let mut buf = u64_buffer(); + let h = write_u64(HEADER, &mut buf); + Some(h) == bytes.get(..h.len()) + } + + fn from_reader(reader: &mut R) -> Result> + where + Self: Sized, + R: Read, + { + let header = read_u64(reader.by_ref())?; + if header != HEADER { + return Err(DeserError::InvalidHeader); + }; + + let hash = read_u64(reader.by_ref())?; + if hash != HASH { + return Err(DeserError::Format(EcdsaError::IncorrectHash(HASH, hash))); + }; + + let encoding = read_u64(reader.by_ref())?; + let mut bytes = [0u8; LEN]; + reader.read_exact(&mut bytes)?; + Ok(Self { encoding, bytes }) + } + + fn to_writer(&self, writer: &mut W) -> Result<(), SerError> + where + W: ?Sized + Write, + { + let mut buf = u64_buffer(); + writer.write_all(write_u64(HEADER, &mut buf))?; + writer.write_all(write_u64(HASH, &mut buf))?; + writer.write_all(write_u64(self.encoding, &mut buf))?; + writer.write_all(&self.bytes)?; + Ok(()) + } + + fn from_bytes(bytes: &[u8]) -> Result> + where + Self: Sized, + { + let mut reader = std::io::Cursor::new(bytes); + Self::from_reader(&mut reader) + } +} diff --git a/varsig/src/common/ed25519.rs b/varsig/src/common/ed25519.rs new file mode 100644 index 0000000..367b813 --- /dev/null +++ b/varsig/src/common/ed25519.rs @@ -0,0 +1,118 @@ +use crate::{DeserError, SerError, VarSigTrait}; +use std::io::{Read, Write}; +use unsigned_varint::{ + decode::Error as VarintError, + encode::{u64 as write_u64, u64_buffer}, + io::read_u64, +}; + +pub const ED25519: u16 = 0xed; + +pub struct Ed25519 { + encoding: u64, + bytes: [u8; 64], +} + +impl Ed25519 { + pub fn new(encoding: u64, bytes: [u8; 64]) -> Self { + Self { encoding, bytes } + } + pub fn encoding(&self) -> u64 { + self.encoding + } + pub fn bytes(&self) -> &[u8; 64] { + &self.bytes + } +} + +#[derive(thiserror::Error, Debug)] +pub enum Ed25519Error { + #[error(transparent)] + Varint(#[from] VarintError), +} + +impl VarSigTrait for Ed25519 { + type SerError = std::convert::Infallible; + type DeserError = Ed25519Error; + + fn valid_header(bytes: &[u8]) -> bool { + let mut buf = u64_buffer(); + let h = write_u64(ED25519 as u64, &mut buf); + Some(h) == bytes.get(..2) + } + + fn from_reader(reader: &mut R) -> Result> + where + Self: Sized, + R: Read, + { + let header = read_u64(reader.by_ref())?; + if header != ED25519 as u64 { + return Err(DeserError::InvalidHeader); + }; + + let encoding = read_u64(reader.by_ref())?; + let mut bytes = [0u8; 64]; + reader.read_exact(&mut bytes)?; + Ok(Self { encoding, bytes }) + } + + fn to_writer(&self, writer: &mut W) -> Result<(), SerError> + where + W: ?Sized + Write, + { + let mut buf = u64_buffer(); + writer.write_all(write_u64(ED25519 as u64, &mut buf))?; + writer.write_all(write_u64(self.encoding, &mut buf))?; + writer.write_all(&self.bytes)?; + Ok(()) + } + + fn from_bytes(bytes: &[u8]) -> Result> + where + Self: Sized, + { + let mut reader = std::io::Cursor::new(bytes); + Self::from_reader(&mut reader) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::VarSig; + + const EXAMPLE: &[u8] = &[ + 0x34, // varsig prefix + 0xed, 0x01, // ed25519 varint encoded header + 0x55, // raw encoding multicodec + 0xae, 0x37, 0x84, 0xf0, 0x3f, 0x9e, 0xe1, 0x16, 0x33, 0x82, 0xfa, 0x6e, 0xfa, 0x73, 0xb0, + 0xc3, 0x1e, 0xcf, 0x58, 0xc8, 0x99, 0xc8, 0x36, 0x70, 0x93, 0x03, 0xba, 0x46, 0x21, 0xd1, + 0xe6, 0xdf, 0x20, 0xe0, 0x9a, 0xaa, 0x56, 0x89, 0x14, 0x29, 0x0b, 0x7e, 0xa1, 0x24, 0xf5, + 0xb3, 0x8e, 0x70, 0xb9, 0xb6, 0x9c, 0x7d, 0xe0, 0xd2, 0x16, 0x88, 0x0e, 0xac, 0x88, 0x5e, + 0xdd, 0x41, 0xc3, 0x02, + ]; + + #[test] + fn basic_roundtrip_1() { + let decoded = VarSig::::from_bytes(EXAMPLE).unwrap(); + let encoded = decoded.to_vec().unwrap(); + + assert_eq!(EXAMPLE, &encoded[..]); + assert_eq!(decoded.sig().encoding(), 0x55); + assert_eq!(decoded.sig().bytes().as_ref(), &EXAMPLE[4..]); + } + + #[test] + fn basic_roundtrip_2() { + let mut reader = EXAMPLE; + let decoded = VarSig::::from_reader(&mut reader).unwrap(); + + let mut buf = Vec::new(); + decoded.to_writer(&mut buf).unwrap(); + + assert_eq!(EXAMPLE, &buf); + assert_eq!(decoded.sig().encoding(), 0x55); + assert_eq!(decoded.sig().bytes().as_ref(), &EXAMPLE[4..]); + } +} diff --git a/varsig/src/common/mod.rs b/varsig/src/common/mod.rs new file mode 100644 index 0000000..e2e2edc --- /dev/null +++ b/varsig/src/common/mod.rs @@ -0,0 +1,10 @@ +mod ecdsa; +mod ed25519; +mod rsa; + +const SHA256: u16 = 0x12; +const SHA512: u16 = 0x13; + +pub use ecdsa::{EcdsaError, Es256, Es256K, Es512}; +pub use ed25519::{Ed25519, Ed25519Error}; +pub use rsa::{Rsa256, Rsa512, RsaError}; diff --git a/varsig/src/common/rsa.rs b/varsig/src/common/rsa.rs new file mode 100644 index 0000000..6e6084a --- /dev/null +++ b/varsig/src/common/rsa.rs @@ -0,0 +1,93 @@ +use super::{SHA256, SHA512}; +use crate::{DeserError, SerError, VarSigTrait}; +use std::io::{Read, Write}; +use unsigned_varint::{ + decode::Error as VarintError, + encode::{u64 as write_u64, u64_buffer}, + io::read_u64, +}; + +pub const RSA: u16 = 0x1205; + +pub struct Rsa { + encoding: u64, + bytes: Vec, +} + +impl Rsa { + pub fn new(encoding: u64, bytes: Vec) -> Self { + Self { encoding, bytes } + } + pub fn encoding(&self) -> u64 { + self.encoding + } + pub fn bytes(&self) -> &[u8] { + &self.bytes + } +} + +pub type Rsa256 = Rsa<{ SHA256 as u64 }>; +pub type Rsa512 = Rsa<{ SHA512 as u64 }>; + +#[derive(thiserror::Error, Debug)] +pub enum RsaError { + #[error(transparent)] + Varint(#[from] VarintError), + #[error("Incorrect hash code, expected {0}, got {1}")] + IncorrectHash(u64, u64), +} + +impl VarSigTrait for Rsa { + type SerError = std::convert::Infallible; + type DeserError = RsaError; + + fn valid_header(bytes: &[u8]) -> bool { + let mut buf = u64_buffer(); + let h = write_u64(RSA as u64, &mut buf); + Some(h) == bytes.get(..2) + } + + fn from_reader(reader: &mut R) -> Result> + where + Self: Sized, + R: Read, + { + let header = read_u64(reader.by_ref())?; + if header != RSA as u64 { + return Err(DeserError::InvalidHeader); + }; + + let hash = read_u64(reader.by_ref())?; + if hash != HASH { + return Err(DeserError::Format(RsaError::IncorrectHash(HASH, hash))); + }; + + let len = read_u64(reader.by_ref())?; + let mut bytes = Vec::with_capacity(len as usize); + + let encoding = read_u64(reader.by_ref())?; + reader.read_exact(&mut bytes)?; + Ok(Self { encoding, bytes }) + } + + fn to_writer(&self, writer: &mut W) -> Result<(), SerError> + where + W: ?Sized + Write, + { + let mut buf = u64_buffer(); + writer.write_all(write_u64(RSA as u64, &mut buf))?; + writer.write_all(write_u64(HASH, &mut buf))?; + writer.write_all(write_u64(self.bytes.len() as u64, &mut buf))?; + writer.write_all(write_u64(self.encoding, &mut buf))?; + writer.write_all(&self.bytes)?; + Ok(()) + } + + fn from_bytes(bytes: &[u8]) -> Result> + where + Self: Sized, + { + let mut reader = std::io::Cursor::new(bytes); + Self::from_reader(&mut reader) + } +} diff --git a/varsig/src/lib.rs b/varsig/src/lib.rs index be6ba5f..b963445 100644 --- a/varsig/src/lib.rs +++ b/varsig/src/lib.rs @@ -1,5 +1,6 @@ use std::io::{Read, Write}; +pub mod common; pub mod eip191; pub mod either; pub mod traits; From aa6e78063f09ac7b9c4bfc7a602277370d963b26 Mon Sep 17 00:00:00 2001 From: chunningham Date: Thu, 13 Apr 2023 22:21:37 +1000 Subject: [PATCH 14/70] move eip191 to ecdsa mod --- varsig/src/common/ecdsa.rs | 4 +++- varsig/src/common/mod.rs | 3 ++- varsig/src/eip191.rs | 3 --- varsig/src/lib.rs | 1 - 4 files changed, 5 insertions(+), 6 deletions(-) delete mode 100644 varsig/src/eip191.rs diff --git a/varsig/src/common/ecdsa.rs b/varsig/src/common/ecdsa.rs index 7a23a12..3c97cdd 100644 --- a/varsig/src/common/ecdsa.rs +++ b/varsig/src/common/ecdsa.rs @@ -1,4 +1,4 @@ -use super::{SHA256, SHA512}; +use super::{KECCAK256, SHA256, SHA512}; use crate::{DeserError, SerError, VarSigTrait}; use std::io::{Read, Write}; use unsigned_varint::{ @@ -32,6 +32,8 @@ pub type Es256 = Ecdsa<{ P256 as u64 }, { SHA256 as u64 }, 64>; pub type Es256K = Ecdsa<{ K256 as u64 }, { SHA256 as u64 }, 64>; pub type Es512 = Ecdsa<{ P521 as u64 }, { SHA512 as u64 }, 128>; +pub type Eip191 = Ecdsa<{ K256 as u64 }, { KECCAK256 as u64 }, 65>; + #[derive(thiserror::Error, Debug)] pub enum EcdsaError { #[error(transparent)] diff --git a/varsig/src/common/mod.rs b/varsig/src/common/mod.rs index e2e2edc..ca8aaef 100644 --- a/varsig/src/common/mod.rs +++ b/varsig/src/common/mod.rs @@ -4,7 +4,8 @@ mod rsa; const SHA256: u16 = 0x12; const SHA512: u16 = 0x13; +const KECCAK256: u16 = 0x1B; -pub use ecdsa::{EcdsaError, Es256, Es256K, Es512}; +pub use ecdsa::{EcdsaError, Eip191, Es256, Es256K, Es512}; pub use ed25519::{Ed25519, Ed25519Error}; pub use rsa::{Rsa256, Rsa512, RsaError}; diff --git a/varsig/src/eip191.rs b/varsig/src/eip191.rs deleted file mode 100644 index 9619557..0000000 --- a/varsig/src/eip191.rs +++ /dev/null @@ -1,3 +0,0 @@ -use crate::{Error, SignatureHeader}; - -pub struct Eip191Signature([u8; 65]); diff --git a/varsig/src/lib.rs b/varsig/src/lib.rs index b963445..e76af95 100644 --- a/varsig/src/lib.rs +++ b/varsig/src/lib.rs @@ -1,7 +1,6 @@ use std::io::{Read, Write}; pub mod common; -pub mod eip191; pub mod either; pub mod traits; From 1f008158012f80b52146ea9245d3abbdd4e6834e Mon Sep 17 00:00:00 2001 From: chunningham Date: Thu, 13 Apr 2023 22:40:52 +1000 Subject: [PATCH 15/70] use common error for common sig types --- varsig/src/common/ecdsa.rs | 11 ++--------- varsig/src/common/mod.rs | 8 ++++++++ varsig/src/common/rsa.rs | 11 ++--------- 3 files changed, 12 insertions(+), 18 deletions(-) diff --git a/varsig/src/common/ecdsa.rs b/varsig/src/common/ecdsa.rs index 3c97cdd..ba41edd 100644 --- a/varsig/src/common/ecdsa.rs +++ b/varsig/src/common/ecdsa.rs @@ -1,8 +1,7 @@ -use super::{KECCAK256, SHA256, SHA512}; +use super::{CommonError, KECCAK256, SHA256, SHA512}; use crate::{DeserError, SerError, VarSigTrait}; use std::io::{Read, Write}; use unsigned_varint::{ - decode::Error as VarintError, encode::{u64 as write_u64, u64_buffer}, io::read_u64, }; @@ -34,13 +33,7 @@ pub type Es512 = Ecdsa<{ P521 as u64 }, { SHA512 as u64 }, 128>; pub type Eip191 = Ecdsa<{ K256 as u64 }, { KECCAK256 as u64 }, 65>; -#[derive(thiserror::Error, Debug)] -pub enum EcdsaError { - #[error(transparent)] - Varint(#[from] VarintError), - #[error("Incorrect hash code, expected {0}, got {1}")] - IncorrectHash(u64, u64), -} +pub type EcdsaError = CommonError; impl VarSigTrait for Ecdsa diff --git a/varsig/src/common/mod.rs b/varsig/src/common/mod.rs index ca8aaef..d3224d4 100644 --- a/varsig/src/common/mod.rs +++ b/varsig/src/common/mod.rs @@ -9,3 +9,11 @@ const KECCAK256: u16 = 0x1B; pub use ecdsa::{EcdsaError, Eip191, Es256, Es256K, Es512}; pub use ed25519::{Ed25519, Ed25519Error}; pub use rsa::{Rsa256, Rsa512, RsaError}; + +#[derive(thiserror::Error, Debug)] +pub enum CommonError { + #[error(transparent)] + Varint(#[from] unsigned_varint::decode::Error), + #[error("Incorrect hash code, expected {0:x}, got {1:x}")] + IncorrectHash(u64, u64), +} diff --git a/varsig/src/common/rsa.rs b/varsig/src/common/rsa.rs index 6e6084a..f9940e4 100644 --- a/varsig/src/common/rsa.rs +++ b/varsig/src/common/rsa.rs @@ -1,8 +1,7 @@ -use super::{SHA256, SHA512}; +use super::{CommonError, SHA256, SHA512}; use crate::{DeserError, SerError, VarSigTrait}; use std::io::{Read, Write}; use unsigned_varint::{ - decode::Error as VarintError, encode::{u64 as write_u64, u64_buffer}, io::read_u64, }; @@ -29,13 +28,7 @@ impl Rsa { pub type Rsa256 = Rsa<{ SHA256 as u64 }>; pub type Rsa512 = Rsa<{ SHA512 as u64 }>; -#[derive(thiserror::Error, Debug)] -pub enum RsaError { - #[error(transparent)] - Varint(#[from] VarintError), - #[error("Incorrect hash code, expected {0}, got {1}")] - IncorrectHash(u64, u64), -} +pub type RsaError = CommonError; impl VarSigTrait for Rsa { type SerError = std::convert::Infallible; From 411ddb1bce2d88cf627ecbc3418faeb07087cbd8 Mon Sep 17 00:00:00 2001 From: chunningham Date: Thu, 13 Apr 2023 22:36:39 +1000 Subject: [PATCH 16/70] make everything generic over const encoding value --- varsig/src/common/ecdsa.rs | 41 ++++++++++++++++++------------------ varsig/src/common/ed25519.rs | 36 ++++++++++++++++--------------- varsig/src/common/mod.rs | 8 ++++--- varsig/src/common/rsa.rs | 31 +++++++++++++-------------- 4 files changed, 60 insertions(+), 56 deletions(-) diff --git a/varsig/src/common/ecdsa.rs b/varsig/src/common/ecdsa.rs index ba41edd..ea95cfa 100644 --- a/varsig/src/common/ecdsa.rs +++ b/varsig/src/common/ecdsa.rs @@ -10,36 +10,33 @@ pub const P256: u16 = 0x1200; pub const K256: u16 = 0xe7; pub const P521: u16 = 0x1202; -pub struct Ecdsa { - encoding: u64, +pub struct Ecdsa { bytes: [u8; LEN], } -impl Ecdsa { - pub fn new(encoding: u64, bytes: [u8; LEN]) -> Self { - Self { encoding, bytes } - } - pub fn encoding(&self) -> u64 { - self.encoding +impl + Ecdsa +{ + pub fn new(bytes: [u8; LEN]) -> Self { + Self { bytes } } pub fn bytes(&self) -> &[u8; LEN] { &self.bytes } } -pub type Es256 = Ecdsa<{ P256 as u64 }, { SHA256 as u64 }, 64>; -pub type Es256K = Ecdsa<{ K256 as u64 }, { SHA256 as u64 }, 64>; -pub type Es512 = Ecdsa<{ P521 as u64 }, { SHA512 as u64 }, 128>; - -pub type Eip191 = Ecdsa<{ K256 as u64 }, { KECCAK256 as u64 }, 65>; +pub type Es256 = Ecdsa<{ P256 as u64 }, { SHA256 as u64 }, 64, ENCODING>; +pub type Es256K = Ecdsa<{ K256 as u64 }, { SHA256 as u64 }, 64, ENCODING>; +pub type Es512 = Ecdsa<{ P521 as u64 }, { SHA512 as u64 }, 128, ENCODING>; +pub type Eip191 = Ecdsa<{ K256 as u64 }, { KECCAK256 as u64 }, 65, 0x55>; -pub type EcdsaError = CommonError; +pub type EcdsaError = CommonError; -impl VarSigTrait - for Ecdsa +impl VarSigTrait + for Ecdsa { type SerError = std::convert::Infallible; - type DeserError = EcdsaError; + type DeserError = EcdsaError; fn valid_header(bytes: &[u8]) -> bool { let mut buf = u64_buffer(); @@ -59,13 +56,17 @@ impl VarSigTrait let hash = read_u64(reader.by_ref())?; if hash != HASH { - return Err(DeserError::Format(EcdsaError::IncorrectHash(HASH, hash))); + return Err(DeserError::Format(EcdsaError::IncorrectHash(hash))); }; let encoding = read_u64(reader.by_ref())?; + if encoding != ENCODING { + return Err(DeserError::Format(EcdsaError::IncorrectEncoding(encoding))); + }; + let mut bytes = [0u8; LEN]; reader.read_exact(&mut bytes)?; - Ok(Self { encoding, bytes }) + Ok(Self { bytes }) } fn to_writer(&self, writer: &mut W) -> Result<(), SerError> @@ -75,7 +76,7 @@ impl VarSigTrait let mut buf = u64_buffer(); writer.write_all(write_u64(HEADER, &mut buf))?; writer.write_all(write_u64(HASH, &mut buf))?; - writer.write_all(write_u64(self.encoding, &mut buf))?; + writer.write_all(write_u64(ENCODING, &mut buf))?; writer.write_all(&self.bytes)?; Ok(()) } diff --git a/varsig/src/common/ed25519.rs b/varsig/src/common/ed25519.rs index 367b813..a6a5800 100644 --- a/varsig/src/common/ed25519.rs +++ b/varsig/src/common/ed25519.rs @@ -8,17 +8,13 @@ use unsigned_varint::{ pub const ED25519: u16 = 0xed; -pub struct Ed25519 { - encoding: u64, +pub struct Ed25519 { bytes: [u8; 64], } -impl Ed25519 { - pub fn new(encoding: u64, bytes: [u8; 64]) -> Self { - Self { encoding, bytes } - } - pub fn encoding(&self) -> u64 { - self.encoding +impl Ed25519 { + pub fn new(bytes: [u8; 64]) -> Self { + Self { bytes } } pub fn bytes(&self) -> &[u8; 64] { &self.bytes @@ -26,14 +22,16 @@ impl Ed25519 { } #[derive(thiserror::Error, Debug)] -pub enum Ed25519Error { +pub enum Ed25519Error { #[error(transparent)] Varint(#[from] VarintError), + #[error("Incorrect encoding, expected {:x}, got {0:x}", ENCODING)] + IncorrectEncoding(u64), } -impl VarSigTrait for Ed25519 { +impl VarSigTrait for Ed25519 { type SerError = std::convert::Infallible; - type DeserError = Ed25519Error; + type DeserError = Ed25519Error; fn valid_header(bytes: &[u8]) -> bool { let mut buf = u64_buffer(); @@ -52,9 +50,15 @@ impl VarSigTrait for Ed25519 { }; let encoding = read_u64(reader.by_ref())?; + if encoding != ENCODING { + return Err(DeserError::Format(Ed25519Error::IncorrectEncoding( + encoding, + ))); + } + let mut bytes = [0u8; 64]; reader.read_exact(&mut bytes)?; - Ok(Self { encoding, bytes }) + Ok(Self { bytes }) } fn to_writer(&self, writer: &mut W) -> Result<(), SerError> @@ -63,7 +67,7 @@ impl VarSigTrait for Ed25519 { { let mut buf = u64_buffer(); writer.write_all(write_u64(ED25519 as u64, &mut buf))?; - writer.write_all(write_u64(self.encoding, &mut buf))?; + writer.write_all(write_u64(ENCODING, &mut buf))?; writer.write_all(&self.bytes)?; Ok(()) } @@ -95,24 +99,22 @@ mod tests { #[test] fn basic_roundtrip_1() { - let decoded = VarSig::::from_bytes(EXAMPLE).unwrap(); + let decoded = VarSig::>::from_bytes(EXAMPLE).unwrap(); let encoded = decoded.to_vec().unwrap(); assert_eq!(EXAMPLE, &encoded[..]); - assert_eq!(decoded.sig().encoding(), 0x55); assert_eq!(decoded.sig().bytes().as_ref(), &EXAMPLE[4..]); } #[test] fn basic_roundtrip_2() { let mut reader = EXAMPLE; - let decoded = VarSig::::from_reader(&mut reader).unwrap(); + let decoded = VarSig::>::from_reader(&mut reader).unwrap(); let mut buf = Vec::new(); decoded.to_writer(&mut buf).unwrap(); assert_eq!(EXAMPLE, &buf); - assert_eq!(decoded.sig().encoding(), 0x55); assert_eq!(decoded.sig().bytes().as_ref(), &EXAMPLE[4..]); } } diff --git a/varsig/src/common/mod.rs b/varsig/src/common/mod.rs index d3224d4..cbfc6b8 100644 --- a/varsig/src/common/mod.rs +++ b/varsig/src/common/mod.rs @@ -11,9 +11,11 @@ pub use ed25519::{Ed25519, Ed25519Error}; pub use rsa::{Rsa256, Rsa512, RsaError}; #[derive(thiserror::Error, Debug)] -pub enum CommonError { +pub enum CommonError { #[error(transparent)] Varint(#[from] unsigned_varint::decode::Error), - #[error("Incorrect hash code, expected {0:x}, got {1:x}")] - IncorrectHash(u64, u64), + #[error("Incorrect hash code, expected {:x}, got {1:x}", HASH)] + IncorrectHash(u64), + #[error("Incorrect encoding, expected {:x}, got {0:x}", ENCODING)] + IncorrectEncoding(u64), } diff --git a/varsig/src/common/rsa.rs b/varsig/src/common/rsa.rs index f9940e4..8f8a4eb 100644 --- a/varsig/src/common/rsa.rs +++ b/varsig/src/common/rsa.rs @@ -8,31 +8,27 @@ use unsigned_varint::{ pub const RSA: u16 = 0x1205; -pub struct Rsa { - encoding: u64, +pub struct Rsa { bytes: Vec, } -impl Rsa { - pub fn new(encoding: u64, bytes: Vec) -> Self { - Self { encoding, bytes } - } - pub fn encoding(&self) -> u64 { - self.encoding +impl Rsa { + pub fn new(bytes: Vec) -> Self { + Self { bytes } } pub fn bytes(&self) -> &[u8] { &self.bytes } } -pub type Rsa256 = Rsa<{ SHA256 as u64 }>; -pub type Rsa512 = Rsa<{ SHA512 as u64 }>; +pub type Rsa256 = Rsa<{ SHA256 as u64 }, ENCODING>; +pub type Rsa512 = Rsa<{ SHA512 as u64 }, ENCODING>; -pub type RsaError = CommonError; +pub type RsaError = CommonError; -impl VarSigTrait for Rsa { +impl VarSigTrait for Rsa { type SerError = std::convert::Infallible; - type DeserError = RsaError; + type DeserError = RsaError; fn valid_header(bytes: &[u8]) -> bool { let mut buf = u64_buffer(); @@ -52,15 +48,18 @@ impl VarSigTrait for Rsa { let hash = read_u64(reader.by_ref())?; if hash != HASH { - return Err(DeserError::Format(RsaError::IncorrectHash(HASH, hash))); + return Err(DeserError::Format(RsaError::IncorrectHash(hash))); }; let len = read_u64(reader.by_ref())?; let mut bytes = Vec::with_capacity(len as usize); let encoding = read_u64(reader.by_ref())?; + if encoding != ENCODING { + return Err(DeserError::Format(RsaError::IncorrectEncoding(encoding))); + }; reader.read_exact(&mut bytes)?; - Ok(Self { encoding, bytes }) + Ok(Self { bytes }) } fn to_writer(&self, writer: &mut W) -> Result<(), SerError> @@ -71,7 +70,7 @@ impl VarSigTrait for Rsa { writer.write_all(write_u64(RSA as u64, &mut buf))?; writer.write_all(write_u64(HASH, &mut buf))?; writer.write_all(write_u64(self.bytes.len() as u64, &mut buf))?; - writer.write_all(write_u64(self.encoding, &mut buf))?; + writer.write_all(write_u64(ENCODING, &mut buf))?; writer.write_all(&self.bytes)?; Ok(()) } From e2a817f0586fad74212d13196e2913dfc8c89699 Mon Sep 17 00:00:00 2001 From: chunningham Date: Thu, 13 Apr 2023 23:06:04 +1000 Subject: [PATCH 17/70] convenience derives, add encoding constants and test --- varsig/src/common/ecdsa.rs | 6 ++++-- varsig/src/common/ed25519.rs | 9 +++++++++ varsig/src/common/mod.rs | 8 +++++++- varsig/src/common/rsa.rs | 1 + varsig/src/lib.rs | 1 + 5 files changed, 22 insertions(+), 3 deletions(-) diff --git a/varsig/src/common/ecdsa.rs b/varsig/src/common/ecdsa.rs index ea95cfa..bf9e311 100644 --- a/varsig/src/common/ecdsa.rs +++ b/varsig/src/common/ecdsa.rs @@ -1,4 +1,4 @@ -use super::{CommonError, KECCAK256, SHA256, SHA512}; +use super::{CommonError, EIP191_ENCODING, KECCAK256, SHA256, SHA512}; use crate::{DeserError, SerError, VarSigTrait}; use std::io::{Read, Write}; use unsigned_varint::{ @@ -10,6 +10,7 @@ pub const P256: u16 = 0x1200; pub const K256: u16 = 0xe7; pub const P521: u16 = 0x1202; +#[derive(Debug, Clone, PartialEq, Eq)] pub struct Ecdsa { bytes: [u8; LEN], } @@ -28,7 +29,8 @@ impl pub type Es256 = Ecdsa<{ P256 as u64 }, { SHA256 as u64 }, 64, ENCODING>; pub type Es256K = Ecdsa<{ K256 as u64 }, { SHA256 as u64 }, 64, ENCODING>; pub type Es512 = Ecdsa<{ P521 as u64 }, { SHA512 as u64 }, 128, ENCODING>; -pub type Eip191 = Ecdsa<{ K256 as u64 }, { KECCAK256 as u64 }, 65, 0x55>; +pub type Ethereum = Ecdsa<{ K256 as u64 }, { KECCAK256 as u64 }, 65, ENCODING>; +pub type Eip191 = Ethereum; pub type EcdsaError = CommonError; diff --git a/varsig/src/common/ed25519.rs b/varsig/src/common/ed25519.rs index a6a5800..657f809 100644 --- a/varsig/src/common/ed25519.rs +++ b/varsig/src/common/ed25519.rs @@ -8,6 +8,7 @@ use unsigned_varint::{ pub const ED25519: u16 = 0xed; +#[derive(Debug, Clone, PartialEq, Eq)] pub struct Ed25519 { bytes: [u8; 64], } @@ -117,4 +118,12 @@ mod tests { assert_eq!(EXAMPLE, &buf); assert_eq!(decoded.sig().bytes().as_ref(), &EXAMPLE[4..]); } + + #[test] + fn enforce_encoding() { + match VarSig::>::from_bytes(EXAMPLE) { + Err(crate::Error::Format(Ed25519Error::IncorrectEncoding(0x55))) => {} + _ => panic!("Expected error"), + }; + } } diff --git a/varsig/src/common/mod.rs b/varsig/src/common/mod.rs index cbfc6b8..aac99e0 100644 --- a/varsig/src/common/mod.rs +++ b/varsig/src/common/mod.rs @@ -6,6 +6,12 @@ const SHA256: u16 = 0x12; const SHA512: u16 = 0x13; const KECCAK256: u16 = 0x1B; +pub const RAW_ENCODING: u64 = 0x55; +pub const EIP191_ENCODING: u64 = 0xe191; +pub const DAG_JSON_ENCODING: u64 = 0x0129; +pub const DAG_PROTOBUF_ENCODING: u64 = 0x70; +pub const DAG_CBOR_ENCODING: u64 = 0x71; + pub use ecdsa::{EcdsaError, Eip191, Es256, Es256K, Es512}; pub use ed25519::{Ed25519, Ed25519Error}; pub use rsa::{Rsa256, Rsa512, RsaError}; @@ -14,7 +20,7 @@ pub use rsa::{Rsa256, Rsa512, RsaError}; pub enum CommonError { #[error(transparent)] Varint(#[from] unsigned_varint::decode::Error), - #[error("Incorrect hash code, expected {:x}, got {1:x}", HASH)] + #[error("Incorrect hash code, expected {:x}, got {0:x}", HASH)] IncorrectHash(u64), #[error("Incorrect encoding, expected {:x}, got {0:x}", ENCODING)] IncorrectEncoding(u64), diff --git a/varsig/src/common/rsa.rs b/varsig/src/common/rsa.rs index 8f8a4eb..e43246f 100644 --- a/varsig/src/common/rsa.rs +++ b/varsig/src/common/rsa.rs @@ -8,6 +8,7 @@ use unsigned_varint::{ pub const RSA: u16 = 0x1205; +#[derive(Debug, Clone, PartialEq, Eq)] pub struct Rsa { bytes: Vec, } diff --git a/varsig/src/lib.rs b/varsig/src/lib.rs index e76af95..434187d 100644 --- a/varsig/src/lib.rs +++ b/varsig/src/lib.rs @@ -9,6 +9,7 @@ pub use traits::{DeserError, SerError, VarSigTrait}; const VARSIG_VARINT_PREFIX: u8 = 0x34; +#[derive(Debug, Clone, PartialEq, Eq)] pub struct VarSig(S); impl VarSig { From b261ae855c4141862ffd06b3607088fef1a0723d Mon Sep 17 00:00:00 2001 From: chunningham Date: Thu, 13 Apr 2023 23:36:29 +1000 Subject: [PATCH 18/70] more exports, make jose sig type, fix EitherSig header check --- varsig/src/common/mod.rs | 9 ++++++++- varsig/src/either.rs | 3 ++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/varsig/src/common/mod.rs b/varsig/src/common/mod.rs index aac99e0..ca6e518 100644 --- a/varsig/src/common/mod.rs +++ b/varsig/src/common/mod.rs @@ -12,7 +12,7 @@ pub const DAG_JSON_ENCODING: u64 = 0x0129; pub const DAG_PROTOBUF_ENCODING: u64 = 0x70; pub const DAG_CBOR_ENCODING: u64 = 0x71; -pub use ecdsa::{EcdsaError, Eip191, Es256, Es256K, Es512}; +pub use ecdsa::{EcdsaError, Eip191, Es256, Es256K, Es512, Ethereum}; pub use ed25519::{Ed25519, Ed25519Error}; pub use rsa::{Rsa256, Rsa512, RsaError}; @@ -25,3 +25,10 @@ pub enum CommonError { #[error("Incorrect encoding, expected {:x}, got {0:x}", ENCODING)] IncorrectEncoding(u64), } + +use crate::EitherSignature; + +pub type JoseCommon = EitherSignature< + EitherSignature, Es512>, EitherSignature, Rsa512>>, + EitherSignature, Es256K>, +>; diff --git a/varsig/src/either.rs b/varsig/src/either.rs index b94f376..cb00531 100644 --- a/varsig/src/either.rs +++ b/varsig/src/either.rs @@ -1,6 +1,7 @@ use crate::{DeserError, SerError, VarSigTrait}; use std::io::{Read, Write}; +#[derive(Debug, Clone, PartialEq, Eq)] pub enum EitherSignature { A(A), B(B), @@ -31,7 +32,7 @@ where Self: Sized, { // check the header to discern the sig type - let mut buf = [0u8; 4]; + let mut buf = [0u8; 10]; reader.read_exact(&mut buf)?; if A::valid_header(&buf) { From 87112f6220f856377a8f952d5d32708df414d4e6 Mon Sep 17 00:00:00 2001 From: chunningham Date: Fri, 14 Apr 2023 09:26:52 +1000 Subject: [PATCH 19/70] export common modules, remove commented test --- varsig/src/common/mod.rs | 6 +++--- varsig/src/lib.rs | 41 ---------------------------------------- 2 files changed, 3 insertions(+), 44 deletions(-) diff --git a/varsig/src/common/mod.rs b/varsig/src/common/mod.rs index ca6e518..11588c6 100644 --- a/varsig/src/common/mod.rs +++ b/varsig/src/common/mod.rs @@ -1,6 +1,6 @@ -mod ecdsa; -mod ed25519; -mod rsa; +pub mod ecdsa; +pub mod ed25519; +pub mod rsa; const SHA256: u16 = 0x12; const SHA512: u16 = 0x13; diff --git a/varsig/src/lib.rs b/varsig/src/lib.rs index 434187d..dfe8bab 100644 --- a/varsig/src/lib.rs +++ b/varsig/src/lib.rs @@ -78,44 +78,3 @@ impl From> for Error { } } } - -#[cfg(test)] -mod tests { - // use super::*; - - // const EXAMPLE: [u8; 15] = [ - // 0x68, 0xd2, 0x04, 0xda, 0x03, 0x24, 0xde, 0xb7, 0xde, 0x9a, 0xf1, 0xd9, 0xa2, 0xa3, 0x02, - // ]; - - // #[test] - // fn basic_roundtrip() { - // let varsig = VarSigTrait::from_reader(&mut EXAMPLE.as_ref()).unwrap(); - // assert_eq!(varsig.codec(), 0x04d2); - // assert_eq!(varsig.hash(), 0x03da); - // assert_eq!(varsig.key_type(), 0x24); - // assert_eq!(varsig.signature(), &EXAMPLE[6..]); - - // assert_eq!(&varsig.to_vec().unwrap(), &EXAMPLE); - // } - - // #[test] - // fn reverse_roundtrip() { - // let varsig = VarSigTrait::new(0x0129, 0x12, 0xed, EXAMPLE[6..].to_vec()); - // let encoded = varsig.to_vec().unwrap(); - // let decoded = VarSigTrait::from_bytes(&encoded.as_ref()).unwrap(); - // assert_eq!(varsig, decoded); - // } - - // #[test] - // fn basic_ser() { - // let varsig = VarSigTrait::new(0x0129, 0x12, 0xed, EXAMPLE[6..].to_vec()); - // assert_eq!(&varsig.to_vec().unwrap(), &EXAMPLE); - // } - - // #[test] - // fn rsad() { - // let b = [0xd2, 0x04]; - // println!("{:#x?}", read_u64(&mut b.as_ref())); - // None::.unwrap(); - // } -} From ffe63f08a12de6a1ada76a2a503c3cfebf1b971d Mon Sep 17 00:00:00 2001 From: chunningham Date: Wed, 19 Apr 2023 18:29:50 +1000 Subject: [PATCH 20/70] varsig minor utils --- varsig/src/common/ecdsa.rs | 2 +- varsig/src/lib.rs | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/varsig/src/common/ecdsa.rs b/varsig/src/common/ecdsa.rs index bf9e311..c98b6e8 100644 --- a/varsig/src/common/ecdsa.rs +++ b/varsig/src/common/ecdsa.rs @@ -10,7 +10,7 @@ pub const P256: u16 = 0x1200; pub const K256: u16 = 0xe7; pub const P521: u16 = 0x1202; -#[derive(Debug, Clone, PartialEq, Eq)] +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct Ecdsa { bytes: [u8; LEN], } diff --git a/varsig/src/lib.rs b/varsig/src/lib.rs index dfe8bab..f2a5326 100644 --- a/varsig/src/lib.rs +++ b/varsig/src/lib.rs @@ -10,9 +10,9 @@ pub use traits::{DeserError, SerError, VarSigTrait}; const VARSIG_VARINT_PREFIX: u8 = 0x34; #[derive(Debug, Clone, PartialEq, Eq)] -pub struct VarSig(S); +pub struct VarSig(S); -impl VarSig { +impl VarSig { pub fn new(s: S) -> Self { Self(s) } @@ -20,7 +20,9 @@ impl VarSig { pub fn sig(&self) -> &S { &self.0 } +} +impl VarSig { pub fn from_reader(reader: &mut R) -> Result> where Self: Sized, From 24920b45df36c3bbd03f5493a37382610e47f241 Mon Sep 17 00:00:00 2001 From: chunningham Date: Wed, 19 Apr 2023 18:31:53 +1000 Subject: [PATCH 21/70] fill out pkh byte parsing --- multidid/src/error.rs | 2 + multidid/src/lib.rs | 15 ++-- multidid/src/pkh.rs | 204 +++++++++++++++++++++++++++++++++++++++--- 3 files changed, 205 insertions(+), 16 deletions(-) diff --git a/multidid/src/error.rs b/multidid/src/error.rs index d152115..967eb9d 100644 --- a/multidid/src/error.rs +++ b/multidid/src/error.rs @@ -6,6 +6,8 @@ pub enum Error { Io(#[from] std::io::Error), #[error("Invalid multidid varint prefix, expected 0x9d1a, recieved {0:x}")] InvalidPrefix(u64), + #[error("Invalid did-pkh type varint: {0:x}")] + InvalidCodec(u64), #[error(transparent)] Parameter(#[from] iri_string::validate::Error), #[error(transparent)] diff --git a/multidid/src/lib.rs b/multidid/src/lib.rs index 43dd258..56c3e61 100644 --- a/multidid/src/lib.rs +++ b/multidid/src/lib.rs @@ -63,10 +63,15 @@ impl MultiDid { } pub fn to_vec(&self) -> Vec { - match (self.query, self.fragment) { - (Some(q), Some(f)) => {} - (None, Some(f)) => {} - (Some(q), None) => {} + match (&self.query, &self.fragment) { + (Some(q), Some(f)) => [ + self.method.to_vec(), + q.as_str().as_bytes().to_vec(), + f.as_str().as_bytes().to_vec(), + ] + .concat(), + (None, Some(f)) => [self.method.to_vec(), f.as_str().as_bytes().to_vec()].concat(), + (Some(q), None) => [self.method.to_vec(), q.as_str().as_bytes().to_vec()].concat(), (None, None) => self.method.to_vec(), } } @@ -201,7 +206,7 @@ mod tests { let did = MultiDid::from_reader(&mut test.encoded.as_slice()).unwrap(); assert_eq!(did.query, test.query); assert_eq!(did.fragment, test.fragment); - assert_eq!(did.to_vec().unwrap(), test.encoded); + assert_eq!(did.to_vec(), test.encoded); assert!(match (did.method(), test.method.as_str()) { (Method::Key(_), "key") => true, (Method::Pkh(_), "pkh") => true, diff --git a/multidid/src/pkh.rs b/multidid/src/pkh.rs index ca1b85a..234c35b 100644 --- a/multidid/src/pkh.rs +++ b/multidid/src/pkh.rs @@ -1,39 +1,221 @@ use crate::Error; use std::io::{Error as IoError, Read, Write}; +use unsigned_varint::{ + encode::{u64 as write_u64, u64_buffer}, + io::read_u64, +}; pub(crate) const PKH_CODEC: u64 = 0xca; #[derive(Debug, Clone, PartialEq)] pub enum DidPkhTypes { - Eip { chain_id: u64, address: [u8; 20] }, + Bip122(Caip10<[u8; 32], [u8; 25]>), + Eip155(Caip10), + Cosmos(Caip10), + Starknet(Caip10), + Hedera(Caip10), + Lip9(Caip10<[u8; 32], [u8; 20]>), } -impl std::fmt::Display for DidPkhTypes { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { +#[derive(Debug, Clone, PartialEq)] +pub struct Caip10 { + chain_id: C, + address: A, +} + +impl Caip10 { + pub fn new(chain_id: C, address: A) -> Self { + Self { chain_id, address } + } + pub fn chain_id(&self) -> &C { + &self.chain_id + } + pub fn address(&self) -> &A { + &self.address + } +} + +#[derive(Debug, Clone, PartialEq)] +pub enum CosmosAddress { + Secp256k1([u8; 20]), + Secp256r1([u8; 32]), +} + +impl CosmosAddress { + fn bytes(&self) -> &[u8] { match self { - // TODO correct address formatting - Self::Eip { chain_id, address } => { - todo!() - } + CosmosAddress::Secp256k1(address) => address, + CosmosAddress::Secp256r1(address) => address, } } } impl DidPkhTypes { + fn caip_2_code(&self) -> &'static str { + use DidPkhTypes::*; + match self { + Bip122(_) => "bip122", + Eip155(_) => "eip155", + Cosmos(_) => "cosmos", + Starknet(_) => "starknet", + Hedera(_) => "hedera", + Lip9(_) => "lip9", + } + } + pub fn codec(&self) -> u64 { PKH_CODEC } - pub(crate) fn from_reader(reader: &mut R) -> Result + + pub fn to_vec(&self) -> Result, IoError> { + let mut buf = Vec::new(); + self.to_writer(&mut buf)?; + Ok(buf) + } + + pub fn from_reader(reader: &mut R) -> Result where R: Read, { - todo!() + let mut buf = [0u8; 1]; + reader.read_exact(&mut buf)?; + let codec = buf[0]; + if codec as u64 != PKH_CODEC { + return Err(Error::InvalidCodec(codec as u64)); + } + let pkh_type = read_u64(reader.by_ref())?; + match pkh_type { + // bitcoin-like + 1 => { + let mut chain_id = [0u8; 32]; + reader.read_exact(&mut chain_id)?; + let mut address = [0u8; 25]; + reader.read_exact(&mut address)?; + Ok(DidPkhTypes::Bip122(Caip10::new(chain_id, address))) + } + // ethereum-like + 2 => { + let ref_len = read_u64(reader.by_ref())?; + let chain_id = vec![0u8; ref_len as usize]; + reader.read_exact(&mut chain_id)?; + let mut address = [0u8; 20]; + reader.read_exact(&mut address)?; + Ok(DidPkhTypes::Eip155(Caip10::new( + String::from_utf8(chain_id)?, + address, + ))) + } + // cosmos + 3 => { + let ref_len = read_u64(reader.by_ref())?; + let chain_id = vec![0u8; ref_len as usize]; + reader.read_exact(&mut chain_id)?; + let address_len = read_u64(reader.by_ref())?; + Ok(DidPkhTypes::Cosmos(Caip10::new( + String::from_utf8(chain_id)?, + match address_len { + 20 => { + let mut address = [0u8; 20]; + reader.read_exact(&mut address)?; + CosmosAddress::Secp256k1(address) + } + 32 => { + let mut address = [0u8; 32]; + reader.read_exact(&mut address)?; + CosmosAddress::Secp256r1(address) + } + }, + ))) + } + // starknet + 4 => { + let ref_len = read_u64(reader.by_ref())?; + let chain_id = vec![0u8; ref_len as usize]; + reader.read_exact(&mut chain_id)?; + let mut address = [0u8; 20]; + reader.read_exact(&mut address)?; + Ok(DidPkhTypes::Starknet(Caip10::new( + String::from_utf8(chain_id)?, + address, + ))) + } + // hedera + 5 => { + let ref_len = read_u64(reader.by_ref())?; + let chain_id = vec![0u8; ref_len as usize]; + reader.read_exact(&mut chain_id)?; + let shard = read_u64(reader.by_ref())?; + let realm = read_u64(reader.by_ref())?; + let account = read_u64(reader.by_ref())?; + Ok(DidPkhTypes::Hedera(Caip10::new( + String::from_utf8(chain_id)?, + (shard, realm, account), + ))) + } + // lip9 + 6 => { + let mut chain_id = [0u8; 32]; + reader.read_exact(&mut chain_id)?; + let mut address = [0u8; 20]; + reader.read_exact(&mut address)?; + Ok(DidPkhTypes::Lip9(Caip10::new(chain_id, address))) + } + } } - pub(crate) fn to_writer(&self, writer: &mut W) -> Result<(), IoError> + pub fn to_writer(&self, writer: &mut W) -> Result<(), IoError> where W: ?Sized + Write, { - todo!() + writer.write_all(&[PKH_CODEC as u8])?; + let mut buf = u64_buffer(); + match self { + DidPkhTypes::Bip122(caip10) => { + writer.write_all(write_u64(1, &mut buf))?; + writer.write_all(caip10.chain_id())?; + writer.write_all(caip10.address())?; + } + DidPkhTypes::Eip155(caip10) => { + writer.write_all(write_u64(2, &mut buf))?; + writer.write_all(write_u64(caip10.chain_id().len() as u64, &mut buf))?; + writer.write_all(caip10.chain_id().as_bytes())?; + writer.write_all(caip10.address())?; + } + DidPkhTypes::Cosmos(caip10) => { + writer.write_all(write_u64(3, &mut buf))?; + writer.write_all(write_u64(caip10.chain_id().len() as u64, &mut buf))?; + writer.write_all(caip10.chain_id().as_bytes())?; + match caip10.address() { + CosmosAddress::Secp256k1(address) => { + writer.write_all(write_u64(20, &mut buf))?; + writer.write_all(address)?; + } + CosmosAddress::Secp256r1(address) => { + writer.write_all(write_u64(32, &mut buf))?; + writer.write_all(address)?; + } + } + } + DidPkhTypes::Starknet(caip10) => { + writer.write_all(write_u64(3, &mut buf))?; + writer.write_all(write_u64(caip10.chain_id().len() as u64, &mut buf))?; + writer.write_all(caip10.chain_id().as_bytes())?; + writer.write_all(caip10.address())?; + } + DidPkhTypes::Hedera(caip10) => { + writer.write_all(write_u64(3, &mut buf))?; + writer.write_all(write_u64(caip10.chain_id().len() as u64, &mut buf))?; + writer.write_all(caip10.chain_id().as_bytes())?; + writer.write_all(write_u64(caip10.address().0, &mut buf))?; + writer.write_all(write_u64(caip10.address().1, &mut buf))?; + writer.write_all(write_u64(caip10.address().2, &mut buf))?; + } + DidPkhTypes::Lip9(caip10) => { + writer.write_all(write_u64(3, &mut buf))?; + writer.write_all(caip10.chain_id())?; + writer.write_all(caip10.address())?; + } + }; + Ok(()) } } From 5fa1d62eec12b2e76589adf62d173e4020485969 Mon Sep 17 00:00:00 2001 From: chunningham Date: Thu, 20 Apr 2023 16:55:35 +1000 Subject: [PATCH 22/70] fix multidid --- multidid/src/error.rs | 2 ++ multidid/src/key.rs | 6 ---- multidid/src/lib.rs | 13 --------- multidid/src/method.rs | 18 +++++------- multidid/src/pkh.rs | 66 +++++++++++++++++++++++++++++++++++++----- 5 files changed, 68 insertions(+), 37 deletions(-) diff --git a/multidid/src/error.rs b/multidid/src/error.rs index 967eb9d..a624dea 100644 --- a/multidid/src/error.rs +++ b/multidid/src/error.rs @@ -12,4 +12,6 @@ pub enum Error { Parameter(#[from] iri_string::validate::Error), #[error(transparent)] DidParse(#[from] std::string::FromUtf8Error), + #[error("multidid formatting error: {0}")] + Format(&'static str), } diff --git a/multidid/src/key.rs b/multidid/src/key.rs index 7b15fa1..66301c7 100644 --- a/multidid/src/key.rs +++ b/multidid/src/key.rs @@ -24,12 +24,6 @@ pub enum DidKeyTypes { // RSA([u8; ??]), } -impl std::fmt::Display for DidKeyTypes { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - todo!() - } -} - impl DidKeyTypes { pub fn codec(&self) -> u64 { use DidKeyTypes::*; diff --git a/multidid/src/lib.rs b/multidid/src/lib.rs index 56c3e61..0b6e358 100644 --- a/multidid/src/lib.rs +++ b/multidid/src/lib.rs @@ -24,19 +24,6 @@ pub struct MultiDid { query: Option, } -impl std::fmt::Display for MultiDid { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}", self.method)?; - if let Some(fragment) = &self.fragment { - write!(f, "#{}", fragment)?; - } - if let Some(query) = &self.query { - write!(f, "?{}", query)?; - } - Ok(()) - } -} - impl MultiDid { pub fn new( method: Method, diff --git a/multidid/src/method.rs b/multidid/src/method.rs index 6b00aae..3496cc1 100644 --- a/multidid/src/method.rs +++ b/multidid/src/method.rs @@ -11,16 +11,6 @@ pub enum Method { Raw(String), } -impl std::fmt::Display for Method { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - Method::Raw(raw) => write!(f, "did:{}", raw), - Method::Key(key) => write!(f, "did:key:{}", key), - Method::Pkh(pkh) => write!(f, "did:pkh:{}", pkh), - } - } -} - impl Method { pub fn codec(&self) -> u64 { use Method::*; @@ -31,6 +21,14 @@ impl Method { } } + pub(crate) fn to_vec(&self) -> Vec { + match self { + Self::Raw(raw) => raw.as_bytes().to_vec(), + Self::Pkh(h) => h.to_vec(), + Self::Key(k) => k.bytes().to_vec(), + } + } + pub(crate) fn from_reader(reader: &mut R) -> Result { let codec = read_u64(reader.by_ref())?; match codec { diff --git a/multidid/src/pkh.rs b/multidid/src/pkh.rs index 234c35b..c36690f 100644 --- a/multidid/src/pkh.rs +++ b/multidid/src/pkh.rs @@ -67,10 +67,58 @@ impl DidPkhTypes { PKH_CODEC } - pub fn to_vec(&self) -> Result, IoError> { - let mut buf = Vec::new(); - self.to_writer(&mut buf)?; - Ok(buf) + pub fn to_vec(&self) -> Vec { + let mut vec = Vec::new(); + vec.push(PKH_CODEC as u8); + let mut buf = u64_buffer(); + match self { + DidPkhTypes::Bip122(caip10) => { + vec.extend(write_u64(1, &mut buf)); + vec.extend(caip10.chain_id()); + vec.extend(caip10.address()); + } + DidPkhTypes::Eip155(caip10) => { + vec.extend(write_u64(2, &mut buf)); + vec.extend(write_u64(caip10.chain_id().len() as u64, &mut buf)); + vec.extend(caip10.chain_id().as_bytes()); + vec.extend(caip10.address()); + } + DidPkhTypes::Cosmos(caip10) => { + vec.extend(write_u64(3, &mut buf)); + vec.extend(write_u64(caip10.chain_id().len() as u64, &mut buf)); + vec.extend(caip10.chain_id().as_bytes()); + match caip10.address() { + CosmosAddress::Secp256k1(address) => { + vec.extend(write_u64(20, &mut buf)); + vec.extend(address); + } + CosmosAddress::Secp256r1(address) => { + vec.extend(write_u64(32, &mut buf)); + vec.extend(address); + } + } + } + DidPkhTypes::Starknet(caip10) => { + vec.extend(write_u64(3, &mut buf)); + vec.extend(write_u64(caip10.chain_id().len() as u64, &mut buf)); + vec.extend(caip10.chain_id().as_bytes()); + vec.extend(caip10.address()); + } + DidPkhTypes::Hedera(caip10) => { + vec.extend(write_u64(3, &mut buf)); + vec.extend(write_u64(caip10.chain_id().len() as u64, &mut buf)); + vec.extend(caip10.chain_id().as_bytes()); + vec.extend(write_u64(caip10.address().0, &mut buf)); + vec.extend(write_u64(caip10.address().1, &mut buf)); + vec.extend(write_u64(caip10.address().2, &mut buf)); + } + DidPkhTypes::Lip9(caip10) => { + vec.extend(write_u64(3, &mut buf)); + vec.extend(caip10.chain_id()); + vec.extend(caip10.address()); + } + }; + vec } pub fn from_reader(reader: &mut R) -> Result @@ -96,7 +144,7 @@ impl DidPkhTypes { // ethereum-like 2 => { let ref_len = read_u64(reader.by_ref())?; - let chain_id = vec![0u8; ref_len as usize]; + let mut chain_id = vec![0u8; ref_len as usize]; reader.read_exact(&mut chain_id)?; let mut address = [0u8; 20]; reader.read_exact(&mut address)?; @@ -108,7 +156,7 @@ impl DidPkhTypes { // cosmos 3 => { let ref_len = read_u64(reader.by_ref())?; - let chain_id = vec![0u8; ref_len as usize]; + let mut chain_id = vec![0u8; ref_len as usize]; reader.read_exact(&mut chain_id)?; let address_len = read_u64(reader.by_ref())?; Ok(DidPkhTypes::Cosmos(Caip10::new( @@ -124,13 +172,14 @@ impl DidPkhTypes { reader.read_exact(&mut address)?; CosmosAddress::Secp256r1(address) } + _ => return Err(Error::Format("Invalid cosmos address length")), }, ))) } // starknet 4 => { let ref_len = read_u64(reader.by_ref())?; - let chain_id = vec![0u8; ref_len as usize]; + let mut chain_id = vec![0u8; ref_len as usize]; reader.read_exact(&mut chain_id)?; let mut address = [0u8; 20]; reader.read_exact(&mut address)?; @@ -142,7 +191,7 @@ impl DidPkhTypes { // hedera 5 => { let ref_len = read_u64(reader.by_ref())?; - let chain_id = vec![0u8; ref_len as usize]; + let mut chain_id = vec![0u8; ref_len as usize]; reader.read_exact(&mut chain_id)?; let shard = read_u64(reader.by_ref())?; let realm = read_u64(reader.by_ref())?; @@ -160,6 +209,7 @@ impl DidPkhTypes { reader.read_exact(&mut address)?; Ok(DidPkhTypes::Lip9(Caip10::new(chain_id, address))) } + _ => Err(Error::Format("Unsupported did-pkh discriminant")), } } From 6728dad5f1bb5e68d110c35ceb0a109d3e2ba743 Mon Sep 17 00:00:00 2001 From: chunningham Date: Thu, 20 Apr 2023 17:40:37 +1000 Subject: [PATCH 23/70] updates to cacao v2 --- Cargo.toml | 1 + src/v2/mod.rs | 50 ++++++++++++++++++++-------------------- src/v2/serde_util.rs | 54 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 79 insertions(+), 26 deletions(-) create mode 100644 src/v2/serde_util.rs diff --git a/Cargo.toml b/Cargo.toml index 63d53d9..9c0004b 100755 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,6 +24,7 @@ async-trait = "0.1" serde = "1" serde_json = "1" libipld = { version = "0.16", default-features = false, features = ["derive", "serde-codec", "dag-cbor"]} +ucan-capabilities-object = { version = "0.1", path = "../ucan-capabilities" } serde_with = "2" time = { version = "0.3", features = ["parsing", "formatting"] } http = "0.2.5" diff --git a/src/v2/mod.rs b/src/v2/mod.rs index f65b88b..2b0cda3 100644 --- a/src/v2/mod.rs +++ b/src/v2/mod.rs @@ -1,57 +1,55 @@ -use std::fmt::Debug; - -use async_trait::async_trait; -use iri_string::types::UriString; use libipld::cid::Cid; use serde::{Deserialize, Serialize}; use serde_json::Value; -use serde_with::{serde_as, serde_conv, DeserializeFromStr, SerializeDisplay}; -use std::collections::BTreeMap; +use serde_with::serde_as; +use std::fmt::Debug; +use ucan_capabilities_object::Capabilities; +pub mod either; pub mod recap_cacao; +pub mod ucan_cacao; + +pub mod serde_util; use multidid::MultiDid; -use varsig::{SignatureHeader, VarSig}; +use serde_util::{MultiDidAsBytes, VarSigAsBytes}; +use varsig::{VarSig, VarSigTrait}; #[serde_as] #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] -pub struct CACAO { +pub struct CACAO { + #[serde_as(as = "MultiDidAsBytes")] #[serde(rename = "iss")] issuer: MultiDid, + #[serde_as(as = "MultiDidAsBytes")] #[serde(rename = "aud")] audience: MultiDid, + #[serde_as(as = "VarSigAsBytes")] #[serde(rename = "s")] - signature: VarSig, + signature: VarSig, #[serde(rename = "v")] version: String, #[serde(rename = "att")] - attenuations: BTreeMap>>>, - #[serde(rename = "nnc")] - nonce: String, + attenuations: Capabilities, + #[serde(rename = "nnc", skip_serializing_if = "Option::is_none")] + nonce: Option, #[serde(rename = "prf", skip_serializing_if = "Vec::is_empty", default)] proof: Vec, - #[serde(rename = "iat")] + #[serde(rename = "iat", skip_serializing_if = "Option::is_none", default)] issued_at: Option, - #[serde(rename = "nbf")] + #[serde(rename = "nbf", skip_serializing_if = "Option::is_none", default)] not_before: Option, - #[serde(rename = "exp")] + #[serde(rename = "exp", skip_serializing_if = "Option::is_none", default)] expiration: Option, - #[serde(rename = "fct")] - facts: P::Facts, + #[serde(rename = "fct", skip_serializing_if = "Option::is_none", default)] + facts: Option, } pub trait CacaoProfile { - type Signature: SignatureHeader; - type Facts: for<'d> Deserialize<'d> + Serialize + Clone + Debug; + type Signature: VarSigTrait; + type Facts: Serialize + for<'d> Deserialize<'d>; } -serde_conv!( - MultiDidToBytes, - MultiDid, - |did: &MultiDid| did.to_vec(), - |value: &[u8]| MultiDid::from_bytes(value) -); - #[cfg(test)] pub mod tests { use super::*; diff --git a/src/v2/serde_util.rs b/src/v2/serde_util.rs new file mode 100644 index 0000000..7713e22 --- /dev/null +++ b/src/v2/serde_util.rs @@ -0,0 +1,54 @@ +use multidid::MultiDid; +use serde::Deserialize; +use serde_with::{DeserializeAs, SerializeAs}; +use varsig::{VarSig, VarSigTrait}; + +pub struct MultiDidAsBytes; + +impl SerializeAs for MultiDidAsBytes { + fn serialize_as(source: &MultiDid, serializer: S) -> Result + where + S: serde::Serializer, + { + serializer.serialize_bytes(&source.to_vec()) + } +} + +impl<'de> DeserializeAs<'de, MultiDid> for MultiDidAsBytes { + fn deserialize_as(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + let v = Vec::::deserialize(deserializer)?; + let mut b: &[u8] = v.as_ref(); + MultiDid::from_reader(&mut b).map_err(serde::de::Error::custom) + } +} + +pub struct VarSigAsBytes; + +impl SerializeAs> for VarSigAsBytes +where + V: VarSigTrait, +{ + fn serialize_as(source: &VarSig, serializer: S) -> Result + where + S: serde::Serializer, + { + serializer.serialize_bytes(&source.to_vec().map_err(serde::ser::Error::custom)?) + } +} + +impl<'de, V> DeserializeAs<'de, VarSig> for VarSigAsBytes +where + V: VarSigTrait, +{ + fn deserialize_as(deserializer: D) -> Result, D::Error> + where + D: serde::Deserializer<'de>, + { + let v = Vec::::deserialize(deserializer)?; + let mut b: &[u8] = v.as_ref(); + VarSig::from_reader(&mut b).map_err(serde::de::Error::custom) + } +} From 31e6a21902f9163ba038b17fbed41b9cfb86d50d Mon Sep 17 00:00:00 2001 From: chunningham Date: Tue, 25 Apr 2023 11:53:15 +0100 Subject: [PATCH 24/70] use published uco crate --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 9c0004b..dfb6b7a 100755 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,7 +24,7 @@ async-trait = "0.1" serde = "1" serde_json = "1" libipld = { version = "0.16", default-features = false, features = ["derive", "serde-codec", "dag-cbor"]} -ucan-capabilities-object = { version = "0.1", path = "../ucan-capabilities" } +ucan-capabilities-object = "0.1" serde_with = "2" time = { version = "0.3", features = ["parsing", "formatting"] } http = "0.2.5" From 6913326c2ae273869762a97912079cad1247a5d2 Mon Sep 17 00:00:00 2001 From: chunningham Date: Wed, 26 Apr 2023 14:23:11 +0200 Subject: [PATCH 25/70] use T over &mut T for io, add JoseCommon --- varsig/src/common/ecdsa.rs | 6 +- varsig/src/common/ed25519.rs | 6 +- varsig/src/common/mod.rs | 127 +++++++++++++++++++++++++++++++++-- varsig/src/common/rsa.rs | 6 +- varsig/src/either.rs | 6 +- varsig/src/traits.rs | 6 +- 6 files changed, 137 insertions(+), 20 deletions(-) diff --git a/varsig/src/common/ecdsa.rs b/varsig/src/common/ecdsa.rs index c98b6e8..bbbc35f 100644 --- a/varsig/src/common/ecdsa.rs +++ b/varsig/src/common/ecdsa.rs @@ -46,7 +46,7 @@ impl Some(h) == bytes.get(..h.len()) } - fn from_reader(reader: &mut R) -> Result> + fn from_reader(mut reader: R) -> Result> where Self: Sized, R: Read, @@ -71,9 +71,9 @@ impl Ok(Self { bytes }) } - fn to_writer(&self, writer: &mut W) -> Result<(), SerError> + fn to_writer(&self, mut writer: W) -> Result<(), SerError> where - W: ?Sized + Write, + W: Write, { let mut buf = u64_buffer(); writer.write_all(write_u64(HEADER, &mut buf))?; diff --git a/varsig/src/common/ed25519.rs b/varsig/src/common/ed25519.rs index 657f809..6270582 100644 --- a/varsig/src/common/ed25519.rs +++ b/varsig/src/common/ed25519.rs @@ -40,7 +40,7 @@ impl VarSigTrait for Ed25519 { Some(h) == bytes.get(..2) } - fn from_reader(reader: &mut R) -> Result> + fn from_reader(mut reader: R) -> Result> where Self: Sized, R: Read, @@ -62,9 +62,9 @@ impl VarSigTrait for Ed25519 { Ok(Self { bytes }) } - fn to_writer(&self, writer: &mut W) -> Result<(), SerError> + fn to_writer(&self, mut writer: W) -> Result<(), SerError> where - W: ?Sized + Write, + W: Write, { let mut buf = u64_buffer(); writer.write_all(write_u64(ED25519 as u64, &mut buf))?; diff --git a/varsig/src/common/mod.rs b/varsig/src/common/mod.rs index 11588c6..64c57ed 100644 --- a/varsig/src/common/mod.rs +++ b/varsig/src/common/mod.rs @@ -1,3 +1,6 @@ +use crate::{DeserError, SerError, VarSigTrait}; +use std::io::{Read, Write}; + pub mod ecdsa; pub mod ed25519; pub mod rsa; @@ -16,6 +19,16 @@ pub use ecdsa::{EcdsaError, Eip191, Es256, Es256K, Es512, Ethereum}; pub use ed25519::{Ed25519, Ed25519Error}; pub use rsa::{Rsa256, Rsa512, RsaError}; +#[derive(thiserror::Error, Debug)] +pub enum JoseError { + #[error(transparent)] + Varint(#[from] unsigned_varint::decode::Error), + #[error("Incorrect hash code, expected {0:x}, got {0:x}")] + IncorrectHash(u64, u64), + #[error("Incorrect encoding, expected {:x}, got {0:x}", ENCODING)] + IncorrectEncoding(u64), +} + #[derive(thiserror::Error, Debug)] pub enum CommonError { #[error(transparent)] @@ -26,9 +39,113 @@ pub enum CommonError { IncorrectEncoding(u64), } -use crate::EitherSignature; +impl From> for JoseError { + fn from(e: CommonError) -> Self { + match e { + CommonError::Varint(e) => Self::Varint(e), + CommonError::IncorrectHash(h) => Self::IncorrectHash(H, h), + CommonError::IncorrectEncoding(e) => Self::IncorrectEncoding(e), + } + } +} + +impl From> for JoseError { + fn from(e: Ed25519Error) -> Self { + match e { + Ed25519Error::Varint(e) => Self::Varint(e), + Ed25519Error::IncorrectEncoding(e) => Self::IncorrectEncoding(e), + } + } +} + +pub enum JoseCommon { + Es256(Es256), + Es512(Es512), + Rsa256(Rsa256), + Rsa512(Rsa512), + Ed25519(Ed25519), + Es256K(Es256K), +} + +impl VarSigTrait for JoseCommon { + type SerError = std::convert::Infallible; + type DeserError = JoseError; + + fn valid_header(bytes: &[u8]) -> bool { + Es256::::valid_header(bytes) + || Es512::::valid_header(bytes) + || Rsa256::::valid_header(bytes) + || Rsa512::::valid_header(bytes) + || Ed25519::::valid_header(bytes) + || Es256K::::valid_header(bytes) + } -pub type JoseCommon = EitherSignature< - EitherSignature, Es512>, EitherSignature, Rsa512>>, - EitherSignature, Es256K>, ->; + fn from_reader(mut reader: R) -> Result> + where + Self: Sized, + R: Read, + { + let mut buf = [0u8; 19]; + reader.read_exact(&mut buf)?; + if Es256::::valid_header(&buf) { + Ok(Self::Es256( + Es256::::from_reader(buf.chain(reader)).map_err(convert_err)?, + )) + } else if Es512::::valid_header(&buf) { + Ok(Self::Es512( + Es512::::from_reader(buf.chain(reader)).map_err(convert_err)?, + )) + } else if Rsa256::::valid_header(&buf) { + Ok(Self::Rsa256( + Rsa256::::from_reader(buf.chain(reader)).map_err(convert_err)?, + )) + } else if Rsa512::::valid_header(&buf) { + Ok(Self::Rsa512( + Rsa512::::from_reader(buf.chain(reader)).map_err(convert_err)?, + )) + } else if Ed25519::::valid_header(&buf) { + Ok(Self::Ed25519( + Ed25519::::from_reader(buf.chain(reader)).map_err(convert_err)?, + )) + } else if Es256K::::valid_header(&buf) { + Ok(Self::Es256K( + Es256K::::from_reader(buf.chain(reader)).map_err(convert_err)?, + )) + } else { + Err(DeserError::InvalidHeader) + } + } + + fn to_writer(&self, writer: W) -> Result<(), SerError> + where + W: Write, + { + match self { + Self::Es256(s) => s.to_writer(writer), + Self::Es512(s) => s.to_writer(writer), + Self::Rsa256(s) => s.to_writer(writer), + Self::Rsa512(s) => s.to_writer(writer), + Self::Ed25519(s) => s.to_writer(writer), + Self::Es256K(s) => s.to_writer(writer), + } + } + + fn from_bytes(bytes: &[u8]) -> Result> + where + Self: Sized, + { + let mut reader = std::io::Cursor::new(bytes); + Self::from_reader(&mut reader) + } +} + +fn convert_err(e: DeserError) -> DeserError +where + E2: From, +{ + match e { + DeserError::Io(e) => DeserError::Io(e), + DeserError::InvalidHeader => DeserError::InvalidHeader, + DeserError::Format(e) => DeserError::Format(e.into()), + } +} diff --git a/varsig/src/common/rsa.rs b/varsig/src/common/rsa.rs index e43246f..5fdef5f 100644 --- a/varsig/src/common/rsa.rs +++ b/varsig/src/common/rsa.rs @@ -37,7 +37,7 @@ impl VarSigTrait for Rsa { Some(h) == bytes.get(..2) } - fn from_reader(reader: &mut R) -> Result> + fn from_reader(mut reader: R) -> Result> where Self: Sized, R: Read, @@ -63,9 +63,9 @@ impl VarSigTrait for Rsa { Ok(Self { bytes }) } - fn to_writer(&self, writer: &mut W) -> Result<(), SerError> + fn to_writer(&self, mut writer: W) -> Result<(), SerError> where - W: ?Sized + Write, + W: Write, { let mut buf = u64_buffer(); writer.write_all(write_u64(RSA as u64, &mut buf))?; diff --git a/varsig/src/either.rs b/varsig/src/either.rs index cb00531..c667d84 100644 --- a/varsig/src/either.rs +++ b/varsig/src/either.rs @@ -26,7 +26,7 @@ where A::valid_header(bytes) || B::valid_header(bytes) } - fn from_reader(reader: &mut R) -> Result> + fn from_reader(mut reader: R) -> Result> where R: Read, Self: Sized, @@ -58,9 +58,9 @@ where } } - fn to_writer(&self, writer: &mut W) -> Result<(), SerError> + fn to_writer(&self, writer: W) -> Result<(), SerError> where - W: ?Sized + Write, + W: Write, { match self { Self::A(a) => a.to_writer(writer).map_err(|e| match e { diff --git a/varsig/src/traits.rs b/varsig/src/traits.rs index 60a8025..72d778d 100644 --- a/varsig/src/traits.rs +++ b/varsig/src/traits.rs @@ -6,14 +6,14 @@ pub trait VarSigTrait { fn valid_header(bytes: &[u8]) -> bool; - fn from_reader(reader: &mut R) -> Result> + fn from_reader(reader: R) -> Result> where Self: Sized, R: Read; - fn to_writer(&self, writer: &mut W) -> Result<(), SerError> + fn to_writer(&self, writer: W) -> Result<(), SerError> where - W: ?Sized + Write; + W: Write; fn to_vec(&self) -> Result, SerError> { let mut buf = Vec::new(); From a140c0a77d254e29d8fbe770e355119573a5f603 Mon Sep 17 00:00:00 2001 From: chunningham Date: Wed, 26 Apr 2023 14:24:01 +0200 Subject: [PATCH 26/70] wip cacaos, impl ucan-cacao --- Cargo.toml | 1 + multidid/src/pkh.rs | 6 +++++ src/v2/either.rs | 18 +++++++++++++++ src/v2/mod.rs | 5 ++-- src/v2/recap_cacao.rs | 53 +++++++++++++++++++++++++++++++++++++++++-- src/v2/ucan_cacao.rs | 42 ++++++++++++++++++++++++++++++++++ 6 files changed, 120 insertions(+), 5 deletions(-) create mode 100644 src/v2/either.rs create mode 100644 src/v2/ucan_cacao.rs diff --git a/Cargo.toml b/Cargo.toml index dfb6b7a..6955442 100755 --- a/Cargo.toml +++ b/Cargo.toml @@ -31,6 +31,7 @@ http = "0.2.5" hex = { version = "0.4", optional = true } varsig = { version = "0.1", path = "./varsig" } multidid = { version = "0.1", path = "./multidid" } +siwe-recap = { version = "0.2", path = "../capgrok" } [dev-dependencies] async-std = { version = "1.10", features = ["attributes"] } diff --git a/multidid/src/pkh.rs b/multidid/src/pkh.rs index c36690f..23da762 100644 --- a/multidid/src/pkh.rs +++ b/multidid/src/pkh.rs @@ -35,6 +35,12 @@ impl Caip10 { } } +impl From<(C, A)> for Caip10 { + fn from((chain_id, address): (C, A)) -> Self { + Self::new(chain_id, address) + } +} + #[derive(Debug, Clone, PartialEq)] pub enum CosmosAddress { Secp256k1([u8; 20]), diff --git a/src/v2/either.rs b/src/v2/either.rs new file mode 100644 index 0000000..e2bf842 --- /dev/null +++ b/src/v2/either.rs @@ -0,0 +1,18 @@ +use super::CacaoProfile; +use serde::{Deserialize, Serialize}; +use varsig::either::EitherSignature; + +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +pub enum Either { + A(A::Facts), + B(B::Facts), +} + +impl CacaoProfile for Either +where + A: CacaoProfile, + B: CacaoProfile, +{ + type Signature = EitherSignature; + type Facts = Either; +} diff --git a/src/v2/mod.rs b/src/v2/mod.rs index 2b0cda3..96a02c9 100644 --- a/src/v2/mod.rs +++ b/src/v2/mod.rs @@ -1,6 +1,5 @@ -use libipld::cid::Cid; +use libipld::{cid::Cid, ipld::Ipld}; use serde::{Deserialize, Serialize}; -use serde_json::Value; use serde_with::serde_as; use std::fmt::Debug; use ucan_capabilities_object::Capabilities; @@ -17,7 +16,7 @@ use varsig::{VarSig, VarSigTrait}; #[serde_as] #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] -pub struct CACAO { +pub struct Cacao { #[serde_as(as = "MultiDidAsBytes")] #[serde(rename = "iss")] issuer: MultiDid, diff --git a/src/v2/recap_cacao.rs b/src/v2/recap_cacao.rs index c6d25fb..3281473 100644 --- a/src/v2/recap_cacao.rs +++ b/src/v2/recap_cacao.rs @@ -1,17 +1,20 @@ -use super::CACAO; +use super::Cacao; use http::uri::Authority; use iri_string::types::UriString; +use multidid::{DidPkhTypes, Method, MultiDid}; use serde::{Deserialize, Serialize}; use serde_json::Value; use serde_with::{serde_as, DeserializeFromStr, DisplayFromStr, SerializeDisplay}; pub use siwe; use siwe::{eip55, Message, TimeStamp, VerificationError as SVE, Version as SVersion}; +use siwe_recap::Capability; use std::fmt::Debug; use std::io::{Read, Seek, Write}; use thiserror::Error; use time::OffsetDateTime; +use varsig::common::{Ethereum, EIP191_ENCODING}; -pub type RecapCacao = CACAO; +pub type RecapCacao = Cacao, RecapFacts, NB>; #[serde_as] #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] @@ -29,3 +32,49 @@ pub struct RecapFacts { request_id: Option, resources: Vec, } + +#[derive(thiserror::Error, Debug)] +pub enum Error {} + +impl TryFrom<(Message, Vec)> for RecapCacao { + type Error = Error; + fn try_from((siwe, sig): (Message, Vec)) -> Result { + let recap = Capability::::extract_and_verify(&siwe)?.into_inner(); + let (issued_at, iat_time_zone) = siwe.issued_at.map(split_tz); + let (not_before, nbf_time_zone) = siwe.not_before.map(split_tz); + let (expiration, exp_time_zone) = siwe.expiration.map(split_tz); + let statement = siwe + .statement + .map(|s| s.get(0..(s.len() - recap.to_statement().len()))); + let (attenuations, proof) = recap.into_inner(); + OK(Self { + issuer: MultiDid::new( + Method::Pkh(DidPkhTypes::Eip155((siwe.chain_id, siwe.address).into())), + None, + None, + ), + audience: MultiDid::from_str(siwe.uri)?, + signature: VarSig::new(sig.try_into()?), + version: siwe.version, + attenuations, + nonce: Some(siwe.nonce), + proof, + issued_at, + not_before, + expiration, + facts: Some(RecapFacts { + iat_time_zone, + nbf_time_zone, + exp_time_zone, + domain: siwe.domain, + request_id: siwe.request_id, + resources: siwe.resources[0..-1], + statement, + }), + }) + } +} + +fn split_tz(t: TimeStamp) -> (u64, String) { + todo!() +} diff --git a/src/v2/ucan_cacao.rs b/src/v2/ucan_cacao.rs new file mode 100644 index 0000000..ecd2b78 --- /dev/null +++ b/src/v2/ucan_cacao.rs @@ -0,0 +1,42 @@ +use super::CacaoProfile; +use serde_json::Value; +use ssi_jwk::Algorithm; +use ssi_ucan::Ucan; +use varsig::common::{JoseCommon, DAG_JSON_ENCODING}; + +pub type UcanCacao = Cacao, F, NB>; + +#[derive(thiserror::Error, Debug)] +pub enum Error {} + +impl TryFrom> for UcanCacao { + type Error = Error; + fn try_from(ucan: Ucan) -> Result { + Ok(Self { + issuer: MultiDid::from_str(ucan.payload.issuer)?, + audience: MultiDid::from_str(ucan.payload.audience)?, + signature: match ucan.header.algorithm { + Algorithm::Es256 => + }, + version: ucan + .header + .additional_parameters + .get("ucv") + .ok_or_else(|| todo!()), + attenuations: ucan.payload.capabilities, + nonce: ucan.payload.nonce, + proof: ucan.payload.proof, + issued_at: ucan.payload.issued_at, + not_before: ucan.payload.not_before, + expiration: ucan.payload.expiration, + facts: ucan.payload.facts, + }) + } +} + +pub struct UcanCacaoProfile; + +impl CacaoProfile for UcanCacaoProfile { + type Signature = JoseCommon; + type Facts = Value; +} From cf3b99b333859adc913f43d44df2b5b81ac825c9 Mon Sep 17 00:00:00 2001 From: chunningham Date: Mon, 1 May 2023 17:22:02 +0200 Subject: [PATCH 27/70] wip cacao to/from ucan and recaps --- Cargo.toml | 2 +- src/v2/mod.rs | 2 +- src/v2/recap_cacao.rs | 69 +++++++++++++++++++++++++++++++++---------- src/v2/ucan_cacao.rs | 67 +++++++++++++++++++++++++++++------------ 4 files changed, 103 insertions(+), 37 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 6955442..e5ddd12 100755 --- a/Cargo.toml +++ b/Cargo.toml @@ -26,7 +26,7 @@ serde_json = "1" libipld = { version = "0.16", default-features = false, features = ["derive", "serde-codec", "dag-cbor"]} ucan-capabilities-object = "0.1" serde_with = "2" -time = { version = "0.3", features = ["parsing", "formatting"] } +time = { version = "0.3", features = ["parsing", "formatting", "serde"] } http = "0.2.5" hex = { version = "0.4", optional = true } varsig = { version = "0.1", path = "./varsig" } diff --git a/src/v2/mod.rs b/src/v2/mod.rs index 96a02c9..34934bc 100644 --- a/src/v2/mod.rs +++ b/src/v2/mod.rs @@ -1,4 +1,4 @@ -use libipld::{cid::Cid, ipld::Ipld}; +use libipld::cid::Cid; use serde::{Deserialize, Serialize}; use serde_with::serde_as; use std::fmt::Debug; diff --git a/src/v2/recap_cacao.rs b/src/v2/recap_cacao.rs index 3281473..d3cc97f 100644 --- a/src/v2/recap_cacao.rs +++ b/src/v2/recap_cacao.rs @@ -11,8 +11,11 @@ use siwe_recap::Capability; use std::fmt::Debug; use std::io::{Read, Seek, Write}; use thiserror::Error; -use time::OffsetDateTime; -use varsig::common::{Ethereum, EIP191_ENCODING}; +use time::UtcOffset; +use varsig::{ + common::{Ethereum, EIP191_ENCODING}, + VarSig, +}; pub type RecapCacao = Cacao, RecapFacts, NB>; @@ -20,11 +23,17 @@ pub type RecapCacao = Cacao, RecapFacts, N #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub struct RecapFacts { #[serde(rename = "iat-z")] - iat_time_zone: Option, + iat_time_zone: Option, #[serde(rename = "nbf-z")] - nbf_time_zone: Option, + nbf_time_zone: Option, #[serde(rename = "nbf-z")] - exp_time_zone: Option, + exp_time_zone: Option, + #[serde(rename = "iat-n")] + iat_nanos: Option, + #[serde(rename = "nbf-n")] + nbf_nanos: Option, + #[serde(rename = "exp-n")] + exp_nanos: Option, #[serde_as(as = "DisplayFromStr")] domain: Authority, statement: Option, @@ -36,26 +45,31 @@ pub struct RecapFacts { #[derive(thiserror::Error, Debug)] pub enum Error {} -impl TryFrom<(Message, Vec)> for RecapCacao { +impl TryFrom<(Message, Vec)> for RecapCacao +where + NB: for<'d> Deserialize<'d>, +{ type Error = Error; fn try_from((siwe, sig): (Message, Vec)) -> Result { - let recap = Capability::::extract_and_verify(&siwe)?.into_inner(); - let (issued_at, iat_time_zone) = siwe.issued_at.map(split_tz); - let (not_before, nbf_time_zone) = siwe.not_before.map(split_tz); - let (expiration, exp_time_zone) = siwe.expiration.map(split_tz); + let recap = Capability::::extract_and_verify(&siwe)?; + let (issued_at, iat_time_zone, iat_nanos) = split_tz(Some(siwe.issued_at)); + let (not_before, nbf_time_zone, nbf_nanos) = split_tz(siwe.not_before); + let (expiration, exp_time_zone, exp_nanos) = split_tz(siwe.expiration_time); let statement = siwe .statement .map(|s| s.get(0..(s.len() - recap.to_statement().len()))); let (attenuations, proof) = recap.into_inner(); - OK(Self { + Ok(Self { issuer: MultiDid::new( - Method::Pkh(DidPkhTypes::Eip155((siwe.chain_id, siwe.address).into())), + Method::Pkh(DidPkhTypes::Eip155( + (siwe.chain_id.to_string(), siwe.address).into(), + )), None, None, ), audience: MultiDid::from_str(siwe.uri)?, - signature: VarSig::new(sig.try_into()?), - version: siwe.version, + signature: VarSig::new(Ethereum::sig.try_into()?), + version: (siwe.version as u8).to_string(), attenuations, nonce: Some(siwe.nonce), proof, @@ -66,6 +80,9 @@ impl TryFrom<(Message, Vec)> for RecapCacao { iat_time_zone, nbf_time_zone, exp_time_zone, + iat_nanos, + nbf_nanos, + exp_nanos, domain: siwe.domain, request_id: siwe.request_id, resources: siwe.resources[0..-1], @@ -75,6 +92,26 @@ impl TryFrom<(Message, Vec)> for RecapCacao { } } -fn split_tz(t: TimeStamp) -> (u64, String) { - todo!() +fn split_tz(t: Option) -> (Option, Option, Option) { + match t { + Some(t) => ( + Some(t.as_ref().unix_timestamp().into()), + Some(t.offset()), + Some(t.nanosecond()), + ), + None => (None, None, None), + } +} + +fn get_tz((unix, tz, nano): (Option, Option, Option)) -> Option { + match (unix, tz, nano) { + (Some(unix), Some(tz), Some(nano)) if nano < 1_000_000_000 => Some( + TimeStamp::from_unix_timestamp(unix) + .replace_offset(tz) + .replace_nanosecond(nano) + // lets just assume nano < 1000000000, we check before here + .unwrap(), + ), + _ => None, + } } diff --git a/src/v2/ucan_cacao.rs b/src/v2/ucan_cacao.rs index ecd2b78..4a5e2bc 100644 --- a/src/v2/ucan_cacao.rs +++ b/src/v2/ucan_cacao.rs @@ -1,42 +1,71 @@ -use super::CacaoProfile; +use super::{Cacao, CacaoProfile}; +use multidid::MultiDid; use serde_json::Value; use ssi_jwk::Algorithm; use ssi_ucan::Ucan; -use varsig::common::{JoseCommon, DAG_JSON_ENCODING}; +use varsig::{ + common::{Ed25519, Es256, Es256K, JoseCommon, Rsa256, Rsa512, DAG_JSON_ENCODING}, + VarSig, +}; pub type UcanCacao = Cacao, F, NB>; #[derive(thiserror::Error, Debug)] -pub enum Error {} +pub enum Error { + #[error("Unsupported Algorithm: {0}")] + UnsupportedAlgorithm(Algorithm), + #[error("Incorrect Signature Length: recieved {0}, expected {1}")] + IncorrectSignatureLength(usize, usize), + #[error("Missing Version Header")] + MissingVersionHeader, +} impl TryFrom> for UcanCacao { type Error = Error; fn try_from(ucan: Ucan) -> Result { + let (header, payload, signature) = ucan.into_inner(); Ok(Self { - issuer: MultiDid::from_str(ucan.payload.issuer)?, - audience: MultiDid::from_str(ucan.payload.audience)?, - signature: match ucan.header.algorithm { - Algorithm::Es256 => - }, - version: ucan - .header + issuer: MultiDid::from_str(payload.issuer)?, + audience: MultiDid::from_str(payload.audience)?, + signature: VarSig::new(match_alg(header.alg, ucan.signature)?), + version: header .additional_parameters .get("ucv") - .ok_or_else(|| todo!()), - attenuations: ucan.payload.capabilities, - nonce: ucan.payload.nonce, - proof: ucan.payload.proof, - issued_at: ucan.payload.issued_at, - not_before: ucan.payload.not_before, - expiration: ucan.payload.expiration, - facts: ucan.payload.facts, + .ok_or_else(|| Error::MissingVersionHeader)?, + attenuations: payload.capabilities, + nonce: payload.nonce, + proof: payload.proof, + issued_at: payload.issued_at, + not_before: payload.not_before, + expiration: payload.expiration, + facts: payload.facts, }) } } +fn match_alg(a: Algorithm, s: Vec) -> Result, Error> { + Ok(match a { + Algorithm::ES256 => JoseCommon::Es256(Es256::new( + s.try_into() + .map_err(|v| Error::IncorrectSignatureLength(v.len(), 64))?, + )), + Algorithm::EdDSA => JoseCommon::Ed25519(Ed25519::new( + s.try_into() + .map_err(|v| Error::IncorrectSignatureLength(v.len(), 64))?, + )), + Algorithm::RS256 => JoseCommon::Rsa256(Rsa256::new(s)), + Algorithm::RS512 => JoseCommon::Rsa512(Rsa512::new(s)), + Algorithm::ES256K => JoseCommon::Es256K(Es256K::new( + s.try_into() + .map_err(|v| Error::IncorrectSignatureLength(v.len(), 64))?, + )), + a => return Err(Error::UnsupportedAlgorithm(a)), + }) +} + pub struct UcanCacaoProfile; impl CacaoProfile for UcanCacaoProfile { - type Signature = JoseCommon; + type Signature = JoseCommon; type Facts = Value; } From b5c48a39accf8bbdb2f0c0a7176c2b84c0f11d29 Mon Sep 17 00:00:00 2001 From: chunningham Date: Wed, 5 Jul 2023 14:19:15 +0200 Subject: [PATCH 28/70] fix hedera pkh type --- multidid/src/pkh.rs | 86 ++++++++++++++++++++++++++++++++++++--------- 1 file changed, 70 insertions(+), 16 deletions(-) diff --git a/multidid/src/pkh.rs b/multidid/src/pkh.rs index 23da762..d3e10ef 100644 --- a/multidid/src/pkh.rs +++ b/multidid/src/pkh.rs @@ -7,17 +7,17 @@ use unsigned_varint::{ pub(crate) const PKH_CODEC: u64 = 0xca; -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub enum DidPkhTypes { Bip122(Caip10<[u8; 32], [u8; 25]>), Eip155(Caip10), Cosmos(Caip10), Starknet(Caip10), - Hedera(Caip10), + Hedera(Caip10), Lip9(Caip10<[u8; 32], [u8; 20]>), } -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct Caip10 { chain_id: C, address: A, @@ -41,14 +41,21 @@ impl From<(C, A)> for Caip10 { } } -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub enum CosmosAddress { Secp256k1([u8; 20]), Secp256r1([u8; 32]), } +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub enum HederaAddress { + EVM([u8; 20]), + Ed25519([u8; 32]), + Secp256k1([u8; 33]), +} + impl CosmosAddress { - fn bytes(&self) -> &[u8] { + pub fn bytes(&self) -> &[u8] { match self { CosmosAddress::Secp256k1(address) => address, CosmosAddress::Secp256r1(address) => address, @@ -56,8 +63,18 @@ impl CosmosAddress { } } +impl HederaAddress { + pub fn bytes(&self) -> &[u8] { + match self { + HederaAddress::EVM(address) => address, + HederaAddress::Ed25519(address) => address, + HederaAddress::Secp256k1(address) => address, + } + } +} + impl DidPkhTypes { - fn caip_2_code(&self) -> &'static str { + pub fn caip_2_code(&self) -> &'static str { use DidPkhTypes::*; match self { Bip122(_) => "bip122", @@ -114,9 +131,20 @@ impl DidPkhTypes { vec.extend(write_u64(3, &mut buf)); vec.extend(write_u64(caip10.chain_id().len() as u64, &mut buf)); vec.extend(caip10.chain_id().as_bytes()); - vec.extend(write_u64(caip10.address().0, &mut buf)); - vec.extend(write_u64(caip10.address().1, &mut buf)); - vec.extend(write_u64(caip10.address().2, &mut buf)); + match caip10.address() { + HederaAddress::EVM(address) => { + vec.extend(write_u64(20, &mut buf)); + vec.extend(address); + } + HederaAddress::Ed25519(address) => { + vec.extend(write_u64(32, &mut buf)); + vec.extend(address); + } + HederaAddress::Secp256k1(address) => { + vec.extend(write_u64(33, &mut buf)); + vec.extend(address); + } + } } DidPkhTypes::Lip9(caip10) => { vec.extend(write_u64(3, &mut buf)); @@ -199,12 +227,27 @@ impl DidPkhTypes { let ref_len = read_u64(reader.by_ref())?; let mut chain_id = vec![0u8; ref_len as usize]; reader.read_exact(&mut chain_id)?; - let shard = read_u64(reader.by_ref())?; - let realm = read_u64(reader.by_ref())?; - let account = read_u64(reader.by_ref())?; + let address_len = read_u64(reader.by_ref())?; Ok(DidPkhTypes::Hedera(Caip10::new( String::from_utf8(chain_id)?, - (shard, realm, account), + match address_len { + 20 => { + let mut address = [0u8; 20]; + reader.read_exact(&mut address)?; + HederaAddress::EVM(address) + } + 32 => { + let mut address = [0u8; 32]; + reader.read_exact(&mut address)?; + HederaAddress::Ed25519(address) + } + 33 => { + let mut address = [0u8; 33]; + reader.read_exact(&mut address)?; + HederaAddress::Secp256k1(address) + } + _ => return Err(Error::Format("Invalid hedera address length")), + }, ))) } // lip9 @@ -262,9 +305,20 @@ impl DidPkhTypes { writer.write_all(write_u64(3, &mut buf))?; writer.write_all(write_u64(caip10.chain_id().len() as u64, &mut buf))?; writer.write_all(caip10.chain_id().as_bytes())?; - writer.write_all(write_u64(caip10.address().0, &mut buf))?; - writer.write_all(write_u64(caip10.address().1, &mut buf))?; - writer.write_all(write_u64(caip10.address().2, &mut buf))?; + match caip10.address() { + HederaAddress::EVM(address) => { + writer.write_all(write_u64(20, &mut buf))?; + writer.write_all(address)?; + } + HederaAddress::Ed25519(address) => { + writer.write_all(write_u64(32, &mut buf))?; + writer.write_all(address)?; + } + HederaAddress::Secp256k1(address) => { + writer.write_all(write_u64(20, &mut buf))?; + writer.write_all(address)?; + } + } } DidPkhTypes::Lip9(caip10) => { writer.write_all(write_u64(3, &mut buf))?; From 58452ea5b25af9754d15f476b697920e2c9e51aa Mon Sep 17 00:00:00 2001 From: chunningham Date: Wed, 5 Jul 2023 16:40:28 +0200 Subject: [PATCH 29/70] fix key did, impl display/fromstr --- multidid/Cargo.toml | 1 + multidid/src/key.rs | 63 ++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 61 insertions(+), 3 deletions(-) diff --git a/multidid/Cargo.toml b/multidid/Cargo.toml index 2db09f0..abc4054 100644 --- a/multidid/Cargo.toml +++ b/multidid/Cargo.toml @@ -9,6 +9,7 @@ edition = "2021" iri-string = { version = "0.6", features = ["serde"] } unsigned-varint = { version = "0.7", features = ["std"] } thiserror = "1" +bs58 = "0.5" [dev-dependencies] serde_json = "1" diff --git a/multidid/src/key.rs b/multidid/src/key.rs index 66301c7..706be81 100644 --- a/multidid/src/key.rs +++ b/multidid/src/key.rs @@ -1,5 +1,7 @@ -use crate::Error; use std::io::{Error as IoError, Read, Write}; +use std::{fmt::Display, str::FromStr}; +use unsigned_varint::encode::{u64 as write_u64, u64_buffer}; +use unsigned_varint::io::read_u64; const SECP256K1_CODEC: u64 = 0xe7; const BLS12_381_G1_CODEC: u64 = 0xea; @@ -24,6 +26,14 @@ pub enum DidKeyTypes { // RSA([u8; ??]), } +#[derive(Debug, thiserror::Error)] +pub enum Error { + #[error(transparent)] + Io(#[from] IoError), + #[error("Invalid key type: {0:x}")] + InvalidCodec(u64), +} + impl DidKeyTypes { pub fn codec(&self) -> u64 { use DidKeyTypes::*; @@ -84,7 +94,7 @@ impl DidKeyTypes { reader.read_exact(&mut buf)?; Ok(Self::P521(buf)) } - _ => Err(Error::InvalidPrefix(codec)), + _ => Err(Error::InvalidCodec(codec)), } } @@ -92,10 +102,12 @@ impl DidKeyTypes { where W: ?Sized + Write, { + let mut buf = u64_buffer(); + writer.write_all(write_u64(self.codec(), &mut buf))?; writer.write_all(&self.bytes()) } - pub fn bytes(&self) -> &[u8] { + fn bytes(&self) -> &[u8] { match self { Self::Secp256k1(key) => key, Self::Bls12_381G1(key) => key, @@ -107,4 +119,49 @@ impl DidKeyTypes { Self::P521(key) => key, } } + + pub(crate) fn to_vec(&self) -> Vec { + let mut vec = Vec::new(); + let mut buf = u64_buffer(); + vec.extend(write_u64(self.codec(), &mut buf)); + vec.extend(self.bytes()); + vec + } +} + +impl Display for DidKeyTypes { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "z{}", bs58::encode(self.to_vec()).into_string()) + } +} + +#[derive(Debug, thiserror::Error)] +pub enum ParseErr { + #[error("Invalid Base")] + InvalidBase, + #[error("Insufficient Length")] + InsuficientBytes, + #[error(transparent)] + Varint(#[from] unsigned_varint::io::ReadError), + #[error(transparent)] + Base58(#[from] bs58::decode::Error), + #[error(transparent)] + Decoding(#[from] Error), +} + +impl FromStr for DidKeyTypes { + type Err = ParseErr; + + fn from_str(s: &str) -> Result { + match (s.get(..1), s.get(1..)) { + (None, _) | (_, None) => Err(ParseErr::InsuficientBytes), + (Some("z"), Some(rest)) => { + let bytes = bs58::decode(rest).into_vec()?; + let mut br = bytes.as_slice(); + let codec = read_u64(&mut br)?; + Ok(Self::from_reader(&mut br, codec)?) + } + _ => Err(ParseErr::InvalidBase), + } + } } From 4c26f98df148749a43ea99d2acf9291148369fe6 Mon Sep 17 00:00:00 2001 From: chunningham Date: Wed, 5 Jul 2023 16:52:22 +0200 Subject: [PATCH 30/70] add pkh specific error and privatise methods --- multidid/src/pkh.rs | 36 +++++++++++++++++++++++------------- 1 file changed, 23 insertions(+), 13 deletions(-) diff --git a/multidid/src/pkh.rs b/multidid/src/pkh.rs index d3e10ef..d8a0456 100644 --- a/multidid/src/pkh.rs +++ b/multidid/src/pkh.rs @@ -1,5 +1,5 @@ -use crate::Error; use std::io::{Error as IoError, Read, Write}; +use std::{fmt::Display, str::FromStr}; use unsigned_varint::{ encode::{u64 as write_u64, u64_buffer}, io::read_u64, @@ -73,6 +73,22 @@ impl HederaAddress { } } +#[derive(Debug, thiserror::Error)] +pub enum Error { + #[error(transparent)] + Io(#[from] IoError), + #[error("Invalid PKH discriminant: {0}")] + InvalidPkh(u64), + #[error(transparent)] + Utf8(#[from] std::string::FromUtf8Error), + #[error(transparent)] + Varint(#[from] unsigned_varint::io::ReadError), + #[error("Invalid Hedera length: {0}")] + InvalidHederaLength(u64), + #[error("Invalid Cosmos length: {0}")] + InvalidCosmosLength(u64), +} + impl DidPkhTypes { pub fn caip_2_code(&self) -> &'static str { use DidPkhTypes::*; @@ -90,7 +106,7 @@ impl DidPkhTypes { PKH_CODEC } - pub fn to_vec(&self) -> Vec { + pub(crate) fn to_vec(&self) -> Vec { let mut vec = Vec::new(); vec.push(PKH_CODEC as u8); let mut buf = u64_buffer(); @@ -155,16 +171,10 @@ impl DidPkhTypes { vec } - pub fn from_reader(reader: &mut R) -> Result + pub(crate) fn from_reader(reader: &mut R) -> Result where R: Read, { - let mut buf = [0u8; 1]; - reader.read_exact(&mut buf)?; - let codec = buf[0]; - if codec as u64 != PKH_CODEC { - return Err(Error::InvalidCodec(codec as u64)); - } let pkh_type = read_u64(reader.by_ref())?; match pkh_type { // bitcoin-like @@ -206,7 +216,7 @@ impl DidPkhTypes { reader.read_exact(&mut address)?; CosmosAddress::Secp256r1(address) } - _ => return Err(Error::Format("Invalid cosmos address length")), + l => return Err(Error::InvalidCosmosLength(l)), }, ))) } @@ -246,7 +256,7 @@ impl DidPkhTypes { reader.read_exact(&mut address)?; HederaAddress::Secp256k1(address) } - _ => return Err(Error::Format("Invalid hedera address length")), + l => return Err(Error::InvalidHederaLength(l)), }, ))) } @@ -258,11 +268,11 @@ impl DidPkhTypes { reader.read_exact(&mut address)?; Ok(DidPkhTypes::Lip9(Caip10::new(chain_id, address))) } - _ => Err(Error::Format("Unsupported did-pkh discriminant")), + t => Err(Error::InvalidPkh(t)), } } - pub fn to_writer(&self, writer: &mut W) -> Result<(), IoError> + pub(crate) fn to_writer(&self, writer: &mut W) -> Result<(), IoError> where W: ?Sized + Write, { From 8f5a143a690dce5c61a2983cee1ef29e31badd1a Mon Sep 17 00:00:00 2001 From: chunningham Date: Tue, 11 Jul 2023 13:05:44 +0200 Subject: [PATCH 31/70] impl serde in subcrates, remove serde-with --- Cargo.toml | 1 - multidid/Cargo.toml | 4 ++++ multidid/src/lib.rs | 27 ++++++++++++++++++++++ src/v2/mod.rs | 18 ++++----------- src/v2/serde_util.rs | 54 -------------------------------------------- varsig/Cargo.toml | 4 ++++ varsig/src/lib.rs | 31 +++++++++++++++++++++++++ 7 files changed, 71 insertions(+), 68 deletions(-) delete mode 100644 src/v2/serde_util.rs diff --git a/Cargo.toml b/Cargo.toml index e5ddd12..c1b7e85 100755 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,7 +25,6 @@ serde = "1" serde_json = "1" libipld = { version = "0.16", default-features = false, features = ["derive", "serde-codec", "dag-cbor"]} ucan-capabilities-object = "0.1" -serde_with = "2" time = { version = "0.3", features = ["parsing", "formatting", "serde"] } http = "0.2.5" hex = { version = "0.4", optional = true } diff --git a/multidid/Cargo.toml b/multidid/Cargo.toml index abc4054..b3f37d4 100644 --- a/multidid/Cargo.toml +++ b/multidid/Cargo.toml @@ -4,12 +4,16 @@ version = "0.1.0" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +[features] +default = ["serde"] +serde = ["dep:serde"] [dependencies] iri-string = { version = "0.6", features = ["serde"] } unsigned-varint = { version = "0.7", features = ["std"] } thiserror = "1" bs58 = "0.5" +serde = { version = "1", optional = true } [dev-dependencies] serde_json = "1" diff --git a/multidid/src/lib.rs b/multidid/src/lib.rs index 0b6e358..f7ab412 100644 --- a/multidid/src/lib.rs +++ b/multidid/src/lib.rs @@ -167,6 +167,33 @@ impl MultiDid { } } +#[cfg(feature = "serde")] +mod serde_util { + use super::*; + use serde::{Deserialize, Deserializer, Serialize, Serializer}; + + impl Serialize for MultiDid { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + serializer.serialize_bytes(&self.to_vec()) + } + } + + impl<'de> Deserialize<'de> for MultiDid { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let bytes = <&[u8]>::deserialize(deserializer)?; + MultiDid::from_bytes(bytes).map_err(|e| { + serde::de::Error::custom(format!("MultiDid deserialization error: {}", e)) + }) + } + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/src/v2/mod.rs b/src/v2/mod.rs index 34934bc..0d97381 100644 --- a/src/v2/mod.rs +++ b/src/v2/mod.rs @@ -1,6 +1,5 @@ use libipld::cid::Cid; use serde::{Deserialize, Serialize}; -use serde_with::serde_as; use std::fmt::Debug; use ucan_capabilities_object::Capabilities; @@ -8,32 +7,25 @@ pub mod either; pub mod recap_cacao; pub mod ucan_cacao; -pub mod serde_util; - use multidid::MultiDid; -use serde_util::{MultiDidAsBytes, VarSigAsBytes}; use varsig::{VarSig, VarSigTrait}; -#[serde_as] #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] -pub struct Cacao { - #[serde_as(as = "MultiDidAsBytes")] +pub struct Cacao { #[serde(rename = "iss")] issuer: MultiDid, - #[serde_as(as = "MultiDidAsBytes")] #[serde(rename = "aud")] audience: MultiDid, - #[serde_as(as = "VarSigAsBytes")] #[serde(rename = "s")] - signature: VarSig, + signature: S, #[serde(rename = "v")] version: String, #[serde(rename = "att")] attenuations: Capabilities, - #[serde(rename = "nnc", skip_serializing_if = "Option::is_none")] + #[serde(rename = "nnc", skip_serializing_if = "Option::is_none", default)] nonce: Option, - #[serde(rename = "prf", skip_serializing_if = "Vec::is_empty", default)] - proof: Vec, + #[serde(rename = "prf", skip_serializing_if = "Option::is_none", default)] + proof: Option>, #[serde(rename = "iat", skip_serializing_if = "Option::is_none", default)] issued_at: Option, #[serde(rename = "nbf", skip_serializing_if = "Option::is_none", default)] diff --git a/src/v2/serde_util.rs b/src/v2/serde_util.rs deleted file mode 100644 index 7713e22..0000000 --- a/src/v2/serde_util.rs +++ /dev/null @@ -1,54 +0,0 @@ -use multidid::MultiDid; -use serde::Deserialize; -use serde_with::{DeserializeAs, SerializeAs}; -use varsig::{VarSig, VarSigTrait}; - -pub struct MultiDidAsBytes; - -impl SerializeAs for MultiDidAsBytes { - fn serialize_as(source: &MultiDid, serializer: S) -> Result - where - S: serde::Serializer, - { - serializer.serialize_bytes(&source.to_vec()) - } -} - -impl<'de> DeserializeAs<'de, MultiDid> for MultiDidAsBytes { - fn deserialize_as(deserializer: D) -> Result - where - D: serde::Deserializer<'de>, - { - let v = Vec::::deserialize(deserializer)?; - let mut b: &[u8] = v.as_ref(); - MultiDid::from_reader(&mut b).map_err(serde::de::Error::custom) - } -} - -pub struct VarSigAsBytes; - -impl SerializeAs> for VarSigAsBytes -where - V: VarSigTrait, -{ - fn serialize_as(source: &VarSig, serializer: S) -> Result - where - S: serde::Serializer, - { - serializer.serialize_bytes(&source.to_vec().map_err(serde::ser::Error::custom)?) - } -} - -impl<'de, V> DeserializeAs<'de, VarSig> for VarSigAsBytes -where - V: VarSigTrait, -{ - fn deserialize_as(deserializer: D) -> Result, D::Error> - where - D: serde::Deserializer<'de>, - { - let v = Vec::::deserialize(deserializer)?; - let mut b: &[u8] = v.as_ref(); - VarSig::from_reader(&mut b).map_err(serde::de::Error::custom) - } -} diff --git a/varsig/Cargo.toml b/varsig/Cargo.toml index 4b4323d..fe86373 100644 --- a/varsig/Cargo.toml +++ b/varsig/Cargo.toml @@ -4,7 +4,11 @@ version = "0.1.0" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +[features] +default = ["serde"] +serde = ["dep:serde"] [dependencies] unsigned-varint = { version = "0.7", features = ["std"] } thiserror = "1" +serde = { version = "1", optional = true } diff --git a/varsig/src/lib.rs b/varsig/src/lib.rs index f2a5326..7378ac5 100644 --- a/varsig/src/lib.rs +++ b/varsig/src/lib.rs @@ -80,3 +80,34 @@ impl From> for Error { } } } + +#[cfg(feature = "serde")] +mod serde_util { + use super::*; + use serde::{Deserialize, Deserializer, Serialize, Serializer}; + + impl Serialize for VarSig { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + self.to_vec() + .map_err(|e| { + serde::ser::Error::custom(format!("VarSig serialization error: {}", e)) + }) + .and_then(|v| serializer.serialize_bytes(&v)) + } + } + + impl<'de, V: VarSigTrait> Deserialize<'de> for VarSig { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let bytes = <&[u8]>::deserialize(deserializer)?; + VarSig::from_bytes(bytes).map_err(|e| { + serde::de::Error::custom(format!("VarSig deserialization error: {}", e)) + }) + } + } +} From 93ee8c868b96bfcd1354e65d1ec63ffdd793b15f Mon Sep 17 00:00:00 2001 From: chunningham Date: Tue, 11 Jul 2023 13:05:58 +0200 Subject: [PATCH 32/70] use published siwe-recap --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index c1b7e85..f554ba0 100755 --- a/Cargo.toml +++ b/Cargo.toml @@ -30,7 +30,7 @@ http = "0.2.5" hex = { version = "0.4", optional = true } varsig = { version = "0.1", path = "./varsig" } multidid = { version = "0.1", path = "./multidid" } -siwe-recap = { version = "0.2", path = "../capgrok" } +siwe-recap = { version = "0.2" } [dev-dependencies] async-std = { version = "1.10", features = ["attributes"] } From 0784eb5cfda37894e6629dbec9ad1155563f7e9e Mon Sep 17 00:00:00 2001 From: chunningham Date: Fri, 21 Jul 2023 14:12:02 +0200 Subject: [PATCH 33/70] stub parse/to str for multidids --- multidid/src/error.rs | 8 ++++++-- multidid/src/lib.rs | 32 ++++++++++++++++++++++++++++++++ multidid/src/method.rs | 38 ++++++++++++++++++++++++++++++++++++-- multidid/src/pkh.rs | 17 +++++++++++++++++ 4 files changed, 91 insertions(+), 4 deletions(-) diff --git a/multidid/src/error.rs b/multidid/src/error.rs index a624dea..be8e253 100644 --- a/multidid/src/error.rs +++ b/multidid/src/error.rs @@ -1,13 +1,17 @@ +use crate::{key, pkh}; + #[derive(Debug, thiserror::Error)] pub enum Error { + #[error(transparent)] + Pkh(#[from] pkh::Error), + #[error(transparent)] + Key(#[from] key::Error), #[error(transparent)] Varint(#[from] unsigned_varint::io::ReadError), #[error(transparent)] Io(#[from] std::io::Error), #[error("Invalid multidid varint prefix, expected 0x9d1a, recieved {0:x}")] InvalidPrefix(u64), - #[error("Invalid did-pkh type varint: {0:x}")] - InvalidCodec(u64), #[error(transparent)] Parameter(#[from] iri_string::validate::Error), #[error(transparent)] diff --git a/multidid/src/lib.rs b/multidid/src/lib.rs index f7ab412..5cfc8b3 100644 --- a/multidid/src/lib.rs +++ b/multidid/src/lib.rs @@ -1,5 +1,6 @@ use iri_string::types::{UriFragmentString, UriQueryString, UriReferenceString, UriRelativeString}; use std::io::{Error as IoError, Read, Write}; +use std::{fmt::Display, str::FromStr}; use unsigned_varint::{ encode::{u64 as write_u64, u64_buffer}, io::read_u64, @@ -167,6 +168,37 @@ impl MultiDid { } } +impl Display for MultiDid { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.method)?; + if let Some(fragment) = &self.fragment { + write!(f, "#{}", fragment)?; + } + if let Some(query) = &self.query { + write!(f, "?{}", query)?; + } + Ok(()) + } +} + +#[derive(Debug, thiserror::Error)] +pub enum ParseErr { + #[error(transparent)] + Pkh(#[from] pkh::ParseErr), + #[error(transparent)] + Key(#[from] key::ParseErr), + #[error("Invalid DID")] + Invalid, +} + +impl FromStr for MultiDid { + type Err = ParseErr; + + fn from_str(s: &str) -> Result { + todo!() + } +} + #[cfg(feature = "serde")] mod serde_util { use super::*; diff --git a/multidid/src/method.rs b/multidid/src/method.rs index 3496cc1..75fba5e 100644 --- a/multidid/src/method.rs +++ b/multidid/src/method.rs @@ -1,8 +1,9 @@ -use crate::{pkh::PKH_CODEC, DidKeyTypes, DidPkhTypes, Error}; +use crate::{key, pkh, pkh::PKH_CODEC, DidKeyTypes, DidPkhTypes, Error}; use std::io::{Error as IoError, Read, Write}; +use std::{fmt::Display, str::FromStr}; use unsigned_varint::io::read_u64; -const RAW_CODEC: u64 = 0x55; +pub const RAW_CODEC: u64 = 0x55; #[derive(Debug, Clone, PartialEq)] pub enum Method { @@ -61,3 +62,36 @@ impl Method { Ok(()) } } + +impl Display for Method { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Method::Raw(raw) => write!(f, "{}", raw), + Method::Pkh(pkh) => write!(f, "pkh:{}", pkh), + Method::Key(key) => write!(f, "key:{}", key), + } + } +} + +#[derive(Debug, thiserror::Error)] +pub enum ParseErr { + #[error(transparent)] + Pkh(#[from] pkh::ParseErr), + #[error(transparent)] + Key(#[from] key::ParseErr), + #[error("Invalid DID")] + Invalid, +} + +impl FromStr for Method { + type Err = ParseErr; + + fn from_str(s: &str) -> Result { + match (s.get(..4), s.get(4..)) { + (Some("pkh:"), Some(rest)) => Ok(Self::Pkh(rest.parse()?)), + (Some("key:"), Some(rest)) => Ok(Self::Key(rest.parse()?)), + // TODO enforce did encoding + _ => Ok(Self::Raw(s.to_string())), + } + } +} diff --git a/multidid/src/pkh.rs b/multidid/src/pkh.rs index d8a0456..0f3294c 100644 --- a/multidid/src/pkh.rs +++ b/multidid/src/pkh.rs @@ -339,3 +339,20 @@ impl DidPkhTypes { Ok(()) } } + +impl Display for DidPkhTypes { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + todo!() + } +} + +#[derive(Debug, thiserror::Error)] +pub enum ParseErr {} + +impl FromStr for DidPkhTypes { + type Err = ParseErr; + + fn from_str(s: &str) -> Result { + todo!() + } +} From 8a66cd6273d743102c672e1c149851fe3b930e08 Mon Sep 17 00:00:00 2001 From: chunningham Date: Fri, 21 Jul 2023 14:12:27 +0200 Subject: [PATCH 34/70] add into_inner fns for varsig types --- varsig/src/common/ecdsa.rs | 3 +++ varsig/src/lib.rs | 4 ++++ 2 files changed, 7 insertions(+) diff --git a/varsig/src/common/ecdsa.rs b/varsig/src/common/ecdsa.rs index bbbc35f..dc8b790 100644 --- a/varsig/src/common/ecdsa.rs +++ b/varsig/src/common/ecdsa.rs @@ -24,6 +24,9 @@ impl pub fn bytes(&self) -> &[u8; LEN] { &self.bytes } + pub fn into_inner(self) -> [u8; LEN] { + self.bytes + } } pub type Es256 = Ecdsa<{ P256 as u64 }, { SHA256 as u64 }, 64, ENCODING>; diff --git a/varsig/src/lib.rs b/varsig/src/lib.rs index 7378ac5..75112f4 100644 --- a/varsig/src/lib.rs +++ b/varsig/src/lib.rs @@ -20,6 +20,10 @@ impl VarSig { pub fn sig(&self) -> &S { &self.0 } + + pub fn into_inner(self) -> S { + self.0 + } } impl VarSig { From 5ed5b35bd468c2d27cfde4588535486a3c93dcaa Mon Sep 17 00:00:00 2001 From: chunningham Date: Fri, 21 Jul 2023 14:13:36 +0200 Subject: [PATCH 35/70] rename JoseCommon -> JoseSig --- src/v2/ucan_cacao.rs | 20 ++++++++++---------- varsig/src/common/mod.rs | 4 ++-- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/v2/ucan_cacao.rs b/src/v2/ucan_cacao.rs index 4a5e2bc..77464ba 100644 --- a/src/v2/ucan_cacao.rs +++ b/src/v2/ucan_cacao.rs @@ -4,7 +4,7 @@ use serde_json::Value; use ssi_jwk::Algorithm; use ssi_ucan::Ucan; use varsig::{ - common::{Ed25519, Es256, Es256K, JoseCommon, Rsa256, Rsa512, DAG_JSON_ENCODING}, + common::{Ed25519, Es256, Es256K, JoseSig, Rsa256, Rsa512, DAG_JSON_ENCODING}, VarSig, }; @@ -43,21 +43,21 @@ impl TryFrom> for UcanCacao { } } -fn match_alg(a: Algorithm, s: Vec) -> Result, Error> { +fn match_alg(a: Algorithm, s: Vec) -> Result, Error> { Ok(match a { - Algorithm::ES256 => JoseCommon::Es256(Es256::new( + Algorithm::ES256 => JoseSig::Es256(Es256::new( s.try_into() - .map_err(|v| Error::IncorrectSignatureLength(v.len(), 64))?, + .map_err(|v: Vec| Error::IncorrectSignatureLength(v.len(), 64))?, )), - Algorithm::EdDSA => JoseCommon::Ed25519(Ed25519::new( + Algorithm::EdDSA => JoseSig::Ed25519(Ed25519::new( s.try_into() - .map_err(|v| Error::IncorrectSignatureLength(v.len(), 64))?, + .map_err(|v: Vec| Error::IncorrectSignatureLength(v.len(), 64))?, )), - Algorithm::RS256 => JoseCommon::Rsa256(Rsa256::new(s)), - Algorithm::RS512 => JoseCommon::Rsa512(Rsa512::new(s)), - Algorithm::ES256K => JoseCommon::Es256K(Es256K::new( + Algorithm::RS256 => JoseSig::Rsa256(Rsa256::new(s)), + Algorithm::RS512 => JoseSig::Rsa512(Rsa512::new(s)), + Algorithm::ES256K => JoseSig::Es256K(Es256K::new( s.try_into() - .map_err(|v| Error::IncorrectSignatureLength(v.len(), 64))?, + .map_err(|v: Vec| Error::IncorrectSignatureLength(v.len(), 64))?, )), a => return Err(Error::UnsupportedAlgorithm(a)), }) diff --git a/varsig/src/common/mod.rs b/varsig/src/common/mod.rs index 64c57ed..b093976 100644 --- a/varsig/src/common/mod.rs +++ b/varsig/src/common/mod.rs @@ -58,7 +58,7 @@ impl From> for JoseError { } } -pub enum JoseCommon { +pub enum JoseSig { Es256(Es256), Es512(Es512), Rsa256(Rsa256), @@ -67,7 +67,7 @@ pub enum JoseCommon { Es256K(Es256K), } -impl VarSigTrait for JoseCommon { +impl VarSigTrait for JoseSig { type SerError = std::convert::Infallible; type DeserError = JoseError; From 64a7a04a3113bb30f32836b431b9b5fcfecfbc59 Mon Sep 17 00:00:00 2001 From: chunningham Date: Fri, 21 Jul 2023 14:14:38 +0200 Subject: [PATCH 36/70] fixes and into_inner fns for multidids --- multidid/src/lib.rs | 5 +++++ multidid/src/method.rs | 2 +- multidid/src/pkh.rs | 3 +++ 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/multidid/src/lib.rs b/multidid/src/lib.rs index 5cfc8b3..ed5e390 100644 --- a/multidid/src/lib.rs +++ b/multidid/src/lib.rs @@ -50,6 +50,10 @@ impl MultiDid { self.query.as_ref() } + pub fn into_inner(self) -> (Method, Option, Option) { + (self.method, self.fragment, self.query) + } + pub fn to_vec(&self) -> Vec { match (&self.query, &self.fragment) { (Some(q), Some(f)) => [ @@ -143,6 +147,7 @@ impl MultiDid { .map(|q| q.as_str().len() + 1) .unwrap_or(0) + raw.len()) as u64; + writer.write_all(&[method::RAW_CODEC as u8])?; writer.write_all(write_u64(len, &mut buf))?; writer.write_all(raw.as_bytes())?; } diff --git a/multidid/src/method.rs b/multidid/src/method.rs index 75fba5e..ac7d3b0 100644 --- a/multidid/src/method.rs +++ b/multidid/src/method.rs @@ -26,7 +26,7 @@ impl Method { match self { Self::Raw(raw) => raw.as_bytes().to_vec(), Self::Pkh(h) => h.to_vec(), - Self::Key(k) => k.bytes().to_vec(), + Self::Key(k) => k.to_vec(), } } diff --git a/multidid/src/pkh.rs b/multidid/src/pkh.rs index 0f3294c..4236f28 100644 --- a/multidid/src/pkh.rs +++ b/multidid/src/pkh.rs @@ -33,6 +33,9 @@ impl Caip10 { pub fn address(&self) -> &A { &self.address } + pub fn into_inner(self) -> (C, A) { + (self.chain_id, self.address) + } } impl From<(C, A)> for Caip10 { From 29bb08bb0e7998c93a333043a1521024de3f3996 Mon Sep 17 00:00:00 2001 From: chunningham Date: Fri, 21 Jul 2023 17:31:22 +0200 Subject: [PATCH 37/70] fixes, finish conversion to/from cacao base types (ucan/recap) --- src/v2/mod.rs | 75 +++++++++++-- src/v2/recap_cacao.rs | 252 ++++++++++++++++++++++++++++++++---------- src/v2/ucan_cacao.rs | 84 ++++++++++---- 3 files changed, 326 insertions(+), 85 deletions(-) diff --git a/src/v2/mod.rs b/src/v2/mod.rs index 0d97381..c1c6e90 100644 --- a/src/v2/mod.rs +++ b/src/v2/mod.rs @@ -1,6 +1,7 @@ -use libipld::cid::Cid; +use async_trait::async_trait; +use libipld::{cid::Cid, Ipld}; use serde::{Deserialize, Serialize}; -use std::fmt::Debug; +use std::{collections::BTreeMap, fmt::Debug}; use ucan_capabilities_object::Capabilities; pub mod either; @@ -8,16 +9,24 @@ pub mod recap_cacao; pub mod ucan_cacao; use multidid::MultiDid; -use varsig::{VarSig, VarSigTrait}; +use varsig::{ + common::{Ethereum, JoseSig, DAG_JSON_ENCODING, EIP191_ENCODING}, + EitherSignature, VarSig, VarSigTrait, +}; #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] -pub struct Cacao { +#[serde(deny_unknown_fields)] +pub struct Cacao< + S = EitherSignature, Ethereum>, + F = BTreeMap, + NB = Ipld, +> { #[serde(rename = "iss")] issuer: MultiDid, #[serde(rename = "aud")] audience: MultiDid, - #[serde(rename = "s")] - signature: S, + #[serde(rename = "s", bound = "S: VarSigTrait")] + signature: VarSig, #[serde(rename = "v")] version: String, #[serde(rename = "att")] @@ -36,9 +45,57 @@ pub struct Cacao { facts: Option, } -pub trait CacaoProfile { - type Signature: VarSigTrait; - type Facts: Serialize + for<'d> Deserialize<'d>; +impl Cacao { + pub fn issuer(&self) -> &MultiDid { + &self.issuer + } + + pub fn audience(&self) -> &MultiDid { + &self.audience + } + + pub fn capabilities(&self) -> &Capabilities { + &self.attenuations + } + + pub fn nonce(&self) -> Option<&str> { + self.nonce.as_deref() + } + + pub fn proof(&self) -> Option<&[Cid]> { + self.proof.as_deref() + } + + pub fn issued_at(&self) -> Option { + self.issued_at + } + + pub fn not_before(&self) -> Option { + self.not_before + } + + pub fn expiration(&self) -> Option { + self.expiration + } + + pub fn facts(&self) -> Option<&F> { + self.facts.as_ref() + } + + pub async fn verify(&self, verifier: &V) -> Result<(), V::Error> + where + V: Verifier, + NB: Send + Sync, + { + verifier.verify(self).await + } +} + +#[async_trait] +pub trait Verifier { + type Facts; + type Error: std::error::Error; + async fn verify(&self, cacao: &Cacao) -> Result<(), Self::Error>; } #[cfg(test)] diff --git a/src/v2/recap_cacao.rs b/src/v2/recap_cacao.rs index d3cc97f..50e40fa 100644 --- a/src/v2/recap_cacao.rs +++ b/src/v2/recap_cacao.rs @@ -1,17 +1,15 @@ -use super::Cacao; +use super::{Cacao, Verifier}; +use async_trait::async_trait; use http::uri::Authority; use iri_string::types::UriString; use multidid::{DidPkhTypes, Method, MultiDid}; use serde::{Deserialize, Serialize}; use serde_json::Value; -use serde_with::{serde_as, DeserializeFromStr, DisplayFromStr, SerializeDisplay}; pub use siwe; -use siwe::{eip55, Message, TimeStamp, VerificationError as SVE, Version as SVersion}; +use siwe::{Message, TimeStamp}; use siwe_recap::Capability; -use std::fmt::Debug; -use std::io::{Read, Seek, Write}; -use thiserror::Error; -use time::UtcOffset; +use std::{fmt::Debug, str::FromStr}; +use time::{format_description::well_known::Rfc3339, OffsetDateTime, UtcOffset}; use varsig::{ common::{Ethereum, EIP191_ENCODING}, VarSig, @@ -19,22 +17,19 @@ use varsig::{ pub type RecapCacao = Cacao, RecapFacts, NB>; -#[serde_as] #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +#[serde(deny_unknown_fields)] pub struct RecapFacts { #[serde(rename = "iat-z")] - iat_time_zone: Option, + iat_info: String, #[serde(rename = "nbf-z")] - nbf_time_zone: Option, + nbf_info: Option, #[serde(rename = "nbf-z")] - exp_time_zone: Option, - #[serde(rename = "iat-n")] - iat_nanos: Option, - #[serde(rename = "nbf-n")] - nbf_nanos: Option, - #[serde(rename = "exp-n")] - exp_nanos: Option, - #[serde_as(as = "DisplayFromStr")] + exp_info: Option, + #[serde( + serialize_with = "serialize_authority", + deserialize_with = "deserialize_authority" + )] domain: Authority, statement: Option, #[serde(rename = "request-id")] @@ -43,22 +38,62 @@ pub struct RecapFacts { } #[derive(thiserror::Error, Debug)] -pub enum Error {} +pub enum Error { + #[error(transparent)] + StatementVerification(#[from] siwe_recap::VerificationError), + #[error(transparent)] + MultididParse(#[from] multidid::ParseErr), + #[error(transparent)] + TimeConversion(#[from] time::error::Error), + #[error("Expected did:pkh:eip155, found {0}")] + IncorrectDidType(Method), + #[error("Recap Issuer DIDs must not have a fragment or query parameters")] + ExtraDidComponents, + #[error("The 'facts' field is required for Recap Cacaos")] + MissingFacts, + #[error("Failed to parse chain ID: {0}")] + ChainId(#[from] std::num::ParseIntError), + #[error("Incorrect Version, expected '1', found: {0}")] + IncorrectVersion(String), + #[error("SIWE messages must have a nonce")] + MissingNonce, + #[error("SIWE messages must have a issuance timestamp")] + MissingIat, + #[error("UNIX timestamps must have associated timezone facts")] + InconsistentTimeInfo, + #[error(transparent)] + MessageBuild(#[from] siwe_recap::EncodingError), + #[error("Failed to parse aud URI: {0}")] + AudUri(#[from] iri_string::validate::Error), + #[error("Invalid Signature: {0}")] + InvalidSignature(#[from] siwe::VerificationError), +} -impl TryFrom<(Message, Vec)> for RecapCacao +impl TryFrom<(Message, [u8; 65])> for RecapCacao where NB: for<'d> Deserialize<'d>, { type Error = Error; - fn try_from((siwe, sig): (Message, Vec)) -> Result { + fn try_from((siwe, sig): (Message, [u8; 65])) -> Result { let recap = Capability::::extract_and_verify(&siwe)?; - let (issued_at, iat_time_zone, iat_nanos) = split_tz(Some(siwe.issued_at)); - let (not_before, nbf_time_zone, nbf_nanos) = split_tz(siwe.not_before); - let (expiration, exp_time_zone, exp_nanos) = split_tz(siwe.expiration_time); - let statement = siwe - .statement - .map(|s| s.get(0..(s.len() - recap.to_statement().len()))); - let (attenuations, proof) = recap.into_inner(); + let (issued_at, iat_info) = split_tz(siwe.issued_at); + let (not_before, nbf_info) = match siwe.not_before.map(split_tz) { + Some((nb, tz)) => (Some(nb), Some(tz)), + None => (None, None), + }; + let (expiration, exp_info) = match siwe.expiration_time.map(split_tz) { + Some((exp, tz)) => (Some(exp), Some(tz)), + None => (None, None), + }; + let statement = siwe.statement.and_then(|s| { + s.get(0..(s.len() - recap.as_ref().map(|r| r.to_statement().len()).unwrap_or(0))) + .map(|s| s.to_string()) + }); + let (attenuations, proof) = recap + .map(|r| r.into_inner()) + .unwrap_or((Default::default(), Vec::new())); + let mut resources = siwe.resources; + resources.pop(); Ok(Self { issuer: MultiDid::new( Method::Pkh(DidPkhTypes::Eip155( @@ -67,51 +102,154 @@ where None, None, ), - audience: MultiDid::from_str(siwe.uri)?, - signature: VarSig::new(Ethereum::sig.try_into()?), + audience: MultiDid::from_str(siwe.uri.as_str())?, + signature: VarSig::new(Ethereum::new(sig)), version: (siwe.version as u8).to_string(), attenuations, nonce: Some(siwe.nonce), - proof, - issued_at, + proof: Some(proof), + issued_at: Some(issued_at), not_before, expiration, facts: Some(RecapFacts { - iat_time_zone, - nbf_time_zone, - exp_time_zone, - iat_nanos, - nbf_nanos, - exp_nanos, + iat_info, + nbf_info, + exp_info, domain: siwe.domain, request_id: siwe.request_id, - resources: siwe.resources[0..-1], + resources, statement, }), }) } } -fn split_tz(t: Option) -> (Option, Option, Option) { - match t { - Some(t) => ( - Some(t.as_ref().unix_timestamp().into()), - Some(t.offset()), - Some(t.nanosecond()), - ), - None => (None, None, None), +impl TryFrom> for (Message, [u8; 65]) +where + NB: Serialize, +{ + type Error = Error; + fn try_from(cacao: RecapCacao) -> Result { + if cacao.issuer.fragment().is_some() || cacao.issuer.query().is_some() { + return Err(Error::ExtraDidComponents); + } + let (chain_id, address) = match cacao.issuer.into_inner().0 { + Method::Pkh(DidPkhTypes::Eip155(eip155)) => eip155.into_inner(), + m => return Err(Error::IncorrectDidType(m)), + }; + let facts = cacao.facts.ok_or(Error::MissingFacts)?; + let mut cap = Capability::new().with_proofs(cacao.proof.unwrap_or_default().iter()); + for (resource, actions) in cacao.attenuations.into_inner() { + cap.with_actions(resource, actions); + } + Ok(( + cap.build_message(Message { + domain: facts.domain, + address, + statement: facts.statement, + uri: cacao.audience.to_string().parse()?, + version: cacao + .version + .parse() + .map_err(|_| Error::IncorrectVersion(cacao.version))?, + chain_id: chain_id.parse()?, + nonce: cacao.nonce.ok_or(Error::MissingNonce)?, + issued_at: make_ts(cacao.issued_at.ok_or(Error::MissingIat)?, &facts.iat_info)?, + expiration_time: match (cacao.expiration, facts.exp_info) { + (Some(exp), Some(zexp)) => Some(make_ts(exp, &zexp)?), + (None, None) => None, + _ => return Err(Error::InconsistentTimeInfo), + }, + not_before: match (cacao.not_before, facts.nbf_info) { + (Some(nbf), Some(znbf)) => Some(make_ts(nbf, &znbf)?), + (None, None) => None, + _ => return Err(Error::InconsistentTimeInfo), + }, + request_id: facts.request_id, + resources: facts.resources, + })?, + cacao.signature.into_inner().into_inner(), + )) } } -fn get_tz((unix, tz, nano): (Option, Option, Option)) -> Option { - match (unix, tz, nano) { - (Some(unix), Some(tz), Some(nano)) if nano < 1_000_000_000 => Some( - TimeStamp::from_unix_timestamp(unix) - .replace_offset(tz) - .replace_nanosecond(nano) - // lets just assume nano < 1000000000, we check before here - .unwrap(), - ), - _ => None, +fn split_tz(t: TimeStamp) -> (u64, String) { + let unix = t.as_ref().unix_timestamp(); + let mut t_str = t.to_string(); + ( + // hmmm + unix as u64, + t_str + // if its fractional, split on '. + .find('.') + // if not, split on 'Z' or 'z' + .or_else(|| t_str.find('Z')) + .or_else(|| t_str.find('z')) + .map(|i| t_str.split_off(i)) + // this default should never actually happen + // as long as TimeStamp serialises properly + .unwrap_or(t.as_ref().offset().to_string()), + ) +} + +fn make_ts(unix: u64, z: &str) -> Result { + // we need to get the serialisation of the date and time + let odt = OffsetDateTime::from_unix_timestamp(unix as i64)? + // by setting the offset to the same one in z + .to_offset(UtcOffset::parse( + if z.starts_with('.') { + z.find('Z') + .or_else(|| z.find('z')) + .and_then(|i| z.get((i + 1)..)) + .unwrap_or(z) + } else { + z + }, + &Rfc3339, + )?) + .to_string(); + + // then concat that serialisation with z to get the original timestamp + TimeStamp::from_str( + &[ + odt.find('.') + .or_else(|| odt.find('Z')) + .or_else(|| odt.find('z')) + .and_then(|i| odt.get(..i)) + .unwrap_or(&odt), + z, + ] + .concat(), + ) +} + +fn serialize_authority(authority: &Authority, serializer: S) -> Result +where + S: serde::Serializer, +{ + serializer.serialize_str(authority.as_str()) +} + +fn deserialize_authority<'de, D>(deserializer: D) -> Result +where + D: serde::Deserializer<'de>, +{ + let s = String::deserialize(deserializer)?; + Authority::from_str(&s).map_err(serde::de::Error::custom) +} + +pub struct RecapVerify; + +#[async_trait] +impl Verifier> for RecapVerify +where + NB: Send + Sync + Serialize, +{ + type Facts = RecapFacts; + type Error = Error; + async fn verify(&self, cacao: &RecapCacao) -> Result<(), Self::Error> { + let (message, signature) = <(Message, [u8; 65])>::try_from(*cacao.clone())?; + message.verify_eip191(&signature)?; + Ok(()) } } diff --git a/src/v2/ucan_cacao.rs b/src/v2/ucan_cacao.rs index 77464ba..35bb702 100644 --- a/src/v2/ucan_cacao.rs +++ b/src/v2/ucan_cacao.rs @@ -1,37 +1,41 @@ -use super::{Cacao, CacaoProfile}; +use super::{Cacao, Verifier}; +use async_trait::async_trait; use multidid::MultiDid; +use serde::{Deserialize, Serialize}; use serde_json::Value; +use ssi_dids::did_resolve::DIDResolver; use ssi_jwk::Algorithm; -use ssi_ucan::Ucan; +use ssi_ucan::{Payload, Ucan}; +use std::collections::BTreeMap; +use std::str::FromStr; use varsig::{ common::{Ed25519, Es256, Es256K, JoseSig, Rsa256, Rsa512, DAG_JSON_ENCODING}, VarSig, }; -pub type UcanCacao = Cacao, F, NB>; +pub type UcanCacao = Cacao, BTreeMap, NB>; #[derive(thiserror::Error, Debug)] pub enum Error { - #[error("Unsupported Algorithm: {0}")] + #[error("Unsupported Algorithm")] UnsupportedAlgorithm(Algorithm), #[error("Incorrect Signature Length: recieved {0}, expected {1}")] IncorrectSignatureLength(usize, usize), - #[error("Missing Version Header")] - MissingVersionHeader, + #[error(transparent)] + MultididParse(#[from] multidid::ParseErr), + #[error(transparent)] + Ucan(#[from] ssi_ucan::Error), } -impl TryFrom> for UcanCacao { +impl TryFrom> for UcanCacao { type Error = Error; - fn try_from(ucan: Ucan) -> Result { + fn try_from(ucan: Ucan) -> Result { let (header, payload, signature) = ucan.into_inner(); Ok(Self { - issuer: MultiDid::from_str(payload.issuer)?, - audience: MultiDid::from_str(payload.audience)?, - signature: VarSig::new(match_alg(header.alg, ucan.signature)?), - version: header - .additional_parameters - .get("ucv") - .ok_or_else(|| Error::MissingVersionHeader)?, + issuer: MultiDid::from_str(&payload.issuer)?, + audience: MultiDid::from_str(&payload.audience)?, + signature: VarSig::new(match_alg(header.algorithm, signature)?), + version: "0.2.0".to_string(), attenuations: payload.capabilities, nonce: payload.nonce, proof: payload.proof, @@ -43,6 +47,28 @@ impl TryFrom> for UcanCacao { } } +impl From> for Ucan { + fn from(cacao: UcanCacao) -> Self { + let (algorithm, signature) = match cacao.signature.into_inner() { + JoseSig::Ed25519(s) => (Algorithm::EdDSA, s.bytes().to_vec()), + JoseSig::Es256(s) => (Algorithm::ES256, s.bytes().to_vec()), + JoseSig::Es512(s) => (Algorithm::ES256, s.bytes().to_vec()), + JoseSig::Es256K(s) => (Algorithm::ES256K, s.bytes().to_vec()), + JoseSig::Rsa256(s) => (Algorithm::RS256, s.bytes().to_vec()), + JoseSig::Rsa512(s) => (Algorithm::RS512, s.bytes().to_vec()), + }; + let mut payload = Payload::new(cacao.issuer.to_string(), cacao.audience.to_string()); + payload.capabilities = cacao.attenuations; + payload.nonce = cacao.nonce; + payload.proof = cacao.proof; + payload.issued_at = cacao.issued_at; + payload.not_before = cacao.not_before; + payload.expiration = cacao.expiration; + payload.facts = cacao.facts; + payload.sign(todo!(), signature) + } +} + fn match_alg(a: Algorithm, s: Vec) -> Result, Error> { Ok(match a { Algorithm::ES256 => JoseSig::Es256(Es256::new( @@ -63,9 +89,29 @@ fn match_alg(a: Algorithm, s: Vec) -> Result, Error }) } -pub struct UcanCacaoProfile; +#[async_trait] +impl Verifier> for T +where + T: DIDResolver, + NB: Send + Sync + Serialize + for<'d> Deserialize<'d>, +{ + type Facts = BTreeMap; + type Error = Error; + async fn verify(&self, cacao: &UcanCacao) -> Result<(), Self::Error> { + let ucan = Ucan::::from(*cacao.clone()).encode_as_canonicalized_jwt()?; + Ucan::::decode_and_verify(&ucan, self).await?; + Ok(()) + } +} + +#[cfg(test)] +pub mod tests { + use super::*; -impl CacaoProfile for UcanCacaoProfile { - type Signature = JoseCommon; - type Facts = Value; + #[test] + fn tz() { + use time::OffsetDateTime; + let time = OffsetDateTime::now_local().unwrap(); + println!("{}", time.offset()); + } } From c4927adfab5d9321f00ede7f9fb92a2e73b10414 Mon Sep 17 00:00:00 2001 From: chunningham Date: Fri, 21 Jul 2023 17:31:51 +0200 Subject: [PATCH 38/70] temp git links for dependancies --- Cargo.toml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Cargo.toml b/Cargo.toml index f554ba0..4dfa16b 100755 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,6 +25,10 @@ serde = "1" serde_json = "1" libipld = { version = "0.16", default-features = false, features = ["derive", "serde-codec", "dag-cbor"]} ucan-capabilities-object = "0.1" +ssi-ucan = { git = "https://github.com/spruceid/ssi.git", branch = "feat/ucan-0.10" } +ssi-jwk = { git = "https://github.com/spruceid/ssi.git", branch = "feat/ucan-0.10" } +ssi-jwt = { git = "https://github.com/spruceid/ssi.git", branch = "feat/ucan-0.10" } +ssi-dids = { git = "https://github.com/spruceid/ssi.git", branch = "feat/ucan-0.10" } time = { version = "0.3", features = ["parsing", "formatting", "serde"] } http = "0.2.5" hex = { version = "0.4", optional = true } From ec2f36190b60c7515da4f7528daaebc3d99d39a4 Mon Sep 17 00:00:00 2001 From: chunningham Date: Mon, 14 Aug 2023 14:50:32 +0200 Subject: [PATCH 39/70] fixes for pkh, get eip55 working --- multidid/Cargo.toml | 1 + multidid/src/pkh.rs | 150 +++++++++++++++++++++++++++++++++++++++----- 2 files changed, 134 insertions(+), 17 deletions(-) diff --git a/multidid/Cargo.toml b/multidid/Cargo.toml index b3f37d4..d99712a 100644 --- a/multidid/Cargo.toml +++ b/multidid/Cargo.toml @@ -14,6 +14,7 @@ unsigned-varint = { version = "0.7", features = ["std"] } thiserror = "1" bs58 = "0.5" serde = { version = "1", optional = true } +hex = "0.4" [dev-dependencies] serde_json = "1" diff --git a/multidid/src/pkh.rs b/multidid/src/pkh.rs index 4236f28..b59b5a2 100644 --- a/multidid/src/pkh.rs +++ b/multidid/src/pkh.rs @@ -1,5 +1,9 @@ +use sha3::{Digest, Keccak256}; use std::io::{Error as IoError, Read, Write}; -use std::{fmt::Display, str::FromStr}; +use std::{ + fmt::{Display, Formatter, Result as FmtResult}, + str::FromStr, +}; use unsigned_varint::{ encode::{u64 as write_u64, u64_buffer}, io::read_u64, @@ -10,7 +14,7 @@ pub(crate) const PKH_CODEC: u64 = 0xca; #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub enum DidPkhTypes { Bip122(Caip10<[u8; 32], [u8; 25]>), - Eip155(Caip10), + Eip155(Caip10), Cosmos(Caip10), Starknet(Caip10), Hedera(Caip10), @@ -121,8 +125,7 @@ impl DidPkhTypes { } DidPkhTypes::Eip155(caip10) => { vec.extend(write_u64(2, &mut buf)); - vec.extend(write_u64(caip10.chain_id().len() as u64, &mut buf)); - vec.extend(caip10.chain_id().as_bytes()); + vec.extend(write_u64(*caip10.chain_id(), &mut buf)); vec.extend(caip10.address()); } DidPkhTypes::Cosmos(caip10) => { @@ -190,15 +193,10 @@ impl DidPkhTypes { } // ethereum-like 2 => { - let ref_len = read_u64(reader.by_ref())?; - let mut chain_id = vec![0u8; ref_len as usize]; - reader.read_exact(&mut chain_id)?; + let chain_id = read_u64(reader.by_ref())?; let mut address = [0u8; 20]; reader.read_exact(&mut address)?; - Ok(DidPkhTypes::Eip155(Caip10::new( - String::from_utf8(chain_id)?, - address, - ))) + Ok(DidPkhTypes::Eip155(Caip10::new(chain_id, address))) } // cosmos 3 => { @@ -289,8 +287,7 @@ impl DidPkhTypes { } DidPkhTypes::Eip155(caip10) => { writer.write_all(write_u64(2, &mut buf))?; - writer.write_all(write_u64(caip10.chain_id().len() as u64, &mut buf))?; - writer.write_all(caip10.chain_id().as_bytes())?; + writer.write_all(write_u64(*caip10.chain_id() as u64, &mut buf))?; writer.write_all(caip10.address())?; } DidPkhTypes::Cosmos(caip10) => { @@ -344,18 +341,137 @@ impl DidPkhTypes { } impl Display for DidPkhTypes { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - todo!() + fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { + match self { + // Self::Bip122(c) => write!(f, "bip122:{}:{}", c.chain_id(), c.address()), + Self::Eip155(c) => write!(f, "eip155:{}:{}", c.chain_id(), eip55(c.address())), + // Self::Cosmos(c) => write!(f, "cosmos:{}:{}", c.chain_id(), c.address()), + // Self::Starknet(c) => write!(f, "starknet:{}:{}", c.chain_id(), c.address()), + // Self::Hedera(c) => write!(f, "hedera:{}:{}", c.chain_id(), c.address()), + // Self::Lip9(c) => write!(f, "lip9:{}:{}", c.chain_id(), c.address()), + _ => todo!(), + } } } #[derive(Debug, thiserror::Error)] -pub enum ParseErr {} +pub enum ParseErr { + #[error("Invalid DID")] + InvalidDid, + #[error("Invalid Integer")] + InvalidInteger(#[from] std::num::ParseIntError), + #[error("Invalid EIP55: {0}")] + Eip55(#[from] Eip55Err), +} impl FromStr for DidPkhTypes { type Err = ParseErr; fn from_str(s: &str) -> Result { - todo!() + Ok( + match s + .split_once(":") + .and_then(|(method, r)| Some((method, r.split_once(":")?))) + { + Some(("bip122", (chain_id, address))) => { + Self::Bip122(Caip10::new(todo!(), todo!())) + } + Some(("eip155", (chain_id, address))) => { + Self::Eip155(Caip10::new(chain_id.parse()?, parse_eip55(address)?)) + } + Some(("cosmos", (chain_id, address))) => { + Self::Cosmos(Caip10::new(chain_id.to_string(), todo!())) + } + Some(("starknet", (chain_id, address))) => { + Self::Starknet(Caip10::new(chain_id.to_string(), todo!())) + } + Some(("hedera", (chain_id, address))) => { + Self::Hedera(Caip10::new(chain_id.to_string(), todo!())) + } + Some(("lip9", (chain_id, address))) => Self::Lip9(Caip10::new(todo!(), todo!())), + _ => return Err(ParseErr::InvalidDid), + }, + ) + } +} + +impl Display for CosmosAddress { + fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { + match self { + CosmosAddress::Secp256k1(address) => { + for byte in address { + write!(f, "{:02x}", byte)?; + } + } + CosmosAddress::Secp256r1(address) => { + for byte in address { + write!(f, "{:02x}", byte)?; + } + } + } + Ok(()) + } +} + +impl Display for HederaAddress { + fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { + match self { + HederaAddress::EVM(address) => { + for byte in address { + write!(f, "{:02x}", byte)?; + } + } + HederaAddress::Ed25519(address) => { + for byte in address { + write!(f, "{:02x}", byte)?; + } + } + HederaAddress::Secp256k1(address) => { + for byte in address { + write!(f, "{:02x}", byte)?; + } + } + } + Ok(()) + } +} + +/// Takes an eth address and returns it as a checksum formatted string. +pub fn eip55(addr: &[u8; 20]) -> String { + let addr_str = hex::encode(addr); + let hash = Keccak256::digest(addr_str.as_bytes()); + "0x".chars() + .chain(addr_str.chars().enumerate().map(|(i, c)| { + match (c, hash[i >> 1] & if i % 2 == 0 { 128 } else { 8 } != 0) { + ('a'..='f' | 'A'..='F', true) => c.to_ascii_uppercase(), + _ => c.to_ascii_lowercase(), + } + })) + .collect() +} + +#[derive(Debug, thiserror::Error)] +pub enum Eip55Err { + #[error("Missing Prefix 0x")] + MissingPrefix, + #[error("Invalid Checksum")] + InvalidChecksum, + #[error("Invalid hex: {0}")] + InvalidHex(#[from] hex::FromHexError), +} + +fn parse_eip55(address: &str) -> Result<[u8; 20], Eip55Err> { + use hex::FromHex; + if !address.starts_with("0x") { + Err(Eip55Err::MissingPrefix) + } else { + let s = <[u8; 20]>::from_hex(address)?; + let sum = eip55(&s); + let sum = sum.trim_start_matches("0x"); + if sum != address { + Err(Eip55Err::InvalidChecksum) + } else { + Ok(s) + } } } From 802f347b10c06012d6beb66073e2035020932665 Mon Sep 17 00:00:00 2001 From: chunningham Date: Mon, 14 Aug 2023 14:50:48 +0200 Subject: [PATCH 40/70] parsing for multidids --- multidid/src/lib.rs | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/multidid/src/lib.rs b/multidid/src/lib.rs index ed5e390..45256d8 100644 --- a/multidid/src/lib.rs +++ b/multidid/src/lib.rs @@ -175,7 +175,7 @@ impl MultiDid { impl Display for MultiDid { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}", self.method)?; + write!(f, "did:{}", self.method)?; if let Some(fragment) = &self.fragment { write!(f, "#{}", fragment)?; } @@ -189,18 +189,31 @@ impl Display for MultiDid { #[derive(Debug, thiserror::Error)] pub enum ParseErr { #[error(transparent)] - Pkh(#[from] pkh::ParseErr), + Did(#[from] method::ParseErr), #[error(transparent)] - Key(#[from] key::ParseErr), - #[error("Invalid DID")] - Invalid, + QueryOrFragment(#[from] iri_string::validate::Error), } impl FromStr for MultiDid { type Err = ParseErr; fn from_str(s: &str) -> Result { - todo!() + let (did, query, fragment) = if let Some((did, rest)) = s.split_once('?') { + if let Some((query, fragment)) = rest.split_once('#') { + (did, Some(query), Some(fragment)) + } else { + (did, Some(rest), None) + } + } else if let Some((did, rest)) = s.split_once('#') { + (did, None, Some(rest)) + } else { + (s, None, None) + }; + Ok(Self { + method: Method::from_str(did)?, + query: query.map(|q| q.parse()).transpose()?, + fragment: fragment.map(|f| f.parse()).transpose()?, + }) } } From 1faea8235a268d0a0026202cfbd7c60fdf3c898d Mon Sep 17 00:00:00 2001 From: chunningham Date: Tue, 29 Aug 2023 21:41:53 +0200 Subject: [PATCH 41/70] varsig util trait derives --- varsig/src/common/ed25519.rs | 2 +- varsig/src/common/mod.rs | 1 + varsig/src/common/rsa.rs | 2 +- varsig/src/lib.rs | 2 +- 4 files changed, 4 insertions(+), 3 deletions(-) diff --git a/varsig/src/common/ed25519.rs b/varsig/src/common/ed25519.rs index 6270582..715dcb1 100644 --- a/varsig/src/common/ed25519.rs +++ b/varsig/src/common/ed25519.rs @@ -8,7 +8,7 @@ use unsigned_varint::{ pub const ED25519: u16 = 0xed; -#[derive(Debug, Clone, PartialEq, Eq)] +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct Ed25519 { bytes: [u8; 64], } diff --git a/varsig/src/common/mod.rs b/varsig/src/common/mod.rs index b093976..2eda33f 100644 --- a/varsig/src/common/mod.rs +++ b/varsig/src/common/mod.rs @@ -58,6 +58,7 @@ impl From> for JoseError { } } +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub enum JoseSig { Es256(Es256), Es512(Es512), diff --git a/varsig/src/common/rsa.rs b/varsig/src/common/rsa.rs index 5fdef5f..f2959cf 100644 --- a/varsig/src/common/rsa.rs +++ b/varsig/src/common/rsa.rs @@ -8,7 +8,7 @@ use unsigned_varint::{ pub const RSA: u16 = 0x1205; -#[derive(Debug, Clone, PartialEq, Eq)] +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct Rsa { bytes: Vec, } diff --git a/varsig/src/lib.rs b/varsig/src/lib.rs index 75112f4..fb3a94c 100644 --- a/varsig/src/lib.rs +++ b/varsig/src/lib.rs @@ -9,7 +9,7 @@ pub use traits::{DeserError, SerError, VarSigTrait}; const VARSIG_VARINT_PREFIX: u8 = 0x34; -#[derive(Debug, Clone, PartialEq, Eq)] +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct VarSig(S); impl VarSig { From 76d24f0f5fc5cd9d3f30a3133e1d99572c64b150 Mon Sep 17 00:00:00 2001 From: chunningham Date: Tue, 29 Aug 2023 22:51:22 +0200 Subject: [PATCH 42/70] cacao verification implementations --- src/v2/either.rs | 182 +++++++++++++++++++++++++++++++++++++++--- src/v2/mod.rs | 33 ++++---- src/v2/recap_cacao.rs | 28 +++---- src/v2/ucan_cacao.rs | 35 ++++---- src/v2/version.rs | 10 +-- 5 files changed, 223 insertions(+), 65 deletions(-) diff --git a/src/v2/either.rs b/src/v2/either.rs index e2bf842..a64535d 100644 --- a/src/v2/either.rs +++ b/src/v2/either.rs @@ -1,18 +1,178 @@ -use super::CacaoProfile; +use super::{ + recap_cacao::{Error as RecapError, RecapCacao, RecapFacts, RecapSignature, RecapVerify}, + ucan_cacao::{Error as UcanError, UcanCacao, UcanFacts, UcanSignature}, + Cacao, CacaoVerifier, +}; +use async_trait::async_trait; use serde::{Deserialize, Serialize}; -use varsig::either::EitherSignature; +use serde_json::Value; +use ssi_dids::did_resolve::DIDResolver; +use varsig::{either::EitherSignature, VarSig}; -#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] -pub enum Either { - A(A::Facts), - B(B::Facts), +type CommonSignature = EitherSignature; + +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Eq, PartialOrd, Hash)] +#[serde(untagged)] +pub enum CommonFacts { + Recap(RecapFacts), + Ucan(UcanFacts), +} + +pub type CommonCacao = Cacao, NB>; + +pub struct CommonVerifier(T); + +impl CommonVerifier { + pub fn new(t: T) -> Self { + Self(t) + } +} + +#[derive(thiserror::Error, Debug)] +pub enum Error { + #[error(transparent)] + Ucan(#[from] UcanError), + #[error(transparent)] + Recap(#[from] RecapError), + #[error("Signature and Facts Mismatch")] + Mismatch, } -impl CacaoProfile for Either +#[async_trait] +impl CacaoVerifier, NB> for CommonVerifier where - A: CacaoProfile, - B: CacaoProfile, + R: Send + Sync + DIDResolver, + F: Send + Sync + for<'a> Deserialize<'a> + Serialize + Clone, + NB: Send + Sync + for<'a> Deserialize<'a> + Serialize + Clone, { - type Signature = EitherSignature; - type Facts = Either; + type Error = Error; + + async fn verify(&self, cacao: &CommonCacao) -> Result<(), Self::Error> { + Ok(match RecapCacao::try_from(cacao.clone()) { + Ok(recap) => RecapVerify::default().verify(&recap).await?, + Err(c) => match UcanCacao::try_from(c) { + Ok(ucan) => self.0.verify(&ucan).await?, + Err(_) => { + return Err(Error::Mismatch); + } + }, + }) + } +} + +impl From> for CommonCacao { + fn from(recap: RecapCacao) -> Self { + CommonCacao { + issuer: recap.issuer, + audience: recap.audience, + version: recap.version, + attenuations: recap.attenuations, + nonce: recap.nonce, + proof: recap.proof, + issued_at: recap.issued_at, + not_before: recap.not_before, + expiration: recap.expiration, + facts: recap.facts.map(CommonFacts::Recap), + signature: VarSig::new(EitherSignature::A(recap.signature.into_inner())), + } + } +} + +impl From> for CommonCacao { + fn from(ucan: UcanCacao) -> Self { + CommonCacao { + issuer: ucan.issuer, + audience: ucan.audience, + version: ucan.version, + attenuations: ucan.attenuations, + nonce: ucan.nonce, + proof: ucan.proof, + issued_at: ucan.issued_at, + not_before: ucan.not_before, + expiration: ucan.expiration, + facts: ucan.facts.map(CommonFacts::Ucan), + signature: VarSig::new(EitherSignature::B(ucan.signature.into_inner())), + } + } +} + +impl TryFrom> for RecapCacao { + type Error = CommonCacao; + fn try_from(cacao: CommonCacao) -> Result { + match (cacao.facts, cacao.signature.into_inner()) { + (Some(CommonFacts::Recap(facts)), EitherSignature::A(sig)) => Ok(RecapCacao { + issuer: cacao.issuer, + audience: cacao.audience, + version: cacao.version, + attenuations: cacao.attenuations, + nonce: cacao.nonce, + proof: cacao.proof, + issued_at: cacao.issued_at, + not_before: cacao.not_before, + expiration: cacao.expiration, + facts: Some(facts), + signature: VarSig::new(sig), + }), + (facts, sig) => Err(CommonCacao { + issuer: cacao.issuer, + audience: cacao.audience, + version: cacao.version, + attenuations: cacao.attenuations, + nonce: cacao.nonce, + proof: cacao.proof, + issued_at: cacao.issued_at, + not_before: cacao.not_before, + expiration: cacao.expiration, + facts, + signature: VarSig::new(sig), + }), + } + } +} + +impl TryFrom> for UcanCacao { + type Error = CommonCacao; + fn try_from(cacao: CommonCacao) -> Result { + match (cacao.facts, cacao.signature.into_inner()) { + (Some(CommonFacts::Ucan(facts)), EitherSignature::B(sig)) => Ok(UcanCacao { + issuer: cacao.issuer, + audience: cacao.audience, + version: cacao.version, + attenuations: cacao.attenuations, + nonce: cacao.nonce, + proof: cacao.proof, + issued_at: cacao.issued_at, + not_before: cacao.not_before, + expiration: cacao.expiration, + facts: Some(facts), + signature: VarSig::new(sig), + }), + (None, EitherSignature::B(sig)) => Ok(UcanCacao { + issuer: cacao.issuer, + audience: cacao.audience, + version: cacao.version, + attenuations: cacao.attenuations, + nonce: cacao.nonce, + proof: cacao.proof, + issued_at: cacao.issued_at, + not_before: cacao.not_before, + expiration: cacao.expiration, + facts: None, + signature: VarSig::new(sig), + }), + (facts, sig) => Err(CommonCacao { + issuer: cacao.issuer, + audience: cacao.audience, + version: cacao.version, + attenuations: cacao.attenuations, + nonce: cacao.nonce, + proof: cacao.proof, + issued_at: cacao.issued_at, + not_before: cacao.not_before, + expiration: cacao.expiration, + facts, + signature: VarSig::new(sig), + }), + } + } } diff --git a/src/v2/mod.rs b/src/v2/mod.rs index c1c6e90..da48df7 100644 --- a/src/v2/mod.rs +++ b/src/v2/mod.rs @@ -1,7 +1,7 @@ use async_trait::async_trait; use libipld::{cid::Cid, Ipld}; use serde::{Deserialize, Serialize}; -use std::{collections::BTreeMap, fmt::Debug}; +use std::fmt::Debug; use ucan_capabilities_object::Capabilities; pub mod either; @@ -9,24 +9,15 @@ pub mod recap_cacao; pub mod ucan_cacao; use multidid::MultiDid; -use varsig::{ - common::{Ethereum, JoseSig, DAG_JSON_ENCODING, EIP191_ENCODING}, - EitherSignature, VarSig, VarSigTrait, -}; +use varsig::{VarSig, VarSigTrait}; -#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Eq, Hash)] #[serde(deny_unknown_fields)] -pub struct Cacao< - S = EitherSignature, Ethereum>, - F = BTreeMap, - NB = Ipld, -> { +pub struct Cacao { #[serde(rename = "iss")] issuer: MultiDid, #[serde(rename = "aud")] audience: MultiDid, - #[serde(rename = "s", bound = "S: VarSigTrait")] - signature: VarSig, #[serde(rename = "v")] version: String, #[serde(rename = "att")] @@ -43,6 +34,8 @@ pub struct Cacao< expiration: Option, #[serde(rename = "fct", skip_serializing_if = "Option::is_none", default)] facts: Option, + #[serde(rename = "s", bound = "S: VarSigTrait")] + signature: VarSig, } impl Cacao { @@ -78,13 +71,17 @@ impl Cacao { self.expiration } - pub fn facts(&self) -> Option<&F> { + fn facts(&self) -> Option<&F> { self.facts.as_ref() } + fn signature(&self) -> &VarSig { + &self.signature + } + pub async fn verify(&self, verifier: &V) -> Result<(), V::Error> where - V: Verifier, + V: CacaoVerifier, NB: Send + Sync, { verifier.verify(self).await @@ -92,10 +89,10 @@ impl Cacao { } #[async_trait] -pub trait Verifier { - type Facts; +pub trait CacaoVerifier { type Error: std::error::Error; - async fn verify(&self, cacao: &Cacao) -> Result<(), Self::Error>; + + async fn verify(&self, cacao: &Cacao) -> Result<(), Self::Error>; } #[cfg(test)] diff --git a/src/v2/recap_cacao.rs b/src/v2/recap_cacao.rs index 50e40fa..bc4da01 100644 --- a/src/v2/recap_cacao.rs +++ b/src/v2/recap_cacao.rs @@ -1,4 +1,4 @@ -use super::{Cacao, Verifier}; +use super::{Cacao, CacaoVerifier}; use async_trait::async_trait; use http::uri::Authority; use iri_string::types::UriString; @@ -15,9 +15,10 @@ use varsig::{ VarSig, }; -pub type RecapCacao = Cacao, RecapFacts, NB>; +pub type RecapSignature = Ethereum; +pub type RecapCacao = Cacao; -#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Eq, PartialOrd, Hash)] #[serde(deny_unknown_fields)] pub struct RecapFacts { #[serde(rename = "iat-z")] @@ -51,8 +52,6 @@ pub enum Error { ExtraDidComponents, #[error("The 'facts' field is required for Recap Cacaos")] MissingFacts, - #[error("Failed to parse chain ID: {0}")] - ChainId(#[from] std::num::ParseIntError), #[error("Incorrect Version, expected '1', found: {0}")] IncorrectVersion(String), #[error("SIWE messages must have a nonce")] @@ -96,14 +95,11 @@ where resources.pop(); Ok(Self { issuer: MultiDid::new( - Method::Pkh(DidPkhTypes::Eip155( - (siwe.chain_id.to_string(), siwe.address).into(), - )), + Method::Pkh(DidPkhTypes::Eip155((siwe.chain_id, siwe.address).into())), None, None, ), audience: MultiDid::from_str(siwe.uri.as_str())?, - signature: VarSig::new(Ethereum::new(sig)), version: (siwe.version as u8).to_string(), attenuations, nonce: Some(siwe.nonce), @@ -120,6 +116,7 @@ where resources, statement, }), + signature: VarSig::new(Ethereum::new(sig)), }) } } @@ -152,7 +149,7 @@ where .version .parse() .map_err(|_| Error::IncorrectVersion(cacao.version))?, - chain_id: chain_id.parse()?, + chain_id, nonce: cacao.nonce.ok_or(Error::MissingNonce)?, issued_at: make_ts(cacao.issued_at.ok_or(Error::MissingIat)?, &facts.iat_info)?, expiration_time: match (cacao.expiration, facts.exp_info) { @@ -238,17 +235,18 @@ where Authority::from_str(&s).map_err(serde::de::Error::custom) } -pub struct RecapVerify; +#[derive(Default)] +pub struct RecapVerify(()); #[async_trait] -impl Verifier> for RecapVerify +impl CacaoVerifier for RecapVerify where - NB: Send + Sync + Serialize, + NB: Send + Sync + Serialize + Clone, { - type Facts = RecapFacts; type Error = Error; + async fn verify(&self, cacao: &RecapCacao) -> Result<(), Self::Error> { - let (message, signature) = <(Message, [u8; 65])>::try_from(*cacao.clone())?; + let (message, signature) = <(Message, [u8; 65])>::try_from(cacao.clone())?; message.verify_eip191(&signature)?; Ok(()) } diff --git a/src/v2/ucan_cacao.rs b/src/v2/ucan_cacao.rs index 35bb702..ed0253e 100644 --- a/src/v2/ucan_cacao.rs +++ b/src/v2/ucan_cacao.rs @@ -1,4 +1,4 @@ -use super::{Cacao, Verifier}; +use super::{Cacao, CacaoVerifier}; use async_trait::async_trait; use multidid::MultiDid; use serde::{Deserialize, Serialize}; @@ -13,7 +13,9 @@ use varsig::{ VarSig, }; -pub type UcanCacao = Cacao, BTreeMap, NB>; +pub type UcanSignature = JoseSig; +pub type UcanFacts = BTreeMap; +pub type UcanCacao = Cacao, NB>; #[derive(thiserror::Error, Debug)] pub enum Error { @@ -27,14 +29,13 @@ pub enum Error { Ucan(#[from] ssi_ucan::Error), } -impl TryFrom> for UcanCacao { +impl TryFrom> for UcanCacao { type Error = Error; - fn try_from(ucan: Ucan) -> Result { - let (header, payload, signature) = ucan.into_inner(); + fn try_from(ucan: Ucan) -> Result { + let (alg, payload, signature) = ucan.into_inner(); Ok(Self { issuer: MultiDid::from_str(&payload.issuer)?, audience: MultiDid::from_str(&payload.audience)?, - signature: VarSig::new(match_alg(header.algorithm, signature)?), version: "0.2.0".to_string(), attenuations: payload.capabilities, nonce: payload.nonce, @@ -43,12 +44,13 @@ impl TryFrom> for UcanCacao { not_before: payload.not_before, expiration: payload.expiration, facts: payload.facts, + signature: VarSig::new(match_alg(alg, signature)?), }) } } -impl From> for Ucan { - fn from(cacao: UcanCacao) -> Self { +impl From> for Ucan { + fn from(cacao: UcanCacao) -> Self { let (algorithm, signature) = match cacao.signature.into_inner() { JoseSig::Ed25519(s) => (Algorithm::EdDSA, s.bytes().to_vec()), JoseSig::Es256(s) => (Algorithm::ES256, s.bytes().to_vec()), @@ -65,7 +67,7 @@ impl From> for Ucan { payload.not_before = cacao.not_before; payload.expiration = cacao.expiration; payload.facts = cacao.facts; - payload.sign(todo!(), signature) + payload.sign(algorithm, signature) } } @@ -90,16 +92,17 @@ fn match_alg(a: Algorithm, s: Vec) -> Result, Error } #[async_trait] -impl Verifier> for T +impl CacaoVerifier, NB> for R where - T: DIDResolver, - NB: Send + Sync + Serialize + for<'d> Deserialize<'d>, + R: DIDResolver, + F: Send + Sync + for<'a> Deserialize<'a> + Serialize + Clone, + NB: Send + Sync + for<'a> Deserialize<'a> + Serialize + Clone, { - type Facts = BTreeMap; type Error = Error; - async fn verify(&self, cacao: &UcanCacao) -> Result<(), Self::Error> { - let ucan = Ucan::::from(*cacao.clone()).encode_as_canonicalized_jwt()?; - Ucan::::decode_and_verify(&ucan, self).await?; + + async fn verify(&self, cacao: &UcanCacao) -> Result<(), Self::Error> { + let ucan = Ucan::::from(cacao.clone()).encode_as_canonicalized_jwt()?; + Ucan::::decode_and_verify(&ucan, self).await?; Ok(()) } } diff --git a/src/v2/version.rs b/src/v2/version.rs index d488267..9a6dec0 100644 --- a/src/v2/version.rs +++ b/src/v2/version.rs @@ -2,21 +2,21 @@ use serde_with::{DeserializeFromStr, SerializeDisplay}; use thiserror::Error; #[derive(Debug, Clone, PartialEq, SerializeDisplay, DeserializeFromStr)] -pub struct Version2; +pub struct Version3; -impl std::fmt::Display for Version2 { +impl std::fmt::Display for Version3 { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> { - write!(f, "2") + write!(f, "3") } } #[derive(Error, Debug, Copy, PartialEq)] pub struct VersionErr; -impl std::str::FromStr for Version2 { +impl std::str::FromStr for Version3 { type Err = VersionErr; fn from_str(s: &str) -> Result { - if s == "2" { + if s == "3" { Ok(Self) } else { Err(VersionErr) From c773fb0fbb48747b9aaffcc7b81f10ca5b802e3c Mon Sep 17 00:00:00 2001 From: chunningham Date: Tue, 29 Aug 2023 22:52:03 +0200 Subject: [PATCH 43/70] multidid util trait derives --- multidid/src/key.rs | 2 +- multidid/src/lib.rs | 2 +- multidid/src/method.rs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/multidid/src/key.rs b/multidid/src/key.rs index 706be81..77e2051 100644 --- a/multidid/src/key.rs +++ b/multidid/src/key.rs @@ -13,7 +13,7 @@ const P384_CODEC: u64 = 0x1201; const P521_CODEC: u64 = 0x1202; // const RSA_CODEC: u64 = 0x1205; -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Hash)] pub enum DidKeyTypes { Secp256k1([u8; 33]), Bls12_381G1([u8; 64]), diff --git a/multidid/src/lib.rs b/multidid/src/lib.rs index 45256d8..643d356 100644 --- a/multidid/src/lib.rs +++ b/multidid/src/lib.rs @@ -18,7 +18,7 @@ pub use pkh::DidPkhTypes; const MULTIDID_VARINT_TAG: u16 = 0x9d1a; -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Hash)] pub struct MultiDid { method: Method, fragment: Option, diff --git a/multidid/src/method.rs b/multidid/src/method.rs index ac7d3b0..7e0da11 100644 --- a/multidid/src/method.rs +++ b/multidid/src/method.rs @@ -5,7 +5,7 @@ use unsigned_varint::io::read_u64; pub const RAW_CODEC: u64 = 0x55; -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Hash)] pub enum Method { Pkh(DidPkhTypes), Key(DidKeyTypes), From f76ef7f0c8576871383f032dd417cfc48fd52523 Mon Sep 17 00:00:00 2001 From: chunningham Date: Tue, 29 Aug 2023 23:00:00 +0200 Subject: [PATCH 44/70] util fns and common verifier impls for base types --- src/v2/either.rs | 31 +++++++++++++++++++++++++++++-- src/v2/mod.rs | 4 ++-- 2 files changed, 31 insertions(+), 4 deletions(-) diff --git a/src/v2/either.rs b/src/v2/either.rs index a64535d..6921121 100644 --- a/src/v2/either.rs +++ b/src/v2/either.rs @@ -49,9 +49,9 @@ where async fn verify(&self, cacao: &CommonCacao) -> Result<(), Self::Error> { Ok(match RecapCacao::try_from(cacao.clone()) { - Ok(recap) => RecapVerify::default().verify(&recap).await?, + Ok(recap) => self.verify(&recap).await?, Err(c) => match UcanCacao::try_from(c) { - Ok(ucan) => self.0.verify(&ucan).await?, + Ok(ucan) => self.verify(&ucan).await?, Err(_) => { return Err(Error::Mismatch); } @@ -60,6 +60,33 @@ where } } +#[async_trait] +impl CacaoVerifier for CommonVerifier +where + R: Send + Sync + DIDResolver, + NB: Send + Sync + for<'a> Deserialize<'a> + Serialize + Clone, +{ + type Error = RecapError; + + async fn verify(&self, cacao: &RecapCacao) -> Result<(), Self::Error> { + RecapVerify::default().verify(cacao).await + } +} + +#[async_trait] +impl CacaoVerifier, NB> for CommonVerifier +where + R: Send + Sync + DIDResolver, + F: Send + Sync + for<'a> Deserialize<'a> + Serialize + Clone, + NB: Send + Sync + for<'a> Deserialize<'a> + Serialize + Clone, +{ + type Error = UcanError; + + async fn verify(&self, cacao: &UcanCacao) -> Result<(), Self::Error> { + self.0.verify(cacao).await + } +} + impl From> for CommonCacao { fn from(recap: RecapCacao) -> Self { CommonCacao { diff --git a/src/v2/mod.rs b/src/v2/mod.rs index da48df7..30c9d72 100644 --- a/src/v2/mod.rs +++ b/src/v2/mod.rs @@ -71,11 +71,11 @@ impl Cacao { self.expiration } - fn facts(&self) -> Option<&F> { + pub fn facts(&self) -> Option<&F> { self.facts.as_ref() } - fn signature(&self) -> &VarSig { + pub fn signature(&self) -> &VarSig { &self.signature } From 854d68310f7fb8c621fe162f5d51fa0dc891d24d Mon Sep 17 00:00:00 2001 From: chunningham Date: Tue, 29 Aug 2023 23:00:52 +0200 Subject: [PATCH 45/70] rename either -> common --- src/v2/{either.rs => common.rs} | 0 src/v2/mod.rs | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename src/v2/{either.rs => common.rs} (100%) diff --git a/src/v2/either.rs b/src/v2/common.rs similarity index 100% rename from src/v2/either.rs rename to src/v2/common.rs diff --git a/src/v2/mod.rs b/src/v2/mod.rs index 30c9d72..97cbaa4 100644 --- a/src/v2/mod.rs +++ b/src/v2/mod.rs @@ -4,7 +4,7 @@ use serde::{Deserialize, Serialize}; use std::fmt::Debug; use ucan_capabilities_object::Capabilities; -pub mod either; +pub mod common; pub mod recap_cacao; pub mod ucan_cacao; From b6fe47e9a7af3862e30540448e7fba0820f1e8c2 Mon Sep 17 00:00:00 2001 From: chunningham Date: Wed, 30 Aug 2023 00:13:38 +0200 Subject: [PATCH 46/70] impl more pkh from/to string --- multidid/Cargo.toml | 2 + multidid/src/pkh.rs | 93 ++++++++++++++++++++++++++++++++++----------- 2 files changed, 72 insertions(+), 23 deletions(-) diff --git a/multidid/Cargo.toml b/multidid/Cargo.toml index d99712a..f02d841 100644 --- a/multidid/Cargo.toml +++ b/multidid/Cargo.toml @@ -14,6 +14,8 @@ unsigned-varint = { version = "0.7", features = ["std"] } thiserror = "1" bs58 = "0.5" serde = { version = "1", optional = true } +bech32 = "0.9" +sha3 = "0.10.8" hex = "0.4" [dev-dependencies] diff --git a/multidid/src/pkh.rs b/multidid/src/pkh.rs index b59b5a2..ae89de0 100644 --- a/multidid/src/pkh.rs +++ b/multidid/src/pkh.rs @@ -1,3 +1,4 @@ +use bech32::{FromBase32, ToBase32}; use sha3::{Digest, Keccak256}; use std::io::{Error as IoError, Read, Write}; use std::{ @@ -16,7 +17,7 @@ pub enum DidPkhTypes { Bip122(Caip10<[u8; 32], [u8; 25]>), Eip155(Caip10), Cosmos(Caip10), - Starknet(Caip10), + Starknet(Caip10), Hedera(Caip10), Lip9(Caip10<[u8; 32], [u8; 20]>), } @@ -226,7 +227,7 @@ impl DidPkhTypes { let ref_len = read_u64(reader.by_ref())?; let mut chain_id = vec![0u8; ref_len as usize]; reader.read_exact(&mut chain_id)?; - let mut address = [0u8; 20]; + let mut address = [0u8; 32]; reader.read_exact(&mut address)?; Ok(DidPkhTypes::Starknet(Caip10::new( String::from_utf8(chain_id)?, @@ -343,10 +344,15 @@ impl DidPkhTypes { impl Display for DidPkhTypes { fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { match self { - // Self::Bip122(c) => write!(f, "bip122:{}:{}", c.chain_id(), c.address()), + Self::Bip122(c) => write!( + f, + "bip122:{}:{}", + hex::encode(c.chain_id()), + bs58::encode(c.address()).into_string() + ), Self::Eip155(c) => write!(f, "eip155:{}:{}", c.chain_id(), eip55(c.address())), - // Self::Cosmos(c) => write!(f, "cosmos:{}:{}", c.chain_id(), c.address()), - // Self::Starknet(c) => write!(f, "starknet:{}:{}", c.chain_id(), c.address()), + Self::Cosmos(c) => write!(f, "cosmos:{}:{}", c.chain_id(), c.address()), + Self::Starknet(c) => write!(f, "starknet:{}:{}", c.chain_id(), eip55(c.address())), // Self::Hedera(c) => write!(f, "hedera:{}:{}", c.chain_id(), c.address()), // Self::Lip9(c) => write!(f, "lip9:{}:{}", c.chain_id(), c.address()), _ => todo!(), @@ -362,6 +368,10 @@ pub enum ParseErr { InvalidInteger(#[from] std::num::ParseIntError), #[error("Invalid EIP55: {0}")] Eip55(#[from] Eip55Err), + #[error("Invalid Bip122")] + Bip122, + #[error("Invalid Cosmos Address")] + Cosmos, } impl FromStr for DidPkhTypes { @@ -373,17 +383,23 @@ impl FromStr for DidPkhTypes { .split_once(":") .and_then(|(method, r)| Some((method, r.split_once(":")?))) { - Some(("bip122", (chain_id, address))) => { - Self::Bip122(Caip10::new(todo!(), todo!())) - } + Some(("bip122", (chain_id, address))) => Self::Bip122(Caip10::new( + hex::decode(chain_id) + .map_err(|_| ParseErr::Bip122) + .and_then(|v| v.try_into().map_err(|_| ParseErr::Bip122))?, + bs58::decode(address) + .into_vec() + .map_err(|_| ParseErr::Bip122) + .and_then(|v| v.try_into().map_err(|_| ParseErr::Bip122))?, + )), Some(("eip155", (chain_id, address))) => { Self::Eip155(Caip10::new(chain_id.parse()?, parse_eip55(address)?)) } Some(("cosmos", (chain_id, address))) => { - Self::Cosmos(Caip10::new(chain_id.to_string(), todo!())) + Self::Cosmos(Caip10::new(chain_id.to_string(), address.parse()?)) } Some(("starknet", (chain_id, address))) => { - Self::Starknet(Caip10::new(chain_id.to_string(), todo!())) + Self::Starknet(Caip10::new(chain_id.to_string(), parse_starknet(address)?)) } Some(("hedera", (chain_id, address))) => { Self::Hedera(Caip10::new(chain_id.to_string(), todo!())) @@ -397,19 +413,34 @@ impl FromStr for DidPkhTypes { impl Display for CosmosAddress { fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { - match self { - CosmosAddress::Secp256k1(address) => { - for byte in address { - write!(f, "{:02x}", byte)?; - } - } - CosmosAddress::Secp256r1(address) => { - for byte in address { - write!(f, "{:02x}", byte)?; + write!( + f, + "{}", + bech32::encode("cosmos", self.bytes().to_base32(), bech32::Variant::Bech32) + .map_err(|_| std::fmt::Error)? + ) + } +} + +impl FromStr for CosmosAddress { + type Err = ParseErr; + fn from_str(s: &str) -> Result { + bech32::decode(s) + .map_err(|_| ParseErr::Cosmos) + .and_then(|(p, b32, _)| { + if p != "cosmos" { + Err(ParseErr::Cosmos) + } else { + match <[u8; 20]>::try_from( + Vec::::from_base32(&b32).map_err(|_| ParseErr::Cosmos)?, + ) { + Ok(b) => Ok(CosmosAddress::Secp256k1(b)), + Err(b) => Ok(CosmosAddress::Secp256r1( + b.try_into().map_err(|_| ParseErr::Cosmos)?, + )), + } } - } - } - Ok(()) + }) } } @@ -437,7 +468,7 @@ impl Display for HederaAddress { } /// Takes an eth address and returns it as a checksum formatted string. -pub fn eip55(addr: &[u8; 20]) -> String { +pub fn eip55(addr: &[u8; N]) -> String { let addr_str = hex::encode(addr); let hash = Keccak256::digest(addr_str.as_bytes()); "0x".chars() @@ -475,3 +506,19 @@ fn parse_eip55(address: &str) -> Result<[u8; 20], Eip55Err> { } } } + +fn parse_starknet(address: &str) -> Result<[u8; 32], Eip55Err> { + use hex::FromHex; + if !address.starts_with("0x") { + Err(Eip55Err::MissingPrefix) + } else { + let s = <[u8; 32]>::from_hex(address)?; + let sum = eip55(&s); + let sum = sum.trim_start_matches("0x"); + if sum != address { + Err(Eip55Err::InvalidChecksum) + } else { + Ok(s) + } + } +} From 96de9d64854a0511c5c2233f46739abf94e6e5dc Mon Sep 17 00:00:00 2001 From: chunningham Date: Wed, 30 Aug 2023 00:16:05 +0200 Subject: [PATCH 47/70] remove lip9 and hedera --- multidid/src/pkh.rs | 137 -------------------------------------------- 1 file changed, 137 deletions(-) diff --git a/multidid/src/pkh.rs b/multidid/src/pkh.rs index ae89de0..31ac544 100644 --- a/multidid/src/pkh.rs +++ b/multidid/src/pkh.rs @@ -18,8 +18,6 @@ pub enum DidPkhTypes { Eip155(Caip10), Cosmos(Caip10), Starknet(Caip10), - Hedera(Caip10), - Lip9(Caip10<[u8; 32], [u8; 20]>), } #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] @@ -55,13 +53,6 @@ pub enum CosmosAddress { Secp256r1([u8; 32]), } -#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub enum HederaAddress { - EVM([u8; 20]), - Ed25519([u8; 32]), - Secp256k1([u8; 33]), -} - impl CosmosAddress { pub fn bytes(&self) -> &[u8] { match self { @@ -71,16 +62,6 @@ impl CosmosAddress { } } -impl HederaAddress { - pub fn bytes(&self) -> &[u8] { - match self { - HederaAddress::EVM(address) => address, - HederaAddress::Ed25519(address) => address, - HederaAddress::Secp256k1(address) => address, - } - } -} - #[derive(Debug, thiserror::Error)] pub enum Error { #[error(transparent)] @@ -91,8 +72,6 @@ pub enum Error { Utf8(#[from] std::string::FromUtf8Error), #[error(transparent)] Varint(#[from] unsigned_varint::io::ReadError), - #[error("Invalid Hedera length: {0}")] - InvalidHederaLength(u64), #[error("Invalid Cosmos length: {0}")] InvalidCosmosLength(u64), } @@ -105,8 +84,6 @@ impl DidPkhTypes { Eip155(_) => "eip155", Cosmos(_) => "cosmos", Starknet(_) => "starknet", - Hedera(_) => "hedera", - Lip9(_) => "lip9", } } @@ -150,30 +127,6 @@ impl DidPkhTypes { vec.extend(caip10.chain_id().as_bytes()); vec.extend(caip10.address()); } - DidPkhTypes::Hedera(caip10) => { - vec.extend(write_u64(3, &mut buf)); - vec.extend(write_u64(caip10.chain_id().len() as u64, &mut buf)); - vec.extend(caip10.chain_id().as_bytes()); - match caip10.address() { - HederaAddress::EVM(address) => { - vec.extend(write_u64(20, &mut buf)); - vec.extend(address); - } - HederaAddress::Ed25519(address) => { - vec.extend(write_u64(32, &mut buf)); - vec.extend(address); - } - HederaAddress::Secp256k1(address) => { - vec.extend(write_u64(33, &mut buf)); - vec.extend(address); - } - } - } - DidPkhTypes::Lip9(caip10) => { - vec.extend(write_u64(3, &mut buf)); - vec.extend(caip10.chain_id()); - vec.extend(caip10.address()); - } }; vec } @@ -234,42 +187,6 @@ impl DidPkhTypes { address, ))) } - // hedera - 5 => { - let ref_len = read_u64(reader.by_ref())?; - let mut chain_id = vec![0u8; ref_len as usize]; - reader.read_exact(&mut chain_id)?; - let address_len = read_u64(reader.by_ref())?; - Ok(DidPkhTypes::Hedera(Caip10::new( - String::from_utf8(chain_id)?, - match address_len { - 20 => { - let mut address = [0u8; 20]; - reader.read_exact(&mut address)?; - HederaAddress::EVM(address) - } - 32 => { - let mut address = [0u8; 32]; - reader.read_exact(&mut address)?; - HederaAddress::Ed25519(address) - } - 33 => { - let mut address = [0u8; 33]; - reader.read_exact(&mut address)?; - HederaAddress::Secp256k1(address) - } - l => return Err(Error::InvalidHederaLength(l)), - }, - ))) - } - // lip9 - 6 => { - let mut chain_id = [0u8; 32]; - reader.read_exact(&mut chain_id)?; - let mut address = [0u8; 20]; - reader.read_exact(&mut address)?; - Ok(DidPkhTypes::Lip9(Caip10::new(chain_id, address))) - } t => Err(Error::InvalidPkh(t)), } } @@ -312,30 +229,6 @@ impl DidPkhTypes { writer.write_all(caip10.chain_id().as_bytes())?; writer.write_all(caip10.address())?; } - DidPkhTypes::Hedera(caip10) => { - writer.write_all(write_u64(3, &mut buf))?; - writer.write_all(write_u64(caip10.chain_id().len() as u64, &mut buf))?; - writer.write_all(caip10.chain_id().as_bytes())?; - match caip10.address() { - HederaAddress::EVM(address) => { - writer.write_all(write_u64(20, &mut buf))?; - writer.write_all(address)?; - } - HederaAddress::Ed25519(address) => { - writer.write_all(write_u64(32, &mut buf))?; - writer.write_all(address)?; - } - HederaAddress::Secp256k1(address) => { - writer.write_all(write_u64(20, &mut buf))?; - writer.write_all(address)?; - } - } - } - DidPkhTypes::Lip9(caip10) => { - writer.write_all(write_u64(3, &mut buf))?; - writer.write_all(caip10.chain_id())?; - writer.write_all(caip10.address())?; - } }; Ok(()) } @@ -353,9 +246,6 @@ impl Display for DidPkhTypes { Self::Eip155(c) => write!(f, "eip155:{}:{}", c.chain_id(), eip55(c.address())), Self::Cosmos(c) => write!(f, "cosmos:{}:{}", c.chain_id(), c.address()), Self::Starknet(c) => write!(f, "starknet:{}:{}", c.chain_id(), eip55(c.address())), - // Self::Hedera(c) => write!(f, "hedera:{}:{}", c.chain_id(), c.address()), - // Self::Lip9(c) => write!(f, "lip9:{}:{}", c.chain_id(), c.address()), - _ => todo!(), } } } @@ -401,10 +291,6 @@ impl FromStr for DidPkhTypes { Some(("starknet", (chain_id, address))) => { Self::Starknet(Caip10::new(chain_id.to_string(), parse_starknet(address)?)) } - Some(("hedera", (chain_id, address))) => { - Self::Hedera(Caip10::new(chain_id.to_string(), todo!())) - } - Some(("lip9", (chain_id, address))) => Self::Lip9(Caip10::new(todo!(), todo!())), _ => return Err(ParseErr::InvalidDid), }, ) @@ -444,29 +330,6 @@ impl FromStr for CosmosAddress { } } -impl Display for HederaAddress { - fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { - match self { - HederaAddress::EVM(address) => { - for byte in address { - write!(f, "{:02x}", byte)?; - } - } - HederaAddress::Ed25519(address) => { - for byte in address { - write!(f, "{:02x}", byte)?; - } - } - HederaAddress::Secp256k1(address) => { - for byte in address { - write!(f, "{:02x}", byte)?; - } - } - } - Ok(()) - } -} - /// Takes an eth address and returns it as a checksum formatted string. pub fn eip55(addr: &[u8; N]) -> String { let addr_str = hex::encode(addr); From be8ad74f5c06ca43afe03608609ac87c5e751bc6 Mon Sep 17 00:00:00 2001 From: chunningham Date: Wed, 30 Aug 2023 16:49:25 +0200 Subject: [PATCH 48/70] util into_inner fns for some types --- varsig/src/common/ed25519.rs | 3 +++ varsig/src/common/rsa.rs | 3 +++ 2 files changed, 6 insertions(+) diff --git a/varsig/src/common/ed25519.rs b/varsig/src/common/ed25519.rs index 715dcb1..3e33096 100644 --- a/varsig/src/common/ed25519.rs +++ b/varsig/src/common/ed25519.rs @@ -20,6 +20,9 @@ impl Ed25519 { pub fn bytes(&self) -> &[u8; 64] { &self.bytes } + pub fn into_inner(&self) -> [u8; 64] { + self.bytes + } } #[derive(thiserror::Error, Debug)] diff --git a/varsig/src/common/rsa.rs b/varsig/src/common/rsa.rs index f2959cf..aa18f57 100644 --- a/varsig/src/common/rsa.rs +++ b/varsig/src/common/rsa.rs @@ -20,6 +20,9 @@ impl Rsa { pub fn bytes(&self) -> &[u8] { &self.bytes } + pub fn into_inner(self) -> Vec { + self.bytes + } } pub type Rsa256 = Rsa<{ SHA256 as u64 }, ENCODING>; From b9572189751ed943c86fcf806219e505a4da3a23 Mon Sep 17 00:00:00 2001 From: chunningham Date: Sun, 10 Sep 2023 12:48:46 +0200 Subject: [PATCH 49/70] update ucan, fix async traits for wasm --- src/v2/recap_cacao.rs | 5 +- src/v2/ucan_cacao.rs | 154 ++++++++++++++++++++++++++++-------------- 2 files changed, 108 insertions(+), 51 deletions(-) diff --git a/src/v2/recap_cacao.rs b/src/v2/recap_cacao.rs index bc4da01..98a927e 100644 --- a/src/v2/recap_cacao.rs +++ b/src/v2/recap_cacao.rs @@ -7,7 +7,7 @@ use serde::{Deserialize, Serialize}; use serde_json::Value; pub use siwe; use siwe::{Message, TimeStamp}; -use siwe_recap::Capability; +pub use siwe_recap::Capability; use std::{fmt::Debug, str::FromStr}; use time::{format_description::well_known::Rfc3339, OffsetDateTime, UtcOffset}; use varsig::{ @@ -238,7 +238,8 @@ where #[derive(Default)] pub struct RecapVerify(()); -#[async_trait] +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] impl CacaoVerifier for RecapVerify where NB: Send + Sync + Serialize + Clone, diff --git a/src/v2/ucan_cacao.rs b/src/v2/ucan_cacao.rs index ed0253e..9fa6d68 100644 --- a/src/v2/ucan_cacao.rs +++ b/src/v2/ucan_cacao.rs @@ -29,10 +29,95 @@ pub enum Error { Ucan(#[from] ssi_ucan::Error), } -impl TryFrom> for UcanCacao { +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] +impl CacaoVerifier, NB> for &R +where + R: DIDResolver, + F: Send + Sync + for<'a> Deserialize<'a> + Serialize + Clone, + NB: Send + Sync + for<'a> Deserialize<'a> + Serialize + Clone, +{ + type Error = Error; + + async fn verify(&self, cacao: &UcanCacao) -> Result<(), Self::Error> { + let ucan = Ucan::::from(cacao.clone()).encode_jwt()?; + Ucan::::decode_and_verify(&ucan, *self).await?; + Ok(()) + } +} + +impl TryFrom> for UcanCacao { + type Error = Error; + fn try_from(ucan: Ucan) -> Result { + let (payload, signature) = ucan.into_inner(); + Ok(Self { + issuer: MultiDid::from_str(&payload.issuer)?, + audience: MultiDid::from_str(&payload.audience)?, + version: "0.2.0".to_string(), + attenuations: payload.capabilities, + nonce: payload.nonce, + proof: payload.proof, + issued_at: payload.issued_at, + not_before: payload.not_before, + expiration: payload.expiration, + facts: payload.facts, + signature: VarSig::new(match signature { + Signature::ES256(sig) => EitherSignature::A(JoseSig::Es256(Es256::new(sig))), + Signature::ES512(sig) => EitherSignature::A(JoseSig::Es512(Es512::new(sig))), + Signature::EdDSA(sig) => EitherSignature::A(JoseSig::EdDSA(Ed25519::new(sig))), + Signature::RS256(sig) => EitherSignature::A(JoseSig::Rsa256(Rsa256::new(sig))), + Signature::RS512(sig) => EitherSignature::A(JoseSig::Rsa512(Rsa512::new(sig))), + Signature::ES256K(sig) => EitherSignature::A(JoseSig::Es256K(Es256K::new(sig))), + }), + }) + } +} + +impl TryFrom> for UcanCacao { + type Error = Error; + fn try_from(ucan: Ucan) -> Result { + let (payload, signature) = ucan.into_inner(); + Ok(Self { + issuer: MultiDid::from_str(&payload.issuer)?, + audience: MultiDid::from_str(&payload.audience)?, + version: "0.2.0".to_string(), + attenuations: payload.capabilities, + nonce: payload.nonce, + proof: payload.proof, + issued_at: payload.issued_at, + not_before: payload.not_before, + expiration: payload.expiration, + facts: payload.facts, + signature: VarSig::new(match signature { + Common::Jose(Signature::ES256(sig)) => { + EitherSignature::A(JoseSig::Es256(Es256::new(sig))) + } + Common::Jose(Signature::ES512(sig)) => { + EitherSignature::A(JoseSig::Es512(Es512::new(sig))) + } + Common::Jose(Signature::EdDSA(sig)) => { + EitherSignature::A(JoseSig::EdDSA(Ed25519::new(sig))) + } + Common::Jose(Signature::RS256(sig)) => { + EitherSignature::A(JoseSig::Rsa256(Rsa256::new(sig))) + } + Common::Jose(Signature::RS512(sig)) => { + EitherSignature::A(JoseSig::Rsa512(Rsa512::new(sig))) + } + Common::Jose(Signature::ES256K(sig)) => { + EitherSignature::A(JoseSig::Es256K(Es256K::new(sig))) + } + Common::Webauthn(sig) => EitherSignature::B(PasskeySig::new(sig)), + Common::Generic(_) => return Err(Error::UnsupportedAlgorithm(Algorithm::None)), + }), + }) + } +} + +impl TryFrom> for UcanCacao { type Error = Error; - fn try_from(ucan: Ucan) -> Result { - let (alg, payload, signature) = ucan.into_inner(); + fn try_from(ucan: Ucan) -> Result { + let (payload, signature) = ucan.into_inner(); Ok(Self { issuer: MultiDid::from_str(&payload.issuer)?, audience: MultiDid::from_str(&payload.audience)?, @@ -44,20 +129,27 @@ impl TryFrom> for UcanCacao { not_before: payload.not_before, expiration: payload.expiration, facts: payload.facts, - signature: VarSig::new(match_alg(alg, signature)?), + signature: VarSig::new(EitherSignature::B(PasskeySig::new(signature))), }) } } -impl From> for Ucan { +impl From> for Ucan { fn from(cacao: UcanCacao) -> Self { - let (algorithm, signature) = match cacao.signature.into_inner() { - JoseSig::Ed25519(s) => (Algorithm::EdDSA, s.bytes().to_vec()), - JoseSig::Es256(s) => (Algorithm::ES256, s.bytes().to_vec()), - JoseSig::Es512(s) => (Algorithm::ES256, s.bytes().to_vec()), - JoseSig::Es256K(s) => (Algorithm::ES256K, s.bytes().to_vec()), - JoseSig::Rsa256(s) => (Algorithm::RS256, s.bytes().to_vec()), - JoseSig::Rsa512(s) => (Algorithm::RS512, s.bytes().to_vec()), + let signature = match cacao.signature.into_inner() { + EitherSignature::A(JoseSig::EdDSA(s)) => Common::Jose(Signature::EdDSA(s.into_inner())), + EitherSignature::A(JoseSig::Es256(s)) => Common::Jose(Signature::ES256(s.into_inner())), + EitherSignature::A(JoseSig::Es512(s)) => Common::Jose(Signature::ES512(s.into_inner())), + EitherSignature::A(JoseSig::Es256K(s)) => { + Common::Jose(Signature::ES256K(s.into_inner())) + } + EitherSignature::A(JoseSig::Rsa256(s)) => { + Common::Jose(Signature::RS256(s.into_inner())) + } + EitherSignature::A(JoseSig::Rsa512(s)) => { + Common::Jose(Signature::RS512(s.into_inner())) + } + EitherSignature::B(passkey) => Common::Webauthn(passkey.into_inner()), }; let mut payload = Payload::new(cacao.issuer.to_string(), cacao.audience.to_string()); payload.capabilities = cacao.attenuations; @@ -67,43 +159,7 @@ impl From> for Ucan { payload.not_before = cacao.not_before; payload.expiration = cacao.expiration; payload.facts = cacao.facts; - payload.sign(algorithm, signature) - } -} - -fn match_alg(a: Algorithm, s: Vec) -> Result, Error> { - Ok(match a { - Algorithm::ES256 => JoseSig::Es256(Es256::new( - s.try_into() - .map_err(|v: Vec| Error::IncorrectSignatureLength(v.len(), 64))?, - )), - Algorithm::EdDSA => JoseSig::Ed25519(Ed25519::new( - s.try_into() - .map_err(|v: Vec| Error::IncorrectSignatureLength(v.len(), 64))?, - )), - Algorithm::RS256 => JoseSig::Rsa256(Rsa256::new(s)), - Algorithm::RS512 => JoseSig::Rsa512(Rsa512::new(s)), - Algorithm::ES256K => JoseSig::Es256K(Es256K::new( - s.try_into() - .map_err(|v: Vec| Error::IncorrectSignatureLength(v.len(), 64))?, - )), - a => return Err(Error::UnsupportedAlgorithm(a)), - }) -} - -#[async_trait] -impl CacaoVerifier, NB> for R -where - R: DIDResolver, - F: Send + Sync + for<'a> Deserialize<'a> + Serialize + Clone, - NB: Send + Sync + for<'a> Deserialize<'a> + Serialize + Clone, -{ - type Error = Error; - - async fn verify(&self, cacao: &UcanCacao) -> Result<(), Self::Error> { - let ucan = Ucan::::from(cacao.clone()).encode_as_canonicalized_jwt()?; - Ucan::::decode_and_verify(&ucan, self).await?; - Ok(()) + payload.sign(signature) } } From ddbb824bb48e0c5f88bb0ab324ed8c0bc28e1e1c Mon Sep 17 00:00:00 2001 From: chunningham Date: Sun, 10 Sep 2023 12:50:07 +0200 Subject: [PATCH 50/70] rename Ed25519 -> EdDSA --- varsig/src/common/mod.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/varsig/src/common/mod.rs b/varsig/src/common/mod.rs index 2eda33f..ec1cf2c 100644 --- a/varsig/src/common/mod.rs +++ b/varsig/src/common/mod.rs @@ -64,7 +64,7 @@ pub enum JoseSig { Es512(Es512), Rsa256(Rsa256), Rsa512(Rsa512), - Ed25519(Ed25519), + EdDSA(Ed25519), Es256K(Es256K), } @@ -105,7 +105,7 @@ impl VarSigTrait for JoseSig { Rsa512::::from_reader(buf.chain(reader)).map_err(convert_err)?, )) } else if Ed25519::::valid_header(&buf) { - Ok(Self::Ed25519( + Ok(Self::EdDSA( Ed25519::::from_reader(buf.chain(reader)).map_err(convert_err)?, )) } else if Es256K::::valid_header(&buf) { @@ -126,7 +126,7 @@ impl VarSigTrait for JoseSig { Self::Es512(s) => s.to_writer(writer), Self::Rsa256(s) => s.to_writer(writer), Self::Rsa512(s) => s.to_writer(writer), - Self::Ed25519(s) => s.to_writer(writer), + Self::EdDSA(s) => s.to_writer(writer), Self::Es256K(s) => s.to_writer(writer), } } From 0084986f8759b3d5f143210466f8473e856e9c13 Mon Sep 17 00:00:00 2001 From: chunningham Date: Sun, 10 Sep 2023 12:53:12 +0200 Subject: [PATCH 51/70] Common fixes and utilities --- src/v2/common.rs | 67 ++++++++++++++++++++++++++++++++++++------------ src/v2/mod.rs | 20 +++++++++++++-- 2 files changed, 69 insertions(+), 18 deletions(-) diff --git a/src/v2/common.rs b/src/v2/common.rs index 6921121..d59071d 100644 --- a/src/v2/common.rs +++ b/src/v2/common.rs @@ -6,8 +6,8 @@ use super::{ use async_trait::async_trait; use serde::{Deserialize, Serialize}; use serde_json::Value; -use ssi_dids::did_resolve::DIDResolver; -use varsig::{either::EitherSignature, VarSig}; +use ssi_ucan::{common::Common, jose::Signature, jwt::UcanEncode, Payload, Ucan}; +use varsig::{common::JoseSig, either::EitherSignature, VarSig}; type CommonSignature = EitherSignature; @@ -28,30 +28,54 @@ impl CommonVerifier { } } +impl CommonCacao +where + F: Clone + Serialize, + NB: Clone + Serialize, +{ + pub fn serialize_jwt(&self) -> Result, Error> { + Ok(match self.signature.sig() { + CommonSignature::A(_) => None, + CommonSignature::B(_) => self + .clone() + .get_ucan() + .map(|ucan| ucan.encode()) + .transpose()?, + }) + } +} + #[derive(thiserror::Error, Debug)] -pub enum Error { +pub enum Error { #[error(transparent)] - Ucan(#[from] UcanError), + Ucan(U), #[error(transparent)] Recap(#[from] RecapError), #[error("Signature and Facts Mismatch")] Mismatch, } -#[async_trait] -impl CacaoVerifier, NB> for CommonVerifier +impl From for Error { + fn from(e: ssi_ucan::Error) -> Self { + Self::Ucan(e.into()) + } +} + +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] +impl<'r, NB, R, F> CacaoVerifier, NB> for CommonVerifier where - R: Send + Sync + DIDResolver, + R: 'r + Send + Sync + CacaoVerifier, NB>, F: Send + Sync + for<'a> Deserialize<'a> + Serialize + Clone, NB: Send + Sync + for<'a> Deserialize<'a> + Serialize + Clone, { - type Error = Error; + type Error = Error; async fn verify(&self, cacao: &CommonCacao) -> Result<(), Self::Error> { Ok(match RecapCacao::try_from(cacao.clone()) { Ok(recap) => self.verify(&recap).await?, Err(c) => match UcanCacao::try_from(c) { - Ok(ucan) => self.verify(&ucan).await?, + Ok(ucan) => self.verify(&ucan).await.map_err(Error::Ucan)?, Err(_) => { return Err(Error::Mismatch); } @@ -60,10 +84,11 @@ where } } -#[async_trait] +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] impl CacaoVerifier for CommonVerifier where - R: Send + Sync + DIDResolver, + R: Send + Sync, NB: Send + Sync + for<'a> Deserialize<'a> + Serialize + Clone, { type Error = RecapError; @@ -73,17 +98,18 @@ where } } -#[async_trait] -impl CacaoVerifier, NB> for CommonVerifier +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] +impl<'r, NB, R, F> CacaoVerifier, NB> for CommonVerifier where - R: Send + Sync + DIDResolver, + R: 'r + Send + Sync + CacaoVerifier, NB>, F: Send + Sync + for<'a> Deserialize<'a> + Serialize + Clone, NB: Send + Sync + for<'a> Deserialize<'a> + Serialize + Clone, { - type Error = UcanError; + type Error = R::Error; async fn verify(&self, cacao: &UcanCacao) -> Result<(), Self::Error> { - self.0.verify(cacao).await + (&self.0).verify(cacao).await } } @@ -203,3 +229,12 @@ impl TryFrom> for UcanCacao { } } } + +impl TryFrom> for CommonCacao { + type Error = Error; + fn try_from(ucan: Ucan) -> Result { + Ok(UcanCacao::try_from(ucan) + .map(Self::from) + .map_err(Error::Ucan)?) + } +} diff --git a/src/v2/mod.rs b/src/v2/mod.rs index 97cbaa4..1aa2ba1 100644 --- a/src/v2/mod.rs +++ b/src/v2/mod.rs @@ -8,7 +8,9 @@ pub mod common; pub mod recap_cacao; pub mod ucan_cacao; +pub use multidid; use multidid::MultiDid; +pub use varsig; use varsig::{VarSig, VarSigTrait}; #[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Eq, Hash)] @@ -32,7 +34,11 @@ pub struct Cacao { not_before: Option, #[serde(rename = "exp", skip_serializing_if = "Option::is_none", default)] expiration: Option, - #[serde(rename = "fct", skip_serializing_if = "Option::is_none", default)] + #[serde( + rename = "fct", + skip_serializing_if = "Option::is_none", + default = "Option::default" + )] facts: Option, #[serde(rename = "s", bound = "S: VarSigTrait")] signature: VarSig, @@ -79,6 +85,15 @@ impl Cacao { &self.signature } + pub fn valid_at_time>(&self, time: T) -> bool { + self.expiration.map_or(true, |exp| time < exp + SKEW) + && self.not_before.map_or(true, |nbf| time >= nbf - SKEW) + && self.issued_at.map_or(true, |iat| { + self.not_before.map_or(true, |nbf| nbf < iat) + && self.expiration.map_or(true, |exp| iat < exp) + }) + } + pub async fn verify(&self, verifier: &V) -> Result<(), V::Error> where V: CacaoVerifier, @@ -88,7 +103,8 @@ impl Cacao { } } -#[async_trait] +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] pub trait CacaoVerifier { type Error: std::error::Error; From 1dc38881337edfa2060d229ec471ea6ca0b8bcf9 Mon Sep 17 00:00:00 2001 From: chunningham Date: Sun, 10 Sep 2023 12:53:57 +0200 Subject: [PATCH 52/70] add test vectors --- multidid/src/lib.rs | 12 ++++++++++++ multidid/tests/valid.json | 16 ++++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/multidid/src/lib.rs b/multidid/src/lib.rs index 643d356..00e1b88 100644 --- a/multidid/src/lib.rs +++ b/multidid/src/lib.rs @@ -266,11 +266,23 @@ mod tests { #[test] fn it_works() { let valid: Vec = serde_json::from_str(VALID_JSON).unwrap(); + for test in &valid { + if test.method.as_str() == "raw" { + let s = test.decoded.as_str()[4..] + .split_once("?") + .map(|(a, b)| a) + .unwrap_or(test.decoded.as_str()); + println!("{:x}", s.as_bytes().len()); + println!("{}", hex::encode(s.as_bytes())); + } + } for test in valid { let did = MultiDid::from_reader(&mut test.encoded.as_slice()).unwrap(); assert_eq!(did.query, test.query); assert_eq!(did.fragment, test.fragment); assert_eq!(did.to_vec(), test.encoded); + assert_eq!(did.to_string(), test.decoded); + assert_eq!(did, test.decoded.parse().unwrap()); assert!(match (did.method(), test.method.as_str()) { (Method::Key(_), "key") => true, (Method::Pkh(_), "pkh") => true, diff --git a/multidid/tests/valid.json b/multidid/tests/valid.json index 1512d2a..ce397a7 100644 --- a/multidid/tests/valid.json +++ b/multidid/tests/valid.json @@ -20,4 +20,20 @@ "encoded": "9d1ae70103874c15c7fda20e539c6e5ba573c139884c351188799f5458b4b41f7924f235cd00", "decoded": "did:key:zQ3shokFTS3brHcDQrn82RUDfCZESWL1ZdCEJwekUDPQiYBme", "method": "key" +}, { + "encoded": "9d1aca0101000000000019d6689c085ae165831e93000c5b86d784abdf01eb30f21868deb8de53abacb7b3e3109f", + "decoded": "did:pkh:bip122:000000000019d6689c085ae165831e93:128Lkh3S7CkDTBZ8W7BbpsN3YYizJMp8p6", + "method": "pkh" +}, { + "encoded": "9d1aca010201ab16a96d359ec26a11e2c2b3d8f8b8942d5bfcdb", + "decoded": "did:pkh:eip155:1:0xab16a96D359eC26a11e2C2b3d8f8B8942d5Bfcdb", + "method": "pkh" +}, { + "encoded": "9d1aca01030b636f736d6f736875622d335ab89f81c0cbd3093e17c26c360796cbb0eabc92", + "decoded": "did:pkh:cosmos:cosmoshub-3:cosmos1t2uflqwqe0fsj0shcfkrvpukewcw40yjj6hdc0", + "method": "pkh" +}, { + "encoded": "9d1aca010409534e5f474f45524c4902dd1b492765c064eac4039e3841aa5f382773b598097a40073bd8b48170ab57", + "decoded": "did:pkh:starknet:SN_GOERLI:0x02dd1b492765c064eac4039e3841aa5f382773b598097a40073bd8b48170ab57", + "method": "pkh" }] From d8e8ee8ec6711cc4edfff08b2345bbd7e481376a Mon Sep 17 00:00:00 2001 From: chunningham Date: Wed, 20 Sep 2023 00:05:43 +0200 Subject: [PATCH 53/70] update ucan usage --- src/v2/common.rs | 48 +++++++---------- src/v2/ucan_cacao.rs | 120 +++++++++---------------------------------- 2 files changed, 43 insertions(+), 125 deletions(-) diff --git a/src/v2/common.rs b/src/v2/common.rs index d59071d..61c1176 100644 --- a/src/v2/common.rs +++ b/src/v2/common.rs @@ -6,8 +6,11 @@ use super::{ use async_trait::async_trait; use serde::{Deserialize, Serialize}; use serde_json::Value; -use ssi_ucan::{common::Common, jose::Signature, jwt::UcanEncode, Payload, Ucan}; -use varsig::{common::JoseSig, either::EitherSignature, VarSig}; +use ssi_ucan::{ + jose::{self, Signature, VerificationError}, + Ucan, +}; +use varsig::{either::EitherSignature, VarSig}; type CommonSignature = EitherSignature; @@ -28,25 +31,8 @@ impl CommonVerifier { } } -impl CommonCacao -where - F: Clone + Serialize, - NB: Clone + Serialize, -{ - pub fn serialize_jwt(&self) -> Result, Error> { - Ok(match self.signature.sig() { - CommonSignature::A(_) => None, - CommonSignature::B(_) => self - .clone() - .get_ucan() - .map(|ucan| ucan.encode()) - .transpose()?, - }) - } -} - #[derive(thiserror::Error, Debug)] -pub enum Error { +pub enum Error> { #[error(transparent)] Ucan(U), #[error(transparent)] @@ -55,9 +41,15 @@ pub enum Error { Mismatch, } -impl From for Error { - fn from(e: ssi_ucan::Error) -> Self { - Self::Ucan(e.into()) +impl From> for Error { + fn from(e: VerificationError) -> Self { + Self::Ucan(e) + } +} + +impl From for Error { + fn from(e: UcanError) -> Self { + Self::Ucan(e) } } @@ -230,11 +222,9 @@ impl TryFrom> for UcanCacao { } } -impl TryFrom> for CommonCacao { - type Error = Error; - fn try_from(ucan: Ucan) -> Result { - Ok(UcanCacao::try_from(ucan) - .map(Self::from) - .map_err(Error::Ucan)?) +impl TryFrom> for CommonCacao { + type Error = Error; + fn try_from(ucan: Ucan) -> Result { + Ok(UcanCacao::try_from(ucan)?.into()) } } diff --git a/src/v2/ucan_cacao.rs b/src/v2/ucan_cacao.rs index 9fa6d68..21b3f0d 100644 --- a/src/v2/ucan_cacao.rs +++ b/src/v2/ucan_cacao.rs @@ -4,12 +4,14 @@ use multidid::MultiDid; use serde::{Deserialize, Serialize}; use serde_json::Value; use ssi_dids::did_resolve::DIDResolver; -use ssi_jwk::Algorithm; -use ssi_ucan::{Payload, Ucan}; +use ssi_ucan::{ + jose::{self, Signature, VerificationError}, + Payload, Ucan, +}; use std::collections::BTreeMap; use std::str::FromStr; use varsig::{ - common::{Ed25519, Es256, Es256K, JoseSig, Rsa256, Rsa512, DAG_JSON_ENCODING}, + common::{Ed25519, Es256, Es256K, Es512, JoseSig, Rsa256, Rsa512, DAG_JSON_ENCODING}, VarSig, }; @@ -19,14 +21,8 @@ pub type UcanCacao = Cacao, N #[derive(thiserror::Error, Debug)] pub enum Error { - #[error("Unsupported Algorithm")] - UnsupportedAlgorithm(Algorithm), - #[error("Incorrect Signature Length: recieved {0}, expected {1}")] - IncorrectSignatureLength(usize, usize), #[error(transparent)] MultididParse(#[from] multidid::ParseErr), - #[error(transparent)] - Ucan(#[from] ssi_ucan::Error), } #[cfg_attr(target_arch = "wasm32", async_trait(?Send))] @@ -37,18 +33,18 @@ where F: Send + Sync + for<'a> Deserialize<'a> + Serialize + Clone, NB: Send + Sync + for<'a> Deserialize<'a> + Serialize + Clone, { - type Error = Error; + type Error = VerificationError; - async fn verify(&self, cacao: &UcanCacao) -> Result<(), Self::Error> { - let ucan = Ucan::::from(cacao.clone()).encode_jwt()?; - Ucan::::decode_and_verify(&ucan, *self).await?; + async fn verify(&self, cacao: &UcanCacao) -> Result<(), VerificationError> { + let ucan = Ucan::::from(cacao.clone()).encode()?; + Ucan::::decode_and_verify_jwt(&ucan, *self, None).await?; Ok(()) } } -impl TryFrom> for UcanCacao { +impl TryFrom> for UcanCacao { type Error = Error; - fn try_from(ucan: Ucan) -> Result { + fn try_from(ucan: Ucan) -> Result { let (payload, signature) = ucan.into_inner(); Ok(Self { issuer: MultiDid::from_str(&payload.issuer)?, @@ -62,94 +58,26 @@ impl TryFrom> for UcanCacao { expiration: payload.expiration, facts: payload.facts, signature: VarSig::new(match signature { - Signature::ES256(sig) => EitherSignature::A(JoseSig::Es256(Es256::new(sig))), - Signature::ES512(sig) => EitherSignature::A(JoseSig::Es512(Es512::new(sig))), - Signature::EdDSA(sig) => EitherSignature::A(JoseSig::EdDSA(Ed25519::new(sig))), - Signature::RS256(sig) => EitherSignature::A(JoseSig::Rsa256(Rsa256::new(sig))), - Signature::RS512(sig) => EitherSignature::A(JoseSig::Rsa512(Rsa512::new(sig))), - Signature::ES256K(sig) => EitherSignature::A(JoseSig::Es256K(Es256K::new(sig))), + Signature::ES256(sig) => JoseSig::Es256(Es256::new(sig)), + Signature::ES512(sig) => JoseSig::Es512(Es512::new(sig)), + Signature::EdDSA(sig) => JoseSig::EdDSA(Ed25519::new(sig)), + Signature::RS256(sig) => JoseSig::Rsa256(Rsa256::new(sig)), + Signature::RS512(sig) => JoseSig::Rsa512(Rsa512::new(sig)), + Signature::ES256K(sig) => JoseSig::Es256K(Es256K::new(sig)), }), }) } } -impl TryFrom> for UcanCacao { - type Error = Error; - fn try_from(ucan: Ucan) -> Result { - let (payload, signature) = ucan.into_inner(); - Ok(Self { - issuer: MultiDid::from_str(&payload.issuer)?, - audience: MultiDid::from_str(&payload.audience)?, - version: "0.2.0".to_string(), - attenuations: payload.capabilities, - nonce: payload.nonce, - proof: payload.proof, - issued_at: payload.issued_at, - not_before: payload.not_before, - expiration: payload.expiration, - facts: payload.facts, - signature: VarSig::new(match signature { - Common::Jose(Signature::ES256(sig)) => { - EitherSignature::A(JoseSig::Es256(Es256::new(sig))) - } - Common::Jose(Signature::ES512(sig)) => { - EitherSignature::A(JoseSig::Es512(Es512::new(sig))) - } - Common::Jose(Signature::EdDSA(sig)) => { - EitherSignature::A(JoseSig::EdDSA(Ed25519::new(sig))) - } - Common::Jose(Signature::RS256(sig)) => { - EitherSignature::A(JoseSig::Rsa256(Rsa256::new(sig))) - } - Common::Jose(Signature::RS512(sig)) => { - EitherSignature::A(JoseSig::Rsa512(Rsa512::new(sig))) - } - Common::Jose(Signature::ES256K(sig)) => { - EitherSignature::A(JoseSig::Es256K(Es256K::new(sig))) - } - Common::Webauthn(sig) => EitherSignature::B(PasskeySig::new(sig)), - Common::Generic(_) => return Err(Error::UnsupportedAlgorithm(Algorithm::None)), - }), - }) - } -} - -impl TryFrom> for UcanCacao { - type Error = Error; - fn try_from(ucan: Ucan) -> Result { - let (payload, signature) = ucan.into_inner(); - Ok(Self { - issuer: MultiDid::from_str(&payload.issuer)?, - audience: MultiDid::from_str(&payload.audience)?, - version: "0.2.0".to_string(), - attenuations: payload.capabilities, - nonce: payload.nonce, - proof: payload.proof, - issued_at: payload.issued_at, - not_before: payload.not_before, - expiration: payload.expiration, - facts: payload.facts, - signature: VarSig::new(EitherSignature::B(PasskeySig::new(signature))), - }) - } -} - -impl From> for Ucan { +impl From> for Ucan { fn from(cacao: UcanCacao) -> Self { let signature = match cacao.signature.into_inner() { - EitherSignature::A(JoseSig::EdDSA(s)) => Common::Jose(Signature::EdDSA(s.into_inner())), - EitherSignature::A(JoseSig::Es256(s)) => Common::Jose(Signature::ES256(s.into_inner())), - EitherSignature::A(JoseSig::Es512(s)) => Common::Jose(Signature::ES512(s.into_inner())), - EitherSignature::A(JoseSig::Es256K(s)) => { - Common::Jose(Signature::ES256K(s.into_inner())) - } - EitherSignature::A(JoseSig::Rsa256(s)) => { - Common::Jose(Signature::RS256(s.into_inner())) - } - EitherSignature::A(JoseSig::Rsa512(s)) => { - Common::Jose(Signature::RS512(s.into_inner())) - } - EitherSignature::B(passkey) => Common::Webauthn(passkey.into_inner()), + JoseSig::EdDSA(s) => Signature::EdDSA(s.into_inner()), + JoseSig::Es256(s) => Signature::ES256(s.into_inner()), + JoseSig::Es512(s) => Signature::ES512(s.into_inner()), + JoseSig::Es256K(s) => Signature::ES256K(s.into_inner()), + JoseSig::Rsa256(s) => Signature::RS256(s.into_inner()), + JoseSig::Rsa512(s) => Signature::RS512(s.into_inner()), }; let mut payload = Payload::new(cacao.issuer.to_string(), cacao.audience.to_string()); payload.capabilities = cacao.attenuations; From f3a163debf8bf370c5aaf2c963ad84354ed253c8 Mon Sep 17 00:00:00 2001 From: chunningham Date: Wed, 20 Sep 2023 00:07:43 +0200 Subject: [PATCH 54/70] derives --- src/v2/common.rs | 1 + src/v2/recap_cacao.rs | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/v2/common.rs b/src/v2/common.rs index 61c1176..f5434ed 100644 --- a/src/v2/common.rs +++ b/src/v2/common.rs @@ -23,6 +23,7 @@ pub enum CommonFacts { pub type CommonCacao = Cacao, NB>; +#[derive(Debug, Clone, PartialEq, Default)] pub struct CommonVerifier(T); impl CommonVerifier { diff --git a/src/v2/recap_cacao.rs b/src/v2/recap_cacao.rs index 98a927e..406a9bc 100644 --- a/src/v2/recap_cacao.rs +++ b/src/v2/recap_cacao.rs @@ -235,7 +235,7 @@ where Authority::from_str(&s).map_err(serde::de::Error::custom) } -#[derive(Default)] +#[derive(Debug, Clone, PartialEq, Default)] pub struct RecapVerify(()); #[cfg_attr(target_arch = "wasm32", async_trait(?Send))] From b60422935434289f86b12ebebb3d91a02fc7895a Mon Sep 17 00:00:00 2001 From: chunningham Date: Wed, 20 Sep 2023 00:31:09 +0200 Subject: [PATCH 55/70] serialize_jwt util --- src/v2/common.rs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/v2/common.rs b/src/v2/common.rs index f5434ed..d869219 100644 --- a/src/v2/common.rs +++ b/src/v2/common.rs @@ -32,6 +32,23 @@ impl CommonVerifier { } } +impl CommonCacao +where + F: Clone + Serialize, + NB: Clone + Serialize, +{ + pub fn serialize_jwt(&self) -> Result, Error> { + Ok(match self.signature.sig() { + CommonSignature::A(_) => None, + CommonSignature::B(_) => UcanCacao::::try_from(self.clone()) + .ok() + .map(|uc| Ucan::::from(uc).encode()) + .transpose() + .map_err(|e| Error::Ucan(e.into()))?, + }) + } +} + #[derive(thiserror::Error, Debug)] pub enum Error> { #[error(transparent)] From cd2a6593f547a8cb9ab4803f56a2ec058952df53 Mon Sep 17 00:00:00 2001 From: chunningham Date: Wed, 20 Sep 2023 00:39:48 +0200 Subject: [PATCH 56/70] better ucan error --- src/v2/common.rs | 14 +++++++------- src/v2/ucan_cacao.rs | 14 +++++++++++--- 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/src/v2/common.rs b/src/v2/common.rs index d869219..3716da1 100644 --- a/src/v2/common.rs +++ b/src/v2/common.rs @@ -50,7 +50,7 @@ where } #[derive(thiserror::Error, Debug)] -pub enum Error> { +pub enum Error { #[error(transparent)] Ucan(U), #[error(transparent)] @@ -59,15 +59,15 @@ pub enum Error> { Mismatch, } -impl From> for Error { - fn from(e: VerificationError) -> Self { +impl From for Error { + fn from(e: UcanError) -> Self { Self::Ucan(e) } } -impl From for Error { - fn from(e: UcanError) -> Self { - Self::Ucan(e) +impl From> for Error { + fn from(e: VerificationError) -> Self { + Self::Ucan(e.into()) } } @@ -241,7 +241,7 @@ impl TryFrom> for UcanCacao { } impl TryFrom> for CommonCacao { - type Error = Error; + type Error = Error; fn try_from(ucan: Ucan) -> Result { Ok(UcanCacao::try_from(ucan)?.into()) } diff --git a/src/v2/ucan_cacao.rs b/src/v2/ucan_cacao.rs index 21b3f0d..edacf5b 100644 --- a/src/v2/ucan_cacao.rs +++ b/src/v2/ucan_cacao.rs @@ -6,7 +6,7 @@ use serde_json::Value; use ssi_dids::did_resolve::DIDResolver; use ssi_ucan::{ jose::{self, Signature, VerificationError}, - Payload, Ucan, + jwt, Payload, Ucan, }; use std::collections::BTreeMap; use std::str::FromStr; @@ -23,6 +23,14 @@ pub type UcanCacao = Cacao, N pub enum Error { #[error(transparent)] MultididParse(#[from] multidid::ParseErr), + #[error(transparent)] + Verification(#[from] VerificationError), +} + +impl From for Error { + fn from(e: jwt::EncodeError) -> Self { + Self::Verification(e.into()) + } } #[cfg_attr(target_arch = "wasm32", async_trait(?Send))] @@ -33,9 +41,9 @@ where F: Send + Sync + for<'a> Deserialize<'a> + Serialize + Clone, NB: Send + Sync + for<'a> Deserialize<'a> + Serialize + Clone, { - type Error = VerificationError; + type Error = Error; - async fn verify(&self, cacao: &UcanCacao) -> Result<(), VerificationError> { + async fn verify(&self, cacao: &UcanCacao) -> Result<(), Error> { let ucan = Ucan::::from(cacao.clone()).encode()?; Ucan::::decode_and_verify_jwt(&ucan, *self, None).await?; Ok(()) From 878067e0a832d31bc84c20d07cc599a9a00f716a Mon Sep 17 00:00:00 2001 From: chunningham Date: Wed, 20 Sep 2023 01:16:47 +0200 Subject: [PATCH 57/70] remove SKEW const generic --- src/v2/mod.rs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/v2/mod.rs b/src/v2/mod.rs index 1aa2ba1..6a64ac9 100644 --- a/src/v2/mod.rs +++ b/src/v2/mod.rs @@ -85,9 +85,12 @@ impl Cacao { &self.signature } - pub fn valid_at_time>(&self, time: T) -> bool { - self.expiration.map_or(true, |exp| time < exp + SKEW) - && self.not_before.map_or(true, |nbf| time >= nbf - SKEW) + pub fn valid_at_time(&self, time: u64, skew: Option) -> bool { + self.expiration + .map_or(true, |exp| time < exp + skew.unwrap_or(0)) + && self + .not_before + .map_or(true, |nbf| time >= nbf - skew.unwrap_or(0)) && self.issued_at.map_or(true, |iat| { self.not_before.map_or(true, |nbf| nbf < iat) && self.expiration.map_or(true, |exp| iat < exp) From f3fa7df3de2fa61fedf2efac1fa359d67d523541 Mon Sep 17 00:00:00 2001 From: chunningham Date: Wed, 20 Sep 2023 13:37:04 +0200 Subject: [PATCH 58/70] siwe to commoncacao conversion --- src/v2/common.rs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/v2/common.rs b/src/v2/common.rs index 3716da1..9f36bf1 100644 --- a/src/v2/common.rs +++ b/src/v2/common.rs @@ -6,6 +6,7 @@ use super::{ use async_trait::async_trait; use serde::{Deserialize, Serialize}; use serde_json::Value; +use siwe::Message; use ssi_ucan::{ jose::{self, Signature, VerificationError}, Ucan, @@ -246,3 +247,13 @@ impl TryFrom> for CommonCacao { Ok(UcanCacao::try_from(ucan)?.into()) } } + +impl TryFrom<(Message, [u8; 65])> for CommonCacao +where + NB: for<'d> Deserialize<'d>, +{ + type Error = Error; + fn try_from(siwe: (Message, [u8; 65])) -> Result { + Ok(RecapCacao::try_from(siwe)?.into()) + } +} From 40c59d5490af963e325c5a334e5c7641ee600f2b Mon Sep 17 00:00:00 2001 From: chunningham Date: Wed, 20 Sep 2023 14:03:16 +0200 Subject: [PATCH 59/70] fix multidid to_vec --- multidid/src/lib.rs | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/multidid/src/lib.rs b/multidid/src/lib.rs index 00e1b88..5634f4a 100644 --- a/multidid/src/lib.rs +++ b/multidid/src/lib.rs @@ -55,17 +55,21 @@ impl MultiDid { } pub fn to_vec(&self) -> Vec { - match (&self.query, &self.fragment) { - (Some(q), Some(f)) => [ - self.method.to_vec(), - q.as_str().as_bytes().to_vec(), - f.as_str().as_bytes().to_vec(), - ] - .concat(), - (None, Some(f)) => [self.method.to_vec(), f.as_str().as_bytes().to_vec()].concat(), - (Some(q), None) => [self.method.to_vec(), q.as_str().as_bytes().to_vec()].concat(), - (None, None) => self.method.to_vec(), - } + [ + [0x9d, 0x1a].to_vec(), + match (&self.query, &self.fragment) { + (Some(q), Some(f)) => [ + self.method.to_vec(), + q.as_str().as_bytes().to_vec(), + f.as_str().as_bytes().to_vec(), + ] + .concat(), + (None, Some(f)) => [self.method.to_vec(), f.as_str().as_bytes().to_vec()].concat(), + (Some(q), None) => [self.method.to_vec(), q.as_str().as_bytes().to_vec()].concat(), + (None, None) => self.method.to_vec(), + }, + ] + .concat() } pub fn from_bytes(b: &[u8]) -> Result { From 88a78d50484a80211ec1da31828329332314220f Mon Sep 17 00:00:00 2001 From: chunningham Date: Wed, 20 Sep 2023 16:17:43 +0200 Subject: [PATCH 60/70] fix multidid pkh and tests --- multidid/src/lib.rs | 95 ++++++++++++++++++++------------------- multidid/src/method.rs | 7 ++- multidid/src/pkh.rs | 47 +++++++++---------- multidid/tests/valid.json | 10 ++--- 4 files changed, 81 insertions(+), 78 deletions(-) diff --git a/multidid/src/lib.rs b/multidid/src/lib.rs index 5634f4a..5e10c24 100644 --- a/multidid/src/lib.rs +++ b/multidid/src/lib.rs @@ -55,19 +55,44 @@ impl MultiDid { } pub fn to_vec(&self) -> Vec { + let plen = self + .fragment + .as_ref() + .map(|f| f.as_str().len() + 1) + .unwrap_or(0) + + self + .query + .as_ref() + .map(|q| q.as_str().len() + 1) + .unwrap_or(0); + let mut len_buf = u64_buffer(); [ - [0x9d, 0x1a].to_vec(), - match (&self.query, &self.fragment) { - (Some(q), Some(f)) => [ - self.method.to_vec(), - q.as_str().as_bytes().to_vec(), - f.as_str().as_bytes().to_vec(), - ] - .concat(), - (None, Some(f)) => [self.method.to_vec(), f.as_str().as_bytes().to_vec()].concat(), - (Some(q), None) => [self.method.to_vec(), q.as_str().as_bytes().to_vec()].concat(), - (None, None) => self.method.to_vec(), + MULTIDID_VARINT_TAG.to_be_bytes().to_vec(), + match self.method() { + Method::Raw(raw) => { + let len: u64 = (plen + raw.len()) as u64; + let mut codec_buf = u64_buffer(); + [ + write_u64(method::RAW_CODEC, &mut codec_buf), + write_u64(len, &mut len_buf), + ] + .concat() + } + _ => Vec::new(), }, + self.method.to_vec(), + match self.method() { + Method::Raw(_) => Vec::new(), + _ => write_u64(plen as u64, &mut len_buf).to_vec(), + }, + self.query + .as_ref() + .map(|q| format!("?{q}").as_bytes().to_vec()) + .unwrap_or_default(), + self.fragment + .as_ref() + .map(|f| format!("#{f}").as_bytes().to_vec()) + .unwrap_or_default(), ] .concat() } @@ -123,36 +148,24 @@ impl MultiDid { // write codec let mut buf = u64_buffer(); writer.write_all(write_u64(self.method.codec(), &mut buf))?; + let plen = self + .fragment + .as_ref() + .map(|f| f.as_str().len() + 1) + .unwrap_or(0) + + self + .query + .as_ref() + .map(|q| q.as_str().len() + 1) + .unwrap_or(0); match self.method { Method::Pkh(_) | Method::Key(_) => { self.method.to_writer(writer)?; - let len: u64 = (self - .fragment - .as_ref() - .map(|f| f.as_str().len() + 1) - .unwrap_or(0) - + self - .query - .as_ref() - .map(|q| q.as_str().len() + 1) - .unwrap_or(0)) as u64; - writer.write_all(write_u64(len, &mut buf))?; + writer.write_all(write_u64(plen as u64, &mut buf))?; } Method::Raw(ref raw) => { - let len: u64 = (self - .fragment - .as_ref() - .map(|f| f.as_str().len() + 1) - .unwrap_or(0) - + self - .query - .as_ref() - .map(|q| q.as_str().len() + 1) - .unwrap_or(0) - + raw.len()) as u64; - writer.write_all(&[method::RAW_CODEC as u8])?; - writer.write_all(write_u64(len, &mut buf))?; + writer.write_all(write_u64((plen + raw.len()) as u64, &mut buf))?; writer.write_all(raw.as_bytes())?; } }; @@ -202,6 +215,7 @@ impl FromStr for MultiDid { type Err = ParseErr; fn from_str(s: &str) -> Result { + let s = s.strip_prefix("did:").ok_or(method::ParseErr::Invalid)?; let (did, query, fragment) = if let Some((did, rest)) = s.split_once('?') { if let Some((query, fragment)) = rest.split_once('#') { (did, Some(query), Some(fragment)) @@ -270,22 +284,13 @@ mod tests { #[test] fn it_works() { let valid: Vec = serde_json::from_str(VALID_JSON).unwrap(); - for test in &valid { - if test.method.as_str() == "raw" { - let s = test.decoded.as_str()[4..] - .split_once("?") - .map(|(a, b)| a) - .unwrap_or(test.decoded.as_str()); - println!("{:x}", s.as_bytes().len()); - println!("{}", hex::encode(s.as_bytes())); - } - } for test in valid { let did = MultiDid::from_reader(&mut test.encoded.as_slice()).unwrap(); assert_eq!(did.query, test.query); assert_eq!(did.fragment, test.fragment); assert_eq!(did.to_vec(), test.encoded); assert_eq!(did.to_string(), test.decoded); + assert_eq!(did, MultiDid::from_bytes(&test.encoded).unwrap()); assert_eq!(did, test.decoded.parse().unwrap()); assert!(match (did.method(), test.method.as_str()) { (Method::Key(_), "key") => true, diff --git a/multidid/src/method.rs b/multidid/src/method.rs index 7e0da11..01ffb51 100644 --- a/multidid/src/method.rs +++ b/multidid/src/method.rs @@ -1,7 +1,10 @@ use crate::{key, pkh, pkh::PKH_CODEC, DidKeyTypes, DidPkhTypes, Error}; use std::io::{Error as IoError, Read, Write}; use std::{fmt::Display, str::FromStr}; -use unsigned_varint::io::read_u64; +use unsigned_varint::{ + encode::{u64 as write_u64, u64_buffer}, + io::read_u64, +}; pub const RAW_CODEC: u64 = 0x55; @@ -50,6 +53,8 @@ impl Method { pub(crate) fn to_writer(&self, writer: &mut W) -> Result<(), IoError> { match self { Self::Raw(buf) => { + let mut cbuf = u64_buffer(); + writer.write_all(write_u64(RAW_CODEC, &mut cbuf))?; writer.write_all(buf.as_bytes())?; } Self::Pkh(pkh) => { diff --git a/multidid/src/pkh.rs b/multidid/src/pkh.rs index 31ac544..daebec7 100644 --- a/multidid/src/pkh.rs +++ b/multidid/src/pkh.rs @@ -14,7 +14,7 @@ pub(crate) const PKH_CODEC: u64 = 0xca; #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub enum DidPkhTypes { - Bip122(Caip10<[u8; 32], [u8; 25]>), + Bip122(Caip10<[u8; 16], [u8; 25]>), Eip155(Caip10), Cosmos(Caip10), Starknet(Caip10), @@ -92,9 +92,8 @@ impl DidPkhTypes { } pub(crate) fn to_vec(&self) -> Vec { - let mut vec = Vec::new(); - vec.push(PKH_CODEC as u8); let mut buf = u64_buffer(); + let mut vec = write_u64(PKH_CODEC, &mut buf).to_vec(); match self { DidPkhTypes::Bip122(caip10) => { vec.extend(write_u64(1, &mut buf)); @@ -122,7 +121,7 @@ impl DidPkhTypes { } } DidPkhTypes::Starknet(caip10) => { - vec.extend(write_u64(3, &mut buf)); + vec.extend(write_u64(4, &mut buf)); vec.extend(write_u64(caip10.chain_id().len() as u64, &mut buf)); vec.extend(caip10.chain_id().as_bytes()); vec.extend(caip10.address()); @@ -139,7 +138,7 @@ impl DidPkhTypes { match pkh_type { // bitcoin-like 1 => { - let mut chain_id = [0u8; 32]; + let mut chain_id = [0u8; 16]; reader.read_exact(&mut chain_id)?; let mut address = [0u8; 25]; reader.read_exact(&mut address)?; @@ -195,8 +194,8 @@ impl DidPkhTypes { where W: ?Sized + Write, { - writer.write_all(&[PKH_CODEC as u8])?; let mut buf = u64_buffer(); + writer.write_all(write_u64(PKH_CODEC, &mut buf))?; match self { DidPkhTypes::Bip122(caip10) => { writer.write_all(write_u64(1, &mut buf))?; @@ -224,7 +223,7 @@ impl DidPkhTypes { } } DidPkhTypes::Starknet(caip10) => { - writer.write_all(write_u64(3, &mut buf))?; + writer.write_all(write_u64(4, &mut buf))?; writer.write_all(write_u64(caip10.chain_id().len() as u64, &mut buf))?; writer.write_all(caip10.chain_id().as_bytes())?; writer.write_all(caip10.address())?; @@ -356,32 +355,26 @@ pub enum Eip55Err { fn parse_eip55(address: &str) -> Result<[u8; 20], Eip55Err> { use hex::FromHex; - if !address.starts_with("0x") { - Err(Eip55Err::MissingPrefix) + let address = address.strip_prefix("0x").ok_or(Eip55Err::MissingPrefix)?; + let s = <[u8; 20]>::from_hex(address)?; + let sum = eip55(&s); + let sum = sum.trim_start_matches("0x"); + if sum != address { + Err(Eip55Err::InvalidChecksum) } else { - let s = <[u8; 20]>::from_hex(address)?; - let sum = eip55(&s); - let sum = sum.trim_start_matches("0x"); - if sum != address { - Err(Eip55Err::InvalidChecksum) - } else { - Ok(s) - } + Ok(s) } } fn parse_starknet(address: &str) -> Result<[u8; 32], Eip55Err> { use hex::FromHex; - if !address.starts_with("0x") { - Err(Eip55Err::MissingPrefix) + let address = address.strip_prefix("0x").ok_or(Eip55Err::MissingPrefix)?; + let s = <[u8; 32]>::from_hex(address)?; + let sum = eip55(&s); + let sum = sum.trim_start_matches("0x"); + if sum != address { + Err(Eip55Err::InvalidChecksum) } else { - let s = <[u8; 32]>::from_hex(address)?; - let sum = eip55(&s); - let sum = sum.trim_start_matches("0x"); - if sum != address { - Err(Eip55Err::InvalidChecksum) - } else { - Ok(s) - } + Ok(s) } } diff --git a/multidid/tests/valid.json b/multidid/tests/valid.json index ce397a7..5f9fccb 100644 --- a/multidid/tests/valid.json +++ b/multidid/tests/valid.json @@ -21,19 +21,19 @@ "decoded": "did:key:zQ3shokFTS3brHcDQrn82RUDfCZESWL1ZdCEJwekUDPQiYBme", "method": "key" }, { - "encoded": "9d1aca0101000000000019d6689c085ae165831e93000c5b86d784abdf01eb30f21868deb8de53abacb7b3e3109f", + "encoded": "9d1aca0101000000000019d6689c085ae165831e93000c5b86d784abdf01eb30f21868deb8de53abacb7b3e3109f00", "decoded": "did:pkh:bip122:000000000019d6689c085ae165831e93:128Lkh3S7CkDTBZ8W7BbpsN3YYizJMp8p6", "method": "pkh" }, { - "encoded": "9d1aca010201ab16a96d359ec26a11e2c2b3d8f8b8942d5bfcdb", + "encoded": "9d1aca010201ab16a96d359ec26a11e2c2b3d8f8b8942d5bfcdb00", "decoded": "did:pkh:eip155:1:0xab16a96D359eC26a11e2C2b3d8f8B8942d5Bfcdb", "method": "pkh" }, { - "encoded": "9d1aca01030b636f736d6f736875622d335ab89f81c0cbd3093e17c26c360796cbb0eabc92", + "encoded": "9d1aca01030b636f736d6f736875622d33145ab89f81c0cbd3093e17c26c360796cbb0eabc9200", "decoded": "did:pkh:cosmos:cosmoshub-3:cosmos1t2uflqwqe0fsj0shcfkrvpukewcw40yjj6hdc0", "method": "pkh" }, { - "encoded": "9d1aca010409534e5f474f45524c4902dd1b492765c064eac4039e3841aa5f382773b598097a40073bd8b48170ab57", - "decoded": "did:pkh:starknet:SN_GOERLI:0x02dd1b492765c064eac4039e3841aa5f382773b598097a40073bd8b48170ab57", + "encoded": "9d1aca010409534e5f474f45524c4902dd1b492765c064eac4039e3841aa5f382773b598097a40073bd8b48170ab5700", + "decoded": "did:pkh:starknet:SN_GOERLI:0x02DD1B492765c064eAc4039E3841Aa5f382773b598097A40073BD8b48170ab57", "method": "pkh" }] From a30415af5ccadf4fd96684c5ad621bb8bfefa5b7 Mon Sep 17 00:00:00 2001 From: chunningham Date: Wed, 20 Sep 2023 16:19:32 +0200 Subject: [PATCH 61/70] rename v2 -> v3 and export in lib --- src/lib.rs | 4 +++- src/{v2 => v3}/common.rs | 0 src/{v2 => v3}/mod.rs | 0 src/{v2 => v3}/recap_cacao.rs | 0 src/{v2 => v3}/ucan_cacao.rs | 0 src/{v2 => v3}/version.rs | 0 6 files changed, 3 insertions(+), 1 deletion(-) rename src/{v2 => v3}/common.rs (100%) rename src/{v2 => v3}/mod.rs (100%) rename src/{v2 => v3}/recap_cacao.rs (100%) rename src/{v2 => v3}/ucan_cacao.rs (100%) rename src/{v2 => v3}/version.rs (100%) diff --git a/src/lib.rs b/src/lib.rs index dc106f2..f998521 100755 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,4 +1,6 @@ pub use siwe; pub mod v1; -pub mod v2; +pub mod v3; + +pub use v3::*; diff --git a/src/v2/common.rs b/src/v3/common.rs similarity index 100% rename from src/v2/common.rs rename to src/v3/common.rs diff --git a/src/v2/mod.rs b/src/v3/mod.rs similarity index 100% rename from src/v2/mod.rs rename to src/v3/mod.rs diff --git a/src/v2/recap_cacao.rs b/src/v3/recap_cacao.rs similarity index 100% rename from src/v2/recap_cacao.rs rename to src/v3/recap_cacao.rs diff --git a/src/v2/ucan_cacao.rs b/src/v3/ucan_cacao.rs similarity index 100% rename from src/v2/ucan_cacao.rs rename to src/v3/ucan_cacao.rs diff --git a/src/v2/version.rs b/src/v3/version.rs similarity index 100% rename from src/v2/version.rs rename to src/v3/version.rs From 70ab0d09ee228c222f773513fe1b8cc98a9cc9f9 Mon Sep 17 00:00:00 2001 From: chunningham Date: Wed, 20 Sep 2023 18:06:14 +0200 Subject: [PATCH 62/70] make some recap facts optional --- src/v3/recap_cacao.rs | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/src/v3/recap_cacao.rs b/src/v3/recap_cacao.rs index 406a9bc..07f6377 100644 --- a/src/v3/recap_cacao.rs +++ b/src/v3/recap_cacao.rs @@ -23,17 +23,22 @@ pub type RecapCacao = Cacao; pub struct RecapFacts { #[serde(rename = "iat-z")] iat_info: String, - #[serde(rename = "nbf-z")] + #[serde(rename = "nbf-z", skip_serializing_if = "Option::is_none", default)] nbf_info: Option, - #[serde(rename = "nbf-z")] + #[serde(rename = "nbf-z", skip_serializing_if = "Option::is_none", default)] exp_info: Option, #[serde( serialize_with = "serialize_authority", deserialize_with = "deserialize_authority" )] domain: Authority, + #[serde(skip_serializing_if = "Option::is_none", default)] statement: Option, - #[serde(rename = "request-id")] + #[serde( + rename = "request-id", + skip_serializing_if = "Option::is_none", + default + )] request_id: Option, resources: Vec, } @@ -86,7 +91,13 @@ where }; let statement = siwe.statement.and_then(|s| { s.get(0..(s.len() - recap.as_ref().map(|r| r.to_statement().len()).unwrap_or(0))) - .map(|s| s.to_string()) + .and_then(|s| { + if s.len() == 0 { + None + } else { + Some(s.to_string()) + } + }) }); let (attenuations, proof) = recap .map(|r| r.into_inner()) From c1fe1b6659a6cbf42b65ff29290fd129d2aab2f5 Mon Sep 17 00:00:00 2001 From: chunningham Date: Fri, 22 Sep 2023 15:08:43 +0200 Subject: [PATCH 63/70] fix recap timestamp conversion + reconstruction --- Cargo.toml | 2 +- src/v3/mod.rs | 5 -- src/v3/recap_cacao.rs | 120 +++++++++++++++++++++++------------------- src/v3/ucan_cacao.rs | 12 ----- 4 files changed, 66 insertions(+), 73 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 4dfa16b..efaeee9 100755 --- a/Cargo.toml +++ b/Cargo.toml @@ -29,7 +29,7 @@ ssi-ucan = { git = "https://github.com/spruceid/ssi.git", branch = "feat/ucan-0. ssi-jwk = { git = "https://github.com/spruceid/ssi.git", branch = "feat/ucan-0.10" } ssi-jwt = { git = "https://github.com/spruceid/ssi.git", branch = "feat/ucan-0.10" } ssi-dids = { git = "https://github.com/spruceid/ssi.git", branch = "feat/ucan-0.10" } -time = { version = "0.3", features = ["parsing", "formatting", "serde"] } +time = { version = "0.3", features = ["parsing", "formatting", "serde", "macros"] } http = "0.2.5" hex = { version = "0.4", optional = true } varsig = { version = "0.1", path = "./varsig" } diff --git a/src/v3/mod.rs b/src/v3/mod.rs index 6a64ac9..d9f6bab 100644 --- a/src/v3/mod.rs +++ b/src/v3/mod.rs @@ -113,8 +113,3 @@ pub trait CacaoVerifier { async fn verify(&self, cacao: &Cacao) -> Result<(), Self::Error>; } - -#[cfg(test)] -pub mod tests { - use super::*; -} diff --git a/src/v3/recap_cacao.rs b/src/v3/recap_cacao.rs index 07f6377..9f3e9e8 100644 --- a/src/v3/recap_cacao.rs +++ b/src/v3/recap_cacao.rs @@ -6,10 +6,10 @@ use multidid::{DidPkhTypes, Method, MultiDid}; use serde::{Deserialize, Serialize}; use serde_json::Value; pub use siwe; -use siwe::{Message, TimeStamp}; +use siwe::Message; pub use siwe_recap::Capability; use std::{fmt::Debug, str::FromStr}; -use time::{format_description::well_known::Rfc3339, OffsetDateTime, UtcOffset}; +use time_conv::*; use varsig::{ common::{Ethereum, EIP191_ENCODING}, VarSig, @@ -80,12 +80,12 @@ where type Error = Error; fn try_from((siwe, sig): (Message, [u8; 65])) -> Result { let recap = Capability::::extract_and_verify(&siwe)?; - let (issued_at, iat_info) = split_tz(siwe.issued_at); - let (not_before, nbf_info) = match siwe.not_before.map(split_tz) { + let (issued_at, iat_info) = split_tz(&siwe.issued_at); + let (not_before, nbf_info) = match siwe.not_before.as_ref().map(split_tz) { Some((nb, tz)) => (Some(nb), Some(tz)), None => (None, None), }; - let (expiration, exp_info) = match siwe.expiration_time.map(split_tz) { + let (expiration, exp_info) = match siwe.expiration_time.as_ref().map(split_tz) { Some((exp, tz)) => (Some(exp), Some(tz)), None => (None, None), }; @@ -181,56 +181,6 @@ where } } -fn split_tz(t: TimeStamp) -> (u64, String) { - let unix = t.as_ref().unix_timestamp(); - let mut t_str = t.to_string(); - ( - // hmmm - unix as u64, - t_str - // if its fractional, split on '. - .find('.') - // if not, split on 'Z' or 'z' - .or_else(|| t_str.find('Z')) - .or_else(|| t_str.find('z')) - .map(|i| t_str.split_off(i)) - // this default should never actually happen - // as long as TimeStamp serialises properly - .unwrap_or(t.as_ref().offset().to_string()), - ) -} - -fn make_ts(unix: u64, z: &str) -> Result { - // we need to get the serialisation of the date and time - let odt = OffsetDateTime::from_unix_timestamp(unix as i64)? - // by setting the offset to the same one in z - .to_offset(UtcOffset::parse( - if z.starts_with('.') { - z.find('Z') - .or_else(|| z.find('z')) - .and_then(|i| z.get((i + 1)..)) - .unwrap_or(z) - } else { - z - }, - &Rfc3339, - )?) - .to_string(); - - // then concat that serialisation with z to get the original timestamp - TimeStamp::from_str( - &[ - odt.find('.') - .or_else(|| odt.find('Z')) - .or_else(|| odt.find('z')) - .and_then(|i| odt.get(..i)) - .unwrap_or(&odt), - z, - ] - .concat(), - ) -} - fn serialize_authority(authority: &Authority, serializer: S) -> Result where S: serde::Serializer, @@ -263,3 +213,63 @@ where Ok(()) } } + +mod time_conv { + use siwe::TimeStamp; + use time::{ + error::Error, + format_description::{well_known::Rfc3339, FormatItem}, + macros::{format_description, offset}, + OffsetDateTime, UtcOffset, + }; + + const TZ_SEP: [char; 4] = ['Z', 'z', '+', '-']; + const T_LEN: usize = 19; + + pub fn split_tz(t: &TimeStamp) -> (u64, String) { + let unix = t.as_ref().unix_timestamp() as u64; + let mut t_str = t.to_string(); + // TimeStamp should always have a valid rfc3339 string, so split_off should never fail + (unix, t_str.split_off(T_LEN)) + } + + const TZ_FORMAT: &'static [FormatItem<'static>] = format_description!( + version = 2, + "[first [[offset_hour]:[offset_minute]] [Z] [z]]" + ); + + pub fn make_ts(unix: u64, z: &str) -> Result { + let i = z.find(TZ_SEP).unwrap(); + let offset = match &z[i..i + 1] { + "Z" | "z" => offset!(+00:00), + _ => UtcOffset::parse(&z[i..], TZ_FORMAT)?, + }; + let mut t_str = OffsetDateTime::from_unix_timestamp(unix as i64)? + .to_offset(offset) + .format(&Rfc3339)?; + t_str.replace_range(T_LEN.., z); + Ok(t_str.parse()?) + } + + #[cfg(test)] + mod test { + use super::*; + + #[test] + fn timestamps() { + let cases = [ + "2985-01-12T23:20:50.52Z", + "2985-02-12T23:20:50Z", + "2985-03-12T23:20:50.52+00:02", + "2985-05-12T23:20:50.52+10:20", + "2985-04-12T23:20:50+10:20", + ]; + for case in cases.iter() { + let ts: TimeStamp = case.parse().unwrap(); + let (unix, z) = split_tz(&ts); + let ts2 = make_ts(unix, &z).unwrap(); + assert_eq!(ts, ts2); + } + } + } +} diff --git a/src/v3/ucan_cacao.rs b/src/v3/ucan_cacao.rs index edacf5b..4a362d0 100644 --- a/src/v3/ucan_cacao.rs +++ b/src/v3/ucan_cacao.rs @@ -98,15 +98,3 @@ impl From> for Ucan { payload.sign(signature) } } - -#[cfg(test)] -pub mod tests { - use super::*; - - #[test] - fn tz() { - use time::OffsetDateTime; - let time = OffsetDateTime::now_local().unwrap(); - println!("{}", time.offset()); - } -} From 4c0139b0a3025b54f18c1c0ac0047377c4b86fc9 Mon Sep 17 00:00:00 2001 From: chunningham Date: Fri, 22 Sep 2023 15:41:39 +0200 Subject: [PATCH 64/70] fix exp_info serde rename --- src/v3/recap_cacao.rs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/v3/recap_cacao.rs b/src/v3/recap_cacao.rs index 9f3e9e8..8f182c3 100644 --- a/src/v3/recap_cacao.rs +++ b/src/v3/recap_cacao.rs @@ -25,7 +25,7 @@ pub struct RecapFacts { iat_info: String, #[serde(rename = "nbf-z", skip_serializing_if = "Option::is_none", default)] nbf_info: Option, - #[serde(rename = "nbf-z", skip_serializing_if = "Option::is_none", default)] + #[serde(rename = "exp-z", skip_serializing_if = "Option::is_none", default)] exp_info: Option, #[serde( serialize_with = "serialize_authority", @@ -233,10 +233,8 @@ mod time_conv { (unix, t_str.split_off(T_LEN)) } - const TZ_FORMAT: &'static [FormatItem<'static>] = format_description!( - version = 2, - "[first [[offset_hour]:[offset_minute]] [Z] [z]]" - ); + const TZ_FORMAT: &'static [FormatItem<'static>] = + format_description!(version = 2, "[offset_hour]:[offset_minute]"); pub fn make_ts(unix: u64, z: &str) -> Result { let i = z.find(TZ_SEP).unwrap(); From bf1254351b602887c165a052da7e0a06fd1ad381 Mon Sep 17 00:00:00 2001 From: chunningham Date: Fri, 22 Sep 2023 15:43:51 +0200 Subject: [PATCH 65/70] basic test for deser and verification --- Cargo.toml | 3 +++ src/v3/common.rs | 14 ++++++++++++++ 2 files changed, 17 insertions(+) diff --git a/Cargo.toml b/Cargo.toml index efaeee9..ee95b9d 100755 --- a/Cargo.toml +++ b/Cargo.toml @@ -38,3 +38,6 @@ siwe-recap = { version = "0.2" } [dev-dependencies] async-std = { version = "1.10", features = ["attributes"] } +base64 = "0.13" +serde_ipld_dagcbor = "0.4" +did-method-key = { git = "https://github.com/spruceid/ssi.git", branch = "feat/ucan-0.10" } diff --git a/src/v3/common.rs b/src/v3/common.rs index 9f36bf1..31cd8bc 100644 --- a/src/v3/common.rs +++ b/src/v3/common.rs @@ -257,3 +257,17 @@ where Ok(RecapCacao::try_from(siwe)?.into()) } } + +#[cfg(test)] +mod test { + use super::*; + + #[async_std::test] + async fn basic() { + let encoded = "qmNpc3NYG50aygECAbFNPE9fv7z7mK8tMwAA1JyVuTqnAGNhdWRYVp0a7QE4NstKM22BZAAHaojdiik61NN1l7wVRKwcQJWixAbFHzEjejZNa2lFaExWMmVxOWFqelBRQTZRMkJVU2t6WHdUMm1GYkR2TkdqTXl0VzRTV2g0YXZhMWNhdHSheEtrZXBsZXI6cGtoOmVpcDE1NToxOjB4QjE0ZDNjNEY1RkJGQkNGQjk4YWYyZDMzMDAwMGQ0OWM5NUI5M2FBNzovL2RlZmF1bHQva3alZmt2L2RlbIGgZmt2L2dldIGgZmt2L3B1dIGgZ2t2L2xpc3SBoGtrdi9tZXRhZGF0YYGgY25uY3FVajg5YkdHWmtoNFJKNjY4dmNwcmaAY2lhdBocvbpSY2V4cBsAAAAHda9C0mNmY3SkZWlhdC16ZC41MlplZXhwLXpkLjUyWmZkb21haW5pbG9jYWxob3N0aXJlc291cmNlc4Bhc1hINOcBG5HDAwx4ASjJGspMdIhMu2NX9o06hfQbz1SqCvdAWjwWOpO6aDh56KfReehDpmKEJxYXdQVVLMAmFVq1SZxBIzljSnkc"; + let decoded = base64::decode(encoded).unwrap(); + let cacao: CommonCacao = serde_ipld_dagcbor::from_slice(&decoded).unwrap(); + let verifier = CommonVerifier::new(&did_method_key::DIDKey); + cacao.verify(&verifier).await.unwrap(); + } +} From 2217f7776760a1b2add7417f0751d9658285fc13 Mon Sep 17 00:00:00 2001 From: chunningham Date: Fri, 22 Sep 2023 17:51:46 +0200 Subject: [PATCH 66/70] clippy --- multidid/src/key.rs | 2 +- multidid/src/lib.rs | 4 ++-- multidid/src/pkh.rs | 6 +++--- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/multidid/src/key.rs b/multidid/src/key.rs index 77e2051..ec3bc8a 100644 --- a/multidid/src/key.rs +++ b/multidid/src/key.rs @@ -104,7 +104,7 @@ impl DidKeyTypes { { let mut buf = u64_buffer(); writer.write_all(write_u64(self.codec(), &mut buf))?; - writer.write_all(&self.bytes()) + writer.write_all(self.bytes()) } fn bytes(&self) -> &[u8] { diff --git a/multidid/src/lib.rs b/multidid/src/lib.rs index 5e10c24..5b13c1d 100644 --- a/multidid/src/lib.rs +++ b/multidid/src/lib.rs @@ -97,8 +97,8 @@ impl MultiDid { .concat() } - pub fn from_bytes(b: &[u8]) -> Result { - Self::from_reader(&mut b.as_ref()) + pub fn from_bytes(mut b: &[u8]) -> Result { + Self::from_reader(&mut b) } pub fn from_reader(reader: &mut R) -> Result { diff --git a/multidid/src/pkh.rs b/multidid/src/pkh.rs index daebec7..0dc6233 100644 --- a/multidid/src/pkh.rs +++ b/multidid/src/pkh.rs @@ -204,7 +204,7 @@ impl DidPkhTypes { } DidPkhTypes::Eip155(caip10) => { writer.write_all(write_u64(2, &mut buf))?; - writer.write_all(write_u64(*caip10.chain_id() as u64, &mut buf))?; + writer.write_all(write_u64(*caip10.chain_id(), &mut buf))?; writer.write_all(caip10.address())?; } DidPkhTypes::Cosmos(caip10) => { @@ -269,8 +269,8 @@ impl FromStr for DidPkhTypes { fn from_str(s: &str) -> Result { Ok( match s - .split_once(":") - .and_then(|(method, r)| Some((method, r.split_once(":")?))) + .split_once(':') + .and_then(|(method, r)| Some((method, r.split_once(':')?))) { Some(("bip122", (chain_id, address))) => Self::Bip122(Caip10::new( hex::decode(chain_id) From 004747e9e3b565d69357bb5820ccd7d26ea74c31 Mon Sep 17 00:00:00 2001 From: chunningham Date: Fri, 22 Sep 2023 19:13:10 +0200 Subject: [PATCH 67/70] more clippy --- src/v3/common.rs | 7 ++++--- src/v3/recap_cacao.rs | 6 +++--- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/v3/common.rs b/src/v3/common.rs index 31cd8bc..718a286 100644 --- a/src/v3/common.rs +++ b/src/v3/common.rs @@ -83,7 +83,7 @@ where type Error = Error; async fn verify(&self, cacao: &CommonCacao) -> Result<(), Self::Error> { - Ok(match RecapCacao::try_from(cacao.clone()) { + match RecapCacao::try_from(cacao.clone()) { Ok(recap) => self.verify(&recap).await?, Err(c) => match UcanCacao::try_from(c) { Ok(ucan) => self.verify(&ucan).await.map_err(Error::Ucan)?, @@ -91,7 +91,8 @@ where return Err(Error::Mismatch); } }, - }) + }; + Ok(()) } } @@ -120,7 +121,7 @@ where type Error = R::Error; async fn verify(&self, cacao: &UcanCacao) -> Result<(), Self::Error> { - (&self.0).verify(cacao).await + self.0.verify(cacao).await } } diff --git a/src/v3/recap_cacao.rs b/src/v3/recap_cacao.rs index 8f182c3..5e35286 100644 --- a/src/v3/recap_cacao.rs +++ b/src/v3/recap_cacao.rs @@ -92,7 +92,7 @@ where let statement = siwe.statement.and_then(|s| { s.get(0..(s.len() - recap.as_ref().map(|r| r.to_statement().len()).unwrap_or(0))) .and_then(|s| { - if s.len() == 0 { + if s.is_empty() { None } else { Some(s.to_string()) @@ -233,7 +233,7 @@ mod time_conv { (unix, t_str.split_off(T_LEN)) } - const TZ_FORMAT: &'static [FormatItem<'static>] = + const TZ_FORMAT: &[FormatItem<'static>] = format_description!(version = 2, "[offset_hour]:[offset_minute]"); pub fn make_ts(unix: u64, z: &str) -> Result { @@ -246,7 +246,7 @@ mod time_conv { .to_offset(offset) .format(&Rfc3339)?; t_str.replace_range(T_LEN.., z); - Ok(t_str.parse()?) + t_str.parse() } #[cfg(test)] From 7d3d65990783b4c2a019b39a5c162a222ad051d5 Mon Sep 17 00:00:00 2001 From: chunningham Date: Wed, 27 Sep 2023 16:11:17 +0200 Subject: [PATCH 68/70] use generic for version to ensure correctness --- src/v3/common.rs | 64 +++++++++++++++++++++++++++++-------------- src/v3/mod.rs | 14 +++++----- src/v3/recap_cacao.rs | 45 ++++++++++++++++++++++++------ src/v3/ucan_cacao.rs | 10 ++++--- src/v3/version.rs | 24 +++++++--------- 5 files changed, 102 insertions(+), 55 deletions(-) diff --git a/src/v3/common.rs b/src/v3/common.rs index 718a286..4f9352f 100644 --- a/src/v3/common.rs +++ b/src/v3/common.rs @@ -1,5 +1,8 @@ use super::{ - recap_cacao::{Error as RecapError, RecapCacao, RecapFacts, RecapSignature, RecapVerify}, + recap_cacao::{ + version::SiweVersion, Error as RecapError, RecapCacao, RecapFacts, RecapSignature, + RecapVerify, + }, ucan_cacao::{Error as UcanError, UcanCacao, UcanFacts, UcanSignature}, Cacao, CacaoVerifier, }; @@ -9,6 +12,7 @@ use serde_json::Value; use siwe::Message; use ssi_ucan::{ jose::{self, Signature, VerificationError}, + version::SemanticVersion, Ucan, }; use varsig::{either::EitherSignature, VarSig}; @@ -22,7 +26,15 @@ pub enum CommonFacts { Ucan(UcanFacts), } -pub type CommonCacao = Cacao, NB>; +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Eq, PartialOrd, Hash)] +#[serde(untagged)] +pub enum CommonVersions { + Recap(SiweVersion), + Ucan(SemanticVersion), +} + +pub type CommonCacao = + Cacao, NB>; #[derive(Debug, Clone, PartialEq, Default)] pub struct CommonVerifier(T); @@ -74,9 +86,10 @@ impl From> for Error { #[cfg_attr(target_arch = "wasm32", async_trait(?Send))] #[cfg_attr(not(target_arch = "wasm32"), async_trait)] -impl<'r, NB, R, F> CacaoVerifier, NB> for CommonVerifier +impl<'r, NB, R, F> CacaoVerifier, NB> + for CommonVerifier where - R: 'r + Send + Sync + CacaoVerifier, NB>, + R: 'r + Send + Sync + CacaoVerifier, NB>, F: Send + Sync + for<'a> Deserialize<'a> + Serialize + Clone, NB: Send + Sync + for<'a> Deserialize<'a> + Serialize + Clone, { @@ -98,7 +111,7 @@ where #[cfg_attr(target_arch = "wasm32", async_trait(?Send))] #[cfg_attr(not(target_arch = "wasm32"), async_trait)] -impl CacaoVerifier for CommonVerifier +impl CacaoVerifier for CommonVerifier where R: Send + Sync, NB: Send + Sync + for<'a> Deserialize<'a> + Serialize + Clone, @@ -112,9 +125,10 @@ where #[cfg_attr(target_arch = "wasm32", async_trait(?Send))] #[cfg_attr(not(target_arch = "wasm32"), async_trait)] -impl<'r, NB, R, F> CacaoVerifier, NB> for CommonVerifier +impl<'r, NB, R, F> CacaoVerifier, NB> + for CommonVerifier where - R: 'r + Send + Sync + CacaoVerifier, NB>, + R: 'r + Send + Sync + CacaoVerifier, NB>, F: Send + Sync + for<'a> Deserialize<'a> + Serialize + Clone, NB: Send + Sync + for<'a> Deserialize<'a> + Serialize + Clone, { @@ -130,7 +144,7 @@ impl From> for CommonCacao { CommonCacao { issuer: recap.issuer, audience: recap.audience, - version: recap.version, + version: CommonVersions::Recap(recap.version), attenuations: recap.attenuations, nonce: recap.nonce, proof: recap.proof, @@ -148,7 +162,7 @@ impl From> for CommonCacao { CommonCacao { issuer: ucan.issuer, audience: ucan.audience, - version: ucan.version, + version: CommonVersions::Ucan(ucan.version), attenuations: ucan.attenuations, nonce: ucan.nonce, proof: ucan.proof, @@ -164,11 +178,15 @@ impl From> for CommonCacao { impl TryFrom> for RecapCacao { type Error = CommonCacao; fn try_from(cacao: CommonCacao) -> Result { - match (cacao.facts, cacao.signature.into_inner()) { - (Some(CommonFacts::Recap(facts)), EitherSignature::A(sig)) => Ok(RecapCacao { + match (cacao.facts, cacao.signature.into_inner(), cacao.version) { + ( + Some(CommonFacts::Recap(facts)), + EitherSignature::A(sig), + CommonVersions::Recap(version), + ) => Ok(RecapCacao { issuer: cacao.issuer, audience: cacao.audience, - version: cacao.version, + version, attenuations: cacao.attenuations, nonce: cacao.nonce, proof: cacao.proof, @@ -178,10 +196,10 @@ impl TryFrom> for RecapCacao { facts: Some(facts), signature: VarSig::new(sig), }), - (facts, sig) => Err(CommonCacao { + (facts, sig, version) => Err(CommonCacao { issuer: cacao.issuer, audience: cacao.audience, - version: cacao.version, + version, attenuations: cacao.attenuations, nonce: cacao.nonce, proof: cacao.proof, @@ -198,11 +216,15 @@ impl TryFrom> for RecapCacao { impl TryFrom> for UcanCacao { type Error = CommonCacao; fn try_from(cacao: CommonCacao) -> Result { - match (cacao.facts, cacao.signature.into_inner()) { - (Some(CommonFacts::Ucan(facts)), EitherSignature::B(sig)) => Ok(UcanCacao { + match (cacao.facts, cacao.signature.into_inner(), cacao.version) { + ( + Some(CommonFacts::Ucan(facts)), + EitherSignature::B(sig), + CommonVersions::Ucan(version), + ) => Ok(UcanCacao { issuer: cacao.issuer, audience: cacao.audience, - version: cacao.version, + version, attenuations: cacao.attenuations, nonce: cacao.nonce, proof: cacao.proof, @@ -212,10 +234,10 @@ impl TryFrom> for UcanCacao { facts: Some(facts), signature: VarSig::new(sig), }), - (None, EitherSignature::B(sig)) => Ok(UcanCacao { + (None, EitherSignature::B(sig), CommonVersions::Ucan(version)) => Ok(UcanCacao { issuer: cacao.issuer, audience: cacao.audience, - version: cacao.version, + version, attenuations: cacao.attenuations, nonce: cacao.nonce, proof: cacao.proof, @@ -225,10 +247,10 @@ impl TryFrom> for UcanCacao { facts: None, signature: VarSig::new(sig), }), - (facts, sig) => Err(CommonCacao { + (facts, sig, version) => Err(CommonCacao { issuer: cacao.issuer, audience: cacao.audience, - version: cacao.version, + version, attenuations: cacao.attenuations, nonce: cacao.nonce, proof: cacao.proof, diff --git a/src/v3/mod.rs b/src/v3/mod.rs index d9f6bab..1634230 100644 --- a/src/v3/mod.rs +++ b/src/v3/mod.rs @@ -15,13 +15,13 @@ use varsig::{VarSig, VarSigTrait}; #[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Eq, Hash)] #[serde(deny_unknown_fields)] -pub struct Cacao { +pub struct Cacao { #[serde(rename = "iss")] issuer: MultiDid, #[serde(rename = "aud")] audience: MultiDid, #[serde(rename = "v")] - version: String, + version: V, #[serde(rename = "att")] attenuations: Capabilities, #[serde(rename = "nnc", skip_serializing_if = "Option::is_none", default)] @@ -44,7 +44,7 @@ pub struct Cacao { signature: VarSig, } -impl Cacao { +impl Cacao { pub fn issuer(&self) -> &MultiDid { &self.issuer } @@ -97,9 +97,9 @@ impl Cacao { }) } - pub async fn verify(&self, verifier: &V) -> Result<(), V::Error> + pub async fn verify(&self, verifier: &VE) -> Result<(), VE::Error> where - V: CacaoVerifier, + VE: CacaoVerifier, NB: Send + Sync, { verifier.verify(self).await @@ -108,8 +108,8 @@ impl Cacao { #[cfg_attr(target_arch = "wasm32", async_trait(?Send))] #[cfg_attr(not(target_arch = "wasm32"), async_trait)] -pub trait CacaoVerifier { +pub trait CacaoVerifier { type Error: std::error::Error; - async fn verify(&self, cacao: &Cacao) -> Result<(), Self::Error>; + async fn verify(&self, cacao: &Cacao) -> Result<(), Self::Error>; } diff --git a/src/v3/recap_cacao.rs b/src/v3/recap_cacao.rs index 5e35286..aa992f2 100644 --- a/src/v3/recap_cacao.rs +++ b/src/v3/recap_cacao.rs @@ -14,9 +14,10 @@ use varsig::{ common::{Ethereum, EIP191_ENCODING}, VarSig, }; +use version::SiweVersion; pub type RecapSignature = Ethereum; -pub type RecapCacao = Cacao; +pub type RecapCacao = Cacao; #[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Eq, PartialOrd, Hash)] #[serde(deny_unknown_fields)] @@ -57,8 +58,6 @@ pub enum Error { ExtraDidComponents, #[error("The 'facts' field is required for Recap Cacaos")] MissingFacts, - #[error("Incorrect Version, expected '1', found: {0}")] - IncorrectVersion(String), #[error("SIWE messages must have a nonce")] MissingNonce, #[error("SIWE messages must have a issuance timestamp")] @@ -111,7 +110,9 @@ where None, ), audience: MultiDid::from_str(siwe.uri.as_str())?, - version: (siwe.version as u8).to_string(), + version: match siwe.version { + siwe::Version::V1 => SiweVersion::V1, + }, attenuations, nonce: Some(siwe.nonce), proof: Some(proof), @@ -156,10 +157,9 @@ where address, statement: facts.statement, uri: cacao.audience.to_string().parse()?, - version: cacao - .version - .parse() - .map_err(|_| Error::IncorrectVersion(cacao.version))?, + version: match cacao.version { + SiweVersion::V1 => siwe::Version::V1, + }, chain_id, nonce: cacao.nonce.ok_or(Error::MissingNonce)?, issued_at: make_ts(cacao.issued_at.ok_or(Error::MissingIat)?, &facts.iat_info)?, @@ -201,7 +201,7 @@ pub struct RecapVerify(()); #[cfg_attr(target_arch = "wasm32", async_trait(?Send))] #[cfg_attr(not(target_arch = "wasm32"), async_trait)] -impl CacaoVerifier for RecapVerify +impl CacaoVerifier for RecapVerify where NB: Send + Sync + Serialize + Clone, { @@ -214,6 +214,33 @@ where } } +pub mod version { + use serde::{de::Deserializer, ser::Serializer, Deserialize, Serialize}; + + #[derive(Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug, Default)] + pub enum SiweVersion { + #[default] + V1 = 1, + } + + impl Serialize for SiweVersion { + fn serialize(&self, serializer: S) -> Result { + serializer.serialize_str("1") + } + } + + impl<'de> Deserialize<'de> for SiweVersion { + fn deserialize>(deserializer: D) -> Result { + let s = String::deserialize(deserializer)?; + if s == "1" { + Ok(Self::V1) + } else { + Err(serde::de::Error::custom("invalid version")) + } + } + } +} + mod time_conv { use siwe::TimeStamp; use time::{ diff --git a/src/v3/ucan_cacao.rs b/src/v3/ucan_cacao.rs index 4a362d0..1a83da0 100644 --- a/src/v3/ucan_cacao.rs +++ b/src/v3/ucan_cacao.rs @@ -6,7 +6,9 @@ use serde_json::Value; use ssi_dids::did_resolve::DIDResolver; use ssi_ucan::{ jose::{self, Signature, VerificationError}, - jwt, Payload, Ucan, + jwt, + version::SemanticVersion, + Payload, Ucan, }; use std::collections::BTreeMap; use std::str::FromStr; @@ -17,7 +19,7 @@ use varsig::{ pub type UcanSignature = JoseSig; pub type UcanFacts = BTreeMap; -pub type UcanCacao = Cacao, NB>; +pub type UcanCacao = Cacao, NB>; #[derive(thiserror::Error, Debug)] pub enum Error { @@ -35,7 +37,7 @@ impl From for Error { #[cfg_attr(target_arch = "wasm32", async_trait(?Send))] #[cfg_attr(not(target_arch = "wasm32"), async_trait)] -impl CacaoVerifier, NB> for &R +impl CacaoVerifier, NB> for &R where R: DIDResolver, F: Send + Sync + for<'a> Deserialize<'a> + Serialize + Clone, @@ -57,7 +59,7 @@ impl TryFrom> for UcanCacao { Ok(Self { issuer: MultiDid::from_str(&payload.issuer)?, audience: MultiDid::from_str(&payload.audience)?, - version: "0.2.0".to_string(), + version: SemanticVersion, attenuations: payload.capabilities, nonce: payload.nonce, proof: payload.proof, diff --git a/src/v3/version.rs b/src/v3/version.rs index 9a6dec0..38c4547 100644 --- a/src/v3/version.rs +++ b/src/v3/version.rs @@ -1,25 +1,21 @@ -use serde_with::{DeserializeFromStr, SerializeDisplay}; -use thiserror::Error; +use serde::{de::Deserializer, ser::Serializer, Deserialize, Serialize}; -#[derive(Debug, Clone, PartialEq, SerializeDisplay, DeserializeFromStr)] +#[derive(Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug, Default)] pub struct Version3; -impl std::fmt::Display for Version3 { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> { - write!(f, "3") +impl Serialize for SiweVersion { + fn serialize(&self, serializer: S) -> Result { + serializer.serialize_str("3") } } -#[derive(Error, Debug, Copy, PartialEq)] -pub struct VersionErr; - -impl std::str::FromStr for Version3 { - type Err = VersionErr; - fn from_str(s: &str) -> Result { +impl<'de> Deserialize<'de> for SiweVersion { + fn deserialize>(deserializer: D) -> Result { + let s = String::deserialize(deserializer)?; if s == "3" { - Ok(Self) + Ok(Self::V1) } else { - Err(VersionErr) + Err(serde::de::Error::custom("invalid version")) } } } From fbfd79f1c2fbd50e2a328be3d30a44846c87427f Mon Sep 17 00:00:00 2001 From: chunningham Date: Wed, 27 Sep 2023 16:14:22 +0200 Subject: [PATCH 69/70] add payload type for builders --- src/v3/mod.rs | 1 + src/v3/payload.rs | 151 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 152 insertions(+) create mode 100644 src/v3/payload.rs diff --git a/src/v3/mod.rs b/src/v3/mod.rs index 1634230..19eb397 100644 --- a/src/v3/mod.rs +++ b/src/v3/mod.rs @@ -5,6 +5,7 @@ use std::fmt::Debug; use ucan_capabilities_object::Capabilities; pub mod common; +pub mod payload; pub mod recap_cacao; pub mod ucan_cacao; diff --git a/src/v3/payload.rs b/src/v3/payload.rs new file mode 100644 index 0000000..0ed41b1 --- /dev/null +++ b/src/v3/payload.rs @@ -0,0 +1,151 @@ +use super::Cacao; +use libipld::{cid::Cid, Ipld}; +use multidid::MultiDid; +use serde::{Deserialize, Serialize}; +use std::fmt::Debug; +use ucan_capabilities_object::Capabilities; +use varsig::VarSig; + +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Eq, Hash)] +#[serde(deny_unknown_fields)] +pub struct Payload { + #[serde(rename = "iss")] + pub issuer: MultiDid, + #[serde(rename = "aud")] + pub audience: MultiDid, + #[serde(rename = "v")] + version: V, + #[serde(rename = "att")] + pub attenuations: Capabilities, + #[serde(rename = "nnc", skip_serializing_if = "Option::is_none", default)] + pub nonce: Option, + #[serde(rename = "prf", skip_serializing_if = "Option::is_none", default)] + pub proof: Option>, + #[serde(rename = "iat", skip_serializing_if = "Option::is_none", default)] + pub issued_at: Option, + #[serde(rename = "nbf", skip_serializing_if = "Option::is_none", default)] + pub not_before: Option, + #[serde(rename = "exp", skip_serializing_if = "Option::is_none", default)] + pub expiration: Option, + #[serde( + rename = "fct", + skip_serializing_if = "Option::is_none", + default = "Option::default" + )] + pub facts: Option, +} + +impl Payload { + pub fn capabilities(&mut self) -> &mut Capabilities { + &mut self.attenuations + } + + pub fn nonce(&mut self, nonce: String) -> &mut Self { + self.nonce = Some(nonce); + self + } + + pub fn proof(&mut self, proof: Vec) -> &mut Self { + self.proof = Some(proof); + self + } + + pub fn issued_at(&mut self, iat: u64) -> &mut Self { + self.issued_at = Some(iat); + self + } + + pub fn not_before(&mut self, nbf: u64) -> &mut Self { + self.not_before = Some(nbf); + self + } + + pub fn expiration(&mut self, exp: u64) -> &mut Self { + self.expiration = Some(exp); + self + } + + pub fn facts(&mut self, facts: F) -> &mut Self { + self.facts = Some(facts); + self + } + + pub fn sign(self, sig: S) -> Cacao { + Cacao { + issuer: self.issuer, + audience: self.audience, + version: self.version, + attenuations: self.attenuations, + nonce: self.nonce, + proof: self.proof, + issued_at: self.issued_at, + not_before: self.not_before, + expiration: self.expiration, + facts: self.facts, + signature: VarSig::new(sig), + } + } +} + +#[derive(Debug, Clone, PartialEq, Serialize, Eq, Hash)] +#[serde(deny_unknown_fields)] +pub(crate) struct BorrowedPayload<'a, V, F, NB> { + #[serde(rename = "iss")] + issuer: &'a MultiDid, + #[serde(rename = "aud")] + audience: &'a MultiDid, + #[serde(rename = "v")] + version: &'a V, + #[serde(rename = "att")] + attenuations: &'a Capabilities, + #[serde(rename = "nnc", skip_serializing_if = "Option::is_none", default)] + nonce: &'a Option, + #[serde(rename = "prf", skip_serializing_if = "Option::is_none", default)] + proof: &'a Option>, + #[serde(rename = "iat", skip_serializing_if = "Option::is_none", default)] + issued_at: &'a Option, + #[serde(rename = "nbf", skip_serializing_if = "Option::is_none", default)] + not_before: &'a Option, + #[serde(rename = "exp", skip_serializing_if = "Option::is_none", default)] + expiration: &'a Option, + #[serde( + rename = "fct", + skip_serializing_if = "Option::is_none", + default = "Option::default" + )] + facts: &'a Option, +} + +impl<'a, V, S, F, NB> From<&'a Cacao> for BorrowedPayload<'a, V, F, NB> { + fn from(cacao: &'a Cacao) -> Self { + Self { + issuer: &cacao.issuer, + audience: &cacao.audience, + version: &cacao.version, + attenuations: &cacao.attenuations, + nonce: &cacao.nonce, + proof: &cacao.proof, + issued_at: &cacao.issued_at, + not_before: &cacao.not_before, + expiration: &cacao.expiration, + facts: &cacao.facts, + } + } +} + +impl<'a, V, F, NB> From<&'a Payload> for BorrowedPayload<'a, V, F, NB> { + fn from(payload: &'a Payload) -> Self { + Self { + issuer: &payload.issuer, + audience: &payload.audience, + version: &payload.version, + attenuations: &payload.attenuations, + nonce: &payload.nonce, + proof: &payload.proof, + issued_at: &payload.issued_at, + not_before: &payload.not_before, + expiration: &payload.expiration, + facts: &payload.facts, + } + } +} From a68b398c5cc89e20d42c9d9f2120d2ff27312d81 Mon Sep 17 00:00:00 2001 From: chunningham Date: Wed, 27 Sep 2023 16:20:01 +0200 Subject: [PATCH 70/70] add builders --- src/v3/payload.rs | 36 +++++++++++++++++------------------- src/v3/recap_cacao.rs | 26 +++++++++++++++++++++++++- src/v3/ucan_cacao.rs | 30 +++++++++++++++++++++++++++--- src/v3/version.rs | 4 ++-- 4 files changed, 71 insertions(+), 25 deletions(-) diff --git a/src/v3/payload.rs b/src/v3/payload.rs index 0ed41b1..2596ea5 100644 --- a/src/v3/payload.rs +++ b/src/v3/payload.rs @@ -1,14 +1,13 @@ use super::Cacao; -use libipld::{cid::Cid, Ipld}; +use libipld::cid::Cid; use multidid::MultiDid; use serde::{Deserialize, Serialize}; use std::fmt::Debug; use ucan_capabilities_object::Capabilities; -use varsig::VarSig; #[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Eq, Hash)] #[serde(deny_unknown_fields)] -pub struct Payload { +pub struct Payload { #[serde(rename = "iss")] pub issuer: MultiDid, #[serde(rename = "aud")] @@ -36,6 +35,21 @@ pub struct Payload { } impl Payload { + pub(crate) fn new(issuer: MultiDid, audience: MultiDid, version: V) -> Self { + Self { + issuer, + audience, + version, + attenuations: Capabilities::default(), + nonce: None, + proof: None, + issued_at: None, + not_before: None, + expiration: None, + facts: None, + } + } + pub fn capabilities(&mut self) -> &mut Capabilities { &mut self.attenuations } @@ -69,22 +83,6 @@ impl Payload { self.facts = Some(facts); self } - - pub fn sign(self, sig: S) -> Cacao { - Cacao { - issuer: self.issuer, - audience: self.audience, - version: self.version, - attenuations: self.attenuations, - nonce: self.nonce, - proof: self.proof, - issued_at: self.issued_at, - not_before: self.not_before, - expiration: self.expiration, - facts: self.facts, - signature: VarSig::new(sig), - } - } } #[derive(Debug, Clone, PartialEq, Serialize, Eq, Hash)] diff --git a/src/v3/recap_cacao.rs b/src/v3/recap_cacao.rs index aa992f2..401a442 100644 --- a/src/v3/recap_cacao.rs +++ b/src/v3/recap_cacao.rs @@ -1,4 +1,4 @@ -use super::{Cacao, CacaoVerifier}; +use super::{payload::Payload, Cacao, CacaoVerifier}; use async_trait::async_trait; use http::uri::Authority; use iri_string::types::UriString; @@ -214,6 +214,30 @@ where } } +impl RecapCacao { + pub fn builder(iss: MultiDid, aud: MultiDid) -> Payload { + Payload::new(iss, aud, SiweVersion::V1) + } +} + +impl Payload { + pub fn sign(self, sig: RecapSignature) -> RecapCacao { + Cacao { + issuer: self.issuer, + audience: self.audience, + version: SiweVersion::V1, + attenuations: self.attenuations, + nonce: self.nonce, + proof: self.proof, + issued_at: self.issued_at, + not_before: self.not_before, + expiration: self.expiration, + facts: self.facts, + signature: VarSig::new(sig), + } + } +} + pub mod version { use serde::{de::Deserializer, ser::Serializer, Deserialize, Serialize}; diff --git a/src/v3/ucan_cacao.rs b/src/v3/ucan_cacao.rs index 1a83da0..4351d33 100644 --- a/src/v3/ucan_cacao.rs +++ b/src/v3/ucan_cacao.rs @@ -1,4 +1,4 @@ -use super::{Cacao, CacaoVerifier}; +use super::{payload::Payload, Cacao, CacaoVerifier}; use async_trait::async_trait; use multidid::MultiDid; use serde::{Deserialize, Serialize}; @@ -8,7 +8,7 @@ use ssi_ucan::{ jose::{self, Signature, VerificationError}, jwt, version::SemanticVersion, - Payload, Ucan, + Payload as UcanPayload, Ucan, }; use std::collections::BTreeMap; use std::str::FromStr; @@ -89,7 +89,7 @@ impl From> for Ucan { JoseSig::Rsa256(s) => Signature::RS256(s.into_inner()), JoseSig::Rsa512(s) => Signature::RS512(s.into_inner()), }; - let mut payload = Payload::new(cacao.issuer.to_string(), cacao.audience.to_string()); + let mut payload = UcanPayload::new(cacao.issuer.to_string(), cacao.audience.to_string()); payload.capabilities = cacao.attenuations; payload.nonce = cacao.nonce; payload.proof = cacao.proof; @@ -100,3 +100,27 @@ impl From> for Ucan { payload.sign(signature) } } + +impl UcanCacao { + pub fn builder(iss: MultiDid, aud: MultiDid) -> Payload { + Payload::new(iss, aud, SemanticVersion) + } +} + +impl Payload, NB> { + pub fn sign(self, sig: UcanSignature) -> UcanCacao { + Cacao { + issuer: self.issuer, + audience: self.audience, + version: SemanticVersion, + attenuations: self.attenuations, + nonce: self.nonce, + proof: self.proof, + issued_at: self.issued_at, + not_before: self.not_before, + expiration: self.expiration, + facts: self.facts, + signature: VarSig::new(sig), + } + } +} diff --git a/src/v3/version.rs b/src/v3/version.rs index 38c4547..9662f1f 100644 --- a/src/v3/version.rs +++ b/src/v3/version.rs @@ -3,13 +3,13 @@ use serde::{de::Deserializer, ser::Serializer, Deserialize, Serialize}; #[derive(Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug, Default)] pub struct Version3; -impl Serialize for SiweVersion { +impl Serialize for Version3 { fn serialize(&self, serializer: S) -> Result { serializer.serialize_str("3") } } -impl<'de> Deserialize<'de> for SiweVersion { +impl<'de> Deserialize<'de> for Version3 { fn deserialize>(deserializer: D) -> Result { let s = String::deserialize(deserializer)?; if s == "3" {