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) -> u8 {
48 ((seed ^ a ^ b ^ c) & 0xFF) as u8
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
74println!("{}", key.serialize::<HexFormat>());
75```
76
77# Verifying a license key
78
79```rust
80use license_key::*;
81
82// Use the exact same hasher that we used when generating the key
83struct DummyHasher { }
84impl KeyHasher for DummyHasher {
85 fn hash(&self, seed: u64, a: u64, b: u64, c: u64) -> u8 {
86 ((seed ^ a ^ b ^ c) & 0xFF) as u8
87 }
88}
89
90// Create the license key verifier
91let mut verifier = Verifier::new(
92 DummyHasher { },
93 vec![
94 // Use the first byte (zero indexed) from the initialization vector.
95 // If a third-party key generator is created for the app, simply change this
96 // to another byte and any forged keys won't work anymore.
97 ByteCheck::new(0, (114, 83, 170)),
98 ],
99);
100
101// Block a specific seed.
102// You might want to do this if a key was leaked or the the
103// license key owner requested a refund.
104verifier.block(11111111_u64);
105
106// Parse a key in hex format
107let key = LicenseKey::parse::<HexFormat>("112210F4B2D230A229552341E723");
108
109// Verify the license key
110match verifier.verify(&key) {
111 Status::Valid => println!("Key is valid!"),
112 Status::Invalid => println!("Key is invalid!"),
113 Status::Blocked => println!("Key has been blocked!"),
114 Status::Forged => println!("Key has been forged!"),
115}
116```
117
118[`Implementing a Partial Serial Number Verification System in Delphi`]:
119https://www.brandonstaggs.com/2007/07/26/implementing-a-partial-serial-number-verification-system-in-delphi
120*/
121
122use std::convert::TryInto;
123
124const SEED_BYTE_LENGTH: u8 = 8;
125const CHECKSUM_BYTE_LENGTH: u8 = 2;
126const SEGMENT_BYTE_LENGTH: u8 = 1;
127
128/// Represent a hasher that turns the seed and a part of the
129/// initialization vector into a license key byte.
130pub trait KeyHasher {
131 fn hash(&self, seed: u64, a: u64, b: u64, c: u64) -> u8;
132}
133
134/// Represents a license key serializer.
135pub trait Serializer {
136 /// Serializes a license key to a string.
137 fn serialize(key: &LicenseKey) -> String;
138
139 /// Deserializes a license key into a byte vector.
140 fn deserialize(input: &str) -> Vec<u8>;
141}
142
143/// License key serializer for hex strings.
144pub struct HexFormat {}
145impl Serializer for HexFormat {
146 fn serialize(key: &LicenseKey) -> String {
147 hex::encode(key.get_bytes())
148 }
149
150 fn deserialize(input: &str) -> Vec<u8> {
151 hex::decode(input).unwrap()
152 }
153}
154
155/// Represents a generated or parsed license key.
156#[derive(Debug, Clone)]
157pub struct LicenseKey {
158 bytes: Vec<u8>,
159}
160
161impl LicenseKey {
162 pub(crate) fn new(bytes: Vec<u8>) -> Self {
163 Self { bytes }
164 }
165
166 /// Deserializes a [`&str`] into a license key by using the
167 /// provided [`Serializer`].
168 ///
169 /// [`&str`]: https://doc.rust-lang.org/std/primitive.str.html
170 /// [`Serializer`]: trait.Serializer.html
171 pub fn parse<T : Serializer>(input: &str) -> LicenseKey {
172 LicenseKey::new(T::deserialize(input))
173 }
174
175 /// Serializes the license key into a [`String`] by using the
176 /// provided [`Serializer`].
177 ///
178 /// [`String`]: https://doc.rust-lang.org/std/string/struct.String.html
179 /// [`Serializer`]: trait.Serializer.html
180 pub fn serialize<T: Serializer>(&self) -> String {
181 T::serialize(&self)
182 }
183
184 /// Gets the individual bytes that makes up the license key.
185 pub fn get_bytes(&self) -> Vec<u8> {
186 self.bytes.clone()
187 }
188
189 pub(crate) fn get_byte(&self, ordinal: usize) -> Option<u8> {
190 let index = SEED_BYTE_LENGTH as usize + (ordinal * SEGMENT_BYTE_LENGTH as usize);
191 if index > self.bytes.len() - 3 {
192 return None;
193 }
194 Some(self.bytes[index])
195 }
196
197 pub(crate) fn get_checksum(&self) -> &[u8] {
198 &self.bytes[self.bytes.len() - CHECKSUM_BYTE_LENGTH as usize..]
199 }
200
201 pub(crate) fn get_seed(&self) -> u64 {
202 u64::from_be_bytes(self.bytes[0..SEED_BYTE_LENGTH as usize].try_into().unwrap())
203 }
204
205 pub(crate) fn calculate_checksum(&self) -> [u8; 2] {
206 calculate_checksum(&self.bytes[0..self.bytes.len() - CHECKSUM_BYTE_LENGTH as usize])
207 }
208}
209
210/// The license key generator.
211#[derive(Debug)]
212pub struct Generator<T: KeyHasher> {
213 hasher: T,
214 iv: Vec<(u64, u64, u64)>,
215}
216
217impl<T: KeyHasher> Generator<T> {
218 /// Creates a new license key generator.
219 pub fn new(hasher: T, iv: Vec<(u64, u64, u64)>) -> Self {
220 Self { hasher, iv }
221 }
222
223 /// Creates a new license key with the specified seed.
224 pub fn generate(&self, seed: u64) -> LicenseKey {
225 // Get the license key as a byte array
226 let mut input = seed.to_be_bytes().to_vec();
227 for iv in self.iv.iter() {
228 for byte in self
229 .hasher
230 .hash(seed, iv.0, iv.1, iv.2)
231 .to_be_bytes()
232 .to_vec()
233 {
234 input.push(byte);
235 }
236 }
237
238 // Calculate the checksum for the license key
239 let checksum = calculate_checksum(&input);
240 for byte in checksum.iter() {
241 input.push(*byte);
242 }
243
244 LicenseKey::new(input)
245 }
246}
247
248/// Representation of a license key status.
249#[derive(Debug, PartialEq)]
250pub enum Status {
251 /// The license is valid.
252 Valid,
253 /// The license is invalid.
254 Invalid,
255 /// The license has been blocked.
256 Blocked,
257 /// The license has been forged.
258 Forged,
259}
260
261/// Represents a license key byte check
262/// that should be used during validation.
263#[derive(Debug)]
264pub struct ByteCheck {
265 pub ordinal: u8,
266 pub a: u64,
267 pub b: u64,
268 pub c: u64,
269}
270
271impl ByteCheck {
272 /// Creates a new byte check.
273 pub fn new(ordinal: u8, iv: (u64, u64, u64)) -> Self {
274 Self {
275 ordinal,
276 a: iv.0,
277 b: iv.1,
278 c: iv.2,
279 }
280 }
281}
282
283/// The license key verifier.
284#[derive(Debug)]
285pub struct Verifier<T: KeyHasher> {
286 hasher: T,
287 checks: Vec<ByteCheck>,
288 blocklist: Vec<u64>,
289}
290
291impl<T: KeyHasher> Verifier<T> {
292 /// Creates a new license key verifier.
293 pub fn new(hasher: T, checks: Vec<ByteCheck>) -> Self {
294 Self {
295 hasher,
296 checks,
297 blocklist: Vec::new(),
298 }
299 }
300
301 /// Blocks the specified seed from being used.
302 pub fn block(&mut self, seed: u64) {
303 self.blocklist.push(seed)
304 }
305
306 /// Perform verification on the provided license key.
307 pub fn verify(&self, key: &LicenseKey) -> Status {
308 // Validate the checksum
309 let checksum = key.calculate_checksum().to_vec();
310 if checksum != key.get_checksum() {
311 return Status::Invalid;
312 }
313
314 // Blocked key?
315 let seed = key.get_seed();
316 for blocked_seed in self.blocklist.iter() {
317 if seed == *blocked_seed {
318 return Status::Blocked;
319 }
320 }
321
322 for check in self.checks.iter() {
323 match key.get_byte(check.ordinal as usize) {
324 Some(value) => {
325 if value != self.hasher.hash(seed, check.a, check.b, check.c) {
326 // Values did not match, but the checksum
327 // was correct, so this is a forged license key
328 return Status::Forged;
329 }
330 }
331 None => {
332 // If we couldn't get the byte from the license
333 // the license is invalid.
334 return Status::Invalid;
335 }
336 }
337 }
338
339 Status::Valid
340 }
341}
342
343fn calculate_checksum(key: &[u8]) -> [u8; 2] {
344 let mut left = 0x56_u16;
345 let mut right = 0xAF_u16;
346
347 for byte in key.iter() {
348 right += *byte as u16;
349 if right > 0xFF {
350 right -= 0xFF;
351 }
352 left += right;
353 if left > 0xFF {
354 left -= 0xFF;
355 }
356 }
357 ((left << 8) + right).to_be_bytes()
358}
359
360#[cfg(test)]
361mod tests {
362 use super::*;
363 use crate::KeyHasher;
364 use crate::Generator;
365
366 #[derive(Default)]
367 pub struct TestHasher {}
368 impl KeyHasher for TestHasher {
369 fn hash(&self, seed: u64, a: u64, b: u64, c: u64) -> u8 {
370 ((seed ^ a ^ b ^ c) & 0xFF) as u8
371 }
372 }
373
374 pub fn generate_key(seed: u64) -> LicenseKey {
375 let generator = Generator::new(
376 TestHasher::default(),
377 vec![(114, 83, 170), (60, 208, 27), (69, 14, 202), (61, 232, 54)],
378 );
379 generator.generate(seed)
380 }
381
382 pub fn create_verifier() -> Verifier<TestHasher> {
383 Verifier::new(
384 TestHasher::default(),
385 vec![
386 ByteCheck::new(0, (114, 83, 170)),
387 ByteCheck::new(2, (69, 14, 202)),
388 ],
389 )
390 }
391
392 #[test]
393 pub fn valid_key_should_be_valid() {
394 // Given
395 let key = generate_key(12345);
396 let verifier = create_verifier();
397
398 // When
399 let result = verifier.verify(&key);
400
401 // Then
402 assert_eq!(Status::Valid, result);
403 }
404
405 #[test]
406 pub fn valid_but_blocked_key_should_return_error() {
407 // Given
408 let key = generate_key(12345);
409 let mut verifier = create_verifier();
410 verifier.block(12345);
411
412 // When
413 let result = verifier.verify(&key);
414
415 // Then
416 assert_eq!(Status::Blocked, result);
417 }
418}