Updated: 29-Jun-2001 [4-May-2001] REBOL Technologies Problems or suggestions: docs@rebol.com
Table of Contents
1. How to Run the Examples
2. Encrypting a String
3. Decrypting a String
4. Creating a Common Crypt Function
5. Creating a Good Encryption Key
5.1. Password Based Keys
5.2. Computer Generated Keys
6. Encrypting a File
7. Decrypting a File
8. Adding Compression
9. Building an Encryption Utility
10. Encrypted Email
11. Decrypting Email
12. Encryption Strength
13. Encryption Methods
14. Encryption Padding
To run any of the examples that are shown below, open a text editor and create a REBOL header line such as:
Then simply cut the example text and paste it after this line. Save the text file and give it a name, such as example.r. Then run the file just as you would run any REBOL file.REBOL [Title: "Example"]
Warning Always keep an unencrypted copy of everything that you encrypt. Otherwise, if you forget the encryption key, or lose the key, or if your encrypted data become damaged, you may lose all or part of your data.
You will need REBOL/View/Pro or REBOL/Command 2.0 to run the examples shown here.
Detailed information about using encryption is provided in the REBOL Encryption Document.
View/Pro has a variety of built-in encryption functions. The examples below use symmetric key encryption. This type of encryption is commonly used to encrypt files where the same key is used for both encryption and decryption.Here is a simple example that encrypts a string. To specify the encryption, you need to open a port with a port object specification. The specification looks like:
The port is opened and returned similar to any other file or network port. The scheme specifies that you want an encryption port. The direction word tells the port whether you are encrypting or decrypting. The key is a binary number that is your secret key for encryption. This same key is required to decrypt your data. (More about this below.) The padding field specifies that you want REBOL to properly maintain the length of your data.port: open [ scheme: 'crypt direction: 'encrypt key: #{1122334455667788} padding: true ]To encrypt a string you use the same series functions that you use on file or network ports. The example below encrypts the string.
First you insert the string into the port. Next, you signal that you want the port to update itself, processing the data that you provided. Finally, you copy the encrypted data out of the port.insert port "This is a string" update port data: copy port
Notice that the data is binary at this point.probe dataDon't forget to close the port when you are finished:
close port
To decrypt the string that you encoded above, all you need to change is the direction of the port. You use the same key and padding. The example below is identical to that above, but specifies DECRYPT for the direction:
The data from the above example is used as input to this decryption port:port: open [ scheme: 'crypt direction: 'decrypt key: #{1122334455667788} padding: true ]
The result of the operation is a binary value. To return it to a string, you must convert it:insert port data update port result: copy port close port
str: to-string result probe string
Notice that the encryption and decryption code above is identical, with the exception of the encryption direction. Because of this, it is helpful to define a function that performs most of the work.The code below defines a function that is used in all the examples that follow:
A full function header is used so you can get help on the function later:crypt: func [ "Encrypts or decrypts data and returns the result." data [any-string!] "Data to encrypt or decrypt" akey [binary!] "The encryption key" /decrypt "Decrypt the data" /binary "Produce binary decryption result." /local port ][ port: open [ scheme: 'crypt direction: pick [encrypt decrypt] not decrypt key: akey padding: true ] insert port data update port data: copy port close port if all [decrypt not binary] [data: to-string data] data ]
The previous examples now become:help crypt
This makes encryption and decryption within your code a lot easier.data: crypt "This is a string" #{1122334455667788} probe crypt/decrypt data #{1122334455667788}
The strength of encryption really comes down to the size and quality of your encryption key. Just like passwords, if a key is short and obvious, it gives someone a greater chance at cracking it.There are two ways to create a key. You can specify your own key as a string or binary value, or you can have a program generate a key.
The examples above used a binary key that was specified within the script:
By default, the strength of the encryption is determined by the length of the key. The key above is 8 bytes, which is 64 bits. So, you can use a much longer binary string if you need greater strength. This key is 128 bits:key: #{1122334455667788}
A key can also be a string:key: #{112233445566778899AABBCCDDEEFF00}
The above keys can be used, however they are far from optimal because they are too simple in their structure. The best way to create a key is to pass the string through an "encoder" that produces a higher quality key.key: "There ain't no such thing as a free lunch."Here is an example of an easy way to encode a key:
Using CHECKSUM with the /SECURE refinement takes that input string and produces a cryptographically secure binary string. This result makes a much better encryption key.key: checksum/secure "This is my pass string."Note that the string returned from CHECKSUM is 160 bits. You can reduce its size with COPY/PART. The example below trims the key to 64 bits (8 bytes):
Here is a handy function that requests the a key phrase from the user, encodes it, and returns it as a result.key64: copy/part key 8
To try it out, do this:request-key: has [pass] [ pass: request-text/title "Enter a pass-phrase:" if pass [checksum/secure pass] ]
The function returns NONE if the user cancels the request.probe request-key
Another way to create a key is to have a program generate it. This can be done in a variety of ways, but here is a simple and dependable way that creates a quality key:
The NOW/PRECISE function returns the date, a detailed time, and the timezone. The FORM converts it to a string which is then encoded with CHECKSUM/SECURE.key: checksum/secure form now/preciseWhen a key is generated by a program, it needs to be stored somewhere, such as on a floppy disk. Store it away from the encrypted data. If you save the key to your disk or send it in an email, then you might as well not encrypt your data.
The functions created above can be used to encrypt a file. Here is an example that takes a file called secrets.r and writes a file called secrets.bin.
Notice that the resulting file is a binary file. If you forget to use WRITE/BINARY you will damage your encrypted data, and it will not be possible to decrypt it later. Remember that.file: %secrets.r key: request-key data: crypt read/binary file key write/binary %secrets.bin data
The file encrypted above can be decrypted with this example:
The secrets2.r file is identical to the original file that was encrypted.file: %secrets.bin key: request-key data: crypt/decrypt/binary read/binary file key write/binary %secrets2.r data
The encrypted file produced above is encoded and cannot be used unless it is decrypted. Because of this, it is often useful to compress the data before it is encrypted. Compressing the data speeds up encryption and decryption processes.
When you encrypt a file, it is compressed:crypt: func [ "Encrypts or decrypts with compression. Returns result." data [any-string!] "Data to encrypt or decrypt" akey [binary!] "The encryption key" /decrypt "Decrypt the data" /binary "Produce binary decryption result." /local port ][ port: open [ scheme: 'crypt direction: pick [encrypt decrypt] not decrypt key: akey padding: true ] if not decrypt [data: compress data] insert port data update port data: copy port close port if decrypt [ data: decompress data if not binary [data: to-string data] ] data ]
When you decrypt the file, it is decompressed:file: %secrets.r key: request-key data: crypt read/binary file key write/binary %secrets.bin data print [size? %secrets.r size? %secrets.bin]
Note that if your data becomes corrupted, the decompression will fail with an error message.file: %secrets.bin key: request-key data: crypt/decrypt/binary read/binary file key write/binary %secrets2.r data
Here is a simple, useful encryption utility that uses the functions above. It asks whether you want encryption or decryption, then lets you select one or more files to be processed.
This utility is posted in the REBOL script library as crypt.r.op: request ["Select action:" "Encrypt" "Decrypt" "Cancel"] if none? op [quit] action: pick ["Encrypt" "Decrypt"] op files: request-file/title join "Select Files to" action action if none? files [quit] key: request-key if none? key [quit] foreach file files [ data: read/binary file key data: either op [crypt data key][crypt/decrypt/binary data key] write/binary file data ]
As easily as you encrypt a file, you can also encrypt an email. Here is a panel that lets you send an encrypted file to someone via email. It is a simple example that embeds the encrypted data into the email message body. It does not use the standard email file attachment format.
The panel looks like:view layout [ style lab label right 80x24 across space 0x4 vh2 "Send Encrypted Email File:" return lab "To:" f-to: field return lab "Subject:" f-sub: field return lab "Key:" f-key: field hide return lab "File:" f-file: text 200x24 white black middle "click to pick" [f-file/text: request-file/keep] return lab button "Send" [unview] button "Close" [quit] ]
It is followed with the code:
The email is sent with the given subject line, and the body of the email contains the base-64 encrypted data.dest: to-email f-to/data file: to-file f-file/data key: checksum/secure f-key/data subject: f-sub/data msg: enbase/base crypt read/binary file 64 send/subject email msg subject
When the above email is received, it needs to be decrypted. Because email is normally received by your email client program, the simplest way to decrypt the email message is to cut and paste the body of the email into the data window in the program below:
The panel looks like:view layout [ style lab label right 80x24 across space 0x4 vh2 "Decrypt Email File:" return lab "Key:" f-key: field hide return lab "File:" f-file: text 200x24 white black middle "click to pick" [f-file/text: request-file/keep] return lab "Data:" f-data: area return lab button "Decrypt" [unview] button "Close" [quit] ]
It is followed with the code:
The decrypted data is written to the file name that you specify.file: to-file f-file/data key: checksum/secure f-key/data data: f-data/data data: debase/base data 64 write/binary file crypt/decrypt/binary data key
In the examples above the length of your key determines the strength of the encryption that is used. In the above examples CHECKSUM/SECURE is used, and the length of the key is 160 bits.You can control the strength of the encryption by specifying it explicitly in the port object. For instance, to reduce the strength to 64 bits, you can write:
The maximum strength allowed for encryption is regulated by US export laws. If you are using an international version of REBOL, your encryption strength may or may not be restricted, depending on your country.port: open [ scheme: 'crypt direction: 'encrypt key: akey strength: 64 padding: true ]The function below indicates whether you have full strength, export strength, or no strength.
If you are using an export version of REBOL, then the strength of your encryption is decreased by reducing the number of bits in the key. To see how many bits were used, you can check the port strength after the port has been opened:print crypt-strength?
port: open [ scheme: 'crypt direction: 'encrypt key: akey strength: 512 padding: true ] print port/strength
The examples above use the default encryption method for symmetric key encryption. The algorithm is called Blowfish and it is well established.The new US Advanced Encryption Standard is also available. The algorithm is called Rijndael. It can be set within the port specification with:
Other types of encryption are also available. REBOL supports RSA public key encryption, DSA digital signature algorithm, and DH (Diffie Hellman) key exchange. These are covered in the REBOL Encryption Document.port: open [ scheme: 'crypt direction: 'encrypt key: akey strength: 512 algorithm: 'rijndael padding: true ] print port/strength
The examples above use padding to allow the results of decryption to be the same length as the data that was encrypted. However, if you require compatibility with data that was encrypted outside of REBOL, or if your encrypted data is decrypted outside of REBOL, then you need to disable padding for compatibility.The example below disables padding:
By default, padding is disabled, so you can simply write:port: open [ scheme: 'crypt direction: 'encrypt key: akey padding: false ]
See the REBOL Encryption Document for a complete discussion about padding.port: open [ scheme: 'crypt direction: 'encrypt key: akey ]