Criptografia em Rust: criptografia e hashes com as bibliotecas ring e sha2

Treinador Online de Python para Iniciantes

Aprenda Python facilmente sem sobrecarga teórica. Resolva tarefas práticas com verificação automática e escreva código diretamente no navegador.

Start Course

Criptografia em Rust: criptografia e hashes com as bibliotecas ring e sha2



Rust é uma linguagem de programação de sistema que garante segurança de memória e alto desempenho. Graças a essas qualidades, ela é ideal para tarefas de criptografia. Neste artigo, veremos duas bibliotecas populares: ring (para criptografia simétrica e hashing) e sha2 (para hashing puro). Você aprenderá como criptografar dados usando AES-GCM, calcular hashes SHA-256 e criar assinaturas HMAC.



Por que Rust para criptografia?

Rust elimina classes inteiras de vulnerabilidades (estouro de buffer, condições de corrida) em tempo de compilação. Bibliotecas criptográficas em Rust, como ring, são escritas em assembly e C com wrappers seguros, proporcionando velocidade comparável ao C, mas com a confiabilidade do Rust.



Configuração do projeto

Crie um novo projeto e adicione as dependências ao Cargo.toml:

[dependencies]ring = "0.17"sha2 = "0.10"hex = "0.4"  # para exibição conveniente de bytes


Criptografia simétrica: AES-GCM com ring

AES-GCM (Galois/Counter Mode) é um padrão moderno de criptografia simétrica que fornece tanto confidencialidade quanto autenticação (verificação de integridade). A biblioteca ring oferece uma API simples e segura.



Criptografando dados

Para criptografar, você precisa de: uma chave (256 bits), um nonce (96 bits) e o texto simples. O nonce deve ser único para cada criptografia com a mesma chave.

use ring::aead::{Aad, LessSafeKey, Nonce, UnboundKey, AES_256_GCM};use ring::rand::{SecureRandom, SystemRandom};

fn encrypt_aes_gcm(key: &[u8; 32], plaintext: &[u8]) -> Vec<u8> { let rng = SystemRandom::new(); let unbound_key = UnboundKey::new(&AES_256_GCM, key).unwrap(); let key = LessSafeKey::new(unbound_key);

// Geração de nonce aleatório let mut nonce_bytes = [0u8; 12]; rng.fill(&mut nonce_bytes).unwrap(); let nonce = Nonce::assume_unique_for_key(nonce_bytes);

// Criptografia com dados adicionais vazios (AAD) let mut in_out = plaintext.to_vec(); let tag = key.seal_in_place_separate_tag(nonce, Aad::empty(), &mut in_out).unwrap(); // Retorna nonce + dados criptografados + tag de autenticação let mut result = nonce_bytes.to_vec(); result.extend_from_slice(&in_out); result.extend_from_slice(tag.as_ref()); result}


Descriptografando

Para descriptografar, extraímos o nonce, a tag e o texto cifrado, e então chamamos open_in_place.

fn decrypt_aes_gcm(key: &[u8; 32], ciphertext: &[u8]) -> Option<Vec<u8>> {    if ciphertext.len() < 28 { return None; } // 12 (nonce) + 16 (tag) mínimo

let (nonce_bytes, rest) = ciphertext.split_at(12); let (encrypted, tag_bytes) = rest.split_at(rest.len() - 16); let nonce = Nonce::assume_unique_for_key(*nonce_bytes); let unbound_key = UnboundKey::new(&AES_256_GCM, key).unwrap(); let key = LessSafeKey::new(unbound_key);

let mut in_out = encrypted.to_vec(); in_out.extend_from_slice(tag_bytes); // ring espera a tag no final match key.open_in_place(nonce, Aad::empty(), &mut in_out) { Ok(plaintext) => { let plain_len = plaintext.len(); Some(plaintext[..plain_len].to_vec()) }, Err(_) => None // chave inválida ou dados corrompidos }}


Exemplo de uso

fn main() {    let key = b"0123456789abcdef0123456789abcdef"; // 32 bytes    let data = b"Mensagem secreta para Rust!";        let encrypted = encrypt_aes_gcm(key, data);    println!("Criptografado (hex): {}", hex::encode(&encrypted));        if let Some(decrypted) = decrypt_aes_gcm(key, &encrypted) {        println!("Descriptografado: {}", String::from_utf8(decrypted).unwrap())                    

Recommendations