diff --git a/.lock b/.lock new file mode 100644 index 00000000..e69de29b diff --git a/.nojekyll b/.nojekyll new file mode 100644 index 00000000..e69de29b diff --git a/crates.js b/crates.js new file mode 100644 index 00000000..a72b8967 --- /dev/null +++ b/crates.js @@ -0,0 +1 @@ +window.ALL_CRATES = ["vodozemac"]; \ No newline at end of file diff --git a/help.html b/help.html new file mode 100644 index 00000000..fcb07ca5 --- /dev/null +++ b/help.html @@ -0,0 +1,2 @@ +Help +

Rustdoc help

Back
\ No newline at end of file diff --git a/index.html b/index.html new file mode 100644 index 00000000..04028966 --- /dev/null +++ b/index.html @@ -0,0 +1,2 @@ +Index of crates +

List of all crates

\ No newline at end of file diff --git a/search-index.js b/search-index.js new file mode 100644 index 00000000..7471aff9 --- /dev/null +++ b/search-index.js @@ -0,0 +1,5 @@ +var searchIndex = new Map(JSON.parse('[\ +["vodozemac",{"doc":"A Rust implementation of Olm and Megolm","t":"PPPPGPFFPGPPFFFFPPPPPPPPPPGFTTTTGPPPPPGFPPPFPPPGJPNNNNHHNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNCNNNCNNNCNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNOOOPPFGPFFFFFPPPFPPFFGGPPPPPPNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNONNNNONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNFFPGFFPPPFGPPPPPGFPPFFFFGFFPNNONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNOONNNNNNNNNNNNNONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNONNNNNNNNNNNNNNNNNNNNNNNNNONNNONONNNNNNNNONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNFFFPFFGNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN","n":["Base64","Base64","Base64","Base64","Base64DecodeError","Base64Error","Curve25519PublicKey","Curve25519SecretKey","Decode","DecodeError","Decryption","Decryption","Ed25519Keypair","Ed25519PublicKey","Ed25519SecretKey","Ed25519Signature","Encode","InvalidByte","InvalidKey","InvalidKeyLength","InvalidLastSymbol","InvalidLength","InvalidMacLength","InvalidPadding","InvalidSession","InvalidVersion","KeyError","KeyId","LENGTH","LENGTH","LENGTH","LENGTH","LibolmPickleError","MessageTooShort","MessageType","MissingVersion","MissingVersion","NonContributoryKey","PickleError","ProtoBufDecodeError","ProtoBufError","PublicKey","Serialization","SharedSecret","Signature","Signature","Signature","SignatureError","VERSION","Version","as_bytes","as_bytes","as_bytes","as_ref","base64_decode","base64_encode","borrow","borrow","borrow","borrow","borrow","borrow","borrow","borrow","borrow","borrow","borrow","borrow","borrow","borrow","borrow","borrow_mut","borrow_mut","borrow_mut","borrow_mut","borrow_mut","borrow_mut","borrow_mut","borrow_mut","borrow_mut","borrow_mut","borrow_mut","borrow_mut","borrow_mut","borrow_mut","borrow_mut","clone","clone","clone","clone","clone","clone","clone","clone","clone_into","clone_into","clone_into","clone_into","clone_into","clone_into","clone_into","clone_into","cmp","decode","default","default","default","deserialize","deserialize","deserialize","deserialize","deserialize","deserialize","diffie_hellman","eq","eq","eq","eq","eq","eq","fmt","fmt","fmt","fmt","fmt","fmt","fmt","fmt","fmt","fmt","fmt","fmt","fmt","fmt","fmt","fmt","fmt","fmt","fmt","fmt","fmt","from","from","from","from","from","from","from","from","from","from","from","from","from","from","from","from","from","from","from","from","from","from","from","from","from","from","from","from","from","from","from","from","from","from_base64","from_base64","from_base64","from_base64","from_bytes","from_slice","from_slice","from_slice","from_slice","from_slice","hash","hash","into","into","into","into","into","into","into","into","into","into","into","into","into","into","into","megolm","new","new","new","olm","partial_cmp","public_key","public_key","sas","serialize","serialize","serialize","serialize","serialize","serialize","sign","sign","source","source","source","source","source","to_base64","to_base64","to_base64","to_base64","to_base64","to_bytes","to_bytes","to_bytes","to_bytes","to_bytes","to_owned","to_owned","to_owned","to_owned","to_owned","to_owned","to_owned","to_owned","to_string","to_string","to_string","to_string","to_string","to_string","to_string","to_string","to_string","to_string","to_vec","try_from","try_from","try_from","try_from","try_from","try_from","try_from","try_from","try_from","try_from","try_from","try_from","try_from","try_from","try_from","try_into","try_into","try_into","try_into","try_into","try_into","try_into","try_into","try_into","try_into","try_into","try_into","try_into","try_into","try_into","type_id","type_id","type_id","type_id","type_id","type_id","type_id","type_id","type_id","type_id","type_id","type_id","type_id","type_id","type_id","verify","vzip","vzip","vzip","vzip","vzip","vzip","vzip","vzip","vzip","vzip","vzip","vzip","vzip","vzip","vzip","was_contributory","zeroize","expected_length","key_type","length","Base64","Better","DecryptedMessage","DecryptionError","Equal","ExportedSessionKey","GroupSession","GroupSessionPickle","InboundGroupSession","InboundGroupSessionPickle","InvalidMAC","InvalidMACLength","InvalidPadding","MegolmMessage","PublicKey","Read","SessionConfig","SessionKey","SessionKeyDecodeError","SessionOrdering","Signature","Signature","Unconnected","UnknownMessageIndex","Version","Worse","advance_to","borrow","borrow","borrow","borrow","borrow","borrow","borrow","borrow","borrow","borrow","borrow","borrow","borrow_mut","borrow_mut","borrow_mut","borrow_mut","borrow_mut","borrow_mut","borrow_mut","borrow_mut","borrow_mut","borrow_mut","borrow_mut","borrow_mut","ciphertext","clone","clone","clone","clone","clone_into","clone_into","clone_into","clone_into","compare","connected","decrypt","default","default","deserialize","deserialize","deserialize","deserialize","deserialize","deserialize","deserialize","drop","drop","encrypt","encrypt","encrypt","eq","eq","eq","eq","export_at","export_at_first_known_index","first_known_index","fmt","fmt","fmt","fmt","fmt","fmt","fmt","fmt","from","from","from","from","from","from","from","from","from","from","from","from","from","from","from","from","from","from","from","from","from","from","from","from_base64","from_base64","from_base64","from_bytes","from_bytes","from_bytes","from_encrypted","from_encrypted","from_libolm_pickle","from_libolm_pickle","from_pickle","from_pickle","import","into","into","into","into","into","into","into","into","into","into","into","into","mac","merge","message_index","message_index","message_index","new","new","pickle","pickle","plaintext","serialize","serialize","serialize","serialize","serialize","serialize","session_config","session_id","session_id","session_key","signature","source","source","to_base64","to_base64","to_base64","to_bytes","to_bytes","to_bytes","to_owned","to_owned","to_owned","to_owned","to_string","to_string","try_from","try_from","try_from","try_from","try_from","try_from","try_from","try_from","try_from","try_from","try_from","try_from","try_from","try_from","try_from","try_from","try_from","try_from","try_from","try_into","try_into","try_into","try_into","try_into","try_into","try_into","try_into","try_into","try_into","try_into","try_into","type_id","type_id","type_id","type_id","type_id","type_id","type_id","type_id","type_id","type_id","type_id","type_id","version","version_1","version_2","vzip","vzip","vzip","vzip","vzip","vzip","vzip","vzip","vzip","vzip","vzip","vzip","zeroize","zeroize","Account","AccountPickle","Decryption","DecryptionError","IdentityKeys","InboundCreationResult","InvalidMAC","InvalidMACLength","InvalidPadding","Message","MessageType","MismatchedIdentityKey","MissingMessageKey","MissingOneTimeKey","Normal","Normal","OlmMessage","OneTimeKeyGenerationResult","PreKey","PreKey","PreKeyMessage","RatchetPublicKey","Session","SessionConfig","SessionCreationError","SessionKeys","SessionPickle","TooBigMessageGap","as_ref","base_key","base_key","borrow","borrow","borrow","borrow","borrow","borrow","borrow","borrow","borrow","borrow","borrow","borrow","borrow","borrow","borrow","borrow","borrow_mut","borrow_mut","borrow_mut","borrow_mut","borrow_mut","borrow_mut","borrow_mut","borrow_mut","borrow_mut","borrow_mut","borrow_mut","borrow_mut","borrow_mut","borrow_mut","borrow_mut","borrow_mut","chain_index","ciphertext","clone","clone","clone","clone","clone","clone","clone","clone","clone_into","clone_into","clone_into","clone_into","clone_into","clone_into","clone_into","clone_into","create_inbound_session","create_outbound_session","created","curve25519","curve25519_key","decode","decrypt","default","default","deserialize","deserialize","deserialize","deserialize","deserialize","deserialize","deserialize","deserialize","ed25519","ed25519_key","encrypt","encrypt","encrypt","eq","eq","eq","eq","eq","eq","eq","eq","fallback_key","fmt","fmt","fmt","fmt","fmt","fmt","fmt","fmt","fmt","fmt","fmt","fmt","fmt","fmt","forget_fallback_key","from","from","from","from","from","from","from","from","from","from","from","from","from","from","from","from","from","from","from","from","from","from","from","from","from_base64","from_base64","from_bytes","from_bytes","from_encrypted","from_encrypted","from_libolm_pickle","from_libolm_pickle","from_parts","from_pickle","from_pickle","generate_fallback_key","generate_one_time_keys","has_received_message","identity_key","identity_key","identity_keys","into","into","into","into","into","into","into","into","into","into","into","into","into","into","into","into","mac_truncated","mark_keys_as_published","max_number_of_one_time_keys","message","message","message_type","new","one_time_key","one_time_key","one_time_keys","pickle","pickle","plaintext","ratchet_key","removed","serialize","serialize","serialize","serialize","serialize","serialize","serialize","serialize","session","session_config","session_id","session_id","session_id","session_keys","session_keys","sign","source","source","stored_one_time_key_count","to_base64","to_base64","to_bytes","to_bytes","to_libolm_pickle","to_owned","to_owned","to_owned","to_owned","to_owned","to_owned","to_owned","to_owned","to_parts","to_string","to_string","try_from","try_from","try_from","try_from","try_from","try_from","try_from","try_from","try_from","try_from","try_from","try_from","try_from","try_from","try_from","try_from","try_from","try_from","try_from","try_from","try_from","try_from","try_from","try_into","try_into","try_into","try_into","try_into","try_into","try_into","try_into","try_into","try_into","try_into","try_into","try_into","try_into","try_into","try_into","type_id","type_id","type_id","type_id","type_id","type_id","type_id","type_id","type_id","type_id","type_id","type_id","type_id","type_id","type_id","type_id","version","version","version_1","version_2","vzip","vzip","vzip","vzip","vzip","vzip","vzip","vzip","vzip","vzip","vzip","vzip","vzip","vzip","vzip","vzip","EstablishedSas","InvalidCount","Mac","Mac","Sas","SasBytes","SasError","as_bytes","as_bytes","borrow","borrow","borrow","borrow","borrow","borrow","borrow_mut","borrow_mut","borrow_mut","borrow_mut","borrow_mut","borrow_mut","bytes","bytes_raw","calculate_mac","calculate_mac_invalid_base64","clone","clone","clone_into","clone_into","decimals","default","diffie_hellman","diffie_hellman_with_raw","emoji_indices","eq","fmt","fmt","fmt","fmt","fmt","fmt","from","from","from","from","from","from","from","from_base64","from_slice","into","into","into","into","into","into","new","our_public_key","public_key","source","their_public_key","to_base64","to_owned","to_owned","to_string","to_string","try_from","try_from","try_from","try_from","try_from","try_from","try_into","try_into","try_into","try_into","try_into","try_into","type_id","type_id","type_id","type_id","type_id","type_id","verify_mac","vzip","vzip","vzip","vzip","vzip","vzip"],"q":[[0,"vodozemac"],[315,"vodozemac::KeyError"],[318,"vodozemac::megolm"],[553,"vodozemac::olm"],[862,"vodozemac::sas"],[953,"alloc::vec"],[954,"core::result"],[955,"core::convert"],[956,"alloc::string"],[957,"core::cmp"],[958,"matrix_pickle::error"],[959,"std::io"],[960,"serde::de"],[961,"core::fmt"],[962,"core::fmt"],[963,"x25519_dalek::x25519"],[964,"serde_json::error"],[965,"matrix_pickle::error"],[966,"core::option"],[967,"serde::ser"],[968,"core::error"],[969,"alloc::boxed"],[970,"core::any"],[971,"digest::mac"],[972,"block_padding"],[973,"std::io::error"],[974,"std::collections::hash::map"]],"d":["The signature wasn’t valid base64.","The pickle wasn’t valid base64.","The pickle wasn’t valid base64.","The message wasn’t valid base64.","Errors that can occur while decoding.","","Struct representing a Curve25519 public key.","Struct representing a Curve25519 secret key.","The payload of the pickle could not be decoded.","Error type describing the different ways message decoding …","The encrypted pickle could not have been decrypted.","The pickle could not have been decrypted.","A struct collecting both a public, and a secret, Ed25519 …","An Ed25519 public key, used to verify digital signatures.","An Ed25519 secret key, used to create digital signatures.","An Ed25519 digital signature, can be used to verify the …","The object could not be encoded as a pickle.","An invalid byte was found in the input. The offset and …","An embedded public key couldn’t be decoded.","","The last non-padding input symbol’s encoded 6 bits have …","The length of the input is invalid. A typical cause of …","The embedded message authentication code couldn’t be …","The nature of the padding was not as configured: absent or …","The pickle does not contain a valid receiving or sending …","The message has a unsupported version.","Error type describing failures that can happen when we try …","","The number of bytes a Curve25519 public key has.","The number of bytes a Ed25519 secret key has.","The number of bytes a Ed25519 public key has.","The number of bytes a Ed25519 signature has.","Error type describing the various ways libolm pickles can …","The message doesn’t have enough data to be correctly …","The Olm message has an invalid type.","The pickle is missing a valid version.","The message is missing a valid version.","At least one of the keys did not have contributory …","Error type describing the various ways Vodozemac pickles …","A Protobuf message decoding error.","The message couldn’t be decoded as a valid protocol …","The pickle contains an invalid public key.","The serialized Vodozemac object couldn’t be deserialized.","The result of a Diffie-Hellman key exchange.","The signature failed to be verified.","","An embedded signature couldn’t be decoded.","Error type describing signature verification failures.","The version of vodozemac that is being used.","The pickle has a unsupported version.","View this shared secret key as a byte array.","View this public key as a byte array.","View this public key as a byte array.","View this shared secret key as a byte array.","Decode the input as base64 with no padding.","Encode the input as base64 with no padding.","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","Perform a Diffie-Hellman key exchange between the given …","","","","","","","","","","","","","","","","","","","","","","","","","","","","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","","","","Returns the argument unchanged.","","Returns the argument unchanged.","","","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","","Returns the argument unchanged.","","","","Returns the argument unchanged.","Returns the argument unchanged.","","","","","","","","Returns the argument unchanged.","","Instantiate a Curve25519 public key from an unpadded base64","Try to create a Ed25519SecretKey from a base64 encoded …","Instantiate a Ed25519PublicKey public key from an unpadded …","Try to create a Ed25519Signature from an unpadded base64 …","Create a Curve25519PublicKey from a byte array.","Create a Curve25519SecretKey from the given slice of bytes.","Try to create a Curve25519PublicKey from a slice of bytes.","Try to create a Ed25519SecretKey from a slice of bytes.","Try to create a Ed25519PublicKey from a slice of bytes.","Try to create a Ed25519Signature from a slice of bytes.","","","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","An implementation of the Megolm ratchet.","Generate a new, random, Curve25519SecretKey.","Create a new, random, Ed25519Keypair.","Create a new random Ed25519SecretKey.","An implementation of the Olm double ratchet.","","Get the public Ed25519 key of this keypair.","Get the public key that matches this Ed25519SecretKey.","User-friendly key verification using short authentication …","","","","","","","Sign the given message with our secret key.","Sign the given slice of bytes with this Ed25519SecretKey.","","","","","","Serialize a Curve25519 public key to an unpadded base64 …","Convert the secret key to a base64 encoded string.","Serialize a Ed25519PublicKey public key to an unpadded …","Serialize an Ed25519Signature to an unpadded base64 …","","Convert this shared secret to a byte array.","Convert the Curve25519SecretKey to a byte array.","Convert this public key to a byte array.","Get the byte representation of the secret key.","Convert the Ed25519Signature to a byte array.","","","","","","","","","","","","","","","","","","","Convert the public key to a vector of bytes.","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","Verify that the provided signature for a given message has …","","","","","","","","","","","","","","","","Ensure in constant-time that this shared secret did not …","","","","","The encoded session key wasn’t valid base64.","The first session has a better initial message index than …","","Error type for Megolm-based decryption failures.","The sessions are the same.","The exported session key.","A Megolm group session represents a single sending …","A format suitable for serialization which implements …","","A format suitable for serialization which implements …","The message authentication code of the message was invalid.","The length of the message authentication code of the …","The ciphertext of the message isn’t padded correctly.","An encrypted Megolm message.","The encoded session key contains an invalid public key.","The encoded session key didn’t contain enough data to be …","A struct to configure how Megolm sessions should work …","The session key, can be used to create a …","Error type describing failure modes for the SessionKey and …","The result of a comparison between two InboundGroupSession …","The signature on the message was invalid.","The signature on the session key was invalid.","The sessions are not the same, they can’t be compared.","The session is missing the correct message key to decrypt …","The encoded session key had a unsupported version.","The first session has a worse initial message index than …","Permanently advance the session to the given index.","","","","","","","","","","","","","","","","","","","","","","","","","The actual ciphertext of the message.","","","","","","","","","Compare the InboundGroupSession with the given other …","Check if two InboundGroupSessions are the same.","","","","","","","","","","","","","Encrypt the plaintext with the group session.","Serialize and encrypt the pickle using the given key.","Serialize and encrypt the pickle using the given key.","","","","","","","","","","","","","","","","Returns the argument unchanged.","","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","","","","Returns the argument unchanged.","Returns the argument unchanged.","","","Returns the argument unchanged.","","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","","","","Returns the argument unchanged.","","Deserialize the ExportedSessionKey from base64 encoded …","Deserialize the SessionKey from base64 encoded string.","Try to decode the given string as a MegolmMessage.","Deserialize the ExportedSessionKey from a byte slice.","Deserialize the SessionKey from a byte slice.","Try to decode the given byte slice as a MegolmMessage.","Obtain a pickle from a ciphertext by decrypting and …","Obtain a pickle from a ciphertext by decrypting and …","","","Restore a GroupSession from a previously saved …","Restore an InboundGroupSession from a previously saved …","","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Get the megolm message’s mac.","Merge the session with the given other session, picking …","Return the current message index.","The index of the message that was used when the message …","","Construct a new group session, with a random ratchet state …","","Convert the group session into a struct which implements …","Convert the inbound group session into a struct which …","","","","","","","","","Returns the globally unique session ID, in base64-encoded …","","Export the group session into a session key.","Get a reference to the megolm message’s signature.","","","Serialize the ExportedSessionKey to a base64 encoded …","Serialize the SessionKey to a base64 encoded string.","Encode the MegolmMessage as a string.","Serialize the ExportedSessionKey to a byte vector.","Serialize the SessionKey to a byte vector.","Encode the MegolmMessage as an array of bytes.","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","Get the numeric version of this SessionConfig.","Create a SessionConfig for the Megolm version 1. This …","Create a SessionConfig for the Megolm version 2. This …","","","","","","","","","","","","","","","An Olm account manages all cryptographic keys used on a …","A format suitable for serialization which implements …","The pre-key message that was used to establish the Session …","Error type for Olm-based decryption failures.","Struct holding the two public identity keys of an Account.","Return type for the creation of inbound Session objects.","The message authentication code of the message was invalid.","The length of the message authentication code of the …","The ciphertext of the message isn’t padded correctly.","An encrypted Olm message.","An enum over the two supported message types.","The pre-key message contains a curve25519 identity key …","The session is missing the correct message key to decrypt …","The pre-key message contained an unknown one-time key. …","A normal message, contains only the ciphertext and …","The normal message type.","Enum over the different Olm message types.","The result type for the one-time key generation operation.","A pre-key message, contains metadata to establish a Session…","The pre-key message type.","An encrypted Olm pre-key message.","","An Olm session represents one end of an encrypted …","A struct to configure how Olm sessions should work under …","Error describing failure modes when creating a Olm Session …","The set of keys that were used to establish the Olm …","A format suitable for serialization which implements …","Too many messages have been skipped to attempt decrypting …","","The base key, a single use key that was created just in …","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","The index of the chain that was used when the message was …","The actual ciphertext of the message.","","","","","","","","","","","","","","","","","Create a Session from the given pre-key message and …","Create a Session with the given identity key and one-time …","The public part of the one-time keys that were newly …","The curve25519 key, used for to establish shared secrets.","Get a reference to the account’s public Curve25519 key","","Try to decrypt an Olm message, which will either return …","","","","","","","","","","","The ed25519 key, used for signing.","Get a reference to the account’s public Ed25519 key","Encrypt the plaintext and construct an OlmMessage.","Serialize and encrypt the pickle using the given key.","Serialize and encrypt the pickle using the given key.","","","","","","","","","Get the currently unpublished fallback key.","","","","","","","","","","","","","","","The Account stores at most two private parts of the …","Returns the argument unchanged.","","Returns the argument unchanged.","Returns the argument unchanged.","","","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","","Returns the argument unchanged.","","Returns the argument unchanged.","","Returns the argument unchanged.","","","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","Try to decode the given string as a Olm Message.","Try to decode the given string as a Olm PreKeyMessage.","Try to decode the given byte slice as a Olm Message.","Try to decode the given byte slice as a Olm Message.","Obtain a pickle from a ciphertext by decrypting and …","Obtain a pickle from a ciphertext by decrypting and …","Create an Account object by unpickling an account pickle …","Create a Session object by unpickling a session pickle in …","Create a OlmMessage from a message type and a ciphertext.","Restore an Account from a previously saved AccountPickle.","Restore a Session from a previously saved SessionPickle.","Generate a single new fallback key.","Generates the supplied number of one time keys. Returns …","Have we ever received and decrypted a message from the …","The long term identity key of the sender of the message. …","","Get the IdentityKeys of this Account","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Has the MAC been truncated in this Olm message.","Mark all currently unpublished one-time and fallback keys …","Get the maximum number of one-time keys the client should …","The actual message that contains the ciphertext.","Get the message as a byte array.","Get the type of the message.","Create a new Account with new random identity keys.","The single-use key that was uploaded to a public key …","","Get the currently unpublished one-time keys.","Convert the account into a struct which implements …","Convert the session into a struct which implements …","The plaintext of the pre-key message.","The public part of the ratchet key, that was used when the …","The public part of the one-time keys that had to be …","","","","","","","","","The Session that was created from a pre-key message.","","Returns the globally unique session ID, in base64-encoded …","Returns the globally unique session ID, in base64-encoded …","Returns the globally unique session ID which these …","Get the keys associated with this session.","The collection of all keys required for establishing an …","Sign the given message using our Ed25519 fingerprint key.","","","","Encode the Message as a string.","Encode the PreKeyMessage as a string.","Encode the Message as an array of bytes.","Encode the PreKeyMessage as an array of bytes.","Pickle an Account into a libolm pickle format.","","","","","","","","","Convert the OlmMessage into a message type, and base64 …","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","The version of the Olm message.","Get the numeric version of this SessionConfig.","Create a SessionConfig for the Olm version 1. This version …","Create a SessionConfig for the Olm version 2. This version …","","","","","","","","","","","","","","","","","A struct representing a short auth string verification …","Error type for the case when we try to generate too many …","The output type for the SAS MAC calculation.","The MAC failed to be validated.","A struct representing a short auth string verification …","Bytes generated from an shared secret that can be used as …","Error type describing failures that can happen during the …","Get the byte slice of the MAC.","Get the raw bytes of the short auth string that can be …","","","","","","","","","","","","","Generate SasBytes using HKDF with the shared secret as the …","Generate the given number of bytes using HKDF with the …","Calculate a MAC for the given input using the info string …","Calculate a MAC for the given input using the info string …","","","","","Get the three decimal numbers that can be presented to …","","Establishes a SAS secret by performing a DH handshake with …","Establishes a SAS secret by performing a DH handshake with …","Get the index of 7 emojis that can be presented to users …","","","","","","","","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","","Returns the argument unchanged.","Returns the argument unchanged.","Create a new Mac object from a base64 encoded string.","Create a new Mac object from a byte slice.","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Create a new random verification object","Get the public key that was created by us, that was used …","Get the public key that can be used to establish a shared …","","Get the public key that was created by the other party, …","Convert the MAC to a base64 encoded string.","","","","","","","","","","","","","","","","","","","","","","","Verify a MAC that was previously created using the …","","","","","",""],"i":[27,29,30,31,0,28,0,0,30,0,29,30,0,0,0,0,30,8,31,28,8,8,31,8,30,31,0,0,4,21,5,15,0,31,31,30,31,28,0,0,31,30,29,0,27,28,31,0,0,30,1,4,5,1,0,0,1,12,8,13,4,27,14,21,5,15,16,28,29,30,31,1,12,8,13,4,27,14,21,5,15,16,28,29,30,31,12,8,13,4,14,5,15,16,12,8,13,4,14,5,15,16,16,4,13,14,21,13,4,14,21,5,16,13,12,8,4,5,15,16,12,12,8,8,4,4,27,27,5,5,15,15,16,28,28,29,29,30,30,31,31,1,12,8,13,4,4,4,4,4,27,27,27,14,21,5,15,16,28,28,28,29,29,29,30,30,30,30,30,31,31,31,31,31,4,21,5,15,4,13,4,21,5,15,4,16,1,12,8,13,4,27,14,21,5,15,16,28,29,30,31,0,13,14,21,0,16,14,21,0,13,4,14,21,5,16,14,21,27,28,29,30,31,4,21,5,15,16,1,13,4,21,15,12,8,13,4,14,5,15,16,12,8,4,27,5,15,28,29,30,31,4,1,12,8,13,4,27,14,21,5,15,16,28,29,30,31,1,12,8,13,4,27,14,21,5,15,16,28,29,30,31,1,12,8,13,4,27,14,21,5,15,16,28,29,30,31,5,1,12,8,13,4,27,14,21,5,15,16,28,29,30,31,1,1,87,87,87,56,47,0,0,47,0,0,0,0,0,50,50,50,0,56,56,0,0,0,0,50,56,47,50,56,47,44,51,52,53,54,47,50,44,48,55,46,49,56,51,52,53,54,47,50,44,48,55,46,49,56,46,47,48,46,49,47,48,46,49,44,44,44,51,49,52,53,54,44,55,46,49,52,53,51,54,55,47,48,46,49,44,44,44,47,50,50,48,46,49,56,56,51,51,52,53,54,47,50,50,50,50,44,44,44,48,55,55,46,49,56,56,56,56,56,52,53,46,52,53,46,54,55,51,44,51,44,44,51,52,53,54,47,50,44,48,55,46,49,56,46,44,51,46,48,51,44,51,44,48,52,53,54,55,46,49,51,51,44,51,46,50,56,52,53,46,52,53,46,47,48,46,49,50,56,51,52,52,52,53,53,53,54,47,50,44,48,55,46,46,46,46,49,56,51,52,53,54,47,50,44,48,55,46,49,56,51,52,53,54,47,50,44,48,55,46,49,56,49,49,49,51,52,53,54,47,50,44,48,55,46,49,56,52,53,0,0,71,0,0,0,73,73,73,0,0,71,73,71,65,66,0,0,65,66,0,0,0,0,0,0,0,73,60,61,68,78,69,72,71,64,70,74,62,61,65,66,60,73,75,67,68,78,69,72,71,64,70,74,62,61,65,66,60,73,75,67,68,62,62,64,62,61,65,66,60,67,68,64,62,61,65,66,60,67,68,69,69,78,64,69,68,72,69,67,64,74,62,61,65,75,67,68,64,69,72,74,75,64,62,61,65,66,60,67,68,69,72,71,71,64,70,62,61,65,66,60,73,73,67,68,69,78,69,69,72,72,71,71,64,70,74,62,61,65,65,65,66,60,60,73,73,73,75,67,68,62,61,62,61,74,75,69,72,65,69,72,69,69,72,61,68,69,78,69,72,71,64,70,74,62,61,65,66,60,73,75,67,68,62,69,69,61,65,65,69,61,68,69,69,72,70,62,78,64,74,62,61,65,75,67,68,70,72,72,61,68,72,61,69,71,73,69,62,61,62,61,69,64,62,61,65,66,60,67,68,65,71,73,78,69,72,71,64,70,74,62,62,62,62,61,61,61,61,65,66,66,60,73,75,67,68,78,69,72,71,64,70,74,62,61,65,66,60,73,75,67,68,78,69,72,71,64,70,74,62,61,65,66,60,73,75,67,68,62,67,67,67,78,69,72,71,64,70,74,62,61,65,66,60,73,75,67,68,0,0,0,86,0,0,0,80,81,80,85,82,83,86,81,80,85,82,83,86,81,82,82,82,82,83,81,83,81,81,85,85,85,81,81,82,83,83,86,86,81,80,85,82,83,86,86,81,80,80,80,85,82,83,86,81,85,82,85,86,82,80,83,81,83,86,80,85,82,83,86,81,80,85,82,83,86,81,80,85,82,83,86,81,82,80,85,82,83,86,81],"f":"``````````````````````````````````````````````````{b{{f{d}}}}{h{{f{d}}}}{j{{f{d}}}}{b{{l{d}}}}{c{{Ab{{n{d}}A`}}}{{Ad{{l{d}}}}}}{cAf{{Ad{{l{d}}}}}}{ce{}{}}00000000000000000000000000000{AhAh}{A`A`}{AjAj}{hh}{AlAl}{jj}{AnAn}{B`B`}{{ce}Bb{}{}}0000000{{B`B`}Bd}{c{{Ab{hBf}}}Bh}{{}Aj}{{}Al}{{}Bj}{c{{Ab{Aj}}}Bl}{c{{Ab{h}}}Bl}{c{{Ab{Al}}}Bl}{c{{Ab{Bj}}}Bl}{c{{Ab{j}}}Bl}{c{{Ab{B`}}}Bl}{{Ajh}b}{{AhAh}Bn}{{A`A`}Bn}{{hh}Bn}{{jj}Bn}{{AnAn}Bn}{{B`B`}Bn}{{AhC`}{{Ab{BbCb}}}}0{{A`C`}{{Ab{BbCb}}}}0{{hC`}Cd}0{{CfC`}Cd}0{{jC`}Cd}0{{AnC`}Cd}0{{B`C`}Cd}{{ChC`}Cd}0{{CjC`}Cd}0{{ClC`}Cd}0{{CnC`}Cd}0{cc{}}000{Ajh}{D`h}{{{f{d}}}h}3{Dbh}4{A`Cf}{DdCf}66666{CfCh}7{A`Ch}{A`Cj}{DfCj}::{DhCl}{BfCl}{A`Cl}{ChCl}{CfCn}{AhCn}{A`Cn}{cc{}}{ChCn}{Dj{{Ab{hCh}}}}{Dj{{Ab{BjCh}}}}{Dj{{Ab{jCh}}}}{Dj{{Ab{AnCf}}}}{{{f{d}}}h}{{{f{d}}}Aj}{{{l{d}}}{{Ab{hCh}}}}{{{f{d}}}Bj}{{{f{d}}}{{Ab{jCh}}}}{{{l{d}}}{{Ab{AnCf}}}}{{hc}BbDl}{{B`c}BbDl}{ce{}{}}00000000000000`{{}Aj}{{}Al}{{}Bj}`{{B`B`}{{Dn{Bd}}}}{Alj}{Bjj}`{{Ajc}AbE`}{{hc}AbE`}{{Alc}AbE`}{{Bjc}AbE`}{{jc}AbE`}{{B`c}AbE`}{{Al{l{d}}}An}{{Bj{l{d}}}An}{Cf{{Dn{Eb}}}}{Ch{{Dn{Eb}}}}{Cj{{Dn{Eb}}}}{Cl{{Dn{Eb}}}}{Cn{{Dn{Eb}}}}{hAf}{BjAf}{jAf}{AnAf}{B`Af}{b{{f{d}}}}{Aj{{Ed{{f{d}}}}}}{h{{f{d}}}}{Bj{{Ed{{f{d}}}}}}{An{{f{d}}}}{ce{}{}}0000000{cAf{}}000000000{h{{n{d}}}}{c{{Ab{e}}}{}{}}00000000000000000000000000000{cEf{}}00000000000000{{j{l{d}}An}{{Ab{BbCf}}}}555555555555555{bBn}{bBb}`````````````````````````````{{EhEj}Bn}888888888888888888888888{El{{l{d}}}}{EnEn}{F`F`}{ElEl}{FbFb}{{ce}Bb{}{}}000{{EhEh}En}{{EhEh}Bn}{{EhEl}{{Ab{F`Fd}}}}{{}Ff}{{}Fb}{c{{Ab{Fh}}}Bl}{c{{Ab{Fj}}}Bl}{c{{Ab{Fl}}}Bl}{c{{Ab{Eh}}}Bl}{c{{Ab{Fn}}}Bl}{c{{Ab{El}}}Bl}{c{{Ab{Fb}}}Bl}{FhBb}{FjBb}{{Ffc}El{{Ad{{l{d}}}}}}{{Fl{f{d}}}Af}{{Fn{f{d}}}Af}{{EnEn}Bn}{{F`F`}Bn}{{ElEl}Bn}{{FbFb}Bn}{{EhEj}{{Dn{Fh}}}}{EhFh}{EhEj}{{EnC`}Cd}{{FdC`}Cd}0{{F`C`}Cd}{{ElC`}Cd}{{FbC`}Cd}{{G`C`}Cd}0{cc{}}{FlFf}1111{GbFd}{GdFd}{CfFd}44{FfEh}{FnEh}6{EhFn}777{GfG`}{CfG`}{A`G`}:{ChG`}{Dj{{Ab{FhG`}}}}{Dj{{Ab{FjG`}}}}{Dj{{Ab{ElCn}}}}{{{l{d}}}{{Ab{FhG`}}}}{{{l{d}}}{{Ab{FjG`}}}}{{{l{d}}}{{Ab{ElCn}}}}{{Dj{f{d}}}{{Ab{FlCj}}}}{{Dj{f{d}}}{{Ab{FnCj}}}}{{Dj{l{d}}}{{Ab{FfCl}}}}{{Dj{l{d}}}{{Ab{EhCl}}}}{FlFf}{FnEh}{{FhFb}Eh}{ce{}{}}00000000000{El{{l{d}}}}{{EhEh}{{Dn{Eh}}}}{FfEj}{ElEj}`{FbFf}{{FjFb}Eh}{FfFl}{EhFn}`{{Fhc}AbE`}{{Fjc}AbE`}{{Flc}AbE`}{{Fnc}AbE`}{{Elc}AbE`}{{Fbc}AbE`}{FfFb}{FfAf}{EhAf}{FfFj}{ElAn}{Fd{{Dn{Eb}}}}{G`{{Dn{Eb}}}}{FhAf}{FjAf}{ElAf}{Fh{{n{d}}}}{Fj{{n{d}}}}{El{{n{d}}}}{ce{}{}}000{cAf{}}0{c{{Ab{e}}}{}{}}{Dj{{Ab{Fhc}}}{}}1{{{l{d}}}{{Ab{Fhc}}}{}}{{{l{d}}}{{Ab{Fjc}}}{}}{Dj{{Ab{Fjc}}}{}}4444444{Dj{{Ab{Elc}}}{}}5{{{n{d}}}{{Ab{Elc}}}{}}{{{l{d}}}{{Ab{Elc}}}{}}77777777777777{cEf{}}00000000000{Fbd}{{}Fb}0<<<<<<<<<<<<{FhBb}{FjBb}````````````````````````````{Ghh}{Gjh}`{ce{}{}}0000000000000000000000000000000{GlGn}{Gl{{l{d}}}}{H`H`}{GlGl}{GjGj}{HbHb}{HdHd}{GhGh}{HfHf}{HhHh}{{ce}Bb{}{}}0000000{{HjhGj}{{Ab{HlHn}}}}{{HjHfhh}I`}``{Hjh}{c{{Ab{HhBf}}}Bh}{{I`Hb}{{Ab{{n{d}}Ib}}}}{{}Hj}{{}Hf}{c{{Ab{H`}}}Bl}{c{{Ab{Id}}}Bl}{c{{Ab{Gl}}}Bl}{c{{Ab{Gj}}}Bl}{c{{Ab{Hb}}}Bl}{c{{Ab{If}}}Bl}{c{{Ab{Hf}}}Bl}{c{{Ab{Hh}}}Bl}`{Hjj}{{I`c}Hb{{Ad{{l{d}}}}}}{{Id{f{d}}}Af}{{If{f{d}}}Af}{{H`H`}Bn}{{GlGl}Bn}{{GjGj}Bn}{{HbHb}Bn}{{HdHd}Bn}{{GhGh}Bn}{{HfHf}Bn}{{HhHh}Bn}{Hj{{Ih{B`h}}}}{{I`C`}Cd}{{HnC`}Cd}0{{H`C`}Cd}{{HlC`}Cd}{{GlC`}Cd}{{GjC`}Cd}{{HbC`}Cd}{{HdC`}Cd}{{GhC`}Cd}{{IbC`}Cd}0{{HfC`}Cd}{{HhC`}Cd}{HjBn}{cc{}}{IdHj}11{IfI`}{IbHn}333333{GlHb}4{GjHb}5{{{f{d}}}Gh}6{GbIb}{GdIb}8888{Dj{{Ab{GlCn}}}}{Dj{{Ab{GjCn}}}}{{{l{d}}}{{Ab{GlCn}}}}{{{l{d}}}{{Ab{GjCn}}}}{{Dj{f{d}}}{{Ab{IdCj}}}}{{Dj{f{d}}}{{Ab{IfCj}}}}{{Dj{l{d}}}{{Ab{HjCl}}}}{{Dj{l{d}}}{{Ab{I`Cl}}}}{{IjDj}{{Ab{HbCn}}}}{IdHj}{IfI`}{Hj{{Dn{h}}}}{{HjIj}Il}{I`Bn}{Gjh}`{HjH`}{ce{}{}}000000000000000{GlBn}{HjBb}{HjIj}{GjGl}{Hb{{l{d}}}}{HbHd}{{}Hj}9`{Hj{{Ih{B`h}}}}{HjId}{I`If}`{Glh}`{{H`c}AbE`}{{Idc}AbE`}{{Glc}AbE`}{{Gjc}AbE`}{{Hbc}AbE`}{{Ifc}AbE`}{{Hfc}AbE`}{{Hhc}AbE`}`{I`Hf}{I`Af}{GjAf}{HhAf}{I`Hh}{GjHh}{{HjDj}An}{Hn{{Dn{Eb}}}}{Ib{{Dn{Eb}}}}{HjIj}{GlAf}8{Gl{{n{d}}}}{Gj{{n{d}}}}{{Hj{l{d}}}{{Ab{AfCl}}}}{ce{}{}}0000000{Hb{{In{IjAf}}}}{cAf{}}0{c{{Ab{e}}}{}{}}000000{{{l{d}}}{{Ab{Glc}}}{}}{Dj{{Ab{Glc}}}{}}2{{{n{d}}}{{Ab{Glc}}}{}}{{{l{d}}}{{Ab{Gjc}}}{}}{{{n{d}}}{{Ab{Gjc}}}{}}5{Dj{{Ab{Gjc}}}{}}66{Ij{{Ab{Hdc}}}{}}777777777777777777777{cEf{}}000000000000000{Gld}{Hfd}{{}Hf}0>>>>>>>>>>>>>>>>```````{J`{{l{d}}}}{Jb{{f{d}}}}{ce{}{}}00000000000{{JdDj}Jb}{{JdDjIj}{{Ab{{n{d}}Jf}}}}{{JdDjDj}J`}{{JdDjDj}Af}{JfJf}{JbJb}{{ce}Bb{}{}}0{Jb{{In{JhJhJh}}}}{{}Jj}{{Jjh}{{Ab{JdCh}}}}{{JjDj}{{Ab{JdCh}}}}<{{JbJb}Bn}{{JdC`}Cd}{{JfC`}Cd}0{{JlC`}Cd}0{{JbC`}Cd}{cc{}}000{GbJl}11{Dj{{Ab{J`A`}}}}{{{l{d}}}J`}{ce{}{}}00000<{Jdh}{Jjh}{Jl{{Dn{Eb}}}}2{J`Af}44{cAf{}}0{c{{Ab{e}}}{}{}}00000000000{cEf{}}00000{{JdDjDjJ`}{{Ab{BbJl}}}}888888","c":[],"p":[[5,"SharedSecret",0],[1,"u8"],[1,"array"],[5,"Curve25519PublicKey",0],[5,"Ed25519PublicKey",0],[1,"slice"],[5,"Vec",953],[6,"Base64DecodeError",0],[6,"Result",954],[10,"AsRef",955],[5,"String",956],[5,"ProtoBufDecodeError",0],[5,"Curve25519SecretKey",0],[5,"Ed25519Keypair",0],[5,"Ed25519Signature",0],[5,"KeyId",0],[1,"unit"],[6,"Ordering",957],[6,"DecodeError",958],[10,"Read",959],[5,"Ed25519SecretKey",0],[10,"Deserializer",960],[1,"bool"],[5,"Formatter",961],[5,"Error",961],[8,"Result",961],[6,"SignatureError",0],[6,"KeyError",0],[6,"PickleError",0],[6,"LibolmPickleError",0],[6,"DecodeError",0],[5,"ReusableSecret",962],[5,"EphemeralSecret",962],[8,"SignatureError",963],[5,"Error",964],[6,"EncodeError",958],[1,"str"],[10,"Hasher",965],[6,"Option",966],[10,"Serializer",967],[10,"Error",968],[5,"Box",969],[5,"TypeId",970],[5,"InboundGroupSession",318],[1,"u32"],[5,"MegolmMessage",318],[6,"SessionOrdering",318],[5,"DecryptedMessage",318],[5,"SessionConfig",318],[6,"DecryptionError",318],[5,"GroupSession",318],[5,"ExportedSessionKey",318],[5,"SessionKey",318],[5,"GroupSessionPickle",318],[5,"InboundGroupSessionPickle",318],[6,"SessionKeyDecodeError",318],[5,"MacError",971],[5,"UnpadError",972],[5,"Error",973],[5,"RatchetPublicKey",553],[5,"PreKeyMessage",553],[5,"Message",553],[1,"u64"],[5,"IdentityKeys",553],[6,"OlmMessage",553],[6,"MessageType",553],[5,"SessionConfig",553],[5,"SessionKeys",553],[5,"Account",553],[5,"InboundCreationResult",553],[6,"SessionCreationError",553],[5,"Session",553],[6,"DecryptionError",553],[5,"AccountPickle",553],[5,"SessionPickle",553],[5,"HashMap",974],[1,"usize"],[5,"OneTimeKeyGenerationResult",553],[1,"tuple"],[5,"Mac",862],[5,"SasBytes",862],[5,"EstablishedSas",862],[5,"InvalidCount",862],[1,"u16"],[5,"Sas",862],[6,"SasError",862],[15,"InvalidKeyLength",315]],"b":[[120,"impl-Display-for-DecodeError"],[121,"impl-Debug-for-DecodeError"],[122,"impl-Display-for-DecodeError"],[123,"impl-Debug-for-DecodeError"],[124,"impl-Debug-for-Curve25519PublicKey"],[125,"impl-Display-for-Curve25519PublicKey"],[126,"impl-Debug-for-SignatureError"],[127,"impl-Display-for-SignatureError"],[128,"impl-Debug-for-Ed25519PublicKey"],[129,"impl-Display-for-Ed25519PublicKey"],[130,"impl-Debug-for-Ed25519Signature"],[131,"impl-Display-for-Ed25519Signature"],[133,"impl-Debug-for-KeyError"],[134,"impl-Display-for-KeyError"],[135,"impl-Display-for-PickleError"],[136,"impl-Debug-for-PickleError"],[137,"impl-Debug-for-LibolmPickleError"],[138,"impl-Display-for-LibolmPickleError"],[139,"impl-Debug-for-DecodeError"],[140,"impl-Display-for-DecodeError"],[145,"impl-From%3C%26Curve25519SecretKey%3E-for-Curve25519PublicKey"],[146,"impl-From%3C%26ReusableSecret%3E-for-Curve25519PublicKey"],[147,"impl-From%3C%5Bu8;+Self::LENGTH%5D%3E-for-Curve25519PublicKey"],[149,"impl-From%3C%26EphemeralSecret%3E-for-Curve25519PublicKey"],[151,"impl-From%3CDecodeError%3E-for-SignatureError"],[152,"impl-From%3CError%3E-for-SignatureError"],[158,"impl-From%3CSignatureError%3E-for-KeyError"],[160,"impl-From%3CDecodeError%3E-for-KeyError"],[161,"impl-From%3CDecodeError%3E-for-PickleError"],[162,"impl-From%3CError%3E-for-PickleError"],[165,"impl-From%3CEncodeError%3E-for-LibolmPickleError"],[166,"impl-From%3CDecodeError%3E-for-LibolmPickleError"],[167,"impl-From%3CDecodeError%3E-for-LibolmPickleError"],[168,"impl-From%3CKeyError%3E-for-LibolmPickleError"],[169,"impl-From%3CSignatureError%3E-for-DecodeError"],[170,"impl-From%3CDecodeError%3E-for-DecodeError"],[171,"impl-From%3CDecodeError%3E-for-DecodeError"],[173,"impl-From%3CKeyError%3E-for-DecodeError"],[403,"impl-Debug-for-DecryptionError"],[404,"impl-Display-for-DecryptionError"],[408,"impl-Display-for-SessionKeyDecodeError"],[409,"impl-Debug-for-SessionKeyDecodeError"],[416,"impl-From%3CMacError%3E-for-DecryptionError"],[417,"impl-From%3CUnpadError%3E-for-DecryptionError"],[418,"impl-From%3CSignatureError%3E-for-DecryptionError"],[421,"impl-From%3C%26GroupSession%3E-for-InboundGroupSession"],[422,"impl-From%3CInboundGroupSessionPickle%3E-for-InboundGroupSession"],[428,"impl-From%3CError%3E-for-SessionKeyDecodeError"],[429,"impl-From%3CSignatureError%3E-for-SessionKeyDecodeError"],[430,"impl-From%3CDecodeError%3E-for-SessionKeyDecodeError"],[432,"impl-From%3CKeyError%3E-for-SessionKeyDecodeError"],[494,"impl-TryFrom%3C%26str%3E-for-ExportedSessionKey"],[496,"impl-TryFrom%3C%26%5Bu8%5D%3E-for-ExportedSessionKey"],[497,"impl-TryFrom%3C%26%5Bu8%5D%3E-for-SessionKey"],[498,"impl-TryFrom%3C%26str%3E-for-SessionKey"],[506,"impl-TryFrom%3C%26str%3E-for-MegolmMessage"],[508,"impl-TryFrom%3CVec%3Cu8%3E%3E-for-MegolmMessage"],[509,"impl-TryFrom%3C%26%5Bu8%5D%3E-for-MegolmMessage"],[666,"impl-Debug-for-SessionCreationError"],[667,"impl-Display-for-SessionCreationError"],[675,"impl-Display-for-DecryptionError"],[676,"impl-Debug-for-DecryptionError"],[692,"impl-From%3CMessage%3E-for-OlmMessage"],[694,"impl-From%3CPreKeyMessage%3E-for-OlmMessage"],[698,"impl-From%3CMacError%3E-for-DecryptionError"],[699,"impl-From%3CUnpadError%3E-for-DecryptionError"],[794,"impl-TryFrom%3C%26%5Bu8%5D%3E-for-Message"],[795,"impl-TryFrom%3C%26str%3E-for-Message"],[797,"impl-TryFrom%3CVec%3Cu8%3E%3E-for-Message"],[798,"impl-TryFrom%3C%26%5Bu8%5D%3E-for-PreKeyMessage"],[799,"impl-TryFrom%3CVec%3Cu8%3E%3E-for-PreKeyMessage"],[801,"impl-TryFrom%3C%26str%3E-for-PreKeyMessage"],[898,"impl-Display-for-InvalidCount"],[899,"impl-Debug-for-InvalidCount"],[900,"impl-Debug-for-SasError"],[901,"impl-Display-for-SasError"]]}]\ +]')); +if (typeof exports !== 'undefined') exports.searchIndex = searchIndex; +else if (window.initSearch) window.initSearch(searchIndex); diff --git a/settings.html b/settings.html new file mode 100644 index 00000000..e9525e46 --- /dev/null +++ b/settings.html @@ -0,0 +1,2 @@ +Settings +

Rustdoc settings

Back
\ No newline at end of file diff --git a/src-files.js b/src-files.js new file mode 100644 index 00000000..876829f1 --- /dev/null +++ b/src-files.js @@ -0,0 +1,4 @@ +var srcIndex = new Map(JSON.parse('[\ +["vodozemac",["",[["cipher",[],["key.rs","mod.rs"]],["megolm",[],["group_session.rs","inbound_group_session.rs","message.rs","mod.rs","ratchet.rs","session_config.rs","session_keys.rs"]],["olm",[["account",[],["fallback_keys.rs","mod.rs","one_time_keys.rs"]],["messages",[],["message.rs","mod.rs","pre_key.rs"]],["session",[],["chain_key.rs","double_ratchet.rs","message_key.rs","mod.rs","ratchet.rs","receiver_chain.rs","root_key.rs"]]],["mod.rs","session_config.rs","session_keys.rs","shared_secret.rs"]],["types",[],["curve25519.rs","ed25519.rs","mod.rs"]],["utilities",[],["libolm_compat.rs","mod.rs"]]],["lib.rs","sas.rs"]]]\ +]')); +createSrcSidebar(); diff --git a/src/vodozemac/cipher/key.rs.html b/src/vodozemac/cipher/key.rs.html new file mode 100644 index 00000000..256dddad --- /dev/null +++ b/src/vodozemac/cipher/key.rs.html @@ -0,0 +1,229 @@ +key.rs - source +
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+
// Copyright 2021 The Matrix.org Foundation C.I.C.
+// Copyright 2021 Damir Jelić
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+use aes::{
+    cipher::{generic_array::GenericArray, IvSizeUser, KeySizeUser},
+    Aes256,
+};
+use hkdf::Hkdf;
+use sha2::Sha256;
+use zeroize::Zeroize;
+
+use super::Aes256CbcEnc;
+
+type Aes256Key = GenericArray<u8, <Aes256 as KeySizeUser>::KeySize>;
+type Aes256Iv = GenericArray<u8, <Aes256CbcEnc as IvSizeUser>::IvSize>;
+type HmacSha256Key = [u8; 32];
+
+#[derive(Zeroize)]
+#[zeroize(drop)]
+struct ExpandedKeys(Box<[u8; 80]>);
+
+impl ExpandedKeys {
+    const OLM_HKDF_INFO: &'static [u8] = b"OLM_KEYS";
+    const MEGOLM_HKDF_INFO: &'static [u8] = b"MEGOLM_KEYS";
+
+    fn new(message_key: &[u8; 32]) -> Self {
+        Self::new_helper(message_key, Self::OLM_HKDF_INFO)
+    }
+
+    fn new_megolm(message_key: &[u8; 128]) -> Self {
+        Self::new_helper(message_key, Self::MEGOLM_HKDF_INFO)
+    }
+
+    #[cfg(feature = "libolm-compat")]
+    fn new_pickle(pickle_key: &[u8]) -> Self {
+        Self::new_helper(pickle_key, b"Pickle")
+    }
+
+    fn new_helper(message_key: &[u8], info: &[u8]) -> Self {
+        let mut expanded_keys = [0u8; 80];
+
+        let hkdf: Hkdf<Sha256> = Hkdf::new(Some(&[0]), message_key);
+
+        hkdf.expand(info, &mut expanded_keys).expect("Can't expand message key");
+
+        Self(Box::new(expanded_keys))
+    }
+}
+
+#[derive(Zeroize)]
+#[zeroize(drop)]
+pub(super) struct CipherKeys {
+    aes_key: Box<[u8; 32]>,
+    aes_iv: Box<[u8; 16]>,
+    mac_key: Box<[u8; 32]>,
+}
+
+impl CipherKeys {
+    pub fn new(message_key: &[u8; 32]) -> Self {
+        let expanded_keys = ExpandedKeys::new(message_key);
+
+        Self::from_expanded_keys(expanded_keys)
+    }
+
+    pub fn new_megolm(message_key: &[u8; 128]) -> Self {
+        let expanded_keys = ExpandedKeys::new_megolm(message_key);
+
+        Self::from_expanded_keys(expanded_keys)
+    }
+
+    #[cfg(feature = "libolm-compat")]
+    pub fn new_pickle(pickle_key: &[u8]) -> Self {
+        let expanded_keys = ExpandedKeys::new_pickle(pickle_key);
+
+        Self::from_expanded_keys(expanded_keys)
+    }
+
+    fn from_expanded_keys(expanded_keys: ExpandedKeys) -> Self {
+        let mut aes_key = Box::new([0u8; 32]);
+        let mut mac_key = Box::new([0u8; 32]);
+        let mut aes_iv = Box::new([0u8; 16]);
+
+        aes_key.copy_from_slice(&expanded_keys.0[0..32]);
+        mac_key.copy_from_slice(&expanded_keys.0[32..64]);
+        aes_iv.copy_from_slice(&expanded_keys.0[64..80]);
+
+        Self { aes_key, aes_iv, mac_key }
+    }
+
+    pub fn aes_key(&self) -> &Aes256Key {
+        Aes256Key::from_slice(self.aes_key.as_slice())
+    }
+
+    pub fn mac_key(&self) -> &HmacSha256Key {
+        &self.mac_key
+    }
+
+    pub fn iv(&self) -> &Aes256Iv {
+        Aes256Iv::from_slice(self.aes_iv.as_slice())
+    }
+}
+
\ No newline at end of file diff --git a/src/vodozemac/cipher/mod.rs.html b/src/vodozemac/cipher/mod.rs.html new file mode 100644 index 00000000..344d6806 --- /dev/null +++ b/src/vodozemac/cipher/mod.rs.html @@ -0,0 +1,391 @@ +mod.rs - source +
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+
// Copyright 2021 The Matrix.org Foundation C.I.C.
+// Copyright 2021 Damir Jelić
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+mod key;
+
+use aes::{
+    cipher::{
+        block_padding::{Pkcs7, UnpadError},
+        BlockDecryptMut, BlockEncryptMut, KeyIvInit,
+    },
+    Aes256,
+};
+use hmac::{digest::MacError, Hmac, Mac as MacT};
+use key::CipherKeys;
+use sha2::Sha256;
+use thiserror::Error;
+
+type Aes256CbcEnc = cbc::Encryptor<Aes256>;
+type Aes256CbcDec = cbc::Decryptor<Aes256>;
+type HmacSha256 = Hmac<Sha256>;
+
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct Mac(pub(crate) [u8; Self::LENGTH]);
+
+impl Mac {
+    pub const LENGTH: usize = 32;
+    pub const TRUNCATED_LEN: usize = 8;
+
+    pub fn truncate(&self) -> [u8; Self::TRUNCATED_LEN] {
+        let mut truncated = [0u8; Self::TRUNCATED_LEN];
+        truncated.copy_from_slice(&self.0[0..Self::TRUNCATED_LEN]);
+
+        truncated
+    }
+
+    pub fn as_bytes(&self) -> &[u8] {
+        self.0.as_ref()
+    }
+}
+
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub(crate) enum MessageMac {
+    Truncated([u8; Mac::TRUNCATED_LEN]),
+    Full(Mac),
+}
+
+impl MessageMac {
+    pub fn as_bytes(&self) -> &[u8] {
+        match self {
+            MessageMac::Truncated(m) => m.as_ref(),
+            MessageMac::Full(m) => m.as_bytes(),
+        }
+    }
+}
+
+impl From<Mac> for MessageMac {
+    fn from(m: Mac) -> Self {
+        Self::Full(m)
+    }
+}
+
+impl From<[u8; Mac::TRUNCATED_LEN]> for MessageMac {
+    fn from(m: [u8; Mac::TRUNCATED_LEN]) -> Self {
+        Self::Truncated(m)
+    }
+}
+
+#[derive(Debug, Error)]
+pub enum DecryptionError {
+    #[error("Failed decrypting, invalid padding")]
+    InvalidPadding(#[from] UnpadError),
+    #[error("The MAC of the ciphertext didn't pass validation {0}")]
+    Mac(#[from] MacError),
+    #[allow(dead_code)]
+    #[error("The ciphertext didn't contain a valid MAC")]
+    MacMissing,
+}
+
+pub struct Cipher {
+    keys: CipherKeys,
+}
+
+impl Cipher {
+    pub fn new(key: &[u8; 32]) -> Self {
+        let keys = CipherKeys::new(key);
+
+        Self { keys }
+    }
+
+    pub fn new_megolm(&key: &[u8; 128]) -> Self {
+        let keys = CipherKeys::new_megolm(&key);
+
+        Self { keys }
+    }
+
+    #[cfg(feature = "libolm-compat")]
+    pub fn new_pickle(key: &[u8]) -> Self {
+        let keys = CipherKeys::new_pickle(key);
+
+        Self { keys }
+    }
+
+    fn get_hmac(&self) -> HmacSha256 {
+        // We don't use HmacSha256::new() here because it expects a 64-byte
+        // large HMAC key while the Olm spec defines a 32-byte one instead.
+        //
+        // https://gitlab.matrix.org/matrix-org/olm/-/blob/master/docs/olm.md#version-1
+        HmacSha256::new_from_slice(self.keys.mac_key()).expect("Invalid HMAC key size")
+    }
+
+    pub fn encrypt(&self, plaintext: &[u8]) -> Vec<u8> {
+        let cipher = Aes256CbcEnc::new(self.keys.aes_key(), self.keys.iv());
+        cipher.encrypt_padded_vec_mut::<Pkcs7>(plaintext)
+    }
+
+    pub fn mac(&self, message: &[u8]) -> Mac {
+        let mut hmac = self.get_hmac();
+        hmac.update(message);
+
+        let mac_bytes = hmac.finalize().into_bytes();
+
+        let mut mac = [0u8; 32];
+        mac.copy_from_slice(&mac_bytes);
+
+        Mac(mac)
+    }
+
+    pub fn decrypt(&self, ciphertext: &[u8]) -> Result<Vec<u8>, UnpadError> {
+        let cipher = Aes256CbcDec::new(self.keys.aes_key(), self.keys.iv());
+        cipher.decrypt_padded_vec_mut::<Pkcs7>(ciphertext)
+    }
+
+    pub fn decrypt_pickle(&self, ciphertext: &[u8]) -> Result<Vec<u8>, DecryptionError> {
+        if ciphertext.len() < Mac::TRUNCATED_LEN + 1 {
+            Err(DecryptionError::MacMissing)
+        } else {
+            let (ciphertext, mac) = ciphertext.split_at(ciphertext.len() - Mac::TRUNCATED_LEN);
+            self.verify_truncated_mac(ciphertext, mac)?;
+
+            Ok(self.decrypt(ciphertext)?)
+        }
+    }
+
+    pub fn encrypt_pickle(&self, plaintext: &[u8]) -> Vec<u8> {
+        let mut ciphertext = self.encrypt(plaintext);
+        let mac = self.mac(&ciphertext);
+
+        ciphertext.extend(mac.truncate());
+
+        ciphertext
+    }
+
+    #[cfg(not(fuzzing))]
+    pub fn verify_mac(&self, message: &[u8], tag: &Mac) -> Result<(), MacError> {
+        let mut hmac = self.get_hmac();
+
+        hmac.update(message);
+        hmac.verify_slice(tag.as_bytes())
+    }
+
+    #[cfg(not(fuzzing))]
+    pub fn verify_truncated_mac(&self, message: &[u8], tag: &[u8]) -> Result<(), MacError> {
+        let mut hmac = self.get_hmac();
+
+        hmac.update(message);
+        hmac.verify_truncated_left(tag)
+    }
+
+    /// A verify_mac method that always succeeds.
+    ///
+    /// Useful if we're fuzzing vodozemac, since MAC verification discards a lot
+    /// of inputs right away.
+    #[cfg(fuzzing)]
+    pub fn verify_mac(&self, _: &[u8], _: &Mac) -> Result<(), MacError> {
+        Ok(())
+    }
+
+    #[cfg(fuzzing)]
+    pub fn verify_truncated_mac(&self, _: &[u8], _: &[u8]) -> Result<(), MacError> {
+        Ok(())
+    }
+}
+
\ No newline at end of file diff --git a/src/vodozemac/lib.rs.html b/src/vodozemac/lib.rs.html new file mode 100644 index 00000000..82a1eee5 --- /dev/null +++ b/src/vodozemac/lib.rs.html @@ -0,0 +1,675 @@ +lib.rs - source +
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+195
+196
+197
+198
+199
+200
+201
+202
+203
+204
+205
+206
+207
+208
+209
+210
+211
+212
+213
+214
+215
+216
+217
+218
+219
+220
+221
+222
+223
+224
+225
+226
+227
+228
+229
+230
+231
+232
+233
+234
+235
+236
+237
+238
+239
+240
+241
+242
+243
+244
+245
+246
+247
+248
+249
+250
+251
+252
+253
+254
+255
+256
+257
+258
+259
+260
+261
+262
+263
+264
+265
+266
+267
+268
+269
+270
+271
+272
+273
+274
+275
+276
+277
+278
+279
+280
+281
+282
+283
+284
+285
+286
+287
+288
+289
+290
+291
+292
+293
+294
+295
+296
+297
+298
+299
+300
+301
+302
+303
+304
+305
+306
+307
+308
+309
+310
+311
+312
+313
+314
+315
+316
+317
+318
+319
+320
+321
+322
+323
+324
+325
+326
+327
+328
+329
+330
+331
+332
+333
+334
+335
+336
+
// Copyright 2021 Damir Jelić
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+//! A Rust implementation of Olm and Megolm
+//!
+//! vodozemac is a Rust reimplementation of
+//! [libolm](https://gitlab.matrix.org/matrix-org/olm), a cryptographic library
+//! used for end-to-end encryption in [Matrix](https://matrix.org). At its core, it
+//! is an implementation of the Olm and Megolm cryptographic ratchets, along
+//! with a high-level API to easily establish cryptographic communication
+//! channels employing those ratchets with other parties. It also implements
+//! some other miscellaneous cryptographic functionality which is useful for
+//! building Matrix clients, such as [SAS][sas].
+//!
+//! [sas]:
+//! <https://spec.matrix.org/v1.2/client-server-api/#short-authentication-string-sas-verification>
+//!
+//! # Olm
+//!
+//! Olm is an implementation of the [Double Ratchet
+//! algorithm](https://whispersystems.org/docs/specifications/doubleratchet/), very
+//! similar to that employed by the Signal Protocol. It allows the establishment
+//! of a 1-to-1 private communication channel, with perfect forward secrecy and
+//! self-healing properties.
+//!
+//! A detailed technical specification can be found at
+//! <https://gitlab.matrix.org/matrix-org/olm/-/blob/master/docs/olm.md>.
+//!
+//! For more information on using vodozemac for Olm, see the [`olm`] module.
+//!
+//! # Megolm
+//!
+//! Megolm is an AES-based single ratchet for group conversations with a large
+//! number of participants, where using Olm would be cost prohibitive because it
+//! would imply encrypting each message individually for each participant.
+//! Megolm sidesteps this by encrypting messages with a symmetric ratchet,
+//! shared once with each participant and then reused for a sequence of messages
+//! before rotating.
+//!
+//! This is a trade-off in which we lose Olm's self-healing properties, because
+//! someone in possession of a Megolm session at a particular state can derive
+//! all future states. However, if the attacker is only able to obtain the
+//! session in a ratcheted state, they cannot use it to decrypt messages
+//! encrypted with an earlier state. This preserves forward secrecy.
+//!
+//! A detailed technical specification can be found at
+//! <https://gitlab.matrix.org/matrix-org/olm/-/blob/master/docs/megolm.md>.
+//!
+//! For more information on using vodozemac for Megolm, see the [`megolm`]
+//! module.
+//!
+//! # Features
+//!
+//! ## Supported
+//!
+//! - [Olm](https://matrix-org.github.io/vodozemac/vodozemac/olm/index.html)
+//! - [Megolm](https://matrix-org.github.io/vodozemac/vodozemac/megolm/index.html)
+//! - [libolm pickle format](#legacy-pickles) (read-only)
+//! - [Modern pickle format](#modern-pickles)
+//! - [SAS (Short Authentication Strings)](https://matrix-org.github.io/vodozemac/vodozemac/sas/index.html)
+//!
+//! ## Unsupported
+//!
+//! - Creating asymmetric [server-side message key
+//!   backups][legacy-message-key-backup], since they are slated to be replaced
+//!   with symmetric backups.
+//!
+//! ## Planned
+//!
+//! - Symmetric [server-side message key backups][symmetric-message-key-backup]
+//! - Importing asymmetric [server-side message key
+//!   backups][legacy-message-key-backup], for compatibility with existing
+//!   backups created by libolm.
+//!
+//! [legacy-message-key-backup]:
+//! <https://spec.matrix.org/v1.2/client-server-api/#server-side-key-backups>
+//!
+//! [symmetric-message-key-backup]:
+//! https://github.com/uhoreg/matrix-doc/blob/symmetric-backups/proposals/3270-symmetric-megolm-backup.md
+//!
+//! # Feature flags
+//!
+//! ## Low-level API
+//!
+//! Feature: `low-level-api` (default: off)
+//!
+//! Vodozemac exposes some lower-level structs and functions that are only
+//! useful in very advanced use cases. These should *not* be needed by the vast
+//! majority of users.
+//!
+//! Extreme care must be taken when using such APIs, as incorrect usage can lead
+//! to broken sessions.
+//!
+//! # Pickling
+//!
+//! vodozemac supports serializing its entire internal state into a form
+//! a "pickle". The state can subsequently be restored from such a pickle
+//! ("unpickled") in order to continue operation. This is used to support some
+//! Matrix features like device dehydration.
+//!
+//! ## Legacy pickles
+//!
+//! The legacy pickle format is a simple binary format used by libolm.
+//! Implemented for interoperability with current clients which are using
+//! libolm. Only *unpickling* is supported.
+//!
+//! ## Modern pickles
+//!
+//! The crate also implements a modern pickling mechanism using
+//! [Serde](https://serde.rs/). The exact serialization format is not mandated
+//! nor specified by this crate, but you can serialize to and deserialize from
+//! any format supported by Serde.
+//!
+//! The following structs support pickling:
+//!
+//! - [`olm::Account`]
+//! - [`olm::Session`]
+//! - [`megolm::GroupSession`]
+//! - [`megolm::InboundGroupSession`]
+//!
+//! For example, the following will print out the JSON representing the
+//! serialized `Account` and will leave no new copies of the account's secrets
+//! in memory:
+//!
+//! ```rust
+//! use anyhow::Result;
+//! use vodozemac::olm::{Account, AccountPickle};
+//!
+//! const PICKLE_KEY: [u8; 32] = [0u8; 32];
+//!
+//! fn main() -> Result<()>{
+//!     let mut account = Account::new();
+//!
+//!     account.generate_one_time_keys(10);
+//!     account.generate_fallback_key();
+//!
+//!     let pickle = account.pickle().encrypt(&PICKLE_KEY);
+//!
+//!     let account2: Account = AccountPickle::from_encrypted(&pickle, &PICKLE_KEY)?.into();
+//!
+//!     assert_eq!(account.identity_keys(), account2.identity_keys());
+//!
+//!     Ok(())
+//! }
+//! ```
+//!
+//! You can unpickle a pickle-able struct directly from its serialized form:
+//!
+//! ```rust
+//! # use anyhow::Result;
+//! # use vodozemac::olm::{Account, AccountPickle};
+//! # use zeroize::Zeroize;
+//! #
+//! # fn main() -> Result<()> {
+//! #   let some_account = Account::new();
+//!     let mut json_str = serde_json::to_string(&some_account.pickle())?;
+//!     // This will produce an account which is identical to `some_account`.
+//!     let account: Account = serde_json::from_str::<AccountPickle>(&json_str)?.into();
+//!
+//!     json_str.zeroize();
+//! #
+//! #    Ok(())
+//! # }
+//! ```
+//!
+//! However, the pickle-able structs do not implement `serde::Serialize`
+//! themselves. If you want to serialize to a format other than JSON, you should
+//! instead call the `.pickle()` method to obtain a special serializable struct.
+//! This struct *does* implement `Serialize` and can therefore be serialized
+//! into any format supported by `serde`. To get back to the original struct
+//! from such as serializeable struct, just call `.unpickle()`.
+//!
+//! ```rust
+//! use anyhow::Result;
+//! use vodozemac::olm::Account;
+//!
+//! fn main() -> Result<()> {
+//!     let account = Account::new();
+//!     let account: Account = account.pickle().into();  // this is identity
+//!
+//!     Ok(())
+//! }
+//! ```
+
+#![deny(
+    clippy::mem_forget,
+    clippy::unwrap_used,
+    dead_code,
+    trivial_casts,
+    trivial_numeric_casts,
+    unsafe_code,
+    unsafe_op_in_unsafe_fn,
+    unused_extern_crates,
+    unused_import_braces,
+    unused_qualifications,
+    rust_2018_idioms
+)]
+#![cfg_attr(docsrs, feature(doc_auto_cfg))]
+
+mod cipher;
+mod types;
+mod utilities;
+
+pub mod hazmat;
+pub mod megolm;
+pub mod olm;
+pub mod sas;
+
+pub use base64::DecodeError as Base64DecodeError;
+pub use prost::DecodeError as ProtoBufDecodeError;
+pub use types::{
+    Curve25519PublicKey, Curve25519SecretKey, Ed25519Keypair, Ed25519PublicKey, Ed25519SecretKey,
+    Ed25519Signature, KeyError, KeyId, SharedSecret, SignatureError,
+};
+pub use utilities::{base64_decode, base64_encode};
+
+/// Error type describing the various ways Vodozemac pickles can fail to be
+/// decoded.
+#[derive(Debug, thiserror::Error)]
+pub enum PickleError {
+    /// The pickle wasn't valid base64.
+    #[error("The pickle wasn't valid base64: {0}")]
+    Base64(#[from] base64::DecodeError),
+    /// The encrypted pickle could not have been decrypted.
+    #[error("The pickle couldn't be decrypted: {0}")]
+    Decryption(#[from] crate::cipher::DecryptionError),
+    /// The serialized Vodozemac object couldn't be deserialized.
+    #[error("The pickle couldn't be deserialized: {0}")]
+    Serialization(#[from] serde_json::Error),
+}
+
+/// Error type describing the various ways libolm pickles can fail to be
+/// decoded.
+#[cfg(feature = "libolm-compat")]
+#[derive(Debug, thiserror::Error)]
+pub enum LibolmPickleError {
+    /// The pickle is missing a valid version.
+    #[error("The pickle doesn't contain a version")]
+    MissingVersion,
+    /// The pickle has a unsupported version.
+    #[error("The pickle uses an unsupported version, expected {0}, got {1}")]
+    Version(u32, u32),
+    /// The pickle wasn't valid base64.
+    #[error("The pickle wasn't valid base64: {0}")]
+    Base64(#[from] Base64DecodeError),
+    /// The pickle could not have been decrypted.
+    #[error("The pickle couldn't be decrypted: {0}")]
+    Decryption(#[from] crate::cipher::DecryptionError),
+    /// The pickle contains an invalid public key.
+    #[error("The pickle contained an invalid ed25519 public key {0}")]
+    PublicKey(#[from] KeyError),
+    /// The pickle does not contain a valid receiving or sending chain. A valid
+    /// Olm session needs to have at least one of them.
+    #[error("The pickle didn't contain a valid Olm session")]
+    InvalidSession,
+    /// The payload of the pickle could not be decoded.
+    #[error(transparent)]
+    Decode(#[from] matrix_pickle::DecodeError),
+    /// The object could not be encoded as a pickle.
+    #[error(transparent)]
+    Encode(#[from] matrix_pickle::EncodeError),
+}
+
+/// Error type describing the different ways message decoding can fail.
+#[derive(Debug, thiserror::Error)]
+pub enum DecodeError {
+    /// The Olm message has an invalid type.
+    #[error("The message has an invalid type, expected 0 or 1, got {0}")]
+    MessageType(usize),
+    /// The message is missing a valid version.
+    #[error("The message didn't contain a version")]
+    MissingVersion,
+    /// The message doesn't have enough data to be correctly decoded.
+    #[error("The message was too short, it didn't contain a valid payload")]
+    MessageTooShort(usize),
+    /// The message has a unsupported version.
+    #[error("The message didn't have a valid version, expected {0}, got {1}")]
+    InvalidVersion(u8, u8),
+    /// An embedded public key couldn't be decoded.
+    #[error("The message contained an invalid public key: {0}")]
+    InvalidKey(#[from] KeyError),
+    /// The embedded message authentication code couldn't be decoded.
+    #[error("The message contained a MAC with an invalid size, expected {0}, got {1}")]
+    InvalidMacLength(usize, usize),
+    /// An embedded signature couldn't be decoded.
+    #[error("The message contained an invalid Signature: {0}")]
+    Signature(#[from] SignatureError),
+    /// The message couldn't be decoded as a valid protocol buffer message.
+    #[error(transparent)]
+    ProtoBufError(#[from] ProtoBufDecodeError),
+    /// The message wasn't valid base64.
+    #[error("The message wasn't valid base64: {0}")]
+    Base64(#[from] Base64DecodeError),
+}
+
+/// The version of vodozemac that is being used.
+pub static VERSION: &str = env!("CARGO_PKG_VERSION");
+
+#[cfg(test)]
+fn corpus_data_path(fuzz_target: &str) -> std::path::PathBuf {
+    let manifest_dir =
+        std::env::var("CARGO_MANIFEST_DIR").expect("Cargo always sets the manifest dir");
+
+    let mut afl_dir = std::path::PathBuf::from(manifest_dir);
+    afl_dir.push("afl");
+    afl_dir.push(fuzz_target);
+    afl_dir.push("in");
+
+    afl_dir
+}
+
+#[cfg(test)]
+fn run_corpus<F>(fuzz_target: &str, method: F)
+where
+    F: FnOnce(&[u8]) + Copy,
+{
+    let dir = corpus_data_path(fuzz_target);
+    let corpus = std::fs::read_dir(dir).expect("Couldn't read the corpus directory");
+
+    for input in corpus {
+        let input = input.expect("Couldn't read the input file");
+        let data = std::fs::read(input.path()).expect("Couldn't read the input file");
+        method(&data)
+    }
+}
+
\ No newline at end of file diff --git a/src/vodozemac/megolm/group_session.rs.html b/src/vodozemac/megolm/group_session.rs.html new file mode 100644 index 00000000..7d60bb4c --- /dev/null +++ b/src/vodozemac/megolm/group_session.rs.html @@ -0,0 +1,437 @@ +group_session.rs - source +
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+195
+196
+197
+198
+199
+200
+201
+202
+203
+204
+205
+206
+207
+208
+209
+210
+211
+212
+213
+214
+215
+216
+217
+
// Copyright 2021 The Matrix.org Foundation C.I.C.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+use serde::{Deserialize, Serialize};
+
+use super::{
+    default_config, message::MegolmMessage, ratchet::Ratchet, session_config::Version,
+    session_keys::SessionKey, SessionConfig,
+};
+use crate::{
+    cipher::Cipher,
+    types::Ed25519Keypair,
+    utilities::{pickle, unpickle},
+    PickleError,
+};
+
+/// A Megolm group session represents a single sending participant in an
+/// encrypted group communication context containing multiple receiving parties.
+///
+/// A group session consists of a ratchet, used for encryption, and an Ed25519
+/// signing key pair, used for authenticity.
+///
+/// A group session containing the signing key pair is also known as an
+/// "outbound" group session. We differentiate this from an *inbound* group
+/// session where this key pair has been removed and which can be used solely
+/// for receipt and decryption of messages.
+///
+/// Such an inbound group session is typically sent by the outbound group
+/// session owner to each of the receiving parties via a secure peer-to-peer
+/// channel (e.g. an Olm channel).
+pub struct GroupSession {
+    ratchet: Ratchet,
+    signing_key: Ed25519Keypair,
+    config: SessionConfig,
+}
+
+impl Default for GroupSession {
+    fn default() -> Self {
+        Self::new(Default::default())
+    }
+}
+
+impl GroupSession {
+    /// Construct a new group session, with a random ratchet state and signing
+    /// key pair.
+    pub fn new(config: SessionConfig) -> Self {
+        let signing_key = Ed25519Keypair::new();
+        Self { signing_key, ratchet: Ratchet::new(), config }
+    }
+
+    /// Returns the globally unique session ID, in base64-encoded form.
+    ///
+    /// A session ID is the public part of the Ed25519 key pair associated with
+    /// the group session. Due to the construction, every session ID is
+    /// (probabilistically) globally unique.
+    pub fn session_id(&self) -> String {
+        self.signing_key.public_key().to_base64()
+    }
+
+    /// Return the current message index.
+    ///
+    /// The message index is incremented each time a message is encrypted with
+    /// the group session.
+    pub fn message_index(&self) -> u32 {
+        self.ratchet.index()
+    }
+
+    pub fn session_config(&self) -> SessionConfig {
+        self.config
+    }
+
+    /// Encrypt the `plaintext` with the group session.
+    ///
+    /// The resulting ciphertext is MAC-ed, then signed with the group session's
+    /// Ed25519 key pair and finally base64-encoded.
+    pub fn encrypt(&mut self, plaintext: impl AsRef<[u8]>) -> MegolmMessage {
+        let cipher = Cipher::new_megolm(self.ratchet.as_bytes());
+
+        let message = match self.config.version {
+            Version::V1 => MegolmMessage::encrypt_truncated_mac(
+                self.message_index(),
+                &cipher,
+                &self.signing_key,
+                plaintext.as_ref(),
+            ),
+            Version::V2 => MegolmMessage::encrypt_full_mac(
+                self.message_index(),
+                &cipher,
+                &self.signing_key,
+                plaintext.as_ref(),
+            ),
+        };
+
+        self.ratchet.advance();
+
+        message
+    }
+
+    /// Export the group session into a session key.
+    ///
+    /// The session key contains the key version constant, the current message
+    /// index, the ratchet state and the *public* part of the signing key pair.
+    /// It is signed by the signing key pair for authenticity.
+    ///
+    /// The session key is in a portable format, suitable for sending over the
+    /// network. It is typically sent to other group participants so that they
+    /// can reconstruct an inbound group session in order to decrypt messages
+    /// sent by this group session.
+    pub fn session_key(&self) -> SessionKey {
+        let mut session_key = SessionKey::new(&self.ratchet, self.signing_key.public_key());
+        let signature = self.signing_key.sign(&session_key.to_signature_bytes());
+        session_key.signature = signature;
+
+        session_key
+    }
+
+    /// Convert the group session into a struct which implements
+    /// [`serde::Serialize`] and [`serde::Deserialize`].
+    pub fn pickle(&self) -> GroupSessionPickle {
+        GroupSessionPickle {
+            ratchet: self.ratchet.clone(),
+            signing_key: self.signing_key.clone(),
+            config: self.config,
+        }
+    }
+
+    /// Restore a [`GroupSession`] from a previously saved
+    /// [`GroupSessionPickle`].
+    pub fn from_pickle(pickle: GroupSessionPickle) -> Self {
+        pickle.into()
+    }
+
+    #[cfg(feature = "libolm-compat")]
+    pub fn from_libolm_pickle(
+        pickle: &str,
+        pickle_key: &[u8],
+    ) -> Result<Self, crate::LibolmPickleError> {
+        use matrix_pickle::Decode;
+        use zeroize::Zeroize;
+
+        use crate::{
+            megolm::libolm::LibolmRatchetPickle,
+            utilities::{unpickle_libolm, LibolmEd25519Keypair},
+        };
+
+        #[derive(Zeroize, Decode)]
+        #[zeroize(drop)]
+        struct Pickle {
+            version: u32,
+            ratchet: LibolmRatchetPickle,
+            ed25519_keypair: LibolmEd25519Keypair,
+        }
+
+        impl TryFrom<Pickle> for GroupSession {
+            type Error = crate::LibolmPickleError;
+
+            fn try_from(pickle: Pickle) -> Result<Self, Self::Error> {
+                // Removing the borrow doesn't work and clippy complains about
+                // this on nightly.
+                #[allow(clippy::needless_borrow)]
+                let ratchet = (&pickle.ratchet).into();
+                let signing_key =
+                    Ed25519Keypair::from_expanded_key(&pickle.ed25519_keypair.private_key)?;
+
+                Ok(Self { ratchet, signing_key, config: SessionConfig::version_1() })
+            }
+        }
+
+        const PICKLE_VERSION: u32 = 1;
+
+        unpickle_libolm::<Pickle, _>(pickle, pickle_key, PICKLE_VERSION)
+    }
+}
+
+/// A format suitable for serialization which implements [`serde::Serialize`]
+/// and [`serde::Deserialize`]. Obtainable by calling [`GroupSession::pickle`].
+#[derive(Serialize, Deserialize)]
+pub struct GroupSessionPickle {
+    ratchet: Ratchet,
+    signing_key: Ed25519Keypair,
+    #[serde(default = "default_config")]
+    config: SessionConfig,
+}
+
+impl GroupSessionPickle {
+    /// Serialize and encrypt the pickle using the given key.
+    ///
+    /// This is the inverse of [`GroupSessionPickle::from_encrypted`].
+    pub fn encrypt(self, pickle_key: &[u8; 32]) -> String {
+        pickle(&self, pickle_key)
+    }
+
+    /// Obtain a pickle from a ciphertext by decrypting and deserializing using
+    /// the given key.
+    ///
+    /// This is the inverse of [`GroupSessionPickle::encrypt`].
+    pub fn from_encrypted(ciphertext: &str, pickle_key: &[u8; 32]) -> Result<Self, PickleError> {
+        unpickle(ciphertext, pickle_key)
+    }
+}
+
+impl From<GroupSessionPickle> for GroupSession {
+    fn from(pickle: GroupSessionPickle) -> Self {
+        Self { ratchet: pickle.ratchet, signing_key: pickle.signing_key, config: pickle.config }
+    }
+}
+
\ No newline at end of file diff --git a/src/vodozemac/megolm/inbound_group_session.rs.html b/src/vodozemac/megolm/inbound_group_session.rs.html new file mode 100644 index 00000000..0e3878cf --- /dev/null +++ b/src/vodozemac/megolm/inbound_group_session.rs.html @@ -0,0 +1,1213 @@ +inbound_group_session.rs - source +
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+195
+196
+197
+198
+199
+200
+201
+202
+203
+204
+205
+206
+207
+208
+209
+210
+211
+212
+213
+214
+215
+216
+217
+218
+219
+220
+221
+222
+223
+224
+225
+226
+227
+228
+229
+230
+231
+232
+233
+234
+235
+236
+237
+238
+239
+240
+241
+242
+243
+244
+245
+246
+247
+248
+249
+250
+251
+252
+253
+254
+255
+256
+257
+258
+259
+260
+261
+262
+263
+264
+265
+266
+267
+268
+269
+270
+271
+272
+273
+274
+275
+276
+277
+278
+279
+280
+281
+282
+283
+284
+285
+286
+287
+288
+289
+290
+291
+292
+293
+294
+295
+296
+297
+298
+299
+300
+301
+302
+303
+304
+305
+306
+307
+308
+309
+310
+311
+312
+313
+314
+315
+316
+317
+318
+319
+320
+321
+322
+323
+324
+325
+326
+327
+328
+329
+330
+331
+332
+333
+334
+335
+336
+337
+338
+339
+340
+341
+342
+343
+344
+345
+346
+347
+348
+349
+350
+351
+352
+353
+354
+355
+356
+357
+358
+359
+360
+361
+362
+363
+364
+365
+366
+367
+368
+369
+370
+371
+372
+373
+374
+375
+376
+377
+378
+379
+380
+381
+382
+383
+384
+385
+386
+387
+388
+389
+390
+391
+392
+393
+394
+395
+396
+397
+398
+399
+400
+401
+402
+403
+404
+405
+406
+407
+408
+409
+410
+411
+412
+413
+414
+415
+416
+417
+418
+419
+420
+421
+422
+423
+424
+425
+426
+427
+428
+429
+430
+431
+432
+433
+434
+435
+436
+437
+438
+439
+440
+441
+442
+443
+444
+445
+446
+447
+448
+449
+450
+451
+452
+453
+454
+455
+456
+457
+458
+459
+460
+461
+462
+463
+464
+465
+466
+467
+468
+469
+470
+471
+472
+473
+474
+475
+476
+477
+478
+479
+480
+481
+482
+483
+484
+485
+486
+487
+488
+489
+490
+491
+492
+493
+494
+495
+496
+497
+498
+499
+500
+501
+502
+503
+504
+505
+506
+507
+508
+509
+510
+511
+512
+513
+514
+515
+516
+517
+518
+519
+520
+521
+522
+523
+524
+525
+526
+527
+528
+529
+530
+531
+532
+533
+534
+535
+536
+537
+538
+539
+540
+541
+542
+543
+544
+545
+546
+547
+548
+549
+550
+551
+552
+553
+554
+555
+556
+557
+558
+559
+560
+561
+562
+563
+564
+565
+566
+567
+568
+569
+570
+571
+572
+573
+574
+575
+576
+577
+578
+579
+580
+581
+582
+583
+584
+585
+586
+587
+588
+589
+590
+591
+592
+593
+594
+595
+596
+597
+598
+599
+600
+601
+602
+603
+604
+605
+
// Copyright 2021 The Matrix.org Foundation C.I.C.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+use std::cmp::Ordering;
+
+use aes::cipher::block_padding::UnpadError;
+use hmac::digest::MacError;
+use serde::{Deserialize, Serialize};
+use subtle::ConstantTimeEq;
+use thiserror::Error;
+use zeroize::Zeroize;
+
+use super::{
+    default_config,
+    message::MegolmMessage,
+    ratchet::Ratchet,
+    session_config::Version,
+    session_keys::{ExportedSessionKey, SessionKey},
+    GroupSession, SessionConfig,
+};
+use crate::{
+    cipher::{Cipher, Mac, MessageMac},
+    types::{Ed25519PublicKey, SignatureError},
+    utilities::{base64_encode, pickle, unpickle},
+    PickleError,
+};
+
+/// The result of a comparison between two [`InboundGroupSession`] types.
+///
+/// Tells us if one session can be considered to be better than another one.
+#[derive(Clone, Copy, Debug, PartialEq, Eq)]
+pub enum SessionOrdering {
+    /// The sessions are the same.
+    Equal,
+    /// The first session has a better initial message index than the second
+    /// one.
+    Better,
+    /// The first session has a worse initial message index than the second one.
+    Worse,
+    /// The sessions are not the same, they can't be compared.
+    Unconnected,
+}
+
+/// Error type for Megolm-based decryption failures.
+#[derive(Debug, Error)]
+pub enum DecryptionError {
+    /// The signature on the message was invalid.
+    #[error("The signature on the message was invalid: {0}")]
+    Signature(#[from] SignatureError),
+
+    /// The message authentication code of the message was invalid.
+    #[error("Failed decrypting Megolm message, invalid MAC: {0}")]
+    InvalidMAC(#[from] MacError),
+
+    /// The length of the message authentication code of the message did not
+    /// match our expected length.
+    #[error("Failed decrypting Olm message, invalid MAC length: expected {0}, got {1}")]
+    InvalidMACLength(usize, usize),
+
+    /// The ciphertext of the message isn't padded correctly.
+    #[error("Failed decrypting Megolm message, invalid padding")]
+    InvalidPadding(#[from] UnpadError),
+
+    /// The session is missing the correct message key to decrypt the message,
+    /// The Session has been ratcheted forwards and the message key isn't
+    /// available anymore.
+    #[error(
+        "The message was encrypted using an unknown message index, \
+        first known index {0}, index of the message {1}"
+    )]
+    UnknownMessageIndex(u32, u32),
+}
+
+#[derive(Deserialize)]
+#[serde(try_from = "InboundGroupSessionPickle")]
+pub struct InboundGroupSession {
+    initial_ratchet: Ratchet,
+    latest_ratchet: Ratchet,
+    signing_key: Ed25519PublicKey,
+    signing_key_verified: bool,
+    config: SessionConfig,
+}
+
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct DecryptedMessage {
+    pub plaintext: Vec<u8>,
+    pub message_index: u32,
+}
+
+impl InboundGroupSession {
+    pub fn new(key: &SessionKey, session_config: SessionConfig) -> Self {
+        let initial_ratchet =
+            Ratchet::from_bytes(key.session_key.ratchet.clone(), key.session_key.ratchet_index);
+        let latest_ratchet = initial_ratchet.clone();
+
+        Self {
+            initial_ratchet,
+            latest_ratchet,
+            signing_key: key.session_key.signing_key,
+            signing_key_verified: true,
+            config: session_config,
+        }
+    }
+
+    pub fn import(session_key: &ExportedSessionKey, session_config: SessionConfig) -> Self {
+        let initial_ratchet =
+            Ratchet::from_bytes(session_key.ratchet.clone(), session_key.ratchet_index);
+        let latest_ratchet = initial_ratchet.clone();
+
+        Self {
+            initial_ratchet,
+            latest_ratchet,
+            signing_key: session_key.signing_key,
+            signing_key_verified: false,
+            config: session_config,
+        }
+    }
+
+    pub fn session_id(&self) -> String {
+        base64_encode(self.signing_key.as_bytes())
+    }
+
+    /// Check if two `InboundGroupSession`s are the same.
+    ///
+    /// An `InboundGroupSession` could be received multiple times with varying
+    /// degrees of trust and first known message indices.
+    ///
+    /// This method checks if the underlying ratchets of the two
+    /// `InboundGroupSession`s are actually the same ratchet, potentially at
+    /// a different ratcheting index. That is, if the sessions are *connected*,
+    /// then ratcheting one of the ratchets to the index of the other should
+    /// yield the same ratchet value, byte-for-byte. This will only be the case
+    /// if the `InboundGroupSession`s were created from the same
+    /// [`GroupSession`].
+    ///
+    /// If the sessions are connected, the session with the lower message index
+    /// can safely replace the one with the higher message index.
+    pub fn connected(&mut self, other: &mut InboundGroupSession) -> bool {
+        // This method attempts to bring the two `Ratchet` values, one from each
+        // session, to the same message index.
+        //
+        // We first try to ratchet our own ratchets towards the initial ratchet
+        // of the other session. If that fails we try to ratchet the other
+        // session's ratchets towards our initial ratchet.
+        //
+        // After that we compare the raw ratchet bytes in constant time.
+
+        if self.config != other.config || self.signing_key != other.signing_key {
+            // Short circuit if session configs differ or the signing keys
+            // differ. This is comparing public key material.
+            false
+        } else if let Some(ratchet) = self.find_ratchet(other.first_known_index()) {
+            ratchet.ct_eq(&other.initial_ratchet).into()
+        } else if let Some(ratchet) = other.find_ratchet(self.first_known_index()) {
+            self.initial_ratchet.ct_eq(ratchet).into()
+        } else {
+            unreachable!("Either index A >= index B, or vice versa. There is no third option.")
+        }
+    }
+
+    /// Compare the `InboundGroupSession` with the given other
+    /// `InboundGroupSession`.
+    ///
+    /// Returns a `SessionOrdering` describing how the two sessions relate to
+    /// each other.
+    pub fn compare(&mut self, other: &mut InboundGroupSession) -> SessionOrdering {
+        if self.connected(other) {
+            match self.first_known_index().cmp(&other.first_known_index()) {
+                Ordering::Less => SessionOrdering::Better,
+                Ordering::Equal => SessionOrdering::Equal,
+                Ordering::Greater => SessionOrdering::Worse,
+            }
+        } else {
+            // If we're not connected to other, other can't be better.
+            SessionOrdering::Unconnected
+        }
+    }
+
+    /// Merge the session with the given other session, picking the best parts
+    /// from each of them.
+    ///
+    /// This method is useful when you receive multiple sessions with
+    /// the same session ID but potentially different ratchet indices and
+    /// authenticity properties.
+    ///
+    /// For example, imagine you receive a `SessionKey` S1 with ratchet index
+    /// A from a fully-trusted source and an `ExportedSessionKey` S2 with
+    /// ratchet state B from a less trusted source. If A > B, then S1 is better
+    /// because it's fully trusted, but worse because it's ratcheted further
+    /// than S2.
+    ///
+    /// This method allows you to merge S1 and S2 safely into a fully-trusted S3
+    /// with ratchet state B, provided S1 and S2 connect with each other
+    /// (meaning they are the same session, just at different ratchet indices).
+    ///
+    /// Returns `Some(session)` if the sessions could be merged, i.e. they are
+    /// considered to be connected and `None` otherwise.
+    ///
+    /// # Example
+    ///
+    /// ```
+    /// use vodozemac::megolm::{GroupSession, InboundGroupSession, SessionOrdering};
+    ///
+    /// let session = GroupSession::new(Default::default());
+    /// let session_key = session.session_key();
+    ///
+    /// let mut first_session = InboundGroupSession::new(&session_key, Default::default());
+    /// let mut second_session = InboundGroupSession::import(&first_session.export_at(10).unwrap(), Default::default());
+    ///
+    /// assert_eq!(first_session.compare(&mut second_session), SessionOrdering::Better);
+    ///
+    /// let mut merged = second_session.merge(&mut first_session).unwrap();
+    ///
+    /// assert_eq!(merged.compare(&mut second_session), SessionOrdering::Better);
+    /// assert_eq!(merged.compare(&mut first_session), SessionOrdering::Equal);
+    /// ```
+    pub fn merge(&mut self, other: &mut InboundGroupSession) -> Option<InboundGroupSession> {
+        let best_ratchet = match self.compare(other) {
+            SessionOrdering::Equal | SessionOrdering::Better => Some(self.initial_ratchet.clone()),
+            SessionOrdering::Worse => Some(other.initial_ratchet.clone()),
+            SessionOrdering::Unconnected => None,
+        }?;
+
+        Some(InboundGroupSession {
+            initial_ratchet: best_ratchet.clone(),
+            latest_ratchet: best_ratchet,
+            signing_key: self.signing_key,
+            signing_key_verified: self.signing_key_verified || other.signing_key_verified,
+            config: self.config,
+        })
+    }
+
+    pub fn first_known_index(&self) -> u32 {
+        self.initial_ratchet.index()
+    }
+
+    /// Permanently advance the session to the given index.
+    ///
+    /// This will remove the ability to decrypt messages that were encrypted
+    /// with a lower message index than what is given as the argument.
+    ///
+    /// Returns true if the ratchet has been advanced, false if the ratchet was
+    /// already advanced past the given index.
+    pub fn advance_to(&mut self, index: u32) -> bool {
+        if self.first_known_index() < index {
+            self.initial_ratchet.advance_to(index);
+
+            if self.latest_ratchet.index() < index {
+                self.latest_ratchet = self.initial_ratchet.clone();
+            }
+
+            true
+        } else {
+            false
+        }
+    }
+
+    /// Returns a copy of the [`Cipher`] at the given message index, without
+    /// advancing the internal ratchets.
+    #[cfg(feature = "low-level-api")]
+    pub fn get_cipher_at(&self, message_index: u32) -> Option<Cipher> {
+        if self.initial_ratchet.index() <= message_index {
+            let mut ratchet = self.initial_ratchet.clone();
+            if self.initial_ratchet.index() < message_index {
+                ratchet.advance_to(message_index);
+            }
+            Some(Cipher::new_megolm(ratchet.as_bytes()))
+        } else {
+            None
+        }
+    }
+
+    fn find_ratchet(&mut self, message_index: u32) -> Option<&Ratchet> {
+        if self.initial_ratchet.index() == message_index {
+            Some(&self.initial_ratchet)
+        } else if self.latest_ratchet.index() == message_index {
+            Some(&self.latest_ratchet)
+        } else if self.latest_ratchet.index() < message_index {
+            self.latest_ratchet.advance_to(message_index);
+            Some(&self.latest_ratchet)
+        } else if self.initial_ratchet.index() < message_index {
+            self.latest_ratchet = self.initial_ratchet.clone();
+            self.latest_ratchet.advance_to(message_index);
+            Some(&self.latest_ratchet)
+        } else {
+            None
+        }
+    }
+
+    fn verify_mac(&self, cipher: &Cipher, message: &MegolmMessage) -> Result<(), DecryptionError> {
+        match self.config.version {
+            Version::V1 => {
+                if let MessageMac::Truncated(m) = &message.mac {
+                    Ok(cipher.verify_truncated_mac(&message.to_mac_bytes(), m)?)
+                } else {
+                    Err(DecryptionError::InvalidMACLength(Mac::TRUNCATED_LEN, Mac::LENGTH))
+                }
+            }
+            Version::V2 => {
+                if let MessageMac::Full(m) = &message.mac {
+                    Ok(cipher.verify_mac(&message.to_mac_bytes(), m)?)
+                } else {
+                    Err(DecryptionError::InvalidMACLength(Mac::LENGTH, Mac::TRUNCATED_LEN))
+                }
+            }
+        }
+    }
+
+    pub fn decrypt(
+        &mut self,
+        message: &MegolmMessage,
+    ) -> Result<DecryptedMessage, DecryptionError> {
+        self.signing_key.verify(&message.to_signature_bytes(), &message.signature)?;
+
+        if let Some(ratchet) = self.find_ratchet(message.message_index) {
+            let cipher = Cipher::new_megolm(ratchet.as_bytes());
+
+            self.verify_mac(&cipher, message)?;
+
+            let plaintext = cipher.decrypt(&message.ciphertext)?;
+
+            Ok(DecryptedMessage { plaintext, message_index: message.message_index })
+        } else {
+            Err(DecryptionError::UnknownMessageIndex(
+                self.initial_ratchet.index(),
+                message.message_index,
+            ))
+        }
+    }
+
+    pub fn export_at(&mut self, index: u32) -> Option<ExportedSessionKey> {
+        let signing_key = self.signing_key;
+
+        self.find_ratchet(index).map(|ratchet| ExportedSessionKey::new(ratchet, signing_key))
+    }
+
+    pub fn export_at_first_known_index(&self) -> ExportedSessionKey {
+        ExportedSessionKey::new(&self.initial_ratchet, self.signing_key)
+    }
+
+    /// Convert the inbound group session into a struct which implements
+    /// [`serde::Serialize`] and [`serde::Deserialize`].
+    pub fn pickle(&self) -> InboundGroupSessionPickle {
+        InboundGroupSessionPickle {
+            initial_ratchet: self.initial_ratchet.clone(),
+            signing_key: self.signing_key,
+            signing_key_verified: self.signing_key_verified,
+            config: self.config,
+        }
+    }
+
+    /// Restore an [`InboundGroupSession`] from a previously saved
+    /// [`InboundGroupSessionPickle`].
+    pub fn from_pickle(pickle: InboundGroupSessionPickle) -> Self {
+        Self::from(pickle)
+    }
+
+    #[cfg(feature = "libolm-compat")]
+    pub fn from_libolm_pickle(
+        pickle: &str,
+        pickle_key: &[u8],
+    ) -> Result<Self, crate::LibolmPickleError> {
+        use matrix_pickle::Decode;
+
+        use super::libolm::LibolmRatchetPickle;
+        use crate::utilities::unpickle_libolm;
+
+        #[derive(Zeroize, Decode)]
+        #[zeroize(drop)]
+        struct Pickle {
+            version: u32,
+            initial_ratchet: LibolmRatchetPickle,
+            latest_ratchet: LibolmRatchetPickle,
+            signing_key: [u8; 32],
+            signing_key_verified: bool,
+        }
+
+        impl TryFrom<Pickle> for InboundGroupSession {
+            type Error = crate::LibolmPickleError;
+
+            fn try_from(pickle: Pickle) -> Result<Self, Self::Error> {
+                // Removing the borrow doesn't work and clippy complains about
+                // this on nightly.
+                #[allow(clippy::needless_borrow)]
+                let initial_ratchet = (&pickle.initial_ratchet).into();
+                #[allow(clippy::needless_borrow)]
+                let latest_ratchet = (&pickle.latest_ratchet).into();
+                let signing_key = Ed25519PublicKey::from_slice(&pickle.signing_key)?;
+                let signing_key_verified = pickle.signing_key_verified;
+
+                Ok(Self {
+                    initial_ratchet,
+                    latest_ratchet,
+                    signing_key,
+                    signing_key_verified,
+                    config: SessionConfig::version_1(),
+                })
+            }
+        }
+
+        const PICKLE_VERSION: u32 = 2;
+
+        unpickle_libolm::<Pickle, _>(pickle, pickle_key, PICKLE_VERSION)
+    }
+}
+
+/// A format suitable for serialization which implements [`serde::Serialize`]
+/// and [`serde::Deserialize`]. Obtainable by calling
+/// [`InboundGroupSession::pickle`].
+#[derive(Serialize, Deserialize)]
+pub struct InboundGroupSessionPickle {
+    initial_ratchet: Ratchet,
+    signing_key: Ed25519PublicKey,
+    #[allow(dead_code)]
+    signing_key_verified: bool,
+    #[serde(default = "default_config")]
+    config: SessionConfig,
+}
+
+impl InboundGroupSessionPickle {
+    /// Serialize and encrypt the pickle using the given key.
+    ///
+    /// This is the inverse of [`InboundGroupSessionPickle::from_encrypted`].
+    pub fn encrypt(self, pickle_key: &[u8; 32]) -> String {
+        pickle(&self, pickle_key)
+    }
+
+    /// Obtain a pickle from a ciphertext by decrypting and deserializing using
+    /// the given key.
+    ///
+    /// This is the inverse of [`InboundGroupSessionPickle::encrypt`].
+    pub fn from_encrypted(ciphertext: &str, pickle_key: &[u8; 32]) -> Result<Self, PickleError> {
+        unpickle(ciphertext, pickle_key)
+    }
+}
+
+impl From<&InboundGroupSession> for InboundGroupSessionPickle {
+    fn from(session: &InboundGroupSession) -> Self {
+        session.pickle()
+    }
+}
+
+impl From<InboundGroupSessionPickle> for InboundGroupSession {
+    fn from(pickle: InboundGroupSessionPickle) -> Self {
+        Self {
+            initial_ratchet: pickle.initial_ratchet.clone(),
+            latest_ratchet: pickle.initial_ratchet,
+            signing_key: pickle.signing_key,
+            signing_key_verified: pickle.signing_key_verified,
+            config: pickle.config,
+        }
+    }
+}
+
+impl From<&GroupSession> for InboundGroupSession {
+    fn from(session: &GroupSession) -> Self {
+        Self::new(&session.session_key(), session.session_config())
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use super::InboundGroupSession;
+    use crate::megolm::{GroupSession, SessionConfig, SessionOrdering};
+
+    #[test]
+    fn advance_inbound_session() {
+        let mut session = InboundGroupSession::from(&GroupSession::new(Default::default()));
+
+        assert_eq!(session.first_known_index(), 0);
+        assert_eq!(session.latest_ratchet.index(), 0);
+
+        assert!(session.advance_to(10));
+        assert_eq!(session.first_known_index(), 10);
+        assert_eq!(session.latest_ratchet.index(), 10);
+
+        assert!(!session.advance_to(10));
+
+        assert!(session.advance_to(20));
+        assert_eq!(session.first_known_index(), 20);
+        assert_eq!(session.latest_ratchet.index(), 20);
+    }
+
+    #[test]
+    fn connecting() {
+        let outbound = GroupSession::new(Default::default());
+        let mut session = InboundGroupSession::from(&outbound);
+        let mut clone = InboundGroupSession::from(&outbound);
+
+        assert!(session.connected(&mut clone));
+        assert!(clone.connected(&mut session));
+
+        clone.advance_to(10);
+
+        assert!(session.connected(&mut clone));
+        assert!(clone.connected(&mut session));
+
+        let mut other = InboundGroupSession::from(&GroupSession::new(Default::default()));
+
+        assert!(!session.connected(&mut other));
+        assert!(!clone.connected(&mut other));
+
+        other.signing_key = session.signing_key;
+
+        assert!(!session.connected(&mut other));
+        assert!(!clone.connected(&mut other));
+
+        let session_key = session.export_at_first_known_index();
+        let mut different_config =
+            InboundGroupSession::import(&session_key, SessionConfig::version_1());
+
+        assert!(!session.connected(&mut different_config));
+        assert!(!different_config.connected(&mut session));
+    }
+
+    #[test]
+    fn comparison() {
+        let outbound = GroupSession::new(Default::default());
+        let mut session = InboundGroupSession::from(&outbound);
+        let mut clone = InboundGroupSession::from(&outbound);
+
+        assert_eq!(session.compare(&mut clone), SessionOrdering::Equal);
+        assert_eq!(clone.compare(&mut session), SessionOrdering::Equal);
+
+        clone.advance_to(10);
+
+        assert_eq!(session.compare(&mut clone), SessionOrdering::Better);
+        assert_eq!(clone.compare(&mut session), SessionOrdering::Worse);
+
+        let mut other = InboundGroupSession::from(&GroupSession::new(Default::default()));
+
+        assert_eq!(session.compare(&mut other), SessionOrdering::Unconnected);
+        assert_eq!(clone.compare(&mut other), SessionOrdering::Unconnected);
+
+        other.signing_key = session.signing_key;
+
+        assert_eq!(session.compare(&mut other), SessionOrdering::Unconnected);
+        assert_eq!(clone.compare(&mut other), SessionOrdering::Unconnected);
+    }
+
+    #[test]
+    fn upgrade() {
+        let session = GroupSession::new(Default::default());
+        let session_key = session.session_key();
+
+        let mut first_session = InboundGroupSession::new(&session_key, Default::default());
+
+        // This one is less trusted because it's imported from an `ExportedSessionKey`.
+        let mut second_session =
+            InboundGroupSession::import(&first_session.export_at(10).unwrap(), Default::default());
+        assert!(!second_session.signing_key_verified);
+
+        assert_eq!(first_session.compare(&mut second_session), SessionOrdering::Better);
+
+        let mut merged = second_session.merge(&mut first_session).unwrap();
+
+        assert!(merged.signing_key_verified);
+        assert_eq!(merged.compare(&mut second_session), SessionOrdering::Better);
+        assert_eq!(merged.compare(&mut first_session), SessionOrdering::Equal);
+    }
+
+    /// Test that [`InboundGroupSession::get_cipher_at`] correctly handles the
+    /// correct range of message indices.`
+    #[cfg(feature = "low-level-api")]
+    #[test]
+    fn get_cipher_at() {
+        let mut group_session = GroupSession::new(Default::default());
+
+        // Advance the ratchet a few times by calling `encrypt`.
+        group_session.encrypt("test1");
+        group_session.encrypt("test2");
+
+        let session = InboundGroupSession::from(&group_session);
+
+        // The inbound session will only be able to decrypt messages from
+        // indices starting at 2 (as we advanced the ratchet twice before
+        // creating the inbound session)
+        assert!(session.get_cipher_at(0).is_none());
+        assert!(session.get_cipher_at(1).is_none());
+        assert!(session.get_cipher_at(2).is_some());
+        assert!(session.get_cipher_at(1000).is_some());
+
+        // Now check that we actually *do* advance the ratchet. We do this by
+        // checking that the ratchet changes.
+        assert_ne!(
+            session.get_cipher_at(2).unwrap().encrypt(b""),
+            session.get_cipher_at(3).unwrap().encrypt(b"")
+        );
+        assert_ne!(
+            session.get_cipher_at(3).unwrap().encrypt(b""),
+            session.get_cipher_at(1000).unwrap().encrypt(b"")
+        );
+    }
+}
+
\ No newline at end of file diff --git a/src/vodozemac/megolm/message.rs.html b/src/vodozemac/megolm/message.rs.html new file mode 100644 index 00000000..3d66df62 --- /dev/null +++ b/src/vodozemac/megolm/message.rs.html @@ -0,0 +1,699 @@ +message.rs - source +
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+195
+196
+197
+198
+199
+200
+201
+202
+203
+204
+205
+206
+207
+208
+209
+210
+211
+212
+213
+214
+215
+216
+217
+218
+219
+220
+221
+222
+223
+224
+225
+226
+227
+228
+229
+230
+231
+232
+233
+234
+235
+236
+237
+238
+239
+240
+241
+242
+243
+244
+245
+246
+247
+248
+249
+250
+251
+252
+253
+254
+255
+256
+257
+258
+259
+260
+261
+262
+263
+264
+265
+266
+267
+268
+269
+270
+271
+272
+273
+274
+275
+276
+277
+278
+279
+280
+281
+282
+283
+284
+285
+286
+287
+288
+289
+290
+291
+292
+293
+294
+295
+296
+297
+298
+299
+300
+301
+302
+303
+304
+305
+306
+307
+308
+309
+310
+311
+312
+313
+314
+315
+316
+317
+318
+319
+320
+321
+322
+323
+324
+325
+326
+327
+328
+329
+330
+331
+332
+333
+334
+335
+336
+337
+338
+339
+340
+341
+342
+343
+344
+345
+346
+347
+348
+
// Copyright 2021 The Matrix.org Foundation C.I.C.
+// Copyright 2022 Damir Jelić
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+use std::fmt::Debug;
+
+use prost::Message;
+use serde::{Deserialize, Serialize};
+
+use crate::{
+    cipher::{Cipher, Mac, MessageMac},
+    types::{Ed25519Keypair, Ed25519Signature},
+    utilities::{base64_decode, base64_encode, extract_mac, VarInt},
+    DecodeError,
+};
+#[cfg(feature = "low-level-api")]
+use crate::{Ed25519PublicKey, SignatureError};
+
+const MAC_TRUNCATED_VERSION: u8 = 3;
+const VERSION: u8 = 4;
+
+/// An encrypted Megolm message.
+///
+/// Contains metadata that is required to find the correct ratchet state of a
+/// [`InboundGroupSession`] necessary to decrypt the message.
+///
+/// [`InboundGroupSession`]: crate::megolm::InboundGroupSession
+#[derive(Clone, PartialEq, Eq)]
+pub struct MegolmMessage {
+    pub(super) version: u8,
+    pub(super) ciphertext: Vec<u8>,
+    pub(super) message_index: u32,
+    pub(super) mac: MessageMac,
+    pub(super) signature: Ed25519Signature,
+}
+
+impl MegolmMessage {
+    const MESSAGE_TRUNCATED_SUFFIX_LENGTH: usize = Mac::TRUNCATED_LEN + Ed25519Signature::LENGTH;
+    const MESSAGE_SUFFIX_LENGTH: usize = Mac::LENGTH + Ed25519Signature::LENGTH;
+
+    /// The actual ciphertext of the message.
+    pub fn ciphertext(&self) -> &[u8] {
+        &self.ciphertext
+    }
+
+    /// The index of the message that was used when the message was encrypted.
+    pub fn message_index(&self) -> u32 {
+        self.message_index
+    }
+
+    /// Get the megolm message's mac.
+    pub fn mac(&self) -> &[u8] {
+        self.mac.as_bytes()
+    }
+
+    /// Get a reference to the megolm message's signature.
+    pub fn signature(&self) -> &Ed25519Signature {
+        &self.signature
+    }
+
+    /// Try to decode the given byte slice as a [`MegolmMessage`].
+    ///
+    /// The expected format of the byte array is described in the
+    /// [`MegolmMessage::to_bytes()`] method.
+    pub fn from_bytes(message: &[u8]) -> Result<Self, DecodeError> {
+        Self::try_from(message)
+    }
+
+    /// Encode the [`MegolmMessage`] as an array of bytes.
+    ///
+    /// Megolm messages consist of a one byte version, followed by a variable
+    /// length payload, a fixed length message authentication code, and a fixed
+    /// length signature.
+    ///
+    /// ```text
+    /// +---+------------------------------------+-----------+------------------+
+    /// | V | Payload Bytes                      | MAC Bytes | Signature Bytes  |
+    /// +---+------------------------------------+-----------+------------------+
+    /// 0   1                                    N          N+8                N+72   bytes
+    /// ```
+    ///
+    /// The payload uses a format based on the Protocol Buffers encoding. It
+    /// consists of the following key-value pairs:
+    ///
+    ///    **Name**  |**Tag**|**Type**|            **Meaning**
+    /// :-----------:|:-----:|:------:|:---------------------------------------:
+    /// Message-Index|  0x08 | Integer|The index of the ratchet, i
+    /// Cipher-Text  |  0x12 | String |The cipher-text, Xi, of the message
+    pub fn to_bytes(&self) -> Vec<u8> {
+        let mut message = self.encode_message();
+
+        message.extend(self.mac.as_bytes());
+        message.extend(self.signature.to_bytes());
+
+        message
+    }
+
+    /// Try to decode the given string as a [`MegolmMessage`].
+    ///
+    /// The string needs to be a base64 encoded byte array that follows the
+    /// format described in the [`MegolmMessage::to_bytes()`] method.
+    pub fn from_base64(message: &str) -> Result<Self, DecodeError> {
+        Self::try_from(message)
+    }
+
+    /// Encode the [`MegolmMessage`] as a string.
+    ///
+    /// This method first calls [`MegolmMessage::to_bytes()`] and then encodes
+    /// the resulting byte array as a string using base64 encoding.
+    pub fn to_base64(&self) -> String {
+        base64_encode(self.to_bytes())
+    }
+
+    /// Set the signature of the message, verifying that the signature matches
+    /// the signing key.
+    #[cfg(feature = "low-level-api")]
+    pub fn add_signature(
+        &mut self,
+        signature: Ed25519Signature,
+        signing_key: Ed25519PublicKey,
+    ) -> Result<(), SignatureError> {
+        signing_key.verify(&self.to_signature_bytes(), &signature)?;
+
+        self.signature = signature;
+
+        Ok(())
+    }
+
+    fn encode_message(&self) -> Vec<u8> {
+        let message = ProtobufMegolmMessage {
+            message_index: self.message_index,
+            ciphertext: self.ciphertext.clone(),
+        };
+
+        message.encode_manual(self.version)
+    }
+
+    fn set_mac(&mut self, mac: Mac) {
+        match self.mac {
+            MessageMac::Truncated(_) => self.mac = mac.truncate().into(),
+            MessageMac::Full(_) => self.mac = mac.into(),
+        }
+    }
+
+    /// Create a new [`MegolmMessage`] with the given plaintext and keys.
+    #[cfg(feature = "low-level-api")]
+    pub fn encrypt(
+        message_index: u32,
+        cipher: &Cipher,
+        signing_key: &Ed25519Keypair,
+        plaintext: &[u8],
+    ) -> Self {
+        MegolmMessage::encrypt_truncated_mac(message_index, cipher, signing_key, plaintext)
+    }
+
+    /// Implementation of [`MegolmMessage::encrypt`] that is used by rest of the
+    /// crate.
+    pub(super) fn encrypt_full_mac(
+        message_index: u32,
+        cipher: &Cipher,
+        signing_key: &Ed25519Keypair,
+        plaintext: &[u8],
+    ) -> Self {
+        let ciphertext = cipher.encrypt(plaintext);
+
+        let message = Self {
+            version: VERSION,
+            ciphertext,
+            message_index,
+            mac: Mac([0u8; Mac::LENGTH]).into(),
+            signature: Ed25519Signature::from_slice(&[0; Ed25519Signature::LENGTH])
+                .expect("Can't create an empty signature"),
+        };
+
+        Self::encrypt_helper(cipher, signing_key, message)
+    }
+
+    pub(super) fn encrypt_truncated_mac(
+        message_index: u32,
+        cipher: &Cipher,
+        signing_key: &Ed25519Keypair,
+        plaintext: &[u8],
+    ) -> Self {
+        let ciphertext = cipher.encrypt(plaintext);
+
+        let message = Self {
+            version: MAC_TRUNCATED_VERSION,
+            ciphertext,
+            message_index,
+            mac: [0u8; Mac::TRUNCATED_LEN].into(),
+            signature: Ed25519Signature::from_slice(&[0; Ed25519Signature::LENGTH])
+                .expect("Can't create an empty signature"),
+        };
+
+        Self::encrypt_helper(cipher, signing_key, message)
+    }
+
+    fn encrypt_helper(
+        cipher: &Cipher,
+        signing_key: &Ed25519Keypair,
+        mut message: MegolmMessage,
+    ) -> Self {
+        let mac = cipher.mac(&message.to_mac_bytes());
+        message.set_mac(mac);
+
+        let signature = signing_key.sign(&message.to_signature_bytes());
+        message.signature = signature;
+
+        message
+    }
+
+    pub(super) fn to_mac_bytes(&self) -> Vec<u8> {
+        self.encode_message()
+    }
+
+    pub(super) fn to_signature_bytes(&self) -> Vec<u8> {
+        let mut message = self.encode_message();
+        message.extend(self.mac.as_bytes());
+
+        message
+    }
+}
+
+impl Serialize for MegolmMessage {
+    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+    where
+        S: serde::Serializer,
+    {
+        let message = self.to_base64();
+        serializer.serialize_str(&message)
+    }
+}
+
+impl<'de> Deserialize<'de> for MegolmMessage {
+    fn deserialize<D: serde::Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
+        let ciphertext = String::deserialize(d)?;
+        Self::from_base64(&ciphertext).map_err(serde::de::Error::custom)
+    }
+}
+
+impl TryFrom<&str> for MegolmMessage {
+    type Error = DecodeError;
+
+    fn try_from(message: &str) -> Result<Self, Self::Error> {
+        let decoded = base64_decode(message)?;
+
+        Self::try_from(decoded)
+    }
+}
+
+impl TryFrom<Vec<u8>> for MegolmMessage {
+    type Error = DecodeError;
+
+    fn try_from(message: Vec<u8>) -> Result<Self, Self::Error> {
+        Self::try_from(message.as_slice())
+    }
+}
+
+impl TryFrom<&[u8]> for MegolmMessage {
+    type Error = DecodeError;
+
+    fn try_from(message: &[u8]) -> Result<Self, Self::Error> {
+        let version = *message.first().ok_or(DecodeError::MissingVersion)?;
+
+        let suffix_length = match version {
+            VERSION => Self::MESSAGE_SUFFIX_LENGTH,
+            MAC_TRUNCATED_VERSION => Self::MESSAGE_TRUNCATED_SUFFIX_LENGTH,
+            _ => return Err(DecodeError::InvalidVersion(VERSION, version)),
+        };
+
+        if message.len() < suffix_length + 2 {
+            Err(DecodeError::MessageTooShort(message.len()))
+        } else {
+            let inner = ProtobufMegolmMessage::decode(
+                message
+                    .get(1..message.len() - suffix_length)
+                    .ok_or_else(|| DecodeError::MessageTooShort(message.len()))?,
+            )?;
+
+            let signature_location = message.len() - Ed25519Signature::LENGTH;
+            let signature_slice = &message[signature_location..];
+            let signature = Ed25519Signature::from_slice(signature_slice)?;
+
+            let mac_slice = &message[message.len() - suffix_length..];
+            let mac = extract_mac(mac_slice, version == MAC_TRUNCATED_VERSION);
+
+            Ok(MegolmMessage {
+                version,
+                ciphertext: inner.ciphertext,
+                message_index: inner.message_index,
+                mac,
+                signature,
+            })
+        }
+    }
+}
+
+impl Debug for MegolmMessage {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        let Self { version, ciphertext: _, message_index, mac: _, signature: _ } = self;
+
+        f.debug_struct("MegolmMessage")
+            .field("version", version)
+            .field("message_index", message_index)
+            .finish_non_exhaustive()
+    }
+}
+
+#[derive(Clone, Message, PartialEq, Eq)]
+struct ProtobufMegolmMessage {
+    #[prost(uint32, tag = "1")]
+    pub message_index: u32,
+    #[prost(bytes, tag = "2")]
+    pub ciphertext: Vec<u8>,
+}
+
+impl ProtobufMegolmMessage {
+    const INDEX_TAG: &'static [u8; 1] = b"\x08";
+    const CIPHER_TAG: &'static [u8; 1] = b"\x12";
+
+    fn encode_manual(&self, version: u8) -> Vec<u8> {
+        // Prost optimizes away the message index if it's 0, libolm can't decode
+        // this, so encode our messages the pedestrian way instead.
+        let index = self.message_index.to_var_int();
+        let ciphertext_len = self.ciphertext.len().to_var_int();
+
+        [
+            [version].as_ref(),
+            Self::INDEX_TAG.as_ref(),
+            &index,
+            Self::CIPHER_TAG.as_ref(),
+            &ciphertext_len,
+            &self.ciphertext,
+        ]
+        .concat()
+    }
+}
+
\ No newline at end of file diff --git a/src/vodozemac/megolm/mod.rs.html b/src/vodozemac/megolm/mod.rs.html new file mode 100644 index 00000000..a0adaba4 --- /dev/null +++ b/src/vodozemac/megolm/mod.rs.html @@ -0,0 +1,641 @@ +mod.rs - source +
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+195
+196
+197
+198
+199
+200
+201
+202
+203
+204
+205
+206
+207
+208
+209
+210
+211
+212
+213
+214
+215
+216
+217
+218
+219
+220
+221
+222
+223
+224
+225
+226
+227
+228
+229
+230
+231
+232
+233
+234
+235
+236
+237
+238
+239
+240
+241
+242
+243
+244
+245
+246
+247
+248
+249
+250
+251
+252
+253
+254
+255
+256
+257
+258
+259
+260
+261
+262
+263
+264
+265
+266
+267
+268
+269
+270
+271
+272
+273
+274
+275
+276
+277
+278
+279
+280
+281
+282
+283
+284
+285
+286
+287
+288
+289
+290
+291
+292
+293
+294
+295
+296
+297
+298
+299
+300
+301
+302
+303
+304
+305
+306
+307
+308
+309
+310
+311
+312
+313
+314
+315
+316
+317
+318
+319
+
// Copyright 2021 The Matrix.org Foundation C.I.C.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+//! An implementation of the Megolm ratchet.
+
+mod group_session;
+mod inbound_group_session;
+pub(crate) mod message;
+mod ratchet;
+mod session_config;
+mod session_keys;
+
+pub use group_session::{GroupSession, GroupSessionPickle};
+pub use inbound_group_session::{
+    DecryptedMessage, DecryptionError, InboundGroupSession, InboundGroupSessionPickle,
+    SessionOrdering,
+};
+pub use message::MegolmMessage;
+pub use session_config::SessionConfig;
+pub use session_keys::{ExportedSessionKey, SessionKey, SessionKeyDecodeError};
+
+fn default_config() -> SessionConfig {
+    SessionConfig::version_1()
+}
+
+#[cfg(feature = "libolm-compat")]
+mod libolm {
+    use matrix_pickle::Decode;
+    use zeroize::Zeroize;
+
+    use super::ratchet::Ratchet;
+
+    #[derive(Zeroize, Decode)]
+    #[zeroize(drop)]
+    pub(crate) struct LibolmRatchetPickle {
+        #[secret]
+        ratchet: Box<[u8; 128]>,
+        index: u32,
+    }
+
+    impl From<&LibolmRatchetPickle> for Ratchet {
+        fn from(pickle: &LibolmRatchetPickle) -> Self {
+            Ratchet::from_bytes(pickle.ratchet.clone(), pickle.index)
+        }
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use anyhow::Result;
+    use olm_rs::{
+        inbound_group_session::OlmInboundGroupSession,
+        outbound_group_session::OlmOutboundGroupSession,
+    };
+
+    use super::{ExportedSessionKey, GroupSession, InboundGroupSession, MegolmMessage};
+    use crate::{
+        megolm::{GroupSessionPickle, InboundGroupSessionPickle, SessionConfig, SessionKey},
+        run_corpus,
+    };
+
+    const PICKLE_KEY: [u8; 32] = [0u8; 32];
+
+    #[test]
+    fn encrypting() -> Result<()> {
+        let mut session = GroupSession::new(SessionConfig::version_1());
+        let session_key = session.session_key();
+
+        let olm_session = OlmInboundGroupSession::new(&session_key.to_base64())?;
+
+        let plaintext = "It's a secret to everybody";
+        let message = session.encrypt(plaintext).to_base64();
+
+        let (decrypted, _) = olm_session.decrypt(message)?;
+
+        assert_eq!(decrypted, plaintext);
+
+        let plaintext = "Another secret";
+        let message = session.encrypt(plaintext).to_base64();
+
+        let (decrypted, _) = olm_session.decrypt(message)?;
+        assert_eq!(decrypted, plaintext);
+
+        let plaintext = "And another secret";
+        let message = session.encrypt(plaintext).to_base64();
+        let (decrypted, _) = olm_session.decrypt(message)?;
+
+        assert_eq!(decrypted, plaintext);
+
+        let plaintext = "Last secret";
+
+        for _ in 1..2000 {
+            session.encrypt(plaintext);
+        }
+
+        let message = session.encrypt(plaintext).to_base64();
+        let (decrypted, _) = olm_session.decrypt(message)?;
+
+        assert_eq!(decrypted, plaintext);
+
+        Ok(())
+    }
+
+    #[test]
+    fn decrypting() -> Result<()> {
+        let olm_session = OlmOutboundGroupSession::new();
+
+        let session_key = SessionKey::from_base64(&olm_session.session_key())?;
+
+        let mut session = InboundGroupSession::new(&session_key, SessionConfig::version_1());
+
+        let plaintext = "Hello";
+        let message = olm_session.encrypt(plaintext).as_str().try_into()?;
+
+        let decrypted = session.decrypt(&message)?;
+
+        assert_eq!(decrypted.plaintext, plaintext.as_bytes());
+        assert_eq!(decrypted.message_index, 0);
+
+        let plaintext = "Another secret";
+        let message = olm_session.encrypt(plaintext).as_str().try_into()?;
+
+        let decrypted = session.decrypt(&message)?;
+
+        assert_eq!(decrypted.plaintext, plaintext.as_bytes());
+        assert_eq!(decrypted.message_index, 1);
+
+        let third_plaintext = "And another secret";
+        let third_message = olm_session.encrypt(third_plaintext).as_str().try_into()?;
+        let decrypted = session.decrypt(&third_message)?;
+
+        assert_eq!(decrypted.plaintext, third_plaintext.as_bytes());
+        assert_eq!(decrypted.message_index, 2);
+
+        let plaintext = "Last secret";
+
+        for _ in 1..2000 {
+            olm_session.encrypt(plaintext);
+        }
+
+        let message = olm_session.encrypt(plaintext).as_str().try_into()?;
+        let decrypted = session.decrypt(&message)?;
+
+        assert_eq!(decrypted.plaintext, plaintext.as_bytes());
+        assert_eq!(decrypted.message_index, 2002);
+
+        let decrypted = session.decrypt(&third_message)?;
+
+        assert_eq!(decrypted.plaintext, third_plaintext.as_bytes());
+        assert_eq!(decrypted.message_index, 2);
+
+        Ok(())
+    }
+
+    #[test]
+    fn exporting() -> Result<()> {
+        let mut session = GroupSession::new(Default::default());
+        let mut inbound =
+            InboundGroupSession::new(&session.session_key(), session.session_config());
+
+        assert_eq!(session.session_id(), inbound.session_id());
+
+        let first_plaintext = "It's a secret to everybody".as_bytes();
+        let first_message = session.encrypt(first_plaintext);
+        let second_plaintext = "It's dangerous to go alone. Take this!".as_bytes();
+        let second_message = session.encrypt(second_plaintext);
+
+        let decrypted = inbound.decrypt(&first_message)?;
+
+        assert_eq!(decrypted.plaintext, first_plaintext);
+        assert_eq!(decrypted.message_index, 0);
+
+        let export = inbound.export_at(1).expect("Can export at the initial index.");
+        let mut imported = InboundGroupSession::import(&export, session.session_config());
+
+        assert_eq!(session.session_id(), imported.session_id());
+
+        imported.decrypt(&first_message).expect_err("Can't decrypt at the initial index.");
+        let second_decrypted =
+            imported.decrypt(&second_message).expect("Can decrypt at the next index.");
+        assert_eq!(
+            second_plaintext, second_decrypted.plaintext,
+            "Decrypted plaintext differs from original."
+        );
+        assert_eq!(1, second_decrypted.message_index, "Expected message index to be 1.");
+
+        assert!(imported.export_at(0).is_none(), "Can't export at the initial index.");
+        assert!(imported.export_at(1).is_some(), "Can export at the next index.");
+
+        Ok(())
+    }
+
+    #[test]
+    fn group_session_pickling_roundtrip_is_identity() -> Result<()> {
+        let session = GroupSession::new(Default::default());
+
+        let pickle = session.pickle().encrypt(&PICKLE_KEY);
+
+        let decrypted_pickle = GroupSessionPickle::from_encrypted(&pickle, &PICKLE_KEY)?;
+        let unpickled_group_session = GroupSession::from_pickle(decrypted_pickle);
+        let repickle = unpickled_group_session.pickle();
+
+        assert_eq!(session.session_id(), unpickled_group_session.session_id());
+
+        let decrypted_pickle = GroupSessionPickle::from_encrypted(&pickle, &PICKLE_KEY)?;
+        let pickle = serde_json::to_value(decrypted_pickle)?;
+        let repickle = serde_json::to_value(repickle)?;
+
+        assert_eq!(pickle, repickle);
+
+        Ok(())
+    }
+
+    #[test]
+    fn inbound_group_session_pickling_roundtrip_is_identity() -> Result<()> {
+        let session = GroupSession::new(Default::default());
+        let session = InboundGroupSession::from(&session);
+
+        let pickle = session.pickle().encrypt(&PICKLE_KEY);
+
+        let decrypted_pickle = InboundGroupSessionPickle::from_encrypted(&pickle, &PICKLE_KEY)?;
+        let unpickled_group_session = InboundGroupSession::from_pickle(decrypted_pickle);
+        let repickle = unpickled_group_session.pickle();
+
+        assert_eq!(session.session_id(), unpickled_group_session.session_id());
+
+        let decrypted_pickle = InboundGroupSessionPickle::from_encrypted(&pickle, &PICKLE_KEY)?;
+        let pickle = serde_json::to_value(decrypted_pickle)?;
+        let repickle = serde_json::to_value(repickle)?;
+
+        assert_eq!(pickle, repickle);
+
+        Ok(())
+    }
+
+    #[test]
+    #[cfg(feature = "libolm-compat")]
+    fn libolm_inbound_unpickling() -> Result<()> {
+        let session = GroupSession::new(SessionConfig::version_1());
+        let session_key = session.session_key();
+
+        let olm = OlmInboundGroupSession::new(&session_key.to_base64())?;
+
+        let key = b"DEFAULT_PICKLE_KEY";
+        let pickle = olm.pickle(olm_rs::PicklingMode::Encrypted { key: key.to_vec() });
+
+        let unpickled = InboundGroupSession::from_libolm_pickle(&pickle, key)?;
+
+        assert_eq!(olm.session_id(), unpickled.session_id());
+        assert_eq!(olm.first_known_index(), unpickled.first_known_index());
+
+        Ok(())
+    }
+
+    #[test]
+    #[cfg(feature = "libolm-compat")]
+    fn libolm_unpickling() -> Result<()> {
+        let olm = OlmOutboundGroupSession::new();
+        let session_key = SessionKey::from_base64(&olm.session_key())?;
+        let mut inbound_session =
+            InboundGroupSession::new(&session_key, SessionConfig::version_1());
+
+        let key = b"DEFAULT_PICKLE_KEY";
+        let pickle = olm.pickle(olm_rs::PicklingMode::Encrypted { key: key.to_vec() });
+
+        let mut unpickled = GroupSession::from_libolm_pickle(&pickle, key)?;
+
+        assert_eq!(olm.session_id(), unpickled.session_id());
+        assert_eq!(olm.session_message_index(), unpickled.message_index());
+
+        let plaintext = "It's a secret to everybody".as_bytes();
+        let message = unpickled.encrypt(plaintext);
+
+        let decrypted = inbound_session
+            .decrypt(&message)
+            .expect("We should be able to decrypt a message with a migrated Megolm session");
+
+        assert_eq!(decrypted.plaintext, plaintext);
+        assert_eq!(decrypted.message_index, 0);
+
+        Ok(())
+    }
+
+    #[test]
+    fn fuzz_corpus_decoding() {
+        run_corpus("megolm-decoding", |data| {
+            let _ = MegolmMessage::from_bytes(data);
+        });
+    }
+
+    #[test]
+    fn fuzz_corpus_session_creation() {
+        run_corpus("megolm-session-creation", |data| {
+            if let Ok(session_key) = SessionKey::from_bytes(data) {
+                let _ = InboundGroupSession::new(&session_key, Default::default());
+            }
+        });
+    }
+
+    #[test]
+    fn fuzz_corpus_session_import() {
+        run_corpus("megolm-session-import", |data| {
+            if let Ok(session_key) = ExportedSessionKey::from_bytes(data) {
+                let _ = InboundGroupSession::import(&session_key, Default::default());
+            }
+        });
+    }
+}
+
\ No newline at end of file diff --git a/src/vodozemac/megolm/ratchet.rs.html b/src/vodozemac/megolm/ratchet.rs.html new file mode 100644 index 00000000..4e4086ed --- /dev/null +++ b/src/vodozemac/megolm/ratchet.rs.html @@ -0,0 +1,549 @@ +ratchet.rs - source +
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+195
+196
+197
+198
+199
+200
+201
+202
+203
+204
+205
+206
+207
+208
+209
+210
+211
+212
+213
+214
+215
+216
+217
+218
+219
+220
+221
+222
+223
+224
+225
+226
+227
+228
+229
+230
+231
+232
+233
+234
+235
+236
+237
+238
+239
+240
+241
+242
+243
+244
+245
+246
+247
+248
+249
+250
+251
+252
+253
+254
+255
+256
+257
+258
+259
+260
+261
+262
+263
+264
+265
+266
+267
+268
+269
+270
+271
+272
+273
+
// Copyright 2016 OpenMarket Ltd
+// Copyright 2021 The Matrix.org Foundation C.I.C.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+use hmac::{Hmac, Mac as _};
+use rand::{thread_rng, RngCore};
+use serde::{Deserialize, Deserializer, Serialize, Serializer};
+use sha2::{digest::CtOutput, Sha256};
+use subtle::{Choice, ConstantTimeEq};
+use thiserror::Error;
+use zeroize::Zeroize;
+
+const ADVANCEMENT_SEEDS: [&[u8; 1]; Ratchet::RATCHET_PART_COUNT] =
+    [b"\x00", b"\x01", b"\x02", b"\x03"];
+
+#[derive(Serialize, Deserialize, Zeroize, Clone)]
+#[zeroize(drop)]
+pub(super) struct Ratchet {
+    inner: RatchetBytes,
+    counter: u32,
+}
+
+impl ConstantTimeEq for Ratchet {
+    fn ct_eq(&self, other: &Self) -> Choice {
+        // Short circuit on the counter, not on the contents of the ratchet.
+        if self.counter != other.counter {
+            Choice::from(0)
+        } else {
+            self.as_bytes().ct_eq(other.as_bytes())
+        }
+    }
+}
+
+#[derive(Zeroize, Clone)]
+#[zeroize(drop)]
+struct RatchetBytes(Box<[u8; Ratchet::RATCHET_LENGTH]>);
+
+impl RatchetBytes {
+    fn from_bytes(bytes: &[u8]) -> Result<Self, RatchetBytesError> {
+        let length = bytes.len();
+
+        if length != Ratchet::RATCHET_LENGTH {
+            Err(RatchetBytesError::InvalidLength(length))
+        } else {
+            let mut ratchet = Self(Box::new([0u8; Ratchet::RATCHET_LENGTH]));
+            ratchet.0.copy_from_slice(bytes);
+
+            Ok(ratchet)
+        }
+    }
+}
+
+impl Serialize for RatchetBytes {
+    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+    where
+        S: Serializer,
+    {
+        let bytes = &self.0;
+        bytes.serialize(serializer)
+    }
+}
+
+impl<'d> Deserialize<'d> for RatchetBytes {
+    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
+    where
+        D: Deserializer<'d>,
+    {
+        let mut bytes = <Vec<u8>>::deserialize(deserializer)?;
+        let ratchet = Self::from_bytes(bytes.as_ref()).map_err(serde::de::Error::custom)?;
+
+        bytes.zeroize();
+
+        Ok(ratchet)
+    }
+}
+
+struct RatchetPart<'a>(&'a mut [u8]);
+
+impl<'a> RatchetPart<'a> {
+    fn hash(&self, seed: &[u8]) -> CtOutput<Hmac<Sha256>> {
+        let mut hmac = Hmac::<Sha256>::new_from_slice(self.0).expect("Can't create a HMAC object");
+        hmac.update(seed);
+
+        hmac.finalize()
+    }
+
+    fn update(&mut self, new_part: &[u8]) {
+        self.0.copy_from_slice(new_part);
+    }
+}
+
+struct RatchetParts<'a> {
+    r_0: RatchetPart<'a>,
+    r_1: RatchetPart<'a>,
+    r_2: RatchetPart<'a>,
+    r_3: RatchetPart<'a>,
+}
+
+impl<'a> RatchetParts<'a> {
+    fn update(&'a mut self, from: usize, to: usize) {
+        let from = match from {
+            0 => &self.r_0,
+            1 => &self.r_1,
+            2 => &self.r_2,
+            3 => &self.r_3,
+            _ => unreachable!(),
+        };
+
+        let result = from.hash(ADVANCEMENT_SEEDS[to]);
+
+        let to = match to {
+            0 => &mut self.r_0,
+            1 => &mut self.r_1,
+            2 => &mut self.r_2,
+            3 => &mut self.r_3,
+            _ => unreachable!(),
+        };
+
+        to.update(&result.into_bytes());
+    }
+}
+
+impl Ratchet {
+    pub const RATCHET_LENGTH: usize = 128;
+    const RATCHET_PART_COUNT: usize = 4;
+    const LAST_RATCHET_INDEX: usize = Self::RATCHET_PART_COUNT - 1;
+
+    pub fn new() -> Self {
+        let mut rng = thread_rng();
+
+        let mut ratchet =
+            Self { inner: RatchetBytes(Box::new([0u8; Self::RATCHET_LENGTH])), counter: 0 };
+
+        rng.fill_bytes(ratchet.inner.0.as_mut());
+
+        ratchet
+    }
+
+    pub fn from_bytes(bytes: Box<[u8; Self::RATCHET_LENGTH]>, counter: u32) -> Self {
+        Self { inner: RatchetBytes(bytes), counter }
+    }
+
+    pub fn index(&self) -> u32 {
+        self.counter
+    }
+
+    pub fn as_bytes(&self) -> &[u8; Self::RATCHET_LENGTH] {
+        &self.inner.0
+    }
+
+    fn as_parts(&mut self) -> RatchetParts<'_> {
+        let (top, bottom) = self.inner.0.split_at_mut(64);
+
+        let (r_0, r_1) = top.split_at_mut(32);
+        let (r_2, r_3) = bottom.split_at_mut(32);
+
+        let r_0 = RatchetPart(r_0);
+        let r_1 = RatchetPart(r_1);
+        let r_2 = RatchetPart(r_2);
+        let r_3 = RatchetPart(r_3);
+
+        RatchetParts { r_0, r_1, r_2, r_3 }
+    }
+
+    pub fn advance(&mut self) {
+        let mut mask: u32 = 0x00FFFFFF;
+
+        // The index of the "slowest" part of the ratchet that needs to be
+        // advanced.
+        let mut h = 0;
+
+        self.counter += 1;
+
+        // Figure out which parts of the ratchet need to be advanced.
+        while h < Self::RATCHET_PART_COUNT {
+            if (self.counter & mask) == 0 {
+                break;
+            }
+
+            h += 1;
+            mask >>= 8;
+        }
+
+        let parts_to_advance = (h..=Self::LAST_RATCHET_INDEX).rev();
+
+        // Now advance R(h)...R(3) based on R(h).
+        for i in parts_to_advance {
+            let mut parts = self.as_parts();
+            parts.update(h, i);
+        }
+    }
+
+    pub fn advance_to(&mut self, advance_to: u32) {
+        for j in 0..Self::RATCHET_PART_COUNT {
+            let shift = (Self::LAST_RATCHET_INDEX - j) * 8;
+            let mask: u32 = !0u32 << shift;
+
+            // How many times do we need to rehash this part? `& 0xff` ensures
+            // we handle integer wrap-around correctly.
+            let mut steps = ((advance_to >> shift) - (self.counter >> shift)) & 0xff;
+
+            if steps == 0 {
+                // Deal with the edge case where the ratchet counter is slightly
+                // larger than the index we need to advance to. This should only
+                // happen for R(0) and implies that advance_to has wrapped
+                // around and we need to advance R(0) 256 times.
+                if advance_to < self.counter {
+                    steps = 0x100;
+                } else {
+                    continue;
+                }
+            }
+
+            // For all but the last step, we can just bump R(j) without regard
+            // to R(j+1)...R(3).
+            while steps > 1 {
+                let mut parts = self.as_parts();
+                parts.update(j, j);
+                steps -= 1;
+            }
+
+            // On the last step we also need to bump R(j+1)...R(3).
+            // (Theoretically, we could skip bumping R(j+2) if we're going to
+            // bump R(j+1) again, but the code to figure that out is a bit
+            // baroque and doesn't save us much).
+
+            let parts_to_update = (j..=Self::LAST_RATCHET_INDEX).rev();
+
+            for k in parts_to_update {
+                let mut parts = self.as_parts();
+                parts.update(j, k);
+            }
+
+            self.counter = advance_to & mask;
+        }
+    }
+}
+
+#[derive(Error, Debug)]
+enum RatchetBytesError {
+    #[error("Invalid Megolm ratchet length: expected 128, got {0}")]
+    InvalidLength(usize),
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    #[test]
+    fn advancing_high_counter_ratchet_doesnt_panic() {
+        let mut ratchet = Ratchet::new();
+        ratchet.counter = 0x00FFFFFF;
+        ratchet.advance();
+    }
+
+    #[test]
+    fn advance_to_with_high_counter_doesnt_panic() {
+        let mut ratchet = Ratchet::new();
+        ratchet.counter = (1 << 24) - 1;
+        ratchet.advance_to(1 << 24);
+    }
+}
+
\ No newline at end of file diff --git a/src/vodozemac/megolm/session_config.rs.html b/src/vodozemac/megolm/session_config.rs.html new file mode 100644 index 00000000..9dfae6cc --- /dev/null +++ b/src/vodozemac/megolm/session_config.rs.html @@ -0,0 +1,113 @@ +session_config.rs - source +
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+
// Copyright 2022 The Matrix.org Foundation C.I.C.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+use serde::{Deserialize, Serialize};
+
+/// A struct to configure how Megolm sessions should work under the hood.
+/// Currently only the MAC truncation behaviour can be configured.
+#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, Eq)]
+pub struct SessionConfig {
+    pub(super) version: Version,
+}
+
+#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, Eq)]
+pub(super) enum Version {
+    V1 = 1,
+    V2 = 2,
+}
+
+impl SessionConfig {
+    /// Get the numeric version of this `SessionConfig`.
+    pub fn version(&self) -> u8 {
+        self.version as u8
+    }
+
+    /// Create a `SessionConfig` for the Megolm version 1. This version of
+    /// Megolm uses AES-256 and HMAC with a truncated MAC to encrypt individual
+    /// messages. The MAC will be truncated to 8 bytes.
+    pub fn version_1() -> Self {
+        SessionConfig { version: Version::V1 }
+    }
+
+    /// Create a `SessionConfig` for the Megolm version 2. This version of
+    /// Megolm uses AES-256 and HMAC to encrypt individual messages. The MAC
+    /// won't be truncated.
+    pub fn version_2() -> Self {
+        SessionConfig { version: Version::V2 }
+    }
+}
+
+impl Default for SessionConfig {
+    fn default() -> Self {
+        Self::version_2()
+    }
+}
+
\ No newline at end of file diff --git a/src/vodozemac/megolm/session_keys.rs.html b/src/vodozemac/megolm/session_keys.rs.html new file mode 100644 index 00000000..e2036f96 --- /dev/null +++ b/src/vodozemac/megolm/session_keys.rs.html @@ -0,0 +1,771 @@ +session_keys.rs - source +
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+195
+196
+197
+198
+199
+200
+201
+202
+203
+204
+205
+206
+207
+208
+209
+210
+211
+212
+213
+214
+215
+216
+217
+218
+219
+220
+221
+222
+223
+224
+225
+226
+227
+228
+229
+230
+231
+232
+233
+234
+235
+236
+237
+238
+239
+240
+241
+242
+243
+244
+245
+246
+247
+248
+249
+250
+251
+252
+253
+254
+255
+256
+257
+258
+259
+260
+261
+262
+263
+264
+265
+266
+267
+268
+269
+270
+271
+272
+273
+274
+275
+276
+277
+278
+279
+280
+281
+282
+283
+284
+285
+286
+287
+288
+289
+290
+291
+292
+293
+294
+295
+296
+297
+298
+299
+300
+301
+302
+303
+304
+305
+306
+307
+308
+309
+310
+311
+312
+313
+314
+315
+316
+317
+318
+319
+320
+321
+322
+323
+324
+325
+326
+327
+328
+329
+330
+331
+332
+333
+334
+335
+336
+337
+338
+339
+340
+341
+342
+343
+344
+345
+346
+347
+348
+349
+350
+351
+352
+353
+354
+355
+356
+357
+358
+359
+360
+361
+362
+363
+364
+365
+366
+367
+368
+369
+370
+371
+372
+373
+374
+375
+376
+377
+378
+379
+380
+381
+382
+383
+384
+
// Copyright 2022 The Matrix.org Foundation C.I.C.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+use std::io::{Cursor, Read};
+
+use serde::{Deserialize, Serialize};
+use thiserror::Error;
+use zeroize::Zeroize;
+
+use super::ratchet::Ratchet;
+use crate::{
+    utilities::{base64_decode, base64_encode},
+    Ed25519PublicKey, Ed25519Signature, SignatureError,
+};
+
+/// Error type describing failure modes for the `SessionKey` and
+/// `ExportedSessionKey` decoding.
+#[derive(Debug, Error)]
+pub enum SessionKeyDecodeError {
+    /// The encoded session key had a unsupported version.
+    #[error("The session key had a invalid version, expected {0}, got {1}")]
+    Version(u8, u8),
+    /// The encoded session key didn't contain enough data to be decoded.
+    #[error("The session key was too short {0}")]
+    Read(#[from] std::io::Error),
+    /// The encoded session key wasn't valid base64.
+    #[error("The session key wasn't valid base64: {0}")]
+    Base64(#[from] base64::DecodeError),
+    /// The signature on the session key was invalid.
+    #[error("The signature on the session key was invalid: {0}")]
+    Signature(#[from] SignatureError),
+    /// The encoded session key contains an invalid public key.
+    #[error("The public key of session was invalid: {0}")]
+    PublicKey(#[from] crate::KeyError),
+}
+
+/// The exported session key.
+///
+/// This uses the same format as the `SessionKey` minus the signature at the
+/// end.
+pub struct ExportedSessionKey {
+    pub(crate) ratchet_index: u32,
+    pub(crate) ratchet: Box<[u8; 128]>,
+    pub(crate) signing_key: Ed25519PublicKey,
+}
+
+impl ExportedSessionKey {
+    const VERSION: u8 = 1;
+
+    pub(super) fn new(ratchet: &Ratchet, signing_key: Ed25519PublicKey) -> Self {
+        let ratchet_index = ratchet.index();
+        let mut ratchet_bytes = Box::new([0u8; Ratchet::RATCHET_LENGTH]);
+
+        ratchet_bytes.copy_from_slice(ratchet.as_bytes());
+
+        Self { ratchet_index, ratchet: ratchet_bytes, signing_key }
+    }
+
+    fn to_bytes_with_version(&self, version: u8) -> Vec<u8> {
+        let index = self.ratchet_index.to_be_bytes();
+
+        [[version].as_ref(), index.as_ref(), self.ratchet.as_ref(), self.signing_key.as_bytes()]
+            .concat()
+    }
+
+    /// Serialize the `ExportedSessionKey` to a byte vector.
+    pub fn to_bytes(&self) -> Vec<u8> {
+        self.to_bytes_with_version(Self::VERSION)
+    }
+
+    /// Deserialize the `ExportedSessionKey` from a byte slice.
+    pub fn from_bytes(bytes: &[u8]) -> Result<Self, SessionKeyDecodeError> {
+        let mut cursor = Cursor::new(bytes);
+        Self::decode_key(Self::VERSION, &mut cursor)
+    }
+
+    /// Serialize the `ExportedSessionKey` to a base64 encoded string.
+    ///
+    /// This method will first use the [`ExportedSessionKey::to_bytes()`] to
+    /// convert the session key to a byte vector and then encode the byte vector
+    /// to a string using unpadded base64 as the encoding.
+    pub fn to_base64(&self) -> String {
+        let mut bytes = self.to_bytes();
+
+        let ret = base64_encode(&bytes);
+
+        bytes.zeroize();
+
+        ret
+    }
+
+    /// Deserialize the `ExportedSessionKey` from base64 encoded string.
+    pub fn from_base64(key: &str) -> Result<Self, SessionKeyDecodeError> {
+        let mut bytes = base64_decode(key)?;
+        let ret = Self::from_bytes(&bytes);
+
+        bytes.zeroize();
+
+        ret
+    }
+
+    fn decode_key(
+        expected_version: u8,
+        cursor: &mut Cursor<&[u8]>,
+    ) -> Result<ExportedSessionKey, SessionKeyDecodeError> {
+        let mut version = [0u8; 1];
+        let mut index = [0u8; 4];
+        let mut ratchet = Box::new([0u8; 128]);
+        let mut public_key = [0u8; Ed25519PublicKey::LENGTH];
+
+        cursor.read_exact(&mut version)?;
+
+        if version[0] != expected_version {
+            Err(SessionKeyDecodeError::Version(expected_version, version[0]))
+        } else {
+            cursor.read_exact(&mut index)?;
+            cursor.read_exact(ratchet.as_mut_slice())?;
+            cursor.read_exact(&mut public_key)?;
+
+            let signing_key = Ed25519PublicKey::from_slice(&public_key)?;
+            let ratchet_index = u32::from_be_bytes(index);
+
+            Ok(ExportedSessionKey { ratchet_index, ratchet, signing_key })
+        }
+    }
+}
+
+impl Zeroize for ExportedSessionKey {
+    fn zeroize(&mut self) {
+        self.ratchet_index.zeroize();
+        self.ratchet.zeroize();
+    }
+}
+
+impl Drop for ExportedSessionKey {
+    fn drop(&mut self) {
+        self.zeroize()
+    }
+}
+
+impl TryFrom<&[u8]> for ExportedSessionKey {
+    type Error = SessionKeyDecodeError;
+
+    fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
+        Self::from_bytes(value)
+    }
+}
+
+impl TryFrom<&str> for ExportedSessionKey {
+    type Error = SessionKeyDecodeError;
+
+    fn try_from(value: &str) -> Result<Self, Self::Error> {
+        Self::from_base64(value)
+    }
+}
+
+impl Serialize for ExportedSessionKey {
+    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+    where
+        S: serde::Serializer,
+    {
+        let mut encoded = self.to_base64();
+        let ret = encoded.serialize(serializer);
+
+        encoded.zeroize();
+
+        ret
+    }
+}
+
+impl<'de> Deserialize<'de> for ExportedSessionKey {
+    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
+    where
+        D: serde::Deserializer<'de>,
+    {
+        let mut session_key = String::deserialize(deserializer)?;
+        let ret = Self::from_base64(&session_key).map_err(serde::de::Error::custom);
+
+        session_key.zeroize();
+
+        ret
+    }
+}
+
+/// The session key, can be used to create a [`InboundGroupSession`].
+///
+/// Uses the session-sharing format described in the [Olm spec].
+///
+/// +---+----+--------+--------+--------+--------+------+-----------+
+/// | V | i  | R(i,0) | R(i,1) | R(i,2) | R(i,3) | Kpub | Signature |
+/// +---+----+--------+--------+--------+--------+------+-----------+
+/// 0   1    5        37       69      101      133    165         229   bytes
+///
+/// The version byte, V, is "\x02".
+/// This is followed by the ratchet index, iii, which is encoded as a
+/// big-endian 32-bit integer; the 128 bytes of the ratchet; and the public
+/// part of the Ed25519 keypair.
+///
+/// The data is then signed using the Ed25519 key, and the 64-byte signature is
+/// appended.
+///
+/// [`InboundGroupSession`]: #crate.megolm.InboundGroupSession
+/// [Olm spec]: https://gitlab.matrix.org/matrix-org/olm/blob/master/docs/megolm.md#session-sharing-format
+pub struct SessionKey {
+    pub(super) session_key: ExportedSessionKey,
+    pub(super) signature: Ed25519Signature,
+}
+
+impl SessionKey {
+    const VERSION: u8 = 2;
+
+    pub(super) fn new(ratchet: &Ratchet, signing_key: Ed25519PublicKey) -> Self {
+        let session_key = ExportedSessionKey::new(ratchet, signing_key);
+
+        Self {
+            session_key,
+            signature: Ed25519Signature::from_slice(&[0; Ed25519Signature::LENGTH])
+                .expect("Can't create an empty signature"),
+        }
+    }
+
+    pub(crate) fn to_signature_bytes(&self) -> Vec<u8> {
+        self.session_key.to_bytes_with_version(Self::VERSION)
+    }
+
+    /// Serialize the `SessionKey` to a byte vector.
+    pub fn to_bytes(&self) -> Vec<u8> {
+        let mut bytes = self.to_signature_bytes();
+        bytes.extend(self.signature.to_bytes());
+
+        bytes
+    }
+
+    /// Deserialize the `SessionKey` from a byte slice.
+    pub fn from_bytes(bytes: &[u8]) -> Result<Self, SessionKeyDecodeError> {
+        let mut cursor = Cursor::new(bytes);
+        let session_key = ExportedSessionKey::decode_key(Self::VERSION, &mut cursor)?;
+
+        let mut signature = [0u8; Ed25519Signature::LENGTH];
+
+        cursor.read_exact(&mut signature)?;
+        let signature = Ed25519Signature::from_slice(&signature)?;
+
+        let decoded = cursor.into_inner();
+
+        session_key
+            .signing_key
+            .verify(&decoded[..decoded.len() - Ed25519Signature::LENGTH], &signature)?;
+
+        Ok(Self { session_key, signature })
+    }
+
+    /// Serialize the `SessionKey` to a base64 encoded string.
+    ///
+    /// This method will first use the [`SessionKey::to_bytes()`] to
+    /// convert the session key to a byte vector and then encode the byte vector
+    /// to a string using unpadded base64 as the encoding.
+    pub fn to_base64(&self) -> String {
+        let mut bytes = self.to_bytes();
+        let ret = base64_encode(&bytes);
+
+        bytes.zeroize();
+
+        ret
+    }
+
+    /// Deserialize the `SessionKey` from base64 encoded string.
+    pub fn from_base64(key: &str) -> Result<Self, SessionKeyDecodeError> {
+        let mut bytes = base64_decode(key)?;
+        let ret = Self::from_bytes(&bytes);
+
+        bytes.zeroize();
+
+        ret
+    }
+}
+
+impl Zeroize for SessionKey {
+    fn zeroize(&mut self) {
+        self.session_key.zeroize();
+    }
+}
+
+impl Drop for SessionKey {
+    fn drop(&mut self) {
+        self.zeroize()
+    }
+}
+
+impl TryFrom<&[u8]> for SessionKey {
+    type Error = SessionKeyDecodeError;
+
+    fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
+        Self::from_bytes(value)
+    }
+}
+
+impl TryFrom<&str> for SessionKey {
+    type Error = SessionKeyDecodeError;
+
+    fn try_from(value: &str) -> Result<Self, Self::Error> {
+        Self::from_base64(value)
+    }
+}
+
+impl Serialize for SessionKey {
+    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+    where
+        S: serde::Serializer,
+    {
+        let mut encoded = self.to_base64();
+        let ret = encoded.serialize(serializer);
+
+        encoded.zeroize();
+
+        ret
+    }
+}
+
+impl<'de> Deserialize<'de> for SessionKey {
+    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
+    where
+        D: serde::Deserializer<'de>,
+    {
+        let mut session_key = String::deserialize(deserializer)?;
+        let ret = Self::from_base64(&session_key).map_err(serde::de::Error::custom);
+
+        session_key.zeroize();
+
+        ret
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use crate::megolm::{ExportedSessionKey, GroupSession, InboundGroupSession, SessionKey};
+
+    #[test]
+    fn session_key_serialization() -> Result<(), anyhow::Error> {
+        let session = GroupSession::new(Default::default());
+
+        let key = session.session_key();
+
+        let serialized = serde_json::to_string(&key)?;
+        let deserialized: SessionKey = serde_json::from_str(&serialized)?;
+
+        assert_eq!(key.session_key.ratchet, deserialized.session_key.ratchet);
+        assert_eq!(key.session_key.ratchet_index, deserialized.session_key.ratchet_index);
+        assert_eq!(key.session_key.signing_key, deserialized.session_key.signing_key);
+        assert_eq!(key.signature, deserialized.signature);
+
+        Ok(())
+    }
+
+    #[test]
+    fn exported_session_key_serialization() -> Result<(), anyhow::Error> {
+        let session = GroupSession::new(Default::default());
+        let mut session = InboundGroupSession::from(&session);
+
+        let key = session.export_at(0).expect(
+            "A freshly created inbound session can always be exported at the initial index",
+        );
+
+        let serialized = serde_json::to_string(&key)?;
+        let deserialized: ExportedSessionKey = serde_json::from_str(&serialized)?;
+
+        assert_eq!(key.ratchet, deserialized.ratchet);
+        assert_eq!(key.ratchet_index, deserialized.ratchet_index);
+        assert_eq!(key.signing_key, deserialized.signing_key);
+
+        Ok(())
+    }
+}
+
\ No newline at end of file diff --git a/src/vodozemac/olm/account/fallback_keys.rs.html b/src/vodozemac/olm/account/fallback_keys.rs.html new file mode 100644 index 00000000..b289b5de --- /dev/null +++ b/src/vodozemac/olm/account/fallback_keys.rs.html @@ -0,0 +1,275 @@ +fallback_keys.rs - source +
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+
// Copyright 2021 Damir Jelić, Denis Kasak
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+use serde::{Deserialize, Serialize};
+
+use crate::{
+    types::{Curve25519SecretKey, KeyId},
+    Curve25519PublicKey,
+};
+
+#[derive(Serialize, Deserialize, Clone)]
+pub(super) struct FallbackKey {
+    pub key_id: KeyId,
+    pub key: Curve25519SecretKey,
+    pub published: bool,
+}
+
+impl FallbackKey {
+    fn new(key_id: KeyId) -> Self {
+        let key = Curve25519SecretKey::new();
+
+        Self { key_id, key, published: false }
+    }
+
+    pub fn public_key(&self) -> Curve25519PublicKey {
+        Curve25519PublicKey::from(&self.key)
+    }
+
+    pub fn secret_key(&self) -> &Curve25519SecretKey {
+        &self.key
+    }
+
+    pub fn key_id(&self) -> KeyId {
+        self.key_id
+    }
+
+    pub fn mark_as_published(&mut self) {
+        self.published = true;
+    }
+
+    pub fn published(&self) -> bool {
+        self.published
+    }
+}
+
+#[derive(Serialize, Deserialize, Clone)]
+pub(super) struct FallbackKeys {
+    pub key_id: u64,
+    pub fallback_key: Option<FallbackKey>,
+    pub previous_fallback_key: Option<FallbackKey>,
+}
+
+impl FallbackKeys {
+    pub fn new() -> Self {
+        Self { key_id: 0, fallback_key: None, previous_fallback_key: None }
+    }
+
+    pub fn mark_as_published(&mut self) {
+        if let Some(f) = self.fallback_key.as_mut() {
+            f.mark_as_published()
+        }
+    }
+
+    pub fn generate_fallback_key(&mut self) -> Option<Curve25519PublicKey> {
+        let key_id = KeyId(self.key_id);
+        self.key_id += 1;
+
+        let ret = self.previous_fallback_key.take().map(|f| f.public_key());
+
+        self.previous_fallback_key = self.fallback_key.take();
+        self.fallback_key = Some(FallbackKey::new(key_id));
+
+        ret
+    }
+
+    pub fn get_secret_key(&self, public_key: &Curve25519PublicKey) -> Option<&Curve25519SecretKey> {
+        self.fallback_key
+            .as_ref()
+            .filter(|f| f.public_key() == *public_key)
+            .or_else(|| {
+                self.previous_fallback_key.as_ref().filter(|f| f.public_key() == *public_key)
+            })
+            .map(|f| f.secret_key())
+    }
+
+    pub fn forget_previous_fallback_key(&mut self) -> Option<FallbackKey> {
+        self.previous_fallback_key.take()
+    }
+
+    pub fn unpublished_fallback_key(&self) -> Option<&FallbackKey> {
+        self.fallback_key.as_ref().filter(|f| !f.published())
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use super::FallbackKeys;
+
+    #[test]
+    fn fallback_key_fetching() {
+        let err = "Missing fallback key";
+        let mut fallback_keys = FallbackKeys::new();
+
+        fallback_keys.generate_fallback_key();
+
+        let public_key = fallback_keys.fallback_key.as_ref().expect(err).public_key();
+        let secret_bytes = fallback_keys.fallback_key.as_ref().expect(err).key.to_bytes();
+
+        let fetched_key = fallback_keys.get_secret_key(&public_key).expect(err);
+
+        assert_eq!(secret_bytes, fetched_key.to_bytes());
+
+        fallback_keys.generate_fallback_key();
+
+        let fetched_key = fallback_keys.get_secret_key(&public_key).expect(err);
+        assert_eq!(secret_bytes, fetched_key.to_bytes());
+
+        let public_key = fallback_keys.fallback_key.as_ref().expect(err).public_key();
+        let secret_bytes = fallback_keys.fallback_key.as_ref().expect(err).key.to_bytes();
+
+        let fetched_key = fallback_keys.get_secret_key(&public_key).expect(err);
+
+        assert_eq!(secret_bytes, fetched_key.to_bytes());
+    }
+}
+
\ No newline at end of file diff --git a/src/vodozemac/olm/account/mod.rs.html b/src/vodozemac/olm/account/mod.rs.html new file mode 100644 index 00000000..68f25514 --- /dev/null +++ b/src/vodozemac/olm/account/mod.rs.html @@ -0,0 +1,2163 @@ +mod.rs - source +
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+195
+196
+197
+198
+199
+200
+201
+202
+203
+204
+205
+206
+207
+208
+209
+210
+211
+212
+213
+214
+215
+216
+217
+218
+219
+220
+221
+222
+223
+224
+225
+226
+227
+228
+229
+230
+231
+232
+233
+234
+235
+236
+237
+238
+239
+240
+241
+242
+243
+244
+245
+246
+247
+248
+249
+250
+251
+252
+253
+254
+255
+256
+257
+258
+259
+260
+261
+262
+263
+264
+265
+266
+267
+268
+269
+270
+271
+272
+273
+274
+275
+276
+277
+278
+279
+280
+281
+282
+283
+284
+285
+286
+287
+288
+289
+290
+291
+292
+293
+294
+295
+296
+297
+298
+299
+300
+301
+302
+303
+304
+305
+306
+307
+308
+309
+310
+311
+312
+313
+314
+315
+316
+317
+318
+319
+320
+321
+322
+323
+324
+325
+326
+327
+328
+329
+330
+331
+332
+333
+334
+335
+336
+337
+338
+339
+340
+341
+342
+343
+344
+345
+346
+347
+348
+349
+350
+351
+352
+353
+354
+355
+356
+357
+358
+359
+360
+361
+362
+363
+364
+365
+366
+367
+368
+369
+370
+371
+372
+373
+374
+375
+376
+377
+378
+379
+380
+381
+382
+383
+384
+385
+386
+387
+388
+389
+390
+391
+392
+393
+394
+395
+396
+397
+398
+399
+400
+401
+402
+403
+404
+405
+406
+407
+408
+409
+410
+411
+412
+413
+414
+415
+416
+417
+418
+419
+420
+421
+422
+423
+424
+425
+426
+427
+428
+429
+430
+431
+432
+433
+434
+435
+436
+437
+438
+439
+440
+441
+442
+443
+444
+445
+446
+447
+448
+449
+450
+451
+452
+453
+454
+455
+456
+457
+458
+459
+460
+461
+462
+463
+464
+465
+466
+467
+468
+469
+470
+471
+472
+473
+474
+475
+476
+477
+478
+479
+480
+481
+482
+483
+484
+485
+486
+487
+488
+489
+490
+491
+492
+493
+494
+495
+496
+497
+498
+499
+500
+501
+502
+503
+504
+505
+506
+507
+508
+509
+510
+511
+512
+513
+514
+515
+516
+517
+518
+519
+520
+521
+522
+523
+524
+525
+526
+527
+528
+529
+530
+531
+532
+533
+534
+535
+536
+537
+538
+539
+540
+541
+542
+543
+544
+545
+546
+547
+548
+549
+550
+551
+552
+553
+554
+555
+556
+557
+558
+559
+560
+561
+562
+563
+564
+565
+566
+567
+568
+569
+570
+571
+572
+573
+574
+575
+576
+577
+578
+579
+580
+581
+582
+583
+584
+585
+586
+587
+588
+589
+590
+591
+592
+593
+594
+595
+596
+597
+598
+599
+600
+601
+602
+603
+604
+605
+606
+607
+608
+609
+610
+611
+612
+613
+614
+615
+616
+617
+618
+619
+620
+621
+622
+623
+624
+625
+626
+627
+628
+629
+630
+631
+632
+633
+634
+635
+636
+637
+638
+639
+640
+641
+642
+643
+644
+645
+646
+647
+648
+649
+650
+651
+652
+653
+654
+655
+656
+657
+658
+659
+660
+661
+662
+663
+664
+665
+666
+667
+668
+669
+670
+671
+672
+673
+674
+675
+676
+677
+678
+679
+680
+681
+682
+683
+684
+685
+686
+687
+688
+689
+690
+691
+692
+693
+694
+695
+696
+697
+698
+699
+700
+701
+702
+703
+704
+705
+706
+707
+708
+709
+710
+711
+712
+713
+714
+715
+716
+717
+718
+719
+720
+721
+722
+723
+724
+725
+726
+727
+728
+729
+730
+731
+732
+733
+734
+735
+736
+737
+738
+739
+740
+741
+742
+743
+744
+745
+746
+747
+748
+749
+750
+751
+752
+753
+754
+755
+756
+757
+758
+759
+760
+761
+762
+763
+764
+765
+766
+767
+768
+769
+770
+771
+772
+773
+774
+775
+776
+777
+778
+779
+780
+781
+782
+783
+784
+785
+786
+787
+788
+789
+790
+791
+792
+793
+794
+795
+796
+797
+798
+799
+800
+801
+802
+803
+804
+805
+806
+807
+808
+809
+810
+811
+812
+813
+814
+815
+816
+817
+818
+819
+820
+821
+822
+823
+824
+825
+826
+827
+828
+829
+830
+831
+832
+833
+834
+835
+836
+837
+838
+839
+840
+841
+842
+843
+844
+845
+846
+847
+848
+849
+850
+851
+852
+853
+854
+855
+856
+857
+858
+859
+860
+861
+862
+863
+864
+865
+866
+867
+868
+869
+870
+871
+872
+873
+874
+875
+876
+877
+878
+879
+880
+881
+882
+883
+884
+885
+886
+887
+888
+889
+890
+891
+892
+893
+894
+895
+896
+897
+898
+899
+900
+901
+902
+903
+904
+905
+906
+907
+908
+909
+910
+911
+912
+913
+914
+915
+916
+917
+918
+919
+920
+921
+922
+923
+924
+925
+926
+927
+928
+929
+930
+931
+932
+933
+934
+935
+936
+937
+938
+939
+940
+941
+942
+943
+944
+945
+946
+947
+948
+949
+950
+951
+952
+953
+954
+955
+956
+957
+958
+959
+960
+961
+962
+963
+964
+965
+966
+967
+968
+969
+970
+971
+972
+973
+974
+975
+976
+977
+978
+979
+980
+981
+982
+983
+984
+985
+986
+987
+988
+989
+990
+991
+992
+993
+994
+995
+996
+997
+998
+999
+1000
+1001
+1002
+1003
+1004
+1005
+1006
+1007
+1008
+1009
+1010
+1011
+1012
+1013
+1014
+1015
+1016
+1017
+1018
+1019
+1020
+1021
+1022
+1023
+1024
+1025
+1026
+1027
+1028
+1029
+1030
+1031
+1032
+1033
+1034
+1035
+1036
+1037
+1038
+1039
+1040
+1041
+1042
+1043
+1044
+1045
+1046
+1047
+1048
+1049
+1050
+1051
+1052
+1053
+1054
+1055
+1056
+1057
+1058
+1059
+1060
+1061
+1062
+1063
+1064
+1065
+1066
+1067
+1068
+1069
+1070
+1071
+1072
+1073
+1074
+1075
+1076
+1077
+1078
+1079
+1080
+
// Copyright 2021 Damir Jelić, Denis Kasak
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+mod fallback_keys;
+mod one_time_keys;
+
+use std::collections::HashMap;
+
+use rand::thread_rng;
+use serde::{Deserialize, Serialize};
+use thiserror::Error;
+use x25519_dalek::ReusableSecret;
+
+pub use self::one_time_keys::OneTimeKeyGenerationResult;
+use self::{
+    fallback_keys::FallbackKeys,
+    one_time_keys::{OneTimeKeys, OneTimeKeysPickle},
+};
+use super::{
+    messages::PreKeyMessage,
+    session::{DecryptionError, Session},
+    session_keys::SessionKeys,
+    shared_secret::{RemoteShared3DHSecret, Shared3DHSecret},
+    SessionConfig,
+};
+use crate::{
+    types::{
+        Curve25519Keypair, Curve25519KeypairPickle, Curve25519PublicKey, Curve25519SecretKey,
+        Ed25519Keypair, Ed25519KeypairPickle, Ed25519PublicKey, KeyId,
+    },
+    utilities::{pickle, unpickle},
+    Ed25519Signature, PickleError,
+};
+
+const PUBLIC_MAX_ONE_TIME_KEYS: usize = 50;
+
+/// Error describing failure modes when creating a Olm Session from an incoming
+/// Olm message.
+#[derive(Error, Debug)]
+pub enum SessionCreationError {
+    /// The pre-key message contained an unknown one-time key. This happens
+    /// either because we never had such a one-time key, or because it has
+    /// already been used up.
+    #[error("The pre-key message contained an unknown one-time key: {0}")]
+    MissingOneTimeKey(Curve25519PublicKey),
+    /// The pre-key message contains a curve25519 identity key that doesn't
+    /// match to the identity key that was given.
+    #[error(
+        "The given identity key doesn't match the one in the pre-key message: \
+        expected {0}, got {1}"
+    )]
+    MismatchedIdentityKey(Curve25519PublicKey, Curve25519PublicKey),
+    /// The pre-key message that was used to establish the Session couldn't be
+    /// decrypted. The message needs to be decryptable, otherwise we will have
+    /// created a Session that wasn't used to encrypt the pre-key message.
+    #[error("The message that was used to establish the Session couldn't be decrypted")]
+    Decryption(#[from] DecryptionError),
+}
+
+/// Struct holding the two public identity keys of an [`Account`].
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
+pub struct IdentityKeys {
+    /// The ed25519 key, used for signing.
+    pub ed25519: Ed25519PublicKey,
+    /// The curve25519 key, used for to establish shared secrets.
+    pub curve25519: Curve25519PublicKey,
+}
+
+/// Return type for the creation of inbound [`Session`] objects.
+#[derive(Debug)]
+pub struct InboundCreationResult {
+    /// The [`Session`] that was created from a pre-key message.
+    pub session: Session,
+    /// The plaintext of the pre-key message.
+    pub plaintext: Vec<u8>,
+}
+
+/// An Olm account manages all cryptographic keys used on a device.
+pub struct Account {
+    /// A permanent Ed25519 key used for signing. Also known as the fingerprint
+    /// key.
+    signing_key: Ed25519Keypair,
+    /// The permanent Curve25519 key used for 3DH. Also known as the sender key
+    /// or the identity key.
+    diffie_hellman_key: Curve25519Keypair,
+    /// The ephemeral (one-time) Curve25519 keys used as part of the 3DH.
+    one_time_keys: OneTimeKeys,
+    /// The ephemeral Curve25519 keys used in lieu of a one-time key as part of
+    /// the 3DH, in case we run out of those. We keep track of both the current
+    /// and the previous fallback key in any given moment.
+    fallback_keys: FallbackKeys,
+}
+
+impl Account {
+    /// Create a new Account with new random identity keys.
+    pub fn new() -> Self {
+        Self {
+            signing_key: Ed25519Keypair::new(),
+            diffie_hellman_key: Curve25519Keypair::new(),
+            one_time_keys: OneTimeKeys::new(),
+            fallback_keys: FallbackKeys::new(),
+        }
+    }
+
+    /// Get the IdentityKeys of this Account
+    pub fn identity_keys(&self) -> IdentityKeys {
+        IdentityKeys { ed25519: self.ed25519_key(), curve25519: self.curve25519_key() }
+    }
+
+    /// Get a reference to the account's public Ed25519 key
+    pub fn ed25519_key(&self) -> Ed25519PublicKey {
+        self.signing_key.public_key()
+    }
+
+    /// Get a reference to the account's public Curve25519 key
+    pub fn curve25519_key(&self) -> Curve25519PublicKey {
+        self.diffie_hellman_key.public_key()
+    }
+
+    /// Sign the given message using our Ed25519 fingerprint key.
+    pub fn sign(&self, message: &str) -> Ed25519Signature {
+        self.signing_key.sign(message.as_bytes())
+    }
+
+    /// Get the maximum number of one-time keys the client should keep on the
+    /// server.
+    ///
+    /// **Note**: this differs from the libolm method of the same name, the
+    /// libolm method returned the maximum amount of one-time keys the `Account`
+    /// could hold and only half of those should be uploaded.
+    pub fn max_number_of_one_time_keys(&self) -> usize {
+        // We tell clients to upload a limited amount of one-time keys, this
+        // amount is smaller than what we can store.
+        //
+        // We do this because a client might receive the count of uploaded keys
+        // from the server before they receive all the pre-key messages that
+        // used some of our one-time keys. This would mean that we would forget
+        // private one-time keys, since we're generating new ones, while we
+        // didn't yet receive the pre-key messages that used those one-time
+        // keys.
+        PUBLIC_MAX_ONE_TIME_KEYS
+    }
+
+    /// Create a `Session` with the given identity key and one-time key.
+    pub fn create_outbound_session(
+        &self,
+        session_config: SessionConfig,
+        identity_key: Curve25519PublicKey,
+        one_time_key: Curve25519PublicKey,
+    ) -> Session {
+        let rng = thread_rng();
+
+        let base_key = ReusableSecret::random_from_rng(rng);
+        let public_base_key = Curve25519PublicKey::from(&base_key);
+
+        let shared_secret = Shared3DHSecret::new(
+            self.diffie_hellman_key.secret_key(),
+            &base_key,
+            &identity_key,
+            &one_time_key,
+        );
+
+        let session_keys = SessionKeys {
+            identity_key: self.curve25519_key(),
+            base_key: public_base_key,
+            one_time_key,
+        };
+
+        Session::new(session_config, shared_secret, session_keys)
+    }
+
+    fn find_one_time_key(&self, public_key: &Curve25519PublicKey) -> Option<&Curve25519SecretKey> {
+        self.one_time_keys
+            .get_secret_key(public_key)
+            .or_else(|| self.fallback_keys.get_secret_key(public_key))
+    }
+
+    /// Remove a one-time key that has previously been published but not yet
+    /// used.
+    ///
+    /// **Note**: This function is only rarely useful and you'll know if you
+    /// need it. Notably, you do *not* need to call it manually when using up
+    /// a key via [`Account::create_inbound_session`] since the key is
+    /// automatically removed in that case.
+    #[cfg(feature = "low-level-api")]
+    pub fn remove_one_time_key(
+        &mut self,
+        public_key: Curve25519PublicKey,
+    ) -> Option<Curve25519SecretKey> {
+        self.remove_one_time_key_helper(public_key)
+    }
+
+    fn remove_one_time_key_helper(
+        &mut self,
+        public_key: Curve25519PublicKey,
+    ) -> Option<Curve25519SecretKey> {
+        self.one_time_keys.remove_secret_key(&public_key)
+    }
+
+    /// Create a [`Session`] from the given pre-key message and identity key
+    pub fn create_inbound_session(
+        &mut self,
+        their_identity_key: Curve25519PublicKey,
+        pre_key_message: &PreKeyMessage,
+    ) -> Result<InboundCreationResult, SessionCreationError> {
+        if their_identity_key != pre_key_message.identity_key() {
+            Err(SessionCreationError::MismatchedIdentityKey(
+                their_identity_key,
+                pre_key_message.identity_key(),
+            ))
+        } else {
+            // Find the matching private part of the OTK that the message claims
+            // was used to create the session that encrypted it.
+            let public_otk = pre_key_message.one_time_key();
+            let private_otk = self
+                .find_one_time_key(&public_otk)
+                .ok_or(SessionCreationError::MissingOneTimeKey(public_otk))?;
+
+            // Construct a 3DH shared secret from the various curve25519 keys.
+            let shared_secret = RemoteShared3DHSecret::new(
+                self.diffie_hellman_key.secret_key(),
+                private_otk,
+                &pre_key_message.identity_key(),
+                &pre_key_message.base_key(),
+            );
+
+            // These will be used to uniquely identify the Session.
+            let session_keys = SessionKeys {
+                identity_key: pre_key_message.identity_key(),
+                base_key: pre_key_message.base_key(),
+                one_time_key: pre_key_message.one_time_key(),
+            };
+
+            let config = if pre_key_message.message.mac_truncated() {
+                SessionConfig::version_1()
+            } else {
+                SessionConfig::version_2()
+            };
+
+            // Create a Session, AKA a double ratchet, this one will have an
+            // inactive sending chain until we decide to encrypt a message.
+            let mut session = Session::new_remote(
+                config,
+                shared_secret,
+                pre_key_message.message.ratchet_key,
+                session_keys,
+            );
+
+            // Decrypt the message to check if the Session is actually valid.
+            let plaintext = session.decrypt_decoded(&pre_key_message.message)?;
+
+            // We only drop the one-time key now, this is why we can't use a
+            // one-time key type that takes `self`. If we didn't do this,
+            // someone could maliciously pretend to use up our one-time key and
+            // make us drop the private part. Unsuspecting users that actually
+            // try to use such an one-time key won't be able to commnuicate with
+            // us. This is strictly worse than the one-time key exhaustion
+            // scenario.
+            self.remove_one_time_key_helper(pre_key_message.one_time_key());
+
+            Ok(InboundCreationResult { session, plaintext })
+        }
+    }
+
+    /// Generates the supplied number of one time keys.
+    /// Returns the public parts of the one-time keys that were created and
+    /// discarded.
+    ///
+    /// Our one-time key store inside the [`Account`] has a limited amount of
+    /// places for one-time keys, If we try to generate new ones while the store
+    /// is completely populated, the oldest one-time keys will get discarded
+    /// to make place for new ones.
+    pub fn generate_one_time_keys(&mut self, count: usize) -> OneTimeKeyGenerationResult {
+        self.one_time_keys.generate(count)
+    }
+
+    pub fn stored_one_time_key_count(&self) -> usize {
+        self.one_time_keys.private_keys.len()
+    }
+
+    /// Get the currently unpublished one-time keys.
+    ///
+    /// The one-time keys should be published to a server and marked as
+    /// published using the `mark_keys_as_published()` method.
+    pub fn one_time_keys(&self) -> HashMap<KeyId, Curve25519PublicKey> {
+        self.one_time_keys
+            .unpublished_public_keys
+            .iter()
+            .map(|(key_id, key)| (*key_id, *key))
+            .collect()
+    }
+
+    /// Generate a single new fallback key.
+    ///
+    /// The fallback key will be used by other users to establish a `Session` if
+    /// all the one-time keys on the server have been used up.
+    ///
+    /// Returns the public Curve25519 key of the *previous* fallback key, that
+    /// is, the one that will get removed from the [`Account`] when this method
+    /// is called. This return value is mostly useful for logging purposes.
+    pub fn generate_fallback_key(&mut self) -> Option<Curve25519PublicKey> {
+        self.fallback_keys.generate_fallback_key()
+    }
+
+    /// Get the currently unpublished fallback key.
+    ///
+    /// The fallback key should be published just like the one-time keys, after
+    /// it has been successfully published it needs to be marked as published
+    /// using the `mark_keys_as_published()` method as well.
+    pub fn fallback_key(&self) -> HashMap<KeyId, Curve25519PublicKey> {
+        let fallback_key = self.fallback_keys.unpublished_fallback_key();
+
+        if let Some(fallback_key) = fallback_key {
+            HashMap::from([(fallback_key.key_id(), fallback_key.public_key())])
+        } else {
+            HashMap::new()
+        }
+    }
+
+    /// The `Account` stores at most two private parts of the fallback key. This
+    /// method lets us forget the previously used fallback key.
+    pub fn forget_fallback_key(&mut self) -> bool {
+        self.fallback_keys.forget_previous_fallback_key().is_some()
+    }
+
+    /// Mark all currently unpublished one-time and fallback keys as published.
+    pub fn mark_keys_as_published(&mut self) {
+        self.one_time_keys.mark_as_published();
+        self.fallback_keys.mark_as_published();
+    }
+
+    /// Convert the account into a struct which implements [`serde::Serialize`]
+    /// and [`serde::Deserialize`].
+    pub fn pickle(&self) -> AccountPickle {
+        AccountPickle {
+            signing_key: self.signing_key.clone().into(),
+            diffie_hellman_key: self.diffie_hellman_key.clone().into(),
+            one_time_keys: self.one_time_keys.clone().into(),
+            fallback_keys: self.fallback_keys.clone(),
+        }
+    }
+
+    /// Restore an [`Account`] from a previously saved [`AccountPickle`].
+    pub fn from_pickle(pickle: AccountPickle) -> Self {
+        pickle.into()
+    }
+
+    /// Create an [`Account`] object by unpickling an account pickle in libolm
+    /// legacy pickle format.
+    ///
+    /// Such pickles are encrypted and need to first be decrypted using
+    /// `pickle_key`.
+    #[cfg(feature = "libolm-compat")]
+    pub fn from_libolm_pickle(
+        pickle: &str,
+        pickle_key: &[u8],
+    ) -> Result<Self, crate::LibolmPickleError> {
+        use self::libolm::Pickle;
+        use crate::utilities::unpickle_libolm;
+
+        const PICKLE_VERSION: u32 = 4;
+        unpickle_libolm::<Pickle, _>(pickle, pickle_key, PICKLE_VERSION)
+    }
+
+    /// Pickle an [`Account`] into a libolm pickle format.
+    ///
+    /// This pickle can be restored using the `[Account::from_libolm_pickle]`
+    /// method, or can be used in the [`libolm`] C library.
+    ///
+    /// The pickle will be encrypted using the pickle key.
+    ///
+    /// *Note*: This method might be lossy, the vodozemac [`Account`] has the
+    /// ability to hold more one-time keys compared to the [`libolm`]
+    /// variant.
+    ///
+    /// ⚠️  ***Security Warning***: The pickle key will get expanded into both an
+    /// AES key and an IV in a deterministic manner. If the same pickle key
+    /// is reused, this will lead to IV reuse. To prevent this, users have
+    /// to ensure that they always use a globally (probabilistically) unique
+    /// pickle key.
+    ///
+    /// [`libolm`]: https://gitlab.matrix.org/matrix-org/olm/
+    ///
+    /// # Examples
+    /// ```
+    /// use vodozemac::olm::Account;
+    /// use olm_rs::{account::OlmAccount, PicklingMode};
+    /// let account = Account::new();
+    ///
+    /// let export = account
+    ///     .to_libolm_pickle(&[0u8; 32])
+    ///     .expect("We should be able to pickle a freshly created Account");
+    ///
+    /// let unpickled = OlmAccount::unpickle(
+    ///     export,
+    ///     PicklingMode::Encrypted { key: [0u8; 32].to_vec() },
+    /// ).expect("We should be able to unpickle our exported Account");
+    /// ```
+    #[cfg(feature = "libolm-compat")]
+    pub fn to_libolm_pickle(&self, pickle_key: &[u8]) -> Result<String, crate::LibolmPickleError> {
+        use self::libolm::Pickle;
+        use crate::utilities::pickle_libolm;
+        pickle_libolm::<Pickle>(self.into(), pickle_key)
+    }
+
+    #[cfg(all(any(fuzzing, test), feature = "libolm-compat"))]
+    pub fn from_decrypted_libolm_pickle(pickle: &[u8]) -> Result<Self, crate::LibolmPickleError> {
+        use std::io::Cursor;
+
+        use matrix_pickle::Decode;
+
+        use self::libolm::Pickle;
+
+        let mut cursor = Cursor::new(&pickle);
+        let pickle = Pickle::decode(&mut cursor)?;
+
+        pickle.try_into()
+    }
+}
+
+impl Default for Account {
+    fn default() -> Self {
+        Self::new()
+    }
+}
+
+/// A format suitable for serialization which implements [`serde::Serialize`]
+/// and [`serde::Deserialize`]. Obtainable by calling [`Account::pickle`].
+#[derive(Serialize, Deserialize)]
+pub struct AccountPickle {
+    signing_key: Ed25519KeypairPickle,
+    diffie_hellman_key: Curve25519KeypairPickle,
+    one_time_keys: OneTimeKeysPickle,
+    fallback_keys: FallbackKeys,
+}
+
+/// A format suitable for serialization which implements [`serde::Serialize`]
+/// and [`serde::Deserialize`]. Obtainable by calling [`Account::pickle`].
+impl AccountPickle {
+    /// Serialize and encrypt the pickle using the given key.
+    ///
+    /// This is the inverse of [`AccountPickle::from_encrypted`].
+    pub fn encrypt(self, pickle_key: &[u8; 32]) -> String {
+        pickle(&self, pickle_key)
+    }
+
+    /// Obtain a pickle from a ciphertext by decrypting and deserializing using
+    /// the given key.
+    ///
+    /// This is the inverse of [`AccountPickle::encrypt`].
+    pub fn from_encrypted(ciphertext: &str, pickle_key: &[u8; 32]) -> Result<Self, PickleError> {
+        unpickle(ciphertext, pickle_key)
+    }
+}
+
+impl From<AccountPickle> for Account {
+    fn from(pickle: AccountPickle) -> Self {
+        Self {
+            signing_key: pickle.signing_key.into(),
+            diffie_hellman_key: pickle.diffie_hellman_key.into(),
+            one_time_keys: pickle.one_time_keys.into(),
+            fallback_keys: pickle.fallback_keys,
+        }
+    }
+}
+
+#[cfg(feature = "libolm-compat")]
+mod libolm {
+    use matrix_pickle::{Decode, DecodeError, Encode, EncodeError};
+    use zeroize::Zeroize;
+
+    use super::{
+        fallback_keys::{FallbackKey, FallbackKeys},
+        one_time_keys::OneTimeKeys,
+        Account,
+    };
+    use crate::{
+        types::{Curve25519Keypair, Curve25519SecretKey},
+        utilities::LibolmEd25519Keypair,
+        Curve25519PublicKey, Ed25519Keypair, KeyId,
+    };
+
+    #[derive(Debug, Zeroize, Encode, Decode)]
+    #[zeroize(drop)]
+    struct OneTimeKey {
+        key_id: u32,
+        published: bool,
+        public_key: [u8; 32],
+        private_key: Box<[u8; 32]>,
+    }
+
+    impl From<&OneTimeKey> for FallbackKey {
+        fn from(key: &OneTimeKey) -> Self {
+            FallbackKey {
+                key_id: KeyId(key.key_id.into()),
+                key: Curve25519SecretKey::from_slice(&key.private_key),
+                published: key.published,
+            }
+        }
+    }
+
+    #[derive(Debug, Zeroize)]
+    #[zeroize(drop)]
+    struct FallbackKeysArray {
+        fallback_key: Option<OneTimeKey>,
+        previous_fallback_key: Option<OneTimeKey>,
+    }
+
+    impl Decode for FallbackKeysArray {
+        fn decode(reader: &mut impl std::io::Read) -> Result<Self, DecodeError> {
+            let count = u8::decode(reader)?;
+
+            let (fallback_key, previous_fallback_key) = if count >= 1 {
+                let fallback_key = OneTimeKey::decode(reader)?;
+
+                let previous_fallback_key =
+                    if count >= 2 { Some(OneTimeKey::decode(reader)?) } else { None };
+
+                (Some(fallback_key), previous_fallback_key)
+            } else {
+                (None, None)
+            };
+
+            Ok(Self { fallback_key, previous_fallback_key })
+        }
+    }
+
+    impl Encode for FallbackKeysArray {
+        fn encode(&self, writer: &mut impl std::io::Write) -> Result<usize, EncodeError> {
+            let ret = match (&self.fallback_key, &self.previous_fallback_key) {
+                (None, None) => 0u8.encode(writer)?,
+                (Some(key), None) | (None, Some(key)) => {
+                    let mut ret = 1u8.encode(writer)?;
+                    ret += key.encode(writer)?;
+
+                    ret
+                }
+                (Some(key), Some(previous_key)) => {
+                    let mut ret = 2u8.encode(writer)?;
+                    ret += key.encode(writer)?;
+                    ret += previous_key.encode(writer)?;
+
+                    ret
+                }
+            };
+
+            Ok(ret)
+        }
+    }
+
+    #[derive(Zeroize, Encode, Decode)]
+    #[zeroize(drop)]
+    pub(super) struct Pickle {
+        version: u32,
+        ed25519_keypair: LibolmEd25519Keypair,
+        public_curve25519_key: [u8; 32],
+        private_curve25519_key: Box<[u8; 32]>,
+        one_time_keys: Vec<OneTimeKey>,
+        fallback_keys: FallbackKeysArray,
+        next_key_id: u32,
+    }
+
+    impl TryFrom<&FallbackKey> for OneTimeKey {
+        type Error = ();
+
+        fn try_from(key: &FallbackKey) -> Result<Self, ()> {
+            Ok(OneTimeKey {
+                key_id: key.key_id.0.try_into().map_err(|_| ())?,
+                published: key.published(),
+                public_key: key.public_key().to_bytes(),
+                private_key: key.secret_key().to_bytes(),
+            })
+        }
+    }
+
+    impl From<&Account> for Pickle {
+        fn from(account: &Account) -> Self {
+            let one_time_keys: Vec<_> = account
+                .one_time_keys
+                .secret_keys()
+                .iter()
+                .filter_map(|(key_id, secret_key)| {
+                    Some(OneTimeKey {
+                        key_id: key_id.0.try_into().ok()?,
+                        published: account.one_time_keys.is_secret_key_published(key_id),
+                        public_key: Curve25519PublicKey::from(secret_key).to_bytes(),
+                        private_key: secret_key.to_bytes(),
+                    })
+                })
+                .collect();
+
+            let fallback_keys = FallbackKeysArray {
+                fallback_key: account
+                    .fallback_keys
+                    .fallback_key
+                    .as_ref()
+                    .and_then(|f| f.try_into().ok()),
+                previous_fallback_key: account
+                    .fallback_keys
+                    .previous_fallback_key
+                    .as_ref()
+                    .and_then(|f| f.try_into().ok()),
+            };
+
+            let next_key_id = account.one_time_keys.next_key_id.try_into().unwrap_or_default();
+
+            Self {
+                version: 4,
+                ed25519_keypair: LibolmEd25519Keypair {
+                    private_key: account.signing_key.expanded_secret_key(),
+                    public_key: account.signing_key.public_key().as_bytes().to_owned(),
+                },
+                public_curve25519_key: account.diffie_hellman_key.public_key().to_bytes(),
+                private_curve25519_key: account.diffie_hellman_key.secret_key().to_bytes(),
+                one_time_keys,
+                fallback_keys,
+                next_key_id,
+            }
+        }
+    }
+
+    impl TryFrom<Pickle> for Account {
+        type Error = crate::LibolmPickleError;
+
+        fn try_from(pickle: Pickle) -> Result<Self, Self::Error> {
+            let mut one_time_keys = OneTimeKeys::new();
+
+            for key in &pickle.one_time_keys {
+                let secret_key = Curve25519SecretKey::from_slice(&key.private_key);
+                let key_id = KeyId(key.key_id.into());
+                one_time_keys.insert_secret_key(key_id, secret_key, key.published);
+            }
+
+            one_time_keys.next_key_id = pickle.next_key_id.into();
+
+            let fallback_keys = FallbackKeys {
+                key_id: pickle
+                    .fallback_keys
+                    .fallback_key
+                    .as_ref()
+                    .map(|k| k.key_id.wrapping_add(1))
+                    .unwrap_or(0) as u64,
+                fallback_key: pickle.fallback_keys.fallback_key.as_ref().map(|k| k.into()),
+                previous_fallback_key: pickle
+                    .fallback_keys
+                    .previous_fallback_key
+                    .as_ref()
+                    .map(|k| k.into()),
+            };
+
+            Ok(Self {
+                signing_key: Ed25519Keypair::from_expanded_key(
+                    &pickle.ed25519_keypair.private_key,
+                )?,
+                diffie_hellman_key: Curve25519Keypair::from_secret_key(
+                    &pickle.private_curve25519_key,
+                ),
+                one_time_keys,
+                fallback_keys,
+            })
+        }
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use anyhow::{bail, Context, Result};
+    use olm_rs::{account::OlmAccount, session::OlmMessage as LibolmOlmMessage};
+
+    use super::{Account, InboundCreationResult, SessionConfig, SessionCreationError};
+    use crate::{
+        cipher::Mac,
+        olm::{
+            messages::{OlmMessage, PreKeyMessage},
+            AccountPickle,
+        },
+        run_corpus, Curve25519PublicKey as PublicKey, Ed25519Signature,
+    };
+
+    const PICKLE_KEY: [u8; 32] = [0u8; 32];
+
+    #[test]
+    fn vodozemac_libolm_communication() -> Result<()> {
+        // vodozemac account
+        let alice = Account::new();
+        // libolm account
+        let bob = OlmAccount::new();
+
+        bob.generate_one_time_keys(1);
+
+        let one_time_key = bob
+            .parsed_one_time_keys()
+            .curve25519()
+            .values()
+            .next()
+            .cloned()
+            .expect("Didn't find a valid one-time key");
+
+        bob.mark_keys_as_published();
+
+        let identity_keys = bob.parsed_identity_keys();
+        let curve25519_key = PublicKey::from_base64(identity_keys.curve25519())?;
+        let one_time_key = PublicKey::from_base64(&one_time_key)?;
+        let mut alice_session =
+            alice.create_outbound_session(SessionConfig::version_1(), curve25519_key, one_time_key);
+
+        let message = "It's a secret to everybody";
+        let olm_message: LibolmOlmMessage = alice_session.encrypt(message).into();
+
+        if let LibolmOlmMessage::PreKey(m) = olm_message.clone() {
+            let libolm_session =
+                bob.create_inbound_session_from(&alice.curve25519_key().to_base64(), m)?;
+            assert_eq!(alice_session.session_id(), libolm_session.session_id());
+
+            let plaintext = libolm_session.decrypt(olm_message)?;
+            assert_eq!(message, plaintext);
+
+            let second_text = "Here's another secret to everybody";
+            let olm_message = alice_session.encrypt(second_text).into();
+
+            let plaintext = libolm_session.decrypt(olm_message)?;
+            assert_eq!(second_text, plaintext);
+
+            let reply_plain = "Yes, take this, it's dangerous out there";
+            let reply = libolm_session.encrypt(reply_plain).into();
+            let plaintext = alice_session.decrypt(&reply)?;
+
+            assert_eq!(plaintext, reply_plain.as_bytes());
+
+            let another_reply = "Last one";
+            let reply = libolm_session.encrypt(another_reply).into();
+            let plaintext = alice_session.decrypt(&reply)?;
+            assert_eq!(plaintext, another_reply.as_bytes());
+
+            let last_text = "Nope, I'll have the last word";
+            let olm_message = alice_session.encrypt(last_text).into();
+
+            let plaintext = libolm_session.decrypt(olm_message)?;
+            assert_eq!(last_text, plaintext);
+        } else {
+            bail!("Received a invalid message type {:?}", olm_message);
+        }
+
+        Ok(())
+    }
+
+    #[test]
+    fn vodozemac_vodozemac_communication() -> Result<()> {
+        // Both of these are vodozemac accounts.
+        let alice = Account::new();
+        let mut bob = Account::new();
+
+        bob.generate_one_time_keys(1);
+
+        let mut alice_session = alice.create_outbound_session(
+            SessionConfig::version_2(),
+            bob.curve25519_key(),
+            *bob.one_time_keys()
+                .iter()
+                .next()
+                .context("Failed getting bob's OTK, which should never happen here.")?
+                .1,
+        );
+
+        bob.mark_keys_as_published();
+
+        let message = "It's a secret to everybody";
+        let olm_message = alice_session.encrypt(message);
+
+        if let OlmMessage::PreKey(m) = olm_message {
+            assert_eq!(m.session_keys(), alice_session.session_keys());
+
+            let InboundCreationResult { session: mut bob_session, plaintext } =
+                bob.create_inbound_session(alice.curve25519_key(), &m)?;
+            assert_eq!(alice_session.session_id(), bob_session.session_id());
+            assert_eq!(m.session_keys(), bob_session.session_keys());
+
+            assert_eq!(message.as_bytes(), plaintext);
+
+            let second_text = "Here's another secret to everybody";
+            let olm_message = alice_session.encrypt(second_text);
+
+            let plaintext = bob_session.decrypt(&olm_message)?;
+            assert_eq!(second_text.as_bytes(), plaintext);
+
+            let reply_plain = "Yes, take this, it's dangerous out there";
+            let reply = bob_session.encrypt(reply_plain);
+            let plaintext = alice_session.decrypt(&reply)?;
+
+            assert_eq!(plaintext, reply_plain.as_bytes());
+
+            let another_reply = "Last one";
+            let reply = bob_session.encrypt(another_reply);
+            let plaintext = alice_session.decrypt(&reply)?;
+            assert_eq!(plaintext, another_reply.as_bytes());
+
+            let last_text = "Nope, I'll have the last word";
+            let olm_message = alice_session.encrypt(last_text);
+
+            let plaintext = bob_session.decrypt(&olm_message)?;
+            assert_eq!(last_text.as_bytes(), plaintext);
+        }
+
+        Ok(())
+    }
+
+    #[test]
+    fn inbound_session_creation() -> Result<()> {
+        let alice = OlmAccount::new();
+        let mut bob = Account::new();
+
+        bob.generate_one_time_keys(1);
+
+        let one_time_key =
+            bob.one_time_keys().values().next().cloned().expect("Didn't find a valid one-time key");
+
+        let alice_session = alice.create_outbound_session(
+            &bob.curve25519_key().to_base64(),
+            &one_time_key.to_base64(),
+        )?;
+
+        let text = "It's a secret to everybody";
+        let message = alice_session.encrypt(text).into();
+
+        let identity_key = PublicKey::from_base64(alice.parsed_identity_keys().curve25519())?;
+
+        let InboundCreationResult { session, plaintext } = if let OlmMessage::PreKey(m) = &message {
+            bob.create_inbound_session(identity_key, m)?
+        } else {
+            bail!("Got invalid message type from olm_rs {:?}", message);
+        };
+
+        assert_eq!(alice_session.session_id(), session.session_id());
+        assert!(bob.one_time_keys.private_keys.is_empty());
+
+        assert_eq!(text.as_bytes(), plaintext);
+
+        Ok(())
+    }
+
+    #[test]
+    fn inbound_session_creation_using_fallback_keys() -> Result<()> {
+        let alice = OlmAccount::new();
+        let mut bob = Account::new();
+
+        bob.generate_fallback_key();
+
+        let one_time_key =
+            bob.fallback_key().values().next().cloned().expect("Didn't find a valid fallback key");
+        assert!(bob.one_time_keys.private_keys.is_empty());
+
+        let alice_session = alice.create_outbound_session(
+            &bob.curve25519_key().to_base64(),
+            &one_time_key.to_base64(),
+        )?;
+
+        let text = "It's a secret to everybody";
+
+        let message = alice_session.encrypt(text).into();
+        let identity_key = PublicKey::from_base64(alice.parsed_identity_keys().curve25519())?;
+
+        if let OlmMessage::PreKey(m) = &message {
+            let InboundCreationResult { session, plaintext } =
+                bob.create_inbound_session(identity_key, m)?;
+
+            assert_eq!(m.session_keys(), session.session_keys());
+            assert_eq!(alice_session.session_id(), session.session_id());
+            assert!(bob.fallback_keys.fallback_key.is_some());
+
+            assert_eq!(text.as_bytes(), plaintext);
+        } else {
+            bail!("Got invalid message type from olm_rs");
+        };
+
+        Ok(())
+    }
+
+    #[test]
+    fn account_pickling_roundtrip_is_identity() -> Result<()> {
+        let mut account = Account::new();
+
+        account.generate_one_time_keys(50);
+
+        // Generate two fallback keys so the previous fallback key field gets populated.
+        account.generate_fallback_key();
+        account.generate_fallback_key();
+
+        let pickle = account.pickle().encrypt(&PICKLE_KEY);
+
+        let decrypted_pickle = AccountPickle::from_encrypted(&pickle, &PICKLE_KEY)?;
+        let unpickled_account = Account::from_pickle(decrypted_pickle);
+        let repickle = unpickled_account.pickle();
+
+        assert_eq!(account.identity_keys(), unpickled_account.identity_keys());
+
+        let decrypted_pickle = AccountPickle::from_encrypted(&pickle, &PICKLE_KEY)?;
+        let pickle = serde_json::to_value(decrypted_pickle)?;
+        let repickle = serde_json::to_value(repickle)?;
+
+        assert_eq!(pickle, repickle);
+
+        Ok(())
+    }
+
+    #[test]
+    #[cfg(feature = "libolm-compat")]
+    fn libolm_unpickling() -> Result<()> {
+        let olm = OlmAccount::new();
+        olm.generate_one_time_keys(10);
+        olm.generate_fallback_key();
+
+        let key = b"DEFAULT_PICKLE_KEY";
+        let pickle = olm.pickle(olm_rs::PicklingMode::Encrypted { key: key.to_vec() });
+
+        let unpickled = Account::from_libolm_pickle(&pickle, key)?;
+
+        assert_eq!(olm.parsed_identity_keys().ed25519(), unpickled.ed25519_key().to_base64());
+        assert_eq!(olm.parsed_identity_keys().curve25519(), unpickled.curve25519_key().to_base64());
+
+        let mut olm_one_time_keys: Vec<_> =
+            olm.parsed_one_time_keys().curve25519().values().map(|k| k.to_owned()).collect();
+        let mut one_time_keys: Vec<_> =
+            unpickled.one_time_keys().values().map(|k| k.to_base64()).collect();
+
+        // We generated 10 one-time keys on the libolm side, we expect the next key id
+        // to be 11.
+        assert_eq!(unpickled.one_time_keys.next_key_id, 11);
+
+        olm_one_time_keys.sort();
+        one_time_keys.sort();
+        assert_eq!(olm_one_time_keys, one_time_keys);
+
+        let olm_fallback_key =
+            olm.parsed_fallback_key().expect("libolm should have a fallback key");
+        assert_eq!(
+            olm_fallback_key.curve25519(),
+            unpickled
+                .fallback_key()
+                .values()
+                .next()
+                .expect("We should have a fallback key")
+                .to_base64()
+        );
+
+        Ok(())
+    }
+
+    #[test]
+    #[cfg(feature = "libolm-compat")]
+    fn signing_with_expanded_key() -> Result<()> {
+        let olm = OlmAccount::new();
+        olm.generate_one_time_keys(10);
+        olm.generate_fallback_key();
+
+        let key = b"DEFAULT_PICKLE_KEY";
+        let pickle = olm.pickle(olm_rs::PicklingMode::Encrypted { key: key.to_vec() });
+
+        let account_with_expanded_key = Account::from_libolm_pickle(&pickle, key)?;
+
+        // The clone is needed since we're later on using the account.
+        #[allow(clippy::redundant_clone)]
+        let signing_key_clone = account_with_expanded_key.signing_key.clone();
+        signing_key_clone.sign("You met with a terrible fate, haven’t you?".as_bytes());
+        account_with_expanded_key.sign("You met with a terrible fate, haven’t you?");
+
+        Ok(())
+    }
+
+    #[test]
+    fn invalid_session_creation_does_not_remove_otk() -> Result<()> {
+        let mut alice = Account::new();
+        let malory = Account::new();
+        alice.generate_one_time_keys(1);
+
+        let mut session = malory.create_outbound_session(
+            SessionConfig::default(),
+            alice.curve25519_key(),
+            *alice.one_time_keys().values().next().expect("Should have one-time key"),
+        );
+
+        let message = session.encrypt("Test");
+
+        if let OlmMessage::PreKey(m) = message {
+            let mut message = m.to_bytes();
+            let message_len = message.len();
+
+            // We mangle the MAC so decryption fails but creating a Session
+            // succeeds.
+            message[message_len - Mac::TRUNCATED_LEN..message_len]
+                .copy_from_slice(&[0u8; Mac::TRUNCATED_LEN]);
+
+            let message = PreKeyMessage::try_from(message)?;
+
+            match alice.create_inbound_session(malory.curve25519_key(), &message) {
+                Err(SessionCreationError::Decryption(_)) => {}
+                e => bail!("Expected a decryption error, got {:?}", e),
+            }
+            assert!(
+                !alice.one_time_keys.private_keys.is_empty(),
+                "The one-time key was removed when it shouldn't"
+            );
+
+            Ok(())
+        } else {
+            bail!("Invalid message type");
+        }
+    }
+
+    #[test]
+    #[cfg(feature = "libolm-compat")]
+    fn fuzz_corpus_unpickling() {
+        run_corpus("olm-account-unpickling", |data| {
+            let _ = Account::from_decrypted_libolm_pickle(data);
+        });
+    }
+
+    #[test]
+    fn libolm_pickle_cycle() -> Result<()> {
+        let message = "It's a secret to everybody";
+
+        let olm = OlmAccount::new();
+        olm.generate_one_time_keys(10);
+        olm.generate_fallback_key();
+
+        let olm_signature = olm.sign(message);
+
+        let key = b"DEFAULT_PICKLE_KEY";
+        let pickle = olm.pickle(olm_rs::PicklingMode::Encrypted { key: key.to_vec() });
+
+        let account = Account::from_libolm_pickle(&pickle, key).unwrap();
+        let vodozemac_pickle = account.to_libolm_pickle(key).unwrap();
+        let _ = Account::from_libolm_pickle(&vodozemac_pickle, key).unwrap();
+
+        let vodozemac_signature = account.sign(message);
+        let olm_signature = Ed25519Signature::from_base64(&olm_signature)
+            .expect("We should be able to parse a signature produced by libolm");
+        account
+            .identity_keys()
+            .ed25519
+            .verify(message.as_bytes(), &olm_signature)
+            .expect("We should be able to verify the libolm signature with our vodozemac Account");
+
+        let unpickled = OlmAccount::unpickle(
+            vodozemac_pickle,
+            olm_rs::PicklingMode::Encrypted { key: key.to_vec() },
+        )
+        .unwrap();
+
+        let utility = olm_rs::utility::OlmUtility::new();
+        utility
+            .ed25519_verify(
+                unpickled.parsed_identity_keys().ed25519(),
+                message,
+                vodozemac_signature.to_base64(),
+            )
+            .expect("We should be able to verify the signature vodozemac created");
+        utility
+            .ed25519_verify(
+                unpickled.parsed_identity_keys().ed25519(),
+                message,
+                olm_signature.to_base64(),
+            )
+            .expect("We should be able to verify the original signature from libolm");
+
+        assert_eq!(olm.parsed_identity_keys(), unpickled.parsed_identity_keys());
+
+        Ok(())
+    }
+}
+
\ No newline at end of file diff --git a/src/vodozemac/olm/account/one_time_keys.rs.html b/src/vodozemac/olm/account/one_time_keys.rs.html new file mode 100644 index 00000000..8816cd94 --- /dev/null +++ b/src/vodozemac/olm/account/one_time_keys.rs.html @@ -0,0 +1,439 @@ +one_time_keys.rs - source +
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+195
+196
+197
+198
+199
+200
+201
+202
+203
+204
+205
+206
+207
+208
+209
+210
+211
+212
+213
+214
+215
+216
+217
+218
+
// Copyright 2021 Damir Jelić, Denis Kasak
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+use std::collections::{BTreeMap, HashMap};
+
+use serde::{Deserialize, Serialize};
+
+use super::PUBLIC_MAX_ONE_TIME_KEYS;
+use crate::{
+    types::{Curve25519SecretKey, KeyId},
+    Curve25519PublicKey,
+};
+
+#[derive(Serialize, Deserialize, Clone)]
+#[serde(from = "OneTimeKeysPickle")]
+#[serde(into = "OneTimeKeysPickle")]
+pub(super) struct OneTimeKeys {
+    pub next_key_id: u64,
+    pub unpublished_public_keys: BTreeMap<KeyId, Curve25519PublicKey>,
+    pub private_keys: BTreeMap<KeyId, Curve25519SecretKey>,
+    pub key_ids_by_key: HashMap<Curve25519PublicKey, KeyId>,
+}
+
+/// The result type for the one-time key generation operation.
+pub struct OneTimeKeyGenerationResult {
+    /// The public part of the one-time keys that were newly generated.
+    pub created: Vec<Curve25519PublicKey>,
+    /// The public part of the one-time keys that had to be removed to make
+    /// space for the new ones.
+    pub removed: Vec<Curve25519PublicKey>,
+}
+
+impl OneTimeKeys {
+    const MAX_ONE_TIME_KEYS: usize = 100 * PUBLIC_MAX_ONE_TIME_KEYS;
+
+    pub fn new() -> Self {
+        Self {
+            next_key_id: 0,
+            unpublished_public_keys: Default::default(),
+            private_keys: Default::default(),
+            key_ids_by_key: Default::default(),
+        }
+    }
+
+    pub fn mark_as_published(&mut self) {
+        self.unpublished_public_keys.clear();
+    }
+
+    pub fn get_secret_key(&self, public_key: &Curve25519PublicKey) -> Option<&Curve25519SecretKey> {
+        self.key_ids_by_key.get(public_key).and_then(|key_id| self.private_keys.get(key_id))
+    }
+
+    pub fn remove_secret_key(
+        &mut self,
+        public_key: &Curve25519PublicKey,
+    ) -> Option<Curve25519SecretKey> {
+        self.key_ids_by_key.remove(public_key).and_then(|key_id| {
+            self.unpublished_public_keys.remove(&key_id);
+            self.private_keys.remove(&key_id)
+        })
+    }
+
+    pub(super) fn insert_secret_key(
+        &mut self,
+        key_id: KeyId,
+        key: Curve25519SecretKey,
+        published: bool,
+    ) -> (Curve25519PublicKey, Option<Curve25519PublicKey>) {
+        // If we hit the max number of one-time keys we'd like to keep, first remove one
+        // before we create a new one.
+        let removed = if self.private_keys.len() >= Self::MAX_ONE_TIME_KEYS {
+            if let Some(key_id) = self.private_keys.keys().next().copied() {
+                let public_key = if let Some(private_key) = self.private_keys.remove(&key_id) {
+                    let public_key = Curve25519PublicKey::from(&private_key);
+                    self.key_ids_by_key.remove(&public_key);
+
+                    Some(public_key)
+                } else {
+                    None
+                };
+
+                self.unpublished_public_keys.remove(&key_id);
+
+                public_key
+            } else {
+                None
+            }
+        } else {
+            None
+        };
+
+        let public_key = Curve25519PublicKey::from(&key);
+
+        self.private_keys.insert(key_id, key);
+        self.key_ids_by_key.insert(public_key, key_id);
+
+        if !published {
+            self.unpublished_public_keys.insert(key_id, public_key);
+        }
+
+        (public_key, removed)
+    }
+
+    fn generate_one_time_key(&mut self) -> (Curve25519PublicKey, Option<Curve25519PublicKey>) {
+        let key_id = KeyId(self.next_key_id);
+        let key = Curve25519SecretKey::new();
+        self.insert_secret_key(key_id, key, false)
+    }
+
+    pub(crate) fn secret_keys(&self) -> &BTreeMap<KeyId, Curve25519SecretKey> {
+        &self.private_keys
+    }
+
+    pub(crate) fn is_secret_key_published(&self, key_id: &KeyId) -> bool {
+        !self.unpublished_public_keys.contains_key(key_id)
+    }
+
+    pub fn generate(&mut self, count: usize) -> OneTimeKeyGenerationResult {
+        let mut removed_keys = Vec::new();
+        let mut created_keys = Vec::new();
+
+        for _ in 0..count {
+            let (created, removed) = self.generate_one_time_key();
+
+            created_keys.push(created);
+            if let Some(removed) = removed {
+                removed_keys.push(removed);
+            }
+
+            self.next_key_id = self.next_key_id.wrapping_add(1);
+        }
+
+        OneTimeKeyGenerationResult { created: created_keys, removed: removed_keys }
+    }
+}
+
+#[derive(Serialize, Deserialize, Clone)]
+pub(super) struct OneTimeKeysPickle {
+    #[serde(alias = "key_id")]
+    next_key_id: u64,
+    public_keys: BTreeMap<KeyId, Curve25519PublicKey>,
+    private_keys: BTreeMap<KeyId, Curve25519SecretKey>,
+}
+
+impl From<OneTimeKeysPickle> for OneTimeKeys {
+    fn from(pickle: OneTimeKeysPickle) -> Self {
+        let mut key_ids_by_key = HashMap::new();
+
+        for (k, v) in pickle.private_keys.iter() {
+            key_ids_by_key.insert(v.into(), *k);
+        }
+
+        Self {
+            next_key_id: pickle.next_key_id,
+            unpublished_public_keys: pickle.public_keys.iter().map(|(&k, &v)| (k, v)).collect(),
+            private_keys: pickle.private_keys,
+            key_ids_by_key,
+        }
+    }
+}
+
+impl From<OneTimeKeys> for OneTimeKeysPickle {
+    fn from(keys: OneTimeKeys) -> Self {
+        OneTimeKeysPickle {
+            next_key_id: keys.next_key_id,
+            public_keys: keys.unpublished_public_keys.iter().map(|(&k, &v)| (k, v)).collect(),
+            private_keys: keys.private_keys,
+        }
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use super::OneTimeKeys;
+    use crate::types::KeyId;
+
+    #[test]
+    fn store_limit() {
+        let mut store = OneTimeKeys::new();
+
+        assert!(store.private_keys.is_empty());
+
+        store.generate(OneTimeKeys::MAX_ONE_TIME_KEYS);
+        assert_eq!(store.private_keys.len(), OneTimeKeys::MAX_ONE_TIME_KEYS);
+        assert_eq!(store.unpublished_public_keys.len(), OneTimeKeys::MAX_ONE_TIME_KEYS);
+        assert_eq!(store.key_ids_by_key.len(), OneTimeKeys::MAX_ONE_TIME_KEYS);
+
+        store.mark_as_published();
+        assert!(store.unpublished_public_keys.is_empty());
+        assert_eq!(store.private_keys.len(), OneTimeKeys::MAX_ONE_TIME_KEYS);
+        assert_eq!(store.key_ids_by_key.len(), OneTimeKeys::MAX_ONE_TIME_KEYS);
+
+        let oldest_key_id =
+            store.private_keys.keys().next().copied().expect("Couldn't get the first key ID");
+        assert_eq!(oldest_key_id, KeyId(0));
+
+        store.generate(10);
+        assert_eq!(store.unpublished_public_keys.len(), 10);
+        assert_eq!(store.private_keys.len(), OneTimeKeys::MAX_ONE_TIME_KEYS);
+        assert_eq!(store.key_ids_by_key.len(), OneTimeKeys::MAX_ONE_TIME_KEYS);
+
+        let oldest_key_id =
+            store.private_keys.keys().next().copied().expect("Couldn't get the first key ID");
+
+        assert_eq!(oldest_key_id, KeyId(10));
+    }
+}
+
\ No newline at end of file diff --git a/src/vodozemac/olm/messages/message.rs.html b/src/vodozemac/olm/messages/message.rs.html new file mode 100644 index 00000000..085ee40a --- /dev/null +++ b/src/vodozemac/olm/messages/message.rs.html @@ -0,0 +1,627 @@ +message.rs - source +
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+195
+196
+197
+198
+199
+200
+201
+202
+203
+204
+205
+206
+207
+208
+209
+210
+211
+212
+213
+214
+215
+216
+217
+218
+219
+220
+221
+222
+223
+224
+225
+226
+227
+228
+229
+230
+231
+232
+233
+234
+235
+236
+237
+238
+239
+240
+241
+242
+243
+244
+245
+246
+247
+248
+249
+250
+251
+252
+253
+254
+255
+256
+257
+258
+259
+260
+261
+262
+263
+264
+265
+266
+267
+268
+269
+270
+271
+272
+273
+274
+275
+276
+277
+278
+279
+280
+281
+282
+283
+284
+285
+286
+287
+288
+289
+290
+291
+292
+293
+294
+295
+296
+297
+298
+299
+300
+301
+302
+303
+304
+305
+306
+307
+308
+309
+310
+311
+312
+
// Copyright 2021 Damir Jelić
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+use std::fmt::Debug;
+
+use prost::Message as ProstMessage;
+use serde::{Deserialize, Serialize};
+
+use crate::{
+    cipher::{Mac, MessageMac},
+    utilities::{base64_decode, base64_encode, extract_mac, VarInt},
+    Curve25519PublicKey, DecodeError,
+};
+
+const MAC_TRUNCATED_VERSION: u8 = 3;
+const VERSION: u8 = 4;
+
+/// An encrypted Olm message.
+///
+/// Contains metadata that is required to find the correct ratchet state of a
+/// [`Session`] necessary to decrypt the message.
+///
+/// [`Session`]: crate::olm::Session
+#[derive(Clone, PartialEq, Eq)]
+pub struct Message {
+    pub(crate) version: u8,
+    pub(crate) ratchet_key: Curve25519PublicKey,
+    pub(crate) chain_index: u64,
+    pub(crate) ciphertext: Vec<u8>,
+    pub(crate) mac: MessageMac,
+}
+
+impl Message {
+    /// The public part of the ratchet key, that was used when the message was
+    /// encrypted.
+    pub fn ratchet_key(&self) -> Curve25519PublicKey {
+        self.ratchet_key
+    }
+
+    /// The index of the chain that was used when the message was encrypted.
+    pub fn chain_index(&self) -> u64 {
+        self.chain_index
+    }
+
+    /// The actual ciphertext of the message.
+    pub fn ciphertext(&self) -> &[u8] {
+        &self.ciphertext
+    }
+
+    /// The version of the Olm message.
+    pub fn version(&self) -> u8 {
+        self.version
+    }
+
+    /// Has the MAC been truncated in this Olm message.
+    pub fn mac_truncated(&self) -> bool {
+        self.version == MAC_TRUNCATED_VERSION
+    }
+
+    /// Try to decode the given byte slice as a Olm [`Message`].
+    ///
+    /// The expected format of the byte array is described in the
+    /// [`Message::to_bytes()`] method.
+    pub fn from_bytes(bytes: &[u8]) -> Result<Self, DecodeError> {
+        Self::try_from(bytes)
+    }
+
+    /// Encode the `Message` as an array of bytes.
+    ///
+    /// Olm `Message`s consist of a one-byte version, followed by a variable
+    /// length payload and a fixed length message authentication code.
+    ///
+    /// ```text
+    /// +--------------+------------------------------------+-----------+
+    /// | Version Byte | Payload Bytes                      | MAC Bytes |
+    /// +--------------+------------------------------------+-----------+
+    /// ```
+    ///
+    /// The payload uses a format based on the Protocol Buffers encoding. It
+    /// consists of the following key-value pairs:
+    ///
+    /// **Name**   |**Tag**|**Type**|               **Meaning**
+    /// :---------:|:-----:|:------:|:-----------------------------------------:
+    /// Ratchet-Key|  0x0A | String |The public part of the ratchet key
+    /// Chain-Index|  0x10 | Integer|The chain index, of the message
+    /// Cipher-Text|  0x22 | String |The cipher-text of the message
+    pub fn to_bytes(&self) -> Vec<u8> {
+        let mut message = self.encode();
+        message.extend(self.mac.as_bytes());
+
+        message
+    }
+
+    /// Try to decode the given string as a Olm [`Message`].
+    ///
+    /// The string needs to be a base64 encoded byte array that follows the
+    /// format described in the [`Message::to_bytes()`] method.
+    pub fn from_base64(message: &str) -> Result<Self, DecodeError> {
+        Self::try_from(message)
+    }
+
+    /// Encode the [`Message`] as a string.
+    ///
+    /// This method first calls [`Message::to_bytes()`] and then encodes the
+    /// resulting byte array as a string using base64 encoding.
+    pub fn to_base64(&self) -> String {
+        base64_encode(self.to_bytes())
+    }
+
+    pub(crate) fn new(
+        ratchet_key: Curve25519PublicKey,
+        chain_index: u64,
+        ciphertext: Vec<u8>,
+    ) -> Self {
+        Self {
+            version: VERSION,
+            ratchet_key,
+            chain_index,
+            ciphertext,
+            mac: Mac([0u8; Mac::LENGTH]).into(),
+        }
+    }
+
+    pub(crate) fn new_truncated_mac(
+        ratchet_key: Curve25519PublicKey,
+        chain_index: u64,
+        ciphertext: Vec<u8>,
+    ) -> Self {
+        Self {
+            version: MAC_TRUNCATED_VERSION,
+            ratchet_key,
+            chain_index,
+            ciphertext,
+            mac: [0u8; Mac::TRUNCATED_LEN].into(),
+        }
+    }
+
+    fn encode(&self) -> Vec<u8> {
+        ProtoBufMessage {
+            ratchet_key: self.ratchet_key.to_bytes().to_vec(),
+            chain_index: self.chain_index,
+            ciphertext: self.ciphertext.clone(),
+        }
+        .encode_manual(self.version)
+    }
+
+    pub(crate) fn to_mac_bytes(&self) -> Vec<u8> {
+        self.encode()
+    }
+
+    pub(crate) fn set_mac(&mut self, mac: Mac) {
+        match self.mac {
+            MessageMac::Truncated(_) => self.mac = mac.truncate().into(),
+            MessageMac::Full(_) => self.mac = mac.into(),
+        }
+    }
+}
+
+impl Serialize for Message {
+    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+    where
+        S: serde::Serializer,
+    {
+        let message = self.to_base64();
+        serializer.serialize_str(&message)
+    }
+}
+
+impl<'de> Deserialize<'de> for Message {
+    fn deserialize<D: serde::Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
+        let ciphertext = String::deserialize(d)?;
+        Message::from_base64(&ciphertext).map_err(serde::de::Error::custom)
+    }
+}
+
+impl TryFrom<&str> for Message {
+    type Error = DecodeError;
+
+    fn try_from(value: &str) -> Result<Self, Self::Error> {
+        let decoded = base64_decode(value)?;
+
+        Self::try_from(decoded)
+    }
+}
+
+impl TryFrom<Vec<u8>> for Message {
+    type Error = DecodeError;
+
+    fn try_from(value: Vec<u8>) -> Result<Self, Self::Error> {
+        Self::try_from(value.as_slice())
+    }
+}
+
+impl TryFrom<&[u8]> for Message {
+    type Error = DecodeError;
+
+    fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
+        let version = *value.first().ok_or(DecodeError::MissingVersion)?;
+
+        let mac_length = match version {
+            VERSION => Mac::LENGTH,
+            MAC_TRUNCATED_VERSION => Mac::TRUNCATED_LEN,
+            _ => return Err(DecodeError::InvalidVersion(VERSION, version)),
+        };
+
+        if value.len() < mac_length + 2 {
+            Err(DecodeError::MessageTooShort(value.len()))
+        } else {
+            let inner = ProtoBufMessage::decode(
+                value
+                    .get(1..value.len() - mac_length)
+                    .ok_or_else(|| DecodeError::MessageTooShort(value.len()))?,
+            )?;
+
+            let mac_slice = &value[value.len() - mac_length..];
+
+            if mac_slice.len() != mac_length {
+                Err(DecodeError::InvalidMacLength(mac_length, mac_slice.len()))
+            } else {
+                let mac = extract_mac(mac_slice, version == MAC_TRUNCATED_VERSION);
+
+                let chain_index = inner.chain_index;
+                let ciphertext = inner.ciphertext;
+                let ratchet_key = Curve25519PublicKey::from_slice(&inner.ratchet_key)?;
+
+                let message = Message { version, ratchet_key, chain_index, ciphertext, mac };
+
+                Ok(message)
+            }
+        }
+    }
+}
+
+impl Debug for Message {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        let Self { version, ratchet_key, chain_index, ciphertext: _, mac: _ } = self;
+
+        f.debug_struct("Message")
+            .field("version", version)
+            .field("ratchet_key", ratchet_key)
+            .field("chain_index", chain_index)
+            .finish_non_exhaustive()
+    }
+}
+
+#[derive(ProstMessage, PartialEq, Eq)]
+struct ProtoBufMessage {
+    #[prost(bytes, tag = "1")]
+    ratchet_key: Vec<u8>,
+    #[prost(uint64, tag = "2")]
+    chain_index: u64,
+    #[prost(bytes, tag = "4")]
+    ciphertext: Vec<u8>,
+}
+
+impl ProtoBufMessage {
+    const RATCHET_TAG: &'static [u8; 1] = b"\x0A";
+    const INDEX_TAG: &'static [u8; 1] = b"\x10";
+    const CIPHER_TAG: &'static [u8; 1] = b"\x22";
+
+    fn encode_manual(&self, version: u8) -> Vec<u8> {
+        let index = self.chain_index.to_var_int();
+        let ratchet_len = self.ratchet_key.len().to_var_int();
+        let ciphertext_len = self.ciphertext.len().to_var_int();
+
+        [
+            [version].as_ref(),
+            Self::RATCHET_TAG.as_ref(),
+            &ratchet_len,
+            &self.ratchet_key,
+            Self::INDEX_TAG.as_ref(),
+            &index,
+            Self::CIPHER_TAG.as_ref(),
+            &ciphertext_len,
+            &self.ciphertext,
+        ]
+        .concat()
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use super::Message;
+    use crate::Curve25519PublicKey;
+
+    #[test]
+    fn encode() {
+        let message = b"\x03\n\x20ratchetkeyhereprettyplease123456\x10\x01\"\nciphertext";
+        let message_mac =
+            b"\x03\n\x20ratchetkeyhereprettyplease123456\x10\x01\"\nciphertextMACHEREE";
+
+        let ratchet_key = Curve25519PublicKey::from(*b"ratchetkeyhereprettyplease123456");
+        let ciphertext = b"ciphertext";
+
+        let mut encoded = Message::new_truncated_mac(ratchet_key, 1, ciphertext.to_vec());
+        encoded.mac = (*b"MACHEREE").into();
+
+        assert_eq!(encoded.to_mac_bytes(), message.as_ref());
+        assert_eq!(encoded.to_bytes(), message_mac.as_ref());
+    }
+}
+
\ No newline at end of file diff --git a/src/vodozemac/olm/messages/mod.rs.html b/src/vodozemac/olm/messages/mod.rs.html new file mode 100644 index 00000000..17f3933a --- /dev/null +++ b/src/vodozemac/olm/messages/mod.rs.html @@ -0,0 +1,547 @@ +mod.rs - source +
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+195
+196
+197
+198
+199
+200
+201
+202
+203
+204
+205
+206
+207
+208
+209
+210
+211
+212
+213
+214
+215
+216
+217
+218
+219
+220
+221
+222
+223
+224
+225
+226
+227
+228
+229
+230
+231
+232
+233
+234
+235
+236
+237
+238
+239
+240
+241
+242
+243
+244
+245
+246
+247
+248
+249
+250
+251
+252
+253
+254
+255
+256
+257
+258
+259
+260
+261
+262
+263
+264
+265
+266
+267
+268
+269
+270
+271
+272
+
// Copyright 2021 Damir Jelić
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+mod message;
+mod pre_key;
+
+pub use message::Message;
+pub use pre_key::PreKeyMessage;
+use serde::{Deserialize, Serialize};
+
+use crate::DecodeError;
+
+/// Enum over the different Olm message types.
+///
+/// Olm uses two types of messages. The underlying transport protocol must
+/// provide a means for recipients to distinguish between them.
+///
+/// [`OlmMessage`] provides [`Serialize`] and [`Deserialize`] implementations
+/// that are compatible with [Matrix].
+///
+/// [Matrix]: https://spec.matrix.org/latest/client-server-api/#molmv1curve25519-aes-sha2
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub enum OlmMessage {
+    /// A normal message, contains only the ciphertext and metadata to decrypt
+    /// it.
+    Normal(Message),
+    /// A pre-key message, contains metadata to establish a [`Session`] as well
+    /// as a [`Message`].
+    ///
+    /// [`Session`]: crate::olm::Session
+    PreKey(PreKeyMessage),
+}
+
+impl From<Message> for OlmMessage {
+    fn from(m: Message) -> Self {
+        Self::Normal(m)
+    }
+}
+
+impl From<PreKeyMessage> for OlmMessage {
+    fn from(m: PreKeyMessage) -> Self {
+        Self::PreKey(m)
+    }
+}
+
+#[derive(Serialize, Deserialize)]
+struct MessageSerdeHelper {
+    #[serde(rename = "type")]
+    message_type: usize,
+    #[serde(rename = "body")]
+    ciphertext: String,
+}
+
+impl Serialize for OlmMessage {
+    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+    where
+        S: serde::Serializer,
+    {
+        let (message_type, ciphertext) = self.clone().to_parts();
+
+        let message = MessageSerdeHelper { message_type, ciphertext };
+
+        message.serialize(serializer)
+    }
+}
+
+impl<'de> Deserialize<'de> for OlmMessage {
+    fn deserialize<D: serde::Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
+        let value = MessageSerdeHelper::deserialize(d)?;
+
+        OlmMessage::from_parts(value.message_type, &value.ciphertext)
+            .map_err(serde::de::Error::custom)
+    }
+}
+
+impl OlmMessage {
+    /// Create a `OlmMessage` from a message type and a ciphertext.
+    pub fn from_parts(message_type: usize, ciphertext: &str) -> Result<Self, DecodeError> {
+        match message_type {
+            0 => Ok(Self::PreKey(PreKeyMessage::try_from(ciphertext)?)),
+            1 => Ok(Self::Normal(Message::try_from(ciphertext)?)),
+            m => Err(DecodeError::MessageType(m)),
+        }
+    }
+
+    /// Get the message as a byte array.
+    pub fn message(&self) -> &[u8] {
+        match self {
+            OlmMessage::Normal(m) => &m.ciphertext,
+            OlmMessage::PreKey(m) => &m.message.ciphertext,
+        }
+    }
+
+    /// Get the type of the message.
+    pub fn message_type(&self) -> MessageType {
+        match self {
+            OlmMessage::Normal(_) => MessageType::Normal,
+            OlmMessage::PreKey(_) => MessageType::PreKey,
+        }
+    }
+
+    /// Convert the `OlmMessage` into a message type, and base64 encoded message
+    /// tuple.
+    pub fn to_parts(self) -> (usize, String) {
+        let message_type = self.message_type();
+
+        match self {
+            OlmMessage::Normal(m) => (message_type.into(), m.to_base64()),
+            OlmMessage::PreKey(m) => (message_type.into(), m.to_base64()),
+        }
+    }
+}
+
+/// An enum over the two supported message types.
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub enum MessageType {
+    /// The pre-key message type.
+    PreKey = 0,
+    /// The normal message type.
+    Normal = 1,
+}
+
+impl TryFrom<usize> for MessageType {
+    type Error = ();
+
+    fn try_from(value: usize) -> Result<Self, Self::Error> {
+        match value {
+            0 => Ok(MessageType::PreKey),
+            1 => Ok(MessageType::Normal),
+            _ => Err(()),
+        }
+    }
+}
+
+impl From<MessageType> for usize {
+    fn from(value: MessageType) -> usize {
+        value as usize
+    }
+}
+
+#[cfg(test)]
+use olm_rs::session::OlmMessage as LibolmMessage;
+
+#[cfg(test)]
+impl From<LibolmMessage> for OlmMessage {
+    fn from(other: LibolmMessage) -> Self {
+        let (message_type, ciphertext) = other.to_tuple();
+
+        Self::from_parts(message_type.into(), &ciphertext).expect("Can't decode a libolm message")
+    }
+}
+
+#[cfg(test)]
+impl From<OlmMessage> for LibolmMessage {
+    fn from(value: OlmMessage) -> LibolmMessage {
+        match value {
+            OlmMessage::Normal(m) => LibolmMessage::from_type_and_ciphertext(1, m.to_base64())
+                .expect("Can't create a valid libolm message"),
+            OlmMessage::PreKey(m) => LibolmMessage::from_type_and_ciphertext(0, m.to_base64())
+                .expect("Can't create a valid libolm pre-key message"),
+        }
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use anyhow::Result;
+    use assert_matches::assert_matches;
+    use serde_json::json;
+
+    use super::*;
+    use crate::run_corpus;
+
+    const PRE_KEY_MESSAGE: &str = "AwoghAEuxPZ+w7M3pgUae4tDNiggUpOsQ/zci457VAti\
+                                   AEYSIO3xOKRDBWKicIfxjSmYCYZ9DD4RMLjvvclbMlE5\
+                                   yIEWGiApLrCr853CKlPpW4Bi7S8ykRcejJ0lq7AfYLXK\
+                                   CjKdHSJPAwoghw3+P+cajhWj9Qzp5g87h+tbpiuh5wEa\
+                                   eUppqmWqug4QASIgRhZ2cgZcIWQbIa23R7U4y1Mo1R/t\
+                                   LCaMU+xjzRV5smGsCrJ6AHwktg";
+
+    const MESSAGE: &str = "AwogI7JhE/UsMZqXKb3xV6kUZWoJc6jTm2+AIgWYmaETIR0QASIQ\
+                           +X2zb7kEX/3JvoLspcNBcLWOFXYpV0nS";
+
+    #[test]
+    fn message_type_from_usize() {
+        assert_eq!(
+            MessageType::try_from(0),
+            Ok(MessageType::PreKey),
+            "0 should denote a pre-key Olm message"
+        );
+        assert_eq!(
+            MessageType::try_from(1),
+            Ok(MessageType::Normal),
+            "1 should denote a normal Olm message"
+        );
+        assert!(
+            MessageType::try_from(2).is_err(),
+            "2 should be recognized as an unknown Olm message type"
+        );
+    }
+
+    #[test]
+    fn from_json() -> Result<()> {
+        let value = json!({
+            "type": 0u8,
+            "body": PRE_KEY_MESSAGE,
+        });
+
+        let message: OlmMessage = serde_json::from_value(value.clone())?;
+        assert_matches!(message, OlmMessage::PreKey(_));
+
+        let serialized = serde_json::to_value(message)?;
+        assert_eq!(value, serialized, "The serialization cycle isn't a noop");
+
+        let value = json!({
+            "type": 1u8,
+            "body": MESSAGE,
+        });
+
+        let message: OlmMessage = serde_json::from_value(value.clone())?;
+        assert_matches!(message, OlmMessage::Normal(_));
+
+        let serialized = serde_json::to_value(message)?;
+        assert_eq!(value, serialized, "The serialization cycle isn't a noop");
+
+        Ok(())
+    }
+
+    #[test]
+    fn from_parts() -> Result<()> {
+        let message = OlmMessage::from_parts(0, PRE_KEY_MESSAGE)?;
+        assert_matches!(message, OlmMessage::PreKey(_));
+        assert_eq!(
+            message.message_type(),
+            MessageType::PreKey,
+            "Expected message to be recognized as a pre-key Olm message."
+        );
+
+        assert_eq!(message.to_parts(), (0, PRE_KEY_MESSAGE.to_string()), "Roundtrip not identity.");
+
+        let message = OlmMessage::from_parts(1, MESSAGE)?;
+        assert_eq!(
+            message.message_type(),
+            MessageType::Normal,
+            "Expected message to be recognized as a normal Olm message."
+        );
+        assert_eq!(message.to_parts(), (1, MESSAGE.to_string()), "Roundtrip not identity.");
+
+        OlmMessage::from_parts(3, PRE_KEY_MESSAGE)
+            .expect_err("Unknown message types can't be parsed");
+
+        Ok(())
+    }
+
+    #[test]
+    fn fuzz_corpus_decoding() {
+        run_corpus("olm-message-decoding", |data| {
+            let _ = PreKeyMessage::from_bytes(data);
+        });
+    }
+}
+
\ No newline at end of file diff --git a/src/vodozemac/olm/messages/pre_key.rs.html b/src/vodozemac/olm/messages/pre_key.rs.html new file mode 100644 index 00000000..8055bc08 --- /dev/null +++ b/src/vodozemac/olm/messages/pre_key.rs.html @@ -0,0 +1,469 @@ +pre_key.rs - source +
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+195
+196
+197
+198
+199
+200
+201
+202
+203
+204
+205
+206
+207
+208
+209
+210
+211
+212
+213
+214
+215
+216
+217
+218
+219
+220
+221
+222
+223
+224
+225
+226
+227
+228
+229
+230
+231
+232
+233
+
// Copyright 2021 Damir Jelić
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+use std::fmt::Debug;
+
+use prost::Message as ProstMessage;
+use serde::{Deserialize, Serialize};
+
+use super::Message;
+use crate::{
+    olm::SessionKeys,
+    utilities::{base64_decode, base64_encode},
+    Curve25519PublicKey, DecodeError,
+};
+
+/// An encrypted Olm pre-key message.
+///
+/// Contains metadata that is required to establish a [`Session`] and a normal
+/// Olm [`Message`].
+///
+/// [`Session`]: crate::olm::Session
+#[derive(Clone, Debug, PartialEq, Eq)]
+pub struct PreKeyMessage {
+    pub(crate) session_keys: SessionKeys,
+    pub(crate) message: Message,
+}
+
+impl PreKeyMessage {
+    const VERSION: u8 = 3;
+
+    /// The single-use key that was uploaded to a public key directory by the
+    /// receiver of the message. Should be used to establish a [`Session`].
+    ///
+    /// [`Session`]: crate::olm::Session
+    pub fn one_time_key(&self) -> Curve25519PublicKey {
+        self.session_keys.one_time_key
+    }
+
+    /// The base key, a single use key that was created just in time by the
+    /// sender of the message. Should be used to establish a [`Session`].
+    ///
+    /// [`Session`]: crate::olm::Session
+    pub fn base_key(&self) -> Curve25519PublicKey {
+        self.session_keys.base_key
+    }
+
+    /// The long term identity key of the sender of the message. Should be used
+    /// to establish a [`Session`]
+    ///
+    /// [`Session`]: crate::olm::Session
+    pub fn identity_key(&self) -> Curve25519PublicKey {
+        self.session_keys.identity_key
+    }
+
+    /// The collection of all keys required for establishing an Olm [`Session`]
+    /// from this pre-key message.
+    ///
+    /// Other methods on this struct (like [`PreKeyMessage::identity_key()`])
+    /// can be used to retrieve individual keys from this collection.
+    ///
+    /// [`Session`]: crate::olm::Session
+    pub fn session_keys(&self) -> SessionKeys {
+        self.session_keys
+    }
+
+    /// Returns the globally unique session ID, in base64-encoded form.
+    ///
+    /// This is a shorthand helper of the [`SessionKeys::session_id()`] method.
+    pub fn session_id(&self) -> String {
+        self.session_keys.session_id()
+    }
+
+    /// The actual message that contains the ciphertext.
+    pub fn message(&self) -> &Message {
+        &self.message
+    }
+
+    /// Try to decode the given byte slice as a Olm [`Message`].
+    ///
+    /// The expected format of the byte array is described in the
+    /// [`PreKeyMessage::to_bytes()`] method.
+    pub fn from_bytes(message: &[u8]) -> Result<Self, DecodeError> {
+        Self::try_from(message)
+    }
+
+    /// Encode the `PreKeyMessage` as an array of bytes.
+    ///
+    /// Olm `PreKeyMessage`s consist of a one-byte version, followed by a
+    /// variable length payload.
+    ///
+    /// ```text
+    /// +--------------+------------------------------------+
+    /// | Version Byte | Payload Bytes                      |
+    /// +--------------+------------------------------------+
+    /// ```
+    ///
+    /// The payload uses a format based on the Protocol Buffers encoding. It
+    /// consists of the following key-value pairs:
+    ///
+    ///   **Name**  |**Tag**|**Type**|              **Meaning**
+    /// :----------:|:-----:|:------:|:----------------------------------------:
+    /// One-Time-Key| 0x0A  | String |The public part of Bob's single-use key
+    ///   Base-Key  | 0x12  | String |The public part of Alice's single-use key
+    /// Identity-Key| 0x1A  | String |The public part of Alice's identity key
+    ///   Message   | 0x22  | String |An embedded Olm message
+    ///
+    /// The last key/value pair in a [`PreKeyMessage`] is a normal Olm
+    /// [`Message`].
+    pub fn to_bytes(&self) -> Vec<u8> {
+        let message = ProtoBufPreKeyMessage {
+            one_time_key: self.session_keys.one_time_key.as_bytes().to_vec(),
+            base_key: self.session_keys.base_key.as_bytes().to_vec(),
+            identity_key: self.session_keys.identity_key.as_bytes().to_vec(),
+            message: self.message.to_bytes(),
+        };
+
+        let mut output: Vec<u8> = vec![0u8; message.encoded_len() + 1];
+        output[0] = Self::VERSION;
+
+        message
+            .encode(&mut output[1..].as_mut())
+            .expect("Couldn't encode our message into a protobuf");
+
+        output
+    }
+
+    /// Try to decode the given string as a Olm [`PreKeyMessage`].
+    ///
+    /// The string needs to be a base64 encoded byte array that follows the
+    /// format described in the [`PreKeyMessage::to_bytes()`] method.
+    pub fn from_base64(message: &str) -> Result<Self, DecodeError> {
+        Self::try_from(message)
+    }
+
+    /// Encode the [`PreKeyMessage`] as a string.
+    ///
+    /// This method first calls [`PreKeyMessage::to_bytes()`] and then encodes
+    /// the resulting byte array as a string using base64 encoding.
+    pub fn to_base64(&self) -> String {
+        base64_encode(self.to_bytes())
+    }
+
+    /// Create a new pre-key message from the session keys and standard message.
+    #[cfg(feature = "low-level-api")]
+    pub fn wrap(session_keys: SessionKeys, message: Message) -> Self {
+        PreKeyMessage::new(session_keys, message)
+    }
+
+    pub(crate) fn new(session_keys: SessionKeys, message: Message) -> Self {
+        Self { session_keys, message }
+    }
+}
+
+impl Serialize for PreKeyMessage {
+    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+    where
+        S: serde::Serializer,
+    {
+        let message = self.to_base64();
+        serializer.serialize_str(&message)
+    }
+}
+
+impl<'de> Deserialize<'de> for PreKeyMessage {
+    fn deserialize<D: serde::Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
+        let ciphertext = String::deserialize(d)?;
+        PreKeyMessage::from_base64(&ciphertext).map_err(serde::de::Error::custom)
+    }
+}
+
+impl TryFrom<&str> for PreKeyMessage {
+    type Error = DecodeError;
+
+    fn try_from(value: &str) -> Result<Self, Self::Error> {
+        let decoded = base64_decode(value)?;
+
+        Self::try_from(decoded)
+    }
+}
+
+impl TryFrom<Vec<u8>> for PreKeyMessage {
+    type Error = DecodeError;
+
+    fn try_from(value: Vec<u8>) -> Result<Self, Self::Error> {
+        Self::try_from(value.as_slice())
+    }
+}
+
+impl TryFrom<&[u8]> for PreKeyMessage {
+    type Error = DecodeError;
+
+    fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
+        let version = *value.first().ok_or(DecodeError::MissingVersion)?;
+
+        if version != Self::VERSION {
+            Err(DecodeError::InvalidVersion(Self::VERSION, version))
+        } else {
+            let decoded = ProtoBufPreKeyMessage::decode(&value[1..value.len()])?;
+            let one_time_key = Curve25519PublicKey::from_slice(&decoded.one_time_key)?;
+            let base_key = Curve25519PublicKey::from_slice(&decoded.base_key)?;
+            let identity_key = Curve25519PublicKey::from_slice(&decoded.identity_key)?;
+
+            let message = decoded.message.try_into()?;
+
+            let session_keys = SessionKeys { one_time_key, identity_key, base_key };
+
+            Ok(Self { session_keys, message })
+        }
+    }
+}
+
+#[derive(Clone, ProstMessage)]
+struct ProtoBufPreKeyMessage {
+    #[prost(bytes, tag = "1")]
+    one_time_key: Vec<u8>,
+    #[prost(bytes, tag = "2")]
+    base_key: Vec<u8>,
+    #[prost(bytes, tag = "3")]
+    identity_key: Vec<u8>,
+    #[prost(bytes, tag = "4")]
+    message: Vec<u8>,
+}
+
\ No newline at end of file diff --git a/src/vodozemac/olm/mod.rs.html b/src/vodozemac/olm/mod.rs.html new file mode 100644 index 00000000..9b5d1956 --- /dev/null +++ b/src/vodozemac/olm/mod.rs.html @@ -0,0 +1,231 @@ +mod.rs - source +
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+
// Copyright 2021 The Matrix.org Foundation C.I.C.
+// Copyright 2021 Damir Jelić, Denis Kasak
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+//! An implementation of the Olm double ratchet.
+//!
+//! ## Overview
+//!
+//! The core component of the crate is the `Account`, representing a single Olm
+//! participant. An Olm `Account` consists of a collection of key pairs, though
+//! often documentation will shorten this to just "keys". These are:
+//!
+//! 1. An Ed25519 *signing key pair* representing the stable cryptographic
+//!    identity of the participant (the participant's "fingerprint").
+//! 2. A Curve25519 *sender key pair* (also sometimes called the *identity key
+//!    pair*, somewhat confusingly).
+//! 3. A number of one-time key pairs.
+//! 4. A current and previous (if any) "fallback" key pair.
+//!
+//! While the key in 1 is used for signing but not encryption, the keys in 2-4
+//! participate in a triple Diffie-Hellman key exchange (3DH) with another Olm
+//! participant, thereby establishing an Olm session on each side of the
+//! communication channel. Ultimately, this session is used for deriving the
+//! concrete encryption keys for a particular message.
+//!
+//! Olm sessions are represented by the `Session` struct. Such a session is
+//! created by calling `Account::create_outbound_session` on one of the
+//! participating accounts, passing it the Curve25519 sender key and one
+//! Curve25519 one-time key of the other side. The protocol is asynchronous, so
+//! the participant can start sending messages to the other side even before the
+//! other side has created a session, producing so-called pre-key messages (see
+//! `PreKeyMessage`).
+//!
+//! Once the other participant receives such a pre-key message, they can create
+//! their own matching session by calling `Account::create_inbound_session` and
+//! passing it the pre-key message they received and the Curve25519 sender key
+//! of the other side. This completes the establishment of the Olm communication
+//! channel.
+//!
+//! ```rust
+//! use anyhow::Result;
+//! use vodozemac::olm::{Account, InboundCreationResult, OlmMessage, SessionConfig};
+//!
+//! fn main() -> Result<()> {
+//!     let alice = Account::new();
+//!     let mut bob = Account::new();
+//!
+//!     bob.generate_one_time_keys(1);
+//!     let bob_otk = *bob.one_time_keys().values().next().unwrap();
+//!
+//!     let mut alice_session = alice
+//!         .create_outbound_session(SessionConfig::version_2(), bob.curve25519_key(), bob_otk);
+//!
+//!     bob.mark_keys_as_published();
+//!
+//!     let message = "Keep it between us, OK?";
+//!     let alice_msg = alice_session.encrypt(message);
+//!
+//!     if let OlmMessage::PreKey(m) = alice_msg.clone() {
+//!         let result = bob.create_inbound_session(alice.curve25519_key(), &m)?;
+//!
+//!         let mut bob_session = result.session;
+//!         let what_bob_received = result.plaintext;
+//!
+//!         assert_eq!(alice_session.session_id(), bob_session.session_id());
+//!
+//!         assert_eq!(message.as_bytes(), what_bob_received);
+//!
+//!         let bob_reply = "Yes. Take this, it's dangerous out there!";
+//!         let bob_encrypted_reply = bob_session.encrypt(bob_reply).into();
+//!
+//!         let what_alice_received = alice_session
+//!             .decrypt(&bob_encrypted_reply)?;
+//!         assert_eq!(what_alice_received, bob_reply.as_bytes());
+//!     }
+//!
+//!     Ok(())
+//! }
+//! ```
+//!
+//! ## Sending messages
+//!
+//! To encrypt a message, just call `Session::encrypt(msg_content)`. This will
+//! either produce an `OlmMessage::PreKey(..)` or `OlmMessage::Normal(..)`
+//! depending on whether the session is fully established. A session is fully
+//! established once you receive (and decrypt) at least one message from the
+//! other side.
+
+mod account;
+mod messages;
+pub(crate) mod session;
+mod session_config;
+mod session_keys;
+mod shared_secret;
+
+pub use account::{
+    Account, AccountPickle, IdentityKeys, InboundCreationResult, OneTimeKeyGenerationResult,
+    SessionCreationError,
+};
+pub use messages::{Message, MessageType, OlmMessage, PreKeyMessage};
+pub use session::{ratchet::RatchetPublicKey, DecryptionError, Session, SessionPickle};
+pub use session_config::SessionConfig;
+pub use session_keys::SessionKeys;
+
\ No newline at end of file diff --git a/src/vodozemac/olm/session/chain_key.rs.html b/src/vodozemac/olm/session/chain_key.rs.html new file mode 100644 index 00000000..a31a1743 --- /dev/null +++ b/src/vodozemac/olm/session/chain_key.rs.html @@ -0,0 +1,249 @@ +chain_key.rs - source +
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+
// Copyright 2021 Damir Jelić
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+use hmac::{Hmac, Mac};
+use serde::{Deserialize, Serialize};
+use sha2::{digest::CtOutput, Sha256};
+use zeroize::Zeroize;
+
+use super::{
+    message_key::{MessageKey, RemoteMessageKey},
+    ratchet::RatchetPublicKey,
+};
+
+const MESSAGE_KEY_SEED: &[u8; 1] = b"\x01";
+const ADVANCEMENT_SEED: &[u8; 1] = b"\x02";
+
+fn expand_chain_key(key: &[u8; 32]) -> Box<[u8; 32]> {
+    let mut mac =
+        Hmac::<Sha256>::new_from_slice(key).expect("Can't create HmacSha256 from the key");
+    mac.update(MESSAGE_KEY_SEED);
+
+    let mut output = mac.finalize().into_bytes();
+
+    let mut key = Box::new([0u8; 32]);
+    key.copy_from_slice(output.as_slice());
+
+    output.zeroize();
+
+    key
+}
+
+fn advance(key: &[u8; 32]) -> CtOutput<Hmac<Sha256>> {
+    let mut mac = Hmac::<Sha256>::new_from_slice(key)
+        .expect("Couldn't create a valid Hmac object to advance the ratchet");
+    mac.update(ADVANCEMENT_SEED);
+
+    mac.finalize()
+}
+
+#[derive(Clone, Zeroize, Serialize, Deserialize)]
+#[zeroize(drop)]
+pub(super) struct ChainKey {
+    key: Box<[u8; 32]>,
+    index: u64,
+}
+
+#[derive(Clone, Zeroize, Serialize, Deserialize)]
+#[zeroize(drop)]
+pub(super) struct RemoteChainKey {
+    key: Box<[u8; 32]>,
+    index: u64,
+}
+
+impl RemoteChainKey {
+    pub fn new(bytes: Box<[u8; 32]>) -> Self {
+        Self { key: bytes, index: 0 }
+    }
+
+    pub fn chain_index(&self) -> u64 {
+        self.index
+    }
+
+    #[cfg(feature = "libolm-compat")]
+    pub fn from_bytes_and_index(bytes: Box<[u8; 32]>, index: u32) -> Self {
+        Self { key: bytes, index: index.into() }
+    }
+
+    pub fn advance(&mut self) {
+        let output = advance(&self.key).into_bytes();
+        self.key.copy_from_slice(output.as_slice());
+        self.index += 1;
+    }
+
+    pub fn create_message_key(&mut self) -> RemoteMessageKey {
+        let key = expand_chain_key(&self.key);
+        let message_key = RemoteMessageKey::new(key, self.index);
+
+        self.advance();
+
+        message_key
+    }
+}
+
+impl ChainKey {
+    pub fn new(bytes: Box<[u8; 32]>) -> Self {
+        Self { key: bytes, index: 0 }
+    }
+
+    #[cfg(feature = "libolm-compat")]
+    pub fn from_bytes_and_index(bytes: Box<[u8; 32]>, index: u32) -> Self {
+        Self { key: bytes, index: index.into() }
+    }
+
+    pub fn advance(&mut self) {
+        let output = advance(&self.key).into_bytes();
+        self.key.copy_from_slice(output.as_slice());
+        self.index += 1;
+    }
+
+    pub fn index(&self) -> u64 {
+        self.index
+    }
+
+    pub fn create_message_key(&mut self, ratchet_key: RatchetPublicKey) -> MessageKey {
+        let key = expand_chain_key(&self.key);
+        let message_key = MessageKey::new(key, ratchet_key, self.index);
+
+        self.advance();
+
+        message_key
+    }
+}
+
\ No newline at end of file diff --git a/src/vodozemac/olm/session/double_ratchet.rs.html b/src/vodozemac/olm/session/double_ratchet.rs.html new file mode 100644 index 00000000..c5afdf46 --- /dev/null +++ b/src/vodozemac/olm/session/double_ratchet.rs.html @@ -0,0 +1,351 @@ +double_ratchet.rs - source +
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+
// Copyright 2021 Damir Jelić
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+use serde::{Deserialize, Serialize};
+
+use super::{
+    chain_key::ChainKey,
+    message_key::MessageKey,
+    ratchet::{Ratchet, RatchetPublicKey, RemoteRatchetKey},
+    receiver_chain::ReceiverChain,
+    root_key::{RemoteRootKey, RootKey},
+};
+use crate::olm::{messages::Message, shared_secret::Shared3DHSecret};
+
+#[derive(Serialize, Deserialize, Clone)]
+#[serde(transparent)]
+pub(super) struct DoubleRatchet {
+    inner: DoubleRatchetState,
+}
+
+impl DoubleRatchet {
+    pub fn chain_index(&self) -> Option<u64> {
+        match &self.inner {
+            DoubleRatchetState::Inactive(_) => None,
+            DoubleRatchetState::Active(r) => Some(r.symmetric_key_ratchet.index()),
+        }
+    }
+
+    pub fn next_message_key(&mut self) -> MessageKey {
+        match &mut self.inner {
+            DoubleRatchetState::Inactive(ratchet) => {
+                let mut ratchet = ratchet.activate();
+
+                let message_key = ratchet.next_message_key();
+                self.inner = DoubleRatchetState::Active(ratchet);
+
+                message_key
+            }
+            DoubleRatchetState::Active(ratchet) => ratchet.next_message_key(),
+        }
+    }
+
+    pub fn encrypt(&mut self, plaintext: &[u8]) -> Message {
+        self.next_message_key().encrypt(plaintext)
+    }
+
+    pub fn encrypt_truncated_mac(&mut self, plaintext: &[u8]) -> Message {
+        self.next_message_key().encrypt_truncated_mac(plaintext)
+    }
+
+    pub fn active(shared_secret: Shared3DHSecret) -> Self {
+        let (root_key, chain_key) = shared_secret.expand();
+
+        let root_key = RootKey::new(root_key);
+        let chain_key = ChainKey::new(chain_key);
+
+        let ratchet = ActiveDoubleRatchet {
+            active_ratchet: Ratchet::new(root_key),
+            symmetric_key_ratchet: chain_key,
+        };
+
+        Self { inner: ratchet.into() }
+    }
+
+    #[cfg(feature = "libolm-compat")]
+    pub fn from_ratchet_and_chain_key(ratchet: Ratchet, chain_key: ChainKey) -> Self {
+        Self {
+            inner: ActiveDoubleRatchet {
+                active_ratchet: ratchet,
+                symmetric_key_ratchet: chain_key,
+            }
+            .into(),
+        }
+    }
+
+    pub fn inactive(root_key: RemoteRootKey, ratchet_key: RemoteRatchetKey) -> Self {
+        let ratchet = InactiveDoubleRatchet { root_key, ratchet_key };
+
+        Self { inner: ratchet.into() }
+    }
+
+    pub fn advance(&mut self, ratchet_key: RemoteRatchetKey) -> (DoubleRatchet, ReceiverChain) {
+        let (ratchet, receiver_chain) = match &self.inner {
+            DoubleRatchetState::Active(r) => r.advance(ratchet_key),
+            DoubleRatchetState::Inactive(r) => {
+                let ratchet = r.activate();
+                // Advancing an inactive ratchet shouldn't be possible since the
+                // other side did not yet receive our new ratchet key.
+                //
+                // This will likely end up in a decryption error but for
+                // consistency sake and avoiding the leakage of our internal
+                // state it's better to error out there.
+                let ret = ratchet.advance(ratchet_key);
+
+                self.inner = ratchet.into();
+
+                ret
+            }
+        };
+
+        (Self { inner: DoubleRatchetState::Inactive(ratchet) }, receiver_chain)
+    }
+}
+
+#[derive(Serialize, Deserialize, Clone)]
+#[serde(rename_all = "snake_case")]
+#[serde(tag = "type")]
+enum DoubleRatchetState {
+    Inactive(InactiveDoubleRatchet),
+    Active(ActiveDoubleRatchet),
+}
+
+impl From<InactiveDoubleRatchet> for DoubleRatchetState {
+    fn from(r: InactiveDoubleRatchet) -> Self {
+        Self::Inactive(r)
+    }
+}
+
+impl From<ActiveDoubleRatchet> for DoubleRatchetState {
+    fn from(r: ActiveDoubleRatchet) -> Self {
+        Self::Active(r)
+    }
+}
+
+#[derive(Serialize, Deserialize, Clone)]
+struct InactiveDoubleRatchet {
+    root_key: RemoteRootKey,
+    ratchet_key: RemoteRatchetKey,
+}
+
+impl InactiveDoubleRatchet {
+    fn activate(&self) -> ActiveDoubleRatchet {
+        let (root_key, chain_key, ratchet_key) = self.root_key.advance(&self.ratchet_key);
+        let active_ratchet = Ratchet::new_with_ratchet_key(root_key, ratchet_key);
+
+        ActiveDoubleRatchet { active_ratchet, symmetric_key_ratchet: chain_key }
+    }
+}
+
+#[derive(Serialize, Deserialize, Clone)]
+struct ActiveDoubleRatchet {
+    active_ratchet: Ratchet,
+    symmetric_key_ratchet: ChainKey,
+}
+
+impl ActiveDoubleRatchet {
+    fn advance(&self, ratchet_key: RemoteRatchetKey) -> (InactiveDoubleRatchet, ReceiverChain) {
+        let (root_key, remote_chain) = self.active_ratchet.advance(ratchet_key);
+
+        let ratchet = InactiveDoubleRatchet { root_key, ratchet_key };
+        let receiver_chain = ReceiverChain::new(ratchet_key, remote_chain);
+
+        (ratchet, receiver_chain)
+    }
+
+    fn ratchet_key(&self) -> RatchetPublicKey {
+        RatchetPublicKey::from(self.active_ratchet.ratchet_key())
+    }
+
+    fn next_message_key(&mut self) -> MessageKey {
+        self.symmetric_key_ratchet.create_message_key(self.ratchet_key())
+    }
+}
+
\ No newline at end of file diff --git a/src/vodozemac/olm/session/message_key.rs.html b/src/vodozemac/olm/session/message_key.rs.html new file mode 100644 index 00000000..259fedb3 --- /dev/null +++ b/src/vodozemac/olm/session/message_key.rs.html @@ -0,0 +1,281 @@ +message_key.rs - source +
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+
// Copyright 2021 Damir Jelić, Denis Kasak
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+use std::fmt::Debug;
+
+use serde::{Deserialize, Serialize};
+use zeroize::Zeroize;
+
+use super::{ratchet::RatchetPublicKey, DecryptionError};
+use crate::{
+    cipher::{Cipher, Mac},
+    olm::messages::Message,
+};
+
+pub struct MessageKey {
+    key: Box<[u8; 32]>,
+    ratchet_key: RatchetPublicKey,
+    index: u64,
+}
+
+impl Drop for MessageKey {
+    fn drop(&mut self) {
+        self.key.zeroize()
+    }
+}
+
+#[derive(Serialize, Deserialize, Clone)]
+pub(super) struct RemoteMessageKey {
+    pub key: Box<[u8; 32]>,
+    pub index: u64,
+}
+
+impl Debug for RemoteMessageKey {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        let Self { key: _, index } = self;
+
+        f.debug_struct("RemoteMessageKey").field("index", index).finish()
+    }
+}
+
+impl Drop for RemoteMessageKey {
+    fn drop(&mut self) {
+        self.key.zeroize()
+    }
+}
+
+impl MessageKey {
+    pub fn new(key: Box<[u8; 32]>, ratchet_key: RatchetPublicKey, index: u64) -> Self {
+        Self { key, ratchet_key, index }
+    }
+
+    pub fn encrypt_truncated_mac(self, plaintext: &[u8]) -> Message {
+        let cipher = Cipher::new(&self.key);
+
+        let ciphertext = cipher.encrypt(plaintext);
+
+        let mut message =
+            Message::new_truncated_mac(*self.ratchet_key.as_ref(), self.index, ciphertext);
+
+        let mac = cipher.mac(&message.to_mac_bytes());
+        message.set_mac(mac);
+
+        message
+    }
+
+    pub fn encrypt(self, plaintext: &[u8]) -> Message {
+        let cipher = Cipher::new(&self.key);
+
+        let ciphertext = cipher.encrypt(plaintext);
+
+        let mut message = Message::new(*self.ratchet_key.as_ref(), self.index, ciphertext);
+
+        let mac = cipher.mac(&message.to_mac_bytes());
+        message.set_mac(mac);
+
+        message
+    }
+
+    /// Get a reference to the message key's key.
+    #[cfg(feature = "low-level-api")]
+    pub fn key(&self) -> &[u8; 32] {
+        self.key.as_ref()
+    }
+
+    /// Get the message key's ratchet key.
+    #[cfg(feature = "low-level-api")]
+    pub fn ratchet_key(&self) -> RatchetPublicKey {
+        self.ratchet_key
+    }
+
+    /// Get the message key's index.
+    #[cfg(feature = "low-level-api")]
+    pub fn index(&self) -> u64 {
+        self.index
+    }
+}
+
+impl RemoteMessageKey {
+    pub fn new(key: Box<[u8; 32]>, index: u64) -> Self {
+        Self { key, index }
+    }
+
+    pub fn chain_index(&self) -> u64 {
+        self.index
+    }
+
+    pub fn decrypt_truncated_mac(&self, message: &Message) -> Result<Vec<u8>, DecryptionError> {
+        let cipher = Cipher::new(&self.key);
+
+        if let crate::cipher::MessageMac::Truncated(m) = &message.mac {
+            cipher.verify_truncated_mac(&message.to_mac_bytes(), m)?;
+            Ok(cipher.decrypt(&message.ciphertext)?)
+        } else {
+            Err(DecryptionError::InvalidMACLength(Mac::TRUNCATED_LEN, Mac::LENGTH))
+        }
+    }
+
+    pub fn decrypt(&self, message: &Message) -> Result<Vec<u8>, DecryptionError> {
+        let cipher = Cipher::new(&self.key);
+
+        if let crate::cipher::MessageMac::Full(m) = &message.mac {
+            cipher.verify_mac(&message.to_mac_bytes(), m)?;
+            Ok(cipher.decrypt(&message.ciphertext)?)
+        } else {
+            Err(DecryptionError::InvalidMACLength(Mac::LENGTH, Mac::TRUNCATED_LEN))
+        }
+    }
+}
+
\ No newline at end of file diff --git a/src/vodozemac/olm/session/mod.rs.html b/src/vodozemac/olm/session/mod.rs.html new file mode 100644 index 00000000..2bb844f3 --- /dev/null +++ b/src/vodozemac/olm/session/mod.rs.html @@ -0,0 +1,1309 @@ +mod.rs - source +
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+195
+196
+197
+198
+199
+200
+201
+202
+203
+204
+205
+206
+207
+208
+209
+210
+211
+212
+213
+214
+215
+216
+217
+218
+219
+220
+221
+222
+223
+224
+225
+226
+227
+228
+229
+230
+231
+232
+233
+234
+235
+236
+237
+238
+239
+240
+241
+242
+243
+244
+245
+246
+247
+248
+249
+250
+251
+252
+253
+254
+255
+256
+257
+258
+259
+260
+261
+262
+263
+264
+265
+266
+267
+268
+269
+270
+271
+272
+273
+274
+275
+276
+277
+278
+279
+280
+281
+282
+283
+284
+285
+286
+287
+288
+289
+290
+291
+292
+293
+294
+295
+296
+297
+298
+299
+300
+301
+302
+303
+304
+305
+306
+307
+308
+309
+310
+311
+312
+313
+314
+315
+316
+317
+318
+319
+320
+321
+322
+323
+324
+325
+326
+327
+328
+329
+330
+331
+332
+333
+334
+335
+336
+337
+338
+339
+340
+341
+342
+343
+344
+345
+346
+347
+348
+349
+350
+351
+352
+353
+354
+355
+356
+357
+358
+359
+360
+361
+362
+363
+364
+365
+366
+367
+368
+369
+370
+371
+372
+373
+374
+375
+376
+377
+378
+379
+380
+381
+382
+383
+384
+385
+386
+387
+388
+389
+390
+391
+392
+393
+394
+395
+396
+397
+398
+399
+400
+401
+402
+403
+404
+405
+406
+407
+408
+409
+410
+411
+412
+413
+414
+415
+416
+417
+418
+419
+420
+421
+422
+423
+424
+425
+426
+427
+428
+429
+430
+431
+432
+433
+434
+435
+436
+437
+438
+439
+440
+441
+442
+443
+444
+445
+446
+447
+448
+449
+450
+451
+452
+453
+454
+455
+456
+457
+458
+459
+460
+461
+462
+463
+464
+465
+466
+467
+468
+469
+470
+471
+472
+473
+474
+475
+476
+477
+478
+479
+480
+481
+482
+483
+484
+485
+486
+487
+488
+489
+490
+491
+492
+493
+494
+495
+496
+497
+498
+499
+500
+501
+502
+503
+504
+505
+506
+507
+508
+509
+510
+511
+512
+513
+514
+515
+516
+517
+518
+519
+520
+521
+522
+523
+524
+525
+526
+527
+528
+529
+530
+531
+532
+533
+534
+535
+536
+537
+538
+539
+540
+541
+542
+543
+544
+545
+546
+547
+548
+549
+550
+551
+552
+553
+554
+555
+556
+557
+558
+559
+560
+561
+562
+563
+564
+565
+566
+567
+568
+569
+570
+571
+572
+573
+574
+575
+576
+577
+578
+579
+580
+581
+582
+583
+584
+585
+586
+587
+588
+589
+590
+591
+592
+593
+594
+595
+596
+597
+598
+599
+600
+601
+602
+603
+604
+605
+606
+607
+608
+609
+610
+611
+612
+613
+614
+615
+616
+617
+618
+619
+620
+621
+622
+623
+624
+625
+626
+627
+628
+629
+630
+631
+632
+633
+634
+635
+636
+637
+638
+639
+640
+641
+642
+643
+644
+645
+646
+647
+648
+649
+650
+651
+652
+653
+
// Copyright 2021 Damir Jelić
+// Copyright 2021 The Matrix.org Foundation C.I.C.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+mod chain_key;
+mod double_ratchet;
+pub mod message_key;
+pub mod ratchet;
+mod receiver_chain;
+mod root_key;
+
+use std::fmt::Debug;
+
+use aes::cipher::block_padding::UnpadError;
+use arrayvec::ArrayVec;
+use chain_key::RemoteChainKey;
+use double_ratchet::DoubleRatchet;
+use hmac::digest::MacError;
+use ratchet::RemoteRatchetKey;
+use receiver_chain::ReceiverChain;
+use root_key::RemoteRootKey;
+use serde::{Deserialize, Serialize};
+use thiserror::Error;
+use zeroize::Zeroize;
+
+use super::{
+    session_config::Version,
+    session_keys::SessionKeys,
+    shared_secret::{RemoteShared3DHSecret, Shared3DHSecret},
+    SessionConfig,
+};
+#[cfg(feature = "low-level-api")]
+use crate::hazmat::olm::MessageKey;
+use crate::{
+    olm::messages::{Message, OlmMessage, PreKeyMessage},
+    utilities::{pickle, unpickle},
+    Curve25519PublicKey, PickleError,
+};
+
+const MAX_RECEIVING_CHAINS: usize = 5;
+
+/// Error type for Olm-based decryption failures.
+#[derive(Error, Debug)]
+pub enum DecryptionError {
+    /// The message authentication code of the message was invalid.
+    #[error("Failed decrypting Olm message, invalid MAC: {0}")]
+    InvalidMAC(#[from] MacError),
+    /// The length of the message authentication code of the message did not
+    /// match our expected length.
+    #[error("Failed decrypting Olm message, invalid MAC length: expected {0}, got {1}")]
+    InvalidMACLength(usize, usize),
+    /// The ciphertext of the message isn't padded correctly.
+    #[error("Failed decrypting Olm message, invalid padding")]
+    InvalidPadding(#[from] UnpadError),
+    /// The session is missing the correct message key to decrypt the message,
+    /// either because it was already used up, or because the Session has been
+    /// ratcheted forwards and the message key has been discarded.
+    #[error("The message key with the given key can't be created, message index: {0}")]
+    MissingMessageKey(u64),
+    /// Too many messages have been skipped to attempt decrypting this message.
+    #[error("The message gap was too big, got {0}, max allowed {1}")]
+    TooBigMessageGap(u64, u64),
+}
+
+#[derive(Serialize, Deserialize, Clone)]
+struct ChainStore {
+    inner: ArrayVec<ReceiverChain, MAX_RECEIVING_CHAINS>,
+}
+
+impl ChainStore {
+    fn new() -> Self {
+        Self { inner: ArrayVec::new() }
+    }
+
+    fn push(&mut self, ratchet: ReceiverChain) {
+        if self.inner.is_full() {
+            self.inner.pop_at(0);
+        }
+
+        self.inner.push(ratchet)
+    }
+
+    fn is_empty(&self) -> bool {
+        self.inner.is_empty()
+    }
+
+    #[cfg(test)]
+    pub fn len(&self) -> usize {
+        self.inner.len()
+    }
+
+    #[cfg(feature = "libolm-compat")]
+    pub fn get(&self, index: usize) -> Option<&ReceiverChain> {
+        self.inner.get(index)
+    }
+
+    fn find_ratchet(&mut self, ratchet_key: &RemoteRatchetKey) -> Option<&mut ReceiverChain> {
+        self.inner.iter_mut().find(|r| r.belongs_to(ratchet_key))
+    }
+}
+
+impl Default for ChainStore {
+    fn default() -> Self {
+        Self::new()
+    }
+}
+
+/// An Olm session represents one end of an encrypted communication channel
+/// between two participants.
+///
+/// A session enables enables the session owner to encrypt messages intended
+/// for, and decrypt messages sent by, the other participant of the channel.
+///
+/// Olm sessions have two important properties:
+///
+/// 1. They are based on a double ratchet algorithm which continuously
+/// introduces new entropy into    the channel as messages are sent and
+/// received. This imbues the channel with *self-healing*    properties,
+/// allowing it to recover from a momentary loss of confidentiality in the event
+/// of    a key compromise.
+/// 2. They are *asynchronous*, allowing the participant to start sending
+/// messages to the other    side even if the other participant is not online at
+/// the moment.
+///
+/// An Olm [`Session`] is acquired from an [`Account`], by calling either
+///
+/// - [`Account::create_outbound_session`], if you are the first participant to
+///   send a message in
+/// this channel, or
+/// - [`Account::create_inbound_session`], if the other participant initiated
+///   the channel by
+/// sending you a message.
+///
+/// [`Account`]: crate::olm::Account
+/// [`Account::create_outbound_session`]: crate::olm::Account::create_outbound_session
+/// [`Account::create_inbound_session`]: crate::olm::Account::create_inbound_session
+pub struct Session {
+    session_keys: SessionKeys,
+    sending_ratchet: DoubleRatchet,
+    receiving_chains: ChainStore,
+    config: SessionConfig,
+}
+
+impl Debug for Session {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        let Self { session_keys: _, sending_ratchet, receiving_chains, config } = self;
+
+        f.debug_struct("Session")
+            .field("session_id", &self.session_id())
+            .field("sending_chain_index", &sending_ratchet.chain_index())
+            .field("receiving_chains", &receiving_chains.inner)
+            .field("config", config)
+            .finish_non_exhaustive()
+    }
+}
+
+impl Session {
+    pub(super) fn new(
+        config: SessionConfig,
+        shared_secret: Shared3DHSecret,
+        session_keys: SessionKeys,
+    ) -> Self {
+        let local_ratchet = DoubleRatchet::active(shared_secret);
+
+        Self {
+            session_keys,
+            sending_ratchet: local_ratchet,
+            receiving_chains: Default::default(),
+            config,
+        }
+    }
+
+    pub(super) fn new_remote(
+        config: SessionConfig,
+        shared_secret: RemoteShared3DHSecret,
+        remote_ratchet_key: Curve25519PublicKey,
+        session_keys: SessionKeys,
+    ) -> Self {
+        let (root_key, remote_chain_key) = shared_secret.expand();
+
+        let remote_ratchet_key = RemoteRatchetKey::from(remote_ratchet_key);
+        let root_key = RemoteRootKey::new(root_key);
+        let remote_chain_key = RemoteChainKey::new(remote_chain_key);
+
+        let local_ratchet = DoubleRatchet::inactive(root_key, remote_ratchet_key);
+        let remote_ratchet = ReceiverChain::new(remote_ratchet_key, remote_chain_key);
+
+        let mut ratchet_store = ChainStore::new();
+        ratchet_store.push(remote_ratchet);
+
+        Self {
+            session_keys,
+            sending_ratchet: local_ratchet,
+            receiving_chains: ratchet_store,
+            config,
+        }
+    }
+
+    /// Returns the globally unique session ID, in base64-encoded form.
+    ///
+    /// This is a shorthand helper of the [`SessionKeys::session_id()`] method.
+    pub fn session_id(&self) -> String {
+        self.session_keys.session_id()
+    }
+
+    /// Have we ever received and decrypted a message from the other side?
+    ///
+    /// Used to decide if outgoing messages should be sent as normal or pre-key
+    /// messages.
+    pub fn has_received_message(&self) -> bool {
+        !self.receiving_chains.is_empty()
+    }
+
+    /// Encrypt the `plaintext` and construct an [`OlmMessage`].
+    ///
+    /// The message will either be a pre-key message or a normal message,
+    /// depending on whether the session is fully established. A session is
+    /// fully established once you receive (and decrypt) at least one
+    /// message from the other side.
+    pub fn encrypt(&mut self, plaintext: impl AsRef<[u8]>) -> OlmMessage {
+        let message = match self.config.version {
+            Version::V1 => self.sending_ratchet.encrypt_truncated_mac(plaintext.as_ref()),
+            Version::V2 => self.sending_ratchet.encrypt(plaintext.as_ref()),
+        };
+
+        if self.has_received_message() {
+            OlmMessage::Normal(message)
+        } else {
+            let message = PreKeyMessage::new(self.session_keys, message);
+
+            OlmMessage::PreKey(message)
+        }
+    }
+
+    /// Get the keys associated with this session.
+    pub fn session_keys(&self) -> SessionKeys {
+        self.session_keys
+    }
+
+    pub fn session_config(&self) -> SessionConfig {
+        self.config
+    }
+
+    /// Get the [`MessageKey`] to encrypt the next message.
+    ///
+    /// **Note**: Each key obtained in this way should be used to encrypt
+    /// a message and the message must then be sent to the recipient.
+    ///
+    /// Failing to do so will increase the number of out-of-order messages on
+    /// the recipient side. Given that a `Session` can only support a limited
+    /// number of out-of-order messages, this will eventually lead to
+    /// undecryptable messages.
+    #[cfg(feature = "low-level-api")]
+    pub fn next_message_key(&mut self) -> MessageKey {
+        self.sending_ratchet.next_message_key()
+    }
+
+    /// Try to decrypt an Olm message, which will either return the plaintext or
+    /// result in a [`DecryptionError`].
+    ///
+    /// [`DecryptionError`]: self::DecryptionError
+    pub fn decrypt(&mut self, message: &OlmMessage) -> Result<Vec<u8>, DecryptionError> {
+        let decrypted = match message {
+            OlmMessage::Normal(m) => self.decrypt_decoded(m)?,
+            OlmMessage::PreKey(m) => self.decrypt_decoded(&m.message)?,
+        };
+
+        Ok(decrypted)
+    }
+
+    pub(super) fn decrypt_decoded(
+        &mut self,
+        message: &Message,
+    ) -> Result<Vec<u8>, DecryptionError> {
+        let ratchet_key = RemoteRatchetKey::from(message.ratchet_key);
+
+        if let Some(ratchet) = self.receiving_chains.find_ratchet(&ratchet_key) {
+            ratchet.decrypt(message, &self.config)
+        } else {
+            let (sending_ratchet, mut remote_ratchet) = self.sending_ratchet.advance(ratchet_key);
+
+            let plaintext = remote_ratchet.decrypt(message, &self.config)?;
+
+            self.sending_ratchet = sending_ratchet;
+            self.receiving_chains.push(remote_ratchet);
+
+            Ok(plaintext)
+        }
+    }
+
+    /// Convert the session into a struct which implements [`serde::Serialize`]
+    /// and [`serde::Deserialize`].
+    pub fn pickle(&self) -> SessionPickle {
+        SessionPickle {
+            session_keys: self.session_keys,
+            sending_ratchet: self.sending_ratchet.clone(),
+            receiving_chains: self.receiving_chains.clone(),
+            config: self.config,
+        }
+    }
+
+    /// Restore a [`Session`] from a previously saved [`SessionPickle`].
+    pub fn from_pickle(pickle: SessionPickle) -> Self {
+        pickle.into()
+    }
+
+    /// Create a [`Session`] object by unpickling a session pickle in libolm
+    /// legacy pickle format.
+    ///
+    /// Such pickles are encrypted and need to first be decrypted using
+    /// `pickle_key`.
+    #[cfg(feature = "libolm-compat")]
+    pub fn from_libolm_pickle(
+        pickle: &str,
+        pickle_key: &[u8],
+    ) -> Result<Self, crate::LibolmPickleError> {
+        use chain_key::ChainKey;
+        use matrix_pickle::Decode;
+        use message_key::RemoteMessageKey;
+        use ratchet::{Ratchet, RatchetKey};
+        use root_key::RootKey;
+
+        use crate::{types::Curve25519SecretKey, utilities::unpickle_libolm};
+
+        #[derive(Debug, Decode, Zeroize)]
+        #[zeroize(drop)]
+        struct SenderChain {
+            public_ratchet_key: [u8; 32],
+            #[secret]
+            secret_ratchet_key: Box<[u8; 32]>,
+            chain_key: Box<[u8; 32]>,
+            chain_key_index: u32,
+        }
+
+        #[derive(Debug, Decode, Zeroize)]
+        #[zeroize(drop)]
+        struct ReceivingChain {
+            public_ratchet_key: [u8; 32],
+            #[secret]
+            chain_key: Box<[u8; 32]>,
+            chain_key_index: u32,
+        }
+
+        impl From<&ReceivingChain> for ReceiverChain {
+            fn from(chain: &ReceivingChain) -> Self {
+                let ratchet_key = RemoteRatchetKey::from(chain.public_ratchet_key);
+                let chain_key = RemoteChainKey::from_bytes_and_index(
+                    chain.chain_key.clone(),
+                    chain.chain_key_index,
+                );
+
+                ReceiverChain::new(ratchet_key, chain_key)
+            }
+        }
+
+        #[derive(Debug, Decode, Zeroize)]
+        #[zeroize(drop)]
+        struct MessageKey {
+            ratchet_key: [u8; 32],
+            #[secret]
+            message_key: Box<[u8; 32]>,
+            index: u32,
+        }
+
+        impl From<&MessageKey> for RemoteMessageKey {
+            fn from(key: &MessageKey) -> Self {
+                RemoteMessageKey { key: key.message_key.clone(), index: key.index.into() }
+            }
+        }
+
+        #[derive(Decode)]
+        struct Pickle {
+            #[allow(dead_code)]
+            version: u32,
+            #[allow(dead_code)]
+            received_message: bool,
+            session_keys: SessionKeys,
+            #[secret]
+            root_key: Box<[u8; 32]>,
+            sender_chains: Vec<SenderChain>,
+            receiver_chains: Vec<ReceivingChain>,
+            message_keys: Vec<MessageKey>,
+        }
+
+        impl Drop for Pickle {
+            fn drop(&mut self) {
+                self.root_key.zeroize();
+                self.sender_chains.zeroize();
+                self.receiver_chains.zeroize();
+                self.message_keys.zeroize();
+            }
+        }
+
+        impl TryFrom<Pickle> for Session {
+            type Error = crate::LibolmPickleError;
+
+            fn try_from(pickle: Pickle) -> Result<Self, Self::Error> {
+                let mut receiving_chains = ChainStore::new();
+
+                for chain in &pickle.receiver_chains {
+                    receiving_chains.push(chain.into())
+                }
+
+                for key in &pickle.message_keys {
+                    let ratchet_key =
+                        RemoteRatchetKey::from(Curve25519PublicKey::from(key.ratchet_key));
+
+                    if let Some(receiving_chain) = receiving_chains.find_ratchet(&ratchet_key) {
+                        receiving_chain.insert_message_key(key.into())
+                    }
+                }
+
+                if let Some(chain) = pickle.sender_chains.first() {
+                    // XXX: Passing in secret array as value.
+                    let ratchet_key = RatchetKey::from(Curve25519SecretKey::from_slice(
+                        chain.secret_ratchet_key.as_ref(),
+                    ));
+                    let chain_key = ChainKey::from_bytes_and_index(
+                        chain.chain_key.clone(),
+                        chain.chain_key_index,
+                    );
+
+                    let root_key = RootKey::new(pickle.root_key.clone());
+
+                    let ratchet = Ratchet::new_with_ratchet_key(root_key, ratchet_key);
+                    let sending_ratchet =
+                        DoubleRatchet::from_ratchet_and_chain_key(ratchet, chain_key);
+
+                    Ok(Self {
+                        session_keys: pickle.session_keys,
+                        sending_ratchet,
+                        receiving_chains,
+                        config: SessionConfig::version_1(),
+                    })
+                } else if let Some(chain) = receiving_chains.get(0) {
+                    let sending_ratchet = DoubleRatchet::inactive(
+                        RemoteRootKey::new(pickle.root_key.clone()),
+                        chain.ratchet_key(),
+                    );
+
+                    Ok(Self {
+                        session_keys: pickle.session_keys,
+                        sending_ratchet,
+                        receiving_chains,
+                        config: SessionConfig::version_1(),
+                    })
+                } else {
+                    Err(crate::LibolmPickleError::InvalidSession)
+                }
+            }
+        }
+
+        const PICKLE_VERSION: u32 = 1;
+        unpickle_libolm::<Pickle, _>(pickle, pickle_key, PICKLE_VERSION)
+    }
+}
+
+/// A format suitable for serialization which implements [`serde::Serialize`]
+/// and [`serde::Deserialize`]. Obtainable by calling [`Session::pickle`].
+#[derive(Deserialize, Serialize)]
+pub struct SessionPickle {
+    session_keys: SessionKeys,
+    sending_ratchet: DoubleRatchet,
+    receiving_chains: ChainStore,
+    #[serde(default = "default_config")]
+    config: SessionConfig,
+}
+
+fn default_config() -> SessionConfig {
+    SessionConfig::version_1()
+}
+
+impl SessionPickle {
+    /// Serialize and encrypt the pickle using the given key.
+    ///
+    /// This is the inverse of [`SessionPickle::from_encrypted`].
+    pub fn encrypt(self, pickle_key: &[u8; 32]) -> String {
+        pickle(&self, pickle_key)
+    }
+
+    /// Obtain a pickle from a ciphertext by decrypting and deserializing using
+    /// the given key.
+    ///
+    /// This is the inverse of [`SessionPickle::encrypt`].
+    pub fn from_encrypted(ciphertext: &str, pickle_key: &[u8; 32]) -> Result<Self, PickleError> {
+        unpickle(ciphertext, pickle_key)
+    }
+}
+
+impl From<SessionPickle> for Session {
+    fn from(pickle: SessionPickle) -> Self {
+        Self {
+            session_keys: pickle.session_keys,
+            sending_ratchet: pickle.sending_ratchet,
+            receiving_chains: pickle.receiving_chains,
+            config: pickle.config,
+        }
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use anyhow::{bail, Result};
+    use olm_rs::{
+        account::OlmAccount,
+        session::{OlmMessage, OlmSession},
+    };
+
+    use super::Session;
+    use crate::{
+        olm::{Account, SessionConfig, SessionPickle},
+        Curve25519PublicKey,
+    };
+
+    const PICKLE_KEY: [u8; 32] = [0u8; 32];
+
+    fn sessions() -> Result<(Account, OlmAccount, Session, OlmSession)> {
+        let alice = Account::new();
+        let bob = OlmAccount::new();
+        bob.generate_one_time_keys(1);
+
+        let one_time_key = bob
+            .parsed_one_time_keys()
+            .curve25519()
+            .values()
+            .next()
+            .cloned()
+            .expect("Couldn't find a one-time key");
+
+        let identity_keys = bob.parsed_identity_keys();
+        let curve25519_key = Curve25519PublicKey::from_base64(identity_keys.curve25519())?;
+        let one_time_key = Curve25519PublicKey::from_base64(&one_time_key)?;
+        let mut alice_session =
+            alice.create_outbound_session(SessionConfig::version_1(), curve25519_key, one_time_key);
+
+        let message = "It's a secret to everybody";
+
+        let olm_message = alice_session.encrypt(message);
+        bob.mark_keys_as_published();
+
+        if let OlmMessage::PreKey(m) = olm_message.into() {
+            let session =
+                bob.create_inbound_session_from(&alice.curve25519_key().to_base64(), m)?;
+
+            Ok((alice, bob, alice_session, session))
+        } else {
+            bail!("Invalid message type");
+        }
+    }
+
+    #[test]
+    fn out_of_order_decryption() -> Result<()> {
+        let (_, _, mut alice_session, bob_session) = sessions()?;
+
+        let message_1 = bob_session.encrypt("Message 1").into();
+        let message_2 = bob_session.encrypt("Message 2").into();
+        let message_3 = bob_session.encrypt("Message 3").into();
+
+        assert_eq!("Message 3".as_bytes(), alice_session.decrypt(&message_3)?);
+        assert_eq!("Message 2".as_bytes(), alice_session.decrypt(&message_2)?);
+        assert_eq!("Message 1".as_bytes(), alice_session.decrypt(&message_1)?);
+
+        Ok(())
+    }
+
+    #[test]
+    fn more_out_of_order_decryption() -> Result<()> {
+        let (_, _, mut alice_session, bob_session) = sessions()?;
+
+        let message_1 = bob_session.encrypt("Message 1").into();
+        let message_2 = bob_session.encrypt("Message 2").into();
+        let message_3 = bob_session.encrypt("Message 3").into();
+
+        assert_eq!("Message 1".as_bytes(), alice_session.decrypt(&message_1)?);
+
+        assert_eq!(alice_session.receiving_chains.len(), 1);
+
+        let message_4 = alice_session.encrypt("Message 4").into();
+        assert_eq!("Message 4", bob_session.decrypt(message_4)?);
+
+        let message_5 = bob_session.encrypt("Message 5").into();
+        assert_eq!("Message 5".as_bytes(), alice_session.decrypt(&message_5)?);
+        assert_eq!("Message 3".as_bytes(), alice_session.decrypt(&message_3)?);
+        assert_eq!("Message 2".as_bytes(), alice_session.decrypt(&message_2)?);
+
+        assert_eq!(alice_session.receiving_chains.len(), 2);
+
+        Ok(())
+    }
+
+    #[test]
+    #[cfg(feature = "libolm-compat")]
+    fn libolm_unpickling() -> Result<()> {
+        let (_, _, mut session, olm) = sessions()?;
+
+        let plaintext = "It's a secret to everybody";
+        let old_message = session.encrypt(plaintext);
+
+        for _ in 0..9 {
+            session.encrypt("Hello");
+        }
+
+        let message = session.encrypt("Hello");
+        olm.decrypt(message.into())?;
+
+        let key = b"DEFAULT_PICKLE_KEY";
+        let pickle = olm.pickle(olm_rs::PicklingMode::Encrypted { key: key.to_vec() });
+
+        let mut unpickled = Session::from_libolm_pickle(&pickle, key)?;
+
+        assert_eq!(olm.session_id(), unpickled.session_id());
+
+        assert_eq!(unpickled.decrypt(&old_message)?, plaintext.as_bytes());
+
+        let message = unpickled.encrypt(plaintext);
+
+        assert_eq!(session.decrypt(&message)?, plaintext.as_bytes());
+
+        Ok(())
+    }
+
+    #[test]
+    fn session_pickling_roundtrip_is_identity() -> Result<()> {
+        let (_, _, session, _) = sessions()?;
+
+        let pickle = session.pickle().encrypt(&PICKLE_KEY);
+
+        let decrypted_pickle = SessionPickle::from_encrypted(&pickle, &PICKLE_KEY)?;
+        let unpickled_group_session = Session::from_pickle(decrypted_pickle);
+        let repickle = unpickled_group_session.pickle();
+
+        assert_eq!(session.session_id(), unpickled_group_session.session_id());
+
+        let decrypted_pickle = SessionPickle::from_encrypted(&pickle, &PICKLE_KEY)?;
+        let pickle = serde_json::to_value(decrypted_pickle)?;
+        let repickle = serde_json::to_value(repickle)?;
+
+        assert_eq!(pickle, repickle);
+
+        Ok(())
+    }
+}
+
\ No newline at end of file diff --git a/src/vodozemac/olm/session/ratchet.rs.html b/src/vodozemac/olm/session/ratchet.rs.html new file mode 100644 index 00000000..11898df8 --- /dev/null +++ b/src/vodozemac/olm/session/ratchet.rs.html @@ -0,0 +1,249 @@ +ratchet.rs - source +
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+
// Copyright 2021 Damir Jelić
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+use std::fmt::Debug;
+
+use matrix_pickle::Decode;
+use serde::{Deserialize, Serialize};
+use x25519_dalek::SharedSecret;
+
+use super::{
+    chain_key::RemoteChainKey,
+    root_key::{RemoteRootKey, RootKey},
+};
+use crate::{types::Curve25519SecretKey, Curve25519PublicKey};
+
+#[derive(Serialize, Deserialize, Clone)]
+#[serde(transparent)]
+pub(super) struct RatchetKey(Curve25519SecretKey);
+
+#[derive(Debug, PartialEq, Eq, Clone, Copy)]
+pub struct RatchetPublicKey(Curve25519PublicKey);
+
+#[derive(Clone, Copy, Hash, PartialEq, Eq, Serialize, Deserialize, Decode)]
+#[serde(transparent)]
+pub struct RemoteRatchetKey(Curve25519PublicKey);
+
+impl Debug for RemoteRatchetKey {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        self.0.fmt(f)
+    }
+}
+
+impl RatchetKey {
+    pub fn new() -> Self {
+        Self(Curve25519SecretKey::new())
+    }
+
+    pub fn diffie_hellman(&self, other: &RemoteRatchetKey) -> SharedSecret {
+        self.0.diffie_hellman(&other.0)
+    }
+}
+
+impl Default for RatchetKey {
+    fn default() -> Self {
+        Self::new()
+    }
+}
+
+impl From<Curve25519SecretKey> for RatchetKey {
+    fn from(key: Curve25519SecretKey) -> Self {
+        Self(key)
+    }
+}
+
+impl From<[u8; 32]> for RatchetPublicKey {
+    fn from(bytes: [u8; 32]) -> Self {
+        RatchetPublicKey(Curve25519PublicKey::from(bytes))
+    }
+}
+
+impl From<[u8; 32]> for RemoteRatchetKey {
+    fn from(bytes: [u8; 32]) -> Self {
+        RemoteRatchetKey(Curve25519PublicKey::from(bytes))
+    }
+}
+
+impl From<Curve25519PublicKey> for RemoteRatchetKey {
+    fn from(key: Curve25519PublicKey) -> Self {
+        RemoteRatchetKey(key)
+    }
+}
+
+impl AsRef<Curve25519PublicKey> for RatchetPublicKey {
+    fn as_ref(&self) -> &Curve25519PublicKey {
+        &self.0
+    }
+}
+
+impl From<&RatchetKey> for RatchetPublicKey {
+    fn from(r: &RatchetKey) -> Self {
+        RatchetPublicKey(Curve25519PublicKey::from(&r.0))
+    }
+}
+
+#[derive(Serialize, Deserialize, Clone)]
+pub(super) struct Ratchet {
+    root_key: RootKey,
+    ratchet_key: RatchetKey,
+}
+
+impl Ratchet {
+    pub fn new(root_key: RootKey) -> Self {
+        let ratchet_key = RatchetKey::new();
+
+        Self { root_key, ratchet_key }
+    }
+
+    pub fn new_with_ratchet_key(root_key: RootKey, ratchet_key: RatchetKey) -> Self {
+        Self { root_key, ratchet_key }
+    }
+
+    pub fn advance(&self, remote_key: RemoteRatchetKey) -> (RemoteRootKey, RemoteChainKey) {
+        let (remote_root_key, remote_chain_key) =
+            self.root_key.advance(&self.ratchet_key, &remote_key);
+
+        (remote_root_key, remote_chain_key)
+    }
+
+    pub fn ratchet_key(&self) -> &RatchetKey {
+        &self.ratchet_key
+    }
+}
+
\ No newline at end of file diff --git a/src/vodozemac/olm/session/receiver_chain.rs.html b/src/vodozemac/olm/session/receiver_chain.rs.html new file mode 100644 index 00000000..9e551786 --- /dev/null +++ b/src/vodozemac/olm/session/receiver_chain.rs.html @@ -0,0 +1,381 @@ +receiver_chain.rs - source +
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+
// Copyright 2021 Damir Jelić, Denis Kasak
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+use std::fmt::Debug;
+
+use arrayvec::ArrayVec;
+use serde::{Deserialize, Serialize};
+
+use super::{
+    chain_key::RemoteChainKey, message_key::RemoteMessageKey, ratchet::RemoteRatchetKey,
+    DecryptionError,
+};
+use crate::olm::{messages::Message, session_config::Version, SessionConfig};
+
+const MAX_MESSAGE_GAP: u64 = 2000;
+const MAX_MESSAGE_KEYS: usize = 40;
+
+#[derive(Serialize, Deserialize, Clone)]
+struct MessageKeyStore {
+    inner: ArrayVec<RemoteMessageKey, MAX_MESSAGE_KEYS>,
+}
+
+impl MessageKeyStore {
+    fn new() -> Self {
+        Self { inner: ArrayVec::new() }
+    }
+
+    fn push(&mut self, message_key: RemoteMessageKey) {
+        if self.inner.is_full() {
+            self.inner.pop_at(0);
+        }
+
+        self.inner.push(message_key)
+    }
+
+    fn merge(&mut self, mut store: MessageKeyStore) {
+        for key in store.inner.drain(..) {
+            self.push(key);
+        }
+    }
+
+    fn get_message_key(&self, chain_index: u64) -> Option<&RemoteMessageKey> {
+        self.inner.iter().find(|k| k.chain_index() == chain_index)
+    }
+
+    fn remove_message_key(&mut self, chain_index: u64) {
+        self.inner.retain(|k| k.chain_index() != chain_index);
+    }
+}
+
+impl Default for MessageKeyStore {
+    fn default() -> Self {
+        Self::new()
+    }
+}
+
+enum FoundMessageKey<'a> {
+    Existing(&'a RemoteMessageKey),
+    New(Box<(RemoteChainKey, MessageKeyStore, RemoteMessageKey)>),
+}
+
+impl FoundMessageKey<'_> {
+    fn decrypt(
+        &self,
+        message: &Message,
+        config: &SessionConfig,
+    ) -> Result<Vec<u8>, DecryptionError> {
+        let message_key = match self {
+            FoundMessageKey::Existing(m) => m,
+            FoundMessageKey::New(m) => &m.2,
+        };
+
+        match config.version {
+            Version::V1 => message_key.decrypt_truncated_mac(message),
+            Version::V2 => message_key.decrypt(message),
+        }
+    }
+}
+
+#[derive(Serialize, Deserialize, Clone)]
+pub(super) struct ReceiverChain {
+    ratchet_key: RemoteRatchetKey,
+    hkdf_ratchet: RemoteChainKey,
+    skipped_message_keys: MessageKeyStore,
+}
+
+impl Debug for ReceiverChain {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        let Self { ratchet_key, hkdf_ratchet, skipped_message_keys } = self;
+
+        f.debug_struct("ReceiverChain")
+            .field("ratchet_key", &ratchet_key)
+            .field("chain_index", &hkdf_ratchet.chain_index())
+            .field("skipped_message_keys", &skipped_message_keys.inner)
+            .finish_non_exhaustive()
+    }
+}
+
+impl ReceiverChain {
+    pub fn new(ratchet_key: RemoteRatchetKey, chain_key: RemoteChainKey) -> Self {
+        ReceiverChain {
+            ratchet_key,
+            hkdf_ratchet: chain_key,
+            skipped_message_keys: Default::default(),
+        }
+    }
+
+    fn find_message_key(&self, chain_index: u64) -> Result<FoundMessageKey<'_>, DecryptionError> {
+        let message_gap = chain_index.saturating_sub(self.hkdf_ratchet.chain_index());
+
+        if message_gap > MAX_MESSAGE_GAP {
+            Err(DecryptionError::TooBigMessageGap(message_gap, MAX_MESSAGE_GAP))
+        } else if self.hkdf_ratchet.chain_index() > chain_index {
+            self.skipped_message_keys
+                .get_message_key(chain_index)
+                .map(FoundMessageKey::Existing)
+                .ok_or(DecryptionError::MissingMessageKey(chain_index))
+        } else {
+            let mut ratchet = self.hkdf_ratchet.clone();
+            let mut skipped_keys = MessageKeyStore::new();
+
+            // Advance the ratchet up until our desired point.
+            while ratchet.chain_index() < chain_index {
+                if chain_index - ratchet.chain_index() > MAX_MESSAGE_KEYS as u64 {
+                    ratchet.advance();
+                } else {
+                    let key = ratchet.create_message_key();
+                    skipped_keys.push(key);
+                }
+            }
+
+            // Create now our desired message key
+            let message_key = ratchet.create_message_key();
+
+            Ok(FoundMessageKey::New(Box::new((ratchet, skipped_keys, message_key))))
+        }
+    }
+
+    pub fn decrypt(
+        &mut self,
+        message: &Message,
+        config: &SessionConfig,
+    ) -> Result<Vec<u8>, DecryptionError> {
+        let chain_index = message.chain_index;
+        let message_key = self.find_message_key(chain_index)?;
+
+        let plaintext = message_key.decrypt(message, config)?;
+
+        match message_key {
+            FoundMessageKey::Existing(m) => {
+                let chain_index = m.chain_index();
+                self.skipped_message_keys.remove_message_key(chain_index)
+            }
+            FoundMessageKey::New(m) => {
+                let (ratchet, skipped_keys, _) = *m;
+
+                self.hkdf_ratchet = ratchet;
+                self.skipped_message_keys.merge(skipped_keys);
+            }
+        }
+
+        Ok(plaintext)
+    }
+
+    #[cfg(feature = "libolm-compat")]
+    pub fn ratchet_key(&self) -> RemoteRatchetKey {
+        self.ratchet_key
+    }
+
+    #[cfg(feature = "libolm-compat")]
+    pub fn insert_message_key(&mut self, message_key: RemoteMessageKey) {
+        self.skipped_message_keys.push(message_key)
+    }
+
+    pub fn belongs_to(&self, ratchet_key: &RemoteRatchetKey) -> bool {
+        &self.ratchet_key == ratchet_key
+    }
+}
+
\ No newline at end of file diff --git a/src/vodozemac/olm/session/root_key.rs.html b/src/vodozemac/olm/session/root_key.rs.html new file mode 100644 index 00000000..6ca26386 --- /dev/null +++ b/src/vodozemac/olm/session/root_key.rs.html @@ -0,0 +1,207 @@ +root_key.rs - source +
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+
// Copyright 2021 Damir Jelić
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+use hkdf::Hkdf;
+use serde::{Deserialize, Serialize};
+use sha2::Sha256;
+use zeroize::Zeroize;
+
+use super::{
+    chain_key::{ChainKey, RemoteChainKey},
+    ratchet::{RatchetKey, RemoteRatchetKey},
+};
+
+const ADVANCEMENT_SEED: &[u8; 11] = b"OLM_RATCHET";
+
+#[derive(Serialize, Deserialize, Clone, Zeroize)]
+#[serde(transparent)]
+#[zeroize(drop)]
+pub(crate) struct RootKey {
+    pub key: Box<[u8; 32]>,
+}
+
+#[derive(Serialize, Deserialize, Clone, Zeroize)]
+#[zeroize(drop)]
+pub(crate) struct RemoteRootKey {
+    pub key: Box<[u8; 32]>,
+}
+
+fn kdf(
+    root_key: &[u8; 32],
+    ratchet_key: &RatchetKey,
+    remote_ratchet_key: &RemoteRatchetKey,
+) -> Box<[u8; 64]> {
+    let shared_secret = ratchet_key.diffie_hellman(remote_ratchet_key);
+    let hkdf: Hkdf<Sha256> = Hkdf::new(Some(root_key.as_ref()), shared_secret.as_bytes());
+    let mut output = Box::new([0u8; 64]);
+
+    hkdf.expand(ADVANCEMENT_SEED, output.as_mut_slice()).expect("Can't expand");
+
+    output
+}
+
+impl RemoteRootKey {
+    pub(super) fn new(bytes: Box<[u8; 32]>) -> Self {
+        Self { key: bytes }
+    }
+
+    pub(super) fn advance(
+        &self,
+        remote_ratchet_key: &RemoteRatchetKey,
+    ) -> (RootKey, ChainKey, RatchetKey) {
+        let ratchet_key = RatchetKey::new();
+        let output = kdf(&self.key, &ratchet_key, remote_ratchet_key);
+
+        let mut chain_key = Box::new([0u8; 32]);
+        let mut root_key = Box::new([0u8; 32]);
+
+        chain_key.copy_from_slice(&output[32..]);
+        root_key.copy_from_slice(&output[..32]);
+
+        let chain_key = ChainKey::new(chain_key);
+        let root_key = RootKey::new(root_key);
+
+        (root_key, chain_key, ratchet_key)
+    }
+}
+
+impl RootKey {
+    pub(super) fn new(bytes: Box<[u8; 32]>) -> Self {
+        Self { key: bytes }
+    }
+
+    pub(super) fn advance(
+        &self,
+        old_ratchet_key: &RatchetKey,
+        remote_ratchet_key: &RemoteRatchetKey,
+    ) -> (RemoteRootKey, RemoteChainKey) {
+        let output = kdf(&self.key, old_ratchet_key, remote_ratchet_key);
+
+        let mut chain_key = Box::new([0u8; 32]);
+        let mut root_key = Box::new([0u8; 32]);
+
+        root_key.copy_from_slice(&output[..32]);
+        chain_key.copy_from_slice(&output[32..]);
+
+        let root_key = RemoteRootKey::new(root_key);
+        let chain_key = RemoteChainKey::new(chain_key);
+
+        (root_key, chain_key)
+    }
+}
+
\ No newline at end of file diff --git a/src/vodozemac/olm/session_config.rs.html b/src/vodozemac/olm/session_config.rs.html new file mode 100644 index 00000000..8a433d62 --- /dev/null +++ b/src/vodozemac/olm/session_config.rs.html @@ -0,0 +1,113 @@ +session_config.rs - source +
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+
// Copyright 2022 The Matrix.org Foundation C.I.C.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+use serde::{Deserialize, Serialize};
+
+/// A struct to configure how Olm sessions should work under the hood.
+/// Currently only the MAC truncation behaviour can be configured.
+#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, Eq)]
+pub struct SessionConfig {
+    pub(super) version: Version,
+}
+
+#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, Eq)]
+pub(super) enum Version {
+    V1 = 1,
+    V2 = 2,
+}
+
+impl SessionConfig {
+    /// Get the numeric version of this `SessionConfig`.
+    pub fn version(&self) -> u8 {
+        self.version as u8
+    }
+
+    /// Create a `SessionConfig` for the Olm version 1. This version of Olm will
+    /// use AES-256 and HMAC with a truncated MAC to encrypt individual
+    /// messages. The MAC will be truncated to 8 bytes.
+    pub fn version_1() -> Self {
+        SessionConfig { version: Version::V1 }
+    }
+
+    /// Create a `SessionConfig` for the Olm version 2. This version of Olm will
+    /// use AES-256 and HMAC to encrypt individual messages. The MAC won't be
+    /// truncated.
+    pub fn version_2() -> Self {
+        SessionConfig { version: Version::V2 }
+    }
+}
+
+impl Default for SessionConfig {
+    fn default() -> Self {
+        Self::version_2()
+    }
+}
+
\ No newline at end of file diff --git a/src/vodozemac/olm/session_keys.rs.html b/src/vodozemac/olm/session_keys.rs.html new file mode 100644 index 00000000..f6973d70 --- /dev/null +++ b/src/vodozemac/olm/session_keys.rs.html @@ -0,0 +1,125 @@ +session_keys.rs - source +
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+
// Copyright 2021 The Matrix.org Foundation C.I.C.
+// Copyright 2021 Damir Jelić, Denis Kasak
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+use matrix_pickle::Decode;
+use serde::{Deserialize, Serialize};
+use sha2::{Digest, Sha256};
+
+use crate::{utilities::base64_encode, Curve25519PublicKey};
+
+/// The set of keys that were used to establish the Olm Session,
+#[derive(Clone, Copy, Deserialize, Serialize, PartialEq, Eq, Decode)]
+pub struct SessionKeys {
+    pub identity_key: Curve25519PublicKey,
+    pub base_key: Curve25519PublicKey,
+    pub one_time_key: Curve25519PublicKey,
+}
+
+impl SessionKeys {
+    /// Returns the globally unique session ID which these [`SessionKeys`] will
+    /// produce.
+    ///
+    /// A session ID is the SHA256 of the concatenation of three `SessionKeys`,
+    /// the account's identity key, the ephemeral base key and the one-time
+    /// key which is used to establish the session.
+    ///
+    /// Due to the construction, every session ID is (probabilistically)
+    /// globally unique.
+    pub fn session_id(&self) -> String {
+        let sha = Sha256::new();
+
+        let digest = sha
+            .chain_update(self.identity_key.as_bytes())
+            .chain_update(self.base_key.as_bytes())
+            .chain_update(self.one_time_key.as_bytes())
+            .finalize();
+
+        base64_encode(digest)
+    }
+}
+
+impl std::fmt::Debug for SessionKeys {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        f.debug_struct("SessionKeys")
+            .field("identity_key", &self.identity_key.to_base64())
+            .field("base_key", &self.base_key.to_base64())
+            .field("one_time_key", &self.one_time_key.to_base64())
+            .finish()
+    }
+}
+
\ No newline at end of file diff --git a/src/vodozemac/olm/shared_secret.rs.html b/src/vodozemac/olm/shared_secret.rs.html new file mode 100644 index 00000000..abd0addb --- /dev/null +++ b/src/vodozemac/olm/shared_secret.rs.html @@ -0,0 +1,319 @@ +shared_secret.rs - source +
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+
// Copyright 2021 Damir Jelić
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+//! A 3DH implementation following the Olm [spec].
+//!
+//! The setup takes four Curve25519 inputs: Identity keys for Alice and Bob,
+//! (Ia, Ib), and one-time keys for Alice and Bob (Ea, Eb).
+//!
+//! A shared secret S is generated via Triple Diffie-Hellman using the above
+//! inputs. The initial 256-bit root key R0 and a 256-bit chain key C0,0 are
+//! derived from the shared secret using an HMAC-based Key Derivation Function
+//! with SHA-256 as the hash function (HKDF-SHA-256), the default salt and
+//! "OLM_ROOT" as the info.
+//!
+//! ```text
+//!     S = ECDH(Ia, Eb) || ECDH(Ea, Ib) || ECDH (Ea, Eb)
+//!
+//!     R0, C0,0 = HKDF(0, S, "OLM_ROOT", 64)
+//! ```
+//!
+//! [spec]: https://gitlab.matrix.org/matrix-org/olm/-/blob/master/docs/olm.md#initial-setup
+
+use hkdf::Hkdf;
+use sha2::Sha256;
+use x25519_dalek::{ReusableSecret, SharedSecret};
+use zeroize::Zeroize;
+
+use crate::{types::Curve25519SecretKey as StaticSecret, Curve25519PublicKey as PublicKey};
+
+#[derive(Zeroize)]
+#[zeroize(drop)]
+pub struct Shared3DHSecret(Box<[u8; 96]>);
+
+#[derive(Zeroize)]
+#[zeroize(drop)]
+pub struct RemoteShared3DHSecret(Box<[u8; 96]>);
+
+fn expand(shared_secret: &[u8; 96]) -> (Box<[u8; 32]>, Box<[u8; 32]>) {
+    let hkdf: Hkdf<Sha256> = Hkdf::new(Some(&[0]), shared_secret);
+    let mut root_key = Box::new([0u8; 32]);
+    let mut chain_key = Box::new([0u8; 32]);
+
+    let mut expanded_keys = [0u8; 64];
+
+    hkdf.expand(b"OLM_ROOT", &mut expanded_keys)
+        .expect("Can't expand the shared 3DH secret into the Olm root");
+
+    root_key.copy_from_slice(&expanded_keys[0..32]);
+    chain_key.copy_from_slice(&expanded_keys[32..64]);
+
+    expanded_keys.zeroize();
+
+    (root_key, chain_key)
+}
+
+fn merge_secrets(
+    first_secret: SharedSecret,
+    second_secret: SharedSecret,
+    third_secret: SharedSecret,
+) -> Box<[u8; 96]> {
+    let mut secret = Box::new([0u8; 96]);
+
+    secret[0..32].copy_from_slice(first_secret.as_bytes());
+    secret[32..64].copy_from_slice(second_secret.as_bytes());
+    secret[64..96].copy_from_slice(third_secret.as_bytes());
+
+    secret
+}
+
+impl RemoteShared3DHSecret {
+    pub(crate) fn new(
+        identity_key: &StaticSecret,
+        one_time_key: &StaticSecret,
+        remote_identity_key: &PublicKey,
+        remote_one_time_key: &PublicKey,
+    ) -> Self {
+        let first_secret = one_time_key.diffie_hellman(remote_identity_key);
+        let second_secret = identity_key.diffie_hellman(remote_one_time_key);
+        let third_secret = one_time_key.diffie_hellman(remote_one_time_key);
+
+        Self(merge_secrets(first_secret, second_secret, third_secret))
+    }
+
+    pub fn expand(self) -> (Box<[u8; 32]>, Box<[u8; 32]>) {
+        expand(&self.0)
+    }
+}
+
+impl Shared3DHSecret {
+    pub(crate) fn new(
+        identity_key: &StaticSecret,
+        one_time_key: &ReusableSecret,
+        remote_identity_key: &PublicKey,
+        remote_one_time_key: &PublicKey,
+    ) -> Self {
+        let first_secret = identity_key.diffie_hellman(remote_one_time_key);
+        let second_secret = one_time_key.diffie_hellman(&remote_identity_key.inner);
+        let third_secret = one_time_key.diffie_hellman(&remote_one_time_key.inner);
+
+        Self(merge_secrets(first_secret, second_secret, third_secret))
+    }
+
+    pub fn expand(self) -> (Box<[u8; 32]>, Box<[u8; 32]>) {
+        expand(&self.0)
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use rand::thread_rng;
+    use x25519_dalek::ReusableSecret;
+
+    use super::{RemoteShared3DHSecret, Shared3DHSecret};
+    use crate::{types::Curve25519SecretKey as StaticSecret, Curve25519PublicKey as PublicKey};
+
+    #[test]
+    fn triple_diffie_hellman() {
+        let rng = thread_rng();
+
+        let alice_identity = StaticSecret::new();
+        let alice_one_time = ReusableSecret::random_from_rng(rng);
+
+        let bob_identity = StaticSecret::new();
+        let bob_one_time = StaticSecret::new();
+
+        let alice_secret = Shared3DHSecret::new(
+            &alice_identity,
+            &alice_one_time,
+            &PublicKey::from(&bob_identity),
+            &PublicKey::from(&bob_one_time),
+        );
+
+        let bob_secret = RemoteShared3DHSecret::new(
+            &bob_identity,
+            &bob_one_time,
+            &PublicKey::from(&alice_identity),
+            &PublicKey::from(&alice_one_time),
+        );
+
+        assert_eq!(alice_secret.0, bob_secret.0);
+
+        let alice_result = alice_secret.expand();
+        let bob_result = bob_secret.expand();
+
+        assert_eq!(alice_result, bob_result);
+    }
+}
+
\ No newline at end of file diff --git a/src/vodozemac/sas.rs.html b/src/vodozemac/sas.rs.html new file mode 100644 index 00000000..8b7b8113 --- /dev/null +++ b/src/vodozemac/sas.rs.html @@ -0,0 +1,1257 @@ +sas.rs - source +
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+195
+196
+197
+198
+199
+200
+201
+202
+203
+204
+205
+206
+207
+208
+209
+210
+211
+212
+213
+214
+215
+216
+217
+218
+219
+220
+221
+222
+223
+224
+225
+226
+227
+228
+229
+230
+231
+232
+233
+234
+235
+236
+237
+238
+239
+240
+241
+242
+243
+244
+245
+246
+247
+248
+249
+250
+251
+252
+253
+254
+255
+256
+257
+258
+259
+260
+261
+262
+263
+264
+265
+266
+267
+268
+269
+270
+271
+272
+273
+274
+275
+276
+277
+278
+279
+280
+281
+282
+283
+284
+285
+286
+287
+288
+289
+290
+291
+292
+293
+294
+295
+296
+297
+298
+299
+300
+301
+302
+303
+304
+305
+306
+307
+308
+309
+310
+311
+312
+313
+314
+315
+316
+317
+318
+319
+320
+321
+322
+323
+324
+325
+326
+327
+328
+329
+330
+331
+332
+333
+334
+335
+336
+337
+338
+339
+340
+341
+342
+343
+344
+345
+346
+347
+348
+349
+350
+351
+352
+353
+354
+355
+356
+357
+358
+359
+360
+361
+362
+363
+364
+365
+366
+367
+368
+369
+370
+371
+372
+373
+374
+375
+376
+377
+378
+379
+380
+381
+382
+383
+384
+385
+386
+387
+388
+389
+390
+391
+392
+393
+394
+395
+396
+397
+398
+399
+400
+401
+402
+403
+404
+405
+406
+407
+408
+409
+410
+411
+412
+413
+414
+415
+416
+417
+418
+419
+420
+421
+422
+423
+424
+425
+426
+427
+428
+429
+430
+431
+432
+433
+434
+435
+436
+437
+438
+439
+440
+441
+442
+443
+444
+445
+446
+447
+448
+449
+450
+451
+452
+453
+454
+455
+456
+457
+458
+459
+460
+461
+462
+463
+464
+465
+466
+467
+468
+469
+470
+471
+472
+473
+474
+475
+476
+477
+478
+479
+480
+481
+482
+483
+484
+485
+486
+487
+488
+489
+490
+491
+492
+493
+494
+495
+496
+497
+498
+499
+500
+501
+502
+503
+504
+505
+506
+507
+508
+509
+510
+511
+512
+513
+514
+515
+516
+517
+518
+519
+520
+521
+522
+523
+524
+525
+526
+527
+528
+529
+530
+531
+532
+533
+534
+535
+536
+537
+538
+539
+540
+541
+542
+543
+544
+545
+546
+547
+548
+549
+550
+551
+552
+553
+554
+555
+556
+557
+558
+559
+560
+561
+562
+563
+564
+565
+566
+567
+568
+569
+570
+571
+572
+573
+574
+575
+576
+577
+578
+579
+580
+581
+582
+583
+584
+585
+586
+587
+588
+589
+590
+591
+592
+593
+594
+595
+596
+597
+598
+599
+600
+601
+602
+603
+604
+605
+606
+607
+608
+609
+610
+611
+612
+613
+614
+615
+616
+617
+618
+619
+620
+621
+622
+623
+624
+625
+626
+627
+
// Copyright 2021 Damir Jelić, Denis Kasak
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+//! User-friendly key verification using short authentication strings (SAS).
+//!
+//! The verification process is heavily inspired by Phil Zimmermann’s [ZRTP]
+//! key agreement handshake. A core part of key agreement in [ZRTP] is the
+//! *hash commitment*: the party that begins the key sharing process sends
+//! a *hash* of their part of the Diffie-Hellman exchange but does not send the
+//! part itself exchange until they had received the other party’s part.
+//!
+//! The verification process can be used to verify the Ed25519 identity key of
+//! an [`Account`].
+//!
+//! # Examples
+//!
+//! ```rust
+//! use vodozemac::sas::Sas;
+//! # use anyhow::Result;
+//! # fn main() -> Result<()> {
+//! let alice = Sas::new();
+//! let bob = Sas::new();
+//!
+//! let bob_public_key = bob.public_key();
+//!
+//! let bob = bob.diffie_hellman(alice.public_key())?;
+//! let alice = alice.diffie_hellman(bob_public_key)?;
+//!
+//! let alice_bytes = alice.bytes("AGREED_INFO");
+//! let bob_bytes = bob.bytes("AGREED_INFO");
+//!
+//! let alice_emojis = alice_bytes.emoji_indices();
+//! let bob_emojis = bob_bytes.emoji_indices();
+//!
+//! assert_eq!(alice_emojis, bob_emojis);
+//! # Ok(())
+//! # }
+//! ```
+//!
+//! [`Account`]: crate::olm::Account
+//! [ZRTP]: https://tools.ietf.org/html/rfc6189#section-4.4.1
+
+use hkdf::Hkdf;
+use hmac::{digest::MacError, Hmac, Mac as _};
+use rand::thread_rng;
+use sha2::Sha256;
+use thiserror::Error;
+use x25519_dalek::{EphemeralSecret, SharedSecret};
+
+use crate::{
+    utilities::{base64_decode, base64_encode},
+    Curve25519PublicKey, KeyError,
+};
+
+type HmacSha256Key = Box<[u8; 32]>;
+
+/// The output type for the SAS MAC calculation.
+pub struct Mac(Vec<u8>);
+
+impl Mac {
+    /// Convert the MAC to a base64 encoded string.
+    pub fn to_base64(&self) -> String {
+        base64_encode(&self.0)
+    }
+
+    /// Get the byte slice of the MAC.
+    pub fn as_bytes(&self) -> &[u8] {
+        &self.0
+    }
+
+    /// Create a new `Mac` object from a byte slice.
+    pub fn from_slice(bytes: &[u8]) -> Self {
+        Self(bytes.to_vec())
+    }
+
+    /// Create a new `Mac` object from a base64 encoded string.
+    pub fn from_base64(mac: &str) -> Result<Self, base64::DecodeError> {
+        let bytes = base64_decode(mac)?;
+
+        Ok(Self(bytes))
+    }
+}
+
+/// Error type for the case when we try to generate too many SAS bytes.
+#[derive(Debug, Clone, Error)]
+#[error("The given count of bytes was too large")]
+pub struct InvalidCount;
+
+/// Error type describing failures that can happen during the key verification.
+#[derive(Debug, Error)]
+pub enum SasError {
+    /// The MAC failed to be validated.
+    #[error("The SAS MAC validation didn't succeed: {0}")]
+    Mac(#[from] MacError),
+}
+
+/// A struct representing a short auth string verification object.
+///
+/// This object can be used to establish a shared secret to perform the short
+/// auth string based key verification.
+pub struct Sas {
+    secret_key: EphemeralSecret,
+    public_key: Curve25519PublicKey,
+}
+
+/// A struct representing a short auth string verification object where the
+/// shared secret has been established.
+///
+/// This object can be used to generate the short auth string and calculate and
+/// verify a MAC that protects information about the keys being verified.
+pub struct EstablishedSas {
+    shared_secret: SharedSecret,
+    our_public_key: Curve25519PublicKey,
+    their_public_key: Curve25519PublicKey,
+}
+
+impl std::fmt::Debug for EstablishedSas {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        f.debug_struct("EstablishedSas")
+            .field("our_public_key", &self.our_public_key.to_base64())
+            .field("their_public_key", &self.their_public_key.to_base64())
+            .finish_non_exhaustive()
+    }
+}
+
+/// Bytes generated from an shared secret that can be used as the short auth
+/// string.
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct SasBytes {
+    bytes: [u8; 6],
+}
+
+impl SasBytes {
+    /// Get the index of 7 emojis that can be presented to users to perform the
+    /// key verification
+    ///
+    /// The table that maps the index to an emoji can be found in the [spec].
+    ///
+    /// [spec]: https://spec.matrix.org/unstable/client-server-api/#sas-method-emoji
+    pub fn emoji_indices(&self) -> [u8; 7] {
+        Self::bytes_to_emoji_index(&self.bytes)
+    }
+
+    /// Get the three decimal numbers that can be presented to users to perform
+    /// the key verification, as described in the [spec]
+    ///
+    /// [spec]: https://spec.matrix.org/unstable/client-server-api/#sas-method-emoji
+    pub fn decimals(&self) -> (u16, u16, u16) {
+        Self::bytes_to_decimal(&self.bytes)
+    }
+
+    /// Get the raw bytes of the short auth string that can be converted to an
+    /// emoji, or decimal representation.
+    pub fn as_bytes(&self) -> &[u8; 6] {
+        &self.bytes
+    }
+
+    /// Split the first 42 bits of our 6 bytes into 7 groups of 6 bits. The 7
+    /// groups of 6 bits represent an emoji index from the [spec].
+    ///
+    /// [spec]: https://spec.matrix.org/unstable/client-server-api/#sas-method-emoji
+    fn bytes_to_emoji_index(bytes: &[u8; 6]) -> [u8; 7] {
+        let bytes: Vec<u64> = bytes.iter().map(|b| *b as u64).collect();
+        // Join the 6 bytes into one 64 bit unsigned int. This u64 will contain 48
+        // bits from our 6 bytes.
+        let mut num: u64 = bytes[0] << 40;
+        num += bytes[1] << 32;
+        num += bytes[2] << 24;
+        num += bytes[3] << 16;
+        num += bytes[4] << 8;
+        num += bytes[5];
+
+        // Take the top 42 bits of our 48 bits from the u64 and convert each 6 bits
+        // into a 6 bit number.
+        [
+            ((num >> 42) & 63) as u8,
+            ((num >> 36) & 63) as u8,
+            ((num >> 30) & 63) as u8,
+            ((num >> 24) & 63) as u8,
+            ((num >> 18) & 63) as u8,
+            ((num >> 12) & 63) as u8,
+            ((num >> 6) & 63) as u8,
+        ]
+    }
+
+    /// Convert the given bytes into three decimals. The 6th byte is ignored,
+    /// it's used for the emoji index conversion.
+    fn bytes_to_decimal(bytes: &[u8; 6]) -> (u16, u16, u16) {
+        let bytes: Vec<u16> = bytes.iter().map(|b| *b as u16).collect();
+
+        // This bitwise operation is taken from the [spec]
+        // [spec]: https://matrix.org/docs/spec/client_server/latest#sas-method-decimal
+        let first = bytes[0] << 5 | bytes[1] >> 3;
+        let second = (bytes[1] & 0x7) << 10 | bytes[2] << 2 | bytes[3] >> 6;
+        let third = (bytes[3] & 0x3F) << 7 | bytes[4] >> 1;
+
+        (first + 1000, second + 1000, third + 1000)
+    }
+}
+
+impl Default for Sas {
+    fn default() -> Self {
+        Self::new()
+    }
+}
+
+impl Sas {
+    /// Create a new random verification object
+    ///
+    /// This creates an ephemeral curve25519 keypair that can be used to
+    /// establish a shared secret.
+    pub fn new() -> Self {
+        let rng = thread_rng();
+
+        let secret_key = EphemeralSecret::random_from_rng(rng);
+        let public_key = Curve25519PublicKey::from(&secret_key);
+
+        Self { secret_key, public_key }
+    }
+
+    /// Get the public key that can be used to establish a shared secret.
+    pub fn public_key(&self) -> Curve25519PublicKey {
+        self.public_key
+    }
+
+    /// Establishes a SAS secret by performing a DH handshake with another
+    /// public key.
+    ///
+    /// Returns an [`EstablishedSas`] object which can be used to generate
+    /// [`SasBytes`] if the given public key was valid, otherwise `None`.
+    pub fn diffie_hellman(
+        self,
+        their_public_key: Curve25519PublicKey,
+    ) -> Result<EstablishedSas, KeyError> {
+        let shared_secret = self.secret_key.diffie_hellman(&their_public_key.inner);
+
+        if shared_secret.was_contributory() {
+            Ok(EstablishedSas { shared_secret, our_public_key: self.public_key, their_public_key })
+        } else {
+            Err(KeyError::NonContributoryKey)
+        }
+    }
+
+    /// Establishes a SAS secret by performing a DH handshake with another
+    /// public key in "raw", base64-encoded form.
+    ///
+    /// Returns an [`EstablishedSas`] object which can be used to generate
+    /// [`SasBytes`] if the received public key is valid, otherwise `None`.
+    pub fn diffie_hellman_with_raw(
+        self,
+        other_public_key: &str,
+    ) -> Result<EstablishedSas, KeyError> {
+        let other_public_key = Curve25519PublicKey::from_base64(other_public_key)?;
+        self.diffie_hellman(other_public_key)
+    }
+}
+
+impl EstablishedSas {
+    /// Generate [`SasBytes`] using HKDF with the shared secret as the input key
+    /// material.
+    ///
+    /// The info string should be agreed upon beforehand, both parties need to
+    /// use the same info string.
+    pub fn bytes(&self, info: &str) -> SasBytes {
+        let mut bytes = [0u8; 6];
+        let byte_vec =
+            self.bytes_raw(info, 6).expect("HKDF should always be able to generate 6 bytes");
+
+        bytes.copy_from_slice(&byte_vec);
+
+        SasBytes { bytes }
+    }
+
+    /// Generate the given number of bytes using HKDF with the shared secret
+    /// as the input key material.
+    ///
+    /// The info string should be agreed upon beforehand, both parties need to
+    /// use the same info string.
+    ///
+    /// The number of bytes we can generate is limited, we can generate up to
+    /// 32 * 255 bytes. The function will not fail if the given count is smaller
+    /// than the limit.
+    pub fn bytes_raw(&self, info: &str, count: usize) -> Result<Vec<u8>, InvalidCount> {
+        let mut output = vec![0u8; count];
+        let hkdf = self.get_hkdf();
+
+        hkdf.expand(info.as_bytes(), &mut output[0..count]).map_err(|_| InvalidCount)?;
+
+        Ok(output)
+    }
+
+    /// Calculate a MAC for the given input using the info string as additional
+    /// data.
+    ///
+    ///
+    /// This should be used to calculate a MAC of the ed25519 identity key of an
+    /// [`Account`]
+    ///
+    /// The MAC is returned as a base64 encoded string.
+    ///
+    /// [`Account`]: crate::olm::Account
+    pub fn calculate_mac(&self, input: &str, info: &str) -> Mac {
+        let mut mac = self.get_mac(info);
+
+        mac.update(input.as_ref());
+
+        Mac(mac.finalize().into_bytes().to_vec())
+    }
+
+    /// Calculate a MAC for the given input using the info string as additional
+    /// data, the MAC is returned as an invalid base64 encoded string.
+    ///
+    /// **Warning**: This method should never be used unless you require libolm
+    /// compatibility. Libolm used to incorrectly encode their MAC because the
+    /// input buffer was reused as the output buffer. This method replicates the
+    /// buggy behaviour.
+    #[cfg(feature = "libolm-compat")]
+    pub fn calculate_mac_invalid_base64(&self, input: &str, info: &str) -> String {
+        // First calculate the MAC as usual.
+        let mac = self.calculate_mac(input, info);
+
+        // Since the input buffer is reused as an output buffer, and base64
+        // operates on 3 input bytes to generate 4 output bytes, the input
+        // buffer gets overrun by the output.
+        //
+        // Only 6 bytes of the MAC get to be used before the output overwrites
+        // the input.
+
+        // All three bytes of the first input chunk are used successfully.
+        let mut out = base64_encode(&mac.as_bytes()[0..3]);
+
+        // For the next input chunk, only two bytes are sourced from the actual
+        // MAC, since the first byte gets overwritten by the output.
+        let mut bytes_from_mac = 2;
+
+        // Subsequent input chunks get progressively more overwritten by the
+        // output, so that after two iterations, none of the original input
+        // bytes remain.
+        for i in (6..10).step_by(3) {
+            let from_mac = &mac.as_bytes()[i - bytes_from_mac..i];
+            let from_out = &out.as_bytes()[out.len() - (3 - bytes_from_mac)..];
+
+            let bytes = [from_out, from_mac].concat();
+            let encoded = base64_encode(bytes);
+            bytes_from_mac -= 1;
+
+            out = out + &encoded;
+        }
+
+        // At this point, the rest of our input will be completely sourced from
+        // the previous output. The MAC has a size of 32, so we abort before we
+        // get to the remainder calculation.
+        for i in (9..30).step_by(3) {
+            let next = &out.as_bytes()[i..i + 3];
+            let next_four = base64_encode(next);
+            out = out + &next_four;
+        }
+
+        // Finally, use the remainder to get the last 3 bytes of output. No
+        // padding is used.
+        let next = &out.as_bytes()[30..32];
+        let next = base64_encode(next);
+
+        out + &next
+    }
+
+    /// Verify a MAC that was previously created using the
+    /// [`EstablishedSas::calculate_mac()`] method.
+    ///
+    /// Users should calculate a MAC and send it to the other side, they should
+    /// then verify each other's MAC using this method.
+    pub fn verify_mac(&self, input: &str, info: &str, tag: &Mac) -> Result<(), SasError> {
+        let mut mac = self.get_mac(info);
+        mac.update(input.as_bytes());
+
+        Ok(mac.verify_slice(&tag.0)?)
+    }
+
+    /// Get the public key that was created by us, that was used to establish
+    /// the shared secret.
+    pub fn our_public_key(&self) -> Curve25519PublicKey {
+        self.our_public_key
+    }
+
+    /// Get the public key that was created by the other party, that was used to
+    /// establish the shared secret.
+    pub fn their_public_key(&self) -> Curve25519PublicKey {
+        self.their_public_key
+    }
+
+    fn get_hkdf(&self) -> Hkdf<Sha256> {
+        Hkdf::new(None, self.shared_secret.as_bytes())
+    }
+
+    fn get_mac_key(&self, info: &str) -> HmacSha256Key {
+        let mut mac_key = Box::new([0u8; 32]);
+        let hkdf = self.get_hkdf();
+
+        hkdf.expand(info.as_bytes(), mac_key.as_mut_slice()).expect("Can't expand the MAC key");
+
+        mac_key
+    }
+
+    fn get_mac(&self, info: &str) -> Hmac<Sha256> {
+        let mac_key = self.get_mac_key(info);
+        Hmac::<Sha256>::new_from_slice(mac_key.as_slice()).expect("Can't create a HMAC object")
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use anyhow::Result;
+    use olm_rs::sas::OlmSas;
+    use proptest::prelude::*;
+
+    use super::{Mac, Sas, SasBytes};
+
+    const ALICE_MXID: &str = "@alice:example.com";
+    const ALICE_DEVICE_ID: &str = "AAAAAAAAAA";
+    const BOB_MXID: &str = "@bob:example.com";
+    const BOB_DEVICE_ID: &str = "BBBBBBBBBB";
+
+    #[test]
+    fn mac_from_slice_as_bytes_is_identity() {
+        let bytes = "ABCDEFGH".as_bytes();
+        assert_eq!(
+            Mac::from_slice(bytes).as_bytes(),
+            bytes,
+            "as_bytes() after from_slice() is not identity"
+        );
+    }
+
+    #[test]
+    fn libolm_and_vodozemac_generate_same_bytes() -> Result<()> {
+        let mut olm = OlmSas::new();
+        let dalek = Sas::new();
+
+        olm.set_their_public_key(dalek.public_key().to_base64())
+            .expect("Couldn't set the public key for libolm");
+        let established = dalek.diffie_hellman_with_raw(&olm.public_key())?;
+
+        assert_eq!(
+            olm.generate_bytes("TEST", 10).expect("libolm couldn't generate SAS bytes"),
+            established.bytes_raw("TEST", 10)?
+        );
+
+        Ok(())
+    }
+
+    #[test]
+    fn vodozemac_and_vodozemac_generate_same_bytes() -> Result<()> {
+        let alice = Sas::default();
+        let bob = Sas::default();
+
+        let alice_public_key_encoded = alice.public_key().to_base64();
+        let alice_public_key = alice.public_key().to_owned();
+        let bob_public_key_encoded = bob.public_key().to_base64();
+        let bob_public_key = bob.public_key();
+
+        let alice_established = alice.diffie_hellman_with_raw(&bob_public_key_encoded)?;
+        let bob_established = bob.diffie_hellman_with_raw(&alice_public_key_encoded)?;
+
+        assert_eq!(alice_established.our_public_key(), alice_public_key);
+        assert_eq!(alice_established.their_public_key(), bob_public_key);
+        assert_eq!(bob_established.our_public_key(), bob_public_key);
+        assert_eq!(bob_established.their_public_key(), alice_public_key);
+
+        let alice_bytes = alice_established.bytes("TEST");
+        let bob_bytes = bob_established.bytes("TEST");
+
+        assert_eq!(alice_bytes, bob_bytes, "The two sides calculated different bytes.");
+        assert_eq!(
+            alice_bytes.emoji_indices(),
+            bob_bytes.emoji_indices(),
+            "The two sides calculated different emoji indices."
+        );
+        assert_eq!(
+            alice_bytes.decimals(),
+            bob_bytes.decimals(),
+            "The two sides calculated different decimals."
+        );
+        assert_eq!(alice_bytes.as_bytes(), bob_bytes.as_bytes());
+
+        Ok(())
+    }
+
+    #[test]
+    fn calculate_mac_vodozemac_vodozemac() -> Result<()> {
+        let alice = Sas::new();
+        let bob = Sas::new();
+
+        let alice_public_key = alice.public_key().to_base64();
+        let bob_public_key = bob.public_key().to_base64();
+
+        let message = format!("ed25519:{BOB_DEVICE_ID}");
+        let extra_info = format!(
+            "MATRIX_KEY_VERIFICATION_MAC\
+             {BOB_MXID}{BOB_DEVICE_ID}\
+             {ALICE_MXID}{ALICE_DEVICE_ID}\
+             $1234567890\
+             KEY_IDS",
+        );
+
+        let alice_established = alice.diffie_hellman_with_raw(&bob_public_key)?;
+        let bob_established = bob.diffie_hellman_with_raw(&alice_public_key)?;
+
+        let alice_mac = alice_established.calculate_mac(&message, &extra_info);
+        let bob_mac = bob_established.calculate_mac(&message, &extra_info);
+
+        assert_eq!(
+            alice_mac.to_base64(),
+            bob_mac.to_base64(),
+            "Two vodozemac devices calculated different SAS MACs."
+        );
+
+        alice_established.verify_mac(&message, &extra_info, &bob_mac)?;
+        bob_established.verify_mac(&message, &extra_info, &alice_mac)?;
+
+        Ok(())
+    }
+
+    #[test]
+    fn calculate_mac_vodozemac_libolm() -> Result<()> {
+        let alice_on_dalek = Sas::new();
+        let mut bob_on_libolm = OlmSas::new();
+
+        let alice_public_key = alice_on_dalek.public_key().to_base64();
+        let bob_public_key = bob_on_libolm.public_key();
+
+        let message = format!("ed25519:{BOB_DEVICE_ID}");
+        let extra_info = format!(
+            "MATRIX_KEY_VERIFICATION_MAC\
+             {BOB_MXID}{BOB_DEVICE_ID}\
+             {ALICE_MXID}{ALICE_DEVICE_ID}\
+             $1234567890\
+             KEY_IDS",
+        );
+
+        bob_on_libolm
+            .set_their_public_key(alice_public_key)
+            .expect("Couldn't set the public key for libolm");
+        let established = alice_on_dalek.diffie_hellman_with_raw(&bob_public_key)?;
+
+        let olm_mac = bob_on_libolm
+            .calculate_mac_fixed_base64(&message, &extra_info)
+            .expect("libolm couldn't calculate SAS MAC.");
+        assert_eq!(olm_mac, established.calculate_mac(&message, &extra_info).to_base64());
+
+        let olm_mac =
+            Mac::from_base64(&olm_mac).expect("SAS MAC generated by libolm wasn't valid base64.");
+
+        established.verify_mac(&message, &extra_info, &olm_mac)?;
+
+        Ok(())
+    }
+
+    #[test]
+    fn calculate_mac_invalid_base64() -> Result<()> {
+        let mut olm = OlmSas::new();
+        let dalek = Sas::new();
+
+        olm.set_their_public_key(dalek.public_key().to_base64())
+            .expect("Couldn't set the public key for libolm");
+        let established = dalek.diffie_hellman_with_raw(&olm.public_key())?;
+
+        let olm_mac = olm.calculate_mac("", "").expect("libolm couldn't calculate a MAC");
+        assert_eq!(olm_mac, established.calculate_mac_invalid_base64("", ""));
+
+        Ok(())
+    }
+
+    #[test]
+    fn emoji_generation() {
+        let bytes: [u8; 6] = [0, 0, 0, 0, 0, 0];
+        let index: [u8; 7] = [0, 0, 0, 0, 0, 0, 0];
+        assert_eq!(SasBytes::bytes_to_emoji_index(&bytes), index.as_ref());
+
+        let bytes: [u8; 6] = [0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF];
+        let index: [u8; 7] = [63, 63, 63, 63, 63, 63, 63];
+        assert_eq!(SasBytes::bytes_to_emoji_index(&bytes), index.as_ref());
+    }
+
+    #[test]
+    fn decimal_generation() {
+        let bytes: [u8; 6] = [0, 0, 0, 0, 0, 0];
+        let result = SasBytes::bytes_to_decimal(&bytes);
+
+        assert_eq!(result, (1000, 1000, 1000));
+
+        let bytes: [u8; 6] = [0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF];
+        let result = SasBytes::bytes_to_decimal(&bytes);
+        assert_eq!(result, (9191, 9191, 9191));
+    }
+
+    proptest! {
+        #[test]
+        fn proptest_emoji(bytes in prop::array::uniform6(0u8..)) {
+            let numbers = SasBytes::bytes_to_emoji_index(&bytes);
+
+            for number in numbers.iter() {
+                prop_assert!(*number < 64);
+            }
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn proptest_decimals(bytes in prop::array::uniform6(0u8..)) {
+            let (first, second, third) = SasBytes::bytes_to_decimal(&bytes);
+
+            prop_assert!((1000..=9191).contains(&first));
+            prop_assert!((1000..=9191).contains(&second));
+            prop_assert!((1000..=9191).contains(&third));
+        }
+    }
+}
+
\ No newline at end of file diff --git a/src/vodozemac/types/curve25519.rs.html b/src/vodozemac/types/curve25519.rs.html new file mode 100644 index 00000000..ab9655ed --- /dev/null +++ b/src/vodozemac/types/curve25519.rs.html @@ -0,0 +1,575 @@ +curve25519.rs - source +
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+195
+196
+197
+198
+199
+200
+201
+202
+203
+204
+205
+206
+207
+208
+209
+210
+211
+212
+213
+214
+215
+216
+217
+218
+219
+220
+221
+222
+223
+224
+225
+226
+227
+228
+229
+230
+231
+232
+233
+234
+235
+236
+237
+238
+239
+240
+241
+242
+243
+244
+245
+246
+247
+248
+249
+250
+251
+252
+253
+254
+255
+256
+257
+258
+259
+260
+261
+262
+263
+264
+265
+266
+267
+268
+269
+270
+271
+272
+273
+274
+275
+276
+277
+278
+279
+280
+281
+282
+283
+284
+285
+286
+
// Copyright 2021 Denis Kasak, Damir Jelić
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+use std::fmt::Display;
+
+use base64::decoded_len_estimate;
+use matrix_pickle::{Decode, DecodeError};
+use rand::thread_rng;
+use serde::{Deserialize, Serialize};
+use x25519_dalek::{EphemeralSecret, PublicKey, ReusableSecret, SharedSecret, StaticSecret};
+use zeroize::Zeroize;
+
+use super::KeyError;
+use crate::utilities::{base64_decode, base64_encode};
+
+/// Struct representing a Curve25519 secret key.
+#[derive(Clone, Deserialize, Serialize)]
+#[serde(transparent)]
+pub struct Curve25519SecretKey(Box<StaticSecret>);
+
+impl Curve25519SecretKey {
+    /// Generate a new, random, Curve25519SecretKey.
+    pub fn new() -> Self {
+        let rng = thread_rng();
+
+        Self(Box::new(StaticSecret::random_from_rng(rng)))
+    }
+
+    /// Create a `Curve25519SecretKey` from the given slice of bytes.
+    pub fn from_slice(bytes: &[u8; 32]) -> Self {
+        // XXX: Passing in secret array as value.
+        Self(Box::new(StaticSecret::from(*bytes)))
+    }
+
+    /// Perform a Diffie-Hellman key exchange between the given
+    /// `Curve25519PublicKey` and this `Curve25519SecretKey` and return a shared
+    /// secret.
+    pub fn diffie_hellman(&self, their_public_key: &Curve25519PublicKey) -> SharedSecret {
+        self.0.diffie_hellman(&their_public_key.inner)
+    }
+
+    /// Convert the `Curve25519SecretKey` to a byte array.
+    ///
+    /// **Note**: This creates a copy of the key which won't be zeroized, the
+    /// caller of the method needs to make sure to zeroize the returned array.
+    pub fn to_bytes(&self) -> Box<[u8; 32]> {
+        let mut key = Box::new([0u8; 32]);
+        let mut bytes = self.0.to_bytes();
+        key.copy_from_slice(&bytes);
+
+        bytes.zeroize();
+
+        key
+    }
+}
+
+impl Default for Curve25519SecretKey {
+    fn default() -> Self {
+        Self::new()
+    }
+}
+
+#[derive(Serialize, Deserialize, Clone)]
+#[serde(from = "Curve25519KeypairPickle")]
+#[serde(into = "Curve25519KeypairPickle")]
+pub(crate) struct Curve25519Keypair {
+    pub secret_key: Curve25519SecretKey,
+    pub public_key: Curve25519PublicKey,
+}
+
+impl Curve25519Keypair {
+    pub fn new() -> Self {
+        let secret_key = Curve25519SecretKey::new();
+        let public_key = Curve25519PublicKey::from(&secret_key);
+
+        Self { secret_key, public_key }
+    }
+
+    #[cfg(feature = "libolm-compat")]
+    pub fn from_secret_key(key: &[u8; 32]) -> Self {
+        let secret_key = Curve25519SecretKey::from_slice(key);
+        let public_key = Curve25519PublicKey::from(&secret_key);
+
+        Curve25519Keypair { secret_key, public_key }
+    }
+
+    pub fn secret_key(&self) -> &Curve25519SecretKey {
+        &self.secret_key
+    }
+
+    pub fn public_key(&self) -> Curve25519PublicKey {
+        self.public_key
+    }
+}
+
+/// Struct representing a Curve25519 public key.
+#[derive(PartialEq, Eq, Hash, Copy, Clone, Serialize, Deserialize)]
+#[serde(transparent)]
+pub struct Curve25519PublicKey {
+    pub(crate) inner: PublicKey,
+}
+
+impl Decode for Curve25519PublicKey {
+    fn decode(reader: &mut impl std::io::Read) -> Result<Self, DecodeError> {
+        let key = <[u8; 32]>::decode(reader)?;
+
+        Ok(Curve25519PublicKey::from(key))
+    }
+}
+
+impl Curve25519PublicKey {
+    /// The number of bytes a Curve25519 public key has.
+    pub const LENGTH: usize = 32;
+
+    const BASE64_LENGTH: usize = 43;
+    const PADDED_BASE64_LENGTH: usize = 44;
+
+    /// Convert this public key to a byte array.
+    #[inline]
+    pub fn to_bytes(&self) -> [u8; Self::LENGTH] {
+        self.inner.to_bytes()
+    }
+
+    /// View this public key as a byte array.
+    #[inline]
+    pub fn as_bytes(&self) -> &[u8; Self::LENGTH] {
+        self.inner.as_bytes()
+    }
+
+    /// Convert the public key to a vector of bytes.
+    pub fn to_vec(&self) -> Vec<u8> {
+        self.inner.as_bytes().to_vec()
+    }
+
+    /// Create a `Curve25519PublicKey` from a byte array.
+    pub fn from_bytes(bytes: [u8; 32]) -> Self {
+        Self { inner: PublicKey::from(bytes) }
+    }
+
+    /// Instantiate a Curve25519 public key from an unpadded base64
+    /// representation.
+    pub fn from_base64(input: &str) -> Result<Curve25519PublicKey, KeyError> {
+        if input.len() != Self::BASE64_LENGTH && input.len() != Self::PADDED_BASE64_LENGTH {
+            Err(KeyError::InvalidKeyLength {
+                key_type: "Curve25519",
+                expected_length: Self::LENGTH,
+                length: decoded_len_estimate(input.len()),
+            })
+        } else {
+            let key = base64_decode(input)?;
+            Self::from_slice(&key)
+        }
+    }
+
+    /// Try to create a `Curve25519PublicKey` from a slice of bytes.
+    pub fn from_slice(slice: &[u8]) -> Result<Curve25519PublicKey, KeyError> {
+        let key_len = slice.len();
+
+        if key_len == Self::LENGTH {
+            let mut key = [0u8; Self::LENGTH];
+            key.copy_from_slice(slice);
+
+            Ok(Self::from(key))
+        } else {
+            Err(KeyError::InvalidKeyLength {
+                key_type: "Curve25519",
+                expected_length: Self::LENGTH,
+                length: key_len,
+            })
+        }
+    }
+
+    /// Serialize a Curve25519 public key to an unpadded base64 representation.
+    pub fn to_base64(&self) -> String {
+        base64_encode(self.inner.as_bytes())
+    }
+}
+
+impl Display for Curve25519PublicKey {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        write!(f, "{}", self.to_base64())
+    }
+}
+
+impl std::fmt::Debug for Curve25519PublicKey {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        let s = format!("curve25519:{self}");
+        <str as std::fmt::Debug>::fmt(&s, f)
+    }
+}
+
+impl From<[u8; Self::LENGTH]> for Curve25519PublicKey {
+    fn from(bytes: [u8; Self::LENGTH]) -> Curve25519PublicKey {
+        Curve25519PublicKey { inner: PublicKey::from(bytes) }
+    }
+}
+
+impl<'a> From<&'a Curve25519SecretKey> for Curve25519PublicKey {
+    fn from(secret: &'a Curve25519SecretKey) -> Curve25519PublicKey {
+        Curve25519PublicKey { inner: PublicKey::from(secret.0.as_ref()) }
+    }
+}
+
+impl<'a> From<&'a EphemeralSecret> for Curve25519PublicKey {
+    fn from(secret: &'a EphemeralSecret) -> Curve25519PublicKey {
+        Curve25519PublicKey { inner: PublicKey::from(secret) }
+    }
+}
+
+impl<'a> From<&'a ReusableSecret> for Curve25519PublicKey {
+    fn from(secret: &'a ReusableSecret) -> Curve25519PublicKey {
+        Curve25519PublicKey { inner: PublicKey::from(secret) }
+    }
+}
+
+#[derive(Serialize, Deserialize)]
+#[serde(transparent)]
+pub(crate) struct Curve25519KeypairPickle(Curve25519SecretKey);
+
+impl From<Curve25519KeypairPickle> for Curve25519Keypair {
+    fn from(pickle: Curve25519KeypairPickle) -> Self {
+        let secret_key = pickle.0;
+        let public_key = Curve25519PublicKey::from(&secret_key);
+
+        Self { secret_key, public_key }
+    }
+}
+
+impl From<Curve25519Keypair> for Curve25519KeypairPickle {
+    fn from(key: Curve25519Keypair) -> Self {
+        Curve25519KeypairPickle(key.secret_key)
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::Curve25519PublicKey;
+    use crate::{utilities::DecodeError, KeyError};
+
+    #[test]
+    fn decoding_invalid_base64_fails() {
+        let base64_payload = "a";
+        assert!(matches!(
+            Curve25519PublicKey::from_base64(base64_payload),
+            Err(KeyError::InvalidKeyLength { .. })
+        ));
+
+        let base64_payload = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA ";
+        assert!(matches!(
+            Curve25519PublicKey::from_base64(base64_payload),
+            Err(KeyError::Base64Error(DecodeError::InvalidByte(..)))
+        ));
+
+        let base64_payload = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAZ";
+        assert!(matches!(
+            Curve25519PublicKey::from_base64(base64_payload),
+            Err(KeyError::Base64Error(DecodeError::InvalidLastSymbol(..)))
+        ));
+    }
+
+    #[test]
+    fn decoding_incorrect_num_of_bytes_fails() {
+        let base64_payload = "aaaa";
+        assert!(matches!(
+            Curve25519PublicKey::from_base64(base64_payload),
+            Err(KeyError::InvalidKeyLength { .. })
+        ));
+    }
+
+    #[test]
+    fn decoding_of_correct_num_of_bytes_succeeds() {
+        let base64_payload = "MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDA";
+        assert!(matches!(Curve25519PublicKey::from_base64(base64_payload), Ok(..)));
+    }
+}
+
\ No newline at end of file diff --git a/src/vodozemac/types/ed25519.rs.html b/src/vodozemac/types/ed25519.rs.html new file mode 100644 index 00000000..f85ccd9e --- /dev/null +++ b/src/vodozemac/types/ed25519.rs.html @@ -0,0 +1,1001 @@ +ed25519.rs - source +
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+195
+196
+197
+198
+199
+200
+201
+202
+203
+204
+205
+206
+207
+208
+209
+210
+211
+212
+213
+214
+215
+216
+217
+218
+219
+220
+221
+222
+223
+224
+225
+226
+227
+228
+229
+230
+231
+232
+233
+234
+235
+236
+237
+238
+239
+240
+241
+242
+243
+244
+245
+246
+247
+248
+249
+250
+251
+252
+253
+254
+255
+256
+257
+258
+259
+260
+261
+262
+263
+264
+265
+266
+267
+268
+269
+270
+271
+272
+273
+274
+275
+276
+277
+278
+279
+280
+281
+282
+283
+284
+285
+286
+287
+288
+289
+290
+291
+292
+293
+294
+295
+296
+297
+298
+299
+300
+301
+302
+303
+304
+305
+306
+307
+308
+309
+310
+311
+312
+313
+314
+315
+316
+317
+318
+319
+320
+321
+322
+323
+324
+325
+326
+327
+328
+329
+330
+331
+332
+333
+334
+335
+336
+337
+338
+339
+340
+341
+342
+343
+344
+345
+346
+347
+348
+349
+350
+351
+352
+353
+354
+355
+356
+357
+358
+359
+360
+361
+362
+363
+364
+365
+366
+367
+368
+369
+370
+371
+372
+373
+374
+375
+376
+377
+378
+379
+380
+381
+382
+383
+384
+385
+386
+387
+388
+389
+390
+391
+392
+393
+394
+395
+396
+397
+398
+399
+400
+401
+402
+403
+404
+405
+406
+407
+408
+409
+410
+411
+412
+413
+414
+415
+416
+417
+418
+419
+420
+421
+422
+423
+424
+425
+426
+427
+428
+429
+430
+431
+432
+433
+434
+435
+436
+437
+438
+439
+440
+441
+442
+443
+444
+445
+446
+447
+448
+449
+450
+451
+452
+453
+454
+455
+456
+457
+458
+459
+460
+461
+462
+463
+464
+465
+466
+467
+468
+469
+470
+471
+472
+473
+474
+475
+476
+477
+478
+479
+480
+481
+482
+483
+484
+485
+486
+487
+488
+489
+490
+491
+492
+493
+494
+495
+496
+497
+498
+499
+
// Copyright 2021 Denis Kasak, Damir Jelić
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+use std::fmt::Display;
+
+use base64::decoded_len_estimate;
+use curve25519_dalek::EdwardsPoint;
+#[cfg(not(fuzzing))]
+use ed25519_dalek::Verifier;
+use ed25519_dalek::{
+    Signature, Signer, SigningKey, VerifyingKey, PUBLIC_KEY_LENGTH, SIGNATURE_LENGTH,
+};
+use rand::thread_rng;
+use serde::{Deserialize, Deserializer, Serialize, Serializer};
+use serde_bytes::{ByteBuf as SerdeByteBuf, Bytes as SerdeBytes};
+use sha2::Sha512;
+use thiserror::Error;
+use zeroize::Zeroize;
+
+use crate::utilities::{base64_decode, base64_encode};
+
+/// Error type describing signature verification failures.
+#[derive(Debug, Error)]
+pub enum SignatureError {
+    /// The signature wasn't valid base64.
+    #[error("The signature couldn't be decoded: {0}")]
+    Base64(#[from] base64::DecodeError),
+    /// The signature failed to be verified.
+    #[error("The signature was invalid: {0}")]
+    Signature(#[from] ed25519_dalek::SignatureError),
+}
+
+/// A struct collecting both a public, and a secret, Ed25519 key.
+#[derive(Deserialize, Serialize)]
+#[serde(try_from = "Ed25519KeypairPickle")]
+#[serde(into = "Ed25519KeypairPickle")]
+pub struct Ed25519Keypair {
+    secret_key: SecretKeys,
+    public_key: Ed25519PublicKey,
+}
+
+struct ExpandedSecretKey {
+    source: Box<[u8; 64]>,
+    inner: Box<ed25519_dalek::hazmat::ExpandedSecretKey>,
+}
+
+impl ExpandedSecretKey {
+    fn from_bytes(bytes: &[u8; 64]) -> Result<Self, ed25519_dalek::SignatureError> {
+        let mut source = Box::new([0u8; 64]);
+        source.copy_from_slice(bytes);
+
+        Ok(Self {
+            source,
+            inner: ed25519_dalek::hazmat::ExpandedSecretKey::from_bytes(bytes).into(),
+        })
+    }
+
+    fn as_bytes(&self) -> &[u8; 64] {
+        &self.source
+    }
+
+    fn sign(&self, message: &[u8]) -> Signature {
+        ed25519_dalek::hazmat::raw_sign::<Sha512>(&self.inner, message, &self.public_key().0)
+    }
+
+    fn public_key(&self) -> Ed25519PublicKey {
+        let point = EdwardsPoint::mul_base(&self.inner.scalar);
+        Ed25519PublicKey(VerifyingKey::from(point))
+    }
+}
+
+impl Clone for ExpandedSecretKey {
+    fn clone(&self) -> Self {
+        let source = self.source.clone();
+        Self {
+            source,
+            inner: ed25519_dalek::hazmat::ExpandedSecretKey::from_bytes(&self.source).into(),
+        }
+    }
+}
+
+impl Serialize for ExpandedSecretKey {
+    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+    where
+        S: Serializer,
+    {
+        let bytes = self.as_bytes();
+        SerdeBytes::new(bytes).serialize(serializer)
+    }
+}
+
+impl<'d> Deserialize<'d> for ExpandedSecretKey {
+    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
+    where
+        D: Deserializer<'d>,
+    {
+        let mut bytes = <SerdeByteBuf>::deserialize(deserializer)?;
+        let length = bytes.len();
+
+        if bytes.len() != 64 {
+            bytes.zeroize();
+
+            Err(serde::de::Error::custom(format!(
+                "Invalid secret key length: expected 64 bytes, got {length}"
+            )))
+        } else {
+            let mut slice = [0u8; 64];
+            slice.copy_from_slice(&bytes);
+
+            let ret = ExpandedSecretKey::from_bytes(&slice);
+
+            slice.zeroize();
+            bytes.zeroize();
+
+            ret.map_err(serde::de::Error::custom)
+        }
+    }
+}
+
+impl Ed25519Keypair {
+    /// Create a new, random, `Ed25519Keypair`.
+    pub fn new() -> Self {
+        let mut rng = thread_rng();
+        let signing_key = SigningKey::generate(&mut rng);
+
+        Self {
+            public_key: Ed25519PublicKey(signing_key.verifying_key()),
+            secret_key: signing_key.into(),
+        }
+    }
+
+    #[cfg(feature = "libolm-compat")]
+    pub(crate) fn from_expanded_key(secret_key: &[u8; 64]) -> Result<Self, crate::KeyError> {
+        let secret_key = ExpandedSecretKey::from_bytes(secret_key).map_err(SignatureError::from)?;
+        let public_key = secret_key.public_key();
+
+        Ok(Self { secret_key: secret_key.into(), public_key })
+    }
+
+    #[cfg(feature = "libolm-compat")]
+    pub(crate) fn expanded_secret_key(&self) -> Box<[u8; 64]> {
+        use sha2::Digest;
+
+        let mut expanded = Box::new([0u8; 64]);
+
+        match &self.secret_key {
+            SecretKeys::Normal(k) => {
+                let mut k = k.to_bytes();
+                Sha512::new().chain_update(k).finalize_into(expanded.as_mut_slice().into());
+                k.zeroize();
+            }
+            SecretKeys::Expanded(k) => expanded.copy_from_slice(k.as_bytes()),
+        }
+
+        expanded
+    }
+
+    /// Get the public Ed25519 key of this keypair.
+    pub fn public_key(&self) -> Ed25519PublicKey {
+        self.public_key
+    }
+
+    /// Sign the given message with our secret key.
+    pub fn sign(&self, message: &[u8]) -> Ed25519Signature {
+        self.secret_key.sign(message)
+    }
+}
+
+impl Default for Ed25519Keypair {
+    fn default() -> Self {
+        Self::new()
+    }
+}
+
+/// An Ed25519 secret key, used to create digital signatures.
+#[derive(Deserialize, Serialize)]
+#[serde(transparent)]
+pub struct Ed25519SecretKey(Box<SigningKey>);
+
+impl Ed25519SecretKey {
+    /// The number of bytes a Ed25519 secret key has.
+    pub const LENGTH: usize = ed25519_dalek::SECRET_KEY_LENGTH;
+
+    const BASE64_LENGTH: usize = 43;
+    const PADDED_BASE64_LENGTH: usize = 44;
+
+    /// Create a new random `Ed25519SecretKey`.
+    pub fn new() -> Self {
+        let mut rng = thread_rng();
+        let signing_key = SigningKey::generate(&mut rng);
+        let key = Box::new(signing_key);
+
+        Self(key)
+    }
+
+    /// Get the byte representation of the secret key.
+    pub fn to_bytes(&self) -> Box<[u8; 32]> {
+        Box::new(self.0.to_bytes())
+    }
+
+    /// Try to create a `Ed25519SecretKey` from a slice of bytes.
+    pub fn from_slice(bytes: &[u8; 32]) -> Self {
+        Self(Box::new(SigningKey::from_bytes(bytes)))
+    }
+
+    /// Convert the secret key to a base64 encoded string.
+    ///
+    /// This can be useful if the secret key needs to be sent over the network
+    /// or persisted.
+    ///
+    /// **Warning**: The string should be zeroized after it has been used,
+    /// otherwise an unintentional copy of the key might exist in memory.
+    pub fn to_base64(&self) -> String {
+        let mut bytes = self.to_bytes();
+        let ret = base64_encode(bytes.as_ref());
+
+        bytes.zeroize();
+
+        ret
+    }
+
+    /// Try to create a `Ed25519SecretKey` from a base64 encoded string.
+    pub fn from_base64(input: &str) -> Result<Self, crate::KeyError> {
+        if input.len() != Self::BASE64_LENGTH && input.len() != Self::PADDED_BASE64_LENGTH {
+            Err(crate::KeyError::InvalidKeyLength {
+                key_type: "Ed25519",
+                expected_length: ed25519_dalek::SECRET_KEY_LENGTH,
+                length: decoded_len_estimate(input.len()),
+            })
+        } else {
+            let mut bytes = base64_decode(input)?;
+            let mut key_bytes = [0u8; 32];
+
+            key_bytes.copy_from_slice(&bytes);
+            let key = Self::from_slice(&key_bytes);
+
+            bytes.zeroize();
+            key_bytes.zeroize();
+
+            Ok(key)
+        }
+    }
+
+    /// Get the public key that matches this `Ed25519SecretKey`.
+    pub fn public_key(&self) -> Ed25519PublicKey {
+        Ed25519PublicKey(self.0.verifying_key())
+    }
+
+    /// Sign the given slice of bytes with this `Ed25519SecretKey`.
+    ///
+    /// The signature can be verified using the public key.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// use vodozemac::{Ed25519SecretKey, Ed25519PublicKey};
+    ///
+    /// let secret = Ed25519SecretKey::new();
+    /// let message = "It's dangerous to go alone";
+    ///
+    /// let signature = secret.sign(message.as_bytes());
+    ///
+    /// let public_key = secret.public_key();
+    ///
+    /// public_key.verify(message.as_bytes(), &signature).expect("The signature has to be valid");
+    /// ```
+    pub fn sign(&self, message: &[u8]) -> Ed25519Signature {
+        Ed25519Signature(self.0.sign(message))
+    }
+}
+
+impl Default for Ed25519SecretKey {
+    fn default() -> Self {
+        Self::new()
+    }
+}
+
+#[derive(Serialize, Deserialize)]
+enum SecretKeys {
+    Normal(Box<SigningKey>),
+    Expanded(Box<ExpandedSecretKey>),
+}
+
+impl SecretKeys {
+    fn public_key(&self) -> Ed25519PublicKey {
+        match &self {
+            SecretKeys::Normal(k) => Ed25519PublicKey(k.verifying_key()),
+            SecretKeys::Expanded(k) => k.public_key(),
+        }
+    }
+
+    fn sign(&self, message: &[u8]) -> Ed25519Signature {
+        let signature = match &self {
+            SecretKeys::Normal(k) => k.sign(message),
+            SecretKeys::Expanded(k) => k.sign(message),
+        };
+
+        Ed25519Signature(signature)
+    }
+}
+
+/// An Ed25519 public key, used to verify digital signatures.
+#[derive(Serialize, Deserialize, Clone, Copy, PartialEq, Eq)]
+#[serde(transparent)]
+pub struct Ed25519PublicKey(VerifyingKey);
+
+impl Ed25519PublicKey {
+    /// The number of bytes a Ed25519 public key has.
+    pub const LENGTH: usize = PUBLIC_KEY_LENGTH;
+
+    const BASE64_LENGTH: usize = 43;
+    const PADDED_BASE64_LENGTH: usize = 44;
+
+    /// Try to create a `Ed25519PublicKey` from a slice of bytes.
+    pub fn from_slice(bytes: &[u8; 32]) -> Result<Self, crate::KeyError> {
+        Ok(Self(VerifyingKey::from_bytes(bytes).map_err(SignatureError::from)?))
+    }
+
+    /// View this public key as a byte array.
+    pub fn as_bytes(&self) -> &[u8; Self::LENGTH] {
+        self.0.as_bytes()
+    }
+
+    /// Instantiate a Ed25519PublicKey public key from an unpadded base64
+    /// representation.
+    pub fn from_base64(input: &str) -> Result<Self, crate::KeyError> {
+        if input.len() != Self::BASE64_LENGTH && input.len() != Self::PADDED_BASE64_LENGTH {
+            Err(crate::KeyError::InvalidKeyLength {
+                key_type: "Ed25519",
+                expected_length: Self::LENGTH,
+                length: decoded_len_estimate(input.len()),
+            })
+        } else {
+            let mut bytes = base64_decode(input)?;
+            let mut key_bytes = [0u8; 32];
+
+            key_bytes.copy_from_slice(&bytes);
+            let key = Self::from_slice(&key_bytes);
+
+            bytes.zeroize();
+            key_bytes.zeroize();
+
+            key
+        }
+    }
+
+    /// Serialize a Ed25519PublicKey public key to an unpadded base64
+    /// representation.
+    pub fn to_base64(&self) -> String {
+        base64_encode(self.as_bytes())
+    }
+
+    /// Verify that the provided signature for a given message has been signed
+    /// by the private key matching this public one.
+    ///
+    /// By default this performs an [RFC8032] compatible signature check. A
+    /// stricter version of the signature check can be enabled with the
+    /// `strict-signatures` feature flag.
+    ///
+    /// The stricter variant is compatible with libsodium 0.16 and under the
+    /// hood uses the [`ed25519_dalek::PublicKey::verify_strict()`] method.
+    ///
+    /// For more info, see the ed25519_dalek [README] and [this] post.
+    ///
+    /// [RFC8032]: https://datatracker.ietf.org/doc/html/rfc8032#section-5.1.7
+    /// [README]: https://github.com/dalek-cryptography/ed25519-dalek#a-note-on-signature-malleability
+    /// [this]: https://hdevalence.ca/blog/2020-10-04-its-25519am
+    #[cfg(not(fuzzing))]
+    pub fn verify(
+        &self,
+        message: &[u8],
+        signature: &Ed25519Signature,
+    ) -> Result<(), SignatureError> {
+        if cfg!(feature = "strict-signatures") {
+            Ok(self.0.verify_strict(message, &signature.0)?)
+        } else {
+            Ok(self.0.verify(message, &signature.0)?)
+        }
+    }
+
+    #[cfg(fuzzing)]
+    pub fn verify(
+        &self,
+        _message: &[u8],
+        _signature: &Ed25519Signature,
+    ) -> Result<(), SignatureError> {
+        Ok(())
+    }
+}
+
+impl Display for Ed25519PublicKey {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        write!(f, "{}", self.to_base64())
+    }
+}
+
+impl std::fmt::Debug for Ed25519PublicKey {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        let s = format!("ed25519:{self}");
+        <str as std::fmt::Debug>::fmt(&s, f)
+    }
+}
+
+/// An Ed25519 digital signature, can be used to verify the authenticity of a
+/// message.
+#[derive(Clone, Copy, PartialEq, Eq)]
+pub struct Ed25519Signature(pub(crate) Signature);
+
+impl Ed25519Signature {
+    /// The number of bytes a Ed25519 signature has.
+    pub const LENGTH: usize = SIGNATURE_LENGTH;
+
+    /// Try to create a `Ed25519Signature` from a slice of bytes.
+    pub fn from_slice(bytes: &[u8]) -> Result<Self, SignatureError> {
+        Ok(Self(Signature::try_from(bytes)?))
+    }
+
+    /// Try to create a `Ed25519Signature` from an unpadded base64
+    /// representation.
+    pub fn from_base64(signature: &str) -> Result<Self, SignatureError> {
+        Ok(Self(Signature::try_from(base64_decode(signature)?.as_slice())?))
+    }
+
+    /// Serialize an `Ed25519Signature` to an unpadded base64 representation.
+    pub fn to_base64(&self) -> String {
+        base64_encode(self.0.to_bytes())
+    }
+
+    /// Convert the `Ed25519Signature` to a byte array.
+    pub fn to_bytes(&self) -> [u8; Self::LENGTH] {
+        self.0.to_bytes()
+    }
+}
+
+impl Display for Ed25519Signature {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        write!(f, "{}", self.to_base64())
+    }
+}
+
+impl std::fmt::Debug for Ed25519Signature {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        let s = format!("ed25519:{self}");
+        <str as std::fmt::Debug>::fmt(&s, f)
+    }
+}
+
+impl Clone for Ed25519Keypair {
+    fn clone(&self) -> Self {
+        let secret_key: SecretKeys = match &self.secret_key {
+            SecretKeys::Normal(k) => SecretKeys::Normal(k.clone()),
+            SecretKeys::Expanded(k) => SecretKeys::Expanded(k.clone()),
+        };
+
+        Self { secret_key, public_key: self.public_key }
+    }
+}
+
+impl From<Ed25519Keypair> for Ed25519KeypairPickle {
+    fn from(key: Ed25519Keypair) -> Self {
+        Self(key.secret_key)
+    }
+}
+
+impl From<SigningKey> for SecretKeys {
+    fn from(key: SigningKey) -> Self {
+        Self::Normal(Box::new(key))
+    }
+}
+
+impl From<ExpandedSecretKey> for SecretKeys {
+    fn from(key: ExpandedSecretKey) -> Self {
+        Self::Expanded(Box::new(key))
+    }
+}
+
+#[derive(Serialize, Deserialize)]
+#[serde(transparent)]
+pub struct Ed25519KeypairPickle(SecretKeys);
+
+impl From<Ed25519KeypairPickle> for Ed25519Keypair {
+    fn from(pickle: Ed25519KeypairPickle) -> Self {
+        let secret_key = pickle.0;
+        let public_key = secret_key.public_key();
+
+        Self { secret_key, public_key }
+    }
+}
+
\ No newline at end of file diff --git a/src/vodozemac/types/mod.rs.html b/src/vodozemac/types/mod.rs.html new file mode 100644 index 00000000..3bb831c0 --- /dev/null +++ b/src/vodozemac/types/mod.rs.html @@ -0,0 +1,123 @@ +mod.rs - source +
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+
// Copyright 2021 Denis Kasak, Damir Jelić
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+mod curve25519;
+mod ed25519;
+
+pub(crate) use curve25519::{Curve25519Keypair, Curve25519KeypairPickle};
+pub use curve25519::{Curve25519PublicKey, Curve25519SecretKey};
+pub use ed25519::{
+    Ed25519Keypair, Ed25519KeypairPickle, Ed25519PublicKey, Ed25519SecretKey, Ed25519Signature,
+    SignatureError,
+};
+use serde::{Deserialize, Serialize};
+use thiserror::Error;
+pub use x25519_dalek::SharedSecret;
+
+#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
+pub struct KeyId(pub(super) u64);
+
+impl From<KeyId> for String {
+    fn from(value: KeyId) -> String {
+        value.to_base64()
+    }
+}
+
+impl KeyId {
+    pub fn to_base64(self) -> String {
+        crate::utilities::base64_encode(self.0.to_be_bytes())
+    }
+}
+
+/// Error type describing failures that can happen when we try decode or use a
+/// cryptographic key.
+#[derive(Error, Debug)]
+pub enum KeyError {
+    #[error("Failed decoding a public key from base64: {}", .0)]
+    Base64Error(#[from] base64::DecodeError),
+    #[error(
+        "Failed decoding {key_type} key from base64: \
+        Invalid number of bytes for {key_type}, expected {expected_length}, got {length}."
+    )]
+    InvalidKeyLength { key_type: &'static str, expected_length: usize, length: usize },
+    #[error(transparent)]
+    Signature(#[from] SignatureError),
+    /// At least one of the keys did not have contributory behaviour and the
+    /// resulting shared secret would have been insecure.
+    #[error("At least one of the keys did not have contributory behaviour")]
+    NonContributoryKey,
+}
+
\ No newline at end of file diff --git a/src/vodozemac/utilities/libolm_compat.rs.html b/src/vodozemac/utilities/libolm_compat.rs.html new file mode 100644 index 00000000..e87886da --- /dev/null +++ b/src/vodozemac/utilities/libolm_compat.rs.html @@ -0,0 +1,211 @@ +libolm_compat.rs - source +
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+
// Copyright 2021 Damir Jelić
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+use std::io::Cursor;
+
+use matrix_pickle::{Decode, Encode};
+use zeroize::Zeroize;
+
+use super::{base64_decode, base64_encode};
+use crate::{cipher::Cipher, LibolmPickleError};
+
+/// Decrypt and decode the given pickle with the given pickle key.
+///
+/// # Arguments
+///
+/// * pickle - The base64-encoded and encrypted libolm pickle string
+/// * pickle_key - The key that was used to encrypt the libolm pickle
+/// * pickle_version - The expected version of the pickle. Unpickling will fail
+///   if the version in the pickle doesn't match this one.
+pub(crate) fn unpickle_libolm<P: Decode, T: TryFrom<P, Error = LibolmPickleError>>(
+    pickle: &str,
+    pickle_key: &[u8],
+    pickle_version: u32,
+) -> Result<T, LibolmPickleError> {
+    /// Fetch the pickle version from the given pickle source.
+    fn get_version(source: &[u8]) -> Option<u32> {
+        // Pickle versions are always u32 encoded as a fixed sized integer in
+        // big endian encoding.
+        let version = source.get(0..4)?;
+        Some(u32::from_be_bytes(version.try_into().ok()?))
+    }
+
+    // libolm pickles are always base64 encoded, so first try to decode.
+    let decoded = base64_decode(pickle)?;
+
+    // The pickle is always encrypted, even if a zero key is given. Try to
+    // decrypt next.
+    let cipher = Cipher::new_pickle(pickle_key);
+    let mut decrypted = cipher.decrypt_pickle(&decoded)?;
+
+    // A pickle starts with a version, which will decide how we need to decode.
+    // We only support the latest version so bail out if it isn't the expected
+    // pickle version.
+    let version = get_version(&decrypted).ok_or(LibolmPickleError::MissingVersion)?;
+
+    if version == pickle_version {
+        let mut cursor = Cursor::new(&decrypted);
+        let pickle = P::decode(&mut cursor)?;
+
+        decrypted.zeroize();
+        pickle.try_into()
+    } else {
+        Err(LibolmPickleError::Version(pickle_version, version))
+    }
+}
+
+pub(crate) fn pickle_libolm<P>(pickle: P, pickle_key: &[u8]) -> Result<String, LibolmPickleError>
+where
+    P: Encode,
+{
+    let mut encoded = pickle.encode_to_vec()?;
+
+    let cipher = Cipher::new_pickle(pickle_key);
+    let encrypted = cipher.encrypt_pickle(&encoded);
+    encoded.zeroize();
+
+    Ok(base64_encode(encrypted))
+}
+
+#[derive(Zeroize, Encode, Decode)]
+#[zeroize(drop)]
+pub(crate) struct LibolmEd25519Keypair {
+    pub public_key: [u8; 32],
+    #[secret]
+    pub private_key: Box<[u8; 64]>,
+}
+
+#[cfg(test)]
+mod test {
+    use super::*;
+
+    #[test]
+    fn encode_cycle() {
+        let key_pair =
+            LibolmEd25519Keypair { public_key: [10u8; 32], private_key: [20u8; 64].into() };
+
+        let encoded = key_pair.encode_to_vec().unwrap();
+        let decoded = LibolmEd25519Keypair::decode_from_slice(&encoded).unwrap();
+
+        assert_eq!(key_pair.public_key, decoded.public_key);
+        assert_eq!(key_pair.private_key, decoded.private_key);
+    }
+}
+
\ No newline at end of file diff --git a/src/vodozemac/utilities/mod.rs.html b/src/vodozemac/utilities/mod.rs.html new file mode 100644 index 00000000..f92fa2d7 --- /dev/null +++ b/src/vodozemac/utilities/mod.rs.html @@ -0,0 +1,353 @@ +mod.rs - source +
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+
// Copyright 2021 The Matrix.org Foundation C.I.C.
+// Copyright 2021 Damir Jelić, Denis Kasak
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#[cfg(feature = "libolm-compat")]
+mod libolm_compat;
+
+pub use base64::DecodeError;
+use base64::{
+    alphabet,
+    engine::{general_purpose, GeneralPurpose},
+    Engine,
+};
+#[cfg(feature = "libolm-compat")]
+pub(crate) use libolm_compat::{pickle_libolm, unpickle_libolm, LibolmEd25519Keypair};
+
+const STANDARD_NO_PAD: GeneralPurpose = GeneralPurpose::new(
+    &alphabet::STANDARD,
+    general_purpose::NO_PAD
+        .with_decode_padding_mode(base64::engine::DecodePaddingMode::Indifferent),
+);
+
+/// Decode the input as base64 with no padding.
+pub fn base64_decode(input: impl AsRef<[u8]>) -> Result<Vec<u8>, DecodeError> {
+    STANDARD_NO_PAD.decode(input)
+}
+
+/// Encode the input as base64 with no padding.
+pub fn base64_encode(input: impl AsRef<[u8]>) -> String {
+    STANDARD_NO_PAD.encode(input)
+}
+
+pub(crate) fn unpickle<T: for<'b> serde::Deserialize<'b>>(
+    ciphertext: &str,
+    pickle_key: &[u8; 32],
+) -> Result<T, crate::PickleError> {
+    use zeroize::Zeroize;
+
+    let cipher = crate::cipher::Cipher::new_pickle(pickle_key);
+    let decoded = base64_decode(ciphertext)?;
+    let mut plaintext = cipher.decrypt_pickle(&decoded)?;
+
+    let pickle = serde_json::from_slice(&plaintext)?;
+
+    plaintext.zeroize();
+
+    Ok(pickle)
+}
+
+pub(crate) fn pickle<T: serde::Serialize>(thing: &T, pickle_key: &[u8; 32]) -> String {
+    use zeroize::Zeroize;
+
+    let mut json = serde_json::to_vec(&thing).expect("Can't serialize a pickled object");
+    let cipher = crate::cipher::Cipher::new_pickle(pickle_key);
+
+    let ciphertext = cipher.encrypt_pickle(json.as_slice());
+
+    json.zeroize();
+
+    base64_encode(ciphertext)
+}
+
+pub(crate) fn extract_mac(slice: &[u8], truncated: bool) -> crate::cipher::MessageMac {
+    use crate::cipher::Mac;
+
+    if truncated {
+        let mac_slice = &slice[0..Mac::TRUNCATED_LEN];
+
+        let mut mac = [0u8; Mac::TRUNCATED_LEN];
+        mac.copy_from_slice(mac_slice);
+        mac.into()
+    } else {
+        let mac_slice = &slice[0..Mac::LENGTH];
+
+        let mut mac = [0u8; Mac::LENGTH];
+        mac.copy_from_slice(mac_slice);
+        Mac(mac).into()
+    }
+}
+
+// The integer encoding logic here has been taken from the integer-encoding[1]
+// crate and is under the MIT license.
+//
+// The MIT License (MIT)
+//
+// Copyright (c) 2016 Google Inc. (lewinb@google.com) -- though not an official
+// Google product or in any way related!
+// Copyright (c) 2018-2020 Lewin Bormann (lbo@spheniscida.de)
+//
+// [1]: https://github.com/dermesser/integer-encoding-rs
+pub(crate) trait VarInt {
+    fn to_var_int(self) -> Vec<u8>;
+}
+
+/// Most-significant byte, == 0x80
+const MSB: u8 = 0b1000_0000;
+
+/// How many bytes an integer uses when being encoded as a VarInt.
+#[inline]
+fn required_encoded_space_unsigned(mut v: u64) -> usize {
+    if v == 0 {
+        return 1;
+    }
+
+    let mut logcounter = 0;
+    while v > 0 {
+        logcounter += 1;
+        v >>= 7;
+    }
+    logcounter
+}
+
+impl VarInt for usize {
+    fn to_var_int(self) -> Vec<u8> {
+        (self as u64).to_var_int()
+    }
+}
+
+impl VarInt for u32 {
+    fn to_var_int(self) -> Vec<u8> {
+        (self as u64).to_var_int()
+    }
+}
+
+impl VarInt for u64 {
+    #[inline]
+    fn to_var_int(self) -> Vec<u8> {
+        let mut v = vec![0u8; required_encoded_space_unsigned(self)];
+
+        let mut n = self;
+        let mut i = 0;
+
+        while n >= 0x80 {
+            v[i] = MSB | (n as u8);
+            i += 1;
+            n >>= 7;
+        }
+
+        v[i] = n as u8;
+
+        v
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use super::*;
+
+    #[test]
+    fn test_b64_decode_any_padding() {
+        let encoded_with_padding = "VGhpc0lzQVRlc3Q=";
+        let encoded_without_padding = "VGhpc0lzQVRlc3Q";
+
+        let first = base64_decode(encoded_with_padding).expect("Should decode if there is padding");
+        let second =
+            base64_decode(encoded_without_padding).expect("Should decode if there is no padding");
+
+        assert_eq!(
+            first,
+            second,
+            "Decoding the same base64 string with and without padding should produce the same result"
+        )
+    }
+}
+
\ No newline at end of file diff --git a/static.files/COPYRIGHT-23e9bde6c69aea69.txt b/static.files/COPYRIGHT-23e9bde6c69aea69.txt new file mode 100644 index 00000000..1447df79 --- /dev/null +++ b/static.files/COPYRIGHT-23e9bde6c69aea69.txt @@ -0,0 +1,50 @@ +# REUSE-IgnoreStart + +These documentation pages include resources by third parties. This copyright +file applies only to those resources. The following third party resources are +included, and carry their own copyright notices and license terms: + +* Fira Sans (FiraSans-Regular.woff2, FiraSans-Medium.woff2): + + Copyright (c) 2014, Mozilla Foundation https://mozilla.org/ + with Reserved Font Name Fira Sans. + + Copyright (c) 2014, Telefonica S.A. + + Licensed under the SIL Open Font License, Version 1.1. + See FiraSans-LICENSE.txt. + +* rustdoc.css, main.js, and playpen.js: + + Copyright 2015 The Rust Developers. + Licensed under the Apache License, Version 2.0 (see LICENSE-APACHE.txt) or + the MIT license (LICENSE-MIT.txt) at your option. + +* normalize.css: + + Copyright (c) Nicolas Gallagher and Jonathan Neal. + Licensed under the MIT license (see LICENSE-MIT.txt). + +* Source Code Pro (SourceCodePro-Regular.ttf.woff2, + SourceCodePro-Semibold.ttf.woff2, SourceCodePro-It.ttf.woff2): + + Copyright 2010, 2012 Adobe Systems Incorporated (http://www.adobe.com/), + with Reserved Font Name 'Source'. All Rights Reserved. Source is a trademark + of Adobe Systems Incorporated in the United States and/or other countries. + + Licensed under the SIL Open Font License, Version 1.1. + See SourceCodePro-LICENSE.txt. + +* Source Serif 4 (SourceSerif4-Regular.ttf.woff2, SourceSerif4-Bold.ttf.woff2, + SourceSerif4-It.ttf.woff2): + + Copyright 2014-2021 Adobe (http://www.adobe.com/), with Reserved Font Name + 'Source'. All Rights Reserved. Source is a trademark of Adobe in the United + States and/or other countries. + + Licensed under the SIL Open Font License, Version 1.1. + See SourceSerif4-LICENSE.md. + +This copyright file is intended to be distributed with rustdoc output. + +# REUSE-IgnoreEnd diff --git a/static.files/FiraSans-LICENSE-db4b642586e02d97.txt b/static.files/FiraSans-LICENSE-db4b642586e02d97.txt new file mode 100644 index 00000000..d7e9c149 --- /dev/null +++ b/static.files/FiraSans-LICENSE-db4b642586e02d97.txt @@ -0,0 +1,98 @@ +// REUSE-IgnoreStart + +Digitized data copyright (c) 2012-2015, The Mozilla Foundation and Telefonica S.A. +with Reserved Font Name < Fira >, + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +http://scripts.sil.org/OFL + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. + +// REUSE-IgnoreEnd diff --git a/static.files/FiraSans-Medium-8f9a781e4970d388.woff2 b/static.files/FiraSans-Medium-8f9a781e4970d388.woff2 new file mode 100644 index 00000000..7a1e5fc5 Binary files /dev/null and b/static.files/FiraSans-Medium-8f9a781e4970d388.woff2 differ diff --git a/static.files/FiraSans-Regular-018c141bf0843ffd.woff2 b/static.files/FiraSans-Regular-018c141bf0843ffd.woff2 new file mode 100644 index 00000000..e766e06c Binary files /dev/null and b/static.files/FiraSans-Regular-018c141bf0843ffd.woff2 differ diff --git a/static.files/LICENSE-APACHE-b91fa81cba47b86a.txt b/static.files/LICENSE-APACHE-b91fa81cba47b86a.txt new file mode 100644 index 00000000..16fe87b0 --- /dev/null +++ b/static.files/LICENSE-APACHE-b91fa81cba47b86a.txt @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/static.files/LICENSE-MIT-65090b722b3f6c56.txt b/static.files/LICENSE-MIT-65090b722b3f6c56.txt new file mode 100644 index 00000000..31aa7938 --- /dev/null +++ b/static.files/LICENSE-MIT-65090b722b3f6c56.txt @@ -0,0 +1,23 @@ +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/static.files/NanumBarunGothic-0f09457c7a19b7c6.ttf.woff2 b/static.files/NanumBarunGothic-0f09457c7a19b7c6.ttf.woff2 new file mode 100644 index 00000000..1866ad4b Binary files /dev/null and b/static.files/NanumBarunGothic-0f09457c7a19b7c6.ttf.woff2 differ diff --git a/static.files/NanumBarunGothic-LICENSE-18c5adf4b52b4041.txt b/static.files/NanumBarunGothic-LICENSE-18c5adf4b52b4041.txt new file mode 100644 index 00000000..4b3edc29 --- /dev/null +++ b/static.files/NanumBarunGothic-LICENSE-18c5adf4b52b4041.txt @@ -0,0 +1,103 @@ +// REUSE-IgnoreStart + +Copyright (c) 2010, NAVER Corporation (https://www.navercorp.com/), + +with Reserved Font Name Nanum, Naver Nanum, NanumGothic, Naver NanumGothic, +NanumMyeongjo, Naver NanumMyeongjo, NanumBrush, Naver NanumBrush, NanumPen, +Naver NanumPen, Naver NanumGothicEco, NanumGothicEco, Naver NanumMyeongjoEco, +NanumMyeongjoEco, Naver NanumGothicLight, NanumGothicLight, NanumBarunGothic, +Naver NanumBarunGothic, NanumSquareRound, NanumBarunPen, MaruBuri + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +http://scripts.sil.org/OFL + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. + +// REUSE-IgnoreEnd diff --git a/static.files/SourceCodePro-It-1cc31594bf4f1f79.ttf.woff2 b/static.files/SourceCodePro-It-1cc31594bf4f1f79.ttf.woff2 new file mode 100644 index 00000000..462c34ef Binary files /dev/null and b/static.files/SourceCodePro-It-1cc31594bf4f1f79.ttf.woff2 differ diff --git a/static.files/SourceCodePro-LICENSE-d180d465a756484a.txt b/static.files/SourceCodePro-LICENSE-d180d465a756484a.txt new file mode 100644 index 00000000..0d2941e1 --- /dev/null +++ b/static.files/SourceCodePro-LICENSE-d180d465a756484a.txt @@ -0,0 +1,97 @@ +// REUSE-IgnoreStart + +Copyright 2010, 2012 Adobe Systems Incorporated (http://www.adobe.com/), with Reserved Font Name 'Source'. All Rights Reserved. Source is a trademark of Adobe Systems Incorporated in the United States and/or other countries. + +This Font Software is licensed under the SIL Open Font License, Version 1.1. + +This license is copied below, and is also available with a FAQ at: http://scripts.sil.org/OFL + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. + +// REUSE-IgnoreEnd diff --git a/static.files/SourceCodePro-Regular-562dcc5011b6de7d.ttf.woff2 b/static.files/SourceCodePro-Regular-562dcc5011b6de7d.ttf.woff2 new file mode 100644 index 00000000..10b558e0 Binary files /dev/null and b/static.files/SourceCodePro-Regular-562dcc5011b6de7d.ttf.woff2 differ diff --git a/static.files/SourceCodePro-Semibold-d899c5a5c4aeb14a.ttf.woff2 b/static.files/SourceCodePro-Semibold-d899c5a5c4aeb14a.ttf.woff2 new file mode 100644 index 00000000..5ec64eef Binary files /dev/null and b/static.files/SourceCodePro-Semibold-d899c5a5c4aeb14a.ttf.woff2 differ diff --git a/static.files/SourceSerif4-Bold-a2c9cd1067f8b328.ttf.woff2 b/static.files/SourceSerif4-Bold-a2c9cd1067f8b328.ttf.woff2 new file mode 100644 index 00000000..181a07f6 Binary files /dev/null and b/static.files/SourceSerif4-Bold-a2c9cd1067f8b328.ttf.woff2 differ diff --git a/static.files/SourceSerif4-It-acdfaf1a8af734b1.ttf.woff2 b/static.files/SourceSerif4-It-acdfaf1a8af734b1.ttf.woff2 new file mode 100644 index 00000000..2ae08a7b Binary files /dev/null and b/static.files/SourceSerif4-It-acdfaf1a8af734b1.ttf.woff2 differ diff --git a/static.files/SourceSerif4-LICENSE-3bb119e13b1258b7.md b/static.files/SourceSerif4-LICENSE-3bb119e13b1258b7.md new file mode 100644 index 00000000..175fa4f4 --- /dev/null +++ b/static.files/SourceSerif4-LICENSE-3bb119e13b1258b7.md @@ -0,0 +1,98 @@ + + +Copyright 2014-2021 Adobe (http://www.adobe.com/), with Reserved Font Name 'Source'. All Rights Reserved. Source is a trademark of Adobe in the United States and/or other countries. +Copyright 2014 - 2023 Adobe (http://www.adobe.com/), with Reserved Font Name ‘Source’. All Rights Reserved. Source is a trademark of Adobe in the United States and/or other countries. + +This Font Software is licensed under the SIL Open Font License, Version 1.1. + +This license is copied below, and is also available with a FAQ at: http://scripts.sil.org/OFL + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. + + diff --git a/static.files/SourceSerif4-Regular-46f98efaafac5295.ttf.woff2 b/static.files/SourceSerif4-Regular-46f98efaafac5295.ttf.woff2 new file mode 100644 index 00000000..0263fc30 Binary files /dev/null and b/static.files/SourceSerif4-Regular-46f98efaafac5295.ttf.woff2 differ diff --git a/static.files/clipboard-7571035ce49a181d.svg b/static.files/clipboard-7571035ce49a181d.svg new file mode 100644 index 00000000..8adbd996 --- /dev/null +++ b/static.files/clipboard-7571035ce49a181d.svg @@ -0,0 +1 @@ + diff --git a/static.files/favicon-16x16-8b506e7a72182f1c.png b/static.files/favicon-16x16-8b506e7a72182f1c.png new file mode 100644 index 00000000..ea4b45ca Binary files /dev/null and b/static.files/favicon-16x16-8b506e7a72182f1c.png differ diff --git a/static.files/favicon-2c020d218678b618.svg b/static.files/favicon-2c020d218678b618.svg new file mode 100644 index 00000000..8b34b511 --- /dev/null +++ b/static.files/favicon-2c020d218678b618.svg @@ -0,0 +1,24 @@ + + + + + diff --git a/static.files/favicon-32x32-422f7d1d52889060.png b/static.files/favicon-32x32-422f7d1d52889060.png new file mode 100644 index 00000000..69b8613c Binary files /dev/null and b/static.files/favicon-32x32-422f7d1d52889060.png differ diff --git a/static.files/main-48f368f3872407c8.js b/static.files/main-48f368f3872407c8.js new file mode 100644 index 00000000..987fae42 --- /dev/null +++ b/static.files/main-48f368f3872407c8.js @@ -0,0 +1,11 @@ +"use strict";window.RUSTDOC_TOOLTIP_HOVER_MS=300;window.RUSTDOC_TOOLTIP_HOVER_EXIT_MS=450;function resourcePath(basename,extension){return getVar("root-path")+basename+getVar("resource-suffix")+extension}function hideMain(){addClass(document.getElementById(MAIN_ID),"hidden")}function showMain(){removeClass(document.getElementById(MAIN_ID),"hidden")}function blurHandler(event,parentElem,hideCallback){if(!parentElem.contains(document.activeElement)&&!parentElem.contains(event.relatedTarget)){hideCallback()}}window.rootPath=getVar("root-path");window.currentCrate=getVar("current-crate");function setMobileTopbar(){const mobileTopbar=document.querySelector(".mobile-topbar");const locationTitle=document.querySelector(".sidebar h2.location");if(mobileTopbar){const mobileTitle=document.createElement("h2");mobileTitle.className="location";if(hasClass(document.querySelector(".rustdoc"),"crate")){mobileTitle.innerText=`Crate ${window.currentCrate}`}else if(locationTitle){mobileTitle.innerHTML=locationTitle.innerHTML}mobileTopbar.appendChild(mobileTitle)}}function getVirtualKey(ev){if("key"in ev&&typeof ev.key!=="undefined"){return ev.key}const c=ev.charCode||ev.keyCode;if(c===27){return"Escape"}return String.fromCharCode(c)}const MAIN_ID="main-content";const SETTINGS_BUTTON_ID="settings-menu";const ALTERNATIVE_DISPLAY_ID="alternative-display";const NOT_DISPLAYED_ID="not-displayed";const HELP_BUTTON_ID="help-button";function getSettingsButton(){return document.getElementById(SETTINGS_BUTTON_ID)}function getHelpButton(){return document.getElementById(HELP_BUTTON_ID)}function getNakedUrl(){return window.location.href.split("?")[0].split("#")[0]}function insertAfter(newNode,referenceNode){referenceNode.parentNode.insertBefore(newNode,referenceNode.nextSibling)}function getOrCreateSection(id,classes){let el=document.getElementById(id);if(!el){el=document.createElement("section");el.id=id;el.className=classes;insertAfter(el,document.getElementById(MAIN_ID))}return el}function getAlternativeDisplayElem(){return getOrCreateSection(ALTERNATIVE_DISPLAY_ID,"content hidden")}function getNotDisplayedElem(){return getOrCreateSection(NOT_DISPLAYED_ID,"hidden")}function switchDisplayedElement(elemToDisplay){const el=getAlternativeDisplayElem();if(el.children.length>0){getNotDisplayedElem().appendChild(el.firstElementChild)}if(elemToDisplay===null){addClass(el,"hidden");showMain();return}el.appendChild(elemToDisplay);hideMain();removeClass(el,"hidden")}function browserSupportsHistoryApi(){return window.history&&typeof window.history.pushState==="function"}function preLoadCss(cssUrl){const link=document.createElement("link");link.href=cssUrl;link.rel="preload";link.as="style";document.getElementsByTagName("head")[0].appendChild(link)}(function(){const isHelpPage=window.location.pathname.endsWith("/help.html");function loadScript(url){const script=document.createElement("script");script.src=url;document.head.append(script)}getSettingsButton().onclick=event=>{if(event.ctrlKey||event.altKey||event.metaKey){return}window.hideAllModals(false);addClass(getSettingsButton(),"rotate");event.preventDefault();loadScript(getVar("static-root-path")+getVar("settings-js"));setTimeout(()=>{const themes=getVar("themes").split(",");for(const theme of themes){if(theme!==""){preLoadCss(getVar("root-path")+theme+".css")}}},0)};window.searchState={loadingText:"Loading search results...",input:document.getElementsByClassName("search-input")[0],outputElement:()=>{let el=document.getElementById("search");if(!el){el=document.createElement("section");el.id="search";getNotDisplayedElem().appendChild(el)}return el},title:document.title,titleBeforeSearch:document.title,timeout:null,currentTab:0,focusedByTab:[null,null,null],clearInputTimeout:()=>{if(searchState.timeout!==null){clearTimeout(searchState.timeout);searchState.timeout=null}},isDisplayed:()=>searchState.outputElement().parentElement.id===ALTERNATIVE_DISPLAY_ID,focus:()=>{searchState.input.focus()},defocus:()=>{searchState.input.blur()},showResults:search=>{if(search===null||typeof search==="undefined"){search=searchState.outputElement()}switchDisplayedElement(search);searchState.mouseMovedAfterSearch=false;document.title=searchState.title},removeQueryParameters:()=>{document.title=searchState.titleBeforeSearch;if(browserSupportsHistoryApi()){history.replaceState(null,"",getNakedUrl()+window.location.hash)}},hideResults:()=>{switchDisplayedElement(null);searchState.removeQueryParameters()},getQueryStringParams:()=>{const params={};window.location.search.substring(1).split("&").map(s=>{const pair=s.split("=").map(x=>x.replace(/\+/g," "));params[decodeURIComponent(pair[0])]=typeof pair[1]==="undefined"?null:decodeURIComponent(pair[1])});return params},setup:()=>{const search_input=searchState.input;if(!searchState.input){return}let searchLoaded=false;function loadSearch(){if(!searchLoaded){searchLoaded=true;loadScript(getVar("static-root-path")+getVar("search-js"));loadScript(resourcePath("search-index",".js"))}}search_input.addEventListener("focus",()=>{search_input.origPlaceholder=search_input.placeholder;search_input.placeholder="Type your search here.";loadSearch()});if(search_input.value!==""){loadSearch()}const params=searchState.getQueryStringParams();if(params.search!==undefined){searchState.setLoadingSearch();loadSearch()}},setLoadingSearch:()=>{const search=searchState.outputElement();search.innerHTML="

"+searchState.loadingText+"

";searchState.showResults(search)},};const toggleAllDocsId="toggle-all-docs";let savedHash="";function handleHashes(ev){if(ev!==null&&searchState.isDisplayed()&&ev.newURL){switchDisplayedElement(null);const hash=ev.newURL.slice(ev.newURL.indexOf("#")+1);if(browserSupportsHistoryApi()){history.replaceState(null,"",getNakedUrl()+window.location.search+"#"+hash)}const elem=document.getElementById(hash);if(elem){elem.scrollIntoView()}}const pageId=window.location.hash.replace(/^#/,"");if(savedHash!==pageId){savedHash=pageId;if(pageId!==""){expandSection(pageId)}}if(savedHash.startsWith("impl-")){const splitAt=savedHash.indexOf("/");if(splitAt!==-1){const implId=savedHash.slice(0,splitAt);const assocId=savedHash.slice(splitAt+1);const implElem=document.getElementById(implId);if(implElem&&implElem.parentElement.tagName==="SUMMARY"&&implElem.parentElement.parentElement.tagName==="DETAILS"){onEachLazy(implElem.parentElement.parentElement.querySelectorAll(`[id^="${assocId}"]`),item=>{const numbered=/([^-]+)-([0-9]+)/.exec(item.id);if(item.id===assocId||(numbered&&numbered[1]===assocId)){openParentDetails(item);item.scrollIntoView();setTimeout(()=>{window.location.replace("#"+item.id)},0)}})}}}}function onHashChange(ev){hideSidebar();handleHashes(ev)}function openParentDetails(elem){while(elem){if(elem.tagName==="DETAILS"){elem.open=true}elem=elem.parentNode}}function expandSection(id){openParentDetails(document.getElementById(id))}function handleEscape(ev){searchState.clearInputTimeout();searchState.hideResults();ev.preventDefault();searchState.defocus();window.hideAllModals(true)}function handleShortcut(ev){const disableShortcuts=getSettingValue("disable-shortcuts")==="true";if(ev.ctrlKey||ev.altKey||ev.metaKey||disableShortcuts){return}if(document.activeElement.tagName==="INPUT"&&document.activeElement.type!=="checkbox"&&document.activeElement.type!=="radio"){switch(getVirtualKey(ev)){case"Escape":handleEscape(ev);break}}else{switch(getVirtualKey(ev)){case"Escape":handleEscape(ev);break;case"s":case"S":ev.preventDefault();searchState.focus();break;case"+":ev.preventDefault();expandAllDocs();break;case"-":ev.preventDefault();collapseAllDocs();break;case"?":showHelp();break;default:break}}}document.addEventListener("keypress",handleShortcut);document.addEventListener("keydown",handleShortcut);function addSidebarItems(){if(!window.SIDEBAR_ITEMS){return}const sidebar=document.getElementsByClassName("sidebar-elems")[0];function block(shortty,id,longty){const filtered=window.SIDEBAR_ITEMS[shortty];if(!filtered){return}const modpath=hasClass(document.querySelector(".rustdoc"),"mod")?"../":"";const h3=document.createElement("h3");h3.innerHTML=`${longty}`;const ul=document.createElement("ul");ul.className="block "+shortty;for(const name of filtered){let path;if(shortty==="mod"){path=`${modpath}${name}/index.html`}else{path=`${modpath}${shortty}.${name}.html`}let current_page=document.location.href.toString();if(current_page.endsWith("/")){current_page+="index.html"}const link=document.createElement("a");link.href=path;if(path===current_page){link.className="current"}link.textContent=name;const li=document.createElement("li");li.appendChild(link);ul.appendChild(li)}sidebar.appendChild(h3);sidebar.appendChild(ul)}if(sidebar){block("primitive","primitives","Primitive Types");block("mod","modules","Modules");block("macro","macros","Macros");block("struct","structs","Structs");block("enum","enums","Enums");block("constant","constants","Constants");block("static","static","Statics");block("trait","traits","Traits");block("fn","functions","Functions");block("type","types","Type Aliases");block("union","unions","Unions");block("foreigntype","foreign-types","Foreign Types");block("keyword","keywords","Keywords");block("opaque","opaque-types","Opaque Types");block("attr","attributes","Attribute Macros");block("derive","derives","Derive Macros");block("traitalias","trait-aliases","Trait Aliases")}}window.register_implementors=imp=>{const implementors=document.getElementById("implementors-list");const synthetic_implementors=document.getElementById("synthetic-implementors-list");const inlined_types=new Set();const TEXT_IDX=0;const SYNTHETIC_IDX=1;const TYPES_IDX=2;if(synthetic_implementors){onEachLazy(synthetic_implementors.getElementsByClassName("impl"),el=>{const aliases=el.getAttribute("data-aliases");if(!aliases){return}aliases.split(",").forEach(alias=>{inlined_types.add(alias)})})}let currentNbImpls=implementors.getElementsByClassName("impl").length;const traitName=document.querySelector(".main-heading h1 > .trait").textContent;const baseIdName="impl-"+traitName+"-";const libs=Object.getOwnPropertyNames(imp);const script=document.querySelector("script[data-ignore-extern-crates]");const ignoreExternCrates=new Set((script?script.getAttribute("data-ignore-extern-crates"):"").split(","));for(const lib of libs){if(lib===window.currentCrate||ignoreExternCrates.has(lib)){continue}const structs=imp[lib];struct_loop:for(const struct of structs){const list=struct[SYNTHETIC_IDX]?synthetic_implementors:implementors;if(struct[SYNTHETIC_IDX]){for(const struct_type of struct[TYPES_IDX]){if(inlined_types.has(struct_type)){continue struct_loop}inlined_types.add(struct_type)}}const code=document.createElement("h3");code.innerHTML=struct[TEXT_IDX];addClass(code,"code-header");onEachLazy(code.getElementsByTagName("a"),elem=>{const href=elem.getAttribute("href");if(href&&!href.startsWith("#")&&!/^(?:[a-z+]+:)?\/\//.test(href)){elem.setAttribute("href",window.rootPath+href)}});const currentId=baseIdName+currentNbImpls;const anchor=document.createElement("a");anchor.href="#"+currentId;addClass(anchor,"anchor");const display=document.createElement("div");display.id=currentId;addClass(display,"impl");display.appendChild(anchor);display.appendChild(code);list.appendChild(display);currentNbImpls+=1}}};if(window.pending_implementors){window.register_implementors(window.pending_implementors)}window.register_type_impls=imp=>{if(!imp||!imp[window.currentCrate]){return}window.pending_type_impls=null;const idMap=new Map();let implementations=document.getElementById("implementations-list");let trait_implementations=document.getElementById("trait-implementations-list");let trait_implementations_header=document.getElementById("trait-implementations");const script=document.querySelector("script[data-self-path]");const selfPath=script?script.getAttribute("data-self-path"):null;const mainContent=document.querySelector("#main-content");const sidebarSection=document.querySelector(".sidebar section");let methods=document.querySelector(".sidebar .block.method");let associatedTypes=document.querySelector(".sidebar .block.associatedtype");let associatedConstants=document.querySelector(".sidebar .block.associatedconstant");let sidebarTraitList=document.querySelector(".sidebar .block.trait-implementation");for(const impList of imp[window.currentCrate]){const types=impList.slice(2);const text=impList[0];const isTrait=impList[1]!==0;const traitName=impList[1];if(types.indexOf(selfPath)===-1){continue}let outputList=isTrait?trait_implementations:implementations;if(outputList===null){const outputListName=isTrait?"Trait Implementations":"Implementations";const outputListId=isTrait?"trait-implementations-list":"implementations-list";const outputListHeaderId=isTrait?"trait-implementations":"implementations";const outputListHeader=document.createElement("h2");outputListHeader.id=outputListHeaderId;outputListHeader.innerText=outputListName;outputList=document.createElement("div");outputList.id=outputListId;if(isTrait){const link=document.createElement("a");link.href=`#${outputListHeaderId}`;link.innerText="Trait Implementations";const h=document.createElement("h3");h.appendChild(link);trait_implementations=outputList;trait_implementations_header=outputListHeader;sidebarSection.appendChild(h);sidebarTraitList=document.createElement("ul");sidebarTraitList.className="block trait-implementation";sidebarSection.appendChild(sidebarTraitList);mainContent.appendChild(outputListHeader);mainContent.appendChild(outputList)}else{implementations=outputList;if(trait_implementations){mainContent.insertBefore(outputListHeader,trait_implementations_header);mainContent.insertBefore(outputList,trait_implementations_header)}else{const mainContent=document.querySelector("#main-content");mainContent.appendChild(outputListHeader);mainContent.appendChild(outputList)}}}const template=document.createElement("template");template.innerHTML=text;onEachLazy(template.content.querySelectorAll("a"),elem=>{const href=elem.getAttribute("href");if(href&&!href.startsWith("#")&&!/^(?:[a-z+]+:)?\/\//.test(href)){elem.setAttribute("href",window.rootPath+href)}});onEachLazy(template.content.querySelectorAll("[id]"),el=>{let i=0;if(idMap.has(el.id)){i=idMap.get(el.id)}else if(document.getElementById(el.id)){i=1;while(document.getElementById(`${el.id}-${2 * i}`)){i=2*i}while(document.getElementById(`${el.id}-${i}`)){i+=1}}if(i!==0){const oldHref=`#${el.id}`;const newHref=`#${el.id}-${i}`;el.id=`${el.id}-${i}`;onEachLazy(template.content.querySelectorAll("a[href]"),link=>{if(link.getAttribute("href")===oldHref){link.href=newHref}})}idMap.set(el.id,i+1)});const templateAssocItems=template.content.querySelectorAll("section.tymethod, "+"section.method, section.associatedtype, section.associatedconstant");if(isTrait){const li=document.createElement("li");const a=document.createElement("a");a.href=`#${template.content.querySelector(".impl").id}`;a.textContent=traitName;li.appendChild(a);sidebarTraitList.append(li)}else{onEachLazy(templateAssocItems,item=>{let block=hasClass(item,"associatedtype")?associatedTypes:(hasClass(item,"associatedconstant")?associatedConstants:(methods));if(!block){const blockTitle=hasClass(item,"associatedtype")?"Associated Types":(hasClass(item,"associatedconstant")?"Associated Constants":("Methods"));const blockClass=hasClass(item,"associatedtype")?"associatedtype":(hasClass(item,"associatedconstant")?"associatedconstant":("method"));const blockHeader=document.createElement("h3");const blockLink=document.createElement("a");blockLink.href="#implementations";blockLink.innerText=blockTitle;blockHeader.appendChild(blockLink);block=document.createElement("ul");block.className=`block ${blockClass}`;const insertionReference=methods||sidebarTraitList;if(insertionReference){const insertionReferenceH=insertionReference.previousElementSibling;sidebarSection.insertBefore(blockHeader,insertionReferenceH);sidebarSection.insertBefore(block,insertionReferenceH)}else{sidebarSection.appendChild(blockHeader);sidebarSection.appendChild(block)}if(hasClass(item,"associatedtype")){associatedTypes=block}else if(hasClass(item,"associatedconstant")){associatedConstants=block}else{methods=block}}const li=document.createElement("li");const a=document.createElement("a");a.innerText=item.id.split("-")[0].split(".")[1];a.href=`#${item.id}`;li.appendChild(a);block.appendChild(li)})}outputList.appendChild(template.content)}for(const list of[methods,associatedTypes,associatedConstants,sidebarTraitList]){if(!list){continue}const newChildren=Array.prototype.slice.call(list.children);newChildren.sort((a,b)=>{const aI=a.innerText;const bI=b.innerText;return aIbI?1:0});list.replaceChildren(...newChildren)}};if(window.pending_type_impls){window.register_type_impls(window.pending_type_impls)}function addSidebarCrates(){if(!window.ALL_CRATES){return}const sidebarElems=document.getElementsByClassName("sidebar-elems")[0];if(!sidebarElems){return}const h3=document.createElement("h3");h3.innerHTML="Crates";const ul=document.createElement("ul");ul.className="block crate";for(const crate of window.ALL_CRATES){const link=document.createElement("a");link.href=window.rootPath+crate+"/index.html";link.textContent=crate;const li=document.createElement("li");if(window.rootPath!=="./"&&crate===window.currentCrate){li.className="current"}li.appendChild(link);ul.appendChild(li)}sidebarElems.appendChild(h3);sidebarElems.appendChild(ul)}function expandAllDocs(){const innerToggle=document.getElementById(toggleAllDocsId);removeClass(innerToggle,"will-expand");onEachLazy(document.getElementsByClassName("toggle"),e=>{if(!hasClass(e,"type-contents-toggle")&&!hasClass(e,"more-examples-toggle")){e.open=true}});innerToggle.title="collapse all docs";innerToggle.children[0].innerText="\u2212"}function collapseAllDocs(){const innerToggle=document.getElementById(toggleAllDocsId);addClass(innerToggle,"will-expand");onEachLazy(document.getElementsByClassName("toggle"),e=>{if(e.parentNode.id!=="implementations-list"||(!hasClass(e,"implementors-toggle")&&!hasClass(e,"type-contents-toggle"))){e.open=false}});innerToggle.title="expand all docs";innerToggle.children[0].innerText="+"}function toggleAllDocs(){const innerToggle=document.getElementById(toggleAllDocsId);if(!innerToggle){return}if(hasClass(innerToggle,"will-expand")){expandAllDocs()}else{collapseAllDocs()}}(function(){const toggles=document.getElementById(toggleAllDocsId);if(toggles){toggles.onclick=toggleAllDocs}const hideMethodDocs=getSettingValue("auto-hide-method-docs")==="true";const hideImplementations=getSettingValue("auto-hide-trait-implementations")==="true";const hideLargeItemContents=getSettingValue("auto-hide-large-items")!=="false";function setImplementorsTogglesOpen(id,open){const list=document.getElementById(id);if(list!==null){onEachLazy(list.getElementsByClassName("implementors-toggle"),e=>{e.open=open})}}if(hideImplementations){setImplementorsTogglesOpen("trait-implementations-list",false);setImplementorsTogglesOpen("blanket-implementations-list",false)}onEachLazy(document.getElementsByClassName("toggle"),e=>{if(!hideLargeItemContents&&hasClass(e,"type-contents-toggle")){e.open=true}if(hideMethodDocs&&hasClass(e,"method-toggle")){e.open=false}})}());window.rustdoc_add_line_numbers_to_examples=()=>{onEachLazy(document.getElementsByClassName("rust-example-rendered"),x=>{const parent=x.parentNode;const line_numbers=parent.querySelectorAll(".example-line-numbers");if(line_numbers.length>0){return}const count=x.textContent.split("\n").length;const elems=[];for(let i=0;i{onEachLazy(document.getElementsByClassName("rust-example-rendered"),x=>{const parent=x.parentNode;const line_numbers=parent.querySelectorAll(".example-line-numbers");for(const node of line_numbers){parent.removeChild(node)}})};if(getSettingValue("line-numbers")==="true"){window.rustdoc_add_line_numbers_to_examples()}function showSidebar(){window.hideAllModals(false);const sidebar=document.getElementsByClassName("sidebar")[0];addClass(sidebar,"shown")}function hideSidebar(){const sidebar=document.getElementsByClassName("sidebar")[0];removeClass(sidebar,"shown")}window.addEventListener("resize",()=>{if(window.CURRENT_TOOLTIP_ELEMENT){const base=window.CURRENT_TOOLTIP_ELEMENT.TOOLTIP_BASE;const force_visible=base.TOOLTIP_FORCE_VISIBLE;hideTooltip(false);if(force_visible){showTooltip(base);base.TOOLTIP_FORCE_VISIBLE=true}}});const mainElem=document.getElementById(MAIN_ID);if(mainElem){mainElem.addEventListener("click",hideSidebar)}onEachLazy(document.querySelectorAll("a[href^='#']"),el=>{el.addEventListener("click",()=>{expandSection(el.hash.slice(1));hideSidebar()})});onEachLazy(document.querySelectorAll(".toggle > summary:not(.hideme)"),el=>{el.addEventListener("click",e=>{if(e.target.tagName!=="SUMMARY"&&e.target.tagName!=="A"){e.preventDefault()}})});function showTooltip(e){const notable_ty=e.getAttribute("data-notable-ty");if(!window.NOTABLE_TRAITS&¬able_ty){const data=document.getElementById("notable-traits-data");if(data){window.NOTABLE_TRAITS=JSON.parse(data.innerText)}else{throw new Error("showTooltip() called with notable without any notable traits!")}}if(window.CURRENT_TOOLTIP_ELEMENT&&window.CURRENT_TOOLTIP_ELEMENT.TOOLTIP_BASE===e){clearTooltipHoverTimeout(window.CURRENT_TOOLTIP_ELEMENT);return}window.hideAllModals(false);const wrapper=document.createElement("div");if(notable_ty){wrapper.innerHTML="
"+window.NOTABLE_TRAITS[notable_ty]+"
"}else{if(e.getAttribute("title")!==null){e.setAttribute("data-title",e.getAttribute("title"));e.removeAttribute("title")}if(e.getAttribute("data-title")!==null){const titleContent=document.createElement("div");titleContent.className="content";titleContent.appendChild(document.createTextNode(e.getAttribute("data-title")));wrapper.appendChild(titleContent)}}wrapper.className="tooltip popover";const focusCatcher=document.createElement("div");focusCatcher.setAttribute("tabindex","0");focusCatcher.onfocus=hideTooltip;wrapper.appendChild(focusCatcher);const pos=e.getBoundingClientRect();wrapper.style.top=(pos.top+window.scrollY+pos.height)+"px";wrapper.style.left=0;wrapper.style.right="auto";wrapper.style.visibility="hidden";const body=document.getElementsByTagName("body")[0];body.appendChild(wrapper);const wrapperPos=wrapper.getBoundingClientRect();const finalPos=pos.left+window.scrollX-wrapperPos.width+24;if(finalPos>0){wrapper.style.left=finalPos+"px"}else{wrapper.style.setProperty("--popover-arrow-offset",(wrapperPos.right-pos.right+4)+"px")}wrapper.style.visibility="";window.CURRENT_TOOLTIP_ELEMENT=wrapper;window.CURRENT_TOOLTIP_ELEMENT.TOOLTIP_BASE=e;clearTooltipHoverTimeout(window.CURRENT_TOOLTIP_ELEMENT);wrapper.onpointerenter=ev=>{if(ev.pointerType!=="mouse"){return}clearTooltipHoverTimeout(e)};wrapper.onpointerleave=ev=>{if(ev.pointerType!=="mouse"){return}if(!e.TOOLTIP_FORCE_VISIBLE&&!e.contains(ev.relatedTarget)){setTooltipHoverTimeout(e,false);addClass(wrapper,"fade-out")}}}function setTooltipHoverTimeout(element,show){clearTooltipHoverTimeout(element);if(!show&&!window.CURRENT_TOOLTIP_ELEMENT){return}if(show&&window.CURRENT_TOOLTIP_ELEMENT){return}if(window.CURRENT_TOOLTIP_ELEMENT&&window.CURRENT_TOOLTIP_ELEMENT.TOOLTIP_BASE!==element){return}element.TOOLTIP_HOVER_TIMEOUT=setTimeout(()=>{if(show){showTooltip(element)}else if(!element.TOOLTIP_FORCE_VISIBLE){hideTooltip(false)}},show?window.RUSTDOC_TOOLTIP_HOVER_MS:window.RUSTDOC_TOOLTIP_HOVER_EXIT_MS)}function clearTooltipHoverTimeout(element){if(element.TOOLTIP_HOVER_TIMEOUT!==undefined){removeClass(window.CURRENT_TOOLTIP_ELEMENT,"fade-out");clearTimeout(element.TOOLTIP_HOVER_TIMEOUT);delete element.TOOLTIP_HOVER_TIMEOUT}}function tooltipBlurHandler(event){if(window.CURRENT_TOOLTIP_ELEMENT&&!window.CURRENT_TOOLTIP_ELEMENT.contains(document.activeElement)&&!window.CURRENT_TOOLTIP_ELEMENT.contains(event.relatedTarget)&&!window.CURRENT_TOOLTIP_ELEMENT.TOOLTIP_BASE.contains(document.activeElement)&&!window.CURRENT_TOOLTIP_ELEMENT.TOOLTIP_BASE.contains(event.relatedTarget)){setTimeout(()=>hideTooltip(false),0)}}function hideTooltip(focus){if(window.CURRENT_TOOLTIP_ELEMENT){if(window.CURRENT_TOOLTIP_ELEMENT.TOOLTIP_BASE.TOOLTIP_FORCE_VISIBLE){if(focus){window.CURRENT_TOOLTIP_ELEMENT.TOOLTIP_BASE.focus()}window.CURRENT_TOOLTIP_ELEMENT.TOOLTIP_BASE.TOOLTIP_FORCE_VISIBLE=false}const body=document.getElementsByTagName("body")[0];body.removeChild(window.CURRENT_TOOLTIP_ELEMENT);clearTooltipHoverTimeout(window.CURRENT_TOOLTIP_ELEMENT);window.CURRENT_TOOLTIP_ELEMENT=null}}onEachLazy(document.getElementsByClassName("tooltip"),e=>{e.onclick=()=>{e.TOOLTIP_FORCE_VISIBLE=e.TOOLTIP_FORCE_VISIBLE?false:true;if(window.CURRENT_TOOLTIP_ELEMENT&&!e.TOOLTIP_FORCE_VISIBLE){hideTooltip(true)}else{showTooltip(e);window.CURRENT_TOOLTIP_ELEMENT.setAttribute("tabindex","0");window.CURRENT_TOOLTIP_ELEMENT.focus();window.CURRENT_TOOLTIP_ELEMENT.onblur=tooltipBlurHandler}return false};e.onpointerenter=ev=>{if(ev.pointerType!=="mouse"){return}setTooltipHoverTimeout(e,true)};e.onpointermove=ev=>{if(ev.pointerType!=="mouse"){return}setTooltipHoverTimeout(e,true)};e.onpointerleave=ev=>{if(ev.pointerType!=="mouse"){return}if(!e.TOOLTIP_FORCE_VISIBLE&&window.CURRENT_TOOLTIP_ELEMENT&&!window.CURRENT_TOOLTIP_ELEMENT.contains(ev.relatedTarget)){setTooltipHoverTimeout(e,false);addClass(window.CURRENT_TOOLTIP_ELEMENT,"fade-out")}}});const sidebar_menu_toggle=document.getElementsByClassName("sidebar-menu-toggle")[0];if(sidebar_menu_toggle){sidebar_menu_toggle.addEventListener("click",()=>{const sidebar=document.getElementsByClassName("sidebar")[0];if(!hasClass(sidebar,"shown")){showSidebar()}else{hideSidebar()}})}function helpBlurHandler(event){blurHandler(event,getHelpButton(),window.hidePopoverMenus)}function buildHelpMenu(){const book_info=document.createElement("span");const channel=getVar("channel");book_info.className="top";book_info.innerHTML=`You can find more information in \ +the rustdoc book.`;const shortcuts=[["?","Show this help dialog"],["S","Focus the search field"],["↑","Move up in search results"],["↓","Move down in search results"],["← / →","Switch result tab (when results focused)"],["⏎","Go to active search result"],["+","Expand all sections"],["-","Collapse all sections"],].map(x=>"
"+x[0].split(" ").map((y,index)=>((index&1)===0?""+y+"":" "+y+" ")).join("")+"
"+x[1]+"
").join("");const div_shortcuts=document.createElement("div");addClass(div_shortcuts,"shortcuts");div_shortcuts.innerHTML="

Keyboard Shortcuts

"+shortcuts+"
";const infos=[`For a full list of all search features, take a look here.`,"Prefix searches with a type followed by a colon (e.g., fn:) to \ + restrict the search to a given item kind.","Accepted kinds are: fn, mod, struct, \ + enum, trait, type, macro, \ + and const.","Search functions by type signature (e.g., vec -> usize or \ + -> vec or String, enum:Cow -> bool)","You can look for items with an exact name by putting double quotes around \ + your request: \"string\"","Look for functions that accept or return \ + slices and \ + arrays by writing \ + square brackets (e.g., -> [u8] or [] -> Option)","Look for items inside another one by searching for a path: vec::Vec",].map(x=>"

"+x+"

").join("");const div_infos=document.createElement("div");addClass(div_infos,"infos");div_infos.innerHTML="

Search Tricks

"+infos;const rustdoc_version=document.createElement("span");rustdoc_version.className="bottom";const rustdoc_version_code=document.createElement("code");rustdoc_version_code.innerText="rustdoc "+getVar("rustdoc-version");rustdoc_version.appendChild(rustdoc_version_code);const container=document.createElement("div");if(!isHelpPage){container.className="popover"}container.id="help";container.style.display="none";const side_by_side=document.createElement("div");side_by_side.className="side-by-side";side_by_side.appendChild(div_shortcuts);side_by_side.appendChild(div_infos);container.appendChild(book_info);container.appendChild(side_by_side);container.appendChild(rustdoc_version);if(isHelpPage){const help_section=document.createElement("section");help_section.appendChild(container);document.getElementById("main-content").appendChild(help_section);container.style.display="block"}else{const help_button=getHelpButton();help_button.appendChild(container);container.onblur=helpBlurHandler;help_button.onblur=helpBlurHandler;help_button.children[0].onblur=helpBlurHandler}return container}window.hideAllModals=switchFocus=>{hideSidebar();window.hidePopoverMenus();hideTooltip(switchFocus)};window.hidePopoverMenus=()=>{onEachLazy(document.querySelectorAll(".search-form .popover"),elem=>{elem.style.display="none"})};function getHelpMenu(buildNeeded){let menu=getHelpButton().querySelector(".popover");if(!menu&&buildNeeded){menu=buildHelpMenu()}return menu}function showHelp(){getHelpButton().querySelector("a").focus();const menu=getHelpMenu(true);if(menu.style.display==="none"){window.hideAllModals();menu.style.display=""}}if(isHelpPage){showHelp();document.querySelector(`#${HELP_BUTTON_ID} > a`).addEventListener("click",event=>{const target=event.target;if(target.tagName!=="A"||target.parentElement.id!==HELP_BUTTON_ID||event.ctrlKey||event.altKey||event.metaKey){return}event.preventDefault()})}else{document.querySelector(`#${HELP_BUTTON_ID} > a`).addEventListener("click",event=>{const target=event.target;if(target.tagName!=="A"||target.parentElement.id!==HELP_BUTTON_ID||event.ctrlKey||event.altKey||event.metaKey){return}event.preventDefault();const menu=getHelpMenu(true);const shouldShowHelp=menu.style.display==="none";if(shouldShowHelp){showHelp()}else{window.hidePopoverMenus()}})}setMobileTopbar();addSidebarItems();addSidebarCrates();onHashChange(null);window.addEventListener("hashchange",onHashChange);searchState.setup()}());(function(){const SIDEBAR_MIN=100;const SIDEBAR_MAX=500;const RUSTDOC_MOBILE_BREAKPOINT=700;const BODY_MIN=400;const SIDEBAR_VANISH_THRESHOLD=SIDEBAR_MIN/2;const sidebarButton=document.getElementById("sidebar-button");if(sidebarButton){sidebarButton.addEventListener("click",e=>{removeClass(document.documentElement,"hide-sidebar");updateLocalStorage("hide-sidebar","false");if(document.querySelector(".rustdoc.src")){window.rustdocToggleSrcSidebar()}e.preventDefault()})}let currentPointerId=null;let desiredSidebarSize=null;let pendingSidebarResizingFrame=false;const resizer=document.querySelector(".sidebar-resizer");const sidebar=document.querySelector(".sidebar");if(!resizer||!sidebar){return}const isSrcPage=hasClass(document.body,"src");function hideSidebar(){if(isSrcPage){window.rustdocCloseSourceSidebar();updateLocalStorage("src-sidebar-width",null);document.documentElement.style.removeProperty("--src-sidebar-width");sidebar.style.removeProperty("--src-sidebar-width");resizer.style.removeProperty("--src-sidebar-width")}else{addClass(document.documentElement,"hide-sidebar");updateLocalStorage("hide-sidebar","true");updateLocalStorage("desktop-sidebar-width",null);document.documentElement.style.removeProperty("--desktop-sidebar-width");sidebar.style.removeProperty("--desktop-sidebar-width");resizer.style.removeProperty("--desktop-sidebar-width")}}function showSidebar(){if(isSrcPage){window.rustdocShowSourceSidebar()}else{removeClass(document.documentElement,"hide-sidebar");updateLocalStorage("hide-sidebar","false")}}function changeSidebarSize(size){if(isSrcPage){updateLocalStorage("src-sidebar-width",size);sidebar.style.setProperty("--src-sidebar-width",size+"px");resizer.style.setProperty("--src-sidebar-width",size+"px")}else{updateLocalStorage("desktop-sidebar-width",size);sidebar.style.setProperty("--desktop-sidebar-width",size+"px");resizer.style.setProperty("--desktop-sidebar-width",size+"px")}}function isSidebarHidden(){return isSrcPage?!hasClass(document.documentElement,"src-sidebar-expanded"):hasClass(document.documentElement,"hide-sidebar")}function resize(e){if(currentPointerId===null||currentPointerId!==e.pointerId){return}e.preventDefault();const pos=e.clientX-3;if(pos=SIDEBAR_MIN){if(isSidebarHidden()){showSidebar()}const constrainedPos=Math.min(pos,window.innerWidth-BODY_MIN,SIDEBAR_MAX);changeSidebarSize(constrainedPos);desiredSidebarSize=constrainedPos;if(pendingSidebarResizingFrame!==false){clearTimeout(pendingSidebarResizingFrame)}pendingSidebarResizingFrame=setTimeout(()=>{if(currentPointerId===null||pendingSidebarResizingFrame===false){return}pendingSidebarResizingFrame=false;document.documentElement.style.setProperty("--resizing-sidebar-width",desiredSidebarSize+"px")},100)}}window.addEventListener("resize",()=>{if(window.innerWidth=(window.innerWidth-BODY_MIN)){changeSidebarSize(window.innerWidth-BODY_MIN)}else if(desiredSidebarSize!==null&&desiredSidebarSize>SIDEBAR_MIN){changeSidebarSize(desiredSidebarSize)}});function stopResize(e){if(currentPointerId===null){return}if(e){e.preventDefault()}desiredSidebarSize=sidebar.getBoundingClientRect().width;removeClass(resizer,"active");window.removeEventListener("pointermove",resize,false);window.removeEventListener("pointerup",stopResize,false);removeClass(document.documentElement,"sidebar-resizing");document.documentElement.style.removeProperty("--resizing-sidebar-width");if(resizer.releasePointerCapture){resizer.releasePointerCapture(currentPointerId);currentPointerId=null}}function initResize(e){if(currentPointerId!==null||e.altKey||e.ctrlKey||e.metaKey||e.button!==0){return}if(resizer.setPointerCapture){resizer.setPointerCapture(e.pointerId);if(!resizer.hasPointerCapture(e.pointerId)){resizer.releasePointerCapture(e.pointerId);return}currentPointerId=e.pointerId}window.hideAllModals(false);e.preventDefault();window.addEventListener("pointermove",resize,false);window.addEventListener("pointercancel",stopResize,false);window.addEventListener("pointerup",stopResize,false);addClass(resizer,"active");addClass(document.documentElement,"sidebar-resizing");const pos=e.clientX-sidebar.offsetLeft-3;document.documentElement.style.setProperty("--resizing-sidebar-width",pos+"px");desiredSidebarSize=null}resizer.addEventListener("pointerdown",initResize,false)}());(function(){let reset_button_timeout=null;const but=document.getElementById("copy-path");if(!but){return}but.onclick=()=>{const parent=but.parentElement;const path=[];onEach(parent.childNodes,child=>{if(child.tagName==="A"){path.push(child.textContent)}});const el=document.createElement("textarea");el.value=path.join("::");el.setAttribute("readonly","");el.style.position="absolute";el.style.left="-9999px";document.body.appendChild(el);el.select();document.execCommand("copy");document.body.removeChild(el);but.children[0].style.display="none";let tmp;if(but.childNodes.length<2){tmp=document.createTextNode("✓");but.appendChild(tmp)}else{onEachLazy(but.childNodes,e=>{if(e.nodeType===Node.TEXT_NODE){tmp=e;return true}});tmp.textContent="✓"}if(reset_button_timeout!==null){window.clearTimeout(reset_button_timeout)}function reset_button(){tmp.textContent="";reset_button_timeout=null;but.children[0].style.display=""}reset_button_timeout=window.setTimeout(reset_button,1000)}}()) \ No newline at end of file diff --git a/static.files/normalize-76eba96aa4d2e634.css b/static.files/normalize-76eba96aa4d2e634.css new file mode 100644 index 00000000..469959f1 --- /dev/null +++ b/static.files/normalize-76eba96aa4d2e634.css @@ -0,0 +1,2 @@ + /*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */ +html{line-height:1.15;-webkit-text-size-adjust:100%}body{margin:0}main{display:block}h1{font-size:2em;margin:0.67em 0}hr{box-sizing:content-box;height:0;overflow:visible}pre{font-family:monospace,monospace;font-size:1em}a{background-color:transparent}abbr[title]{border-bottom:none;text-decoration:underline;text-decoration:underline dotted}b,strong{font-weight:bolder}code,kbd,samp{font-family:monospace,monospace;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-0.25em}sup{top:-0.5em}img{border-style:none}button,input,optgroup,select,textarea{font-family:inherit;font-size:100%;line-height:1.15;margin:0}button,input{overflow:visible}button,select{text-transform:none}[type="button"],[type="reset"],[type="submit"],button{-webkit-appearance:button}[type="button"]::-moz-focus-inner,[type="reset"]::-moz-focus-inner,[type="submit"]::-moz-focus-inner,button::-moz-focus-inner{border-style:none;padding:0}[type="button"]:-moz-focusring,[type="reset"]:-moz-focusring,[type="submit"]:-moz-focusring,button:-moz-focusring{outline:1px dotted ButtonText}fieldset{padding:0.35em 0.75em 0.625em}legend{box-sizing:border-box;color:inherit;display:table;max-width:100%;padding:0;white-space:normal}progress{vertical-align:baseline}textarea{overflow:auto}[type="checkbox"],[type="radio"]{box-sizing:border-box;padding:0}[type="number"]::-webkit-inner-spin-button,[type="number"]::-webkit-outer-spin-button{height:auto}[type="search"]{-webkit-appearance:textfield;outline-offset:-2px}[type="search"]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}details{display:block}summary{display:list-item}template{display:none}[hidden]{display:none} \ No newline at end of file diff --git a/static.files/noscript-04d5337699b92874.css b/static.files/noscript-04d5337699b92874.css new file mode 100644 index 00000000..fbd55f57 --- /dev/null +++ b/static.files/noscript-04d5337699b92874.css @@ -0,0 +1 @@ + #main-content .attributes{margin-left:0 !important;}#copy-path,#sidebar-button,.sidebar-resizer{display:none !important;}nav.sub{display:none;}.src .sidebar{display:none;}.notable-traits{display:none;}:root{--main-background-color:white;--main-color:black;--settings-input-color:#2196f3;--settings-input-border-color:#717171;--settings-button-color:#000;--settings-button-border-focus:#717171;--sidebar-background-color:#f5f5f5;--sidebar-background-color-hover:#e0e0e0;--code-block-background-color:#f5f5f5;--scrollbar-track-background-color:#dcdcdc;--scrollbar-thumb-background-color:rgba(36,37,39,0.6);--scrollbar-color:rgba(36,37,39,0.6) #d9d9d9;--headings-border-bottom-color:#ddd;--border-color:#e0e0e0;--button-background-color:#fff;--right-side-color:grey;--code-attribute-color:#999;--toggles-color:#999;--toggle-filter:none;--mobile-sidebar-menu-filter:none;--search-input-focused-border-color:#66afe9;--copy-path-button-color:#999;--copy-path-img-filter:invert(50%);--copy-path-img-hover-filter:invert(35%);--codeblock-error-hover-color:rgb(255,0,0);--codeblock-error-color:rgba(255,0,0,.5);--codeblock-ignore-hover-color:rgb(255,142,0);--codeblock-ignore-color:rgba(255,142,0,.6);--warning-border-color:#ff8e00;--type-link-color:#ad378a;--trait-link-color:#6e4fc9;--assoc-item-link-color:#3873ad;--function-link-color:#ad7c37;--macro-link-color:#068000;--keyword-link-color:#3873ad;--mod-link-color:#3873ad;--link-color:#3873ad;--sidebar-link-color:#356da4;--sidebar-current-link-background-color:#fff;--search-result-link-focus-background-color:#ccc;--search-result-border-color:#aaa3;--search-color:#000;--search-error-code-background-color:#d0cccc;--search-results-alias-color:#000;--search-results-grey-color:#999;--search-tab-title-count-color:#888;--search-tab-button-not-selected-border-top-color:#e6e6e6;--search-tab-button-not-selected-background:#e6e6e6;--search-tab-button-selected-border-top-color:#0089ff;--search-tab-button-selected-background:#fff;--stab-background-color:#fff5d6;--stab-code-color:#000;--code-highlight-kw-color:#8959a8;--code-highlight-kw-2-color:#4271ae;--code-highlight-lifetime-color:#b76514;--code-highlight-prelude-color:#4271ae;--code-highlight-prelude-val-color:#c82829;--code-highlight-number-color:#718c00;--code-highlight-string-color:#718c00;--code-highlight-literal-color:#c82829;--code-highlight-attribute-color:#c82829;--code-highlight-self-color:#c82829;--code-highlight-macro-color:#3e999f;--code-highlight-question-mark-color:#ff9011;--code-highlight-comment-color:#8e908c;--code-highlight-doc-comment-color:#4d4d4c;--src-line-numbers-span-color:#c67e2d;--src-line-number-highlighted-background-color:#fdffd3;--test-arrow-color:#f5f5f5;--test-arrow-background-color:rgba(78,139,202,0.2);--test-arrow-hover-color:#f5f5f5;--test-arrow-hover-background-color:rgb(78,139,202);--target-background-color:#fdffd3;--target-border-color:#ad7c37;--kbd-color:#000;--kbd-background:#fafbfc;--kbd-box-shadow-color:#c6cbd1;--rust-logo-filter:initial;--crate-search-div-filter:invert(100%) sepia(0%) saturate(4223%) hue-rotate(289deg) brightness(114%) contrast(76%);--crate-search-div-hover-filter:invert(44%) sepia(18%) saturate(23%) hue-rotate(317deg) brightness(96%) contrast(93%);--crate-search-hover-border:#717171;--src-sidebar-background-selected:#fff;--src-sidebar-background-hover:#e0e0e0;--table-alt-row-background-color:#f5f5f5;--codeblock-link-background:#eee;--scrape-example-toggle-line-background:#ccc;--scrape-example-toggle-line-hover-background:#999;--scrape-example-code-line-highlight:#fcffd6;--scrape-example-code-line-highlight-focus:#f6fdb0;--scrape-example-help-border-color:#555;--scrape-example-help-color:#333;--scrape-example-help-hover-border-color:#000;--scrape-example-help-hover-color:#000;--scrape-example-code-wrapper-background-start:rgba(255,255,255,1);--scrape-example-code-wrapper-background-end:rgba(255,255,255,0);--sidebar-resizer-hover:hsl(207,90%,66%);--sidebar-resizer-active:hsl(207,90%,54%);}@media (prefers-color-scheme:dark){:root{--main-background-color:#353535;--main-color:#ddd;--settings-input-color:#2196f3;--settings-input-border-color:#999;--settings-button-color:#000;--settings-button-border-focus:#ffb900;--sidebar-background-color:#505050;--sidebar-background-color-hover:#676767;--code-block-background-color:#2A2A2A;--scrollbar-track-background-color:#717171;--scrollbar-thumb-background-color:rgba(32,34,37,.6);--scrollbar-color:rgba(32,34,37,.6) #5a5a5a;--headings-border-bottom-color:#d2d2d2;--border-color:#e0e0e0;--button-background-color:#f0f0f0;--right-side-color:grey;--code-attribute-color:#999;--toggles-color:#999;--toggle-filter:invert(100%);--mobile-sidebar-menu-filter:invert(100%);--search-input-focused-border-color:#008dfd;--copy-path-button-color:#999;--copy-path-img-filter:invert(50%);--copy-path-img-hover-filter:invert(65%);--codeblock-error-hover-color:rgb(255,0,0);--codeblock-error-color:rgba(255,0,0,.5);--codeblock-ignore-hover-color:rgb(255,142,0);--codeblock-ignore-color:rgba(255,142,0,.6);--warning-border-color:#ff8e00;--type-link-color:#2dbfb8;--trait-link-color:#b78cf2;--assoc-item-link-color:#d2991d;--function-link-color:#2bab63;--macro-link-color:#09bd00;--keyword-link-color:#d2991d;--mod-link-color:#d2991d;--link-color:#d2991d;--sidebar-link-color:#fdbf35;--sidebar-current-link-background-color:#444;--search-result-link-focus-background-color:#616161;--search-result-border-color:#aaa3;--search-color:#111;--search-error-code-background-color:#484848;--search-results-alias-color:#fff;--search-results-grey-color:#ccc;--search-tab-title-count-color:#888;--search-tab-button-not-selected-border-top-color:#252525;--search-tab-button-not-selected-background:#252525;--search-tab-button-selected-border-top-color:#0089ff;--search-tab-button-selected-background:#353535;--stab-background-color:#314559;--stab-code-color:#e6e1cf;--code-highlight-kw-color:#ab8ac1;--code-highlight-kw-2-color:#769acb;--code-highlight-lifetime-color:#d97f26;--code-highlight-prelude-color:#769acb;--code-highlight-prelude-val-color:#ee6868;--code-highlight-number-color:#83a300;--code-highlight-string-color:#83a300;--code-highlight-literal-color:#ee6868;--code-highlight-attribute-color:#ee6868;--code-highlight-self-color:#ee6868;--code-highlight-macro-color:#3e999f;--code-highlight-question-mark-color:#ff9011;--code-highlight-comment-color:#8d8d8b;--code-highlight-doc-comment-color:#8ca375;--src-line-numbers-span-color:#3b91e2;--src-line-number-highlighted-background-color:#0a042f;--test-arrow-color:#dedede;--test-arrow-background-color:rgba(78,139,202,0.2);--test-arrow-hover-color:#dedede;--test-arrow-hover-background-color:#4e8bca;--target-background-color:#494a3d;--target-border-color:#bb7410;--kbd-color:#000;--kbd-background:#fafbfc;--kbd-box-shadow-color:#c6cbd1;--rust-logo-filter:drop-shadow(1px 0 0px #fff) drop-shadow(0 1px 0 #fff) drop-shadow(-1px 0 0 #fff) drop-shadow(0 -1px 0 #fff);--crate-search-div-filter:invert(94%) sepia(0%) saturate(721%) hue-rotate(255deg) brightness(90%) contrast(90%);--crate-search-div-hover-filter:invert(69%) sepia(60%) saturate(6613%) hue-rotate(184deg) brightness(100%) contrast(91%);--crate-search-hover-border:#2196f3;--src-sidebar-background-selected:#333;--src-sidebar-background-hover:#444;--table-alt-row-background-color:#2a2a2a;--codeblock-link-background:#333;--scrape-example-toggle-line-background:#999;--scrape-example-toggle-line-hover-background:#c5c5c5;--scrape-example-code-line-highlight:#5b3b01;--scrape-example-code-line-highlight-focus:#7c4b0f;--scrape-example-help-border-color:#aaa;--scrape-example-help-color:#eee;--scrape-example-help-hover-border-color:#fff;--scrape-example-help-hover-color:#fff;--scrape-example-code-wrapper-background-start:rgba(53,53,53,1);--scrape-example-code-wrapper-background-end:rgba(53,53,53,0);--sidebar-resizer-hover:hsl(207,30%,54%);--sidebar-resizer-active:hsl(207,90%,54%);}} \ No newline at end of file diff --git a/static.files/rust-logo-151179464ae7ed46.svg b/static.files/rust-logo-151179464ae7ed46.svg new file mode 100644 index 00000000..62424d8f --- /dev/null +++ b/static.files/rust-logo-151179464ae7ed46.svg @@ -0,0 +1,61 @@ + + + diff --git a/static.files/rustdoc-5bc39a1768837dd0.css b/static.files/rustdoc-5bc39a1768837dd0.css new file mode 100644 index 00000000..175164ef --- /dev/null +++ b/static.files/rustdoc-5bc39a1768837dd0.css @@ -0,0 +1,24 @@ + :root{--nav-sub-mobile-padding:8px;--search-typename-width:6.75rem;--desktop-sidebar-width:200px;--src-sidebar-width:300px;--desktop-sidebar-z-index:100;}@font-face {font-family:'Fira Sans';font-style:normal;font-weight:400;src:local('Fira Sans'),url("FiraSans-Regular-018c141bf0843ffd.woff2") format("woff2");font-display:swap;}@font-face {font-family:'Fira Sans';font-style:normal;font-weight:500;src:local('Fira Sans Medium'),url("FiraSans-Medium-8f9a781e4970d388.woff2") format("woff2");font-display:swap;}@font-face {font-family:'Source Serif 4';font-style:normal;font-weight:400;src:local('Source Serif 4'),url("SourceSerif4-Regular-46f98efaafac5295.ttf.woff2") format("woff2");font-display:swap;}@font-face {font-family:'Source Serif 4';font-style:italic;font-weight:400;src:local('Source Serif 4 Italic'),url("SourceSerif4-It-acdfaf1a8af734b1.ttf.woff2") format("woff2");font-display:swap;}@font-face {font-family:'Source Serif 4';font-style:normal;font-weight:700;src:local('Source Serif 4 Bold'),url("SourceSerif4-Bold-a2c9cd1067f8b328.ttf.woff2") format("woff2");font-display:swap;}@font-face {font-family:'Source Code Pro';font-style:normal;font-weight:400;src:url("SourceCodePro-Regular-562dcc5011b6de7d.ttf.woff2") format("woff2");font-display:swap;}@font-face {font-family:'Source Code Pro';font-style:italic;font-weight:400;src:url("SourceCodePro-It-1cc31594bf4f1f79.ttf.woff2") format("woff2");font-display:swap;}@font-face {font-family:'Source Code Pro';font-style:normal;font-weight:600;src:url("SourceCodePro-Semibold-d899c5a5c4aeb14a.ttf.woff2") format("woff2");font-display:swap;}@font-face {font-family:'NanumBarunGothic';src:url("NanumBarunGothic-0f09457c7a19b7c6.ttf.woff2") format("woff2");font-display:swap;unicode-range:U+AC00-D7AF,U+1100-11FF,U+3130-318F,U+A960-A97F,U+D7B0-D7FF;}*{box-sizing:border-box;}body{font:1rem/1.5 "Source Serif 4",NanumBarunGothic,serif;margin:0;position:relative;overflow-wrap:break-word;overflow-wrap:anywhere;font-feature-settings:"kern","liga";background-color:var(--main-background-color);color:var(--main-color);}h1{font-size:1.5rem;}h2{font-size:1.375rem;}h3{font-size:1.25rem;}h1,h2,h3,h4,h5,h6{font-weight:500;}h1,h2,h3,h4{margin:25px 0 15px 0;padding-bottom:6px;}.docblock h3,.docblock h4,h5,h6{margin:15px 0 5px 0;}.docblock>h2:first-child,.docblock>h3:first-child,.docblock>h4:first-child,.docblock>h5:first-child,.docblock>h6:first-child{margin-top:0;}.main-heading h1{margin:0;padding:0;flex-grow:1;overflow-wrap:break-word;overflow-wrap:anywhere;}.main-heading{display:flex;flex-wrap:wrap;padding-bottom:6px;margin-bottom:15px;}.content h2,.top-doc .docblock>h3,.top-doc .docblock>h4{border-bottom:1px solid var(--headings-border-bottom-color);}h1,h2{line-height:1.25;padding-top:3px;padding-bottom:9px;}h3.code-header{font-size:1.125rem;}h4.code-header{font-size:1rem;}.code-header{font-weight:600;margin:0;padding:0;white-space:pre-wrap;}#crate-search,h1,h2,h3,h4,h5,h6,.sidebar,.mobile-topbar,.search-input,.search-results .result-name,.item-name>a,.out-of-band,span.since,a.src,#help-button>a,summary.hideme,.scraped-example-list,ul.all-items{font-family:"Fira Sans",Arial,NanumBarunGothic,sans-serif;}#toggle-all-docs,a.anchor,.section-header a,#src-sidebar a,.rust a,.sidebar h2 a,.sidebar h3 a,.mobile-topbar h2 a,h1 a,.search-results a,.stab,.result-name i{color:var(--main-color);}span.enum,a.enum,span.struct,a.struct,span.union,a.union,span.primitive,a.primitive,span.type,a.type,span.foreigntype,a.foreigntype{color:var(--type-link-color);}span.trait,a.trait,span.traitalias,a.traitalias{color:var(--trait-link-color);}span.associatedtype,a.associatedtype,span.constant,a.constant,span.static,a.static{color:var(--assoc-item-link-color);}span.fn,a.fn,span.method,a.method,span.tymethod,a.tymethod{color:var(--function-link-color);}span.attr,a.attr,span.derive,a.derive,span.macro,a.macro{color:var(--macro-link-color);}span.mod,a.mod{color:var(--mod-link-color);}span.keyword,a.keyword{color:var(--keyword-link-color);}a{color:var(--link-color);text-decoration:none;}ol,ul{padding-left:24px;}ul ul,ol ul,ul ol,ol ol{margin-bottom:.625em;}p,.docblock>.warning{margin:0 0 .75em 0;}p:last-child,.docblock>.warning:last-child{margin:0;}button{padding:1px 6px;cursor:pointer;}button#toggle-all-docs{padding:0;background:none;border:none;-webkit-appearance:none;opacity:1;}.rustdoc{display:flex;flex-direction:row;flex-wrap:nowrap;}main{position:relative;flex-grow:1;padding:10px 15px 40px 45px;min-width:0;}.src main{padding:15px;}.width-limiter{max-width:960px;margin-right:auto;}details:not(.toggle) summary{margin-bottom:.6em;}code,pre,a.test-arrow,.code-header{font-family:"Source Code Pro",monospace;}.docblock code,.docblock-short code{border-radius:3px;padding:0 0.125em;}.docblock pre code,.docblock-short pre code{padding:0;}pre{padding:14px;line-height:1.5;}pre.item-decl{overflow-x:auto;}.item-decl .type-contents-toggle{contain:initial;}.src .content pre{padding:20px;}.rustdoc.src .example-wrap pre.src-line-numbers{padding:20px 0 20px 4px;}img{max-width:100%;}.logo-container{line-height:0;display:block;}.rust-logo{filter:var(--rust-logo-filter);}.sidebar{font-size:0.875rem;flex:0 0 var(--desktop-sidebar-width);width:var(--desktop-sidebar-width);overflow-y:scroll;overscroll-behavior:contain;position:sticky;height:100vh;top:0;left:0;z-index:var(--desktop-sidebar-z-index);}.rustdoc.src .sidebar{flex-basis:50px;width:50px;border-right:1px solid;overflow-x:hidden;overflow-y:hidden;}.hide-sidebar .sidebar,.hide-sidebar .sidebar-resizer{display:none;}.sidebar-resizer{touch-action:none;width:9px;cursor:col-resize;z-index:calc(var(--desktop-sidebar-z-index) + 1);position:fixed;height:100%;left:calc(var(--desktop-sidebar-width) + 1px);}.rustdoc.src .sidebar-resizer{left:49px;}.src-sidebar-expanded .src .sidebar-resizer{left:var(--src-sidebar-width);}.sidebar-resizing{-moz-user-select:none;-webkit-user-select:none;-ms-user-select:none;user-select:none;}.sidebar-resizing*{cursor:col-resize !important;}.sidebar-resizing .sidebar{position:fixed;}.sidebar-resizing>body{padding-left:var(--resizing-sidebar-width);}.sidebar-resizer:hover,.sidebar-resizer:active,.sidebar-resizer:focus,.sidebar-resizer.active{width:10px;margin:0;left:var(--desktop-sidebar-width);border-left:solid 1px var(--sidebar-resizer-hover);}.src-sidebar-expanded .rustdoc.src .sidebar-resizer:hover,.src-sidebar-expanded .rustdoc.src .sidebar-resizer:active,.src-sidebar-expanded .rustdoc.src .sidebar-resizer:focus,.src-sidebar-expanded .rustdoc.src .sidebar-resizer.active{left:calc(var(--src-sidebar-width) - 1px);}@media (pointer:coarse){.sidebar-resizer{display:none !important;}}.sidebar-resizer.active{padding:0 140px;width:2px;margin-left:-140px;border-left:none;}.sidebar-resizer.active:before{border-left:solid 2px var(--sidebar-resizer-active);display:block;height:100%;content:"";}.sidebar,.mobile-topbar,.sidebar-menu-toggle,#src-sidebar{background-color:var(--sidebar-background-color);}.src .sidebar>*{visibility:hidden;}.src-sidebar-expanded .src .sidebar{overflow-y:auto;flex-basis:var(--src-sidebar-width);width:var(--src-sidebar-width);}.src-sidebar-expanded .src .sidebar>*{visibility:visible;}#all-types{margin-top:1em;}*{scrollbar-width:initial;scrollbar-color:var(--scrollbar-color);}.sidebar{scrollbar-width:thin;scrollbar-color:var(--scrollbar-color);}::-webkit-scrollbar{width:12px;}.sidebar::-webkit-scrollbar{width:8px;}::-webkit-scrollbar-track{-webkit-box-shadow:inset 0;background-color:var(--scrollbar-track-background-color);}.sidebar::-webkit-scrollbar-track{background-color:var(--scrollbar-track-background-color);}::-webkit-scrollbar-thumb,.sidebar::-webkit-scrollbar-thumb{background-color:var(--scrollbar-thumb-background-color);}.hidden{display:none !important;}.logo-container>img{height:48px;width:48px;}ul.block,.block li{padding:0;margin:0;list-style:none;}.sidebar-elems a,.sidebar>h2 a{display:block;padding:0.25rem;margin-left:-0.25rem;margin-right:0.25rem;}.sidebar h2{overflow-wrap:anywhere;padding:0;margin:0.7rem 0;}.sidebar h3{font-size:1.125rem;padding:0;margin:0;}.sidebar-elems,.sidebar>.version,.sidebar>h2{padding-left:24px;}.sidebar a{color:var(--sidebar-link-color);}.sidebar .current,.sidebar .current a,.sidebar-crate a.logo-container:hover+h2 a,.sidebar a:hover:not(.logo-container){background-color:var(--sidebar-current-link-background-color);}.sidebar-elems .block{margin-bottom:2em;}.sidebar-elems .block li a{white-space:nowrap;text-overflow:ellipsis;overflow:hidden;}.sidebar-crate{display:flex;align-items:center;justify-content:center;margin:14px 32px 1rem;row-gap:10px;column-gap:32px;flex-wrap:wrap;}.sidebar-crate h2{flex-grow:1;margin:0 -8px;align-self:start;}.sidebar-crate .logo-container{margin:0 -16px 0 -16px;text-align:center;}.sidebar-crate h2 a{display:block;margin:0 calc(-24px + 0.25rem) 0 -0.2rem;padding:calc((16px - 0.57rem ) / 2 ) 0.25rem;padding-left:0.2rem;}.sidebar-crate h2 .version{display:block;font-weight:normal;font-size:1rem;overflow-wrap:break-word;}.sidebar-crate+.version{margin-top:-1rem;margin-bottom:1rem;}.mobile-topbar{display:none;}.rustdoc .example-wrap{display:flex;position:relative;margin-bottom:10px;}.rustdoc .example-wrap:last-child{margin-bottom:0px;}.rustdoc .example-wrap pre{margin:0;flex-grow:1;}.rustdoc:not(.src) .example-wrap pre{overflow:auto hidden;}.rustdoc .example-wrap pre.example-line-numbers,.rustdoc .example-wrap pre.src-line-numbers{flex-grow:0;min-width:fit-content;overflow:initial;text-align:right;-webkit-user-select:none;user-select:none;padding:14px 8px;color:var(--src-line-numbers-span-color);}.rustdoc .example-wrap pre.src-line-numbers{padding:14px 0;}.src-line-numbers a,.src-line-numbers span{color:var(--src-line-numbers-span-color);padding:0 8px;}.src-line-numbers :target{background-color:transparent;border-right:none;padding:0 8px;}.src-line-numbers .line-highlighted{background-color:var(--src-line-number-highlighted-background-color);}.search-loading{text-align:center;}.docblock-short{overflow-wrap:break-word;overflow-wrap:anywhere;}.docblock :not(pre)>code,.docblock-short code{white-space:pre-wrap;}.top-doc .docblock h2{font-size:1.375rem;}.top-doc .docblock h3{font-size:1.25rem;}.top-doc .docblock h4,.top-doc .docblock h5{font-size:1.125rem;}.top-doc .docblock h6{font-size:1rem;}.docblock h5{font-size:1rem;}.docblock h6{font-size:0.875rem;}.docblock{margin-left:24px;position:relative;}.docblock>:not(.more-examples-toggle):not(.example-wrap){max-width:100%;overflow-x:auto;}.out-of-band{flex-grow:0;font-size:1.125rem;}.docblock code,.docblock-short code,pre,.rustdoc.src .example-wrap{background-color:var(--code-block-background-color);}#main-content{position:relative;}.docblock table{margin:.5em 0;border-collapse:collapse;}.docblock table td,.docblock table th{padding:.5em;border:1px solid var(--border-color);}.docblock table tbody tr:nth-child(2n){background:var(--table-alt-row-background-color);}div.where{white-space:pre-wrap;font-size:0.875rem;}.item-info{display:block;margin-left:24px;}.item-info code{font-size:0.875rem;}#main-content>.item-info{margin-left:0;}nav.sub{flex-grow:1;flex-flow:row nowrap;margin:4px 0 25px 0;display:flex;align-items:center;}.search-form{position:relative;display:flex;height:34px;flex-grow:1;}.src nav.sub{margin:0 0 15px 0;}.section-header{display:block;position:relative;}.section-header:hover>.anchor,.impl:hover>.anchor,.trait-impl:hover>.anchor,.variant:hover>.anchor{display:initial;}.anchor{display:none;position:absolute;left:-0.5em;background:none !important;}.anchor.field{left:-5px;}.section-header>.anchor{left:-15px;padding-right:8px;}h2.section-header>.anchor{padding-right:6px;}a.doc-anchor{color:var(--main-color);display:none;position:absolute;left:-17px;padding-right:5px;padding-left:3px;}*:hover>.doc-anchor{display:block;}.top-doc>.docblock>*:first-child>.doc-anchor{display:none !important;}.main-heading a:hover,.example-wrap .rust a:hover,.all-items a:hover,.docblock a:not(.test-arrow):not(.scrape-help):not(.tooltip):hover:not(.doc-anchor),.docblock-short a:not(.test-arrow):not(.scrape-help):not(.tooltip):hover,.item-info a{text-decoration:underline;}.crate.block li.current a{font-weight:500;}table,.item-table{overflow-wrap:break-word;}.item-table{display:table;padding:0;margin:0;}.item-table>li{display:table-row;}.item-table>li>div{display:table-cell;}.item-table>li>.item-name{padding-right:1.25rem;}.search-results-title{margin-top:0;white-space:nowrap;display:flex;align-items:baseline;}#crate-search-div{position:relative;min-width:5em;}#crate-search{min-width:115px;padding:0 23px 0 4px;max-width:100%;text-overflow:ellipsis;border:1px solid var(--border-color);border-radius:4px;outline:none;cursor:pointer;-moz-appearance:none;-webkit-appearance:none;text-indent:0.01px;background-color:var(--main-background-color);color:inherit;line-height:1.5;font-weight:500;}#crate-search:hover,#crate-search:focus{border-color:var(--crate-search-hover-border);}#crate-search-div::after{pointer-events:none;width:100%;height:100%;position:absolute;top:0;left:0;content:"";background-repeat:no-repeat;background-size:20px;background-position:calc(100% - 2px) 56%;background-image:url('data:image/svg+xml, \ + ');filter:var(--crate-search-div-filter);}#crate-search-div:hover::after,#crate-search-div:focus-within::after{filter:var(--crate-search-div-hover-filter);}#crate-search>option{font-size:1rem;}.search-input{-webkit-appearance:none;outline:none;border:1px solid var(--border-color);border-radius:2px;padding:8px;font-size:1rem;flex-grow:1;background-color:var(--button-background-color);color:var(--search-color);}.search-input:focus{border-color:var(--search-input-focused-border-color);}.search-results{display:none;}.search-results.active{display:block;}.search-results>a{display:flex;margin-left:2px;margin-right:2px;border-bottom:1px solid var(--search-result-border-color);gap:1em;}.search-results>a>div.desc{white-space:nowrap;text-overflow:ellipsis;overflow:hidden;flex:2;}.search-results a:hover,.search-results a:focus{background-color:var(--search-result-link-focus-background-color);}.search-results .result-name{display:flex;align-items:center;justify-content:start;flex:3;}.search-results .result-name .alias{color:var(--search-results-alias-color);}.search-results .result-name .grey{color:var(--search-results-grey-color);}.search-results .result-name .typename{color:var(--search-results-grey-color);font-size:0.875rem;width:var(--search-typename-width);}.search-results .result-name .path{word-break:break-all;max-width:calc(100% - var(--search-typename-width));display:inline-block;}.search-results .result-name .path>*{display:inline;}.popover{position:absolute;top:100%;right:0;z-index:calc(var(--desktop-sidebar-z-index) + 1);margin-top:7px;border-radius:3px;border:1px solid var(--border-color);background-color:var(--main-background-color);color:var(--main-color);--popover-arrow-offset:11px;}.popover::before{content:'';position:absolute;right:var(--popover-arrow-offset);border:solid var(--border-color);border-width:1px 1px 0 0;background-color:var(--main-background-color);padding:4px;transform:rotate(-45deg);top:-5px;}.setting-line{margin:1.2em 0.6em;}.setting-radio input,.setting-check input{margin-right:0.3em;height:1.2rem;width:1.2rem;border:2px solid var(--settings-input-border-color);outline:none;-webkit-appearance:none;cursor:pointer;}.setting-radio input{border-radius:50%;}.setting-radio span,.setting-check span{padding-bottom:1px;}.setting-radio{margin-top:0.1em;margin-bottom:0.1em;min-width:3.8em;padding:0.3em;display:inline-flex;align-items:center;cursor:pointer;}.setting-radio+.setting-radio{margin-left:0.5em;}.setting-check{margin-right:20px;display:flex;align-items:center;cursor:pointer;}.setting-radio input:checked{box-shadow:inset 0 0 0 3px var(--main-background-color);background-color:var(--settings-input-color);}.setting-check input:checked{background-color:var(--settings-input-color);border-width:1px;content:url('data:image/svg+xml,\ + \ + ');}.setting-radio input:focus,.setting-check input:focus{box-shadow:0 0 1px 1px var(--settings-input-color);}.setting-radio input:checked:focus{box-shadow:inset 0 0 0 3px var(--main-background-color),0 0 2px 2px var(--settings-input-color);}.setting-radio input:hover,.setting-check input:hover{border-color:var(--settings-input-color) !important;}#help.popover{max-width:600px;--popover-arrow-offset:48px;}#help dt{float:left;clear:left;margin-right:0.5rem;}#help span.top,#help span.bottom{text-align:center;display:block;font-size:1.125rem;}#help span.top{margin:10px 0;border-bottom:1px solid var(--border-color);padding-bottom:4px;margin-bottom:6px;}#help span.bottom{clear:both;border-top:1px solid var(--border-color);}.side-by-side>div{width:50%;float:left;padding:0 20px 20px 17px;}.item-info .stab{display:block;padding:3px;margin-bottom:5px;}.item-name .stab{margin-left:0.3125em;}.stab{padding:0 2px;font-size:0.875rem;font-weight:normal;color:var(--main-color);background-color:var(--stab-background-color);width:fit-content;white-space:pre-wrap;border-radius:3px;display:inline;vertical-align:baseline;}.stab.portability>code{background:none;color:var(--stab-code-color);}.stab .emoji,.item-info .stab::before{font-size:1.25rem;}.stab .emoji{margin-right:0.3rem;}.item-info .stab::before{content:"\0";width:0;display:inline-block;color:transparent;}.emoji{text-shadow:1px 0 0 black,-1px 0 0 black,0 1px 0 black,0 -1px 0 black;}.since{font-weight:normal;font-size:initial;}.rightside{padding-left:12px;float:right;}.rightside:not(a),.out-of-band{color:var(--right-side-color);}pre.rust{tab-size:4;-moz-tab-size:4;}pre.rust .kw{color:var(--code-highlight-kw-color);}pre.rust .kw-2{color:var(--code-highlight-kw-2-color);}pre.rust .lifetime{color:var(--code-highlight-lifetime-color);}pre.rust .prelude-ty{color:var(--code-highlight-prelude-color);}pre.rust .prelude-val{color:var(--code-highlight-prelude-val-color);}pre.rust .string{color:var(--code-highlight-string-color);}pre.rust .number{color:var(--code-highlight-number-color);}pre.rust .bool-val{color:var(--code-highlight-literal-color);}pre.rust .self{color:var(--code-highlight-self-color);}pre.rust .attr{color:var(--code-highlight-attribute-color);}pre.rust .macro,pre.rust .macro-nonterminal{color:var(--code-highlight-macro-color);}pre.rust .question-mark{font-weight:bold;color:var(--code-highlight-question-mark-color);}pre.rust .comment{color:var(--code-highlight-comment-color);}pre.rust .doccomment{color:var(--code-highlight-doc-comment-color);}.rustdoc.src .example-wrap pre.rust a{background:var(--codeblock-link-background);}.example-wrap.compile_fail,.example-wrap.should_panic{border-left:2px solid var(--codeblock-error-color);}.ignore.example-wrap{border-left:2px solid var(--codeblock-ignore-color);}.example-wrap.compile_fail:hover,.example-wrap.should_panic:hover{border-left:2px solid var(--codeblock-error-hover-color);}.example-wrap.ignore:hover{border-left:2px solid var(--codeblock-ignore-hover-color);}.example-wrap.compile_fail .tooltip,.example-wrap.should_panic .tooltip{color:var(--codeblock-error-color);}.example-wrap.ignore .tooltip{color:var(--codeblock-ignore-color);}.example-wrap.compile_fail:hover .tooltip,.example-wrap.should_panic:hover .tooltip{color:var(--codeblock-error-hover-color);}.example-wrap.ignore:hover .tooltip{color:var(--codeblock-ignore-hover-color);}.example-wrap .tooltip{position:absolute;display:block;left:-25px;top:5px;margin:0;line-height:1;}.example-wrap.compile_fail .tooltip,.example-wrap.should_panic .tooltip,.example-wrap.ignore .tooltip{font-weight:bold;font-size:1.25rem;}.content .docblock .warning{border-left:2px solid var(--warning-border-color);padding:14px;position:relative;overflow-x:visible !important;}.content .docblock .warning::before{color:var(--warning-border-color);content:"ⓘ";position:absolute;left:-25px;top:5px;font-weight:bold;font-size:1.25rem;}.top-doc>.docblock>.warning:first-child::before{top:20px;}a.test-arrow{visibility:hidden;position:absolute;padding:5px 10px 5px 10px;border-radius:5px;font-size:1.375rem;top:5px;right:5px;z-index:1;color:var(--test-arrow-color);background-color:var(--test-arrow-background-color);}a.test-arrow:hover{color:var(--test-arrow-hover-color);background-color:var(--test-arrow-hover-background-color);}.example-wrap:hover .test-arrow{visibility:visible;}.code-attribute{font-weight:300;color:var(--code-attribute-color);}.item-spacer{width:100%;height:12px;display:block;}.out-of-band>span.since{font-size:1.25rem;}.sub-variant h4{font-size:1rem;font-weight:400;margin-top:0;margin-bottom:0;}.sub-variant{margin-left:24px;margin-bottom:40px;}.sub-variant>.sub-variant-field{margin-left:24px;}:target{padding-right:3px;background-color:var(--target-background-color);border-right:3px solid var(--target-border-color);}.code-header a.tooltip{color:inherit;margin-right:15px;position:relative;}.code-header a.tooltip:hover{color:var(--link-color);}a.tooltip:hover::after{position:absolute;top:calc(100% - 10px);left:-15px;right:-15px;height:20px;content:"\00a0";}.fade-out{opacity:0;transition:opacity 0.45s cubic-bezier(0,0,0.1,1.0);}.popover.tooltip .content{margin:0.25em 0.5em;}.popover.tooltip .content pre,.popover.tooltip .content code{background:transparent;margin:0;padding:0;font-size:1.25rem;white-space:pre-wrap;}.popover.tooltip .content>h3:first-child{margin:0 0 5px 0;}.search-failed{text-align:center;margin-top:20px;display:none;}.search-failed.active{display:block;}.search-failed>ul{text-align:left;max-width:570px;margin-left:auto;margin-right:auto;}#search-tabs{display:flex;flex-direction:row;gap:1px;margin-bottom:4px;}#search-tabs button{text-align:center;font-size:1.125rem;border:0;border-top:2px solid;flex:1;line-height:1.5;color:inherit;}#search-tabs button:not(.selected){background-color:var(--search-tab-button-not-selected-background);border-top-color:var(--search-tab-button-not-selected-border-top-color);}#search-tabs button:hover,#search-tabs button.selected{background-color:var(--search-tab-button-selected-background);border-top-color:var(--search-tab-button-selected-border-top-color);}#search-tabs .count{font-size:1rem;font-variant-numeric:tabular-nums;color:var(--search-tab-title-count-color);}#search .error code{border-radius:3px;background-color:var(--search-error-code-background-color);}.search-corrections{font-weight:normal;}#src-sidebar{width:100%;overflow:auto;}#src-sidebar div.files>a:hover,details.dir-entry summary:hover,#src-sidebar div.files>a:focus,details.dir-entry summary:focus{background-color:var(--src-sidebar-background-hover);}#src-sidebar div.files>a.selected{background-color:var(--src-sidebar-background-selected);}.src-sidebar-title{position:sticky;top:0;display:flex;padding:8px 8px 0 48px;margin-bottom:7px;background:var(--sidebar-background-color);border-bottom:1px solid var(--border-color);}#settings-menu,#help-button{margin-left:4px;display:flex;}#sidebar-button{display:none;line-height:0;}.hide-sidebar #sidebar-button,.src #sidebar-button{display:flex;margin-right:4px;position:fixed;left:6px;height:34px;width:34px;background-color:var(--main-background-color);z-index:1;}.src #sidebar-button{left:8px;z-index:calc(var(--desktop-sidebar-z-index) + 1);}.hide-sidebar .src #sidebar-button{position:static;}#settings-menu>a,#help-button>a,#sidebar-button>a{display:flex;align-items:center;justify-content:center;background-color:var(--button-background-color);border:1px solid var(--border-color);border-radius:2px;color:var(--settings-button-color);font-size:20px;width:33px;}#settings-menu>a:hover,#settings-menu>a:focus,#help-button>a:hover,#help-button>a:focus,#sidebar-button>a:hover,#sidebar-button>a:focus{border-color:var(--settings-button-border-focus);}#sidebar-button>a:before{content:url('data:image/svg+xml,\ + \ + \ + ');width:22px;height:22px;}#copy-path{color:var(--copy-path-button-color);background:var(--main-background-color);height:34px;margin-left:10px;padding:0;padding-left:2px;border:0;width:33px;}#copy-path>img{filter:var(--copy-path-img-filter);}#copy-path:hover>img{filter:var(--copy-path-img-hover-filter);}@keyframes rotating{from{transform:rotate(0deg);}to{transform:rotate(360deg);}}#settings-menu.rotate>a img{animation:rotating 2s linear infinite;}kbd{display:inline-block;padding:3px 5px;font:15px monospace;line-height:10px;vertical-align:middle;border:solid 1px var(--border-color);border-radius:3px;color:var(--kbd-color);background-color:var(--kbd-background);box-shadow:inset 0 -1px 0 var(--kbd-box-shadow-color);}ul.all-items>li{list-style:none;}details.dir-entry{padding-left:4px;}details.dir-entry>summary{margin:0 0 0 -4px;padding:0 0 0 4px;cursor:pointer;}details.dir-entry div.folders,details.dir-entry div.files{padding-left:23px;}details.dir-entry a{display:block;}details.toggle{contain:layout;position:relative;}details.toggle>summary.hideme{cursor:pointer;font-size:1rem;}details.toggle>summary{list-style:none;outline:none;}details.toggle>summary::-webkit-details-marker,details.toggle>summary::marker{display:none;}details.toggle>summary.hideme>span{margin-left:9px;}details.toggle>summary::before{background:url('data:image/svg+xml,') no-repeat top left;content:"";cursor:pointer;width:16px;height:16px;display:inline-block;vertical-align:middle;opacity:.5;filter:var(--toggle-filter);}details.toggle>summary.hideme>span,.more-examples-toggle summary,.more-examples-toggle .hide-more{color:var(--toggles-color);}details.toggle>summary::after{content:"Expand";overflow:hidden;width:0;height:0;position:absolute;}details.toggle>summary.hideme::after{content:"";}details.toggle>summary:focus::before,details.toggle>summary:hover::before{opacity:1;}details.toggle>summary:focus-visible::before{outline:1px dotted #000;outline-offset:1px;}details.non-exhaustive{margin-bottom:8px;}details.toggle>summary.hideme::before{position:relative;}details.toggle>summary:not(.hideme)::before{position:absolute;left:-24px;top:4px;}.impl-items>details.toggle>summary:not(.hideme)::before{position:absolute;left:-24px;}details.toggle[open] >summary.hideme{position:absolute;}details.toggle[open] >summary.hideme>span{display:none;}details.toggle[open] >summary::before{background:url('data:image/svg+xml,') no-repeat top left;}details.toggle[open] >summary::after{content:"Collapse";}.docblock summary>*{display:inline-block;}.docblock>.example-wrap:first-child .tooltip{margin-top:16px;}.src #sidebar-button>a:before,.sidebar-menu-toggle:before{content:url('data:image/svg+xml,\ + ');opacity:0.75;}.sidebar-menu-toggle:hover:before,.sidebar-menu-toggle:active:before,.sidebar-menu-toggle:focus:before{opacity:1;}.src #sidebar-button>a:before{content:url('data:image/svg+xml,\ + \ + \ + ');opacity:0.75;}@media (max-width:850px){#search-tabs .count{display:block;}}@media (max-width:700px){*[id]{scroll-margin-top:45px;}.rustdoc{display:block;}main{padding-left:15px;padding-top:0px;}.main-heading{flex-direction:column;}.out-of-band{text-align:left;margin-left:initial;padding:initial;}.out-of-band .since::before{content:"Since ";}.sidebar .logo-container,.sidebar .location,.sidebar-resizer{display:none;}.sidebar{position:fixed;top:45px;left:-1000px;z-index:11;height:calc(100vh - 45px);width:200px;}.src main,.rustdoc.src .sidebar{top:0;padding:0;height:100vh;border:0;}.src .search-form{margin-left:40px;}.hide-sidebar .search-form{margin-left:32px;}.hide-sidebar .src .search-form{margin-left:0;}.sidebar.shown,.src-sidebar-expanded .src .sidebar,.rustdoc:not(.src) .sidebar:focus-within{left:0;}.mobile-topbar h2{padding-bottom:0;margin:auto 0.5em auto auto;overflow:hidden;font-size:24px;}.mobile-topbar h2 a{display:block;text-overflow:ellipsis;overflow:hidden;white-space:nowrap;}.mobile-topbar .logo-container>img{max-width:35px;max-height:35px;margin:5px 0 5px 20px;}.mobile-topbar{display:flex;flex-direction:row;position:sticky;z-index:10;font-size:2rem;height:45px;width:100%;left:0;top:0;}.hide-sidebar .mobile-topbar{display:none;}.sidebar-menu-toggle{width:45px;border:none;line-height:0;}.hide-sidebar .sidebar-menu-toggle{display:none;}.sidebar-elems{margin-top:1em;}.anchor{display:none !important;}#main-content>details.toggle>summary::before,#main-content>div>details.toggle>summary::before{left:-11px;}#copy-path,#help-button{display:none;}#sidebar-button>a:before{content:url('data:image/svg+xml,\ + \ + \ + ');width:22px;height:22px;}.sidebar-menu-toggle:before{filter:var(--mobile-sidebar-menu-filter);}.sidebar-menu-toggle:hover{background:var(--main-background-color);}.item-table,.item-row,.item-table>li,.item-table>li>div,.search-results>a,.search-results>a>div{display:block;}.search-results>a{padding:5px 0px;}.search-results>a>div.desc,.item-table>li>div.desc{padding-left:2em;}.search-results .result-name{display:block;}.search-results .result-name .typename{width:initial;margin-right:0;}.search-results .result-name .typename,.search-results .result-name .path{display:inline;}.src-sidebar-expanded .src .sidebar{position:fixed;max-width:100vw;width:100vw;}.src .src-sidebar-title{padding-top:0;}details.toggle:not(.top-doc)>summary{margin-left:10px;}.impl-items>details.toggle>summary:not(.hideme)::before,#main-content>details.toggle:not(.top-doc)>summary::before,#main-content>div>details.toggle>summary::before{left:-11px;}.impl-items>.item-info{margin-left:34px;}.src nav.sub{margin:0;padding:var(--nav-sub-mobile-padding);}}@media (min-width:701px){.scraped-example-title{position:absolute;z-index:10;background:var(--main-background-color);bottom:8px;right:5px;padding:2px 4px;box-shadow:0 0 4px var(--main-background-color);}}@media print{nav.sidebar,nav.sub,.out-of-band,a.src,#copy-path,details.toggle[open] >summary::before,details.toggle>summary::before,details.toggle.top-doc>summary{display:none;}.docblock{margin-left:0;}main{padding:10px;}}@media (max-width:464px){.docblock{margin-left:12px;}.docblock code{overflow-wrap:break-word;overflow-wrap:anywhere;}nav.sub{flex-direction:column;}.search-form{align-self:stretch;}}.variant,.implementors-toggle>summary,.impl,#implementors-list>.docblock,.impl-items>section,.impl-items>.toggle>summary,.methods>section,.methods>.toggle>summary{margin-bottom:0.75em;}.variants>.docblock,.implementors-toggle>.docblock,.impl-items>.toggle[open]:not(:last-child),.methods>.toggle[open]:not(:last-child),.implementors-toggle[open]:not(:last-child){margin-bottom:2em;}#trait-implementations-list .impl-items>.toggle:not(:last-child),#synthetic-implementations-list .impl-items>.toggle:not(:last-child),#blanket-implementations-list .impl-items>.toggle:not(:last-child){margin-bottom:1em;}.scraped-example-list .scrape-help{margin-left:10px;padding:0 4px;font-weight:normal;font-size:12px;position:relative;bottom:1px;border:1px solid var(--scrape-example-help-border-color);border-radius:50px;color:var(--scrape-example-help-color);}.scraped-example-list .scrape-help:hover{border-color:var(--scrape-example-help-hover-border-color);color:var(--scrape-example-help-hover-color);}.scraped-example{position:relative;}.scraped-example .code-wrapper{position:relative;display:flex;flex-direction:row;flex-wrap:wrap;width:100%;}.scraped-example:not(.expanded) .code-wrapper{max-height:calc(1.5em * 5 + 10px);}.scraped-example:not(.expanded) .code-wrapper pre{overflow-y:hidden;padding-bottom:0;max-height:calc(1.5em * 5 + 10px);}.more-scraped-examples .scraped-example:not(.expanded) .code-wrapper,.more-scraped-examples .scraped-example:not(.expanded) .code-wrapper pre{max-height:calc(1.5em * 10 + 10px);}.scraped-example .code-wrapper .next,.scraped-example .code-wrapper .prev,.scraped-example .code-wrapper .expand{color:var(--main-color);position:absolute;top:0.25em;z-index:1;padding:0;background:none;border:none;-webkit-appearance:none;opacity:1;}.scraped-example .code-wrapper .prev{right:2.25em;}.scraped-example .code-wrapper .next{right:1.25em;}.scraped-example .code-wrapper .expand{right:0.25em;}.scraped-example:not(.expanded) .code-wrapper::before,.scraped-example:not(.expanded) .code-wrapper::after{content:" ";width:100%;height:5px;position:absolute;z-index:1;}.scraped-example:not(.expanded) .code-wrapper::before{top:0;background:linear-gradient(to bottom,var(--scrape-example-code-wrapper-background-start),var(--scrape-example-code-wrapper-background-end));}.scraped-example:not(.expanded) .code-wrapper::after{bottom:0;background:linear-gradient(to top,var(--scrape-example-code-wrapper-background-start),var(--scrape-example-code-wrapper-background-end));}.scraped-example .code-wrapper .example-wrap{width:100%;overflow-y:hidden;margin-bottom:0;}.scraped-example:not(.expanded) .code-wrapper .example-wrap{overflow-x:hidden;}.scraped-example .example-wrap .rust span.highlight{background:var(--scrape-example-code-line-highlight);}.scraped-example .example-wrap .rust span.highlight.focus{background:var(--scrape-example-code-line-highlight-focus);}.more-examples-toggle{max-width:calc(100% + 25px);margin-top:10px;margin-left:-25px;}.more-examples-toggle .hide-more{margin-left:25px;cursor:pointer;}.more-scraped-examples{margin-left:25px;position:relative;}.toggle-line{position:absolute;top:5px;bottom:0;right:calc(100% + 10px);padding:0 4px;cursor:pointer;}.toggle-line-inner{min-width:2px;height:100%;background:var(--scrape-example-toggle-line-background);}.toggle-line:hover .toggle-line-inner{background:var(--scrape-example-toggle-line-hover-background);}.more-scraped-examples .scraped-example,.example-links{margin-top:20px;}.more-scraped-examples .scraped-example:first-child{margin-top:5px;}.example-links ul{margin-bottom:0;}:root[data-theme="light"]{--main-background-color:white;--main-color:black;--settings-input-color:#2196f3;--settings-input-border-color:#717171;--settings-button-color:#000;--settings-button-border-focus:#717171;--sidebar-background-color:#f5f5f5;--sidebar-background-color-hover:#e0e0e0;--code-block-background-color:#f5f5f5;--scrollbar-track-background-color:#dcdcdc;--scrollbar-thumb-background-color:rgba(36,37,39,0.6);--scrollbar-color:rgba(36,37,39,0.6) #d9d9d9;--headings-border-bottom-color:#ddd;--border-color:#e0e0e0;--button-background-color:#fff;--right-side-color:grey;--code-attribute-color:#999;--toggles-color:#999;--toggle-filter:none;--mobile-sidebar-menu-filter:none;--search-input-focused-border-color:#66afe9;--copy-path-button-color:#999;--copy-path-img-filter:invert(50%);--copy-path-img-hover-filter:invert(35%);--codeblock-error-hover-color:rgb(255,0,0);--codeblock-error-color:rgba(255,0,0,.5);--codeblock-ignore-hover-color:rgb(255,142,0);--codeblock-ignore-color:rgba(255,142,0,.6);--warning-border-color:#ff8e00;--type-link-color:#ad378a;--trait-link-color:#6e4fc9;--assoc-item-link-color:#3873ad;--function-link-color:#ad7c37;--macro-link-color:#068000;--keyword-link-color:#3873ad;--mod-link-color:#3873ad;--link-color:#3873ad;--sidebar-link-color:#356da4;--sidebar-current-link-background-color:#fff;--search-result-link-focus-background-color:#ccc;--search-result-border-color:#aaa3;--search-color:#000;--search-error-code-background-color:#d0cccc;--search-results-alias-color:#000;--search-results-grey-color:#999;--search-tab-title-count-color:#888;--search-tab-button-not-selected-border-top-color:#e6e6e6;--search-tab-button-not-selected-background:#e6e6e6;--search-tab-button-selected-border-top-color:#0089ff;--search-tab-button-selected-background:#fff;--stab-background-color:#fff5d6;--stab-code-color:#000;--code-highlight-kw-color:#8959a8;--code-highlight-kw-2-color:#4271ae;--code-highlight-lifetime-color:#b76514;--code-highlight-prelude-color:#4271ae;--code-highlight-prelude-val-color:#c82829;--code-highlight-number-color:#718c00;--code-highlight-string-color:#718c00;--code-highlight-literal-color:#c82829;--code-highlight-attribute-color:#c82829;--code-highlight-self-color:#c82829;--code-highlight-macro-color:#3e999f;--code-highlight-question-mark-color:#ff9011;--code-highlight-comment-color:#8e908c;--code-highlight-doc-comment-color:#4d4d4c;--src-line-numbers-span-color:#c67e2d;--src-line-number-highlighted-background-color:#fdffd3;--test-arrow-color:#f5f5f5;--test-arrow-background-color:rgba(78,139,202,0.2);--test-arrow-hover-color:#f5f5f5;--test-arrow-hover-background-color:rgb(78,139,202);--target-background-color:#fdffd3;--target-border-color:#ad7c37;--kbd-color:#000;--kbd-background:#fafbfc;--kbd-box-shadow-color:#c6cbd1;--rust-logo-filter:initial;--crate-search-div-filter:invert(100%) sepia(0%) saturate(4223%) hue-rotate(289deg) brightness(114%) contrast(76%);--crate-search-div-hover-filter:invert(44%) sepia(18%) saturate(23%) hue-rotate(317deg) brightness(96%) contrast(93%);--crate-search-hover-border:#717171;--src-sidebar-background-selected:#fff;--src-sidebar-background-hover:#e0e0e0;--table-alt-row-background-color:#f5f5f5;--codeblock-link-background:#eee;--scrape-example-toggle-line-background:#ccc;--scrape-example-toggle-line-hover-background:#999;--scrape-example-code-line-highlight:#fcffd6;--scrape-example-code-line-highlight-focus:#f6fdb0;--scrape-example-help-border-color:#555;--scrape-example-help-color:#333;--scrape-example-help-hover-border-color:#000;--scrape-example-help-hover-color:#000;--scrape-example-code-wrapper-background-start:rgba(255,255,255,1);--scrape-example-code-wrapper-background-end:rgba(255,255,255,0);--sidebar-resizer-hover:hsl(207,90%,66%);--sidebar-resizer-active:hsl(207,90%,54%);}:root[data-theme="dark"]{--main-background-color:#353535;--main-color:#ddd;--settings-input-color:#2196f3;--settings-input-border-color:#999;--settings-button-color:#000;--settings-button-border-focus:#ffb900;--sidebar-background-color:#505050;--sidebar-background-color-hover:#676767;--code-block-background-color:#2A2A2A;--scrollbar-track-background-color:#717171;--scrollbar-thumb-background-color:rgba(32,34,37,.6);--scrollbar-color:rgba(32,34,37,.6) #5a5a5a;--headings-border-bottom-color:#d2d2d2;--border-color:#e0e0e0;--button-background-color:#f0f0f0;--right-side-color:grey;--code-attribute-color:#999;--toggles-color:#999;--toggle-filter:invert(100%);--mobile-sidebar-menu-filter:invert(100%);--search-input-focused-border-color:#008dfd;--copy-path-button-color:#999;--copy-path-img-filter:invert(50%);--copy-path-img-hover-filter:invert(65%);--codeblock-error-hover-color:rgb(255,0,0);--codeblock-error-color:rgba(255,0,0,.5);--codeblock-ignore-hover-color:rgb(255,142,0);--codeblock-ignore-color:rgba(255,142,0,.6);--warning-border-color:#ff8e00;--type-link-color:#2dbfb8;--trait-link-color:#b78cf2;--assoc-item-link-color:#d2991d;--function-link-color:#2bab63;--macro-link-color:#09bd00;--keyword-link-color:#d2991d;--mod-link-color:#d2991d;--link-color:#d2991d;--sidebar-link-color:#fdbf35;--sidebar-current-link-background-color:#444;--search-result-link-focus-background-color:#616161;--search-result-border-color:#aaa3;--search-color:#111;--search-error-code-background-color:#484848;--search-results-alias-color:#fff;--search-results-grey-color:#ccc;--search-tab-title-count-color:#888;--search-tab-button-not-selected-border-top-color:#252525;--search-tab-button-not-selected-background:#252525;--search-tab-button-selected-border-top-color:#0089ff;--search-tab-button-selected-background:#353535;--stab-background-color:#314559;--stab-code-color:#e6e1cf;--code-highlight-kw-color:#ab8ac1;--code-highlight-kw-2-color:#769acb;--code-highlight-lifetime-color:#d97f26;--code-highlight-prelude-color:#769acb;--code-highlight-prelude-val-color:#ee6868;--code-highlight-number-color:#83a300;--code-highlight-string-color:#83a300;--code-highlight-literal-color:#ee6868;--code-highlight-attribute-color:#ee6868;--code-highlight-self-color:#ee6868;--code-highlight-macro-color:#3e999f;--code-highlight-question-mark-color:#ff9011;--code-highlight-comment-color:#8d8d8b;--code-highlight-doc-comment-color:#8ca375;--src-line-numbers-span-color:#3b91e2;--src-line-number-highlighted-background-color:#0a042f;--test-arrow-color:#dedede;--test-arrow-background-color:rgba(78,139,202,0.2);--test-arrow-hover-color:#dedede;--test-arrow-hover-background-color:#4e8bca;--target-background-color:#494a3d;--target-border-color:#bb7410;--kbd-color:#000;--kbd-background:#fafbfc;--kbd-box-shadow-color:#c6cbd1;--rust-logo-filter:drop-shadow(1px 0 0px #fff) drop-shadow(0 1px 0 #fff) drop-shadow(-1px 0 0 #fff) drop-shadow(0 -1px 0 #fff);--crate-search-div-filter:invert(94%) sepia(0%) saturate(721%) hue-rotate(255deg) brightness(90%) contrast(90%);--crate-search-div-hover-filter:invert(69%) sepia(60%) saturate(6613%) hue-rotate(184deg) brightness(100%) contrast(91%);--crate-search-hover-border:#2196f3;--src-sidebar-background-selected:#333;--src-sidebar-background-hover:#444;--table-alt-row-background-color:#2a2a2a;--codeblock-link-background:#333;--scrape-example-toggle-line-background:#999;--scrape-example-toggle-line-hover-background:#c5c5c5;--scrape-example-code-line-highlight:#5b3b01;--scrape-example-code-line-highlight-focus:#7c4b0f;--scrape-example-help-border-color:#aaa;--scrape-example-help-color:#eee;--scrape-example-help-hover-border-color:#fff;--scrape-example-help-hover-color:#fff;--scrape-example-code-wrapper-background-start:rgba(53,53,53,1);--scrape-example-code-wrapper-background-end:rgba(53,53,53,0);--sidebar-resizer-hover:hsl(207,30%,54%);--sidebar-resizer-active:hsl(207,90%,54%);}:root[data-theme="ayu"]{--main-background-color:#0f1419;--main-color:#c5c5c5;--settings-input-color:#ffb454;--settings-input-border-color:#999;--settings-button-color:#fff;--settings-button-border-focus:#e0e0e0;--sidebar-background-color:#14191f;--sidebar-background-color-hover:rgba(70,70,70,0.33);--code-block-background-color:#191f26;--scrollbar-track-background-color:transparent;--scrollbar-thumb-background-color:#5c6773;--scrollbar-color:#5c6773 #24292f;--headings-border-bottom-color:#5c6773;--border-color:#5c6773;--button-background-color:#141920;--right-side-color:grey;--code-attribute-color:#999;--toggles-color:#999;--toggle-filter:invert(100%);--mobile-sidebar-menu-filter:invert(100%);--search-input-focused-border-color:#5c6773;--copy-path-button-color:#fff;--copy-path-img-filter:invert(70%);--copy-path-img-hover-filter:invert(100%);--codeblock-error-hover-color:rgb(255,0,0);--codeblock-error-color:rgba(255,0,0,.5);--codeblock-ignore-hover-color:rgb(255,142,0);--codeblock-ignore-color:rgba(255,142,0,.6);--warning-border-color:#ff8e00;--type-link-color:#ffa0a5;--trait-link-color:#39afd7;--assoc-item-link-color:#39afd7;--function-link-color:#fdd687;--macro-link-color:#a37acc;--keyword-link-color:#39afd7;--mod-link-color:#39afd7;--link-color:#39afd7;--sidebar-link-color:#53b1db;--sidebar-current-link-background-color:transparent;--search-result-link-focus-background-color:#3c3c3c;--search-result-border-color:#aaa3;--search-color:#fff;--search-error-code-background-color:#4f4c4c;--search-results-alias-color:#c5c5c5;--search-results-grey-color:#999;--search-tab-title-count-color:#888;--search-tab-button-not-selected-border-top-color:none;--search-tab-button-not-selected-background:transparent !important;--search-tab-button-selected-border-top-color:none;--search-tab-button-selected-background:#141920 !important;--stab-background-color:#314559;--stab-code-color:#e6e1cf;--code-highlight-kw-color:#ff7733;--code-highlight-kw-2-color:#ff7733;--code-highlight-lifetime-color:#ff7733;--code-highlight-prelude-color:#69f2df;--code-highlight-prelude-val-color:#ff7733;--code-highlight-number-color:#b8cc52;--code-highlight-string-color:#b8cc52;--code-highlight-literal-color:#ff7733;--code-highlight-attribute-color:#e6e1cf;--code-highlight-self-color:#36a3d9;--code-highlight-macro-color:#a37acc;--code-highlight-question-mark-color:#ff9011;--code-highlight-comment-color:#788797;--code-highlight-doc-comment-color:#a1ac88;--src-line-numbers-span-color:#5c6773;--src-line-number-highlighted-background-color:rgba(255,236,164,0.06);--test-arrow-color:#788797;--test-arrow-background-color:rgba(57,175,215,0.09);--test-arrow-hover-color:#c5c5c5;--test-arrow-hover-background-color:rgba(57,175,215,0.368);--target-background-color:rgba(255,236,164,0.06);--target-border-color:rgba(255,180,76,0.85);--kbd-color:#c5c5c5;--kbd-background:#314559;--kbd-box-shadow-color:#5c6773;--rust-logo-filter:drop-shadow(1px 0 0px #fff) drop-shadow(0 1px 0 #fff) drop-shadow(-1px 0 0 #fff) drop-shadow(0 -1px 0 #fff);--crate-search-div-filter:invert(41%) sepia(12%) saturate(487%) hue-rotate(171deg) brightness(94%) contrast(94%);--crate-search-div-hover-filter:invert(98%) sepia(12%) saturate(81%) hue-rotate(343deg) brightness(113%) contrast(76%);--crate-search-hover-border:#e0e0e0;--src-sidebar-background-selected:#14191f;--src-sidebar-background-hover:#14191f;--table-alt-row-background-color:#191f26;--codeblock-link-background:#333;--scrape-example-toggle-line-background:#999;--scrape-example-toggle-line-hover-background:#c5c5c5;--scrape-example-code-line-highlight:#5b3b01;--scrape-example-code-line-highlight-focus:#7c4b0f;--scrape-example-help-border-color:#aaa;--scrape-example-help-color:#eee;--scrape-example-help-hover-border-color:#fff;--scrape-example-help-hover-color:#fff;--scrape-example-code-wrapper-background-start:rgba(15,20,25,1);--scrape-example-code-wrapper-background-end:rgba(15,20,25,0);--sidebar-resizer-hover:hsl(34,50%,33%);--sidebar-resizer-active:hsl(34,100%,66%);}:root[data-theme="ayu"] h1,:root[data-theme="ayu"] h2,:root[data-theme="ayu"] h3,:root[data-theme="ayu"] h4,:where(:root[data-theme="ayu"]) h1 a,:root[data-theme="ayu"] .sidebar h2 a,:root[data-theme="ayu"] .sidebar h3 a{color:#fff;}:root[data-theme="ayu"] .docblock code{color:#ffb454;}:root[data-theme="ayu"] .docblock a>code{color:#39AFD7 !important;}:root[data-theme="ayu"] .code-header,:root[data-theme="ayu"] .docblock pre>code,:root[data-theme="ayu"] pre,:root[data-theme="ayu"] pre>code,:root[data-theme="ayu"] .item-info code,:root[data-theme="ayu"] .rustdoc.source .example-wrap{color:#e6e1cf;}:root[data-theme="ayu"] .sidebar .current,:root[data-theme="ayu"] .sidebar .current a,:root[data-theme="ayu"] .sidebar a:hover,:root[data-theme="ayu"] #src-sidebar div.files>a:hover,:root[data-theme="ayu"] details.dir-entry summary:hover,:root[data-theme="ayu"] #src-sidebar div.files>a:focus,:root[data-theme="ayu"] details.dir-entry summary:focus,:root[data-theme="ayu"] #src-sidebar div.files>a.selected{color:#ffb44c;}:root[data-theme="ayu"] .sidebar-elems .location{color:#ff7733;}:root[data-theme="ayu"] .src-line-numbers .line-highlighted{color:#708090;padding-right:7px;border-right:1px solid #ffb44c;}:root[data-theme="ayu"] .search-results a:hover,:root[data-theme="ayu"] .search-results a:focus{color:#fff !important;background-color:#3c3c3c;}:root[data-theme="ayu"] .search-results a{color:#0096cf;}:root[data-theme="ayu"] .search-results a div.desc{color:#c5c5c5;}:root[data-theme="ayu"] .result-name .primitive>i,:root[data-theme="ayu"] .result-name .keyword>i{color:#788797;}:root[data-theme="ayu"] #search-tabs>button.selected{border-bottom:1px solid #ffb44c !important;border-top:none;}:root[data-theme="ayu"] #search-tabs>button:not(.selected){border:none;background-color:transparent !important;}:root[data-theme="ayu"] #search-tabs>button:hover{border-bottom:1px solid rgba(242,151,24,0.3);}:root[data-theme="ayu"] #settings-menu>a img,:root[data-theme="ayu"] #sidebar-button>a:before{filter:invert(100);} \ No newline at end of file diff --git a/static.files/scrape-examples-ef1e698c1d417c0c.js b/static.files/scrape-examples-ef1e698c1d417c0c.js new file mode 100644 index 00000000..ba830e37 --- /dev/null +++ b/static.files/scrape-examples-ef1e698c1d417c0c.js @@ -0,0 +1 @@ +"use strict";(function(){const DEFAULT_MAX_LINES=5;const HIDDEN_MAX_LINES=10;function scrollToLoc(elt,loc,isHidden){const lines=elt.querySelector(".src-line-numbers");let scrollOffset;const maxLines=isHidden?HIDDEN_MAX_LINES:DEFAULT_MAX_LINES;if(loc[1]-loc[0]>maxLines){const line=Math.max(0,loc[0]-1);scrollOffset=lines.children[line].offsetTop}else{const wrapper=elt.querySelector(".code-wrapper");const halfHeight=wrapper.offsetHeight/2;const offsetTop=lines.children[loc[0]].offsetTop;const lastLine=lines.children[loc[1]];const offsetBot=lastLine.offsetTop+lastLine.offsetHeight;const offsetMid=(offsetTop+offsetBot)/2;scrollOffset=offsetMid-halfHeight}lines.scrollTo(0,scrollOffset);elt.querySelector(".rust").scrollTo(0,scrollOffset)}function updateScrapedExample(example,isHidden){const locs=JSON.parse(example.attributes.getNamedItem("data-locs").textContent);let locIndex=0;const highlights=Array.prototype.slice.call(example.querySelectorAll(".highlight"));const link=example.querySelector(".scraped-example-title a");if(locs.length>1){const onChangeLoc=changeIndex=>{removeClass(highlights[locIndex],"focus");changeIndex();scrollToLoc(example,locs[locIndex][0],isHidden);addClass(highlights[locIndex],"focus");const url=locs[locIndex][1];const title=locs[locIndex][2];link.href=url;link.innerHTML=title};example.querySelector(".prev").addEventListener("click",()=>{onChangeLoc(()=>{locIndex=(locIndex-1+locs.length)%locs.length})});example.querySelector(".next").addEventListener("click",()=>{onChangeLoc(()=>{locIndex=(locIndex+1)%locs.length})})}const expandButton=example.querySelector(".expand");if(expandButton){expandButton.addEventListener("click",()=>{if(hasClass(example,"expanded")){removeClass(example,"expanded");scrollToLoc(example,locs[0][0],isHidden)}else{addClass(example,"expanded")}})}scrollToLoc(example,locs[0][0],isHidden)}const firstExamples=document.querySelectorAll(".scraped-example-list > .scraped-example");onEachLazy(firstExamples,el=>updateScrapedExample(el,false));onEachLazy(document.querySelectorAll(".more-examples-toggle"),toggle=>{onEachLazy(toggle.querySelectorAll(".toggle-line, .hide-more"),button=>{button.addEventListener("click",()=>{toggle.open=false})});const moreExamples=toggle.querySelectorAll(".scraped-example");toggle.querySelector("summary").addEventListener("click",()=>{setTimeout(()=>{onEachLazy(moreExamples,el=>updateScrapedExample(el,true))})},{once:true})})})() \ No newline at end of file diff --git a/static.files/search-dd67cee4cfa65049.js b/static.files/search-dd67cee4cfa65049.js new file mode 100644 index 00000000..ef8bf865 --- /dev/null +++ b/static.files/search-dd67cee4cfa65049.js @@ -0,0 +1,5 @@ +"use strict";if(!Array.prototype.toSpliced){Array.prototype.toSpliced=function(){const me=this.slice();Array.prototype.splice.apply(me,arguments);return me}}(function(){const itemTypes=["keyword","primitive","mod","externcrate","import","struct","enum","fn","type","static","trait","impl","tymethod","method","structfield","variant","macro","associatedtype","constant","associatedconstant","union","foreigntype","existential","attr","derive","traitalias","generic",];const longItemTypes=["keyword","primitive type","module","extern crate","re-export","struct","enum","function","type alias","static","trait","","trait method","method","struct field","enum variant","macro","assoc type","constant","assoc const","union","foreign type","existential type","attribute macro","derive macro","trait alias",];const TY_GENERIC=itemTypes.indexOf("generic");const ROOT_PATH=typeof window!=="undefined"?window.rootPath:"../";function printTab(nb){let iter=0;let foundCurrentTab=false;let foundCurrentResultSet=false;onEachLazy(document.getElementById("search-tabs").childNodes,elem=>{if(nb===iter){addClass(elem,"selected");foundCurrentTab=true}else{removeClass(elem,"selected")}iter+=1});const isTypeSearch=(nb>0||iter===1);iter=0;onEachLazy(document.getElementById("results").childNodes,elem=>{if(nb===iter){addClass(elem,"active");foundCurrentResultSet=true}else{removeClass(elem,"active")}iter+=1});if(foundCurrentTab&&foundCurrentResultSet){searchState.currentTab=nb;const correctionsElem=document.getElementsByClassName("search-corrections");if(isTypeSearch){removeClass(correctionsElem[0],"hidden")}else{addClass(correctionsElem[0],"hidden")}}else if(nb!==0){printTab(0)}}const editDistanceState={current:[],prev:[],prevPrev:[],calculate:function calculate(a,b,limit){if(a.lengthlimit){return limit+1}while(b.length>0&&b[0]===a[0]){a=a.substring(1);b=b.substring(1)}while(b.length>0&&b[b.length-1]===a[a.length-1]){a=a.substring(0,a.length-1);b=b.substring(0,b.length-1)}if(b.length===0){return minDist}const aLength=a.length;const bLength=b.length;for(let i=0;i<=bLength;++i){this.current[i]=0;this.prev[i]=i;this.prevPrev[i]=Number.MAX_VALUE}for(let i=1;i<=aLength;++i){this.current[0]=i;const aIdx=i-1;for(let j=1;j<=bLength;++j){const bIdx=j-1;const substitutionCost=a[aIdx]===b[bIdx]?0:1;this.current[j]=Math.min(this.prev[j]+1,this.current[j-1]+1,this.prev[j-1]+substitutionCost);if((i>1)&&(j>1)&&(a[aIdx]===b[bIdx-1])&&(a[aIdx-1]===b[bIdx])){this.current[j]=Math.min(this.current[j],this.prevPrev[j-2]+1)}}const prevPrevTmp=this.prevPrev;this.prevPrev=this.prev;this.prev=this.current;this.current=prevPrevTmp}const distance=this.prev[bLength];return distance<=limit?distance:(limit+1)},};function editDistance(a,b,limit){return editDistanceState.calculate(a,b,limit)}function initSearch(rawSearchIndex){const MAX_RESULTS=200;const NO_TYPE_FILTER=-1;let searchIndex;let functionTypeFingerprint;let currentResults;let typeNameIdMap;const ALIASES=new Map();let typeNameIdOfArray;let typeNameIdOfSlice;let typeNameIdOfArrayOrSlice;let typeNameIdOfTuple;let typeNameIdOfUnit;let typeNameIdOfTupleOrUnit;function buildTypeMapIndex(name,isAssocType){if(name===""||name===null){return null}if(typeNameIdMap.has(name)){const obj=typeNameIdMap.get(name);obj.assocOnly=isAssocType&&obj.assocOnly;return obj.id}else{const id=typeNameIdMap.size;typeNameIdMap.set(name,{id,assocOnly:isAssocType});return id}}function isSpecialStartCharacter(c){return"<\"".indexOf(c)!==-1}function isEndCharacter(c){return"=,>-])".indexOf(c)!==-1}function itemTypeFromName(typename){const index=itemTypes.findIndex(i=>i===typename);if(index<0){throw["Unknown type filter ",typename]}return index}function getStringElem(query,parserState,isInGenerics){if(isInGenerics){throw["Unexpected ","\""," in generics"]}else if(query.literalSearch){throw["Cannot have more than one literal search element"]}else if(parserState.totalElems-parserState.genericsElems>0){throw["Cannot use literal search when there is more than one element"]}parserState.pos+=1;const start=parserState.pos;const end=getIdentEndPosition(parserState);if(parserState.pos>=parserState.length){throw["Unclosed ","\""]}else if(parserState.userQuery[end]!=="\""){throw["Unexpected ",parserState.userQuery[end]," in a string element"]}else if(start===end){throw["Cannot have empty string element"]}parserState.pos+=1;query.literalSearch=true}function isPathStart(parserState){return parserState.userQuery.slice(parserState.pos,parserState.pos+2)==="::"}function isReturnArrow(parserState){return parserState.userQuery.slice(parserState.pos,parserState.pos+2)==="->"}function isIdentCharacter(c){return(c==="_"||(c>="0"&&c<="9")||(c>="a"&&c<="z")||(c>="A"&&c<="Z"))}function isSeparatorCharacter(c){return c===","||c==="="}function isPathSeparator(c){return c===":"||c===" "}function prevIs(parserState,lookingFor){let pos=parserState.pos;while(pos>0){const c=parserState.userQuery[pos-1];if(c===lookingFor){return true}else if(c!==" "){break}pos-=1}return false}function isLastElemGeneric(elems,parserState){return(elems.length>0&&elems[elems.length-1].generics.length>0)||prevIs(parserState,">")}function skipWhitespace(parserState){while(parserState.pos0){throw["Cannot have more than one element if you use quotes"]}const typeFilter=parserState.typeFilter;parserState.typeFilter=null;if(name==="!"){if(typeFilter!==null&&typeFilter!=="primitive"){throw["Invalid search type: primitive never type ","!"," and ",typeFilter," both specified",]}if(generics.length!==0){throw["Never type ","!"," does not accept generic parameters",]}const bindingName=parserState.isInBinding;parserState.isInBinding=null;return{name:"never",id:null,fullPath:["never"],pathWithoutLast:[],pathLast:"never",normalizedPathLast:"never",generics:[],bindings:new Map(),typeFilter:"primitive",bindingName,}}const quadcolon=/::\s*::/.exec(path);if(path.startsWith("::")){throw["Paths cannot start with ","::"]}else if(path.endsWith("::")){throw["Paths cannot end with ","::"]}else if(quadcolon!==null){throw["Unexpected ",quadcolon[0]]}const pathSegments=path.split(/(?:::\s*)|(?:\s+(?:::\s*)?)/);if(pathSegments.length===0||(pathSegments.length===1&&pathSegments[0]==="")){if(generics.length>0||prevIs(parserState,">")){throw["Found generics without a path"]}else{throw["Unexpected ",parserState.userQuery[parserState.pos]]}}for(const[i,pathSegment]of pathSegments.entries()){if(pathSegment==="!"){if(i!==0){throw["Never type ","!"," is not associated item"]}pathSegments[i]="never"}}parserState.totalElems+=1;if(isInGenerics){parserState.genericsElems+=1}const bindingName=parserState.isInBinding;parserState.isInBinding=null;const bindings=new Map();const pathLast=pathSegments[pathSegments.length-1];return{name:name.trim(),id:null,fullPath:pathSegments,pathWithoutLast:pathSegments.slice(0,pathSegments.length-1),pathLast,normalizedPathLast:pathLast.replace(/_/g,""),generics:generics.filter(gen=>{if(gen.bindingName!==null){bindings.set(gen.bindingName.name,[gen,...gen.bindingName.generics]);return false}return true}),bindings,typeFilter,bindingName,}}function getIdentEndPosition(parserState){const start=parserState.pos;let end=parserState.pos;let foundExclamation=-1;while(parserState.pos0){throw["Unexpected ",c," after ",parserState.userQuery[parserState.pos-1]]}else{throw["Unexpected ",c]}}parserState.pos+=1;end=parserState.pos}if(foundExclamation!==-1&&foundExclamation!==start&&isIdentCharacter(parserState.userQuery[foundExclamation-1])){if(parserState.typeFilter===null){parserState.typeFilter="macro"}else if(parserState.typeFilter!=="macro"){throw["Invalid search type: macro ","!"," and ",parserState.typeFilter," both specified",]}end=foundExclamation}return end}function getNextElem(query,parserState,elems,isInGenerics){const generics=[];skipWhitespace(parserState);let start=parserState.pos;let end;if("[(".indexOf(parserState.userQuery[parserState.pos])!==-1){let endChar=")";let name="()";let friendlyName="tuple";if(parserState.userQuery[parserState.pos]==="["){endChar="]";name="[]";friendlyName="slice"}parserState.pos+=1;const{foundSeparator}=getItemsBefore(query,parserState,generics,endChar);const typeFilter=parserState.typeFilter;const isInBinding=parserState.isInBinding;if(typeFilter!==null&&typeFilter!=="primitive"){throw["Invalid search type: primitive ",name," and ",typeFilter," both specified",]}parserState.typeFilter=null;parserState.isInBinding=null;for(const gen of generics){if(gen.bindingName!==null){throw["Type parameter ","=",` cannot be within ${friendlyName} `,name]}}if(name==="()"&&!foundSeparator&&generics.length===1&&typeFilter===null){elems.push(generics[0])}else{parserState.totalElems+=1;if(isInGenerics){parserState.genericsElems+=1}elems.push({name:name,id:null,fullPath:[name],pathWithoutLast:[],pathLast:name,normalizedPathLast:name,generics,bindings:new Map(),typeFilter:"primitive",bindingName:isInBinding,})}}else{const isStringElem=parserState.userQuery[start]==="\"";if(isStringElem){start+=1;getStringElem(query,parserState,isInGenerics);end=parserState.pos-1}else{end=getIdentEndPosition(parserState)}if(parserState.pos=end){throw["Found generics without a path"]}parserState.pos+=1;getItemsBefore(query,parserState,generics,">")}if(isStringElem){skipWhitespace(parserState)}if(start>=end&&generics.length===0){return}if(parserState.userQuery[parserState.pos]==="="){if(parserState.isInBinding){throw["Cannot write ","="," twice in a binding"]}if(!isInGenerics){throw["Type parameter ","="," must be within generics list"]}const name=parserState.userQuery.slice(start,end).trim();if(name==="!"){throw["Type parameter ","="," key cannot be ","!"," never type"]}if(name.includes("!")){throw["Type parameter ","="," key cannot be ","!"," macro"]}if(name.includes("::")){throw["Type parameter ","="," key cannot contain ","::"," path"]}if(name.includes(":")){throw["Type parameter ","="," key cannot contain ",":"," type"]}parserState.isInBinding={name,generics}}else{elems.push(createQueryElement(query,parserState,parserState.userQuery.slice(start,end),generics,isInGenerics))}}}function getItemsBefore(query,parserState,elems,endChar){let foundStopChar=true;let foundSeparator=false;let start=parserState.pos;const oldTypeFilter=parserState.typeFilter;parserState.typeFilter=null;const oldIsInBinding=parserState.isInBinding;parserState.isInBinding=null;let extra="";if(endChar===">"){extra="<"}else if(endChar==="]"){extra="["}else if(endChar===")"){extra="("}else if(endChar===""){extra="->"}else{extra=endChar}while(parserState.pos"]}else if(prevIs(parserState,"\"")){throw["Cannot have more than one element if you use quotes"]}if(endChar!==""){throw["Expected ",",",", ","=",", or ",endChar,...extra,", found ",c,]}throw["Expected ",","," or ","=",...extra,", found ",c,]}const posBefore=parserState.pos;start=parserState.pos;getNextElem(query,parserState,elems,endChar!=="");if(endChar!==""&&parserState.pos>=parserState.length){throw["Unclosed ",extra]}if(posBefore===parserState.pos){parserState.pos+=1}foundStopChar=false}if(parserState.pos>=parserState.length&&endChar!==""){throw["Unclosed ",extra]}parserState.pos+=1;parserState.typeFilter=oldTypeFilter;parserState.isInBinding=oldIsInBinding;return{foundSeparator}}function checkExtraTypeFilterCharacters(start,parserState){const query=parserState.userQuery.slice(start,parserState.pos).trim();for(const c in query){if(!isIdentCharacter(query[c])){throw["Unexpected ",query[c]," in type filter (before ",":",")",]}}}function parseInput(query,parserState){let foundStopChar=true;let start=parserState.pos;while(parserState.pos"){if(isReturnArrow(parserState)){break}throw["Unexpected ",c," (did you mean ","->","?)"]}else if(parserState.pos>0){throw["Unexpected ",c," after ",parserState.userQuery[parserState.pos-1]]}throw["Unexpected ",c]}else if(c===":"&&!isPathStart(parserState)){if(parserState.typeFilter!==null){throw["Unexpected ",":"," (expected path after type filter ",parserState.typeFilter+":",")",]}else if(query.elems.length===0){throw["Expected type filter before ",":"]}else if(query.literalSearch){throw["Cannot use quotes on type filter"]}const typeFilterElem=query.elems.pop();checkExtraTypeFilterCharacters(start,parserState);parserState.typeFilter=typeFilterElem.name;parserState.pos+=1;parserState.totalElems-=1;query.literalSearch=false;foundStopChar=true;continue}else if(c===" "){skipWhitespace(parserState);continue}if(!foundStopChar){let extra="";if(isLastElemGeneric(query.elems,parserState)){extra=[" after ",">"]}else if(prevIs(parserState,"\"")){throw["Cannot have more than one element if you use quotes"]}if(parserState.typeFilter!==null){throw["Expected ",","," or ","->",...extra,", found ",c,]}throw["Expected ",",",", ",":"," or ","->",...extra,", found ",c,]}const before=query.elems.length;start=parserState.pos;getNextElem(query,parserState,query.elems,false);if(query.elems.length===before){parserState.pos+=1}foundStopChar=false}if(parserState.typeFilter!==null){throw["Unexpected ",":"," (expected path after type filter ",parserState.typeFilter+":",")",]}while(parserState.pos"]}break}else{parserState.pos+=1}}}function newParsedQuery(userQuery){return{original:userQuery,userQuery:userQuery.toLowerCase(),elems:[],returned:[],foundElems:0,totalElems:0,literalSearch:false,error:null,correction:null,proposeCorrectionFrom:null,proposeCorrectionTo:null,typeFingerprint:new Uint32Array(4),}}function buildUrl(search,filterCrates){let extra="?search="+encodeURIComponent(search);if(filterCrates!==null){extra+="&filter-crate="+encodeURIComponent(filterCrates)}return getNakedUrl()+extra+window.location.hash}function getFilterCrates(){const elem=document.getElementById("crate-search");if(elem&&elem.value!=="all crates"&&rawSearchIndex.has(elem.value)){return elem.value}return null}function parseQuery(userQuery){function convertTypeFilterOnElem(elem){if(elem.typeFilter!==null){let typeFilter=elem.typeFilter;if(typeFilter==="const"){typeFilter="constant"}elem.typeFilter=itemTypeFromName(typeFilter)}else{elem.typeFilter=NO_TYPE_FILTER}for(const elem2 of elem.generics){convertTypeFilterOnElem(elem2)}for(const constraints of elem.bindings.values()){for(const constraint of constraints){convertTypeFilterOnElem(constraint)}}}userQuery=userQuery.trim().replace(/\r|\n|\t/g," ");const parserState={length:userQuery.length,pos:0,totalElems:0,genericsElems:0,typeFilter:null,isInBinding:null,userQuery:userQuery.toLowerCase(),};let query=newParsedQuery(userQuery);try{parseInput(query,parserState);for(const elem of query.elems){convertTypeFilterOnElem(elem)}for(const elem of query.returned){convertTypeFilterOnElem(elem)}}catch(err){query=newParsedQuery(userQuery);query.error=err;return query}if(!query.literalSearch){query.literalSearch=parserState.totalElems>1}query.foundElems=query.elems.length+query.returned.length;query.totalElems=parserState.totalElems;return query}function createQueryResults(results_in_args,results_returned,results_others,parsedQuery){return{"in_args":results_in_args,"returned":results_returned,"others":results_others,"query":parsedQuery,}}function execQuery(parsedQuery,filterCrates,currentCrate){const results_others=new Map(),results_in_args=new Map(),results_returned=new Map();function transformResults(results){const duplicates=new Set();const out=[];for(const result of results){if(result.id!==-1){const obj=searchIndex[result.id];obj.dist=result.dist;const res=buildHrefAndPath(obj);obj.displayPath=pathSplitter(res[0]);obj.fullPath=obj.displayPath+obj.name;obj.fullPath+="|"+obj.ty;if(duplicates.has(obj.fullPath)){continue}duplicates.add(obj.fullPath);obj.href=res[1];out.push(obj);if(out.length>=MAX_RESULTS){break}}}return out}function sortResults(results,isType,preferredCrate){if(results.size===0){return[]}const userQuery=parsedQuery.userQuery;const result_list=[];for(const result of results.values()){result.item=searchIndex[result.id];result.word=searchIndex[result.id].word;result_list.push(result)}result_list.sort((aaa,bbb)=>{let a,b;a=(aaa.word!==userQuery);b=(bbb.word!==userQuery);if(a!==b){return a-b}a=(aaa.index<0);b=(bbb.index<0);if(a!==b){return a-b}a=aaa.path_dist;b=bbb.path_dist;if(a!==b){return a-b}a=aaa.index;b=bbb.index;if(a!==b){return a-b}a=(aaa.dist);b=(bbb.dist);if(a!==b){return a-b}a=aaa.item.deprecated;b=bbb.item.deprecated;if(a!==b){return a-b}a=(aaa.item.crate!==preferredCrate);b=(bbb.item.crate!==preferredCrate);if(a!==b){return a-b}a=aaa.word.length;b=bbb.word.length;if(a!==b){return a-b}a=aaa.word;b=bbb.word;if(a!==b){return(a>b?+1:-1)}a=(aaa.item.desc==="");b=(bbb.item.desc==="");if(a!==b){return a-b}a=aaa.item.ty;b=bbb.item.ty;if(a!==b){return a-b}a=aaa.item.path;b=bbb.item.path;if(a!==b){return(a>b?+1:-1)}return 0});return transformResults(result_list)}function unifyFunctionTypes(fnTypesIn,queryElems,whereClause,mgensIn,solutionCb){const mgens=mgensIn===null?null:new Map(mgensIn);if(queryElems.length===0){return!solutionCb||solutionCb(mgens)}if(!fnTypesIn||fnTypesIn.length===0){return false}const ql=queryElems.length;const fl=fnTypesIn.length;if(ql===1&&queryElems[0].generics.length===0&&queryElems[0].bindings.size===0){const queryElem=queryElems[0];for(const fnType of fnTypesIn){if(!unifyFunctionTypeIsMatchCandidate(fnType,queryElem,whereClause,mgens)){continue}if(fnType.id<0&&queryElem.id<0){if(mgens&&mgens.has(fnType.id)&&mgens.get(fnType.id)!==queryElem.id){continue}const mgensScratch=new Map(mgens);mgensScratch.set(fnType.id,queryElem.id);if(!solutionCb||solutionCb(mgensScratch)){return true}}else if(!solutionCb||solutionCb(mgens?new Map(mgens):null)){return true}}for(const fnType of fnTypesIn){if(!unifyFunctionTypeIsUnboxCandidate(fnType,queryElem,whereClause,mgens)){continue}if(fnType.id<0){if(mgens&&mgens.has(fnType.id)&&mgens.get(fnType.id)!==0){continue}const mgensScratch=new Map(mgens);mgensScratch.set(fnType.id,0);if(unifyFunctionTypes(whereClause[(-fnType.id)-1],queryElems,whereClause,mgensScratch,solutionCb)){return true}}else if(unifyFunctionTypes([...fnType.generics,...Array.from(fnType.bindings.values()).flat()],queryElems,whereClause,mgens?new Map(mgens):null,solutionCb)){return true}}return false}const fnTypes=fnTypesIn.slice();const flast=fl-1;const qlast=ql-1;const queryElem=queryElems[qlast];let queryElemsTmp=null;for(let i=flast;i>=0;i-=1){const fnType=fnTypes[i];if(!unifyFunctionTypeIsMatchCandidate(fnType,queryElem,whereClause,mgens)){continue}let mgensScratch;if(fnType.id<0){mgensScratch=new Map(mgens);if(mgensScratch.has(fnType.id)&&mgensScratch.get(fnType.id)!==queryElem.id){continue}mgensScratch.set(fnType.id,queryElem.id)}else{mgensScratch=mgens}fnTypes[i]=fnTypes[flast];fnTypes.length=flast;if(!queryElemsTmp){queryElemsTmp=queryElems.slice(0,qlast)}const passesUnification=unifyFunctionTypes(fnTypes,queryElemsTmp,whereClause,mgensScratch,mgensScratch=>{if(fnType.generics.length===0&&queryElem.generics.length===0&&fnType.bindings.size===0&&queryElem.bindings.size===0){return!solutionCb||solutionCb(mgensScratch)}const solution=unifyFunctionTypeCheckBindings(fnType,queryElem,whereClause,mgensScratch);if(!solution){return false}const simplifiedGenerics=solution.simplifiedGenerics;for(const simplifiedMgens of solution.mgens){const passesUnification=unifyFunctionTypes(simplifiedGenerics,queryElem.generics,whereClause,simplifiedMgens,solutionCb);if(passesUnification){return true}}return false});if(passesUnification){return true}fnTypes[flast]=fnTypes[i];fnTypes[i]=fnType;fnTypes.length=fl}for(let i=flast;i>=0;i-=1){const fnType=fnTypes[i];if(!unifyFunctionTypeIsUnboxCandidate(fnType,queryElem,whereClause,mgens)){continue}let mgensScratch;if(fnType.id<0){mgensScratch=new Map(mgens);if(mgensScratch.has(fnType.id)&&mgensScratch.get(fnType.id)!==0){continue}mgensScratch.set(fnType.id,0)}else{mgensScratch=mgens}const generics=fnType.id<0?whereClause[(-fnType.id)-1]:fnType.generics;const bindings=fnType.bindings?Array.from(fnType.bindings.values()).flat():[];const passesUnification=unifyFunctionTypes(fnTypes.toSpliced(i,1,...generics,...bindings),queryElems,whereClause,mgensScratch,solutionCb);if(passesUnification){return true}}return false}function unifyFunctionTypeIsMatchCandidate(fnType,queryElem,whereClause,mgensIn){if(!typePassesFilter(queryElem.typeFilter,fnType.ty)){return false}if(fnType.id<0&&queryElem.id<0){if(mgensIn){if(mgensIn.has(fnType.id)&&mgensIn.get(fnType.id)!==queryElem.id){return false}for(const[fid,qid]of mgensIn.entries()){if(fnType.id!==fid&&queryElem.id===qid){return false}if(fnType.id===fid&&queryElem.id!==qid){return false}}}return true}else{if(queryElem.id===typeNameIdOfArrayOrSlice&&(fnType.id===typeNameIdOfSlice||fnType.id===typeNameIdOfArray)){}else if(queryElem.id===typeNameIdOfTupleOrUnit&&(fnType.id===typeNameIdOfTuple||fnType.id===typeNameIdOfUnit)){}else if(fnType.id!==queryElem.id||queryElem.id===null){return false}if((fnType.generics.length+fnType.bindings.size)===0&&queryElem.generics.length!==0){return false}if(fnType.bindings.size0){const fnTypePath=fnType.path!==undefined&&fnType.path!==null?fnType.path.split("::"):[];if(queryElemPathLength>fnTypePath.length){return false}let i=0;for(const path of fnTypePath){if(path===queryElem.pathWithoutLast[i]){i+=1;if(i>=queryElemPathLength){break}}}if(i0){let mgensSolutionSet=[mgensIn];for(const[name,constraints]of queryElem.bindings.entries()){if(mgensSolutionSet.length===0){return false}if(!fnType.bindings.has(name)){return false}const fnTypeBindings=fnType.bindings.get(name);mgensSolutionSet=mgensSolutionSet.flatMap(mgens=>{const newSolutions=[];unifyFunctionTypes(fnTypeBindings,constraints,whereClause,mgens,newMgens=>{newSolutions.push(newMgens);return false});return newSolutions})}if(mgensSolutionSet.length===0){return false}const binds=Array.from(fnType.bindings.entries()).flatMap(entry=>{const[name,constraints]=entry;if(queryElem.bindings.has(name)){return[]}else{return constraints}});if(simplifiedGenerics.length>0){simplifiedGenerics=[...simplifiedGenerics,...binds]}else{simplifiedGenerics=binds}return{simplifiedGenerics,mgens:mgensSolutionSet}}return{simplifiedGenerics,mgens:[mgensIn]}}function unifyFunctionTypeIsUnboxCandidate(fnType,queryElem,whereClause,mgens){if(fnType.id<0&&queryElem.id>=0){if(!whereClause){return false}if(mgens&&mgens.has(fnType.id)&&mgens.get(fnType.id)!==0){return false}const mgensTmp=new Map(mgens);mgensTmp.set(fnType.id,null);return checkIfInList(whereClause[(-fnType.id)-1],queryElem,whereClause,mgensTmp)}else if(fnType.generics.length>0||fnType.bindings.size>0){const simplifiedGenerics=[...fnType.generics,...Array.from(fnType.bindings.values()).flat(),];return checkIfInList(simplifiedGenerics,queryElem,whereClause,mgens)}return false}function checkIfInList(list,elem,whereClause,mgens){for(const entry of list){if(checkType(entry,elem,whereClause,mgens)){return true}}return false}function checkType(row,elem,whereClause,mgens){if(row.bindings.size===0&&elem.bindings.size===0){if(elem.id<0){return row.id<0||checkIfInList(row.generics,elem,whereClause,mgens)}if(row.id>0&&elem.id>0&&elem.pathWithoutLast.length===0&&typePassesFilter(elem.typeFilter,row.ty)&&elem.generics.length===0&&elem.id!==typeNameIdOfArrayOrSlice&&elem.id!==typeNameIdOfTupleOrUnit){return row.id===elem.id||checkIfInList(row.generics,elem,whereClause,mgens)}}return unifyFunctionTypes([row],[elem],whereClause,mgens)}function checkPath(contains,ty){if(contains.length===0){return 0}const maxPathEditDistance=Math.floor(contains.reduce((acc,next)=>acc+next.length,0)/3);let ret_dist=maxPathEditDistance+1;const path=ty.path.split("::");if(ty.parent&&ty.parent.name){path.push(ty.parent.name.toLowerCase())}const length=path.length;const clength=contains.length;pathiter:for(let i=length-clength;i>=0;i-=1){let dist_total=0;for(let x=0;xmaxPathEditDistance){continue pathiter}dist_total+=dist}}ret_dist=Math.min(ret_dist,Math.round(dist_total/clength))}return ret_dist>maxPathEditDistance?null:ret_dist}function typePassesFilter(filter,type){if(filter<=NO_TYPE_FILTER||filter===type)return true;const name=itemTypes[type];switch(itemTypes[filter]){case"constant":return name==="associatedconstant";case"fn":return name==="method"||name==="tymethod";case"type":return name==="primitive"||name==="associatedtype";case"trait":return name==="traitalias"}return false}function createAliasFromItem(item){return{crate:item.crate,name:item.name,path:item.path,desc:item.desc,ty:item.ty,parent:item.parent,type:item.type,is_alias:true,deprecated:item.deprecated,implDisambiguator:item.implDisambiguator,}}function handleAliases(ret,query,filterCrates,currentCrate){const lowerQuery=query.toLowerCase();const aliases=[];const crateAliases=[];if(filterCrates!==null){if(ALIASES.has(filterCrates)&&ALIASES.get(filterCrates).has(lowerQuery)){const query_aliases=ALIASES.get(filterCrates).get(lowerQuery);for(const alias of query_aliases){aliases.push(createAliasFromItem(searchIndex[alias]))}}}else{for(const[crate,crateAliasesIndex]of ALIASES){if(crateAliasesIndex.has(lowerQuery)){const pushTo=crate===currentCrate?crateAliases:aliases;const query_aliases=crateAliasesIndex.get(lowerQuery);for(const alias of query_aliases){pushTo.push(createAliasFromItem(searchIndex[alias]))}}}}const sortFunc=(aaa,bbb)=>{if(aaa.path{alias.alias=query;const res=buildHrefAndPath(alias);alias.displayPath=pathSplitter(res[0]);alias.fullPath=alias.displayPath+alias.name;alias.href=res[1];ret.others.unshift(alias);if(ret.others.length>MAX_RESULTS){ret.others.pop()}};aliases.forEach(pushFunc);crateAliases.forEach(pushFunc)}function addIntoResults(results,fullId,id,index,dist,path_dist,maxEditDistance){if(dist<=maxEditDistance||index!==-1){if(results.has(fullId)){const result=results.get(fullId);if(result.dontValidate||result.dist<=dist){return}}results.set(fullId,{id:id,index:index,dontValidate:parsedQuery.literalSearch,dist:dist,path_dist:path_dist,})}}function handleSingleArg(row,pos,elem,results_others,results_in_args,results_returned,maxEditDistance){if(!row||(filterCrates!==null&&row.crate!==filterCrates)){return}let path_dist=0;const fullId=row.id;const tfpDist=compareTypeFingerprints(fullId,parsedQuery.typeFingerprint);if(tfpDist!==null){const in_args=row.type&&row.type.inputs&&checkIfInList(row.type.inputs,elem,row.type.where_clause);const returned=row.type&&row.type.output&&checkIfInList(row.type.output,elem,row.type.where_clause);if(in_args){results_in_args.max_dist=Math.max(results_in_args.max_dist||0,tfpDist);const maxDist=results_in_args.sizenormalizedIndex&&normalizedIndex!==-1)){index=normalizedIndex}if(elem.fullPath.length>1){path_dist=checkPath(elem.pathWithoutLast,row);if(path_dist===null){return}}if(parsedQuery.literalSearch){if(row.word===elem.pathLast){addIntoResults(results_others,fullId,pos,index,0,path_dist)}return}const dist=editDistance(row.normalizedName,elem.normalizedPathLast,maxEditDistance);if(index===-1&&dist>maxEditDistance){return}addIntoResults(results_others,fullId,pos,index,dist,path_dist,maxEditDistance)}function handleArgs(row,pos,results){if(!row||(filterCrates!==null&&row.crate!==filterCrates)||!row.type){return}const tfpDist=compareTypeFingerprints(row.id,parsedQuery.typeFingerprint);if(tfpDist===null){return}if(results.size>=MAX_RESULTS&&tfpDist>results.max_dist){return}if(!unifyFunctionTypes(row.type.inputs,parsedQuery.elems,row.type.where_clause,null,mgens=>{return unifyFunctionTypes(row.type.output,parsedQuery.returned,row.type.where_clause,mgens)})){return}results.max_dist=Math.max(results.max_dist||0,tfpDist);addIntoResults(results,row.id,pos,0,tfpDist,0,Number.MAX_VALUE)}function innerRunQuery(){const queryLen=parsedQuery.elems.reduce((acc,next)=>acc+next.pathLast.length,0)+parsedQuery.returned.reduce((acc,next)=>acc+next.pathLast.length,0);const maxEditDistance=Math.floor(queryLen/3);const genericSymbols=new Map();function convertNameToId(elem,isAssocType){if(typeNameIdMap.has(elem.normalizedPathLast)&&(isAssocType||!typeNameIdMap.get(elem.normalizedPathLast).assocOnly)){elem.id=typeNameIdMap.get(elem.normalizedPathLast).id}else if(!parsedQuery.literalSearch){let match=null;let matchDist=maxEditDistance+1;let matchName="";for(const[name,{id,assocOnly}]of typeNameIdMap){const dist=editDistance(name,elem.normalizedPathLast,maxEditDistance);if(dist<=matchDist&&dist<=maxEditDistance&&(isAssocType||!assocOnly)){if(dist===matchDist&&matchName>name){continue}match=id;matchDist=dist;matchName=name}}if(match!==null){parsedQuery.correction=matchName}elem.id=match}if((elem.id===null&&parsedQuery.totalElems>1&&elem.typeFilter===-1&&elem.generics.length===0&&elem.bindings.size===0)||elem.typeFilter===TY_GENERIC){if(genericSymbols.has(elem.name)){elem.id=genericSymbols.get(elem.name)}else{elem.id=-(genericSymbols.size+1);genericSymbols.set(elem.name,elem.id)}if(elem.typeFilter===-1&&elem.name.length>=3){const maxPartDistance=Math.floor(elem.name.length/3);let matchDist=maxPartDistance+1;let matchName="";for(const name of typeNameIdMap.keys()){const dist=editDistance(name,elem.name,maxPartDistance);if(dist<=matchDist&&dist<=maxPartDistance){if(dist===matchDist&&matchName>name){continue}matchDist=dist;matchName=name}}if(matchName!==""){parsedQuery.proposeCorrectionFrom=elem.name;parsedQuery.proposeCorrectionTo=matchName}}elem.typeFilter=TY_GENERIC}if(elem.generics.length>0&&elem.typeFilter===TY_GENERIC){parsedQuery.error=["Generic type parameter ",elem.name," does not accept generic parameters",]}for(const elem2 of elem.generics){convertNameToId(elem2)}elem.bindings=new Map(Array.from(elem.bindings.entries()).map(entry=>{const[name,constraints]=entry;if(!typeNameIdMap.has(name)){parsedQuery.error=["Type parameter ",name," does not exist",];return[null,[]]}for(const elem2 of constraints){convertNameToId(elem2)}return[typeNameIdMap.get(name).id,constraints]}))}const fps=new Set();for(const elem of parsedQuery.elems){convertNameToId(elem);buildFunctionTypeFingerprint(elem,parsedQuery.typeFingerprint,fps)}for(const elem of parsedQuery.returned){convertNameToId(elem);buildFunctionTypeFingerprint(elem,parsedQuery.typeFingerprint,fps)}if(parsedQuery.foundElems===1&&parsedQuery.returned.length===0){if(parsedQuery.elems.length===1){const elem=parsedQuery.elems[0];for(let i=0,nSearchIndex=searchIndex.length;i0){const sortQ=(a,b)=>{const ag=a.generics.length===0&&a.bindings.size===0;const bg=b.generics.length===0&&b.bindings.size===0;if(ag!==bg){return ag-bg}const ai=a.id>0;const bi=b.id>0;return ai-bi};parsedQuery.elems.sort(sortQ);parsedQuery.returned.sort(sortQ);for(let i=0,nSearchIndex=searchIndex.length;i");if(tmp.endsWith("")){return tmp.slice(0,tmp.length-6)}return tmp}function addTab(array,query,display){const extraClass=display?" active":"";const output=document.createElement("div");if(array.length>0){output.className="search-results "+extraClass;array.forEach(item=>{const name=item.name;const type=itemTypes[item.ty];const longType=longItemTypes[item.ty];const typeName=longType.length!==0?`${longType}`:"?";const link=document.createElement("a");link.className="result-"+type;link.href=item.href;const resultName=document.createElement("div");resultName.className="result-name";resultName.insertAdjacentHTML("beforeend",`${typeName}`);link.appendChild(resultName);let alias=" ";if(item.is_alias){alias=`
\ +${item.alias} - see \ +
`}resultName.insertAdjacentHTML("beforeend",`
${alias}\ +${item.displayPath}${name}\ +
`);const description=document.createElement("div");description.className="desc";description.insertAdjacentHTML("beforeend",item.desc);link.appendChild(description);output.appendChild(link)})}else if(query.error===null){output.className="search-failed"+extraClass;output.innerHTML="No results :(
"+"Try on DuckDuckGo?

"+"Or try looking in one of these:"}return[output,array.length]}function makeTabHeader(tabNb,text,nbElems){const fmtNbElems=nbElems<10?`\u{2007}(${nbElems})\u{2007}\u{2007}`:nbElems<100?`\u{2007}(${nbElems})\u{2007}`:`\u{2007}(${nbElems})`;if(searchState.currentTab===tabNb){return""}return""}function showResults(results,go_to_first,filterCrates){const search=searchState.outputElement();if(go_to_first||(results.others.length===1&&getSettingValue("go-to-only-result")==="true")){window.onunload=()=>{};searchState.removeQueryParameters();const elem=document.createElement("a");elem.href=results.others[0].href;removeClass(elem,"active");document.body.appendChild(elem);elem.click();return}if(results.query===undefined){results.query=parseQuery(searchState.input.value)}currentResults=results.query.userQuery;const ret_others=addTab(results.others,results.query,true);const ret_in_args=addTab(results.in_args,results.query,false);const ret_returned=addTab(results.returned,results.query,false);let currentTab=searchState.currentTab;if((currentTab===0&&ret_others[1]===0)||(currentTab===1&&ret_in_args[1]===0)||(currentTab===2&&ret_returned[1]===0)){if(ret_others[1]!==0){currentTab=0}else if(ret_in_args[1]!==0){currentTab=1}else if(ret_returned[1]!==0){currentTab=2}}let crates="";if(rawSearchIndex.size>1){crates=" in 
"}let output=`

Results${crates}

`;if(results.query.error!==null){const error=results.query.error;error.forEach((value,index)=>{value=value.split("<").join("<").split(">").join(">");if(index%2!==0){error[index]=`${value.replaceAll(" ", " ")}`}else{error[index]=value}});output+=`

Query parser error: "${error.join("")}".

`;output+="
"+makeTabHeader(0,"In Names",ret_others[1])+"
";currentTab=0}else if(results.query.foundElems<=1&&results.query.returned.length===0){output+="
"+makeTabHeader(0,"In Names",ret_others[1])+makeTabHeader(1,"In Parameters",ret_in_args[1])+makeTabHeader(2,"In Return Types",ret_returned[1])+"
"}else{const signatureTabTitle=results.query.elems.length===0?"In Function Return Types":results.query.returned.length===0?"In Function Parameters":"In Function Signatures";output+="
"+makeTabHeader(0,signatureTabTitle,ret_others[1])+"
";currentTab=0}if(results.query.correction!==null){const orig=results.query.returned.length>0?results.query.returned[0].name:results.query.elems[0].name;output+="

"+`Type "${orig}" not found. `+"Showing results for closest type name "+`"${results.query.correction}" instead.

`}if(results.query.proposeCorrectionFrom!==null){const orig=results.query.proposeCorrectionFrom;const targ=results.query.proposeCorrectionTo;output+="

"+`Type "${orig}" not found and used as generic parameter. `+`Consider searching for "${targ}" instead.

`}const resultsElem=document.createElement("div");resultsElem.id="results";resultsElem.appendChild(ret_others[0]);resultsElem.appendChild(ret_in_args[0]);resultsElem.appendChild(ret_returned[0]);search.innerHTML=output;const crateSearch=document.getElementById("crate-search");if(crateSearch){crateSearch.addEventListener("input",updateCrate)}search.appendChild(resultsElem);searchState.showResults(search);const elems=document.getElementById("search-tabs").childNodes;searchState.focusedByTab=[];let i=0;for(const elem of elems){const j=i;elem.onclick=()=>printTab(j);searchState.focusedByTab.push(null);i+=1}printTab(currentTab)}function updateSearchHistory(url){if(!browserSupportsHistoryApi()){return}const params=searchState.getQueryStringParams();if(!history.state&&!params.search){history.pushState(null,"",url)}else{history.replaceState(null,"",url)}}function search(forced){const query=parseQuery(searchState.input.value.trim());let filterCrates=getFilterCrates();if(!forced&&query.userQuery===currentResults){if(query.userQuery.length>0){putBackSearch()}return}searchState.setLoadingSearch();const params=searchState.getQueryStringParams();if(filterCrates===null&¶ms["filter-crate"]!==undefined){filterCrates=params["filter-crate"]}searchState.title="Results for "+query.original+" - Rust";updateSearchHistory(buildUrl(query.original,filterCrates));showResults(execQuery(query,filterCrates,window.currentCrate),params.go_to_first,filterCrates)}function buildItemSearchTypeAll(types,lowercasePaths){return types.length>0?types.map(type=>buildItemSearchType(type,lowercasePaths)):EMPTY_GENERICS_ARRAY}const EMPTY_BINDINGS_MAP=new Map();const EMPTY_GENERICS_ARRAY=[];let TYPES_POOL=new Map();function buildItemSearchType(type,lowercasePaths,isAssocType){const PATH_INDEX_DATA=0;const GENERICS_DATA=1;const BINDINGS_DATA=2;let pathIndex,generics,bindings;if(typeof type==="number"){pathIndex=type;generics=EMPTY_GENERICS_ARRAY;bindings=EMPTY_BINDINGS_MAP}else{pathIndex=type[PATH_INDEX_DATA];generics=buildItemSearchTypeAll(type[GENERICS_DATA],lowercasePaths);if(type.length>BINDINGS_DATA&&type[BINDINGS_DATA].length>0){bindings=new Map(type[BINDINGS_DATA].map(binding=>{const[assocType,constraints]=binding;return[buildItemSearchType(assocType,lowercasePaths,true).id,buildItemSearchTypeAll(constraints,lowercasePaths),]}))}else{bindings=EMPTY_BINDINGS_MAP}}let result;if(pathIndex<0){result={id:pathIndex,ty:TY_GENERIC,path:null,generics,bindings,}}else if(pathIndex===0){result={id:null,ty:null,path:null,generics,bindings,}}else{const item=lowercasePaths[pathIndex-1];result={id:buildTypeMapIndex(item.name,isAssocType),ty:item.ty,path:item.path,generics,bindings,}}const cr=TYPES_POOL.get(result.id);if(cr){if(cr.generics.length===result.generics.length&&cr.generics!==result.generics&&cr.generics.every((x,i)=>result.generics[i]===x)){result.generics=cr.generics}if(cr.bindings.size===result.bindings.size&&cr.bindings!==result.bindings){let ok=true;for(const[k,v]of cr.bindings.entries()){const v2=result.bindings.get(v);if(!v2){ok=false;break}if(v!==v2&&v.length===v2.length&&v.every((x,i)=>v2[i]===x)){result.bindings.set(k,v)}else if(v!==v2){ok=false;break}}if(ok){result.bindings=cr.bindings}}if(cr.ty===result.ty&&cr.path===result.path&&cr.bindings===result.bindings&&cr.generics===result.generics&&cr.ty===result.ty){return cr}}TYPES_POOL.set(result.id,result);return result}function buildFunctionSearchType(itemFunctionDecoder,lowercasePaths){const c=itemFunctionDecoder.string.charCodeAt(itemFunctionDecoder.offset);itemFunctionDecoder.offset+=1;const[zero,ua,la,ob,cb]=["0","@","`","{","}"].map(c=>c.charCodeAt(0));if(c===la){return null}if(c>=zero&&c>1];itemFunctionDecoder.offset+=1;return sign?-value:value}const functionSearchType=decodeList();const INPUTS_DATA=0;const OUTPUT_DATA=1;let inputs,output;if(typeof functionSearchType[INPUTS_DATA]==="number"){inputs=[buildItemSearchType(functionSearchType[INPUTS_DATA],lowercasePaths)]}else{inputs=buildItemSearchTypeAll(functionSearchType[INPUTS_DATA],lowercasePaths)}if(functionSearchType.length>1){if(typeof functionSearchType[OUTPUT_DATA]==="number"){output=[buildItemSearchType(functionSearchType[OUTPUT_DATA],lowercasePaths)]}else{output=buildItemSearchTypeAll(functionSearchType[OUTPUT_DATA],lowercasePaths)}}else{output=[]}const where_clause=[];const l=functionSearchType.length;for(let i=2;i16){itemFunctionDecoder.backrefQueue.pop()}return ret}function buildFunctionTypeFingerprint(type,output,fps){let input=type.id;if(input===typeNameIdOfArray||input===typeNameIdOfSlice){input=typeNameIdOfArrayOrSlice}if(input===typeNameIdOfTuple||input===typeNameIdOfUnit){input=typeNameIdOfTupleOrUnit}const hashint1=k=>{k=(~~k+0x7ed55d16)+(k<<12);k=(k ^ 0xc761c23c)^(k>>>19);k=(~~k+0x165667b1)+(k<<5);k=(~~k+0xd3a2646c)^(k<<9);k=(~~k+0xfd7046c5)+(k<<3);return(k ^ 0xb55a4f09)^(k>>>16)};const hashint2=k=>{k=~k+(k<<15);k ^=k>>>12;k+=k<<2;k ^=k>>>4;k=Math.imul(k,2057);return k ^(k>>16)};if(input!==null){const h0a=hashint1(input);const h0b=hashint2(input);const h1a=~~(h0a+Math.imul(h0b,2));const h1b=~~(h0a+Math.imul(h0b,3));const h2a=~~(h0a+Math.imul(h0b,4));const h2b=~~(h0a+Math.imul(h0b,5));output[0]|=(1<<(h0a%32))|(1<<(h1b%32));output[1]|=(1<<(h1a%32))|(1<<(h2b%32));output[2]|=(1<<(h2a%32))|(1<<(h0b%32));fps.add(input)}for(const g of type.generics){buildFunctionTypeFingerprint(g,output,fps)}const fb={id:null,ty:0,generics:EMPTY_GENERICS_ARRAY,bindings:EMPTY_BINDINGS_MAP,};for(const[k,v]of type.bindings.entries()){fb.id=k;fb.generics=v;buildFunctionTypeFingerprint(fb,output,fps)}output[3]=fps.size}function compareTypeFingerprints(fullId,queryFingerprint){const fh0=functionTypeFingerprint[fullId*4];const fh1=functionTypeFingerprint[(fullId*4)+1];const fh2=functionTypeFingerprint[(fullId*4)+2];const[qh0,qh1,qh2]=queryFingerprint;const[in0,in1,in2]=[fh0&qh0,fh1&qh1,fh2&qh2];if((in0 ^ qh0)||(in1 ^ qh1)||(in2 ^ qh2)){return null}return functionTypeFingerprint[(fullId*4)+3]}function buildIndex(rawSearchIndex){searchIndex=[];typeNameIdMap=new Map();const charA="A".charCodeAt(0);let currentIndex=0;let id=0;typeNameIdOfArray=buildTypeMapIndex("array");typeNameIdOfSlice=buildTypeMapIndex("slice");typeNameIdOfTuple=buildTypeMapIndex("tuple");typeNameIdOfUnit=buildTypeMapIndex("unit");typeNameIdOfArrayOrSlice=buildTypeMapIndex("[]");typeNameIdOfTupleOrUnit=buildTypeMapIndex("()");for(const crate of rawSearchIndex.values()){id+=crate.t.length+1}functionTypeFingerprint=new Uint32Array((id+1)*4);id=0;for(const[crate,crateCorpus]of rawSearchIndex){const crateRow={crate:crate,ty:3,name:crate,path:"",desc:crateCorpus.doc,parent:undefined,type:null,id:id,word:crate,normalizedName:crate.indexOf("_")===-1?crate:crate.replace(/_/g,""),deprecated:null,implDisambiguator:null,};id+=1;searchIndex.push(crateRow);currentIndex+=1;const itemTypes=crateCorpus.t;const itemNames=crateCorpus.n;const itemPaths=new Map(crateCorpus.q);const itemDescs=crateCorpus.d;const itemParentIdxs=crateCorpus.i;const itemFunctionDecoder={string:crateCorpus.f,offset:0,backrefQueue:[],};const deprecatedItems=new Set(crateCorpus.c);const implDisambiguator=new Map(crateCorpus.b);const paths=crateCorpus.p;const aliases=crateCorpus.a;const lowercasePaths=[];let len=paths.length;let lastPath=itemPaths.get(0);for(let i=0;i2){path=itemPaths.has(elem[2])?itemPaths.get(elem[2]):lastPath;lastPath=path}lowercasePaths.push({ty:ty,name:name.toLowerCase(),path:path});paths[i]={ty:ty,name:name,path:path}}lastPath="";len=itemTypes.length;for(let i=0;i0?paths[itemParentIdxs[i]-1]:undefined,type,id:id,word,normalizedName:word.indexOf("_")===-1?word:word.replace(/_/g,""),deprecated:deprecatedItems.has(i),implDisambiguator:implDisambiguator.has(i)?implDisambiguator.get(i):null,};id+=1;searchIndex.push(row);lastPath=row.path}if(aliases){const currentCrateAliases=new Map();ALIASES.set(crate,currentCrateAliases);for(const alias_name in aliases){if(!Object.prototype.hasOwnProperty.call(aliases,alias_name)){continue}let currentNameAliases;if(currentCrateAliases.has(alias_name)){currentNameAliases=currentCrateAliases.get(alias_name)}else{currentNameAliases=[];currentCrateAliases.set(alias_name,currentNameAliases)}for(const local_alias of aliases[alias_name]){currentNameAliases.push(local_alias+currentIndex)}}}currentIndex+=itemTypes.length}TYPES_POOL=new Map()}function onSearchSubmit(e){e.preventDefault();searchState.clearInputTimeout();search()}function putBackSearch(){const search_input=searchState.input;if(!searchState.input){return}if(search_input.value!==""&&!searchState.isDisplayed()){searchState.showResults();if(browserSupportsHistoryApi()){history.replaceState(null,"",buildUrl(search_input.value,getFilterCrates()))}document.title=searchState.title}}function registerSearchEvents(){const params=searchState.getQueryStringParams();if(searchState.input.value===""){searchState.input.value=params.search||""}const searchAfter500ms=()=>{searchState.clearInputTimeout();if(searchState.input.value.length===0){searchState.hideResults()}else{searchState.timeout=setTimeout(search,500)}};searchState.input.onkeyup=searchAfter500ms;searchState.input.oninput=searchAfter500ms;document.getElementsByClassName("search-form")[0].onsubmit=onSearchSubmit;searchState.input.onchange=e=>{if(e.target!==document.activeElement){return}searchState.clearInputTimeout();setTimeout(search,0)};searchState.input.onpaste=searchState.input.onchange;searchState.outputElement().addEventListener("keydown",e=>{if(e.altKey||e.ctrlKey||e.shiftKey||e.metaKey){return}if(e.which===38){const previous=document.activeElement.previousElementSibling;if(previous){previous.focus()}else{searchState.focus()}e.preventDefault()}else if(e.which===40){const next=document.activeElement.nextElementSibling;if(next){next.focus()}const rect=document.activeElement.getBoundingClientRect();if(window.innerHeight-rect.bottom{if(e.which===40){focusSearchResult();e.preventDefault()}});searchState.input.addEventListener("focus",()=>{putBackSearch()});searchState.input.addEventListener("blur",()=>{searchState.input.placeholder=searchState.input.origPlaceholder});if(browserSupportsHistoryApi()){const previousTitle=document.title;window.addEventListener("popstate",e=>{const params=searchState.getQueryStringParams();document.title=previousTitle;currentResults=null;if(params.search&¶ms.search.length>0){searchState.input.value=params.search;e.preventDefault();search()}else{searchState.input.value="";searchState.hideResults()}})}window.onpageshow=()=>{const qSearch=searchState.getQueryStringParams().search;if(searchState.input.value===""&&qSearch){searchState.input.value=qSearch}search()}}function updateCrate(ev){if(ev.target.value==="all crates"){const query=searchState.input.value.trim();updateSearchHistory(buildUrl(query,null))}currentResults=null;search(true)}buildIndex(rawSearchIndex);if(typeof window!=="undefined"){registerSearchEvents();if(window.searchState.getQueryStringParams().search){search()}}if(typeof exports!=="undefined"){exports.initSearch=initSearch;exports.execQuery=execQuery;exports.parseQuery=parseQuery}}if(typeof window!=="undefined"){window.initSearch=initSearch;if(window.searchIndex!==undefined){initSearch(window.searchIndex)}}else{initSearch(new Map())}})() \ No newline at end of file diff --git a/static.files/settings-4313503d2e1961c2.js b/static.files/settings-4313503d2e1961c2.js new file mode 100644 index 00000000..ab425fe4 --- /dev/null +++ b/static.files/settings-4313503d2e1961c2.js @@ -0,0 +1,17 @@ +"use strict";(function(){const isSettingsPage=window.location.pathname.endsWith("/settings.html");function changeSetting(settingName,value){if(settingName==="theme"){const useSystem=value==="system preference"?"true":"false";updateLocalStorage("use-system-theme",useSystem)}updateLocalStorage(settingName,value);switch(settingName){case"theme":case"preferred-dark-theme":case"preferred-light-theme":updateTheme();updateLightAndDark();break;case"line-numbers":if(value===true){window.rustdoc_add_line_numbers_to_examples()}else{window.rustdoc_remove_line_numbers_from_examples()}break;case"hide-sidebar":if(value===true){addClass(document.documentElement,"hide-sidebar")}else{removeClass(document.documentElement,"hide-sidebar")}break}}function showLightAndDark(){removeClass(document.getElementById("preferred-light-theme"),"hidden");removeClass(document.getElementById("preferred-dark-theme"),"hidden")}function hideLightAndDark(){addClass(document.getElementById("preferred-light-theme"),"hidden");addClass(document.getElementById("preferred-dark-theme"),"hidden")}function updateLightAndDark(){const useSystem=getSettingValue("use-system-theme");if(useSystem==="true"||(useSystem===null&&getSettingValue("theme")===null)){showLightAndDark()}else{hideLightAndDark()}}function setEvents(settingsElement){updateLightAndDark();onEachLazy(settingsElement.querySelectorAll("input[type=\"checkbox\"]"),toggle=>{const settingId=toggle.id;const settingValue=getSettingValue(settingId);if(settingValue!==null){toggle.checked=settingValue==="true"}toggle.onchange=()=>{changeSetting(toggle.id,toggle.checked)}});onEachLazy(settingsElement.querySelectorAll("input[type=\"radio\"]"),elem=>{const settingId=elem.name;let settingValue=getSettingValue(settingId);if(settingId==="theme"){const useSystem=getSettingValue("use-system-theme");if(useSystem==="true"||settingValue===null){settingValue=useSystem==="false"?"light":"system preference"}}if(settingValue!==null&&settingValue!=="null"){elem.checked=settingValue===elem.value}elem.addEventListener("change",ev=>{changeSetting(ev.target.name,ev.target.value)})})}function buildSettingsPageSections(settings){let output="";for(const setting of settings){const js_data_name=setting["js_name"];const setting_name=setting["name"];if(setting["options"]!==undefined){output+=`\ +
+
${setting_name}
+
`;onEach(setting["options"],option=>{const checked=option===setting["default"]?" checked":"";const full=`${js_data_name}-${option.replace(/ /g,"-")}`;output+=`\ + `});output+=`\ +
+
`}else{const checked=setting["default"]===true?" checked":"";output+=`\ +
\ + \ +
`}}return output}function buildSettingsPage(){const theme_names=getVar("themes").split(",").filter(t=>t);theme_names.push("light","dark","ayu");const settings=[{"name":"Theme","js_name":"theme","default":"system preference","options":theme_names.concat("system preference"),},{"name":"Preferred light theme","js_name":"preferred-light-theme","default":"light","options":theme_names,},{"name":"Preferred dark theme","js_name":"preferred-dark-theme","default":"dark","options":theme_names,},{"name":"Auto-hide item contents for large items","js_name":"auto-hide-large-items","default":true,},{"name":"Auto-hide item methods' documentation","js_name":"auto-hide-method-docs","default":false,},{"name":"Auto-hide trait implementation documentation","js_name":"auto-hide-trait-implementations","default":false,},{"name":"Directly go to item in search if there is only one result","js_name":"go-to-only-result","default":false,},{"name":"Show line numbers on code examples","js_name":"line-numbers","default":false,},{"name":"Hide persistent navigation bar","js_name":"hide-sidebar","default":false,},{"name":"Disable keyboard shortcuts","js_name":"disable-shortcuts","default":false,},];const elementKind=isSettingsPage?"section":"div";const innerHTML=`
${buildSettingsPageSections(settings)}
`;const el=document.createElement(elementKind);el.id="settings";if(!isSettingsPage){el.className="popover"}el.innerHTML=innerHTML;if(isSettingsPage){document.getElementById(MAIN_ID).appendChild(el)}else{el.setAttribute("tabindex","-1");getSettingsButton().appendChild(el)}return el}const settingsMenu=buildSettingsPage();function displaySettings(){settingsMenu.style.display="";onEachLazy(settingsMenu.querySelectorAll("input[type='checkbox']"),el=>{const val=getSettingValue(el.id);const checked=val==="true";if(checked!==el.checked&&val!==null){el.checked=checked}})}function settingsBlurHandler(event){blurHandler(event,getSettingsButton(),window.hidePopoverMenus)}if(isSettingsPage){getSettingsButton().onclick=event=>{event.preventDefault()}}else{const settingsButton=getSettingsButton();const settingsMenu=document.getElementById("settings");settingsButton.onclick=event=>{if(settingsMenu.contains(event.target)){return}event.preventDefault();const shouldDisplaySettings=settingsMenu.style.display==="none";window.hideAllModals();if(shouldDisplaySettings){displaySettings()}};settingsButton.onblur=settingsBlurHandler;settingsButton.querySelector("a").onblur=settingsBlurHandler;onEachLazy(settingsMenu.querySelectorAll("input"),el=>{el.onblur=settingsBlurHandler});settingsMenu.onblur=settingsBlurHandler}setTimeout(()=>{setEvents(settingsMenu);if(!isSettingsPage){displaySettings()}removeClass(getSettingsButton(),"rotate")},0)})() \ No newline at end of file diff --git a/static.files/src-script-e66d777a5a92e9b2.js b/static.files/src-script-e66d777a5a92e9b2.js new file mode 100644 index 00000000..d0aebb85 --- /dev/null +++ b/static.files/src-script-e66d777a5a92e9b2.js @@ -0,0 +1 @@ +"use strict";(function(){const rootPath=getVar("root-path");const NAME_OFFSET=0;const DIRS_OFFSET=1;const FILES_OFFSET=2;const RUSTDOC_MOBILE_BREAKPOINT=700;function closeSidebarIfMobile(){if(window.innerWidth{removeClass(document.documentElement,"src-sidebar-expanded");updateLocalStorage("source-sidebar-show","false")};window.rustdocShowSourceSidebar=()=>{addClass(document.documentElement,"src-sidebar-expanded");updateLocalStorage("source-sidebar-show","true")};window.rustdocToggleSrcSidebar=()=>{if(document.documentElement.classList.contains("src-sidebar-expanded")){window.rustdocCloseSourceSidebar()}else{window.rustdocShowSourceSidebar()}};function createSrcSidebar(){const container=document.querySelector("nav.sidebar");const sidebar=document.createElement("div");sidebar.id="src-sidebar";let hasFoundFile=false;for(const[key,source]of srcIndex){source[NAME_OFFSET]=key;hasFoundFile=createDirEntry(source,sidebar,"",hasFoundFile)}container.appendChild(sidebar);const selected_elem=sidebar.getElementsByClassName("selected")[0];if(typeof selected_elem!=="undefined"){selected_elem.focus()}}function highlightSrcLines(){const match=window.location.hash.match(/^#?(\d+)(?:-(\d+))?$/);if(!match){return}let from=parseInt(match[1],10);let to=from;if(typeof match[2]!=="undefined"){to=parseInt(match[2],10)}if(to{onEachLazy(e.getElementsByTagName("a"),i_e=>{removeClass(i_e,"line-highlighted")})});for(let i=from;i<=to;++i){elem=document.getElementById(i);if(!elem){break}addClass(elem,"line-highlighted")}}const handleSrcHighlight=(function(){let prev_line_id=0;const set_fragment=name=>{const x=window.scrollX,y=window.scrollY;if(browserSupportsHistoryApi()){history.replaceState(null,null,"#"+name);highlightSrcLines()}else{location.replace("#"+name)}window.scrollTo(x,y)};return ev=>{let cur_line_id=parseInt(ev.target.id,10);if(isNaN(cur_line_id)||ev.ctrlKey||ev.altKey||ev.metaKey){return}ev.preventDefault();if(ev.shiftKey&&prev_line_id){if(prev_line_id>cur_line_id){const tmp=prev_line_id;prev_line_id=cur_line_id;cur_line_id=tmp}set_fragment(prev_line_id+"-"+cur_line_id)}else{prev_line_id=cur_line_id;set_fragment(cur_line_id)}}}());window.addEventListener("hashchange",highlightSrcLines);onEachLazy(document.getElementsByClassName("src-line-numbers"),el=>{el.addEventListener("click",handleSrcHighlight)});highlightSrcLines();window.createSrcSidebar=createSrcSidebar})() \ No newline at end of file diff --git a/static.files/storage-4c98445ec4002617.js b/static.files/storage-4c98445ec4002617.js new file mode 100644 index 00000000..b378b856 --- /dev/null +++ b/static.files/storage-4c98445ec4002617.js @@ -0,0 +1 @@ +"use strict";const builtinThemes=["light","dark","ayu"];const darkThemes=["dark","ayu"];window.currentTheme=document.getElementById("themeStyle");const settingsDataset=(function(){const settingsElement=document.getElementById("default-settings");return settingsElement&&settingsElement.dataset?settingsElement.dataset:null})();function getSettingValue(settingName){const current=getCurrentValue(settingName);if(current===null&&settingsDataset!==null){const def=settingsDataset[settingName.replace(/-/g,"_")];if(def!==undefined){return def}}return current}const localStoredTheme=getSettingValue("theme");function hasClass(elem,className){return elem&&elem.classList&&elem.classList.contains(className)}function addClass(elem,className){if(elem&&elem.classList){elem.classList.add(className)}}function removeClass(elem,className){if(elem&&elem.classList){elem.classList.remove(className)}}function onEach(arr,func){for(const elem of arr){if(func(elem)){return true}}return false}function onEachLazy(lazyArray,func){return onEach(Array.prototype.slice.call(lazyArray),func)}function updateLocalStorage(name,value){try{window.localStorage.setItem("rustdoc-"+name,value)}catch(e){}}function getCurrentValue(name){try{return window.localStorage.getItem("rustdoc-"+name)}catch(e){return null}}const getVar=(function getVar(name){const el=document.querySelector("head > meta[name='rustdoc-vars']");return el?el.attributes["data-"+name].value:null});function switchTheme(newThemeName,saveTheme){const themeNames=getVar("themes").split(",").filter(t=>t);themeNames.push(...builtinThemes);if(themeNames.indexOf(newThemeName)===-1){return}if(saveTheme){updateLocalStorage("theme",newThemeName)}document.documentElement.setAttribute("data-theme",newThemeName);if(builtinThemes.indexOf(newThemeName)!==-1){if(window.currentTheme){window.currentTheme.parentNode.removeChild(window.currentTheme);window.currentTheme=null}}else{const newHref=getVar("root-path")+encodeURIComponent(newThemeName)+getVar("resource-suffix")+".css";if(!window.currentTheme){if(document.readyState==="loading"){document.write(``);window.currentTheme=document.getElementById("themeStyle")}else{window.currentTheme=document.createElement("link");window.currentTheme.rel="stylesheet";window.currentTheme.id="themeStyle";window.currentTheme.href=newHref;document.documentElement.appendChild(window.currentTheme)}}else if(newHref!==window.currentTheme.href){window.currentTheme.href=newHref}}}const updateTheme=(function(){const mql=window.matchMedia("(prefers-color-scheme: dark)");function updateTheme(){if(getSettingValue("use-system-theme")!=="false"){const lightTheme=getSettingValue("preferred-light-theme")||"light";const darkTheme=getSettingValue("preferred-dark-theme")||"dark";updateLocalStorage("use-system-theme","true");switchTheme(mql.matches?darkTheme:lightTheme,true)}else{switchTheme(getSettingValue("theme"),false)}}mql.addEventListener("change",updateTheme);return updateTheme})();if(getSettingValue("use-system-theme")!=="false"&&window.matchMedia){if(getSettingValue("use-system-theme")===null&&getSettingValue("preferred-dark-theme")===null&&darkThemes.indexOf(localStoredTheme)>=0){updateLocalStorage("preferred-dark-theme",localStoredTheme)}}updateTheme();if(getSettingValue("source-sidebar-show")==="true"){addClass(document.documentElement,"src-sidebar-expanded")}if(getSettingValue("hide-sidebar")==="true"){addClass(document.documentElement,"hide-sidebar")}function updateSidebarWidth(){const desktopSidebarWidth=getSettingValue("desktop-sidebar-width");if(desktopSidebarWidth&&desktopSidebarWidth!=="null"){document.documentElement.style.setProperty("--desktop-sidebar-width",desktopSidebarWidth+"px")}const srcSidebarWidth=getSettingValue("src-sidebar-width");if(srcSidebarWidth&&srcSidebarWidth!=="null"){document.documentElement.style.setProperty("--src-sidebar-width",srcSidebarWidth+"px")}}updateSidebarWidth();window.addEventListener("pageshow",ev=>{if(ev.persisted){setTimeout(updateTheme,0);setTimeout(updateSidebarWidth,0)}}) \ No newline at end of file diff --git a/static.files/wheel-7b819b6101059cd0.svg b/static.files/wheel-7b819b6101059cd0.svg new file mode 100644 index 00000000..83c07f63 --- /dev/null +++ b/static.files/wheel-7b819b6101059cd0.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/trait.impl/core/clone/trait.Clone.js b/trait.impl/core/clone/trait.Clone.js new file mode 100644 index 00000000..6ae4b315 --- /dev/null +++ b/trait.impl/core/clone/trait.Clone.js @@ -0,0 +1,3 @@ +(function() {var implementors = { +"vodozemac":[["impl Clone for Curve25519PublicKey"],["impl Clone for OlmMessage"],["impl Clone for Curve25519SecretKey"],["impl Clone for SessionConfig"],["impl Clone for SasBytes"],["impl Clone for Message"],["impl Clone for Ed25519Signature"],["impl Clone for Ed25519PublicKey"],["impl Clone for IdentityKeys"],["impl Clone for InvalidCount"],["impl Clone for KeyId"],["impl Clone for MessageType"],["impl Clone for DecryptedMessage"],["impl Clone for RatchetPublicKey"],["impl Clone for Ed25519Keypair"],["impl Clone for MegolmMessage"],["impl Clone for PreKeyMessage"],["impl Clone for SessionConfig"],["impl Clone for SessionOrdering"],["impl Clone for SessionKeys"]] +};if (window.register_implementors) {window.register_implementors(implementors);} else {window.pending_implementors = implementors;}})() \ No newline at end of file diff --git a/trait.impl/core/cmp/trait.Eq.js b/trait.impl/core/cmp/trait.Eq.js new file mode 100644 index 00000000..507fb69b --- /dev/null +++ b/trait.impl/core/cmp/trait.Eq.js @@ -0,0 +1,3 @@ +(function() {var implementors = { +"vodozemac":[["impl Eq for SessionKeys"],["impl Eq for SessionConfig"],["impl Eq for KeyId"],["impl Eq for Ed25519Signature"],["impl Eq for SessionConfig"],["impl Eq for SasBytes"],["impl Eq for Curve25519PublicKey"],["impl Eq for IdentityKeys"],["impl Eq for RatchetPublicKey"],["impl Eq for MessageType"],["impl Eq for Ed25519PublicKey"],["impl Eq for DecryptedMessage"],["impl Eq for Message"],["impl Eq for PreKeyMessage"],["impl Eq for SessionOrdering"],["impl Eq for MegolmMessage"],["impl Eq for OlmMessage"]] +};if (window.register_implementors) {window.register_implementors(implementors);} else {window.pending_implementors = implementors;}})() \ No newline at end of file diff --git a/trait.impl/core/cmp/trait.Ord.js b/trait.impl/core/cmp/trait.Ord.js new file mode 100644 index 00000000..034523f8 --- /dev/null +++ b/trait.impl/core/cmp/trait.Ord.js @@ -0,0 +1,3 @@ +(function() {var implementors = { +"vodozemac":[["impl Ord for KeyId"]] +};if (window.register_implementors) {window.register_implementors(implementors);} else {window.pending_implementors = implementors;}})() \ No newline at end of file diff --git a/trait.impl/core/cmp/trait.PartialEq.js b/trait.impl/core/cmp/trait.PartialEq.js new file mode 100644 index 00000000..5875e3ec --- /dev/null +++ b/trait.impl/core/cmp/trait.PartialEq.js @@ -0,0 +1,3 @@ +(function() {var implementors = { +"vodozemac":[["impl PartialEq for Ed25519PublicKey"],["impl PartialEq for DecryptedMessage"],["impl PartialEq for Curve25519PublicKey"],["impl PartialEq for SessionOrdering"],["impl PartialEq for MegolmMessage"],["impl PartialEq for SessionConfig"],["impl PartialEq for SasBytes"],["impl PartialEq for SessionKeys"],["impl PartialEq for SessionConfig"],["impl PartialEq for PreKeyMessage"],["impl PartialEq for IdentityKeys"],["impl PartialEq for OlmMessage"],["impl PartialEq for Message"],["impl PartialEq for RatchetPublicKey"],["impl PartialEq for KeyId"],["impl PartialEq for MessageType"],["impl PartialEq for Ed25519Signature"]] +};if (window.register_implementors) {window.register_implementors(implementors);} else {window.pending_implementors = implementors;}})() \ No newline at end of file diff --git a/trait.impl/core/cmp/trait.PartialOrd.js b/trait.impl/core/cmp/trait.PartialOrd.js new file mode 100644 index 00000000..65136fab --- /dev/null +++ b/trait.impl/core/cmp/trait.PartialOrd.js @@ -0,0 +1,3 @@ +(function() {var implementors = { +"vodozemac":[["impl PartialOrd for KeyId"]] +};if (window.register_implementors) {window.register_implementors(implementors);} else {window.pending_implementors = implementors;}})() \ No newline at end of file diff --git a/trait.impl/core/convert/trait.AsRef.js b/trait.impl/core/convert/trait.AsRef.js new file mode 100644 index 00000000..e7da9902 --- /dev/null +++ b/trait.impl/core/convert/trait.AsRef.js @@ -0,0 +1,3 @@ +(function() {var implementors = { +"vodozemac":[["impl AsRef<Curve25519PublicKey> for RatchetPublicKey"]] +};if (window.register_implementors) {window.register_implementors(implementors);} else {window.pending_implementors = implementors;}})() \ No newline at end of file diff --git a/trait.impl/core/convert/trait.From.js b/trait.impl/core/convert/trait.From.js new file mode 100644 index 00000000..4a2a086c --- /dev/null +++ b/trait.impl/core/convert/trait.From.js @@ -0,0 +1,3 @@ +(function() {var implementors = { +"vodozemac":[["impl From<KeyError> for LibolmPickleError"],["impl From<Error> for SignatureError"],["impl From<UnpadError> for DecryptionError"],["impl From<[u8; 32]> for Curve25519PublicKey"],["impl From<MacError> for SasError"],["impl From<EncodeError> for LibolmPickleError"],["impl From<UnpadError> for DecryptionError"],["impl<'a> From<&'a EphemeralSecret> for Curve25519PublicKey"],["impl From<DecodeError> for SessionKeyDecodeError"],["impl From<SignatureError> for DecryptionError"],["impl From<KeyId> for String"],["impl From<SignatureError> for SessionKeyDecodeError"],["impl From<InboundGroupSessionPickle> for InboundGroupSession"],["impl From<DecodeError> for DecodeError"],["impl From<KeyError> for DecodeError"],["impl From<PreKeyMessage> for OlmMessage"],["impl From<DecodeError> for SignatureError"],["impl From<DecodeError> for LibolmPickleError"],["impl From<MessageType> for usize"],["impl From<DecryptionError> for SessionCreationError"],["impl From<SessionPickle> for Session"],["impl From<MacError> for DecryptionError"],["impl From<AccountPickle> for Account"],["impl From<KeyError> for SessionKeyDecodeError"],["impl From<DecodeError> for LibolmPickleError"],["impl From<Error> for SessionKeyDecodeError"],["impl<'a> From<&'a ReusableSecret> for Curve25519PublicKey"],["impl<'a> From<&'a Curve25519SecretKey> for Curve25519PublicKey"],["impl From<Error> for PickleError"],["impl From<SignatureError> for KeyError"],["impl From<SignatureError> for DecodeError"],["impl From<MacError> for DecryptionError"],["impl From<DecodeError> for PickleError"],["impl From<&GroupSession> for InboundGroupSession"],["impl From<&InboundGroupSession> for InboundGroupSessionPickle"],["impl From<DecodeError> for KeyError"],["impl From<[u8; 32]> for RatchetPublicKey"],["impl From<Message> for OlmMessage"],["impl From<GroupSessionPickle> for GroupSession"],["impl From<DecodeError> for DecodeError"]] +};if (window.register_implementors) {window.register_implementors(implementors);} else {window.pending_implementors = implementors;}})() \ No newline at end of file diff --git a/trait.impl/core/convert/trait.TryFrom.js b/trait.impl/core/convert/trait.TryFrom.js new file mode 100644 index 00000000..5bb88894 --- /dev/null +++ b/trait.impl/core/convert/trait.TryFrom.js @@ -0,0 +1,3 @@ +(function() {var implementors = { +"vodozemac":[["impl TryFrom<&str> for SessionKey"],["impl TryFrom<&[u8]> for SessionKey"],["impl TryFrom<&str> for MegolmMessage"],["impl TryFrom<Vec<u8>> for PreKeyMessage"],["impl TryFrom<&str> for PreKeyMessage"],["impl TryFrom<Vec<u8>> for MegolmMessage"],["impl TryFrom<&[u8]> for PreKeyMessage"],["impl TryFrom<&[u8]> for Message"],["impl TryFrom<&str> for ExportedSessionKey"],["impl TryFrom<&str> for Message"],["impl TryFrom<&[u8]> for ExportedSessionKey"],["impl TryFrom<Vec<u8>> for Message"],["impl TryFrom<usize> for MessageType"],["impl TryFrom<&[u8]> for MegolmMessage"]] +};if (window.register_implementors) {window.register_implementors(implementors);} else {window.pending_implementors = implementors;}})() \ No newline at end of file diff --git a/trait.impl/core/default/trait.Default.js b/trait.impl/core/default/trait.Default.js new file mode 100644 index 00000000..5ad2e30c --- /dev/null +++ b/trait.impl/core/default/trait.Default.js @@ -0,0 +1,3 @@ +(function() {var implementors = { +"vodozemac":[["impl Default for Ed25519Keypair"],["impl Default for Ed25519SecretKey"],["impl Default for Account"],["impl Default for Sas"],["impl Default for SessionConfig"],["impl Default for Curve25519SecretKey"],["impl Default for SessionConfig"],["impl Default for GroupSession"]] +};if (window.register_implementors) {window.register_implementors(implementors);} else {window.pending_implementors = implementors;}})() \ No newline at end of file diff --git a/trait.impl/core/error/trait.Error.js b/trait.impl/core/error/trait.Error.js new file mode 100644 index 00000000..97e9a661 --- /dev/null +++ b/trait.impl/core/error/trait.Error.js @@ -0,0 +1,3 @@ +(function() {var implementors = { +"vodozemac":[["impl Error for SasError"],["impl Error for KeyError"],["impl Error for SignatureError"],["impl Error for DecryptionError"],["impl Error for DecodeError"],["impl Error for PickleError"],["impl Error for SessionCreationError"],["impl Error for SessionKeyDecodeError"],["impl Error for InvalidCount"],["impl Error for LibolmPickleError"],["impl Error for DecryptionError"]] +};if (window.register_implementors) {window.register_implementors(implementors);} else {window.pending_implementors = implementors;}})() \ No newline at end of file diff --git a/trait.impl/core/fmt/trait.Debug.js b/trait.impl/core/fmt/trait.Debug.js new file mode 100644 index 00000000..092bea91 --- /dev/null +++ b/trait.impl/core/fmt/trait.Debug.js @@ -0,0 +1,3 @@ +(function() {var implementors = { +"vodozemac":[["impl Debug for SignatureError"],["impl Debug for KeyError"],["impl Debug for DecryptedMessage"],["impl Debug for SessionKeys"],["impl Debug for MessageType"],["impl Debug for InboundCreationResult"],["impl Debug for DecryptionError"],["impl Debug for SessionOrdering"],["impl Debug for Message"],["impl Debug for PreKeyMessage"],["impl Debug for PickleError"],["impl Debug for SessionCreationError"],["impl Debug for SessionConfig"],["impl Debug for LibolmPickleError"],["impl Debug for Session"],["impl Debug for OlmMessage"],["impl Debug for InvalidCount"],["impl Debug for SasBytes"],["impl Debug for SasError"],["impl Debug for Ed25519PublicKey"],["impl Debug for DecodeError"],["impl Debug for SessionConfig"],["impl Debug for EstablishedSas"],["impl Debug for Ed25519Signature"],["impl Debug for DecryptionError"],["impl Debug for MegolmMessage"],["impl Debug for IdentityKeys"],["impl Debug for Curve25519PublicKey"],["impl Debug for SessionKeyDecodeError"],["impl Debug for KeyId"],["impl Debug for RatchetPublicKey"]] +};if (window.register_implementors) {window.register_implementors(implementors);} else {window.pending_implementors = implementors;}})() \ No newline at end of file diff --git a/trait.impl/core/fmt/trait.Display.js b/trait.impl/core/fmt/trait.Display.js new file mode 100644 index 00000000..971e788c --- /dev/null +++ b/trait.impl/core/fmt/trait.Display.js @@ -0,0 +1,3 @@ +(function() {var implementors = { +"vodozemac":[["impl Display for DecodeError"],["impl Display for SignatureError"],["impl Display for KeyError"],["impl Display for DecryptionError"],["impl Display for Curve25519PublicKey"],["impl Display for LibolmPickleError"],["impl Display for SessionCreationError"],["impl Display for SasError"],["impl Display for SessionKeyDecodeError"],["impl Display for PickleError"],["impl Display for DecryptionError"],["impl Display for InvalidCount"],["impl Display for Ed25519Signature"],["impl Display for Ed25519PublicKey"]] +};if (window.register_implementors) {window.register_implementors(implementors);} else {window.pending_implementors = implementors;}})() \ No newline at end of file diff --git a/trait.impl/core/hash/trait.Hash.js b/trait.impl/core/hash/trait.Hash.js new file mode 100644 index 00000000..20976e0c --- /dev/null +++ b/trait.impl/core/hash/trait.Hash.js @@ -0,0 +1,3 @@ +(function() {var implementors = { +"vodozemac":[["impl Hash for KeyId"],["impl Hash for Curve25519PublicKey"]] +};if (window.register_implementors) {window.register_implementors(implementors);} else {window.pending_implementors = implementors;}})() \ No newline at end of file diff --git a/trait.impl/core/marker/trait.Copy.js b/trait.impl/core/marker/trait.Copy.js new file mode 100644 index 00000000..3b8cbacd --- /dev/null +++ b/trait.impl/core/marker/trait.Copy.js @@ -0,0 +1,3 @@ +(function() {var implementors = { +"vodozemac":[["impl Copy for SessionKeys"],["impl Copy for RatchetPublicKey"],["impl Copy for Ed25519PublicKey"],["impl Copy for IdentityKeys"],["impl Copy for MessageType"],["impl Copy for Ed25519Signature"],["impl Copy for SessionOrdering"],["impl Copy for SessionConfig"],["impl Copy for SessionConfig"],["impl Copy for KeyId"],["impl Copy for Curve25519PublicKey"]] +};if (window.register_implementors) {window.register_implementors(implementors);} else {window.pending_implementors = implementors;}})() \ No newline at end of file diff --git a/trait.impl/core/marker/trait.Freeze.js b/trait.impl/core/marker/trait.Freeze.js new file mode 100644 index 00000000..4a355a7a --- /dev/null +++ b/trait.impl/core/marker/trait.Freeze.js @@ -0,0 +1,3 @@ +(function() {var implementors = { +"vodozemac":[["impl Freeze for Curve25519SecretKey",1,["vodozemac::types::curve25519::Curve25519SecretKey"]],["impl Freeze for Curve25519PublicKey",1,["vodozemac::types::curve25519::Curve25519PublicKey"]],["impl Freeze for SignatureError",1,["vodozemac::types::ed25519::SignatureError"]],["impl Freeze for Ed25519Keypair",1,["vodozemac::types::ed25519::Ed25519Keypair"]],["impl Freeze for Ed25519SecretKey",1,["vodozemac::types::ed25519::Ed25519SecretKey"]],["impl Freeze for Ed25519PublicKey",1,["vodozemac::types::ed25519::Ed25519PublicKey"]],["impl Freeze for Ed25519Signature",1,["vodozemac::types::ed25519::Ed25519Signature"]],["impl Freeze for KeyId",1,["vodozemac::types::KeyId"]],["impl Freeze for KeyError",1,["vodozemac::types::KeyError"]],["impl Freeze for GroupSession",1,["vodozemac::megolm::group_session::GroupSession"]],["impl Freeze for GroupSessionPickle",1,["vodozemac::megolm::group_session::GroupSessionPickle"]],["impl Freeze for SessionOrdering",1,["vodozemac::megolm::inbound_group_session::SessionOrdering"]],["impl Freeze for DecryptionError",1,["vodozemac::megolm::inbound_group_session::DecryptionError"]],["impl Freeze for InboundGroupSession",1,["vodozemac::megolm::inbound_group_session::InboundGroupSession"]],["impl Freeze for DecryptedMessage",1,["vodozemac::megolm::inbound_group_session::DecryptedMessage"]],["impl Freeze for InboundGroupSessionPickle",1,["vodozemac::megolm::inbound_group_session::InboundGroupSessionPickle"]],["impl Freeze for MegolmMessage",1,["vodozemac::megolm::message::MegolmMessage"]],["impl Freeze for SessionConfig",1,["vodozemac::megolm::session_config::SessionConfig"]],["impl Freeze for SessionKeyDecodeError",1,["vodozemac::megolm::session_keys::SessionKeyDecodeError"]],["impl Freeze for ExportedSessionKey",1,["vodozemac::megolm::session_keys::ExportedSessionKey"]],["impl Freeze for SessionKey",1,["vodozemac::megolm::session_keys::SessionKey"]],["impl Freeze for OneTimeKeyGenerationResult",1,["vodozemac::olm::account::one_time_keys::OneTimeKeyGenerationResult"]],["impl Freeze for SessionCreationError",1,["vodozemac::olm::account::SessionCreationError"]],["impl Freeze for IdentityKeys",1,["vodozemac::olm::account::IdentityKeys"]],["impl Freeze for InboundCreationResult",1,["vodozemac::olm::account::InboundCreationResult"]],["impl Freeze for Account",1,["vodozemac::olm::account::Account"]],["impl Freeze for AccountPickle",1,["vodozemac::olm::account::AccountPickle"]],["impl Freeze for Message",1,["vodozemac::olm::messages::message::Message"]],["impl Freeze for PreKeyMessage",1,["vodozemac::olm::messages::pre_key::PreKeyMessage"]],["impl Freeze for OlmMessage",1,["vodozemac::olm::messages::OlmMessage"]],["impl Freeze for MessageType",1,["vodozemac::olm::messages::MessageType"]],["impl Freeze for RatchetPublicKey",1,["vodozemac::olm::session::ratchet::RatchetPublicKey"]],["impl Freeze for DecryptionError",1,["vodozemac::olm::session::DecryptionError"]],["impl Freeze for Session",1,["vodozemac::olm::session::Session"]],["impl Freeze for SessionPickle",1,["vodozemac::olm::session::SessionPickle"]],["impl Freeze for SessionConfig",1,["vodozemac::olm::session_config::SessionConfig"]],["impl Freeze for SessionKeys",1,["vodozemac::olm::session_keys::SessionKeys"]],["impl Freeze for Mac",1,["vodozemac::sas::Mac"]],["impl Freeze for InvalidCount",1,["vodozemac::sas::InvalidCount"]],["impl Freeze for SasError",1,["vodozemac::sas::SasError"]],["impl Freeze for Sas",1,["vodozemac::sas::Sas"]],["impl Freeze for EstablishedSas",1,["vodozemac::sas::EstablishedSas"]],["impl Freeze for SasBytes",1,["vodozemac::sas::SasBytes"]],["impl Freeze for PickleError",1,["vodozemac::PickleError"]],["impl Freeze for LibolmPickleError",1,["vodozemac::LibolmPickleError"]],["impl Freeze for DecodeError",1,["vodozemac::DecodeError"]]] +};if (window.register_implementors) {window.register_implementors(implementors);} else {window.pending_implementors = implementors;}})() \ No newline at end of file diff --git a/trait.impl/core/marker/trait.Send.js b/trait.impl/core/marker/trait.Send.js new file mode 100644 index 00000000..ba62da6b --- /dev/null +++ b/trait.impl/core/marker/trait.Send.js @@ -0,0 +1,3 @@ +(function() {var implementors = { +"vodozemac":[["impl Send for Curve25519SecretKey",1,["vodozemac::types::curve25519::Curve25519SecretKey"]],["impl Send for Curve25519PublicKey",1,["vodozemac::types::curve25519::Curve25519PublicKey"]],["impl Send for SignatureError",1,["vodozemac::types::ed25519::SignatureError"]],["impl Send for Ed25519Keypair",1,["vodozemac::types::ed25519::Ed25519Keypair"]],["impl Send for Ed25519SecretKey",1,["vodozemac::types::ed25519::Ed25519SecretKey"]],["impl Send for Ed25519PublicKey",1,["vodozemac::types::ed25519::Ed25519PublicKey"]],["impl Send for Ed25519Signature",1,["vodozemac::types::ed25519::Ed25519Signature"]],["impl Send for KeyId",1,["vodozemac::types::KeyId"]],["impl Send for KeyError",1,["vodozemac::types::KeyError"]],["impl Send for GroupSession",1,["vodozemac::megolm::group_session::GroupSession"]],["impl Send for GroupSessionPickle",1,["vodozemac::megolm::group_session::GroupSessionPickle"]],["impl Send for SessionOrdering",1,["vodozemac::megolm::inbound_group_session::SessionOrdering"]],["impl Send for DecryptionError",1,["vodozemac::megolm::inbound_group_session::DecryptionError"]],["impl Send for InboundGroupSession",1,["vodozemac::megolm::inbound_group_session::InboundGroupSession"]],["impl Send for DecryptedMessage",1,["vodozemac::megolm::inbound_group_session::DecryptedMessage"]],["impl Send for InboundGroupSessionPickle",1,["vodozemac::megolm::inbound_group_session::InboundGroupSessionPickle"]],["impl Send for MegolmMessage",1,["vodozemac::megolm::message::MegolmMessage"]],["impl Send for SessionConfig",1,["vodozemac::megolm::session_config::SessionConfig"]],["impl Send for SessionKeyDecodeError",1,["vodozemac::megolm::session_keys::SessionKeyDecodeError"]],["impl Send for ExportedSessionKey",1,["vodozemac::megolm::session_keys::ExportedSessionKey"]],["impl Send for SessionKey",1,["vodozemac::megolm::session_keys::SessionKey"]],["impl Send for OneTimeKeyGenerationResult",1,["vodozemac::olm::account::one_time_keys::OneTimeKeyGenerationResult"]],["impl Send for SessionCreationError",1,["vodozemac::olm::account::SessionCreationError"]],["impl Send for IdentityKeys",1,["vodozemac::olm::account::IdentityKeys"]],["impl Send for InboundCreationResult",1,["vodozemac::olm::account::InboundCreationResult"]],["impl Send for Account",1,["vodozemac::olm::account::Account"]],["impl Send for AccountPickle",1,["vodozemac::olm::account::AccountPickle"]],["impl Send for Message",1,["vodozemac::olm::messages::message::Message"]],["impl Send for PreKeyMessage",1,["vodozemac::olm::messages::pre_key::PreKeyMessage"]],["impl Send for OlmMessage",1,["vodozemac::olm::messages::OlmMessage"]],["impl Send for MessageType",1,["vodozemac::olm::messages::MessageType"]],["impl Send for RatchetPublicKey",1,["vodozemac::olm::session::ratchet::RatchetPublicKey"]],["impl Send for DecryptionError",1,["vodozemac::olm::session::DecryptionError"]],["impl Send for Session",1,["vodozemac::olm::session::Session"]],["impl Send for SessionPickle",1,["vodozemac::olm::session::SessionPickle"]],["impl Send for SessionConfig",1,["vodozemac::olm::session_config::SessionConfig"]],["impl Send for SessionKeys",1,["vodozemac::olm::session_keys::SessionKeys"]],["impl Send for Mac",1,["vodozemac::sas::Mac"]],["impl Send for InvalidCount",1,["vodozemac::sas::InvalidCount"]],["impl Send for SasError",1,["vodozemac::sas::SasError"]],["impl Send for Sas",1,["vodozemac::sas::Sas"]],["impl Send for EstablishedSas",1,["vodozemac::sas::EstablishedSas"]],["impl Send for SasBytes",1,["vodozemac::sas::SasBytes"]],["impl Send for PickleError",1,["vodozemac::PickleError"]],["impl Send for LibolmPickleError",1,["vodozemac::LibolmPickleError"]],["impl Send for DecodeError",1,["vodozemac::DecodeError"]]] +};if (window.register_implementors) {window.register_implementors(implementors);} else {window.pending_implementors = implementors;}})() \ No newline at end of file diff --git a/trait.impl/core/marker/trait.StructuralPartialEq.js b/trait.impl/core/marker/trait.StructuralPartialEq.js new file mode 100644 index 00000000..521aae42 --- /dev/null +++ b/trait.impl/core/marker/trait.StructuralPartialEq.js @@ -0,0 +1,3 @@ +(function() {var implementors = { +"vodozemac":[["impl StructuralPartialEq for IdentityKeys"],["impl StructuralPartialEq for Curve25519PublicKey"],["impl StructuralPartialEq for KeyId"],["impl StructuralPartialEq for MegolmMessage"],["impl StructuralPartialEq for Ed25519Signature"],["impl StructuralPartialEq for MessageType"],["impl StructuralPartialEq for PreKeyMessage"],["impl StructuralPartialEq for SessionKeys"],["impl StructuralPartialEq for SessionConfig"],["impl StructuralPartialEq for SasBytes"],["impl StructuralPartialEq for OlmMessage"],["impl StructuralPartialEq for SessionOrdering"],["impl StructuralPartialEq for Message"],["impl StructuralPartialEq for Ed25519PublicKey"],["impl StructuralPartialEq for DecryptedMessage"],["impl StructuralPartialEq for SessionConfig"],["impl StructuralPartialEq for RatchetPublicKey"]] +};if (window.register_implementors) {window.register_implementors(implementors);} else {window.pending_implementors = implementors;}})() \ No newline at end of file diff --git a/trait.impl/core/marker/trait.Sync.js b/trait.impl/core/marker/trait.Sync.js new file mode 100644 index 00000000..d4e47317 --- /dev/null +++ b/trait.impl/core/marker/trait.Sync.js @@ -0,0 +1,3 @@ +(function() {var implementors = { +"vodozemac":[["impl Sync for Curve25519SecretKey",1,["vodozemac::types::curve25519::Curve25519SecretKey"]],["impl Sync for Curve25519PublicKey",1,["vodozemac::types::curve25519::Curve25519PublicKey"]],["impl Sync for SignatureError",1,["vodozemac::types::ed25519::SignatureError"]],["impl Sync for Ed25519Keypair",1,["vodozemac::types::ed25519::Ed25519Keypair"]],["impl Sync for Ed25519SecretKey",1,["vodozemac::types::ed25519::Ed25519SecretKey"]],["impl Sync for Ed25519PublicKey",1,["vodozemac::types::ed25519::Ed25519PublicKey"]],["impl Sync for Ed25519Signature",1,["vodozemac::types::ed25519::Ed25519Signature"]],["impl Sync for KeyId",1,["vodozemac::types::KeyId"]],["impl Sync for KeyError",1,["vodozemac::types::KeyError"]],["impl Sync for GroupSession",1,["vodozemac::megolm::group_session::GroupSession"]],["impl Sync for GroupSessionPickle",1,["vodozemac::megolm::group_session::GroupSessionPickle"]],["impl Sync for SessionOrdering",1,["vodozemac::megolm::inbound_group_session::SessionOrdering"]],["impl Sync for DecryptionError",1,["vodozemac::megolm::inbound_group_session::DecryptionError"]],["impl Sync for InboundGroupSession",1,["vodozemac::megolm::inbound_group_session::InboundGroupSession"]],["impl Sync for DecryptedMessage",1,["vodozemac::megolm::inbound_group_session::DecryptedMessage"]],["impl Sync for InboundGroupSessionPickle",1,["vodozemac::megolm::inbound_group_session::InboundGroupSessionPickle"]],["impl Sync for MegolmMessage",1,["vodozemac::megolm::message::MegolmMessage"]],["impl Sync for SessionConfig",1,["vodozemac::megolm::session_config::SessionConfig"]],["impl Sync for SessionKeyDecodeError",1,["vodozemac::megolm::session_keys::SessionKeyDecodeError"]],["impl Sync for ExportedSessionKey",1,["vodozemac::megolm::session_keys::ExportedSessionKey"]],["impl Sync for SessionKey",1,["vodozemac::megolm::session_keys::SessionKey"]],["impl Sync for OneTimeKeyGenerationResult",1,["vodozemac::olm::account::one_time_keys::OneTimeKeyGenerationResult"]],["impl Sync for SessionCreationError",1,["vodozemac::olm::account::SessionCreationError"]],["impl Sync for IdentityKeys",1,["vodozemac::olm::account::IdentityKeys"]],["impl Sync for InboundCreationResult",1,["vodozemac::olm::account::InboundCreationResult"]],["impl Sync for Account",1,["vodozemac::olm::account::Account"]],["impl Sync for AccountPickle",1,["vodozemac::olm::account::AccountPickle"]],["impl Sync for Message",1,["vodozemac::olm::messages::message::Message"]],["impl Sync for PreKeyMessage",1,["vodozemac::olm::messages::pre_key::PreKeyMessage"]],["impl Sync for OlmMessage",1,["vodozemac::olm::messages::OlmMessage"]],["impl Sync for MessageType",1,["vodozemac::olm::messages::MessageType"]],["impl Sync for RatchetPublicKey",1,["vodozemac::olm::session::ratchet::RatchetPublicKey"]],["impl Sync for DecryptionError",1,["vodozemac::olm::session::DecryptionError"]],["impl Sync for Session",1,["vodozemac::olm::session::Session"]],["impl Sync for SessionPickle",1,["vodozemac::olm::session::SessionPickle"]],["impl Sync for SessionConfig",1,["vodozemac::olm::session_config::SessionConfig"]],["impl Sync for SessionKeys",1,["vodozemac::olm::session_keys::SessionKeys"]],["impl Sync for Mac",1,["vodozemac::sas::Mac"]],["impl Sync for InvalidCount",1,["vodozemac::sas::InvalidCount"]],["impl Sync for SasError",1,["vodozemac::sas::SasError"]],["impl Sync for Sas",1,["vodozemac::sas::Sas"]],["impl Sync for EstablishedSas",1,["vodozemac::sas::EstablishedSas"]],["impl Sync for SasBytes",1,["vodozemac::sas::SasBytes"]],["impl Sync for PickleError",1,["vodozemac::PickleError"]],["impl Sync for LibolmPickleError",1,["vodozemac::LibolmPickleError"]],["impl Sync for DecodeError",1,["vodozemac::DecodeError"]]] +};if (window.register_implementors) {window.register_implementors(implementors);} else {window.pending_implementors = implementors;}})() \ No newline at end of file diff --git a/trait.impl/core/marker/trait.Unpin.js b/trait.impl/core/marker/trait.Unpin.js new file mode 100644 index 00000000..0c3e1e14 --- /dev/null +++ b/trait.impl/core/marker/trait.Unpin.js @@ -0,0 +1,3 @@ +(function() {var implementors = { +"vodozemac":[["impl Unpin for Curve25519SecretKey",1,["vodozemac::types::curve25519::Curve25519SecretKey"]],["impl Unpin for Curve25519PublicKey",1,["vodozemac::types::curve25519::Curve25519PublicKey"]],["impl Unpin for SignatureError",1,["vodozemac::types::ed25519::SignatureError"]],["impl Unpin for Ed25519Keypair",1,["vodozemac::types::ed25519::Ed25519Keypair"]],["impl Unpin for Ed25519SecretKey",1,["vodozemac::types::ed25519::Ed25519SecretKey"]],["impl Unpin for Ed25519PublicKey",1,["vodozemac::types::ed25519::Ed25519PublicKey"]],["impl Unpin for Ed25519Signature",1,["vodozemac::types::ed25519::Ed25519Signature"]],["impl Unpin for KeyId",1,["vodozemac::types::KeyId"]],["impl Unpin for KeyError",1,["vodozemac::types::KeyError"]],["impl Unpin for GroupSession",1,["vodozemac::megolm::group_session::GroupSession"]],["impl Unpin for GroupSessionPickle",1,["vodozemac::megolm::group_session::GroupSessionPickle"]],["impl Unpin for SessionOrdering",1,["vodozemac::megolm::inbound_group_session::SessionOrdering"]],["impl Unpin for DecryptionError",1,["vodozemac::megolm::inbound_group_session::DecryptionError"]],["impl Unpin for InboundGroupSession",1,["vodozemac::megolm::inbound_group_session::InboundGroupSession"]],["impl Unpin for DecryptedMessage",1,["vodozemac::megolm::inbound_group_session::DecryptedMessage"]],["impl Unpin for InboundGroupSessionPickle",1,["vodozemac::megolm::inbound_group_session::InboundGroupSessionPickle"]],["impl Unpin for MegolmMessage",1,["vodozemac::megolm::message::MegolmMessage"]],["impl Unpin for SessionConfig",1,["vodozemac::megolm::session_config::SessionConfig"]],["impl Unpin for SessionKeyDecodeError",1,["vodozemac::megolm::session_keys::SessionKeyDecodeError"]],["impl Unpin for ExportedSessionKey",1,["vodozemac::megolm::session_keys::ExportedSessionKey"]],["impl Unpin for SessionKey",1,["vodozemac::megolm::session_keys::SessionKey"]],["impl Unpin for OneTimeKeyGenerationResult",1,["vodozemac::olm::account::one_time_keys::OneTimeKeyGenerationResult"]],["impl Unpin for SessionCreationError",1,["vodozemac::olm::account::SessionCreationError"]],["impl Unpin for IdentityKeys",1,["vodozemac::olm::account::IdentityKeys"]],["impl Unpin for InboundCreationResult",1,["vodozemac::olm::account::InboundCreationResult"]],["impl Unpin for Account",1,["vodozemac::olm::account::Account"]],["impl Unpin for AccountPickle",1,["vodozemac::olm::account::AccountPickle"]],["impl Unpin for Message",1,["vodozemac::olm::messages::message::Message"]],["impl Unpin for PreKeyMessage",1,["vodozemac::olm::messages::pre_key::PreKeyMessage"]],["impl Unpin for OlmMessage",1,["vodozemac::olm::messages::OlmMessage"]],["impl Unpin for MessageType",1,["vodozemac::olm::messages::MessageType"]],["impl Unpin for RatchetPublicKey",1,["vodozemac::olm::session::ratchet::RatchetPublicKey"]],["impl Unpin for DecryptionError",1,["vodozemac::olm::session::DecryptionError"]],["impl Unpin for Session",1,["vodozemac::olm::session::Session"]],["impl Unpin for SessionPickle",1,["vodozemac::olm::session::SessionPickle"]],["impl Unpin for SessionConfig",1,["vodozemac::olm::session_config::SessionConfig"]],["impl Unpin for SessionKeys",1,["vodozemac::olm::session_keys::SessionKeys"]],["impl Unpin for Mac",1,["vodozemac::sas::Mac"]],["impl Unpin for InvalidCount",1,["vodozemac::sas::InvalidCount"]],["impl Unpin for SasError",1,["vodozemac::sas::SasError"]],["impl Unpin for Sas",1,["vodozemac::sas::Sas"]],["impl Unpin for EstablishedSas",1,["vodozemac::sas::EstablishedSas"]],["impl Unpin for SasBytes",1,["vodozemac::sas::SasBytes"]],["impl Unpin for PickleError",1,["vodozemac::PickleError"]],["impl Unpin for LibolmPickleError",1,["vodozemac::LibolmPickleError"]],["impl Unpin for DecodeError",1,["vodozemac::DecodeError"]]] +};if (window.register_implementors) {window.register_implementors(implementors);} else {window.pending_implementors = implementors;}})() \ No newline at end of file diff --git a/trait.impl/core/ops/drop/trait.Drop.js b/trait.impl/core/ops/drop/trait.Drop.js new file mode 100644 index 00000000..625d9fcb --- /dev/null +++ b/trait.impl/core/ops/drop/trait.Drop.js @@ -0,0 +1,3 @@ +(function() {var implementors = { +"vodozemac":[["impl Drop for ExportedSessionKey"],["impl Drop for SessionKey"]] +};if (window.register_implementors) {window.register_implementors(implementors);} else {window.pending_implementors = implementors;}})() \ No newline at end of file diff --git a/trait.impl/core/panic/unwind_safe/trait.RefUnwindSafe.js b/trait.impl/core/panic/unwind_safe/trait.RefUnwindSafe.js new file mode 100644 index 00000000..974a718d --- /dev/null +++ b/trait.impl/core/panic/unwind_safe/trait.RefUnwindSafe.js @@ -0,0 +1,3 @@ +(function() {var implementors = { +"vodozemac":[["impl RefUnwindSafe for Curve25519SecretKey",1,["vodozemac::types::curve25519::Curve25519SecretKey"]],["impl RefUnwindSafe for Curve25519PublicKey",1,["vodozemac::types::curve25519::Curve25519PublicKey"]],["impl !RefUnwindSafe for SignatureError",1,["vodozemac::types::ed25519::SignatureError"]],["impl RefUnwindSafe for Ed25519Keypair",1,["vodozemac::types::ed25519::Ed25519Keypair"]],["impl RefUnwindSafe for Ed25519SecretKey",1,["vodozemac::types::ed25519::Ed25519SecretKey"]],["impl RefUnwindSafe for Ed25519PublicKey",1,["vodozemac::types::ed25519::Ed25519PublicKey"]],["impl RefUnwindSafe for Ed25519Signature",1,["vodozemac::types::ed25519::Ed25519Signature"]],["impl RefUnwindSafe for KeyId",1,["vodozemac::types::KeyId"]],["impl !RefUnwindSafe for KeyError",1,["vodozemac::types::KeyError"]],["impl RefUnwindSafe for GroupSession",1,["vodozemac::megolm::group_session::GroupSession"]],["impl RefUnwindSafe for GroupSessionPickle",1,["vodozemac::megolm::group_session::GroupSessionPickle"]],["impl RefUnwindSafe for SessionOrdering",1,["vodozemac::megolm::inbound_group_session::SessionOrdering"]],["impl !RefUnwindSafe for DecryptionError",1,["vodozemac::megolm::inbound_group_session::DecryptionError"]],["impl RefUnwindSafe for InboundGroupSession",1,["vodozemac::megolm::inbound_group_session::InboundGroupSession"]],["impl RefUnwindSafe for DecryptedMessage",1,["vodozemac::megolm::inbound_group_session::DecryptedMessage"]],["impl RefUnwindSafe for InboundGroupSessionPickle",1,["vodozemac::megolm::inbound_group_session::InboundGroupSessionPickle"]],["impl RefUnwindSafe for MegolmMessage",1,["vodozemac::megolm::message::MegolmMessage"]],["impl RefUnwindSafe for SessionConfig",1,["vodozemac::megolm::session_config::SessionConfig"]],["impl !RefUnwindSafe for SessionKeyDecodeError",1,["vodozemac::megolm::session_keys::SessionKeyDecodeError"]],["impl RefUnwindSafe for ExportedSessionKey",1,["vodozemac::megolm::session_keys::ExportedSessionKey"]],["impl RefUnwindSafe for SessionKey",1,["vodozemac::megolm::session_keys::SessionKey"]],["impl RefUnwindSafe for OneTimeKeyGenerationResult",1,["vodozemac::olm::account::one_time_keys::OneTimeKeyGenerationResult"]],["impl RefUnwindSafe for SessionCreationError",1,["vodozemac::olm::account::SessionCreationError"]],["impl RefUnwindSafe for IdentityKeys",1,["vodozemac::olm::account::IdentityKeys"]],["impl RefUnwindSafe for InboundCreationResult",1,["vodozemac::olm::account::InboundCreationResult"]],["impl RefUnwindSafe for Account",1,["vodozemac::olm::account::Account"]],["impl RefUnwindSafe for AccountPickle",1,["vodozemac::olm::account::AccountPickle"]],["impl RefUnwindSafe for Message",1,["vodozemac::olm::messages::message::Message"]],["impl RefUnwindSafe for PreKeyMessage",1,["vodozemac::olm::messages::pre_key::PreKeyMessage"]],["impl RefUnwindSafe for OlmMessage",1,["vodozemac::olm::messages::OlmMessage"]],["impl RefUnwindSafe for MessageType",1,["vodozemac::olm::messages::MessageType"]],["impl RefUnwindSafe for RatchetPublicKey",1,["vodozemac::olm::session::ratchet::RatchetPublicKey"]],["impl RefUnwindSafe for DecryptionError",1,["vodozemac::olm::session::DecryptionError"]],["impl RefUnwindSafe for Session",1,["vodozemac::olm::session::Session"]],["impl RefUnwindSafe for SessionPickle",1,["vodozemac::olm::session::SessionPickle"]],["impl RefUnwindSafe for SessionConfig",1,["vodozemac::olm::session_config::SessionConfig"]],["impl RefUnwindSafe for SessionKeys",1,["vodozemac::olm::session_keys::SessionKeys"]],["impl RefUnwindSafe for Mac",1,["vodozemac::sas::Mac"]],["impl RefUnwindSafe for InvalidCount",1,["vodozemac::sas::InvalidCount"]],["impl RefUnwindSafe for SasError",1,["vodozemac::sas::SasError"]],["impl RefUnwindSafe for Sas",1,["vodozemac::sas::Sas"]],["impl RefUnwindSafe for EstablishedSas",1,["vodozemac::sas::EstablishedSas"]],["impl RefUnwindSafe for SasBytes",1,["vodozemac::sas::SasBytes"]],["impl !RefUnwindSafe for PickleError",1,["vodozemac::PickleError"]],["impl !RefUnwindSafe for LibolmPickleError",1,["vodozemac::LibolmPickleError"]],["impl !RefUnwindSafe for DecodeError",1,["vodozemac::DecodeError"]]] +};if (window.register_implementors) {window.register_implementors(implementors);} else {window.pending_implementors = implementors;}})() \ No newline at end of file diff --git a/trait.impl/core/panic/unwind_safe/trait.UnwindSafe.js b/trait.impl/core/panic/unwind_safe/trait.UnwindSafe.js new file mode 100644 index 00000000..ec36890b --- /dev/null +++ b/trait.impl/core/panic/unwind_safe/trait.UnwindSafe.js @@ -0,0 +1,3 @@ +(function() {var implementors = { +"vodozemac":[["impl UnwindSafe for Curve25519SecretKey",1,["vodozemac::types::curve25519::Curve25519SecretKey"]],["impl UnwindSafe for Curve25519PublicKey",1,["vodozemac::types::curve25519::Curve25519PublicKey"]],["impl !UnwindSafe for SignatureError",1,["vodozemac::types::ed25519::SignatureError"]],["impl UnwindSafe for Ed25519Keypair",1,["vodozemac::types::ed25519::Ed25519Keypair"]],["impl UnwindSafe for Ed25519SecretKey",1,["vodozemac::types::ed25519::Ed25519SecretKey"]],["impl UnwindSafe for Ed25519PublicKey",1,["vodozemac::types::ed25519::Ed25519PublicKey"]],["impl UnwindSafe for Ed25519Signature",1,["vodozemac::types::ed25519::Ed25519Signature"]],["impl UnwindSafe for KeyId",1,["vodozemac::types::KeyId"]],["impl !UnwindSafe for KeyError",1,["vodozemac::types::KeyError"]],["impl UnwindSafe for GroupSession",1,["vodozemac::megolm::group_session::GroupSession"]],["impl UnwindSafe for GroupSessionPickle",1,["vodozemac::megolm::group_session::GroupSessionPickle"]],["impl UnwindSafe for SessionOrdering",1,["vodozemac::megolm::inbound_group_session::SessionOrdering"]],["impl !UnwindSafe for DecryptionError",1,["vodozemac::megolm::inbound_group_session::DecryptionError"]],["impl UnwindSafe for InboundGroupSession",1,["vodozemac::megolm::inbound_group_session::InboundGroupSession"]],["impl UnwindSafe for DecryptedMessage",1,["vodozemac::megolm::inbound_group_session::DecryptedMessage"]],["impl UnwindSafe for InboundGroupSessionPickle",1,["vodozemac::megolm::inbound_group_session::InboundGroupSessionPickle"]],["impl UnwindSafe for MegolmMessage",1,["vodozemac::megolm::message::MegolmMessage"]],["impl UnwindSafe for SessionConfig",1,["vodozemac::megolm::session_config::SessionConfig"]],["impl !UnwindSafe for SessionKeyDecodeError",1,["vodozemac::megolm::session_keys::SessionKeyDecodeError"]],["impl UnwindSafe for ExportedSessionKey",1,["vodozemac::megolm::session_keys::ExportedSessionKey"]],["impl UnwindSafe for SessionKey",1,["vodozemac::megolm::session_keys::SessionKey"]],["impl UnwindSafe for OneTimeKeyGenerationResult",1,["vodozemac::olm::account::one_time_keys::OneTimeKeyGenerationResult"]],["impl UnwindSafe for SessionCreationError",1,["vodozemac::olm::account::SessionCreationError"]],["impl UnwindSafe for IdentityKeys",1,["vodozemac::olm::account::IdentityKeys"]],["impl UnwindSafe for InboundCreationResult",1,["vodozemac::olm::account::InboundCreationResult"]],["impl UnwindSafe for Account",1,["vodozemac::olm::account::Account"]],["impl UnwindSafe for AccountPickle",1,["vodozemac::olm::account::AccountPickle"]],["impl UnwindSafe for Message",1,["vodozemac::olm::messages::message::Message"]],["impl UnwindSafe for PreKeyMessage",1,["vodozemac::olm::messages::pre_key::PreKeyMessage"]],["impl UnwindSafe for OlmMessage",1,["vodozemac::olm::messages::OlmMessage"]],["impl UnwindSafe for MessageType",1,["vodozemac::olm::messages::MessageType"]],["impl UnwindSafe for RatchetPublicKey",1,["vodozemac::olm::session::ratchet::RatchetPublicKey"]],["impl UnwindSafe for DecryptionError",1,["vodozemac::olm::session::DecryptionError"]],["impl UnwindSafe for Session",1,["vodozemac::olm::session::Session"]],["impl UnwindSafe for SessionPickle",1,["vodozemac::olm::session::SessionPickle"]],["impl UnwindSafe for SessionConfig",1,["vodozemac::olm::session_config::SessionConfig"]],["impl UnwindSafe for SessionKeys",1,["vodozemac::olm::session_keys::SessionKeys"]],["impl UnwindSafe for Mac",1,["vodozemac::sas::Mac"]],["impl UnwindSafe for InvalidCount",1,["vodozemac::sas::InvalidCount"]],["impl UnwindSafe for SasError",1,["vodozemac::sas::SasError"]],["impl UnwindSafe for Sas",1,["vodozemac::sas::Sas"]],["impl UnwindSafe for EstablishedSas",1,["vodozemac::sas::EstablishedSas"]],["impl UnwindSafe for SasBytes",1,["vodozemac::sas::SasBytes"]],["impl !UnwindSafe for PickleError",1,["vodozemac::PickleError"]],["impl !UnwindSafe for LibolmPickleError",1,["vodozemac::LibolmPickleError"]],["impl !UnwindSafe for DecodeError",1,["vodozemac::DecodeError"]]] +};if (window.register_implementors) {window.register_implementors(implementors);} else {window.pending_implementors = implementors;}})() \ No newline at end of file diff --git a/trait.impl/matrix_pickle/decode/trait.Decode.js b/trait.impl/matrix_pickle/decode/trait.Decode.js new file mode 100644 index 00000000..81a8eaa5 --- /dev/null +++ b/trait.impl/matrix_pickle/decode/trait.Decode.js @@ -0,0 +1,3 @@ +(function() {var implementors = { +"vodozemac":[["impl Decode for SessionKeys"],["impl Decode for Curve25519PublicKey"]] +};if (window.register_implementors) {window.register_implementors(implementors);} else {window.pending_implementors = implementors;}})() \ No newline at end of file diff --git a/trait.impl/serde/de/trait.Deserialize.js b/trait.impl/serde/de/trait.Deserialize.js new file mode 100644 index 00000000..be60ba47 --- /dev/null +++ b/trait.impl/serde/de/trait.Deserialize.js @@ -0,0 +1,3 @@ +(function() {var implementors = { +"vodozemac":[["impl<'de> Deserialize<'de> for PreKeyMessage"],["impl<'de> Deserialize<'de> for AccountPickle"],["impl<'de> Deserialize<'de> for IdentityKeys"],["impl<'de> Deserialize<'de> for Ed25519PublicKey"],["impl<'de> Deserialize<'de> for KeyId"],["impl<'de> Deserialize<'de> for Curve25519SecretKey"],["impl<'de> Deserialize<'de> for SessionConfig"],["impl<'de> Deserialize<'de> for SessionPickle"],["impl<'de> Deserialize<'de> for MegolmMessage"],["impl<'de> Deserialize<'de> for Ed25519SecretKey"],["impl<'de> Deserialize<'de> for Curve25519PublicKey"],["impl<'de> Deserialize<'de> for ExportedSessionKey"],["impl<'de> Deserialize<'de> for SessionConfig"],["impl<'de> Deserialize<'de> for SessionKeys"],["impl<'de> Deserialize<'de> for Message"],["impl<'de> Deserialize<'de> for InboundGroupSessionPickle"],["impl<'de> Deserialize<'de> for GroupSessionPickle"],["impl<'de> Deserialize<'de> for InboundGroupSession"],["impl<'de> Deserialize<'de> for OlmMessage"],["impl<'de> Deserialize<'de> for SessionKey"],["impl<'de> Deserialize<'de> for Ed25519Keypair"]] +};if (window.register_implementors) {window.register_implementors(implementors);} else {window.pending_implementors = implementors;}})() \ No newline at end of file diff --git a/trait.impl/serde/ser/trait.Serialize.js b/trait.impl/serde/ser/trait.Serialize.js new file mode 100644 index 00000000..f022cbf8 --- /dev/null +++ b/trait.impl/serde/ser/trait.Serialize.js @@ -0,0 +1,3 @@ +(function() {var implementors = { +"vodozemac":[["impl Serialize for InboundGroupSessionPickle"],["impl Serialize for GroupSessionPickle"],["impl Serialize for Message"],["impl Serialize for AccountPickle"],["impl Serialize for Curve25519SecretKey"],["impl Serialize for IdentityKeys"],["impl Serialize for Ed25519Keypair"],["impl Serialize for Ed25519PublicKey"],["impl Serialize for KeyId"],["impl Serialize for ExportedSessionKey"],["impl Serialize for SessionKey"],["impl Serialize for Ed25519SecretKey"],["impl Serialize for OlmMessage"],["impl Serialize for SessionConfig"],["impl Serialize for MegolmMessage"],["impl Serialize for SessionConfig"],["impl Serialize for SessionKeys"],["impl Serialize for PreKeyMessage"],["impl Serialize for SessionPickle"],["impl Serialize for Curve25519PublicKey"]] +};if (window.register_implementors) {window.register_implementors(implementors);} else {window.pending_implementors = implementors;}})() \ No newline at end of file diff --git a/trait.impl/zeroize/trait.Zeroize.js b/trait.impl/zeroize/trait.Zeroize.js new file mode 100644 index 00000000..c197d016 --- /dev/null +++ b/trait.impl/zeroize/trait.Zeroize.js @@ -0,0 +1,3 @@ +(function() {var implementors = { +"vodozemac":[["impl Zeroize for ExportedSessionKey"],["impl Zeroize for SessionKey"]] +};if (window.register_implementors) {window.register_implementors(implementors);} else {window.pending_implementors = implementors;}})() \ No newline at end of file diff --git a/vodozemac/all.html b/vodozemac/all.html new file mode 100644 index 00000000..47879511 --- /dev/null +++ b/vodozemac/all.html @@ -0,0 +1,2 @@ +List of all items in this crate +
\ No newline at end of file diff --git a/vodozemac/enum.Base64DecodeError.html b/vodozemac/enum.Base64DecodeError.html new file mode 100644 index 00000000..2dafd6ec --- /dev/null +++ b/vodozemac/enum.Base64DecodeError.html @@ -0,0 +1,36 @@ +Base64DecodeError in vodozemac - Rust +
pub enum Base64DecodeError {
+    InvalidByte(usize, u8),
+    InvalidLength,
+    InvalidLastSymbol(usize, u8),
+    InvalidPadding,
+}
Expand description

Errors that can occur while decoding.

+

Variants§

§

InvalidByte(usize, u8)

An invalid byte was found in the input. The offset and offending byte are provided. +Padding characters (=) interspersed in the encoded form will be treated as invalid bytes.

+
§

InvalidLength

The length of the input is invalid. +A typical cause of this is stray trailing whitespace or other separator bytes. +In the case where excess trailing bytes have produced an invalid length and the last byte +is also an invalid base64 symbol (as would be the case for whitespace, etc), InvalidByte +will be emitted instead of InvalidLength to make the issue easier to debug.

+
§

InvalidLastSymbol(usize, u8)

The last non-padding input symbol’s encoded 6 bits have nonzero bits that will be discarded. +This is indicative of corrupted or truncated Base64. +Unlike InvalidByte, which reports symbols that aren’t in the alphabet, this error is for +symbols that are in the alphabet but represent nonsensical encodings.

+
§

InvalidPadding

The nature of the padding was not as configured: absent or incorrect when it must be +canonical, or present when it must be absent, etc.

+

Trait Implementations§

source§

impl Clone for DecodeError

source§

fn clone(&self) -> DecodeError

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for DecodeError

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error>

Formats the value using the given formatter. Read more
source§

impl Display for DecodeError

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error>

Formats the value using the given formatter. Read more
source§

impl Error for DecodeError

Available on crate feature std only.
1.30.0 · source§

fn source(&self) -> Option<&(dyn Error + 'static)>

The lower-level source of this error, if any. Read more
1.0.0 · source§

fn description(&self) -> &str

👎Deprecated since 1.42.0: use the Display impl or to_string()
1.0.0 · source§

fn cause(&self) -> Option<&dyn Error>

👎Deprecated since 1.33.0: replaced by Error::source, which can support downcasting
source§

fn provide<'a>(&'a self, request: &mut Request<'a>)

🔬This is a nightly-only experimental API. (error_generic_member_access)
Provides type based access to context intended for error reports. Read more
source§

impl From<DecodeError> for DecodeError

source§

fn from(source: Base64DecodeError) -> Self

Converts to this type from the input type.
source§

impl From<DecodeError> for KeyError

source§

fn from(source: DecodeError) -> Self

Converts to this type from the input type.
source§

impl From<DecodeError> for LibolmPickleError

source§

fn from(source: Base64DecodeError) -> Self

Converts to this type from the input type.
source§

impl From<DecodeError> for PickleError

source§

fn from(source: DecodeError) -> Self

Converts to this type from the input type.
source§

impl From<DecodeError> for SessionKeyDecodeError

source§

fn from(source: DecodeError) -> Self

Converts to this type from the input type.
source§

impl From<DecodeError> for SignatureError

source§

fn from(source: DecodeError) -> Self

Converts to this type from the input type.
source§

impl PartialEq for DecodeError

source§

fn eq(&self, other: &DecodeError) -> bool

This method tests for self and other values to be equal, and is used +by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

This method tests for !=. The default implementation is almost always +sufficient, and should not be overridden without very good reason.
source§

impl Eq for DecodeError

source§

impl StructuralPartialEq for DecodeError

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T> ToString for T
where + T: Display + ?Sized,

source§

default fn to_string(&self) -> String

Converts the given value to a String. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

\ No newline at end of file diff --git a/vodozemac/enum.DecodeError.html b/vodozemac/enum.DecodeError.html new file mode 100644 index 00000000..7d69d593 --- /dev/null +++ b/vodozemac/enum.DecodeError.html @@ -0,0 +1,34 @@ +DecodeError in vodozemac - Rust +
pub enum DecodeError {
+    MessageType(usize),
+    MissingVersion,
+    MessageTooShort(usize),
+    InvalidVersion(u8, u8),
+    InvalidKey(KeyError),
+    InvalidMacLength(usize, usize),
+    Signature(SignatureError),
+    ProtoBufError(ProtoBufDecodeError),
+    Base64(Base64DecodeError),
+}
Expand description

Error type describing the different ways message decoding can fail.

+

Variants§

§

MessageType(usize)

The Olm message has an invalid type.

+
§

MissingVersion

The message is missing a valid version.

+
§

MessageTooShort(usize)

The message doesn’t have enough data to be correctly decoded.

+
§

InvalidVersion(u8, u8)

The message has a unsupported version.

+
§

InvalidKey(KeyError)

An embedded public key couldn’t be decoded.

+
§

InvalidMacLength(usize, usize)

The embedded message authentication code couldn’t be decoded.

+
§

Signature(SignatureError)

An embedded signature couldn’t be decoded.

+
§

ProtoBufError(ProtoBufDecodeError)

The message couldn’t be decoded as a valid protocol buffer message.

+
§

Base64(Base64DecodeError)

The message wasn’t valid base64.

+

Trait Implementations§

source§

impl Debug for DecodeError

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl Display for DecodeError

source§

fn fmt(&self, __formatter: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl Error for DecodeError

source§

fn source(&self) -> Option<&(dyn Error + 'static)>

The lower-level source of this error, if any. Read more
1.0.0 · source§

fn description(&self) -> &str

👎Deprecated since 1.42.0: use the Display impl or to_string()
1.0.0 · source§

fn cause(&self) -> Option<&dyn Error>

👎Deprecated since 1.33.0: replaced by Error::source, which can support downcasting
source§

fn provide<'a>(&'a self, request: &mut Request<'a>)

🔬This is a nightly-only experimental API. (error_generic_member_access)
Provides type based access to context intended for error reports. Read more
source§

impl From<DecodeError> for DecodeError

source§

fn from(source: ProtoBufDecodeError) -> Self

Converts to this type from the input type.
source§

impl From<DecodeError> for DecodeError

source§

fn from(source: Base64DecodeError) -> Self

Converts to this type from the input type.
source§

impl From<KeyError> for DecodeError

source§

fn from(source: KeyError) -> Self

Converts to this type from the input type.
source§

impl From<SignatureError> for DecodeError

source§

fn from(source: SignatureError) -> Self

Converts to this type from the input type.

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToString for T
where + T: Display + ?Sized,

source§

default fn to_string(&self) -> String

Converts the given value to a String. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

\ No newline at end of file diff --git a/vodozemac/enum.KeyError.html b/vodozemac/enum.KeyError.html new file mode 100644 index 00000000..f65fb56c --- /dev/null +++ b/vodozemac/enum.KeyError.html @@ -0,0 +1,27 @@ +KeyError in vodozemac - Rust +

Enum vodozemac::KeyError

source ·
pub enum KeyError {
+    Base64Error(DecodeError),
+    InvalidKeyLength {
+        key_type: &'static str,
+        expected_length: usize,
+        length: usize,
+    },
+    Signature(SignatureError),
+    NonContributoryKey,
+}
Expand description

Error type describing failures that can happen when we try decode or use a +cryptographic key.

+

Variants§

§

Base64Error(DecodeError)

§

InvalidKeyLength

Fields

§key_type: &'static str
§expected_length: usize
§length: usize
§

Signature(SignatureError)

§

NonContributoryKey

At least one of the keys did not have contributory behaviour and the +resulting shared secret would have been insecure.

+

Trait Implementations§

source§

impl Debug for KeyError

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl Display for KeyError

source§

fn fmt(&self, __formatter: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl Error for KeyError

source§

fn source(&self) -> Option<&(dyn Error + 'static)>

The lower-level source of this error, if any. Read more
1.0.0 · source§

fn description(&self) -> &str

👎Deprecated since 1.42.0: use the Display impl or to_string()
1.0.0 · source§

fn cause(&self) -> Option<&dyn Error>

👎Deprecated since 1.33.0: replaced by Error::source, which can support downcasting
source§

fn provide<'a>(&'a self, request: &mut Request<'a>)

🔬This is a nightly-only experimental API. (error_generic_member_access)
Provides type based access to context intended for error reports. Read more
source§

impl From<DecodeError> for KeyError

source§

fn from(source: DecodeError) -> Self

Converts to this type from the input type.
source§

impl From<KeyError> for DecodeError

source§

fn from(source: KeyError) -> Self

Converts to this type from the input type.
source§

impl From<KeyError> for LibolmPickleError

source§

fn from(source: KeyError) -> Self

Converts to this type from the input type.
source§

impl From<KeyError> for SessionKeyDecodeError

source§

fn from(source: KeyError) -> Self

Converts to this type from the input type.
source§

impl From<SignatureError> for KeyError

source§

fn from(source: SignatureError) -> Self

Converts to this type from the input type.

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToString for T
where + T: Display + ?Sized,

source§

default fn to_string(&self) -> String

Converts the given value to a String. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

\ No newline at end of file diff --git a/vodozemac/enum.LibolmPickleError.html b/vodozemac/enum.LibolmPickleError.html new file mode 100644 index 00000000..3daa9559 --- /dev/null +++ b/vodozemac/enum.LibolmPickleError.html @@ -0,0 +1,34 @@ +LibolmPickleError in vodozemac - Rust +
pub enum LibolmPickleError {
+    MissingVersion,
+    Version(u32, u32),
+    Base64(Base64DecodeError),
+    Decryption(DecryptionError),
+    PublicKey(KeyError),
+    InvalidSession,
+    Decode(DecodeError),
+    Encode(EncodeError),
+}
Available on crate feature libolm-compat only.
Expand description

Error type describing the various ways libolm pickles can fail to be +decoded.

+

Variants§

§

MissingVersion

The pickle is missing a valid version.

+
§

Version(u32, u32)

The pickle has a unsupported version.

+
§

Base64(Base64DecodeError)

The pickle wasn’t valid base64.

+
§

Decryption(DecryptionError)

The pickle could not have been decrypted.

+
§

PublicKey(KeyError)

The pickle contains an invalid public key.

+
§

InvalidSession

The pickle does not contain a valid receiving or sending chain. A valid +Olm session needs to have at least one of them.

+
§

Decode(DecodeError)

The payload of the pickle could not be decoded.

+
§

Encode(EncodeError)

The object could not be encoded as a pickle.

+

Trait Implementations§

source§

impl Debug for LibolmPickleError

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl Display for LibolmPickleError

source§

fn fmt(&self, __formatter: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl Error for LibolmPickleError

source§

fn source(&self) -> Option<&(dyn Error + 'static)>

The lower-level source of this error, if any. Read more
1.0.0 · source§

fn description(&self) -> &str

👎Deprecated since 1.42.0: use the Display impl or to_string()
1.0.0 · source§

fn cause(&self) -> Option<&dyn Error>

👎Deprecated since 1.33.0: replaced by Error::source, which can support downcasting
source§

fn provide<'a>(&'a self, request: &mut Request<'a>)

🔬This is a nightly-only experimental API. (error_generic_member_access)
Provides type based access to context intended for error reports. Read more
source§

impl From<DecodeError> for LibolmPickleError

source§

fn from(source: Base64DecodeError) -> Self

Converts to this type from the input type.
source§

impl From<DecodeError> for LibolmPickleError

source§

fn from(source: DecodeError) -> Self

Converts to this type from the input type.
source§

impl From<EncodeError> for LibolmPickleError

source§

fn from(source: EncodeError) -> Self

Converts to this type from the input type.
source§

impl From<KeyError> for LibolmPickleError

source§

fn from(source: KeyError) -> Self

Converts to this type from the input type.

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToString for T
where + T: Display + ?Sized,

source§

default fn to_string(&self) -> String

Converts the given value to a String. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

\ No newline at end of file diff --git a/vodozemac/enum.PickleError.html b/vodozemac/enum.PickleError.html new file mode 100644 index 00000000..0f8733bd --- /dev/null +++ b/vodozemac/enum.PickleError.html @@ -0,0 +1,23 @@ +PickleError in vodozemac - Rust +
pub enum PickleError {
+    Base64(DecodeError),
+    Decryption(DecryptionError),
+    Serialization(Error),
+}
Expand description

Error type describing the various ways Vodozemac pickles can fail to be +decoded.

+

Variants§

§

Base64(DecodeError)

The pickle wasn’t valid base64.

+
§

Decryption(DecryptionError)

The encrypted pickle could not have been decrypted.

+
§

Serialization(Error)

The serialized Vodozemac object couldn’t be deserialized.

+

Trait Implementations§

source§

impl Debug for PickleError

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl Display for PickleError

source§

fn fmt(&self, __formatter: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl Error for PickleError

source§

fn source(&self) -> Option<&(dyn Error + 'static)>

The lower-level source of this error, if any. Read more
1.0.0 · source§

fn description(&self) -> &str

👎Deprecated since 1.42.0: use the Display impl or to_string()
1.0.0 · source§

fn cause(&self) -> Option<&dyn Error>

👎Deprecated since 1.33.0: replaced by Error::source, which can support downcasting
source§

fn provide<'a>(&'a self, request: &mut Request<'a>)

🔬This is a nightly-only experimental API. (error_generic_member_access)
Provides type based access to context intended for error reports. Read more
source§

impl From<DecodeError> for PickleError

source§

fn from(source: DecodeError) -> Self

Converts to this type from the input type.
source§

impl From<Error> for PickleError

source§

fn from(source: Error) -> Self

Converts to this type from the input type.

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToString for T
where + T: Display + ?Sized,

source§

default fn to_string(&self) -> String

Converts the given value to a String. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

\ No newline at end of file diff --git a/vodozemac/enum.SignatureError.html b/vodozemac/enum.SignatureError.html new file mode 100644 index 00000000..3ef634f5 --- /dev/null +++ b/vodozemac/enum.SignatureError.html @@ -0,0 +1,20 @@ +SignatureError in vodozemac - Rust +
pub enum SignatureError {
+    Base64(DecodeError),
+    Signature(SignatureError),
+}
Expand description

Error type describing signature verification failures.

+

Variants§

§

Base64(DecodeError)

The signature wasn’t valid base64.

+
§

Signature(SignatureError)

The signature failed to be verified.

+

Trait Implementations§

source§

impl Debug for SignatureError

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl Display for SignatureError

source§

fn fmt(&self, __formatter: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl Error for SignatureError

source§

fn source(&self) -> Option<&(dyn Error + 'static)>

The lower-level source of this error, if any. Read more
1.0.0 · source§

fn description(&self) -> &str

👎Deprecated since 1.42.0: use the Display impl or to_string()
1.0.0 · source§

fn cause(&self) -> Option<&dyn Error>

👎Deprecated since 1.33.0: replaced by Error::source, which can support downcasting
source§

fn provide<'a>(&'a self, request: &mut Request<'a>)

🔬This is a nightly-only experimental API. (error_generic_member_access)
Provides type based access to context intended for error reports. Read more
source§

impl From<DecodeError> for SignatureError

source§

fn from(source: DecodeError) -> Self

Converts to this type from the input type.
source§

impl From<Error> for SignatureError

source§

fn from(source: SignatureError) -> Self

Converts to this type from the input type.
source§

impl From<SignatureError> for DecodeError

source§

fn from(source: SignatureError) -> Self

Converts to this type from the input type.
source§

impl From<SignatureError> for DecryptionError

source§

fn from(source: SignatureError) -> Self

Converts to this type from the input type.
source§

impl From<SignatureError> for KeyError

source§

fn from(source: SignatureError) -> Self

Converts to this type from the input type.
source§

impl From<SignatureError> for SessionKeyDecodeError

source§

fn from(source: SignatureError) -> Self

Converts to this type from the input type.

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToString for T
where + T: Display + ?Sized,

source§

default fn to_string(&self) -> String

Converts the given value to a String. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

\ No newline at end of file diff --git a/vodozemac/fn.base64_decode.html b/vodozemac/fn.base64_decode.html new file mode 100644 index 00000000..69159bc4 --- /dev/null +++ b/vodozemac/fn.base64_decode.html @@ -0,0 +1,3 @@ +base64_decode in vodozemac - Rust +

Function vodozemac::base64_decode

source ·
pub fn base64_decode(input: impl AsRef<[u8]>) -> Result<Vec<u8>, DecodeError>
Expand description

Decode the input as base64 with no padding.

+
\ No newline at end of file diff --git a/vodozemac/fn.base64_encode.html b/vodozemac/fn.base64_encode.html new file mode 100644 index 00000000..ff588383 --- /dev/null +++ b/vodozemac/fn.base64_encode.html @@ -0,0 +1,3 @@ +base64_encode in vodozemac - Rust +

Function vodozemac::base64_encode

source ·
pub fn base64_encode(input: impl AsRef<[u8]>) -> String
Expand description

Encode the input as base64 with no padding.

+
\ No newline at end of file diff --git a/vodozemac/index.html b/vodozemac/index.html new file mode 100644 index 00000000..683daf71 --- /dev/null +++ b/vodozemac/index.html @@ -0,0 +1,136 @@ +vodozemac - Rust +

Crate vodozemac

source ·
Expand description

A Rust implementation of Olm and Megolm

+

vodozemac is a Rust reimplementation of +libolm, a cryptographic library +used for end-to-end encryption in Matrix. At its core, it +is an implementation of the Olm and Megolm cryptographic ratchets, along +with a high-level API to easily establish cryptographic communication +channels employing those ratchets with other parties. It also implements +some other miscellaneous cryptographic functionality which is useful for +building Matrix clients, such as SAS.

+

§Olm

+

Olm is an implementation of the Double Ratchet +algorithm, very +similar to that employed by the Signal Protocol. It allows the establishment +of a 1-to-1 private communication channel, with perfect forward secrecy and +self-healing properties.

+

A detailed technical specification can be found at +https://gitlab.matrix.org/matrix-org/olm/-/blob/master/docs/olm.md.

+

For more information on using vodozemac for Olm, see the olm module.

+

§Megolm

+

Megolm is an AES-based single ratchet for group conversations with a large +number of participants, where using Olm would be cost prohibitive because it +would imply encrypting each message individually for each participant. +Megolm sidesteps this by encrypting messages with a symmetric ratchet, +shared once with each participant and then reused for a sequence of messages +before rotating.

+

This is a trade-off in which we lose Olm’s self-healing properties, because +someone in possession of a Megolm session at a particular state can derive +all future states. However, if the attacker is only able to obtain the +session in a ratcheted state, they cannot use it to decrypt messages +encrypted with an earlier state. This preserves forward secrecy.

+

A detailed technical specification can be found at +https://gitlab.matrix.org/matrix-org/olm/-/blob/master/docs/megolm.md.

+

For more information on using vodozemac for Megolm, see the megolm +module.

+

§Features

§Supported

+ +

§Unsupported

+ +

§Planned

+ +

§Feature flags

§Low-level API

+

Feature: low-level-api (default: off)

+

Vodozemac exposes some lower-level structs and functions that are only +useful in very advanced use cases. These should not be needed by the vast +majority of users.

+

Extreme care must be taken when using such APIs, as incorrect usage can lead +to broken sessions.

+

§Pickling

+

vodozemac supports serializing its entire internal state into a form +a “pickle”. The state can subsequently be restored from such a pickle +(“unpickled”) in order to continue operation. This is used to support some +Matrix features like device dehydration.

+

§Legacy pickles

+

The legacy pickle format is a simple binary format used by libolm. +Implemented for interoperability with current clients which are using +libolm. Only unpickling is supported.

+

§Modern pickles

+

The crate also implements a modern pickling mechanism using +Serde. The exact serialization format is not mandated +nor specified by this crate, but you can serialize to and deserialize from +any format supported by Serde.

+

The following structs support pickling:

+ +

For example, the following will print out the JSON representing the +serialized Account and will leave no new copies of the account’s secrets +in memory:

+ +
use anyhow::Result;
+use vodozemac::olm::{Account, AccountPickle};
+
+const PICKLE_KEY: [u8; 32] = [0u8; 32];
+
+fn main() -> Result<()>{
+    let mut account = Account::new();
+
+    account.generate_one_time_keys(10);
+    account.generate_fallback_key();
+
+    let pickle = account.pickle().encrypt(&PICKLE_KEY);
+
+    let account2: Account = AccountPickle::from_encrypted(&pickle, &PICKLE_KEY)?.into();
+
+    assert_eq!(account.identity_keys(), account2.identity_keys());
+
+    Ok(())
+}
+

You can unpickle a pickle-able struct directly from its serialized form:

+ +
    let mut json_str = serde_json::to_string(&some_account.pickle())?;
+    // This will produce an account which is identical to `some_account`.
+    let account: Account = serde_json::from_str::<AccountPickle>(&json_str)?.into();
+
+    json_str.zeroize();
+

However, the pickle-able structs do not implement serde::Serialize +themselves. If you want to serialize to a format other than JSON, you should +instead call the .pickle() method to obtain a special serializable struct. +This struct does implement Serialize and can therefore be serialized +into any format supported by serde. To get back to the original struct +from such as serializeable struct, just call .unpickle().

+ +
use anyhow::Result;
+use vodozemac::olm::Account;
+
+fn main() -> Result<()> {
+    let account = Account::new();
+    let account: Account = account.pickle().into();  // this is identity
+
+    Ok(())
+}
+

Modules§

  • An implementation of the Megolm ratchet.
  • An implementation of the Olm double ratchet.
  • User-friendly key verification using short authentication strings (SAS).

Structs§

Enums§

  • Errors that can occur while decoding.
  • Error type describing the different ways message decoding can fail.
  • Error type describing failures that can happen when we try decode or use a +cryptographic key.
  • LibolmPickleErrorlibolm-compat
    Error type describing the various ways libolm pickles can fail to be +decoded.
  • Error type describing the various ways Vodozemac pickles can fail to be +decoded.
  • Error type describing signature verification failures.

Statics§

  • The version of vodozemac that is being used.

Functions§

\ No newline at end of file diff --git a/vodozemac/megolm/enum.DecryptionError.html b/vodozemac/megolm/enum.DecryptionError.html new file mode 100644 index 00000000..ba0d96b6 --- /dev/null +++ b/vodozemac/megolm/enum.DecryptionError.html @@ -0,0 +1,29 @@ +DecryptionError in vodozemac::megolm - Rust +
pub enum DecryptionError {
+    Signature(SignatureError),
+    InvalidMAC(MacError),
+    InvalidMACLength(usize, usize),
+    InvalidPadding(UnpadError),
+    UnknownMessageIndex(u32, u32),
+}
Expand description

Error type for Megolm-based decryption failures.

+

Variants§

§

Signature(SignatureError)

The signature on the message was invalid.

+
§

InvalidMAC(MacError)

The message authentication code of the message was invalid.

+
§

InvalidMACLength(usize, usize)

The length of the message authentication code of the message did not +match our expected length.

+
§

InvalidPadding(UnpadError)

The ciphertext of the message isn’t padded correctly.

+
§

UnknownMessageIndex(u32, u32)

The session is missing the correct message key to decrypt the message, +The Session has been ratcheted forwards and the message key isn’t +available anymore.

+

Trait Implementations§

source§

impl Debug for DecryptionError

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl Display for DecryptionError

source§

fn fmt(&self, __formatter: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl Error for DecryptionError

source§

fn source(&self) -> Option<&(dyn Error + 'static)>

The lower-level source of this error, if any. Read more
1.0.0 · source§

fn description(&self) -> &str

👎Deprecated since 1.42.0: use the Display impl or to_string()
1.0.0 · source§

fn cause(&self) -> Option<&dyn Error>

👎Deprecated since 1.33.0: replaced by Error::source, which can support downcasting
source§

fn provide<'a>(&'a self, request: &mut Request<'a>)

🔬This is a nightly-only experimental API. (error_generic_member_access)
Provides type based access to context intended for error reports. Read more
source§

impl From<MacError> for DecryptionError

source§

fn from(source: MacError) -> Self

Converts to this type from the input type.
source§

impl From<SignatureError> for DecryptionError

source§

fn from(source: SignatureError) -> Self

Converts to this type from the input type.
source§

impl From<UnpadError> for DecryptionError

source§

fn from(source: UnpadError) -> Self

Converts to this type from the input type.

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToString for T
where + T: Display + ?Sized,

source§

default fn to_string(&self) -> String

Converts the given value to a String. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

\ No newline at end of file diff --git a/vodozemac/megolm/enum.SessionKeyDecodeError.html b/vodozemac/megolm/enum.SessionKeyDecodeError.html new file mode 100644 index 00000000..71977d9b --- /dev/null +++ b/vodozemac/megolm/enum.SessionKeyDecodeError.html @@ -0,0 +1,27 @@ +SessionKeyDecodeError in vodozemac::megolm - Rust +
pub enum SessionKeyDecodeError {
+    Version(u8, u8),
+    Read(Error),
+    Base64(DecodeError),
+    Signature(SignatureError),
+    PublicKey(KeyError),
+}
Expand description

Error type describing failure modes for the SessionKey and +ExportedSessionKey decoding.

+

Variants§

§

Version(u8, u8)

The encoded session key had a unsupported version.

+
§

Read(Error)

The encoded session key didn’t contain enough data to be decoded.

+
§

Base64(DecodeError)

The encoded session key wasn’t valid base64.

+
§

Signature(SignatureError)

The signature on the session key was invalid.

+
§

PublicKey(KeyError)

The encoded session key contains an invalid public key.

+

Trait Implementations§

source§

impl Debug for SessionKeyDecodeError

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl Display for SessionKeyDecodeError

source§

fn fmt(&self, __formatter: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl Error for SessionKeyDecodeError

source§

fn source(&self) -> Option<&(dyn Error + 'static)>

The lower-level source of this error, if any. Read more
1.0.0 · source§

fn description(&self) -> &str

👎Deprecated since 1.42.0: use the Display impl or to_string()
1.0.0 · source§

fn cause(&self) -> Option<&dyn Error>

👎Deprecated since 1.33.0: replaced by Error::source, which can support downcasting
source§

fn provide<'a>(&'a self, request: &mut Request<'a>)

🔬This is a nightly-only experimental API. (error_generic_member_access)
Provides type based access to context intended for error reports. Read more
source§

impl From<DecodeError> for SessionKeyDecodeError

source§

fn from(source: DecodeError) -> Self

Converts to this type from the input type.
source§

impl From<Error> for SessionKeyDecodeError

source§

fn from(source: Error) -> Self

Converts to this type from the input type.
source§

impl From<KeyError> for SessionKeyDecodeError

source§

fn from(source: KeyError) -> Self

Converts to this type from the input type.
source§

impl From<SignatureError> for SessionKeyDecodeError

source§

fn from(source: SignatureError) -> Self

Converts to this type from the input type.

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToString for T
where + T: Display + ?Sized,

source§

default fn to_string(&self) -> String

Converts the given value to a String. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

\ No newline at end of file diff --git a/vodozemac/megolm/enum.SessionOrdering.html b/vodozemac/megolm/enum.SessionOrdering.html new file mode 100644 index 00000000..ee781d76 --- /dev/null +++ b/vodozemac/megolm/enum.SessionOrdering.html @@ -0,0 +1,28 @@ +SessionOrdering in vodozemac::megolm - Rust +
pub enum SessionOrdering {
+    Equal,
+    Better,
+    Worse,
+    Unconnected,
+}
Expand description

The result of a comparison between two InboundGroupSession types.

+

Tells us if one session can be considered to be better than another one.

+

Variants§

§

Equal

The sessions are the same.

+
§

Better

The first session has a better initial message index than the second +one.

+
§

Worse

The first session has a worse initial message index than the second one.

+
§

Unconnected

The sessions are not the same, they can’t be compared.

+

Trait Implementations§

source§

impl Clone for SessionOrdering

source§

fn clone(&self) -> SessionOrdering

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for SessionOrdering

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl PartialEq for SessionOrdering

source§

fn eq(&self, other: &SessionOrdering) -> bool

This method tests for self and other values to be equal, and is used +by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

This method tests for !=. The default implementation is almost always +sufficient, and should not be overridden without very good reason.
source§

impl Copy for SessionOrdering

source§

impl Eq for SessionOrdering

source§

impl StructuralPartialEq for SessionOrdering

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

\ No newline at end of file diff --git a/vodozemac/megolm/group_session/struct.GroupSession.html b/vodozemac/megolm/group_session/struct.GroupSession.html new file mode 100644 index 00000000..29a24783 --- /dev/null +++ b/vodozemac/megolm/group_session/struct.GroupSession.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../vodozemac/megolm/struct.GroupSession.html...

+ + + \ No newline at end of file diff --git a/vodozemac/megolm/group_session/struct.GroupSessionPickle.html b/vodozemac/megolm/group_session/struct.GroupSessionPickle.html new file mode 100644 index 00000000..ad1c5ac5 --- /dev/null +++ b/vodozemac/megolm/group_session/struct.GroupSessionPickle.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../vodozemac/megolm/struct.GroupSessionPickle.html...

+ + + \ No newline at end of file diff --git a/vodozemac/megolm/inbound_group_session/enum.DecryptionError.html b/vodozemac/megolm/inbound_group_session/enum.DecryptionError.html new file mode 100644 index 00000000..2a788eea --- /dev/null +++ b/vodozemac/megolm/inbound_group_session/enum.DecryptionError.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../vodozemac/megolm/enum.DecryptionError.html...

+ + + \ No newline at end of file diff --git a/vodozemac/megolm/inbound_group_session/enum.SessionOrdering.html b/vodozemac/megolm/inbound_group_session/enum.SessionOrdering.html new file mode 100644 index 00000000..fbd26a56 --- /dev/null +++ b/vodozemac/megolm/inbound_group_session/enum.SessionOrdering.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../vodozemac/megolm/enum.SessionOrdering.html...

+ + + \ No newline at end of file diff --git a/vodozemac/megolm/inbound_group_session/struct.DecryptedMessage.html b/vodozemac/megolm/inbound_group_session/struct.DecryptedMessage.html new file mode 100644 index 00000000..cf8a3d0a --- /dev/null +++ b/vodozemac/megolm/inbound_group_session/struct.DecryptedMessage.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../vodozemac/megolm/struct.DecryptedMessage.html...

+ + + \ No newline at end of file diff --git a/vodozemac/megolm/inbound_group_session/struct.InboundGroupSession.html b/vodozemac/megolm/inbound_group_session/struct.InboundGroupSession.html new file mode 100644 index 00000000..0b8ca53b --- /dev/null +++ b/vodozemac/megolm/inbound_group_session/struct.InboundGroupSession.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../vodozemac/megolm/struct.InboundGroupSession.html...

+ + + \ No newline at end of file diff --git a/vodozemac/megolm/inbound_group_session/struct.InboundGroupSessionPickle.html b/vodozemac/megolm/inbound_group_session/struct.InboundGroupSessionPickle.html new file mode 100644 index 00000000..28290125 --- /dev/null +++ b/vodozemac/megolm/inbound_group_session/struct.InboundGroupSessionPickle.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../vodozemac/megolm/struct.InboundGroupSessionPickle.html...

+ + + \ No newline at end of file diff --git a/vodozemac/megolm/index.html b/vodozemac/megolm/index.html new file mode 100644 index 00000000..e9231d33 --- /dev/null +++ b/vodozemac/megolm/index.html @@ -0,0 +1,9 @@ +vodozemac::megolm - Rust +

Module vodozemac::megolm

source ·
Expand description

An implementation of the Megolm ratchet.

+

Structs§

Enums§

\ No newline at end of file diff --git a/vodozemac/megolm/message/struct.MegolmMessage.html b/vodozemac/megolm/message/struct.MegolmMessage.html new file mode 100644 index 00000000..02076c4e --- /dev/null +++ b/vodozemac/megolm/message/struct.MegolmMessage.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../vodozemac/megolm/struct.MegolmMessage.html...

+ + + \ No newline at end of file diff --git a/vodozemac/megolm/session_config/struct.SessionConfig.html b/vodozemac/megolm/session_config/struct.SessionConfig.html new file mode 100644 index 00000000..4cb4a66c --- /dev/null +++ b/vodozemac/megolm/session_config/struct.SessionConfig.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../vodozemac/megolm/struct.SessionConfig.html...

+ + + \ No newline at end of file diff --git a/vodozemac/megolm/session_keys/enum.SessionKeyDecodeError.html b/vodozemac/megolm/session_keys/enum.SessionKeyDecodeError.html new file mode 100644 index 00000000..0193bf09 --- /dev/null +++ b/vodozemac/megolm/session_keys/enum.SessionKeyDecodeError.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../vodozemac/megolm/enum.SessionKeyDecodeError.html...

+ + + \ No newline at end of file diff --git a/vodozemac/megolm/session_keys/struct.ExportedSessionKey.html b/vodozemac/megolm/session_keys/struct.ExportedSessionKey.html new file mode 100644 index 00000000..9b39fa93 --- /dev/null +++ b/vodozemac/megolm/session_keys/struct.ExportedSessionKey.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../vodozemac/megolm/struct.ExportedSessionKey.html...

+ + + \ No newline at end of file diff --git a/vodozemac/megolm/session_keys/struct.SessionKey.html b/vodozemac/megolm/session_keys/struct.SessionKey.html new file mode 100644 index 00000000..d9d14b6b --- /dev/null +++ b/vodozemac/megolm/session_keys/struct.SessionKey.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../vodozemac/megolm/struct.SessionKey.html...

+ + + \ No newline at end of file diff --git a/vodozemac/megolm/sidebar-items.js b/vodozemac/megolm/sidebar-items.js new file mode 100644 index 00000000..70c35234 --- /dev/null +++ b/vodozemac/megolm/sidebar-items.js @@ -0,0 +1 @@ +window.SIDEBAR_ITEMS = {"enum":["DecryptionError","SessionKeyDecodeError","SessionOrdering"],"struct":["DecryptedMessage","ExportedSessionKey","GroupSession","GroupSessionPickle","InboundGroupSession","InboundGroupSessionPickle","MegolmMessage","SessionConfig","SessionKey"]}; \ No newline at end of file diff --git a/vodozemac/megolm/struct.DecryptedMessage.html b/vodozemac/megolm/struct.DecryptedMessage.html new file mode 100644 index 00000000..02a993b7 --- /dev/null +++ b/vodozemac/megolm/struct.DecryptedMessage.html @@ -0,0 +1,19 @@ +DecryptedMessage in vodozemac::megolm - Rust +
pub struct DecryptedMessage {
+    pub plaintext: Vec<u8>,
+    pub message_index: u32,
+}

Fields§

§plaintext: Vec<u8>§message_index: u32

Trait Implementations§

source§

impl Clone for DecryptedMessage

source§

fn clone(&self) -> DecryptedMessage

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for DecryptedMessage

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl PartialEq for DecryptedMessage

source§

fn eq(&self, other: &DecryptedMessage) -> bool

This method tests for self and other values to be equal, and is used +by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

This method tests for !=. The default implementation is almost always +sufficient, and should not be overridden without very good reason.
source§

impl Eq for DecryptedMessage

source§

impl StructuralPartialEq for DecryptedMessage

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

\ No newline at end of file diff --git a/vodozemac/megolm/struct.ExportedSessionKey.html b/vodozemac/megolm/struct.ExportedSessionKey.html new file mode 100644 index 00000000..f4537ba2 --- /dev/null +++ b/vodozemac/megolm/struct.ExportedSessionKey.html @@ -0,0 +1,27 @@ +ExportedSessionKey in vodozemac::megolm - Rust +
pub struct ExportedSessionKey { /* private fields */ }
Expand description

The exported session key.

+

This uses the same format as the SessionKey minus the signature at the +end.

+

Implementations§

source§

impl ExportedSessionKey

source

pub fn to_bytes(&self) -> Vec<u8>

Serialize the ExportedSessionKey to a byte vector.

+
source

pub fn from_bytes(bytes: &[u8]) -> Result<Self, SessionKeyDecodeError>

Deserialize the ExportedSessionKey from a byte slice.

+
source

pub fn to_base64(&self) -> String

Serialize the ExportedSessionKey to a base64 encoded string.

+

This method will first use the ExportedSessionKey::to_bytes() to +convert the session key to a byte vector and then encode the byte vector +to a string using unpadded base64 as the encoding.

+
source

pub fn from_base64(key: &str) -> Result<Self, SessionKeyDecodeError>

Deserialize the ExportedSessionKey from base64 encoded string.

+

Trait Implementations§

source§

impl<'de> Deserialize<'de> for ExportedSessionKey

source§

fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where + D: Deserializer<'de>,

Deserialize this value from the given Serde deserializer. Read more
source§

impl Drop for ExportedSessionKey

source§

fn drop(&mut self)

Executes the destructor for this type. Read more
source§

impl Serialize for ExportedSessionKey

source§

fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where + S: Serializer,

Serialize this value into the given Serde serializer. Read more
source§

impl TryFrom<&[u8]> for ExportedSessionKey

§

type Error = SessionKeyDecodeError

The type returned in the event of a conversion error.
source§

fn try_from(value: &[u8]) -> Result<Self, Self::Error>

Performs the conversion.
source§

impl TryFrom<&str> for ExportedSessionKey

§

type Error = SessionKeyDecodeError

The type returned in the event of a conversion error.
source§

fn try_from(value: &str) -> Result<Self, Self::Error>

Performs the conversion.
source§

impl Zeroize for ExportedSessionKey

source§

fn zeroize(&mut self)

Zero out this object from memory using Rust intrinsics which ensure the +zeroization operation is not “optimized away” by the compiler.

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

source§

impl<T> DeserializeOwned for T
where + T: for<'de> Deserialize<'de>,

\ No newline at end of file diff --git a/vodozemac/megolm/struct.GroupSession.html b/vodozemac/megolm/struct.GroupSession.html new file mode 100644 index 00000000..1390cf0c --- /dev/null +++ b/vodozemac/megolm/struct.GroupSession.html @@ -0,0 +1,51 @@ +GroupSession in vodozemac::megolm - Rust +
pub struct GroupSession { /* private fields */ }
Expand description

A Megolm group session represents a single sending participant in an +encrypted group communication context containing multiple receiving parties.

+

A group session consists of a ratchet, used for encryption, and an Ed25519 +signing key pair, used for authenticity.

+

A group session containing the signing key pair is also known as an +“outbound” group session. We differentiate this from an inbound group +session where this key pair has been removed and which can be used solely +for receipt and decryption of messages.

+

Such an inbound group session is typically sent by the outbound group +session owner to each of the receiving parties via a secure peer-to-peer +channel (e.g. an Olm channel).

+

Implementations§

source§

impl GroupSession

source

pub fn new(config: SessionConfig) -> Self

Construct a new group session, with a random ratchet state and signing +key pair.

+
source

pub fn session_id(&self) -> String

Returns the globally unique session ID, in base64-encoded form.

+

A session ID is the public part of the Ed25519 key pair associated with +the group session. Due to the construction, every session ID is +(probabilistically) globally unique.

+
source

pub fn message_index(&self) -> u32

Return the current message index.

+

The message index is incremented each time a message is encrypted with +the group session.

+
source

pub fn session_config(&self) -> SessionConfig

source

pub fn encrypt(&mut self, plaintext: impl AsRef<[u8]>) -> MegolmMessage

Encrypt the plaintext with the group session.

+

The resulting ciphertext is MAC-ed, then signed with the group session’s +Ed25519 key pair and finally base64-encoded.

+
source

pub fn session_key(&self) -> SessionKey

Export the group session into a session key.

+

The session key contains the key version constant, the current message +index, the ratchet state and the public part of the signing key pair. +It is signed by the signing key pair for authenticity.

+

The session key is in a portable format, suitable for sending over the +network. It is typically sent to other group participants so that they +can reconstruct an inbound group session in order to decrypt messages +sent by this group session.

+
source

pub fn pickle(&self) -> GroupSessionPickle

Convert the group session into a struct which implements +serde::Serialize and serde::Deserialize.

+
source

pub fn from_pickle(pickle: GroupSessionPickle) -> Self

Restore a GroupSession from a previously saved +GroupSessionPickle.

+
source

pub fn from_libolm_pickle( + pickle: &str, + pickle_key: &[u8] +) -> Result<Self, LibolmPickleError>

Available on crate feature libolm-compat only.

Trait Implementations§

source§

impl Default for GroupSession

source§

fn default() -> Self

Returns the “default value” for a type. Read more
source§

impl From<&GroupSession> for InboundGroupSession

source§

fn from(session: &GroupSession) -> Self

Converts to this type from the input type.
source§

impl From<GroupSessionPickle> for GroupSession

source§

fn from(pickle: GroupSessionPickle) -> Self

Converts to this type from the input type.

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

\ No newline at end of file diff --git a/vodozemac/megolm/struct.GroupSessionPickle.html b/vodozemac/megolm/struct.GroupSessionPickle.html new file mode 100644 index 00000000..8d73fb26 --- /dev/null +++ b/vodozemac/megolm/struct.GroupSessionPickle.html @@ -0,0 +1,26 @@ +GroupSessionPickle in vodozemac::megolm - Rust +
pub struct GroupSessionPickle { /* private fields */ }
Expand description

A format suitable for serialization which implements serde::Serialize +and serde::Deserialize. Obtainable by calling GroupSession::pickle.

+

Implementations§

source§

impl GroupSessionPickle

source

pub fn encrypt(self, pickle_key: &[u8; 32]) -> String

Serialize and encrypt the pickle using the given key.

+

This is the inverse of GroupSessionPickle::from_encrypted.

+
source

pub fn from_encrypted( + ciphertext: &str, + pickle_key: &[u8; 32] +) -> Result<Self, PickleError>

Obtain a pickle from a ciphertext by decrypting and deserializing using +the given key.

+

This is the inverse of GroupSessionPickle::encrypt.

+

Trait Implementations§

source§

impl<'de> Deserialize<'de> for GroupSessionPickle

source§

fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>
where + __D: Deserializer<'de>,

Deserialize this value from the given Serde deserializer. Read more
source§

impl From<GroupSessionPickle> for GroupSession

source§

fn from(pickle: GroupSessionPickle) -> Self

Converts to this type from the input type.
source§

impl Serialize for GroupSessionPickle

source§

fn serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>
where + __S: Serializer,

Serialize this value into the given Serde serializer. Read more

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

source§

impl<T> DeserializeOwned for T
where + T: for<'de> Deserialize<'de>,

\ No newline at end of file diff --git a/vodozemac/megolm/struct.InboundGroupSession.html b/vodozemac/megolm/struct.InboundGroupSession.html new file mode 100644 index 00000000..5c081563 --- /dev/null +++ b/vodozemac/megolm/struct.InboundGroupSession.html @@ -0,0 +1,82 @@ +InboundGroupSession in vodozemac::megolm - Rust +
pub struct InboundGroupSession { /* private fields */ }

Implementations§

source§

impl InboundGroupSession

source

pub fn new(key: &SessionKey, session_config: SessionConfig) -> Self

source

pub fn import( + session_key: &ExportedSessionKey, + session_config: SessionConfig +) -> Self

source

pub fn session_id(&self) -> String

source

pub fn connected(&mut self, other: &mut InboundGroupSession) -> bool

Check if two InboundGroupSessions are the same.

+

An InboundGroupSession could be received multiple times with varying +degrees of trust and first known message indices.

+

This method checks if the underlying ratchets of the two +InboundGroupSessions are actually the same ratchet, potentially at +a different ratcheting index. That is, if the sessions are connected, +then ratcheting one of the ratchets to the index of the other should +yield the same ratchet value, byte-for-byte. This will only be the case +if the InboundGroupSessions were created from the same +GroupSession.

+

If the sessions are connected, the session with the lower message index +can safely replace the one with the higher message index.

+
source

pub fn compare(&mut self, other: &mut InboundGroupSession) -> SessionOrdering

Compare the InboundGroupSession with the given other +InboundGroupSession.

+

Returns a SessionOrdering describing how the two sessions relate to +each other.

+
source

pub fn merge( + &mut self, + other: &mut InboundGroupSession +) -> Option<InboundGroupSession>

Merge the session with the given other session, picking the best parts +from each of them.

+

This method is useful when you receive multiple sessions with +the same session ID but potentially different ratchet indices and +authenticity properties.

+

For example, imagine you receive a SessionKey S1 with ratchet index +A from a fully-trusted source and an ExportedSessionKey S2 with +ratchet state B from a less trusted source. If A > B, then S1 is better +because it’s fully trusted, but worse because it’s ratcheted further +than S2.

+

This method allows you to merge S1 and S2 safely into a fully-trusted S3 +with ratchet state B, provided S1 and S2 connect with each other +(meaning they are the same session, just at different ratchet indices).

+

Returns Some(session) if the sessions could be merged, i.e. they are +considered to be connected and None otherwise.

+
§Example
+
use vodozemac::megolm::{GroupSession, InboundGroupSession, SessionOrdering};
+
+let session = GroupSession::new(Default::default());
+let session_key = session.session_key();
+
+let mut first_session = InboundGroupSession::new(&session_key, Default::default());
+let mut second_session = InboundGroupSession::import(&first_session.export_at(10).unwrap(), Default::default());
+
+assert_eq!(first_session.compare(&mut second_session), SessionOrdering::Better);
+
+let mut merged = second_session.merge(&mut first_session).unwrap();
+
+assert_eq!(merged.compare(&mut second_session), SessionOrdering::Better);
+assert_eq!(merged.compare(&mut first_session), SessionOrdering::Equal);
+
source

pub fn first_known_index(&self) -> u32

source

pub fn advance_to(&mut self, index: u32) -> bool

Permanently advance the session to the given index.

+

This will remove the ability to decrypt messages that were encrypted +with a lower message index than what is given as the argument.

+

Returns true if the ratchet has been advanced, false if the ratchet was +already advanced past the given index.

+
source

pub fn decrypt( + &mut self, + message: &MegolmMessage +) -> Result<DecryptedMessage, DecryptionError>

source

pub fn export_at(&mut self, index: u32) -> Option<ExportedSessionKey>

source

pub fn export_at_first_known_index(&self) -> ExportedSessionKey

source

pub fn pickle(&self) -> InboundGroupSessionPickle

Convert the inbound group session into a struct which implements +serde::Serialize and serde::Deserialize.

+
source

pub fn from_pickle(pickle: InboundGroupSessionPickle) -> Self

Restore an InboundGroupSession from a previously saved +InboundGroupSessionPickle.

+
source

pub fn from_libolm_pickle( + pickle: &str, + pickle_key: &[u8] +) -> Result<Self, LibolmPickleError>

Available on crate feature libolm-compat only.

Trait Implementations§

source§

impl<'de> Deserialize<'de> for InboundGroupSession

source§

fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>
where + __D: Deserializer<'de>,

Deserialize this value from the given Serde deserializer. Read more
source§

impl From<&GroupSession> for InboundGroupSession

source§

fn from(session: &GroupSession) -> Self

Converts to this type from the input type.
source§

impl From<&InboundGroupSession> for InboundGroupSessionPickle

source§

fn from(session: &InboundGroupSession) -> Self

Converts to this type from the input type.
source§

impl From<InboundGroupSessionPickle> for InboundGroupSession

source§

fn from(pickle: InboundGroupSessionPickle) -> Self

Converts to this type from the input type.

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

source§

impl<T> DeserializeOwned for T
where + T: for<'de> Deserialize<'de>,

\ No newline at end of file diff --git a/vodozemac/megolm/struct.InboundGroupSessionPickle.html b/vodozemac/megolm/struct.InboundGroupSessionPickle.html new file mode 100644 index 00000000..09877703 --- /dev/null +++ b/vodozemac/megolm/struct.InboundGroupSessionPickle.html @@ -0,0 +1,27 @@ +InboundGroupSessionPickle in vodozemac::megolm - Rust +
pub struct InboundGroupSessionPickle { /* private fields */ }
Expand description

A format suitable for serialization which implements serde::Serialize +and serde::Deserialize. Obtainable by calling +InboundGroupSession::pickle.

+

Implementations§

source§

impl InboundGroupSessionPickle

source

pub fn encrypt(self, pickle_key: &[u8; 32]) -> String

Serialize and encrypt the pickle using the given key.

+

This is the inverse of InboundGroupSessionPickle::from_encrypted.

+
source

pub fn from_encrypted( + ciphertext: &str, + pickle_key: &[u8; 32] +) -> Result<Self, PickleError>

Obtain a pickle from a ciphertext by decrypting and deserializing using +the given key.

+

This is the inverse of InboundGroupSessionPickle::encrypt.

+

Trait Implementations§

source§

impl<'de> Deserialize<'de> for InboundGroupSessionPickle

source§

fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>
where + __D: Deserializer<'de>,

Deserialize this value from the given Serde deserializer. Read more
source§

impl From<&InboundGroupSession> for InboundGroupSessionPickle

source§

fn from(session: &InboundGroupSession) -> Self

Converts to this type from the input type.
source§

impl From<InboundGroupSessionPickle> for InboundGroupSession

source§

fn from(pickle: InboundGroupSessionPickle) -> Self

Converts to this type from the input type.
source§

impl Serialize for InboundGroupSessionPickle

source§

fn serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>
where + __S: Serializer,

Serialize this value into the given Serde serializer. Read more

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

source§

impl<T> DeserializeOwned for T
where + T: for<'de> Deserialize<'de>,

\ No newline at end of file diff --git a/vodozemac/megolm/struct.MegolmMessage.html b/vodozemac/megolm/struct.MegolmMessage.html new file mode 100644 index 00000000..1444d6c1 --- /dev/null +++ b/vodozemac/megolm/struct.MegolmMessage.html @@ -0,0 +1,49 @@ +MegolmMessage in vodozemac::megolm - Rust +
pub struct MegolmMessage { /* private fields */ }
Expand description

An encrypted Megolm message.

+

Contains metadata that is required to find the correct ratchet state of a +InboundGroupSession necessary to decrypt the message.

+

Implementations§

source§

impl MegolmMessage

source

pub fn ciphertext(&self) -> &[u8]

The actual ciphertext of the message.

+
source

pub fn message_index(&self) -> u32

The index of the message that was used when the message was encrypted.

+
source

pub fn mac(&self) -> &[u8]

Get the megolm message’s mac.

+
source

pub fn signature(&self) -> &Ed25519Signature

Get a reference to the megolm message’s signature.

+
source

pub fn from_bytes(message: &[u8]) -> Result<Self, DecodeError>

Try to decode the given byte slice as a MegolmMessage.

+

The expected format of the byte array is described in the +MegolmMessage::to_bytes() method.

+
source

pub fn to_bytes(&self) -> Vec<u8>

Encode the MegolmMessage as an array of bytes.

+

Megolm messages consist of a one byte version, followed by a variable +length payload, a fixed length message authentication code, and a fixed +length signature.

+
+---+------------------------------------+-----------+------------------+
+| V | Payload Bytes                      | MAC Bytes | Signature Bytes  |
++---+------------------------------------+-----------+------------------+
+0   1                                    N          N+8                N+72   bytes
+
+

The payload uses a format based on the Protocol Buffers encoding. It +consists of the following key-value pairs:

+
+ + +
NameTagTypeMeaning
Message-Index0x08IntegerThe index of the ratchet, i
Cipher-Text0x12StringThe cipher-text, Xi, of the message
+
source

pub fn from_base64(message: &str) -> Result<Self, DecodeError>

Try to decode the given string as a MegolmMessage.

+

The string needs to be a base64 encoded byte array that follows the +format described in the MegolmMessage::to_bytes() method.

+
source

pub fn to_base64(&self) -> String

Encode the MegolmMessage as a string.

+

This method first calls MegolmMessage::to_bytes() and then encodes +the resulting byte array as a string using base64 encoding.

+

Trait Implementations§

source§

impl Clone for MegolmMessage

source§

fn clone(&self) -> MegolmMessage

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for MegolmMessage

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl<'de> Deserialize<'de> for MegolmMessage

source§

fn deserialize<D: Deserializer<'de>>(d: D) -> Result<Self, D::Error>

Deserialize this value from the given Serde deserializer. Read more
source§

impl PartialEq for MegolmMessage

source§

fn eq(&self, other: &MegolmMessage) -> bool

This method tests for self and other values to be equal, and is used +by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

This method tests for !=. The default implementation is almost always +sufficient, and should not be overridden without very good reason.
source§

impl Serialize for MegolmMessage

source§

fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where + S: Serializer,

Serialize this value into the given Serde serializer. Read more
source§

impl TryFrom<&[u8]> for MegolmMessage

§

type Error = DecodeError

The type returned in the event of a conversion error.
source§

fn try_from(message: &[u8]) -> Result<Self, Self::Error>

Performs the conversion.
source§

impl TryFrom<&str> for MegolmMessage

§

type Error = DecodeError

The type returned in the event of a conversion error.
source§

fn try_from(message: &str) -> Result<Self, Self::Error>

Performs the conversion.
source§

impl TryFrom<Vec<u8>> for MegolmMessage

§

type Error = DecodeError

The type returned in the event of a conversion error.
source§

fn try_from(message: Vec<u8>) -> Result<Self, Self::Error>

Performs the conversion.
source§

impl Eq for MegolmMessage

source§

impl StructuralPartialEq for MegolmMessage

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

source§

impl<T> DeserializeOwned for T
where + T: for<'de> Deserialize<'de>,

\ No newline at end of file diff --git a/vodozemac/megolm/struct.SessionConfig.html b/vodozemac/megolm/struct.SessionConfig.html new file mode 100644 index 00000000..29cc16d1 --- /dev/null +++ b/vodozemac/megolm/struct.SessionConfig.html @@ -0,0 +1,28 @@ +SessionConfig in vodozemac::megolm - Rust +
pub struct SessionConfig { /* private fields */ }
Expand description

A struct to configure how Megolm sessions should work under the hood. +Currently only the MAC truncation behaviour can be configured.

+

Implementations§

source§

impl SessionConfig

source

pub fn version(&self) -> u8

Get the numeric version of this SessionConfig.

+
source

pub fn version_1() -> Self

Create a SessionConfig for the Megolm version 1. This version of +Megolm uses AES-256 and HMAC with a truncated MAC to encrypt individual +messages. The MAC will be truncated to 8 bytes.

+
source

pub fn version_2() -> Self

Create a SessionConfig for the Megolm version 2. This version of +Megolm uses AES-256 and HMAC to encrypt individual messages. The MAC +won’t be truncated.

+

Trait Implementations§

source§

impl Clone for SessionConfig

source§

fn clone(&self) -> SessionConfig

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for SessionConfig

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl Default for SessionConfig

source§

fn default() -> Self

Returns the “default value” for a type. Read more
source§

impl<'de> Deserialize<'de> for SessionConfig

source§

fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>
where + __D: Deserializer<'de>,

Deserialize this value from the given Serde deserializer. Read more
source§

impl PartialEq for SessionConfig

source§

fn eq(&self, other: &SessionConfig) -> bool

This method tests for self and other values to be equal, and is used +by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

This method tests for !=. The default implementation is almost always +sufficient, and should not be overridden without very good reason.
source§

impl Serialize for SessionConfig

source§

fn serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>
where + __S: Serializer,

Serialize this value into the given Serde serializer. Read more
source§

impl Copy for SessionConfig

source§

impl Eq for SessionConfig

source§

impl StructuralPartialEq for SessionConfig

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

source§

impl<T> DeserializeOwned for T
where + T: for<'de> Deserialize<'de>,

\ No newline at end of file diff --git a/vodozemac/megolm/struct.SessionKey.html b/vodozemac/megolm/struct.SessionKey.html new file mode 100644 index 00000000..b9a25d97 --- /dev/null +++ b/vodozemac/megolm/struct.SessionKey.html @@ -0,0 +1,36 @@ +SessionKey in vodozemac::megolm - Rust +
pub struct SessionKey { /* private fields */ }
Expand description

The session key, can be used to create a InboundGroupSession.

+

Uses the session-sharing format described in the Olm spec.

+

+—+––+––––+––––+––––+––––+——+———–+ +| V | i | R(i,0) | R(i,1) | R(i,2) | R(i,3) | Kpub | Signature | ++—+––+––––+––––+––––+––––+——+———–+ +0 1 5 37 69 101 133 165 229 bytes

+

The version byte, V, is “\x02”. +This is followed by the ratchet index, iii, which is encoded as a +big-endian 32-bit integer; the 128 bytes of the ratchet; and the public +part of the Ed25519 keypair.

+

The data is then signed using the Ed25519 key, and the 64-byte signature is +appended.

+

Implementations§

source§

impl SessionKey

source

pub fn to_bytes(&self) -> Vec<u8>

Serialize the SessionKey to a byte vector.

+
source

pub fn from_bytes(bytes: &[u8]) -> Result<Self, SessionKeyDecodeError>

Deserialize the SessionKey from a byte slice.

+
source

pub fn to_base64(&self) -> String

Serialize the SessionKey to a base64 encoded string.

+

This method will first use the SessionKey::to_bytes() to +convert the session key to a byte vector and then encode the byte vector +to a string using unpadded base64 as the encoding.

+
source

pub fn from_base64(key: &str) -> Result<Self, SessionKeyDecodeError>

Deserialize the SessionKey from base64 encoded string.

+

Trait Implementations§

source§

impl<'de> Deserialize<'de> for SessionKey

source§

fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where + D: Deserializer<'de>,

Deserialize this value from the given Serde deserializer. Read more
source§

impl Drop for SessionKey

source§

fn drop(&mut self)

Executes the destructor for this type. Read more
source§

impl Serialize for SessionKey

source§

fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where + S: Serializer,

Serialize this value into the given Serde serializer. Read more
source§

impl TryFrom<&[u8]> for SessionKey

§

type Error = SessionKeyDecodeError

The type returned in the event of a conversion error.
source§

fn try_from(value: &[u8]) -> Result<Self, Self::Error>

Performs the conversion.
source§

impl TryFrom<&str> for SessionKey

§

type Error = SessionKeyDecodeError

The type returned in the event of a conversion error.
source§

fn try_from(value: &str) -> Result<Self, Self::Error>

Performs the conversion.
source§

impl Zeroize for SessionKey

source§

fn zeroize(&mut self)

Zero out this object from memory using Rust intrinsics which ensure the +zeroization operation is not “optimized away” by the compiler.

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

source§

impl<T> DeserializeOwned for T
where + T: for<'de> Deserialize<'de>,

\ No newline at end of file diff --git a/vodozemac/olm/account/enum.SessionCreationError.html b/vodozemac/olm/account/enum.SessionCreationError.html new file mode 100644 index 00000000..48997960 --- /dev/null +++ b/vodozemac/olm/account/enum.SessionCreationError.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../vodozemac/olm/enum.SessionCreationError.html...

+ + + \ No newline at end of file diff --git a/vodozemac/olm/account/one_time_keys/struct.OneTimeKeyGenerationResult.html b/vodozemac/olm/account/one_time_keys/struct.OneTimeKeyGenerationResult.html new file mode 100644 index 00000000..11b26b6b --- /dev/null +++ b/vodozemac/olm/account/one_time_keys/struct.OneTimeKeyGenerationResult.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../../vodozemac/olm/struct.OneTimeKeyGenerationResult.html...

+ + + \ No newline at end of file diff --git a/vodozemac/olm/account/struct.Account.html b/vodozemac/olm/account/struct.Account.html new file mode 100644 index 00000000..ae067822 --- /dev/null +++ b/vodozemac/olm/account/struct.Account.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../vodozemac/olm/struct.Account.html...

+ + + \ No newline at end of file diff --git a/vodozemac/olm/account/struct.AccountPickle.html b/vodozemac/olm/account/struct.AccountPickle.html new file mode 100644 index 00000000..088e62fc --- /dev/null +++ b/vodozemac/olm/account/struct.AccountPickle.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../vodozemac/olm/struct.AccountPickle.html...

+ + + \ No newline at end of file diff --git a/vodozemac/olm/account/struct.IdentityKeys.html b/vodozemac/olm/account/struct.IdentityKeys.html new file mode 100644 index 00000000..8516157d --- /dev/null +++ b/vodozemac/olm/account/struct.IdentityKeys.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../vodozemac/olm/struct.IdentityKeys.html...

+ + + \ No newline at end of file diff --git a/vodozemac/olm/account/struct.InboundCreationResult.html b/vodozemac/olm/account/struct.InboundCreationResult.html new file mode 100644 index 00000000..fde91318 --- /dev/null +++ b/vodozemac/olm/account/struct.InboundCreationResult.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../vodozemac/olm/struct.InboundCreationResult.html...

+ + + \ No newline at end of file diff --git a/vodozemac/olm/enum.DecryptionError.html b/vodozemac/olm/enum.DecryptionError.html new file mode 100644 index 00000000..babcf9b8 --- /dev/null +++ b/vodozemac/olm/enum.DecryptionError.html @@ -0,0 +1,29 @@ +DecryptionError in vodozemac::olm - Rust +
pub enum DecryptionError {
+    InvalidMAC(MacError),
+    InvalidMACLength(usize, usize),
+    InvalidPadding(UnpadError),
+    MissingMessageKey(u64),
+    TooBigMessageGap(u64, u64),
+}
Expand description

Error type for Olm-based decryption failures.

+

Variants§

§

InvalidMAC(MacError)

The message authentication code of the message was invalid.

+
§

InvalidMACLength(usize, usize)

The length of the message authentication code of the message did not +match our expected length.

+
§

InvalidPadding(UnpadError)

The ciphertext of the message isn’t padded correctly.

+
§

MissingMessageKey(u64)

The session is missing the correct message key to decrypt the message, +either because it was already used up, or because the Session has been +ratcheted forwards and the message key has been discarded.

+
§

TooBigMessageGap(u64, u64)

Too many messages have been skipped to attempt decrypting this message.

+

Trait Implementations§

source§

impl Debug for DecryptionError

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl Display for DecryptionError

source§

fn fmt(&self, __formatter: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl Error for DecryptionError

source§

fn source(&self) -> Option<&(dyn Error + 'static)>

The lower-level source of this error, if any. Read more
1.0.0 · source§

fn description(&self) -> &str

👎Deprecated since 1.42.0: use the Display impl or to_string()
1.0.0 · source§

fn cause(&self) -> Option<&dyn Error>

👎Deprecated since 1.33.0: replaced by Error::source, which can support downcasting
source§

fn provide<'a>(&'a self, request: &mut Request<'a>)

🔬This is a nightly-only experimental API. (error_generic_member_access)
Provides type based access to context intended for error reports. Read more
source§

impl From<DecryptionError> for SessionCreationError

source§

fn from(source: DecryptionError) -> Self

Converts to this type from the input type.
source§

impl From<MacError> for DecryptionError

source§

fn from(source: MacError) -> Self

Converts to this type from the input type.
source§

impl From<UnpadError> for DecryptionError

source§

fn from(source: UnpadError) -> Self

Converts to this type from the input type.

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToString for T
where + T: Display + ?Sized,

source§

default fn to_string(&self) -> String

Converts the given value to a String. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

\ No newline at end of file diff --git a/vodozemac/olm/enum.MessageType.html b/vodozemac/olm/enum.MessageType.html new file mode 100644 index 00000000..fa5480a5 --- /dev/null +++ b/vodozemac/olm/enum.MessageType.html @@ -0,0 +1,22 @@ +MessageType in vodozemac::olm - Rust +
pub enum MessageType {
+    PreKey = 0,
+    Normal = 1,
+}
Expand description

An enum over the two supported message types.

+

Variants§

§

PreKey = 0

The pre-key message type.

+
§

Normal = 1

The normal message type.

+

Trait Implementations§

source§

impl Clone for MessageType

source§

fn clone(&self) -> MessageType

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for MessageType

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl From<MessageType> for usize

source§

fn from(value: MessageType) -> usize

Converts to this type from the input type.
source§

impl PartialEq for MessageType

source§

fn eq(&self, other: &MessageType) -> bool

This method tests for self and other values to be equal, and is used +by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

This method tests for !=. The default implementation is almost always +sufficient, and should not be overridden without very good reason.
source§

impl TryFrom<usize> for MessageType

§

type Error = ()

The type returned in the event of a conversion error.
source§

fn try_from(value: usize) -> Result<Self, Self::Error>

Performs the conversion.
source§

impl Copy for MessageType

source§

impl Eq for MessageType

source§

impl StructuralPartialEq for MessageType

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

\ No newline at end of file diff --git a/vodozemac/olm/enum.OlmMessage.html b/vodozemac/olm/enum.OlmMessage.html new file mode 100644 index 00000000..30dc62ce --- /dev/null +++ b/vodozemac/olm/enum.OlmMessage.html @@ -0,0 +1,38 @@ +OlmMessage in vodozemac::olm - Rust +
pub enum OlmMessage {
+    Normal(Message),
+    PreKey(PreKeyMessage),
+}
Expand description

Enum over the different Olm message types.

+

Olm uses two types of messages. The underlying transport protocol must +provide a means for recipients to distinguish between them.

+

OlmMessage provides Serialize and Deserialize implementations +that are compatible with Matrix.

+

Variants§

§

Normal(Message)

A normal message, contains only the ciphertext and metadata to decrypt +it.

+
§

PreKey(PreKeyMessage)

A pre-key message, contains metadata to establish a Session as well +as a Message.

+

Implementations§

source§

impl OlmMessage

source

pub fn from_parts( + message_type: usize, + ciphertext: &str +) -> Result<Self, DecodeError>

Create a OlmMessage from a message type and a ciphertext.

+
source

pub fn message(&self) -> &[u8]

Get the message as a byte array.

+
source

pub fn message_type(&self) -> MessageType

Get the type of the message.

+
source

pub fn to_parts(self) -> (usize, String)

Convert the OlmMessage into a message type, and base64 encoded message +tuple.

+

Trait Implementations§

source§

impl Clone for OlmMessage

source§

fn clone(&self) -> OlmMessage

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for OlmMessage

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl<'de> Deserialize<'de> for OlmMessage

source§

fn deserialize<D: Deserializer<'de>>(d: D) -> Result<Self, D::Error>

Deserialize this value from the given Serde deserializer. Read more
source§

impl From<Message> for OlmMessage

source§

fn from(m: Message) -> Self

Converts to this type from the input type.
source§

impl From<PreKeyMessage> for OlmMessage

source§

fn from(m: PreKeyMessage) -> Self

Converts to this type from the input type.
source§

impl PartialEq for OlmMessage

source§

fn eq(&self, other: &OlmMessage) -> bool

This method tests for self and other values to be equal, and is used +by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

This method tests for !=. The default implementation is almost always +sufficient, and should not be overridden without very good reason.
source§

impl Serialize for OlmMessage

source§

fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where + S: Serializer,

Serialize this value into the given Serde serializer. Read more
source§

impl Eq for OlmMessage

source§

impl StructuralPartialEq for OlmMessage

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

source§

impl<T> DeserializeOwned for T
where + T: for<'de> Deserialize<'de>,

\ No newline at end of file diff --git a/vodozemac/olm/enum.SessionCreationError.html b/vodozemac/olm/enum.SessionCreationError.html new file mode 100644 index 00000000..e4eab05a --- /dev/null +++ b/vodozemac/olm/enum.SessionCreationError.html @@ -0,0 +1,28 @@ +SessionCreationError in vodozemac::olm - Rust +
pub enum SessionCreationError {
+    MissingOneTimeKey(Curve25519PublicKey),
+    MismatchedIdentityKey(Curve25519PublicKey, Curve25519PublicKey),
+    Decryption(DecryptionError),
+}
Expand description

Error describing failure modes when creating a Olm Session from an incoming +Olm message.

+

Variants§

§

MissingOneTimeKey(Curve25519PublicKey)

The pre-key message contained an unknown one-time key. This happens +either because we never had such a one-time key, or because it has +already been used up.

+
§

MismatchedIdentityKey(Curve25519PublicKey, Curve25519PublicKey)

The pre-key message contains a curve25519 identity key that doesn’t +match to the identity key that was given.

+
§

Decryption(DecryptionError)

The pre-key message that was used to establish the Session couldn’t be +decrypted. The message needs to be decryptable, otherwise we will have +created a Session that wasn’t used to encrypt the pre-key message.

+

Trait Implementations§

source§

impl Debug for SessionCreationError

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl Display for SessionCreationError

source§

fn fmt(&self, __formatter: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl Error for SessionCreationError

source§

fn source(&self) -> Option<&(dyn Error + 'static)>

The lower-level source of this error, if any. Read more
1.0.0 · source§

fn description(&self) -> &str

👎Deprecated since 1.42.0: use the Display impl or to_string()
1.0.0 · source§

fn cause(&self) -> Option<&dyn Error>

👎Deprecated since 1.33.0: replaced by Error::source, which can support downcasting
source§

fn provide<'a>(&'a self, request: &mut Request<'a>)

🔬This is a nightly-only experimental API. (error_generic_member_access)
Provides type based access to context intended for error reports. Read more
source§

impl From<DecryptionError> for SessionCreationError

source§

fn from(source: DecryptionError) -> Self

Converts to this type from the input type.

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToString for T
where + T: Display + ?Sized,

source§

default fn to_string(&self) -> String

Converts the given value to a String. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

\ No newline at end of file diff --git a/vodozemac/olm/index.html b/vodozemac/olm/index.html new file mode 100644 index 00000000..59948025 --- /dev/null +++ b/vodozemac/olm/index.html @@ -0,0 +1,82 @@ +vodozemac::olm - Rust +

Module vodozemac::olm

source ·
Expand description

An implementation of the Olm double ratchet.

+

§Overview

+

The core component of the crate is the Account, representing a single Olm +participant. An Olm Account consists of a collection of key pairs, though +often documentation will shorten this to just “keys”. These are:

+
    +
  1. An Ed25519 signing key pair representing the stable cryptographic +identity of the participant (the participant’s “fingerprint”).
  2. +
  3. A Curve25519 sender key pair (also sometimes called the identity key +pair, somewhat confusingly).
  4. +
  5. A number of one-time key pairs.
  6. +
  7. A current and previous (if any) “fallback” key pair.
  8. +
+

While the key in 1 is used for signing but not encryption, the keys in 2-4 +participate in a triple Diffie-Hellman key exchange (3DH) with another Olm +participant, thereby establishing an Olm session on each side of the +communication channel. Ultimately, this session is used for deriving the +concrete encryption keys for a particular message.

+

Olm sessions are represented by the Session struct. Such a session is +created by calling Account::create_outbound_session on one of the +participating accounts, passing it the Curve25519 sender key and one +Curve25519 one-time key of the other side. The protocol is asynchronous, so +the participant can start sending messages to the other side even before the +other side has created a session, producing so-called pre-key messages (see +PreKeyMessage).

+

Once the other participant receives such a pre-key message, they can create +their own matching session by calling Account::create_inbound_session and +passing it the pre-key message they received and the Curve25519 sender key +of the other side. This completes the establishment of the Olm communication +channel.

+ +
use anyhow::Result;
+use vodozemac::olm::{Account, InboundCreationResult, OlmMessage, SessionConfig};
+
+fn main() -> Result<()> {
+    let alice = Account::new();
+    let mut bob = Account::new();
+
+    bob.generate_one_time_keys(1);
+    let bob_otk = *bob.one_time_keys().values().next().unwrap();
+
+    let mut alice_session = alice
+        .create_outbound_session(SessionConfig::version_2(), bob.curve25519_key(), bob_otk);
+
+    bob.mark_keys_as_published();
+
+    let message = "Keep it between us, OK?";
+    let alice_msg = alice_session.encrypt(message);
+
+    if let OlmMessage::PreKey(m) = alice_msg.clone() {
+        let result = bob.create_inbound_session(alice.curve25519_key(), &m)?;
+
+        let mut bob_session = result.session;
+        let what_bob_received = result.plaintext;
+
+        assert_eq!(alice_session.session_id(), bob_session.session_id());
+
+        assert_eq!(message.as_bytes(), what_bob_received);
+
+        let bob_reply = "Yes. Take this, it's dangerous out there!";
+        let bob_encrypted_reply = bob_session.encrypt(bob_reply).into();
+
+        let what_alice_received = alice_session
+            .decrypt(&bob_encrypted_reply)?;
+        assert_eq!(what_alice_received, bob_reply.as_bytes());
+    }
+
+    Ok(())
+}
+

§Sending messages

+

To encrypt a message, just call Session::encrypt(msg_content). This will +either produce an OlmMessage::PreKey(..) or OlmMessage::Normal(..) +depending on whether the session is fully established. A session is fully +established once you receive (and decrypt) at least one message from the +other side.

+

Structs§

Enums§

\ No newline at end of file diff --git a/vodozemac/olm/messages/enum.MessageType.html b/vodozemac/olm/messages/enum.MessageType.html new file mode 100644 index 00000000..bd5c8472 --- /dev/null +++ b/vodozemac/olm/messages/enum.MessageType.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../vodozemac/olm/enum.MessageType.html...

+ + + \ No newline at end of file diff --git a/vodozemac/olm/messages/enum.OlmMessage.html b/vodozemac/olm/messages/enum.OlmMessage.html new file mode 100644 index 00000000..9aba00a2 --- /dev/null +++ b/vodozemac/olm/messages/enum.OlmMessage.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../vodozemac/olm/enum.OlmMessage.html...

+ + + \ No newline at end of file diff --git a/vodozemac/olm/messages/message/struct.Message.html b/vodozemac/olm/messages/message/struct.Message.html new file mode 100644 index 00000000..7c65a4b9 --- /dev/null +++ b/vodozemac/olm/messages/message/struct.Message.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../../vodozemac/olm/struct.Message.html...

+ + + \ No newline at end of file diff --git a/vodozemac/olm/messages/pre_key/struct.PreKeyMessage.html b/vodozemac/olm/messages/pre_key/struct.PreKeyMessage.html new file mode 100644 index 00000000..882fd9b5 --- /dev/null +++ b/vodozemac/olm/messages/pre_key/struct.PreKeyMessage.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../../vodozemac/olm/struct.PreKeyMessage.html...

+ + + \ No newline at end of file diff --git a/vodozemac/olm/session/enum.DecryptionError.html b/vodozemac/olm/session/enum.DecryptionError.html new file mode 100644 index 00000000..64c3f723 --- /dev/null +++ b/vodozemac/olm/session/enum.DecryptionError.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../vodozemac/olm/enum.DecryptionError.html...

+ + + \ No newline at end of file diff --git a/vodozemac/olm/session/ratchet/struct.RatchetPublicKey.html b/vodozemac/olm/session/ratchet/struct.RatchetPublicKey.html new file mode 100644 index 00000000..0bf8b973 --- /dev/null +++ b/vodozemac/olm/session/ratchet/struct.RatchetPublicKey.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../../vodozemac/olm/struct.RatchetPublicKey.html...

+ + + \ No newline at end of file diff --git a/vodozemac/olm/session/struct.Session.html b/vodozemac/olm/session/struct.Session.html new file mode 100644 index 00000000..44238f12 --- /dev/null +++ b/vodozemac/olm/session/struct.Session.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../vodozemac/olm/struct.Session.html...

+ + + \ No newline at end of file diff --git a/vodozemac/olm/session/struct.SessionPickle.html b/vodozemac/olm/session/struct.SessionPickle.html new file mode 100644 index 00000000..5c0db13c --- /dev/null +++ b/vodozemac/olm/session/struct.SessionPickle.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../vodozemac/olm/struct.SessionPickle.html...

+ + + \ No newline at end of file diff --git a/vodozemac/olm/session_config/struct.SessionConfig.html b/vodozemac/olm/session_config/struct.SessionConfig.html new file mode 100644 index 00000000..d8251a70 --- /dev/null +++ b/vodozemac/olm/session_config/struct.SessionConfig.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../vodozemac/olm/struct.SessionConfig.html...

+ + + \ No newline at end of file diff --git a/vodozemac/olm/session_keys/struct.SessionKeys.html b/vodozemac/olm/session_keys/struct.SessionKeys.html new file mode 100644 index 00000000..13a001a3 --- /dev/null +++ b/vodozemac/olm/session_keys/struct.SessionKeys.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../vodozemac/olm/struct.SessionKeys.html...

+ + + \ No newline at end of file diff --git a/vodozemac/olm/sidebar-items.js b/vodozemac/olm/sidebar-items.js new file mode 100644 index 00000000..0b77a43a --- /dev/null +++ b/vodozemac/olm/sidebar-items.js @@ -0,0 +1 @@ +window.SIDEBAR_ITEMS = {"enum":["DecryptionError","MessageType","OlmMessage","SessionCreationError"],"struct":["Account","AccountPickle","IdentityKeys","InboundCreationResult","Message","OneTimeKeyGenerationResult","PreKeyMessage","RatchetPublicKey","Session","SessionConfig","SessionKeys","SessionPickle"]}; \ No newline at end of file diff --git a/vodozemac/olm/struct.Account.html b/vodozemac/olm/struct.Account.html new file mode 100644 index 00000000..e93b8410 --- /dev/null +++ b/vodozemac/olm/struct.Account.html @@ -0,0 +1,99 @@ +Account in vodozemac::olm - Rust +

Struct vodozemac::olm::Account

source ·
pub struct Account { /* private fields */ }
Expand description

An Olm account manages all cryptographic keys used on a device.

+

Implementations§

source§

impl Account

source

pub fn new() -> Self

Create a new Account with new random identity keys.

+
source

pub fn identity_keys(&self) -> IdentityKeys

Get the IdentityKeys of this Account

+
source

pub fn ed25519_key(&self) -> Ed25519PublicKey

Get a reference to the account’s public Ed25519 key

+
source

pub fn curve25519_key(&self) -> Curve25519PublicKey

Get a reference to the account’s public Curve25519 key

+
source

pub fn sign(&self, message: &str) -> Ed25519Signature

Sign the given message using our Ed25519 fingerprint key.

+
source

pub fn max_number_of_one_time_keys(&self) -> usize

Get the maximum number of one-time keys the client should keep on the +server.

+

Note: this differs from the libolm method of the same name, the +libolm method returned the maximum amount of one-time keys the Account +could hold and only half of those should be uploaded.

+
source

pub fn create_outbound_session( + &self, + session_config: SessionConfig, + identity_key: Curve25519PublicKey, + one_time_key: Curve25519PublicKey +) -> Session

Create a Session with the given identity key and one-time key.

+
source

pub fn create_inbound_session( + &mut self, + their_identity_key: Curve25519PublicKey, + pre_key_message: &PreKeyMessage +) -> Result<InboundCreationResult, SessionCreationError>

Create a Session from the given pre-key message and identity key

+
source

pub fn generate_one_time_keys( + &mut self, + count: usize +) -> OneTimeKeyGenerationResult

Generates the supplied number of one time keys. +Returns the public parts of the one-time keys that were created and +discarded.

+

Our one-time key store inside the Account has a limited amount of +places for one-time keys, If we try to generate new ones while the store +is completely populated, the oldest one-time keys will get discarded +to make place for new ones.

+
source

pub fn stored_one_time_key_count(&self) -> usize

source

pub fn one_time_keys(&self) -> HashMap<KeyId, Curve25519PublicKey>

Get the currently unpublished one-time keys.

+

The one-time keys should be published to a server and marked as +published using the mark_keys_as_published() method.

+
source

pub fn generate_fallback_key(&mut self) -> Option<Curve25519PublicKey>

Generate a single new fallback key.

+

The fallback key will be used by other users to establish a Session if +all the one-time keys on the server have been used up.

+

Returns the public Curve25519 key of the previous fallback key, that +is, the one that will get removed from the Account when this method +is called. This return value is mostly useful for logging purposes.

+
source

pub fn fallback_key(&self) -> HashMap<KeyId, Curve25519PublicKey>

Get the currently unpublished fallback key.

+

The fallback key should be published just like the one-time keys, after +it has been successfully published it needs to be marked as published +using the mark_keys_as_published() method as well.

+
source

pub fn forget_fallback_key(&mut self) -> bool

The Account stores at most two private parts of the fallback key. This +method lets us forget the previously used fallback key.

+
source

pub fn mark_keys_as_published(&mut self)

Mark all currently unpublished one-time and fallback keys as published.

+
source

pub fn pickle(&self) -> AccountPickle

Convert the account into a struct which implements serde::Serialize +and serde::Deserialize.

+
source

pub fn from_pickle(pickle: AccountPickle) -> Self

Restore an Account from a previously saved AccountPickle.

+
source

pub fn from_libolm_pickle( + pickle: &str, + pickle_key: &[u8] +) -> Result<Self, LibolmPickleError>

Available on crate feature libolm-compat only.

Create an Account object by unpickling an account pickle in libolm +legacy pickle format.

+

Such pickles are encrypted and need to first be decrypted using +pickle_key.

+
source

pub fn to_libolm_pickle( + &self, + pickle_key: &[u8] +) -> Result<String, LibolmPickleError>

Available on crate feature libolm-compat only.

Pickle an Account into a libolm pickle format.

+

This pickle can be restored using the [Account::from_libolm_pickle] +method, or can be used in the libolm C library.

+

The pickle will be encrypted using the pickle key.

+

Note: This method might be lossy, the vodozemac Account has the +ability to hold more one-time keys compared to the libolm +variant.

+

⚠️ Security Warning: The pickle key will get expanded into both an +AES key and an IV in a deterministic manner. If the same pickle key +is reused, this will lead to IV reuse. To prevent this, users have +to ensure that they always use a globally (probabilistically) unique +pickle key.

+
§Examples
+
use vodozemac::olm::Account;
+use olm_rs::{account::OlmAccount, PicklingMode};
+let account = Account::new();
+
+let export = account
+    .to_libolm_pickle(&[0u8; 32])
+    .expect("We should be able to pickle a freshly created Account");
+
+let unpickled = OlmAccount::unpickle(
+    export,
+    PicklingMode::Encrypted { key: [0u8; 32].to_vec() },
+).expect("We should be able to unpickle our exported Account");
+

Trait Implementations§

source§

impl Default for Account

source§

fn default() -> Self

Returns the “default value” for a type. Read more
source§

impl From<AccountPickle> for Account

source§

fn from(pickle: AccountPickle) -> Self

Converts to this type from the input type.

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

\ No newline at end of file diff --git a/vodozemac/olm/struct.AccountPickle.html b/vodozemac/olm/struct.AccountPickle.html new file mode 100644 index 00000000..b877c890 --- /dev/null +++ b/vodozemac/olm/struct.AccountPickle.html @@ -0,0 +1,28 @@ +AccountPickle in vodozemac::olm - Rust +
pub struct AccountPickle { /* private fields */ }
Expand description

A format suitable for serialization which implements serde::Serialize +and serde::Deserialize. Obtainable by calling Account::pickle.

+

Implementations§

source§

impl AccountPickle

A format suitable for serialization which implements serde::Serialize +and serde::Deserialize. Obtainable by calling Account::pickle.

+
source

pub fn encrypt(self, pickle_key: &[u8; 32]) -> String

Serialize and encrypt the pickle using the given key.

+

This is the inverse of AccountPickle::from_encrypted.

+
source

pub fn from_encrypted( + ciphertext: &str, + pickle_key: &[u8; 32] +) -> Result<Self, PickleError>

Obtain a pickle from a ciphertext by decrypting and deserializing using +the given key.

+

This is the inverse of AccountPickle::encrypt.

+

Trait Implementations§

source§

impl<'de> Deserialize<'de> for AccountPickle

source§

fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>
where + __D: Deserializer<'de>,

Deserialize this value from the given Serde deserializer. Read more
source§

impl From<AccountPickle> for Account

source§

fn from(pickle: AccountPickle) -> Self

Converts to this type from the input type.
source§

impl Serialize for AccountPickle

source§

fn serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>
where + __S: Serializer,

Serialize this value into the given Serde serializer. Read more

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

source§

impl<T> DeserializeOwned for T
where + T: for<'de> Deserialize<'de>,

\ No newline at end of file diff --git a/vodozemac/olm/struct.IdentityKeys.html b/vodozemac/olm/struct.IdentityKeys.html new file mode 100644 index 00000000..d4154b66 --- /dev/null +++ b/vodozemac/olm/struct.IdentityKeys.html @@ -0,0 +1,25 @@ +IdentityKeys in vodozemac::olm - Rust +

Struct vodozemac::olm::IdentityKeys

source ·
pub struct IdentityKeys {
+    pub ed25519: Ed25519PublicKey,
+    pub curve25519: Curve25519PublicKey,
+}
Expand description

Struct holding the two public identity keys of an Account.

+

Fields§

§ed25519: Ed25519PublicKey

The ed25519 key, used for signing.

+
§curve25519: Curve25519PublicKey

The curve25519 key, used for to establish shared secrets.

+

Trait Implementations§

source§

impl Clone for IdentityKeys

source§

fn clone(&self) -> IdentityKeys

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for IdentityKeys

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl<'de> Deserialize<'de> for IdentityKeys

source§

fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>
where + __D: Deserializer<'de>,

Deserialize this value from the given Serde deserializer. Read more
source§

impl PartialEq for IdentityKeys

source§

fn eq(&self, other: &IdentityKeys) -> bool

This method tests for self and other values to be equal, and is used +by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

This method tests for !=. The default implementation is almost always +sufficient, and should not be overridden without very good reason.
source§

impl Serialize for IdentityKeys

source§

fn serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>
where + __S: Serializer,

Serialize this value into the given Serde serializer. Read more
source§

impl Copy for IdentityKeys

source§

impl Eq for IdentityKeys

source§

impl StructuralPartialEq for IdentityKeys

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

source§

impl<T> DeserializeOwned for T
where + T: for<'de> Deserialize<'de>,

\ No newline at end of file diff --git a/vodozemac/olm/struct.InboundCreationResult.html b/vodozemac/olm/struct.InboundCreationResult.html new file mode 100644 index 00000000..d50884ae --- /dev/null +++ b/vodozemac/olm/struct.InboundCreationResult.html @@ -0,0 +1,19 @@ +InboundCreationResult in vodozemac::olm - Rust +
pub struct InboundCreationResult {
+    pub session: Session,
+    pub plaintext: Vec<u8>,
+}
Expand description

Return type for the creation of inbound Session objects.

+

Fields§

§session: Session

The Session that was created from a pre-key message.

+
§plaintext: Vec<u8>

The plaintext of the pre-key message.

+

Trait Implementations§

source§

impl Debug for InboundCreationResult

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

\ No newline at end of file diff --git a/vodozemac/olm/struct.Message.html b/vodozemac/olm/struct.Message.html new file mode 100644 index 00000000..8f11ba4e --- /dev/null +++ b/vodozemac/olm/struct.Message.html @@ -0,0 +1,50 @@ +Message in vodozemac::olm - Rust +

Struct vodozemac::olm::Message

source ·
pub struct Message { /* private fields */ }
Expand description

An encrypted Olm message.

+

Contains metadata that is required to find the correct ratchet state of a +Session necessary to decrypt the message.

+

Implementations§

source§

impl Message

source

pub fn ratchet_key(&self) -> Curve25519PublicKey

The public part of the ratchet key, that was used when the message was +encrypted.

+
source

pub fn chain_index(&self) -> u64

The index of the chain that was used when the message was encrypted.

+
source

pub fn ciphertext(&self) -> &[u8]

The actual ciphertext of the message.

+
source

pub fn version(&self) -> u8

The version of the Olm message.

+
source

pub fn mac_truncated(&self) -> bool

Has the MAC been truncated in this Olm message.

+
source

pub fn from_bytes(bytes: &[u8]) -> Result<Self, DecodeError>

Try to decode the given byte slice as a Olm Message.

+

The expected format of the byte array is described in the +Message::to_bytes() method.

+
source

pub fn to_bytes(&self) -> Vec<u8>

Encode the Message as an array of bytes.

+

Olm Messages consist of a one-byte version, followed by a variable +length payload and a fixed length message authentication code.

+
+--------------+------------------------------------+-----------+
+| Version Byte | Payload Bytes                      | MAC Bytes |
++--------------+------------------------------------+-----------+
+
+

The payload uses a format based on the Protocol Buffers encoding. It +consists of the following key-value pairs:

+
+ + + +
NameTagTypeMeaning
Ratchet-Key0x0AStringThe public part of the ratchet key
Chain-Index0x10IntegerThe chain index, of the message
Cipher-Text0x22StringThe cipher-text of the message
+
source

pub fn from_base64(message: &str) -> Result<Self, DecodeError>

Try to decode the given string as a Olm Message.

+

The string needs to be a base64 encoded byte array that follows the +format described in the Message::to_bytes() method.

+
source

pub fn to_base64(&self) -> String

Encode the Message as a string.

+

This method first calls Message::to_bytes() and then encodes the +resulting byte array as a string using base64 encoding.

+

Trait Implementations§

source§

impl Clone for Message

source§

fn clone(&self) -> Message

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for Message

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl<'de> Deserialize<'de> for Message

source§

fn deserialize<D: Deserializer<'de>>(d: D) -> Result<Self, D::Error>

Deserialize this value from the given Serde deserializer. Read more
source§

impl From<Message> for OlmMessage

source§

fn from(m: Message) -> Self

Converts to this type from the input type.
source§

impl PartialEq for Message

source§

fn eq(&self, other: &Message) -> bool

This method tests for self and other values to be equal, and is used +by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

This method tests for !=. The default implementation is almost always +sufficient, and should not be overridden without very good reason.
source§

impl Serialize for Message

source§

fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where + S: Serializer,

Serialize this value into the given Serde serializer. Read more
source§

impl TryFrom<&[u8]> for Message

§

type Error = DecodeError

The type returned in the event of a conversion error.
source§

fn try_from(value: &[u8]) -> Result<Self, Self::Error>

Performs the conversion.
source§

impl TryFrom<&str> for Message

§

type Error = DecodeError

The type returned in the event of a conversion error.
source§

fn try_from(value: &str) -> Result<Self, Self::Error>

Performs the conversion.
source§

impl TryFrom<Vec<u8>> for Message

§

type Error = DecodeError

The type returned in the event of a conversion error.
source§

fn try_from(value: Vec<u8>) -> Result<Self, Self::Error>

Performs the conversion.
source§

impl Eq for Message

source§

impl StructuralPartialEq for Message

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

source§

impl<T> DeserializeOwned for T
where + T: for<'de> Deserialize<'de>,

\ No newline at end of file diff --git a/vodozemac/olm/struct.OneTimeKeyGenerationResult.html b/vodozemac/olm/struct.OneTimeKeyGenerationResult.html new file mode 100644 index 00000000..45df0bcd --- /dev/null +++ b/vodozemac/olm/struct.OneTimeKeyGenerationResult.html @@ -0,0 +1,20 @@ +OneTimeKeyGenerationResult in vodozemac::olm - Rust +
pub struct OneTimeKeyGenerationResult {
+    pub created: Vec<Curve25519PublicKey>,
+    pub removed: Vec<Curve25519PublicKey>,
+}
Expand description

The result type for the one-time key generation operation.

+

Fields§

§created: Vec<Curve25519PublicKey>

The public part of the one-time keys that were newly generated.

+
§removed: Vec<Curve25519PublicKey>

The public part of the one-time keys that had to be removed to make +space for the new ones.

+

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

\ No newline at end of file diff --git a/vodozemac/olm/struct.PreKeyMessage.html b/vodozemac/olm/struct.PreKeyMessage.html new file mode 100644 index 00000000..03ab86e4 --- /dev/null +++ b/vodozemac/olm/struct.PreKeyMessage.html @@ -0,0 +1,61 @@ +PreKeyMessage in vodozemac::olm - Rust +
pub struct PreKeyMessage { /* private fields */ }
Expand description

An encrypted Olm pre-key message.

+

Contains metadata that is required to establish a Session and a normal +Olm Message.

+

Implementations§

source§

impl PreKeyMessage

source

pub fn one_time_key(&self) -> Curve25519PublicKey

The single-use key that was uploaded to a public key directory by the +receiver of the message. Should be used to establish a Session.

+
source

pub fn base_key(&self) -> Curve25519PublicKey

The base key, a single use key that was created just in time by the +sender of the message. Should be used to establish a Session.

+
source

pub fn identity_key(&self) -> Curve25519PublicKey

The long term identity key of the sender of the message. Should be used +to establish a Session

+
source

pub fn session_keys(&self) -> SessionKeys

The collection of all keys required for establishing an Olm Session +from this pre-key message.

+

Other methods on this struct (like PreKeyMessage::identity_key()) +can be used to retrieve individual keys from this collection.

+
source

pub fn session_id(&self) -> String

Returns the globally unique session ID, in base64-encoded form.

+

This is a shorthand helper of the SessionKeys::session_id() method.

+
source

pub fn message(&self) -> &Message

The actual message that contains the ciphertext.

+
source

pub fn from_bytes(message: &[u8]) -> Result<Self, DecodeError>

Try to decode the given byte slice as a Olm Message.

+

The expected format of the byte array is described in the +PreKeyMessage::to_bytes() method.

+
source

pub fn to_bytes(&self) -> Vec<u8>

Encode the PreKeyMessage as an array of bytes.

+

Olm PreKeyMessages consist of a one-byte version, followed by a +variable length payload.

+
+--------------+------------------------------------+
+| Version Byte | Payload Bytes                      |
++--------------+------------------------------------+
+
+

The payload uses a format based on the Protocol Buffers encoding. It +consists of the following key-value pairs:

+
+ + + + +
NameTagTypeMeaning
One-Time-Key0x0AStringThe public part of Bob’s single-use key
Base-Key0x12StringThe public part of Alice’s single-use key
Identity-Key0x1AStringThe public part of Alice’s identity key
Message0x22StringAn embedded Olm message
+
+

The last key/value pair in a PreKeyMessage is a normal Olm +Message.

+
source

pub fn from_base64(message: &str) -> Result<Self, DecodeError>

Try to decode the given string as a Olm PreKeyMessage.

+

The string needs to be a base64 encoded byte array that follows the +format described in the PreKeyMessage::to_bytes() method.

+
source

pub fn to_base64(&self) -> String

Encode the PreKeyMessage as a string.

+

This method first calls PreKeyMessage::to_bytes() and then encodes +the resulting byte array as a string using base64 encoding.

+

Trait Implementations§

source§

impl Clone for PreKeyMessage

source§

fn clone(&self) -> PreKeyMessage

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for PreKeyMessage

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl<'de> Deserialize<'de> for PreKeyMessage

source§

fn deserialize<D: Deserializer<'de>>(d: D) -> Result<Self, D::Error>

Deserialize this value from the given Serde deserializer. Read more
source§

impl From<PreKeyMessage> for OlmMessage

source§

fn from(m: PreKeyMessage) -> Self

Converts to this type from the input type.
source§

impl PartialEq for PreKeyMessage

source§

fn eq(&self, other: &PreKeyMessage) -> bool

This method tests for self and other values to be equal, and is used +by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

This method tests for !=. The default implementation is almost always +sufficient, and should not be overridden without very good reason.
source§

impl Serialize for PreKeyMessage

source§

fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where + S: Serializer,

Serialize this value into the given Serde serializer. Read more
source§

impl TryFrom<&[u8]> for PreKeyMessage

§

type Error = DecodeError

The type returned in the event of a conversion error.
source§

fn try_from(value: &[u8]) -> Result<Self, Self::Error>

Performs the conversion.
source§

impl TryFrom<&str> for PreKeyMessage

§

type Error = DecodeError

The type returned in the event of a conversion error.
source§

fn try_from(value: &str) -> Result<Self, Self::Error>

Performs the conversion.
source§

impl TryFrom<Vec<u8>> for PreKeyMessage

§

type Error = DecodeError

The type returned in the event of a conversion error.
source§

fn try_from(value: Vec<u8>) -> Result<Self, Self::Error>

Performs the conversion.
source§

impl Eq for PreKeyMessage

source§

impl StructuralPartialEq for PreKeyMessage

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

source§

impl<T> DeserializeOwned for T
where + T: for<'de> Deserialize<'de>,

\ No newline at end of file diff --git a/vodozemac/olm/struct.RatchetPublicKey.html b/vodozemac/olm/struct.RatchetPublicKey.html new file mode 100644 index 00000000..43e6feaa --- /dev/null +++ b/vodozemac/olm/struct.RatchetPublicKey.html @@ -0,0 +1,16 @@ +RatchetPublicKey in vodozemac::olm - Rust +
pub struct RatchetPublicKey(/* private fields */);

Trait Implementations§

source§

impl AsRef<Curve25519PublicKey> for RatchetPublicKey

source§

fn as_ref(&self) -> &Curve25519PublicKey

Converts this type into a shared reference of the (usually inferred) input type.
source§

impl Clone for RatchetPublicKey

source§

fn clone(&self) -> RatchetPublicKey

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for RatchetPublicKey

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl From<[u8; 32]> for RatchetPublicKey

source§

fn from(bytes: [u8; 32]) -> Self

Converts to this type from the input type.
source§

impl PartialEq for RatchetPublicKey

source§

fn eq(&self, other: &RatchetPublicKey) -> bool

This method tests for self and other values to be equal, and is used +by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

This method tests for !=. The default implementation is almost always +sufficient, and should not be overridden without very good reason.
source§

impl Copy for RatchetPublicKey

source§

impl Eq for RatchetPublicKey

source§

impl StructuralPartialEq for RatchetPublicKey

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

\ No newline at end of file diff --git a/vodozemac/olm/struct.Session.html b/vodozemac/olm/struct.Session.html new file mode 100644 index 00000000..515fa78c --- /dev/null +++ b/vodozemac/olm/struct.Session.html @@ -0,0 +1,63 @@ +Session in vodozemac::olm - Rust +

Struct vodozemac::olm::Session

source ·
pub struct Session { /* private fields */ }
Expand description

An Olm session represents one end of an encrypted communication channel +between two participants.

+

A session enables enables the session owner to encrypt messages intended +for, and decrypt messages sent by, the other participant of the channel.

+

Olm sessions have two important properties:

+
    +
  1. They are based on a double ratchet algorithm which continuously +introduces new entropy into the channel as messages are sent and +received. This imbues the channel with self-healing properties, +allowing it to recover from a momentary loss of confidentiality in the event +of a key compromise.
  2. +
  3. They are asynchronous, allowing the participant to start sending +messages to the other side even if the other participant is not online at +the moment.
  4. +
+

An Olm Session is acquired from an Account, by calling either

+ +

Implementations§

source§

impl Session

source

pub fn session_id(&self) -> String

Returns the globally unique session ID, in base64-encoded form.

+

This is a shorthand helper of the SessionKeys::session_id() method.

+
source

pub fn has_received_message(&self) -> bool

Have we ever received and decrypted a message from the other side?

+

Used to decide if outgoing messages should be sent as normal or pre-key +messages.

+
source

pub fn encrypt(&mut self, plaintext: impl AsRef<[u8]>) -> OlmMessage

Encrypt the plaintext and construct an OlmMessage.

+

The message will either be a pre-key message or a normal message, +depending on whether the session is fully established. A session is +fully established once you receive (and decrypt) at least one +message from the other side.

+
source

pub fn session_keys(&self) -> SessionKeys

Get the keys associated with this session.

+
source

pub fn session_config(&self) -> SessionConfig

source

pub fn decrypt( + &mut self, + message: &OlmMessage +) -> Result<Vec<u8>, DecryptionError>

Try to decrypt an Olm message, which will either return the plaintext or +result in a DecryptionError.

+
source

pub fn pickle(&self) -> SessionPickle

Convert the session into a struct which implements serde::Serialize +and serde::Deserialize.

+
source

pub fn from_pickle(pickle: SessionPickle) -> Self

Restore a Session from a previously saved SessionPickle.

+
source

pub fn from_libolm_pickle( + pickle: &str, + pickle_key: &[u8] +) -> Result<Self, LibolmPickleError>

Available on crate feature libolm-compat only.

Create a Session object by unpickling a session pickle in libolm +legacy pickle format.

+

Such pickles are encrypted and need to first be decrypted using +pickle_key.

+

Trait Implementations§

source§

impl Debug for Session

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl From<SessionPickle> for Session

source§

fn from(pickle: SessionPickle) -> Self

Converts to this type from the input type.

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

\ No newline at end of file diff --git a/vodozemac/olm/struct.SessionConfig.html b/vodozemac/olm/struct.SessionConfig.html new file mode 100644 index 00000000..52c7ad0c --- /dev/null +++ b/vodozemac/olm/struct.SessionConfig.html @@ -0,0 +1,28 @@ +SessionConfig in vodozemac::olm - Rust +
pub struct SessionConfig { /* private fields */ }
Expand description

A struct to configure how Olm sessions should work under the hood. +Currently only the MAC truncation behaviour can be configured.

+

Implementations§

source§

impl SessionConfig

source

pub fn version(&self) -> u8

Get the numeric version of this SessionConfig.

+
source

pub fn version_1() -> Self

Create a SessionConfig for the Olm version 1. This version of Olm will +use AES-256 and HMAC with a truncated MAC to encrypt individual +messages. The MAC will be truncated to 8 bytes.

+
source

pub fn version_2() -> Self

Create a SessionConfig for the Olm version 2. This version of Olm will +use AES-256 and HMAC to encrypt individual messages. The MAC won’t be +truncated.

+

Trait Implementations§

source§

impl Clone for SessionConfig

source§

fn clone(&self) -> SessionConfig

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for SessionConfig

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl Default for SessionConfig

source§

fn default() -> Self

Returns the “default value” for a type. Read more
source§

impl<'de> Deserialize<'de> for SessionConfig

source§

fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>
where + __D: Deserializer<'de>,

Deserialize this value from the given Serde deserializer. Read more
source§

impl PartialEq for SessionConfig

source§

fn eq(&self, other: &SessionConfig) -> bool

This method tests for self and other values to be equal, and is used +by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

This method tests for !=. The default implementation is almost always +sufficient, and should not be overridden without very good reason.
source§

impl Serialize for SessionConfig

source§

fn serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>
where + __S: Serializer,

Serialize this value into the given Serde serializer. Read more
source§

impl Copy for SessionConfig

source§

impl Eq for SessionConfig

source§

impl StructuralPartialEq for SessionConfig

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

source§

impl<T> DeserializeOwned for T
where + T: for<'de> Deserialize<'de>,

\ No newline at end of file diff --git a/vodozemac/olm/struct.SessionKeys.html b/vodozemac/olm/struct.SessionKeys.html new file mode 100644 index 00000000..1285eff5 --- /dev/null +++ b/vodozemac/olm/struct.SessionKeys.html @@ -0,0 +1,32 @@ +SessionKeys in vodozemac::olm - Rust +

Struct vodozemac::olm::SessionKeys

source ·
pub struct SessionKeys {
+    pub identity_key: Curve25519PublicKey,
+    pub base_key: Curve25519PublicKey,
+    pub one_time_key: Curve25519PublicKey,
+}
Expand description

The set of keys that were used to establish the Olm Session,

+

Fields§

§identity_key: Curve25519PublicKey§base_key: Curve25519PublicKey§one_time_key: Curve25519PublicKey

Implementations§

source§

impl SessionKeys

source

pub fn session_id(&self) -> String

Returns the globally unique session ID which these SessionKeys will +produce.

+

A session ID is the SHA256 of the concatenation of three SessionKeys, +the account’s identity key, the ephemeral base key and the one-time +key which is used to establish the session.

+

Due to the construction, every session ID is (probabilistically) +globally unique.

+

Trait Implementations§

source§

impl Clone for SessionKeys

source§

fn clone(&self) -> SessionKeys

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for SessionKeys

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl Decode for SessionKeys

source§

fn decode(reader: &mut impl Read) -> Result<Self, DecodeError>

Try to read and decode a value from the given reader.
source§

fn decode_from_slice(buffer: &[u8]) -> Result<Self, DecodeError>
where + Self: Sized,

Try to read and decode a value from the given byte slice.
source§

impl<'de> Deserialize<'de> for SessionKeys

source§

fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>
where + __D: Deserializer<'de>,

Deserialize this value from the given Serde deserializer. Read more
source§

impl PartialEq for SessionKeys

source§

fn eq(&self, other: &SessionKeys) -> bool

This method tests for self and other values to be equal, and is used +by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

This method tests for !=. The default implementation is almost always +sufficient, and should not be overridden without very good reason.
source§

impl Serialize for SessionKeys

source§

fn serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>
where + __S: Serializer,

Serialize this value into the given Serde serializer. Read more
source§

impl Copy for SessionKeys

source§

impl Eq for SessionKeys

source§

impl StructuralPartialEq for SessionKeys

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

source§

impl<T> DeserializeOwned for T
where + T: for<'de> Deserialize<'de>,

\ No newline at end of file diff --git a/vodozemac/olm/struct.SessionPickle.html b/vodozemac/olm/struct.SessionPickle.html new file mode 100644 index 00000000..38074ea6 --- /dev/null +++ b/vodozemac/olm/struct.SessionPickle.html @@ -0,0 +1,26 @@ +SessionPickle in vodozemac::olm - Rust +
pub struct SessionPickle { /* private fields */ }
Expand description

A format suitable for serialization which implements serde::Serialize +and serde::Deserialize. Obtainable by calling Session::pickle.

+

Implementations§

source§

impl SessionPickle

source

pub fn encrypt(self, pickle_key: &[u8; 32]) -> String

Serialize and encrypt the pickle using the given key.

+

This is the inverse of SessionPickle::from_encrypted.

+
source

pub fn from_encrypted( + ciphertext: &str, + pickle_key: &[u8; 32] +) -> Result<Self, PickleError>

Obtain a pickle from a ciphertext by decrypting and deserializing using +the given key.

+

This is the inverse of SessionPickle::encrypt.

+

Trait Implementations§

source§

impl<'de> Deserialize<'de> for SessionPickle

source§

fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>
where + __D: Deserializer<'de>,

Deserialize this value from the given Serde deserializer. Read more
source§

impl From<SessionPickle> for Session

source§

fn from(pickle: SessionPickle) -> Self

Converts to this type from the input type.
source§

impl Serialize for SessionPickle

source§

fn serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>
where + __S: Serializer,

Serialize this value into the given Serde serializer. Read more

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

source§

impl<T> DeserializeOwned for T
where + T: for<'de> Deserialize<'de>,

\ No newline at end of file diff --git a/vodozemac/sas/enum.SasError.html b/vodozemac/sas/enum.SasError.html new file mode 100644 index 00000000..4849c5a4 --- /dev/null +++ b/vodozemac/sas/enum.SasError.html @@ -0,0 +1,18 @@ +SasError in vodozemac::sas - Rust +

Enum vodozemac::sas::SasError

source ·
pub enum SasError {
+    Mac(MacError),
+}
Expand description

Error type describing failures that can happen during the key verification.

+

Variants§

§

Mac(MacError)

The MAC failed to be validated.

+

Trait Implementations§

source§

impl Debug for SasError

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl Display for SasError

source§

fn fmt(&self, __formatter: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl Error for SasError

source§

fn source(&self) -> Option<&(dyn Error + 'static)>

The lower-level source of this error, if any. Read more
1.0.0 · source§

fn description(&self) -> &str

👎Deprecated since 1.42.0: use the Display impl or to_string()
1.0.0 · source§

fn cause(&self) -> Option<&dyn Error>

👎Deprecated since 1.33.0: replaced by Error::source, which can support downcasting
source§

fn provide<'a>(&'a self, request: &mut Request<'a>)

🔬This is a nightly-only experimental API. (error_generic_member_access)
Provides type based access to context intended for error reports. Read more
source§

impl From<MacError> for SasError

source§

fn from(source: MacError) -> Self

Converts to this type from the input type.

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToString for T
where + T: Display + ?Sized,

source§

default fn to_string(&self) -> String

Converts the given value to a String. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

\ No newline at end of file diff --git a/vodozemac/sas/index.html b/vodozemac/sas/index.html new file mode 100644 index 00000000..58214152 --- /dev/null +++ b/vodozemac/sas/index.html @@ -0,0 +1,29 @@ +vodozemac::sas - Rust +

Module vodozemac::sas

source ·
Expand description

User-friendly key verification using short authentication strings (SAS).

+

The verification process is heavily inspired by Phil Zimmermann’s ZRTP +key agreement handshake. A core part of key agreement in ZRTP is the +hash commitment: the party that begins the key sharing process sends +a hash of their part of the Diffie-Hellman exchange but does not send the +part itself exchange until they had received the other party’s part.

+

The verification process can be used to verify the Ed25519 identity key of +an Account.

+

§Examples

+
use vodozemac::sas::Sas;
+let alice = Sas::new();
+let bob = Sas::new();
+
+let bob_public_key = bob.public_key();
+
+let bob = bob.diffie_hellman(alice.public_key())?;
+let alice = alice.diffie_hellman(bob_public_key)?;
+
+let alice_bytes = alice.bytes("AGREED_INFO");
+let bob_bytes = bob.bytes("AGREED_INFO");
+
+let alice_emojis = alice_bytes.emoji_indices();
+let bob_emojis = bob_bytes.emoji_indices();
+
+assert_eq!(alice_emojis, bob_emojis);
+

Structs§

  • A struct representing a short auth string verification object where the +shared secret has been established.
  • Error type for the case when we try to generate too many SAS bytes.
  • The output type for the SAS MAC calculation.
  • A struct representing a short auth string verification object.
  • Bytes generated from an shared secret that can be used as the short auth +string.

Enums§

  • Error type describing failures that can happen during the key verification.
\ No newline at end of file diff --git a/vodozemac/sas/sidebar-items.js b/vodozemac/sas/sidebar-items.js new file mode 100644 index 00000000..822f677a --- /dev/null +++ b/vodozemac/sas/sidebar-items.js @@ -0,0 +1 @@ +window.SIDEBAR_ITEMS = {"enum":["SasError"],"struct":["EstablishedSas","InvalidCount","Mac","Sas","SasBytes"]}; \ No newline at end of file diff --git a/vodozemac/sas/struct.EstablishedSas.html b/vodozemac/sas/struct.EstablishedSas.html new file mode 100644 index 00000000..427829c1 --- /dev/null +++ b/vodozemac/sas/struct.EstablishedSas.html @@ -0,0 +1,56 @@ +EstablishedSas in vodozemac::sas - Rust +
pub struct EstablishedSas { /* private fields */ }
Expand description

A struct representing a short auth string verification object where the +shared secret has been established.

+

This object can be used to generate the short auth string and calculate and +verify a MAC that protects information about the keys being verified.

+

Implementations§

source§

impl EstablishedSas

source

pub fn bytes(&self, info: &str) -> SasBytes

Generate SasBytes using HKDF with the shared secret as the input key +material.

+

The info string should be agreed upon beforehand, both parties need to +use the same info string.

+
source

pub fn bytes_raw( + &self, + info: &str, + count: usize +) -> Result<Vec<u8>, InvalidCount>

Generate the given number of bytes using HKDF with the shared secret +as the input key material.

+

The info string should be agreed upon beforehand, both parties need to +use the same info string.

+

The number of bytes we can generate is limited, we can generate up to +32 * 255 bytes. The function will not fail if the given count is smaller +than the limit.

+
source

pub fn calculate_mac(&self, input: &str, info: &str) -> Mac

Calculate a MAC for the given input using the info string as additional +data.

+

This should be used to calculate a MAC of the ed25519 identity key of an +Account

+

The MAC is returned as a base64 encoded string.

+
source

pub fn calculate_mac_invalid_base64(&self, input: &str, info: &str) -> String

Available on crate feature libolm-compat only.

Calculate a MAC for the given input using the info string as additional +data, the MAC is returned as an invalid base64 encoded string.

+

Warning: This method should never be used unless you require libolm +compatibility. Libolm used to incorrectly encode their MAC because the +input buffer was reused as the output buffer. This method replicates the +buggy behaviour.

+
source

pub fn verify_mac( + &self, + input: &str, + info: &str, + tag: &Mac +) -> Result<(), SasError>

Verify a MAC that was previously created using the +EstablishedSas::calculate_mac() method.

+

Users should calculate a MAC and send it to the other side, they should +then verify each other’s MAC using this method.

+
source

pub fn our_public_key(&self) -> Curve25519PublicKey

Get the public key that was created by us, that was used to establish +the shared secret.

+
source

pub fn their_public_key(&self) -> Curve25519PublicKey

Get the public key that was created by the other party, that was used to +establish the shared secret.

+

Trait Implementations§

source§

impl Debug for EstablishedSas

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

\ No newline at end of file diff --git a/vodozemac/sas/struct.InvalidCount.html b/vodozemac/sas/struct.InvalidCount.html new file mode 100644 index 00000000..beb1cb2b --- /dev/null +++ b/vodozemac/sas/struct.InvalidCount.html @@ -0,0 +1,16 @@ +InvalidCount in vodozemac::sas - Rust +

Struct vodozemac::sas::InvalidCount

source ·
pub struct InvalidCount;
Expand description

Error type for the case when we try to generate too many SAS bytes.

+

Trait Implementations§

source§

impl Clone for InvalidCount

source§

fn clone(&self) -> InvalidCount

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for InvalidCount

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl Display for InvalidCount

source§

fn fmt(&self, __formatter: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl Error for InvalidCount

1.30.0 · source§

fn source(&self) -> Option<&(dyn Error + 'static)>

The lower-level source of this error, if any. Read more
1.0.0 · source§

fn description(&self) -> &str

👎Deprecated since 1.42.0: use the Display impl or to_string()
1.0.0 · source§

fn cause(&self) -> Option<&dyn Error>

👎Deprecated since 1.33.0: replaced by Error::source, which can support downcasting
source§

fn provide<'a>(&'a self, request: &mut Request<'a>)

🔬This is a nightly-only experimental API. (error_generic_member_access)
Provides type based access to context intended for error reports. Read more

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T> ToString for T
where + T: Display + ?Sized,

source§

default fn to_string(&self) -> String

Converts the given value to a String. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

\ No newline at end of file diff --git a/vodozemac/sas/struct.Mac.html b/vodozemac/sas/struct.Mac.html new file mode 100644 index 00000000..6d1c07d8 --- /dev/null +++ b/vodozemac/sas/struct.Mac.html @@ -0,0 +1,18 @@ +Mac in vodozemac::sas - Rust +

Struct vodozemac::sas::Mac

source ·
pub struct Mac(/* private fields */);
Expand description

The output type for the SAS MAC calculation.

+

Implementations§

source§

impl Mac

source

pub fn to_base64(&self) -> String

Convert the MAC to a base64 encoded string.

+
source

pub fn as_bytes(&self) -> &[u8]

Get the byte slice of the MAC.

+
source

pub fn from_slice(bytes: &[u8]) -> Self

Create a new Mac object from a byte slice.

+
source

pub fn from_base64(mac: &str) -> Result<Self, DecodeError>

Create a new Mac object from a base64 encoded string.

+

Auto Trait Implementations§

§

impl RefUnwindSafe for Mac

§

impl Send for Mac

§

impl Sync for Mac

§

impl Unpin for Mac

§

impl UnwindSafe for Mac

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

\ No newline at end of file diff --git a/vodozemac/sas/struct.Sas.html b/vodozemac/sas/struct.Sas.html new file mode 100644 index 00000000..b3628cea --- /dev/null +++ b/vodozemac/sas/struct.Sas.html @@ -0,0 +1,34 @@ +Sas in vodozemac::sas - Rust +

Struct vodozemac::sas::Sas

source ·
pub struct Sas { /* private fields */ }
Expand description

A struct representing a short auth string verification object.

+

This object can be used to establish a shared secret to perform the short +auth string based key verification.

+

Implementations§

source§

impl Sas

source

pub fn new() -> Self

Create a new random verification object

+

This creates an ephemeral curve25519 keypair that can be used to +establish a shared secret.

+
source

pub fn public_key(&self) -> Curve25519PublicKey

Get the public key that can be used to establish a shared secret.

+
source

pub fn diffie_hellman( + self, + their_public_key: Curve25519PublicKey +) -> Result<EstablishedSas, KeyError>

Establishes a SAS secret by performing a DH handshake with another +public key.

+

Returns an EstablishedSas object which can be used to generate +SasBytes if the given public key was valid, otherwise None.

+
source

pub fn diffie_hellman_with_raw( + self, + other_public_key: &str +) -> Result<EstablishedSas, KeyError>

Establishes a SAS secret by performing a DH handshake with another +public key in “raw”, base64-encoded form.

+

Returns an EstablishedSas object which can be used to generate +SasBytes if the received public key is valid, otherwise None.

+

Trait Implementations§

source§

impl Default for Sas

source§

fn default() -> Self

Returns the “default value” for a type. Read more

Auto Trait Implementations§

§

impl RefUnwindSafe for Sas

§

impl Send for Sas

§

impl Sync for Sas

§

impl Unpin for Sas

§

impl UnwindSafe for Sas

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

\ No newline at end of file diff --git a/vodozemac/sas/struct.SasBytes.html b/vodozemac/sas/struct.SasBytes.html new file mode 100644 index 00000000..f6e32f82 --- /dev/null +++ b/vodozemac/sas/struct.SasBytes.html @@ -0,0 +1,25 @@ +SasBytes in vodozemac::sas - Rust +

Struct vodozemac::sas::SasBytes

source ·
pub struct SasBytes { /* private fields */ }
Expand description

Bytes generated from an shared secret that can be used as the short auth +string.

+

Implementations§

source§

impl SasBytes

source

pub fn emoji_indices(&self) -> [u8; 7]

Get the index of 7 emojis that can be presented to users to perform the +key verification

+

The table that maps the index to an emoji can be found in the spec.

+
source

pub fn decimals(&self) -> (u16, u16, u16)

Get the three decimal numbers that can be presented to users to perform +the key verification, as described in the spec

+
source

pub fn as_bytes(&self) -> &[u8; 6]

Get the raw bytes of the short auth string that can be converted to an +emoji, or decimal representation.

+

Trait Implementations§

source§

impl Clone for SasBytes

source§

fn clone(&self) -> SasBytes

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for SasBytes

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl PartialEq for SasBytes

source§

fn eq(&self, other: &SasBytes) -> bool

This method tests for self and other values to be equal, and is used +by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

This method tests for !=. The default implementation is almost always +sufficient, and should not be overridden without very good reason.
source§

impl Eq for SasBytes

source§

impl StructuralPartialEq for SasBytes

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

\ No newline at end of file diff --git a/vodozemac/sidebar-items.js b/vodozemac/sidebar-items.js new file mode 100644 index 00000000..a99e7c2e --- /dev/null +++ b/vodozemac/sidebar-items.js @@ -0,0 +1 @@ +window.SIDEBAR_ITEMS = {"enum":["Base64DecodeError","DecodeError","KeyError","LibolmPickleError","PickleError","SignatureError"],"fn":["base64_decode","base64_encode"],"mod":["megolm","olm","sas"],"static":["VERSION"],"struct":["Curve25519PublicKey","Curve25519SecretKey","Ed25519Keypair","Ed25519PublicKey","Ed25519SecretKey","Ed25519Signature","KeyId","ProtoBufDecodeError","SharedSecret"]}; \ No newline at end of file diff --git a/vodozemac/static.VERSION.html b/vodozemac/static.VERSION.html new file mode 100644 index 00000000..b342667c --- /dev/null +++ b/vodozemac/static.VERSION.html @@ -0,0 +1,3 @@ +VERSION in vodozemac - Rust +

Static vodozemac::VERSION

source ·
pub static VERSION: &str
Expand description

The version of vodozemac that is being used.

+
\ No newline at end of file diff --git a/vodozemac/struct.Curve25519PublicKey.html b/vodozemac/struct.Curve25519PublicKey.html new file mode 100644 index 00000000..a1133889 --- /dev/null +++ b/vodozemac/struct.Curve25519PublicKey.html @@ -0,0 +1,33 @@ +Curve25519PublicKey in vodozemac - Rust +
pub struct Curve25519PublicKey { /* private fields */ }
Expand description

Struct representing a Curve25519 public key.

+

Implementations§

source§

impl Curve25519PublicKey

source

pub const LENGTH: usize = 32usize

The number of bytes a Curve25519 public key has.

+
source

pub fn to_bytes(&self) -> [u8; 32]

Convert this public key to a byte array.

+
source

pub fn as_bytes(&self) -> &[u8; 32]

View this public key as a byte array.

+
source

pub fn to_vec(&self) -> Vec<u8>

Convert the public key to a vector of bytes.

+
source

pub fn from_bytes(bytes: [u8; 32]) -> Self

Create a Curve25519PublicKey from a byte array.

+
source

pub fn from_base64(input: &str) -> Result<Curve25519PublicKey, KeyError>

Instantiate a Curve25519 public key from an unpadded base64 +representation.

+
source

pub fn from_slice(slice: &[u8]) -> Result<Curve25519PublicKey, KeyError>

Try to create a Curve25519PublicKey from a slice of bytes.

+
source

pub fn to_base64(&self) -> String

Serialize a Curve25519 public key to an unpadded base64 representation.

+

Trait Implementations§

source§

impl AsRef<Curve25519PublicKey> for RatchetPublicKey

source§

fn as_ref(&self) -> &Curve25519PublicKey

Converts this type into a shared reference of the (usually inferred) input type.
source§

impl Clone for Curve25519PublicKey

source§

fn clone(&self) -> Curve25519PublicKey

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for Curve25519PublicKey

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl Decode for Curve25519PublicKey

source§

fn decode(reader: &mut impl Read) -> Result<Self, DecodeError>

Try to read and decode a value from the given reader.
source§

fn decode_from_slice(buffer: &[u8]) -> Result<Self, DecodeError>
where + Self: Sized,

Try to read and decode a value from the given byte slice.
source§

impl<'de> Deserialize<'de> for Curve25519PublicKey

source§

fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>
where + __D: Deserializer<'de>,

Deserialize this value from the given Serde deserializer. Read more
source§

impl Display for Curve25519PublicKey

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl<'a> From<&'a Curve25519SecretKey> for Curve25519PublicKey

source§

fn from(secret: &'a Curve25519SecretKey) -> Curve25519PublicKey

Converts to this type from the input type.
source§

impl<'a> From<&'a EphemeralSecret> for Curve25519PublicKey

source§

fn from(secret: &'a EphemeralSecret) -> Curve25519PublicKey

Converts to this type from the input type.
source§

impl<'a> From<&'a ReusableSecret> for Curve25519PublicKey

source§

fn from(secret: &'a ReusableSecret) -> Curve25519PublicKey

Converts to this type from the input type.
source§

impl From<[u8; 32]> for Curve25519PublicKey

source§

fn from(bytes: [u8; 32]) -> Curve25519PublicKey

Converts to this type from the input type.
source§

impl Hash for Curve25519PublicKey

source§

fn hash<__H: Hasher>(&self, state: &mut __H)

Feeds this value into the given Hasher. Read more
1.3.0 · source§

fn hash_slice<H>(data: &[Self], state: &mut H)
where + H: Hasher, + Self: Sized,

Feeds a slice of this type into the given Hasher. Read more
source§

impl PartialEq for Curve25519PublicKey

source§

fn eq(&self, other: &Curve25519PublicKey) -> bool

This method tests for self and other values to be equal, and is used +by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

This method tests for !=. The default implementation is almost always +sufficient, and should not be overridden without very good reason.
source§

impl Serialize for Curve25519PublicKey

source§

fn serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>
where + __S: Serializer,

Serialize this value into the given Serde serializer. Read more
source§

impl Copy for Curve25519PublicKey

source§

impl Eq for Curve25519PublicKey

source§

impl StructuralPartialEq for Curve25519PublicKey

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T> ToString for T
where + T: Display + ?Sized,

source§

default fn to_string(&self) -> String

Converts the given value to a String. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

source§

impl<T> DeserializeOwned for T
where + T: for<'de> Deserialize<'de>,

\ No newline at end of file diff --git a/vodozemac/struct.Curve25519SecretKey.html b/vodozemac/struct.Curve25519SecretKey.html new file mode 100644 index 00000000..241a28be --- /dev/null +++ b/vodozemac/struct.Curve25519SecretKey.html @@ -0,0 +1,29 @@ +Curve25519SecretKey in vodozemac - Rust +
pub struct Curve25519SecretKey(/* private fields */);
Expand description

Struct representing a Curve25519 secret key.

+

Implementations§

source§

impl Curve25519SecretKey

source

pub fn new() -> Self

Generate a new, random, Curve25519SecretKey.

+
source

pub fn from_slice(bytes: &[u8; 32]) -> Self

Create a Curve25519SecretKey from the given slice of bytes.

+
source

pub fn diffie_hellman( + &self, + their_public_key: &Curve25519PublicKey +) -> SharedSecret

Perform a Diffie-Hellman key exchange between the given +Curve25519PublicKey and this Curve25519SecretKey and return a shared +secret.

+
source

pub fn to_bytes(&self) -> Box<[u8; 32]>

Convert the Curve25519SecretKey to a byte array.

+

Note: This creates a copy of the key which won’t be zeroized, the +caller of the method needs to make sure to zeroize the returned array.

+

Trait Implementations§

source§

impl Clone for Curve25519SecretKey

source§

fn clone(&self) -> Curve25519SecretKey

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Default for Curve25519SecretKey

source§

fn default() -> Self

Returns the “default value” for a type. Read more
source§

impl<'de> Deserialize<'de> for Curve25519SecretKey

source§

fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>
where + __D: Deserializer<'de>,

Deserialize this value from the given Serde deserializer. Read more
source§

impl<'a> From<&'a Curve25519SecretKey> for Curve25519PublicKey

source§

fn from(secret: &'a Curve25519SecretKey) -> Curve25519PublicKey

Converts to this type from the input type.
source§

impl Serialize for Curve25519SecretKey

source§

fn serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>
where + __S: Serializer,

Serialize this value into the given Serde serializer. Read more

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

source§

impl<T> DeserializeOwned for T
where + T: for<'de> Deserialize<'de>,

\ No newline at end of file diff --git a/vodozemac/struct.Ed25519Keypair.html b/vodozemac/struct.Ed25519Keypair.html new file mode 100644 index 00000000..f42c422d --- /dev/null +++ b/vodozemac/struct.Ed25519Keypair.html @@ -0,0 +1,21 @@ +Ed25519Keypair in vodozemac - Rust +
pub struct Ed25519Keypair { /* private fields */ }
Expand description

A struct collecting both a public, and a secret, Ed25519 key.

+

Implementations§

source§

impl Ed25519Keypair

source

pub fn new() -> Self

Create a new, random, Ed25519Keypair.

+
source

pub fn public_key(&self) -> Ed25519PublicKey

Get the public Ed25519 key of this keypair.

+
source

pub fn sign(&self, message: &[u8]) -> Ed25519Signature

Sign the given message with our secret key.

+

Trait Implementations§

source§

impl Clone for Ed25519Keypair

source§

fn clone(&self) -> Self

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Default for Ed25519Keypair

source§

fn default() -> Self

Returns the “default value” for a type. Read more
source§

impl<'de> Deserialize<'de> for Ed25519Keypair

source§

fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>
where + __D: Deserializer<'de>,

Deserialize this value from the given Serde deserializer. Read more
source§

impl Serialize for Ed25519Keypair

source§

fn serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>
where + __S: Serializer,

Serialize this value into the given Serde serializer. Read more

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

source§

impl<T> DeserializeOwned for T
where + T: for<'de> Deserialize<'de>,

\ No newline at end of file diff --git a/vodozemac/struct.Ed25519PublicKey.html b/vodozemac/struct.Ed25519PublicKey.html new file mode 100644 index 00000000..d29d7f67 --- /dev/null +++ b/vodozemac/struct.Ed25519PublicKey.html @@ -0,0 +1,40 @@ +Ed25519PublicKey in vodozemac - Rust +
pub struct Ed25519PublicKey(/* private fields */);
Expand description

An Ed25519 public key, used to verify digital signatures.

+

Implementations§

source§

impl Ed25519PublicKey

source

pub const LENGTH: usize = 32usize

The number of bytes a Ed25519 public key has.

+
source

pub fn from_slice(bytes: &[u8; 32]) -> Result<Self, KeyError>

Try to create a Ed25519PublicKey from a slice of bytes.

+
source

pub fn as_bytes(&self) -> &[u8; 32]

View this public key as a byte array.

+
source

pub fn from_base64(input: &str) -> Result<Self, KeyError>

Instantiate a Ed25519PublicKey public key from an unpadded base64 +representation.

+
source

pub fn to_base64(&self) -> String

Serialize a Ed25519PublicKey public key to an unpadded base64 +representation.

+
source

pub fn verify( + &self, + message: &[u8], + signature: &Ed25519Signature +) -> Result<(), SignatureError>

Available on non-fuzzing only.

Verify that the provided signature for a given message has been signed +by the private key matching this public one.

+

By default this performs an RFC8032 compatible signature check. A +stricter version of the signature check can be enabled with the +strict-signatures feature flag.

+

The stricter variant is compatible with libsodium 0.16 and under the +hood uses the [ed25519_dalek::PublicKey::verify_strict()] method.

+

For more info, see the ed25519_dalek README and this post.

+

Trait Implementations§

source§

impl Clone for Ed25519PublicKey

source§

fn clone(&self) -> Ed25519PublicKey

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for Ed25519PublicKey

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl<'de> Deserialize<'de> for Ed25519PublicKey

source§

fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>
where + __D: Deserializer<'de>,

Deserialize this value from the given Serde deserializer. Read more
source§

impl Display for Ed25519PublicKey

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl PartialEq for Ed25519PublicKey

source§

fn eq(&self, other: &Ed25519PublicKey) -> bool

This method tests for self and other values to be equal, and is used +by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

This method tests for !=. The default implementation is almost always +sufficient, and should not be overridden without very good reason.
source§

impl Serialize for Ed25519PublicKey

source§

fn serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>
where + __S: Serializer,

Serialize this value into the given Serde serializer. Read more
source§

impl Copy for Ed25519PublicKey

source§

impl Eq for Ed25519PublicKey

source§

impl StructuralPartialEq for Ed25519PublicKey

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T> ToString for T
where + T: Display + ?Sized,

source§

default fn to_string(&self) -> String

Converts the given value to a String. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

source§

impl<T> DeserializeOwned for T
where + T: for<'de> Deserialize<'de>,

\ No newline at end of file diff --git a/vodozemac/struct.Ed25519SecretKey.html b/vodozemac/struct.Ed25519SecretKey.html new file mode 100644 index 00000000..a2a2377e --- /dev/null +++ b/vodozemac/struct.Ed25519SecretKey.html @@ -0,0 +1,41 @@ +Ed25519SecretKey in vodozemac - Rust +
pub struct Ed25519SecretKey(/* private fields */);
Expand description

An Ed25519 secret key, used to create digital signatures.

+

Implementations§

source§

impl Ed25519SecretKey

source

pub const LENGTH: usize = 32usize

The number of bytes a Ed25519 secret key has.

+
source

pub fn new() -> Self

Create a new random Ed25519SecretKey.

+
source

pub fn to_bytes(&self) -> Box<[u8; 32]>

Get the byte representation of the secret key.

+
source

pub fn from_slice(bytes: &[u8; 32]) -> Self

Try to create a Ed25519SecretKey from a slice of bytes.

+
source

pub fn to_base64(&self) -> String

Convert the secret key to a base64 encoded string.

+

This can be useful if the secret key needs to be sent over the network +or persisted.

+

Warning: The string should be zeroized after it has been used, +otherwise an unintentional copy of the key might exist in memory.

+
source

pub fn from_base64(input: &str) -> Result<Self, KeyError>

Try to create a Ed25519SecretKey from a base64 encoded string.

+
source

pub fn public_key(&self) -> Ed25519PublicKey

Get the public key that matches this Ed25519SecretKey.

+
source

pub fn sign(&self, message: &[u8]) -> Ed25519Signature

Sign the given slice of bytes with this Ed25519SecretKey.

+

The signature can be verified using the public key.

+
§Examples
+
use vodozemac::{Ed25519SecretKey, Ed25519PublicKey};
+
+let secret = Ed25519SecretKey::new();
+let message = "It's dangerous to go alone";
+
+let signature = secret.sign(message.as_bytes());
+
+let public_key = secret.public_key();
+
+public_key.verify(message.as_bytes(), &signature).expect("The signature has to be valid");
+

Trait Implementations§

source§

impl Default for Ed25519SecretKey

source§

fn default() -> Self

Returns the “default value” for a type. Read more
source§

impl<'de> Deserialize<'de> for Ed25519SecretKey

source§

fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>
where + __D: Deserializer<'de>,

Deserialize this value from the given Serde deserializer. Read more
source§

impl Serialize for Ed25519SecretKey

source§

fn serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>
where + __S: Serializer,

Serialize this value into the given Serde serializer. Read more

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

source§

impl<T> DeserializeOwned for T
where + T: for<'de> Deserialize<'de>,

\ No newline at end of file diff --git a/vodozemac/struct.Ed25519Signature.html b/vodozemac/struct.Ed25519Signature.html new file mode 100644 index 00000000..3d999c04 --- /dev/null +++ b/vodozemac/struct.Ed25519Signature.html @@ -0,0 +1,25 @@ +Ed25519Signature in vodozemac - Rust +
pub struct Ed25519Signature(/* private fields */);
Expand description

An Ed25519 digital signature, can be used to verify the authenticity of a +message.

+

Implementations§

source§

impl Ed25519Signature

source

pub const LENGTH: usize = 64usize

The number of bytes a Ed25519 signature has.

+
source

pub fn from_slice(bytes: &[u8]) -> Result<Self, SignatureError>

Try to create a Ed25519Signature from a slice of bytes.

+
source

pub fn from_base64(signature: &str) -> Result<Self, SignatureError>

Try to create a Ed25519Signature from an unpadded base64 +representation.

+
source

pub fn to_base64(&self) -> String

Serialize an Ed25519Signature to an unpadded base64 representation.

+
source

pub fn to_bytes(&self) -> [u8; 64]

Convert the Ed25519Signature to a byte array.

+

Trait Implementations§

source§

impl Clone for Ed25519Signature

source§

fn clone(&self) -> Ed25519Signature

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for Ed25519Signature

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl Display for Ed25519Signature

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl PartialEq for Ed25519Signature

source§

fn eq(&self, other: &Ed25519Signature) -> bool

This method tests for self and other values to be equal, and is used +by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

This method tests for !=. The default implementation is almost always +sufficient, and should not be overridden without very good reason.
source§

impl Copy for Ed25519Signature

source§

impl Eq for Ed25519Signature

source§

impl StructuralPartialEq for Ed25519Signature

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T> ToString for T
where + T: Display + ?Sized,

source§

default fn to_string(&self) -> String

Converts the given value to a String. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

\ No newline at end of file diff --git a/vodozemac/struct.KeyId.html b/vodozemac/struct.KeyId.html new file mode 100644 index 00000000..c63e0962 --- /dev/null +++ b/vodozemac/struct.KeyId.html @@ -0,0 +1,26 @@ +KeyId in vodozemac - Rust +

Struct vodozemac::KeyId

source ·
pub struct KeyId(/* private fields */);

Implementations§

Trait Implementations§

source§

impl Clone for KeyId

source§

fn clone(&self) -> KeyId

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for KeyId

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl<'de> Deserialize<'de> for KeyId

source§

fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>
where + __D: Deserializer<'de>,

Deserialize this value from the given Serde deserializer. Read more
source§

impl From<KeyId> for String

source§

fn from(value: KeyId) -> String

Converts to this type from the input type.
source§

impl Hash for KeyId

source§

fn hash<__H: Hasher>(&self, state: &mut __H)

Feeds this value into the given Hasher. Read more
1.3.0 · source§

fn hash_slice<H>(data: &[Self], state: &mut H)
where + H: Hasher, + Self: Sized,

Feeds a slice of this type into the given Hasher. Read more
source§

impl Ord for KeyId

source§

fn cmp(&self, other: &KeyId) -> Ordering

This method returns an Ordering between self and other. Read more
1.21.0 · source§

fn max(self, other: Self) -> Self
where + Self: Sized,

Compares and returns the maximum of two values. Read more
1.21.0 · source§

fn min(self, other: Self) -> Self
where + Self: Sized,

Compares and returns the minimum of two values. Read more
1.50.0 · source§

fn clamp(self, min: Self, max: Self) -> Self
where + Self: Sized + PartialOrd,

Restrict a value to a certain interval. Read more
source§

impl PartialEq for KeyId

source§

fn eq(&self, other: &KeyId) -> bool

This method tests for self and other values to be equal, and is used +by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

This method tests for !=. The default implementation is almost always +sufficient, and should not be overridden without very good reason.
source§

impl PartialOrd for KeyId

source§

fn partial_cmp(&self, other: &KeyId) -> Option<Ordering>

This method returns an ordering between self and other values if one exists. Read more
1.0.0 · source§

fn lt(&self, other: &Rhs) -> bool

This method tests less than (for self and other) and is used by the < operator. Read more
1.0.0 · source§

fn le(&self, other: &Rhs) -> bool

This method tests less than or equal to (for self and other) and is used by the <= +operator. Read more
1.0.0 · source§

fn gt(&self, other: &Rhs) -> bool

This method tests greater than (for self and other) and is used by the > operator. Read more
1.0.0 · source§

fn ge(&self, other: &Rhs) -> bool

This method tests greater than or equal to (for self and other) and is used by the >= +operator. Read more
source§

impl Serialize for KeyId

source§

fn serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>
where + __S: Serializer,

Serialize this value into the given Serde serializer. Read more
source§

impl Copy for KeyId

source§

impl Eq for KeyId

source§

impl StructuralPartialEq for KeyId

Auto Trait Implementations§

§

impl RefUnwindSafe for KeyId

§

impl Send for KeyId

§

impl Sync for KeyId

§

impl Unpin for KeyId

§

impl UnwindSafe for KeyId

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

source§

impl<T> DeserializeOwned for T
where + T: for<'de> Deserialize<'de>,

\ No newline at end of file diff --git a/vodozemac/struct.ProtoBufDecodeError.html b/vodozemac/struct.ProtoBufDecodeError.html new file mode 100644 index 00000000..84163de8 --- /dev/null +++ b/vodozemac/struct.ProtoBufDecodeError.html @@ -0,0 +1,21 @@ +ProtoBufDecodeError in vodozemac - Rust +
pub struct ProtoBufDecodeError { /* private fields */ }
Expand description

A Protobuf message decoding error.

+

DecodeError indicates that the input buffer does not contain a valid +Protobuf message. The error details should be considered ‘best effort’: in +general it is not possible to exactly pinpoint why data is malformed.

+

Trait Implementations§

source§

impl Clone for DecodeError

source§

fn clone(&self) -> DecodeError

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for DecodeError

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error>

Formats the value using the given formatter. Read more
source§

impl Display for DecodeError

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error>

Formats the value using the given formatter. Read more
source§

impl Error for DecodeError

Available on crate feature std only.
1.30.0 · source§

fn source(&self) -> Option<&(dyn Error + 'static)>

The lower-level source of this error, if any. Read more
1.0.0 · source§

fn description(&self) -> &str

👎Deprecated since 1.42.0: use the Display impl or to_string()
1.0.0 · source§

fn cause(&self) -> Option<&dyn Error>

👎Deprecated since 1.33.0: replaced by Error::source, which can support downcasting
source§

fn provide<'a>(&'a self, request: &mut Request<'a>)

🔬This is a nightly-only experimental API. (error_generic_member_access)
Provides type based access to context intended for error reports. Read more
source§

impl From<DecodeError> for DecodeError

source§

fn from(source: ProtoBufDecodeError) -> Self

Converts to this type from the input type.
source§

impl PartialEq for DecodeError

source§

fn eq(&self, other: &DecodeError) -> bool

This method tests for self and other values to be equal, and is used +by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

This method tests for !=. The default implementation is almost always +sufficient, and should not be overridden without very good reason.
source§

impl Eq for DecodeError

source§

impl StructuralPartialEq for DecodeError

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T> ToString for T
where + T: Display + ?Sized,

source§

default fn to_string(&self) -> String

Converts the given value to a String. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

\ No newline at end of file diff --git a/vodozemac/struct.SharedSecret.html b/vodozemac/struct.SharedSecret.html new file mode 100644 index 00000000..c0a94c69 --- /dev/null +++ b/vodozemac/struct.SharedSecret.html @@ -0,0 +1,46 @@ +SharedSecret in vodozemac - Rust +

Struct vodozemac::SharedSecret

source ·
pub struct SharedSecret(/* private fields */);
Expand description

The result of a Diffie-Hellman key exchange.

+

Each party computes this using their EphemeralSecret or StaticSecret and their +counterparty’s PublicKey.

+

Implementations§

source§

impl SharedSecret

source

pub fn to_bytes(&self) -> [u8; 32]

Convert this shared secret to a byte array.

+
source

pub fn as_bytes(&self) -> &[u8; 32]

View this shared secret key as a byte array.

+
source

pub fn was_contributory(&self) -> bool

Ensure in constant-time that this shared secret did not result from a +key exchange with non-contributory behaviour.

+

In some more exotic protocols which need to guarantee “contributory” +behaviour for both parties, that is, that each party contributed a public +value which increased the security of the resulting shared secret. +To take an example protocol attack where this could lead to undesirable +results from Thái “thaidn” Dương:

+
+

If Mallory replaces Alice’s and Bob’s public keys with zero, which is +a valid Curve25519 public key, he would be able to force the ECDH +shared value to be zero, which is the encoding of the point at infinity, +and thus get to dictate some publicly known values as the shared +keys. It still requires an active man-in-the-middle attack to pull the +trick, after which, however, not only Mallory can decode Alice’s data, +but everyone too! It is also impossible for Alice and Bob to detect the +intrusion, as they still share the same keys, and can communicate with +each other as normal.

+
+

The original Curve25519 specification argues that checks for +non-contributory behaviour are “unnecessary for Diffie-Hellman”. +Whether this check is necessary for any particular given protocol is +often a matter of debate, which we will not re-hash here, but simply +cite some of the relevant public discussions.

+
§Returns
+

Returns true if the key exchange was contributory (good), and false +otherwise (can be bad for some protocols).

+

Trait Implementations§

source§

impl AsRef<[u8]> for SharedSecret

source§

fn as_ref(&self) -> &[u8]

View this shared secret key as a byte array.

+
source§

impl Zeroize for SharedSecret

source§

fn zeroize(&mut self)

Zero out this object from memory using Rust intrinsics which ensure the +zeroization operation is not “optimized away” by the compiler.

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

\ No newline at end of file diff --git a/vodozemac/types/curve25519/struct.Curve25519PublicKey.html b/vodozemac/types/curve25519/struct.Curve25519PublicKey.html new file mode 100644 index 00000000..c3606738 --- /dev/null +++ b/vodozemac/types/curve25519/struct.Curve25519PublicKey.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../vodozemac/struct.Curve25519PublicKey.html...

+ + + \ No newline at end of file diff --git a/vodozemac/types/curve25519/struct.Curve25519SecretKey.html b/vodozemac/types/curve25519/struct.Curve25519SecretKey.html new file mode 100644 index 00000000..2c2c5e06 --- /dev/null +++ b/vodozemac/types/curve25519/struct.Curve25519SecretKey.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../vodozemac/struct.Curve25519SecretKey.html...

+ + + \ No newline at end of file diff --git a/vodozemac/types/ed25519/enum.SignatureError.html b/vodozemac/types/ed25519/enum.SignatureError.html new file mode 100644 index 00000000..1221b611 --- /dev/null +++ b/vodozemac/types/ed25519/enum.SignatureError.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../vodozemac/enum.SignatureError.html...

+ + + \ No newline at end of file diff --git a/vodozemac/types/ed25519/struct.Ed25519Keypair.html b/vodozemac/types/ed25519/struct.Ed25519Keypair.html new file mode 100644 index 00000000..59371dc1 --- /dev/null +++ b/vodozemac/types/ed25519/struct.Ed25519Keypair.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../vodozemac/struct.Ed25519Keypair.html...

+ + + \ No newline at end of file diff --git a/vodozemac/types/ed25519/struct.Ed25519PublicKey.html b/vodozemac/types/ed25519/struct.Ed25519PublicKey.html new file mode 100644 index 00000000..b647a68d --- /dev/null +++ b/vodozemac/types/ed25519/struct.Ed25519PublicKey.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../vodozemac/struct.Ed25519PublicKey.html...

+ + + \ No newline at end of file diff --git a/vodozemac/types/ed25519/struct.Ed25519SecretKey.html b/vodozemac/types/ed25519/struct.Ed25519SecretKey.html new file mode 100644 index 00000000..d92b0092 --- /dev/null +++ b/vodozemac/types/ed25519/struct.Ed25519SecretKey.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../vodozemac/struct.Ed25519SecretKey.html...

+ + + \ No newline at end of file diff --git a/vodozemac/types/ed25519/struct.Ed25519Signature.html b/vodozemac/types/ed25519/struct.Ed25519Signature.html new file mode 100644 index 00000000..b0ef1372 --- /dev/null +++ b/vodozemac/types/ed25519/struct.Ed25519Signature.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../vodozemac/struct.Ed25519Signature.html...

+ + + \ No newline at end of file diff --git a/vodozemac/types/enum.KeyError.html b/vodozemac/types/enum.KeyError.html new file mode 100644 index 00000000..d12c3b14 --- /dev/null +++ b/vodozemac/types/enum.KeyError.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../vodozemac/enum.KeyError.html...

+ + + \ No newline at end of file diff --git a/vodozemac/types/struct.KeyId.html b/vodozemac/types/struct.KeyId.html new file mode 100644 index 00000000..618927e7 --- /dev/null +++ b/vodozemac/types/struct.KeyId.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../vodozemac/struct.KeyId.html...

+ + + \ No newline at end of file diff --git a/vodozemac/types/struct.SharedSecret.html b/vodozemac/types/struct.SharedSecret.html new file mode 100644 index 00000000..0809c86f --- /dev/null +++ b/vodozemac/types/struct.SharedSecret.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../vodozemac/struct.SharedSecret.html...

+ + + \ No newline at end of file diff --git a/vodozemac/utilities/enum.DecodeError.html b/vodozemac/utilities/enum.DecodeError.html new file mode 100644 index 00000000..c6e77d3e --- /dev/null +++ b/vodozemac/utilities/enum.DecodeError.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../vodozemac/enum.Base64DecodeError.html...

+ + + \ No newline at end of file diff --git a/vodozemac/utilities/fn.base64_decode.html b/vodozemac/utilities/fn.base64_decode.html new file mode 100644 index 00000000..30d6adf2 --- /dev/null +++ b/vodozemac/utilities/fn.base64_decode.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../vodozemac/fn.base64_decode.html...

+ + + \ No newline at end of file diff --git a/vodozemac/utilities/fn.base64_encode.html b/vodozemac/utilities/fn.base64_encode.html new file mode 100644 index 00000000..2fd077c8 --- /dev/null +++ b/vodozemac/utilities/fn.base64_encode.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../vodozemac/fn.base64_encode.html...

+ + + \ No newline at end of file