From Fedora Project Wiki

See Encrypted volume passphrase change or recovery GUI for details.

import nss.nss
import os
import pycryptsetup
import tempfile
import volume_key

def _cs_log(unused_pri, txt):
    print 'cryptsetup: %s' % (txt,)

def _cs_askyes(unused_pri, txt):
    print 'cryptsetup %s (y/n)?' % (txt,)
    raise NotImplementedError

def passphrase_slot(device, passphrase):
    '''Return slot number used for PASSPHRASE in DEVICE.'''
    c = pycryptsetup.CryptSetup(_cs_askyes, _cs_log)
    if c.isLuks(device) != 0:
        raise ValueError, '%s is not a LUKS device' % (device,)
    dm_name = os.path.basename(tempfile.mktemp(dir='/dev/mapper'))
    key_file = c.prepare_passphrase_file(passphrase)
    try:
        # The C API allows setting dm_name to NULL in order to only check the
        # result without creating a dm-crypt device.  This would make luksClose
        # unnecessary.
        slot = c.luksOpen(device, dm_name, key_file)
    finally:
        os.remove(key_file)
    c.luksClose(dm_name)
    return slot

def get_info(packet):
    '''Parse an escrpw packet, create an object that returns the  metadata.'''
    pass # FIXME

# Keep the character set size a power of two to make sure all characters are
# equally likely
_PASSPHRASE_CHARSET = ("0123456789"
                       "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
                       "abcdefghijklmnopqrstuvwxyz"
                       "./")
# 20 chars * 6 bits per char = 120 "bits of security"
_PASSPHRASE_LENGTH = 20
def add_backup_passphrase(dest, device, passphrase, cert_file):
    '''Add PASSPHRASE to DEVICE, store it in an escrow packet DEST.'''
    nss.nss.nss_init_nodb()

    with open(cert_file, 'rb') as f:
        cert_data = f.read()

    def known_passphrase_cb(unused_prompt, failed_attempts):
        if failed_attempts == 0:
            return passphrase
        return None
    vol = volume_key.Volume.open(device)
    ui = volume_key.UI()
    ui.passphrase_cb = known_passphrase_cb
    vol.get_secret(volume_key.SECRET_DEFAULT, ui)

    rnd = nss.nss.generate_random(_PASSPHRASE_LENGTH)
    cs = _PASSPHRASE_CHARSET
    new_passphrase = ''.join([cs[ord(c) % len(cs)] for c in rnd])
    # Make the result easier to read
    parts = []
    for i in xrange(0, len(new_passphrase), 5):
        parts.append(new_passphrase[i : i + 5])
    new_passphrase = "-".join(parts)

    vol.add_secret(volume_key.SECRET_PASSPHRASE, new_passphrase)
    packet = vol.create_packet_assymetric_from_cert_data \
        (volume_key.SECRET_PASSPHRASE, cert_data, ui)

    with open(dest, 'wb') as f:
        f.write(packet)

def store_passphrase(dest, device, passphrase, cert_file):
    '''Create an escrow packet for DEVICE with PASSPHRASE, write it to DEST.

    PASSPHRASE must already have been added to DEVICE.

    '''
    nss.nss.nss_init_nodb()

    with open(cert_file, 'rb') as f:
        cert_data = f.read()

    def known_passphrase_cb(unused_prompt, failed_attempts):
        if failed_attempts == 0:
            return passphrase
        return None
    vol = volume_key.Volume.open(device)
    ui = volume_key.UI()
    ui.passphrase_cb = known_passphrase_cb
    vol.get_secret(volume_key.SECRET_PASSPHRASE, ui)
    packet = vol.create_packet_assymetric_from_cert_data \
        (volume_key.SECRET_PASSPHRASE, cert_data, ui)

    with open(dest, 'wb') as f:
        f.write(packet)

if __name__ == '__main__':
    cert_file = 'cert.pem'
    device = '/dev/loop0'
    passphrase = '8sme84zu'
    if False:
        store_passphrase('packet', device, passphrase, cert_file)
    if True:
        add_backup_passphrase('packet', device, passphrase, cert_file)
    if False:
        print 'Slot %d' % (passphrase_slot(device, passphrase),)