Example #1
0
    def atcab_verify_stored(self, message, signature, key_id):
        if not isinstance(message, _BYTES_LIKE_OBJECT):
            raise ATCA_EXCEPTIONS.BadArgumentError()

        if not isinstance(signature, _BYTES_LIKE_OBJECT):
            raise ATCA_EXCEPTIONS.BadArgumentError()

        nonce_target = ATCA_CONSTANTS.NONCE_MODE_TARGET_TEMPKEY
        verify_source = ATCA_CONSTANTS.VERIFY_MODE_SOURCE_TEMPKEY

        # Load message into device
        if self._device == "ATECC608A":
            # Use the Message Digest Buffer for the ATECC608A
            nonce_target = ATCA_CONSTANTS.NONCE_MODE_TARGET_MSGDIGBUF
            verify_source = ATCA_CONSTANTS.VERIFY_MODE_SOURCE_MSGDIGBUF

        self.atcab_nonce_load(nonce_target, message)
        try:
            packet = self.atcab_verify(
                ATCA_CONSTANTS.VERIFY_MODE_STORED | verify_source, key_id,
                signature)
            return packet[1] == ATCA_STATUS.ATCA_SUCCESS
        except ATCA_EXCEPTIONS.CheckmacVerifyFailedError:
            # Verify failed, but command succeeded
            return False
Example #2
0
    def atcab_write_pubkey(self, slot, public_key):
        if not isinstance(public_key, _BYTES_LIKE_OBJECT):
            raise ATCA_EXCEPTIONS.BadArgumentError()

        # Check the value of the slot. Only slots 8 to 15 are large enough to store a public key
        if slot < 8 or slot > 15:
            raise ATCA_EXCEPTIONS.BadArgumentError(
                "Only slots 8 to 15 are large enough to store a public key")

        public_key_formatted = bytearray(72)

        # The 64 byte P256 public key gets written to a 72 byte slot in the following pattern
        #
        # | Block 1                     | Block 2                                      | Block 3       |
        # | Pad: 4 Bytes | PubKey[0:27] | PubKey[28:31] | Pad: 4 Bytes | PubKey[32:55] | PubKey[56:63] |

        PKD = ATCA_CONSTANTS.ATCA_PUB_KEY_PAD
        KS = ATCA_CONSTANTS.ATCA_KEY_SIZE

        # Copy X to padded position
        public_key_formatted[PKD:PKD + KS] = public_key[0:KS]
        # Copy Y to padded position
        public_key_formatted[KS + PKD + PKD:KS + PKD + PKD +
                             KS] = public_key[KS:KS + KS]

        packets = self.atcab_write_bytes_zone(ATCA_CONSTANTS.ATCA_ZONE_DATA,
                                              slot=slot,
                                              data=public_key_formatted)

        return packets
Example #3
0
    def atcab_sha_base(self, mode=0, data=b'', key_slot=None):
        if not isinstance(data, _BYTES_LIKE_OBJECT):
            raise ATCA_EXCEPTIONS.BadArgumentError()

        txsize = 0
        cmd_mode = mode & ATCA_CONSTANTS.SHA_MODE_MASK
        if cmd_mode in (ATCA_CONSTANTS.SHA_MODE_SHA256_START,
                        ATCA_CONSTANTS.SHA_MODE_HMAC_START,
                        ATCA_CONSTANTS.SHA_MODE_SHA256_PUBLIC):
            txsize = ATCA_CONSTANTS.ATCA_CMD_SIZE_MIN
        elif cmd_mode in (ATCA_CONSTANTS.SHA_MODE_SHA256_UPDATE,
                          ATCA_CONSTANTS.SHA_MODE_SHA256_END,
                          ATCA_CONSTANTS.SHA_MODE_HMAC_END):
            txsize = ATCA_CONSTANTS.ATCA_CMD_SIZE_MIN + len(data)
        else:
            raise ATCA_EXCEPTIONS.BadArgumentError()

        packet = ATCAPacket(
            txsize=txsize,
            opcode=ATCA_CONSTANTS.ATCA_SHA,
            param1=mode,
            param2=len(data) if not isinstance(key_slot, int) else key_slot,
            request_data=data)
        self.execute(packet)
        return packet
