1 Overview
1.1 Installation
1.2 License
2 API Organization
3 Message Digests
3.1 Digest Algorithms
digest: md5
digest: ripemd160
digest: dss1
digest: sha1
digest: sha224
digest: sha256
digest: sha384
digest: sha512
available-digests
!digest?
digest-size
3.2 Computing Digests
digest
md5
ripemd160
dss1
sha1
sha224
sha256
sha384
sha512
hmac
3.3 Low Level Digest Operations
digest-new
digest-update!
digest-final!
digest-copy
digest->bytes
digest?
hmac-new
hmac-update!
hmac-final!
hmac?
4 Symmetric Ciphers
4.1 Cipher Algorithms
cipher: des
cipher: des-ede
cipher: des-ede3
cipher: idea
cipher: bf
cipher: cast5
cipher: aes-128
cipher: aes-192
cipher: aes-256
cipher: camellia-128
cipher: camellia-192
cipher: camellia-256
available-ciphers
!cipher?
cipher-block-size
cipher-key-length
cipher-iv-length
4.2 Encryption and Decryption
encrypt
decrypt
4.3 Low Level Cipher Operations
cipher-encrypt
cipher-decrypt
cipher-update!
cipher-final!
cipher?
cipher-encrypt?
5 Public Key Cryptography
5.1 Algorithms and Keys
pkey: rsa
pkey: dsa
!pkey?
pkey?
pkey-private?
pkey->public-key
public-key->bytes
private-key->bytes
bytes->public-key
bytes->private-key
pkey-size
pkey-bits
pkey=?
5.2 Signatures
sign
verify
digest-sign
digest-verify
5.3 Encryption
encrypt/ pkey
decrypt/ pkey
encrypt/ envelope
decrypt/ envelope
6 Diffie-Hellman Key Exchange
dh: 192
dh: 512
dh: 1024
dh: 2048
dh: 4096
compute-key
dhkey?
!dh?
dh-bites
7 Utilities
7.1 Key Generation
generate-key
7.2 Randomness
random-bytes
random-bytes!
pseudo-random-bytes
pseudo-random-bytes!
random-rnd-status
random-rnd-add
random-rnd-seed
random-rnd-read
random-rnd-write
random-rnd-filename
7.3 Engine Support
engine-load-builtin
engine-cleanup
7.4 Miscellaneous
hex
unhex
bytes-xor
bytes-xor!
shrink-bytes
8 Examples
8.1 Message Digests
8.2 Ciphers
8.3 Public Key Cryptography
8.4 Diffie-Hellman Key Exchange
Version: 4.1.3.3

mzcrypto

1 Overview

mzcrypto is a cryptographic library for mzscheme.

The library provides a high level interface for accessing primitives from libcrypto. To use this library you will need OpenSSL (0.9.8 or later) installed on your system.

1.1 Installation

To use the library through PLaneT:

  > (require (planet vyzo/crypto))

To locally install the library, extract the library archive to your collects directory and run

  setup-plt -l crypto

To use the local library:

  > (require crypto)

To run basic tests on the library:

  > (require (planet vyzo/crypto/test))

or if you have locally installed:

  > (require crypto/test)

  > (run-tests)

1.2 License

(C) Copyright 2007,2008 Dimitris Vyzovitis <vyzo at media.mit.edu>

mzcrypto is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.

mzcrypto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public License along with mzcrypto. If not, see <http://www.gnu.org/licenses/>.

2 API Organization

 (require crypto)
 (require (planet vyzo/crypto))

The API provided by mzcrypto is conceptually organized in 5 sections:

Each section documents the relevant scheme bindings, with tutorial-style examples in Examples

3 Message Digests

3.1 Digest Algorithms

A <digest> is a first class object which captures algorithm details. The set of digest algorithms depends on the local libcrypto configuration and is determined at module load-time.

digest:md5 : <digest>
digest:ripemd160 : <digest>
digest:dss1 : <digest>
digest:sha1 : <digest>
digest:sha224 : <digest>
digest:sha256 : <digest>
digest:sha384 : <digest>
digest:sha512 : <digest>

