I played with this for my purposes and want to share back.
Changes
(1) INstalled function for test period (in days). Effectively, I create a registry entry with a (very simply) obfuscated timestamp and check that
(2) I didn't like the user and O/S depended Computer footprint. On the other hand, I did not want to use only processor etc as there are too few options. So I am creating two random registry entries at first run that get incorporated in the footprint. Seemed like a good compromise for low level protection (which is all I need)
(3) Library shows a splash screen if program is not registered. Text in splash depends on the status of the testmode (still testing or ran out)
(4) I wanted all keys nicely formatted in 4 strings of four
Obviously, none of this is great against a semi good Hacker, but it was fine for my application
registration_Library.ahk:
Code:
; Demo for Testperiod and registration
; Author: dorfl68@gmail.com
;
;
;
/* global variables defined in here
license_k0 ; secret keys
license_k1
license_k2
license_k3
license_l0
license_l1
license_m0
license_m1
regkey1, regkey2, regkey3, regkey4 - registration keys
*/
;-------------------------------------------------------------------------------
; higher level functions
;-------------------------------------------------------------------------------
;
; Registered_copy() ; Returns TRUE if this a registered copy or a copy in testmode
; copy_is_registered() ; Shows registration screen if not registered; returns TRUE if this a registered copy
; in_testmode() ; Shows registration screen if in testmode; returns TRUE if in testmode
;
;-------------------------------------------------------------------------------
Registered_copy()
{
; Returns TRUE if this a registered copy or a copy in testmode
; shows splash screen if not registered
license_Initialize() ; make sure all keys are set etc
If (copy_is_registered())
Return True
If (in_testmode())
Return True
Else
Return False
}
copy_is_registered()
{
; test if software is already registered
IniRead, k, %A_ScriptDir%\registration, registration, regkey,0 ; Get registration key
If (K=0) ; registration key doesn't exist, this is not registered
Return False
; now K hold a registration key.
If (IsUserAuthenticated(k)) ; is the key correct?
Return True ; yes, correct key
Else
Return, False ; wrong key
}
in_testmode()
{
; returns True if in testmode, False if expired
; shows splashscreen
time_divider := 6.3737 ; to obfuscate time
test_time := 8 ; days test period
RegRead, installation_time, HKEY_LOCAL_MACHINE, SOFTWARE\Dorflsoft, time1 ; read installation time
If installation_time=
{
testperiod_splash("First run - you have " . test_time " days to test this software")
code_time := A_Now/time_divider ; obfuscated installation time
RegWrite, REG_SZ, HKEY_LOCAL_MACHINE, SOFTWARE\dorflsoft, time1, %code_time% ; write into reg
Return True ; we are in testmode
}
installation_time := Floor(Installation_time * time_divider) ; get installation time
OutputDebug, Installed: %installation_time% ; debugging
now :=A_Now ; today's time
EnvSub, now, %installation_time%, Days ; get difference from installation to today in days
If ((now<test_time) And (now>-1)) ; true if we are in test period
{
now := test_time - now
testperiod_splash("You have " . now . " days left in testmode")
Return True ; we are still in test period
}
; we are outside test period
testperiod_splash("Testperiod expired - please register!")
Return False ; outside test period
}
testperiod_splash(message)
{
global regkey1, regkey2, regkey3, regkey4 ; need to be global to be used in GUI
install_number := Get_install_number()
Gui, 91: +ToolWindow
Gui, 91: Font, S20 bold, Verdana
Gui, 91: Add, Text, Section x20 y10 w660 h30 +Center, Registration example
Gui, 91: Font, S10 norm, Verdana
Gui, 91: Add, Text, xs yp+30 w660 h40 +Center , This software was written in AHK (http://www.autohotkey.com)
Gui, 91: Font, S12 norm bold, Verdana
Gui, 91: Add, Text, xs yp+30 w660 h30 +Center , %message%
Gui, 91: Font, S12 norm, Verdana
Gui, 91: Add, Text, xs yp+30 w660 h30 +Center gcopyinstallnumber, Installation Number (Click to copy to Clipboard for email)
Gui, 91: Font, cblue
Gui, 91: Add, Text, xs yp+30 w660 h30 +Center gcopyinstallnumber, %install_number%
Gui, 91: Font, norm cblack
Gui, 91: Add, Text, xs+100 yp+60, Registration Key:
Gui, 91: Font, s12, courier
Gui, 91: Add, Edit, limit4 w50 Section Uppercase yp xp+150 Center vregkey1,
Gui, 91: Add, Text, YP w5 xp+51,-
Gui, 91: Add, Edit, limit4 xp+6 w50 Uppercase ys Center vregkey2,
Gui, 91: Add, Text, YP xp+51,-
Gui, 91: Add, Edit, limit4 xp+6 w50 Uppercase ys Center vregkey3,
Gui, 91: Add, Text, YP xp+51,-
Gui, 91: Add, Edit, limit4 xp+6 w50 Uppercase ys Center vregkey4,
Gui, 91: Font, S8 norm, Verdana
Gui, 91: Add, Button, yp xp+60 hp Default Center -Wrap gregister_me, Register
Gui, 91: Font, S14 norm, Verdana
Gui, 91: Add, Button, x300 yp+50 w100 h30 Default Center -Wrap, OK
Gui, 91: Show, w700 Center , Testmode
; wait until user is done with this GUI
Gui, 91: +LastFound
WinWaitActive
WinWaitClose
Return
copyinstallnumber:
{
Clipboard := install_number
MsgBox, 4160, Registration, Install Number copied to Clipboard. `n Email the Number to [tbd]@tbd.com] for registration
Return
}
register_me:
{
Loop, 4
GuiControlGet, regkey%A_Index%
k := regkey1 . regkey2 . regkey3 . regkey4 ; create key
If IsUserAuthenticated(k) ; test whether this is a valid key
{
Gui, 91: Destroy
IniWrite, %k%, %A_ScriptDir%\registration, registration, regkey ; write key in registry
MsgBox, 4144, Registration, Registration key recognized `n Thank You!
Return
}
Else
MsgBox, 4144, Registration Error, Sorry, that seems to be an`n invalid registration key
Return
}
91GuiClose:
91ButtonOK:
Gui, 91: Destroy
Return
}
;-------------------------------------------------------------------------------
; core functions Functions
;-------------------------------------------------------------------------------
;
; licence_Initialize() ; initialize the secret keys
; install_number() ; returns the installation number (fingerprint)
; IsUserAuthenticated(key) ; returns true if Key is a valid registration key
; GenerateLicenseKey(install_number) ; generates a registration key from the install number (fingerprint)
;
;-------------------------------------------------------------------------------
license_Initialize()
{
; initialize the variables and create the registry entries
global
license_k0 := 0x89879798 ; 128-bit secret key (example)
license_k1 := 0x98987987
license_k2 := 0x82826363
license_k3 := 0x82928637
license_l0 := 0x91873619 ; 64- bit 2nd secret key (example)
license_l1 := 0x92929292
license_m0 := 0x11111982 ; 64- bit 3rd secret key (example)
license_m1 := 0x98292929
; now create the random keys if they don't exist
RegRead, license_reg1, HKEY_LOCAL_MACHINE, SOFTWARE\Dorflsoft, r1
RegRead, license_reg2, HKEY_LOCAL_MACHINE, SOFTWARE\Dorflsoft, r2
If license_reg1=
{
Random,license_reg1,10000000,2147483647
RegWrite, REG_SZ, HKEY_LOCAL_MACHINE, SOFTWARE\Dorflsoft, r1, %license_reg1%
}
If license_reg2=
{
Random,license_reg2,10000000,2147483647
RegWrite, REG_SZ, HKEY_LOCAL_MACHINE, SOFTWARE\Dorflsoft, r2, %license_reg2%
}
Return
}
Get_install_number()
{
; gets installation number (formally called fingerprint)
; kept all envgets if other folks want to add them back in
EnvGet, COMPUTERNAME, COMPUTERNAME
EnvGet, HOMEPATH, HOMEPATH
EnvGet, USERNAME, USERNAME
EnvGet, PROCESSOR_ARCHITECTURE, PROCESSOR_ARCHITECTURE
EnvGet, PROCESSOR_IDENTIFIER, PROCESSOR_IDENTIFIER
EnvGet, PROCESSOR_LEVEL, PROCESSOR_LEVEL
EnvGet, PROCESSOR_REVISION, PROCESSOR_REVISION
; new: read random keys
RegRead, license_reg1, HKEY_LOCAL_MACHINE, SOFTWARE\Dorflsoft, r1
RegRead, license_reg2, HKEY_LOCAL_MACHINE, SOFTWARE\Dorflsoft, r2
; construct the key - feel free to vary
; this tries to be hardware specific
PCdata = %license_reg1%%PROCESSOR_ARCHITECTURE%%PROCESSOR_IDENTIFIER%
PCdata = %PCdata%%PROCESSOR_LEVEL%%PROCESSOR_REVISION%%license_reg2%
Fingerprint := XCBC(Hex(PCdata,StrLen(PCdata)), 0,0, 0,0,0,0, 4,3, 4,3)
StringUpper, fingerprint,fingerprint ; make it look good
fp := create_blocks(fingerprint) ; create blocks of four with dashes in between
Return FP
}
GenerateLicenseKey(fingerprint )
{
; almost as original - create the registration key from fingerprint
; also now takes a fingerprint in blocks of 4 with dashes
global license_k0,license_k1,license_k2,license_k3,license_l0,license_l1,license_m0,license_m1
StringReplace, fingerprint,fingerprint,-,,All ; remove any dashes
Auth := XCBC(Hex(fingerprint,StrLen(fingerprint)), 0,0, license_k0,license_k1,license_k2,license_k3, license_l0,license_l1, license_m0,license_m1)
StringUpper, auth, auth ; make it look good
a := create_blocks(auth) ; create blocks of four with dashes in between
Return A
}
IsUserAuthenticated(key )
{
; check whether user is authenticated
; takes both keys with or without dashed
global license_k0,license_k1,license_k2,license_k3,license_l0,license_l1,license_m0,license_m1
StringReplace, key,key,-,,All ; remove any dashes
AuthData := GenerateLicenseKey(Get_install_number())
StringReplace, authdata,authdata,-,,All ; remove any dashes
StringUpper, key,key
Return Key=AuthData
}
create_blocks(in)
{
; takes string of 16 and returns blocks of four with dashes
out :=""
Loop
{
out .= SubStr(in,1,4)
If StrLen(in) =4
Break ; last one
out .= "-"
StringTrimLeft, in, in, 4 ; remove 4 and repeat
}
Return out
}
;-------------------------------------------------------------------------------
; Internal Functions by Laszlo
;-------------------------------------------------------------------------------
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; TEA cipher ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Block encryption with the TEA cipher
; [y,z] = 64-bit I/0 block
; [k0,k1,k2,k3] = 128-bit key
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
TEA(ByRef y,ByRef z, k0,k1,k2,k3)
{ ; need SetFormat Integer, D
s = 0
d = 0x9E3779B9
Loop 32 ; could be reduced to 8 for speed
{
k := "k" . s & 3 ; indexing the key
y := 0xFFFFFFFF & (y + ((z << 4 ^ z >> 5) + z ^ s + %k%))
s := 0xFFFFFFFF & (s + d) ; simulate 32 bit operations
k := "k" . s >> 11 & 3
z := 0xFFFFFFFF & (z + ((y << 4 ^ y >> 5) + y ^ s + %k%))
}
}
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; XCBC-MAC ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; x = long hex string input
; [u,v] = 64-bit initial value (0,0)
; [k0,k1,k2,k3] = 128-bit key
; [l0,l1] = 64-bit key for not padded last block
; [m0,m1] = 64-bit key for padded last block
; Return 16 hex digits (64 bits) digest
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
XCBC(x, u,v, k0,k1,k2,k3, l0,l1, m0,m1)
{
Loop % Ceil(StrLen(x)/16)-1 ; full length intermediate message blocks
XCBCstep(u, v, x, k0,k1,k2,k3)
If (StrLen(x) = 16) ; full length last message block
{
u := u ^ l0 ; l-key modifies last state
v := v ^ l1
XCBCstep(u, v, x, k0,k1,k2,k3)
}
Else { ; padded last message block
u := u ^ m0 ; m-key modifies last state
v := v ^ m1
x = %x%100000000000000
XCBCstep(u, v, x, k0,k1,k2,k3)
}
Return Hex8(u) . Hex8(v) ; 16 hex digits returned
}
XCBCstep(ByRef u, ByRef v, ByRef x, k0,k1,k2,k3)
{
StringLeft p, x, 8 ; Msg blocks
StringMid q, x, 9, 8
StringTrimLeft x, x, 16
p = 0x%p%
q = 0x%q%
u := u ^ p
v := v ^ q
TEA(u,v,k0,k1,k2,k3)
}
Hex8(i) ; 32-bit integer -> 8 hex digits
{
format = %A_FormatInteger% ; save original integer format
SetFormat Integer, Hex
i += 0x100000000 ; convert to hex, set MS bit
StringTrimLeft i, i, 3 ; remove leading 0x1
SetFormat Integer, %format% ; restore original format
Return i
}
Hex(ByRef b, n=0) ; n bytes data -> stream of 2-digit hex
{ ; n = 0: all (SetCapacity can be larger than used!)
format = %A_FormatInteger% ; save original integer format
SetFormat Integer, Hex ; for converting bytes to hex
m := VarSetCapacity(b)
If (n < 1 or n > m)
n := m
Loop %n%
{
x := 256 + *(&b+A_Index-1) ; get byte in hex, set 17th bit
StringTrimLeft x, x, 3 ; remove 0x1
h = %h%%x%
}
SetFormat Integer, %format% ; restore original format
Return h
}
example usage
Code:
/*
Main program for Demo only
*/
#SingleInstance force
#NoEnv ; Recommended for performance and compatibility with future AutoHotkey releases.
SendMode Input ; Recommended for new scripts due to its superior speed and reliability.
SetWorkingDir %A_ScriptDir% ; Ensures a consistent starting directory.
If (!registered_copy())
ExitApp
MsgBox, App is registered or in testperiod, have fun
; main program starts here
Return
#Include registration_library.ahk
Tool to calculate the reg code
Code:
/*
Creates registration key
*/
#NoEnv
#SingleInstance force
Gui, Font, S14
Gui, Add, Text,Section,Dorfl Registration key generator
Gui, Add, Text,, Installion Number:
Gui, Add, Edit, yp xp+200 vinstallnumber gnumberchange,
Gui, Add, Text,XS, Registration key:
Gui, Add, Text , yp xp+200 w200 vregkey ReadOnly, %A_Space%
Gui, Add, Button,gcopytoclip,Copy registration key to Clipboard
Gui, Show
Return
numberchange:
{
License_Initialize()
GuiControlGet, installnumber
r := GenerateLicenseKey(installnumber)
GuiControl,, regkey, %r%
Return
}
copytoclip:
{
GuiControlGet, regkey
Clipboard := regkey
MsgBox, %regkey% copied to Clipboard
Return
}
#Include registration_library.ahk