Exemplo n.º 1
0
async def restore_complete_doit(fname_or_fd, words):
    from main import dis

    # build password
    password = '******'.join(words)

    # filename already picked, taste it and maybe consider using it's data.
    try:
        fd = open(fname_or_fd, 'rb') if isinstance(fname_or_fd, str) else fname_or_fd
    except:
        await ux_show_story('Unable to open backup file. \n\n' + str(fname_or_fd))
        return

    try:
        if not words:
            contents = fd.read()
        else:
            try:
                compat7z.check_file_headers(fd)
            except Exception as e:
                await ux_show_story('Unable to read backup file. Has it been touched?'
                                        '\n\nError: ' + str(e))
                return

            dis.fullscreen("Decrypting...")
            try:
                zz = compat7z.Builder()
                fname, contents = zz.read_file(fd, password, progress_fcn=dis.progress_bar_show)

                assert fname == 'ckcc-backup.txt', "Wrong filename in archive"

                # simple quick sanity check
                assert contents[0:1] == b'#' and contents[-1:] == b'\n', "Corrupted after decrypt"

            except Exception as e:
                # assume everything here is "password wrong" errors
                print("pw wrong?  %s" % e)

                await ux_show_story('Unable to decrypt backup file. Incorrect password?'
                                        '\n\nTried:\n\n' + password)

                return
    finally:
        fd.close()

    vals = {}
    for line in contents.decode().split('\n'):
        if not line: continue
        if line[0] == '#': continue

        try:
            k,v = line.split(' = ', 1)
            #print("%s = %s" % (k, v))

            vals[k] = ujson.loads(v)
        except:
            print("unable to decode line: %r" % line)
            # but keep going!

    await restore_from_dict(vals)
Exemplo n.º 2
0
async def verify_backup_file(fname_or_fd):
    # read 7z header, and measure checksums
    # - no password is wanted/required
    # - really just checking CRC32, but that's enough against truncated files
    from files import CardSlot, CardMissingError
    from actions import needs_microsd
    prob = None
    fd = None

    # filename already picked, open it.
    try:
        with CardSlot() as card:
            prob = 'Unable to open backup file.'
            fd = open(fname_or_fd, 'rb') if isinstance(fname_or_fd,
                                                       str) else fname_or_fd

            prob = 'Unable to read backup file headers. Might be truncated.'
            compat7z.check_file_headers(fd)

            prob = 'Unable to verify backup file contents.'
            zz = compat7z.Builder()
            files = zz.verify_file_crc(fd, MAX_BACKUP_FILE_SIZE)

            assert len(files) == 1
            fname, fsize = files[0]
            assert fname == 'ckcc-backup.txt'
            assert 400 < fsize < MAX_BACKUP_FILE_SIZE, 'size'

    except CardMissingError:
        await needs_microsd()
        return
    except Exception as e:
        await ux_show_story(prob + '\n\nError: ' + str(e))
        return
    finally:
        if fd:
            fd.close()

    await ux_show_story(
        "Backup file CRC checks out okay.\n\nPlease note this is only a check against accidental truncation and similar. Targeted modifications can still pass this test."
    )
Exemplo n.º 3
0
async def restore_complete_doit(fname_or_fd, words):
    # Open file, read it, maybe decrypt it; return string if any error
    # - some errors will be shown, None return in that case
    # - no return if successful (due to reboot)
    from main import dis
    from files import CardSlot, CardMissingError
    from actions import needs_microsd

    # build password
    password = '******'.join(words)

    prob = None

    try:
        with CardSlot() as card:
            # filename already picked, taste it and maybe consider using its data.
            try:
                fd = open(fname_or_fd, 'rb') if isinstance(
                    fname_or_fd, str) else fname_or_fd
            except:
                return 'Unable to open backup file.\n\n' + str(fname_or_fd)

            try:
                if not words:
                    contents = fd.read()
                else:
                    try:
                        compat7z.check_file_headers(fd)
                    except Exception as e:
                        return 'Unable to read backup file. Has it been touched?\n\nError: ' \
                                            + str(e)

                    dis.fullscreen("Decrypting...")
                    try:
                        zz = compat7z.Builder()
                        fname, contents = zz.read_file(
                            fd,
                            password,
                            MAX_BACKUP_FILE_SIZE,
                            progress_fcn=dis.progress_bar_show)

                        # simple quick sanity checks
                        assert fname == 'ckcc-backup.txt'
                        assert contents[0:1] == b'#' and contents[-1:] == b'\n'

                    except Exception as e:
                        # assume everything here is "password wrong" errors
                        #print("pw wrong?  %s" % e)

                        return (
                            'Unable to decrypt backup file. Incorrect password?'
                            '\n\nTried:\n\n' + password)
            finally:
                fd.close()
    except CardMissingError:
        await needs_microsd()
        return

    vals = {}
    for line in contents.decode().split('\n'):
        if not line: continue
        if line[0] == '#': continue

        try:
            k, v = line.split(' = ', 1)
            #print("%s = %s" % (k, v))

            vals[k] = ujson.loads(v)
        except:
            print("unable to decode line: %r" % line)
            # but keep going!

    # this leads to reboot if it works, else errors shown, etc.
    return await restore_from_dict(vals)
Exemplo n.º 4
0
async def write_complete_backup(words, fname_pattern, write_sflash):
    # Just do the writing
    from main import dis, pa, settings
    from files import CardSlot, CardMissingError

    # Show progress:
    dis.fullscreen('Encrypting...' if words else 'Generating...')
    body = render_backup_contents().encode()

    gc.collect()

    if words:
        # NOTE: Takes a few seconds to do the key-streching, but little actual
        # time to do the encryption.

        pw = ' '.join(words)
        zz = compat7z.Builder(password=pw, progress_fcn=dis.progress_bar_show)
        zz.add_data(body)

        hdr, footer = zz.save('ckcc-backup.txt')

        filesize = len(body) + MAX_BACKUP_FILE_SIZE

        del body

        gc.collect()
    else:
        # cleartext dump
        zz = None
        filesize = len(body) + 10

    if write_sflash:
        # for use over USB and unit testing: commit file into SPI flash
        from sffile import SFFile

        with SFFile(0, max_size=filesize, message='Saving...') as fd:
            await fd.erase()

            if zz:
                fd.write(hdr)
                fd.write(zz.body)
                fd.write(footer)
            else:
                fd.write(body)

            return fd.tell(), fd.checksum.digest()

    for copy in range(25):
        # choose a filename

        try:
            with CardSlot() as card:
                fname, nice = card.pick_filename(fname_pattern)

                # do actual write
                with open(fname, 'wb') as fd:
                    if zz:
                        fd.write(hdr)
                        fd.write(zz.body)
                        fd.write(footer)
                    else:
                        fd.write(body)

        except Exception as e:
            # includes CardMissingError
            import sys
            sys.print_exception(e)
            # catch any error
            ch = await ux_show_story(
                'Failed to write! Please insert formated MicroSD card, '
                'and press OK to try again.\n\nX to cancel.\n\n\n' + str(e))
            if ch == 'x': break
            continue

        if copy == 0:
            while 1:
                msg = '''Backup file written:\n\n%s\n\n\
To view or restore the file, you must have the full password.\n\n\
Insert another SD card and press 2 to make another copy.''' % (nice)

                ch = await ux_show_story(msg, escape='2')

                if ch == 'y': return
                if ch == '2': break

        else:
            ch = await ux_show_story('''File (#%d) written:\n\n%s\n\n\
Press OK for another copy, or press X to stop.''' % (copy + 1, nice),
                                     escape='2')
            if ch == 'x': break