Abstract
CL-DONGLE is a Common Lisp library which provides full access to all the functionality of SG-Lock's hardware-based copy protection system, i.e. you can use it to protect your Lisp applications natively using features like storing Lisp objects in the dongle or encrypting and signing arbitrary data.The library also contains a fast Lisp implementation of the Tiny Encryption Algorithm which works independently of attached dongles or the SG-Lock DLL and might thus be useful on its own.
CL-DONGLE works only with the LispWorks Common Lisp implementation and, like the SG-Lock system itself, only on Microsoft Windows. It should be pretty easy to port the code to OS X, though, once SG-Lock dongles are available for Macs.
The code comes with a BSD-style license so you can basically do with it whatever you want.
Download shortcut: http://weitz.de/files/cl-dongle.tar.gz.
Before you install CL-DONGLE you first need to install the LW-WIN library unless you already have it.
CL-DONGLE comes with a system definition for ASDF so you can install the library with
(asdf:oos 'asdf:load-op :cl-dongle)if you've unpacked it in a place where ASDF can find it. Unless you only want to use the in-RAM encryption functions, you will also need to put the SG-Lock DLL in a place where your Lisp (or your delivered application) will find it.
Don't worry if you see warnings about HARP::NREG
and HARP::GREG
- this is
a known
compiler bug which is harmless.
You can run a test suite which tests all aspects of the library with
(asdf:oos 'asdf:test-op :cl-dongle)if you're using a demo dongle. If you have a "real" dongle, load the
CL-DONGLE-TEST
system, invoke the
function CL-DONGLE-TEST:RUN-TEST
, but read its docstring
first. Running all the tests might take a while...
AUTHENTICATE
must
always be called first before any communication with a dongle can
happen. Also note that the value
of *PRODUCT-ID*
is the
default value for all optional and keyword parameters
called product-id
.
[Function]
authenticate code => |
Authenticates the Lisp application against the SG-Lock DLL and vice versa.code
must be a sequence of twelve(UNSIGNED-BYTE 32)
integers representing the authentication code you received from SG-Lock when you bought your dongles (or*DEMO-AUTHENTICATION-CODE*
for demo dongles).This function must be called before any communication with the corresponding dongle is attempted. It returns no values in the case of success and signals an
AUTHENTICATION-FAILED
error otherwise.If you call any function which has to communicate with a dongle without successfully calling
AUTHENTICATE
first, you'll get anAUTHENTICATION-REQUIRED
error. (Well, at least that's how it should be. In some cases you'll get aDONGLE-NOT-FOUND
error instead which is slightly misleading.)
[Special variable]
*demo-authentication-code*
The authentication code for demo dongles distributed by SG-Lock.
[Special variable]
*product-id*
The default value for all functions which have an optional or keywordproduct-id
parameter.
[Accessor]
product-id => product-id
(setf (product-id &optional product-id) new-product-id)
The reader returns the (first) dongle's product ID. The writer sets the product ID of the dongle with the product IDproduct-id
tonew-product-id
. It does not change the value of a href="#*product-id*">*PRODUCT-ID*
.
[Function]
dongle-present-p &optional product-id => boolean
ReturnsT
if the dongle with the product IDproduct-id
is present,NIL
otherwise.See also
ASSERT-DONGLE
.
[Function]
assert-dongle &optional product-id => |
Checks if the dongle with the product IDproduct-id
is present. Signals aDONGLE-NOT-FOUND
error if it is not, returns no value if successful.See also
DONGLE-PRESENT-P
.
[Function]
dongle-type &optional product-id => integer
Returns the "type" of the dongle with the product IDproduct-id
. The type is one of 2, 3, or 4 corresponding to the number in SG-Lock product names like "U2" or "L4".
[Function]
dongle-interface &optional product-id => keyword
Returns the "interface" of the dongle with the product IDproduct-id
which is one of the keywords:USB
or:LPT
.
[Function]
dongle-memory-size &optional product-id => integer
Returns the memory size of the dongle with the product IDproduct-id
. The size is measured in dwords, i.e. in 32-bit blocks. So, if the return value is, say, 256, the memory size is 1024 octets.
[Function]
dongle-number-of-counters &optional product-id => integer
Returns the number of available programmable counters of the dongle with the product IDproduct-id
.
[Function]
dongle-number-of-keys &optional product-id => integer
Returns the number of available programmable keys of the dongle with the product IDproduct-id
.
[Function]
dongle-hardware-version &optional product-id => list
Returns the hardware version of the dongle with the product IDproduct-id
as a list of two integers - the major and minor version.
[Function]
dongle-software-version &optional product-id => list
Returns the software version of the dongle with the product IDproduct-id
as a list of two integers - the major and minor version.
[Function]
dongle-serial-number &optional product-id => integer
Returns the serial number for the dongle with the product IDproduct-id
.
[Function]
write-key key-number key &optional product-id => key
Overwrites the key numberedkey-number
in the dongle with product IDproduct-id
and sets it tokey
.key
must be a sequence of four(UNSIGNED-BYTE 32)
integers. Returnskey
.Note that for security reasons there's no inverse operation to read the keys of a dongle. See also
RANDOM-KEY
.
[Accessor]
counter-value counter-number &optional product-id => counter-value
(setf (counter-value counter-number &optional product-id) new-counter-value)
Returns or sets the value of the countercounter-number
in the dongle with the product IDproduct-id
.
[Generic function]
make-static-u32-array initial-elements &optional copyp => static-array
Utility function which returns an array of element type(UNSIGNED-BYTE 32)
allocated in the static area and filled with the initial elementsinitial-elements
(a list or an array). The length of the array is determined byinitial-elements
. Ifinitial-elements
is already a static array andcopyp
isNIL
, then this array will simply be returned.Static arrays are mainly used for performance reasons within CL-DONGLE. Most functions which accept sequence arguments can send (the addresses of) static arrays directly to the SG-Lock API while non-static arrays or lists have to be converted first.
[Function]
random-key => array
Utility function which returns a static array of 4 random(UNSIGNED-BYTE 32)
integers, i.e. a value which can be used as a random key within CL-DONGLE.
DONGLE-
" which means that the operation is performed in
the dongle and one starting with "TEA-
"
(for Tiny
Encryption Algorithm) which means that the operation is
implemented in pure Lisp and performed in RAM. (Specifically, this
means that the "TEA-
" functions can be used without a
dongle and without the SG-Lock DLL.)
As long as they're using the same key, these functions should return the same values no matter whether they are performed in the dongle or not. So, for instance, you can encrypt something in RAM and then decrypt it using the dongle or vice versa. See the test code for examples.
[Function]
tea-encrypt key data &key length copyp => data'
Encryptsdata
in RAM (without using a dongle and without accessing the SG-Lock DLL) with the Tiny Encryption Algorithm using the keykey
.data
must either be a sequence of(UNSIGNED-BYTE 32)
integers or an FLI pointer. In the latter case,length
is the number of dwords to be processed. In both cases, the number of dwords to be processed must be even.key
must be a four-element sequence of(UNSIGNED-BYTE 32)
integers representing the key to be used.If
data
is a pointer, the encryption is performed in place and the pointer is returned, otherwise a static array containing the encrypted elements fromdata
is the return value. Ifdata
is a static array of element type(UNSIGNED-BYTE 32)
andcopyp
isNIL
, the contents ofdata
will also be modified in place anddata
will be returned.This is the inverse of
TEA-DECRYPT
. See alsoDONGLE-ENCRYPT
.
[Function]
tea-decrypt key data &key length copyp => data'
Decryptsdata
in RAM (without using a dongle and without accessing the SG-Lock DLL) with the Tiny Encryption Algorithm using the keykey
.data
must either be a sequence of(unsigned-byte 32)
integers or an FLI pointer. In the latter case,length
is the number of dwords to be processed. In both cases, the number of dwords to be processed must be even.key
must be a four-element sequence of(UNSIGNED-BYTE 32)
integers representing the key to be used.If
data
is a pointer, the encryption is performed in place and the pointer is returned, otherwise a static array containing the encrypted elements from DATA is the return value. If DATA is a static array of element type(UNSIGNED-BYTE 32)
andcopyp
isNIL
, the contents ofdata
will also be modified in place anddata
will be returned.This is the inverse of
TEA-ENCRYPT
. See alsoDONGLE-DECRYPT
.
[Function]
dongle-encrypt key-number data &key product-id length copyp => data'
Encryptsdata
using the dongle with the product IDproduct-id
and the key with the numberkey-number
.data
must either be a sequence of(UNSIGNED-BYTE 32)
integers or an FLI pointer. In the latter case,length
is the number of dwords to be processed. In both cases, the number of dwords to be processed must be even.If
data
is a pointer, the encryption is performed in place and the pointer is returned, otherwise a static array containing the encrypted elements from DATA is the return value. Ifdata
is a static array of element type(UNSIGNED-BYTE 32)
andcopyp
isNIL
, the contents ofdata
will also be modified in place anddata
will be returned.This is the inverse of
DONGLE-DECRYPT
. See alsoTEA-ENCRYPT
.
[Function]
dongle-decrypt key-number data &key product-id length copyp => data'
Decryptsdata
using the dongle with the product IDproduct-id
and the key with the numberkey-number
.data
must either be a sequence of(UNSIGNED-BYTE 32)
integers or an FLI pointer. In the latter case,length
is the number of dwords to be processed. In both cases, the number of dwords to be processed must be even.If
data
is a pointer, the decryption is performed in place and the pointer is returned, otherwise a static array containing the decrypted elements fromdata
is the return value. Ifdata
is a static array of element type(UNSIGNED-BYTE 32)
andcopyp
isNIL
, the contents ofdata
will also be modified in place anddata
will be returned.This is the inverse of
DONGLE-ENCRYPT
. See alsoTEA-DECRYPT
.
[Function]
tea-encrypt-string key string => array
Encrypts the Lisp stringstring
in RAM (without using a dongle and without accessing the SG-Lock DLL) with the Tiny Encryption Algorithm using the keykey
.key
must be a four-element sequence of(UNSIGNED-BYTE 32)
integers representing the key to be used.The return value is a (static) array of integers of type
(UNSIGNED-BYTE 32)
which can be (given the right key) decrypted back to a string usingTEA-DECRYPT-TO-STRING
orDONGLE-DECRYPT-TO-STRING
.
[Function]
tea-decrypt-to-string key data => string
Decryptsdata
to a Lisp string in RAM (without using a dongle and without accessing the SG-Lock DLL) with the Tiny Encryption Algorithm using the keykey
.key
must be a four-element sequence of(UNSIGNED-BYTE 32)
integers representing the key to be used.data
must be a sequence of integers of type(UNSIGNED-BYTE 32)
. This operation will obviously only succeed ifdata
is (equivalent to) the result ofTEA-ENCRYPT-STRING
orDONGLE-ENCRYPT-STRING
with the same key.The returned string will be
STRING=
but not identical to the string that was originally encrypted.
[Function]
dongle-encrypt-string key-number string &key product-id => array
Encrypts the Lisp stringstring
using the dongle with the product IDproduct-id
and the key with the numberkey-number
. The return value is a (static) array of integers of type(UNSIGNED-BYTE 32)
which can be (given the right key) decrypted back to a string usingDONGLE-DECRYPT-TO-STRING
orTEA-DECRYPT-TO-STRING
.
[Function]
dongle-decrypt-to-string key-number data &key product-id => string
Decryptsdata
to a Lisp string using the dongle with the product IDproduct-id
and the key with the numberkey-number
.data
must be a sequence of integers of type(UNSIGNED-BYTE 32)
. This operation will obviously only succeed ifdata
is (equivalent to) the result ofDONGLE-ENCRYPT-STRING
orTEA-ENCRYPT-STRING
with the same key.The returned string will be
STRING=
but not identical to the string that was originally encrypted.
[Function]
tea-encrypt-lisp-object key object => array
Encrypts the Lisp objectobject
in RAM (without using a dongle and without accessing the SG-Lock DLL) with the Tiny Encryption Algorithm using the keykey
.key
must be a four-element sequence of(UNSIGNED-BYTE 32)
integers representing the key to be used.The return value is a (static) array of integers of type
(UNSIGNED-BYTE 32)
which can be (given the right key) decrypted back to a Lisp object usingTEA-DECRYPT-TO-LISP-OBJECT
orDONGLE-DECRYPT-TO-LISP-OBJECT
.Objects are encrypted using a naïve serialization algorithm based on
WRITE-TO-STRING
which is neither fast nor space-efficient. Furthermore, only objects which can be printed readably can be encrypted this way.
[Function]
tea-decrypt-to-lisp-object key data => object
Decryptsdata
to a Lisp object in RAM (without using a dongle and without accessing the SG-Lock DLL) with the Tiny Encryption Algorithm using the keykey
.key
must be a four-element sequence of(UNSIGNED-BYTE 32)
integers representing the key to be used. This operation will obviously only succeed ifdata
is (equivalent to) the result ofTEA-ENCRYPT-LISP-OBJECT
orDONGLE-ENCRYPT-LISP-OBJECT
with the same key.Decrypted numbers, symbols, and characters will be
EQL
to the objects originally encrypted. Other objects will only beEQUAL
orEQUALP
. See the docstring ofSTORE-LISP-OBJECT
and the code inserialize.lisp
.
[Function]
dongle-encrypt-lisp-object key-number object &key product-id => array
Encrypts the Lisp objectobject
using the dongle with the product IDproduct-id
and the key with the numberkey-number
. The return value is a (static) array of integers of type(UNSIGNED-BYTE 32)
which can be (given the right key) decrypted back to a Lisp object usingDONGLE-DECRYPT-TO-LISP-OBJECT
orTEA-DECRYPT-TO-LISP-OBJECT
.Objects are encrypted using a naïve serialization algorithm based on
WRITE-TO-STRING
which is neither fast nor space-efficient. Furthermore, only objects which can be printed readably can be encrypted this way.
[Function]
dongle-decrypt-to-lisp-object key-number data &key product-id => object
Decryptsdata
to a Lisp object using the dongle with the product IDproduct-id
and the key with the numberkey-number
.data
must be a sequence of integers of type(UNSIGNED-BYTE 32)
. This operation will obviously only succeed ifdata
is (equivalent to) the result ofDONGLE-ENCRYPT-LISP-OBJECT
orTEA-ENCRYPT-LISP-OBJECT
with the same key.Decrypted numbers, symbols, and characters will be
EQL
to the objects originally encrypted. Other objects will only beEQUAL
orEQUALP
. See the docstring ofSTORE-LISP-OBJECT
and the code inserialize.lisp
.
[Function]
store-data address data &key product-id start end => address'
Stores the contents of the(UNSIGNED-BYTE 32)
sequencedata
fromstart
toend
in the dongle with the product IDproduct-id
starting at memory addressaddress
.address
uses a 32-bit addressing scheme corresponding to the values returned byDONGLE-MEMORY-SIZE
.Returns the address following the stored data. (Note that depending on the memory size of the dongle this might be an illegal address.)
[Function]
store-string address string &key product-id start end => address'
Stores the Lisp stringstring
fromstart
toend
in the dongle with the product IDproduct-id
starting at memory addressaddress
.address
uses a 32-bit addressing scheme corresponding to the values returned byDONGLE-MEMORY-SIZE
. The string is stored in such a way that a string which isSTRING=
to it can be retrieved usingRETRIEVE-STRING
. A string of lengthN
will use up(ceiling (1+ n) 2)dwords in the dongle.Returns the address following the stored data. (Note that depending on the memory size of the dongle this might be an illegal address.)
[Function]
store-lisp-object address object &optional product-id => address'
Stores the Lisp objectobject
in the dongle with the product IDproduct-id
starting at memory addressaddress
.address
uses a 32-bit addressing scheme corresponding to the values returned byDONGLE-MEMORY-SIZE
. The object is stored in such a way that it can be retrieved usingRETRIEVE-LISP-OBJECT
.Objects are stored using a naïve serialization algorithm based on
WRITE-TO-STRING
which is neither fast nor space-efficient. Furthermore, only objects which can be printed readably can be stored this way.Returns the address following the stored data. (Note that depending on the memory size of the dongle this might be an illegal address.)
[Function]
retrieve-data address count &optional product-id => array
Returnscount
values stored at the memory area starting ataddress
from the dongle with the product IDproduct-id
as a static array of element-type(UNSIGNED-BYTE 32)
.address
uses a 32-bit addressing scheme corresponding to the values returned byDONGLE-MEMORY-SIZE
.
[Function]
retrieve-string address &optional product-id => string
Returns the Lisp string from the dongle with the product IDproduct-id
which is stored at memory addressaddress
. Obviously, this will only work if the string was previously stored usingSTORE-STRING
.address
uses a 32-bit addressing scheme corresponding to the values returned byDONGLE-MEMORY-SIZE
.The returned string will be
STRING=
but not identical to the string that was originally stored.
[Function]
retrieve-lisp-object address &optional product-id => object
Returns the Lisp object from the dongle with the product IDproduct-id
which is stored at memory addressaddress
. Obviously, this will only work if the object was previously stored usingSTORE-LISP-OBJECT
.address
uses a 32-bit addressing scheme corresponding to the values returned byDONGLE-MEMORY-SIZE
.Numbers, symbols, and characters will be
EQL
to the objects originally stored. Other objects will only beEQUAL
orEQUALP
. See the docstring ofSTORE-LISP-OBJECT
and the code inserialize.lisp
.
NIL
as the value
for interval
below. If the
whole file is signed in the dongle (interval
being 0
), I can have dinner in the meantime.
Unfortunately, the "all-in-RAM" method is orders of magnitude more insecure than using the dongle (which is the reason you're using this thingy in the first place), so the trick is to find the right compromise...
[Generic function]
sign data &key feedback-value interval key-number key product-id &allow-other-keys => integer
Signsdata
using the dongle with the product IDproduct-id
(the default being*PRODUCT-ID*
as usual) and/or the Tiny Encryption Algorithm in RAM.data
can be a sequence (i.e. a list or an array) of(UNSIGNED-BYTE 32)
integers or a pathname denoting a file or an FLI pointer pointing to an area of memory which is to be signed. In the last case, the keyword argumentlength
specifies how many octets of data are to be signed.If
data
is a pathname, the whole contents of the file denoted by DATA will be mapped into memory. If you don't want that, you will have to read smaller chunks of the file usingREAD-SEQUENCE
and sign them using successive calls toSIGN
. The test code contains an example for this technique - see the filesign.lisp
.If
interval
isNIL
, neither a dongle nor the SG-Lock DLL is used and the signing takes place in RAM using the Tiny Encryption Algorithm. Otherwise,interval
must be a non-negative integerN
such that2^(N+3
) is still a fixnum. IfM
is2^N
, then eachM
th 64-bit block (which always include the first one) will be signed using the dongle while all others will be signed in RAM. Specifically, ifN
is0
(zero), all the data will be signed using the dongle. The default forinterval
is the greatest possible non-NIL
value.If
interval
is notNIL
,key-number
must be specified to denote which key of the dongle is to be used for signing. Ifinterval
is not0
(zero),key
must be specified to denote the key - a four-element sequence of(UNSIGNED-BYTE 32)
integers - for the in-RAM signing.The signing process operates on successive 64-bit blocks using the signature of the previous block as the initial "feedback" value. The signature of the last block is returned by
SIGN
as the signature ofdata
. This, together with the fact that the first block is always signed using the dongle ifinterval
is notNIL
, ensures that the signature ofdata
always depends on the dongle ifinterval
is an integer.If the block of data to be signed is a sequence of octets the length of which is not divisible by
8
, then the function makes sure that the sequence is extended consistently. As a technical detail, this 64-bit "fill" block is only ever signed using the dongle ifinterval
is notNIL
and ifdata
consists of less than eight octets.The return value of
SIGN
is an integer of type(UNSIGNED-BYTE 64)
.Signing always starts with the same "feedback" value unless you explicitly provide an
(UNSIGNED-BYTE 64)
integerfeedback-value
to start with. This can be utilized to sign larger amounts of data with several successive calls toSIGN
usign the return value of one call as the "feedback value" for the next call. See also the remark about the test code above.
[Function]
sign-lisp-object object &key feedback-value interval key-number key product-id => integer
Signs the Lisp objectobject
by first serializing it using a naïve algorithm based onWRITE-TO-STRING
and then callingSIGN
. All parameters except forobject
are fed toSIGN
.
DONGLE-ERROR
. More
details about the conditions you can encounter can be found in this
section.
[Condition type]
dongle-error
The superclass for all errors signalled by the CL-DONGLE library.
[Condition type]
authentication-failed
This error is signalled if the mandatory initial authentication (functionAUTHENTICATE
) failed. This is equivalent to getting the return valueSGL_AUTHENTICATION_FAILED
from the SG-Lock API.
[Condition type]
authentication-required
This error is signalled if functions of the SG-Lock API are called prior to the mandatory initial authentication - see the functionAUTHENTICATE
. This is equivalent to getting the return valueSGL_AUTHENTICATION_REQUIRED
from the SG-Lock API.
[Condition type]
parameter-invalid
This error is signalled if an API function was called with parameters which are out of range. This is equivalent to getting the return valueSGL_PARAMETER_INVALID
from the SG-Lock API.
[Condition type]
function-not-supported
This error is signalled if an API function was called which is not supported by the current dongle. This is equivalent to getting the return valueSGL_FUNCTION_NOT_SUPPORTED
from the SG-Lock API.
[Condition type]
dongle-not-found
This error is signalled if no dongle corresponding to the product ID which was provided was found. This is equivalent to getting the return valueSGL_DGL_NOT_FOUND
from the SG-Lock API. But see also the description of theAUTHENTICATE
function.
[Condition type]
usb-port-busy
This error is signalled if at least one USB port is busy and no matching dongles were found on the other ports, if there are any. This is equivalent to getting the return valueSGL_USB_BUSY
from the SG-Lock API.
[Condition type]
lpt-open-error
This error is signalled if the SG-Lock LPT driver wasn't found although LPT support was installed. This is equivalent to getting the return valueSGL_LPT_OPEN_ERROR
from the SG-Lock API.
[Condition type]
lpt-port-busy
This error is signalled if at least one LPT port is busy and no matching dongles were found on the other ports, if there are any. This is equivalent to getting the return valueSGL_LPT_BUSY
from the SG-Lock API.
[Condition type]
no-lpt-port-found
This error is signalled if no LPT port was found on the PC although LPT support was installed. This is equivalent to getting the return valueSGL_NO_LPT_PORT_FOUND
from the SG-Lock API.
[Condition type]
signature-invalid
This error is signalled if one of the API function was asked to verify a signature which turned out to be invalid. This is equivalent to getting the return valueSGL_SIGNATURE_INVALID
from the SG-Lock API.You actually shouldn't see this error when using CL-DONGLE as you're supposed to check signatures yourself - by simply applying the function = to the values returned by
SIGN
orSIGN-LISP-OBJECT
.
[Condition type]
unknown-return-value
This is a typical this-should-not-happen error. It will be signalled in the case that one of the C functions in the SG-Lock DLL returns an undocumented value.
*demo-authentication-code*
*product-id*
assert-dongle
authenticate
authentication-failed
authentication-required
counter-value
dongle-decrypt
dongle-decrypt-to-lisp-object
dongle-decrypt-to-string
dongle-encrypt
dongle-encrypt-lisp-object
dongle-encrypt-string
dongle-error
dongle-error
dongle-hardware-version
dongle-interface
dongle-memory-size
dongle-not-found
dongle-number-of-counters
dongle-number-of-keys
dongle-present-p
dongle-serial-number
dongle-software-version
dongle-type
function-not-supported
lpt-open-error
lpt-port-busy
make-static-u32-array
no-lpt-port-found
parameter-invalid
product-id
random-key
retrieve-data
retrieve-lisp-object
retrieve-string
sign
sign-lisp-object
signature-invalid
store-data
store-lisp-object
store-string
tea-decrypt
tea-decrypt-to-lisp-object
tea-decrypt-to-string
tea-encrypt
tea-encrypt-lisp-object
tea-encrypt-string
unknown-return-value
usb-port-busy
write-key
This documentation was prepared with DOCUMENTATION-TEMPLATE.
$Header: /usr/local/cvsrep/cl-dongle/doc/index.html,v 1.18 2008/05/01 14:47:43 edi Exp $