COLDCARD RNG incident the public record, collected and explained
Informational only, and this site never asks for your seed words. details

Informational only. This is an open source collection of what others have published about the incident, together with an explanation of it. It is not financial, security or legal advice, and not a substitute for professional advice about your own situation. It is not affiliated with, endorsed by, or speaking for Coinkite. Material is attributed and quoted as published; where sources disagree their scenarios are kept separate with their assumptions rather than reconciled into one answer. Everything is meant to be checked against the linked evidence rather than taken on trust. Act on your own judgement about a particular situation. Editorial standards and corrections.

Do not disclose recovery material to a website, form, message or support account. This site never asks for it, and contributions containing recovery words or private keys are not accepted.

libngu random.c

libngu-random-c

https://raw.githubusercontent.com/switck/libngu/master/ngu/random.c

Latest reviewed change

source content difference between and

The random.c implementation was refactored behind random_backend.h, replacing the platform-specific CHIP_TRNG macros with a checked chip_trng_read helper, and corrected the bit-length and mask calculations used during range reduction.

seen +22 -57 full history below
 //
-// random - RNG stuff
+// random - cryptographic random number generation
 //
-// - common interface to TRNG specific to your chip
-// - whitening
-// - pick new privkeys
+// Cifra Hash_DRBG is seeded from the target entropy source. Generated bytes

First lines only. The complete diff is in the timeline below.

Organisation
switck
Evidence role
Repository file
Published
continuously updated
Source changes
4
Detected differences
4
Unreviewed
0
Copies held
5

The #ifndef guard and the generator. On 5 Aug 2026 upstream merged #61, replacing Yasmarang with a SHA-256 Hash-DRBG and rejecting reseeds under 32 bytes; the guard's presence test is unchanged. Still tracked for any further upstream revision, including the open value-check proposal #58.

