Commits

Erik Grinaker committed b0d11bf

moved bundle dir from src/lib/ to src/

Comments (0)

Files changed (13)

 	data/ui/Makefile
 	po/Makefile.in
 	src/Makefile
+	src/bundle/Makefile
 	src/lib/Makefile
-	src/lib/bundle/Makefile
 	src/lib/datahandler/Makefile
 	src/wrap/Makefile
 	src/wrap/crack/Makefile
 # $Id$
 #
 
-SUBDIRS		= lib wrap
+SUBDIRS		= bundle lib wrap
 bin_SCRIPTS	= revelation
 libexec_SCRIPTS	= revelation-applet
 EXTRA_DIST	= revelation.in revelation-applet.in

src/bundle/AfSplitter.py

+"""Implementation of anti-forensic information splitting
+
+The AFsplitter supports secure data destruction crucial for secure on-disk key
+management. The key idea is to bloat information and therefor improving the
+chance of destroying a single bit of it. The information is bloated in such a
+way, that a single missing bit causes the original information become
+unrecoverable. The theory behind AFsplitter is presented in TKS1.
+
+The interface is simple. It consists of two functions:
+
+AFSplit(data, stripes, digestmod=sha)
+AFMerge(data, stripes, digestmod=sha)
+
+AFSplit operates on data and returns information splitted data.  AFMerge does
+just the opposite: uses the information stored in data to recover the original
+splitted data.
+
+http://clemens.endorphin.org/AFsplitter
+TKS1 paper from http://clemens.endorphin.org/publications
+
+Copyright 2006 John Lenz <lenz@cs.wisc.edu>
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+ 
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+ 
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA
+
+http://www.gnu.org/copyleft/gpl.html
+"""
+
+
+import sha, string, math, struct
+from Crypto.Util.randpool import RandomPool
+from Crypto.Cipher import XOR
+
+def _xor(a, b):
+	"""Internal function to performs XOR on two strings a and b"""
+
+	xor = XOR.new(a)
+	return xor.encrypt(b)
+
+def _diffuse(block, size, digest):
+	"""Internal function to diffuse information inside a buffer"""
+
+	# Compute the number of full blocks, and the size of the leftover block
+	full_blocks = int(math.floor(float(len(block)) / float(digest.digest_size)))
+	padding = len(block) % digest.digest_size
+
+	# hash the full blocks
+	ret = ""
+	for i in range(0, full_blocks):
+
+		hash = digest.new()
+		hash.update(struct.pack(">I", i))
+		hash.update(block[i*digest.digest_size:(i+1)*digest.digest_size])
+		ret += hash.digest()
+
+	# Hash the remaining data
+	if padding > 0:
+		hash = digest.new()
+		hash.update(struct.pack(">I", full_blocks))
+		hash.update(block[full_blocks * digest.digest_size:])
+		ret += hash.digest()[:padding]
+
+	return ret
+
+def AFSplit(data, stripes, digestmod=sha):
+	"""AF-Split data using digestmod.  Returned data size will be len(data) * stripes"""
+
+	blockSize = len(data)
+
+	rand = RandomPool()
+
+	bufblock = "\x00" * blockSize
+
+	ret = ""
+	for i in range(0, stripes-1):
+
+		# Get some random data
+		rand.randomize()
+		rand.stir()
+		r = rand.get_bytes(blockSize)
+		if rand.entropy < 0:
+			print "Warning: RandomPool entropy dropped below 0"
+
+		ret += r
+		bufblock = _xor(r, bufblock)
+		bufblock = _diffuse(bufblock, blockSize, digestmod)
+		rand.add_event(bufblock)
+
+	ret += _xor(bufblock, data)
+	return ret
+
+def AFMerge(data, stripes, digestmod=sha):
+	"""AF-Merge data using digestmod.  len(data) must be a multiple of stripes"""
+
+	if len(data) % stripes != 0:
+		raise "ERROR: data is not a multiple of strips"
+
+	blockSize = len(data) / stripes
+
+	bufblock = "\x00" * blockSize
+	for i in range(0, stripes - 1):
+		bufblock = _xor(data[i*blockSize:(i+1)*blockSize], bufblock)
+		bufblock = _diffuse(bufblock, blockSize, digestmod)
+
+	return _xor(data[(stripes-1)*blockSize:], bufblock)

src/bundle/Makefile.am

+## Process this file with automake to produce Makefile.in
+#
+# src/bundle/Makefile.am
+#
+# $Id$
+#
+
+bundledir	= $(pyexecdir)/revelation/bundle
+bundle_PYTHON	= \
+	__init__.py \
+	AfSplitter.py \
+	PBKDFv2.py \
+	luks.py
+

src/bundle/PBKDFv2.py

+#!/usr/bin/env python
+# -*- coding: iso-8859-1 -*-
+"""
+  - Implements the PKCS#5 v2.0: Password-Based Cryptography Standard
+    from RSA Laboratories. RFC2898 http://www.rfc-editor.org/rfc/rfc2898.txt
+
+Modifications by John Lenz <lenz@cs.wisc.edu>, April 2006
+  + Fix the PBKDFv2 algorithm so it is correct
+  + Use Cipher.XOR instead of slow python xor
+  + other performance improvements
+
+Original Code written by:
+
+Copyright (C) 2004 - Lars Strand <lars strand at gnist org>
+ 
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+ 
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+ 
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA
+
+http://www.gnu.org/copyleft/gpl.html
+
+"""
+
+import struct, string, math, sha, hmac # RFC2104
+from Crypto.Cipher import XOR
+
+################ PBKDFv2
+class PBKDFv2:
+    """Implements the PKCS#5 v2.0: Password-Based Cryptography Standard
+    from RSA Laboratories. RFC2898
+
+    http://www.rfc-editor.org/rfc/rfc2898.txt
+    """
+
+    ################ init
+    def __init__(self):
+
+        # length of pseudorandom function: 20 for SHA-1, 16 for MD5
+        self.hLen = 20
+        
+    ################ makeKey
+    def makeKey(self, P, S, c, dkLen, digestmod=sha):
+        """
+           Input:   P         password, an octet string
+                    S         salt, an octet string
+                    c         iteration count, a positive integer (>1000)
+                    dkLen     intended length on octets of the derived key, a positive integer,
+                              at most (2^32 - 1) * hLen
+                    digestmod Digest used, passed to hmac module
+
+           Output   DK    derived key, a dkLen-octet string
+           """
+
+        # do some sanity checks
+        try:
+            str(P); str(S); int(c); float(dkLen); int(c)
+        except:
+            print "P = %s, S = %s, c = %s, dkLen = %s:" % (P, S, c, dkLen)
+            raise "ERROR! Input is not correct!"
+
+        # Step 1: if dkLen is larger than maximum possible key - exit
+        if dkLen > ((2^32 - 1) * self.hLen):
+            maxlength = (2^32 - 1) * self.hLen
+            raise "ERROR! Key is to large! Maxlength is", str(maxlength)
+
+        # Step 2:
+        # Let l be the number of hLen-octet blocks in the derived key, rounding up
+        # and let r be the number of octets in the last block
+        l = math.ceil(dkLen / float(self.hLen))
+        #if (dkLen % float(self.hLen)): l = int(l) + 1 # round up if necessary
+        r = dkLen - (l - 1) * self.hLen
+
+        # Step 3:
+        # For each block of the derived key, apply the function F to the
+        # password P, the salt S, the iteration count c, and the block index
+        # to compute the block
+        T = ""
+        for blockindex in range(int(l)):
+            T += self.F(P, S, c, blockindex, digestmod)
+        # Step 4 - extract the first dkLen octet to produce a derived key DK
+        DK = T[:dkLen]
+
+        # Step 5 - return the derived key DK
+        return DK
+            
+    ################ F
+    def F(self, P, S, c, i, digest):
+        """For each block of the derived key, apply this function.
+
+        Notation:
+        ||   = concatenation operator
+        PRF  = Underlying pseudorandom function
+        
+        The function F is defined as the exclusive-or sum of the first c
+        iterates if the underlying pseudorandom function PRF applied to
+        the password P and the concatenation of the salt S and the block
+        index i:
+
+        F(P, S, c, i) = U1 XOR U2 XOR ... XOR Uc
+
+        where
+
+        U1 = PRF(P, S || INT(i)),
+        U2 = PRF(P, U1)
+        ...
+        Uc = PRF(P, Uc-1)
+        """
+
+        # The pseudorandom function, PRF, used is HMAC-SHA1 (rfc2104)
+        iteration = 1
+
+        # the first iteration; P is the key, and a concatination of
+        # S and blocknumber is the message
+	istr = struct.pack(">I", i+1)
+	PRFMaster = hmac.new(P,digestmod=digest)
+	PRF = PRFMaster.copy()
+	PRF.update(S)
+	PRF.update(istr)
+        U = PRF.digest() # the first iteration
+
+	Fbuf = U
+
+        while iteration < c:                  # loop through all iterations
+	    PRF = PRFMaster.copy()
+	    PRF.update(U)
+            U = PRF.digest()
+            Fbuf = self._xor(U, Fbuf)    # XOR this new iteration with the old one
+            iteration += 1
+        return Fbuf
+
+    ################ xor
+    def _xor(self, a, b):
+        """Performs XOR on two strings a and b"""
+
+        if len(a) != len(b):
+            raise "ERROR: Strings are of different size! %s %s" % (len(a), len(b))
+
+	xor = XOR.new(a)
+	return xor.encrypt(b)

