예제 #1
0
 def parse_data(self, data):
     """Parse data stored on the card"""
     # smartcard id to understand it's our data
     fingerprint = tagged_hash("scid", self.secret)[:4]
     l = len(self.MAGIC) + 4
     prefix = data[:l + 1]
     encrypted = True
     if prefix == bytes([l]) + self.MAGIC + fingerprint:
         # smartcard encryption key
         key = tagged_hash("scenc", self.secret)
     elif prefix == bytes([l]) + self.MAGIC + b"\x00" * 4:
         key = b"\xcc" * 32
         encrypted = False
     else:
         raise KeyStoreError(
             "Looks like stored data is created on a different device.")
     adata, plaintext = aead_decrypt(data, key=key)
     s = BytesIO(plaintext)
     o = {}
     while True:
         k = s.read(1)
         if len(k) == 0:
             break
         l = s.read(1)[0]
         v = s.read(l)
         assert len(v) == l
         if k in self.KEYS:
             o[self.KEYS[k]] = v
     return o, encrypted
예제 #2
0
 def unlock(self, pin):
     """
     Unlock the keystore, raises PinError if PIN is invalid.
     Raises CriticalErrorWipeImmediately if no attempts left.
     """
     # decrease the counter
     self._pin_attempts_left -= 1
     self.save_state()
     # check we have attempts
     if self._pin_attempts_left <= 0:
         self.wipe(self.path)
         raise CriticalErrorWipeImmediately("No more PIN attempts!\nWipe!")
     # calculate hmac with entered PIN
     key = tagged_hash("pin", self.secret)
     pin_hmac = hmac.new(key=key, msg=pin.encode(),
                         digestmod="sha256").digest()
     # check hmac is the same
     if pin_hmac != self.pin:
         raise PinError("Invalid PIN!\n%d of %d attempts left..." %
                        (self._pin_attempts_left, self._pin_attempts_max))
     self._pin_attempts_left = self._pin_attempts_max
     self._is_locked = False
     self.save_state()
     # derive PIN keys for reckless storage
     self.pin_secret = tagged_hash("pin", self.secret + pin.encode())
     self.load_enc_secret()
예제 #3
0
 def serialize_data(self, obj):
     """Serialize secrets for storage on the card"""
     r = b""
     for k in self.KEYS:
         v = self.KEYS[k]
         if v in obj:
             r += k + bytes([len(obj[v])]) + obj[v]
     # smartcard encryption key
     key = tagged_hash("scenc", self.secret)
     # smartcard id to understand it's our data
     fingerprint = tagged_hash("scid", self.secret)[:4]
     res = aead_encrypt(key, self.MAGIC + fingerprint, r)
     print(res)
     return res
예제 #4
0
 def set_pin(self, pin):
     """Saves hmac of the PIN code for verification later"""
     # set up pin
     key = tagged_hash("pin", self.secret)
     self.pin = hmac.new(key=key, msg=pin, digestmod="sha256").digest()
     self.pin_secret = tagged_hash("pin", self.secret + pin.encode())
     self.save_state()
     # update encryption secret
     if self.enc_secret is None:
         self.enc_secret = get_random_bytes(32)
     self.save_aead(self.path + "/enc_secret",
                    plaintext=self.enc_secret,
                    key=self.pin_secret)
     # call unlock now
     self.unlock(pin)
예제 #5
0
 def serialize_data(self, obj, encrypt=True):
     """Serialize secrets for storage on the card"""
     r = b""
     for k in self.KEYS:
         v = self.KEYS[k]
         if v in obj:
             r += k + bytes([len(obj[v])]) + obj[v]
     if encrypt:
         # smartcard encryption key
         key = tagged_hash("scenc", self.secret)
         # smartcard id to understand it's our data
         fingerprint = tagged_hash("scid", self.secret)[:4]
         res = aead_encrypt(key, self.MAGIC + fingerprint, r)
     else:
         # "unencrypted" data
         fingerprint = b"\x00" * 4
         res = aead_encrypt(b"\xcc" * 32, self.MAGIC + fingerprint, r)
     return res
