#playing #cards #entropy #rng #derive #signing-key

cardseed

Pseudo-random numbers derived from playing cards using PBKDF2

2 releases

0.0.2 Sep 8, 2023
0.0.1 Sep 8, 2023

#1079 in Cryptography

Unlicense

23KB
477 lines

cardseed

The cardseed crate provides parsing and serializing of playing cards.

In cryptographic contexts, cards can be used to encode and generate entropy. A deck of 52 randomly shuffled playing cards encodes $52! \approx 2^{228}$ different possible combinations. The cardseed crate makes it easy to parse and convert cards shuffled in the real world into secure pseudo-random data which can be used to derive encryption or signing keys, seed random number generators, or other such mischief.

use cardseed::Deck;
use std::error::Error;

fn main() -> Result<(), Box<dyn Error>> {
    let deck = "AS 3H KC 3C".parse::<Deck>()?;
    let secret = deck.hash(Some("bestpasswordever"))?;
    let expected = [35, 20, 205, 7, 35, 104, 123, 150, 57, 148, 101,
                    109, 151, 0, 87, 15, 103, 14, 67, 214, 165, 165,
                    44, 218, 5, 232, 30, 26, 100, 90, 169, 244];
    assert_eq!(secret, expected);
    Ok(())
}

Cards

Cards are composed of two fields: a value and a suit. The value field is a u32 corresponding to the face value of the card, minus 1 (since we index from zero). The suit field is a cardseed::Suit enum member, corresponding to one of the four playing card suits.

When serialized, a Card is represented as a string with two characters. The first character represents the face value of the card, and the second represents the suit. For instance, "7H" is the seven of hearts.

Face Values

Character Card Value
A Ace 0
2 2 1
3 3 2
4 4 3
5 5 4
6 6 5
7 7 6
8 8 7
9 9 8
T Ten 9
J Jack 10
Q Queen 11
K King 12

Suits

Character Suit Value
S Spades 0
C Clubs 1
H Hearts 2
D Diamonds 3

Any card in a standard deck can be represented uniquely as any u32 from 0 to 51, by multiplying the suit field's value by 13, and adding the card's value field.

use cardseed::{Card, Suit};

let card = Card {
    suit: Suit::Clubs,
    value: 4,
};
assert_eq!(u32::from(card), 17);

For instance, the card "TD", the ten of diamonds, could be represented as the integer $(3 \cdot 13) + 9 = 48$.

Decks

A Deck is a vector of any number of cards. A Deck can be serialized and parsed as a string of serialized Cards, each Card string separated by whitespace.

use cardseed::{Deck, Card, Suit};
use std::vec::Vec;

let deck = Deck {
    cards: vec![
        Card {
            suit: Suit::Clubs,
            value: 10,
        },
        Card {
            suit: Suit::Hearts,
            value: 12,
        },
        Card {
            suit: Suit::Diamonds,
            value: 1,
        },
    ],
};

let deck_str = format!("{}", deck);
assert_eq!(deck_str, "JC KH 2D");

let parsed_deck = match deck_str.parse::<Deck>() {
    Ok(deck) => deck,
    Err(e) => panic!("failed to parse deck: {}", e),
};
assert_eq!(parsed_deck, deck);

Dependencies

~1MB
~16K SLoC