1
0
Fork 0
mirror of https://github.com/borgbackup/borg.git synced 2024-12-25 09:19:31 +00:00

Verify cindex integrity on archive open

This commit is contained in:
Jonas Borgström 2010-10-24 20:18:18 +02:00
parent 480562570f
commit f5f8065dd3
3 changed files with 38 additions and 24 deletions

View file

@ -2,6 +2,7 @@
import logging import logging
import msgpack import msgpack
import os import os
import socket
import stat import stat
import sys import sys
@ -26,34 +27,40 @@ def __init__(self, store, crypto, name=None):
def load(self, id): def load(self, id):
self.id = id self.id = id
archive = msgpack.unpackb(self.crypto.decrypt(self.store.get(NS_ARCHIVES, self.id))) data, hash = self.crypto.decrypt(self.store.get(NS_ARCHIVES, self.id))
archive = msgpack.unpackb(data)
if archive['version'] != 1: if archive['version'] != 1:
raise Exception('Archive version %r not supported' % archive['version']) raise Exception('Archive version %r not supported' % archive['version'])
self.items = archive['items'] self.items = archive['items']
self.name = archive['name'] self.name = archive['name']
cindex = msgpack.unpackb(self.crypto.decrypt(self.store.get(NS_CINDEX, self.id))) data, hash = self.crypto.decrypt(self.store.get(NS_CINDEX, self.id))
cindex = msgpack.unpackb(data)
assert cindex['version'] == 1 assert cindex['version'] == 1
if archive['cindex'] != hash:
raise Exception('decryption failed')
self.chunks = cindex['chunks'] self.chunks = cindex['chunks']
for i, chunk in enumerate(self.chunks): for i, chunk in enumerate(self.chunks):
self.chunk_idx[i] = chunk[0] self.chunk_idx[i] = chunk[0]
def save(self, name): def save(self, name):
self.id = self.crypto.id_hash(name) self.id = self.crypto.id_hash(name)
archive = {
'version': 1,
'name': name,
'cmdline': sys.argv,
'ts': datetime.utcnow().isoformat(),
'items': self.items,
}
data = self.crypto.encrypt_read(msgpack.packb(archive))
self.store.put(NS_ARCHIVES, self.id, data)
cindex = { cindex = {
'version': 1, 'version': 1,
'chunks': self.chunks, 'chunks': self.chunks,
} }
data = self.crypto.encrypt_create(msgpack.packb(cindex)) data, cindex_hash = self.crypto.encrypt_create(msgpack.packb(cindex))
self.store.put(NS_CINDEX, self.id, data) self.store.put(NS_CINDEX, self.id, data)
archive = {
'version': 1,
'name': name,
'cindex': cindex_hash,
'cmdline': sys.argv,
'hostname': socket.gethostname(),
'ts': datetime.utcnow().isoformat(),
'items': self.items,
}
data, hash = self.crypto.encrypt_read(msgpack.packb(archive))
self.store.put(NS_ARCHIVES, self.id, data)
self.store.commit() self.store.commit()
def add_chunk(self, id, size): def add_chunk(self, id, size):
@ -117,7 +124,10 @@ def extract(self, dest=None):
for chunk in item['chunks']: for chunk in item['chunks']:
id = self.chunk_idx[chunk] id = self.chunk_idx[chunk]
try: try:
fd.write(self.crypto.decrypt(self.store.get(NS_CHUNKS, id))) data, hash = self.crypto.decrypt(self.store.get(NS_CHUNKS, id))
if self.crypto.id_hash(data) != id:
raise IntegrityError('chunk id did not match')
fd.write(data)
except ValueError: except ValueError:
raise Exception('Invalid chunk checksum') raise Exception('Invalid chunk checksum')
self.restore_stat(path, item) self.restore_stat(path, item)
@ -145,7 +155,9 @@ def verify(self):
for chunk in item['chunks']: for chunk in item['chunks']:
id = self.chunk_idx[chunk] id = self.chunk_idx[chunk]
try: try:
self.crypto.decrypt(self.store.get(NS_CHUNKS, id)) data, hash = self.crypto.decrypt(self.store.get(NS_CHUNKS, id))
if self.crypto.id_hash(data) != id:
raise IntegrityError('chunk id did not match')
except IntegrityError: except IntegrityError:
logging.error('%s ... ERROR', item['path']) logging.error('%s ... ERROR', item['path'])
break break

View file