src/bundle/__init__.py

+#
+# Revelation 0.4.7 - a password manager for GNOME 2
+# http://oss.codepoet.no/revelation/
+# $Id$
+#
+# Bundled third-party modules
+#
+#
+# Copyright (c) 2003-2006 Erik Grinaker
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+#
+
+import AfSplitter, PBKDFv2, luks
+
+"""
+Implements the LUKS (Linux Unified Key Setup) Version 1.0
+on disk specification inside a file.
+
+http://luks.endorphin.org/
+
+LUKS offers:
+    * compatiblity via standardization,
+    * secure against low entropy attacks,
+    * support for multiple keys,
+    * effective passphrase revocation,
+
+This module is compatible with dm-crypt and cryptsetup tools for the Linux
+kernel, as long as hashSpec="sha1" is used. Loopback files or partitions created
+with the linux kernel can be decrypted using this module.  FreeOTFE
+(http://www.freeotfe.org/) should provide support for reading and writing on
+Windows.
+
+This module has one class LuksFile.
+
+Loading a LUKS disk image (use both, one after another):
+- load_from_file(file)
+- open_any_key(password)
+
+Creating a new LUKS disk image (use both):
+- create(file, cipherName, cipherMode, hashSpec, masterSize, stripes)
+- set_key(0, password, iterations)
+
+Once a file is unlocked (either because it was just created or
+open_any_key() returned True), you can perform the key operations:
+- enabled_key_count()
+- key_information(keyIndex)
+- set_key(keyIndex, password, iterations)
+- delete_key(keyIndex)
+
+Once a file is unlocked, you can perform data encryption/decryption with
+- data_length()
+- encrypt_data(offset, data)
+- decrypt_data(offset, length)
+- truncate(length)
+
+Finally, to close the file:
+- close()
+
+Copyright 2006 John Lenz <lenz@cs.wisc.edu>
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+ 
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+ 
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA
+
+http://www.gnu.org/copyleft/gpl.html
+"""
+
+import os, math, struct, stat, sha, md5
+
+from Crypto.Util.randpool import RandomPool
+from Crypto.Cipher import *
+from Crypto.Hash import *
+import PBKDFv2, AfSplitter
+
+class LuksFile:
+	"""Implements the LUKS (Linux Unified Key Setup) Version 1.0 http://luks.endorphin.org/"""
+
+	LUKS_FORMAT = ">6sH32s32s32sII20s32sI40s"
+	LUKS_MAGIC = "LUKS\xba\xbe"
+
+	LUKS_KEY_DISABLED = 0x0000DEAD
+	LUKS_KEY_ENABLED = 0x00AC71F3
+
+	SECTOR_SIZE = 512.0
+
+	KEY_STRIPES = 4000
+	SALT_SIZE = 32
+	DIGEST_SIZE = 20
+
+	def __init__(self):
+		self.file = None
+		self.masterKey = None
+		self.ivGen = None
+
+	# Read the header from the file descriptor
+	def load_from_file(self, file):
+		"""Initialize this LuksFile class from the file.
+
+		The file parameter should be an object implementing the File Object API
+        	This function will error if the file is not a LUKS partition (the LUKS_MAGIC does not match)
+		"""
+
+		if self.file != None:
+			raise "This LuksFile has already been initialized"
+
+		# Read the main parameters
+		self.file = file
+		self.file.seek(0)
+
+		self.magic, \
+		self.version, \
+		cipherName, \
+		cipherMode, \
+		hashSpec, \
+		self.payloadOffset, \
+		self.keyBytes, \
+		self.mkDigest, \
+		self.mkDigestSalt, \
+		self.mkDigestIterations, \
+		self.uuid = \
+		struct.unpack(self.LUKS_FORMAT, self.file.read(208))
+
+		# check magic
+		if self.magic != self.LUKS_MAGIC:
+			self.file = None
+			raise "%s is not a LUKS data file" % filename
+
+		# Check the hash and cipher
+		self.hashSpec = hashSpec.strip(" \x00")
+		self.hash = self._check_hash(self.hashSpec)
+		self._check_cipher(cipherName.strip(" \x00"), cipherMode.strip(" \x00"))
+		
+		# Load the key information
+		self.keys = [None] * 8
+		for i in range(0, 8):
+			self.keys[i] = self._key_block()
+			self.keys[i].load_from_str(self.file.read(48))
+
+		# set the digest to be the correct size
+		self.mkDigest = self.mkDigest[:self.hash.digest_size]
+
+		self.masterKey = None
+
+	# Generate a new header
+	def create(self, file, cipherName, cipherMode, hashSpec, masterSize, stripes):
+		"""Initializes the file class passed in with the LUKS header
+
+        	Parameters
+		   cipherName: aes, cast5, blowfish
+		   cipherMode: cbc-plain, cbc-essiv:<hash>
+		   hashSpec: sha1, sha256, md5, ripemd160
+		   masterSize: length of the master key in bytes (must match cipher)
+		   stripes: number of stripes when Af Splitting keys
+
+		For compatibility with the Linux kernel dm-crypt, hashSpec must equal "sha1"
+
+		cbc-plain uses the sector number as the IV.  This has a weakness: an attacker
+		may be able to detect the existance of watermarked files.
+		cbc-essiv:<hash> protects against the weakness in cbc-plain, but is 
+		slightly slower. The digest size of the hash function passed to cbc-essiv
+		must match the key size of the cipher.  
+		aes-cbc-essiv:sha256 works, while aes-cbc-essiv:sha1 does not
+
+		For more information about the details of the attacks and risk assesment, see
+		http://clemens.endorphin.org/LinuxHDEncSettings
+		"""
+
+		if self.file != None:
+			raise "This LuksFile has already been initialized"
+
+		self._check_cipher(cipherName, cipherMode)
+
+		self.magic = self.LUKS_MAGIC
+		self.version = 1
+		self.mkDigestIterations = 10
+		self.keyBytes = masterSize
+		self.hashSpec = hashSpec
+		self.hash = self._check_hash(hashSpec)
+
+		rand = RandomPool(self.SALT_SIZE + 16 + masterSize)
+
+		# Generate the salt
+		self.mkDigestSalt = rand.get_bytes(self.SALT_SIZE)
+
+		# Generate a random master key
+		self.masterKey = rand.get_bytes(self.keyBytes)
+		self.ivGen.set_key(self.masterKey)
+
+		# generate the master key digest
+		pbkdf = PBKDFv2.PBKDFv2()
+		self.mkDigest = pbkdf.makeKey(self.masterKey, self.mkDigestSalt, self.mkDigestIterations, self.hash.digest_size, self.hash)
+
+		# init the key information
+		currentSector = math.ceil(592.0 / self.SECTOR_SIZE)
+		alignSectors = 4096 / self.SECTOR_SIZE
+		blocksPerStripe = math.ceil(float(self.keyBytes * stripes) / self.SECTOR_SIZE)
+
+		self.keys = [None] * 8
+		for i in range(0, 8):
+			if currentSector % alignSectors > 0:
+				currentSector += alignSectors - currentSector % alignSectors
+			self.keys[i] = self._key_block()
+			self.keys[i].create(currentSector, stripes, self.LUKS_KEY_DISABLED)
+			currentSector += blocksPerStripe
+
+		# Set the data offset
+		if currentSector % alignSectors > 0:
+			currentSector += alignSectors - currentSector % alignSectors
+		self.payloadOffset = currentSector
+
+		# Generate a UUID for this file
+		self._uuidgen(rand)
+
+		# Create a new file, and save the header into it
+		self.file = file
+		self._save_header()
+
+		# Write FF into all the key slots
+		FFData = "\xFF" * int(self.SECTOR_SIZE)
+		for i in range(0, 8):
+			self.file.seek(int(self.keys[i].keyMaterialOffset))
+			for sector in range(0, int(blocksPerStripe)):
+				self.file.write(FFData)
+
+		# Flush the file to disk
+		try:
+			self.file.flush()
+			os.fsync(self.file.fileno())
+		except:
+			# We might get an error because self.file.fileno() does not exist on StringIO
+			pass
+
+	def set_key(self, keyIndex, password, iterations, checkMinStripes=0):
+		"""Sets the key block at keyIndex using password
+
+		Sets the key block at keyIndex using password, hashed iterations
+		times using PBKDFv2 (RFC2104).  This LuksFile must be unlocked by
+		calling open_any_key() before calling this function.
+		checkMinStripes is used to detect basic header manipulation, since
+		the number of stripes for the key is set by create(), before
+		we write a password to disk we make sure the key is not weak because
+		of a small number of stripes.
+
+		This function will raise an error if the key is already enabled.
+		"""
+
+		# Some checks
+		if self.masterKey == None:
+			raise "A key has not been unlocked.  Call open_any_key() first."
+
+		if keyIndex < 0 or keyIndex > 7:
+			raise "keyIndex out of range"
+
+		key = self.keys[keyIndex]
+		if key.active != self.LUKS_KEY_DISABLED:
+			raise "Key is active.  Delete the key and try again"
+
+		if checkMinStripes == 0: checkMinStripes = self.KEY_STRIPES
+		if key.stripes < checkMinStripes:
+			raise "Key section %i contains too few stripes.  Header manipulation?" % keyIndex
+
+		key.passwordIterations = iterations
+
+		# Generate a random salt for this key
+		rand = RandomPool(self.SALT_SIZE)
+		key.passwordSalt = rand.get_bytes(self.SALT_SIZE)
+
+		# Hash the key using PBKDFv2
+		pbkdf = PBKDFv2.PBKDFv2()
+		derived_key = pbkdf.makeKey(password, key.passwordSalt, key.passwordIterations, self.keyBytes, self.hash)
+
+		# Split the key into key.stripes
+		AfKey = AfSplitter.AFSplit(self.masterKey, key.stripes, self.hash)
+
+		AfKeySize = len(AfKey)
+		if AfKeySize != key.stripes * self.keyBytes:
+			raise "ERROR: AFSplit did not return the correct length of key"
+
+		# Set the key for IV generation
+		self.ivGen.set_key(derived_key)
+
+		# Encrypt the splitted key using the hashed password
+		AfSectors = int(math.ceil(float(AfKeySize) / self.SECTOR_SIZE))
+		for sector in range(0, AfSectors):
+			self._encrypt_sector(derived_key, key.keyMaterialOffset + sector, sector, \
+                               AfKey[int(sector*self.SECTOR_SIZE):int((sector+1)*self.SECTOR_SIZE)])
+
+		key.active = self.LUKS_KEY_ENABLED
+
+		# Reset the key used for to IV generation in data mode
+		self.ivGen.set_key(self.masterKey)
+
+		self._save_header()
+
+	def open_key(self, keyIndex, password):
+		"""Open a specific keyIndex using password.  Returns True on success"""
+
+		if self.file == None:
+			raise "LuksFile has not been initialized"
+
+		if keyIndex < 0 or keyIndex > 7:
+			raise "keyIndex is out of range"
+
+		key = self.keys[keyIndex]
+
+		if key.active != self.LUKS_KEY_ENABLED:
+			return False
+
+		# Hash the password using PBKDFv2
+		pbkdf = PBKDFv2.PBKDFv2()
+		derived_key = pbkdf.makeKey(password, key.passwordSalt, key.passwordIterations, self.keyBytes, self.hash)
+
+		# Setup the IV generation to use this key
+		self.ivGen.set_key(derived_key)
+
+		# Decrypt the master key data using the hashed password
+		AfKeySize = key.stripes * self.keyBytes
+		AfSectors = int(math.ceil(float(AfKeySize) / self.SECTOR_SIZE))
+		AfKey = ""
+		for sector in range(0, AfSectors):
+			AfKey += self._decrypt_sector(derived_key, key.keyMaterialOffset + sector, sector)
+		AfKey = AfKey[0:AfKeySize]
+
+		# Merge the decrypted master key
+		masterKey = AfSplitter.AFMerge(AfKey, key.stripes, self.hash)
+
+		# Check if the password was the correct one, by checking the master key digest
+		checkDigest = pbkdf.makeKey(masterKey, self.mkDigestSalt, self.mkDigestIterations, self.hash.digest_size, self.hash)
+		
+		# Since the header only stores DIGEST_SIZE (which is smaller than sha256 digest size)
+		#   trim the digest to DIGEST_SIZE
+		checkDigest = checkDigest[:self.DIGEST_SIZE]
+
+		if checkDigest != self.mkDigest:
+			return False
+
+		self.masterKey = masterKey
+		self.ivGen.set_key(self.masterKey)
+		return True
+		
+
+	def open_any_key(self, password):
+		"""Try to open any enabled key using the provided password.  Returns index number on success, or None"""
+
+		if self.file == None:
+			raise "LuksFile has not been initialized"
+
+		for i in range(0, 8):
+			if self.open_key(i, password):
+				return i
+		return None
+
+	def enabled_key_count(self):
+		"""Returns the number of enabled key slots"""
+
+		if self.file == None:
+			raise "LuksFile has not been initialized"
+
+		cnt = 0
+		for i in range(0, 8):
+			if self.keys[i].active == self.LUKS_KEY_ENABLED:
+				cnt += 1
+		return cnt
+
+	def key_information(self, keyIndex):
+		"""Returns a tuple of information about the key at keyIndex (enabled, iterations, stripes)"""
+
+		if self.file == None:
+			raise "LuksFile has not been initialized"
+
+		if keyIndex < 0 or keyIndex > 7:
+			raise "keyIndex out of range"
+
+		key = self.keys[keyIndex]
+		active = (key.active == self.LUKS_KEY_ENABLED)
+		return (active, key.passwordIterations, key.stripes)
+
+	def delete_key(self, keyIndex):
+		"""Delete the key located in slot keyIndex.  WARNING! This is NOT a secure delete
+
+		Warning! If keyIndex is the last enabled key, the data will become unrecoverable
+
+		This function will not securely delete the key.  Because this class is designed
+		for reading and writing to a file, there is no guarante that writing over the data
+		inside the file will destroy it.  If you have a key leaked, you need to investigate
+		other methods of securely destroying data, including destroying the entire file system
+		and disk this file was located on, and any backups of this file that were created
+		(depending on your needs).  The good news is, because of the way LUKS encrypts the
+		master key, only one bit in the master key needs to be destoryed.  But the same bit
+		needs to be destroyed in all copies, including (possibly) the journal, bad remapped
+		blocks on the disk, etc.
+
+		If you would like to continue using this encrypted file, you need to set_key() a new
+		key, delete_key() the leaked key, and then copy the file into a new file on a
+		different disk and filesystem.  This class writes "FF" to the key location during
+		delete_key(), so during the copy the new disk will just get "FF" in the key location,
+		which will be unrecoverable on the new disk.
+		"""
+
+		if self.file == None:
+			raise "LuksFile has not been initialized"
+
+		if keyIndex < 0 or keyIndex > 7:
+			raise "keyIndex out of range"
+
+		key = self.keys[keyIndex]
+
+		# Start and end offset of the key material
+		startOffset = key.keyMaterialOffset
+		endOffset = startOffset + int(math.ceil((self.keyBytes * key.stripes) / self.SECTOR_SIZE))
+
+		# Just write "FF" into the locations
+		for i in range(startOffset, endOffset):
+			self.file.seek(int(i * self.SECTOR_SIZE))
+			self.file.write("\xFF" * int(self.SECTOR_SIZE))
+
+		try:
+			self.file.flush()
+			os.fsync(self.file.fileno())
+		except:
+			# We might get an error because self.file.fileno() does not exist on StringIO
+			pass
+		
+		key.active = self.LUKS_KEY_DISABLED
+		key.passwordIterations = 0
+		key.passwordSalt = ''
+
+		self._save_header()
+
+	def close(self):
+		"""Close the underlying file descriptor, and discard the cached master key used for decryption"""
+
+		if self.ivGen != None:
+			self.ivGen.set_key("")
+		if self.file != None:
+			self.file.close()
+
+		self.file = None
+		self.masterKey = None
+		self.ivGen = None
+
+	def data_length(self):
+		"""Returns the total data length"""
+
+		if self.file == None:
+			raise "LuksFile has not been initialized"
+
+		# Seek to the end of the file, and use tell()
+		self.file.seek(0, 2)
+		fLen = self.file.tell()
+		return fLen - int(self.payloadOffset * self.SECTOR_SIZE)
+
+	def truncate(self, length):
+		"""Truncate the file so that the data is maximum of length in size"""
+
+		if self.file == None:
+			raise "LuksFile has not been initialized"
+
+		if length % self.SECTOR_SIZE != 0:
+			raise "length must be a multiple of %s" % self.SECTOR_SIZE
+
+		if length < 0:
+			raise "length must be positive"
+
+		self.file.truncate(int(self.payloadOffset * self.SECTOR_SIZE) + length)
+
+	def encrypt_data(self, offset, data):
+		"""Encrypt data into the file.
+
+		Offset is a zero indexed location in the data to write.
+		Both offset and len(data) must be multiples of 512.
+		"""
+
+		# Check conditions
+		if self.masterKey == None:
+			raise "A key has not been unlocked.  Call open_any_key() first."
+
+		if offset % self.SECTOR_SIZE != 0:
+			raise "offset must be a multiple of %s" % self.SECTOR_SIZE
+
+		dataLen = len(data)
+		if dataLen % self.SECTOR_SIZE != 0:
+			raise "data length must be a multiple of %s" % self.SECTOR_SIZE
+
+		# Encrypt all the data
+		startSector = int(offset / self.SECTOR_SIZE)
+		endSector = int((offset + dataLen) / self.SECTOR_SIZE)
+		for sector in range(startSector, endSector):
+			dslice_sector = sector - startSector
+			dslice = data[int(dslice_sector * self.SECTOR_SIZE):int((dslice_sector+1)*self.SECTOR_SIZE)]
+			self._encrypt_sector(self.masterKey, self.payloadOffset + sector, sector, dslice)
+
+	def decrypt_data(self, offset, length):
+		"""Decrypt data from the file.
+
+		Offset is a zero indexed location into the data, and length is
+		the ammout of data to return.  offset and length can be any value,
+		but multiples of 512 are the most efficient.
+		"""
+		
+		if self.masterKey == None:
+			raise "A key has not been unlocked.  Call open_any_key() first."
+
+		frontPad = int(offset % self.SECTOR_SIZE)
+		startSector = int(math.floor(offset / self.SECTOR_SIZE))
+		endSector = int(math.ceil((offset + length) / self.SECTOR_SIZE))
+		ret = ""
+		for sector in range(startSector, endSector):
+			ret += self._decrypt_sector(self.masterKey, self.payloadOffset + sector, sector)
+
+		return ret[frontPad:frontPad+length]
+
+	##### Private functions
+
+	class _key_block:
+		"""Internal class, used to store the key information about each key."""
+
+		LUKS_KEY_FORMAT = ">II32sII"
+
+		def load_from_str(self,str):
+			"""Unpack the key information from a string"""
+			self.active, \
+			self.passwordIterations, \
+			self.passwordSalt, \
+			self.keyMaterialOffset, \
+			self.stripes =  \
+			struct.unpack(self.LUKS_KEY_FORMAT,str)
+
+		def create(self,offset, stripes, disabled):
+			"""Create a new set of key information.  Called from LuksFile.create()"""
+			self.active = disabled
+			self.passwordIterations = 0
+			self.passwordSalt = ''
+			self.keyMaterialOffset = offset
+			self.stripes = stripes
+
+		def save(self):
+			"""Pack the key information into a string"""
+			return struct.pack(self.LUKS_KEY_FORMAT, self.active, self.passwordIterations, \
+                                    self.passwordSalt, self.keyMaterialOffset, self.stripes)
+
+	def _check_hash(self, hashSpec):
+		"""Internal function to check for a valid hash specification"""
+		if hashSpec == "sha1":
+			hash = sha
+		elif hashSpec == "sha256":
+			hash = SHA256
+		elif hashSpec == "md5":
+			hash = md5
+		elif hashSpec == "ripemd160":
+			hash = RIPEMD
+		else:
+			raise "invalid hash %s" % hashSpec
+
+		return hash
+
+	class _plain_iv_gen:
+		"""Internal class to represent cbc-plain cipherMode"""
+
+		def set_key(self, key):
+			# plain IV generation does not use the key in any way
+			pass
+
+		def generate(self, sectorOffset, size):
+			istr = struct.pack("<I", sectorOffset)
+			return istr + "\x00" * (size - 4)
+
+	class _essiv_gen:
+		"""Internal class to represent cbc-essiv:<hash> cipherMode"""
+
+		# essiv mode is defined by 
+		# SALT=Hash(KEY)
+		# IV=E(SALT,sectornumber)
+		def __init__(self, str, cipher, luksParent):
+			self.hashSpec = str[1:]
+			self.hash = luksParent._check_hash(self.hashSpec)
+			self.cipher = cipher
+
+		def set_key(self, key):
+			h = self.hash.new(key)
+			self.salt = h.digest()
+			self.encr = self.cipher.new(self.salt, self.cipher.MODE_ECB)
+
+		def generate(self, sectorOffset, size):
+			istr = struct.pack("<I", sectorOffset) + "\x00" * (size - 4)
+			return self.encr.encrypt(istr)
+
+	def _check_cipher(self, cipherName, cipherMode):
+		"""Internal function to check for a valid cipherName and cipherMode"""
+		if cipherName == "aes":
+			self.cipher = AES
+		elif cipherName == "cast5":
+			self.cipher = CAST
+		elif cipherName == "blowfish":
+			self.cipher = Blowfish
+		else:
+			raise "invalid cipher %s" % cipherName
+
+		# All supported ciphers are block ciphers, so modes are the same (CBC)
+		self.mode = self.cipher.MODE_CBC
+
+		if cipherMode == "cbc-plain":
+			self.ivGen = self._plain_iv_gen()
+		elif cipherMode[:10] == "cbc-essiv:":
+			self.ivGen = self._essiv_gen(cipherMode[9:], self.cipher, self)
+		else:
+			raise "invalid cipher mode %s" % cipherName
+
+		self.cipherName = cipherName
+		self.cipherMode = cipherMode
+
+	def _uuidgen(self, rand):
+		"""Internal function to generate a UUID"""
+
+		# I copied this code (and slightly modified it) from a module written
+		# by Denys Duchier http://ofxsuite.berlios.de/uuid.py  (which is under the GPL)
+
+		buf = rand.get_bytes(16)
+		low,mid,hi_and_version,seq,node = struct.unpack(">IHHH6s",buf)
+		seq = (seq & 0x3FFF) | 0x8000
+		hi_and_version = (hi_and_version & 0x0FFF) | 0x4000
+		uuid = struct.pack(">IHHH6s",low,mid,hi_and_version,seq,node)
+		low,mid,hi,seq,b5,b4,b3,b2,b1,b0 = struct.unpack(">IHHHBBBBBB",uuid)
+		self.uuid =  "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x" % (low,mid,hi,seq>>8,seq&0xFF,b5,b4,b3,b2,b1,b0)
+
+	def _save_header(self):
+		"""Internal function to save the header info into the file"""
+
+		str=struct.pack(self.LUKS_FORMAT, self.magic, self.version, self.cipherName, self.cipherMode, self.hashSpec, \
+                         self.payloadOffset, self.keyBytes, self.mkDigest, self.mkDigestSalt, self.mkDigestIterations, self.uuid)
+		self.file.seek(0)
+		self.file.write(str)
+
+		for i in range(0, 8):
+			self.file.write(self.keys[i].save())
+
+		try:
+			self.file.flush()
+			os.fsync(self.file.fileno())
+		except:
+			# We might get an error because self.file.fileno() does not exist on StringIO
+			pass
+
+	def _encrypt_sector(self, key, sector, sectorOffset, data):
+		"""Internal function to encrypt a single sector"""
+
+		if len(data) > self.SECTOR_SIZE:
+			raise "_encrypt_page only accepts data of size <= %i" % self.SECTOR_SIZE
+
+		# Encrypt the data using the cipher, iv generation, and mode
+		IV = self.ivGen.generate(sectorOffset, self.cipher.block_size)
+		cipher = self.cipher.new(key, self.mode, IV)
+		encrData = cipher.encrypt(data)
+
+		# Write the encrypted data to disk
+		self.file.seek(int(sector * self.SECTOR_SIZE))
+		self.file.write(encrData)
+
+	def _decrypt_sector(self, key, sector, sectorOffset):
+		"""Internal function to decrypt a single sector"""
+
+		# Read the ciphertext from disk
+		self.file.seek(int(sector * self.SECTOR_SIZE))
+		encrData = self.file.read(int(self.SECTOR_SIZE))
+
+		# Decrypt the data using cipher, iv generation, and mode
+		IV = self.ivGen.generate(sectorOffset, self.cipher.block_size)
+		cipher = self.cipher.new(key, self.mode, IV)
+		return cipher.decrypt(encrData)
+
+# The following was copied from the reference implementation of LUKS in cryptsetup-luks-1.0.1 from
+# http://luks.endorphin.org/dm-crypt
+
+#define LUKS_CIPHERNAME_L 32
+#define LUKS_CIPHERMODE_L 32
+#define LUKS_HASHSPEC_L 32
+#define LUKS_DIGESTSIZE 20 // since SHA1
+#define LUKS_HMACSIZE 32
+#define LUKS_SALTSIZE 32
+#define LUKS_NUMKEYS 8
+#define LUKS_MAGIC_L 6
+
+#/* Actually we need only 37, but we don't want struct autoaligning to kick in */
+#define UUID_STRING_L 40
+
+#struct luks_phdr {
+#	char		magic[LUKS_MAGIC_L];
+#	uint16_t	version;
+#	char		cipherName[LUKS_CIPHERNAME_L];
+#	char		cipherMode[LUKS_CIPHERMODE_L];
+#	char            hashSpec[LUKS_HASHSPEC_L];
+#	uint32_t	payloadOffset;
+#	uint32_t	keyBytes;
+#	char		mkDigest[LUKS_DIGESTSIZE];
+#	char		mkDigestSalt[LUKS_SALTSIZE];
+#	uint32_t	mkDigestIterations;
+#	char            uuid[UUID_STRING_L];
+#
+#	struct {
+#		uint32_t active;
+#	
+#		/* parameters used for password processing */
+#		uint32_t passwordIterations;
+#		char     passwordSalt[LUKS_SALTSIZE];
+#		
+#		/* parameters used for AF store/load */		
+#		uint32_t keyMaterialOffset;
+#		uint32_t stripes;		
+#	} keyblock[LUKS_NUMKEYS];
+#};
+
+# size is 208 bytes + 48 * LUKS_NUMKEYS  = 592 bytes
 # $Id$
 #
 