예제 #6
0
 def get_auth_word(self, pin_part):
     """
     Get anti-phishing word to check internal secret
     from part of the PIN so user can stop when he sees wrong words
     """
     key = tagged_hash("auth", self.secret)
     h = hmac.new(key, pin_part, digestmod="sha256").digest()
     # wordlist is 2048 long (11 bits) so
     # this modulo doesn't create an offset
     word_number = int.from_bytes(h[:2], 'big') % len(bip39.WORDLIST)
     return bip39.WORDLIST[word_number]
예제 #7
0
 def is_locked(self):
     """
     Override this method!!!
     with your locking check function
     """
     # hack: we don't support PIN but
     # we need enc_secret, so let's do it here.
     # DONT USE THIS IF YOU HAVE PIN SUPPORT!
     if self.enc_secret is None:
         self.enc_secret = tagged_hash("enc", self.secret)
     return False
예제 #8
0
 def get_auth_word(self, pin_part):
     """
     Get anti-phishing word to check
     integrity of the device and the card.
     Internal secret, verified card's pubkey and PIN part
     are used to generate the words.
     The user should stop entering the PIN if he sees wrong words.
     It can happen if the device or the card is different.
     """
     # check if secure channel is already open
     # so the card can't lie about it's pubkey
     if not self.applet.is_secure_channel_open:
         raise KeyStoreError("Secure channel is closed.")
     # use both internal secret and card's key to generate
     # anti-phishing words
     key = tagged_hash("auth", self.secret + self.applet.card_pubkey)
     h = hmac.new(key, pin_part, digestmod="sha256").digest()
     # wordlist is 2048 long (11 bits) so
     # this modulo doesn't create an offset
     word_number = int.from_bytes(h[:2], "big") % len(bip39.WORDLIST)
     return bip39.WORDLIST[word_number]
예제 #9
0
    def _unlock(self, pin):
        """
        Implement this.
        Unlock the keystore, raises PinError if PIN is invalid.
        Raises CriticalErrorWipeImmediately if no attempts left.
        """
        # check we have attempts
        if self.pin_attempts_left <= 0:
            # wipe is happening automatically on this error
            raise CriticalErrorWipeImmediately("No more PIN attempts!\nWipe!")
        # check PIN code somehow, raise PinError if it's incorrect
        # for reference - first decrease PIN counter, then check PIN
        # raise PIN Error if it's invalid like this:
        # if pin == "INVALID PIN":
        #     raise PinError("Invalid PIN!\n%d of %d attempts left..." % (
        #         self._pin_attempts_left, self._pin_attempts_max)
        #     )
        # reset PIN counter here and unlock

        # set encryption secret somehow mb save it
        # don't use this approach, it's just for reference
        self.enc_secret = tagged_hash("enc", self.secret)
예제 #10
0
 def app_secret(self, app):
     return tagged_hash(app, self.secret)
예제 #11
0
 def settings_key(self):
     return tagged_hash("settings key", self.secret)
예제 #12
0
 def hexid(self):
     return hexlify(
         tagged_hash("smartcard/pubkey",
                     self.applet.card_pubkey)[:4]).decode()
예제 #13
0
 def userkey(self):
     if self._userkey is None:
         # userkey is uniquie for every smart card
         self._userkey = tagged_hash(
             "userkey", self.secret + (self.applet.card_pubkey or b""))
     return self._userkey
예제 #14
0
 def sdpath(self):
     hexid = hexlify(tagged_hash("sdid", self.secret)[:4]).decode()
     return platform.fpath("/sd/specterdiy%s" % hexid)
예제 #15
0
    def fileprefix(self, path):
        if path is self.flashpath:
            return 'reckless'

        hexid = hexlify(tagged_hash("sdid", self.secret)[:4]).decode()
        return "specterdiy%s" % hexid