The post An Example ECDH Key Exchange with HKDF and Authenticated Encryption in Rust appeared first on Web3 Developer.

]]>Note that the key exchange used in this project is unauthenticated on both sides because the keys are ephemeral and so there is no way for the client to know if it is communicating with the intended server, and vice versa. The means that a man in the middle attacker could intercept the encrypted communications by doing an ephemeral key exchange with the client and server at the same time and forwarding messages between them. In order to authenticate the server to the client we would need to have some root of trust that we can use to trust the server such as using certificates or static public keys which are verified as trusted out of band.

I started the project by creating the server which listens on a TCP socket and waits for connections from clients. When a new client connection comes in the server accepts the connection and then proceeds to send and receive messages over the `TcpStream`

. In the example below we simply start the server and then once a connection is created we wait for the client to send a message, print it and then reply to the client with a response message. Note that this example TCP server is single threaded and so would only be able to accept connections from one client at a time.

```
const HOST: &str = "127.0.0.1";
const PORT: &str = "7654";
const BUFFER_SIZE: usize = 50;
fn main() {
// Create a new socket
let listener = TcpListener::bind(format!("{}:{}", HOST, PORT)).unwrap();
loop {
// Create a new connection with the client
let (mut stream, address) = listener.accept().unwrap();
println!("Connection established with client at {:?}", address);
loop {
// Read and write using the connection
handle_connection(&mut stream);
}
}
}
fn handle_connection(stream: &mut TcpStream) {
let mut buffer = [0; BUFFER_SIZE];
// read from stream
stream.read(&mut buffer).unwrap();
println!("Server received request: {:?}", String::from_utf8(buffer.to_vec()).unwrap());
// write to stream
let response = "Response data\n";
stream.write(response.as_bytes()).unwrap();
}
```

Next we create the TCP client which connects to the server and once connected, sends a request to the server and then waits for the response which it then prints to the console. So far nothing is encrypted and the client and server are both assuming the data can be parsed as UTF-8 strings. The client sends the data over the `TcpStream`

once every 5 seconds.

```
const HOST: &str = "127.0.0.1";
const PORT: &str = "7654";
const BUFFER_SIZE: usize = 50;
fn main() {
let mut stream = TcpStream::connect(format!("{}:{}", HOST, PORT)).unwrap();
let mut buffer = [0; BUFFER_SIZE];
loop {
// write to stream
let request = "Request data\n";
stream.write(request.as_bytes()).unwrap();
// read from stream
stream.read(&mut buffer).unwrap();
println!("Client received response: {:?}", String::from_utf8(buffer.to_vec()).unwrap());
sleep(Duration::new(5, 0));
}
}
```

For this part I created a helper type called `EcdhEphemeralKeyExchange`

which is used by both the client and server programs and which internally uses the Ring cryptography library to implement the key exchange. An enum type `Actor`

is used to determine if the instance is being called from the client or server. The type creates a new `SystemRandom`

instance which is used as a source of entropy to generate the ephemeral keys used in the key exchange. During the key exchange the public keys are stored in the `pub_key`

and `peer_pub_key`

variables which are used later as input into the hash transcript. The run method executes the key change over the `TcpStream`

which is passed in as a parameter by sending and receiving the public keys to and from the peer.

```
#[derive(Debug)]
enum Actor {
CLIENT, SERVER
}
pub struct EcdhEphemeralKeyExchange {
actor: Actor,
rand: SystemRandom,
pub_key: Option<Vec<u8>>,
peer_pub_key: Option<Vec<u8>>,
}
impl EcdhEphemeralKeyExchange {
pub fn new_client() -> Self {
Self::new(CLIENT)
}
pub fn new_server() -> Self {
Self::new(SERVER)
}
fn new(actor: Actor) -> Self {
EcdhEphemeralKeyExchange {
actor,
rand: SystemRandom::new(),
pub_key: None,
peer_pub_key: None
}
}
pub fn client_pub_key(&self) -> Option<Vec<u8>> {
match self.actor {
CLIENT => return self.pub_key.clone(),
SERVER => return self.peer_pub_key.clone()
}
}
pub fn server_pub_key(&self) -> Option<Vec<u8>> {
match self.actor {
CLIENT => self.peer_pub_key.clone(),
SERVER => self.pub_key.clone()
}
}
pub fn run(&mut self, stream: &mut TcpStream) -> Result<(Vec<u8>, Vec<u8>), Unspecified> {
let alg = &X25519;
let my_private_key: EphemeralPrivateKey = EphemeralPrivateKey::generate(alg, &self.rand)?;
let my_public_key: PublicKey = my_private_key.compute_public_key()?;
println!("{:?}: public_key = {}", self.actor, hex::encode(my_public_key.as_ref()));
// Send our public key to the peer
stream.write_all(my_public_key.as_ref()).unwrap();
// Receive a public key from the peer
let mut peer_public_key = [0u8; 32];
stream.read_exact(&mut peer_public_key).unwrap();
println!("{:?}: peer_public_key = {}", self.actor, hex::encode(peer_public_key.as_ref()));
// The peer public key needs to be parsed before use so wrap it creating as an instance of UnparsedPublicKey
let peer_public_key = UnparsedPublicKey::new(alg, peer_public_key);
// Store public keys for usage in the HashTranscript
self.pub_key = Some(my_public_key.as_ref().to_vec());
self.peer_pub_key = Some(peer_public_key.bytes().to_vec());
// run ECDH to agree on a shared secret
agree_ephemeral(my_private_key,
&peer_public_key,
|shared_secret| self.kdf(shared_secret))
}
```

The output of ECDH should not directly be used as a session key to encrypt messages because the bits are not uniformly distributed. For this reason it is best practice to run the ECDH output through a key derivation function such as HKDF which will produce a uniformly distributed output which is cryptographically indistinguishable from random.

In the code below I’ve added a `kdf`

method to the `EcdhEphemeralKeyExchange`

type which is called just after the key change. In this method we use the HKDF extract and expand functions implemented by the Ring cryptography library to derive the session keys. The HKDF extract function is used to produce the uniformly distributed output and HKDF expand is used to stretch the key material to double the length so that we can create two session keys, one for the client to server communication and the other for the server to client communication. Having two session keys limits the scope of a compromise in the event that one of the session keys are recovered by an attacker.

```
fn kdf(&self, shared_secret: &[u8]) -> (Vec<u8>, Vec<u8>) {
// As recommended in RFC 7748 we should apply a KDF on the key material here
let salt = Salt::new(HKDF_SHA256, b""); // salt is optional
let pseudo_rand_key: Prk = salt.extract(shared_secret);
let mut context = self.client_pub_key().unwrap();
context.append(&mut self.server_pub_key().unwrap());
let context_data = [context.as_slice()];
const SESSION_KEY_LEN: usize = 2 * SHA256_OUTPUT_LEN;
struct SessionKeyType;
impl KeyType for SessionKeyType {
fn len(&self) -> usize {
SESSION_KEY_LEN
}
}
let output_key_material = pseudo_rand_key.expand(&context_data, SessionKeyType).unwrap();
let mut result = [0u8; SESSION_KEY_LEN];
output_key_material.fill(&mut result).unwrap();
let session_key = result.split_at(SESSION_KEY_LEN / 2);
(session_key.0.to_vec(), session_key.1.to_vec())
}
```

Next we create two helper types `AeadEncrypter`

and `AeadDecrypter`

which handle the encryption and decryption for each session. Both the client and the server will each create an instance of both types, one for each direction of the communication. For example the client will create an `AeadEncrypter`

to send messages to the server and an `AeadDecrypter`

to receive messages from the server. Each type gets constructed with the respective session key. These types both use AES-GCM from the Ring cryptography library which we use for authenticated encryption. AES-GCM requires passing a nonce to each encryption operation in order to randomise the encryption. The nonce must be unique and so we need to take care to never use repeated nonces for the same encryption key. The nonce doesn’t need to be unpredictable so we simply use a counter starting from one which is incremented for each encryption operation.

In the code below we implement the `NonceSequence`

interface as required by the library which takes the starting index as a parameter and increments the number to be used as the nonce with each call to the encryption and decryption operations.

```
pub struct CounterNonceSequence(u32);
impl NonceSequence for CounterNonceSequence {
// called once for each seal operation
fn advance(&mut self) -> Result<Nonce, Unspecified> {
let mut nonce_bytes = vec![0; NONCE_LEN];
nonce_bytes[8..].copy_from_slice(&self.0.to_be_bytes());
//println!("nonce_bytes = {}", hex::encode(&nonce_bytes));
self.0 += 1; // advance the counter
Nonce::try_assume_unique_for_key(&nonce_bytes)
}
}
```

The `AeadEncrypter`

type contains a `SealingKey`

which is constructed by passing in the session key and the nonce sequence. This allows us to use the same encryption key for the entire session. If we need to use a new key for another session we create another instance of `AeadEncrypter`

. The `encrypt`

method takes the data to be encrypted and the associated data as parameters. The associated data is not encrypted but rather is used to bind and authenticate some context data to the encryption.

```
pub struct AeadEncrypter {
sealing_key: SealingKey<CounterNonceSequence>
}
impl AeadEncrypter {
pub fn new(key: &[u8]) -> Self {
let unbound_key = UnboundKey::new(&AES_256_GCM, &key).unwrap();
AeadEncrypter {
sealing_key: SealingKey::new(unbound_key, CounterNonceSequence(1))
}
}
pub fn encrypt(&mut self, data: &[u8], associated_data: &[u8]) -> Result<Vec<u8>, Unspecified> {
let associated_data = Aad::from(associated_data);
let mut in_out = data.to_vec();
let tag = self.sealing_key.seal_in_place_separate_tag(associated_data, &mut in_out)?;
Ok([&in_out, tag.as_ref()].concat())
}
}
```

The `AeadDecrypter`

type is implemented similarly to the `AeadEncrypter`

and contains an `OpeningKey`

type which is constructed using the session key and nonce sequence as input. The `decrypt`

method takes the ciphertext and associated data as parameters. If the associated_data passed in does not match the data used in the encryption operation then the decryption will fail. This prevents attackers from tampering with the ciphertext while it is in transit.

```
pub struct AeadDecrypter {
opening_key: OpeningKey<CounterNonceSequence>
}
impl AeadDecrypter {
pub fn new(key: &[u8]) -> Self {
let unbound_key = UnboundKey::new(&AES_256_GCM, &key).unwrap();
AeadDecrypter {
opening_key: OpeningKey::new(unbound_key, CounterNonceSequence(1))
}
}
pub fn decrypt(&mut self, ciphertext: &[u8], associated_data: &[u8]) -> Result<Vec<u8>, Unspecified> {
let associated_data = Aad::from(associated_data);
let mut in_out = ciphertext.to_vec();
Ok(self.opening_key.open_in_place(associated_data, &mut in_out)?.to_vec())
}
}
```

A hash transcript is used to authenticate each AEAD decryption operation (similar to the design used in Noise Protocol Framework). We append the public keys and every message sent and received to the hash transcript in order to enforce that both the client and server are seeing the exact same messages in the same order. The hash transcript is implemented by using SHA-256 to hash new messages with the previous transcript hash which creates a kind of hash chain.

Here is the `HashTranscript`

type which has an `append`

method which hashes a new value with the previous hash to produce the updated transcript hash. This type is used by both the client and the server to record the full transcript of messages sent and received and the public keys used in the key exchange. The as_bytes method returns the transcript hash which is passed in as authenticated data to the `AeadEncrypter::encrypt`

and `AeadDecrypter::decrypt`

methods respectively.

```
pub struct HashTranscript {
hash: Hash
}
impl HashTranscript {
pub fn new() -> Self {
HashTranscript{ hash: Sha256.hash(b"") }
}
pub fn append(&mut self, bytes: &[u8]) {
let mut ctx = Sha256.new_context();
ctx.update(self.hash.as_bytes());
ctx.update(bytes);
self.hash = ctx.finish();
}
pub fn as_bytes(&self) -> &[u8] {
println!("transcript = {}", self.hash.to_hex());
self.hash.as_bytes()
}
}
```

Finally I updated the client and server to use these helper types to run the key exchange, and send and receive the encrypted messages.

Here is the `handle_session`