-SUBDIRS				= bundle datahandler
+SUBDIRS				= datahandler
 
 librevelationdir		= $(pyexecdir)/revelation
 librevelation_PYTHON		= \

src/lib/bundle/AfSplitter.py

-"""Implementation of anti-forensic information splitting
-
-The AFsplitter supports secure data destruction crucial for secure on-disk key
-management. The key idea is to bloat information and therefor improving the
-chance of destroying a single bit of it. The information is bloated in such a
-way, that a single missing bit causes the original information become
-unrecoverable. The theory behind AFsplitter is presented in TKS1.
-
-The interface is simple. It consists of two functions:
-
-AFSplit(data, stripes, digestmod=sha)
-AFMerge(data, stripes, digestmod=sha)
-
-AFSplit operates on data and returns information splitted data.  AFMerge does
-just the opposite: uses the information stored in data to recover the original
-splitted data.
-
-http://clemens.endorphin.org/AFsplitter
-TKS1 paper from http://clemens.endorphin.org/publications
-
-Copyright 2006 John Lenz <lenz@cs.wisc.edu>
-
-This program is free software; you can redistribute it and/or
-modify it under the terms of the GNU General Public License
-as published by the Free Software Foundation; either version 2
-of the License, or (at your option) any later version.
- 
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-GNU General Public License for more details.
- 
-You should have received a copy of the GNU General Public License
-along with this program; if not, write to the Free Software
-Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA
-
-http://www.gnu.org/copyleft/gpl.html
-"""
-
-
-import sha, string, math, struct
-from Crypto.Util.randpool import RandomPool
-from Crypto.Cipher import XOR
-
-def _xor(a, b):
-	"""Internal function to performs XOR on two strings a and b"""
-
-	xor = XOR.new(a)
-	return xor.encrypt(b)
-
-def _diffuse(block, size, digest):
-	"""Internal function to diffuse information inside a buffer"""
-
-	# Compute the number of full blocks, and the size of the leftover block
-	full_blocks = int(math.floor(float(len(block)) / float(digest.digest_size)))
-	padding = len(block) % digest.digest_size
-
-	# hash the full blocks
-	ret = ""
-	for i in range(0, full_blocks):
-
-		hash = digest.new()
-		hash.update(struct.pack(">I", i))
-		hash.update(block[i*digest.digest_size:(i+1)*digest.digest_size])
-		ret += hash.digest()
-
-	# Hash the remaining data
-	if padding > 0:
-		hash = digest.new()
-		hash.update(struct.pack(">I", full_blocks))
-		hash.update(block[full_blocks * digest.digest_size:])
-		ret += hash.digest()[:padding]
-
-	return ret
-
-def AFSplit(data, stripes, digestmod=sha):
-	"""AF-Split data using digestmod.  Returned data size will be len(data) * stripes"""
-
-	blockSize = len(data)
-
-	rand = RandomPool()
-
-	bufblock = "\x00" * blockSize
-
-	ret = ""
-	for i in range(0, stripes-1):
-
-		# Get some random data
-		rand.randomize()
-		rand.stir()
-		r = rand.get_bytes(blockSize)
-		if rand.entropy < 0:
-			print "Warning: RandomPool entropy dropped below 0"
-
-		ret += r
-		bufblock = _xor(r, bufblock)
-		bufblock = _diffuse(bufblock, blockSize, digestmod)
-		rand.add_event(bufblock)
-
-	ret += _xor(bufblock, data)
-	return ret
-
-def AFMerge(data, stripes, digestmod=sha):
-	"""AF-Merge data using digestmod.  len(data) must be a multiple of stripes"""
-
-	if len(data) % stripes != 0:
-		raise "ERROR: data is not a multiple of strips"
-
-	blockSize = len(data) / stripes
-
-	bufblock = "\x00" * blockSize
-	for i in range(0, stripes - 1):
-		bufblock = _xor(data[i*blockSize:(i+1)*blockSize], bufblock)
-		bufblock = _diffuse(bufblock, blockSize, digestmod)
-
-	return _xor(data[(stripes-1)*blockSize:], bufblock)

