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}