Encrypted volume passphrase change infrastructure

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

_PASSPHRASE_CHARSET = ("0123456789"                      "ABCDEFGHIJKLMNOPQRSTUVWXYZ"                       "abcdefghijklmnopqrstuvwxyz"                       "./") _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
 * 1) Keep the character set size a power of two to make sure all characters are
 * 2) equally likely
 * 1) 20 chars * 6 bits per char = 120 "bits of security"

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),)