diff --git a/src/crypto/dsa.rs b/src/crypto/dsa.rs index c765200..8ae7a2c 100644 --- a/src/crypto/dsa.rs +++ b/src/crypto/dsa.rs @@ -32,7 +32,7 @@ pub struct DsaPublicKey { impl DsaSignature { pub fn from_bytes(data: &[u8]) -> Result { if data.len() < 40 { - return Err(super::Error::Dsa); + return Err(super::Error::InvalidSignature); } let mut rbar = [0u8; 20]; diff --git a/src/crypto/elgamal.rs b/src/crypto/elgamal.rs index b44dde1..1a7afb7 100644 --- a/src/crypto/elgamal.rs +++ b/src/crypto/elgamal.rs @@ -12,7 +12,7 @@ use rand::{OsRng, Rng}; use sha2::{Digest, Sha256}; use std::ops::{Mul, Rem, Sub}; -use super::{math::rectify, PrivateKey, PublicKey}; +use super::{math::rectify, Error, PrivateKey, PublicKey}; use constants::{ELGAMAL_G, ELGAMAL_P, ELGAMAL_PM1, ELGAMAL_PM2}; fn gen_gamma_k() -> (BigUint, BigUint) { @@ -70,12 +70,11 @@ impl<'a> From<&'a PublicKey> for Encryptor { impl Encryptor { /// Basic ElGamal encryption, following algorithm 8.18 1). - // TODO: Errors - fn encrypt_basic(&self, msg: &[u8]) -> Result<(BigUint, BigUint), ()> { + fn encrypt_basic(&self, msg: &[u8]) -> Result<(BigUint, BigUint), Error> { // Represent the message as an integer m in the range {0, 1, ..., p - 1} let m = BigUint::from_bytes_be(msg); if m > *ELGAMAL_PM1 { - return Err(()); + return Err(Error::InvalidMessage); } // Select a random integer k, 1 <= k <= p - 2 @@ -90,11 +89,10 @@ impl Encryptor { } /// ElGamal encryption using I2P's message and ciphertext encoding schemes. - // TODO: Errors - pub fn encrypt(&self, msg: &[u8]) -> Result<[u8; 514], ()> { + pub fn encrypt(&self, msg: &[u8]) -> Result<[u8; 514], Error> { // Message must be no more than 222 bytes if msg.len() > 222 { - return Err(()); + return Err(Error::InvalidMessage); } let mut rng = OsRng::new().expect("should be able to construct RNG"); @@ -149,10 +147,10 @@ impl Decryptor { /// ElGamal decryption using I2P's message and ciphertext encoding schemes. // TODO: Errors - pub fn decrypt(&self, ct: &[u8]) -> Result, ()> { + pub fn decrypt(&self, ct: &[u8]) -> Result, Error> { // Ciphertext must be 514 bytes if ct.len() != 514 { - return Err(()); + return Err(Error::InvalidCiphertext); } // ElGamal ciphertext: @@ -164,7 +162,7 @@ impl Decryptor { let data = self.decrypt_basic((gamma, delta)); if data.len() < 33 { // Decrypted data is too small - return Err(()); + return Err(Error::InvalidCiphertext); } // ElGamal plaintext: @@ -175,7 +173,7 @@ impl Decryptor { if hash.as_slice() == &data[1..33] { Ok(msg) } else { - Err(()) + Err(Error::InvalidCiphertext) } } } diff --git a/src/crypto/mod.rs b/src/crypto/mod.rs index da797e0..df6b486 100644 --- a/src/crypto/mod.rs +++ b/src/crypto/mod.rs @@ -3,12 +3,11 @@ use aes::{self, block_cipher_trait::generic_array::GenericArray as AesGenericArray}; use block_modes::{block_padding::ZeroPadding, BlockMode, BlockModeIv, Cbc}; use nom::Err; -use ring::{self, signature as ring_signature}; +use ring::signature as ring_signature; use signatory::{ curve::{NistP256, NistP384, WeierstrassCurve}, ecdsa::FixedSignature, ed25519, - error::Error as SignatoryError, generic_array::{typenum::Unsigned, GenericArray as SignatoryGenericArray}, public_key, sign, verify, verify_sha256, verify_sha384, EcdsaPublicKey, Ed25519PublicKey, Ed25519Seed, Ed25519Signature, Signature as SignatorySignature, @@ -31,24 +30,28 @@ pub(crate) mod math; pub(crate) const AES_BLOCK_SIZE: usize = 16; /// Cryptographic errors -#[derive(Debug)] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum Error { + InvalidCiphertext, + InvalidKey, + InvalidMessage, + InvalidSignature, NoSignature, + SigningFailed, TypeMismatch, - Dsa, - Ring(ring::error::Unspecified), - Signatory(SignatoryError), } -impl From for Error { - fn from(e: ring::error::Unspecified) -> Self { - Error::Ring(e) - } -} - -impl From for Error { - fn from(e: SignatoryError) -> Self { - Error::Signatory(e) +impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + Error::InvalidCiphertext => "Invalid ciphertext".fmt(f), + Error::InvalidKey => "Invalid cryptographic key".fmt(f), + Error::InvalidMessage => "Invalid message".fmt(f), + Error::InvalidSignature => "Bad signature".fmt(f), + Error::NoSignature => "No signature".fmt(f), + Error::SigningFailed => "Failed to create a signature".fmt(f), + Error::TypeMismatch => "Signature type doesn't match key type".fmt(f), + } } } @@ -274,9 +277,9 @@ impl SigningPublicKey { SigType::Rsa2048Sha256 | SigType::Rsa3072Sha384 | SigType::Rsa4096Sha512 => { panic!("Online verifying not supported") } - SigType::Ed25519 => Ok(SigningPublicKey::Ed25519(Ed25519PublicKey::from_bytes( - data, - )?)), + SigType::Ed25519 => Ok(SigningPublicKey::Ed25519( + Ed25519PublicKey::from_bytes(data).map_err(|_| Error::InvalidKey)?, + )), } } @@ -286,9 +289,9 @@ impl SigningPublicKey { SigningPrivateKey::EcdsaSha256P256 => unimplemented!(), SigningPrivateKey::EcdsaSha384P384 => unimplemented!(), SigningPrivateKey::EcdsaSha512P521 => unimplemented!(), - SigningPrivateKey::Ed25519(ref seed) => Ok(SigningPublicKey::Ed25519(public_key( - &Ed25519Signer::from(seed), - )?)), + SigningPrivateKey::Ed25519(ref seed) => Ok(SigningPublicKey::Ed25519( + public_key(&Ed25519Signer::from(seed)).map_err(|_| Error::InvalidKey)?, + )), } } @@ -308,23 +311,22 @@ impl SigningPublicKey { if pk.verify(message, s) { Ok(()) } else { - Err(Error::Dsa) + Err(Error::InvalidSignature) } } (&SigningPublicKey::EcdsaSha256P256(ref pk), &Signature::EcdsaSha256P256(ref s)) => { - verify_sha256(&P256Verifier::from(pk), message, s).map_err(|e| e.into()) + verify_sha256(&P256Verifier::from(pk), message, s) + .map_err(|_| Error::InvalidSignature) } (&SigningPublicKey::EcdsaSha384P384(ref pk), &Signature::EcdsaSha384P384(ref s)) => { - verify_sha384(&P384Verifier::from(pk), message, s).map_err(|e| e.into()) + verify_sha384(&P384Verifier::from(pk), message, s) + .map_err(|_| Error::InvalidSignature) } (&SigningPublicKey::EcdsaSha512P521, &Signature::EcdsaSha512P521) => unimplemented!(), (&SigningPublicKey::Ed25519(ref pk), &Signature::Ed25519(ref s)) => { - verify(&Ed25519Verifier::from(pk), message, s).map_err(|e| e.into()) - } - _ => { - println!("Signature type doesn't match key type"); - Err(Error::TypeMismatch) + verify(&Ed25519Verifier::from(pk), message, s).map_err(|_| Error::InvalidSignature) } + _ => Err(Error::TypeMismatch), } } } @@ -365,7 +367,9 @@ impl SigningPrivateKey { SigType::Rsa2048Sha256 | SigType::Rsa3072Sha384 | SigType::Rsa4096Sha512 => { panic!("Online signing not supported") } - SigType::Ed25519 => Ok(SigningPrivateKey::Ed25519(Ed25519Seed::from_bytes(data)?)), + SigType::Ed25519 => Ok(SigningPrivateKey::Ed25519( + Ed25519Seed::from_bytes(data).map_err(|_| Error::InvalidKey)?, + )), } } @@ -385,9 +389,9 @@ impl SigningPrivateKey { SigningPrivateKey::EcdsaSha256P256 => unimplemented!(), SigningPrivateKey::EcdsaSha384P384 => unimplemented!(), SigningPrivateKey::EcdsaSha512P521 => unimplemented!(), - SigningPrivateKey::Ed25519(ref seed) => { - Ok(Signature::Ed25519(sign(&Ed25519Signer::from(seed), msg)?)) - } + SigningPrivateKey::Ed25519(ref seed) => Ok(Signature::Ed25519( + sign(&Ed25519Signer::from(seed), msg).map_err(|_| Error::SigningFailed)?, + )), } } } @@ -436,33 +440,30 @@ impl OfflineSigningPublicKey { pub fn verify(&self, message: &[u8], signature: &Signature) -> Result<(), Error> { match (self, signature) { (&OfflineSigningPublicKey::Rsa2048Sha256(ref pk), &Signature::Rsa2048Sha256(ref s)) => { - Ok(ring_signature::verify( + ring_signature::verify( &ring_signature::RSA_PKCS1_2048_8192_SHA256_RAW, untrusted::Input::from(pk), untrusted::Input::from(message), untrusted::Input::from(s), - )?) + ).map_err(|_| Error::InvalidSignature) } (&OfflineSigningPublicKey::Rsa3072Sha384(ref pk), &Signature::Rsa3072Sha384(ref s)) => { - Ok(ring_signature::verify( + ring_signature::verify( &ring_signature::RSA_PKCS1_3072_8192_SHA384_RAW, untrusted::Input::from(pk), untrusted::Input::from(message), untrusted::Input::from(s), - )?) + ).map_err(|_| Error::InvalidSignature) } (&OfflineSigningPublicKey::Rsa4096Sha512(ref pk), &Signature::Rsa4096Sha512(ref s)) => { - Ok(ring_signature::verify( + ring_signature::verify( &ring_signature::RSA_PKCS1_4096_8192_SHA512_RAW, untrusted::Input::from(pk), untrusted::Input::from(message), untrusted::Input::from(s), - )?) - } - _ => { - println!("Signature type doesn't match key type"); - Err(Error::TypeMismatch) + ).map_err(|_| Error::InvalidSignature) } + _ => Err(Error::TypeMismatch), } } } @@ -485,13 +486,15 @@ impl Signature { pub fn from_bytes(sig_type: SigType, data: &[u8]) -> Result { match sig_type { SigType::DsaSha1 => Ok(Signature::DsaSha1(dsa::DsaSignature::from_bytes(data)?)), - SigType::EcdsaSha256P256 => Ok(Signature::EcdsaSha256P256(FixedSignature::from_bytes( - data, - )?)), - SigType::EcdsaSha384P384 => Ok(Signature::EcdsaSha384P384(FixedSignature::from_bytes( - data, - )?)), - SigType::Ed25519 => Ok(Signature::Ed25519(Ed25519Signature::from_bytes(data)?)), + SigType::EcdsaSha256P256 => Ok(Signature::EcdsaSha256P256( + FixedSignature::from_bytes(data).map_err(|_| Error::InvalidSignature)?, + )), + SigType::EcdsaSha384P384 => Ok(Signature::EcdsaSha384P384( + FixedSignature::from_bytes(data).map_err(|_| Error::InvalidSignature)?, + )), + SigType::Ed25519 => Ok(Signature::Ed25519( + Ed25519Signature::from_bytes(data).map_err(|_| Error::InvalidSignature)?, + )), SigType::EcdsaSha512P521 | SigType::Rsa2048Sha256 | SigType::Rsa3072Sha384 diff --git a/src/data/frame.rs b/src/data/frame.rs index 5193c06..5e60e70 100644 --- a/src/data/frame.rs +++ b/src/data/frame.rs @@ -1,6 +1,6 @@ use cookie_factory::*; use itertools::Itertools; -use nom::{be_u16, be_u32, be_u64, be_u8, Err, ErrorKind}; +use nom::{be_u16, be_u32, be_u64, be_u8, Err, ErrorKind, IResult}; use super::*; use constants; diff --git a/src/data/mod.rs b/src/data/mod.rs index 299eee1..2faed4f 100644 --- a/src/data/mod.rs +++ b/src/data/mod.rs @@ -3,7 +3,7 @@ //! [Common structures specification](https://geti2p.net/spec/common-structures) use cookie_factory::GenError; -use nom::{Err, IResult}; +use nom::{self, Needed}; use rand::{OsRng, Rng}; use sha2::{Digest, Sha256}; use std::collections::HashMap; @@ -23,6 +23,39 @@ use crypto::{ #[allow(needless_pass_by_value)] pub(crate) mod frame; +/// Data read errors +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum ReadError { + FileIo(String), + Incomplete(Needed), + Parser, +} + +impl fmt::Display for ReadError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + ReadError::FileIo(e) => format!("File IO error: {}", e).fmt(f), + ReadError::Incomplete(n) => format!("Data is incomplete (needed: {:?})", n).fmt(f), + ReadError::Parser => "Parser error".fmt(f), + } + } +} + +impl From for ReadError { + fn from(e: io::Error) -> Self { + ReadError::FileIo(format!("{}", e)) + } +} + +impl From> for ReadError { + fn from(e: nom::Err) -> Self { + match e { + nom::Err::Incomplete(n) => ReadError::Incomplete(n), + _ => ReadError::Parser, + } + } +} + // // Simple data types // @@ -135,14 +168,6 @@ pub enum Certificate { } impl Certificate { - pub fn from(buf: &[u8]) -> Option { - match frame::certificate(buf) { - Ok((_, s)) => Some(s), - Err(Err::Incomplete(_)) => None, - Err(Err::Error(_)) | Err(Err::Failure(_)) => panic!("Unsupported Certificate"), - } - } - pub fn code(&self) -> u8 { match *self { Certificate::Null => constants::NULL_CERT, @@ -165,21 +190,12 @@ pub struct RouterIdentity { } impl RouterIdentity { - pub fn from_file(path: &str) -> io::Result { + pub fn from_file(path: &str) -> Result { let mut rid = File::open(path)?; let mut data: Vec = Vec::new(); rid.read_to_end(&mut data)?; - match frame::router_identity(&data[..]) { - Ok((_, res)) => Ok(res), - Err(Err::Incomplete(n)) => Err(io::Error::new( - io::ErrorKind::UnexpectedEof, - format!("needed: {:?}", n), - )), - Err(Err::Error(e)) | Err(Err::Failure(e)) => Err(io::Error::new( - io::ErrorKind::Other, - e.into_error_kind().description(), - )), - } + let (_, res) = frame::router_identity(&data[..])?; + Ok(res) } fn from_keys(public_key: PublicKey, signing_key: SigningPublicKey) -> Self { @@ -261,21 +277,12 @@ impl RouterSecretKeys { } } - pub fn from_file(path: &str) -> io::Result { + pub fn from_file(path: &str) -> Result { let mut rsk = File::open(path)?; let mut data: Vec = Vec::new(); rsk.read_to_end(&mut data)?; - match frame::router_secret_keys(&data[..]) { - Ok((_, res)) => Ok(res), - Err(Err::Incomplete(n)) => Err(io::Error::new( - io::ErrorKind::UnexpectedEof, - format!("needed: {:?}", n), - )), - Err(Err::Error(e)) | Err(Err::Failure(e)) => Err(io::Error::new( - io::ErrorKind::Other, - e.into_error_kind().description(), - )), - } + let (_, res) = frame::router_secret_keys(&data[..])?; + Ok(res) } pub fn to_bytes(&self) -> Vec { @@ -437,21 +444,12 @@ impl RouterInfo { .map(|a| (*a).clone()) } - pub fn from_file(path: &str) -> io::Result { + pub fn from_file(path: &str) -> Result { let mut ri = File::open(path)?; let mut data: Vec = Vec::new(); ri.read_to_end(&mut data)?; - match frame::router_info(&data[..]) { - Ok((_, res)) => Ok(res), - Err(Err::Incomplete(n)) => Err(io::Error::new( - io::ErrorKind::UnexpectedEof, - format!("needed: {:?}", n), - )), - Err(Err::Error(e)) | Err(Err::Failure(e)) => Err(io::Error::new( - io::ErrorKind::Other, - e.into_error_kind().description(), - )), - } + let (_, res) = frame::router_info(&data[..])?; + Ok(res) } pub fn to_bytes(&self) -> Vec { diff --git a/src/file/mod.rs b/src/file/mod.rs index 8401d90..ee26dca 100644 --- a/src/file/mod.rs +++ b/src/file/mod.rs @@ -2,23 +2,29 @@ use nom; use std::collections::HashMap; use crypto::{self, OfflineSigningPublicKey, SigType, Signature}; -use data::RouterInfo; +use data::{ReadError, RouterInfo}; mod frame; const SU3_MAGIC: &[u8; 6] = b"I2Psu3"; /// SU3 errors -#[derive(Debug)] -pub enum Error<'a> { - Nom(nom::Err<&'a [u8]>), +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum Error { + Crypto(crypto::Error), + Read(ReadError), UnknownSigner, - InvalidSignature(crypto::Error), } -impl<'a> From> for Error<'a> { - fn from(e: nom::Err<&'a [u8]>) -> Self { - Error::Nom(e) +impl From for Error { + fn from(e: crypto::Error) -> Self { + Error::Crypto(e) + } +} + +impl From> for Error { + fn from(e: nom::Err) -> Self { + Error::Read(e.into()) } } @@ -38,30 +44,28 @@ pub struct Su3File { } impl Su3File { - pub fn from_http_data<'a>( - input: &'a [u8], + pub fn from_http_data( + input: &[u8], signers: &HashMap<&'static str, OfflineSigningPublicKey>, - ) -> Result> { + ) -> Result { let (data, _) = take_until!(input, &SU3_MAGIC[..])?; Su3File::from_bytes(data, signers) } - pub fn from_bytes<'a>( - data: &'a [u8], + pub fn from_bytes( + data: &[u8], signers: &HashMap<&'static str, OfflineSigningPublicKey>, - ) -> Result> { + ) -> Result { let (_, su3_file) = frame::su3_file(data)?; // Verify the SU3 file signature - match if let Some(pk) = signers.get(&su3_file.signer.as_str()) { - pk.verify(&data[..su3_file.msg_len], &su3_file.sig) - .map_err(|e| Error::InvalidSignature(e)) + if let Some(pk) = signers.get(&su3_file.signer.as_str()) { + pk.verify(&data[..su3_file.msg_len], &su3_file.sig)?; } else { - Err(Error::UnknownSigner) - } { - Ok(()) => Ok(su3_file), - Err(e) => Err(e), + return Err(Error::UnknownSigner); } + + Ok(su3_file) } } diff --git a/src/netdb/mod.rs b/src/netdb/mod.rs index 2b84a09..bc6cde7 100644 --- a/src/netdb/mod.rs +++ b/src/netdb/mod.rs @@ -7,7 +7,7 @@ use std::time::Duration; use tokio_timer::sleep; use data::{Hash, LeaseSet, RouterInfo}; -use router::types::NetworkDatabase; +use router::types::{NetworkDatabase, NetworkDatabaseError}; pub mod reseed; @@ -100,10 +100,10 @@ impl NetworkDatabase for LocalNetworkDatabase { &mut self, key: &Hash, timeout_ms: u64, - ) -> Box> { + ) -> Box> { match self.ri_ds.get(key) { Some(ri) => Box::new(future::ok(ri.clone())), - None => Box::new(future::err(())), + None => Box::new(future::err(NetworkDatabaseError::NotFound)), } } @@ -112,14 +112,18 @@ impl NetworkDatabase for LocalNetworkDatabase { key: &Hash, timeout_ms: u64, from_local_dest: Option, - ) -> Box> { + ) -> Box> { match self.ls_ds.get(key) { Some(ls) => Box::new(future::ok(ls.clone())), - None => Box::new(future::err(())), + None => Box::new(future::err(NetworkDatabaseError::NotFound)), } } - fn store_router_info(&mut self, key: Hash, ri: RouterInfo) -> Result, ()> { + fn store_router_info( + &mut self, + key: Hash, + ri: RouterInfo, + ) -> Result, NetworkDatabaseError> { debug!( "Storing RouterInfo for peer {} at key {}", ri.router_id.hash(), @@ -128,7 +132,11 @@ impl NetworkDatabase for LocalNetworkDatabase { Ok(self.ri_ds.insert(key, ri)) } - fn store_lease_set(&mut self, key: Hash, ls: LeaseSet) -> Result, ()> { + fn store_lease_set( + &mut self, + key: Hash, + ls: LeaseSet, + ) -> Result, NetworkDatabaseError> { debug!("Storing LeaseSet at key {}", key); Ok(self.ls_ds.insert(key, ls)) } @@ -162,7 +170,7 @@ mod tests { match netdb.lookup_router_info(&key, 100).poll() { Ok(Async::Ready(entry)) => assert_eq!(entry, ri), Ok(_) => panic!("Local lookup should complete immediately"), - Err(_) => panic!("Error while looking up RouterInfo"), + Err(e) => panic!("Unexpected error: {}", e), } } } diff --git a/src/router/builder.rs b/src/router/builder.rs index 2d70ad8..05ebc7b 100644 --- a/src/router/builder.rs +++ b/src/router/builder.rs @@ -6,7 +6,7 @@ use super::{ types::{CommSystem, InboundMessageHandler, NetworkDatabase}, Config, Inner, MessageHandler, Router, }; -use data::{RouterInfo, RouterSecretKeys}; +use data::{ReadError, RouterInfo, RouterSecretKeys}; use netdb::LocalNetworkDatabase; use transport; @@ -31,7 +31,7 @@ impl<'a> Builder<'a> { } /// Create a Builder from the given Config. - pub fn from_config(cfg: Config) -> io::Result { + pub fn from_config(cfg: Config) -> Result { let ntcp_addr = cfg.ntcp_addr; let ntcp2_addr = cfg.ntcp2_addr; let ntcp2_keyfile = cfg.ntcp2_keyfile; diff --git a/src/router/types.rs b/src/router/types.rs index 5aef08b..e2e788c 100644 --- a/src/router/types.rs +++ b/src/router/types.rs @@ -1,6 +1,7 @@ //! The traits for the various router components. use futures::Future; +use std::fmt; use tokio_io::IoFuture; use data::{Hash, LeaseSet, RouterAddress, RouterInfo, RouterSecretKeys}; @@ -30,6 +31,20 @@ pub trait CommSystem: OutboundMessageHandler { fn start(&mut self, rsk: RouterSecretKeys) -> IoFuture<()>; } +/// Network database errors +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum NetworkDatabaseError { + NotFound, +} + +impl fmt::Display for NetworkDatabaseError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + NetworkDatabaseError::NotFound => "Key not found".fmt(f), + } + } +} + /// Defines the mechanism for interacting with I2P's network database. pub trait NetworkDatabase: Send + Sync { /// Returns the number of RouterInfos that this database contains. @@ -40,7 +55,7 @@ pub trait NetworkDatabase: Send + Sync { &mut self, key: &Hash, timeout_ms: u64, - ) -> Box>; + ) -> Box>; /// Finds the LeaseSet stored at the given key. If not known locally, the LeaseSet is /// looked up using the client tunnels for `from_local_dest` if provided, or @@ -50,15 +65,23 @@ pub trait NetworkDatabase: Send + Sync { key: &Hash, timeout_ms: u64, from_local_dest: Option, - ) -> Box>; + ) -> Box>; /// Stores a RouterInfo locally. /// /// Returns the RouterInfo that was previously at this key. - fn store_router_info(&mut self, key: Hash, ri: RouterInfo) -> Result, ()>; + fn store_router_info( + &mut self, + key: Hash, + ri: RouterInfo, + ) -> Result, NetworkDatabaseError>; /// Stores a LeaseSet locally. /// /// Returns the LeaseSet that was previously at this key. - fn store_lease_set(&mut self, key: Hash, ls: LeaseSet) -> Result, ()>; + fn store_lease_set( + &mut self, + key: Hash, + ls: LeaseSet, + ) -> Result, NetworkDatabaseError>; }