diff --git a/examples/typescript/src/index.html b/examples/typescript/src/index.html index 82d40802..638273e8 100644 --- a/examples/typescript/src/index.html +++ b/examples/typescript/src/index.html @@ -54,6 +54,7 @@

Program

Flash Address + Encrypt file ? File diff --git a/examples/typescript/src/index.ts b/examples/typescript/src/index.ts index 7df3008f..bc0377de 100644 --- a/examples/typescript/src/index.ts +++ b/examples/typescript/src/index.ts @@ -155,35 +155,43 @@ addFileButton.onclick = () => { element1.value = "0x1000"; cell1.appendChild(element1); - // Column 2 - File selector + // Column 2 - Encrypt file ? const cell2 = row.insertCell(1); const element2 = document.createElement("input"); - element2.type = "file"; - element2.id = "selectFile" + rowCount; - element2.name = "selected_File" + rowCount; - element2.addEventListener("change", handleFileSelect, false); + element2.type = "checkbox"; + element2.id = "file_encrypted" + rowCount; + element2.checked = false; cell2.appendChild(element2); - // Column 3 - Progress + // Column 3 - File selector const cell3 = row.insertCell(2); - cell3.classList.add("progress-cell"); - cell3.style.display = "none"; - cell3.innerHTML = ``; - - // Column 4 - Remove File + const element3 = document.createElement("input"); + element3.type = "file"; + element3.id = "selectFile" + rowCount; + element3.name = "selected_File" + rowCount; + element3.addEventListener("change", handleFileSelect, false); + cell3.appendChild(element3); + + // Column 4 - Progress const cell4 = row.insertCell(3); - cell4.classList.add("action-cell"); + cell4.classList.add("progress-cell"); + cell4.style.display = "none"; + cell4.innerHTML = ``; + + // Column 5 - Remove File + const cell5 = row.insertCell(4); + cell5.classList.add("action-cell"); if (rowCount > 1) { - const element4 = document.createElement("input"); - element4.type = "button"; + const element5 = document.createElement("input"); + element5.type = "button"; const btnName = "button" + rowCount; - element4.name = btnName; - element4.setAttribute("class", "btn"); - element4.setAttribute("value", "Remove"); // or element1.value = "button"; - element4.onclick = function () { + element5.name = btnName; + element5.setAttribute("class", "btn"); + element5.setAttribute("value", "Remove"); // or element1.value = "button"; + element5.onclick = function () { removeRow(row); }; - cell4.appendChild(element4); + cell5.appendChild(element5); } }; @@ -299,7 +307,7 @@ function validateProgramInputs() { else if (offsetArr.includes(offset)) return "Offset field in row " + index + " is already in use!"; else offsetArr.push(offset); - const fileObj = row.cells[1].childNodes[0]; + const fileObj = row.cells[2].childNodes[0]; fileData = fileObj.data; if (fileData == null) return "No file selected for row " + index + "!"; } @@ -319,7 +327,7 @@ programButton.onclick = async () => { // Hide error message alertDiv.style.display = "none"; - const fileArray = []; + const fileArray: { data: string; address: number; encrypted: boolean }[] = []; const progressBars = []; for (let index = 1; index < table.rows.length; index++) { @@ -328,16 +336,19 @@ programButton.onclick = async () => { const offSetObj = row.cells[0].childNodes[0] as HTMLInputElement; const offset = parseInt(offSetObj.value); - const fileObj = row.cells[1].childNodes[0] as ChildNode & { data: string }; - const progressBar = row.cells[2].childNodes[0]; + const encryptedObj = row.cells[1].childNodes[0] as HTMLInputElement; + const encryptFlag = encryptedObj.checked; + + const fileObj = row.cells[2].childNodes[0] as ChildNode & { data: string }; + const progressBar = row.cells[3].childNodes[0]; progressBar.textContent = "0"; progressBars.push(progressBar); - row.cells[2].style.display = "initial"; - row.cells[3].style.display = "none"; + row.cells[3].style.display = "initial"; + row.cells[4].style.display = "none"; - fileArray.push({ data: fileObj.data, address: offset }); + fileArray.push({ data: fileObj.data, address: offset, encrypted: encryptFlag }); } try { @@ -358,8 +369,8 @@ programButton.onclick = async () => { } finally { // Hide progress bars and show erase buttons for (let index = 1; index < table.rows.length; index++) { - table.rows[index].cells[2].style.display = "none"; - table.rows[index].cells[3].style.display = "initial"; + table.rows[index].cells[3].style.display = "none"; + table.rows[index].cells[4].style.display = "initial"; } } }; diff --git a/src/esploader.ts b/src/esploader.ts index e47b1900..f6ff6c6d 100644 --- a/src/esploader.ts +++ b/src/esploader.ts @@ -13,10 +13,10 @@ import atob from "atob-lite"; */ export interface FlashOptions { /** - * An array of file objects representing the data to be flashed. - * @type {Array<{ data: string; address: number }>} + * An array of file objects representing the data to be flashed, address offset and if to be encrypted. + * @type {Array<{ data: string; address: number, encrypted: boolean }>} */ - fileArray: { data: string; address: number }[]; + fileArray: { data: string; address: number; encrypted: boolean }[]; /** * The size of the flash memory to be used. @@ -207,12 +207,17 @@ export class ESPLoader { ESP_READ_FLASH = 0xd2; ESP_RUN_USER_CODE = 0xd3; + ESP_FLASH_ENCRYPT_DATA = 0xd4; + ESP_IMAGE_MAGIC = 0xe9; ESP_CHECKSUM_MAGIC = 0xef; // Response code(s) sent by ROM ROM_INVALID_RECV_MSG = 0x05; // response if an invalid message is received + // Commands supported by ESP32-S2 and later chips ROM bootloader only + ESP_GET_SECURITY_INFO = 0x14; + ERASE_REGION_TIMEOUT_PER_MB = 30000; ERASE_WRITE_TIMEOUT_PER_MB = 40000; MD5_TIMEOUT_PER_MB = 8000; @@ -255,6 +260,7 @@ export class ESPLoader { private romBaudrate = 115200; private debugLogging = false; private syncStubDetected = false; + private secureDownloadMode = false; /** * Create a new ESPLoader to perform serial communication @@ -266,7 +272,7 @@ export class ESPLoader { */ constructor(options: LoaderOptions) { this.IS_STUB = false; - this.FLASH_WRITE_SIZE = 0x4000; + this.FLASH_WRITE_SIZE = 0x400; this.transport = options.transport; this.baudrate = options.baudrate; @@ -670,13 +676,21 @@ export class ESPLoader { this.info("\n\r", false); if (!detecting) { - const chipMagicValue = (await this.readReg(0x40001000)) >>> 0; - this.debug("Chip Magic " + chipMagicValue.toString(16)); - const chip = await magic2Chip(chipMagicValue); - if (this.chip === null) { - throw new ESPError(`Unexpected CHIP magic value ${chipMagicValue}. Failed to autodetect chip type.`); - } else { - this.chip = chip as ROM; + try { + const chipMagicValue = (await this.readReg(this.CHIP_DETECT_MAGIC_REG_ADDR)) >>> 0; + this.debug("Chip Magic " + chipMagicValue.toString(16)); + const chip = await magic2Chip(chipMagicValue); + if (this.chip === null) { + throw new ESPError(`Unexpected CHIP magic value ${chipMagicValue}. Failed to autodetect chip type.`); + } else { + this.chip = chip as ROM; + } + } catch (error) { + if (error instanceof ESPError && error.message && error.message.indexOf("unsupported command error") !== -1) { + this.secureDownloadMode = true; + } else { + throw error; + } } } } @@ -695,6 +709,36 @@ export class ESPLoader { } } + /** + * Get the chip ID using the check command + * @returns {number} Chip ID + */ + async getChipId() { + const securityInfo = await this.getSecurityInfo(); + return securityInfo.chipId; + } + + /** + * Get the chip security information + * @returns {number} Chip Security Information + */ + async getSecurityInfo() { + const res = (await this.checkCommand( + "get security info", + this.ESP_GET_SECURITY_INFO, + new Uint8Array(0), + 0, + )) as Uint8Array; + const esp32s2 = res && res.length === 12; + return { + flags: res[0], + flashCryptCnt: res[1], + keyPurposes: res.slice(2, 9), + chipId: esp32s2 ? undefined : res[9], + apiVersion: esp32s2 ? undefined : res[10], + }; + } + /** * Execute the command and check the command response. * @param {string} opDescription Command operation description. @@ -780,7 +824,10 @@ export class ESPLoader { * @param {number} hspiArg - Argument for SPI attachment */ async flashSpiAttach(hspiArg: number) { - const pkt = this._intToByteArray(hspiArg); + let pkt = this._intToByteArray(hspiArg); + if (!this.IS_STUB) { + pkt = this._appendArray(pkt, this._intToByteArray(0)); + } await this.checkCommand("configure SPI flash pins", this.ESP_SPI_ATTACH, pkt); } @@ -803,9 +850,10 @@ export class ESPLoader { * Start downloading to Flash (performs an erase) * @param {number} size Size to erase * @param {number} offset Offset to erase + * @param {boolean} encrypt Flag if encrypt * @returns {number} Number of blocks (of size self.FLASH_WRITE_SIZE) to write. */ - async flashBegin(size: number, offset: number) { + async flashBegin(size: number, offset: number, encrypt = false) { const numBlocks = Math.floor((size + this.FLASH_WRITE_SIZE - 1) / this.FLASH_WRITE_SIZE); const eraseSize = this.chip.getEraseSize(offset, size); @@ -822,7 +870,7 @@ export class ESPLoader { pkt = this._appendArray(pkt, this._intToByteArray(this.FLASH_WRITE_SIZE)); pkt = this._appendArray(pkt, this._intToByteArray(offset)); if (this.IS_STUB == false) { - pkt = this._appendArray(pkt, this._intToByteArray(0)); // XXX: Support encrypted + pkt = this._appendArray(pkt, this._intToByteArray(encrypt ? 1 : 0)); } await this.checkCommand("enter Flash download mode", this.ESP_FLASH_BEGIN, pkt, undefined, timeout); @@ -896,6 +944,31 @@ export class ESPLoader { await this.checkCommand("write to target Flash after seq " + seq, this.ESP_FLASH_DATA, pkt, checksum, timeout); } + /** + * Encrypt, write block to flash, retry if fail + * @param {Uint8Array} data Unsigned 8-bit array data. + * @param {number} seq Sequence number + * @param {number} timeout Timeout in milliseconds (ms) + */ + async flashEncryptBlock(data: Uint8Array, seq: number, timeout: number) { + if (this.chip.SUPPORTS_ENCRYPTED_FLASH && !this.IS_STUB) { + await this.flashBlock(data, seq, timeout); + } else { + let pkt = this._appendArray(this._intToByteArray(data.length), this._intToByteArray(seq)); + pkt = this._appendArray(pkt, this._intToByteArray(0)); + pkt = this._appendArray(pkt, this._intToByteArray(0)); + pkt = this._appendArray(pkt, data); + const checksum = this.checksum(data); + await this.checkCommand( + "Write encrypted to target Flash after seq " + seq, + this.ESP_FLASH_ENCRYPT_DATA, + pkt, + checksum, + timeout, + ); + } + } + /** * Write block to flash, send compressed, retry if fail * @param {Uint8Array} data Unsigned int 8-bit array data to write @@ -1335,13 +1408,13 @@ export class ESPLoader { aFlashSize = this.parseFlashSizeArg(flashSize); } - const flashParams = (aFlashMode << 8) | (aFlashFreq + aFlashSize); + const flashParams = (aFlashMode << 8) | (aFlashSize + aFlashFreq); this.info("Flash params set to " + flashParams.toString(16)); if (parseInt(image[2]) !== aFlashMode << 8) { image = image.substring(0, 2) + (aFlashMode << 8).toString() + image.substring(2 + 1); } - if (parseInt(image[3]) !== aFlashFreq + aFlashSize) { - image = image.substring(0, 3) + (aFlashFreq + aFlashSize).toString() + image.substring(3 + 1); + if (parseInt(image[3]) !== aFlashSize + aFlashFreq) { + image = image.substring(0, 3) + (aFlashSize + aFlashFreq).toString() + image.substring(3 + 1); } return image; } @@ -1361,12 +1434,68 @@ export class ESPLoader { } } + const encryptedFiles = options.fileArray.filter((f) => f.encrypted); + + if (encryptedFiles && encryptedFiles.length > 0) { + let doWrite = true; + const isEncryptedDownloadDisabled = await this.chip.getEncryptedDownloadDisabled(this); + if (!this.secureDownloadMode && isEncryptedDownloadDisabled) { + const errMsg = + "This chip has encrypt functionality \n" + + "in UART download mode disabled. \n" + + "This is the Flash Encryption configuration for Production mode \n" + + "instead of Development mode."; + this.debug(errMsg); + throw new ESPError(errMsg); + } + for (const f of encryptedFiles) { + if (f.encrypted && this.chip.SUPPORTS_ENCRYPTED_FLASH && f.address % this.chip.FLASH_ENCRYPTED_WRITE_ALIGN) { + doWrite = false; + this.info(`File at address ${f.address} is not %d byte aligned, can't flash encrypted`); + } + } + if (!doWrite) { + const errMsg = + "Can't perform encrypted flash write,\n" + "consult Flash Encryption documentation for more information"; + this.debug(errMsg); + throw new ESPError(errMsg); + } + } else if (this.chip.CHIP_NAME !== "ESP8266") { + const flashSecurityInfo = await this.getSecurityInfo(); + const flashCryptCntBinaryString = flashSecurityInfo.flashCryptCnt.toString(2); + const onesCount = flashCryptCntBinaryString.split("").filter((char) => char === "1").length & 1; + + if (this.chip.CHIP_NAME !== "ESP32" && this.secureDownloadMode && onesCount !== 0) { + throw new ESPError( + "WARNING: Detected flash encryption and " + + "secure download mode enabled.\n" + + "Flashing plaintext binary may brick your device! ", + ); + } + const isEncryptedDownloadDisabled = await this.chip.getEncryptedDownloadDisabled(this); + const isFlashEncryptionEnabled = await this.chip.getFlashEncryptionEnabled(this); + + if (!this.secureDownloadMode && isEncryptedDownloadDisabled && isFlashEncryptionEnabled) { + throw new ESPError( + "WARNING: Detected flash encryption enabled and " + + "download manual encrypt disabled.\n" + + "Flashing plaintext binary may brick your device! " + + "Use --force to override the warning.", + ); + } + } + if (this.IS_STUB === true && options.eraseAll === true) { await this.eraseFlash(); } let image: string, address: number; for (let i = 0; i < options.fileArray.length; i++) { this.debug("Data Length " + options.fileArray[i].data.length); + + let compress = options.compress; + if (compress && options.fileArray[i].encrypted) { + compress = false; + } image = options.fileArray[i].data; const reminder = options.fileArray[i].data.length % 4; if (reminder > 0) image += "\xff\xff\xff\xff".substring(4 - reminder); @@ -1384,12 +1513,12 @@ export class ESPLoader { } const uncsize = image.length; let blocks: number; - if (options.compress) { + if (compress) { const uncimage = this.bstrToUi8(image); image = this.ui8ToBstr(deflate(uncimage, { level: 9 })); blocks = await this.flashDeflBegin(uncsize, image.length, address); } else { - blocks = await this.flashBegin(uncsize, address); + blocks = await this.flashBegin(uncsize, address, options.fileArray[i].encrypted); } let seq = 0; let bytesSent = 0; @@ -1418,7 +1547,7 @@ export class ESPLoader { ); const block = this.bstrToUi8(image.slice(0, this.FLASH_WRITE_SIZE)); - if (options.compress) { + if (compress) { const lenUncompressedPrevious = totalLenUncompressed; inflate.push(block, false); const blockUncompressed = totalLenUncompressed - lenUncompressedPrevious; @@ -1436,7 +1565,11 @@ export class ESPLoader { timeout = blockTimeout; } } else { - throw new ESPError("Yet to handle Non Compressed writes"); + if (options.fileArray[i].encrypted) { + await this.flashEncryptBlock(block, seq, timeout); + } else { + await this.flashBlock(block, seq, timeout); + } } bytesSent += block.length; image = image.slice(this.FLASH_WRITE_SIZE, image.length); @@ -1448,7 +1581,7 @@ export class ESPLoader { } d = new Date(); const t = d.getTime() - t1; - if (options.compress) { + if (compress) { this.info( "Wrote " + uncsize + @@ -1461,7 +1594,7 @@ export class ESPLoader { " seconds.", ); } - if (calcmd5) { + if (calcmd5 && !options.fileArray[i].encrypted && !this.secureDownloadMode) { const res = await this.flashMd5sum(address, uncsize); if (new String(res).valueOf() != new String(calcmd5).valueOf()) { this.info("File md5: " + calcmd5); @@ -1476,7 +1609,8 @@ export class ESPLoader { if (this.IS_STUB) { await this.flashBegin(0, 0); - if (options.compress) { + const lastFileIndex = options.fileArray && options.fileArray.length ? options.fileArray.length - 1 : 0; + if (options.compress && lastFileIndex && !options.fileArray[lastFileIndex].encrypted) { await this.flashDeflFinish(); } else { await this.flashFinish(); @@ -1496,11 +1630,34 @@ export class ESPLoader { this.info("Detected flash size: " + this.DETECTED_FLASH_SIZES[flidLowbyte]); } - async getFlashSize() { - this.debug("flash_id"); + async detectFlashSize(options?: FlashOptions) { + this.debug("flash_size"); + if (this.secureDownloadMode) { + if (options && options.flashSize === "detect") { + const errMsg = + "Detecting flash size is not supported in secure download mode. Need to manually specify flash size."; + this.debug(errMsg); + throw new ESPError(errMsg); + } else { + return; + } + } const flashid = await this.readFlashId(); const flidLowbyte = (flashid >> 16) & 0xff; - return this.DETECTED_FLASH_SIZES_NUM[flidLowbyte]; + let flashSize = this.DETECTED_FLASH_SIZES[flidLowbyte]; + + if (options && options.flashSize === "detect") { + if (!flashSize) { + this.info( + `WARNING: Could not auto-detect Flash size. FlashID: ${flashid}, SizeId: ${flashSize}. Defaulting to 4MB.`, + ); + flashSize = "4MB"; + } else { + this.info(`Auto-detected Flash size: ${flashSize}`); + } + options.flashSize = flashSize; + } + return flashSize; } /** diff --git a/src/targets/esp32.ts b/src/targets/esp32.ts index 6ef67f3c..9f942855 100644 --- a/src/targets/esp32.ts +++ b/src/targets/esp32.ts @@ -37,6 +37,43 @@ export class ESP32ROM extends ROM { public ROM_DATA = ESP32_STUB.data; public ROM_TEXT = ESP32_STUB.text; + public SUPPORTS_ENCRYPTED_FLASH = false; + public FLASH_ENCRYPTED_WRITE_ALIGN = 32; + + public EFUSE_DIS_DOWNLOAD_MANUAL_ENCRYPT_REG = this.EFUSE_RD_REG_BASE + 0x18; + public EFUSE_DIS_DOWNLOAD_MANUAL_ENCRYPT = 1 << 7; // EFUSE_RD_DISABLE_DL_ENCRYPT; + + public EFUSE_SPI_BOOT_CRYPT_CNT_REG = this.EFUSE_RD_REG_BASE; // EFUSE_BLK0_WDATA0_REG + public EFUSE_SPI_BOOT_CRYPT_CNT_MASK = 0x7f << 20; // EFUSE_FLASH_CRYPT_CNT + + public EFUSE_RD_ABS_DONE_REG = this.EFUSE_RD_REG_BASE + 0x018; + public EFUSE_RD_ABS_DONE_0_MASK = 1 << 4; + public EFUSE_RD_ABS_DONE_1_MASK = 1 << 5; + + public async getSecureBootEnabled(loader: ESPLoader): Promise { + const efuses = await loader.readReg(this.EFUSE_RD_ABS_DONE_REG); + const rev = await this.getChipRevision(loader); + return ( + (efuses & this.EFUSE_RD_ABS_DONE_0_MASK) !== 0 || (rev >= 300 && efuses & this.EFUSE_RD_ABS_DONE_1_MASK) !== 0 + ); + } + + public async getEncryptedDownloadDisabled(loader: ESPLoader): Promise { + return ( + ((await loader.readReg(this.EFUSE_DIS_DOWNLOAD_MANUAL_ENCRYPT_REG)) & this.EFUSE_DIS_DOWNLOAD_MANUAL_ENCRYPT) !== + 0 + ); + } + + public async getFlashEncryptionEnabled(loader: ESPLoader): Promise { + const flashCryptCounter = + (await loader.readReg(this.EFUSE_SPI_BOOT_CRYPT_CNT_REG)) & this.EFUSE_SPI_BOOT_CRYPT_CNT_MASK; + + const binaryString = flashCryptCounter.toString(2); + const onesCount = binaryString.split("").filter((char) => char === "1").length & 1; + return onesCount !== 0; + } + public async readEfuse(loader: ESPLoader, offset: number): Promise { const addr = this.EFUSE_RD_REG_BASE + 4 * offset; loader.debug("Read efuse " + addr); diff --git a/src/targets/esp32c2.ts b/src/targets/esp32c2.ts index 17620ab1..b2912985 100644 --- a/src/targets/esp32c2.ts +++ b/src/targets/esp32c2.ts @@ -37,6 +37,36 @@ export class ESP32C2ROM extends ESP32C3ROM { public ROM_DATA = ESP32C2_STUB.data; public ROM_TEXT = ESP32C2_STUB.text; + public EFUSE_DIS_DOWNLOAD_MANUAL_ENCRYPT_REG = this.EFUSE_BASE + 0x30; + public EFUSE_DIS_DOWNLOAD_MANUAL_ENCRYPT = 1 << 6; + + public EFUSE_SPI_BOOT_CRYPT_CNT_REG = this.EFUSE_BASE + 0x30; // EFUSE_BLK0_WDATA0_REG + public EFUSE_SPI_BOOT_CRYPT_CNT_MASK = 0x7 << 18; // EFUSE_FLASH_CRYPT_CNT + + public EFUSE_SECURE_BOOT_EN_REG = this.EFUSE_BASE + 0x038; + public EFUSE_SECURE_BOOT_EN_MASK = 1 << 20; + + public async getSecureBootEnabled(loader: ESPLoader): Promise { + const secureBootEnableReg = await loader.readReg(this.EFUSE_SECURE_BOOT_EN_REG); + return (secureBootEnableReg & this.EFUSE_SECURE_BOOT_EN_MASK) !== 0; + } + + public async getEncryptedDownloadDisabled(loader: ESPLoader): Promise { + return ( + ((await loader.readReg(this.EFUSE_DIS_DOWNLOAD_MANUAL_ENCRYPT_REG)) & this.EFUSE_DIS_DOWNLOAD_MANUAL_ENCRYPT) !== + 0 + ); + } + + public async getFlashEncryptionEnabled(loader: ESPLoader): Promise { + const flashCryptCounter = + (await loader.readReg(this.EFUSE_SPI_BOOT_CRYPT_CNT_REG)) & this.EFUSE_SPI_BOOT_CRYPT_CNT_MASK; + + const binaryString = flashCryptCounter.toString(2); + const onesCount = binaryString.split("").filter((char) => char === "1").length & 1; + return onesCount !== 0; + } + public async getPkgVersion(loader: ESPLoader): Promise { const numWord = 1; const block1Addr = this.EFUSE_BASE + 0x040; diff --git a/src/targets/esp32c3.ts b/src/targets/esp32c3.ts index af65a081..4af37a94 100644 --- a/src/targets/esp32c3.ts +++ b/src/targets/esp32c3.ts @@ -36,6 +36,40 @@ export class ESP32C3ROM extends ROM { public ROM_DATA = ESP32C3_STUB.data; public ROM_TEXT = ESP32C3_STUB.text; + public SUPPORTS_ENCRYPTED_FLASH = true; + public FLASH_ENCRYPTED_WRITE_ALIGN = 16; + + public EFUSE_RD_REG_BASE = this.EFUSE_BASE + 0x030; + public EFUSE_DIS_DOWNLOAD_MANUAL_ENCRYPT_REG = this.EFUSE_RD_REG_BASE + 0x30; + public EFUSE_DIS_DOWNLOAD_MANUAL_ENCRYPT = 1 << 20; + + public EFUSE_SPI_BOOT_CRYPT_CNT_REG = this.EFUSE_BASE + 0x034; // EFUSE_BLK0_WDATA0_REG + public EFUSE_SPI_BOOT_CRYPT_CNT_MASK = 0x7 << 18; // EFUSE_FLASH_CRYPT_CNT + + public EFUSE_SECURE_BOOT_EN_REG = this.EFUSE_BASE + 0x038; + public EFUSE_SECURE_BOOT_EN_MASK = 1 << 20; + + public async getSecureBootEnabled(loader: ESPLoader): Promise { + const secureBootEnableReg = await loader.readReg(this.EFUSE_SECURE_BOOT_EN_REG); + return (secureBootEnableReg & this.EFUSE_SECURE_BOOT_EN_MASK) !== 0; + } + + public async getEncryptedDownloadDisabled(loader: ESPLoader): Promise { + return ( + ((await loader.readReg(this.EFUSE_DIS_DOWNLOAD_MANUAL_ENCRYPT_REG)) & this.EFUSE_DIS_DOWNLOAD_MANUAL_ENCRYPT) !== + 0 + ); + } + + public async getFlashEncryptionEnabled(loader: ESPLoader): Promise { + const flashCryptCounter = + (await loader.readReg(this.EFUSE_SPI_BOOT_CRYPT_CNT_REG)) & this.EFUSE_SPI_BOOT_CRYPT_CNT_MASK; + + const binaryString = flashCryptCounter.toString(2); + const onesCount = binaryString.split("").filter((char) => char === "1").length & 1; + return onesCount !== 0; + } + public async getPkgVersion(loader: ESPLoader): Promise { const numWord = 3; const block1Addr = this.EFUSE_BASE + 0x044; diff --git a/src/targets/esp32c6.ts b/src/targets/esp32c6.ts index 6d82e3bd..326c6c82 100644 --- a/src/targets/esp32c6.ts +++ b/src/targets/esp32c6.ts @@ -36,6 +36,39 @@ export class ESP32C6ROM extends ROM { public ROM_DATA = ESP32C6_STUB.data; public ROM_TEXT = ESP32C6_STUB.text; + public SUPPORTS_ENCRYPTED_FLASH = true; + public FLASH_ENCRYPTED_WRITE_ALIGN = 16; + + public EFUSE_DIS_DOWNLOAD_MANUAL_ENCRYPT_REG = this.EFUSE_BASE + 0x30; + public EFUSE_DIS_DOWNLOAD_MANUAL_ENCRYPT = 1 << 20; + + public EFUSE_SPI_BOOT_CRYPT_CNT_REG = this.EFUSE_BASE + 0x034; // EFUSE_BLK0_WDATA0_REG + public EFUSE_SPI_BOOT_CRYPT_CNT_MASK = 0x7 << 18; // EFUSE_FLASH_CRYPT_CNT + + public EFUSE_SECURE_BOOT_EN_REG = this.EFUSE_BASE + 0x038; + public EFUSE_SECURE_BOOT_EN_MASK = 1 << 20; + + public async getSecureBootEnabled(loader: ESPLoader): Promise { + const secureBootEnableReg = await loader.readReg(this.EFUSE_SECURE_BOOT_EN_REG); + return (secureBootEnableReg & this.EFUSE_SECURE_BOOT_EN_MASK) !== 0; + } + + public async getEncryptedDownloadDisabled(loader: ESPLoader): Promise { + return ( + ((await loader.readReg(this.EFUSE_DIS_DOWNLOAD_MANUAL_ENCRYPT_REG)) & this.EFUSE_DIS_DOWNLOAD_MANUAL_ENCRYPT) !== + 0 + ); + } + + public async getFlashEncryptionEnabled(loader: ESPLoader): Promise { + const flashCryptCounter = + (await loader.readReg(this.EFUSE_SPI_BOOT_CRYPT_CNT_REG)) & this.EFUSE_SPI_BOOT_CRYPT_CNT_MASK; + + const binaryString = flashCryptCounter.toString(2); + const onesCount = binaryString.split("").filter((char) => char === "1").length & 1; + return onesCount !== 0; + } + public async getPkgVersion(loader: ESPLoader) { const numWord = 3; const block1Addr = this.EFUSE_BASE + 0x044; diff --git a/src/targets/esp32h2.ts b/src/targets/esp32h2.ts index 4d2ccfaf..d0e4d9a4 100644 --- a/src/targets/esp32h2.ts +++ b/src/targets/esp32h2.ts @@ -40,6 +40,39 @@ export class ESP32H2ROM extends ROM { public ROM_DATA = ESP32H2_STUB.data; public ROM_TEXT = ESP32H2_STUB.text; + public SUPPORTS_ENCRYPTED_FLASH = true; + public FLASH_ENCRYPTED_WRITE_ALIGN = 16; + + public EFUSE_DIS_DOWNLOAD_MANUAL_ENCRYPT_REG = this.EFUSE_BASE + 0x30; + public EFUSE_DIS_DOWNLOAD_MANUAL_ENCRYPT = 1 << 20; + + public EFUSE_SPI_BOOT_CRYPT_CNT_REG = this.EFUSE_BASE + 0x034; // EFUSE_BLK0_WDATA0_REG + public EFUSE_SPI_BOOT_CRYPT_CNT_MASK = 0x7 << 18; // EFUSE_FLASH_CRYPT_CNT + + public EFUSE_SECURE_BOOT_EN_REG = this.EFUSE_BASE + 0x038; + public EFUSE_SECURE_BOOT_EN_MASK = 1 << 20; + + public async getSecureBootEnabled(loader: ESPLoader): Promise { + const secureBootEnableReg = await loader.readReg(this.EFUSE_SECURE_BOOT_EN_REG); + return (secureBootEnableReg & this.EFUSE_SECURE_BOOT_EN_MASK) !== 0; + } + + public async getEncryptedDownloadDisabled(loader: ESPLoader): Promise { + return ( + ((await loader.readReg(this.EFUSE_DIS_DOWNLOAD_MANUAL_ENCRYPT_REG)) & this.EFUSE_DIS_DOWNLOAD_MANUAL_ENCRYPT) !== + 0 + ); + } + + public async getFlashEncryptionEnabled(loader: ESPLoader): Promise { + const flashCryptCounter = + (await loader.readReg(this.EFUSE_SPI_BOOT_CRYPT_CNT_REG)) & this.EFUSE_SPI_BOOT_CRYPT_CNT_MASK; + + const binaryString = flashCryptCounter.toString(2); + const onesCount = binaryString.split("").filter((char) => char === "1").length & 1; + return onesCount !== 0; + } + public async getChipDescription(loader: ESPLoader) { return this.CHIP_NAME; } diff --git a/src/targets/esp32s2.ts b/src/targets/esp32s2.ts index f23387a3..f4e84802 100644 --- a/src/targets/esp32s2.ts +++ b/src/targets/esp32s2.ts @@ -36,6 +36,39 @@ export class ESP32S2ROM extends ROM { public ROM_DATA = ESP32S2_STUB.data; public ROM_TEXT = ESP32S2_STUB.text; + public SUPPORTS_ENCRYPTED_FLASH = true; + public FLASH_ENCRYPTED_WRITE_ALIGN = 16; + + public EFUSE_DIS_DOWNLOAD_MANUAL_ENCRYPT_REG = this.EFUSE_BASE + 0x30; + public EFUSE_DIS_DOWNLOAD_MANUAL_ENCRYPT = 1 << 19; + + public EFUSE_SPI_BOOT_CRYPT_CNT_REG = this.EFUSE_BASE + 0x034; // EFUSE_BLK0_WDATA0_REG + public EFUSE_SPI_BOOT_CRYPT_CNT_MASK = 0x7 << 18; // EFUSE_FLASH_CRYPT_CNT + + public EFUSE_SECURE_BOOT_EN_REG = this.EFUSE_BASE + 0x038; + public EFUSE_SECURE_BOOT_EN_MASK = 1 << 20; + + public async getSecureBootEnabled(loader: ESPLoader): Promise { + const secureBootEnableReg = await loader.readReg(this.EFUSE_SECURE_BOOT_EN_REG); + return (secureBootEnableReg & this.EFUSE_SECURE_BOOT_EN_MASK) !== 0; + } + + public async getEncryptedDownloadDisabled(loader: ESPLoader): Promise { + return ( + ((await loader.readReg(this.EFUSE_DIS_DOWNLOAD_MANUAL_ENCRYPT_REG)) & this.EFUSE_DIS_DOWNLOAD_MANUAL_ENCRYPT) !== + 0 + ); + } + + public async getFlashEncryptionEnabled(loader: ESPLoader): Promise { + const flashCryptCounter = + (await loader.readReg(this.EFUSE_SPI_BOOT_CRYPT_CNT_REG)) & this.EFUSE_SPI_BOOT_CRYPT_CNT_MASK; + + const binaryString = flashCryptCounter.toString(2); + const onesCount = binaryString.split("").filter((char) => char === "1").length & 1; + return onesCount !== 0; + } + public async getPkgVersion(loader: ESPLoader): Promise { const numWord = 3; const block1Addr = this.EFUSE_BASE + 0x044; diff --git a/src/targets/esp32s3.ts b/src/targets/esp32s3.ts index 583a3f87..f506be29 100644 --- a/src/targets/esp32s3.ts +++ b/src/targets/esp32s3.ts @@ -40,6 +40,39 @@ export class ESP32S3ROM extends ROM { public ROM_DATA = ESP32S3_STUB.data; public ROM_TEXT = ESP32S3_STUB.text; + public SUPPORTS_ENCRYPTED_FLASH = true; + public FLASH_ENCRYPTED_WRITE_ALIGN = 16; + + public EFUSE_DIS_DOWNLOAD_MANUAL_ENCRYPT_REG = this.EFUSE_BASE + 0x30; + public EFUSE_DIS_DOWNLOAD_MANUAL_ENCRYPT = 1 << 20; + + public EFUSE_SPI_BOOT_CRYPT_CNT_REG = this.EFUSE_BASE + 0x034; // EFUSE_BLK0_WDATA0_REG + public EFUSE_SPI_BOOT_CRYPT_CNT_MASK = 0x7 << 18; // EFUSE_FLASH_CRYPT_CNT + + public EFUSE_SECURE_BOOT_EN_REG = this.EFUSE_BASE + 0x038; + public EFUSE_SECURE_BOOT_EN_MASK = 1 << 20; + + public async getSecureBootEnabled(loader: ESPLoader): Promise { + const secureBootEnableReg = await loader.readReg(this.EFUSE_SECURE_BOOT_EN_REG); + return (secureBootEnableReg & this.EFUSE_SECURE_BOOT_EN_MASK) !== 0; + } + + public async getEncryptedDownloadDisabled(loader: ESPLoader): Promise { + return ( + ((await loader.readReg(this.EFUSE_DIS_DOWNLOAD_MANUAL_ENCRYPT_REG)) & this.EFUSE_DIS_DOWNLOAD_MANUAL_ENCRYPT) !== + 0 + ); + } + + public async getFlashEncryptionEnabled(loader: ESPLoader): Promise { + const flashCryptCounter = + (await loader.readReg(this.EFUSE_SPI_BOOT_CRYPT_CNT_REG)) & this.EFUSE_SPI_BOOT_CRYPT_CNT_MASK; + + const binaryString = flashCryptCounter.toString(2); + const onesCount = binaryString.split("").filter((char) => char === "1").length & 1; + return onesCount !== 0; + } + public async getChipDescription(loader: ESPLoader) { return "ESP32-S3"; } diff --git a/src/targets/esp8266.ts b/src/targets/esp8266.ts index 96ba8df5..75740673 100644 --- a/src/targets/esp8266.ts +++ b/src/targets/esp8266.ts @@ -42,6 +42,9 @@ export class ESP8266ROM extends ROM { public ROM_DATA = ESP8266_STUB.data; public ROM_TEXT = ESP8266_STUB.text; + public SUPPORTS_ENCRYPTED_FLASH = false; + public FLASH_ENCRYPTED_WRITE_ALIGN = 16; + public async readEfuse(loader: ESPLoader, offset: number): Promise { const addr = this.EFUSE_RD_REG_BASE + 4 * offset; loader.debug("Read efuse " + addr); @@ -135,4 +138,16 @@ export class ESP8266ROM extends ROM { public getEraseSize(offset: number, size: number) { return size; } + + public async getEncryptedDownloadDisabled(loader: ESPLoader): Promise { + return false; + } + + public async getFlashEncryptionEnabled(loader: ESPLoader): Promise { + return false; + } + + public async getSecureBootEnabled(loader: ESPLoader) { + return false; + } } diff --git a/src/targets/rom.ts b/src/targets/rom.ts index 02146ded..4fcf417e 100644 --- a/src/targets/rom.ts +++ b/src/targets/rom.ts @@ -75,6 +75,24 @@ export abstract class ROM { return size; } + /** + * Check if download has been disabled due to encryption + * @returns {boolean} Is encrypted download disabled (EFUSE_RD_DISABLE_DL_ENCRYPT). + */ + abstract getEncryptedDownloadDisabled(loader: ESPLoader): Promise; + + /** + * Check if flash encryption is enabled + * @returns {boolean} Is flash encryption enabled (EFUSE_FLASH_CRYPT_CNT). + */ + abstract getFlashEncryptionEnabled(loader: ESPLoader): Promise; + + /** + * Check if secure boot is enabled + * @returns {number} Is Secure boot enabled (EFUSE_RD_ABS_DONE_REG). + */ + abstract getSecureBootEnabled(loader: ESPLoader): Promise; + abstract FLASH_SIZES: { [key: string]: number }; abstract BOOTLOADER_FLASH_OFFSET: number; @@ -100,4 +118,7 @@ export abstract class ROM { abstract UART_DATE_REG_ADDR: number; // not in esp8266 abstract TEXT_START: number; // abstract XTAL_CLK_DIVIDER: number; //esp32 + + abstract SUPPORTS_ENCRYPTED_FLASH: boolean; + abstract FLASH_ENCRYPTED_WRITE_ALIGN: number; }