예제 #1
0
def pin_stuff(submethod, buf_io):
    from pincodes import (PIN_ATTEMPT_SIZE, PIN_ATTEMPT_FMT, PA_ZERO_SECRET,
                        PA_SUCCESSFUL, PA_IS_BLANK, PA_HAS_DURESS, PA_HAS_BRICKME,
                        CHANGE_WALLET_PIN, CHANGE_DURESS_PIN, CHANGE_BRICKME_PIN,
                        CHANGE_SECRET, CHANGE_DURESS_SECRET, CHANGE_SECONDARY_WALLET_PIN )

    if len(buf_io) != PIN_ATTEMPT_SIZE: return ERANGE

    global SECRETS

    (magic, is_secondary,
            pin, pin_len,
            delay_achieved,
            delay_required,
            num_fails,
            attempt_target,
            state_flags,
            private_state,
            hmac,
            change_flags,
            old_pin, old_pin_len,
            new_pin, new_pin_len,
            secret) = ustruct.unpack_from(PIN_ATTEMPT_FMT, buf_io)

    # NOTE: using strings here, not bytes; real bootrom uses bytes
    pin = pin[0:pin_len].decode()
    old_pin = old_pin[0:old_pin_len].decode()
    new_pin = new_pin[0:new_pin_len].decode()

    kk = '_pin1' if not is_secondary else '_pin2'

    if submethod == 0:
        # setup
        state_flags = (PA_SUCCESSFUL | PA_IS_BLANK) if not SECRETS.get(kk, False) else 0

        if pin.startswith('77'):
            delay_required = int(pin.split('-')[1]) * 2
            num_fails = 3

    elif submethod == 1:
        # delay
        time.sleep(0.500)
        delay_achieved += 1

    elif submethod == 2:
        # Login

        expect = SECRETS.get(kk, '')
        if pin == expect:
            state_flags = PA_SUCCESSFUL

            ts = a2b_hex(SECRETS.get(kk+'_secret', '00'*72))

        elif pin == SECRETS.get(kk + '_duress', None):
            state_flags = PA_SUCCESSFUL

            ts = a2b_hex(SECRETS.get(kk+'_duress_secret', '00'*72))

        else:
            state_flags = 0
            return EPIN_AUTH_FAIL

        time.sleep(0.5)

        if ts == b'\0'*72:
            state_flags |= PA_ZERO_SECRET

        del ts

    elif submethod == 3:
        # CHANGE pin and/or wallet secrets

        cf = change_flags;

        # NOTE: this logic copied from real deal

        # must be here to do something.
        if cf == 0: return EPIN_RANGE_ERR;

        if cf & CHANGE_BRICKME_PIN:
            if is_secondary:
                # only main PIN holder can define brickme PIN
                return EPIN_PRIMARY_ONLY
            if cf != CHANGE_BRICKME_PIN:
                # only pin can be changed, nothing else.
                return EPIN_BAD_REQUEST

        if (cf & CHANGE_DURESS_SECRET) and (cf & CHANGE_SECRET):
            # can't change two secrets at once.
            return EPIN_BAD_REQUEST

        if (cf & CHANGE_SECONDARY_WALLET_PIN):
            if is_secondary:
                # only main user uses this call 
                return EPIN_BAD_REQUEST;

            if (cf != CHANGE_SECONDARY_WALLET_PIN):
                # only changing PIN, no secret-setting
                return EPIN_BAD_REQUEST;

            kk = '_pin2'

        
        # what PIN will we change
        pk = None
        if change_flags & (CHANGE_WALLET_PIN | CHANGE_SECONDARY_WALLET_PIN):
            pk = kk
        if change_flags & CHANGE_DURESS_PIN:
            pk = kk+'_duress'
        if change_flags & CHANGE_BRICKME_PIN:
            pk = kk+'_brickme'

        if pin == SECRETS.get(kk + '_duress', None):
            # acting duress mode... let them only change duress pin
            if pk == kk:
                pk = kk+'_duress'
            else:
                return EPIN_OLD_AUTH_FAIL

        if pk != None:
            # Must match old pin correctly, if it is defined.
            if SECRETS.get(pk, '') != old_pin:
                print("secel: wrong OLD pin (expect %s, got %s)" % (SECRETS[pk], old_pin))
                return EPIN_OLD_AUTH_FAIL

            # make change
            SECRETS[pk] = new_pin

        if change_flags & CHANGE_SECRET:
            SECRETS[kk+'_secret'] = str(b2a_hex(secret), 'ascii')
        if change_flags & CHANGE_DURESS_SECRET:
            SECRETS[kk+'_duress_secret'] = str(b2a_hex(secret), 'ascii')

        time.sleep(1.5)

    elif submethod == 4:
        # Fetch secrets
        duress_pin = SECRETS.get(kk+'_duress')

        if pin == duress_pin:
            secret = a2b_hex(SECRETS.get(kk+'_duress_secret', '00'*72))
        else:
            # main/sec secrets
            expect = SECRETS.get(kk, '')
            if pin == expect:
                secret = a2b_hex(SECRETS.get(kk+'_secret', '00'*72))
            else:
                return EPIN_AUTH_FAIL

    elif submethod == 5:
        # greenlight firmware
        from ckcc import genuine_led, led_pipe
        genuine_led = True
        led_pipe.write(b'\x01')


    hmac = b'69'*16

    ustruct.pack_into(PIN_ATTEMPT_FMT, buf_io, 0, magic, is_secondary,
            pin.encode(), pin_len, delay_achieved, delay_required,
            num_fails, attempt_target,
            state_flags, private_state, hmac,
            change_flags, old_pin.encode(), old_pin_len, new_pin.encode(), new_pin_len, secret)

    return 0
