Пример #1
0
def sign_data(data):
    with NamedTemporaryFile() as data_file, \
            NamedTemporaryFile() as signature_file:
        data_file.write(data)
        data_file.flush()
        gpg_command('--detach-sig', '-o', signature_file.name, data_file.name)
        return signature_file.read()
Пример #2
0
def decrypt_iterator(document_iterator,
                     document_keys=None,
                     selectors=None,
                     full_documents=False):
    """Decrypt documents and return decrypted data

    `selectors` defaults to `get_selectors()`

    Each yield is a tuple of a document containing the document keys, a
    document containing the decrypted data in the same structure as the
    original document, and a list of tuples of selectors that were decrypted
    and the decrypted datum for each selector. I.e., the same decrypted data is
    returned in two different forms, to simplify the use of the data by the
    caller.

    if `full_documents` is true, then the document in each yield will be the
    full document returned by the iterator with encrypted fields replaced,
    rather than a document containing just the decrypted fields.

    Assumes that the secret key has already been combined _before_ this
    function is called.

    Note: There is a yield for every document that is returned by
    document_iterator, even if nothing was decrypted. If that's the case, then
    the document keys in the yield will be populated, but the decrypted data
    document and list will be empty.
    """
    if selectors is None:
        selectors = get_selectors()
    for doc in document_iterator:
        document_keys = {k: doc[k] for k in document_keys or ()}
        output_dict = doc if full_documents else {}
        output_tuples = []
        for s in selectors:
            encrypted_data = get_setting(doc, s.enc_mem, check_defaults=False)
            if not encrypted_data:
                continue
            with NamedTemporaryFile('w+b') as unencrypted_file, \
                    NamedTemporaryFile('w+b') as encrypted_file:
                encrypted_file.write(b64decode(encrypted_data['data']))
                encrypted_file.flush()
                gpg_command('--decrypt', '-o', unencrypted_file.name,
                            encrypted_file.name)
                unencrypted_file.seek(0)
                unencrypted_data = json.loads(
                    unencrypted_file.read().decode('utf-8'))
            # Order is important here. We unset before we set because the
            # selector key may be the same for both unencrypted and encrypted
            # fields.
            if full_documents:
                set_setting(output_dict, s.enc_mem, None)
            set_setting(output_dict, s.plain_mem, unencrypted_data)
            output_tuples.append((s, unencrypted_data))
        yield (document_keys, output_dict if output_tuples else {},
               output_tuples)
Пример #3
0
def generate_key(mode, user_id):
    set_gpg(mode)
    try:
        gpg_command('--list-keys', user_id, with_trustdb=True)
    except Exception:
        entropy_warning()
        try:
            gpg_command('--passphrase',
                        '',
                        '--quick-gen-key',
                        user_id,
                        with_trustdb=True)
        except subprocess.CalledProcessError as e:
            sys.exit('PenguinDome requires GnuPG version 2.1 or newer. '
                     'gpg output:\n{}'.format(e.output))
Пример #4
0
def combine_secret_key():
    key_name = get_server_setting('secret_keeping:key_name')
    split_dir = os.path.join(var_dir, key_name)
    key_file = os.path.join(split_dir, 'private_key.asc')
    os.makedirs(split_dir, exist_ok=True)
    split_files = [f for f in os.listdir(split_dir) if re.search(r'\.\d', f)]
    combine_threshold = get_server_setting('secret_keeping:combine_threshold')
    if len(split_files) < combine_threshold:
        input('Put at least {} of the secret-keeper files into\n{}.\n'
              'Hit Enter when done: '.format(combine_threshold, split_dir))
        split_files = [
            f for f in os.listdir(split_dir) if re.search(r'\.\d', f)
        ]
        if len(split_files) < combine_threshold:
            sys.exit('Too few secret-keeper files provided.')
    cmd = ['gfcombine', '-o', key_file]
    cmd.extend(os.path.join(split_dir, f) for f in split_files)
    subprocess.check_output(cmd, stderr=subprocess.STDOUT)
    gpg_command('--import', key_file)
    subprocess.check_output(('shred', '-u', key_file))
Пример #5
0
 def wrapper(*args, **kwargs):
     try:
         data = request.form['data']
     except:
         remote_addr = getattr(request, 'remote_addr', None)
         # Probably just somebody port-scanning us, not worth logging as
         # an error and making people waste time investigating.
         if remote_addr:
             log.info('Ignoring empty request from {}'.format(
                 remote_addr))
         else:
             log.info('Ignoring empty request with no remote address')
         return('error')
     try:
         signature = request.form['signature']
     except:
         # We're not logging the data here because it may contain
         # sensitive info that should not be logged. Perhaps I am being
         # too paranoid about this. ¯\_(ツ)_/¯
         hostname = getattr(data, 'hostname', None)
         remote_addr = getattr(request, 'remote_addr', None)
         log.error('Ignoring malformed request (no signature) from '
                   'host {}, addr {}'.format(hostname, remote_addr))
         return('error')
     with tempfile.NamedTemporaryFile('w+') as data_file, \
             tempfile.NamedTemporaryFile('w+') as signature_file:
         data_file.write(data)
         data_file.flush()
         signature_file.write(signature)
         signature_file.flush()
         try:
             gpg_command('--verify', signature_file.name,
                         data_file.name)
         except:
             hostname = getattr(data, 'hostname', None)
             remote_addr = getattr(request, 'remote_addr', None)
             log.error('Ignoring malformed request (bad signature) from '
                       'host {}, addr {}'.format(hostname, remote_addr))
             return('error')
     return f(*args, **kwargs)