function defined in the client. Once we have a connection, we run the key exchange to create the two session keys, then we append the public keys to the `HashTranscript`

. Next, new `AeadEncrypter`

and `AeadDecrypter`

instances are created which are then used to encrypt and decrypt messages every 5 seconds. After each encryption and decryption operation we append each plaintext message to the hash transcript.

```
fn handle_session(mut stream: TcpStream) -> io::Result<usize> {
// Run the ephemeral to ephemeral key exchange as the client and return the session keys.
let mut ecdh = EcdhEphemeralKeyExchange::new_client();
let (client_to_server, server_to_client) = ecdh.run(&mut stream)
.map_err(|_e| Error::new(ErrorKind::Other, "Key exchange failed"))?;
let mut transcript = HashTranscript::new();
transcript.append(&ecdh.client_pub_key().unwrap());
transcript.append(&ecdh.server_pub_key().unwrap());
let mut encrypter = AeadEncrypter::new(&client_to_server);
let mut decrypter = AeadDecrypter::new(&server_to_client);
let mut counter: u32 = 0;
loop {
counter += 1;
// write to stream
let request = format!("Hello {}", counter);
let ciphertext = encrypter.encrypt(request.as_bytes(), transcript.as_bytes())
.map_err(|_e| Error::new(ErrorKind::Other, "Encryption failed"))?;
transcript.append(request.as_bytes());
let bytes_written = stream.write(&ciphertext)?;
println!("Client sent request {:?} bytes", bytes_written);
// read from stream
let mut buffer = [0; BUFFER_SIZE];
let bytes_read = stream.read(&mut buffer)?;
let plaintext = decrypter.decrypt(&buffer[..bytes_read], transcript.as_bytes())
.map_err(|_e| Error::new(ErrorKind::Other, "Decryption failed"))?;
transcript.append(&plaintext);
println!("Client received response: {:?}", String::from_utf8(plaintext)
.map_err(|e| Error::new(ErrorKind::Other, e.to_string()))?);
sleep(Duration::new(5, 0));
}
```

To see how all these code samples fit together you can view the full code example on Github here.

To start the server run:

`cargo run --bin ecdh-server`

To start the client run:

`cargo run --bin ecdh-client`

In this post I showed how to create a simple project which implements a ECDH key exchange using Rust and the Ring cryptography library. We created helper types which use the `ring::agreement`

, `ring::hkdf`

and `ring::aead`

modules for each of the required cryptography primitives. We covered how to create a tcp client and server, how to implement the ECDH key exchange, how to derive session keys using HKDF and how to encrypt and decrypt using AES-GCM with a hash transcript for message authentication.

The post An Example ECDH Key Exchange with HKDF and Authenticated Encryption in Rust appeared first on Web3 Developer.

]]>The post Group Theory Continued – Rings & Fields appeared first on Web3 Developer.

]]>A ring is an an algebraic type that is defined over addition, subtraction and multiplication but not division. It is also a group and therefore is also a set with a binary operation (specifically over addition). More precisely a ring is an abelian group over addition and a monoid under multiplication and distributive for multiplication with respect to addition. It fulfils the mathematical properties defined in the comments below. In the sample code below I define a Rust trait `Ring`

which extends `AbelianGroup`

and `Monoid`

and which defines the `add`

, `substract`

and `multiply`

methods. Each method takes an element type `E`

which represents an element in the set and returns another element after applying the operation.

```
/// A Ring is an abelian group over addition, a monoid under multiplication and distributive
/// for multiplication with respect to addition.
///
/// It has the following properties over multiplication:
/// Closure - a x b = c where a, b, and c are in the set
/// Associativity - a x (b x c) = (a x b) x c
/// Identity - a x I = a
///
/// Multiplication is distributive with respect to addition:
/// Left distributive - a x (b + c) = (a x b) + (a x c)
/// Right distributive - (b + c) x a = (b x a) + (c x a)
trait Ring<E>: AbelianGroup<Addition, E> + Monoid<Multiplication, E> {
/// Supports addition
fn add(&self, e1: E, e2: E) -> E;
/// Supports subtraction which is the same as adding the inverse
fn subtract(&self, e1: E, e2: E) -> E;
/// Supports multiplication
fn multiply(&self, e1: E, e2: E) -> E;
}
```

Here is an example of a ring, the natural numbers mod n. The natural numbers are basically the non-negative numbers and mod n means we are using modular arithmetic to restrict the numbers in the set to have elements less than n. You can read more about modular arithmetic here for a more detailed explanation. Note that we are not always able to divide the natural numbers mod n because when n is not prime some elements in the set may not have an inverse and so calculating a modular inverse would not always be possible.

In the code below we create a struct `NaturalNumbersModN`

which has a single parameter representing the size of the ring n. Next we implement the `Group`

trait for addition, setting the identity element to zero and the `Monoid`

trait setting the identity element to 1. The `add`

method is implemented by calling the `Group`

`apply`

method, `substract`

is implemented similarly except we add the inverse. `multiply`

is implemented by calling the `Monoid`

`apply`

method.

```
/// The natural numbers mod n form a ring.
struct NaturalNumbersModN(u32);
impl AbelianGroup<Addition, u32> for NaturalNumbersModN {}
impl Group<Addition, u32> for NaturalNumbersModN {
fn apply(&self, e1: u32, e2: u32) -> u32 {
(e1 + e2) % self.0
}
fn identity(&self) -> u32 {
0
}
fn inverse(&self, e: u32) -> u32 {
self.0 - e % self.0
}
}
impl Monoid<Multiplication, u32> for NaturalNumbersModN {
fn apply(&self, e1: u32, e2: u32) -> u32 {
(e1 * e2) % self.0
}
fn identity(&self) -> u32 {
1
}
}
impl Ring<u32> for NaturalNumbersModN {
fn add(&self, e1: u32, e2: u32) -> u32 {
<dyn AbelianGroup<Addition, u32>>::apply(self, e1, e2)
}
fn subtract(&self, e1: u32, e2: u32) -> u32 {
<dyn AbelianGroup<Addition, u32>>::apply(self, e1, self.inverse(e2))
}
fn multiply(&self, e1: u32, e2: u32) -> u32 {
Monoid::apply(self, e1, e2)
}
}
```

Next we test out the code using the natural numbers mod 4, which means our ring has a set with elements 0, 1, 2 and 3. The code below tests the `add`

, `substract`

and `multiply`

methods. Notice that when we add 3 and 2 we get 1 instead of 5 because the numbers wrap around. The `substract`

and `multiply`

methods work similarly always returning an element in the set between 0 and 3.

```
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn run_natural_numbers_modn() {
let ring = NaturalNumbersModN(4);
let result = ring.add(3, 2);
assert_eq!(1, result);
let result = ring.subtract(3, 3);
assert_eq!(0, result);
let result = ring.multiply(2, 3);
assert_eq!(2, result);
}
}
```

So now you have a basic idea of what a ring is and the operations it defines. Next I introduce fields and explain how they relate to rings and groups.

A field is an an algebraic type that is defined over addition, subtraction, multiplication and division. It is also a group and therefore is also a set but in this case with two binary operations (specifically over addition and multiplication). Put another way, a field is a ring which is also defined over division. More precisely a field is an abelian group over addition and an abelian group over multiplication and is distributive for multiplication with respect to addition.

It fulfils the mathematical properties defined for a group under both addition and multiplication. In the sample code below I define a Rust trait `Field`

which extends `AbelianGroup`

for both the `Addition`

and `Multiplication`

operations and which defines the `add`

, `substract`

, `multiply`

and `divide`

methods. Each method takes an element type `E`

which represents an element in the set and returns another element after applying the operation.

```
/// A Field is an abelian group over addition and multiplication and distributive
/// for multiplication with respect to addition.
trait Field<E>: AbelianGroup<Addition, E> + AbelianGroup<Multiplication, E> {
/// Supports addition
fn add(&self, e1: E, e2: E) -> E;
/// Supports subtraction which is the same as addition with the inverse
fn subtract(&self, e1: E, e2: E) -> E;
/// Supports multiplication
fn multiply(&self, e1: E, e2: E) -> E;
/// Supports division where the divisor is not equal to zero
fn divide(&self, e1: E, e2: E) -> E;
}
```

In this example we create a prime field using the natural numbers mod p where p is a prime. This is an example of a finite field because the number of elements in the set are bounded. It just so happens that if a prime number is selected for the modulus we have a field. This is because division is always defined due to every element in the set having an multiplicative inverse. In the code below I define a new struct `PrimeField`

which has a single parameter for the modulus p. The `new`

method is used to construct new instances of the type and uses a library to check if the number is a prime.

Next we implement the `Group`

traits for addition and multiplication. The inverse method for multiplication is defined using a trial and error approach which is not efficient, where we check if each inverse candidate multiplied by the number is equal to 1 until we get a match. In a future post I will show a better way to calculate multiplicative inverses using the Extended Euclidean algorithm. We then implement the `add`

, `subtract`

, `multiply`

and `divide`

methods for the `Field`

trait which each call their respective additive and multiplicative group operations which have already been defined.

```
/// The natural numbers mod p where p is a prime form a field.
struct PrimeField(u32);
impl PrimeField {
fn new(modulus: u32) -> Self {
if !primes::is_prime(modulus.into()) {
panic!("modulus is not a prime")
}
PrimeField(modulus)
}
}
impl AbelianGroup<Addition, u32> for PrimeField {}
impl Group<Addition, u32> for PrimeField {
fn apply(&self, e1: u32, e2: u32) -> u32 {
(e1 + e2) % self.0
}
fn identity(&self) -> u32 {
0
}
fn inverse(&self, e: u32) -> u32 {
self.0 - e % self.0
}
}
impl AbelianGroup<Multiplication, u32> for PrimeField {}
impl Group<Multiplication, u32> for PrimeField {
fn apply(&self, e1: u32, e2: u32) -> u32 {
(e1 * e2) % self.0
}
fn identity(&self) -> u32 {
1
}
fn inverse(&self, e: u32) -> u32 {
if e % self.0 == 0 {
panic!("Cannot calculate inverse for zero")
}
// This trial and error method to calculate the inverse is not efficient.
// In another post we will use the Extended Euclidean Algorithm to solve this instead.
for i in 0..self.0 {
if (e * i) % self.0 == 1 {
return i;
}
}
panic!("No inverse found");
}
}
impl Field<u32> for PrimeField {
fn add(&self, e1: u32, e2: u32) -> u32 {
<dyn AbelianGroup<Addition, u32>>::apply(self, e1, e2)
}
fn subtract(&self, e1: u32, e2: u32) -> u32 {
let inverse = <dyn AbelianGroup<Addition, u32>>::inverse(self,e2);
<dyn AbelianGroup<Addition, u32>>::apply(self, e1, inverse)
}
fn multiply(&self, e1: u32, e2: u32) -> u32 {
<dyn AbelianGroup<Multiplication, u32>>::apply(self, e1, e2)
}
fn divide(&self, e1: u32, e2: u32) -> u32 {
let inverse = <dyn AbelianGroup<Multiplication, u32>>::inverse(self,e2);
<dyn AbelianGroup<Multiplication, u32>>::apply(self, e1, inverse)
}
}
```

In the sample code below we test out the `PrimeField`

using 11 as the prime modulus. We test the `add`

, `substract`

, `multiply`

and `divide`

methods which each behave as expected and return elements in the set after applying the respective group operation under addition or multiplication. Finally we test that we can divide by any number in the field by looping through all possible divisors from 1 to 10 and verify that the code doesn’t panic with ‘No inverse found’, which would occur if a particular element had no multiplicative inverse.

```
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn run_prime_field() {
let field = PrimeField::new(11);
let result = field.add(3, 10);
assert_eq!(2, result);
let result = field.subtract(1, 3);
assert_eq!(9, result);
let result = field.multiply(2, 10);
assert_eq!(9, result);
let result = field.divide(10, 2);
assert_eq!(5, result);
// Every element in the field apart from zero has an inverse
for i in 1..11 {
let _result = field.divide(10, i);
}
}
}
```

In this post we continued on the topic of group theory by introducing two new algebraic types; rings and fields. We defined a `Ring`

trait to help explain the concept of a ring and created an example implementation using the natural numbers mod n which is always defined over addition, subtraction and multiplication. We then defined a `Field`

