Location and interface conventions

To access the Python functions provided by libpqcrypto, add /home/libpqcrypto/python to your PYTHONPATH:

    export PYTHONPATH="/home/libpqcrypto/python${PYTHONPATH+:$PYTHONPATH}"

Also insert

    import pqcrypto

into your Python 2 or Python 3 script.

All inputs and outputs are byte strings, the bytes type in Python (which is the same as str in Python 2 but different in Python 3). Verification failures and decapsulation failures raise exceptions.

Signature systems

There is a unified interface for all signature systems; these examples use mqdss64. To generate a key pair:

    pk,sk = pqcrypto.sign.mqdss64.keypair()

To sign a message m:

    sm = pqcrypto.sign.mqdss64.sign(m,sk)

To recover a message from a signed message:

    m =,pk)

As a larger example, the following test script signs and then recovers a message under a random key pair:

    import pqcrypto
    sig = pqcrypto.sign.mqdss64
    pk,sk = sig.keypair()
    m = b"hello world"
    sm = sig.sign(m,sk)
    assert m ==,pk)

Key-encapsulation mechanisms

There is a unified interface for all KEMs; these examples use newhope1024cca. To generate a key pair:

    pk,sk = pqcrypto.kem.newhope1024cca.keypair()

To generate a ciphertext c encapsulating a randomly generated session key k:

    c,k = pqcrypto.kem.newhope1024cca.enc(pk)

To recover a session key from a ciphertext:

    k = pqcrypto.kem.newhope1024cca.dec(c,sk)

As a larger example, the following test script creates a key pair, creates a ciphertext and session key, and then recovers the session key from the ciphertext:

    import pqcrypto
    kem = pqcrypto.kem.newhope1024cca
    pk,sk = kem.keypair()
    c,k = kem.enc(pk)
    assert k == kem.dec(c,sk)

As another example, the following script generates 10000 key pairs and checks that they are all different:

    import pqcrypto
    kem = pqcrypto.kem.newhope1024cca
    n = 10000
    assert len(set(kem.keypair() for i in range(n))) == n

Lower-level primitives

There are interfaces for various lower-level functions such as stream ciphers (stream), one-time authenticators (onetimeauth), and hash functions (hash). For example, the following test script checks the SHA-512 hash of a string against Python's hashlib library:

    import pqcrypto
    m = pqcrypto.randombytes(1234567)
    h = pqcrypto.hash.sha512(m)

    import hashlib
    H = hashlib.sha512()
    assert H.digest() == h

The following test script computes and checks an authenticator:

    import pqcrypto
    mac = pqcrypto.onetimeauth.poly1305
    k = pqcrypto.randombytes(mac.klen)
    m = pqcrypto.randombytes(1234567)
    a = mac.auth(m,k)

Beware that the key for a one-time authenticator must not be used for more than one message.

The following test script checks an AES-256-CTR ciphertext against Python's Crypto library:

    import pqcrypto
    cipher =

    k = pqcrypto.randombytes(cipher.klen)
    n = pqcrypto.randombytes(cipher.nlen)
    m = pqcrypto.randombytes(1234567)
    c = cipher.xor(m,n,k)
    assert m == cipher.xor(c,n,k)

    from Crypto.Cipher import AES
    from Crypto.Util import Counter
    import binascii

    nint = int(binascii.hexlify(n),16)
    s =,AES.MODE_CTR,,initial_value=nint))
    assert s.encrypt(m) == c

Beware that nonces must be handled carefully in general, to avoid having the same nonce used for two messages under the same key; and even more carefully for AES-CTR, since each new 16-byte message block moves to a new nonce.

The following script has the same effect as the pq-decrypt-mceliece8192128 command:

    import os
    import sys

    import pqcrypto
    kem = pqcrypto.kem.mceliece8192128
    hash = pqcrypto.hash.shake256
    enc =
    auth = pqcrypto.onetimeauth.poly1305

    with os.fdopen(0,"rb") as f: c =
    with os.fdopen(8,"rb") as f: sk =
    k = kem.dec(c[-kem.clen:],sk)
    c = c[:-kem.clen]

    h = hash(k)
    kenc,h = h[:enc.klen],h[enc.klen:]
    kauth = h[:auth.klen]

    a,c = c[:auth.alen],c[auth.alen:]

    n = b"\0"*enc.nlen
    m = enc.xor(c,n,kenc)
    with os.fdopen(1,"wb") as f: f.write(m)

Version: This is version 2018.04.19 of the "Python API" web page.