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 = pqcrypto.sign.mqdss64.open(sm,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 == sig.open(sm,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()
H.update(m)
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)
mac.verify(a,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 = pqcrypto.stream.aes256ctr
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.new(k,AES.MODE_CTR,counter=Counter.new(128,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 = pqcrypto.stream.salsa20
auth = pqcrypto.onetimeauth.poly1305
with os.fdopen(0,"rb") as f: c = f.read()
with os.fdopen(8,"rb") as f: sk = f.read()
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:]
auth.verify(a,c,kauth)
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.