trait while explaining the definition of a field and how it relates to groups and rings. We then created an example field over the natural numbers mod p by defining a new type `PrimeField`

which implements the respective group operations over addition and multiplication. In a future post I will show how prime fields are used to implement certain cryptographic primitives such as the Diffie-Hellman key exchange so stay tuned for more updates.

The post Group Theory Continued – Rings & Fields appeared first on Web3 Developer.

]]>The post Mathematics for Cryptography – Introduction to Group Theory appeared first on Web3 Developer.

]]>Group theory is a branch of mathematics in the area of abstract algebra that studies the symmetries that recur throughout mathematics. A group is a very abstract concept and a generalisation of some other algebraic types such as rings and fields which I will explain more in a future post. It turns out that the patterns and laws described by group theory show up in number theory, algebraic equations and geometry. Most importantly cryptography relies on group theory and finite fields for primitives such as the Diffie-Hellman key exchange, RSA encryption and also for zero knowledge proofs.

So what is a group?

A group is defined as a set with a binary operation that satisfies a few properties; closure, associativity, identity and invert-ability. A set is just a collection of elements and the binary operation is not yet defined. It could be addition or multiplication but the definition of a group is more abstract than that and so it could also be geometric rotations over a shape for example.

In the sample code below I define a Rust trait `Group`

which models a group. It takes a type `T`

which must implement the `Operation`

marker trait. The comments describe the properties which much be satisfied by the group. I represent the binary operation as ‘.’ in the comments which is implemented by the `apply`

method which takes two elements and returns another element. The `identity`

method should return the identity element for the group and the `inverse`

method should return the inverse of the given element.

```
/// A group is a set and a binary operation (denoted by '.') which satisfies the following properties:
/// Closure - a . b = c where a, b, and c are in the set
/// Associativity - a . (b . c) = (a . b) . c
/// Identity - a . I = a
/// Inverse - a . a^-1 = I
pub trait Group<T: Operation, E> {
/// The binary operation which takes elements e1 and e2 of type E and returns the result
fn apply(&self, e1: E, e2: E) -> E;
/// Returns the identity element
fn identity(&self) -> E;
/// Returns the inverse of element e
fn inverse(&self, e: E) -> E;
}
```

An abelian group is a group that also supports the commutativity property (in addition to the other group properties) which basically means the binary operation can be applied in any order to the elements a and b and the result will be the same. I define it here as a marker trait for completeness which extends the `Group`

trait even though no additional methods are required.

```
/// A type which supports commutativity in addition to the standard group properties:
/// Commutativity - a . b = b . a
pub trait AbelianGroup<T: Operation, E>: Group<T, E> {}
```

A monoid is similar to a group except that elements may not have an inverse and it only supports the associativity and identity properties. It once again is a very abstract concept having a set and a binary operation. I define the `Monoid`

trait below with an `apply`

and `identity`

method but no inverse method. I will be using this type later when defining a trait for a ring in a future post.

```
/// A monoid is a set and a binary operation (denoted by '.') which satisfies the following properties:
/// Associativity - a . (b . c) = (a . b) . c
/// Identity - a . I = a
pub trait Monoid<T: Operation, E> {
/// The binary operation which takes elements e1 and e2 of type E and returns the result
fn apply(&self, e1: E, e2: E) -> E;
/// Returns the identity element
fn identity(&self) -> E;
}
```

Next I define the `Operation`

marker trait and implement `Addition`

and `Multiplication`

types to be used as markers. This is required so that a type can implement the trait over multiple operations. For example we might want to implement the `Group`

trait under both `Addition`

and `Multiplcation`

.

```
/// Marks the Monoid and Group traits with a type
pub trait Operation {}
pub struct Addition;
pub struct Multiplication;
impl Operation for Addition {}
impl Operation for Multiplication {}
```

Finally here is a very simple example of a group, the set of integers under addition. In mathematics the set of integers is defined as the whole numbers, positive and negative, including zero. With addition the set of integers forms a group because all the group properties are for-filled. The identity element is zero and the inverse is the negation of the given element. We also implement the `AbelianGroup`

trait for our new `AdditiveIntegers`

type because integer addition is commutative. You can check for yourself to verify that each of the group properties are for-filled with this implementation.

```
/// Integers under addition form a group where plus (+) is the group operation, zero is the identity
/// and negation (-) can be used to get the inverse of an element
struct AdditiveIntegers();
impl Group<Addition, i32> for AdditiveIntegers {
fn apply(&self, a: i32, b: i32) -> i32 {
a + b
}
fn identity(&self) -> i32 {
0
}
fn inverse(&self, a: i32) -> i32 {
-a
}
}
// Integers under addition supports commutativity
impl AbelianGroup<Addition, i32> for AdditiveIntegers {}
```

Next we test out the code by creating a new instance of the `AdditiveIntegers`

group. Then we test each of the methods to confirm that they each work as expected and for-fill the required properties, closure, associativity, identity, invert-ability and commutativity.

```
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn run_additive_integers() {
let group = AdditiveIntegers();
let a: i32 = 10;
let b: i32 = 20;
let c: i32 = 30;
// Closure - a . b = c where a, b, and c are in the set
let result: i32 = group.apply(a, b);
assert_eq!(c, result); // result is an integer so in the set
// Associativity - a . (b . c) = (a . b) . c
assert_eq!(group.apply(a, group.apply(b, c)),
group.apply(group.apply(a , b), c));
// Identity - a . I = a
assert_eq!(group.apply(a, group.identity()), a);
// Inverse - a . a^-1 = I
assert_eq!(group.apply(a, group.inverse(a)), group.identity());
// Commutativity - a . b = b . a
assert_eq!(group.apply(a, b), group.apply(b, a));
}
}
```

This post introduced group theory and explained the definition of a group, abelian group and monoid. We then created an example group, the integers over addition and showed an example usage. Future posts will cover topics such as rings and fields which will help explain why I’ve created these group types and will further explain these group theory concepts.

The post Mathematics for Cryptography – Introduction to Group Theory appeared first on Web3 Developer.

]]>The post Introduction to CryptoPals – Solutions to Set 1 in Rust appeared first on Web3 Developer.

]]>From the website… *‘This is a different way to learn about crypto than taking a class or reading a book. We give you problems to solve. They’re derived from weaknesses in real-world systems and modern cryptographic constructions. We give you enough info to learn about the underlying crypto concepts yourself. When you’re finished, you’ll not only have learned a good deal about how cryptosystems are built, but you’ll also understand how they’re attacked.’*

I’ve decided to have a go at solving these challenges and have recently completed the first set, so here are my solutions to CryptoPals Set 1 in Rust.

Here are the solutions to the first 8 CryptoPals challenges which are a part of Set 1. Set 1 covers some basic foundational topics that are essential knowledge in order to implement and code cryptography. I’ve provided a brief explanation of each problem and the solution to give you some background and context. You can find all my solutions to these problems on Github here.

The goal for the first challenge was to implement a solution to convert a hex encoded string to a base64 encoded string. The intention is that the hex string should first be converted to raw bytes and then the bytes converted to base64. This is important because generally cryptography algorithms operate on raw bytes and not strings. The hex and base64 string formats are mainly used for transfer or storage of data in a human readable format or for pretty printing.

It’s important to understand how each data type is stored in memory to better understand what it means to convert hex to bytes, bytes to base64 and vice versa. Strings in Rust are encoded in UTF-8 which is equivalent to ASCII for the alpha numeric characters. Using this format strings are encoded in a way where each character is represented by a designated number in memory. Converting the hex string to raw bytes means that we are decoding the hex characters and converting them to the number which the hex characters represent and then returning the number in binary format as a byte array. The reverse is to encode the binary number/data in the byte array as a hex string which represents the number.

See my full solution to this problem on Github here.

The goal of this challenge was to create a function to XOR two byte arrays together (of the same length) and return the result as another byte array. XOR is used everywhere in cryptography because of its security properties. Basically it can be used to encrypt data is a secure way as long as the key material is never used more than once. This form of encryption is called the one time pad.

In the code example I first convert the hex strings to bytes and then run the two input byte arrays through the XOR function which builds a new byte array by doing a bitwise XOR on each byte of two input arrays and returning the new array.

See my solution to this problem on Github here.

In this challenge we are given a ciphertext in hex and are told that it has been encrypted using XOR with a single character. In other words the key used is just a single character repeating. The challenge suggests looking at the frequency of characters used in the English language. It’s also worth noting that because the key is a single character then we know that the key space is 256 (2^8) because a character uses 8 bits in memory (assuming ASCII).

The approach used in my solution loops through each of the 256 possible keys, decrypts the ciphertext with the given key and then assesses the probability that the result is English text. We can use XOR to decrypt a ciphertext that was encrypted with XOR so we use the solution from Challenge 2 for this part. Each candidate plaintext is scored by comparing the expected frequency of the most common English characters to the actual frequency in the plaintext and a score is calculated by summing the difference of the actual minus the expect frequency for each letter. The lower the score the better and the plaintext candidate with the lowest score is returned and selected as the result which turns out to be the correct plaintext input. We also print the repeating key that was used to encrypt and decrypt along with the plaintext.

See my solution to this problem on Github here.

This problem is similar to Challenge 3 except that we are given a list of ciphertexts rather than just one and we need to determine which of the ciphertexts was encrypted with the single character repeating key XOR.

To solve this we load in the hex strings as a list and the map each to raw bytes using the hex to bytes function from Challenge 1. Then for each ciphertext we run the code from Challenge 3 which returns a score for that ciphertext and the best candidate key and plaintext. We then find the lowest score over all the ciphertexts and return it as the solution along with the key and the decrypted plaintext.

See my solution to this problem on Github here.

The goal of this challenge is to encrypt a plaintext using repeating key XOR. We are given a plaintext and told to encrypt it using a given key that repeats.

In my solution I have created a function that creates a repeating key which can then be used to XOR against the plaintext. The function takes the input key which needs to repeat and an output array which is filled with the key multiple times up to the length of the output array. After generating the repeating key using the function I then XOR the input plaintext given in the problem with the key to produce the ciphertext.

See my solution to this problem on Github here.

The goal of this challenge is to break repeating key XOR where we don’t know the length of the key used. We are given a ciphertext and told to decrypt it. The steps needed to get closer to solving the problem are given in the question and these are summarised below.

We start by creating a function that can calculate the edit distance (also known as hamming distance) between two byte arrays which is basically the number of differing bits. To implement this function we XOR the two input arrays together and then count the bits set to one in the resulting array.

Next we calculate a normalised edit distance for each possible key size (between 2 to 40 bytes). The normalised edit distance is calculated by dividing the edit distance by the key size. I eventually used first 4 key size slices of the ciphertext as input into this calculation and averaged the normalised edit distances to get a more actuate result. The key size that gives the lowest average normalised edit distance is the key size we use with the rest of the solution.

Now we use the selected key size to divide the ciphertext into blocks with length equal to the key size. Then we transpose the blocks by putting the first byte of each block into an array and then the second byte of each block into another array and so on. Then we use the solution from Challenge 3 to find the best candidate key for each of the transposed blocks. The idea here is that because the key is repeating, each of the bytes of the transposed blocks will have been encrypted with the same key.

After finding the key for each transposed block we concatenate each (partial key) together to produce the full key. We then use the repeating key function from Challenge 5 to build the full repeating key and then XOR it against the ciphertext to get the plaintext and solve the challenge.

See my solution to this problem on Github here.

In this challenge we are asked to decrypt a given ciphertext that has been encrypted with AES ECB. The encryption key used is provided in the question and it is suggested to use OpenSSL in the solution.

For my solution I simply imported the Rust bindings to OpenSSL and then used the `openssl::symm::decrypt`

function passing in the `Cipher::`

cipher to decrypt the ciphertext. The plaintext bytes are then converted to a string and printed. *aes_128_ecb*()

See my solution to this problem on Github here.

For this problem we are simply asked to detect which ciphertext in a list has been encrypted with AES ECB. Note that we don’t need to actually decrypt it. We are given the hint that AES ECB is stateless and deterministic so we know that a given plaintext input will always produce the same ciphertext. We also know that AES uses a block size of 16 bytes for encryption and decryption and so we can use this fact to detect ECB by looking for repeating blocks of ciphertext given that it is statistically likely for blocks of the ciphertext to repeat given that the ciphertext is long enough.

