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
13pub static DEFAULT_LIBINDY_DID: Lazy<DidValue> =
15 Lazy::new(|| DidValue::new("LibindyDid111111111111", None));
16
17#[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#[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 } 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#[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 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}