Example #4
0
    def atcab_verify(self,
                     mode,
                     key_id,
                     signature,
                     public_key=None,
                     other_data=None,
                     mac=None):
        if not isinstance(signature, _BYTES_LIKE_OBJECT):
            raise ATCA_EXCEPTIONS.BadArgumentError()

        verify_mode = (mode & ATCA_CONSTANTS.VERIFY_MODE_MASK)

        VME = ATCA_CONSTANTS.VERIFY_MODE_EXTERNAL
        has_public_key = isinstance(public_key, _BYTES_LIKE_OBJECT)
        if verify_mode == VME and not has_public_key:
            raise ATCA_EXCEPTIONS.BadArgumentError()

        VMV = ATCA_CONSTANTS.VERIFY_MODE_VALIDATE
        VMI = ATCA_CONSTANTS.VERIFY_MODE_INVALIDATE
        has_other_data = isinstance(other_data, _BYTES_LIKE_OBJECT)
        if verify_mode in (VMV, VMI) and not has_other_data:
            raise ATCA_EXCEPTIONS.BadArgumentError()

        txsize = 0
        if verify_mode == ATCA_CONSTANTS.VERIFY_MODE_STORED:
            txsize = ATCA_CONSTANTS.VERIFY_256_STORED_COUNT
        elif verify_mode in (ATCA_CONSTANTS.VERIFY_MODE_VALIDATE_EXTERNAL,
                             ATCA_CONSTANTS.VERIFY_MODE_EXTERNAL):
            txsize = ATCA_CONSTANTS.VERIFY_256_EXTERNAL_COUNT
        elif verify_mode in (ATCA_CONSTANTS.VERIFY_MODE_VALIDATE,
                             ATCA_CONSTANTS.VERIFY_MODE_INVALIDATE):
            txsize = ATCA_CONSTANTS.VERIFY_256_VALIDATE_COUNT

        SS = ATCA_CONSTANTS.ATCA_SIG_SIZE
        PKS = ATCA_CONSTANTS.ATCA_PUB_KEY_SIZE
        VODS = ATCA_CONSTANTS.VERIFY_OTHER_DATA_SIZE
        data_size = SS
        if has_public_key:
            data_size += PKS
        elif has_other_data:
            data_size += VODS

        data = bytearray(data_size)

        data[0:SS] = signature
        if has_public_key:
            data[SS:SS + PKS] = public_key
        elif has_other_data:
            data[SS:SS + VODS] = other_data

        packet = ATCAPacket(txsize=txsize,
                            opcode=ATCA_CONSTANTS.ATCA_VERIFY,
                            param1=mode,
                            param2=key_id,
                            request_data=data)
        self.execute(packet)
        return packet
Example #5
0
    def atcab_write_zone(self, zone, slot=0, block=0, offset=0, data=None):
        if not isinstance(data, _BYTES_LIKE_OBJECT):
            raise ATCA_EXCEPTIONS.BadArgumentError()

        length = len(data)
        if length not in (ATCA_CONSTANTS.ATCA_WORD_SIZE,
                          ATCA_CONSTANTS.ATCA_BLOCK_SIZE):
            raise ATCA_EXCEPTIONS.BadArgumentError()

        if length == ATCA_CONSTANTS.ATCA_BLOCK_SIZE:
            zone = zone | ATCA_CONSTANTS.ATCA_ZONE_READWRITE_32

        addr = self.atcab_get_addr(zone, slot=slot, block=block, offset=offset)
        return self.atcab_write(zone, addr, data)
