Generar claves aleatorias

Al final, nuestro generador de PINs de 6 dígitos, para un intruso es imprevisible, pero no tan impredecible. Hay que comprender sus límites: su naturaleza numérica y su límite de longitud fija y corta lo hacen adivinable sí o sí, como todos los PINs, con suficiente tiempo y recursos - aunque hayamos puesto nuestro empeño en que no sea una puerta abierta. A veces, una puerta de cristal, si está cerrada evita la entrada de muchos, a pesar de no estar blindada.

En cualquier caso, ahí queda nuestra puerta de cristal: algo tintada, con cerradura. No es inexpugnable, pero a más de un intruso le costará un rato abrirla.

Y ese rato - si se gestiona bien sí puede blindar hasta esa puerta de cristal: introduciendo demoras entre intentos de autenticación, notificando sobre ello, o incluso limitando un número máximo de intentos por dispositivo que se conecta.

Aquí llega la tarea pendiente de la creación de las claves de 128 bits - eso  son 16 bytes - o como se escribe en Zig: [16]u8.

Las claves las creamos en tiempo de ejecución:

getkey.zig

const std = @import("std");

const print = std.debug.print;

pub fn get_key(o_alloc: std.mem.Allocator, n_size: usize) ![]const u8 {

    const s_key = try o_alloc.alloc(u8, n_size);

    // genera bytes hasta llenar el buffer s_key

    std.crypto.random.bytes(s_key);

    return s_key;

}

pub fn main() !void {

    const gpa = std.heap.page_allocator;

    const s_key = try get_key(gpa, 16);

    for (s_key) |c_x| print("{X} ", .{c_x});

    print("\n", .{});

}

$ zig run getkey.zig

D3 F5 3B 59 96 BC 29 AF 24 EE 1E 85 61 DF 7 60

Hemos creado una clave de 16 bytes y cada vez que ejecutes el programa te imprimirá una distinta.

Usamos una función de la librería estándar de Zig para generar los números aleatorios:

  // genera bytes hasta llenar el buffer s_key

  std.crypto.random.bytes(s_key);

También, hay un detalle que te puede haber llamado la atención. A pesar de solicitar la memoria con el allocator, nunca la liberamos de forma manual. Podríamos hacerlo, pero en realidad no es necesario en este caso. El programa que estamos ejecutando es totalmente lineal. La poca memoria que asignamos, a pesar de suponer una leak (fuga de memoria) será liberada al terminar main, sí o sí. En un “script” de prueba tan corto no es necesario pero cuidado - muchos “scripts de prueba” evolucionan o son retomados al día o a la semana siguiente y terminan siendo parte de algo más serio. Así que - efectivamente, igual no es tan mala idea incluir siempre el free, por si las moscas.

El jugador después de testear el programa lo ejecuta sobre la terminal de la IA primigenia:

genvac.zig

const std = @import("std");

const print = std.debug.print;

// obtener un pin para un índice

fn get_pin4index(n_x: usize) usize {

    const n_a = n_x + 1;

    // formula de números pentagonales

    return (n_a * (3 * n_a - 1)) / 2 % 1_000_000;

}

// convertir el número a una cadena rellena con ceros a la izquierda

fn left_pad_zeros(n_a: usize, n_len: usize) [n_len]u8 {

    comptime var s_resp: [n_len]u8 = undefined;

    for (0..n_len) |n_ix| {

        s_resp[n_ix] = '0';

    }

    var n_x: usize = n_a;

    if (n_x == 0) return s_resp;

    for (0..s_resp.len - 1) |n_i| {

        const n_pos = s_resp.len - 1 - n_i;

        const n_digit = n_x % 10;

        s_resp[n_pos] = @as(u8, '0' + n_digit);

        n_x = n_x / 10;

    }

    return s_resp;

}

fn get_permuted_index(n_i: usize, n_items: usize) usize {

    // Semilla simple determinista

    return (n_i * 524287) % n_items;

}

pub fn get_key(o_alloc: std.mem.Allocator, n_size: usize) ![]const u8 {

    const s_key = try o_alloc.alloc(u8, n_size);

    // genera bytes hasta llenar el buffer s_key

    std.crypto.random.bytes(s_key);

    return s_key;

}

// declaramos la longitud del PIN máxima del PIN

const N_PIN_LENGTH = 6;

const N_START = 100;

// declaramos el total de PINs creados

const N_TOTAL_PINS = 1000;

// longitud key en bytes

const N_KEY_LENGTH = 16;

pub fn main() !void {

    comptime var a_pins: [N_TOTAL_PINS][N_PIN_LENGTH]u8 = undefined;

    comptime {

        // añadimos 1000 más por la llamada a get_permuted_index

        @setEvalBranchQuota(15000);

        for (0..a_pins.len) |n_i| {

            // obtenemos el pin en función del indice

            const n_pin = get_pin4index(n_i + N_START);

            // obtenemos una nueva posición en el array

            const n_pos = get_permuted_index(n_i, N_TOTAL_PINS);

            // convertirmos el pin a cadena rellena con ceros por la izquierda

            a_pins[n_pos] = left_pad_zeros(n_pin, N_PIN_LENGTH);

        }

    }

    const gpa = std.heap.page_allocator;

    // imprimimos los primeros 5 y los últimos 5

    // PIN y claves para comprobar el resultado

    for (a_pins[0..5].*, 0..) |s_pin, n_i| {

        const s_key = try get_key(gpa, N_KEY_LENGTH);

        defer gpa.free(s_key);

        print("{}:{s}:{X}", .{ n_i + 1, s_pin, s_key });

        print("\n", .{});

    }

    print("...\n", .{});

    const n_last = a_pins.len;

    for (a_pins[n_last - 5 .. n_last].*, n_last - 5..) |s_pin, n_i| {

        const s_key = try get_key(gpa, N_KEY_LENGTH);

        defer gpa.free(s_key);

        print("{}:{s}:{X}", .{ n_i + 1, s_pin, s_key });

        print("\n", .{});

    }

}

$ zig run genvac.zig

1:015251:81F9F4BF7C7F2297A5CEDAF2CCA1A53D

2:057302:11F940ABAAB81B5E2757703E5B655B9F

3:048540:8499E016EF4C0FBEFE8C939789353EEB

4:088965:13DA40F342C781229A4F32314FA3392E

5:078577:1E72EAC88941085666357B2AAE453842

...

996:057801:3D22AD46BEBABC4A5A2CB24BE27BA80A

997:065417:76964ED5F087DFA7DE9DBC952B248D39

998:079720:9BA1B4BC56B743F8A2C8ACF26ABDF853

999:043210:80893DBA936F860AE454C6365CC5589A

1000:055887:FE35AFFE7A4F183C0334903663E4FDBE

La IA procesa los datos de los PINs y las claves generadas para cada uno de los robots. Los interpola con los comandos que sobrescriben las configuraciones actuales que llevaron al desastre.

Inmediatamente después, inicia la restauración de la copia de seguridad del sistema global. El resto… es historia de un futuro no determinista.


Generar cadenas
Enums, Structs, Errores
© 2025 Zen of Zig