On this page:
pk-spec?
pk-impl?
get-pk
pk-can-sign?
pk-can-encrypt?
pk-can-key-agree?
pk-has-parameters?
5.1 PK Keys and Parameters
pk-key?
private-key?
public-only-key?
pk-parameters?
pk-key->parameters
public-key=?
pk-key->public-only-key
generate-pk-parameters
generate-private-key
5.2 PK Signatures
pk-sign
pk-verify
digest/  sign
digest/  verify
pk-sign-digest
pk-verify-digest
5.3 PK Encryption
pk-encrypt
pk-decrypt
5.4 PK Key Agreement
pk-derive-secret
5.5 PK External Representations
pk-key->datum
datum->pk-key
pk-parameters->datum
datum->pk-parameters
5.6 PKCS #8 Encrypted Private Keys
pkcs8-encrypt/  pbkdf2-hmac
pkcs8-encrypt/  scrypt
pkcs8-decrypt-bytes
pkcs8-decrypt-key
Bibliography

5 Public-Key Cryptography

Public-key (PK) cryptography covers operations such as signing, encryption, and key agreement between parties that do not start with any shared secrets. Instead of shared secrets, each party possesses a keypair consisting of a secret private key and a widely-published public key. Not all PK cryptosystems support all PK operations (for example, DSA does not support encryption or secret derivation), and some PK implementations may support a subset of a PK cryptosystem’s potential operations.

procedure

(pk-spec? v)  boolean?

  v : any/c
Returns #t if v is a PK cryptosystem specifier, #f otherwise.

A PK cryptosystem specifies the information represented by the public and private keys and the algorithms that operate on that information. The following PK systems are supported:

Changed in version 1.1 of package crypto-lib: Added 'eddsa and 'ecx.

procedure

(pk-impl? v)  boolean?

  v : any/c
Returns #t if v is a PK cryptosystem implementation, #f otherwise.

procedure