Example #6
0
    def atcab_nonce_load(self, target, numbers=None):
        if not isinstance(numbers, _BYTES_LIKE_OBJECT):
            raise ATCA_EXCEPTIONS.BadArgumentError()

        mode = ATCA_CONSTANTS.NONCE_MODE_PASSTHROUGH
        mode = mode | (ATCA_CONSTANTS.NONCE_MODE_TARGET_MASK & target)

        if len(numbers) == 32:
            mode = mode | ATCA_CONSTANTS.NONCE_MODE_INPUT_LEN_32
        elif len(numbers) == 64:
            mode = mode | ATCA_CONSTANTS.NONCE_MODE_INPUT_LEN_64
        else:
            raise ATCA_EXCEPTIONS.BadArgumentError()

        return self.atcab_nonce_base(mode, numbers=numbers)
Example #7
0
    def atcab_write_bytes_zone(self, zone, slot=0, offset=0, data=None):
        if not isinstance(data, _BYTES_LIKE_OBJECT):
            raise ATCA_EXCEPTIONS.BadArgumentError()

        zone_size = self.atcab_get_zone_size(zone, slot=slot)

        length = len(data)
        if offset + length > zone_size:
            raise ATCA_EXCEPTIONS.BadArgumentError()

        packets = []

        BS = ATCA_CONSTANTS.ATCA_BLOCK_SIZE
        WS = ATCA_CONSTANTS.ATCA_WORD_SIZE
        ZC = ATCA_CONSTANTS.ATCA_ZONE_CONFIG

        d_idx = 0
        c_blk = offset // BS
        c_wrd = (offset % BS) // WS
        d_mv = memoryview(data)
        while d_idx < length:
            # The last item makes sure we handle the selector, user extra, and lock bytes in the config properly
            if c_wrd == 0 and length - d_idx >= BS and not (zone == ZC
                                                            and c_blk == 2):
                packet = self.atcab_write_zone(zone,
                                               slot=slot,
                                               block=c_blk,
                                               offset=0,
                                               data=d_mv[d_idx:d_idx + BS])
                packets.append(packet)
                d_idx += BS
                c_blk += 1
            else:
                # Skip trying to change UserExtra, Selector, LockValue and LockConfig which require the UpdateExtra command to change
                if not (zone == ZC and c_blk == 2 and c_wrd == 5):
                    packet = self.atcab_write_zone(zone,
                                                   slot=slot,
                                                   block=c_blk,
                                                   offset=c_wrd,
                                                   data=d_mv[d_idx:d_idx + WS])
                    packets.append(packet)
                d_idx += WS
                c_wrd += 1
                if c_wrd == BS // WS:
                    c_blk += 1
                    c_wrd = 0

        return packets
Example #8
0
    def atcab_write(self, zone, address, value=None, mac=None):
        if not isinstance(value, _BYTES_LIKE_OBJECT):
            raise ATCA_EXCEPTIONS.BadArgumentError()

        txsize = ATCA_CONSTANTS.ATCA_CMD_SIZE_MIN
        data = bytearray(64)
        if zone & ATCA_CONSTANTS.ATCA_ZONE_READWRITE_32:
            # 32-byte write
            data[0:32] = value
            txsize += ATCA_CONSTANTS.ATCA_BLOCK_SIZE
            # Only 32-byte writes can have a MAC
            if isinstance(mac, _BYTES_LIKE_OBJECT):
                data[32:64] = mac
                txsize += ATCA_CONSTANTS.WRITE_MAC_SIZE
        else:
            # 4-byte write
            data[0:4] = value
            txsize += ATCA_CONSTANTS.ATCA_WORD_SIZE

        packet = ATCAPacket(
            txsize=txsize,
            opcode=ATCA_CONSTANTS.ATCA_WRITE,
            param1=zone,
            param2=address,
            request_data=data[:txsize - ATCA_CONSTANTS.ATCA_CMD_SIZE_MIN])
        self.execute(packet)
        return packet