My solution to this problem works by looping through each ciphertext and breaking them into blocks of 16 bytes. We then insert each block into a set and stop when we detect the first duplicate block. We use a set type in Rust that returns false when inserting an element into the set that already exists. We then print the line number and the cipher text when the duplicate block is detected. It turns out that this method correctly identifies the ciphertext which was encrypted with AES ECB.

See my solution to this problem on Github here.

So there you have it, that covers the solutions to CryptoPals Set 1 with explanations. These problems cover some foundational cryptography concepts. Most of the questions are straight forward to solve but still are a valuable exercise to ground your knowledge in the basics of coding cryptography.

The post Introduction to CryptoPals – Solutions to Set 1 in Rust appeared first on Web3 Developer.

]]>The post Authenticated Encryption in Rust using Ring appeared first on Web3 Developer.

]]>`ring::aead`

module to generate an encryption key, encrypt some data and then decrypt using the provided `UnboundKey`

, `SealingKey`

and `OpeningKey`

types.
Basic encryption (without authentication) only protects against a passive man in the middle attacker who can observe the data as it travels over an insecure channel but it doesn’t prevent active attacks where the encrypted ciphertext can still be altered. Allowing the ciphertext to be altered without detection can lead to a number of vulnerabilities and so modern systems generally use authenticated encryption to prevent this. For example the AES-CBC-HMAC scheme uses HMAC authentication tags to verify the integrity of the ciphertext after encryption.

AEAD is a type of authenticated encryption which supports data integrity verification and additionally allows you to bind some plaintext associated data to the ciphertext which must be supplied as a part of the decryption. The decryption will fail if either the ciphertext or the associated data was altered as both are part of the integrity checks. The associated data is not encrypted but is authenticated and is useful for sending contextual information related to the encryption.

**AES with the Galois/Counter mode (AES-GCM)** is the most widely used AEAD. It has hardware support on many devices and internally uses GMAC which is a high performance MAC algorithm which is used for the integrity checks. It is based on AES-CTR which uses a nonce combined with an incrementing counter to produce a key stream which is then XORed with the plaintext to produce the ciphertext. This is why the algorithm requires supplying a nonce as a part of the encryption and decryption as you will see later in this post.

**ChaCha20-Poly1305** is an AEAD designed by Daniel J. Bernstein which was standardised by Google in 2013. It is faster than AES-GCM when AES hardware support is not available and so it is generally preferred for low end devices. Similar to AES-CTR, ChaCha20 is used to encrypt a nonce and counter to produce a key stream which is XORed with the plaintext to produce the ciphertext. Poly1305 is a high performance MAC algorithm much like GMAC which is used to verify the integrity of messages.

- For both AES-GCM and ChaCha20-Poly1305 a unique nonce must be supplied for each encryption operation. Repeated nonce usage can lead to vulnerabilities and so care must be taken to ensure that that there are no duplicate nonce values used with the same encryption key.
- Each nonce needs to be unique but doesn’t need to be unpredictable.
- The nonce has a size of 12 bytes which can hold up to 2^96 – 1 distinct values.
- An incrementing counter could be used as the nonce but requires keeping track of the state of the last counter used which can be error prone. If using a counter for the nonce the maximum number of encryption operations per key is 2^96 – 1.
- Alternatively a randomised nonce can be used which randomly generates a new nonce for each encryption operation. If using a random nonce no more than 2^30 encryption operations should be used to prevent increasing the probability of randomly generating a duplicate nonce.
- Encryption keys should be rotated periodically if encrypting a larger amount a data.
- Use ChaCha20-Poly1305 instead of AES-GCM when hardware support for AES is not available.

The `ring::aead`

module contains the following types and functions:

**trait BoundKey –** An AEAD key bound to a nonce sequence. Both SealingKey and OpeningKey implement this trait.

**trait NonceSequence –** A sequence of unique nonces. We will create a type which implements this trait for generating nonces as the library doesn’t implement this for us.

**struct Algorithm –** An AEAD algorithm. `AES_128_GCM`

, `AES_256_GCM`

and `CHACHA20_POLY1305`

algorithms are supported.

**struct UnboundKey –** An AEAD key without a designated role or nonce sequence.

**struct SealingKey –** An AEAD key for encrypting and signing (“sealing”), bound to a nonce sequence.

**struct Nonce –** A nonce for a single AEAD opening or sealing operation. Returned by the `NonceSequence`

type.

**struct Aad –** The additionally authenticated data (AAD) for an opening or sealing operation. This data is authenticated but is **not** encrypted.

**struct Tag –** An authentication tag. Used for integrity checking the ciphertext during decryption.

**struct OpeningKey –** An AEAD key for authenticating and decrypting (“opening”), bound to a nonce sequence.

Let’s start by importing the required types into our project.

```
use ring::error::Unspecified;
use ring::rand::SecureRandom;
use ring::rand::SystemRandom;
use ring::aead::Algorithm;
use ring::aead::AES_128_GCM;
use ring::aead::AES_256_GCM;
use ring::aead::CHACHA20_POLY1305;
use ring::aead::UnboundKey;
use ring::aead::BoundKey;
use ring::aead::SealingKey;
use ring::aead::OpeningKey;
use ring::aead::Aad;
use ring::aead::Tag;
use ring::aead::NonceSequence;
use ring::aead::NONCE_LEN;
use ring::aead::Nonce;
```

We start by generating a symmetric encryption key by using a new instance of `SystemRandom`

from the rand module. We collect 32 bytes of random data and use it as our secret key which is passed in to create a new instance of `UnboundKey`

which wraps the key value.

```
// Create a new instance of SystemRandom to be used as the single source of entropy
let rand = SystemRandom::new();
// Generate a new symmetric encryption key
let mut key_bytes = vec![0; AES_256_GCM.key_len()];
rand.fill(&mut key_bytes)?;
println!("key_bytes = {}", hex::encode(&key_bytes)); // don't print this in production code
// Create a new AEAD key without a designated role or nonce sequence
let unbound_key = UnboundKey::new(&AES_256_GCM, &key_bytes)?;
```

Next we create a `NonceSequence`

by implementing the trait. In this example I am using a simple incrementing counter as the method to generate nonces. We then create a new instance of the `NonceSequence`

starting from one and then pass it along with the `UnboundKey`

to create a new `SealingKey`

which is a type that binds the key to the nonce sequence. The first nonce value used will be one and so on. Note that the `UnboundKey`

and `NonceSequence`

instances are moved when creating the new `SealingKey`

. This design help to prevent reuse of the these sensitive values.

```
struct CounterNonceSequence(u32);
impl NonceSequence for CounterNonceSequence {
// called once for each seal operation
fn advance(&mut self) -> Result<Nonce, Unspecified> {
let mut nonce_bytes = vec![0; NONCE_LEN];
let bytes = self.0.to_be_bytes();
nonce_bytes[8..].copy_from_slice(&bytes);
println!("nonce_bytes = {}", hex::encode(&nonce_bytes));
self.0 += 1; // advance the counter
Nonce::try_assume_unique_for_key(&nonce_bytes)
}
}
// Create a new NonceSequence type which generates nonces
let nonce_sequence = CounterNonceSequence(1);
// Create a new AEAD key for encrypting and signing ("sealing"), bound to a nonce sequence
// The SealingKey can be used multiple times, each time a new nonce will be used
let mut sealing_key = SealingKey::new(unbound_key, nonce_sequence);
```

We prepare the data to be encrypted, the associated data, and then create a copy of the data so it can be encrypted in place. The original copy of the data will simply be used for comparison later. We then use the `SealingKey::seal_in_place_separate_tag`

method to encrypt the data, bind the associated data to it, and return the authentication tag. After returning, the `in_out`

variable will contain the ciphertext. The `SealingKey`

instance can be used to encrypt data multiple times using the same key.

```
// This data will be authenticated but not encrypted
//let associated_data = Aad::empty(); // is optional so can be empty
let associated_data = Aad::from(b"additional public data");
// Data to be encrypted
let data = b"hello world";
println!("data = {}", String::from_utf8(data.to_vec()).unwrap());
// Create a mutable copy of the data that will be encrypted in place
let mut in_out = data.clone();
// Encrypt the data with AEAD using the AES_256_GCM algorithm
let tag = sealing_key.seal_in_place_separate_tag(associated_data, &mut in_out)?;
```

We need to create new instances of some of the types because they were previously moved during use. We then create a new instance of `OpeningKey`

which binds the `UnboundKey`

to the `NonceSequence`

ready for decryption. Note that the `NonceSequence`

needs to generate the same nonce values that were used during the encryption. In this example I’m starting the nonce counter from one again.

```
// Recreate the previously moved variables
let unbound_key = UnboundKey::new(&AES_256_GCM, &key_bytes)?;
let nonce_sequence = CounterNonceSequence(1);
//let associated_data = Aad::empty(); // supplying the wrong data causes the decryption to fail
let associated_data = Aad::from(b"additional public data");
// Create a new AEAD key for decrypting and verifying the authentication tag
let mut opening_key = OpeningKey::new(unbound_key, nonce_sequence);
```

Finally we decrypt the ciphertext using the `OpeningKey::open_in_place`

method, passing in the associated data and the ciphertext concatenated with the authentication tag. If the integrity checks using the authentication tag succeed then the decrypted data is returned from the method otherwise an `error::Unspecified`

is returned.

```
// Decrypt the data by passing in the associated data and the cyphertext with the authentication tag appended
let mut cypher_text_with_tag = [&in_out, tag.as_ref()].concat();
let decrypted_data = opening_key.open_in_place(associated_data, &mut cypher_text_with_tag)?;
println!("decrypted_data = {}", String::from_utf8(decrypted_data.to_vec()).unwrap());
assert_eq!(data, decrypted_data);
```

Here is the full code sample for reference.

```
use ring::error::Unspecified;
use ring::rand::SecureRandom;
use ring::rand::SystemRandom;
use ring::aead::Algorithm;
use ring::aead::AES_128_GCM;
use ring::aead::AES_256_GCM;
use ring::aead::CHACHA20_POLY1305;
use ring::aead::UnboundKey;
use ring::aead::BoundKey;
use ring::aead::SealingKey;
use ring::aead::OpeningKey;
use ring::aead::Aad;
use ring::aead::Tag;
use ring::aead::NonceSequence;
use ring::aead::NONCE_LEN;
use ring::aead::Nonce;
struct CounterNonceSequence(u32);
impl NonceSequence for CounterNonceSequence {
// called once for each seal operation
fn advance(&mut self) -> Result<Nonce, Unspecified> {
let mut nonce_bytes = vec![0; NONCE_LEN];
let bytes = self.0.to_be_bytes();
nonce_bytes[8..].copy_from_slice(&bytes);
println!("nonce_bytes = {}", hex::encode(&nonce_bytes));
self.0 += 1; // advance the counter
Nonce::try_assume_unique_for_key(&nonce_bytes)
}
}
fn main() -> Result<(), Unspecified> {
// Create a new instance of SystemRandom to be used as the single source of entropy
let rand = SystemRandom::new();
// Generate a new symmetric encryption key
let mut key_bytes = vec![0; AES_256_GCM.key_len()];
rand.fill(&mut key_bytes)?;
println!("key_bytes = {}", hex::encode(&key_bytes)); // don't print this in production code
// Create a new AEAD key without a designated role or nonce sequence
let unbound_key = UnboundKey::new(&AES_256_GCM, &key_bytes)?;
// Create a new NonceSequence type which generates nonces
let nonce_sequence = CounterNonceSequence(1);
// Create a new AEAD key for encrypting and signing ("sealing"), bound to a nonce sequence
// The SealingKey can be used multiple times, each time a new nonce will be used
let mut sealing_key = SealingKey::new(unbound_key, nonce_sequence);
// This data will be authenticated but not encrypted
//let associated_data = Aad::empty(); // is optional so can be empty
let associated_data = Aad::from(b"additional public data");
// Data to be encrypted
let data = b"hello world";
println!("data = {}", String::from_utf8(data.to_vec()).unwrap());
// Create a mutable copy of the data that will be encrypted in place
let mut in_out = data.clone();
// Encrypt the data with AEAD using the AES_256_GCM algorithm
let tag = sealing_key.seal_in_place_separate_tag(associated_data, &mut in_out)?;
// Recreate the previously moved variables
let unbound_key = UnboundKey::new(&AES_256_GCM, &key_bytes)?;
let nonce_sequence = CounterNonceSequence(1);
//let associated_data = Aad::empty(); // supplying the wrong data causes the decryption to fail
let associated_data = Aad::from(b"additional public data");
// Create a new AEAD key for decrypting and verifying the authentication tag
let mut opening_key = OpeningKey::new(unbound_key, nonce_sequence);
// Decrypt the data by passing in the associated data and the cypher text with the authentication tag appended
let mut cypher_text_with_tag = [&in_out, tag.as_ref()].concat();
let decrypted_data = opening_key.open_in_place( associated_data, &mut cypher_text_with_tag)?;
println!("decrypted_data = {}", String::from_utf8(decrypted_data.to_vec()).unwrap());
assert_eq!(data, decrypted_data);
Ok(())
}
```