(get-pk pki factories)  (or/c pk-impl? #f)

  pki : pk-spec?
  factories : (or/c crypto-factory? (listof crypto-factory?))
Returns an implementation of PK algorithm pki from the given factories. If no factory in factories implements pki, returns #f.

procedure

(pk-can-sign? pk)  boolean?

  pk : (or/c pk-impl? pk-key?)

procedure

(pk-can-encrypt? pk)  boolean?

  pk : (or/c pk-impl? pk-key?)

procedure

(pk-can-key-agree? pk)  boolean?

  pk : (or/c pk-impl? pk-key?)
Indicates whether the cryptosystem implementation pk (or the implementation corresponding to pk, if pk is a key) supports signing, encryption, and key agreement, respectively.

Note that the functions only report the capabilities of the cryptosystem implementation, regardless of the limitations of pk if pk is a key. For example, (pk-can-sign? pk) would return true when pk is an RSA public-only key, even though signing requires a private key.

procedure

(pk-has-parameters? pk)  boolean?

  pk : (or/c pk-impl? pk-key?)
Returns #f if the PK cryptosystem represented by pk uses key parameters, #f otherwise. See PK Keys and Parameters for more information.

5.1 PK Keys and Parameters

A PK keypair consists of public key component and private key components. A public key is a key that contains the public key components. In this library, a private key contains both private and public components, so it can also be used wherever a public key is required. That is, every private key is also a public key. This library uses the term “public-only key” to refer to a public key that is not a private key.

In some PK cryptosystems, the public components are further divided into key-specific values and “key parameters.” Key parameters are public quantities that are expensive to compute; they can be generated once and many keypairs can use the same parameter values. For example, a DSA key requires a large prime with certain relatively rare mathematical properties, and so finding such a prime is relatively expensive, but once a suitable prime is found, generating private keys is relatively fast, and since the prime is public, many keypairs can use the same prime. Elliptic curve (EC) cryptography is another example: the key parameter is the curve equation, and the public and private key components are points on the curve. In contrast, RSA does not have key parameters; simple quantities like the size of an RSA modulus are not key parameters.

procedure

(pk-key? v)  boolean?

  v : any/c
Returns #t if v is a public key or a private key, #f otherwise. Since all PK keys contain the public key components, pk-key? is a predicate for public keys.

procedure

(private-key? v)  boolean?

  v : any/c
Returns #t if v is a private key.

procedure

(public-only-key? v)  boolean?

  v : any/c
Returns #t if v is a public key but not a private key, #f otherwise. Equivalent to (and (pk-key? v) (not (private-key? v))).

procedure

(pk-parameters? v)  boolean?

  v : any/c
Returns #t if v is a value representing PK key parameters for some cryptosystem, #f otherwise.

procedure

(pk-key->parameters pk)  (or/c pk-parameters? #f)

  pk : pk-key?
Returns a value representing the key parameters of pk, or #f if pk’s cryptosystem does not use key parameters.

procedure

(public-key=? pk1 pk2 ...)  boolean?

  pk1 : pk-key?
  pk2 : pk-key?
Returns #t if the public key components of pk1 and pk2 are equal, #f otherwise. One use of this function is to check whether a private key matches some public-only key.

procedure

(pk-key->public-only-key pk)  public-only-key?

  pk : pk-key?
Returns a public-only key pub-pk such that (public-key=? pk pub-pk). If pk is already a public-only key, the function may simply return pk.

procedure

(generate-pk-parameters pki    
  [paramgen-config])  pk-parameters?
  pki : (or/c pk-spec? pk-impl?)
  paramgen-config : (listof (list/c symbol? any/c)) = '()
Generate PK parameter values for the cryptosystem of pki using the given configuration options. The default values of optional configuration arguments are implementation-dependent.

The acceptable configuration values depend on pki.
  • The following configuration values are recognized for DSA ('dsa):
    • (list 'nbits nbits) Optional. Generate a prime modulus of size nbits. Examples include 1024 and 2048.

  • The following configuration values are recognized for DH ('dh):
    • (list 'nbits nbits) Optional. Generate a prime modulus of size nbits. Examples include 1024 and 2048.

    • (list 'generator generator) Optional. Use the given generator; must be either 2 or 5.

  • The following configuration values are recognized for EC ('ec):
    • (list 'curve curve-name) Required. Use the standard curve named curve-name. Examples include "NIST P-256" and "secp192r1". Use factory-print-info to show available curves.

  • The following configuration values are recognized for EdDSA ('eddsa):
    • (list 'curve curve-sym) Required. Generate a key for the given curve. The curve-sym must be 'ed25519 or 'ed448.

  • The following configuration values are recognized for 'ecx:
    • (list 'curve curve-sym) Required. Generate a key for the given curve. The curve-sym must be 'x25519 or 'x448.

Changed in version 1.1 of package crypto-lib: Added 'eddsa and 'ecx options.

procedure

(generate-private-key pki [keygen-config])  private-key?

  pki : (or/c pk-spec? pk-impl? pk-parameters?)
  keygen-config : (listof (list/c symbol? any/c)) = '()
Generate a private key from the given PK implementation or PK parameters. The default values of optional configuration arguments are implementation dependent.

The accepted configuration arguments depend on pki:
  • If pki is a PK parameters object (pk-parameters?), then keygen-config must be empty.

  • The following configuration values are recognized for RSA ('rsa):
    • (list 'nbits nbits) Optional. Generate a modulus of size nbits. Examples include 1024 and 2048.

  • If pki is a PK specifier (pk-spec?) or PK implementation (pk-impl?), then the same configuration arguments are supported as for generate-parameters. This is equivalent to (generate-private-key (generate-pk-parameters pki keygen-config) '()).

5.2 PK Signatures

In PK signing, the sender uses their own private key to sign a message; any other party can verify the sender’s signature using the sender’s public key.

In RSA, DSA, and ECDSA, only short messages can be signed directly (limits are generally proportional to the size of the keys), so a typical process is to compute a digest of the message and sign the digest. The message and digest signature are sent together, possibly with additional data.

In EdDSA, messages are signed directly. (The signing process computes a message digest internally.)

procedure

(pk-sign pk msg [#:pad padding #:digest dspec])  bytes?

  pk : private-key?
  msg : bytes?
  padding : (or/c #f 'pkcs1-v1.5 'pss 'pss*) = #f
  dspec : (or/c digest-spec? 'none #f) = #f
Returns the signature using the private key pk of the message msg.

If pk is an RSA private key, then padding must be one of the following:
  • 'pkcs1-v1.5 or #f use PKCS#1-v1.5 padding

  • 'pss use PSS padding with a salt length equal to (digest-size di)

  • 'pss* sign using PSS padding with a salt length equal to (digest-size di), but infer the salt length when verifying

For all other cryptosystems, padding must be #f.

If pk is an RSA private key, then dspec must be the name of a digest algorithm, and msg must be a digest computed with dspec (in particular, it must have the correct size for dspec). The resulting signature depends on the identity of the digest algorithm. Different RSA implementations may support different digest algorithms.

If pk is a DSA or EC private key, the signature does not depend on the digest algorithm; the dspec should be omitted. (For backwards compatibility, the dspec argument is accepted, but it has no effect other than checking the length of msg.)

If pk is a EdDSA private key, then dspec must be #f or 'none (both values mean the same thing). The message may be of any length, and the EdDSA signature is computed. Future versions of this library may accept other values of dspec and compute HashEdDSA signatures (eg, Ed25519ph) in reponse.

Added in version 1.1 of package crypto-lib.

procedure

(pk-verify pk    
  msg    
  sig    
  [#:digest dspec    
  #:pad padding])  boolean?
  pk : pk-key?
  msg : bytes?
  sig : bytes?
  dspec : (or/c digest-spec? #f 'none) = #f
  padding : (or/c #f 'pkcs1-v1.5 'pss) = #f
Returns #t if pk verifies that sig is a valid signature of the message msg, or #f if the signature is invalid.

The dspec and padding arguments have the same meanings as for pk-sign.

Added in version 1.1 of package crypto-lib.

procedure

(digest/sign pk di input [#:pad padding])  bytes?

  pk : private-key?
  di : (or/c digest-spec? digest-impl?)
  input : input/c
  padding : (or/c #f 'pkcs1-v1.5 'pss) = #f

procedure

(digest/verify pk di input sig [#:pad padding])  boolean?

  pk : pk-key?
  di : (or/c digest-spec? digest-impl?)
  input : input/c
  sig : bytes?
  padding : (or/c #f 'pkcs1-v1.5 'pss) = #f
Computes or verifies signature of the di message digest of input; equivalent to calling digest then pk-sign-digest or pk-verify-digest, respectively.

Do not use these functions with EdDSA keys; use pk-sign and pk-verify directly on the messages. (This library currently does not support pre-hashing EdDSA variants, eg Ed25519ph.)

procedure

(pk-sign-digest pk di dgst [#:pad padding])  bytes?

  pk : private-key?
  di : (or/c digest-spec? digest-impl?)
  dgst : bytes?
  padding : (or/c #f 'pkcs1-v1.5 'pss 'pss*) = #f

procedure

(pk-verify-digest pk    
  di    
  dgst    
  sig    
  [#:pad padding])  boolean?
  pk : pk-key?
  di : (or/c digest-spec? digest-impl?)
  dgst : bytes?
  sig : bytes?
  padding : (or/c #f 'pkcs1-v1.5 'pss) = #f
Equivalent to (pk-sign pk dgst #:digest di #:pad padding) and (pk-verify pk dgst sig #:digest di #:pad padding), respectively.

5.3 PK Encryption

In PK encryption, the sender uses the public key of the intended receiver to encrypt a message; the receiver decrypts the message with the receiver’s own private key. Only short messages can be directly encrypted using PK cryptosystems (limits are generally proportional to the size of the PK keys), so a typical approach is to encrypt the message using a symmetric cipher with a randomly-generated key (sometimes called the bulk encryption key) and encrypt that key using PK cryptography. The symmetric-key-encrypted message and PK-encrypted symmetric key are sent together, perhaps with additional data such as a MAC. PK encryption is supported by the RSA cryptosystem.

procedure

(pk-encrypt pk msg [#:pad padding])  bytes?

  pk : pk-key?
  msg : bytes?
  padding : (or/c #f 'pkcs1-v1.5 'oaep) = #f

procedure

(pk-decrypt pk msg [#:pad padding])  bytes?

  pk : private-key?
  msg : bytes?
  padding : (or/c #f 'pkcs1-v1.5 'oaep) = #f
Encrypt or decrypt, respectively, the message msg using PK key pk.

If pk is an RSA key, then padding choses between PKCS#1-v1.5 padding and OAEP padding [PKCS1]. If padding is #f, then an implementation-dependent mode is chosen. For all other cryptosystems, padding must be #f.

If msg is too large to encrypt using pk, then an exception is raised.

5.4 PK Key Agreement

In PK key agreement (sometimes called key exchange) two parties derive a shared secret by exchanging public keys. Each party can compute the secret from their own private key and the other’s public key, but it is believed infeasible for an observer to compute the secret from the two public keys alone. PK secret derivation is supported by the 'dh, 'ec, and 'ecx cryptosystems.

procedure

(pk-derive-secret pk peer-pk)  bytes?

  pk : private-key?
  peer-pk : (or/c pk-key? bytes?)
Returns the shared secret derived from the private key pk and the public key peer-pk. If peer-pk is a PK key, it must be a key belonging to the same cryptosystem and implementation as pk; otherwise an exception is raised. If peer-pk is a bytestring, an exception is raised if it cannot be interpreted as raw public key data.

Note that the derived secret is a deterministic function of the private keys: if two parties perform secret derivation twice, they will produce the same secret both times. In addition, the secret is not uniformly distributed. For these reasons, the derived secret should not be used directly as a key; instead, it should be used to generate key material using a process such as described in RFC 2631 [RFC2631].

5.5 PK External Representations

This section describes serialization of public and private keys in various formats.

procedure

(pk-key->datum pk fmt)  printable/c

  pk : pk-key?
  fmt : symbol?
Returns a datum representing the key pk, where the encoding is selected by fmt. Unless noted below, the result is a bytestring (bytes?). The following fmt options are supported:

More formats may be added in future versions of this library.

Changed in version 1.1 of package crypto-lib: Added 'OneAsymmetricKey, 'rkt-private, and 'rkt-public support.

procedure

(datum->pk-key datum fmt [factories])  pk-key?

  datum : any/c
  fmt : symbol?
  factories : (or/c crypto-factory? (listof crypto-factory?))
   = (crypto-factories)
Parses datum and returns a PK key associated with an implementation from factories. If no implementation in factories supports fmt, an exception is raised.

See pk-key->datum for information about the fmt argument.

procedure

(pk-parameters->datum pkp fmt)  printable/c

  pkp : pk-parameters?
  fmt : symbol?
Returns a datum representing the key parameters pkp, where the encoding is selected by fmt. Unless noted below, the result is a bytestring (bytes?). The following fmt options are supported:

More formats may be added in future versions of this library.

Changed in version 1.1 of package crypto-lib: Added 'rkt-params support.

procedure

(datum->pk-parameters datum fmt [factories])  pk-parameters?

  datum : any/c
  fmt : symbol?
  factories : (or/c crypto-factory? (listof crypto-factory?))
   = (crypto-factories)
Parses datum and returns a key-parameters value associated with an implementation in factories. If no implementation is found that accepts fmt, an exception is raised.

5.6 PKCS #8 Encrypted Private Keys

Added in version 1.5 of package crypto-lib.

procedure

(pkcs8-encrypt/pbkdf2-hmac password    
  pk    
  [#:digest digest-spec    
  #:iterations iterations    
  #:cipher cipher-spec    
  #:key-size key-size])  bytes?
  password : bytes?
  pk : (or/c private-key? bytes?)
  digest-spec : digest-spec? = 'sha512
  iterations : exact-positive-integer? = (expt 2 16)
  cipher-spec : cipher-spec? = '(aes cbc)
  key-size : exact-positive-integer?
   = (cipher-default-key-size cipher-spec)

procedure

(pkcs8-encrypt/scrypt password    
  pk    
  [#:N N    
  #:r r    
  #:p p    
  #:cipher cipher-spec    
  #:key-size key-size])  bytes?
  password : bytes?
  pk : (or/c private-key? bytes?)
  N : exact-positive-integer? = (expt 2 14)
  r : exact-positive-integer? = 8
  p : exact-positive-integer? = 1
  cipher-spec : cipher-spec? = '(aes cbc)
  key-size : exact-positive-integer?
   = (cipher-default-key-size cipher-spec)
Produce a DER-encoded EncryptedPrivateKeyInfo [PKCS8] containing the private key pk encrypted with a key derived from password. If pk is a private key object (private-key?), it is converted to a byte string using OneAsymmetricKey format. If pk is a byte string, it should be a OneAsymmetricKey or PrivateKeyInfo encoding, but the structure of the byte string is not inspected.

See pbkdf2-hmac for the meaning of the iterations argument, and see scrypt for the meanings of the N, r, and p arguments.

The following values of cipher-spec are currently supported: '(aes cbc), '(des-ede3 cbc), '(aes gcm), and '(chacha20-poly1305 stream).

The following values of digest-spec are currently supported: 'sha1, 'sha224, 'sha256, 'sha384, and 'sha512.

Compatibility with OpenSSL 1.1.0 has been tested with '(aes cbc) and '(des-ede3 cbc).

procedure

(pkcs8-decrypt-bytes password p8-epki)  bytes?

  password : bytes?
  p8-epki : bytes?
Decrypts the DER-encoded EncryptedPrivateKeyInfo p8-epki using password and returns the unencrypted private key as a byte string. The structure of the decrypted byte string is not inspected.

If the wrong password is given, then an exception will probably be raised, but for non-AEAD ciphers (such as AES-CBC), there is a possibility that the decryption will succeed and return nonsense.

procedure

(pkcs8-decrypt-key password p8-epki)  private-key?

  password : bytes?
  p8-epki : bytes?
Decrypts the DER-encodedEncryptedPrivateKeyInfo p8-epki using password and parses the unencrypted private key as OneAsymmetricKey.

Equivalent to the following:

(datum->pk-key (pkcs8-decrypt-bytes password p8-epki) 'OneAsymmetricKey)

Bibliography

[AKP] “RFC 5958: Asymmetric Key Packages.” https://tools.ietf.org/html/rfc5958
[PKCS1] “PKCS #1: RSA Cryptography, version 2.1.” https://tools.ietf.org/html/rfc3447
[PKCS3] “PKCS #3: Diffie-Hellman Key-Agreement Standard.”
[PKCS8] “PKCS #8: Private-Key Information Syntax Specification, version 1.2.” https://tools.ietf.org/html/rfc5208
[PKIX] “RFC 5280: Internet X.509 Public Key Infrastructure: Certificate and CRL Profile.” https://tools.ietf.org/html/rfc5280
[PKIX-AlgId] “RFC 3279: Algorithms and Identifiers for the Internet X.509 Public Key Infrastructure Certificate and Certificate Revocation List (CRL) Profile.” https://tools.ietf.org/html/rfc3279
[PKIX-EdC] “RFC 8410: Algorithm Identifiers for Ed25519, Ed448, X25519 and X448 for use in the Internet X.509 Public Key Infrastructure.” https://tools.ietf.org/html/rfc8410
[RFC2631] “RFC 2631: Diffie-Hellman Key Agreement Method.” https://tools.ietf.org/html/rfc2631
[SEC1] “SEC 1: Elliptic Curve Cryptography.” http://www.secg.org/sec1-v2.pdf