"Fossies" - the Fresh Open Source Software Archive  

Source code changes of the file "src/bundle/luks.py" between
revelation-0.5.3.tar.xz and revelation-0.5.4.tar.xz

About: Revelation is a password manager for the GNOME 3 desktop.

luks.py  (revelation-0.5.3.tar.xz):luks.py  (revelation-0.5.4.tar.xz)
skipping to change at line 67 skipping to change at line 67
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
http://www.gnu.org/copyleft/gpl.html http://www.gnu.org/copyleft/gpl.html
""" """
import os, math, struct, stat, hashlib import os, math, struct, stat, hashlib
import Cryptodome.Random as Random import Cryptodome.Random as Random
from Cryptodome.Cipher import * from Cryptodome.Cipher import AES
from . import PBKDFv2, AfSplitter from . import PBKDFv2, AfSplitter
class LuksError(Exception): class LuksError(Exception):
def __init__(self, value): def __init__(self, value):
self.value = value self.value = value
def __str__(self): def __str__(self):
return repr(self.value) return repr(self.value)
class LuksFile: class LuksFile:
"""Implements the LUKS (Linux Unified Key Setup) Version 1.0 http://luks.end orphin.org/""" """Implements the LUKS (Linux Unified Key Setup) Version 1.0 http://luks.end orphin.org/"""
LUKS_FORMAT = ">6sH32s32s32sII20s32sI40s" LUKS_FORMAT = ">6sH32s32s32sII20s32sI40s"
LUKS_MAGIC = "LUKS\xba\xbe" LUKS_MAGIC = b"LUKS\xba\xbe"
LUKS_KEY_DISABLED = 0x0000DEAD LUKS_KEY_DISABLED = 0x0000DEAD
LUKS_KEY_ENABLED = 0x00AC71F3 LUKS_KEY_ENABLED = 0x00AC71F3
SECTOR_SIZE = 512.0 SECTOR_SIZE = 512.0
KEY_STRIPES = 4000 KEY_STRIPES = 4000
SALT_SIZE = 32 SALT_SIZE = 32
DIGEST_SIZE = 20 DIGEST_SIZE = 20
skipping to change at line 124 skipping to change at line 124
cipherMode, \ cipherMode, \
hashSpec, \ hashSpec, \
self.payloadOffset, \ self.payloadOffset, \
self.keyBytes, \ self.keyBytes, \
self.mkDigest, \ self.mkDigest, \
self.mkDigestSalt, \ self.mkDigestSalt, \
self.mkDigestIterations, \ self.mkDigestIterations, \
self.uuid = \ self.uuid = \
struct.unpack(self.LUKS_FORMAT, self.file.read(208)) struct.unpack(self.LUKS_FORMAT, self.file.read(208))
cipherName = cipherName.decode()
cipherMode = cipherMode.decode()
hashSpec = hashSpec.decode()
# check magic # check magic
if self.magic != self.LUKS_MAGIC: if self.magic != self.LUKS_MAGIC:
self.file = None self.file = None
raise LuksError("%s is not a LUKS data file" % filename) raise LuksError("%s is not a LUKS data file" % filename)
# Check the hash and cipher # Check the hash and cipher
self.hashSpec = hashSpec.strip(" \x00") self.hashSpec = hashSpec.strip(" \x00")
self._check_cipher(cipherName.strip(" \x00"), cipherMode.strip(" \x00")) self._check_cipher(cipherName.strip(" \x00"), cipherMode.strip(" \x00"))
# Load the key information # Load the key information
skipping to change at line 208 skipping to change at line 212
for i in range(0, 8): for i in range(0, 8):
if currentSector % alignSectors > 0: if currentSector % alignSectors > 0:
currentSector += alignSectors - currentSector % alignSectors currentSector += alignSectors - currentSector % alignSectors
self.keys[i] = self._key_block() self.keys[i] = self._key_block()
self.keys[i].create(currentSector, stripes, self.LUKS_KEY_DISABLED) self.keys[i].create(currentSector, stripes, self.LUKS_KEY_DISABLED)
currentSector += blocksPerStripe currentSector += blocksPerStripe
# Set the data offset # Set the data offset
if currentSector % alignSectors > 0: if currentSector % alignSectors > 0:
currentSector += alignSectors - currentSector % alignSectors currentSector += alignSectors - currentSector % alignSectors
self.payloadOffset = currentSector self.payloadOffset = int(currentSector)
# Generate a UUID for this file # Generate a UUID for this file
self._uuidgen(rand) self._uuidgen(rand)
# Create a new file, and save the header into it # Create a new file, and save the header into it
self.file = file self.file = file
self._save_header() self._save_header()
# Write FF into all the key slots # Write FF into all the key slots
FFData = "\xFF" * int(self.SECTOR_SIZE) FFData = bytes([255] * int(self.SECTOR_SIZE))
for i in range(0, 8): for i in range(0, 8):
self.file.seek(int(self.keys[i].keyMaterialOffset)) self.file.seek(int(self.keys[i].keyMaterialOffset))
for sector in range(0, int(blocksPerStripe)): for sector in range(0, int(blocksPerStripe)):
self.file.write(FFData) self.file.write(FFData)
# Flush the file to disk # Flush the file to disk
try: try:
self.file.flush() self.file.flush()
os.fsync(self.file.fileno()) os.fsync(self.file.fileno())
except: except:
skipping to change at line 269 skipping to change at line 273
raise LuksError("Key section %i contains too few stripes. Header ma nipulation?" % keyIndex) raise LuksError("Key section %i contains too few stripes. Header ma nipulation?" % keyIndex)
key.passwordIterations = iterations key.passwordIterations = iterations
# Generate a random salt for this key # Generate a random salt for this key
rand = Random.new() rand = Random.new()
key.passwordSalt = rand.read(self.SALT_SIZE) key.passwordSalt = rand.read(self.SALT_SIZE)
# Hash the key using PBKDFv2 # Hash the key using PBKDFv2
pbkdf = PBKDFv2.PBKDFv2() pbkdf = PBKDFv2.PBKDFv2()
derived_key = pbkdf.makeKey(password, key.passwordSalt, key.passwordIter ations, self.keyBytes, self.hashSpec) derived_key = pbkdf.makeKey(password.encode(), key.passwordSalt, key.pas swordIterations, self.keyBytes, self.hashSpec)
# Split the key into key.stripes # Split the key into key.stripes
AfKey = AfSplitter.AFSplit(self.masterKey, key.stripes, self.hashSpec) AfKey = AfSplitter.AFSplit(self.masterKey, key.stripes, self.hashSpec)
AfKeySize = len(AfKey) AfKeySize = len(AfKey)
if AfKeySize != key.stripes * self.keyBytes: if AfKeySize != key.stripes * self.keyBytes:
raise LuksError("ERROR: AFSplit did not return the correct length of key") raise LuksError("ERROR: AFSplit did not return the correct length of key")
# Set the key for IV generation # Set the key for IV generation
self.ivGen.set_key(derived_key) self.ivGen.set_key(derived_key)
skipping to change at line 310 skipping to change at line 314
if keyIndex < 0 or keyIndex > 7: if keyIndex < 0 or keyIndex > 7:
raise LuksError("keyIndex is out of range") raise LuksError("keyIndex is out of range")
key = self.keys[keyIndex] key = self.keys[keyIndex]
if key.active != self.LUKS_KEY_ENABLED: if key.active != self.LUKS_KEY_ENABLED:
return False return False
# Hash the password using PBKDFv2 # Hash the password using PBKDFv2
pbkdf = PBKDFv2.PBKDFv2() pbkdf = PBKDFv2.PBKDFv2()
derived_key = pbkdf.makeKey(password, key.passwordSalt, key.passwordIter ations, self.keyBytes, self.hashSpec) derived_key = pbkdf.makeKey(password.encode(), key.passwordSalt, key.pas swordIterations, self.keyBytes, self.hashSpec)
# Setup the IV generation to use this key # Setup the IV generation to use this key
self.ivGen.set_key(derived_key) self.ivGen.set_key(derived_key)
# Decrypt the master key data using the hashed password # Decrypt the master key data using the hashed password
AfKeySize = key.stripes * self.keyBytes AfKeySize = key.stripes * self.keyBytes
AfSectors = int(math.ceil(float(AfKeySize) / self.SECTOR_SIZE)) AfSectors = int(math.ceil(float(AfKeySize) / self.SECTOR_SIZE))
AfKey = "" AfKey = b""
for sector in range(0, AfSectors): for sector in range(0, AfSectors):
AfKey += self._decrypt_sector(derived_key, key.keyMaterialOffset + s ector, sector) AfKey += self._decrypt_sector(derived_key, key.keyMaterialOffset + s ector, sector)
AfKey = AfKey[0:AfKeySize] AfKey = AfKey[0:AfKeySize]
# Merge the decrypted master key # Merge the decrypted master key
masterKey = AfSplitter.AFMerge(AfKey, key.stripes, self.hashSpec) masterKey = AfSplitter.AFMerge(AfKey, key.stripes, self.hashSpec)
# Check if the password was the correct one, by checking the master key digest # Check if the password was the correct one, by checking the master key digest
checkDigest = pbkdf.makeKey(masterKey, self.mkDigestSalt, self.mkDigestI terations, hashlib.new(self.hashSpec).digest_size, self.hashSpec) checkDigest = pbkdf.makeKey(masterKey, self.mkDigestSalt, self.mkDigestI terations, hashlib.new(self.hashSpec).digest_size, self.hashSpec)
skipping to change at line 432 skipping to change at line 436
key.active = self.LUKS_KEY_DISABLED key.active = self.LUKS_KEY_DISABLED
key.passwordIterations = 0 key.passwordIterations = 0
key.passwordSalt = '' key.passwordSalt = ''
self._save_header() self._save_header()
def close(self): def close(self):
"""Close the underlying file descriptor, and discard the cached master k ey used for decryption""" """Close the underlying file descriptor, and discard the cached master k ey used for decryption"""
if self.ivGen != None: if self.ivGen != None:
self.ivGen.set_key("") self.ivGen.set_key(b"")
if self.file != None: if self.file != None:
self.file.close() self.file.close()
self.file = None self.file = None
self.masterKey = None self.masterKey = None
self.ivGen = None self.ivGen = None
def data_length(self): def data_length(self):
"""Returns the total data length""" """Returns the total data length"""
skipping to change at line 505 skipping to change at line 509
the ammout of data to return. offset and length can be any value, the ammout of data to return. offset and length can be any value,
but multiples of 512 are the most efficient. but multiples of 512 are the most efficient.
""" """
if self.masterKey == None: if self.masterKey == None:
raise LuksError("A key has not been unlocked. Call open_any_key() f irst.") raise LuksError("A key has not been unlocked. Call open_any_key() f irst.")
frontPad = int(offset % self.SECTOR_SIZE) frontPad = int(offset % self.SECTOR_SIZE)
startSector = int(math.floor(offset / self.SECTOR_SIZE)) startSector = int(math.floor(offset / self.SECTOR_SIZE))
endSector = int(math.ceil((offset + length) / self.SECTOR_SIZE)) endSector = int(math.ceil((offset + length) / self.SECTOR_SIZE))
ret = "" ret = b""
for sector in range(startSector, endSector): for sector in range(startSector, endSector):
ret += self._decrypt_sector(self.masterKey, self.payloadOffset + sec tor, sector) ret += self._decrypt_sector(self.masterKey, self.payloadOffset + sec tor, sector)
return ret[frontPad:frontPad+length] return ret[frontPad:frontPad+length]
##### Private functions ##### Private functions
class _key_block: class _key_block:
"""Internal class, used to store the key information about each key.""" """Internal class, used to store the key information about each key."""
skipping to change at line 531 skipping to change at line 535
self.passwordIterations, \ self.passwordIterations, \
self.passwordSalt, \ self.passwordSalt, \
self.keyMaterialOffset, \ self.keyMaterialOffset, \
self.stripes = \ self.stripes = \
struct.unpack(self.LUKS_KEY_FORMAT,str) struct.unpack(self.LUKS_KEY_FORMAT,str)
def create(self,offset, stripes, disabled): def create(self,offset, stripes, disabled):
"""Create a new set of key information. Called from LuksFile.create ()""" """Create a new set of key information. Called from LuksFile.create ()"""
self.active = disabled self.active = disabled
self.passwordIterations = 0 self.passwordIterations = 0
self.passwordSalt = '' self.passwordSalt = b''
self.keyMaterialOffset = offset self.keyMaterialOffset = int(offset)
self.stripes = stripes self.stripes = stripes
def save(self): def save(self):
"""Pack the key information into a string""" """Pack the key information into a string"""
return struct.pack(self.LUKS_KEY_FORMAT, self.active, self.passwordI terations, \ return struct.pack(self.LUKS_KEY_FORMAT, self.active, self.passwordI terations, \
self.passwordSalt, self.keyMaterialOffset, self.stripes) self.passwordSalt, self.keyMaterialOffset, self.stripes)
class _plain_iv_gen: class _plain_iv_gen:
"""Internal class to represent cbc-plain cipherMode""" """Internal class to represent cbc-plain cipherMode"""
def set_key(self, key): def set_key(self, key):
# plain IV generation does not use the key in any way # plain IV generation does not use the key in any way
pass pass
def generate(self, sectorOffset, size): def generate(self, sectorOffset, size):
istr = struct.pack("<I", sectorOffset) istr = struct.pack("<I", sectorOffset)
return istr + "\x00" * (size - 4) return istr + bytes([0] * (size - 4))
class _essiv_gen: class _essiv_gen:
"""Internal class to represent cbc-essiv:<hash> cipherMode""" """Internal class to represent cbc-essiv:<hash> cipherMode"""
# essiv mode is defined by # essiv mode is defined by
# SALT=Hash(KEY) # SALT=Hash(KEY)
# IV=E(SALT,sectornumber) # IV=E(SALT,sectornumber)
def __init__(self, str, cipher, luksParent): def __init__(self, str, cipher, luksParent):
self.hashSpec = str[1:] self.hashSpec = str[1:]
self.cipher = cipher self.cipher = cipher
def set_key(self, key): def set_key(self, key):
h = hashlib.new(self.hashSpec, key) h = hashlib.new(self.hashSpec, key)
self.salt = h.digest() self.salt = h.digest()
self.encr = self.cipher.new(self.salt, self.cipher.MODE_ECB) self.encr = self.cipher.new(self.salt, self.cipher.MODE_ECB)
def generate(self, sectorOffset, size): def generate(self, sectorOffset, size):
istr = struct.pack("<I", sectorOffset) + "\x00" * (size - 4) istr = struct.pack("<I", sectorOffset) + bytes([0] * (size - 4))
return self.encr.encrypt(istr) return self.encr.encrypt(istr)
def _check_cipher(self, cipherName, cipherMode): def _check_cipher(self, cipherName, cipherMode):
"""Internal function to check for a valid cipherName and cipherMode""" """Internal function to check for a valid cipherName and cipherMode"""
if cipherName == "aes": if cipherName == "aes":
self.cipher = AES self.cipher = AES
elif cipherName == "cast5": elif cipherName == "cast5":
self.cipher = CAST self.cipher = CAST
elif cipherName == "blowfish": elif cipherName == "blowfish":
self.cipher = Blowfish self.cipher = Blowfish
skipping to change at line 606 skipping to change at line 610
# I copied this code (and slightly modified it) from a module written # I copied this code (and slightly modified it) from a module written
# by Denys Duchier http://ofxsuite.berlios.de/uuid.py (which is under t he GPL) # by Denys Duchier http://ofxsuite.berlios.de/uuid.py (which is under t he GPL)
buf = rand.read(16) buf = rand.read(16)
low,mid,hi_and_version,seq,node = struct.unpack(">IHHH6s",buf) low,mid,hi_and_version,seq,node = struct.unpack(">IHHH6s",buf)
seq = (seq & 0x3FFF) | 0x8000 seq = (seq & 0x3FFF) | 0x8000
hi_and_version = (hi_and_version & 0x0FFF) | 0x4000 hi_and_version = (hi_and_version & 0x0FFF) | 0x4000
uuid = struct.pack(">IHHH6s",low,mid,hi_and_version,seq,node) 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) 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,m id,hi,seq>>8,seq&0xFF,b5,b4,b3,b2,b1,b0) 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)
def _save_header(self): def _save_header(self):
"""Internal function to save the header info into the file""" """Internal function to save the header info into the file"""
str=struct.pack(self.LUKS_FORMAT, self.magic, self.version, self.cipherN
str=struct.pack(self.LUKS_FORMAT, self.magic, self.version, self.cipherN ame.encode(), self.cipherMode.encode(), self.hashSpec.encode(), \
ame, self.cipherMode, self.hashSpec, \
self.payloadOffset, self.keyBytes, self.mkDigest, self.mkDigestSalt , self.mkDigestIterations, self.uuid) self.payloadOffset, self.keyBytes, self.mkDigest, self.mkDigestSalt , self.mkDigestIterations, self.uuid)
self.file.seek(0) self.file.seek(0)
self.file.write(str) self.file.write(str)
for i in range(0, 8): for i in range(0, 8):
self.file.write(self.keys[i].save()) self.file.write(self.keys[i].save())
try: try:
self.file.flush() self.file.flush()
os.fsync(self.file.fileno()) os.fsync(self.file.fileno())
 End of changes. 15 change blocks. 
17 lines changed or deleted 20 lines changed or added

Home  |  About  |  Features  |  All  |  Newest  |  Dox  |  Diffs  |  RSS Feeds  |  Screenshots  |  Comments  |  Imprint  |  Privacy  |  HTTP(S)