In this post I introduced the concept of authenticated encryption and explained what it is used for and how it works. I also provided an overview of the two most commonly used AEAD algorithms AES-GCM and ChaCha20-Poly1305 and explained how they both require a nonce as input which must to be unique in order to ensure the security of the encryption scheme. We then walked through an example where we randomly generated a symmetric key then used the `UnboundKey`

, `SealingKey`

and `OpeningKey`

to securely encrypt the data and then later decrypt by supplying the authentication tag and associated data along with the key.

The post Authenticated Encryption in Rust using Ring appeared first on Web3 Developer.

]]>The post Ring by Example – Sample Code Cheatsheet appeared first on Web3 Developer.

]]>This is basically a cheatsheet of example code showing how to use each of the cryptography primitives supported by Ring. I’ve tried to keep the examples as simple as possible in order to make the code samples easy to read through and as re-usable as possible. For a more detailed explanation on how to use each of the modules you can see my previous posts on each topic which are linked in the relevant sections below.

The Ring cryptography library contains the following modules for working with various cryptography primitives:

`ring::digest`

– Cryptographic hash functions`ring::rand`

– Cryptographically secure pseudo-random number generation`ring::hmac`

– Signing and verification with HMAC`ring::hkdf`

– Key derivation with HKDF`ring::pbkdf2`

– Password hashing and verification with PBKDF2`ring::agreement`

– Key exchange with ECDH`ring::signature`

– Signing and verification with digital signatures`ring::aead`

– Authenticated encryption using AEAD

The modules support primitives such as hash functions, random numbers, message authentication codes, key derivation, key agreement, digital signatures and authenticated encryption. The code samples below show how to use the APIs defined in each of these modules. Note that this post was written using version 0.16.20 of the library.

**Disclaimer: Make sure you understand what the code is doing before copying into any real world application.**

See the full post on using the `ring::digest`

module here.

```
use ring::digest;
use ring::digest::Digest;
use ring::digest::Context;
use ring::digest::SHA256;
fn main() {
// Calculate a hash using the digest function
let _digest: Digest = digest::digest(&SHA256, b"hello, world");
// Calculate a hash using the Context struct
let mut ctx = Context::new(&SHA256);
ctx.update(b"hello, ");
ctx.update(b"world");
let _digest = ctx.finish();
}
```

See the full post on using the `ring::rand`

module here.

```
use ring::error::Unspecified;
use ring::rand;
use ring::rand::SecureRandom;
use ring::rand::SystemRandom;
use ring::rand::Random;
fn main() -> Result<(), Unspecified> {
const NUM_SIZE_BYTES: usize = 32;
let rand = SystemRandom::new();
// Generate random number using the SecureRandom::fill method
let mut rand_bytes = [0u8; NUM_SIZE_BYTES];
rand.fill(&mut rand_bytes)?;
// Generate random number using the rand::generate function
let result: Random<[u8; NUM_SIZE_BYTES]> = rand::generate(&rand)?;
let _rand_bytes = result.expose();
Ok(())
}
```

See the full post on using the `ring::hmac`

module here.

```
use ring::error::Unspecified;
use ring::rand::SystemRandom;
use ring::hmac;
use ring::hmac::Key;
use ring::hmac::Context;
use ring::hmac::HMAC_SHA256;
fn main() -> Result<(), Unspecified> {
let rand = SystemRandom::new();
let key = Key::generate(HMAC_SHA256, &rand)?;
// Sign a message and then verify using the hmac::sign function
let tag = hmac::sign(&key, b"hello, world");
hmac::verify(&key, b"hello, world", tag.as_ref())?;
// Sign a message and then verify using the Context struct
let mut ctx = Context::with_key(&key);
ctx.update(b"hello, ");
ctx.update(b"world");
let tag = ctx.sign();
hmac::verify(&key, b"hello, world", tag.as_ref())
}
```

See the full post on using the `ring::hkdf`

module here.

```
use ring::error::Unspecified;
use ring::digest::SHA256_OUTPUT_LEN;
use ring::hkdf::Salt;
use ring::hkdf::Prk;
use ring::hkdf::Okm;
use ring::hkdf::Algorithm;
use ring::hkdf::HKDF_SHA256;
fn main() -> Result<(), Unspecified> {
// Derive a single output key using Salt::extract and Prk::expand
let input_key_material = b"secret key";
let salt = Salt::new(HKDF_SHA256, b"salt bytes");
let pseudo_rand_key: Prk = salt.extract(input_key_material);
let context_data = ["context field 1".as_bytes(), "context field 2".as_bytes()];
let output_key_material: Okm<Algorithm> = pseudo_rand_key.expand(&context_data, HKDF_SHA256)?;
let mut result = [0u8; SHA256_OUTPUT_LEN];
output_key_material.fill(&mut result)
}
```

See the full post on using the `ring::pbkdf2`

module here.

```
use ring::error::Unspecified;
use std::num::NonZeroU32;
use ring::digest::SHA256_OUTPUT_LEN;
use ring::pbkdf2;
use ring::pbkdf2::PBKDF2_HMAC_SHA256;
fn main() -> Result<(), Unspecified> {
// Derive a password hash and verify using pbkdf2::derive and pbkdf2::verify
let iterations = NonZeroU32::new(600_000).unwrap();
let salt = b"random salt";
let secret = b"strong password";
let mut password_hash = [0u8; SHA256_OUTPUT_LEN];
pbkdf2::derive(PBKDF2_HMAC_SHA256, iterations, salt, secret, &mut password_hash);
pbkdf2::verify(PBKDF2_HMAC_SHA256, iterations, salt, secret, &password_hash)
}
```

See the full post on using the `ring::agreement`

module here.

```
use ring::error::Unspecified;
use ring::rand::SystemRandom;
use ring::agreement;
use ring::agreement::Algorithm;
use ring::agreement::X25519;
use ring::agreement::EphemeralPrivateKey;
use ring::agreement::PublicKey;
use ring::agreement::UnparsedPublicKey;
fn main() -> Result<(), Unspecified> {
// Derived a shared secret using ECDH
let rng = SystemRandom::new();
let alg: &Algorithm = &X25519;
let my_private_key: EphemeralPrivateKey = EphemeralPrivateKey::generate(alg, &rng)?;
let _my_public_key: PublicKey = my_private_key.compute_public_key()?;
// Send our public key to the peer here
let peer_public_key: PublicKey = { // Simulate receiving a public key from the peer
let peer_private_key = EphemeralPrivateKey::generate(alg, &rng)?;
peer_private_key.compute_public_key()?
};
let peer_public_key = UnparsedPublicKey::new(alg, peer_public_key);
agreement::agree_ephemeral(my_private_key,
&peer_public_key,
|_shared_secret: &[u8]| {
// use the shared secret
})
}
```

See the full post on using the `ring::signature`

module here.

```
use ring::error::Unspecified;
use ring::rand::SystemRandom;
use ring::signature::ED25519;
use ring::signature::KeyPair;
use ring::signature::Ed25519KeyPair;
use ring::signature::UnparsedPublicKey;
fn main() -> Result<(), Unspecified> {
// Sign and verify a message using EdDSA
let rand = SystemRandom::new();
let pkcs8_bytes = Ed25519KeyPair::generate_pkcs8(&rand)?;
let key_pair = Ed25519KeyPair::from_pkcs8(pkcs8_bytes.as_ref()).map_err(|_| Unspecified)?;
const MESSAGE: &[u8] = b"hello, world";
let sig = key_pair.sign(MESSAGE);
let peer_public_key_bytes = key_pair.public_key().as_ref();
let peer_public_key = UnparsedPublicKey::new(&ED25519, peer_public_key_bytes);
peer_public_key.verify(MESSAGE, sig.as_ref())
}
```

See the full post on using the `ring::aead`

module here.

```
use ring::error::Unspecified;
use ring::rand::SecureRandom;
use ring::rand::SystemRandom;
use ring::aead::AES_256_GCM;
use ring::aead::UnboundKey;
use ring::aead::BoundKey;
use ring::aead::SealingKey;
use ring::aead::OpeningKey;
use ring::aead::Aad;
use ring::aead::NonceSequence;
use ring::aead::NONCE_LEN;
use ring::aead::Nonce;
// Always returns the same nonce value for simplicity, don't use for more than 1 sealing operation!
struct SingleNonceSequence([u8; NONCE_LEN]);
impl NonceSequence for SingleNonceSequence {
fn advance(&mut self) -> Result<Nonce, Unspecified> {
Nonce::try_assume_unique_for_key(&self.0)
}
}
fn main() -> Result<(), Unspecified> {
// Encrypt and decrypt some data using AES-GCM-256
let rand = SystemRandom::new();
let mut key_bytes = vec![0; AES_256_GCM.key_len()];
rand.fill(&mut key_bytes)?;
let mut nonce_value = [0; NONCE_LEN];
rand.fill(&mut nonce_value)?;
let unbound_key = UnboundKey::new(&AES_256_GCM, &key_bytes)?;
let nonce_sequence = SingleNonceSequence(nonce_value);
let mut sealing_key = SealingKey::new(unbound_key, nonce_sequence);
let associated_data = Aad::from(b"additional public data");
let data = b"hello world";
let mut in_out = data.clone();
let tag = sealing_key.seal_in_place_separate_tag(associated_data, &mut in_out)?;
let unbound_key = UnboundKey::new(&AES_256_GCM, &key_bytes)?;
let nonce_sequence = SingleNonceSequence(nonce_value);
let mut opening_key = OpeningKey::new(unbound_key, nonce_sequence);
let associated_data = Aad::from(b"additional public data");
let mut cypher_text_with_tag = [&in_out, tag.as_ref()].concat();
let decrypted_data = opening_key.open_in_place(associated_data, &mut cypher_text_with_tag)?;
assert_eq!(data, decrypted_data);
Ok(())
}
```

In this example code cheatsheet we covered how to use each of the Ring modules to implement cryptography primitives such as hash functions, random numbers, message authentication codes, key derivation, key agreement, digital signatures and authenticated encryption. If you want to read more about any of the topics check out the linked resources above as well.

Thanks for reading. I hope you find this resource useful. If you have any questions about using the Ring cryptography library, feel free to reach out.

The post Ring by Example – Sample Code Cheatsheet appeared first on Web3 Developer.

]]>The post Signing & Verifying Messages with Digital Signatures in Rust using Ring appeared first on Web3 Developer.

]]>`sign`

and `verify`

methods defined in the `ring::signature`

module to create and then verify signatures for any given message.
Digital signatures are similar to message authentication codes in that they provide a way to check the integrity and authenticity of messages (see my post on HMAC here) but they additionally provide non-repudiation. This is because MACs use a symmetric key as the secret where as digital signatures use a public/private key pair. The signature is produce by using the private key to sign a message and then later the signature can be checked against the message using the public key to determine if the signature is valid for that message.

If a signature successfully verifies then we can be sure that the message has not been modified and that it originated from the owner of the private key which is associated with the public key. This means that the owner of the private key cannot deny sending the message because of the valid signature associated with the message and hence we have non-repudiation.

The interface of a digital signature **sign** function looks something like this:

`signature = sign(private_key, message)`

The interface of a digital signature **verify** function looks something like this:

`result = verify(public_key, message, signature)`