Digest algorithms. Bound to #f when an algorithm is unavailable.

(available-digests)  (list symbol?)

List of available digest names.

(!digest? o)  boolean?
  o : _

True if o is a <digest>.

(digest-size o)  exact-nonnegative-integer?
  o : (or <digest> digest? hmac?)

The block size of a digest algorithm

3.2 Computing Digests

(digest t inp)  bytes?
  t : <digest>
  inp : (or bytes? input-port?)

Computes a digest for inp using t as the digest algorithm.

(md5 inp)  bytes?
  inp : (or bytes? input-port?)
(ripemd160 inp)  bytes?
  inp : (or bytes? input-port?)
(dss1 inp)  bytes?
  inp : (or bytes? input-port?)
(sha1 inp)  bytes?
  inp : (or bytes? input-port?)
(sha224 inp)  bytes?
  inp : (or bytes? input-port?)
(sha256 inp)  bytes?
  inp : (or bytes? input-port?)
(sha384 inp)  bytes?
  inp : (or bytes? input-port?)
(sha512 inp)  bytes?
  inp : (or bytes? input-port?)

Shortcuts for (digest <digest> inp).

(hmac t key inp)  bytes?
  t : <digest>
  key : bytes?
  inp : (or bytes? input-port?)

Computes an HMAC for inp using t as the digest algorithm and key as the authentication key.

3.3 Low Level Digest Operations

Low level operations are performed on digest contexts for message digest computations and hmac contexts for hmac computations.

(digest-new t)  digest?
  t : <digest>

Create and initialize a new digest context

(digest-update! o data [start end])  _
  o : digest?
  data : bytes?
  start : exact-nonnegative-integer? = 0
  end : exact-nonnegative-integer? = (bytes-length data)

Incrementally update a digest context.

(digest-final! o)  bytes?
  o : digest?
(digest-final! o outp [start end])  exact-nonnegative-integer?
  o : digest?
  outp : bytes?
  start : exact-nonnegative-integer? = 0
  end : exact-nonnegative-integer? = (bytes-length outp)

Finalize the digest context.

The first form returns the output; The second form writes the output in outp which must have enough room for the digest and return the digest size.

(digest-copy o)  digest?
  o : digest?

Copies a digest context, which must not be finalized.

(digest->bytes o)  bytes?
  o : digest?

Returns the current value of the digest.

(digest? o)  boolean?
  o : _

True if o is a digest context.

(hmac-new t key)  hmac?
  t : <digest>
  key : bytes?

Create and initialize a hmac context

(hmac-update! o data [start end])  _
  o : hmac?
  data : bytes?
  start : exact-nonnegative-integer? = 0
  end : exact-nonnegative-integer? = (bytes-length data)

Incrementally update an hmac context.

(hmac-final! o)  bytes?
  o : hmac?
(hmac-final! o outp [start end])  exact-nonnegative-integer?
  o : hmac?
  outp : bytes?
  start : exact-nonnegative-integer? = 0
  end : exact-nonnegative-integer? = (bytes-length outp)

Finalize an hmac context.

(hmac? o)  boolean?
  o : _

True if o is an hmac context.

4 Symmetric Ciphers

4.1 Cipher Algorithms

A <cipher> is a first class object which captures cipher algorithm details. The set of cipher algorithms depends on the local libcrypto configuration and is determined at module load-time.

cipher:des : <cipher>
cipher:des-ede : <cipher>
cipher:des-ede3 : <cipher>
cipher:idea : <cipher>
cipher:bf : <cipher>
cipher:cast5 : <cipher>
cipher:aes-128 : <cipher>
cipher:aes-192 : <cipher>
cipher:aes-256 : <cipher>
cipher:camellia-128 : <cipher>
cipher:camellia-192 : <cipher>
cipher:camellia-256 : <cipher>

Cipher algorithms. Bound to #f when an algorithm is unavailable.

The default mode of operation is cbc. Different modes are bound to <cipher>-mode, where mode is one of (ecb cbc cfb ofb).

(available-ciphers)  (list symbol?)

List of available cipher names.

(!cipher? o)  boolean?
  o : _