Example #9
0
    def atcab_read_pubkey(self, slot):
        # Check the value of the slot. Only slots 8 to 15 are large enough to store a public key
        if slot < 8 or slot > 15:
            raise ATCA_EXCEPTIONS.BadArgumentError(
                "Only slots 8 to 15 are large enough to store a public key")

        # The 64 byte P256 public key gets written to a 72 byte slot in the following pattern
        #
        # | Block 1                     | Block 2                                      | Block 3       |
        # | Pad: 4 Bytes | PubKey[0:27] | PubKey[28:31] | Pad: 4 Bytes | PubKey[32:55] | PubKey[56:63] |

        ZD = ATCA_CONSTANTS.ATCA_ZONE_DATA
        BS = ATCA_CONSTANTS.ATCA_BLOCK_SIZE
        PKD = ATCA_CONSTANTS.ATCA_PUB_KEY_PAD
        KS = ATCA_CONSTANTS.ATCA_KEY_SIZE

        public_key = b''
        packet = self.atcab_read_zone(ZD, slot=slot, block=0, length=BS)
        public_key += packet[1:-2]
        packet = self.atcab_read_zone(ZD, slot=slot, block=1, length=BS)
        public_key += packet[1:-2]
        packet = self.atcab_read_zone(ZD, slot=slot, block=2, length=BS)
        public_key += packet[1:-2]

        return public_key[PKD:PKD + KS] + public_key[KS + PKD + PKD:KS + PKD +
                                                     PKD + KS]
Example #10
0
    def atcab_gendig(self, zone, key_id, other_data):
        if (zone < 0x00 or zone > 0x05):
            raise ATCA_EXCEPTIONS.BadArgumentError(
                "Zone must be between zero and 5")

        packet = ATCAPacket(opcode=ATCA_CONSTANTS.ATCA_GENDIG,
                            param1=zone,
                            param2=key_id,
                            request_data=other_data)
        self.execute(packet)
        return packet
Example #11
0
    def atcab_get_zone_size(self, zone, slot=0):
        if zone not in (ATCA_CONSTANTS.ATCA_ZONE_CONFIG,
                        ATCA_CONSTANTS.ATCA_ZONE_DATA,
                        ATCA_CONSTANTS.ATCA_ZONE_OTP):
            raise ATCA_EXCEPTIONS.BadArgumentError()

        if slot < 0 or slot > 15:
            raise ATCA_EXCEPTIONS.BadArgumentError()

        if zone == ATCA_CONSTANTS.ATCA_ZONE_CONFIG:
            return 128
        elif zone == ATCA_CONSTANTS.ATCA_ZONE_OTP:
            return 64
        elif zone == ATCA_CONSTANTS.ATCA_ZONE_DATA:
            if slot < 8:
                return 36
            elif slot == 8:
                return 412
            elif slot < 16:
                return 72
Example #12
0
    def atcab_get_addr(self, zone, slot=0, block=0, offset=0):
        mem_zone = zone & ATCA_CONSTANTS.ATCA_ZONE_MASK
        if mem_zone not in (ATCA_CONSTANTS.ATCA_ZONE_CONFIG,
                            ATCA_CONSTANTS.ATCA_ZONE_DATA,
                            ATCA_CONSTANTS.ATCA_ZONE_OTP):
            raise ATCA_EXCEPTIONS.BadArgumentError()

        if slot < 0 or slot > 15:
            raise ATCA_EXCEPTIONS.BadArgumentError()

        addr = 0
        offset = offset & 0x07
        if mem_zone in (ATCA_CONSTANTS.ATCA_ZONE_CONFIG,
                        ATCA_CONSTANTS.ATCA_ZONE_OTP):
            addr = block << 3
            addr = addr | offset
        elif mem_zone == ATCA_CONSTANTS.ATCA_ZONE_DATA:
            addr = slot << 3
            addr = addr | offset
            addr = addr | block << 8

        return addr