Result is a boolean where true means that the signature is valid over the message and false means that the signature verification has failed.

Internally digital signature algorithms use asymmetric cryptography where public/private key pairs are used for encryption and decryption. To generate a signature the message to be signed is first hashed using a cryptographically secure hash function and then the hash is encrypted using the private key. To verify the signature the encrypted hash is decrypted using the public key and the message is hashed to verify that it matches the decrypted hash value. Messages are hashed in this way because asymmetric encryption can only encrypt values of limited size and it is also much slower then symmetric encryption in terms of performance.

In the code examples below I will show how to sign and verify messages using both **ECDSA** and **EdDSA** digital signature algorithms so lets start with a bit of background.

**Elliptic Curve Digital Signature Algorithm (ECDSA)** is a cryptographically secure digital signature scheme based on elliptic-curve cryptography (ECC). It relies on the math of the cyclic groups of elliptic curves over finite fields and on the difficulty of the elliptic-curve discrete logarithm problem. The signature algorithm uses a random nonce as an input which must not be re-used and must be completely unpredictable. In order to get 128 bits of security (relative to symmetric cryptography) we can use 256 bit key pairs which will generate 64 byte signatures.

**Edwards-curve Digital Signature Algorithm (EdDSA)** is a digital signature scheme that uses a variant of Schnorr signature based on twisted Edwards curves. It is designed to be faster than existing digital signature schemes without sacrificing security. EdDSA signatures use the Edwards form of elliptic curves and the algorithm relies on the difficulty of the elliptic-curve discrete logarithm problem. As was the case above, EdDSA provides 128 bits of security when used with 256 bit keys (such as Ed25519) which generate 64 byte signatures.

The `ring::signature`

module contains the following types and functions:

**trait KeyPair** – Key pairs for signing messages (private key and public key). Each of the key pairs for the various digital signature algorithms implement this trait.

**trait VerificationAlgorithm** – A signature verification algorithm. Each of the various signature verification algorithms implement this trait.

**struct Signature** – A public key signature returned from a signing operation. This is basically a wrapper around the signature byte array.

**struct UnparsedPublicKey** – An unparsed, possibly malformed, public key for signature verification. The message and signature is passed into the verify message which is a method on the public key type. This design enforces validation/parsing of the public key before verifying a signature which is an important security consideration to prevent certain vulnerabilities.

For a full listing of all types see the module documentation here.

Let’s start by importing the required types into our project.

```
use ring::error::Unspecified;
use ring::rand::SystemRandom;
use ring::signature;
use ring::signature::KeyPair;
use ring::signature::UnparsedPublicKey;
use ring::signature::EcdsaKeyPair;
use ring::signature::Ed25519KeyPair;
use ring::signature::ECDSA_P256_SHA256_ASN1;
use ring::signature::ECDSA_P256_SHA256_ASN1_SIGNING;
use ring::signature::ED25519;
```

In the example below we use the `ECDSA_P256_SHA256_ASN1_SIGNING`

algorithm for signing a message and the `ECDSA_P256_SHA256_ASN1`

algorithm for verifying the signature.

First start by generating a new ECDSA key pair using an instance of `SystemRandom`

as the source of entropy (for more background on generating random numbers in Rust see here). First create the key pair in pkcs8 document format which can be used to persist the key if needed and then we convert it into an `EcdsaKeyPair`

.

```
// generate a new ECDSA key pair
let rand = SystemRandom::new();
let pkcs8_bytes = EcdsaKeyPair::generate_pkcs8(&ECDSA_P256_SHA256_ASN1_SIGNING, &rand)?; // pkcs8 format used for persistent storage
let key_pair = EcdsaKeyPair::from_pkcs8(&ECDSA_P256_SHA256_ASN1_SIGNING, pkcs8_bytes.as_ref(), &rand).map_err(|_| Unspecified)?;
```

We then create a message and sign it by passing in the message to the `sign`

method which returns the the signature on success. Note that the `SystemRandom`

is used in the sign method to generate a random nonce for each signature. The nonce must be random and never re-used so you should make sure that only a single instance of SystemRandom is used throughout the program in order to prevent the possibility of using the same source of entropy multiple times.

```
// create a message and sign using the key pair
const MESSAGE: &[u8] = b"hello, world";
let sig = key_pair.sign(&rand, MESSAGE)?;
```

The public key is then passed into a new instance of `UnparsedPublicKey`

. Next the verify method is called on the `UnparsedPublicKey`

instance passing in the message and signature bytes. The verify method returns `()`

on success and `error::Unspecified`

otherwise.

```
// get the public key as bytes
let peer_public_key_bytes = key_pair.public_key().as_ref();
// verify the signature using the public key and message
let peer_public_key = UnparsedPublicKey::new(&ECDSA_P256_SHA256_ASN1, peer_public_key_bytes);
peer_public_key.verify(MESSAGE, sig.as_ref())?;
```

Here is another example which is similar to the above scenario but instead this time we use EdDSA for signing and verification. Note that the signing method doesn’t require passing in the `SystemRandom`

instance in this case. We use the default EdDSA signing algorithm and the `ED25519`

verification algorithm.

```
// generate a new Ed25519 key pair
let rand = SystemRandom::new();
let pkcs8_bytes = Ed25519KeyPair::generate_pkcs8(&rand)?; // pkcs8 format used for persistent storage
let key_pair = Ed25519KeyPair::from_pkcs8(pkcs8_bytes.as_ref()).map_err(|_| Unspecified)?;
// create a message and sign using the key pair
const MESSAGE: &[u8] = b"hello, world";
let sig = key_pair.sign(MESSAGE);
// get the public key as bytes
let peer_public_key_bytes = key_pair.public_key().as_ref();
// verify the signature using the public key and message
let peer_public_key = UnparsedPublicKey::new(&ED25519, peer_public_key_bytes);
peer_public_key.verify(MESSAGE, sig.as_ref())
```

Here is the full code sample for reference.

```
use ring::error::Unspecified;
use ring::rand::SystemRandom;
use ring::signature;
use ring::signature::KeyPair;
use ring::signature::UnparsedPublicKey;
use ring::signature::EcdsaKeyPair;
use ring::signature::Ed25519KeyPair;
use ring::signature::ECDSA_P256_SHA256_ASN1;
use ring::signature::ECDSA_P256_SHA256_ASN1_SIGNING;
use ring::signature::ED25519;
fn main() -> Result<(), Unspecified> {
// generate a new ECDSA key pair
let rand = SystemRandom::new();
let pkcs8_bytes = EcdsaKeyPair::generate_pkcs8(&ECDSA_P256_SHA256_ASN1_SIGNING,&rand)?; // pkcs8 format used for persistent storage
let key_pair = EcdsaKeyPair::from_pkcs8(&ECDSA_P256_SHA256_ASN1_SIGNING, pkcs8_bytes.as_ref(), &rand).map_err(|_| Unspecified)?;
// create a message and sign using the key pair
const MESSAGE: &[u8] = b"hello, world";
let sig = key_pair.sign(&rand,MESSAGE)?;
// get the public key as bytes
let peer_public_key_bytes = key_pair.public_key().as_ref();
// verify the signature using the public key and message
let peer_public_key = UnparsedPublicKey::new(&ECDSA_P256_SHA256_ASN1, peer_public_key_bytes);
peer_public_key.verify(MESSAGE, sig.as_ref())?;
// generate a new Ed25519 key pair
let pkcs8_bytes = Ed25519KeyPair::generate_pkcs8(&rand)?; // pkcs8 format used for persistent storage
let key_pair = Ed25519KeyPair::from_pkcs8(pkcs8_bytes.as_ref()).map_err(|_| Unspecified)?;
// create a message and sign using the key pair
let sig = key_pair.sign(MESSAGE);
// get the public key as bytes
let peer_public_key_bytes = key_pair.public_key().as_ref();
// verify the signature using the public key and message
let peer_public_key = UnparsedPublicKey::new(&ED25519, peer_public_key_bytes);
peer_public_key.verify(MESSAGE, sig.as_ref())
}
```

In this post I gave a brief introduction to digital signatures, introduced the `signature`

module in the Ring cryptography library and gave a few examples showing how to create and verify signatures. I demonstrated how to use the `sign`

method defined on the key pair types to return an instance of `Signature`

and then how to use the `verify`

method defined on the `UnparsedPublicKey`

type to check signatures for validity.

The post Signing & Verifying Messages with Digital Signatures in Rust using Ring appeared first on Web3 Developer.

]]>The post ECDH Key Agreement in Rust using Ring appeared first on Web3 Developer.

]]>`agreement`

module of the Rust cryptography library Ring. We will go through generating a public/private key pair, exchanging public keys with a peer and then using the `agree_ephemeral`

function to securely compute a shared secret.
**Elliptic-curve Diffie-Hellman (ECDH)** is a key agreement protocol which allows two parties to compute a shared secret over an insecure channel using elliptic curve public/private key pairs. It is based on the earlier **Diffie-Hellman** protocol which uses modular exponentiation and whose security is based on the difficulty of solving the discrete logarithm problem. For more details on the classical Diffie-Hellman protocol see here. ECDH is faster and considered more secure than classical Diffie-Hellman and therefore ECDH should generally be preferred for modern applications. It is usually used as one step in a larger protocol to establish a shared secret to be used in subsequent operations as a symmetric encryption key.

From a high level, Diffie-Hellman works as follows (for both classical Diffie-Hellman & ECDH). Both parties in the key exchange do the following steps:

**Generate a new private key**– This key must be kept secret and never shared.**Compute a new public key from the private key**– There is a special mathematical relationship between the public key and private key which is based on the chosen key agreement algorithm.**Share the public key with the peer**– This peer could be a client or server.**Receive the peer’s public key and validate**– The peer’s public key needs to be validated for correctness before using.**Use our private key and the peer’s validated public key to produce the shared secret**– Due to the mathematics of the protocol our private key combined with our peers public key is equal to our peers private key combined with our public key. To understand the details of why this works you will need to read up on the mathematics of DH/ECDH but I won’t be covering that in this post.

- ECDH prevents a passive man-in-the-middle (MITM) attacker from being able to obtain the shared secret.
- ECDH by default does not protect against an active MITM attacker from impersonating the peer during the key exchange, so additional authentication mechanisms are needed to authenticate each peer if we need to protect against an active MITM attacker. For example, we could have the server generate a long term public/private key pair and then have the client include a known copy of the public key. During the key exchange the client can check that the public key matches what it expects. This is called an authenticated key exchange. If both sides perform the same form of authentication to the peer than it is called a mutually authenticated key exchange.
- The keys used on either side of a ECDH key exchange can be static (long term) or ephemeral and each configuration has pros and cons. The combinations are listed below with their pros and cons:
- Ephemeral to ephemeral – Most common scenario for key agreement. Provides forward secrecy but no authenticity on either side of the exchange.
- Static to static – Generates a long term shared secret so provides no forward secrecy. Provides authenticity on both sides.
- Ephemeral to static – Provides no forward secrecy. Static side can be authenticated by the ephemeral side.

- The current minimum recommended key length for classical Diffie-Hellman is 2,048-bits and for ECDH is 256-bits. Applications with higher security requirements can use DH keys with 3072-bits or ECDH keys with 384-bits.
- ECDH can be used with a selection of standardised elliptic curves. Ring supports three curves that are considered secure and safe to use; P-256 and P-384 by NIST and X25519 by Daniel J. Bernstein. X25519 is a good default if you are unsure which to use.
- As recommended in RFC 7748 you should apply a KDF to the computed shared secret before using as a session key or other in a real application.

The `ring::agreement`

module contains the following types and functions:

**struct Algorithm –** The type of ECDH algorithm. `ECDH_P256`

, `ECDH_P384`

and `X25519`

algorithms are supported.

**struct EphemeralPrivateKey –** An ephemeral private key for use (only) with

`agree_ephemeral`

. The signature of `agree_ephemeral`

ensures that an `EphemeralPrivateKey`

can be used for at most one key agreement.**struct PublicKey –** A public key for key agreement.

**struct UnparsedPublicKey –** An unparsed, possibly malformed, public key for key agreement.

**fn agree_ephemeral –** Performs a key agreement with an ephemeral private key and the given public key.