Every check is recorded, including checks that found no text change. A detected edit is therefore bounded between two checks. The publisher's exact save time is not observable from this record. Last checked .

  1. source content difference between and Current source content +22 -57

    The random.c implementation was refactored behind random_backend.h, replacing the platform-specific CHIP_TRNG macros with a checked chip_trng_read helper, and corrected the bit-length and mask calculations used during range reduction.

    seen · Captured here 4,544 chars
    What changed from the previous capture 79 lines
     //
    -// random - RNG stuff
    +// random - cryptographic random number generation
     //
    -// - common interface to TRNG specific to your chip
    -// - whitening
    -// - pick new privkeys
    +// Cifra Hash_DRBG is seeded from the target entropy source. Generated bytes
    +// are also XORed with fresh source words after basic source-failure checks.
     //
     #include "py/runtime.h"
     #include "py/mperrno.h"
    -#include <string.h>
    -#include <stdlib.h>
    -#include <stdio.h>
     #include "my_assert.h"
     #include "cifra/drbg.h"
     #include "cifra/ext/handy.h"
    -// ESP32 code
    -#ifdef ESP_PLATFORM
    -# include "esp_system.h"
    -# define CHIP_TRNG_SETUP()
    -# define CHIP_TRNG_32()         esp_random()
    -#endif
    -#ifdef MICROPY_PY_STM
    -// ports/stm32/rng.c
    -extern uint32_t rng_get(void);
    -# define CHIP_TRNG_SETUP()
    -# define CHIP_TRNG_32()         rng_get()
    -# if MICROPY_HW_ENABLE_RNG == 0 && NGU_STM32_EXTERNAL_RNG_GET != 1
    -# error "get a HW TRNG plz"
    -# endif
    -#endif
    -#if defined(__APPLE__) || defined(__FreeBSD__)
    -# define CHIP_TRNG_SETUP()
    -# define CHIP_TRNG_32()         arc4random()
    -#endif
    -#ifdef __linux__
    -# include <sys/random.h>
    -// glibc random() is not a TRNG and is typically unseeded here — never use for keys.
    -static uint32_t linux_trng_32(void)
    -{
    -    uint32_t v = 0;
    -    ssize_t n = getrandom(&v, sizeof v, 0);
    -    if(n != (ssize_t)sizeof v) {
    -        mp_raise_OSError(MP_EFAULT);
    -    }
    -    return v;
    -}
    -# define CHIP_TRNG_SETUP()
    -# define CHIP_TRNG_32()         linux_trng_32()
    -#endif
    -#ifndef CHIP_TRNG_SETUP
    -# error "need chip TRNG function"
    -# define CHIP_TRNG_SETUP()
    -# define CHIP_TRNG_32()         0x5a5a5a5a
    -#endif
    +#include "random_backend.h"
     static cf_hash_drbg_sha256 drbg;
     static bool drbg_ready;
     static uint32_t last_chip;
     #define DRBG_ENTROPY_WORDS 32
    +static bool checked_chip_trng_read(uint32_t *out)
    +{
    +    uint32_t chip;
    +    if(!chip_trng_read(&chip) || !chip || chip == last_chip) {
    +        return false;
    +    }
    +    last_chip = chip;
    +    *out = chip;
    +    return true;
    +}
     static uint32_t checked_chip_trng(void)
     {
    -    uint32_t chip = CHIP_TRNG_32();
    -    if(!chip || chip == last_chip) {
    -        // maybe TRNG is not clocked? Fail hard
    +    uint32_t chip;
    +    if(!checked_chip_trng_read(&chip)) {
             mp_raise_OSError(MP_EFAULT);
         }
    -    last_chip = chip;
         return chip;
     }
     static void drbg_seed_from_chip(void)
     {
    -    CHIP_TRNG_SETUP();
         uint32_t entropy[DRBG_ENTROPY_WORDS];
         for(int i = 0; i < DRBG_ENTROPY_WORDS; i++) {
    -        entropy[i] = checked_chip_trng();
    +        if(!checked_chip_trng_read(&entropy[i])) {
    +            mem_clean(entropy, sizeof entropy);
    +            mp_raise_OSError(MP_EFAULT);
    +        }
         }
         if(drbg_ready) {
             cf_hash_drbg_sha256_reseed(&drbg, entropy, sizeof(entropy), NULL, 0);
     }
     void my_random_bytes(uint8_t *dest, uint32_t count)
     {
    -    CHIP_TRNG_SETUP();
         if(!drbg_ready) {
             drbg_setup(NULL, 0);
         }
     }
     STATIC mp_obj_t random_uint32(void) {
         // full 32-bit values, not 30
    -    CHIP_TRNG_SETUP();
         uint32_t rv;
         my_random_bytes((uint8_t *)&rv, sizeof(rv));
         return mp_obj_new_int_from_uint(rv);
     int _rand_below(int mx)
     {
         if(mx <= 1) return 0;
    -    int bl = _bit_length(mx);
    +    int bl = _bit_length(mx - 1);
         assert(bl && (bl < 31));
    -    CHIP_TRNG_SETUP();
    -    uint32_t mask = (2 << bl)-1;
    +    uint32_t mask = (1u << bl) - 1;
         while(1) {
             uint32_t pt;
             my_random_bytes((uint8_t *)&pt, sizeof(pt));
    
    Extracted text as captured
    //
    // random - cryptographic random number generation
    //
    // Cifra Hash_DRBG is seeded from the target entropy source. Generated bytes
    // are also XORed with fresh source words after basic source-failure checks.
    //
    #include "py/runtime.h"
    #include "py/mperrno.h"
    #include "my_assert.h"
    #include "cifra/drbg.h"
    #include "cifra/ext/handy.h"
    #include "random_backend.h"
    static cf_hash_drbg_sha256 drbg;
    static bool drbg_ready;
    static uint32_t last_chip;
    #define DRBG_ENTROPY_WORDS 32
    static bool checked_chip_trng_read(uint32_t *out)
    {
        uint32_t chip;
        if(!chip_trng_read(&chip) || !chip || chip == last_chip) {
            return false;
        }
        last_chip = chip;
        *out = chip;
        return true;
    }
    static uint32_t checked_chip_trng(void)
    {
        uint32_t chip;
        if(!checked_chip_trng_read(&chip)) {
            mp_raise_OSError(MP_EFAULT);
        }
        return chip;
    }
    static void drbg_seed_from_chip(void)
    {
        uint32_t entropy[DRBG_ENTROPY_WORDS];
        for(int i = 0; i < DRBG_ENTROPY_WORDS; i++) {
            if(!checked_chip_trng_read(&entropy[i])) {
                mem_clean(entropy, sizeof entropy);

    Excerpt only. The complete copy is held offline and backs quotations on this site. The original publication remains the canonical public source.

  2. source content difference between and source content +1 -1

    The preprocessor guard around the RNG was narrowed from MICROPY_HW_ENABLE_RNG == 0 to MICROPY_HW_ENABLE_RNG == 0 && NGU_STM32_EXTERNAL_RNG_GET != 1.

    seen · Captured here 5,377 chars
    What changed from the previous capture 2 lines
     extern uint32_t rng_get(void);
     # define CHIP_TRNG_SETUP()
     # define CHIP_TRNG_32()         rng_get()
    -# if MICROPY_HW_ENABLE_RNG == 0
    +# if MICROPY_HW_ENABLE_RNG == 0 && NGU_STM32_EXTERNAL_RNG_GET != 1
     # error "get a HW TRNG plz"
     # endif
     #endif
    
    Extracted text as captured
    //
    // random - RNG stuff
    //
    // - common interface to TRNG specific to your chip
    // - whitening
    // - pick new privkeys
    //
    #include "py/runtime.h"
    #include "py/mperrno.h"
    #include <string.h>
    #include <stdlib.h>
    #include <stdio.h>
    #include "my_assert.h"
    #include "cifra/drbg.h"
    #include "cifra/ext/handy.h"
    // ESP32 code
    #ifdef ESP_PLATFORM
    # include "esp_system.h"
    # define CHIP_TRNG_SETUP()
    # define CHIP_TRNG_32()         esp_random()
    #endif
    #ifdef MICROPY_PY_STM
    // ports/stm32/rng.c
    extern uint32_t rng_get(void);
    # define CHIP_TRNG_SETUP()
    # define CHIP_TRNG_32()         rng_get()
    # if MICROPY_HW_ENABLE_RNG == 0 && NGU_STM32_EXTERNAL_RNG_GET != 1
    # error "get a HW TRNG plz"
    # endif
    #endif
    #if defined(__APPLE__) || defined(__FreeBSD__)
    # define CHIP_TRNG_SETUP()
    # define CHIP_TRNG_32()         arc4random()
    #endif
    #ifdef __linux__
    # include <sys/random.h>
    // glibc random() is not a TRNG and is typically unseeded here — never use for keys.
    static uint32_t linux_trng_32(void)
    {
        uint32_t v = 0;

    Excerpt only. The complete copy is held offline and backs quotations on this site. The original publication remains the canonical public source.

  3. source content difference between and source content +15 -4

    random.c on master changed with the merged HW-TRNG enforcement: build now errors unless MICROPY_HW_ENABLE_RNG, Linux uses getrandom() via a checked helper instead of random(), and random_bytes() rejects negative counts.

    seen · Captured here 5,342 chars
    What changed from the previous capture 19 lines
     extern uint32_t rng_get(void);
     # define CHIP_TRNG_SETUP()
     # define CHIP_TRNG_32()         rng_get()
    -# ifndef MICROPY_HW_ENABLE_RNG
    +# if MICROPY_HW_ENABLE_RNG == 0
     # error "get a HW TRNG plz"
     # endif
     #endif
     # define CHIP_TRNG_32()         arc4random()
     #endif
     #ifdef __linux__
    +# include <sys/random.h>
    +// glibc random() is not a TRNG and is typically unseeded here — never use for keys.
    +static uint32_t linux_trng_32(void)
    +{
    +    uint32_t v = 0;
    +    ssize_t n = getrandom(&v, sizeof v, 0);
    +    if(n != (ssize_t)sizeof v) {
    +        mp_raise_OSError(MP_EFAULT);
    +    }
    +    return v;
    +}
     # define CHIP_TRNG_SETUP()
    -# define CHIP_TRNG_32()         random()
    +# define CHIP_TRNG_32()         linux_trng_32()
     #endif
     #ifndef CHIP_TRNG_SETUP
     # error "need chip TRNG function"
     STATIC mp_obj_t random_bytes(mp_obj_t count_in)
     {
         int count = mp_obj_get_int_truncated(count_in);
    -    if(count > 4096) {
    -        mp_raise_ValueError(MP_ERROR_TEXT("too many"));
    +    if(count < 0 || count > 4096) {
    +        mp_raise_ValueError(MP_ERROR_TEXT("out of range"));
         }
         vstr_t rv;
         vstr_init_len(&rv, count);
    
    Extracted text as captured
    //
    // random - RNG stuff
    //
    // - common interface to TRNG specific to your chip
    // - whitening
    // - pick new privkeys
    //
    #include "py/runtime.h"
    #include "py/mperrno.h"
    #include <string.h>
    #include <stdlib.h>
    #include <stdio.h>
    #include "my_assert.h"
    #include "cifra/drbg.h"
    #include "cifra/ext/handy.h"
    // ESP32 code
    #ifdef ESP_PLATFORM
    # include "esp_system.h"
    # define CHIP_TRNG_SETUP()
    # define CHIP_TRNG_32()         esp_random()
    #endif
    #ifdef MICROPY_PY_STM
    // ports/stm32/rng.c
    extern uint32_t rng_get(void);
    # define CHIP_TRNG_SETUP()
    # define CHIP_TRNG_32()         rng_get()
    # if MICROPY_HW_ENABLE_RNG == 0
    # error "get a HW TRNG plz"
    # endif
    #endif
    #if defined(__APPLE__) || defined(__FreeBSD__)
    # define CHIP_TRNG_SETUP()
    # define CHIP_TRNG_32()         arc4random()
    #endif
    #ifdef __linux__
    # include <sys/random.h>
    // glibc random() is not a TRNG and is typically unseeded here — never use for keys.
    static uint32_t linux_trng_32(void)
    {
        uint32_t v = 0;

    Excerpt only. The complete copy is held offline and backs quotations on this site. The original publication remains the canonical public source.

  4. source content difference between and source content +63 -29

    The random-number implementation replaces Yasmarang with a SHA-256 Hash-DRBG seeded from 32 checked chip-TRNG words. It also rejects seed inputs shorter than 32 bytes and routes random-word generation through the new byte generator.

    seen · Captured here 5,013 chars
    What changed from the previous capture 92 lines
     #include <stdlib.h>
     #include <stdio.h>
     #include "my_assert.h"
    +#include "cifra/drbg.h"
    +#include "cifra/ext/handy.h"
     // ESP32 code
     #ifdef ESP_PLATFORM
     # include "esp_system.h"
     # define CHIP_TRNG_SETUP()
     # define CHIP_TRNG_32()         0x5a5a5a5a
     #endif
    -// Yasmarang random number generator
    -// by Ilya Levin
    -// http://www.literatecode.com/yasmarang
    -// Public Domain
    -// TODO should be marked as confidential memory
    -static uint32_t yasmarang_pad = 0x0a8ce26f, yasmarang_n = 69, yasmarang_d = 233;
    -static uint8_t yasmarang_dat = 0;
    -STATIC uint32_t my_yasmarang(void) {
    -    yasmarang_pad += yasmarang_dat + yasmarang_d * yasmarang_n;
    -    yasmarang_pad = (yasmarang_pad << 3) + (yasmarang_pad >> 29);
    -    yasmarang_n = yasmarang_pad | 2;
    -    yasmarang_d ^= (yasmarang_pad << 31) + (yasmarang_pad >> 1);
    -    yasmarang_dat ^= (char)yasmarang_pad ^ (yasmarang_d >> 8) ^ 1;
    -    return yasmarang_pad ^ (yasmarang_d << 5) ^ (yasmarang_pad >> 18) ^ (yasmarang_dat << 1);
    +static cf_hash_drbg_sha256 drbg;
    +static bool drbg_ready;
    +static uint32_t last_chip;
    +#define DRBG_ENTROPY_WORDS 32
    +static uint32_t checked_chip_trng(void)
    +{
    +    uint32_t chip = CHIP_TRNG_32();
    +    if(!chip || chip == last_chip) {
    +        // maybe TRNG is not clocked? Fail hard
    +        mp_raise_OSError(MP_EFAULT);
    +    }
    +    last_chip = chip;
    +    return chip;
    +}
    +static void drbg_seed_from_chip(void)
    +{
    +    CHIP_TRNG_SETUP();
    +    uint32_t entropy[DRBG_ENTROPY_WORDS];
    +    for(int i = 0; i < DRBG_ENTROPY_WORDS; i++) {
    +        entropy[i] = checked_chip_trng();
    +    }
    +    if(drbg_ready) {
    +        cf_hash_drbg_sha256_reseed(&drbg, entropy, sizeof(entropy), NULL, 0);
    +    } else {
    +        static const char domain[] = "libngu.random";
    +        cf_hash_drbg_sha256_init(&drbg, entropy, sizeof(entropy),
    +                                 NULL, 0, domain, sizeof(domain)-1);
    +        drbg_ready = true;
    +    }
    +    mem_clean(entropy, sizeof entropy);
    +}
    +static void drbg_setup(const void *seed, size_t seed_len)
    +{
    +    if(!drbg_ready) {
    +        drbg_seed_from_chip();
    +    }
    +    if(seed_len) {
    +        cf_hash_drbg_sha256_reseed(&drbg, seed, seed_len, NULL, 0);
    +    }
     }
     void my_random_bytes(uint8_t *dest, uint32_t count)
     {
    -    uint32_t last = 0;
    +    CHIP_TRNG_SETUP();
    +    if(!drbg_ready) {
    +        drbg_setup(NULL, 0);
    +    }
    +    if(cf_hash_drbg_sha256_needs_reseed(&drbg)) {
    +        drbg_seed_from_chip();
    +    }
    +    cf_hash_drbg_sha256_gen(&drbg, dest, count);
         while(count) {
    -        uint32_t chip = CHIP_TRNG_32();
    -        if(chip == last) {
    -            // maybe TRNG is not clocked? Fail hard
    -            mp_raise_OSError(MP_EFAULT);
    +        uint32_t chip = checked_chip_trng();
    +        int here = MIN(4, count);
    +        for(int i = 0; i < here; i++) {
    +            dest[i] ^= ((uint8_t *)&chip)[i];
             }
    -        last = chip;
    -        chip ^= my_yasmarang();
    -        int here = MIN(4, count);
    -        memcpy(dest, &chip, here);
             dest += here;
             count -= here;
         }
     STATIC mp_obj_t random_uint32(void) {
         // full 32-bit values, not 30
         CHIP_TRNG_SETUP();
    -    uint32_t rv = my_yasmarang();
    -    rv ^= CHIP_TRNG_32();
    +    uint32_t rv;
    +    my_random_bytes((uint8_t *)&rv, sizeof(rv));
         return mp_obj_new_int_from_uint(rv);
     }
     STATIC MP_DEFINE_CONST_FUN_OBJ_0(random_uint32_obj, random_uint32);
         assert(bl && (bl < 31));
         CHIP_TRNG_SETUP();
         uint32_t mask = (2 << bl)-1;
    -    uint32_t pt = my_yasmarang();
    -    pt ^= CHIP_TRNG_32();
         while(1) {
    +        uint32_t pt;
    +        my_random_bytes((uint8_t *)&pt, sizeof(pt));
             int rv = (int)(pt & mask);
             if(rv < mx) {
                 return rv;
             }
    -        pt ^= my_yasmarang();
         }
     }
     STATIC mp_obj_t random_uniform(mp_obj_t mx_in) {
     STATIC MP_DEFINE_CONST_FUN_OBJ_1(random_bytes_obj, random_bytes);
     STATIC mp_obj_t random_reseed(mp_obj_t arg)
     {
    -    yasmarang_pad = mp_obj_get_int_truncated(arg);
    +    mp_buffer_info_t seed;
    +    mp_get_buffer_raise(arg, &seed, MP_BUFFER_READ);
    +    if(seed.len < 32) {
    +        mp_raise_ValueError(MP_ERROR_TEXT("seed too short"));
    +    }
    +    drbg_setup(seed.buf, seed.len);
         return mp_const_none;
     }
     STATIC MP_DEFINE_CONST_FUN_OBJ_1(random_reseed_obj, random_reseed);
    
    Extracted text as captured
    //
    // random - RNG stuff
    //
    // - common interface to TRNG specific to your chip
    // - whitening
    // - pick new privkeys
    //
    #include "py/runtime.h"
    #include "py/mperrno.h"
    #include <string.h>
    #include <stdlib.h>
    #include <stdio.h>
    #include "my_assert.h"
    #include "cifra/drbg.h"
    #include "cifra/ext/handy.h"
    // ESP32 code
    #ifdef ESP_PLATFORM
    # include "esp_system.h"
    # define CHIP_TRNG_SETUP()
    # define CHIP_TRNG_32()         esp_random()
    #endif
    #ifdef MICROPY_PY_STM
    // ports/stm32/rng.c
    extern uint32_t rng_get(void);
    # define CHIP_TRNG_SETUP()
    # define CHIP_TRNG_32()         rng_get()
    # ifndef MICROPY_HW_ENABLE_RNG
    # error "get a HW TRNG plz"
    # endif
    #endif
    #if defined(__APPLE__) || defined(__FreeBSD__)
    # define CHIP_TRNG_SETUP()
    # define CHIP_TRNG_32()         arc4random()
    #endif
    #ifdef __linux__
    # define CHIP_TRNG_SETUP()
    # define CHIP_TRNG_32()         random()
    #endif
    #ifndef CHIP_TRNG_SETUP
    # error "need chip TRNG function"

    Excerpt only. The complete copy is held offline and backs quotations on this site. The original publication remains the canonical public source.

  5. Earliest copy held
    seen · Captured here 4,290 chars
    Extracted text as captured
    //
    // random - RNG stuff
    //
    // - common interface to TRNG specific to your chip
    // - whitening
    // - pick new privkeys
    //
    #include "py/runtime.h"
    #include "py/mperrno.h"
    #include <string.h>
    #include <stdlib.h>
    #include <stdio.h>
    #include "my_assert.h"
    // ESP32 code
    #ifdef ESP_PLATFORM
    # include "esp_system.h"
    # define CHIP_TRNG_SETUP()
    # define CHIP_TRNG_32()         esp_random()
    #endif
    #ifdef MICROPY_PY_STM
    // ports/stm32/rng.c
    extern uint32_t rng_get(void);
    # define CHIP_TRNG_SETUP()
    # define CHIP_TRNG_32()         rng_get()
    # ifndef MICROPY_HW_ENABLE_RNG
    # error "get a HW TRNG plz"
    # endif
    #endif
    #if defined(__APPLE__) || defined(__FreeBSD__)
    # define CHIP_TRNG_SETUP()
    # define CHIP_TRNG_32()         arc4random()
    #endif
    #ifdef __linux__
    # define CHIP_TRNG_SETUP()
    # define CHIP_TRNG_32()         random()
    #endif
    #ifndef CHIP_TRNG_SETUP
    # error "need chip TRNG function"
    # define CHIP_TRNG_SETUP()
    # define CHIP_TRNG_32()         0x5a5a5a5a

    Excerpt only. The complete copy is held offline and backs quotations on this site. The original publication remains the canonical public source.

How to check this yourself

The excerpts and plain unified diffs above show the text this project held and how it changed. To verify a quotation, compare it against the page itself or against the Internet Archive's copies, which are independent of this project.

Complete captures are held offline rather than mirrored here, so this page shows diffs and excerpts. If a quotation is ever disputed, the full copy can be produced. Ask.