"Fossies" - the Fresh Open Source Software Archive

Member "revelation-0.5.4/src/bundle/luks.py" (4 Oct 2020, 25891 Bytes) of package /linux/privat/revelation-0.5.4.tar.xz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) Python source code syntax highlighting (style: standard) with prefixed line numbers. Alternatively you can here view or download the uninterpreted source code file. For more information about "luks.py" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 0.5.3_vs_0.5.4.

    1 """
    2 Implements the LUKS (Linux Unified Key Setup) Version 1.0
    3 on disk specification inside a file.
    4 
    5 http://luks.endorphin.org/
    6 
    7 LUKS offers:
    8     * compatiblity via standardization,
    9     * secure against low entropy attacks,
   10     * support for multiple keys,
   11     * effective passphrase revocation,
   12 
   13 This module is compatible with dm-crypt and cryptsetup tools for the Linux
   14 kernel, as long as hashSpec="sha1" is used. Loopback files or partitions created
   15 with the linux kernel can be decrypted using this module.  FreeOTFE
   16 (http://www.freeotfe.org/) should provide support for reading and writing on
   17 Windows.
   18 
   19 This module has one class LuksFile.
   20 
   21 Loading a LUKS disk image (use both, one after another):
   22 - load_from_file(file)
   23 - open_any_key(password)
   24 
   25 Creating a new LUKS disk image (use both):
   26 - create(file, cipherName, cipherMode, hashSpec, masterSize, stripes)
   27 - set_key(0, password, iterations)
   28 
   29 Once a file is unlocked (either because it was just created or
   30 open_any_key() returned True), you can perform the key operations:
   31 - enabled_key_count()
   32 - key_information(keyIndex)
   33 - set_key(keyIndex, password, iterations)
   34 - delete_key(keyIndex)
   35 
   36 Once a file is unlocked, you can perform data encryption/decryption with
   37 - data_length()
   38 - encrypt_data(offset, data)
   39 - decrypt_data(offset, length)
   40 - truncate(length)
   41 
   42 Finally, to close the file:
   43 - close()
   44 
   45 Copyright 2006 John Lenz <lenz@cs.wisc.edu>
   46 
   47 This program is free software; you can redistribute it and/or
   48 modify it under the terms of the GNU General Public License
   49 as published by the Free Software Foundation; either version 2
   50 of the License, or (at your option) any later version.
   51 
   52 This program is distributed in the hope that it will be useful,
   53 but WITHOUT ANY WARRANTY; without even the implied warranty of
   54 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   55 GNU General Public License for more details.
   56 
   57 You should have received a copy of the GNU General Public License
   58 along with this program; if not, write to the Free Software
   59 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
   60 
   61 http://www.gnu.org/copyleft/gpl.html
   62 """
   63 
   64 import os, math, struct, stat, hashlib
   65 
   66 import Cryptodome.Random as Random
   67 from Cryptodome.Cipher import AES
   68 from . import PBKDFv2, AfSplitter
   69 
   70 class LuksError(Exception):
   71     def __init__(self, value):
   72         self.value = value
   73     def __str__(self):
   74         return repr(self.value)
   75 
   76 class LuksFile:
   77     """Implements the LUKS (Linux Unified Key Setup) Version 1.0 http://luks.endorphin.org/"""
   78 
   79     LUKS_FORMAT = ">6sH32s32s32sII20s32sI40s"
   80     LUKS_MAGIC = b"LUKS\xba\xbe"
   81 
   82     LUKS_KEY_DISABLED = 0x0000DEAD
   83     LUKS_KEY_ENABLED = 0x00AC71F3
   84 
   85     SECTOR_SIZE = 512.0
   86 
   87     KEY_STRIPES = 4000
   88     SALT_SIZE = 32
   89     DIGEST_SIZE = 20
   90 
   91     def __init__(self):
   92         self.file = None
   93         self.masterKey = None
   94         self.ivGen = None
   95 
   96     # Read the header from the file descriptor
   97     def load_from_file(self, file):
   98         """Initialize this LuksFile class from the file.
   99 
  100         The file parameter should be an object implementing the File Object API
  101         This function will error if the file is not a LUKS partition (the LUKS_MAGIC does not match)
  102         """
  103 
  104         if self.file != None:
  105             raise LuksError("This LuksFile has already been initialized")
  106 
  107         # Read the main parameters
  108         self.file = file
  109         self.file.seek(0)
  110 
  111         self.magic, \
  112         self.version, \
  113         cipherName, \
  114         cipherMode, \
  115         hashSpec, \
  116         self.payloadOffset, \
  117         self.keyBytes, \
  118         self.mkDigest, \
  119         self.mkDigestSalt, \
  120         self.mkDigestIterations, \
  121         self.uuid = \
  122         struct.unpack(self.LUKS_FORMAT, self.file.read(208))
  123 
  124         cipherName = cipherName.decode()
  125         cipherMode = cipherMode.decode()
  126         hashSpec = hashSpec.decode()
  127 
  128         # check magic
  129         if self.magic != self.LUKS_MAGIC:
  130             self.file = None
  131             raise LuksError("%s is not a LUKS data file" % filename)
  132 
  133         # Check the hash and cipher
  134         self.hashSpec = hashSpec.strip(" \x00")
  135         self._check_cipher(cipherName.strip(" \x00"), cipherMode.strip(" \x00"))
  136 
  137         # Load the key information
  138         self.keys = [None] * 8
  139         for i in range(0, 8):
  140             self.keys[i] = self._key_block()
  141             self.keys[i].load_from_str(self.file.read(48))
  142 
  143         # set the digest to be the correct size
  144         self.mkDigest = self.mkDigest[:hashlib.new(self.hashSpec).digest_size]
  145 
  146         self.masterKey = None
  147 
  148     # Generate a new header
  149     def create(self, file, cipherName, cipherMode, hashSpec, masterSize, stripes):
  150         """Initializes the file class passed in with the LUKS header
  151 
  152         Parameters
  153            cipherName: aes, cast5, blowfish
  154            cipherMode: cbc-plain, cbc-essiv:<hash>
  155            hashSpec: sha1, sha256, md5, ripemd160
  156            masterSize: length of the master key in bytes (must match cipher)
  157            stripes: number of stripes when Af Splitting keys
  158 
  159         For compatibility with the Linux kernel dm-crypt, hashSpec must equal "sha1"
  160 
  161         cbc-plain uses the sector number as the IV.  This has a weakness: an attacker
  162         may be able to detect the existance of watermarked files.
  163         cbc-essiv:<hash> protects against the weakness in cbc-plain, but is
  164         slightly slower. The digest size of the hash function passed to cbc-essiv
  165         must match the key size of the cipher.
  166         aes-cbc-essiv:sha256 works, while aes-cbc-essiv:sha1 does not
  167 
  168         For more information about the details of the attacks and risk assesment, see
  169         http://clemens.endorphin.org/LinuxHDEncSettings
  170         """
  171 
  172         if self.file != None:
  173             raise LuksError("This LuksFile has already been initialized")
  174 
  175         self._check_cipher(cipherName, cipherMode)
  176 
  177         self.magic = self.LUKS_MAGIC
  178         self.version = 1
  179         self.mkDigestIterations = 10
  180         self.keyBytes = masterSize
  181         self.hashSpec = hashSpec
  182 
  183         rand = Random.new()
  184 
  185         # Generate the salt
  186         self.mkDigestSalt = rand.read(self.SALT_SIZE)
  187 
  188         # Generate a random master key
  189         self.masterKey = rand.read(self.keyBytes)
  190         self.ivGen.set_key(self.masterKey)
  191 
  192         # generate the master key digest
  193         pbkdf = PBKDFv2.PBKDFv2()
  194         self.mkDigest = pbkdf.makeKey(self.masterKey, self.mkDigestSalt, self.mkDigestIterations, hashlib.new(self.hashSpec).digest_size, self.hashSpec)
  195 
  196         # init the key information
  197         currentSector = math.ceil(592.0 / self.SECTOR_SIZE)
  198         alignSectors = 4096 / self.SECTOR_SIZE
  199         blocksPerStripe = math.ceil(float(self.keyBytes * stripes) / self.SECTOR_SIZE)
  200 
  201         self.keys = [None] * 8
  202         for i in range(0, 8):
  203             if currentSector % alignSectors > 0:
  204                 currentSector += alignSectors - currentSector % alignSectors
  205             self.keys[i] = self._key_block()
  206             self.keys[i].create(currentSector, stripes, self.LUKS_KEY_DISABLED)
  207             currentSector += blocksPerStripe
  208 
  209         # Set the data offset
  210         if currentSector % alignSectors > 0:
  211             currentSector += alignSectors - currentSector % alignSectors
  212         self.payloadOffset = int(currentSector)
  213 
  214         # Generate a UUID for this file
  215         self._uuidgen(rand)
  216 
  217         # Create a new file, and save the header into it
  218         self.file = file
  219         self._save_header()
  220 
  221         # Write FF into all the key slots
  222         FFData = bytes([255] * int(self.SECTOR_SIZE))
  223         for i in range(0, 8):
  224             self.file.seek(int(self.keys[i].keyMaterialOffset))
  225             for sector in range(0, int(blocksPerStripe)):
  226                 self.file.write(FFData)
  227 
  228         # Flush the file to disk
  229         try:
  230             self.file.flush()
  231             os.fsync(self.file.fileno())
  232         except:
  233             # We might get an error because self.file.fileno() does not exist on StringIO
  234             pass
  235 
  236     def set_key(self, keyIndex, password, iterations, checkMinStripes=0):
  237         """Sets the key block at keyIndex using password
  238 
  239         Sets the key block at keyIndex using password, hashed iterations
  240         times using PBKDFv2 (RFC2104).  This LuksFile must be unlocked by
  241         calling open_any_key() before calling this function.
  242         checkMinStripes is used to detect basic header manipulation, since
  243         the number of stripes for the key is set by create(), before
  244         we write a password to disk we make sure the key is not weak because
  245         of a small number of stripes.
  246 
  247         This function will raise an error if the key is already enabled.
  248         """
  249 
  250         # Some checks
  251         if self.masterKey == None:
  252             raise LuksError("A key has not been unlocked.  Call open_any_key() first.")
  253 
  254         if keyIndex < 0 or keyIndex > 7:
  255             raise LuksError("keyIndex out of range")
  256 
  257         key = self.keys[keyIndex]
  258         if key.active != self.LUKS_KEY_DISABLED:
  259             raise LuksError("Key is active.  Delete the key and try again")
  260 
  261         if checkMinStripes == 0: checkMinStripes = self.KEY_STRIPES
  262         if key.stripes < checkMinStripes:
  263             raise LuksError("Key section %i contains too few stripes.  Header manipulation?" % keyIndex)
  264 
  265         key.passwordIterations = iterations
  266 
  267         # Generate a random salt for this key
  268         rand = Random.new()
  269         key.passwordSalt = rand.read(self.SALT_SIZE)
  270 
  271         # Hash the key using PBKDFv2
  272         pbkdf = PBKDFv2.PBKDFv2()
  273         derived_key = pbkdf.makeKey(password.encode(), key.passwordSalt, key.passwordIterations, self.keyBytes, self.hashSpec)
  274 
  275         # Split the key into key.stripes
  276         AfKey = AfSplitter.AFSplit(self.masterKey, key.stripes, self.hashSpec)
  277 
  278         AfKeySize = len(AfKey)
  279         if AfKeySize != key.stripes * self.keyBytes:
  280             raise LuksError("ERROR: AFSplit did not return the correct length of key")
  281 
  282         # Set the key for IV generation
  283         self.ivGen.set_key(derived_key)
  284 
  285         # Encrypt the splitted key using the hashed password
  286         AfSectors = int(math.ceil(float(AfKeySize) / self.SECTOR_SIZE))
  287         for sector in range(0, AfSectors):
  288             self._encrypt_sector(derived_key, key.keyMaterialOffset + sector, sector, \
  289                    AfKey[int(sector*self.SECTOR_SIZE):int((sector+1)*self.SECTOR_SIZE)])
  290 
  291         key.active = self.LUKS_KEY_ENABLED
  292 
  293         # Reset the key used for to IV generation in data mode
  294         self.ivGen.set_key(self.masterKey)
  295 
  296         self._save_header()
  297 
  298     def open_key(self, keyIndex, password):
  299         """Open a specific keyIndex using password.  Returns True on success"""
  300 
  301         if self.file == None:
  302             raise LuksError("LuksFile has not been initialized")
  303 
  304         if keyIndex < 0 or keyIndex > 7:
  305             raise LuksError("keyIndex is out of range")
  306 
  307         key = self.keys[keyIndex]
  308 
  309         if key.active != self.LUKS_KEY_ENABLED:
  310             return False
  311 
  312         # Hash the password using PBKDFv2
  313         pbkdf = PBKDFv2.PBKDFv2()
  314         derived_key = pbkdf.makeKey(password.encode(), key.passwordSalt, key.passwordIterations, self.keyBytes, self.hashSpec)
  315 
  316         # Setup the IV generation to use this key
  317         self.ivGen.set_key(derived_key)
  318 
  319         # Decrypt the master key data using the hashed password
  320         AfKeySize = key.stripes * self.keyBytes
  321         AfSectors = int(math.ceil(float(AfKeySize) / self.SECTOR_SIZE))
  322         AfKey = b""
  323         for sector in range(0, AfSectors):
  324             AfKey += self._decrypt_sector(derived_key, key.keyMaterialOffset + sector, sector)
  325         AfKey = AfKey[0:AfKeySize]
  326 
  327         # Merge the decrypted master key
  328         masterKey = AfSplitter.AFMerge(AfKey, key.stripes, self.hashSpec)
  329 
  330         # Check if the password was the correct one, by checking the master key digest
  331         checkDigest = pbkdf.makeKey(masterKey, self.mkDigestSalt, self.mkDigestIterations, hashlib.new(self.hashSpec).digest_size, self.hashSpec)
  332 
  333         # Since the header only stores DIGEST_SIZE (which is smaller than sha256 digest size)
  334         #   trim the digest to DIGEST_SIZE
  335         checkDigest = checkDigest[:self.DIGEST_SIZE]
  336 
  337         if checkDigest != self.mkDigest:
  338             return False
  339 
  340         self.masterKey = masterKey
  341         self.ivGen.set_key(self.masterKey)
  342         return True
  343 
  344 
  345     def open_any_key(self, password):
  346         """Try to open any enabled key using the provided password.  Returns index number on success, or None"""
  347 
  348         if self.file == None:
  349             raise LuksError("LuksFile has not been initialized")
  350 
  351         for i in range(0, 8):
  352             if self.open_key(i, password):
  353                 return i
  354         return None
  355 
  356     def enabled_key_count(self):
  357         """Returns the number of enabled key slots"""
  358 
  359         if self.file == None:
  360             raise LuksError("LuksFile has not been initialized")
  361 
  362         cnt = 0
  363         for i in range(0, 8):
  364             if self.keys[i].active == self.LUKS_KEY_ENABLED:
  365                 cnt += 1
  366         return cnt
  367 
  368     def key_information(self, keyIndex):
  369         """Returns a tuple of information about the key at keyIndex (enabled, iterations, stripes)"""
  370 
  371         if self.file == None:
  372             raise LuksError("LuksFile has not been initialized")
  373 
  374         if keyIndex < 0 or keyIndex > 7:
  375             raise LuksError("keyIndex out of range")
  376 
  377         key = self.keys[keyIndex]
  378         active = (key.active == self.LUKS_KEY_ENABLED)
  379         return (active, key.passwordIterations, key.stripes)
  380 
  381     def delete_key(self, keyIndex):
  382         """Delete the key located in slot keyIndex.  WARNING! This is NOT a secure delete
  383 
  384         Warning! If keyIndex is the last enabled key, the data will become unrecoverable
  385 
  386         This function will not securely delete the key.  Because this class is designed
  387         for reading and writing to a file, there is no guarante that writing over the data
  388         inside the file will destroy it.  If you have a key leaked, you need to investigate
  389         other methods of securely destroying data, including destroying the entire file system
  390         and disk this file was located on, and any backups of this file that were created
  391         (depending on your needs).  The good news is, because of the way LUKS encrypts the
  392         master key, only one bit in the master key needs to be destoryed.  But the same bit
  393         needs to be destroyed in all copies, including (possibly) the journal, bad remapped
  394         blocks on the disk, etc.
  395 
  396         If you would like to continue using this encrypted file, you need to set_key() a new
  397         key, delete_key() the leaked key, and then copy the file into a new file on a
  398         different disk and filesystem.  This class writes "FF" to the key location during
  399         delete_key(), so during the copy the new disk will just get "FF" in the key location,
  400         which will be unrecoverable on the new disk.
  401         """
  402 
  403         if self.file == None:
  404             raise LuksError("LuksFile has not been initialized")
  405 
  406         if keyIndex < 0 or keyIndex > 7:
  407             raise LuksError("keyIndex out of range")
  408 
  409         key = self.keys[keyIndex]
  410 
  411         # Start and end offset of the key material
  412         startOffset = key.keyMaterialOffset
  413         endOffset = startOffset + int(math.ceil((self.keyBytes * key.stripes) / self.SECTOR_SIZE))
  414 
  415         # Just write "FF" into the locations
  416         for i in range(startOffset, endOffset):
  417             self.file.seek(int(i * self.SECTOR_SIZE))
  418             self.file.write("\xFF" * int(self.SECTOR_SIZE))
  419 
  420         try:
  421             self.file.flush()
  422             os.fsync(self.file.fileno())
  423         except:
  424             # We might get an error because self.file.fileno() does not exist on StringIO
  425             pass
  426 
  427         key.active = self.LUKS_KEY_DISABLED
  428         key.passwordIterations = 0
  429         key.passwordSalt = ''
  430 
  431         self._save_header()
  432 
  433     def close(self):
  434         """Close the underlying file descriptor, and discard the cached master key used for decryption"""
  435 
  436         if self.ivGen != None:
  437             self.ivGen.set_key(b"")
  438         if self.file != None:
  439             self.file.close()
  440 
  441         self.file = None
  442         self.masterKey = None
  443         self.ivGen = None
  444 
  445     def data_length(self):
  446         """Returns the total data length"""
  447 
  448         if self.file == None:
  449             raise LuksError("LuksFile has not been initialized")
  450 
  451         # Seek to the end of the file, and use tell()
  452         self.file.seek(0, 2)
  453         fLen = self.file.tell()
  454         return fLen - int(self.payloadOffset * self.SECTOR_SIZE)
  455 
  456     def truncate(self, length):
  457         """Truncate the file so that the data is maximum of length in size"""
  458 
  459         if self.file == None:
  460             raise LuksError("LuksFile has not been initialized")
  461 
  462         if length % self.SECTOR_SIZE != 0:
  463             raise LuksError("length must be a multiple of %s" % self.SECTOR_SIZE)
  464 
  465         if length < 0:
  466             raise LuksError("length must be positive")
  467 
  468         self.file.truncate(int(self.payloadOffset * self.SECTOR_SIZE) + length)
  469 
  470     def encrypt_data(self, offset, data):
  471         """Encrypt data into the file.
  472 
  473         Offset is a zero indexed location in the data to write.
  474         Both offset and len(data) must be multiples of 512.
  475         """
  476 
  477         # Check conditions
  478         if self.masterKey == None:
  479             raise LuksError("A key has not been unlocked.  Call open_any_key() first.")
  480 
  481         if offset % self.SECTOR_SIZE != 0:
  482             raise LuksError("offset must be a multiple of %s" % self.SECTOR_SIZE)
  483 
  484         dataLen = len(data)
  485         if dataLen % self.SECTOR_SIZE != 0:
  486             raise LuksError("data length must be a multiple of %s" % self.SECTOR_SIZE)
  487 
  488         # Encrypt all the data
  489         startSector = int(offset / self.SECTOR_SIZE)
  490         endSector = int((offset + dataLen) / self.SECTOR_SIZE)
  491         for sector in range(startSector, endSector):
  492             dslice_sector = sector - startSector
  493             dslice = data[int(dslice_sector * self.SECTOR_SIZE):int((dslice_sector+1)*self.SECTOR_SIZE)]
  494             self._encrypt_sector(self.masterKey, self.payloadOffset + sector, sector, dslice)
  495 
  496     def decrypt_data(self, offset, length):
  497         """Decrypt data from the file.
  498 
  499         Offset is a zero indexed location into the data, and length is
  500         the ammout of data to return.  offset and length can be any value,
  501         but multiples of 512 are the most efficient.
  502         """
  503 
  504         if self.masterKey == None:
  505             raise LuksError("A key has not been unlocked.  Call open_any_key() first.")
  506 
  507         frontPad = int(offset % self.SECTOR_SIZE)
  508         startSector = int(math.floor(offset / self.SECTOR_SIZE))
  509         endSector = int(math.ceil((offset + length) / self.SECTOR_SIZE))
  510         ret = b""
  511         for sector in range(startSector, endSector):
  512             ret += self._decrypt_sector(self.masterKey, self.payloadOffset + sector, sector)
  513 
  514         return ret[frontPad:frontPad+length]
  515 
  516     ##### Private functions
  517 
  518     class _key_block:
  519         """Internal class, used to store the key information about each key."""
  520 
  521         LUKS_KEY_FORMAT = ">II32sII"
  522 
  523         def load_from_str(self,str):
  524             """Unpack the key information from a string"""
  525             self.active, \
  526             self.passwordIterations, \
  527             self.passwordSalt, \
  528             self.keyMaterialOffset, \
  529             self.stripes =  \
  530             struct.unpack(self.LUKS_KEY_FORMAT,str)
  531 
  532         def create(self,offset, stripes, disabled):
  533             """Create a new set of key information.  Called from LuksFile.create()"""
  534             self.active = disabled
  535             self.passwordIterations = 0
  536             self.passwordSalt = b''
  537             self.keyMaterialOffset = int(offset)
  538             self.stripes = stripes
  539 
  540         def save(self):
  541             """Pack the key information into a string"""
  542             return struct.pack(self.LUKS_KEY_FORMAT, self.active, self.passwordIterations, \
  543                     self.passwordSalt, self.keyMaterialOffset, self.stripes)
  544 
  545     class _plain_iv_gen:
  546         """Internal class to represent cbc-plain cipherMode"""
  547 
  548         def set_key(self, key):
  549             # plain IV generation does not use the key in any way
  550             pass
  551 
  552         def generate(self, sectorOffset, size):
  553             istr = struct.pack("<I", sectorOffset)
  554             return istr + bytes([0] * (size - 4))
  555 
  556     class _essiv_gen:
  557         """Internal class to represent cbc-essiv:<hash> cipherMode"""
  558 
  559         # essiv mode is defined by
  560         # SALT=Hash(KEY)
  561         # IV=E(SALT,sectornumber)
  562         def __init__(self, str, cipher, luksParent):
  563             self.hashSpec = str[1:]
  564             self.cipher = cipher
  565 
  566         def set_key(self, key):
  567             h = hashlib.new(self.hashSpec, key)
  568             self.salt = h.digest()
  569             self.encr = self.cipher.new(self.salt, self.cipher.MODE_ECB)
  570 
  571         def generate(self, sectorOffset, size):
  572             istr = struct.pack("<I", sectorOffset) + bytes([0] * (size - 4))
  573             return self.encr.encrypt(istr)
  574 
  575     def _check_cipher(self, cipherName, cipherMode):
  576         """Internal function to check for a valid cipherName and cipherMode"""
  577         if cipherName == "aes":
  578             self.cipher = AES
  579         elif cipherName == "cast5":
  580             self.cipher = CAST
  581         elif cipherName == "blowfish":
  582             self.cipher = Blowfish
  583         else:
  584             raise LuksError("invalid cipher %s" % cipherName)
  585 
  586         # All supported ciphers are block ciphers, so modes are the same (CBC)
  587         self.mode = self.cipher.MODE_CBC
  588 
  589         if cipherMode == "cbc-plain":
  590             self.ivGen = self._plain_iv_gen()
  591         elif cipherMode[:10] == "cbc-essiv:":
  592             self.ivGen = self._essiv_gen(cipherMode[9:], self.cipher, self)
  593         else:
  594             raise LuksError("invalid cipher mode %s" % cipherMode)
  595 
  596         self.cipherName = cipherName
  597         self.cipherMode = cipherMode
  598 
  599     def _uuidgen(self, rand):
  600         """Internal function to generate a UUID"""
  601 
  602         # I copied this code (and slightly modified it) from a module written
  603         # by Denys Duchier http://ofxsuite.berlios.de/uuid.py  (which is under the GPL)
  604 
  605         buf = rand.read(16)
  606         low,mid,hi_and_version,seq,node = struct.unpack(">IHHH6s",buf)
  607         seq = (seq & 0x3FFF) | 0x8000
  608         hi_and_version = (hi_and_version & 0x0FFF) | 0x4000
  609         uuid = struct.pack(">IHHH6s",low,mid,hi_and_version,seq,node)
  610         low,mid,hi,seq,b5,b4,b3,b2,b1,b0 = struct.unpack(">IHHHBBBBBB",uuid)
  611         self.uuid =  b"%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x" % (low,mid,hi,seq>>8,seq&0xFF,b5,b4,b3,b2,b1,b0)
  612 
  613     def _save_header(self):
  614         """Internal function to save the header info into the file"""
  615         str=struct.pack(self.LUKS_FORMAT, self.magic, self.version, self.cipherName.encode(), self.cipherMode.encode(), self.hashSpec.encode(), \
  616              self.payloadOffset, self.keyBytes, self.mkDigest, self.mkDigestSalt, self.mkDigestIterations, self.uuid)
  617         self.file.seek(0)
  618         self.file.write(str)
  619 
  620         for i in range(0, 8):
  621             self.file.write(self.keys[i].save())
  622 
  623         try:
  624             self.file.flush()
  625             os.fsync(self.file.fileno())
  626         except:
  627             # We might get an error because self.file.fileno() does not exist on StringIO
  628             pass
  629 
  630     def _encrypt_sector(self, key, sector, sectorOffset, data):
  631         """Internal function to encrypt a single sector"""
  632 
  633         if len(data) > self.SECTOR_SIZE:
  634             raise LuksError("_encrypt_page only accepts data of size <= %i" % self.SECTOR_SIZE)
  635 
  636         # Encrypt the data using the cipher, iv generation, and mode
  637         IV = self.ivGen.generate(sectorOffset, self.cipher.block_size)
  638         cipher = self.cipher.new(key, self.mode, IV)
  639         encrData = cipher.encrypt(data)
  640 
  641         # Write the encrypted data to disk
  642         self.file.seek(int(sector * self.SECTOR_SIZE))
  643         self.file.write(encrData)
  644 
  645     def _decrypt_sector(self, key, sector, sectorOffset):
  646         """Internal function to decrypt a single sector"""
  647 
  648         # Read the ciphertext from disk
  649         self.file.seek(int(sector * self.SECTOR_SIZE))
  650         encrData = self.file.read(int(self.SECTOR_SIZE))
  651 
  652         # Decrypt the data using cipher, iv generation, and mode
  653         IV = self.ivGen.generate(sectorOffset, self.cipher.block_size)
  654         cipher = self.cipher.new(key, self.mode, IV)
  655         return cipher.decrypt(encrData)
  656 
  657 # The following was copied from the reference implementation of LUKS in cryptsetup-luks-1.0.1 from
  658 # http://luks.endorphin.org/dm-crypt
  659 
  660 #define LUKS_CIPHERNAME_L 32
  661 #define LUKS_CIPHERMODE_L 32
  662 #define LUKS_HASHSPEC_L 32
  663 #define LUKS_DIGESTSIZE 20 // since SHA1
  664 #define LUKS_HMACSIZE 32
  665 #define LUKS_SALTSIZE 32
  666 #define LUKS_NUMKEYS 8
  667 #define LUKS_MAGIC_L 6
  668 
  669 #/* Actually we need only 37, but we don't want struct autoaligning to kick in */
  670 #define UUID_STRING_L 40
  671 
  672 #struct luks_phdr {
  673 #   char        magic[LUKS_MAGIC_L];
  674 #   uint16_t    version;
  675 #   char        cipherName[LUKS_CIPHERNAME_L];
  676 #   char        cipherMode[LUKS_CIPHERMODE_L];
  677 #   char            hashSpec[LUKS_HASHSPEC_L];
  678 #   uint32_t    payloadOffset;
  679 #   uint32_t    keyBytes;
  680 #   char        mkDigest[LUKS_DIGESTSIZE];
  681 #   char        mkDigestSalt[LUKS_SALTSIZE];
  682 #   uint32_t    mkDigestIterations;
  683 #   char            uuid[UUID_STRING_L];
  684 #
  685 #   struct {
  686 #       uint32_t active;
  687 #
  688 #       /* parameters used for password processing */
  689 #       uint32_t passwordIterations;
  690 #       char     passwordSalt[LUKS_SALTSIZE];
  691 #
  692 #       /* parameters used for AF store/load */
  693 #       uint32_t keyMaterialOffset;
  694 #       uint32_t stripes;
  695 #   } keyblock[LUKS_NUMKEYS];
  696 #};
  697 
  698 # size is 208 bytes + 48 * LUKS_NUMKEYS  = 592 bytes