```
pub fn agree_ephemeral<B: AsRef<[u8]>, R>(
my_private_key: EphemeralPrivateKey,
peer_public_key: &UnparsedPublicKey<B>,
kdf: impl FnOnce(&[u8]) -> R,
) -> Result<R, error::Unspecified>
```

See the `ring::agreement`

module documentation here.

Let’s start by importing the required types into our project.

```
use ring::error::Unspecified;
use ring::rand::SystemRandom;
use ring::agreement::Algorithm;
use ring::agreement::ECDH_P256;
use ring::agreement::ECDH_P384;
use ring::agreement::X25519;
use ring::agreement::EphemeralPrivateKey;
use ring::agreement::PublicKey;
use ring::agreement::UnparsedPublicKey;
use ring::agreement::agree_ephemeral;
```

Here we start by creating an instance of SystemRandom which is used as a source of entropy for generating the private key. We then select the key agreement algorithm to use, in this case we are using `X25519`

. We then call `EphemeralPrivateKey::generate`

to generate the private key and then we compute the public key from the private key using `EphemeralPrivateKey::compute_public_key`

.

```
// Use a rand::SystemRandom as the source of entropy
let rng = SystemRandom::new();
// Select a key agreement algorithm. All agreement algorithms follow the same flow
let alg: &Algorithm = &X25519;
// Generate a private key and public key
let my_private_key: EphemeralPrivateKey = EphemeralPrivateKey::generate(alg, &rng)?;
let my_public_key: PublicKey = my_private_key.compute_public_key()?;
// The EphemeralPrivateKey type doesn't allow us to directly access the private key as designed
println!("my_public_key = {}", hex::encode(my_public_key.as_ref()));
```

In this first example we are simply simulating sending and receiving the public keys with a peer, just to show the steps. We then create an instance of UnparsedPublicKey which will be used to parse and validate the public key later.

```
// Send our public key to the peer here
// Simulate receiving a public key from the peer
let peer_public_key: PublicKey = {
let peer_private_key = EphemeralPrivateKey::generate(alg, &rng)?;
peer_private_key.compute_public_key()?
};
println!("peer_public_key = {}", hex::encode(peer_public_key.as_ref()));
// The peer public key needs to be parsed before use so wrap it creating as an instance of UnparsedPublicKey
let peer_public_key = UnparsedPublicKey::new(alg, peer_public_key);
```

Then the final step is to run the `agree_ephemeral`

function which takes our private key, the peer’s public key, the error to return on failure and a lambda which gets called with the computed shared secret passed as a parameter. Note that RFC 7748 recommends applying a KDF to the shared secret before usage.

```
// run ECDH to agree on a shared secret
agree_ephemeral(my_private_key,
&peer_public_key,
|shared_secret: &[u8]| { // the result of the key agreement is passed to this lambda
println!("shared_secret = {}", hex::encode(shared_secret.as_ref())); // don't print this in production
// As recommended in RFC 7748 we should apply a KDF on the key material here before using in a real application
// We can return the derived key from the kdf here, otherwise we just return () if the key isn't needed outside this scope
})
```

Running the code gives the following output:

```
my_public_key = 0b226d3e97b342d800a681cafbb79f0a151122562a306983f48e4841ea8dc714
peer_public_key = df5e6313fec0909914c686b63578b9aeee13ab3f49300c5e692bf3a204ed2475
shared_secret = cb210ba8b6909dd7a4d9c5a0c5e28329f142780cdfb556594e55efe390deb735
```

In this second example we start a tcp server in another thread and then connect to it as a tcp client in the main thread. The call to `server.join`

blocks the main thread until the child thread completes. The `ecdh_x25519`

function runs the full ECDH protocol as described above except the public keys are exchanged over the tcp stream after the connection is established.

```
use std::thread;
use std::net::{TcpListener, TcpStream};
use std::io::{Read, Write};
fn main() {
let server = thread::spawn(move || {
let listener = TcpListener::bind("127.0.0.1:8080").unwrap();
let mut stream = listener.accept().unwrap().0;
ecdh_x25519("server", &mut stream).unwrap();
});
let mut stream = TcpStream::connect("127.0.0.1:8080").unwrap();
ecdh_x25519("client", &mut stream).unwrap();
server.join().unwrap();
}
```

Running the code gives the following output. As you can see the `server_shared_secret`

and `server_shared_secret`

values match as expected:

```
client_public_key = 648185523469cf6678912c9e9b24b243851b2137ed065729621e4ecba4ef1564
server_public_key = c7a92fe60880181d7c11ac688c98be671dff7892cfc51de10563be41f48d264d
server_peer_public_key = 648185523469cf6678912c9e9b24b243851b2137ed065729621e4ecba4ef1564
client_peer_public_key = c7a92fe60880181d7c11ac688c98be671dff7892cfc51de10563be41f48d264d
server_shared_secret = 3ab034715237b6d34ec14b3f65c66418cd715284febf3f9102553ec8f5aa8a4d
client_shared_secret = 3ab034715237b6d34ec14b3f65c66418cd715284febf3f9102553ec8f5aa8a4d
```

Here is the full code sample for reference. Note that this example is not production ready code due to the usage of calls to `unwrap`

and the limited error checking in the code that sends and receives the public keys over the TCP streams.

```
use ring::error::Unspecified;
use ring::rand::SystemRandom;
use ring::agreement::Algorithm;
use ring::agreement::ECDH_P256;
use ring::agreement::ECDH_P384;
use ring::agreement::X25519;
use ring::agreement::EphemeralPrivateKey;
use ring::agreement::PublicKey;
use ring::agreement::UnparsedPublicKey;
use ring::agreement::agree_ephemeral;
use std::thread;
use std::net::{TcpListener, TcpStream};
use std::io::{Read, Write};
fn main() {
let server = thread::spawn(move || {
let listener = TcpListener::bind("127.0.0.1:8080").unwrap();
let mut stream = listener.accept().unwrap().0;
ecdh_x25519("server", &mut stream).unwrap();
});
let mut stream = TcpStream::connect("127.0.0.1:8080").unwrap();
ecdh_x25519("client", &mut stream).unwrap();
server.join().unwrap();
}
fn ecdh_x25519(actor: &str, stream: &mut TcpStream) -> Result<(), Unspecified> {
// Use a rand::SystemRandom as the source of entropy
let rng = SystemRandom::new();
// Select a key agreement algorithm. All agreement algorithms follow the same flow
let alg: &Algorithm = &X25519;
// Generate a private key and public key
let my_private_key: EphemeralPrivateKey = EphemeralPrivateKey::generate(alg, &rng)?;
let my_public_key: PublicKey = my_private_key.compute_public_key()?;
// The EphemeralPrivateKey doesn't allow us to directly access the private key as designed
println!("{}_public_key = {}", actor, hex::encode(my_public_key.as_ref()));
// Send our public key to the peer here
stream.write_all(my_public_key.as_ref()).unwrap();
// Simulate receiving a public key from the peer
let mut peer_public_key = [0u8; 32];
stream.read_exact(&mut peer_public_key).unwrap();
println!("{}_peer_public_key = {}", actor, hex::encode(peer_public_key.as_ref()));
// The peer public key needs to be parsed before use so wrap it creating as an instance of UnparsedPublicKey
let peer_public_key = UnparsedPublicKey::new(alg, peer_public_key);
// run ECDH to agree on a shared secret
agree_ephemeral(my_private_key,
&peer_public_key,
|shared_secret: &[u8]| { // the result of the key agreement is passed to this lambda
println!("{}_shared_secret = {}", actor, hex::encode(shared_secret.as_ref())); // don't print this in production
// As recommended in RFC 7748 we should apply a KDF on the key material here before using in a real application
// We can return the derived key from the kdf here, otherwise we just return () if the key isn't needed outside this scope
})
)
}
```

In this post I introduced the ECDH protocol, explained what it is used for and the high level algorithm which allows us to securely compute a shared secret over an insecure medium using public/private key cryptography. Then we covered how to use the `agreement`

module of the Ring cryptography library to implement ECDH in our applications by using the `EphemeralPrivateKey`

, `PublicKey`

and `UnparsedPublicKey`

types and then calling the `agree_ephemeral`

function. We went through two examples, the first a very simple example which only simulates the key exchange with the peer and the second which actually connects to the peer over a tcp connection and does the full key exchange.

The post ECDH Key Agreement in Rust using Ring appeared first on Web3 Developer.

]]>The post Introducing Fluent Hash – A Rust Hashing Library with a Fluent Interface appeared first on Web3 Developer.

]]>`digest`

module. See the Github link for the new project here.
In this post I will show you how to import the library into your Rust project and how to use the `Hashing`

, `Hash`

and `HashContext`

types for creating hashes from various data types. I also show you how to format `Hash`

values as bytes or hexadecimal using the provided convenience methods.

**Note that this library is new and has not been audited or formally verified so use at your own risk. **

From Wikipedia: ‘In software engineering, a fluent interface is an object-oriented API whose design relies extensively on method chaining. Its goal is to increase code legibility by creating a domain-specific language (DSL).’

This basically means we can make a library easier to use and make the code more readible by designing the library in a way that allows the user to chain simple method calls together. As you will see in this post, fluent-hash uses this design pattern.

The fluent-hash library contains the following types:

**enum Hashing –** The hashing algorithm. Supports selecting from the following algorithms: `Sha1`

, `Sha256`

, `Sha384`

, `Sha512`

and `Sha512_256`

. Implements convenience methods such as `hash`

, `hash_vec`

, `hash_str`

and `hash_file`

for producing digests from various data types. These convenience methods return an instance of the `Hash`

struct.

**struct Hash –** A hash value which holds the digest produced by one of the `Hashing`

algorithms. Supports formatting as a byte array, byte vector or a hexadecimal string.

**struct HashContext** – A context to be used for multi-step hash calculations. Useful when hashing a data structure with multiple fields or when hashing larger inputs.

See the full Rust library documentation here.

To get started using fluent-hash, add the library as a dependancy to your Rust Cargo.toml file.

```
[dependencies]
fluent-hash = "0.2.3"
```

Let’s start by importing the required types into our project. In this example we are importing all the fluent-hash types including the `Hashing`

enum values.

```
use fluent_hash::Hashing::Sha1;
use fluent_hash::Hashing::Sha256;
use fluent_hash::Hashing::Sha384;
use fluent_hash::Hashing::Sha512;
use fluent_hash::Hashing::Sha512_256;
use fluent_hash::Hashing;
use fluent_hash::HashContext;
use fluent_hash::Hash;
```

In this example we are using the SHA-1 hashing algorithm and hashing the bytes of the string ‘hello, world’.

`let hash: Hash = Sha1.hash(b"hello, world");`

Here we run the SHA-256 hashing algorithm on a vector of bytes from the string ‘hello, world’.

`let hash: Hash = Sha256.hash_vec(b"hello, world".to_vec());`

Here we run the SHA-384 hashing algorithm on the string ‘hello, world’.

`let hash: Hash = Sha384.hash_str("hello, world");`

In this example we create a text file containing the string ‘hello, world’ and then run it through the SHA-512 hashing algorithm.

```
let mut file = File::create("file.txt")?;
file.write_all(b"hello, world")?;
file.sync_all()?;
let hash: Hash = Sha512.hash_file("file.txt")?;
```

The `hash_file`

method also supports error handling.

```
let error: Error = match Sha512.hash_file("notfound.txt") {
Ok(_) => panic!("Expecting std::io::Error"),
Err(e) => e
};
```

Here we use the lower level `HashContext`

type to manually pass data into the hash function in chunks. This can be useful when hashing a data structure with multiple fields or when hashing larger inputs.

```
let mut ctx: HashContext = Sha512_256.new_context();
ctx.update(b"hello, world");
ctx.update(b"more data");
let hash = ctx.finish();
```

fluent-hash supports formatting hash values as bytes or hexadecimal using the convenience methods provided on the `Hash`

type.

```
let bytes: &[u8] = hash.as_bytes();
let bytes_vec: Vec<u8> = hash.to_vec();
let hex: String = hash.to_hex();
println!("bytes = {:?}", bytes);
println!("bytes_vec = {:?}", bytes_vec);
println!("hex = {}", hex);
```

The fluent interface allows you to use method chaining to easily calculate a hash for a given algorithm returning it in the desired format. The resulting code is clean and readable.

