Skip to main content

license_key/
lib.rs

1/*!
2A library for generating and verifying license keys without requiring
3an Internet connection. For further protection, you can of course
4validate the license key over the Internet.
5
6# Features
7
8* Does not require an Internet connection.
9* Easy to revoke specific license keys in a software update.
10* Not possible to disassemble an application to gain
11  insight into how to generate a 100% working key since
12  the verification process doesn't check the whole license key.
13
14For more information, read [`Implementing a Partial Serial Number Verification System in Delphi`]
15by Brandon Staggs, which this crate was based upon.
16
17# Anatomy of a license key
18
19Every license key consists of a seed, a payload and a checksum.
20Each byte in the payload is an operation of the seed and an
21initialization vector. The 16-bit checksum is there to quickly check if
22the key is valid at all, while the seed is a 64-bit hash of something
23that identifies the license key owner such as an e-mail address or similar.
24
25The size of the payload depends on how big the initialization vector is.
26In the example below, we are using a 5-byte intitialization vector which
27results in a 5-byte payload.
28
29```text
30┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐
31│0x0│0x1│0x2│0x3│0x4│0x5│0x6│0x7│0x8│0x9│0xa│0xb│0xc│0xd│0xe│0xf│
32├───┴───┴───┴───┴───┴───┴───┴───┴───┼───┴───┴───┴───┴───┼───┴───┤
33│ SEED                              │ PAYLOAD           │ CHECK │
34│                                   │                   │  SUM  │
35└───────────────────────────────────┴───────────────────┴───────┘
36```
37
38# Generating a license key
39
40```rust
41use license_key::*;
42
43// Define a hasher that will hash the seed and a initialization vector.
44// DON'T USE THIS ONE. It's only for demonstrational purposes.
45struct DummyHasher { }
46impl KeyHasher for DummyHasher {
47    fn hash(&self, seed: u64, a: u64, b: u64, c: u64) -> u64 {
48        seed ^ a ^ b ^ c
49    }
50}
51
52// Create a license generator
53// We use only four triplets in our initialization vector,
54// but in a real world scenario you would want to use a lot more.
55let generator = Generator::new(
56    DummyHasher { },
57    vec![
58        // DON'T USE THIS ONE.
59        // Generate your own.
60        (114, 83, 170),
61        (60, 208, 27),
62        (69, 14, 202),
63        (61, 232, 54)
64     ],
65);
66
67// Generate a license key using a seed.
68// A seed is unique per license key, and could be a hash of an e-mail address or similar.
69// You can later block individual seeds during verification.
70let key = generator.generate(1234567891011121314_u64);
71
72// Write the key in hex format to the console.
73// This will output something like: 112210F4B2D230A229552341B2E723
74eprintln!("{}", key.serialize::<HexFormat>());
75
76```
77
78# Verifying a license key
79
80```rust
81use license_key::*;
82
83// Use the exact same hasher that we used when generating the key
84struct DummyHasher { }
85impl KeyHasher for DummyHasher {
86    fn hash(&self, seed: u64, a: u64, b: u64, c: u64) -> u64 {
87        seed ^ a ^ b ^ c
88    }
89}
90
91// Create the license key verifier
92let mut verifier = Verifier::new(
93    DummyHasher { },
94    vec![
95        // Use the first byte (zero indexed) from the initialization vector.
96        // If a third-party key generator is created for the app, simply change this
97        // to another byte and any forged keys won't work anymore.
98        ByteCheck::new(0, (114, 83, 170)),
99    ],
100);
101
102// Block a specific seed.
103// You might want to do this if a key was leaked or the the
104// license key owner requested a refund.
105verifier.block(11111111_u64);
106
107// Parse a key in hex format
108let key = LicenseKey::parse::<HexFormat>("112210F4B2D230A2112210F4B2D23029112210F4B2D23055112210F4B2D23023112210F4B2D230419DDA").unwrap();
109
110// Verify the license key
111match verifier.verify(&key) {
112    Status::Valid => println!("Key is valid!"),
113    Status::Invalid => println!("Key is invalid!"),
114    Status::Blocked => println!("Key has been blocked!"),
115    Status::Forged => println!("Key has been forged!"),
116}
117```
118
119[`Implementing a Partial Serial Number Verification System in Delphi`]:
120https://www.brandonstaggs.com/2007/07/26/implementing-a-partial-serial-number-verification-system-in-delphi
121*/
122
123use std::{convert::TryInto, error::Error};
124
125use hex::FromHexError;
126
127const SEED_BYTE_LENGTH: u8 = 8;
128const CHECKSUM_BYTE_LENGTH: u8 = 2;
129const SEGMENT_BYTE_LENGTH: u8 = 8;
130
131/// Represent a hasher that turns the seed and a part of the
132/// initialization vector into a license key byte.
133pub trait KeyHasher {
134    fn hash(&self, seed: u64, a: u64, b: u64, c: u64) -> u64;
135}
136
137/// Represents a license key serializer.
138pub trait LicenseSD {
139    /// Serializes a license key to a string.
140    fn serialize(key: &LicenseKey) -> String;
141
142    /// Deserializes a license key into a byte vector.
143    fn deserialize(input: &str) -> Result<Vec<u8>, impl Error>;
144}
145
146/// License key serializer/deserializer for hex strings.
147pub struct HexFormat {}
148impl LicenseSD for HexFormat {
149    fn serialize(key: &LicenseKey) -> String {
150        hex::encode_upper(key.get_bytes())
151    }
152
153    fn deserialize(input: &str) -> Result<Vec<u8>, FromHexError> {
154        hex::decode(input)
155    }
156}
157
158#[cfg(feature = "base2048")]
159pub struct Base2048Format{
160
161}
162#[cfg(feature = "base2048")]
163impl LicenseSD for Base2048Format{
164    fn serialize(key: &LicenseKey) -> String {
165        base2048::encode(&key.get_bytes())
166    }
167
168    fn deserialize(input: &str) -> Result<Vec<u8>, impl Error> {
169        match base2048::decode(input){
170            Some(a) => Ok(a),
171            None => Err(B2048Error)
172        }
173    }
174}
175#[cfg(feature = "base2048")]
176#[derive(thiserror::Error, Debug)]
177#[error("failed to decode base2048")]
178struct B2048Error;
179
180/// Represents a generated or parsed license key.
181#[derive(Debug, Clone)]
182pub struct LicenseKey {
183    bytes: Vec<u8>,
184}
185
186impl LicenseKey {
187    pub(crate) fn new(bytes: Vec<u8>) -> Self {
188        Self { bytes }
189    }
190
191    /// Deserializes a [`&str`] into a license key by using the
192    /// provided [`Serializer`].
193    ///
194    /// [`&str`]: https://doc.rust-lang.org/std/primitive.str.html
195    /// [`Serializer`]: trait.Serializer.html
196    pub fn parse<'a,T: LicenseSD + 'a>(input: &'a str) -> Result<LicenseKey, impl Error + 'a> {
197        Ok(LicenseKey::new(match T::deserialize(input){
198            Ok(a) => a,
199            Err(e) => return Err(e)
200        }))
201    }
202
203    /// Serializes the license key into a [`String`] by using the
204    /// provided [`Serializer`].
205    ///
206    /// [`String`]: https://doc.rust-lang.org/std/string/struct.String.html
207    /// [`Serializer`]: trait.Serializer.html
208    pub fn serialize<T: LicenseSD>(&self) -> String {
209        T::serialize(&self)
210    }
211
212    /// Gets the individual bytes that makes up the license key.
213    pub fn get_bytes(&self) -> Vec<u8> {
214        self.bytes.clone()
215    }
216
217    pub(crate) fn get_byte(&self, ordinal: usize) -> Option<u64> {
218        let index = SEED_BYTE_LENGTH as usize + (ordinal * SEGMENT_BYTE_LENGTH as usize);
219        if index > self.bytes.len() - 3 {
220            return None;
221        }
222        Some(u64::from_be_bytes(
223            self.bytes[index..][..8].try_into().ok()?,
224        ))
225    }
226
227    pub(crate) fn get_checksum(&self) -> &[u8] {
228        &self.bytes[self.bytes.len() - CHECKSUM_BYTE_LENGTH as usize..]
229    }
230
231    pub(crate) fn get_seed(&self) -> u64 {
232        u64::from_be_bytes(self.bytes[0..SEED_BYTE_LENGTH as usize].try_into().unwrap())
233    }
234
235    pub(crate) fn calculate_checksum(&self) -> [u8; 2] {
236        calculate_checksum(&self.bytes[0..self.bytes.len() - CHECKSUM_BYTE_LENGTH as usize])
237    }
238}
239
240/// The license key generator.
241#[derive(Debug, Clone)]
242pub struct Generator<T: KeyHasher> {
243    hasher: T,
244    iv: Vec<(u64, u64, u64)>,
245}
246
247impl<T: KeyHasher> Generator<T> {
248    /// Creates a new license key generator.
249    pub fn new(hasher: T, iv: Vec<(u64, u64, u64)>) -> Self {
250        Self { hasher, iv }
251    }
252
253    /// Creates a new license key with the specified seed.
254    pub fn generate(&self, seed: u64) -> LicenseKey {
255        // Get the license key as a byte array
256        let mut input = seed.to_be_bytes().to_vec();
257        for iv in self.iv.iter() {
258            for byte in self
259                .hasher
260                .hash(seed, iv.0, iv.1, iv.2)
261                .to_be_bytes()
262                .to_vec()
263            {
264                input.push(byte);
265            }
266        }
267
268        // Calculate the checksum for the license key
269        let checksum = calculate_checksum(&input);
270        for byte in checksum.iter() {
271            input.push(*byte);
272        }
273
274        LicenseKey::new(input)
275    }
276}
277
278/// Representation of a license key status.
279#[derive(Debug, Clone, PartialEq)]
280pub enum Status {
281    /// The license is valid.
282    Valid,
283    /// The license is invalid.
284    Invalid,
285    /// The license has been blocked.
286    Blocked,
287    /// The license has been forged.
288    Forged,
289}
290
291/// Represents a license key byte check
292/// that should be used during validation.
293#[derive(Debug, Clone)]
294pub struct ByteCheck {
295    pub ordinal: usize,
296    pub a: u64,
297    pub b: u64,
298    pub c: u64,
299}
300
301impl ByteCheck {
302    /// Creates a new byte check.
303    pub fn new(ordinal: usize, iv: (u64, u64, u64)) -> Self {
304        Self {
305            ordinal,
306            a: iv.0,
307            b: iv.1,
308            c: iv.2,
309        }
310    }
311}
312
313/// The license key verifier.
314#[derive(Debug, Clone)]
315pub struct Verifier<T: KeyHasher> {
316    hasher: T,
317    checks: Vec<ByteCheck>,
318    blocklist: Vec<u64>,
319}
320
321impl<T: KeyHasher> Verifier<T> {
322    /// Creates a new license key verifier.
323    pub fn new(hasher: T, checks: Vec<ByteCheck>) -> Self {
324        Self {
325            hasher,
326            checks,
327            blocklist: Vec::new(),
328        }
329    }
330
331    /// Blocks the specified seed from being used.
332    pub fn block(&mut self, seed: u64) {
333        self.blocklist.push(seed)
334    }
335
336    /// Perform verification on the provided license key.
337    pub fn verify(&self, key: &LicenseKey) -> Status {
338        // Validate the checksum
339        let checksum = key.calculate_checksum().to_vec();
340        if checksum != key.get_checksum() {
341            return Status::Invalid;
342        }
343
344        // Blocked key?
345        let seed = key.get_seed();
346        for blocked_seed in self.blocklist.iter() {
347            if seed == *blocked_seed {
348                return Status::Blocked;
349            }
350        }
351
352        for check in self.checks.iter() {
353            match key.get_byte(check.ordinal as usize) {
354                Some(value) => {
355                    if value != self.hasher.hash(seed, check.a, check.b, check.c) {
356                        // Values did not match, but the checksum
357                        // was correct, so this is a forged license key
358                        return Status::Forged;
359                    }
360                }
361                None => {
362                    // If we couldn't get the byte from the license
363                    // the license is invalid.
364                    return Status::Invalid;
365                }
366            }
367        }
368
369        Status::Valid
370    }
371}
372
373fn calculate_checksum(key: &[u8]) -> [u8; 2] {
374    let mut left = 0x56_u16;
375    let mut right = 0xAF_u16;
376
377    for byte in key.iter() {
378        right += *byte as u16;
379        if right > 0xFF {
380            right -= 0xFF;
381        }
382        left += right;
383        if left > 0xFF {
384            left -= 0xFF;
385        }
386    }
387    ((left << 8) + right).to_be_bytes()
388}
389
390#[cfg(test)]
391mod tests {
392    use super::*;
393    use crate::Generator;
394    use crate::KeyHasher;
395
396    #[derive(Default)]
397    pub struct TestHasher {}
398    impl KeyHasher for TestHasher {
399        fn hash(&self, seed: u64, a: u64, b: u64, c: u64) -> u64 {
400            seed ^ a ^ b ^ c
401        }
402    }
403
404    pub fn generate_key(seed: u64) -> LicenseKey {
405        let generator = Generator::new(
406            TestHasher::default(),
407            vec![(114, 83, 170), (60, 208, 27), (69, 14, 202), (61, 232, 54)],
408        );
409        generator.generate(seed)
410    }
411
412    pub fn create_verifier() -> Verifier<TestHasher> {
413        Verifier::new(
414            TestHasher::default(),
415            vec![
416                ByteCheck::new(0, (114, 83, 170)),
417                ByteCheck::new(2, (69, 14, 202)),
418            ],
419        )
420    }
421
422    #[test]
423    pub fn valid_key_should_be_valid() {
424        // Given
425        let key = generate_key(12345);
426        let verifier = create_verifier();
427
428        // When
429        let result = verifier.verify(&key);
430
431        // Then
432        assert_eq!(Status::Valid, result);
433    }
434
435    #[test]
436    pub fn valid_but_blocked_key_should_return_error() {
437        // Given
438        let key = generate_key(12345);
439        let mut verifier = create_verifier();
440        verifier.block(12345);
441
442        // When
443        let result = verifier.verify(&key);
444
445        // Then
446        assert_eq!(Status::Blocked, result);
447    }
448}