src/lib/bundle/Makefile.am

-## Process this file with automake to produce Makefile.in
-#
-# src/lib/bundle/Makefile.am
-#
-# $Id$
-#
-
-bundledir	= $(pyexecdir)/revelation/bundle
-bundle_PYTHON	= \
-	__init__.py \
-	AfSplitter.py \
-	PBKDFv2.py \
-	luks.py
-

src/lib/bundle/PBKDFv2.py

-#!/usr/bin/env python
-# -*- coding: iso-8859-1 -*-
-"""
-  - Implements the PKCS#5 v2.0: Password-Based Cryptography Standard
-    from RSA Laboratories. RFC2898 http://www.rfc-editor.org/rfc/rfc2898.txt
-
-Modifications by John Lenz <lenz@cs.wisc.edu>, April 2006
-  + Fix the PBKDFv2 algorithm so it is correct
-  + Use Cipher.XOR instead of slow python xor
-  + other performance improvements
-
-Original Code written by:
-
-Copyright (C) 2004 - Lars Strand <lars strand at gnist org>
- 
-This program is free software; you can redistribute it and/or
-modify it under the terms of the GNU General Public License
-as published by the Free Software Foundation; either version 2
-of the License, or (at your option) any later version.
- 
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-GNU General Public License for more details.
- 
-You should have received a copy of the GNU General Public License
-along with this program; if not, write to the Free Software
-Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA
-
-http://www.gnu.org/copyleft/gpl.html
-
-"""
-
-import struct, string, math, sha, hmac # RFC2104
-from Crypto.Cipher import XOR
-
-################ PBKDFv2
-class PBKDFv2:
-    """Implements the PKCS#5 v2.0: Password-Based Cryptography Standard
-    from RSA Laboratories. RFC2898
-
-    http://www.rfc-editor.org/rfc/rfc2898.txt
-    """
-
-    ################ init
-    def __init__(self):
-
-        # length of pseudorandom function: 20 for SHA-1, 16 for MD5
-        self.hLen = 20
-        
-    ################ makeKey
-    def makeKey(self, P, S, c, dkLen, digestmod=sha):
-        """
-           Input:   P         password, an octet string
-                    S         salt, an octet string
-                    c         iteration count, a positive integer (>1000)
-                    dkLen     intended length on octets of the derived key, a positive integer,
-                              at most (2^32 - 1) * hLen
-                    digestmod Digest used, passed to hmac module
-
-           Output   DK    derived key, a dkLen-octet string
-           """
-
-        # do some sanity checks
-        try:
-            str(P); str(S); int(c); float(dkLen); int(c)
-        except:
-            print "P = %s, S = %s, c = %s, dkLen = %s:" % (P, S, c, dkLen)
-            raise "ERROR! Input is not correct!"
-
-        # Step 1: if dkLen is larger than maximum possible key - exit
-        if dkLen > ((2^32 - 1) * self.hLen):
-            maxlength = (2^32 - 1) * self.hLen
-            raise "ERROR! Key is to large! Maxlength is", str(maxlength)
-
-        # Step 2:
-        # Let l be the number of hLen-octet blocks in the derived key, rounding up
-        # and let r be the number of octets in the last block
-        l = math.ceil(dkLen / float(self.hLen))
-        #if (dkLen % float(self.hLen)): l = int(l) + 1 # round up if necessary
-        r = dkLen - (l - 1) * self.hLen
-
-        # Step 3:
-        # For each block of the derived key, apply the function F to the
-        # password P, the salt S, the iteration count c, and the block index
-        # to compute the block
-        T = ""
-        for blockindex in range(int(l)):
-            T += self.F(P, S, c, blockindex, digestmod)
-        # Step 4 - extract the first dkLen octet to produce a derived key DK
-        DK = T[:dkLen]
-
-        # Step 5 - return the derived key DK
-        return DK
-            
-    ################ F
-    def F(self, P, S, c, i, digest):
-        """For each block of the derived key, apply this function.
-
-        Notation:
-        ||   = concatenation operator
-        PRF  = Underlying pseudorandom function
-        
-        The function F is defined as the exclusive-or sum of the first c
-        iterates if the underlying pseudorandom function PRF applied to
-        the password P and the concatenation of the salt S and the block
-        index i:
-
-        F(P, S, c, i) = U1 XOR U2 XOR ... XOR Uc
-
-        where
-
-        U1 = PRF(P, S || INT(i)),
-        U2 = PRF(P, U1)
-        ...
-        Uc = PRF(P, Uc-1)
-        """
-
-        # The pseudorandom function, PRF, used is HMAC-SHA1 (rfc2104)
-        iteration = 1
-
-        # the first iteration; P is the key, and a concatination of
-        # S and blocknumber is the message
-	istr = struct.pack(">I", i+1)
-	PRFMaster = hmac.new(P,digestmod=digest)
-	PRF = PRFMaster.copy()
-	PRF.update(S)
-	PRF.update(istr)
-        U = PRF.digest() # the first iteration
-
-	Fbuf = U
-
-        while iteration < c:                  # loop through all iterations
-	    PRF = PRFMaster.copy()
-	    PRF.update(U)
-            U = PRF.digest()
-            Fbuf = self._xor(U, Fbuf)    # XOR this new iteration with the old one
-            iteration += 1
-        return Fbuf
-
-    ################ xor
-    def _xor(self, a, b):
-        """Performs XOR on two strings a and b"""
-
-        if len(a) != len(b):
-            raise "ERROR: Strings are of different size! %s %s" % (len(a), len(b))
-
-	xor = XOR.new(a)
-	return xor.encrypt(b)