```
let result = Hashing::Sha256
.hash(b"hello, world")
.to_hex();
println!("result = {}", result);
```

Here is the full code sample for reference.

```
use std::fs::File;
use std::io::Write;
use std::io::Error;
use fluent_hash::Hashing::Sha1;
use fluent_hash::Hashing::Sha256;
use fluent_hash::Hashing::Sha384;
use fluent_hash::Hashing::Sha512;
use fluent_hash::Hashing::Sha512_256;
use fluent_hash::Hashing;
use fluent_hash::HashContext;
use fluent_hash::Hash;
fn main() -> Result<(), Error> {
// Hashing a byte array
let hash: Hash = Sha1.hash(b"hello, world");
// Hashing a byte vector
let hash: Hash = Sha256.hash_vec(b"hello, world".to_vec());
// Hashing a string
let hash: Hash = Sha384.hash_str("hello, world");
// Hashing a file
let mut file = File::create("file.txt")?;
file.write_all(b"hello, world")?;
file.sync_all()?;
let hash: Hash = Sha512.hash_file("file.txt")?;
// Hashing a file supports error handling
let error: Error = match Sha512.hash_file("notfound.txt") {
Ok(_) => panic!("Expecting std::io::Error"),
Err(e) => e
};
// Using the HashContext
let mut ctx: HashContext = Sha512_256.new_context();
ctx.update(b"hello, world");
ctx.update(b"more data");
let hash = ctx.finish();
// Format the hash
let bytes: &[u8] = hash.as_bytes();
let bytes_vec: Vec<u8> = hash.to_vec();
let hex: String = hash.to_hex();
println!("bytes = {:?}", bytes);
println!("bytes_vec = {:?}", bytes_vec);
println!("hex = {}", hex);
// Fluent interface supports method chaining
let result = Hashing::Sha256
.hash(b"hello, world")
.to_hex();
println!("result = {}", result);
Ok(())
}
```

In this post I introduced the fluent-hash library and showed how you can use it to generate SHA-1 and SHA-2 hashes by using the provided `Hashing`

, `Hash`

and `HashContext`

types. I also explained the fluent interface design pattern and showed how fluent-hash uses method chaining to provide an easy to use interface and supports creating clean and readable code.

Here are the links to the library and documentation for reference:

https://github.com/web3-developer/fluent-hash

https://crates.io/crates/fluent-hash

The post Introducing Fluent Hash – A Rust Hashing Library with a Fluent Interface appeared first on Web3 Developer.

]]>The post Password Hashing with PBKDF2 in Rust using Ring appeared first on Web3 Developer.

]]>`ring::pbkdf2`

module, we can derive a password hash and then also verify whether or not a hash matches a given password.
**Password Based Key Derivation Function 2** **(PBKDF2)** is specified in RFC 2898 Section 5.2. PBKDF2 is not a very modern algorithm but it is still considered secure as long as it is used correctly with carefully chosen parameters. PBKDF2 applies a pseudorandom function (such as a HMAC) to the input password and salt value and repeats the process a number of times to produce a derived key which can then be used as a cryptographic key in subsequent operations. The added computational work of the numerous iterations makes password cracking very difficult. PBKDF2 is also recommended by NIST and has implementations conforming to FIPS-140.

**The RFC document defines the the PBKDF2 function as follows:**

`PBKDF2 (P, S, c, dkLen) -> DK`

**These are the PBKDF2 function parameters:**

```
Options: PRF underlying pseudorandom function (hLen
denotes the length in octets of the
pseudorandom function output)
Input: P password, an octet string
S salt, an octet string
c iteration count, a positive integer
dkLen intended length in octets of the derived
key, a positive integer, at most
(2^32 - 1) * hLen
Output: DK derived key, a dkLen-octet string
```

- Weak passwords can be vulnerable to brute force attacks so users should be required to set strong passwords that are long and contain sufficient entropy.
- Using a low entropy salt can make the hash (derived key) vulnerable to pre-computation attacks using rainbow tables. The salt parameter improves the security of PBKDF2 by changing the output of the hash for any given input password. The salt should be unpredictable and be selected to be unique per user and per database (this part is sometimes called a pepper).
- Using a low number of iterations can also make the hash vulnerable to brute force attacks. The larger the number of iterations selected, the longer it takes to run the PBKDF2 function and therefore requires more work to successfully crack passwords. The number of iterations which you should select is a moving target and increases over time as cpu power increases. OWASP provides a good resource on the recommend parameters here. Currently for PBKDF2-HMAC-SHA256 the recommend iteration count is 600000 and for PBKDF2-HMAC-SHA512 it is 210000.
- The output key length needs to be long enough to prevent brute force attacks and so a minimum key length of at least 128 bits is recommend. Generally the output length should be set to be equal to the length of the chosen hash function.
- When PBKDF2 is used with a HMAC, manual pre-hashing of the password may be required in some cases to prevent certain denial of service vulnerabilities. This can occur when the input password is longer than the block size of the hash function. See here for more details.

The `ring::pbkdf2`

module contains the following types and functions:

**struct Algorithm –** The type of PBKDF2 algorithm. `PBKDF2_HMAC_SHA1`

, `PBKDF2_HMAC_SHA256`

, `PBKDF2_HMAC_SHA384`

and `PBKDF2_HMAC_SHA512`

algorithms are supported.

**fn derive –** The PBKDF2 algorithm used to derive a password hash from a given password. We pass in the chosen algorithm, the number of iterations to use, a salt and the secret (password). The hash value is stored in the given byte array, `out`

. The size of the `out`

array determines the size of the hash value returned.

```
pub fn derive(
algorithm: Algorithm,
iterations: NonZeroU32,
salt: &[u8],
secret: &[u8],
out: &mut [u8]
)
```

**fn verify –** Checks if a given secret (password) matches a previously derived hash. We pass in the algorithm, the number of iterations, the salt, the secret to check and the hash which was previously derived using the `pbkdf2::derive`

function.

```
pub fn verify(
algorithm: Algorithm,
iterations: NonZeroU32,
salt: &[u8],
secret: &[u8],
previously_derived: &[u8]
) -> Result<(), Unspecified>
```

Let’s start by importing the required types into our project.

```
use std::num::NonZeroU32;
use ring::digest::SHA256_OUTPUT_LEN;
use ring::digest::SHA512_OUTPUT_LEN;
use ring::pbkdf2;
use ring::pbkdf2::Algorithm;
use ring::pbkdf2::PBKDF2_HMAC_SHA1;
use ring::pbkdf2::PBKDF2_HMAC_SHA256;
use ring::pbkdf2::PBKDF2_HMAC_SHA384;
use ring::pbkdf2::PBKDF2_HMAC_SHA512;
```

In the code below we declare the parameters to be passed into the `pbkdf2::derive`

function. In this example we are using the `PBKDF2_HMAC_SHA256`

algorithm and so iterations is set to 600000 as recommend by OWASP. The iterations variable is a `NonZeroU32`

type from the Rust standard library which prevents setting an iteration count of zero.

```
const PBKDF2_HMAC_SHA256_ITERATIONS: u32 = 600_000; // number recommended by OWASP for PBKDF2 with SHA256
let iterations = NonZeroU32::new(PBKDF2_HMAC_SHA256_ITERATIONS).unwrap();
let salt = b"random salt"; // this should be randomly generated, using some user specific component and database specific component
let secret = b"strong password"; // select a strong password
println!("Secret/password value: {}", hex::encode(secret)); // don't print this in production
```

Next we call the `pbkdf2::derive`

function with our chosen parameters and store the hash in a byte array. In this example the size of the output hash will be the same length as the output of SHA256 but we could make this longer if required. The length is set by choosing the size of the `password_hash`

array.

```
let mut password_hash = [0u8; SHA256_OUTPUT_LEN]; // initialise with zeros
pbkdf2::derive(PBKDF2_HMAC_SHA256, iterations, salt, secret, &mut password_hash);
println!("Password hash: {}", hex::encode(password_hash)); // don't print this in production
```

Using the same input parameters as above we can call the `pbkdf2::verify`

function to check if a given password matches the previously derived hash. If the password matches the hash then the verify function returns a `Result::Ok`

type containing a `()`

, otherwise it returns a `Result::Err`

containing `error::Unspecified`

(defined in the Ring `error`

module).

```
pbkdf2::verify(PBKDF2_HMAC_SHA256, iterations, salt, secret, &password_hash).unwrap(); // success case
pbkdf2::verify(PBKDF2_HMAC_SHA256, iterations, salt, "wrong password".as_bytes(), &password_hash).unwrap(); // failure case
```

Here is the full code sample for reference. The scenario 2 example below uses `PBKDF2_HMAC_SHA512`

with the recommended iteration count of 210000 and uses an output hash size equal to the size of SHA512.

```
use std::num::NonZeroU32;
use ring::digest::SHA256_OUTPUT_LEN;
use ring::digest::SHA512_OUTPUT_LEN;
use ring::pbkdf2;
use ring::pbkdf2::Algorithm;
use ring::pbkdf2::PBKDF2_HMAC_SHA1;
use ring::pbkdf2::PBKDF2_HMAC_SHA256;
use ring::pbkdf2::PBKDF2_HMAC_SHA384;
use ring::pbkdf2::PBKDF2_HMAC_SHA512;
fn main() {
// scenario 1 - PBKDF2_HMAC_SHA256
const PBKDF2_HMAC_SHA256_ITERATIONS: u32 = 600_000; // number recommended by OWASP for PBKDF2 with SHA256
// Prepare iterations, salt and secret
let iterations = NonZeroU32::new(PBKDF2_HMAC_SHA256_ITERATIONS).unwrap();
let salt = b"random salt"; // this should be randomly generated, using some user specific component and database specific component
let secret = b"strong password"; // select a strong password
println!("Secret/password value: {}", hex::encode(secret)); // don't print this in production
// Derive the password hash and store
let mut password_hash = [0u8; SHA256_OUTPUT_LEN]; // initialise with zeros
pbkdf2::derive(PBKDF2_HMAC_SHA256, iterations, salt, secret, &mut password_hash);
println!("Password hash: {}", hex::encode(password_hash)); // don't print this in production
// Verify whether or not a password matches the stored password hash
pbkdf2::verify(PBKDF2_HMAC_SHA256, iterations, salt, secret, &password_hash).unwrap(); // success case
//pbkdf2::verify(PBKDF2_HMAC_SHA256, iterations, salt, "wrong password".as_bytes(), &password_hash).unwrap(); // failure case
// scenario 2 - PBKDF2_HMAC_SHA512
const PBKDF2_HMAC_SHA512_ITERATIONS: u32 = 210_000; // number recommended by OWASP for PBKDF2 with SHA512
// Prepare iterations, salt and secret
let iterations = NonZeroU32::new(PBKDF2_HMAC_SHA512_ITERATIONS).unwrap();
let salt = b"random salt"; // this should be randomly generated, using some user specific component and database specific component
let secret = b"strong password"; // select a strong password
println!("Secret/password value: {}", hex::encode(secret)); // don't print this in production
// Derive the password hash and store
let mut password_hash = [0u8; SHA512_OUTPUT_LEN]; // initialise with zeros
pbkdf2::derive(PBKDF2_HMAC_SHA512, iterations, salt, secret, &mut password_hash);
println!("Password hash: {}", hex::encode(password_hash)); // don't print this in production
// Verify whether or not a password matches the stored password hash
pbkdf2::verify(PBKDF2_HMAC_SHA512, iterations, salt, secret, &password_hash).unwrap(); // success case
//pbkdf2::verify(PBKDF2_HMAC_SHA512, iterations, salt, "wrong password".as_bytes(), &password_hash).unwrap(); // failure case
}
```

In this post we introduced the PBKDF2 algorithm, explained how it works and described how to correctly choose the parameters for the PBKDF2 function. We then explained how to use the Ring `pbkdf2`

module to derive a password hash by using the `pbkdf2::derive`

function and verify a password matches a password hash using the `pbkdf2::verify`

function. The examples show two scenarios using the most commonly used PBKDF2 algorithms; `PBKDF2_HMAC_SHA256`

and `PBKDF2_HMAC_SHA512`

.

The post Password Hashing with PBKDF2 in Rust using Ring appeared first on Web3 Developer.

]]>