REBOL Encryption
Contents
Encryption Strength
Symmetric Key Encryption
Encrypting/Decrypting Data
Blocking/Padding
Examples
Creating a pseudo-random 128-bit encryption key
Encrypting a file
Decrypting a file
RSA Public Key Encryption
Functions
Examples
Generating a new 1024-bit RSA key
Encrypting a text file using RSA
Decrypting a text file using RSA
Signing a text file using RSA
Verifying a signature using RSA
DSA Digital Signatures
Functions
Examples
Generating a new 1024-bit DSA key
Signing a text file using DSA
Verifying a signature using DSA
DH Diffie Hellman Key Exchange
Functions
Examples
Generating a new DH session key
Computing the key for symmetric key encryption
Overview
The following types of data encryption are available:
- Symmetric key encryption
: Currently the supported algorithms are Blowfish (up to 576 bits) and Rijndael/AES (up to 256 bits). The maximum key length in the export version is 56 bits for both algorithms. - RSA public key encryption
: Unlimited key size (limited to 512 bits in the export version). RSA is primarily used for session key exchange and electronic document signing. - DSA digital signature algorithm
: Unlimited key size (limited to 512 bits in the export version). Used for electronic document signing. - DH (Diffie Hellman) key exchange
: Unlimited key size (limited to 512 bits in the export version). Used for session key exchange.
Encryption Strength
Due to United States export laws, full strength encryption is not available in all countries. To determine the strength of your version, use the crypt-strength? function.
The crypt-strength? function returns the maximum encryption strength as a word. The result can be:
full | Full strength encryption available. |
export | Reduced strength encryption. |
none | No encryption available. |
For example,
if crypt-strength? [print "Encryption is available"]
will indicate that encryption is available. Or:
if crypt-strength? = 'full [print "Full encryption strength"]
will indicate that full strength encryption is available.
Symmetric Key Encryption
Symmetric key encryption uses the 'crypt scheme of the REBOL port mechanism to encrypt and decrypt data. To create a crypt port use
crypt-port: make port! [ scheme: 'crypt algorithm: 'blowfish direction: 'encrypt strength: 128 key: encryption-key padding: true ]
The scheme is always 'crypt. The algorithm currently can be 'blowfish or 'rijndael. The direction is 'encrypt or 'decrypt. The strength can be the number of bits (up to the maximum strength supported by the algorithm), or 'export for the maximum strength supported in the export version.
Padding should usually be set to true, except if interoperability with other (non-REBOL) implementations of the encryption algorithms is required. Please see the "Blocking/Padding" section for more information.
The encryption key has to be set to a binary of at least the size specified in strength. The same key has to be used to both encrypt and decrypt data.
Other parameters that can be set in crypt ports include:
block-chaining: 'cbc init-vector: some-binary
Block-chaining can be 'cbc (cypher block chaining) or 'ecb (electronic cook book). 'cbc is the default, more secure and should generally be used. The 'ecb is available for compatibility with other software. Init-vector is the initialization vector for 'cbc mode. The default is a sequence of binary zeroes.
Encrypting/Decrypting Data
Open a 'crypt port for encryption or decryption. Then use
open crypt-port insert crypt-port some-data (...possibly more insert calls here...) update crypt-port new-data: copy crypt-port close crypt-port
The data to be inserted has to be a string series (e.g. string or binary). The data copied from the port is always of type binary!.
Update has to be called after the last insert call to flush the output buffer and pad the final data block.
It is possible to copy from the port before all data has been written, e.g.
open crypt-port insert crypt-port some-data new-data: copy crypt-port insert crypt-port some-more-data append new-data copy crypt-port ... insert crypt-port even-more-data update crypt-port append new-data copy crypt-port close crypt-port
Please note that because of padding there may not be data to copy after every insert, i.e. after inserting data part of that data may linger in internal block buffers, and the next call to copy does not necessarily contain all the data previously inserted. It is necessary to call update in order to force all inserted data to be padded and encrypted.
Blocking/Padding
Blowfish and Rijndael are block-oriented algorithms. They encrypt data blocks of a fixed size (typically 8 bytes) at a time. Because of that, data has to be padded to the block size for encryption, and the padding has to be removed again after decryption. REBOL currently supports two types of padding:
- padding: true
self-describing padding - padding: false
padded with binary zeroes
Most of the time self-describing padding should be used (padding: true). With this setting REBOL adds its own padding in such a way that during decryption REBOL can remove the padding and return the original data. Example:
insert encrypt-port "test" update encrypt-port encrypted-data: copy encrypt-port insert decrypt-port encrypted-data update decrypt-port print to-string copy decrypt-port == "test"
It is also possible to apply custom padding algorithms to the data to be encrypted, before inserting them into crypt ports. This can be useful for interoperability with existing encryption schemes (SSH, SSL etc.) which have their own padding algorithms. In that case set padding to false. The length of decrypted data will always be a multiple of the encryption block size, padded with binary zeroes. Example:
insert encrypt-port "test" update encrypt-port encrypted-data: copy encrypt-port insert decrypt-port encrypted-data update decrypt-port print to-string copy decrypt-port == "test^@^@^@^@"
Examples
Creating a pseudo-random 128-bit encryption key
crypt-key: copy/part checksum/secure mold now/precise 16
Encrypting a file
crypt-port: make port! [ scheme: 'crypt algorithm: 'blowfish direction: 'encrypt strength: 128 key: crypt-key padding: true ] open crypt-port insert crypt-port read/binary %inputfile update crypt-port write/binary %outputfile copy crypt-port close crypt-port
Decrypting a file
crypt-port: make port! [ scheme: 'crypt algorithm: 'blowfish direction: 'decrypt strength: 128 key: crypt-key padding: true ] open crypt-port insert crypt-port read/binary %outputfile update crypt-port write/binary %inputfile2 copy crypt-port close crypt-port (read/binary %inputfile2) = (read/binary %inputfile) == true
RSA Public Key Encryption
RSA public key encryption is an asymmetric encryption algorithm. In order to encrypt data an RSA key has to be generated. RSA keys have a "public" and a "private" portion. The "private" key has to be kept secret, the "public" key can be distributed. If data is encrypted with the public key then it needs to be decrypted with the private key, and vice versa. This leads to two modes of operation:
Encryption | Use the public key to encrypt, and the private key to decrypt. Anyone can encrypt data, but only the owner of the private key can decrypt it. |
Signing | Use the private key to encrypt, and the public key to decrypt. The public key decryption is used as a verification that the private key was used for encryption, i.e. if the public key decryption succeeds then this is proof that the person encrypting the data had access to the private key (electronic signature). |
RSA is significantly slower than symmetric key encryption algorithms, and a single encryption or decryption operation can only process an amount of data up to the size of the RSA key. For encrypting or decrypting large amounts of data RSA is usually used in combination with symmetric key algorithms or secure checksums as follows:
Encryption | Generate a random session key. Use symmetric key encryption to encrypt the payload with the session key. RSA-encrypt the session key with the public RSA key. Send the encrypted payload and the encrypted session key to the recipient. |
Signing | Use checksum/secure to create a cryptographically secure checksum of the data. RSA-encrypt the checksum with the private RSA key. Send the data and the encrypted checksum to the recipient. |
Functions
rsa-make-key
This function returns an empty object! laid out as an RSA key. It does not fill in any data. The function is mainly used in preparation for creating an RSA key used for public encryption or decryption.
rsa-generate-key rsa-key size generator
This function creates a new, random RSA key (public and private portion), and fills the data into the object! passed as rsa-key. Size is the number of bits (e.g. 1024). Generator is the prime number used as a generator. The typical value is 3. An RSA key object contains the following fields:
- n
Public key. This is the value that has to be published in order to allow public key encryption or decryption. - d
Private key. This value is the secret part of the private key. - e
Generator (usually 3). - p, q, dmp1, dmq1, iqmp
Parts of the secret key, used to improve the performance of private key operations. If only n, d and e are saved for a private key then the key can still be used for private key operations, but those operations will be slower than for a key that contains p, q, dmp1, dmq1 and iqmp. - various mont fields
These are temporary values to further improve performance. They do not have to be saved.
Only the "n" field of a key should be published. The "e" field is usually set to 3 by convention. All other fields have to be kept secret. When saving a private key it is usually most convenient to write the complete molded object to a file, instead of individual fields.
rsa-encrypt rsa-key data /decrypt /private /padding padding-type
This function encrypts or decrypts a binary with an RSA key. The result is another binary. The /decrypt refinement selects decryption (default is encryption). The /private refinement selects encryption or decryption using the private key (default is to use the public key).
The /padding refinement selects the type of padding to use. The default (no /padding refinement) is PKCS1 Type 1 and should be used whenever communication takes place among REBOL scripts only. For compatibility with other environments using RSA the following alternative padding methods are supported:
- none
No padding. Only works if the amount of data to be encrypted is exactly identical to the key size. - 'pkcs1
PKCS1. Type 1 for private key blocks. Type 2 for public key blocks. - 'oaep
PKCS1 v2.0 EME-OAEP (RFC 2437). For public key blocks only. - 'ssl
This is a slight variation of PKCS1 Type 2, used by SSL v2 and v3. For public key blocks only.
Examples
Generating a new 1024-bit RSA key
rsa-key: rsa-make-key rsa-generate-key rsa-key 1024 3
Now rsa-key/n can be distributed as the public key.
Encrypting a text file using RSA
The rsa-pub-key is assumed to contain the RSA public key ('n' value).
rsa-key: rsa-make-key rsa-key/e: 3 rsa-key/n: rsa-pub-key crypt-key: copy/part checksum/secure mold now/precise 16 crypt-port: make port! [ scheme: 'crypt algorithm: 'blowfish direction: 'encrypt strength: 128 key: crypt-key padding: true ] open crypt-port insert crypt-port read/binary %inputfile update crypt-port crypt-data: copy crypt-port close crypt-port crypt-key: rsa-encrypt rsa-key crypt-key
At this time crypt-key and crypt-data have to be sent to the recipient.
Decrypting a text file using RSA
The rsa-key is assumed to be set to the RSA private key generated earlier. The crypt-key and crypt-data have been received from the sender.
crypt-key: rsa-encrypt/private/decrypt rsa-key crypt-key crypt-port: make port! [ scheme: 'crypt algorithm: 'blowfish direction: 'decrypt strength: 128 key: crypt-key padding: true ] open crypt-port insert crypt-port crypt-data update crypt-port write/binary %outputfile copy crypt-port close crypt-port
At this the contents of outputfile are identical to the contents of inputfile at the sender.
Signing a text file using RSA
The rsa-key is assumed to be set to the RSA private key generated earlier.
signature: rsa-encrypt/private rsa-key checksum/secure read %inputfile
At this time signature has to be sent to the recipient, along with the contents of %inputfile.
Verifying a signature using RSA
The rsa-pub-key is assumed to contain the RSA public key ('n' value). signature contains the signature, and %inputfile the data the signature applies to.
rsa-key: rsa-make-key rsa-key/e: 3 rsa-key/n: rsa-pub-key valid-signature?: all [ not error? try [cksum: rsa-encrypt/decrypt rsa-key signature] cksum = checksum/secure read %inputfile ]
DSA Digital Signatures
DSA is used to electronically sign a document. It is similar in functionality to RSA, except it can only be used for signing, not for encryption, and DSA is usually slower than RSA. Like RSA, DSA uses keys which consist of a public and private portion. The public portion has to be distributed to allow signature verification. The private portion has to be kept secret.
DSA can only sign data up to the size of the DSA key. To sign large amounts of data DSA is usually used in combination with secure checksums. This means use checksum/secure on the data to create a cryptographically secure checksum, then use DSA to sign the checksum, and send the signed checksum to the recipient along with the data.
Functions
dsa-make-key /generate length
This function returns an empty object! laid out as a DSA key. It does not fill in any data. The function is mainly used in preparation for creating a DSA key used for signing or signature verification.
The /generate refinement causes the creation of a new "generation environment". A "generation environment" is a set of three numbers (p, q and g) with certain mathematical properties that is shared among a large group of participants and can be used to create DSA keys of a certain size.
The number set describes a convention, is not secret, and is usually hard-coded into applications. It is not part of any individual secret or public key, but is needed in order to perform secret or public key operations. The following generation environments can be used safely:
384 bits: p: #{A8942EC56B32B48DCA3350F414707B74D5D390480BB5016C0FE72CDAFDC442BA 0AF56CC2E8B7AED44A7DB3B25A797428F4E779CCA3C1081B7E7169B364C4323D} q: #{CE7C81B214412D0E1AB5C75D6B863CAB0A40CD17} g: #{235F1E4BE5A88C88146D54F3FDE6093A03631A66B3754C05844EF98E9F38B754 35F39349484DD22B061461E040CA619D4BB416A8DEC655CE11FDE2A1ACE88180} 512 bits: p: #{DEC53F716DA5EB7A8DD3D77525998DB388702ACD81EEE9E58827EA9CD1CD01AE 54B5C185982A1F6F29EFEFAE4D5FF91F0F02C5508F5A9A9A508E599413AB1723} q: #{9BB981CD50EB5C995AE51175B6FA3C5130260A55} g: #{07F85EC72C6F2D4FC5DBD07A341231407EC461DC7973AEA36D796B3671B59EBE FF30FC9828AB278C7A992E00FD4E36E335F2E167E974392A17DA4F6577944141} 1024 bits: p: #{A9FC81DF8EA328CC03D8D1EC88471B56F71A2CF7523C9D10FC60A5D7335D3B00 8670CE2C620F1287B406BBEF35D56B2FDFAA0CFC8E02331126AC0BF9EAF6AFB7 A66DCEEF80B41D5243C70C208F57027544C6733B8D865F9D9B1A04E10584CE1B F9DEAD21AF7F6AC75F4B7D39F3771A01665CFC4F17830644096A94A03F383B75} q: #{FBF92DE23BC5F1CB20F123516B35E4BB2B8290AF} g: #{37EF72A3BEBD342F1FBBCB001906F97A90BBED38AD06CFE9FEE48F243C48E06D ABFED1AE6AA202040D0F8EE612A526F9E1DB6329FD233A27AE5C0440846BB335 6E3D4E994376384FEA2E5E8764EABF89F87816FAAC4F10BA5F002EC9DD33A667 DD2466D8D34BF102432934EAA13ECCF60F19C404BBD386C9679311B98478AFBB}
Please note that creating a new generation environment can take a long time, possibly several minutes, depending on the key size.
dsa-generate-key dsa-key
This function creates a new private/public DSA key pair. Before calling this function a dsa-key object! has to be created using dsa-make-key, and the generation environment (p, q and g) has to be filled in. After calling this function pub-key contains the public key and priv-key contains the private key.
dsa-make-signature /sign dsa-key data
This function creates a signature object. Without the "/sign" refinement an empty signature object is created, ready to be filled in with a signature received in a transmission, for the purpose of signature verification. With the "/sign" refinement dsa-make-signature signs the specified data, and stores the signature in the returned signature object. The dsa-key passed in needs to contain the fields p, q, g, pub-key and priv-key.
Signatures contain two fields, r and s. Both fields together form the signature, and have to be sent to the recipient in order to allow verification of the signature.
dsa-verify-signature dsa-key data signature
This function verifies whether the supplied signature is valid for the supplied data and has been generated by the private key corresponding to the supplied dsa-key. The dsa-key has to be a DSA public key with the fields p, q, g and pub-key filled in. The dsa-verify-signature returns true (valid signature) or false (invalid signature).
Examples
All binary literals are taken from the sample "generation environment" shown above.
Generating a new 1024-bit DSA key
dsa-key: dsa-make-key dsa-key/p: #{A9FC81DF8EA328CC03D8D1EC88471B56F71A2CF7523C9D10FC60A5D7335D3B00 8670CE2C620F1287B406BBEF35D56B2FDFAA0CFC8E02331126AC0BF9EAF6AFB7 A66DCEEF80B41D5243C70C208F57027544C6733B8D865F9D9B1A04E10584CE1B F9DEAD21AF7F6AC75F4B7D39F3771A01665CFC4F17830644096A94A03F383B75} dsa-key/q: #{FBF92DE23BC5F1CB20F123516B35E4BB2B8290AF} dsa-key/g: #{37EF72A3BEBD342F1FBBCB001906F97A90BBED38AD06CFE9FEE48F243C48E06D ABFED1AE6AA202040D0F8EE612A526F9E1DB6329FD233A27AE5C0440846BB335 6E3D4E994376384FEA2E5E8764EABF89F87816FAAC4F10BA5F002EC9DD33A667 DD2466D8D34BF102432934EAA13ECCF60F19C404BBD386C9679311B98478AFBB} dsa-generate-key dsa-key
Now the dsa-key/pub-key can be distributed as the public key.
Signing a text file using DSA
The dsa-key is assumed to be set to the DSA private key generated earlier.
signature: dsa-make-signature/sign dsa-key checksum/secure read %inputfile
At this time signature/r and signature/s have to be sent to the recipient, along with the contents of %inputfile.
Verifying a signature using DSA
The dsa-pub-key is assumed to contain the DSA public key. signature-s and signature-s contain the 'r' and 's' portions of the signature, respectively, and %inputfile the data the signature applies to.
dsa-key: dsa-make-key dsa-key/p: #{A9FC81DF8EA328CC03D8D1EC88471B56F71A2CF7523C9D10FC60A5D7335D3B00 8670CE2C620F1287B406BBEF35D56B2FDFAA0CFC8E02331126AC0BF9EAF6AFB7 A66DCEEF80B41D5243C70C208F57027544C6733B8D865F9D9B1A04E10584CE1B F9DEAD21AF7F6AC75F4B7D39F3771A01665CFC4F17830644096A94A03F383B75} dsa-key/q: #{FBF92DE23BC5F1CB20F123516B35E4BB2B8290AF} dsa-key/g: #{37EF72A3BEBD342F1FBBCB001906F97A90BBED38AD06CFE9FEE48F243C48E06D ABFED1AE6AA202040D0F8EE612A526F9E1DB6329FD233A27AE5C0440846BB335 6E3D4E994376384FEA2E5E8764EABF89F87816FAAC4F10BA5F002EC9DD33A667 DD2466D8D34BF102432934EAA13ECCF60F19C404BBD386C9679311B98478AFBB} dsa-key/pub-key: dsa-pub-key signature: dsa-make-signature signature/r: signature-r signature/s: signature-s valid-signature?: dsa-verify-signature dsa-key checksum/secure read %inputfile signature
DH Diffie Hellman Key Exchange
The Diffie Hellman algorithm is used to negotiate a session key between two computers across an insecure line. An (asymmetric and usually faster) alternative is to use RSA, by having one party pick a random session key, encrypt it with a public key and have the other party decrypt it with the corresponding private key.
DH works as follows: each side creates a DH key object. Each side creates a DH private/public key pair. Each side sends its public key to the other side. Each side computes a session key from its private key and the other side's public key. DH guarantees that both session keys are identical. Those keys can then be used for symmetric key encryption.
Functions
dh-make-key /generate length generator
This function returns an empty object! laid out as a DH key. It does not fill in any data. The function is used in preparation for generating a new DH public/private key pair.
The /generate refinement causes the creation of a new "generation environment". The "generator" argument is part of the generation environment, and usually has the value 2. A "generation environment" is a set of numbers (g, p) with certain mathematical properties that is shared among a large group of participant and can be used to create DH keys of a certain size.
The number set describes a convention, is not secret, and is usually hard-coded into applications. It is not part of any individual DH key, but is needed in order in order to create DH keys and to use DH for exchanging session keys. The following generation environments can be used safely:
384 bits: g: 2 p: #{A6B3DA18CFA6A9DA375DB8863D8C30E6A6D30078A4B3F0FC7CA480AF0EA6EA63 4EECD1D90E58E2EE12EA1A83D978D55F} 512 bits: g: 2 p: #{D4D33F6CDD1B333E8C16130F16965D3D0A1233E45AFB3AA02AB18D31E20910DA 8ED72AEBAA16BCB2250F0C57A54960D4301A7A628E457AC082CBAA11C559F233} 1024 bits: g: 2 p: #{B19958A84CBF18FB833081908979DEA42C4BA113C1E40C48820324C19A277AC5 0B7FFA7184128C169542912A1463FABD50894D13DD7BF8E16B3C8481C43AFDC1 1E9DF980E47FE9B8502BC2F1D4D9D29FA9FADB7B904B2D912907008FC4448B0A 417E40C4AEE2CD6D6CD3E8906EA0E5AF4A80F9E5E4790773302A228502B56143}
Please note that creating a new generation environment can take a VERY long time, possibly several minutes or hours, depending on the key size.
dh-generate-key dh-key
This function creates a new DH public/private key pair. Before calling this function a dh-key object! has to be created using dh-make-key, and the generation environment (g, p) has to be filled in. After calling this function pub-key contains the public key and priv-key contains the private key.
dh-compute-key dh-key pub-key
This function computes a session key from a locally generated DH private key object and the pub-key binary received from the peer. It returns the negotiated session key as a binary.
Examples
All binary literals are taken from the sample "generation environment" shown above. Each of the following steps has to be executed on both sides participating in the key exchange.
Generating a new DH session key
dh-key: dh-make-key dh-key/g: 2 dh-key/p: #{B19958A84CBF18FB833081908979DEA42C4BA113C1E40C48820324C19A277AC5 0B7FFA7184128C169542912A1463FABD50894D13DD7BF8E16B3C8481C43AFDC1 1E9DF980E47FE9B8502BC2F1D4D9D29FA9FADB7B904B2D912907008FC4448B0A 417E40C4AEE2CD6D6CD3E8906EA0E5AF4A80F9E5E4790773302A228502B56143} dh-generate-key dh-key
At this time dh-key/pub-key contains the public portion of the DH session key object and has to be sent to the other side.
Computing the key for symmetric key encryption
The dh-pub-key is assumed to contain the public key received from the other side.
crypt-key: copy/part checksum/secure dh-compute-key dh-key dh-pub-key 16
At this time crypt-key contains the 128-bit key for symmetric key encryption. Both sides have calculated the same key.