|  | @@ -21,6 +21,7 @@ from .helpers import yes
 | 
	
		
			
				|  |  |  from .helpers import get_keys_dir
 | 
	
		
			
				|  |  |  from .helpers import bin_to_hex
 | 
	
		
			
				|  |  |  from .helpers import CompressionDecider2, CompressionSpec
 | 
	
		
			
				|  |  | +from .item import Key, EncryptedKey
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  PREFIX = b'\0' * 8
 | 
	
	
		
			
				|  | @@ -341,24 +342,26 @@ class KeyfileKeyBase(AESKeyBase):
 | 
	
		
			
				|  |  |          cdata = a2b_base64(key_data)
 | 
	
		
			
				|  |  |          data = self.decrypt_key_file(cdata, passphrase)
 | 
	
		
			
				|  |  |          if data:
 | 
	
		
			
				|  |  | -            key = msgpack.unpackb(data)
 | 
	
		
			
				|  |  | -            if key[b'version'] != 1:
 | 
	
		
			
				|  |  | +            data = msgpack.unpackb(data)
 | 
	
		
			
				|  |  | +            key = Key(internal_dict=data)
 | 
	
		
			
				|  |  | +            if key.version != 1:
 | 
	
		
			
				|  |  |                  raise IntegrityError('Invalid key file header')
 | 
	
		
			
				|  |  | -            self.repository_id = key[b'repository_id']
 | 
	
		
			
				|  |  | -            self.enc_key = key[b'enc_key']
 | 
	
		
			
				|  |  | -            self.enc_hmac_key = key[b'enc_hmac_key']
 | 
	
		
			
				|  |  | -            self.id_key = key[b'id_key']
 | 
	
		
			
				|  |  | -            self.chunk_seed = key[b'chunk_seed']
 | 
	
		
			
				|  |  | +            self.repository_id = key.repository_id
 | 
	
		
			
				|  |  | +            self.enc_key = key.enc_key
 | 
	
		
			
				|  |  | +            self.enc_hmac_key = key.enc_hmac_key
 | 
	
		
			
				|  |  | +            self.id_key = key.id_key
 | 
	
		
			
				|  |  | +            self.chunk_seed = key.chunk_seed
 | 
	
		
			
				|  |  |              return True
 | 
	
		
			
				|  |  |          return False
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      def decrypt_key_file(self, data, passphrase):
 | 
	
		
			
				|  |  | -        d = msgpack.unpackb(data)
 | 
	
		
			
				|  |  | -        assert d[b'version'] == 1
 | 
	
		
			
				|  |  | -        assert d[b'algorithm'] == b'sha256'
 | 
	
		
			
				|  |  | -        key = passphrase.kdf(d[b'salt'], d[b'iterations'], 32)
 | 
	
		
			
				|  |  | -        data = AES(is_encrypt=False, key=key).decrypt(d[b'data'])
 | 
	
		
			
				|  |  | -        if hmac_sha256(key, data) == d[b'hash']:
 | 
	
		
			
				|  |  | +        data = msgpack.unpackb(data)
 | 
	
		
			
				|  |  | +        enc_key = EncryptedKey(internal_dict=data)
 | 
	
		
			
				|  |  | +        assert enc_key.version == 1
 | 
	
		
			
				|  |  | +        assert enc_key.algorithm == 'sha256'
 | 
	
		
			
				|  |  | +        key = passphrase.kdf(enc_key.salt, enc_key.iterations, 32)
 | 
	
		
			
				|  |  | +        data = AES(is_encrypt=False, key=key).decrypt(enc_key.data)
 | 
	
		
			
				|  |  | +        if hmac_sha256(key, data) == enc_key.hash:
 | 
	
		
			
				|  |  |              return data
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      def encrypt_key_file(self, data, passphrase):
 | 
	
	
		
			
				|  | @@ -367,26 +370,26 @@ class KeyfileKeyBase(AESKeyBase):
 | 
	
		
			
				|  |  |          key = passphrase.kdf(salt, iterations, 32)
 | 
	
		
			
				|  |  |          hash = hmac_sha256(key, data)
 | 
	
		
			
				|  |  |          cdata = AES(is_encrypt=True, key=key).encrypt(data)
 | 
	
		
			
				|  |  | -        d = {
 | 
	
		
			
				|  |  | -            'version': 1,
 | 
	
		
			
				|  |  | -            'salt': salt,
 | 
	
		
			
				|  |  | -            'iterations': iterations,
 | 
	
		
			
				|  |  | -            'algorithm': 'sha256',
 | 
	
		
			
				|  |  | -            'hash': hash,
 | 
	
		
			
				|  |  | -            'data': cdata,
 | 
	
		
			
				|  |  | -        }
 | 
	
		
			
				|  |  | -        return msgpack.packb(d)
 | 
	
		
			
				|  |  | +        enc_key = EncryptedKey(
 | 
	
		
			
				|  |  | +            version=1,
 | 
	
		
			
				|  |  | +            salt=salt,
 | 
	
		
			
				|  |  | +            iterations=iterations,
 | 
	
		
			
				|  |  | +            algorithm='sha256',
 | 
	
		
			
				|  |  | +            hash=hash,
 | 
	
		
			
				|  |  | +            data=cdata,
 | 
	
		
			
				|  |  | +        )
 | 
	
		
			
				|  |  | +        return msgpack.packb(enc_key.as_dict())
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      def _save(self, passphrase):
 | 
	
		
			
				|  |  | -        key = {
 | 
	
		
			
				|  |  | -            'version': 1,
 | 
	
		
			
				|  |  | -            'repository_id': self.repository_id,
 | 
	
		
			
				|  |  | -            'enc_key': self.enc_key,
 | 
	
		
			
				|  |  | -            'enc_hmac_key': self.enc_hmac_key,
 | 
	
		
			
				|  |  | -            'id_key': self.id_key,
 | 
	
		
			
				|  |  | -            'chunk_seed': self.chunk_seed,
 | 
	
		
			
				|  |  | -        }
 | 
	
		
			
				|  |  | -        data = self.encrypt_key_file(msgpack.packb(key), passphrase)
 | 
	
		
			
				|  |  | +        key = Key(
 | 
	
		
			
				|  |  | +            version=1,
 | 
	
		
			
				|  |  | +            repository_id=self.repository_id,
 | 
	
		
			
				|  |  | +            enc_key=self.enc_key,
 | 
	
		
			
				|  |  | +            enc_hmac_key=self.enc_hmac_key,
 | 
	
		
			
				|  |  | +            id_key=self.id_key,
 | 
	
		
			
				|  |  | +            chunk_seed=self.chunk_seed,
 | 
	
		
			
				|  |  | +        )
 | 
	
		
			
				|  |  | +        data = self.encrypt_key_file(msgpack.packb(key.as_dict()), passphrase)
 | 
	
		
			
				|  |  |          key_data = '\n'.join(textwrap.wrap(b2a_base64(data).decode('ascii')))
 | 
	
		
			
				|  |  |          return key_data
 | 
	
		
			
				|  |  |  
 |