Example #13
0
    def atcab_read_enc(self, key_id, block, enc_key_id, num_in=b'\x00' * 32):
        """Perform a simple encrypted read.

        See Atmel Application Note: Encrypted Reads and Writes
        """
        if key_id < 0 or key_id > 15:
            raise ATCA_EXCEPTIONS.BadArgumentError("Read key out of range")
        if enc_key_id < 0 or enc_key_id > 15:
            raise ATCA_EXCEPTIONS.BadArgumentError(
                "Encryption key out of range")
        if len(num_in) != 32:
            raise ATCA_EXCEPTIONS.BadArgumentError("numin length must be 32")

        # Write nonce to TempKey in pass through mode (p71)
        self.atcab_nonce(num_in)

        # Supply OtherData so GenDig behavior is the same for keys with
        # SlotConfig.NoMac set
        other_data = bytearray(4)
        other_data[0] = ATCA_CONSTANTS.ATCA_GENDIG
        other_data[1] = ATCA_CONSTANTS.GENDIG_ZONE_DATA
        other_data[2] = enc_key_id
        other_data[3] = enc_key_id >> 8

        # Generate digest and retreive session key from TempKey.value (p68)
        gendig_packet = self.atcab_gendig(ATCA_CONSTANTS.GENDIG_ZONE_DATA,
                                          enc_key_id, other_data)
        session_key = gendig_packet.response_data[-32:]

        # Read cipher text value (p81)
        read_packet = self.atcab_read_zone(
            ATCA_CONSTANTS.ATCA_ZONE_DATA
            | ATCA_CONSTANTS.ATCA_ZONE_READWRITE_32, key_id, block, 0,
            ATCA_CONSTANTS.ATCA_BLOCK_SIZE)
        cipher_text = read_packet.response_data

        # Decrypt
        plain_text = bytes(a ^ b for (a, b) in zip(cipher_text, session_key))
        return plain_text
Example #14
0
    def atcab_nonce_base(self, mode, zero=0, numbers=None):
        nonce_mode = mode & ATCA_CONSTANTS.NONCE_MODE_MASK
        if nonce_mode not in (ATCA_CONSTANTS.NONCE_MODE_SEED_UPDATE,
                              ATCA_CONSTANTS.NONCE_MODE_NO_SEED_UPDATE,
                              ATCA_CONSTANTS.NONCE_MODE_PASSTHROUGH):
            raise ATCA_EXCEPTIONS.BadArgumentError()

        if not isinstance(numbers, _BYTES_LIKE_OBJECT):
            raise ATCA_EXCEPTIONS.BadArgumentError()

        txsize = 0
        if nonce_mode in (ATCA_CONSTANTS.NONCE_MODE_SEED_UPDATE,
                          ATCA_CONSTANTS.NONCE_MODE_NO_SEED_UPDATE):
            txsize = ATCA_CONSTANTS.NONCE_COUNT_SHORT
        elif nonce_mode == ATCA_CONSTANTS.NONCE_MODE_PASSTHROUGH:
            nonce_mode_input = mode & ATCA_CONSTANTS.NONCE_MODE_INPUT_LEN_MASK
            if nonce_mode_input == ATCA_CONSTANTS.NONCE_MODE_INPUT_LEN_64:
                txsize = ATCA_CONSTANTS.NONCE_COUNT_LONG_64
            else:
                txsize = ATCA_CONSTANTS.NONCE_COUNT_LONG
        else:
            raise ATCA_EXCEPTIONS.BadArgumentError()

        n_mv = memoryview(numbers)
        if len(n_mv) < txsize - ATCA_CONSTANTS.ATCA_CMD_SIZE_MIN:
            raise ATCA_EXCEPTIONS.BadArgumentError()

        packet = ATCAPacket(
            txsize=txsize,
            opcode=ATCA_CONSTANTS.ATCA_NONCE,
            param1=mode,
            param2=zero,
            request_data=n_mv[:txsize - ATCA_CONSTANTS.ATCA_CMD_SIZE_MIN])

        self.execute(packet)
        return packet