예제 #2
0
def pin_stuff(submethod, buf_io):
    from pincodes import (PIN_ATTEMPT_SIZE, PIN_ATTEMPT_FMT_V1, PA_ZERO_SECRET,
                        PIN_ATTEMPT_SIZE_V1, CHANGE_LS_OFFSET,
                        PA_SUCCESSFUL, PA_IS_BLANK, PA_HAS_DURESS, PA_HAS_BRICKME,
                        CHANGE_WALLET_PIN, CHANGE_DURESS_PIN, CHANGE_BRICKME_PIN,
                        CHANGE_SECRET, CHANGE_DURESS_SECRET, CHANGE_SECONDARY_WALLET_PIN )

    if len(buf_io) != (PIN_ATTEMPT_SIZE if version.has_608 else PIN_ATTEMPT_SIZE_V1):
        return ERANGE

    global SECRETS

    (magic, is_secondary,
            pin, pin_len,
            delay_achieved,
            delay_required,
            num_fails,
            attempts_left,
            state_flags,
            private_state,
            hmac,
            change_flags,
            old_pin, old_pin_len,
            new_pin, new_pin_len,
            secret) = ustruct.unpack_from(PIN_ATTEMPT_FMT_V1, buf_io)

    # NOTE: ignoring mk2 additions for now, we have no need for it.

    # NOTE: using strings here, not bytes; real bootrom uses bytes
    pin = pin[0:pin_len].decode()
    old_pin = old_pin[0:old_pin_len].decode()
    new_pin = new_pin[0:new_pin_len].decode()

    kk = '_pin1' if not is_secondary else '_pin2'

    if submethod == 0:
        # setup
        state_flags = (PA_SUCCESSFUL | PA_IS_BLANK) if not SECRETS.get(kk, False) else 0

        if pin.startswith('77'):
            delay_required = int(pin.split('-')[1]) * 2
            num_fails = 3
        if pin.startswith('88'):
            attempts_left = 2
            num_fails = 11

        if version.has_608:
            attempts_left = 13
            num_fails = 0
            if 0:       # XXX  test
                num_fails = 10
                attempts_left = 3

    elif submethod == 1:
        # delay
        time.sleep(0.05)
        delay_achieved += 1

    elif submethod == 2:
        # Login

        expect = SECRETS.get(kk, '')
        if pin == expect:
            state_flags = PA_SUCCESSFUL

            ts = a2b_hex(SECRETS.get(kk+'_secret', '00'*72))

        elif pin == SECRETS.get(kk + '_duress', None):
            state_flags = PA_SUCCESSFUL

            ts = a2b_hex(SECRETS.get(kk+'_duress_secret', '00'*72))

        else:
            if version.has_608:
                num_fails += 1
                attempts_left -= 1
            else:
                state_flags = 0

            return EPIN_AUTH_FAIL

        time.sleep(0.05)

        if ts == b'\0'*72:
            state_flags |= PA_ZERO_SECRET

        if kk+'_duress' in SECRETS:
            state_flags |= PA_HAS_DURESS
        if kk+'_brickme' in SECRETS:
            state_flags |= PA_HAS_BRICKME

        del ts

    elif submethod == 3:
        # CHANGE pin and/or wallet secrets

        cf = change_flags

        # NOTE: this logic copied from real deal

        # must be here to do something.
        if cf == 0: return EPIN_RANGE_ERR;

        if cf & CHANGE_BRICKME_PIN:
            if is_secondary:
                # only main PIN holder can define brickme PIN
                return EPIN_PRIMARY_ONLY
            if cf != CHANGE_BRICKME_PIN:
                # only pin can be changed, nothing else.
                return EPIN_BAD_REQUEST

        if (cf & CHANGE_DURESS_SECRET) and (cf & CHANGE_SECRET):
            # can't change two secrets at once.
            return EPIN_BAD_REQUEST

        if (cf & CHANGE_SECONDARY_WALLET_PIN):
            if is_secondary:
                # only main user uses this call 
                return EPIN_BAD_REQUEST;

            if (cf != CHANGE_SECONDARY_WALLET_PIN):
                # only changing PIN, no secret-setting
                return EPIN_BAD_REQUEST;

            kk = '_pin2'

        
        # what PIN will we change
        pk = None
        if change_flags & (CHANGE_WALLET_PIN | CHANGE_SECONDARY_WALLET_PIN):
            pk = kk
        if change_flags & CHANGE_DURESS_PIN:
            pk = kk+'_duress'
        if change_flags & CHANGE_BRICKME_PIN:
            pk = kk+'_brickme'

        if pin == SECRETS.get(kk + '_duress', None):
            # acting duress mode... let them only change duress pin
            if pk == kk:
                pk = kk+'_duress'
            else:
                return EPIN_OLD_AUTH_FAIL

        if pk != None:
            # Must match old pin correctly, if it is defined.
            if SECRETS.get(pk, '') != old_pin:
                print("secel: wrong OLD pin (expect %s, got %s)" % (SECRETS[pk], old_pin))
                return EPIN_OLD_AUTH_FAIL

            # make change
            SECRETS[pk] = new_pin

            if not new_pin and pk != kk:
                del SECRETS[pk]

        if change_flags & CHANGE_SECRET:
            SECRETS[kk+'_secret'] = str(b2a_hex(secret), 'ascii')
        if change_flags & CHANGE_DURESS_SECRET:
            SECRETS[kk+'_duress_secret'] = str(b2a_hex(secret), 'ascii')

        time.sleep(0.05)

    elif submethod == 4:
        # Fetch secrets
        duress_pin = SECRETS.get(kk+'_duress')

        secret = None

        if pin == duress_pin:
            secret = a2b_hex(SECRETS.get(kk+'_duress_secret', '00'*72))
        else:
            if change_flags & CHANGE_DURESS_SECRET:
                # wants the duress secret
                expect = SECRETS.get(kk, '')
                if pin == expect:
                    secret = a2b_hex(SECRETS.get(kk+'_duress_secret', '00'*72))
            else:
                # main/secondary secret
                expect = SECRETS.get(kk, '')
                if pin == expect:
                    secret = a2b_hex(SECRETS.get(kk+'_secret', '00'*72))

        if secret is None:
            return EPIN_AUTH_FAIL

    elif submethod == 5:
        # greenlight firmware
        from ckcc import genuine_led, led_pipe
        genuine_led = True
        led_pipe.write(b'\x01')

    elif submethod == 6:
        if not version.has_608:
            return ENOENT

        # long secret read/change.
        cf = change_flags
        assert CHANGE_LS_OFFSET == 0xf00
        blk = (cf >> 8) & 0xf
        if blk > 13: return EPIN_RANGE_ERR
        off = blk * 32

        if 'ls' not in SECRETS:
            SECRETS['ls'] = bytearray(416)

        if (cf & CHANGE_SECRET):
            SECRETS['ls'][off:off+32] = secret
        else:
            secret = SECRETS['ls'][off:off+32]

    else:
        # bogus submethod
        return ENOENT


    hmac = b'69'*16

    ustruct.pack_into(PIN_ATTEMPT_FMT_V1, buf_io, 0, magic, is_secondary,
            pin.encode(), pin_len, delay_achieved, delay_required,
            num_fails, attempts_left,
            state_flags, private_state, hmac,
            change_flags, old_pin.encode(), old_pin_len, new_pin.encode(), new_pin_len, secret)

    return 0
예제 #3
0
 def power(st=0):
     from ckcc import led_pipe
     led_pipe.write(bytes([0x20 | (0x2 if st else 0x0)]))
     if st:
         time.sleep(0.100)  # drama
     return False