Пример #6
0
def import_key(to_mode, user_id):
    from_mode = 'client' if to_mode == 'server' else 'server'
    set_gpg(to_mode)
    try:
        gpg_command('--list-keys', user_id)
    except Exception:
        with NamedTemporaryFile() as key_file:
            set_gpg(from_mode)
            gpg_command('--export', '-o', key_file.name, user_id)
            set_gpg(to_mode)
            gpg_command('--import', key_file.name)
Пример #7
0
def delete_secret_key():
    key_fingerprint = get_server_setting('secret_keeping:key_fingerprint')
    gpg_command('--delete-secret-keys', key_fingerprint)
    print("\nDon't forget to 'shred -u' the secret-keeper files!\n")
    log.warn("Don't forget to 'shred -u' the secret-keeper files!")
Пример #8
0
def enable_handler(args):
    if get_server_setting('secret_keeping:enabled'):
        sys.exit('Secret-keeping is already enabled.')

    if get_server_setting('secret_keeping:key_id'):
        if not (args.replace or args.preserve):
            sys.exit('Must specify --replace or --preserve.')
    else:
        args.replace = True

    args.shares = args.shares or \
        get_server_setting('secret_keeping:num_shares')
    if args.shares < 2:
        sys.exit('--num-shares must be at least 2.')

    args.combine_threshold = args.combine_threshold or \
        get_server_setting('secret_keeping:combine_threshold')
    if args.combine_threshold < 2:
        sys.exit('--combine-threshold must be at least 2.')
    if args.combine_threshold > args.shares:
        sys.exit(
            '--combine-threshold must be less than {}.'.format(args.shares +
                                                               1))

    if args.replace:
        key_name = 'penguindome-secret-keeping-' + uuid.uuid4().hex
        output = gpg_command('--passphrase',
                             '',
                             '--quick-gen-key',
                             key_name,
                             with_trustdb=True,
                             quiet=False)
        match = re.search(r'key (.*) marked as ultimately trusted', output)
        key_id = match.group(1)
        match = re.search(r'/([0-9A-F]+)\.rev', output)
        key_fingerprint = match.group(1)

        split_dir = os.path.join(var_dir, key_name)
        key_file = os.path.join(split_dir, 'private_key.asc')
        os.makedirs(split_dir)
        gpg_command('--export-secret-key', '--armor', '-o', key_file, key_id)
        subprocess.check_output(('gfsplit', '-n', str(
            args.combine_threshold), '-m', str(args.shares), key_file),
                                stderr=subprocess.STDOUT)
        try:
            gpg_command('--delete-secret-keys', key_fingerprint)
        except subprocess.CalledProcessError as e:
            sys.exit('Failed to delete secret key:\n{}'.format(
                e.output.decode('utf8')))
        subprocess.check_output(('shred', '-u', key_file),
                                stderr=subprocess.STDOUT)

        with NamedTemporaryFile() as public_key_file:
            gpg_command('--export', '-o', public_key_file.name, key_id)
            set_gpg('client')
            try:
                gpg_command('--import', public_key_file.name)
            finally:
                set_gpg('server')

        set_server_setting('secret_keeping:key_name', key_name)
        set_server_setting('secret_keeping:key_id', key_id)
        set_server_setting('secret_keeping:key_fingerprint', key_fingerprint)

        set_client_setting('secret_keeping:key_id', key_id)

    set_server_setting('secret_keeping:num_shares', args.shares)
    set_server_setting('secret_keeping:combine_threshold',
                       args.combine_threshold)
    set_server_setting('secret_keeping:enabled', True)
    save_server_settings()

    set_client_setting('secret_keeping:enabled', True)
    save_client_settings()

    print(
        distribute_secrets_note.format(m=args.shares,
                                       n=args.combine_threshold,
                                       split_dir=split_dir))
    print(restart_note)

    log.info('Enabled secret-keeping')
Пример #9
0
    os.makedirs(releases_dir, exist_ok=True)
    with NamedTemporaryFile('w+') as file_list, \
            NamedTemporaryFile('w+') as staging_file_list:
        release_files = release_files_iter(with_signatures=True)
        release_files = tuple(chain.from_iterable(release_files))
        file_list.write('\n'.join(release_files) + '\n')
        file_list.flush()

        staging_files = release_files_iter(with_signatures=True,
                                           top_dir=staging_dir)
        staging_files = list(chain.from_iterable(staging_files))

        if not any(f.startswith(commands_dir) for f in release_files):
            os.makedirs(os.path.join(staging_dir, commands_dir))
            staging_files.append(commands_dir)

        staging_file_list.write('\n'.join(staging_files) + '\n')
        staging_file_list.flush()

        tar_file = os.path.join(releases_dir, '{}.tar'.format(release))
        subprocess.check_output(('tar', '--create', '--file', tar_file,
                                 '--files-from', file_list.name,
                                 '--directory', staging_dir,
                                 '--files-from', staging_file_list.name))
    gpg_command('--armor', '--sign', tar_file)
    assert os.path.exists(tar_file + '.asc')
finally:
    shutil.rmtree(staging_dir)

print('Built release {}'.format(release))
Пример #10
0
def sign_file(file, top_dir=top_dir):
    signature_file = os.path.join(top_dir, signatures_dir, file + '.sig')
    file = os.path.join(top_dir, file)
    os.makedirs(os.path.dirname(signature_file), exist_ok=True)
    gpg_command('--detach-sig', '-o', signature_file, file)
    return signature_file[len(top_dir) + 1:]