ockam_identity/models/
credential_and_purpose_key.rs

1use minicbor::{CborLen, Decode, Encode};
2use ockam_core::compat::string::String;
3use ockam_core::compat::vec::Vec;
4use ockam_core::errcode::{Kind, Origin};
5use ockam_core::{cbor_encode_preallocate, Decodable, Encodable, Encoded, Error, Message, Result};
6
7use crate::alloc::string::ToString;
8use crate::models::{Credential, CredentialData, PurposeKeyAttestation};
9use crate::TimestampInSeconds;
10
11/// [`Credential`] and the corresponding [`PurposeKeyAttestation`] that was used to issue that
12/// [`Credential`] and will be used to verify it
13#[derive(Clone, Debug, PartialEq, Eq, Encode, Decode, CborLen, Message)]
14#[rustfmt::skip]
15pub struct CredentialAndPurposeKey {
16    /// [`Credential`]
17    #[n(0)] pub credential: Credential,
18    /// Corresponding [`PurposeKeyAttestation`] that was used to issue that
19    /// [`Credential`] and will be used to verify it
20    #[n(1)] pub purpose_key_attestation: PurposeKeyAttestation,
21}
22
23impl Encodable for CredentialAndPurposeKey {
24    fn encode(self) -> Result<Encoded> {
25        cbor_encode_preallocate(self)
26    }
27}
28
29impl Decodable for CredentialAndPurposeKey {
30    fn decode(e: &[u8]) -> Result<Self> {
31        Ok(minicbor::decode(e)?)
32    }
33}
34
35impl CredentialAndPurposeKey {
36    /// Encode the credential as a hex String
37    pub fn encode_as_string(&self) -> Result<String> {
38        Ok(hex::encode(self.encode_as_cbor_bytes()?))
39    }
40
41    /// Encode the credential as a CBOR bytes
42    pub fn encode_as_cbor_bytes(&self) -> Result<Vec<u8>> {
43        cbor_encode_preallocate(self)
44    }
45
46    /// Decode the credential from bytes
47    pub fn decode_from_cbor_bytes(bytes: &[u8]) -> Result<CredentialAndPurposeKey> {
48        Ok(minicbor::decode(bytes)?)
49    }
50
51    /// Decode the credential from an hex string
52    pub fn decode_from_string(as_hex: &str) -> Result<CredentialAndPurposeKey> {
53        let hex_decoded = hex::decode(as_hex.as_bytes())
54            .map_err(|e| Error::new(Origin::Api, Kind::Serialization, e.to_string()))?;
55        Self::decode_from_cbor_bytes(&hex_decoded)
56    }
57
58    /// Return the encoded credential data
59    pub fn get_credential_data(&self) -> Result<CredentialData> {
60        self.credential.get_credential_data()
61    }
62
63    /// Return the `expires_at` field
64    pub fn get_expires_at(&self) -> Result<TimestampInSeconds> {
65        Ok(self.get_credential_data()?.expires_at)
66    }
67}
68
69#[cfg(test)]
70mod tests {
71    use std::time::Duration;
72
73    use crate::identities;
74    use crate::models::CredentialSchemaIdentifier;
75    use crate::utils::AttributesBuilder;
76
77    use super::*;
78
79    #[tokio::test]
80    async fn test_encode_decode_as_bytes() -> Result<()> {
81        let credential = create_credential().await?;
82        let decoded = CredentialAndPurposeKey::decode_from_cbor_bytes(
83            &credential.encode_as_cbor_bytes().unwrap(),
84        );
85        assert!(decoded.is_ok());
86        assert_eq!(decoded.unwrap(), credential);
87
88        Ok(())
89    }
90
91    #[tokio::test]
92    async fn test_encode_decode_as_string() -> Result<()> {
93        let credential = create_credential().await?;
94        let decoded =
95            CredentialAndPurposeKey::decode_from_string(&credential.encode_as_string().unwrap());
96        assert!(decoded.is_ok());
97        assert_eq!(decoded.unwrap(), credential);
98
99        Ok(())
100    }
101
102    // HELPERS
103    async fn create_credential() -> Result<CredentialAndPurposeKey> {
104        let identities = identities().await?;
105        let issuer = identities.identities_creation().create_identity().await?;
106        let subject = identities.identities_creation().create_identity().await?;
107
108        let attributes = AttributesBuilder::with_schema(CredentialSchemaIdentifier(1))
109            .with_attribute("name".as_bytes().to_vec(), b"value".to_vec())
110            .build();
111
112        identities
113            .credentials()
114            .credentials_creation()
115            .issue_credential(&issuer, &subject, attributes, Duration::from_secs(1))
116            .await
117    }
118}