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
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
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
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
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)
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)
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
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
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]
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
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
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
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
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
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
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)
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
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
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)