Skip to content
Snippets Groups Projects
Select Git revision
  • bbbb18f8cb0ad248a853c93f5924a2d0d3863d43
  • master default protected
  • chrome-manifest-v3
  • feature/migrate-cordova-13
  • feat/improve-network-scan
  • feat/force-migration-check
  • develop
  • feature/encrypted_comment
  • feature/android_api_19
  • gitlab_migration_1
  • rml8
  • v1.7.15-rc1
  • v1.7.14
  • v1.7.13
  • v1.7.12
  • v1.7.11
  • v1.7.10
  • v1.7.9
  • v1.7.8
  • v1.7.7
  • v1.7.6
  • v1.7.5
  • v1.7.4
  • v1.7.3
  • v1.7.2
  • v1.7.1
  • v1.7.0
  • v1.7.0-rc2
  • v1.7.0-rc1
  • v1.6.12
  • v1.6.11
31 results

app.js

Blame
  • identity.rs 8.47 KiB
    //  Copyright (C) 2018  The Duniter Project Developers.
    //
    // This program is free software: you can redistribute it and/or modify
    // it under the terms of the GNU Affero General Public License as
    // published by the Free Software Foundation, either version 3 of the
    // License, or (at your option) any later version.
    //
    // This program is distributed in the hope that it will be useful,
    // but WITHOUT ANY WARRANTY; without even the implied warranty of
    // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    // GNU Affero General Public License for more details.
    //
    // You should have received a copy of the GNU Affero General Public License
    // along with this program.  If not, see <https://www.gnu.org/licenses/>.
    
    //! Wrappers around Identity documents.
    
    use duniter_crypto::keys::{PublicKey, ed25519};
    use regex::Regex;
    
    use Blockstamp;
    use blockchain::{BlockchainProtocol, Document, DocumentBuilder, IntoSpecializedDocument};
    use blockchain::v10::documents::{StandardTextDocumentParser, TextDocument, TextDocumentBuilder,
                                     V10Document, V10DocumentParsingError};
    
    lazy_static! {
        static ref IDENTITY_REGEX: Regex = Regex::new(
            "^Issuer: (?P<issuer>[1-9A-Za-z][^OIl]{43,44})\nUniqueID: (?P<uid>[[:alnum:]_-]+)\nTimestamp: (?P<blockstamp>[0-9]+-[0-9A-F]{64})\n$"
        ).unwrap();
    }
    
    /// Wrap an Identity document.
    ///
    /// Must be created by parsing a text document or using a builder.
    #[derive(Debug, Clone)]
    pub struct IdentityDocument {
        /// Document as text.
        ///
        /// Is used to check signatures, and other values
        /// must be extracted from it.
        text: String,
    
        /// Currency.
        currency: String,
        /// Unique ID
        unique_id: String,
        /// Blockstamp
        blockstamp: Blockstamp,
        /// Document issuer (there should be only one).
        issuers: Vec<ed25519::PublicKey>,
        /// Document signature (there should be only one).
        signatures: Vec<ed25519::Signature>,
    }
    
    impl IdentityDocument {
        /// Unique ID
        pub fn unique_id(&self) -> &str {
            &self.unique_id
        }
    }
    
    impl Document for IdentityDocument {
        type PublicKey = ed25519::PublicKey;
        type CurrencyType = str;
    
        fn version(&self) -> u16 {
            10
        }
    
        fn currency(&self) -> &str {
            &self.currency
        }
    
        fn issuers(&self) -> &Vec<ed25519::PublicKey> {
            &self.issuers
        }
    
        fn signatures(&self) -> &Vec<ed25519::Signature> {
            &self.signatures
        }
    
        fn as_bytes(&self) -> &[u8] {
            self.as_text().as_bytes()
        }
    }
    
    impl TextDocument for IdentityDocument {
        fn as_text(&self) -> &str {
            &self.text
        }
    }
    
    impl IntoSpecializedDocument<BlockchainProtocol> for IdentityDocument {
        fn into_specialized(self) -> BlockchainProtocol {
            BlockchainProtocol::V10(V10Document::Identity(self))
        }
    }
    
    /// Identity document builder.
    #[derive(Debug, Copy, Clone)]
    pub struct IdentityDocumentBuilder<'a> {
        /// Document currency.
        pub currency: &'a str,
        /// Identity unique id.
        pub unique_id: &'a str,
        /// Reference blockstamp.
        pub blockstamp: &'a Blockstamp,
        /// Document/identity issuer.
        pub issuer: &'a ed25519::PublicKey,
    }
    
    impl<'a> IdentityDocumentBuilder<'a> {
        fn build_with_text_and_sigs(
            self,
            text: String,
            signatures: Vec<ed25519::Signature>,
        ) -> IdentityDocument {
            IdentityDocument {
                text,
                currency: self.currency.to_string(),
                unique_id: self.unique_id.to_string(),
                blockstamp: *self.blockstamp,
                issuers: vec![*self.issuer],
                signatures,
            }
        }
    }
    
    impl<'a> DocumentBuilder for IdentityDocumentBuilder<'a> {
        type Document = IdentityDocument;
        type PrivateKey = ed25519::PrivateKey;
    
        fn build_with_signature(&self, signatures: Vec<ed25519::Signature>) -> IdentityDocument {
            self.build_with_text_and_sigs(self.generate_text(), signatures)
        }
    
        fn build_and_sign(&self, private_keys: Vec<ed25519::PrivateKey>) -> IdentityDocument {
            let (text, signatures) = self.build_signed_text(private_keys);
            self.build_with_text_and_sigs(text, signatures)
        }
    }
    
    impl<'a> TextDocumentBuilder for IdentityDocumentBuilder<'a> {
        fn generate_text(&self) -> String {
            format!(
                "Version: 10
    Type: Identity
    Currency: {currency}
    Issuer: {issuer}
    UniqueID: {unique_id}
    Timestamp: {blockstamp}
    ",
                currency = self.currency,
                issuer = self.issuer,
                unique_id = self.unique_id,
                blockstamp = self.blockstamp
            )
        }
    }
    
    /// Identity document parser
    #[derive(Debug, Clone, Copy)]
    pub struct IdentityDocumentParser;
    
    impl StandardTextDocumentParser for IdentityDocumentParser {
        fn parse_standard(
            doc: &str,
            body: &str,
            currency: &str,
            signatures: Vec<ed25519::Signature>,
        ) -> Result<V10Document, V10DocumentParsingError> {
            if let Some(caps) = IDENTITY_REGEX.captures(body) {
                let issuer = &caps["issuer"];
                let uid = &caps["uid"];
                let blockstamp = &caps["blockstamp"];
    
                // Regex match so should not fail.
                // TODO : Test it anyway
                let issuer = ed25519::PublicKey::from_base58(issuer).unwrap();
                let blockstamp = Blockstamp::from_string(blockstamp).unwrap();
    
                Ok(V10Document::Identity(IdentityDocument {
                    text: doc.to_owned(),
                    currency: currency.to_owned(),
                    unique_id: uid.to_owned(),
                    blockstamp,
                    issuers: vec![issuer],
                    signatures,
                }))
            } else {
                Err(V10DocumentParsingError::InvalidInnerFormat(
                    "Identity".to_string(),
                ))
            }
        }
    }
    
    /*impl DocumentHasher<A, IdentityDocument> for IdentityDocument
        where A : HashAlgorithm {
        fn hash_str(algo: A, document: IdentityDocument) -> String {
            algo.hash_str(document.as_text())
        }
    }*/
    
    #[cfg(test)]
    mod tests {
        use super::*;
        use duniter_crypto::keys::{PrivateKey, PublicKey, Signature};
        use blockchain::{Document, VerificationResult};
    
        #[test]
        fn generate_real_document() {
            let pubkey = ed25519::PublicKey::from_base58(
                "DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV",
            ).unwrap();
    
            let prikey = ed25519::PrivateKey::from_base58(
                "468Q1XtTq7h84NorZdWBZFJrGkB18CbmbHr9tkp9snt5G\
                 iERP7ySs3wM8myLccbAAGejgMRC9rqnXuW3iAfZACm7",
            ).unwrap();
    
            let sig = ed25519::Signature::from_base64(
                "1eubHHbuNfilHMM0G2bI30iZzebQ2cQ1PC7uPAw08FGM\
                 MmQCRerlF/3pc4sAcsnexsxBseA/3lY03KlONqJBAg==",
            ).unwrap();
    
            let block = Blockstamp::from_string(
                "0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855",
            ).unwrap();
    
            let builder = IdentityDocumentBuilder {
                currency: "duniter_unit_test_currency",
                unique_id: "tic",
                blockstamp: &block,
                issuer: &pubkey,
            };
    
            assert_eq!(
                builder.build_with_signature(vec![sig]).verify_signatures(),
                VerificationResult::Valid()
            );
            assert_eq!(
                builder.build_and_sign(vec![prikey]).verify_signatures(),
                VerificationResult::Valid()
            );
        }
    
        #[test]
        fn identity_standard_regex() {
            assert!(IDENTITY_REGEX.is_match(
                "Issuer: DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV
    UniqueID: tic
    Timestamp: 0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855
    "
            ));
        }
    
        #[test]
        fn parse_identity_document() {
            let doc = "Version: 10
    Type: Identity
    Currency: duniter_unit_test_currency
    Issuer: DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV
    UniqueID: tic
    Timestamp: 0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855
    ";
    
            let body = "Issuer: DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV
    UniqueID: tic
    Timestamp: 0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855
    ";
    
            let currency = "duniter_unit_test_currency";
    
            let signatures = vec![Signature::from_base64(
    "1eubHHbuNfilHMM0G2bI30iZzebQ2cQ1PC7uPAw08FGMMmQCRerlF/3pc4sAcsnexsxBseA/3lY03KlONqJBAg=="
            ).unwrap(),];
    
            let doc = IdentityDocumentParser::parse_standard(doc, body, currency, signatures).unwrap();
            if let V10Document::Identity(doc) = doc {
                println!("Doc : {:?}", doc);
                assert_eq!(doc.verify_signatures(), VerificationResult::Valid())
            } else {
                panic!("Wrong document type");
            }
        }
    }