True if o is a <cipher>.

(cipher-block-size o)  exact-nonnegative-integer?
  o : (or <cipher> cipher?)
(cipher-key-length o)  exact-nonnegative-integer?
  o : (or <cipher> cipher?)
(cipher-iv-length o)  exact-nonnegative-integer?
  o : (or <cipher> cipher?)

Return the block size, key length, and iv length of o

4.2 Encryption and Decryption

(encrypt c key iv)  
input-port? output-port?
  c : <cipher>
  key : bytes?
  iv : bytes?
(encrypt c key iv inp)  input-port?
  c : <cipher>
  key : bytes?
  iv : bytes?
  inp : (or bytes? input-port?)
(encrypt c key iv inp outp)  _
  c : <cipher>
  key : bytes?
  iv : bytes?
  inp : (or bytes? input-port?)
  outp : (output-port?)

Encrypt with cipher c, using key as the secret key and iv as the initialization vector.

The first form creates an encryption pipe. The result is two values, an input-port where ciphertext can be read from and an output-port where plaintext should be written to.

In the second form, when inp is a port a half-pipe is created that reads plaintext from inp; the result is a ciphertext input-port. When inp is a byte string, then it is synchronously encrypted returning the ciphertext.

The third form synchronously encrypts plaintext from inp, writing ciphertext to outp.

(decrypt c key iv)  
input-port? output-port?
  c : <cipher>
  key : bytes?
  iv : bytes?
(decrypt c key iv inp)  input-port?
  c : <cipher>
  key : bytes?
  iv : bytes?
  inp : (or bytes? input-port?)
(decrypt c key iv inp outp)  _
  c : <cipher>
  key : bytes?
  iv : bytes?
  inp : (or bytes? input-port?)
  outp : (output-port?)

Decrypt with cipher c, using key as the secret key and iv as the initialization vector.

Semantics of arguments and return values are symmetric to encrypt.

4.3 Low Level Cipher Operations

Low level operations are performed on cipher contexts. The same set of operations is used both for encryption and decryption, depending on the initialization of the context.