Example #15
0
    def atcab_read_zone(self, zone, slot=0, block=0, offset=0, length=0):
        if length not in (ATCA_CONSTANTS.ATCA_WORD_SIZE,
                          ATCA_CONSTANTS.ATCA_BLOCK_SIZE):
            raise ATCA_EXCEPTIONS.BadArgumentError()

        addr = self.atcab_get_addr(zone, slot=slot, block=block, offset=offset)

        if length == ATCA_CONSTANTS.ATCA_BLOCK_SIZE:
            zone = zone | ATCA_CONSTANTS.ATCA_ZONE_READWRITE_32

        packet = ATCAPacket(opcode=ATCA_CONSTANTS.ATCA_READ,
                            param1=zone,
                            param2=addr)
        self.execute(packet)
        return packet
Example #16
0
    def atcab_is_locked(self, zone):
        if zone not in (ATCA_CONSTANTS.LOCK_ZONE_CONFIG,
                        ATCA_CONSTANTS.LOCK_ZONE_DATA):
            raise ATCA_EXCEPTIONS.BadArgumentError()

        # Read the word with the lock bytes
        # (UserExtra, Selector, LockValue, LockConfig) (config block = 2, word offset = 5)
        packet = self.atcab_read_zone(ATCA_CONSTANTS.ATCA_ZONE_CONFIG,
                                      slot=0,
                                      block=2,
                                      offset=5,
                                      length=ATCA_CONSTANTS.ATCA_WORD_SIZE)
        if zone == ATCA_CONSTANTS.LOCK_ZONE_CONFIG:
            return bool(packet[3 + 1] != 0x55)
        elif zone == ATCA_CONSTANTS.LOCK_ZONE_DATA:
            return bool(packet[2 + 1] != 0x55)
Example #17
0
    def atcab_write_config_zone(self, config_data):
        if not isinstance(config_data, _BYTES_LIKE_OBJECT):
            raise ATCA_EXCEPTIONS.BadArgumentError()

        config_size = self.atcab_get_zone_size(ATCA_CONSTANTS.ATCA_ZONE_CONFIG)

        # Write config zone excluding UserExtra and Selector
        packets = self.atcab_write_bytes_zone(ATCA_CONSTANTS.ATCA_ZONE_CONFIG,
                                              slot=0,
                                              offset=16,
                                              data=config_data[16:config_size -
                                                               16])

        # Write the UserExtra and Selector. This may fail if either value is already non-zero.
        packet = self.atcab_updateextra(ATCA_CONSTANTS.UPDATE_MODE_USER_EXTRA,
                                        config_data[84])
        packets.append(packet)

        packet = self.atcab_updateextra(ATCA_CONSTANTS.UPDATE_MODE_SELECTOR,
                                        config_data[85])
        packets.append(packet)

        return packets
Example #18
0
    def atcab_read_bytes_zone(self, zone, slot=0, block=0, offset=0, length=0):
        zone_size = self.atcab_get_zone_size(zone, slot=slot)

        if offset + length > zone_size:
            raise ATCA_EXCEPTIONS.BadArgumentError()

        packets = []

        BS = ATCA_CONSTANTS.ATCA_BLOCK_SIZE
        WS = ATCA_CONSTANTS.ATCA_WORD_SIZE

        r_sz = BS
        d_idx = r_idx = r_of = c_blk = c_of = 0
        c_blk = offset // BS
        while d_idx < length:
            if r_sz == BS and zone_size - c_blk * BS < BS:
                r_sz = WS
                c_of = ((d_idx + offset) // WS) % (BS // WS)

            packet = self.atcab_read_zone(zone,
                                          slot=slot,
                                          block=c_blk,
                                          offset=c_of,
                                          length=r_sz)
            packets.append(packet)

            r_of = c_blk * BS + c_of * WS
            r_idx = offset - r_of if r_of < offset else 0
            d_idx += length - d_idx if length - d_idx < r_sz - r_idx else r_sz - r_idx

            if r_sz == BS:
                c_blk += 1
            else:
                c_of += 1

        return packets
Example #19
0
    def atcab_lock_data_slot(self, slot):
        if slot < 0 or slot > 15:
            raise ATCA_EXCEPTIONS.BadArgumentError()

        return self.atcab_lock((slot << 2)
                               | ATCA_CONSTANTS.LOCK_ZONE_DATA_SLOT)