| Crates.io | magstripe-rs |
| lib.rs | magstripe-rs |
| version | 0.1.0 |
| created_at | 2025-08-17 17:23:36.679803+00 |
| updated_at | 2025-08-17 17:23:36.679803+00 |
| description | Magnetic stripe card data decoder library and CLI tool |
| homepage | https://github.com/Induct-ie/magstripe-rs |
| repository | https://github.com/Induct-ie/magstripe-rs |
| max_upload_size | |
| id | 1799590 |
| size | 99,300 |
A robust Rust library and CLI tool for decoding magnetic stripe card data from raw binary streams. Supports multiple track formats and encoding schemes commonly found in magnetic stripe cards.
Multiple Track Format Support
Encoding Variants
Robust Decoding
Flexible Input
Add to your Cargo.toml:
[dependencies]
magstripe-rs = "0.1.0"
use magstripe_rs::{BitStream, Decoder, Format};
// Raw magnetic stripe data
let data = vec![255, 255, 255, 151, 222, 246, 253, 190, 141, 247, 7, 127, 255, 255, 255, 255, 192];
let bit_count = 130;
// Create a bit stream
let stream = BitStream::new(&data, bit_count).unwrap();
// Set up decoder with desired formats
let formats = vec![
Format::Track2,
Format::Track2Inverted,
];
let decoder = Decoder::new(&formats);
// Decode the data
match decoder.decode(stream) {
Ok(output) => {
println!("Format: {:?}", output.format);
println!("Data: {}", output.data);
}
Err(e) => {
eprintln!("Decode error: {:?}", e);
}
}
use magstripe_rs::{Format, FormatSpec, ParityType, Decoder, BitStream};
let custom = FormatSpec {
bits_per_char: 5,
start_sentinel: Some(0b11010),
end_sentinel: Some(0b11111),
lsb_first: true,
parity: ParityType::Odd,
inverted: false,
};
let decoder = Decoder::new(&[Format::Custom(custom)]);
# Decode with automatic format detection
magstripe-decode "[255, 255, 255, 151, 222, 246, 253, 190, 141, 247, 7, 127, 255, 255, 255, 255, 192]" -b 130
# Verbose output with tracing
magstripe-decode "[255, 255, 255, 151, 222, 246, 253, 190, 141, 247, 7, 127, 255, 255, 255, 255, 192]" -b 130 -v
# Try specific formats
magstripe-decode "[255, 255, 255, 151, 222, 246, 253, 190, 141, 247, 7, 127, 255, 255, 255, 255, 192]" -b 130 -f Track2Inverted
-b, --bits <count>: Number of bits to process from the input-f, --format <format>: Specific format to try (default: tries all)-v, --verbose: Enable verbose output with tracingTrack1: Standard Track 1 (7-bit IATA)Track1Inverted: Track 1 with inverted bitsTrack2: Standard Track 2 (5-bit ABA)Track2Inverted: Track 2 with inverted bitsTrack2MSB: Track 2 with MSB-first bit orderTrack2LSB: Track 2 with LSB-first bit orderTrack2Raw: Track 2 without sentinel checkingTrack2SwappedParity: Track 2 with swapped parity bitsTrack2EvenParity: Track 2 with even parityTrack3: Standard Track 3 format% (0x05)? (0x1F)%[data]?[LRC]; (0x0B)? (0x0F);[account]=[expiry][discretionary]?[LRC]| Character | Binary (LSB) | Hex | Decimal |
|---|---|---|---|
| 0 | 00001 | 0x01 | 1 |
| 1 | 10000 | 0x10 | 16 |
| 2 | 01000 | 0x08 | 8 |
| 3 | 11001 | 0x19 | 25 |
| 4 | 00100 | 0x04 | 4 |
| 5 | 10101 | 0x15 | 21 |
| 6 | 01101 | 0x0D | 13 |
| 7 | 11100 | 0x1C | 28 |
| 8 | 00010 | 0x02 | 2 |
| 9 | 10011 | 0x13 | 19 |
| ; | 11010 | 0x1A | 26 |
| = | 10110 | 0x16 | 22 |
| ? | 11111 | 0x1F | 31 |
The decoder provides detailed error information:
pub enum DecoderError {
InvalidBitStream,
BitstreamTooShort { bit_count: usize, minimum_required: usize },
NoStartSentinel,
NoEndSentinel,
ParityError { position: usize },
LrcCheckFailed,
NoFormatsProvided,
NoValidFormat { attempted: usize },
InvalidCharacterValue { value: u8, position: usize },
}
The library includes comprehensive tests for various card formats:
# Run all tests
cargo test
# Run with verbose output
RUST_LOG=trace cargo test
# Run specific test
cargo test test_track2_inverted_decode
use magstripe_rs::{BitStream, Decoder, Format};
// Example: Card that reads as inverted Track 2
let data = vec![255, 255, 255, 151, 222, 246, 253, 190, 141, 247, 7, 127, 255, 255, 255, 255, 192];
let stream = BitStream::new(&data, 130).unwrap();
let decoder = Decoder::new(&[Format::Track2Inverted]);
let output = decoder.decode(stream).unwrap();
assert_eq!(output.data, "0004048712");
use magstripe_rs::{BitStream, Decoder, Format, DecoderError};
# let data = vec![255, 255, 255, 151, 222, 246, 253, 190, 141, 247, 7, 127, 255, 255, 255, 255, 192];
# let stream = BitStream::new(&data, 130).unwrap();
// Try all common formats
let formats = vec![
Format::Track1,
Format::Track1Inverted,
Format::Track2,
Format::Track2Inverted,
Format::Track2MSB,
Format::Track3,
];
let decoder = Decoder::new(&formats);
match decoder.decode(stream) {
Ok(output) => println!("Success with format: {:?}", output.format),
Err(DecoderError::NoValidFormat { attempted }) => {
println!("Tried {} formats, none worked", attempted);
}
Err(e) => println!("Other error: {:?}", e),
}
Contributions are welcome! Please feel free to submit pull requests or open issues for bugs and feature requests.
This project is licensed under the Mozilla Public License Version 2.0 - see the LICENSE file for details.
This library is designed for educational and legitimate testing purposes only. Always ensure you have proper authorization before reading or decoding magnetic stripe cards.