├── .gitignore ├── .travis.yml ├── Cargo.toml ├── LICENSE ├── src ├── fingerprint.rs ├── revoker.rs ├── root_validator.rs ├── signature.rs ├── meta.rs ├── bytescontainer.rs ├── validator.rs ├── lib.rs ├── ed25519.rs ├── trust_validator.rs └── certificate.rs └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | Cargo.lock 3 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: rust 2 | sudo: false 3 | rust: 4 | - stable 5 | - beta 6 | - nightly 7 | install: 8 | - wget https://github.com/jedisct1/libsodium/releases/download/1.0.6/libsodium-1.0.6.tar.gz 9 | - tar xvfz libsodium-1.0.6.tar.gz 10 | - cd libsodium-1.0.6 && ./configure --prefix=$HOME/installed_libsodium && make && make install && cd .. 11 | - export PKG_CONFIG_PATH=$HOME/installed_libsodium/lib/pkgconfig:$PKG_CONFIG_PATH 12 | - export LD_LIBRARY_PATH=$HOME/installed_libsodium/lib:$LD_LIBRARY_PATH 13 | matrix: 14 | allow_failures: 15 | - rust: nightly 16 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "edcert" 3 | version = "9.0.1" 4 | authors = [ "Marvin Böcker " ] 5 | 6 | description = "A crate which can be used to sign and verify content based on Ed25519." 7 | repository = "https://github.com/zombiemuffin/edcert/" 8 | documentation = "https://docs.rombie.de/edcert/" 9 | readme = "README.md" 10 | keywords = ["ed25519", "crypto", "cryptography", "security", "edcert"] 11 | license = "MIT" 12 | 13 | [dependencies] 14 | secrets = "^0.11.1" 15 | chrono = "^0.2" 16 | rustc-serialize = "^0.3" 17 | sodiumoxide = "^0.0.12" 18 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Marvin Böcker 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/fingerprint.rs: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright (c) 2016 Marvin Böcker 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | //! This module contains the trait `Fingerprint`. It provides a fingerprint() method, which is 24 | //! used in cryptographic context to identify some value. For example, a `Certificate` implements 25 | //! `Fingerprint` and returns its public key on fingerprint(). On the other hand, secure 26 | //! containers (like Letter in edcert-letter) could return a hash of the contained value. 27 | 28 | /// The fingerprint method should return a value that is unique to the implementing type. 29 | pub trait Fingerprint { 30 | /// The fingerprint method should return a value that is unique to the implementing type. 31 | fn fingerprint(&self) -> Vec; 32 | } 33 | 34 | impl Fingerprint for T 35 | where T: AsRef<[u8]> 36 | { 37 | fn fingerprint(&self) -> Vec { 38 | self.as_ref().into() 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/revoker.rs: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright (c) 2016 Marvin Böcker 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | //! This module provides a revoker, which can be used to check if a certificate has been revoked. 24 | 25 | use fingerprint::Fingerprint; 26 | 27 | /// A type which indicates failure while checking for revokation. 28 | #[derive(Debug, Hash, PartialEq, Eq)] 29 | pub enum RevokeError { 30 | /// The certificate was revoked. 31 | Revoked, 32 | 33 | /// The revoke server was not availiable. 34 | ServerUnavailiable, 35 | } 36 | 37 | /// This trait must be implemented for types, which can be revoked. 38 | pub trait Revokable: Fingerprint { 39 | /// This method can use the revoker to check if it has been revoked. 40 | /// 41 | /// **Don't call this method directly, it will be invoked by Revoker::is_revoked(_).** 42 | fn self_check_revoked(&self, revoker: &R) -> Result<(), RevokeError>; 43 | } 44 | 45 | /// This trait is used by a `Validator` to check, if a `Certificate` has been revoked. 46 | pub trait Revoker { 47 | /// This method should return Ok, if the `Certificate` has not been revoked, and Err(_), if it 48 | /// has been. 49 | fn is_revoked(&self, &F) -> Result<(), RevokeError>; 50 | } 51 | 52 | /// Use this in a Validator to *NOT* check `Certificate`s whether they have been revoked. 53 | /// This is *not* recommended though. If a private key has been disclosed, the `Certificate` MUST be 54 | /// revoked and invalidated, or else the whole system is endangered. 55 | pub struct NoRevoker; 56 | 57 | impl Revoker for NoRevoker { 58 | fn is_revoked(&self, _: &F) -> Result<(), RevokeError> { 59 | Ok(()) 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/root_validator.rs: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright (c) 2016 Marvin Böcker 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | //! This module provides a validator, which analyzes the trust chain to validate a Certificate. 24 | 25 | use validator::ValidationError; 26 | use validator::Validator; 27 | use validator::Validatable; 28 | use bytescontainer::BytesContainer; 29 | use revoker::Revoker; 30 | use revoker::Revokable; 31 | 32 | /// This is a simple Validator, which checks the trust chain for valid certificates. The top-most 33 | /// Certificate must be signed with the right master private key. 34 | #[derive(Clone,Debug,PartialEq)] 35 | pub struct RootValidator { 36 | revoker: R, 37 | master_public_key: BytesContainer, 38 | } 39 | 40 | impl RootValidator { 41 | /// Call this to create a CV with a revoke server. 42 | /// For every certificate the revoke server is asked if it is known. 43 | pub fn new(master_public_key: &[u8; 32], revoker: R) -> RootValidator { 44 | let mut vec: Vec = Vec::new(); 45 | vec.extend_from_slice(master_public_key); 46 | 47 | RootValidator { 48 | revoker: revoker, 49 | master_public_key: BytesContainer::new(vec), 50 | } 51 | } 52 | } 53 | 54 | impl Validator for RootValidator { 55 | /// Checks the certificate if it is valid. 56 | /// If the CV knows a revoke server, that is queried as well. 57 | fn is_valid(&self, cert: &V) -> Result<(), ValidationError> { 58 | // this returns an Error if it fails 59 | try!(cert.self_validate(self)); 60 | 61 | // this returns an Error if it fails 62 | try!(self.revoker.is_revoked(cert)); 63 | 64 | // if nothing fails, the certificate is valid! 65 | Ok(()) 66 | } 67 | 68 | /// Checks if the signature is valid. 69 | fn is_signature_valid(&self, data: &[u8], signature: &[u8]) -> bool { 70 | use ed25519; 71 | ed25519::verify(data, signature, self.master_public_key.get()) 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/signature.rs: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright (c) 2016 Marvin Böcker 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | //! This module contains the signature struct, a container for the Ed25519 signature and a 24 | //! reference to the signer. 25 | 26 | use bytescontainer::BytesContainer; 27 | use certificate::Certificate; 28 | 29 | /// This struct contains a ed25519 signature and a reference to the Certificate, which signed it. 30 | #[derive(Clone,RustcDecodable,RustcEncodable,Debug,PartialEq)] 31 | pub struct Signature { 32 | /// This is the actual signature generated with the certificate data and the parents private key 33 | /// It can be validated with the parents public key. 34 | hash: BytesContainer, 35 | 36 | /// If this is None, then the Certificate is signed with the master key. 37 | signed_by: Option>, 38 | } 39 | 40 | impl Signature { 41 | /// Creates a new Signature with the given signature. It is assumed that the signature is 42 | /// computed usign the master key. 43 | pub fn new(signature: Vec) -> Signature { 44 | Signature { 45 | hash: BytesContainer::new(signature), 46 | signed_by: None, 47 | } 48 | } 49 | 50 | /// Creates a new Signature with the given parent and given signature. 51 | pub fn with_parent(parent: Box, signature: Vec) -> Signature { 52 | Signature { 53 | hash: BytesContainer::new(signature), 54 | signed_by: Some(parent), 55 | } 56 | } 57 | 58 | /// This method will return true iff the certificate has no parent certificate. 59 | /// It is then signed with the master key. 60 | pub fn is_signed_by_master(&self) -> bool { 61 | self.signed_by.is_none() 62 | } 63 | 64 | /// This method will return the parent Certificate, or None, if it is signed with the 65 | /// master key. 66 | pub fn parent(&self) -> Option<&Certificate> { 67 | if self.signed_by.is_none() { 68 | None 69 | } else { 70 | let parent: &Certificate = self.signed_by.as_ref().unwrap(); 71 | Some(parent) 72 | } 73 | } 74 | 75 | /// This method will return the signature given by the parent. 76 | pub fn hash(&self) -> &Vec { 77 | self.hash.get() 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/meta.rs: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright (c) 2016 Marvin Böcker 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | //! This module contains the struct which holds the meta data of a Certificate. 24 | 25 | use std::collections::BTreeMap; 26 | 27 | /// This struct holds meta data for a Certificate. It is also capable of generating a hash, which 28 | /// is based on SHA512. The hash is equal, regardless of the ordering of meta elements. 29 | #[derive(Clone,RustcEncodable,RustcDecodable,Debug,PartialEq)] 30 | pub struct Meta { 31 | values: BTreeMap, 32 | } 33 | 34 | impl Meta { 35 | /// Creates a new Meta object, which can be used to store metadata for a certificate. 36 | pub fn new_empty() -> Meta { 37 | Meta { values: BTreeMap::new() } 38 | } 39 | 40 | /// Creates a new Meta object using the given BTreeMap. 41 | pub fn new(values: BTreeMap) -> Meta { 42 | Meta { values: values } 43 | } 44 | 45 | /// This method returns true iff the key exists. 46 | pub fn key_exists(&self, key: &str) -> bool { 47 | self.get(key).is_some() 48 | } 49 | 50 | /// This method returns the value of key, if it exists. Otherwise it returns None. 51 | pub fn get(&self, key: &str) -> Option<&String> { 52 | self.values.get(key) 53 | } 54 | 55 | /// This method assigns a value to a given key. 56 | pub fn set(&mut self, key: &str, value: &str) { 57 | self.values.insert(key.to_string(), value.to_string()); 58 | } 59 | 60 | /// This method returns a mutable reference to the tree object. 61 | pub fn values_mut(&mut self) -> &mut BTreeMap { 62 | &mut self.values 63 | } 64 | 65 | /// This method returns a reference to the tree object. 66 | pub fn values(&self) -> &BTreeMap { 67 | &self.values 68 | } 69 | 70 | /// This method fills the given byte vector with a "hash" which is created from all keys 71 | /// and values. 72 | pub fn fill_bytes(&self, bytes: &mut [u8]) { 73 | use sodiumoxide::crypto::hash::sha512; 74 | 75 | let mut hash = [0; 64]; 76 | 77 | for key in self.values.keys() { 78 | let value = self.values.get(key).unwrap(); 79 | add_hash(&mut hash, &sha512::hash(key.as_bytes()).0); 80 | add_hash(&mut hash, &sha512::hash(value.as_bytes()).0); 81 | } 82 | 83 | copy_bytes(bytes, &hash, 0, 0, hash.len()) 84 | } 85 | } 86 | 87 | /// This is a simple copy function. This should be replaced by memcpy or something... 88 | fn copy_bytes(dest: &mut [u8], src: &[u8], start_dest: usize, start_src: usize, len: usize) { 89 | for i in 0..(len - 1) { 90 | dest[start_dest + i] = src[start_src + i]; 91 | } 92 | } 93 | 94 | /// This method adds h2 to h1. 95 | fn add_hash(h1: &mut [u8], h2: &[u8]) { 96 | for i in 0..(h1.len()) { 97 | let a: u16 = ((h1[i] as u16) + (h2[i] as u16)) % 256; 98 | h1[i] = a as u8; 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Build Status](https://travis-ci.org/zombiemuffin/edcert.svg?branch=master)](https://travis-ci.org/zombiemuffin/edcert) 2 | 3 | Hi and welcome on the git page of my crate "edcert". 4 | 5 | Edcert is a simple library for certification and authentication of data. 6 | 7 | # How it works 8 | 9 | 1. You create a master keypair. This will be used to sign the highest certificate. 10 | 2. You create a root certificate. Sign this with the master key. 11 | 3. You can now create other certificates and use certificates to sign each other. 12 | 4. Transmit your certificates in a json-encoded format over the network. 13 | 5. Sign and verify data with the certificates using the ".sign" and ".verify" methods. 14 | 15 | The design uses the "super-secure, super-fast" elliptic curve [Ed25519], 16 | which you can learn more about here 17 | 18 | For cryptography it uses the [sodiumoxide] library, which is based on [NaCl], 19 | the well known cryptography libraray by Dan Bernstein et al. 20 | 21 | # Example 22 | 23 | ```rust 24 | use chrono::Timelike; 25 | use chrono::UTC; 26 | use chrono::duration::Duration; 27 | use meta::Meta; 28 | use certificate::Certificate; 29 | use validator::Validatable; 30 | use validator::Validator; 31 | use root_validator::RootValidator; 32 | use trust_validator::TrustValidator; 33 | use revoker::NoRevoker; 34 | 35 | // create random master key 36 | let (mpk, msk) = ed25519::generate_keypair(); 37 | 38 | // create random certificate 39 | let meta = Meta::new_empty(); 40 | let expires = UTC::now() 41 | .checked_add(Duration::days(90)) 42 | .expect("Failed to add 90 days to expiration date.") 43 | .with_nanosecond(0) 44 | .unwrap(); 45 | let mut cert = Certificate::generate_random(meta, expires); 46 | 47 | // sign certificate with master key 48 | cert.sign_with_master(&msk); 49 | 50 | // we can use a RootValidator, which analyzes the trust chain. 51 | // in this case, the top-most certificate must be signed with the right private key for mpk. 52 | let cv = RootValidator::new(&mpk, NoRevoker); 53 | 54 | // now we use the CV to validate certificates 55 | assert_eq!(true, cv.is_valid(&cert).is_ok()); 56 | 57 | // we could also use a TrustValidator. It's like RootValidator, but you can also give trusted 58 | // certificates. If the chain contains one of these, the upper certificates aren't checked 59 | // with the master public key. We can give any 32 byte key here, it doesn't matter. 60 | let mut tcv = TrustValidator::new(&[0; 32], NoRevoker); 61 | tcv.add_trusted_certificates(vec![cert.get_id()]); 62 | 63 | // even though we gave a wrong master key, this certificate is valid, because it is trusted. 64 | assert_eq!(true, tcv.is_valid(&cert).is_ok()); 65 | 66 | // now we sign data with it 67 | let data = [1; 42]; 68 | 69 | // and sign the data with the certificate 70 | let signature = cert.sign(&data) 71 | .expect("This fails, if no private key is known to the certificate."); 72 | 73 | // the signature must be valid 74 | assert_eq!(true, cert.verify(&data, &signature)); 75 | ``` 76 | 77 | # To-Do: 78 | 79 | There are always things to work on in cryptographic projects. Here are just some 80 | of these: 81 | 82 | - Add safe memory zeroing (using the `secrets` crate) 83 | - Add self-signed certificates* 84 | 85 | \*: If you identify a certificate via a fingerprint that is say his public key, 86 | anyone could send you a version of that certificate with whatever expiry date 87 | they wish. If you have that certificate in your trust store, you won't notice, 88 | because you only check if the fingerprint is known. 89 | 90 | To prevent this, we will only allow self-signed certificates in trust-stores 91 | and check if the signature is valid, because an attacker cannot recreate the 92 | signature. 93 | 94 | 95 | # License 96 | 97 | MIT 98 | 99 | That means you can use this code in open source projects and/or commercial 100 | projects without any problems. Please read the license file "LICENSE" for 101 | details 102 | 103 | [Ed25519]: https://ed25519.cr.yp.to/ 104 | [sodiumoxide]: http://dnaq.github.io/sodiumoxide/sodiumoxide/index.html 105 | [NaCl]: https://nacl.cr.yp.to/ 106 | -------------------------------------------------------------------------------- /src/bytescontainer.rs: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright (c) 2016 Marvin Böcker 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | use rustc_serialize::Encoder; 24 | use rustc_serialize::Encodable; 25 | use rustc_serialize::Decoder; 26 | use rustc_serialize::Decodable; 27 | use rustc_serialize::hex::FromHex; 28 | 29 | #[derive(Clone,Debug,PartialEq)] 30 | pub struct BytesContainer { 31 | // TODO: replace with that secure memory container thingy. Also remove that horrible "safe" 32 | // Drop implementation 33 | bytes: Vec, 34 | } 35 | 36 | impl BytesContainer { 37 | pub fn new(bytes: Vec) -> BytesContainer { 38 | BytesContainer { bytes: bytes } 39 | } 40 | 41 | pub fn get(&self) -> &Vec { 42 | &self.bytes 43 | } 44 | 45 | pub fn to_bytestr(&self) -> String { 46 | let bytestr: Vec = self.bytes.iter().map(|b| format!("{:02X}", b)).collect(); 47 | bytestr.join("") 48 | } 49 | 50 | pub fn from_bytestr(bytestr: &str) -> Result { 51 | if bytestr.is_empty() { 52 | return Ok(BytesContainer::new(vec![])); 53 | } 54 | match bytestr.from_hex() { 55 | Ok(vec) => Ok(BytesContainer::new(vec)), 56 | _ => Err(()), 57 | } 58 | } 59 | } 60 | 61 | impl Decodable for BytesContainer { 62 | fn decode(d: &mut T) -> Result { 63 | let bytestr = try!(String::decode(d)); 64 | match BytesContainer::from_bytestr(&bytestr) { 65 | Ok(bc) => Ok(bc), 66 | Err(_) => Err(d.error("Failed to parse hex string")), 67 | } 68 | } 69 | } 70 | 71 | impl Encodable for BytesContainer { 72 | fn encode(&self, d: &mut T) -> Result<(), T::Error> { 73 | self.to_bytestr().encode(d) 74 | } 75 | } 76 | 77 | impl Drop for BytesContainer { 78 | fn drop(&mut self) { 79 | use std::ptr::write_volatile; 80 | for a in &mut self.bytes { 81 | unsafe { 82 | write_volatile(a, 0xA0); 83 | } 84 | } 85 | } 86 | } 87 | 88 | #[test] 89 | fn test_encoding() { 90 | use rustc_serialize::json; 91 | 92 | let bc = BytesContainer::new(vec![1, 2, 3, 100]); 93 | assert_eq!(json::encode(&bc).unwrap(), "\"01020364\""); 94 | let bc = BytesContainer::new(vec![]); 95 | assert_eq!(json::encode(&bc).unwrap(), "\"\""); 96 | } 97 | 98 | #[test] 99 | fn test_decoding() { 100 | use rustc_serialize::json; 101 | 102 | let bytestr = "\"A099\""; 103 | let bc: BytesContainer = json::decode(bytestr).unwrap(); 104 | assert_eq!(bc.get(), &vec![160, 153]); 105 | 106 | let bytestr = "\"\""; 107 | let bc: BytesContainer = json::decode(bytestr).unwrap(); 108 | assert_eq!(bc.get(), &vec![]); 109 | } 110 | 111 | #[test] 112 | fn test_memclear() { 113 | unsafe { 114 | // this will point to the vec of bytes 115 | let ptr: *const Vec; 116 | 117 | // init a vec and let it go out of scope 118 | { 119 | let a = vec![1, 2, 3]; 120 | let bc = BytesContainer::new(a); 121 | 122 | // assign pointer 123 | ptr = bc.get(); 124 | } 125 | 126 | // now the data should be cleared 127 | assert_eq!(*ptr, vec![0, 0, 0]); 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /src/validator.rs: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright (c) 2016 Marvin Böcker 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | //! This module contains the `Validator`, which can be used to validate `Certificate`s, 24 | //! as well as some traits used by the struct. 25 | 26 | use revoker::RevokeError; 27 | use revoker::Revokable; 28 | 29 | /// This type contains information about why a validation failed. 30 | #[derive(Debug, Hash, PartialEq, Eq)] 31 | pub enum ValidationError { 32 | /// Some signature is invalid. 33 | SignatureInvalid, 34 | 35 | /// The parent certificate is invalid. 36 | ParentInvalid, 37 | 38 | /// Something is expired. 39 | Expired, 40 | 41 | /// The certificate has been revoked. 42 | Revoked, 43 | 44 | /// Some other error happened while trying to validate. (eg. a server was not responding) 45 | Other, 46 | } 47 | 48 | impl From for ValidationError { 49 | fn from(r: RevokeError) -> ValidationError { 50 | match r { 51 | RevokeError::Revoked => ValidationError::Revoked, 52 | _ => ValidationError::Other, 53 | } 54 | } 55 | } 56 | 57 | /// This trait must be implemented for types, which must be validated. 58 | pub trait Validatable { 59 | /// This method is given a validator. It can access the master public key indirectly using the 60 | /// `Validator`. 61 | /// 62 | /// **Don't call this method directly, it will be called if you call Validator::is_valid(_).** 63 | fn self_validate(&self, validator: &T) -> Result<(), ValidationError>; 64 | } 65 | 66 | /// This trait can be implemented to verify `Validatable`s with it. 67 | pub trait Validator { 68 | /// This method is called with a certificate or a secure container, which should be validated. 69 | fn is_valid(&self, cert: &V) -> Result<(), ValidationError>; 70 | 71 | /// This method will check if the given signature is a valid signature issued by the master 72 | /// key. 73 | fn is_signature_valid(&self, data: &[u8], signature: &[u8]) -> bool; 74 | } 75 | 76 | #[test] 77 | fn test_validator() { 78 | use ed25519; 79 | use chrono::Timelike; 80 | use chrono::UTC; 81 | use chrono::duration::Duration; 82 | use meta::Meta; 83 | use certificate::Certificate; 84 | use root_validator::RootValidator; 85 | use revoker::NoRevoker; 86 | 87 | let (mpk, msk) = ed25519::generate_keypair(); 88 | 89 | let cv = RootValidator::new(&mpk, NoRevoker); 90 | 91 | let meta = Meta::new_empty(); 92 | let expires = UTC::now() 93 | .checked_add(Duration::days(90)) 94 | .expect("Failed to add 90 days to expiration date.") 95 | .with_nanosecond(0) 96 | .unwrap(); 97 | 98 | let mut cert = Certificate::generate_random(meta.clone(), expires.clone()); 99 | 100 | cert.sign_with_master(&msk); 101 | 102 | assert_eq!(cv.is_valid(&cert).is_ok(), true); 103 | 104 | let cert_invalid = Certificate::generate_random(meta.clone(), expires.clone()); 105 | 106 | assert_eq!(cv.is_valid(&cert_invalid).is_ok(), false); 107 | } 108 | 109 | #[test] 110 | fn test_meta_can_sign() { 111 | use ed25519; 112 | use chrono::Timelike; 113 | use chrono::UTC; 114 | use chrono::duration::Duration; 115 | use meta::Meta; 116 | use certificate::Certificate; 117 | use root_validator::RootValidator; 118 | use revoker::NoRevoker; 119 | 120 | let (mpk, msk) = ed25519::generate_keypair(); 121 | 122 | let cv = RootValidator::new(&mpk, NoRevoker); 123 | 124 | let mut meta = Meta::new_empty(); 125 | let expires = UTC::now() 126 | .checked_add(Duration::days(90)) 127 | .expect("Failed to add 90 days to expiration date.") 128 | .with_nanosecond(0) 129 | .unwrap(); 130 | 131 | { 132 | let mut cert = Certificate::generate_random(meta.clone(), expires.clone()); 133 | cert.sign_with_master(&msk); 134 | assert_eq!(cv.is_valid(&cert).is_ok(), true); 135 | 136 | let mut cert_child = Certificate::generate_random(meta.clone(), expires.clone()); 137 | cert.sign_certificate(&mut cert_child).expect("Failed to sign certificate"); 138 | assert_eq!(cv.is_valid(&cert_child).is_ok(), false); 139 | } 140 | 141 | { 142 | meta.set("use-for", "[\"edcert.sign\"]"); 143 | 144 | let mut cert = Certificate::generate_random(meta.clone(), expires.clone()); 145 | cert.sign_with_master(&msk); 146 | assert_eq!(cv.is_valid(&cert).is_ok(), true); 147 | 148 | let mut cert_child = Certificate::generate_random(meta.clone(), expires.clone()); 149 | cert.sign_certificate(&mut cert_child).expect("Failed to sign certificate"); 150 | assert_eq!(cv.is_valid(&cert_child).is_ok(), true); 151 | } 152 | } 153 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright (c) 2016 Marvin Böcker 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | //! This crate is a simple digital signature crate and can be used to verify data integrity by 24 | //! using public-key cryptography. It uses the "super-fast, super-secure" elliptic curve and 25 | //! digital signature algorithm [Ed25519](https://ed25519.cr.yp.to/). 26 | //! 27 | //! It provides the struct `Certificate`, which holds the public key, metadata and a signature. 28 | //! 29 | //! # The basics 30 | //! A `Certificate` can be signed by a master key, or another `Certificate`. The top-most `Certificate` 31 | //! must be signed with the master key, or it will not be valid. For validation, the master public 32 | //! key will be given. This way, a `Certificate` can only be valid, if it has been signed with a 33 | //! trust chain, which top-most `Certificate` has been signed with the right private key. 34 | //! 35 | //! See also [here](https://en.wikipedia.org/wiki/EdDSA). 36 | //! 37 | //! ## Other crates 38 | //! 39 | //! To use the edcert ecosystem, there are a few other crates to make your life simpler: 40 | //! 41 | //! - [edcert-letter](https://crates.io/crates/edcert-letter), which provides a container for 42 | //! signed data, Letter<T>. 43 | //! - [edcert-restrevoke](https://crates.io/crates/edcert-restrevoke), which provides a REST-based 44 | //! revokation system. 45 | //! - [edcert-compressor](https://crates.io/crates/edcert-compressor), which provides methods to 46 | //! (de)compress `Certificate`s using JSON/LZMA and manages loading/saving certificates for you. 47 | //! - [edcert-tools](https://crates.io/crates/edcert-tools), which provides a binary for 48 | //! generation, signing, validation, etc using edcert (and all of the above). 49 | 50 | #![deny(missing_docs)] 51 | 52 | // External crates. 53 | extern crate chrono; 54 | extern crate rustc_serialize; 55 | extern crate sodiumoxide; 56 | 57 | // Internal modules 58 | mod bytescontainer; 59 | pub mod ed25519; 60 | 61 | // External modules 62 | pub mod certificate; 63 | pub mod fingerprint; 64 | pub mod meta; 65 | pub mod signature; 66 | 67 | // Validation 68 | pub mod validator; 69 | pub mod root_validator; 70 | pub mod trust_validator; 71 | 72 | // Revokation 73 | pub mod revoker; 74 | 75 | /// This is a simple copy function. This should be equivalent to memcpy. 76 | pub fn copy_bytes(dest: &mut [u8], src: &[u8], start_dest: usize, start_src: usize, len: usize) { 77 | let end_dest = start_dest + len; 78 | let end_src = start_src + len; 79 | dest[start_dest..end_dest].copy_from_slice(&src[start_src..end_src]); 80 | } 81 | 82 | #[test] 83 | fn test_readme_example() { 84 | use chrono::Timelike; 85 | use chrono::UTC; 86 | use chrono::duration::Duration; 87 | use meta::Meta; 88 | use certificate::Certificate; 89 | use validator::Validator; 90 | use root_validator::RootValidator; 91 | use trust_validator::TrustValidator; 92 | use revoker::NoRevoker; 93 | use fingerprint::Fingerprint; 94 | 95 | // create random master key 96 | let (mpk, msk) = ed25519::generate_keypair(); 97 | 98 | // create random certificate 99 | let meta = Meta::new_empty(); 100 | let expires = UTC::now() 101 | .checked_add(Duration::days(90)) 102 | .expect("Failed to add 90 days to expiration date.") 103 | .with_nanosecond(0) 104 | .unwrap(); 105 | let mut cert = Certificate::generate_random(meta, expires); 106 | 107 | // sign certificate with master key 108 | cert.sign_with_master(&msk); 109 | 110 | // we can use a RootValidator, which analyzes the trust chain. 111 | // in this case, the top-most certificate must be signed with the right private key for mpk. 112 | let cv = RootValidator::new(&mpk, NoRevoker); 113 | 114 | // now we use the CV to validate certificates 115 | assert_eq!(true, cv.is_valid(&cert).is_ok()); 116 | 117 | // we could also use a TrustValidator. It's like RootValidator, but you can also give trusted 118 | // certificates. If the chain contains one of these, the upper certificates aren't checked 119 | // with the master public key. We can give any 32 byte key here, it doesn't matter. 120 | let mut tcv = TrustValidator::new(NoRevoker); 121 | tcv.add_trusted_certificates(vec![cert.fingerprint()]); 122 | 123 | // even though we gave a wrong master key, this certificate is valid, because it is trusted. 124 | assert_eq!(true, tcv.is_valid(&cert).is_ok()); 125 | 126 | // now we sign data with it 127 | let data = [1; 42]; 128 | 129 | // and sign the data with the certificate 130 | let signature = cert.sign(&data) 131 | .expect("This fails, if no private key is known to the certificate."); 132 | 133 | // the signature must be valid 134 | assert_eq!(true, cert.verify(&data, &signature)); 135 | } 136 | -------------------------------------------------------------------------------- /src/ed25519.rs: -------------------------------------------------------------------------------- 1 | 2 | // The MIT License (MIT) 3 | // 4 | // Copyright (c) 2016 Marvin Böcker 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in all 14 | // copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | // SOFTWARE. 23 | 24 | //! This module contains a wrapper around the libsodium implementation of ed25519. 25 | //! It reduces the size of signatures to 64 byte. 26 | 27 | use std::sync::Once; 28 | use std::sync::ONCE_INIT; 29 | 30 | static START: Once = ONCE_INIT; 31 | 32 | use sodiumoxide; 33 | use sodiumoxide::crypto::sign::ed25519; 34 | 35 | /// This is the length of a ed25519 private key. 36 | pub const PRIVATE_KEY_LEN: usize = 64; 37 | 38 | /// This is the length of a ed25519 public key. 39 | pub const PUBLIC_KEY_LEN: usize = 32; 40 | 41 | /// This is the length of a ed25519 signature. 42 | pub const SIGNATURE_LEN: usize = 64; 43 | 44 | /// This method generates a random ed25519 keypair from a cryptographically secure source 45 | /// (on unix this is /dev/urandom). Returns (`public_key`, `private_key`). 46 | pub fn generate_keypair() -> ([u8; PUBLIC_KEY_LEN], [u8; PRIVATE_KEY_LEN]) { 47 | 48 | // Initialize the random number generator provided by libsodium. 49 | START.call_once(|| { sodiumoxide::init(); }); 50 | 51 | let (pk, sk) = ed25519::gen_keypair(); 52 | 53 | let public_key = pk.0; 54 | let private_key = sk.0; 55 | 56 | assert_eq!(PUBLIC_KEY_LEN, public_key.len()); 57 | assert_eq!(PRIVATE_KEY_LEN, private_key.len()); 58 | 59 | (public_key, private_key) 60 | } 61 | 62 | /// This method takes a data vector and a private key and computes the signature which can be 63 | /// verified using the public key. 64 | pub fn sign(data: &[u8], private_key: &[u8]) -> Vec { 65 | assert_eq!(private_key.len(), PRIVATE_KEY_LEN); 66 | 67 | use sodiumoxide::crypto::sign::ed25519::Signature; 68 | 69 | let sk = ed25519::SecretKey::from_slice(private_key); 70 | let sk = sk.as_ref().unwrap(); 71 | 72 | let sig = match ed25519::sign_detached(data, sk) { 73 | Signature(signature) => signature 74 | }; 75 | 76 | let mut vec = Vec::new(); 77 | vec.extend_from_slice(&sig); 78 | vec 79 | } 80 | 81 | /// This method takes a data vector, a signature and a public key and returns true, if the 82 | /// signature has been created using the correct private key. 83 | pub fn verify(data: &[u8], signature: &[u8], public_key: &[u8]) -> bool { 84 | assert_eq!(signature.len(), SIGNATURE_LEN); 85 | assert_eq!(public_key.len(), PUBLIC_KEY_LEN); 86 | 87 | let pk = ed25519::PublicKey::from_slice(public_key); 88 | let pk = pk.as_ref().unwrap(); 89 | 90 | let sig = ed25519::Signature::from_slice(signature); 91 | let sig = sig.as_ref().unwrap(); 92 | 93 | ed25519::verify_detached(sig, data, pk) 94 | } 95 | 96 | #[test] 97 | fn test_ed25519_simple() { 98 | let (pk, sk) = generate_keypair(); 99 | 100 | let msg = &[0; 128][..]; 101 | let mut sig = sign(msg, &sk); 102 | 103 | println!("signature: {:?}", sig); 104 | println!(""); 105 | 106 | sig[0] = ((sig[0] as u16 + 1) % 256) as u8; 107 | 108 | assert_eq!(verify(msg, &sig, &pk), false); 109 | 110 | sig[0] = ((sig[0] as u16 + 255) % 256) as u8; 111 | 112 | assert_eq!(verify(msg, &sig, &pk), true); 113 | } 114 | 115 | #[test] 116 | fn test_ed25519_shortmsg() { 117 | let (pk, sk) = generate_keypair(); 118 | 119 | let msg = &[0; 32][..]; 120 | let mut sig = sign(msg, &sk); 121 | 122 | println!("signature: {:?}", sig); 123 | println!(""); 124 | 125 | sig[0] = ((sig[0] as u16 + 1) % 256) as u8; 126 | 127 | assert_eq!(verify(msg, &sig, &pk), false); 128 | 129 | sig[0] = ((sig[0] as u16 + 255) % 256) as u8; 130 | 131 | assert_eq!(verify(msg, &sig, &pk), true); 132 | } 133 | 134 | #[test] 135 | fn test_testvectors() { 136 | use rustc_serialize::json; 137 | use bytescontainer::BytesContainer; 138 | 139 | let decode = |t| { 140 | let t = json::decode::(&format!("\"{}\"", t)).unwrap(); 141 | t.get().clone() 142 | }; 143 | 144 | // test vectors from 145 | // https://github.com/cryptosphere/rbnacl/blob/master/lib/rbnacl/test_vectors.rb 146 | 147 | let prv = decode("b18e1d0045995ec3d010c387ccfeb984d783af8fbb0f40fa7db126d889f6dadd77f48b59cae\ 148 | da77751ed138b0ec667ff50f8768c25d48309a8f386a2bad187fb"); 149 | 150 | let pbl = decode("77f48b59caeda77751ed138b0ec667ff50f8768c25d48309a8f386a2bad187fb"); 151 | 152 | let msg = 153 | decode("916c7d1d268fc0e77c1bef238432573c39be577bbea0998936add2b50a653171ce18a542b0b7f96c1691a3be6031522894a8634183eda38798a0c5d5d79fbd01dd04a8646d71873b77b221998a81922d8105f892316369d5224c9983372d2313c6b1f4556ea26ba49d46e8b561e0fc76633ac9766e68e21fba7edca93c4c7460376d7f3ac22ff372c18f613f2ae2e856af40"); 154 | 155 | let sig = decode("6bd710a368c1249923fc7a1610747403040f0cc30815a00f9ff548a896bbda0b4eb2ca19ebc\ 156 | f917f0f34200a9edbad3901b64ab09cc5ef7b9bcc3c40c0ff7509"); 157 | 158 | let sig_my = sign(&msg, &prv); 159 | 160 | // both signatures must be equal 161 | assert_eq!(&sig_my, &sig); 162 | 163 | // the signature must be valid 164 | assert_eq!(true, verify(&msg, &sig_my, &pbl)); 165 | } 166 | -------------------------------------------------------------------------------- /src/trust_validator.rs: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright (c) 2016 Marvin Böcker 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | //! This module provides a validator, which analyzes the trust chain to validate a Certificate but 24 | //! instead of a single master signature, it uses a set of trusted certificates for validation. 25 | 26 | use validator::ValidationError; 27 | use validator::Validator; 28 | use validator::Validatable; 29 | use revoker::RevokeError; 30 | use revoker::Revoker; 31 | use revoker::Revokable; 32 | 33 | /// This is a simple Validator, which checks the trust chain for valid certificates. The top-most 34 | /// Certificate must be signed with the right master private key. 35 | #[derive(Clone,Debug,PartialEq)] 36 | pub struct TrustValidator { 37 | revoker: R, 38 | trusted_certificates: Vec>, 39 | } 40 | 41 | impl TrustValidator { 42 | /// Call this to create a CV with a revoke server. 43 | /// For every certificate the revoke server is asked if it is known. 44 | pub fn new(revoker: R) -> TrustValidator { 45 | TrustValidator { 46 | revoker: revoker, 47 | trusted_certificates: Vec::new(), 48 | } 49 | } 50 | 51 | /// Call this to create a CV with a revoke server and the given set of trusted certificates. 52 | /// For every certificate the revoke server is asked if it is known. 53 | pub fn with_trusted_certificates(trusted_certificates: T, revoker: R) -> TrustValidator 54 | where T: Into>> 55 | { 56 | TrustValidator { 57 | revoker: revoker, 58 | trusted_certificates: trusted_certificates.into(), 59 | } 60 | } 61 | 62 | /// This method trusts the give certificates. 63 | pub fn add_trusted_certificates(&mut self, trusted_certificates: T) 64 | where T: IntoIterator> 65 | { 66 | self.trusted_certificates.extend(trusted_certificates); 67 | } 68 | 69 | /// This method calls the revoker to check the status of the certificate cert. 70 | pub fn is_revoked(&self, cert: &V) -> Result<(), RevokeError> { 71 | self.revoker.is_revoked(cert) 72 | } 73 | } 74 | 75 | impl Validator for TrustValidator { 76 | /// Checks the certificate if it is valid. 77 | /// If the CV knows a revoke server, that is queried as well. 78 | fn is_valid(&self, cert: &V) -> Result<(), ValidationError> { 79 | // if the certificate is trusted, it is valid 80 | if self.trusted_certificates.contains(&cert.fingerprint()) { 81 | return Ok(()); 82 | } 83 | 84 | // this returns an Error if it fails 85 | try!(cert.self_validate(self)); 86 | 87 | // this returns an Error if it fails 88 | try!(self.revoker.is_revoked(cert)); 89 | 90 | // if nothing fails, the certificate is valid! 91 | Ok(()) 92 | } 93 | 94 | fn is_signature_valid(&self, _: &[u8], _: &[u8]) -> bool { 95 | // The TrustValidator is not using a single root public key, so no certificate signed by 96 | // a "root" key is valid. 97 | false 98 | } 99 | } 100 | 101 | #[test] 102 | fn test_trusted_certificates() { 103 | use fingerprint::Fingerprint; 104 | use meta::Meta; 105 | use certificate::Certificate; 106 | use revoker::NoRevoker; 107 | use chrono::datetime::DateTime; 108 | use chrono::UTC; 109 | 110 | let mut meta = Meta::new_empty(); 111 | meta.set("use-for", r#"["edcert.sign"]"#); 112 | 113 | let cert: Certificate = 114 | Certificate::generate_random(meta, 115 | DateTime::parse_from_rfc3339("2020-01-01T00:00:00+00:00") 116 | .unwrap() 117 | .with_timezone(&UTC)); 118 | let mut child: Certificate = 119 | Certificate::generate_random(Meta::new_empty(), 120 | DateTime::parse_from_rfc3339("2020-01-01T00:00:00+00:00") 121 | .unwrap() 122 | .with_timezone(&UTC)); 123 | 124 | cert.sign_certificate(&mut child).unwrap(); 125 | 126 | let trusted = vec![cert.fingerprint()]; 127 | 128 | let cv = TrustValidator::with_trusted_certificates(trusted, NoRevoker); 129 | 130 | match cv.is_valid(&child) { 131 | Err(x) => { 132 | println!("{:?}", x); 133 | panic!(); 134 | } 135 | _ => {} 136 | }; 137 | 138 | assert_eq!(cv.is_valid(&child).is_ok(), true); 139 | } 140 | 141 | #[test] 142 | fn test_add_trusted_certificates() { 143 | use fingerprint::Fingerprint; 144 | use meta::Meta; 145 | use certificate::Certificate; 146 | use revoker::NoRevoker; 147 | use chrono::datetime::DateTime; 148 | use chrono::UTC; 149 | 150 | let meta = Meta::new_empty(); 151 | 152 | let cert: Certificate = 153 | Certificate::generate_random(meta, 154 | DateTime::parse_from_rfc3339("2020-01-01T00:00:00+00:00") 155 | .unwrap() 156 | .with_timezone(&UTC)); 157 | 158 | let mut cv = TrustValidator::new(NoRevoker); 159 | 160 | assert_eq!(cv.is_valid(&cert).is_ok(), false); 161 | 162 | cv.add_trusted_certificates(vec![cert.fingerprint()]); 163 | 164 | assert_eq!(cv.is_valid(&cert).is_ok(), true); 165 | } 166 | 167 | #[test] 168 | fn test_trusted_certificates_fail() { 169 | use fingerprint::Fingerprint; 170 | use meta::Meta; 171 | use certificate::Certificate; 172 | use revoker::NoRevoker; 173 | use chrono::datetime::DateTime; 174 | use chrono::UTC; 175 | 176 | let mut meta = Meta::new_empty(); 177 | meta.set("use-for", r#"["edcert.sign"]"#); 178 | 179 | let cert: Certificate = 180 | Certificate::generate_random(meta, 181 | DateTime::parse_from_rfc3339("2020-01-01T00:00:00+00:00") 182 | .unwrap() 183 | .with_timezone(&UTC)); 184 | let child: Certificate = 185 | Certificate::generate_random(Meta::new_empty(), 186 | DateTime::parse_from_rfc3339("2020-01-01T00:00:00+00:00") 187 | .unwrap() 188 | .with_timezone(&UTC)); 189 | 190 | let trusted = vec![cert.fingerprint()]; 191 | 192 | let cv = TrustValidator::with_trusted_certificates(trusted, NoRevoker); 193 | 194 | assert_eq!(cv.is_valid(&child).is_ok(), false); 195 | } 196 | -------------------------------------------------------------------------------- /src/certificate.rs: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright (c) 2016 Marvin Böcker 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | //! This module contains the most important struct of this crate: The Certificate 24 | //! It holds a public key, meta data and a signature. 25 | //! It can also optionally hold a private key and sign data. 26 | 27 | use bytescontainer::BytesContainer; 28 | use meta::Meta; 29 | use signature::Signature; 30 | use chrono; 31 | use ed25519; 32 | use fingerprint::Fingerprint; 33 | use validator::ValidationError; 34 | use validator::Validatable; 35 | use validator::Validator; 36 | use revoker::RevokeError; 37 | use revoker::Revokable; 38 | use revoker::Revoker; 39 | 40 | /// This is the length of a ed25519 signature. 41 | pub const SIGNATURE_LEN: usize = ed25519::SIGNATURE_LEN; 42 | 43 | /// This is the length of a ed25519 private key. 44 | pub const PRIVATE_KEY_LEN: usize = ed25519::PRIVATE_KEY_LEN; 45 | 46 | /// This is the length of a ed25519 public key. 47 | pub const PUBLIC_KEY_LEN: usize = ed25519::PUBLIC_KEY_LEN; 48 | 49 | /// This is the length of a safehash of a certificate. 50 | pub const CERTIFICATE_BYTE_LEN: usize = 51 | 25 /* expires as string */ + 64 /* hash of meta */ + PUBLIC_KEY_LEN; 52 | 53 | /// A certificate holds a public key, meta data and a signature. 54 | /// 55 | /// 1. It can be randomly generated by using the `generate_random()` method. 56 | /// 2. It can then be signed using the `sign_with_master()` and `sign_certificate()` methods. 57 | /// 3. It can then be used to sign data / certificates by using the `sign() / sign_certificate()` 58 | /// methods. 59 | /// 4. Data can be verified by using the `verify` method. 60 | /// 5. A Certificate can be validated by a `CertificateValidator`, because it implements the 61 | /// `Validatable` trait. 62 | #[derive(Clone,RustcDecodable,RustcEncodable,Debug,PartialEq)] 63 | pub struct Certificate { 64 | /// The meta element contains data associated with the certificate. 65 | /// Common data is "use-for" which contains a list of permissions. 66 | meta: Meta, 67 | 68 | /// the public key of this certificate. 69 | public_key: BytesContainer, 70 | 71 | /// the private key, if it is known. 72 | private_key: Option, 73 | 74 | /// a timestamp when this certificate expires. 75 | expires: String, 76 | 77 | /// a signature for trust-chaining certificates 78 | /// if the certificate is not signed yet, this is None 79 | signature: Option, 80 | } 81 | 82 | impl Certificate { 83 | /// This method generates a random public/private keypair and a certificate for it. 84 | pub fn generate_random(meta: Meta, expires: chrono::DateTime) -> Certificate { 85 | // generate a keypair. this returns two arrays 86 | let (pubslice, prvslice) = ed25519::generate_keypair(); 87 | 88 | // convert the arrays to vectors 89 | let mut public_key = Vec::new(); 90 | let mut private_key = Vec::new(); 91 | 92 | public_key.extend_from_slice(&pubslice[..]); 93 | private_key.extend_from_slice(&prvslice[..]); 94 | 95 | // create the certificate 96 | Certificate { 97 | private_key: Some(BytesContainer::new(private_key)), 98 | public_key: BytesContainer::new(public_key), 99 | expires: expires.to_rfc3339(), 100 | meta: meta, 101 | signature: None, 102 | } 103 | } 104 | 105 | /// This method returns a mutable reference to the meta structure. 106 | pub fn meta_mut(&mut self) -> &mut Meta { 107 | &mut self.meta 108 | } 109 | 110 | /// This method returns a reference to the meta structure. 111 | pub fn meta(&self) -> &Meta { 112 | &self.meta 113 | } 114 | 115 | /// This method returns a reference to the public key. 116 | pub fn public_key(&self) -> &Vec { 117 | self.public_key.get() 118 | } 119 | 120 | /// This method returns the private key, if it is known, or None if the certificate has been 121 | /// initialized without the private key. 122 | pub fn private_key(&self) -> Option<&Vec> { 123 | if self.has_private_key() { 124 | let vec = self.private_key.as_ref().unwrap().get(); 125 | Some(vec) 126 | } else { 127 | None 128 | } 129 | } 130 | 131 | /// This method returns true, if the private key is saved in the certificate. 132 | pub fn has_private_key(&self) -> bool { 133 | self.private_key.is_some() 134 | } 135 | 136 | /// This method clears the private key, if it has one. 137 | pub fn remove_private_key(&mut self) { 138 | // because the BytesContainer goes out of scope, it will be dropped and the memory will be cleared. 139 | self.private_key = None 140 | } 141 | 142 | /// This method returns the expiration date as a RFC 3339 string. 143 | pub fn expiration_date(&self) -> &str { 144 | &self.expires 145 | } 146 | 147 | /// This method returns either the signature, or None. 148 | pub fn signature(&self) -> Option<&Signature> { 149 | self.signature.as_ref() 150 | } 151 | 152 | /// This method replaces the current private key of this certificate with the given one. 153 | pub fn set_private_key(&mut self, private_key: Vec) { 154 | self.private_key = Some(BytesContainer::new(private_key)); 155 | } 156 | 157 | /// This method checks, if the "use-for" meta tag contains the use "edcert.sign" 158 | pub fn can_sign(&self) -> Result<(), &'static str> { 159 | use rustc_serialize::json; 160 | 161 | let meta = self.meta(); 162 | 163 | match meta.get("use-for") { 164 | Some(use_for) => { 165 | let use_for: Vec = match json::decode(use_for) { 166 | Ok(x) => x, 167 | Err(_) => { 168 | return Err("Failed to parse content of meta value \"use-for\""); 169 | } 170 | }; 171 | 172 | if use_for.contains(&"edcert.sign".to_string()) { 173 | Ok(()) 174 | } else { 175 | 176 | Err("This certificate is not allowed to sign certificates") 177 | } 178 | } 179 | None => Err("The meta value \"use-for\" could not be found"), 180 | } 181 | } 182 | 183 | /// This method returns a "hash". This is used to validate the certificate. 184 | /// All relevant information of the certificate is used to produce the hash, 185 | /// including the public key, meta data and the expiration date. 186 | pub fn safehash(&self) -> [u8; CERTIFICATE_BYTE_LEN] { 187 | 188 | // create a array of this length 189 | let mut bytes = [0; CERTIFICATE_BYTE_LEN]; 190 | 191 | // first 64 bytes are a sha512 hash of meta. the ordering of meta entries is irrelevant 192 | self.meta.fill_bytes(&mut bytes[0..64]); 193 | 194 | // next 25 bytes are string representation of the expiration string 195 | ::copy_bytes(&mut bytes[64..], self.expires.as_bytes(), 0, 0, 25); 196 | 197 | // finally, the public key is appended 198 | ::copy_bytes(&mut bytes[89..], 199 | &self.public_key.get()[..], 200 | 0, 201 | 0, 202 | PUBLIC_KEY_LEN); 203 | 204 | bytes 205 | } 206 | 207 | /// This method returns the parent certificate of this certificate, if it exists. 208 | pub fn parent(&self) -> Option<&Certificate> { 209 | if self.is_signed() { 210 | let sig = &self.signature.as_ref().unwrap(); 211 | sig.parent() 212 | } else { 213 | None 214 | } 215 | } 216 | 217 | /// This method returns true, if a signature exists (is not None). This doesn't validate the 218 | /// signature. 219 | pub fn is_signed(&self) -> bool { 220 | self.signature.is_some() 221 | } 222 | 223 | /// This method signs the given data and returns the signature. 224 | pub fn sign(&self, data: &[u8]) -> Option> { 225 | if self.has_private_key() { 226 | let signature = ed25519::sign(data, self.private_key().unwrap()); 227 | Some(signature) 228 | } else { 229 | None 230 | } 231 | } 232 | 233 | /// This method signs this certificate with the given private master key. 234 | pub fn sign_with_master(&mut self, master_private_key: &[u8]) { 235 | let bytes = self.safehash(); 236 | let hash = ed25519::sign(&bytes[..], master_private_key); 237 | self.signature = Some(Signature::new(hash)); 238 | } 239 | 240 | /// This method signs another certificate with the private key of this certificate. 241 | pub fn sign_certificate(&self, other: &mut Certificate) -> Result<(), &'static str> { 242 | if self.has_private_key() { 243 | let child_bytes = other.safehash(); 244 | let signature_bytes = self.sign(&child_bytes).unwrap().to_vec(); 245 | let parent = Box::new(self.clone()); 246 | let signature = Signature::with_parent(parent, signature_bytes); 247 | 248 | other.signature = Some(signature); 249 | 250 | Ok(()) 251 | } else { 252 | Err("This certificate has no private key") 253 | } 254 | } 255 | 256 | /// This method checks, if this certificates expiration date is now or in the past. 257 | pub fn is_expired(&self) -> bool { 258 | 259 | // try to parse the string of this certificate 260 | let expires = match chrono::DateTime::parse_from_rfc3339(&self.expires) { 261 | Err(_) => return true, 262 | Ok(expires) => expires.with_timezone(&chrono::UTC), 263 | }; 264 | 265 | // if the parsing is ok, then this must be true for the certificate to be expired 266 | expires <= chrono::UTC::now() 267 | } 268 | 269 | /// This method verifies that the given signature is valid for the given data. 270 | pub fn verify(&self, data: &[u8], signature: &[u8]) -> bool { 271 | ed25519::verify(data, signature, self.public_key()) 272 | } 273 | } 274 | 275 | impl Validatable for Certificate { 276 | fn self_validate(&self, cv: &V) -> Result<(), ValidationError> { 277 | if self.is_signed() { 278 | // get a hash unique to this certificate 279 | let bytes: &[u8] = &self.safehash()[..]; 280 | 281 | // get the signature 282 | let signature = self.signature.as_ref().unwrap(); 283 | 284 | // if it is signed by the master key 285 | if signature.is_signed_by_master() { 286 | 287 | // get the signature hash 288 | let hash = signature.hash(); 289 | 290 | // verify it for the safehash, master public key and the signature 291 | // if it is valid 292 | if cv.is_signature_valid(bytes, hash) { 293 | 294 | // check if the certificate is expired 295 | if self.is_expired() { 296 | Err(ValidationError::Expired) 297 | } else { 298 | Ok(()) 299 | } 300 | 301 | } else { 302 | 303 | // else the signature isn't from the master key 304 | Err(ValidationError::SignatureInvalid) 305 | } 306 | 307 | } else { 308 | 309 | // if it is not signed by the master key, get the parent 310 | let parent: &Certificate = signature.parent().unwrap(); 311 | 312 | // verify the signature of the parent 313 | let sign_real = parent.verify(bytes, signature.hash()); 314 | 315 | // verify that the parent is valid 316 | let parent_real = cv.is_valid(parent); 317 | 318 | // can parent sign other certificates? 319 | let parent_can_sign = parent.can_sign().is_ok(); 320 | 321 | // if the signature is valid 322 | if sign_real { 323 | 324 | // and the parent certificate is valid 325 | if parent_real.is_ok() { 326 | 327 | // and the parent can sign 328 | if parent_can_sign { 329 | 330 | // and the certificate is not expired 331 | if self.is_expired() { 332 | Err(ValidationError::Expired) 333 | } else { 334 | Ok(()) 335 | } 336 | } else { 337 | // The certificate isn't allowed to sign certificates. 338 | Err(ValidationError::Other) 339 | } 340 | } else { 341 | Err(ValidationError::ParentInvalid) 342 | } 343 | } else { 344 | // Parent signature is invalid. 345 | Err(ValidationError::ParentInvalid) 346 | } 347 | } 348 | } else { 349 | // Certificate is unsigned 350 | Err(ValidationError::SignatureInvalid) 351 | } 352 | } 353 | } 354 | 355 | impl Fingerprint for Certificate { 356 | fn fingerprint(&self) -> Vec { 357 | self.public_key().clone() 358 | } 359 | } 360 | 361 | impl Revokable for Certificate { 362 | fn self_check_revoked(&self, revoker: &R) -> Result<(), RevokeError> { 363 | revoker.is_revoked(self) 364 | } 365 | } 366 | 367 | #[test] 368 | fn test_generate_certificate() { 369 | use chrono::Timelike; 370 | use chrono::UTC; 371 | use chrono::duration::Duration; 372 | 373 | let meta = Meta::new_empty(); 374 | let expires = UTC::now() 375 | .checked_add(Duration::days(90)) 376 | .expect("Failed to add a day to expiration date.") 377 | .with_nanosecond(0) 378 | .unwrap(); 379 | 380 | let a = Certificate::generate_random(meta, expires); 381 | 382 | let meta = Meta::new_empty(); 383 | 384 | let b = Certificate::generate_random(meta, expires); 385 | 386 | assert!(a.public_key() != b.public_key()); 387 | } 388 | --------------------------------------------------------------------------------