def __init__( self, dek: Optional[bytes] = None, mac: Optional[bytes] = None, nonce: Optional[bytes] = None, timestamp: Optional[datetime] = None, ): """Initialize SBV2xAdvancedParams. :param dek: DEK key :param mac: MAC key :param nonce: nonce :param timestamp: fixed timestamp for the header; use None to use current date/time :raises SPSDKError: Invalid dek or mac :raises SPSDKError: Invalid length of nonce """ self._dek: bytes = dek if dek else crypto_backend().random_bytes(32) self._mac: bytes = mac if mac else crypto_backend().random_bytes(32) self._nonce: bytes = nonce if nonce else SBV2xAdvancedParams._create_nonce( ) if timestamp is None: timestamp = datetime.now() self._timestamp = datetime.fromtimestamp(int(timestamp.timestamp())) if len(self._dek) != 32 and len(self._mac) != 32: raise SPSDKError("Invalid dek or mac") if len(self._nonce) != 16: raise SPSDKError("Invalid length of nonce")
def test_seg_bee() -> None: """Test SegBEE class - BEE segment of the bootable image""" # empty segment seg = SegBEE([]) assert seg.info() assert seg.export() == b'' assert seg.size == 0 seg.validate() # single region sw_key = crypto_backend().random_bytes(16) hdr = BeeRegionHeader(sw_key=sw_key) hdr.add_fac(BeeFacRegion(0x00000000, 0x00010000, 0)) seg.add_region(hdr) assert seg.info() seg.validate() data = seg.export() assert (data is not None) and (len(data) == seg.size) parsed_seg = SegBEE.parse(b'\xFF' + data, 1, [sw_key]) assert seg == parsed_seg # two regions sw_key2 = crypto_backend().random_bytes(16) hdr2 = BeeRegionHeader(sw_key=sw_key2) hdr2.add_fac(BeeFacRegion(0x10000000, 0x00010000, 1)) hdr2.add_fac(BeeFacRegion(0x20000000, 0x00010000, 2)) seg.add_region(hdr2) assert seg.info() seg.validate() data = seg.export() assert (data is not None) and (len(data) == seg.size) parsed_seg = SegBEE.parse(b'\xFF' + data, 1, [sw_key, sw_key2]) assert seg == parsed_seg # total number of FACs exceeded hdr.add_fac(BeeFacRegion(0xF0000000, 0x00010000, 3)) with pytest.raises(ValueError): seg.validate()
def test_invalid_export_cert_section_v2(data_dir): cs = CertSectionV2(_create_cert_block_v2(data_dir)) dek = crypto_backend().random_bytes(16) mac = crypto_backend().random_bytes(16) nonce = crypto_backend().random_bytes(16) cs.HMAC_SIZE = 137 with pytest.raises(SPSDKError, match="Invalid size"): cs.export(dek, mac, Counter(nonce))
def export(self, padding: Optional[bytes] = None) -> bytes: """Serialize image object. :param padding: header padding (8 bytes) for testing purpose; None to use random values (recommended) :return: exported bytes :raises SPSDKError: Raised when there are no boot sections or is not signed or private keys are missing :raises SPSDKError: Raised when there is invalid dek or mac :raises SPSDKError: Raised when certificate data is not present :raises SPSDKError: Raised when there is invalid certificate block :raises SPSDKError: Raised when there is invalid length of exported data """ if len(self.dek) != 32 or len(self.mac) != 32: raise SPSDKError("Invalid dek or mac") # validate params if not self._boot_sections: raise SPSDKError("No boot section") if self.signed and (self._cert_section is None): raise SPSDKError( "Certificate section is required for signed images") # update internals self.update() # Add Image Header data data = self._header.export(padding=padding) # Add Image Header HMAC data data += crypto_backend().hmac(self.mac, data) # Add DEK and MAC keys data += crypto_backend().aes_key_wrap(self.kek, self.dek + self.mac) # Add Padding data += padding if padding else crypto_backend().random_bytes(8) # Add Certificates data if not self._header.nonce: raise SPSDKError("There is no nonce in the header") counter = Counter(self._header.nonce) counter.increment(calc_cypher_block_count(len(data))) if self._cert_section is not None: cert_sect_bin = self._cert_section.export(dek=self.dek, mac=self.mac, counter=counter) counter.increment(calc_cypher_block_count(len(cert_sect_bin))) data += cert_sect_bin # Add Boot Sections data for sect in self._boot_sections: data += sect.export(dek=self.dek, mac=self.mac, counter=counter) # Add Signature data if self.signed: private_key_pem_data = self.private_key_pem_data if private_key_pem_data is None: raise SPSDKError( "Private key not assigned, cannot sign the image") certificate_block = self.cert_block if not ((certificate_block is not None) and certificate_block. verify_private_key(private_key_pem_data)): raise SPSDKError("Invalid certificate block") data += crypto_backend().rsa_sign(private_key_pem_data, data) if len(data) != self.raw_size: raise SPSDKError("Invalid length of exported data") return data
def __init__(self, kib_key: Optional[bytes] = None, kib_iv: Optional[bytes] = None): """Constructor. :param kib_key: AES key :param kib_iv: AES initialization vector """ # Key Info Block (KIB) self.kib_key = kib_key if kib_key else crypto_backend().random_bytes(16) self.kib_iv = kib_iv if kib_iv else crypto_backend().random_bytes(16)
def test_invalid_header_hmac(data_dir): cs = CertSectionV2(_create_cert_block_v2(data_dir)) dek = crypto_backend().random_bytes(32) mac = crypto_backend().random_bytes(32) nonce = crypto_backend().random_bytes(16) valid_data = cs.export(dek, mac, Counter(nonce)) invalid_data = valid_data invalid_data = bytearray(invalid_data) invalid_data[0:32] = bytearray(32) with pytest.raises(SPSDKError, match="HMAC"): CertSectionV2.parse(invalid_data, 0, dek, mac, Counter(nonce))
def test_invalid_header_flag(data_dir): cs = CertSectionV2(_create_cert_block_v2(data_dir)) cs._header.address += 1 dek = crypto_backend().random_bytes(32) mac = crypto_backend().random_bytes(32) nonce = crypto_backend().random_bytes(16) valid_data = cs.export(dek, mac, Counter(nonce)) with pytest.raises(SPSDKError, match="Mark"): CertSectionV2.parse(data=valid_data, mac=mac, dek=dek, counter=Counter(nonce))
def test_certificate_section_v2(data_dir: str) -> None: with pytest.raises(AssertionError): CertSectionV2(None) cs = CertSectionV2(_create_cert_block_v2(data_dir)) dek = crypto_backend().random_bytes(32) mac = crypto_backend().random_bytes(32) nonce = crypto_backend().random_bytes(16) data = cs.export(dek, mac, Counter(nonce)) assert data assert CertSectionV2.parse(data, 0, dek, mac, Counter(nonce)) with pytest.raises(Exception): CertSectionV2.parse(data, 0, dek, crypto_backend().random_bytes(32), Counter(nonce))
def export(self) -> bytes: """Master boot image (binary).""" data = self._update_ivt(self.data) # signed or encrypted if MasterBootImageType.is_signed(self.image_type): assert self._priv_key_pem_data cb = self.cert_block assert (cb is not None) and cb.verify_private_key(self._priv_key_pem_data) # type: ignore # encrypt encr_data = self._encrypt(data) encr_data = (self._update_ivt(encr_data[:self.HMAC_OFFSET]) + # header encr_data[self.HMAC_OFFSET:self.app_len] + # encrypted image self._certificate(encr_data) + # certificate + encoded image header + CTR init vector encr_data[self.app_len:]) # TZ encoded data encr_data += crypto_backend().rsa_sign(self._priv_key_pem_data, encr_data) # signature # hmac + key store if MasterBootImageType.has_hmac(self.image_type): hmac_keystore = self._hmac(encr_data[:self.HMAC_OFFSET]) if self.key_store: hmac_keystore += self.key_store.export() encr_data = encr_data[:self.HMAC_OFFSET] + hmac_keystore + encr_data[self.HMAC_OFFSET:] return bytes(encr_data) return bytes(data)
def parse(cls, data: bytes, offset: int = 0) -> "SecureBootV1": """Convert binary data into the instance (deserialization). :param data: given binary data to be converted :param offset: to start parsing the data :return: converted instance :raise ValueError: raised when digest does not match :raises SPSDKError: Raised when section is invalid """ obj = SecureBootV1() cur_pos = offset obj._header = SecureBootHeaderV1.parse(data, cur_pos) cur_pos += obj._header.size # sections header table for _ in range(obj._header.section_count): sect_header = SectionHeaderItemV1.parse(data, cur_pos) obj._sections_hdr_table.append(sect_header) cur_pos += sect_header.size # sections new_pos = offset + obj._header.first_boot_tag_block * SecBootBlckSize.BLOCK_SIZE if new_pos < cur_pos: raise SPSDKError("Invalid section") cur_pos = new_pos for _ in range(obj._header.section_count): boot_sect = BootSectionV1.parse(data, cur_pos) obj.append(boot_sect) cur_pos += boot_sect.size # authentication code sha1_auth = crypto_backend().hash(data[offset:cur_pos], "sha1") if sha1_auth != data[cur_pos:cur_pos + len(sha1_auth)]: raise ValueError("Authentication failure: digest does not match") # done return obj
def encrypt_block(self, key: bytes, start_addr: int, data: bytes) -> bytes: """Encrypt block located in any FAC region. :param key: user for encryption :param start_addr: start address of the data :param data: binary block to be encrypted; the block size must be BEE_ENCR_BLOCK_SIZE :return: encrypted block if it is inside any FAC region; untouched block if it is not in any FAC region :raises SPSDKError: When incorrect length of binary block :raises SPSDKError: When encryption mode different from AES/CTR provided :raises SPSDKError: When invalid length of key :raises SPSDKError: When invalid range of region """ if len(data) != BEE_ENCR_BLOCK_SIZE: raise SPSDKError( "Incorrect length of binary block to be encrypted") if self._start_addr <= start_addr < self._end_addr: if self.mode != BeeProtectRegionBlockAesMode.CTR: raise SPSDKError("only AES/CTR encryption mode supported now") if len(key) != 16: raise SPSDKError("Invalid length of key") for fac in self.fac_regions: if fac.start_addr <= start_addr < fac.end_addr: if start_addr + len(data) > fac.end_addr: raise SPSDKError("Invalid range of region") cntr_key = Counter( self.counter, ctr_value=start_addr >> 4, ctr_byteorder_encoding="big", ) return crypto_backend().aes_ctr_encrypt( key, data, cntr_key.value) return data
def store_ctr_init_vector(self, ctr_iv: bytes = None) -> None: """Stores the Counter init vector, if not specified the random value is used. param ctr_iv: Counter Initial Vector. """ self.ctr_init_vector = ctr_iv or crypto_backend().random_bytes( self._CTR_INIT_VECTOR_SIZE)
def _create_nonce() -> bytes: """Return random nonce.""" nonce = bytearray(crypto_backend().random_bytes(16)) # clear nonce bit at offsets 31 and 63 nonce[9] &= 0x7F nonce[13] &= 0x7F return bytes(nonce)
def test_boot_section_v2(): boot_section = BootSectionV2(0, CmdErase(address=0, length=100000), CmdLoad(address=0, data=b"0123456789"), CmdReset()) assert boot_section.uid == 0 assert not boot_section.is_last assert boot_section.hmac_count == 1 assert boot_section.raw_size == 144 dek = crypto_backend().random_bytes(32) mac = crypto_backend().random_bytes(32) nonce = crypto_backend().random_bytes(16) data = boot_section.export(dek, mac, Counter(nonce)) assert data assert BootSectionV2.parse(data, 0, False, dek, mac, Counter(nonce)) with pytest.raises(SPSDKError, match="Invalid type of dek, should be bytes"): BootSectionV2.parse(data=data, offset=0, plain_sect=False, dek=4, mac=mac, counter=Counter(nonce)) with pytest.raises(SPSDKError, match="Invalid type of mac, should be bytes"): BootSectionV2.parse(data=data, offset=0, plain_sect=False, dek=dek, mac=4, counter=Counter(nonce)) with pytest.raises(SPSDKError, match="Invalid type of counter"): BootSectionV2.parse(data=data, offset=0, plain_sect=False, dek=dek, mac=mac, counter=5) with pytest.raises(SPSDKError): assert BootSectionV2.parse(data, 0, False, dek, crypto_backend().random_bytes(32), Counter(nonce))
def sign(self, image: bytes) -> bytes: """Do calculation of RSA signature and return updated image with it. :param image: Input raw image. :return: Image enriched by RSA signature at end of image. """ assert self.priv_key_data return image + crypto_backend().rsa_sign(self.priv_key_data, image)
def export( self, header_padding8: Optional[bytes] = None, auth_padding: Optional[bytes] = None, dbg_info: DebugInfo = DebugInfo.disabled(), ) -> bytes: """Serialization to binary form. :param header_padding8: optional header padding, 8-bytes; recommended to use None to apply random value :param auth_padding: optional padding used after authentication; recommended to use None to apply random value :param dbg_info: instance allowing to debug generated output :return: serialize the instance into binary data :raises SPSDKError: Invalid section data :raises SPSDKError: Invalid padding length """ self.update() self.validate() dbg_info.append_section("SB-FILE-1.x") data = self._header.export(padding8=header_padding8, dbg_info=dbg_info) # header table dbg_info.append_section("Sections-Header-Table") for sect_hdr in self._sections_hdr_table: sect_hdr_data = sect_hdr.export() dbg_info.append_binary_data("Section-Header-Item", sect_hdr_data) data += sect_hdr_data # sections dbg_info.append_section("Sections") for sect in self._sections: sect_data = sect.export(dbg_info) if len(sect_data) != sect.size: raise SPSDKError("Invalid section data") data += sect_data # authentication: SHA1 auth_code = crypto_backend().hash(data, "sha1") dbg_info.append_binary_section("SHA1", auth_code) data += auth_code # padding padding_len = align(len(auth_code), SecBootBlckSize.BLOCK_SIZE) - len(auth_code) if auth_padding is None: auth_padding = crypto_backend().random_bytes(padding_len) if padding_len != len(auth_padding): raise SPSDKError("Invalid padding length") data += auth_padding dbg_info.append_binary_section("padding", auth_padding) return data
def __init__(self, app: Union[bytes, bytearray], load_addr: int, image_type: MasterBootImageType = MasterBootImageType.PLAIN_IMAGE, trust_zone: Optional[TrustZone] = None, app_table: Optional[MultipleImageTable] = None, cert_block: Optional[CertBlock] = None, priv_key_pem_data: Optional[bytes] = None, hmac_key: Union[bytes, str] = None, key_store: KeyStore = None, enable_hw_user_mode_keys: bool = False, ctr_init_vector: bytes = None) -> None: """Constructor. :param app: input image (binary) :param load_addr: address in RAM, where 'RAM' image will be copied; for XIP images address, where the image is located in FLASH memory :param image_type: type of the master boot image :param trust_zone: TrustZone instance; None to use default settings (TrustZone enabled) :param app_table: optional table with additional images; None if no additional images needed :param cert_block: block of certificates; None for unsigned image :param priv_key_pem_data: private key to sign the image, decrypted binary data in PEM format :param hmac_key: optional key for HMAC generation (either binary ot HEX string; 32 bytes); None if HMAC is not in the image If key_store.key_source == KeySourceType.KEYSTORE, this is a user-key from key-store If key_store.key_source == KeySourceType.OTP, this is a master-key burned in OTP :param key_store: optional key store binary content; None if key store is not in the image :param enable_hw_user_mode_keys: flag for controlling secure hardware key bus. If true, then it is possible to access keys on hardware secure bus from non-secure application, else non-secure application will read zeros. :param ctr_init_vector: optional initial vector for encryption counter; None to use random vector :raises TypeError: if type is not binary data :raises ValueError: if images are not loaded from RAM """ if not isinstance(app, (bytes, bytearray)): raise TypeError("app must be binary data (bytes, bytearray)") if app_table and not MasterBootImageType.is_copied_to_ram(image_type): raise ValueError('app_table can be used only for images loaded to RAM') assert load_addr >= 0 self.load_addr = load_addr self.image_type = image_type alignment = MasterBootImage._IMAGE_ALIGNMENT self.app = misc.align_block(bytes(app), alignment) self.app_table = app_table # hmac + key store self.hmac_key = bytes.fromhex(hmac_key) if isinstance(hmac_key, str) else hmac_key self.key_store = key_store # trust zone self.trust_zone = trust_zone or TrustZone.enabled() # security stuff self.cert_block = cert_block if self.cert_block: self.cert_block.alignment = 4 #type: ignore # this value is used by elf-to-sb-gui self.signature_len = self.cert_block.signature_size #type: ignore else: self.signature_len = 0 self._priv_key_pem_data = priv_key_pem_data self.enable_hw_user_mode_keys = enable_hw_user_mode_keys self.ctr_init_vector = ctr_init_vector if MasterBootImageType.is_encrypted(self.image_type) and not ctr_init_vector: self.ctr_init_vector = crypto_backend().random_bytes(self._CTR_INIT_VECTOR_SIZE) self._verify_private_key() # validate parameters self._validate_new_instance()
def test_boot_section_v2_invalid_export(): boot_section = BootSectionV2(0, CmdErase(address=0, length=100000), CmdLoad(address=0, data=b"0123456789"), CmdReset()) dek = 32 mac = 4 nonce = crypto_backend().random_bytes(16) with pytest.raises(SPSDKError, match="Invalid type of dek, should be bytes"): boot_section.export(dek, mac, Counter(nonce)) dek = crypto_backend().random_bytes(32) with pytest.raises(SPSDKError, match="Invalid type of mac, should be bytes"): boot_section.export(dek, mac, Counter(nonce)) counter = 5 mac = crypto_backend().random_bytes(32) with pytest.raises(SPSDKError, match="Invalid type of counter"): boot_section.export(dek, mac, counter)
def test_bee_region_header() -> None: """Test BeeRegionHeader class""" sw_key = crypto_backend().random_bytes(16) hdr = BeeRegionHeader(sw_key=sw_key) hdr.add_fac(BeeFacRegion(0x00000000, 0x00010000, 0)) verify_base_class_features(hdr, decrypt_key=sw_key) hdr.add_fac(BeeFacRegion(0x10000000, 0x00010000, 1)) hdr.add_fac(BeeFacRegion(0x20000000, 0x00010000, 2)) hdr.add_fac(BeeFacRegion(0xF0000000, 0x00010000, 3)) verify_base_class_features(hdr, decrypt_key=sw_key)
def finalize(self, image: bytes) -> bytes: """Finalize the image for export by adding HMAC a optionally KeyStore. :param image: Input image. :return: Finalized image suitable for export. """ ret = image if self.attach_sign_digest: ret += crypto_backend().hash(image, self.attach_sign_digest) return ret
def __init__( self, signed: bool, kek: bytes, *sections: BootSectionV2, product_version: str = "1.0.0", component_version: str = "1.0.0", build_number: int = 0, advanced_params: SBV2xAdvancedParams = SBV2xAdvancedParams(), ) -> None: """Initialize Secure Boot Image V2.0. :param signed: True if image is signed, False otherwise :param kek: key for wrapping DEK and MAC keys :param product_version: The product version (default: 1.0.0) :param component_version: The component version (default: 1.0.0) :param build_number: The build number value (default: 0) :param advanced_params: Advanced parameters for encryption of the SB file, use for tests only :param sections: Boot sections :raises SPSDKError: Invalid dek or mac """ self._kek = kek # Set Flags value self._signed = signed self._private_key_pem_data: Optional[bytes] = None flags = 0x08 if self.signed else 0x04 # Set private attributes self._dek: bytes = advanced_params.dek self._mac: bytes = advanced_params.mac if ( len(self._dek) != self.HEADER_MAC_SIZE and len(self._mac) != self.HEADER_MAC_SIZE ): # pragma: no cover # condition checked in SBV2xAdvancedParams constructor raise SPSDKError("Invalid dek or mac") self._header = ImageHeaderV2( version="2.0", product_version=product_version, component_version=component_version, build_number=build_number, flags=flags, nonce=advanced_params.nonce, timestamp=advanced_params.timestamp, ) self._cert_section: Optional[CertSectionV2] = None self._boot_sections: List[BootSectionV2] = [] # Generate nonce if self._header.nonce is None: nonce = bytearray(crypto_backend().random_bytes(16)) # clear nonce bit at offsets 31 and 63 nonce[9] &= 0x7F nonce[13] &= 0x7F self._header.nonce = bytes(nonce) # Sections for section in sections: self.add_boot_section(section)
def __init__( self, version: str = "1.0", flags: int = 0, drive_tag: int = 0, product_version: BcdVersion3Format = BcdVersion3.DEFAULT, component_version: BcdVersion3Format = BcdVersion3.DEFAULT, dek: Optional[bytes] = None, mac: Optional[bytes] = None, digest: bytes = b"\0" * 20, timestamp: Optional[datetime] = None, ): """Initialize Secure Boot Image V1.x. :param version: string in format #.# Major version of the boot image format, currently 1. Minor version of the boot image format, currently 1 or 2. :param flags: for the header, 0 by default: Flags associated with the entire image. :param product_version: Product version. :param component_version: Component version. :param drive_tag: For header: identifier for the disk drive or partition containing this image. :param dek: DEK key for encrypted SB file; this is not supported yet :param mac: MAC for encrypted SB file, this is not supported yet :param digest: SHA-1 digest of all fields of the header (it will be updated before export anyway) The first 16 bytes (of 20 total) also act as the initialization vector for CBC-encrypted regions. :param timestamp: datetime of the file creation, use None for current date/time Fixed value should be used only for regression testing to generate same results """ self._dek = dek if dek else crypto_backend().random_bytes(32) self._mac = mac if mac else crypto_backend().random_bytes(32) self._header = SecureBootHeaderV1( version=version, product_version=product_version, component_version=component_version, flags=flags, drive_tag=drive_tag, digest=digest, timestamp=timestamp, ) self._sections_hdr_table: List[SectionHeaderItemV1] = [] self._sections: List[BootSectionV1] = [] self._signature = None
def export( self, header_padding8: Optional[bytes] = None, auth_padding: Optional[bytes] = None, dbg_info: DebugInfo = DebugInfo.disabled() ) -> bytes: """Serialization to binary form. :param header_padding8: optional header padding, 8-bytes; recommended to use None to apply random value :param auth_padding: optional padding used after authentication; recommended to use None to apply random value :param dbg_info: instance allowing to debug generated output :return: serialize the instance into binary data """ self.update() self.validate() dbg_info.append_section('SB-FILE-1.x') data = self._header.export(padding8=header_padding8, dbg_info=dbg_info) # header table dbg_info.append_section('Sections-Header-Table') for sect_hdr in self._sections_hdr_table: sect_hdr_data = sect_hdr.export() dbg_info.append_binary_data('Section-Header-Item', sect_hdr_data) data += sect_hdr_data # sections dbg_info.append_section('Sections') for sect in self._sections: sect_data = sect.export(dbg_info) assert len(sect_data) == sect.size data += sect_data # authentication: SHA1 auth_code = crypto_backend().hash(data, 'sha1') dbg_info.append_binary_section('SHA1', auth_code) data += auth_code # padding padding_len = align(len(auth_code), SecBootBlckSize.BLOCK_SIZE) - len(auth_code) if auth_padding is None: auth_padding = crypto_backend().random_bytes(padding_len) assert padding_len == len(auth_padding) data += auth_padding dbg_info.append_binary_section('padding', auth_padding) return data
def test_boot_section_v2(): boot_section = BootSectionV2(0, CmdErase(address=0, length=100000), CmdLoad(address=0, data=b'0123456789'), CmdReset()) assert boot_section.uid == 0 assert not boot_section.is_last assert boot_section.hmac_count == 1 assert boot_section.raw_size == 144 dek = crypto_backend().random_bytes(32) mac = crypto_backend().random_bytes(32) nonce = crypto_backend().random_bytes(16) data = boot_section.export(dek, mac, Counter(nonce)) assert data assert BootSectionV2.parse(data, 0, False, dek, mac, Counter(nonce)) with pytest.raises(Exception): assert BootSectionV2.parse(data, 0, False, dek, crypto_backend().random_bytes(32), Counter(nonce))
def __init__(self, dek: Optional[bytes] = None, mac: Optional[bytes] = None, nonce: Optional[bytes] = None, timestamp: Optional[datetime] = None): """Initialize SBV2xAdvancedParams. :param dek: DEK key :param mac: MAC key :param nonce: nonce :param timestamp: fixed timestamp for the header; use None to use current date/time """ self._dek: bytes = dek if dek else crypto_backend().random_bytes(32) self._mac: bytes = mac if mac else crypto_backend().random_bytes(32) self._nonce: bytes = nonce if nonce else SBV2xAdvancedParams._create_nonce( ) if timestamp is None: timestamp = datetime.now() self._timestamp = datetime.fromtimestamp(int(timestamp.timestamp())) assert len(self._dek) == 32 and len(self._mac) == 32 assert len(self._nonce) == 16
def compute_hmac(self, data: bytes) -> bytes: """Compute HMAC hash. :param data: Data to be hashed. :return: Result HMAC hash of input data. """ if not self.hmac_key: return bytes() key = KeyStore.derive_hmac_key(self.hmac_key) result = crypto_backend().hmac(key, data) assert len(result) == self.HMAC_SIZE return result
def __init__(self, prdb: Optional[BeeProtectRegionBlock] = None, sw_key: Optional[bytes] = None, kib: Optional[BeeKIB] = None): """Constructor. :param prdb: protect region block; None to use default :param sw_key: key used to encrypt KIB content :param kib: keys block; None to use default """ self._prdb = prdb if (prdb is not None) else BeeProtectRegionBlock() self._sw_key = sw_key if ( sw_key is not None) else crypto_backend().random_bytes(16) self._kib = kib if (kib is not None) else BeeKIB()
def _hmac(self, data: bytes) -> bytes: """Calculate HMAC for provided data. :param data: to calculate hmac :return: calculated hmac; empty bytes if the block does not contain any HMAC """ if not MasterBootImageType.has_hmac(self.image_type): return bytes() assert self.hmac_key and len(self.hmac_key) == self._HMAC_KEY_LENGTH key = KeyStore.derive_hmac_key(self.hmac_key) assert len(key) == self._HMAC_DERIVED_KEY_LEN result = crypto_backend().hmac(key, data) assert len(result) == self.HMAC_SIZE return result
def align_block(data: bytes, alignment: int = 4, padding: int = 0) -> bytes: """Align binary data block length to specified boundary by adding padding bytes to the end. :param data: to be aligned :param alignment: boundary alignment (typically 2, 4, 16, 64 or 256 boundary) :param padding: byte to be added, use -1 to fill with random data :return: aligned block """ assert isinstance(data, bytes) assert alignment > 0 assert -1 <= padding <= 255 curr_size = len(data) num_padding = align(curr_size, alignment) - curr_size if not num_padding: return data if padding == -1: return data + crypto_backend().random_bytes(num_padding) return data + bytes([padding]) * num_padding
def encrypt_block(self, key: bytes, start_addr: int, data: bytes) -> bytes: """Encrypt block located in any FAC region. :param key: user for encryption :param start_addr: start address of the data :param data: binary block to be encrypted; the block size must be BEE_ENCR_BLOCK_SIZE :return: encrypted block if it is inside any FAC region; untouched block if it is not in any FAC region """ assert len(data) == BEE_ENCR_BLOCK_SIZE if self._start_addr <= start_addr < self._end_addr: assert self.mode == BeeProtectRegionBlockAesMode.CTR, 'only AES/CTR encryption mode supported now' assert len(key) == 16 for fac in self.fac_regions: if fac.start_addr <= start_addr < fac.end_addr: assert start_addr + len(data) <= fac.end_addr cntr_key = Counter(self.counter, ctr_value=start_addr >> 4, ctr_byteorder_encoding='big') return crypto_backend().aes_ctr_encrypt( key, data, cntr_key.value) return data