15. Bigloo The Programming Language (4.6a) User manual for version 4.6a January 2025 -- Cryptography

main page

top: | Bigloo The Programming Language (4.6a) User manual for version 4.6a January 2025

Cryptography

Chapters

Bigloo provides several functions for encrypting and decrypting documents. These are described in the chapter. Unless explicitly mentioned all functions presented in this document are accessible via the `crypto` library.
None of the cryptographic functions are protected against timing attacks. No effort has been spent on protecting used memory.
Here is an example of a module that uses this library:

    ;; Encrypt a string using AES.
    (module aes-encrypt
       (library crypto)
       (main main))

    (define (main argv)
       (when (and (pair? (cdr argv)) (pair? (cddr argv)))
          (let ((encrypt? (string=? "-e" (cadr argv)))
                (passwd (caddr argv))
                (input (read-string)))
             (if encrypt?
                 (display (encrypt 'aes input passwd))
                 (display (decrypt 'aes input passwd))))))

### 15.1 Symmetric Block Ciphers

Bigloo supports some common block ciphers. Block ciphers work on blocks of fixed size. A mode of operation defines the way bigger input is handled. For instance in ECB (Electronic Codebook mode) the blocks are all encrypted separately, whereas CBC (Cipher-Block Chaining) chains all blocks.
All modes that chain the blocks need an IV (Initial Vector) to ``bootstrap'' the chaining.
Block ciphers by themselves can only work on full blocks. Some modes are constructed in a way that even incomplete blocks can be safely processed. For the remaining blocks a padding function needs to be given.
Most block ciphers only work with keys of specific length. The following functions take passwords (strings of arbitrary length) as input, and preprocess the given password by a :string->key function. The result must then be of correct length.

encrypt::bstring cipher plain password [:string->key] [:mode 'cfb] [:IV #f] [:pad 'none] [:nonce-init!] [:nonce-update!] | bigloo cryptography procedure

encrypt-string::bstring cipher plaintext::bstring password [:string->key] [:mode 'cfb] [:IV #f] [:pad 'none] [:nonce-init!] [:nonce-update!] | bigloo cryptography procedure

encrypt-mmap::bstring cipher plaintext::mmap password [:string->key] [:mode 'cfb] [:IV #f] [:pad 'none] [:nonce-init!] [:nonce-update!] | bigloo cryptography procedure

encrypt-port::bstring cipher plaintext::input-port password [:string->key] [:mode 'cfb] [:IV #f] [:pad 'none] [:nonce-init!] [:nonce-update!] | bigloo cryptography procedure

encrypt-file::bstring cipher filename::bstring password [:string->key] [:mode 'cfb] [:IV #f] [:pad 'none] [:nonce-init!] [:nonce-update!] | bigloo cryptography procedure

encrypt-sendchars cipher in::input-port out::output-port password [:string->key] [:mode 'cfb] [:IV #f] [:pad 'none] [:nonce-init!] [:nonce-update!] | bigloo cryptography procedure

The procedure `encrypt` encrypts its input using the chosen `cipher`. The result is returned as string. `encrypt` dispatches depending on the type of `plain`. Strings are processed by `encrypt-string` (and not `encrypt-file`).
The function `encrypt-sendchars` reads from an input-port `in` and encrypts its output directly into an output-port `out`.
The symbol `cipher` can be one of:

- `des`: Data Encryption Standard (DES). DES works on blocks of 64 bits. DES requires keys of length 64 (bits), but only 56 of these bits are actually used. Bigloo's implementation therefore accepts both. DES is considered to be insecure and its usage is discouraged.
- `des3`: Triple DES, Triple Data Encryption Algorithm (DES3, TDEA). DES3 works on blocks of 64 bits. DES3 requires keys of length 128 or 192 (bits), but only 112/168 of these bits are actually used. Bigloo's implementation therefore accepts the smaller keys too.
  Bigloo's DES3 implementation has been changed with release 3.4b. Earlier versions did not use the full key for en/decryption.
- `des-np`: Same as `des`, but the initial and final permutations are not performed.
- `des3-np`: Same as `des3`, but the initial and final permutations are not performed.
- `aes`: Advanced Encryption Standard (AES). AES works on blocks of 128 bits. AES requires keys of length 128, 192 or 256 bits.
- `cast-128`: CAST-128 (CAST5). CAST-128 works on blocks of 64 bits. CAST-128 requires a key-length of 40-128 bits.
- `idea`: International Data Encryption Algorithm (IDEA). IDEA works on blocks of 64 bits. It requires keys of length 128 (in bits). IDEA is patented in many countries (including the USA and most European countries) but it is free for non-commercial use.

The given password must be a string. An optional parameter `:string->key` should transform this password so that it has the correct length for the cipher. A small list of possible functions are provided in the String to Key section.
By default `string->key-hash` with SHA-1 will be used. The key-length will depend on the chosen cipher:

- `des`: 56 bits.
- `des3`: 112 bits.
- `des-np`: Same as `des`.
- `des3-np`: Same as `des3`.
- `aes`: 192 bits.
- `cast-128`: 128 bits.
- `idea`: 128 bits.

Bigloo supports the following block cipher modes (`:mode`):

- `ecb`: Electronic codebook.
- `cbc`: Cipher-block chaining.
- `pcbc`: Propagating cipher-block chaining.
- `cfb`: Cipher feedback.
- `ofb`: Output feedback.
- `ctr`: Counter.

By default `cfb` is chosen.
Electronic codebook mode en/decodes each block independently and is hence the closest to the block cipher. It is however inherently unsafe as blocks with the same content are encrypted to the same output.
With the exception of `ecb` all other modes can be initialized with an IV (Initialization vector). If `:IV` is false, then a random one will be generated. During encryption this randomly generated IV will be prefixed to the result. When calling the decryption routine without any IV the procedure will use the first block of the input as IV.
In `ctr` (counter) mode the IV parameter serves as nonce. Two additional key-parameters `:nonce-init` and `:nonce-update` are then used to initialize and update the block-sized nonce string. Before encrypting the first block `nonce-init` will be invoked with an empty block-sized string and the initial nonce (IV). It must initialize the string with the nonce. For each block `nonce-update` will be called with the string, the nonce, and the number of already encrypted blocks (hence 0 at the very beginning). By default `nonce-init` takes the IV-string and blits it into the given string. `nonce-update` simply increments the string (treating the given string as one big number).
Note that the initial nonce (passed using IV) may be of any type. As long as `nonce-init` and `nonce-update` correctly initialize and update the passed string.
The input's length of modes `ecb`, `cbc` and `pcbc` must be a multiple of the block-size. Should this not be the case a padding algorithm must be specified (`:pad`). Currently are implemented (examples for hexadecimal string ``DD'' and cipher block size 4):

- `none`: No padding. Raises an error should the input not be a multiple.
- `bit`: Bit padding. Add a '1' bit and then '0' bits. Example: ``DD 80 00 00''.
- `ansi-x.923`: Byte padding. Fill with #x00s followed by the number of added bytes (the counter inclusive). Example: ``DD 00 00 03''.
- `iso-10126`: Fill with random characters followed by the number of added bytes (the counter inclusive). Example: ``DD 42 31 03''.
- `pkcs7`: Fill with the number of added bytes. Example: ``DD 03 03 03''.
- `zero`: Fill with zeros. This is only reversible if the input is guaranteed not to finish with a zero character. Example: ``DD 00 00 00''.

Alternatively users can supply their own (un)pad functions (instead of a symbol). The signature of a padding function is `(pad::bool str::bstring valid-chars::long)`. It receives the last block of the input. Should the input be of correct length then the an empty block will be sent to the padding function. `valid-chars` indicates the number of read characters. It ranges from 0 to blocksize-1. The padding function should fill the block and return `#t` if this last block should be encoded. By returning `#f` the last block will be discarded. This makes only sense if `valid-chars` was equal to 0.
The unpadding procedure has the signature `(unpad::long str::bstring)`. The input string will have the length of the block-size. The unpadding function may modify the string and must return the number of characters that are valid.

decrypt::bstring cipher ciphertext password [:string->key] [:mode 'cfb] [:IV #f] [:pad 'none] [:nonce-init!] [:nonce-update!] | bigloo cryptography procedure

decrypt-string::bstring cipher ciphertext::bstring password [:string->key] [:mode 'cfb] [:IV #f] [:pad 'none] [:nonce-init!] [:nonce-update!] | bigloo cryptography procedure

decrypt-mmap::bstring cipher ciphertext::mmap password [:string->key] [:mode 'cfb] [:IV #f] [:pad 'none] [:nonce-init!] [:nonce-update!] | bigloo cryptography procedure

decrypt-port::bstring cipher ciphertext::input-port password [:string->key] [:mode 'cfb] [:IV #f] [:pad 'none] [:nonce-init!] [:nonce-update!] | bigloo cryptography procedure

decrypt-file::bstring cipher filename::bstring password [:string->key] [:mode 'cfb] [:IV #f] [:pad 'none] [:nonce-init!] [:nonce-update!] | bigloo cryptography procedure

decrypt-sendchars cipher in::input-port out::output-port password [:string->key] [:mode 'cfb] [:IV #f] [:pad 'none] [:nonce-init!] [:nonce-update!] | bigloo cryptography procedure

Counterpart to the encryption functions. With the same parameters the `decrypt` function will decrypt the result of an `encrypt` call. Without `:IV` (Initial Vector) the `decrypt` function will use the first block as IV.

For compatibility the following functions remain in Bigloo. They are in the default library and not inside the `crypto` library.

aes-ctr-encrypt text password [nbits 128] | bigloo procedure

aes-ctr-encrypt-mmap mmap password [nbits 128] | bigloo procedure

aes-ctr-encrypt-string string password [nbits 128] | bigloo procedure

aes-ctr-encrypt-port iport password [nbits 128] | bigloo procedure

aes-ctr-encrypt-file filename password [nbits 128] | bigloo procedure

These functions are equivalent to a call to `aes-encrypt` with mode set to `ctr` and a special `:string->key` parameter. The optional argument `nbits` must either be `128`, `192`, or `256` and determines the size of the key.

aes-ctr-decrypt text password [nbits 128] | bigloo procedure

aes-ctr-decrypt-mmap mmap password [nbits 128] | bigloo procedure

aes-ctr-decrypt-string string password [nbits 128] | bigloo procedure

aes-ctr-decrypt-port iport password [nbits 128] | bigloo procedure

aes-ctr-decrypt-file filename password [nbits 128] | bigloo procedure

Counterpart to `aes-ctr-encrypt`.

### 15.1.1 String to Key

The following string->key algorithms take a password string and transform it to a key string of a given length. In all the functions the `len` is expressed in bytes.

string->key-zero str len | bigloo cryptography procedure

If the length of the input string `str` is greater or equal to `len` bytes then the first `str` characters are returned. Otherwise `str` is suffixed with '0' (`#a000`) characters.

string->key-hash str len hash-fun | bigloo cryptography procedure

The input string `str` is run through the given hash function `hash-fun`. The result is then concatenated multiple times (with itself) until a string of the `len` bytes is obtained.
In the following example we encrypt `some-message` using a password `"my password"`. The password will be transformed to 256 bits (32 bytes) using the `string->key256` function.

    (define (string->key256 password)
      (string->key-hash password 32
                        (lambda (str) (string-hex-intern (sha1sum str)))))
    (encrypt 'aes some-message "my password" :string->key string->key256)

Note that the following example yields an identical result:

    (define (string->key256 password)
      (string->key-hash password 32
                        (lambda (str) (string-hex-intern (sha1sum str)))))
    (encrypt 'aes some-message (string->key256 "my password")
             :string->key (lambda (x) x))

string->key-simple str len hash-fun | bigloo cryptography procedure

This function implements the simple s2k algorithm of OpenPGP (RFC 2440). Basically `str` is run through the hash-fun several times until the concatenation of the results is long enough. At each iteration the string is prefixed with `count` '0'-bytes (where `count` is the iteration counter).

string->key-salted str len hash-fun salt | bigloo cryptography procedure

This function implements the salted s2k algorithm of OpenPGP (RFC 2440). Similar to `string->key-simple` but the input string is first prefixed with `salt`.

string->key-iterated-salted str len hash-fun salt count | bigloo cryptography procedure

This function implements the iterated salted s2k algorithm of OpenPGP (RFC 2440). The variable `count` must be a long. This algorithm is an extension of `string->key-salted` where the hash function is applied repeatedly.
This function has changed with release 3.4b. Earlier versions could be incompatible with RFC 2440.

### 15.2 Public Key Cryptography

### 15.2.1 Rivest, Shamir, and Adleman (RSA)

Bigloo's implementation of RSA is based on RFC 3447, PKCS #1 v2.1. It does not feature multiprime RSA, though.
Bigloo's implementation is not secure against timing attacks. Furthermore some error codes might reveal information to attackers.

#### 15.2.1.1 RSA Keys

There are two kinds of RSA keys inside Bigloo: complete and partial keys. A complete key contains the information of both the public and the private key (together with other information that could be reconstructed out of the private key). A partial key just contains the modulus and the private or public exponent.

RSA-Key | bigloo cryptography class

Complete-RSA-Key | bigloo cryptography class

    (class Rsa-Key modulus::bignum exponent::bignum)
    (final-class Complete-Rsa-Key::Rsa-Key
      ;; for the complete-rsa-key "exponent" takes the role of 'd'
      e::bignum p::bignum q::bignum
      exp1::bignum   ;; d mod (p-1)
      exp2::bignum   ;; d mod (q-1)
      coeff::bignum) ;; (inverse of q) mod p

RSA keys can be read and written using `read-pem-key` and `write-pem-key` (PEM).

generate-rsa-key [:key 1024] [:show-trace] | bigloo cryptography procedure

This function generates a new RSA key (with its public and private components).
Do not use this function for critical applications. No special effort has been undertaken to guarantee the randomness of the generated prime numbers, nor to weed out insecure keys.

Complete keys can be accessed using the following functions:

extract-public-rsa-key complete-key | bigloo cryptography procedure

Returns the public partial key of the given complete key.
This procedure is implemented as follows:

    (define (extract-public-rsa-key::Rsa-Key key::Complete-Rsa-Key)
       (with-access::Complete-Rsa-Key key (modulus e)
          (make-Rsa-Key modulus e)))

extract-private-rsa-key complete-key | bigloo cryptography procedure

Returns the private partial key of the given complete key.

rsa-key=? key1 key2 | bigloo cryptography procedure

Returns true if the two keys have the same modulus and public exponent. The exponent of a partial key is considered to be public.

rsa-key-length key | bigloo cryptography procedure

Returns the key length in bytes.

#### 15.2.1.2 RSA basic operations

RSA only works on bignums (up to the size of the modulus). The following procedures implement basic encryption, decryption, signing and signature verification.

rsa-encrypt key m | bigloo cryptography procedure

Encrypts the bignum `m` using the given key. If the key is a complete key then its public exponent is used. For partial keys only one exponent is available (which is assumed to be the public 'e' of the recipient). The result is again a bignum.

rsa-decrypt key c | bigloo cryptography procedure

Decrypts the bignum `c` using the given key. If the key is a complete key then its private exponent is used. For partial keys only one exponent is available (which is assumed to be the private 'd'). The result is again a bignum.

rsa-sign k m | bigloo cryptography procedure

Signs the bignum `m` using key `k`. Uses the private exponent of complete keys. The result is a bignum.

rsa-verify k m s | bigloo cryptography procedure

Verifies the signature `s`. Returns true if `s` is the signature of `m`. The key `k` should be the public key of the signer.

#### 15.2.1.3 Examples

In this section we will present an example of using RSA.
Let's start by generating an RSA key in openssl:

    $ openssl genrsa -out my_rsa_key.pem 1024

Our key will have 1024 bits (for the public modulus), and therefore RSA will only be able to work with bignums up to 1024 bits (128 bytes).
Now some Bigloo code that uses this key.
Start by loading the library.

    (module rsa-example (library crypto))

Now read the key:

    (define *key* (read-pem-key "my_rsa_key.pem"))
    (define *public-key* (extract-public-rsa-key *key*))

The public portion of the key can be distributed:

    ;; publish the *public-key*:
    (write-pem-key-string *public-key*)

Now let's sign the message ``My Important Message''. This message is sufficiently short to be signed directly, but in general it is better to get a hash of the message:

    (define msg-hash (sha1sum "my message"))
    (define msg-hash-bignum (octet-string->bignum msg-hash))

The result of `sha1sum` returns a human readable representation of the hash. It would hence be possible to transform it back to an internal representation before applying the `octet-string->bignum` function:

    (define msg-hash-bignum (octet-string->bignum (string-hex-intern msg-hash)))

In our case both variants are small enough to fit into our keys. The latter version is however more often used.
Now that we have a message hash in bignum form we can sign it.

    (define signature (rsa-sign *key* msg-hash-bignum))

The signature is again in bignum form. If needed there are several ways to transform it into string-form (for instance `bignum->string` or `bignum->octet-string`).
The signature can now be distributed. Anyone wanting to verify the signature simply has to create the same message-hash and call `rsa-verify` with our public key:

    (rsa-verify *public-key* msg-hash-bignum signature) => #t

Encryption and decryption work in a similar way.
Suppose someone (let's say ``Alice'') wants to send us the following secret message ``Cryptography''. The encryption and decryption functions work, similar to the signature functions, on bignums. We could, as before, simply transform this short string into a bignum and directly encrypt the bignum. This approach would however not work for longer strings. In the following we will present the generic version that works with strings of any size.
Public key cryptography is relatively slow and Alice thus starts by encrypting our message a fast block cipher with a ``random'' password:

    (define encrypted (encrypt 'aes "Cryptography" "my random password"))

Alice can already send us the encrypted message. We will just not yet be able to decrypt it, as we don't have the random password yet.
Alice now takes her random password string and encrypts it with our public key:

    (define encrypted-key (rsa-encrypt *public-key* (octet-string->bignum "my random password")))

Alice simply sends us the `encrypted-key`. On our side we can now decrypt the key:

    (define aes-key (bignum->octet-string (rsa-decrypt *key* encrypted-key)))

We can now decrypt the previously received message:

    (decrypt 'aes aes-key encrypted) => "Cryptography"

#### 15.2.1.4 RSA RFC 3447

The following functions have been defined in RFC 3447.

RSAEP k m | bigloo cryptography procedure

RSADP k c | bigloo cryptography procedure

RSASP1 k m | bigloo cryptography procedure

RSAVP1 k s | bigloo cryptography procedure

These are the RFC 3447 names for encryption, decryption, signature and signature verification. Note that the verification does not receive the original message as parameter.
In fact `rsa-verify` is implemented as follows:

    (define (rsa-verify k m s)
      (=bx m (RSAVP1 k s)))

PKCS1-v1.5-pad m-str key-len mode | bigloo cryptography procedure

PKCS1-v1.5-unpad em-str mode | bigloo cryptography procedure

Pads (resp. unpads) the given string using PKCS1-v1.5 specifications. Mode must be 0, 1 or 2.

RSAES-PKCS1-v1.5-encrypt t key m-str | bigloo cryptography procedure

RSAES-PKCS1-v1.5-decrypt key c-str | bigloo cryptography procedure

RSASSA-PKCS1-v1.5-sign key msg-str [:hash-algo 'sha-1] | bigloo cryptography procedure

RSASSA-PKCS1-v1.5-verify key msg-str S-str | bigloo cryptography procedure

RSASSA-PKCS1-v1.5-sign-bignum key msg-str [:hash-algo 'sha-1] | bigloo cryptography procedure

RSASSA-PKCS1-v1.5-verify-bignum key msg-str S | bigloo cryptography procedure

RSAES-PKCS1-v1.5 functions work on strings. However their length is limited by the size of the modulus (to be exact: by key-len - 11). The `-bignum` functions skip the last step of converting the internal bignum to strings.
The optional `:hash-algo` must be either `sha-1` or `md5` (RFC 3447 allows other hash algorithms, but they are not yet implemented).

RSAES-OAEP-encrypt key m-str [:label ""] | bigloo cryptography procedure

RSAES-OAEP-decrypt key cypher-str [:label ""] | bigloo cryptography procedure

RSASSA-PSS-sign key msg-str | bigloo cryptography procedure

RSASSA-PSS-verify key msg-str sig-str | bigloo cryptography procedure

These functions pad, mask, etc the input string before they perform their operation on them. See RFC 3447 for more information.

### 15.2.2 Digital Signature Algorithm (DSA)

Bigloo has rudimentary (but usually sufficient) support for DSA. While it is not possible to generate new DSA keys inside Bigloo one can sign or verify with Bigloo.
DSA keys can be read and written using `read-pem` (PEM).
For consistency with RSA we have named DSA keys in a similar way as the RSA keys. The public part of a DSA key can be found in the class `DSA-Key` while the private part is added in the `Complete-DSA-Key` subclass.

DSA-Key | bigloo cryptography class

Complete-DSA-Key | bigloo cryptography class

    (class Dsa-Key
       p::bignum q::bignum g::bignum y::bignum)
    (final-class Complete-Dsa-Key::Dsa-Key
       x::bignum)) ;; the private key

extract-public-dsa-key complete-key | bigloo cryptography procedure

Returns a `DSA-Key` without the private `x`.

dsa-sign m key | bigloo cryptography procedure

Signs the bignum `m` using the private dsa key `key`. The result are two values: `r` and `s`.
A typical call to `dsa-sign` is hence of the following form

    (receive (r s)
      (dsa-sign secret-key hashed-msg-bignum)
      (process-signature r s))

dsa-verify m key r s | bigloo cryptography procedure

Verifies a signature (consisting of `r` and `s`).

DSA works very similar to RSA. Have a look at RSA's example section.

### 15.2.3 ElGamal

Bigloo supports ElGamal encryption (but not signing). Bigloo's implementation is minimal.
For consistency with RSA ElGamal keys are similarly named as their RSA counterparts.

ElGamal-Key | bigloo cryptography class

Complete-ElGamal-Key | bigloo cryptography class

    (class ElGamal-Key
      p::bignum
      g::bignum
      y::bignum)
    (final-class Complete-ElGamal-Key::ElGamal-Key
      x::bignum)) ;; the private key

extract-public-elgamal-key complete-key | bigloo cryptography procedure

Returns a copy of the public part (as `ElGamal Key`).

elgamal-encrypt key m | bigloo cryptography procedure

Encrypts the bignum `m` using the given public key. The result are two values `c1` and `c2`.
Note that ElGamal encryption needs random bytes for every encryption. This means that this function may return different results with the same parameters. It furthermore implies that the result is insecure if the operating system provides bad random numbers, or if Bigloo's random-number generation is buggy. For critical applications be sure to verify both requirements.

elgamal-decrypt complete-key c1 c2 | bigloo cryptography procedure

Decrypts an ElGamal encrypted message (consisting of the two bignums `c1` and `c2`) with the given private key.

elgamal-key-length key | bigloo cryptography procedure

Returns the key length in bytes.

ElGamal works very similar to RSA. Have a look at RSA's example section.

### 15.2.4 PEM

Bigloo is able to read and write RSA and DSA keys in PEM format. This is the default format used by OpenSSL.
The following example creates a new DSA key pair in OpenSSL and stores it in PEM format.

    $ openssl dsaparam 1024 -out /tmp/dsaparam
    $ openssl gendsa /tmp/dsaparam

read-pem-key in | bigloo cryptography procedure

read-pem-key-port input-port | bigloo cryptography procedure

read-pem-key-file filename | bigloo cryptography procedure

read-pem-key-string str | bigloo cryptography procedure

These functions will read a PEM encoded key. The encoded file may contain a private or public RSA key, or a private or public DSA key.
The procedure `read-pem-key` accepts input-ports and strings. In the case of a string it will invoke `read-pem-key-file` (and not `read-pem-key-string`).

write-pem-key key out [public-key-only?] | bigloo cryptography procedure

write-pem-key-port key out [public-key-only?] | bigloo cryptography procedure

write-pem-key-file key out [public-key-only?] | bigloo cryptography procedure

write-pem-key-string key [public-key-only?] | bigloo cryptography procedure

These functions write the given key. The key may be a private/public RSA/DSA key.
The procedure `write-pem-key` accepts output-ports and strings as `out` parameter. If `out` is a string it will delegate to `write-pem-key-file`.

### 15.3 OpenPGP

Bigloo implements parts of OpenPGP (RFC 2440, RFC 4880). All OpenPGP functions are accessible via the `openpgp` library.
Here is an example of a module that uses this library:

    ;; Encrypt a string using openpgp default encryption.
    (module pgp-encrypt
       (library openpgp)
       (main main))

    (define (main argv)
       (when (and (pair? (cdr argv)) (pair? (cddr argv)))
          (let ((encrypt? (string=? "-e" (cadr argv)))
                (passwd (caddr argv))
                (input (read-string)))
             (if encrypt?
                 (display (pgp-write-string (pgp-encrypt input
                                                         '()  ;; no public keys
                                                         (list passwd))))
                 (let ((composition (pgp-read-string input)))
                   (display (pgp-decrypt composition
                                         :passkey-provider (lambda () passwd))))))))

pgp-composition? composition | bigloo openpgp procedure

Returns `true` if and only if `composition` is a PGP-Composition. Returns `false` otherwise.

pgp-read-string str | bigloo openpgp procedure

pgp-read-port iport | bigloo openpgp procedure

pgp-read-file file-name | bigloo openpgp procedure

These functions read and decode PGP data. OpenPGP allows several keys to be stored in the same message. Therefore `pgp-read` will return keys always in a list (even if the message only contains one key).
The return value is either a list of PGP-compositions (PGP-Keys), or a single PGP-composition.

pgp-write-string composition [:format 'armored] | bigloo openpgp procedure

pgp-write-port oport composition [:format 'armored] | bigloo openpgp procedure

pgp-write-file file-name composition [:format 'armored] | bigloo openpgp procedure

The counter-part of `pgp-read`. These functions encode PGP-compositions. By default the result is armored (i.e. encoded with ASCII characters). If the optional `:format` parameter is different than the symbol `armored`, then the composition is encoded in binary.
Note that there is no means to encode a list of PGP-keys.

pgp-encrypt msg-string keys passwords [:hash-algo 'sha-1] [:symmetric-algo 'cast5] | bigloo openpgp procedure

Encrypts the given string. The returned composition can be decrypted by the owners of the keys, or with one of the passwords.
In the following example Alice and Bob may use their private key to decrypt the secret message. Users knowing the one of the passwords (``foo'' and ``bar'') will also be able to decrypt the message.

    (pgp-write-file "encrypted.pgp"
       (pgp-encrypt "my secret message"
                    (list alice-key bob-key)
                    '("foo" "bar")))

The given keys should be subkeys of a PGP-key, but if a PGP-key is given Bigloo will do its best to pick the correct subkey for encryption.

- If only one subkey exists (the main-key) then this subkey is used.
- If two subkeys exist, and the non-main key is suitable for encryption, then the non-main key is used.
- If only one of many subkeys (including the main-key) is suitable for encryption, then this subkey is used.
- Else Bigloo raises an error.

pgp-password-encrypt msg-string password [:hash-algo 'sha-1] [:symmetric-algo 'cast5] [:mdc #t] | bigloo openpgp procedure

Deprecated. Encrypts `msg-string` with the given password. The returned PGP-composition does not contain any information which hash-algorithm and symmetric encryption algorithm has been used. RFC 4880 specifies that IDEA and MD5 should be used. However GnuPG uses SHA-1 and CAST5. Therefore Bigloo defaults to the latter algorithms.
Even though the usage of this PGP message is deprecated it yields the smallest encrypted data. It may be of interest when compatibility with other tools is not a requirement (but why use OpenPGP then).
The optional `mdc` flag triggers the usage of a modification detection code. It is more secure against tampering but requires more space and might not be recognized by old openpgp implementations.

pgp-decrypt encrypted [:passkey-provider (lambda () #f)] [:password-provider (lambda (key) #f)] [:key-manager (lambda (key-id) '())] [:hash-algo 'sha-1] [:symmetric-algo 'cast5] [:ignore-bad-packets #f] | bigloo openpgp procedure

Decrypts a PGP-composition that has been generated by `pgp-encrypt` or by `pgp-password-encrypt`. The function returns the decrypted message (a string) or `#f` if decryption was not possible.
If the message can be decrypted with a private key, then Bigloo will call the `key-manager` and request a list of PGP-subkeys that match the given key-id.
If a subkey (returned by the key-manager) is not yet decrypted, Bigloo will invoke the `password-provider` with the subkey, and request a password to decrypt the private part of the subkey.
If the message can be decrypted with a password Bigloo will then request a passkey by invoking the `passkey-provider`.
The optional arguments `hash-algo` and `symmetric-algo` are only used for messages that have been encrypted with `pgp-password-encrypt`.
The option arguments `ignore-bad-packets` enables decoding partial packets but pay attention that a message decoded with bad packets may have been tempered.

pgp-sign msg-string key password-provider [:detached-signature? #t] [:one-pass? #f] [:hash-algo 'sha-1] | bigloo openpgp procedure

Signs `msg-string` with the given key. Ideally the key should be a subkey, but if a complete PGP-Key is given, Bigloo will use the main-key instead. If the main-key is not suitable for signing, then an error is raised.
If the private part of the key has not yet been decrypted then Bigloo will call the password-provider (a procedure) with the subkey to get a password (a string).
The function returns a PGP-composition.
If the optional `detached-signature?` parameter is set to `#f` then the msg-string is not included in the returned composition.
The `one-pass?` and `hash-algo` parameters are usually left at its default values.
Example:

    (let ((my-key (car (pgp-read-file "my-key.pgp"))))
      (pgp-write-file "msg.sig"
         (pgp-sign "my signed message"
                   my-key
                   (lambda (key) "my-password")
                   :detached-signature? #f)))

pgp-verify signature key-manager [:msg #f] | bigloo openpgp procedure

Verifies a signature.
The key-manager is a function that takes a substring identifier and returns a list of keys matching this id. Since a signature composition may contain several signatures this function may be invoked several times.
The result is a list of subkeys that signed the message. If the key-manager doesn't have any of the signature-keys then the result is the empty list.
A message (string) needs only be given if the signature is detached. Otherwise the original message is encoded in the signature-composition.
Example:

    (let ((sig (pgp-read-file "msg.sig")))
      (let ((signers (pgp-verify sig my-key-manager)))
       (for-each (lambda (subkey)
                   (print (subkey->string subkey) " signed the message"))
                 signers)))

pgp-signature-message signature | bigloo openpgp procedure

Returns the signature's message, or `#f` if the signature is a detached signature.

pgp-key? key | bigloo openpgp procedure

pgp-subkey? key | bigloo openpgp procedure

Predicates for PGP-Key and PGP-Subkey.

pgp-subkeys key | bigloo openpgp procedure

Returns a list of PGP-Subkeys of the PGP-Key. The first key in the list is the main-key. The main-key is used as default for signatures.

pgp-key->string key | bigloo openpgp procedure

pgp-subkey->string key | bigloo openpgp procedure

Returns a string representation of the key (resp. subkey).
Example outputs:

    (pgp-key->string key)
    => John Doe john.doe@gmail.com
    => bd4df3b2ddef790c RSA (Encrypt or Sign)
    => 424610a65032c42e RSA (Encrypt or Sign)

    (pgp-subkey->string (car (pgp-subkeys key)))
    => John Doe john.doe@gmail.com
    => bd4df3b2ddef790c RSA (Encrypt or Sign)

pgp-key-id subkey | bigloo openpgp procedure

pgp-key-fingerprint subkey | bigloo openpgp procedure

Returns the id (resp. fingerprint) of a subkey.
A subkey-id is a 8-character binary string.
A fingerprint is a 20-character binary string.

pgp-make-key-db | bigloo openpgp procedure

pgp-key-db? | bigloo openpgp procedure

pgp-add-key-to-db db key | bigloo openpgp procedure

pgp-add-keys-to-db db keys | bigloo openpgp procedure

pgp-resolve-key db id | bigloo openpgp procedure

pgp-db-print-keys db | bigloo openpgp procedure

A simple key-manager implementation based on lists.

### 15.3.1 Examples

#### 15.3.1.1 Signatures

Unless you already have a gpg key create a new PGP key with gpg. Note that DSA with a keysize greater than 1024 does not work with SHA-1. SHA-224,256,384,512 would work, but are not yet implemented in Bigloo.

    $ gpg --gen-key
    ...
    pub   1024D/A2DA694E 2010-08-07 [expires: 2010-08-27]
          Key fingerprint = DFAF 5894 9003 8640 D45B  6199 07CA 0495 A2DA 694E
    uid                  Bigloo Example
    sub   1024g/0B8985E5 2010-08-07 [expires: 2010-08-27]

We export both the public and the private key.

    $ gpg  -a -o A8453FAB_Bigloo_Example_User.pkey --export A8453FAB
    $ gpg  -a -o A8453FAB_Bigloo_Example_User.skey --export-secret-keys A8453FAB

This small program will simply read the key and print a human-readable representation.

    ;; contents of print-key.scm
    (module print-key
       (library openpgp)
       (main my-main))

    (define (my-main args)
       (let ((public-key (car (pgp-read-file "A2DA694E_Bigloo_Example.pkey")))
             (secret-key (car (pgp-read-file "A2DA694E_Bigloo_Example.skey"))))
          (display (pgp-key->string public-key))
          (display (pgp-key->string secret-key))))

The compilation is straight-forward and does not require any special flags:

    $ bigloo print-key.scm -o print-key
    $ ./print-key
    Bigloo Example
    07ca0495a2da694e DSA (Digital Signature Standard)
    5fa4e8c90b8985e5 ElGamal (Encrypt-Only)
    Bigloo Example
    07ca0495a2da694e DSA (Digital Signature Standard)
    5fa4e8c90b8985e5 ElGamal (Encrypt-Only)

As can be seen, the `pgp-key->string` routine does not differentiate between public and private keys.
We can also sign a message:

    (let ((my-key (car (pgp-read-file "A2DA694E_Bigloo_Example.skey"))))
      (pgp-write-file "msg.sig"
         (pgp-sign (read-string)
                   my-key
                   (lambda (key) "<Bigloo Example Password>")
                   :detached-signature? #f)))

Signatures from Bigloo follow RFC 4880 and can therefore be verified by `gpg`.

    $ echo "Gpg can verify Bigloo's signature" | ./sign
    $ gpg --verify msg.sig
    gpg: Signature made Sat 07 Aug 2010 10:12:21 PM CEST using DSA key ID A2DA694E
    gpg: Good signature from "Bigloo Example"

Inversely Bigloo can verify `pgp`'s signature. Here we first generate a signature with `gpg`.

    $ echo "Bigloo can verify gpg's signatures." | \
     gpg -o msg_gpg.sig -a \
         --default-key "Bigloo Example" \
         --passphrase <Bigloo Example Password> \
         --sign

    You need a passphrase to unlock the secret key for
    user: "Bigloo Example"
    1024-bit DSA key, ID A2DA694E, created 2010-08-07

The following program reads OpenPGP signatures and verifies them. For simplicity the key database will only contain one key, but it could contain any number of keys.

    (let ((my-key (car (pgp-read-file "A2DA694E_Bigloo_Example.pkey")))
          (sig (pgp-read-file "msg_gpg.sig"))
          (db (pgp-make-key-db)))
       (pgp-add-key-to-db db my-key)
       (print "Signature message: " (pgp-signature-message sig))
       (let ((signers (pgp-verify sig (lambda (id) (pgp-resolve-key db id)))))
          (for-each (lambda (subkey)
                       (display (pgp-subkey->string subkey)))
                    signers)))

As expected, the program verifies the correct signature.

    $ ./verify
    Signature message: Bigloo can verify gpg's signatures.

    Bigloo Example
    07ca0495a2da694e DSA (Digital Signature Standard)

#### 15.3.1.2 Email Usage

Usage of OpenPGP within mails is described in RFC 3156.
Encrypted parts and signatures are encoded with their separate content-types. Signatures are done over a canonicalized version of the message. They also hash over the content-type headers.
OpenPGP's recette program has an example for a signature from kmail, that can be succesfully verified with Bigloo.

#### 15.3.1.3 Encryption

OpenPGP allows messages to be encrypted with passwords (in this context ``passkey'') or public keys. It is also possible to encrypt a message for more than one recipient. In such a case the data will be encrypted by a session-key which in turn is encrypted separately for each recipient. Since the session-key is not very big (compared to the data) the size overhead is usually insignificant.
Let's start by encrypting a message with a simple passkey.

    (let* ((secret-data "My secret data\n")
           (composition (pgp-encrypt secret-data '() '("My secret passkey"))))
      (pgp-write-file "secret.pgp" composition))

As usual the pgp message is compatible with `gpg`:

    $ gpg secret.pgp
    gpg: CAST5 encrypted data
    Enter passphrase: <My secret passkey>
    gpg: encrypted with 1 passphrase
    $ cat secret
    My secret data

As expected, Bigloo can decrypt password protected files that have been generated by gpg:

    $ echo "A secret message encrypted with gpg." | \
      gpg -o encrypted.pgp --symmetric \
          --passphrase "secret key"

The Bigloo code to decrypt the message is very simple:

    (print (pgp-decrypt (pgp-read-file "encrypted.pgp")
                        :passkey-provider (lambda () "secret key"))))

In a similar vein it is possible to use public key encryption. The following example tests the encryption and decryption part of Bigloo.

    (let* ((my-key (car (pgp-read-file "A2DA694E_Bigloo_Example.skey")))
           (db (pgp-make-key-db))
           (secret-data "My secret message")
           (encrypted (pgp-encrypt secret-data `(,my-key) '())))
       (pgp-add-key-to-db db my-key)
       (let* ((key-manager (lambda (id) (pgp-resolve-key db id)))
              (password-provider (lambda (key) <Bigloo Example Password>))
              (decrypted (pgp-decrypt encrypted
                                     :key-manager key-manager
                                     :password-provider password-provider)))
         (if (not (string=? decrypted secret-data))
             (error "decrypt-test"
                    "Something went horribly wrong"
                    decrypted))))

Note that a secret secret key has a part that is encrypted by a password. During decryption Bigloo needs access to this encrypted data and therefore invokes the password-provider so it can decrypt it. In many cases this will trigger an interactive callback with the user. Here, in this toy example, we know that the password that is needed is for the Bigloo Example key. In a more general case the password-provider will have to print the key to give more information to the user.
In the following example we show how to encrypt data for 3 passwords and one key.

    (let* ((my-key (car (pgp-read-file "A2DA694E_Bigloo_Example.skey")))
           (db (pgp-make-key-db))
           (secret-data "My secret message")
           (encrypted (pgp-encrypt secret-data `(,my-key)
                                               '("pass1" "pass2" "pass3"))))
       (pgp-write-file "multi_receiver.pgp" encrypted))

We believe that `gpg` has a bug and does not know how to handle such messages correctly. Bigloo, however, decrypts the message with any of the possible options.

### 15.4 Development

Bigloo's OpenPGP implementation only exposes few library functions. As a consequence some features are not accessible. The key-management system is very rough, and there are no means to inspect messages in more detail. It should be possible to expose or implement many of those missing features with little effort. The most time-consuming part is generally designing a clean API and the testing/debugging of new features: when something goes wrong it can take a huge amount of time to find the reason.
Developers interested in improving Bigloo's OpenPGP library can print a huge amount of debugging information by enabling the debug-macro in `util.scm`. Bigloo's OpenPGP implementation is not designed for speed and takes no shortcuts. The debugging output can therefore be used to follow the specification of RFC 4880 (or 2440).

This Html page has been produced by Skribe.
Last update Mon Jan 27 07:55:49 2025.