(cipher-encrypt t key iv [#:padding pad?])  cipher?
  t : <cipher>
  key : bytes?
  iv : bytes?
  pad? : boolean? = #t
(cipher-decrypt t key iv [#:padding pad?])  cipher?
  t : <cipher>
  key : bytes?
  iv : bytes?
  pad? : boolean? = #t

Create and initialize a cipher context for encryption or decryption respectively.

(cipher-update! c ibs)  bytes?
  c : cipher?
  ibs : bytes?
(cipher-update! c ibs obs)  exact-nonnegative-integer?
  c : cipher?
  ibs : bytes?
  obs : bytes?
(cipher-update! c    
  ibs    
  obs    
  istart    
  iend    
  ostart    
  oend)  exact-nonnegative-integer?
  c : cipher?
  ibs : bytes?
  obs : bytes?
  istart : exact-nonnegative-integer?
  iend : exact-nonnegative-integer?
  ostart : exact-nonnegative-integer?
  oend : exact-nonnegative-integer?

Incrementally update a cipher context, with input ibs.

The first form returns the output of the update; the other two forms write the output in obs, which must have room for at least (cipher-block-length c) plus the input length, and return the number of bytes written.

(cipher-final! c)  bytes?
  c : cipher?
(cipher-final! c obs [ostart oend])  exact-nonnegative-integer?
  c : cipher?
  obs : bytes?
  ostart : exact-nonnegative-integer? = 0
  oend : exact-nonnegative-integer? = (bytes-length obs)

Finalize a cipher context.

The first form returns the final output block; the second form writes the final output block in obs, which must have room for at least (cipher-block-size c) bytes and return the number of bytes written.

(cipher? o)  boolean?
  o : _

True if o is a cipher context.

(cipher-encrypt? o)  boolean?
  o : cipher?

True if o is a cipher-context used for encryption.

5 Public Key Cryptography

5.1 Algorithms and Keys

A <pkey> is a first class object which captures public key algorithm details. Key-pairs can be generated using generate-key with a <pkey>.

pkey:rsa : <pkey>
pkey:dsa : <pkey>

Builtin <pkey> algorithms.

(!pkey? o)  boolean?
  o : _

True if o is a <pkey>.

(pkey? o)  boolean?
  o : _

True if o is a public or private key.

(pkey-private? o)  boolean?
  o : pkey?

True if o is a private key.

(pkey->public-key o)  pkey?
  o : pkey?

Extracts the public key component.

(public-key->bytes o)  bytes?
  o : pkey?
(private-key->bytes o)  bytes?
  o : pkey?
(bytes->public-key bs)  pkey?
  bs : bytes?
(bytes->private-key bs)  pkey?
  bs : bytes?

Conversions between keys and bytes.

(pkey-size o)  exact-nonnegative-integer?
  o : pkey?
(pkey-bits o)  exact-nonnegative-integer?
  o : pkey?

The size of a key in bytes and bits respectively.

(pkey=? x ...+)  boolean?
  x : pkey?

Key equality predicate.

5.2 Signatures

(sign pk t data)  bytes?
  pk : pkey?
  t : <digest>
  data : (or bytes? input-port?)

Computes a signature, using the private key pk and t as the digest type.

Note: As of openssl-0.9.8 only certain types of digests can be used with specific public key algorithms. Specifically, pkey:rsa keys can only sign using sha* and ripemd160 as digests, while pkey:dsa can only sign with dss1 digests.

This restriction has been removed in development versions of openssl (0.9.9).

(verify pk t sig data)  boolean?
  pk : pkey?
  t : <digest>
  sig : bytes?
  data : (or bytes? input-port?)

Verifies a signature sig, using the public key pk and t as the digest type.

(digest-sign dg pk)  bytes?
  dg : digest?
  pk : pkey?
(digest-sign dg pk bs [start end])  exact-nonnegative-integer?
  dg : digest?
  pk : pkey?
  bs : bytes?
  start : exact-nonnegative-integer? = 0
  end : exact-nonnegative-integer? = (bytes-length bs)
(digest-verify dg pk bs [start end])  boolean?
  dg : digest?
  pk : pkey?
  bs : bytes?
  start : exact-nonnegative-integer? = 0
  end : exact-nonnegative-integer? = (bytes-length bs)

Signature and verification using digest contexts directly.

5.3 Encryption

(encrypt/pkey pk data [start end])  bytes?
  pk : pkey?
  data : bytes?
  start : exact-nonnegative-integer? = 0
  end : exact-nonnegative-integer? = (bytes-length data)
(decrypt/pkey pk data [start end])  bytes?
  pk : pkey?
  data : bytes?
  start : exact-nonnegative-integer? = 0
  end : exact-nonnegative-integer? = (bytes-length data)

Encrypt and decrypt using a public/private key.

(encrypt/envelope pk c arg ...)  
bytes? bytes? _ ...
  pk : pkey?
  c : <cipher>
  arg : _

Encrypt using c as the <cipher> with a random key sealed using the public key pkey.

Returns the sealed key and iv for the cipher, prepended to the values returned by the nested encrypt.

(decrypt/envelope pk c sk iv arg ...)  
_ ...
  pk : pkey?
  c : <cipher>
  sk : bytes?
  iv : bytes?
  arg : _

Decrypt using c as the <cipher>, using the sealed key sk decrypted with the private key pk.

6 Diffie-Hellman Key Exchange

Diffie-Hellman key parameters are encapsulated in instances of <dh>. Keys can be generated from a parameter instance using generate-key.

dh:192 : <dh>
dh:512 : <dh>
dh:1024 : <dh>
dh:2048 : <dh>
dh:4096 : <dh>

Pre-computed Diffie-Hellman parameters from the OpenSSL project.

(compute-key priv key)  bytes?
  priv : dhkey?
  key : bytes?

Computes a shared key using the private key priv and the peer public key key.

(dhkey? o)  boolean?
  o : _

True if o is a Diffie-Hellman key.

(!dh? o)  boolean?
  o : _

True if o is a <dh> parameter object.

(dh-bites o)  exact-nonnegative-integer?
  o : <dh>

The size in bites of the keys generated from o.

7 Utilities

7.1 Key Generation

(generate-key t)  
bytes? bytes?
  t : <cipher>
(generate-key t bits arg ...)  pkey?
  t : <pkey>
  bits : exact-nonnegative-integer?
  arg : _
(generate-key t)  
dhkey? bytes?
  t : <dh>

Random key generation.

When t is a <cipher> instance the returned values are a fresh key and iv for the algorithm.

When t is a <pkey> instance the bits argument specifies the size of the requested key and the returned value is a fresh pkey. For pkey:rsa the function optionally accepts an exponent argument (defaults to 65537).

Finally, when t is a <dh> instance the returned values are the private dh key and the public key part for the exchange.

7.2 Randomness

(random-bytes len)  bytes?
  len : exact-nonnegative-integer?
(random-bytes! o [start end])  bytes?
  o : bytes?
  start : exact-nonnegative-integer? = 0
  end : exact-nonnegative-integer? = (bytes-length o)

Generate cryptographically secure random data.

(pseudo-random-bytes len)  bytes?
  len : exact-nonnegative-integer?
(pseudo-random-bytes! o [start end])  bytes?
  o : bytes?
  start : exact-nonnegative-integer? = 0
  end : exact-nonnegative-integer? = (bytes-length o)

Generate pseudorandom data (not cryptographically secure).

(random-rnd-status)  boolean?
(random-rnd-add o)  _
  o : bytes?
(random-rnd-seed o)  _
  o : bytes?
(random-rnd-read f len)  integer?
  f : path
  len : exact-nonnegative-integer?
(random-rnd-write f)  integer?
  f : path
(random-rnd-filename)  path?

Query and manipulate the random entropy pool.

In general, you should not have to use these functions directly as libcrypto automatically refreshes the entropy pool using OS-provided cryptographic facilities.

7.3 Engine Support

(engine-load-builtin)  _
(engine-cleanup)  _

engine-load-builtin loads the builtin accelerated libcrypto engine implementations.

The application must cleanup by explicitly calling engine-cleanup as there is currently no reliable way to automatically cleanup using ffi.

7.4 Miscellaneous

 (require crypto/util)
 (require (planet vyzo/crypto/util))

This module provides some additional utilities that are not exported by the main crypto library.

(hex o)  bytes?
  o : bytes?
(unhex o)  bytes?
  o : bytes

hex-encode and decode a byte-string

(bytes-xor in key)  bytes?
  in : bytes?
  key : bytes?
(bytes-xor! in key)  bytes?
  in : bytes?
  key : bytes?

Compute the bitwise-xor of two byte-strings; bytes-xor! computes the result in-place by mutating in.

key must be at least as long as in.

(shrink-bytes o len)  bytes?
  o : bytes?
  len : exact-nonnegative-integer?

Returns (subbytes o len) when o is longer than len and o otherwise.

8 Examples

In order to run the examples, you should first require the library and the utilities module.

Using planet:

  (require (planet vyzo/crypto) (planet vyzo/crypto/util))

Or if you have locally installed:

  (require crypto crypto/util)

In the following we use this definition for msg

  (define msg #"There is a cat in the box.")

8.1 Message Digests

Message digests are computed in two fundamental ways: one-shot or incrementally.

For one shot digest computations one can use the named digest functions or the generic digest function:

  (hex (sha1 msg))
  => #"2f888f0fa9a7cdd78fbbb15816f492d14b252e23"
  (hex (sha1 (open-input-bytes msg))) ; using a port
  => #"2f888f0fa9a7cdd78fbbb15816f492d14b252e23"
  (hex (digest digest:sha1 msg)) ; can use a port as well
  => #"2f888f0fa9a7cdd78fbbb15816f492d14b252e23"

For incremental computation we use digest-new, digest-update!, and digest-final!.

  (let ((dg (digest-new digest:sha1)))
    (digest-update! dg msg)
    (digest-final! dg))
  => #"2f888f0fa9a7cdd78fbbb15816f492d14b252e23"

HMACs can be computed using hmac:

  (let ((hkey (random-bytes (digest-size digest:sha1))))
    (hex (hmac digest:sha1 hkey msg)))
  => #"8ef155b9b05d11970241401eb23678df5db44686"

8.2 Ciphers

In the following we encrypt msg using AES-128 (in the default cbc mode).

First, we need to generate a key/iv pair:

  (define-values (key iv) (generate-key cipher:aes-128))

To encrypt and decrypt directly:

  (let ((ct (encrypt cipher:aes-128 key iv msg)))
    (decrypt cipher:aes-128 key iv ct))
  => #"There is a cat in the box."

To encrypt and decrypt using ports:

  (let* ((cin (encrypt cipher:aes-128 key iv (open-input-bytes msg)))
         (pin (decrypt cipher:aes-128 key iv cin)))
    (read-bytes 128 pin))
  => #"There is a cat in the box."

Using pipes:

  (let-values (((pin) (open-input-bytes msg))
               ((cin cout) (make-pipe))
               ((pout) (open-output-bytes)))
    (encrypt cipher:aes-128 key iv pin cout)
    (close-output-port cout)
    (decrypt cipher:aes-128 key iv cin pout)
    (get-output-bytes pout))
  => #"There is a cat in the box."

The pipe interface is quite flexible: encrypt can create a ciphertext input-port and a plaintext output-port, while decrypt can create a plaintext input-port and a ciphertext output-port. The ports can be connected:

  (let-values (((cin pout) (encrypt cipher:aes-128 key iv))
               ((pin cout) (decrypt cipher:aes-128 key iv)))
        (write-bytes msg pout)
        (close-output-port pout)
        (write-bytes (read-bytes 128 cin) cout)
        (close-output-port cout)
        (read-bytes 128 pin))
  => #"There is a cat in the box."

Finally, the most general interface is the low level interface, used internally to implement the high level operations illustrated above. cipher-encrypt and cipher-decrypt create cipher contexts; the contexts can be updated incrementally with cipher-update!, while cipher-final! completes the operation.

In this vain, we can implement custom encryption and decryption functions:

  (define (my-encrypt key iv)
    (lambda (ptext)
      (let ((octx (cipher-encrypt cipher:aes-128 key iv)))
        (bytes-append (cipher-update! octx ptext)
                      (cipher-final! octx)))))
  
  (define (my-decrypt key iv)
    (lambda (ctext)
      (let ((ictx (cipher-decrypt cipher:aes-128 key iv)))
        (bytes-append (cipher-update! ictx ctext)
                      (cipher-final! ictx)))))
  
  ((my-decrypt key iv) ((my-encrypt key iv) msg))
  => #"There is a cat in the box."

8.3 Public Key Cryptography

The public key API uses the pkey type to encapsulate keys:

  (define privk (generate-key pkey:rsa 1024))
  (define pubk (pkey->public-key privk)) ; the public key

Signatures are computed using sign with the private key, and verified using verify with the public key:

  (let ((sig (sign privk digest:sha1 msg)))
    (verify pubk digest:sha1 sig msg))
  => #t

The key pair can be used for direct encryption as well:

  (let ((ct (encrypt/pkey pubk msg)))
    (decrypt/pkey privk ct))
  => #"There is a cat in the box."

However, public keys are rarely used directly for encryption. Rather, the key pair is used to encrypt/decrypt a sealed key and then perform symmetric encryption using a cipher. This pattern is simplified using encrypt/envelope and decrypt/envelope:

  (let-values (((skey iv ct) (encrypt/envelope pubk cipher:aes-128 msg)))
    (decrypt/envelope privk cipher:aes-128 skey iv ct))
  => #"There is a cat in the box."

8.4 Diffie-Hellman Key Exchange

In order to perform a DH key exchange, a pair of peers each generates a key pair using generate-key and exchange the public keys. The shared key can then be computed using compute-key:

  (define-values (priv1 pub1) (generate-key dh:1024))
  (define-values (priv2 pub2) (generate-key dh:1024))
  (define sk1 (compute-key priv1 pub2))
  (define sk2 (compute-key priv2 pub1))
  (equal? sk1 sk2)
  => #t