@ -42,7 +42,8 @@ def init(self, crypto):
if self.store.tid == 0: if self.store.tid == 0:
return return
for id in list(self.store.list(NS_CINDEX)): for id in list(self.store.list(NS_CINDEX)):
cindex = msgpack.unpackb(crypto.decrypt(self.store.get(NS_CINDEX, id))) data, hash = crypto.decrypt(self.store.get(NS_CINDEX, id))
cindex = msgpack.unpackb(data)
for id, size in cindex['chunks']: for id, size in cindex['chunks']:
try: try:
count, size = self.chunkmap[id] count, size = self.chunkmap[id]
@ -68,7 +69,7 @@ def save(self):
def add_chunk(self, id, data, crypto): def add_chunk(self, id, data, crypto):
if self.seen_chunk(id): if self.seen_chunk(id):
return self.chunk_incref(id) return self.chunk_incref(id)
data = crypto.encrypt_read(data) data, hash = crypto.encrypt_read(data)
csize = len(data) csize = len(data)
self.store.put(NS_CHUNKS, id, data) self.store.put(NS_CHUNKS, id, data)
self.chunkmap[id] = (1, csize) self.chunkmap[id] = (1, csize)

View file

@ -68,17 +68,17 @@ def id_hash(self, data):
def encrypt_read(self, data): def encrypt_read(self, data):
data = zlib.compress(data) data = zlib.compress(data)
hash = SHA256.new(data).digest() hash = self.id_hash(data)
counter = Counter.new(128, initial_value=bytes_to_long(hash[:16]), allow_wraparound=True) counter = Counter.new(128, initial_value=bytes_to_long(hash[:16]), allow_wraparound=True)
data = AES.new(self.read_key, AES.MODE_CTR, '', counter=counter).encrypt(data) data = AES.new(self.read_key, AES.MODE_CTR, '', counter=counter).encrypt(data)
return ''.join((self.READ, self.read_encrypted, hash, data)) return ''.join((self.READ, self.read_encrypted, hash, data)), hash
def encrypt_create(self, data): def encrypt_create(self, data):
data = zlib.compress(data) data = zlib.compress(data)
hash = SHA256.new(data).digest() hash = self.id_hash(data)
counter = Counter.new(128, initial_value=bytes_to_long(hash[:16]), allow_wraparound=True) counter = Counter.new(128, initial_value=bytes_to_long(hash[:16]), allow_wraparound=True)
data = AES.new(self.create_key, AES.MODE_CTR, '', counter=counter).encrypt(data) data = AES.new(self.create_key, AES.MODE_CTR, '', counter=counter).encrypt(data)
return ''.join((self.CREATE, self.create_encrypted, hash, data)) return ''.join((self.CREATE, self.create_encrypted, hash, data)), hash
def decrypt_key(self, data, rsa_key): def decrypt_key(self, data, rsa_key):
try: try:
@ -94,16 +94,17 @@ def decrypt(self, data):
hash = data[257:289] hash = data[257:289]
counter = Counter.new(128, initial_value=bytes_to_long(hash[:16]), allow_wraparound=True) counter = Counter.new(128, initial_value=bytes_to_long(hash[:16]), allow_wraparound=True)
data = AES.new(key, AES.MODE_CTR, counter=counter).decrypt(data[289:]) data = AES.new(key, AES.MODE_CTR, counter=counter).decrypt(data[289:])
if SHA256.new(data).digest() != hash: if self.id_hash(data) != hash:
raise IntegrityError('decryption failed') raise IntegrityError('decryption failed')
return zlib.decompress(data) return zlib.decompress(data), hash
elif type == self.CREATE: elif type == self.CREATE:
key = self.decrypt_key(data[1:257], self.keychain.rsa_create) key = self.decrypt_key(data[1:257], self.keychain.rsa_create)
hash = data[257:289] hash = data[257:289]
counter = Counter.new(128, initial_value=bytes_to_long(hash[:16]), allow_wraparound=True) counter = Counter.new(128, initial_value=bytes_to_long(hash[:16]), allow_wraparound=True)
data = AES.new(key, AES.MODE_CTR, '', counter=counter).decrypt(data[289:]) data = AES.new(key, AES.MODE_CTR, '', counter=counter).decrypt(data[289:])
if SHA256.new(data).digest() != hash: if self.id_hash(data) != hash:
raise IntegrityError('decryption failed') raise IntegrityError('decryption failed')
return zlib.decompress(data) return zlib.decompress(data), hash
else: else:
raise Exception('Unknown pack type %d found' % ord(type)) raise Exception('Unknown pack type %d found' % ord(type))