1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
use once_cell::sync::Lazy;

use regex::Regex;

use crate::base58;
#[cfg(feature = "ed25519")]
use crate::keys::{KeyType, PrivateKey, VerKey};
use crate::{Qualifiable, Validatable, ValidationError};

/// The default identifier DID used when submitting ledger read requests
pub static DEFAULT_LIBINDY_DID: Lazy<DidValue> =
    Lazy::new(|| DidValue::new("LibindyDid111111111111", None));

/// Create a new DID with an optional seed value
#[cfg(feature = "ed25519")]
pub fn generate_did(
    seed: Option<&[u8]>,
) -> Result<(ShortDidValue, PrivateKey, VerKey), crate::ConversionError> {
    let sk = match seed {
        Some(seed) => PrivateKey::from_seed(seed)?,
        None => PrivateKey::generate(Some(KeyType::ED25519))?,
    };
    let pk = sk.public_key()?;
    let did = base58::encode(&pk.as_ref()[..16]);
    Ok((ShortDidValue::from(did), sk, pk))
}

/// A wrapper providing validation for DID methods
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct DidMethod(pub String);

impl Validatable for DidMethod {
    fn validate(&self) -> Result<(), ValidationError> {
        static REGEX_METHOD_NAME: Lazy<Regex> = Lazy::new(|| Regex::new("^[a-z0-9]+$").unwrap());

        if !REGEX_METHOD_NAME.is_match(&self.0) {
            return Err(invalid!(
                "Invalid default name: {}. It does not match the DID method name format.",
                self.0
            ));
        }
        Ok(())
    }
}

qualifiable_type!(DidValue, "A qualifiable DID type");

impl Qualifiable for DidValue {
    fn prefix() -> &'static str {
        "did"
    }
}

impl DidValue {
    pub fn new(did: &str, method: Option<&str>) -> DidValue {
        DidValue::combine(method, did)
    }

    pub fn to_short(&self) -> ShortDidValue {
        ShortDidValue(self.to_unqualified().0)
    }

    pub fn is_abbreviatable(&self) -> bool {
        match self.get_method() {
            Some(ref method) if method.starts_with("sov") => true,
            Some(_) => false,
            None => true,
        }
    }
}

impl Validatable for DidValue {
    fn validate(&self) -> Result<(), ValidationError> {
        if self.is_fully_qualified() {
            // pass
        } else {
            let did = base58::decode(&self.0).map_err(ValidationError::from_msg)?;
            if did.len() != 16 && did.len() != 32 {
                return Err(invalid!(
                    "Trying to use DID with unexpected length: {}. \
                    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()
                ));
            }
        }
        Ok(())
    }
}

/// A short DID with no prefix or method
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct ShortDidValue(pub String);

impl From<String> for ShortDidValue {
    fn from(val: String) -> Self {
        Self(val)
    }
}

impl std::ops::Deref for ShortDidValue {
    type Target = str;
    fn deref(&self) -> &str {
        &self.0
    }
}

impl ShortDidValue {
    /// Convert a short DID value to a qualified DID
    pub fn qualify(&self, method: Option<String>) -> DidValue {
        DidValue::combine(method.as_ref().map(String::as_str), &self)
    }
}

impl Validatable for ShortDidValue {
    fn validate(&self) -> Result<(), ValidationError> {
        let did = base58::decode(&self.0).map_err(ValidationError::from_msg)?;
        if did.len() != 16 && did.len() != 32 {
            return Err(invalid!(
                "Trying to use DID with unexpected length: {}. \
                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()
            ));
        }
        Ok(())
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::keys::EncodedVerKey;

    #[test]
    fn generate_abbreviate() {
        let (did, _sk, vk) = generate_did(None).unwrap();
        let vk_b58 = vk.as_base58().unwrap();
        let vk_short = vk_b58.abbreviated_for_did(&did).unwrap();
        assert_eq!(vk_short.chars().next(), Some('~'));
        let vk_long = EncodedVerKey::from_did_and_verkey(&did, &vk_short).unwrap();
        assert_eq!(vk_long, vk_b58);
        let cmp_vk = vk_long.decode().unwrap();
        assert_eq!(vk, cmp_vk);
    }
}