summaryrefslogtreecommitdiff
path: root/src/leap/soledad/common/preamble.py
blob: 7254903122195a167559e0e589f702ef633ecb01 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
# -*- coding: utf-8 -*-
# preamble.py
# Copyright (C) 2017 LEAP Encryption Access Project
#
# 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 3 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, see <http://www.gnu.org/licenses/>.
"""
Preamble is a metadata payload present on encrypted documents. It holds data
about encryption scheme, iv, document id and sync related data.
   BLOB_SIGNATURE_MAGIC, -> used to differentiate from other data formats
   ENC_SCHEME, -> cryptographic scheme (symmetric or asymmetric)
   ENC_METHOD, -> cipher used, such as AES-GCM or AES-CTR or GPG
   current_time, -> time.time()
   self.iv, -> initialization vector if any, or 0 when not applicable
   str(self.doc_id), -> document id
   str(self.rev), -> current revision
   self._content_size) -> size, rounded to ceiling
"""
import warnings
import struct
import time
from collections import namedtuple
PACMAN = struct.Struct('2sbbQ16s255p255pQ')
LEGACY_PACMAN = struct.Struct('2sbbQ16s255p255p')  # DEPRECATED
BLOB_SIGNATURE_MAGIC = '\x13\x37'
ENC_SCHEME = namedtuple('SCHEME', 'symkey external')(1, 2)
ENC_METHOD = namedtuple('METHOD', 'aes_256_ctr aes_256_gcm pgp')(1, 2, 3)


class InvalidPreambleException(Exception):
    pass


class Preamble:

    def __init__(self, doc_id, rev, scheme, method,
                 timestamp=0, iv='', magic=None, content_size=0):
        self.doc_id = doc_id
        self.rev = rev
        self.scheme = scheme
        self.method = method
        self.iv = iv
        self.timestamp = int(timestamp) or int(time.time())
        self.magic = magic or BLOB_SIGNATURE_MAGIC
        self.content_size = int(content_size)

    def encode(self):
        preamble = PACMAN.pack(
            self.magic,
            self.scheme,
            self.method,
            self.timestamp,
            self.iv,
            str(self.doc_id),
            str(self.rev),
            self.content_size)
        return preamble


def decode_preamble(encoded_preamble):
    preamble_size = len(encoded_preamble)
    try:
        if preamble_size == LEGACY_PACMAN.size:
            unpacked_data = LEGACY_PACMAN.unpack(encoded_preamble)
            magic, sch, meth, ts, iv, doc_id, rev = unpacked_data
            warnings.warn("Decoding a legacy preamble without size. " +
                          "This will be deprecated in 0.12. Doc was: " +
                          "doc_id: %s rev: %s" % (doc_id, rev), Warning)
            return Preamble(doc_id, rev, sch, meth, ts, iv, magic)
        elif preamble_size == PACMAN.size:
            unpacked_data = PACMAN.unpack(encoded_preamble)
            magic, sch, meth, ts, iv, doc_id, rev, size = unpacked_data
            return Preamble(doc_id, rev, sch, meth, ts, iv, magic, int(size))
        else:
            raise InvalidPreambleException("Unexpected preamble size %d",
                                           preamble_size)
    except struct.error as e:
        raise InvalidPreambleException(e)