src/lib/bundle/__init__.py

-#
-# Revelation 0.4.7 - a password manager for GNOME 2
-# http://oss.codepoet.no/revelation/
-# $Id$
-#
-# Bundled third-party modules
-#
-#
-# Copyright (c) 2003-2006 Erik Grinaker
-#
-# This program is free software; you can redistribute it and/or
-# modify it under the terms of the GNU General Public License
-# as published by the Free Software Foundation; either version 2
-# of the License, or (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
-#
-
-import AfSplitter, PBKDFv2, luks
-

src/lib/bundle/luks.py

-"""
-Implements the LUKS (Linux Unified Key Setup) Version 1.0
-on disk specification inside a file.
-
-http://luks.endorphin.org/
-
-LUKS offers:
-    * compatiblity via standardization,
-    * secure against low entropy attacks,
-    * support for multiple keys,
-    * effective passphrase revocation,
-
-This module is compatible with dm-crypt and cryptsetup tools for the Linux
-kernel, as long as hashSpec="sha1" is used. Loopback files or partitions created
-with the linux kernel can be decrypted using this module.  FreeOTFE
-(http://www.freeotfe.org/) should provide support for reading and writing on
-Windows.
-
-This module has one class LuksFile.
-
-Loading a LUKS disk image (use both, one after another):
-- load_from_file(file)
-- open_any_key(password)
-
-Creating a new LUKS disk image (use both):
-- create(file, cipherName, cipherMode, hashSpec, masterSize, stripes)
-- set_key(0, password, iterations)
-
-Once a file is unlocked (either because it was just created or
-open_any_key() returned True), you can perform the key operations:
-- enabled_key_count()
-- key_information(keyIndex)
-- set_key(keyIndex, password, iterations)
-- delete_key(keyIndex)
-
-Once a file is unlocked, you can perform data encryption/decryption with
-- data_length()
-- encrypt_data(offset, data)
-- decrypt_data(offset, length)
-- truncate(length)
-
-Finally, to close the file:
-- close()
-
-Copyright 2006 John Lenz <lenz@cs.wisc.edu>
-
-This program is free software; you can redistribute it and/or
-modify it under the terms of the GNU General Public License
-as published by the Free Software Foundation; either version 2
-of the License, or (at your option) any later version.
- 
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-GNU General Public License for more details.
- 
-You should have received a copy of the GNU General Public License
-along with this program; if not, write to the Free Software
-Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA
-
-http://www.gnu.org/copyleft/gpl.html
-"""
-
-import os, math, struct, stat, sha, md5
-
-from Crypto.Util.randpool import RandomPool
-from Crypto.Cipher import *
-from Crypto.Hash import *
-import PBKDFv2, AfSplitter
-
-class LuksFile:
-	"""Implements the LUKS (Linux Unified Key Setup) Version 1.0 http://luks.endorphin.org/"""
-
-	LUKS_FORMAT = ">6sH32s32s32sII20s32sI40s"
-	LUKS_MAGIC = "LUKS\xba\xbe"
-
-	LUKS_KEY_DISABLED = 0x0000DEAD
-	LUKS_KEY_ENABLED = 0x00AC71F3
-
-	SECTOR_SIZE = 512.0
-
-	KEY_STRIPES = 4000
-	SALT_SIZE = 32
-	DIGEST_SIZE = 20
-
-	def __init__(self):
-		self.file = None
-		self.masterKey = None
-		self.ivGen = None
-
-	# Read the header from the file descriptor
-	def load_from_file(self, file):
-		"""Initialize this LuksFile class from the file.
-
-		The file parameter should be an object implementing the File Object API
-        	This function will error if the file is not a LUKS partition (the LUKS_MAGIC does not match)
-		"""
-
-		if self.file != None:
-			raise "This LuksFile has already been initialized"
-
-		# Read the main parameters
-		self.file = file
-		self.file.seek(0)
-
-		self.magic, \
-		self.version, \
-		cipherName, \
-		cipherMode, \
-		hashSpec, \
-		self.payloadOffset, \
-		self.keyBytes, \
-		self.mkDigest, \
-		self.mkDigestSalt, \
-		self.mkDigestIterations, \
-		self.uuid = \
-		struct.unpack(self.LUKS_FORMAT, self.file.read(208))
-
-		# check magic
-		if self.magic != self.LUKS_MAGIC:
-			self.file = None
-			raise "%s is not a LUKS data file" % filename
-
-		# Check the hash and cipher
-		self.hashSpec = hashSpec.strip(" \x00")
-		self.hash = self._check_hash(self.hashSpec)
-		self._check_cipher(cipherName.strip(" \x00"), cipherMode.strip(" \x00"))
-		
-		# Load the key information
-		self.keys = [None] * 8
-		for i in range(0, 8):
-			self.keys[i] = self._key_block()
-			self.keys[i].load_from_str(self.file.read(48))
-
-		# set the digest to be the correct size
-		self.mkDigest = self.mkDigest[:self.hash.digest_size]
-
-		self.masterKey = None
-
-	# Generate a new header
-	def create(self, file, cipherName, cipherMode, hashSpec, masterSize, stripes):
-		"""Initializes the file class passed in with the LUKS header
-
-        	Parameters
-		   cipherName: aes, cast5, blowfish
-		   cipherMode: cbc-plain, cbc-essiv:<hash>
-		   hashSpec: sha1, sha256, md5, ripemd160
-		   masterSize: length of the master key in bytes (must match cipher)
-		   stripes: number of stripes when Af Splitting keys
-
-		For compatibility with the Linux kernel dm-crypt, hashSpec must equal "sha1"
-
-		cbc-plain uses the sector number as the IV.  This has a weakness: an attacker
-		may be able to detect the existance of watermarked files.
-		cbc-essiv:<hash> protects against the weakness in cbc-plain, but is 
-		slightly slower. The digest size of the hash function passed to cbc-essiv
-		must match the key size of the cipher.  
-		aes-cbc-essiv:sha256 works, while aes-cbc-essiv:sha1 does not
-
-		For more information about the details of the attacks and risk assesment, see
-		http://clemens.endorphin.org/LinuxHDEncSettings
-		"""
-
-		if self.file != None:
-			raise "This LuksFile has already been initialized"
-
-		self._check_cipher(cipherName, cipherMode)
-
-		self.magic = self.LUKS_MAGIC
-		self.version = 1
-		self.mkDigestIterations = 10
-		self.keyBytes = masterSize
-		self.hashSpec = hashSpec
-		self.hash = self._check_hash(hashSpec)
-
-		rand = RandomPool(self.SALT_SIZE + 16 + masterSize)
-
-		# Generate the salt
-		self.mkDigestSalt = rand.get_bytes(self.SALT_SIZE)
-
-		# Generate a random master key
-		self.masterKey = rand.get_bytes(self.keyBytes)
-		self.ivGen.set_key(self.masterKey)
-
-		# generate the master key digest
-		pbkdf = PBKDFv2.PBKDFv2()
-		self.mkDigest = pbkdf.makeKey(self.masterKey, self.mkDigestSalt, self.mkDigestIterations, self.hash.digest_size, self.hash)
-
-		# init the key information
-		currentSector = math.ceil(592.0 / self.SECTOR_SIZE)
-		alignSectors = 4096 / self.SECTOR_SIZE
-		blocksPerStripe = math.ceil(float(self.keyBytes * stripes) / self.SECTOR_SIZE)
-
-		self.keys = [None] * 8
-		for i in range(0, 8):
-			if currentSector % alignSectors > 0:
-				currentSector += alignSectors - currentSector % alignSectors
-			self.keys[i] = self._key_block()
-			self.keys[i].create(currentSector, stripes, self.LUKS_KEY_DISABLED)
-			currentSector += blocksPerStripe
-
-		# Set the data offset
-		if currentSector % alignSectors > 0:
-			currentSector += alignSectors - currentSector % alignSectors
-		self.payloadOffset = currentSector
-
-		# Generate a UUID for this file
-		self._uuidgen(rand)
-
-		# Create a new file, and save the header into it
-		self.file = file
-		self._save_header()
-
-		# Write FF into all the key slots
-		FFData = "\xFF" * int(self.SECTOR_SIZE)
-		for i in range(0, 8):
-			self.file.seek(int(self.keys[i].keyMaterialOffset))
-			for sector in range(0, int(blocksPerStripe)):
-				self.file.write(FFData)
-
-		# Flush the file to disk
-		try:
-			self.file.flush()
-			os.fsync(self.file.fileno())
-		except:
-			# We might get an error because self.file.fileno() does not exist on StringIO
-			pass
-
-	def set_key(self, keyIndex, password, iterations, checkMinStripes=0):
-		"""Sets the key block at keyIndex using password
-
-		Sets the key block at keyIndex using password, hashed iterations
-		times using PBKDFv2 (RFC2104).  This LuksFile must be unlocked by
-		calling open_any_key() before calling this function.
-		checkMinStripes is used to detect basic header manipulation, since
-		the number of stripes for the key is set by create(), before
-		we write a password to disk we make sure the key is not weak because
-		of a small number of stripes.
-
-		This function will raise an error if the key is already enabled.
-		"""
-
-		# Some checks
-		if self.masterKey == None:
-			raise "A key has not been unlocked.  Call open_any_key() first."
-
-		if keyIndex < 0 or keyIndex > 7:
-			raise "keyIndex out of range"
-
-		key = self.keys[keyIndex]
-		if key.active != self.LUKS_KEY_DISABLED:
-			raise "Key is active.  Delete the key and try again"
-
-		if checkMinStripes == 0: checkMinStripes = self.KEY_STRIPES
-		if key.stripes < checkMinStripes:
-			raise "Key section %i contains too few stripes.  Header manipulation?" % keyIndex
-
-		key.passwordIterations = iterations
-
-		# Generate a random salt for this key
-		rand = RandomPool(self.SALT_SIZE)
-		key.passwordSalt = rand.get_bytes(self.SALT_SIZE)
-
-		# Hash the key using PBKDFv2
-		pbkdf = PBKDFv2.PBKDFv2()
-		derived_key = pbkdf.makeKey(password, key.passwordSalt, key.passwordIterations, self.keyBytes, self.hash)
-
-		# Split the key into key.stripes
-		AfKey = AfSplitter.AFSplit(self.masterKey, key.stripes, self.hash)
-
-		AfKeySize = len(AfKey)
-		if AfKeySize != key.stripes * self.keyBytes:
-			raise "ERROR: AFSplit did not return the correct length of key"
-
-		# Set the key for IV generation
-		self.ivGen.set_key(derived_key)
-
-		# Encrypt the splitted key using the hashed password
-		AfSectors = int(math.ceil(float(AfKeySize) / self.SECTOR_SIZE))
-		for sector in range(0, AfSectors):
-			self._encrypt_sector(derived_key, key.keyMaterialOffset + sector, sector, \
-                               AfKey[int(sector*self.SECTOR_SIZE):int((sector+1)*self.SECTOR_SIZE)])
-
-		key.active = self.LUKS_KEY_ENABLED
-
-		# Reset the key used for to IV generation in data mode
-		self.ivGen.set_key(self.masterKey)
-
-		self._save_header()
-
-	def open_key(self, keyIndex, password):
-		"""Open a specific keyIndex using password.  Returns True on success"""
-
-		if self.file == None:
-			raise "LuksFile has not been initialized"
-
-		if keyIndex < 0 or keyIndex > 7:
-			raise "keyIndex is out of range"
-
-		key = self.keys[keyIndex]
-
-		if key.active != self.LUKS_KEY_ENABLED:
-			return False
-
-		# Hash the password using PBKDFv2
-		pbkdf = PBKDFv2.PBKDFv2()
-		derived_key = pbkdf.makeKey(password, key.passwordSalt, key.passwordIterations, self.keyBytes, self.hash)
-
-		# Setup the IV generation to use this key
-		self.ivGen.set_key(derived_key)
-
-		# Decrypt the master key data using the hashed password
-		AfKeySize = key.stripes * self.keyBytes
-		AfSectors = int(math.ceil(float(AfKeySize) / self.SECTOR_SIZE))
-		AfKey = ""
-		for sector in range(0, AfSectors):
-			AfKey += self._decrypt_sector(derived_key, key.keyMaterialOffset + sector, sector)
-		AfKey = AfKey[0:AfKeySize]
-
-		# Merge the decrypted master key
-		masterKey = AfSplitter.AFMerge(AfKey, key.stripes, self.hash)
-
-		# Check if the password was the correct one, by checking the master key digest
-		checkDigest = pbkdf.makeKey(masterKey, self.mkDigestSalt, self.mkDigestIterations, self.hash.digest_size, self.hash)
-		
-		# Since the header only stores DIGEST_SIZE (which is smaller than sha256 digest size)
-		#   trim the digest to DIGEST_SIZE
-		checkDigest = checkDigest[:self.DIGEST_SIZE]
-
-		if checkDigest != self.mkDigest:
-			return False
-
-		self.masterKey = masterKey
-		self.ivGen.set_key(self.masterKey)
-		return True
-		
-
-	def open_any_key(self, password):
-		"""Try to open any enabled key using the provided password.  Returns index number on success, or None"""
-
-		if self.file == None:
-			raise "LuksFile has not been initialized"
-
-		for i in range(0, 8):
-			if self.open_key(i, password):
-				return i
-		return None
-
-	def enabled_key_count(self):
-		"""Returns the number of enabled key slots"""
-
-		if self.file == None:
-			raise "LuksFile has not been initialized"
-
-		cnt = 0
-		for i in range(0, 8):
-			if self.keys[i].active == self.LUKS_KEY_ENABLED:
-				cnt += 1
-		return cnt
-
-	def key_information(self, keyIndex):
-		"""Returns a tuple of information about the key at keyIndex (enabled, iterations, stripes)"""
-
-		if self.file == None:
-			raise "LuksFile has not been initialized"
-
-		if keyIndex < 0 or keyIndex > 7:
-			raise "keyIndex out of range"
-
-		key = self.keys[keyIndex]
-		active = (key.active == self.LUKS_KEY_ENABLED)
-		return (active, key.passwordIterations, key.stripes)
-
-	def delete_key(self, keyIndex):
-		"""Delete the key located in slot keyIndex.  WARNING! This is NOT a secure delete
-
-		Warning! If keyIndex is the last enabled key, the data will become unrecoverable
-
-		This function will not securely delete the key.  Because this class is designed
-		for reading and writing to a file, there is no guarante that writing over the data
-		inside the file will destroy it.  If you have a key leaked, you need to investigate
-		other methods of securely destroying data, including destroying the entire file system
-		and disk this file was located on, and any backups of this file that were created
-		(depending on your needs).  The good news is, because of the way LUKS encrypts the
-		master key, only one bit in the master key needs to be destoryed.  But the same bit
-		needs to be destroyed in all copies, including (possibly) the journal, bad remapped
-		blocks on the disk, etc.
-
-		If you would like to continue using this encrypted file, you need to set_key() a new
-		key, delete_key() the leaked key, and then copy the file into a new file on a
-		different disk and filesystem.  This class writes "FF" to the key location during
-		delete_key(), so during the copy the new disk will just get "FF" in the key location,
-		which will be unrecoverable on the new disk.
-		"""
-
-		if self.file == None:
-			raise "LuksFile has not been initialized"
-
-		if keyIndex < 0 or keyIndex > 7:
-			raise "keyIndex out of range"
-
-		key = self.keys[keyIndex]
-
-		# Start and end offset of the key material
-		startOffset = key.keyMaterialOffset
-		endOffset = startOffset + int(math.ceil((self.keyBytes * key.stripes) / self.SECTOR_SIZE))
-
-		# Just write "FF" into the locations
-		for i in range(startOffset, endOffset):
-			self.file.seek(int(i * self.SECTOR_SIZE))
-			self.file.write("\xFF" * int(self.SECTOR_SIZE))
-
-		try:
-			self.file.flush()
-			os.fsync(self.file.fileno())
-		except:
-			# We might get an error because self.file.fileno() does not exist on StringIO
-			pass
-		
-		key.active = self.LUKS_KEY_DISABLED
-		key.passwordIterations = 0
-		key.passwordSalt = ''
-
-		self._save_header()
-
-	def close(self):
-		"""Close the underlying file descriptor, and discard the cached master key used for decryption"""
-
-		if self.ivGen != None:
-			self.ivGen.set_key("")
-		if self.file != None:
-			self.file.close()
-
-		self.file = None
-		self.masterKey = None
-		self.ivGen = None
-
-	def data_length(self):
-		"""Returns the total data length"""
-
-		if self.file == None:
-			raise "LuksFile has not been initialized"
-
-		# Seek to the end of the file, and use tell()
-		self.file.seek(0, 2)
-		fLen = self.file.tell()
-		return fLen - int(self.payloadOffset * self.SECTOR_SIZE)
-
-	def truncate(self, length):
-		"""Truncate the file so that the data is maximum of length in size"""
-
-		if self.file == None:
-			raise "LuksFile has not been initialized"
-
-		if length % self.SECTOR_SIZE != 0:
-			raise "length must be a multiple of %s" % self.SECTOR_SIZE
-
-		if length < 0:
-			raise "length must be positive"
-
-		self.file.truncate(int(self.payloadOffset * self.SECTOR_SIZE) + length)
-
-	def encrypt_data(self, offset, data):
-		"""Encrypt data into the file.
-
-		Offset is a zero indexed location in the data to write.
-		Both offset and len(data) must be multiples of 512.
-		"""
-
-		# Check conditions
-		if self.masterKey == None:
-			raise "A key has not been unlocked.  Call open_any_key() first."
-
-		if offset % self.SECTOR_SIZE != 0:
-			raise "offset must be a multiple of %s" % self.SECTOR_SIZE
-
-		dataLen = len(data)
-		if dataLen % self.SECTOR_SIZE != 0:
-			raise "data length must be a multiple of %s" % self.SECTOR_SIZE
-
-		# Encrypt all the data
-		startSector = int(offset / self.SECTOR_SIZE)
-		endSector = int((offset + dataLen) / self.SECTOR_SIZE)
-		for sector in range(startSector, endSector):
-			dslice_sector = sector - startSector
-			dslice = data[int(dslice_sector * self.SECTOR_SIZE):int((dslice_sector+1)*self.SECTOR_SIZE)]
-			self._encrypt_sector(self.masterKey, self.payloadOffset + sector, sector, dslice)
-
-	def decrypt_data(self, offset, length):
-		"""Decrypt data from the file.
-
-		Offset is a zero indexed location into the data, and length is
-		the ammout of data to return.  offset and length can be any value,
-		but multiples of 512 are the most efficient.
-		"""
-		
-		if self.masterKey == None:
-			raise "A key has not been unlocked.  Call open_any_key() first."
-
-		frontPad = int(offset % self.SECTOR_SIZE)
-		startSector = int(math.floor(offset / self.SECTOR_SIZE))
-		endSector = int(math.ceil((offset + length) / self.SECTOR_SIZE))
-		ret = ""
-		for sector in range(startSector, endSector):
-			ret += self._decrypt_sector(self.masterKey, self.payloadOffset + sector, sector)
-
-		return ret[frontPad:frontPad+length]
-
-	##### Private functions
-
-	class _key_block:
-		"""Internal class, used to store the key information about each key."""
-
-		LUKS_KEY_FORMAT = ">II32sII"
-
-		def load_from_str(self,str):
-			"""Unpack the key information from a string"""
-			self.active, \
-			self.passwordIterations, \
-			self.passwordSalt, \
-			self.keyMaterialOffset, \
-			self.stripes =  \
-			struct.unpack(self.LUKS_KEY_FORMAT,str)
-
-		def create(self,offset, stripes, disabled):
-			"""Create a new set of key information.  Called from LuksFile.create()"""
-			self.active = disabled
-			self.passwordIterations = 0
-			self.passwordSalt = ''
-			self.keyMaterialOffset = offset
-			self.stripes = stripes
-
-		def save(self):
-			"""Pack the key information into a string"""
-			return struct.pack(self.LUKS_KEY_FORMAT, self.active, self.passwordIterations, \
-                                    self.passwordSalt, self.keyMaterialOffset, self.stripes)
-
-	def _check_hash(self, hashSpec):
-		"""Internal function to check for a valid hash specification"""
-		if hashSpec == "sha1":
-			hash = sha
-		elif hashSpec == "sha256":
-			hash = SHA256
-		elif hashSpec == "md5":
-			hash = md5
-		elif hashSpec == "ripemd160":
-			hash = RIPEMD
-		else:
-			raise "invalid hash %s" % hashSpec
-
-		return hash
-
-	class _plain_iv_gen:
-		"""Internal class to represent cbc-plain cipherMode"""
-
-		def set_key(self, key):
-			# plain IV generation does not use the key in any way
-			pass
-
-		def generate(self, sectorOffset, size):
-			istr = struct.pack("<I", sectorOffset)
-			return istr + "\x00" * (size - 4)
-
-	class _essiv_gen:
-		"""Internal class to represent cbc-essiv:<hash> cipherMode"""
-
-		# essiv mode is defined by 
-		# SALT=Hash(KEY)
-		# IV=E(SALT,sectornumber)
-		def __init__(self, str, cipher, luksParent):
-			self.hashSpec = str[1:]
-			self.hash = luksParent._check_hash(self.hashSpec)
-			self.cipher = cipher
-
-		def set_key(self, key):
-			h = self.hash.new(key)
-			self.salt = h.digest()
-			self.encr = self.cipher.new(self.salt, self.cipher.MODE_ECB)
-
-		def generate(self, sectorOffset, size):
-			istr = struct.pack("<I", sectorOffset) + "\x00" * (size - 4)
-			return self.encr.encrypt(istr)
-
-	def _check_cipher(self, cipherName, cipherMode):
-		"""Internal function to check for a valid cipherName and cipherMode"""
-		if cipherName == "aes":
-			self.cipher = AES
-		elif cipherName == "cast5":
-			self.cipher = CAST
-		elif cipherName == "blowfish":
-			self.cipher = Blowfish
-		else:
-			raise "invalid cipher %s" % cipherName
-
-		# All supported ciphers are block ciphers, so modes are the same (CBC)
-		self.mode = self.cipher.MODE_CBC
-
-		if cipherMode == "cbc-plain":
-			self.ivGen = self._plain_iv_gen()
-		elif cipherMode[:10] == "cbc-essiv:":
-			self.ivGen = self._essiv_gen(cipherMode[9:], self.cipher, self)
-		else:
-			raise "invalid cipher mode %s" % cipherName
-
-		self.cipherName = cipherName
-		self.cipherMode = cipherMode
-
-	def _uuidgen(self, rand):
-		"""Internal function to generate a UUID"""
-
-		# I copied this code (and slightly modified it) from a module written
-		# by Denys Duchier http://ofxsuite.berlios.de/uuid.py  (which is under the GPL)
-
-		buf = rand.get_bytes(16)
-		low,mid,hi_and_version,seq,node = struct.unpack(">IHHH6s",buf)
-		seq = (seq & 0x3FFF) | 0x8000
-		hi_and_version = (hi_and_version & 0x0FFF) | 0x4000
-		uuid = struct.pack(">IHHH6s",low,mid,hi_and_version,seq,node)
-		low,mid,hi,seq,b5,b4,b3,b2,b1,b0 = struct.unpack(">IHHHBBBBBB",uuid)
-		self.uuid =  "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x" % (low,mid,hi,seq>>8,seq&0xFF,b5,b4,b3,b2,b1,b0)
-
-	def _save_header(self):
-		"""Internal function to save the header info into the file"""
-
-		str=struct.pack(self.LUKS_FORMAT, self.magic, self.version, self.cipherName, self.cipherMode, self.hashSpec, \
-                         self.payloadOffset, self.keyBytes, self.mkDigest, self.mkDigestSalt, self.mkDigestIterations, self.uuid)
-		self.file.seek(0)
-		self.file.write(str)
-
-		for i in range(0, 8):
-			self.file.write(self.keys[i].save())
-
-		try:
-			self.file.flush()
-			os.fsync(self.file.fileno())
-		except:
-			# We might get an error because self.file.fileno() does not exist on StringIO
-			pass
-
-	def _encrypt_sector(self, key, sector, sectorOffset, data):
-		"""Internal function to encrypt a single sector"""
-
-		if len(data) > self.SECTOR_SIZE:
-			raise "_encrypt_page only accepts data of size <= %i" % self.SECTOR_SIZE
-
-		# Encrypt the data using the cipher, iv generation, and mode
-		IV = self.ivGen.generate(sectorOffset, self.cipher.block_size)
-		cipher = self.cipher.new(key, self.mode, IV)
-		encrData = cipher.encrypt(data)
-
-		# Write the encrypted data to disk
-		self.file.seek(int(sector * self.SECTOR_SIZE))
-		self.file.write(encrData)
-
-	def _decrypt_sector(self, key, sector, sectorOffset):
-		"""Internal function to decrypt a single sector"""
-
-		# Read the ciphertext from disk
-		self.file.seek(int(sector * self.SECTOR_SIZE))
-		encrData = self.file.read(int(self.SECTOR_SIZE))
-
-		# Decrypt the data using cipher, iv generation, and mode
-		IV = self.ivGen.generate(sectorOffset, self.cipher.block_size)
-		cipher = self.cipher.new(key, self.mode, IV)
-		return cipher.decrypt(encrData)
-
-# The following was copied from the reference implementation of LUKS in cryptsetup-luks-1.0.1 from
-# http://luks.endorphin.org/dm-crypt
-
-#define LUKS_CIPHERNAME_L 32
-#define LUKS_CIPHERMODE_L 32
-#define LUKS_HASHSPEC_L 32
-#define LUKS_DIGESTSIZE 20 // since SHA1
-#define LUKS_HMACSIZE 32
-#define LUKS_SALTSIZE 32
-#define LUKS_NUMKEYS 8
-#define LUKS_MAGIC_L 6
-
-#/* Actually we need only 37, but we don't want struct autoaligning to kick in */
-#define UUID_STRING_L 40
-
-#struct luks_phdr {
-#	char		magic[LUKS_MAGIC_L];
-#	uint16_t	version;
-#	char		cipherName[LUKS_CIPHERNAME_L];
-#	char		cipherMode[LUKS_CIPHERMODE_L];
-#	char            hashSpec[LUKS_HASHSPEC_L];
-#	uint32_t	payloadOffset;
-#	uint32_t	keyBytes;
-#	char		mkDigest[LUKS_DIGESTSIZE];
-#	char		mkDigestSalt[LUKS_SALTSIZE];
-#	uint32_t	mkDigestIterations;
-#	char            uuid[UUID_STRING_L];
-#
-#	struct {
-#		uint32_t active;
-#	
-#		/* parameters used for password processing */
-#		uint32_t passwordIterations;
-#		char     passwordSalt[LUKS_SALTSIZE];
-#		
-#		/* parameters used for AF store/load */		
-#		uint32_t keyMaterialOffset;
-#		uint32_t stripes;		
-#	} keyblock[LUKS_NUMKEYS];
-#};
-
-# size is 208 bytes + 48 * LUKS_NUMKEYS  = 592 bytes