indy_data_types/
did.rs

1use once_cell::sync::Lazy;
2
3use regex::Regex;
4#[cfg(feature = "ed25519")]
5use sha2::{Digest, Sha256};
6
7#[cfg(feature = "ed25519")]
8use crate::keys::{KeyType, PrivateKey, VerKey};
9use crate::qualifiable::{qualifiable_type, Qualifiable};
10use crate::utils::base58;
11use crate::{Validatable, ValidationError};
12
13/// The default identifier DID used when submitting ledger read requests
14pub static DEFAULT_LIBINDY_DID: Lazy<DidValue> =
15    Lazy::new(|| DidValue::new("LibindyDid111111111111", None));
16
17/// Create a new DID with an optional seed value
18/// Version determines version of self-certification to be used
19/// 1 (default) = did:sov
20/// 2 = did:indy
21#[cfg(feature = "ed25519")]
22pub fn generate_did(
23    seed: Option<&[u8]>,
24    version: Option<usize>,
25) -> Result<(ShortDidValue, PrivateKey, VerKey), crate::ConversionError> {
26    let sk = match seed {
27        Some(seed) => PrivateKey::from_seed(seed)?,
28        None => PrivateKey::generate(Some(KeyType::ED25519))?,
29    };
30
31    let pk = sk.public_key()?;
32    let did = match version {
33        Some(1) | None => Ok(base58::encode(&pk.as_ref()[..16])),
34        Some(2) => {
35            let mut hasher = Sha256::new();
36            Digest::update(&mut hasher, pk.as_ref());
37            let hash = hasher.finalize();
38            Ok(base58::encode(&hash[..16]))
39        }
40        _ => Err("Version must be one of 1,2"),
41    }?;
42    Ok((ShortDidValue::from(did), sk, pk))
43}
44
45/// A wrapper providing validation for DID methods
46#[derive(Debug, Clone, PartialEq, Eq, Hash)]
47#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
48pub struct DidMethod(pub String);
49
50impl Validatable for DidMethod {
51    fn validate(&self) -> Result<(), ValidationError> {
52        static REGEX_METHOD_NAME: Lazy<Regex> = Lazy::new(|| Regex::new("^[a-z0-9]+$").unwrap());
53
54        if !REGEX_METHOD_NAME.is_match(&self.0) {
55            return Err(invalid!(
56                "Invalid default name: {}. It does not match the DID method name format.",
57                self.0
58            ));
59        }
60        Ok(())
61    }
62}
63
64qualifiable_type!(DidValue, "A qualifiable DID type");
65
66impl Qualifiable for DidValue {
67    fn prefix() -> &'static str {
68        "did"
69    }
70}
71
72impl DidValue {
73    pub fn new(did: &str, method: Option<&str>) -> DidValue {
74        DidValue::combine(method, did)
75    }
76
77    pub fn to_short(&self) -> ShortDidValue {
78        ShortDidValue(self.to_unqualified().0)
79    }
80
81    pub fn is_abbreviatable(&self) -> bool {
82        match self.get_method() {
83            Some(method) if method.starts_with("sov") => true,
84            Some(_) => false,
85            None => true,
86        }
87    }
88}
89
90impl Validatable for DidValue {
91    fn validate(&self) -> Result<(), ValidationError> {
92        if self.is_fully_qualified() {
93            // pass
94        } else {
95            let did = base58::decode(&self.0).map_err(ValidationError::from_msg)?;
96            if did.len() != 16 && did.len() != 32 {
97                return Err(invalid!(
98                    "Trying to use DID with unexpected length: {}. \
99                    The 16- or 32-byte number upon which a DID is based should be 22/23 or 44/45 bytes when encoded as base58.", did.len()
100                ));
101            }
102        }
103        Ok(())
104    }
105}
106
107/// A short DID with no prefix or method
108#[derive(Debug, Clone, PartialEq, Eq, Hash)]
109#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
110pub struct ShortDidValue(pub String);
111
112impl From<String> for ShortDidValue {
113    fn from(val: String) -> Self {
114        Self(val)
115    }
116}
117
118impl std::ops::Deref for ShortDidValue {
119    type Target = str;
120    fn deref(&self) -> &str {
121        &self.0
122    }
123}
124
125impl ShortDidValue {
126    /// Convert a short DID value to a qualified DID
127    pub fn qualify(&self, method: Option<String>) -> DidValue {
128        DidValue::combine(method.as_deref(), self)
129    }
130}
131
132impl Validatable for ShortDidValue {
133    fn validate(&self) -> Result<(), ValidationError> {
134        let did = base58::decode(&self.0).map_err(ValidationError::from_msg)?;
135        if did.len() != 16 && did.len() != 32 {
136            return Err(invalid!(
137                "Trying to use DID with unexpected length: {}. \
138                The 16- or 32-byte number upon which a DID is based should be 22/23 or 44/45 bytes when encoded as base58.", did.len()
139            ));
140        }
141        Ok(())
142    }
143}
144
145#[cfg(all(test, feature = "ed25519"))]
146mod tests {
147    use super::*;
148    use crate::keys::EncodedVerKey;
149
150    #[test]
151    fn generate_abbreviate() {
152        let (did, _sk, vk) = generate_did(None, None).unwrap();
153        let vk_b58 = vk.as_base58().unwrap();
154        let vk_short = vk_b58.abbreviated_for_did(&did).unwrap();
155        assert_eq!(vk_short.chars().next(), Some('~'));
156        let vk_long = EncodedVerKey::from_did_and_verkey(&did, &vk_short).unwrap();
157        assert_eq!(vk_long, vk_b58);
158        let cmp_vk = vk_long.decode().unwrap();
159        assert_eq!(vk, cmp_vk);
160    }
161}