indy_utils/
did.rs

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