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.codemust 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-FAILEDerror otherwise.If you call any function which has to communicate with a dongle without successfully calling
AUTHENTICATEfirst, you'll get anAUTHENTICATION-REQUIREDerror. (Well, at least that's how it should be. In some cases you'll get aDONGLE-NOT-FOUNDerror 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-idparameter.
[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-idtonew-product-id. It does not change the value of a href="#*product-id*">*PRODUCT-ID*.
[Function]
dongle-present-p &optional product-id => boolean
ReturnsTif the dongle with the product IDproduct-idis present,NILotherwise.See also
ASSERT-DONGLE.
[Function]
assert-dongle &optional product-id => |
Checks if the dongle with the product IDproduct-idis present. Signals aDONGLE-NOT-FOUNDerror 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-idwhich is one of the keywords:USBor: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-idas 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-idas 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-numberin the dongle with product IDproduct-idand sets it tokey.keymust 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-numberin 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-elementsis already a static array andcopypisNIL, 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'
Encryptsdatain RAM (without using a dongle and without accessing the SG-Lock DLL) with the Tiny Encryption Algorithm using the keykey.datamust either be a sequence of(UNSIGNED-BYTE 32)integers or an FLI pointer. In the latter case,lengthis the number of dwords to be processed. In both cases, the number of dwords to be processed must be even.keymust be a four-element sequence of(UNSIGNED-BYTE 32)integers representing the key to be used.If
datais a pointer, the encryption is performed in place and the pointer is returned, otherwise a static array containing the encrypted elements fromdatais the return value. Ifdatais a static array of element type(UNSIGNED-BYTE 32)andcopypisNIL, the contents ofdatawill also be modified in place anddatawill be returned.This is the inverse of
TEA-DECRYPT. See alsoDONGLE-ENCRYPT.
[Function]
tea-decrypt key data &key length copyp => data'
Decryptsdatain RAM (without using a dongle and without accessing the SG-Lock DLL) with the Tiny Encryption Algorithm using the keykey.datamust either be a sequence of(unsigned-byte 32)integers or an FLI pointer. In the latter case,lengthis the number of dwords to be processed. In both cases, the number of dwords to be processed must be even.keymust be a four-element sequence of(UNSIGNED-BYTE 32)integers representing the key to be used.If
datais 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)andcopypisNIL, the contents ofdatawill also be modified in place anddatawill be returned.This is the inverse of
TEA-ENCRYPT. See alsoDONGLE-DECRYPT.
[Function]
dongle-encrypt key-number data &key product-id length copyp => data'
Encryptsdatausing the dongle with the product IDproduct-idand the key with the numberkey-number.datamust either be a sequence of(UNSIGNED-BYTE 32)integers or an FLI pointer. In the latter case,lengthis the number of dwords to be processed. In both cases, the number of dwords to be processed must be even.If
datais 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. Ifdatais a static array of element type(UNSIGNED-BYTE 32)andcopypisNIL, the contents ofdatawill also be modified in place anddatawill be returned.This is the inverse of
DONGLE-DECRYPT. See alsoTEA-ENCRYPT.
[Function]
dongle-decrypt key-number data &key product-id length copyp => data'
Decryptsdatausing the dongle with the product IDproduct-idand the key with the numberkey-number.datamust either be a sequence of(UNSIGNED-BYTE 32)integers or an FLI pointer. In the latter case,lengthis the number of dwords to be processed. In both cases, the number of dwords to be processed must be even.If
datais a pointer, the decryption is performed in place and the pointer is returned, otherwise a static array containing the decrypted elements fromdatais the return value. Ifdatais a static array of element type(UNSIGNED-BYTE 32)andcopypisNIL, the contents ofdatawill also be modified in place anddatawill be returned.This is the inverse of
DONGLE-ENCRYPT. See alsoTEA-DECRYPT.
[Function]
tea-encrypt-string key string => array
Encrypts the Lisp stringstringin RAM (without using a dongle and without accessing the SG-Lock DLL) with the Tiny Encryption Algorithm using the keykey.keymust 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-STRINGorDONGLE-DECRYPT-TO-STRING.
[Function]
tea-decrypt-to-string key data => string
Decryptsdatato a Lisp string in RAM (without using a dongle and without accessing the SG-Lock DLL) with the Tiny Encryption Algorithm using the keykey.keymust be a four-element sequence of(UNSIGNED-BYTE 32)integers representing the key to be used.datamust be a sequence of integers of type(UNSIGNED-BYTE 32). This operation will obviously only succeed ifdatais (equivalent to) the result ofTEA-ENCRYPT-STRINGorDONGLE-ENCRYPT-STRINGwith 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 stringstringusing the dongle with the product IDproduct-idand 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-STRINGorTEA-DECRYPT-TO-STRING.
[Function]
dongle-decrypt-to-string key-number data &key product-id => string
Decryptsdatato a Lisp string using the dongle with the product IDproduct-idand the key with the numberkey-number.datamust be a sequence of integers of type(UNSIGNED-BYTE 32). This operation will obviously only succeed ifdatais (equivalent to) the result ofDONGLE-ENCRYPT-STRINGorTEA-ENCRYPT-STRINGwith 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 objectobjectin RAM (without using a dongle and without accessing the SG-Lock DLL) with the Tiny Encryption Algorithm using the keykey.keymust 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-OBJECTorDONGLE-DECRYPT-TO-LISP-OBJECT.Objects are encrypted using a naïve serialization algorithm based on
WRITE-TO-STRINGwhich 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
Decryptsdatato a Lisp object in RAM (without using a dongle and without accessing the SG-Lock DLL) with the Tiny Encryption Algorithm using the keykey.keymust be a four-element sequence of(UNSIGNED-BYTE 32)integers representing the key to be used. This operation will obviously only succeed ifdatais (equivalent to) the result ofTEA-ENCRYPT-LISP-OBJECTorDONGLE-ENCRYPT-LISP-OBJECTwith the same key.Decrypted numbers, symbols, and characters will be
EQLto the objects originally encrypted. Other objects will only beEQUALorEQUALP. See the docstring ofSTORE-LISP-OBJECTand the code inserialize.lisp.
[Function]
dongle-encrypt-lisp-object key-number object &key product-id => array
Encrypts the Lisp objectobjectusing the dongle with the product IDproduct-idand 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-OBJECTorTEA-DECRYPT-TO-LISP-OBJECT.Objects are encrypted using a naïve serialization algorithm based on
WRITE-TO-STRINGwhich 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
Decryptsdatato a Lisp object using the dongle with the product IDproduct-idand the key with the numberkey-number.datamust be a sequence of integers of type(UNSIGNED-BYTE 32). This operation will obviously only succeed ifdatais (equivalent to) the result ofDONGLE-ENCRYPT-LISP-OBJECTorTEA-ENCRYPT-LISP-OBJECTwith the same key.Decrypted numbers, symbols, and characters will be
EQLto the objects originally encrypted. Other objects will only beEQUALorEQUALP. See the docstring ofSTORE-LISP-OBJECTand the code inserialize.lisp.
[Function]
store-data address data &key product-id start end => address'
Stores the contents of the(UNSIGNED-BYTE 32)sequencedatafromstarttoendin the dongle with the product IDproduct-idstarting at memory addressaddress.addressuses 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 stringstringfromstarttoendin the dongle with the product IDproduct-idstarting at memory addressaddress.addressuses 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 lengthNwill 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 objectobjectin the dongle with the product IDproduct-idstarting at memory addressaddress.addressuses 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-STRINGwhich 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
Returnscountvalues stored at the memory area starting ataddressfrom the dongle with the product IDproduct-idas a static array of element-type(UNSIGNED-BYTE 32).addressuses 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-idwhich is stored at memory addressaddress. Obviously, this will only work if the string was previously stored usingSTORE-STRING.addressuses 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-idwhich is stored at memory addressaddress. Obviously, this will only work if the object was previously stored usingSTORE-LISP-OBJECT.addressuses a 32-bit addressing scheme corresponding to the values returned byDONGLE-MEMORY-SIZE.Numbers, symbols, and characters will be
EQLto the objects originally stored. Other objects will only beEQUALorEQUALP. See the docstring ofSTORE-LISP-OBJECTand 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
Signsdatausing the dongle with the product IDproduct-id(the default being*PRODUCT-ID*as usual) and/or the Tiny Encryption Algorithm in RAM.datacan 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 argumentlengthspecifies how many octets of data are to be signed.If
datais 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-SEQUENCEand sign them using successive calls toSIGN. The test code contains an example for this technique - see the filesign.lisp.If
intervalisNIL, neither a dongle nor the SG-Lock DLL is used and the signing takes place in RAM using the Tiny Encryption Algorithm. Otherwise,intervalmust be a non-negative integerNsuch that2^(N+3) is still a fixnum. IfMis2^N, then eachMth 64-bit block (which always include the first one) will be signed using the dongle while all others will be signed in RAM. Specifically, ifNis0(zero), all the data will be signed using the dongle. The default forintervalis the greatest possible non-NILvalue.If
intervalis notNIL,key-numbermust be specified to denote which key of the dongle is to be used for signing. Ifintervalis not0(zero),keymust 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
SIGNas the signature ofdata. This, together with the fact that the first block is always signed using the dongle ifintervalis notNIL, ensures that the signature ofdataalways depends on the dongle ifintervalis 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 ifintervalis notNILand ifdataconsists of less than eight octets.The return value of
SIGNis an integer of type(UNSIGNED-BYTE 64).Signing always starts with the same "feedback" value unless you explicitly provide an
(UNSIGNED-BYTE 64)integerfeedback-valueto start with. This can be utilized to sign larger amounts of data with several successive calls toSIGNusign 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 objectobjectby first serializing it using a naïve algorithm based onWRITE-TO-STRINGand then callingSIGN. All parameters except forobjectare 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_FAILEDfrom 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_REQUIREDfrom 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_INVALIDfrom 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_SUPPORTEDfrom 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_FOUNDfrom the SG-Lock API. But see also the description of theAUTHENTICATEfunction.
[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_BUSYfrom 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_ERRORfrom 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_BUSYfrom 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_FOUNDfrom 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_INVALIDfrom 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
SIGNorSIGN-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 $