Laravel Decrypter

July 30, 2022

One of Laravel's security features is Encryption. This feature creates a convenient interface for encrypting and decrypting via OpenSSL.

The documentation on the topic Encrypting A Value, explained that developers can use encryptString function from Crypt module likes:

<?php

namespace App\Http\Controllers;

use App\Http\Controllers\Controller;
use App\Models\User;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Crypt;

class DigitalOceanTokenController extends Controller
{
    /**
     * Store a DigitalOcean API token for the user.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return \Illuminate\Http\Response
     */
    public function storeSecret(Request $request)
    {
        $request->user()->fill([
            'token' => Crypt::encryptString($request->token),
        ])->save();
    }

All the ciphertext results are encrypted with AES-256-CBC chiper. And on some occasional event, there is a requirement to decrypt the value from non-technical people who do not code.

So, I try to create laravel_decrypt. A tool written in Rust and using egui for the user interface.

egui

Egui is an easy-to-use immediate mode GUI which can be compiled and targetted into multiple platforms (Mac, Windows, Linux, and Web), example:

...
impl eframe::App for MyApp {
    fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
        egui::CentralPanel::default().show(ctx, |ui| {
            ui.heading("My egui Application");
            ui.horizontal(|ui| {
                ui.label("Your name: ");
                ui.text_edit_singleline(&mut self.name);
            });
            ui.add(egui::Slider::new(&mut self.age, 0..=120).text("age"));
            if ui.button("Click each year").clicked() {
                self.age += 1;
            }
            ui.label(format!("Hello '{}', age {}", self.name, self.age));
        });
    }
}
...

Decryption

For doing encryption aes, base64, block-modes and serde_json creates are used.

Chipertext wrapped in base64 encoding, after decoding it will output JSON object with three keys: iv, value, and mac.

pub fn parse_ciphertext(ciphertext: &str) -> Result<LaravelEncryptedData, String> {
    let payload = match base64::decode(ciphertext) {
        Ok(v) => v,
        Err(_) => return Err("failed to decode base64".to_owned()),
    };
    match serde_json::from_slice(&payload) {
        Ok(v) => Ok(v),
        Err(e) => Err(e.to_string()),
    }
}

To convert it to plain text without authentication, we need key, ciphertext, and iv.

pub fn decrypt(key: Vec<u8>, ciphertext: Vec<u8>, iv: Vec<u8>) -> Result<String, String> {
    let cipher = match Aes256Cbc::new_from_slices(&key, &iv) {
        Ok(v) => v,
        Err(e) => return Err(e.to_string()),
    };
    let data = match cipher.decrypt_vec(&ciphertext) {
        Ok(v) => v,
        Err(e) => return Err(e.to_string()),
    };
    Ok(String::from_utf8_lossy(&data).to_string())
}

For full source-code, you can visit https://github.com/sakti/laravel_decrypt.

Return to blog

footer