diff --git a/code/.gitignore b/code/.gitignore new file mode 100644 index 0000000..ede80b8 --- /dev/null +++ b/code/.gitignore @@ -0,0 +1,2 @@ +# created by virtualenv automatically +* diff --git a/code/.venv/.gitignore b/code/.venv/.gitignore new file mode 100644 index 0000000..ede80b8 --- /dev/null +++ b/code/.venv/.gitignore @@ -0,0 +1,2 @@ +# created by virtualenv automatically +* diff --git a/code/.venv/bin/__pycache__/bin2hex.cpython-312.pyc b/code/.venv/bin/__pycache__/bin2hex.cpython-312.pyc new file mode 100644 index 0000000..128cdad Binary files /dev/null and b/code/.venv/bin/__pycache__/bin2hex.cpython-312.pyc differ diff --git a/code/.venv/bin/__pycache__/esp_rfc2217_server.cpython-312.pyc b/code/.venv/bin/__pycache__/esp_rfc2217_server.cpython-312.pyc new file mode 100644 index 0000000..0107253 Binary files /dev/null and b/code/.venv/bin/__pycache__/esp_rfc2217_server.cpython-312.pyc differ diff --git a/code/.venv/bin/__pycache__/espefuse.cpython-312.pyc b/code/.venv/bin/__pycache__/espefuse.cpython-312.pyc new file mode 100644 index 0000000..87d9689 Binary files /dev/null and b/code/.venv/bin/__pycache__/espefuse.cpython-312.pyc differ diff --git a/code/.venv/bin/__pycache__/espsecure.cpython-312.pyc b/code/.venv/bin/__pycache__/espsecure.cpython-312.pyc new file mode 100644 index 0000000..a8a4d33 Binary files /dev/null and b/code/.venv/bin/__pycache__/espsecure.cpython-312.pyc differ diff --git a/code/.venv/bin/__pycache__/esptool.cpython-312.pyc b/code/.venv/bin/__pycache__/esptool.cpython-312.pyc new file mode 100644 index 0000000..d52b6b0 Binary files /dev/null and b/code/.venv/bin/__pycache__/esptool.cpython-312.pyc differ diff --git a/code/.venv/bin/__pycache__/hex2bin.cpython-312.pyc b/code/.venv/bin/__pycache__/hex2bin.cpython-312.pyc new file mode 100644 index 0000000..50c8757 Binary files /dev/null and b/code/.venv/bin/__pycache__/hex2bin.cpython-312.pyc differ diff --git a/code/.venv/bin/__pycache__/hex2dump.cpython-312.pyc b/code/.venv/bin/__pycache__/hex2dump.cpython-312.pyc new file mode 100644 index 0000000..f21d6e3 Binary files /dev/null and b/code/.venv/bin/__pycache__/hex2dump.cpython-312.pyc differ diff --git a/code/.venv/bin/__pycache__/hexdiff.cpython-312.pyc b/code/.venv/bin/__pycache__/hexdiff.cpython-312.pyc new file mode 100644 index 0000000..ed73800 Binary files /dev/null and b/code/.venv/bin/__pycache__/hexdiff.cpython-312.pyc differ diff --git a/code/.venv/bin/__pycache__/hexinfo.cpython-312.pyc b/code/.venv/bin/__pycache__/hexinfo.cpython-312.pyc new file mode 100644 index 0000000..c060b33 Binary files /dev/null and b/code/.venv/bin/__pycache__/hexinfo.cpython-312.pyc differ diff --git a/code/.venv/bin/__pycache__/hexmerge.cpython-312.pyc b/code/.venv/bin/__pycache__/hexmerge.cpython-312.pyc new file mode 100644 index 0000000..845f1f8 Binary files /dev/null and b/code/.venv/bin/__pycache__/hexmerge.cpython-312.pyc differ diff --git a/code/.venv/bin/activate b/code/.venv/bin/activate new file mode 100644 index 0000000..400f559 --- /dev/null +++ b/code/.venv/bin/activate @@ -0,0 +1,87 @@ +# This file must be used with "source bin/activate" *from bash* +# you cannot run it directly + + +if [ "${BASH_SOURCE-}" = "$0" ]; then + echo "You must source this script: \$ source $0" >&2 + exit 33 +fi + +deactivate () { + unset -f pydoc >/dev/null 2>&1 || true + + # reset old environment variables + # ! [ -z ${VAR+_} ] returns true if VAR is declared at all + if ! [ -z "${_OLD_VIRTUAL_PATH:+_}" ] ; then + PATH="$_OLD_VIRTUAL_PATH" + export PATH + unset _OLD_VIRTUAL_PATH + fi + if ! [ -z "${_OLD_VIRTUAL_PYTHONHOME+_}" ] ; then + PYTHONHOME="$_OLD_VIRTUAL_PYTHONHOME" + export PYTHONHOME + unset _OLD_VIRTUAL_PYTHONHOME + fi + + # The hash command must be called to get it to forget past + # commands. Without forgetting past commands the $PATH changes + # we made may not be respected + hash -r 2>/dev/null + + if ! [ -z "${_OLD_VIRTUAL_PS1+_}" ] ; then + PS1="$_OLD_VIRTUAL_PS1" + export PS1 + unset _OLD_VIRTUAL_PS1 + fi + + unset VIRTUAL_ENV + unset VIRTUAL_ENV_PROMPT + if [ ! "${1-}" = "nondestructive" ] ; then + # Self destruct! + unset -f deactivate + fi +} + +# unset irrelevant variables +deactivate nondestructive + +VIRTUAL_ENV='/home/rhetenor/Projects/esp/python/.venv' +if ([ "$OSTYPE" = "cygwin" ] || [ "$OSTYPE" = "msys" ]) && $(command -v cygpath &> /dev/null) ; then + VIRTUAL_ENV=$(cygpath -u "$VIRTUAL_ENV") +fi +export VIRTUAL_ENV + +_OLD_VIRTUAL_PATH="$PATH" +PATH="$VIRTUAL_ENV/bin:$PATH" +export PATH + +if [ "x" != x ] ; then + VIRTUAL_ENV_PROMPT="" +else + VIRTUAL_ENV_PROMPT=$(basename "$VIRTUAL_ENV") +fi +export VIRTUAL_ENV_PROMPT + +# unset PYTHONHOME if set +if ! [ -z "${PYTHONHOME+_}" ] ; then + _OLD_VIRTUAL_PYTHONHOME="$PYTHONHOME" + unset PYTHONHOME +fi + +if [ -z "${VIRTUAL_ENV_DISABLE_PROMPT-}" ] ; then + _OLD_VIRTUAL_PS1="${PS1-}" + PS1="(${VIRTUAL_ENV_PROMPT}) ${PS1-}" + export PS1 +fi + +# Make sure to unalias pydoc if it's already there +alias pydoc 2>/dev/null >/dev/null && unalias pydoc || true + +pydoc () { + python -m pydoc "$@" +} + +# The hash command must be called to get it to forget past +# commands. Without forgetting past commands the $PATH changes +# we made may not be respected +hash -r 2>/dev/null || true diff --git a/code/.venv/bin/activate.csh b/code/.venv/bin/activate.csh new file mode 100644 index 0000000..d0ea19c --- /dev/null +++ b/code/.venv/bin/activate.csh @@ -0,0 +1,55 @@ +# This file must be used with "source bin/activate.csh" *from csh*. +# You cannot run it directly. +# Created by Davide Di Blasi . + +set newline='\ +' + +alias deactivate 'test $?_OLD_VIRTUAL_PATH != 0 && setenv PATH "$_OLD_VIRTUAL_PATH:q" && unset _OLD_VIRTUAL_PATH; rehash; test $?_OLD_VIRTUAL_PROMPT != 0 && set prompt="$_OLD_VIRTUAL_PROMPT:q" && unset _OLD_VIRTUAL_PROMPT; unsetenv VIRTUAL_ENV; unsetenv VIRTUAL_ENV_PROMPT; test "\!:*" != "nondestructive" && unalias deactivate && unalias pydoc' + +# Unset irrelevant variables. +deactivate nondestructive + +setenv VIRTUAL_ENV '/home/rhetenor/Projects/esp/python/.venv' + +set _OLD_VIRTUAL_PATH="$PATH:q" +setenv PATH "$VIRTUAL_ENV:q/bin:$PATH:q" + + + +if ('' != "") then + setenv VIRTUAL_ENV_PROMPT '' +else + setenv VIRTUAL_ENV_PROMPT "$VIRTUAL_ENV:t:q" +endif + +if ( $?VIRTUAL_ENV_DISABLE_PROMPT ) then + if ( $VIRTUAL_ENV_DISABLE_PROMPT == "" ) then + set do_prompt = "1" + else + set do_prompt = "0" + endif +else + set do_prompt = "1" +endif + +if ( $do_prompt == "1" ) then + # Could be in a non-interactive environment, + # in which case, $prompt is undefined and we wouldn't + # care about the prompt anyway. + if ( $?prompt ) then + set _OLD_VIRTUAL_PROMPT="$prompt:q" + if ( "$prompt:q" =~ *"$newline:q"* ) then + : + else + set prompt = '('"$VIRTUAL_ENV_PROMPT:q"') '"$prompt:q" + endif + endif +endif + +unset env_name +unset do_prompt + +alias pydoc python -m pydoc + +rehash diff --git a/code/.venv/bin/activate.fish b/code/.venv/bin/activate.fish new file mode 100644 index 0000000..4f9051f --- /dev/null +++ b/code/.venv/bin/activate.fish @@ -0,0 +1,103 @@ +# This file must be used using `source bin/activate.fish` *within a running fish ( http://fishshell.com ) session*. +# Do not run it directly. + +function _bashify_path -d "Converts a fish path to something bash can recognize" + set fishy_path $argv + set bashy_path $fishy_path[1] + for path_part in $fishy_path[2..-1] + set bashy_path "$bashy_path:$path_part" + end + echo $bashy_path +end + +function _fishify_path -d "Converts a bash path to something fish can recognize" + echo $argv | tr ':' '\n' +end + +function deactivate -d 'Exit virtualenv mode and return to the normal environment.' + # reset old environment variables + if test -n "$_OLD_VIRTUAL_PATH" + # https://github.com/fish-shell/fish-shell/issues/436 altered PATH handling + if test (echo $FISH_VERSION | head -c 1) -lt 3 + set -gx PATH (_fishify_path "$_OLD_VIRTUAL_PATH") + else + set -gx PATH $_OLD_VIRTUAL_PATH + end + set -e _OLD_VIRTUAL_PATH + end + + if test -n "$_OLD_VIRTUAL_PYTHONHOME" + set -gx PYTHONHOME "$_OLD_VIRTUAL_PYTHONHOME" + set -e _OLD_VIRTUAL_PYTHONHOME + end + + if test -n "$_OLD_FISH_PROMPT_OVERRIDE" + and functions -q _old_fish_prompt + # Set an empty local `$fish_function_path` to allow the removal of `fish_prompt` using `functions -e`. + set -l fish_function_path + + # Erase virtualenv's `fish_prompt` and restore the original. + functions -e fish_prompt + functions -c _old_fish_prompt fish_prompt + functions -e _old_fish_prompt + set -e _OLD_FISH_PROMPT_OVERRIDE + end + + set -e VIRTUAL_ENV + set -e VIRTUAL_ENV_PROMPT + + if test "$argv[1]" != 'nondestructive' + # Self-destruct! + functions -e pydoc + functions -e deactivate + functions -e _bashify_path + functions -e _fishify_path + end +end + +# Unset irrelevant variables. +deactivate nondestructive + +set -gx VIRTUAL_ENV '/home/rhetenor/Projects/esp/python/.venv' + +# https://github.com/fish-shell/fish-shell/issues/436 altered PATH handling +if test (echo $FISH_VERSION | head -c 1) -lt 3 + set -gx _OLD_VIRTUAL_PATH (_bashify_path $PATH) +else + set -gx _OLD_VIRTUAL_PATH $PATH +end +set -gx PATH "$VIRTUAL_ENV"'/bin' $PATH + +# Prompt override provided? +# If not, just use the environment name. +if test -n '' + set -gx VIRTUAL_ENV_PROMPT '' +else + set -gx VIRTUAL_ENV_PROMPT (basename "$VIRTUAL_ENV") +end + +# Unset `$PYTHONHOME` if set. +if set -q PYTHONHOME + set -gx _OLD_VIRTUAL_PYTHONHOME $PYTHONHOME + set -e PYTHONHOME +end + +function pydoc + python -m pydoc $argv +end + +if test -z "$VIRTUAL_ENV_DISABLE_PROMPT" + # Copy the current `fish_prompt` function as `_old_fish_prompt`. + functions -c fish_prompt _old_fish_prompt + + function fish_prompt + # Run the user's prompt first; it might depend on (pipe)status. + set -l prompt (_old_fish_prompt) + + printf '(%s) ' $VIRTUAL_ENV_PROMPT + + string join -- \n $prompt # handle multi-line prompts + end + + set -gx _OLD_FISH_PROMPT_OVERRIDE "$VIRTUAL_ENV" +end diff --git a/code/.venv/bin/activate.nu b/code/.venv/bin/activate.nu new file mode 100644 index 0000000..6cd94bd --- /dev/null +++ b/code/.venv/bin/activate.nu @@ -0,0 +1,96 @@ +# virtualenv activation module +# Activate with `overlay use activate.nu` +# Deactivate with `deactivate`, as usual +# +# To customize the overlay name, you can call `overlay use activate.nu as foo`, +# but then simply `deactivate` won't work because it is just an alias to hide +# the "activate" overlay. You'd need to call `overlay hide foo` manually. + +export-env { + def is-string [x] { + ($x | describe) == 'string' + } + + def has-env [...names] { + $names | each {|n| + $n in $env + } | all {|i| $i == true} + } + + # Emulates a `test -z`, but btter as it handles e.g 'false' + def is-env-true [name: string] { + if (has-env $name) { + # Try to parse 'true', '0', '1', and fail if not convertible + let parsed = (do -i { $env | get $name | into bool }) + if ($parsed | describe) == 'bool' { + $parsed + } else { + not ($env | get -i $name | is-empty) + } + } else { + false + } + } + + let virtual_env = '/home/rhetenor/Projects/esp/python/.venv' + let bin = 'bin' + + let is_windows = ($nu.os-info.family) == 'windows' + let path_name = (if (has-env 'Path') { + 'Path' + } else { + 'PATH' + } + ) + + let venv_path = ([$virtual_env $bin] | path join) + let new_path = ($env | get $path_name | prepend $venv_path) + + # If there is no default prompt, then use the env name instead + let virtual_env_prompt = (if ('' | is-empty) { + ($virtual_env | path basename) + } else { + '' + }) + + let new_env = { + $path_name : $new_path + VIRTUAL_ENV : $virtual_env + VIRTUAL_ENV_PROMPT : $virtual_env_prompt + } + + let new_env = (if (is-env-true 'VIRTUAL_ENV_DISABLE_PROMPT') { + $new_env + } else { + # Creating the new prompt for the session + let virtual_prefix = $'(char lparen)($virtual_env_prompt)(char rparen) ' + + # Back up the old prompt builder + let old_prompt_command = (if (has-env 'PROMPT_COMMAND') { + $env.PROMPT_COMMAND + } else { + '' + }) + + let new_prompt = (if (has-env 'PROMPT_COMMAND') { + if 'closure' in ($old_prompt_command | describe) { + {|| $'($virtual_prefix)(do $old_prompt_command)' } + } else { + {|| $'($virtual_prefix)($old_prompt_command)' } + } + } else { + {|| $'($virtual_prefix)' } + }) + + $new_env | merge { + PROMPT_COMMAND : $new_prompt + VIRTUAL_PREFIX : $virtual_prefix + } + }) + + # Environment variables that will be loaded as the virtual env + load-env $new_env +} + +export alias pydoc = python -m pydoc +export alias deactivate = overlay hide activate diff --git a/code/.venv/bin/activate.ps1 b/code/.venv/bin/activate.ps1 new file mode 100644 index 0000000..eba2893 --- /dev/null +++ b/code/.venv/bin/activate.ps1 @@ -0,0 +1,61 @@ +$script:THIS_PATH = $myinvocation.mycommand.path +$script:BASE_DIR = Split-Path (Resolve-Path "$THIS_PATH/..") -Parent + +function global:deactivate([switch] $NonDestructive) { + if (Test-Path variable:_OLD_VIRTUAL_PATH) { + $env:PATH = $variable:_OLD_VIRTUAL_PATH + Remove-Variable "_OLD_VIRTUAL_PATH" -Scope global + } + + if (Test-Path function:_old_virtual_prompt) { + $function:prompt = $function:_old_virtual_prompt + Remove-Item function:\_old_virtual_prompt + } + + if ($env:VIRTUAL_ENV) { + Remove-Item env:VIRTUAL_ENV -ErrorAction SilentlyContinue + } + + if ($env:VIRTUAL_ENV_PROMPT) { + Remove-Item env:VIRTUAL_ENV_PROMPT -ErrorAction SilentlyContinue + } + + if (!$NonDestructive) { + # Self destruct! + Remove-Item function:deactivate + Remove-Item function:pydoc + } +} + +function global:pydoc { + python -m pydoc $args +} + +# unset irrelevant variables +deactivate -nondestructive + +$VIRTUAL_ENV = $BASE_DIR +$env:VIRTUAL_ENV = $VIRTUAL_ENV + +if ("" -ne "") { + $env:VIRTUAL_ENV_PROMPT = "" +} +else { + $env:VIRTUAL_ENV_PROMPT = $( Split-Path $env:VIRTUAL_ENV -Leaf ) +} + +New-Variable -Scope global -Name _OLD_VIRTUAL_PATH -Value $env:PATH + +$env:PATH = "$env:VIRTUAL_ENV/bin:" + $env:PATH +if (!$env:VIRTUAL_ENV_DISABLE_PROMPT) { + function global:_old_virtual_prompt { + "" + } + $function:_old_virtual_prompt = $function:prompt + + function global:prompt { + # Add the custom prefix to the existing prompt + $previous_prompt_value = & $function:_old_virtual_prompt + ("(" + $env:VIRTUAL_ENV_PROMPT + ") " + $previous_prompt_value) + } +} diff --git a/code/.venv/bin/activate_this.py b/code/.venv/bin/activate_this.py new file mode 100644 index 0000000..aac33f5 --- /dev/null +++ b/code/.venv/bin/activate_this.py @@ -0,0 +1,38 @@ +""" +Activate virtualenv for current interpreter: + +import runpy +runpy.run_path(this_file) + +This can be used when you must use an existing Python interpreter, not the virtualenv bin/python. +""" # noqa: D415 + +from __future__ import annotations + +import os +import site +import sys + +try: + abs_file = os.path.abspath(__file__) +except NameError as exc: + msg = "You must use import runpy; runpy.run_path(this_file)" + raise AssertionError(msg) from exc + +bin_dir = os.path.dirname(abs_file) +base = bin_dir[: -len("bin") - 1] # strip away the bin part from the __file__, plus the path separator + +# prepend bin to PATH (this file is inside the bin directory) +os.environ["PATH"] = os.pathsep.join([bin_dir, *os.environ.get("PATH", "").split(os.pathsep)]) +os.environ["VIRTUAL_ENV"] = base # virtual env is right above bin directory +os.environ["VIRTUAL_ENV_PROMPT"] = "" or os.path.basename(base) # noqa: SIM222 + +# add the virtual environments libraries to the host python import mechanism +prev_length = len(sys.path) +for lib in "../lib/python3.12/site-packages".split(os.pathsep): + path = os.path.realpath(os.path.join(bin_dir, lib)) + site.addsitedir(path.decode("utf-8") if "" else path) +sys.path[:] = sys.path[prev_length:] + sys.path[0:prev_length] + +sys.real_prefix = sys.prefix +sys.prefix = base diff --git a/code/.venv/bin/bin2hex.py b/code/.venv/bin/bin2hex.py new file mode 100755 index 0000000..446a33e --- /dev/null +++ b/code/.venv/bin/bin2hex.py @@ -0,0 +1,115 @@ +#!/home/rhetenor/Projects/esp/python/.venv/bin/python + +# Copyright (c) 2008-2018 Alexander Belchenko +# All rights reserved. +# +# Redistribution and use in source and binary forms, +# with or without modification, are permitted provided +# that the following conditions are met: +# +# * Redistributions of source code must retain +# the above copyright notice, this list of conditions +# and the following disclaimer. +# * Redistributions in binary form must reproduce +# the above copyright notice, this list of conditions +# and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# * Neither the name of the author nor the names +# of its contributors may be used to endorse +# or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, +# BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY +# AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +# IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, +# OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +# OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED +# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +# EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +'''Intel HEX file format bin2hex convertor utility.''' + +VERSION = '2.3.0' + +if __name__ == '__main__': + import getopt + import os + import sys + + usage = '''Bin2Hex convertor utility. +Usage: + python bin2hex.py [options] INFILE [OUTFILE] + +Arguments: + INFILE name of bin file for processing. + Use '-' for reading from stdin. + + OUTFILE name of output file. If omitted then output + will be writing to stdout. + +Options: + -h, --help this help message. + -v, --version version info. + --offset=N offset for loading bin file (default: 0). +''' + + offset = 0 + + try: + opts, args = getopt.getopt(sys.argv[1:], "hv", + ["help", "version", "offset="]) + + for o, a in opts: + if o in ("-h", "--help"): + print(usage) + sys.exit(0) + elif o in ("-v", "--version"): + print(VERSION) + sys.exit(0) + elif o in ("--offset"): + base = 10 + if a[:2].lower() == '0x': + base = 16 + try: + offset = int(a, base) + except: + raise getopt.GetoptError('Bad offset value') + + if not args: + raise getopt.GetoptError('Input file is not specified') + + if len(args) > 2: + raise getopt.GetoptError('Too many arguments') + + except getopt.GetoptError: + msg = sys.exc_info()[1] # current exception + txt = 'ERROR: '+str(msg) # that's required to get not-so-dumb result from 2to3 tool + print(txt) + print(usage) + sys.exit(2) + + from intelhex import compat + + fin = args[0] + if fin == '-': + # read from stdin + fin = compat.get_binary_stdin() + elif not os.path.isfile(fin): + txt = "ERROR: File not found: %s" % fin # that's required to get not-so-dumb result from 2to3 tool + print(txt) + sys.exit(1) + + if len(args) == 2: + fout = args[1] + else: + # write to stdout + fout = sys.stdout # compat.get_binary_stdout() + + from intelhex import bin2hex + sys.exit(bin2hex(fin, fout, offset)) diff --git a/code/.venv/bin/esp_rfc2217_server.py b/code/.venv/bin/esp_rfc2217_server.py new file mode 100755 index 0000000..5fa965c --- /dev/null +++ b/code/.venv/bin/esp_rfc2217_server.py @@ -0,0 +1,311 @@ +#!/home/rhetenor/Projects/esp/python/.venv/bin/python + +# SPDX-FileCopyrightText: 2009-2015 Chris Liechti +# SPDX-FileContributor: 2020-2022 Espressif Systems (Shanghai) CO LTD +# SPDX-License-Identifier: BSD-3-Clause +# +# Redirect data from a TCP/IP connection to a serial port and vice versa using RFC 2217. +# +# This is a modified version of rfc2217_server.py provided by the pyserial package +# (pythonhosted.org/pyserial/examples.html#single-port-tcp-ip-serial-bridge-rfc-2217). +# It uses a custom PortManager to properly apply the RTS & DTR signals +# for reseting ESP chips. +# +# Run the following command on the server side to make +# connection between /dev/ttyUSB1 and TCP port 4000: +# +# python esp_rfc2217_server.py -p 4000 /dev/ttyUSB1 +# +# Esptool can connect to the ESP device through that server as it is +# demonstrated in the following example: +# +# esptool.py --port rfc2217://localhost:4000?ign_set_control flash_id +# +################################################################################### +# redirect data from a TCP/IP connection to a serial port and vice versa +# using RFC 2217 +# +# (C) 2009-2015 Chris Liechti +# +# SPDX-License-Identifier: BSD-3-Clause + +import logging +import os +import socket +import sys +import threading +import time + +from esptool.config import load_config_file +from esptool.reset import ( + ClassicReset, + CustomReset, + DEFAULT_RESET_DELAY, + HardReset, + UnixTightReset, +) + +import serial +import serial.rfc2217 +from serial.rfc2217 import ( + COM_PORT_OPTION, + SET_CONTROL, + SET_CONTROL_DTR_OFF, + SET_CONTROL_DTR_ON, + SET_CONTROL_RTS_OFF, + SET_CONTROL_RTS_ON, +) + +cfg, _ = load_config_file(verbose=True) +cfg = cfg["esptool"] + + +class EspPortManager(serial.rfc2217.PortManager): + """ + The beginning of the reset sequence is detected and the proper reset sequence + is applied in a thread. The rest of the reset sequence received is just ignored + and not sent to the serial port. + """ + + def __init__(self, serial_port, connection, esp32r0_delay, logger=None): + self.esp32r0_delay = esp32r0_delay + self.is_download_mode = False + super(EspPortManager, self).__init__(serial_port, connection, logger) + + def _telnet_process_subnegotiation(self, suboption): + if suboption[0:1] == COM_PORT_OPTION and suboption[1:2] == SET_CONTROL: + if suboption[2:3] == SET_CONTROL_DTR_OFF: + self.is_download_mode = False + self.serial.dtr = False + return + elif suboption[2:3] == SET_CONTROL_RTS_OFF and not self.is_download_mode: + reset_thread = threading.Thread(target=self._hard_reset_thread) + reset_thread.daemon = True + reset_thread.name = "hard_reset_thread" + reset_thread.start() + return + elif suboption[2:3] == SET_CONTROL_DTR_ON and not self.is_download_mode: + self.is_download_mode = True + reset_thread = threading.Thread(target=self._reset_thread) + reset_thread.daemon = True + reset_thread.name = "reset_thread" + reset_thread.start() + return + elif suboption[2:3] in [ + SET_CONTROL_DTR_ON, + SET_CONTROL_RTS_ON, + SET_CONTROL_RTS_OFF, + ]: + return + # only in cases not handled above do the original implementation in PortManager + super(EspPortManager, self)._telnet_process_subnegotiation(suboption) + + def _hard_reset_thread(self): + """ + The reset logic used for hard resetting the chip. + """ + if self.logger: + self.logger.info("Activating hard reset in thread") + HardReset(self.serial)() + + def _reset_thread(self): + """ + The reset logic is used from esptool.py because the RTS and DTR signals + cannot be retransmitted through RFC 2217 with proper timing. + """ + if self.logger: + self.logger.info("Activating reset in thread") + + delay = DEFAULT_RESET_DELAY + if self.esp32r0_delay: + delay += 0.5 + + cfg_custom_reset_sequence = cfg.get("custom_reset_sequence") + if cfg_custom_reset_sequence is not None: + CustomReset(self.serial, cfg_custom_reset_sequence)() + elif os.name != "nt": + UnixTightReset(self.serial, delay)() + else: + ClassicReset(self.serial, delay)() + + +class Redirector(object): + def __init__(self, serial_instance, socket, debug=False, esp32r0delay=False): + self.serial = serial_instance + self.socket = socket + self._write_lock = threading.Lock() + self.rfc2217 = EspPortManager( + self.serial, + self, + esp32r0delay, + logger=logging.getLogger("rfc2217.server") if debug else None, + ) + self.log = logging.getLogger("redirector") + + def statusline_poller(self): + self.log.debug("status line poll thread started") + while self.alive: + time.sleep(1) + self.rfc2217.check_modem_lines() + self.log.debug("status line poll thread terminated") + + def shortcircuit(self): + """connect the serial port to the TCP port by copying everything + from one side to the other""" + self.alive = True + self.thread_read = threading.Thread(target=self.reader) + self.thread_read.daemon = True + self.thread_read.name = "serial->socket" + self.thread_read.start() + self.thread_poll = threading.Thread(target=self.statusline_poller) + self.thread_poll.daemon = True + self.thread_poll.name = "status line poll" + self.thread_poll.start() + self.writer() + + def reader(self): + """loop forever and copy serial->socket""" + self.log.debug("reader thread started") + while self.alive: + try: + data = self.serial.read(self.serial.in_waiting or 1) + if data: + # escape outgoing data when needed (Telnet IAC (0xff) character) + self.write(b"".join(self.rfc2217.escape(data))) + except socket.error as msg: + self.log.error("{}".format(msg)) + # probably got disconnected + break + self.alive = False + self.log.debug("reader thread terminated") + + def write(self, data): + """thread safe socket write with no data escaping. used to send telnet stuff""" + with self._write_lock: + self.socket.sendall(data) + + def writer(self): + """loop forever and copy socket->serial""" + while self.alive: + try: + data = self.socket.recv(1024) + if not data: + break + self.serial.write(b"".join(self.rfc2217.filter(data))) + except socket.error as msg: + self.log.error("{}".format(msg)) + # probably got disconnected + break + self.stop() + + def stop(self): + """Stop copying""" + self.log.debug("stopping") + if self.alive: + self.alive = False + self.thread_read.join() + self.thread_poll.join() + + +def main(): + import argparse + + parser = argparse.ArgumentParser( + description="RFC 2217 Serial to Network (TCP/IP) redirector.", + epilog="NOTE: no security measures are implemented. " + "Anyone can remotely connect to this service over the network.\n" + "Only one connection at once is supported. " + "When the connection is terminated it waits for the next connect.", + ) + + parser.add_argument("SERIALPORT") + + parser.add_argument( + "-p", + "--localport", + type=int, + help="local TCP port, default: %(default)s", + metavar="TCPPORT", + default=2217, + ) + + parser.add_argument( + "-v", + "--verbose", + dest="verbosity", + action="count", + help="print more diagnostic messages (option can be given multiple times)", + default=0, + ) + + parser.add_argument( + "--r0", + help="Use delays necessary for ESP32 revision 0 chips", + action="store_true", + ) + + args = parser.parse_args() + + if args.verbosity > 3: + args.verbosity = 3 + level = (logging.WARNING, logging.INFO, logging.DEBUG, logging.NOTSET)[ + args.verbosity + ] + logging.basicConfig(level=logging.INFO) + # logging.getLogger('root').setLevel(logging.INFO) + logging.getLogger("rfc2217").setLevel(level) + + # connect to serial port + ser = serial.serial_for_url(args.SERIALPORT, do_not_open=True) + ser.timeout = 3 # required so that the reader thread can exit + # reset control line as no _remote_ "terminal" has been connected yet + ser.dtr = False + ser.rts = False + + logging.info("RFC 2217 TCP/IP to Serial redirector - type Ctrl-C / BREAK to quit") + + try: + ser.open() + except serial.SerialException as e: + logging.error("Could not open serial port {}: {}".format(ser.name, e)) + sys.exit(1) + + logging.info("Serving serial port: {}".format(ser.name)) + settings = ser.get_settings() + + srv = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + srv.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + srv.bind(("", args.localport)) + srv.listen(1) + logging.info("TCP/IP port: {}".format(args.localport)) + while True: + try: + client_socket, addr = srv.accept() + logging.info("Connected by {}:{}".format(addr[0], addr[1])) + client_socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) + ser.rts = True + ser.dtr = True + # enter network <-> serial loop + r = Redirector(ser, client_socket, args.verbosity > 0, args.r0) + try: + r.shortcircuit() + finally: + logging.info("Disconnected") + r.stop() + client_socket.close() + ser.dtr = False + ser.rts = False + # Restore port settings (may have been changed by RFC 2217 + # capable client) + ser.apply_settings(settings) + except KeyboardInterrupt: + sys.stdout.write("\n") + break + except socket.error as msg: + logging.error(str(msg)) + + logging.info("--- exit ---") + + +if __name__ == "__main__": + main() diff --git a/code/.venv/bin/espefuse.py b/code/.venv/bin/espefuse.py new file mode 100755 index 0000000..e1aa785 --- /dev/null +++ b/code/.venv/bin/espefuse.py @@ -0,0 +1,37 @@ +#!/home/rhetenor/Projects/esp/python/.venv/bin/python +# +# SPDX-FileCopyrightText: 2014-2022 Fredrik Ahlberg, Angus Gratton, +# Espressif Systems (Shanghai) CO LTD, other contributors as noted. +# +# SPDX-License-Identifier: GPL-2.0-or-later + +# This executable script is a thin wrapper around the main functionality +# in the espefuse Python package + +# When updating this script, please also update esptool.py and espsecure.py + +import contextlib +import os +import sys + +if os.name != "nt": + # Linux/macOS: remove current script directory to avoid importing this file + # as a module; we want to import the installed espefuse module instead + with contextlib.suppress(ValueError): + executable_dir = os.path.dirname(sys.executable) + sys.path = [ + path + for path in sys.path + if not path.endswith(("/bin", "/sbin")) and path != executable_dir + ] + + # Linux/macOS: delete imported module entry to force Python to load + # the module from scratch; this enables importing espefuse module in + # other Python scripts + with contextlib.suppress(KeyError): + del sys.modules["espefuse"] + +import espefuse + +if __name__ == "__main__": + espefuse._main() diff --git a/code/.venv/bin/espsecure.py b/code/.venv/bin/espsecure.py new file mode 100755 index 0000000..833555e --- /dev/null +++ b/code/.venv/bin/espsecure.py @@ -0,0 +1,37 @@ +#!/home/rhetenor/Projects/esp/python/.venv/bin/python +# +# SPDX-FileCopyrightText: 2014-2022 Fredrik Ahlberg, Angus Gratton, +# Espressif Systems (Shanghai) CO LTD, other contributors as noted. +# +# SPDX-License-Identifier: GPL-2.0-or-later + +# This executable script is a thin wrapper around the main functionality +# in the espsecure Python package + +# When updating this script, please also update esptool.py and espefuse.py + +import contextlib +import os +import sys + +if os.name != "nt": + # Linux/macOS: remove current script directory to avoid importing this file + # as a module; we want to import the installed espsecure module instead + with contextlib.suppress(ValueError): + executable_dir = os.path.dirname(sys.executable) + sys.path = [ + path + for path in sys.path + if not path.endswith(("/bin", "/sbin")) and path != executable_dir + ] + + # Linux/macOS: delete imported module entry to force Python to load + # the module from scratch; this enables importing espsecure module in + # other Python scripts + with contextlib.suppress(KeyError): + del sys.modules["espsecure"] + +import espsecure + +if __name__ == "__main__": + espsecure._main() diff --git a/code/.venv/bin/esptool.py b/code/.venv/bin/esptool.py new file mode 100755 index 0000000..15feb81 --- /dev/null +++ b/code/.venv/bin/esptool.py @@ -0,0 +1,37 @@ +#!/home/rhetenor/Projects/esp/python/.venv/bin/python +# +# SPDX-FileCopyrightText: 2014-2022 Fredrik Ahlberg, Angus Gratton, +# Espressif Systems (Shanghai) CO LTD, other contributors as noted. +# +# SPDX-License-Identifier: GPL-2.0-or-later + +# This executable script is a thin wrapper around the main functionality +# in the esptool Python package + +# When updating this script, please also update espefuse.py and espsecure.py + +import contextlib +import os +import sys + +if os.name != "nt": + # Linux/macOS: remove current script directory to avoid importing this file + # as a module; we want to import the installed esptool module instead + with contextlib.suppress(ValueError): + executable_dir = os.path.dirname(sys.executable) + sys.path = [ + path + for path in sys.path + if not path.endswith(("/bin", "/sbin")) and path != executable_dir + ] + + # Linux/macOS: delete imported module entry to force Python to load + # the module from scratch; this enables importing esptool module in + # other Python scripts + with contextlib.suppress(KeyError): + del sys.modules["esptool"] + +import esptool + +if __name__ == "__main__": + esptool._main() diff --git a/code/.venv/bin/hex2bin.py b/code/.venv/bin/hex2bin.py new file mode 100755 index 0000000..8ed572f --- /dev/null +++ b/code/.venv/bin/hex2bin.py @@ -0,0 +1,132 @@ +#!/home/rhetenor/Projects/esp/python/.venv/bin/python + +# Copyright (c) 2005-2018 Alexander Belchenko +# All rights reserved. +# +# Redistribution and use in source and binary forms, +# with or without modification, are permitted provided +# that the following conditions are met: +# +# * Redistributions of source code must retain +# the above copyright notice, this list of conditions +# and the following disclaimer. +# * Redistributions in binary form must reproduce +# the above copyright notice, this list of conditions +# and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# * Neither the name of the author nor the names +# of its contributors may be used to endorse +# or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, +# BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY +# AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +# IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, +# OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +# OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED +# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +# EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +'''Intel HEX file format hex2bin convertor utility.''' + +VERSION = '2.3.0' + +if __name__ == '__main__': + import getopt + import os + import sys + + usage = '''Hex2Bin convertor utility. +Usage: + python hex2bin.py [options] INFILE [OUTFILE] + +Arguments: + INFILE name of hex file for processing. + OUTFILE name of output file. If omitted then output + will be writing to stdout. + +Options: + -h, --help this help message. + -v, --version version info. + -p, --pad=FF pad byte for empty spaces (ascii hex value). + -r, --range=START:END specify address range for writing output + (ascii hex value). + Range can be in form 'START:' or ':END'. + -l, --length=NNNN, + -s, --size=NNNN size of output (decimal value). +''' + + pad = None + start = None + end = None + size = None + + try: + opts, args = getopt.getopt(sys.argv[1:], "hvp:r:l:s:", + ["help", "version", "pad=", "range=", + "length=", "size="]) + + for o, a in opts: + if o in ("-h", "--help"): + print(usage) + sys.exit(0) + elif o in ("-v", "--version"): + print(VERSION) + sys.exit(0) + elif o in ("-p", "--pad"): + try: + pad = int(a, 16) & 0x0FF + except: + raise getopt.GetoptError('Bad pad value') + elif o in ("-r", "--range"): + try: + l = a.split(":") + if l[0] != '': + start = int(l[0], 16) + if l[1] != '': + end = int(l[1], 16) + except: + raise getopt.GetoptError('Bad range value(s)') + elif o in ("-l", "--lenght", "-s", "--size"): + try: + size = int(a, 10) + except: + raise getopt.GetoptError('Bad size value') + + if start != None and end != None and size != None: + raise getopt.GetoptError('Cannot specify START:END and SIZE simultaneously') + + if not args: + raise getopt.GetoptError('Hex file is not specified') + + if len(args) > 2: + raise getopt.GetoptError('Too many arguments') + + except getopt.GetoptError: + msg = sys.exc_info()[1] # current exception + txt = 'ERROR: '+str(msg) # that's required to get not-so-dumb result from 2to3 tool + print(txt) + print(usage) + sys.exit(2) + + fin = args[0] + if not os.path.isfile(fin): + txt = "ERROR: File not found: %s" % fin # that's required to get not-so-dumb result from 2to3 tool + print(txt) + sys.exit(1) + + if len(args) == 2: + fout = args[1] + else: + # write to stdout + from intelhex import compat + fout = compat.get_binary_stdout() + + from intelhex import hex2bin + sys.exit(hex2bin(fin, fout, start, end, size, pad)) diff --git a/code/.venv/bin/hex2dump.py b/code/.venv/bin/hex2dump.py new file mode 100755 index 0000000..a965418 --- /dev/null +++ b/code/.venv/bin/hex2dump.py @@ -0,0 +1,135 @@ +#!/home/rhetenor/Projects/esp/python/.venv/bin/python + +# Copyright (c) 2008-2018 Alexander Belchenko +# All rights reserved. +# +# Redistribution and use in source and binary forms, +# with or without modification, are permitted provided +# that the following conditions are met: +# +# * Redistributions of source code must retain +# the above copyright notice, this list of conditions +# and the following disclaimer. +# * Redistributions in binary form must reproduce +# the above copyright notice, this list of conditions +# and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# * Neither the name of the author nor the names +# of its contributors may be used to endorse +# or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, +# BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY +# AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +# IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, +# OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +# OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED +# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +# EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +"""Show content of hex file as hexdump.""" + +VERSION = '2.3.0' + +USAGE = '''hex2dump: show content of hex file as hexdump. +Usage: + python hex2dump.py [options] HEXFILE + +Options: + -h, --help this help message. + -v, --version version info. + -r, --range=START:END specify address range for dumping + (ascii hex value). + Range can be in form 'START:' or ':END'. + --width=N dump N data bytes per line (default: 16). + +Arguments: + HEXFILE name of hex file for processing (use '-' to read + from stdin) +''' + +import sys + +DEFAULT_WIDTH = 16 + +def hex2dump(hexfile, start=None, end=None, width=DEFAULT_WIDTH): + import intelhex + if hexfile == '-': + hexfile = sys.stdin + try: + ih = intelhex.IntelHex(hexfile) + except (IOError, intelhex.IntelHexError): + e = sys.exc_info()[1] # current exception + sys.stderr.write('Error reading file: %s\n' % e) + return 1 + if not (start is None and end is None): + ih = ih[slice(start,end)] + ih.dump(tofile=sys.stdout, width=width) + return 0 + + +def main(argv=None): + import getopt + + if argv is None: + argv = sys.argv[1:] + + start = None + end = None + width = DEFAULT_WIDTH + + try: + opts, args = getopt.getopt(sys.argv[1:], "hvp:r:", + ["help", "version", "range=", "width="]) + for o, a in opts: + if o in ("-h", "--help"): + print(USAGE) + return 0 + elif o in ("-v", "--version"): + print(VERSION) + return 0 + elif o in ("-r", "--range"): + try: + l = a.split(":") + if l[0] != '': + start = int(l[0], 16) + if l[1] != '': + end = int(l[1], 16) + except: + raise getopt.GetoptError('Bad range value(s)') + elif o == "--width": + try: + width = int(a) + if width < 1: + raise ValueError + except: + raise getopt.GetoptError('Bad width value (%s)' % a) + if not args: + raise getopt.GetoptError('Hex file is not specified') + if len(args) > 1: + raise getopt.GetoptError('Too many arguments') + except getopt.GetoptError: + msg = sys.exc_info()[1] # current exception + txt = 'ERROR: '+str(msg) # that's required to get not-so-dumb result from 2to3 tool + print(txt) + print(USAGE) + return 2 + + try: + return hex2dump(args[0], start, end, width) + except IOError: + e = sys.exc_info()[1] # current exception + import errno + if e.errno not in (0, errno.EPIPE): + raise + + +if __name__ == '__main__': + import sys + sys.exit(main()) diff --git a/code/.venv/bin/hexdiff.py b/code/.venv/bin/hexdiff.py new file mode 100755 index 0000000..546bb22 --- /dev/null +++ b/code/.venv/bin/hexdiff.py @@ -0,0 +1,90 @@ +#!/home/rhetenor/Projects/esp/python/.venv/bin/python + +# Copyright (c) 2011-2018 Alexander Belchenko +# All rights reserved. +# +# Redistribution and use in source and binary forms, +# with or without modification, are permitted provided +# that the following conditions are met: +# +# * Redistributions of source code must retain +# the above copyright notice, this list of conditions +# and the following disclaimer. +# * Redistributions in binary form must reproduce +# the above copyright notice, this list of conditions +# and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# * Neither the name of the author nor the names +# of its contributors may be used to endorse +# or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, +# BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY +# AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +# IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, +# OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +# OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED +# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +# EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +"""Produce diff for 2 hex files using hex dump as string representation +of compared data. +""" + +VERSION = '2.3.0' + +USAGE = '''hexdiff: diff dumps of 2 hex files. +Usage: + python hexdiff.py [options] FILE1 FILE2 + +Options: + -h, --help this help message. + -v, --version version info. +''' + +import sys + + +def main(argv=None): + import getopt + + if argv is None: + argv = sys.argv[1:] + try: + opts, args = getopt.gnu_getopt(argv, 'hv', ['help', 'version']) + + for o,a in opts: + if o in ('-h', '--help'): + print(USAGE) + return 0 + elif o in ('-v', '--version'): + print(VERSION) + return 0 + + except getopt.GetoptError: + e = sys.exc_info()[1] # current exception + sys.stderr.write(str(e)+"\n") + sys.stderr.write(USAGE+"\n") + return 1 + + if len(args) != 2: + sys.stderr.write("ERROR: You should specify 2 files to diff.\n") + sys.stderr.write(USAGE+"\n") + return 1 + + fname1, fname2 = args + + from intelhex import IntelHex, diff_dumps + ih1 = IntelHex(fname1) + ih2 = IntelHex(fname2) + diff_dumps(ih1, ih2, name1=fname1, name2=fname2) + + +if __name__ == '__main__': + sys.exit(main()) diff --git a/code/.venv/bin/hexinfo.py b/code/.venv/bin/hexinfo.py new file mode 100755 index 0000000..f6ace50 --- /dev/null +++ b/code/.venv/bin/hexinfo.py @@ -0,0 +1,108 @@ +#!/home/rhetenor/Projects/esp/python/.venv/bin/python + +# Copyright (c) 2015 Andrew Fernandes +# All rights reserved. +# +# Redistribution and use in source and binary forms, +# with or without modification, are permitted provided +# that the following conditions are met: +# +# * Redistributions of source code must retain +# the above copyright notice, this list of conditions +# and the following disclaimer. +# * Redistributions in binary form must reproduce +# the above copyright notice, this list of conditions +# and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# * Neither the name of the author nor the names +# of its contributors may be used to endorse +# or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, +# BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY +# AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +# IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, +# OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +# OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED +# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +# EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +"""Summarize the information in a hex file by printing the execution + start address (if any), and the address ranges covered by the + data (if any), in YAML format. +""" + +VERSION = '2.3.0' + +USAGE = '''hexinfo: summarize a hex file's contents. +Usage: + python hexinfo.py [options] FILE [ FILE ... ] + +Options: + -h, --help this help message. + -v, --version version info. +''' + +import sys + +INDENT = ' ' +INLIST = '- ' + +def summarize_yaml(fname): + print("{:s}file: '{:s}'".format(INLIST, fname)) + from intelhex import IntelHex + ih = IntelHex(fname) + if ih.start_addr: + keys = sorted(ih.start_addr.keys()) + if keys == ['CS','IP']: + entry = ih.start_addr['CS'] * 65536 + ih.start_addr['IP'] + elif keys == ['EIP']: + entry = ih.start_addr['EIP'] + else: + raise RuntimeError("unknown 'IntelHex.start_addr' found") + print("{:s}entry: 0x{:08X}".format(INDENT, entry)) + segments = ih.segments() + if segments: + print("{:s}data:".format(INDENT)) + for s in segments: + print("{:s}{:s}{{ first: 0x{:08X}, last: 0x{:08X}, length: 0x{:08X} }}".format(INDENT, INLIST, s[0], s[1]-1, s[1]-s[0])) + print("") + +def main(argv=None): + import getopt + + if argv is None: + argv = sys.argv[1:] + try: + opts, args = getopt.gnu_getopt(argv, 'hv', ['help', 'version']) + + for o,a in opts: + if o in ('-h', '--help'): + print(USAGE) + return 0 + elif o in ('-v', '--version'): + print(VERSION) + return 0 + + except getopt.GetoptError: + e = sys.exc_info()[1] # current exception + sys.stderr.write(str(e)+"\n") + sys.stderr.write(USAGE+"\n") + return 1 + + if len(args) < 1: + sys.stderr.write("ERROR: You should specify one or more files to summarize.\n") + sys.stderr.write(USAGE+"\n") + return 1 + + for fname in args: + summarize_yaml(fname) + +if __name__ == '__main__': + sys.exit(main()) diff --git a/code/.venv/bin/hexmerge.py b/code/.venv/bin/hexmerge.py new file mode 100755 index 0000000..cc12520 --- /dev/null +++ b/code/.venv/bin/hexmerge.py @@ -0,0 +1,178 @@ +#!/home/rhetenor/Projects/esp/python/.venv/bin/python + +# Copyright (c) 2008-2018 Alexander Belchenko +# All rights reserved. +# +# Redistribution and use in source and binary forms, +# with or without modification, are permitted provided +# that the following conditions are met: +# +# * Redistributions of source code must retain +# the above copyright notice, this list of conditions +# and the following disclaimer. +# * Redistributions in binary form must reproduce +# the above copyright notice, this list of conditions +# and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# * Neither the name of the author nor the names +# of its contributors may be used to endorse +# or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, +# BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY +# AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +# IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, +# OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +# OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED +# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +# EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +"""Merge content of several hex files into one file.""" + +VERSION = '2.3.0' + +USAGE = '''hexmerge: merge content of hex files. +Usage: + python hexmerge.py [options] FILES... + +Options: + -h, --help this help message. + -v, --version version info. + -o, --output=FILENAME output file name (emit output to stdout + if option is not specified) + -r, --range=START:END specify address range for output + (ascii hex value). Both values are inclusive. + Range can be in form 'START:' or ':END'. + --no-start-addr Don't write start addr to output file. + --overlap=METHOD What to do when data in files overlapped. + Supported variants: + * error -- stop and show error message (default) + * ignore -- keep data from first file that + contains data at overlapped address + * replace -- use data from last file that + contains data at overlapped address + +Arguments: + FILES list of hex files for merging + (use '-' to read content from stdin) + +You can specify address range for each file in the form: + + filename:START:END + +See description of range option above. + +You can omit START or END, so supported variants are: + + filename:START: read filename and use data starting from START addr + filename::END read filename and use data till END addr + +Use entire file content: + + filename +or + filename:: +''' + +import sys + + +def main(args=None): + import getopt + + output = None + start = None + end = None + write_start_addr = True + overlap = 'error' + + if args is None: + args = sys.argv[1:] + try: + opts, args = getopt.gnu_getopt(args, 'hvo:r:', + ['help', 'version', + 'output=', 'range=', + 'no-start-addr', 'overlap=', + ]) + + for o,a in opts: + if o in ('-h', '--help'): + print(USAGE) + return 0 + elif o in ('-v', '--version'): + print(VERSION) + return 0 + elif o in ('-o', '--output'): + output = a + elif o in ("-r", "--range"): + try: + l = a.split(":") + if l[0] != '': + start = int(l[0], 16) + if l[1] != '': + end = int(l[1], 16) + except (ValueError, IndexError): + raise getopt.GetoptError('Bad range value(s)') + elif o == '--no-start-addr': + write_start_addr = False + elif o == '--overlap': + if a in ('error', 'ignore', 'replace'): + overlap = a + else: + raise getopt.GetoptError('Bad overlap value') + + if len(args) == 0: + raise getopt.GetoptError('You should specify file list') + + except getopt.GetoptError: + e = sys.exc_info()[1] # current exception + sys.stderr.write(str(e)+"\n") + sys.stderr.write(USAGE+"\n") + return 1 + + import intelhex + # TODO: move actual merge code into intelhex package as helper function + # and write couple of tests for it. + res = intelhex.IntelHex() + + def end_addr_inclusive(addr): + if addr is not None: + return addr + 1 + return addr + + for f in args: + try: + fname, fstart, fend = intelhex._get_file_and_addr_range(f) + except intelhex._BadFileNotation: + sys.stderr.write('Bad argument: "%s"\n' % f) + sys.stderr.write(USAGE+"\n") + return 1 + if fname == '-': + fname = sys.stdin + ih = intelhex.IntelHex(fname) + if (fstart, fend) != (None, None): + ih = ih[fstart:end_addr_inclusive(fend)] + try: + res.merge(ih, overlap) + except intelhex.AddressOverlapError: + e = sys.exc_info()[1] # current exception + sys.stderr.write('Merging: '+fname+"\n") + sys.stderr.write(str(e)+"\n") + return 1 + + if (start, end) != (None, None): + res = res[start:end_addr_inclusive(end)] + if output is None: + output = sys.stdout + res.write_hex_file(output, write_start_addr) + return 0 + + +if __name__ == '__main__': + sys.exit(main()) diff --git a/code/.venv/bin/list_serial_ports b/code/.venv/bin/list_serial_ports new file mode 100755 index 0000000..89dbc0f --- /dev/null +++ b/code/.venv/bin/list_serial_ports @@ -0,0 +1,8 @@ +#!/home/rhetenor/Projects/esp/python/.venv/bin/python +# -*- coding: utf-8 -*- +import re +import sys +from utilities.list_serial_ports import lsp +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.exit(lsp()) diff --git a/code/.venv/bin/pip b/code/.venv/bin/pip new file mode 100755 index 0000000..0e11f38 --- /dev/null +++ b/code/.venv/bin/pip @@ -0,0 +1,8 @@ +#!/home/rhetenor/Projects/esp/python/.venv/bin/python +# -*- coding: utf-8 -*- +import re +import sys +from pip._internal.cli.main import main +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.exit(main()) diff --git a/code/.venv/bin/pip-3.12 b/code/.venv/bin/pip-3.12 new file mode 100755 index 0000000..0e11f38 --- /dev/null +++ b/code/.venv/bin/pip-3.12 @@ -0,0 +1,8 @@ +#!/home/rhetenor/Projects/esp/python/.venv/bin/python +# -*- coding: utf-8 -*- +import re +import sys +from pip._internal.cli.main import main +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.exit(main()) diff --git a/code/.venv/bin/pip3 b/code/.venv/bin/pip3 new file mode 100755 index 0000000..0e11f38 --- /dev/null +++ b/code/.venv/bin/pip3 @@ -0,0 +1,8 @@ +#!/home/rhetenor/Projects/esp/python/.venv/bin/python +# -*- coding: utf-8 -*- +import re +import sys +from pip._internal.cli.main import main +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.exit(main()) diff --git a/code/.venv/bin/pip3.12 b/code/.venv/bin/pip3.12 new file mode 100755 index 0000000..0e11f38 --- /dev/null +++ b/code/.venv/bin/pip3.12 @@ -0,0 +1,8 @@ +#!/home/rhetenor/Projects/esp/python/.venv/bin/python +# -*- coding: utf-8 -*- +import re +import sys +from pip._internal.cli.main import main +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.exit(main()) diff --git a/code/.venv/bin/pyserial-miniterm b/code/.venv/bin/pyserial-miniterm new file mode 100755 index 0000000..e6395bf --- /dev/null +++ b/code/.venv/bin/pyserial-miniterm @@ -0,0 +1,8 @@ +#!/home/rhetenor/Projects/esp/python/.venv/bin/python +# -*- coding: utf-8 -*- +import re +import sys +from serial.tools.miniterm import main +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.exit(main()) diff --git a/code/.venv/bin/pyserial-ports b/code/.venv/bin/pyserial-ports new file mode 100755 index 0000000..60af3d4 --- /dev/null +++ b/code/.venv/bin/pyserial-ports @@ -0,0 +1,8 @@ +#!/home/rhetenor/Projects/esp/python/.venv/bin/python +# -*- coding: utf-8 -*- +import re +import sys +from serial.tools.list_ports import main +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.exit(main()) diff --git a/code/.venv/bin/python b/code/.venv/bin/python new file mode 120000 index 0000000..dc92e12 --- /dev/null +++ b/code/.venv/bin/python @@ -0,0 +1 @@ +/usr/bin/python3.12 \ No newline at end of file diff --git a/code/.venv/bin/python3 b/code/.venv/bin/python3 new file mode 120000 index 0000000..d8654aa --- /dev/null +++ b/code/.venv/bin/python3 @@ -0,0 +1 @@ +python \ No newline at end of file diff --git a/code/.venv/bin/python3.12 b/code/.venv/bin/python3.12 new file mode 120000 index 0000000..d8654aa --- /dev/null +++ b/code/.venv/bin/python3.12 @@ -0,0 +1 @@ +python \ No newline at end of file diff --git a/code/.venv/bin/webreplcmd b/code/.venv/bin/webreplcmd new file mode 100755 index 0000000..cfb3c79 --- /dev/null +++ b/code/.venv/bin/webreplcmd @@ -0,0 +1,162 @@ +#!/home/rhetenor/Projects/esp/python/.venv/bin/python + +import argparse +import os +import sys + +import webrepl + +examples="""webreplcmd --host 192.168.4.1 --password ulx3s ls +webreplcmd --host 192.168.4.1 --password ulx3s get src-remote-file.txt dest-local-file.txt +webreplcmd --host 192.168.4.1 --password ulx3s put src-local-file.txt dest-remote-file.txt +webreplcmd --host 192.168.4.1 --password ulx3s cat main.py +webreplcmd --host 192.168.4.1 --password ulx3s cmd 'import os; os.listdir()' +""" + +parser = argparse.ArgumentParser(description='webrepl - connect to websocket webrepl', + formatter_class=argparse.RawDescriptionHelpFormatter, + epilog=examples) +parser.add_argument('--host', '-i', default=os.environ.get('WEBREPL_HOST', None), help="Host to connect to") +parser.add_argument('--port', '-P', type=int, default=os.environ.get('WEBREPL_PORT', 8266), help="Port to connect to") +parser.add_argument('--verbose', '-v', action='store_true', help="Verbose information") +parser.add_argument('--debug', '-d', action='store_true', help="Enable debugging messages") +parser.add_argument('--password', '-p', default=os.environ.get('WEBREPL_PASSWORD', None), help="Use following password to connect") +parser.add_argument('--before', '-B', action='append', default=os.environ.get('WEBREPL_BEFORE', None), help="command to execute before") +parser.add_argument('--cmd', '-c', action='append', default=os.environ.get('WEBREPL_CMD', None), help="command to execute") +parser.add_argument('--after', '-A', action='append', default=os.environ.get('WEBREPL_AFTER', None), help="command to execute after") +parser.add_argument('commands', metavar='CMD', type=str, nargs='+', + help='commands for repl') + +args = parser.parse_args() + +if not args.host: + print("You need to specify host") + parser.print_usage() + sys.exit() + +password='' +if not args.password: + print("You need to specify password") + try: + import getpass + password = getpass.getpass() + except: + parser.print_usage() + sys.exit() +else: + password = args.password + +if not args.commands or len(args.commands)<1: + print("Command is not recognized/given") + parser.print_usage() + sys.exit() + +repl=webrepl.Webrepl(**{}) + +if args.debug: + repl.debug = True + +if args.verbose: + repl.verbose = True + +try: + repl.connect(args.host, args.port) + repl.login(password) +except Exception as e: + print("Error connecting to host",args.host,"at port",args.port,":",e) + sys.exit() + +if not repl.connected: + print("Not connected. Check your password!") + sys.exit() + +if args.before: + for cmd in args.before: + print("[i] Issuing command: "+cmd) + try: + r=repl.send_cmd(cmd) + print(r.decode()) + except Exception as e: + print("[e] Error running command:",cmd,":",e) + +cmd = args.commands[0].lower() + +if cmd == "ver" or cmd == "version": + if args.verbose: + print("[i] Getting version") + ver=repl.get_ver() + print("[i] Version ",ver[0], ver[1], ver[2]) +elif cmd == "list" or cmd == "ls": + print("[i] Listing") + r=repl.sendcmd("import os; os.listdir()") + print(r.decode()) +elif cmd == "get" or cmd == "download": + if len(args.commands)<3: + sys.stderr.write("Not enough arguments for "+cmd+"\n") + sys.exit() + source=args.commands[1] + dest=args.commands[2] + if args.verbose: + print("[i] Downloading "+source+" to "+dest) + repl.get_file(source, dest) +elif cmd == "put" or cmd == "upload": + if len(args.commands)<3: + sys.stderr.write("Not enough arguments for "+cmd+"\n") + sys.exit() + source=args.commands[1] + dest=args.commands[2] + if args.verbose: + print("[i] Uploading "+source+" to "+dest) + repl.put_file(source, dest) +elif cmd == "del" or cmd == "rm" or cmd == "dele": + if len(args.commands)<2: + sys.stderr.write("Not enough arguments for "+cmd+"\n") + sys.exit() + filename=args.commands[1] + if args.verbose: + print("[i] Deleting "+filename) + r=repl.sendcmd("import os; os.remove('"+filename+"')") + print(r.decode()) +elif cmd == "print" or cmd == "cat" or cmd == "type": + if len(args.commands)<2: + sys.stderr.write("Not enough arguments for "+cmd+"\n") + sys.exit() + source=args.commands[1] + if args.verbose: + print("[i] Content of file",source) + r=repl.get_file_content(source) + print(r.decode()) +elif cmd == "command" or cmd == "cmd": + if len(args.commands)<2: + sys.stderr.write("Not enough arguments for "+cmd+"\n") + sys.exit() + cmd=args.commands[1] + if args.verbose: + print("[i] Executing "+cmd) + r=repl.sendcmd(cmd) + print(r.decode()) +else: + sys.stderr.write("Command not recognized\n") + +if args.cmd: + for cmd in args.cmd: + print("[i] Issuing command: "+cmd) + try: + r=repl.send_cmd(cmd) + print(r.decode()) + except Exception as e: + print("[e] Error running command:",cmd,":",e) + +if args.after: + for cmd in args.after: + print("[i] Issuing command: "+cmd) + try: + r=repl.send_cmd(cmd) + print(r.decode()) + except Exception as e: + print("[e] Error running command:",cmd,":",e) + +if args.verbose: + print("[i] closing REPL/WS") +repl.disconnect() + diff --git a/code/.venv/lib/python3.12/site-packages/PyBoard-1.1.4.dist-info/INSTALLER b/code/.venv/lib/python3.12/site-packages/PyBoard-1.1.4.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/PyBoard-1.1.4.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/code/.venv/lib/python3.12/site-packages/PyBoard-1.1.4.dist-info/METADATA b/code/.venv/lib/python3.12/site-packages/PyBoard-1.1.4.dist-info/METADATA new file mode 100644 index 0000000..979a415 --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/PyBoard-1.1.4.dist-info/METADATA @@ -0,0 +1,36 @@ +Metadata-Version: 2.1 +Name: PyBoard +Version: 1.1.4 +Summary: A Python package for programming with Arduino. +Home-page: https://github.com/kaiyu-liu/PyBoard +Author: Kevin Liu +Author-email: winaes@126.com +License: GPL v3 +Classifier: Environment :: Other Environment +Classifier: Intended Audience :: Developers +Classifier: Intended Audience :: Education +Classifier: Natural Language :: English +Classifier: Operating System :: MacOS +Classifier: Operating System :: Microsoft +Classifier: Operating System :: POSIX +Classifier: Operating System :: Unix +Classifier: Topic :: Scientific/Engineering +Classifier: Topic :: Education +Classifier: Programming Language :: Python :: 3.5 +Requires-Dist: pymata-aio >=2.25 + +# PyBoard +A project that helps to use Python in STEM projects easily. The project is based on PyMata and makes the APIs as close to the functions in Arduino as possible. + +== Installation + +pip install PyBoard + +== Usage + +Please look at the examples folder to see how it works. + +== License + +GPL v3 + diff --git a/code/.venv/lib/python3.12/site-packages/PyBoard-1.1.4.dist-info/RECORD b/code/.venv/lib/python3.12/site-packages/PyBoard-1.1.4.dist-info/RECORD new file mode 100644 index 0000000..e958f3c --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/PyBoard-1.1.4.dist-info/RECORD @@ -0,0 +1,18 @@ +PyBoard-1.1.4.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +PyBoard-1.1.4.dist-info/METADATA,sha256=75bL6KHfusE99zg6qBm4C-zKf-yJK7-5ocJC3fLKetQ,1003 +PyBoard-1.1.4.dist-info/RECORD,, +PyBoard-1.1.4.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +PyBoard-1.1.4.dist-info/WHEEL,sha256=HiCZjzuy6Dw0hdX5R3LCFPDmFS4BWl8H-8W39XfmgX4,91 +PyBoard-1.1.4.dist-info/top_level.txt,sha256=Lxn4VWX3v2-acNb1pnoNCabY7LslscnlLsGkKCd67sI,8 +pyboard/__init__.py,sha256=MAiDuBlNc7SGg1NuQvafd1_MRIUO5zn1CryL_akIiCk,768 +pyboard/__pycache__/__init__.cpython-312.pyc,, +pyboard/__pycache__/board.cpython-312.pyc,, +pyboard/__pycache__/main.cpython-312.pyc,, +pyboard/__pycache__/pyboard_constants.cpython-312.pyc,, +pyboard/__pycache__/pyboard_core.cpython-312.pyc,, +pyboard/__pycache__/pyboard_iot.cpython-312.pyc,, +pyboard/board.py,sha256=xni359QZD1eM_r5LEeaLJG4dC13n-e-pDVFBHP_zCl4,31731 +pyboard/main.py,sha256=6vwDSMG7w9RQb66JONgyGwKjp3EHBzUkZbRVLjdoAwc,901 +pyboard/pyboard_constants.py,sha256=vXr56UFRGTbuvABYiKbxUVsDzSl0sEXh3SI81ro2dDU,1131 +pyboard/pyboard_core.py,sha256=K20f2JxM--809EBmToOUsH8WpbQpvrunNbfEJQDlaFs,7227 +pyboard/pyboard_iot.py,sha256=D3MhMPKpGadnEFzSKydmqe6_UGxAj0Wulmn-qByS5XY,30600 diff --git a/code/.venv/lib/python3.12/site-packages/PyBoard-1.1.4.dist-info/REQUESTED b/code/.venv/lib/python3.12/site-packages/PyBoard-1.1.4.dist-info/REQUESTED new file mode 100644 index 0000000..e69de29 diff --git a/code/.venv/lib/python3.12/site-packages/PyBoard-1.1.4.dist-info/WHEEL b/code/.venv/lib/python3.12/site-packages/PyBoard-1.1.4.dist-info/WHEEL new file mode 100644 index 0000000..71360e0 --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/PyBoard-1.1.4.dist-info/WHEEL @@ -0,0 +1,5 @@ +Wheel-Version: 1.0 +Generator: setuptools (72.2.0) +Root-Is-Purelib: true +Tag: py3-none-any + diff --git a/code/.venv/lib/python3.12/site-packages/PyBoard-1.1.4.dist-info/top_level.txt b/code/.venv/lib/python3.12/site-packages/PyBoard-1.1.4.dist-info/top_level.txt new file mode 100644 index 0000000..7782f23 --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/PyBoard-1.1.4.dist-info/top_level.txt @@ -0,0 +1 @@ +pyboard diff --git a/code/.venv/lib/python3.12/site-packages/PyYAML-6.0.2.dist-info/INSTALLER b/code/.venv/lib/python3.12/site-packages/PyYAML-6.0.2.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/PyYAML-6.0.2.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/code/.venv/lib/python3.12/site-packages/PyYAML-6.0.2.dist-info/LICENSE b/code/.venv/lib/python3.12/site-packages/PyYAML-6.0.2.dist-info/LICENSE new file mode 100644 index 0000000..2f1b8e1 --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/PyYAML-6.0.2.dist-info/LICENSE @@ -0,0 +1,20 @@ +Copyright (c) 2017-2021 Ingy döt Net +Copyright (c) 2006-2016 Kirill Simonov + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/code/.venv/lib/python3.12/site-packages/PyYAML-6.0.2.dist-info/METADATA b/code/.venv/lib/python3.12/site-packages/PyYAML-6.0.2.dist-info/METADATA new file mode 100644 index 0000000..db029b7 --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/PyYAML-6.0.2.dist-info/METADATA @@ -0,0 +1,46 @@ +Metadata-Version: 2.1 +Name: PyYAML +Version: 6.0.2 +Summary: YAML parser and emitter for Python +Home-page: https://pyyaml.org/ +Download-URL: https://pypi.org/project/PyYAML/ +Author: Kirill Simonov +Author-email: xi@resolvent.net +License: MIT +Project-URL: Bug Tracker, https://github.com/yaml/pyyaml/issues +Project-URL: CI, https://github.com/yaml/pyyaml/actions +Project-URL: Documentation, https://pyyaml.org/wiki/PyYAMLDocumentation +Project-URL: Mailing lists, http://lists.sourceforge.net/lists/listinfo/yaml-core +Project-URL: Source Code, https://github.com/yaml/pyyaml +Platform: Any +Classifier: Development Status :: 5 - Production/Stable +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: MIT License +Classifier: Operating System :: OS Independent +Classifier: Programming Language :: Cython +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: 3.9 +Classifier: Programming Language :: Python :: 3.10 +Classifier: Programming Language :: Python :: 3.11 +Classifier: Programming Language :: Python :: 3.12 +Classifier: Programming Language :: Python :: 3.13 +Classifier: Programming Language :: Python :: Implementation :: CPython +Classifier: Programming Language :: Python :: Implementation :: PyPy +Classifier: Topic :: Software Development :: Libraries :: Python Modules +Classifier: Topic :: Text Processing :: Markup +Requires-Python: >=3.8 +License-File: LICENSE + +YAML is a data serialization format designed for human readability +and interaction with scripting languages. PyYAML is a YAML parser +and emitter for Python. + +PyYAML features a complete YAML 1.1 parser, Unicode support, pickle +support, capable extension API, and sensible error messages. PyYAML +supports standard YAML tags and provides Python-specific tags that +allow to represent an arbitrary Python object. + +PyYAML is applicable for a broad range of tasks from complex +configuration files to object serialization and persistence. diff --git a/code/.venv/lib/python3.12/site-packages/PyYAML-6.0.2.dist-info/RECORD b/code/.venv/lib/python3.12/site-packages/PyYAML-6.0.2.dist-info/RECORD new file mode 100644 index 0000000..f596c8e --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/PyYAML-6.0.2.dist-info/RECORD @@ -0,0 +1,43 @@ +PyYAML-6.0.2.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +PyYAML-6.0.2.dist-info/LICENSE,sha256=jTko-dxEkP1jVwfLiOsmvXZBAqcoKVQwfT5RZ6V36KQ,1101 +PyYAML-6.0.2.dist-info/METADATA,sha256=9-odFB5seu4pGPcEv7E8iyxNF51_uKnaNGjLAhz2lto,2060 +PyYAML-6.0.2.dist-info/RECORD,, +PyYAML-6.0.2.dist-info/WHEEL,sha256=1pP4yhrbipRtdbm4Rbg3aoTjzc7pDhpHKO0CEY24CNM,152 +PyYAML-6.0.2.dist-info/top_level.txt,sha256=rpj0IVMTisAjh_1vG3Ccf9v5jpCQwAz6cD1IVU5ZdhQ,11 +_yaml/__init__.py,sha256=04Ae_5osxahpJHa3XBZUAf4wi6XX32gR8D6X6p64GEA,1402 +_yaml/__pycache__/__init__.cpython-312.pyc,, +yaml/__init__.py,sha256=N35S01HMesFTe0aRRMWkPj0Pa8IEbHpE9FK7cr5Bdtw,12311 +yaml/__pycache__/__init__.cpython-312.pyc,, +yaml/__pycache__/composer.cpython-312.pyc,, +yaml/__pycache__/constructor.cpython-312.pyc,, +yaml/__pycache__/cyaml.cpython-312.pyc,, +yaml/__pycache__/dumper.cpython-312.pyc,, +yaml/__pycache__/emitter.cpython-312.pyc,, +yaml/__pycache__/error.cpython-312.pyc,, +yaml/__pycache__/events.cpython-312.pyc,, +yaml/__pycache__/loader.cpython-312.pyc,, +yaml/__pycache__/nodes.cpython-312.pyc,, +yaml/__pycache__/parser.cpython-312.pyc,, +yaml/__pycache__/reader.cpython-312.pyc,, +yaml/__pycache__/representer.cpython-312.pyc,, +yaml/__pycache__/resolver.cpython-312.pyc,, +yaml/__pycache__/scanner.cpython-312.pyc,, +yaml/__pycache__/serializer.cpython-312.pyc,, +yaml/__pycache__/tokens.cpython-312.pyc,, +yaml/_yaml.cpython-312-x86_64-linux-gnu.so,sha256=PJFgxnc0f5Dyde6WKmBm6fZWapawmWl7aBRruXjRA80,2481784 +yaml/composer.py,sha256=_Ko30Wr6eDWUeUpauUGT3Lcg9QPBnOPVlTnIMRGJ9FM,4883 +yaml/constructor.py,sha256=kNgkfaeLUkwQYY_Q6Ff1Tz2XVw_pG1xVE9Ak7z-viLA,28639 +yaml/cyaml.py,sha256=6ZrAG9fAYvdVe2FK_w0hmXoG7ZYsoYUwapG8CiC72H0,3851 +yaml/dumper.py,sha256=PLctZlYwZLp7XmeUdwRuv4nYOZ2UBnDIUy8-lKfLF-o,2837 +yaml/emitter.py,sha256=jghtaU7eFwg31bG0B7RZea_29Adi9CKmXq_QjgQpCkQ,43006 +yaml/error.py,sha256=Ah9z-toHJUbE9j-M8YpxgSRM5CgLCcwVzJgLLRF2Fxo,2533 +yaml/events.py,sha256=50_TksgQiE4up-lKo_V-nBy-tAIxkIPQxY5qDhKCeHw,2445 +yaml/loader.py,sha256=UVa-zIqmkFSCIYq_PgSGm4NSJttHY2Rf_zQ4_b1fHN0,2061 +yaml/nodes.py,sha256=gPKNj8pKCdh2d4gr3gIYINnPOaOxGhJAUiYhGRnPE84,1440 +yaml/parser.py,sha256=ilWp5vvgoHFGzvOZDItFoGjD6D42nhlZrZyjAwa0oJo,25495 +yaml/reader.py,sha256=0dmzirOiDG4Xo41RnuQS7K9rkY3xjHiVasfDMNTqCNw,6794 +yaml/representer.py,sha256=IuWP-cAW9sHKEnS0gCqSa894k1Bg4cgTxaDwIcbRQ-Y,14190 +yaml/resolver.py,sha256=9L-VYfm4mWHxUD1Vg4X7rjDRK_7VZd6b92wzq7Y2IKY,9004 +yaml/scanner.py,sha256=YEM3iLZSaQwXcQRg2l2R4MdT0zGP2F9eHkKGKnHyWQY,51279 +yaml/serializer.py,sha256=ChuFgmhU01hj4xgI8GaKv6vfM2Bujwa9i7d2FAHj7cA,4165 +yaml/tokens.py,sha256=lTQIzSVw8Mg9wv459-TjiOQe6wVziqaRlqX2_89rp54,2573 diff --git a/code/.venv/lib/python3.12/site-packages/PyYAML-6.0.2.dist-info/WHEEL b/code/.venv/lib/python3.12/site-packages/PyYAML-6.0.2.dist-info/WHEEL new file mode 100644 index 0000000..56616a8 --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/PyYAML-6.0.2.dist-info/WHEEL @@ -0,0 +1,6 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.44.0) +Root-Is-Purelib: false +Tag: cp312-cp312-manylinux_2_17_x86_64 +Tag: cp312-cp312-manylinux2014_x86_64 + diff --git a/code/.venv/lib/python3.12/site-packages/PyYAML-6.0.2.dist-info/top_level.txt b/code/.venv/lib/python3.12/site-packages/PyYAML-6.0.2.dist-info/top_level.txt new file mode 100644 index 0000000..e6475e9 --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/PyYAML-6.0.2.dist-info/top_level.txt @@ -0,0 +1,2 @@ +_yaml +yaml diff --git a/code/.venv/lib/python3.12/site-packages/__pycache__/_virtualenv.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/__pycache__/_virtualenv.cpython-312.pyc new file mode 100644 index 0000000..3c1bd67 Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/__pycache__/_virtualenv.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/__pycache__/reedsolo.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/__pycache__/reedsolo.cpython-312.pyc new file mode 100644 index 0000000..113b41b Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/__pycache__/reedsolo.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/__pycache__/six.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/__pycache__/six.cpython-312.pyc new file mode 100644 index 0000000..c81f1ed Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/__pycache__/six.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/__pycache__/webrepl.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/__pycache__/webrepl.cpython-312.pyc new file mode 100644 index 0000000..897fa12 Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/__pycache__/webrepl.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/_cffi_backend.cpython-312-x86_64-linux-gnu.so b/code/.venv/lib/python3.12/site-packages/_cffi_backend.cpython-312-x86_64-linux-gnu.so new file mode 100755 index 0000000..6bf6e4f Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/_cffi_backend.cpython-312-x86_64-linux-gnu.so differ diff --git a/code/.venv/lib/python3.12/site-packages/_virtualenv.pth b/code/.venv/lib/python3.12/site-packages/_virtualenv.pth new file mode 100644 index 0000000..1c3ff99 --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/_virtualenv.pth @@ -0,0 +1 @@ +import _virtualenv \ No newline at end of file diff --git a/code/.venv/lib/python3.12/site-packages/_virtualenv.py b/code/.venv/lib/python3.12/site-packages/_virtualenv.py new file mode 100644 index 0000000..b61db30 --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/_virtualenv.py @@ -0,0 +1,103 @@ +"""Patches that are applied at runtime to the virtual environment.""" + +from __future__ import annotations + +import os +import sys + +VIRTUALENV_PATCH_FILE = os.path.join(__file__) + + +def patch_dist(dist): + """ + Distutils allows user to configure some arguments via a configuration file: + https://docs.python.org/3/install/index.html#distutils-configuration-files. + + Some of this arguments though don't make sense in context of the virtual environment files, let's fix them up. + """ # noqa: D205 + # we cannot allow some install config as that would get packages installed outside of the virtual environment + old_parse_config_files = dist.Distribution.parse_config_files + + def parse_config_files(self, *args, **kwargs): + result = old_parse_config_files(self, *args, **kwargs) + install = self.get_option_dict("install") + + if "prefix" in install: # the prefix governs where to install the libraries + install["prefix"] = VIRTUALENV_PATCH_FILE, os.path.abspath(sys.prefix) + for base in ("purelib", "platlib", "headers", "scripts", "data"): + key = f"install_{base}" + if key in install: # do not allow global configs to hijack venv paths + install.pop(key, None) + return result + + dist.Distribution.parse_config_files = parse_config_files + + +# Import hook that patches some modules to ignore configuration values that break package installation in case +# of virtual environments. +_DISTUTILS_PATCH = "distutils.dist", "setuptools.dist" +# https://docs.python.org/3/library/importlib.html#setting-up-an-importer + + +class _Finder: + """A meta path finder that allows patching the imported distutils modules.""" + + fullname = None + + # lock[0] is threading.Lock(), but initialized lazily to avoid importing threading very early at startup, + # because there are gevent-based applications that need to be first to import threading by themselves. + # See https://github.com/pypa/virtualenv/issues/1895 for details. + lock = [] # noqa: RUF012 + + def find_spec(self, fullname, path, target=None): # noqa: ARG002 + if fullname in _DISTUTILS_PATCH and self.fullname is None: # noqa: PLR1702 + # initialize lock[0] lazily + if len(self.lock) == 0: + import threading # noqa: PLC0415 + + lock = threading.Lock() + # there is possibility that two threads T1 and T2 are simultaneously running into find_spec, + # observing .lock as empty, and further going into hereby initialization. However due to the GIL, + # list.append() operation is atomic and this way only one of the threads will "win" to put the lock + # - that every thread will use - into .lock[0]. + # https://docs.python.org/3/faq/library.html#what-kinds-of-global-value-mutation-are-thread-safe + self.lock.append(lock) + + from functools import partial # noqa: PLC0415 + from importlib.util import find_spec # noqa: PLC0415 + + with self.lock[0]: + self.fullname = fullname + try: + spec = find_spec(fullname, path) + if spec is not None: + # https://www.python.org/dev/peps/pep-0451/#how-loading-will-work + is_new_api = hasattr(spec.loader, "exec_module") + func_name = "exec_module" if is_new_api else "load_module" + old = getattr(spec.loader, func_name) + func = self.exec_module if is_new_api else self.load_module + if old is not func: + try: # noqa: SIM105 + setattr(spec.loader, func_name, partial(func, old)) + except AttributeError: + pass # C-Extension loaders are r/o such as zipimporter with <3.7 + return spec + finally: + self.fullname = None + return None + + @staticmethod + def exec_module(old, module): + old(module) + if module.__name__ in _DISTUTILS_PATCH: + patch_dist(module) + + @staticmethod + def load_module(old, name): + module = old(name) + if module.__name__ in _DISTUTILS_PATCH: + patch_dist(module) + return module + + +sys.meta_path.insert(0, _Finder()) diff --git a/code/.venv/lib/python3.12/site-packages/_yaml/__init__.py b/code/.venv/lib/python3.12/site-packages/_yaml/__init__.py new file mode 100644 index 0000000..7baa8c4 --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/_yaml/__init__.py @@ -0,0 +1,33 @@ +# This is a stub package designed to roughly emulate the _yaml +# extension module, which previously existed as a standalone module +# and has been moved into the `yaml` package namespace. +# It does not perfectly mimic its old counterpart, but should get +# close enough for anyone who's relying on it even when they shouldn't. +import yaml + +# in some circumstances, the yaml module we imoprted may be from a different version, so we need +# to tread carefully when poking at it here (it may not have the attributes we expect) +if not getattr(yaml, '__with_libyaml__', False): + from sys import version_info + + exc = ModuleNotFoundError if version_info >= (3, 6) else ImportError + raise exc("No module named '_yaml'") +else: + from yaml._yaml import * + import warnings + warnings.warn( + 'The _yaml extension module is now located at yaml._yaml' + ' and its location is subject to change. To use the' + ' LibYAML-based parser and emitter, import from `yaml`:' + ' `from yaml import CLoader as Loader, CDumper as Dumper`.', + DeprecationWarning + ) + del warnings + # Don't `del yaml` here because yaml is actually an existing + # namespace member of _yaml. + +__name__ = '_yaml' +# If the module is top-level (i.e. not a part of any specific package) +# then the attribute should be set to ''. +# https://docs.python.org/3.8/library/types.html +__package__ = '' diff --git a/code/.venv/lib/python3.12/site-packages/_yaml/__pycache__/__init__.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/_yaml/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..a8d96e0 Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/_yaml/__pycache__/__init__.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/bitarray-2.9.2.dist-info/INSTALLER b/code/.venv/lib/python3.12/site-packages/bitarray-2.9.2.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/bitarray-2.9.2.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/code/.venv/lib/python3.12/site-packages/bitarray-2.9.2.dist-info/LICENSE b/code/.venv/lib/python3.12/site-packages/bitarray-2.9.2.dist-info/LICENSE new file mode 100644 index 0000000..f99e04d --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/bitarray-2.9.2.dist-info/LICENSE @@ -0,0 +1,46 @@ +PYTHON SOFTWARE FOUNDATION LICENSE +---------------------------------- + +1. This LICENSE AGREEMENT is between Ilan Schnell, and the Individual or +Organization ("Licensee") accessing and otherwise using this software +("bitarray") in source or binary form and its associated documentation. + +2. Subject to the terms and conditions of this License Agreement, Ilan Schnell +hereby grants Licensee a nonexclusive, royalty-free, world-wide +license to reproduce, analyze, test, perform and/or display publicly, +prepare derivative works, distribute, and otherwise use bitarray +alone or in any derivative version, provided, however, that Ilan Schnell's +License Agreement and Ilan Schnell's notice of copyright, i.e., "Copyright (c) +2008 - 2024 Ilan Schnell; All Rights Reserved" are retained in bitarray +alone or in any derivative version prepared by Licensee. + +3. In the event Licensee prepares a derivative work that is based on +or incorporates bitarray or any part thereof, and wants to make +the derivative work available to others as provided herein, then +Licensee hereby agrees to include in any such work a brief summary of +the changes made to bitarray. + +4. Ilan Schnell is making bitarray available to Licensee on an "AS IS" +basis. ILAN SCHNELL MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR +IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, ILAN SCHNELL MAKES NO AND +DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS +FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF BITARRAY WILL NOT +INFRINGE ANY THIRD PARTY RIGHTS. + +5. ILAN SCHNELL SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF BITARRAY +FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS +A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING BITARRAY, +OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF. + +6. This License Agreement will automatically terminate upon a material +breach of its terms and conditions. + +7. Nothing in this License Agreement shall be deemed to create any +relationship of agency, partnership, or joint venture between Ilan Schnell +and Licensee. This License Agreement does not grant permission to use Ilan +Schnell trademarks or trade name in a trademark sense to endorse or promote +products or services of Licensee, or any third party. + +8. By copying, installing or otherwise using bitarray, Licensee +agrees to be bound by the terms and conditions of this License +Agreement. diff --git a/code/.venv/lib/python3.12/site-packages/bitarray-2.9.2.dist-info/METADATA b/code/.venv/lib/python3.12/site-packages/bitarray-2.9.2.dist-info/METADATA new file mode 100644 index 0000000..367f8ae --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/bitarray-2.9.2.dist-info/METADATA @@ -0,0 +1,1022 @@ +Metadata-Version: 2.1 +Name: bitarray +Version: 2.9.2 +Summary: efficient arrays of booleans -- C extension +Home-page: https://github.com/ilanschnell/bitarray +Author: Ilan Schnell +Author-email: ilanschnell@gmail.com +License: PSF +Classifier: License :: OSI Approved :: Python Software Foundation License +Classifier: Development Status :: 6 - Mature +Classifier: Intended Audience :: Developers +Classifier: Operating System :: OS Independent +Classifier: Programming Language :: C +Classifier: Programming Language :: Python :: 2 +Classifier: Programming Language :: Python :: 2.7 +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3.5 +Classifier: Programming Language :: Python :: 3.6 +Classifier: Programming Language :: Python :: 3.7 +Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: 3.9 +Classifier: Programming Language :: Python :: 3.10 +Classifier: Programming Language :: Python :: 3.11 +Classifier: Programming Language :: Python :: 3.12 +Classifier: Topic :: Utilities +License-File: LICENSE + +bitarray: efficient arrays of booleans +====================================== + +This library provides an object type which efficiently represents an array +of booleans. Bitarrays are sequence types and behave very much like usual +lists. Eight bits are represented by one byte in a contiguous block of +memory. The user can select between two representations: little-endian +and big-endian. All functionality is implemented in C. +Methods for accessing the machine representation are provided, including the +ability to import and export buffers. This allows creating bitarrays that +are mapped to other objects, including memory-mapped files. + + +Roadmap +------- + +In 2024 (probably around July), we are planning the release of bitarray 3.0. +The 3.0 release will: + +* Remove Python 2.7 support. +* Rename ``.itersearch()`` to ``.search()`` and ``.iterdecode()`` + to ``.decode()`` (and remove their non-iterator counterpart). +* Remove ``util.rindex()``, use ``.index(..., right=1)`` instead +* Remove ``util.make_endian()``, use ``bitarray(..., endian=...)`` instead +* Remove hackish support for ``bitarray()`` handling unpickling, + see detailed explaination in `#207 `__. This will close `#206 `__. + + +Key features +------------ + +* The bit-endianness can be specified for each bitarray object, see below. +* Sequence methods: slicing (including slice assignment and deletion), + operations ``+``, ``*``, ``+=``, ``*=``, the ``in`` operator, ``len()`` +* Bitwise operations: ``~``, ``&``, ``|``, ``^``, ``<<``, ``>>`` (as well as + their in-place versions ``&=``, ``|=``, ``^=``, ``<<=``, ``>>=``). +* Fast methods for encoding and decoding variable bit length prefix codes. +* Bitarray objects support the buffer protocol (both importing and + exporting buffers). +* Packing and unpacking to other binary data formats, e.g. ``numpy.ndarray``. +* Pickling and unpickling of bitarray objects. +* Immutable ``frozenbitarray`` objects which are hashable +* Sequential search +* Type hinting +* Extensive test suite with about 500 unittests. +* Utility module ``bitarray.util``: + + * conversion to and from hexadecimal strings + * (de-) serialization + * pretty printing + * conversion to and from integers + * creating Huffman codes + * compression of sparse bitarrays + * various count functions + * other helpful functions + + +Installation +------------ + +Python wheels are are available on PyPI for all mayor platforms and Python +versions. Which means you can simply: + +.. code-block:: shell-session + + $ pip install bitarray + +In addition, conda packages are available (both the default Anaconda +repository as well as conda-forge support bitarray): + +.. code-block:: shell-session + + $ conda install bitarray + +Once you have installed the package, you may want to test it: + +.. code-block:: shell-session + + $ python -c 'import bitarray; bitarray.test()' + bitarray is installed in: /Users/ilan/bitarray/bitarray + bitarray version: 2.9.2 + sys.version: 3.11.0 (main, Oct 25 2022) [Clang 14.0.4] + sys.prefix: /Users/ilan/Mini3/envs/py311 + pointer size: 64 bit + sizeof(size_t): 8 + sizeof(bitarrayobject): 80 + HAVE_BUILTIN_BSWAP64: 1 + default bit-endianness: big + machine byte-order: little + DEBUG: 0 + ......................................................................... + ......................................................................... + ................................................................ + ---------------------------------------------------------------------- + Ran 502 tests in 0.416s + + OK + +The ``test()`` function is part of the API. It will return +a ``unittest.runner.TextTestResult`` object, such that one can verify that +all tests ran successfully by: + +.. code-block:: python + + import bitarray + assert bitarray.test().wasSuccessful() + + +Usage +----- + +As mentioned above, bitarray objects behave very much like lists, so +there is not too much to learn. The biggest difference from list +objects (except that bitarray are obviously homogeneous) is the ability +to access the machine representation of the object. +When doing so, the bit-endianness is of importance; this issue is +explained in detail in the section below. Here, we demonstrate the +basic usage of bitarray objects: + +.. code-block:: python + + >>> from bitarray import bitarray + >>> a = bitarray() # create empty bitarray + >>> a.append(1) + >>> a.extend([1, 0]) + >>> a + bitarray('110') + >>> x = bitarray(2 ** 20) # bitarray of length 1048576 (initialized to 0) + >>> len(x) + 1048576 + >>> bitarray('1001 011') # initialize from string (whitespace is ignored) + bitarray('1001011') + >>> lst = [1, 0, False, True, True] + >>> a = bitarray(lst) # initialize from iterable + >>> a + bitarray('10011') + >>> a[2] # indexing a single item will always return an integer + 0 + >>> a[2:4] # whereas indexing a slice will always return a bitarray + bitarray('01') + >>> a[2:3] # even when the slice length is just one + bitarray('0') + >>> a.count(1) + 3 + >>> a.remove(0) # removes first occurrence of 0 + >>> a + bitarray('1011') + +Like lists, bitarray objects support slice assignment and deletion: + +.. code-block:: python + + >>> a = bitarray(50) + >>> a.setall(0) # set all elements in a to 0 + >>> a[11:37:3] = 9 * bitarray('1') + >>> a + bitarray('00000000000100100100100100100100100100000000000000') + >>> del a[12::3] + >>> a + bitarray('0000000000010101010101010101000000000') + >>> a[-6:] = bitarray('10011') + >>> a + bitarray('000000000001010101010101010100010011') + >>> a += bitarray('000111') + >>> a[9:] + bitarray('001010101010101010100010011000111') + +In addition, slices can be assigned to booleans, which is easier (and +faster) than assigning to a bitarray in which all values are the same: + +.. code-block:: python + + >>> a = 20 * bitarray('0') + >>> a[1:15:3] = True + >>> a + bitarray('01001001001001000000') + +This is easier and faster than: + +.. code-block:: python + + >>> a = 20 * bitarray('0') + >>> a[1:15:3] = 5 * bitarray('1') + >>> a + bitarray('01001001001001000000') + +Note that in the latter we have to create a temporary bitarray whose length +must be known or calculated. Another example of assigning slices to Booleans, +is setting ranges: + +.. code-block:: python + + >>> a = bitarray(30) + >>> a[:] = 0 # set all elements to 0 - equivalent to a.setall(0) + >>> a[10:25] = 1 # set elements in range(10, 25) to 1 + >>> a + bitarray('000000000011111111111111100000') + +As of bitarray version 2.8, indices may also be lists of arbitrary +indices (like in NumPy), or bitarrays that are treated as masks, +see `Bitarray indexing `__. + + +Bitwise operators +----------------- + +Bitarray objects support the bitwise operators ``~``, ``&``, ``|``, ``^``, +``<<``, ``>>`` (as well as their in-place versions ``&=``, ``|=``, ``^=``, +``<<=``, ``>>=``). The behavior is very much what one would expect: + +.. code-block:: python + + >>> a = bitarray('101110001') + >>> ~a # invert + bitarray('010001110') + >>> b = bitarray('111001011') + >>> a ^ b + bitarray('010111010') + >>> a &= b + >>> a + bitarray('101000001') + >>> a <<= 2 # in-place left shift by 2 + >>> a + bitarray('100000100') + >>> b >> 1 + bitarray('011100101') + +The C language does not specify the behavior of negative shifts and +of left shifts larger or equal than the width of the promoted left operand. +The exact behavior is compiler/machine specific. +This Python bitarray library specifies the behavior as follows: + +* the length of the bitarray is never changed by any shift operation +* blanks are filled by 0 +* negative shifts raise ``ValueError`` +* shifts larger or equal to the length of the bitarray result in + bitarrays with all values 0 + +It is worth noting that (regardless of bit-endianness) the bitarray left +shift (``<<``) always shifts towards lower indices, and the right +shift (``>>``) always shifts towards higher indices. + + +Bit-endianness +-------------- + +Unless explicitly converting to machine representation, using +the ``.tobytes()``, ``.frombytes()``, ``.tofile()`` and ``.fromfile()`` +methods, as well as using ``memoryview``, the bit-endianness will have no +effect on any computation, and one can skip this section. + +Since bitarrays allows addressing individual bits, where the machine +represents 8 bits in one byte, there are two obvious choices for this +mapping: little-endian and big-endian. + +When dealing with the machine representation of bitarray objects, it is +recommended to always explicitly specify the endianness. + +By default, bitarrays use big-endian representation: + +.. code-block:: python + + >>> a = bitarray() + >>> a.endian() + 'big' + >>> a.frombytes(b'A') + >>> a + bitarray('01000001') + >>> a[6] = 1 + >>> a.tobytes() + b'C' + +Big-endian means that the most-significant bit comes first. +Here, ``a[0]`` is the lowest address (index) and most significant bit, +and ``a[7]`` is the highest address and least significant bit. + +When creating a new bitarray object, the endianness can always be +specified explicitly: + +.. code-block:: python + + >>> a = bitarray(endian='little') + >>> a.frombytes(b'A') + >>> a + bitarray('10000010') + >>> a.endian() + 'little' + +Here, the low-bit comes first because little-endian means that increasing +numeric significance corresponds to an increasing address. +So ``a[0]`` is the lowest address and least significant bit, +and ``a[7]`` is the highest address and most significant bit. + +The bit-endianness is a property of the bitarray object. +The endianness cannot be changed once a bitarray object is created. +When comparing bitarray objects, the endianness (and hence the machine +representation) is irrelevant; what matters is the mapping from indices +to bits: + +.. code-block:: python + + >>> bitarray('11001', endian='big') == bitarray('11001', endian='little') + True + +Bitwise operations (``|``, ``^``, ``&=``, ``|=``, ``^=``, ``~``) are +implemented efficiently using the corresponding byte operations in C, i.e. the +operators act on the machine representation of the bitarray objects. +Therefore, it is not possible to perform bitwise operators on bitarrays +with different endianness. + +When converting to and from machine representation, using +the ``.tobytes()``, ``.frombytes()``, ``.tofile()`` and ``.fromfile()`` +methods, the endianness matters: + +.. code-block:: python + + >>> a = bitarray(endian='little') + >>> a.frombytes(b'\x01') + >>> a + bitarray('10000000') + >>> b = bitarray(endian='big') + >>> b.frombytes(b'\x80') + >>> b + bitarray('10000000') + >>> a == b + True + >>> a.tobytes() == b.tobytes() + False + +As mentioned above, the endianness can not be changed once an object is +created. However, you can create a new bitarray with different endianness: + +.. code-block:: python + + >>> a = bitarray('111000', endian='little') + >>> b = bitarray(a, endian='big') + >>> b + bitarray('111000') + >>> a == b + True + + +Buffer protocol +--------------- + +Bitarray objects support the buffer protocol. They can both export their +own buffer, as well as import another object's buffer. To learn more about +this topic, please read `buffer protocol `__. There is also an example that shows how +to memory-map a file to a bitarray: `mmapped-file.py `__ + + +Variable bit length prefix codes +-------------------------------- + +The ``.encode()`` method takes a dictionary mapping symbols to bitarrays +and an iterable, and extends the bitarray object with the encoded symbols +found while iterating. For example: + +.. code-block:: python + + >>> d = {'H':bitarray('111'), 'e':bitarray('0'), + ... 'l':bitarray('110'), 'o':bitarray('10')} + ... + >>> a = bitarray() + >>> a.encode(d, 'Hello') + >>> a + bitarray('111011011010') + +Note that the string ``'Hello'`` is an iterable, but the symbols are not +limited to characters, in fact any immutable Python object can be a symbol. +Taking the same dictionary, we can apply the ``.decode()`` method which will +return a list of the symbols: + +.. code-block:: python + + >>> a.decode(d) + ['H', 'e', 'l', 'l', 'o'] + >>> ''.join(a.decode(d)) + 'Hello' + +Since symbols are not limited to being characters, it is necessary to return +them as elements of a list, rather than simply returning the joined string. +The above dictionary ``d`` can be efficiently constructed using the function +``bitarray.util.huffman_code()``. I also wrote `Huffman coding in Python +using bitarray `__ for more +background information. + +When the codes are large, and you have many decode calls, most time will +be spent creating the (same) internal decode tree objects. In this case, +it will be much faster to create a ``decodetree`` object, which can be +passed to bitarray's ``.decode()`` and ``.iterdecode()`` methods, instead +of passing the prefix code dictionary to those methods itself: + +.. code-block:: python + + >>> from bitarray import bitarray, decodetree + >>> t = decodetree({'a': bitarray('0'), 'b': bitarray('1')}) + >>> a = bitarray('0110') + >>> a.decode(t) + ['a', 'b', 'b', 'a'] + >>> ''.join(a.iterdecode(t)) + 'abba' + +The sole purpose of the immutable ``decodetree`` object is to be passed +to bitarray's ``.decode()`` and ``.iterdecode()`` methods. + + +Frozenbitarrays +--------------- + +A ``frozenbitarray`` object is very similar to the bitarray object. +The difference is that this a ``frozenbitarray`` is immutable, and hashable, +and can therefore be used as a dictionary key: + +.. code-block:: python + + >>> from bitarray import frozenbitarray + >>> key = frozenbitarray('1100011') + >>> {key: 'some value'} + {frozenbitarray('1100011'): 'some value'} + >>> key[3] = 1 + Traceback (most recent call last): + ... + TypeError: frozenbitarray is immutable + + +Reference +========= + +bitarray version: 2.9.2 -- `change log `__ + +In the following, ``item`` and ``value`` are usually a single bit - +an integer 0 or 1. + +Also, ``sub_bitarray`` refers to either a bitarray, or an ``item``. + + +The bitarray object: +-------------------- + +``bitarray(initializer=0, /, endian='big', buffer=None)`` -> bitarray + Return a new bitarray object whose items are bits initialized from + the optional initial object, and endianness. + The initializer may be of the following types: + + ``int``: Create a bitarray of given integer length. The initial values are + all ``0``. + + ``str``: Create bitarray from a string of ``0`` and ``1``. + + ``iterable``: Create bitarray from iterable or sequence of integers 0 or 1. + + Optional keyword arguments: + + ``endian``: Specifies the bit-endianness of the created bitarray object. + Allowed values are ``big`` and ``little`` (the default is ``big``). + The bit-endianness effects the buffer representation of the bitarray. + + ``buffer``: Any object which exposes a buffer. When provided, ``initializer`` + cannot be present (or has to be ``None``). The imported buffer may be + read-only or writable, depending on the object type. + + New in version 2.3: optional ``buffer`` argument. + + +bitarray methods: +----------------- + +``all()`` -> bool + Return True when all bits in bitarray are True. + Note that ``a.all()`` is faster than ``all(a)``. + + +``any()`` -> bool + Return True when any bit in bitarray is True. + Note that ``a.any()`` is faster than ``any(a)``. + + +``append(item, /)`` + Append ``item`` to the end of the bitarray. + + +``buffer_info()`` -> tuple + Return a tuple containing: + + 0. memory address of buffer + 1. buffer size (in bytes) + 2. bit-endianness as a string + 3. number of pad bits + 4. allocated memory for the buffer (in bytes) + 5. memory is read-only + 6. buffer is imported + 7. number of buffer exports + + +``bytereverse(start=0, stop=, /)`` + For each byte in byte-range(start, stop) reverse bits in-place. + The start and stop indices are given in terms of bytes (not bits). + Also note that this method only changes the buffer; it does not change the + endianness of the bitarray object. Padbits are left unchanged such that + two consecutive calls will always leave the bitarray unchanged. + + New in version 2.2.5: optional start and stop arguments. + + +``clear()`` + Remove all items from the bitarray. + + New in version 1.4. + + +``copy()`` -> bitarray + Return a copy of the bitarray. + + +``count(value=1, start=0, stop=, step=1, /)`` -> int + Number of occurrences of ``value`` bitarray within ``[start:stop:step]``. + Optional arguments ``start``, ``stop`` and ``step`` are interpreted in + slice notation, meaning ``a.count(value, start, stop, step)`` equals + ``a[start:stop:step].count(value)``. + The ``value`` may also be a sub-bitarray. In this case non-overlapping + occurrences are counted within ``[start:stop]`` (``step`` must be 1). + + New in version 1.1.0: optional start and stop arguments. + + New in version 2.3.7: optional step argument. + + New in version 2.9: add non-overlapping sub-bitarray count. + + +``decode(code, /)`` -> list + Given a prefix code (a dict mapping symbols to bitarrays, or ``decodetree`` + object), decode content of bitarray and return it as a list of symbols. + + +``encode(code, iterable, /)`` + Given a prefix code (a dict mapping symbols to bitarrays), + iterate over the iterable object with symbols, and extend bitarray + with corresponding bitarray for each symbol. + + +``endian()`` -> str + Return the bit-endianness of the bitarray as a string (``little`` or ``big``). + + +``extend(iterable, /)`` + Append all items from ``iterable`` to the end of the bitarray. + If the iterable is a string, each ``0`` and ``1`` are appended as + bits (ignoring whitespace and underscore). + + +``fill()`` -> int + Add zeros to the end of the bitarray, such that the length will be + a multiple of 8, and return the number of bits added [0..7]. + + +``find(sub_bitarray, start=0, stop=, /, right=False)`` -> int + Return lowest (or rightmost when ``right=True``) index where sub_bitarray + is found, such that sub_bitarray is contained within ``[start:stop]``. + Return -1 when sub_bitarray is not found. + + New in version 2.1. + + New in version 2.9: add optional keyword argument ``right``. + + +``frombytes(bytes, /)`` + Extend bitarray with raw bytes from a bytes-like object. + Each added byte will add eight bits to the bitarray. + + New in version 2.5.0: allow bytes-like argument. + + +``fromfile(f, n=-1, /)`` + Extend bitarray with up to ``n`` bytes read from file object ``f`` (or any + other binary stream what supports a ``.read()`` method, e.g. ``io.BytesIO``). + Each read byte will add eight bits to the bitarray. When ``n`` is omitted or + negative, all bytes until EOF are read. When ``n`` is non-negative but + exceeds the data available, ``EOFError`` is raised (but the available data + is still read and appended). + + +``index(sub_bitarray, start=0, stop=, /, right=False)`` -> int + Return lowest (or rightmost when ``right=True``) index where sub_bitarray + is found, such that sub_bitarray is contained within ``[start:stop]``. + Raises ``ValueError`` when the sub_bitarray is not present. + + New in version 2.9: add optional keyword argument ``right``. + + +``insert(index, value, /)`` + Insert ``value`` into bitarray before ``index``. + + +``invert(index=, /)`` + Invert all bits in bitarray (in-place). + When the optional ``index`` is given, only invert the single bit at index. + + New in version 1.5.3: optional index argument. + + +``iterdecode(code, /)`` -> iterator + Given a prefix code (a dict mapping symbols to bitarrays, or ``decodetree`` + object), decode content of bitarray and return an iterator over + the symbols. + + +``itersearch(sub_bitarray, start=0, stop=, /, right=False)`` -> iterator + Return iterator over indices where sub_bitarray is found, such that + sub_bitarray is contained within ``[start:stop]``. + The indices are iterated in ascending order (from lowest to highest), + unless ``right=True``, which will iterate in descending oder (starting with + rightmost match). + + New in version 2.9: optional start and stop arguments - add optional keyword argument ``right``. + + +``pack(bytes, /)`` + Extend bitarray from a bytes-like object, where each byte corresponds + to a single bit. The byte ``b'\x00'`` maps to bit 0 and all other bytes + map to bit 1. + + This method, as well as the ``.unpack()`` method, are meant for efficient + transfer of data between bitarray objects to other Python objects (for + example NumPy's ndarray object) which have a different memory view. + + New in version 2.5.0: allow bytes-like argument. + + +``pop(index=-1, /)`` -> item + Remove and return item at ``index`` (default last). + Raises ``IndexError`` if index is out of range. + + +``remove(value, /)`` + Remove the first occurrence of ``value``. + Raises ``ValueError`` if value is not present. + + +``reverse()`` + Reverse all bits in bitarray (in-place). + + +``search(sub_bitarray, limit=, /)`` -> list + Searches for given sub_bitarray in self, and return list of start + positions. + The optional argument limits the number of search results to the integer + specified. By default, all search results are returned. + + +``setall(value, /)`` + Set all elements in bitarray to ``value``. + Note that ``a.setall(value)`` is equivalent to ``a[:] = value``. + + +``sort(reverse=False)`` + Sort all bits in bitarray (in-place). + + +``to01()`` -> str + Return a string containing '0's and '1's, representing the bits in the + bitarray. + + +``tobytes()`` -> bytes + Return the bitarray buffer in bytes (pad bits are set to zero). + + +``tofile(f, /)`` + Write byte representation of bitarray to file object f. + + +``tolist()`` -> list + Return bitarray as list of integer items. + ``a.tolist()`` is equal to ``list(a)``. + + Note that the list object being created will require 32 or 64 times more + memory (depending on the machine architecture) than the bitarray object, + which may cause a memory error if the bitarray is very large. + + +``unpack(zero=b'\x00', one=b'\x01')`` -> bytes + Return bytes containing one character for each bit in the bitarray, + using specified mapping. + + +bitarray data descriptors: +-------------------------- + +Data descriptors were added in version 2.6. + +``nbytes`` -> int + buffer size in bytes + + +``padbits`` -> int + number of pad bits + + +``readonly`` -> bool + bool indicating whether buffer is read-only + + +Other objects: +-------------- + +``frozenbitarray(initializer=0, /, endian='big', buffer=None)`` -> frozenbitarray + Return a ``frozenbitarray`` object. Initialized the same way a ``bitarray`` + object is initialized. A ``frozenbitarray`` is immutable and hashable, + and may therefore be used as a dictionary key. + + New in version 1.1. + + +``decodetree(code, /)`` -> decodetree + Given a prefix code (a dict mapping symbols to bitarrays), + create a binary tree object to be passed to ``.decode()`` or ``.iterdecode()``. + + New in version 1.6. + + +Functions defined in the `bitarray` module: +------------------------------------------- + +``bits2bytes(n, /)`` -> int + Return the number of bytes necessary to store n bits. + + +``get_default_endian()`` -> str + Return the default endianness for new bitarray objects being created. + Unless ``_set_default_endian('little')`` was called, the default endianness + is ``big``. + + New in version 1.3. + + +``test(verbosity=1)`` -> TextTestResult + Run self-test, and return unittest.runner.TextTestResult object. + + +Functions defined in `bitarray.util` module: +-------------------------------------------- + +This sub-module was added in version 1.2. + +``zeros(length, /, endian=None)`` -> bitarray + Create a bitarray of length, with all values 0, and optional + endianness, which may be 'big', 'little'. + + +``ones(length, /, endian=None)`` -> bitarray + Create a bitarray of length, with all values 1, and optional + endianness, which may be 'big', 'little'. + + New in version 2.9. + + +``urandom(length, /, endian=None)`` -> bitarray + Return a bitarray of ``length`` random bits (uses ``os.urandom``). + + New in version 1.7. + + +``pprint(bitarray, /, stream=None, group=8, indent=4, width=80)`` + Prints the formatted representation of object on ``stream`` (which defaults + to ``sys.stdout``). By default, elements are grouped in bytes (8 elements), + and 8 bytes (64 elements) per line. + Non-bitarray objects are printed by the standard library + function ``pprint.pprint()``. + + New in version 1.8. + + +``make_endian(bitarray, /, endian)`` -> bitarray + When the endianness of the given bitarray is different from ``endian``, + return a new bitarray, with endianness ``endian`` and the same elements + as the original bitarray. + Otherwise (endianness is already ``endian``) the original bitarray is returned + unchanged. + + New in version 1.3. + + New in version 2.9: deprecated - use ``bitarray()``. + + +``rindex(bitarray, sub_bitarray=1, start=0, stop=, /)`` -> int + Return rightmost (highest) index where sub_bitarray (or item - defaults + to 1) is found in bitarray (``a``), such that sub_bitarray is contained + within ``a[start:stop]``. + Raises ``ValueError`` when the sub_bitarray is not present. + + New in version 2.3.0: optional start and stop arguments. + + New in version 2.9: deprecated - use ``.index(..., right=1)``. + + +``strip(bitarray, /, mode='right')`` -> bitarray + Return a new bitarray with zeros stripped from left, right or both ends. + Allowed values for mode are the strings: ``left``, ``right``, ``both`` + + +``count_n(a, n, value=1, /)`` -> int + Return lowest index ``i`` for which ``a[:i].count(value) == n``. + Raises ``ValueError`` when ``n`` exceeds total count (``a.count(value)``). + + New in version 2.3.6: optional value argument. + + +``parity(a, /)`` -> int + Return parity of bitarray ``a``. + ``parity(a)`` is equivalent to ``a.count() % 2`` but more efficient. + + New in version 1.9. + + +``count_and(a, b, /)`` -> int + Return ``(a & b).count()`` in a memory efficient manner, + as no intermediate bitarray object gets created. + + +``count_or(a, b, /)`` -> int + Return ``(a | b).count()`` in a memory efficient manner, + as no intermediate bitarray object gets created. + + +``count_xor(a, b, /)`` -> int + Return ``(a ^ b).count()`` in a memory efficient manner, + as no intermediate bitarray object gets created. + + This is also known as the Hamming distance. + + +``any_and(a, b, /)`` -> bool + Efficient implementation of ``any(a & b)``. + + New in version 2.7. + + +``subset(a, b, /)`` -> bool + Return ``True`` if bitarray ``a`` is a subset of bitarray ``b``. + ``subset(a, b)`` is equivalent to ``a | b == b`` (and equally ``a & b == a``) but + more efficient as no intermediate bitarray object is created and the buffer + iteration is stopped as soon as one mismatch is found. + + +``intervals(bitarray, /)`` -> iterator + Compute all uninterrupted intervals of 1s and 0s, and return an + iterator over tuples ``(value, start, stop)``. The intervals are guaranteed + to be in order, and their size is always non-zero (``stop - start > 0``). + + New in version 2.7. + + +``ba2hex(bitarray, /)`` -> hexstr + Return a string containing the hexadecimal representation of + the bitarray (which has to be multiple of 4 in length). + + +``hex2ba(hexstr, /, endian=None)`` -> bitarray + Bitarray of hexadecimal representation. hexstr may contain any number + (including odd numbers) of hex digits (upper or lower case). + + +``ba2base(n, bitarray, /)`` -> str + Return a string containing the base ``n`` ASCII representation of + the bitarray. Allowed values for ``n`` are 2, 4, 8, 16, 32 and 64. + The bitarray has to be multiple of length 1, 2, 3, 4, 5 or 6 respectively. + For ``n=32`` the RFC 4648 Base32 alphabet is used, and for ``n=64`` the + standard base 64 alphabet is used. + + See also: `Bitarray representations `__ + + New in version 1.9. + + +``base2ba(n, asciistr, /, endian=None)`` -> bitarray + Bitarray of base ``n`` ASCII representation. + Allowed values for ``n`` are 2, 4, 8, 16, 32 and 64. + For ``n=32`` the RFC 4648 Base32 alphabet is used, and for ``n=64`` the + standard base 64 alphabet is used. + + See also: `Bitarray representations `__ + + New in version 1.9. + + +``ba2int(bitarray, /, signed=False)`` -> int + Convert the given bitarray to an integer. + The bit-endianness of the bitarray is respected. + ``signed`` indicates whether two's complement is used to represent the integer. + + +``int2ba(int, /, length=None, endian=None, signed=False)`` -> bitarray + Convert the given integer to a bitarray (with given endianness, + and no leading (big-endian) / trailing (little-endian) zeros), unless + the ``length`` of the bitarray is provided. An ``OverflowError`` is raised + if the integer is not representable with the given number of bits. + ``signed`` determines whether two's complement is used to represent the integer, + and requires ``length`` to be provided. + + +``serialize(bitarray, /)`` -> bytes + Return a serialized representation of the bitarray, which may be passed to + ``deserialize()``. It efficiently represents the bitarray object (including + its bit-endianness) and is guaranteed not to change in future releases. + + See also: `Bitarray representations `__ + + New in version 1.8. + + +``deserialize(bytes, /)`` -> bitarray + Return a bitarray given a bytes-like representation such as returned + by ``serialize()``. + + See also: `Bitarray representations `__ + + New in version 1.8. + + New in version 2.5.0: allow bytes-like argument. + + +``sc_encode(bitarray, /)`` -> bytes + Compress a sparse bitarray and return its binary representation. + This representation is useful for efficiently storing sparse bitarrays. + Use ``sc_decode()`` for decompressing (decoding). + + See also: `Compression of sparse bitarrays `__ + + New in version 2.7. + + +``sc_decode(stream)`` -> bitarray + Decompress binary stream (an integer iterator, or bytes-like object) of a + sparse compressed (``sc``) bitarray, and return the decoded bitarray. + This function consumes only one bitarray and leaves the remaining stream + untouched. Use ``sc_encode()`` for compressing (encoding). + + See also: `Compression of sparse bitarrays `__ + + New in version 2.7. + + +``vl_encode(bitarray, /)`` -> bytes + Return variable length binary representation of bitarray. + This representation is useful for efficiently storing small bitarray + in a binary stream. Use ``vl_decode()`` for decoding. + + See also: `Variable length bitarray format `__ + + New in version 2.2. + + +``vl_decode(stream, /, endian=None)`` -> bitarray + Decode binary stream (an integer iterator, or bytes-like object), and + return the decoded bitarray. This function consumes only one bitarray and + leaves the remaining stream untouched. Use ``vl_encode()`` for encoding. + + See also: `Variable length bitarray format `__ + + New in version 2.2. + + +``huffman_code(dict, /, endian=None)`` -> dict + Given a frequency map, a dictionary mapping symbols to their frequency, + calculate the Huffman code, i.e. a dict mapping those symbols to + bitarrays (with given endianness). Note that the symbols are not limited + to being strings. Symbols may may be any hashable object (such as ``None``). + + +``canonical_huffman(dict, /)`` -> tuple + Given a frequency map, a dictionary mapping symbols to their frequency, + calculate the canonical Huffman code. Returns a tuple containing: + + 0. the canonical Huffman code as a dict mapping symbols to bitarrays + 1. a list containing the number of symbols of each code length + 2. a list of symbols in canonical order + + Note: the two lists may be used as input for ``canonical_decode()``. + + See also: `Canonical Huffman Coding `__ + + New in version 2.5. + + +``canonical_decode(bitarray, count, symbol, /)`` -> iterator + Decode bitarray using canonical Huffman decoding tables + where ``count`` is a sequence containing the number of symbols of each length + and ``symbol`` is a sequence of symbols in canonical order. + + See also: `Canonical Huffman Coding `__ + + New in version 2.5. + + diff --git a/code/.venv/lib/python3.12/site-packages/bitarray-2.9.2.dist-info/RECORD b/code/.venv/lib/python3.12/site-packages/bitarray-2.9.2.dist-info/RECORD new file mode 100644 index 0000000..c7a6a8f --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/bitarray-2.9.2.dist-info/RECORD @@ -0,0 +1,23 @@ +bitarray-2.9.2.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +bitarray-2.9.2.dist-info/LICENSE,sha256=a14E9uBGLhuXvoM_nWXmbNXs6zKD9Kb1MiYQh9CrEjM,2412 +bitarray-2.9.2.dist-info/METADATA,sha256=AcCMyOnbnpLfWGRf8pb8AQwTtuIXrJ1tPV04U3RorNM,34424 +bitarray-2.9.2.dist-info/RECORD,, +bitarray-2.9.2.dist-info/WHEEL,sha256=4ZiCdXIWMxJyEClivrQv1QAHZpQh8kVYU92_ZAVwaok,152 +bitarray-2.9.2.dist-info/top_level.txt,sha256=cLdoeIHotTwh4eHSe8qy8umF7zzyxUM90I8zFb2_xhg,9 +bitarray/__init__.py,sha256=LzhfIzq4GYmC7-nRwx4veFnPO4Cy5XA5b6qdjVhQYl0,2511 +bitarray/__init__.pyi,sha256=aELtBsMYq66ob-8icr3COdYa0CrFJmsd88sdPE2sbto,5256 +bitarray/__pycache__/__init__.cpython-312.pyc,, +bitarray/__pycache__/test_bitarray.cpython-312.pyc,, +bitarray/__pycache__/test_util.cpython-312.pyc,, +bitarray/__pycache__/util.cpython-312.pyc,, +bitarray/_bitarray.cpython-312-x86_64-linux-gnu.so,sha256=14HHa1TjQ1pmIePCLwjb3r_D7SkzcigQfQU6F0dK2_w,494720 +bitarray/_util.cpython-312-x86_64-linux-gnu.so,sha256=ADHJVpvewtOhuK5bgeOiJn34VDTsPSm7y_HVusnv7wY,196136 +bitarray/bitarray.h,sha256=f5CXTBVDfR9XF_YZMXqOB52zg9u_6dwaQYCEqsX_AIM,10447 +bitarray/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +bitarray/pythoncapi_compat.h,sha256=9ywdY-5gQSwyy61cQ-pEH2Wn4GyowXe93m3bGSlDN1E,16187 +bitarray/test_150.pickle,sha256=v5oAS4jJwWDVDh8s0mPn4iCvwN3-Y5sZWpVGkpwyCqw,356 +bitarray/test_281.pickle,sha256=GaA8vsZmcyZZmIzGDP6ppfaRlsthUxgJ9tMsyjpynoc,442 +bitarray/test_bitarray.py,sha256=-o4hN4T1Dan34YIOTBqq0BXpiLIAGV8QtTSfjRW5ZhE,182731 +bitarray/test_util.py,sha256=4zURWe3wT5ZOnaRZ_u8K4AfTDxCQmYspBOclZiMF2rc,88028 +bitarray/util.py,sha256=8KY_zK5hWhAKAfe94LmYsojkLt8ienaDfZgcd1EBVIg,14015 +bitarray/util.pyi,sha256=6Ol-9EQ3Ad6RzN8b-nmYp9ZGtRjz1knMSK-gL_8urmo,2532 diff --git a/code/.venv/lib/python3.12/site-packages/bitarray-2.9.2.dist-info/WHEEL b/code/.venv/lib/python3.12/site-packages/bitarray-2.9.2.dist-info/WHEEL new file mode 100644 index 0000000..d1b3f1d --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/bitarray-2.9.2.dist-info/WHEEL @@ -0,0 +1,6 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.41.2) +Root-Is-Purelib: false +Tag: cp312-cp312-manylinux_2_17_x86_64 +Tag: cp312-cp312-manylinux2014_x86_64 + diff --git a/code/.venv/lib/python3.12/site-packages/bitarray-2.9.2.dist-info/top_level.txt b/code/.venv/lib/python3.12/site-packages/bitarray-2.9.2.dist-info/top_level.txt new file mode 100644 index 0000000..aab91b8 --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/bitarray-2.9.2.dist-info/top_level.txt @@ -0,0 +1 @@ +bitarray diff --git a/code/.venv/lib/python3.12/site-packages/bitarray/__init__.py b/code/.venv/lib/python3.12/site-packages/bitarray/__init__.py new file mode 100644 index 0000000..86982eb --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/bitarray/__init__.py @@ -0,0 +1,76 @@ +# Copyright (c) 2008 - 2024, Ilan Schnell; All Rights Reserved +""" +This package defines an object type which can efficiently represent +a bitarray. Bitarrays are sequence types and behave very much like lists. + +Please find a description of this package at: + + https://github.com/ilanschnell/bitarray + +Author: Ilan Schnell +""" +from __future__ import absolute_import + +from bitarray._bitarray import (bitarray, decodetree, _sysinfo, + _bitarray_reconstructor, + get_default_endian, _set_default_endian, + __version__) + + +__all__ = ['bitarray', 'frozenbitarray', 'decodetree', 'bits2bytes'] + + +class frozenbitarray(bitarray): + """frozenbitarray(initializer=0, /, endian='big', buffer=None) -> \ +frozenbitarray + +Return a `frozenbitarray` object. Initialized the same way a `bitarray` +object is initialized. A `frozenbitarray` is immutable and hashable, +and may therefore be used as a dictionary key. +""" + def __init__(self, *args, **kwargs): + self._freeze() + + def __repr__(self): + return 'frozen' + bitarray.__repr__(self) + + def __hash__(self): + "Return hash(self)." + # ensure hash is independent of endianness + a = bitarray(self, 'big') + return hash((len(a), a.tobytes())) + + # Technically the code below is not necessary, as all these methods will + # raise a TypeError on read-only memory. However, with a different error + # message. + def __delitem__(self, *args, **kwargs): + "" # no docstring + raise TypeError("frozenbitarray is immutable") + + append = bytereverse = clear = extend = encode = fill = __delitem__ + frombytes = fromfile = insert = invert = pack = pop = __delitem__ + remove = reverse = setall = sort = __setitem__ = __delitem__ + __iadd__ = __iand__ = __imul__ = __ior__ = __ixor__ = __delitem__ + __ilshift__ = __irshift__ = __delitem__ + + +def bits2bytes(__n): + """bits2bytes(n, /) -> int + +Return the number of bytes necessary to store n bits. +""" + import sys + if not isinstance(__n, (int, long) if sys.version_info[0] == 2 else int): + raise TypeError("integer expected") + if __n < 0: + raise ValueError("non-negative integer expected") + return (__n + 7) // 8 + + +def test(verbosity=1): + """test(verbosity=1) -> TextTestResult + +Run self-test, and return unittest.runner.TextTestResult object. +""" + from bitarray import test_bitarray + return test_bitarray.run(verbosity=verbosity) diff --git a/code/.venv/lib/python3.12/site-packages/bitarray/__init__.pyi b/code/.venv/lib/python3.12/site-packages/bitarray/__init__.pyi new file mode 100644 index 0000000..68b6dd6 --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/bitarray/__init__.pyi @@ -0,0 +1,149 @@ +# Copyright (c) 2021 - 2024, Ilan Schnell; All Rights Reserved +# +# This stub, as well as util.pyi, are tested with Python 3.9 and mypy 0.950 + +from collections.abc import Iterable, Iterator, Sequence +from unittest.runner import TextTestResult + +from typing import Any, BinaryIO, Dict, Union, overload + + +CodeDict = Dict[Any, bitarray] +BytesLike = Union[bytes, Iterable[int]] + + +class decodetree: + def __init__(self, code: CodeDict) -> None: ... + def complete(self) -> bool: ... + def nodes(self) -> int: ... + def todict(self) -> CodeDict: ... + + +class bitarray: + def __init__(self, + initializer: Union[int, str, Iterable[int], None] = ..., + endian: Union[str, None] = ..., + buffer: Any = ...) -> None: ... + + def all(self) -> bool: ... + def any(self) -> bool: ... + def append(self, value: int) -> None: ... + def buffer_info(self) -> tuple: ... + def bytereverse(self, + start: int = ..., + stop: int = ...) -> None: ... + + def clear(self) -> None: ... + def copy(self) -> bitarray: ... + def count(self, + sub_bitarray: Union[bitarray, int] = ..., + start: int = ..., + stop: int = ..., + step: int = ...) -> int: ... + + def decode(self, code: Union[CodeDict, decodetree]) -> list: ... + def encode(self, code: CodeDict, x: Iterable) -> None: ... + def endian(self) -> str: ... + def extend(self, x: Union[str, Iterable[int]]) -> None: ... + def fill(self) -> int: ... + def find(self, + sub_bitarray: Union[bitarray, int], + start: int = ..., + stop: int = ..., + right: int = ...) -> int: ... + + def frombytes(self, a: BytesLike) -> None: ... + def fromfile(self, f: BinaryIO, n: int = ...) -> None: ... + def index(self, + sub_bitarray: Union[bitarray, int], + start: int = ..., + stop: int = ..., + right: int = ...) -> int: ... + + def insert(self, i: int, value: int) -> None: ... + def invert(self, i: int = ...) -> None: ... + def iterdecode(self, + code: Union[CodeDict, decodetree]) -> Iterator: ... + + def itersearch(self, + sub_bitarray: Union[bitarray, int], + start: int = ..., + stop: int = ..., + right: int = ...) -> Iterator[int]: ... + + def pack(self, b: BytesLike) -> None: ... + def pop(self, i: int = ...) -> int: ... + def remove(self, value: int) -> None: ... + def reverse(self) -> None: ... + def search(self, sub_bitarray: Union[bitarray, int], + limit: int = ...) -> list[int]: ... + + def setall(self, value: int) -> None: ... + def sort(self, reverse: int) -> None: ... + def to01(self) -> str: ... + def tobytes(self) -> bytes: ... + def tofile(self, f: BinaryIO) -> None: ... + def tolist(self) -> list[int]: ... + def unpack(self, + zero: bytes = ..., + one: bytes = ...) -> bytes: ... + + def __len__(self) -> int: ... + def __iter__(self) -> Iterator[int]: ... + @overload + def __getitem__(self, i: int) -> int: ... + @overload + def __getitem__(self, s: Union[slice, Sequence]) -> bitarray: ... + @overload + def __setitem__(self, i: Union[int, slice, Sequence], o: int) -> None: ... + @overload + def __setitem__(self, s: Union[slice, Sequence] , o: bitarray) -> None: ... + def __delitem__(self, i: Union[int, slice, Sequence]) -> None: ... + + def __add__(self, other: bitarray) -> bitarray: ... + def __iadd__(self, other: bitarray) -> bitarray: ... + def __mul__(self, n: int) -> bitarray: ... + def __imul__(self, n: int) -> bitarray: ... + def __rmul__(self, n: int) -> bitarray: ... + + def __ge__(self, other: bitarray) -> bool: ... + def __gt__(self, other: bitarray) -> bool: ... + def __le__(self, other: bitarray) -> bool: ... + def __lt__(self, other: bitarray) -> bool: ... + + def __and__(self, other: bitarray) -> bitarray: ... + def __or__(self, other: bitarray) -> bitarray: ... + def __xor__(self, other: bitarray) -> bitarray: ... + def __iand__(self, other: bitarray) -> bitarray: ... + def __ior__(self, other: bitarray) -> bitarray: ... + def __ixor__(self, other: bitarray) -> bitarray: ... + def __invert__(self) -> bitarray: ... + def __lshift__(self, n: int) -> bitarray: ... + def __rshift__(self, n: int) -> bitarray: ... + def __ilshift__(self, n: int) -> bitarray: ... + def __irshift__(self, n: int) -> bitarray: ... + + # data descriptors + @property + def nbytes(self) -> int: ... + @property + def padbits(self) -> int: ... + @property + def readonly(self) -> bool: ... + + +class frozenbitarray(bitarray): + def __hash__(self) -> int: ... + + +__version__: str +def bits2bytes(n: int) -> int: ... +def get_default_endian() -> str: ... +def test(verbosity: int = ...) -> TextTestResult: ... +def _set_default_endian(endian: str) -> None: ... +def _sysinfo() -> tuple: ... +def _bitarray_reconstructor(cls: type, + buffer: bytes, + endian: str, + padbits: int, + readonly: int) -> bitarray: ... diff --git a/code/.venv/lib/python3.12/site-packages/bitarray/__pycache__/__init__.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/bitarray/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..69c9f9a Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/bitarray/__pycache__/__init__.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/bitarray/__pycache__/test_bitarray.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/bitarray/__pycache__/test_bitarray.cpython-312.pyc new file mode 100644 index 0000000..8e93a4c Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/bitarray/__pycache__/test_bitarray.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/bitarray/__pycache__/test_util.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/bitarray/__pycache__/test_util.cpython-312.pyc new file mode 100644 index 0000000..c572fb8 Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/bitarray/__pycache__/test_util.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/bitarray/__pycache__/util.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/bitarray/__pycache__/util.cpython-312.pyc new file mode 100644 index 0000000..1851b2b Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/bitarray/__pycache__/util.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/bitarray/_bitarray.cpython-312-x86_64-linux-gnu.so b/code/.venv/lib/python3.12/site-packages/bitarray/_bitarray.cpython-312-x86_64-linux-gnu.so new file mode 100755 index 0000000..feeb925 Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/bitarray/_bitarray.cpython-312-x86_64-linux-gnu.so differ diff --git a/code/.venv/lib/python3.12/site-packages/bitarray/_util.cpython-312-x86_64-linux-gnu.so b/code/.venv/lib/python3.12/site-packages/bitarray/_util.cpython-312-x86_64-linux-gnu.so new file mode 100755 index 0000000..e07edac Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/bitarray/_util.cpython-312-x86_64-linux-gnu.so differ diff --git a/code/.venv/lib/python3.12/site-packages/bitarray/bitarray.h b/code/.venv/lib/python3.12/site-packages/bitarray/bitarray.h new file mode 100644 index 0000000..c6f9e0b --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/bitarray/bitarray.h @@ -0,0 +1,336 @@ +/* + Copyright (c) 2008 - 2024, Ilan Schnell; All Rights Reserved + bitarray is published under the PSF license. + + Author: Ilan Schnell +*/ +#define BITARRAY_VERSION "2.9.2" + +#ifdef STDC_HEADERS +# include +#else +# ifdef HAVE_SYS_TYPES_H +# include /* For size_t */ +# endif +#endif + +/* Compatibility with Visual Studio 2013 and older which don't support + the inline keyword in C (only in C++): use __inline instead. + (copied from pythoncapi_compat.h) */ +#if (defined(_MSC_VER) && _MSC_VER < 1900 \ + && !defined(__cplusplus) && !defined(inline)) +#define inline __inline +#endif + +#ifdef _MSC_VER +#include /* For _byteswap_uint64() */ +#endif + +/* --- definitions specific to Python --- */ + +/* Py_UNREACHABLE was introduced in Python 3.7 */ +#ifndef Py_UNREACHABLE +#define Py_UNREACHABLE() abort() +#endif + +#if PY_MAJOR_VERSION >= 3 +#define IS_PY3K 1 +#define BYTES_SIZE_FMT "y#" +#else +#define IS_PY3K 0 +/* the Py_MIN and Py_MAX macros were introduced in Python 3.3 */ +#define Py_MIN(x, y) (((x) > (y)) ? (y) : (x)) +#define Py_MAX(x, y) (((x) > (y)) ? (x) : (y)) +#define PySlice_GetIndicesEx(slice, len, start, stop, step, slicelength) \ + PySlice_GetIndicesEx(((PySliceObject *) slice), \ + (len), (start), (stop), (step), (slicelength)) +#define PyLong_FromLong PyInt_FromLong +#define BYTES_SIZE_FMT "s#" +#endif + +/* --- bitarrayobject --- */ + +/* .ob_size is buffer size (in bytes), not the number of elements. + The number of elements (bits) is .nbits. */ +typedef struct { + PyObject_VAR_HEAD + char *ob_item; /* buffer */ + Py_ssize_t allocated; /* allocated buffer size (in bytes) */ + Py_ssize_t nbits; /* length of bitarray, i.e. elements */ + int endian; /* bit-endianness of bitarray */ + int ob_exports; /* how many buffer exports */ + PyObject *weakreflist; /* list of weak references */ + Py_buffer *buffer; /* used when importing a buffer */ + int readonly; /* buffer is readonly */ +} bitarrayobject; + +/* --- bit-endianness --- */ +#define ENDIAN_LITTLE 0 +#define ENDIAN_BIG 1 + +#define IS_LE(self) ((self)->endian == ENDIAN_LITTLE) +#define IS_BE(self) ((self)->endian == ENDIAN_BIG) + +/* endianness as string */ +#define ENDIAN_STR(endian) ((endian) == ENDIAN_LITTLE ? "little" : "big") + +/* number of pad bits */ +#define PADBITS(self) (8 * Py_SIZE(self) - (self)->nbits) + +/* number of bytes necessary to store given bits */ +#define BYTES(bits) (((bits) + 7) >> 3) + +/* we're not using bitmask_table here, as it is actually slower */ +#define BITMASK(self, i) (((char) 1) << ((self)->endian == ENDIAN_LITTLE ? \ + ((i) % 8) : (7 - (i) % 8))) + +/* buffer as uint64 array */ +#define WBUFF(self) ((uint64_t *) (self)->ob_item) + +/* assert that .nbits is in agreement with .ob_size */ +#define assert_nbits(self) assert(BYTES((self)->nbits) == Py_SIZE(self)) + +/* assert byte index is in range */ +#define assert_byte_in_range(self, j) \ + assert(self->ob_item && 0 <= (j) && (j) < Py_SIZE(self)) + +/* ------------ low level access to bits in bitarrayobject ------------- */ + +static inline int +getbit(bitarrayobject *self, Py_ssize_t i) +{ + assert_nbits(self); + assert(0 <= i && i < self->nbits); + return self->ob_item[i >> 3] & BITMASK(self, i) ? 1 : 0; +} + +static inline void +setbit(bitarrayobject *self, Py_ssize_t i, int vi) +{ + char *cp, mask; + + assert_nbits(self); + assert(0 <= i && i < self->nbits); + assert(self->readonly == 0); + + mask = BITMASK(self, i); + cp = self->ob_item + (i >> 3); + if (vi) + *cp |= mask; + else + *cp &= ~mask; +} + +static const char bitmask_table[2][8] = { + {0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80}, /* little endian */ + {0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01}, /* big endian */ +}; + +/* character with n leading ones is: ones_table[endian][n] */ +static const char ones_table[2][8] = { + {0x00, 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f}, /* little endian */ + {0x00, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe}, /* big endian */ +}; + +/* Return last byte in buffer with pad bits zeroed out. + If the length of the bitarray is a multiple of 8 (which includes an empty + bitarray), 0 is returned. */ +static inline char +zlc(bitarrayobject *self) /* zlc = zeroed last char */ +{ + const int r = self->nbits % 8; /* index into mask table */ + + if (r == 0) + return 0; + return self->ob_item[Py_SIZE(self) - 1] & ones_table[IS_BE(self)][r]; +} + +/* Return a uint64_t word representing the last (up to 63) remaining bits + of the buffer. All missing bytes (to complete the word) and padbits are + treated as zeros. + If the length of the bitarray is a multiple of 64 (which includes an empty + bitarray), 0 is returned. */ +static inline uint64_t +zlw(bitarrayobject *self) /* zlw = zeroed last word */ +{ + const Py_ssize_t nbits = self->nbits; + const Py_ssize_t nw = 8 * (nbits / 64); /* bytes in complete words */ + const int nr = (nbits % 64) / 8; /* complete remaining bytes */ + uint64_t res = 0; + + assert(nw + nr == nbits / 8 && nw + nr <= Py_SIZE(self)); + memcpy((char *) &res, self->ob_item + nw, (size_t) nr); + if (nbits % 8) + *(((char *) &res) + nr) = zlc(self); + + assert(nbits % 64 || res == 0); + return res; +} + +/* unless buffer is readonly, zero out pad bits - self->nbits is unchanged */ +static inline void +set_padbits(bitarrayobject *self) +{ + const int r = self->nbits % 8; /* index into mask table */ + + if (self->readonly == 0 && r) + self->ob_item[Py_SIZE(self) - 1] &= ones_table[IS_BE(self)][r]; +} + +/* population count - number of 1's in uint64 */ +static inline int +popcnt_64(uint64_t x) +{ +#if (defined(__clang__) || defined(__GNUC__)) + return __builtin_popcountll(x); +#else + /* https://en.wikipedia.org/wiki/Hamming_weight popcount64c */ + const uint64_t m1 = 0x5555555555555555; + const uint64_t m2 = 0x3333333333333333; + const uint64_t m4 = 0x0f0f0f0f0f0f0f0f; + const uint64_t h01 = 0x0101010101010101; + + x -= (x >> 1) & m1; + x = (x & m2) + ((x >> 2) & m2); + x = (x + (x >> 4)) & m4; + return (x * h01) >> 56; +#endif +} + +static inline uint64_t +builtin_bswap64(uint64_t word) +{ +#if (defined(__clang__) || \ + (defined(__GNUC__) \ + && ((__GNUC__ >= 5) || (__GNUC__ == 4) && (__GNUC_MINOR__ >= 3)))) + /* __builtin_bswap64() is available since GCC 4.3. */ +# define HAVE_BUILTIN_BSWAP64 1 + return __builtin_bswap64(word); +#elif defined(_MSC_VER) +# define HAVE_BUILTIN_BSWAP64 1 + return _byteswap_uint64(word); +#else +# define HAVE_BUILTIN_BSWAP64 0 + Py_UNREACHABLE(); +#endif +} + +/* Return distance [0..3] to next aligned pointer. + While on modern compilers uint64_t pointers may be misaligned, it may + cause problems on older ones. Moreover, it may lead to slowdown (even + on modern compilers). */ +static inline int +to_aligned(void *p) +{ + int r = ((uintptr_t) p) % 4; + return r ? 4 - r : 0; +} + +/* population count of n words starting from at uint64_t pointer w */ +static inline Py_ssize_t +popcnt_words(uint64_t *w, Py_ssize_t n) +{ + Py_ssize_t cnt = 0; + + assert(n >= 0 && to_aligned((void *) w) == 0); + while (n--) + cnt += popcnt_64(*w++); + return cnt; +} + +/* adjust index a manner consistent with the handling of normal slices */ +static inline void +adjust_index(Py_ssize_t length, Py_ssize_t *i, Py_ssize_t step) +{ + if (*i < 0) { + *i += length; + if (*i < 0) + *i = (step < 0) ? -1 : 0; + } + else if (*i >= length) { + *i = (step < 0) ? length - 1 : length; + } +} + +/* same as PySlice_AdjustIndices() which was introduced in Python 3.6.1 */ +static inline Py_ssize_t +adjust_indices(Py_ssize_t length, Py_ssize_t *start, Py_ssize_t *stop, + Py_ssize_t step) +{ +#if PY_VERSION_HEX > 0x03060100 + return PySlice_AdjustIndices(length, start, stop, step); +#else + assert(step != 0); + adjust_index(length, start, step); + adjust_index(length, stop, step); + /* + a / b does integer division. If either a or b is negative, the result + depends on the compiler (rounding can go toward 0 or negative infinity). + Therefore, we are careful that both a and b are always positive. + */ + if (step < 0) { + if (*stop < *start) + return (*start - *stop - 1) / (-step) + 1; + } + else { + if (*start < *stop) + return (*stop - *start - 1) / step + 1; + } + return 0; +#endif +} + +/* adjust slice parameters such that step is always positive; produces + simpler loops over elements when their order is irrelevant */ +static inline void +adjust_step_positive(Py_ssize_t slicelength, + Py_ssize_t *start, Py_ssize_t *stop, Py_ssize_t *step) +{ + if (*step < 0) { + *stop = *start + 1; + *start = *stop + *step * (slicelength - 1) - 1; + *step = -(*step); + } + assert(*start >= 0 && *stop >= 0 && *step > 0 && slicelength >= 0); + /* slicelength == 0 implies stop <= start */ + assert(slicelength != 0 || *stop <= *start); + /* step == 1 and slicelength != 0 implies stop - start == slicelength */ + assert(*step != 1 || slicelength == 0 || *stop - *start == slicelength); +} + +/* convert Python object to C int and set value at address - + return 1 on success, 0 on failure (and set exception) */ +static inline int +conv_pybit(PyObject *value, int *vi) +{ + Py_ssize_t n; + + n = PyNumber_AsSsize_t(value, NULL); + if (n == -1 && PyErr_Occurred()) + return 0; + + if (n < 0 || n > 1) { + PyErr_Format(PyExc_ValueError, "bit must be 0 or 1, got %zd", n); + return 0; + } + *vi = (int) n; + return 1; +} + +/* Return 0 if bitarrays have equal length and bit-endianness. + Otherwise, set exception and return -1. */ +static inline int +ensure_eq_size_endian(bitarrayobject *a, bitarrayobject *b) +{ + if (a->nbits != b->nbits) { + PyErr_SetString(PyExc_ValueError, + "bitarrays of equal length expected"); + return -1; + } + if (a->endian != b->endian) { + PyErr_SetString(PyExc_ValueError, + "bitarrays of equal bit-endianness expected"); + return -1; + } + return 0; +} diff --git a/code/.venv/lib/python3.12/site-packages/bitarray/py.typed b/code/.venv/lib/python3.12/site-packages/bitarray/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/code/.venv/lib/python3.12/site-packages/bitarray/pythoncapi_compat.h b/code/.venv/lib/python3.12/site-packages/bitarray/pythoncapi_compat.h new file mode 100644 index 0000000..4600568 --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/bitarray/pythoncapi_compat.h @@ -0,0 +1,577 @@ +// Header file providing new C API functions to old Python versions. +// +// File distributed under the Zero Clause BSD (0BSD) license. +// Copyright Contributors to the pythoncapi_compat project. +// +// Homepage: +// https://github.com/python/pythoncapi_compat +// +// Latest version: +// https://raw.githubusercontent.com/python/pythoncapi_compat/master/pythoncapi_compat.h +// +// SPDX-License-Identifier: 0BSD + +#ifndef PYTHONCAPI_COMPAT +#define PYTHONCAPI_COMPAT + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include "frameobject.h" // PyFrameObject, PyFrame_GetBack() + + +// Compatibility with Visual Studio 2013 and older which don't support +// the inline keyword in C (only in C++): use __inline instead. +#if (defined(_MSC_VER) && _MSC_VER < 1900 \ + && !defined(__cplusplus) && !defined(inline)) +# define PYCAPI_COMPAT_STATIC_INLINE(TYPE) static __inline TYPE +#else +# define PYCAPI_COMPAT_STATIC_INLINE(TYPE) static inline TYPE +#endif + + +#ifndef _Py_CAST +# define _Py_CAST(type, expr) ((type)(expr)) +#endif + +// On C++11 and newer, _Py_NULL is defined as nullptr on C++11, +// otherwise it is defined as NULL. +#ifndef _Py_NULL +# if defined(__cplusplus) && __cplusplus >= 201103 +# define _Py_NULL nullptr +# else +# define _Py_NULL NULL +# endif +#endif + +// Cast argument to PyObject* type. +#ifndef _PyObject_CAST +# define _PyObject_CAST(op) _Py_CAST(PyObject*, op) +#endif + + +// bpo-42262 added Py_NewRef() to Python 3.10.0a3 +#if PY_VERSION_HEX < 0x030A00A3 && !defined(Py_NewRef) +PYCAPI_COMPAT_STATIC_INLINE(PyObject*) +_Py_NewRef(PyObject *obj) +{ + Py_INCREF(obj); + return obj; +} +#define Py_NewRef(obj) _Py_NewRef(_PyObject_CAST(obj)) +#endif + + +// bpo-42262 added Py_XNewRef() to Python 3.10.0a3 +#if PY_VERSION_HEX < 0x030A00A3 && !defined(Py_XNewRef) +PYCAPI_COMPAT_STATIC_INLINE(PyObject*) +_Py_XNewRef(PyObject *obj) +{ + Py_XINCREF(obj); + return obj; +} +#define Py_XNewRef(obj) _Py_XNewRef(_PyObject_CAST(obj)) +#endif + + +// bpo-39573 added Py_SET_REFCNT() to Python 3.9.0a4 +#if PY_VERSION_HEX < 0x030900A4 && !defined(Py_SET_REFCNT) +PYCAPI_COMPAT_STATIC_INLINE(void) +_Py_SET_REFCNT(PyObject *ob, Py_ssize_t refcnt) +{ + ob->ob_refcnt = refcnt; +} +#define Py_SET_REFCNT(ob, refcnt) _Py_SET_REFCNT(_PyObject_CAST(ob), refcnt) +#endif + + +// Py_SETREF() and Py_XSETREF() were added to Python 3.5.2. +// It is excluded from the limited C API. +#if (PY_VERSION_HEX < 0x03050200 && !defined(Py_SETREF)) && !defined(Py_LIMITED_API) +#define Py_SETREF(dst, src) \ + do { \ + PyObject **_tmp_dst_ptr = _Py_CAST(PyObject**, &(dst)); \ + PyObject *_tmp_dst = (*_tmp_dst_ptr); \ + *_tmp_dst_ptr = _PyObject_CAST(src); \ + Py_DECREF(_tmp_dst); \ + } while (0) + +#define Py_XSETREF(dst, src) \ + do { \ + PyObject **_tmp_dst_ptr = _Py_CAST(PyObject**, &(dst)); \ + PyObject *_tmp_dst = (*_tmp_dst_ptr); \ + *_tmp_dst_ptr = _PyObject_CAST(src); \ + Py_XDECREF(_tmp_dst); \ + } while (0) +#endif + + +// bpo-43753 added Py_Is(), Py_IsNone(), Py_IsTrue() and Py_IsFalse() +// to Python 3.10.0b1. +#if PY_VERSION_HEX < 0x030A00B1 && !defined(Py_Is) +# define Py_Is(x, y) ((x) == (y)) +#endif +#if PY_VERSION_HEX < 0x030A00B1 && !defined(Py_IsNone) +# define Py_IsNone(x) Py_Is(x, Py_None) +#endif +#if PY_VERSION_HEX < 0x030A00B1 && !defined(Py_IsTrue) +# define Py_IsTrue(x) Py_Is(x, Py_True) +#endif +#if PY_VERSION_HEX < 0x030A00B1 && !defined(Py_IsFalse) +# define Py_IsFalse(x) Py_Is(x, Py_False) +#endif + + +// bpo-39573 added Py_SET_TYPE() to Python 3.9.0a4 +#if PY_VERSION_HEX < 0x030900A4 && !defined(Py_SET_TYPE) +PYCAPI_COMPAT_STATIC_INLINE(void) +_Py_SET_TYPE(PyObject *ob, PyTypeObject *type) +{ + ob->ob_type = type; +} +#define Py_SET_TYPE(ob, type) _Py_SET_TYPE(_PyObject_CAST(ob), type) +#endif + + +// bpo-39573 added Py_SET_SIZE() to Python 3.9.0a4 +#if PY_VERSION_HEX < 0x030900A4 && !defined(Py_SET_SIZE) +PYCAPI_COMPAT_STATIC_INLINE(void) +_Py_SET_SIZE(PyVarObject *ob, Py_ssize_t size) +{ + ob->ob_size = size; +} +#define Py_SET_SIZE(ob, size) _Py_SET_SIZE((PyVarObject*)(ob), size) +#endif + + +// bpo-40421 added PyFrame_GetCode() to Python 3.9.0b1 +#if PY_VERSION_HEX < 0x030900B1 || defined(PYPY_VERSION) +PYCAPI_COMPAT_STATIC_INLINE(PyCodeObject*) +PyFrame_GetCode(PyFrameObject *frame) +{ + assert(frame != _Py_NULL); + assert(frame->f_code != _Py_NULL); + return _Py_CAST(PyCodeObject*, Py_NewRef(frame->f_code)); +} +#endif + +PYCAPI_COMPAT_STATIC_INLINE(PyCodeObject*) +_PyFrame_GetCodeBorrow(PyFrameObject *frame) +{ + PyCodeObject *code = PyFrame_GetCode(frame); + Py_DECREF(code); + return code; +} + + +// bpo-40421 added PyFrame_GetBack() to Python 3.9.0b1 +#if PY_VERSION_HEX < 0x030900B1 && !defined(PYPY_VERSION) +PYCAPI_COMPAT_STATIC_INLINE(PyFrameObject*) +PyFrame_GetBack(PyFrameObject *frame) +{ + assert(frame != _Py_NULL); + return _Py_CAST(PyFrameObject*, Py_XNewRef(frame->f_back)); +} +#endif + +#if !defined(PYPY_VERSION) +PYCAPI_COMPAT_STATIC_INLINE(PyFrameObject*) +_PyFrame_GetBackBorrow(PyFrameObject *frame) +{ + PyFrameObject *back = PyFrame_GetBack(frame); + Py_XDECREF(back); + return back; +} +#endif + + +// bpo-40421 added PyFrame_GetLocals() to Python 3.11.0a7 +#if PY_VERSION_HEX < 0x030B00A7 && !defined(PYPY_VERSION) +PYCAPI_COMPAT_STATIC_INLINE(PyObject*) +PyFrame_GetLocals(PyFrameObject *frame) +{ +#if PY_VERSION_HEX >= 0x030400B1 + if (PyFrame_FastToLocalsWithError(frame) < 0) { + return NULL; + } +#else + PyFrame_FastToLocals(frame); +#endif + return Py_NewRef(frame->f_locals); +} +#endif + + +// bpo-40421 added PyFrame_GetGlobals() to Python 3.11.0a7 +#if PY_VERSION_HEX < 0x030B00A7 && !defined(PYPY_VERSION) +PYCAPI_COMPAT_STATIC_INLINE(PyObject*) +PyFrame_GetGlobals(PyFrameObject *frame) +{ + return Py_NewRef(frame->f_globals); +} +#endif + + +// bpo-40421 added PyFrame_GetBuiltins() to Python 3.11.0a7 +#if PY_VERSION_HEX < 0x030B00A7 && !defined(PYPY_VERSION) +PYCAPI_COMPAT_STATIC_INLINE(PyObject*) +PyFrame_GetBuiltins(PyFrameObject *frame) +{ + return Py_NewRef(frame->f_builtins); +} +#endif + + +// bpo-40421 added PyFrame_GetLasti() to Python 3.11.0b1 +#if PY_VERSION_HEX < 0x030B00B1 && !defined(PYPY_VERSION) +PYCAPI_COMPAT_STATIC_INLINE(int) +PyFrame_GetLasti(PyFrameObject *frame) +{ +#if PY_VERSION_HEX >= 0x030A00A7 + // bpo-27129: Since Python 3.10.0a7, f_lasti is an instruction offset, + // not a bytes offset anymore. Python uses 16-bit "wordcode" (2 bytes) + // instructions. + if (frame->f_lasti < 0) { + return -1; + } + return frame->f_lasti * 2; +#else + return frame->f_lasti; +#endif +} +#endif + + +// gh-91248 added PyFrame_GetVar() to Python 3.12.0a2 +#if PY_VERSION_HEX < 0x030C00A2 && !defined(PYPY_VERSION) +PYCAPI_COMPAT_STATIC_INLINE(PyObject*) +PyFrame_GetVar(PyFrameObject *frame, PyObject *name) +{ + PyObject *locals, *value; + + locals = PyFrame_GetLocals(frame); + if (locals == NULL) { + return NULL; + } +#if PY_VERSION_HEX >= 0x03000000 + value = PyDict_GetItemWithError(locals, name); +#else + value = PyDict_GetItem(locals, name); +#endif + Py_DECREF(locals); + + if (value == NULL) { + if (PyErr_Occurred()) { + return NULL; + } +#if PY_VERSION_HEX >= 0x03000000 + PyErr_Format(PyExc_NameError, "variable %R does not exist", name); +#else + PyErr_SetString(PyExc_NameError, "variable does not exist"); +#endif + return NULL; + } + return Py_NewRef(value); +} +#endif + + +// gh-91248 added PyFrame_GetVarString() to Python 3.12.0a2 +#if PY_VERSION_HEX < 0x030C00A2 && !defined(PYPY_VERSION) +PYCAPI_COMPAT_STATIC_INLINE(PyObject*) +PyFrame_GetVarString(PyFrameObject *frame, const char *name) +{ + PyObject *name_obj, *value; + name_obj = PyUnicode_FromString(name); + if (name_obj == NULL) { + return NULL; + } + value = PyFrame_GetVar(frame, name_obj); + Py_DECREF(name_obj); + return value; +} +#endif + + +// bpo-39947 added PyThreadState_GetInterpreter() to Python 3.9.0a5 +#if PY_VERSION_HEX < 0x030900A5 || defined(PYPY_VERSION) +PYCAPI_COMPAT_STATIC_INLINE(PyInterpreterState *) +PyThreadState_GetInterpreter(PyThreadState *tstate) +{ + assert(tstate != _Py_NULL); + return tstate->interp; +} +#endif + + +// bpo-40429 added PyThreadState_GetFrame() to Python 3.9.0b1 +#if PY_VERSION_HEX < 0x030900B1 && !defined(PYPY_VERSION) +PYCAPI_COMPAT_STATIC_INLINE(PyFrameObject*) +PyThreadState_GetFrame(PyThreadState *tstate) +{ + assert(tstate != _Py_NULL); + return _Py_CAST(PyFrameObject *, Py_XNewRef(tstate->frame)); +} +#endif + +#if !defined(PYPY_VERSION) +PYCAPI_COMPAT_STATIC_INLINE(PyFrameObject*) +_PyThreadState_GetFrameBorrow(PyThreadState *tstate) +{ + PyFrameObject *frame = PyThreadState_GetFrame(tstate); + Py_XDECREF(frame); + return frame; +} +#endif + + +// bpo-39947 added PyInterpreterState_Get() to Python 3.9.0a5 +#if PY_VERSION_HEX < 0x030900A5 || defined(PYPY_VERSION) +PYCAPI_COMPAT_STATIC_INLINE(PyInterpreterState*) +PyInterpreterState_Get(void) +{ + PyThreadState *tstate; + PyInterpreterState *interp; + + tstate = PyThreadState_GET(); + if (tstate == _Py_NULL) { + Py_FatalError("GIL released (tstate is NULL)"); + } + interp = tstate->interp; + if (interp == _Py_NULL) { + Py_FatalError("no current interpreter"); + } + return interp; +} +#endif + + +// bpo-39947 added PyInterpreterState_Get() to Python 3.9.0a6 +#if 0x030700A1 <= PY_VERSION_HEX && PY_VERSION_HEX < 0x030900A6 && !defined(PYPY_VERSION) +PYCAPI_COMPAT_STATIC_INLINE(uint64_t) +PyThreadState_GetID(PyThreadState *tstate) +{ + assert(tstate != _Py_NULL); + return tstate->id; +} +#endif + +// bpo-43760 added PyThreadState_EnterTracing() to Python 3.11.0a2 +#if PY_VERSION_HEX < 0x030B00A2 && !defined(PYPY_VERSION) +PYCAPI_COMPAT_STATIC_INLINE(void) +PyThreadState_EnterTracing(PyThreadState *tstate) +{ + tstate->tracing++; +#if PY_VERSION_HEX >= 0x030A00A1 + tstate->cframe->use_tracing = 0; +#else + tstate->use_tracing = 0; +#endif +} +#endif + +// bpo-43760 added PyThreadState_LeaveTracing() to Python 3.11.0a2 +#if PY_VERSION_HEX < 0x030B00A2 && !defined(PYPY_VERSION) +PYCAPI_COMPAT_STATIC_INLINE(void) +PyThreadState_LeaveTracing(PyThreadState *tstate) +{ + int use_tracing = (tstate->c_tracefunc != _Py_NULL + || tstate->c_profilefunc != _Py_NULL); + tstate->tracing--; +#if PY_VERSION_HEX >= 0x030A00A1 + tstate->cframe->use_tracing = use_tracing; +#else + tstate->use_tracing = use_tracing; +#endif +} +#endif + + +// bpo-37194 added PyObject_CallNoArgs() to Python 3.9.0a1 +// PyObject_CallNoArgs() added to PyPy 3.9.16-v7.3.11 +#if !defined(PyObject_CallNoArgs) && PY_VERSION_HEX < 0x030900A1 +PYCAPI_COMPAT_STATIC_INLINE(PyObject*) +PyObject_CallNoArgs(PyObject *func) +{ + return PyObject_CallFunctionObjArgs(func, NULL); +} +#endif + + +// bpo-39245 made PyObject_CallOneArg() public (previously called +// _PyObject_CallOneArg) in Python 3.9.0a4 +// PyObject_CallOneArg() added to PyPy 3.9.16-v7.3.11 +#if !defined(PyObject_CallOneArg) && PY_VERSION_HEX < 0x030900A4 +PYCAPI_COMPAT_STATIC_INLINE(PyObject*) +PyObject_CallOneArg(PyObject *func, PyObject *arg) +{ + return PyObject_CallFunctionObjArgs(func, arg, NULL); +} +#endif + + +// bpo-1635741 added PyModule_AddObjectRef() to Python 3.10.0a3 +#if PY_VERSION_HEX < 0x030A00A3 +PYCAPI_COMPAT_STATIC_INLINE(int) +PyModule_AddObjectRef(PyObject *module, const char *name, PyObject *value) +{ + int res; + Py_XINCREF(value); + res = PyModule_AddObject(module, name, value); + if (res < 0) { + Py_XDECREF(value); + } + return res; +} +#endif + + +// bpo-40024 added PyModule_AddType() to Python 3.9.0a5 +#if PY_VERSION_HEX < 0x030900A5 +PYCAPI_COMPAT_STATIC_INLINE(int) +PyModule_AddType(PyObject *module, PyTypeObject *type) +{ + const char *name, *dot; + + if (PyType_Ready(type) < 0) { + return -1; + } + + // inline _PyType_Name() + name = type->tp_name; + assert(name != _Py_NULL); + dot = strrchr(name, '.'); + if (dot != _Py_NULL) { + name = dot + 1; + } + + return PyModule_AddObjectRef(module, name, _PyObject_CAST(type)); +} +#endif + + +// bpo-40241 added PyObject_GC_IsTracked() to Python 3.9.0a6. +// bpo-4688 added _PyObject_GC_IS_TRACKED() to Python 2.7.0a2. +#if PY_VERSION_HEX < 0x030900A6 && !defined(PYPY_VERSION) +PYCAPI_COMPAT_STATIC_INLINE(int) +PyObject_GC_IsTracked(PyObject* obj) +{ + return (PyObject_IS_GC(obj) && _PyObject_GC_IS_TRACKED(obj)); +} +#endif + +// bpo-40241 added PyObject_GC_IsFinalized() to Python 3.9.0a6. +// bpo-18112 added _PyGCHead_FINALIZED() to Python 3.4.0 final. +#if PY_VERSION_HEX < 0x030900A6 && PY_VERSION_HEX >= 0x030400F0 && !defined(PYPY_VERSION) +PYCAPI_COMPAT_STATIC_INLINE(int) +PyObject_GC_IsFinalized(PyObject *obj) +{ + PyGC_Head *gc = _Py_CAST(PyGC_Head*, obj) - 1; + return (PyObject_IS_GC(obj) && _PyGCHead_FINALIZED(gc)); +} +#endif + + +// bpo-39573 added Py_IS_TYPE() to Python 3.9.0a4 +#if PY_VERSION_HEX < 0x030900A4 && !defined(Py_IS_TYPE) +PYCAPI_COMPAT_STATIC_INLINE(int) +_Py_IS_TYPE(PyObject *ob, PyTypeObject *type) { + return Py_TYPE(ob) == type; +} +#define Py_IS_TYPE(ob, type) _Py_IS_TYPE(_PyObject_CAST(ob), type) +#endif + + +// bpo-46906 added PyFloat_Pack2() and PyFloat_Unpack2() to Python 3.11a7. +// bpo-11734 added _PyFloat_Pack2() and _PyFloat_Unpack2() to Python 3.6.0b1. +// Python 3.11a2 moved _PyFloat_Pack2() and _PyFloat_Unpack2() to the internal +// C API: Python 3.11a2-3.11a6 versions are not supported. +#if 0x030600B1 <= PY_VERSION_HEX && PY_VERSION_HEX <= 0x030B00A1 && !defined(PYPY_VERSION) +PYCAPI_COMPAT_STATIC_INLINE(int) +PyFloat_Pack2(double x, char *p, int le) +{ return _PyFloat_Pack2(x, (unsigned char*)p, le); } + +PYCAPI_COMPAT_STATIC_INLINE(double) +PyFloat_Unpack2(const char *p, int le) +{ return _PyFloat_Unpack2((const unsigned char *)p, le); } +#endif + + +// bpo-46906 added PyFloat_Pack4(), PyFloat_Pack8(), PyFloat_Unpack4() and +// PyFloat_Unpack8() to Python 3.11a7. +// Python 3.11a2 moved _PyFloat_Pack4(), _PyFloat_Pack8(), _PyFloat_Unpack4() +// and _PyFloat_Unpack8() to the internal C API: Python 3.11a2-3.11a6 versions +// are not supported. +#if PY_VERSION_HEX <= 0x030B00A1 && !defined(PYPY_VERSION) +PYCAPI_COMPAT_STATIC_INLINE(int) +PyFloat_Pack4(double x, char *p, int le) +{ return _PyFloat_Pack4(x, (unsigned char*)p, le); } + +PYCAPI_COMPAT_STATIC_INLINE(int) +PyFloat_Pack8(double x, char *p, int le) +{ return _PyFloat_Pack8(x, (unsigned char*)p, le); } + +PYCAPI_COMPAT_STATIC_INLINE(double) +PyFloat_Unpack4(const char *p, int le) +{ return _PyFloat_Unpack4((const unsigned char *)p, le); } + +PYCAPI_COMPAT_STATIC_INLINE(double) +PyFloat_Unpack8(const char *p, int le) +{ return _PyFloat_Unpack8((const unsigned char *)p, le); } +#endif + + +// gh-92154 added PyCode_GetCode() to Python 3.11.0b1 +#if PY_VERSION_HEX < 0x030B00B1 && !defined(PYPY_VERSION) +PYCAPI_COMPAT_STATIC_INLINE(PyObject*) +PyCode_GetCode(PyCodeObject *code) +{ + return Py_NewRef(code->co_code); +} +#endif + + +// gh-95008 added PyCode_GetVarnames() to Python 3.11.0rc1 +#if PY_VERSION_HEX < 0x030B00C1 && !defined(PYPY_VERSION) +PYCAPI_COMPAT_STATIC_INLINE(PyObject*) +PyCode_GetVarnames(PyCodeObject *code) +{ + return Py_NewRef(code->co_varnames); +} +#endif + +// gh-95008 added PyCode_GetFreevars() to Python 3.11.0rc1 +#if PY_VERSION_HEX < 0x030B00C1 && !defined(PYPY_VERSION) +PYCAPI_COMPAT_STATIC_INLINE(PyObject*) +PyCode_GetFreevars(PyCodeObject *code) +{ + return Py_NewRef(code->co_freevars); +} +#endif + +// gh-95008 added PyCode_GetCellvars() to Python 3.11.0rc1 +#if PY_VERSION_HEX < 0x030B00C1 && !defined(PYPY_VERSION) +PYCAPI_COMPAT_STATIC_INLINE(PyObject*) +PyCode_GetCellvars(PyCodeObject *code) +{ + return Py_NewRef(code->co_cellvars); +} +#endif + + +// Py_UNUSED() was added to Python 3.4.0b2. +#if PY_VERSION_HEX < 0x030400B2 && !defined(Py_UNUSED) +# if defined(__GNUC__) || defined(__clang__) +# define Py_UNUSED(name) _unused_ ## name __attribute__((unused)) +# else +# define Py_UNUSED(name) _unused_ ## name +# endif +#endif + + +#ifdef __cplusplus +} +#endif +#endif // PYTHONCAPI_COMPAT diff --git a/code/.venv/lib/python3.12/site-packages/bitarray/test_150.pickle b/code/.venv/lib/python3.12/site-packages/bitarray/test_150.pickle new file mode 100644 index 0000000..fb8d8ab Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/bitarray/test_150.pickle differ diff --git a/code/.venv/lib/python3.12/site-packages/bitarray/test_281.pickle b/code/.venv/lib/python3.12/site-packages/bitarray/test_281.pickle new file mode 100644 index 0000000..8827214 Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/bitarray/test_281.pickle differ diff --git a/code/.venv/lib/python3.12/site-packages/bitarray/test_bitarray.py b/code/.venv/lib/python3.12/site-packages/bitarray/test_bitarray.py new file mode 100644 index 0000000..4347781 --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/bitarray/test_bitarray.py @@ -0,0 +1,5157 @@ +""" +Tests for bitarray + +Author: Ilan Schnell +""" +from __future__ import absolute_import + +import re +import os +import sys +import platform +import unittest +import shutil +import tempfile +from random import getrandbits, randrange, randint, shuffle + +# imports needed inside tests +import array +import copy +import itertools +import mmap +import pickle +import shelve +import weakref + + +is_py3k = bool(sys.version_info[0] == 3) +pyodide = bool(platform.machine() == 'wasm32') +is_pypy = bool(platform.python_implementation() == 'PyPy') + +if is_py3k: + from io import BytesIO +else: + from cStringIO import StringIO as BytesIO # type: ignore + range = xrange # type: ignore + + +from bitarray import (bitarray, frozenbitarray, bits2bytes, decodetree, + get_default_endian, _set_default_endian, + _bitarray_reconstructor, _sysinfo, __version__) + +def skipIf(condition): + "Skip a test if the condition is true." + if condition: + return lambda f: None + return lambda f: f + +SYSINFO = _sysinfo() +DEBUG = SYSINFO[6] + +def buffer_info(a, key=None): + fields = ( + "address", # 0. address of byte buffer + "size", # 1. buffer size in bytes + "endian", # 2. bit-endianness + "padding", # 3. number of pad bits + "allocated", # 4. allocated memory + "readonly", # 5. memory is read-only + "imported", # 6. buffer is imported + "exports", # 7. number of buffer exports + ) + info = a.buffer_info() + res = dict(zip(fields, info)) + return res if key is None else res[key] + + +# avoid importing from bitarray.util +zeros = bitarray + +def ones(n, endian=None): + a = bitarray(n, endian) + a.setall(1) + return a + +def urandom(n, endian=None): + a = bitarray(0, endian) + a.frombytes(os.urandom(bits2bytes(n))) + del a[n:] + return a + +WHITESPACE = ' \n\r\t\v' + + +class Util(object): + + @staticmethod + def random_endian(): + return ['little', 'big'][getrandbits(1)] + + def randombitarrays(self, start=0): + for n in range(start, 10): + yield urandom(n, self.random_endian()) + for _ in range(3): + yield urandom(randrange(start, 1000), self.random_endian()) + + def randomlists(self): + for a in self.randombitarrays(): + yield a.tolist() + + @staticmethod + def rndsliceidx(length): + if getrandbits(1): + return None + else: + return randint(-length - 5, length + 5) + + @staticmethod + def opposite_endian(endian): + t = {'little': 'big', + 'big': 'little'} + return t[endian] + + @staticmethod + def calc_slicelength(s, length): + assert isinstance(s, slice) + start, stop, step = s.indices(length) + assert step < 0 or (start >= 0 and stop >= 0) + assert step > 0 or (start >= -1 and stop >= -1) + + # This implementation works because Python's floor division (a // b) + # always rounds to the lowest integer, even when a or b are negative. + res1 = (stop - start + (1 if step < 0 else -1)) // step + 1 + if res1 < 0: + res1 = 0 + + # The above implementation is not used in C. + # In C's a / b, if either a or b is negative, the result depends on + # the compiler. Therefore, we use the implementation below (where + # both a and b are always positive). + res2 = 0 + if step < 0: + if stop < start: + res2 = (start - stop - 1) // (-step) + 1 + else: + if start < stop: + res2 = (stop - start - 1) // step + 1 + + assert res1 == res2 + return res1 + + def check_obj(self, a): + self.assertIsInstance(a, bitarray) + + ptr, size, endian, padbits, alloc, readonly, buf, exports = \ + a.buffer_info() + + self.assertEqual(size, bits2bytes(len(a))) + self.assertEqual(padbits, 8 * size - len(a)) + self.assertTrue(0 <= padbits < 8) + self.assertEqual(endian, a.endian()) + self.assertTrue(endian in ('little', 'big')) + + self.assertEqual(a.nbytes, size) + self.assertEqual(a.padbits, padbits) + self.assertEqual(a.readonly, readonly) + self.assertEqual(len(a) + a.padbits, 8 * a.nbytes) + + if buf: + # imported buffer implies that no extra memory is allocated + self.assertEqual(alloc, 0) + # an imported buffer will always have a multiple of 8 bits + self.assertEqual(len(a) % 8, 0) + self.assertEqual(len(a), 8 * size) + self.assertEqual(padbits, 0) + else: + # the allocated memory is always larger than the buffer size + self.assertTrue(alloc >= size) + + if ptr == 0: + # the buffer being a NULL pointer implies that the buffer size + # and the allocated memory size are 0 + self.assertEqual(size, 0) + self.assertEqual(alloc, 0) + + if type(a).__name__ == 'frozenbitarray': + # frozenbitarray have read-only memory + self.assertEqual(readonly, 1) + if padbits: # ensure padbits are zero + b = bitarray(endian=endian) + b.frombytes(a.tobytes()[-1:]) + self.assertFalse(b[-padbits:].any()) + elif not buf: + # otherwise, unless the buffer is imported, it is writable + self.assertEqual(readonly, 0) + + def assertEQUAL(self, a, b): + self.assertEqual(a, b) + self.assertEqual(a.endian(), b.endian()) + + def assertIsType(self, a, b): + self.assertEqual(type(a).__name__, b) + self.assertEqual( + repr(type(a)), "<%s 'bitarray.%s'>" % + ('class' if is_py3k or b == 'frozenbitarray' else 'type', b)) + + def assertBitEqual(self, x, y): + for z in x, y: + self.assertEqual('01'[z], repr(z)) + self.assertEqual(x, y) + + def assertStopIteration(self, it): + self.assertRaises(StopIteration, next, it) + + def assertRaisesMessage(self, excClass, msg, callable, *args, **kwargs): + try: + callable(*args, **kwargs) + raise AssertionError("%s not raised" % excClass.__name__) + except excClass as e: + if msg != str(e): + raise AssertionError("message: %s\n got: %s" % (msg, e)) + +# --------------------------------------------------------------------------- + +class TestsModuleFunctions(unittest.TestCase, Util): + + def test_version_string(self): + # the version string is not a function, but test it here anyway + self.assertIsInstance(__version__, str) + + def test_sysinfo(self): + info = _sysinfo() + self.assertIsInstance(info, tuple) + for x in info: + self.assertIsInstance(x, int) + + if not is_pypy: + self.assertEqual(info[0], tuple.__itemsize__) + self.assertEqual(info[7], int(sys.byteorder == 'little')) + self.assertEqual(info[8], int(sys.byteorder == 'big')) + self.assertEqual(info[7] + info[8], 1) + + def test_set_default_endian(self): + self.assertRaises(TypeError, _set_default_endian, 0) + self.assertRaises(TypeError, _set_default_endian, 'little', 0) + self.assertRaises(ValueError, _set_default_endian, 'foo') + for default_endian in 'big', 'little', u'big', u'little': + _set_default_endian(default_endian) + a = bitarray() + self.assertEqual(a.endian(), default_endian) + for x in None, 0, 64, '10111', [1, 0]: + a = bitarray(x) + self.assertEqual(a.endian(), default_endian) + + for endian in 'big', 'little', None: + a = bitarray(endian=endian) + self.assertEqual(a.endian(), + default_endian if endian is None else endian) + + # make sure that calling _set_default_endian wrong does not + # change the default endianness + self.assertRaises(ValueError, _set_default_endian, 'foobar') + self.assertEqual(bitarray().endian(), default_endian) + + def test_get_default_endian(self): + # takes no arguments + self.assertRaises(TypeError, get_default_endian, 'big') + for default_endian in 'big', 'little': + _set_default_endian(default_endian) + endian = get_default_endian() + self.assertEqual(endian, default_endian) + self.assertIsInstance(endian, str) + + def test_bits2bytes(self): + for arg in 'foo', [], None, {}, 187.0, -4.0: + self.assertRaises(TypeError, bits2bytes, arg) + + self.assertRaises(TypeError, bits2bytes) + self.assertRaises(TypeError, bits2bytes, 1, 2) + + self.assertRaises(ValueError, bits2bytes, -1) + self.assertRaises(ValueError, bits2bytes, -924) + + self.assertEqual(bits2bytes(0), 0) + for n in range(1, 100): + m = bits2bytes(n) + self.assertEqual(m, (n - 1) // 8 + 1) + self.assertIsInstance(m, int) + + for n, m in [(0, 0), (1, 1), (2, 1), (7, 1), (8, 1), (9, 2), + (10, 2), (15, 2), (16, 2), (64, 8), (65, 9), + (2**31, 2**28), (2**32, 2**29), (2**34, 2**31), + (2**34+793, 2**31+100), (2**35-8, 2**32-1), + (2**62, 2**59), (2**63-8, 2**60-1)]: + self.assertEqual(bits2bytes(n), m) + +# --------------------------------------------------------------------------- + +class CreateObjectTests(unittest.TestCase, Util): + + def test_noInitializer(self): + a = bitarray() + self.assertEqual(len(a), 0) + self.assertEqual(a.tolist(), []) + self.assertIsType(a, 'bitarray') + self.check_obj(a) + + def test_endian(self): + a = bitarray(endian='little') + a.frombytes(b'ABC') + self.assertEqual(a.endian(), 'little') + self.assertIsInstance(a.endian(), str) + self.check_obj(a) + + b = bitarray(endian='big') + b.frombytes(b'ABC') + self.assertEqual(b.endian(), 'big') + self.assertIsInstance(a.endian(), str) + self.check_obj(b) + + self.assertNotEqual(a, b) + self.assertEqual(a.tobytes(), b.tobytes()) + + def test_endian_default(self): + _set_default_endian('big') + a_big = bitarray() + _set_default_endian('little') + a_little = bitarray() + _set_default_endian('big') + + self.assertEqual(a_big.endian(), 'big') + self.assertEqual(a_little.endian(), 'little') + + def test_endian_wrong(self): + self.assertRaises(TypeError, bitarray, endian=0) + self.assertRaises(ValueError, bitarray, endian='') + self.assertRaisesMessage( + ValueError, + "bit-endianness must be either 'little' or 'big', not 'foo'", + bitarray, endian='foo') + self.assertRaisesMessage(TypeError, + "'ellipsis' object is not iterable", + bitarray, Ellipsis) + + def test_buffer_endian(self): + for endian in 'big', 'little': + a = bitarray(buffer=b'', endian=endian) + self.assertEQUAL(a, bitarray(0, endian)) + + _set_default_endian(endian) + a = bitarray(buffer=b'A') + self.assertEqual(a.endian(), endian) + self.assertEqual(len(a), 8) + + def test_buffer_readonly(self): + a = bitarray(buffer=b'\xf0', endian='little') + self.assertTrue(a.readonly) + self.assertRaises(TypeError, a.clear) + self.assertRaises(TypeError, a.__setitem__, 3, 1) + self.assertEQUAL(a, bitarray('00001111', 'little')) + self.check_obj(a) + + @skipIf(is_pypy) + def test_buffer_writeable(self): + a = bitarray(buffer=bytearray([65])) + self.assertFalse(a.readonly) + a[6] = 1 + + def test_buffer_args(self): + # buffer requires no initial argument + self.assertRaises(TypeError, bitarray, 5, buffer=b'DATA\0') + + # positinal arguments + a = bitarray(None, 'big', bytearray([15])) + self.assertEQUAL(a, bitarray('00001111', 'big')) + a = bitarray(None, 'little', None) + self.assertEQUAL(a, bitarray(0, 'little')) + + def test_none(self): + for a in [bitarray(None), + bitarray(None, buffer=None), + bitarray(None, buffer=Ellipsis), + bitarray(None, None, None), + bitarray(None, None, Ellipsis)]: + self.assertEqual(len(a), 0) + + def test_int(self): + for n in range(50): + a = bitarray(n) + self.assertEqual(len(a), n) + self.assertFalse(a.any()) + self.assertEqual(a.to01(), n * '0') + self.check_obj(a) + + # uninitialized buffer + a = bitarray(n, buffer=Ellipsis) + self.assertEqual(len(a), n) + self.check_obj(a) + + if not is_py3k: + a = bitarray(long(29)) + self.assertEqual(len(a), 29) + + self.assertRaises(ValueError, bitarray, -1) + self.assertRaises(ValueError, bitarray, -924) + + def test_list(self): + lst = [0, 1, False, True] + a = bitarray(lst) + self.assertEqual(a, bitarray('0101')) + self.check_obj(a) + + if not is_py3k: + a = bitarray([long(1), long(0)]) + self.assertEqual(a, bitarray('10')) + + self.assertRaises(ValueError, bitarray, [0, 1, 2]) + self.assertRaises(TypeError, bitarray, [0, 1, None]) + + for n in range(50): + lst = [bool(getrandbits(1)) for d in range(n)] + a = bitarray(lst) + self.assertEqual(a.tolist(), lst) + self.check_obj(a) + + def test_tuple(self): + tup = (0, True, False, 1) + a = bitarray(tup) + self.assertEqual(a, bitarray('0101')) + self.check_obj(a) + + self.assertRaises(ValueError, bitarray, (0, 1, 2)) + self.assertRaises(TypeError, bitarray, (0, 1, None)) + + for n in range(50): + lst = [bool(getrandbits(1)) for d in range(n)] + a = bitarray(tuple(lst)) + self.assertEqual(a.tolist(), lst) + self.check_obj(a) + + def test_iter1(self): + for n in range(50): + lst = [bool(getrandbits(1)) for d in range(n)] + a = bitarray(iter(lst)) + self.assertEqual(a.tolist(), lst) + self.check_obj(a) + + def test_iter2(self): + for lst in self.randomlists(): + def foo(): + for x in lst: + yield x + a = bitarray(foo()) + self.assertEqual(a, bitarray(lst)) + self.check_obj(a) + + def test_iter3(self): + a = bitarray(itertools.repeat(False, 10)) + self.assertEqual(a, zeros(10)) + a = bitarray(itertools.repeat(1, 10)) + self.assertEqual(a, bitarray(10 * '1')) + + def test_range(self): + self.assertEqual(bitarray(range(2)), bitarray('01')) + self.assertRaises(ValueError, bitarray, range(0, 3)) + + def test_string01(self): + for s in ('0010111', u'0010111', '0010 111', u'0010 111', + '0010_111', u'0010_111'): + a = bitarray(s) + self.assertEqual(a.tolist(), [0, 0, 1, 0, 1, 1, 1]) + self.check_obj(a) + + for n in range(50): + lst = [bool(getrandbits(1)) for d in range(n)] + s = ''.join([['0', '1'][x] for x in lst]) + a = bitarray(s) + self.assertEqual(a.tolist(), lst) + self.check_obj(a) + + self.assertRaises(ValueError, bitarray, '01021') + self.assertRaises(UnicodeEncodeError, bitarray, u'1\u26050') + + def test_string01_whitespace(self): + a = bitarray(WHITESPACE) + self.assertEqual(a, bitarray()) + + # For Python 2 (where strings are bytes), we are in the lucky + # position that none of the valid characters ('\t'=9, '\n'=10, + # '\v'=11, '\r'=13, ' '=32, '0'=48 and '1'=49) are valid pickle + # header bytes (0..7). + # Therefore a string of '0's and '1'a can start with any whitespace + # character, as well as '0' or '1' (obviously). + for c in WHITESPACE: + a = bitarray(c + '1101110001') + self.assertEqual(a, bitarray('1101110001')) + + a = bitarray(' 0\n1\r0\t1\v0 ') + self.assertEqual(a, bitarray('01010')) + + def test_rawbytes(self): # representation used for pickling + for blob, endian, s in [ + (b'\x00', 'little', ''), + (b'\x07\x01', 'little', '1'), + (b'\x07\x80', 'big', '1'), + (b'\x03\xff', 'big', '11111'), + (b'\x00\x0f', 'little', '11110000'), + (b'\x00\xf0', 'big', '11110000'), + (b'\x02\x87\xda', 'big', '10000111 110110') + ]: + a = bitarray(blob, endian) + self.assertEqual(a, bitarray(s)) + self.assertEqual(a.endian(), endian) + self.check_obj(a) + + def test_rawbytes_invalid(self): + msg3 = ("cannot extend bitarray with 'bytes', " + "use .pack() or .frombytes() instead") + if is_py3k: + # no bytes will cause TypeError + self.assertRaisesMessage(TypeError, msg3, bitarray, b'') + else: + # no bytes are interpreted as an empty string on Python 2 + self.assertEqual(bitarray(b''), bitarray()) + + for blob in b'\x00', b'\x07\x80': + self.assertRaisesMessage(ValueError, + "endianness missing for pickle", + bitarray, blob) + + for i in range(1, 8): + b = bytes(bytearray([i])) + # this error is raised in newbitarray_from_pickle() + self.assertRaises(ValueError, bitarray, b, 'big') + # Python 2: PyErr_Format() seems to handle "0x%02x" + # incorrectly. E.g. instead of "0x01", I get "0x1" + if is_py3k: + self.assertRaisesMessage(ValueError, + "invalid pickle header byte: 0x%02x" % b[0], + bitarray, b, 'big') + + for i in range(8, 256): + b = bytes(bytearray([i])) + if is_py3k: + # we don't allow bitarrays being created from bytes + self.assertRaises(TypeError, bitarray, b) + continue + + # on Python 2 + if b in WHITESPACE + '_01': + # character is valid + self.assertEqual(len(bitarray(b)), 1 if b in '01' else 0) + else: + # character is invalid + self.assertRaises(ValueError, bitarray, b) + + def test_bitarray_simple(self): + for n in range(10): + a = bitarray(n) + b = bitarray(a, endian=None) + self.assertFalse(a is b) + self.assertEQUAL(a, b) + + def test_bitarray_endian(self): + # Test creating a new bitarray with different endianness from an + # existing bitarray. + for endian in 'little', 'big', u'little', u'big': + a = bitarray(endian=endian) + b = bitarray(a) + self.assertFalse(a is b) + self.assertEQUAL(a, b) + + endian2 = self.opposite_endian(endian) + b = bitarray(a, endian2) + self.assertEqual(b.endian(), endian2) + self.assertEqual(a, b) + + for a in self.randombitarrays(): + endian2 = self.opposite_endian(a.endian()) + b = bitarray(a, endian2) + self.assertEqual(a, b) + self.assertEqual(b.endian(), endian2) + self.assertNotEqual(a.endian(), b.endian()) + + def test_bitarray_endianness(self): + a = bitarray('11100001', endian='little') + b = bitarray(a, endian='big') + self.assertEqual(a, b) + self.assertNotEqual(a.tobytes(), b.tobytes()) + + b.bytereverse() + self.assertNotEqual(a, b) + self.assertEqual(a.tobytes(), b.tobytes()) + + c = bitarray('11100001', endian='big') + self.assertEqual(a, c) + + def test_frozenbitarray(self): + a = bitarray(frozenbitarray()) + self.assertEQUAL(a, bitarray()) + self.assertIsType(a, 'bitarray') + + for endian in 'little', 'big': + a = bitarray(frozenbitarray('011', endian=endian)) + self.assertEQUAL(a, bitarray('011', endian)) + self.assertIsType(a, 'bitarray') + + def test_create_empty(self): + for x in (None, 0, '', list(), tuple(), set(), dict(), u'', + bitarray(), frozenbitarray()): + a = bitarray(x) + self.assertEqual(len(a), 0) + self.assertEQUAL(a, bitarray()) + + if is_py3k: + self.assertRaises(TypeError, bitarray, b'') + else: + self.assertEqual(bitarray(b''), bitarray()) + + def test_wrong_args(self): + # wrong types + for x in False, True, Ellipsis, slice(0), 0.0, 0 + 0j: + self.assertRaises(TypeError, bitarray, x) + if is_py3k: + self.assertRaises(TypeError, bitarray, b'10') + else: + self.assertEQUAL(bitarray(b'10'), bitarray('10')) + # wrong values + for x in -1, 'A': + self.assertRaises(ValueError, bitarray, x) + # test second (endian) argument + self.assertRaises(TypeError, bitarray, 0, 0) + self.assertRaises(ValueError, bitarray, 0, 'foo') + # too many args + self.assertRaises(TypeError, bitarray, 0, 'big', 0) + + @skipIf(is_pypy) + def test_weakref(self): + a = bitarray('0100') + b = weakref.proxy(a) + self.assertEqual(b.to01(), a.to01()) + a = None + self.assertRaises(ReferenceError, len, b) + +# --------------------------------------------------------------------------- + +class ToObjectsTests(unittest.TestCase, Util): + + def test_numeric(self): + a = bitarray() + self.assertRaises(Exception, int, a) + self.assertRaises(Exception, float, a) + self.assertRaises(Exception, complex, a) + + def test_list(self): + for a in self.randombitarrays(): + self.assertEqual(list(a), a.tolist()) + + def test_tuple(self): + for a in self.randombitarrays(): + self.assertEqual(tuple(a), tuple(a.tolist())) + +# --------------------------------------------------------------------------- + +class MetaDataTests(unittest.TestCase): + + def test_buffer_info(self): + a = bitarray(13, endian='little') + self.assertEqual(a.buffer_info()[1:4], (2, 'little', 3)) + + info = a.buffer_info() + self.assertIsInstance(info, tuple) + self.assertEqual(len(info), 8) + for i, item in enumerate(info): + if i == 2: + self.assertIsInstance(item, str) + continue + self.assertIsInstance(item, int) + + def test_endian(self): + for endian in 'big', 'little': + a = bitarray(endian=endian) + self.assertEqual(a.endian(), endian) + + def test_len(self): + for n in range(100): + a = bitarray(n) + self.assertEqual(len(a), n) + +# --------------------------------------------------------------------------- + +@skipIf(not DEBUG) +class InternalTests(unittest.TestCase, Util): + + # Internal functionality exposed for the purpose of testing. + # This class will only be part of the test suite in debug mode. + + def test_shift_r8_empty(self): + a = bitarray() + a._shift_r8(0, 0, 3) + self.assertEqual(a, bitarray()) + + def test_shift_r8_explicit(self): + x = bitarray('11000100 11111111 11100111 10111111 00001000') + y = bitarray('11000100 00000111 11111111 00111101 00001000') + x._shift_r8(1, 4, 5) + self.assertEqual(x, y) + x._shift_r8(2, 1, 5) # start > stop -- do nothing + self.assertEqual(x, y) + x._shift_r8(0, 5, 0) # shift = 0 -- do nothing + self.assertEqual(x, y) + + x = bitarray('11000100 11110') + y = bitarray('00011000 10011') + x._shift_r8(0, 2, 3) + self.assertEqual(x, y) + + x = bitarray('1100011') + y = bitarray('0110001') + x._shift_r8(0, 1, 1) + self.assertEqual(x, y) + + def test_shift_r8_random(self): + for _ in range(5000): + N = randrange(200) + x = urandom(N, self.random_endian()) + a = randint(0, x.nbytes) + b = randint(a, x.nbytes) + n = randrange(8) + y = x.copy() + y[8 * a : 8 * b] >>= n + s = x.to01() + if a < b: + s = s[:8 * a] + n * "0" + s[8 * a : 8 * b - n] + s[8 * b:] + if 8 * b > N: + s = s[:N] + x._shift_r8(a, b, n) + self.assertEqual(x.to01(), s) + self.assertEqual(x, y) + self.assertEqual(x.endian(), y.endian()) + self.assertEqual(len(x), N) + + def test_copy_n_explicit(self): + x = bitarray('11000100 11110') + # ^^^^ ^ + y = bitarray('0101110001') + # ^^^^^ + x._copy_n(4, y, 1, 5) + self.assertEqual(x, bitarray('11001011 11110')) + # ^^^^ ^ + x = bitarray('10110111 101', 'little') + y = x.copy() + x._copy_n(3, x, 3, 7) # copy region of x onto x + self.assertEqual(x, y) + x._copy_n(3, bitarray(x, 'big'), 3, 7) # as before but other endian + self.assertEqual(x, y) + x._copy_n(5, bitarray(), 0, 0) # copy empty bitarray onto x + self.assertEqual(x, y) + + def test_copy_n_example(self): + # example given in examples/copy_n.py + y = bitarray( + '00101110 11111001 01011101 11001011 10110000 01011110 011') + x = bitarray( + '01011101 11100101 01110101 01011001 01110100 10001010 01111011') + x._copy_n(21, y, 6, 31) + self.assertEqual(x, bitarray( + '01011101 11100101 01110101 11110010 10111011 10010111 01101011')) + + def check_copy_n(self, N, M, a, b, n): + x = urandom(N, self.random_endian()) + x_lst = x.tolist() + y = x if M < 0 else urandom(M, self.random_endian()) + y_lst = y.tolist() + x_lst[a:a + n] = y_lst[b:b + n] + x._copy_n(a, y, b, n) + self.assertEqual(x, bitarray(x_lst)) + self.assertEqual(len(x), N) + self.check_obj(x) + + if M < 0: + return + self.assertEqual(y, bitarray(y_lst)) + self.assertEqual(len(y), M) + self.check_obj(y) + + def test_copy_n_random(self): + for repeat, max_size in (1000, 25), (100, 200): + for _ in range(repeat): + N = randrange(max_size) + n = randint(0, N) + a = randint(0, N - n) + b = randint(0, N - n) + self.check_copy_n(N, -1, a, b, n) + + M = randrange(max_size) + n = randint(0, min(N, M)) + a = randint(0, N - n) + b = randint(0, M - n) + self.check_copy_n(N, M, a, b, n) + + @staticmethod + def getslice(a, start, slicelength): + # this is the Python eqivalent of __getitem__ for slices with step=1 + b = bitarray(slicelength, a.endian()) + b._copy_n(0, a, start, slicelength) + return b + + def test_getslice(self): + for a in self.randombitarrays(): + a_lst = a.tolist() + n = len(a) + i = randint(0, n) + j = randint(i, n) + b = self.getslice(a, i, j - i) + self.assertEqual(b.tolist(), a_lst[i:j]) + self.assertEQUAL(b, a[i:j]) + + def check_overlap(self, a, b, res): + r1 = a._overlap(b) + r2 = b._overlap(a) + self.assertIsInstance(r1, bool) + self.assertTrue(r1 == r2 == res) + self.check_obj(a) + self.check_obj(b) + + def test_overlap_empty(self): + a = bitarray() + self.check_overlap(a, a, False) + b = bitarray() + self.check_overlap(a, b, False) + + def test_overlap_distinct(self): + for a in self.randombitarrays(): + # buffers overlaps with itself, unless buffer is NULL + self.check_overlap(a, a, bool(a)) + b = a.copy() + self.check_overlap(a, b, False) + + def test_overlap_shared(self): + a = bitarray(64) + b = bitarray(buffer=a) + self.check_overlap(b, a, True) + + c = bitarray(buffer=memoryview(a)[2:4]) + self.check_overlap(c, a, True) + + d = bitarray(buffer=memoryview(a)[5:]) + self.check_overlap(d, c, False) + self.check_overlap(d, b, True) + + e = bitarray(buffer=memoryview(a)[3:3]) + self.check_overlap(e, c, False) + self.check_overlap(e, d, False) + + def test_overlap_shared_random(self): + n = 100 # buffer size in bytes + a = bitarray(8 * n) + for _ in range(1000): + i1 = randint(0, n) + j1 = randint(i1, n) + b1 = bitarray(buffer=memoryview(a)[i1:j1]) + + i2 = randint(0, n) + j2 = randint(i2, n) + b2 = bitarray(buffer=memoryview(a)[i2:j2]) + + x1, x2 = zeros(n), zeros(n) + x1[i1:j1] = x2[i2:j2] = 1 + self.check_overlap(b1, b2, (x1 & x2).any()) + +# --------------------------------------------------------------------------- + +class SliceTests(unittest.TestCase, Util): + + def test_getitem_1(self): + a = bitarray() + self.assertRaises(IndexError, a.__getitem__, 0) + a.append(True) + self.assertBitEqual(a[0], 1) + self.assertBitEqual(a[-1], 1) + self.assertRaises(IndexError, a.__getitem__, 1) + self.assertRaises(IndexError, a.__getitem__, -2) + a.append(False) + self.assertBitEqual(a[1], 0) + self.assertBitEqual(a[-1], 0) + self.assertRaises(IndexError, a.__getitem__, 2) + self.assertRaises(IndexError, a.__getitem__, -3) + self.assertRaises(TypeError, a.__getitem__, 1.5) + self.assertRaises(TypeError, a.__getitem__, None) + self.assertRaises(TypeError, a.__getitem__, 'A') + + def test_getitem_2(self): + a = bitarray('1100010') + for i, b in enumerate(a): + self.assertBitEqual(a[i], b) + self.assertBitEqual(a[i - 7], b) + self.assertRaises(IndexError, a.__getitem__, 7) + self.assertRaises(IndexError, a.__getitem__, -8) + + def test_getslice(self): + a = bitarray('01001111 00001') + self.assertEQUAL(a[:], a) + self.assertFalse(a[:] is a) + self.assertEQUAL(a[13:2:-3], bitarray('1010')) + self.assertEQUAL(a[2:-1:4], bitarray('010')) + self.assertEQUAL(a[::2], bitarray('0011001')) + self.assertEQUAL(a[8:], bitarray('00001')) + self.assertEQUAL(a[7:], bitarray('100001')) + self.assertEQUAL(a[:8], bitarray('01001111')) + self.assertEQUAL(a[::-1], bitarray('10000111 10010')) + self.assertEQUAL(a[:8:-1], bitarray('1000')) + + self.assertRaises(ValueError, a.__getitem__, slice(None, None, 0)) + + def test_getslice_random(self): + for a in self.randombitarrays(start=1): + aa = a.tolist() + la = len(a) + for _ in range(10): + step = self.rndsliceidx(la) or None + s = slice(self.rndsliceidx(la), self.rndsliceidx(la), step) + self.assertEQUAL(a[s], bitarray(aa[s], endian=a.endian())) + + def test_getslice_random_step1(self): + for _ in range(1000): + n = randrange(200) + a = urandom(n, self.random_endian()) + sa = a.to01() + i = randint(0, n) + j = randint(0, n) + b = a[i:j] + self.assertEqual(b.to01(), sa[i:j]) + self.assertEqual(len(b), max(j - i, 0)) + self.assertEqual(b.endian(), a.endian()) + + def test_setitem_simple(self): + a = bitarray('0') + a[0] = 1 + self.assertEqual(a, bitarray('1')) + + a = bitarray(2) + a[0] = 0 + a[1] = 1 + self.assertEqual(a, bitarray('01')) + a[-1] = 0 + a[-2] = 1 + self.assertEqual(a, bitarray('10')) + + self.assertRaises(ValueError, a.__setitem__, 0, -1) + self.assertRaises(TypeError, a.__setitem__, 1, None) + + self.assertRaises(IndexError, a.__setitem__, 2, True) + self.assertRaises(IndexError, a.__setitem__, -3, False) + self.assertRaises(TypeError, a.__setitem__, 1.5, 1) # see issue 114 + self.assertRaises(TypeError, a.__setitem__, None, 0) + self.assertRaises(TypeError, a.__setitem__, 'a', True) + self.assertEqual(a, bitarray('10')) + + def test_setitem_random(self): + for a in self.randombitarrays(start=1): + i = randrange(len(a)) + aa = a.tolist() + val = bool(getrandbits(1)) + a[i] = val + aa[i] = val + self.assertEqual(a.tolist(), aa) + self.check_obj(a) + + def test_setslice_simple(self): + for a in self.randombitarrays(start=1): + la = len(a) + b = bitarray(la) + b[0:la] = bitarray(a) + self.assertEqual(a, b) + self.assertFalse(a is b) + + b = bitarray(la) + b[:] = bitarray(a) + self.assertEqual(a, b) + self.assertFalse(a is b) + + b = bitarray(la) + b[::-1] = bitarray(a) + self.assertEqual(a.tolist()[::-1], b.tolist()) + + def test_setslice_random(self): + for a in self.randombitarrays(start=1): + la = len(a) + for _ in range(10): + step = self.rndsliceidx(la) or None + s = slice(self.rndsliceidx(la), self.rndsliceidx(la), step) + lb = (randrange(10) if step is None else + self.calc_slicelength(s, la)) + b = bitarray(lb) + c = bitarray(a) + c[s] = b + self.check_obj(c) + cc = a.tolist() + cc[s] = b.tolist() + self.assertEqual(c, bitarray(cc)) + + def test_setslice_self_random(self): + for a in self.randombitarrays(): + for step in -1, 1: + s = slice(None, None, step) + aa = a.tolist() + a[s] = a + aa[s] = aa + self.assertEqual(a, bitarray(aa)) + + def test_setslice_special(self): + for n in 0, 1, 10, 87: + a = urandom(n) + for m in 0, 1, 10, 99: + x = urandom(m) + b = a.copy() + b[n:n] = x # insert at end - extend + self.assertEqual(b, a + x) + self.assertEqual(len(b), len(a) + len(x)) + b[0:0] = x # insert at 0 - prepend + self.assertEqual(b, x + a + x) + self.check_obj(b) + self.assertEqual(len(b), len(a) + 2 * len(x)) + + def test_setslice_range(self): + # tests C function insert_n() + for endian in 'big', 'little': + for n in range(500): + a = urandom(n, endian) + p = randint(0, n) + m = randint(0, 500) + + x = urandom(m, self.random_endian()) + b = a.copy() + b[p:p] = x + self.assertEQUAL(b, a[:p] + x + a[p:]) + self.assertEqual(len(b), len(a) + m) + self.check_obj(b) + + def test_setslice_resize(self): + N, M = 200, 300 + for endian in 'big', 'little': + for n in 0, randint(0, N), N: + a = urandom(n, endian) + for p1 in 0, randint(0, n), n: + for p2 in 0, randint(0, p1), p1, randint(0, n), n: + for m in 0, randint(0, M), M: + x = urandom(m, self.random_endian()) + b = a.copy() + b[p1:p2] = x + b_lst = a.tolist() + b_lst[p1:p2] = x.tolist() + self.assertEqual(b.tolist(), b_lst) + if p1 <= p2: + self.assertEQUAL(b, a[:p1] + x + a[p2:]) + self.assertEqual(len(b), n + p1 - p2 + len(x)) + else: + self.assertEqual(b, a[:p1] + x + a[p1:]) + self.assertEqual(len(b), n + len(x)) + self.check_obj(b) + + def test_setslice_self(self): + a = bitarray('1100111') + a[::-1] = a + self.assertEqual(a, bitarray('1110011')) + a[4:] = a + self.assertEqual(a, bitarray('11101110011')) + a[:-5] = a + self.assertEqual(a, bitarray('1110111001110011')) + + a = bitarray('01001') + a[:-1] = a + self.assertEqual(a, bitarray('010011')) + a[2::] = a + self.assertEqual(a, bitarray('01010011')) + a[2:-2:1] = a + self.assertEqual(a, bitarray('010101001111')) + + a = bitarray('011') + a[2:2] = a + self.assertEqual(a, bitarray('010111')) + a[:] = a + self.assertEqual(a, bitarray('010111')) + + def test_setslice_self_shared_buffer(self): + # This is a special case. We have two bitarrays which share the + # same buffer, and then do a slice assignment. The bitarray is + # copied onto itself in reverse order. So we need to make a copy + # in setslice_bitarray(). However, since a and b are two distinct + # objects, it is not enough to check for self == other, but rather + # check whether their buffers overlap. + a = bitarray('11100000') + b = bitarray(buffer=a) + b[::-1] = a + self.assertEqual(a, b) + self.assertEqual(a, bitarray('00000111')) + + def test_setslice_self_shared_buffer_2(self): + # This is an even more special case. We have a bitarrays which + # shares part of anothers bitarray buffer. So in setslice_bitarray(), + # we need to make a copy of other if: + # + # self->ob_item <= other->ob_item <= self->ob_item + Py_SIZE(self) + # + # In words: Is the other buffer inside the self buffer (which inclues + # the previous case) + a = bitarray('11111111 11000000 00000000') + b = bitarray(buffer=memoryview(a)[1:2]) + self.assertEqual(b, bitarray('11000000')) + a[15:7:-1] = b + self.assertEqual(a, bitarray('11111111 00000011 00000000')) + + @skipIf(is_pypy) + def test_setslice_self_shared_buffer_3(self): + # Requires to check for (in setslice_bitarray()): + # + # other->ob_item <= self->ob_item <= other->ob_item + Py_SIZE(other) + # + a = bitarray('11111111 11000000 00000000') + b = bitarray(buffer=memoryview(a)[:2]) + c = bitarray(buffer=memoryview(a)[1:]) + self.assertEqual(b, bitarray('11111111 11000000')) + self.assertEqual(c, bitarray('11000000 00000000')) + c[::-1] = b + self.assertEqual(c, bitarray('00000011 11111111')) + self.assertEqual(a, bitarray('11111111 00000011 11111111')) + + def test_setslice_bitarray(self): + a = ones(12) + a[2:6] = bitarray('0010') + self.assertEqual(a, bitarray('11001011 1111')) + a.setall(0) + a[::2] = bitarray('111001') + self.assertEqual(a, bitarray('10101000 0010')) + a.setall(0) + a[3:] = bitarray('111') + self.assertEqual(a, bitarray('000111')) + + a = zeros(12) + a[1:11:2] = bitarray('11101') + self.assertEqual(a, bitarray('01010100 0100')) + a.setall(0) + a[5:2] = bitarray('111') # make sure inserts before 5 (not 2) + self.assertEqual(a, bitarray('00000111 0000000')) + + a = zeros(12) + a[:-6:-1] = bitarray('10111') + self.assertEqual(a, bitarray('00000001 1101')) + + def test_setslice_bitarray_2(self): + a = bitarray('1111') + a[3:3] = bitarray('000') # insert + self.assertEqual(a, bitarray('1110001')) + a[2:5] = bitarray() # remove + self.assertEqual(a, bitarray('1101')) + + a = bitarray('1111') + a[1:3] = bitarray('0000') + self.assertEqual(a, bitarray('100001')) + a[:] = bitarray('010') # replace all values + self.assertEqual(a, bitarray('010')) + + # assign slice to bitarray with different length + a = bitarray('111111') + a[3:4] = bitarray('00') + self.assertEqual(a, bitarray('1110011')) + a[2:5] = bitarray('0') # remove + self.assertEqual(a, bitarray('11011')) + + def test_setslice_frozenbitarray(self): + a = bitarray('11111111 1111') + b = frozenbitarray('0000') + a[2:6] = b + self.assertEqual(a, bitarray('11000011 1111')) + self.assertIsType(b, 'frozenbitarray') + self.assertEqual(b, bitarray('0000')) + + b = frozenbitarray('011100') + a[::2] = b + self.assertEqual(a, bitarray('01101011 0101')) + self.check_obj(a) + self.assertIsType(b, 'frozenbitarray') + self.assertEqual(b, bitarray('011100')) + + def test_setslice_bitarray_random_same_length(self): + for endian in 'little', 'big': + for _ in range(100): + n = randrange(200) + a = urandom(n, endian) + lst_a = a.tolist() + b = urandom(randint(0, n), self.random_endian()) + lst_b = b.tolist() + i = randint(0, n - len(b)) + j = i + len(b) + self.assertEqual(j - i, len(b)) + a[i:j] = b + lst_a[i:j] = lst_b + self.assertEqual(a.tolist(), lst_a) + # a didn't change length + self.assertEqual(len(a), n) + self.assertEqual(a.endian(), endian) + self.check_obj(a) + + def test_setslice_bitarray_random_step_1(self): + for _ in range(50): + n = randrange(300) + a = urandom(n, self.random_endian()) + lst_a = a.tolist() + b = urandom(randint(0, 100), self.random_endian()) + lst_b = b.tolist() + s = slice(self.rndsliceidx(n), self.rndsliceidx(n), None) + a[s] = b + lst_a[s] = lst_b + self.assertEqual(a.tolist(), lst_a) + self.check_obj(a) + + def test_setslice_bitarray_random(self): + for _ in range(100): + n = randrange(50) + a = urandom(n, self.random_endian()) + lst_a = a.tolist() + b = urandom(randrange(50), self.random_endian()) + lst_b = b.tolist() + s = slice(self.rndsliceidx(n), self.rndsliceidx(n), + randint(-3, 3) or None) + try: + a[s] = b + except ValueError: + a = None + + try: + lst_a[s] = lst_b + except ValueError: + lst_a = None + + if a is None: + self.assertTrue(lst_a is None) + else: + self.assertEqual(a.tolist(), lst_a) + self.check_obj(a) + + def test_setslice_bool_explicit(self): + a = bitarray('11111111') + a[::2] = False + self.assertEqual(a, bitarray('01010101')) + a[4::] = True # ^^^^ + self.assertEqual(a, bitarray('01011111')) + a[-2:] = False # ^^ + self.assertEqual(a, bitarray('01011100')) + a[:2:] = True # ^^ + self.assertEqual(a, bitarray('11011100')) + a[:] = True # ^^^^^^^^ + self.assertEqual(a, bitarray('11111111')) + a[2:5] = False # ^^^ + self.assertEqual(a, bitarray('11000111')) + a[1::3] = False # ^ ^ ^ + self.assertEqual(a, bitarray('10000110')) + a[1:6:2] = True # ^ ^ ^ + self.assertEqual(a, bitarray('11010110')) + a[3:3] = False # zero slicelength + self.assertEqual(a, bitarray('11010110')) + a[:] = False # ^^^^^^^^ + self.assertEqual(a, bitarray('00000000')) + a[-2:2:-1] = 1 # ^^^^ + self.assertEqual(a, bitarray('00011110')) + + def test_setslice_bool_simple(self): + for _ in range(100): + N = randint(100, 2000) + s = slice(randint(0, 20), randint(N - 20, N), randint(1, 20)) + a = zeros(N) + a[s] = 1 + b = zeros(N) + b[list(range(s.start, s.stop, s.step))] = 1 + self.assertEqual(a, b) + + def test_setslice_bool_range(self): + N = 200 + a = bitarray(N, self.random_endian()) + b = bitarray(N) + for step in range(-N - 1, N): + if step == 0: + continue + v = getrandbits(1) + a.setall(not v) + a[::step] = v + + b.setall(not v) + b[list(range(0, N, abs(step)))] = v + if step < 0: + b.reverse() + self.assertEqual(a, b) + + def test_setslice_bool_random(self): + N = 100 + a = bitarray(N) + for _ in range(100): + a.setall(0) + aa = a.tolist() + step = self.rndsliceidx(N) or None + s = slice(self.rndsliceidx(N), self.rndsliceidx(N), step) + a[s] = 1 + aa[s] = self.calc_slicelength(s, N) * [1] + self.assertEqual(a.tolist(), aa) + + def test_setslice_bool_random2(self): + for a in self.randombitarrays(): + n = len(a) + aa = a.tolist() + step = self.rndsliceidx(n) or None + s = slice(self.rndsliceidx(n), self.rndsliceidx(n), step) + v = getrandbits(1) + a[s] = v + aa[s] = self.calc_slicelength(s, n) * [v] + self.assertEqual(a.tolist(), aa) + + def test_setslice_to_int(self): + a = bitarray('11111111') + a[::2] = 0 # ^ ^ ^ ^ + self.assertEqual(a, bitarray('01010101')) + a[4::] = 1 # ^^^^ + self.assertEqual(a, bitarray('01011111')) + a.__setitem__(slice(-2, None, None), 0) + self.assertEqual(a, bitarray('01011100')) + self.assertRaises(ValueError, a.__setitem__, slice(None, None, 2), 3) + self.assertRaises(ValueError, a.__setitem__, slice(None, 2, None), -1) + # a[:2:] = '0' + self.assertRaises(TypeError, a.__setitem__, slice(None, 2, None), '0') + + def test_setslice_to_invalid(self): + a = bitarray('11111111') + s = slice(2, 6, None) + self.assertRaises(TypeError, a.__setitem__, s, 1.2) + self.assertRaises(TypeError, a.__setitem__, s, None) + self.assertRaises(TypeError, a.__setitem__, s, "0110") + a[s] = False + self.assertEqual(a, bitarray('11000011')) + # step != 1 and slicelen != length of assigned bitarray + self.assertRaisesMessage( + ValueError, + "attempt to assign sequence of size 3 to extended slice of size 4", + a.__setitem__, slice(None, None, 2), bitarray('000')) + self.assertRaisesMessage( + ValueError, + "attempt to assign sequence of size 3 to extended slice of size 2", + a.__setitem__, slice(None, None, 4), bitarray('000')) + self.assertRaisesMessage( + ValueError, + "attempt to assign sequence of size 7 to extended slice of size 8", + a.__setitem__, slice(None, None, -1), bitarray('0001000')) + self.assertEqual(a, bitarray('11000011')) + + def test_delitem_simple(self): + a = bitarray('100110') + del a[1] + self.assertEqual(len(a), 5) + del a[3], a[-2] + self.assertEqual(a, bitarray('100')) + self.assertRaises(IndexError, a.__delitem__, 3) + self.assertRaises(IndexError, a.__delitem__, -4) + + def test_delitem_random(self): + for a in self.randombitarrays(start=1): + n = len(a) + b = a.copy() + i = randrange(n) + del b[i] + self.assertEQUAL(b, a[:i] + a[i + 1:]) + self.assertEqual(len(b), n - 1) + self.check_obj(b) + + def test_delslice_explicit(self): + a = bitarray('10101100 10110') + del a[3:9] # ^^^^^ ^ + self.assertEqual(a, bitarray('1010110')) + del a[::3] # ^ ^ ^ + self.assertEqual(a, bitarray('0111')) + a = bitarray('10101100 101101111') + del a[5:-3:3] # ^ ^ ^ + self.assertEqual(a, bitarray('1010100 0101111')) + a = bitarray('10101100 1011011') + del a[:-9:-2] # ^ ^ ^ ^ + self.assertEqual(a, bitarray('10101100 011')) + del a[3:3] # zero slicelength + self.assertEqual(a, bitarray('10101100 011')) + self.assertRaises(ValueError, a.__delitem__, slice(None, None, 0)) + self.assertEqual(len(a), 11) + del a[:] + self.assertEqual(a, bitarray()) + + def test_delslice_special(self): + for n in 0, 1, 10, 73: + a = urandom(n) + b = a.copy() + del b[:0] + del b[n:] + self.assertEqual(b, a) + del b[10:] # delete at end + self.assertEqual(b, a[:10]) + del b[:] # clear + self.assertEqual(len(b), 0) + self.check_obj(b) + + def test_delslice_random(self): + for a in self.randombitarrays(): + la = len(a) + for _ in range(10): + step = self.rndsliceidx(la) or None + s = slice(self.rndsliceidx(la), self.rndsliceidx(la), step) + c = a.copy() + del c[s] + self.check_obj(c) + c_lst = a.tolist() + del c_lst[s] + self.assertEQUAL(c, bitarray(c_lst, endian=c.endian())) + + def test_delslice_range(self): + # tests C function delete_n() + for n in range(500): + a = urandom(n, self.random_endian()) + p = randint(0, n) + m = randint(0, 500) + + b = a.copy() + del b[p:p + m] + self.assertEQUAL(b, a[:p] + a[p + m:]) + self.check_obj(b) + + def test_delslice_range_step(self): + N = 200 + for step in range(-N - 1, N): + if step == 0: + continue + a = urandom(N, self.random_endian()) + lst = a.tolist() + del a[::step] + del lst[::step] + self.assertEqual(a.tolist(), lst) + +# --------------------------------------------------------------------------- + +class MaskedIndexTests(unittest.TestCase, Util): + + def test_get_basic(self): + a = bitarray('1001001') + mask = bitarray('1010111') + self.assertEqual(a[mask], bitarray('10001')) + self.assertRaises(IndexError, a.__getitem__, bitarray('1011')) + + def test_get_random(self): + for a in self.randombitarrays(): + n = len(a) + self.assertEqual(a[a], a.count() * bitarray('1')) + + mask = zeros(n) + self.assertEqual(a[mask], bitarray()) + + mask.setall(1) + self.assertEqual(a[mask], a) + + mask = urandom(n) + res = bitarray(a[i] for i in range(n) if mask[i]) + self.assertEqual(a[mask], res) + + def test_set_basic(self): + a = bitarray('1001001') + mask = bitarray('1010111') + self.assertRaises(NotImplementedError, a.__setitem__, mask, 1) + + def test_del_basic(self): + a = bitarray('1001001') + mask = bitarray('1010111') + del a[mask] + self.assertEqual(a, bitarray('01')) + self.assertRaises(IndexError, a.__delitem__, bitarray('101')) + + def test_del_random(self): + for a in self.randombitarrays(): + n = len(a) + b = a.copy() + # mask has only zeros - nothing will be removed + mask = zeros(n) + del b[mask] + self.assertEqual(b, a) + + b = a.copy() + # mask has only ones - everything will be removed + mask.setall(1) + del b[mask] + self.assertEqual(b, bitarray()) + + b = a.copy() + # mask is bitarray itself - all 1 items are removed - + # only all the 0's remain + del b[b] + self.assertEqual(b, zeros(a.count(0))) + + b = a.copy() + mask = urandom(n) + res = bitarray(a[i] for i in range(n) if not mask[i]) + del b[mask] + self.assertEqual(b, res) + # `del a[mask]` is equivalent to the in-place version of + # selecting the reverse mask `a = a[~mask]` + self.assertEqual(a[~mask], b) + +# --------------------------------------------------------------------------- + +class SequenceIndexTests(unittest.TestCase, Util): + + def test_get_basic(self): + a = bitarray('00110101 00') + self.assertEqual(a[[2, 4, -3, 9]], bitarray('1010')) + self.assertEqual(a[71 * [2, 4, 7]], 71 * bitarray('101')) + self.assertEqual(a[[-1]], bitarray('0')) + self.assertEqual(a[[]], bitarray()) + self.assertRaises(IndexError, a.__getitem__, [1, 10]) + self.assertRaises(IndexError, a.__getitem__, [-11]) + + def test_get_types(self): + a = bitarray('11001101 01') + lst = [1, 3, -2] + for b in [lst, array.array('i', lst)]: + self.assertEqual(a[b], bitarray('100')) + lst[2] += len(a) + self.assertEqual(a[bytearray(lst)], bitarray('100')) + if is_py3k: + self.assertEqual(a[bytes(lst)], bitarray('100')) + + self.assertRaises(TypeError, a.__getitem__, [2, "B"]) + self.assertRaises(TypeError, a.__getitem__, [2, 1.2]) + self.assertRaises(TypeError, a.__getitem__, tuple(lst)) + + def test_get_random(self): + for a in self.randombitarrays(): + n = len(a) + lst = [randrange(n) for _ in range(n // 2)] + b = a[lst] + self.assertEqual(b, bitarray(a[i] for i in lst)) + self.assertEqual(b.endian(), a.endian()) + + def test_set_bool_basic(self): + a = zeros(10) + a[[2, 3, 5, 7]] = 1 + self.assertEqual(a, bitarray('00110101 00')) + a[[]] = 1 + self.assertEqual(a, bitarray('00110101 00')) + a[[-1]] = True + self.assertEqual(a, bitarray('00110101 01')) + a[[3, -1]] = 0 + self.assertEqual(a, bitarray('00100101 00')) + self.assertRaises(IndexError, a.__setitem__, [1, 10], 0) + self.assertRaises(ValueError, a.__setitem__, [1], 2) + self.assertRaises(TypeError, a.__setitem__, [1], "A") + self.assertRaises(TypeError, a.__setitem__, (3, -1)) + self.assertRaises(TypeError, a.__setitem__, a) + + def test_set_bool_random(self): + for a in self.randombitarrays(): + n = len(a) + lst = [randrange(n) for _ in range(n // 2)] + b = a.copy() + for v in 0, 1: + a[lst] = v + for i in lst: + b[i] = v + self.assertEqual(a, b) + + def test_set_bitarray_basic(self): + a = zeros(10) + a[[2, 3, 5, 7]] = bitarray('1101') + self.assertEqual(a, bitarray('00110001 00')) + a[[]] = bitarray() + self.assertEqual(a, bitarray('00110001 00')) + a[[5, -1]] = bitarray('11') + self.assertEqual(a, bitarray('00110101 01')) + self.assertRaises(IndexError, a.__setitem__, [1, 10], bitarray('11')) + self.assertRaises(ValueError, a.__setitem__, [1], bitarray()) + msg = "attempt to assign sequence of size 2 to bitarray of size 3" + self.assertRaisesMessage(ValueError, msg, + a.__setitem__, [1, 2], bitarray('001')) + + def test_set_bitarray_random(self): + for a in self.randombitarrays(): + n = len(a) + lst = [randrange(n) for _ in range(n // 2)] + c = urandom(len(lst)) + b = a.copy() + + a[lst] = c + for i, j in enumerate(lst): + b[j] = c[i] + self.assertEqual(a, b) + + def test_set_bitarray_random_self(self): + for a in self.randombitarrays(): + lst = list(range(len(a))) + shuffle(lst) + b = a.copy() + c = a.copy() + + a[lst] = a + for i, j in enumerate(lst): + b[j] = c[i] + self.assertEqual(a, b) + + def test_del_basic(self): + a = bitarray('00110101 00') + # ^ ^ ^ ^ + del a[[2, 4, 7, 9]] + self.assertEqual(a, bitarray('001100')) + del a[[]] # delete nothing + self.assertEqual(a, bitarray('001100')) + a = bitarray('00110101 00') + del a[71 * [2, 4, 7, 9]] + self.assertEqual(a, bitarray('001100')) + self.assertRaises(IndexError, a.__delitem__, [1, 10]) + self.assertRaises(TypeError, a.__delitem__, (1, 3)) + + def test_delitems_random(self): + for a in self.randombitarrays(): + n = len(a) + lst = [randrange(n) for _ in range(n // 2)] + b = a.copy() + c = a.copy() + del a[lst] + for i in sorted(set(lst), reverse=True): + del b[i] + self.assertEqual(a, b) + + lst = list(range(n)) + shuffle(lst) + del c[lst] + self.assertEqual(len(c), 0) + + def test_type_messages(self): + for item, msg in [ + (tuple([1, 2]), "multiple dimensions not supported"), + (None, "bitarray indices must be integers, slices or " + "sequences, not 'NoneType'"), + (0.12, "bitarray indices must be integers, slices or " + "sequences, not 'float'"), + ]: + a = bitarray('10111') + self.assertRaisesMessage(TypeError, msg, a.__getitem__, item) + self.assertRaisesMessage(TypeError, msg, a.__setitem__, item, 1) + self.assertRaisesMessage(TypeError, msg, a.__delitem__, item) + +# --------------------------------------------------------------------------- + +class MiscTests(unittest.TestCase, Util): + + def test_instancecheck(self): + a = bitarray('011') + self.assertIsInstance(a, bitarray) + self.assertFalse(isinstance(a, str)) + + def test_booleanness(self): + self.assertEqual(bool(bitarray('')), False) + self.assertEqual(bool(bitarray('0')), True) + self.assertEqual(bool(bitarray('1')), True) + + def test_to01(self): + a = bitarray() + self.assertEqual(a.to01(), '') + self.assertIsInstance(a.to01(), str) + + a = bitarray('101') + self.assertEqual(a.to01(), '101') + self.assertIsInstance(a.to01(), str) + + def test_iterate(self): + for lst in self.randomlists(): + acc = [] + for b in bitarray(lst): + acc.append(b) + self.assertEqual(acc, lst) + + def test_iter1(self): + it = iter(bitarray('011')) + self.assertIsType(it, 'bitarrayiterator') + self.assertBitEqual(next(it), 0) + self.assertBitEqual(next(it), 1) + self.assertBitEqual(next(it), 1) + self.assertStopIteration(it) + + def test_iter2(self): + for a in self.randombitarrays(): + aa = a.tolist() + self.assertEqual(list(a), aa) + self.assertEqual(list(iter(a)), aa) + + def test_assignment(self): + a = bitarray('00110111001') + a[1:3] = a[7:9] + a[-1:] = a[:1] + b = bitarray('01010111000') + self.assertEqual(a, b) + + def test_subclassing(self): + class ExaggeratingBitarray(bitarray): + + def __new__(cls, data, offset): + return bitarray.__new__(cls, data) + + def __init__(self, data, offset): + self.offset = offset + + def __getitem__(self, i): + return bitarray.__getitem__(self, i - self.offset) + + for a in self.randombitarrays(): + b = ExaggeratingBitarray(a, 1234) + for i in range(len(a)): + self.assertEqual(a[i], b[i + 1234]) + + def test_endianness1(self): + a = bitarray(endian='little') + a.frombytes(b'\x01') + self.assertEqual(a.to01(), '10000000') + + b = bitarray(endian='little') + b.frombytes(b'\x80') + self.assertEqual(b.to01(), '00000001') + + c = bitarray(endian='big') + c.frombytes(b'\x80') + self.assertEqual(c.to01(), '10000000') + + d = bitarray(endian='big') + d.frombytes(b'\x01') + self.assertEqual(d.to01(), '00000001') + + self.assertEqual(a, c) + self.assertEqual(b, d) + + def test_endianness2(self): + a = zeros(8, endian='little') + a[0] = True + self.assertEqual(a.tobytes(), b'\x01') + a[1] = True + self.assertEqual(a.tobytes(), b'\x03') + a.frombytes(b' ') + self.assertEqual(a.tobytes(), b'\x03 ') + self.assertEqual(a.to01(), '1100000000000100') + + def test_endianness3(self): + a = zeros(8, endian='big') + a[7] = True + self.assertEqual(a.tobytes(), b'\x01') + a[6] = True + self.assertEqual(a.tobytes(), b'\x03') + a.frombytes(b' ') + self.assertEqual(a.tobytes(), b'\x03 ') + self.assertEqual(a.to01(), '0000001100100000') + + def test_endianness4(self): + a = bitarray('00100000', endian='big') + self.assertEqual(a.tobytes(), b' ') + b = bitarray('00000100', endian='little') + self.assertEqual(b.tobytes(), b' ') + self.assertNotEqual(a, b) + + @skipIf(is_pypy) + def test_overflow(self): + a = bitarray(1) + for i in -7, -1, 0, 1: + n = 2 ** 63 + i + self.assertRaises(OverflowError, a.__imul__, n) + self.assertRaises(OverflowError, bitarray, n) + + a = bitarray(2 ** 10) + self.assertRaises(OverflowError, a.__imul__, 2 ** 53) + + if SYSINFO[0] == 8: + return + + a = bitarray(10 ** 6) + self.assertRaises(OverflowError, a.__imul__, 17180) + for i in -7, -1, 0, 1: + self.assertRaises(OverflowError, bitarray, 2 ** 31 + i) + try: + a = bitarray(2 ** 31 - 8); + except MemoryError: + return + self.assertRaises(OverflowError, bitarray.append, a, True) + + def test_unicode_create(self): + a = bitarray(u'') + self.assertEqual(a, bitarray()) + + a = bitarray(u'111001') + self.assertEqual(a, bitarray('111001')) + + def test_unhashable(self): + a = bitarray() + self.assertRaises(TypeError, hash, a) + self.assertRaises(TypeError, dict, [(a, 'foo')]) + + @skipIf(sys.version_info[0] == 2) + def test_abc(self): + from collections import abc + + a = bitarray('001') + self.assertIsInstance(a, abc.Sequence) + self.assertIsInstance(a, abc.MutableSequence) + if sys.platform != "win32": + self.assertFalse(isinstance(a, abc.Hashable)) + +# --------------------------------------------------------------------------- + +class PickleTests(unittest.TestCase, Util): + + def test_attributes(self): + a = frozenbitarray("00110") + # as a is a subclass of bitarray, we can have attributes + a.x = "bar" + a.y = "baz" + + b = pickle.loads(pickle.dumps(a)) + self.assertEqual(b, a) + self.assertEqual(b.x, "bar") + self.assertEqual(b.y, "baz") + + def test_readonly(self): + a = bitarray(buffer=b'A') + # readonly (because buffer is readonly), but not frozenbitarray + self.assertTrue(a.readonly) + self.assertIsType(a, 'bitarray') + + b = pickle.loads(pickle.dumps(a)) + self.assertTrue(b.readonly) + self.assertIsType(b, 'bitarray') + + def test_endian(self): + for endian in 'little', 'big': + a = bitarray(endian=endian) + b = pickle.loads(pickle.dumps(a)) + self.assertEqual(b.endian(), endian) + + def test_reduce_explicit(self): + a = frozenbitarray('11001111 01001', 'little') + a.quux = 12 + res = (_bitarray_reconstructor, + (frozenbitarray, b'\xf3\x12', 'little', 3, 1), + {'quux': 12}) + self.assertEqual(a.__reduce__(), res) + + def check_reduce(self, a): + try: + attrs = a.__dict__ + except AttributeError: + attrs = None + + res = ( + _bitarray_reconstructor, + ( + type(a), # type object + a.tobytes(), # buffer + a.endian(), # endianness + a.padbits, # number of pad bits + int(a.readonly) # readonly + ), + attrs) # __dict__ or None + self.assertEqual(a.__reduce__(), res) + + b = _bitarray_reconstructor(*res[1]) + self.assertEqual(a, b) + self.assertEqual(type(a), type(b)) + self.assertEqual(a.endian(), b.endian()) + self.assertEqual(a.readonly, b.readonly) + self.check_obj(b) + + @skipIf(is_pypy) + def test_reduce_random(self): + for a in self.randombitarrays(): + self.check_reduce(a) + b = frozenbitarray(a) + self.check_reduce(b) + b.foo = 42 + self.check_reduce(b) + + def test_reconstructor_explicit(self): + a = _bitarray_reconstructor(bitarray, b'', 'little', 0, 0) + self.assertEqual(len(a), 0) + self.assertEqual(a.endian(), 'little') + self.check_obj(a) + + a = _bitarray_reconstructor(bitarray, b'\x0f', 'big', 1, 0) + self.assertEqual(a, bitarray("0000111")) + self.assertEqual(a.endian(), 'big') + self.check_obj(a) + + def test_reconstructor_invalid_args(self): + # argument 1 - type object + self.assertRaisesMessage( + TypeError, "first argument must be a type object, got 'str'", + _bitarray_reconstructor, "foo", b'', 'big', 0, 0) + + self.assertRaisesMessage( + TypeError, "'list' is not a subtype of bitarray", + _bitarray_reconstructor, list, b'', 'big', 0, 0) + + # argument 2 - buffer + self.assertRaisesMessage( + TypeError, "second argument must be bytes, got 'int'", + _bitarray_reconstructor, bitarray, 123, 'big', 0, 0) + + # argument 3 - bit-endianness + self.assertRaises(TypeError, _bitarray_reconstructor, + bitarray, b'\x0f', 123, 1, 0) + self.assertRaisesMessage( + ValueError, + "bit-endianness must be either 'little' or 'big', not 'small'", + _bitarray_reconstructor, bitarray, b"", "small", 0, 0) + + # argument 4 - number of pad bits + self.assertRaises(TypeError, _bitarray_reconstructor, + bitarray, b'\x0f', 'big', 0.0, 0) + self.assertRaisesMessage( + ValueError, "invalid number of padbits: 8", + _bitarray_reconstructor, bitarray, b"A", "big", 8, 0) + self.assertRaisesMessage( + # the number of bytes is 0 zero, so padbits cannot be 1 + ValueError, "invalid number of padbits: 1", + _bitarray_reconstructor, bitarray, b"", "big", 1, 0) + + # argument 5 - readonly + self.assertRaises(TypeError, _bitarray_reconstructor, + bitarray, b'\x0f', 'big', 1, 'foo') + + def check_file(self, fn): + path = os.path.join(os.path.dirname(__file__), fn) + with open(path, 'rb') as fi: + d = pickle.load(fi) + + for i, (s, end) in enumerate([ + # 0x03 + ('110', 'little'), + # 0x60 + ('011', 'big'), + # 0x07 0x12 0x00 0x40 + ('1110000001001000000000000000001', 'little'), + # 0x27 0x80 0x00 0x02 + ('0010011110000000000000000000001', 'big'), + ]): + b = d['b%d' % i] + self.assertEqual(b.to01(), s) + self.assertEqual(b.endian(), end) + self.assertIsType(b, 'bitarray') + self.assertFalse(b.readonly) + self.check_obj(b) + + f = d['f%d' % i] + self.assertEqual(f.to01(), s) + self.assertEqual(f.endian(), end) + self.assertIsType(f, 'frozenbitarray') + self.assertTrue(f.readonly) + self.check_obj(f) + + @skipIf(sys.version_info[0] == 2) + def test_load(self): + # test data file was created using bitarray 1.5.0 / Python 3.5.5 + self.check_file('test_150.pickle') + # using bitarray 2.8.1 / Python 3.5.5 (_bitarray_reconstructor) + self.check_file('test_281.pickle') + + def test_random(self): + for a in self.randombitarrays(): + b = pickle.loads(pickle.dumps(a)) + self.assertFalse(b.readonly) + self.assertFalse(b is a) + self.assertEQUAL(a, b) + self.check_obj(b) + +# --------------------------------------------------------------------------- + +class RichCompareTests(unittest.TestCase, Util): + + def test_wrong_types(self): + a = bitarray() + for x in None, 7, 'A': + self.assertEqual(a.__eq__(x), NotImplemented) + self.assertEqual(a.__ne__(x), NotImplemented) + self.assertEqual(a.__ge__(x), NotImplemented) + self.assertEqual(a.__gt__(x), NotImplemented) + self.assertEqual(a.__le__(x), NotImplemented) + self.assertEqual(a.__lt__(x), NotImplemented) + + def test_explicit(self): + for sa, sb, res in [ + ('', '', '101010'), + ('0', '0', '101010'), + ('1', '1', '101010'), + ('0', '', '011100'), + ('1', '', '011100'), + ('1', '0', '011100'), + ('11', '10', '011100'), + ('01', '00', '011100'), + ('0', '1', '010011'), + ('', '0', '010011'), + ('', '1', '010011'), + ]: + a = bitarray(sa, self.random_endian()) + b = bitarray(sb, self.random_endian()) + self.assertEqual(a == b, int(res[0])) + self.assertEqual(a != b, int(res[1])) + self.assertEqual(a >= b, int(res[2])) + self.assertEqual(a > b, int(res[3])) + self.assertEqual(a <= b, int(res[4])) + self.assertEqual(a < b, int(res[5])) + + def test_eq_ne(self): + for _ in range(10): + self.assertTrue(bitarray(0, self.random_endian()) == + bitarray(0, self.random_endian())) + self.assertFalse(bitarray(0, self.random_endian()) != + bitarray(0, self.random_endian())) + + for n in range(1, 20): + a = ones(n, self.random_endian()) + b = bitarray(a, self.random_endian()) + self.assertTrue(a == b) + self.assertFalse(a != b) + b[n - 1] = 0 + self.assertTrue(a != b) + self.assertFalse(a == b) + + def test_eq_ne_random(self): + for a in self.randombitarrays(start=1): + b = bitarray(a, self.random_endian()) + self.assertTrue(a == b) + self.assertFalse(a != b) + b.invert(randrange(len(a))) + self.assertTrue(a != b) + self.assertFalse(a == b) + + def check(self, a, b, c, d): + self.assertEqual(a == b, c == d) + self.assertEqual(a != b, c != d) + self.assertEqual(a <= b, c <= d) + self.assertEqual(a < b, c < d) + self.assertEqual(a >= b, c >= d) + self.assertEqual(a > b, c > d) + + def test_invert_random_element(self): + for a in self.randombitarrays(start=1): + n = len(a) + b = bitarray(a, self.random_endian()) + i = randrange(n) + b.invert(i) + self.check(a, b, a[i], b[i]) + + def test_size(self): + for _ in range(100): + a = zeros(randint(1, 20), self.random_endian()) + b = zeros(randint(1, 20), self.random_endian()) + self.check(a, b, len(a), len(b)) + + def test_random(self): + for a in self.randombitarrays(): + aa = a.tolist() + if getrandbits(1): + a = frozenbitarray(a) + for b in self.randombitarrays(): + bb = b.tolist() + if getrandbits(1): + b = frozenbitarray(b) + self.check(a, b, aa, bb) + self.check(a, b, aa, bb) + +# --------------------------------------------------------------------------- + +class SpecialMethodTests(unittest.TestCase, Util): + + def test_all(self): + a = bitarray() + self.assertTrue(a.all()) + for s, r in ('0', False), ('1', True), ('01', False): + self.assertTrue(bitarray(s).all() is r) + + for a in self.randombitarrays(): + self.assertTrue(a.all() is all(a)) + + N = randint(1000, 2000) + a = ones(N) + self.assertTrue(a.all()) + a[N - 1] = 0 + self.assertFalse(a.all()) + + def test_any(self): + a = bitarray() + self.assertFalse(a.any()) + for s, r in ('0', False), ('1', True), ('01', True): + self.assertTrue(bitarray(s).any() is r) + + for a in self.randombitarrays(): + self.assertTrue(a.any() is any(a)) + + N = randint(1000, 2000) + a = zeros(N) + self.assertFalse(a.any()) + a[N - 1] = 1 + self.assertTrue(a.any()) + + def test_repr(self): + r = repr(bitarray()) + self.assertEqual(r, "bitarray()") + self.assertIsInstance(r, str) + + r = repr(bitarray('10111')) + self.assertEqual(r, "bitarray('10111')") + self.assertIsInstance(r, str) + + for a in self.randombitarrays(): + b = eval(repr(a)) + self.assertFalse(b is a) + self.assertEqual(a, b) + self.check_obj(b) + + def test_copy(self): + for a in self.randombitarrays(): + b = a.copy() + self.assertFalse(b is a) + self.assertEQUAL(a, b) + + b = copy.copy(a) + self.assertFalse(b is a) + self.assertEQUAL(a, b) + + b = copy.deepcopy(a) + self.assertFalse(b is a) + self.assertEQUAL(a, b) + + def assertReallyEqual(self, a, b): + # assertEqual first, because it will have a good message if the + # assertion fails. + self.assertEqual(a, b) + self.assertEqual(b, a) + self.assertTrue(a == b) + self.assertTrue(b == a) + self.assertFalse(a != b) + self.assertFalse(b != a) + if not is_py3k: + self.assertEqual(0, cmp(a, b)) + self.assertEqual(0, cmp(b, a)) + + def assertReallyNotEqual(self, a, b): + # assertNotEqual first, because it will have a good message if the + # assertion fails. + self.assertNotEqual(a, b) + self.assertNotEqual(b, a) + self.assertFalse(a == b) + self.assertFalse(b == a) + self.assertTrue(a != b) + self.assertTrue(b != a) + if not is_py3k: + self.assertNotEqual(0, cmp(a, b)) + self.assertNotEqual(0, cmp(b, a)) + + def test_equality(self): + self.assertReallyEqual(bitarray(''), bitarray('')) + self.assertReallyEqual(bitarray('0'), bitarray('0')) + self.assertReallyEqual(bitarray('1'), bitarray('1')) + + def test_not_equality(self): + self.assertReallyNotEqual(bitarray(''), bitarray('1')) + self.assertReallyNotEqual(bitarray(''), bitarray('0')) + self.assertReallyNotEqual(bitarray('0'), bitarray('1')) + + def test_equality_random(self): + for a in self.randombitarrays(start=1): + b = a.copy() + self.assertReallyEqual(a, b) + n = len(a) + b.invert(n - 1) # flip last bit + self.assertReallyNotEqual(a, b) + + @skipIf(is_pypy) + def test_sizeof(self): + a = bitarray() + size = sys.getsizeof(a) + self.assertEqual(size, a.__sizeof__()) + self.assertIsInstance(size, int if is_py3k else (int, long)) + self.assertTrue(size < 200) + a = bitarray(8000) + self.assertTrue(sys.getsizeof(a) > 1000) + +# --------------------------------------------------------------------------- + +class SequenceMethodsTests(unittest.TestCase, Util): + + def test_concat(self): + a = bitarray('001') + b = a + bitarray('110') + self.assertEQUAL(b, bitarray('001110')) + b = a + [0, 1, True] + self.assertEQUAL(b, bitarray('001011')) + b = a + '100' + self.assertEQUAL(b, bitarray('001100')) + b = a + (1, 0, True) + self.assertEQUAL(b, bitarray('001101')) + self.assertRaises(ValueError, a.__add__, (0, 1, 2)) + self.assertEQUAL(a, bitarray('001')) + + self.assertRaises(TypeError, a.__add__, 42) + if is_py3k: + self.assertRaises(TypeError, a.__add__, b'1101') + else: + self.assertEqual(a + b'10', bitarray('00110')) + + for a in self.randombitarrays(): + aa = a.copy() + for b in self.randombitarrays(): + bb = b.copy() + c = a + b + self.assertEqual(c, bitarray(a.tolist() + b.tolist())) + self.assertEqual(c.endian(), a.endian()) + self.check_obj(c) + + self.assertEQUAL(a, aa) + self.assertEQUAL(b, bb) + + def test_inplace_concat(self): + a = bitarray('001') + a += bitarray('110') + self.assertEqual(a, bitarray('001110')) + a += [0, 1, True] + self.assertEqual(a, bitarray('001110011')) + a += '100' + self.assertEqual(a, bitarray('001110011100')) + a += (1, 0, True) + self.assertEqual(a, bitarray('001110011100101')) + + a = bitarray('110') + self.assertRaises(ValueError, a.__iadd__, [0, 1, 2]) + self.assertEqual(a, bitarray('110')) + + self.assertRaises(TypeError, a.__iadd__, 42) + b = b'101' + if is_py3k: + self.assertRaises(TypeError, a.__iadd__, b) + else: + a += b + self.assertEqual(a, bitarray('110101')) + + for a in self.randombitarrays(): + for b in self.randombitarrays(): + c = bitarray(a) + d = c + d += b + self.assertEqual(d, a + b) + self.assertTrue(c is d) + self.assertEQUAL(c, d) + self.assertEqual(d.endian(), a.endian()) + self.check_obj(d) + + def test_repeat_explicit(self): + for m, s, r in [ + ( 0, '', ''), + ( 0, '1001111', ''), + (-1, '100110', ''), + (11, '', ''), + ( 1, '110', '110'), + ( 2, '01', '0101'), + ( 5, '1', '11111'), + ]: + a = bitarray(s) + self.assertEqual(a * m, bitarray(r)) + self.assertEqual(m * a, bitarray(r)) + c = a.copy() + c *= m + self.assertEqual(c, bitarray(r)) + + def test_repeat_wrong_args(self): + a = bitarray() + self.assertRaises(TypeError, a.__mul__, None) + self.assertRaises(TypeError, a.__mul__, 2.0) + self.assertRaises(TypeError, a.__imul__, None) + self.assertRaises(TypeError, a.__imul__, 3.0) + + def test_repeat_random(self): + for a in self.randombitarrays(): + b = a.copy() + for m in list(range(-3, 5)) + [randint(100, 200)]: + res = bitarray(m * a.to01(), endian=a.endian()) + self.assertEqual(len(res), len(a) * max(0, m)) + + c = a * m + self.assertEQUAL(c, res) + c = m * a + self.assertEQUAL(c, res) + + c = a.copy() + c *= m + self.assertEQUAL(c, res) + self.check_obj(c) + + self.assertEQUAL(a, b) + + def test_contains_simple(self): + a = bitarray() + self.assertFalse(False in a) + self.assertFalse(True in a) + self.assertTrue(bitarray() in a) + a.append(True) + self.assertTrue(True in a) + self.assertFalse(False in a) + a = bitarray([False]) + self.assertTrue(False in a) + self.assertFalse(True in a) + a.append(True) + self.assertTrue(0 in a) + self.assertTrue(1 in a) + if not is_py3k: + self.assertTrue(long(0) in a) + self.assertTrue(long(1) in a) + + def test_contains_errors(self): + a = bitarray() + self.assertEqual(a.__contains__(1), False) + a.append(1) + self.assertEqual(a.__contains__(1), True) + a = bitarray('0011') + self.assertEqual(a.__contains__(bitarray('01')), True) + self.assertEqual(a.__contains__(bitarray('10')), False) + self.assertRaises(TypeError, a.__contains__, 'asdf') + self.assertRaises(ValueError, a.__contains__, 2) + self.assertRaises(ValueError, a.__contains__, -1) + if not is_py3k: + self.assertRaises(ValueError, a.__contains__, long(2)) + + def test_contains_range(self): + for n in range(2, 50): + a = zeros(n) + self.assertTrue(False in a) + self.assertFalse(True in a) + a[randrange(n)] = 1 + self.assertTrue(True in a) + self.assertTrue(False in a) + a.setall(1) + self.assertTrue(True in a) + self.assertFalse(False in a) + a[randrange(n)] = 0 + self.assertTrue(True in a) + self.assertTrue(False in a) + + def test_contains_explicit(self): + a = bitarray('011010000001') + for s, r in [('', True), # every bitarray contains an empty one + ('1', True), ('11', True), ('111', False), + ('011', True), ('0001', True), ('00011', False)]: + self.assertEqual(bitarray(s) in a, r) + +# --------------------------------------------------------------------------- + +class NumberTests(unittest.TestCase, Util): + + def test_misc(self): + for a in self.randombitarrays(): + b = ~a + c = a & b + self.assertEqual(c.any(), False) + self.assertEqual(a, a ^ c) + d = a ^ b + self.assertEqual(d.all(), True) + b &= d + self.assertEqual(~b, a) + + def test_bool(self): + a = bitarray() + self.assertTrue(bool(a) is False) + a.append(0) + self.assertTrue(bool(a) is True) + a.append(1) + self.assertTrue(bool(a) is True) + + def test_size_error(self): + a = bitarray('11001') + b = bitarray('100111') + self.assertRaises(ValueError, lambda: a & b) + self.assertRaises(ValueError, lambda: a | b) + self.assertRaises(ValueError, lambda: a ^ b) + for x in (a.__and__, a.__or__, a.__xor__, + a.__iand__, a.__ior__, a.__ixor__): + self.assertRaises(ValueError, x, b) + + def test_endianness_error(self): + a = bitarray('11001', 'big') + b = bitarray('10011', 'little') + self.assertRaises(ValueError, lambda: a & b) + self.assertRaises(ValueError, lambda: a | b) + self.assertRaises(ValueError, lambda: a ^ b) + for x in (a.__and__, a.__or__, a.__xor__, + a.__iand__, a.__ior__, a.__ixor__): + self.assertRaises(ValueError, x, b) + + def test_and(self): + a = bitarray('11001') + b = bitarray('10011') + c = a & b + self.assertEqual(c, bitarray('10001')) + self.check_obj(c) + + self.assertRaises(TypeError, lambda: a & 1) + self.assertRaises(TypeError, lambda: 1 & a) + self.assertEqual(a, bitarray('11001')) + self.assertEqual(b, bitarray('10011')) + + def test_or(self): + a = bitarray('11001') + b = bitarray('10011') + c = a | b + self.assertEqual(c, bitarray('11011')) + self.check_obj(c) + + self.assertRaises(TypeError, lambda: a | 1) + self.assertRaises(TypeError, lambda: 1 | a) + self.assertEqual(a, bitarray('11001')) + self.assertEqual(b, bitarray('10011')) + + def test_xor(self): + a = bitarray('11001') + b = bitarray('10011') + c = a ^ b + self.assertEQUAL(c, bitarray('01010')) + self.check_obj(c) + + self.assertRaises(TypeError, lambda: a ^ 1) + self.assertRaises(TypeError, lambda: 1 ^ a) + self.assertEqual(a, bitarray('11001')) + self.assertEqual(b, bitarray('10011')) + + def test_iand(self): + a = bitarray('110010110') + b = bitarray('100110011') + a &= b + self.assertEqual(a, bitarray('100010010')) + self.assertEqual(b, bitarray('100110011')) + self.check_obj(a) + self.check_obj(b) + try: + a &= 1 + except TypeError: + error = 1 + self.assertEqual(error, 1) + + def test_ior(self): + a = bitarray('110010110') + b = bitarray('100110011') + a |= b + self.assertEQUAL(a, bitarray('110110111')) + self.assertEQUAL(b, bitarray('100110011')) + try: + a |= 1 + except TypeError: + error = 1 + self.assertEqual(error, 1) + + def test_ixor(self): + a = bitarray('110010110') + b = bitarray('100110011') + a ^= b + self.assertEQUAL(a, bitarray('010100101')) + self.assertEQUAL(b, bitarray('100110011')) + try: + a ^= 1 + except TypeError: + error = 1 + self.assertEqual(error, 1) + + def test_bitwise_self(self): + for a in self.randombitarrays(): + aa = a.copy() + self.assertEQUAL(a & a, aa) + self.assertEQUAL(a | a, aa) + self.assertEQUAL(a ^ a, zeros(len(aa), aa.endian())) + self.assertEQUAL(a, aa) + + def test_bitwise_inplace_self(self): + for a in self.randombitarrays(): + aa = a.copy() + a &= a + self.assertEQUAL(a, aa) + a |= a + self.assertEQUAL(a, aa) + a ^= a + self.assertEqual(a, zeros(len(aa), aa.endian())) + + def test_invert(self): + a = bitarray('11011') + b = ~a + self.assertEQUAL(b, bitarray('00100')) + self.assertEQUAL(a, bitarray('11011')) + self.assertFalse(a is b) + self.check_obj(b) + + for a in self.randombitarrays(): + b = bitarray(a) + b.invert() + for i in range(len(a)): + self.assertEqual(b[i], not a[i]) + self.check_obj(b) + self.assertEQUAL(~a, b) + + @staticmethod + def shift(a, n, direction): + if n >= len(a): + return zeros(len(a), a.endian()) + + if direction == 'right': + return zeros(n, a.endian()) + a[:len(a)-n] + elif direction == 'left': + return a[n:] + zeros(n, a.endian()) + else: + raise ValueError("invalid direction: %s" % direction) + + def test_lshift(self): + a = bitarray('11011') + b = a << 2 + self.assertEQUAL(b, bitarray('01100')) + self.assertRaises(TypeError, lambda: a << 1.2) + self.assertRaises(TypeError, a.__lshift__, 1.2) + self.assertRaises(ValueError, lambda: a << -1) + self.assertRaises(OverflowError, a.__lshift__, 2 ** 63) + + for a in self.randombitarrays(): + c = a.copy() + n = randrange(len(a) + 4) + b = a << n + self.assertEqual(len(b), len(a)) + self.assertEQUAL(b, self.shift(a, n, 'left')) + self.assertEQUAL(a, c) + + def test_rshift(self): + a = bitarray('1101101') + b = a >> 1 + self.assertEQUAL(b, bitarray('0110110')) + self.assertRaises(TypeError, lambda: a >> 1.2) + self.assertRaises(TypeError, a.__rshift__, 1.2) + self.assertRaises(ValueError, lambda: a >> -1) + + for a in self.randombitarrays(): + c = a.copy() + n = randrange(len(a) + 4) + b = a >> n + self.assertEqual(len(b), len(a)) + self.assertEQUAL(b, self.shift(a, n, 'right')) + self.assertEQUAL(a, c) + + def test_ilshift(self): + a = bitarray('110110101') + a <<= 7 + self.assertEQUAL(a, bitarray('010000000')) + self.assertRaises(TypeError, a.__ilshift__, 1.2) + self.assertRaises(ValueError, a.__ilshift__, -3) + + for a in self.randombitarrays(): + b = a.copy() + n = randrange(len(a) + 4) + b <<= n + self.assertEqual(len(b), len(a)) + self.assertEQUAL(b, self.shift(a, n, 'left')) + + def test_irshift(self): + a = bitarray('110110111') + a >>= 3 + self.assertEQUAL(a, bitarray('000110110')) + self.assertRaises(TypeError, a.__irshift__, 1.2) + self.assertRaises(ValueError, a.__irshift__, -4) + + for a in self.randombitarrays(): + b = a.copy() + n = randrange(len(a) + 4) + b >>= n + self.assertEqual(len(b), len(a)) + self.assertEQUAL(b, self.shift(a, n, 'right')) + + def check_random(self, n, endian, n_shift, direction): + a = urandom(n, endian) + self.assertEqual(len(a), n) + + b = a.copy() + if direction == 'left': + b <<= n_shift + else: + b >>= n_shift + self.assertEQUAL(b, self.shift(a, n_shift, direction)) + + def test_shift_range(self): + for endian in 'little', 'big': + for direction in 'left', 'right': + for n in range(0, 200): + self.check_random(n, endian, 1, direction) + self.check_random(n, endian, randint(0, n), direction) + for n_shift in range(0, 100): + self.check_random(100, endian, n_shift, direction) + + def test_zero_shift(self): + for a in self.randombitarrays(): + aa = a.copy() + self.assertEQUAL(a << 0, aa) + self.assertEQUAL(a >> 0, aa) + a <<= 0 + self.assertEQUAL(a, aa) + a >>= 0 + self.assertEQUAL(a, aa) + + def test_len_or_larger_shift(self): + # ensure shifts with len(a) (or larger) result in all zero bitarrays + for a in self.randombitarrays(): + c = a.copy() + z = zeros(len(a), a.endian()) + n = randint(len(a), len(a) + 10) + self.assertEQUAL(a << n, z) + self.assertEQUAL(a >> n, z) + self.assertEQUAL(a, c) + a <<= n + self.assertEQUAL(a, z) + a = bitarray(c) + a >>= n + self.assertEQUAL(a, z) + + def test_shift_example(self): + a = bitarray('0010011') + self.assertEqual(a << 3, bitarray('0011000')) + a >>= 4 + self.assertEqual(a, bitarray('0000001')) + + def test_frozenbitarray(self): + a = frozenbitarray('0010011') + self.assertEqual(a << 3, bitarray('0011000')) + self.assertRaises(TypeError, a.__ilshift__, 4) + +# --------------------------------------------------------------------------- + +class ExtendTests(unittest.TestCase, Util): + + def test_wrongArgs(self): + a = bitarray() + self.assertRaises(TypeError, a.extend) + self.assertRaises(TypeError, a.extend, None) + self.assertRaises(TypeError, a.extend, True) + self.assertRaises(TypeError, a.extend, 24) + self.assertRaises(TypeError, a.extend, 1.0) + + def test_bitarray(self): + a = bitarray() + a.extend(bitarray()) + self.assertEqual(a, bitarray()) + a.extend(bitarray('110')) + self.assertEqual(a, bitarray('110')) + a.extend(bitarray('1110')) + self.assertEqual(a, bitarray('1101110')) + + a = bitarray('00001111', endian='little') + a.extend(bitarray('00100111', endian='big')) + self.assertEqual(a, bitarray('00001111 00100111')) + + def test_bitarray_random(self): + for a in self.randombitarrays(): + sa = a.to01() + for b in self.randombitarrays(): + bb = b.copy() + c = bitarray(a) + c.extend(b) + self.assertEqual(c.to01(), sa + bb.to01()) + self.assertEqual(c.endian(), a.endian()) + self.assertEqual(len(c), len(a) + len(b)) + self.check_obj(c) + # ensure b hasn't changed + self.assertEQUAL(b, bb) + self.check_obj(b) + + def test_list(self): + a = bitarray() + a.extend([]) + self.assertEqual(a, bitarray()) + a.extend([0, 1, True, False]) + self.assertEqual(a, bitarray('0110')) + self.assertRaises(ValueError, a.extend, [0, 1, 2]) + self.assertRaises(TypeError, a.extend, [0, 1, 'a']) + self.assertEqual(a, bitarray('0110')) + + for a in self.randomlists(): + for b in self.randomlists(): + c = bitarray(a) + c.extend(b) + self.assertEqual(c.tolist(), a + b) + self.check_obj(c) + + def test_tuple(self): + a = bitarray() + a.extend(tuple()) + self.assertEqual(a, bitarray()) + a.extend((0, 1, True, 0, False)) + self.assertEqual(a, bitarray('01100')) + self.assertRaises(ValueError, a.extend, (0, 1, 2)) + self.assertRaises(TypeError, a.extend, (0, 1, 'a')) + self.assertEqual(a, bitarray('01100')) + + for a in self.randomlists(): + for b in self.randomlists(): + c = bitarray(a) + c.extend(tuple(b)) + self.assertEqual(c.tolist(), a + b) + self.check_obj(c) + + def test_generator_1(self): + def gen(lst): + for x in lst: + yield x + a = bitarray('0011') + a.extend(gen([0, 1, False, True, 0])) + self.assertEqual(a, bitarray('0011 01010')) + self.assertRaises(ValueError, a.extend, gen([0, 1, 2])) + self.assertRaises(TypeError, a.extend, gen([1, 0, None])) + self.assertEqual(a, bitarray('0011 01010')) + + a = bytearray() + a.extend(gen([0, 1, 255])) + self.assertEqual(a, b'\x00\x01\xff') + self.assertRaises(ValueError, a.extend, gen([0, 1, 256])) + self.assertRaises(TypeError, a.extend, gen([1, 0, None])) + self.assertEqual(a, b'\x00\x01\xff') + + for a in self.randomlists(): + def foo(): + for e in a: + yield e + b = bitarray() + b.extend(foo()) + self.assertEqual(b.tolist(), a) + self.check_obj(b) + + def test_generator_2(self): + def gen(): + for i in range(10): + if i == 4: + raise KeyError + yield i % 2 + + a = bitarray() + self.assertRaises(KeyError, a.extend, gen()) + self.assertEqual(a, bitarray('0101')) + a = [] + self.assertRaises(KeyError, a.extend, gen()) + self.assertEqual(a, [0, 1, 0, 1]) + + def test_iterator_1(self): + a = bitarray() + a.extend(iter([])) + self.assertEqual(a, bitarray()) + a.extend(iter([1, 1, 0, True, False])) + self.assertEqual(a, bitarray('11010')) + self.assertRaises(ValueError, a.extend, iter([1, 1, 0, 0, 2])) + self.assertEqual(a, bitarray('11010')) + + for a in self.randomlists(): + for b in self.randomlists(): + c = bitarray(a) + c.extend(iter(b)) + self.assertEqual(c.tolist(), a + b) + self.check_obj(c) + + def test_iterator_2(self): + a = bitarray() + a.extend(itertools.repeat(True, 23)) + self.assertEqual(a, bitarray(23 * '1')) + self.check_obj(a) + + def test_iterator_change(self): + a = bitarray(1000) + c = 0 + for i, x in enumerate(a): + if i == 10: + a.clear() + c += 1 + self.assertEqual(c, 11) + self.check_obj(a) + + def test_string01(self): + a = bitarray() + a.extend(str()) + a.extend('') + self.assertEqual(a, bitarray()) + a.extend('0110111') + self.assertEqual(a, bitarray('0110111')) + self.assertRaises(ValueError, a.extend, '0011201') + # ensure no bits got added after error was raised + self.assertEqual(a, bitarray('0110111')) + + a = bitarray() + self.assertRaises(ValueError, a.extend, 1000 * '01' + '.') + self.assertEqual(a, bitarray()) + + for a in self.randomlists(): + for b in self.randomlists(): + c = bitarray(a) + c.extend(''.join(['0', '1'][x] for x in b)) + self.assertEqual(c, bitarray(a + b)) + self.check_obj(c) + + def test_string01_whitespace(self): + a = bitarray() + a.extend(WHITESPACE) + self.assertEqual(len(a), 0) + a.extend('0 1\n0\r1\t0\v1_') + self.assertEqual(a, bitarray('010101')) + a += '_ 1\n0\r1\t0\v' + self.assertEqual(a, bitarray('010101 1010')) + self.check_obj(a) + + def test_unicode(self): + a = bitarray() + a.extend(u'') + self.assertEqual(a, bitarray()) + self.assertRaises(ValueError, a.extend, u'0011201') + # ensure no bits got added after error was raised + self.assertEqual(a, bitarray()) + self.check_obj(a) + + a = bitarray() + a.extend(u'001 011_') + self.assertEqual(a, bitarray('001011')) + self.assertRaises(UnicodeEncodeError, a.extend, u'1\u2605 0') + self.assertEqual(a, bitarray('001011')) + self.check_obj(a) + + def test_bytes(self): + a = bitarray() + b = b'10110' + if is_py3k: + self.assertRaises(TypeError, a.extend, b) + else: + a.extend(b) + self.assertEqual(a, bitarray('10110')) + self.check_obj(a) + + def test_self(self): + for s in '', '1', '110', '00110111': + a = bitarray(s) + a.extend(a) + self.assertEqual(a, bitarray(2 * s)) + + for a in self.randombitarrays(): + endian = a.endian() + s = a.to01() + a.extend(a) + self.assertEqual(a.to01(), 2 * s) + self.assertEqual(a.endian(), endian) + self.assertEqual(len(a), 2 * len(s)) + self.check_obj(a) + +# --------------------------------------------------------------------------- + +class MethodTests(unittest.TestCase, Util): + + def test_append_simple(self): + a = bitarray() + a.append(True) + a.append(False) + a.append(False) + self.assertEQUAL(a, bitarray('100')) + a.append(0) + a.append(1) + self.assertEQUAL(a, bitarray('10001')) + self.assertRaises(ValueError, a.append, 2) + self.assertRaises(TypeError, a.append, None) + self.assertRaises(TypeError, a.append, '') + self.assertEQUAL(a, bitarray('10001')) + self.check_obj(a) + + def test_append_random(self): + for a in self.randombitarrays(): + aa = a.tolist() + a.append(1) + self.assertEQUAL(a, bitarray(aa + [1], endian=a.endian())) + a.append(0) + self.assertEQUAL(a, bitarray(aa + [1, 0], endian=a.endian())) + self.check_obj(a) + + def test_insert(self): + a = bitarray('111100') + a.insert(3, False) + self.assertEqual(a, bitarray('1110100')) + self.assertRaises(ValueError, a.insert, 0, 2) + self.assertRaises(TypeError, a.insert, 0, None) + self.assertRaises(TypeError, a.insert) + self.assertRaises(TypeError, a.insert, None) + self.assertEqual(a, bitarray('1110100')) + self.check_obj(a) + + def test_insert_random(self): + for a in self.randombitarrays(): + aa = a.tolist() + for _ in range(20): + item = getrandbits(1) + pos = randint(-len(a) - 2, len(a) + 2) + a.insert(pos, item) + aa.insert(pos, item) + self.assertEqual(a.tolist(), aa) + self.check_obj(a) + + def test_fill_simple(self): + for endian in 'little', 'big': + a = bitarray(endian=endian) + self.assertEqual(a.fill(), 0) + self.assertEqual(len(a), 0) + + a = bitarray('101', endian) + self.assertEqual(a.fill(), 5) + self.assertEqual(a, bitarray('10100000')) + self.assertEqual(a.fill(), 0) + self.assertEqual(a, bitarray('10100000')) + self.check_obj(a) + + def test_fill_exported(self): + a = bitarray('11101') + b = bitarray(buffer=a) + v = memoryview(a) + self.assertEqual(a.fill(), 3) + self.assertEqual(a, b) + if is_py3k: + self.assertEqual(v.nbytes, 1) + + def test_fill_random(self): + for a in self.randombitarrays(): + b = a.copy() + res = b.fill() + self.assertTrue(0 <= res < 8) + self.assertEqual(b.endian(), a.endian()) + self.check_obj(b) + if len(a) % 8 == 0: + self.assertEqual(b, a) + else: + self.assertEqual(len(b) % 8, 0) + self.assertNotEqual(b, a) + self.assertEqual(b[:len(a)], a) + self.assertEqual(b[len(a):], zeros(len(b) - len(a))) + + def test_invert_simple(self): + a = bitarray() + a.invert() + self.assertEQUAL(a, bitarray()) + self.check_obj(a) + + a = bitarray('11011') + a.invert() + self.assertEQUAL(a, bitarray('00100')) + a.invert(2) + self.assertEQUAL(a, bitarray('00000')) + a.invert(-1) + self.assertEQUAL(a, bitarray('00001')) + + def test_invert_errors(self): + a = bitarray(5) + self.assertRaises(IndexError, a.invert, 5) + self.assertRaises(IndexError, a.invert, -6) + self.assertRaises(TypeError, a.invert, "A") + self.assertRaises(TypeError, a.invert, 0, 1) + + def test_invert_random(self): + for a in self.randombitarrays(start=1): + b = a.copy() + i = randrange(len(a)) + b.invert(i) + a[i] = not a[i] + self.assertEQUAL(a, b) + + def test_sort_simple(self): + a = bitarray('1101000') + a.sort() + self.assertEqual(a, bitarray('0000111')) + self.check_obj(a) + + a = bitarray('1101000') + a.sort(reverse=True) + self.assertEqual(a, bitarray('1110000')) + a.sort(reverse=False) + self.assertEqual(a, bitarray('0000111')) + a.sort(True) + self.assertEqual(a, bitarray('1110000')) + a.sort(False) + self.assertEqual(a, bitarray('0000111')) + + self.assertRaises(TypeError, a.sort, 'A') + + def test_sort_random(self): + for rev in False, True, 0, 1, 7, -1, -7, None: + for a in self.randombitarrays(): + lst = a.tolist() + if rev is None: + lst.sort() + a.sort() + else: + lst.sort(reverse=rev) + a.sort(reverse=rev) + self.assertEqual(a, bitarray(lst)) + self.check_obj(a) + + def test_reverse_explicit(self): + for x, y in [('', ''), ('1', '1'), ('10', '01'), ('001', '100'), + ('1110', '0111'), ('11100', '00111'), + ('011000', '000110'), ('1101100', '0011011'), + ('11110000', '00001111'), + ('11111000011', '11000011111'), + ('11011111 00100000 000111', + '111000 00000100 11111011')]: + a = bitarray(x) + a.reverse() + self.assertEQUAL(a, bitarray(y)) + self.check_obj(a) + + self.assertRaises(TypeError, bitarray().reverse, 42) + + def test_reverse_random(self): + for a in self.randombitarrays(): + b = a.copy() + a.reverse() + self.assertEqual(a.tolist(), b.tolist()[::-1]) + self.assertEQUAL(a, bitarray(reversed(b), endian=a.endian())) + self.assertEQUAL(a, b[::-1]) + self.check_obj(a) + + def test_tolist(self): + a = bitarray() + self.assertEqual(a.tolist(), []) + + a = bitarray('110') + lst = a.tolist() + self.assertIsInstance(lst, list) + self.assertEqual(repr(lst), '[1, 1, 0]') + + for lst in self.randomlists(): + a = bitarray(lst) + self.assertEqual(a.tolist(), lst) + + def test_remove(self): + a = bitarray('1010110') + for val, res in [(False, '110110'), (True, '10110'), + (1, '0110'), (1, '010'), (0, '10'), + (0, '1'), (1, '')]: + a.remove(val) + self.assertEQUAL(a, bitarray(res)) + self.check_obj(a) + + a = bitarray('0010011') + a.remove(1) + self.assertEQUAL(a, bitarray('000011')) + self.assertRaises(TypeError, a.remove, 'A') + self.assertRaises(ValueError, a.remove, 21) + + def test_remove_errors(self): + a = bitarray() + for i in (True, False, 1, 0): + self.assertRaises(ValueError, a.remove, i) + + a = zeros(21) + self.assertRaises(ValueError, a.remove, 1) + a.setall(1) + self.assertRaises(ValueError, a.remove, 0) + + def test_pop_simple(self): + for x, n, r, y in [('1', 0, 1, ''), + ('0', -1, 0, ''), + ('0011100', 3, 1, '001100')]: + a = bitarray(x) + self.assertTrue(a.pop(n) is r) + self.assertEqual(a, bitarray(y)) + self.check_obj(a) + + a = bitarray('01') + self.assertEqual(a.pop(), True) + self.assertEqual(a.pop(), False) + # pop from empty bitarray + self.assertRaises(IndexError, a.pop) + + def test_pop_random_1(self): + for a in self.randombitarrays(): + self.assertRaises(IndexError, a.pop, len(a)) + self.assertRaises(IndexError, a.pop, -len(a) - 1) + if len(a) == 0: + continue + aa = a.tolist() + enda = a.endian() + self.assertEqual(a.pop(), aa[-1]) + self.check_obj(a) + self.assertEqual(a.endian(), enda) + + def test_pop_random_2(self): + for a in self.randombitarrays(start=1): + n = randrange(-len(a), len(a)) + aa = a.tolist() + x = a.pop(n) + self.assertBitEqual(x, aa[n]) + y = aa.pop(n) + self.assertEqual(a, bitarray(aa)) + self.assertBitEqual(x, y) + self.check_obj(a) + + def test_clear(self): + for a in self.randombitarrays(): + endian = a.endian() + a.clear() + self.assertEqual(len(a), 0) + self.assertEqual(a.endian(), endian) + self.check_obj(a) + + def test_setall(self): + a = urandom(5) + a.setall(True) + self.assertRaises(ValueError, a.setall, -1) + self.assertRaises(TypeError, a.setall, None) + self.assertEqual(a.to01(), '11111') + a.setall(0) + self.assertEqual(a.to01(), '00000') + self.check_obj(a) + + def test_setall_empty(self): + a = bitarray() + for v in 0, 1: + a.setall(v) + self.assertEqual(len(a), 0) + self.check_obj(a) + + def test_setall_random(self): + for a in self.randombitarrays(): + end = a.endian() + val = getrandbits(1) + a.setall(val) + self.assertEqual(a.to01(), len(a) * str(val)) + self.assertEqual(a.endian(), end) + self.check_obj(a) + +# --------------------------------------------------------------------------- + +class ByteReverseTests(unittest.TestCase, Util): + + def test_explicit_all(self): + for x, y in [('', ''), + ('11101101', '10110111'), + ('00000001', '10000000'), + ('11011111 00100000 00011111', + '11111011 00000100 11111000')]: + a = bitarray(x) + a.bytereverse() + self.assertEqual(a, bitarray(y)) + + def test_explicit_range(self): + a = bitarray('11100000 00000011 00111111 11111000') + a.bytereverse(0, 1) # reverse byte 0 + self.assertEqual(a, bitarray('00000111 00000011 00111111 11111000')) + a.bytereverse(1, -1) # reverse bytes 1 and 2 + self.assertEqual(a, bitarray('00000111 11000000 11111100 11111000')) + a.bytereverse(2) # reverse bytes 2 till end of buffer + self.assertEqual(a, bitarray('00000111 11000000 00111111 00011111')) + a.bytereverse(-1) # reverse last byte + self.assertEqual(a, bitarray('00000111 11000000 00111111 11111000')) + a.bytereverse(3, 1) # start > stop (nothing to reverse) + self.assertEqual(a, bitarray('00000111 11000000 00111111 11111000')) + a.bytereverse(0, 4) # reverse all bytes + self.assertEqual(a, bitarray('11100000 00000011 11111100 00011111')) + a.bytereverse(-2) # last two bytes + self.assertEqual(a, bitarray('11100000 00000011 00111111 11111000')) + + self.assertRaises(IndexError, a.bytereverse, -5) + self.assertRaises(IndexError, a.bytereverse, 0, -5) + self.assertRaises(IndexError, a.bytereverse, 5) + self.assertRaises(IndexError, a.bytereverse, 0, 5) + + def test_byte(self): + for i in range(256): + a = bitarray() + a.frombytes(bytearray([i])) + self.assertEqual(len(a), 8) + b = a.copy() + b.bytereverse() + self.assertEqual(b, a[::-1]) + a.reverse() + self.assertEqual(b, a) + self.check_obj(b) + + def test_consecutive(self): + for a in self.randombitarrays(): + b = a.copy() + # two consecutive calls to .bytereverse() leave the bitarray + # unchanged (even when the length is not a multiple of 8). + a.bytereverse() + a.bytereverse() + self.assertEQUAL(a, b) + + def test_random(self): + t = bitarray(endian=self.random_endian()) + t.frombytes(bytearray(range(256))) + t.bytereverse() + table = t.tobytes() # translation table + self.assertEqual(table[:9], b'\x00\x80\x40\xc0\x20\xa0\x60\xe0\x10') + + for n in range(100): + a = urandom(8 * n, self.random_endian()) + i = randint(0, n) # start + j = randint(0, n) # stop + b = a.copy() + memoryview(b)[i:j] = b.tobytes()[i:j].translate(table) + a.bytereverse(i, j) + self.assertEQUAL(a, b) + self.check_obj(a) + + def test_endian(self): + for n in range(20): + a = urandom(8 * n, self.random_endian()) + b = a.copy() + a.bytereverse() + a = bitarray(a, self.opposite_endian(a.endian())) + self.assertEqual(a.tobytes(), b.tobytes()) + +# --------------------------------------------------------------------------- + +class CountTests(unittest.TestCase, Util): + + def test_basic(self): + a = bitarray('10011') + self.assertEqual(a.count(), 3) + self.assertEqual(a.count(True), 3) + self.assertEqual(a.count(False), 2) + self.assertEqual(a.count(1), 3) + self.assertEqual(a.count(0), 2) + self.assertEqual(a.count(0, 5, 0, -1), 2) + self.assertEqual(a.count(bitarray('0')), 2) + self.assertEqual(a.count(bitarray('00')), 1) + self.assertRaises(ValueError, a.count, 2) + self.assertRaises(ValueError, a.count, 1, 0, 5, 0) + self.assertRaises(TypeError, a.count, '') + self.assertRaises(TypeError, a.count, 'A') + self.assertRaises(TypeError, a.count, 1, 2.0) + self.assertRaises(TypeError, a.count, 1, 2, 4.0) + self.assertRaises(TypeError, a.count, 0, 'A') + self.assertRaises(TypeError, a.count, 0, 0, 'A') + + def test_sub(self): + a = bitarray('10011000 1110000') + self.assertEqual(len(a), 15) + self.assertEqual(a.count(bitarray('')), 16) + self.assertEqual(a.count(bitarray('00')), 4) + self.assertEqual(a.count(bitarray('11')), 2) + self.assertEqual(a.count(bitarray('000')), 2) + self.assertEqual(a.count(bitarray('000'), 8), 1) + self.assertEqual(a.count(bitarray('000'), -3), 1) + self.assertEqual(a.count(bitarray('000'), -4), 1) + self.assertEqual(a.count(bitarray('000'), 4, -1), 2) + self.assertEqual(a.count(bitarray('00'), -3), 1) + self.assertEqual(a.count(bitarray('00'), -4), 2) + self.assertRaises(ValueError, a.count, bitarray(''), 0, 15, 2) + self.assertRaises(ValueError, a.count, bitarray('11'), 0, 15, 2) + self.assertRaises(ValueError, a.count, bitarray('11'), 15, 0, -1) + + def test_random_sub(self): + for _ in range(1000): + n = randrange(100) + a = urandom(n) + s = a.to01() + b = urandom(randrange(8)) + t = b.to01() + i = randint(-n - 10, n + 10) + j = randint(-n - 10, n + 10) + self.assertEqual(a.count(b, i, j), s.count(t, i, j)) + + def test_byte(self): + for i in range(256): + a = bitarray() + a.frombytes(bytearray([i])) + self.assertEqual(len(a), 8) + self.assertEqual(a.count(), bin(i)[2:].count('1')) + + def test_whole_range(self): + for n in range(500): + a = urandom(n, self.random_endian()) + s = a.to01() + for v in 0, 1: + ref = s.count(str(v)) + self.assertEqual(a.count(v), ref) + self.assertEqual(a.count(v, n, -n - 1, -1), ref) + + def test_sparse(self): + N = 65536 + a = zeros(N) + indices = set(randrange(N) for _ in range(256)) + a[list(indices)] = 1 + self.assertEqual(a.count(1), len(indices)) + self.assertEqual(a.count(0), N - len(indices)) + + for _ in range(100): + i = randrange(N) + j = randrange(i, N) + cnt = sum(1 for k in indices if i <= k < j) + self.assertEqual(a.count(1, i, j), cnt) + self.assertEqual(a.count(0, i, j), j - i - cnt) + + def test_zeros(self): + N = 30 + a = zeros(N, self.random_endian()) + for _ in range(10): + i = randrange(N) + j = randrange(i, N) + self.assertEqual(a.count(0, i, j), j - i) + + for step in range(-N - 3, N + 3): + if step == 0: + continue + self.assertEqual(a.count(0, i, i, step), 0) + + def test_range(self): + N = 300 + a = urandom(N, self.random_endian()) + s = a.to01() + for _ in range(1000): + i = randrange(N) + j = randrange(i, N) + + t = s[i:j] + c0 = t.count('0') + c1 = t.count('1') + self.assertEqual(c0 + c1, j - i) + + self.assertEqual(a.count(0, i, j), c0) + self.assertEqual(a.count(1, i, j), c1) + + b = a[i:j] + self.assertEqual(b.count(0), c0) + self.assertEqual(b.count(1), c1) + + def test_slicelength(self): + for N in range(100): + step = randint(-N - 1, N) + if step == 0: + continue + + a = zeros(N, self.random_endian()) + i = randint(-N - 1, N) + j = randint(-N - 1, N) + slicelength = self.calc_slicelength(slice(i, j, step), N) + self.assertEqual(len(a[i:j:step]), slicelength) + + self.assertEqual(a.count(0, i, j, step), slicelength) + self.assertEqual(a.count(1, i, j, step), 0) + a[i:j:step] = 1 + self.assertEqual(a.count(0), N - slicelength) + self.assertEqual(a.count(1), slicelength) + del a[i:j:step] + self.assertEqual(len(a), N - slicelength) + self.assertFalse(a.any()) + + def test_explicit(self): + a = bitarray('01001100 01110011 01') + self.assertEqual(a.count(), 9) + self.assertEqual(a.count(0, 12), 3) + self.assertEqual(a.count(1, 1, 18, 2), 6) + self.assertEqual(a.count(1, 0, 18, 3), 2) + self.assertEqual(a.count(1, 15, 4, -3), 2) + self.assertEqual(a.count(1, -5), 3) + self.assertEqual(a.count(1, 2, 17), 7) + self.assertEqual(a.count(1, 6, 11), 2) + self.assertEqual(a.count(0, 7, -3), 4) + self.assertEqual(a.count(1, 1, -1), 8) + self.assertEqual(a.count(1, 17, 14), 0) + + def test_random(self): + for _ in range(1000): + n = randrange(200) + a = urandom(n, self.random_endian()) + v = randrange(2) + i = randint(-n - 3, n + 3) + j = randint(-n - 3, n + 3) + step = randint(-n - 3, n + 3) + if step == 0: + continue + self.assertEqual(a.count(v, i, j, step), a[i:j:step].count(v)) + + def test_offest_buffer(self): + # this tests if words are aligned in popcnt_words() + N = 1 << 16 + for i in range(20): + a = urandom(N, 'little') + b = bitarray(buffer=memoryview(a)[i:], endian='little') + self.assertEqual(b.count(), a.count(1, 8 * i)) + +# --------------------------------------------------------------------------- + +class IndexTests(unittest.TestCase, Util): + + def test_errors(self): + a = bitarray() + for i in True, False, 1, 0: + self.assertEqual(a.find(i), -1) + self.assertRaises(ValueError, a.index, i) + + a = zeros(100) + self.assertRaises(TypeError, a.find) + self.assertRaises(TypeError, a.find, 1, 'a') + self.assertRaises(TypeError, a.find, 1, 0, 'a') + self.assertRaises(TypeError, a.find, 1, 0, 100, 'a') + self.assertEqual(a.find(1, right=True), -1) + + self.assertRaises(ValueError, a.index, True) + self.assertRaises(TypeError, a.index) + self.assertRaises(TypeError, a.index, 1, 'a') + self.assertRaises(TypeError, a.index, 1, 0, 'a') + self.assertRaises(TypeError, a.index, 1, 0, 100, 'a') + + def test_explicit(self): + a = bitarray('10011000 101000') + for sub, start, stop, right, res in [ + ('', 7, 13, 0, 7), + ('', 15, 99, 0, -1), + ('0', 0, 99, 0, 1), + ('1', 8, 12, 1, 10), + ('1', -99, -4, 1, 8), + ('11', 0, 99, 0, 3), + ('11', 4, 99, 0, -1), + ('111', 0, 99, 1, -1), + ('101', 0, 99, 1, 8), + (a.to01(), 0, 99, 0, 0), + ]: + b = bitarray(sub, self.random_endian()) + self.assertEqual(a.find(b, start, stop, right), res) + if res >= 0: + self.assertEqual(a.index(b, start, stop, right), res) + else: + self.assertRaises(ValueError, a.index, start, stop, right) + + if len(b) == 1: + self.assertEqual(a.find(b[0], start, stop, right), res) + + @staticmethod + def find_empty(n, start=0, stop=sys.maxsize, right=0): + """ + Return first (or rightmost (right=1)) index of an empty sequence + inside a sequence S of length n with S[start:stop], or -1 when no + empty sequence is found. + """ + if start > n: + return -1 + s = slice(start, stop, 1) + start, stop, stride = s.indices(n) + stop += 1 + i = stop - 1 if right else start + return i if start <= i < stop else -1 + + def test_find_empty(self): + # test staticmethod .find_empty() against Python builtins + for x in bytearray([0]), b"\0", "A": + empty = 0 * x # empty sequence + self.assertEqual(len(empty), 0) + for n in range(5): + z = n * x # sequence of length n + self.assertEqual(len(z), n) + self.assertTrue(type(x) == type(empty) == type(z)) + + self.assertEqual(z.find(empty), self.find_empty(n)) + self.assertEqual(z.rfind(empty), self.find_empty(n, right=1)) + + for start in range(-5, 5): + self.assertEqual(z.find(empty, start), + self.find_empty(n, start)) + self.assertEqual(z.rfind(empty, start), + self.find_empty(n, start, right=1)) + + for stop in range(-5, 5): + self.assertEqual(z.find(empty, start, stop), + self.find_empty(n, start, stop)) + self.assertEqual(z.rfind(empty, start, stop), + self.find_empty(n, start, stop, 1)) + + def test_empty(self): + # now that we have the tested staticmethod .find_empty(), we use it + # to test .find() with an empty bitarray + empty = bitarray() + for n in range(5): + z = bitarray(n) + for r in 0, 1: + self.assertEqual(z.find(empty, right=r), + self.find_empty(n, right=r)) + + for start in range(-5, 5): + self.assertEqual(z.find(empty, start, right=r), + self.find_empty(n, start, right=r)) + + for stop in range(-5, 5): + self.assertEqual(z.find(empty, start, stop, r), + self.find_empty(n, start, stop, r)) + + def test_range_explicit(self): + n = 150 + a = bitarray(n) + for m in range(n): + a.setall(0) + self.assertRaises(ValueError, a.index, 1) + self.assertEqual(a.find(1), -1) + a[m] = 1 + self.assertEqual(a.index(1), m) + self.assertEqual(a.find(1), m) + + a.setall(1) + self.assertRaises(ValueError, a.index, 0) + self.assertEqual(a.find(0), -1) + a[m] = 0 + self.assertEqual(a.index(0), m) + self.assertEqual(a.find(0), m) + + def test_random_start_stop(self): + for _ in range(500): + n = randrange(1, 200) + a = zeros(n) + plst = sorted(randrange(n) for _ in range(1, 10)) + a[plst] = 1 + # test without start and stop + self.assertEqual(a.find(1, right=0), plst[0]) + self.assertEqual(a.find(1, right=1), plst[-1]) + start = randint(0, n) + stop = randint(0, n) + + plst2 = [i for i in plst if start <= i < stop] + if plst2: + self.assertEqual(a.find(1, start, stop, 0), plst2[0]) + self.assertEqual(a.find(1, start, stop, 1), plst2[-1]) + else: + for right in 0, 1: + self.assertEqual(a.find(1, start, stop, right), -1) + + def test_random_sub(self): # test finding sub_bitarray + for _ in range(500): + n = randrange(1, 100) + a = urandom(n, self.random_endian()) + s = a.to01() + self.assertEqual(a.find(a), 0) + + n = len(a) + b = bitarray(randrange(0, 10), self.random_endian()) + t = b.to01() + self.assertEqual(a.find(b), s.find(t)) + + i = randint(-n - 5, n + 5) + j = randint(-n - 5, n + 5) + ref_l = s.find(t, i, j) + ref_r = s.rfind(t, i, j) + + self.assertEqual(ref_l == -1, ref_r == -1) + self.assertEqual(a.find(b, i, j, 0), ref_l) + self.assertEqual(a.find(b, i, j, 1), ref_r) + + if len(b) == 1: # test finding int + self.assertEqual(a.find(b[0], i, j, 0), ref_l) + self.assertEqual(a.find(b[0], i, j, 1), ref_r) + +# --------------------------------------------------------------------------- + +class SearchTests(unittest.TestCase, Util): + + def test_simple(self): + a = bitarray() + for s in 0, 1, False, True, bitarray('0'), bitarray('1'): + self.assertEqual(a.search(s), []) + + a = bitarray('00100') + for s in 1, True, bitarray('1'), bitarray('10'): + self.assertEqual(a.search(s), [2]) + + a = 100 * bitarray('1') + self.assertEqual(a.search(0), []) + self.assertEqual(a.search(1), list(range(100))) + + a = bitarray('10010101110011111001011') + for limit in range(10): + self.assertEqual(a.search(bitarray('011'), limit), + [6, 11, 20][:limit]) + + self.assertRaises(ValueError, a.search, bitarray()) + self.assertRaises(TypeError, a.search, '010') + + def test_itersearch_next(self): + a = bitarray('10011') + self.assertRaises(TypeError, a.itersearch, '') + it = a.itersearch(1) + self.assertIsType(it, 'searchiterator') + self.assertEqual(next(it), 0) + self.assertEqual(next(it), 3) + self.assertEqual(next(it), 4) + self.assertStopIteration(it) + x = bitarray('11') + it = a.itersearch(x) + del a, x + self.assertEqual(next(it), 3) + + def test_itersearch_empty(self): + a = bitarray('10011') + empty = bitarray() + self.assertEqual(list(a.itersearch(empty)), [0, 1, 2, 3, 4, 5]) + for start, stop, right, res in [ + (-9, 9, 0, [0, 1, 2, 3, 4, 5]), + ( 1, 4, 0, [1, 2, 3, 4]), + (-3, -2, 0, [2, 3]), + (-1, 0, 1, []), + ( 3, 3, 0, [3]), + ( 4, 3, 0, []), + ( 2, 2, 1, [2]), + ( 2, 1, 1, []), + ]: + self.assertEqual(list(a.itersearch(empty, start, stop, right)), + res) + + def test_explicit_1(self): + a = bitarray('10011', self.random_endian()) + for s, res in [('0', [1, 2]), ('1', [0, 3, 4]), + ('01', [2]), ('11', [3]), + ('000', []), ('1001', [0]), + ('011', [2]), ('0011', [1]), + ('10011', [0]), ('100111', [])]: + b = bitarray(s, self.random_endian()) + self.assertEqual(a.search(b), res) + self.assertEqual(list(a.itersearch(b)), res) + + def test_explicit_2(self): + a = bitarray('10010101 11001111 1001011') + for s, res in [('011', [6, 11, 20]), + ('111', [7, 12, 13, 14]), # note the overlap + ('1011', [5, 19]), + ('100', [0, 9, 16])]: + b = bitarray(s) + self.assertEqual(a.search(b), res) + self.assertEqual(list(a.itersearch(b)), res) + + def test_bool_random(self): + for a in self.randombitarrays(): + b = a.copy() + b.setall(0) + b[list(a.itersearch(1))] = 1 + self.assertEQUAL(b, a) + + b.setall(1) + b[list(a.itersearch(0))] = 0 + self.assertEQUAL(b, a) + + s = set(a.search(0) + a.search(1)) + self.assertEqual(len(s), len(a)) + + def test_random(self): + for a in self.randombitarrays(): + if a: + # search for a in itself + self.assertEqual(a.search(a), [0]) + self.assertEqual(list(a.itersearch(a)), [0]) + self.assertEqual(list(a.itersearch(a, right=1)), [0]) + + for sub in '0', '1', '01', '01', '11', '101', '1101', '01100': + b = bitarray(sub, self.random_endian()) + plst = [i for i in range(len(a)) if a[i:i + len(b)] == b] + self.assertEqual(a.search(b), plst) + + for p in a.itersearch(b): + self.assertEqual(a[p:p + len(b)], b) + self.assertEqual(list(a.itersearch(b)), plst) + + for p in a.itersearch(b, right=1): + self.assertEqual(a[p:p + len(b)], b) + self.assertEqual(list(a.itersearch(b, right=1)), plst[::-1]) + + def test_itersearch_random(self): + for _ in range(500): + n = randrange(1, 50) + a = urandom(n, self.random_endian()) + b = urandom(randrange(0, 10), self.random_endian()) + i = randrange(n) + j = randrange(n) + aa = a[i:j] + # list of positions + if b: + plst = [i + k for k in range(len(aa)) + if aa[k:k + len(b)] == b] + else: # empty sub-bitarray + plst = list(range(i, j + 1)) + + self.assertEqual(sorted(plst), plst) + self.assertEqual(list(a.itersearch(b, i, j)), plst) + + if len(b) == 1: # test sub-bitarray being int + self.assertEqual(list(a.itersearch(b[0], i, j)), plst) + + if plst: # test first and last using .find() + self.assertEqual(a.find(b, i, j, 0), plst[0]) + self.assertEqual(a.find(b, i, j, 1), plst[-1]) + + plst.reverse() + self.assertEqual(list(a.itersearch(b, i, j, 1)), plst) + + if len(b) == 1: # test sub-bitarray being int + self.assertEqual(list(a.itersearch(b[0], i, j, 1)), plst) + + # test contains + self.assertEqual(b in aa, bool(plst) if b else True) + + if not plst: # test .find() not found + for right in 0, 1: + self.assertEqual(a.find(b, i, j, right), -1) + + def test_iterator_change(self): + for right in 0, 1: + a = zeros(100) + b = zeros(10) + c = 0 + for i, x in enumerate(a.itersearch(b, right=right)): + if i == 40: + a.clear() + c += 1 + self.assertEqual(c, 41) + + def test_iterator_change_sub(self): + for right in 0, 1: + a = zeros(100) + b = zeros(0) + c = 0 + for i, x in enumerate(a.itersearch(b, right=right)): + if i == 20: + b.append(1) + c += 1 + self.assertEqual(c, 21) + +# --------------------------------------------------------------------------- + +class BytesTests(unittest.TestCase, Util): + + @staticmethod + def randombytes(): + for n in range(1, 20): + yield os.urandom(n) + + def test_frombytes_simple(self): + a = bitarray(endian='big') + a.frombytes(b'A') + self.assertEqual(a, bitarray('01000001')) + + b = a + b.frombytes(b'BC') + self.assertEQUAL(b, bitarray('01000001 01000010 01000011', + endian='big')) + self.assertTrue(b is a) + + def test_frombytes_types(self): + a = bitarray(endian='big') + a.frombytes(b'A') # bytes + self.assertEqual(a, bitarray('01000001')) + a.frombytes(bytearray([254])) # bytearray + self.assertEqual(a, bitarray('01000001 11111110')) + a.frombytes(memoryview(b'C')) # memoryview + self.assertEqual(a, bitarray('01000001 11111110 01000011')) + + a.clear() + if is_py3k: # Python 2's array cannot be used as buffer + a.frombytes(array.array('B', [5, 255, 192])) + self.assertEqual(a, bitarray('00000101 11111111 11000000')) + + self.check_obj(a) + + for x in u'', 0, 1, False, True, None, []: + self.assertRaises(TypeError, a.frombytes, x) + + def test_frombytes_bitarray(self): + for endian in 'little', 'big': + # endianness doesn't matter here as we're writting the buffer + # from bytes, and then getting the memoryview + b = bitarray(0, endian) + b.frombytes(b'ABC') + + a = bitarray(0, 'big') + a.frombytes(bitarray(b)) + self.assertEqual(a.endian(), 'big') + self.assertEqual(a, bitarray('01000001 01000010 01000011')) + self.check_obj(a) + + def test_frombytes_self(self): + a = bitarray() + self.assertRaisesMessage( + BufferError, + "cannot resize bitarray that is exporting buffers", + a.frombytes, a) + + def test_frombytes_empty(self): + for a in self.randombitarrays(): + b = a.copy() + a.frombytes(b'') + a.frombytes(bytearray()) + self.assertEQUAL(a, b) + self.assertFalse(a is b) + self.check_obj(a) + + def test_frombytes_errors(self): + a = bitarray() + self.assertRaises(TypeError, a.frombytes) + self.assertRaises(TypeError, a.frombytes, b'', b'') + self.assertRaises(TypeError, a.frombytes, 1) + self.check_obj(a) + + def test_frombytes_random(self): + for b in self.randombitarrays(): + for s in self.randombytes(): + a = bitarray(endian=b.endian()) + a.frombytes(s) + c = b.copy() + b.frombytes(s) + self.assertEQUAL(b[-len(a):], a) + self.assertEQUAL(b[:-len(a)], c) + self.assertEQUAL(b, c + a) + self.check_obj(a) + + def test_tobytes_empty(self): + a = bitarray() + self.assertEqual(a.tobytes(), b'') + + def test_tobytes_endian(self): + for end in ('big', 'little'): + a = bitarray(endian=end) + a.frombytes(b'foo') + self.assertEqual(a.tobytes(), b'foo') + + for s in self.randombytes(): + a = bitarray(endian=end) + a.frombytes(s) + self.assertEqual(a.tobytes(), s) + self.check_obj(a) + + def test_tobytes_explicit_ones(self): + for n, s in [(1, b'\x01'), (2, b'\x03'), (3, b'\x07'), (4, b'\x0f'), + (5, b'\x1f'), (6, b'\x3f'), (7, b'\x7f'), (8, b'\xff'), + (12, b'\xff\x0f'), (15, b'\xff\x7f'), (16, b'\xff\xff'), + (17, b'\xff\xff\x01'), (24, b'\xff\xff\xff')]: + a = ones(n, endian='little') + self.assertEqual(a.tobytes(), s) + + def test_unpack_simple(self): + a = bitarray('01') + self.assertIsInstance(a.unpack(), bytes) + self.assertEqual(a.unpack(), b'\x00\x01') + self.assertEqual(a.unpack(b'A'), b'A\x01') + self.assertEqual(a.unpack(b'0', b'1'), b'01') + self.assertEqual(a.unpack(one=b'\xff'), b'\x00\xff') + self.assertEqual(a.unpack(zero=b'A'), b'A\x01') + self.assertEqual(a.unpack(one=b't', zero=b'f'), b'ft') + + def test_unpack_random(self): + for a in self.randombitarrays(): + self.assertEqual(a.unpack(b'0', b'1'), + a.to01().encode()) + # round trip + b = bitarray() + b.pack(a.unpack()) + self.assertEqual(b, a) + # round trip with invert + b = bitarray() + b.pack(a.unpack(b'\x01', b'\x00')) + b.invert() + self.assertEqual(b, a) + + def test_unpack_errors(self): + a = bitarray('01') + self.assertRaises(TypeError, a.unpack, b'') + self.assertRaises(TypeError, a.unpack, b'0', b'') + self.assertRaises(TypeError, a.unpack, b'a', zero=b'b') + self.assertRaises(TypeError, a.unpack, foo=b'b') + self.assertRaises(TypeError, a.unpack, one=b'aa', zero=b'b') + if is_py3k: + self.assertRaises(TypeError, a.unpack, '0') + self.assertRaises(TypeError, a.unpack, one='a') + self.assertRaises(TypeError, a.unpack, b'0', '1') + + def test_pack_simple(self): + for endian in 'little', 'big': + _set_default_endian(endian) + a = bitarray() + a.pack(bytes()) + self.assertEQUAL(a, bitarray()) + a.pack(b'\x00') + self.assertEQUAL(a, bitarray('0')) + a.pack(b'\xff') + self.assertEQUAL(a, bitarray('01')) + a.pack(b'\x01\x00\x7a') + self.assertEQUAL(a, bitarray('01101')) + a.pack(bytearray([0x01, 0x00, 0xff, 0xa7])) + self.assertEQUAL(a, bitarray('01101 1011')) + self.check_obj(a) + + def test_pack_types(self): + a = bitarray() + a.pack(b'\0\x01') # bytes + self.assertEqual(a, bitarray('01')) + a.pack(bytearray([0, 2])) # bytearray + self.assertEqual(a, bitarray('01 01')) + a.pack(memoryview(b'\x02\0')) # memoryview + self.assertEqual(a, bitarray('01 01 10')) + + if is_py3k: # Python 2's array cannot be used as buffer + a.pack(array.array('B', [0, 255, 192])) + self.assertEqual(a, bitarray('01 01 10 011')) + + self.check_obj(a) + + def test_pack_bitarray(self): + b = bitarray("00000000 00000001 10000000 11111111 00000000") + a = bitarray() + a.pack(bitarray(b)) + self.assertEqual(a, bitarray('01110')) + self.check_obj(a) + + def test_pack_self(self): + a = bitarray() + self.assertRaisesMessage( + BufferError, + "cannot resize bitarray that is exporting buffers", + a.pack, a) + + def test_pack_allbytes(self): + a = bitarray() + a.pack(bytearray(range(256))) + self.assertEqual(a, bitarray('0' + 255 * '1')) + self.check_obj(a) + + def test_pack_errors(self): + a = bitarray() + self.assertRaises(TypeError, a.pack, 0) + if is_py3k: + self.assertRaises(TypeError, a.pack, '1') + self.assertRaises(TypeError, a.pack, [1, 3]) + +# --------------------------------------------------------------------------- + +class DescriptorTests(unittest.TestCase, Util): + + def test_nbytes_padbits(self): + for a in self.randombitarrays(): + self.assertEqual(a.nbytes, bits2bytes(len(a))) + self.assertEqual(a.padbits, 8 * a.nbytes - len(a)) + if is_py3k: + self.assertIsInstance(a.nbytes, int) + self.assertIsInstance(a.padbits, int) + + def test_readonly(self): + a = bitarray('110') + self.assertFalse(a.readonly) + self.assertIsInstance(a.readonly, bool) + + b = frozenbitarray(a) + self.assertTrue(b.readonly) + self.assertIsInstance(b.readonly, bool) + +# --------------------------------------------------------------------------- + +class FileTests(unittest.TestCase, Util): + + def setUp(self): + self.tmpdir = tempfile.mkdtemp() + self.tmpfname = os.path.join(self.tmpdir, 'testfile') + + def tearDown(self): + shutil.rmtree(self.tmpdir) + + def read_file(self): + with open(self.tmpfname, 'rb') as fi: + return fi.read() + + def assertFileSize(self, size): + self.assertEqual(os.path.getsize(self.tmpfname), size) + + def test_pickle(self): + d1 = {i: a for i, a in enumerate(self.randombitarrays())} + with open(self.tmpfname, 'wb') as fo: + pickle.dump(d1, fo) + with open(self.tmpfname, 'rb') as fi: + d2 = pickle.load(fi) + for key in d1.keys(): + self.assertEQUAL(d1[key], d2[key]) + + # pyodide has no dbm module + @skipIf(pyodide) + def test_shelve(self): + d1 = shelve.open(self.tmpfname) + stored = [] + for i, a in enumerate(self.randombitarrays()): + key = str(i) + d1[key] = a + stored.append((key, a)) + d1.close() + + d2 = shelve.open(self.tmpfname) + for k, v in stored: + self.assertEQUAL(d2[k], v) + d2.close() + + def test_fromfile_empty(self): + with open(self.tmpfname, 'wb') as fo: + pass + self.assertFileSize(0) + + a = bitarray() + with open(self.tmpfname, 'rb') as fi: + a.fromfile(fi) + self.assertEqual(a, bitarray()) + self.check_obj(a) + + def test_fromfile_Foo(self): + with open(self.tmpfname, 'wb') as fo: + fo.write(b'Foo') + self.assertFileSize(3) + + a = bitarray(endian='big') + with open(self.tmpfname, 'rb') as fi: + a.fromfile(fi) + self.assertEqual(a, bitarray('01000110 01101111 01101111')) + + a = bitarray(endian='little') + with open(self.tmpfname, 'rb') as fi: + a.fromfile(fi) + self.assertEqual(a, bitarray('01100010 11110110 11110110')) + + def test_fromfile_wrong_args(self): + a = bitarray() + self.assertRaises(TypeError, a.fromfile) + self.assertRaises(Exception, a.fromfile, 42) + self.assertRaises(Exception, a.fromfile, 'bar') + + with open(self.tmpfname, 'wb') as fo: + pass + with open(self.tmpfname, 'rb') as fi: + self.assertRaises(TypeError, a.fromfile, fi, None) + + def test_fromfile_erros(self): + with open(self.tmpfname, 'wb') as fo: + fo.write(b'0123456789') + self.assertFileSize(10) + + a = bitarray() + with open(self.tmpfname, 'wb') as fi: + self.assertRaises(Exception, a.fromfile, fi) + + if is_py3k: + with open(self.tmpfname, 'r') as fi: + self.assertRaises(TypeError, a.fromfile, fi) + + def test_from_large_files(self): + for N in range(65534, 65538): + data = os.urandom(N) + with open(self.tmpfname, 'wb') as fo: + fo.write(data) + + a = bitarray() + with open(self.tmpfname, 'rb') as fi: + a.fromfile(fi) + self.assertEqual(len(a), 8 * N) + self.assertEqual(buffer_info(a, 'size'), N) + self.assertEqual(a.tobytes(), data) + self.check_obj(a) + + def test_fromfile_extend_existing(self): + with open(self.tmpfname, 'wb') as fo: + fo.write(b'Foo') + + foo_le = '01100010 11110110 11110110' + + for n in range(20): + a = bitarray(n * '1', endian='little') + with open(self.tmpfname, 'rb') as fi: + a.fromfile(fi) + self.assertEqual(a, bitarray(n * '1' + foo_le)) + self.check_obj(a) + + def test_fromfile_n(self): + a = bitarray() + a.frombytes(b'ABCDEFGHIJ') + with open(self.tmpfname, 'wb') as fo: + a.tofile(fo) + self.assertFileSize(10) + + with open(self.tmpfname, 'rb') as f: + a = bitarray() + a.fromfile(f, 0); self.assertEqual(a.tobytes(), b'') + a.fromfile(f, 1); self.assertEqual(a.tobytes(), b'A') + f.read(1) # skip B + a.fromfile(f, 1); self.assertEqual(a.tobytes(), b'AC') + a = bitarray() + a.fromfile(f, 2); self.assertEqual(a.tobytes(), b'DE') + a.fromfile(f, 1); self.assertEqual(a.tobytes(), b'DEF') + a.fromfile(f, 0); self.assertEqual(a.tobytes(), b'DEF') + a.fromfile(f); self.assertEqual(a.tobytes(), b'DEFGHIJ') + a.fromfile(f); self.assertEqual(a.tobytes(), b'DEFGHIJ') + self.check_obj(a) + + a = bitarray() + with open(self.tmpfname, 'rb') as f: + f.read(1) + self.assertRaises(EOFError, a.fromfile, f, 10) + # check that although we received an EOFError, the bytes were read + self.assertEqual(a.tobytes(), b'BCDEFGHIJ') + + a = bitarray() + with open(self.tmpfname, 'rb') as f: + # negative values - like ommiting the argument + a.fromfile(f, -1) + self.assertEqual(a.tobytes(), b'ABCDEFGHIJ') + self.assertRaises(EOFError, a.fromfile, f, 1) + + def test_fromfile_BytesIO(self): + f = BytesIO(b'somedata') + a = bitarray() + a.fromfile(f, 4) + self.assertEqual(len(a), 32) + self.assertEqual(a.tobytes(), b'some') + a.fromfile(f) + self.assertEqual(len(a), 64) + self.assertEqual(a.tobytes(), b'somedata') + self.check_obj(a) + + def test_tofile_empty(self): + a = bitarray() + with open(self.tmpfname, 'wb') as f: + a.tofile(f) + + self.assertFileSize(0) + + def test_tofile_Foo(self): + a = bitarray('0100011 001101111 01101111', endian='big') + b = a.copy() + with open(self.tmpfname, 'wb') as f: + a.tofile(f) + self.assertEQUAL(a, b) + + self.assertFileSize(3) + self.assertEqual(self.read_file(), b'Foo') + + def test_tofile_random(self): + for a in self.randombitarrays(): + with open(self.tmpfname, 'wb') as fo: + a.tofile(fo) + n = a.nbytes + self.assertFileSize(n) + raw = self.read_file() + self.assertEqual(len(raw), n) + self.assertEqual(raw, a.tobytes()) + + def test_tofile_errors(self): + n = 100 + a = bitarray(8 * n) + self.assertRaises(TypeError, a.tofile) + + with open(self.tmpfname, 'wb') as f: + a.tofile(f) + self.assertFileSize(n) + # write to closed file + self.assertRaises(ValueError, a.tofile, f) + + if is_py3k: + with open(self.tmpfname, 'w') as f: + self.assertRaises(TypeError, a.tofile, f) + + with open(self.tmpfname, 'rb') as f: + self.assertRaises(Exception, a.tofile, f) + + def test_tofile_large(self): + n = 100 * 1000 + a = zeros(8 * n) + a[2::37] = 1 + with open(self.tmpfname, 'wb') as f: + a.tofile(f) + self.assertFileSize(n) + + raw = self.read_file() + self.assertEqual(len(raw), n) + self.assertEqual(raw, a.tobytes()) + + def test_tofile_ones(self): + for n in range(20): + a = n * bitarray('1', endian='little') + with open(self.tmpfname, 'wb') as fo: + a.tofile(fo) + + raw = self.read_file() + self.assertEqual(len(raw), a.nbytes) + # when we fill the padbits in a, we can compare + a.fill() + b = bitarray(endian='little') + b.frombytes(raw) + self.assertEqual(a, b) + + def test_tofile_BytesIO(self): + for n in list(range(10)) + list(range(65534, 65538)): + data = os.urandom(n) + a = bitarray(0, 'big') + a.frombytes(data) + self.assertEqual(a.nbytes, n) + f = BytesIO() + a.tofile(f) + self.assertEqual(f.getvalue(), data) + + @skipIf(sys.version_info[0] == 2 or is_pypy) + def test_mmap(self): + with open(self.tmpfname, 'wb') as fo: + fo.write(1000 * b'\0') + + with open(self.tmpfname, 'r+b') as f: # see issue #141 + with mmap.mmap(f.fileno(), 0) as mapping: + a = bitarray(buffer=mapping, endian='little') + info = buffer_info(a) + self.assertFalse(info['readonly']) + self.assertTrue(info['imported']) + self.assertEqual(a, zeros(8000)) + a[::2] = True + # not sure this is necessary, without 'del a', I get: + # BufferError: cannot close exported pointers exist + del a + + self.assertEqual(self.read_file(), 1000 * b'\x55') + + # pyodide hits emscripten mmap bug + @skipIf(sys.version_info[0] == 2 or pyodide or is_pypy) + def test_mmap_2(self): + with open(self.tmpfname, 'wb') as fo: + fo.write(1000 * b'\x22') + + with open(self.tmpfname, 'r+b') as f: + a = bitarray(buffer=mmap.mmap(f.fileno(), 0), endian='little') + info = buffer_info(a) + self.assertFalse(info['readonly']) + self.assertTrue(info['imported']) + self.assertEqual(a, 1000 * bitarray('0100 0100')) + a[::4] = 1 + + self.assertEqual(self.read_file(), 1000 * b'\x33') + + @skipIf(sys.version_info[0] == 2 or is_pypy) + def test_mmap_readonly(self): + with open(self.tmpfname, 'wb') as fo: + fo.write(994 * b'\x89' + b'Veedon') + + with open(self.tmpfname, 'rb') as fi: # readonly + m = mmap.mmap(fi.fileno(), 0, access=mmap.ACCESS_READ) + a = bitarray(buffer=m, endian='big') + info = buffer_info(a) + self.assertTrue(info['readonly']) + self.assertTrue(info['imported']) + self.assertRaisesMessage(TypeError, + "cannot modify read-only memory", + a.__setitem__, 0, 1) + self.assertEqual(a[:8 * 994], 994 * bitarray('1000 1001')) + self.assertEqual(a[8 * 994:].tobytes(), b'Veedon') + +# ----------------------------- Decode Tree --------------------------------- + +alphabet_code = { + ' ': bitarray('001'), '.': bitarray('0101010'), + 'a': bitarray('0110'), 'b': bitarray('0001100'), + 'c': bitarray('000011'), 'd': bitarray('01011'), + 'e': bitarray('111'), 'f': bitarray('010100'), + 'g': bitarray('101000'), 'h': bitarray('00000'), + 'i': bitarray('1011'), 'j': bitarray('0111101111'), + 'k': bitarray('00011010'), 'l': bitarray('01110'), + 'm': bitarray('000111'), 'n': bitarray('1001'), + 'o': bitarray('1000'), 'p': bitarray('101001'), + 'q': bitarray('00001001101'), 'r': bitarray('1101'), + 's': bitarray('1100'), 't': bitarray('0100'), + 'u': bitarray('000100'), 'v': bitarray('0111100'), + 'w': bitarray('011111'), 'x': bitarray('0000100011'), + 'y': bitarray('101010'), 'z': bitarray('00011011110') +} + +class DecodeTreeTests(unittest.TestCase, Util): + + def test_create(self): + dt = decodetree(alphabet_code) + self.assertIsType(dt, 'decodetree') + self.assertIsInstance(dt, decodetree) + self.assertRaises(TypeError, decodetree, None) + self.assertRaises(TypeError, decodetree, 'foo') + d = dict(alphabet_code) + d['-'] = bitarray() + self.assertRaises(ValueError, decodetree, d) + + def test_ambiguous_code(self): + for d in [ + {'a': bitarray('0'), 'b': bitarray('0'), 'c': bitarray('1')}, + {'a': bitarray('01'), 'b': bitarray('01'), 'c': bitarray('1')}, + {'a': bitarray('0'), 'b': bitarray('01')}, + {'a': bitarray('0'), 'b': bitarray('11'), 'c': bitarray('111')}, + ]: + self.assertRaises(ValueError, decodetree, d) + + @skipIf(is_pypy) + def test_sizeof(self): + dt = decodetree({'.': bitarray('1')}) + self.assertTrue(0 < sys.getsizeof(dt) < 100) + + dt = decodetree({'a': zeros(20)}) + self.assertTrue(sys.getsizeof(dt) > 200) + + def test_nodes(self): + for n in range(1, 20): + dt = decodetree({'a': zeros(n)}) + self.assertEqual(dt.nodes(), n + 1) + self.assertFalse(dt.complete()) + + dt = decodetree({'I': bitarray('1'), 'l': bitarray('01'), + 'a': bitarray('001'), 'n': bitarray('000')}) + self.assertEqual(dt.nodes(), 7) + dt = decodetree(alphabet_code) + self.assertEqual(dt.nodes(), 70) + + def test_complete(self): + dt = decodetree({'.': bitarray('1')}) + self.assertIsInstance(dt.complete(), bool) + self.assertFalse(dt.complete()) + + dt = decodetree({'a': bitarray('0'), + 'b': bitarray('1')}) + self.assertTrue(dt.complete()) + + dt = decodetree({'a': bitarray('0'), + 'b': bitarray('11')}) + self.assertFalse(dt.complete()) + + dt = decodetree({'a': bitarray('0'), + 'b': bitarray('11'), + 'c': bitarray('10')}) + self.assertTrue(dt.complete()) + + def test_todict(self): + t = decodetree(alphabet_code) + d = t.todict() + self.assertIsInstance(d, dict) + self.assertEqual(d, alphabet_code) + + def test_decode(self): + t = decodetree(alphabet_code) + a = bitarray('1011 01110 0110 1001') + self.assertEqual(a.decode(t), ['i', 'l', 'a', 'n']) + self.assertEqual(''.join(a.iterdecode(t)), 'ilan') + a = bitarray() + self.assertEqual(a.decode(t), []) + self.assertEqual(''.join(a.iterdecode(t)), '') + self.check_obj(a) + + @skipIf(is_pypy) + def test_large(self): + d = {i: bitarray(bool((1 << j) & i) for j in range(10)) + for i in range(1024)} + t = decodetree(d) + self.assertEqual(t.todict(), d) + self.assertEqual(t.nodes(), 2047) + self.assertTrue(sys.getsizeof(t) > 10000) + +# ------------------ variable length encoding and decoding ------------------ + +class PrefixCodeTests(unittest.TestCase, Util): + + def test_encode_string(self): + a = bitarray() + a.encode(alphabet_code, '') + self.assertEqual(a, bitarray()) + a.encode(alphabet_code, 'a') + self.assertEqual(a, bitarray('0110')) + + def test_encode_list(self): + a = bitarray() + a.encode(alphabet_code, []) + self.assertEqual(a, bitarray()) + a.encode(alphabet_code, ['e']) + self.assertEqual(a, bitarray('111')) + + def test_encode_iter(self): + a = bitarray() + d = {0: bitarray('0'), 1: bitarray('1')} + a.encode(d, iter([0, 1, 1, 0])) + self.assertEqual(a, bitarray('0110')) + + def foo(): + for c in 1, 1, 0, 0, 1, 1: + yield c + + a.clear() + a.encode(d, foo()) + a.encode(d, range(2)) + self.assertEqual(a, bitarray('11001101')) + self.assertEqual(d, {0: bitarray('0'), 1: bitarray('1')}) + + def test_encode_symbol_not_in_code(self): + d = dict(alphabet_code) + a = bitarray() + a.encode(d, 'is') + self.assertEqual(a, bitarray('1011 1100')) + self.assertRaises(ValueError, a.encode, d, 'ilAn') + msg = "symbol not defined in prefix code" + if is_py3k: + msg += ": None" + self.assertRaisesMessage(ValueError, msg, a.encode, d, [None, 2]) + + def test_encode_not_iterable(self): + d = {'a': bitarray('0'), 'b': bitarray('1')} + a = bitarray() + a.encode(d, 'abba') + self.assertRaises(TypeError, a.encode, d, 42) + self.assertRaises(TypeError, a.encode, d, 1.3) + self.assertRaises(TypeError, a.encode, d, None) + self.assertEqual(a, bitarray('0110')) + + def test_check_codedict_encode(self): + a = bitarray() + self.assertRaises(TypeError, a.encode, None, '') + self.assertRaises(ValueError, a.encode, {}, '') + self.assertRaises(TypeError, a.encode, {'a': 'b'}, 'a') + self.assertRaises(ValueError, a.encode, {'a': bitarray()}, 'a') + self.assertEqual(len(a), 0) + + def test_check_codedict_decode(self): + a = bitarray('101') + self.assertRaises(TypeError, a.decode, 0) + self.assertRaises(ValueError, a.decode, {}) + self.assertRaises(TypeError, a.decode, {'a': 42}) + self.assertRaises(ValueError, a.decode, {'a': bitarray()}) + self.assertEqual(a, bitarray('101')) + + def test_check_codedict_iterdecode(self): + a = bitarray('1100101') + self.assertRaises(TypeError, a.iterdecode, 0) + self.assertRaises(ValueError, a.iterdecode, {}) + self.assertRaises(TypeError, a.iterdecode, {'a': []}) + self.assertRaises(ValueError, a.iterdecode, {'a': bitarray()}) + self.assertEqual(a, bitarray('1100101')) + + def test_decode_simple(self): + d = {'I': bitarray('1'), 'l': bitarray('01'), + 'a': bitarray('001'), 'n': bitarray('000')} + dcopy = dict(d) + a = bitarray('101001000') + res = list("Ilan") + self.assertEqual(a.decode(d), res) + self.assertEqual(list(a.iterdecode(d)), res) + self.assertEqual(d, dcopy) + self.assertEqual(a, bitarray('101001000')) + + def test_iterdecode_type(self): + a = bitarray('0110') + it = a.iterdecode(alphabet_code) + self.assertIsType(it, 'decodeiterator') + self.assertEqual(list(it), ['a']) + + def test_iterdecode_remove(self): + d = {'I': bitarray('1'), 'l': bitarray('01'), + 'a': bitarray('001'), 'n': bitarray('000')} + t = decodetree(d) + a = bitarray('101001000') + it = a.iterdecode(t) + del t # remove tree + self.assertEqual(''.join(it), "Ilan") + + it = a.iterdecode(d) + del a + self.assertEqual(''.join(it), "Ilan") + + def test_decode_empty(self): + d = {'a': bitarray('1')} + a = bitarray() + self.assertEqual(a.decode(d), []) + self.assertEqual(d, {'a': bitarray('1')}) + # test decode iterator + self.assertEqual(list(a.iterdecode(d)), []) + self.assertEqual(d, {'a': bitarray('1')}) + self.assertEqual(len(a), 0) + + def test_decode_incomplete(self): + d = {'a': bitarray('0'), 'b': bitarray('111')} + a = bitarray('00011') + msg = "incomplete prefix code at position 3" + self.assertRaisesMessage(ValueError, msg, a.decode, d) + it = a.iterdecode(d) + self.assertIsType(it, 'decodeiterator') + self.assertRaisesMessage(ValueError, msg, list, it) + t = decodetree(d) + self.assertRaisesMessage(ValueError, msg, a.decode, t) + self.assertRaisesMessage(ValueError, msg, list, a.iterdecode(t)) + + self.assertEqual(a, bitarray('00011')) + self.assertEqual(d, {'a': bitarray('0'), 'b': bitarray('111')}) + self.assertEqual(t.todict(), d) + + def test_decode_incomplete_2(self): + a = bitarray() + a.encode(alphabet_code, "now we rise") + x = len(a) + a.extend('00') + msg = "incomplete prefix code at position %d" % x + self.assertRaisesMessage(ValueError, msg, a.decode, alphabet_code) + + def test_decode_buggybitarray(self): + d = dict(alphabet_code) + # i s t + a = bitarray('1011 1100 0100 011110111001101001') + msg = "prefix code unrecognized in bitarray at position 12 .. 21" + self.assertRaisesMessage(ValueError, msg, a.decode, d) + self.assertRaisesMessage(ValueError, msg, list, a.iterdecode(d)) + t = decodetree(d) + self.assertRaisesMessage(ValueError, msg, a.decode, t) + self.assertRaisesMessage(ValueError, msg, list, a.iterdecode(d)) + + self.check_obj(a) + self.assertEqual(t.todict(), d) + + def test_iterdecode_no_term(self): + d = {'a': bitarray('0'), 'b': bitarray('111')} + a = bitarray('011') + it = a.iterdecode(d) + self.assertEqual(next(it), 'a') + self.assertRaisesMessage(ValueError, + "incomplete prefix code at position 1", + next, it) + self.assertEqual(a, bitarray('011')) + + def test_iterdecode_buggybitarray(self): + d = {'a': bitarray('0')} + a = bitarray('1') + it = a.iterdecode(d) + self.assertRaises(ValueError, next, it) + self.assertEqual(a, bitarray('1')) + self.assertEqual(d, {'a': bitarray('0')}) + + def test_decode_buggybitarray2(self): + d = {'a': bitarray('00'), 'b': bitarray('01')} + a = bitarray('1') + self.assertRaises(ValueError, a.decode, d) + self.assertRaises(ValueError, next, a.iterdecode(d)) + + t = decodetree(d) + self.assertRaises(ValueError, a.decode, t) + self.assertRaises(ValueError, next, a.iterdecode(t)) + + self.assertEqual(a, bitarray('1')) + self.assertEqual(d, {'a': bitarray('00'), 'b': bitarray('01')}) + self.assertEqual(t.todict(), d) + + def test_decode_random(self): + pat1 = re.compile(r'incomplete prefix code.+\s(\d+)') + pat2 = re.compile(r'prefix code unrecognized.+\s(\d+)\s*\.\.\s*(\d+)') + t = decodetree(alphabet_code) + for a in self.randombitarrays(): + try: + a.decode(t) + except ValueError as e: + msg = str(e) + m1 = pat1.match(msg) + m2 = pat2.match(msg) + self.assertFalse(m1 and m2) + if m1: + i = int(m1.group(1)) + if m2: + i, j = int(m2.group(1)), int(m2.group(2)) + self.assertFalse(a[i:j] in alphabet_code.values()) + a[:i].decode(t) + + def test_decode_ambiguous_code(self): + for d in [ + {'a': bitarray('0'), 'b': bitarray('0'), 'c': bitarray('1')}, + {'a': bitarray('01'), 'b': bitarray('01'), 'c': bitarray('1')}, + {'a': bitarray('0'), 'b': bitarray('01')}, + {'a': bitarray('0'), 'b': bitarray('11'), 'c': bitarray('111')}, + ]: + a = bitarray() + self.assertRaises(ValueError, a.decode, d) + self.assertRaises(ValueError, a.iterdecode, d) + self.check_obj(a) + + def test_miscitems(self): + d = {None : bitarray('00'), + 0 : bitarray('110'), + 1 : bitarray('111'), + '' : bitarray('010'), + 2 : bitarray('011')} + a = bitarray() + a.encode(d, [None, 0, 1, '', 2]) + self.assertEqual(a, bitarray('00110111010011')) + self.assertEqual(a.decode(d), [None, 0, 1, '', 2]) + # iterator + it = a.iterdecode(d) + self.assertEqual(next(it), None) + self.assertEqual(next(it), 0) + self.assertEqual(next(it), 1) + self.assertEqual(next(it), '') + self.assertEqual(next(it), 2) + self.assertStopIteration(it) + + def test_quick_example(self): + a = bitarray() + message = 'the quick brown fox jumps over the lazy dog.' + a.encode(alphabet_code, message) + self.assertEqual(a, bitarray( + # t h e q u i c k + '0100 00000 111 001 00001001101 000100 1011 000011 00011010 001' + # b r o w n f o x + '0001100 1101 1000 011111 1001 001 010100 1000 0000100011 001' + # j u m p s o v e r + '0111101111 000100 000111 101001 1100 001 1000 0111100 111 1101' + # t h e l a z y + '001 0100 00000 111 001 01110 0110 00011011110 101010 001' + # d o g . + '01011 1000 101000 0101010')) + self.assertEqual(''.join(a.decode(alphabet_code)), message) + self.assertEqual(''.join(a.iterdecode(alphabet_code)), message) + t = decodetree(alphabet_code) + self.assertEqual(''.join(a.decode(t)), message) + self.assertEqual(''.join(a.iterdecode(t)), message) + self.check_obj(a) + +# --------------------------- Buffer Import --------------------------------- + +class BufferImportTests(unittest.TestCase, Util): + + def test_bytes(self): + b = 100 * b'\0' + a = bitarray(buffer=b) + + info = buffer_info(a) + self.assertFalse(info['allocated']) + self.assertTrue(info['readonly']) + self.assertTrue(info['imported']) + + self.assertRaises(TypeError, a.setall, 1) + self.assertRaises(TypeError, a.clear) + self.assertEqual(a, zeros(800)) + self.check_obj(a) + + @skipIf(is_pypy) + def test_bytearray(self): + b = bytearray(100 * [0]) + a = bitarray(buffer=b, endian='little') + + info = buffer_info(a) + self.assertFalse(info['allocated']) + self.assertFalse(info['readonly']) + self.assertTrue(info['imported']) + + a[0] = 1 + self.assertEqual(b[0], 1) + a[7] = 1 + self.assertEqual(b[0], 129) + a[:] = 1 + self.assertEqual(b, bytearray(100 * [255])) + self.assertRaises(BufferError, a.pop) + a[8:16] = bitarray('10000010', endian='big') + self.assertEqual(b, bytearray([255, 65] + 98 * [255])) + self.assertEqual(a.tobytes(), bytes(b)) + for n in 7, 9: + self.assertRaises(BufferError, a.__setitem__, slice(8, 16), + bitarray(n)) + b[1] = b[2] = 255 + self.assertEqual(b, bytearray(100 * [255])) + self.assertEqual(a, 800 * bitarray('1')) + self.check_obj(a) + + # Python 2's array cannot be used as buffer + @skipIf(sys.version_info[0] == 2 or is_pypy) + def test_array(self): + a = array.array('B', [0, 255, 64]) + b = bitarray(None, 'little', a) + self.assertEqual(b, bitarray('00000000 11111111 00000010')) + a[1] = 32 + self.assertEqual(b, bitarray('00000000 00000100 00000010')) + b[3] = 1 + self.assertEqual(a.tolist(), [8, 32, 64]) + self.check_obj(b) + + def test_bitarray(self): + a = urandom(10000) + b = bitarray(buffer=a) + # a and b are two distinct bitarrays that share the same buffer now + self.assertFalse(a is b) + + a_info = buffer_info(a) + self.assertFalse(a_info['imported']) + self.assertEqual(a_info['exports'], 1) + b_info = buffer_info(b) + self.assertTrue(b_info['imported']) + self.assertEqual(b_info['exports'], 0) + # buffer address is the same! + self.assertEqual(a_info['address'], + b_info['address']) + + self.assertFalse(a is b) + self.assertEqual(a, b) + b[437:461] = 0 + self.assertEqual(a, b) + a[327:350] = 1 + self.assertEqual(a, b) + b[101:1187] <<= 79 + self.assertEqual(a, b) + a[100:9800:5] = 1 + self.assertEqual(a, b) + + self.assertRaisesMessage( + BufferError, + "cannot resize bitarray that is exporting buffers", + a.pop) + self.assertRaisesMessage( + BufferError, + "cannot resize imported buffer", + b.pop) + self.check_obj(a) + self.check_obj(b) + + def test_copy(self): + a = bitarray(buffer=b'XA') + self.assertTrue(a.readonly) + for b in [a.copy(), 3 * a, 5 * a, a & bitarray(16), + a >> 2, ~a, a + bitarray(8*'1'), + a[:], a[::2], a[[0, 1]], a[bitarray(16)]]: + self.assertFalse(b.readonly) + self.check_obj(b) + + @skipIf(is_pypy) + def test_bitarray_shared_sections(self): + a = urandom(0x2000) + b = bitarray(buffer=memoryview(a)[0x100:0x300]) + self.assertEqual(buffer_info(b, 'address'), + buffer_info(a, 'address') + 0x100) + c = bitarray(buffer=memoryview(a)[0x200:0x800]) + self.assertEqual(buffer_info(c, 'address'), + buffer_info(a, 'address') + 0x200) + self.assertEqual(a[8 * 0x100 : 8 * 0x300], b) + self.assertEqual(a[8 * 0x200 : 8 * 0x800], c) + a.setall(0) + b.setall(1) + c.setall(0) + + d = bitarray(0x2000) + d.setall(0) + d[8 * 0x100 : 8 * 0x200] = 1 + self.assertEqual(a, d) + + def test_bitarray_range(self): + for n in range(100): + a = urandom(n, self.random_endian()) + b = bitarray(buffer=a, endian=a.endian()) + # an imported buffer will never have padbits + self.assertEqual(b.padbits, 0) + self.assertEqual(len(b) % 8, 0) + self.assertEQUAL(b[:n], a) + self.check_obj(a) + self.check_obj(b) + + def test_bitarray_chain(self): + a = urandom(64) + d = {0: a} + for n in range(1, 100): + d[n] = bitarray(buffer=d[n - 1]) + + self.assertEqual(d[99], a) + a.setall(0) + self.assertEqual(d[99], zeros(64)) + a[:] = 1 + self.assertTrue(d[99].all()) + for c in d.values(): + self.check_obj(c) + + def test_frozenbitarray(self): + a = frozenbitarray('10011011 011') + self.assertTrue(a.readonly) + self.check_obj(a) + + b = bitarray(buffer=a) + self.assertTrue(b.readonly) # also readonly + self.assertRaises(TypeError, b.__setitem__, 1, 0) + self.check_obj(b) + + def test_invalid_buffer(self): + # these objects do not expose a buffer + for arg in (123, 1.23, [1, 2, 3], (1, 2, 3), {1: 2}, + set([1, 2, 3]),): + self.assertRaises(TypeError, bitarray, buffer=arg) + + @skipIf(is_pypy) + def test_del_import_object(self): + b = bytearray(100 * [0]) + a = bitarray(buffer=b) + del b + self.assertEqual(a, zeros(800)) + a.setall(1) + self.assertTrue(a.all()) + self.check_obj(a) + + @skipIf(is_pypy) + def test_readonly_errors(self): + a = bitarray(buffer=b'A') + info = buffer_info(a) + self.assertTrue(info['readonly']) + self.assertTrue(info['imported']) + + self.assertRaises(TypeError, a.append, True) + self.assertRaises(TypeError, a.bytereverse) + self.assertRaises(TypeError, a.clear) + self.assertRaises(TypeError, a.encode, {'a': bitarray('0')}, 'aa') + self.assertRaises(TypeError, a.extend, [0, 1, 0]) + self.assertRaises(TypeError, a.fill) + self.assertRaises(TypeError, a.frombytes, b'') + self.assertRaises(TypeError, a.insert, 0, 1) + self.assertRaises(TypeError, a.invert) + self.assertRaises(TypeError, a.pack, b'\0\0\xff') + self.assertRaises(TypeError, a.pop) + self.assertRaises(TypeError, a.remove, 1) + self.assertRaises(TypeError, a.reverse) + self.assertRaises(TypeError, a.setall, 0) + self.assertRaises(TypeError, a.sort) + self.assertRaises(TypeError, a.__delitem__, 0) + self.assertRaises(TypeError, a.__delitem__, slice(None, None, 2)) + self.assertRaises(TypeError, a.__setitem__, 0, 0) + self.assertRaises(TypeError, a.__iadd__, bitarray(8)) + self.assertRaises(TypeError, a.__ior__, bitarray(8)) + self.assertRaises(TypeError, a.__ixor__, bitarray(8)) + self.assertRaises(TypeError, a.__irshift__, 1) + self.assertRaises(TypeError, a.__ilshift__, 1) + self.check_obj(a) + + @skipIf(is_pypy) + def test_resize_errors(self): + a = bitarray(buffer=bytearray([123])) + info = buffer_info(a) + self.assertFalse(info['readonly']) + self.assertTrue(info['imported']) + + self.assertRaises(BufferError, a.append, True) + self.assertRaises(BufferError, a.clear) + self.assertRaises(BufferError, a.encode, {'a': bitarray('0')}, 'aa') + self.assertRaises(BufferError, a.extend, [0, 1, 0]) + self.assertRaises(BufferError, a.frombytes, b'a') + self.assertRaises(BufferError, a.insert, 0, 1) + self.assertRaises(BufferError, a.pack, b'\0\0\xff') + self.assertRaises(BufferError, a.pop) + self.assertRaises(BufferError, a.remove, 1) + self.assertRaises(BufferError, a.__delitem__, 0) + self.check_obj(a) + +# --------------------------- Buffer Export --------------------------------- + +class BufferExportTests(unittest.TestCase, Util): + + def test_read_simple(self): + a = bitarray('01000001 01000010 01000011', endian='big') + v = memoryview(a) + self.assertFalse(v.readonly) + self.assertEqual(buffer_info(a, 'exports'), 1) + self.assertEqual(len(v), 3) + self.assertEqual(v[0], 65 if is_py3k else 'A') + self.assertEqual(v.tobytes(), b'ABC') + a[13] = 1 + self.assertEqual(v.tobytes(), b'AFC') + + w = memoryview(a) # a second buffer export + self.assertFalse(w.readonly) + self.assertEqual(buffer_info(a, 'exports'), 2) + self.check_obj(a) + + def test_many_exports(self): + a = bitarray('01000111 01011111') + d = {} # put bitarrays in dict to key object around + for n in range(1, 20): + d[n] = bitarray(buffer=a) + self.assertEqual(buffer_info(a, 'exports'), n) + self.assertEqual(len(d[n]), 16) + self.check_obj(a) + + def test_range(self): + for n in range(100): + a = bitarray(n) + v = memoryview(a) + self.assertEqual(len(v), a.nbytes) + info = buffer_info(a) + self.assertFalse(info['readonly']) + self.assertFalse(info['imported']) + self.assertEqual(info['exports'], 1) + self.check_obj(a) + + def test_read_random(self): + a = bitarray() + a.frombytes(os.urandom(100)) + v = memoryview(a) + self.assertEqual(len(v), 100) + b = a[34 * 8 : 67 * 8] + self.assertEqual(v[34:67].tobytes(), b.tobytes()) + self.assertEqual(v.tobytes(), a.tobytes()) + self.check_obj(a) + + def test_resize(self): + a = bitarray('011', endian='big') + v = memoryview(a) + self.assertFalse(v.readonly) + self.assertRaises(BufferError, a.append, 1) + self.assertRaises(BufferError, a.clear) + self.assertRaises(BufferError, a.encode, {'a': bitarray('0')}, 'aa') + self.assertRaises(BufferError, a.extend, '0') + self.assertRaises(BufferError, a.frombytes, b'\0') + self.assertRaises(BufferError, a.insert, 0, 1) + self.assertRaises(BufferError, a.pack, b'\0') + self.assertRaises(BufferError, a.pop) + self.assertRaises(BufferError, a.remove, 1) + self.assertRaises(BufferError, a.__delitem__, slice(0, 8)) + a.fill() + self.assertEqual(v.tobytes(), a.tobytes()) + self.check_obj(a) + + def test_frozenbitarray(self): + a = frozenbitarray(40) + v = memoryview(a) + self.assertTrue(v.readonly) + self.assertEqual(len(v), 5) + self.assertEqual(v.tobytes(), a.tobytes()) + self.check_obj(a) + + def test_write(self): + a = zeros(8000) + v = memoryview(a) + self.assertFalse(v.readonly) + v[500] = 255 if is_py3k else '\xff' + self.assertEqual(a[3999:4009], bitarray('0111111110')) + a[4003] = 0 + self.assertEqual(a[3999:4009], bitarray('0111011110')) + v[301:304] = b'ABC' + self.assertEqual(a[300 * 8 : 305 * 8].tobytes(), b'\x00ABC\x00') + self.check_obj(a) + + @skipIf(sys.version_info[0] == 2) + def test_write_py3(self): + a = zeros(40) + m = memoryview(a) + v = m[1:4] + v[0] = 65 + v[1] = 66 + v[2] = 67 + self.assertEqual(a.tobytes(), b'\x00ABC\x00') + self.check_obj(a) + +# --------------------------------------------------------------------------- + +class TestsFrozenbitarray(unittest.TestCase, Util): + + def test_init(self): + a = frozenbitarray('110') + self.assertEqual(a, bitarray('110')) + self.assertEqual(a.to01(), '110') + self.assertIsInstance(a, bitarray) + self.assertIsType(a, 'frozenbitarray') + self.assertTrue(a.readonly) + self.check_obj(a) + + a = frozenbitarray(bitarray()) + self.assertEQUAL(a, frozenbitarray()) + self.assertIsType(a, 'frozenbitarray') + + for endian in 'big', 'little': + a = frozenbitarray(0, endian) + self.assertEqual(a.endian(), endian) + self.assertIsType(a, 'frozenbitarray') + + a = frozenbitarray(bitarray(0, endian)) + self.assertEqual(a.endian(), endian) + self.assertIsType(a, 'frozenbitarray') + + def test_methods(self): + # test a few methods which do not raise the TypeError + a = frozenbitarray('1101100') + self.assertEqual(a[2], 0) + self.assertEqual(a[:4].to01(), '1101') + self.assertEqual(a.count(), 4) + self.assertEqual(a.index(0), 2) + b = a.copy() + self.assertEqual(b, a) + self.assertIsType(b, 'frozenbitarray') + self.assertEqual(len(b), 7) + self.assertFalse(b.all()) + self.assertTrue(b.any()) + self.check_obj(a) + + def test_init_from_bitarray(self): + for a in self.randombitarrays(): + b = frozenbitarray(a) + self.assertFalse(b is a) + self.assertEQUAL(b, a) + c = frozenbitarray(b) + self.assertFalse(c is b) + self.assertEQUAL(c, b) + self.assertEqual(hash(c), hash(b)) + self.check_obj(b) + + def test_init_from_misc(self): + tup = 0, 1, 0, 1, 1, False, True + for obj in list(tup), tup, iter(tup), bitarray(tup): + a = frozenbitarray(obj) + self.assertEqual(a, bitarray(tup)) + + def test_repr(self): + a = frozenbitarray() + self.assertEqual(repr(a), "frozenbitarray()") + self.assertEqual(str(a), "frozenbitarray()") + a = frozenbitarray('10111') + self.assertEqual(repr(a), "frozenbitarray('10111')") + self.assertEqual(str(a), "frozenbitarray('10111')") + + def test_immutable(self): + a = frozenbitarray('111') + self.assertRaises(TypeError, a.append, True) + self.assertRaises(TypeError, a.bytereverse) + self.assertRaises(TypeError, a.clear) + self.assertRaises(TypeError, a.encode, {'a': bitarray('0')}, 'aa') + self.assertRaises(TypeError, a.extend, [0, 1, 0]) + self.assertRaises(TypeError, a.fill) + self.assertRaises(TypeError, a.frombytes, b'') + self.assertRaises(TypeError, a.insert, 0, 1) + self.assertRaises(TypeError, a.invert) + self.assertRaises(TypeError, a.pack, b'\0\0\xff') + self.assertRaises(TypeError, a.pop) + self.assertRaises(TypeError, a.remove, 1) + self.assertRaises(TypeError, a.reverse) + self.assertRaises(TypeError, a.setall, 0) + self.assertRaises(TypeError, a.sort) + self.assertRaises(TypeError, a.__delitem__, 0) + self.assertRaises(TypeError, a.__delitem__, slice(None, None, 2)) + self.assertRaises(TypeError, a.__setitem__, 0, 0) + self.assertRaises(TypeError, a.__iadd__, bitarray('010')) + self.assertRaises(TypeError, a.__ior__, bitarray('100')) + self.assertRaises(TypeError, a.__ixor__, bitarray('110')) + self.assertRaises(TypeError, a.__irshift__, 1) + self.assertRaises(TypeError, a.__ilshift__, 1) + self.check_obj(a) + + def test_copy(self): + a = frozenbitarray('101') + # not only .copy() creates new frozenbitarray which are read-only + for b in [a, a.copy(), 3 * a, 5 * a, a & bitarray('110'), + a >> 2, ~a, a + bitarray(8*'1'), + a[:], a[::2], a[[0, 1]], a[bitarray('011')]]: + self.assertIsType(b, 'frozenbitarray') + self.assertTrue(b.readonly) + self.check_obj(b) + + def test_freeze(self): + # not so much a test for frozenbitarray, but how it is initialized + a = bitarray(78) + self.assertFalse(a.readonly) # not readonly + a._freeze() + self.assertTrue(a.readonly) # readonly + + def test_memoryview(self): + a = frozenbitarray('01000001 01000010', 'big') + v = memoryview(a) + self.assertEqual(v.tobytes(), b'AB') + self.assertRaises(TypeError, v.__setitem__, 0, 255) + + def test_buffer_import_readonly(self): + b = bytes(bytearray([15, 95, 128])) + a = frozenbitarray(buffer=b, endian='big') + self.assertEQUAL(a, bitarray('00001111 01011111 10000000', 'big')) + info = buffer_info(a) + self.assertTrue(info['readonly']) + self.assertTrue(info['imported']) + + @skipIf(is_pypy) + def test_buffer_import_writable(self): + c = bytearray([15, 95]) + self.assertRaisesMessage( + TypeError, + "cannot import writable buffer into frozenbitarray", + frozenbitarray, buffer=c) + + def test_set(self): + a = frozenbitarray('1') + b = frozenbitarray('11') + c = frozenbitarray('01') + d = frozenbitarray('011') + s = set([a, b, c, d]) + self.assertEqual(len(s), 4) + self.assertTrue(d in s) + self.assertFalse(frozenbitarray('0') in s) + + def test_dictkey(self): + a = frozenbitarray('01') + b = frozenbitarray('1001') + d = {a: 123, b: 345} + self.assertEqual(d[frozenbitarray('01')], 123) + self.assertEqual(d[frozenbitarray(b)], 345) + + def test_dictkey2(self): # taken slightly modified from issue #74 + a1 = frozenbitarray([True, False]) + a2 = frozenbitarray([False, False]) + dct = {a1: "one", a2: "two"} + a3 = frozenbitarray([True, False]) + self.assertEqual(a3, a1) + self.assertEqual(dct[a3], 'one') + + def test_mix(self): + a = bitarray('110') + b = frozenbitarray('0011') + self.assertEqual(a + b, bitarray('1100011')) + a.extend(b) + self.assertEqual(a, bitarray('1100011')) + + def test_hash_endianness_simple(self): + a = frozenbitarray('1', 'big') + b = frozenbitarray('1', 'little') + self.assertEqual(a, b) + self.assertEqual(hash(a), hash(b)) + d = {a: 'value'} + self.assertEqual(d[b], 'value') + self.assertEqual(len(set([a, b])), 1) + + def test_hash_endianness_random(self): + for a in self.randombitarrays(): + a = frozenbitarray(a) + b = frozenbitarray(a, self.opposite_endian(a.endian())) + self.assertEqual(a, b) + self.assertNotEqual(a.endian(), b.endian()) + self.assertEqual(hash(a), hash(b)) + d = {a: 1, b: 2} + self.assertEqual(len(d), 1) + + def test_pickle(self): + for a in self.randombitarrays(): + f = frozenbitarray(a) + f.foo = 42 # unlike bitarray itself, we can have attributes + g = pickle.loads(pickle.dumps(f)) + self.assertEqual(f, g) + self.assertEqual(f.endian(), g.endian()) + self.assertTrue(str(g).startswith('frozenbitarray')) + self.assertTrue(g.readonly) + self.check_obj(a) + self.check_obj(f) + self.check_obj(g) + self.assertEqual(g.foo, 42) + +# --------------------------------------------------------------------------- + +def run(verbosity=1): + import bitarray.test_util + + default_endian = get_default_endian() + print('bitarray is installed in: %s' % os.path.dirname(__file__)) + print('bitarray version: %s' % __version__) + print('sys.version: %s' % sys.version) + print('sys.prefix: %s' % sys.prefix) + print('pointer size: %d bit' % (8 * SYSINFO[0])) + print('sizeof(size_t): %d' % SYSINFO[1]) + print('sizeof(bitarrayobject): %d' % SYSINFO[2]) + print('HAVE_BUILTIN_BSWAP64: %d' % SYSINFO[5]) + print('default bit-endianness: %s' % default_endian) + print('machine byte-order: %s' % sys.byteorder) + print('DEBUG: %s' % DEBUG) + loader = unittest.TestLoader() + suite = unittest.TestSuite() + suite.addTests(loader.loadTestsFromModule(sys.modules[__name__])) + suite.addTests(loader.loadTestsFromModule(bitarray.test_util)) + + runner = unittest.TextTestRunner(verbosity=verbosity) + result = runner.run(suite) + _set_default_endian(default_endian) + return result + +if __name__ == '__main__': + unittest.main() diff --git a/code/.venv/lib/python3.12/site-packages/bitarray/test_util.py b/code/.venv/lib/python3.12/site-packages/bitarray/test_util.py new file mode 100644 index 0000000..2b5aae0 --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/bitarray/test_util.py @@ -0,0 +1,2339 @@ +""" +Tests for bitarray.util module +""" +from __future__ import absolute_import + +import os +import sys +import base64 +import binascii +import shutil +import tempfile +import unittest +from array import array +from string import hexdigits +from random import choice, getrandbits, randrange, randint, random +from collections import Counter + +from bitarray import (bitarray, frozenbitarray, decodetree, bits2bytes, + _set_default_endian) +from bitarray.test_bitarray import Util, skipIf, SYSINFO, DEBUG + +from bitarray.util import ( + zeros, ones, urandom, pprint, make_endian, rindex, strip, count_n, + parity, count_and, count_or, count_xor, any_and, subset, _correspond_all, + intervals, + serialize, deserialize, ba2hex, hex2ba, ba2base, base2ba, + ba2int, int2ba, + sc_encode, sc_decode, vl_encode, vl_decode, + huffman_code, canonical_huffman, canonical_decode, +) + +if DEBUG: + from bitarray._util import _sc_rts, _SEGSIZE # type: ignore + +if sys.version_info[0] == 3: + from io import StringIO +else: + from io import BytesIO as StringIO + +# --------------------------------------------------------------------------- + +class TestsZerosOnes(unittest.TestCase): + + def test_range(self): + for n in range(100): + a = zeros(n) + self.assertEqual(len(a), n) + self.assertFalse(a.any()) + self.assertEqual(a.count(0), n) + self.assertEqual(a.count(1), 0) + self.assertIsInstance(a, bitarray) + b = ones(n) + self.assertEqual(len(b), n) + self.assertTrue(b.all()) + self.assertEqual(b.count(0), 0) + self.assertEqual(b.count(1), n) + self.assertIsInstance(b, bitarray) + + def test_endian(self): + for default_endian in 'big', 'little': + _set_default_endian(default_endian) + + for a in (zeros(0), zeros(0, None), zeros(0, endian=None), + ones(0), ones(0, None), ones(0, endian=None)): + self.assertEqual(a, bitarray()) + self.assertEqual(a.endian(), default_endian) + + for endian in 'big', 'little': + for a in zeros(3, endian), zeros(3, endian=endian): + self.assertEqual(a, bitarray('000')) + self.assertEqual(a.endian(), endian) + for b in ones(3, endian), ones(3, endian=endian): + self.assertEqual(b, bitarray('111')) + self.assertEqual(b.endian(), endian) + + def test_wrong_args(self): + for f in zeros, ones: + self.assertRaises(TypeError, f) # no argument + self.assertRaises(TypeError, f, '') + self.assertRaises(TypeError, f, bitarray()) + self.assertRaises(TypeError, f, []) + self.assertRaises(TypeError, f, 1.0) + self.assertRaises(ValueError, f, -1) + + # endian not string + for x in 0, 1, {}, [], False, True: + self.assertRaises(TypeError, f, 0, x) + # endian wrong string + self.assertRaises(ValueError, f, 0, 'foo') + +# --------------------------------------------------------------------------- + +class TestsURandom(unittest.TestCase): + + def test_basic(self): + for default_endian in 'big', 'little': + _set_default_endian(default_endian) + + for a in urandom(0), urandom(0, None), urandom(0, endian=None): + self.assertEqual(a, bitarray()) + self.assertEqual(a.endian(), default_endian) + + for n in range(50): + a = urandom(n) + self.assertEqual(len(a), n) + self.assertEqual(a.endian(), default_endian) + + for endian in 'big', 'little': + for a in urandom(11, endian), urandom(11, endian=endian): + self.assertEqual(len(a), 11) + self.assertEqual(a.endian(), endian) + + def test_count(self): + a = urandom(1000) + b = urandom(1000) + self.assertNotEqual(a, b) + self.assertTrue(400 < a.count() < 600) + self.assertTrue(400 < b.count() < 600) + + def test_wrong_args(self): + self.assertRaises(TypeError, urandom) + self.assertRaises(TypeError, urandom, '') + self.assertRaises(TypeError, urandom, bitarray()) + self.assertRaises(TypeError, urandom, []) + self.assertRaises(TypeError, urandom, 1.0) + self.assertRaises(ValueError, urandom, -1) + + self.assertRaises(TypeError, urandom, 0, 1) + self.assertRaises(ValueError, urandom, 0, 'foo') + +# --------------------------------------------------------------------------- + +class TestsPPrint(unittest.TestCase): + + @staticmethod + def get_code_string(a): + f = StringIO() + pprint(a, stream=f) + return f.getvalue() + + def round_trip(self, a): + b = eval(self.get_code_string(a)) + self.assertEqual(b, a) + self.assertEqual(type(b), type(a)) + + def test_bitarray(self): + a = bitarray('110') + self.assertEqual(self.get_code_string(a), "bitarray('110')\n") + self.round_trip(a) + + def test_frozenbitarray(self): + a = frozenbitarray('01') + self.assertEqual(self.get_code_string(a), "frozenbitarray('01')\n") + self.round_trip(a) + + def test_formatting(self): + a = bitarray(200) + for width in range(40, 130, 10): + for n in range(1, 10): + f = StringIO() + pprint(a, stream=f, group=n, width=width) + r = f.getvalue() + self.assertEqual(eval(r), a) + s = r.strip("bitary(')\n") + for group in s.split()[:-1]: + self.assertEqual(len(group), n) + for line in s.split('\n'): + self.assertTrue(len(line) < width) + + def test_fallback(self): + for a in None, 'asd', [1, 2], bitarray(), frozenbitarray('1'): + self.round_trip(a) + + def test_subclass(self): + class Foo(bitarray): + pass + + a = Foo() + code = self.get_code_string(a) + self.assertEqual(code, "Foo()\n") + b = eval(code) + self.assertEqual(b, a) + self.assertEqual(type(b), type(a)) + + def test_random(self): + for n in range(150): + self.round_trip(urandom(n)) + + def test_file(self): + tmpdir = tempfile.mkdtemp() + tmpfile = os.path.join(tmpdir, 'testfile') + a = bitarray(1000) + try: + with open(tmpfile, 'w') as fo: + pprint(a, fo) + with open(tmpfile, 'r') as fi: + b = eval(fi.read()) + self.assertEqual(a, b) + finally: + shutil.rmtree(tmpdir) + +# --------------------------------------------------------------------------- + +class TestsMakeEndian(unittest.TestCase, Util): + + def test_simple(self): + a = bitarray('1110001', endian='big') + b = make_endian(a, 'big') + self.assertTrue(b is a) + c = make_endian(a, endian='little') + self.assertTrue(c == a) + self.assertEqual(c.endian(), 'little') + self.assertIsType(c, 'bitarray') + + # wrong arguments + self.assertRaises(TypeError, make_endian, '', 'big') + self.assertRaises(TypeError, make_endian, bitarray(), 1) + self.assertRaises(ValueError, make_endian, bitarray(), 'foo') + + def test_empty(self): + a = bitarray(endian='little') + b = make_endian(a, 'big') + self.assertTrue(b == a) + self.assertEqual(len(b), 0) + self.assertEqual(b.endian(), 'big') + + def test_from_frozen(self): + a = frozenbitarray('1101111', 'big') + b = make_endian(a, 'big') + self.assertTrue(b is a) + c = make_endian(a, 'little') + self.assertTrue(c == a) + self.assertEqual(c.endian(), 'little') + #self.assertIsType(c, 'frozenbitarray') + + def test_random(self): + for a in self.randombitarrays(): + aa = a.copy() + for endian in 'big', 'little': + b = make_endian(a, endian) + self.assertEqual(a, b) + self.assertEqual(b.endian(), endian) + if a.endian() == endian: + self.assertTrue(b is a) + self.assertEQUAL(a, aa) + +# --------------------------------------------------------------------------- + +class TestsRIndex(unittest.TestCase, Util): + + def test_simple(self): + self.assertRaises(TypeError, rindex) + self.assertRaises(TypeError, rindex, None) + self.assertRaises(ValueError, rindex, bitarray(), 1) + for endian in 'big', 'little': + a = bitarray('00010110 000', endian) + self.assertEqual(rindex(a), 6) + self.assertEqual(rindex(a, 1), 6) + self.assertEqual(rindex(a, 1, 3), 6) + self.assertEqual(rindex(a, 1, 3, 8), 6) + self.assertEqual(rindex(a, 1, -20, 20), 6) + self.assertEqual(rindex(a, 1, 0, 5), 3) + self.assertEqual(rindex(a, 1, 0, -6), 3) + self.assertEqual(rindex(a, 1, 0, -5), 5) + self.assertRaises(TypeError, rindex, a, 'A') + self.assertRaises(ValueError, rindex, a, 2) + self.assertRaises(ValueError, rindex, a, 1, 7) + self.assertRaises(ValueError, rindex, a, 1, 10, 3) + self.assertRaises(ValueError, rindex, a, 1, -1, 0) + self.assertRaises(TypeError, rindex, a, 1, 10, 3, 4) + + a = bitarray('00010110 111', endian) + self.assertEqual(rindex(a, 0), 7) + self.assertEqual(rindex(a, 0, 0, 4), 2) + self.assertEqual(rindex(a, False), 7) + + a = frozenbitarray('00010110 111', endian) + self.assertEqual(rindex(a, 0), 7) + self.assertRaises(TypeError, rindex, a, None) + self.assertRaises(ValueError, rindex, a, 7) + + for v in 0, 1: + self.assertRaises(ValueError, rindex, + bitarray(0, endian), v) + self.assertRaises(ValueError, rindex, + bitarray('000', endian), 1) + self.assertRaises(ValueError, rindex, + bitarray('11111', endian), 0) + + def test_range(self): + n = 100 + a = bitarray(n) + for m in range(n): + a.setall(0) + self.assertRaises(ValueError, rindex, a, 1) + a[m] = 1 + self.assertEqual(rindex(a, 1), m) + + a.setall(1) + self.assertRaises(ValueError, rindex, a, 0) + a[m] = 0 + self.assertEqual(rindex(a, 0), m) + + def test_random(self): + for a in self.randombitarrays(): + v = getrandbits(1) + try: + i = rindex(a, v) + except ValueError: + i = None + s = a.to01() + try: + j = s.rindex(str(v)) + except ValueError: + j = None + self.assertEqual(i, j) + + def test_random_start_stop(self): + for _ in range(10): + n = randrange(1, 1000) + a = zeros(n) + indices = [randrange(n) for _ in range(100)] + a[indices] = 1 + start = randint(0, n) + stop = randint(0, n) + filtered = [i for i in indices if i >= start and i < stop] + ref = max(filtered) if filtered else -1 + try: + res = rindex(a, 1, start, stop) + except ValueError: + res = -1 + self.assertEqual(res, ref) + + def test_many_set(self): + for _ in range(10): + n = randint(1, 10000) + v = getrandbits(1) + a = bitarray(n) + a.setall(not v) + lst = [randrange(n) for _ in range(100)] + a[lst] = v + self.assertEqual(rindex(a, v), max(lst)) + + def test_one_set(self): + for _ in range(10): + N = randint(1, 10000) + a = zeros(N) + a[randrange(N)] = 1 + self.assertEqual(rindex(a), a.index(1)) + +# --------------------------------------------------------------------------- + +class TestsStrip(unittest.TestCase, Util): + + def test_simple(self): + self.assertRaises(TypeError, strip, '0110') + self.assertRaises(TypeError, strip, bitarray(), 123) + self.assertRaises(ValueError, strip, bitarray(), 'up') + for default_endian in 'big', 'little': + _set_default_endian(default_endian) + a = bitarray('00010110000') + self.assertEQUAL(strip(a), bitarray('0001011')) + self.assertEQUAL(strip(a, 'left'), bitarray('10110000')) + self.assertEQUAL(strip(a, 'both'), bitarray('1011')) + b = frozenbitarray('00010110000') + c = strip(b, 'both') + self.assertEqual(c, bitarray('1011')) + self.assertIsType(c, 'frozenbitarray') + + def test_zeros_ones(self): + for n in range(10): + for mode in 'left', 'right', 'both': + a = zeros(n) + c = strip(a, mode) + self.assertIsType(c, 'bitarray') + self.assertEqual(c, bitarray()) + self.assertEqual(a, zeros(n)) + + b = frozenbitarray(a) + c = strip(b, mode) + self.assertIsType(c, 'frozenbitarray') + self.assertEqual(c, bitarray()) + + a.setall(1) + c = strip(a, mode) + self.assertEqual(c, ones(n)) + + def test_random(self): + for a in self.randombitarrays(): + b = a.copy() + f = frozenbitarray(a) + s = a.to01() + for mode, res in [ + ('left', bitarray(s.lstrip('0'), a.endian())), + ('right', bitarray(s.rstrip('0'), a.endian())), + ('both', bitarray(s.strip('0'), a.endian())), + ]: + c = strip(a, mode) + self.assertEQUAL(c, res) + self.assertIsType(c, 'bitarray') + self.assertEQUAL(a, b) + + c = strip(f, mode) + self.assertEQUAL(c, res) + self.assertIsType(c, 'frozenbitarray') + self.assertEQUAL(f, b) + + def test_one_set(self): + for _ in range(10): + n = randint(1, 10000) + a = bitarray(n) + a.setall(0) + a[randrange(n)] = 1 + self.assertEqual(strip(a, 'both'), bitarray('1')) + self.assertEqual(len(a), n) + +# --------------------------------------------------------------------------- + +class TestsCount_N(unittest.TestCase, Util): + + @staticmethod + def count_n(a, n): + "return lowest index i for which a[:i].count() == n" + i, j = n, a.count(1, 0, n) + while j < n: + j += a[i] + i += 1 + return i + + def check_result(self, a, n, i, v=1): + self.assertEqual(a.count(v, 0, i), n) + if i == 0: + self.assertEqual(n, 0) + else: + self.assertEqual(a[i - 1], v) + + def test_empty(self): + a = bitarray() + self.assertEqual(count_n(a, 0), 0) + self.assertEqual(count_n(a, 0, 0), 0) + self.assertEqual(count_n(a, 0, 1), 0) + self.assertRaises(ValueError, count_n, a, 1) + self.assertRaises(TypeError, count_n, '', 0) + self.assertRaises(TypeError, count_n, a, 7.0) + self.assertRaises(ValueError, count_n, a, 0, 2) + self.assertRaisesMessage(ValueError, "n = 1 larger than bitarray " + "size (len(a) = 0)", count_n, a, 1) + + def test_simple(self): + a = bitarray('111110111110111110111110011110111110111110111000') + b = a.copy() + self.assertEqual(len(a), 48) + self.assertEqual(a.count(), 37) + self.assertEqual(a.count(0), 11) + + self.assertEqual(count_n(a, 0, 0), 0) + self.assertEqual(count_n(a, 2, 0), 12) + self.assertEqual(count_n(a, 10, 0), 47) + # n < 0 + self.assertRaisesMessage(ValueError, "non-negative integer expected", + count_n, a, -1, 0) + # n > len(a) + self.assertRaisesMessage(ValueError, "n = 49 larger than bitarray " + "size (len(a) = 48)", count_n, a, 49, 0) + # n > a.count(0) + self.assertRaisesMessage(ValueError, "n = 12 exceeds total count " + "(a.count(0) = 11)", + count_n, a, 12, 0) + + self.assertEqual(count_n(a, 0), 0) + self.assertEqual(count_n(a, 20), 23) + self.assertEqual(count_n(a, 20, 1), 23) + self.assertEqual(count_n(a, 37), 45) + # n < 0 + self.assertRaisesMessage(ValueError, "non-negative integer expected", + count_n, a, -1) + # n > len(a) + self.assertRaisesMessage(ValueError, "n = 49 larger than bitarray " + "size (len(a) = 48)", count_n, a, 49) + # n > a.count(1) + self.assertRaisesMessage(ValueError, "n = 38 exceeds total count " + "(a.count(1) = 37)", count_n, a, 38) + + for v in 0, 1: + for n in range(a.count(v) + 1): + i = count_n(a, n, v) + self.check_result(a, n, i, v) + self.assertEqual(a[:i].count(v), n) + self.assertEqual(i, self.count_n(a if v else ~a, n)) + self.assertEQUAL(a, b) + + def test_frozen(self): + a = frozenbitarray('001111101111101111101111100111100') + self.assertEqual(len(a), 33) + self.assertEqual(a.count(), 24) + self.assertEqual(count_n(a, 0), 0) + self.assertEqual(count_n(a, 10), 13) + self.assertEqual(count_n(a, 24), 31) + self.assertRaises(ValueError, count_n, a, -1) # n < 0 + self.assertRaises(ValueError, count_n, a, 25) # n > a.count() + self.assertRaises(ValueError, count_n, a, 34) # n > len(a) + for n in range(25): + self.check_result(a, n, count_n(a, n)) + + def test_ones(self): + n = randint(1, 100000) + a = ones(n) + self.assertEqual(count_n(a, n), n) + self.assertRaises(ValueError, count_n, a, 1, 0) + self.assertRaises(ValueError, count_n, a, n + 1) + for _ in range(20): + i = randint(0, n) + self.assertEqual(count_n(a, i), i) + + def test_one_set(self): + n = randint(1, 100000) + a = zeros(n) + self.assertEqual(count_n(a, 0), 0) + self.assertRaises(ValueError, count_n, a, 1) + for _ in range(20): + a.setall(0) + i = randrange(n) + a[i] = 1 + self.assertEqual(count_n(a, 1), i + 1) + self.assertRaises(ValueError, count_n, a, 2) + + def test_last(self): + for N in range(1, 1000): + a = zeros(N) + a[-1] = 1 + self.assertEqual(a.count(), 1) + self.assertEqual(count_n(a, 1), N) + if N == 1: + msg = "n = 2 larger than bitarray size (len(a) = 1)" + else: + msg = "n = 2 exceeds total count (a.count(1) = 1)" + self.assertRaisesMessage(ValueError, msg, count_n, a, 2) + + def test_large(self): + for _ in range(100): + N = randint(100000, 250000) + a = bitarray(N) + v = getrandbits(1) + a.setall(not v) + for _ in range(randrange(100)): + a[randrange(N)] = v + tc = a.count(v) # total count + i = count_n(a, tc, v) + self.check_result(a, tc, i, v) + n = tc + 1 + self.assertRaisesMessage(ValueError, "n = %d exceeds total count " + "(a.count(%d) = %d)" % (n, v, tc), + count_n, a, n, v) + for _ in range(20): + n = randint(0, tc) + i = count_n(a, n, v) + self.check_result(a, n, i, v) + + def test_random(self): + for a in self.randombitarrays(): + for v in 0, 1: + n = a.count(v) // 2 + i = count_n(a, n, v) + self.check_result(a, n, i, v) + # n = 0 -> count_n always 0 + self.assertEqual(count_n(a, 0, v), 0) + +# --------------------------------------------------------------------------- + +class TestsBitwiseCount(unittest.TestCase, Util): + + def test_count_byte(self): + for i in range(256): + a = bitarray() + a.frombytes(bytes(bytearray([i]))) + cnt = a.count() + self.assertEqual(count_and(a, zeros(8)), 0) + self.assertEqual(count_and(a, ones(8)), cnt) + self.assertEqual(count_and(a, a), cnt) + self.assertEqual(count_or(a, zeros(8)), cnt) + self.assertEqual(count_or(a, ones(8)), 8) + self.assertEqual(count_or(a, a), cnt) + self.assertEqual(count_xor(a, zeros(8)), cnt) + self.assertEqual(count_xor(a, ones(8)), 8 - cnt) + self.assertEqual(count_xor(a, a), 0) + + def test_1(self): + a = bitarray('001111') + aa = a.copy() + b = bitarray('010011') + bb = b.copy() + self.assertEqual(count_and(a, b), 2) + self.assertEqual(count_or(a, b), 5) + self.assertEqual(count_xor(a, b), 3) + for f in count_and, count_or, count_xor: + # not two arguments + self.assertRaises(TypeError, f) + self.assertRaises(TypeError, f, a) + self.assertRaises(TypeError, f, a, b, 3) + # wrong argument types + self.assertRaises(TypeError, f, a, '') + self.assertRaises(TypeError, f, '1', b) + self.assertRaises(TypeError, f, a, 4) + self.assertEQUAL(a, aa) + self.assertEQUAL(b, bb) + + b.append(1) + for f in count_and, count_or, count_xor: + self.assertRaises(ValueError, f, a, b) + self.assertRaises(ValueError, f, + bitarray('110', 'big'), + bitarray('101', 'little')) + + def test_frozen(self): + a = frozenbitarray('001111') + b = frozenbitarray('010011') + self.assertEqual(count_and(a, b), 2) + self.assertEqual(count_or(a, b), 5) + self.assertEqual(count_xor(a, b), 3) + + def test_random(self): + for _ in range(100): + n = randrange(1000) + a = urandom(n, self.random_endian()) + b = urandom(n, a.endian()) + self.assertEqual(count_and(a, b), (a & b).count()) + self.assertEqual(count_or(a, b), (a | b).count()) + self.assertEqual(count_xor(a, b), (a ^ b).count()) + + def test_misc(self): + for a in self.randombitarrays(): + n = len(a) + b = urandom(n, a.endian()) + # any and + self.assertEqual(any(a & b), count_and(a, b) > 0) + self.assertEqual(any_and(a, b), any(a & b)) + # any or + self.assertEqual(any(a | b), count_or(a, b) > 0) + self.assertEqual(any(a | b), any(a) or any(b)) + # any xor + self.assertEqual(any(a ^ b), count_xor(a, b) > 0) + self.assertEqual(any(a ^ b), a != b) + + # all and + self.assertEqual(all(a & b), count_and(a, b) == n) + self.assertEqual(all(a & b), all(a) and all(b)) + # all or + self.assertEqual(all(a | b), count_or(a, b) == n) + # all xor + self.assertEqual(all(a ^ b), count_xor(a, b) == n) + self.assertEqual(all(a ^ b), a == ~b) + +# --------------------------------------------------------------------------- + +class TestsBitwiseAny(unittest.TestCase, Util): + + def test_basic(self): + a = frozenbitarray('0101') + b = bitarray('0111') + self.assertTrue(any_and(a, b)) + self.assertRaises(TypeError, any_and) + self.assertRaises(TypeError, any_and, a, 4) + b.append(1) + self.assertRaises(ValueError, any_and, a, b) + self.assertRaises(ValueError, any_and, + bitarray('01', 'little'), + bitarray('11', 'big')) + + def check(self, a, b): + r = any_and(a, b) + self.assertIsInstance(r, bool) + self.assertEqual(r, any_and(b, a)) # symmetry + self.assertEqual(r, any(a & b)) + self.assertEqual(r, (a & b).any()) + self.assertEqual(r, count_and(a, b) > 0) + + def test_explitcit(self): + for a, b , res in [ + ('', '', False), + ('0', '1', False), + ('0', '0', False), + ('1', '1', True), + ('00011', '11100', False), + ('00001011 1', '01000100 1', True)]: + a = bitarray(a) + b = bitarray(b) + self.assertTrue(any_and(a, b) is res) + self.check(a, b) + + def test_random(self): + for a in self.randombitarrays(): + n = len(a) + b = urandom(n, a.endian()) + self.check(a, b) + + def test_one(self): + for n in range(1, 300): + a = zeros(n) + b = urandom(n) + i = randrange(n) + a[i] = 1 + self.assertEqual(b[i], any_and(a, b)) + +# --------------------------------------------------------------------------- + +class TestsSubset(unittest.TestCase, Util): + + def test_basic(self): + a = frozenbitarray('0101') + b = bitarray('0111') + self.assertTrue(subset(a, b)) + self.assertFalse(subset(b, a)) + self.assertRaises(TypeError, subset) + self.assertRaises(TypeError, subset, a, '') + self.assertRaises(TypeError, subset, '1', b) + self.assertRaises(TypeError, subset, a, 4) + b.append(1) + self.assertRaises(ValueError, subset, a, b) + self.assertRaises(ValueError, subset, + bitarray('01', 'little'), + bitarray('11', 'big')) + + def check(self, a, b, res): + r = subset(a, b) + self.assertIsInstance(r, bool) + self.assertEqual(r, res) + self.assertEqual(a | b == b, res) + self.assertEqual(a & b == a, res) + + def test_True(self): + for a, b in [('', ''), ('0', '1'), ('0', '0'), ('1', '1'), + ('000', '111'), ('0101', '0111'), + ('000010111', '010011111')]: + self.check(bitarray(a), bitarray(b), True) + + def test_False(self): + for a, b in [('1', '0'), ('1101', '0111'), + ('0000101111', '0100111011')]: + self.check(bitarray(a), bitarray(b), False) + + def test_random(self): + for a in self.randombitarrays(start=1): + b = a.copy() + # we set one random bit in b to 1, so a is always a subset of b + b[randrange(len(a))] == 1 + self.check(a, b, True) + # but b is only a subset when they are equal + self.check(b, a, a == b) + # we set all bits in a, which ensures that b is a subset of a + a.setall(1) + self.check(b, a, True) + +# --------------------------------------------------------------------------- + +class TestsCorrespondAll(unittest.TestCase, Util): + + def test_basic(self): + a = frozenbitarray('0101') + b = bitarray('0111') + self.assertTrue(_correspond_all(a, b), (1, 1, 1, 1)) + self.assertRaises(TypeError, _correspond_all) + b.append(1) + self.assertRaises(ValueError, _correspond_all, a, b) + self.assertRaises(ValueError, _correspond_all, + bitarray('01', 'little'), + bitarray('11', 'big')) + + def test_explitcit(self): + for a, b, res in [ + ('', '', (0, 0, 0, 0)), + ('0000011111', + '0000100111', (4, 1, 2, 3)), + ]: + self.assertEqual(_correspond_all(bitarray(a), bitarray(b)), res) + + def test_random(self): + for a in self.randombitarrays(): + n = len(a) + b = urandom(n, a.endian()) + res = _correspond_all(a, b) + self.assertEqual(res[0], count_and(~a, ~b)) + self.assertEqual(res[1], count_and(~a, b)) + self.assertEqual(res[2], count_and(a, ~b)) + self.assertEqual(res[3], count_and(a, b)) + + self.assertEqual(res[0], n - count_or(a, b)) + self.assertEqual(res[1] + res[2], count_xor(a, b)) + self.assertEqual(sum(res), n) + +# --------------------------------------------------------------------------- + +class TestsParity(unittest.TestCase, Util): + + def test_bitarray(self): + a = bitarray() + self.assertBitEqual(parity(a), 0) + par = False + for _ in range(1000): + self.assertEqual(parity(a), par) + a.append(1) + par = not par + + def test_pad_ignored(self): + a = ones(1) + self.assertTrue(parity(a)) + + def test_frozenbitarray(self): + for s, p in [('', 0), ('0010011', 1), ('10100110', 0)]: + self.assertBitEqual(parity(frozenbitarray(s)), p) + + def test_wrong_args(self): + self.assertRaises(TypeError, parity, '') + self.assertRaises(TypeError, bitarray(), 1) + + def test_byte(self): + for i in range(256): + a = bitarray() + a.frombytes(bytes(bytearray([i]))) + self.assertEqual(parity(a), a.count() % 2) + + def test_random(self): + for a in self.randombitarrays(): + b = a.copy() + self.assertEqual(parity(a), a.count() % 2) + self.assertEqual(a, b) + +# --------------------------------------------------------------------------- + +class TestsIntervals(unittest.TestCase, Util): + + def test_explicit(self): + for s, lst in [ + ('', []), + ('0', [(0, 0, 1)]), + ('1', [(1, 0, 1)]), + ('00111100 00000111 00', + [(0, 0, 2), (1, 2, 6), (0, 6, 13), (1, 13, 16), (0, 16, 18)]), + ]: + a = bitarray(s) + self.assertEqual(list(intervals(a)), lst) + + def test_count(self): + for s, res in [ + ('', 0), + ('0', 1), + ('1', 1), + ('00', 1), + ('01', 2), + ('10', 2), + ('11', 1), + ('0011110000000', 3), + ]: + a = bitarray(s) + self.assertEqual(res, len(list(intervals(a)))) + self.assertEqual(res, sum(1 for _ in intervals(a))) + + def test_random(self): + for a in self.randombitarrays(): + b = urandom(len(a)) + cnt = [0, 0] + v = a[0] if a else None + for value, start, stop in intervals(a): + self.assertFalse(isinstance(value, bool)) + self.assertEqual(value, v) + v = not v + self.assertTrue(0 <= start < stop <= len(a)) + cnt[value] += stop - start + b[start:stop] = value + self.assertEqual(a, b) + for v in 0, 1: + self.assertEqual(cnt[v], a.count(v)) + + def test_runs(self): + for a in self.randombitarrays(): + first = a[0] if a else None + # list runs of alternating bits + runs = [stop - start for _, start, stop in intervals(a)] + + b = bitarray() + v = first + for length in runs: + b.extend(length * bitarray([v])) + v = not v + + self.assertEqual(a, b) + +# --------------------------------------------------------------------------- + +class TestsHexlify(unittest.TestCase, Util): + + def test_ba2hex(self): + self.assertEqual(ba2hex(bitarray(0, 'big')), '') + self.assertEqual(ba2hex(bitarray('1110', 'big')), 'e') + self.assertEqual(ba2hex(bitarray('1110', 'little')), '7') + self.assertEqual(ba2hex(bitarray('0000 0001', 'big')), '01') + self.assertEqual(ba2hex(bitarray('1000 0000', 'big')), '80') + self.assertEqual(ba2hex(bitarray('0000 0001', 'little')), '08') + self.assertEqual(ba2hex(bitarray('1000 0000', 'little')), '10') + self.assertEqual(ba2hex(frozenbitarray('1100 0111', 'big')), 'c7') + # length not multiple of 4 + self.assertRaises(ValueError, ba2hex, bitarray('10')) + self.assertRaises(TypeError, ba2hex, '101') + + c = ba2hex(bitarray('1101', 'big')) + self.assertIsInstance(c, str) + + for n in range(7): + a = bitarray(n * '1111', 'big') + b = a.copy() + self.assertEqual(ba2hex(a), n * 'f') + # ensure original object wasn't altered + self.assertEQUAL(a, b) + + def test_hex2ba(self): + _set_default_endian('big') + self.assertEqual(hex2ba(''), bitarray()) + for c in 'e', 'E', b'e', b'E', u'e', u'E': + a = hex2ba(c) + self.assertEqual(a.to01(), '1110') + self.assertEqual(a.endian(), 'big') + self.assertIsType(a, 'bitarray') + self.assertEQUAL(hex2ba('01'), bitarray('0000 0001', 'big')) + self.assertEQUAL(hex2ba('08', 'little'), + bitarray('0000 0001', 'little')) + self.assertEQUAL(hex2ba('aD'), bitarray('1010 1101', 'big')) + self.assertEQUAL(hex2ba(b'10aF'), + bitarray('0001 0000 1010 1111', 'big')) + self.assertEQUAL(hex2ba(b'10aF', 'little'), + bitarray('1000 0000 0101 1111', 'little')) + + def test_hex2ba_errors(self): + self.assertRaises(TypeError, hex2ba, 0) + + for endian in 'little', 'big': + _set_default_endian(endian) + self.assertRaises(ValueError, hex2ba, '01a7g89') + self.assertRaises(ValueError, hex2ba, u'0\u20ac') + + for s in 'g', 'ag', 'aag' 'aaaga', 'ag': + msg = "non-hexadecimal digit found, got 'g' (0x67)" + self.assertRaisesMessage(ValueError, msg, hex2ba, s, endian) + + # check for NUL bytes + for b in b'\0', b'\0f', b'f\0', b'\0ff', b'f\0f', b'ff\0': + msg = "non-hexadecimal digit found, got '\0' (0x00)" + if sys.version_info[0] == 2: + msg = msg.replace("0x00", "0x0") + self.assertRaisesMessage(ValueError, msg, hex2ba, b, endian) + + def test_explicit(self): + data = [ # little big + ('', '', ''), + ('1000', '1', '8'), + ('1000 1100', '13', '8c'), + ('1000 1100 1110', '137', '8ce'), + ('1000 1100 1110 1111' , '137f', '8cef'), + ('1000 1100 1110 1111 0100', '137f2', '8cef4'), + ] + for bs, hex_le, hex_be in data: + a_be = bitarray(bs, 'big') + a_le = bitarray(bs, 'little') + self.assertEQUAL(hex2ba(hex_be, 'big'), a_be) + self.assertEQUAL(hex2ba(hex_le, 'little'), a_le) + self.assertEqual(ba2hex(a_be), hex_be) + self.assertEqual(ba2hex(a_le), hex_le) + + def test_hexdigits(self): + for default_endian in 'big', 'little': + _set_default_endian(default_endian) + a = hex2ba(hexdigits) + self.assertEqual(len(a) % 4, 0) + self.assertEqual(a.endian(), default_endian) + self.assertIsType(a, 'bitarray') + self.check_obj(a) + + t = ba2hex(a) + self.assertEqual(t, hexdigits.lower()) + self.assertIsInstance(t, str) + self.assertEQUAL(a, hex2ba(t, default_endian)) + + def test_binascii(self): + a = urandom(80, 'big') + s = binascii.hexlify(a.tobytes()).decode() + self.assertEqual(ba2hex(a), s) + b = bitarray(endian='big') + b.frombytes(binascii.unhexlify(s)) + self.assertEQUAL(hex2ba(s, 'big'), b) + +# --------------------------------------------------------------------------- + +class TestsBase(unittest.TestCase, Util): + + def test_ba2base(self): + c = ba2base(16, bitarray('1101', 'big')) + self.assertIsInstance(c, str) + + def test_base2ba(self): + _set_default_endian('big') + for c in 'e', 'E', b'e', b'E', u'e', u'E': + a = base2ba(16, c) + self.assertEqual(a.to01(), '1110') + self.assertEqual(a.endian(), 'big') + self.assertIsType(a, 'bitarray') + + def test_explicit(self): + data = [ # n little big + ('', 2, '', ''), + ('1 0 1', 2, '101', '101'), + ('11 01 00', 4, '320', '310'), + ('111 001', 8, '74', '71'), + ('1111 0001', 16, 'f8', 'f1'), + ('11111 00001', 32, '7Q', '7B'), + ('111111 000001', 64, '/g', '/B'), + ] + for bs, n, s_le, s_be in data: + a_le = bitarray(bs, 'little') + a_be = bitarray(bs, 'big') + self.assertEQUAL(base2ba(n, s_le, 'little'), a_le) + self.assertEQUAL(base2ba(n, s_be, 'big'), a_be) + self.assertEqual(ba2base(n, a_le), s_le) + self.assertEqual(ba2base(n, a_be), s_be) + + def test_empty(self): + for n in 2, 4, 8, 16, 32, 64: + a = base2ba(n, '') + self.assertEqual(a, bitarray()) + self.assertEqual(ba2base(n, a), '') + + def test_upper(self): + self.assertEqual(base2ba(16, 'F'), bitarray('1111')) + + def test_invalid_characters(self): + for n, s in ((2, '2'), (4, '4'), (8, '8'), (16, 'g'), (32, '8'), + (32, '1'), (32, 'a'), (64, '-'), (64, '_')): + if n == 16: + msg = "non-hexadecimal digit found, got 'g' (0x67)" + else: + msg = ("invalid digit found for base %d, " + "got '%s' (0x%02x)" % (n, s, ord(s))) + self.assertRaisesMessage(ValueError, msg, base2ba, n, s) + + def test_invalid_args(self): + a = bitarray() + self.assertRaises(TypeError, ba2base, None, a) + self.assertRaises(TypeError, base2ba, None, '') + self.assertRaises(TypeError, ba2base, 16.0, a) + self.assertRaises(TypeError, base2ba, 16.0, '') + for i in range(-10, 260): + if i in (2, 4, 8, 16, 32, 64): + continue + self.assertRaises(ValueError, ba2base, i, a) + self.assertRaises(ValueError, base2ba, i, '') + + self.assertRaises(TypeError, ba2base, 32, None) + self.assertRaises(TypeError, base2ba, 32, None) + + for i in 2, 4, 8, 16, 32, 64: + self.assertRaises(ValueError, base2ba, i, 60 * u'\u20ac') + self.assertRaises(ValueError, base2ba, i, 60 * b'\0') + + def test_binary(self): + a = base2ba(2, '1011') + self.assertEqual(a, bitarray('1011')) + self.assertEqual(ba2base(2, a), '1011') + + for a in self.randombitarrays(): + s = ba2base(2, a) + self.assertEqual(s, a.to01()) + self.assertEQUAL(base2ba(2, s, a.endian()), a) + + def test_quaternary(self): + a = base2ba(4, '0123', 'big') + self.assertEqual(a, bitarray('00 01 10 11')) + self.assertEqual(ba2base(4, a), '0123') + + def test_octal(self): + a = base2ba(8, '0147', 'big') + self.assertEqual(a, bitarray('000 001 100 111')) + self.assertEqual(ba2base(8, a), '0147') + + def test_hexadecimal(self): + a = base2ba(16, 'F61', 'big') + self.assertEqual(a, bitarray('1111 0110 0001')) + self.assertEqual(ba2base(16, a), 'f61') + + for n in range(50): + s = ''.join(choice(hexdigits) for _ in range(n)) + for endian in 'big', 'little': + a = base2ba(16, s, endian) + self.assertEQUAL(a, hex2ba(s, endian)) + self.assertEqual(ba2base(16, a), ba2hex(a)) + + def test_base32(self): + a = base2ba(32, '7SH', 'big') + self.assertEqual(a, bitarray('11111 10010 00111')) + self.assertEqual(ba2base(32, a), '7SH') + + msg = os.urandom(randint(10, 100) * 5) + s = base64.b32encode(msg).decode() + a = base2ba(32, s, 'big') + self.assertEqual(a.tobytes(), msg) + self.assertEqual(ba2base(32, a), s) + + def test_base64(self): + a = base2ba(64, '/jH', 'big') + self.assertEqual(a, bitarray('111111 100011 000111')) + self.assertEqual(ba2base(64, a), '/jH') + + msg = os.urandom(randint(10, 100) * 3) + s = base64.standard_b64encode(msg).decode() + a = base2ba(64, s, 'big') + self.assertEqual(a.tobytes(), msg) + self.assertEqual(ba2base(64, a), s) + + def test_alphabets(self): + for m, n, alpabet in [ + (1, 2, '01'), + (2, 4, '0123'), + (3, 8, '01234567'), + (4, 16, '0123456789abcdef'), + (5, 32, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567'), + (6, 64, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' + 'abcdefghijklmnopqrstuvwxyz0123456789+/'), + ]: + self.assertEqual(1 << m, n) + self.assertEqual(len(alpabet), n) + for i, c in enumerate(alpabet): + for endian in 'big', 'little': + self.assertEqual(ba2int(base2ba(n, c, endian)), i) + self.assertEqual(ba2base(n, int2ba(i, m, endian)), c) + + def test_random(self): + for a in self.randombitarrays(): + for m in range(1, 7): + n = 1 << m + if len(a) % m == 0: + s = ba2base(n, a) + b = base2ba(n, s, a.endian()) + self.assertEQUAL(a, b) + self.check_obj(b) + else: + self.assertRaises(ValueError, ba2base, n, a) + + def test_random2(self): + for m in range(1, 7): + n = 1 << m + for length in range(0, 100, m): + a = urandom(length, 'little') + s = ba2base(n, a) + self.assertIsInstance(s, str) + self.assertEQUAL(base2ba(n, s, 'little'), a) + b = bitarray(a, 'big') + self.assertEQUAL(base2ba(n, ba2base(n, b), 'big'), b) + +# --------------------------------------------------------------------------- + +class SC_Tests(unittest.TestCase, Util): + + def test_explicit(self): + for b, bits, endian in [ + (b'\x00\0', '', 'little'), + (b'\x01\x03\x01\x03\0', '110', 'little'), + (b'\x11\x07\x01\x02\0', '0000001', 'big'), + (b'\x01\x10\x02\xf0\x0f\0', '00001111 11110000', 'little'), + (b'\x11\x10\xa1\x0c\0', '00000000 00001000', 'big'), + (b'\x11\x09\xa1\x08\0', '00000000 1', 'big'), + (b'\x01E\xa3ABD\0', 65 * '0' + '1101', 'little'), + ]: + a = bitarray(bits, endian) + self.assertEqual(sc_encode(a), b) + self.assertEqual(sc_decode(b), a) + + def test_decode_header_nbits(self): + for b, n in [ + (b'\x00\0', 0), + (b'\x01\x00\0', 0), + (b'\x01\x01\0', 1), + (b'\x02\x00\x00\0', 0), + (b'\x02\x00\x01\0', 256), + (b'\x03\x00\x00\x00\0', 0), + (b'\x03\x00\x00\x01\0', 65536), + ]: + a = sc_decode(b) + self.assertEqual(len(a), n) + self.assertFalse(a.any()) + + @skipIf(sys.version_info[0] == 2) + def test_decode_untouch(self): + stream = iter(b'\x01\x03\x01\x03\0XYZ') + self.assertEqual(sc_decode(stream), bitarray('110')) + self.assertEqual(next(stream), ord('X')) + + stream = iter([0x11, 0x05, 0x01, 0xff, 0, None, 'foo']) + self.assertEqual(sc_decode(stream), bitarray('11111')) + self.assertTrue(next(stream) is None) + self.assertEqual(next(stream), 'foo') + + @skipIf(sys.version_info[0] == 2) + def test_decode_header_errors(self): + # invalid header + for c in 0x20, 0x21, 0x40, 0x80, 0xc0, 0xf0, 0xff: + self.assertRaisesMessage(ValueError, + "invalid header: 0x%02x" % c, + sc_decode, + bytearray([c])) + # invalid block head + for c in 0xc0, 0xc1, 0xc5, 0xff: + self.assertRaisesMessage(ValueError, + "invalid block head: 0x%02x" % c, + sc_decode, + bytearray([0x01, 0x10, c])) + + def test_decode_header_overflow(self): + nbytes = SYSINFO[1] + self.assertRaisesMessage( + OverflowError, + "sizeof(Py_ssize_t) = %d: cannot read 9 bytes" % nbytes, + sc_decode, b'\x09' + 9 * b'\x00') + + self.assertRaisesMessage( + ValueError, + "read %d bytes got negative value: -1" % nbytes, + sc_decode, bytes(bytearray([nbytes] + nbytes * [0xff]))) + + if nbytes == 4: + self.assertRaisesMessage( + OverflowError, + "sizeof(Py_ssize_t) = 4: cannot read 5 bytes", + sc_decode, b'\x05' + 5 * b'\x00') + + self.assertRaisesMessage( + ValueError, + "read 4 bytes got negative value: -2147483648", + sc_decode, b'\x04\x00\x00\x00\x80') + + def test_decode_errors(self): + # too many raw bytes + self.assertRaisesMessage( + ValueError, "decode error (raw): 0 + 2 > 1", + sc_decode, b"\x01\x05\x02\xff\xff\0") + self.assertRaisesMessage( + ValueError, "decode error (raw): 32 + 3 > 34", + sc_decode, b"\x02\x0f\x01\xa0\x03\xff\xff\xff\0") + # sparse index too high + self.assertRaisesMessage( + ValueError, "decode error (n=1): 128 >= 128", + sc_decode, b"\x01\x80\xa1\x80\0") + self.assertRaisesMessage( + ValueError, "decode error (n=2): 512 >= 512", + sc_decode, b"\x02\x00\x02\xc2\x01\x00\x02\0") + self.assertRaisesMessage( + ValueError, "decode error (n=3): 32768 >= 32768", + sc_decode, b"\x02\x00\x80\xc3\x01\x00\x80\x00\0") + + if SYSINFO[1] == 4: + msg = "read 4 bytes got negative value: -2147483648" + else: + msg = "decode error (n=4): 2147483648 >= 16" + self.assertRaisesMessage( + ValueError, msg, + sc_decode, b"\x01\x10\xc4\x01\x00\x00\x00\x80\0") + + if SYSINFO[1] == 4: + msg = "read 4 bytes got negative value: -1" + else: + msg = "decode error (n=4): 4294967295 >= 16" + self.assertRaisesMessage( + ValueError, msg, + sc_decode, b"\x01\x10\xc4\x01\xff\xff\xff\xff\0") + + def test_decode_end_of_stream(self): + for stream in [b'', b'\x00', b'\x01', b'\x02\x77', + b'\x01\x04\x01', b'\x01\x04\xa1', b'\x01\x04\xa0']: + self.assertRaisesMessage(ValueError, "unexpected end of stream", + sc_decode, stream) + + @skipIf(sys.version_info[0] == 2) + def test_decode_types(self): + blob = b'\x11\x03\x01\x20\0' + for b in blob, bytearray(blob), list(blob), array('B', blob): + a = sc_decode(b) + self.assertIsType(a, 'bitarray') + self.assertEqual(a.endian(), 'big') + self.assertEqual(a.to01(), '001') + + self.assertRaises(TypeError, sc_decode, [0x02, None]) + for x in None, 3, 3.2, Ellipsis: + self.assertRaises(TypeError, sc_decode, x) + for _ in range(10): + self.assertRaises(TypeError, sc_decode, [0x00, None]) + + def test_decode_ambiguity(self): + for b in [ + # raw: + b'\x11\x03\x01\x20\0', # this is what sc_encode gives us + b'\x11\x03\x01\x2f\0', # some pad bits are 1 + # sparse: + b'\x11\x03\xa1\x02\0', # using block type 1 + b'\x11\x03\xc2\x01\x02\x00\0', # using block type 2 + b'\x11\x03\xc3\x01\x02\x00\x00\0', # using block type 3 + b'\x11\x03\xc4\x01\x02\x00\x00\x00\0', # using block type 4 + ]: + a = sc_decode(b) + self.assertEqual(a.to01(), '001') + + @skipIf(sys.version_info[0] == 2) + def test_sparse_block_type1(self): + a = bitarray(256, 'little') + for n in range(1, 32): + positions = os.urandom(n) + b = bytearray([0x02, 0x00, 0x01, 0xa0 + n]) + b.extend(positions) + b.append(0) # stop byte + + a.setall(0) + a[positions] = 1 + self.assertEqual(sc_decode(b), a) + + # in order to recreate the block sc_encode generates, we need + # a sorted list of the positions with no duplicates + lst = sorted(set(positions)) + b = bytearray([0x02, 0x00, 0x01, 0xa0 + len(lst)]) + b.extend(lst) + b.append(0) # stop + + self.assertEqual(sc_decode(b), a) + self.assertEqual(sc_encode(a), bytes(b)) + + def test_decode_random_bytes(self): + # ensure random input doesn't crash the decoder + for _ in range(100): + n = randrange(20) + b = b'\x02\x00\x04' + os.urandom(n) + try: + a = sc_decode(b) + except ValueError as e: + if e != 'unexpected end of stream': + continue + self.assertEqual(len(a), 1024) + self.assertEqual(a.endian(), 'little') + + def test_encode_types(self): + for a in bitarray('1', 'big'), frozenbitarray('1', 'big'): + b = sc_encode(a) + self.assertIsInstance(b, bytes) + self.assertEqual(b, b'\x11\x01\x01\x80\0') + + for a in None, [], 0, 123, b'', b'\x00', 3.14: + self.assertRaises(TypeError, sc_encode, a) + + def round_trip(self, a): + c = a.copy() + i = iter(sc_encode(a)) + b = sc_decode(i) + self.assertTrue(a == b == c) + self.assertTrue(a.endian() == b.endian() == c.endian()) + if sys.version_info[0] == 3: + self.assertEqual(bytes(i), b'') + + def test_encode_zeros(self): + for i in range(18): + n = 1 << i + a = zeros(n) + m = 2 # head byte and stop byte + m += bits2bytes(n.bit_length()) # size bytes + #print(i, n, m, sc_encode(a)) + self.assertEqual(m, len(sc_encode(a))) + self.round_trip(a) + + a[0] = 1 + m += 2 # block head byte and one index byte + m += 2 * bool(n > 512) # second block head and second index byte + m += bool(n > 65536) # third index byte + self.assertEqual(m, len(sc_encode(a))) + self.round_trip(a) + + a = zeros(1 << 25, 'big') + a[0] = 1 + self.assertEqual( + sc_encode(a), + b'\x14\x00\x00\x00\x02\xc4\x01\x00\x00\x00\x00\x00') + + def test_encode_ones(self): + for _ in range(50): + nbits = randrange(100000) + a = ones(nbits) + m = 2 # head byte and stop byte + m += bits2bytes(nbits.bit_length()) # size bytes + nbytes = bits2bytes(nbits) + m += (nbytes // 32 + 127) // 128 # number of blocks (head bytes) + m += bool(nbytes % 32) # block type 0 range(1, 32) + m += nbytes # actual raw bytes + self.assertEqual(m, len(sc_encode(a))) + self.round_trip(a) + + def test_random(self): + for _ in range(10): + n = randrange(100000) + endian = self.random_endian() + a = ones(n, endian) + for _ in range(16): + a &= urandom(n, endian) + self.round_trip(a) + + @skipIf(not DEBUG) + def test_rts_empty(self): + rts = _sc_rts(bitarray()) + self.assertEqual(len(rts), 1) + self.assertEqual(rts, [0]) + + @skipIf(not DEBUG or _SEGSIZE != 32) + def test_rts_example(self): + # see example before sc_calc_rts() in _util.c + a = zeros(987) + a[:5] = a[512:515] = a[768:772] = 1 + self.assertEqual(a.count(), 12) + rts = _sc_rts(a) + self.assertEqual(len(rts), 5) + self.assertEqual(rts, [0, 5, 5, 8, 12]) + + @skipIf(not DEBUG) + def test_rts_ones(self): + for _ in range(20): + n = randrange(10000) + a = ones(n) + rts = _sc_rts(a) + self.assertEqual(rts[0], 0) + self.assertEqual(rts[-1], n) + for i, v in enumerate(rts): + self.assertEqual(v, min(8 * _SEGSIZE * i, n)) + + @skipIf(not DEBUG) + def test_rts_random(self): + segbits = 8 * _SEGSIZE + for _ in range(20): + n = randrange(10000) + a = urandom(n) + rts = _sc_rts(a) + self.assertEqual(len(rts), (n + segbits - 1) // segbits + 1) + self.assertEqual(rts[0], 0) + self.assertEqual(rts[-1], a.count()) + for i in range(len(rts) - 1): + seg_pop = a.count(1, segbits * i, segbits * (i + 1)) + self.assertEqual(rts[i + 1] - rts[i], seg_pop) + +# --------------------------------------------------------------------------- + +class VLFTests(unittest.TestCase, Util): + + def test_explicit(self): + for s, bits in [ + (b'\x40', ''), + (b'\x30', '0'), + (b'\x38', '1'), + (b'\x00', '0000'), + (b'\x01', '0001'), + (b'\xe0\x40', '0000 1'), + (b'\x90\x02', '0000 000001'), + (b'\xb5\xa7\x18', '0101 0100111 0011'), + ]: + a = bitarray(bits) + self.assertEqual(vl_encode(a), s) + self.assertEqual(vl_decode(s), a) + + def test_encode(self): + for endian in 'big', 'little': + s = vl_encode(bitarray('001101', endian)) + self.assertIsInstance(s, bytes) + self.assertEqual(s, b'\xd3\x20') + + def test_decode_args(self): + if sys.version_info[0] == 3: + self.assertRaises(TypeError, vl_decode, 'foo') + # item not integer + self.assertRaises(TypeError, vl_decode, iter([b'\x40'])) + + self.assertRaises(TypeError, vl_decode, b'\x40', 'big', 3) + self.assertRaises(ValueError, vl_decode, b'\x40', 'foo') + # these objects are not iterable + for arg in None, 0, 1, 0.0: + self.assertRaises(TypeError, vl_decode, arg) + # these items cannot be interpreted as ints + for item in None, 2.34, Ellipsis: + self.assertRaises(TypeError, vl_decode, iter([0x95, item])) + + b = b'\xd3\x20' + lst = [b, iter(b), memoryview(b)] + if sys.version_info[0] == 3: + lst.append(iter([0xd3, 0x20])) + lst.append(bytearray(b)) + for s in lst: + a = vl_decode(s, endian=self.random_endian()) + self.assertIsType(a, 'bitarray') + self.assertEqual(a, bitarray('0011 01')) + + def test_decode_endian(self): + blob = b'\xd3\x20' + res = bitarray('0011 01') + + for default_endian in 'little', 'big': + _set_default_endian(default_endian) + + for endian in 'little', 'big', None: + a = vl_decode(blob, endian) + self.assertEqual(a, res) + self.assertEqual(a.endian(), + endian if endian else default_endian) + + a = vl_decode(blob) + self.assertEqual(a, res) + self.assertEqual(a.endian(), default_endian) + + def test_decode_trailing(self): + for s, bits in [(b'\x40ABC', ''), + (b'\xe0\x40A', '00001')]: + stream = iter(s) + self.assertEqual(vl_decode(stream), bitarray(bits)) + self.assertEqual(next(stream), + b'A' if sys.version_info[0] == 2 else 65) + + def test_decode_ambiguity(self): + for s in b'\x40', b'\x4f', b'\x45': + self.assertEqual(vl_decode(iter(s)), bitarray()) + for s in b'\x1e', b'\x1f': + self.assertEqual(vl_decode(iter(s)), bitarray('111')) + + def test_decode_stream(self): + stream = iter(b'\x40\x30\x38\x40\x2c\xe0\x40\xd3\x20') + for bits in '', '0', '1', '', '11', '0000 1', '0011 01': + self.assertEqual(vl_decode(stream), bitarray(bits)) + + arrays = [urandom(randrange(30)) for _ in range(1000)] + stream = iter(b''.join(vl_encode(a) for a in arrays)) + for a in arrays: + self.assertEqual(vl_decode(stream), a) + + def test_decode_errors(self): + # decode empty bits + self.assertRaises(ValueError, vl_decode, b'') + # invalid number of padding bits + for s in b'\x50', b'\x60', b'\x70': + self.assertRaises(ValueError, vl_decode, s) + self.assertRaises(ValueError, vl_decode, b'\xf0') + # high bit set, but no terminating byte + for s in b'\x80', b'\x80\x80': + self.assertRaises(ValueError, vl_decode, s) + + @skipIf(sys.version_info[0] == 2) + def test_decode_invalid_stream(self): + N = 100 + s = iter(N * (3 * [0x80] + ['XX']) + ['end.']) + for _ in range(N): + a = None + try: + a = vl_decode(s) + except TypeError: + pass + self.assertTrue(a is None) + self.assertEqual(next(s), 'end.') + + def test_explicit_zeros(self): + for n in range(100): + a = zeros(4 + n * 7) + s = n * b'\x80' + b'\x00' + self.assertEqual(vl_encode(a), s) + self.assertEqual(vl_decode(s), a) + + def round_trip(self, a): + c = a.copy() + s = vl_encode(a) + b = vl_decode(s) + self.assertTrue(a == b == c) + LEN_PAD_BITS = 3 + self.assertEqual(len(s), (len(a) + LEN_PAD_BITS + 6) // 7) + + head = ord(s[0]) if sys.version_info[0] == 2 else s[0] + padding = (head & 0x70) >> 4 + self.assertEqual(len(a) + padding, 7 * len(s) - LEN_PAD_BITS) + + def test_range(self): + for n in range(500): + self.round_trip(urandom(n)) + + def test_large(self): + a = urandom(randint(50000, 100000)) + self.round_trip(a) + + def test_random(self): + for a in self.randombitarrays(): + self.round_trip(a) + +# --------------------------------------------------------------------------- + +class TestsIntegerization(unittest.TestCase, Util): + + def test_ba2int(self): + self.assertEqual(ba2int(bitarray('0')), 0) + self.assertEqual(ba2int(bitarray('1')), 1) + self.assertEqual(ba2int(bitarray('00101', 'big')), 5) + self.assertEqual(ba2int(bitarray('00101', 'little')), 20) + self.assertEqual(ba2int(frozenbitarray('11')), 3) + self.assertRaises(ValueError, ba2int, bitarray()) + self.assertRaises(ValueError, ba2int, frozenbitarray()) + self.assertRaises(TypeError, ba2int, '101') + a = bitarray('111') + b = a.copy() + self.assertEqual(ba2int(a), 7) + # ensure original object wasn't altered + self.assertEQUAL(a, b) + + def test_ba2int_frozen(self): + for a in self.randombitarrays(start=1): + b = frozenbitarray(a) + self.assertEqual(ba2int(b), ba2int(a)) + self.assertEQUAL(a, b) + + def test_ba2int_random(self): + for a in self.randombitarrays(start=1): + b = bitarray(a, 'big') + self.assertEqual(a, b) + self.assertEqual(ba2int(b), int(b.to01(), 2)) + + def test_ba2int_bytes(self): + for n in range(1, 50): + a = urandom(8 * n, self.random_endian()) + c = bytearray(a.tobytes()) + i = 0 + for x in (c if a.endian() == 'big' else reversed(c)): + i <<= 8 + i |= x + self.assertEqual(ba2int(a), i) + + def test_int2ba(self): + self.assertEqual(int2ba(0), bitarray('0')) + self.assertEqual(int2ba(1), bitarray('1')) + self.assertEqual(int2ba(5), bitarray('101')) + self.assertEQUAL(int2ba(6, endian='big'), bitarray('110', 'big')) + self.assertEQUAL(int2ba(6, endian='little'), + bitarray('011', 'little')) + self.assertRaises(TypeError, int2ba, 1.0) + self.assertRaises(TypeError, int2ba, 1, 3.0) + self.assertRaises(ValueError, int2ba, 1, 0) + self.assertRaises(TypeError, int2ba, 1, 10, 123) + self.assertRaises(ValueError, int2ba, 1, 10, 'asd') + # signed integer requires length + self.assertRaises(TypeError, int2ba, 100, signed=True) + + def test_signed(self): + for s, i in [ + ('0', 0), + ('1', -1), + ('00', 0), + ('10', 1), + ('01', -2), + ('11', -1), + ('000', 0), + ('100', 1), + ('010', 2), + ('110', 3), + ('001', -4), + ('101', -3), + ('011', -2), + ('111', -1), + ('00000', 0), + ('11110', 15), + ('00001', -16), + ('11111', -1), + ('00000000 0', 0), + ('11111111 0', 255), + ('00000000 1', -256), + ('11111111 1', -1), + ('00000000 00000000 000000', 0), + ('10010000 11000000 100010', 9 + 3 * 256 + 17 * 2 ** 16), + ('11111111 11111111 111110', 2 ** 21 - 1), + ('00000000 00000000 000001', -2 ** 21), + ('10010000 11000000 100011', -2 ** 21 + + (9 + 3 * 256 + 17 * 2 ** 16)), + ('11111111 11111111 111111', -1), + ]: + self.assertEqual(ba2int(bitarray(s, 'little'), signed=1), i) + self.assertEqual(ba2int(bitarray(s[::-1], 'big'), signed=1), i) + + len_s = len(bitarray(s)) + self.assertEQUAL(int2ba(i, len_s, 'little', signed=1), + bitarray(s, 'little')) + self.assertEQUAL(int2ba(i, len_s, 'big', signed=1), + bitarray(s[::-1], 'big')) + + def test_int2ba_overflow(self): + self.assertRaises(OverflowError, int2ba, -1) + self.assertRaises(OverflowError, int2ba, -1, 4) + + self.assertRaises(OverflowError, int2ba, 128, 7) + self.assertRaises(OverflowError, int2ba, 64, 7, signed=1) + self.assertRaises(OverflowError, int2ba, -65, 7, signed=1) + + for n in range(1, 20): + self.assertRaises(OverflowError, int2ba, 2 ** n, n) + self.assertRaises(OverflowError, int2ba, 2 ** (n - 1), n, + signed=1) + self.assertRaises(OverflowError, int2ba, -2 ** (n - 1) - 1, n, + signed=1) + + def test_int2ba_length(self): + self.assertRaises(TypeError, int2ba, 0, 1.0) + self.assertRaises(ValueError, int2ba, 0, 0) + self.assertEqual(int2ba(5, length=6, endian='big'), + bitarray('000101')) + for n in range(1, 100): + ab = int2ba(1, n, 'big') + al = int2ba(1, n, 'little') + self.assertEqual(ab.endian(), 'big') + self.assertEqual(al.endian(), 'little') + self.assertEqual(len(ab), n), + self.assertEqual(len(al), n) + self.assertEqual(ab, bitarray((n - 1) * '0') + bitarray('1')) + self.assertEqual(al, bitarray('1') + bitarray((n - 1) * '0')) + + ab = int2ba(0, n, 'big') + al = int2ba(0, n, 'little') + self.assertEqual(len(ab), n) + self.assertEqual(len(al), n) + self.assertEqual(ab, bitarray(n * '0', 'big')) + self.assertEqual(al, bitarray(n * '0', 'little')) + + self.assertEqual(int2ba(2 ** n - 1), bitarray(n * '1')) + self.assertEqual(int2ba(2 ** n - 1, endian='little'), + bitarray(n * '1')) + for endian in 'big', 'little': + self.assertEqual(int2ba(-1, n, endian, signed=True), + bitarray(n * '1')) + + def test_explicit(self): + _set_default_endian('big') + for i, sa in [( 0, '0'), (1, '1'), + ( 2, '10'), (3, '11'), + (25, '11001'), (265, '100001001'), + (3691038, '1110000101001000011110')]: + ab = bitarray(sa, 'big') + al = bitarray(sa[::-1], 'little') + self.assertEQUAL(int2ba(i), ab) + self.assertEQUAL(int2ba(i, endian='big'), ab) + self.assertEQUAL(int2ba(i, endian='little'), al) + self.assertEqual(ba2int(ab), ba2int(al), i) + + def check_round_trip(self, i): + for endian in 'big', 'little': + a = int2ba(i, endian=endian) + self.check_obj(a) + self.assertEqual(a.endian(), endian) + self.assertTrue(len(a) > 0) + # ensure we have no leading zeros + if a.endian == 'big': + self.assertTrue(len(a) == 1 or a.index(1) == 0) + self.assertEqual(ba2int(a), i) + if i > 0: + self.assertEqual(i.bit_length(), len(a)) + # add a few trailing / leading zeros to bitarray + if endian == 'big': + a = zeros(randrange(4), endian) + a + else: + a = a + zeros(randrange(4), endian) + self.assertEqual(a.endian(), endian) + self.assertEqual(ba2int(a), i) + + def test_many(self): + for i in range(20): + self.check_round_trip(i) + self.check_round_trip(randrange(10 ** randint(3, 300))) + + @staticmethod + def twos_complement(i, num_bits): + # https://en.wikipedia.org/wiki/Two%27s_complement + mask = 2 ** (num_bits - 1) + return -(i & mask) + (i & ~mask) + + def test_random_signed(self): + for a in self.randombitarrays(start=1): + i = ba2int(a, signed=True) + b = int2ba(i, len(a), a.endian(), signed=True) + self.assertEQUAL(a, b) + + j = ba2int(a, signed=False) # unsigned + if i >= 0: + self.assertEqual(i, j) + + self.assertEqual(i, self.twos_complement(j, len(a))) + +# --------------------------------------------------------------------------- + +class MixedTests(unittest.TestCase, Util): + + def test_bin(self): + for i in range(100): + s = bin(i) + self.assertEqual(s[:2], '0b') + a = bitarray(s[2:], 'big') + self.assertEqual(ba2int(a), i) + t = '0b%s' % a.to01() + self.assertEqual(t, s) + self.assertEqual(eval(t), i) + + @skipIf(sys.version_info[0] == 2) + def test_oct(self): + for i in range(1000): + s = oct(i) + self.assertEqual(s[:2], '0o') + a = base2ba(8, s[2:], 'big') + self.assertEqual(ba2int(a), i) + t = '0o%s' % ba2base(8, a) + self.assertEqual(t, s) + self.assertEqual(eval(t), i) + + def test_hex(self): + for i in range(1000): + s = hex(i) + self.assertEqual(s[:2], '0x') + a = hex2ba(s[2:], 'big') + self.assertEqual(ba2int(a), i) + t = '0x%s' % ba2hex(a) + self.assertEqual(t, s) + self.assertEqual(eval(t), i) + + def test_bitwise(self): + for a in self.randombitarrays(start=1): + b = urandom(len(a), a.endian()) + aa = a.copy() + bb = b.copy() + i = ba2int(a) + j = ba2int(b) + self.assertEqual(ba2int(a & b), i & j) + self.assertEqual(ba2int(a | b), i | j) + self.assertEqual(ba2int(a ^ b), i ^ j) + + n = randint(0, len(a)) + if a.endian() == 'big': + self.assertEqual(ba2int(a >> n), i >> n) + c = zeros(len(a), 'big') + a + self.assertEqual(ba2int(c << n), i << n) + + self.assertEQUAL(a, aa) + self.assertEQUAL(b, bb) + + def test_bitwise_inplace(self): + for a in self.randombitarrays(start=1): + b = urandom(len(a), a.endian()) + bb = b.copy() + i = ba2int(a) + j = ba2int(b) + c = a.copy() + c &= b + self.assertEqual(ba2int(c), i & j) + c = a.copy() + c |= b + self.assertEqual(ba2int(c), i | j) + c = a.copy() + c ^= b + self.assertEqual(ba2int(c), i ^ j) + self.assertEQUAL(b, bb) + + n = randint(0, len(a)) + if a.endian() == 'big': + c = a.copy() + c >>= n + self.assertEqual(ba2int(c), i >> n) + c = zeros(len(a), 'big') + a + c <<= n + self.assertEqual(ba2int(c), i << n) + + def test_primes(self): # Sieve of Eratosthenes + sieve = ones(10000) + sieve[:2] = 0 # zero and one are not prime + for i in range(2, 100): + if sieve[i]: + sieve[i * i::i] = 0 + # the first 15 primes + self.assertEqual(sieve.search(1, 15), [2, 3, 5, 7, 11, 13, 17, 19, + 23, 29, 31, 37, 41, 43, 47]) + # there are 1229 primes between 1 and 10000 + self.assertEqual(sieve.count(1), 1229) + # there are 119 primes between 4000 and 5000 + self.assertEqual(sieve.count(1, 4000, 5000), 119) + # the 1000th prime is 7919 + self.assertEqual(count_n(sieve, 1000) - 1, 7919) + +# --------------------------------------------------------------------------- + +class TestsSerialization(unittest.TestCase, Util): + + def test_explicit(self): + for blob, endian, bits in [ + (b'\x00', 'little', ''), + (b'\x07\x01', 'little', '1'), + (b'\x17\x80', 'big', '1'), + (b'\x13\xf8', 'big', '11111'), + (b'\x00\x0f', 'little', '11110000'), + (b'\x10\xf0', 'big', '11110000'), + (b'\x12\x87\xd8', 'big', '10000111 110110') + ]: + a = bitarray(bits, endian) + s = serialize(a) + self.assertEqual(blob, s) + self.assertIsInstance(s, bytes) + + b = deserialize(blob) + self.assertEqual(b, a) + self.assertEqual(b.endian(), endian) + self.assertIsType(b, 'bitarray') + + def test_serialize_args(self): + for x in '0', 0, 1, b'\x00', 0.0, [0, 1], bytearray([0]): + self.assertRaises(TypeError, serialize, x) + # no arguments + self.assertRaises(TypeError, serialize) + # too many arguments + self.assertRaises(TypeError, serialize, bitarray(), 1) + + for a in bitarray('0111', 'big'), frozenbitarray('0111', 'big'): + self.assertEqual(serialize(a), b'\x14\x70') + + def test_deserialize_args(self): + for x in 0, 1, False, True, None, u'', u'01', 0.0, [0, 1]: + self.assertRaises(TypeError, deserialize, x) + # no arguments + self.assertRaises(TypeError, deserialize) + # too many arguments + self.assertRaises(TypeError, deserialize, b'\x00', 1) + + blob = b'\x03\x06' + x = bitarray() # we can deserialize a bitarray as it has a buffer + x.frombytes(blob) + for s in blob, bytearray(blob), memoryview(blob), x: + a = deserialize(s) + self.assertEqual(a.to01(), '01100') + self.assertEqual(a.endian(), 'little') + + def test_invalid_bytes(self): + self.assertRaises(ValueError, deserialize, b'') + + def check_msg(b): + # Python 2: PyErr_Format() seems to handle "0x%02x" + # incorrectly. E.g. instead of "0x01", I get "0x1" + if sys.version_info[0] == 3: + msg = "invalid header byte: 0x%02x" % b[0] + self.assertRaisesMessage(ValueError, msg, deserialize, b) + + for i in range(256): + b = bytes(bytearray([i])) + if i == 0 or i == 16: + self.assertEqual(deserialize(b), bitarray()) + else: + self.assertRaises(ValueError, deserialize, b) + check_msg(b) + + b += b'\0' + if i < 32 and i % 16 < 8: + self.assertEqual(deserialize(b), zeros(8 - i % 8)) + else: + self.assertRaises(ValueError, deserialize, b) + check_msg(b) + + def test_bits_ignored(self): + # the unused padding bits (with the last bytes) are ignored + for blob, endian in [ + (b'\x07\x01', 'little'), + (b'\x07\x03', 'little'), + (b'\x07\xff', 'little'), + (b'\x17\x80', 'big'), + (b'\x17\xc0', 'big'), + (b'\x17\xff', 'big'), + ]: + a = deserialize(blob) + self.assertEqual(a.to01(), '1') + self.assertEqual(a.endian(), endian) + + def test_random(self): + for a in self.randombitarrays(): + b = serialize(a) + c = deserialize(b) + self.assertEqual(a, c) + self.assertEqual(a.endian(), c.endian()) + self.check_obj(c) + +# --------------------------------------------------------------------------- + +class TestsHuffman(unittest.TestCase): + + def test_simple(self): + freq = {0: 10, 'as': 2, None: 1.6} + code = huffman_code(freq) + self.assertEqual(len(code), 3) + self.assertEqual(len(code[0]), 1) + self.assertEqual(len(code['as']), 2) + self.assertEqual(len(code[None]), 2) + + def test_endianness(self): + freq = {'A': 10, 'B': 2, 'C': 5} + for endian in 'big', 'little': + code = huffman_code(freq, endian) + self.assertEqual(len(code), 3) + for v in code.values(): + self.assertEqual(v.endian(), endian) + + def test_wrong_arg(self): + self.assertRaises(TypeError, huffman_code, [('a', 1)]) + self.assertRaises(TypeError, huffman_code, 123) + self.assertRaises(TypeError, huffman_code, None) + # cannot compare 'a' with 1 + self.assertRaises(TypeError, huffman_code, {'A': 'a', 'B': 1}) + # frequency map cannot be empty + self.assertRaises(ValueError, huffman_code, {}) + + def test_one_symbol(self): + cnt = {'a': 1} + code = huffman_code(cnt) + self.assertEqual(code, {'a': bitarray('0')}) + for n in range(4): + msg = n * ['a'] + a = bitarray() + a.encode(code, msg) + self.assertEqual(a.to01(), n * '0') + self.assertEqual(a.decode(code), msg) + a.append(1) + self.assertRaises(ValueError, a.decode, code) + self.assertRaises(ValueError, list, a.iterdecode(code)) + + def check_tree(self, code): + n = len(code) + tree = decodetree(code) + self.assertEqual(tree.todict(), code) + # ensure tree has 2n-1 nodes (n symbol nodes and n-1 internal nodes) + self.assertEqual(tree.nodes(), 2 * n - 1) + # a proper Huffman tree is complete + self.assertTrue(tree.complete()) + + def test_balanced(self): + n = 6 + freq = {} + for i in range(2 ** n): + freq[i] = 1 + code = huffman_code(freq) + self.assertEqual(len(code), 2 ** n) + self.assertTrue(all(len(v) == n for v in code.values())) + self.check_tree(code) + + def test_unbalanced(self): + N = 27 + freq = {} + for i in range(N): + freq[i] = 2 ** i + code = huffman_code(freq) + self.assertEqual(len(code), N) + for i in range(N): + self.assertEqual(len(code[i]), N - (1 if i <= 1 else i)) + self.check_tree(code) + + def test_counter(self): + message = 'the quick brown fox jumps over the lazy dog.' + code = huffman_code(Counter(message)) + a = bitarray() + a.encode(code, message) + self.assertEqual(''.join(a.decode(code)), message) + self.check_tree(code) + + def test_random_list(self): + plain = [randrange(100) for _ in range(500)] + code = huffman_code(Counter(plain)) + a = bitarray() + a.encode(code, plain) + self.assertEqual(a.decode(code), plain) + self.check_tree(code) + + def test_random_freq(self): + for n in 2, 3, 5, randint(50, 200): + # create Huffman code for n symbols + code = huffman_code({i: random() for i in range(n)}) + self.check_tree(code) + +# --------------------------------------------------------------------------- + +class TestsCanonicalHuffman(unittest.TestCase, Util): + + def test_basic(self): + plain = bytearray(b'the quick brown fox jumps over the lazy dog.') + chc, count, symbol = canonical_huffman(Counter(plain)) + self.assertIsInstance(chc, dict) + self.assertIsInstance(count, list) + self.assertIsInstance(symbol, list) + a = bitarray() + a.encode(chc, plain) + self.assertEqual(bytearray(a.iterdecode(chc)), plain) + self.assertEqual(bytearray(canonical_decode(a, count, symbol)), plain) + + def test_canonical_huffman_errors(self): + self.assertRaises(TypeError, canonical_huffman, []) + # frequency map cannot be empty + self.assertRaises(ValueError, canonical_huffman, {}) + self.assertRaises(TypeError, canonical_huffman) + cnt = huffman_code(Counter('aabc')) + self.assertRaises(TypeError, canonical_huffman, cnt, 'a') + + def test_one_symbol(self): + cnt = {'a': 1} + chc, count, symbol = canonical_huffman(cnt) + self.assertEqual(chc, {'a': bitarray('0')}) + self.assertEqual(count, [0, 1]) + self.assertEqual(symbol, ['a']) + for n in range(4): + msg = n * ['a'] + a = bitarray() + a.encode(chc, msg) + self.assertEqual(a.to01(), n * '0') + self.assertEqual(list(canonical_decode(a, count, symbol)), msg) + a.append(1) + self.assertRaises(ValueError, list, + canonical_decode(a, count, symbol)) + + def test_canonical_decode_errors(self): + a = bitarray('1101') + s = ['a'] + # bitarray not of bitarray type + self.assertRaises(TypeError, canonical_decode, '11', [0, 1], s) + # count not sequence + self.assertRaises(TypeError, canonical_decode, a, {0, 1}, s) + # count element not an int + self.assertRaises(TypeError, canonical_decode, a, [0, 1.0], s) + # count element overflow + self.assertRaises(OverflowError, canonical_decode, a, [0, 1 << 65], s) + # negative count + self.assertRaises(ValueError, canonical_decode, a, [0, -1], s) + # count list too long + self.assertRaises(ValueError, canonical_decode, a, 32 * [0], s) + # symbol not sequence + self.assertRaises(TypeError, canonical_decode, a, [0, 1], 43) + + symbol = ['a', 'b', 'c', 'd'] + # sum(count) != len(symbol) + self.assertRaisesMessage(ValueError, + "sum(count) = 3, but len(symbol) = 4", + canonical_decode, a, [0, 1, 2], symbol) + # count[i] > 1 << i + self.assertRaisesMessage(ValueError, + "count[2] cannot be negative or larger than 4, got 5", + canonical_decode, a, [0, 2, 5], symbol) + + def test_canonical_decode_simple(self): + # symbols can be anything, they do not even have to be hashable here + cnt = [0, 0, 4] + s = ['A', 42, [1.2-3.7j, 4j], {'B': 6}] + a = bitarray('00 01 10 11') + # count can be a list + self.assertEqual(list(canonical_decode(a, cnt, s)), s) + # count can also be a tuple (any sequence object in fact) + self.assertEqual(list(canonical_decode(a, (0, 0, 4), s)), s) + self.assertEqual(list(canonical_decode(7 * a, cnt, s)), 7 * s) + # the count list may have extra 0's at the end (but not too many) + count = [0, 0, 4, 0, 0, 0, 0, 0] + self.assertEqual(list(canonical_decode(a, count, s)), s) + # the element count[0] is unused + self.assertEqual(list(canonical_decode(a, [-47, 0, 4], s)), s) + # in fact it can be anything, as it is entirely ignored + self.assertEqual(list(canonical_decode(a, [s, 0, 4], s)), s) + + # the symbol argument can be any sequence object + s = [65, 66, 67, 98] + self.assertEqual(list(canonical_decode(a, cnt, s)), s) + self.assertEqual(list(canonical_decode(a, cnt, bytearray(s))), s) + self.assertEqual(list(canonical_decode(a, cnt, tuple(s))), s) + if sys.version_info[0] == 3: + self.assertEqual(list(canonical_decode(a, cnt, bytes(s))), s) + # Implementation Note: + # The symbol can even be an iterable. This was done because we + # want to use PySequence_Fast in order to convert sequence + # objects (like bytes and bytearray) to a list. This is faster + # as all objects are now elements in an array of pointers (as + # opposed to having the object's __getitem__ method called on + # every iteration). + self.assertEqual(list(canonical_decode(a, cnt, iter(s))), s) + + def test_canonical_decode_empty(self): + a = bitarray() + # count and symbol are empty, ok because sum([]) == len([]) + self.assertEqual(list(canonical_decode(a, [], [])), []) + a.append(0) + self.assertRaisesMessage(ValueError, "reached end of bitarray", + list, canonical_decode(a, [], [])) + a = bitarray(31 * '0') + self.assertRaisesMessage(ValueError, "ran out of codes", + list, canonical_decode(a, [], [])) + + def test_canonical_decode_one_symbol(self): + symbols = ['A'] + count = [0, 1] + a = bitarray('000') + self.assertEqual(list(canonical_decode(a, count, symbols)), + 3 * symbols) + a.append(1) + a.extend(bitarray(10 * '0')) + iterator = canonical_decode(a, count, symbols) + self.assertRaisesMessage(ValueError, "reached end of bitarray", + list, iterator) + + a.extend(bitarray(20 * '0')) + iterator = canonical_decode(a, count, symbols) + self.assertRaisesMessage(ValueError, "ran out of codes", + list, iterator) + + def test_canonical_decode_large(self): + with open(__file__, 'rb') as f: + msg = bytearray(f.read()) + self.assertTrue(len(msg) > 50000) + codedict, count, symbol = canonical_huffman(Counter(msg)) + a = bitarray() + a.encode(codedict, msg) + self.assertEqual(bytearray(canonical_decode(a, count, symbol)), msg) + self.check_code(codedict, count, symbol) + + def test_canonical_decode_symbol_change(self): + msg = bytearray(b"Hello World!") + codedict, count, symbol = canonical_huffman(Counter(msg)) + self.check_code(codedict, count, symbol) + a = bitarray() + a.encode(codedict, 10 * msg) + + it = canonical_decode(a, count, symbol) + def decode_one_msg(): + return bytearray(next(it) for _ in range(len(msg))) + + self.assertEqual(decode_one_msg(), msg) + symbol[symbol.index(ord("l"))] = ord("k") + self.assertEqual(decode_one_msg(), bytearray(b"Hekko Workd!")) + del symbol[:] + self.assertRaises(IndexError, decode_one_msg) + + def ensure_sorted(self, chc, symbol): + # ensure codes are sorted + for i in range(len(symbol) - 1): + a = chc[symbol[i]] + b = chc[symbol[i + 1]] + self.assertTrue(ba2int(a) < ba2int(b)) + + def ensure_consecutive(self, chc, count, symbol): + first = 0 + for nbits, cnt in enumerate(count): + for i in range(first, first + cnt - 1): + # ensure two consecutive codes (with same bit length) have + # consecutive integer values + a = chc[symbol[i]] + b = chc[symbol[i + 1]] + self.assertTrue(len(a) == len(b) == nbits) + self.assertEqual(ba2int(a) + 1, ba2int(b)) + first += cnt + + def ensure_count(self, chc, count): + # ensure count list corresponds to length counts from codedict + maxbits = max(len(a) for a in chc.values()) + my_count = (maxbits + 1) * [0] + for a in chc.values(): + self.assertEqual(a.endian(), 'big') + my_count[len(a)] += 1 + self.assertEqual(my_count, list(count)) + + def ensure_complete(self, count): + # ensure code is complete and not oversubscribed + maxbits = len(count) + x = sum(count[i] << (maxbits - i) for i in range(1, maxbits)) + self.assertEqual(x, 1 << maxbits) + + def ensure_complete_2(self, chc): + # ensure code is complete + dt = decodetree(chc) + self.assertTrue(dt.complete()) + + def ensure_round_trip(self, chc, count, symbol): + # create a short test message, encode and decode + msg = [choice(symbol) for _ in range(10)] + a = bitarray() + a.encode(chc, msg) + it = canonical_decode(a, count, symbol) + # the iterator holds a reference to the bitarray and symbol list + del a, count, symbol + self.assertEqual(type(it).__name__, 'canonical_decodeiter') + self.assertEqual(list(it), msg) + + def check_code(self, chc, count, symbol): + self.assertTrue(len(chc) == len(symbol) == sum(count)) + self.assertEqual(count[0], 0) # no codes have length 0 + self.assertTrue(set(chc) == set(symbol)) + # the code of the last symbol has all 1 bits + self.assertTrue(chc[symbol[-1]].all()) + # the code of the first symbol starts with bit 0 + self.assertFalse(chc[symbol[0]][0]) + + self.ensure_sorted(chc, symbol) + self.ensure_consecutive(chc, count, symbol) + self.ensure_count(chc, count) + self.ensure_complete(count) + self.ensure_complete_2(chc) + self.ensure_round_trip(chc, count, symbol) + + def test_simple_counter(self): + plain = bytearray(b'the quick brown fox jumps over the lazy dog.') + cnt = Counter(plain) + code, count, symbol = canonical_huffman(cnt) + self.check_code(code, count, symbol) + self.check_code(code, tuple(count), tuple(symbol)) + self.check_code(code, bytearray(count), symbol) + self.check_code(code, count, bytearray(symbol)) + + def test_balanced(self): + n = 7 + freq = {} + for i in range(2 ** n): + freq[i] = 1 + code, count, sym = canonical_huffman(freq) + self.assertEqual(len(code), 2 ** n) + self.assertTrue(all(len(v) == n for v in code.values())) + self.check_code(code, count, sym) + + def test_unbalanced(self): + n = 29 + freq = {} + for i in range(n): + freq[i] = 2 ** i + code = canonical_huffman(freq)[0] + self.assertEqual(len(code), n) + for i in range(n): + self.assertEqual(len(code[i]), n - (1 if i <= 1 else i)) + self.check_code(*canonical_huffman(freq)) + + def test_random_freq(self): + for n in 2, 3, 5, randint(50, 200): + freq = {i: random() for i in range(n)} + self.check_code(*canonical_huffman(freq)) + +# --------------------------------------------------------------------------- + +if __name__ == '__main__': + unittest.main() diff --git a/code/.venv/lib/python3.12/site-packages/bitarray/util.py b/code/.venv/lib/python3.12/site-packages/bitarray/util.py new file mode 100644 index 0000000..f926eb5 --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/bitarray/util.py @@ -0,0 +1,423 @@ +# Copyright (c) 2019 - 2024, Ilan Schnell; All Rights Reserved +# bitarray is published under the PSF license. +# +# Author: Ilan Schnell +""" +Useful utilities for working with bitarrays. +""" +from __future__ import absolute_import + +import os +import sys + +from bitarray import bitarray, bits2bytes + +from bitarray._util import ( + zeros, ones, count_n, parity, + count_and, count_or, count_xor, any_and, subset, + _correspond_all, + serialize, deserialize, + ba2hex, hex2ba, + ba2base, base2ba, + sc_encode, sc_decode, + vl_encode, vl_decode, + canonical_decode, +) + +__all__ = [ + 'zeros', 'ones', 'urandom', + 'pprint', 'make_endian', 'rindex', 'strip', 'count_n', + 'parity', 'count_and', 'count_or', 'count_xor', 'any_and', 'subset', + 'intervals', + 'ba2hex', 'hex2ba', + 'ba2base', 'base2ba', + 'ba2int', 'int2ba', + 'serialize', 'deserialize', + 'sc_encode', 'sc_decode', + 'vl_encode', 'vl_decode', + 'huffman_code', 'canonical_huffman', 'canonical_decode', +] + + +_is_py2 = bool(sys.version_info[0] == 2) + + +def urandom(__length, endian=None): + """urandom(length, /, endian=None) -> bitarray + +Return a bitarray of `length` random bits (uses `os.urandom`). +""" + a = bitarray(0, endian) + a.frombytes(os.urandom(bits2bytes(__length))) + del a[__length:] + return a + + +def rindex(__a, __sub_bitarray=1, __start=0, __stop=sys.maxsize): + """rindex(bitarray, sub_bitarray=1, start=0, stop=, /) -> int + +Return rightmost (highest) index where sub_bitarray (or item - defaults +to 1) is found in bitarray (`a`), such that sub_bitarray is contained +within `a[start:stop]`. +Raises `ValueError` when the sub_bitarray is not present. +""" + from warnings import warn + + warn("rindex() is deprecated and will be removed in bitarray 3.0 - " + "use .index(..., right=True) method instead.", + DeprecationWarning, stacklevel=1) + + if not isinstance(__a, bitarray): + raise TypeError("bitarray expected, got '%s'" % type(__a).__name__) + + return __a.index(__sub_bitarray, __start, __stop, right=True) + + +def pprint(__a, stream=None, group=8, indent=4, width=80): + """pprint(bitarray, /, stream=None, group=8, indent=4, width=80) + +Prints the formatted representation of object on `stream` (which defaults +to `sys.stdout`). By default, elements are grouped in bytes (8 elements), +and 8 bytes (64 elements) per line. +Non-bitarray objects are printed by the standard library +function `pprint.pprint()`. +""" + if stream is None: + stream = sys.stdout + + if not isinstance(__a, bitarray): + import pprint as _pprint + _pprint.pprint(__a, stream=stream, indent=indent, width=width) + return + + group = int(group) + if group < 1: + raise ValueError('group must be >= 1') + indent = int(indent) + if indent < 0: + raise ValueError('indent must be >= 0') + width = int(width) + if width <= indent: + raise ValueError('width must be > %d (indent)' % indent) + + gpl = (width - indent) // (group + 1) # groups per line + epl = group * gpl # elements per line + if epl == 0: + epl = width - indent - 2 + type_name = type(__a).__name__ + # here 4 is len("'()'") + multiline = len(type_name) + 4 + len(__a) + len(__a) // group >= width + if multiline: + quotes = "'''" + elif __a: + quotes = "'" + else: + quotes = "" + + stream.write("%s(%s" % (type_name, quotes)) + for i, b in enumerate(__a): + if multiline and i % epl == 0: + stream.write('\n%s' % (indent * ' ')) + if i % group == 0 and i % epl != 0: + stream.write(' ') + stream.write(str(b)) + + if multiline: + stream.write('\n') + + stream.write("%s)\n" % quotes) + stream.flush() + + +def make_endian(__a, endian): + """make_endian(bitarray, /, endian) -> bitarray + +When the endianness of the given bitarray is different from `endian`, +return a new bitarray, with endianness `endian` and the same elements +as the original bitarray. +Otherwise (endianness is already `endian`) the original bitarray is returned +unchanged. +""" + from warnings import warn + + warn("make_endian() is deprecated and will be removed in bitarray 3.0 - " + "use bitarray(..., endian=...) instead", + DeprecationWarning, stacklevel=1) + + if not isinstance(__a, bitarray): + raise TypeError("bitarray expected, got '%s'" % type(__a).__name__) + + if __a.endian() == endian: + return __a + + return bitarray(__a, endian) + + +def strip(__a, mode='right'): + """strip(bitarray, /, mode='right') -> bitarray + +Return a new bitarray with zeros stripped from left, right or both ends. +Allowed values for mode are the strings: `left`, `right`, `both` +""" + if not isinstance(mode, str): + raise TypeError("str expected for mode, got '%s'" % type(__a).__name__) + if mode not in ('left', 'right', 'both'): + raise ValueError("mode must be 'left', 'right' or 'both', got %r" % + mode) + + start = None if mode == 'right' else __a.find(1) + if start == -1: + return __a[:0] + stop = None if mode == 'left' else __a.find(1, right=1) + 1 + return __a[start:stop] + + +def intervals(__a): + """intervals(bitarray, /) -> iterator + +Compute all uninterrupted intervals of 1s and 0s, and return an +iterator over tuples `(value, start, stop)`. The intervals are guaranteed +to be in order, and their size is always non-zero (`stop - start > 0`). +""" + try: + value = __a[0] # value of current interval + except IndexError: + return + n = len(__a) + stop = 0 # "previous" stop - becomes next start + + while stop < n: + start = stop + # assert __a[start] == value + try: # find next occurrence of opposite value + stop = __a.index(not value, start) + except ValueError: + stop = n + yield int(value), start, stop + value = not value # next interval has opposite value + + +def ba2int(__a, signed=False): + """ba2int(bitarray, /, signed=False) -> int + +Convert the given bitarray to an integer. +The bit-endianness of the bitarray is respected. +`signed` indicates whether two's complement is used to represent the integer. +""" + if not isinstance(__a, bitarray): + raise TypeError("bitarray expected, got '%s'" % type(__a).__name__) + length = len(__a) + if length == 0: + raise ValueError("non-empty bitarray expected") + + le = bool(__a.endian() == 'little') + if __a.padbits: + pad = zeros(__a.padbits, __a.endian()) + __a = __a + pad if le else pad + __a + + if _is_py2: + a = bitarray(__a, 'big') + if le: + a.reverse() + res = int(ba2hex(a), 16) + else: # py3 + res = int.from_bytes(__a.tobytes(), byteorder=__a.endian()) + + if signed and res >= 1 << (length - 1): + res -= 1 << length + return res + + +def int2ba(__i, length=None, endian=None, signed=False): + """int2ba(int, /, length=None, endian=None, signed=False) -> bitarray + +Convert the given integer to a bitarray (with given endianness, +and no leading (big-endian) / trailing (little-endian) zeros), unless +the `length` of the bitarray is provided. An `OverflowError` is raised +if the integer is not representable with the given number of bits. +`signed` determines whether two's complement is used to represent the integer, +and requires `length` to be provided. +""" + if not isinstance(__i, (int, long) if _is_py2 else int): + raise TypeError("int expected, got '%s'" % type(__i).__name__) + if length is not None: + if not isinstance(length, int): + raise TypeError("int expected for length") + if length <= 0: + raise ValueError("length must be > 0") + if signed and length is None: + raise TypeError("signed requires length") + + if __i == 0: + # there are special cases for 0 which we'd rather not deal with below + return zeros(length or 1, endian) + + if signed: + m = 1 << (length - 1) + if not (-m <= __i < m): + raise OverflowError("signed integer not in range(%d, %d), " + "got %d" % (-m, m, __i)) + if __i < 0: + __i += 1 << length + else: # unsigned + if __i < 0: + raise OverflowError("unsigned integer not positive, got %d" % __i) + if length and __i >= (1 << length): + raise OverflowError("unsigned integer not in range(0, %d), " + "got %d" % (1 << length, __i)) + + a = bitarray(0, endian) + le = bool(a.endian() == 'little') + if _is_py2: + s = hex(__i)[2:].rstrip('L') + a.extend(hex2ba(s, 'big')) + if le: + a.reverse() + else: # py3 + b = __i.to_bytes(bits2bytes(__i.bit_length()), byteorder=a.endian()) + a.frombytes(b) + + if length is None: + return strip(a, 'right' if le else 'left') + + la = len(a) + if la > length: + a = a[:length] if le else a[-length:] + if la < length: + pad = zeros(length - la, a.endian()) + a = a + pad if le else pad + a + assert len(a) == length + return a + +# ------------------------------ Huffman coding ----------------------------- + +def _huffman_tree(__freq_map): + """_huffman_tree(dict, /) -> Node + +Given a dict mapping symbols to their frequency, construct a Huffman tree +and return its root node. +""" + from heapq import heappush, heappop + + class Node(object): + """ + A Node instance will either have a 'symbol' (leaf node) or + a 'child' (a tuple with both children) attribute. + The 'freq' attribute will always be present. + """ + def __lt__(self, other): + # heapq needs to be able to compare the nodes + return self.freq < other.freq + + minheap = [] + # create all leaf nodes and push them onto the queue + for sym, f in __freq_map.items(): + leaf = Node() + leaf.symbol = sym + leaf.freq = f + heappush(minheap, leaf) + + # repeat the process until only one node remains + while len(minheap) > 1: + # take the two nodes with lowest frequencies from the queue + # to construct a new node and push it onto the queue + parent = Node() + parent.child = heappop(minheap), heappop(minheap) + parent.freq = parent.child[0].freq + parent.child[1].freq + heappush(minheap, parent) + + # the single remaining node is the root of the Huffman tree + return minheap[0] + + +def huffman_code(__freq_map, endian=None): + """huffman_code(dict, /, endian=None) -> dict + +Given a frequency map, a dictionary mapping symbols to their frequency, +calculate the Huffman code, i.e. a dict mapping those symbols to +bitarrays (with given endianness). Note that the symbols are not limited +to being strings. Symbols may may be any hashable object (such as `None`). +""" + if not isinstance(__freq_map, dict): + raise TypeError("dict expected, got '%s'" % type(__freq_map).__name__) + + b0 = bitarray('0', endian) + b1 = bitarray('1', endian) + + if len(__freq_map) < 2: + if len(__freq_map) == 0: + raise ValueError("cannot create Huffman code with no symbols") + # Only one symbol: Normally if only one symbol is given, the code + # could be represented with zero bits. However here, the code should + # be at least one bit for the .encode() and .decode() methods to work. + # So we represent the symbol by a single code of length one, in + # particular one 0 bit. This is an incomplete code, since if a 1 bit + # is received, it has no meaning and will result in an error. + return {list(__freq_map)[0]: b0} + + result = {} + + def traverse(nd, prefix=bitarray(0, endian)): + try: # leaf + result[nd.symbol] = prefix + except AttributeError: # parent, so traverse each of the children + traverse(nd.child[0], prefix + b0) + traverse(nd.child[1], prefix + b1) + + traverse(_huffman_tree(__freq_map)) + return result + + +def canonical_huffman(__freq_map): + """canonical_huffman(dict, /) -> tuple + +Given a frequency map, a dictionary mapping symbols to their frequency, +calculate the canonical Huffman code. Returns a tuple containing: + +0. the canonical Huffman code as a dict mapping symbols to bitarrays +1. a list containing the number of symbols of each code length +2. a list of symbols in canonical order + +Note: the two lists may be used as input for `canonical_decode()`. +""" + if not isinstance(__freq_map, dict): + raise TypeError("dict expected, got '%s'" % type(__freq_map).__name__) + + if len(__freq_map) < 2: + if len(__freq_map) == 0: + raise ValueError("cannot create Huffman code with no symbols") + # Only one symbol: see note above in huffman_code() + sym = list(__freq_map)[0] + return {sym: bitarray('0', 'big')}, [0, 1], [sym] + + code_length = {} # map symbols to their code length + + def traverse(nd, length=0): + # traverse the Huffman tree, but (unlike in huffman_code() above) we + # now just simply record the length for reaching each symbol + try: # leaf + code_length[nd.symbol] = length + except AttributeError: # parent, so traverse each of the children + traverse(nd.child[0], length + 1) + traverse(nd.child[1], length + 1) + + traverse(_huffman_tree(__freq_map)) + + # we now have a mapping of symbols to their code length, + # which is all we need + + table = sorted(code_length.items(), key=lambda item: (item[1], item[0])) + + maxbits = max(item[1] for item in table) + codedict = {} + count = (maxbits + 1) * [0] + + code = 0 + for i, (sym, length) in enumerate(table): + codedict[sym] = int2ba(code, length, 'big') + count[length] += 1 + if i + 1 < len(table): + code += 1 + code <<= table[i + 1][1] - length + + return codedict, count, [item[0] for item in table] diff --git a/code/.venv/lib/python3.12/site-packages/bitarray/util.pyi b/code/.venv/lib/python3.12/site-packages/bitarray/util.pyi new file mode 100644 index 0000000..cc5b962 --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/bitarray/util.pyi @@ -0,0 +1,71 @@ +# Copyright (c) 2021 - 2024, Ilan Schnell; All Rights Reserved + +from collections import Counter +from collections.abc import Iterable, Iterator, Sequence +from typing import Any, AnyStr, BinaryIO, Optional, Union + +from bitarray import bitarray, BytesLike, CodeDict + + +FreqMap = Union[Counter[int], dict[Any, Union[int, float]]] + + +def zeros(length: int, endian: Optional[str] = ...) -> bitarray: ... +def ones(length: int, endian: Optional[str] = ...) -> bitarray: ... + +def urandom(length: int, endian: Optional[str] = ...) -> bitarray: ... +def pprint(a: Any, stream: BinaryIO = ..., + group: int = ..., + indent: int = ..., + width: int = ...) -> None: ... + +def make_endian(a: bitarray, endian: str) -> bitarray: ... +def rindex(a: bitarray, + sub_bitarray: Union[bitarray, int] = ..., + start: int = ..., + stop: int = ...) -> int: ... + +def strip(a: bitarray, mode: str = ...) -> bitarray: ... + +def count_n(a: bitarray, + n: int, + value: int = ...) -> int: ... + +def parity(a: bitarray) -> int: ... +def count_and(a: bitarray, b: bitarray) -> int: ... +def count_or(a: bitarray, b: bitarray) -> int: ... +def count_xor(a: bitarray, b: bitarray) -> int: ... +def any_and(a: bitarray, b: bitarray) -> bool: ... +def subset(a: bitarray, b: bitarray) -> bool: ... +def _correspond_all(a: bitarray, b: bitarray) -> tuple: ... + +def intervals(a: bitarray) -> Iterator: ... + +def ba2hex(a: bitarray) -> str: ... +def hex2ba(s: AnyStr, endian: Optional[str] = ...) -> bitarray: ... +def ba2base(n: int, a: bitarray) -> str: ... +def base2ba(n: int, + s: AnyStr, + endian: Optional[str] = ...) -> bitarray: ... + +def ba2int(a: bitarray, signed: int = ...) -> int: ... +def int2ba(i: int, + length: int = ..., + endian: str = ..., + signed: int = ...) -> bitarray: ... + +def serialize(a: bitarray) -> bytes: ... +def deserialize(b: BytesLike) -> bitarray: ... +def sc_encode(a: bitarray) -> bytes: ... +def sc_decode(stream: BytesLike) -> bitarray: ... +def vl_encode(a: bitarray) -> bytes: ... +def vl_decode(stream: BytesLike, + endian: Optional[str] = ...) -> bitarray: ... + +def _huffman_tree(freq_map: FreqMap) -> Any: ... +def huffman_code(freq_map: FreqMap, + endian: Optional[str] = ...) -> CodeDict: ... +def canonical_huffman(Freq_Map) -> tuple[CodeDict, list, list]: ... +def canonical_decode(a: bitarray, + count: Sequence[int], + symbol: Iterable[Any]) -> Iterator: ... diff --git a/code/.venv/lib/python3.12/site-packages/bitstring-4.2.3.dist-info/INSTALLER b/code/.venv/lib/python3.12/site-packages/bitstring-4.2.3.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/bitstring-4.2.3.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/code/.venv/lib/python3.12/site-packages/bitstring-4.2.3.dist-info/LICENSE b/code/.venv/lib/python3.12/site-packages/bitstring-4.2.3.dist-info/LICENSE new file mode 100644 index 0000000..b6ecdf5 --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/bitstring-4.2.3.dist-info/LICENSE @@ -0,0 +1,21 @@ +The MIT License + +Copyright (c) 2006 Scott Griffiths (dr.scottgriffiths@gmail.com) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/code/.venv/lib/python3.12/site-packages/bitstring-4.2.3.dist-info/METADATA b/code/.venv/lib/python3.12/site-packages/bitstring-4.2.3.dist-info/METADATA new file mode 100644 index 0000000..17d6f14 --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/bitstring-4.2.3.dist-info/METADATA @@ -0,0 +1,133 @@ +Metadata-Version: 2.1 +Name: bitstring +Version: 4.2.3 +Summary: Simple construction, analysis and modification of binary data. +Author-email: Scott Griffiths +Project-URL: homepage, https://github.com/scott-griffiths/bitstring +Project-URL: documentation, https://bitstring.readthedocs.io/ +Keywords: binary,bitarray,bitvector,bitfield +Classifier: Development Status :: 5 - Production/Stable +Classifier: Intended Audience :: Developers +Classifier: Operating System :: OS Independent +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: 3.9 +Classifier: Programming Language :: Python :: 3.10 +Classifier: Programming Language :: Python :: 3.11 +Classifier: Programming Language :: Python :: 3.12 +Classifier: License :: OSI Approved :: MIT License +Classifier: Topic :: Software Development :: Libraries :: Python Modules +Requires-Python: >=3.8 +Description-Content-Type: text/markdown +License-File: LICENSE +Requires-Dist: bitarray <3.0.0,>=2.9.0 + + + +![bitstring](https://raw.githubusercontent.com/scott-griffiths/bitstring/main/doc/bitstring_logo_small.png "bitstring") + +**bitstring** is a Python module to help make the creation and analysis of all types of bit-level binary data as simple and efficient as possible. + +It has been actively maintained since 2006. + + + +[![CI badge](https://github.com/scott-griffiths/bitstring/actions/workflows/.github/workflows/ci.yml/badge.svg)](https://github.com/scott-griffiths/bitstring/actions/workflows/ci.yml) +[![Docs](https://img.shields.io/readthedocs/bitstring?logo=readthedocs&logoColor=white)](https://bitstring.readthedocs.io/en/latest/) +[![Codacy Badge](https://img.shields.io/codacy/grade/8869499b2eed44548fa1a5149dd451f4?logo=codacy)](https://app.codacy.com/gh/scott-griffiths/bitstring/dashboard?utm_source=gh&utm_medium=referral&utm_content=&utm_campaign=Badge_grade) +[![Dependents (via libraries.io)](https://img.shields.io/librariesio/dependents/pypi/bitstring?logo=libraries.io&logoColor=white)](https://libraries.io/pypi/bitstring) +    +[![Pepy Total Downlods](https://img.shields.io/pepy/dt/bitstring?logo=python&logoColor=white&labelColor=blue&color=blue)](https://www.pepy.tech/projects/bitstring) +[![PyPI - Downloads](https://img.shields.io/pypi/dm/bitstring?label=%40&labelColor=blue&color=blue)](https://pypistats.org/packages/bitstring) + +---- + +> [!NOTE] +> To see what been added, improved or fixed, and also to see what's coming in the next version, see the [release notes](https://github.com/scott-griffiths/bitstring/blob/main/release_notes.md). + + +# Overview + +* Efficiently store and manipulate binary data in idiomatic Python. +* Create bitstrings from hex, octal, binary, files, formatted strings, bytes, integers and floats of different endiannesses. +* Powerful binary packing and unpacking functions. +* Bit-level slicing, joining, searching, replacing and more. +* Create and manipulate arrays of fixed-length bitstrings. +* Read from and interpret bitstrings as streams of binary data. +* Rich API - chances are that whatever you want to do there's a simple and elegant way of doing it. +* Open source software, released under the MIT licence. + +# Documentation + +Extensive documentation for the bitstring module is available. +Some starting points are given below: + +* [Overview](https://bitstring.readthedocs.io/en/stable/index.html) +* [Quick Reference](https://bitstring.readthedocs.io/en/stable/quick_reference.html) +* [Full Reference](https://bitstring.readthedocs.io/en/stable/reference.html) + +There is also an introductory walkthrough notebook on [binder](https://mybinder.org/v2/gh/scott-griffiths/bitstring/main?labpath=doc%2Fwalkthrough.ipynb). + +# Examples + +### Installation +``` +$ pip install bitstring +``` + +### Creation +```pycon +>>> from bitstring import Bits, BitArray, BitStream, pack +>>> a = BitArray(bin='00101') +>>> b = Bits(a_file_object) +>>> c = BitArray('0xff, 0b101, 0o65, uint6=22') +>>> d = pack('intle16, hex=a, 0b1', 100, a='0x34f') +>>> e = pack('<16h', *range(16)) +``` + +### Different interpretations, slicing and concatenation +```pycon +>>> a = BitArray('0x3348') +>>> a.hex, a.bin, a.uint, a.float, a.bytes +('3348', '0011001101001000', 13128, 0.2275390625, b'3H') +>>> a[10:3:-1].bin +'0101100' +>>> '0b100' + 3*a +BitArray('0x866906690669, 0b000') +``` + +### Reading data sequentially +```pycon +>>> b = BitStream('0x160120f') +>>> b.read(12).hex +'160' +>>> b.pos = 0 +>>> b.read('uint12') +352 +>>> b.readlist('uint12, bin3') +[288, '111'] +``` + +### Searching, inserting and deleting +```pycon +>>> c = BitArray('0b00010010010010001111') # c.hex == '0x1248f' +>>> c.find('0x48') +(8,) +>>> c.replace('0b001', '0xabc') +>>> c.insert('0b0000', pos=3) +>>> del c[12:16] +``` + +### Arrays of fixed-length formats +```pycon +>>> from bitstring import Array +>>> a = Array('uint7', [9, 100, 3, 1]) +>>> a.data +BitArray('0x1390181') +>>> a[::2] *= 5 +>>> a +Array('uint7', [45, 100, 15, 1]) +``` + + +Copyright (c) 2006 - 2024 Scott Griffiths diff --git a/code/.venv/lib/python3.12/site-packages/bitstring-4.2.3.dist-info/RECORD b/code/.venv/lib/python3.12/site-packages/bitstring-4.2.3.dist-info/RECORD new file mode 100644 index 0000000..f7df7e3 --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/bitstring-4.2.3.dist-info/RECORD @@ -0,0 +1,39 @@ +bitstring-4.2.3.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +bitstring-4.2.3.dist-info/LICENSE,sha256=NwXu1akj812b-sofEOkTbMhNbldlcK7GYb2mmZHxKeo,1106 +bitstring-4.2.3.dist-info/METADATA,sha256=aUUR8ljmKXchDLSPxTGzHot_7Wi659yUYUWS5Zi-3Ek,5017 +bitstring-4.2.3.dist-info/RECORD,, +bitstring-4.2.3.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92 +bitstring-4.2.3.dist-info/top_level.txt,sha256=9Xh4qfKH0fMhwxzzkSBk3uzTRGiYPPM1FuYFAgCB-ks,10 +bitstring/__init__.py,sha256=GjT0V01IeR3DBanzVa5utd4csONZx-PUSr6H0fuXnNU,13899 +bitstring/__main__.py,sha256=Lg6ARiAqKssp_uj1msYpiTGDybhJRshQmeftQ-yTpc0,1658 +bitstring/__pycache__/__init__.cpython-312.pyc,, +bitstring/__pycache__/__main__.cpython-312.pyc,, +bitstring/__pycache__/array_.cpython-312.pyc,, +bitstring/__pycache__/bitarray_.cpython-312.pyc,, +bitstring/__pycache__/bits.cpython-312.pyc,, +bitstring/__pycache__/bitstore.cpython-312.pyc,, +bitstring/__pycache__/bitstore_helpers.cpython-312.pyc,, +bitstring/__pycache__/bitstream.cpython-312.pyc,, +bitstring/__pycache__/bitstring_options.cpython-312.pyc,, +bitstring/__pycache__/dtypes.cpython-312.pyc,, +bitstring/__pycache__/exceptions.cpython-312.pyc,, +bitstring/__pycache__/fp8.cpython-312.pyc,, +bitstring/__pycache__/luts.cpython-312.pyc,, +bitstring/__pycache__/methods.cpython-312.pyc,, +bitstring/__pycache__/mxfp.cpython-312.pyc,, +bitstring/__pycache__/utils.cpython-312.pyc,, +bitstring/array_.py,sha256=d4vuZAc0ecJC95RD5_WPApehyH-ZGms4pcxl2I0R90I,36201 +bitstring/bitarray_.py,sha256=kZTVYBo32vQuhQ4TZQldQkqRE5OMPVoKCoxEfKOTg8U,22319 +bitstring/bits.py,sha256=N2bszuYM_WrTfqx-5-m56Vu_XgPZLeTta4xysBqCSYs,76754 +bitstring/bitstore.py,sha256=jHk1cwBPqCJoYq2TEgIE37u_7K8HZq5K0L__X8edMgc,10337 +bitstring/bitstore_helpers.py,sha256=6UHBomeOZlfQmY_Wa17U1zi7FBo73n2D9GPPmPztIrk,9107 +bitstring/bitstream.py,sha256=Hu1ZpcCeP6lfpJiKAwKoJI22A-vsKATN1F2RvnS3rFY,29017 +bitstring/bitstring_options.py,sha256=54Io4xLDylwXdRQpb12eNkSpSXQhRTW9Aidx7G42h7w,3560 +bitstring/dtypes.py,sha256=oBrcavnz15vaBZ_GOVm0tOaW6Ml0oXoz8RyZSyqG6Zo,16897 +bitstring/exceptions.py,sha256=7O3oVJJgvUOadqIzg6U-8fUGyOTu_lmjbMtX3yPc18o,553 +bitstring/fp8.py,sha256=8CIiGlijn9OmBwdpJOl-1oVPUY6qVL2jGl1N8u8LjAg,3768 +bitstring/luts.py,sha256=KPuXC9MvTOQSOlb0bFBKbnuxXuVBYlhaFEdIgSrwtzs,27072 +bitstring/methods.py,sha256=3JM4jo8B6r3xQYY2nvbibmf7byQYTHTZye0F8D2lF10,4351 +bitstring/mxfp.py,sha256=IxXK0SZy5RTD883YwATg6tlqVfl1uCFQplp5BVBwx9Q,9058 +bitstring/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +bitstring/utils.py,sha256=tuWHppQUcCVLbtrZoO7eUkpNk6pJLtsOZT7QdWADW78,9221 diff --git a/code/.venv/lib/python3.12/site-packages/bitstring-4.2.3.dist-info/WHEEL b/code/.venv/lib/python3.12/site-packages/bitstring-4.2.3.dist-info/WHEEL new file mode 100644 index 0000000..bab98d6 --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/bitstring-4.2.3.dist-info/WHEEL @@ -0,0 +1,5 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.43.0) +Root-Is-Purelib: true +Tag: py3-none-any + diff --git a/code/.venv/lib/python3.12/site-packages/bitstring-4.2.3.dist-info/top_level.txt b/code/.venv/lib/python3.12/site-packages/bitstring-4.2.3.dist-info/top_level.txt new file mode 100644 index 0000000..3ef76dc --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/bitstring-4.2.3.dist-info/top_level.txt @@ -0,0 +1 @@ +bitstring diff --git a/code/.venv/lib/python3.12/site-packages/bitstring/__init__.py b/code/.venv/lib/python3.12/site-packages/bitstring/__init__.py new file mode 100644 index 0000000..87cd54b --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/bitstring/__init__.py @@ -0,0 +1,337 @@ +#!/usr/bin/env python +r""" +This package defines classes that simplify bit-wise creation, manipulation and +interpretation of data. + +Classes: + +Bits -- An immutable container for binary data. +BitArray -- A mutable container for binary data. +ConstBitStream -- An immutable container with streaming methods. +BitStream -- A mutable container with streaming methods. +Array -- An efficient list-like container where each item has a fixed-length binary format. +Dtype -- Encapsulate the data types used in the other classes. + +Functions: + +pack -- Create a BitStream from a format string. + +Data: + +options -- Module-wide options. + +Exceptions: + +Error -- Module exception base class. +CreationError -- Error during creation. +InterpretError -- Inappropriate interpretation of binary data. +ByteAlignError -- Whole byte position or length needed. +ReadError -- Reading or peeking past the end of a bitstring. + +https://github.com/scott-griffiths/bitstring +""" + +__licence__ = """ +The MIT License + +Copyright (c) 2006 Scott Griffiths (dr.scottgriffiths@gmail.com) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +""" + +__version__ = "4.2.3" + +__author__ = "Scott Griffiths" + +import sys + +from .bits import Bits +from .bitstring_options import Options +from .bitarray_ import BitArray +from .bitstream import ConstBitStream, BitStream +from .methods import pack +from .array_ import Array +from .exceptions import Error, ReadError, InterpretError, ByteAlignError, CreationError +from .dtypes import DtypeDefinition, dtype_register, Dtype +import types +from typing import List, Tuple, Literal +from .mxfp import decompress_luts as mxfp_decompress_luts +from .fp8 import decompress_luts as binary8_decompress_luts + +# Decompress the LUTs for the exotic floating point formats +mxfp_decompress_luts() +binary8_decompress_luts() + +# The Options class returns a singleton. +options = Options() + +# These get defined properly by the module magic below. This just stops mypy complaining about them. +bytealigned = lsb0 = None + + +# An opaque way of adding module level properties. Taken from https://peps.python.org/pep-0549/ +# This is now deprecated. Use the options object directly instead. +class _MyModuleType(types.ModuleType): + @property + def bytealigned(self) -> bool: + """Determines whether a number of methods default to working only on byte boundaries.""" + return options.bytealigned + + @bytealigned.setter + def bytealigned(self, value: bool) -> None: + """Determines whether a number of methods default to working only on byte boundaries.""" + options.bytealigned = value + + @property + def lsb0(self) -> bool: + """If True, the least significant bit (the final bit) is indexed as bit zero.""" + return options.lsb0 + + @lsb0.setter + def lsb0(self, value: bool) -> None: + """If True, the least significant bit (the final bit) is indexed as bit zero.""" + options.lsb0 = value + + +sys.modules[__name__].__class__ = _MyModuleType + + +# These methods convert a bit length to the number of characters needed to print it for different interpretations. +def hex_bits2chars(bitlength: int): + # One character for every 4 bits + return bitlength // 4 + + +def oct_bits2chars(bitlength: int): + # One character for every 3 bits + return bitlength // 3 + + +def bin_bits2chars(bitlength: int): + # One character for each bit + return bitlength + + +def bytes_bits2chars(bitlength: int): + # One character for every 8 bits + return bitlength // 8 + + +def uint_bits2chars(bitlength: int): + # How many characters is largest possible int of this length? + return len(str((1 << bitlength) - 1)) + + +def int_bits2chars(bitlength: int): + # How many characters is largest negative int of this length? (To include minus sign). + return len(str((-1 << (bitlength - 1)))) + + +def float_bits2chars(bitlength: Literal[16, 32, 64]): + # These bit lengths were found by looking at lots of possible values + if bitlength in [16, 32]: + return 23 # Empirical value + else: + return 24 # Empirical value + + +def p3binary_bits2chars(_: Literal[8]): + return 19 # Empirical value + + +def p4binary_bits2chars(_: Literal[8]): + # Found by looking at all the possible values + return 13 # Empirical value + + +def e4m3mxfp_bits2chars(_: Literal[8]): + return 13 + + +def e5m2mxfp_bits2chars(_: Literal[8]): + return 19 + + +def e3m2mxfp_bits2chars(_: Literal[6]): + # Not sure what the best value is here. It's 7 without considering the scale that could be applied. + return 7 + + +def e2m3mxfp_bits2chars(_: Literal[6]): + # Not sure what the best value is here. + return 7 + + +def e2m1mxfp_bits2chars(_: Literal[4]): + # Not sure what the best value is here. + return 7 + + +def e8m0mxfp_bits2chars(_: Literal[8]): + # Has same range as float32 + return 23 + + +def mxint_bits2chars(_: Literal[8]): + # Not sure what the best value is here. + return 10 + + +def bfloat_bits2chars(_: Literal[16]): + # Found by looking at all the possible values + return 23 # Empirical value + + +def bits_bits2chars(bitlength: int): + # For bits type we can see how long it needs to be printed by trying any value + temp = Bits(bitlength) + return len(str(temp)) + + +def bool_bits2chars(_: Literal[1]): + # Bools are printed as 1 or 0, not True or False, so are one character each + return 1 + + +dtype_definitions = [ + # Integer types + DtypeDefinition('uint', Bits._setuint, Bits._getuint, int, False, uint_bits2chars, + description="a two's complement unsigned int"), + DtypeDefinition('uintle', Bits._setuintle, Bits._getuintle, int, False, uint_bits2chars, + allowed_lengths=(8, 16, 24, ...), description="a two's complement little-endian unsigned int"), + DtypeDefinition('uintbe', Bits._setuintbe, Bits._getuintbe, int, False, uint_bits2chars, + allowed_lengths=(8, 16, 24, ...), description="a two's complement big-endian unsigned int"), + DtypeDefinition('int', Bits._setint, Bits._getint, int, True, int_bits2chars, + description="a two's complement signed int"), + DtypeDefinition('intle', Bits._setintle, Bits._getintle, int, True, int_bits2chars, + allowed_lengths=(8, 16, 24, ...), description="a two's complement little-endian signed int"), + DtypeDefinition('intbe', Bits._setintbe, Bits._getintbe, int, True, int_bits2chars, + allowed_lengths=(8, 16, 24, ...), description="a two's complement big-endian signed int"), + # String types + DtypeDefinition('hex', Bits._sethex, Bits._gethex, str, False, hex_bits2chars, + allowed_lengths=(0, 4, 8, ...), description="a hexadecimal string"), + DtypeDefinition('bin', Bits._setbin_safe, Bits._getbin, str, False, bin_bits2chars, + description="a binary string"), + DtypeDefinition('oct', Bits._setoct, Bits._getoct, str, False, oct_bits2chars, + allowed_lengths=(0, 3, 6, ...), description="an octal string"), + # Float types + DtypeDefinition('float', Bits._setfloatbe, Bits._getfloatbe, float, True, float_bits2chars, + allowed_lengths=(16, 32, 64), description="a big-endian floating point number"), + DtypeDefinition('floatle', Bits._setfloatle, Bits._getfloatle, float, True, float_bits2chars, + allowed_lengths=(16, 32, 64), description="a little-endian floating point number"), + DtypeDefinition('bfloat', Bits._setbfloatbe, Bits._getbfloatbe, float, True, bfloat_bits2chars, + allowed_lengths=(16,), description="a 16 bit big-endian bfloat floating point number"), + DtypeDefinition('bfloatle', Bits._setbfloatle, Bits._getbfloatle, float, True, bfloat_bits2chars, + allowed_lengths=(16,), description="a 16 bit little-endian bfloat floating point number"), + # Other known length types + DtypeDefinition('bits', Bits._setbits, Bits._getbits, Bits, False, bits_bits2chars, + description="a bitstring object"), + DtypeDefinition('bool', Bits._setbool, Bits._getbool, bool, False, bool_bits2chars, + allowed_lengths=(1,), description="a bool (True or False)"), + DtypeDefinition('bytes', Bits._setbytes, Bits._getbytes, bytes, False, bytes_bits2chars, + multiplier=8, description="a bytes object"), + # Unknown length types + DtypeDefinition('se', Bits._setse, Bits._getse, int, True, None, + variable_length=True, description="a signed exponential-Golomb code"), + DtypeDefinition('ue', Bits._setue, Bits._getue, int, False, None, + variable_length=True, description="an unsigned exponential-Golomb code"), + DtypeDefinition('sie', Bits._setsie, Bits._getsie, int, True, None, + variable_length=True, description="a signed interleaved exponential-Golomb code"), + DtypeDefinition('uie', Bits._setuie, Bits._getuie, int, False, None, + variable_length=True, description="an unsigned interleaved exponential-Golomb code"), + # Special case pad type + DtypeDefinition('pad', Bits._setpad, Bits._getpad, None, False, None, + description="a skipped section of padding"), + + # MXFP and IEEE 8-bit float types + DtypeDefinition('p3binary', Bits._setp3binary, Bits._getp3binary, float, True, p3binary_bits2chars, + allowed_lengths=(8,), description="an 8 bit float with binary8p3 format"), + DtypeDefinition('p4binary', Bits._setp4binary, Bits._getp4binary, float, True, p4binary_bits2chars, + allowed_lengths=(8,), description="an 8 bit float with binary8p4 format"), + DtypeDefinition('e4m3mxfp', Bits._sete4m3mxfp, Bits._gete4m3mxfp, float, True, e4m3mxfp_bits2chars, + allowed_lengths=(8,), description="an 8 bit float with MXFP E4M3 format"), + DtypeDefinition('e5m2mxfp', Bits._sete5m2mxfp, Bits._gete5m2mxfp, float, True, e5m2mxfp_bits2chars, + allowed_lengths=(8,), description="an 8 bit float with MXFP E5M2 format"), + DtypeDefinition('e3m2mxfp', Bits._sete3m2mxfp, Bits._gete3m2mxfp, float, True, e3m2mxfp_bits2chars, + allowed_lengths=(6,), description="a 6 bit float with MXFP E3M2 format"), + DtypeDefinition('e2m3mxfp', Bits._sete2m3mxfp, Bits._gete2m3mxfp, float, True, e2m3mxfp_bits2chars, + allowed_lengths=(6,), description="a 6 bit float with MXFP E2M3 format"), + DtypeDefinition('e2m1mxfp', Bits._sete2m1mxfp, Bits._gete2m1mxfp, float, True, e2m1mxfp_bits2chars, + allowed_lengths=(4,), description="a 4 bit float with MXFP E2M1 format"), + DtypeDefinition('e8m0mxfp', Bits._sete8m0mxfp, Bits._gete8m0mxfp, float, False, e8m0mxfp_bits2chars, + allowed_lengths=(8,), description="an 8 bit float with MXFP E8M0 format"), + DtypeDefinition('mxint', Bits._setmxint, Bits._getmxint, float, True, mxint_bits2chars, + allowed_lengths=(8,), description="an 8 bit float with MXFP INT8 format"), +] + + +aliases: List[Tuple[str, str]] = [ + # Floats default to big endian + ('float', 'floatbe'), + ('bfloat', 'bfloatbe'), + + # Some single letter aliases for popular types + ('int', 'i'), + ('uint', 'u'), + ('hex', 'h'), + ('oct', 'o'), + ('bin', 'b'), + ('float', 'f'), +] + +# Create native-endian aliases depending on the byteorder of the system +byteorder: str = sys.byteorder +if byteorder == 'little': + aliases.extend([ + ('uintle', 'uintne'), + ('intle', 'intne'), + ('floatle', 'floatne'), + ('bfloatle', 'bfloatne'), + ]) +else: + aliases.extend([ + ('uintbe', 'uintne'), + ('intbe', 'intne'), + ('floatbe', 'floatne'), + ('bfloatbe', 'bfloatne'), + ]) + + +for dt in dtype_definitions: + dtype_register.add_dtype(dt) +for alias in aliases: + dtype_register.add_dtype_alias(alias[0], alias[1]) + +property_docstrings = [f'{name} -- Interpret as {dtype_register[name].description}.' for name in dtype_register.names] +property_docstring = '\n '.join(property_docstrings) + +# We can't be sure the docstrings are present, as it might be compiled without docstrings. +if Bits.__doc__ is not None: + Bits.__doc__ = Bits.__doc__.replace('[GENERATED_PROPERTY_DESCRIPTIONS]', property_docstring) +if BitArray.__doc__ is not None: + BitArray.__doc__ = BitArray.__doc__.replace('[GENERATED_PROPERTY_DESCRIPTIONS]', property_docstring) +if ConstBitStream.__doc__ is not None: + ConstBitStream.__doc__ = ConstBitStream.__doc__.replace('[GENERATED_PROPERTY_DESCRIPTIONS]', property_docstring) +if BitStream.__doc__ is not None: + BitStream.__doc__ = BitStream.__doc__.replace('[GENERATED_PROPERTY_DESCRIPTIONS]', property_docstring) + + +__all__ = ['ConstBitStream', 'BitStream', 'BitArray', 'Array', + 'Bits', 'pack', 'Error', 'ReadError', 'InterpretError', + 'ByteAlignError', 'CreationError', 'bytealigned', 'lsb0', 'Dtype', 'options'] diff --git a/code/.venv/lib/python3.12/site-packages/bitstring/__main__.py b/code/.venv/lib/python3.12/site-packages/bitstring/__main__.py new file mode 100644 index 0000000..f5949ff --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/bitstring/__main__.py @@ -0,0 +1,50 @@ +import sys +from bitstring.bits import Bits +from bitstring.dtypes import Register + +dtype_register = Register() + + +def main() -> None: + # check if final parameter is an interpretation string + fp = sys.argv[-1] + if fp in ['-h', '--help'] or len(sys.argv) == 1: + print("""Create and interpret a bitstring from command-line parameters. + +Command-line parameters are concatenated and a bitstring created +from them. If the final parameter is either an interpretation string +or ends with a '.' followed by an interpretation string then that +interpretation of the bitstring will be used when printing it. + +Typical usage might be invoking the Python module from a console +as a one-off calculation: + +$ python -m bitstring int:16=-400 +0xfe70 +$ python -m bitstring float:32=0.2 bin +00111110010011001100110011001101 +$ python -m bitstring 0xff 3*0b01,0b11 uint +65367 +$ python -m bitstring hex=01, uint:12=352.hex +01160 + """) + return + if fp in dtype_register.names: + # concatenate all other parameters and interpret using the final one + b1 = Bits(','.join(sys.argv[1: -1])) + print(b1._readtoken(fp, 0, b1.__len__())[0]) + else: + # does final parameter end with a dot then an interpretation string? + interp = fp[fp.rfind('.') + 1:] + if interp in dtype_register.names: + sys.argv[-1] = fp[:fp.rfind('.')] + b1 = Bits(','.join(sys.argv[1:])) + print(b1._readtoken(interp, 0, b1.__len__())[0]) + else: + # No interpretation - just use default print + b1 = Bits(','.join(sys.argv[1:])) + print(b1) + + +if __name__ == '__main__': + main() diff --git a/code/.venv/lib/python3.12/site-packages/bitstring/__pycache__/__init__.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/bitstring/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..cab7a65 Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/bitstring/__pycache__/__init__.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/bitstring/__pycache__/__main__.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/bitstring/__pycache__/__main__.cpython-312.pyc new file mode 100644 index 0000000..55ebe75 Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/bitstring/__pycache__/__main__.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/bitstring/__pycache__/array_.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/bitstring/__pycache__/array_.cpython-312.pyc new file mode 100644 index 0000000..1fc2ef5 Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/bitstring/__pycache__/array_.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/bitstring/__pycache__/bitarray_.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/bitstring/__pycache__/bitarray_.cpython-312.pyc new file mode 100644 index 0000000..6cd42b4 Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/bitstring/__pycache__/bitarray_.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/bitstring/__pycache__/bits.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/bitstring/__pycache__/bits.cpython-312.pyc new file mode 100644 index 0000000..6cb7eb8 Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/bitstring/__pycache__/bits.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/bitstring/__pycache__/bitstore.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/bitstring/__pycache__/bitstore.cpython-312.pyc new file mode 100644 index 0000000..0210adc Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/bitstring/__pycache__/bitstore.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/bitstring/__pycache__/bitstore_helpers.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/bitstring/__pycache__/bitstore_helpers.cpython-312.pyc new file mode 100644 index 0000000..beaf635 Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/bitstring/__pycache__/bitstore_helpers.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/bitstring/__pycache__/bitstream.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/bitstring/__pycache__/bitstream.cpython-312.pyc new file mode 100644 index 0000000..0fddcd0 Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/bitstring/__pycache__/bitstream.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/bitstring/__pycache__/bitstring_options.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/bitstring/__pycache__/bitstring_options.cpython-312.pyc new file mode 100644 index 0000000..85b0336 Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/bitstring/__pycache__/bitstring_options.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/bitstring/__pycache__/dtypes.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/bitstring/__pycache__/dtypes.cpython-312.pyc new file mode 100644 index 0000000..bb87ea2 Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/bitstring/__pycache__/dtypes.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/bitstring/__pycache__/exceptions.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/bitstring/__pycache__/exceptions.cpython-312.pyc new file mode 100644 index 0000000..ed5cac6 Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/bitstring/__pycache__/exceptions.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/bitstring/__pycache__/fp8.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/bitstring/__pycache__/fp8.cpython-312.pyc new file mode 100644 index 0000000..0916deb Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/bitstring/__pycache__/fp8.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/bitstring/__pycache__/luts.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/bitstring/__pycache__/luts.cpython-312.pyc new file mode 100644 index 0000000..0558561 Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/bitstring/__pycache__/luts.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/bitstring/__pycache__/methods.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/bitstring/__pycache__/methods.cpython-312.pyc new file mode 100644 index 0000000..66dffc4 Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/bitstring/__pycache__/methods.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/bitstring/__pycache__/mxfp.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/bitstring/__pycache__/mxfp.cpython-312.pyc new file mode 100644 index 0000000..4640738 Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/bitstring/__pycache__/mxfp.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/bitstring/__pycache__/utils.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/bitstring/__pycache__/utils.cpython-312.pyc new file mode 100644 index 0000000..a89bc68 Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/bitstring/__pycache__/utils.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/bitstring/array_.py b/code/.venv/lib/python3.12/site-packages/bitstring/array_.py new file mode 100644 index 0000000..5aaed8b --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/bitstring/array_.py @@ -0,0 +1,782 @@ +from __future__ import annotations + +import math +import numbers +from collections.abc import Sized +from bitstring.exceptions import CreationError +from typing import Union, List, Iterable, Any, Optional, BinaryIO, overload, TextIO +from bitstring.bits import Bits, BitsType +from bitstring.bitarray_ import BitArray +from bitstring.dtypes import Dtype, dtype_register +from bitstring import utils +from bitstring.bitstring_options import Options, Colour +import copy +import array +import operator +import io +import sys + +# The possible types stored in each element of the Array +ElementType = Union[float, str, int, bytes, bool, Bits] + +options = Options() + + +class Array: + """Return an Array whose elements are initialised according to the fmt string. + The dtype string can be typecode as used in the struct module or any fixed-length bitstring + format. + + a = Array('>H', [1, 15, 105]) + b = Array('int5', [-9, 0, 4]) + + The Array data is stored compactly as a BitArray object and the Array behaves very like + a list of items of the given format. Both the Array data and fmt properties can be freely + modified after creation. If the data length is not a multiple of the fmt length then the + Array will have 'trailing_bits' which will prevent some methods from appending to the + Array. + + Methods: + + append() -- Append a single item to the end of the Array. + byteswap() -- Change byte endianness of all items. + count() -- Count the number of occurences of a value. + extend() -- Append new items to the end of the Array from an iterable. + fromfile() -- Append items read from a file object. + insert() -- Insert an item at a given position. + pop() -- Remove and return an item. + pp() -- Pretty print the Array. + reverse() -- Reverse the order of all items. + tobytes() -- Return Array data as bytes object, padding with zero bits at the end if needed. + tofile() -- Write Array data to a file, padding with zero bits at the end if needed. + tolist() -- Return Array items as a list. + + Special methods: + + Also available are the operators [], ==, !=, +, *, <<, >>, &, |, ^, + plus the mutating operators [], +=, *=, <<=, >>=, &=, |=, ^=. + + Properties: + + data -- The BitArray binary data of the Array. Can be freely modified. + dtype -- The format string or typecode. Can be freely modified. + itemsize -- The length *in bits* of a single item. Read only. + trailing_bits -- If the data length is not a multiple of the fmt length, this BitArray + gives the leftovers at the end of the data. + + + """ + + def __init__(self, dtype: Union[str, Dtype], initializer: Optional[Union[int, Array, array.array, Iterable, Bits, bytes, bytearray, memoryview, BinaryIO]] = None, + trailing_bits: Optional[BitsType] = None) -> None: + self.data = BitArray() + if isinstance(dtype, Dtype) and dtype.scale == 'auto': + if isinstance(initializer, (int, Bits, bytes, bytearray, memoryview, BinaryIO)): + raise TypeError("An Array with an 'auto' scale factor can only be created from an iterable of values.") + auto_scale = self._calculate_auto_scale(initializer, dtype.name, dtype.length) + dtype = Dtype(dtype.name, dtype.length, scale=auto_scale) + try: + self._set_dtype(dtype) + except ValueError as e: + raise CreationError(e) + + if isinstance(initializer, numbers.Integral): + self.data = BitArray(initializer * self._dtype.bitlength) + elif isinstance(initializer, (Bits, bytes, bytearray, memoryview)): + self.data += initializer + elif isinstance(initializer, io.BufferedReader): + self.fromfile(initializer) + elif initializer is not None: + self.extend(initializer) + + if trailing_bits is not None: + self.data += BitArray._create_from_bitstype(trailing_bits) + + _largest_values = None + + @staticmethod + def _calculate_auto_scale(initializer, name: str, length: Optional[int]) -> float: + # Now need to find the largest power of 2 representable with this format. + if Array._largest_values is None: + Array._largest_values = { + 'mxint8': Bits('0b01111111').mxint8, # 1.0 + 63.0/64.0, + 'e2m1mxfp4': Bits('0b0111').e2m1mxfp4, # 6.0 + 'e2m3mxfp6': Bits('0b011111').e2m3mxfp6, # 7.5 + 'e3m2mxfp6': Bits('0b011111').e3m2mxfp6, # 28.0 + 'e4m3mxfp8': Bits('0b01111110').e4m3mxfp8, # 448.0 + 'e5m2mxfp8': Bits('0b01111011').e5m2mxfp8, # 57344.0 + 'p4binary8': Bits('0b01111110').p4binary8, # 224.0 + 'p3binary8': Bits('0b01111110').p3binary8, # 49152.0 + 'float16': Bits('0x7bff').float16, # 65504.0 + # The bfloat range is so large the scaling algorithm doesn't work well, so I'm disallowing it. + # 'bfloat16': Bits('0x7f7f').bfloat16, # 3.38953139e38, + } + if f'{name}{length}' in Array._largest_values.keys(): + float_values = Array('float64', initializer).tolist() + if not float_values: + raise ValueError("Can't calculate an 'auto' scale with an empty Array initializer.") + max_float_value = max(abs(x) for x in float_values) + if max_float_value == 0: + # This special case isn't covered in the standard. I'm choosing to return no scale. + return 1.0 + # We need to find the largest power of 2 that is less than the max value + log2 = math.floor(math.log2(max_float_value)) + lp2 = math.floor(math.log2(Array._largest_values[f'{name}{length}'])) + lg_scale = log2 - lp2 + # Saturate at values representable in E8M0 format. + if lg_scale > 127: + lg_scale = 127 + elif lg_scale < -127: + lg_scale = -127 + return 2 ** lg_scale + else: + raise ValueError(f"Can't calculate auto scale for format '{name}{length}'. " + f"This feature is only available for these formats: {list(Array._largest_values.keys())}.") + + @property + def itemsize(self) -> int: + return self._dtype.length + + @property + def trailing_bits(self) -> BitArray: + trailing_bit_length = len(self.data) % self._dtype.bitlength + return BitArray() if trailing_bit_length == 0 else self.data[-trailing_bit_length:] + + @property + def dtype(self) -> Dtype: + return self._dtype + + @dtype.setter + def dtype(self, new_dtype: Union[str, Dtype]) -> None: + self._set_dtype(new_dtype) + + def _set_dtype(self, new_dtype: Union[str, Dtype]) -> None: + if isinstance(new_dtype, Dtype): + self._dtype = new_dtype + else: + try: + dtype = Dtype(new_dtype) + except ValueError: + name_length = utils.parse_single_struct_token(new_dtype) + if name_length is not None: + dtype = Dtype(name_length[0], name_length[1]) + else: + raise ValueError(f"Inappropriate Dtype for Array: '{new_dtype}'.") + if dtype.length is None: + raise ValueError(f"A fixed length format is needed for an Array, received '{new_dtype}'.") + self._dtype = dtype + if self._dtype.scale == 'auto': + raise ValueError("A Dtype with an 'auto' scale factor can only be used when creating a new Array.") + + def _create_element(self, value: ElementType) -> Bits: + """Create Bits from value according to the token_name and token_length""" + b = self._dtype.build(value) + if len(b) != self._dtype.length: + raise ValueError(f"The value {value!r} has the wrong length for the format '{self._dtype}'.") + return b + + def __len__(self) -> int: + return len(self.data) // self._dtype.length + + @overload + def __getitem__(self, key: slice) -> Array: + ... + + @overload + def __getitem__(self, key: int) -> ElementType: + ... + + def __getitem__(self, key: Union[slice, int]) -> Union[Array, ElementType]: + if isinstance(key, slice): + start, stop, step = key.indices(len(self)) + if step != 1: + d = BitArray() + for s in range(start * self._dtype.length, stop * self._dtype.length, step * self._dtype.length): + d.append(self.data[s: s + self._dtype.length]) + a = self.__class__(self._dtype) + a.data = d + return a + else: + a = self.__class__(self._dtype) + a.data = self.data[start * self._dtype.length: stop * self._dtype.length] + return a + else: + if key < 0: + key += len(self) + if key < 0 or key >= len(self): + raise IndexError(f"Index {key} out of range for Array of length {len(self)}.") + return self._dtype.read_fn(self.data, start=self._dtype.length * key) + + @overload + def __setitem__(self, key: slice, value: Iterable[ElementType]) -> None: + ... + + @overload + def __setitem__(self, key: int, value: ElementType) -> None: + ... + + def __setitem__(self, key: Union[slice, int], value: Union[Iterable[ElementType], ElementType]) -> None: + if isinstance(key, slice): + start, stop, step = key.indices(len(self)) + if not isinstance(value, Iterable): + raise TypeError("Can only assign an iterable to a slice.") + if step == 1: + new_data = BitArray() + for x in value: + new_data += self._create_element(x) + self.data[start * self._dtype.length: stop * self._dtype.length] = new_data + return + items_in_slice = len(range(start, stop, step)) + if not isinstance(value, Sized): + value = list(value) + if len(value) == items_in_slice: + for s, v in zip(range(start, stop, step), value): + self.data.overwrite(self._create_element(v), s * self._dtype.length) + else: + raise ValueError(f"Can't assign {len(value)} values to an extended slice of length {items_in_slice}.") + else: + if key < 0: + key += len(self) + if key < 0 or key >= len(self): + raise IndexError(f"Index {key} out of range for Array of length {len(self)}.") + start = self._dtype.length * key + self.data.overwrite(self._create_element(value), start) + return + + def __delitem__(self, key: Union[slice, int]) -> None: + if isinstance(key, slice): + start, stop, step = key.indices(len(self)) + if step == 1: + self.data.__delitem__(slice(start * self._dtype.length, stop * self._dtype.length)) + return + # We need to delete from the end or the earlier positions will change + r = reversed(range(start, stop, step)) if step > 0 else range(start, stop, step) + for s in r: + self.data.__delitem__(slice(s * self._dtype.length, (s + 1) * self._dtype.length)) + else: + if key < 0: + key += len(self) + if key < 0 or key >= len(self): + raise IndexError + start = self._dtype.length * key + del self.data[start: start + self._dtype.length] + + def __repr__(self) -> str: + list_str = f"{self.tolist()}" + trailing_bit_length = len(self.data) % self._dtype.length + final_str = "" if trailing_bit_length == 0 else ", trailing_bits=" + repr( + self.data[-trailing_bit_length:]) + return f"Array('{self._dtype}', {list_str}{final_str})" + + def astype(self, dtype: Union[str, Dtype]) -> Array: + """Return Array with elements of new dtype, initialised from current Array.""" + new_array = self.__class__(dtype, self.tolist()) + return new_array + + def tolist(self) -> List[ElementType]: + return [self._dtype.read_fn(self.data, start=start) + for start in range(0, len(self.data) - self._dtype.length + 1, self._dtype.length)] + + def append(self, x: ElementType) -> None: + if len(self.data) % self._dtype.length != 0: + raise ValueError("Cannot append to Array as its length is not a multiple of the format length.") + self.data += self._create_element(x) + + def extend(self, iterable: Union[Array, array.array, Iterable[Any]]) -> None: + if len(self.data) % self._dtype.length != 0: + raise ValueError(f"Cannot extend Array as its data length ({len(self.data)} bits) is not a multiple of the format length ({self._dtype.length} bits).") + if isinstance(iterable, Array): + if self._dtype.name != iterable._dtype.name or self._dtype.length != iterable._dtype.length: + raise TypeError( + f"Cannot extend an Array with format '{self._dtype}' from an Array of format '{iterable._dtype}'.") + # No need to iterate over the elements, we can just append the data + self.data.append(iterable.data) + elif isinstance(iterable, array.array): + # array.array types are always native-endian, hence the '=' + name_value = utils.parse_single_struct_token('=' + iterable.typecode) + if name_value is None: + raise ValueError(f"Cannot extend from array with typecode {iterable.typecode}.") + other_dtype = dtype_register.get_dtype(*name_value, scale=None) + if self._dtype.name != other_dtype.name or self._dtype.length != other_dtype.length: + raise ValueError( + f"Cannot extend an Array with format '{self._dtype}' from an array with typecode '{iterable.typecode}'.") + self.data += iterable.tobytes() + else: + if isinstance(iterable, str): + raise TypeError("Can't extend an Array with a str.") + for item in iterable: + self.data += self._create_element(item) + + def insert(self, i: int, x: ElementType) -> None: + """Insert a new element into the Array at position i. + + """ + i = min(i, len(self)) # Inserting beyond len of array inserts at the end (copying standard behaviour) + self.data.insert(self._create_element(x), i * self._dtype.length) + + def pop(self, i: int = -1) -> ElementType: + """Return and remove an element of the Array. + + Default is to return and remove the final element. + + """ + if len(self) == 0: + raise IndexError("Can't pop from an empty Array.") + x = self[i] + del self[i] + return x + + def byteswap(self) -> None: + """Change the endianness in-place of all items in the Array. + + If the Array format is not a whole number of bytes a ValueError will be raised. + + """ + if self._dtype.length % 8 != 0: + raise ValueError( + f"byteswap can only be used for whole-byte elements. The '{self._dtype}' format is {self._dtype.length} bits long.") + self.data.byteswap(self.itemsize // 8) + + def count(self, value: ElementType) -> int: + """Return count of Array items that equal value. + + value -- The quantity to compare each Array element to. Type should be appropriate for the Array format. + + For floating point types using a value of float('nan') will count the number of elements that are NaN. + + """ + if math.isnan(value): + return sum(math.isnan(i) for i in self) + else: + return sum(i == value for i in self) + + def tobytes(self) -> bytes: + """Return the Array data as a bytes object, padding with zero bits if needed. + + Up to seven zero bits will be added at the end to byte align. + + """ + return self.data.tobytes() + + def tofile(self, f: BinaryIO) -> None: + """Write the Array data to a file object, padding with zero bits if needed. + + Up to seven zero bits will be added at the end to byte align. + + """ + self.data.tofile(f) + + def fromfile(self, f: BinaryIO, n: Optional[int] = None) -> None: + trailing_bit_length = len(self.data) % self._dtype.bitlength + if trailing_bit_length != 0: + raise ValueError(f"Cannot extend Array as its data length ({len(self.data)} bits) is not a multiple of the format length ({self._dtype.bitlength} bits).") + + new_data = Bits(f) + max_items = len(new_data) // self._dtype.length + items_to_append = max_items if n is None else min(n, max_items) + self.data += new_data[0: items_to_append * self._dtype.bitlength] + if n is not None and items_to_append < n: + raise EOFError(f"Only {items_to_append} were appended, not the {n} items requested.") + + def reverse(self) -> None: + trailing_bit_length = len(self.data) % self._dtype.length + if trailing_bit_length != 0: + raise ValueError(f"Cannot reverse the items in the Array as its data length ({len(self.data)} bits) is not a multiple of the format length ({self._dtype.length} bits).") + for start_bit in range(0, len(self.data) // 2, self._dtype.length): + start_swap_bit = len(self.data) - start_bit - self._dtype.length + temp = self.data[start_bit: start_bit + self._dtype.length] + self.data[start_bit: start_bit + self._dtype.length] = self.data[ + start_swap_bit: start_swap_bit + self._dtype.length] + self.data[start_swap_bit: start_swap_bit + self._dtype.length] = temp + + def pp(self, fmt: Optional[str] = None, width: int = 120, + show_offset: bool = True, stream: TextIO = sys.stdout) -> None: + """Pretty-print the Array contents. + + fmt -- Data format string. Defaults to current Array dtype. + width -- Max width of printed lines in characters. Defaults to 120. A single group will always + be printed per line even if it exceeds the max width. + show_offset -- If True shows the element offset in the first column of each line. + stream -- A TextIO object with a write() method. Defaults to sys.stdout. + + """ + colour = Colour(not options.no_color) + sep = ' ' + dtype2 = None + tidy_fmt = None + if fmt is None: + fmt = self.dtype + dtype1 = self.dtype + tidy_fmt = "dtype='" + colour.purple + str(self.dtype) + "'" + colour.off + else: + token_list = utils.preprocess_tokens(fmt) + if len(token_list) not in [1, 2]: + raise ValueError(f"Only one or two tokens can be used in an Array.pp() format - '{fmt}' has {len(token_list)} tokens.") + name1, length1 = utils.parse_name_length_token(token_list[0]) + dtype1 = Dtype(name1, length1) + if len(token_list) == 2: + name2, length2 = utils.parse_name_length_token(token_list[1]) + dtype2 = Dtype(name2, length2) + + token_length = dtype1.bitlength + if dtype2 is not None: + # For two types we're OK as long as they don't have different lengths given. + if dtype1.bitlength is not None and dtype2.bitlength is not None and dtype1.bitlength != dtype2.bitlength: + raise ValueError(f"Two different format lengths specified ('{fmt}'). Either specify just one, or two the same length.") + if token_length is None: + token_length = dtype2.bitlength + if token_length is None: + token_length = self.itemsize + + trailing_bit_length = len(self.data) % token_length + format_sep = " : " # String to insert on each line between multiple formats + if tidy_fmt is None: + tidy_fmt = colour.purple + str(dtype1) + colour.off + if dtype2 is not None: + tidy_fmt += ', ' + colour.blue + str(dtype2) + colour.off + tidy_fmt = "fmt='" + tidy_fmt + "'" + data = self.data if trailing_bit_length == 0 else self.data[0: -trailing_bit_length] + length = len(self.data) // token_length + len_str = colour.green + str(length) + colour.off + stream.write(f"<{self.__class__.__name__} {tidy_fmt}, length={len_str}, itemsize={token_length} bits, total data size={(len(self.data) + 7) // 8} bytes> [\n") + data._pp(dtype1, dtype2, token_length, width, sep, format_sep, show_offset, stream, False, token_length) + stream.write("]") + if trailing_bit_length != 0: + stream.write(" + trailing_bits = " + str(self.data[-trailing_bit_length:])) + stream.write("\n") + + def equals(self, other: Any) -> bool: + """Return True if format and all Array items are equal.""" + if isinstance(other, Array): + if self._dtype.length != other._dtype.length: + return False + if self._dtype.name != other._dtype.name: + return False + if self.data != other.data: + return False + return True + elif isinstance(other, array.array): + # Assume we are comparing with an array type + if self.trailing_bits: + return False + # array's itemsize is in bytes, not bits. + if self.itemsize != other.itemsize * 8: + return False + if len(self) != len(other): + return False + if self.tolist() != other.tolist(): + return False + return True + return False + + def __iter__(self) -> Iterable[ElementType]: + start = 0 + for _ in range(len(self)): + yield self._dtype.read_fn(self.data, start=start) + start += self._dtype.length + + def __copy__(self) -> Array: + a_copy = self.__class__(self._dtype) + a_copy.data = copy.copy(self.data) + return a_copy + + def _apply_op_to_all_elements(self, op, value: Union[int, float, None], is_comparison: bool = False) -> Array: + """Apply op with value to each element of the Array and return a new Array""" + new_array = self.__class__('bool' if is_comparison else self._dtype) + new_data = BitArray() + failures = index = 0 + msg = '' + if value is not None: + def partial_op(a): + return op(a, value) + else: + def partial_op(a): + return op(a) + for i in range(len(self)): + v = self._dtype.read_fn(self.data, start=self._dtype.length * i) + try: + new_data.append(new_array._create_element(partial_op(v))) + except (CreationError, ZeroDivisionError, ValueError) as e: + if failures == 0: + msg = str(e) + index = i + failures += 1 + if failures != 0: + raise ValueError(f"Applying operator '{op.__name__}' to Array caused {failures} errors. " + f'First error at index {index} was: "{msg}"') + new_array.data = new_data + return new_array + + def _apply_op_to_all_elements_inplace(self, op, value: Union[int, float]) -> Array: + """Apply op with value to each element of the Array in place.""" + # This isn't really being done in-place, but it's simpler and faster for now? + new_data = BitArray() + failures = index = 0 + msg = '' + for i in range(len(self)): + v = self._dtype.read_fn(self.data, start=self._dtype.length * i) + try: + new_data.append(self._create_element(op(v, value))) + except (CreationError, ZeroDivisionError, ValueError) as e: + if failures == 0: + msg = str(e) + index = i + failures += 1 + if failures != 0: + raise ValueError(f"Applying operator '{op.__name__}' to Array caused {failures} errors. " + f'First error at index {index} was: "{msg}"') + self.data = new_data + return self + + def _apply_bitwise_op_to_all_elements(self, op, value: BitsType) -> Array: + """Apply op with value to each element of the Array as an unsigned integer and return a new Array""" + a_copy = self[:] + a_copy._apply_bitwise_op_to_all_elements_inplace(op, value) + return a_copy + + def _apply_bitwise_op_to_all_elements_inplace(self, op, value: BitsType) -> Array: + """Apply op with value to each element of the Array as an unsigned integer in place.""" + value = BitArray._create_from_bitstype(value) + if len(value) != self._dtype.length: + raise ValueError(f"Bitwise op needs a bitstring of length {self._dtype.length} to match format {self._dtype}.") + for start in range(0, len(self) * self._dtype.length, self._dtype.length): + self.data[start: start + self._dtype.length] = op(self.data[start: start + self._dtype.length], value) + return self + + def _apply_op_between_arrays(self, op, other: Array, is_comparison: bool = False) -> Array: + if len(self) != len(other): + msg = f"Cannot operate element-wise on Arrays with different lengths ({len(self)} and {len(other)})." + if op in [operator.add, operator.iadd]: + msg += " Use extend() method to concatenate Arrays." + if op in [operator.eq, operator.ne]: + msg += " Use equals() method to compare Arrays for a single boolean result." + raise ValueError(msg) + if is_comparison: + new_type = dtype_register.get_dtype('bool', 1) + else: + new_type = self._promotetype(self._dtype, other._dtype) + new_array = self.__class__(new_type) + new_data = BitArray() + failures = index = 0 + msg = '' + for i in range(len(self)): + a = self._dtype.read_fn(self.data, start=self._dtype.length * i) + b = other._dtype.read_fn(other.data, start=other._dtype.length * i) + try: + new_data.append(new_array._create_element(op(a, b))) + except (CreationError, ValueError, ZeroDivisionError) as e: + if failures == 0: + msg = str(e) + index = i + failures += 1 + if failures != 0: + raise ValueError(f"Applying operator '{op.__name__}' between Arrays caused {failures} errors. " + f'First error at index {index} was: "{msg}"') + new_array.data = new_data + return new_array + + @classmethod + def _promotetype(cls, type1: Dtype, type2: Dtype) -> Dtype: + """When combining types which one wins? + + 1. We only deal with types representing floats or integers. + 2. One of the two types gets returned. We never create a new one. + 3. Floating point types always win against integer types. + 4. Signed integer types always win against unsigned integer types. + 5. Longer types win against shorter types. + 6. In a tie the first type wins against the second type. + + """ + def is_float(x): return x.return_type is float + def is_int(x): return x.return_type is int or x.return_type is bool + if is_float(type1) + is_int(type1) + is_float(type2) + is_int(type2) != 2: + raise ValueError(f"Only integer and floating point types can be combined - not '{type1}' and '{type2}'.") + # If same type choose the widest + if type1.name == type2.name: + return type1 if type1.length > type2.length else type2 + # We choose floats above integers, irrespective of the widths + if is_float(type1) and is_int(type2): + return type1 + if is_int(type1) and is_float(type2): + return type2 + if is_float(type1) and is_float(type2): + return type2 if type2.length > type1.length else type1 + assert is_int(type1) and is_int(type2) + if type1.is_signed and not type2.is_signed: + return type1 + if type2.is_signed and not type1.is_signed: + return type2 + return type2 if type2.length > type1.length else type1 + + # Operators between Arrays or an Array and scalar value + + def __add__(self, other: Union[int, float, Array]) -> Array: + """Add int or float to all elements.""" + if isinstance(other, Array): + return self._apply_op_between_arrays(operator.add, other) + return self._apply_op_to_all_elements(operator.add, other) + + def __iadd__(self, other: Union[int, float, Array]) -> Array: + if isinstance(other, Array): + return self._apply_op_between_arrays(operator.add, other) + return self._apply_op_to_all_elements_inplace(operator.add, other) + + def __isub__(self, other: Union[int, float, Array]) -> Array: + if isinstance(other, Array): + return self._apply_op_between_arrays(operator.sub, other) + return self._apply_op_to_all_elements_inplace(operator.sub, other) + + def __sub__(self, other: Union[int, float, Array]) -> Array: + if isinstance(other, Array): + return self._apply_op_between_arrays(operator.sub, other) + return self._apply_op_to_all_elements(operator.sub, other) + + def __mul__(self, other: Union[int, float, Array]) -> Array: + if isinstance(other, Array): + return self._apply_op_between_arrays(operator.mul, other) + return self._apply_op_to_all_elements(operator.mul, other) + + def __imul__(self, other: Union[int, float, Array]) -> Array: + if isinstance(other, Array): + return self._apply_op_between_arrays(operator.mul, other) + return self._apply_op_to_all_elements_inplace(operator.mul, other) + + def __floordiv__(self, other: Union[int, float, Array]) -> Array: + if isinstance(other, Array): + return self._apply_op_between_arrays(operator.floordiv, other) + return self._apply_op_to_all_elements(operator.floordiv, other) + + def __ifloordiv__(self, other: Union[int, float, Array]) -> Array: + if isinstance(other, Array): + return self._apply_op_between_arrays(operator.floordiv, other) + return self._apply_op_to_all_elements_inplace(operator.floordiv, other) + + def __truediv__(self, other: Union[int, float, Array]) -> Array: + if isinstance(other, Array): + return self._apply_op_between_arrays(operator.truediv, other) + return self._apply_op_to_all_elements(operator.truediv, other) + + def __itruediv__(self, other: Union[int, float, Array]) -> Array: + if isinstance(other, Array): + return self._apply_op_between_arrays(operator.truediv, other) + return self._apply_op_to_all_elements_inplace(operator.truediv, other) + + def __rshift__(self, other: Union[int, Array]) -> Array: + if isinstance(other, Array): + return self._apply_op_between_arrays(operator.rshift, other) + return self._apply_op_to_all_elements(operator.rshift, other) + + def __lshift__(self, other: Union[int, Array]) -> Array: + if isinstance(other, Array): + return self._apply_op_between_arrays(operator.lshift, other) + return self._apply_op_to_all_elements(operator.lshift, other) + + def __irshift__(self, other: Union[int, Array]) -> Array: + if isinstance(other, Array): + return self._apply_op_between_arrays(operator.rshift, other) + return self._apply_op_to_all_elements_inplace(operator.rshift, other) + + def __ilshift__(self, other: Union[int, Array]) -> Array: + if isinstance(other, Array): + return self._apply_op_between_arrays(operator.lshift, other) + return self._apply_op_to_all_elements_inplace(operator.lshift, other) + + def __mod__(self, other: Union[int, Array]) -> Array: + if isinstance(other, Array): + return self._apply_op_between_arrays(operator.mod, other) + return self._apply_op_to_all_elements(operator.mod, other) + + def __imod__(self, other: Union[int, Array]) -> Array: + if isinstance(other, Array): + return self._apply_op_between_arrays(operator.mod, other) + return self._apply_op_to_all_elements_inplace(operator.mod, other) + + # Bitwise operators + + def __and__(self, other: BitsType) -> Array: + return self._apply_bitwise_op_to_all_elements(operator.iand, other) + + def __iand__(self, other: BitsType) -> Array: + return self._apply_bitwise_op_to_all_elements_inplace(operator.iand, other) + + def __or__(self, other: BitsType) -> Array: + return self._apply_bitwise_op_to_all_elements(operator.ior, other) + + def __ior__(self, other: BitsType) -> Array: + return self._apply_bitwise_op_to_all_elements_inplace(operator.ior, other) + + def __xor__(self, other: BitsType) -> Array: + return self._apply_bitwise_op_to_all_elements(operator.ixor, other) + + def __ixor__(self, other: BitsType) -> Array: + return self._apply_bitwise_op_to_all_elements_inplace(operator.ixor, other) + + # Reverse operators between a scalar value and an Array + + def __rmul__(self, other: Union[int, float]) -> Array: + return self._apply_op_to_all_elements(operator.mul, other) + + def __radd__(self, other: Union[int, float]) -> Array: + return self._apply_op_to_all_elements(operator.add, other) + + def __rsub__(self, other: Union[int, float]) -> Array: + # i - A == (-A) + i + neg = self._apply_op_to_all_elements(operator.neg, None) + return neg._apply_op_to_all_elements(operator.add, other) + + # Reverse operators between a scalar and something that can be a BitArray. + + def __rand__(self, other: BitsType) -> Array: + return self._apply_bitwise_op_to_all_elements(operator.iand, other) + + def __ror__(self, other: BitsType) -> Array: + return self._apply_bitwise_op_to_all_elements(operator.ior, other) + + def __rxor__(self, other: BitsType) -> Array: + return self._apply_bitwise_op_to_all_elements(operator.ixor, other) + + # Comparison operators + + def __lt__(self, other: Union[int, float, Array]) -> Array: + if isinstance(other, Array): + return self._apply_op_between_arrays(operator.lt, other, is_comparison=True) + return self._apply_op_to_all_elements(operator.lt, other, is_comparison=True) + + def __gt__(self, other: Union[int, float, Array]) -> Array: + if isinstance(other, Array): + return self._apply_op_between_arrays(operator.gt, other, is_comparison=True) + return self._apply_op_to_all_elements(operator.gt, other, is_comparison=True) + + def __ge__(self, other: Union[int, float, Array]) -> Array: + if isinstance(other, Array): + return self._apply_op_between_arrays(operator.ge, other, is_comparison=True) + return self._apply_op_to_all_elements(operator.ge, other, is_comparison=True) + + def __le__(self, other: Union[int, float, Array]) -> Array: + if isinstance(other, Array): + return self._apply_op_between_arrays(operator.le, other, is_comparison=True) + return self._apply_op_to_all_elements(operator.le, other, is_comparison=True) + + def _eq_ne(self, op, other: Any) -> Array: + if isinstance(other, (int, float, str, Bits)): + return self._apply_op_to_all_elements(op, other, is_comparison=True) + try: + other = self.__class__(self.dtype, other) + except: + return NotImplemented + finally: + return self._apply_op_between_arrays(op, other, is_comparison=True) + + def __eq__(self, other: Any) -> Array: + return self._eq_ne(operator.eq, other) + + def __ne__(self, other: Any) -> Array: + return self._eq_ne(operator.ne, other) + + # Unary operators + + def __neg__(self): + return self._apply_op_to_all_elements(operator.neg, None) + + def __abs__(self): + return self._apply_op_to_all_elements(operator.abs, None) \ No newline at end of file diff --git a/code/.venv/lib/python3.12/site-packages/bitstring/bitarray_.py b/code/.venv/lib/python3.12/site-packages/bitstring/bitarray_.py new file mode 100644 index 0000000..4e68684 --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/bitstring/bitarray_.py @@ -0,0 +1,576 @@ +from __future__ import annotations + +import copy +import numbers +import re +from collections import abc +from typing import Union, List, Iterable, Any, Optional +from bitstring import utils +from bitstring.exceptions import CreationError, Error +from bitstring.bits import Bits, BitsType, TBits + +import bitstring.dtypes + + +class BitArray(Bits): + """A container holding a mutable sequence of bits. + + Subclass of the immutable Bits class. Inherits all of its + methods (except __hash__) and adds mutating methods. + + Mutating methods: + + append() -- Append a bitstring. + byteswap() -- Change byte endianness in-place. + clear() -- Remove all bits from the bitstring. + insert() -- Insert a bitstring. + invert() -- Flip bit(s) between one and zero. + overwrite() -- Overwrite a section with a new bitstring. + prepend() -- Prepend a bitstring. + replace() -- Replace occurrences of one bitstring with another. + reverse() -- Reverse bits in-place. + rol() -- Rotate bits to the left. + ror() -- Rotate bits to the right. + set() -- Set bit(s) to 1 or 0. + + Methods inherited from Bits: + + all() -- Check if all specified bits are set to 1 or 0. + any() -- Check if any of specified bits are set to 1 or 0. + copy() -- Return a copy of the bitstring. + count() -- Count the number of bits set to 1 or 0. + cut() -- Create generator of constant sized chunks. + endswith() -- Return whether the bitstring ends with a sub-string. + find() -- Find a sub-bitstring in the current bitstring. + findall() -- Find all occurrences of a sub-bitstring in the current bitstring. + fromstring() -- Create a bitstring from a formatted string. + join() -- Join bitstrings together using current bitstring. + pp() -- Pretty print the bitstring. + rfind() -- Seek backwards to find a sub-bitstring. + split() -- Create generator of chunks split by a delimiter. + startswith() -- Return whether the bitstring starts with a sub-bitstring. + tobitarray() -- Return bitstring as a bitarray from the bitarray package. + tobytes() -- Return bitstring as bytes, padding if needed. + tofile() -- Write bitstring to file, padding if needed. + unpack() -- Interpret bits using format string. + + Special methods: + + Mutating operators are available: [], <<=, >>=, +=, *=, &=, |= and ^= + in addition to the inherited [], ==, !=, +, *, ~, <<, >>, &, | and ^. + + Properties: + + [GENERATED_PROPERTY_DESCRIPTIONS] + + len -- Length of the bitstring in bits. + + """ + + __slots__ = () + + # As BitArray objects are mutable, we shouldn't allow them to be hashed. + __hash__: None = None + + def __init__(self, auto: Optional[Union[BitsType, int]] = None, /, length: Optional[int] = None, + offset: Optional[int] = None, **kwargs) -> None: + """Either specify an 'auto' initialiser: + A string of comma separated tokens, an integer, a file object, + a bytearray, a boolean iterable or another bitstring. + + Or initialise via **kwargs with one (and only one) of: + bin -- binary string representation, e.g. '0b001010'. + hex -- hexadecimal string representation, e.g. '0x2ef' + oct -- octal string representation, e.g. '0o777'. + bytes -- raw data as a bytes object, for example read from a binary file. + int -- a signed integer. + uint -- an unsigned integer. + float / floatbe -- a big-endian floating point number. + bool -- a boolean (True or False). + se -- a signed exponential-Golomb code. + ue -- an unsigned exponential-Golomb code. + sie -- a signed interleaved exponential-Golomb code. + uie -- an unsigned interleaved exponential-Golomb code. + floatle -- a little-endian floating point number. + floatne -- a native-endian floating point number. + bfloat / bfloatbe - a big-endian bfloat format 16-bit floating point number. + bfloatle -- a little-endian bfloat format 16-bit floating point number. + bfloatne -- a native-endian bfloat format 16-bit floating point number. + intbe -- a signed big-endian whole byte integer. + intle -- a signed little-endian whole byte integer. + intne -- a signed native-endian whole byte integer. + uintbe -- an unsigned big-endian whole byte integer. + uintle -- an unsigned little-endian whole byte integer. + uintne -- an unsigned native-endian whole byte integer. + filename -- the path of a file which will be opened in binary read-only mode. + + Other keyword arguments: + length -- length of the bitstring in bits, if needed and appropriate. + It must be supplied for all integer and float initialisers. + offset -- bit offset to the data. These offset bits are + ignored and this is intended for use when + initialising using 'bytes' or 'filename'. + + """ + if self._bitstore.immutable: + self._bitstore = self._bitstore._copy() + self._bitstore.immutable = False + + def copy(self: TBits) -> TBits: + """Return a copy of the bitstring.""" + return self.__copy__() + + def __setattr__(self, attribute, value) -> None: + try: + # First try the ordinary attribute setter + super().__setattr__(attribute, value) + except AttributeError: + dtype = bitstring.dtypes.Dtype(attribute) + x = object.__new__(Bits) + if (set_fn := dtype.set_fn) is None: + raise AttributeError(f"Cannot set attribute '{attribute}' as it does not have a set_fn.") + set_fn(x, value) + if len(x) != dtype.bitlength: + raise CreationError(f"Can't initialise with value of length {len(x)} bits, " + f"as attribute has length of {dtype.bitlength} bits.") + self._bitstore = x._bitstore + return + + def __iadd__(self, bs: BitsType) -> BitArray: + """Append bs to current bitstring. Return self. + + bs -- the bitstring to append. + + """ + self._append(bs) + return self + + def __copy__(self) -> BitArray: + """Return a new copy of the BitArray.""" + s_copy = BitArray() + s_copy._bitstore = self._bitstore._copy() + assert s_copy._bitstore.immutable is False + return s_copy + + def _setitem_int(self, key: int, value: Union[BitsType, int]) -> None: + if isinstance(value, numbers.Integral): + if value == 0: + self._bitstore[key] = 0 + return + if value in (1, -1): + self._bitstore[key] = 1 + return + raise ValueError(f"Cannot set a single bit with integer {value}.") + try: + value = self._create_from_bitstype(value) + except TypeError: + raise TypeError(f"Bitstring, integer or string expected. Got {type(value)}.") + positive_key = key + len(self) if key < 0 else key + if positive_key < 0 or positive_key >= len(self._bitstore): + raise IndexError(f"Bit position {key} out of range.") + self._bitstore[positive_key: positive_key + 1] = value._bitstore + + def _setitem_slice(self, key: slice, value: BitsType) -> None: + if isinstance(value, numbers.Integral): + value = int(value) + if key.step not in [None, -1, 1]: + if value in [0, 1]: + self.set(value, range(*key.indices(len(self)))) + return + else: + raise ValueError("Can't assign an integer except 0 or 1 to a slice with a step value.") + # To find the length we first get the slice + s = self._bitstore.getslice(key.start, key.stop) + length = len(s) + # Now create an int of the correct length + if value >= 0: + value = self.__class__(uint=value, length=length) + else: + value = self.__class__(int=value, length=length) + else: + try: + value = self._create_from_bitstype(value) + except TypeError: + raise TypeError(f"Bitstring, integer or string expected. Got {type(value)}.") + self._bitstore.__setitem__(key, value._bitstore) + + def __setitem__(self, key: Union[slice, int], value: BitsType) -> None: + if isinstance(key, numbers.Integral): + self._setitem_int(int(key), value) + else: + self._setitem_slice(key, value) + + def __delitem__(self, key: Union[slice, int]) -> None: + """Delete item or range. + + >>> a = BitArray('0x001122') + >>> del a[8:16] + >>> print a + 0x0022 + + """ + self._bitstore.__delitem__(key) + return + + def __ilshift__(self: TBits, n: int) -> TBits: + """Shift bits by n to the left in place. Return self. + + n -- the number of bits to shift. Must be >= 0. + + """ + if n < 0: + raise ValueError("Cannot shift by a negative amount.") + if not len(self): + raise ValueError("Cannot shift an empty bitstring.") + if not n: + return self + n = min(n, len(self)) + return self._ilshift(n) + + def __irshift__(self: TBits, n: int) -> TBits: + """Shift bits by n to the right in place. Return self. + + n -- the number of bits to shift. Must be >= 0. + + """ + if n < 0: + raise ValueError("Cannot shift by a negative amount.") + if not len(self): + raise ValueError("Cannot shift an empty bitstring.") + if not n: + return self + n = min(n, len(self)) + return self._irshift(n) + + def __imul__(self: TBits, n: int) -> TBits: + """Concatenate n copies of self in place. Return self. + + Called for expressions of the form 'a *= 3'. + n -- The number of concatenations. Must be >= 0. + + """ + if n < 0: + raise ValueError("Cannot multiply by a negative integer.") + return self._imul(n) + + def __ior__(self: TBits, bs: BitsType) -> TBits: + bs = self._create_from_bitstype(bs) + self._bitstore |= bs._bitstore + return self + + def __iand__(self: TBits, bs: BitsType) -> TBits: + bs = self._create_from_bitstype(bs) + self._bitstore &= bs._bitstore + return self + + def __ixor__(self: TBits, bs: BitsType) -> TBits: + bs = self._create_from_bitstype(bs) + self._bitstore ^= bs._bitstore + return self + + def _replace(self, old: Bits, new: Bits, start: int, end: int, count: int, bytealigned: Optional[bool]) -> int: + if bytealigned is None: + bytealigned = bitstring.options.bytealigned + # First find all the places where we want to do the replacements + starting_points: List[int] = [] + for x in self.findall(old, start, end, bytealigned=bytealigned): + if not starting_points: + starting_points.append(x) + elif x >= starting_points[-1] + len(old): + # Can only replace here if it hasn't already been replaced! + starting_points.append(x) + if count != 0 and len(starting_points) == count: + break + if not starting_points: + return 0 + replacement_list = [self._bitstore.getslice(0, starting_points[0])] + for i in range(len(starting_points) - 1): + replacement_list.append(new._bitstore) + replacement_list.append( + self._bitstore.getslice(starting_points[i] + len(old), starting_points[i + 1])) + # Final replacement + replacement_list.append(new._bitstore) + replacement_list.append(self._bitstore.getslice(starting_points[-1] + len(old), None)) + if bitstring.options.lsb0: + # Addition of bitarray is always on the right, so assemble from other end + replacement_list.reverse() + self._bitstore.clear() + for r in replacement_list: + self._bitstore += r + return len(starting_points) + + def replace(self, old: BitsType, new: BitsType, start: Optional[int] = None, end: Optional[int] = None, + count: Optional[int] = None, bytealigned: Optional[bool] = None) -> int: + """Replace all occurrences of old with new in place. + + Returns number of replacements made. + + old -- The bitstring to replace. + new -- The replacement bitstring. + start -- Any occurrences that start before this will not be replaced. + Defaults to 0. + end -- Any occurrences that finish after this will not be replaced. + Defaults to len(self). + count -- The maximum number of replacements to make. Defaults to + replace all occurrences. + bytealigned -- If True replacements will only be made on byte + boundaries. + + Raises ValueError if old is empty or if start or end are + out of range. + + """ + if count == 0: + return 0 + old = self._create_from_bitstype(old) + new = self._create_from_bitstype(new) + if len(old) == 0: + raise ValueError("Empty bitstring cannot be replaced.") + start, end = self._validate_slice(start, end) + + if new is self: + # Prevent self assignment woes + new = copy.copy(self) + return self._replace(old, new, start, end, 0 if count is None else count, bytealigned) + + def insert(self, bs: BitsType, pos: int) -> None: + """Insert bs at bit position pos. + + bs -- The bitstring to insert. + pos -- The bit position to insert at. + + Raises ValueError if pos < 0 or pos > len(self). + + """ + bs = self._create_from_bitstype(bs) + if len(bs) == 0: + return + if bs is self: + bs = self._copy() + if pos < 0: + pos += len(self) + if not 0 <= pos <= len(self): + raise ValueError("Invalid insert position.") + self._insert(bs, pos) + + def overwrite(self, bs: BitsType, pos: int) -> None: + """Overwrite with bs at bit position pos. + + bs -- The bitstring to overwrite with. + pos -- The bit position to begin overwriting from. + + Raises ValueError if pos < 0 or pos > len(self). + + """ + bs = self._create_from_bitstype(bs) + if len(bs) == 0: + return + if pos < 0: + pos += len(self) + if pos < 0 or pos > len(self): + raise ValueError("Overwrite starts outside boundary of bitstring.") + self._overwrite(bs, pos) + + def append(self, bs: BitsType) -> None: + """Append a bitstring to the current bitstring. + + bs -- The bitstring to append. + + """ + self._append(bs) + + def prepend(self, bs: BitsType) -> None: + """Prepend a bitstring to the current bitstring. + + bs -- The bitstring to prepend. + + """ + self._prepend(bs) + + def _append_msb0(self, bs: BitsType) -> None: + self._addright(self._create_from_bitstype(bs)) + + def _append_lsb0(self, bs: BitsType) -> None: + bs = self._create_from_bitstype(bs) + self._addleft(bs) + + def reverse(self, start: Optional[int] = None, end: Optional[int] = None) -> None: + """Reverse bits in-place. + + start -- Position of first bit to reverse. Defaults to 0. + end -- One past the position of the last bit to reverse. + Defaults to len(self). + + Using on an empty bitstring will have no effect. + + Raises ValueError if start < 0, end > len(self) or end < start. + + """ + start, end = self._validate_slice(start, end) + if start == 0 and end == len(self): + self._bitstore.reverse() + return + s = self._slice(start, end) + s._bitstore.reverse() + self[start:end] = s + + def set(self, value: Any, pos: Optional[Union[int, Iterable[int]]] = None) -> None: + """Set one or many bits to 1 or 0. + + value -- If bool(value) is True bits are set to 1, otherwise they are set to 0. + pos -- Either a single bit position or an iterable of bit positions. + Negative numbers are treated in the same way as slice indices. + Defaults to the entire bitstring. + + Raises IndexError if pos < -len(self) or pos >= len(self). + + """ + if pos is None: + # Set all bits to either 1 or 0 + self._setint(-1 if value else 0) + return + if not isinstance(pos, abc.Iterable): + pos = (pos,) + v = 1 if value else 0 + if isinstance(pos, range): + self._bitstore.__setitem__(slice(pos.start, pos.stop, pos.step), v) + return + for p in pos: + self._bitstore[p] = v + + def invert(self, pos: Optional[Union[Iterable[int], int]] = None) -> None: + """Invert one or many bits from 0 to 1 or vice versa. + + pos -- Either a single bit position or an iterable of bit positions. + Negative numbers are treated in the same way as slice indices. + + Raises IndexError if pos < -len(self) or pos >= len(self). + + """ + if pos is None: + self._invert_all() + return + if not isinstance(pos, abc.Iterable): + pos = (pos,) + length = len(self) + + for p in pos: + if p < 0: + p += length + if not 0 <= p < length: + raise IndexError(f"Bit position {p} out of range.") + self._invert(p) + + def ror(self, bits: int, start: Optional[int] = None, end: Optional[int] = None) -> None: + """Rotate bits to the right in-place. + + bits -- The number of bits to rotate by. + start -- Start of slice to rotate. Defaults to 0. + end -- End of slice to rotate. Defaults to len(self). + + Raises ValueError if bits < 0. + + """ + if not len(self): + raise Error("Cannot rotate an empty bitstring.") + if bits < 0: + raise ValueError("Cannot rotate by negative amount.") + self._ror(bits, start, end) + + def _ror_msb0(self, bits: int, start: Optional[int] = None, end: Optional[int] = None) -> None: + start, end = self._validate_slice(start, end) # the _slice deals with msb0/lsb0 + bits %= (end - start) + if not bits: + return + rhs = self._slice(end - bits, end) + self._delete(bits, end - bits) + self._insert(rhs, start) + + def rol(self, bits: int, start: Optional[int] = None, end: Optional[int] = None) -> None: + """Rotate bits to the left in-place. + + bits -- The number of bits to rotate by. + start -- Start of slice to rotate. Defaults to 0. + end -- End of slice to rotate. Defaults to len(self). + + Raises ValueError if bits < 0. + + """ + if not len(self): + raise Error("Cannot rotate an empty bitstring.") + if bits < 0: + raise ValueError("Cannot rotate by negative amount.") + self._rol(bits, start, end) + + def _rol_msb0(self, bits: int, start: Optional[int] = None, end: Optional[int] = None): + start, end = self._validate_slice(start, end) + bits %= (end - start) + if bits == 0: + return + lhs = self._slice(start, start + bits) + self._delete(bits, start) + self._insert(lhs, end - bits) + + def byteswap(self, fmt: Optional[Union[int, Iterable[int], str]] = None, start: Optional[int] = None, + end: Optional[int] = None, repeat: bool = True) -> int: + """Change the endianness in-place. Return number of repeats of fmt done. + + fmt -- A compact structure string, an integer number of bytes or + an iterable of integers. Defaults to 0, which byte reverses the + whole bitstring. + start -- Start bit position, defaults to 0. + end -- End bit position, defaults to len(self). + repeat -- If True (the default) the byte swapping pattern is repeated + as much as possible. + + """ + start_v, end_v = self._validate_slice(start, end) + if fmt is None or fmt == 0: + # reverse all of the whole bytes. + bytesizes = [(end_v - start_v) // 8] + elif isinstance(fmt, numbers.Integral): + if fmt < 0: + raise ValueError(f"Improper byte length {fmt}.") + bytesizes = [fmt] + elif isinstance(fmt, str): + if not (m := utils.BYTESWAP_STRUCT_PACK_RE.match(fmt)): + raise ValueError(f"Cannot parse format string {fmt}.") + # Split the format string into a list of 'q', '4h' etc. + formatlist = re.findall(utils.STRUCT_SPLIT_RE, m.group('fmt')) + # Now deal with multiplicative factors, 4h -> hhhh etc. + bytesizes = [] + for f in formatlist: + if len(f) == 1: + bytesizes.append(utils.PACK_CODE_SIZE[f]) + else: + bytesizes.extend([utils.PACK_CODE_SIZE[f[-1]]] * int(f[:-1])) + elif isinstance(fmt, abc.Iterable): + bytesizes = fmt + for bytesize in bytesizes: + if not isinstance(bytesize, numbers.Integral) or bytesize < 0: + raise ValueError(f"Improper byte length {bytesize}.") + else: + raise TypeError("Format must be an integer, string or iterable.") + + repeats = 0 + totalbitsize: int = 8 * sum(bytesizes) + if not totalbitsize: + return 0 + if repeat: + # Try to repeat up to the end of the bitstring. + finalbit = end_v + else: + # Just try one (set of) byteswap(s). + finalbit = start_v + totalbitsize + for patternend in range(start_v + totalbitsize, finalbit + 1, totalbitsize): + bytestart = patternend - totalbitsize + for bytesize in bytesizes: + byteend = bytestart + bytesize * 8 + self._reversebytes(bytestart, byteend) + bytestart += bytesize * 8 + repeats += 1 + return repeats + + def clear(self) -> None: + """Remove all bits, reset to zero length.""" + self._clear() diff --git a/code/.venv/lib/python3.12/site-packages/bitstring/bits.py b/code/.venv/lib/python3.12/site-packages/bitstring/bits.py new file mode 100644 index 0000000..6893709 --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/bitstring/bits.py @@ -0,0 +1,1791 @@ +from __future__ import annotations + +import numbers +import pathlib +import sys +import mmap +import struct +import array +import io +from collections import abc +import functools +from typing import Tuple, Union, List, Iterable, Any, Optional, BinaryIO, TextIO, overload, Iterator, Type, TypeVar +import bitarray +import bitarray.util +import bitstring +from bitstring.bitstore import BitStore +from bitstring import bitstore_helpers, utils +from bitstring.dtypes import Dtype, dtype_register +from bitstring.fp8 import p4binary_fmt, p3binary_fmt +from bitstring.mxfp import e3m2mxfp_fmt, e2m3mxfp_fmt, e2m1mxfp_fmt, e4m3mxfp_saturate_fmt, e5m2mxfp_saturate_fmt +from bitstring.bitstring_options import Colour + +# Things that can be converted to Bits when a Bits type is needed +BitsType = Union['Bits', str, Iterable[Any], bool, BinaryIO, bytearray, bytes, memoryview, bitarray.bitarray] + +TBits = TypeVar("TBits", bound='Bits') + +# Maximum number of digits to use in __str__ and __repr__. +MAX_CHARS: int = 250 + + +class Bits: + """A container holding an immutable sequence of bits. + + For a mutable container use the BitArray class instead. + + Methods: + + all() -- Check if all specified bits are set to 1 or 0. + any() -- Check if any of specified bits are set to 1 or 0. + copy() - Return a copy of the bitstring. + count() -- Count the number of bits set to 1 or 0. + cut() -- Create generator of constant sized chunks. + endswith() -- Return whether the bitstring ends with a sub-string. + find() -- Find a sub-bitstring in the current bitstring. + findall() -- Find all occurrences of a sub-bitstring in the current bitstring. + fromstring() -- Create a bitstring from a formatted string. + join() -- Join bitstrings together using current bitstring. + pp() -- Pretty print the bitstring. + rfind() -- Seek backwards to find a sub-bitstring. + split() -- Create generator of chunks split by a delimiter. + startswith() -- Return whether the bitstring starts with a sub-bitstring. + tobitarray() -- Return bitstring as a bitarray from the bitarray package. + tobytes() -- Return bitstring as bytes, padding if needed. + tofile() -- Write bitstring to file, padding if needed. + unpack() -- Interpret bits using format string. + + Special methods: + + Also available are the operators [], ==, !=, +, *, ~, <<, >>, &, |, ^. + + Properties: + + [GENERATED_PROPERTY_DESCRIPTIONS] + + len -- Length of the bitstring in bits. + + """ + __slots__ = ('_bitstore', '_filename') + + def __init__(self, auto: Optional[Union[BitsType, int]] = None, /, length: Optional[int] = None, + offset: Optional[int] = None, **kwargs) -> None: + """Either specify an 'auto' initialiser: + A string of comma separated tokens, an integer, a file object, + a bytearray, a boolean iterable, an array or another bitstring. + + Or initialise via **kwargs with one (and only one) of: + bin -- binary string representation, e.g. '0b001010'. + hex -- hexadecimal string representation, e.g. '0x2ef' + oct -- octal string representation, e.g. '0o777'. + bytes -- raw data as a bytes object, for example read from a binary file. + int -- a signed integer. + uint -- an unsigned integer. + float / floatbe -- a big-endian floating point number. + bool -- a boolean (True or False). + se -- a signed exponential-Golomb code. + ue -- an unsigned exponential-Golomb code. + sie -- a signed interleaved exponential-Golomb code. + uie -- an unsigned interleaved exponential-Golomb code. + floatle -- a little-endian floating point number. + floatne -- a native-endian floating point number. + bfloat / bfloatbe - a big-endian bfloat format 16-bit floating point number. + bfloatle -- a little-endian bfloat format 16-bit floating point number. + bfloatne -- a native-endian bfloat format 16-bit floating point number. + intbe -- a signed big-endian whole byte integer. + intle -- a signed little-endian whole byte integer. + intne -- a signed native-endian whole byte integer. + uintbe -- an unsigned big-endian whole byte integer. + uintle -- an unsigned little-endian whole byte integer. + uintne -- an unsigned native-endian whole byte integer. + filename -- the path of a file which will be opened in binary read-only mode. + + Other keyword arguments: + length -- length of the bitstring in bits, if needed and appropriate. + It must be supplied for all integer and float initialisers. + offset -- bit offset to the data. These offset bits are + ignored and this is mainly intended for use when + initialising using 'bytes' or 'filename'. + + """ + self._bitstore.immutable = True + + def __new__(cls: Type[TBits], auto: Optional[Union[BitsType, int]] = None, /, length: Optional[int] = None, + offset: Optional[int] = None, pos: Optional[int] = None, **kwargs) -> TBits: + x = super().__new__(cls) + if auto is None and not kwargs: + # No initialiser so fill with zero bits up to length + if length is not None: + x._bitstore = BitStore(length) + x._bitstore.setall(0) + else: + x._bitstore = BitStore() + return x + x._initialise(auto, length, offset, **kwargs) + return x + + @classmethod + def _create_from_bitstype(cls: Type[TBits], auto: BitsType, /) -> TBits: + if isinstance(auto, cls): + return auto + b = super().__new__(cls) + b._setauto_no_length_or_offset(auto) + return b + + def _initialise(self, auto: Any, /, length: Optional[int], offset: Optional[int], **kwargs) -> None: + if auto is not None: + if isinstance(auto, numbers.Integral): + # Initialise with s zero bits. + if auto < 0: + raise bitstring.CreationError(f"Can't create bitstring of negative length {auto}.") + self._bitstore = BitStore(int(auto)) + self._bitstore.setall(0) + return + self._setauto(auto, length, offset) + return + k, v = kwargs.popitem() + if k == 'bytes': + # Special case for bytes as we want to allow offsets and lengths to work only on creation. + self._setbytes_with_truncation(v, length, offset) + return + if k == 'filename': + self._setfile(v, length, offset) + return + if k == 'bitarray': + self._setbitarray(v, length, offset) + return + if k == 'auto': + raise bitstring.CreationError( + f"The 'auto' parameter should not be given explicitly - just use the first positional argument. " + f"Instead of '{self.__class__.__name__}(auto=x)' use '{self.__class__.__name__}(x)'.") + if offset is not None: + raise bitstring.CreationError("offset cannot be used when initialising with '{k}'.") + try: + Dtype(k, length).set_fn(self, v) + except ValueError as e: + raise bitstring.CreationError(e) + + def __getattr__(self, attribute: str) -> Any: + # Support for arbitrary attributes like u16 or f64. + try: + d = Dtype(attribute) + except ValueError: + raise AttributeError(f"'{self.__class__.__name__}' object has no attribute '{attribute}'.") + if d.bitlength is not None and len(self) != d.bitlength: + raise ValueError(f"bitstring length {len(self)} doesn't match length {d.bitlength} of property '{attribute}'.") + return d.get_fn(self) + + def __iter__(self) -> Iterable[bool]: + return iter(self._bitstore) + + def __copy__(self: TBits) -> TBits: + """Return a new copy of the Bits for the copy module.""" + # Note that if you want a new copy (different ID), use _copy instead. + # The copy can return self as it's immutable. + return self + + def __lt__(self, other: Any) -> bool: + # bitstrings can't really be ordered. + return NotImplemented + + def __gt__(self, other: Any) -> bool: + return NotImplemented + + def __le__(self, other: Any) -> bool: + return NotImplemented + + def __ge__(self, other: Any) -> bool: + return NotImplemented + + def __add__(self: TBits, bs: BitsType) -> TBits: + """Concatenate bitstrings and return new bitstring. + + bs -- the bitstring to append. + + """ + bs = self.__class__._create_from_bitstype(bs) + s = self._copy() if len(bs) <= len(self) else bs._copy() + if len(bs) <= len(self): + s._addright(bs) + else: + s._addleft(self) + return s + + def __radd__(self: TBits, bs: BitsType) -> TBits: + """Append current bitstring to bs and return new bitstring. + + bs -- An object that can be 'auto' initialised as a bitstring that will be appended to. + + """ + bs = self.__class__._create_from_bitstype(bs) + return bs.__add__(self) + + @overload + def __getitem__(self: TBits, key: slice, /) -> TBits: + ... + + @overload + def __getitem__(self, key: int, /) -> bool: + ... + + def __getitem__(self: TBits, key: Union[slice, int], /) -> Union[TBits, bool]: + """Return a new bitstring representing a slice of the current bitstring. + + Indices are in units of the step parameter (default 1 bit). + Stepping is used to specify the number of bits in each item. + + >>> print(BitArray('0b00110')[1:4]) + '0b011' + >>> print(BitArray('0x00112233')[1:3:8]) + '0x1122' + + """ + if isinstance(key, numbers.Integral): + return bool(self._bitstore.getindex(key)) + bs = super().__new__(self.__class__) + bs._bitstore = self._bitstore.getslice_withstep(key) + return bs + + def __len__(self) -> int: + """Return the length of the bitstring in bits.""" + return self._getlength() + + def __bytes__(self) -> bytes: + return self.tobytes() + + def __str__(self) -> str: + """Return approximate string representation of bitstring for printing. + + Short strings will be given wholly in hexadecimal or binary. Longer + strings may be part hexadecimal and part binary. Very long strings will + be truncated with '...'. + + """ + length = len(self) + if not length: + return '' + if length > MAX_CHARS * 4: + # Too long for hex. Truncate... + return ''.join(('0x', self[0:MAX_CHARS*4]._gethex(), '...')) + # If it's quite short and we can't do hex then use bin + if length < 32 and length % 4 != 0: + return '0b' + self.bin + # If we can use hex then do so + if not length % 4: + return '0x' + self.hex + # Otherwise first we do as much as we can in hex + # then add on 1, 2 or 3 bits on at the end + bits_at_end = length % 4 + return ''.join(('0x', self[0:length - bits_at_end]._gethex(), + ', ', '0b', self[length - bits_at_end:]._getbin())) + + def _repr(self, classname: str, length: int, pos: int): + pos_string = f', pos={pos}' if pos else '' + if hasattr(self, '_filename') and self._filename: + return f"{classname}(filename={self._filename!r}, length={length}{pos_string})" + else: + s = self.__str__() + lengthstring = '' + if s.endswith('...'): + lengthstring = f' # length={length}' + return f"{classname}('{s}'{pos_string}){lengthstring}" + + def __repr__(self) -> str: + """Return representation that could be used to recreate the bitstring. + + If the returned string is too long it will be truncated. See __str__(). + + """ + return self._repr(self.__class__.__name__, len(self), 0) + + def __eq__(self, bs: Any, /) -> bool: + """Return True if two bitstrings have the same binary representation. + + >>> BitArray('0b1110') == '0xe' + True + + """ + try: + return self._bitstore == Bits._create_from_bitstype(bs)._bitstore + except TypeError: + return False + + def __ne__(self, bs: Any, /) -> bool: + """Return False if two bitstrings have the same binary representation. + + >>> BitArray('0b111') == '0x7' + False + + """ + return not self.__eq__(bs) + + def __invert__(self: TBits) -> TBits: + """Return bitstring with every bit inverted. + + Raises Error if the bitstring is empty. + + """ + if len(self) == 0: + raise bitstring.Error("Cannot invert empty bitstring.") + s = self._copy() + s._invert_all() + return s + + def __lshift__(self: TBits, n: int, /) -> TBits: + """Return bitstring with bits shifted by n to the left. + + n -- the number of bits to shift. Must be >= 0. + + """ + if n < 0: + raise ValueError("Cannot shift by a negative amount.") + if len(self) == 0: + raise ValueError("Cannot shift an empty bitstring.") + n = min(n, len(self)) + s = self._absolute_slice(n, len(self)) + s._addright(Bits(n)) + return s + + def __rshift__(self: TBits, n: int, /) -> TBits: + """Return bitstring with bits shifted by n to the right. + + n -- the number of bits to shift. Must be >= 0. + + """ + if n < 0: + raise ValueError("Cannot shift by a negative amount.") + if len(self) == 0: + raise ValueError("Cannot shift an empty bitstring.") + if not n: + return self._copy() + s = self.__class__(length=min(n, len(self))) + n = min(n, len(self)) + s._addright(self._absolute_slice(0, len(self) - n)) + return s + + def __mul__(self: TBits, n: int, /) -> TBits: + """Return bitstring consisting of n concatenations of self. + + Called for expression of the form 'a = b*3'. + n -- The number of concatenations. Must be >= 0. + + """ + if n < 0: + raise ValueError("Cannot multiply by a negative integer.") + if not n: + return self.__class__() + s = self._copy() + s._imul(n) + return s + + def __rmul__(self: TBits, n: int, /) -> TBits: + """Return bitstring consisting of n concatenations of self. + + Called for expressions of the form 'a = 3*b'. + n -- The number of concatenations. Must be >= 0. + + """ + return self.__mul__(n) + + def __and__(self: TBits, bs: BitsType, /) -> TBits: + """Bit-wise 'and' between two bitstrings. Returns new bitstring. + + bs -- The bitstring to '&' with. + + Raises ValueError if the two bitstrings have differing lengths. + + """ + if bs is self: + return self.copy() + bs = Bits._create_from_bitstype(bs) + s = object.__new__(self.__class__) + s._bitstore = self._bitstore & bs._bitstore + return s + + def __rand__(self: TBits, bs: BitsType, /) -> TBits: + """Bit-wise 'and' between two bitstrings. Returns new bitstring. + + bs -- the bitstring to '&' with. + + Raises ValueError if the two bitstrings have differing lengths. + + """ + return self.__and__(bs) + + def __or__(self: TBits, bs: BitsType, /) -> TBits: + """Bit-wise 'or' between two bitstrings. Returns new bitstring. + + bs -- The bitstring to '|' with. + + Raises ValueError if the two bitstrings have differing lengths. + + """ + if bs is self: + return self.copy() + bs = Bits._create_from_bitstype(bs) + s = object.__new__(self.__class__) + s._bitstore = self._bitstore | bs._bitstore + return s + + def __ror__(self: TBits, bs: BitsType, /) -> TBits: + """Bit-wise 'or' between two bitstrings. Returns new bitstring. + + bs -- The bitstring to '|' with. + + Raises ValueError if the two bitstrings have differing lengths. + + """ + return self.__or__(bs) + + def __xor__(self: TBits, bs: BitsType, /) -> TBits: + """Bit-wise 'xor' between two bitstrings. Returns new bitstring. + + bs -- The bitstring to '^' with. + + Raises ValueError if the two bitstrings have differing lengths. + + """ + bs = Bits._create_from_bitstype(bs) + s = object.__new__(self.__class__) + s._bitstore = self._bitstore ^ bs._bitstore + return s + + def __rxor__(self: TBits, bs: BitsType, /) -> TBits: + """Bit-wise 'xor' between two bitstrings. Returns new bitstring. + + bs -- The bitstring to '^' with. + + Raises ValueError if the two bitstrings have differing lengths. + + """ + return self.__xor__(bs) + + def __contains__(self, bs: BitsType, /) -> bool: + """Return whether bs is contained in the current bitstring. + + bs -- The bitstring to search for. + + """ + found = Bits.find(self, bs, bytealigned=False) + return bool(found) + + def __hash__(self) -> int: + """Return an integer hash of the object.""" + # Only requirement is that equal bitstring should return the same hash. + # For equal bitstrings the bytes at the start/end will be the same and they will have the same length + # (need to check the length as there could be zero padding when getting the bytes). We do not check any + # bit position inside the bitstring as that does not feature in the __eq__ operation. + if len(self) <= 2000: + # Use the whole bitstring. + return hash((self.tobytes(), len(self))) + else: + # We can't in general hash the whole bitstring (it could take hours!) + # So instead take some bits from the start and end. + return hash(((self[:800] + self[-800:]).tobytes(), len(self))) + + def __bool__(self) -> bool: + """Return False if bitstring is empty, otherwise return True.""" + return len(self) != 0 + + def _clear(self) -> None: + """Reset the bitstring to an empty state.""" + self._bitstore = BitStore() + + def _setauto_no_length_or_offset(self, s: BitsType, /) -> None: + """Set bitstring from a bitstring, file, bool, array, iterable or string.""" + if isinstance(s, str): + self._bitstore = bitstore_helpers.str_to_bitstore(s) + elif isinstance(s, Bits): + self._bitstore = s._bitstore.copy() + elif isinstance(s, (bytes, bytearray, memoryview)): + self._bitstore = BitStore.frombytes(bytearray(s)) + elif isinstance(s, io.BytesIO): + self._bitstore = BitStore.frombytes(s.getvalue()) + elif isinstance(s, io.BufferedReader): + self._setfile(s.name) + elif isinstance(s, bitarray.bitarray): + self._bitstore = BitStore(s) + elif isinstance(s, array.array): + self._bitstore = BitStore.frombytes(s.tobytes()) + elif isinstance(s, abc.Iterable): + # Evaluate each item as True or False and set bits to 1 or 0. + self._setbin_unsafe(''.join(str(int(bool(x))) for x in s)) + elif isinstance(s, numbers.Integral): + raise TypeError(f"It's no longer possible to auto initialise a bitstring from an integer." + f" Use '{self.__class__.__name__}({s})' instead of just '{s}' as this makes it " + f"clearer that a bitstring of {int(s)} zero bits will be created.") + else: + raise TypeError(f"Cannot initialise bitstring from type '{type(s)}'.") + + def _setauto(self, s: BitsType, length: Optional[int], offset: Optional[int], /) -> None: + """Set bitstring from a bitstring, file, bool, array, iterable or string.""" + # As s can be so many different things it's important to do the checks + # in the correct order, as some types are also other allowed types. + if offset is None and length is None: + return self._setauto_no_length_or_offset(s) + if offset is None: + offset = 0 + + if isinstance(s, io.BytesIO): + if length is None: + length = s.seek(0, 2) * 8 - offset + byteoffset, offset = divmod(offset, 8) + bytelength = (length + byteoffset * 8 + offset + 7) // 8 - byteoffset + if length + byteoffset * 8 + offset > s.seek(0, 2) * 8: + raise bitstring.CreationError("BytesIO object is not long enough for specified length and offset.") + self._bitstore = BitStore.frombytes(s.getvalue()[byteoffset: byteoffset + bytelength]).getslice( + offset, offset + length) + return + + if isinstance(s, io.BufferedReader): + self._setfile(s.name, length, offset) + return + + if isinstance(s, (str, Bits, bytes, bytearray, memoryview, io.BytesIO, io.BufferedReader, + bitarray.bitarray, array.array, abc.Iterable)): + raise bitstring.CreationError(f"Cannot initialise bitstring from type '{type(s)}' when using explicit lengths or offsets.") + raise TypeError(f"Cannot initialise bitstring from type '{type(s)}'.") + + def _setfile(self, filename: str, length: Optional[int] = None, offset: Optional[int] = None) -> None: + """Use file as source of bits.""" + with open(pathlib.Path(filename), 'rb') as source: + if offset is None: + offset = 0 + m = mmap.mmap(source.fileno(), 0, access=mmap.ACCESS_READ) + if offset == 0: + self._filename = source.name + self._bitstore = BitStore.frombuffer(m, length=length) + else: + # If offset is given then always read into memory. + temp = BitStore.frombuffer(m) + if length is None: + if offset > len(temp): + raise bitstring.CreationError(f"The offset of {offset} bits is greater than the file length ({len(temp)} bits).") + self._bitstore = temp.getslice(offset, None) + else: + self._bitstore = temp.getslice(offset, offset + length) + if len(self) != length: + raise bitstring.CreationError(f"Can't use a length of {length} bits and an offset of {offset} bits as file length is only {len(temp)} bits.") + + def _setbitarray(self, ba: bitarray.bitarray, length: Optional[int], offset: Optional[int]) -> None: + if offset is None: + offset = 0 + if offset > len(ba): + raise bitstring.CreationError(f"Offset of {offset} too large for bitarray of length {len(ba)}.") + if length is None: + self._bitstore = BitStore(ba[offset:]) + else: + if offset + length > len(ba): + raise bitstring.CreationError( + f"Offset of {offset} and length of {length} too large for bitarray of length {len(ba)}.") + self._bitstore = BitStore(ba[offset: offset + length]) + + def _setbits(self, bs: BitsType, length: None = None) -> None: + bs = Bits._create_from_bitstype(bs) + self._bitstore = bs._bitstore + + def _setp3binary(self, f: float) -> None: + self._bitstore = bitstore_helpers.p3binary2bitstore(f) + + def _setp4binary(self, f: float) -> None: + self._bitstore = bitstore_helpers.p4binary2bitstore(f) + + def _sete4m3mxfp(self, f: float) -> None: + self._bitstore = bitstore_helpers.e4m3mxfp2bitstore(f) + + def _sete5m2mxfp(self, f: float) -> None: + self._bitstore = bitstore_helpers.e5m2mxfp2bitstore(f) + + def _sete3m2mxfp(self, f: float) -> None: + self._bitstore = bitstore_helpers.e3m2mxfp2bitstore(f) + + def _sete2m3mxfp(self, f: float) -> None: + self._bitstore = bitstore_helpers.e2m3mxfp2bitstore(f) + + def _sete2m1mxfp(self, f: float) -> None: + self._bitstore = bitstore_helpers.e2m1mxfp2bitstore(f) + + def _sete8m0mxfp(self, f: float) -> None: + self._bitstore = bitstore_helpers.e8m0mxfp2bitstore(f) + + def _setmxint(self, f: float) -> None: + self._bitstore = bitstore_helpers.mxint2bitstore(f) + + def _setbytes(self, data: Union[bytearray, bytes, List], length:None = None) -> None: + """Set the data from a bytes or bytearray object.""" + self._bitstore = BitStore.frombytes(bytes(data)) + + def _setbytes_with_truncation(self, data: Union[bytearray, bytes], length: Optional[int] = None, offset: Optional[int] = None) -> None: + """Set the data from a bytes or bytearray object, with optional offset and length truncations.""" + if offset is None and length is None: + return self._setbytes(data) + data = bytearray(data) + if offset is None: + offset = 0 + if length is None: + # Use to the end of the data + length = len(data) * 8 - offset + else: + if length + offset > len(data) * 8: + raise bitstring.CreationError(f"Not enough data present. Need {length + offset} bits, have {len(data) * 8}.") + self._bitstore = BitStore.frombytes(data).getslice_msb0(offset, offset + length) + + def _getbytes(self) -> bytes: + """Return the data as an ordinary bytes object.""" + if len(self) % 8: + raise bitstring.InterpretError("Cannot interpret as bytes unambiguously - not multiple of 8 bits.") + return self._bitstore.tobytes() + + _unprintable = list(range(0x00, 0x20)) # ASCII control characters + _unprintable.extend(range(0x7f, 0xff)) # DEL char + non-ASCII + + def _getbytes_printable(self) -> str: + """Return an approximation of the data as a string of printable characters.""" + bytes_ = self._getbytes() + # For everything that isn't printable ASCII, use value from 'Latin Extended-A' unicode block. + string = ''.join(chr(0x100 + x) if x in Bits._unprintable else chr(x) for x in bytes_) + return string + + def _setuint(self, uint: int, length: Optional[int] = None) -> None: + """Reset the bitstring to have given unsigned int interpretation.""" + # If no length given, and we've previously been given a length, use it. + if length is None and hasattr(self, 'len') and len(self) != 0: + length = len(self) + if length is None or length == 0: + raise bitstring.CreationError("A non-zero length must be specified with a uint initialiser.") + self._bitstore = bitstore_helpers.int2bitstore(uint, length, False) + + def _getuint(self) -> int: + """Return data as an unsigned int.""" + if len(self) == 0: + raise bitstring.InterpretError("Cannot interpret a zero length bitstring as an integer.") + return self._bitstore.slice_to_uint() + + def _setint(self, int_: int, length: Optional[int] = None) -> None: + """Reset the bitstring to have given signed int interpretation.""" + # If no length given, and we've previously been given a length, use it. + if length is None and hasattr(self, 'len') and len(self) != 0: + length = len(self) + if length is None or length == 0: + raise bitstring.CreationError("A non-zero length must be specified with an int initialiser.") + self._bitstore = bitstore_helpers.int2bitstore(int_, length, True) + + def _getint(self) -> int: + """Return data as a two's complement signed int.""" + if len(self) == 0: + raise bitstring.InterpretError("Cannot interpret bitstring without a length as an integer.") + return self._bitstore.slice_to_int() + + def _setuintbe(self, uintbe: int, length: Optional[int] = None) -> None: + """Set the bitstring to a big-endian unsigned int interpretation.""" + if length is None and hasattr(self, 'len') and len(self) != 0: + length = len(self) + if length is None or length == 0: + raise bitstring.CreationError("A non-zero length must be specified with a uintbe initialiser.") + self._bitstore = bitstore_helpers.int2bitstore(uintbe, length, False) + + def _getuintbe(self) -> int: + """Return data as a big-endian two's complement unsigned int.""" + if len(self) % 8: + raise bitstring.InterpretError(f"Big-endian integers must be whole-byte. Length = {len(self)} bits.") + return self._getuint() + + def _setintbe(self, intbe: int, length: Optional[int] = None) -> None: + """Set bitstring to a big-endian signed int interpretation.""" + if length is None and hasattr(self, 'len') and len(self) != 0: + length = len(self) + if length is None or length == 0: + raise bitstring.CreationError("A non-zero length must be specified with a intbe initialiser.") + self._bitstore = bitstore_helpers.int2bitstore(intbe, length, True) + + def _getintbe(self) -> int: + """Return data as a big-endian two's complement signed int.""" + if len(self) % 8: + raise bitstring.InterpretError(f"Big-endian integers must be whole-byte. Length = {len(self)} bits.") + return self._getint() + + def _setuintle(self, uintle: int, length: Optional[int] = None) -> None: + if length is None and hasattr(self, 'len') and len(self) != 0: + length = len(self) + if length is None or length == 0: + raise bitstring.CreationError("A non-zero length must be specified with a uintle initialiser.") + self._bitstore = bitstore_helpers.intle2bitstore(uintle, length, False) + + def _getuintle(self) -> int: + """Interpret as a little-endian unsigned int.""" + if len(self) % 8: + raise bitstring.InterpretError(f"Little-endian integers must be whole-byte. Length = {len(self)} bits.") + bs = BitStore.frombytes(self._bitstore.tobytes()[::-1]) + return bs.slice_to_uint() + + def _setintle(self, intle: int, length: Optional[int] = None) -> None: + if length is None and hasattr(self, 'len') and len(self) != 0: + length = len(self) + if length is None or length == 0: + raise bitstring.CreationError("A non-zero length must be specified with an intle initialiser.") + self._bitstore = bitstore_helpers.intle2bitstore(intle, length, True) + + def _getintle(self) -> int: + """Interpret as a little-endian signed int.""" + if len(self) % 8: + raise bitstring.InterpretError(f"Little-endian integers must be whole-byte. Length = {len(self)} bits.") + bs = BitStore.frombytes(self._bitstore.tobytes()[::-1]) + return bs.slice_to_int() + + def _getp4binary(self) -> float: + u = self._getuint() + return p4binary_fmt.lut_binary8_to_float[u] + + def _getp3binary(self) -> float: + u = self._getuint() + return p3binary_fmt.lut_binary8_to_float[u] + + def _gete4m3mxfp(self) -> float: + u = self._getuint() + return e4m3mxfp_saturate_fmt.lut_int_to_float[u] + + def _gete5m2mxfp(self) -> float: + u = self._getuint() + return e5m2mxfp_saturate_fmt.lut_int_to_float[u] + + def _gete3m2mxfp(self) -> float: + u = self._getuint() + return e3m2mxfp_fmt.lut_int_to_float[u] + + def _gete2m3mxfp(self) -> float: + u = self._getuint() + return e2m3mxfp_fmt.lut_int_to_float[u] + + def _gete2m1mxfp(self) -> float: + u = self._getuint() + return e2m1mxfp_fmt.lut_int_to_float[u] + + def _gete8m0mxfp(self) -> float: + u = self._getuint() - 127 + if u == 128: + return float('nan') + return 2.0 ** u + + def _getmxint(self) -> float: + u = self._getint() + return float(u) * 2 ** -6 + + def _setfloat(self, f: float, length: Optional[int], big_endian: bool) -> None: + if length is None and hasattr(self, 'len') and len(self) != 0: + length = len(self) + if length is None or length not in [16, 32, 64]: + raise bitstring.CreationError("A length of 16, 32, or 64 must be specified with a float initialiser.") + self._bitstore = bitstore_helpers.float2bitstore(f, length, big_endian) + + def _setfloatbe(self, f: float, length: Optional[int] = None) -> None: + self._setfloat(f, length, True) + + def _getfloatbe(self) -> float: + """Interpret the whole bitstring as a big-endian float.""" + fmt = {16: '>e', 32: '>f', 64: '>d'}[len(self)] + return struct.unpack(fmt, self._bitstore.tobytes())[0] + + def _setfloatle(self, f: float, length: Optional[int] = None) -> None: + self._setfloat(f, length, False) + + def _getfloatle(self) -> float: + """Interpret the whole bitstring as a little-endian float.""" + fmt = {16: ' float: + zero_padded = self + Bits(16) + return zero_padded._getfloatbe() + + def _setbfloatbe(self, f: Union[float, str], length: Optional[int] = None) -> None: + if length is not None and length != 16: + raise bitstring.CreationError(f"bfloats must be length 16, received a length of {length} bits.") + self._bitstore = bitstore_helpers.bfloat2bitstore(f, True) + + def _getbfloatle(self) -> float: + zero_padded = Bits(16) + self + return zero_padded._getfloatle() + + def _setbfloatle(self, f: Union[float, str], length: Optional[int] = None) -> None: + if length is not None and length != 16: + raise bitstring.CreationError(f"bfloats must be length 16, received a length of {length} bits.") + self._bitstore = bitstore_helpers.bfloat2bitstore(f, False) + + def _setue(self, i: int) -> None: + """Initialise bitstring with unsigned exponential-Golomb code for integer i. + + Raises CreationError if i < 0. + + """ + if bitstring.options.lsb0: + raise bitstring.CreationError("Exp-Golomb codes cannot be used in lsb0 mode.") + self._bitstore = bitstore_helpers.ue2bitstore(i) + + def _readue(self, pos: int) -> Tuple[int, int]: + """Return interpretation of next bits as unsigned exponential-Golomb code. + + Raises ReadError if the end of the bitstring is encountered while + reading the code. + + """ + if bitstring.options.lsb0: + raise bitstring.ReadError("Exp-Golomb codes cannot be read in lsb0 mode.") + oldpos = pos + try: + while not self[pos]: + pos += 1 + except IndexError: + raise bitstring.ReadError("Read off end of bitstring trying to read code.") + leadingzeros = pos - oldpos + codenum = (1 << leadingzeros) - 1 + if leadingzeros > 0: + if pos + leadingzeros + 1 > len(self): + raise bitstring.ReadError("Read off end of bitstring trying to read code.") + codenum += self[pos + 1:pos + 1 + leadingzeros]._getuint() + pos += leadingzeros + 1 + else: + assert codenum == 0 + pos += 1 + return codenum, pos + + def _getue(self) -> Tuple[int, int]: + try: + return self._readue(0) + except bitstring.ReadError: + raise bitstring.InterpretError + + def _getse(self) -> Tuple[int, int]: + try: + return self._readse(0) + except bitstring.ReadError: + raise bitstring.InterpretError + + def _getuie(self) -> Tuple[int, int]: + try: + return self._readuie(0) + except bitstring.ReadError: + raise bitstring.InterpretError + + def _getsie(self) -> Tuple[int, int]: + try: + return self._readsie(0) + except bitstring.ReadError: + raise bitstring.InterpretError + + def _setse(self, i: int) -> None: + """Initialise bitstring with signed exponential-Golomb code for integer i.""" + if bitstring.options.lsb0: + raise bitstring.CreationError("Exp-Golomb codes cannot be used in lsb0 mode.") + self._bitstore = bitstore_helpers.se2bitstore(i) + + def _readse(self, pos: int) -> Tuple[int, int]: + """Return interpretation of next bits as a signed exponential-Golomb code. + + Advances position to after the read code. + + Raises ReadError if the end of the bitstring is encountered while + reading the code. + + """ + codenum, pos = self._readue(pos) + m = (codenum + 1) // 2 + return (m, pos) if codenum % 2 else (-m, pos) + + def _setuie(self, i: int) -> None: + """Initialise bitstring with unsigned interleaved exponential-Golomb code for integer i. + + Raises CreationError if i < 0. + + """ + if bitstring.options.lsb0: + raise bitstring.CreationError("Exp-Golomb codes cannot be used in lsb0 mode.") + self._bitstore = bitstore_helpers.uie2bitstore(i) + + def _readuie(self, pos: int) -> Tuple[int, int]: + """Return interpretation of next bits as unsigned interleaved exponential-Golomb code. + + Raises ReadError if the end of the bitstring is encountered while + reading the code. + + """ + if bitstring.options.lsb0: + raise bitstring.ReadError("Exp-Golomb codes cannot be read in lsb0 mode.") + try: + codenum: int = 1 + while not self[pos]: + pos += 1 + codenum <<= 1 + codenum += self[pos] + pos += 1 + pos += 1 + except IndexError: + raise bitstring.ReadError("Read off end of bitstring trying to read code.") + return codenum - 1, pos + + def _setsie(self, i: int, ) -> None: + """Initialise bitstring with signed interleaved exponential-Golomb code for integer i.""" + if bitstring.options.lsb0: + raise bitstring.CreationError("Exp-Golomb codes cannot be used in lsb0 mode.") + self._bitstore = bitstore_helpers.sie2bitstore(i) + + def _readsie(self, pos: int) -> Tuple[int, int]: + """Return interpretation of next bits as a signed interleaved exponential-Golomb code. + + Advances position to after the read code. + + Raises ReadError if the end of the bitstring is encountered while + reading the code. + + """ + codenum, pos = self._readuie(pos) + if not codenum: + return 0, pos + try: + return (-codenum, pos + 1) if self[pos] else (codenum, pos + 1) + except IndexError: + raise bitstring.ReadError("Read off end of bitstring trying to read code.") + + def _setbool(self, value: Union[bool, str]) -> None: + # We deliberately don't want to have implicit conversions to bool here. + # If we did then it would be difficult to deal with the 'False' string. + if value in (1, 'True', '1'): + self._bitstore = BitStore('1') + elif value in (0, 'False', '0'): + self._bitstore = BitStore('0') + else: + raise bitstring.CreationError(f"Cannot initialise boolean with {value}.") + + def _getbool(self) -> bool: + return self[0] + + def _getpad(self) -> None: + return None + + def _setpad(self, value: None, length: int) -> None: + self._bitstore = BitStore(length) + + def _setbin_safe(self, binstring: str, length: None = None) -> None: + """Reset the bitstring to the value given in binstring.""" + self._bitstore = bitstore_helpers.bin2bitstore(binstring) + + def _setbin_unsafe(self, binstring: str, length: None = None) -> None: + """Same as _setbin_safe, but input isn't sanity checked. binstring mustn't start with '0b'.""" + self._bitstore = bitstore_helpers.bin2bitstore_unsafe(binstring) + + def _getbin(self) -> str: + """Return interpretation as a binary string.""" + return self._bitstore.slice_to_bin() + + def _setoct(self, octstring: str, length: None = None) -> None: + """Reset the bitstring to have the value given in octstring.""" + self._bitstore = bitstore_helpers.oct2bitstore(octstring) + + def _getoct(self) -> str: + """Return interpretation as an octal string.""" + return self._bitstore.slice_to_oct() + + def _sethex(self, hexstring: str, length: None = None) -> None: + """Reset the bitstring to have the value given in hexstring.""" + self._bitstore = bitstore_helpers.hex2bitstore(hexstring) + + def _gethex(self) -> str: + """Return the hexadecimal representation as a string. + + Raises an InterpretError if the bitstring's length is not a multiple of 4. + + """ + return self._bitstore.slice_to_hex() + + def _getlength(self) -> int: + """Return the length of the bitstring in bits.""" + return len(self._bitstore) + + def _copy(self: TBits) -> TBits: + """Create and return a new copy of the Bits (always in memory).""" + # Note that __copy__ may choose to return self if it's immutable. This method always makes a copy. + s_copy = self.__class__() + s_copy._bitstore = self._bitstore._copy() + return s_copy + + def _slice(self: TBits, start: int, end: int) -> TBits: + """Used internally to get a slice, without error checking.""" + bs = self.__class__() + bs._bitstore = self._bitstore.getslice(start, end) + return bs + + def _absolute_slice(self: TBits, start: int, end: int) -> TBits: + """Used internally to get a slice, without error checking. + Uses MSB0 bit numbering even if LSB0 is set.""" + if end == start: + return self.__class__() + assert start < end, f"start={start}, end={end}" + bs = self.__class__() + bs._bitstore = self._bitstore.getslice_msb0(start, end) + return bs + + def _readtoken(self, name: str, pos: int, length: Optional[int]) -> Tuple[Union[float, int, str, None, Bits], int]: + """Reads a token from the bitstring and returns the result.""" + dtype = dtype_register.get_dtype(name, length) + if dtype.bitlength is not None and dtype.bitlength > len(self) - pos: + raise bitstring.ReadError("Reading off the end of the data. " + f"Tried to read {dtype.bitlength} bits when only {len(self) - pos} available.") + try: + val = dtype.read_fn(self, pos) + if isinstance(val, tuple): + return val + else: + assert length is not None + return val, pos + dtype.bitlength + except KeyError: + raise ValueError(f"Can't parse token {name}:{length}") + + def _addright(self, bs: Bits, /) -> None: + """Add a bitstring to the RHS of the current bitstring.""" + self._bitstore += bs._bitstore + + def _addleft(self, bs: Bits, /) -> None: + """Prepend a bitstring to the current bitstring.""" + if bs._bitstore.immutable: + self._bitstore = bs._bitstore._copy() + self._bitstore + else: + self._bitstore = bs._bitstore + self._bitstore + + def _truncateleft(self: TBits, bits: int, /) -> TBits: + """Truncate bits from the start of the bitstring. Return the truncated bits.""" + assert 0 <= bits <= len(self) + if bits == 0: + return self.__class__() + truncated_bits = self._absolute_slice(0, bits) + if bits == len(self): + self._clear() + return truncated_bits + self._bitstore = self._bitstore.getslice_msb0(bits, None) + return truncated_bits + + def _truncateright(self: TBits, bits: int, /) -> TBits: + """Truncate bits from the end of the bitstring. Return the truncated bits.""" + assert 0 <= bits <= len(self) + if bits == 0: + return self.__class__() + truncated_bits = self._absolute_slice(len(self) - bits, len(self)) + if bits == len(self): + self._clear() + return truncated_bits + self._bitstore = self._bitstore.getslice_msb0(None, -bits) + return truncated_bits + + def _insert(self, bs: Bits, pos: int, /) -> None: + """Insert bs at pos.""" + assert 0 <= pos <= len(self) + self._bitstore[pos: pos] = bs._bitstore + return + + def _overwrite(self, bs: Bits, pos: int, /) -> None: + """Overwrite with bs at pos.""" + assert 0 <= pos <= len(self) + if bs is self: + # Just overwriting with self, so do nothing. + assert pos == 0 + return + self._bitstore[pos: pos + len(bs)] = bs._bitstore + + def _delete(self, bits: int, pos: int, /) -> None: + """Delete bits at pos.""" + assert 0 <= pos <= len(self) + assert pos + bits <= len(self), f"pos={pos}, bits={bits}, len={len(self)}" + del self._bitstore[pos: pos + bits] + return + + def _reversebytes(self, start: int, end: int) -> None: + """Reverse bytes in-place.""" + assert (end - start) % 8 == 0 + self._bitstore[start:end] = BitStore.frombytes(self._bitstore.getslice(start, end).tobytes()[::-1]) + + def _invert(self, pos: int, /) -> None: + """Flip bit at pos 1<->0.""" + assert 0 <= pos < len(self) + self._bitstore.invert(pos) + + def _invert_all(self) -> None: + """Invert every bit.""" + self._bitstore.invert() + + def _ilshift(self: TBits, n: int, /) -> TBits: + """Shift bits by n to the left in place. Return self.""" + assert 0 < n <= len(self) + self._addright(Bits(n)) + self._truncateleft(n) + return self + + def _irshift(self: TBits, n: int, /) -> TBits: + """Shift bits by n to the right in place. Return self.""" + assert 0 < n <= len(self) + self._addleft(Bits(n)) + self._truncateright(n) + return self + + def _imul(self: TBits, n: int, /) -> TBits: + """Concatenate n copies of self in place. Return self.""" + assert n >= 0 + if n == 0: + self._clear() + else: + m = 1 + old_len = len(self) + while m * 2 < n: + self._addright(self) + m *= 2 + self._addright(self[0:(n - m) * old_len]) + return self + + def _getbits(self: TBits): + return self._copy() + + def _validate_slice(self, start: Optional[int], end: Optional[int]) -> Tuple[int, int]: + """Validate start and end and return them as positive bit positions.""" + start = 0 if start is None else (start + len(self) if start < 0 else start) + end = len(self) if end is None else (end + len(self) if end < 0 else end) + if not 0 <= start <= end <= len(self): + raise ValueError(f"Invalid slice positions for bitstring length {len(self)}: start={start}, end={end}.") + return start, end + + def unpack(self, fmt: Union[str, List[Union[str, int]]], **kwargs) -> List[Union[int, float, str, Bits, bool, bytes, None]]: + """Interpret the whole bitstring using fmt and return list. + + fmt -- A single string or a list of strings with comma separated tokens + describing how to interpret the bits in the bitstring. Items + can also be integers, for reading new bitstring of the given length. + kwargs -- A dictionary or keyword-value pairs - the keywords used in the + format string will be replaced with their given value. + + Raises ValueError if the format is not understood. If not enough bits + are available then all bits to the end of the bitstring will be used. + + See the docstring for 'read' for token examples. + + """ + return self._readlist(fmt, 0, **kwargs)[0] + + def _readlist(self, fmt: Union[str, List[Union[str, int, Dtype]]], pos: int, **kwargs) \ + -> Tuple[List[Union[int, float, str, Bits, bool, bytes, None]], int]: + if isinstance(fmt, str): + fmt = [fmt] + # Convert to a flat list of Dtypes + dtype_list = [] + for f_item in fmt: + if isinstance(f_item, numbers.Integral): + dtype_list.append(Dtype('bits', f_item)) + elif isinstance(f_item, Dtype): + dtype_list.append(f_item) + else: + token_list = utils.preprocess_tokens(f_item) + for t in token_list: + try: + name, length = utils.parse_name_length_token(t, **kwargs) + except ValueError: + dtype_list.append(Dtype('bits', int(t))) + else: + dtype_list.append(Dtype(name, length)) + return self._read_dtype_list(dtype_list, pos) + + def _read_dtype_list(self, dtypes: List[Dtype], pos: int) -> Tuple[List[Union[int, float, str, Bits, bool, bytes, None]], int]: + has_stretchy_token = False + bits_after_stretchy_token = 0 + for dtype in dtypes: + stretchy = dtype.bitlength is None and not dtype.variable_length + if stretchy: + if has_stretchy_token: + raise bitstring.Error("It's not possible to have more than one 'filler' token.") + has_stretchy_token = True + elif has_stretchy_token: + if dtype.variable_length: + raise bitstring.Error(f"It's not possible to parse a variable length token '{dtype}' after a 'filler' token.") + bits_after_stretchy_token += dtype.bitlength + + # We should have precisely zero or one stretchy token + vals = [] + for dtype in dtypes: + stretchy = dtype.bitlength is None and not dtype.variable_length + if stretchy: + bits_remaining = len(self) - pos + # Set length to the remaining bits + bitlength = max(bits_remaining - bits_after_stretchy_token, 0) + items, remainder = divmod(bitlength, dtype.bits_per_item) + if remainder != 0: + raise ValueError( + f"The '{dtype.name}' type must have a bit length that is a multiple of {dtype.bits_per_item}" + f" so cannot be created from the {bitlength} bits that are available for this stretchy token.") + dtype = Dtype(dtype.name, items) + if dtype.bitlength is not None: + val = dtype.read_fn(self, pos) + pos += dtype.bitlength + else: + val, pos = dtype.read_fn(self, pos) + if val is not None: # Don't append pad tokens + vals.append(val) + return vals, pos + + def find(self, bs: BitsType, /, start: Optional[int] = None, end: Optional[int] = None, + bytealigned: Optional[bool] = None) -> Union[Tuple[int], Tuple[()]]: + """Find first occurrence of substring bs. + + Returns a single item tuple with the bit position if found, or an + empty tuple if not found. The bit position (pos property) will + also be set to the start of the substring if it is found. + + bs -- The bitstring to find. + start -- The bit position to start the search. Defaults to 0. + end -- The bit position one past the last bit to search. + Defaults to len(self). + bytealigned -- If True the bitstring will only be + found on byte boundaries. + + Raises ValueError if bs is empty, if start < 0, if end > len(self) or + if end < start. + + >>> BitArray('0xc3e').find('0b1111') + (6,) + + """ + bs = Bits._create_from_bitstype(bs) + if len(bs) == 0: + raise ValueError("Cannot find an empty bitstring.") + start, end = self._validate_slice(start, end) + ba = bitstring.options.bytealigned if bytealigned is None else bytealigned + p = self._find(bs, start, end, ba) + return p + + def _find_lsb0(self, bs: Bits, start: int, end: int, bytealigned: bool) -> Union[Tuple[int], Tuple[()]]: + # A forward find in lsb0 is very like a reverse find in msb0. + assert start <= end + assert bitstring.options.lsb0 + + new_slice = bitstring.bitstore.offset_slice_indices_lsb0(slice(start, end, None), len(self)) + msb0_start, msb0_end = self._validate_slice(new_slice.start, new_slice.stop) + p = self._rfind_msb0(bs, msb0_start, msb0_end, bytealigned) + + if p: + return (len(self) - p[0] - len(bs),) + else: + return () + + def _find_msb0(self, bs: Bits, start: int, end: int, bytealigned: bool) -> Union[Tuple[int], Tuple[()]]: + """Find first occurrence of a binary string.""" + p = self._bitstore.find(bs._bitstore, start, end, bytealigned) + return () if p == -1 else (p,) + + def findall(self, bs: BitsType, start: Optional[int] = None, end: Optional[int] = None, count: Optional[int] = None, + bytealigned: Optional[bool] = None) -> Iterable[int]: + """Find all occurrences of bs. Return generator of bit positions. + + bs -- The bitstring to find. + start -- The bit position to start the search. Defaults to 0. + end -- The bit position one past the last bit to search. + Defaults to len(self). + count -- The maximum number of occurrences to find. + bytealigned -- If True the bitstring will only be found on + byte boundaries. + + Raises ValueError if bs is empty, if start < 0, if end > len(self) or + if end < start. + + Note that all occurrences of bs are found, even if they overlap. + + """ + if count is not None and count < 0: + raise ValueError("In findall, count must be >= 0.") + bs = Bits._create_from_bitstype(bs) + start, end = self._validate_slice(start, end) + ba = bitstring.options.bytealigned if bytealigned is None else bytealigned + return self._findall(bs, start, end, count, ba) + + def _findall_msb0(self, bs: Bits, start: int, end: int, count: Optional[int], + bytealigned: bool) -> Iterable[int]: + c = 0 + for i in self._bitstore.findall_msb0(bs._bitstore, start, end, bytealigned): + if count is not None and c >= count: + return + c += 1 + yield i + return + + def _findall_lsb0(self, bs: Bits, start: int, end: int, count: Optional[int], + bytealigned: bool) -> Iterable[int]: + assert start <= end + assert bitstring.options.lsb0 + + new_slice = bitstring.bitstore.offset_slice_indices_lsb0(slice(start, end, None), len(self)) + msb0_start, msb0_end = self._validate_slice(new_slice.start, new_slice.stop) + + # Search chunks starting near the end and then moving back. + c = 0 + increment = max(8192, len(bs) * 80) + buffersize = min(increment + len(bs), msb0_end - msb0_start) + pos = max(msb0_start, msb0_end - buffersize) + while True: + found = list(self._findall_msb0(bs, start=pos, end=pos + buffersize, count=None, bytealigned=False)) + if not found: + if pos == msb0_start: + return + pos = max(msb0_start, pos - increment) + continue + while found: + if count is not None and c >= count: + return + c += 1 + lsb0_pos = len(self) - found.pop() - len(bs) + if not bytealigned or lsb0_pos % 8 == 0: + yield lsb0_pos + + pos = max(msb0_start, pos - increment) + if pos == msb0_start: + return + + def rfind(self, bs: BitsType, /, start: Optional[int] = None, end: Optional[int] = None, + bytealigned: Optional[bool] = None) -> Union[Tuple[int], Tuple[()]]: + """Find final occurrence of substring bs. + + Returns a single item tuple with the bit position if found, or an + empty tuple if not found. The bit position (pos property) will + also be set to the start of the substring if it is found. + + bs -- The bitstring to find. + start -- The bit position to end the reverse search. Defaults to 0. + end -- The bit position one past the first bit to reverse search. + Defaults to len(self). + bytealigned -- If True the bitstring will only be found on byte + boundaries. + + Raises ValueError if bs is empty, if start < 0, if end > len(self) or + if end < start. + + """ + bs = Bits._create_from_bitstype(bs) + start, end = self._validate_slice(start, end) + ba = bitstring.options.bytealigned if bytealigned is None else bytealigned + if len(bs) == 0: + raise ValueError("Cannot find an empty bitstring.") + p = self._rfind(bs, start, end, ba) + return p + + def _rfind_msb0(self, bs: Bits, start: int, end: int, bytealigned: bool) -> Union[Tuple[int], Tuple[()]]: + """Find final occurrence of a binary string.""" + p = self._bitstore.rfind(bs._bitstore, start, end, bytealigned) + return () if p == -1 else (p,) + + def _rfind_lsb0(self, bs: Bits, start: int, end: int, bytealigned: bool) -> Union[Tuple[int], Tuple[()]]: + # A reverse find in lsb0 is very like a forward find in msb0. + assert start <= end + assert bitstring.options.lsb0 + new_slice = bitstring.bitstore.offset_slice_indices_lsb0(slice(start, end, None), len(self)) + msb0_start, msb0_end = self._validate_slice(new_slice.start, new_slice.stop) + + p = self._find_msb0(bs, msb0_start, msb0_end, bytealigned) + if p: + return (len(self) - p[0] - len(bs),) + else: + return () + + def cut(self, bits: int, start: Optional[int] = None, end: Optional[int] = None, + count: Optional[int] = None) -> Iterator[Bits]: + """Return bitstring generator by cutting into bits sized chunks. + + bits -- The size in bits of the bitstring chunks to generate. + start -- The bit position to start the first cut. Defaults to 0. + end -- The bit position one past the last bit to use in the cut. + Defaults to len(self). + count -- If specified then at most count items are generated. + Default is to cut as many times as possible. + + """ + start_, end_ = self._validate_slice(start, end) + if count is not None and count < 0: + raise ValueError("Cannot cut - count must be >= 0.") + if bits <= 0: + raise ValueError("Cannot cut - bits must be >= 0.") + c = 0 + while count is None or c < count: + c += 1 + nextchunk = self._slice(start_, min(start_ + bits, end_)) + if len(nextchunk) == 0: + return + yield nextchunk + if len(nextchunk) != bits: + return + start_ += bits + return + + def split(self, delimiter: BitsType, start: Optional[int] = None, end: Optional[int] = None, + count: Optional[int] = None, bytealigned: Optional[bool] = None) -> Iterable[Bits]: + """Return bitstring generator by splitting using a delimiter. + + The first item returned is the initial bitstring before the delimiter, + which may be an empty bitstring. + + delimiter -- The bitstring used as the divider. + start -- The bit position to start the split. Defaults to 0. + end -- The bit position one past the last bit to use in the split. + Defaults to len(self). + count -- If specified then at most count items are generated. + Default is to split as many times as possible. + bytealigned -- If True splits will only occur on byte boundaries. + + Raises ValueError if the delimiter is empty. + + """ + delimiter = Bits._create_from_bitstype(delimiter) + if len(delimiter) == 0: + raise ValueError("split delimiter cannot be empty.") + start, end = self._validate_slice(start, end) + bytealigned_: bool = bitstring.options.bytealigned if bytealigned is None else bytealigned + if count is not None and count < 0: + raise ValueError("Cannot split - count must be >= 0.") + if count == 0: + return + f = functools.partial(self._find_msb0, bs=delimiter, bytealigned=bytealigned_) + found = f(start=start, end=end) + if not found: + # Initial bits are the whole bitstring being searched + yield self._slice(start, end) + return + # yield the bytes before the first occurrence of the delimiter, even if empty + yield self._slice(start, found[0]) + startpos = pos = found[0] + c = 1 + while count is None or c < count: + pos += len(delimiter) + found = f(start=pos, end=end) + if not found: + # No more occurrences, so return the rest of the bitstring + yield self._slice(startpos, end) + return + c += 1 + yield self._slice(startpos, found[0]) + startpos = pos = found[0] + # Have generated count bitstrings, so time to quit. + return + + def join(self: TBits, sequence: Iterable[Any]) -> TBits: + """Return concatenation of bitstrings joined by self. + + sequence -- A sequence of bitstrings. + + """ + s = self.__class__() + if len(self) == 0: + # Optimised version that doesn't need to add self between every item + for item in sequence: + s._addright(Bits._create_from_bitstype(item)) + return s + else: + sequence_iter = iter(sequence) + try: + s._addright(Bits._create_from_bitstype(next(sequence_iter))) + except StopIteration: + return s + for item in sequence_iter: + s._addright(self) + s._addright(Bits._create_from_bitstype(item)) + return s + + def tobytes(self) -> bytes: + """Return the bitstring as bytes, padding with zero bits if needed. + + Up to seven zero bits will be added at the end to byte align. + + """ + return self._bitstore.tobytes() + + def tobitarray(self) -> bitarray.bitarray: + """Convert the bitstring to a bitarray object.""" + if self._bitstore.modified_length is not None: + # Removes the offset and truncates to length + return self._bitstore.getslice(0, len(self))._bitarray + else: + return self._bitstore._bitarray + + def tofile(self, f: BinaryIO) -> None: + """Write the bitstring to a file object, padding with zero bits if needed. + + Up to seven zero bits will be added at the end to byte align. + + """ + # If the bitstring is file based then we don't want to read it all in to memory first. + chunk_size = 8 * 100 * 1024 * 1024 # 100 MiB + for chunk in self.cut(chunk_size): + f.write(chunk.tobytes()) + + def startswith(self, prefix: BitsType, start: Optional[int] = None, end: Optional[int] = None) -> bool: + """Return whether the current bitstring starts with prefix. + + prefix -- The bitstring to search for. + start -- The bit position to start from. Defaults to 0. + end -- The bit position to end at. Defaults to len(self). + + """ + prefix = self._create_from_bitstype(prefix) + start, end = self._validate_slice(start, end) + return self._slice(start, start + len(prefix)) == prefix if end >= start + len(prefix) else False + + def endswith(self, suffix: BitsType, start: Optional[int] = None, end: Optional[int] = None) -> bool: + """Return whether the current bitstring ends with suffix. + + suffix -- The bitstring to search for. + start -- The bit position to start from. Defaults to 0. + end -- The bit position to end at. Defaults to len(self). + + """ + suffix = self._create_from_bitstype(suffix) + start, end = self._validate_slice(start, end) + return self._slice(end - len(suffix), end) == suffix if start + len(suffix) <= end else False + + def all(self, value: Any, pos: Optional[Iterable[int]] = None) -> bool: + """Return True if one or many bits are all set to bool(value). + + value -- If value is True then checks for bits set to 1, otherwise + checks for bits set to 0. + pos -- An iterable of bit positions. Negative numbers are treated in + the same way as slice indices. Defaults to the whole bitstring. + + """ + value = 1 if bool(value) else 0 + if pos is None: + return self._bitstore.all_set() if value else not self._bitstore.any_set() + for p in pos: + if self._bitstore.getindex(p) != value: + return False + return True + + def any(self, value: Any, pos: Optional[Iterable[int]] = None) -> bool: + """Return True if any of one or many bits are set to bool(value). + + value -- If value is True then checks for bits set to 1, otherwise + checks for bits set to 0. + pos -- An iterable of bit positions. Negative numbers are treated in + the same way as slice indices. Defaults to the whole bitstring. + + """ + value = 1 if bool(value) else 0 + if pos is None: + return self._bitstore.any_set() if value else not self._bitstore.all_set() + for p in pos: + if self._bitstore.getindex(p) == value: + return True + return False + + def count(self, value: Any) -> int: + """Return count of total number of either zero or one bits. + + value -- If bool(value) is True then bits set to 1 are counted, otherwise bits set + to 0 are counted. + + >>> Bits('0xef').count(1) + 7 + + """ + # count the number of 1s (from which it's easy to work out the 0s). + count = self._bitstore.count(1) + return count if value else len(self) - count + + @staticmethod + def _format_bits(bits: Bits, bits_per_group: int, sep: str, dtype: Dtype, + colour_start: str, colour_end: str, width: Optional[int]=None) -> Tuple[str, int]: + get_fn = dtype.get_fn + if dtype.name == 'bytes': # Special case for bytes to print one character each. + get_fn = Bits._getbytes_printable + if dtype.name == 'bool': # Special case for bool to print '1' or '0' instead of `True` or `False`. + get_fn = dtype_register.get_dtype('uint', bits_per_group).get_fn + if bits_per_group == 0: + x = str(get_fn(bits)) + else: + # Left-align for fixed width types when msb0, otherwise right-align. + align = '<' if dtype.name in ['bin', 'oct', 'hex', 'bits', 'bytes'] and not bitstring.options.lsb0 else '>' + chars_per_group = 0 + if dtype_register[dtype.name].bitlength2chars_fn is not None: + chars_per_group = dtype_register[dtype.name].bitlength2chars_fn(bits_per_group) + x = sep.join(f"{str(get_fn(b)): {align}{chars_per_group}}" for b in bits.cut(bits_per_group)) + + chars_used = len(x) + padding_spaces = 0 if width is None else max(width - len(x), 0) + x = colour_start + x + colour_end + # Pad final line with spaces to align it + if bitstring.options.lsb0: + x = ' ' * padding_spaces + x + else: + x += ' ' * padding_spaces + return x, chars_used + + @staticmethod + def _chars_per_group(bits_per_group: int, fmt: Optional[str]): + """How many characters are needed to represent a number of bits with a given format.""" + if fmt is None or dtype_register[fmt].bitlength2chars_fn is None: + return 0 + return dtype_register[fmt].bitlength2chars_fn(bits_per_group) + + @staticmethod + def _bits_per_char(fmt: str): + """How many bits are represented by each character of a given format.""" + if fmt not in ['bin', 'oct', 'hex', 'bytes']: + raise ValueError + return 24 // dtype_register[fmt].bitlength2chars_fn(24) + + def _pp(self, dtype1: Dtype, dtype2: Optional[Dtype], bits_per_group: int, width: int, sep: str, format_sep: str, + show_offset: bool, stream: TextIO, lsb0: bool, offset_factor: int) -> None: + """Internal pretty print method.""" + colour = Colour(not bitstring.options.no_color) + name1 = dtype1.name + name2 = dtype2.name if dtype2 is not None else None + if dtype1.variable_length: + raise ValueError(f"Can't use Dtype '{dtype1}' in pp() as it has a variable length.") + if dtype2 is not None and dtype2.variable_length: + raise ValueError(f"Can't use Dtype '{dtype2}' in pp() as it has a variable length.") + offset_width = 0 + offset_sep = ' :' if lsb0 else ': ' + if show_offset: + # This could be 1 too large in some circumstances. Slightly recurrent logic needed to fix it... + offset_width = len(str(len(self))) + len(offset_sep) + if bits_per_group > 0: + group_chars1 = Bits._chars_per_group(bits_per_group, name1) + group_chars2 = Bits._chars_per_group(bits_per_group, name2) + # The number of characters that get added when we add an extra group (after the first one) + total_group_chars = group_chars1 + group_chars2 + len(sep) + len(sep) * bool(group_chars2) + width_excluding_offset_and_final_group = width - offset_width - group_chars1 - group_chars2 - len( + format_sep) * bool(group_chars2) + width_excluding_offset_and_final_group = max(width_excluding_offset_and_final_group, 0) + groups_per_line = 1 + width_excluding_offset_and_final_group // total_group_chars + max_bits_per_line = groups_per_line * bits_per_group # Number of bits represented on each line + else: + assert bits_per_group == 0 # Don't divide into groups + width_available = width - offset_width - len(format_sep) * (name2 is not None) + width_available = max(width_available, 1) + if name2 is None: + max_bits_per_line = width_available * Bits._bits_per_char(name1) + else: + chars_per_24_bits = dtype_register[name1].bitlength2chars_fn(24) + dtype_register[name2].bitlength2chars_fn(24) + max_bits_per_line = 24 * (width_available // chars_per_24_bits) + if max_bits_per_line == 0: + max_bits_per_line = 24 # We can't fit into the width asked for. Show something small. + assert max_bits_per_line > 0 + + bitpos = 0 + first_fb_width = second_fb_width = None + for bits in self.cut(max_bits_per_line): + offset_str = '' + if show_offset: + offset = bitpos // offset_factor + bitpos += len(bits) + if bitstring.options.lsb0: + offset_str = colour.green + offset_sep + f'{offset: <{offset_width - len(offset_sep)}}' + colour.off + else: + offset_str = colour.green + f'{offset: >{offset_width - len(offset_sep)}}' + offset_sep + colour.off + + fb1, chars_used = Bits._format_bits(bits, bits_per_group, sep, dtype1, colour.purple, colour.off, first_fb_width) + if first_fb_width is None: + first_fb_width = chars_used + + fb2 = '' + if dtype2 is not None: + fb2, chars_used = Bits._format_bits(bits, bits_per_group, sep, dtype2, colour.blue, colour.off, second_fb_width) + if second_fb_width is None: + second_fb_width = chars_used + fb2 = format_sep + fb2 + + if bitstring.options.lsb0 is True: + line_fmt = fb1 + fb2 + offset_str + '\n' + else: + line_fmt = offset_str + fb1 + fb2 + '\n' + stream.write(line_fmt) + return + + @staticmethod + def _process_pp_tokens(token_list, fmt): + if len(token_list) not in [1, 2]: + raise ValueError( + f"Only one or two tokens can be used in an pp() format - '{fmt}' has {len(token_list)} tokens.") + has_length_in_fmt = True + name1, length1 = utils.parse_name_length_token(token_list[0]) + dtype1 = Dtype(name1, length1) + bits_per_group = dtype1.bitlength + dtype2 = None + + if len(token_list) == 2: + name2, length2 = utils.parse_name_length_token(token_list[1]) + dtype2 = Dtype(name2, length2) + if None not in {dtype1.bitlength, dtype2.bitlength} and dtype1.bitlength != dtype2.bitlength: + raise ValueError( + f"Differing bit lengths of {dtype1.bitlength} and {dtype2.bitlength} in format string '{fmt}'.") + if bits_per_group is None: + bits_per_group = dtype2.bitlength + + if bits_per_group is None: + has_length_in_fmt = False + if len(token_list) == 1: + bits_per_group = {'bin': 8, 'hex': 8, 'oct': 12, 'bytes': 32}.get(dtype1.name) + if bits_per_group is None: + raise ValueError(f"No length or default length available for pp() format '{fmt}'.") + else: + try: + bits_per_group = 2 * Bits._bits_per_char(dtype1.name) * Bits._bits_per_char(dtype2.name) + except ValueError: + raise ValueError(f"Can't find a default bitlength to use for pp() format '{fmt}'.") + if bits_per_group >= 24: + bits_per_group //= 2 + return dtype1, dtype2, bits_per_group, has_length_in_fmt + + def pp(self, fmt: Optional[str] = None, width: int = 120, sep: str = ' ', + show_offset: bool = True, stream: TextIO = sys.stdout) -> None: + """Pretty print the bitstring's value. + + fmt -- Printed data format. One or two of 'bin', 'oct', 'hex' or 'bytes'. + The number of bits represented in each printed group defaults to 8 for hex and bin, + 12 for oct and 32 for bytes. This can be overridden with an explicit length, e.g. 'hex:64'. + Use a length of 0 to not split into groups, e.g. `bin:0`. + width -- Max width of printed lines. Defaults to 120. A single group will always be printed + per line even if it exceeds the max width. + sep -- A separator string to insert between groups. Defaults to a single space. + show_offset -- If True (the default) shows the bit offset in the first column of each line. + stream -- A TextIO object with a write() method. Defaults to sys.stdout. + + >>> s.pp('hex16') + >>> s.pp('b, h', sep='_', show_offset=False) + + """ + colour = Colour(not bitstring.options.no_color) + if fmt is None: + fmt = 'bin, hex' if len(self) % 8 == 0 and len(self) >= 8 else 'bin' + token_list = utils.preprocess_tokens(fmt) + dtype1, dtype2, bits_per_group, has_length_in_fmt = Bits._process_pp_tokens(token_list, fmt) + trailing_bit_length = len(self) % bits_per_group if has_length_in_fmt and bits_per_group else 0 + data = self if trailing_bit_length == 0 else self[0: -trailing_bit_length] + format_sep = " : " # String to insert on each line between multiple formats + tidy_fmt = colour.purple + str(dtype1) + colour.off + if dtype2 is not None: + tidy_fmt += ', ' + colour.blue + str(dtype2) + colour.off + output_stream = io.StringIO() + len_str = colour.green + str(len(self)) + colour.off + output_stream.write(f"<{self.__class__.__name__}, fmt='{tidy_fmt}', length={len_str} bits> [\n") + data._pp(dtype1, dtype2, bits_per_group, width, sep, format_sep, show_offset, + output_stream, bitstring.options.lsb0, 1) + output_stream.write("]") + if trailing_bit_length != 0: + output_stream.write(" + trailing_bits = " + str(self[-trailing_bit_length:])) + output_stream.write("\n") + stream.write(output_stream.getvalue()) + return + + def copy(self: TBits) -> TBits: + """Return a copy of the bitstring.""" + # Note that if you want a new copy (different ID), use _copy instead. + # The copy can return self as it's immutable. + return self + + @classmethod + def fromstring(cls: TBits, s: str, /) -> TBits: + """Create a new bitstring from a formatted string.""" + x = super().__new__(cls) + x._bitstore = bitstore_helpers.str_to_bitstore(s) + return x + + len = length = property(_getlength, doc="The length of the bitstring in bits. Read only.") + + diff --git a/code/.venv/lib/python3.12/site-packages/bitstring/bitstore.py b/code/.venv/lib/python3.12/site-packages/bitstring/bitstore.py new file mode 100644 index 0000000..cdb199f --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/bitstring/bitstore.py @@ -0,0 +1,272 @@ +from __future__ import annotations + +import bitarray +from bitstring.exceptions import CreationError +from typing import Union, Iterable, Optional, overload, Iterator, Any + + +def offset_slice_indices_lsb0(key: slice, length: int) -> slice: + # First convert slice to all integers + # Length already should take account of the offset + start, stop, step = key.indices(length) + new_start = length - stop + new_stop = length - start + # For negative step we sometimes get a negative stop, which can't be used correctly in a new slice + return slice(new_start, None if new_stop < 0 else new_stop, step) + + +def offset_start_stop_lsb0(start: Optional[int], stop: Optional[int], length: int) -> tuple[int, int]: + # First convert slice to all integers + # Length already should take account of the offset + start, stop, _ = slice(start, stop, None).indices(length) + new_start = length - stop + new_stop = length - start + return new_start, new_stop + + +class BitStore: + """A light wrapper around bitarray that does the LSB0 stuff""" + + __slots__ = ('_bitarray', 'modified_length', 'immutable') + + def __init__(self, initializer: Union[int, bitarray.bitarray, str, None] = None, + immutable: bool = False) -> None: + self._bitarray = bitarray.bitarray(initializer) + self.immutable = immutable + self.modified_length = None + + @classmethod + def frombytes(cls, b: Union[bytes, bytearray, memoryview], /) -> BitStore: + x = super().__new__(cls) + x._bitarray = bitarray.bitarray() + x._bitarray.frombytes(b) + x.immutable = False + x.modified_length = None + return x + + @classmethod + def frombuffer(cls, buffer, /, length: Optional[int] = None) -> BitStore: + x = super().__new__(cls) + x._bitarray = bitarray.bitarray(buffer=buffer) + x.immutable = True + x.modified_length = length + # Here 'modified' means it shouldn't be changed further, so setting, deleting etc. are disallowed. + if x.modified_length is not None: + if x.modified_length < 0: + raise CreationError("Can't create bitstring with a negative length.") + if x.modified_length > len(x._bitarray): + raise CreationError( + f"Can't create bitstring with a length of {x.modified_length} from {len(x._bitarray)} bits of data.") + return x + + def setall(self, value: int, /) -> None: + self._bitarray.setall(value) + + def tobytes(self) -> bytes: + if self.modified_length is not None: + return self._bitarray[:self.modified_length].tobytes() + return self._bitarray.tobytes() + + def slice_to_uint(self, start: Optional[int] = None, end: Optional[int] = None) -> int: + return bitarray.util.ba2int(self.getslice(start, end)._bitarray, signed=False) + + def slice_to_int(self, start: Optional[int] = None, end: Optional[int] = None) -> int: + return bitarray.util.ba2int(self.getslice(start, end)._bitarray, signed=True) + + def slice_to_hex(self, start: Optional[int] = None, end: Optional[int] = None) -> str: + return bitarray.util.ba2hex(self.getslice(start, end)._bitarray) + + def slice_to_bin(self, start: Optional[int] = None, end: Optional[int] = None) -> str: + return self.getslice(start, end)._bitarray.to01() + + def slice_to_oct(self, start: Optional[int] = None, end: Optional[int] = None) -> str: + return bitarray.util.ba2base(8, self.getslice(start, end)._bitarray) + + def __iadd__(self, other: BitStore, /) -> BitStore: + self._bitarray += other._bitarray + return self + + def __add__(self, other: BitStore, /) -> BitStore: + bs = self._copy() + bs += other + return bs + + def __eq__(self, other: Any, /) -> bool: + return self._bitarray == other._bitarray + + def __and__(self, other: BitStore, /) -> BitStore: + return BitStore(self._bitarray & other._bitarray) + + def __or__(self, other: BitStore, /) -> BitStore: + return BitStore(self._bitarray | other._bitarray) + + def __xor__(self, other: BitStore, /) -> BitStore: + return BitStore(self._bitarray ^ other._bitarray) + + def __iand__(self, other: BitStore, /) -> BitStore: + self._bitarray &= other._bitarray + return self + + def __ior__(self, other: BitStore, /) -> BitStore: + self._bitarray |= other._bitarray + return self + + def __ixor__(self, other: BitStore, /) -> BitStore: + self._bitarray ^= other._bitarray + return self + + def find(self, bs: BitStore, start: int, end: int, bytealigned: bool = False) -> int: + if not bytealigned: + return self._bitarray.find(bs._bitarray, start, end) + try: + return next(self.findall_msb0(bs, start, end, bytealigned)) + except StopIteration: + return -1 + + def rfind(self, bs: BitStore, start: int, end: int, bytealigned: bool = False): + if not bytealigned: + return self._bitarray.find(bs._bitarray, start, end, right=True) + try: + return next(self.rfindall_msb0(bs, start, end, bytealigned)) + except StopIteration: + return -1 + + def findall_msb0(self, bs: BitStore, start: int, end: int, bytealigned: bool = False) -> Iterator[int]: + if bytealigned is True and len(bs) % 8 == 0: + # Special case, looking for whole bytes on whole byte boundaries + bytes_ = bs.tobytes() + # Round up start byte to next byte, and round end byte down. + # We're only looking for whole bytes, so can ignore bits at either end. + start_byte = (start + 7) // 8 + end_byte = end // 8 + b = self._bitarray[start_byte * 8: end_byte * 8].tobytes() + byte_pos = 0 + bytes_to_search = end_byte - start_byte + while byte_pos < bytes_to_search: + byte_pos = b.find(bytes_, byte_pos) + if byte_pos == -1: + break + yield (byte_pos + start_byte) * 8 + byte_pos = byte_pos + 1 + return + # General case + i = self._bitarray.itersearch(bs._bitarray, start, end) + if not bytealigned: + for p in i: + yield p + else: + for p in i: + if (p % 8) == 0: + yield p + + def rfindall_msb0(self, bs: BitStore, start: int, end: int, bytealigned: bool = False) -> Iterator[int]: + i = self._bitarray.itersearch(bs._bitarray, start, end, right=True) + if not bytealigned: + for p in i: + yield p + else: + for p in i: + if (p % 8) == 0: + yield p + + def count(self, value, /) -> int: + return self._bitarray.count(value) + + def clear(self) -> None: + self._bitarray.clear() + + def reverse(self) -> None: + self._bitarray.reverse() + + def __iter__(self) -> Iterable[bool]: + for i in range(len(self)): + yield self.getindex(i) + + def _copy(self) -> BitStore: + """Always creates a copy, even if instance is immutable.""" + return BitStore(self._bitarray) + + def copy(self) -> BitStore: + return self if self.immutable else self._copy() + + def __getitem__(self, item: Union[int, slice], /) -> Union[int, BitStore]: + # Use getindex or getslice instead + raise NotImplementedError + + def getindex_msb0(self, index: int, /) -> bool: + return bool(self._bitarray.__getitem__(index)) + + def getslice_withstep_msb0(self, key: slice, /) -> BitStore: + if self.modified_length is not None: + key = slice(*key.indices(self.modified_length)) + return BitStore(self._bitarray.__getitem__(key)) + + def getslice_withstep_lsb0(self, key: slice, /) -> BitStore: + key = offset_slice_indices_lsb0(key, len(self)) + return BitStore(self._bitarray.__getitem__(key)) + + def getslice_msb0(self, start: Optional[int], stop: Optional[int], /) -> BitStore: + if self.modified_length is not None: + key = slice(*slice(start, stop, None).indices(self.modified_length)) + start = key.start + stop = key.stop + return BitStore(self._bitarray[start:stop]) + + def getslice_lsb0(self, start: Optional[int], stop: Optional[int], /) -> BitStore: + start, stop = offset_start_stop_lsb0(start, stop, len(self)) + return BitStore(self._bitarray[start:stop]) + + def getindex_lsb0(self, index: int, /) -> bool: + return bool(self._bitarray.__getitem__(-index - 1)) + + @overload + def setitem_lsb0(self, key: int, value: int, /) -> None: + ... + + @overload + def setitem_lsb0(self, key: slice, value: BitStore, /) -> None: + ... + + def setitem_lsb0(self, key: Union[int, slice], value: Union[int, BitStore], /) -> None: + if isinstance(key, slice): + new_slice = offset_slice_indices_lsb0(key, len(self)) + self._bitarray.__setitem__(new_slice, value._bitarray) + else: + self._bitarray.__setitem__(-key - 1, value) + + def delitem_lsb0(self, key: Union[int, slice], /) -> None: + if isinstance(key, slice): + new_slice = offset_slice_indices_lsb0(key, len(self)) + self._bitarray.__delitem__(new_slice) + else: + self._bitarray.__delitem__(-key - 1) + + def invert_msb0(self, index: Optional[int] = None, /) -> None: + if index is not None: + self._bitarray.invert(index) + else: + self._bitarray.invert() + + def invert_lsb0(self, index: Optional[int] = None, /) -> None: + if index is not None: + self._bitarray.invert(-index - 1) + else: + self._bitarray.invert() + + def any_set(self) -> bool: + return self._bitarray.any() + + def all_set(self) -> bool: + return self._bitarray.all() + + def __len__(self) -> int: + return self.modified_length if self.modified_length is not None else len(self._bitarray) + + def setitem_msb0(self, key, value, /): + if isinstance(value, BitStore): + self._bitarray.__setitem__(key, value._bitarray) + else: + self._bitarray.__setitem__(key, value) + + def delitem_msb0(self, key, /): + self._bitarray.__delitem__(key) diff --git a/code/.venv/lib/python3.12/site-packages/bitstring/bitstore_helpers.py b/code/.venv/lib/python3.12/site-packages/bitstring/bitstore_helpers.py new file mode 100644 index 0000000..41d30d2 --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/bitstring/bitstore_helpers.py @@ -0,0 +1,270 @@ +from __future__ import annotations + +import struct +import math +import functools +from typing import Union, Optional, Dict, Callable +import bitarray +from bitstring.bitstore import BitStore +import bitstring +from bitstring.fp8 import p4binary_fmt, p3binary_fmt +from bitstring.mxfp import (e3m2mxfp_fmt, e2m3mxfp_fmt, e2m1mxfp_fmt, e4m3mxfp_saturate_fmt, + e5m2mxfp_saturate_fmt, e4m3mxfp_overflow_fmt, e5m2mxfp_overflow_fmt) + +# The size of various caches used to improve performance +CACHE_SIZE = 256 + + +def tidy_input_string(s: str) -> str: + """Return string made lowercase and with all whitespace and underscores removed.""" + try: + t = s.split() + except (AttributeError, TypeError): + raise ValueError(f"Expected str object but received a {type(s)} with value {s}.") + return ''.join(t).lower().replace('_', '') + + +@functools.lru_cache(CACHE_SIZE) +def str_to_bitstore(s: str) -> BitStore: + _, tokens = bitstring.utils.tokenparser(s) + bs = BitStore() + for token in tokens: + bs += bitstore_from_token(*token) + bs.immutable = True + return bs + + +def bin2bitstore(binstring: str) -> BitStore: + binstring = tidy_input_string(binstring) + binstring = binstring.replace('0b', '') + try: + return BitStore(binstring) + except ValueError: + raise bitstring.CreationError(f"Invalid character in bin initialiser {binstring}.") + + +def bin2bitstore_unsafe(binstring: str) -> BitStore: + return BitStore(binstring) + + +def hex2bitstore(hexstring: str) -> BitStore: + hexstring = tidy_input_string(hexstring) + hexstring = hexstring.replace('0x', '') + try: + ba = bitarray.util.hex2ba(hexstring) + except ValueError: + raise bitstring.CreationError("Invalid symbol in hex initialiser.") + return BitStore(ba) + + +def oct2bitstore(octstring: str) -> BitStore: + octstring = tidy_input_string(octstring) + octstring = octstring.replace('0o', '') + try: + ba = bitarray.util.base2ba(8, octstring) + except ValueError: + raise bitstring.CreationError("Invalid symbol in oct initialiser.") + return BitStore(ba) + + +def ue2bitstore(i: Union[str, int]) -> BitStore: + i = int(i) + if i < 0: + raise bitstring.CreationError("Cannot use negative initialiser for unsigned exponential-Golomb.") + if i == 0: + return BitStore('1') + tmp = i + 1 + leadingzeros = -1 + while tmp > 0: + tmp >>= 1 + leadingzeros += 1 + remainingpart = i + 1 - (1 << leadingzeros) + return BitStore('0' * leadingzeros + '1') + int2bitstore(remainingpart, leadingzeros, False) + + +def se2bitstore(i: Union[str, int]) -> BitStore: + i = int(i) + if i > 0: + u = (i * 2) - 1 + else: + u = -2 * i + return ue2bitstore(u) + + +def uie2bitstore(i: Union[str, int]) -> BitStore: + i = int(i) + if i < 0: + raise bitstring.CreationError("Cannot use negative initialiser for unsigned interleaved exponential-Golomb.") + return BitStore('1' if i == 0 else '0' + '0'.join(bin(i + 1)[3:]) + '1') + + +def sie2bitstore(i: Union[str, int]) -> BitStore: + i = int(i) + if i == 0: + return BitStore('1') + else: + return uie2bitstore(abs(i)) + (BitStore('1') if i < 0 else BitStore('0')) + + +def bfloat2bitstore(f: Union[str, float], big_endian: bool) -> BitStore: + f = float(f) + fmt = '>f' if big_endian else ' 0 else float('-inf')) + return BitStore.frombytes(b[0:2]) if big_endian else BitStore.frombytes(b[2:4]) + + +def p4binary2bitstore(f: Union[str, float]) -> BitStore: + f = float(f) + u = p4binary_fmt.float_to_int8(f) + return int2bitstore(u, 8, False) + + +def p3binary2bitstore(f: Union[str, float]) -> BitStore: + f = float(f) + u = p3binary_fmt.float_to_int8(f) + return int2bitstore(u, 8, False) + + +def e4m3mxfp2bitstore(f: Union[str, float]) -> BitStore: + f = float(f) + if bitstring.options.mxfp_overflow == 'saturate': + u = e4m3mxfp_saturate_fmt.float_to_int(f) + else: + u = e4m3mxfp_overflow_fmt.float_to_int(f) + return int2bitstore(u, 8, False) + + +def e5m2mxfp2bitstore(f: Union[str, float]) -> BitStore: + f = float(f) + if bitstring.options.mxfp_overflow == 'saturate': + u = e5m2mxfp_saturate_fmt.float_to_int(f) + else: + u = e5m2mxfp_overflow_fmt.float_to_int(f) + return int2bitstore(u, 8, False) + + +def e3m2mxfp2bitstore(f: Union[str, float]) -> BitStore: + f = float(f) + if math.isnan(f): + raise ValueError("Cannot convert float('nan') to e3m2mxfp format as it has no representation for it.") + u = e3m2mxfp_fmt.float_to_int(f) + return int2bitstore(u, 6, False) + + +def e2m3mxfp2bitstore(f: Union[str, float]) -> BitStore: + f = float(f) + if math.isnan(f): + raise ValueError("Cannot convert float('nan') to e2m3mxfp format as it has no representation for it.") + u = e2m3mxfp_fmt.float_to_int(f) + return int2bitstore(u, 6, False) + + +def e2m1mxfp2bitstore(f: Union[str, float]) -> BitStore: + f = float(f) + if math.isnan(f): + raise ValueError("Cannot convert float('nan') to e2m1mxfp format as it has no representation for it.") + u = e2m1mxfp_fmt.float_to_int(f) + return int2bitstore(u, 4, False) + + +e8m0mxfp_allowed_values = [float(2 ** x) for x in range(-127, 128)] + + +def e8m0mxfp2bitstore(f: Union[str, float]) -> BitStore: + f = float(f) + if math.isnan(f): + return BitStore('11111111') + try: + i = e8m0mxfp_allowed_values.index(f) + except ValueError: + raise ValueError(f"{f} is not a valid e8m0mxfp value. It must be exactly 2 ** i, for -127 <= i <= 127 or float('nan') as no rounding will be done.") + return int2bitstore(i, 8, False) + + +def mxint2bitstore(f: Union[str, float]) -> BitStore: + f = float(f) + if math.isnan(f): + raise ValueError("Cannot convert float('nan') to mxint format as it has no representation for it.") + f *= 2 ** 6 # Remove the implicit scaling factor + if f > 127: # 1 + 63/64 + return BitStore('01111111') + if f <= -128: # -2 + return BitStore('10000000') + # Want to round to nearest, so move by 0.5 away from zero and round down by converting to int + if f >= 0.0: + f += 0.5 + i = int(f) + # For ties-round-to-even + if f - i == 0.0 and i % 2: + i -= 1 + else: + f -= 0.5 + i = int(f) + if f - i == 0.0 and i % 2: + i += 1 + return int2bitstore(i, 8, True) + + +def int2bitstore(i: int, length: int, signed: bool) -> BitStore: + i = int(i) + try: + x = BitStore(bitarray.util.int2ba(i, length=length, endian='big', signed=signed)) + except OverflowError as e: + if signed: + if i >= (1 << (length - 1)) or i < -(1 << (length - 1)): + raise bitstring.CreationError(f"{i} is too large a signed integer for a bitstring of length {length}. " + f"The allowed range is [{-(1 << (length - 1))}, {(1 << (length - 1)) - 1}].") + else: + if i >= (1 << length): + raise bitstring.CreationError(f"{i} is too large an unsigned integer for a bitstring of length {length}. " + f"The allowed range is [0, {(1 << length) - 1}].") + if i < 0: + raise bitstring.CreationError("uint cannot be initialised with a negative number.") + raise e + return x + + +def intle2bitstore(i: int, length: int, signed: bool) -> BitStore: + x = int2bitstore(i, length, signed).tobytes() + return BitStore.frombytes(x[::-1]) + + +def float2bitstore(f: Union[str, float], length: int, big_endian: bool) -> BitStore: + f = float(f) + fmt = {16: '>e', 32: '>f', 64: '>d'}[length] if big_endian else {16: ' 0 else float('-inf')) + return BitStore.frombytes(b) + + +literal_bit_funcs: Dict[str, Callable[..., BitStore]] = { + '0x': hex2bitstore, + '0X': hex2bitstore, + '0b': bin2bitstore, + '0B': bin2bitstore, + '0o': oct2bitstore, + '0O': oct2bitstore, +} + + +def bitstore_from_token(name: str, token_length: Optional[int], value: Optional[str]) -> BitStore: + if name in literal_bit_funcs: + return literal_bit_funcs[name](value) + try: + d = bitstring.dtypes.Dtype(name, token_length) + except ValueError as e: + raise bitstring.CreationError(f"Can't parse token: {e}") + if value is None and name != 'pad': + raise ValueError(f"Token {name} requires a value.") + bs = d.build(value)._bitstore + if token_length is not None and len(bs) != d.bitlength: + raise bitstring.CreationError(f"Token with length {token_length} packed with value of length {len(bs)} " + f"({name}:{token_length}={value}).") + return bs diff --git a/code/.venv/lib/python3.12/site-packages/bitstring/bitstream.py b/code/.venv/lib/python3.12/site-packages/bitstring/bitstream.py new file mode 100644 index 0000000..8e7c6a1 --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/bitstring/bitstream.py @@ -0,0 +1,714 @@ +from __future__ import annotations + +import bitstring +from bitstring.bits import Bits, BitsType +from bitstring.dtypes import Dtype +from typing import Union, List, Any, Optional, overload, TypeVar, Tuple +import copy +import numbers + +TConstBitStream = TypeVar("TConstBitStream", bound='ConstBitStream') + + +class ConstBitStream(Bits): + """A container or stream holding an immutable sequence of bits. + + For a mutable container use the BitStream class instead. + + Methods inherited from Bits: + + all() -- Check if all specified bits are set to 1 or 0. + any() -- Check if any of specified bits are set to 1 or 0. + copy() -- Return a copy of the bitstring. + count() -- Count the number of bits set to 1 or 0. + cut() -- Create generator of constant sized chunks. + endswith() -- Return whether the bitstring ends with a sub-string. + find() -- Find a sub-bitstring in the current bitstring. + findall() -- Find all occurrences of a sub-bitstring in the current bitstring. + fromstring() -- Create a bitstring from a formatted string. + join() -- Join bitstrings together using current bitstring. + pp() -- Pretty print the bitstring. + rfind() -- Seek backwards to find a sub-bitstring. + split() -- Create generator of chunks split by a delimiter. + startswith() -- Return whether the bitstring starts with a sub-bitstring. + tobitarray() -- Return bitstring as a bitarray from the bitarray package. + tobytes() -- Return bitstring as bytes, padding if needed. + tofile() -- Write bitstring to file, padding if needed. + unpack() -- Interpret bits using format string. + + Other methods: + + bytealign() -- Align to next byte boundary. + peek() -- Peek at and interpret next bits as a single item. + peeklist() -- Peek at and interpret next bits as a list of items. + read() -- Read and interpret next bits as a single item. + readlist() -- Read and interpret next bits as a list of items. + readto() -- Read up to and including next occurrence of a bitstring. + + Special methods: + + Also available are the operators [], ==, !=, +, *, ~, <<, >>, &, |, ^. + + Properties: + + [GENERATED_PROPERTY_DESCRIPTIONS] + + len -- Length of the bitstring in bits. + pos -- The current bit position in the bitstring. + """ + + __slots__ = ('_pos',) + + def __init__(self, auto: Optional[Union[BitsType, int]] = None, /, length: Optional[int] = None, + offset: Optional[int] = None, pos: int = 0, **kwargs) -> None: + """Either specify an 'auto' initialiser: + A string of comma separated tokens, an integer, a file object, + a bytearray, a boolean iterable or another bitstring. + + Or initialise via **kwargs with one (and only one) of: + bin -- binary string representation, e.g. '0b001010'. + hex -- hexadecimal string representation, e.g. '0x2ef' + oct -- octal string representation, e.g. '0o777'. + bytes -- raw data as a bytes object, for example read from a binary file. + int -- a signed integer. + uint -- an unsigned integer. + float / floatbe -- a big-endian floating point number. + bool -- a boolean (True or False). + se -- a signed exponential-Golomb code. + ue -- an unsigned exponential-Golomb code. + sie -- a signed interleaved exponential-Golomb code. + uie -- an unsigned interleaved exponential-Golomb code. + floatle -- a little-endian floating point number. + floatne -- a native-endian floating point number. + bfloat / bfloatbe - a big-endian bfloat format 16-bit floating point number. + bfloatle -- a little-endian bfloat format 16-bit floating point number. + bfloatne -- a native-endian bfloat format 16-bit floating point number. + intbe -- a signed big-endian whole byte integer. + intle -- a signed little-endian whole byte integer. + intne -- a signed native-endian whole byte integer. + uintbe -- an unsigned big-endian whole byte integer. + uintle -- an unsigned little-endian whole byte integer. + uintne -- an unsigned native-endian whole byte integer. + filename -- the path of a file which will be opened in binary read-only mode. + + Other keyword arguments: + length -- length of the bitstring in bits, if needed and appropriate. + It must be supplied for all integer and float initialisers. + offset -- bit offset to the data. These offset bits are + ignored and this is mainly intended for use when + initialising using 'bytes' or 'filename'. + pos -- Initial bit position, defaults to 0. + + """ + if pos < 0: + pos += len(self._bitstore) + if pos < 0 or pos > len(self._bitstore): + raise bitstring.CreationError(f"Cannot set pos to {pos} when length is {len(self._bitstore)}.") + self._pos = pos + self._bitstore.immutable = True + + def _setbytepos(self, bytepos: int) -> None: + """Move to absolute byte-aligned position in stream.""" + self._setbitpos(bytepos * 8) + + def _getbytepos(self) -> int: + """Return the current position in the stream in bytes. Must be byte aligned.""" + if self._pos % 8: + raise bitstring.ByteAlignError("Not byte aligned when using bytepos property.") + return self._pos // 8 + + def _setbitpos(self, pos: int) -> None: + """Move to absolute position bit in bitstream.""" + if pos < 0: + raise ValueError("Bit position cannot be negative.") + if pos > len(self): + raise ValueError("Cannot seek past the end of the data.") + self._pos = pos + + def _getbitpos(self) -> int: + """Return the current position in the stream in bits.""" + return self._pos + + def _clear(self) -> None: + Bits._clear(self) + self._pos = 0 + + def __copy__(self: TConstBitStream) -> TConstBitStream: + """Return a new copy of the ConstBitStream for the copy module.""" + # Note that if you want a new copy (different ID), use _copy instead. + # The copy can use the same datastore as it's immutable. + s = self.__class__() + s._bitstore = self._bitstore + # Reset the bit position, don't copy it. + s._pos = 0 + return s + + def __and__(self: TConstBitStream, bs: BitsType, /) -> TConstBitStream: + """Bit-wise 'and' between two bitstrings. Returns new bitstring. + + bs -- The bitstring to '&' with. + + Raises ValueError if the two bitstrings have differing lengths. + + """ + s = Bits.__and__(self, bs) + s._pos = 0 + return s + + def __or__(self: TConstBitStream, bs: BitsType, /) -> TConstBitStream: + """Bit-wise 'or' between two bitstrings. Returns new bitstring. + + bs -- The bitstring to '|' with. + + Raises ValueError if the two bitstrings have differing lengths. + + """ + s = Bits.__or__(self, bs) + s._pos = 0 + return s + + def __xor__(self: TConstBitStream, bs: BitsType, /) -> TConstBitStream: + """Bit-wise 'xor' between two bitstrings. Returns new bitstring. + + bs -- The bitstring to '^' with. + + Raises ValueError if the two bitstrings have differing lengths. + + """ + s = Bits.__xor__(self, bs) + s._pos = 0 + return s + + def __add__(self: TConstBitStream, bs: BitsType, /) -> TConstBitStream: + """Concatenate bitstrings and return new bitstring. + + bs -- the bitstring to append. + + """ + s = Bits.__add__(self, bs) + s._pos = 0 + return s + + def append(self, bs: BitsType, /) -> None: + """Append a bitstring to the current bitstring. + + bs -- The bitstring to append. + + The current bit position will be moved to the end of the BitStream. + + """ + self._append(bs) + self._pos = len(self) + + def __repr__(self) -> str: + """Return representation that could be used to recreate the bitstring. + + If the returned string is too long it will be truncated. See __str__(). + + """ + return self._repr(self.__class__.__name__, len(self), self._pos) + + def overwrite(self, bs: BitsType, /, pos: Optional[int] = None) -> None: + """Overwrite with bitstring at bit position pos. + + bs -- The bitstring to overwrite with. + pos -- The bit position to begin overwriting from. + + The current bit position will be moved to the end of the overwritten section. + Raises ValueError if pos < 0 or pos > len(self). + + """ + bs = Bits._create_from_bitstype(bs) + if len(bs) == 0: + return + if pos is None: + pos = self._pos + if pos < 0: + pos += len(self) + if pos < 0 or pos > len(self): + raise ValueError("Overwrite starts outside boundary of bitstring.") + self._overwrite(bs, pos) + self._pos = pos + len(bs) + + def find(self, bs: BitsType, /, start: Optional[int] = None, end: Optional[int] = None, + bytealigned: Optional[bool] = None) -> Union[Tuple[int], Tuple[()]]: + """Find first occurrence of substring bs. + + Returns a single item tuple with the bit position if found, or an + empty tuple if not found. The bit position (pos property) will + also be set to the start of the substring if it is found. + + bs -- The bitstring to find. + start -- The bit position to start the search. Defaults to 0. + end -- The bit position one past the last bit to search. + Defaults to len(self). + bytealigned -- If True the bitstring will only be + found on byte boundaries. + + Raises ValueError if bs is empty, if start < 0, if end > len(self) or + if end < start. + + >>> BitStream('0xc3e').find('0b1111') + (6,) + + """ + + p = super().find(bs, start, end, bytealigned) + if p: + self._pos = p[0] + return p + + def rfind(self, bs: BitsType, /, start: Optional[int] = None, end: Optional[int] = None, + bytealigned: Optional[bool] = None) -> Union[Tuple[int], Tuple[()]]: + """Find final occurrence of substring bs. + + Returns a single item tuple with the bit position if found, or an + empty tuple if not found. The bit position (pos property) will + also be set to the start of the substring if it is found. + + bs -- The bitstring to find. + start -- The bit position to end the reverse search. Defaults to 0. + end -- The bit position one past the first bit to reverse search. + Defaults to len(self). + bytealigned -- If True the bitstring will only be found on byte + boundaries. + + Raises ValueError if bs is empty, if start < 0, if end > len(self) or + if end < start. + + """ + p = super().rfind(bs, start, end, bytealigned) + if p: + self._pos = p[0] + return p + + @overload + def read(self, fmt: int) -> Bits: + ... + + @overload + def read(self, fmt: str) -> Any: + ... + + def read(self, fmt: Union[int, str, Dtype]) -> Union[int, float, str, Bits, bool, bytes, None]: + """Interpret next bits according to the format string and return result. + + fmt -- Token string describing how to interpret the next bits. + + Token examples: 'int:12' : 12 bits as a signed integer + 'uint:8' : 8 bits as an unsigned integer + 'float:64' : 8 bytes as a big-endian float + 'intbe:16' : 2 bytes as a big-endian signed integer + 'uintbe:16' : 2 bytes as a big-endian unsigned integer + 'intle:32' : 4 bytes as a little-endian signed integer + 'uintle:32' : 4 bytes as a little-endian unsigned integer + 'floatle:64': 8 bytes as a little-endian float + 'intne:24' : 3 bytes as a native-endian signed integer + 'uintne:24' : 3 bytes as a native-endian unsigned integer + 'floatne:32': 4 bytes as a native-endian float + 'hex:80' : 80 bits as a hex string + 'oct:9' : 9 bits as an octal string + 'bin:1' : single bit binary string + 'ue' : next bits as unsigned exp-Golomb code + 'se' : next bits as signed exp-Golomb code + 'uie' : next bits as unsigned interleaved exp-Golomb code + 'sie' : next bits as signed interleaved exp-Golomb code + 'bits:5' : 5 bits as a bitstring + 'bytes:10' : 10 bytes as a bytes object + 'bool' : 1 bit as a bool + 'pad:3' : 3 bits of padding to ignore - returns None + + fmt may also be an integer, which will be treated like the 'bits' token. + + The position in the bitstring is advanced to after the read items. + + Raises ReadError if not enough bits are available. + Raises ValueError if the format is not understood. + + """ + p = self._pos + if isinstance(fmt, numbers.Integral): + if fmt < 0: + raise ValueError("Cannot read negative amount.") + if fmt > len(self) - self._pos: + raise bitstring.ReadError(f"Cannot read {fmt} bits, only {len(self) - self._pos} available.") + bs = self._slice(self._pos, self._pos + fmt) + self._pos += fmt + return bs + dtype = bitstring.dtypes.Dtype(fmt) + if dtype.bitlength is None and not dtype.variable_length: + # No length specified? Try again, but read to end. + bitlength = len(self) - self._pos + items, remainder = divmod(bitlength, dtype.bits_per_item) + if remainder != 0: + raise ValueError( + f"The '{dtype.name}' type must have a bit length that is a multiple of {dtype.bits_per_item}" + f" so cannot be read from the {bitlength} bits that are available.") + dtype = bitstring.dtypes.Dtype(fmt, items) + if dtype.bitlength is not None: + val = dtype.read_fn(self, self._pos) + self._pos += dtype.bitlength + else: + val, self._pos = dtype.read_fn(self, self._pos) + + if self._pos > len(self): + self._pos = p + raise bitstring.ReadError(f"Reading off end of bitstring with fmt '{fmt}'. Only {len(self) - p} bits available.") + return val + + def readlist(self, fmt: Union[str, List[Union[int, str, Dtype]]], **kwargs) \ + -> List[Union[int, float, str, Bits, bool, bytes, None]]: + """Interpret next bits according to format string(s) and return list. + + fmt -- A single string or list of strings with comma separated tokens + describing how to interpret the next bits in the bitstring. Items + can also be integers, for reading new bitstring of the given length. + kwargs -- A dictionary or keyword-value pairs - the keywords used in the + format string will be replaced with their given value. + + The position in the bitstring is advanced to after the read items. + + Raises ReadError is not enough bits are available. + Raises ValueError if the format is not understood. + + See the docstring for 'read' for token examples. 'pad' tokens are skipped + and not added to the returned list. + + >>> h, b1, b2 = s.readlist('hex:20, bin:5, bin:3') + >>> i, bs1, bs2 = s.readlist(['uint:12', 10, 10]) + + """ + value, self._pos = self._readlist(fmt, self._pos, **kwargs) + return value + + def readto(self: TConstBitStream, bs: BitsType, /, bytealigned: Optional[bool] = None) -> TConstBitStream: + """Read up to and including next occurrence of bs and return result. + + bs -- The bitstring to find. + bytealigned -- If True the bitstring will only be + found on byte boundaries. + + Raises ValueError if bs is empty. + Raises ReadError if bs is not found. + + """ + if isinstance(bs, numbers.Integral): + raise ValueError("Integers cannot be searched for") + bs = Bits._create_from_bitstype(bs) + oldpos = self._pos + p = self.find(bs, self._pos, bytealigned=bytealigned) + if not p: + raise bitstring.ReadError("Substring not found") + self._pos += len(bs) + return self._slice(oldpos, self._pos) + + @overload + def peek(self: TConstBitStream, fmt: int) -> TConstBitStream: + ... + + @overload + def peek(self, fmt: str) -> Union[int, float, str, TConstBitStream, bool, bytes, None]: + ... + + def peek(self: TConstBitStream, fmt: Union[int, str]) -> Union[int, float, str, TConstBitStream, bool, bytes, None]: + """Interpret next bits according to format string and return result. + + fmt -- Token string describing how to interpret the next bits. + + The position in the bitstring is not changed. If not enough bits are + available then all bits to the end of the bitstring will be used. + + Raises ReadError if not enough bits are available. + Raises ValueError if the format is not understood. + + See the docstring for 'read' for token examples. + + """ + pos_before = self._pos + value = self.read(fmt) + self._pos = pos_before + return value + + def peeklist(self, fmt: Union[str, List[Union[int, str]]], **kwargs) \ + -> List[Union[int, float, str, Bits, None]]: + """Interpret next bits according to format string(s) and return list. + + fmt -- One or more integers or strings with comma separated tokens describing + how to interpret the next bits in the bitstring. + kwargs -- A dictionary or keyword-value pairs - the keywords used in the + format string will be replaced with their given value. + + The position in the bitstring is not changed. If not enough bits are + available then all bits to the end of the bitstring will be used. + + Raises ReadError if not enough bits are available. + Raises ValueError if the format is not understood. + + See the docstring for 'read' for token examples. + + """ + pos = self._pos + return_values = self.readlist(fmt, **kwargs) + self._pos = pos + return return_values + + def bytealign(self) -> int: + """Align to next byte and return number of skipped bits. + + Raises ValueError if the end of the bitstring is reached before + aligning to the next byte. + + """ + skipped = (8 - (self._pos % 8)) % 8 + self.pos += skipped + return skipped + + @classmethod + def fromstring(cls: TBits, s: str, /) -> TBits: + x = super().fromstring(s) + x._pos = 0 + x._bitstore.immutable = True + return x + + @overload + def __getitem__(self: TBits, key: slice, /) -> TBits: + ... + + @overload + def __getitem__(self: TBits, key: int, /) -> bool: + ... + + def __getitem__(self: TBits, key: Union[slice, int], /) -> Union[TBits, bool]: + """Return a new bitstring representing a slice of the current bitstring.""" + if isinstance(key, numbers.Integral): + return bool(self._bitstore.getindex(key)) + bs = super().__new__(self.__class__) + bs._bitstore = self._bitstore.getslice_withstep(key) + bs._pos = 0 + return bs + + pos = property(_getbitpos, _setbitpos, + doc="""The position in the bitstring in bits. Read and write. + """) + bitpos = property(_getbitpos, _setbitpos, + doc="""The position in the bitstring in bits. Read and write. + """) + bytepos = property(_getbytepos, _setbytepos, + doc="""The position in the bitstring in bytes. Read and write. + """) + + +class BitStream(ConstBitStream, bitstring.BitArray): + """A container or stream holding a mutable sequence of bits + + Subclass of the ConstBitStream and BitArray classes. Inherits all of + their methods. + + Methods: + + all() -- Check if all specified bits are set to 1 or 0. + any() -- Check if any of specified bits are set to 1 or 0. + append() -- Append a bitstring. + bytealign() -- Align to next byte boundary. + byteswap() -- Change byte endianness in-place. + clear() -- Remove all bits from the bitstring. + copy() -- Return a copy of the bitstring. + count() -- Count the number of bits set to 1 or 0. + cut() -- Create generator of constant sized chunks. + endswith() -- Return whether the bitstring ends with a sub-string. + find() -- Find a sub-bitstring in the current bitstring. + findall() -- Find all occurrences of a sub-bitstring in the current bitstring. + fromstring() -- Create a bitstring from a formatted string. + insert() -- Insert a bitstring. + invert() -- Flip bit(s) between one and zero. + join() -- Join bitstrings together using current bitstring. + overwrite() -- Overwrite a section with a new bitstring. + peek() -- Peek at and interpret next bits as a single item. + peeklist() -- Peek at and interpret next bits as a list of items. + pp() -- Pretty print the bitstring. + prepend() -- Prepend a bitstring. + read() -- Read and interpret next bits as a single item. + readlist() -- Read and interpret next bits as a list of items. + readto() -- Read up to and including next occurrence of a bitstring. + replace() -- Replace occurrences of one bitstring with another. + reverse() -- Reverse bits in-place. + rfind() -- Seek backwards to find a sub-bitstring. + rol() -- Rotate bits to the left. + ror() -- Rotate bits to the right. + set() -- Set bit(s) to 1 or 0. + split() -- Create generator of chunks split by a delimiter. + startswith() -- Return whether the bitstring starts with a sub-bitstring. + tobitarray() -- Return bitstring as a bitarray from the bitarray package. + tobytes() -- Return bitstring as bytes, padding if needed. + tofile() -- Write bitstring to file, padding if needed. + unpack() -- Interpret bits using format string. + + Special methods: + + Mutating operators are available: [], <<=, >>=, +=, *=, &=, |= and ^= + in addition to [], ==, !=, +, *, ~, <<, >>, &, | and ^. + + Properties: + + [GENERATED_PROPERTY_DESCRIPTIONS] + + len -- Length of the bitstring in bits. + pos -- The current bit position in the bitstring. + """ + + __slots__ = () + + def __init__(self, auto: Optional[Union[BitsType, int]] = None, /, length: Optional[int] = None, + offset: Optional[int] = None, pos: int = 0, **kwargs) -> None: + """Either specify an 'auto' initialiser: + A string of comma separated tokens, an integer, a file object, + a bytearray, a boolean iterable or another bitstring. + + Or initialise via **kwargs with one (and only one) of: + bin -- binary string representation, e.g. '0b001010'. + hex -- hexadecimal string representation, e.g. '0x2ef' + oct -- octal string representation, e.g. '0o777'. + bytes -- raw data as a bytes object, for example read from a binary file. + int -- a signed integer. + uint -- an unsigned integer. + float / floatbe -- a big-endian floating point number. + bool -- a boolean (True or False). + se -- a signed exponential-Golomb code. + ue -- an unsigned exponential-Golomb code. + sie -- a signed interleaved exponential-Golomb code. + uie -- an unsigned interleaved exponential-Golomb code. + floatle -- a little-endian floating point number. + floatne -- a native-endian floating point number. + bfloat / bfloatbe - a big-endian bfloat format 16-bit floating point number. + bfloatle -- a little-endian bfloat format 16-bit floating point number. + bfloatne -- a native-endian bfloat format 16-bit floating point number. + intbe -- a signed big-endian whole byte integer. + intle -- a signed little-endian whole byte integer. + intne -- a signed native-endian whole byte integer. + uintbe -- an unsigned big-endian whole byte integer. + uintle -- an unsigned little-endian whole byte integer. + uintne -- an unsigned native-endian whole byte integer. + filename -- the path of a file which will be opened in binary read-only mode. + + Other keyword arguments: + length -- length of the bitstring in bits, if needed and appropriate. + It must be supplied for all integer and float initialisers. + offset -- bit offset to the data. These offset bits are + ignored and this is intended for use when + initialising using 'bytes' or 'filename'. + pos -- Initial bit position, defaults to 0. + + """ + ConstBitStream.__init__(self, auto, length, offset, pos, **kwargs) + if self._bitstore.immutable: + self._bitstore = self._bitstore._copy() + self._bitstore.immutable = False + + def __copy__(self) -> BitStream: + """Return a new copy of the BitStream.""" + s_copy = object.__new__(BitStream) + s_copy._pos = 0 + s_copy._bitstore = self._bitstore.copy() + return s_copy + + def __iadd__(self, bs: BitsType, /) -> BitStream: + """Append to current bitstring. Return self. + + bs -- the bitstring to append. + + The current bit position will be moved to the end of the BitStream. + """ + self._append(bs) + self._pos = len(self) + return self + + def prepend(self, bs: BitsType, /) -> None: + """Prepend a bitstring to the current bitstring. + + bs -- The bitstring to prepend. + + """ + bs = Bits._create_from_bitstype(bs) + super().prepend(bs) + self._pos = 0 + + def __setitem__(self, /, key: Union[slice, int], value: BitsType) -> None: + length_before = len(self) + super().__setitem__(key, value) + if len(self) != length_before: + self._pos = 0 + return + + def __delitem__(self, /, key: Union[slice, int]) -> None: + """Delete item or range. + + >>> a = BitStream('0x001122') + >>> del a[8:16] + >>> print a + 0x0022 + + """ + length_before = len(self) + self._bitstore.__delitem__(key) + if len(self) != length_before: + self._pos = 0 + + def insert(self, bs: BitsType, /, pos: Optional[int] = None) -> None: + """Insert bitstring at bit position pos. + + bs -- The bitstring to insert. + pos -- The bit position to insert at. + + The current bit position will be moved to the end of the inserted section. + Raises ValueError if pos < 0 or pos > len(self). + + """ + bs = Bits._create_from_bitstype(bs) + if len(bs) == 0: + return + if bs is self: + bs = self._copy() + if pos is None: + pos = self._pos + if pos < 0: + pos += len(self) + if not 0 <= pos <= len(self): + raise ValueError("Invalid insert position.") + self._insert(bs, pos) + self._pos = pos + len(bs) + + def replace(self, old: BitsType, new: BitsType, start: Optional[int] = None, end: Optional[int] = None, + count: Optional[int] = None, bytealigned: Optional[bool] = None) -> int: + """Replace all occurrences of old with new in place. + + Returns number of replacements made. + + old -- The bitstring to replace. + new -- The replacement bitstring. + start -- Any occurrences that start before this will not be replaced. + Defaults to 0. + end -- Any occurrences that finish after this will not be replaced. + Defaults to len(self). + count -- The maximum number of replacements to make. Defaults to + replace all occurrences. + bytealigned -- If True replacements will only be made on byte + boundaries. + + Raises ValueError if old is empty or if start or end are + out of range. + + """ + if count == 0: + return 0 + if len(old := Bits._create_from_bitstype(old)) == 0: + raise ValueError("Empty bitstring cannot be replaced.") + start, end = self._validate_slice(start, end) + new = Bits._create_from_bitstype(new) + if new is self: + # Prevent self assignment woes + new = copy.copy(self) + length_before = len(self) + replacement_count = self._replace(old, new, start, end, 0 if count is None else count, bytealigned) + if len(self) != length_before: + self._pos = 0 + return replacement_count \ No newline at end of file diff --git a/code/.venv/lib/python3.12/site-packages/bitstring/bitstring_options.py b/code/.venv/lib/python3.12/site-packages/bitstring/bitstring_options.py new file mode 100644 index 0000000..de84ae5 --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/bitstring/bitstring_options.py @@ -0,0 +1,95 @@ +from __future__ import annotations + +import bitstring +import os + + +class Options: + """Internal class to create singleton module options instance.""" + + _instance = None + + def __init__(self): + self.set_lsb0(False) + self._bytealigned = False + self.mxfp_overflow = 'saturate' + + self.no_color = False + no_color = os.getenv('NO_COLOR') + self.no_color = True if no_color else False + + @property + def mxfp_overflow(self) -> str: + return self._mxfp_overflow + + @mxfp_overflow.setter + def mxfp_overflow(self, value: str) -> None: + allowed_values = ('saturate', 'overflow') + if value not in allowed_values: + raise ValueError(f"mxfp_overflow must be one of {allowed_values}, not {value}.") + self._mxfp_overflow = value + + def __repr__(self) -> str: + attributes = {attr: getattr(self, attr) for attr in dir(self) if not attr.startswith('_') and not callable(getattr(self, attr))} + return '\n'.join(f"{attr}: {value!r}" for attr, value in attributes.items()) + + @property + def lsb0(self) -> bool: + return self._lsb0 + + @lsb0.setter + def lsb0(self, value: bool) -> None: + self.set_lsb0(value) + + def set_lsb0(self, value: bool) -> None: + self._lsb0 = bool(value) + Bits = bitstring.bits.Bits + BitArray = bitstring.bitarray_.BitArray + BitStore = bitstring.bitstore.BitStore + + lsb0_methods = { + Bits: {'_find': Bits._find_lsb0, '_rfind': Bits._rfind_lsb0, '_findall': Bits._findall_lsb0}, + BitArray: {'_ror': BitArray._rol_msb0, '_rol': BitArray._ror_msb0, '_append': BitArray._append_lsb0, + '_prepend': BitArray._append_msb0}, + BitStore: {'__setitem__': BitStore.setitem_lsb0, '__delitem__': BitStore.delitem_lsb0, + 'getindex': BitStore.getindex_lsb0, 'getslice': BitStore.getslice_lsb0, + 'getslice_withstep': BitStore.getslice_withstep_lsb0, 'invert': BitStore.invert_lsb0} + } + msb0_methods = { + Bits: {'_find': Bits._find_msb0, '_rfind': Bits._rfind_msb0, '_findall': Bits._findall_msb0}, + BitArray: {'_ror': BitArray._ror_msb0, '_rol': BitArray._rol_msb0, '_append': BitArray._append_msb0, + '_prepend': BitArray._append_lsb0}, + BitStore: {'__setitem__': BitStore.setitem_msb0, '__delitem__': BitStore.delitem_msb0, + 'getindex': BitStore.getindex_msb0, 'getslice': BitStore.getslice_msb0, + 'getslice_withstep': BitStore.getslice_withstep_msb0, 'invert': BitStore.invert_msb0} + } + methods = lsb0_methods if self._lsb0 else msb0_methods + for cls, method_dict in methods.items(): + for attr, method in method_dict.items(): + setattr(cls, attr, method) + + @property + def bytealigned(self) -> bool: + return self._bytealigned + + @bytealigned.setter + def bytealigned(self, value: bool) -> None: + self._bytealigned = bool(value) + + def __new__(cls): + if cls._instance is None: + cls._instance = super(Options, cls).__new__(cls) + return cls._instance + + +class Colour: + def __new__(cls, use_colour: bool) -> Colour: + x = super().__new__(cls) + if use_colour: + cls.blue = '\033[34m' + cls.purple = '\033[35m' + cls.green = '\033[32m' + cls.off = '\033[0m' + else: + cls.blue = cls.purple = cls.green = cls.off = '' + return x diff --git a/code/.venv/lib/python3.12/site-packages/bitstring/dtypes.py b/code/.venv/lib/python3.12/site-packages/bitstring/dtypes.py new file mode 100644 index 0000000..86c4ad1 --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/bitstring/dtypes.py @@ -0,0 +1,403 @@ +from __future__ import annotations + +import functools +from typing import Optional, Dict, Any, Union, Tuple, Callable +import inspect +import bitstring +from bitstring import utils + +CACHE_SIZE = 256 + + +def scaled_get_fn(get_fn, s: Union[int, float]): + def wrapper(*args, scale=s, **kwargs): + return get_fn(*args, **kwargs) * scale + return wrapper + + +def scaled_set_fn(set_fn, s: Union[int, float]): + def wrapper(bs, value, *args, scale=s, **kwargs): + return set_fn(bs, value / scale, *args, **kwargs) + return wrapper + + +def scaled_read_fn(read_fn, s: Union[int, float]): + def wrapper(*args, scale=s, **kwargs): + val = read_fn(*args, **kwargs) + if isinstance(val, tuple): + val, pos = val + return val * scale, pos + return val * scale + return wrapper + + +class Dtype: + """A data type class, representing a concrete interpretation of binary data. + + Dtype instances are immutable. They are often created implicitly elsewhere via a token string. + + >>> u12 = Dtype('uint', 12) # length separate from token string. + >>> float16 = Dtype('float16') # length part of token string. + >>> mxfp = Dtype('e3m2mxfp', scale=2 ** 6) # dtype with scaling factor + + """ + + _name: str + _read_fn: Callable + _set_fn: Callable + _get_fn: Callable + _return_type: Any + _is_signed: bool + _set_fn_needs_length: bool + _variable_length: bool + _bitlength: Optional[int] + _bits_per_item: int + _length: Optional[int] + _scale: Union[None, float, int] + + def __new__(cls, token: Union[str, Dtype], /, length: Optional[int] = None, scale: Union[None, float, int] = None) -> Dtype: + if isinstance(token, cls): + return token + if length is None: + x = cls._new_from_token(token, scale) + return x + else: + x = dtype_register.get_dtype(token, length, scale) + return x + + @property + def scale(self) -> Union[int, float, None]: + """The multiplicative scale applied when interpreting the data.""" + return self._scale + + @property + def name(self) -> str: + """A string giving the name of the data type.""" + return self._name + + @property + def length(self) -> int: + """The length of the data type in units of bits_per_item. Set to None for variable length dtypes.""" + return self._length + + @property + def bitlength(self) -> Optional[int]: + """The number of bits needed to represent a single instance of the data type. Set to None for variable length dtypes.""" + return self._bitlength + + @property + def bits_per_item(self) -> int: + """The number of bits for each unit of length. Usually 1, but equals 8 for bytes type.""" + return self._bits_per_item + + @property + def variable_length(self) -> bool: + """If True then the length of the data type depends on the data being interpreted, and must not be specified.""" + return self._variable_length + + @property + def return_type(self) -> Any: + """The type of the value returned by the parse method, such as int, float or str.""" + return self._return_type + + @property + def is_signed(self) -> bool: + """If True then the data type represents a signed quantity.""" + return self._is_signed + + @property + def set_fn(self) -> Optional[Callable]: + """A function to set the value of the data type.""" + return self._set_fn + + @property + def get_fn(self) -> Callable: + """A function to get the value of the data type.""" + return self._get_fn + + @property + def read_fn(self) -> Callable: + """A function to read the value of the data type.""" + return self._read_fn + + def _set_scale(self, value: Union[None, float, int]) -> None: + self._scale = value + if self._scale is None: + return + if self._scale == 0: + raise ValueError("A Dtype's scale factor must not be zero.") + if not hasattr(self, 'unscaled_get_fn'): + self.unscaled_get_fn = self._get_fn + self.unscaled_set_fn = self._set_fn + self.unscaled_read_fn = self._read_fn + self._get_fn = scaled_get_fn(self.unscaled_get_fn, self._scale) + self._set_fn = scaled_set_fn(self.unscaled_set_fn, self._scale) + self._read_fn = scaled_read_fn(self.unscaled_read_fn, self._scale) + + @classmethod + @functools.lru_cache(CACHE_SIZE) + def _new_from_token(cls, token: str, scale: Union[None, float, int] = None) -> Dtype: + token = ''.join(token.split()) + return dtype_register.get_dtype(*utils.parse_name_length_token(token), scale=scale) + + def __hash__(self) -> int: + return hash((self._name, self._length)) + + @classmethod + @functools.lru_cache(CACHE_SIZE) + def _create(cls, definition: DtypeDefinition, length: Optional[int], scale: Union[None, float, int]) -> Dtype: + x = super().__new__(cls) + x._name = definition.name + x._bitlength = x._length = length + x._bits_per_item = definition.multiplier + if x._bitlength is not None: + x._bitlength *= x._bits_per_item + x._set_fn_needs_length = definition.set_fn_needs_length + x._variable_length = definition.variable_length + if x._variable_length or dtype_register.names[x._name].allowed_lengths.only_one_value(): + x._read_fn = definition.read_fn + else: + x._read_fn = functools.partial(definition.read_fn, length=x._bitlength) + if definition.set_fn is None: + x._set_fn = None + else: + if x._set_fn_needs_length: + x._set_fn = functools.partial(definition.set_fn, length=x._bitlength) + else: + x._set_fn = definition.set_fn + x._get_fn = definition.get_fn + x._return_type = definition.return_type + x._is_signed = definition.is_signed + x._set_scale(scale) + return x + + def build(self, value: Any, /) -> bitstring.Bits: + """Create a bitstring from a value. + + The value parameter should be of a type appropriate to the dtype. + """ + b = bitstring.Bits() + self._set_fn(b, value) + if self.bitlength is not None and len(b) != self.bitlength: + raise ValueError(f"Dtype has a length of {self.bitlength} bits, but value '{value}' has {len(b)} bits.") + return b + + def parse(self, b: BitsType, /) -> Any: + """Parse a bitstring to find its value. + + The b parameter should be a bitstring of the appropriate length, or an object that can be converted to a bitstring.""" + b = bitstring.Bits._create_from_bitstype(b) + return self._get_fn(bitstring.Bits(b)) + + def __str__(self) -> str: + if self._scale is not None: + return self.__repr__() + hide_length = self._variable_length or dtype_register.names[self._name].allowed_lengths.only_one_value() or self._length is None + length_str = '' if hide_length else str(self._length) + return f"{self._name}{length_str}" + + def __repr__(self) -> str: + hide_length = self._variable_length or dtype_register.names[self._name].allowed_lengths.only_one_value() or self._length is None + length_str = '' if hide_length else ', ' + str(self._length) + if self._scale is None: + scale_str = '' + else: + try: + # This will only succeed for powers of two from -127 to 127. + e8m0 = bitstring.Bits(e8m0mxfp=self._scale) + except ValueError: + scale_str = f', scale={self._scale}' + else: + power_of_two = e8m0.uint - 127 + if power_of_two in [0, 1]: + scale_str = f', scale={self._scale}' + else: + scale_str = f', scale=2 ** {power_of_two}' + return f"{self.__class__.__name__}('{self._name}'{length_str}{scale_str})" + + def __eq__(self, other: Any) -> bool: + if isinstance(other, Dtype): + return self._name == other._name and self._length == other._length + return False + + +class AllowedLengths: + def __init__(self, value: Tuple[int, ...] = tuple()) -> None: + if len(value) >= 3 and value[-1] is Ellipsis: + step = value[1] - value[0] + for i in range(1, len(value) - 1): + if value[i] - value[i - 1] != step: + raise ValueError(f"Allowed length tuples must be equally spaced when final element is Ellipsis, but got {value}.") + self.values = (value[0], value[1], Ellipsis) + else: + self.values = value + + def __str__(self) -> str: + if self.values and self.values[-1] is Ellipsis: + return f"({self.values[0]}, {self.values[1]}, ...)" + return str(self.values) + + def __contains__(self, other: Any) -> bool: + if not self.values: + return True + if self.values[-1] is Ellipsis: + return (other - self.values[0]) % (self.values[1] - self.values[0]) == 0 + return other in self.values + + def only_one_value(self) -> bool: + return self.values and len(self.values) == 1 + + +class DtypeDefinition: + """Represents a class of dtypes, such as uint or float, rather than a concrete dtype such as uint8. + Not (yet) part of the public interface.""" + + def __init__(self, name: str, set_fn, get_fn, return_type: Any = Any, is_signed: bool = False, bitlength2chars_fn=None, + variable_length: bool = False, allowed_lengths: Tuple[int, ...] = tuple(), multiplier: int = 1, description: str = ''): + + # Consistency checks + if int(multiplier) != multiplier or multiplier <= 0: + raise ValueError("multiplier must be an positive integer") + if variable_length and allowed_lengths: + raise ValueError("A variable length dtype can't have allowed lengths.") + if variable_length and set_fn is not None and 'length' in inspect.signature(set_fn).parameters: + raise ValueError("A variable length dtype can't have a set_fn which takes a length.") + + self.name = name + self.description = description + self.return_type = return_type + self.is_signed = is_signed + self.variable_length = variable_length + self.allowed_lengths = AllowedLengths(allowed_lengths) + + self.multiplier = multiplier + + # Can work out if set_fn needs length based on its signature. + self.set_fn_needs_length = set_fn is not None and 'length' in inspect.signature(set_fn).parameters + self.set_fn = set_fn + + if self.allowed_lengths.values: + def allowed_length_checked_get_fn(bs): + if len(bs) not in self.allowed_lengths: + if self.allowed_lengths.only_one_value(): + raise bitstring.InterpretError(f"'{self.name}' dtypes must have a length of {self.allowed_lengths.values[0]}, but received a length of {len(bs)}.") + else: + raise bitstring.InterpretError(f"'{self.name}' dtypes must have a length in {self.allowed_lengths}, but received a length of {len(bs)}.") + return get_fn(bs) + self.get_fn = allowed_length_checked_get_fn # Interpret everything and check the length + else: + self.get_fn = get_fn # Interpret everything + + # Create a reading function from the get_fn. + if not self.variable_length: + if self.allowed_lengths.only_one_value(): + def read_fn(bs, start): + return self.get_fn(bs[start:start + self.allowed_lengths.values[0]]) + else: + def read_fn(bs, start, length): + if len(bs) < start + length: + raise bitstring.ReadError(f"Needed a length of at least {length} bits, but only {len(bs) - start} bits were available.") + return self.get_fn(bs[start:start + length]) + self.read_fn = read_fn + else: + # We only find out the length when we read/get. + def length_checked_get_fn(bs): + x, length = get_fn(bs) + if length != len(bs): + raise ValueError + return x + self.get_fn = length_checked_get_fn + + def read_fn(bs, start): + try: + x, length = get_fn(bs[start:]) + except bitstring.InterpretError: + raise bitstring.ReadError + return x, start + length + self.read_fn = read_fn + self.bitlength2chars_fn = bitlength2chars_fn + + def get_dtype(self, length: Optional[int] = None, scale: Union[None, float, int] = None) -> Dtype: + if self.allowed_lengths: + if length is None: + if self.allowed_lengths.only_one_value(): + length = self.allowed_lengths.values[0] + else: + if length not in self.allowed_lengths: + if self.allowed_lengths.only_one_value(): + raise ValueError(f"A length of {length} was supplied for the '{self.name}' dtype, but its only allowed length is {self.allowed_lengths.values[0]}.") + else: + raise ValueError(f"A length of {length} was supplied for the '{self.name}' dtype which is not one of its possible lengths (must be one of {self.allowed_lengths}).") + if length is None: + d = Dtype._create(self, None, scale) + return d + if self.variable_length: + raise ValueError(f"A length ({length}) shouldn't be supplied for the variable length dtype '{self.name}'.") + d = Dtype._create(self, length, scale) + return d + + def __repr__(self) -> str: + s = f"{self.__class__.__name__}(name='{self.name}', description='{self.description}', return_type={self.return_type.__name__}, " + s += f"is_signed={self.is_signed}, set_fn_needs_length={self.set_fn_needs_length}, allowed_lengths={self.allowed_lengths!s}, multiplier={self.multiplier})" + return s + + +class Register: + """A singleton class that holds all the DtypeDefinitions. Not (yet) part of the public interface.""" + + _instance: Optional[Register] = None + names: Dict[str, DtypeDefinition] = {} + + def __new__(cls) -> Register: + # Singleton. Only one Register instance can ever exist. + if cls._instance is None: + cls._instance = super(Register, cls).__new__(cls) + return cls._instance + + @classmethod + def add_dtype(cls, definition: DtypeDefinition): + cls.names[definition.name] = definition + if definition.get_fn is not None: + setattr(bitstring.bits.Bits, definition.name, property(fget=definition.get_fn, doc=f"The bitstring as {definition.description}. Read only.")) + if definition.set_fn is not None: + setattr(bitstring.bitarray_.BitArray, definition.name, property(fget=definition.get_fn, fset=definition.set_fn, doc=f"The bitstring as {definition.description}. Read and write.")) + + @classmethod + def add_dtype_alias(cls, name: str, alias: str): + cls.names[alias] = cls.names[name] + definition = cls.names[alias] + if definition.get_fn is not None: + setattr(bitstring.bits.Bits, alias, property(fget=definition.get_fn, doc=f"An alias for '{name}'. Read only.")) + if definition.set_fn is not None: + setattr(bitstring.bitarray_.BitArray, alias, property(fget=definition.get_fn, fset=definition.set_fn, doc=f"An alias for '{name}'. Read and write.")) + + @classmethod + def get_dtype(cls, name: str, length: Optional[int], scale: Union[None, float, int] = None) -> Dtype: + try: + definition = cls.names[name] + except KeyError: + raise ValueError(f"Unknown Dtype name '{name}'. Names available: {list(cls.names.keys())}.") + else: + return definition.get_dtype(length, scale) + + @classmethod + def __getitem__(cls, name: str) -> DtypeDefinition: + return cls.names[name] + + @classmethod + def __delitem__(cls, name: str) -> None: + del cls.names[name] + + def __repr__(self) -> str: + s = [f"{'key':<12}:{'name':^12}{'signed':^8}{'set_fn_needs_length':^23}{'allowed_lengths':^16}{'multiplier':^12}{'return_type':<13}"] + s.append('-' * 85) + for key in self.names: + m = self.names[key] + allowed = '' if not m.allowed_lengths else m.allowed_lengths + ret = 'None' if m.return_type is None else m.return_type.__name__ + s.append(f"{key:<12}:{m.name:>12}{m.is_signed:^8}{m.set_fn_needs_length:^16}{allowed!s:^16}{m.multiplier:^12}{ret:<13} # {m.description}") + return '\n'.join(s) + + +# Create the Register singleton +dtype_register = Register() diff --git a/code/.venv/lib/python3.12/site-packages/bitstring/exceptions.py b/code/.venv/lib/python3.12/site-packages/bitstring/exceptions.py new file mode 100644 index 0000000..374d393 --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/bitstring/exceptions.py @@ -0,0 +1,23 @@ + +class Error(Exception): + """Base class for errors in the bitstring module.""" + + def __init__(self, *params: object) -> None: + self.msg = params[0] if params else '' + self.params = params[1:] + + +class ReadError(Error, IndexError): + """Reading or peeking past the end of a bitstring.""" + + +InterpretError = ValueError +"""Inappropriate interpretation of binary data.""" + + +class ByteAlignError(Error): + """Whole-byte position or length needed.""" + + +CreationError = ValueError +"""Inappropriate argument during bitstring creation.""" diff --git a/code/.venv/lib/python3.12/site-packages/bitstring/fp8.py b/code/.venv/lib/python3.12/site-packages/bitstring/fp8.py new file mode 100644 index 0000000..575dbbb --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/bitstring/fp8.py @@ -0,0 +1,97 @@ +""" +The 8-bit float formats used here are from a proposal supported by Graphcore, AMD and Qualcomm. +See https://arxiv.org/abs/2206.02915 + +""" + +import struct +import zlib +import array +import bitarray +from bitstring.luts import binary8_luts_compressed +import math + + +class Binary8Format: + """8-bit floating point formats based on draft IEEE binary8""" + + def __init__(self, exp_bits: int, bias: int): + self.exp_bits = exp_bits + self.bias = bias + self.pos_clamp_value = 0b01111111 + self.neg_clamp_value = 0b11111111 + + def __str__(self): + return f"Binary8Format(exp_bits={self.exp_bits}, bias={self.bias})" + + def decompress_luts(self): + binary8_to_float_compressed, float16_to_binary8_compressed = binary8_luts_compressed[(self.exp_bits, self.bias)] + self.lut_float16_to_binary8 = zlib.decompress(float16_to_binary8_compressed) + dec = zlib.decompress(binary8_to_float_compressed) + self.lut_binary8_to_float = struct.unpack(f'<{len(dec) // 4}f', dec) + + def create_luts(self): + self.lut_binary8_to_float = self.createLUT_for_binary8_to_float() + self.lut_float16_to_binary8 = self.createLUT_for_float16_to_binary8() + + def float_to_int8(self, f: float) -> int: + """Given a Python float convert to the best float8 (expressed as an integer in 0-255 range).""" + # First convert the float to a float16, then a 16 bit uint + try: + b = struct.pack('>e', f) + except (OverflowError, struct.error): + # Return the largest representable positive or negative value + return self.pos_clamp_value if f > 0 else self.neg_clamp_value + f16_int = int.from_bytes(b, byteorder='big') + # Then use this as an index to our large LUT + return self.lut_float16_to_binary8[f16_int] + + def createLUT_for_float16_to_binary8(self) -> bytes: + # Used to create the LUT that was compressed and stored for the fp8 code + import gfloat + fi = gfloat.formats.format_info_p3109(8 - self.exp_bits) + fp16_to_fp8 = bytearray(1 << 16) + for i in range(1 << 16): + b = struct.pack('>H', i) + f, = struct.unpack('>e', b) + fp = gfloat.round_float(fi, f) + if math.isnan(fp): + fp8_i = 0b10000000 + else: + fp8_i = self.lut_binary8_to_float.index(fp) + fp16_to_fp8[i] = fp8_i + return bytes(fp16_to_fp8) + + def createLUT_for_binary8_to_float(self): + """Create a LUT to convert an int in range 0-255 representing a float8 into a Python float""" + i2f = [] + for i in range(256): + b = bitarray.util.int2ba(i, length=8, endian='big', signed=False) + sign = b[0] + exponent = bitarray.util.ba2int(b[1:1 + self.exp_bits]) + significand = b[1 + self.exp_bits:] + if exponent == 0: + significand = bitarray.bitarray('0') + significand + exponent = -self.bias + 1 + else: + significand = bitarray.bitarray('1') + significand + exponent -= self.bias + f = float(bitarray.util.ba2int(significand)) / (2.0 ** (7 - self.exp_bits)) + f *= 2 ** exponent + i2f.append(f if not sign else -f) + # One special case for minus zero + i2f[0b10000000] = float('nan') + # and for plus and minus infinity + i2f[0b01111111] = float('inf') + i2f[0b11111111] = float('-inf') + return array.array('f', i2f) + + +# We create the 1.5.2 and 1.4.3 formats. +p4binary_fmt = Binary8Format(exp_bits=4, bias=8) +p3binary_fmt = Binary8Format(exp_bits=5, bias=16) + + +def decompress_luts(): + p4binary_fmt.decompress_luts() + p3binary_fmt.decompress_luts() diff --git a/code/.venv/lib/python3.12/site-packages/bitstring/luts.py b/code/.venv/lib/python3.12/site-packages/bitstring/luts.py new file mode 100644 index 0000000..f4ca298 --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/bitstring/luts.py @@ -0,0 +1,245 @@ +# +# This file is generated by generate_luts.py. DO NOT EDIT. +# + +mxfp_luts_compressed = \ +{(2, 1, 1, 'saturate'): (b'x\x01\x1d\xc9\xc1\r\xc00\x00\xc2@o\xd6\x8c\xc6f\xf5h\xb1\x828\xf1\x00^>X\x0c\xa7f1,\x7f' + b'\x13\x83\xfdY\xf4\x027\xf1\x0c\xfb', + b"x\x01\xed\xdd\t\r\xc00\x10\x03\xc1\xf4\xff[\xfehS\x1a'y\x96\x81\x87\x80[\x13\x01\x02" + b'\x04\x08\x10 @\x80\x00\x01\x02\x04\x08\x10 @\x80\x00\x01\x02\x04\x08\x10 @\x80' + b'\x00\x01\x02\x04\x08\x10 @\x80\x00\x01\x02\x04\x08\x10 @\x80\x00\x01\x02\x04\x08\x10' + b' @\x80\x00\x01\x02e\x05\x06\x11 @\x80@\xa2\xc0(\x02\xc9\x02\x93\x92\x05fe\x0b,J\x16X\x95-' + b'\xb0\x89\x00\x01\x02\x04\x08\x10 @\x80\x00\x01\x02\x04\x08\x10 @\x80\x00\x01\x02\x04' + b'\x08\x10 @\x80\x00\x01\x02\x04\x08\x10 @\x80\x00\x01\x02\x04\x08\x10 @\x80\x00' + b'\x01\x02\x04\x08\x10 @\x80\x00\x01\x02\x04\x08\x14\x14\xe8"@ U`\x17\x01\x02\x04\x08\x10 ' + b'@\x80\x00\x01\x02\x04\x08\x10 @\x80\x00\x01\x02\x04\x08\x10 @\x80\x00\x01\x02\x04\x08\x10 @' + b'\x80\x00\x01\x02\x04\x08\x10 @\x80\x00\x01\x02\x04\x08\x10 @\x80\x00\x01\x02u\x05\x0e\x11 @' + b'\x80@\xa2\xc0)\x02\xc9\x02\x97\x92\x05ne\x0b\xb7\x9b\x00\x81\xfe\x03\\M8\xf2'), + (2, 3, 1, 'saturate'): (b'x\x01\x1d\xcdQ\r\xc30\x10\x03\xd0\x830\x08\x85P\x04[ \x14B \x0cB \x04B \x14\xc1v\x10' + b'\x02!\x10\x02aO\xb3\xf4\xe4?;\xe2\x9fgD#\x89W\xc4A\xa1\xd2\xe8\x0cn\x92\xc9b\x13%\xe2\xc1' + b'\xc1I\xe1\xa2\xf2\xa6\xd1\x19\xdc$\x93\xc5&\x1a\x1fE\x12_[\x14*\x8d\xce\xe0&\x99,6\x91\xfe98' + b')\\T\xde4:\x83\x9bd\xb2\xd8\xf9\x03~S=\xdd', + b'x\x01\xed\xdd\x85qB\x01\x14DQ\x12\x88\x11#\xeeF\x84\xb8\xbb\xf6_\x15\xa9\x82\xd9\xf9\xf3' + b'\xce\xed`O\x03\xdbj\x89\x00\x01\x02\x04\x08\x10 @\x80\x00\x01\x02\x04\x08\x10 @\x80\x00\x01' + b'\x02\x04\x08\x10 @\x80\x00\x01\x02\x04\x08\x10 @\x80\x00\x01\x02\x04\x08\x10 @' + b'\x80\x00\x81\xc6\x08\x8c\x89\x00\x01\x02\x04*\n\x8c\x8b@e\x81\xb6*\x0bt\x94\x15\x98\xa8\xddd' + b'\xf5\xa6j7\xdd\xf4f\xb2u\xd3\xcdf\x9bK7\x9fm!\xddb\xb6^\xba\xa5l\xcb\xe9V\xb2\xad\xa6[' + b'\xcb\xb6\x9en#\xdbf\xba\xadl\xdb\xe9v\xb2\xed\xa6\xdb\xcb\xb6\x9f\xee@\x04\x08\x10 @\x80' + b'\x00\x01\x02\x04\x08\x10 @\x80\x00\x01\x02\x04\x08\x10 @\x80\x00\x01\x02\x04\x08\x10' + b' @\x80\x00\x01\x02\x04\x08\x10 @\x80\x00\x01\x02\x04\x08\x10 @\x80\x00\x01\x02' + b'\x04\x08\x10\x18\x91\xc0P\x04\x08T\x158\x14\x01\x02\x04\x08\x10 @\x80\x00\x01\x02' + b'\x04\x08\x10 @\x80\x00\x01\x02\x04\x08\x10 @\x80\x00\x01\x02\x04\x08\x10 @\x80' + b'\x00\x01\x02\x04\x08\x10 @\x80\x00\x01\x02\xcd\x118\x12\x01\x02\x04\x08T\x14\xe8\x8b@e\x81c' + b'U\x168QV\xe0\xb4vg\xd5\x1b\xd4\xee\xbc\xe9]d\xbbLw\x95\xed:\xddM\xb6\xdbtw\xd9\xee\xd3=d{L' + b'\xf7\x94\xed9\xddK\xb6\xd7to\xd9\xde\xd3}d\xfbL\xf7\x95\xed;\xddO\xb6\xdft\x7f"@\x80\x00\x01' + b'\x02\x04\x08\x10 @\x80\x00\x01\x02\x04\x08\x10 @\x80\x00\x01\x02\x04\x08\x10 @' + b'\x80\x00\x01\x02\x04\x08\x10 @\x80\x00\x01\x02\x04\x08\x10 @\x80\x00\x01\x02\x04\x08' + b'\x10 @\x80\xc0\x88\x04\xaa\x1e\x9f\xdbM\x80\xc0\xf0\x1f\x9f\x0cK\xfb'), + (3, 2, 3, 'saturate'): (b'x\x01\x15\xcbA\x15BQ\x08\x05@\xa2XD\xc5&/\nQ(\xa2\xf2\x9b\x10\xc5\x91\xc3\xc0\xe2B\xc4\xbf' + b'\xean<"\x92\xa2\x19\x96xF\xdcH\x0eE3,\x91r\x92C\xd1\x0cK\xbc\xe4$\x87\xa2\x19\x96(\xfd' + b'\xb6?n(\x9aa\x89\xaf\x7f\x92C\xd1\x0cK\x8c\x9c\xe4P4\xc3\x12\x97\x9c\xe4P4\xc3^?\xc7\x8a;c', + b'x\x01\xed\xdd\xd7U\x15\x00\x00\x04Q$\x89HV@r\x90 Q\xb2\x92\xfb\xaf\xea\xd1\x02\x7f' + b'\x1c\xce\xdc\xe9`o\x03;4$\x02\x04\x08\x10 @\x80\x00\x01\x02\x04\x08\x10 @\x80\x00\x01' + b'\x02\x04\x08\x10 @\x80\x00\x01\x02\x04\x08\x10 @\x80\x00\x01\x02\x04\x08\x10 @\xe0\xd3\t|' + b'\x11\x01\x02\x04\x08\x14\x05\x86E\xa0,0\xa2\xb2\xc0\xa8>V`\xac\xddx\xbd\xaf\xed&\xea}k7Y\xef' + b'{\xbb\xa9z\xd3\xedf\xea\xcd\xb6\x9b\xab7\xdfn\xa1\xde\x8fv?\xeb-\xb6[\xaa\xb7\xdc\xee' + b'W\xbd\x95v\xab\xf5\xd6\xda\xad\xd7\xdbh\xb7YoK\x04\x08\x10 @\x80\x00\x01\x02\x04\x08\x10' + b' @\x80\x00\x01\x02\x04\x08\x10 @\x80\x00\x01\x02\x04\x08\x10 @\x80\x00\x01\x02\x04\x08\x10 ' + b'@\x80\x00\x01\x02\x04\x08\x10 \xf0.\x81\x81\x08\x10\xa8\nl\x8b\x00\x01\x02\x04\x08\x10 @\x80' + b'\x00\x01\x02\x04\x08\x10 @\x80\x00\x01\x02\x04\x08\x10 @\x80\x00\x01\x02\x04\x08\x10' + b' @\x80\x00\x01\x02\x04\x08\x10\xf8|\x02;"@\x80\x00\x81\xa2\xc0\xae\x08\x94\x05~\xab,\xb0' + b'\xa7\x8f\x15\xd8owP\xef\xb0\xdd\x9fzG\xed\x8e\xeb\x9d\xb4;\xadw\xd6\xee\xbc\xde\xdfv\x17' + b'\xf5.\xdb]\xd5\xbbnwS\xef\xb6\xdd]\xbd\x7f\xed\xfe\xd7\xbbo\xf7P\xef\xb1\xddS\xbd\xe7v/\xf5^' + b'E\x80\x00\x01\x02\x04\x08\x10 @\x80\x00\x01\x02\x04\x08\x10 @\x80\x00\x01\x02\x04\x08\x10 @' + b'\x80\x00\x01\x02\x04\x08\x10 @\x80\x00\x01\x02\x04\x08\x10 @\x80\x00\x01\x02\xef\x12' + b'\xa8\x1e\x9f\xdbM\x80\xc0\xe0\r\xd9\xf2;{'), + (4, 3, 7, 'saturate'): (b'x\x01\x1d\xcd[\xb5\x90!\x10\x80Q"\x18\x81\x08\xbc{C-@\x04"\xd0\xe0\x10\x81\x08D\xe0\xdd\x1bj' + b'\x01"\x10\xe1o\xa0\xfb\xc8Z{\xcd\xbc|C\x08\xff\xdf\xdb\x10:\x9b\xf0.\x84H\xa6\xd2\x19' + b'L\x16\x9b\xc3\xe5!\xbc\x0f\xe1\r\x91D\xa6Pit\x06\x93\xc5\xe6py\x08\x1f\xf4D\x12\x99' + b'B\xa5\xd1\x19L\x16\x9b\xc3\xe5!|\xd4\x13Id\n\x95Fg0Yl\x0e\x97\x87\x90\xf5D\x12\x99B\xa5' + b'\xd1\x19L\x16\x9b\xc3\xe5!|\xd2\x13Id\n\x95Fg0Yl\x0e\x97\x87\xf0YO$\x91)T\x1a\x9d\xc1d\xb19' + b'\\\x1e\xc2\x17=\x91D\xa6Pit\x06\x93\xc5\xe6p_\xf7\x17}\xe7\xab\xc1&|s\x8bL\xa53\x98,' + b'6\x87\xcbC\xf8\xee\x7f"\x89L\xa1\xd2\xe8\x0c&\x8b\xcd\xe1\xf2\x10~\xe8\x89$2\x85J\xa33\x98,6' + b"\x87\xcbC\xf8\xa9'\x92\xc8\x14*\x8d\xce`\xb2\xd8\x1c.\x0fa\xeb\x89$2\x85J\xa33\x98,6\x87\xcb" + b'C\xf8\xa5\'\x92\xc8\x14*\x8d\xce`\xb2\xd8\x1c.\x0f\xe1\xb7\x9eH"S\xa84:\x83\xc9bs\xb8<\x84' + b'?z"\x89L\xa1\xd2\xe8\x0c&\x8b\xcd\xe1\xbe\xee\x7f\xff\x01!\xba\xf7\x9b', + b'x\x01\xed\xdd\xd5\x96\x90\x05\x00E\xe1\x01\xe9\x90n\x90\x96F\xbaE\xa4S@\xba\x15\xa4\xbbKX H*' + b'\xdd)\xa9\x12Jw*H\x08Jww\x87\xd25\\\xccC\xec5\xf3\xef\xef\r\xce~\x81\x13\x12"\x0bX' + b'\xc0\x02\x16\xb0\x80\x05,`\x01\x0bX\xc0\x02\x16\xb0\x80\x05,`\x01\x0bX \xc2\x16\x88$\x0b' + b'X\xc0\x02\x16\x08b\x81\xc8\xb2@\x90\x0b|\xa0 \x17\x88"\xb6@\xd4`\x8b\x16t\xd1\x83-Fx\x17\x93' + b'\x15\x8b\x16\x9b\x15\x87\x16\x97\xf5!-\x1e+>-\x01+!-\x11+1-\t+)-\x19+9-\x05+%-\x15+5-\r' + b'\xeb#ZZV:ZzV\x06ZFV&Zf\xd6\xc7\xb4,\xac\xac\xb4l\xac\xec\xb4\x1c\xac\x9c\xb4\\\xac\xdc\xb4O' + b'XyhyY\xf9h\xf9Y\x05h\x05Y\x85h\x85YEhEY\xc5h\xc5Y%h\x9f\xb2J\xd2>c\x95\xa2}\xce*M+\xc3*K+' + b'\xc7*O\xab\xc0\xaaH\xab\xc4\xaaL\xab\xc2\xaaJ\xab\xc6\xfa\x82V\x9dU\x83V\x93\xf5%\xad' + b'\x16\xab6\xad\x0e\xab.\xad\x1e\xab>\xad\x01\xab!\xad\x11\xab1\xad\t\xab)\xad\x19\xeb+\xda' + b'\xd7\xac\xe6\xb4\x16\xacoh-Y\xadh\xadYmhmY\xedh\xedY\x1dh\x1dY\x9dh\x9dY]h]Y\xddh\xddY=h' + b'=Y\xbdh\xbdY}h}Y\xfdh\xfdY\xdf\xd2\x06\xb0\x06\xca\x02\x16\xb0\x80\x05,`\x01\x0bX\xc0\x02' + b'\x16\xb0\x80\x05,`\x01\x0bX\xc0\x02\x16\xb0\x80\x05,`\x01\x0bX\xc0\x02\x16\xb0\x80\x05\xc2\n' + b'\x84\xca\x02\x16\x08j\x81A\xb2\x80\x05,`\x01\x0bX\xc0\x02\x16\xb0\x80\x05,`\x01\x0bX\xc0' + b'\x02\x16\xb0\x80\x05"n\x81\xc1\xb2\x80\x05,`\x81 \x16\xf8N\x16\x08r\x81!\nr\x81\xa1' + b'b\x0b|\x1fl\xc3\x82nx\xb0\x8d\x08\xefF\xb2F\xd1F\xb3~\xa0\xfd\xc8\x1aC\x1b\xcb\x1a' + b'G\x1b\xcf\x9a@\x9b\xc8\x9aD\x9b\xcc\x9aB\x9b\xca\x9aF\x9b\xce\x9aA\x9b\xc9\x9aE\x9b\xcd\x9a' + b'C\x9b\xcb\xfa\x896\x8f5\x9f\xb6\x80\xb5\x90\xb6\x88\xb5\x98\xf63\xeb\x17\xda\xaf\xac' + b'%\xb4\xa5\xace\xb4\xe5\xac\xdfh\xbf\xb3V\xd0V\xb2V\xd1V\xb3\xd6\xd0\xd6\xb2\xd6\xd1\xd6\xb3' + b'6\xd06\xb26\xd16\xb3\xb6\xd0\xb6\xb2\xb6\xd1\xb6\xb3v\xd0v\xb2\xfe\xa0\xfd\xc9\xdaE\xdb\xcd' + b'\xfa\x8b\xb6\x87\xb5\x97\xb6\x8f\xb5\x9f\xf67\xeb\x00\xed \xeb\x1f\xda\xbf\xacC\xb4\xc3' + b'\xac#\xb4\xa3\xacc\xb4\xe3\xac\x13\xb4\x93\xacS\xb4\xd3\xac3\xb4\xb3\xacs\xb4\xf3' + b'\xac\x0b\xb4\x8b\xacK\xb4\xcb\xac+\xb4\xab\xack\xb4\xeb\xac\x1b\xb4\x9b\xac[\xb4\xdb' + b'\xac;\xb4\xbb\xac{\xb4\xfb\xac\x07\xb4\x87\xacG\xb4\xc7\xac\xffh\xff\xb3\x9e\xd0\x9e' + b'\xb2\x9e\xd1\x9e\xb3^\xd0^\xb2^\xd1^\xb3\xde\xd0\xde\xb2\xde\xc9\x02\x16\xb0\x80\x05' + b',`\x01\x0bX\xc0\x02\x16\xb0\x80\x05,`\x01\x0bX\xc0\x02\x16\xb0\x80\x05,`\x01\x0bX\xc0' + b'\x02\x16\xb0\x80\x05\xc2\n\x04\xf5\xf8\xdc\xdd\x16\xb0@\xe8{\t?\xc8\x90'), + (5, 2, 15, 'saturate'): (b'x\x01M\xcb\xd9\x11\x10T\x0c@\xd1\x94B\x17 \x08\x06Dh#\x9d\x90R\xd2\x05\xca\x1a\x17' + b'\xa0\x8dt\x02\xc7\x1f\xc7\xcc\x9c\xf7\xf1n\x12\xf1\xef\xf4C\xcf\xa3\x88\xa4\x19\x96#~\x8ax@' + b"R4\xc3r\xc4c\x9d\xa4h\x86\xe5\x88':I\xd1\x0c\xcb\x11?\xeb$E3,G<\xd5I\x8afX\x8ex\xa6\x93" + b'\x14\xcd\xb0\x1c\xf1\x8bNR4\xc3rD\xea$E3,G<\xd7I\x8afX\x8ex\xa1\x93\x14\xcd\xb0\x1c' + b'\xf1\xabNR4\xc3r\xc4K\x9d\xa4h\x86\xe5\x88\xdft\x92\xa2\x19\x96#^\xe9$E3,G\xbc\xd6I' + b'\x8a~\xa3\xfdO\xb4\xbf\xb7\xf6~\xb7C3,G\xfc\xe1\x9e\xa4h\x86\xe5\x88w:I\xd1\x0c' + b'\xcb\x11\xefu\x92\xa2\x19\x96#>\xe8$E3,G|\xd4I\x8afX\x8e\xf8\xa4\x93\x14\xcd\xb0\x1c\xf1Y' + b"')\x9aa9bu\x92\xa2\x19\x96#\xfe\xd4I\x8afX\x8e\xf8K')\x9aa9\xe2o\x9d\xa4h\x86\xe5\x88\x7ft" + b'\x92\xa2\x19\x96#\xbe\xe8$E3,G|\xd5I\x8afX\x8e\xf8\xa6\x93\x14\xfd]\xfb\xcf\x0f' + b'\xd2\x15\xf0\xcf', + b'x\x01\xed\xddC\xa2\x1c\x00\x00D\xc1\xd8\xb6m\xdb\xb6m\xdb66\xb1m\xdb\xb6m\xdb\xb6m' + b',\x92c\xfcEM\xdd\xa0\xdf\x05:X\xb0 \x16\xdc\x16B\x17\xd2\x16J\x17\xda\x16F\x17\xd6' + b'\x16N\x17\xde\x16A\x17\xd1\x16I\x17\xd9\x16E\x17\xd5\x16M\x17\xdd\x16C\x17\xd3\x16K\x17\xdb' + b'\x16G\x17\xd7\x16O\x17\xdf\x96@\x97\xd0\x96H\x97\xd8\x96D\x97\xd4\x96L\x97\xdc\x96B\x97\xd2' + b'\x96J\x97\xda\x96F\x97\xd6\x96N\x97\xde\x96A\x97\xd1\x96I\x97\xd9\x96E\x97\xd5\x96M\x97\xdd' + b'\x96C\x97\xd3\x96K\x97\xdb\x96G\x97\xd7\x96O\x97\xdfV@W\xd0VHW\xd8VDW\xd4VLW\xdcVBW\xd2' + b'VJW\xdaVFW\xd6VNW\xdeVAW\xd1VIW\xd9VEW\xd5VMW\xddVCW\xd3VKW\xdbVGW\xd7VOW\xdf\xd6@\xd7\xd0' + b'\xd6H\xd7\xd8\xd6D\xd7\xd4\xd6L\xd7\xdc\xd6B\xd7\xd2\xd6J\xd7\xda\xd6F\xd7\xd6\xd6N\xd7\xde' + b'\xd6A\xd7\xd1\xd6I\xd7\xd9\xd6E\xd7\xd5\xd6M\xd7\xdd\xd6C\xd7\xd3\xd6K\xd7\xdb\xd6G\xd7\xd7' + b'\xd6O\xd7\xdf6@70 H\x0b\xfc\x0b\x08\x14\x08\x14P\x0b\x0c\nj\x83mCtCm\xc3t\xc3m#t#m' + b'\xa3t\xa3mctcm\xe3t\xe3m\x13t\x13m\x93t\x93mStSm\xd3t\xd3m3t3m\xb3t\xb3mstsm\xf3t\xf3m' + b'\x0bt\x0bm\x8bt\x8bmKtKm\xcbt\xcbm+t+m\xabt\xabmktkm\xebt\xebm\x1bt\x1bm\x9bt\x9bm[t[m' + b"\xdbt\xdbm;t;m\xbbt\xbbm{t{m\xfbt\xfbm\x07t\x07m\x87t\x87mGtGm\xc7t\xc7m't'm\xa7t\xa7mgtgm" + b'\xe7t\xe7m\x17t\x17m\x97t\x97mWtWm\xd7t\xd7m7t7m\xb7t\xb7mwtwm\xf7t\xf7m\x0ft\x0fm' + b'\x8ft\x8fmOtOm\xcft\xcfm/t/m\xaft\xafmotom\xeft\xefm\x1ft\x1fm\x9ft\x9fm_t_m\xdft\xdfm?t?m' + b'\xbft\xbfm\x7ft\x7f\x03\x82\xb4\x80z|\x1e\xd8\x1d(\x10(\xf0\xef?\xe6\xfc\r\x9b'), + (4, 3, 7, 'overflow'): (b'x\x01\x1d\xcd[\xb5\x90!\x10\x80Q"\x18\x81\x08\xbc{C-@\x04"\xd0\xe0\x10\x81\x08D\xe0\xdd\x1bj' + b'\x01"\x10\xe1o\xa0\xfb\xc8Z{\xcd\xbc|C\x08\xff\xdf\xdb\x10:\x9b\xf0.\x84H\xa6\xd2\x19' + b'L\x16\x9b\xc3\xe5!\xbc\x0f\xe1\r\x91D\xa6Pit\x06\x93\xc5\xe6py\x08\x1f\xf4D\x12\x99' + b'B\xa5\xd1\x19L\x16\x9b\xc3\xe5!|\xd4\x13Id\n\x95Fg0Yl\x0e\x97\x87\x90\xf5D\x12\x99B\xa5' + b'\xd1\x19L\x16\x9b\xc3\xe5!|\xd2\x13Id\n\x95Fg0Yl\x0e\x97\x87\xf0YO$\x91)T\x1a\x9d\xc1d\xb19' + b'\\\x1e\xc2\x17=\x91D\xa6Pit\x06\x93\xc5\xe6p_\xf7\x17}\xe7\xab\xc1&|s\x8bL\xa53\x98,' + b'6\x87\xcbC\xf8\xee\x7f"\x89L\xa1\xd2\xe8\x0c&\x8b\xcd\xe1\xf2\x10~\xe8\x89$2\x85J\xa33\x98,6' + b"\x87\xcbC\xf8\xa9'\x92\xc8\x14*\x8d\xce`\xb2\xd8\x1c.\x0fa\xeb\x89$2\x85J\xa33\x98,6\x87\xcb" + b'C\xf8\xa5\'\x92\xc8\x14*\x8d\xce`\xb2\xd8\x1c.\x0f\xe1\xb7\x9eH"S\xa84:\x83\xc9bs\xb8<\x84' + b'?z"\x89L\xa1\xd2\xe8\x0c&\x8b\xcd\xe1\xbe\xee\x7f\xff\x01!\xba\xf7\x9b', + b'x\x01\xed\xdc\x05\xb2\x90\x05\x00E\xe1\x87\x80\xa4t\x83tw\x97Jw\x0bHKw\x83\x80\x92Cww7' + b'\x92\xd2\x1dRJww\x97\xa4\x92J\xba\x8c3\x8f\xff|\x1b\xb83g\x017$D\x16\xb0\x80\x05,`\x01' + b'\x0bX\xc0\x02\x16\xb0\x80\x05,`\x01\x0bX\xc0\x02\x16\xb0\xc0g[ \x8c,`\x01\x0bX ' + b'\x88\x05\xbe\x90\x05\x82\\ \xac\x82\\ \x9c\xd8\x02\xe1\x83\xed\xcb\xa0\x8b\x10l\x11' + b'C\xbbH\xac\xc8\xb4(\xac\xa8\xb4\xafX\xd1h\xd1Y1h1Y\xb1h\xb1YqhqY\xf1h\xf1Y\th\tY\x89h\x89Y' + b'Ih_\xb3\x92\xd2\x92\xb1\x92\xd3R\xb0R\xd2R\xb1R\xd3\xd2\xb0\xd2\xd2\xd2\xb1\xd2\xd32\xb0' + b'2\xd22\xb12\xd3\xb2\xb0\xb2\xd2\xb2\xb1\xb2\xd3r\xb0r\xd2r\xb1r\xd3\xf2\xb0\xf2\xd2\xf2\xb1' + b'\xf2\xd3\n\xb0\n\xd2\xbea}K\xfb\x8eU\x88V\x98U\x84V\x94U\x8cV\x9cU\x82V\x92U\x8aV\x9a' + b'U\x86V\x96U\x8eV\x9eU\x81V\x91U\x89V\x99U\x85\xf6=\xab*\xad\x1a\xab:\xed\x07V\rZMV-ZmV\x1dZ]' + b'V=\xda\x8f\xac\xfa\xb4\x06\xac\x86\xb4F\xac\xc6\xb4&\xac\xa6\xb4f\xac\xe6\xb4\x16' + b'\xac\x96\xb4V\xac\xd6\xb46\xac\xb6\xb4v\xac\xf6\xb4\x0e\xac\x8e\xb4N\xac\xce\xb4\x9fX]h]' + b'Y\xddh?\xb3~\xa1ug\xf5\xa0\xf5d\xf5\xa2\xf5f\xf5\xa1}\x92\x05,`\x01\x0bX\xc0\x02\x16\xb0\x80' + b'\x05,`\x01\x0bX\xc0\x02\x16\xb0\x80\x05,`\x01\x0bX\xc0\x02\x16\xb0\x80\x05,`\x01\x0bX' + b'\xc0\x02\x16`\x0b\xf4\x95\x05,`\x01\x0bX\xc0\x02\x16\xb0\x80\x05,`\x01\x0bX\xc0\x02\x16\xb0' + b"\x80\x05,\xf0\xf9\x16\xe8'\x0bX\xc0\x02\x16\x08b\x81\xfe\xb2@\x90\x0b\x0cP\x90\x0b\x0c\x14[" + b'`P\xb0\r\x0e\xba!\xc164\xb4\x1b\xc6\x1aN\x1b\xc1\x1aI\x1b\xc5\x1aM\x1b\xc3\x1aK\x1b' + b'\xc7\x1aO\x9b\xc0\x9aH\x9b\xc4\x9aL\x9b\xc2\x9aJ\x9b\xc6\x9aN\x9b\xc1\x9aI\x9b\xc5\x9aM\x9b' + b'\xc3\x9aK\x9b\xc7\x9aO[\xc0ZH[\xc4\xfa\x95\xb6\x98\xb5\x84\xb6\x94\xb5\x8c\xb6' + b'\x9c\xb5\x82\xf6\x1bk%m\x15k5m\rk-m\x1dk=m\x03k#m\x13k3m\x0bk+m\x1bk;m\x07\xebw\xda' + b"N\xd6.\xdan\xd6\x1e\xda^\xd6\x1f\xb4?Y\xfbh\xfbY\x07h\x07Y\x87h\x87YGhGY\xc7h\xc7Y'h'Y\xa7h" + b'\xa7YghgY\xe7h\xe7Y\x17h\x17Y\x97h\x97YWhWY\xd7h\xd7Y7h7Y\xb7h\xb7YwhwY\xf7h\xf7Y\x0fh' + b'\x7f\xb1\x1e\xd2\x1e\xb1\x1e\xd3\x9e\xb0\x9e\xd2\x9e\xb1\xfe\xa6\xfd\xc3zN{\xc1zI{\xc5zM' + b'{\xc3\xfa\x97\xf6\x1f\xeb-\xed\x1d\xeb=\xed\x03\xeb#\x8d\xbd\xefw\xdd\x02\x16\xb0\x80\x05,`' + b'\x01\x0bX\xc0\x02\x16\xb0\x80\x05,`\x01\x0bX\xc0\x02\x16\xb0\x80\x05,`\x01\x0bX\xc0\x02\x16' + b'\xb0\x80\x05,`\x01\x0b|\xfa\x1f\xb2\xf6b\xf1'), + (5, 2, 15, 'overflow'): (b'x\x01M\xcb\xd9\x11\x10T\x0c@\xd1\x94B\x17 \x08\x06Dh#\x9d\x90R\xd2\x05\xca\x1a\x17' + b'\xa0\x8dt\x02\xc7\x1f\xc7\xcc\x9c\xf7\xf1n\x12\xf1\xef\xf4C\xcf\xa3\x88\xa4\x19\x96#~\x8ax@' + b"R4\xc3r\xc4c\x9d\xa4h\x86\xe5\x88':I\xd1\x0c\xcb\x11?\xeb$E3,G<\xd5I\x8afX\x8ex\xa6\x93" + b'\x14\xcd\xb0\x1c\xf1\x8bNR4\xc3rD\xea$E3,G<\xd7I\x8afX\x8ex\xa1\x93\x14\xcd\xb0\x1c' + b'\xf1\xabNR4\xc3r\xc4K\x9d\xa4h\x86\xe5\x88\xdft\x92\xa2\x19\x96#^\xe9$E3,G\xbc\xd6I' + b'\x8a~\xa3\xfdO\xb4\xbf\xb7\xf6~\xb7C3,G\xfc\xe1\x9e\xa4h\x86\xe5\x88w:I\xd1\x0c' + b'\xcb\x11\xefu\x92\xa2\x19\x96#>\xe8$E3,G|\xd4I\x8afX\x8e\xf8\xa4\x93\x14\xcd\xb0\x1c\xf1Y' + b"')\x9aa9bu\x92\xa2\x19\x96#\xfe\xd4I\x8afX\x8e\xf8K')\x9aa9\xe2o\x9d\xa4h\x86\xe5\x88\x7ft" + b'\x92\xa2\x19\x96#\xbe\xe8$E3,G|\xd5I\x8afX\x8e\xf8\xa6\x93\x14\xfd]\xfb\xcf\x0f' + b'\xd2\x15\xf0\xcf', + b'x\x01\xed\xddS\xb6\x1e\x06\x00\x85\xd1\x1b\xdb\xb6\x1b\xdb\xb6\x9b\x06\x8dm\xdbz\x88' + b"m\xabI\x1b\xa3\xb1m\xdb\xb6mg\xad\x9ba\xdc\x87\xfd\xef\x19\x9co\x02'((\x84\x85\xb2" + b'\x85\xd6\x85\xb1\x85\xd5\x85\xb3\x85\xd7E\xb0E\xd4E\xb2E\xd6E\xb1E\xd5E\xb3E\xd7\xc5\xb0' + b'\xc5\xd4\xc5\xb2\xc5\xd6\xc5\xb1\xc5\xd5\xc5\xb3\xc5\xd7%\xb0%\xd4%\xb2%\xd6%\xb1%\xd5%\xb3' + b'%\xd7\xa5\xb0\xa5\xd4\xa5\xb2\xa5\xd6\xa5\xb1\xa5\xd5\xa5\xb3\xfd\xa1Ko\xcb\xa0\xcbh' + b'\xcb\xa4\xcbl\xcb\xa2\xcbj\xcb\xa6\xcbn\xcb\xa1\xcbi\xcb\xa5\xcbm\xcb\xa3\xcbk\xcb\xa7\xcbo' + b'+\xa0+h+\xa4+l+\xa2+j+\xa6+n+\xa1+i+\xa5+m+\xa3+k+\xa7+o\xab\xa0\xabh\xab\xa4\xabl' + b'\xfbSW\xc5\xf6\x97\xae\xaa\xad\x9a\xae\xba\xad\x86\xeeo[M]-[m]\x1d[]]=[}]\x03[C]#[c]\x13' + b"[S]3[s]\x0b[K]+[k]\x1b[[];[{]\x07[G]'[g]\x17[W]7[w]\x0f[O]/[o]\x1f[_]?[\x7f\xdd\x00" + b'\xdb\xc0\x90\x16\x1c\x10(\x10(\xa0\x16\x18\x14\xd2\x06\xdb\x86\xe8\x86\xda\x86\xe9\x86\xdb' + b'F\xe8F\xdaF\xe9F\xdb\xc6\xe8\xc6\xda\xc6\xe9\xc6\xdb&\xe8&\xda&\xe9&\xdb\xa6\xe8\xa6\xda' + b'\xa6\xe9\xa6\xdbf\xe8f\xdaf\xe9f\xdb\xe6\xe8\xe6\xda\xe6\xe9\xfe\xb1\xcd\xd7-\xb0' + b'\xfd\xab\xfb\xcf\xb6P\xb7\xc8\xb6X\xb7\xc4\xb6T\xb7\xcc\xb6\\\xb7\xc2\xb6R\xb7\xca' + b'\xf6\xbfn\xb5m\x8dn\xadm\x9dn\xbdm\x83n\xa3m\x93n\xb3m\x8bn\xabm\x9bn\xbbm\x87n\xa7' + b'm\x97n\xb7m\x8fn\xafm\x9fn\xbf\xed\x80\xee\xa0\xed\x90\xee\xb0\xed\x88\xee\xa8' + b'\xed\x98\xee\xb8\xed\x84\xee\xa4\xed\x94\xee\xb4\xed\x8c\xee\xac\xed\x9c\xee\xbc' + b'\xed\x82\xee\xa2\xed\x92\xee\xb2\xed\x8a\xee\xaa\xed\x9a\xee\xba\xed\x86\xee\xa6' + b'\xed\x96\xee\xb6\xed\x8e\xee\xae\xed\x9e\xee\xbe\xed\x81\xee\xa1\xed\x91\xee\xb1' + b'\xed\x89\xee\xa9\xed\x99\xee\xb9\xed\x85\xee\xa5\xed\x95\xee\xb5\xed\x8d\xee\xad' + b'\xed\x9d\xee\xbd\xed\x83\xee\xa3\xed\x93\xee\xb3\xed\x8b\xee\xab\xed\x9b\xee\xbb' + b'\xed\x87\xee\xa7\xedWHS\x8f\xcf\x03\xbb\x03\x05\x02\x05\x82\x7f\x03\xb3\x87\x0e\x9d')} + + +binary8_luts_compressed = \ +{(4, 8): (b'x\x01\x15\xcb[\xd5P!\x10\x80Q"\x18\x81\x08<{E-@\x04"\xd0@"\x10\x81\x08<{E\xff\x02\'\x02\x11h\xa0\xdbY' + b'k\xcf\xcb\xcc\x17\xc2\xff\xe9\xaf\xad7!d:\x93\xcd!\xbc\r\xe1\x15\x91D\xa6Pit\x06\x93\xc5\xe6\xe1p\t\xef\xf4' + b'D\x12\x99B\xa5\xd1\x19L\x16\x9b\x87\xc3%\xbc\xd7\x13Id\n\x95Fg0Yl\x1e\x0e\x97\xf0AO$\x91)T\x1a\x9d\xc1d\xb1' + b"y8\\B\xd6\x13Id\n\x95Fg0Yl\x1e\x0e\x97\xf0QO$\x91)T\x1a\x9d\xc1d\xb1y8\\\xc2'=\x91D\xa6Pit\x06\x93" + b'\xc5\xe6\xe1p\t\x9f\xf5D\x12\x99B\xa5\x7f\xf1O\xff\xea\xef\x9b\x1b\x9d\xc9\xe6\x10\xbe\xeb\x89$2\x85J\xa3' + b'3\x98,6\x0f\x87K\xf8\xa1\'\x92\xc8\x14*\x8d\xce`\xb2\xd8<\x1c.\xe1\xa7\x9eH"S\xa84:\x83\xc9b\xf3p\xb8\x84_z' + b'"\x89L\xa1\xd2\xe8\x0c&\x8b\xcd\xc3\xe1\x12\xb6\x9eH"S\xa84:\x83\xc9b\xf3p\xb8\x84\xdfz"\x89L\xa1\xd2\xe8' + b'\x0c&\x8b\xcd\xc3\xe1\x12\xfe\xe8\x89$2\x85J\xa33\x98,6\x0f\x87Kx\xd1\x13Id\n\x95\xfe\xf7\x1f[)\xf3`', + b'x\x01\xed\xdd\x05\xba\x96\x05\x00\x05\xe1\x9f\xee\x06\xe9FZA\xa4\xbb\xbb;\xa4SB\xba\xeb\xd2\xdd\x8dt\x97' + b'\x92J(\xa14\xa2\x84\x92\x8a\xa4\x82\xd2\x1d\x12.c\x9e\xcb7\xef\x0e\xcel\xe0\x84B\xb2\x80\x05,`\x01' + b'\x0bX\xc0\x02\x16\xb0\x80\x05,`\x01\x0bX\xe0\xbd/\x10A\x16\xb0\x80\x05,\x10\xc4\x02\x11e\x81 \x17\x88' + b'\xa4 \x17\x88,\xb6@\x94`\x8b\x1at\xd1\x82-zx\x17\x83\x15\x93\x16\x8b\x15\x9b\x16\x87\x15\x97\x16\x8f\x15' + b'\x9f\x96\x80\x95\x90\x96\x88\x95\x98\x96\x84\xf5\x01-)+\x19-9+\x05-%+\x15-5+\r--+\x1d-=+\x03-#+\x13\xedCV' + b'fZ\x16VVZ6VvZ\x0eVN\xdaG\xac\x8fi\xb9X\xb9i\x9f\xb0\xf2\xd0>e\xe5\xa5\xe5c\xe5\xa7\x15`\x15\xa4\x15b' + b'\x15\xa6\x15a\x15\xa5\x15c\x15\xa7\x95`\x95\xa4\x95b\x95\xa6\x95a\x95\xa5\x95c\x95\xa7U`U\xa4UbU\xa6Ua' + b'U\xa5UcU\xa7\xd5`\xd5\xa4\xd5b\xd5\xa6\xd5a\xd5\xa5\xd5c\xd5\xa75`5\xa45b5\xa65a}Fk\xcajFk\xcejAk\xc9' + b"jEk\xcdjCk\xcbjGk\xcf\xea@\xfb\x9c\xd5\x91\xd6\x89\xd5\x99\xd6\x85\xf5\x05\xad+\xab\x1b\xad;\xab\x07\xad'" + b'\xab\x17\xad7\xab\x0f\xad/\xab\x1f\xad?k\x00m k\x10m0k\x08m\xa8,`\x01\x0bX\xc0\x02\x16\xb0\x80\x05,' + b'`\x01\x0bX\xc0\x02\x16\xb0\x80\x05,`\x01\x0bX\xc0\x02\x16\xb0\x80\x05,`\x01\x0bX\xc0\x02\x16\xb0\x00\\' + b' L\x16\xb0@P\x0b\xbc\xf7\xff\x86\x0e\xb4\x80\x05,`\x01\x0bX\xc0\x02\x16\xb0\x80\x05,`\x01\x0bX ' + b'\x14\x1a&\x0bX\xc0\x02\x16\x08b\x81\xe1\xb2@\x90\x0b\x8cP\x90\x0b\x8c\x14[`T\xb0\x8d\x0e\xba1\xc16' + b'6\xbc\x1b\xc7\x1aO\x9b\xc0\x9aH\x9b\xc4\x9aL\x9b\xc2\x9aJ\x9b\xc6\x9aN\x9b\xc1\x9aI\x9b\xc5\x9aM\x9b\xc3' + b'\x9aK\x9b\xc7\x9aO\xfb\x92\xb5\x80\xb6\x90\xb5\x88\xb6\x98\xb5\x84\xb6\x94\xb5\x8c\xb6\x9c\xb5\x82\xb6\x92' + b'\xb5\x8a\xb6\x9a\xb5\x86\xb6\x96\xb5\x8e\xb6\x9e\xb5\x81\xf6\x15\xebk\xdaF\xd6&\xdaf\xd6\x16\xdaV\xd66\xda7' + b'\xacoi\xdbY;h;Y\xbbh\xdf\xb1\xbe\xa7\xedf\xed\xa1\xede\xed\xa3\xfd\xc0\xfa\x91\xb6\x9fu\x80v\x90u\x88v' + b'\x98u\x84v\x94u\x8c\xf6\x13\xeb8\xedg\xd6/\xb4\x13\xac\x93\xb4S\xac\xd3\xb4_Y\xbf\xd1\xce\xb0\xce\xd2' + b'\xce\xb1\xce\xd3.\xb0.\xd2~g\xfdA\xbb\xc4\xfa\x93v\x99u\x85v\x95u\x8dv\x9du\x83\xf6\x17\xebo\xdaM\xd6-' + b'\xda?\xac\x7fi\xb7YwhwY\xf7h\xf7Y\x0fh\x0fY\x8fh\x8fYOhOY\xcfh\xcfY/h/Y\xafh\xff\xb1^\xd3\xde\xb0\xde' + b'\xd2\xde\xc9\x02\x16\xb0\x80\x05,`\x01\x0bX\xc0\x02\x16\xb0\x80\x05,`\x01\x0bX\xc0\x02\x16\xb0\x80\x05,`' + b'\x01\x0bX\xc0\x02\x16\xb0\x80\x05,`\x01\x0b\xc0\x05\x82z|\xeen\x0bX \xec\x7f\xc6\xe4\x02%'), + (5, 16): (b'x\x01\x1d\xc9\x89\r\x10\x04\x0c\x86\xd1\x8e\xc2\x16 \x97\x059\xd6\xe8&t\x94n\xa1\\RQ`\x8dn\x02/6yI' + b"\xf3\x7f\x11\xff\xdf\xc3\x88f\x89G\x11\x0fH\x8afX\x8e\xf8M')\x9aa9\xe2\xb1NR4\xc3r\xc4\x13\x9d\xa4h" + b'\x86\xe5\x88\xa7:I\xd1\x0c\xcb\x11\xcft\x92\xa2\x19\x96#\x9e\xeb$E3,G\xfc\xae\x93\x14\xcd\xb0\x1c\x91' + b":I\xd1\x0c\xcb\x11/t\x92\xa2\x19\x96#^\xea$E3,G\xfc\xa1\x93\x14\xcd\xb0\x1c\xf1J')\x9aa9\xe2\xb5NR4\xc3" + b'r\xc4\x1b\x9d\xa4h\x86\xe5\x88\xb7:I\xbf\xb3\x13\x7f\xfaY\xe2/\x9d\xa4h\x86\xe5\x88\xf7:I\xd1\x0c\xcb' + b'\x11\x1ft\x92\xa2\x19\x96#>\xea$E3,G|\xd2I\x8afX\x8e\xf8\xac\x93\x14\xcd\xb0\x1c\xf1\xb7NR4\xc3r' + b"\xc4\x17\x9d\xa4h\x86\xe5\x88\xd5I\x8afX\x8e\xf8G')\x9aa9\xe2\xabNR4\xc3r\xc4\xbf:I\xd1\x0c\xcb\x11" + b"\xff\xe9$E3,G|\xd3I\x8afX\x8e\xf8\xae\x93\x14\xcd\xb0\x1c\xf1C'\xe9\x9f\xbf\x00Gi\xed\x02", + b'x\x01\xed\xddU\xd6\x96\x05\x00\x85\xd1\x9f\x16\xa4\x14\x04\xe9P\xa4K\x1a\xe9\x14\x10\t\xe9\x0eQ' + b'\xba\xc1\xa0\xbb\xbb\xa5\x1b)%\xa5;%\xa5\xbb;%\x94\x8eAp\xf1.\xd6\xfe\xf6\x0c\xce3\x81\x13\x16\xf6' + b'\x8e\xc2\x05+|\xd0"\x04+b\xd0"\x05+r\xd0\xa2\x04\xeb\x83\xf7]T[4\xdd\x87\xb6\xe8\xba\x18\xb6\x98\xbaX\xb6' + b'\xd8\xba\x8fl\x1f\xeb\xe2\xd8\xe2\xea>\xb1\xc5\xd3\xc5\xb7}\xaaK`K\xa8KdK\xacKbK\xaaKfK\xaeKaK\xa9\xfb\xcc' + b'\xf6\xb9.\x95\xed\x0b]j[\x1a]Z[:]z[\x06]F[&]f[\x16]V\xdb\x97\xbal\xb6\xec\xba\x1c\xb6\x9c\xba\\' + b'\xb6\xdc\xba<\xb6\xbc\xba\xafl\xf9t\xf9m\x05t\x05m\x85t\x85mEtEm\xc5t\xc5m%t%m_\xebJ\xd9J\xeb\xca' + b'\xd8\xbe\xd1\x95\xb5}\xab+g+\xaf\xab`\xab\xa8\xfb\xceVIW\xd9VEW\xd5VMW\xddVCW\xd3VKW\xdbVGW\xd7VOW' + b'\xdf\xd6@\xd7\xd0\xf6\xbd\xae\x91\xed\x07\xdd\x8f\xb6\xc6\xba&\xb6\xa6\xbaf\xb6\xe6\xba\x16\xb6\x96\xba' + b'V\xb6\xd6\xba6\xb6\xb6\xbav\xb6\xf6\xba\x0e\xb6\x8e\xba\x9fl?\xeb~\xb1\xfd\xaa\xebd\xeb\xac\xebb\xeb\xaa' + b"\xeb\x16\x12h\x81\xee!\xa1\x02\xa1\x02j\x81\xb0w\xd5#X=\x83\xd6+X\xbd\x83\xd6'X}\x83\xd6/X\xfd\xdfw" + b'\x03l\x03u\x83l\x83uClCu\xc3l\xc3u#l#u\xa3l\xa3uclcu\xe3l\xe3u\x13l\xbf\xe9&\xda&\xe9&\xdb\xa6\xe8' + b'\xa6\xda\xa6\xe9\xa6\xdbf\xe8f\xdaf\xe9f\xdb\xe6\xe8\xe6\xda\xe6\xe9~\xb7\xcd\xd7-\xb0-\xd4-\xb2-\xd6' + b'\xfda\xfbS\xb7\xc4\xb6T\xb7\xcc\xb6\\\xb7\xc2\xb6R\xf7\x97m\x95n\xb5m\x8dn\xadm\x9dn\xbdm\x83n\xa3m\x93' + b'n\xb3m\x8bn\xabm\x9bn\xbbm\x87n\xa7m\x97n\xb7\xedo\xdd\x1e\xdb^\xdd>\xdb~\xdd\x01\xdbA\xdd?\xb6C' + b'\xba\xc3\xb6#\xba\xa3\xb6c\xba\xe3\xb6\x13\xba\x93\xb6S\xba\xd3\xb63\xba\xb3\xb6s\xba\xf3\xb6\x0b' + b'\xba\x8b\xb6K\xba\xcb\xb6+\xba\xab\xb6k\xba\xeb\xb6\x1b\xba\x9b\xb6[\xba\xdb\xb6;\xba\xbb\xb6{' + b'\xba\xfb\xb6\x7fu\x0fl\x0fu\x8fl\x8fu\xff\xd9\xfe\xd7=\xb1=\xd5=\xb3=\xd7\xbd\xb0\xbd\xd4\xbd\xb2\xbd' + b'\xd6\xbd\t\t\xb4\x80z|\x1e\xda\x1d*\x10*\xd0\xfd-\x8c\x93\xc6\x0e')} diff --git a/code/.venv/lib/python3.12/site-packages/bitstring/methods.py b/code/.venv/lib/python3.12/site-packages/bitstring/methods.py new file mode 100644 index 0000000..18d7acc --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/bitstring/methods.py @@ -0,0 +1,95 @@ +from __future__ import annotations + +import bitstring +from bitstring.bitstream import BitStream +from bitstring.utils import tokenparser +from bitstring.exceptions import CreationError +from typing import Union, List +from bitstring.bitstore import BitStore +from bitstring.bitstore_helpers import bitstore_from_token + + +def pack(fmt: Union[str, List[str]], *values, **kwargs) -> BitStream: + """Pack the values according to the format string and return a new BitStream. + + fmt -- A single string or a list of strings with comma separated tokens + describing how to create the BitStream. + values -- Zero or more values to pack according to the format. + kwargs -- A dictionary or keyword-value pairs - the keywords used in the + format string will be replaced with their given value. + + Token examples: 'int:12' : 12 bits as a signed integer + 'uint:8' : 8 bits as an unsigned integer + 'float:64' : 8 bytes as a big-endian float + 'intbe:16' : 2 bytes as a big-endian signed integer + 'uintbe:16' : 2 bytes as a big-endian unsigned integer + 'intle:32' : 4 bytes as a little-endian signed integer + 'uintle:32' : 4 bytes as a little-endian unsigned integer + 'floatle:64': 8 bytes as a little-endian float + 'intne:24' : 3 bytes as a native-endian signed integer + 'uintne:24' : 3 bytes as a native-endian unsigned integer + 'floatne:32': 4 bytes as a native-endian float + 'hex:80' : 80 bits as a hex string + 'oct:9' : 9 bits as an octal string + 'bin:1' : single bit binary string + 'ue' / 'uie': next bits as unsigned exp-Golomb code + 'se' / 'sie': next bits as signed exp-Golomb code + 'bits:5' : 5 bits as a bitstring object + 'bytes:10' : 10 bytes as a bytes object + 'bool' : 1 bit as a bool + 'pad:3' : 3 zero bits as padding + + >>> s = pack('uint:12, bits', 100, '0xffe') + >>> t = pack(['bits', 'bin:3'], s, '111') + >>> u = pack('uint:8=a, uint:8=b, uint:55=a', a=6, b=44) + + """ + tokens = [] + if isinstance(fmt, str): + fmt = [fmt] + try: + for f_item in fmt: + _, tkns = tokenparser(f_item, tuple(sorted(kwargs.keys()))) + tokens.extend(tkns) + except ValueError as e: + raise CreationError(*e.args) + value_iter = iter(values) + bsl: List[BitStore] = [] + try: + for name, length, value in tokens: + # If the value is in the kwd dictionary then it takes precedence. + value = kwargs.get(value, value) + # If the length is in the kwd dictionary then use that too. + length = kwargs.get(length, length) + # Also if we just have a dictionary name then we want to use it + if name in kwargs and length is None and value is None: + bsl.append(BitStream(kwargs[name])._bitstore) + continue + if length is not None: + length = int(length) + if value is None and name != 'pad': + # Take the next value from the ones provided + value = next(value_iter) + if name == 'bits': + value = bitstring.bits.Bits(value) + if length is not None and length != len(value): + raise CreationError(f"Token with length {length} packed with value of length {len(value)}.") + bsl.append(value._bitstore) + continue + bsl.append(bitstore_from_token(name, length, value)) + except StopIteration: + raise CreationError(f"Not enough parameters present to pack according to the " + f"format. {len(tokens)} values are needed.") + + try: + next(value_iter) + except StopIteration: + # Good, we've used up all the *values. + s = BitStream() + if bitstring.options.lsb0: + bsl.reverse() + for b in bsl: + s._bitstore += b + return s + + raise CreationError(f"Too many parameters present to pack according to the format. Only {len(tokens)} values were expected.") diff --git a/code/.venv/lib/python3.12/site-packages/bitstring/mxfp.py b/code/.venv/lib/python3.12/site-packages/bitstring/mxfp.py new file mode 100644 index 0000000..d675c35 --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/bitstring/mxfp.py @@ -0,0 +1,206 @@ +import array +import math +import struct +import bitarray +from bitstring.luts import mxfp_luts_compressed +import zlib +from typing import Optional + + +def round_to_nearest_ties_to_even(lut_int_to_float, lower: int, f: float) -> Optional[int]: + upper = lower + 1 + # Special case for LUTs without a negative zero. + lower_float = 0.0 if lower == 128 else lut_int_to_float[lower] + upper_float = lut_int_to_float[upper] + if upper_float < lower_float: + lower, upper = upper, lower + lower_float, upper_float = upper_float, lower_float + if f == lower_float: + return lower + if f == upper_float: + return upper + if lower_float < f < upper_float: + d1 = f - lower_float + d2 = upper_float - f + if d1 < d2: + return lower + if d2 < d1: + return upper + return lower if lower % 2 == 0 else upper + return None + + +class MXFPFormat: + """Defining an MXFP micro-scaling floating point format""" + + def __init__(self, exp_bits: int, mantissa_bits: int, bias: int, mxfp_overflow: str): + self.exp_bits = exp_bits + self.mantissa_bits = mantissa_bits + self.bias = bias + self.mxfp_overflow = mxfp_overflow + + self.pos_clamp_value = (1 << (self.exp_bits + self.mantissa_bits)) - 1 + self.neg_clamp_value = (1 << (1 + self.exp_bits + self.mantissa_bits)) - 1 + + # Special cases for e4m3 and e5m2 + if self.exp_bits == 4 and self.mantissa_bits == 3: + if self.mxfp_overflow == 'saturate': + self.pos_clamp_value = 0b01111110 # 448 + self.neg_clamp_value = 0b11111110 # -448 + else: + self.pos_clamp_value = self.neg_clamp_value = 0b11111111 # NaN + if self.exp_bits == 5 and self.mantissa_bits == 2: + if self.mxfp_overflow == 'saturate': + self.pos_clamp_value = 0b01111011 # 57344 + self.neg_clamp_value = 0b11111011 # -57344 + else: + self.pos_clamp_value = 0b01111100 # +inf + self.neg_clamp_value = 0b11111100 # -inf + + # If we calculate these LUTs now it creates a bootstrap problem in generate_luts.py. + self.lut_float16_to_mxfp = None + self.lut_int_to_float = None + + def __str__(self): + return f"MXFPFormat(exp_bits={self.exp_bits}, mantissa_bits={self.mantissa_bits}, bias={self.bias}, mxfp_overflow='{self.mxfp_overflow}')" + + def decompress_luts(self): + int_to_float_compressed, float16_to_mxfp_compressed = mxfp_luts_compressed[(self.exp_bits, self.mantissa_bits, self.bias, self.mxfp_overflow)] + self.lut_float16_to_mxfp = zlib.decompress(float16_to_mxfp_compressed) + dec = zlib.decompress(int_to_float_compressed) + self.lut_int_to_float = struct.unpack(f'<{len(dec) // 4}f', dec) + + def create_luts(self): + self.lut_int_to_float = self.createLUT_for_int_to_float() + self.lut_float16_to_mxfp = self.createLUT_for_float16_to_mxfp() + + def float_to_int(self, f: float) -> int: + """Given a Python float convert to the best mxfp float (expressed as an int) that represents it.""" + # First convert the float to a float16, then a 16 bit uint + try: + b = struct.pack('>e', f) + except (OverflowError, struct.error): + # Return the largest representable positive or negative value + return self.pos_clamp_value if f > 0 else self.neg_clamp_value + + f16_int = int.from_bytes(b, byteorder='big') + # Then use this as an index to our large LUT + return self.lut_float16_to_mxfp[f16_int] + + def slow_float_to_int(self, f: float) -> int: + # Slow, but easier to follow than the faster version. + # The output int has the binary sequence needed for the float. + length = 1 + self.exp_bits + self.mantissa_bits + values = 1 << length + # First get the NaN case out of the way + if math.isnan(f): + if length == 8: + return 0xff # Works for both e5m2 and e4m3 + # For smaller lengths, NaN isn't supported so we instead return an invalid value to detect later + return 0xff + # This is so we can distinguish between 0.0 and -0.0 + is_positive = math.copysign(1.0, f) == 1.0 + if is_positive: + # Positive, so top bit is not set + for i in range(values // 2 - 1): + upper = self.lut_int_to_float[i + 1] + if upper == float('inf'): + break + x = round_to_nearest_ties_to_even(self.lut_int_to_float, i, f) + if x is not None: + return x + return self.pos_clamp_value + else: + # Negative, so top bit is set + for i in range(values // 2, values - 1): + lower = self.lut_int_to_float[i + 1] + if lower == float('-inf'): + break + x = round_to_nearest_ties_to_even(self.lut_int_to_float, i, f) + if x is not None: + return x + # Clip to negative max + return self.neg_clamp_value + + def createLUT_for_int_to_float(self) -> array.array: + """Create a LUT to convert an int in representing a MXFP float into a Python float""" + i2f = [] + length = 1 + self.exp_bits + self.mantissa_bits + for i in range(1 << length): + b = bitarray.util.int2ba(i, length=length, endian='big', signed=False) + sign = b[0] + exponent = bitarray.util.ba2int(b[1:1 + self.exp_bits]) + significand = b[1 + self.exp_bits:] + if exponent == 0: + significand = bitarray.bitarray('0') + significand + exponent = -self.bias + 1 + else: + significand = bitarray.bitarray('1') + significand + exponent -= self.bias + f = float(bitarray.util.ba2int(significand)) / (2.0 ** self.mantissa_bits) + f *= 2 ** exponent + if length == 8: + # Some special cases + if self.exp_bits == 5: + if i in [0b01111100, 0b11111100]: + f = float('inf') + if i in [0b01111101, 0b11111101, 0b01111110, 0b11111110, 0b01111111, 0b11111111]: + f = float('nan') + if self.exp_bits == 4: + if i in [0b01111111, 0b11111111]: + f = float('nan') + i2f.append(f if not sign else -f) + return array.array('f', i2f) + + def createLUT_for_float16_to_mxfp(self) -> bytes: + """Create a LUT to convert a float16 into a MXFP format""" + # Used to create the LUT that was compressed and stored for the fp8 code + length = 1 + self.exp_bits + self.mantissa_bits + if length == 8: + import gfloat + from gfloat.formats import format_info_ocp_e5m2, format_info_ocp_e4m3 + fi = format_info_ocp_e5m2 if self.exp_bits == 5 else format_info_ocp_e4m3 + + fp16_to_fp8 = bytearray(1 << 16) + for i in range(1 << 16): + b = struct.pack('>H', i) + f, = struct.unpack('>e', b) + fp = gfloat.round_float(fi, f, sat=self.mxfp_overflow == 'saturate') + if math.isnan(fp): + fp8_i = 0b11111111 + else: + # Special case for negative zero + if fp == 0.0 and math.copysign(1.0, fp) == -1.0: + fp8_i = 0b10000000 + else: + fp8_i = self.lut_int_to_float.index(fp) + fp16_to_fp8[i] = fp8_i + return bytes(fp16_to_fp8) + else: + assert length in [4, 6] + fp16_to_fp8 = bytearray(1 << 16) + for i in range(1 << 16): + b = struct.pack('>H', i) + f, = struct.unpack('>e', b) + fp8_i = self.slow_float_to_int(f) + fp16_to_fp8[i] = fp8_i + return bytes(fp16_to_fp8) + + +e2m1mxfp_fmt = MXFPFormat(exp_bits=2, mantissa_bits=1, bias=1, mxfp_overflow='saturate') +e2m3mxfp_fmt = MXFPFormat(exp_bits=2, mantissa_bits=3, bias=1, mxfp_overflow='saturate') +e3m2mxfp_fmt = MXFPFormat(exp_bits=3, mantissa_bits=2, bias=3, mxfp_overflow='saturate') +e4m3mxfp_saturate_fmt = MXFPFormat(exp_bits=4, mantissa_bits=3, bias=7, mxfp_overflow='saturate') +e5m2mxfp_saturate_fmt = MXFPFormat(exp_bits=5, mantissa_bits=2, bias=15, mxfp_overflow='saturate') +e4m3mxfp_overflow_fmt = MXFPFormat(exp_bits=4, mantissa_bits=3, bias=7, mxfp_overflow='overflow') +e5m2mxfp_overflow_fmt = MXFPFormat(exp_bits=5, mantissa_bits=2, bias=15, mxfp_overflow='overflow') + + +def decompress_luts(): + e2m1mxfp_fmt.decompress_luts() + e2m3mxfp_fmt.decompress_luts() + e3m2mxfp_fmt.decompress_luts() + e4m3mxfp_saturate_fmt.decompress_luts() + e5m2mxfp_saturate_fmt.decompress_luts() + e4m3mxfp_overflow_fmt.decompress_luts() + e5m2mxfp_overflow_fmt.decompress_luts() diff --git a/code/.venv/lib/python3.12/site-packages/bitstring/py.typed b/code/.venv/lib/python3.12/site-packages/bitstring/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/code/.venv/lib/python3.12/site-packages/bitstring/utils.py b/code/.venv/lib/python3.12/site-packages/bitstring/utils.py new file mode 100644 index 0000000..4dae4d9 --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/bitstring/utils.py @@ -0,0 +1,238 @@ +from __future__ import annotations + +import functools +import re +from typing import Tuple, List, Optional, Pattern, Dict, Union, Match + + +# A token name followed by optional : then an integer number +NAME_INT_RE: Pattern[str] = re.compile(r'^([a-zA-Z][a-zA-Z0-9_]*?):?(\d*)$') + +# A token name followed by optional : then an arbitrary keyword +NAME_KWARG_RE: Pattern[str] = re.compile(r'^([a-zA-Z][a-zA-Z0-9_]*?):?([a-zA-Z0-9_]+)$') + +CACHE_SIZE = 256 + +DEFAULT_BITS: Pattern[str] = re.compile(r'^(?P[^=]+)?(=(?P.*))?$', re.IGNORECASE) + +MULTIPLICATIVE_RE: Pattern[str] = re.compile(r'^(?P.*)\*(?P.+)') + +# Hex, oct or binary literals +LITERAL_RE: Pattern[str] = re.compile(r'^(?P0([xob]))(?P.+)', re.IGNORECASE) + +# An endianness indicator followed by one or more struct.pack codes +STRUCT_PACK_RE: Pattern[str] = re.compile(r'^(?P[<>@=])(?P(?:\d*[bBhHlLqQefd])+)$') +# The same as above, but it doesn't insist on an endianness as it's byteswapping anyway. +BYTESWAP_STRUCT_PACK_RE: Pattern[str] = re.compile(r'^(?P[<>@=])?(?P(?:\d*[bBhHlLqQefd])+)$') +# An endianness indicator followed by exactly one struct.pack codes +SINGLE_STRUCT_PACK_RE: Pattern[str] = re.compile(r'^(?P[<>@=])(?P[bBhHlLqQefd])$') + +# A number followed by a single character struct.pack code +STRUCT_SPLIT_RE: Pattern[str] = re.compile(r'\d*[bBhHlLqQefd]') + +# These replicate the struct.pack codes +# Big-endian +REPLACEMENTS_BE: Dict[str, str] = {'b': 'int8', 'B': 'uint8', + 'h': 'intbe16', 'H': 'uintbe16', + 'l': 'intbe32', 'L': 'uintbe32', + 'q': 'intbe64', 'Q': 'uintbe64', + 'e': 'floatbe16', 'f': 'floatbe32', 'd': 'floatbe64'} +# Little-endian +REPLACEMENTS_LE: Dict[str, str] = {'b': 'int8', 'B': 'uint8', + 'h': 'intle16', 'H': 'uintle16', + 'l': 'intle32', 'L': 'uintle32', + 'q': 'intle64', 'Q': 'uintle64', + 'e': 'floatle16', 'f': 'floatle32', 'd': 'floatle64'} + +# Native-endian +REPLACEMENTS_NE: Dict[str, str] = {'b': 'int8', 'B': 'uint8', + 'h': 'intne16', 'H': 'uintne16', + 'l': 'intne32', 'L': 'uintne32', + 'q': 'intne64', 'Q': 'uintne64', + 'e': 'floatne16', 'f': 'floatne32', 'd': 'floatne64'} + +# Size in bytes of all the pack codes. +PACK_CODE_SIZE: Dict[str, int] = {'b': 1, 'B': 1, 'h': 2, 'H': 2, 'l': 4, 'L': 4, + 'q': 8, 'Q': 8, 'e': 2, 'f': 4, 'd': 8} + + +def structparser(m: Match[str]) -> List[str]: + """Parse struct-like format string token into sub-token list.""" + endian = m.group('endian') + # Split the format string into a list of 'q', '4h' etc. + formatlist = re.findall(STRUCT_SPLIT_RE, m.group('fmt')) + # Now deal with multiplicative factors, 4h -> hhhh etc. + fmt = ''.join([f[-1] * int(f[:-1]) if len(f) != 1 else + f for f in formatlist]) + if endian in '@=': + # Native endianness + tokens = [REPLACEMENTS_NE[c] for c in fmt] + elif endian == '<': + tokens = [REPLACEMENTS_LE[c] for c in fmt] + else: + assert endian == '>' + tokens = [REPLACEMENTS_BE[c] for c in fmt] + return tokens + + +@functools.lru_cache(CACHE_SIZE) +def parse_name_length_token(fmt: str, **kwargs) -> Tuple[str, Optional[int]]: + # Any single token with just a name and length + if m2 := NAME_INT_RE.match(fmt): + name = m2.group(1) + length_str = m2.group(2) + length = None if length_str == '' else int(length_str) + else: + # Maybe the length is in the kwargs? + if m := NAME_KWARG_RE.match(fmt): + name = m.group(1) + try: + length_str = kwargs[m.group(2)] + except KeyError: + raise ValueError(f"Can't parse 'name[:]length' token '{fmt}'.") + length = int(length_str) + else: + raise ValueError(f"Can't parse 'name[:]length' token '{fmt}'.") + return name, length + + +@functools.lru_cache(CACHE_SIZE) +def parse_single_struct_token(fmt: str) -> Optional[Tuple[str, Optional[int]]]: + if m := SINGLE_STRUCT_PACK_RE.match(fmt): + endian = m.group('endian') + f = m.group('fmt') + if endian == '>': + fmt = REPLACEMENTS_BE[f] + elif endian == '<': + fmt = REPLACEMENTS_LE[f] + else: + assert endian in '=@' + fmt = REPLACEMENTS_NE[f] + return parse_name_length_token(fmt) + else: + return None + + +@functools.lru_cache(CACHE_SIZE) +def parse_single_token(token: str) -> Tuple[str, str, Optional[str]]: + if (equals_pos := token.find('=')) == -1: + value = None + else: + value = token[equals_pos + 1:] + token = token[:equals_pos] + + if m2 := NAME_INT_RE.match(token): + name = m2.group(1) + length_str = m2.group(2) + length = None if length_str == '' else length_str + elif m3 := NAME_KWARG_RE.match(token): + # name then a keyword for a length + name = m3.group(1) + length = m3.group(2) + else: + # If you don't specify a 'name' then the default is 'bits' + name = 'bits' + length = token + return name, length, value + + +@functools.lru_cache(CACHE_SIZE) +def preprocess_tokens(fmt: str) -> List[str]: + # Remove whitespace and expand brackets + fmt = expand_brackets(''.join(fmt.split())) + + # Split tokens by ',' and remove whitespace + # The meta_tokens can either be ordinary single tokens or multiple struct-format token strings. + meta_tokens = [f.strip() for f in fmt.split(',')] + final_tokens = [] + + for meta_token in meta_tokens: + if meta_token == '': + continue + # Extract factor and actual token if a multiplicative factor exists + factor = 1 + if m := MULTIPLICATIVE_RE.match(meta_token): + factor = int(m.group('factor')) + meta_token = m.group('token') + + # Parse struct-like format into sub-tokens or treat as single token + tokens = structparser(m) if (m := STRUCT_PACK_RE.match(meta_token)) else [meta_token] + + # Extend final tokens list with parsed tokens, repeated by the factor + final_tokens.extend(tokens * factor) + return final_tokens + + +@functools.lru_cache(CACHE_SIZE) +def tokenparser(fmt: str, keys: Tuple[str, ...] = ()) -> \ + Tuple[bool, List[Tuple[str, Union[int, str, None], Optional[str]]]]: + """Divide the format string into tokens and parse them. + + Return stretchy token and list of [initialiser, length, value] + initialiser is one of: hex, oct, bin, uint, int, se, ue, 0x, 0o, 0b etc. + length is None if not known, as is value. + + If the token is in the keyword dictionary (keys) then it counts as a + special case and isn't messed with. + + tokens must be of the form: [factor*][initialiser][:][length][=value] + + """ + tokens = preprocess_tokens(fmt) + stretchy_token = False + ret_vals: List[Tuple[str, Union[str, int, None], Optional[str]]] = [] + for token in tokens: + if keys and token in keys: + # Don't bother parsing it, it's a keyword argument + ret_vals.append((token, None, None)) + continue + if token == '': + continue + # Match literal tokens of the form 0x... 0o... and 0b... + if m := LITERAL_RE.match(token): + ret_vals.append((m.group('name'), None, m.group('value'))) + continue + name, length, value = parse_single_token(token) + if length is None: + stretchy_token = True + if length is not None: + # Try converting length to int, otherwise check it's a key. + try: + length = int(length) + except ValueError: + if not keys or length not in keys: + raise ValueError(f"Don't understand length '{length}' of token.") + ret_vals.append((name, length, value)) + return stretchy_token, ret_vals + + +BRACKET_RE = re.compile(r'(?P\d+)\*\(') + + +def expand_brackets(s: str) -> str: + """Expand all brackets.""" + while True: + start = s.find('(') + if start == -1: + break + count = 1 # Number of hanging open brackets + p = start + 1 + while p < len(s): + count += (s[p] == '(') - (s[p] == ')') + if count == 0: + break + p += 1 + if count != 0: + raise ValueError(f"Unbalanced parenthesis in '{s}'.") + if start == 0 or s[start - 1] != '*': + s = s[0:start] + s[start + 1:p] + s[p + 1:] + else: + # Looks for first number*( + m = BRACKET_RE.search(s) + if m: + factor = int(m.group('factor')) + matchstart = m.start('factor') + s = s[0:matchstart] + (factor - 1) * (s[start + 1:p] + ',') + s[start + 1:p] + s[p + 1:] + else: + raise ValueError(f"Failed to parse '{s}'.") + return s diff --git a/code/.venv/lib/python3.12/site-packages/cffi-1.17.0.dist-info/INSTALLER b/code/.venv/lib/python3.12/site-packages/cffi-1.17.0.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/cffi-1.17.0.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/code/.venv/lib/python3.12/site-packages/cffi-1.17.0.dist-info/LICENSE b/code/.venv/lib/python3.12/site-packages/cffi-1.17.0.dist-info/LICENSE new file mode 100644 index 0000000..29225ee --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/cffi-1.17.0.dist-info/LICENSE @@ -0,0 +1,26 @@ + +Except when otherwise stated (look for LICENSE files in directories or +information at the beginning of each file) all software and +documentation is licensed as follows: + + The MIT License + + Permission is hereby granted, free of charge, to any person + obtaining a copy of this software and associated documentation + files (the "Software"), to deal in the Software without + restriction, including without limitation the rights to use, + copy, modify, merge, publish, distribute, sublicense, and/or + sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. + diff --git a/code/.venv/lib/python3.12/site-packages/cffi-1.17.0.dist-info/METADATA b/code/.venv/lib/python3.12/site-packages/cffi-1.17.0.dist-info/METADATA new file mode 100644 index 0000000..a0d90a3 --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/cffi-1.17.0.dist-info/METADATA @@ -0,0 +1,40 @@ +Metadata-Version: 2.1 +Name: cffi +Version: 1.17.0 +Summary: Foreign Function Interface for Python calling C code. +Home-page: http://cffi.readthedocs.org +Author: Armin Rigo, Maciej Fijalkowski +Author-email: python-cffi@googlegroups.com +License: MIT +Project-URL: Documentation, http://cffi.readthedocs.org/ +Project-URL: Source Code, https://github.com/python-cffi/cffi +Project-URL: Issue Tracker, https://github.com/python-cffi/cffi/issues +Project-URL: Changelog, https://cffi.readthedocs.io/en/latest/whatsnew.html +Project-URL: Downloads, https://github.com/python-cffi/cffi/releases +Project-URL: Contact, https://groups.google.com/forum/#!forum/python-cffi +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: 3.9 +Classifier: Programming Language :: Python :: 3.10 +Classifier: Programming Language :: Python :: 3.11 +Classifier: Programming Language :: Python :: 3.12 +Classifier: Programming Language :: Python :: 3.13 +Classifier: Programming Language :: Python :: Implementation :: CPython +Classifier: Programming Language :: Python :: Implementation :: PyPy +Classifier: License :: OSI Approved :: MIT License +Requires-Python: >=3.8 +License-File: LICENSE +Requires-Dist: pycparser + + +CFFI +==== + +Foreign Function Interface for Python calling C code. +Please see the `Documentation `_. + +Contact +------- + +`Mailing list `_ diff --git a/code/.venv/lib/python3.12/site-packages/cffi-1.17.0.dist-info/RECORD b/code/.venv/lib/python3.12/site-packages/cffi-1.17.0.dist-info/RECORD new file mode 100644 index 0000000..c8eea12 --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/cffi-1.17.0.dist-info/RECORD @@ -0,0 +1,48 @@ +_cffi_backend.cpython-312-x86_64-linux-gnu.so,sha256=6xKpkTOZLhQwLs-koyNTRWLl0uU2zelLme8BFaQtoAA,1114632 +cffi-1.17.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +cffi-1.17.0.dist-info/LICENSE,sha256=BLgPWwd7vtaICM_rreteNSPyqMmpZJXFh72W3x6sKjM,1294 +cffi-1.17.0.dist-info/METADATA,sha256=nQRP9FLzGuTmMppBjZq_FTiwkP3LFhTwwbk-6-_Z9uw,1531 +cffi-1.17.0.dist-info/RECORD,, +cffi-1.17.0.dist-info/WHEEL,sha256=3kl6A45eSyE5JSahRrEmKMwp_kGk5NpuqX0WHIJWabQ,151 +cffi-1.17.0.dist-info/entry_points.txt,sha256=y6jTxnyeuLnL-XJcDv8uML3n6wyYiGRg8MTp_QGJ9Ho,75 +cffi-1.17.0.dist-info/top_level.txt,sha256=rE7WR3rZfNKxWI9-jn6hsHCAl7MDkB-FmuQbxWjFehQ,19 +cffi/__init__.py,sha256=5QOG9wcFEENTxG1y_G0_IXDdFCooTVgAy4ZGMFCSpD8,513 +cffi/__pycache__/__init__.cpython-312.pyc,, +cffi/__pycache__/_imp_emulation.cpython-312.pyc,, +cffi/__pycache__/_shimmed_dist_utils.cpython-312.pyc,, +cffi/__pycache__/api.cpython-312.pyc,, +cffi/__pycache__/backend_ctypes.cpython-312.pyc,, +cffi/__pycache__/cffi_opcode.cpython-312.pyc,, +cffi/__pycache__/commontypes.cpython-312.pyc,, +cffi/__pycache__/cparser.cpython-312.pyc,, +cffi/__pycache__/error.cpython-312.pyc,, +cffi/__pycache__/ffiplatform.cpython-312.pyc,, +cffi/__pycache__/lock.cpython-312.pyc,, +cffi/__pycache__/model.cpython-312.pyc,, +cffi/__pycache__/pkgconfig.cpython-312.pyc,, +cffi/__pycache__/recompiler.cpython-312.pyc,, +cffi/__pycache__/setuptools_ext.cpython-312.pyc,, +cffi/__pycache__/vengine_cpy.cpython-312.pyc,, +cffi/__pycache__/vengine_gen.cpython-312.pyc,, +cffi/__pycache__/verifier.cpython-312.pyc,, +cffi/_cffi_errors.h,sha256=zQXt7uR_m8gUW-fI2hJg0KoSkJFwXv8RGUkEDZ177dQ,3908 +cffi/_cffi_include.h,sha256=Exhmgm9qzHWzWivjfTe0D7Xp4rPUkVxdNuwGhMTMzbw,15055 +cffi/_embedding.h,sha256=39XMUYv8obSIww-s0MXQt5LoV_U6-BI_Fn0-43DVt0U,18787 +cffi/_imp_emulation.py,sha256=RxREG8zAbI2RPGBww90u_5fi8sWdahpdipOoPzkp7C0,2960 +cffi/_shimmed_dist_utils.py,sha256=mLuEtxw4gbuA2De_gD7zEhb6Q8Wm2lBPtwC68gd9XTs,2007 +cffi/api.py,sha256=wtJU0aGUC3TyYnjBIgsOIlv7drF19jV-y_srt7c8yhg,42085 +cffi/backend_ctypes.py,sha256=h5ZIzLc6BFVXnGyc9xPqZWUS7qGy7yFSDqXe68Sa8z4,42454 +cffi/cffi_opcode.py,sha256=JDV5l0R0_OadBX_uE7xPPTYtMdmpp8I9UYd6av7aiDU,5731 +cffi/commontypes.py,sha256=7N6zPtCFlvxXMWhHV08psUjdYIK2XgsN3yo5dgua_v4,2805 +cffi/cparser.py,sha256=0qI3mEzZSNVcCangoyXOoAcL-RhpQL08eG8798T024s,44789 +cffi/error.py,sha256=v6xTiS4U0kvDcy4h_BDRo5v39ZQuj-IMRYLv5ETddZs,877 +cffi/ffiplatform.py,sha256=avxFjdikYGJoEtmJO7ewVmwG_VEVl6EZ_WaNhZYCqv4,3584 +cffi/lock.py,sha256=l9TTdwMIMpi6jDkJGnQgE9cvTIR7CAntIJr8EGHt3pY,747 +cffi/model.py,sha256=W30UFQZE73jL5Mx5N81YT77us2W2iJjTm0XYfnwz1cg,21797 +cffi/parse_c_type.h,sha256=OdwQfwM9ktq6vlCB43exFQmxDBtj2MBNdK8LYl15tjw,5976 +cffi/pkgconfig.py,sha256=LP1w7vmWvmKwyqLaU1Z243FOWGNQMrgMUZrvgFuOlco,4374 +cffi/recompiler.py,sha256=V-DEjnxaPxMUIdkzrJutW7Is8s_T2pB6Henq6Aud4FQ,64607 +cffi/setuptools_ext.py,sha256=-ebj79lO2_AUH-kRcaja2pKY1Z_5tloGwsJgzK8P3Cc,8871 +cffi/vengine_cpy.py,sha256=8UagT6ZEOZf6Dju7_CfNulue8CnsHLEzJYhnqUhoF04,43752 +cffi/vengine_gen.py,sha256=DUlEIrDiVin1Pnhn1sfoamnS5NLqfJcOdhRoeSNeJRg,26939 +cffi/verifier.py,sha256=oX8jpaohg2Qm3aHcznidAdvrVm5N4sQYG0a3Eo5mIl4,11182 diff --git a/code/.venv/lib/python3.12/site-packages/cffi-1.17.0.dist-info/WHEEL b/code/.venv/lib/python3.12/site-packages/cffi-1.17.0.dist-info/WHEEL new file mode 100644 index 0000000..0974922 --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/cffi-1.17.0.dist-info/WHEEL @@ -0,0 +1,6 @@ +Wheel-Version: 1.0 +Generator: setuptools (72.1.0) +Root-Is-Purelib: false +Tag: cp312-cp312-manylinux_2_17_x86_64 +Tag: cp312-cp312-manylinux2014_x86_64 + diff --git a/code/.venv/lib/python3.12/site-packages/cffi-1.17.0.dist-info/entry_points.txt b/code/.venv/lib/python3.12/site-packages/cffi-1.17.0.dist-info/entry_points.txt new file mode 100644 index 0000000..4b0274f --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/cffi-1.17.0.dist-info/entry_points.txt @@ -0,0 +1,2 @@ +[distutils.setup_keywords] +cffi_modules = cffi.setuptools_ext:cffi_modules diff --git a/code/.venv/lib/python3.12/site-packages/cffi-1.17.0.dist-info/top_level.txt b/code/.venv/lib/python3.12/site-packages/cffi-1.17.0.dist-info/top_level.txt new file mode 100644 index 0000000..f645779 --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/cffi-1.17.0.dist-info/top_level.txt @@ -0,0 +1,2 @@ +_cffi_backend +cffi diff --git a/code/.venv/lib/python3.12/site-packages/cffi/__init__.py b/code/.venv/lib/python3.12/site-packages/cffi/__init__.py new file mode 100644 index 0000000..deeacc5 --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/cffi/__init__.py @@ -0,0 +1,14 @@ +__all__ = ['FFI', 'VerificationError', 'VerificationMissing', 'CDefError', + 'FFIError'] + +from .api import FFI +from .error import CDefError, FFIError, VerificationError, VerificationMissing +from .error import PkgConfigError + +__version__ = "1.17.0" +__version_info__ = (1, 17, 0) + +# The verifier module file names are based on the CRC32 of a string that +# contains the following version number. It may be older than __version__ +# if nothing is clearly incompatible. +__version_verifier_modules__ = "0.8.6" diff --git a/code/.venv/lib/python3.12/site-packages/cffi/__pycache__/__init__.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/cffi/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..a0c87cc Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/cffi/__pycache__/__init__.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/cffi/__pycache__/_imp_emulation.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/cffi/__pycache__/_imp_emulation.cpython-312.pyc new file mode 100644 index 0000000..98cf463 Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/cffi/__pycache__/_imp_emulation.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/cffi/__pycache__/_shimmed_dist_utils.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/cffi/__pycache__/_shimmed_dist_utils.cpython-312.pyc new file mode 100644 index 0000000..9404194 Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/cffi/__pycache__/_shimmed_dist_utils.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/cffi/__pycache__/api.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/cffi/__pycache__/api.cpython-312.pyc new file mode 100644 index 0000000..a9f60a2 Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/cffi/__pycache__/api.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/cffi/__pycache__/backend_ctypes.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/cffi/__pycache__/backend_ctypes.cpython-312.pyc new file mode 100644 index 0000000..81f1135 Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/cffi/__pycache__/backend_ctypes.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/cffi/__pycache__/cffi_opcode.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/cffi/__pycache__/cffi_opcode.cpython-312.pyc new file mode 100644 index 0000000..a56b5ad Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/cffi/__pycache__/cffi_opcode.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/cffi/__pycache__/commontypes.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/cffi/__pycache__/commontypes.cpython-312.pyc new file mode 100644 index 0000000..9dc4696 Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/cffi/__pycache__/commontypes.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/cffi/__pycache__/cparser.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/cffi/__pycache__/cparser.cpython-312.pyc new file mode 100644 index 0000000..d24218f Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/cffi/__pycache__/cparser.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/cffi/__pycache__/error.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/cffi/__pycache__/error.cpython-312.pyc new file mode 100644 index 0000000..3196573 Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/cffi/__pycache__/error.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/cffi/__pycache__/ffiplatform.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/cffi/__pycache__/ffiplatform.cpython-312.pyc new file mode 100644 index 0000000..d5a733c Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/cffi/__pycache__/ffiplatform.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/cffi/__pycache__/lock.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/cffi/__pycache__/lock.cpython-312.pyc new file mode 100644 index 0000000..b19d1b4 Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/cffi/__pycache__/lock.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/cffi/__pycache__/model.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/cffi/__pycache__/model.cpython-312.pyc new file mode 100644 index 0000000..5b6d9b8 Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/cffi/__pycache__/model.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/cffi/__pycache__/pkgconfig.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/cffi/__pycache__/pkgconfig.cpython-312.pyc new file mode 100644 index 0000000..d011a3b Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/cffi/__pycache__/pkgconfig.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/cffi/__pycache__/recompiler.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/cffi/__pycache__/recompiler.cpython-312.pyc new file mode 100644 index 0000000..246b5dd Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/cffi/__pycache__/recompiler.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/cffi/__pycache__/setuptools_ext.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/cffi/__pycache__/setuptools_ext.cpython-312.pyc new file mode 100644 index 0000000..d961208 Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/cffi/__pycache__/setuptools_ext.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/cffi/__pycache__/vengine_cpy.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/cffi/__pycache__/vengine_cpy.cpython-312.pyc new file mode 100644 index 0000000..0a7e45e Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/cffi/__pycache__/vengine_cpy.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/cffi/__pycache__/vengine_gen.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/cffi/__pycache__/vengine_gen.cpython-312.pyc new file mode 100644 index 0000000..568983f Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/cffi/__pycache__/vengine_gen.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/cffi/__pycache__/verifier.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/cffi/__pycache__/verifier.cpython-312.pyc new file mode 100644 index 0000000..9ab9d5f Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/cffi/__pycache__/verifier.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/cffi/_cffi_errors.h b/code/.venv/lib/python3.12/site-packages/cffi/_cffi_errors.h new file mode 100644 index 0000000..158e059 --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/cffi/_cffi_errors.h @@ -0,0 +1,149 @@ +#ifndef CFFI_MESSAGEBOX +# ifdef _MSC_VER +# define CFFI_MESSAGEBOX 1 +# else +# define CFFI_MESSAGEBOX 0 +# endif +#endif + + +#if CFFI_MESSAGEBOX +/* Windows only: logic to take the Python-CFFI embedding logic + initialization errors and display them in a background thread + with MessageBox. The idea is that if the whole program closes + as a result of this problem, then likely it is already a console + program and you can read the stderr output in the console too. + If it is not a console program, then it will likely show its own + dialog to complain, or generally not abruptly close, and for this + case the background thread should stay alive. +*/ +static void *volatile _cffi_bootstrap_text; + +static PyObject *_cffi_start_error_capture(void) +{ + PyObject *result = NULL; + PyObject *x, *m, *bi; + + if (InterlockedCompareExchangePointer(&_cffi_bootstrap_text, + (void *)1, NULL) != NULL) + return (PyObject *)1; + + m = PyImport_AddModule("_cffi_error_capture"); + if (m == NULL) + goto error; + + result = PyModule_GetDict(m); + if (result == NULL) + goto error; + +#if PY_MAJOR_VERSION >= 3 + bi = PyImport_ImportModule("builtins"); +#else + bi = PyImport_ImportModule("__builtin__"); +#endif + if (bi == NULL) + goto error; + PyDict_SetItemString(result, "__builtins__", bi); + Py_DECREF(bi); + + x = PyRun_String( + "import sys\n" + "class FileLike:\n" + " def write(self, x):\n" + " try:\n" + " of.write(x)\n" + " except: pass\n" + " self.buf += x\n" + " def flush(self):\n" + " pass\n" + "fl = FileLike()\n" + "fl.buf = ''\n" + "of = sys.stderr\n" + "sys.stderr = fl\n" + "def done():\n" + " sys.stderr = of\n" + " return fl.buf\n", /* make sure the returned value stays alive */ + Py_file_input, + result, result); + Py_XDECREF(x); + + error: + if (PyErr_Occurred()) + { + PyErr_WriteUnraisable(Py_None); + PyErr_Clear(); + } + return result; +} + +#pragma comment(lib, "user32.lib") + +static DWORD WINAPI _cffi_bootstrap_dialog(LPVOID ignored) +{ + Sleep(666); /* may be interrupted if the whole process is closing */ +#if PY_MAJOR_VERSION >= 3 + MessageBoxW(NULL, (wchar_t *)_cffi_bootstrap_text, + L"Python-CFFI error", + MB_OK | MB_ICONERROR); +#else + MessageBoxA(NULL, (char *)_cffi_bootstrap_text, + "Python-CFFI error", + MB_OK | MB_ICONERROR); +#endif + _cffi_bootstrap_text = NULL; + return 0; +} + +static void _cffi_stop_error_capture(PyObject *ecap) +{ + PyObject *s; + void *text; + + if (ecap == (PyObject *)1) + return; + + if (ecap == NULL) + goto error; + + s = PyRun_String("done()", Py_eval_input, ecap, ecap); + if (s == NULL) + goto error; + + /* Show a dialog box, but in a background thread, and + never show multiple dialog boxes at once. */ +#if PY_MAJOR_VERSION >= 3 + text = PyUnicode_AsWideCharString(s, NULL); +#else + text = PyString_AsString(s); +#endif + + _cffi_bootstrap_text = text; + + if (text != NULL) + { + HANDLE h; + h = CreateThread(NULL, 0, _cffi_bootstrap_dialog, + NULL, 0, NULL); + if (h != NULL) + CloseHandle(h); + } + /* decref the string, but it should stay alive as 'fl.buf' + in the small module above. It will really be freed only if + we later get another similar error. So it's a leak of at + most one copy of the small module. That's fine for this + situation which is usually a "fatal error" anyway. */ + Py_DECREF(s); + PyErr_Clear(); + return; + + error: + _cffi_bootstrap_text = NULL; + PyErr_Clear(); +} + +#else + +static PyObject *_cffi_start_error_capture(void) { return NULL; } +static void _cffi_stop_error_capture(PyObject *ecap) { } + +#endif diff --git a/code/.venv/lib/python3.12/site-packages/cffi/_cffi_include.h b/code/.venv/lib/python3.12/site-packages/cffi/_cffi_include.h new file mode 100644 index 0000000..908a1d7 --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/cffi/_cffi_include.h @@ -0,0 +1,389 @@ +#define _CFFI_ + +/* We try to define Py_LIMITED_API before including Python.h. + + Mess: we can only define it if Py_DEBUG, Py_TRACE_REFS and + Py_REF_DEBUG are not defined. This is a best-effort approximation: + we can learn about Py_DEBUG from pyconfig.h, but it is unclear if + the same works for the other two macros. Py_DEBUG implies them, + but not the other way around. + + The implementation is messy (issue #350): on Windows, with _MSC_VER, + we have to define Py_LIMITED_API even before including pyconfig.h. + In that case, we guess what pyconfig.h will do to the macros above, + and check our guess after the #include. + + Note that on Windows, with CPython 3.x, you need >= 3.5 and virtualenv + version >= 16.0.0. With older versions of either, you don't get a + copy of PYTHON3.DLL in the virtualenv. We can't check the version of + CPython *before* we even include pyconfig.h. ffi.set_source() puts + a ``#define _CFFI_NO_LIMITED_API'' at the start of this file if it is + running on Windows < 3.5, as an attempt at fixing it, but that's + arguably wrong because it may not be the target version of Python. + Still better than nothing I guess. As another workaround, you can + remove the definition of Py_LIMITED_API here. + + See also 'py_limited_api' in cffi/setuptools_ext.py. +*/ +#if !defined(_CFFI_USE_EMBEDDING) && !defined(Py_LIMITED_API) +# ifdef _MSC_VER +# if !defined(_DEBUG) && !defined(Py_DEBUG) && !defined(Py_TRACE_REFS) && !defined(Py_REF_DEBUG) && !defined(_CFFI_NO_LIMITED_API) +# define Py_LIMITED_API +# endif +# include + /* sanity-check: Py_LIMITED_API will cause crashes if any of these + are also defined. Normally, the Python file PC/pyconfig.h does not + cause any of these to be defined, with the exception that _DEBUG + causes Py_DEBUG. Double-check that. */ +# ifdef Py_LIMITED_API +# if defined(Py_DEBUG) +# error "pyconfig.h unexpectedly defines Py_DEBUG, but Py_LIMITED_API is set" +# endif +# if defined(Py_TRACE_REFS) +# error "pyconfig.h unexpectedly defines Py_TRACE_REFS, but Py_LIMITED_API is set" +# endif +# if defined(Py_REF_DEBUG) +# error "pyconfig.h unexpectedly defines Py_REF_DEBUG, but Py_LIMITED_API is set" +# endif +# endif +# else +# include +# if !defined(Py_DEBUG) && !defined(Py_TRACE_REFS) && !defined(Py_REF_DEBUG) && !defined(_CFFI_NO_LIMITED_API) +# define Py_LIMITED_API +# endif +# endif +#endif + +#include +#ifdef __cplusplus +extern "C" { +#endif +#include +#include "parse_c_type.h" + +/* this block of #ifs should be kept exactly identical between + c/_cffi_backend.c, cffi/vengine_cpy.py, cffi/vengine_gen.py + and cffi/_cffi_include.h */ +#if defined(_MSC_VER) +# include /* for alloca() */ +# if _MSC_VER < 1600 /* MSVC < 2010 */ + typedef __int8 int8_t; + typedef __int16 int16_t; + typedef __int32 int32_t; + typedef __int64 int64_t; + typedef unsigned __int8 uint8_t; + typedef unsigned __int16 uint16_t; + typedef unsigned __int32 uint32_t; + typedef unsigned __int64 uint64_t; + typedef __int8 int_least8_t; + typedef __int16 int_least16_t; + typedef __int32 int_least32_t; + typedef __int64 int_least64_t; + typedef unsigned __int8 uint_least8_t; + typedef unsigned __int16 uint_least16_t; + typedef unsigned __int32 uint_least32_t; + typedef unsigned __int64 uint_least64_t; + typedef __int8 int_fast8_t; + typedef __int16 int_fast16_t; + typedef __int32 int_fast32_t; + typedef __int64 int_fast64_t; + typedef unsigned __int8 uint_fast8_t; + typedef unsigned __int16 uint_fast16_t; + typedef unsigned __int32 uint_fast32_t; + typedef unsigned __int64 uint_fast64_t; + typedef __int64 intmax_t; + typedef unsigned __int64 uintmax_t; +# else +# include +# endif +# if _MSC_VER < 1800 /* MSVC < 2013 */ +# ifndef __cplusplus + typedef unsigned char _Bool; +# endif +# endif +# define _cffi_float_complex_t _Fcomplex /* include for it */ +# define _cffi_double_complex_t _Dcomplex /* include for it */ +#else +# include +# if (defined (__SVR4) && defined (__sun)) || defined(_AIX) || defined(__hpux) +# include +# endif +# define _cffi_float_complex_t float _Complex +# define _cffi_double_complex_t double _Complex +#endif + +#ifdef __GNUC__ +# define _CFFI_UNUSED_FN __attribute__((unused)) +#else +# define _CFFI_UNUSED_FN /* nothing */ +#endif + +#ifdef __cplusplus +# ifndef _Bool + typedef bool _Bool; /* semi-hackish: C++ has no _Bool; bool is builtin */ +# endif +#endif + +/********** CPython-specific section **********/ +#ifndef PYPY_VERSION + + +#if PY_MAJOR_VERSION >= 3 +# define PyInt_FromLong PyLong_FromLong +#endif + +#define _cffi_from_c_double PyFloat_FromDouble +#define _cffi_from_c_float PyFloat_FromDouble +#define _cffi_from_c_long PyInt_FromLong +#define _cffi_from_c_ulong PyLong_FromUnsignedLong +#define _cffi_from_c_longlong PyLong_FromLongLong +#define _cffi_from_c_ulonglong PyLong_FromUnsignedLongLong +#define _cffi_from_c__Bool PyBool_FromLong + +#define _cffi_to_c_double PyFloat_AsDouble +#define _cffi_to_c_float PyFloat_AsDouble + +#define _cffi_from_c_int(x, type) \ + (((type)-1) > 0 ? /* unsigned */ \ + (sizeof(type) < sizeof(long) ? \ + PyInt_FromLong((long)x) : \ + sizeof(type) == sizeof(long) ? \ + PyLong_FromUnsignedLong((unsigned long)x) : \ + PyLong_FromUnsignedLongLong((unsigned long long)x)) : \ + (sizeof(type) <= sizeof(long) ? \ + PyInt_FromLong((long)x) : \ + PyLong_FromLongLong((long long)x))) + +#define _cffi_to_c_int(o, type) \ + ((type)( \ + sizeof(type) == 1 ? (((type)-1) > 0 ? (type)_cffi_to_c_u8(o) \ + : (type)_cffi_to_c_i8(o)) : \ + sizeof(type) == 2 ? (((type)-1) > 0 ? (type)_cffi_to_c_u16(o) \ + : (type)_cffi_to_c_i16(o)) : \ + sizeof(type) == 4 ? (((type)-1) > 0 ? (type)_cffi_to_c_u32(o) \ + : (type)_cffi_to_c_i32(o)) : \ + sizeof(type) == 8 ? (((type)-1) > 0 ? (type)_cffi_to_c_u64(o) \ + : (type)_cffi_to_c_i64(o)) : \ + (Py_FatalError("unsupported size for type " #type), (type)0))) + +#define _cffi_to_c_i8 \ + ((int(*)(PyObject *))_cffi_exports[1]) +#define _cffi_to_c_u8 \ + ((int(*)(PyObject *))_cffi_exports[2]) +#define _cffi_to_c_i16 \ + ((int(*)(PyObject *))_cffi_exports[3]) +#define _cffi_to_c_u16 \ + ((int(*)(PyObject *))_cffi_exports[4]) +#define _cffi_to_c_i32 \ + ((int(*)(PyObject *))_cffi_exports[5]) +#define _cffi_to_c_u32 \ + ((unsigned int(*)(PyObject *))_cffi_exports[6]) +#define _cffi_to_c_i64 \ + ((long long(*)(PyObject *))_cffi_exports[7]) +#define _cffi_to_c_u64 \ + ((unsigned long long(*)(PyObject *))_cffi_exports[8]) +#define _cffi_to_c_char \ + ((int(*)(PyObject *))_cffi_exports[9]) +#define _cffi_from_c_pointer \ + ((PyObject *(*)(char *, struct _cffi_ctypedescr *))_cffi_exports[10]) +#define _cffi_to_c_pointer \ + ((char *(*)(PyObject *, struct _cffi_ctypedescr *))_cffi_exports[11]) +#define _cffi_get_struct_layout \ + not used any more +#define _cffi_restore_errno \ + ((void(*)(void))_cffi_exports[13]) +#define _cffi_save_errno \ + ((void(*)(void))_cffi_exports[14]) +#define _cffi_from_c_char \ + ((PyObject *(*)(char))_cffi_exports[15]) +#define _cffi_from_c_deref \ + ((PyObject *(*)(char *, struct _cffi_ctypedescr *))_cffi_exports[16]) +#define _cffi_to_c \ + ((int(*)(char *, struct _cffi_ctypedescr *, PyObject *))_cffi_exports[17]) +#define _cffi_from_c_struct \ + ((PyObject *(*)(char *, struct _cffi_ctypedescr *))_cffi_exports[18]) +#define _cffi_to_c_wchar_t \ + ((_cffi_wchar_t(*)(PyObject *))_cffi_exports[19]) +#define _cffi_from_c_wchar_t \ + ((PyObject *(*)(_cffi_wchar_t))_cffi_exports[20]) +#define _cffi_to_c_long_double \ + ((long double(*)(PyObject *))_cffi_exports[21]) +#define _cffi_to_c__Bool \ + ((_Bool(*)(PyObject *))_cffi_exports[22]) +#define _cffi_prepare_pointer_call_argument \ + ((Py_ssize_t(*)(struct _cffi_ctypedescr *, \ + PyObject *, char **))_cffi_exports[23]) +#define _cffi_convert_array_from_object \ + ((int(*)(char *, struct _cffi_ctypedescr *, PyObject *))_cffi_exports[24]) +#define _CFFI_CPIDX 25 +#define _cffi_call_python \ + ((void(*)(struct _cffi_externpy_s *, char *))_cffi_exports[_CFFI_CPIDX]) +#define _cffi_to_c_wchar3216_t \ + ((int(*)(PyObject *))_cffi_exports[26]) +#define _cffi_from_c_wchar3216_t \ + ((PyObject *(*)(int))_cffi_exports[27]) +#define _CFFI_NUM_EXPORTS 28 + +struct _cffi_ctypedescr; + +static void *_cffi_exports[_CFFI_NUM_EXPORTS]; + +#define _cffi_type(index) ( \ + assert((((uintptr_t)_cffi_types[index]) & 1) == 0), \ + (struct _cffi_ctypedescr *)_cffi_types[index]) + +static PyObject *_cffi_init(const char *module_name, Py_ssize_t version, + const struct _cffi_type_context_s *ctx) +{ + PyObject *module, *o_arg, *new_module; + void *raw[] = { + (void *)module_name, + (void *)version, + (void *)_cffi_exports, + (void *)ctx, + }; + + module = PyImport_ImportModule("_cffi_backend"); + if (module == NULL) + goto failure; + + o_arg = PyLong_FromVoidPtr((void *)raw); + if (o_arg == NULL) + goto failure; + + new_module = PyObject_CallMethod( + module, (char *)"_init_cffi_1_0_external_module", (char *)"O", o_arg); + + Py_DECREF(o_arg); + Py_DECREF(module); + return new_module; + + failure: + Py_XDECREF(module); + return NULL; +} + + +#ifdef HAVE_WCHAR_H +typedef wchar_t _cffi_wchar_t; +#else +typedef uint16_t _cffi_wchar_t; /* same random pick as _cffi_backend.c */ +#endif + +_CFFI_UNUSED_FN static uint16_t _cffi_to_c_char16_t(PyObject *o) +{ + if (sizeof(_cffi_wchar_t) == 2) + return (uint16_t)_cffi_to_c_wchar_t(o); + else + return (uint16_t)_cffi_to_c_wchar3216_t(o); +} + +_CFFI_UNUSED_FN static PyObject *_cffi_from_c_char16_t(uint16_t x) +{ + if (sizeof(_cffi_wchar_t) == 2) + return _cffi_from_c_wchar_t((_cffi_wchar_t)x); + else + return _cffi_from_c_wchar3216_t((int)x); +} + +_CFFI_UNUSED_FN static int _cffi_to_c_char32_t(PyObject *o) +{ + if (sizeof(_cffi_wchar_t) == 4) + return (int)_cffi_to_c_wchar_t(o); + else + return (int)_cffi_to_c_wchar3216_t(o); +} + +_CFFI_UNUSED_FN static PyObject *_cffi_from_c_char32_t(unsigned int x) +{ + if (sizeof(_cffi_wchar_t) == 4) + return _cffi_from_c_wchar_t((_cffi_wchar_t)x); + else + return _cffi_from_c_wchar3216_t((int)x); +} + +union _cffi_union_alignment_u { + unsigned char m_char; + unsigned short m_short; + unsigned int m_int; + unsigned long m_long; + unsigned long long m_longlong; + float m_float; + double m_double; + long double m_longdouble; +}; + +struct _cffi_freeme_s { + struct _cffi_freeme_s *next; + union _cffi_union_alignment_u alignment; +}; + +_CFFI_UNUSED_FN static int +_cffi_convert_array_argument(struct _cffi_ctypedescr *ctptr, PyObject *arg, + char **output_data, Py_ssize_t datasize, + struct _cffi_freeme_s **freeme) +{ + char *p; + if (datasize < 0) + return -1; + + p = *output_data; + if (p == NULL) { + struct _cffi_freeme_s *fp = (struct _cffi_freeme_s *)PyObject_Malloc( + offsetof(struct _cffi_freeme_s, alignment) + (size_t)datasize); + if (fp == NULL) + return -1; + fp->next = *freeme; + *freeme = fp; + p = *output_data = (char *)&fp->alignment; + } + memset((void *)p, 0, (size_t)datasize); + return _cffi_convert_array_from_object(p, ctptr, arg); +} + +_CFFI_UNUSED_FN static void +_cffi_free_array_arguments(struct _cffi_freeme_s *freeme) +{ + do { + void *p = (void *)freeme; + freeme = freeme->next; + PyObject_Free(p); + } while (freeme != NULL); +} + +/********** end CPython-specific section **********/ +#else +_CFFI_UNUSED_FN +static void (*_cffi_call_python_org)(struct _cffi_externpy_s *, char *); +# define _cffi_call_python _cffi_call_python_org +#endif + + +#define _cffi_array_len(array) (sizeof(array) / sizeof((array)[0])) + +#define _cffi_prim_int(size, sign) \ + ((size) == 1 ? ((sign) ? _CFFI_PRIM_INT8 : _CFFI_PRIM_UINT8) : \ + (size) == 2 ? ((sign) ? _CFFI_PRIM_INT16 : _CFFI_PRIM_UINT16) : \ + (size) == 4 ? ((sign) ? _CFFI_PRIM_INT32 : _CFFI_PRIM_UINT32) : \ + (size) == 8 ? ((sign) ? _CFFI_PRIM_INT64 : _CFFI_PRIM_UINT64) : \ + _CFFI__UNKNOWN_PRIM) + +#define _cffi_prim_float(size) \ + ((size) == sizeof(float) ? _CFFI_PRIM_FLOAT : \ + (size) == sizeof(double) ? _CFFI_PRIM_DOUBLE : \ + (size) == sizeof(long double) ? _CFFI__UNKNOWN_LONG_DOUBLE : \ + _CFFI__UNKNOWN_FLOAT_PRIM) + +#define _cffi_check_int(got, got_nonpos, expected) \ + ((got_nonpos) == (expected <= 0) && \ + (got) == (unsigned long long)expected) + +#ifdef MS_WIN32 +# define _cffi_stdcall __stdcall +#else +# define _cffi_stdcall /* nothing */ +#endif + +#ifdef __cplusplus +} +#endif diff --git a/code/.venv/lib/python3.12/site-packages/cffi/_embedding.h b/code/.venv/lib/python3.12/site-packages/cffi/_embedding.h new file mode 100644 index 0000000..0eeeea8 --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/cffi/_embedding.h @@ -0,0 +1,550 @@ + +/***** Support code for embedding *****/ + +#ifdef __cplusplus +extern "C" { +#endif + + +#if defined(_WIN32) +# define CFFI_DLLEXPORT __declspec(dllexport) +#elif defined(__GNUC__) +# define CFFI_DLLEXPORT __attribute__((visibility("default"))) +#else +# define CFFI_DLLEXPORT /* nothing */ +#endif + + +/* There are two global variables of type _cffi_call_python_fnptr: + + * _cffi_call_python, which we declare just below, is the one called + by ``extern "Python"`` implementations. + + * _cffi_call_python_org, which on CPython is actually part of the + _cffi_exports[] array, is the function pointer copied from + _cffi_backend. If _cffi_start_python() fails, then this is set + to NULL; otherwise, it should never be NULL. + + After initialization is complete, both are equal. However, the + first one remains equal to &_cffi_start_and_call_python until the + very end of initialization, when we are (or should be) sure that + concurrent threads also see a completely initialized world, and + only then is it changed. +*/ +#undef _cffi_call_python +typedef void (*_cffi_call_python_fnptr)(struct _cffi_externpy_s *, char *); +static void _cffi_start_and_call_python(struct _cffi_externpy_s *, char *); +static _cffi_call_python_fnptr _cffi_call_python = &_cffi_start_and_call_python; + + +#ifndef _MSC_VER + /* --- Assuming a GCC not infinitely old --- */ +# define cffi_compare_and_swap(l,o,n) __sync_bool_compare_and_swap(l,o,n) +# define cffi_write_barrier() __sync_synchronize() +# if !defined(__amd64__) && !defined(__x86_64__) && \ + !defined(__i386__) && !defined(__i386) +# define cffi_read_barrier() __sync_synchronize() +# else +# define cffi_read_barrier() (void)0 +# endif +#else + /* --- Windows threads version --- */ +# include +# define cffi_compare_and_swap(l,o,n) \ + (InterlockedCompareExchangePointer(l,n,o) == (o)) +# define cffi_write_barrier() InterlockedCompareExchange(&_cffi_dummy,0,0) +# define cffi_read_barrier() (void)0 +static volatile LONG _cffi_dummy; +#endif + +#ifdef WITH_THREAD +# ifndef _MSC_VER +# include + static pthread_mutex_t _cffi_embed_startup_lock; +# else + static CRITICAL_SECTION _cffi_embed_startup_lock; +# endif + static char _cffi_embed_startup_lock_ready = 0; +#endif + +static void _cffi_acquire_reentrant_mutex(void) +{ + static void *volatile lock = NULL; + + while (!cffi_compare_and_swap(&lock, NULL, (void *)1)) { + /* should ideally do a spin loop instruction here, but + hard to do it portably and doesn't really matter I + think: pthread_mutex_init() should be very fast, and + this is only run at start-up anyway. */ + } + +#ifdef WITH_THREAD + if (!_cffi_embed_startup_lock_ready) { +# ifndef _MSC_VER + pthread_mutexattr_t attr; + pthread_mutexattr_init(&attr); + pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); + pthread_mutex_init(&_cffi_embed_startup_lock, &attr); +# else + InitializeCriticalSection(&_cffi_embed_startup_lock); +# endif + _cffi_embed_startup_lock_ready = 1; + } +#endif + + while (!cffi_compare_and_swap(&lock, (void *)1, NULL)) + ; + +#ifndef _MSC_VER + pthread_mutex_lock(&_cffi_embed_startup_lock); +#else + EnterCriticalSection(&_cffi_embed_startup_lock); +#endif +} + +static void _cffi_release_reentrant_mutex(void) +{ +#ifndef _MSC_VER + pthread_mutex_unlock(&_cffi_embed_startup_lock); +#else + LeaveCriticalSection(&_cffi_embed_startup_lock); +#endif +} + + +/********** CPython-specific section **********/ +#ifndef PYPY_VERSION + +#include "_cffi_errors.h" + + +#define _cffi_call_python_org _cffi_exports[_CFFI_CPIDX] + +PyMODINIT_FUNC _CFFI_PYTHON_STARTUP_FUNC(void); /* forward */ + +static void _cffi_py_initialize(void) +{ + /* XXX use initsigs=0, which "skips initialization registration of + signal handlers, which might be useful when Python is + embedded" according to the Python docs. But review and think + if it should be a user-controllable setting. + + XXX we should also give a way to write errors to a buffer + instead of to stderr. + + XXX if importing 'site' fails, CPython (any version) calls + exit(). Should we try to work around this behavior here? + */ + Py_InitializeEx(0); +} + +static int _cffi_initialize_python(void) +{ + /* This initializes Python, imports _cffi_backend, and then the + present .dll/.so is set up as a CPython C extension module. + */ + int result; + PyGILState_STATE state; + PyObject *pycode=NULL, *global_dict=NULL, *x; + PyObject *builtins; + + state = PyGILState_Ensure(); + + /* Call the initxxx() function from the present module. It will + create and initialize us as a CPython extension module, instead + of letting the startup Python code do it---it might reimport + the same .dll/.so and get maybe confused on some platforms. + It might also have troubles locating the .dll/.so again for all + I know. + */ + (void)_CFFI_PYTHON_STARTUP_FUNC(); + if (PyErr_Occurred()) + goto error; + + /* Now run the Python code provided to ffi.embedding_init_code(). + */ + pycode = Py_CompileString(_CFFI_PYTHON_STARTUP_CODE, + "", + Py_file_input); + if (pycode == NULL) + goto error; + global_dict = PyDict_New(); + if (global_dict == NULL) + goto error; + builtins = PyEval_GetBuiltins(); + if (builtins == NULL) + goto error; + if (PyDict_SetItemString(global_dict, "__builtins__", builtins) < 0) + goto error; + x = PyEval_EvalCode( +#if PY_MAJOR_VERSION < 3 + (PyCodeObject *) +#endif + pycode, global_dict, global_dict); + if (x == NULL) + goto error; + Py_DECREF(x); + + /* Done! Now if we've been called from + _cffi_start_and_call_python() in an ``extern "Python"``, we can + only hope that the Python code did correctly set up the + corresponding @ffi.def_extern() function. Otherwise, the + general logic of ``extern "Python"`` functions (inside the + _cffi_backend module) will find that the reference is still + missing and print an error. + */ + result = 0; + done: + Py_XDECREF(pycode); + Py_XDECREF(global_dict); + PyGILState_Release(state); + return result; + + error:; + { + /* Print as much information as potentially useful. + Debugging load-time failures with embedding is not fun + */ + PyObject *ecap; + PyObject *exception, *v, *tb, *f, *modules, *mod; + PyErr_Fetch(&exception, &v, &tb); + ecap = _cffi_start_error_capture(); + f = PySys_GetObject((char *)"stderr"); + if (f != NULL && f != Py_None) { + PyFile_WriteString( + "Failed to initialize the Python-CFFI embedding logic:\n\n", f); + } + + if (exception != NULL) { + PyErr_NormalizeException(&exception, &v, &tb); + PyErr_Display(exception, v, tb); + } + Py_XDECREF(exception); + Py_XDECREF(v); + Py_XDECREF(tb); + + if (f != NULL && f != Py_None) { + PyFile_WriteString("\nFrom: " _CFFI_MODULE_NAME + "\ncompiled with cffi version: 1.17.0" + "\n_cffi_backend module: ", f); + modules = PyImport_GetModuleDict(); + mod = PyDict_GetItemString(modules, "_cffi_backend"); + if (mod == NULL) { + PyFile_WriteString("not loaded", f); + } + else { + v = PyObject_GetAttrString(mod, "__file__"); + PyFile_WriteObject(v, f, 0); + Py_XDECREF(v); + } + PyFile_WriteString("\nsys.path: ", f); + PyFile_WriteObject(PySys_GetObject((char *)"path"), f, 0); + PyFile_WriteString("\n\n", f); + } + _cffi_stop_error_capture(ecap); + } + result = -1; + goto done; +} + +#if PY_VERSION_HEX < 0x03080000 +PyAPI_DATA(char *) _PyParser_TokenNames[]; /* from CPython */ +#endif + +static int _cffi_carefully_make_gil(void) +{ + /* This does the basic initialization of Python. It can be called + completely concurrently from unrelated threads. It assumes + that we don't hold the GIL before (if it exists), and we don't + hold it afterwards. + + (What it really does used to be completely different in Python 2 + and Python 3, with the Python 2 solution avoiding the spin-lock + around the Py_InitializeEx() call. However, after recent changes + to CPython 2.7 (issue #358) it no longer works. So we use the + Python 3 solution everywhere.) + + This initializes Python by calling Py_InitializeEx(). + Important: this must not be called concurrently at all. + So we use a global variable as a simple spin lock. This global + variable must be from 'libpythonX.Y.so', not from this + cffi-based extension module, because it must be shared from + different cffi-based extension modules. + + In Python < 3.8, we choose + _PyParser_TokenNames[0] as a completely arbitrary pointer value + that is never written to. The default is to point to the + string "ENDMARKER". We change it temporarily to point to the + next character in that string. (Yes, I know it's REALLY + obscure.) + + In Python >= 3.8, this string array is no longer writable, so + instead we pick PyCapsuleType.tp_version_tag. We can't change + Python < 3.8 because someone might use a mixture of cffi + embedded modules, some of which were compiled before this file + changed. + + In Python >= 3.12, this stopped working because that particular + tp_version_tag gets modified during interpreter startup. It's + arguably a bad idea before 3.12 too, but again we can't change + that because someone might use a mixture of cffi embedded + modules, and no-one reported a bug so far. In Python >= 3.12 + we go instead for PyCapsuleType.tp_as_buffer, which is supposed + to always be NULL. We write to it temporarily a pointer to + a struct full of NULLs, which is semantically the same. + */ + +#ifdef WITH_THREAD +# if PY_VERSION_HEX < 0x03080000 + char *volatile *lock = (char *volatile *)_PyParser_TokenNames; + char *old_value, *locked_value; + + while (1) { /* spin loop */ + old_value = *lock; + locked_value = old_value + 1; + if (old_value[0] == 'E') { + assert(old_value[1] == 'N'); + if (cffi_compare_and_swap(lock, old_value, locked_value)) + break; + } + else { + assert(old_value[0] == 'N'); + /* should ideally do a spin loop instruction here, but + hard to do it portably and doesn't really matter I + think: PyEval_InitThreads() should be very fast, and + this is only run at start-up anyway. */ + } + } +# else +# if PY_VERSION_HEX < 0x030C0000 + int volatile *lock = (int volatile *)&PyCapsule_Type.tp_version_tag; + int old_value, locked_value = -42; + assert(!(PyCapsule_Type.tp_flags & Py_TPFLAGS_HAVE_VERSION_TAG)); +# else + static struct ebp_s { PyBufferProcs buf; int mark; } empty_buffer_procs; + empty_buffer_procs.mark = -42; + PyBufferProcs *volatile *lock = (PyBufferProcs *volatile *) + &PyCapsule_Type.tp_as_buffer; + PyBufferProcs *old_value, *locked_value = &empty_buffer_procs.buf; +# endif + + while (1) { /* spin loop */ + old_value = *lock; + if (old_value == 0) { + if (cffi_compare_and_swap(lock, old_value, locked_value)) + break; + } + else { +# if PY_VERSION_HEX < 0x030C0000 + assert(old_value == locked_value); +# else + /* The pointer should point to a possibly different + empty_buffer_procs from another C extension module */ + assert(((struct ebp_s *)old_value)->mark == -42); +# endif + /* should ideally do a spin loop instruction here, but + hard to do it portably and doesn't really matter I + think: PyEval_InitThreads() should be very fast, and + this is only run at start-up anyway. */ + } + } +# endif +#endif + + /* call Py_InitializeEx() */ + if (!Py_IsInitialized()) { + _cffi_py_initialize(); +#if PY_VERSION_HEX < 0x03070000 + PyEval_InitThreads(); +#endif + PyEval_SaveThread(); /* release the GIL */ + /* the returned tstate must be the one that has been stored into the + autoTLSkey by _PyGILState_Init() called from Py_Initialize(). */ + } + else { +#if PY_VERSION_HEX < 0x03070000 + /* PyEval_InitThreads() is always a no-op from CPython 3.7 */ + PyGILState_STATE state = PyGILState_Ensure(); + PyEval_InitThreads(); + PyGILState_Release(state); +#endif + } + +#ifdef WITH_THREAD + /* release the lock */ + while (!cffi_compare_and_swap(lock, locked_value, old_value)) + ; +#endif + + return 0; +} + +/********** end CPython-specific section **********/ + + +#else + + +/********** PyPy-specific section **********/ + +PyMODINIT_FUNC _CFFI_PYTHON_STARTUP_FUNC(const void *[]); /* forward */ + +static struct _cffi_pypy_init_s { + const char *name; + void *func; /* function pointer */ + const char *code; +} _cffi_pypy_init = { + _CFFI_MODULE_NAME, + _CFFI_PYTHON_STARTUP_FUNC, + _CFFI_PYTHON_STARTUP_CODE, +}; + +extern int pypy_carefully_make_gil(const char *); +extern int pypy_init_embedded_cffi_module(int, struct _cffi_pypy_init_s *); + +static int _cffi_carefully_make_gil(void) +{ + return pypy_carefully_make_gil(_CFFI_MODULE_NAME); +} + +static int _cffi_initialize_python(void) +{ + return pypy_init_embedded_cffi_module(0xB011, &_cffi_pypy_init); +} + +/********** end PyPy-specific section **********/ + + +#endif + + +#ifdef __GNUC__ +__attribute__((noinline)) +#endif +static _cffi_call_python_fnptr _cffi_start_python(void) +{ + /* Delicate logic to initialize Python. This function can be + called multiple times concurrently, e.g. when the process calls + its first ``extern "Python"`` functions in multiple threads at + once. It can also be called recursively, in which case we must + ignore it. We also have to consider what occurs if several + different cffi-based extensions reach this code in parallel + threads---it is a different copy of the code, then, and we + can't have any shared global variable unless it comes from + 'libpythonX.Y.so'. + + Idea: + + * _cffi_carefully_make_gil(): "carefully" call + PyEval_InitThreads() (possibly with Py_InitializeEx() first). + + * then we use a (local) custom lock to make sure that a call to this + cffi-based extension will wait if another call to the *same* + extension is running the initialization in another thread. + It is reentrant, so that a recursive call will not block, but + only one from a different thread. + + * then we grab the GIL and (Python 2) we call Py_InitializeEx(). + At this point, concurrent calls to Py_InitializeEx() are not + possible: we have the GIL. + + * do the rest of the specific initialization, which may + temporarily release the GIL but not the custom lock. + Only release the custom lock when we are done. + */ + static char called = 0; + + if (_cffi_carefully_make_gil() != 0) + return NULL; + + _cffi_acquire_reentrant_mutex(); + + /* Here the GIL exists, but we don't have it. We're only protected + from concurrency by the reentrant mutex. */ + + /* This file only initializes the embedded module once, the first + time this is called, even if there are subinterpreters. */ + if (!called) { + called = 1; /* invoke _cffi_initialize_python() only once, + but don't set '_cffi_call_python' right now, + otherwise concurrent threads won't call + this function at all (we need them to wait) */ + if (_cffi_initialize_python() == 0) { + /* now initialization is finished. Switch to the fast-path. */ + + /* We would like nobody to see the new value of + '_cffi_call_python' without also seeing the rest of the + data initialized. However, this is not possible. But + the new value of '_cffi_call_python' is the function + 'cffi_call_python()' from _cffi_backend. So: */ + cffi_write_barrier(); + /* ^^^ we put a write barrier here, and a corresponding + read barrier at the start of cffi_call_python(). This + ensures that after that read barrier, we see everything + done here before the write barrier. + */ + + assert(_cffi_call_python_org != NULL); + _cffi_call_python = (_cffi_call_python_fnptr)_cffi_call_python_org; + } + else { + /* initialization failed. Reset this to NULL, even if it was + already set to some other value. Future calls to + _cffi_start_python() are still forced to occur, and will + always return NULL from now on. */ + _cffi_call_python_org = NULL; + } + } + + _cffi_release_reentrant_mutex(); + + return (_cffi_call_python_fnptr)_cffi_call_python_org; +} + +static +void _cffi_start_and_call_python(struct _cffi_externpy_s *externpy, char *args) +{ + _cffi_call_python_fnptr fnptr; + int current_err = errno; +#ifdef _MSC_VER + int current_lasterr = GetLastError(); +#endif + fnptr = _cffi_start_python(); + if (fnptr == NULL) { + fprintf(stderr, "function %s() called, but initialization code " + "failed. Returning 0.\n", externpy->name); + memset(args, 0, externpy->size_of_result); + } +#ifdef _MSC_VER + SetLastError(current_lasterr); +#endif + errno = current_err; + + if (fnptr != NULL) + fnptr(externpy, args); +} + + +/* The cffi_start_python() function makes sure Python is initialized + and our cffi module is set up. It can be called manually from the + user C code. The same effect is obtained automatically from any + dll-exported ``extern "Python"`` function. This function returns + -1 if initialization failed, 0 if all is OK. */ +_CFFI_UNUSED_FN +static int cffi_start_python(void) +{ + if (_cffi_call_python == &_cffi_start_and_call_python) { + if (_cffi_start_python() == NULL) + return -1; + } + cffi_read_barrier(); + return 0; +} + +#undef cffi_compare_and_swap +#undef cffi_write_barrier +#undef cffi_read_barrier + +#ifdef __cplusplus +} +#endif diff --git a/code/.venv/lib/python3.12/site-packages/cffi/_imp_emulation.py b/code/.venv/lib/python3.12/site-packages/cffi/_imp_emulation.py new file mode 100644 index 0000000..136abdd --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/cffi/_imp_emulation.py @@ -0,0 +1,83 @@ + +try: + # this works on Python < 3.12 + from imp import * + +except ImportError: + # this is a limited emulation for Python >= 3.12. + # Note that this is used only for tests or for the old ffi.verify(). + # This is copied from the source code of Python 3.11. + + from _imp import (acquire_lock, release_lock, + is_builtin, is_frozen) + + from importlib._bootstrap import _load + + from importlib import machinery + import os + import sys + import tokenize + + SEARCH_ERROR = 0 + PY_SOURCE = 1 + PY_COMPILED = 2 + C_EXTENSION = 3 + PY_RESOURCE = 4 + PKG_DIRECTORY = 5 + C_BUILTIN = 6 + PY_FROZEN = 7 + PY_CODERESOURCE = 8 + IMP_HOOK = 9 + + def get_suffixes(): + extensions = [(s, 'rb', C_EXTENSION) + for s in machinery.EXTENSION_SUFFIXES] + source = [(s, 'r', PY_SOURCE) for s in machinery.SOURCE_SUFFIXES] + bytecode = [(s, 'rb', PY_COMPILED) for s in machinery.BYTECODE_SUFFIXES] + return extensions + source + bytecode + + def find_module(name, path=None): + if not isinstance(name, str): + raise TypeError("'name' must be a str, not {}".format(type(name))) + elif not isinstance(path, (type(None), list)): + # Backwards-compatibility + raise RuntimeError("'path' must be None or a list, " + "not {}".format(type(path))) + + if path is None: + if is_builtin(name): + return None, None, ('', '', C_BUILTIN) + elif is_frozen(name): + return None, None, ('', '', PY_FROZEN) + else: + path = sys.path + + for entry in path: + package_directory = os.path.join(entry, name) + for suffix in ['.py', machinery.BYTECODE_SUFFIXES[0]]: + package_file_name = '__init__' + suffix + file_path = os.path.join(package_directory, package_file_name) + if os.path.isfile(file_path): + return None, package_directory, ('', '', PKG_DIRECTORY) + for suffix, mode, type_ in get_suffixes(): + file_name = name + suffix + file_path = os.path.join(entry, file_name) + if os.path.isfile(file_path): + break + else: + continue + break # Break out of outer loop when breaking out of inner loop. + else: + raise ImportError(name, name=name) + + encoding = None + if 'b' not in mode: + with open(file_path, 'rb') as file: + encoding = tokenize.detect_encoding(file.readline)[0] + file = open(file_path, mode, encoding=encoding) + return file, file_path, (suffix, mode, type_) + + def load_dynamic(name, path, file=None): + loader = machinery.ExtensionFileLoader(name, path) + spec = machinery.ModuleSpec(name=name, loader=loader, origin=path) + return _load(spec) diff --git a/code/.venv/lib/python3.12/site-packages/cffi/_shimmed_dist_utils.py b/code/.venv/lib/python3.12/site-packages/cffi/_shimmed_dist_utils.py new file mode 100644 index 0000000..611bf40 --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/cffi/_shimmed_dist_utils.py @@ -0,0 +1,41 @@ +""" +Temporary shim module to indirect the bits of distutils we need from setuptools/distutils while providing useful +error messages beyond `No module named 'distutils' on Python >= 3.12, or when setuptools' vendored distutils is broken. + +This is a compromise to avoid a hard-dep on setuptools for Python >= 3.12, since many users don't need runtime compilation support from CFFI. +""" +import sys + +try: + # import setuptools first; this is the most robust way to ensure its embedded distutils is available + # (the .pth shim should usually work, but this is even more robust) + import setuptools +except Exception as ex: + if sys.version_info >= (3, 12): + # Python 3.12 has no built-in distutils to fall back on, so any import problem is fatal + raise Exception("This CFFI feature requires setuptools on Python >= 3.12. The setuptools module is missing or non-functional.") from ex + + # silently ignore on older Pythons (support fallback to stdlib distutils where available) +else: + del setuptools + +try: + # bring in just the bits of distutils we need, whether they really came from setuptools or stdlib-embedded distutils + from distutils import log, sysconfig + from distutils.ccompiler import CCompiler + from distutils.command.build_ext import build_ext + from distutils.core import Distribution, Extension + from distutils.dir_util import mkpath + from distutils.errors import DistutilsSetupError, CompileError, LinkError + from distutils.log import set_threshold, set_verbosity + + if sys.platform == 'win32': + from distutils.msvc9compiler import MSVCCompiler +except Exception as ex: + if sys.version_info >= (3, 12): + raise Exception("This CFFI feature requires setuptools on Python >= 3.12. Please install the setuptools package.") from ex + + # anything older, just let the underlying distutils import error fly + raise Exception("This CFFI feature requires distutils. Please install the distutils or setuptools package.") from ex + +del sys diff --git a/code/.venv/lib/python3.12/site-packages/cffi/api.py b/code/.venv/lib/python3.12/site-packages/cffi/api.py new file mode 100644 index 0000000..edeb792 --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/cffi/api.py @@ -0,0 +1,965 @@ +import sys, types +from .lock import allocate_lock +from .error import CDefError +from . import model + +try: + callable +except NameError: + # Python 3.1 + from collections import Callable + callable = lambda x: isinstance(x, Callable) + +try: + basestring +except NameError: + # Python 3.x + basestring = str + +_unspecified = object() + + + +class FFI(object): + r''' + The main top-level class that you instantiate once, or once per module. + + Example usage: + + ffi = FFI() + ffi.cdef(""" + int printf(const char *, ...); + """) + + C = ffi.dlopen(None) # standard library + -or- + C = ffi.verify() # use a C compiler: verify the decl above is right + + C.printf("hello, %s!\n", ffi.new("char[]", "world")) + ''' + + def __init__(self, backend=None): + """Create an FFI instance. The 'backend' argument is used to + select a non-default backend, mostly for tests. + """ + if backend is None: + # You need PyPy (>= 2.0 beta), or a CPython (>= 2.6) with + # _cffi_backend.so compiled. + import _cffi_backend as backend + from . import __version__ + if backend.__version__ != __version__: + # bad version! Try to be as explicit as possible. + if hasattr(backend, '__file__'): + # CPython + raise Exception("Version mismatch: this is the 'cffi' package version %s, located in %r. When we import the top-level '_cffi_backend' extension module, we get version %s, located in %r. The two versions should be equal; check your installation." % ( + __version__, __file__, + backend.__version__, backend.__file__)) + else: + # PyPy + raise Exception("Version mismatch: this is the 'cffi' package version %s, located in %r. This interpreter comes with a built-in '_cffi_backend' module, which is version %s. The two versions should be equal; check your installation." % ( + __version__, __file__, backend.__version__)) + # (If you insist you can also try to pass the option + # 'backend=backend_ctypes.CTypesBackend()', but don't + # rely on it! It's probably not going to work well.) + + from . import cparser + self._backend = backend + self._lock = allocate_lock() + self._parser = cparser.Parser() + self._cached_btypes = {} + self._parsed_types = types.ModuleType('parsed_types').__dict__ + self._new_types = types.ModuleType('new_types').__dict__ + self._function_caches = [] + self._libraries = [] + self._cdefsources = [] + self._included_ffis = [] + self._windows_unicode = None + self._init_once_cache = {} + self._cdef_version = None + self._embedding = None + self._typecache = model.get_typecache(backend) + if hasattr(backend, 'set_ffi'): + backend.set_ffi(self) + for name in list(backend.__dict__): + if name.startswith('RTLD_'): + setattr(self, name, getattr(backend, name)) + # + with self._lock: + self.BVoidP = self._get_cached_btype(model.voidp_type) + self.BCharA = self._get_cached_btype(model.char_array_type) + if isinstance(backend, types.ModuleType): + # _cffi_backend: attach these constants to the class + if not hasattr(FFI, 'NULL'): + FFI.NULL = self.cast(self.BVoidP, 0) + FFI.CData, FFI.CType = backend._get_types() + else: + # ctypes backend: attach these constants to the instance + self.NULL = self.cast(self.BVoidP, 0) + self.CData, self.CType = backend._get_types() + self.buffer = backend.buffer + + def cdef(self, csource, override=False, packed=False, pack=None): + """Parse the given C source. This registers all declared functions, + types, and global variables. The functions and global variables can + then be accessed via either 'ffi.dlopen()' or 'ffi.verify()'. + The types can be used in 'ffi.new()' and other functions. + If 'packed' is specified as True, all structs declared inside this + cdef are packed, i.e. laid out without any field alignment at all. + Alternatively, 'pack' can be a small integer, and requests for + alignment greater than that are ignored (pack=1 is equivalent to + packed=True). + """ + self._cdef(csource, override=override, packed=packed, pack=pack) + + def embedding_api(self, csource, packed=False, pack=None): + self._cdef(csource, packed=packed, pack=pack, dllexport=True) + if self._embedding is None: + self._embedding = '' + + def _cdef(self, csource, override=False, **options): + if not isinstance(csource, str): # unicode, on Python 2 + if not isinstance(csource, basestring): + raise TypeError("cdef() argument must be a string") + csource = csource.encode('ascii') + with self._lock: + self._cdef_version = object() + self._parser.parse(csource, override=override, **options) + self._cdefsources.append(csource) + if override: + for cache in self._function_caches: + cache.clear() + finishlist = self._parser._recomplete + if finishlist: + self._parser._recomplete = [] + for tp in finishlist: + tp.finish_backend_type(self, finishlist) + + def dlopen(self, name, flags=0): + """Load and return a dynamic library identified by 'name'. + The standard C library can be loaded by passing None. + Note that functions and types declared by 'ffi.cdef()' are not + linked to a particular library, just like C headers; in the + library we only look for the actual (untyped) symbols. + """ + if not (isinstance(name, basestring) or + name is None or + isinstance(name, self.CData)): + raise TypeError("dlopen(name): name must be a file name, None, " + "or an already-opened 'void *' handle") + with self._lock: + lib, function_cache = _make_ffi_library(self, name, flags) + self._function_caches.append(function_cache) + self._libraries.append(lib) + return lib + + def dlclose(self, lib): + """Close a library obtained with ffi.dlopen(). After this call, + access to functions or variables from the library will fail + (possibly with a segmentation fault). + """ + type(lib).__cffi_close__(lib) + + def _typeof_locked(self, cdecl): + # call me with the lock! + key = cdecl + if key in self._parsed_types: + return self._parsed_types[key] + # + if not isinstance(cdecl, str): # unicode, on Python 2 + cdecl = cdecl.encode('ascii') + # + type = self._parser.parse_type(cdecl) + really_a_function_type = type.is_raw_function + if really_a_function_type: + type = type.as_function_pointer() + btype = self._get_cached_btype(type) + result = btype, really_a_function_type + self._parsed_types[key] = result + return result + + def _typeof(self, cdecl, consider_function_as_funcptr=False): + # string -> ctype object + try: + result = self._parsed_types[cdecl] + except KeyError: + with self._lock: + result = self._typeof_locked(cdecl) + # + btype, really_a_function_type = result + if really_a_function_type and not consider_function_as_funcptr: + raise CDefError("the type %r is a function type, not a " + "pointer-to-function type" % (cdecl,)) + return btype + + def typeof(self, cdecl): + """Parse the C type given as a string and return the + corresponding object. + It can also be used on 'cdata' instance to get its C type. + """ + if isinstance(cdecl, basestring): + return self._typeof(cdecl) + if isinstance(cdecl, self.CData): + return self._backend.typeof(cdecl) + if isinstance(cdecl, types.BuiltinFunctionType): + res = _builtin_function_type(cdecl) + if res is not None: + return res + if (isinstance(cdecl, types.FunctionType) + and hasattr(cdecl, '_cffi_base_type')): + with self._lock: + return self._get_cached_btype(cdecl._cffi_base_type) + raise TypeError(type(cdecl)) + + def sizeof(self, cdecl): + """Return the size in bytes of the argument. It can be a + string naming a C type, or a 'cdata' instance. + """ + if isinstance(cdecl, basestring): + BType = self._typeof(cdecl) + return self._backend.sizeof(BType) + else: + return self._backend.sizeof(cdecl) + + def alignof(self, cdecl): + """Return the natural alignment size in bytes of the C type + given as a string. + """ + if isinstance(cdecl, basestring): + cdecl = self._typeof(cdecl) + return self._backend.alignof(cdecl) + + def offsetof(self, cdecl, *fields_or_indexes): + """Return the offset of the named field inside the given + structure or array, which must be given as a C type name. + You can give several field names in case of nested structures. + You can also give numeric values which correspond to array + items, in case of an array type. + """ + if isinstance(cdecl, basestring): + cdecl = self._typeof(cdecl) + return self._typeoffsetof(cdecl, *fields_or_indexes)[1] + + def new(self, cdecl, init=None): + """Allocate an instance according to the specified C type and + return a pointer to it. The specified C type must be either a + pointer or an array: ``new('X *')`` allocates an X and returns + a pointer to it, whereas ``new('X[n]')`` allocates an array of + n X'es and returns an array referencing it (which works + mostly like a pointer, like in C). You can also use + ``new('X[]', n)`` to allocate an array of a non-constant + length n. + + The memory is initialized following the rules of declaring a + global variable in C: by default it is zero-initialized, but + an explicit initializer can be given which can be used to + fill all or part of the memory. + + When the returned object goes out of scope, the memory + is freed. In other words the returned object has + ownership of the value of type 'cdecl' that it points to. This + means that the raw data can be used as long as this object is + kept alive, but must not be used for a longer time. Be careful + about that when copying the pointer to the memory somewhere + else, e.g. into another structure. + """ + if isinstance(cdecl, basestring): + cdecl = self._typeof(cdecl) + return self._backend.newp(cdecl, init) + + def new_allocator(self, alloc=None, free=None, + should_clear_after_alloc=True): + """Return a new allocator, i.e. a function that behaves like ffi.new() + but uses the provided low-level 'alloc' and 'free' functions. + + 'alloc' is called with the size as argument. If it returns NULL, a + MemoryError is raised. 'free' is called with the result of 'alloc' + as argument. Both can be either Python function or directly C + functions. If 'free' is None, then no free function is called. + If both 'alloc' and 'free' are None, the default is used. + + If 'should_clear_after_alloc' is set to False, then the memory + returned by 'alloc' is assumed to be already cleared (or you are + fine with garbage); otherwise CFFI will clear it. + """ + compiled_ffi = self._backend.FFI() + allocator = compiled_ffi.new_allocator(alloc, free, + should_clear_after_alloc) + def allocate(cdecl, init=None): + if isinstance(cdecl, basestring): + cdecl = self._typeof(cdecl) + return allocator(cdecl, init) + return allocate + + def cast(self, cdecl, source): + """Similar to a C cast: returns an instance of the named C + type initialized with the given 'source'. The source is + casted between integers or pointers of any type. + """ + if isinstance(cdecl, basestring): + cdecl = self._typeof(cdecl) + return self._backend.cast(cdecl, source) + + def string(self, cdata, maxlen=-1): + """Return a Python string (or unicode string) from the 'cdata'. + If 'cdata' is a pointer or array of characters or bytes, returns + the null-terminated string. The returned string extends until + the first null character, or at most 'maxlen' characters. If + 'cdata' is an array then 'maxlen' defaults to its length. + + If 'cdata' is a pointer or array of wchar_t, returns a unicode + string following the same rules. + + If 'cdata' is a single character or byte or a wchar_t, returns + it as a string or unicode string. + + If 'cdata' is an enum, returns the value of the enumerator as a + string, or 'NUMBER' if the value is out of range. + """ + return self._backend.string(cdata, maxlen) + + def unpack(self, cdata, length): + """Unpack an array of C data of the given length, + returning a Python string/unicode/list. + + If 'cdata' is a pointer to 'char', returns a byte string. + It does not stop at the first null. This is equivalent to: + ffi.buffer(cdata, length)[:] + + If 'cdata' is a pointer to 'wchar_t', returns a unicode string. + 'length' is measured in wchar_t's; it is not the size in bytes. + + If 'cdata' is a pointer to anything else, returns a list of + 'length' items. This is a faster equivalent to: + [cdata[i] for i in range(length)] + """ + return self._backend.unpack(cdata, length) + + #def buffer(self, cdata, size=-1): + # """Return a read-write buffer object that references the raw C data + # pointed to by the given 'cdata'. The 'cdata' must be a pointer or + # an array. Can be passed to functions expecting a buffer, or directly + # manipulated with: + # + # buf[:] get a copy of it in a regular string, or + # buf[idx] as a single character + # buf[:] = ... + # buf[idx] = ... change the content + # """ + # note that 'buffer' is a type, set on this instance by __init__ + + def from_buffer(self, cdecl, python_buffer=_unspecified, + require_writable=False): + """Return a cdata of the given type pointing to the data of the + given Python object, which must support the buffer interface. + Note that this is not meant to be used on the built-in types + str or unicode (you can build 'char[]' arrays explicitly) + but only on objects containing large quantities of raw data + in some other format, like 'array.array' or numpy arrays. + + The first argument is optional and default to 'char[]'. + """ + if python_buffer is _unspecified: + cdecl, python_buffer = self.BCharA, cdecl + elif isinstance(cdecl, basestring): + cdecl = self._typeof(cdecl) + return self._backend.from_buffer(cdecl, python_buffer, + require_writable) + + def memmove(self, dest, src, n): + """ffi.memmove(dest, src, n) copies n bytes of memory from src to dest. + + Like the C function memmove(), the memory areas may overlap; + apart from that it behaves like the C function memcpy(). + + 'src' can be any cdata ptr or array, or any Python buffer object. + 'dest' can be any cdata ptr or array, or a writable Python buffer + object. The size to copy, 'n', is always measured in bytes. + + Unlike other methods, this one supports all Python buffer including + byte strings and bytearrays---but it still does not support + non-contiguous buffers. + """ + return self._backend.memmove(dest, src, n) + + def callback(self, cdecl, python_callable=None, error=None, onerror=None): + """Return a callback object or a decorator making such a + callback object. 'cdecl' must name a C function pointer type. + The callback invokes the specified 'python_callable' (which may + be provided either directly or via a decorator). Important: the + callback object must be manually kept alive for as long as the + callback may be invoked from the C level. + """ + def callback_decorator_wrap(python_callable): + if not callable(python_callable): + raise TypeError("the 'python_callable' argument " + "is not callable") + return self._backend.callback(cdecl, python_callable, + error, onerror) + if isinstance(cdecl, basestring): + cdecl = self._typeof(cdecl, consider_function_as_funcptr=True) + if python_callable is None: + return callback_decorator_wrap # decorator mode + else: + return callback_decorator_wrap(python_callable) # direct mode + + def getctype(self, cdecl, replace_with=''): + """Return a string giving the C type 'cdecl', which may be itself + a string or a object. If 'replace_with' is given, it gives + extra text to append (or insert for more complicated C types), like + a variable name, or '*' to get actually the C type 'pointer-to-cdecl'. + """ + if isinstance(cdecl, basestring): + cdecl = self._typeof(cdecl) + replace_with = replace_with.strip() + if (replace_with.startswith('*') + and '&[' in self._backend.getcname(cdecl, '&')): + replace_with = '(%s)' % replace_with + elif replace_with and not replace_with[0] in '[(': + replace_with = ' ' + replace_with + return self._backend.getcname(cdecl, replace_with) + + def gc(self, cdata, destructor, size=0): + """Return a new cdata object that points to the same + data. Later, when this new cdata object is garbage-collected, + 'destructor(old_cdata_object)' will be called. + + The optional 'size' gives an estimate of the size, used to + trigger the garbage collection more eagerly. So far only used + on PyPy. It tells the GC that the returned object keeps alive + roughly 'size' bytes of external memory. + """ + return self._backend.gcp(cdata, destructor, size) + + def _get_cached_btype(self, type): + assert self._lock.acquire(False) is False + # call me with the lock! + try: + BType = self._cached_btypes[type] + except KeyError: + finishlist = [] + BType = type.get_cached_btype(self, finishlist) + for type in finishlist: + type.finish_backend_type(self, finishlist) + return BType + + def verify(self, source='', tmpdir=None, **kwargs): + """Verify that the current ffi signatures compile on this + machine, and return a dynamic library object. The dynamic + library can be used to call functions and access global + variables declared in this 'ffi'. The library is compiled + by the C compiler: it gives you C-level API compatibility + (including calling macros). This is unlike 'ffi.dlopen()', + which requires binary compatibility in the signatures. + """ + from .verifier import Verifier, _caller_dir_pycache + # + # If set_unicode(True) was called, insert the UNICODE and + # _UNICODE macro declarations + if self._windows_unicode: + self._apply_windows_unicode(kwargs) + # + # Set the tmpdir here, and not in Verifier.__init__: it picks + # up the caller's directory, which we want to be the caller of + # ffi.verify(), as opposed to the caller of Veritier(). + tmpdir = tmpdir or _caller_dir_pycache() + # + # Make a Verifier() and use it to load the library. + self.verifier = Verifier(self, source, tmpdir, **kwargs) + lib = self.verifier.load_library() + # + # Save the loaded library for keep-alive purposes, even + # if the caller doesn't keep it alive itself (it should). + self._libraries.append(lib) + return lib + + def _get_errno(self): + return self._backend.get_errno() + def _set_errno(self, errno): + self._backend.set_errno(errno) + errno = property(_get_errno, _set_errno, None, + "the value of 'errno' from/to the C calls") + + def getwinerror(self, code=-1): + return self._backend.getwinerror(code) + + def _pointer_to(self, ctype): + with self._lock: + return model.pointer_cache(self, ctype) + + def addressof(self, cdata, *fields_or_indexes): + """Return the address of a . + If 'fields_or_indexes' are given, returns the address of that + field or array item in the structure or array, recursively in + case of nested structures. + """ + try: + ctype = self._backend.typeof(cdata) + except TypeError: + if '__addressof__' in type(cdata).__dict__: + return type(cdata).__addressof__(cdata, *fields_or_indexes) + raise + if fields_or_indexes: + ctype, offset = self._typeoffsetof(ctype, *fields_or_indexes) + else: + if ctype.kind == "pointer": + raise TypeError("addressof(pointer)") + offset = 0 + ctypeptr = self._pointer_to(ctype) + return self._backend.rawaddressof(ctypeptr, cdata, offset) + + def _typeoffsetof(self, ctype, field_or_index, *fields_or_indexes): + ctype, offset = self._backend.typeoffsetof(ctype, field_or_index) + for field1 in fields_or_indexes: + ctype, offset1 = self._backend.typeoffsetof(ctype, field1, 1) + offset += offset1 + return ctype, offset + + def include(self, ffi_to_include): + """Includes the typedefs, structs, unions and enums defined + in another FFI instance. Usage is similar to a #include in C, + where a part of the program might include types defined in + another part for its own usage. Note that the include() + method has no effect on functions, constants and global + variables, which must anyway be accessed directly from the + lib object returned by the original FFI instance. + """ + if not isinstance(ffi_to_include, FFI): + raise TypeError("ffi.include() expects an argument that is also of" + " type cffi.FFI, not %r" % ( + type(ffi_to_include).__name__,)) + if ffi_to_include is self: + raise ValueError("self.include(self)") + with ffi_to_include._lock: + with self._lock: + self._parser.include(ffi_to_include._parser) + self._cdefsources.append('[') + self._cdefsources.extend(ffi_to_include._cdefsources) + self._cdefsources.append(']') + self._included_ffis.append(ffi_to_include) + + def new_handle(self, x): + return self._backend.newp_handle(self.BVoidP, x) + + def from_handle(self, x): + return self._backend.from_handle(x) + + def release(self, x): + self._backend.release(x) + + def set_unicode(self, enabled_flag): + """Windows: if 'enabled_flag' is True, enable the UNICODE and + _UNICODE defines in C, and declare the types like TCHAR and LPTCSTR + to be (pointers to) wchar_t. If 'enabled_flag' is False, + declare these types to be (pointers to) plain 8-bit characters. + This is mostly for backward compatibility; you usually want True. + """ + if self._windows_unicode is not None: + raise ValueError("set_unicode() can only be called once") + enabled_flag = bool(enabled_flag) + if enabled_flag: + self.cdef("typedef wchar_t TBYTE;" + "typedef wchar_t TCHAR;" + "typedef const wchar_t *LPCTSTR;" + "typedef const wchar_t *PCTSTR;" + "typedef wchar_t *LPTSTR;" + "typedef wchar_t *PTSTR;" + "typedef TBYTE *PTBYTE;" + "typedef TCHAR *PTCHAR;") + else: + self.cdef("typedef char TBYTE;" + "typedef char TCHAR;" + "typedef const char *LPCTSTR;" + "typedef const char *PCTSTR;" + "typedef char *LPTSTR;" + "typedef char *PTSTR;" + "typedef TBYTE *PTBYTE;" + "typedef TCHAR *PTCHAR;") + self._windows_unicode = enabled_flag + + def _apply_windows_unicode(self, kwds): + defmacros = kwds.get('define_macros', ()) + if not isinstance(defmacros, (list, tuple)): + raise TypeError("'define_macros' must be a list or tuple") + defmacros = list(defmacros) + [('UNICODE', '1'), + ('_UNICODE', '1')] + kwds['define_macros'] = defmacros + + def _apply_embedding_fix(self, kwds): + # must include an argument like "-lpython2.7" for the compiler + def ensure(key, value): + lst = kwds.setdefault(key, []) + if value not in lst: + lst.append(value) + # + if '__pypy__' in sys.builtin_module_names: + import os + if sys.platform == "win32": + # we need 'libpypy-c.lib'. Current distributions of + # pypy (>= 4.1) contain it as 'libs/python27.lib'. + pythonlib = "python{0[0]}{0[1]}".format(sys.version_info) + if hasattr(sys, 'prefix'): + ensure('library_dirs', os.path.join(sys.prefix, 'libs')) + else: + # we need 'libpypy-c.{so,dylib}', which should be by + # default located in 'sys.prefix/bin' for installed + # systems. + if sys.version_info < (3,): + pythonlib = "pypy-c" + else: + pythonlib = "pypy3-c" + if hasattr(sys, 'prefix'): + ensure('library_dirs', os.path.join(sys.prefix, 'bin')) + # On uninstalled pypy's, the libpypy-c is typically found in + # .../pypy/goal/. + if hasattr(sys, 'prefix'): + ensure('library_dirs', os.path.join(sys.prefix, 'pypy', 'goal')) + else: + if sys.platform == "win32": + template = "python%d%d" + if hasattr(sys, 'gettotalrefcount'): + template += '_d' + else: + try: + import sysconfig + except ImportError: # 2.6 + from cffi._shimmed_dist_utils import sysconfig + template = "python%d.%d" + if sysconfig.get_config_var('DEBUG_EXT'): + template += sysconfig.get_config_var('DEBUG_EXT') + pythonlib = (template % + (sys.hexversion >> 24, (sys.hexversion >> 16) & 0xff)) + if hasattr(sys, 'abiflags'): + pythonlib += sys.abiflags + ensure('libraries', pythonlib) + if sys.platform == "win32": + ensure('extra_link_args', '/MANIFEST') + + def set_source(self, module_name, source, source_extension='.c', **kwds): + import os + if hasattr(self, '_assigned_source'): + raise ValueError("set_source() cannot be called several times " + "per ffi object") + if not isinstance(module_name, basestring): + raise TypeError("'module_name' must be a string") + if os.sep in module_name or (os.altsep and os.altsep in module_name): + raise ValueError("'module_name' must not contain '/': use a dotted " + "name to make a 'package.module' location") + self._assigned_source = (str(module_name), source, + source_extension, kwds) + + def set_source_pkgconfig(self, module_name, pkgconfig_libs, source, + source_extension='.c', **kwds): + from . import pkgconfig + if not isinstance(pkgconfig_libs, list): + raise TypeError("the pkgconfig_libs argument must be a list " + "of package names") + kwds2 = pkgconfig.flags_from_pkgconfig(pkgconfig_libs) + pkgconfig.merge_flags(kwds, kwds2) + self.set_source(module_name, source, source_extension, **kwds) + + def distutils_extension(self, tmpdir='build', verbose=True): + from cffi._shimmed_dist_utils import mkpath + from .recompiler import recompile + # + if not hasattr(self, '_assigned_source'): + if hasattr(self, 'verifier'): # fallback, 'tmpdir' ignored + return self.verifier.get_extension() + raise ValueError("set_source() must be called before" + " distutils_extension()") + module_name, source, source_extension, kwds = self._assigned_source + if source is None: + raise TypeError("distutils_extension() is only for C extension " + "modules, not for dlopen()-style pure Python " + "modules") + mkpath(tmpdir) + ext, updated = recompile(self, module_name, + source, tmpdir=tmpdir, extradir=tmpdir, + source_extension=source_extension, + call_c_compiler=False, **kwds) + if verbose: + if updated: + sys.stderr.write("regenerated: %r\n" % (ext.sources[0],)) + else: + sys.stderr.write("not modified: %r\n" % (ext.sources[0],)) + return ext + + def emit_c_code(self, filename): + from .recompiler import recompile + # + if not hasattr(self, '_assigned_source'): + raise ValueError("set_source() must be called before emit_c_code()") + module_name, source, source_extension, kwds = self._assigned_source + if source is None: + raise TypeError("emit_c_code() is only for C extension modules, " + "not for dlopen()-style pure Python modules") + recompile(self, module_name, source, + c_file=filename, call_c_compiler=False, **kwds) + + def emit_python_code(self, filename): + from .recompiler import recompile + # + if not hasattr(self, '_assigned_source'): + raise ValueError("set_source() must be called before emit_c_code()") + module_name, source, source_extension, kwds = self._assigned_source + if source is not None: + raise TypeError("emit_python_code() is only for dlopen()-style " + "pure Python modules, not for C extension modules") + recompile(self, module_name, source, + c_file=filename, call_c_compiler=False, **kwds) + + def compile(self, tmpdir='.', verbose=0, target=None, debug=None): + """The 'target' argument gives the final file name of the + compiled DLL. Use '*' to force distutils' choice, suitable for + regular CPython C API modules. Use a file name ending in '.*' + to ask for the system's default extension for dynamic libraries + (.so/.dll/.dylib). + + The default is '*' when building a non-embedded C API extension, + and (module_name + '.*') when building an embedded library. + """ + from .recompiler import recompile + # + if not hasattr(self, '_assigned_source'): + raise ValueError("set_source() must be called before compile()") + module_name, source, source_extension, kwds = self._assigned_source + return recompile(self, module_name, source, tmpdir=tmpdir, + target=target, source_extension=source_extension, + compiler_verbose=verbose, debug=debug, **kwds) + + def init_once(self, func, tag): + # Read _init_once_cache[tag], which is either (False, lock) if + # we're calling the function now in some thread, or (True, result). + # Don't call setdefault() in most cases, to avoid allocating and + # immediately freeing a lock; but still use setdefaut() to avoid + # races. + try: + x = self._init_once_cache[tag] + except KeyError: + x = self._init_once_cache.setdefault(tag, (False, allocate_lock())) + # Common case: we got (True, result), so we return the result. + if x[0]: + return x[1] + # Else, it's a lock. Acquire it to serialize the following tests. + with x[1]: + # Read again from _init_once_cache the current status. + x = self._init_once_cache[tag] + if x[0]: + return x[1] + # Call the function and store the result back. + result = func() + self._init_once_cache[tag] = (True, result) + return result + + def embedding_init_code(self, pysource): + if self._embedding: + raise ValueError("embedding_init_code() can only be called once") + # fix 'pysource' before it gets dumped into the C file: + # - remove empty lines at the beginning, so it starts at "line 1" + # - dedent, if all non-empty lines are indented + # - check for SyntaxErrors + import re + match = re.match(r'\s*\n', pysource) + if match: + pysource = pysource[match.end():] + lines = pysource.splitlines() or [''] + prefix = re.match(r'\s*', lines[0]).group() + for i in range(1, len(lines)): + line = lines[i] + if line.rstrip(): + while not line.startswith(prefix): + prefix = prefix[:-1] + i = len(prefix) + lines = [line[i:]+'\n' for line in lines] + pysource = ''.join(lines) + # + compile(pysource, "cffi_init", "exec") + # + self._embedding = pysource + + def def_extern(self, *args, **kwds): + raise ValueError("ffi.def_extern() is only available on API-mode FFI " + "objects") + + def list_types(self): + """Returns the user type names known to this FFI instance. + This returns a tuple containing three lists of names: + (typedef_names, names_of_structs, names_of_unions) + """ + typedefs = [] + structs = [] + unions = [] + for key in self._parser._declarations: + if key.startswith('typedef '): + typedefs.append(key[8:]) + elif key.startswith('struct '): + structs.append(key[7:]) + elif key.startswith('union '): + unions.append(key[6:]) + typedefs.sort() + structs.sort() + unions.sort() + return (typedefs, structs, unions) + + +def _load_backend_lib(backend, name, flags): + import os + if not isinstance(name, basestring): + if sys.platform != "win32" or name is not None: + return backend.load_library(name, flags) + name = "c" # Windows: load_library(None) fails, but this works + # on Python 2 (backward compatibility hack only) + first_error = None + if '.' in name or '/' in name or os.sep in name: + try: + return backend.load_library(name, flags) + except OSError as e: + first_error = e + import ctypes.util + path = ctypes.util.find_library(name) + if path is None: + if name == "c" and sys.platform == "win32" and sys.version_info >= (3,): + raise OSError("dlopen(None) cannot work on Windows for Python 3 " + "(see http://bugs.python.org/issue23606)") + msg = ("ctypes.util.find_library() did not manage " + "to locate a library called %r" % (name,)) + if first_error is not None: + msg = "%s. Additionally, %s" % (first_error, msg) + raise OSError(msg) + return backend.load_library(path, flags) + +def _make_ffi_library(ffi, libname, flags): + backend = ffi._backend + backendlib = _load_backend_lib(backend, libname, flags) + # + def accessor_function(name): + key = 'function ' + name + tp, _ = ffi._parser._declarations[key] + BType = ffi._get_cached_btype(tp) + value = backendlib.load_function(BType, name) + library.__dict__[name] = value + # + def accessor_variable(name): + key = 'variable ' + name + tp, _ = ffi._parser._declarations[key] + BType = ffi._get_cached_btype(tp) + read_variable = backendlib.read_variable + write_variable = backendlib.write_variable + setattr(FFILibrary, name, property( + lambda self: read_variable(BType, name), + lambda self, value: write_variable(BType, name, value))) + # + def addressof_var(name): + try: + return addr_variables[name] + except KeyError: + with ffi._lock: + if name not in addr_variables: + key = 'variable ' + name + tp, _ = ffi._parser._declarations[key] + BType = ffi._get_cached_btype(tp) + if BType.kind != 'array': + BType = model.pointer_cache(ffi, BType) + p = backendlib.load_function(BType, name) + addr_variables[name] = p + return addr_variables[name] + # + def accessor_constant(name): + raise NotImplementedError("non-integer constant '%s' cannot be " + "accessed from a dlopen() library" % (name,)) + # + def accessor_int_constant(name): + library.__dict__[name] = ffi._parser._int_constants[name] + # + accessors = {} + accessors_version = [False] + addr_variables = {} + # + def update_accessors(): + if accessors_version[0] is ffi._cdef_version: + return + # + for key, (tp, _) in ffi._parser._declarations.items(): + if not isinstance(tp, model.EnumType): + tag, name = key.split(' ', 1) + if tag == 'function': + accessors[name] = accessor_function + elif tag == 'variable': + accessors[name] = accessor_variable + elif tag == 'constant': + accessors[name] = accessor_constant + else: + for i, enumname in enumerate(tp.enumerators): + def accessor_enum(name, tp=tp, i=i): + tp.check_not_partial() + library.__dict__[name] = tp.enumvalues[i] + accessors[enumname] = accessor_enum + for name in ffi._parser._int_constants: + accessors.setdefault(name, accessor_int_constant) + accessors_version[0] = ffi._cdef_version + # + def make_accessor(name): + with ffi._lock: + if name in library.__dict__ or name in FFILibrary.__dict__: + return # added by another thread while waiting for the lock + if name not in accessors: + update_accessors() + if name not in accessors: + raise AttributeError(name) + accessors[name](name) + # + class FFILibrary(object): + def __getattr__(self, name): + make_accessor(name) + return getattr(self, name) + def __setattr__(self, name, value): + try: + property = getattr(self.__class__, name) + except AttributeError: + make_accessor(name) + setattr(self, name, value) + else: + property.__set__(self, value) + def __dir__(self): + with ffi._lock: + update_accessors() + return accessors.keys() + def __addressof__(self, name): + if name in library.__dict__: + return library.__dict__[name] + if name in FFILibrary.__dict__: + return addressof_var(name) + make_accessor(name) + if name in library.__dict__: + return library.__dict__[name] + if name in FFILibrary.__dict__: + return addressof_var(name) + raise AttributeError("cffi library has no function or " + "global variable named '%s'" % (name,)) + def __cffi_close__(self): + backendlib.close_lib() + self.__dict__.clear() + # + if isinstance(libname, basestring): + try: + if not isinstance(libname, str): # unicode, on Python 2 + libname = libname.encode('utf-8') + FFILibrary.__name__ = 'FFILibrary_%s' % libname + except UnicodeError: + pass + library = FFILibrary() + return library, library.__dict__ + +def _builtin_function_type(func): + # a hack to make at least ffi.typeof(builtin_function) work, + # if the builtin function was obtained by 'vengine_cpy'. + import sys + try: + module = sys.modules[func.__module__] + ffi = module._cffi_original_ffi + types_of_builtin_funcs = module._cffi_types_of_builtin_funcs + tp = types_of_builtin_funcs[func] + except (KeyError, AttributeError, TypeError): + return None + else: + with ffi._lock: + return ffi._get_cached_btype(tp) diff --git a/code/.venv/lib/python3.12/site-packages/cffi/backend_ctypes.py b/code/.venv/lib/python3.12/site-packages/cffi/backend_ctypes.py new file mode 100644 index 0000000..e7956a7 --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/cffi/backend_ctypes.py @@ -0,0 +1,1121 @@ +import ctypes, ctypes.util, operator, sys +from . import model + +if sys.version_info < (3,): + bytechr = chr +else: + unicode = str + long = int + xrange = range + bytechr = lambda num: bytes([num]) + +class CTypesType(type): + pass + +class CTypesData(object): + __metaclass__ = CTypesType + __slots__ = ['__weakref__'] + __name__ = '' + + def __init__(self, *args): + raise TypeError("cannot instantiate %r" % (self.__class__,)) + + @classmethod + def _newp(cls, init): + raise TypeError("expected a pointer or array ctype, got '%s'" + % (cls._get_c_name(),)) + + @staticmethod + def _to_ctypes(value): + raise TypeError + + @classmethod + def _arg_to_ctypes(cls, *value): + try: + ctype = cls._ctype + except AttributeError: + raise TypeError("cannot create an instance of %r" % (cls,)) + if value: + res = cls._to_ctypes(*value) + if not isinstance(res, ctype): + res = cls._ctype(res) + else: + res = cls._ctype() + return res + + @classmethod + def _create_ctype_obj(cls, init): + if init is None: + return cls._arg_to_ctypes() + else: + return cls._arg_to_ctypes(init) + + @staticmethod + def _from_ctypes(ctypes_value): + raise TypeError + + @classmethod + def _get_c_name(cls, replace_with=''): + return cls._reftypename.replace(' &', replace_with) + + @classmethod + def _fix_class(cls): + cls.__name__ = 'CData<%s>' % (cls._get_c_name(),) + cls.__qualname__ = 'CData<%s>' % (cls._get_c_name(),) + cls.__module__ = 'ffi' + + def _get_own_repr(self): + raise NotImplementedError + + def _addr_repr(self, address): + if address == 0: + return 'NULL' + else: + if address < 0: + address += 1 << (8*ctypes.sizeof(ctypes.c_void_p)) + return '0x%x' % address + + def __repr__(self, c_name=None): + own = self._get_own_repr() + return '' % (c_name or self._get_c_name(), own) + + def _convert_to_address(self, BClass): + if BClass is None: + raise TypeError("cannot convert %r to an address" % ( + self._get_c_name(),)) + else: + raise TypeError("cannot convert %r to %r" % ( + self._get_c_name(), BClass._get_c_name())) + + @classmethod + def _get_size(cls): + return ctypes.sizeof(cls._ctype) + + def _get_size_of_instance(self): + return ctypes.sizeof(self._ctype) + + @classmethod + def _cast_from(cls, source): + raise TypeError("cannot cast to %r" % (cls._get_c_name(),)) + + def _cast_to_integer(self): + return self._convert_to_address(None) + + @classmethod + def _alignment(cls): + return ctypes.alignment(cls._ctype) + + def __iter__(self): + raise TypeError("cdata %r does not support iteration" % ( + self._get_c_name()),) + + def _make_cmp(name): + cmpfunc = getattr(operator, name) + def cmp(self, other): + v_is_ptr = not isinstance(self, CTypesGenericPrimitive) + w_is_ptr = (isinstance(other, CTypesData) and + not isinstance(other, CTypesGenericPrimitive)) + if v_is_ptr and w_is_ptr: + return cmpfunc(self._convert_to_address(None), + other._convert_to_address(None)) + elif v_is_ptr or w_is_ptr: + return NotImplemented + else: + if isinstance(self, CTypesGenericPrimitive): + self = self._value + if isinstance(other, CTypesGenericPrimitive): + other = other._value + return cmpfunc(self, other) + cmp.func_name = name + return cmp + + __eq__ = _make_cmp('__eq__') + __ne__ = _make_cmp('__ne__') + __lt__ = _make_cmp('__lt__') + __le__ = _make_cmp('__le__') + __gt__ = _make_cmp('__gt__') + __ge__ = _make_cmp('__ge__') + + def __hash__(self): + return hash(self._convert_to_address(None)) + + def _to_string(self, maxlen): + raise TypeError("string(): %r" % (self,)) + + +class CTypesGenericPrimitive(CTypesData): + __slots__ = [] + + def __hash__(self): + return hash(self._value) + + def _get_own_repr(self): + return repr(self._from_ctypes(self._value)) + + +class CTypesGenericArray(CTypesData): + __slots__ = [] + + @classmethod + def _newp(cls, init): + return cls(init) + + def __iter__(self): + for i in xrange(len(self)): + yield self[i] + + def _get_own_repr(self): + return self._addr_repr(ctypes.addressof(self._blob)) + + +class CTypesGenericPtr(CTypesData): + __slots__ = ['_address', '_as_ctype_ptr'] + _automatic_casts = False + kind = "pointer" + + @classmethod + def _newp(cls, init): + return cls(init) + + @classmethod + def _cast_from(cls, source): + if source is None: + address = 0 + elif isinstance(source, CTypesData): + address = source._cast_to_integer() + elif isinstance(source, (int, long)): + address = source + else: + raise TypeError("bad type for cast to %r: %r" % + (cls, type(source).__name__)) + return cls._new_pointer_at(address) + + @classmethod + def _new_pointer_at(cls, address): + self = cls.__new__(cls) + self._address = address + self._as_ctype_ptr = ctypes.cast(address, cls._ctype) + return self + + def _get_own_repr(self): + try: + return self._addr_repr(self._address) + except AttributeError: + return '???' + + def _cast_to_integer(self): + return self._address + + def __nonzero__(self): + return bool(self._address) + __bool__ = __nonzero__ + + @classmethod + def _to_ctypes(cls, value): + if not isinstance(value, CTypesData): + raise TypeError("unexpected %s object" % type(value).__name__) + address = value._convert_to_address(cls) + return ctypes.cast(address, cls._ctype) + + @classmethod + def _from_ctypes(cls, ctypes_ptr): + address = ctypes.cast(ctypes_ptr, ctypes.c_void_p).value or 0 + return cls._new_pointer_at(address) + + @classmethod + def _initialize(cls, ctypes_ptr, value): + if value: + ctypes_ptr.contents = cls._to_ctypes(value).contents + + def _convert_to_address(self, BClass): + if (BClass in (self.__class__, None) or BClass._automatic_casts + or self._automatic_casts): + return self._address + else: + return CTypesData._convert_to_address(self, BClass) + + +class CTypesBaseStructOrUnion(CTypesData): + __slots__ = ['_blob'] + + @classmethod + def _create_ctype_obj(cls, init): + # may be overridden + raise TypeError("cannot instantiate opaque type %s" % (cls,)) + + def _get_own_repr(self): + return self._addr_repr(ctypes.addressof(self._blob)) + + @classmethod + def _offsetof(cls, fieldname): + return getattr(cls._ctype, fieldname).offset + + def _convert_to_address(self, BClass): + if getattr(BClass, '_BItem', None) is self.__class__: + return ctypes.addressof(self._blob) + else: + return CTypesData._convert_to_address(self, BClass) + + @classmethod + def _from_ctypes(cls, ctypes_struct_or_union): + self = cls.__new__(cls) + self._blob = ctypes_struct_or_union + return self + + @classmethod + def _to_ctypes(cls, value): + return value._blob + + def __repr__(self, c_name=None): + return CTypesData.__repr__(self, c_name or self._get_c_name(' &')) + + +class CTypesBackend(object): + + PRIMITIVE_TYPES = { + 'char': ctypes.c_char, + 'short': ctypes.c_short, + 'int': ctypes.c_int, + 'long': ctypes.c_long, + 'long long': ctypes.c_longlong, + 'signed char': ctypes.c_byte, + 'unsigned char': ctypes.c_ubyte, + 'unsigned short': ctypes.c_ushort, + 'unsigned int': ctypes.c_uint, + 'unsigned long': ctypes.c_ulong, + 'unsigned long long': ctypes.c_ulonglong, + 'float': ctypes.c_float, + 'double': ctypes.c_double, + '_Bool': ctypes.c_bool, + } + + for _name in ['unsigned long long', 'unsigned long', + 'unsigned int', 'unsigned short', 'unsigned char']: + _size = ctypes.sizeof(PRIMITIVE_TYPES[_name]) + PRIMITIVE_TYPES['uint%d_t' % (8*_size)] = PRIMITIVE_TYPES[_name] + if _size == ctypes.sizeof(ctypes.c_void_p): + PRIMITIVE_TYPES['uintptr_t'] = PRIMITIVE_TYPES[_name] + if _size == ctypes.sizeof(ctypes.c_size_t): + PRIMITIVE_TYPES['size_t'] = PRIMITIVE_TYPES[_name] + + for _name in ['long long', 'long', 'int', 'short', 'signed char']: + _size = ctypes.sizeof(PRIMITIVE_TYPES[_name]) + PRIMITIVE_TYPES['int%d_t' % (8*_size)] = PRIMITIVE_TYPES[_name] + if _size == ctypes.sizeof(ctypes.c_void_p): + PRIMITIVE_TYPES['intptr_t'] = PRIMITIVE_TYPES[_name] + PRIMITIVE_TYPES['ptrdiff_t'] = PRIMITIVE_TYPES[_name] + if _size == ctypes.sizeof(ctypes.c_size_t): + PRIMITIVE_TYPES['ssize_t'] = PRIMITIVE_TYPES[_name] + + + def __init__(self): + self.RTLD_LAZY = 0 # not supported anyway by ctypes + self.RTLD_NOW = 0 + self.RTLD_GLOBAL = ctypes.RTLD_GLOBAL + self.RTLD_LOCAL = ctypes.RTLD_LOCAL + + def set_ffi(self, ffi): + self.ffi = ffi + + def _get_types(self): + return CTypesData, CTypesType + + def load_library(self, path, flags=0): + cdll = ctypes.CDLL(path, flags) + return CTypesLibrary(self, cdll) + + def new_void_type(self): + class CTypesVoid(CTypesData): + __slots__ = [] + _reftypename = 'void &' + @staticmethod + def _from_ctypes(novalue): + return None + @staticmethod + def _to_ctypes(novalue): + if novalue is not None: + raise TypeError("None expected, got %s object" % + (type(novalue).__name__,)) + return None + CTypesVoid._fix_class() + return CTypesVoid + + def new_primitive_type(self, name): + if name == 'wchar_t': + raise NotImplementedError(name) + ctype = self.PRIMITIVE_TYPES[name] + if name == 'char': + kind = 'char' + elif name in ('float', 'double'): + kind = 'float' + else: + if name in ('signed char', 'unsigned char'): + kind = 'byte' + elif name == '_Bool': + kind = 'bool' + else: + kind = 'int' + is_signed = (ctype(-1).value == -1) + # + def _cast_source_to_int(source): + if isinstance(source, (int, long, float)): + source = int(source) + elif isinstance(source, CTypesData): + source = source._cast_to_integer() + elif isinstance(source, bytes): + source = ord(source) + elif source is None: + source = 0 + else: + raise TypeError("bad type for cast to %r: %r" % + (CTypesPrimitive, type(source).__name__)) + return source + # + kind1 = kind + class CTypesPrimitive(CTypesGenericPrimitive): + __slots__ = ['_value'] + _ctype = ctype + _reftypename = '%s &' % name + kind = kind1 + + def __init__(self, value): + self._value = value + + @staticmethod + def _create_ctype_obj(init): + if init is None: + return ctype() + return ctype(CTypesPrimitive._to_ctypes(init)) + + if kind == 'int' or kind == 'byte': + @classmethod + def _cast_from(cls, source): + source = _cast_source_to_int(source) + source = ctype(source).value # cast within range + return cls(source) + def __int__(self): + return self._value + + if kind == 'bool': + @classmethod + def _cast_from(cls, source): + if not isinstance(source, (int, long, float)): + source = _cast_source_to_int(source) + return cls(bool(source)) + def __int__(self): + return int(self._value) + + if kind == 'char': + @classmethod + def _cast_from(cls, source): + source = _cast_source_to_int(source) + source = bytechr(source & 0xFF) + return cls(source) + def __int__(self): + return ord(self._value) + + if kind == 'float': + @classmethod + def _cast_from(cls, source): + if isinstance(source, float): + pass + elif isinstance(source, CTypesGenericPrimitive): + if hasattr(source, '__float__'): + source = float(source) + else: + source = int(source) + else: + source = _cast_source_to_int(source) + source = ctype(source).value # fix precision + return cls(source) + def __int__(self): + return int(self._value) + def __float__(self): + return self._value + + _cast_to_integer = __int__ + + if kind == 'int' or kind == 'byte' or kind == 'bool': + @staticmethod + def _to_ctypes(x): + if not isinstance(x, (int, long)): + if isinstance(x, CTypesData): + x = int(x) + else: + raise TypeError("integer expected, got %s" % + type(x).__name__) + if ctype(x).value != x: + if not is_signed and x < 0: + raise OverflowError("%s: negative integer" % name) + else: + raise OverflowError("%s: integer out of bounds" + % name) + return x + + if kind == 'char': + @staticmethod + def _to_ctypes(x): + if isinstance(x, bytes) and len(x) == 1: + return x + if isinstance(x, CTypesPrimitive): # > + return x._value + raise TypeError("character expected, got %s" % + type(x).__name__) + def __nonzero__(self): + return ord(self._value) != 0 + else: + def __nonzero__(self): + return self._value != 0 + __bool__ = __nonzero__ + + if kind == 'float': + @staticmethod + def _to_ctypes(x): + if not isinstance(x, (int, long, float, CTypesData)): + raise TypeError("float expected, got %s" % + type(x).__name__) + return ctype(x).value + + @staticmethod + def _from_ctypes(value): + return getattr(value, 'value', value) + + @staticmethod + def _initialize(blob, init): + blob.value = CTypesPrimitive._to_ctypes(init) + + if kind == 'char': + def _to_string(self, maxlen): + return self._value + if kind == 'byte': + def _to_string(self, maxlen): + return chr(self._value & 0xff) + # + CTypesPrimitive._fix_class() + return CTypesPrimitive + + def new_pointer_type(self, BItem): + getbtype = self.ffi._get_cached_btype + if BItem is getbtype(model.PrimitiveType('char')): + kind = 'charp' + elif BItem in (getbtype(model.PrimitiveType('signed char')), + getbtype(model.PrimitiveType('unsigned char'))): + kind = 'bytep' + elif BItem is getbtype(model.void_type): + kind = 'voidp' + else: + kind = 'generic' + # + class CTypesPtr(CTypesGenericPtr): + __slots__ = ['_own'] + if kind == 'charp': + __slots__ += ['__as_strbuf'] + _BItem = BItem + if hasattr(BItem, '_ctype'): + _ctype = ctypes.POINTER(BItem._ctype) + _bitem_size = ctypes.sizeof(BItem._ctype) + else: + _ctype = ctypes.c_void_p + if issubclass(BItem, CTypesGenericArray): + _reftypename = BItem._get_c_name('(* &)') + else: + _reftypename = BItem._get_c_name(' * &') + + def __init__(self, init): + ctypeobj = BItem._create_ctype_obj(init) + if kind == 'charp': + self.__as_strbuf = ctypes.create_string_buffer( + ctypeobj.value + b'\x00') + self._as_ctype_ptr = ctypes.cast( + self.__as_strbuf, self._ctype) + else: + self._as_ctype_ptr = ctypes.pointer(ctypeobj) + self._address = ctypes.cast(self._as_ctype_ptr, + ctypes.c_void_p).value + self._own = True + + def __add__(self, other): + if isinstance(other, (int, long)): + return self._new_pointer_at(self._address + + other * self._bitem_size) + else: + return NotImplemented + + def __sub__(self, other): + if isinstance(other, (int, long)): + return self._new_pointer_at(self._address - + other * self._bitem_size) + elif type(self) is type(other): + return (self._address - other._address) // self._bitem_size + else: + return NotImplemented + + def __getitem__(self, index): + if getattr(self, '_own', False) and index != 0: + raise IndexError + return BItem._from_ctypes(self._as_ctype_ptr[index]) + + def __setitem__(self, index, value): + self._as_ctype_ptr[index] = BItem._to_ctypes(value) + + if kind == 'charp' or kind == 'voidp': + @classmethod + def _arg_to_ctypes(cls, *value): + if value and isinstance(value[0], bytes): + return ctypes.c_char_p(value[0]) + else: + return super(CTypesPtr, cls)._arg_to_ctypes(*value) + + if kind == 'charp' or kind == 'bytep': + def _to_string(self, maxlen): + if maxlen < 0: + maxlen = sys.maxsize + p = ctypes.cast(self._as_ctype_ptr, + ctypes.POINTER(ctypes.c_char)) + n = 0 + while n < maxlen and p[n] != b'\x00': + n += 1 + return b''.join([p[i] for i in range(n)]) + + def _get_own_repr(self): + if getattr(self, '_own', False): + return 'owning %d bytes' % ( + ctypes.sizeof(self._as_ctype_ptr.contents),) + return super(CTypesPtr, self)._get_own_repr() + # + if (BItem is self.ffi._get_cached_btype(model.void_type) or + BItem is self.ffi._get_cached_btype(model.PrimitiveType('char'))): + CTypesPtr._automatic_casts = True + # + CTypesPtr._fix_class() + return CTypesPtr + + def new_array_type(self, CTypesPtr, length): + if length is None: + brackets = ' &[]' + else: + brackets = ' &[%d]' % length + BItem = CTypesPtr._BItem + getbtype = self.ffi._get_cached_btype + if BItem is getbtype(model.PrimitiveType('char')): + kind = 'char' + elif BItem in (getbtype(model.PrimitiveType('signed char')), + getbtype(model.PrimitiveType('unsigned char'))): + kind = 'byte' + else: + kind = 'generic' + # + class CTypesArray(CTypesGenericArray): + __slots__ = ['_blob', '_own'] + if length is not None: + _ctype = BItem._ctype * length + else: + __slots__.append('_ctype') + _reftypename = BItem._get_c_name(brackets) + _declared_length = length + _CTPtr = CTypesPtr + + def __init__(self, init): + if length is None: + if isinstance(init, (int, long)): + len1 = init + init = None + elif kind == 'char' and isinstance(init, bytes): + len1 = len(init) + 1 # extra null + else: + init = tuple(init) + len1 = len(init) + self._ctype = BItem._ctype * len1 + self._blob = self._ctype() + self._own = True + if init is not None: + self._initialize(self._blob, init) + + @staticmethod + def _initialize(blob, init): + if isinstance(init, bytes): + init = [init[i:i+1] for i in range(len(init))] + else: + if isinstance(init, CTypesGenericArray): + if (len(init) != len(blob) or + not isinstance(init, CTypesArray)): + raise TypeError("length/type mismatch: %s" % (init,)) + init = tuple(init) + if len(init) > len(blob): + raise IndexError("too many initializers") + addr = ctypes.cast(blob, ctypes.c_void_p).value + PTR = ctypes.POINTER(BItem._ctype) + itemsize = ctypes.sizeof(BItem._ctype) + for i, value in enumerate(init): + p = ctypes.cast(addr + i * itemsize, PTR) + BItem._initialize(p.contents, value) + + def __len__(self): + return len(self._blob) + + def __getitem__(self, index): + if not (0 <= index < len(self._blob)): + raise IndexError + return BItem._from_ctypes(self._blob[index]) + + def __setitem__(self, index, value): + if not (0 <= index < len(self._blob)): + raise IndexError + self._blob[index] = BItem._to_ctypes(value) + + if kind == 'char' or kind == 'byte': + def _to_string(self, maxlen): + if maxlen < 0: + maxlen = len(self._blob) + p = ctypes.cast(self._blob, + ctypes.POINTER(ctypes.c_char)) + n = 0 + while n < maxlen and p[n] != b'\x00': + n += 1 + return b''.join([p[i] for i in range(n)]) + + def _get_own_repr(self): + if getattr(self, '_own', False): + return 'owning %d bytes' % (ctypes.sizeof(self._blob),) + return super(CTypesArray, self)._get_own_repr() + + def _convert_to_address(self, BClass): + if BClass in (CTypesPtr, None) or BClass._automatic_casts: + return ctypes.addressof(self._blob) + else: + return CTypesData._convert_to_address(self, BClass) + + @staticmethod + def _from_ctypes(ctypes_array): + self = CTypesArray.__new__(CTypesArray) + self._blob = ctypes_array + return self + + @staticmethod + def _arg_to_ctypes(value): + return CTypesPtr._arg_to_ctypes(value) + + def __add__(self, other): + if isinstance(other, (int, long)): + return CTypesPtr._new_pointer_at( + ctypes.addressof(self._blob) + + other * ctypes.sizeof(BItem._ctype)) + else: + return NotImplemented + + @classmethod + def _cast_from(cls, source): + raise NotImplementedError("casting to %r" % ( + cls._get_c_name(),)) + # + CTypesArray._fix_class() + return CTypesArray + + def _new_struct_or_union(self, kind, name, base_ctypes_class): + # + class struct_or_union(base_ctypes_class): + pass + struct_or_union.__name__ = '%s_%s' % (kind, name) + kind1 = kind + # + class CTypesStructOrUnion(CTypesBaseStructOrUnion): + __slots__ = ['_blob'] + _ctype = struct_or_union + _reftypename = '%s &' % (name,) + _kind = kind = kind1 + # + CTypesStructOrUnion._fix_class() + return CTypesStructOrUnion + + def new_struct_type(self, name): + return self._new_struct_or_union('struct', name, ctypes.Structure) + + def new_union_type(self, name): + return self._new_struct_or_union('union', name, ctypes.Union) + + def complete_struct_or_union(self, CTypesStructOrUnion, fields, tp, + totalsize=-1, totalalignment=-1, sflags=0, + pack=0): + if totalsize >= 0 or totalalignment >= 0: + raise NotImplementedError("the ctypes backend of CFFI does not support " + "structures completed by verify(); please " + "compile and install the _cffi_backend module.") + struct_or_union = CTypesStructOrUnion._ctype + fnames = [fname for (fname, BField, bitsize) in fields] + btypes = [BField for (fname, BField, bitsize) in fields] + bitfields = [bitsize for (fname, BField, bitsize) in fields] + # + bfield_types = {} + cfields = [] + for (fname, BField, bitsize) in fields: + if bitsize < 0: + cfields.append((fname, BField._ctype)) + bfield_types[fname] = BField + else: + cfields.append((fname, BField._ctype, bitsize)) + bfield_types[fname] = Ellipsis + if sflags & 8: + struct_or_union._pack_ = 1 + elif pack: + struct_or_union._pack_ = pack + struct_or_union._fields_ = cfields + CTypesStructOrUnion._bfield_types = bfield_types + # + @staticmethod + def _create_ctype_obj(init): + result = struct_or_union() + if init is not None: + initialize(result, init) + return result + CTypesStructOrUnion._create_ctype_obj = _create_ctype_obj + # + def initialize(blob, init): + if is_union: + if len(init) > 1: + raise ValueError("union initializer: %d items given, but " + "only one supported (use a dict if needed)" + % (len(init),)) + if not isinstance(init, dict): + if isinstance(init, (bytes, unicode)): + raise TypeError("union initializer: got a str") + init = tuple(init) + if len(init) > len(fnames): + raise ValueError("too many values for %s initializer" % + CTypesStructOrUnion._get_c_name()) + init = dict(zip(fnames, init)) + addr = ctypes.addressof(blob) + for fname, value in init.items(): + BField, bitsize = name2fieldtype[fname] + assert bitsize < 0, \ + "not implemented: initializer with bit fields" + offset = CTypesStructOrUnion._offsetof(fname) + PTR = ctypes.POINTER(BField._ctype) + p = ctypes.cast(addr + offset, PTR) + BField._initialize(p.contents, value) + is_union = CTypesStructOrUnion._kind == 'union' + name2fieldtype = dict(zip(fnames, zip(btypes, bitfields))) + # + for fname, BField, bitsize in fields: + if fname == '': + raise NotImplementedError("nested anonymous structs/unions") + if hasattr(CTypesStructOrUnion, fname): + raise ValueError("the field name %r conflicts in " + "the ctypes backend" % fname) + if bitsize < 0: + def getter(self, fname=fname, BField=BField, + offset=CTypesStructOrUnion._offsetof(fname), + PTR=ctypes.POINTER(BField._ctype)): + addr = ctypes.addressof(self._blob) + p = ctypes.cast(addr + offset, PTR) + return BField._from_ctypes(p.contents) + def setter(self, value, fname=fname, BField=BField): + setattr(self._blob, fname, BField._to_ctypes(value)) + # + if issubclass(BField, CTypesGenericArray): + setter = None + if BField._declared_length == 0: + def getter(self, fname=fname, BFieldPtr=BField._CTPtr, + offset=CTypesStructOrUnion._offsetof(fname), + PTR=ctypes.POINTER(BField._ctype)): + addr = ctypes.addressof(self._blob) + p = ctypes.cast(addr + offset, PTR) + return BFieldPtr._from_ctypes(p) + # + else: + def getter(self, fname=fname, BField=BField): + return BField._from_ctypes(getattr(self._blob, fname)) + def setter(self, value, fname=fname, BField=BField): + # xxx obscure workaround + value = BField._to_ctypes(value) + oldvalue = getattr(self._blob, fname) + setattr(self._blob, fname, value) + if value != getattr(self._blob, fname): + setattr(self._blob, fname, oldvalue) + raise OverflowError("value too large for bitfield") + setattr(CTypesStructOrUnion, fname, property(getter, setter)) + # + CTypesPtr = self.ffi._get_cached_btype(model.PointerType(tp)) + for fname in fnames: + if hasattr(CTypesPtr, fname): + raise ValueError("the field name %r conflicts in " + "the ctypes backend" % fname) + def getter(self, fname=fname): + return getattr(self[0], fname) + def setter(self, value, fname=fname): + setattr(self[0], fname, value) + setattr(CTypesPtr, fname, property(getter, setter)) + + def new_function_type(self, BArgs, BResult, has_varargs): + nameargs = [BArg._get_c_name() for BArg in BArgs] + if has_varargs: + nameargs.append('...') + nameargs = ', '.join(nameargs) + # + class CTypesFunctionPtr(CTypesGenericPtr): + __slots__ = ['_own_callback', '_name'] + _ctype = ctypes.CFUNCTYPE(getattr(BResult, '_ctype', None), + *[BArg._ctype for BArg in BArgs], + use_errno=True) + _reftypename = BResult._get_c_name('(* &)(%s)' % (nameargs,)) + + def __init__(self, init, error=None): + # create a callback to the Python callable init() + import traceback + assert not has_varargs, "varargs not supported for callbacks" + if getattr(BResult, '_ctype', None) is not None: + error = BResult._from_ctypes( + BResult._create_ctype_obj(error)) + else: + error = None + def callback(*args): + args2 = [] + for arg, BArg in zip(args, BArgs): + args2.append(BArg._from_ctypes(arg)) + try: + res2 = init(*args2) + res2 = BResult._to_ctypes(res2) + except: + traceback.print_exc() + res2 = error + if issubclass(BResult, CTypesGenericPtr): + if res2: + res2 = ctypes.cast(res2, ctypes.c_void_p).value + # .value: http://bugs.python.org/issue1574593 + else: + res2 = None + #print repr(res2) + return res2 + if issubclass(BResult, CTypesGenericPtr): + # The only pointers callbacks can return are void*s: + # http://bugs.python.org/issue5710 + callback_ctype = ctypes.CFUNCTYPE( + ctypes.c_void_p, + *[BArg._ctype for BArg in BArgs], + use_errno=True) + else: + callback_ctype = CTypesFunctionPtr._ctype + self._as_ctype_ptr = callback_ctype(callback) + self._address = ctypes.cast(self._as_ctype_ptr, + ctypes.c_void_p).value + self._own_callback = init + + @staticmethod + def _initialize(ctypes_ptr, value): + if value: + raise NotImplementedError("ctypes backend: not supported: " + "initializers for function pointers") + + def __repr__(self): + c_name = getattr(self, '_name', None) + if c_name: + i = self._reftypename.index('(* &)') + if self._reftypename[i-1] not in ' )*': + c_name = ' ' + c_name + c_name = self._reftypename.replace('(* &)', c_name) + return CTypesData.__repr__(self, c_name) + + def _get_own_repr(self): + if getattr(self, '_own_callback', None) is not None: + return 'calling %r' % (self._own_callback,) + return super(CTypesFunctionPtr, self)._get_own_repr() + + def __call__(self, *args): + if has_varargs: + assert len(args) >= len(BArgs) + extraargs = args[len(BArgs):] + args = args[:len(BArgs)] + else: + assert len(args) == len(BArgs) + ctypes_args = [] + for arg, BArg in zip(args, BArgs): + ctypes_args.append(BArg._arg_to_ctypes(arg)) + if has_varargs: + for i, arg in enumerate(extraargs): + if arg is None: + ctypes_args.append(ctypes.c_void_p(0)) # NULL + continue + if not isinstance(arg, CTypesData): + raise TypeError( + "argument %d passed in the variadic part " + "needs to be a cdata object (got %s)" % + (1 + len(BArgs) + i, type(arg).__name__)) + ctypes_args.append(arg._arg_to_ctypes(arg)) + result = self._as_ctype_ptr(*ctypes_args) + return BResult._from_ctypes(result) + # + CTypesFunctionPtr._fix_class() + return CTypesFunctionPtr + + def new_enum_type(self, name, enumerators, enumvalues, CTypesInt): + assert isinstance(name, str) + reverse_mapping = dict(zip(reversed(enumvalues), + reversed(enumerators))) + # + class CTypesEnum(CTypesInt): + __slots__ = [] + _reftypename = '%s &' % name + + def _get_own_repr(self): + value = self._value + try: + return '%d: %s' % (value, reverse_mapping[value]) + except KeyError: + return str(value) + + def _to_string(self, maxlen): + value = self._value + try: + return reverse_mapping[value] + except KeyError: + return str(value) + # + CTypesEnum._fix_class() + return CTypesEnum + + def get_errno(self): + return ctypes.get_errno() + + def set_errno(self, value): + ctypes.set_errno(value) + + def string(self, b, maxlen=-1): + return b._to_string(maxlen) + + def buffer(self, bptr, size=-1): + raise NotImplementedError("buffer() with ctypes backend") + + def sizeof(self, cdata_or_BType): + if isinstance(cdata_or_BType, CTypesData): + return cdata_or_BType._get_size_of_instance() + else: + assert issubclass(cdata_or_BType, CTypesData) + return cdata_or_BType._get_size() + + def alignof(self, BType): + assert issubclass(BType, CTypesData) + return BType._alignment() + + def newp(self, BType, source): + if not issubclass(BType, CTypesData): + raise TypeError + return BType._newp(source) + + def cast(self, BType, source): + return BType._cast_from(source) + + def callback(self, BType, source, error, onerror): + assert onerror is None # XXX not implemented + return BType(source, error) + + _weakref_cache_ref = None + + def gcp(self, cdata, destructor, size=0): + if self._weakref_cache_ref is None: + import weakref + class MyRef(weakref.ref): + def __eq__(self, other): + myref = self() + return self is other or ( + myref is not None and myref is other()) + def __ne__(self, other): + return not (self == other) + def __hash__(self): + try: + return self._hash + except AttributeError: + self._hash = hash(self()) + return self._hash + self._weakref_cache_ref = {}, MyRef + weak_cache, MyRef = self._weakref_cache_ref + + if destructor is None: + try: + del weak_cache[MyRef(cdata)] + except KeyError: + raise TypeError("Can remove destructor only on a object " + "previously returned by ffi.gc()") + return None + + def remove(k): + cdata, destructor = weak_cache.pop(k, (None, None)) + if destructor is not None: + destructor(cdata) + + new_cdata = self.cast(self.typeof(cdata), cdata) + assert new_cdata is not cdata + weak_cache[MyRef(new_cdata, remove)] = (cdata, destructor) + return new_cdata + + typeof = type + + def getcname(self, BType, replace_with): + return BType._get_c_name(replace_with) + + def typeoffsetof(self, BType, fieldname, num=0): + if isinstance(fieldname, str): + if num == 0 and issubclass(BType, CTypesGenericPtr): + BType = BType._BItem + if not issubclass(BType, CTypesBaseStructOrUnion): + raise TypeError("expected a struct or union ctype") + BField = BType._bfield_types[fieldname] + if BField is Ellipsis: + raise TypeError("not supported for bitfields") + return (BField, BType._offsetof(fieldname)) + elif isinstance(fieldname, (int, long)): + if issubclass(BType, CTypesGenericArray): + BType = BType._CTPtr + if not issubclass(BType, CTypesGenericPtr): + raise TypeError("expected an array or ptr ctype") + BItem = BType._BItem + offset = BItem._get_size() * fieldname + if offset > sys.maxsize: + raise OverflowError + return (BItem, offset) + else: + raise TypeError(type(fieldname)) + + def rawaddressof(self, BTypePtr, cdata, offset=None): + if isinstance(cdata, CTypesBaseStructOrUnion): + ptr = ctypes.pointer(type(cdata)._to_ctypes(cdata)) + elif isinstance(cdata, CTypesGenericPtr): + if offset is None or not issubclass(type(cdata)._BItem, + CTypesBaseStructOrUnion): + raise TypeError("unexpected cdata type") + ptr = type(cdata)._to_ctypes(cdata) + elif isinstance(cdata, CTypesGenericArray): + ptr = type(cdata)._to_ctypes(cdata) + else: + raise TypeError("expected a ") + if offset: + ptr = ctypes.cast( + ctypes.c_void_p( + ctypes.cast(ptr, ctypes.c_void_p).value + offset), + type(ptr)) + return BTypePtr._from_ctypes(ptr) + + +class CTypesLibrary(object): + + def __init__(self, backend, cdll): + self.backend = backend + self.cdll = cdll + + def load_function(self, BType, name): + c_func = getattr(self.cdll, name) + funcobj = BType._from_ctypes(c_func) + funcobj._name = name + return funcobj + + def read_variable(self, BType, name): + try: + ctypes_obj = BType._ctype.in_dll(self.cdll, name) + except AttributeError as e: + raise NotImplementedError(e) + return BType._from_ctypes(ctypes_obj) + + def write_variable(self, BType, name, value): + new_ctypes_obj = BType._to_ctypes(value) + ctypes_obj = BType._ctype.in_dll(self.cdll, name) + ctypes.memmove(ctypes.addressof(ctypes_obj), + ctypes.addressof(new_ctypes_obj), + ctypes.sizeof(BType._ctype)) diff --git a/code/.venv/lib/python3.12/site-packages/cffi/cffi_opcode.py b/code/.venv/lib/python3.12/site-packages/cffi/cffi_opcode.py new file mode 100644 index 0000000..6421df6 --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/cffi/cffi_opcode.py @@ -0,0 +1,187 @@ +from .error import VerificationError + +class CffiOp(object): + def __init__(self, op, arg): + self.op = op + self.arg = arg + + def as_c_expr(self): + if self.op is None: + assert isinstance(self.arg, str) + return '(_cffi_opcode_t)(%s)' % (self.arg,) + classname = CLASS_NAME[self.op] + return '_CFFI_OP(_CFFI_OP_%s, %s)' % (classname, self.arg) + + def as_python_bytes(self): + if self.op is None and self.arg.isdigit(): + value = int(self.arg) # non-negative: '-' not in self.arg + if value >= 2**31: + raise OverflowError("cannot emit %r: limited to 2**31-1" + % (self.arg,)) + return format_four_bytes(value) + if isinstance(self.arg, str): + raise VerificationError("cannot emit to Python: %r" % (self.arg,)) + return format_four_bytes((self.arg << 8) | self.op) + + def __str__(self): + classname = CLASS_NAME.get(self.op, self.op) + return '(%s %s)' % (classname, self.arg) + +def format_four_bytes(num): + return '\\x%02X\\x%02X\\x%02X\\x%02X' % ( + (num >> 24) & 0xFF, + (num >> 16) & 0xFF, + (num >> 8) & 0xFF, + (num ) & 0xFF) + +OP_PRIMITIVE = 1 +OP_POINTER = 3 +OP_ARRAY = 5 +OP_OPEN_ARRAY = 7 +OP_STRUCT_UNION = 9 +OP_ENUM = 11 +OP_FUNCTION = 13 +OP_FUNCTION_END = 15 +OP_NOOP = 17 +OP_BITFIELD = 19 +OP_TYPENAME = 21 +OP_CPYTHON_BLTN_V = 23 # varargs +OP_CPYTHON_BLTN_N = 25 # noargs +OP_CPYTHON_BLTN_O = 27 # O (i.e. a single arg) +OP_CONSTANT = 29 +OP_CONSTANT_INT = 31 +OP_GLOBAL_VAR = 33 +OP_DLOPEN_FUNC = 35 +OP_DLOPEN_CONST = 37 +OP_GLOBAL_VAR_F = 39 +OP_EXTERN_PYTHON = 41 + +PRIM_VOID = 0 +PRIM_BOOL = 1 +PRIM_CHAR = 2 +PRIM_SCHAR = 3 +PRIM_UCHAR = 4 +PRIM_SHORT = 5 +PRIM_USHORT = 6 +PRIM_INT = 7 +PRIM_UINT = 8 +PRIM_LONG = 9 +PRIM_ULONG = 10 +PRIM_LONGLONG = 11 +PRIM_ULONGLONG = 12 +PRIM_FLOAT = 13 +PRIM_DOUBLE = 14 +PRIM_LONGDOUBLE = 15 + +PRIM_WCHAR = 16 +PRIM_INT8 = 17 +PRIM_UINT8 = 18 +PRIM_INT16 = 19 +PRIM_UINT16 = 20 +PRIM_INT32 = 21 +PRIM_UINT32 = 22 +PRIM_INT64 = 23 +PRIM_UINT64 = 24 +PRIM_INTPTR = 25 +PRIM_UINTPTR = 26 +PRIM_PTRDIFF = 27 +PRIM_SIZE = 28 +PRIM_SSIZE = 29 +PRIM_INT_LEAST8 = 30 +PRIM_UINT_LEAST8 = 31 +PRIM_INT_LEAST16 = 32 +PRIM_UINT_LEAST16 = 33 +PRIM_INT_LEAST32 = 34 +PRIM_UINT_LEAST32 = 35 +PRIM_INT_LEAST64 = 36 +PRIM_UINT_LEAST64 = 37 +PRIM_INT_FAST8 = 38 +PRIM_UINT_FAST8 = 39 +PRIM_INT_FAST16 = 40 +PRIM_UINT_FAST16 = 41 +PRIM_INT_FAST32 = 42 +PRIM_UINT_FAST32 = 43 +PRIM_INT_FAST64 = 44 +PRIM_UINT_FAST64 = 45 +PRIM_INTMAX = 46 +PRIM_UINTMAX = 47 +PRIM_FLOATCOMPLEX = 48 +PRIM_DOUBLECOMPLEX = 49 +PRIM_CHAR16 = 50 +PRIM_CHAR32 = 51 + +_NUM_PRIM = 52 +_UNKNOWN_PRIM = -1 +_UNKNOWN_FLOAT_PRIM = -2 +_UNKNOWN_LONG_DOUBLE = -3 + +_IO_FILE_STRUCT = -1 + +PRIMITIVE_TO_INDEX = { + 'char': PRIM_CHAR, + 'short': PRIM_SHORT, + 'int': PRIM_INT, + 'long': PRIM_LONG, + 'long long': PRIM_LONGLONG, + 'signed char': PRIM_SCHAR, + 'unsigned char': PRIM_UCHAR, + 'unsigned short': PRIM_USHORT, + 'unsigned int': PRIM_UINT, + 'unsigned long': PRIM_ULONG, + 'unsigned long long': PRIM_ULONGLONG, + 'float': PRIM_FLOAT, + 'double': PRIM_DOUBLE, + 'long double': PRIM_LONGDOUBLE, + '_cffi_float_complex_t': PRIM_FLOATCOMPLEX, + '_cffi_double_complex_t': PRIM_DOUBLECOMPLEX, + '_Bool': PRIM_BOOL, + 'wchar_t': PRIM_WCHAR, + 'char16_t': PRIM_CHAR16, + 'char32_t': PRIM_CHAR32, + 'int8_t': PRIM_INT8, + 'uint8_t': PRIM_UINT8, + 'int16_t': PRIM_INT16, + 'uint16_t': PRIM_UINT16, + 'int32_t': PRIM_INT32, + 'uint32_t': PRIM_UINT32, + 'int64_t': PRIM_INT64, + 'uint64_t': PRIM_UINT64, + 'intptr_t': PRIM_INTPTR, + 'uintptr_t': PRIM_UINTPTR, + 'ptrdiff_t': PRIM_PTRDIFF, + 'size_t': PRIM_SIZE, + 'ssize_t': PRIM_SSIZE, + 'int_least8_t': PRIM_INT_LEAST8, + 'uint_least8_t': PRIM_UINT_LEAST8, + 'int_least16_t': PRIM_INT_LEAST16, + 'uint_least16_t': PRIM_UINT_LEAST16, + 'int_least32_t': PRIM_INT_LEAST32, + 'uint_least32_t': PRIM_UINT_LEAST32, + 'int_least64_t': PRIM_INT_LEAST64, + 'uint_least64_t': PRIM_UINT_LEAST64, + 'int_fast8_t': PRIM_INT_FAST8, + 'uint_fast8_t': PRIM_UINT_FAST8, + 'int_fast16_t': PRIM_INT_FAST16, + 'uint_fast16_t': PRIM_UINT_FAST16, + 'int_fast32_t': PRIM_INT_FAST32, + 'uint_fast32_t': PRIM_UINT_FAST32, + 'int_fast64_t': PRIM_INT_FAST64, + 'uint_fast64_t': PRIM_UINT_FAST64, + 'intmax_t': PRIM_INTMAX, + 'uintmax_t': PRIM_UINTMAX, + } + +F_UNION = 0x01 +F_CHECK_FIELDS = 0x02 +F_PACKED = 0x04 +F_EXTERNAL = 0x08 +F_OPAQUE = 0x10 + +G_FLAGS = dict([('_CFFI_' + _key, globals()[_key]) + for _key in ['F_UNION', 'F_CHECK_FIELDS', 'F_PACKED', + 'F_EXTERNAL', 'F_OPAQUE']]) + +CLASS_NAME = {} +for _name, _value in list(globals().items()): + if _name.startswith('OP_') and isinstance(_value, int): + CLASS_NAME[_value] = _name[3:] diff --git a/code/.venv/lib/python3.12/site-packages/cffi/commontypes.py b/code/.venv/lib/python3.12/site-packages/cffi/commontypes.py new file mode 100644 index 0000000..d4dae35 --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/cffi/commontypes.py @@ -0,0 +1,82 @@ +import sys +from . import model +from .error import FFIError + + +COMMON_TYPES = {} + +try: + # fetch "bool" and all simple Windows types + from _cffi_backend import _get_common_types + _get_common_types(COMMON_TYPES) +except ImportError: + pass + +COMMON_TYPES['FILE'] = model.unknown_type('FILE', '_IO_FILE') +COMMON_TYPES['bool'] = '_Bool' # in case we got ImportError above +COMMON_TYPES['float _Complex'] = '_cffi_float_complex_t' +COMMON_TYPES['double _Complex'] = '_cffi_double_complex_t' + +for _type in model.PrimitiveType.ALL_PRIMITIVE_TYPES: + if _type.endswith('_t'): + COMMON_TYPES[_type] = _type +del _type + +_CACHE = {} + +def resolve_common_type(parser, commontype): + try: + return _CACHE[commontype] + except KeyError: + cdecl = COMMON_TYPES.get(commontype, commontype) + if not isinstance(cdecl, str): + result, quals = cdecl, 0 # cdecl is already a BaseType + elif cdecl in model.PrimitiveType.ALL_PRIMITIVE_TYPES: + result, quals = model.PrimitiveType(cdecl), 0 + elif cdecl == 'set-unicode-needed': + raise FFIError("The Windows type %r is only available after " + "you call ffi.set_unicode()" % (commontype,)) + else: + if commontype == cdecl: + raise FFIError( + "Unsupported type: %r. Please look at " + "http://cffi.readthedocs.io/en/latest/cdef.html#ffi-cdef-limitations " + "and file an issue if you think this type should really " + "be supported." % (commontype,)) + result, quals = parser.parse_type_and_quals(cdecl) # recursive + + assert isinstance(result, model.BaseTypeByIdentity) + _CACHE[commontype] = result, quals + return result, quals + + +# ____________________________________________________________ +# extra types for Windows (most of them are in commontypes.c) + + +def win_common_types(): + return { + "UNICODE_STRING": model.StructType( + "_UNICODE_STRING", + ["Length", + "MaximumLength", + "Buffer"], + [model.PrimitiveType("unsigned short"), + model.PrimitiveType("unsigned short"), + model.PointerType(model.PrimitiveType("wchar_t"))], + [-1, -1, -1]), + "PUNICODE_STRING": "UNICODE_STRING *", + "PCUNICODE_STRING": "const UNICODE_STRING *", + + "TBYTE": "set-unicode-needed", + "TCHAR": "set-unicode-needed", + "LPCTSTR": "set-unicode-needed", + "PCTSTR": "set-unicode-needed", + "LPTSTR": "set-unicode-needed", + "PTSTR": "set-unicode-needed", + "PTBYTE": "set-unicode-needed", + "PTCHAR": "set-unicode-needed", + } + +if sys.platform == 'win32': + COMMON_TYPES.update(win_common_types()) diff --git a/code/.venv/lib/python3.12/site-packages/cffi/cparser.py b/code/.venv/lib/python3.12/site-packages/cffi/cparser.py new file mode 100644 index 0000000..eee83ca --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/cffi/cparser.py @@ -0,0 +1,1015 @@ +from . import model +from .commontypes import COMMON_TYPES, resolve_common_type +from .error import FFIError, CDefError +try: + from . import _pycparser as pycparser +except ImportError: + import pycparser +import weakref, re, sys + +try: + if sys.version_info < (3,): + import thread as _thread + else: + import _thread + lock = _thread.allocate_lock() +except ImportError: + lock = None + +def _workaround_for_static_import_finders(): + # Issue #392: packaging tools like cx_Freeze can not find these + # because pycparser uses exec dynamic import. This is an obscure + # workaround. This function is never called. + import pycparser.yacctab + import pycparser.lextab + +CDEF_SOURCE_STRING = "" +_r_comment = re.compile(r"/\*.*?\*/|//([^\n\\]|\\.)*?$", + re.DOTALL | re.MULTILINE) +_r_define = re.compile(r"^\s*#\s*define\s+([A-Za-z_][A-Za-z_0-9]*)" + r"\b((?:[^\n\\]|\\.)*?)$", + re.DOTALL | re.MULTILINE) +_r_line_directive = re.compile(r"^[ \t]*#[ \t]*(?:line|\d+)\b.*$", re.MULTILINE) +_r_partial_enum = re.compile(r"=\s*\.\.\.\s*[,}]|\.\.\.\s*\}") +_r_enum_dotdotdot = re.compile(r"__dotdotdot\d+__$") +_r_partial_array = re.compile(r"\[\s*\.\.\.\s*\]") +_r_words = re.compile(r"\w+|\S") +_parser_cache = None +_r_int_literal = re.compile(r"-?0?x?[0-9a-f]+[lu]*$", re.IGNORECASE) +_r_stdcall1 = re.compile(r"\b(__stdcall|WINAPI)\b") +_r_stdcall2 = re.compile(r"[(]\s*(__stdcall|WINAPI)\b") +_r_cdecl = re.compile(r"\b__cdecl\b") +_r_extern_python = re.compile(r'\bextern\s*"' + r'(Python|Python\s*\+\s*C|C\s*\+\s*Python)"\s*.') +_r_star_const_space = re.compile( # matches "* const " + r"[*]\s*((const|volatile|restrict)\b\s*)+") +_r_int_dotdotdot = re.compile(r"(\b(int|long|short|signed|unsigned|char)\s*)+" + r"\.\.\.") +_r_float_dotdotdot = re.compile(r"\b(double|float)\s*\.\.\.") + +def _get_parser(): + global _parser_cache + if _parser_cache is None: + _parser_cache = pycparser.CParser() + return _parser_cache + +def _workaround_for_old_pycparser(csource): + # Workaround for a pycparser issue (fixed between pycparser 2.10 and + # 2.14): "char*const***" gives us a wrong syntax tree, the same as + # for "char***(*const)". This means we can't tell the difference + # afterwards. But "char(*const(***))" gives us the right syntax + # tree. The issue only occurs if there are several stars in + # sequence with no parenthesis inbetween, just possibly qualifiers. + # Attempt to fix it by adding some parentheses in the source: each + # time we see "* const" or "* const *", we add an opening + # parenthesis before each star---the hard part is figuring out where + # to close them. + parts = [] + while True: + match = _r_star_const_space.search(csource) + if not match: + break + #print repr(''.join(parts)+csource), '=>', + parts.append(csource[:match.start()]) + parts.append('('); closing = ')' + parts.append(match.group()) # e.g. "* const " + endpos = match.end() + if csource.startswith('*', endpos): + parts.append('('); closing += ')' + level = 0 + i = endpos + while i < len(csource): + c = csource[i] + if c == '(': + level += 1 + elif c == ')': + if level == 0: + break + level -= 1 + elif c in ',;=': + if level == 0: + break + i += 1 + csource = csource[endpos:i] + closing + csource[i:] + #print repr(''.join(parts)+csource) + parts.append(csource) + return ''.join(parts) + +def _preprocess_extern_python(csource): + # input: `extern "Python" int foo(int);` or + # `extern "Python" { int foo(int); }` + # output: + # void __cffi_extern_python_start; + # int foo(int); + # void __cffi_extern_python_stop; + # + # input: `extern "Python+C" int foo(int);` + # output: + # void __cffi_extern_python_plus_c_start; + # int foo(int); + # void __cffi_extern_python_stop; + parts = [] + while True: + match = _r_extern_python.search(csource) + if not match: + break + endpos = match.end() - 1 + #print + #print ''.join(parts)+csource + #print '=>' + parts.append(csource[:match.start()]) + if 'C' in match.group(1): + parts.append('void __cffi_extern_python_plus_c_start; ') + else: + parts.append('void __cffi_extern_python_start; ') + if csource[endpos] == '{': + # grouping variant + closing = csource.find('}', endpos) + if closing < 0: + raise CDefError("'extern \"Python\" {': no '}' found") + if csource.find('{', endpos + 1, closing) >= 0: + raise NotImplementedError("cannot use { } inside a block " + "'extern \"Python\" { ... }'") + parts.append(csource[endpos+1:closing]) + csource = csource[closing+1:] + else: + # non-grouping variant + semicolon = csource.find(';', endpos) + if semicolon < 0: + raise CDefError("'extern \"Python\": no ';' found") + parts.append(csource[endpos:semicolon+1]) + csource = csource[semicolon+1:] + parts.append(' void __cffi_extern_python_stop;') + #print ''.join(parts)+csource + #print + parts.append(csource) + return ''.join(parts) + +def _warn_for_string_literal(csource): + if '"' not in csource: + return + for line in csource.splitlines(): + if '"' in line and not line.lstrip().startswith('#'): + import warnings + warnings.warn("String literal found in cdef() or type source. " + "String literals are ignored here, but you should " + "remove them anyway because some character sequences " + "confuse pre-parsing.") + break + +def _warn_for_non_extern_non_static_global_variable(decl): + if not decl.storage: + import warnings + warnings.warn("Global variable '%s' in cdef(): for consistency " + "with C it should have a storage class specifier " + "(usually 'extern')" % (decl.name,)) + +def _remove_line_directives(csource): + # _r_line_directive matches whole lines, without the final \n, if they + # start with '#line' with some spacing allowed, or '#NUMBER'. This + # function stores them away and replaces them with exactly the string + # '#line@N', where N is the index in the list 'line_directives'. + line_directives = [] + def replace(m): + i = len(line_directives) + line_directives.append(m.group()) + return '#line@%d' % i + csource = _r_line_directive.sub(replace, csource) + return csource, line_directives + +def _put_back_line_directives(csource, line_directives): + def replace(m): + s = m.group() + if not s.startswith('#line@'): + raise AssertionError("unexpected #line directive " + "(should have been processed and removed") + return line_directives[int(s[6:])] + return _r_line_directive.sub(replace, csource) + +def _preprocess(csource): + # First, remove the lines of the form '#line N "filename"' because + # the "filename" part could confuse the rest + csource, line_directives = _remove_line_directives(csource) + # Remove comments. NOTE: this only work because the cdef() section + # should not contain any string literals (except in line directives)! + def replace_keeping_newlines(m): + return ' ' + m.group().count('\n') * '\n' + csource = _r_comment.sub(replace_keeping_newlines, csource) + # Remove the "#define FOO x" lines + macros = {} + for match in _r_define.finditer(csource): + macroname, macrovalue = match.groups() + macrovalue = macrovalue.replace('\\\n', '').strip() + macros[macroname] = macrovalue + csource = _r_define.sub('', csource) + # + if pycparser.__version__ < '2.14': + csource = _workaround_for_old_pycparser(csource) + # + # BIG HACK: replace WINAPI or __stdcall with "volatile const". + # It doesn't make sense for the return type of a function to be + # "volatile volatile const", so we abuse it to detect __stdcall... + # Hack number 2 is that "int(volatile *fptr)();" is not valid C + # syntax, so we place the "volatile" before the opening parenthesis. + csource = _r_stdcall2.sub(' volatile volatile const(', csource) + csource = _r_stdcall1.sub(' volatile volatile const ', csource) + csource = _r_cdecl.sub(' ', csource) + # + # Replace `extern "Python"` with start/end markers + csource = _preprocess_extern_python(csource) + # + # Now there should not be any string literal left; warn if we get one + _warn_for_string_literal(csource) + # + # Replace "[...]" with "[__dotdotdotarray__]" + csource = _r_partial_array.sub('[__dotdotdotarray__]', csource) + # + # Replace "...}" with "__dotdotdotNUM__}". This construction should + # occur only at the end of enums; at the end of structs we have "...;}" + # and at the end of vararg functions "...);". Also replace "=...[,}]" + # with ",__dotdotdotNUM__[,}]": this occurs in the enums too, when + # giving an unknown value. + matches = list(_r_partial_enum.finditer(csource)) + for number, match in enumerate(reversed(matches)): + p = match.start() + if csource[p] == '=': + p2 = csource.find('...', p, match.end()) + assert p2 > p + csource = '%s,__dotdotdot%d__ %s' % (csource[:p], number, + csource[p2+3:]) + else: + assert csource[p:p+3] == '...' + csource = '%s __dotdotdot%d__ %s' % (csource[:p], number, + csource[p+3:]) + # Replace "int ..." or "unsigned long int..." with "__dotdotdotint__" + csource = _r_int_dotdotdot.sub(' __dotdotdotint__ ', csource) + # Replace "float ..." or "double..." with "__dotdotdotfloat__" + csource = _r_float_dotdotdot.sub(' __dotdotdotfloat__ ', csource) + # Replace all remaining "..." with the same name, "__dotdotdot__", + # which is declared with a typedef for the purpose of C parsing. + csource = csource.replace('...', ' __dotdotdot__ ') + # Finally, put back the line directives + csource = _put_back_line_directives(csource, line_directives) + return csource, macros + +def _common_type_names(csource): + # Look in the source for what looks like usages of types from the + # list of common types. A "usage" is approximated here as the + # appearance of the word, minus a "definition" of the type, which + # is the last word in a "typedef" statement. Approximative only + # but should be fine for all the common types. + look_for_words = set(COMMON_TYPES) + look_for_words.add(';') + look_for_words.add(',') + look_for_words.add('(') + look_for_words.add(')') + look_for_words.add('typedef') + words_used = set() + is_typedef = False + paren = 0 + previous_word = '' + for word in _r_words.findall(csource): + if word in look_for_words: + if word == ';': + if is_typedef: + words_used.discard(previous_word) + look_for_words.discard(previous_word) + is_typedef = False + elif word == 'typedef': + is_typedef = True + paren = 0 + elif word == '(': + paren += 1 + elif word == ')': + paren -= 1 + elif word == ',': + if is_typedef and paren == 0: + words_used.discard(previous_word) + look_for_words.discard(previous_word) + else: # word in COMMON_TYPES + words_used.add(word) + previous_word = word + return words_used + + +class Parser(object): + + def __init__(self): + self._declarations = {} + self._included_declarations = set() + self._anonymous_counter = 0 + self._structnode2type = weakref.WeakKeyDictionary() + self._options = {} + self._int_constants = {} + self._recomplete = [] + self._uses_new_feature = None + + def _parse(self, csource): + csource, macros = _preprocess(csource) + # XXX: for more efficiency we would need to poke into the + # internals of CParser... the following registers the + # typedefs, because their presence or absence influences the + # parsing itself (but what they are typedef'ed to plays no role) + ctn = _common_type_names(csource) + typenames = [] + for name in sorted(self._declarations): + if name.startswith('typedef '): + name = name[8:] + typenames.append(name) + ctn.discard(name) + typenames += sorted(ctn) + # + csourcelines = [] + csourcelines.append('# 1 ""') + for typename in typenames: + csourcelines.append('typedef int %s;' % typename) + csourcelines.append('typedef int __dotdotdotint__, __dotdotdotfloat__,' + ' __dotdotdot__;') + # this forces pycparser to consider the following in the file + # called from line 1 + csourcelines.append('# 1 "%s"' % (CDEF_SOURCE_STRING,)) + csourcelines.append(csource) + csourcelines.append('') # see test_missing_newline_bug + fullcsource = '\n'.join(csourcelines) + if lock is not None: + lock.acquire() # pycparser is not thread-safe... + try: + ast = _get_parser().parse(fullcsource) + except pycparser.c_parser.ParseError as e: + self.convert_pycparser_error(e, csource) + finally: + if lock is not None: + lock.release() + # csource will be used to find buggy source text + return ast, macros, csource + + def _convert_pycparser_error(self, e, csource): + # xxx look for ":NUM:" at the start of str(e) + # and interpret that as a line number. This will not work if + # the user gives explicit ``# NUM "FILE"`` directives. + line = None + msg = str(e) + match = re.match(r"%s:(\d+):" % (CDEF_SOURCE_STRING,), msg) + if match: + linenum = int(match.group(1), 10) + csourcelines = csource.splitlines() + if 1 <= linenum <= len(csourcelines): + line = csourcelines[linenum-1] + return line + + def convert_pycparser_error(self, e, csource): + line = self._convert_pycparser_error(e, csource) + + msg = str(e) + if line: + msg = 'cannot parse "%s"\n%s' % (line.strip(), msg) + else: + msg = 'parse error\n%s' % (msg,) + raise CDefError(msg) + + def parse(self, csource, override=False, packed=False, pack=None, + dllexport=False): + if packed: + if packed != True: + raise ValueError("'packed' should be False or True; use " + "'pack' to give another value") + if pack: + raise ValueError("cannot give both 'pack' and 'packed'") + pack = 1 + elif pack: + if pack & (pack - 1): + raise ValueError("'pack' must be a power of two, not %r" % + (pack,)) + else: + pack = 0 + prev_options = self._options + try: + self._options = {'override': override, + 'packed': pack, + 'dllexport': dllexport} + self._internal_parse(csource) + finally: + self._options = prev_options + + def _internal_parse(self, csource): + ast, macros, csource = self._parse(csource) + # add the macros + self._process_macros(macros) + # find the first "__dotdotdot__" and use that as a separator + # between the repeated typedefs and the real csource + iterator = iter(ast.ext) + for decl in iterator: + if decl.name == '__dotdotdot__': + break + else: + assert 0 + current_decl = None + # + try: + self._inside_extern_python = '__cffi_extern_python_stop' + for decl in iterator: + current_decl = decl + if isinstance(decl, pycparser.c_ast.Decl): + self._parse_decl(decl) + elif isinstance(decl, pycparser.c_ast.Typedef): + if not decl.name: + raise CDefError("typedef does not declare any name", + decl) + quals = 0 + if (isinstance(decl.type.type, pycparser.c_ast.IdentifierType) and + decl.type.type.names[-1].startswith('__dotdotdot')): + realtype = self._get_unknown_type(decl) + elif (isinstance(decl.type, pycparser.c_ast.PtrDecl) and + isinstance(decl.type.type, pycparser.c_ast.TypeDecl) and + isinstance(decl.type.type.type, + pycparser.c_ast.IdentifierType) and + decl.type.type.type.names[-1].startswith('__dotdotdot')): + realtype = self._get_unknown_ptr_type(decl) + else: + realtype, quals = self._get_type_and_quals( + decl.type, name=decl.name, partial_length_ok=True, + typedef_example="*(%s *)0" % (decl.name,)) + self._declare('typedef ' + decl.name, realtype, quals=quals) + elif decl.__class__.__name__ == 'Pragma': + # skip pragma, only in pycparser 2.15 + import warnings + warnings.warn( + "#pragma in cdef() are entirely ignored. " + "They should be removed for now, otherwise your " + "code might behave differently in a future version " + "of CFFI if #pragma support gets added. Note that " + "'#pragma pack' needs to be replaced with the " + "'packed' keyword argument to cdef().") + else: + raise CDefError("unexpected <%s>: this construct is valid " + "C but not valid in cdef()" % + decl.__class__.__name__, decl) + except CDefError as e: + if len(e.args) == 1: + e.args = e.args + (current_decl,) + raise + except FFIError as e: + msg = self._convert_pycparser_error(e, csource) + if msg: + e.args = (e.args[0] + "\n *** Err: %s" % msg,) + raise + + def _add_constants(self, key, val): + if key in self._int_constants: + if self._int_constants[key] == val: + return # ignore identical double declarations + raise FFIError( + "multiple declarations of constant: %s" % (key,)) + self._int_constants[key] = val + + def _add_integer_constant(self, name, int_str): + int_str = int_str.lower().rstrip("ul") + neg = int_str.startswith('-') + if neg: + int_str = int_str[1:] + # "010" is not valid oct in py3 + if (int_str.startswith("0") and int_str != '0' + and not int_str.startswith("0x")): + int_str = "0o" + int_str[1:] + pyvalue = int(int_str, 0) + if neg: + pyvalue = -pyvalue + self._add_constants(name, pyvalue) + self._declare('macro ' + name, pyvalue) + + def _process_macros(self, macros): + for key, value in macros.items(): + value = value.strip() + if _r_int_literal.match(value): + self._add_integer_constant(key, value) + elif value == '...': + self._declare('macro ' + key, value) + else: + raise CDefError( + 'only supports one of the following syntax:\n' + ' #define %s ... (literally dot-dot-dot)\n' + ' #define %s NUMBER (with NUMBER an integer' + ' constant, decimal/hex/octal)\n' + 'got:\n' + ' #define %s %s' + % (key, key, key, value)) + + def _declare_function(self, tp, quals, decl): + tp = self._get_type_pointer(tp, quals) + if self._options.get('dllexport'): + tag = 'dllexport_python ' + elif self._inside_extern_python == '__cffi_extern_python_start': + tag = 'extern_python ' + elif self._inside_extern_python == '__cffi_extern_python_plus_c_start': + tag = 'extern_python_plus_c ' + else: + tag = 'function ' + self._declare(tag + decl.name, tp) + + def _parse_decl(self, decl): + node = decl.type + if isinstance(node, pycparser.c_ast.FuncDecl): + tp, quals = self._get_type_and_quals(node, name=decl.name) + assert isinstance(tp, model.RawFunctionType) + self._declare_function(tp, quals, decl) + else: + if isinstance(node, pycparser.c_ast.Struct): + self._get_struct_union_enum_type('struct', node) + elif isinstance(node, pycparser.c_ast.Union): + self._get_struct_union_enum_type('union', node) + elif isinstance(node, pycparser.c_ast.Enum): + self._get_struct_union_enum_type('enum', node) + elif not decl.name: + raise CDefError("construct does not declare any variable", + decl) + # + if decl.name: + tp, quals = self._get_type_and_quals(node, + partial_length_ok=True) + if tp.is_raw_function: + self._declare_function(tp, quals, decl) + elif (tp.is_integer_type() and + hasattr(decl, 'init') and + hasattr(decl.init, 'value') and + _r_int_literal.match(decl.init.value)): + self._add_integer_constant(decl.name, decl.init.value) + elif (tp.is_integer_type() and + isinstance(decl.init, pycparser.c_ast.UnaryOp) and + decl.init.op == '-' and + hasattr(decl.init.expr, 'value') and + _r_int_literal.match(decl.init.expr.value)): + self._add_integer_constant(decl.name, + '-' + decl.init.expr.value) + elif (tp is model.void_type and + decl.name.startswith('__cffi_extern_python_')): + # hack: `extern "Python"` in the C source is replaced + # with "void __cffi_extern_python_start;" and + # "void __cffi_extern_python_stop;" + self._inside_extern_python = decl.name + else: + if self._inside_extern_python !='__cffi_extern_python_stop': + raise CDefError( + "cannot declare constants or " + "variables with 'extern \"Python\"'") + if (quals & model.Q_CONST) and not tp.is_array_type: + self._declare('constant ' + decl.name, tp, quals=quals) + else: + _warn_for_non_extern_non_static_global_variable(decl) + self._declare('variable ' + decl.name, tp, quals=quals) + + def parse_type(self, cdecl): + return self.parse_type_and_quals(cdecl)[0] + + def parse_type_and_quals(self, cdecl): + ast, macros = self._parse('void __dummy(\n%s\n);' % cdecl)[:2] + assert not macros + exprnode = ast.ext[-1].type.args.params[0] + if isinstance(exprnode, pycparser.c_ast.ID): + raise CDefError("unknown identifier '%s'" % (exprnode.name,)) + return self._get_type_and_quals(exprnode.type) + + def _declare(self, name, obj, included=False, quals=0): + if name in self._declarations: + prevobj, prevquals = self._declarations[name] + if prevobj is obj and prevquals == quals: + return + if not self._options.get('override'): + raise FFIError( + "multiple declarations of %s (for interactive usage, " + "try cdef(xx, override=True))" % (name,)) + assert '__dotdotdot__' not in name.split() + self._declarations[name] = (obj, quals) + if included: + self._included_declarations.add(obj) + + def _extract_quals(self, type): + quals = 0 + if isinstance(type, (pycparser.c_ast.TypeDecl, + pycparser.c_ast.PtrDecl)): + if 'const' in type.quals: + quals |= model.Q_CONST + if 'volatile' in type.quals: + quals |= model.Q_VOLATILE + if 'restrict' in type.quals: + quals |= model.Q_RESTRICT + return quals + + def _get_type_pointer(self, type, quals, declname=None): + if isinstance(type, model.RawFunctionType): + return type.as_function_pointer() + if (isinstance(type, model.StructOrUnionOrEnum) and + type.name.startswith('$') and type.name[1:].isdigit() and + type.forcename is None and declname is not None): + return model.NamedPointerType(type, declname, quals) + return model.PointerType(type, quals) + + def _get_type_and_quals(self, typenode, name=None, partial_length_ok=False, + typedef_example=None): + # first, dereference typedefs, if we have it already parsed, we're good + if (isinstance(typenode, pycparser.c_ast.TypeDecl) and + isinstance(typenode.type, pycparser.c_ast.IdentifierType) and + len(typenode.type.names) == 1 and + ('typedef ' + typenode.type.names[0]) in self._declarations): + tp, quals = self._declarations['typedef ' + typenode.type.names[0]] + quals |= self._extract_quals(typenode) + return tp, quals + # + if isinstance(typenode, pycparser.c_ast.ArrayDecl): + # array type + if typenode.dim is None: + length = None + else: + length = self._parse_constant( + typenode.dim, partial_length_ok=partial_length_ok) + # a hack: in 'typedef int foo_t[...][...];', don't use '...' as + # the length but use directly the C expression that would be + # generated by recompiler.py. This lets the typedef be used in + # many more places within recompiler.py + if typedef_example is not None: + if length == '...': + length = '_cffi_array_len(%s)' % (typedef_example,) + typedef_example = "*" + typedef_example + # + tp, quals = self._get_type_and_quals(typenode.type, + partial_length_ok=partial_length_ok, + typedef_example=typedef_example) + return model.ArrayType(tp, length), quals + # + if isinstance(typenode, pycparser.c_ast.PtrDecl): + # pointer type + itemtype, itemquals = self._get_type_and_quals(typenode.type) + tp = self._get_type_pointer(itemtype, itemquals, declname=name) + quals = self._extract_quals(typenode) + return tp, quals + # + if isinstance(typenode, pycparser.c_ast.TypeDecl): + quals = self._extract_quals(typenode) + type = typenode.type + if isinstance(type, pycparser.c_ast.IdentifierType): + # assume a primitive type. get it from .names, but reduce + # synonyms to a single chosen combination + names = list(type.names) + if names != ['signed', 'char']: # keep this unmodified + prefixes = {} + while names: + name = names[0] + if name in ('short', 'long', 'signed', 'unsigned'): + prefixes[name] = prefixes.get(name, 0) + 1 + del names[0] + else: + break + # ignore the 'signed' prefix below, and reorder the others + newnames = [] + for prefix in ('unsigned', 'short', 'long'): + for i in range(prefixes.get(prefix, 0)): + newnames.append(prefix) + if not names: + names = ['int'] # implicitly + if names == ['int']: # but kill it if 'short' or 'long' + if 'short' in prefixes or 'long' in prefixes: + names = [] + names = newnames + names + ident = ' '.join(names) + if ident == 'void': + return model.void_type, quals + if ident == '__dotdotdot__': + raise FFIError(':%d: bad usage of "..."' % + typenode.coord.line) + tp0, quals0 = resolve_common_type(self, ident) + return tp0, (quals | quals0) + # + if isinstance(type, pycparser.c_ast.Struct): + # 'struct foobar' + tp = self._get_struct_union_enum_type('struct', type, name) + return tp, quals + # + if isinstance(type, pycparser.c_ast.Union): + # 'union foobar' + tp = self._get_struct_union_enum_type('union', type, name) + return tp, quals + # + if isinstance(type, pycparser.c_ast.Enum): + # 'enum foobar' + tp = self._get_struct_union_enum_type('enum', type, name) + return tp, quals + # + if isinstance(typenode, pycparser.c_ast.FuncDecl): + # a function type + return self._parse_function_type(typenode, name), 0 + # + # nested anonymous structs or unions end up here + if isinstance(typenode, pycparser.c_ast.Struct): + return self._get_struct_union_enum_type('struct', typenode, name, + nested=True), 0 + if isinstance(typenode, pycparser.c_ast.Union): + return self._get_struct_union_enum_type('union', typenode, name, + nested=True), 0 + # + raise FFIError(":%d: bad or unsupported type declaration" % + typenode.coord.line) + + def _parse_function_type(self, typenode, funcname=None): + params = list(getattr(typenode.args, 'params', [])) + for i, arg in enumerate(params): + if not hasattr(arg, 'type'): + raise CDefError("%s arg %d: unknown type '%s'" + " (if you meant to use the old C syntax of giving" + " untyped arguments, it is not supported)" + % (funcname or 'in expression', i + 1, + getattr(arg, 'name', '?'))) + ellipsis = ( + len(params) > 0 and + isinstance(params[-1].type, pycparser.c_ast.TypeDecl) and + isinstance(params[-1].type.type, + pycparser.c_ast.IdentifierType) and + params[-1].type.type.names == ['__dotdotdot__']) + if ellipsis: + params.pop() + if not params: + raise CDefError( + "%s: a function with only '(...)' as argument" + " is not correct C" % (funcname or 'in expression')) + args = [self._as_func_arg(*self._get_type_and_quals(argdeclnode.type)) + for argdeclnode in params] + if not ellipsis and args == [model.void_type]: + args = [] + result, quals = self._get_type_and_quals(typenode.type) + # the 'quals' on the result type are ignored. HACK: we absure them + # to detect __stdcall functions: we textually replace "__stdcall" + # with "volatile volatile const" above. + abi = None + if hasattr(typenode.type, 'quals'): # else, probable syntax error anyway + if typenode.type.quals[-3:] == ['volatile', 'volatile', 'const']: + abi = '__stdcall' + return model.RawFunctionType(tuple(args), result, ellipsis, abi) + + def _as_func_arg(self, type, quals): + if isinstance(type, model.ArrayType): + return model.PointerType(type.item, quals) + elif isinstance(type, model.RawFunctionType): + return type.as_function_pointer() + else: + return type + + def _get_struct_union_enum_type(self, kind, type, name=None, nested=False): + # First, a level of caching on the exact 'type' node of the AST. + # This is obscure, but needed because pycparser "unrolls" declarations + # such as "typedef struct { } foo_t, *foo_p" and we end up with + # an AST that is not a tree, but a DAG, with the "type" node of the + # two branches foo_t and foo_p of the trees being the same node. + # It's a bit silly but detecting "DAG-ness" in the AST tree seems + # to be the only way to distinguish this case from two independent + # structs. See test_struct_with_two_usages. + try: + return self._structnode2type[type] + except KeyError: + pass + # + # Note that this must handle parsing "struct foo" any number of + # times and always return the same StructType object. Additionally, + # one of these times (not necessarily the first), the fields of + # the struct can be specified with "struct foo { ...fields... }". + # If no name is given, then we have to create a new anonymous struct + # with no caching; in this case, the fields are either specified + # right now or never. + # + force_name = name + name = type.name + # + # get the type or create it if needed + if name is None: + # 'force_name' is used to guess a more readable name for + # anonymous structs, for the common case "typedef struct { } foo". + if force_name is not None: + explicit_name = '$%s' % force_name + else: + self._anonymous_counter += 1 + explicit_name = '$%d' % self._anonymous_counter + tp = None + else: + explicit_name = name + key = '%s %s' % (kind, name) + tp, _ = self._declarations.get(key, (None, None)) + # + if tp is None: + if kind == 'struct': + tp = model.StructType(explicit_name, None, None, None) + elif kind == 'union': + tp = model.UnionType(explicit_name, None, None, None) + elif kind == 'enum': + if explicit_name == '__dotdotdot__': + raise CDefError("Enums cannot be declared with ...") + tp = self._build_enum_type(explicit_name, type.values) + else: + raise AssertionError("kind = %r" % (kind,)) + if name is not None: + self._declare(key, tp) + else: + if kind == 'enum' and type.values is not None: + raise NotImplementedError( + "enum %s: the '{}' declaration should appear on the first " + "time the enum is mentioned, not later" % explicit_name) + if not tp.forcename: + tp.force_the_name(force_name) + if tp.forcename and '$' in tp.name: + self._declare('anonymous %s' % tp.forcename, tp) + # + self._structnode2type[type] = tp + # + # enums: done here + if kind == 'enum': + return tp + # + # is there a 'type.decls'? If yes, then this is the place in the + # C sources that declare the fields. If no, then just return the + # existing type, possibly still incomplete. + if type.decls is None: + return tp + # + if tp.fldnames is not None: + raise CDefError("duplicate declaration of struct %s" % name) + fldnames = [] + fldtypes = [] + fldbitsize = [] + fldquals = [] + for decl in type.decls: + if (isinstance(decl.type, pycparser.c_ast.IdentifierType) and + ''.join(decl.type.names) == '__dotdotdot__'): + # XXX pycparser is inconsistent: 'names' should be a list + # of strings, but is sometimes just one string. Use + # str.join() as a way to cope with both. + self._make_partial(tp, nested) + continue + if decl.bitsize is None: + bitsize = -1 + else: + bitsize = self._parse_constant(decl.bitsize) + self._partial_length = False + type, fqual = self._get_type_and_quals(decl.type, + partial_length_ok=True) + if self._partial_length: + self._make_partial(tp, nested) + if isinstance(type, model.StructType) and type.partial: + self._make_partial(tp, nested) + fldnames.append(decl.name or '') + fldtypes.append(type) + fldbitsize.append(bitsize) + fldquals.append(fqual) + tp.fldnames = tuple(fldnames) + tp.fldtypes = tuple(fldtypes) + tp.fldbitsize = tuple(fldbitsize) + tp.fldquals = tuple(fldquals) + if fldbitsize != [-1] * len(fldbitsize): + if isinstance(tp, model.StructType) and tp.partial: + raise NotImplementedError("%s: using both bitfields and '...;'" + % (tp,)) + tp.packed = self._options.get('packed') + if tp.completed: # must be re-completed: it is not opaque any more + tp.completed = 0 + self._recomplete.append(tp) + return tp + + def _make_partial(self, tp, nested): + if not isinstance(tp, model.StructOrUnion): + raise CDefError("%s cannot be partial" % (tp,)) + if not tp.has_c_name() and not nested: + raise NotImplementedError("%s is partial but has no C name" %(tp,)) + tp.partial = True + + def _parse_constant(self, exprnode, partial_length_ok=False): + # for now, limited to expressions that are an immediate number + # or positive/negative number + if isinstance(exprnode, pycparser.c_ast.Constant): + s = exprnode.value + if '0' <= s[0] <= '9': + s = s.rstrip('uUlL') + try: + if s.startswith('0'): + return int(s, 8) + else: + return int(s, 10) + except ValueError: + if len(s) > 1: + if s.lower()[0:2] == '0x': + return int(s, 16) + elif s.lower()[0:2] == '0b': + return int(s, 2) + raise CDefError("invalid constant %r" % (s,)) + elif s[0] == "'" and s[-1] == "'" and ( + len(s) == 3 or (len(s) == 4 and s[1] == "\\")): + return ord(s[-2]) + else: + raise CDefError("invalid constant %r" % (s,)) + # + if (isinstance(exprnode, pycparser.c_ast.UnaryOp) and + exprnode.op == '+'): + return self._parse_constant(exprnode.expr) + # + if (isinstance(exprnode, pycparser.c_ast.UnaryOp) and + exprnode.op == '-'): + return -self._parse_constant(exprnode.expr) + # load previously defined int constant + if (isinstance(exprnode, pycparser.c_ast.ID) and + exprnode.name in self._int_constants): + return self._int_constants[exprnode.name] + # + if (isinstance(exprnode, pycparser.c_ast.ID) and + exprnode.name == '__dotdotdotarray__'): + if partial_length_ok: + self._partial_length = True + return '...' + raise FFIError(":%d: unsupported '[...]' here, cannot derive " + "the actual array length in this context" + % exprnode.coord.line) + # + if isinstance(exprnode, pycparser.c_ast.BinaryOp): + left = self._parse_constant(exprnode.left) + right = self._parse_constant(exprnode.right) + if exprnode.op == '+': + return left + right + elif exprnode.op == '-': + return left - right + elif exprnode.op == '*': + return left * right + elif exprnode.op == '/': + return self._c_div(left, right) + elif exprnode.op == '%': + return left - self._c_div(left, right) * right + elif exprnode.op == '<<': + return left << right + elif exprnode.op == '>>': + return left >> right + elif exprnode.op == '&': + return left & right + elif exprnode.op == '|': + return left | right + elif exprnode.op == '^': + return left ^ right + # + raise FFIError(":%d: unsupported expression: expected a " + "simple numeric constant" % exprnode.coord.line) + + def _c_div(self, a, b): + result = a // b + if ((a < 0) ^ (b < 0)) and (a % b) != 0: + result += 1 + return result + + def _build_enum_type(self, explicit_name, decls): + if decls is not None: + partial = False + enumerators = [] + enumvalues = [] + nextenumvalue = 0 + for enum in decls.enumerators: + if _r_enum_dotdotdot.match(enum.name): + partial = True + continue + if enum.value is not None: + nextenumvalue = self._parse_constant(enum.value) + enumerators.append(enum.name) + enumvalues.append(nextenumvalue) + self._add_constants(enum.name, nextenumvalue) + nextenumvalue += 1 + enumerators = tuple(enumerators) + enumvalues = tuple(enumvalues) + tp = model.EnumType(explicit_name, enumerators, enumvalues) + tp.partial = partial + else: # opaque enum + tp = model.EnumType(explicit_name, (), ()) + return tp + + def include(self, other): + for name, (tp, quals) in other._declarations.items(): + if name.startswith('anonymous $enum_$'): + continue # fix for test_anonymous_enum_include + kind = name.split(' ', 1)[0] + if kind in ('struct', 'union', 'enum', 'anonymous', 'typedef'): + self._declare(name, tp, included=True, quals=quals) + for k, v in other._int_constants.items(): + self._add_constants(k, v) + + def _get_unknown_type(self, decl): + typenames = decl.type.type.names + if typenames == ['__dotdotdot__']: + return model.unknown_type(decl.name) + + if typenames == ['__dotdotdotint__']: + if self._uses_new_feature is None: + self._uses_new_feature = "'typedef int... %s'" % decl.name + return model.UnknownIntegerType(decl.name) + + if typenames == ['__dotdotdotfloat__']: + # note: not for 'long double' so far + if self._uses_new_feature is None: + self._uses_new_feature = "'typedef float... %s'" % decl.name + return model.UnknownFloatType(decl.name) + + raise FFIError(':%d: unsupported usage of "..." in typedef' + % decl.coord.line) + + def _get_unknown_ptr_type(self, decl): + if decl.type.type.type.names == ['__dotdotdot__']: + return model.unknown_ptr_type(decl.name) + raise FFIError(':%d: unsupported usage of "..." in typedef' + % decl.coord.line) diff --git a/code/.venv/lib/python3.12/site-packages/cffi/error.py b/code/.venv/lib/python3.12/site-packages/cffi/error.py new file mode 100644 index 0000000..0a27247 --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/cffi/error.py @@ -0,0 +1,31 @@ + +class FFIError(Exception): + __module__ = 'cffi' + +class CDefError(Exception): + __module__ = 'cffi' + def __str__(self): + try: + current_decl = self.args[1] + filename = current_decl.coord.file + linenum = current_decl.coord.line + prefix = '%s:%d: ' % (filename, linenum) + except (AttributeError, TypeError, IndexError): + prefix = '' + return '%s%s' % (prefix, self.args[0]) + +class VerificationError(Exception): + """ An error raised when verification fails + """ + __module__ = 'cffi' + +class VerificationMissing(Exception): + """ An error raised when incomplete structures are passed into + cdef, but no verification has been done + """ + __module__ = 'cffi' + +class PkgConfigError(Exception): + """ An error raised for missing modules in pkg-config + """ + __module__ = 'cffi' diff --git a/code/.venv/lib/python3.12/site-packages/cffi/ffiplatform.py b/code/.venv/lib/python3.12/site-packages/cffi/ffiplatform.py new file mode 100644 index 0000000..adca28f --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/cffi/ffiplatform.py @@ -0,0 +1,113 @@ +import sys, os +from .error import VerificationError + + +LIST_OF_FILE_NAMES = ['sources', 'include_dirs', 'library_dirs', + 'extra_objects', 'depends'] + +def get_extension(srcfilename, modname, sources=(), **kwds): + from cffi._shimmed_dist_utils import Extension + allsources = [srcfilename] + for src in sources: + allsources.append(os.path.normpath(src)) + return Extension(name=modname, sources=allsources, **kwds) + +def compile(tmpdir, ext, compiler_verbose=0, debug=None): + """Compile a C extension module using distutils.""" + + saved_environ = os.environ.copy() + try: + outputfilename = _build(tmpdir, ext, compiler_verbose, debug) + outputfilename = os.path.abspath(outputfilename) + finally: + # workaround for a distutils bugs where some env vars can + # become longer and longer every time it is used + for key, value in saved_environ.items(): + if os.environ.get(key) != value: + os.environ[key] = value + return outputfilename + +def _build(tmpdir, ext, compiler_verbose=0, debug=None): + # XXX compact but horrible :-( + from cffi._shimmed_dist_utils import Distribution, CompileError, LinkError, set_threshold, set_verbosity + + dist = Distribution({'ext_modules': [ext]}) + dist.parse_config_files() + options = dist.get_option_dict('build_ext') + if debug is None: + debug = sys.flags.debug + options['debug'] = ('ffiplatform', debug) + options['force'] = ('ffiplatform', True) + options['build_lib'] = ('ffiplatform', tmpdir) + options['build_temp'] = ('ffiplatform', tmpdir) + # + try: + old_level = set_threshold(0) or 0 + try: + set_verbosity(compiler_verbose) + dist.run_command('build_ext') + cmd_obj = dist.get_command_obj('build_ext') + [soname] = cmd_obj.get_outputs() + finally: + set_threshold(old_level) + except (CompileError, LinkError) as e: + raise VerificationError('%s: %s' % (e.__class__.__name__, e)) + # + return soname + +try: + from os.path import samefile +except ImportError: + def samefile(f1, f2): + return os.path.abspath(f1) == os.path.abspath(f2) + +def maybe_relative_path(path): + if not os.path.isabs(path): + return path # already relative + dir = path + names = [] + while True: + prevdir = dir + dir, name = os.path.split(prevdir) + if dir == prevdir or not dir: + return path # failed to make it relative + names.append(name) + try: + if samefile(dir, os.curdir): + names.reverse() + return os.path.join(*names) + except OSError: + pass + +# ____________________________________________________________ + +try: + int_or_long = (int, long) + import cStringIO +except NameError: + int_or_long = int # Python 3 + import io as cStringIO + +def _flatten(x, f): + if isinstance(x, str): + f.write('%ds%s' % (len(x), x)) + elif isinstance(x, dict): + keys = sorted(x.keys()) + f.write('%dd' % len(keys)) + for key in keys: + _flatten(key, f) + _flatten(x[key], f) + elif isinstance(x, (list, tuple)): + f.write('%dl' % len(x)) + for value in x: + _flatten(value, f) + elif isinstance(x, int_or_long): + f.write('%di' % (x,)) + else: + raise TypeError( + "the keywords to verify() contains unsupported object %r" % (x,)) + +def flatten(x): + f = cStringIO.StringIO() + _flatten(x, f) + return f.getvalue() diff --git a/code/.venv/lib/python3.12/site-packages/cffi/lock.py b/code/.venv/lib/python3.12/site-packages/cffi/lock.py new file mode 100644 index 0000000..db91b71 --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/cffi/lock.py @@ -0,0 +1,30 @@ +import sys + +if sys.version_info < (3,): + try: + from thread import allocate_lock + except ImportError: + from dummy_thread import allocate_lock +else: + try: + from _thread import allocate_lock + except ImportError: + from _dummy_thread import allocate_lock + + +##import sys +##l1 = allocate_lock + +##class allocate_lock(object): +## def __init__(self): +## self._real = l1() +## def __enter__(self): +## for i in range(4, 0, -1): +## print sys._getframe(i).f_code +## print +## return self._real.__enter__() +## def __exit__(self, *args): +## return self._real.__exit__(*args) +## def acquire(self, f): +## assert f is False +## return self._real.acquire(f) diff --git a/code/.venv/lib/python3.12/site-packages/cffi/model.py b/code/.venv/lib/python3.12/site-packages/cffi/model.py new file mode 100644 index 0000000..e5f4cae --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/cffi/model.py @@ -0,0 +1,618 @@ +import types +import weakref + +from .lock import allocate_lock +from .error import CDefError, VerificationError, VerificationMissing + +# type qualifiers +Q_CONST = 0x01 +Q_RESTRICT = 0x02 +Q_VOLATILE = 0x04 + +def qualify(quals, replace_with): + if quals & Q_CONST: + replace_with = ' const ' + replace_with.lstrip() + if quals & Q_VOLATILE: + replace_with = ' volatile ' + replace_with.lstrip() + if quals & Q_RESTRICT: + # It seems that __restrict is supported by gcc and msvc. + # If you hit some different compiler, add a #define in + # _cffi_include.h for it (and in its copies, documented there) + replace_with = ' __restrict ' + replace_with.lstrip() + return replace_with + + +class BaseTypeByIdentity(object): + is_array_type = False + is_raw_function = False + + def get_c_name(self, replace_with='', context='a C file', quals=0): + result = self.c_name_with_marker + assert result.count('&') == 1 + # some logic duplication with ffi.getctype()... :-( + replace_with = replace_with.strip() + if replace_with: + if replace_with.startswith('*') and '&[' in result: + replace_with = '(%s)' % replace_with + elif not replace_with[0] in '[(': + replace_with = ' ' + replace_with + replace_with = qualify(quals, replace_with) + result = result.replace('&', replace_with) + if '$' in result: + raise VerificationError( + "cannot generate '%s' in %s: unknown type name" + % (self._get_c_name(), context)) + return result + + def _get_c_name(self): + return self.c_name_with_marker.replace('&', '') + + def has_c_name(self): + return '$' not in self._get_c_name() + + def is_integer_type(self): + return False + + def get_cached_btype(self, ffi, finishlist, can_delay=False): + try: + BType = ffi._cached_btypes[self] + except KeyError: + BType = self.build_backend_type(ffi, finishlist) + BType2 = ffi._cached_btypes.setdefault(self, BType) + assert BType2 is BType + return BType + + def __repr__(self): + return '<%s>' % (self._get_c_name(),) + + def _get_items(self): + return [(name, getattr(self, name)) for name in self._attrs_] + + +class BaseType(BaseTypeByIdentity): + + def __eq__(self, other): + return (self.__class__ == other.__class__ and + self._get_items() == other._get_items()) + + def __ne__(self, other): + return not self == other + + def __hash__(self): + return hash((self.__class__, tuple(self._get_items()))) + + +class VoidType(BaseType): + _attrs_ = () + + def __init__(self): + self.c_name_with_marker = 'void&' + + def build_backend_type(self, ffi, finishlist): + return global_cache(self, ffi, 'new_void_type') + +void_type = VoidType() + + +class BasePrimitiveType(BaseType): + def is_complex_type(self): + return False + + +class PrimitiveType(BasePrimitiveType): + _attrs_ = ('name',) + + ALL_PRIMITIVE_TYPES = { + 'char': 'c', + 'short': 'i', + 'int': 'i', + 'long': 'i', + 'long long': 'i', + 'signed char': 'i', + 'unsigned char': 'i', + 'unsigned short': 'i', + 'unsigned int': 'i', + 'unsigned long': 'i', + 'unsigned long long': 'i', + 'float': 'f', + 'double': 'f', + 'long double': 'f', + '_cffi_float_complex_t': 'j', + '_cffi_double_complex_t': 'j', + '_Bool': 'i', + # the following types are not primitive in the C sense + 'wchar_t': 'c', + 'char16_t': 'c', + 'char32_t': 'c', + 'int8_t': 'i', + 'uint8_t': 'i', + 'int16_t': 'i', + 'uint16_t': 'i', + 'int32_t': 'i', + 'uint32_t': 'i', + 'int64_t': 'i', + 'uint64_t': 'i', + 'int_least8_t': 'i', + 'uint_least8_t': 'i', + 'int_least16_t': 'i', + 'uint_least16_t': 'i', + 'int_least32_t': 'i', + 'uint_least32_t': 'i', + 'int_least64_t': 'i', + 'uint_least64_t': 'i', + 'int_fast8_t': 'i', + 'uint_fast8_t': 'i', + 'int_fast16_t': 'i', + 'uint_fast16_t': 'i', + 'int_fast32_t': 'i', + 'uint_fast32_t': 'i', + 'int_fast64_t': 'i', + 'uint_fast64_t': 'i', + 'intptr_t': 'i', + 'uintptr_t': 'i', + 'intmax_t': 'i', + 'uintmax_t': 'i', + 'ptrdiff_t': 'i', + 'size_t': 'i', + 'ssize_t': 'i', + } + + def __init__(self, name): + assert name in self.ALL_PRIMITIVE_TYPES + self.name = name + self.c_name_with_marker = name + '&' + + def is_char_type(self): + return self.ALL_PRIMITIVE_TYPES[self.name] == 'c' + def is_integer_type(self): + return self.ALL_PRIMITIVE_TYPES[self.name] == 'i' + def is_float_type(self): + return self.ALL_PRIMITIVE_TYPES[self.name] == 'f' + def is_complex_type(self): + return self.ALL_PRIMITIVE_TYPES[self.name] == 'j' + + def build_backend_type(self, ffi, finishlist): + return global_cache(self, ffi, 'new_primitive_type', self.name) + + +class UnknownIntegerType(BasePrimitiveType): + _attrs_ = ('name',) + + def __init__(self, name): + self.name = name + self.c_name_with_marker = name + '&' + + def is_integer_type(self): + return True + + def build_backend_type(self, ffi, finishlist): + raise NotImplementedError("integer type '%s' can only be used after " + "compilation" % self.name) + +class UnknownFloatType(BasePrimitiveType): + _attrs_ = ('name', ) + + def __init__(self, name): + self.name = name + self.c_name_with_marker = name + '&' + + def build_backend_type(self, ffi, finishlist): + raise NotImplementedError("float type '%s' can only be used after " + "compilation" % self.name) + + +class BaseFunctionType(BaseType): + _attrs_ = ('args', 'result', 'ellipsis', 'abi') + + def __init__(self, args, result, ellipsis, abi=None): + self.args = args + self.result = result + self.ellipsis = ellipsis + self.abi = abi + # + reprargs = [arg._get_c_name() for arg in self.args] + if self.ellipsis: + reprargs.append('...') + reprargs = reprargs or ['void'] + replace_with = self._base_pattern % (', '.join(reprargs),) + if abi is not None: + replace_with = replace_with[:1] + abi + ' ' + replace_with[1:] + self.c_name_with_marker = ( + self.result.c_name_with_marker.replace('&', replace_with)) + + +class RawFunctionType(BaseFunctionType): + # Corresponds to a C type like 'int(int)', which is the C type of + # a function, but not a pointer-to-function. The backend has no + # notion of such a type; it's used temporarily by parsing. + _base_pattern = '(&)(%s)' + is_raw_function = True + + def build_backend_type(self, ffi, finishlist): + raise CDefError("cannot render the type %r: it is a function " + "type, not a pointer-to-function type" % (self,)) + + def as_function_pointer(self): + return FunctionPtrType(self.args, self.result, self.ellipsis, self.abi) + + +class FunctionPtrType(BaseFunctionType): + _base_pattern = '(*&)(%s)' + + def build_backend_type(self, ffi, finishlist): + result = self.result.get_cached_btype(ffi, finishlist) + args = [] + for tp in self.args: + args.append(tp.get_cached_btype(ffi, finishlist)) + abi_args = () + if self.abi == "__stdcall": + if not self.ellipsis: # __stdcall ignored for variadic funcs + try: + abi_args = (ffi._backend.FFI_STDCALL,) + except AttributeError: + pass + return global_cache(self, ffi, 'new_function_type', + tuple(args), result, self.ellipsis, *abi_args) + + def as_raw_function(self): + return RawFunctionType(self.args, self.result, self.ellipsis, self.abi) + + +class PointerType(BaseType): + _attrs_ = ('totype', 'quals') + + def __init__(self, totype, quals=0): + self.totype = totype + self.quals = quals + extra = " *&" + if totype.is_array_type: + extra = "(%s)" % (extra.lstrip(),) + extra = qualify(quals, extra) + self.c_name_with_marker = totype.c_name_with_marker.replace('&', extra) + + def build_backend_type(self, ffi, finishlist): + BItem = self.totype.get_cached_btype(ffi, finishlist, can_delay=True) + return global_cache(self, ffi, 'new_pointer_type', BItem) + +voidp_type = PointerType(void_type) + +def ConstPointerType(totype): + return PointerType(totype, Q_CONST) + +const_voidp_type = ConstPointerType(void_type) + + +class NamedPointerType(PointerType): + _attrs_ = ('totype', 'name') + + def __init__(self, totype, name, quals=0): + PointerType.__init__(self, totype, quals) + self.name = name + self.c_name_with_marker = name + '&' + + +class ArrayType(BaseType): + _attrs_ = ('item', 'length') + is_array_type = True + + def __init__(self, item, length): + self.item = item + self.length = length + # + if length is None: + brackets = '&[]' + elif length == '...': + brackets = '&[/*...*/]' + else: + brackets = '&[%s]' % length + self.c_name_with_marker = ( + self.item.c_name_with_marker.replace('&', brackets)) + + def length_is_unknown(self): + return isinstance(self.length, str) + + def resolve_length(self, newlength): + return ArrayType(self.item, newlength) + + def build_backend_type(self, ffi, finishlist): + if self.length_is_unknown(): + raise CDefError("cannot render the type %r: unknown length" % + (self,)) + self.item.get_cached_btype(ffi, finishlist) # force the item BType + BPtrItem = PointerType(self.item).get_cached_btype(ffi, finishlist) + return global_cache(self, ffi, 'new_array_type', BPtrItem, self.length) + +char_array_type = ArrayType(PrimitiveType('char'), None) + + +class StructOrUnionOrEnum(BaseTypeByIdentity): + _attrs_ = ('name',) + forcename = None + + def build_c_name_with_marker(self): + name = self.forcename or '%s %s' % (self.kind, self.name) + self.c_name_with_marker = name + '&' + + def force_the_name(self, forcename): + self.forcename = forcename + self.build_c_name_with_marker() + + def get_official_name(self): + assert self.c_name_with_marker.endswith('&') + return self.c_name_with_marker[:-1] + + +class StructOrUnion(StructOrUnionOrEnum): + fixedlayout = None + completed = 0 + partial = False + packed = 0 + + def __init__(self, name, fldnames, fldtypes, fldbitsize, fldquals=None): + self.name = name + self.fldnames = fldnames + self.fldtypes = fldtypes + self.fldbitsize = fldbitsize + self.fldquals = fldquals + self.build_c_name_with_marker() + + def anonymous_struct_fields(self): + if self.fldtypes is not None: + for name, type in zip(self.fldnames, self.fldtypes): + if name == '' and isinstance(type, StructOrUnion): + yield type + + def enumfields(self, expand_anonymous_struct_union=True): + fldquals = self.fldquals + if fldquals is None: + fldquals = (0,) * len(self.fldnames) + for name, type, bitsize, quals in zip(self.fldnames, self.fldtypes, + self.fldbitsize, fldquals): + if (name == '' and isinstance(type, StructOrUnion) + and expand_anonymous_struct_union): + # nested anonymous struct/union + for result in type.enumfields(): + yield result + else: + yield (name, type, bitsize, quals) + + def force_flatten(self): + # force the struct or union to have a declaration that lists + # directly all fields returned by enumfields(), flattening + # nested anonymous structs/unions. + names = [] + types = [] + bitsizes = [] + fldquals = [] + for name, type, bitsize, quals in self.enumfields(): + names.append(name) + types.append(type) + bitsizes.append(bitsize) + fldquals.append(quals) + self.fldnames = tuple(names) + self.fldtypes = tuple(types) + self.fldbitsize = tuple(bitsizes) + self.fldquals = tuple(fldquals) + + def get_cached_btype(self, ffi, finishlist, can_delay=False): + BType = StructOrUnionOrEnum.get_cached_btype(self, ffi, finishlist, + can_delay) + if not can_delay: + self.finish_backend_type(ffi, finishlist) + return BType + + def finish_backend_type(self, ffi, finishlist): + if self.completed: + if self.completed != 2: + raise NotImplementedError("recursive structure declaration " + "for '%s'" % (self.name,)) + return + BType = ffi._cached_btypes[self] + # + self.completed = 1 + # + if self.fldtypes is None: + pass # not completing it: it's an opaque struct + # + elif self.fixedlayout is None: + fldtypes = [tp.get_cached_btype(ffi, finishlist) + for tp in self.fldtypes] + lst = list(zip(self.fldnames, fldtypes, self.fldbitsize)) + extra_flags = () + if self.packed: + if self.packed == 1: + extra_flags = (8,) # SF_PACKED + else: + extra_flags = (0, self.packed) + ffi._backend.complete_struct_or_union(BType, lst, self, + -1, -1, *extra_flags) + # + else: + fldtypes = [] + fieldofs, fieldsize, totalsize, totalalignment = self.fixedlayout + for i in range(len(self.fldnames)): + fsize = fieldsize[i] + ftype = self.fldtypes[i] + # + if isinstance(ftype, ArrayType) and ftype.length_is_unknown(): + # fix the length to match the total size + BItemType = ftype.item.get_cached_btype(ffi, finishlist) + nlen, nrest = divmod(fsize, ffi.sizeof(BItemType)) + if nrest != 0: + self._verification_error( + "field '%s.%s' has a bogus size?" % ( + self.name, self.fldnames[i] or '{}')) + ftype = ftype.resolve_length(nlen) + self.fldtypes = (self.fldtypes[:i] + (ftype,) + + self.fldtypes[i+1:]) + # + BFieldType = ftype.get_cached_btype(ffi, finishlist) + if isinstance(ftype, ArrayType) and ftype.length is None: + assert fsize == 0 + else: + bitemsize = ffi.sizeof(BFieldType) + if bitemsize != fsize: + self._verification_error( + "field '%s.%s' is declared as %d bytes, but is " + "really %d bytes" % (self.name, + self.fldnames[i] or '{}', + bitemsize, fsize)) + fldtypes.append(BFieldType) + # + lst = list(zip(self.fldnames, fldtypes, self.fldbitsize, fieldofs)) + ffi._backend.complete_struct_or_union(BType, lst, self, + totalsize, totalalignment) + self.completed = 2 + + def _verification_error(self, msg): + raise VerificationError(msg) + + def check_not_partial(self): + if self.partial and self.fixedlayout is None: + raise VerificationMissing(self._get_c_name()) + + def build_backend_type(self, ffi, finishlist): + self.check_not_partial() + finishlist.append(self) + # + return global_cache(self, ffi, 'new_%s_type' % self.kind, + self.get_official_name(), key=self) + + +class StructType(StructOrUnion): + kind = 'struct' + + +class UnionType(StructOrUnion): + kind = 'union' + + +class EnumType(StructOrUnionOrEnum): + kind = 'enum' + partial = False + partial_resolved = False + + def __init__(self, name, enumerators, enumvalues, baseinttype=None): + self.name = name + self.enumerators = enumerators + self.enumvalues = enumvalues + self.baseinttype = baseinttype + self.build_c_name_with_marker() + + def force_the_name(self, forcename): + StructOrUnionOrEnum.force_the_name(self, forcename) + if self.forcename is None: + name = self.get_official_name() + self.forcename = '$' + name.replace(' ', '_') + + def check_not_partial(self): + if self.partial and not self.partial_resolved: + raise VerificationMissing(self._get_c_name()) + + def build_backend_type(self, ffi, finishlist): + self.check_not_partial() + base_btype = self.build_baseinttype(ffi, finishlist) + return global_cache(self, ffi, 'new_enum_type', + self.get_official_name(), + self.enumerators, self.enumvalues, + base_btype, key=self) + + def build_baseinttype(self, ffi, finishlist): + if self.baseinttype is not None: + return self.baseinttype.get_cached_btype(ffi, finishlist) + # + if self.enumvalues: + smallest_value = min(self.enumvalues) + largest_value = max(self.enumvalues) + else: + import warnings + try: + # XXX! The goal is to ensure that the warnings.warn() + # will not suppress the warning. We want to get it + # several times if we reach this point several times. + __warningregistry__.clear() + except NameError: + pass + warnings.warn("%r has no values explicitly defined; " + "guessing that it is equivalent to 'unsigned int'" + % self._get_c_name()) + smallest_value = largest_value = 0 + if smallest_value < 0: # needs a signed type + sign = 1 + candidate1 = PrimitiveType("int") + candidate2 = PrimitiveType("long") + else: + sign = 0 + candidate1 = PrimitiveType("unsigned int") + candidate2 = PrimitiveType("unsigned long") + btype1 = candidate1.get_cached_btype(ffi, finishlist) + btype2 = candidate2.get_cached_btype(ffi, finishlist) + size1 = ffi.sizeof(btype1) + size2 = ffi.sizeof(btype2) + if (smallest_value >= ((-1) << (8*size1-1)) and + largest_value < (1 << (8*size1-sign))): + return btype1 + if (smallest_value >= ((-1) << (8*size2-1)) and + largest_value < (1 << (8*size2-sign))): + return btype2 + raise CDefError("%s values don't all fit into either 'long' " + "or 'unsigned long'" % self._get_c_name()) + +def unknown_type(name, structname=None): + if structname is None: + structname = '$%s' % name + tp = StructType(structname, None, None, None) + tp.force_the_name(name) + tp.origin = "unknown_type" + return tp + +def unknown_ptr_type(name, structname=None): + if structname is None: + structname = '$$%s' % name + tp = StructType(structname, None, None, None) + return NamedPointerType(tp, name) + + +global_lock = allocate_lock() +_typecache_cffi_backend = weakref.WeakValueDictionary() + +def get_typecache(backend): + # returns _typecache_cffi_backend if backend is the _cffi_backend + # module, or type(backend).__typecache if backend is an instance of + # CTypesBackend (or some FakeBackend class during tests) + if isinstance(backend, types.ModuleType): + return _typecache_cffi_backend + with global_lock: + if not hasattr(type(backend), '__typecache'): + type(backend).__typecache = weakref.WeakValueDictionary() + return type(backend).__typecache + +def global_cache(srctype, ffi, funcname, *args, **kwds): + key = kwds.pop('key', (funcname, args)) + assert not kwds + try: + return ffi._typecache[key] + except KeyError: + pass + try: + res = getattr(ffi._backend, funcname)(*args) + except NotImplementedError as e: + raise NotImplementedError("%s: %r: %s" % (funcname, srctype, e)) + # note that setdefault() on WeakValueDictionary is not atomic + # and contains a rare bug (http://bugs.python.org/issue19542); + # we have to use a lock and do it ourselves + cache = ffi._typecache + with global_lock: + res1 = cache.get(key) + if res1 is None: + cache[key] = res + return res + else: + return res1 + +def pointer_cache(ffi, BType): + return global_cache('?', ffi, 'new_pointer_type', BType) + +def attach_exception_info(e, name): + if e.args and type(e.args[0]) is str: + e.args = ('%s: %s' % (name, e.args[0]),) + e.args[1:] diff --git a/code/.venv/lib/python3.12/site-packages/cffi/parse_c_type.h b/code/.venv/lib/python3.12/site-packages/cffi/parse_c_type.h new file mode 100644 index 0000000..84e4ef8 --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/cffi/parse_c_type.h @@ -0,0 +1,181 @@ + +/* This part is from file 'cffi/parse_c_type.h'. It is copied at the + beginning of C sources generated by CFFI's ffi.set_source(). */ + +typedef void *_cffi_opcode_t; + +#define _CFFI_OP(opcode, arg) (_cffi_opcode_t)(opcode | (((uintptr_t)(arg)) << 8)) +#define _CFFI_GETOP(cffi_opcode) ((unsigned char)(uintptr_t)cffi_opcode) +#define _CFFI_GETARG(cffi_opcode) (((intptr_t)cffi_opcode) >> 8) + +#define _CFFI_OP_PRIMITIVE 1 +#define _CFFI_OP_POINTER 3 +#define _CFFI_OP_ARRAY 5 +#define _CFFI_OP_OPEN_ARRAY 7 +#define _CFFI_OP_STRUCT_UNION 9 +#define _CFFI_OP_ENUM 11 +#define _CFFI_OP_FUNCTION 13 +#define _CFFI_OP_FUNCTION_END 15 +#define _CFFI_OP_NOOP 17 +#define _CFFI_OP_BITFIELD 19 +#define _CFFI_OP_TYPENAME 21 +#define _CFFI_OP_CPYTHON_BLTN_V 23 // varargs +#define _CFFI_OP_CPYTHON_BLTN_N 25 // noargs +#define _CFFI_OP_CPYTHON_BLTN_O 27 // O (i.e. a single arg) +#define _CFFI_OP_CONSTANT 29 +#define _CFFI_OP_CONSTANT_INT 31 +#define _CFFI_OP_GLOBAL_VAR 33 +#define _CFFI_OP_DLOPEN_FUNC 35 +#define _CFFI_OP_DLOPEN_CONST 37 +#define _CFFI_OP_GLOBAL_VAR_F 39 +#define _CFFI_OP_EXTERN_PYTHON 41 + +#define _CFFI_PRIM_VOID 0 +#define _CFFI_PRIM_BOOL 1 +#define _CFFI_PRIM_CHAR 2 +#define _CFFI_PRIM_SCHAR 3 +#define _CFFI_PRIM_UCHAR 4 +#define _CFFI_PRIM_SHORT 5 +#define _CFFI_PRIM_USHORT 6 +#define _CFFI_PRIM_INT 7 +#define _CFFI_PRIM_UINT 8 +#define _CFFI_PRIM_LONG 9 +#define _CFFI_PRIM_ULONG 10 +#define _CFFI_PRIM_LONGLONG 11 +#define _CFFI_PRIM_ULONGLONG 12 +#define _CFFI_PRIM_FLOAT 13 +#define _CFFI_PRIM_DOUBLE 14 +#define _CFFI_PRIM_LONGDOUBLE 15 + +#define _CFFI_PRIM_WCHAR 16 +#define _CFFI_PRIM_INT8 17 +#define _CFFI_PRIM_UINT8 18 +#define _CFFI_PRIM_INT16 19 +#define _CFFI_PRIM_UINT16 20 +#define _CFFI_PRIM_INT32 21 +#define _CFFI_PRIM_UINT32 22 +#define _CFFI_PRIM_INT64 23 +#define _CFFI_PRIM_UINT64 24 +#define _CFFI_PRIM_INTPTR 25 +#define _CFFI_PRIM_UINTPTR 26 +#define _CFFI_PRIM_PTRDIFF 27 +#define _CFFI_PRIM_SIZE 28 +#define _CFFI_PRIM_SSIZE 29 +#define _CFFI_PRIM_INT_LEAST8 30 +#define _CFFI_PRIM_UINT_LEAST8 31 +#define _CFFI_PRIM_INT_LEAST16 32 +#define _CFFI_PRIM_UINT_LEAST16 33 +#define _CFFI_PRIM_INT_LEAST32 34 +#define _CFFI_PRIM_UINT_LEAST32 35 +#define _CFFI_PRIM_INT_LEAST64 36 +#define _CFFI_PRIM_UINT_LEAST64 37 +#define _CFFI_PRIM_INT_FAST8 38 +#define _CFFI_PRIM_UINT_FAST8 39 +#define _CFFI_PRIM_INT_FAST16 40 +#define _CFFI_PRIM_UINT_FAST16 41 +#define _CFFI_PRIM_INT_FAST32 42 +#define _CFFI_PRIM_UINT_FAST32 43 +#define _CFFI_PRIM_INT_FAST64 44 +#define _CFFI_PRIM_UINT_FAST64 45 +#define _CFFI_PRIM_INTMAX 46 +#define _CFFI_PRIM_UINTMAX 47 +#define _CFFI_PRIM_FLOATCOMPLEX 48 +#define _CFFI_PRIM_DOUBLECOMPLEX 49 +#define _CFFI_PRIM_CHAR16 50 +#define _CFFI_PRIM_CHAR32 51 + +#define _CFFI__NUM_PRIM 52 +#define _CFFI__UNKNOWN_PRIM (-1) +#define _CFFI__UNKNOWN_FLOAT_PRIM (-2) +#define _CFFI__UNKNOWN_LONG_DOUBLE (-3) + +#define _CFFI__IO_FILE_STRUCT (-1) + + +struct _cffi_global_s { + const char *name; + void *address; + _cffi_opcode_t type_op; + void *size_or_direct_fn; // OP_GLOBAL_VAR: size, or 0 if unknown + // OP_CPYTHON_BLTN_*: addr of direct function +}; + +struct _cffi_getconst_s { + unsigned long long value; + const struct _cffi_type_context_s *ctx; + int gindex; +}; + +struct _cffi_struct_union_s { + const char *name; + int type_index; // -> _cffi_types, on a OP_STRUCT_UNION + int flags; // _CFFI_F_* flags below + size_t size; + int alignment; + int first_field_index; // -> _cffi_fields array + int num_fields; +}; +#define _CFFI_F_UNION 0x01 // is a union, not a struct +#define _CFFI_F_CHECK_FIELDS 0x02 // complain if fields are not in the + // "standard layout" or if some are missing +#define _CFFI_F_PACKED 0x04 // for CHECK_FIELDS, assume a packed struct +#define _CFFI_F_EXTERNAL 0x08 // in some other ffi.include() +#define _CFFI_F_OPAQUE 0x10 // opaque + +struct _cffi_field_s { + const char *name; + size_t field_offset; + size_t field_size; + _cffi_opcode_t field_type_op; +}; + +struct _cffi_enum_s { + const char *name; + int type_index; // -> _cffi_types, on a OP_ENUM + int type_prim; // _CFFI_PRIM_xxx + const char *enumerators; // comma-delimited string +}; + +struct _cffi_typename_s { + const char *name; + int type_index; /* if opaque, points to a possibly artificial + OP_STRUCT which is itself opaque */ +}; + +struct _cffi_type_context_s { + _cffi_opcode_t *types; + const struct _cffi_global_s *globals; + const struct _cffi_field_s *fields; + const struct _cffi_struct_union_s *struct_unions; + const struct _cffi_enum_s *enums; + const struct _cffi_typename_s *typenames; + int num_globals; + int num_struct_unions; + int num_enums; + int num_typenames; + const char *const *includes; + int num_types; + int flags; /* future extension */ +}; + +struct _cffi_parse_info_s { + const struct _cffi_type_context_s *ctx; + _cffi_opcode_t *output; + unsigned int output_size; + size_t error_location; + const char *error_message; +}; + +struct _cffi_externpy_s { + const char *name; + size_t size_of_result; + void *reserved1, *reserved2; +}; + +#ifdef _CFFI_INTERNAL +static int parse_c_type(struct _cffi_parse_info_s *info, const char *input); +static int search_in_globals(const struct _cffi_type_context_s *ctx, + const char *search, size_t search_len); +static int search_in_struct_unions(const struct _cffi_type_context_s *ctx, + const char *search, size_t search_len); +#endif diff --git a/code/.venv/lib/python3.12/site-packages/cffi/pkgconfig.py b/code/.venv/lib/python3.12/site-packages/cffi/pkgconfig.py new file mode 100644 index 0000000..5c93f15 --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/cffi/pkgconfig.py @@ -0,0 +1,121 @@ +# pkg-config, https://www.freedesktop.org/wiki/Software/pkg-config/ integration for cffi +import sys, os, subprocess + +from .error import PkgConfigError + + +def merge_flags(cfg1, cfg2): + """Merge values from cffi config flags cfg2 to cf1 + + Example: + merge_flags({"libraries": ["one"]}, {"libraries": ["two"]}) + {"libraries": ["one", "two"]} + """ + for key, value in cfg2.items(): + if key not in cfg1: + cfg1[key] = value + else: + if not isinstance(cfg1[key], list): + raise TypeError("cfg1[%r] should be a list of strings" % (key,)) + if not isinstance(value, list): + raise TypeError("cfg2[%r] should be a list of strings" % (key,)) + cfg1[key].extend(value) + return cfg1 + + +def call(libname, flag, encoding=sys.getfilesystemencoding()): + """Calls pkg-config and returns the output if found + """ + a = ["pkg-config", "--print-errors"] + a.append(flag) + a.append(libname) + try: + pc = subprocess.Popen(a, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + except EnvironmentError as e: + raise PkgConfigError("cannot run pkg-config: %s" % (str(e).strip(),)) + + bout, berr = pc.communicate() + if pc.returncode != 0: + try: + berr = berr.decode(encoding) + except Exception: + pass + raise PkgConfigError(berr.strip()) + + if sys.version_info >= (3,) and not isinstance(bout, str): # Python 3.x + try: + bout = bout.decode(encoding) + except UnicodeDecodeError: + raise PkgConfigError("pkg-config %s %s returned bytes that cannot " + "be decoded with encoding %r:\n%r" % + (flag, libname, encoding, bout)) + + if os.altsep != '\\' and '\\' in bout: + raise PkgConfigError("pkg-config %s %s returned an unsupported " + "backslash-escaped output:\n%r" % + (flag, libname, bout)) + return bout + + +def flags_from_pkgconfig(libs): + r"""Return compiler line flags for FFI.set_source based on pkg-config output + + Usage + ... + ffibuilder.set_source("_foo", pkgconfig = ["libfoo", "libbar >= 1.8.3"]) + + If pkg-config is installed on build machine, then arguments include_dirs, + library_dirs, libraries, define_macros, extra_compile_args and + extra_link_args are extended with an output of pkg-config for libfoo and + libbar. + + Raises PkgConfigError in case the pkg-config call fails. + """ + + def get_include_dirs(string): + return [x[2:] for x in string.split() if x.startswith("-I")] + + def get_library_dirs(string): + return [x[2:] for x in string.split() if x.startswith("-L")] + + def get_libraries(string): + return [x[2:] for x in string.split() if x.startswith("-l")] + + # convert -Dfoo=bar to list of tuples [("foo", "bar")] expected by distutils + def get_macros(string): + def _macro(x): + x = x[2:] # drop "-D" + if '=' in x: + return tuple(x.split("=", 1)) # "-Dfoo=bar" => ("foo", "bar") + else: + return (x, None) # "-Dfoo" => ("foo", None) + return [_macro(x) for x in string.split() if x.startswith("-D")] + + def get_other_cflags(string): + return [x for x in string.split() if not x.startswith("-I") and + not x.startswith("-D")] + + def get_other_libs(string): + return [x for x in string.split() if not x.startswith("-L") and + not x.startswith("-l")] + + # return kwargs for given libname + def kwargs(libname): + fse = sys.getfilesystemencoding() + all_cflags = call(libname, "--cflags") + all_libs = call(libname, "--libs") + return { + "include_dirs": get_include_dirs(all_cflags), + "library_dirs": get_library_dirs(all_libs), + "libraries": get_libraries(all_libs), + "define_macros": get_macros(all_cflags), + "extra_compile_args": get_other_cflags(all_cflags), + "extra_link_args": get_other_libs(all_libs), + } + + # merge all arguments together + ret = {} + for libname in libs: + lib_flags = kwargs(libname) + merge_flags(ret, lib_flags) + return ret diff --git a/code/.venv/lib/python3.12/site-packages/cffi/recompiler.py b/code/.venv/lib/python3.12/site-packages/cffi/recompiler.py new file mode 100644 index 0000000..ac6c163 --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/cffi/recompiler.py @@ -0,0 +1,1581 @@ +import os, sys, io +from . import ffiplatform, model +from .error import VerificationError +from .cffi_opcode import * + +VERSION_BASE = 0x2601 +VERSION_EMBEDDED = 0x2701 +VERSION_CHAR16CHAR32 = 0x2801 + +USE_LIMITED_API = (sys.platform != 'win32' or sys.version_info < (3, 0) or + sys.version_info >= (3, 5)) + + +class GlobalExpr: + def __init__(self, name, address, type_op, size=0, check_value=0): + self.name = name + self.address = address + self.type_op = type_op + self.size = size + self.check_value = check_value + + def as_c_expr(self): + return ' { "%s", (void *)%s, %s, (void *)%s },' % ( + self.name, self.address, self.type_op.as_c_expr(), self.size) + + def as_python_expr(self): + return "b'%s%s',%d" % (self.type_op.as_python_bytes(), self.name, + self.check_value) + +class FieldExpr: + def __init__(self, name, field_offset, field_size, fbitsize, field_type_op): + self.name = name + self.field_offset = field_offset + self.field_size = field_size + self.fbitsize = fbitsize + self.field_type_op = field_type_op + + def as_c_expr(self): + spaces = " " * len(self.name) + return (' { "%s", %s,\n' % (self.name, self.field_offset) + + ' %s %s,\n' % (spaces, self.field_size) + + ' %s %s },' % (spaces, self.field_type_op.as_c_expr())) + + def as_python_expr(self): + raise NotImplementedError + + def as_field_python_expr(self): + if self.field_type_op.op == OP_NOOP: + size_expr = '' + elif self.field_type_op.op == OP_BITFIELD: + size_expr = format_four_bytes(self.fbitsize) + else: + raise NotImplementedError + return "b'%s%s%s'" % (self.field_type_op.as_python_bytes(), + size_expr, + self.name) + +class StructUnionExpr: + def __init__(self, name, type_index, flags, size, alignment, comment, + first_field_index, c_fields): + self.name = name + self.type_index = type_index + self.flags = flags + self.size = size + self.alignment = alignment + self.comment = comment + self.first_field_index = first_field_index + self.c_fields = c_fields + + def as_c_expr(self): + return (' { "%s", %d, %s,' % (self.name, self.type_index, self.flags) + + '\n %s, %s, ' % (self.size, self.alignment) + + '%d, %d ' % (self.first_field_index, len(self.c_fields)) + + ('/* %s */ ' % self.comment if self.comment else '') + + '},') + + def as_python_expr(self): + flags = eval(self.flags, G_FLAGS) + fields_expr = [c_field.as_field_python_expr() + for c_field in self.c_fields] + return "(b'%s%s%s',%s)" % ( + format_four_bytes(self.type_index), + format_four_bytes(flags), + self.name, + ','.join(fields_expr)) + +class EnumExpr: + def __init__(self, name, type_index, size, signed, allenums): + self.name = name + self.type_index = type_index + self.size = size + self.signed = signed + self.allenums = allenums + + def as_c_expr(self): + return (' { "%s", %d, _cffi_prim_int(%s, %s),\n' + ' "%s" },' % (self.name, self.type_index, + self.size, self.signed, self.allenums)) + + def as_python_expr(self): + prim_index = { + (1, 0): PRIM_UINT8, (1, 1): PRIM_INT8, + (2, 0): PRIM_UINT16, (2, 1): PRIM_INT16, + (4, 0): PRIM_UINT32, (4, 1): PRIM_INT32, + (8, 0): PRIM_UINT64, (8, 1): PRIM_INT64, + }[self.size, self.signed] + return "b'%s%s%s\\x00%s'" % (format_four_bytes(self.type_index), + format_four_bytes(prim_index), + self.name, self.allenums) + +class TypenameExpr: + def __init__(self, name, type_index): + self.name = name + self.type_index = type_index + + def as_c_expr(self): + return ' { "%s", %d },' % (self.name, self.type_index) + + def as_python_expr(self): + return "b'%s%s'" % (format_four_bytes(self.type_index), self.name) + + +# ____________________________________________________________ + + +class Recompiler: + _num_externpy = 0 + + def __init__(self, ffi, module_name, target_is_python=False): + self.ffi = ffi + self.module_name = module_name + self.target_is_python = target_is_python + self._version = VERSION_BASE + + def needs_version(self, ver): + self._version = max(self._version, ver) + + def collect_type_table(self): + self._typesdict = {} + self._generate("collecttype") + # + all_decls = sorted(self._typesdict, key=str) + # + # prepare all FUNCTION bytecode sequences first + self.cffi_types = [] + for tp in all_decls: + if tp.is_raw_function: + assert self._typesdict[tp] is None + self._typesdict[tp] = len(self.cffi_types) + self.cffi_types.append(tp) # placeholder + for tp1 in tp.args: + assert isinstance(tp1, (model.VoidType, + model.BasePrimitiveType, + model.PointerType, + model.StructOrUnionOrEnum, + model.FunctionPtrType)) + if self._typesdict[tp1] is None: + self._typesdict[tp1] = len(self.cffi_types) + self.cffi_types.append(tp1) # placeholder + self.cffi_types.append('END') # placeholder + # + # prepare all OTHER bytecode sequences + for tp in all_decls: + if not tp.is_raw_function and self._typesdict[tp] is None: + self._typesdict[tp] = len(self.cffi_types) + self.cffi_types.append(tp) # placeholder + if tp.is_array_type and tp.length is not None: + self.cffi_types.append('LEN') # placeholder + assert None not in self._typesdict.values() + # + # collect all structs and unions and enums + self._struct_unions = {} + self._enums = {} + for tp in all_decls: + if isinstance(tp, model.StructOrUnion): + self._struct_unions[tp] = None + elif isinstance(tp, model.EnumType): + self._enums[tp] = None + for i, tp in enumerate(sorted(self._struct_unions, + key=lambda tp: tp.name)): + self._struct_unions[tp] = i + for i, tp in enumerate(sorted(self._enums, + key=lambda tp: tp.name)): + self._enums[tp] = i + # + # emit all bytecode sequences now + for tp in all_decls: + method = getattr(self, '_emit_bytecode_' + tp.__class__.__name__) + method(tp, self._typesdict[tp]) + # + # consistency check + for op in self.cffi_types: + assert isinstance(op, CffiOp) + self.cffi_types = tuple(self.cffi_types) # don't change any more + + def _enum_fields(self, tp): + # When producing C, expand all anonymous struct/union fields. + # That's necessary to have C code checking the offsets of the + # individual fields contained in them. When producing Python, + # don't do it and instead write it like it is, with the + # corresponding fields having an empty name. Empty names are + # recognized at runtime when we import the generated Python + # file. + expand_anonymous_struct_union = not self.target_is_python + return tp.enumfields(expand_anonymous_struct_union) + + def _do_collect_type(self, tp): + if not isinstance(tp, model.BaseTypeByIdentity): + if isinstance(tp, tuple): + for x in tp: + self._do_collect_type(x) + return + if tp not in self._typesdict: + self._typesdict[tp] = None + if isinstance(tp, model.FunctionPtrType): + self._do_collect_type(tp.as_raw_function()) + elif isinstance(tp, model.StructOrUnion): + if tp.fldtypes is not None and ( + tp not in self.ffi._parser._included_declarations): + for name1, tp1, _, _ in self._enum_fields(tp): + self._do_collect_type(self._field_type(tp, name1, tp1)) + else: + for _, x in tp._get_items(): + self._do_collect_type(x) + + def _generate(self, step_name): + lst = self.ffi._parser._declarations.items() + for name, (tp, quals) in sorted(lst): + kind, realname = name.split(' ', 1) + try: + method = getattr(self, '_generate_cpy_%s_%s' % (kind, + step_name)) + except AttributeError: + raise VerificationError( + "not implemented in recompile(): %r" % name) + try: + self._current_quals = quals + method(tp, realname) + except Exception as e: + model.attach_exception_info(e, name) + raise + + # ---------- + + ALL_STEPS = ["global", "field", "struct_union", "enum", "typename"] + + def collect_step_tables(self): + # collect the declarations for '_cffi_globals', '_cffi_typenames', etc. + self._lsts = {} + for step_name in self.ALL_STEPS: + self._lsts[step_name] = [] + self._seen_struct_unions = set() + self._generate("ctx") + self._add_missing_struct_unions() + # + for step_name in self.ALL_STEPS: + lst = self._lsts[step_name] + if step_name != "field": + lst.sort(key=lambda entry: entry.name) + self._lsts[step_name] = tuple(lst) # don't change any more + # + # check for a possible internal inconsistency: _cffi_struct_unions + # should have been generated with exactly self._struct_unions + lst = self._lsts["struct_union"] + for tp, i in self._struct_unions.items(): + assert i < len(lst) + assert lst[i].name == tp.name + assert len(lst) == len(self._struct_unions) + # same with enums + lst = self._lsts["enum"] + for tp, i in self._enums.items(): + assert i < len(lst) + assert lst[i].name == tp.name + assert len(lst) == len(self._enums) + + # ---------- + + def _prnt(self, what=''): + self._f.write(what + '\n') + + def write_source_to_f(self, f, preamble): + if self.target_is_python: + assert preamble is None + self.write_py_source_to_f(f) + else: + assert preamble is not None + self.write_c_source_to_f(f, preamble) + + def _rel_readlines(self, filename): + g = open(os.path.join(os.path.dirname(__file__), filename), 'r') + lines = g.readlines() + g.close() + return lines + + def write_c_source_to_f(self, f, preamble): + self._f = f + prnt = self._prnt + if self.ffi._embedding is not None: + prnt('#define _CFFI_USE_EMBEDDING') + if not USE_LIMITED_API: + prnt('#define _CFFI_NO_LIMITED_API') + # + # first the '#include' (actually done by inlining the file's content) + lines = self._rel_readlines('_cffi_include.h') + i = lines.index('#include "parse_c_type.h"\n') + lines[i:i+1] = self._rel_readlines('parse_c_type.h') + prnt(''.join(lines)) + # + # if we have ffi._embedding != None, we give it here as a macro + # and include an extra file + base_module_name = self.module_name.split('.')[-1] + if self.ffi._embedding is not None: + prnt('#define _CFFI_MODULE_NAME "%s"' % (self.module_name,)) + prnt('static const char _CFFI_PYTHON_STARTUP_CODE[] = {') + self._print_string_literal_in_array(self.ffi._embedding) + prnt('0 };') + prnt('#ifdef PYPY_VERSION') + prnt('# define _CFFI_PYTHON_STARTUP_FUNC _cffi_pypyinit_%s' % ( + base_module_name,)) + prnt('#elif PY_MAJOR_VERSION >= 3') + prnt('# define _CFFI_PYTHON_STARTUP_FUNC PyInit_%s' % ( + base_module_name,)) + prnt('#else') + prnt('# define _CFFI_PYTHON_STARTUP_FUNC init%s' % ( + base_module_name,)) + prnt('#endif') + lines = self._rel_readlines('_embedding.h') + i = lines.index('#include "_cffi_errors.h"\n') + lines[i:i+1] = self._rel_readlines('_cffi_errors.h') + prnt(''.join(lines)) + self.needs_version(VERSION_EMBEDDED) + # + # then paste the C source given by the user, verbatim. + prnt('/************************************************************/') + prnt() + prnt(preamble) + prnt() + prnt('/************************************************************/') + prnt() + # + # the declaration of '_cffi_types' + prnt('static void *_cffi_types[] = {') + typeindex2type = dict([(i, tp) for (tp, i) in self._typesdict.items()]) + for i, op in enumerate(self.cffi_types): + comment = '' + if i in typeindex2type: + comment = ' // ' + typeindex2type[i]._get_c_name() + prnt('/* %2d */ %s,%s' % (i, op.as_c_expr(), comment)) + if not self.cffi_types: + prnt(' 0') + prnt('};') + prnt() + # + # call generate_cpy_xxx_decl(), for every xxx found from + # ffi._parser._declarations. This generates all the functions. + self._seen_constants = set() + self._generate("decl") + # + # the declaration of '_cffi_globals' and '_cffi_typenames' + nums = {} + for step_name in self.ALL_STEPS: + lst = self._lsts[step_name] + nums[step_name] = len(lst) + if nums[step_name] > 0: + prnt('static const struct _cffi_%s_s _cffi_%ss[] = {' % ( + step_name, step_name)) + for entry in lst: + prnt(entry.as_c_expr()) + prnt('};') + prnt() + # + # the declaration of '_cffi_includes' + if self.ffi._included_ffis: + prnt('static const char * const _cffi_includes[] = {') + for ffi_to_include in self.ffi._included_ffis: + try: + included_module_name, included_source = ( + ffi_to_include._assigned_source[:2]) + except AttributeError: + raise VerificationError( + "ffi object %r includes %r, but the latter has not " + "been prepared with set_source()" % ( + self.ffi, ffi_to_include,)) + if included_source is None: + raise VerificationError( + "not implemented yet: ffi.include() of a Python-based " + "ffi inside a C-based ffi") + prnt(' "%s",' % (included_module_name,)) + prnt(' NULL') + prnt('};') + prnt() + # + # the declaration of '_cffi_type_context' + prnt('static const struct _cffi_type_context_s _cffi_type_context = {') + prnt(' _cffi_types,') + for step_name in self.ALL_STEPS: + if nums[step_name] > 0: + prnt(' _cffi_%ss,' % step_name) + else: + prnt(' NULL, /* no %ss */' % step_name) + for step_name in self.ALL_STEPS: + if step_name != "field": + prnt(' %d, /* num_%ss */' % (nums[step_name], step_name)) + if self.ffi._included_ffis: + prnt(' _cffi_includes,') + else: + prnt(' NULL, /* no includes */') + prnt(' %d, /* num_types */' % (len(self.cffi_types),)) + flags = 0 + if self._num_externpy > 0 or self.ffi._embedding is not None: + flags |= 1 # set to mean that we use extern "Python" + prnt(' %d, /* flags */' % flags) + prnt('};') + prnt() + # + # the init function + prnt('#ifdef __GNUC__') + prnt('# pragma GCC visibility push(default) /* for -fvisibility= */') + prnt('#endif') + prnt() + prnt('#ifdef PYPY_VERSION') + prnt('PyMODINIT_FUNC') + prnt('_cffi_pypyinit_%s(const void *p[])' % (base_module_name,)) + prnt('{') + if flags & 1: + prnt(' if (((intptr_t)p[0]) >= 0x0A03) {') + prnt(' _cffi_call_python_org = ' + '(void(*)(struct _cffi_externpy_s *, char *))p[1];') + prnt(' }') + prnt(' p[0] = (const void *)0x%x;' % self._version) + prnt(' p[1] = &_cffi_type_context;') + prnt('#if PY_MAJOR_VERSION >= 3') + prnt(' return NULL;') + prnt('#endif') + prnt('}') + # on Windows, distutils insists on putting init_cffi_xyz in + # 'export_symbols', so instead of fighting it, just give up and + # give it one + prnt('# ifdef _MSC_VER') + prnt(' PyMODINIT_FUNC') + prnt('# if PY_MAJOR_VERSION >= 3') + prnt(' PyInit_%s(void) { return NULL; }' % (base_module_name,)) + prnt('# else') + prnt(' init%s(void) { }' % (base_module_name,)) + prnt('# endif') + prnt('# endif') + prnt('#elif PY_MAJOR_VERSION >= 3') + prnt('PyMODINIT_FUNC') + prnt('PyInit_%s(void)' % (base_module_name,)) + prnt('{') + prnt(' return _cffi_init("%s", 0x%x, &_cffi_type_context);' % ( + self.module_name, self._version)) + prnt('}') + prnt('#else') + prnt('PyMODINIT_FUNC') + prnt('init%s(void)' % (base_module_name,)) + prnt('{') + prnt(' _cffi_init("%s", 0x%x, &_cffi_type_context);' % ( + self.module_name, self._version)) + prnt('}') + prnt('#endif') + prnt() + prnt('#ifdef __GNUC__') + prnt('# pragma GCC visibility pop') + prnt('#endif') + self._version = None + + def _to_py(self, x): + if isinstance(x, str): + return "b'%s'" % (x,) + if isinstance(x, (list, tuple)): + rep = [self._to_py(item) for item in x] + if len(rep) == 1: + rep.append('') + return "(%s)" % (','.join(rep),) + return x.as_python_expr() # Py2: unicode unexpected; Py3: bytes unexp. + + def write_py_source_to_f(self, f): + self._f = f + prnt = self._prnt + # + # header + prnt("# auto-generated file") + prnt("import _cffi_backend") + # + # the 'import' of the included ffis + num_includes = len(self.ffi._included_ffis or ()) + for i in range(num_includes): + ffi_to_include = self.ffi._included_ffis[i] + try: + included_module_name, included_source = ( + ffi_to_include._assigned_source[:2]) + except AttributeError: + raise VerificationError( + "ffi object %r includes %r, but the latter has not " + "been prepared with set_source()" % ( + self.ffi, ffi_to_include,)) + if included_source is not None: + raise VerificationError( + "not implemented yet: ffi.include() of a C-based " + "ffi inside a Python-based ffi") + prnt('from %s import ffi as _ffi%d' % (included_module_name, i)) + prnt() + prnt("ffi = _cffi_backend.FFI('%s'," % (self.module_name,)) + prnt(" _version = 0x%x," % (self._version,)) + self._version = None + # + # the '_types' keyword argument + self.cffi_types = tuple(self.cffi_types) # don't change any more + types_lst = [op.as_python_bytes() for op in self.cffi_types] + prnt(' _types = %s,' % (self._to_py(''.join(types_lst)),)) + typeindex2type = dict([(i, tp) for (tp, i) in self._typesdict.items()]) + # + # the keyword arguments from ALL_STEPS + for step_name in self.ALL_STEPS: + lst = self._lsts[step_name] + if len(lst) > 0 and step_name != "field": + prnt(' _%ss = %s,' % (step_name, self._to_py(lst))) + # + # the '_includes' keyword argument + if num_includes > 0: + prnt(' _includes = (%s,),' % ( + ', '.join(['_ffi%d' % i for i in range(num_includes)]),)) + # + # the footer + prnt(')') + + # ---------- + + def _gettypenum(self, type): + # a KeyError here is a bug. please report it! :-) + return self._typesdict[type] + + def _convert_funcarg_to_c(self, tp, fromvar, tovar, errcode): + extraarg = '' + if isinstance(tp, model.BasePrimitiveType) and not tp.is_complex_type(): + if tp.is_integer_type() and tp.name != '_Bool': + converter = '_cffi_to_c_int' + extraarg = ', %s' % tp.name + elif isinstance(tp, model.UnknownFloatType): + # don't check with is_float_type(): it may be a 'long + # double' here, and _cffi_to_c_double would loose precision + converter = '(%s)_cffi_to_c_double' % (tp.get_c_name(''),) + else: + cname = tp.get_c_name('') + converter = '(%s)_cffi_to_c_%s' % (cname, + tp.name.replace(' ', '_')) + if cname in ('char16_t', 'char32_t'): + self.needs_version(VERSION_CHAR16CHAR32) + errvalue = '-1' + # + elif isinstance(tp, model.PointerType): + self._convert_funcarg_to_c_ptr_or_array(tp, fromvar, + tovar, errcode) + return + # + elif (isinstance(tp, model.StructOrUnionOrEnum) or + isinstance(tp, model.BasePrimitiveType)): + # a struct (not a struct pointer) as a function argument; + # or, a complex (the same code works) + self._prnt(' if (_cffi_to_c((char *)&%s, _cffi_type(%d), %s) < 0)' + % (tovar, self._gettypenum(tp), fromvar)) + self._prnt(' %s;' % errcode) + return + # + elif isinstance(tp, model.FunctionPtrType): + converter = '(%s)_cffi_to_c_pointer' % tp.get_c_name('') + extraarg = ', _cffi_type(%d)' % self._gettypenum(tp) + errvalue = 'NULL' + # + else: + raise NotImplementedError(tp) + # + self._prnt(' %s = %s(%s%s);' % (tovar, converter, fromvar, extraarg)) + self._prnt(' if (%s == (%s)%s && PyErr_Occurred())' % ( + tovar, tp.get_c_name(''), errvalue)) + self._prnt(' %s;' % errcode) + + def _extra_local_variables(self, tp, localvars, freelines): + if isinstance(tp, model.PointerType): + localvars.add('Py_ssize_t datasize') + localvars.add('struct _cffi_freeme_s *large_args_free = NULL') + freelines.add('if (large_args_free != NULL)' + ' _cffi_free_array_arguments(large_args_free);') + + def _convert_funcarg_to_c_ptr_or_array(self, tp, fromvar, tovar, errcode): + self._prnt(' datasize = _cffi_prepare_pointer_call_argument(') + self._prnt(' _cffi_type(%d), %s, (char **)&%s);' % ( + self._gettypenum(tp), fromvar, tovar)) + self._prnt(' if (datasize != 0) {') + self._prnt(' %s = ((size_t)datasize) <= 640 ? ' + '(%s)alloca((size_t)datasize) : NULL;' % ( + tovar, tp.get_c_name(''))) + self._prnt(' if (_cffi_convert_array_argument(_cffi_type(%d), %s, ' + '(char **)&%s,' % (self._gettypenum(tp), fromvar, tovar)) + self._prnt(' datasize, &large_args_free) < 0)') + self._prnt(' %s;' % errcode) + self._prnt(' }') + + def _convert_expr_from_c(self, tp, var, context): + if isinstance(tp, model.BasePrimitiveType): + if tp.is_integer_type() and tp.name != '_Bool': + return '_cffi_from_c_int(%s, %s)' % (var, tp.name) + elif isinstance(tp, model.UnknownFloatType): + return '_cffi_from_c_double(%s)' % (var,) + elif tp.name != 'long double' and not tp.is_complex_type(): + cname = tp.name.replace(' ', '_') + if cname in ('char16_t', 'char32_t'): + self.needs_version(VERSION_CHAR16CHAR32) + return '_cffi_from_c_%s(%s)' % (cname, var) + else: + return '_cffi_from_c_deref((char *)&%s, _cffi_type(%d))' % ( + var, self._gettypenum(tp)) + elif isinstance(tp, (model.PointerType, model.FunctionPtrType)): + return '_cffi_from_c_pointer((char *)%s, _cffi_type(%d))' % ( + var, self._gettypenum(tp)) + elif isinstance(tp, model.ArrayType): + return '_cffi_from_c_pointer((char *)%s, _cffi_type(%d))' % ( + var, self._gettypenum(model.PointerType(tp.item))) + elif isinstance(tp, model.StructOrUnion): + if tp.fldnames is None: + raise TypeError("'%s' is used as %s, but is opaque" % ( + tp._get_c_name(), context)) + return '_cffi_from_c_struct((char *)&%s, _cffi_type(%d))' % ( + var, self._gettypenum(tp)) + elif isinstance(tp, model.EnumType): + return '_cffi_from_c_deref((char *)&%s, _cffi_type(%d))' % ( + var, self._gettypenum(tp)) + else: + raise NotImplementedError(tp) + + # ---------- + # typedefs + + def _typedef_type(self, tp, name): + return self._global_type(tp, "(*(%s *)0)" % (name,)) + + def _generate_cpy_typedef_collecttype(self, tp, name): + self._do_collect_type(self._typedef_type(tp, name)) + + def _generate_cpy_typedef_decl(self, tp, name): + pass + + def _typedef_ctx(self, tp, name): + type_index = self._typesdict[tp] + self._lsts["typename"].append(TypenameExpr(name, type_index)) + + def _generate_cpy_typedef_ctx(self, tp, name): + tp = self._typedef_type(tp, name) + self._typedef_ctx(tp, name) + if getattr(tp, "origin", None) == "unknown_type": + self._struct_ctx(tp, tp.name, approxname=None) + elif isinstance(tp, model.NamedPointerType): + self._struct_ctx(tp.totype, tp.totype.name, approxname=tp.name, + named_ptr=tp) + + # ---------- + # function declarations + + def _generate_cpy_function_collecttype(self, tp, name): + self._do_collect_type(tp.as_raw_function()) + if tp.ellipsis and not self.target_is_python: + self._do_collect_type(tp) + + def _generate_cpy_function_decl(self, tp, name): + assert not self.target_is_python + assert isinstance(tp, model.FunctionPtrType) + if tp.ellipsis: + # cannot support vararg functions better than this: check for its + # exact type (including the fixed arguments), and build it as a + # constant function pointer (no CPython wrapper) + self._generate_cpy_constant_decl(tp, name) + return + prnt = self._prnt + numargs = len(tp.args) + if numargs == 0: + argname = 'noarg' + elif numargs == 1: + argname = 'arg0' + else: + argname = 'args' + # + # ------------------------------ + # the 'd' version of the function, only for addressof(lib, 'func') + arguments = [] + call_arguments = [] + context = 'argument of %s' % name + for i, type in enumerate(tp.args): + arguments.append(type.get_c_name(' x%d' % i, context)) + call_arguments.append('x%d' % i) + repr_arguments = ', '.join(arguments) + repr_arguments = repr_arguments or 'void' + if tp.abi: + abi = tp.abi + ' ' + else: + abi = '' + name_and_arguments = '%s_cffi_d_%s(%s)' % (abi, name, repr_arguments) + prnt('static %s' % (tp.result.get_c_name(name_and_arguments),)) + prnt('{') + call_arguments = ', '.join(call_arguments) + result_code = 'return ' + if isinstance(tp.result, model.VoidType): + result_code = '' + prnt(' %s%s(%s);' % (result_code, name, call_arguments)) + prnt('}') + # + prnt('#ifndef PYPY_VERSION') # ------------------------------ + # + prnt('static PyObject *') + prnt('_cffi_f_%s(PyObject *self, PyObject *%s)' % (name, argname)) + prnt('{') + # + context = 'argument of %s' % name + for i, type in enumerate(tp.args): + arg = type.get_c_name(' x%d' % i, context) + prnt(' %s;' % arg) + # + localvars = set() + freelines = set() + for type in tp.args: + self._extra_local_variables(type, localvars, freelines) + for decl in sorted(localvars): + prnt(' %s;' % (decl,)) + # + if not isinstance(tp.result, model.VoidType): + result_code = 'result = ' + context = 'result of %s' % name + result_decl = ' %s;' % tp.result.get_c_name(' result', context) + prnt(result_decl) + prnt(' PyObject *pyresult;') + else: + result_decl = None + result_code = '' + # + if len(tp.args) > 1: + rng = range(len(tp.args)) + for i in rng: + prnt(' PyObject *arg%d;' % i) + prnt() + prnt(' if (!PyArg_UnpackTuple(args, "%s", %d, %d, %s))' % ( + name, len(rng), len(rng), + ', '.join(['&arg%d' % i for i in rng]))) + prnt(' return NULL;') + prnt() + # + for i, type in enumerate(tp.args): + self._convert_funcarg_to_c(type, 'arg%d' % i, 'x%d' % i, + 'return NULL') + prnt() + # + prnt(' Py_BEGIN_ALLOW_THREADS') + prnt(' _cffi_restore_errno();') + call_arguments = ['x%d' % i for i in range(len(tp.args))] + call_arguments = ', '.join(call_arguments) + prnt(' { %s%s(%s); }' % (result_code, name, call_arguments)) + prnt(' _cffi_save_errno();') + prnt(' Py_END_ALLOW_THREADS') + prnt() + # + prnt(' (void)self; /* unused */') + if numargs == 0: + prnt(' (void)noarg; /* unused */') + if result_code: + prnt(' pyresult = %s;' % + self._convert_expr_from_c(tp.result, 'result', 'result type')) + for freeline in freelines: + prnt(' ' + freeline) + prnt(' return pyresult;') + else: + for freeline in freelines: + prnt(' ' + freeline) + prnt(' Py_INCREF(Py_None);') + prnt(' return Py_None;') + prnt('}') + # + prnt('#else') # ------------------------------ + # + # the PyPy version: need to replace struct/union arguments with + # pointers, and if the result is a struct/union, insert a first + # arg that is a pointer to the result. We also do that for + # complex args and return type. + def need_indirection(type): + return (isinstance(type, model.StructOrUnion) or + (isinstance(type, model.PrimitiveType) and + type.is_complex_type())) + difference = False + arguments = [] + call_arguments = [] + context = 'argument of %s' % name + for i, type in enumerate(tp.args): + indirection = '' + if need_indirection(type): + indirection = '*' + difference = True + arg = type.get_c_name(' %sx%d' % (indirection, i), context) + arguments.append(arg) + call_arguments.append('%sx%d' % (indirection, i)) + tp_result = tp.result + if need_indirection(tp_result): + context = 'result of %s' % name + arg = tp_result.get_c_name(' *result', context) + arguments.insert(0, arg) + tp_result = model.void_type + result_decl = None + result_code = '*result = ' + difference = True + if difference: + repr_arguments = ', '.join(arguments) + repr_arguments = repr_arguments or 'void' + name_and_arguments = '%s_cffi_f_%s(%s)' % (abi, name, + repr_arguments) + prnt('static %s' % (tp_result.get_c_name(name_and_arguments),)) + prnt('{') + if result_decl: + prnt(result_decl) + call_arguments = ', '.join(call_arguments) + prnt(' { %s%s(%s); }' % (result_code, name, call_arguments)) + if result_decl: + prnt(' return result;') + prnt('}') + else: + prnt('# define _cffi_f_%s _cffi_d_%s' % (name, name)) + # + prnt('#endif') # ------------------------------ + prnt() + + def _generate_cpy_function_ctx(self, tp, name): + if tp.ellipsis and not self.target_is_python: + self._generate_cpy_constant_ctx(tp, name) + return + type_index = self._typesdict[tp.as_raw_function()] + numargs = len(tp.args) + if self.target_is_python: + meth_kind = OP_DLOPEN_FUNC + elif numargs == 0: + meth_kind = OP_CPYTHON_BLTN_N # 'METH_NOARGS' + elif numargs == 1: + meth_kind = OP_CPYTHON_BLTN_O # 'METH_O' + else: + meth_kind = OP_CPYTHON_BLTN_V # 'METH_VARARGS' + self._lsts["global"].append( + GlobalExpr(name, '_cffi_f_%s' % name, + CffiOp(meth_kind, type_index), + size='_cffi_d_%s' % name)) + + # ---------- + # named structs or unions + + def _field_type(self, tp_struct, field_name, tp_field): + if isinstance(tp_field, model.ArrayType): + actual_length = tp_field.length + if actual_length == '...': + ptr_struct_name = tp_struct.get_c_name('*') + actual_length = '_cffi_array_len(((%s)0)->%s)' % ( + ptr_struct_name, field_name) + tp_item = self._field_type(tp_struct, '%s[0]' % field_name, + tp_field.item) + tp_field = model.ArrayType(tp_item, actual_length) + return tp_field + + def _struct_collecttype(self, tp): + self._do_collect_type(tp) + if self.target_is_python: + # also requires nested anon struct/unions in ABI mode, recursively + for fldtype in tp.anonymous_struct_fields(): + self._struct_collecttype(fldtype) + + def _struct_decl(self, tp, cname, approxname): + if tp.fldtypes is None: + return + prnt = self._prnt + checkfuncname = '_cffi_checkfld_%s' % (approxname,) + prnt('_CFFI_UNUSED_FN') + prnt('static void %s(%s *p)' % (checkfuncname, cname)) + prnt('{') + prnt(' /* only to generate compile-time warnings or errors */') + prnt(' (void)p;') + for fname, ftype, fbitsize, fqual in self._enum_fields(tp): + try: + if ftype.is_integer_type() or fbitsize >= 0: + # accept all integers, but complain on float or double + if fname != '': + prnt(" (void)((p->%s) | 0); /* check that '%s.%s' is " + "an integer */" % (fname, cname, fname)) + continue + # only accept exactly the type declared, except that '[]' + # is interpreted as a '*' and so will match any array length. + # (It would also match '*', but that's harder to detect...) + while (isinstance(ftype, model.ArrayType) + and (ftype.length is None or ftype.length == '...')): + ftype = ftype.item + fname = fname + '[0]' + prnt(' { %s = &p->%s; (void)tmp; }' % ( + ftype.get_c_name('*tmp', 'field %r'%fname, quals=fqual), + fname)) + except VerificationError as e: + prnt(' /* %s */' % str(e)) # cannot verify it, ignore + prnt('}') + prnt('struct _cffi_align_%s { char x; %s y; };' % (approxname, cname)) + prnt() + + def _struct_ctx(self, tp, cname, approxname, named_ptr=None): + type_index = self._typesdict[tp] + reason_for_not_expanding = None + flags = [] + if isinstance(tp, model.UnionType): + flags.append("_CFFI_F_UNION") + if tp.fldtypes is None: + flags.append("_CFFI_F_OPAQUE") + reason_for_not_expanding = "opaque" + if (tp not in self.ffi._parser._included_declarations and + (named_ptr is None or + named_ptr not in self.ffi._parser._included_declarations)): + if tp.fldtypes is None: + pass # opaque + elif tp.partial or any(tp.anonymous_struct_fields()): + pass # field layout obtained silently from the C compiler + else: + flags.append("_CFFI_F_CHECK_FIELDS") + if tp.packed: + if tp.packed > 1: + raise NotImplementedError( + "%r is declared with 'pack=%r'; only 0 or 1 are " + "supported in API mode (try to use \"...;\", which " + "does not require a 'pack' declaration)" % + (tp, tp.packed)) + flags.append("_CFFI_F_PACKED") + else: + flags.append("_CFFI_F_EXTERNAL") + reason_for_not_expanding = "external" + flags = '|'.join(flags) or '0' + c_fields = [] + if reason_for_not_expanding is None: + enumfields = list(self._enum_fields(tp)) + for fldname, fldtype, fbitsize, fqual in enumfields: + fldtype = self._field_type(tp, fldname, fldtype) + self._check_not_opaque(fldtype, + "field '%s.%s'" % (tp.name, fldname)) + # cname is None for _add_missing_struct_unions() only + op = OP_NOOP + if fbitsize >= 0: + op = OP_BITFIELD + size = '%d /* bits */' % fbitsize + elif cname is None or ( + isinstance(fldtype, model.ArrayType) and + fldtype.length is None): + size = '(size_t)-1' + else: + size = 'sizeof(((%s)0)->%s)' % ( + tp.get_c_name('*') if named_ptr is None + else named_ptr.name, + fldname) + if cname is None or fbitsize >= 0: + offset = '(size_t)-1' + elif named_ptr is not None: + offset = '((char *)&((%s)4096)->%s) - (char *)4096' % ( + named_ptr.name, fldname) + else: + offset = 'offsetof(%s, %s)' % (tp.get_c_name(''), fldname) + c_fields.append( + FieldExpr(fldname, offset, size, fbitsize, + CffiOp(op, self._typesdict[fldtype]))) + first_field_index = len(self._lsts["field"]) + self._lsts["field"].extend(c_fields) + # + if cname is None: # unknown name, for _add_missing_struct_unions + size = '(size_t)-2' + align = -2 + comment = "unnamed" + else: + if named_ptr is not None: + size = 'sizeof(*(%s)0)' % (named_ptr.name,) + align = '-1 /* unknown alignment */' + else: + size = 'sizeof(%s)' % (cname,) + align = 'offsetof(struct _cffi_align_%s, y)' % (approxname,) + comment = None + else: + size = '(size_t)-1' + align = -1 + first_field_index = -1 + comment = reason_for_not_expanding + self._lsts["struct_union"].append( + StructUnionExpr(tp.name, type_index, flags, size, align, comment, + first_field_index, c_fields)) + self._seen_struct_unions.add(tp) + + def _check_not_opaque(self, tp, location): + while isinstance(tp, model.ArrayType): + tp = tp.item + if isinstance(tp, model.StructOrUnion) and tp.fldtypes is None: + raise TypeError( + "%s is of an opaque type (not declared in cdef())" % location) + + def _add_missing_struct_unions(self): + # not very nice, but some struct declarations might be missing + # because they don't have any known C name. Check that they are + # not partial (we can't complete or verify them!) and emit them + # anonymously. + lst = list(self._struct_unions.items()) + lst.sort(key=lambda tp_order: tp_order[1]) + for tp, order in lst: + if tp not in self._seen_struct_unions: + if tp.partial: + raise NotImplementedError("internal inconsistency: %r is " + "partial but was not seen at " + "this point" % (tp,)) + if tp.name.startswith('$') and tp.name[1:].isdigit(): + approxname = tp.name[1:] + elif tp.name == '_IO_FILE' and tp.forcename == 'FILE': + approxname = 'FILE' + self._typedef_ctx(tp, 'FILE') + else: + raise NotImplementedError("internal inconsistency: %r" % + (tp,)) + self._struct_ctx(tp, None, approxname) + + def _generate_cpy_struct_collecttype(self, tp, name): + self._struct_collecttype(tp) + _generate_cpy_union_collecttype = _generate_cpy_struct_collecttype + + def _struct_names(self, tp): + cname = tp.get_c_name('') + if ' ' in cname: + return cname, cname.replace(' ', '_') + else: + return cname, '_' + cname + + def _generate_cpy_struct_decl(self, tp, name): + self._struct_decl(tp, *self._struct_names(tp)) + _generate_cpy_union_decl = _generate_cpy_struct_decl + + def _generate_cpy_struct_ctx(self, tp, name): + self._struct_ctx(tp, *self._struct_names(tp)) + _generate_cpy_union_ctx = _generate_cpy_struct_ctx + + # ---------- + # 'anonymous' declarations. These are produced for anonymous structs + # or unions; the 'name' is obtained by a typedef. + + def _generate_cpy_anonymous_collecttype(self, tp, name): + if isinstance(tp, model.EnumType): + self._generate_cpy_enum_collecttype(tp, name) + else: + self._struct_collecttype(tp) + + def _generate_cpy_anonymous_decl(self, tp, name): + if isinstance(tp, model.EnumType): + self._generate_cpy_enum_decl(tp) + else: + self._struct_decl(tp, name, 'typedef_' + name) + + def _generate_cpy_anonymous_ctx(self, tp, name): + if isinstance(tp, model.EnumType): + self._enum_ctx(tp, name) + else: + self._struct_ctx(tp, name, 'typedef_' + name) + + # ---------- + # constants, declared with "static const ..." + + def _generate_cpy_const(self, is_int, name, tp=None, category='const', + check_value=None): + if (category, name) in self._seen_constants: + raise VerificationError( + "duplicate declaration of %s '%s'" % (category, name)) + self._seen_constants.add((category, name)) + # + prnt = self._prnt + funcname = '_cffi_%s_%s' % (category, name) + if is_int: + prnt('static int %s(unsigned long long *o)' % funcname) + prnt('{') + prnt(' int n = (%s) <= 0;' % (name,)) + prnt(' *o = (unsigned long long)((%s) | 0);' + ' /* check that %s is an integer */' % (name, name)) + if check_value is not None: + if check_value > 0: + check_value = '%dU' % (check_value,) + prnt(' if (!_cffi_check_int(*o, n, %s))' % (check_value,)) + prnt(' n |= 2;') + prnt(' return n;') + prnt('}') + else: + assert check_value is None + prnt('static void %s(char *o)' % funcname) + prnt('{') + prnt(' *(%s)o = %s;' % (tp.get_c_name('*'), name)) + prnt('}') + prnt() + + def _generate_cpy_constant_collecttype(self, tp, name): + is_int = tp.is_integer_type() + if not is_int or self.target_is_python: + self._do_collect_type(tp) + + def _generate_cpy_constant_decl(self, tp, name): + is_int = tp.is_integer_type() + self._generate_cpy_const(is_int, name, tp) + + def _generate_cpy_constant_ctx(self, tp, name): + if not self.target_is_python and tp.is_integer_type(): + type_op = CffiOp(OP_CONSTANT_INT, -1) + else: + if self.target_is_python: + const_kind = OP_DLOPEN_CONST + else: + const_kind = OP_CONSTANT + type_index = self._typesdict[tp] + type_op = CffiOp(const_kind, type_index) + self._lsts["global"].append( + GlobalExpr(name, '_cffi_const_%s' % name, type_op)) + + # ---------- + # enums + + def _generate_cpy_enum_collecttype(self, tp, name): + self._do_collect_type(tp) + + def _generate_cpy_enum_decl(self, tp, name=None): + for enumerator in tp.enumerators: + self._generate_cpy_const(True, enumerator) + + def _enum_ctx(self, tp, cname): + type_index = self._typesdict[tp] + type_op = CffiOp(OP_ENUM, -1) + if self.target_is_python: + tp.check_not_partial() + for enumerator, enumvalue in zip(tp.enumerators, tp.enumvalues): + self._lsts["global"].append( + GlobalExpr(enumerator, '_cffi_const_%s' % enumerator, type_op, + check_value=enumvalue)) + # + if cname is not None and '$' not in cname and not self.target_is_python: + size = "sizeof(%s)" % cname + signed = "((%s)-1) <= 0" % cname + else: + basetp = tp.build_baseinttype(self.ffi, []) + size = self.ffi.sizeof(basetp) + signed = int(int(self.ffi.cast(basetp, -1)) < 0) + allenums = ",".join(tp.enumerators) + self._lsts["enum"].append( + EnumExpr(tp.name, type_index, size, signed, allenums)) + + def _generate_cpy_enum_ctx(self, tp, name): + self._enum_ctx(tp, tp._get_c_name()) + + # ---------- + # macros: for now only for integers + + def _generate_cpy_macro_collecttype(self, tp, name): + pass + + def _generate_cpy_macro_decl(self, tp, name): + if tp == '...': + check_value = None + else: + check_value = tp # an integer + self._generate_cpy_const(True, name, check_value=check_value) + + def _generate_cpy_macro_ctx(self, tp, name): + if tp == '...': + if self.target_is_python: + raise VerificationError( + "cannot use the syntax '...' in '#define %s ...' when " + "using the ABI mode" % (name,)) + check_value = None + else: + check_value = tp # an integer + type_op = CffiOp(OP_CONSTANT_INT, -1) + self._lsts["global"].append( + GlobalExpr(name, '_cffi_const_%s' % name, type_op, + check_value=check_value)) + + # ---------- + # global variables + + def _global_type(self, tp, global_name): + if isinstance(tp, model.ArrayType): + actual_length = tp.length + if actual_length == '...': + actual_length = '_cffi_array_len(%s)' % (global_name,) + tp_item = self._global_type(tp.item, '%s[0]' % global_name) + tp = model.ArrayType(tp_item, actual_length) + return tp + + def _generate_cpy_variable_collecttype(self, tp, name): + self._do_collect_type(self._global_type(tp, name)) + + def _generate_cpy_variable_decl(self, tp, name): + prnt = self._prnt + tp = self._global_type(tp, name) + if isinstance(tp, model.ArrayType) and tp.length is None: + tp = tp.item + ampersand = '' + else: + ampersand = '&' + # This code assumes that casts from "tp *" to "void *" is a + # no-op, i.e. a function that returns a "tp *" can be called + # as if it returned a "void *". This should be generally true + # on any modern machine. The only exception to that rule (on + # uncommon architectures, and as far as I can tell) might be + # if 'tp' were a function type, but that is not possible here. + # (If 'tp' is a function _pointer_ type, then casts from "fn_t + # **" to "void *" are again no-ops, as far as I can tell.) + decl = '*_cffi_var_%s(void)' % (name,) + prnt('static ' + tp.get_c_name(decl, quals=self._current_quals)) + prnt('{') + prnt(' return %s(%s);' % (ampersand, name)) + prnt('}') + prnt() + + def _generate_cpy_variable_ctx(self, tp, name): + tp = self._global_type(tp, name) + type_index = self._typesdict[tp] + if self.target_is_python: + op = OP_GLOBAL_VAR + else: + op = OP_GLOBAL_VAR_F + self._lsts["global"].append( + GlobalExpr(name, '_cffi_var_%s' % name, CffiOp(op, type_index))) + + # ---------- + # extern "Python" + + def _generate_cpy_extern_python_collecttype(self, tp, name): + assert isinstance(tp, model.FunctionPtrType) + self._do_collect_type(tp) + _generate_cpy_dllexport_python_collecttype = \ + _generate_cpy_extern_python_plus_c_collecttype = \ + _generate_cpy_extern_python_collecttype + + def _extern_python_decl(self, tp, name, tag_and_space): + prnt = self._prnt + if isinstance(tp.result, model.VoidType): + size_of_result = '0' + else: + context = 'result of %s' % name + size_of_result = '(int)sizeof(%s)' % ( + tp.result.get_c_name('', context),) + prnt('static struct _cffi_externpy_s _cffi_externpy__%s =' % name) + prnt(' { "%s.%s", %s, 0, 0 };' % ( + self.module_name, name, size_of_result)) + prnt() + # + arguments = [] + context = 'argument of %s' % name + for i, type in enumerate(tp.args): + arg = type.get_c_name(' a%d' % i, context) + arguments.append(arg) + # + repr_arguments = ', '.join(arguments) + repr_arguments = repr_arguments or 'void' + name_and_arguments = '%s(%s)' % (name, repr_arguments) + if tp.abi == "__stdcall": + name_and_arguments = '_cffi_stdcall ' + name_and_arguments + # + def may_need_128_bits(tp): + return (isinstance(tp, model.PrimitiveType) and + tp.name == 'long double') + # + size_of_a = max(len(tp.args)*8, 8) + if may_need_128_bits(tp.result): + size_of_a = max(size_of_a, 16) + if isinstance(tp.result, model.StructOrUnion): + size_of_a = 'sizeof(%s) > %d ? sizeof(%s) : %d' % ( + tp.result.get_c_name(''), size_of_a, + tp.result.get_c_name(''), size_of_a) + prnt('%s%s' % (tag_and_space, tp.result.get_c_name(name_and_arguments))) + prnt('{') + prnt(' char a[%s];' % size_of_a) + prnt(' char *p = a;') + for i, type in enumerate(tp.args): + arg = 'a%d' % i + if (isinstance(type, model.StructOrUnion) or + may_need_128_bits(type)): + arg = '&' + arg + type = model.PointerType(type) + prnt(' *(%s)(p + %d) = %s;' % (type.get_c_name('*'), i*8, arg)) + prnt(' _cffi_call_python(&_cffi_externpy__%s, p);' % name) + if not isinstance(tp.result, model.VoidType): + prnt(' return *(%s)p;' % (tp.result.get_c_name('*'),)) + prnt('}') + prnt() + self._num_externpy += 1 + + def _generate_cpy_extern_python_decl(self, tp, name): + self._extern_python_decl(tp, name, 'static ') + + def _generate_cpy_dllexport_python_decl(self, tp, name): + self._extern_python_decl(tp, name, 'CFFI_DLLEXPORT ') + + def _generate_cpy_extern_python_plus_c_decl(self, tp, name): + self._extern_python_decl(tp, name, '') + + def _generate_cpy_extern_python_ctx(self, tp, name): + if self.target_is_python: + raise VerificationError( + "cannot use 'extern \"Python\"' in the ABI mode") + if tp.ellipsis: + raise NotImplementedError("a vararg function is extern \"Python\"") + type_index = self._typesdict[tp] + type_op = CffiOp(OP_EXTERN_PYTHON, type_index) + self._lsts["global"].append( + GlobalExpr(name, '&_cffi_externpy__%s' % name, type_op, name)) + + _generate_cpy_dllexport_python_ctx = \ + _generate_cpy_extern_python_plus_c_ctx = \ + _generate_cpy_extern_python_ctx + + def _print_string_literal_in_array(self, s): + prnt = self._prnt + prnt('// # NB. this is not a string because of a size limit in MSVC') + if not isinstance(s, bytes): # unicode + s = s.encode('utf-8') # -> bytes + else: + s.decode('utf-8') # got bytes, check for valid utf-8 + try: + s.decode('ascii') + except UnicodeDecodeError: + s = b'# -*- encoding: utf8 -*-\n' + s + for line in s.splitlines(True): + comment = line + if type('//') is bytes: # python2 + line = map(ord, line) # make a list of integers + else: # python3 + # type(line) is bytes, which enumerates like a list of integers + comment = ascii(comment)[1:-1] + prnt(('// ' + comment).rstrip()) + printed_line = '' + for c in line: + if len(printed_line) >= 76: + prnt(printed_line) + printed_line = '' + printed_line += '%d,' % (c,) + prnt(printed_line) + + # ---------- + # emitting the opcodes for individual types + + def _emit_bytecode_VoidType(self, tp, index): + self.cffi_types[index] = CffiOp(OP_PRIMITIVE, PRIM_VOID) + + def _emit_bytecode_PrimitiveType(self, tp, index): + prim_index = PRIMITIVE_TO_INDEX[tp.name] + self.cffi_types[index] = CffiOp(OP_PRIMITIVE, prim_index) + + def _emit_bytecode_UnknownIntegerType(self, tp, index): + s = ('_cffi_prim_int(sizeof(%s), (\n' + ' ((%s)-1) | 0 /* check that %s is an integer type */\n' + ' ) <= 0)' % (tp.name, tp.name, tp.name)) + self.cffi_types[index] = CffiOp(OP_PRIMITIVE, s) + + def _emit_bytecode_UnknownFloatType(self, tp, index): + s = ('_cffi_prim_float(sizeof(%s) *\n' + ' (((%s)1) / 2) * 2 /* integer => 0, float => 1 */\n' + ' )' % (tp.name, tp.name)) + self.cffi_types[index] = CffiOp(OP_PRIMITIVE, s) + + def _emit_bytecode_RawFunctionType(self, tp, index): + self.cffi_types[index] = CffiOp(OP_FUNCTION, self._typesdict[tp.result]) + index += 1 + for tp1 in tp.args: + realindex = self._typesdict[tp1] + if index != realindex: + if isinstance(tp1, model.PrimitiveType): + self._emit_bytecode_PrimitiveType(tp1, index) + else: + self.cffi_types[index] = CffiOp(OP_NOOP, realindex) + index += 1 + flags = int(tp.ellipsis) + if tp.abi is not None: + if tp.abi == '__stdcall': + flags |= 2 + else: + raise NotImplementedError("abi=%r" % (tp.abi,)) + self.cffi_types[index] = CffiOp(OP_FUNCTION_END, flags) + + def _emit_bytecode_PointerType(self, tp, index): + self.cffi_types[index] = CffiOp(OP_POINTER, self._typesdict[tp.totype]) + + _emit_bytecode_ConstPointerType = _emit_bytecode_PointerType + _emit_bytecode_NamedPointerType = _emit_bytecode_PointerType + + def _emit_bytecode_FunctionPtrType(self, tp, index): + raw = tp.as_raw_function() + self.cffi_types[index] = CffiOp(OP_POINTER, self._typesdict[raw]) + + def _emit_bytecode_ArrayType(self, tp, index): + item_index = self._typesdict[tp.item] + if tp.length is None: + self.cffi_types[index] = CffiOp(OP_OPEN_ARRAY, item_index) + elif tp.length == '...': + raise VerificationError( + "type %s badly placed: the '...' array length can only be " + "used on global arrays or on fields of structures" % ( + str(tp).replace('/*...*/', '...'),)) + else: + assert self.cffi_types[index + 1] == 'LEN' + self.cffi_types[index] = CffiOp(OP_ARRAY, item_index) + self.cffi_types[index + 1] = CffiOp(None, str(tp.length)) + + def _emit_bytecode_StructType(self, tp, index): + struct_index = self._struct_unions[tp] + self.cffi_types[index] = CffiOp(OP_STRUCT_UNION, struct_index) + _emit_bytecode_UnionType = _emit_bytecode_StructType + + def _emit_bytecode_EnumType(self, tp, index): + enum_index = self._enums[tp] + self.cffi_types[index] = CffiOp(OP_ENUM, enum_index) + + +if sys.version_info >= (3,): + NativeIO = io.StringIO +else: + class NativeIO(io.BytesIO): + def write(self, s): + if isinstance(s, unicode): + s = s.encode('ascii') + super(NativeIO, self).write(s) + +def _make_c_or_py_source(ffi, module_name, preamble, target_file, verbose): + if verbose: + print("generating %s" % (target_file,)) + recompiler = Recompiler(ffi, module_name, + target_is_python=(preamble is None)) + recompiler.collect_type_table() + recompiler.collect_step_tables() + f = NativeIO() + recompiler.write_source_to_f(f, preamble) + output = f.getvalue() + try: + with open(target_file, 'r') as f1: + if f1.read(len(output) + 1) != output: + raise IOError + if verbose: + print("(already up-to-date)") + return False # already up-to-date + except IOError: + tmp_file = '%s.~%d' % (target_file, os.getpid()) + with open(tmp_file, 'w') as f1: + f1.write(output) + try: + os.rename(tmp_file, target_file) + except OSError: + os.unlink(target_file) + os.rename(tmp_file, target_file) + return True + +def make_c_source(ffi, module_name, preamble, target_c_file, verbose=False): + assert preamble is not None + return _make_c_or_py_source(ffi, module_name, preamble, target_c_file, + verbose) + +def make_py_source(ffi, module_name, target_py_file, verbose=False): + return _make_c_or_py_source(ffi, module_name, None, target_py_file, + verbose) + +def _modname_to_file(outputdir, modname, extension): + parts = modname.split('.') + try: + os.makedirs(os.path.join(outputdir, *parts[:-1])) + except OSError: + pass + parts[-1] += extension + return os.path.join(outputdir, *parts), parts + + +# Aaargh. Distutils is not tested at all for the purpose of compiling +# DLLs that are not extension modules. Here are some hacks to work +# around that, in the _patch_for_*() functions... + +def _patch_meth(patchlist, cls, name, new_meth): + old = getattr(cls, name) + patchlist.append((cls, name, old)) + setattr(cls, name, new_meth) + return old + +def _unpatch_meths(patchlist): + for cls, name, old_meth in reversed(patchlist): + setattr(cls, name, old_meth) + +def _patch_for_embedding(patchlist): + if sys.platform == 'win32': + # we must not remove the manifest when building for embedding! + from cffi._shimmed_dist_utils import MSVCCompiler + _patch_meth(patchlist, MSVCCompiler, '_remove_visual_c_ref', + lambda self, manifest_file: manifest_file) + + if sys.platform == 'darwin': + # we must not make a '-bundle', but a '-dynamiclib' instead + from cffi._shimmed_dist_utils import CCompiler + def my_link_shared_object(self, *args, **kwds): + if '-bundle' in self.linker_so: + self.linker_so = list(self.linker_so) + i = self.linker_so.index('-bundle') + self.linker_so[i] = '-dynamiclib' + return old_link_shared_object(self, *args, **kwds) + old_link_shared_object = _patch_meth(patchlist, CCompiler, + 'link_shared_object', + my_link_shared_object) + +def _patch_for_target(patchlist, target): + from cffi._shimmed_dist_utils import build_ext + # if 'target' is different from '*', we need to patch some internal + # method to just return this 'target' value, instead of having it + # built from module_name + if target.endswith('.*'): + target = target[:-2] + if sys.platform == 'win32': + target += '.dll' + elif sys.platform == 'darwin': + target += '.dylib' + else: + target += '.so' + _patch_meth(patchlist, build_ext, 'get_ext_filename', + lambda self, ext_name: target) + + +def recompile(ffi, module_name, preamble, tmpdir='.', call_c_compiler=True, + c_file=None, source_extension='.c', extradir=None, + compiler_verbose=1, target=None, debug=None, **kwds): + if not isinstance(module_name, str): + module_name = module_name.encode('ascii') + if ffi._windows_unicode: + ffi._apply_windows_unicode(kwds) + if preamble is not None: + embedding = (ffi._embedding is not None) + if embedding: + ffi._apply_embedding_fix(kwds) + if c_file is None: + c_file, parts = _modname_to_file(tmpdir, module_name, + source_extension) + if extradir: + parts = [extradir] + parts + ext_c_file = os.path.join(*parts) + else: + ext_c_file = c_file + # + if target is None: + if embedding: + target = '%s.*' % module_name + else: + target = '*' + # + ext = ffiplatform.get_extension(ext_c_file, module_name, **kwds) + updated = make_c_source(ffi, module_name, preamble, c_file, + verbose=compiler_verbose) + if call_c_compiler: + patchlist = [] + cwd = os.getcwd() + try: + if embedding: + _patch_for_embedding(patchlist) + if target != '*': + _patch_for_target(patchlist, target) + if compiler_verbose: + if tmpdir == '.': + msg = 'the current directory is' + else: + msg = 'setting the current directory to' + print('%s %r' % (msg, os.path.abspath(tmpdir))) + os.chdir(tmpdir) + outputfilename = ffiplatform.compile('.', ext, + compiler_verbose, debug) + finally: + os.chdir(cwd) + _unpatch_meths(patchlist) + return outputfilename + else: + return ext, updated + else: + if c_file is None: + c_file, _ = _modname_to_file(tmpdir, module_name, '.py') + updated = make_py_source(ffi, module_name, c_file, + verbose=compiler_verbose) + if call_c_compiler: + return c_file + else: + return None, updated + diff --git a/code/.venv/lib/python3.12/site-packages/cffi/setuptools_ext.py b/code/.venv/lib/python3.12/site-packages/cffi/setuptools_ext.py new file mode 100644 index 0000000..681b49d --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/cffi/setuptools_ext.py @@ -0,0 +1,216 @@ +import os +import sys + +try: + basestring +except NameError: + # Python 3.x + basestring = str + +def error(msg): + from cffi._shimmed_dist_utils import DistutilsSetupError + raise DistutilsSetupError(msg) + + +def execfile(filename, glob): + # We use execfile() (here rewritten for Python 3) instead of + # __import__() to load the build script. The problem with + # a normal import is that in some packages, the intermediate + # __init__.py files may already try to import the file that + # we are generating. + with open(filename) as f: + src = f.read() + src += '\n' # Python 2.6 compatibility + code = compile(src, filename, 'exec') + exec(code, glob, glob) + + +def add_cffi_module(dist, mod_spec): + from cffi.api import FFI + + if not isinstance(mod_spec, basestring): + error("argument to 'cffi_modules=...' must be a str or a list of str," + " not %r" % (type(mod_spec).__name__,)) + mod_spec = str(mod_spec) + try: + build_file_name, ffi_var_name = mod_spec.split(':') + except ValueError: + error("%r must be of the form 'path/build.py:ffi_variable'" % + (mod_spec,)) + if not os.path.exists(build_file_name): + ext = '' + rewritten = build_file_name.replace('.', '/') + '.py' + if os.path.exists(rewritten): + ext = ' (rewrite cffi_modules to [%r])' % ( + rewritten + ':' + ffi_var_name,) + error("%r does not name an existing file%s" % (build_file_name, ext)) + + mod_vars = {'__name__': '__cffi__', '__file__': build_file_name} + execfile(build_file_name, mod_vars) + + try: + ffi = mod_vars[ffi_var_name] + except KeyError: + error("%r: object %r not found in module" % (mod_spec, + ffi_var_name)) + if not isinstance(ffi, FFI): + ffi = ffi() # maybe it's a function instead of directly an ffi + if not isinstance(ffi, FFI): + error("%r is not an FFI instance (got %r)" % (mod_spec, + type(ffi).__name__)) + if not hasattr(ffi, '_assigned_source'): + error("%r: the set_source() method was not called" % (mod_spec,)) + module_name, source, source_extension, kwds = ffi._assigned_source + if ffi._windows_unicode: + kwds = kwds.copy() + ffi._apply_windows_unicode(kwds) + + if source is None: + _add_py_module(dist, ffi, module_name) + else: + _add_c_module(dist, ffi, module_name, source, source_extension, kwds) + +def _set_py_limited_api(Extension, kwds): + """ + Add py_limited_api to kwds if setuptools >= 26 is in use. + Do not alter the setting if it already exists. + Setuptools takes care of ignoring the flag on Python 2 and PyPy. + + CPython itself should ignore the flag in a debugging version + (by not listing .abi3.so in the extensions it supports), but + it doesn't so far, creating troubles. That's why we check + for "not hasattr(sys, 'gettotalrefcount')" (the 2.7 compatible equivalent + of 'd' not in sys.abiflags). (http://bugs.python.org/issue28401) + + On Windows, with CPython <= 3.4, it's better not to use py_limited_api + because virtualenv *still* doesn't copy PYTHON3.DLL on these versions. + Recently (2020) we started shipping only >= 3.5 wheels, though. So + we'll give it another try and set py_limited_api on Windows >= 3.5. + """ + from cffi import recompiler + + if ('py_limited_api' not in kwds and not hasattr(sys, 'gettotalrefcount') + and recompiler.USE_LIMITED_API): + import setuptools + try: + setuptools_major_version = int(setuptools.__version__.partition('.')[0]) + if setuptools_major_version >= 26: + kwds['py_limited_api'] = True + except ValueError: # certain development versions of setuptools + # If we don't know the version number of setuptools, we + # try to set 'py_limited_api' anyway. At worst, we get a + # warning. + kwds['py_limited_api'] = True + return kwds + +def _add_c_module(dist, ffi, module_name, source, source_extension, kwds): + # We are a setuptools extension. Need this build_ext for py_limited_api. + from setuptools.command.build_ext import build_ext + from cffi._shimmed_dist_utils import Extension, log, mkpath + from cffi import recompiler + + allsources = ['$PLACEHOLDER'] + allsources.extend(kwds.pop('sources', [])) + kwds = _set_py_limited_api(Extension, kwds) + ext = Extension(name=module_name, sources=allsources, **kwds) + + def make_mod(tmpdir, pre_run=None): + c_file = os.path.join(tmpdir, module_name + source_extension) + log.info("generating cffi module %r" % c_file) + mkpath(tmpdir) + # a setuptools-only, API-only hook: called with the "ext" and "ffi" + # arguments just before we turn the ffi into C code. To use it, + # subclass the 'distutils.command.build_ext.build_ext' class and + # add a method 'def pre_run(self, ext, ffi)'. + if pre_run is not None: + pre_run(ext, ffi) + updated = recompiler.make_c_source(ffi, module_name, source, c_file) + if not updated: + log.info("already up-to-date") + return c_file + + if dist.ext_modules is None: + dist.ext_modules = [] + dist.ext_modules.append(ext) + + base_class = dist.cmdclass.get('build_ext', build_ext) + class build_ext_make_mod(base_class): + def run(self): + if ext.sources[0] == '$PLACEHOLDER': + pre_run = getattr(self, 'pre_run', None) + ext.sources[0] = make_mod(self.build_temp, pre_run) + base_class.run(self) + dist.cmdclass['build_ext'] = build_ext_make_mod + # NB. multiple runs here will create multiple 'build_ext_make_mod' + # classes. Even in this case the 'build_ext' command should be + # run once; but just in case, the logic above does nothing if + # called again. + + +def _add_py_module(dist, ffi, module_name): + from setuptools.command.build_py import build_py + from setuptools.command.build_ext import build_ext + from cffi._shimmed_dist_utils import log, mkpath + from cffi import recompiler + + def generate_mod(py_file): + log.info("generating cffi module %r" % py_file) + mkpath(os.path.dirname(py_file)) + updated = recompiler.make_py_source(ffi, module_name, py_file) + if not updated: + log.info("already up-to-date") + + base_class = dist.cmdclass.get('build_py', build_py) + class build_py_make_mod(base_class): + def run(self): + base_class.run(self) + module_path = module_name.split('.') + module_path[-1] += '.py' + generate_mod(os.path.join(self.build_lib, *module_path)) + def get_source_files(self): + # This is called from 'setup.py sdist' only. Exclude + # the generate .py module in this case. + saved_py_modules = self.py_modules + try: + if saved_py_modules: + self.py_modules = [m for m in saved_py_modules + if m != module_name] + return base_class.get_source_files(self) + finally: + self.py_modules = saved_py_modules + dist.cmdclass['build_py'] = build_py_make_mod + + # distutils and setuptools have no notion I could find of a + # generated python module. If we don't add module_name to + # dist.py_modules, then things mostly work but there are some + # combination of options (--root and --record) that will miss + # the module. So we add it here, which gives a few apparently + # harmless warnings about not finding the file outside the + # build directory. + # Then we need to hack more in get_source_files(); see above. + if dist.py_modules is None: + dist.py_modules = [] + dist.py_modules.append(module_name) + + # the following is only for "build_ext -i" + base_class_2 = dist.cmdclass.get('build_ext', build_ext) + class build_ext_make_mod(base_class_2): + def run(self): + base_class_2.run(self) + if self.inplace: + # from get_ext_fullpath() in distutils/command/build_ext.py + module_path = module_name.split('.') + package = '.'.join(module_path[:-1]) + build_py = self.get_finalized_command('build_py') + package_dir = build_py.get_package_dir(package) + file_name = module_path[-1] + '.py' + generate_mod(os.path.join(package_dir, file_name)) + dist.cmdclass['build_ext'] = build_ext_make_mod + +def cffi_modules(dist, attr, value): + assert attr == 'cffi_modules' + if isinstance(value, basestring): + value = [value] + + for cffi_module in value: + add_cffi_module(dist, cffi_module) diff --git a/code/.venv/lib/python3.12/site-packages/cffi/vengine_cpy.py b/code/.venv/lib/python3.12/site-packages/cffi/vengine_cpy.py new file mode 100644 index 0000000..eb0b6f7 --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/cffi/vengine_cpy.py @@ -0,0 +1,1084 @@ +# +# DEPRECATED: implementation for ffi.verify() +# +import sys +from . import model +from .error import VerificationError +from . import _imp_emulation as imp + + +class VCPythonEngine(object): + _class_key = 'x' + _gen_python_module = True + + def __init__(self, verifier): + self.verifier = verifier + self.ffi = verifier.ffi + self._struct_pending_verification = {} + self._types_of_builtin_functions = {} + + def patch_extension_kwds(self, kwds): + pass + + def find_module(self, module_name, path, so_suffixes): + try: + f, filename, descr = imp.find_module(module_name, path) + except ImportError: + return None + if f is not None: + f.close() + # Note that after a setuptools installation, there are both .py + # and .so files with the same basename. The code here relies on + # imp.find_module() locating the .so in priority. + if descr[0] not in so_suffixes: + return None + return filename + + def collect_types(self): + self._typesdict = {} + self._generate("collecttype") + + def _prnt(self, what=''): + self._f.write(what + '\n') + + def _gettypenum(self, type): + # a KeyError here is a bug. please report it! :-) + return self._typesdict[type] + + def _do_collect_type(self, tp): + if ((not isinstance(tp, model.PrimitiveType) + or tp.name == 'long double') + and tp not in self._typesdict): + num = len(self._typesdict) + self._typesdict[tp] = num + + def write_source_to_f(self): + self.collect_types() + # + # The new module will have a _cffi_setup() function that receives + # objects from the ffi world, and that calls some setup code in + # the module. This setup code is split in several independent + # functions, e.g. one per constant. The functions are "chained" + # by ending in a tail call to each other. + # + # This is further split in two chained lists, depending on if we + # can do it at import-time or if we must wait for _cffi_setup() to + # provide us with the objects. This is needed because we + # need the values of the enum constants in order to build the + # that we may have to pass to _cffi_setup(). + # + # The following two 'chained_list_constants' items contains + # the head of these two chained lists, as a string that gives the + # call to do, if any. + self._chained_list_constants = ['((void)lib,0)', '((void)lib,0)'] + # + prnt = self._prnt + # first paste some standard set of lines that are mostly '#define' + prnt(cffimod_header) + prnt() + # then paste the C source given by the user, verbatim. + prnt(self.verifier.preamble) + prnt() + # + # call generate_cpy_xxx_decl(), for every xxx found from + # ffi._parser._declarations. This generates all the functions. + self._generate("decl") + # + # implement the function _cffi_setup_custom() as calling the + # head of the chained list. + self._generate_setup_custom() + prnt() + # + # produce the method table, including the entries for the + # generated Python->C function wrappers, which are done + # by generate_cpy_function_method(). + prnt('static PyMethodDef _cffi_methods[] = {') + self._generate("method") + prnt(' {"_cffi_setup", _cffi_setup, METH_VARARGS, NULL},') + prnt(' {NULL, NULL, 0, NULL} /* Sentinel */') + prnt('};') + prnt() + # + # standard init. + modname = self.verifier.get_module_name() + constants = self._chained_list_constants[False] + prnt('#if PY_MAJOR_VERSION >= 3') + prnt() + prnt('static struct PyModuleDef _cffi_module_def = {') + prnt(' PyModuleDef_HEAD_INIT,') + prnt(' "%s",' % modname) + prnt(' NULL,') + prnt(' -1,') + prnt(' _cffi_methods,') + prnt(' NULL, NULL, NULL, NULL') + prnt('};') + prnt() + prnt('PyMODINIT_FUNC') + prnt('PyInit_%s(void)' % modname) + prnt('{') + prnt(' PyObject *lib;') + prnt(' lib = PyModule_Create(&_cffi_module_def);') + prnt(' if (lib == NULL)') + prnt(' return NULL;') + prnt(' if (%s < 0 || _cffi_init() < 0) {' % (constants,)) + prnt(' Py_DECREF(lib);') + prnt(' return NULL;') + prnt(' }') + prnt(' return lib;') + prnt('}') + prnt() + prnt('#else') + prnt() + prnt('PyMODINIT_FUNC') + prnt('init%s(void)' % modname) + prnt('{') + prnt(' PyObject *lib;') + prnt(' lib = Py_InitModule("%s", _cffi_methods);' % modname) + prnt(' if (lib == NULL)') + prnt(' return;') + prnt(' if (%s < 0 || _cffi_init() < 0)' % (constants,)) + prnt(' return;') + prnt(' return;') + prnt('}') + prnt() + prnt('#endif') + + def load_library(self, flags=None): + # XXX review all usages of 'self' here! + # import it as a new extension module + imp.acquire_lock() + try: + if hasattr(sys, "getdlopenflags"): + previous_flags = sys.getdlopenflags() + try: + if hasattr(sys, "setdlopenflags") and flags is not None: + sys.setdlopenflags(flags) + module = imp.load_dynamic(self.verifier.get_module_name(), + self.verifier.modulefilename) + except ImportError as e: + error = "importing %r: %s" % (self.verifier.modulefilename, e) + raise VerificationError(error) + finally: + if hasattr(sys, "setdlopenflags"): + sys.setdlopenflags(previous_flags) + finally: + imp.release_lock() + # + # call loading_cpy_struct() to get the struct layout inferred by + # the C compiler + self._load(module, 'loading') + # + # the C code will need the objects. Collect them in + # order in a list. + revmapping = dict([(value, key) + for (key, value) in self._typesdict.items()]) + lst = [revmapping[i] for i in range(len(revmapping))] + lst = list(map(self.ffi._get_cached_btype, lst)) + # + # build the FFILibrary class and instance and call _cffi_setup(). + # this will set up some fields like '_cffi_types', and only then + # it will invoke the chained list of functions that will really + # build (notably) the constant objects, as if they are + # pointers, and store them as attributes on the 'library' object. + class FFILibrary(object): + _cffi_python_module = module + _cffi_ffi = self.ffi + _cffi_dir = [] + def __dir__(self): + return FFILibrary._cffi_dir + list(self.__dict__) + library = FFILibrary() + if module._cffi_setup(lst, VerificationError, library): + import warnings + warnings.warn("reimporting %r might overwrite older definitions" + % (self.verifier.get_module_name())) + # + # finally, call the loaded_cpy_xxx() functions. This will perform + # the final adjustments, like copying the Python->C wrapper + # functions from the module to the 'library' object, and setting + # up the FFILibrary class with properties for the global C variables. + self._load(module, 'loaded', library=library) + module._cffi_original_ffi = self.ffi + module._cffi_types_of_builtin_funcs = self._types_of_builtin_functions + return library + + def _get_declarations(self): + lst = [(key, tp) for (key, (tp, qual)) in + self.ffi._parser._declarations.items()] + lst.sort() + return lst + + def _generate(self, step_name): + for name, tp in self._get_declarations(): + kind, realname = name.split(' ', 1) + try: + method = getattr(self, '_generate_cpy_%s_%s' % (kind, + step_name)) + except AttributeError: + raise VerificationError( + "not implemented in verify(): %r" % name) + try: + method(tp, realname) + except Exception as e: + model.attach_exception_info(e, name) + raise + + def _load(self, module, step_name, **kwds): + for name, tp in self._get_declarations(): + kind, realname = name.split(' ', 1) + method = getattr(self, '_%s_cpy_%s' % (step_name, kind)) + try: + method(tp, realname, module, **kwds) + except Exception as e: + model.attach_exception_info(e, name) + raise + + def _generate_nothing(self, tp, name): + pass + + def _loaded_noop(self, tp, name, module, **kwds): + pass + + # ---------- + + def _convert_funcarg_to_c(self, tp, fromvar, tovar, errcode): + extraarg = '' + if isinstance(tp, model.PrimitiveType): + if tp.is_integer_type() and tp.name != '_Bool': + converter = '_cffi_to_c_int' + extraarg = ', %s' % tp.name + elif tp.is_complex_type(): + raise VerificationError( + "not implemented in verify(): complex types") + else: + converter = '(%s)_cffi_to_c_%s' % (tp.get_c_name(''), + tp.name.replace(' ', '_')) + errvalue = '-1' + # + elif isinstance(tp, model.PointerType): + self._convert_funcarg_to_c_ptr_or_array(tp, fromvar, + tovar, errcode) + return + # + elif isinstance(tp, (model.StructOrUnion, model.EnumType)): + # a struct (not a struct pointer) as a function argument + self._prnt(' if (_cffi_to_c((char *)&%s, _cffi_type(%d), %s) < 0)' + % (tovar, self._gettypenum(tp), fromvar)) + self._prnt(' %s;' % errcode) + return + # + elif isinstance(tp, model.FunctionPtrType): + converter = '(%s)_cffi_to_c_pointer' % tp.get_c_name('') + extraarg = ', _cffi_type(%d)' % self._gettypenum(tp) + errvalue = 'NULL' + # + else: + raise NotImplementedError(tp) + # + self._prnt(' %s = %s(%s%s);' % (tovar, converter, fromvar, extraarg)) + self._prnt(' if (%s == (%s)%s && PyErr_Occurred())' % ( + tovar, tp.get_c_name(''), errvalue)) + self._prnt(' %s;' % errcode) + + def _extra_local_variables(self, tp, localvars, freelines): + if isinstance(tp, model.PointerType): + localvars.add('Py_ssize_t datasize') + localvars.add('struct _cffi_freeme_s *large_args_free = NULL') + freelines.add('if (large_args_free != NULL)' + ' _cffi_free_array_arguments(large_args_free);') + + def _convert_funcarg_to_c_ptr_or_array(self, tp, fromvar, tovar, errcode): + self._prnt(' datasize = _cffi_prepare_pointer_call_argument(') + self._prnt(' _cffi_type(%d), %s, (char **)&%s);' % ( + self._gettypenum(tp), fromvar, tovar)) + self._prnt(' if (datasize != 0) {') + self._prnt(' %s = ((size_t)datasize) <= 640 ? ' + 'alloca((size_t)datasize) : NULL;' % (tovar,)) + self._prnt(' if (_cffi_convert_array_argument(_cffi_type(%d), %s, ' + '(char **)&%s,' % (self._gettypenum(tp), fromvar, tovar)) + self._prnt(' datasize, &large_args_free) < 0)') + self._prnt(' %s;' % errcode) + self._prnt(' }') + + def _convert_expr_from_c(self, tp, var, context): + if isinstance(tp, model.PrimitiveType): + if tp.is_integer_type() and tp.name != '_Bool': + return '_cffi_from_c_int(%s, %s)' % (var, tp.name) + elif tp.name != 'long double': + return '_cffi_from_c_%s(%s)' % (tp.name.replace(' ', '_'), var) + else: + return '_cffi_from_c_deref((char *)&%s, _cffi_type(%d))' % ( + var, self._gettypenum(tp)) + elif isinstance(tp, (model.PointerType, model.FunctionPtrType)): + return '_cffi_from_c_pointer((char *)%s, _cffi_type(%d))' % ( + var, self._gettypenum(tp)) + elif isinstance(tp, model.ArrayType): + return '_cffi_from_c_pointer((char *)%s, _cffi_type(%d))' % ( + var, self._gettypenum(model.PointerType(tp.item))) + elif isinstance(tp, model.StructOrUnion): + if tp.fldnames is None: + raise TypeError("'%s' is used as %s, but is opaque" % ( + tp._get_c_name(), context)) + return '_cffi_from_c_struct((char *)&%s, _cffi_type(%d))' % ( + var, self._gettypenum(tp)) + elif isinstance(tp, model.EnumType): + return '_cffi_from_c_deref((char *)&%s, _cffi_type(%d))' % ( + var, self._gettypenum(tp)) + else: + raise NotImplementedError(tp) + + # ---------- + # typedefs: generates no code so far + + _generate_cpy_typedef_collecttype = _generate_nothing + _generate_cpy_typedef_decl = _generate_nothing + _generate_cpy_typedef_method = _generate_nothing + _loading_cpy_typedef = _loaded_noop + _loaded_cpy_typedef = _loaded_noop + + # ---------- + # function declarations + + def _generate_cpy_function_collecttype(self, tp, name): + assert isinstance(tp, model.FunctionPtrType) + if tp.ellipsis: + self._do_collect_type(tp) + else: + # don't call _do_collect_type(tp) in this common case, + # otherwise test_autofilled_struct_as_argument fails + for type in tp.args: + self._do_collect_type(type) + self._do_collect_type(tp.result) + + def _generate_cpy_function_decl(self, tp, name): + assert isinstance(tp, model.FunctionPtrType) + if tp.ellipsis: + # cannot support vararg functions better than this: check for its + # exact type (including the fixed arguments), and build it as a + # constant function pointer (no CPython wrapper) + self._generate_cpy_const(False, name, tp) + return + prnt = self._prnt + numargs = len(tp.args) + if numargs == 0: + argname = 'noarg' + elif numargs == 1: + argname = 'arg0' + else: + argname = 'args' + prnt('static PyObject *') + prnt('_cffi_f_%s(PyObject *self, PyObject *%s)' % (name, argname)) + prnt('{') + # + context = 'argument of %s' % name + for i, type in enumerate(tp.args): + prnt(' %s;' % type.get_c_name(' x%d' % i, context)) + # + localvars = set() + freelines = set() + for type in tp.args: + self._extra_local_variables(type, localvars, freelines) + for decl in sorted(localvars): + prnt(' %s;' % (decl,)) + # + if not isinstance(tp.result, model.VoidType): + result_code = 'result = ' + context = 'result of %s' % name + prnt(' %s;' % tp.result.get_c_name(' result', context)) + prnt(' PyObject *pyresult;') + else: + result_code = '' + # + if len(tp.args) > 1: + rng = range(len(tp.args)) + for i in rng: + prnt(' PyObject *arg%d;' % i) + prnt() + prnt(' if (!PyArg_ParseTuple(args, "%s:%s", %s))' % ( + 'O' * numargs, name, ', '.join(['&arg%d' % i for i in rng]))) + prnt(' return NULL;') + prnt() + # + for i, type in enumerate(tp.args): + self._convert_funcarg_to_c(type, 'arg%d' % i, 'x%d' % i, + 'return NULL') + prnt() + # + prnt(' Py_BEGIN_ALLOW_THREADS') + prnt(' _cffi_restore_errno();') + prnt(' { %s%s(%s); }' % ( + result_code, name, + ', '.join(['x%d' % i for i in range(len(tp.args))]))) + prnt(' _cffi_save_errno();') + prnt(' Py_END_ALLOW_THREADS') + prnt() + # + prnt(' (void)self; /* unused */') + if numargs == 0: + prnt(' (void)noarg; /* unused */') + if result_code: + prnt(' pyresult = %s;' % + self._convert_expr_from_c(tp.result, 'result', 'result type')) + for freeline in freelines: + prnt(' ' + freeline) + prnt(' return pyresult;') + else: + for freeline in freelines: + prnt(' ' + freeline) + prnt(' Py_INCREF(Py_None);') + prnt(' return Py_None;') + prnt('}') + prnt() + + def _generate_cpy_function_method(self, tp, name): + if tp.ellipsis: + return + numargs = len(tp.args) + if numargs == 0: + meth = 'METH_NOARGS' + elif numargs == 1: + meth = 'METH_O' + else: + meth = 'METH_VARARGS' + self._prnt(' {"%s", _cffi_f_%s, %s, NULL},' % (name, name, meth)) + + _loading_cpy_function = _loaded_noop + + def _loaded_cpy_function(self, tp, name, module, library): + if tp.ellipsis: + return + func = getattr(module, name) + setattr(library, name, func) + self._types_of_builtin_functions[func] = tp + + # ---------- + # named structs + + _generate_cpy_struct_collecttype = _generate_nothing + def _generate_cpy_struct_decl(self, tp, name): + assert name == tp.name + self._generate_struct_or_union_decl(tp, 'struct', name) + def _generate_cpy_struct_method(self, tp, name): + self._generate_struct_or_union_method(tp, 'struct', name) + def _loading_cpy_struct(self, tp, name, module): + self._loading_struct_or_union(tp, 'struct', name, module) + def _loaded_cpy_struct(self, tp, name, module, **kwds): + self._loaded_struct_or_union(tp) + + _generate_cpy_union_collecttype = _generate_nothing + def _generate_cpy_union_decl(self, tp, name): + assert name == tp.name + self._generate_struct_or_union_decl(tp, 'union', name) + def _generate_cpy_union_method(self, tp, name): + self._generate_struct_or_union_method(tp, 'union', name) + def _loading_cpy_union(self, tp, name, module): + self._loading_struct_or_union(tp, 'union', name, module) + def _loaded_cpy_union(self, tp, name, module, **kwds): + self._loaded_struct_or_union(tp) + + def _generate_struct_or_union_decl(self, tp, prefix, name): + if tp.fldnames is None: + return # nothing to do with opaque structs + checkfuncname = '_cffi_check_%s_%s' % (prefix, name) + layoutfuncname = '_cffi_layout_%s_%s' % (prefix, name) + cname = ('%s %s' % (prefix, name)).strip() + # + prnt = self._prnt + prnt('static void %s(%s *p)' % (checkfuncname, cname)) + prnt('{') + prnt(' /* only to generate compile-time warnings or errors */') + prnt(' (void)p;') + for fname, ftype, fbitsize, fqual in tp.enumfields(): + if (isinstance(ftype, model.PrimitiveType) + and ftype.is_integer_type()) or fbitsize >= 0: + # accept all integers, but complain on float or double + prnt(' (void)((p->%s) << 1);' % fname) + else: + # only accept exactly the type declared. + try: + prnt(' { %s = &p->%s; (void)tmp; }' % ( + ftype.get_c_name('*tmp', 'field %r'%fname, quals=fqual), + fname)) + except VerificationError as e: + prnt(' /* %s */' % str(e)) # cannot verify it, ignore + prnt('}') + prnt('static PyObject *') + prnt('%s(PyObject *self, PyObject *noarg)' % (layoutfuncname,)) + prnt('{') + prnt(' struct _cffi_aligncheck { char x; %s y; };' % cname) + prnt(' static Py_ssize_t nums[] = {') + prnt(' sizeof(%s),' % cname) + prnt(' offsetof(struct _cffi_aligncheck, y),') + for fname, ftype, fbitsize, fqual in tp.enumfields(): + if fbitsize >= 0: + continue # xxx ignore fbitsize for now + prnt(' offsetof(%s, %s),' % (cname, fname)) + if isinstance(ftype, model.ArrayType) and ftype.length is None: + prnt(' 0, /* %s */' % ftype._get_c_name()) + else: + prnt(' sizeof(((%s *)0)->%s),' % (cname, fname)) + prnt(' -1') + prnt(' };') + prnt(' (void)self; /* unused */') + prnt(' (void)noarg; /* unused */') + prnt(' return _cffi_get_struct_layout(nums);') + prnt(' /* the next line is not executed, but compiled */') + prnt(' %s(0);' % (checkfuncname,)) + prnt('}') + prnt() + + def _generate_struct_or_union_method(self, tp, prefix, name): + if tp.fldnames is None: + return # nothing to do with opaque structs + layoutfuncname = '_cffi_layout_%s_%s' % (prefix, name) + self._prnt(' {"%s", %s, METH_NOARGS, NULL},' % (layoutfuncname, + layoutfuncname)) + + def _loading_struct_or_union(self, tp, prefix, name, module): + if tp.fldnames is None: + return # nothing to do with opaque structs + layoutfuncname = '_cffi_layout_%s_%s' % (prefix, name) + # + function = getattr(module, layoutfuncname) + layout = function() + if isinstance(tp, model.StructOrUnion) and tp.partial: + # use the function()'s sizes and offsets to guide the + # layout of the struct + totalsize = layout[0] + totalalignment = layout[1] + fieldofs = layout[2::2] + fieldsize = layout[3::2] + tp.force_flatten() + assert len(fieldofs) == len(fieldsize) == len(tp.fldnames) + tp.fixedlayout = fieldofs, fieldsize, totalsize, totalalignment + else: + cname = ('%s %s' % (prefix, name)).strip() + self._struct_pending_verification[tp] = layout, cname + + def _loaded_struct_or_union(self, tp): + if tp.fldnames is None: + return # nothing to do with opaque structs + self.ffi._get_cached_btype(tp) # force 'fixedlayout' to be considered + + if tp in self._struct_pending_verification: + # check that the layout sizes and offsets match the real ones + def check(realvalue, expectedvalue, msg): + if realvalue != expectedvalue: + raise VerificationError( + "%s (we have %d, but C compiler says %d)" + % (msg, expectedvalue, realvalue)) + ffi = self.ffi + BStruct = ffi._get_cached_btype(tp) + layout, cname = self._struct_pending_verification.pop(tp) + check(layout[0], ffi.sizeof(BStruct), "wrong total size") + check(layout[1], ffi.alignof(BStruct), "wrong total alignment") + i = 2 + for fname, ftype, fbitsize, fqual in tp.enumfields(): + if fbitsize >= 0: + continue # xxx ignore fbitsize for now + check(layout[i], ffi.offsetof(BStruct, fname), + "wrong offset for field %r" % (fname,)) + if layout[i+1] != 0: + BField = ffi._get_cached_btype(ftype) + check(layout[i+1], ffi.sizeof(BField), + "wrong size for field %r" % (fname,)) + i += 2 + assert i == len(layout) + + # ---------- + # 'anonymous' declarations. These are produced for anonymous structs + # or unions; the 'name' is obtained by a typedef. + + _generate_cpy_anonymous_collecttype = _generate_nothing + + def _generate_cpy_anonymous_decl(self, tp, name): + if isinstance(tp, model.EnumType): + self._generate_cpy_enum_decl(tp, name, '') + else: + self._generate_struct_or_union_decl(tp, '', name) + + def _generate_cpy_anonymous_method(self, tp, name): + if not isinstance(tp, model.EnumType): + self._generate_struct_or_union_method(tp, '', name) + + def _loading_cpy_anonymous(self, tp, name, module): + if isinstance(tp, model.EnumType): + self._loading_cpy_enum(tp, name, module) + else: + self._loading_struct_or_union(tp, '', name, module) + + def _loaded_cpy_anonymous(self, tp, name, module, **kwds): + if isinstance(tp, model.EnumType): + self._loaded_cpy_enum(tp, name, module, **kwds) + else: + self._loaded_struct_or_union(tp) + + # ---------- + # constants, likely declared with '#define' + + def _generate_cpy_const(self, is_int, name, tp=None, category='const', + vartp=None, delayed=True, size_too=False, + check_value=None): + prnt = self._prnt + funcname = '_cffi_%s_%s' % (category, name) + prnt('static int %s(PyObject *lib)' % funcname) + prnt('{') + prnt(' PyObject *o;') + prnt(' int res;') + if not is_int: + prnt(' %s;' % (vartp or tp).get_c_name(' i', name)) + else: + assert category == 'const' + # + if check_value is not None: + self._check_int_constant_value(name, check_value) + # + if not is_int: + if category == 'var': + realexpr = '&' + name + else: + realexpr = name + prnt(' i = (%s);' % (realexpr,)) + prnt(' o = %s;' % (self._convert_expr_from_c(tp, 'i', + 'variable type'),)) + assert delayed + else: + prnt(' o = _cffi_from_c_int_const(%s);' % name) + prnt(' if (o == NULL)') + prnt(' return -1;') + if size_too: + prnt(' {') + prnt(' PyObject *o1 = o;') + prnt(' o = Py_BuildValue("On", o1, (Py_ssize_t)sizeof(%s));' + % (name,)) + prnt(' Py_DECREF(o1);') + prnt(' if (o == NULL)') + prnt(' return -1;') + prnt(' }') + prnt(' res = PyObject_SetAttrString(lib, "%s", o);' % name) + prnt(' Py_DECREF(o);') + prnt(' if (res < 0)') + prnt(' return -1;') + prnt(' return %s;' % self._chained_list_constants[delayed]) + self._chained_list_constants[delayed] = funcname + '(lib)' + prnt('}') + prnt() + + def _generate_cpy_constant_collecttype(self, tp, name): + is_int = isinstance(tp, model.PrimitiveType) and tp.is_integer_type() + if not is_int: + self._do_collect_type(tp) + + def _generate_cpy_constant_decl(self, tp, name): + is_int = isinstance(tp, model.PrimitiveType) and tp.is_integer_type() + self._generate_cpy_const(is_int, name, tp) + + _generate_cpy_constant_method = _generate_nothing + _loading_cpy_constant = _loaded_noop + _loaded_cpy_constant = _loaded_noop + + # ---------- + # enums + + def _check_int_constant_value(self, name, value, err_prefix=''): + prnt = self._prnt + if value <= 0: + prnt(' if ((%s) > 0 || (long)(%s) != %dL) {' % ( + name, name, value)) + else: + prnt(' if ((%s) <= 0 || (unsigned long)(%s) != %dUL) {' % ( + name, name, value)) + prnt(' char buf[64];') + prnt(' if ((%s) <= 0)' % name) + prnt(' snprintf(buf, 63, "%%ld", (long)(%s));' % name) + prnt(' else') + prnt(' snprintf(buf, 63, "%%lu", (unsigned long)(%s));' % + name) + prnt(' PyErr_Format(_cffi_VerificationError,') + prnt(' "%s%s has the real value %s, not %s",') + prnt(' "%s", "%s", buf, "%d");' % ( + err_prefix, name, value)) + prnt(' return -1;') + prnt(' }') + + def _enum_funcname(self, prefix, name): + # "$enum_$1" => "___D_enum____D_1" + name = name.replace('$', '___D_') + return '_cffi_e_%s_%s' % (prefix, name) + + def _generate_cpy_enum_decl(self, tp, name, prefix='enum'): + if tp.partial: + for enumerator in tp.enumerators: + self._generate_cpy_const(True, enumerator, delayed=False) + return + # + funcname = self._enum_funcname(prefix, name) + prnt = self._prnt + prnt('static int %s(PyObject *lib)' % funcname) + prnt('{') + for enumerator, enumvalue in zip(tp.enumerators, tp.enumvalues): + self._check_int_constant_value(enumerator, enumvalue, + "enum %s: " % name) + prnt(' return %s;' % self._chained_list_constants[True]) + self._chained_list_constants[True] = funcname + '(lib)' + prnt('}') + prnt() + + _generate_cpy_enum_collecttype = _generate_nothing + _generate_cpy_enum_method = _generate_nothing + + def _loading_cpy_enum(self, tp, name, module): + if tp.partial: + enumvalues = [getattr(module, enumerator) + for enumerator in tp.enumerators] + tp.enumvalues = tuple(enumvalues) + tp.partial_resolved = True + + def _loaded_cpy_enum(self, tp, name, module, library): + for enumerator, enumvalue in zip(tp.enumerators, tp.enumvalues): + setattr(library, enumerator, enumvalue) + + # ---------- + # macros: for now only for integers + + def _generate_cpy_macro_decl(self, tp, name): + if tp == '...': + check_value = None + else: + check_value = tp # an integer + self._generate_cpy_const(True, name, check_value=check_value) + + _generate_cpy_macro_collecttype = _generate_nothing + _generate_cpy_macro_method = _generate_nothing + _loading_cpy_macro = _loaded_noop + _loaded_cpy_macro = _loaded_noop + + # ---------- + # global variables + + def _generate_cpy_variable_collecttype(self, tp, name): + if isinstance(tp, model.ArrayType): + tp_ptr = model.PointerType(tp.item) + else: + tp_ptr = model.PointerType(tp) + self._do_collect_type(tp_ptr) + + def _generate_cpy_variable_decl(self, tp, name): + if isinstance(tp, model.ArrayType): + tp_ptr = model.PointerType(tp.item) + self._generate_cpy_const(False, name, tp, vartp=tp_ptr, + size_too = tp.length_is_unknown()) + else: + tp_ptr = model.PointerType(tp) + self._generate_cpy_const(False, name, tp_ptr, category='var') + + _generate_cpy_variable_method = _generate_nothing + _loading_cpy_variable = _loaded_noop + + def _loaded_cpy_variable(self, tp, name, module, library): + value = getattr(library, name) + if isinstance(tp, model.ArrayType): # int a[5] is "constant" in the + # sense that "a=..." is forbidden + if tp.length_is_unknown(): + assert isinstance(value, tuple) + (value, size) = value + BItemType = self.ffi._get_cached_btype(tp.item) + length, rest = divmod(size, self.ffi.sizeof(BItemType)) + if rest != 0: + raise VerificationError( + "bad size: %r does not seem to be an array of %s" % + (name, tp.item)) + tp = tp.resolve_length(length) + # 'value' is a which we have to replace with + # a if the N is actually known + if tp.length is not None: + BArray = self.ffi._get_cached_btype(tp) + value = self.ffi.cast(BArray, value) + setattr(library, name, value) + return + # remove ptr= from the library instance, and replace + # it by a property on the class, which reads/writes into ptr[0]. + ptr = value + delattr(library, name) + def getter(library): + return ptr[0] + def setter(library, value): + ptr[0] = value + setattr(type(library), name, property(getter, setter)) + type(library)._cffi_dir.append(name) + + # ---------- + + def _generate_setup_custom(self): + prnt = self._prnt + prnt('static int _cffi_setup_custom(PyObject *lib)') + prnt('{') + prnt(' return %s;' % self._chained_list_constants[True]) + prnt('}') + +cffimod_header = r''' +#include +#include + +/* this block of #ifs should be kept exactly identical between + c/_cffi_backend.c, cffi/vengine_cpy.py, cffi/vengine_gen.py + and cffi/_cffi_include.h */ +#if defined(_MSC_VER) +# include /* for alloca() */ +# if _MSC_VER < 1600 /* MSVC < 2010 */ + typedef __int8 int8_t; + typedef __int16 int16_t; + typedef __int32 int32_t; + typedef __int64 int64_t; + typedef unsigned __int8 uint8_t; + typedef unsigned __int16 uint16_t; + typedef unsigned __int32 uint32_t; + typedef unsigned __int64 uint64_t; + typedef __int8 int_least8_t; + typedef __int16 int_least16_t; + typedef __int32 int_least32_t; + typedef __int64 int_least64_t; + typedef unsigned __int8 uint_least8_t; + typedef unsigned __int16 uint_least16_t; + typedef unsigned __int32 uint_least32_t; + typedef unsigned __int64 uint_least64_t; + typedef __int8 int_fast8_t; + typedef __int16 int_fast16_t; + typedef __int32 int_fast32_t; + typedef __int64 int_fast64_t; + typedef unsigned __int8 uint_fast8_t; + typedef unsigned __int16 uint_fast16_t; + typedef unsigned __int32 uint_fast32_t; + typedef unsigned __int64 uint_fast64_t; + typedef __int64 intmax_t; + typedef unsigned __int64 uintmax_t; +# else +# include +# endif +# if _MSC_VER < 1800 /* MSVC < 2013 */ +# ifndef __cplusplus + typedef unsigned char _Bool; +# endif +# endif +# define _cffi_float_complex_t _Fcomplex /* include for it */ +# define _cffi_double_complex_t _Dcomplex /* include for it */ +#else +# include +# if (defined (__SVR4) && defined (__sun)) || defined(_AIX) || defined(__hpux) +# include +# endif +# define _cffi_float_complex_t float _Complex +# define _cffi_double_complex_t double _Complex +#endif + +#if PY_MAJOR_VERSION < 3 +# undef PyCapsule_CheckExact +# undef PyCapsule_GetPointer +# define PyCapsule_CheckExact(capsule) (PyCObject_Check(capsule)) +# define PyCapsule_GetPointer(capsule, name) \ + (PyCObject_AsVoidPtr(capsule)) +#endif + +#if PY_MAJOR_VERSION >= 3 +# define PyInt_FromLong PyLong_FromLong +#endif + +#define _cffi_from_c_double PyFloat_FromDouble +#define _cffi_from_c_float PyFloat_FromDouble +#define _cffi_from_c_long PyInt_FromLong +#define _cffi_from_c_ulong PyLong_FromUnsignedLong +#define _cffi_from_c_longlong PyLong_FromLongLong +#define _cffi_from_c_ulonglong PyLong_FromUnsignedLongLong +#define _cffi_from_c__Bool PyBool_FromLong + +#define _cffi_to_c_double PyFloat_AsDouble +#define _cffi_to_c_float PyFloat_AsDouble + +#define _cffi_from_c_int_const(x) \ + (((x) > 0) ? \ + ((unsigned long long)(x) <= (unsigned long long)LONG_MAX) ? \ + PyInt_FromLong((long)(x)) : \ + PyLong_FromUnsignedLongLong((unsigned long long)(x)) : \ + ((long long)(x) >= (long long)LONG_MIN) ? \ + PyInt_FromLong((long)(x)) : \ + PyLong_FromLongLong((long long)(x))) + +#define _cffi_from_c_int(x, type) \ + (((type)-1) > 0 ? /* unsigned */ \ + (sizeof(type) < sizeof(long) ? \ + PyInt_FromLong((long)x) : \ + sizeof(type) == sizeof(long) ? \ + PyLong_FromUnsignedLong((unsigned long)x) : \ + PyLong_FromUnsignedLongLong((unsigned long long)x)) : \ + (sizeof(type) <= sizeof(long) ? \ + PyInt_FromLong((long)x) : \ + PyLong_FromLongLong((long long)x))) + +#define _cffi_to_c_int(o, type) \ + ((type)( \ + sizeof(type) == 1 ? (((type)-1) > 0 ? (type)_cffi_to_c_u8(o) \ + : (type)_cffi_to_c_i8(o)) : \ + sizeof(type) == 2 ? (((type)-1) > 0 ? (type)_cffi_to_c_u16(o) \ + : (type)_cffi_to_c_i16(o)) : \ + sizeof(type) == 4 ? (((type)-1) > 0 ? (type)_cffi_to_c_u32(o) \ + : (type)_cffi_to_c_i32(o)) : \ + sizeof(type) == 8 ? (((type)-1) > 0 ? (type)_cffi_to_c_u64(o) \ + : (type)_cffi_to_c_i64(o)) : \ + (Py_FatalError("unsupported size for type " #type), (type)0))) + +#define _cffi_to_c_i8 \ + ((int(*)(PyObject *))_cffi_exports[1]) +#define _cffi_to_c_u8 \ + ((int(*)(PyObject *))_cffi_exports[2]) +#define _cffi_to_c_i16 \ + ((int(*)(PyObject *))_cffi_exports[3]) +#define _cffi_to_c_u16 \ + ((int(*)(PyObject *))_cffi_exports[4]) +#define _cffi_to_c_i32 \ + ((int(*)(PyObject *))_cffi_exports[5]) +#define _cffi_to_c_u32 \ + ((unsigned int(*)(PyObject *))_cffi_exports[6]) +#define _cffi_to_c_i64 \ + ((long long(*)(PyObject *))_cffi_exports[7]) +#define _cffi_to_c_u64 \ + ((unsigned long long(*)(PyObject *))_cffi_exports[8]) +#define _cffi_to_c_char \ + ((int(*)(PyObject *))_cffi_exports[9]) +#define _cffi_from_c_pointer \ + ((PyObject *(*)(char *, CTypeDescrObject *))_cffi_exports[10]) +#define _cffi_to_c_pointer \ + ((char *(*)(PyObject *, CTypeDescrObject *))_cffi_exports[11]) +#define _cffi_get_struct_layout \ + ((PyObject *(*)(Py_ssize_t[]))_cffi_exports[12]) +#define _cffi_restore_errno \ + ((void(*)(void))_cffi_exports[13]) +#define _cffi_save_errno \ + ((void(*)(void))_cffi_exports[14]) +#define _cffi_from_c_char \ + ((PyObject *(*)(char))_cffi_exports[15]) +#define _cffi_from_c_deref \ + ((PyObject *(*)(char *, CTypeDescrObject *))_cffi_exports[16]) +#define _cffi_to_c \ + ((int(*)(char *, CTypeDescrObject *, PyObject *))_cffi_exports[17]) +#define _cffi_from_c_struct \ + ((PyObject *(*)(char *, CTypeDescrObject *))_cffi_exports[18]) +#define _cffi_to_c_wchar_t \ + ((wchar_t(*)(PyObject *))_cffi_exports[19]) +#define _cffi_from_c_wchar_t \ + ((PyObject *(*)(wchar_t))_cffi_exports[20]) +#define _cffi_to_c_long_double \ + ((long double(*)(PyObject *))_cffi_exports[21]) +#define _cffi_to_c__Bool \ + ((_Bool(*)(PyObject *))_cffi_exports[22]) +#define _cffi_prepare_pointer_call_argument \ + ((Py_ssize_t(*)(CTypeDescrObject *, PyObject *, char **))_cffi_exports[23]) +#define _cffi_convert_array_from_object \ + ((int(*)(char *, CTypeDescrObject *, PyObject *))_cffi_exports[24]) +#define _CFFI_NUM_EXPORTS 25 + +typedef struct _ctypedescr CTypeDescrObject; + +static void *_cffi_exports[_CFFI_NUM_EXPORTS]; +static PyObject *_cffi_types, *_cffi_VerificationError; + +static int _cffi_setup_custom(PyObject *lib); /* forward */ + +static PyObject *_cffi_setup(PyObject *self, PyObject *args) +{ + PyObject *library; + int was_alive = (_cffi_types != NULL); + (void)self; /* unused */ + if (!PyArg_ParseTuple(args, "OOO", &_cffi_types, &_cffi_VerificationError, + &library)) + return NULL; + Py_INCREF(_cffi_types); + Py_INCREF(_cffi_VerificationError); + if (_cffi_setup_custom(library) < 0) + return NULL; + return PyBool_FromLong(was_alive); +} + +union _cffi_union_alignment_u { + unsigned char m_char; + unsigned short m_short; + unsigned int m_int; + unsigned long m_long; + unsigned long long m_longlong; + float m_float; + double m_double; + long double m_longdouble; +}; + +struct _cffi_freeme_s { + struct _cffi_freeme_s *next; + union _cffi_union_alignment_u alignment; +}; + +#ifdef __GNUC__ + __attribute__((unused)) +#endif +static int _cffi_convert_array_argument(CTypeDescrObject *ctptr, PyObject *arg, + char **output_data, Py_ssize_t datasize, + struct _cffi_freeme_s **freeme) +{ + char *p; + if (datasize < 0) + return -1; + + p = *output_data; + if (p == NULL) { + struct _cffi_freeme_s *fp = (struct _cffi_freeme_s *)PyObject_Malloc( + offsetof(struct _cffi_freeme_s, alignment) + (size_t)datasize); + if (fp == NULL) + return -1; + fp->next = *freeme; + *freeme = fp; + p = *output_data = (char *)&fp->alignment; + } + memset((void *)p, 0, (size_t)datasize); + return _cffi_convert_array_from_object(p, ctptr, arg); +} + +#ifdef __GNUC__ + __attribute__((unused)) +#endif +static void _cffi_free_array_arguments(struct _cffi_freeme_s *freeme) +{ + do { + void *p = (void *)freeme; + freeme = freeme->next; + PyObject_Free(p); + } while (freeme != NULL); +} + +static int _cffi_init(void) +{ + PyObject *module, *c_api_object = NULL; + + module = PyImport_ImportModule("_cffi_backend"); + if (module == NULL) + goto failure; + + c_api_object = PyObject_GetAttrString(module, "_C_API"); + if (c_api_object == NULL) + goto failure; + if (!PyCapsule_CheckExact(c_api_object)) { + PyErr_SetNone(PyExc_ImportError); + goto failure; + } + memcpy(_cffi_exports, PyCapsule_GetPointer(c_api_object, "cffi"), + _CFFI_NUM_EXPORTS * sizeof(void *)); + + Py_DECREF(module); + Py_DECREF(c_api_object); + return 0; + + failure: + Py_XDECREF(module); + Py_XDECREF(c_api_object); + return -1; +} + +#define _cffi_type(num) ((CTypeDescrObject *)PyList_GET_ITEM(_cffi_types, num)) + +/**********/ +''' diff --git a/code/.venv/lib/python3.12/site-packages/cffi/vengine_gen.py b/code/.venv/lib/python3.12/site-packages/cffi/vengine_gen.py new file mode 100644 index 0000000..bffc821 --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/cffi/vengine_gen.py @@ -0,0 +1,679 @@ +# +# DEPRECATED: implementation for ffi.verify() +# +import sys, os +import types + +from . import model +from .error import VerificationError + + +class VGenericEngine(object): + _class_key = 'g' + _gen_python_module = False + + def __init__(self, verifier): + self.verifier = verifier + self.ffi = verifier.ffi + self.export_symbols = [] + self._struct_pending_verification = {} + + def patch_extension_kwds(self, kwds): + # add 'export_symbols' to the dictionary. Note that we add the + # list before filling it. When we fill it, it will thus also show + # up in kwds['export_symbols']. + kwds.setdefault('export_symbols', self.export_symbols) + + def find_module(self, module_name, path, so_suffixes): + for so_suffix in so_suffixes: + basename = module_name + so_suffix + if path is None: + path = sys.path + for dirname in path: + filename = os.path.join(dirname, basename) + if os.path.isfile(filename): + return filename + + def collect_types(self): + pass # not needed in the generic engine + + def _prnt(self, what=''): + self._f.write(what + '\n') + + def write_source_to_f(self): + prnt = self._prnt + # first paste some standard set of lines that are mostly '#include' + prnt(cffimod_header) + # then paste the C source given by the user, verbatim. + prnt(self.verifier.preamble) + # + # call generate_gen_xxx_decl(), for every xxx found from + # ffi._parser._declarations. This generates all the functions. + self._generate('decl') + # + # on Windows, distutils insists on putting init_cffi_xyz in + # 'export_symbols', so instead of fighting it, just give up and + # give it one + if sys.platform == 'win32': + if sys.version_info >= (3,): + prefix = 'PyInit_' + else: + prefix = 'init' + modname = self.verifier.get_module_name() + prnt("void %s%s(void) { }\n" % (prefix, modname)) + + def load_library(self, flags=0): + # import it with the CFFI backend + backend = self.ffi._backend + # needs to make a path that contains '/', on Posix + filename = os.path.join(os.curdir, self.verifier.modulefilename) + module = backend.load_library(filename, flags) + # + # call loading_gen_struct() to get the struct layout inferred by + # the C compiler + self._load(module, 'loading') + + # build the FFILibrary class and instance, this is a module subclass + # because modules are expected to have usually-constant-attributes and + # in PyPy this means the JIT is able to treat attributes as constant, + # which we want. + class FFILibrary(types.ModuleType): + _cffi_generic_module = module + _cffi_ffi = self.ffi + _cffi_dir = [] + def __dir__(self): + return FFILibrary._cffi_dir + library = FFILibrary("") + # + # finally, call the loaded_gen_xxx() functions. This will set + # up the 'library' object. + self._load(module, 'loaded', library=library) + return library + + def _get_declarations(self): + lst = [(key, tp) for (key, (tp, qual)) in + self.ffi._parser._declarations.items()] + lst.sort() + return lst + + def _generate(self, step_name): + for name, tp in self._get_declarations(): + kind, realname = name.split(' ', 1) + try: + method = getattr(self, '_generate_gen_%s_%s' % (kind, + step_name)) + except AttributeError: + raise VerificationError( + "not implemented in verify(): %r" % name) + try: + method(tp, realname) + except Exception as e: + model.attach_exception_info(e, name) + raise + + def _load(self, module, step_name, **kwds): + for name, tp in self._get_declarations(): + kind, realname = name.split(' ', 1) + method = getattr(self, '_%s_gen_%s' % (step_name, kind)) + try: + method(tp, realname, module, **kwds) + except Exception as e: + model.attach_exception_info(e, name) + raise + + def _generate_nothing(self, tp, name): + pass + + def _loaded_noop(self, tp, name, module, **kwds): + pass + + # ---------- + # typedefs: generates no code so far + + _generate_gen_typedef_decl = _generate_nothing + _loading_gen_typedef = _loaded_noop + _loaded_gen_typedef = _loaded_noop + + # ---------- + # function declarations + + def _generate_gen_function_decl(self, tp, name): + assert isinstance(tp, model.FunctionPtrType) + if tp.ellipsis: + # cannot support vararg functions better than this: check for its + # exact type (including the fixed arguments), and build it as a + # constant function pointer (no _cffi_f_%s wrapper) + self._generate_gen_const(False, name, tp) + return + prnt = self._prnt + numargs = len(tp.args) + argnames = [] + for i, type in enumerate(tp.args): + indirection = '' + if isinstance(type, model.StructOrUnion): + indirection = '*' + argnames.append('%sx%d' % (indirection, i)) + context = 'argument of %s' % name + arglist = [type.get_c_name(' %s' % arg, context) + for type, arg in zip(tp.args, argnames)] + tpresult = tp.result + if isinstance(tpresult, model.StructOrUnion): + arglist.insert(0, tpresult.get_c_name(' *r', context)) + tpresult = model.void_type + arglist = ', '.join(arglist) or 'void' + wrappername = '_cffi_f_%s' % name + self.export_symbols.append(wrappername) + if tp.abi: + abi = tp.abi + ' ' + else: + abi = '' + funcdecl = ' %s%s(%s)' % (abi, wrappername, arglist) + context = 'result of %s' % name + prnt(tpresult.get_c_name(funcdecl, context)) + prnt('{') + # + if isinstance(tp.result, model.StructOrUnion): + result_code = '*r = ' + elif not isinstance(tp.result, model.VoidType): + result_code = 'return ' + else: + result_code = '' + prnt(' %s%s(%s);' % (result_code, name, ', '.join(argnames))) + prnt('}') + prnt() + + _loading_gen_function = _loaded_noop + + def _loaded_gen_function(self, tp, name, module, library): + assert isinstance(tp, model.FunctionPtrType) + if tp.ellipsis: + newfunction = self._load_constant(False, tp, name, module) + else: + indirections = [] + base_tp = tp + if (any(isinstance(typ, model.StructOrUnion) for typ in tp.args) + or isinstance(tp.result, model.StructOrUnion)): + indirect_args = [] + for i, typ in enumerate(tp.args): + if isinstance(typ, model.StructOrUnion): + typ = model.PointerType(typ) + indirections.append((i, typ)) + indirect_args.append(typ) + indirect_result = tp.result + if isinstance(indirect_result, model.StructOrUnion): + if indirect_result.fldtypes is None: + raise TypeError("'%s' is used as result type, " + "but is opaque" % ( + indirect_result._get_c_name(),)) + indirect_result = model.PointerType(indirect_result) + indirect_args.insert(0, indirect_result) + indirections.insert(0, ("result", indirect_result)) + indirect_result = model.void_type + tp = model.FunctionPtrType(tuple(indirect_args), + indirect_result, tp.ellipsis) + BFunc = self.ffi._get_cached_btype(tp) + wrappername = '_cffi_f_%s' % name + newfunction = module.load_function(BFunc, wrappername) + for i, typ in indirections: + newfunction = self._make_struct_wrapper(newfunction, i, typ, + base_tp) + setattr(library, name, newfunction) + type(library)._cffi_dir.append(name) + + def _make_struct_wrapper(self, oldfunc, i, tp, base_tp): + backend = self.ffi._backend + BType = self.ffi._get_cached_btype(tp) + if i == "result": + ffi = self.ffi + def newfunc(*args): + res = ffi.new(BType) + oldfunc(res, *args) + return res[0] + else: + def newfunc(*args): + args = args[:i] + (backend.newp(BType, args[i]),) + args[i+1:] + return oldfunc(*args) + newfunc._cffi_base_type = base_tp + return newfunc + + # ---------- + # named structs + + def _generate_gen_struct_decl(self, tp, name): + assert name == tp.name + self._generate_struct_or_union_decl(tp, 'struct', name) + + def _loading_gen_struct(self, tp, name, module): + self._loading_struct_or_union(tp, 'struct', name, module) + + def _loaded_gen_struct(self, tp, name, module, **kwds): + self._loaded_struct_or_union(tp) + + def _generate_gen_union_decl(self, tp, name): + assert name == tp.name + self._generate_struct_or_union_decl(tp, 'union', name) + + def _loading_gen_union(self, tp, name, module): + self._loading_struct_or_union(tp, 'union', name, module) + + def _loaded_gen_union(self, tp, name, module, **kwds): + self._loaded_struct_or_union(tp) + + def _generate_struct_or_union_decl(self, tp, prefix, name): + if tp.fldnames is None: + return # nothing to do with opaque structs + checkfuncname = '_cffi_check_%s_%s' % (prefix, name) + layoutfuncname = '_cffi_layout_%s_%s' % (prefix, name) + cname = ('%s %s' % (prefix, name)).strip() + # + prnt = self._prnt + prnt('static void %s(%s *p)' % (checkfuncname, cname)) + prnt('{') + prnt(' /* only to generate compile-time warnings or errors */') + prnt(' (void)p;') + for fname, ftype, fbitsize, fqual in tp.enumfields(): + if (isinstance(ftype, model.PrimitiveType) + and ftype.is_integer_type()) or fbitsize >= 0: + # accept all integers, but complain on float or double + prnt(' (void)((p->%s) << 1);' % fname) + else: + # only accept exactly the type declared. + try: + prnt(' { %s = &p->%s; (void)tmp; }' % ( + ftype.get_c_name('*tmp', 'field %r'%fname, quals=fqual), + fname)) + except VerificationError as e: + prnt(' /* %s */' % str(e)) # cannot verify it, ignore + prnt('}') + self.export_symbols.append(layoutfuncname) + prnt('intptr_t %s(intptr_t i)' % (layoutfuncname,)) + prnt('{') + prnt(' struct _cffi_aligncheck { char x; %s y; };' % cname) + prnt(' static intptr_t nums[] = {') + prnt(' sizeof(%s),' % cname) + prnt(' offsetof(struct _cffi_aligncheck, y),') + for fname, ftype, fbitsize, fqual in tp.enumfields(): + if fbitsize >= 0: + continue # xxx ignore fbitsize for now + prnt(' offsetof(%s, %s),' % (cname, fname)) + if isinstance(ftype, model.ArrayType) and ftype.length is None: + prnt(' 0, /* %s */' % ftype._get_c_name()) + else: + prnt(' sizeof(((%s *)0)->%s),' % (cname, fname)) + prnt(' -1') + prnt(' };') + prnt(' return nums[i];') + prnt(' /* the next line is not executed, but compiled */') + prnt(' %s(0);' % (checkfuncname,)) + prnt('}') + prnt() + + def _loading_struct_or_union(self, tp, prefix, name, module): + if tp.fldnames is None: + return # nothing to do with opaque structs + layoutfuncname = '_cffi_layout_%s_%s' % (prefix, name) + # + BFunc = self.ffi._typeof_locked("intptr_t(*)(intptr_t)")[0] + function = module.load_function(BFunc, layoutfuncname) + layout = [] + num = 0 + while True: + x = function(num) + if x < 0: break + layout.append(x) + num += 1 + if isinstance(tp, model.StructOrUnion) and tp.partial: + # use the function()'s sizes and offsets to guide the + # layout of the struct + totalsize = layout[0] + totalalignment = layout[1] + fieldofs = layout[2::2] + fieldsize = layout[3::2] + tp.force_flatten() + assert len(fieldofs) == len(fieldsize) == len(tp.fldnames) + tp.fixedlayout = fieldofs, fieldsize, totalsize, totalalignment + else: + cname = ('%s %s' % (prefix, name)).strip() + self._struct_pending_verification[tp] = layout, cname + + def _loaded_struct_or_union(self, tp): + if tp.fldnames is None: + return # nothing to do with opaque structs + self.ffi._get_cached_btype(tp) # force 'fixedlayout' to be considered + + if tp in self._struct_pending_verification: + # check that the layout sizes and offsets match the real ones + def check(realvalue, expectedvalue, msg): + if realvalue != expectedvalue: + raise VerificationError( + "%s (we have %d, but C compiler says %d)" + % (msg, expectedvalue, realvalue)) + ffi = self.ffi + BStruct = ffi._get_cached_btype(tp) + layout, cname = self._struct_pending_verification.pop(tp) + check(layout[0], ffi.sizeof(BStruct), "wrong total size") + check(layout[1], ffi.alignof(BStruct), "wrong total alignment") + i = 2 + for fname, ftype, fbitsize, fqual in tp.enumfields(): + if fbitsize >= 0: + continue # xxx ignore fbitsize for now + check(layout[i], ffi.offsetof(BStruct, fname), + "wrong offset for field %r" % (fname,)) + if layout[i+1] != 0: + BField = ffi._get_cached_btype(ftype) + check(layout[i+1], ffi.sizeof(BField), + "wrong size for field %r" % (fname,)) + i += 2 + assert i == len(layout) + + # ---------- + # 'anonymous' declarations. These are produced for anonymous structs + # or unions; the 'name' is obtained by a typedef. + + def _generate_gen_anonymous_decl(self, tp, name): + if isinstance(tp, model.EnumType): + self._generate_gen_enum_decl(tp, name, '') + else: + self._generate_struct_or_union_decl(tp, '', name) + + def _loading_gen_anonymous(self, tp, name, module): + if isinstance(tp, model.EnumType): + self._loading_gen_enum(tp, name, module, '') + else: + self._loading_struct_or_union(tp, '', name, module) + + def _loaded_gen_anonymous(self, tp, name, module, **kwds): + if isinstance(tp, model.EnumType): + self._loaded_gen_enum(tp, name, module, **kwds) + else: + self._loaded_struct_or_union(tp) + + # ---------- + # constants, likely declared with '#define' + + def _generate_gen_const(self, is_int, name, tp=None, category='const', + check_value=None): + prnt = self._prnt + funcname = '_cffi_%s_%s' % (category, name) + self.export_symbols.append(funcname) + if check_value is not None: + assert is_int + assert category == 'const' + prnt('int %s(char *out_error)' % funcname) + prnt('{') + self._check_int_constant_value(name, check_value) + prnt(' return 0;') + prnt('}') + elif is_int: + assert category == 'const' + prnt('int %s(long long *out_value)' % funcname) + prnt('{') + prnt(' *out_value = (long long)(%s);' % (name,)) + prnt(' return (%s) <= 0;' % (name,)) + prnt('}') + else: + assert tp is not None + assert check_value is None + if category == 'var': + ampersand = '&' + else: + ampersand = '' + extra = '' + if category == 'const' and isinstance(tp, model.StructOrUnion): + extra = 'const *' + ampersand = '&' + prnt(tp.get_c_name(' %s%s(void)' % (extra, funcname), name)) + prnt('{') + prnt(' return (%s%s);' % (ampersand, name)) + prnt('}') + prnt() + + def _generate_gen_constant_decl(self, tp, name): + is_int = isinstance(tp, model.PrimitiveType) and tp.is_integer_type() + self._generate_gen_const(is_int, name, tp) + + _loading_gen_constant = _loaded_noop + + def _load_constant(self, is_int, tp, name, module, check_value=None): + funcname = '_cffi_const_%s' % name + if check_value is not None: + assert is_int + self._load_known_int_constant(module, funcname) + value = check_value + elif is_int: + BType = self.ffi._typeof_locked("long long*")[0] + BFunc = self.ffi._typeof_locked("int(*)(long long*)")[0] + function = module.load_function(BFunc, funcname) + p = self.ffi.new(BType) + negative = function(p) + value = int(p[0]) + if value < 0 and not negative: + BLongLong = self.ffi._typeof_locked("long long")[0] + value += (1 << (8*self.ffi.sizeof(BLongLong))) + else: + assert check_value is None + fntypeextra = '(*)(void)' + if isinstance(tp, model.StructOrUnion): + fntypeextra = '*' + fntypeextra + BFunc = self.ffi._typeof_locked(tp.get_c_name(fntypeextra, name))[0] + function = module.load_function(BFunc, funcname) + value = function() + if isinstance(tp, model.StructOrUnion): + value = value[0] + return value + + def _loaded_gen_constant(self, tp, name, module, library): + is_int = isinstance(tp, model.PrimitiveType) and tp.is_integer_type() + value = self._load_constant(is_int, tp, name, module) + setattr(library, name, value) + type(library)._cffi_dir.append(name) + + # ---------- + # enums + + def _check_int_constant_value(self, name, value): + prnt = self._prnt + if value <= 0: + prnt(' if ((%s) > 0 || (long)(%s) != %dL) {' % ( + name, name, value)) + else: + prnt(' if ((%s) <= 0 || (unsigned long)(%s) != %dUL) {' % ( + name, name, value)) + prnt(' char buf[64];') + prnt(' if ((%s) <= 0)' % name) + prnt(' sprintf(buf, "%%ld", (long)(%s));' % name) + prnt(' else') + prnt(' sprintf(buf, "%%lu", (unsigned long)(%s));' % + name) + prnt(' sprintf(out_error, "%s has the real value %s, not %s",') + prnt(' "%s", buf, "%d");' % (name[:100], value)) + prnt(' return -1;') + prnt(' }') + + def _load_known_int_constant(self, module, funcname): + BType = self.ffi._typeof_locked("char[]")[0] + BFunc = self.ffi._typeof_locked("int(*)(char*)")[0] + function = module.load_function(BFunc, funcname) + p = self.ffi.new(BType, 256) + if function(p) < 0: + error = self.ffi.string(p) + if sys.version_info >= (3,): + error = str(error, 'utf-8') + raise VerificationError(error) + + def _enum_funcname(self, prefix, name): + # "$enum_$1" => "___D_enum____D_1" + name = name.replace('$', '___D_') + return '_cffi_e_%s_%s' % (prefix, name) + + def _generate_gen_enum_decl(self, tp, name, prefix='enum'): + if tp.partial: + for enumerator in tp.enumerators: + self._generate_gen_const(True, enumerator) + return + # + funcname = self._enum_funcname(prefix, name) + self.export_symbols.append(funcname) + prnt = self._prnt + prnt('int %s(char *out_error)' % funcname) + prnt('{') + for enumerator, enumvalue in zip(tp.enumerators, tp.enumvalues): + self._check_int_constant_value(enumerator, enumvalue) + prnt(' return 0;') + prnt('}') + prnt() + + def _loading_gen_enum(self, tp, name, module, prefix='enum'): + if tp.partial: + enumvalues = [self._load_constant(True, tp, enumerator, module) + for enumerator in tp.enumerators] + tp.enumvalues = tuple(enumvalues) + tp.partial_resolved = True + else: + funcname = self._enum_funcname(prefix, name) + self._load_known_int_constant(module, funcname) + + def _loaded_gen_enum(self, tp, name, module, library): + for enumerator, enumvalue in zip(tp.enumerators, tp.enumvalues): + setattr(library, enumerator, enumvalue) + type(library)._cffi_dir.append(enumerator) + + # ---------- + # macros: for now only for integers + + def _generate_gen_macro_decl(self, tp, name): + if tp == '...': + check_value = None + else: + check_value = tp # an integer + self._generate_gen_const(True, name, check_value=check_value) + + _loading_gen_macro = _loaded_noop + + def _loaded_gen_macro(self, tp, name, module, library): + if tp == '...': + check_value = None + else: + check_value = tp # an integer + value = self._load_constant(True, tp, name, module, + check_value=check_value) + setattr(library, name, value) + type(library)._cffi_dir.append(name) + + # ---------- + # global variables + + def _generate_gen_variable_decl(self, tp, name): + if isinstance(tp, model.ArrayType): + if tp.length_is_unknown(): + prnt = self._prnt + funcname = '_cffi_sizeof_%s' % (name,) + self.export_symbols.append(funcname) + prnt("size_t %s(void)" % funcname) + prnt("{") + prnt(" return sizeof(%s);" % (name,)) + prnt("}") + tp_ptr = model.PointerType(tp.item) + self._generate_gen_const(False, name, tp_ptr) + else: + tp_ptr = model.PointerType(tp) + self._generate_gen_const(False, name, tp_ptr, category='var') + + _loading_gen_variable = _loaded_noop + + def _loaded_gen_variable(self, tp, name, module, library): + if isinstance(tp, model.ArrayType): # int a[5] is "constant" in the + # sense that "a=..." is forbidden + if tp.length_is_unknown(): + funcname = '_cffi_sizeof_%s' % (name,) + BFunc = self.ffi._typeof_locked('size_t(*)(void)')[0] + function = module.load_function(BFunc, funcname) + size = function() + BItemType = self.ffi._get_cached_btype(tp.item) + length, rest = divmod(size, self.ffi.sizeof(BItemType)) + if rest != 0: + raise VerificationError( + "bad size: %r does not seem to be an array of %s" % + (name, tp.item)) + tp = tp.resolve_length(length) + tp_ptr = model.PointerType(tp.item) + value = self._load_constant(False, tp_ptr, name, module) + # 'value' is a which we have to replace with + # a if the N is actually known + if tp.length is not None: + BArray = self.ffi._get_cached_btype(tp) + value = self.ffi.cast(BArray, value) + setattr(library, name, value) + type(library)._cffi_dir.append(name) + return + # remove ptr= from the library instance, and replace + # it by a property on the class, which reads/writes into ptr[0]. + funcname = '_cffi_var_%s' % name + BFunc = self.ffi._typeof_locked(tp.get_c_name('*(*)(void)', name))[0] + function = module.load_function(BFunc, funcname) + ptr = function() + def getter(library): + return ptr[0] + def setter(library, value): + ptr[0] = value + setattr(type(library), name, property(getter, setter)) + type(library)._cffi_dir.append(name) + +cffimod_header = r''' +#include +#include +#include +#include +#include /* XXX for ssize_t on some platforms */ + +/* this block of #ifs should be kept exactly identical between + c/_cffi_backend.c, cffi/vengine_cpy.py, cffi/vengine_gen.py + and cffi/_cffi_include.h */ +#if defined(_MSC_VER) +# include /* for alloca() */ +# if _MSC_VER < 1600 /* MSVC < 2010 */ + typedef __int8 int8_t; + typedef __int16 int16_t; + typedef __int32 int32_t; + typedef __int64 int64_t; + typedef unsigned __int8 uint8_t; + typedef unsigned __int16 uint16_t; + typedef unsigned __int32 uint32_t; + typedef unsigned __int64 uint64_t; + typedef __int8 int_least8_t; + typedef __int16 int_least16_t; + typedef __int32 int_least32_t; + typedef __int64 int_least64_t; + typedef unsigned __int8 uint_least8_t; + typedef unsigned __int16 uint_least16_t; + typedef unsigned __int32 uint_least32_t; + typedef unsigned __int64 uint_least64_t; + typedef __int8 int_fast8_t; + typedef __int16 int_fast16_t; + typedef __int32 int_fast32_t; + typedef __int64 int_fast64_t; + typedef unsigned __int8 uint_fast8_t; + typedef unsigned __int16 uint_fast16_t; + typedef unsigned __int32 uint_fast32_t; + typedef unsigned __int64 uint_fast64_t; + typedef __int64 intmax_t; + typedef unsigned __int64 uintmax_t; +# else +# include +# endif +# if _MSC_VER < 1800 /* MSVC < 2013 */ +# ifndef __cplusplus + typedef unsigned char _Bool; +# endif +# endif +# define _cffi_float_complex_t _Fcomplex /* include for it */ +# define _cffi_double_complex_t _Dcomplex /* include for it */ +#else +# include +# if (defined (__SVR4) && defined (__sun)) || defined(_AIX) || defined(__hpux) +# include +# endif +# define _cffi_float_complex_t float _Complex +# define _cffi_double_complex_t double _Complex +#endif +''' diff --git a/code/.venv/lib/python3.12/site-packages/cffi/verifier.py b/code/.venv/lib/python3.12/site-packages/cffi/verifier.py new file mode 100644 index 0000000..e392a2b --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/cffi/verifier.py @@ -0,0 +1,306 @@ +# +# DEPRECATED: implementation for ffi.verify() +# +import sys, os, binascii, shutil, io +from . import __version_verifier_modules__ +from . import ffiplatform +from .error import VerificationError + +if sys.version_info >= (3, 3): + import importlib.machinery + def _extension_suffixes(): + return importlib.machinery.EXTENSION_SUFFIXES[:] +else: + import imp + def _extension_suffixes(): + return [suffix for suffix, _, type in imp.get_suffixes() + if type == imp.C_EXTENSION] + + +if sys.version_info >= (3,): + NativeIO = io.StringIO +else: + class NativeIO(io.BytesIO): + def write(self, s): + if isinstance(s, unicode): + s = s.encode('ascii') + super(NativeIO, self).write(s) + + +class Verifier(object): + + def __init__(self, ffi, preamble, tmpdir=None, modulename=None, + ext_package=None, tag='', force_generic_engine=False, + source_extension='.c', flags=None, relative_to=None, **kwds): + if ffi._parser._uses_new_feature: + raise VerificationError( + "feature not supported with ffi.verify(), but only " + "with ffi.set_source(): %s" % (ffi._parser._uses_new_feature,)) + self.ffi = ffi + self.preamble = preamble + if not modulename: + flattened_kwds = ffiplatform.flatten(kwds) + vengine_class = _locate_engine_class(ffi, force_generic_engine) + self._vengine = vengine_class(self) + self._vengine.patch_extension_kwds(kwds) + self.flags = flags + self.kwds = self.make_relative_to(kwds, relative_to) + # + if modulename: + if tag: + raise TypeError("can't specify both 'modulename' and 'tag'") + else: + key = '\x00'.join(['%d.%d' % sys.version_info[:2], + __version_verifier_modules__, + preamble, flattened_kwds] + + ffi._cdefsources) + if sys.version_info >= (3,): + key = key.encode('utf-8') + k1 = hex(binascii.crc32(key[0::2]) & 0xffffffff) + k1 = k1.lstrip('0x').rstrip('L') + k2 = hex(binascii.crc32(key[1::2]) & 0xffffffff) + k2 = k2.lstrip('0').rstrip('L') + modulename = '_cffi_%s_%s%s%s' % (tag, self._vengine._class_key, + k1, k2) + suffix = _get_so_suffixes()[0] + self.tmpdir = tmpdir or _caller_dir_pycache() + self.sourcefilename = os.path.join(self.tmpdir, modulename + source_extension) + self.modulefilename = os.path.join(self.tmpdir, modulename + suffix) + self.ext_package = ext_package + self._has_source = False + self._has_module = False + + def write_source(self, file=None): + """Write the C source code. It is produced in 'self.sourcefilename', + which can be tweaked beforehand.""" + with self.ffi._lock: + if self._has_source and file is None: + raise VerificationError( + "source code already written") + self._write_source(file) + + def compile_module(self): + """Write the C source code (if not done already) and compile it. + This produces a dynamic link library in 'self.modulefilename'.""" + with self.ffi._lock: + if self._has_module: + raise VerificationError("module already compiled") + if not self._has_source: + self._write_source() + self._compile_module() + + def load_library(self): + """Get a C module from this Verifier instance. + Returns an instance of a FFILibrary class that behaves like the + objects returned by ffi.dlopen(), but that delegates all + operations to the C module. If necessary, the C code is written + and compiled first. + """ + with self.ffi._lock: + if not self._has_module: + self._locate_module() + if not self._has_module: + if not self._has_source: + self._write_source() + self._compile_module() + return self._load_library() + + def get_module_name(self): + basename = os.path.basename(self.modulefilename) + # kill both the .so extension and the other .'s, as introduced + # by Python 3: 'basename.cpython-33m.so' + basename = basename.split('.', 1)[0] + # and the _d added in Python 2 debug builds --- but try to be + # conservative and not kill a legitimate _d + if basename.endswith('_d') and hasattr(sys, 'gettotalrefcount'): + basename = basename[:-2] + return basename + + def get_extension(self): + if not self._has_source: + with self.ffi._lock: + if not self._has_source: + self._write_source() + sourcename = ffiplatform.maybe_relative_path(self.sourcefilename) + modname = self.get_module_name() + return ffiplatform.get_extension(sourcename, modname, **self.kwds) + + def generates_python_module(self): + return self._vengine._gen_python_module + + def make_relative_to(self, kwds, relative_to): + if relative_to and os.path.dirname(relative_to): + dirname = os.path.dirname(relative_to) + kwds = kwds.copy() + for key in ffiplatform.LIST_OF_FILE_NAMES: + if key in kwds: + lst = kwds[key] + if not isinstance(lst, (list, tuple)): + raise TypeError("keyword '%s' should be a list or tuple" + % (key,)) + lst = [os.path.join(dirname, fn) for fn in lst] + kwds[key] = lst + return kwds + + # ---------- + + def _locate_module(self): + if not os.path.isfile(self.modulefilename): + if self.ext_package: + try: + pkg = __import__(self.ext_package, None, None, ['__doc__']) + except ImportError: + return # cannot import the package itself, give up + # (e.g. it might be called differently before installation) + path = pkg.__path__ + else: + path = None + filename = self._vengine.find_module(self.get_module_name(), path, + _get_so_suffixes()) + if filename is None: + return + self.modulefilename = filename + self._vengine.collect_types() + self._has_module = True + + def _write_source_to(self, file): + self._vengine._f = file + try: + self._vengine.write_source_to_f() + finally: + del self._vengine._f + + def _write_source(self, file=None): + if file is not None: + self._write_source_to(file) + else: + # Write our source file to an in memory file. + f = NativeIO() + self._write_source_to(f) + source_data = f.getvalue() + + # Determine if this matches the current file + if os.path.exists(self.sourcefilename): + with open(self.sourcefilename, "r") as fp: + needs_written = not (fp.read() == source_data) + else: + needs_written = True + + # Actually write the file out if it doesn't match + if needs_written: + _ensure_dir(self.sourcefilename) + with open(self.sourcefilename, "w") as fp: + fp.write(source_data) + + # Set this flag + self._has_source = True + + def _compile_module(self): + # compile this C source + tmpdir = os.path.dirname(self.sourcefilename) + outputfilename = ffiplatform.compile(tmpdir, self.get_extension()) + try: + same = ffiplatform.samefile(outputfilename, self.modulefilename) + except OSError: + same = False + if not same: + _ensure_dir(self.modulefilename) + shutil.move(outputfilename, self.modulefilename) + self._has_module = True + + def _load_library(self): + assert self._has_module + if self.flags is not None: + return self._vengine.load_library(self.flags) + else: + return self._vengine.load_library() + +# ____________________________________________________________ + +_FORCE_GENERIC_ENGINE = False # for tests + +def _locate_engine_class(ffi, force_generic_engine): + if _FORCE_GENERIC_ENGINE: + force_generic_engine = True + if not force_generic_engine: + if '__pypy__' in sys.builtin_module_names: + force_generic_engine = True + else: + try: + import _cffi_backend + except ImportError: + _cffi_backend = '?' + if ffi._backend is not _cffi_backend: + force_generic_engine = True + if force_generic_engine: + from . import vengine_gen + return vengine_gen.VGenericEngine + else: + from . import vengine_cpy + return vengine_cpy.VCPythonEngine + +# ____________________________________________________________ + +_TMPDIR = None + +def _caller_dir_pycache(): + if _TMPDIR: + return _TMPDIR + result = os.environ.get('CFFI_TMPDIR') + if result: + return result + filename = sys._getframe(2).f_code.co_filename + return os.path.abspath(os.path.join(os.path.dirname(filename), + '__pycache__')) + +def set_tmpdir(dirname): + """Set the temporary directory to use instead of __pycache__.""" + global _TMPDIR + _TMPDIR = dirname + +def cleanup_tmpdir(tmpdir=None, keep_so=False): + """Clean up the temporary directory by removing all files in it + called `_cffi_*.{c,so}` as well as the `build` subdirectory.""" + tmpdir = tmpdir or _caller_dir_pycache() + try: + filelist = os.listdir(tmpdir) + except OSError: + return + if keep_so: + suffix = '.c' # only remove .c files + else: + suffix = _get_so_suffixes()[0].lower() + for fn in filelist: + if fn.lower().startswith('_cffi_') and ( + fn.lower().endswith(suffix) or fn.lower().endswith('.c')): + try: + os.unlink(os.path.join(tmpdir, fn)) + except OSError: + pass + clean_dir = [os.path.join(tmpdir, 'build')] + for dir in clean_dir: + try: + for fn in os.listdir(dir): + fn = os.path.join(dir, fn) + if os.path.isdir(fn): + clean_dir.append(fn) + else: + os.unlink(fn) + except OSError: + pass + +def _get_so_suffixes(): + suffixes = _extension_suffixes() + if not suffixes: + # bah, no C_EXTENSION available. Occurs on pypy without cpyext + if sys.platform == 'win32': + suffixes = [".pyd"] + else: + suffixes = [".so"] + + return suffixes + +def _ensure_dir(filename): + dirname = os.path.dirname(filename) + if dirname and not os.path.isdir(dirname): + os.makedirs(dirname) diff --git a/code/.venv/lib/python3.12/site-packages/cryptography-43.0.0.dist-info/INSTALLER b/code/.venv/lib/python3.12/site-packages/cryptography-43.0.0.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/cryptography-43.0.0.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/code/.venv/lib/python3.12/site-packages/cryptography-43.0.0.dist-info/METADATA b/code/.venv/lib/python3.12/site-packages/cryptography-43.0.0.dist-info/METADATA new file mode 100644 index 0000000..aa1528c --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/cryptography-43.0.0.dist-info/METADATA @@ -0,0 +1,138 @@ +Metadata-Version: 2.3 +Name: cryptography +Version: 43.0.0 +Classifier: Development Status :: 5 - Production/Stable +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: Apache Software License +Classifier: License :: OSI Approved :: BSD License +Classifier: Natural Language :: English +Classifier: Operating System :: MacOS :: MacOS X +Classifier: Operating System :: POSIX +Classifier: Operating System :: POSIX :: BSD +Classifier: Operating System :: POSIX :: Linux +Classifier: Operating System :: Microsoft :: Windows +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3 :: Only +Classifier: Programming Language :: Python :: 3.7 +Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: 3.9 +Classifier: Programming Language :: Python :: 3.10 +Classifier: Programming Language :: Python :: 3.11 +Classifier: Programming Language :: Python :: 3.12 +Classifier: Programming Language :: Python :: Implementation :: CPython +Classifier: Programming Language :: Python :: Implementation :: PyPy +Classifier: Topic :: Security :: Cryptography +Requires-Dist: cffi >=1.12 ; platform_python_implementation != 'PyPy' +Requires-Dist: bcrypt >=3.1.5 ; extra == 'ssh' +Requires-Dist: nox ; extra == 'nox' +Requires-Dist: cryptography-vectors ==43.0.0 ; extra == 'test' +Requires-Dist: pytest >=6.2.0 ; extra == 'test' +Requires-Dist: pytest-benchmark ; extra == 'test' +Requires-Dist: pytest-cov ; extra == 'test' +Requires-Dist: pytest-xdist ; extra == 'test' +Requires-Dist: pretend ; extra == 'test' +Requires-Dist: certifi ; extra == 'test' +Requires-Dist: pytest-randomly ; extra == 'test-randomorder' +Requires-Dist: sphinx >=5.3.0 ; extra == 'docs' +Requires-Dist: sphinx-rtd-theme >=1.1.1 ; extra == 'docs' +Requires-Dist: pyenchant >=1.6.11 ; extra == 'docstest' +Requires-Dist: readme-renderer ; extra == 'docstest' +Requires-Dist: sphinxcontrib-spelling >=4.0.1 ; extra == 'docstest' +Requires-Dist: build ; extra == 'sdist' +Requires-Dist: ruff ; extra == 'pep8test' +Requires-Dist: mypy ; extra == 'pep8test' +Requires-Dist: check-sdist ; extra == 'pep8test' +Requires-Dist: click ; extra == 'pep8test' +Provides-Extra: ssh +Provides-Extra: nox +Provides-Extra: test +Provides-Extra: test-randomorder +Provides-Extra: docs +Provides-Extra: docstest +Provides-Extra: sdist +Provides-Extra: pep8test +License-File: LICENSE +License-File: LICENSE.APACHE +License-File: LICENSE.BSD +Summary: cryptography is a package which provides cryptographic recipes and primitives to Python developers. +Author: The cryptography developers +Author-email: The Python Cryptographic Authority and individual contributors +License: Apache-2.0 OR BSD-3-Clause +Requires-Python: >=3.7 +Description-Content-Type: text/x-rst; charset=UTF-8 +Project-URL: homepage, https://github.com/pyca/cryptography +Project-URL: documentation, https://cryptography.io/ +Project-URL: source, https://github.com/pyca/cryptography/ +Project-URL: issues, https://github.com/pyca/cryptography/issues +Project-URL: changelog, https://cryptography.io/en/latest/changelog/ + +pyca/cryptography +================= + +.. image:: https://img.shields.io/pypi/v/cryptography.svg + :target: https://pypi.org/project/cryptography/ + :alt: Latest Version + +.. image:: https://readthedocs.org/projects/cryptography/badge/?version=latest + :target: https://cryptography.io + :alt: Latest Docs + +.. image:: https://github.com/pyca/cryptography/workflows/CI/badge.svg?branch=main + :target: https://github.com/pyca/cryptography/actions?query=workflow%3ACI+branch%3Amain + + +``cryptography`` is a package which provides cryptographic recipes and +primitives to Python developers. Our goal is for it to be your "cryptographic +standard library". It supports Python 3.7+ and PyPy3 7.3.11+. + +``cryptography`` includes both high level recipes and low level interfaces to +common cryptographic algorithms such as symmetric ciphers, message digests, and +key derivation functions. For example, to encrypt something with +``cryptography``'s high level symmetric encryption recipe: + +.. code-block:: pycon + + >>> from cryptography.fernet import Fernet + >>> # Put this somewhere safe! + >>> key = Fernet.generate_key() + >>> f = Fernet(key) + >>> token = f.encrypt(b"A really secret message. Not for prying eyes.") + >>> token + b'...' + >>> f.decrypt(token) + b'A really secret message. Not for prying eyes.' + +You can find more information in the `documentation`_. + +You can install ``cryptography`` with: + +.. code-block:: console + + $ pip install cryptography + +For full details see `the installation documentation`_. + +Discussion +~~~~~~~~~~ + +If you run into bugs, you can file them in our `issue tracker`_. + +We maintain a `cryptography-dev`_ mailing list for development discussion. + +You can also join ``#pyca`` on ``irc.libera.chat`` to ask questions or get +involved. + +Security +~~~~~~~~ + +Need to report a security issue? Please consult our `security reporting`_ +documentation. + + +.. _`documentation`: https://cryptography.io/ +.. _`the installation documentation`: https://cryptography.io/en/latest/installation/ +.. _`issue tracker`: https://github.com/pyca/cryptography/issues +.. _`cryptography-dev`: https://mail.python.org/mailman/listinfo/cryptography-dev +.. _`security reporting`: https://cryptography.io/en/latest/security/ + diff --git a/code/.venv/lib/python3.12/site-packages/cryptography-43.0.0.dist-info/RECORD b/code/.venv/lib/python3.12/site-packages/cryptography-43.0.0.dist-info/RECORD new file mode 100644 index 0000000..b3cab14 --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/cryptography-43.0.0.dist-info/RECORD @@ -0,0 +1,173 @@ +cryptography-43.0.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +cryptography-43.0.0.dist-info/METADATA,sha256=JPmDiHQjPeafnemuvZU1nkmUmFCNlitgXZAYYojX2MA,5440 +cryptography-43.0.0.dist-info/RECORD,, +cryptography-43.0.0.dist-info/WHEEL,sha256=5SNCVD9cb88a-xAIrDHIo1CvpgNriOYcNgb4b8rPcOw,107 +cryptography-43.0.0.dist-info/license_files/LICENSE,sha256=Pgx8CRqUi4JTO6mP18u0BDLW8amsv4X1ki0vmak65rs,197 +cryptography-43.0.0.dist-info/license_files/LICENSE.APACHE,sha256=qsc7MUj20dcRHbyjIJn2jSbGRMaBOuHk8F9leaomY_4,11360 +cryptography-43.0.0.dist-info/license_files/LICENSE.BSD,sha256=YCxMdILeZHndLpeTzaJ15eY9dz2s0eymiSMqtwCPtPs,1532 +cryptography/__about__.py,sha256=AuJuBuUXFu8XM-ndNcp4DzJNCld3qQyfRJFH_AgNI-0,445 +cryptography/__init__.py,sha256=mthuUrTd4FROCpUYrTIqhjz6s6T9djAZrV7nZ1oMm2o,364 +cryptography/__pycache__/__about__.cpython-312.pyc,, +cryptography/__pycache__/__init__.cpython-312.pyc,, +cryptography/__pycache__/exceptions.cpython-312.pyc,, +cryptography/__pycache__/fernet.cpython-312.pyc,, +cryptography/__pycache__/utils.cpython-312.pyc,, +cryptography/exceptions.py,sha256=835EWILc2fwxw-gyFMriciC2SqhViETB10LBSytnDIc,1087 +cryptography/fernet.py,sha256=aPj82w-Z_1GBXUtWRUsZdVbMwRo5Mbjj0wkA9wG4rkw,6696 +cryptography/hazmat/__init__.py,sha256=5IwrLWrVp0AjEr_4FdWG_V057NSJGY_W4egNNsuct0g,455 +cryptography/hazmat/__pycache__/__init__.cpython-312.pyc,, +cryptography/hazmat/__pycache__/_oid.cpython-312.pyc,, +cryptography/hazmat/_oid.py,sha256=e9yLmxtdQtuL94ztQv3SGtt_ea1Mx6aUwGftJsP6EXk,15201 +cryptography/hazmat/backends/__init__.py,sha256=O5jvKFQdZnXhKeqJ-HtulaEL9Ni7mr1mDzZY5kHlYhI,361 +cryptography/hazmat/backends/__pycache__/__init__.cpython-312.pyc,, +cryptography/hazmat/backends/openssl/__init__.py,sha256=p3jmJfnCag9iE5sdMrN6VvVEu55u46xaS_IjoI0SrmA,305 +cryptography/hazmat/backends/openssl/__pycache__/__init__.cpython-312.pyc,, +cryptography/hazmat/backends/openssl/__pycache__/backend.cpython-312.pyc,, +cryptography/hazmat/backends/openssl/backend.py,sha256=pUXUbugLwMm2Gls-h5U5fw2RvepaNjEvnao6CTmL1xQ,9648 +cryptography/hazmat/bindings/__init__.py,sha256=s9oKCQ2ycFdXoERdS1imafueSkBsL9kvbyfghaauZ9Y,180 +cryptography/hazmat/bindings/__pycache__/__init__.cpython-312.pyc,, +cryptography/hazmat/bindings/_rust.abi3.so,sha256=v61R48rrE-pS7kU41NHr1dR7TnlJd4alKQMpz9qT_6Y,10881144 +cryptography/hazmat/bindings/_rust/__init__.pyi,sha256=wb1OT76lG19vjq97_q2MM3qdJlQhyloXfVbKFDmRse4,737 +cryptography/hazmat/bindings/_rust/_openssl.pyi,sha256=mpNJLuYLbCVrd5i33FBTmWwL_55Dw7JPkSLlSX9Q7oI,230 +cryptography/hazmat/bindings/_rust/asn1.pyi,sha256=BrGjC8J6nwuS-r3EVcdXJB8ndotfY9mbQYOfpbPG0HA,354 +cryptography/hazmat/bindings/_rust/exceptions.pyi,sha256=exXr2xw_0pB1kk93cYbM3MohbzoUkjOms1ZMUi0uQZE,640 +cryptography/hazmat/bindings/_rust/ocsp.pyi,sha256=R-xJ-XmJZ1lOk-fWHHvRnP3QNTCFnKv-l3xlNWfLVt4,868 +cryptography/hazmat/bindings/_rust/openssl/__init__.pyi,sha256=Lvn250QMdPyeF-hoBF6rkQgHLBJxVauXCb8i8uYTomQ,1368 +cryptography/hazmat/bindings/_rust/openssl/aead.pyi,sha256=i0gA3jUQ4rkJXTGGZrq-AuY-VQLN31lyDeWuDZ0zJYw,2553 +cryptography/hazmat/bindings/_rust/openssl/ciphers.pyi,sha256=iK0ZhQ-WyCQbjaraaFgK6q4PpD-7Rf5RDHkFD3YEW_g,1301 +cryptography/hazmat/bindings/_rust/openssl/cmac.pyi,sha256=nPH0X57RYpsAkRowVpjQiHE566ThUTx7YXrsadmrmHk,564 +cryptography/hazmat/bindings/_rust/openssl/dh.pyi,sha256=Z3TC-G04-THtSdAOPLM1h2G7ml5bda1ElZUcn5wpuhk,1564 +cryptography/hazmat/bindings/_rust/openssl/dsa.pyi,sha256=qBtkgj2albt2qFcnZ9UDrhzoNhCVO7HTby5VSf1EXMI,1299 +cryptography/hazmat/bindings/_rust/openssl/ec.pyi,sha256=zJy0pRa5n-_p2dm45PxECB_-B6SVZyNKfjxFDpPqT38,1691 +cryptography/hazmat/bindings/_rust/openssl/ed25519.pyi,sha256=OJsrblS2nHptZctva-pAKFL5q8yPEAkhmjPZpJ6TA94,493 +cryptography/hazmat/bindings/_rust/openssl/ed448.pyi,sha256=SkPHK2HdbYN02TVQEUOgW3iTdiEY7HBE4DijpdkAzmk,475 +cryptography/hazmat/bindings/_rust/openssl/hashes.pyi,sha256=J8HoN0GdtPcjRAfNHr5Elva_nkmQfq63L75_z9dd8Uc,573 +cryptography/hazmat/bindings/_rust/openssl/hmac.pyi,sha256=ZmLJ73pmxcZFC1XosWEiXMRYtvJJor3ZLdCQOJu85Cw,662 +cryptography/hazmat/bindings/_rust/openssl/kdf.pyi,sha256=wPS5c7NLspM2632II0I4iH1RSxZvSRtBOVqmpyQATfk,544 +cryptography/hazmat/bindings/_rust/openssl/keys.pyi,sha256=JSrlGNaW49ZCZ1hcb-YJdS1EAbsMwRbVEcLL0P9OApA,872 +cryptography/hazmat/bindings/_rust/openssl/poly1305.pyi,sha256=9iogF7Q4i81IkOS-IMXp6HvxFF_3cNy_ucrAjVQnn14,540 +cryptography/hazmat/bindings/_rust/openssl/rsa.pyi,sha256=2OQCNSXkxgc-3uw1xiCCloIQTV6p9_kK79Yu0rhZgPc,1364 +cryptography/hazmat/bindings/_rust/openssl/x25519.pyi,sha256=2BKdbrddM_9SMUpdvHKGhb9MNjURCarPxccbUDzHeoA,484 +cryptography/hazmat/bindings/_rust/openssl/x448.pyi,sha256=AoRMWNvCJTiH5L-lkIkCdPlrPLUdJvvfXpIvf1GmxpM,466 +cryptography/hazmat/bindings/_rust/pkcs12.pyi,sha256=afhB_6M8xI1MIE5vxkaDF1jSxA48ib1--NiOxtf6boM,1394 +cryptography/hazmat/bindings/_rust/pkcs7.pyi,sha256=QCmuA0IgDr4iOecUOXgUUeh3BAjJx8ubjz__EnNbyGY,972 +cryptography/hazmat/bindings/_rust/test_support.pyi,sha256=Xo1Gd7bh9rU4HuIS4pm9UwCY6IS1gInvFwmhABLOVO4,936 +cryptography/hazmat/bindings/_rust/x509.pyi,sha256=WLrGmqmFss8dXKhlG_J9nVhoCcodR72xJdCoxEuBtjY,3551 +cryptography/hazmat/bindings/openssl/__init__.py,sha256=s9oKCQ2ycFdXoERdS1imafueSkBsL9kvbyfghaauZ9Y,180 +cryptography/hazmat/bindings/openssl/__pycache__/__init__.cpython-312.pyc,, +cryptography/hazmat/bindings/openssl/__pycache__/_conditional.cpython-312.pyc,, +cryptography/hazmat/bindings/openssl/__pycache__/binding.cpython-312.pyc,, +cryptography/hazmat/bindings/openssl/_conditional.py,sha256=dkGKGU-22uR2ZKeOOwaSxEJCGaafgUjb2romWcu03QE,5163 +cryptography/hazmat/bindings/openssl/binding.py,sha256=e1gnFAZBPrkJ3CsiZV-ug6kaPdNTAEROaUFiFrUh71M,4042 +cryptography/hazmat/decrepit/__init__.py,sha256=wHCbWfaefa-fk6THSw9th9fJUsStJo7245wfFBqmduA,216 +cryptography/hazmat/decrepit/__pycache__/__init__.cpython-312.pyc,, +cryptography/hazmat/decrepit/ciphers/__init__.py,sha256=wHCbWfaefa-fk6THSw9th9fJUsStJo7245wfFBqmduA,216 +cryptography/hazmat/decrepit/ciphers/__pycache__/__init__.cpython-312.pyc,, +cryptography/hazmat/decrepit/ciphers/__pycache__/algorithms.cpython-312.pyc,, +cryptography/hazmat/decrepit/ciphers/algorithms.py,sha256=HWA4PKDS2w4D2dQoRerpLRU7Kntt5vJeJC7j--AlZVU,2520 +cryptography/hazmat/primitives/__init__.py,sha256=s9oKCQ2ycFdXoERdS1imafueSkBsL9kvbyfghaauZ9Y,180 +cryptography/hazmat/primitives/__pycache__/__init__.cpython-312.pyc,, +cryptography/hazmat/primitives/__pycache__/_asymmetric.cpython-312.pyc,, +cryptography/hazmat/primitives/__pycache__/_cipheralgorithm.cpython-312.pyc,, +cryptography/hazmat/primitives/__pycache__/_serialization.cpython-312.pyc,, +cryptography/hazmat/primitives/__pycache__/cmac.cpython-312.pyc,, +cryptography/hazmat/primitives/__pycache__/constant_time.cpython-312.pyc,, +cryptography/hazmat/primitives/__pycache__/hashes.cpython-312.pyc,, +cryptography/hazmat/primitives/__pycache__/hmac.cpython-312.pyc,, +cryptography/hazmat/primitives/__pycache__/keywrap.cpython-312.pyc,, +cryptography/hazmat/primitives/__pycache__/padding.cpython-312.pyc,, +cryptography/hazmat/primitives/__pycache__/poly1305.cpython-312.pyc,, +cryptography/hazmat/primitives/_asymmetric.py,sha256=RhgcouUB6HTiFDBrR1LxqkMjpUxIiNvQ1r_zJjRG6qQ,532 +cryptography/hazmat/primitives/_cipheralgorithm.py,sha256=gKa0WrLz6K4fqhnGbfBYKDSxgLxsPU0uj_EK2UT47W4,1495 +cryptography/hazmat/primitives/_serialization.py,sha256=qrozc8fw2WZSbjk3DAlSl3ResxpauwJ74ZgGoUL-mj0,5142 +cryptography/hazmat/primitives/asymmetric/__init__.py,sha256=s9oKCQ2ycFdXoERdS1imafueSkBsL9kvbyfghaauZ9Y,180 +cryptography/hazmat/primitives/asymmetric/__pycache__/__init__.cpython-312.pyc,, +cryptography/hazmat/primitives/asymmetric/__pycache__/dh.cpython-312.pyc,, +cryptography/hazmat/primitives/asymmetric/__pycache__/dsa.cpython-312.pyc,, +cryptography/hazmat/primitives/asymmetric/__pycache__/ec.cpython-312.pyc,, +cryptography/hazmat/primitives/asymmetric/__pycache__/ed25519.cpython-312.pyc,, +cryptography/hazmat/primitives/asymmetric/__pycache__/ed448.cpython-312.pyc,, +cryptography/hazmat/primitives/asymmetric/__pycache__/padding.cpython-312.pyc,, +cryptography/hazmat/primitives/asymmetric/__pycache__/rsa.cpython-312.pyc,, +cryptography/hazmat/primitives/asymmetric/__pycache__/types.cpython-312.pyc,, +cryptography/hazmat/primitives/asymmetric/__pycache__/utils.cpython-312.pyc,, +cryptography/hazmat/primitives/asymmetric/__pycache__/x25519.cpython-312.pyc,, +cryptography/hazmat/primitives/asymmetric/__pycache__/x448.cpython-312.pyc,, +cryptography/hazmat/primitives/asymmetric/dh.py,sha256=OOCjMClH1Bf14Sy7jAdwzEeCxFPb8XUe2qePbExvXwc,3420 +cryptography/hazmat/primitives/asymmetric/dsa.py,sha256=xBwdf0pZOgvqjUKcO7Q0L3NxwalYj0SJDUqThemhSmI,3945 +cryptography/hazmat/primitives/asymmetric/ec.py,sha256=lwZmtAwi3PM8lsY1MsNaby_bVi--49OCxwE_1yqKC-A,10428 +cryptography/hazmat/primitives/asymmetric/ed25519.py,sha256=kl63fg7myuMjNTmMoVFeH6iVr0x5FkjNmggxIRTloJk,3423 +cryptography/hazmat/primitives/asymmetric/ed448.py,sha256=2UzEDzzfkPn83UFVFlMZfIMbAixxY09WmQyrwinWTn8,3456 +cryptography/hazmat/primitives/asymmetric/padding.py,sha256=eZcvUqVLbe3u48SunLdeniaPlV4-k6pwBl67OW4jSy8,2885 +cryptography/hazmat/primitives/asymmetric/rsa.py,sha256=nW_Ko7PID9UBJF10GVJOc_1L00ymFsfZDUJYtM5kfGQ,7637 +cryptography/hazmat/primitives/asymmetric/types.py,sha256=LnsOJym-wmPUJ7Knu_7bCNU3kIiELCd6krOaW_JU08I,2996 +cryptography/hazmat/primitives/asymmetric/utils.py,sha256=DPTs6T4F-UhwzFQTh-1fSEpQzazH2jf2xpIro3ItF4o,790 +cryptography/hazmat/primitives/asymmetric/x25519.py,sha256=VGYuRdIYuVBtizpFdNWd2bTrT10JRa1admQdBr08xz8,3341 +cryptography/hazmat/primitives/asymmetric/x448.py,sha256=GKKJBqYLr03VewMF18bXIM941aaWcZIQ4rC02GLLEmw,3374 +cryptography/hazmat/primitives/ciphers/__init__.py,sha256=eyEXmjk6_CZXaOPYDr7vAYGXr29QvzgWL2-4CSolLFs,680 +cryptography/hazmat/primitives/ciphers/__pycache__/__init__.cpython-312.pyc,, +cryptography/hazmat/primitives/ciphers/__pycache__/aead.cpython-312.pyc,, +cryptography/hazmat/primitives/ciphers/__pycache__/algorithms.cpython-312.pyc,, +cryptography/hazmat/primitives/ciphers/__pycache__/base.cpython-312.pyc,, +cryptography/hazmat/primitives/ciphers/__pycache__/modes.cpython-312.pyc,, +cryptography/hazmat/primitives/ciphers/aead.py,sha256=Fzlyx7w8KYQakzDp1zWgJnIr62zgZrgVh1u2h4exB54,634 +cryptography/hazmat/primitives/ciphers/algorithms.py,sha256=QvBMDmphRZfNmykij58L5eDkd_2NnCzIpJpyX2QwMxc,4223 +cryptography/hazmat/primitives/ciphers/base.py,sha256=tg-XNaKUyETBi7ounGDEL1_ICn-s4FF9LR7moV58blI,4211 +cryptography/hazmat/primitives/ciphers/modes.py,sha256=BFpxEGSaxoeZjrQ4sqpyPDvKClrqfDKIBv7kYtFURhE,8192 +cryptography/hazmat/primitives/cmac.py,sha256=sz_s6H_cYnOvx-VNWdIKhRhe3Ymp8z8J0D3CBqOX3gg,338 +cryptography/hazmat/primitives/constant_time.py,sha256=xdunWT0nf8OvKdcqUhhlFKayGp4_PgVJRU2W1wLSr_A,422 +cryptography/hazmat/primitives/hashes.py,sha256=EvDIJBhj83Z7f-oHbsA0TzZLFSDV_Yv8hQRdM4o8FD0,5091 +cryptography/hazmat/primitives/hmac.py,sha256=RpB3z9z5skirCQrm7zQbtnp9pLMnAjrlTUvKqF5aDDc,423 +cryptography/hazmat/primitives/kdf/__init__.py,sha256=4XibZnrYq4hh5xBjWiIXzaYW6FKx8hPbVaa_cB9zS64,750 +cryptography/hazmat/primitives/kdf/__pycache__/__init__.cpython-312.pyc,, +cryptography/hazmat/primitives/kdf/__pycache__/concatkdf.cpython-312.pyc,, +cryptography/hazmat/primitives/kdf/__pycache__/hkdf.cpython-312.pyc,, +cryptography/hazmat/primitives/kdf/__pycache__/kbkdf.cpython-312.pyc,, +cryptography/hazmat/primitives/kdf/__pycache__/pbkdf2.cpython-312.pyc,, +cryptography/hazmat/primitives/kdf/__pycache__/scrypt.cpython-312.pyc,, +cryptography/hazmat/primitives/kdf/__pycache__/x963kdf.cpython-312.pyc,, +cryptography/hazmat/primitives/kdf/concatkdf.py,sha256=bcn4NGXse-EsFl7nlU83e5ilop7TSHcX-CJJS107W80,3686 +cryptography/hazmat/primitives/kdf/hkdf.py,sha256=uhN5L87w4JvtAqQcPh_Ji2TPSc18IDThpaYJiHOWy3A,3015 +cryptography/hazmat/primitives/kdf/kbkdf.py,sha256=eSuLK1sATkamgCAit794jLr7sDNlu5X0USdcWhwJdmk,9146 +cryptography/hazmat/primitives/kdf/pbkdf2.py,sha256=Xj3YIeX30h2BUaoJAtOo1RMXV_em0-eCG0PU_0FHJzM,1950 +cryptography/hazmat/primitives/kdf/scrypt.py,sha256=4QONhjxA_ZtuQtQ7QV3FnbB8ftrFnM52B4HPfV7hFys,2354 +cryptography/hazmat/primitives/kdf/x963kdf.py,sha256=wCpWmwQjZ2vAu2rlk3R_PX0nINl8WGXYBmlyMOC5iPw,1992 +cryptography/hazmat/primitives/keywrap.py,sha256=XV4Pj2fqSeD-RqZVvY2cA3j5_7RwJSFygYuLfk2ujCo,5650 +cryptography/hazmat/primitives/padding.py,sha256=QUq0n-EAgEan9aQzuTsiJYGKbWiK1nSHkcYjDF1L1ok,5518 +cryptography/hazmat/primitives/poly1305.py,sha256=P5EPQV-RB_FJPahpg01u0Ts4S_PnAmsroxIGXbGeRRo,355 +cryptography/hazmat/primitives/serialization/__init__.py,sha256=jyNx_7NcOEbVRBY4nP9ks0IVXBafbcYnTK27vafPLW8,1653 +cryptography/hazmat/primitives/serialization/__pycache__/__init__.cpython-312.pyc,, +cryptography/hazmat/primitives/serialization/__pycache__/base.cpython-312.pyc,, +cryptography/hazmat/primitives/serialization/__pycache__/pkcs12.cpython-312.pyc,, +cryptography/hazmat/primitives/serialization/__pycache__/pkcs7.cpython-312.pyc,, +cryptography/hazmat/primitives/serialization/__pycache__/ssh.cpython-312.pyc,, +cryptography/hazmat/primitives/serialization/base.py,sha256=ikq5MJIwp_oUnjiaBco_PmQwOTYuGi-XkYUYHKy8Vo0,615 +cryptography/hazmat/primitives/serialization/pkcs12.py,sha256=7vVXbiP7qhhvKAHJT_M8-LBZdbpOwrpWRHWxNrNqzXE,4492 +cryptography/hazmat/primitives/serialization/pkcs7.py,sha256=CNzcsuDMyEFMe3EUii4NfJlQzmakB2hLlfRFYObnHRs,11141 +cryptography/hazmat/primitives/serialization/ssh.py,sha256=VKscMrVdYK5B9PQISjjdRMglRvqa_L3sDNm5vdjVHJY,51915 +cryptography/hazmat/primitives/twofactor/__init__.py,sha256=tmMZGB-g4IU1r7lIFqASU019zr0uPp_wEBYcwdDCKCA,258 +cryptography/hazmat/primitives/twofactor/__pycache__/__init__.cpython-312.pyc,, +cryptography/hazmat/primitives/twofactor/__pycache__/hotp.cpython-312.pyc,, +cryptography/hazmat/primitives/twofactor/__pycache__/totp.cpython-312.pyc,, +cryptography/hazmat/primitives/twofactor/hotp.py,sha256=l1YdRMIhfPIuHKkA66keBDHhNbnBAlh6-O44P-OHIK8,2976 +cryptography/hazmat/primitives/twofactor/totp.py,sha256=v0y0xKwtYrP83ypOo5Ofd441RJLOkaFfjmp554jo5F0,1450 +cryptography/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +cryptography/utils.py,sha256=Rp7ppg4XIBVVzNQ6XngGndwkICJoYp6FoFOOgTWLJ7g,3925 +cryptography/x509/__init__.py,sha256=uGdiViR7KFnWGoJFVUStt-e_ufomWc87RQBGAZ7dT-4,7980 +cryptography/x509/__pycache__/__init__.cpython-312.pyc,, +cryptography/x509/__pycache__/base.cpython-312.pyc,, +cryptography/x509/__pycache__/certificate_transparency.cpython-312.pyc,, +cryptography/x509/__pycache__/extensions.cpython-312.pyc,, +cryptography/x509/__pycache__/general_name.cpython-312.pyc,, +cryptography/x509/__pycache__/name.cpython-312.pyc,, +cryptography/x509/__pycache__/ocsp.cpython-312.pyc,, +cryptography/x509/__pycache__/oid.cpython-312.pyc,, +cryptography/x509/__pycache__/verification.cpython-312.pyc,, +cryptography/x509/base.py,sha256=3NbbUn9wPruhmoPO7Cl3trc3SrqV2OFIBBE0P2l05mg,37081 +cryptography/x509/certificate_transparency.py,sha256=6HvzAD0dlSQVxy6tnDhGj0-pisp1MaJ9bxQNRr92inI,2261 +cryptography/x509/extensions.py,sha256=R70KkJ_c5NQ6Kx7Rho0sGJ0Rh-bOuBHjVOFSQGRAFCs,67370 +cryptography/x509/general_name.py,sha256=sP_rV11Qlpsk4x3XXGJY_Mv0Q_s9dtjeLckHsjpLQoQ,7836 +cryptography/x509/name.py,sha256=MYCxCSTQTpzhjxFPZaANqJ9fGrhESH73vPkoay8HSWM,14830 +cryptography/x509/ocsp.py,sha256=P6A02msz5pe-IkUFpvxezHvnEHGvPdXiD3S0wsuf4-I,20003 +cryptography/x509/oid.py,sha256=X8EbhkRTLrGuv9vHZSGqPd9zpvRVsonU_joWAL5LLY8,885 +cryptography/x509/verification.py,sha256=alfx3VaTSb2bMz7_7s788oL90vzgHwBjVINssdz0Gv0,796 diff --git a/code/.venv/lib/python3.12/site-packages/cryptography-43.0.0.dist-info/WHEEL b/code/.venv/lib/python3.12/site-packages/cryptography-43.0.0.dist-info/WHEEL new file mode 100644 index 0000000..b3e268a --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/cryptography-43.0.0.dist-info/WHEEL @@ -0,0 +1,5 @@ +Wheel-Version: 1.0 +Generator: maturin (1.7.0) +Root-Is-Purelib: false +Tag: cp39-abi3-manylinux_2_28_x86_64 + diff --git a/code/.venv/lib/python3.12/site-packages/cryptography-43.0.0.dist-info/license_files/LICENSE b/code/.venv/lib/python3.12/site-packages/cryptography-43.0.0.dist-info/license_files/LICENSE new file mode 100644 index 0000000..b11f379 --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/cryptography-43.0.0.dist-info/license_files/LICENSE @@ -0,0 +1,3 @@ +This software is made available under the terms of *either* of the licenses +found in LICENSE.APACHE or LICENSE.BSD. Contributions to cryptography are made +under the terms of *both* these licenses. diff --git a/code/.venv/lib/python3.12/site-packages/cryptography-43.0.0.dist-info/license_files/LICENSE.APACHE b/code/.venv/lib/python3.12/site-packages/cryptography-43.0.0.dist-info/license_files/LICENSE.APACHE new file mode 100644 index 0000000..62589ed --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/cryptography-43.0.0.dist-info/license_files/LICENSE.APACHE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + https://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/code/.venv/lib/python3.12/site-packages/cryptography-43.0.0.dist-info/license_files/LICENSE.BSD b/code/.venv/lib/python3.12/site-packages/cryptography-43.0.0.dist-info/license_files/LICENSE.BSD new file mode 100644 index 0000000..ec1a29d --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/cryptography-43.0.0.dist-info/license_files/LICENSE.BSD @@ -0,0 +1,27 @@ +Copyright (c) Individual contributors. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + 3. Neither the name of PyCA Cryptography nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/code/.venv/lib/python3.12/site-packages/cryptography/__about__.py b/code/.venv/lib/python3.12/site-packages/cryptography/__about__.py new file mode 100644 index 0000000..4362aed --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/cryptography/__about__.py @@ -0,0 +1,17 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +from __future__ import annotations + +__all__ = [ + "__author__", + "__copyright__", + "__version__", +] + +__version__ = "43.0.0" + + +__author__ = "The Python Cryptographic Authority and individual contributors" +__copyright__ = f"Copyright 2013-2024 {__author__}" diff --git a/code/.venv/lib/python3.12/site-packages/cryptography/__init__.py b/code/.venv/lib/python3.12/site-packages/cryptography/__init__.py new file mode 100644 index 0000000..d374f75 --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/cryptography/__init__.py @@ -0,0 +1,13 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +from __future__ import annotations + +from cryptography.__about__ import __author__, __copyright__, __version__ + +__all__ = [ + "__author__", + "__copyright__", + "__version__", +] diff --git a/code/.venv/lib/python3.12/site-packages/cryptography/__pycache__/__about__.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/cryptography/__pycache__/__about__.cpython-312.pyc new file mode 100644 index 0000000..b484c35 Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/cryptography/__pycache__/__about__.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/cryptography/__pycache__/__init__.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/cryptography/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..3e4448b Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/cryptography/__pycache__/__init__.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/cryptography/__pycache__/exceptions.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/cryptography/__pycache__/exceptions.cpython-312.pyc new file mode 100644 index 0000000..fd79969 Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/cryptography/__pycache__/exceptions.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/cryptography/__pycache__/fernet.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/cryptography/__pycache__/fernet.cpython-312.pyc new file mode 100644 index 0000000..89153d0 Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/cryptography/__pycache__/fernet.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/cryptography/__pycache__/utils.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/cryptography/__pycache__/utils.cpython-312.pyc new file mode 100644 index 0000000..df01658 Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/cryptography/__pycache__/utils.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/cryptography/exceptions.py b/code/.venv/lib/python3.12/site-packages/cryptography/exceptions.py new file mode 100644 index 0000000..fe125ea --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/cryptography/exceptions.py @@ -0,0 +1,52 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +from __future__ import annotations + +import typing + +from cryptography.hazmat.bindings._rust import exceptions as rust_exceptions + +if typing.TYPE_CHECKING: + from cryptography.hazmat.bindings._rust import openssl as rust_openssl + +_Reasons = rust_exceptions._Reasons + + +class UnsupportedAlgorithm(Exception): + def __init__(self, message: str, reason: _Reasons | None = None) -> None: + super().__init__(message) + self._reason = reason + + +class AlreadyFinalized(Exception): + pass + + +class AlreadyUpdated(Exception): + pass + + +class NotYetFinalized(Exception): + pass + + +class InvalidTag(Exception): + pass + + +class InvalidSignature(Exception): + pass + + +class InternalError(Exception): + def __init__( + self, msg: str, err_code: list[rust_openssl.OpenSSLError] + ) -> None: + super().__init__(msg) + self.err_code = err_code + + +class InvalidKey(Exception): + pass diff --git a/code/.venv/lib/python3.12/site-packages/cryptography/fernet.py b/code/.venv/lib/python3.12/site-packages/cryptography/fernet.py new file mode 100644 index 0000000..35ce113 --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/cryptography/fernet.py @@ -0,0 +1,215 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +from __future__ import annotations + +import base64 +import binascii +import os +import time +import typing + +from cryptography import utils +from cryptography.exceptions import InvalidSignature +from cryptography.hazmat.primitives import hashes, padding +from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes +from cryptography.hazmat.primitives.hmac import HMAC + + +class InvalidToken(Exception): + pass + + +_MAX_CLOCK_SKEW = 60 + + +class Fernet: + def __init__( + self, + key: bytes | str, + backend: typing.Any = None, + ) -> None: + try: + key = base64.urlsafe_b64decode(key) + except binascii.Error as exc: + raise ValueError( + "Fernet key must be 32 url-safe base64-encoded bytes." + ) from exc + if len(key) != 32: + raise ValueError( + "Fernet key must be 32 url-safe base64-encoded bytes." + ) + + self._signing_key = key[:16] + self._encryption_key = key[16:] + + @classmethod + def generate_key(cls) -> bytes: + return base64.urlsafe_b64encode(os.urandom(32)) + + def encrypt(self, data: bytes) -> bytes: + return self.encrypt_at_time(data, int(time.time())) + + def encrypt_at_time(self, data: bytes, current_time: int) -> bytes: + iv = os.urandom(16) + return self._encrypt_from_parts(data, current_time, iv) + + def _encrypt_from_parts( + self, data: bytes, current_time: int, iv: bytes + ) -> bytes: + utils._check_bytes("data", data) + + padder = padding.PKCS7(algorithms.AES.block_size).padder() + padded_data = padder.update(data) + padder.finalize() + encryptor = Cipher( + algorithms.AES(self._encryption_key), + modes.CBC(iv), + ).encryptor() + ciphertext = encryptor.update(padded_data) + encryptor.finalize() + + basic_parts = ( + b"\x80" + + current_time.to_bytes(length=8, byteorder="big") + + iv + + ciphertext + ) + + h = HMAC(self._signing_key, hashes.SHA256()) + h.update(basic_parts) + hmac = h.finalize() + return base64.urlsafe_b64encode(basic_parts + hmac) + + def decrypt(self, token: bytes | str, ttl: int | None = None) -> bytes: + timestamp, data = Fernet._get_unverified_token_data(token) + if ttl is None: + time_info = None + else: + time_info = (ttl, int(time.time())) + return self._decrypt_data(data, timestamp, time_info) + + def decrypt_at_time( + self, token: bytes | str, ttl: int, current_time: int + ) -> bytes: + if ttl is None: + raise ValueError( + "decrypt_at_time() can only be used with a non-None ttl" + ) + timestamp, data = Fernet._get_unverified_token_data(token) + return self._decrypt_data(data, timestamp, (ttl, current_time)) + + def extract_timestamp(self, token: bytes | str) -> int: + timestamp, data = Fernet._get_unverified_token_data(token) + # Verify the token was not tampered with. + self._verify_signature(data) + return timestamp + + @staticmethod + def _get_unverified_token_data(token: bytes | str) -> tuple[int, bytes]: + if not isinstance(token, (str, bytes)): + raise TypeError("token must be bytes or str") + + try: + data = base64.urlsafe_b64decode(token) + except (TypeError, binascii.Error): + raise InvalidToken + + if not data or data[0] != 0x80: + raise InvalidToken + + if len(data) < 9: + raise InvalidToken + + timestamp = int.from_bytes(data[1:9], byteorder="big") + return timestamp, data + + def _verify_signature(self, data: bytes) -> None: + h = HMAC(self._signing_key, hashes.SHA256()) + h.update(data[:-32]) + try: + h.verify(data[-32:]) + except InvalidSignature: + raise InvalidToken + + def _decrypt_data( + self, + data: bytes, + timestamp: int, + time_info: tuple[int, int] | None, + ) -> bytes: + if time_info is not None: + ttl, current_time = time_info + if timestamp + ttl < current_time: + raise InvalidToken + + if current_time + _MAX_CLOCK_SKEW < timestamp: + raise InvalidToken + + self._verify_signature(data) + + iv = data[9:25] + ciphertext = data[25:-32] + decryptor = Cipher( + algorithms.AES(self._encryption_key), modes.CBC(iv) + ).decryptor() + plaintext_padded = decryptor.update(ciphertext) + try: + plaintext_padded += decryptor.finalize() + except ValueError: + raise InvalidToken + unpadder = padding.PKCS7(algorithms.AES.block_size).unpadder() + + unpadded = unpadder.update(plaintext_padded) + try: + unpadded += unpadder.finalize() + except ValueError: + raise InvalidToken + return unpadded + + +class MultiFernet: + def __init__(self, fernets: typing.Iterable[Fernet]): + fernets = list(fernets) + if not fernets: + raise ValueError( + "MultiFernet requires at least one Fernet instance" + ) + self._fernets = fernets + + def encrypt(self, msg: bytes) -> bytes: + return self.encrypt_at_time(msg, int(time.time())) + + def encrypt_at_time(self, msg: bytes, current_time: int) -> bytes: + return self._fernets[0].encrypt_at_time(msg, current_time) + + def rotate(self, msg: bytes | str) -> bytes: + timestamp, data = Fernet._get_unverified_token_data(msg) + for f in self._fernets: + try: + p = f._decrypt_data(data, timestamp, None) + break + except InvalidToken: + pass + else: + raise InvalidToken + + iv = os.urandom(16) + return self._fernets[0]._encrypt_from_parts(p, timestamp, iv) + + def decrypt(self, msg: bytes | str, ttl: int | None = None) -> bytes: + for f in self._fernets: + try: + return f.decrypt(msg, ttl) + except InvalidToken: + pass + raise InvalidToken + + def decrypt_at_time( + self, msg: bytes | str, ttl: int, current_time: int + ) -> bytes: + for f in self._fernets: + try: + return f.decrypt_at_time(msg, ttl, current_time) + except InvalidToken: + pass + raise InvalidToken diff --git a/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/__init__.py b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/__init__.py new file mode 100644 index 0000000..b9f1187 --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/__init__.py @@ -0,0 +1,13 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +from __future__ import annotations + +""" +Hazardous Materials + +This is a "Hazardous Materials" module. You should ONLY use it if you're +100% absolutely sure that you know what you're doing because this module +is full of land mines, dragons, and dinosaurs with laser guns. +""" diff --git a/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/__pycache__/__init__.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..771714c Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/__pycache__/__init__.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/__pycache__/_oid.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/__pycache__/_oid.cpython-312.pyc new file mode 100644 index 0000000..4c51293 Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/__pycache__/_oid.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/_oid.py b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/_oid.py new file mode 100644 index 0000000..fd5e37d --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/_oid.py @@ -0,0 +1,313 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +from __future__ import annotations + +from cryptography.hazmat.bindings._rust import ( + ObjectIdentifier as ObjectIdentifier, +) +from cryptography.hazmat.primitives import hashes + + +class ExtensionOID: + SUBJECT_DIRECTORY_ATTRIBUTES = ObjectIdentifier("2.5.29.9") + SUBJECT_KEY_IDENTIFIER = ObjectIdentifier("2.5.29.14") + KEY_USAGE = ObjectIdentifier("2.5.29.15") + SUBJECT_ALTERNATIVE_NAME = ObjectIdentifier("2.5.29.17") + ISSUER_ALTERNATIVE_NAME = ObjectIdentifier("2.5.29.18") + BASIC_CONSTRAINTS = ObjectIdentifier("2.5.29.19") + NAME_CONSTRAINTS = ObjectIdentifier("2.5.29.30") + CRL_DISTRIBUTION_POINTS = ObjectIdentifier("2.5.29.31") + CERTIFICATE_POLICIES = ObjectIdentifier("2.5.29.32") + POLICY_MAPPINGS = ObjectIdentifier("2.5.29.33") + AUTHORITY_KEY_IDENTIFIER = ObjectIdentifier("2.5.29.35") + POLICY_CONSTRAINTS = ObjectIdentifier("2.5.29.36") + EXTENDED_KEY_USAGE = ObjectIdentifier("2.5.29.37") + FRESHEST_CRL = ObjectIdentifier("2.5.29.46") + INHIBIT_ANY_POLICY = ObjectIdentifier("2.5.29.54") + ISSUING_DISTRIBUTION_POINT = ObjectIdentifier("2.5.29.28") + AUTHORITY_INFORMATION_ACCESS = ObjectIdentifier("1.3.6.1.5.5.7.1.1") + SUBJECT_INFORMATION_ACCESS = ObjectIdentifier("1.3.6.1.5.5.7.1.11") + OCSP_NO_CHECK = ObjectIdentifier("1.3.6.1.5.5.7.48.1.5") + TLS_FEATURE = ObjectIdentifier("1.3.6.1.5.5.7.1.24") + CRL_NUMBER = ObjectIdentifier("2.5.29.20") + DELTA_CRL_INDICATOR = ObjectIdentifier("2.5.29.27") + PRECERT_SIGNED_CERTIFICATE_TIMESTAMPS = ObjectIdentifier( + "1.3.6.1.4.1.11129.2.4.2" + ) + PRECERT_POISON = ObjectIdentifier("1.3.6.1.4.1.11129.2.4.3") + SIGNED_CERTIFICATE_TIMESTAMPS = ObjectIdentifier("1.3.6.1.4.1.11129.2.4.5") + MS_CERTIFICATE_TEMPLATE = ObjectIdentifier("1.3.6.1.4.1.311.21.7") + + +class OCSPExtensionOID: + NONCE = ObjectIdentifier("1.3.6.1.5.5.7.48.1.2") + ACCEPTABLE_RESPONSES = ObjectIdentifier("1.3.6.1.5.5.7.48.1.4") + + +class CRLEntryExtensionOID: + CERTIFICATE_ISSUER = ObjectIdentifier("2.5.29.29") + CRL_REASON = ObjectIdentifier("2.5.29.21") + INVALIDITY_DATE = ObjectIdentifier("2.5.29.24") + + +class NameOID: + COMMON_NAME = ObjectIdentifier("2.5.4.3") + COUNTRY_NAME = ObjectIdentifier("2.5.4.6") + LOCALITY_NAME = ObjectIdentifier("2.5.4.7") + STATE_OR_PROVINCE_NAME = ObjectIdentifier("2.5.4.8") + STREET_ADDRESS = ObjectIdentifier("2.5.4.9") + ORGANIZATION_IDENTIFIER = ObjectIdentifier("2.5.4.97") + ORGANIZATION_NAME = ObjectIdentifier("2.5.4.10") + ORGANIZATIONAL_UNIT_NAME = ObjectIdentifier("2.5.4.11") + SERIAL_NUMBER = ObjectIdentifier("2.5.4.5") + SURNAME = ObjectIdentifier("2.5.4.4") + GIVEN_NAME = ObjectIdentifier("2.5.4.42") + TITLE = ObjectIdentifier("2.5.4.12") + INITIALS = ObjectIdentifier("2.5.4.43") + GENERATION_QUALIFIER = ObjectIdentifier("2.5.4.44") + X500_UNIQUE_IDENTIFIER = ObjectIdentifier("2.5.4.45") + DN_QUALIFIER = ObjectIdentifier("2.5.4.46") + PSEUDONYM = ObjectIdentifier("2.5.4.65") + USER_ID = ObjectIdentifier("0.9.2342.19200300.100.1.1") + DOMAIN_COMPONENT = ObjectIdentifier("0.9.2342.19200300.100.1.25") + EMAIL_ADDRESS = ObjectIdentifier("1.2.840.113549.1.9.1") + JURISDICTION_COUNTRY_NAME = ObjectIdentifier("1.3.6.1.4.1.311.60.2.1.3") + JURISDICTION_LOCALITY_NAME = ObjectIdentifier("1.3.6.1.4.1.311.60.2.1.1") + JURISDICTION_STATE_OR_PROVINCE_NAME = ObjectIdentifier( + "1.3.6.1.4.1.311.60.2.1.2" + ) + BUSINESS_CATEGORY = ObjectIdentifier("2.5.4.15") + POSTAL_ADDRESS = ObjectIdentifier("2.5.4.16") + POSTAL_CODE = ObjectIdentifier("2.5.4.17") + INN = ObjectIdentifier("1.2.643.3.131.1.1") + OGRN = ObjectIdentifier("1.2.643.100.1") + SNILS = ObjectIdentifier("1.2.643.100.3") + UNSTRUCTURED_NAME = ObjectIdentifier("1.2.840.113549.1.9.2") + + +class SignatureAlgorithmOID: + RSA_WITH_MD5 = ObjectIdentifier("1.2.840.113549.1.1.4") + RSA_WITH_SHA1 = ObjectIdentifier("1.2.840.113549.1.1.5") + # This is an alternate OID for RSA with SHA1 that is occasionally seen + _RSA_WITH_SHA1 = ObjectIdentifier("1.3.14.3.2.29") + RSA_WITH_SHA224 = ObjectIdentifier("1.2.840.113549.1.1.14") + RSA_WITH_SHA256 = ObjectIdentifier("1.2.840.113549.1.1.11") + RSA_WITH_SHA384 = ObjectIdentifier("1.2.840.113549.1.1.12") + RSA_WITH_SHA512 = ObjectIdentifier("1.2.840.113549.1.1.13") + RSA_WITH_SHA3_224 = ObjectIdentifier("2.16.840.1.101.3.4.3.13") + RSA_WITH_SHA3_256 = ObjectIdentifier("2.16.840.1.101.3.4.3.14") + RSA_WITH_SHA3_384 = ObjectIdentifier("2.16.840.1.101.3.4.3.15") + RSA_WITH_SHA3_512 = ObjectIdentifier("2.16.840.1.101.3.4.3.16") + RSASSA_PSS = ObjectIdentifier("1.2.840.113549.1.1.10") + ECDSA_WITH_SHA1 = ObjectIdentifier("1.2.840.10045.4.1") + ECDSA_WITH_SHA224 = ObjectIdentifier("1.2.840.10045.4.3.1") + ECDSA_WITH_SHA256 = ObjectIdentifier("1.2.840.10045.4.3.2") + ECDSA_WITH_SHA384 = ObjectIdentifier("1.2.840.10045.4.3.3") + ECDSA_WITH_SHA512 = ObjectIdentifier("1.2.840.10045.4.3.4") + ECDSA_WITH_SHA3_224 = ObjectIdentifier("2.16.840.1.101.3.4.3.9") + ECDSA_WITH_SHA3_256 = ObjectIdentifier("2.16.840.1.101.3.4.3.10") + ECDSA_WITH_SHA3_384 = ObjectIdentifier("2.16.840.1.101.3.4.3.11") + ECDSA_WITH_SHA3_512 = ObjectIdentifier("2.16.840.1.101.3.4.3.12") + DSA_WITH_SHA1 = ObjectIdentifier("1.2.840.10040.4.3") + DSA_WITH_SHA224 = ObjectIdentifier("2.16.840.1.101.3.4.3.1") + DSA_WITH_SHA256 = ObjectIdentifier("2.16.840.1.101.3.4.3.2") + DSA_WITH_SHA384 = ObjectIdentifier("2.16.840.1.101.3.4.3.3") + DSA_WITH_SHA512 = ObjectIdentifier("2.16.840.1.101.3.4.3.4") + ED25519 = ObjectIdentifier("1.3.101.112") + ED448 = ObjectIdentifier("1.3.101.113") + GOSTR3411_94_WITH_3410_2001 = ObjectIdentifier("1.2.643.2.2.3") + GOSTR3410_2012_WITH_3411_2012_256 = ObjectIdentifier("1.2.643.7.1.1.3.2") + GOSTR3410_2012_WITH_3411_2012_512 = ObjectIdentifier("1.2.643.7.1.1.3.3") + + +_SIG_OIDS_TO_HASH: dict[ObjectIdentifier, hashes.HashAlgorithm | None] = { + SignatureAlgorithmOID.RSA_WITH_MD5: hashes.MD5(), + SignatureAlgorithmOID.RSA_WITH_SHA1: hashes.SHA1(), + SignatureAlgorithmOID._RSA_WITH_SHA1: hashes.SHA1(), + SignatureAlgorithmOID.RSA_WITH_SHA224: hashes.SHA224(), + SignatureAlgorithmOID.RSA_WITH_SHA256: hashes.SHA256(), + SignatureAlgorithmOID.RSA_WITH_SHA384: hashes.SHA384(), + SignatureAlgorithmOID.RSA_WITH_SHA512: hashes.SHA512(), + SignatureAlgorithmOID.RSA_WITH_SHA3_224: hashes.SHA3_224(), + SignatureAlgorithmOID.RSA_WITH_SHA3_256: hashes.SHA3_256(), + SignatureAlgorithmOID.RSA_WITH_SHA3_384: hashes.SHA3_384(), + SignatureAlgorithmOID.RSA_WITH_SHA3_512: hashes.SHA3_512(), + SignatureAlgorithmOID.ECDSA_WITH_SHA1: hashes.SHA1(), + SignatureAlgorithmOID.ECDSA_WITH_SHA224: hashes.SHA224(), + SignatureAlgorithmOID.ECDSA_WITH_SHA256: hashes.SHA256(), + SignatureAlgorithmOID.ECDSA_WITH_SHA384: hashes.SHA384(), + SignatureAlgorithmOID.ECDSA_WITH_SHA512: hashes.SHA512(), + SignatureAlgorithmOID.ECDSA_WITH_SHA3_224: hashes.SHA3_224(), + SignatureAlgorithmOID.ECDSA_WITH_SHA3_256: hashes.SHA3_256(), + SignatureAlgorithmOID.ECDSA_WITH_SHA3_384: hashes.SHA3_384(), + SignatureAlgorithmOID.ECDSA_WITH_SHA3_512: hashes.SHA3_512(), + SignatureAlgorithmOID.DSA_WITH_SHA1: hashes.SHA1(), + SignatureAlgorithmOID.DSA_WITH_SHA224: hashes.SHA224(), + SignatureAlgorithmOID.DSA_WITH_SHA256: hashes.SHA256(), + SignatureAlgorithmOID.ED25519: None, + SignatureAlgorithmOID.ED448: None, + SignatureAlgorithmOID.GOSTR3411_94_WITH_3410_2001: None, + SignatureAlgorithmOID.GOSTR3410_2012_WITH_3411_2012_256: None, + SignatureAlgorithmOID.GOSTR3410_2012_WITH_3411_2012_512: None, +} + + +class PublicKeyAlgorithmOID: + DSA = ObjectIdentifier("1.2.840.10040.4.1") + EC_PUBLIC_KEY = ObjectIdentifier("1.2.840.10045.2.1") + RSAES_PKCS1_v1_5 = ObjectIdentifier("1.2.840.113549.1.1.1") + RSASSA_PSS = ObjectIdentifier("1.2.840.113549.1.1.10") + X25519 = ObjectIdentifier("1.3.101.110") + X448 = ObjectIdentifier("1.3.101.111") + ED25519 = ObjectIdentifier("1.3.101.112") + ED448 = ObjectIdentifier("1.3.101.113") + + +class ExtendedKeyUsageOID: + SERVER_AUTH = ObjectIdentifier("1.3.6.1.5.5.7.3.1") + CLIENT_AUTH = ObjectIdentifier("1.3.6.1.5.5.7.3.2") + CODE_SIGNING = ObjectIdentifier("1.3.6.1.5.5.7.3.3") + EMAIL_PROTECTION = ObjectIdentifier("1.3.6.1.5.5.7.3.4") + TIME_STAMPING = ObjectIdentifier("1.3.6.1.5.5.7.3.8") + OCSP_SIGNING = ObjectIdentifier("1.3.6.1.5.5.7.3.9") + ANY_EXTENDED_KEY_USAGE = ObjectIdentifier("2.5.29.37.0") + SMARTCARD_LOGON = ObjectIdentifier("1.3.6.1.4.1.311.20.2.2") + KERBEROS_PKINIT_KDC = ObjectIdentifier("1.3.6.1.5.2.3.5") + IPSEC_IKE = ObjectIdentifier("1.3.6.1.5.5.7.3.17") + CERTIFICATE_TRANSPARENCY = ObjectIdentifier("1.3.6.1.4.1.11129.2.4.4") + + +class AuthorityInformationAccessOID: + CA_ISSUERS = ObjectIdentifier("1.3.6.1.5.5.7.48.2") + OCSP = ObjectIdentifier("1.3.6.1.5.5.7.48.1") + + +class SubjectInformationAccessOID: + CA_REPOSITORY = ObjectIdentifier("1.3.6.1.5.5.7.48.5") + + +class CertificatePoliciesOID: + CPS_QUALIFIER = ObjectIdentifier("1.3.6.1.5.5.7.2.1") + CPS_USER_NOTICE = ObjectIdentifier("1.3.6.1.5.5.7.2.2") + ANY_POLICY = ObjectIdentifier("2.5.29.32.0") + + +class AttributeOID: + CHALLENGE_PASSWORD = ObjectIdentifier("1.2.840.113549.1.9.7") + UNSTRUCTURED_NAME = ObjectIdentifier("1.2.840.113549.1.9.2") + + +_OID_NAMES = { + NameOID.COMMON_NAME: "commonName", + NameOID.COUNTRY_NAME: "countryName", + NameOID.LOCALITY_NAME: "localityName", + NameOID.STATE_OR_PROVINCE_NAME: "stateOrProvinceName", + NameOID.STREET_ADDRESS: "streetAddress", + NameOID.ORGANIZATION_NAME: "organizationName", + NameOID.ORGANIZATIONAL_UNIT_NAME: "organizationalUnitName", + NameOID.SERIAL_NUMBER: "serialNumber", + NameOID.SURNAME: "surname", + NameOID.GIVEN_NAME: "givenName", + NameOID.TITLE: "title", + NameOID.GENERATION_QUALIFIER: "generationQualifier", + NameOID.X500_UNIQUE_IDENTIFIER: "x500UniqueIdentifier", + NameOID.DN_QUALIFIER: "dnQualifier", + NameOID.PSEUDONYM: "pseudonym", + NameOID.USER_ID: "userID", + NameOID.DOMAIN_COMPONENT: "domainComponent", + NameOID.EMAIL_ADDRESS: "emailAddress", + NameOID.JURISDICTION_COUNTRY_NAME: "jurisdictionCountryName", + NameOID.JURISDICTION_LOCALITY_NAME: "jurisdictionLocalityName", + NameOID.JURISDICTION_STATE_OR_PROVINCE_NAME: ( + "jurisdictionStateOrProvinceName" + ), + NameOID.BUSINESS_CATEGORY: "businessCategory", + NameOID.POSTAL_ADDRESS: "postalAddress", + NameOID.POSTAL_CODE: "postalCode", + NameOID.INN: "INN", + NameOID.OGRN: "OGRN", + NameOID.SNILS: "SNILS", + NameOID.UNSTRUCTURED_NAME: "unstructuredName", + SignatureAlgorithmOID.RSA_WITH_MD5: "md5WithRSAEncryption", + SignatureAlgorithmOID.RSA_WITH_SHA1: "sha1WithRSAEncryption", + SignatureAlgorithmOID.RSA_WITH_SHA224: "sha224WithRSAEncryption", + SignatureAlgorithmOID.RSA_WITH_SHA256: "sha256WithRSAEncryption", + SignatureAlgorithmOID.RSA_WITH_SHA384: "sha384WithRSAEncryption", + SignatureAlgorithmOID.RSA_WITH_SHA512: "sha512WithRSAEncryption", + SignatureAlgorithmOID.RSASSA_PSS: "RSASSA-PSS", + SignatureAlgorithmOID.ECDSA_WITH_SHA1: "ecdsa-with-SHA1", + SignatureAlgorithmOID.ECDSA_WITH_SHA224: "ecdsa-with-SHA224", + SignatureAlgorithmOID.ECDSA_WITH_SHA256: "ecdsa-with-SHA256", + SignatureAlgorithmOID.ECDSA_WITH_SHA384: "ecdsa-with-SHA384", + SignatureAlgorithmOID.ECDSA_WITH_SHA512: "ecdsa-with-SHA512", + SignatureAlgorithmOID.DSA_WITH_SHA1: "dsa-with-sha1", + SignatureAlgorithmOID.DSA_WITH_SHA224: "dsa-with-sha224", + SignatureAlgorithmOID.DSA_WITH_SHA256: "dsa-with-sha256", + SignatureAlgorithmOID.ED25519: "ed25519", + SignatureAlgorithmOID.ED448: "ed448", + SignatureAlgorithmOID.GOSTR3411_94_WITH_3410_2001: ( + "GOST R 34.11-94 with GOST R 34.10-2001" + ), + SignatureAlgorithmOID.GOSTR3410_2012_WITH_3411_2012_256: ( + "GOST R 34.10-2012 with GOST R 34.11-2012 (256 bit)" + ), + SignatureAlgorithmOID.GOSTR3410_2012_WITH_3411_2012_512: ( + "GOST R 34.10-2012 with GOST R 34.11-2012 (512 bit)" + ), + PublicKeyAlgorithmOID.DSA: "dsaEncryption", + PublicKeyAlgorithmOID.EC_PUBLIC_KEY: "id-ecPublicKey", + PublicKeyAlgorithmOID.RSAES_PKCS1_v1_5: "rsaEncryption", + PublicKeyAlgorithmOID.RSASSA_PSS: "rsassaPss", + PublicKeyAlgorithmOID.X25519: "X25519", + PublicKeyAlgorithmOID.X448: "X448", + ExtendedKeyUsageOID.SERVER_AUTH: "serverAuth", + ExtendedKeyUsageOID.CLIENT_AUTH: "clientAuth", + ExtendedKeyUsageOID.CODE_SIGNING: "codeSigning", + ExtendedKeyUsageOID.EMAIL_PROTECTION: "emailProtection", + ExtendedKeyUsageOID.TIME_STAMPING: "timeStamping", + ExtendedKeyUsageOID.OCSP_SIGNING: "OCSPSigning", + ExtendedKeyUsageOID.SMARTCARD_LOGON: "msSmartcardLogin", + ExtendedKeyUsageOID.KERBEROS_PKINIT_KDC: "pkInitKDC", + ExtensionOID.SUBJECT_DIRECTORY_ATTRIBUTES: "subjectDirectoryAttributes", + ExtensionOID.SUBJECT_KEY_IDENTIFIER: "subjectKeyIdentifier", + ExtensionOID.KEY_USAGE: "keyUsage", + ExtensionOID.SUBJECT_ALTERNATIVE_NAME: "subjectAltName", + ExtensionOID.ISSUER_ALTERNATIVE_NAME: "issuerAltName", + ExtensionOID.BASIC_CONSTRAINTS: "basicConstraints", + ExtensionOID.PRECERT_SIGNED_CERTIFICATE_TIMESTAMPS: ( + "signedCertificateTimestampList" + ), + ExtensionOID.SIGNED_CERTIFICATE_TIMESTAMPS: ( + "signedCertificateTimestampList" + ), + ExtensionOID.PRECERT_POISON: "ctPoison", + ExtensionOID.MS_CERTIFICATE_TEMPLATE: "msCertificateTemplate", + CRLEntryExtensionOID.CRL_REASON: "cRLReason", + CRLEntryExtensionOID.INVALIDITY_DATE: "invalidityDate", + CRLEntryExtensionOID.CERTIFICATE_ISSUER: "certificateIssuer", + ExtensionOID.NAME_CONSTRAINTS: "nameConstraints", + ExtensionOID.CRL_DISTRIBUTION_POINTS: "cRLDistributionPoints", + ExtensionOID.CERTIFICATE_POLICIES: "certificatePolicies", + ExtensionOID.POLICY_MAPPINGS: "policyMappings", + ExtensionOID.AUTHORITY_KEY_IDENTIFIER: "authorityKeyIdentifier", + ExtensionOID.POLICY_CONSTRAINTS: "policyConstraints", + ExtensionOID.EXTENDED_KEY_USAGE: "extendedKeyUsage", + ExtensionOID.FRESHEST_CRL: "freshestCRL", + ExtensionOID.INHIBIT_ANY_POLICY: "inhibitAnyPolicy", + ExtensionOID.ISSUING_DISTRIBUTION_POINT: "issuingDistributionPoint", + ExtensionOID.AUTHORITY_INFORMATION_ACCESS: "authorityInfoAccess", + ExtensionOID.SUBJECT_INFORMATION_ACCESS: "subjectInfoAccess", + ExtensionOID.OCSP_NO_CHECK: "OCSPNoCheck", + ExtensionOID.CRL_NUMBER: "cRLNumber", + ExtensionOID.DELTA_CRL_INDICATOR: "deltaCRLIndicator", + ExtensionOID.TLS_FEATURE: "TLSFeature", + AuthorityInformationAccessOID.OCSP: "OCSP", + AuthorityInformationAccessOID.CA_ISSUERS: "caIssuers", + SubjectInformationAccessOID.CA_REPOSITORY: "caRepository", + CertificatePoliciesOID.CPS_QUALIFIER: "id-qt-cps", + CertificatePoliciesOID.CPS_USER_NOTICE: "id-qt-unotice", + OCSPExtensionOID.NONCE: "OCSPNonce", + AttributeOID.CHALLENGE_PASSWORD: "challengePassword", +} diff --git a/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/backends/__init__.py b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/backends/__init__.py new file mode 100644 index 0000000..b4400aa --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/backends/__init__.py @@ -0,0 +1,13 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +from __future__ import annotations + +from typing import Any + + +def default_backend() -> Any: + from cryptography.hazmat.backends.openssl.backend import backend + + return backend diff --git a/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/backends/__pycache__/__init__.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/backends/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..69cdb07 Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/backends/__pycache__/__init__.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/backends/openssl/__init__.py b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/backends/openssl/__init__.py new file mode 100644 index 0000000..51b0447 --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/backends/openssl/__init__.py @@ -0,0 +1,9 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +from __future__ import annotations + +from cryptography.hazmat.backends.openssl.backend import backend + +__all__ = ["backend"] diff --git a/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/backends/openssl/__pycache__/__init__.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/backends/openssl/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..348c114 Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/backends/openssl/__pycache__/__init__.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/backends/openssl/__pycache__/backend.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/backends/openssl/__pycache__/backend.cpython-312.pyc new file mode 100644 index 0000000..f951089 Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/backends/openssl/__pycache__/backend.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/backends/openssl/backend.py b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/backends/openssl/backend.py new file mode 100644 index 0000000..c87d3e8 --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/backends/openssl/backend.py @@ -0,0 +1,291 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +from __future__ import annotations + +from cryptography.hazmat.bindings._rust import openssl as rust_openssl +from cryptography.hazmat.bindings.openssl import binding +from cryptography.hazmat.primitives import hashes +from cryptography.hazmat.primitives._asymmetric import AsymmetricPadding +from cryptography.hazmat.primitives.asymmetric import ec +from cryptography.hazmat.primitives.asymmetric import utils as asym_utils +from cryptography.hazmat.primitives.asymmetric.padding import ( + MGF1, + OAEP, + PSS, + PKCS1v15, +) +from cryptography.hazmat.primitives.ciphers import ( + CipherAlgorithm, +) +from cryptography.hazmat.primitives.ciphers.algorithms import ( + AES, +) +from cryptography.hazmat.primitives.ciphers.modes import ( + CBC, + Mode, +) + + +class Backend: + """ + OpenSSL API binding interfaces. + """ + + name = "openssl" + + # TripleDES encryption is disallowed/deprecated throughout 2023 in + # FIPS 140-3. To keep it simple we denylist any use of TripleDES (TDEA). + _fips_ciphers = (AES,) + # Sometimes SHA1 is still permissible. That logic is contained + # within the various *_supported methods. + _fips_hashes = ( + hashes.SHA224, + hashes.SHA256, + hashes.SHA384, + hashes.SHA512, + hashes.SHA512_224, + hashes.SHA512_256, + hashes.SHA3_224, + hashes.SHA3_256, + hashes.SHA3_384, + hashes.SHA3_512, + hashes.SHAKE128, + hashes.SHAKE256, + ) + _fips_ecdh_curves = ( + ec.SECP224R1, + ec.SECP256R1, + ec.SECP384R1, + ec.SECP521R1, + ) + _fips_rsa_min_key_size = 2048 + _fips_rsa_min_public_exponent = 65537 + _fips_dsa_min_modulus = 1 << 2048 + _fips_dh_min_key_size = 2048 + _fips_dh_min_modulus = 1 << _fips_dh_min_key_size + + def __init__(self) -> None: + self._binding = binding.Binding() + self._ffi = self._binding.ffi + self._lib = self._binding.lib + self._fips_enabled = rust_openssl.is_fips_enabled() + + def __repr__(self) -> str: + return ( + f"" + ) + + def openssl_assert(self, ok: bool) -> None: + return binding._openssl_assert(ok) + + def _enable_fips(self) -> None: + # This function enables FIPS mode for OpenSSL 3.0.0 on installs that + # have the FIPS provider installed properly. + rust_openssl.enable_fips(rust_openssl._providers) + assert rust_openssl.is_fips_enabled() + self._fips_enabled = rust_openssl.is_fips_enabled() + + def openssl_version_text(self) -> str: + """ + Friendly string name of the loaded OpenSSL library. This is not + necessarily the same version as it was compiled against. + + Example: OpenSSL 3.2.1 30 Jan 2024 + """ + return rust_openssl.openssl_version_text() + + def openssl_version_number(self) -> int: + return rust_openssl.openssl_version() + + def _evp_md_from_algorithm(self, algorithm: hashes.HashAlgorithm): + if algorithm.name in ("blake2b", "blake2s"): + alg = f"{algorithm.name}{algorithm.digest_size * 8}".encode( + "ascii" + ) + else: + alg = algorithm.name.encode("ascii") + + evp_md = self._lib.EVP_get_digestbyname(alg) + return evp_md + + def hash_supported(self, algorithm: hashes.HashAlgorithm) -> bool: + if self._fips_enabled and not isinstance(algorithm, self._fips_hashes): + return False + + evp_md = self._evp_md_from_algorithm(algorithm) + return evp_md != self._ffi.NULL + + def signature_hash_supported( + self, algorithm: hashes.HashAlgorithm + ) -> bool: + # Dedicated check for hashing algorithm use in message digest for + # signatures, e.g. RSA PKCS#1 v1.5 SHA1 (sha1WithRSAEncryption). + if self._fips_enabled and isinstance(algorithm, hashes.SHA1): + return False + return self.hash_supported(algorithm) + + def scrypt_supported(self) -> bool: + if self._fips_enabled: + return False + else: + return hasattr(rust_openssl.kdf, "derive_scrypt") + + def hmac_supported(self, algorithm: hashes.HashAlgorithm) -> bool: + # FIPS mode still allows SHA1 for HMAC + if self._fips_enabled and isinstance(algorithm, hashes.SHA1): + return True + + return self.hash_supported(algorithm) + + def cipher_supported(self, cipher: CipherAlgorithm, mode: Mode) -> bool: + if self._fips_enabled: + # FIPS mode requires AES. TripleDES is disallowed/deprecated in + # FIPS 140-3. + if not isinstance(cipher, self._fips_ciphers): + return False + + return rust_openssl.ciphers.cipher_supported(cipher, mode) + + def pbkdf2_hmac_supported(self, algorithm: hashes.HashAlgorithm) -> bool: + return self.hmac_supported(algorithm) + + def _consume_errors(self) -> list[rust_openssl.OpenSSLError]: + return rust_openssl.capture_error_stack() + + def _oaep_hash_supported(self, algorithm: hashes.HashAlgorithm) -> bool: + if self._fips_enabled and isinstance(algorithm, hashes.SHA1): + return False + + return isinstance( + algorithm, + ( + hashes.SHA1, + hashes.SHA224, + hashes.SHA256, + hashes.SHA384, + hashes.SHA512, + ), + ) + + def rsa_padding_supported(self, padding: AsymmetricPadding) -> bool: + if isinstance(padding, PKCS1v15): + return True + elif isinstance(padding, PSS) and isinstance(padding._mgf, MGF1): + # SHA1 is permissible in MGF1 in FIPS even when SHA1 is blocked + # as signature algorithm. + if self._fips_enabled and isinstance( + padding._mgf._algorithm, hashes.SHA1 + ): + return True + else: + return self.hash_supported(padding._mgf._algorithm) + elif isinstance(padding, OAEP) and isinstance(padding._mgf, MGF1): + return self._oaep_hash_supported( + padding._mgf._algorithm + ) and self._oaep_hash_supported(padding._algorithm) + else: + return False + + def rsa_encryption_supported(self, padding: AsymmetricPadding) -> bool: + if self._fips_enabled and isinstance(padding, PKCS1v15): + return False + else: + return self.rsa_padding_supported(padding) + + def dsa_supported(self) -> bool: + return ( + not rust_openssl.CRYPTOGRAPHY_IS_BORINGSSL + and not self._fips_enabled + ) + + def dsa_hash_supported(self, algorithm: hashes.HashAlgorithm) -> bool: + if not self.dsa_supported(): + return False + return self.signature_hash_supported(algorithm) + + def cmac_algorithm_supported(self, algorithm) -> bool: + return self.cipher_supported( + algorithm, CBC(b"\x00" * algorithm.block_size) + ) + + def elliptic_curve_supported(self, curve: ec.EllipticCurve) -> bool: + if self._fips_enabled and not isinstance( + curve, self._fips_ecdh_curves + ): + return False + + return rust_openssl.ec.curve_supported(curve) + + def elliptic_curve_signature_algorithm_supported( + self, + signature_algorithm: ec.EllipticCurveSignatureAlgorithm, + curve: ec.EllipticCurve, + ) -> bool: + # We only support ECDSA right now. + if not isinstance(signature_algorithm, ec.ECDSA): + return False + + return self.elliptic_curve_supported(curve) and ( + isinstance(signature_algorithm.algorithm, asym_utils.Prehashed) + or self.hash_supported(signature_algorithm.algorithm) + ) + + def elliptic_curve_exchange_algorithm_supported( + self, algorithm: ec.ECDH, curve: ec.EllipticCurve + ) -> bool: + return self.elliptic_curve_supported(curve) and isinstance( + algorithm, ec.ECDH + ) + + def dh_supported(self) -> bool: + return not rust_openssl.CRYPTOGRAPHY_IS_BORINGSSL + + def dh_x942_serialization_supported(self) -> bool: + return self._lib.Cryptography_HAS_EVP_PKEY_DHX == 1 + + def x25519_supported(self) -> bool: + if self._fips_enabled: + return False + return True + + def x448_supported(self) -> bool: + if self._fips_enabled: + return False + return ( + not rust_openssl.CRYPTOGRAPHY_IS_LIBRESSL + and not rust_openssl.CRYPTOGRAPHY_IS_BORINGSSL + ) + + def ed25519_supported(self) -> bool: + if self._fips_enabled: + return False + return True + + def ed448_supported(self) -> bool: + if self._fips_enabled: + return False + return ( + not rust_openssl.CRYPTOGRAPHY_IS_LIBRESSL + and not rust_openssl.CRYPTOGRAPHY_IS_BORINGSSL + ) + + def ecdsa_deterministic_supported(self) -> bool: + return ( + rust_openssl.CRYPTOGRAPHY_OPENSSL_320_OR_GREATER + and not self._fips_enabled + ) + + def poly1305_supported(self) -> bool: + if self._fips_enabled: + return False + return True + + def pkcs7_supported(self) -> bool: + return not rust_openssl.CRYPTOGRAPHY_IS_BORINGSSL + + +backend = Backend() diff --git a/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/__init__.py b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/__init__.py new file mode 100644 index 0000000..b509336 --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/__init__.py @@ -0,0 +1,3 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. diff --git a/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/__pycache__/__init__.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..97d4a53 Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/__pycache__/__init__.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust.abi3.so b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust.abi3.so new file mode 100755 index 0000000..1a10da4 Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust.abi3.so differ diff --git a/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/__init__.pyi b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/__init__.pyi new file mode 100644 index 0000000..c0ea0a5 --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/__init__.pyi @@ -0,0 +1,24 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +import typing + +from cryptography.hazmat.primitives import padding + +def check_pkcs7_padding(data: bytes) -> bool: ... +def check_ansix923_padding(data: bytes) -> bool: ... + +class PKCS7PaddingContext(padding.PaddingContext): + def __init__(self, block_size: int) -> None: ... + def update(self, data: bytes) -> bytes: ... + def finalize(self) -> bytes: ... + +class ObjectIdentifier: + def __init__(self, val: str) -> None: ... + @property + def dotted_string(self) -> str: ... + @property + def _name(self) -> str: ... + +T = typing.TypeVar("T") diff --git a/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/_openssl.pyi b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/_openssl.pyi new file mode 100644 index 0000000..8010008 --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/_openssl.pyi @@ -0,0 +1,8 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +import typing + +lib = typing.Any +ffi = typing.Any diff --git a/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/asn1.pyi b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/asn1.pyi new file mode 100644 index 0000000..3b5f208 --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/asn1.pyi @@ -0,0 +1,7 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +def decode_dss_signature(signature: bytes) -> tuple[int, int]: ... +def encode_dss_signature(r: int, s: int) -> bytes: ... +def parse_spki_for_data(data: bytes) -> bytes: ... diff --git a/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/exceptions.pyi b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/exceptions.pyi new file mode 100644 index 0000000..09f46b1 --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/exceptions.pyi @@ -0,0 +1,17 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +class _Reasons: + BACKEND_MISSING_INTERFACE: _Reasons + UNSUPPORTED_HASH: _Reasons + UNSUPPORTED_CIPHER: _Reasons + UNSUPPORTED_PADDING: _Reasons + UNSUPPORTED_MGF: _Reasons + UNSUPPORTED_PUBLIC_KEY_ALGORITHM: _Reasons + UNSUPPORTED_ELLIPTIC_CURVE: _Reasons + UNSUPPORTED_SERIALIZATION: _Reasons + UNSUPPORTED_X509: _Reasons + UNSUPPORTED_EXCHANGE_ALGORITHM: _Reasons + UNSUPPORTED_DIFFIE_HELLMAN: _Reasons + UNSUPPORTED_MAC: _Reasons diff --git a/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/ocsp.pyi b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/ocsp.pyi new file mode 100644 index 0000000..5e02145 --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/ocsp.pyi @@ -0,0 +1,23 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +from cryptography.hazmat.primitives import hashes +from cryptography.hazmat.primitives.asymmetric.types import PrivateKeyTypes +from cryptography.x509 import ocsp + +class OCSPRequest: ... +class OCSPResponse: ... +class OCSPSingleResponse: ... + +def load_der_ocsp_request(data: bytes) -> ocsp.OCSPRequest: ... +def load_der_ocsp_response(data: bytes) -> ocsp.OCSPResponse: ... +def create_ocsp_request( + builder: ocsp.OCSPRequestBuilder, +) -> ocsp.OCSPRequest: ... +def create_ocsp_response( + status: ocsp.OCSPResponseStatus, + builder: ocsp.OCSPResponseBuilder | None, + private_key: PrivateKeyTypes | None, + hash_algorithm: hashes.HashAlgorithm | None, +) -> ocsp.OCSPResponse: ... diff --git a/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/__init__.pyi b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/__init__.pyi new file mode 100644 index 0000000..1e66d33 --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/__init__.pyi @@ -0,0 +1,71 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +import typing + +from cryptography.hazmat.bindings._rust.openssl import ( + aead, + ciphers, + cmac, + dh, + dsa, + ec, + ed448, + ed25519, + hashes, + hmac, + kdf, + keys, + poly1305, + rsa, + x448, + x25519, +) + +__all__ = [ + "aead", + "ciphers", + "cmac", + "dh", + "dsa", + "ec", + "ed448", + "ed25519", + "hashes", + "hmac", + "kdf", + "keys", + "openssl_version", + "openssl_version_text", + "poly1305", + "raise_openssl_error", + "rsa", + "x448", + "x25519", +] + +CRYPTOGRAPHY_IS_LIBRESSL: bool +CRYPTOGRAPHY_IS_BORINGSSL: bool +CRYPTOGRAPHY_OPENSSL_300_OR_GREATER: bool +CRYPTOGRAPHY_OPENSSL_320_OR_GREATER: bool + +class Providers: ... + +_legacy_provider_loaded: bool +_providers: Providers + +def openssl_version() -> int: ... +def openssl_version_text() -> str: ... +def raise_openssl_error() -> typing.NoReturn: ... +def capture_error_stack() -> list[OpenSSLError]: ... +def is_fips_enabled() -> bool: ... +def enable_fips(providers: Providers) -> None: ... + +class OpenSSLError: + @property + def lib(self) -> int: ... + @property + def reason(self) -> int: ... + @property + def reason_text(self) -> bytes: ... diff --git a/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/aead.pyi b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/aead.pyi new file mode 100644 index 0000000..047f49d --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/aead.pyi @@ -0,0 +1,103 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +class AESGCM: + def __init__(self, key: bytes) -> None: ... + @staticmethod + def generate_key(key_size: int) -> bytes: ... + def encrypt( + self, + nonce: bytes, + data: bytes, + associated_data: bytes | None, + ) -> bytes: ... + def decrypt( + self, + nonce: bytes, + data: bytes, + associated_data: bytes | None, + ) -> bytes: ... + +class ChaCha20Poly1305: + def __init__(self, key: bytes) -> None: ... + @staticmethod + def generate_key() -> bytes: ... + def encrypt( + self, + nonce: bytes, + data: bytes, + associated_data: bytes | None, + ) -> bytes: ... + def decrypt( + self, + nonce: bytes, + data: bytes, + associated_data: bytes | None, + ) -> bytes: ... + +class AESCCM: + def __init__(self, key: bytes, tag_length: int = 16) -> None: ... + @staticmethod + def generate_key(key_size: int) -> bytes: ... + def encrypt( + self, + nonce: bytes, + data: bytes, + associated_data: bytes | None, + ) -> bytes: ... + def decrypt( + self, + nonce: bytes, + data: bytes, + associated_data: bytes | None, + ) -> bytes: ... + +class AESSIV: + def __init__(self, key: bytes) -> None: ... + @staticmethod + def generate_key(key_size: int) -> bytes: ... + def encrypt( + self, + data: bytes, + associated_data: list[bytes] | None, + ) -> bytes: ... + def decrypt( + self, + data: bytes, + associated_data: list[bytes] | None, + ) -> bytes: ... + +class AESOCB3: + def __init__(self, key: bytes) -> None: ... + @staticmethod + def generate_key(key_size: int) -> bytes: ... + def encrypt( + self, + nonce: bytes, + data: bytes, + associated_data: bytes | None, + ) -> bytes: ... + def decrypt( + self, + nonce: bytes, + data: bytes, + associated_data: bytes | None, + ) -> bytes: ... + +class AESGCMSIV: + def __init__(self, key: bytes) -> None: ... + @staticmethod + def generate_key(key_size: int) -> bytes: ... + def encrypt( + self, + nonce: bytes, + data: bytes, + associated_data: bytes | None, + ) -> bytes: ... + def decrypt( + self, + nonce: bytes, + data: bytes, + associated_data: bytes | None, + ) -> bytes: ... diff --git a/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/ciphers.pyi b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/ciphers.pyi new file mode 100644 index 0000000..759f3b5 --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/ciphers.pyi @@ -0,0 +1,38 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +import typing + +from cryptography.hazmat.primitives import ciphers +from cryptography.hazmat.primitives.ciphers import modes + +@typing.overload +def create_encryption_ctx( + algorithm: ciphers.CipherAlgorithm, mode: modes.ModeWithAuthenticationTag +) -> ciphers.AEADEncryptionContext: ... +@typing.overload +def create_encryption_ctx( + algorithm: ciphers.CipherAlgorithm, mode: modes.Mode +) -> ciphers.CipherContext: ... +@typing.overload +def create_decryption_ctx( + algorithm: ciphers.CipherAlgorithm, mode: modes.ModeWithAuthenticationTag +) -> ciphers.AEADDecryptionContext: ... +@typing.overload +def create_decryption_ctx( + algorithm: ciphers.CipherAlgorithm, mode: modes.Mode +) -> ciphers.CipherContext: ... +def cipher_supported( + algorithm: ciphers.CipherAlgorithm, mode: modes.Mode +) -> bool: ... +def _advance( + ctx: ciphers.AEADEncryptionContext | ciphers.AEADDecryptionContext, n: int +) -> None: ... +def _advance_aad( + ctx: ciphers.AEADEncryptionContext | ciphers.AEADDecryptionContext, n: int +) -> None: ... + +class CipherContext: ... +class AEADEncryptionContext: ... +class AEADDecryptionContext: ... diff --git a/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/cmac.pyi b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/cmac.pyi new file mode 100644 index 0000000..9c03508 --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/cmac.pyi @@ -0,0 +1,18 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +import typing + +from cryptography.hazmat.primitives import ciphers + +class CMAC: + def __init__( + self, + algorithm: ciphers.BlockCipherAlgorithm, + backend: typing.Any = None, + ) -> None: ... + def update(self, data: bytes) -> None: ... + def finalize(self) -> bytes: ... + def verify(self, signature: bytes) -> None: ... + def copy(self) -> CMAC: ... diff --git a/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/dh.pyi b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/dh.pyi new file mode 100644 index 0000000..08733d7 --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/dh.pyi @@ -0,0 +1,51 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +import typing + +from cryptography.hazmat.primitives.asymmetric import dh + +MIN_MODULUS_SIZE: int + +class DHPrivateKey: ... +class DHPublicKey: ... +class DHParameters: ... + +class DHPrivateNumbers: + def __init__(self, x: int, public_numbers: DHPublicNumbers) -> None: ... + def private_key(self, backend: typing.Any = None) -> dh.DHPrivateKey: ... + @property + def x(self) -> int: ... + @property + def public_numbers(self) -> DHPublicNumbers: ... + +class DHPublicNumbers: + def __init__( + self, y: int, parameter_numbers: DHParameterNumbers + ) -> None: ... + def public_key(self, backend: typing.Any = None) -> dh.DHPublicKey: ... + @property + def y(self) -> int: ... + @property + def parameter_numbers(self) -> DHParameterNumbers: ... + +class DHParameterNumbers: + def __init__(self, p: int, g: int, q: int | None = None) -> None: ... + def parameters(self, backend: typing.Any = None) -> dh.DHParameters: ... + @property + def p(self) -> int: ... + @property + def g(self) -> int: ... + @property + def q(self) -> int | None: ... + +def generate_parameters( + generator: int, key_size: int, backend: typing.Any = None +) -> dh.DHParameters: ... +def from_pem_parameters( + data: bytes, backend: typing.Any = None +) -> dh.DHParameters: ... +def from_der_parameters( + data: bytes, backend: typing.Any = None +) -> dh.DHParameters: ... diff --git a/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/dsa.pyi b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/dsa.pyi new file mode 100644 index 0000000..0922a4c --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/dsa.pyi @@ -0,0 +1,41 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +import typing + +from cryptography.hazmat.primitives.asymmetric import dsa + +class DSAPrivateKey: ... +class DSAPublicKey: ... +class DSAParameters: ... + +class DSAPrivateNumbers: + def __init__(self, x: int, public_numbers: DSAPublicNumbers) -> None: ... + @property + def x(self) -> int: ... + @property + def public_numbers(self) -> DSAPublicNumbers: ... + def private_key(self, backend: typing.Any = None) -> dsa.DSAPrivateKey: ... + +class DSAPublicNumbers: + def __init__( + self, y: int, parameter_numbers: DSAParameterNumbers + ) -> None: ... + @property + def y(self) -> int: ... + @property + def parameter_numbers(self) -> DSAParameterNumbers: ... + def public_key(self, backend: typing.Any = None) -> dsa.DSAPublicKey: ... + +class DSAParameterNumbers: + def __init__(self, p: int, q: int, g: int) -> None: ... + @property + def p(self) -> int: ... + @property + def q(self) -> int: ... + @property + def g(self) -> int: ... + def parameters(self, backend: typing.Any = None) -> dsa.DSAParameters: ... + +def generate_parameters(key_size: int) -> dsa.DSAParameters: ... diff --git a/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/ec.pyi b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/ec.pyi new file mode 100644 index 0000000..5c3b7bf --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/ec.pyi @@ -0,0 +1,52 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +import typing + +from cryptography.hazmat.primitives.asymmetric import ec + +class ECPrivateKey: ... +class ECPublicKey: ... + +class EllipticCurvePrivateNumbers: + def __init__( + self, private_value: int, public_numbers: EllipticCurvePublicNumbers + ) -> None: ... + def private_key( + self, backend: typing.Any = None + ) -> ec.EllipticCurvePrivateKey: ... + @property + def private_value(self) -> int: ... + @property + def public_numbers(self) -> EllipticCurvePublicNumbers: ... + +class EllipticCurvePublicNumbers: + def __init__(self, x: int, y: int, curve: ec.EllipticCurve) -> None: ... + def public_key( + self, backend: typing.Any = None + ) -> ec.EllipticCurvePublicKey: ... + @property + def x(self) -> int: ... + @property + def y(self) -> int: ... + @property + def curve(self) -> ec.EllipticCurve: ... + def __eq__(self, other: object) -> bool: ... + +def curve_supported(curve: ec.EllipticCurve) -> bool: ... +def generate_private_key( + curve: ec.EllipticCurve, backend: typing.Any = None +) -> ec.EllipticCurvePrivateKey: ... +def from_private_numbers( + numbers: ec.EllipticCurvePrivateNumbers, +) -> ec.EllipticCurvePrivateKey: ... +def from_public_numbers( + numbers: ec.EllipticCurvePublicNumbers, +) -> ec.EllipticCurvePublicKey: ... +def from_public_bytes( + curve: ec.EllipticCurve, data: bytes +) -> ec.EllipticCurvePublicKey: ... +def derive_private_key( + private_value: int, curve: ec.EllipticCurve +) -> ec.EllipticCurvePrivateKey: ... diff --git a/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/ed25519.pyi b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/ed25519.pyi new file mode 100644 index 0000000..5233f9a --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/ed25519.pyi @@ -0,0 +1,12 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +from cryptography.hazmat.primitives.asymmetric import ed25519 + +class Ed25519PrivateKey: ... +class Ed25519PublicKey: ... + +def generate_key() -> ed25519.Ed25519PrivateKey: ... +def from_private_bytes(data: bytes) -> ed25519.Ed25519PrivateKey: ... +def from_public_bytes(data: bytes) -> ed25519.Ed25519PublicKey: ... diff --git a/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/ed448.pyi b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/ed448.pyi new file mode 100644 index 0000000..7a06520 --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/ed448.pyi @@ -0,0 +1,12 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +from cryptography.hazmat.primitives.asymmetric import ed448 + +class Ed448PrivateKey: ... +class Ed448PublicKey: ... + +def generate_key() -> ed448.Ed448PrivateKey: ... +def from_private_bytes(data: bytes) -> ed448.Ed448PrivateKey: ... +def from_public_bytes(data: bytes) -> ed448.Ed448PublicKey: ... diff --git a/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/hashes.pyi b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/hashes.pyi new file mode 100644 index 0000000..ca5f42a --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/hashes.pyi @@ -0,0 +1,17 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +import typing + +from cryptography.hazmat.primitives import hashes + +class Hash(hashes.HashContext): + def __init__( + self, algorithm: hashes.HashAlgorithm, backend: typing.Any = None + ) -> None: ... + @property + def algorithm(self) -> hashes.HashAlgorithm: ... + def update(self, data: bytes) -> None: ... + def finalize(self) -> bytes: ... + def copy(self) -> Hash: ... diff --git a/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/hmac.pyi b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/hmac.pyi new file mode 100644 index 0000000..e38d9b5 --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/hmac.pyi @@ -0,0 +1,21 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +import typing + +from cryptography.hazmat.primitives import hashes + +class HMAC(hashes.HashContext): + def __init__( + self, + key: bytes, + algorithm: hashes.HashAlgorithm, + backend: typing.Any = None, + ) -> None: ... + @property + def algorithm(self) -> hashes.HashAlgorithm: ... + def update(self, data: bytes) -> None: ... + def finalize(self) -> bytes: ... + def verify(self, signature: bytes) -> None: ... + def copy(self) -> HMAC: ... diff --git a/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/kdf.pyi b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/kdf.pyi new file mode 100644 index 0000000..034a8fe --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/kdf.pyi @@ -0,0 +1,22 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +from cryptography.hazmat.primitives.hashes import HashAlgorithm + +def derive_pbkdf2_hmac( + key_material: bytes, + algorithm: HashAlgorithm, + salt: bytes, + iterations: int, + length: int, +) -> bytes: ... +def derive_scrypt( + key_material: bytes, + salt: bytes, + n: int, + r: int, + p: int, + max_mem: int, + length: int, +) -> bytes: ... diff --git a/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/keys.pyi b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/keys.pyi new file mode 100644 index 0000000..6815b7d --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/keys.pyi @@ -0,0 +1,33 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +import typing + +from cryptography.hazmat.primitives.asymmetric.types import ( + PrivateKeyTypes, + PublicKeyTypes, +) + +def load_der_private_key( + data: bytes, + password: bytes | None, + backend: typing.Any = None, + *, + unsafe_skip_rsa_key_validation: bool = False, +) -> PrivateKeyTypes: ... +def load_pem_private_key( + data: bytes, + password: bytes | None, + backend: typing.Any = None, + *, + unsafe_skip_rsa_key_validation: bool = False, +) -> PrivateKeyTypes: ... +def load_der_public_key( + data: bytes, + backend: typing.Any = None, +) -> PublicKeyTypes: ... +def load_pem_public_key( + data: bytes, + backend: typing.Any = None, +) -> PublicKeyTypes: ... diff --git a/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/poly1305.pyi b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/poly1305.pyi new file mode 100644 index 0000000..2e9b0a9 --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/poly1305.pyi @@ -0,0 +1,13 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +class Poly1305: + def __init__(self, key: bytes) -> None: ... + @staticmethod + def generate_tag(key: bytes, data: bytes) -> bytes: ... + @staticmethod + def verify_tag(key: bytes, data: bytes, tag: bytes) -> None: ... + def update(self, data: bytes) -> None: ... + def finalize(self) -> bytes: ... + def verify(self, tag: bytes) -> None: ... diff --git a/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/rsa.pyi b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/rsa.pyi new file mode 100644 index 0000000..ef7752d --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/rsa.pyi @@ -0,0 +1,55 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +import typing + +from cryptography.hazmat.primitives.asymmetric import rsa + +class RSAPrivateKey: ... +class RSAPublicKey: ... + +class RSAPrivateNumbers: + def __init__( + self, + p: int, + q: int, + d: int, + dmp1: int, + dmq1: int, + iqmp: int, + public_numbers: RSAPublicNumbers, + ) -> None: ... + @property + def p(self) -> int: ... + @property + def q(self) -> int: ... + @property + def d(self) -> int: ... + @property + def dmp1(self) -> int: ... + @property + def dmq1(self) -> int: ... + @property + def iqmp(self) -> int: ... + @property + def public_numbers(self) -> RSAPublicNumbers: ... + def private_key( + self, + backend: typing.Any = None, + *, + unsafe_skip_rsa_key_validation: bool = False, + ) -> rsa.RSAPrivateKey: ... + +class RSAPublicNumbers: + def __init__(self, e: int, n: int) -> None: ... + @property + def n(self) -> int: ... + @property + def e(self) -> int: ... + def public_key(self, backend: typing.Any = None) -> rsa.RSAPublicKey: ... + +def generate_private_key( + public_exponent: int, + key_size: int, +) -> rsa.RSAPrivateKey: ... diff --git a/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/x25519.pyi b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/x25519.pyi new file mode 100644 index 0000000..da0f3ec --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/x25519.pyi @@ -0,0 +1,12 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +from cryptography.hazmat.primitives.asymmetric import x25519 + +class X25519PrivateKey: ... +class X25519PublicKey: ... + +def generate_key() -> x25519.X25519PrivateKey: ... +def from_private_bytes(data: bytes) -> x25519.X25519PrivateKey: ... +def from_public_bytes(data: bytes) -> x25519.X25519PublicKey: ... diff --git a/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/x448.pyi b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/x448.pyi new file mode 100644 index 0000000..e51cfeb --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/x448.pyi @@ -0,0 +1,12 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +from cryptography.hazmat.primitives.asymmetric import x448 + +class X448PrivateKey: ... +class X448PublicKey: ... + +def generate_key() -> x448.X448PrivateKey: ... +def from_private_bytes(data: bytes) -> x448.X448PrivateKey: ... +def from_public_bytes(data: bytes) -> x448.X448PublicKey: ... diff --git a/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/pkcs12.pyi b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/pkcs12.pyi new file mode 100644 index 0000000..40514c4 --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/pkcs12.pyi @@ -0,0 +1,46 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +import typing + +from cryptography import x509 +from cryptography.hazmat.primitives.asymmetric.types import PrivateKeyTypes +from cryptography.hazmat.primitives.serialization import ( + KeySerializationEncryption, +) +from cryptography.hazmat.primitives.serialization.pkcs12 import ( + PKCS12KeyAndCertificates, + PKCS12PrivateKeyTypes, +) + +class PKCS12Certificate: + def __init__( + self, cert: x509.Certificate, friendly_name: bytes | None + ) -> None: ... + @property + def friendly_name(self) -> bytes | None: ... + @property + def certificate(self) -> x509.Certificate: ... + +def load_key_and_certificates( + data: bytes, + password: bytes | None, + backend: typing.Any = None, +) -> tuple[ + PrivateKeyTypes | None, + x509.Certificate | None, + list[x509.Certificate], +]: ... +def load_pkcs12( + data: bytes, + password: bytes | None, + backend: typing.Any = None, +) -> PKCS12KeyAndCertificates: ... +def serialize_key_and_certificates( + name: bytes | None, + key: PKCS12PrivateKeyTypes | None, + cert: x509.Certificate | None, + cas: typing.Iterable[x509.Certificate | PKCS12Certificate] | None, + encryption_algorithm: KeySerializationEncryption, +) -> bytes: ... diff --git a/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/pkcs7.pyi b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/pkcs7.pyi new file mode 100644 index 0000000..a72120a --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/pkcs7.pyi @@ -0,0 +1,30 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +import typing + +from cryptography import x509 +from cryptography.hazmat.primitives import serialization +from cryptography.hazmat.primitives.serialization import pkcs7 + +def serialize_certificates( + certs: list[x509.Certificate], + encoding: serialization.Encoding, +) -> bytes: ... +def encrypt_and_serialize( + builder: pkcs7.PKCS7EnvelopeBuilder, + encoding: serialization.Encoding, + options: typing.Iterable[pkcs7.PKCS7Options], +) -> bytes: ... +def sign_and_serialize( + builder: pkcs7.PKCS7SignatureBuilder, + encoding: serialization.Encoding, + options: typing.Iterable[pkcs7.PKCS7Options], +) -> bytes: ... +def load_pem_pkcs7_certificates( + data: bytes, +) -> list[x509.Certificate]: ... +def load_der_pkcs7_certificates( + data: bytes, +) -> list[x509.Certificate]: ... diff --git a/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/test_support.pyi b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/test_support.pyi new file mode 100644 index 0000000..a53ee25 --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/test_support.pyi @@ -0,0 +1,29 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +from cryptography import x509 +from cryptography.hazmat.primitives import serialization +from cryptography.hazmat.primitives.serialization import pkcs7 + +class TestCertificate: + not_after_tag: int + not_before_tag: int + issuer_value_tags: list[int] + subject_value_tags: list[int] + +def test_parse_certificate(data: bytes) -> TestCertificate: ... +def pkcs7_decrypt( + encoding: serialization.Encoding, + msg: bytes, + pkey: serialization.pkcs7.PKCS7PrivateKeyTypes, + cert_recipient: x509.Certificate, + options: list[pkcs7.PKCS7Options], +) -> bytes: ... +def pkcs7_verify( + encoding: serialization.Encoding, + sig: bytes, + msg: bytes | None, + certs: list[x509.Certificate], + options: list[pkcs7.PKCS7Options], +) -> None: ... diff --git a/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/x509.pyi b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/x509.pyi new file mode 100644 index 0000000..aa85657 --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/x509.pyi @@ -0,0 +1,108 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +import datetime +import typing + +from cryptography import x509 +from cryptography.hazmat.primitives import hashes +from cryptography.hazmat.primitives.asymmetric.padding import PSS, PKCS1v15 +from cryptography.hazmat.primitives.asymmetric.types import PrivateKeyTypes + +def load_pem_x509_certificate( + data: bytes, backend: typing.Any = None +) -> x509.Certificate: ... +def load_der_x509_certificate( + data: bytes, backend: typing.Any = None +) -> x509.Certificate: ... +def load_pem_x509_certificates( + data: bytes, +) -> list[x509.Certificate]: ... +def load_pem_x509_crl( + data: bytes, backend: typing.Any = None +) -> x509.CertificateRevocationList: ... +def load_der_x509_crl( + data: bytes, backend: typing.Any = None +) -> x509.CertificateRevocationList: ... +def load_pem_x509_csr( + data: bytes, backend: typing.Any = None +) -> x509.CertificateSigningRequest: ... +def load_der_x509_csr( + data: bytes, backend: typing.Any = None +) -> x509.CertificateSigningRequest: ... +def encode_name_bytes(name: x509.Name) -> bytes: ... +def encode_extension_value(extension: x509.ExtensionType) -> bytes: ... +def create_x509_certificate( + builder: x509.CertificateBuilder, + private_key: PrivateKeyTypes, + hash_algorithm: hashes.HashAlgorithm | None, + rsa_padding: PKCS1v15 | PSS | None, +) -> x509.Certificate: ... +def create_x509_csr( + builder: x509.CertificateSigningRequestBuilder, + private_key: PrivateKeyTypes, + hash_algorithm: hashes.HashAlgorithm | None, + rsa_padding: PKCS1v15 | PSS | None, +) -> x509.CertificateSigningRequest: ... +def create_x509_crl( + builder: x509.CertificateRevocationListBuilder, + private_key: PrivateKeyTypes, + hash_algorithm: hashes.HashAlgorithm | None, + rsa_padding: PKCS1v15 | PSS | None, +) -> x509.CertificateRevocationList: ... + +class Sct: ... +class Certificate: ... +class RevokedCertificate: ... +class CertificateRevocationList: ... +class CertificateSigningRequest: ... + +class PolicyBuilder: + def time(self, new_time: datetime.datetime) -> PolicyBuilder: ... + def store(self, new_store: Store) -> PolicyBuilder: ... + def max_chain_depth(self, new_max_chain_depth: int) -> PolicyBuilder: ... + def build_client_verifier(self) -> ClientVerifier: ... + def build_server_verifier( + self, subject: x509.verification.Subject + ) -> ServerVerifier: ... + +class VerifiedClient: + @property + def subjects(self) -> list[x509.GeneralName]: ... + @property + def chain(self) -> list[x509.Certificate]: ... + +class ClientVerifier: + @property + def validation_time(self) -> datetime.datetime: ... + @property + def store(self) -> Store: ... + @property + def max_chain_depth(self) -> int: ... + def verify( + self, + leaf: x509.Certificate, + intermediates: list[x509.Certificate], + ) -> VerifiedClient: ... + +class ServerVerifier: + @property + def subject(self) -> x509.verification.Subject: ... + @property + def validation_time(self) -> datetime.datetime: ... + @property + def store(self) -> Store: ... + @property + def max_chain_depth(self) -> int: ... + def verify( + self, + leaf: x509.Certificate, + intermediates: list[x509.Certificate], + ) -> list[x509.Certificate]: ... + +class Store: + def __init__(self, certs: list[x509.Certificate]) -> None: ... + +class VerificationError(Exception): + pass diff --git a/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/openssl/__init__.py b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/openssl/__init__.py new file mode 100644 index 0000000..b509336 --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/openssl/__init__.py @@ -0,0 +1,3 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. diff --git a/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/openssl/__pycache__/__init__.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/openssl/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..c8561bc Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/openssl/__pycache__/__init__.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/openssl/__pycache__/_conditional.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/openssl/__pycache__/_conditional.cpython-312.pyc new file mode 100644 index 0000000..95c3f83 Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/openssl/__pycache__/_conditional.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/openssl/__pycache__/binding.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/openssl/__pycache__/binding.cpython-312.pyc new file mode 100644 index 0000000..61b3f55 Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/openssl/__pycache__/binding.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/openssl/_conditional.py b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/openssl/_conditional.py new file mode 100644 index 0000000..73c06f7 --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/openssl/_conditional.py @@ -0,0 +1,183 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +from __future__ import annotations + + +def cryptography_has_set_cert_cb() -> list[str]: + return [ + "SSL_CTX_set_cert_cb", + "SSL_set_cert_cb", + ] + + +def cryptography_has_ssl_st() -> list[str]: + return [ + "SSL_ST_BEFORE", + "SSL_ST_OK", + "SSL_ST_INIT", + "SSL_ST_RENEGOTIATE", + ] + + +def cryptography_has_tls_st() -> list[str]: + return [ + "TLS_ST_BEFORE", + "TLS_ST_OK", + ] + + +def cryptography_has_ssl_sigalgs() -> list[str]: + return [ + "SSL_CTX_set1_sigalgs_list", + ] + + +def cryptography_has_psk() -> list[str]: + return [ + "SSL_CTX_use_psk_identity_hint", + "SSL_CTX_set_psk_server_callback", + "SSL_CTX_set_psk_client_callback", + ] + + +def cryptography_has_psk_tlsv13() -> list[str]: + return [ + "SSL_CTX_set_psk_find_session_callback", + "SSL_CTX_set_psk_use_session_callback", + "Cryptography_SSL_SESSION_new", + "SSL_CIPHER_find", + "SSL_SESSION_set1_master_key", + "SSL_SESSION_set_cipher", + "SSL_SESSION_set_protocol_version", + ] + + +def cryptography_has_custom_ext() -> list[str]: + return [ + "SSL_CTX_add_client_custom_ext", + "SSL_CTX_add_server_custom_ext", + "SSL_extension_supported", + ] + + +def cryptography_has_tlsv13_functions() -> list[str]: + return [ + "SSL_VERIFY_POST_HANDSHAKE", + "SSL_CTX_set_ciphersuites", + "SSL_verify_client_post_handshake", + "SSL_CTX_set_post_handshake_auth", + "SSL_set_post_handshake_auth", + "SSL_SESSION_get_max_early_data", + "SSL_write_early_data", + "SSL_read_early_data", + "SSL_CTX_set_max_early_data", + ] + + +def cryptography_has_engine() -> list[str]: + return [ + "ENGINE_by_id", + "ENGINE_init", + "ENGINE_finish", + "ENGINE_get_default_RAND", + "ENGINE_set_default_RAND", + "ENGINE_unregister_RAND", + "ENGINE_ctrl_cmd", + "ENGINE_free", + "ENGINE_get_name", + "ENGINE_ctrl_cmd_string", + "ENGINE_load_builtin_engines", + "ENGINE_load_private_key", + "ENGINE_load_public_key", + "SSL_CTX_set_client_cert_engine", + ] + + +def cryptography_has_verified_chain() -> list[str]: + return [ + "SSL_get0_verified_chain", + ] + + +def cryptography_has_srtp() -> list[str]: + return [ + "SSL_CTX_set_tlsext_use_srtp", + "SSL_set_tlsext_use_srtp", + "SSL_get_selected_srtp_profile", + ] + + +def cryptography_has_op_no_renegotiation() -> list[str]: + return [ + "SSL_OP_NO_RENEGOTIATION", + ] + + +def cryptography_has_dtls_get_data_mtu() -> list[str]: + return [ + "DTLS_get_data_mtu", + ] + + +def cryptography_has_ssl_cookie() -> list[str]: + return [ + "SSL_OP_COOKIE_EXCHANGE", + "DTLSv1_listen", + "SSL_CTX_set_cookie_generate_cb", + "SSL_CTX_set_cookie_verify_cb", + ] + + +def cryptography_has_prime_checks() -> list[str]: + return [ + "BN_prime_checks_for_size", + ] + + +def cryptography_has_unexpected_eof_while_reading() -> list[str]: + return ["SSL_R_UNEXPECTED_EOF_WHILE_READING"] + + +def cryptography_has_ssl_op_ignore_unexpected_eof() -> list[str]: + return [ + "SSL_OP_IGNORE_UNEXPECTED_EOF", + ] + + +def cryptography_has_get_extms_support() -> list[str]: + return ["SSL_get_extms_support"] + + +# This is a mapping of +# {condition: function-returning-names-dependent-on-that-condition} so we can +# loop over them and delete unsupported names at runtime. It will be removed +# when cffi supports #if in cdef. We use functions instead of just a dict of +# lists so we can use coverage to measure which are used. +CONDITIONAL_NAMES = { + "Cryptography_HAS_SET_CERT_CB": cryptography_has_set_cert_cb, + "Cryptography_HAS_SSL_ST": cryptography_has_ssl_st, + "Cryptography_HAS_TLS_ST": cryptography_has_tls_st, + "Cryptography_HAS_SIGALGS": cryptography_has_ssl_sigalgs, + "Cryptography_HAS_PSK": cryptography_has_psk, + "Cryptography_HAS_PSK_TLSv1_3": cryptography_has_psk_tlsv13, + "Cryptography_HAS_CUSTOM_EXT": cryptography_has_custom_ext, + "Cryptography_HAS_TLSv1_3_FUNCTIONS": cryptography_has_tlsv13_functions, + "Cryptography_HAS_ENGINE": cryptography_has_engine, + "Cryptography_HAS_VERIFIED_CHAIN": cryptography_has_verified_chain, + "Cryptography_HAS_SRTP": cryptography_has_srtp, + "Cryptography_HAS_OP_NO_RENEGOTIATION": ( + cryptography_has_op_no_renegotiation + ), + "Cryptography_HAS_DTLS_GET_DATA_MTU": cryptography_has_dtls_get_data_mtu, + "Cryptography_HAS_SSL_COOKIE": cryptography_has_ssl_cookie, + "Cryptography_HAS_PRIME_CHECKS": cryptography_has_prime_checks, + "Cryptography_HAS_UNEXPECTED_EOF_WHILE_READING": ( + cryptography_has_unexpected_eof_while_reading + ), + "Cryptography_HAS_SSL_OP_IGNORE_UNEXPECTED_EOF": ( + cryptography_has_ssl_op_ignore_unexpected_eof + ), + "Cryptography_HAS_GET_EXTMS_SUPPORT": cryptography_has_get_extms_support, +} diff --git a/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/openssl/binding.py b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/openssl/binding.py new file mode 100644 index 0000000..d4dfeef --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/openssl/binding.py @@ -0,0 +1,121 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +from __future__ import annotations + +import os +import sys +import threading +import types +import typing +import warnings + +import cryptography +from cryptography.exceptions import InternalError +from cryptography.hazmat.bindings._rust import _openssl, openssl +from cryptography.hazmat.bindings.openssl._conditional import CONDITIONAL_NAMES + + +def _openssl_assert(ok: bool) -> None: + if not ok: + errors = openssl.capture_error_stack() + + raise InternalError( + "Unknown OpenSSL error. This error is commonly encountered when " + "another library is not cleaning up the OpenSSL error stack. If " + "you are using cryptography with another library that uses " + "OpenSSL try disabling it before reporting a bug. Otherwise " + "please file an issue at https://github.com/pyca/cryptography/" + "issues with information on how to reproduce " + f"this. ({errors!r})", + errors, + ) + + +def build_conditional_library( + lib: typing.Any, + conditional_names: dict[str, typing.Callable[[], list[str]]], +) -> typing.Any: + conditional_lib = types.ModuleType("lib") + conditional_lib._original_lib = lib # type: ignore[attr-defined] + excluded_names = set() + for condition, names_cb in conditional_names.items(): + if not getattr(lib, condition): + excluded_names.update(names_cb()) + + for attr in dir(lib): + if attr not in excluded_names: + setattr(conditional_lib, attr, getattr(lib, attr)) + + return conditional_lib + + +class Binding: + """ + OpenSSL API wrapper. + """ + + lib: typing.ClassVar = None + ffi = _openssl.ffi + _lib_loaded = False + _init_lock = threading.Lock() + + def __init__(self) -> None: + self._ensure_ffi_initialized() + + @classmethod + def _ensure_ffi_initialized(cls) -> None: + with cls._init_lock: + if not cls._lib_loaded: + cls.lib = build_conditional_library( + _openssl.lib, CONDITIONAL_NAMES + ) + cls._lib_loaded = True + + @classmethod + def init_static_locks(cls) -> None: + cls._ensure_ffi_initialized() + + +def _verify_package_version(version: str) -> None: + # Occasionally we run into situations where the version of the Python + # package does not match the version of the shared object that is loaded. + # This may occur in environments where multiple versions of cryptography + # are installed and available in the python path. To avoid errors cropping + # up later this code checks that the currently imported package and the + # shared object that were loaded have the same version and raise an + # ImportError if they do not + so_package_version = _openssl.ffi.string( + _openssl.lib.CRYPTOGRAPHY_PACKAGE_VERSION + ) + if version.encode("ascii") != so_package_version: + raise ImportError( + "The version of cryptography does not match the loaded " + "shared object. This can happen if you have multiple copies of " + "cryptography installed in your Python path. Please try creating " + "a new virtual environment to resolve this issue. " + f"Loaded python version: {version}, " + f"shared object version: {so_package_version}" + ) + + _openssl_assert( + _openssl.lib.OpenSSL_version_num() == openssl.openssl_version(), + ) + + +_verify_package_version(cryptography.__version__) + +Binding.init_static_locks() + +if ( + sys.platform == "win32" + and os.environ.get("PROCESSOR_ARCHITEW6432") is not None +): + warnings.warn( + "You are using cryptography on a 32-bit Python on a 64-bit Windows " + "Operating System. Cryptography will be significantly faster if you " + "switch to using a 64-bit Python.", + UserWarning, + stacklevel=2, + ) diff --git a/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/decrepit/__init__.py b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/decrepit/__init__.py new file mode 100644 index 0000000..41d7318 --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/decrepit/__init__.py @@ -0,0 +1,5 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +from __future__ import annotations diff --git a/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/decrepit/__pycache__/__init__.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/decrepit/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..b6505bc Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/decrepit/__pycache__/__init__.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/decrepit/ciphers/__init__.py b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/decrepit/ciphers/__init__.py new file mode 100644 index 0000000..41d7318 --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/decrepit/ciphers/__init__.py @@ -0,0 +1,5 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +from __future__ import annotations diff --git a/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/decrepit/ciphers/__pycache__/__init__.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/decrepit/ciphers/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..b05e439 Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/decrepit/ciphers/__pycache__/__init__.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/decrepit/ciphers/__pycache__/algorithms.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/decrepit/ciphers/__pycache__/algorithms.cpython-312.pyc new file mode 100644 index 0000000..870fcb0 Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/decrepit/ciphers/__pycache__/algorithms.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/decrepit/ciphers/algorithms.py b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/decrepit/ciphers/algorithms.py new file mode 100644 index 0000000..a7d4aa3 --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/decrepit/ciphers/algorithms.py @@ -0,0 +1,107 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +from __future__ import annotations + +from cryptography.hazmat.primitives._cipheralgorithm import ( + BlockCipherAlgorithm, + CipherAlgorithm, + _verify_key_size, +) + + +class ARC4(CipherAlgorithm): + name = "RC4" + key_sizes = frozenset([40, 56, 64, 80, 128, 160, 192, 256]) + + def __init__(self, key: bytes): + self.key = _verify_key_size(self, key) + + @property + def key_size(self) -> int: + return len(self.key) * 8 + + +class TripleDES(BlockCipherAlgorithm): + name = "3DES" + block_size = 64 + key_sizes = frozenset([64, 128, 192]) + + def __init__(self, key: bytes): + if len(key) == 8: + key += key + key + elif len(key) == 16: + key += key[:8] + self.key = _verify_key_size(self, key) + + @property + def key_size(self) -> int: + return len(self.key) * 8 + + +class Blowfish(BlockCipherAlgorithm): + name = "Blowfish" + block_size = 64 + key_sizes = frozenset(range(32, 449, 8)) + + def __init__(self, key: bytes): + self.key = _verify_key_size(self, key) + + @property + def key_size(self) -> int: + return len(self.key) * 8 + + +class CAST5(BlockCipherAlgorithm): + name = "CAST5" + block_size = 64 + key_sizes = frozenset(range(40, 129, 8)) + + def __init__(self, key: bytes): + self.key = _verify_key_size(self, key) + + @property + def key_size(self) -> int: + return len(self.key) * 8 + + +class SEED(BlockCipherAlgorithm): + name = "SEED" + block_size = 128 + key_sizes = frozenset([128]) + + def __init__(self, key: bytes): + self.key = _verify_key_size(self, key) + + @property + def key_size(self) -> int: + return len(self.key) * 8 + + +class IDEA(BlockCipherAlgorithm): + name = "IDEA" + block_size = 64 + key_sizes = frozenset([128]) + + def __init__(self, key: bytes): + self.key = _verify_key_size(self, key) + + @property + def key_size(self) -> int: + return len(self.key) * 8 + + +# This class only allows RC2 with a 128-bit key. No support for +# effective key bits or other key sizes is provided. +class RC2(BlockCipherAlgorithm): + name = "RC2" + block_size = 64 + key_sizes = frozenset([128]) + + def __init__(self, key: bytes): + self.key = _verify_key_size(self, key) + + @property + def key_size(self) -> int: + return len(self.key) * 8 diff --git a/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/__init__.py b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/__init__.py new file mode 100644 index 0000000..b509336 --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/__init__.py @@ -0,0 +1,3 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. diff --git a/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/__pycache__/__init__.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..1325c30 Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/__pycache__/__init__.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/__pycache__/_asymmetric.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/__pycache__/_asymmetric.cpython-312.pyc new file mode 100644 index 0000000..55abc3d Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/__pycache__/_asymmetric.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/__pycache__/_cipheralgorithm.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/__pycache__/_cipheralgorithm.cpython-312.pyc new file mode 100644 index 0000000..71d0c11 Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/__pycache__/_cipheralgorithm.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/__pycache__/_serialization.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/__pycache__/_serialization.cpython-312.pyc new file mode 100644 index 0000000..7845659 Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/__pycache__/_serialization.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/__pycache__/cmac.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/__pycache__/cmac.cpython-312.pyc new file mode 100644 index 0000000..12724cd Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/__pycache__/cmac.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/__pycache__/constant_time.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/__pycache__/constant_time.cpython-312.pyc new file mode 100644 index 0000000..75a72ba Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/__pycache__/constant_time.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/__pycache__/hashes.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/__pycache__/hashes.cpython-312.pyc new file mode 100644 index 0000000..36696dd Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/__pycache__/hashes.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/__pycache__/hmac.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/__pycache__/hmac.cpython-312.pyc new file mode 100644 index 0000000..b494bd0 Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/__pycache__/hmac.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/__pycache__/keywrap.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/__pycache__/keywrap.cpython-312.pyc new file mode 100644 index 0000000..78325ad Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/__pycache__/keywrap.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/__pycache__/padding.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/__pycache__/padding.cpython-312.pyc new file mode 100644 index 0000000..1127615 Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/__pycache__/padding.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/__pycache__/poly1305.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/__pycache__/poly1305.cpython-312.pyc new file mode 100644 index 0000000..fe7a95e Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/__pycache__/poly1305.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/_asymmetric.py b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/_asymmetric.py new file mode 100644 index 0000000..ea55ffd --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/_asymmetric.py @@ -0,0 +1,19 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +from __future__ import annotations + +import abc + +# This exists to break an import cycle. It is normally accessible from the +# asymmetric padding module. + + +class AsymmetricPadding(metaclass=abc.ABCMeta): + @property + @abc.abstractmethod + def name(self) -> str: + """ + A string naming this padding (e.g. "PSS", "PKCS1"). + """ diff --git a/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/_cipheralgorithm.py b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/_cipheralgorithm.py new file mode 100644 index 0000000..588a616 --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/_cipheralgorithm.py @@ -0,0 +1,58 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +from __future__ import annotations + +import abc + +from cryptography import utils + +# This exists to break an import cycle. It is normally accessible from the +# ciphers module. + + +class CipherAlgorithm(metaclass=abc.ABCMeta): + @property + @abc.abstractmethod + def name(self) -> str: + """ + A string naming this mode (e.g. "AES", "Camellia"). + """ + + @property + @abc.abstractmethod + def key_sizes(self) -> frozenset[int]: + """ + Valid key sizes for this algorithm in bits + """ + + @property + @abc.abstractmethod + def key_size(self) -> int: + """ + The size of the key being used as an integer in bits (e.g. 128, 256). + """ + + +class BlockCipherAlgorithm(CipherAlgorithm): + key: bytes + + @property + @abc.abstractmethod + def block_size(self) -> int: + """ + The size of a block as an integer in bits (e.g. 64, 128). + """ + + +def _verify_key_size(algorithm: CipherAlgorithm, key: bytes) -> bytes: + # Verify that the key is instance of bytes + utils._check_byteslike("key", key) + + # Verify that the key size matches the expected key size + if len(key) * 8 not in algorithm.key_sizes: + raise ValueError( + f"Invalid key size ({len(key) * 8}) for {algorithm.name}." + ) + return key diff --git a/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/_serialization.py b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/_serialization.py new file mode 100644 index 0000000..4615772 --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/_serialization.py @@ -0,0 +1,169 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +from __future__ import annotations + +import abc + +from cryptography import utils +from cryptography.hazmat.primitives.hashes import HashAlgorithm + +# This exists to break an import cycle. These classes are normally accessible +# from the serialization module. + + +class PBES(utils.Enum): + PBESv1SHA1And3KeyTripleDESCBC = "PBESv1 using SHA1 and 3-Key TripleDES" + PBESv2SHA256AndAES256CBC = "PBESv2 using SHA256 PBKDF2 and AES256 CBC" + + +class Encoding(utils.Enum): + PEM = "PEM" + DER = "DER" + OpenSSH = "OpenSSH" + Raw = "Raw" + X962 = "ANSI X9.62" + SMIME = "S/MIME" + + +class PrivateFormat(utils.Enum): + PKCS8 = "PKCS8" + TraditionalOpenSSL = "TraditionalOpenSSL" + Raw = "Raw" + OpenSSH = "OpenSSH" + PKCS12 = "PKCS12" + + def encryption_builder(self) -> KeySerializationEncryptionBuilder: + if self not in (PrivateFormat.OpenSSH, PrivateFormat.PKCS12): + raise ValueError( + "encryption_builder only supported with PrivateFormat.OpenSSH" + " and PrivateFormat.PKCS12" + ) + return KeySerializationEncryptionBuilder(self) + + +class PublicFormat(utils.Enum): + SubjectPublicKeyInfo = "X.509 subjectPublicKeyInfo with PKCS#1" + PKCS1 = "Raw PKCS#1" + OpenSSH = "OpenSSH" + Raw = "Raw" + CompressedPoint = "X9.62 Compressed Point" + UncompressedPoint = "X9.62 Uncompressed Point" + + +class ParameterFormat(utils.Enum): + PKCS3 = "PKCS3" + + +class KeySerializationEncryption(metaclass=abc.ABCMeta): + pass + + +class BestAvailableEncryption(KeySerializationEncryption): + def __init__(self, password: bytes): + if not isinstance(password, bytes) or len(password) == 0: + raise ValueError("Password must be 1 or more bytes.") + + self.password = password + + +class NoEncryption(KeySerializationEncryption): + pass + + +class KeySerializationEncryptionBuilder: + def __init__( + self, + format: PrivateFormat, + *, + _kdf_rounds: int | None = None, + _hmac_hash: HashAlgorithm | None = None, + _key_cert_algorithm: PBES | None = None, + ) -> None: + self._format = format + + self._kdf_rounds = _kdf_rounds + self._hmac_hash = _hmac_hash + self._key_cert_algorithm = _key_cert_algorithm + + def kdf_rounds(self, rounds: int) -> KeySerializationEncryptionBuilder: + if self._kdf_rounds is not None: + raise ValueError("kdf_rounds already set") + + if not isinstance(rounds, int): + raise TypeError("kdf_rounds must be an integer") + + if rounds < 1: + raise ValueError("kdf_rounds must be a positive integer") + + return KeySerializationEncryptionBuilder( + self._format, + _kdf_rounds=rounds, + _hmac_hash=self._hmac_hash, + _key_cert_algorithm=self._key_cert_algorithm, + ) + + def hmac_hash( + self, algorithm: HashAlgorithm + ) -> KeySerializationEncryptionBuilder: + if self._format is not PrivateFormat.PKCS12: + raise TypeError( + "hmac_hash only supported with PrivateFormat.PKCS12" + ) + + if self._hmac_hash is not None: + raise ValueError("hmac_hash already set") + return KeySerializationEncryptionBuilder( + self._format, + _kdf_rounds=self._kdf_rounds, + _hmac_hash=algorithm, + _key_cert_algorithm=self._key_cert_algorithm, + ) + + def key_cert_algorithm( + self, algorithm: PBES + ) -> KeySerializationEncryptionBuilder: + if self._format is not PrivateFormat.PKCS12: + raise TypeError( + "key_cert_algorithm only supported with " + "PrivateFormat.PKCS12" + ) + if self._key_cert_algorithm is not None: + raise ValueError("key_cert_algorithm already set") + return KeySerializationEncryptionBuilder( + self._format, + _kdf_rounds=self._kdf_rounds, + _hmac_hash=self._hmac_hash, + _key_cert_algorithm=algorithm, + ) + + def build(self, password: bytes) -> KeySerializationEncryption: + if not isinstance(password, bytes) or len(password) == 0: + raise ValueError("Password must be 1 or more bytes.") + + return _KeySerializationEncryption( + self._format, + password, + kdf_rounds=self._kdf_rounds, + hmac_hash=self._hmac_hash, + key_cert_algorithm=self._key_cert_algorithm, + ) + + +class _KeySerializationEncryption(KeySerializationEncryption): + def __init__( + self, + format: PrivateFormat, + password: bytes, + *, + kdf_rounds: int | None, + hmac_hash: HashAlgorithm | None, + key_cert_algorithm: PBES | None, + ): + self._format = format + self.password = password + + self._kdf_rounds = kdf_rounds + self._hmac_hash = hmac_hash + self._key_cert_algorithm = key_cert_algorithm diff --git a/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/__init__.py b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/__init__.py new file mode 100644 index 0000000..b509336 --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/__init__.py @@ -0,0 +1,3 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. diff --git a/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/__pycache__/__init__.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..5dad54c Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/__pycache__/__init__.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/__pycache__/dh.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/__pycache__/dh.cpython-312.pyc new file mode 100644 index 0000000..4ee3beb Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/__pycache__/dh.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/__pycache__/dsa.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/__pycache__/dsa.cpython-312.pyc new file mode 100644 index 0000000..c71eb21 Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/__pycache__/dsa.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/__pycache__/ec.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/__pycache__/ec.cpython-312.pyc new file mode 100644 index 0000000..12df915 Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/__pycache__/ec.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/__pycache__/ed25519.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/__pycache__/ed25519.cpython-312.pyc new file mode 100644 index 0000000..1045a89 Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/__pycache__/ed25519.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/__pycache__/ed448.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/__pycache__/ed448.cpython-312.pyc new file mode 100644 index 0000000..3dea3bc Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/__pycache__/ed448.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/__pycache__/padding.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/__pycache__/padding.cpython-312.pyc new file mode 100644 index 0000000..1418492 Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/__pycache__/padding.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/__pycache__/rsa.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/__pycache__/rsa.cpython-312.pyc new file mode 100644 index 0000000..43bb03f Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/__pycache__/rsa.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/__pycache__/types.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/__pycache__/types.cpython-312.pyc new file mode 100644 index 0000000..a77b79d Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/__pycache__/types.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/__pycache__/utils.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/__pycache__/utils.cpython-312.pyc new file mode 100644 index 0000000..3412bee Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/__pycache__/utils.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/__pycache__/x25519.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/__pycache__/x25519.cpython-312.pyc new file mode 100644 index 0000000..75b079e Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/__pycache__/x25519.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/__pycache__/x448.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/__pycache__/x448.cpython-312.pyc new file mode 100644 index 0000000..5f0de4e Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/__pycache__/x448.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/dh.py b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/dh.py new file mode 100644 index 0000000..31c9748 --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/dh.py @@ -0,0 +1,135 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +from __future__ import annotations + +import abc + +from cryptography.hazmat.bindings._rust import openssl as rust_openssl +from cryptography.hazmat.primitives import _serialization + +generate_parameters = rust_openssl.dh.generate_parameters + + +DHPrivateNumbers = rust_openssl.dh.DHPrivateNumbers +DHPublicNumbers = rust_openssl.dh.DHPublicNumbers +DHParameterNumbers = rust_openssl.dh.DHParameterNumbers + + +class DHParameters(metaclass=abc.ABCMeta): + @abc.abstractmethod + def generate_private_key(self) -> DHPrivateKey: + """ + Generates and returns a DHPrivateKey. + """ + + @abc.abstractmethod + def parameter_bytes( + self, + encoding: _serialization.Encoding, + format: _serialization.ParameterFormat, + ) -> bytes: + """ + Returns the parameters serialized as bytes. + """ + + @abc.abstractmethod + def parameter_numbers(self) -> DHParameterNumbers: + """ + Returns a DHParameterNumbers. + """ + + +DHParametersWithSerialization = DHParameters +DHParameters.register(rust_openssl.dh.DHParameters) + + +class DHPublicKey(metaclass=abc.ABCMeta): + @property + @abc.abstractmethod + def key_size(self) -> int: + """ + The bit length of the prime modulus. + """ + + @abc.abstractmethod + def parameters(self) -> DHParameters: + """ + The DHParameters object associated with this public key. + """ + + @abc.abstractmethod + def public_numbers(self) -> DHPublicNumbers: + """ + Returns a DHPublicNumbers. + """ + + @abc.abstractmethod + def public_bytes( + self, + encoding: _serialization.Encoding, + format: _serialization.PublicFormat, + ) -> bytes: + """ + Returns the key serialized as bytes. + """ + + @abc.abstractmethod + def __eq__(self, other: object) -> bool: + """ + Checks equality. + """ + + +DHPublicKeyWithSerialization = DHPublicKey +DHPublicKey.register(rust_openssl.dh.DHPublicKey) + + +class DHPrivateKey(metaclass=abc.ABCMeta): + @property + @abc.abstractmethod + def key_size(self) -> int: + """ + The bit length of the prime modulus. + """ + + @abc.abstractmethod + def public_key(self) -> DHPublicKey: + """ + The DHPublicKey associated with this private key. + """ + + @abc.abstractmethod + def parameters(self) -> DHParameters: + """ + The DHParameters object associated with this private key. + """ + + @abc.abstractmethod + def exchange(self, peer_public_key: DHPublicKey) -> bytes: + """ + Given peer's DHPublicKey, carry out the key exchange and + return shared key as bytes. + """ + + @abc.abstractmethod + def private_numbers(self) -> DHPrivateNumbers: + """ + Returns a DHPrivateNumbers. + """ + + @abc.abstractmethod + def private_bytes( + self, + encoding: _serialization.Encoding, + format: _serialization.PrivateFormat, + encryption_algorithm: _serialization.KeySerializationEncryption, + ) -> bytes: + """ + Returns the key serialized as bytes. + """ + + +DHPrivateKeyWithSerialization = DHPrivateKey +DHPrivateKey.register(rust_openssl.dh.DHPrivateKey) diff --git a/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/dsa.py b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/dsa.py new file mode 100644 index 0000000..6dd34c0 --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/dsa.py @@ -0,0 +1,154 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +from __future__ import annotations + +import abc +import typing + +from cryptography.hazmat.bindings._rust import openssl as rust_openssl +from cryptography.hazmat.primitives import _serialization, hashes +from cryptography.hazmat.primitives.asymmetric import utils as asym_utils + + +class DSAParameters(metaclass=abc.ABCMeta): + @abc.abstractmethod + def generate_private_key(self) -> DSAPrivateKey: + """ + Generates and returns a DSAPrivateKey. + """ + + @abc.abstractmethod + def parameter_numbers(self) -> DSAParameterNumbers: + """ + Returns a DSAParameterNumbers. + """ + + +DSAParametersWithNumbers = DSAParameters +DSAParameters.register(rust_openssl.dsa.DSAParameters) + + +class DSAPrivateKey(metaclass=abc.ABCMeta): + @property + @abc.abstractmethod + def key_size(self) -> int: + """ + The bit length of the prime modulus. + """ + + @abc.abstractmethod + def public_key(self) -> DSAPublicKey: + """ + The DSAPublicKey associated with this private key. + """ + + @abc.abstractmethod + def parameters(self) -> DSAParameters: + """ + The DSAParameters object associated with this private key. + """ + + @abc.abstractmethod + def sign( + self, + data: bytes, + algorithm: asym_utils.Prehashed | hashes.HashAlgorithm, + ) -> bytes: + """ + Signs the data + """ + + @abc.abstractmethod + def private_numbers(self) -> DSAPrivateNumbers: + """ + Returns a DSAPrivateNumbers. + """ + + @abc.abstractmethod + def private_bytes( + self, + encoding: _serialization.Encoding, + format: _serialization.PrivateFormat, + encryption_algorithm: _serialization.KeySerializationEncryption, + ) -> bytes: + """ + Returns the key serialized as bytes. + """ + + +DSAPrivateKeyWithSerialization = DSAPrivateKey +DSAPrivateKey.register(rust_openssl.dsa.DSAPrivateKey) + + +class DSAPublicKey(metaclass=abc.ABCMeta): + @property + @abc.abstractmethod + def key_size(self) -> int: + """ + The bit length of the prime modulus. + """ + + @abc.abstractmethod + def parameters(self) -> DSAParameters: + """ + The DSAParameters object associated with this public key. + """ + + @abc.abstractmethod + def public_numbers(self) -> DSAPublicNumbers: + """ + Returns a DSAPublicNumbers. + """ + + @abc.abstractmethod + def public_bytes( + self, + encoding: _serialization.Encoding, + format: _serialization.PublicFormat, + ) -> bytes: + """ + Returns the key serialized as bytes. + """ + + @abc.abstractmethod + def verify( + self, + signature: bytes, + data: bytes, + algorithm: asym_utils.Prehashed | hashes.HashAlgorithm, + ) -> None: + """ + Verifies the signature of the data. + """ + + @abc.abstractmethod + def __eq__(self, other: object) -> bool: + """ + Checks equality. + """ + + +DSAPublicKeyWithSerialization = DSAPublicKey +DSAPublicKey.register(rust_openssl.dsa.DSAPublicKey) + +DSAPrivateNumbers = rust_openssl.dsa.DSAPrivateNumbers +DSAPublicNumbers = rust_openssl.dsa.DSAPublicNumbers +DSAParameterNumbers = rust_openssl.dsa.DSAParameterNumbers + + +def generate_parameters( + key_size: int, backend: typing.Any = None +) -> DSAParameters: + if key_size not in (1024, 2048, 3072, 4096): + raise ValueError("Key size must be 1024, 2048, 3072, or 4096 bits.") + + return rust_openssl.dsa.generate_parameters(key_size) + + +def generate_private_key( + key_size: int, backend: typing.Any = None +) -> DSAPrivateKey: + parameters = generate_parameters(key_size) + return parameters.generate_private_key() diff --git a/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/ec.py b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/ec.py new file mode 100644 index 0000000..da1fbea --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/ec.py @@ -0,0 +1,403 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +from __future__ import annotations + +import abc +import typing + +from cryptography import utils +from cryptography.exceptions import UnsupportedAlgorithm, _Reasons +from cryptography.hazmat._oid import ObjectIdentifier +from cryptography.hazmat.bindings._rust import openssl as rust_openssl +from cryptography.hazmat.primitives import _serialization, hashes +from cryptography.hazmat.primitives.asymmetric import utils as asym_utils + + +class EllipticCurveOID: + SECP192R1 = ObjectIdentifier("1.2.840.10045.3.1.1") + SECP224R1 = ObjectIdentifier("1.3.132.0.33") + SECP256K1 = ObjectIdentifier("1.3.132.0.10") + SECP256R1 = ObjectIdentifier("1.2.840.10045.3.1.7") + SECP384R1 = ObjectIdentifier("1.3.132.0.34") + SECP521R1 = ObjectIdentifier("1.3.132.0.35") + BRAINPOOLP256R1 = ObjectIdentifier("1.3.36.3.3.2.8.1.1.7") + BRAINPOOLP384R1 = ObjectIdentifier("1.3.36.3.3.2.8.1.1.11") + BRAINPOOLP512R1 = ObjectIdentifier("1.3.36.3.3.2.8.1.1.13") + SECT163K1 = ObjectIdentifier("1.3.132.0.1") + SECT163R2 = ObjectIdentifier("1.3.132.0.15") + SECT233K1 = ObjectIdentifier("1.3.132.0.26") + SECT233R1 = ObjectIdentifier("1.3.132.0.27") + SECT283K1 = ObjectIdentifier("1.3.132.0.16") + SECT283R1 = ObjectIdentifier("1.3.132.0.17") + SECT409K1 = ObjectIdentifier("1.3.132.0.36") + SECT409R1 = ObjectIdentifier("1.3.132.0.37") + SECT571K1 = ObjectIdentifier("1.3.132.0.38") + SECT571R1 = ObjectIdentifier("1.3.132.0.39") + + +class EllipticCurve(metaclass=abc.ABCMeta): + @property + @abc.abstractmethod + def name(self) -> str: + """ + The name of the curve. e.g. secp256r1. + """ + + @property + @abc.abstractmethod + def key_size(self) -> int: + """ + Bit size of a secret scalar for the curve. + """ + + +class EllipticCurveSignatureAlgorithm(metaclass=abc.ABCMeta): + @property + @abc.abstractmethod + def algorithm( + self, + ) -> asym_utils.Prehashed | hashes.HashAlgorithm: + """ + The digest algorithm used with this signature. + """ + + +class EllipticCurvePrivateKey(metaclass=abc.ABCMeta): + @abc.abstractmethod + def exchange( + self, algorithm: ECDH, peer_public_key: EllipticCurvePublicKey + ) -> bytes: + """ + Performs a key exchange operation using the provided algorithm with the + provided peer's public key. + """ + + @abc.abstractmethod + def public_key(self) -> EllipticCurvePublicKey: + """ + The EllipticCurvePublicKey for this private key. + """ + + @property + @abc.abstractmethod + def curve(self) -> EllipticCurve: + """ + The EllipticCurve that this key is on. + """ + + @property + @abc.abstractmethod + def key_size(self) -> int: + """ + Bit size of a secret scalar for the curve. + """ + + @abc.abstractmethod + def sign( + self, + data: bytes, + signature_algorithm: EllipticCurveSignatureAlgorithm, + ) -> bytes: + """ + Signs the data + """ + + @abc.abstractmethod + def private_numbers(self) -> EllipticCurvePrivateNumbers: + """ + Returns an EllipticCurvePrivateNumbers. + """ + + @abc.abstractmethod + def private_bytes( + self, + encoding: _serialization.Encoding, + format: _serialization.PrivateFormat, + encryption_algorithm: _serialization.KeySerializationEncryption, + ) -> bytes: + """ + Returns the key serialized as bytes. + """ + + +EllipticCurvePrivateKeyWithSerialization = EllipticCurvePrivateKey +EllipticCurvePrivateKey.register(rust_openssl.ec.ECPrivateKey) + + +class EllipticCurvePublicKey(metaclass=abc.ABCMeta): + @property + @abc.abstractmethod + def curve(self) -> EllipticCurve: + """ + The EllipticCurve that this key is on. + """ + + @property + @abc.abstractmethod + def key_size(self) -> int: + """ + Bit size of a secret scalar for the curve. + """ + + @abc.abstractmethod + def public_numbers(self) -> EllipticCurvePublicNumbers: + """ + Returns an EllipticCurvePublicNumbers. + """ + + @abc.abstractmethod + def public_bytes( + self, + encoding: _serialization.Encoding, + format: _serialization.PublicFormat, + ) -> bytes: + """ + Returns the key serialized as bytes. + """ + + @abc.abstractmethod + def verify( + self, + signature: bytes, + data: bytes, + signature_algorithm: EllipticCurveSignatureAlgorithm, + ) -> None: + """ + Verifies the signature of the data. + """ + + @classmethod + def from_encoded_point( + cls, curve: EllipticCurve, data: bytes + ) -> EllipticCurvePublicKey: + utils._check_bytes("data", data) + + if len(data) == 0: + raise ValueError("data must not be an empty byte string") + + if data[0] not in [0x02, 0x03, 0x04]: + raise ValueError("Unsupported elliptic curve point type") + + return rust_openssl.ec.from_public_bytes(curve, data) + + @abc.abstractmethod + def __eq__(self, other: object) -> bool: + """ + Checks equality. + """ + + +EllipticCurvePublicKeyWithSerialization = EllipticCurvePublicKey +EllipticCurvePublicKey.register(rust_openssl.ec.ECPublicKey) + +EllipticCurvePrivateNumbers = rust_openssl.ec.EllipticCurvePrivateNumbers +EllipticCurvePublicNumbers = rust_openssl.ec.EllipticCurvePublicNumbers + + +class SECT571R1(EllipticCurve): + name = "sect571r1" + key_size = 570 + + +class SECT409R1(EllipticCurve): + name = "sect409r1" + key_size = 409 + + +class SECT283R1(EllipticCurve): + name = "sect283r1" + key_size = 283 + + +class SECT233R1(EllipticCurve): + name = "sect233r1" + key_size = 233 + + +class SECT163R2(EllipticCurve): + name = "sect163r2" + key_size = 163 + + +class SECT571K1(EllipticCurve): + name = "sect571k1" + key_size = 571 + + +class SECT409K1(EllipticCurve): + name = "sect409k1" + key_size = 409 + + +class SECT283K1(EllipticCurve): + name = "sect283k1" + key_size = 283 + + +class SECT233K1(EllipticCurve): + name = "sect233k1" + key_size = 233 + + +class SECT163K1(EllipticCurve): + name = "sect163k1" + key_size = 163 + + +class SECP521R1(EllipticCurve): + name = "secp521r1" + key_size = 521 + + +class SECP384R1(EllipticCurve): + name = "secp384r1" + key_size = 384 + + +class SECP256R1(EllipticCurve): + name = "secp256r1" + key_size = 256 + + +class SECP256K1(EllipticCurve): + name = "secp256k1" + key_size = 256 + + +class SECP224R1(EllipticCurve): + name = "secp224r1" + key_size = 224 + + +class SECP192R1(EllipticCurve): + name = "secp192r1" + key_size = 192 + + +class BrainpoolP256R1(EllipticCurve): + name = "brainpoolP256r1" + key_size = 256 + + +class BrainpoolP384R1(EllipticCurve): + name = "brainpoolP384r1" + key_size = 384 + + +class BrainpoolP512R1(EllipticCurve): + name = "brainpoolP512r1" + key_size = 512 + + +_CURVE_TYPES: dict[str, EllipticCurve] = { + "prime192v1": SECP192R1(), + "prime256v1": SECP256R1(), + "secp192r1": SECP192R1(), + "secp224r1": SECP224R1(), + "secp256r1": SECP256R1(), + "secp384r1": SECP384R1(), + "secp521r1": SECP521R1(), + "secp256k1": SECP256K1(), + "sect163k1": SECT163K1(), + "sect233k1": SECT233K1(), + "sect283k1": SECT283K1(), + "sect409k1": SECT409K1(), + "sect571k1": SECT571K1(), + "sect163r2": SECT163R2(), + "sect233r1": SECT233R1(), + "sect283r1": SECT283R1(), + "sect409r1": SECT409R1(), + "sect571r1": SECT571R1(), + "brainpoolP256r1": BrainpoolP256R1(), + "brainpoolP384r1": BrainpoolP384R1(), + "brainpoolP512r1": BrainpoolP512R1(), +} + + +class ECDSA(EllipticCurveSignatureAlgorithm): + def __init__( + self, + algorithm: asym_utils.Prehashed | hashes.HashAlgorithm, + deterministic_signing: bool = False, + ): + from cryptography.hazmat.backends.openssl.backend import backend + + if ( + deterministic_signing + and not backend.ecdsa_deterministic_supported() + ): + raise UnsupportedAlgorithm( + "ECDSA with deterministic signature (RFC 6979) is not " + "supported by this version of OpenSSL.", + _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM, + ) + self._algorithm = algorithm + self._deterministic_signing = deterministic_signing + + @property + def algorithm( + self, + ) -> asym_utils.Prehashed | hashes.HashAlgorithm: + return self._algorithm + + @property + def deterministic_signing( + self, + ) -> bool: + return self._deterministic_signing + + +generate_private_key = rust_openssl.ec.generate_private_key + + +def derive_private_key( + private_value: int, + curve: EllipticCurve, + backend: typing.Any = None, +) -> EllipticCurvePrivateKey: + if not isinstance(private_value, int): + raise TypeError("private_value must be an integer type.") + + if private_value <= 0: + raise ValueError("private_value must be a positive integer.") + + return rust_openssl.ec.derive_private_key(private_value, curve) + + +class ECDH: + pass + + +_OID_TO_CURVE = { + EllipticCurveOID.SECP192R1: SECP192R1, + EllipticCurveOID.SECP224R1: SECP224R1, + EllipticCurveOID.SECP256K1: SECP256K1, + EllipticCurveOID.SECP256R1: SECP256R1, + EllipticCurveOID.SECP384R1: SECP384R1, + EllipticCurveOID.SECP521R1: SECP521R1, + EllipticCurveOID.BRAINPOOLP256R1: BrainpoolP256R1, + EllipticCurveOID.BRAINPOOLP384R1: BrainpoolP384R1, + EllipticCurveOID.BRAINPOOLP512R1: BrainpoolP512R1, + EllipticCurveOID.SECT163K1: SECT163K1, + EllipticCurveOID.SECT163R2: SECT163R2, + EllipticCurveOID.SECT233K1: SECT233K1, + EllipticCurveOID.SECT233R1: SECT233R1, + EllipticCurveOID.SECT283K1: SECT283K1, + EllipticCurveOID.SECT283R1: SECT283R1, + EllipticCurveOID.SECT409K1: SECT409K1, + EllipticCurveOID.SECT409R1: SECT409R1, + EllipticCurveOID.SECT571K1: SECT571K1, + EllipticCurveOID.SECT571R1: SECT571R1, +} + + +def get_curve_for_oid(oid: ObjectIdentifier) -> type[EllipticCurve]: + try: + return _OID_TO_CURVE[oid] + except KeyError: + raise LookupError( + "The provided object identifier has no matching elliptic " + "curve class" + ) diff --git a/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/ed25519.py b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/ed25519.py new file mode 100644 index 0000000..3a26185 --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/ed25519.py @@ -0,0 +1,116 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +from __future__ import annotations + +import abc + +from cryptography.exceptions import UnsupportedAlgorithm, _Reasons +from cryptography.hazmat.bindings._rust import openssl as rust_openssl +from cryptography.hazmat.primitives import _serialization + + +class Ed25519PublicKey(metaclass=abc.ABCMeta): + @classmethod + def from_public_bytes(cls, data: bytes) -> Ed25519PublicKey: + from cryptography.hazmat.backends.openssl.backend import backend + + if not backend.ed25519_supported(): + raise UnsupportedAlgorithm( + "ed25519 is not supported by this version of OpenSSL.", + _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM, + ) + + return rust_openssl.ed25519.from_public_bytes(data) + + @abc.abstractmethod + def public_bytes( + self, + encoding: _serialization.Encoding, + format: _serialization.PublicFormat, + ) -> bytes: + """ + The serialized bytes of the public key. + """ + + @abc.abstractmethod + def public_bytes_raw(self) -> bytes: + """ + The raw bytes of the public key. + Equivalent to public_bytes(Raw, Raw). + """ + + @abc.abstractmethod + def verify(self, signature: bytes, data: bytes) -> None: + """ + Verify the signature. + """ + + @abc.abstractmethod + def __eq__(self, other: object) -> bool: + """ + Checks equality. + """ + + +Ed25519PublicKey.register(rust_openssl.ed25519.Ed25519PublicKey) + + +class Ed25519PrivateKey(metaclass=abc.ABCMeta): + @classmethod + def generate(cls) -> Ed25519PrivateKey: + from cryptography.hazmat.backends.openssl.backend import backend + + if not backend.ed25519_supported(): + raise UnsupportedAlgorithm( + "ed25519 is not supported by this version of OpenSSL.", + _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM, + ) + + return rust_openssl.ed25519.generate_key() + + @classmethod + def from_private_bytes(cls, data: bytes) -> Ed25519PrivateKey: + from cryptography.hazmat.backends.openssl.backend import backend + + if not backend.ed25519_supported(): + raise UnsupportedAlgorithm( + "ed25519 is not supported by this version of OpenSSL.", + _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM, + ) + + return rust_openssl.ed25519.from_private_bytes(data) + + @abc.abstractmethod + def public_key(self) -> Ed25519PublicKey: + """ + The Ed25519PublicKey derived from the private key. + """ + + @abc.abstractmethod + def private_bytes( + self, + encoding: _serialization.Encoding, + format: _serialization.PrivateFormat, + encryption_algorithm: _serialization.KeySerializationEncryption, + ) -> bytes: + """ + The serialized bytes of the private key. + """ + + @abc.abstractmethod + def private_bytes_raw(self) -> bytes: + """ + The raw bytes of the private key. + Equivalent to private_bytes(Raw, Raw, NoEncryption()). + """ + + @abc.abstractmethod + def sign(self, data: bytes) -> bytes: + """ + Signs the data. + """ + + +Ed25519PrivateKey.register(rust_openssl.ed25519.Ed25519PrivateKey) diff --git a/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/ed448.py b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/ed448.py new file mode 100644 index 0000000..78c82c4 --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/ed448.py @@ -0,0 +1,118 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +from __future__ import annotations + +import abc + +from cryptography.exceptions import UnsupportedAlgorithm, _Reasons +from cryptography.hazmat.bindings._rust import openssl as rust_openssl +from cryptography.hazmat.primitives import _serialization + + +class Ed448PublicKey(metaclass=abc.ABCMeta): + @classmethod + def from_public_bytes(cls, data: bytes) -> Ed448PublicKey: + from cryptography.hazmat.backends.openssl.backend import backend + + if not backend.ed448_supported(): + raise UnsupportedAlgorithm( + "ed448 is not supported by this version of OpenSSL.", + _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM, + ) + + return rust_openssl.ed448.from_public_bytes(data) + + @abc.abstractmethod + def public_bytes( + self, + encoding: _serialization.Encoding, + format: _serialization.PublicFormat, + ) -> bytes: + """ + The serialized bytes of the public key. + """ + + @abc.abstractmethod + def public_bytes_raw(self) -> bytes: + """ + The raw bytes of the public key. + Equivalent to public_bytes(Raw, Raw). + """ + + @abc.abstractmethod + def verify(self, signature: bytes, data: bytes) -> None: + """ + Verify the signature. + """ + + @abc.abstractmethod + def __eq__(self, other: object) -> bool: + """ + Checks equality. + """ + + +if hasattr(rust_openssl, "ed448"): + Ed448PublicKey.register(rust_openssl.ed448.Ed448PublicKey) + + +class Ed448PrivateKey(metaclass=abc.ABCMeta): + @classmethod + def generate(cls) -> Ed448PrivateKey: + from cryptography.hazmat.backends.openssl.backend import backend + + if not backend.ed448_supported(): + raise UnsupportedAlgorithm( + "ed448 is not supported by this version of OpenSSL.", + _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM, + ) + + return rust_openssl.ed448.generate_key() + + @classmethod + def from_private_bytes(cls, data: bytes) -> Ed448PrivateKey: + from cryptography.hazmat.backends.openssl.backend import backend + + if not backend.ed448_supported(): + raise UnsupportedAlgorithm( + "ed448 is not supported by this version of OpenSSL.", + _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM, + ) + + return rust_openssl.ed448.from_private_bytes(data) + + @abc.abstractmethod + def public_key(self) -> Ed448PublicKey: + """ + The Ed448PublicKey derived from the private key. + """ + + @abc.abstractmethod + def sign(self, data: bytes) -> bytes: + """ + Signs the data. + """ + + @abc.abstractmethod + def private_bytes( + self, + encoding: _serialization.Encoding, + format: _serialization.PrivateFormat, + encryption_algorithm: _serialization.KeySerializationEncryption, + ) -> bytes: + """ + The serialized bytes of the private key. + """ + + @abc.abstractmethod + def private_bytes_raw(self) -> bytes: + """ + The raw bytes of the private key. + Equivalent to private_bytes(Raw, Raw, NoEncryption()). + """ + + +if hasattr(rust_openssl, "x448"): + Ed448PrivateKey.register(rust_openssl.ed448.Ed448PrivateKey) diff --git a/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/padding.py b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/padding.py new file mode 100644 index 0000000..b4babf4 --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/padding.py @@ -0,0 +1,113 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +from __future__ import annotations + +import abc + +from cryptography.hazmat.primitives import hashes +from cryptography.hazmat.primitives._asymmetric import ( + AsymmetricPadding as AsymmetricPadding, +) +from cryptography.hazmat.primitives.asymmetric import rsa + + +class PKCS1v15(AsymmetricPadding): + name = "EMSA-PKCS1-v1_5" + + +class _MaxLength: + "Sentinel value for `MAX_LENGTH`." + + +class _Auto: + "Sentinel value for `AUTO`." + + +class _DigestLength: + "Sentinel value for `DIGEST_LENGTH`." + + +class PSS(AsymmetricPadding): + MAX_LENGTH = _MaxLength() + AUTO = _Auto() + DIGEST_LENGTH = _DigestLength() + name = "EMSA-PSS" + _salt_length: int | _MaxLength | _Auto | _DigestLength + + def __init__( + self, + mgf: MGF, + salt_length: int | _MaxLength | _Auto | _DigestLength, + ) -> None: + self._mgf = mgf + + if not isinstance( + salt_length, (int, _MaxLength, _Auto, _DigestLength) + ): + raise TypeError( + "salt_length must be an integer, MAX_LENGTH, " + "DIGEST_LENGTH, or AUTO" + ) + + if isinstance(salt_length, int) and salt_length < 0: + raise ValueError("salt_length must be zero or greater.") + + self._salt_length = salt_length + + @property + def mgf(self) -> MGF: + return self._mgf + + +class OAEP(AsymmetricPadding): + name = "EME-OAEP" + + def __init__( + self, + mgf: MGF, + algorithm: hashes.HashAlgorithm, + label: bytes | None, + ): + if not isinstance(algorithm, hashes.HashAlgorithm): + raise TypeError("Expected instance of hashes.HashAlgorithm.") + + self._mgf = mgf + self._algorithm = algorithm + self._label = label + + @property + def algorithm(self) -> hashes.HashAlgorithm: + return self._algorithm + + @property + def mgf(self) -> MGF: + return self._mgf + + +class MGF(metaclass=abc.ABCMeta): + _algorithm: hashes.HashAlgorithm + + +class MGF1(MGF): + MAX_LENGTH = _MaxLength() + + def __init__(self, algorithm: hashes.HashAlgorithm): + if not isinstance(algorithm, hashes.HashAlgorithm): + raise TypeError("Expected instance of hashes.HashAlgorithm.") + + self._algorithm = algorithm + + +def calculate_max_pss_salt_length( + key: rsa.RSAPrivateKey | rsa.RSAPublicKey, + hash_algorithm: hashes.HashAlgorithm, +) -> int: + if not isinstance(key, (rsa.RSAPrivateKey, rsa.RSAPublicKey)): + raise TypeError("key must be an RSA public or private key") + # bit length - 1 per RFC 3447 + emlen = (key.key_size + 6) // 8 + salt_length = emlen - hash_algorithm.digest_size - 2 + assert salt_length >= 0 + return salt_length diff --git a/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/rsa.py b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/rsa.py new file mode 100644 index 0000000..7a387b5 --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/rsa.py @@ -0,0 +1,260 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +from __future__ import annotations + +import abc +import typing +from math import gcd + +from cryptography.hazmat.bindings._rust import openssl as rust_openssl +from cryptography.hazmat.primitives import _serialization, hashes +from cryptography.hazmat.primitives._asymmetric import AsymmetricPadding +from cryptography.hazmat.primitives.asymmetric import utils as asym_utils + + +class RSAPrivateKey(metaclass=abc.ABCMeta): + @abc.abstractmethod + def decrypt(self, ciphertext: bytes, padding: AsymmetricPadding) -> bytes: + """ + Decrypts the provided ciphertext. + """ + + @property + @abc.abstractmethod + def key_size(self) -> int: + """ + The bit length of the public modulus. + """ + + @abc.abstractmethod + def public_key(self) -> RSAPublicKey: + """ + The RSAPublicKey associated with this private key. + """ + + @abc.abstractmethod + def sign( + self, + data: bytes, + padding: AsymmetricPadding, + algorithm: asym_utils.Prehashed | hashes.HashAlgorithm, + ) -> bytes: + """ + Signs the data. + """ + + @abc.abstractmethod + def private_numbers(self) -> RSAPrivateNumbers: + """ + Returns an RSAPrivateNumbers. + """ + + @abc.abstractmethod + def private_bytes( + self, + encoding: _serialization.Encoding, + format: _serialization.PrivateFormat, + encryption_algorithm: _serialization.KeySerializationEncryption, + ) -> bytes: + """ + Returns the key serialized as bytes. + """ + + +RSAPrivateKeyWithSerialization = RSAPrivateKey +RSAPrivateKey.register(rust_openssl.rsa.RSAPrivateKey) + + +class RSAPublicKey(metaclass=abc.ABCMeta): + @abc.abstractmethod + def encrypt(self, plaintext: bytes, padding: AsymmetricPadding) -> bytes: + """ + Encrypts the given plaintext. + """ + + @property + @abc.abstractmethod + def key_size(self) -> int: + """ + The bit length of the public modulus. + """ + + @abc.abstractmethod + def public_numbers(self) -> RSAPublicNumbers: + """ + Returns an RSAPublicNumbers + """ + + @abc.abstractmethod + def public_bytes( + self, + encoding: _serialization.Encoding, + format: _serialization.PublicFormat, + ) -> bytes: + """ + Returns the key serialized as bytes. + """ + + @abc.abstractmethod + def verify( + self, + signature: bytes, + data: bytes, + padding: AsymmetricPadding, + algorithm: asym_utils.Prehashed | hashes.HashAlgorithm, + ) -> None: + """ + Verifies the signature of the data. + """ + + @abc.abstractmethod + def recover_data_from_signature( + self, + signature: bytes, + padding: AsymmetricPadding, + algorithm: hashes.HashAlgorithm | None, + ) -> bytes: + """ + Recovers the original data from the signature. + """ + + @abc.abstractmethod + def __eq__(self, other: object) -> bool: + """ + Checks equality. + """ + + +RSAPublicKeyWithSerialization = RSAPublicKey +RSAPublicKey.register(rust_openssl.rsa.RSAPublicKey) + +RSAPrivateNumbers = rust_openssl.rsa.RSAPrivateNumbers +RSAPublicNumbers = rust_openssl.rsa.RSAPublicNumbers + + +def generate_private_key( + public_exponent: int, + key_size: int, + backend: typing.Any = None, +) -> RSAPrivateKey: + _verify_rsa_parameters(public_exponent, key_size) + return rust_openssl.rsa.generate_private_key(public_exponent, key_size) + + +def _verify_rsa_parameters(public_exponent: int, key_size: int) -> None: + if public_exponent not in (3, 65537): + raise ValueError( + "public_exponent must be either 3 (for legacy compatibility) or " + "65537. Almost everyone should choose 65537 here!" + ) + + if key_size < 1024: + raise ValueError("key_size must be at least 1024-bits.") + + +def _modinv(e: int, m: int) -> int: + """ + Modular Multiplicative Inverse. Returns x such that: (x*e) mod m == 1 + """ + x1, x2 = 1, 0 + a, b = e, m + while b > 0: + q, r = divmod(a, b) + xn = x1 - q * x2 + a, b, x1, x2 = b, r, x2, xn + return x1 % m + + +def rsa_crt_iqmp(p: int, q: int) -> int: + """ + Compute the CRT (q ** -1) % p value from RSA primes p and q. + """ + return _modinv(q, p) + + +def rsa_crt_dmp1(private_exponent: int, p: int) -> int: + """ + Compute the CRT private_exponent % (p - 1) value from the RSA + private_exponent (d) and p. + """ + return private_exponent % (p - 1) + + +def rsa_crt_dmq1(private_exponent: int, q: int) -> int: + """ + Compute the CRT private_exponent % (q - 1) value from the RSA + private_exponent (d) and q. + """ + return private_exponent % (q - 1) + + +def rsa_recover_private_exponent(e: int, p: int, q: int) -> int: + """ + Compute the RSA private_exponent (d) given the public exponent (e) + and the RSA primes p and q. + + This uses the Carmichael totient function to generate the + smallest possible working value of the private exponent. + """ + # This lambda_n is the Carmichael totient function. + # The original RSA paper uses the Euler totient function + # here: phi_n = (p - 1) * (q - 1) + # Either version of the private exponent will work, but the + # one generated by the older formulation may be larger + # than necessary. (lambda_n always divides phi_n) + # + # TODO: Replace with lcm(p - 1, q - 1) once the minimum + # supported Python version is >= 3.9. + lambda_n = (p - 1) * (q - 1) // gcd(p - 1, q - 1) + return _modinv(e, lambda_n) + + +# Controls the number of iterations rsa_recover_prime_factors will perform +# to obtain the prime factors. Each iteration increments by 2 so the actual +# maximum attempts is half this number. +_MAX_RECOVERY_ATTEMPTS = 1000 + + +def rsa_recover_prime_factors(n: int, e: int, d: int) -> tuple[int, int]: + """ + Compute factors p and q from the private exponent d. We assume that n has + no more than two factors. This function is adapted from code in PyCrypto. + """ + # See 8.2.2(i) in Handbook of Applied Cryptography. + ktot = d * e - 1 + # The quantity d*e-1 is a multiple of phi(n), even, + # and can be represented as t*2^s. + t = ktot + while t % 2 == 0: + t = t // 2 + # Cycle through all multiplicative inverses in Zn. + # The algorithm is non-deterministic, but there is a 50% chance + # any candidate a leads to successful factoring. + # See "Digitalized Signatures and Public Key Functions as Intractable + # as Factorization", M. Rabin, 1979 + spotted = False + a = 2 + while not spotted and a < _MAX_RECOVERY_ATTEMPTS: + k = t + # Cycle through all values a^{t*2^i}=a^k + while k < ktot: + cand = pow(a, k, n) + # Check if a^k is a non-trivial root of unity (mod n) + if cand != 1 and cand != (n - 1) and pow(cand, 2, n) == 1: + # We have found a number such that (cand-1)(cand+1)=0 (mod n). + # Either of the terms divides n. + p = gcd(cand + 1, n) + spotted = True + break + k *= 2 + # This value was not any good... let's try another! + a += 2 + if not spotted: + raise ValueError("Unable to compute factors p and q from exponent d.") + # Found ! + q, r = divmod(n, p) + assert r == 0 + p, q = sorted((p, q), reverse=True) + return (p, q) diff --git a/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/types.py b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/types.py new file mode 100644 index 0000000..1fe4eaf --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/types.py @@ -0,0 +1,111 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +from __future__ import annotations + +import typing + +from cryptography import utils +from cryptography.hazmat.primitives.asymmetric import ( + dh, + dsa, + ec, + ed448, + ed25519, + rsa, + x448, + x25519, +) + +# Every asymmetric key type +PublicKeyTypes = typing.Union[ + dh.DHPublicKey, + dsa.DSAPublicKey, + rsa.RSAPublicKey, + ec.EllipticCurvePublicKey, + ed25519.Ed25519PublicKey, + ed448.Ed448PublicKey, + x25519.X25519PublicKey, + x448.X448PublicKey, +] +PUBLIC_KEY_TYPES = PublicKeyTypes +utils.deprecated( + PUBLIC_KEY_TYPES, + __name__, + "Use PublicKeyTypes instead", + utils.DeprecatedIn40, + name="PUBLIC_KEY_TYPES", +) +# Every asymmetric key type +PrivateKeyTypes = typing.Union[ + dh.DHPrivateKey, + ed25519.Ed25519PrivateKey, + ed448.Ed448PrivateKey, + rsa.RSAPrivateKey, + dsa.DSAPrivateKey, + ec.EllipticCurvePrivateKey, + x25519.X25519PrivateKey, + x448.X448PrivateKey, +] +PRIVATE_KEY_TYPES = PrivateKeyTypes +utils.deprecated( + PRIVATE_KEY_TYPES, + __name__, + "Use PrivateKeyTypes instead", + utils.DeprecatedIn40, + name="PRIVATE_KEY_TYPES", +) +# Just the key types we allow to be used for x509 signing. This mirrors +# the certificate public key types +CertificateIssuerPrivateKeyTypes = typing.Union[ + ed25519.Ed25519PrivateKey, + ed448.Ed448PrivateKey, + rsa.RSAPrivateKey, + dsa.DSAPrivateKey, + ec.EllipticCurvePrivateKey, +] +CERTIFICATE_PRIVATE_KEY_TYPES = CertificateIssuerPrivateKeyTypes +utils.deprecated( + CERTIFICATE_PRIVATE_KEY_TYPES, + __name__, + "Use CertificateIssuerPrivateKeyTypes instead", + utils.DeprecatedIn40, + name="CERTIFICATE_PRIVATE_KEY_TYPES", +) +# Just the key types we allow to be used for x509 signing. This mirrors +# the certificate private key types +CertificateIssuerPublicKeyTypes = typing.Union[ + dsa.DSAPublicKey, + rsa.RSAPublicKey, + ec.EllipticCurvePublicKey, + ed25519.Ed25519PublicKey, + ed448.Ed448PublicKey, +] +CERTIFICATE_ISSUER_PUBLIC_KEY_TYPES = CertificateIssuerPublicKeyTypes +utils.deprecated( + CERTIFICATE_ISSUER_PUBLIC_KEY_TYPES, + __name__, + "Use CertificateIssuerPublicKeyTypes instead", + utils.DeprecatedIn40, + name="CERTIFICATE_ISSUER_PUBLIC_KEY_TYPES", +) +# This type removes DHPublicKey. x448/x25519 can be a public key +# but cannot be used in signing so they are allowed here. +CertificatePublicKeyTypes = typing.Union[ + dsa.DSAPublicKey, + rsa.RSAPublicKey, + ec.EllipticCurvePublicKey, + ed25519.Ed25519PublicKey, + ed448.Ed448PublicKey, + x25519.X25519PublicKey, + x448.X448PublicKey, +] +CERTIFICATE_PUBLIC_KEY_TYPES = CertificatePublicKeyTypes +utils.deprecated( + CERTIFICATE_PUBLIC_KEY_TYPES, + __name__, + "Use CertificatePublicKeyTypes instead", + utils.DeprecatedIn40, + name="CERTIFICATE_PUBLIC_KEY_TYPES", +) diff --git a/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/utils.py b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/utils.py new file mode 100644 index 0000000..826b956 --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/utils.py @@ -0,0 +1,24 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +from __future__ import annotations + +from cryptography.hazmat.bindings._rust import asn1 +from cryptography.hazmat.primitives import hashes + +decode_dss_signature = asn1.decode_dss_signature +encode_dss_signature = asn1.encode_dss_signature + + +class Prehashed: + def __init__(self, algorithm: hashes.HashAlgorithm): + if not isinstance(algorithm, hashes.HashAlgorithm): + raise TypeError("Expected instance of HashAlgorithm.") + + self._algorithm = algorithm + self._digest_size = algorithm.digest_size + + @property + def digest_size(self) -> int: + return self._digest_size diff --git a/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/x25519.py b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/x25519.py new file mode 100644 index 0000000..0cfa36e --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/x25519.py @@ -0,0 +1,109 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +from __future__ import annotations + +import abc + +from cryptography.exceptions import UnsupportedAlgorithm, _Reasons +from cryptography.hazmat.bindings._rust import openssl as rust_openssl +from cryptography.hazmat.primitives import _serialization + + +class X25519PublicKey(metaclass=abc.ABCMeta): + @classmethod + def from_public_bytes(cls, data: bytes) -> X25519PublicKey: + from cryptography.hazmat.backends.openssl.backend import backend + + if not backend.x25519_supported(): + raise UnsupportedAlgorithm( + "X25519 is not supported by this version of OpenSSL.", + _Reasons.UNSUPPORTED_EXCHANGE_ALGORITHM, + ) + + return rust_openssl.x25519.from_public_bytes(data) + + @abc.abstractmethod + def public_bytes( + self, + encoding: _serialization.Encoding, + format: _serialization.PublicFormat, + ) -> bytes: + """ + The serialized bytes of the public key. + """ + + @abc.abstractmethod + def public_bytes_raw(self) -> bytes: + """ + The raw bytes of the public key. + Equivalent to public_bytes(Raw, Raw). + """ + + @abc.abstractmethod + def __eq__(self, other: object) -> bool: + """ + Checks equality. + """ + + +X25519PublicKey.register(rust_openssl.x25519.X25519PublicKey) + + +class X25519PrivateKey(metaclass=abc.ABCMeta): + @classmethod + def generate(cls) -> X25519PrivateKey: + from cryptography.hazmat.backends.openssl.backend import backend + + if not backend.x25519_supported(): + raise UnsupportedAlgorithm( + "X25519 is not supported by this version of OpenSSL.", + _Reasons.UNSUPPORTED_EXCHANGE_ALGORITHM, + ) + return rust_openssl.x25519.generate_key() + + @classmethod + def from_private_bytes(cls, data: bytes) -> X25519PrivateKey: + from cryptography.hazmat.backends.openssl.backend import backend + + if not backend.x25519_supported(): + raise UnsupportedAlgorithm( + "X25519 is not supported by this version of OpenSSL.", + _Reasons.UNSUPPORTED_EXCHANGE_ALGORITHM, + ) + + return rust_openssl.x25519.from_private_bytes(data) + + @abc.abstractmethod + def public_key(self) -> X25519PublicKey: + """ + Returns the public key associated with this private key + """ + + @abc.abstractmethod + def private_bytes( + self, + encoding: _serialization.Encoding, + format: _serialization.PrivateFormat, + encryption_algorithm: _serialization.KeySerializationEncryption, + ) -> bytes: + """ + The serialized bytes of the private key. + """ + + @abc.abstractmethod + def private_bytes_raw(self) -> bytes: + """ + The raw bytes of the private key. + Equivalent to private_bytes(Raw, Raw, NoEncryption()). + """ + + @abc.abstractmethod + def exchange(self, peer_public_key: X25519PublicKey) -> bytes: + """ + Performs a key exchange operation using the provided peer's public key. + """ + + +X25519PrivateKey.register(rust_openssl.x25519.X25519PrivateKey) diff --git a/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/x448.py b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/x448.py new file mode 100644 index 0000000..86086ab --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/x448.py @@ -0,0 +1,112 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +from __future__ import annotations + +import abc + +from cryptography.exceptions import UnsupportedAlgorithm, _Reasons +from cryptography.hazmat.bindings._rust import openssl as rust_openssl +from cryptography.hazmat.primitives import _serialization + + +class X448PublicKey(metaclass=abc.ABCMeta): + @classmethod + def from_public_bytes(cls, data: bytes) -> X448PublicKey: + from cryptography.hazmat.backends.openssl.backend import backend + + if not backend.x448_supported(): + raise UnsupportedAlgorithm( + "X448 is not supported by this version of OpenSSL.", + _Reasons.UNSUPPORTED_EXCHANGE_ALGORITHM, + ) + + return rust_openssl.x448.from_public_bytes(data) + + @abc.abstractmethod + def public_bytes( + self, + encoding: _serialization.Encoding, + format: _serialization.PublicFormat, + ) -> bytes: + """ + The serialized bytes of the public key. + """ + + @abc.abstractmethod + def public_bytes_raw(self) -> bytes: + """ + The raw bytes of the public key. + Equivalent to public_bytes(Raw, Raw). + """ + + @abc.abstractmethod + def __eq__(self, other: object) -> bool: + """ + Checks equality. + """ + + +if hasattr(rust_openssl, "x448"): + X448PublicKey.register(rust_openssl.x448.X448PublicKey) + + +class X448PrivateKey(metaclass=abc.ABCMeta): + @classmethod + def generate(cls) -> X448PrivateKey: + from cryptography.hazmat.backends.openssl.backend import backend + + if not backend.x448_supported(): + raise UnsupportedAlgorithm( + "X448 is not supported by this version of OpenSSL.", + _Reasons.UNSUPPORTED_EXCHANGE_ALGORITHM, + ) + + return rust_openssl.x448.generate_key() + + @classmethod + def from_private_bytes(cls, data: bytes) -> X448PrivateKey: + from cryptography.hazmat.backends.openssl.backend import backend + + if not backend.x448_supported(): + raise UnsupportedAlgorithm( + "X448 is not supported by this version of OpenSSL.", + _Reasons.UNSUPPORTED_EXCHANGE_ALGORITHM, + ) + + return rust_openssl.x448.from_private_bytes(data) + + @abc.abstractmethod + def public_key(self) -> X448PublicKey: + """ + Returns the public key associated with this private key + """ + + @abc.abstractmethod + def private_bytes( + self, + encoding: _serialization.Encoding, + format: _serialization.PrivateFormat, + encryption_algorithm: _serialization.KeySerializationEncryption, + ) -> bytes: + """ + The serialized bytes of the private key. + """ + + @abc.abstractmethod + def private_bytes_raw(self) -> bytes: + """ + The raw bytes of the private key. + Equivalent to private_bytes(Raw, Raw, NoEncryption()). + """ + + @abc.abstractmethod + def exchange(self, peer_public_key: X448PublicKey) -> bytes: + """ + Performs a key exchange operation using the provided peer's public key. + """ + + +if hasattr(rust_openssl, "x448"): + X448PrivateKey.register(rust_openssl.x448.X448PrivateKey) diff --git a/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/ciphers/__init__.py b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/ciphers/__init__.py new file mode 100644 index 0000000..10c15d0 --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/ciphers/__init__.py @@ -0,0 +1,27 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +from __future__ import annotations + +from cryptography.hazmat.primitives._cipheralgorithm import ( + BlockCipherAlgorithm, + CipherAlgorithm, +) +from cryptography.hazmat.primitives.ciphers.base import ( + AEADCipherContext, + AEADDecryptionContext, + AEADEncryptionContext, + Cipher, + CipherContext, +) + +__all__ = [ + "AEADCipherContext", + "AEADDecryptionContext", + "AEADEncryptionContext", + "BlockCipherAlgorithm", + "Cipher", + "CipherAlgorithm", + "CipherContext", +] diff --git a/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/ciphers/__pycache__/__init__.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/ciphers/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..6d35407 Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/ciphers/__pycache__/__init__.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/ciphers/__pycache__/aead.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/ciphers/__pycache__/aead.cpython-312.pyc new file mode 100644 index 0000000..b4233f6 Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/ciphers/__pycache__/aead.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/ciphers/__pycache__/algorithms.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/ciphers/__pycache__/algorithms.cpython-312.pyc new file mode 100644 index 0000000..cc25532 Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/ciphers/__pycache__/algorithms.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/ciphers/__pycache__/base.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/ciphers/__pycache__/base.cpython-312.pyc new file mode 100644 index 0000000..f38b6fb Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/ciphers/__pycache__/base.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/ciphers/__pycache__/modes.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/ciphers/__pycache__/modes.cpython-312.pyc new file mode 100644 index 0000000..3219779 Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/ciphers/__pycache__/modes.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/ciphers/aead.py b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/ciphers/aead.py new file mode 100644 index 0000000..c8a582d --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/ciphers/aead.py @@ -0,0 +1,23 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +from __future__ import annotations + +from cryptography.hazmat.bindings._rust import openssl as rust_openssl + +__all__ = [ + "AESCCM", + "AESGCM", + "AESGCMSIV", + "AESOCB3", + "AESSIV", + "ChaCha20Poly1305", +] + +AESGCM = rust_openssl.aead.AESGCM +ChaCha20Poly1305 = rust_openssl.aead.ChaCha20Poly1305 +AESCCM = rust_openssl.aead.AESCCM +AESSIV = rust_openssl.aead.AESSIV +AESOCB3 = rust_openssl.aead.AESOCB3 +AESGCMSIV = rust_openssl.aead.AESGCMSIV diff --git a/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/ciphers/algorithms.py b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/ciphers/algorithms.py new file mode 100644 index 0000000..1051ba3 --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/ciphers/algorithms.py @@ -0,0 +1,177 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +from __future__ import annotations + +from cryptography import utils +from cryptography.hazmat.decrepit.ciphers.algorithms import ( + ARC4 as ARC4, +) +from cryptography.hazmat.decrepit.ciphers.algorithms import ( + CAST5 as CAST5, +) +from cryptography.hazmat.decrepit.ciphers.algorithms import ( + IDEA as IDEA, +) +from cryptography.hazmat.decrepit.ciphers.algorithms import ( + SEED as SEED, +) +from cryptography.hazmat.decrepit.ciphers.algorithms import ( + Blowfish as Blowfish, +) +from cryptography.hazmat.decrepit.ciphers.algorithms import ( + TripleDES as TripleDES, +) +from cryptography.hazmat.primitives._cipheralgorithm import _verify_key_size +from cryptography.hazmat.primitives.ciphers import ( + BlockCipherAlgorithm, + CipherAlgorithm, +) + + +class AES(BlockCipherAlgorithm): + name = "AES" + block_size = 128 + # 512 added to support AES-256-XTS, which uses 512-bit keys + key_sizes = frozenset([128, 192, 256, 512]) + + def __init__(self, key: bytes): + self.key = _verify_key_size(self, key) + + @property + def key_size(self) -> int: + return len(self.key) * 8 + + +class AES128(BlockCipherAlgorithm): + name = "AES" + block_size = 128 + key_sizes = frozenset([128]) + key_size = 128 + + def __init__(self, key: bytes): + self.key = _verify_key_size(self, key) + + +class AES256(BlockCipherAlgorithm): + name = "AES" + block_size = 128 + key_sizes = frozenset([256]) + key_size = 256 + + def __init__(self, key: bytes): + self.key = _verify_key_size(self, key) + + +class Camellia(BlockCipherAlgorithm): + name = "camellia" + block_size = 128 + key_sizes = frozenset([128, 192, 256]) + + def __init__(self, key: bytes): + self.key = _verify_key_size(self, key) + + @property + def key_size(self) -> int: + return len(self.key) * 8 + + +utils.deprecated( + ARC4, + __name__, + "ARC4 has been moved to " + "cryptography.hazmat.decrepit.ciphers.algorithms.ARC4 and " + "will be removed from this module in 48.0.0.", + utils.DeprecatedIn43, + name="ARC4", +) + + +utils.deprecated( + TripleDES, + __name__, + "TripleDES has been moved to " + "cryptography.hazmat.decrepit.ciphers.algorithms.TripleDES and " + "will be removed from this module in 48.0.0.", + utils.DeprecatedIn43, + name="TripleDES", +) + +utils.deprecated( + Blowfish, + __name__, + "Blowfish has been moved to " + "cryptography.hazmat.decrepit.ciphers.algorithms.Blowfish and " + "will be removed from this module in 45.0.0.", + utils.DeprecatedIn37, + name="Blowfish", +) + + +utils.deprecated( + CAST5, + __name__, + "CAST5 has been moved to " + "cryptography.hazmat.decrepit.ciphers.algorithms.CAST5 and " + "will be removed from this module in 45.0.0.", + utils.DeprecatedIn37, + name="CAST5", +) + + +utils.deprecated( + IDEA, + __name__, + "IDEA has been moved to " + "cryptography.hazmat.decrepit.ciphers.algorithms.IDEA and " + "will be removed from this module in 45.0.0.", + utils.DeprecatedIn37, + name="IDEA", +) + + +utils.deprecated( + SEED, + __name__, + "SEED has been moved to " + "cryptography.hazmat.decrepit.ciphers.algorithms.SEED and " + "will be removed from this module in 45.0.0.", + utils.DeprecatedIn37, + name="SEED", +) + + +class ChaCha20(CipherAlgorithm): + name = "ChaCha20" + key_sizes = frozenset([256]) + + def __init__(self, key: bytes, nonce: bytes): + self.key = _verify_key_size(self, key) + utils._check_byteslike("nonce", nonce) + + if len(nonce) != 16: + raise ValueError("nonce must be 128-bits (16 bytes)") + + self._nonce = nonce + + @property + def nonce(self) -> bytes: + return self._nonce + + @property + def key_size(self) -> int: + return len(self.key) * 8 + + +class SM4(BlockCipherAlgorithm): + name = "SM4" + block_size = 128 + key_sizes = frozenset([128]) + + def __init__(self, key: bytes): + self.key = _verify_key_size(self, key) + + @property + def key_size(self) -> int: + return len(self.key) * 8 diff --git a/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/ciphers/base.py b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/ciphers/base.py new file mode 100644 index 0000000..ebfa805 --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/ciphers/base.py @@ -0,0 +1,145 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +from __future__ import annotations + +import abc +import typing + +from cryptography.hazmat.bindings._rust import openssl as rust_openssl +from cryptography.hazmat.primitives._cipheralgorithm import CipherAlgorithm +from cryptography.hazmat.primitives.ciphers import modes + + +class CipherContext(metaclass=abc.ABCMeta): + @abc.abstractmethod + def update(self, data: bytes) -> bytes: + """ + Processes the provided bytes through the cipher and returns the results + as bytes. + """ + + @abc.abstractmethod + def update_into(self, data: bytes, buf: bytes) -> int: + """ + Processes the provided bytes and writes the resulting data into the + provided buffer. Returns the number of bytes written. + """ + + @abc.abstractmethod + def finalize(self) -> bytes: + """ + Returns the results of processing the final block as bytes. + """ + + @abc.abstractmethod + def reset_nonce(self, nonce: bytes) -> None: + """ + Resets the nonce for the cipher context to the provided value. + Raises an exception if it does not support reset or if the + provided nonce does not have a valid length. + """ + + +class AEADCipherContext(CipherContext, metaclass=abc.ABCMeta): + @abc.abstractmethod + def authenticate_additional_data(self, data: bytes) -> None: + """ + Authenticates the provided bytes. + """ + + +class AEADDecryptionContext(AEADCipherContext, metaclass=abc.ABCMeta): + @abc.abstractmethod + def finalize_with_tag(self, tag: bytes) -> bytes: + """ + Returns the results of processing the final block as bytes and allows + delayed passing of the authentication tag. + """ + + +class AEADEncryptionContext(AEADCipherContext, metaclass=abc.ABCMeta): + @property + @abc.abstractmethod + def tag(self) -> bytes: + """ + Returns tag bytes. This is only available after encryption is + finalized. + """ + + +Mode = typing.TypeVar( + "Mode", bound=typing.Optional[modes.Mode], covariant=True +) + + +class Cipher(typing.Generic[Mode]): + def __init__( + self, + algorithm: CipherAlgorithm, + mode: Mode, + backend: typing.Any = None, + ) -> None: + if not isinstance(algorithm, CipherAlgorithm): + raise TypeError("Expected interface of CipherAlgorithm.") + + if mode is not None: + # mypy needs this assert to narrow the type from our generic + # type. Maybe it won't some time in the future. + assert isinstance(mode, modes.Mode) + mode.validate_for_algorithm(algorithm) + + self.algorithm = algorithm + self.mode = mode + + @typing.overload + def encryptor( + self: Cipher[modes.ModeWithAuthenticationTag], + ) -> AEADEncryptionContext: ... + + @typing.overload + def encryptor( + self: _CIPHER_TYPE, + ) -> CipherContext: ... + + def encryptor(self): + if isinstance(self.mode, modes.ModeWithAuthenticationTag): + if self.mode.tag is not None: + raise ValueError( + "Authentication tag must be None when encrypting." + ) + + return rust_openssl.ciphers.create_encryption_ctx( + self.algorithm, self.mode + ) + + @typing.overload + def decryptor( + self: Cipher[modes.ModeWithAuthenticationTag], + ) -> AEADDecryptionContext: ... + + @typing.overload + def decryptor( + self: _CIPHER_TYPE, + ) -> CipherContext: ... + + def decryptor(self): + return rust_openssl.ciphers.create_decryption_ctx( + self.algorithm, self.mode + ) + + +_CIPHER_TYPE = Cipher[ + typing.Union[ + modes.ModeWithNonce, + modes.ModeWithTweak, + None, + modes.ECB, + modes.ModeWithInitializationVector, + ] +] + +CipherContext.register(rust_openssl.ciphers.CipherContext) +AEADEncryptionContext.register(rust_openssl.ciphers.AEADEncryptionContext) +AEADDecryptionContext.register(rust_openssl.ciphers.AEADDecryptionContext) diff --git a/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/ciphers/modes.py b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/ciphers/modes.py new file mode 100644 index 0000000..1dd2cc1 --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/ciphers/modes.py @@ -0,0 +1,268 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +from __future__ import annotations + +import abc + +from cryptography import utils +from cryptography.exceptions import UnsupportedAlgorithm, _Reasons +from cryptography.hazmat.primitives._cipheralgorithm import ( + BlockCipherAlgorithm, + CipherAlgorithm, +) +from cryptography.hazmat.primitives.ciphers import algorithms + + +class Mode(metaclass=abc.ABCMeta): + @property + @abc.abstractmethod + def name(self) -> str: + """ + A string naming this mode (e.g. "ECB", "CBC"). + """ + + @abc.abstractmethod + def validate_for_algorithm(self, algorithm: CipherAlgorithm) -> None: + """ + Checks that all the necessary invariants of this (mode, algorithm) + combination are met. + """ + + +class ModeWithInitializationVector(Mode, metaclass=abc.ABCMeta): + @property + @abc.abstractmethod + def initialization_vector(self) -> bytes: + """ + The value of the initialization vector for this mode as bytes. + """ + + +class ModeWithTweak(Mode, metaclass=abc.ABCMeta): + @property + @abc.abstractmethod + def tweak(self) -> bytes: + """ + The value of the tweak for this mode as bytes. + """ + + +class ModeWithNonce(Mode, metaclass=abc.ABCMeta): + @property + @abc.abstractmethod + def nonce(self) -> bytes: + """ + The value of the nonce for this mode as bytes. + """ + + +class ModeWithAuthenticationTag(Mode, metaclass=abc.ABCMeta): + @property + @abc.abstractmethod + def tag(self) -> bytes | None: + """ + The value of the tag supplied to the constructor of this mode. + """ + + +def _check_aes_key_length(self: Mode, algorithm: CipherAlgorithm) -> None: + if algorithm.key_size > 256 and algorithm.name == "AES": + raise ValueError( + "Only 128, 192, and 256 bit keys are allowed for this AES mode" + ) + + +def _check_iv_length( + self: ModeWithInitializationVector, algorithm: BlockCipherAlgorithm +) -> None: + iv_len = len(self.initialization_vector) + if iv_len * 8 != algorithm.block_size: + raise ValueError(f"Invalid IV size ({iv_len}) for {self.name}.") + + +def _check_nonce_length( + nonce: bytes, name: str, algorithm: CipherAlgorithm +) -> None: + if not isinstance(algorithm, BlockCipherAlgorithm): + raise UnsupportedAlgorithm( + f"{name} requires a block cipher algorithm", + _Reasons.UNSUPPORTED_CIPHER, + ) + if len(nonce) * 8 != algorithm.block_size: + raise ValueError(f"Invalid nonce size ({len(nonce)}) for {name}.") + + +def _check_iv_and_key_length( + self: ModeWithInitializationVector, algorithm: CipherAlgorithm +) -> None: + if not isinstance(algorithm, BlockCipherAlgorithm): + raise UnsupportedAlgorithm( + f"{self} requires a block cipher algorithm", + _Reasons.UNSUPPORTED_CIPHER, + ) + _check_aes_key_length(self, algorithm) + _check_iv_length(self, algorithm) + + +class CBC(ModeWithInitializationVector): + name = "CBC" + + def __init__(self, initialization_vector: bytes): + utils._check_byteslike("initialization_vector", initialization_vector) + self._initialization_vector = initialization_vector + + @property + def initialization_vector(self) -> bytes: + return self._initialization_vector + + validate_for_algorithm = _check_iv_and_key_length + + +class XTS(ModeWithTweak): + name = "XTS" + + def __init__(self, tweak: bytes): + utils._check_byteslike("tweak", tweak) + + if len(tweak) != 16: + raise ValueError("tweak must be 128-bits (16 bytes)") + + self._tweak = tweak + + @property + def tweak(self) -> bytes: + return self._tweak + + def validate_for_algorithm(self, algorithm: CipherAlgorithm) -> None: + if isinstance(algorithm, (algorithms.AES128, algorithms.AES256)): + raise TypeError( + "The AES128 and AES256 classes do not support XTS, please use " + "the standard AES class instead." + ) + + if algorithm.key_size not in (256, 512): + raise ValueError( + "The XTS specification requires a 256-bit key for AES-128-XTS" + " and 512-bit key for AES-256-XTS" + ) + + +class ECB(Mode): + name = "ECB" + + validate_for_algorithm = _check_aes_key_length + + +class OFB(ModeWithInitializationVector): + name = "OFB" + + def __init__(self, initialization_vector: bytes): + utils._check_byteslike("initialization_vector", initialization_vector) + self._initialization_vector = initialization_vector + + @property + def initialization_vector(self) -> bytes: + return self._initialization_vector + + validate_for_algorithm = _check_iv_and_key_length + + +class CFB(ModeWithInitializationVector): + name = "CFB" + + def __init__(self, initialization_vector: bytes): + utils._check_byteslike("initialization_vector", initialization_vector) + self._initialization_vector = initialization_vector + + @property + def initialization_vector(self) -> bytes: + return self._initialization_vector + + validate_for_algorithm = _check_iv_and_key_length + + +class CFB8(ModeWithInitializationVector): + name = "CFB8" + + def __init__(self, initialization_vector: bytes): + utils._check_byteslike("initialization_vector", initialization_vector) + self._initialization_vector = initialization_vector + + @property + def initialization_vector(self) -> bytes: + return self._initialization_vector + + validate_for_algorithm = _check_iv_and_key_length + + +class CTR(ModeWithNonce): + name = "CTR" + + def __init__(self, nonce: bytes): + utils._check_byteslike("nonce", nonce) + self._nonce = nonce + + @property + def nonce(self) -> bytes: + return self._nonce + + def validate_for_algorithm(self, algorithm: CipherAlgorithm) -> None: + _check_aes_key_length(self, algorithm) + _check_nonce_length(self.nonce, self.name, algorithm) + + +class GCM(ModeWithInitializationVector, ModeWithAuthenticationTag): + name = "GCM" + _MAX_ENCRYPTED_BYTES = (2**39 - 256) // 8 + _MAX_AAD_BYTES = (2**64) // 8 + + def __init__( + self, + initialization_vector: bytes, + tag: bytes | None = None, + min_tag_length: int = 16, + ): + # OpenSSL 3.0.0 constrains GCM IVs to [64, 1024] bits inclusive + # This is a sane limit anyway so we'll enforce it here. + utils._check_byteslike("initialization_vector", initialization_vector) + if len(initialization_vector) < 8 or len(initialization_vector) > 128: + raise ValueError( + "initialization_vector must be between 8 and 128 bytes (64 " + "and 1024 bits)." + ) + self._initialization_vector = initialization_vector + if tag is not None: + utils._check_bytes("tag", tag) + if min_tag_length < 4: + raise ValueError("min_tag_length must be >= 4") + if len(tag) < min_tag_length: + raise ValueError( + f"Authentication tag must be {min_tag_length} bytes or " + "longer." + ) + self._tag = tag + self._min_tag_length = min_tag_length + + @property + def tag(self) -> bytes | None: + return self._tag + + @property + def initialization_vector(self) -> bytes: + return self._initialization_vector + + def validate_for_algorithm(self, algorithm: CipherAlgorithm) -> None: + _check_aes_key_length(self, algorithm) + if not isinstance(algorithm, BlockCipherAlgorithm): + raise UnsupportedAlgorithm( + "GCM requires a block cipher algorithm", + _Reasons.UNSUPPORTED_CIPHER, + ) + block_size_bytes = algorithm.block_size // 8 + if self._tag is not None and len(self._tag) > block_size_bytes: + raise ValueError( + f"Authentication tag cannot be more than {block_size_bytes} " + "bytes." + ) diff --git a/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/cmac.py b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/cmac.py new file mode 100644 index 0000000..2c67ce2 --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/cmac.py @@ -0,0 +1,10 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +from __future__ import annotations + +from cryptography.hazmat.bindings._rust import openssl as rust_openssl + +__all__ = ["CMAC"] +CMAC = rust_openssl.cmac.CMAC diff --git a/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/constant_time.py b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/constant_time.py new file mode 100644 index 0000000..3975c71 --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/constant_time.py @@ -0,0 +1,14 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +from __future__ import annotations + +import hmac + + +def bytes_eq(a: bytes, b: bytes) -> bool: + if not isinstance(a, bytes) or not isinstance(b, bytes): + raise TypeError("a and b must be bytes.") + + return hmac.compare_digest(a, b) diff --git a/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/hashes.py b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/hashes.py new file mode 100644 index 0000000..b819e39 --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/hashes.py @@ -0,0 +1,242 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +from __future__ import annotations + +import abc + +from cryptography.hazmat.bindings._rust import openssl as rust_openssl + +__all__ = [ + "MD5", + "SHA1", + "SHA3_224", + "SHA3_256", + "SHA3_384", + "SHA3_512", + "SHA224", + "SHA256", + "SHA384", + "SHA512", + "SHA512_224", + "SHA512_256", + "SHAKE128", + "SHAKE256", + "SM3", + "BLAKE2b", + "BLAKE2s", + "ExtendableOutputFunction", + "Hash", + "HashAlgorithm", + "HashContext", +] + + +class HashAlgorithm(metaclass=abc.ABCMeta): + @property + @abc.abstractmethod + def name(self) -> str: + """ + A string naming this algorithm (e.g. "sha256", "md5"). + """ + + @property + @abc.abstractmethod + def digest_size(self) -> int: + """ + The size of the resulting digest in bytes. + """ + + @property + @abc.abstractmethod + def block_size(self) -> int | None: + """ + The internal block size of the hash function, or None if the hash + function does not use blocks internally (e.g. SHA3). + """ + + +class HashContext(metaclass=abc.ABCMeta): + @property + @abc.abstractmethod + def algorithm(self) -> HashAlgorithm: + """ + A HashAlgorithm that will be used by this context. + """ + + @abc.abstractmethod + def update(self, data: bytes) -> None: + """ + Processes the provided bytes through the hash. + """ + + @abc.abstractmethod + def finalize(self) -> bytes: + """ + Finalizes the hash context and returns the hash digest as bytes. + """ + + @abc.abstractmethod + def copy(self) -> HashContext: + """ + Return a HashContext that is a copy of the current context. + """ + + +Hash = rust_openssl.hashes.Hash +HashContext.register(Hash) + + +class ExtendableOutputFunction(metaclass=abc.ABCMeta): + """ + An interface for extendable output functions. + """ + + +class SHA1(HashAlgorithm): + name = "sha1" + digest_size = 20 + block_size = 64 + + +class SHA512_224(HashAlgorithm): # noqa: N801 + name = "sha512-224" + digest_size = 28 + block_size = 128 + + +class SHA512_256(HashAlgorithm): # noqa: N801 + name = "sha512-256" + digest_size = 32 + block_size = 128 + + +class SHA224(HashAlgorithm): + name = "sha224" + digest_size = 28 + block_size = 64 + + +class SHA256(HashAlgorithm): + name = "sha256" + digest_size = 32 + block_size = 64 + + +class SHA384(HashAlgorithm): + name = "sha384" + digest_size = 48 + block_size = 128 + + +class SHA512(HashAlgorithm): + name = "sha512" + digest_size = 64 + block_size = 128 + + +class SHA3_224(HashAlgorithm): # noqa: N801 + name = "sha3-224" + digest_size = 28 + block_size = None + + +class SHA3_256(HashAlgorithm): # noqa: N801 + name = "sha3-256" + digest_size = 32 + block_size = None + + +class SHA3_384(HashAlgorithm): # noqa: N801 + name = "sha3-384" + digest_size = 48 + block_size = None + + +class SHA3_512(HashAlgorithm): # noqa: N801 + name = "sha3-512" + digest_size = 64 + block_size = None + + +class SHAKE128(HashAlgorithm, ExtendableOutputFunction): + name = "shake128" + block_size = None + + def __init__(self, digest_size: int): + if not isinstance(digest_size, int): + raise TypeError("digest_size must be an integer") + + if digest_size < 1: + raise ValueError("digest_size must be a positive integer") + + self._digest_size = digest_size + + @property + def digest_size(self) -> int: + return self._digest_size + + +class SHAKE256(HashAlgorithm, ExtendableOutputFunction): + name = "shake256" + block_size = None + + def __init__(self, digest_size: int): + if not isinstance(digest_size, int): + raise TypeError("digest_size must be an integer") + + if digest_size < 1: + raise ValueError("digest_size must be a positive integer") + + self._digest_size = digest_size + + @property + def digest_size(self) -> int: + return self._digest_size + + +class MD5(HashAlgorithm): + name = "md5" + digest_size = 16 + block_size = 64 + + +class BLAKE2b(HashAlgorithm): + name = "blake2b" + _max_digest_size = 64 + _min_digest_size = 1 + block_size = 128 + + def __init__(self, digest_size: int): + if digest_size != 64: + raise ValueError("Digest size must be 64") + + self._digest_size = digest_size + + @property + def digest_size(self) -> int: + return self._digest_size + + +class BLAKE2s(HashAlgorithm): + name = "blake2s" + block_size = 64 + _max_digest_size = 32 + _min_digest_size = 1 + + def __init__(self, digest_size: int): + if digest_size != 32: + raise ValueError("Digest size must be 32") + + self._digest_size = digest_size + + @property + def digest_size(self) -> int: + return self._digest_size + + +class SM3(HashAlgorithm): + name = "sm3" + digest_size = 32 + block_size = 64 diff --git a/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/hmac.py b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/hmac.py new file mode 100644 index 0000000..a9442d5 --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/hmac.py @@ -0,0 +1,13 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +from __future__ import annotations + +from cryptography.hazmat.bindings._rust import openssl as rust_openssl +from cryptography.hazmat.primitives import hashes + +__all__ = ["HMAC"] + +HMAC = rust_openssl.hmac.HMAC +hashes.HashContext.register(HMAC) diff --git a/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/kdf/__init__.py b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/kdf/__init__.py new file mode 100644 index 0000000..79bb459 --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/kdf/__init__.py @@ -0,0 +1,23 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +from __future__ import annotations + +import abc + + +class KeyDerivationFunction(metaclass=abc.ABCMeta): + @abc.abstractmethod + def derive(self, key_material: bytes) -> bytes: + """ + Deterministically generates and returns a new key based on the existing + key material. + """ + + @abc.abstractmethod + def verify(self, key_material: bytes, expected_key: bytes) -> None: + """ + Checks whether the key generated by the key material matches the + expected derived key. Raises an exception if they do not match. + """ diff --git a/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/kdf/__pycache__/__init__.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/kdf/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..a9df8f0 Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/kdf/__pycache__/__init__.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/kdf/__pycache__/concatkdf.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/kdf/__pycache__/concatkdf.cpython-312.pyc new file mode 100644 index 0000000..bfd0251 Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/kdf/__pycache__/concatkdf.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/kdf/__pycache__/hkdf.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/kdf/__pycache__/hkdf.cpython-312.pyc new file mode 100644 index 0000000..0758721 Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/kdf/__pycache__/hkdf.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/kdf/__pycache__/kbkdf.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/kdf/__pycache__/kbkdf.cpython-312.pyc new file mode 100644 index 0000000..0537d85 Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/kdf/__pycache__/kbkdf.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/kdf/__pycache__/pbkdf2.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/kdf/__pycache__/pbkdf2.cpython-312.pyc new file mode 100644 index 0000000..b27d749 Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/kdf/__pycache__/pbkdf2.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/kdf/__pycache__/scrypt.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/kdf/__pycache__/scrypt.cpython-312.pyc new file mode 100644 index 0000000..8c28ea8 Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/kdf/__pycache__/scrypt.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/kdf/__pycache__/x963kdf.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/kdf/__pycache__/x963kdf.cpython-312.pyc new file mode 100644 index 0000000..5065845 Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/kdf/__pycache__/x963kdf.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/kdf/concatkdf.py b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/kdf/concatkdf.py new file mode 100644 index 0000000..96d9d4c --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/kdf/concatkdf.py @@ -0,0 +1,124 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +from __future__ import annotations + +import typing + +from cryptography import utils +from cryptography.exceptions import AlreadyFinalized, InvalidKey +from cryptography.hazmat.primitives import constant_time, hashes, hmac +from cryptography.hazmat.primitives.kdf import KeyDerivationFunction + + +def _int_to_u32be(n: int) -> bytes: + return n.to_bytes(length=4, byteorder="big") + + +def _common_args_checks( + algorithm: hashes.HashAlgorithm, + length: int, + otherinfo: bytes | None, +) -> None: + max_length = algorithm.digest_size * (2**32 - 1) + if length > max_length: + raise ValueError(f"Cannot derive keys larger than {max_length} bits.") + if otherinfo is not None: + utils._check_bytes("otherinfo", otherinfo) + + +def _concatkdf_derive( + key_material: bytes, + length: int, + auxfn: typing.Callable[[], hashes.HashContext], + otherinfo: bytes, +) -> bytes: + utils._check_byteslike("key_material", key_material) + output = [b""] + outlen = 0 + counter = 1 + + while length > outlen: + h = auxfn() + h.update(_int_to_u32be(counter)) + h.update(key_material) + h.update(otherinfo) + output.append(h.finalize()) + outlen += len(output[-1]) + counter += 1 + + return b"".join(output)[:length] + + +class ConcatKDFHash(KeyDerivationFunction): + def __init__( + self, + algorithm: hashes.HashAlgorithm, + length: int, + otherinfo: bytes | None, + backend: typing.Any = None, + ): + _common_args_checks(algorithm, length, otherinfo) + self._algorithm = algorithm + self._length = length + self._otherinfo: bytes = otherinfo if otherinfo is not None else b"" + + self._used = False + + def _hash(self) -> hashes.Hash: + return hashes.Hash(self._algorithm) + + def derive(self, key_material: bytes) -> bytes: + if self._used: + raise AlreadyFinalized + self._used = True + return _concatkdf_derive( + key_material, self._length, self._hash, self._otherinfo + ) + + def verify(self, key_material: bytes, expected_key: bytes) -> None: + if not constant_time.bytes_eq(self.derive(key_material), expected_key): + raise InvalidKey + + +class ConcatKDFHMAC(KeyDerivationFunction): + def __init__( + self, + algorithm: hashes.HashAlgorithm, + length: int, + salt: bytes | None, + otherinfo: bytes | None, + backend: typing.Any = None, + ): + _common_args_checks(algorithm, length, otherinfo) + self._algorithm = algorithm + self._length = length + self._otherinfo: bytes = otherinfo if otherinfo is not None else b"" + + if algorithm.block_size is None: + raise TypeError(f"{algorithm.name} is unsupported for ConcatKDF") + + if salt is None: + salt = b"\x00" * algorithm.block_size + else: + utils._check_bytes("salt", salt) + + self._salt = salt + + self._used = False + + def _hmac(self) -> hmac.HMAC: + return hmac.HMAC(self._salt, self._algorithm) + + def derive(self, key_material: bytes) -> bytes: + if self._used: + raise AlreadyFinalized + self._used = True + return _concatkdf_derive( + key_material, self._length, self._hmac, self._otherinfo + ) + + def verify(self, key_material: bytes, expected_key: bytes) -> None: + if not constant_time.bytes_eq(self.derive(key_material), expected_key): + raise InvalidKey diff --git a/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/kdf/hkdf.py b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/kdf/hkdf.py new file mode 100644 index 0000000..ee562d2 --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/kdf/hkdf.py @@ -0,0 +1,101 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +from __future__ import annotations + +import typing + +from cryptography import utils +from cryptography.exceptions import AlreadyFinalized, InvalidKey +from cryptography.hazmat.primitives import constant_time, hashes, hmac +from cryptography.hazmat.primitives.kdf import KeyDerivationFunction + + +class HKDF(KeyDerivationFunction): + def __init__( + self, + algorithm: hashes.HashAlgorithm, + length: int, + salt: bytes | None, + info: bytes | None, + backend: typing.Any = None, + ): + self._algorithm = algorithm + + if salt is None: + salt = b"\x00" * self._algorithm.digest_size + else: + utils._check_bytes("salt", salt) + + self._salt = salt + + self._hkdf_expand = HKDFExpand(self._algorithm, length, info) + + def _extract(self, key_material: bytes) -> bytes: + h = hmac.HMAC(self._salt, self._algorithm) + h.update(key_material) + return h.finalize() + + def derive(self, key_material: bytes) -> bytes: + utils._check_byteslike("key_material", key_material) + return self._hkdf_expand.derive(self._extract(key_material)) + + def verify(self, key_material: bytes, expected_key: bytes) -> None: + if not constant_time.bytes_eq(self.derive(key_material), expected_key): + raise InvalidKey + + +class HKDFExpand(KeyDerivationFunction): + def __init__( + self, + algorithm: hashes.HashAlgorithm, + length: int, + info: bytes | None, + backend: typing.Any = None, + ): + self._algorithm = algorithm + + max_length = 255 * algorithm.digest_size + + if length > max_length: + raise ValueError( + f"Cannot derive keys larger than {max_length} octets." + ) + + self._length = length + + if info is None: + info = b"" + else: + utils._check_bytes("info", info) + + self._info = info + + self._used = False + + def _expand(self, key_material: bytes) -> bytes: + output = [b""] + counter = 1 + + while self._algorithm.digest_size * (len(output) - 1) < self._length: + h = hmac.HMAC(key_material, self._algorithm) + h.update(output[-1]) + h.update(self._info) + h.update(bytes([counter])) + output.append(h.finalize()) + counter += 1 + + return b"".join(output)[: self._length] + + def derive(self, key_material: bytes) -> bytes: + utils._check_byteslike("key_material", key_material) + if self._used: + raise AlreadyFinalized + + self._used = True + return self._expand(key_material) + + def verify(self, key_material: bytes, expected_key: bytes) -> None: + if not constant_time.bytes_eq(self.derive(key_material), expected_key): + raise InvalidKey diff --git a/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/kdf/kbkdf.py b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/kdf/kbkdf.py new file mode 100644 index 0000000..802b484 --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/kdf/kbkdf.py @@ -0,0 +1,302 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +from __future__ import annotations + +import typing + +from cryptography import utils +from cryptography.exceptions import ( + AlreadyFinalized, + InvalidKey, + UnsupportedAlgorithm, + _Reasons, +) +from cryptography.hazmat.primitives import ( + ciphers, + cmac, + constant_time, + hashes, + hmac, +) +from cryptography.hazmat.primitives.kdf import KeyDerivationFunction + + +class Mode(utils.Enum): + CounterMode = "ctr" + + +class CounterLocation(utils.Enum): + BeforeFixed = "before_fixed" + AfterFixed = "after_fixed" + MiddleFixed = "middle_fixed" + + +class _KBKDFDeriver: + def __init__( + self, + prf: typing.Callable, + mode: Mode, + length: int, + rlen: int, + llen: int | None, + location: CounterLocation, + break_location: int | None, + label: bytes | None, + context: bytes | None, + fixed: bytes | None, + ): + assert callable(prf) + + if not isinstance(mode, Mode): + raise TypeError("mode must be of type Mode") + + if not isinstance(location, CounterLocation): + raise TypeError("location must be of type CounterLocation") + + if break_location is None and location is CounterLocation.MiddleFixed: + raise ValueError("Please specify a break_location") + + if ( + break_location is not None + and location != CounterLocation.MiddleFixed + ): + raise ValueError( + "break_location is ignored when location is not" + " CounterLocation.MiddleFixed" + ) + + if break_location is not None and not isinstance(break_location, int): + raise TypeError("break_location must be an integer") + + if break_location is not None and break_location < 0: + raise ValueError("break_location must be a positive integer") + + if (label or context) and fixed: + raise ValueError( + "When supplying fixed data, label and context are ignored." + ) + + if rlen is None or not self._valid_byte_length(rlen): + raise ValueError("rlen must be between 1 and 4") + + if llen is None and fixed is None: + raise ValueError("Please specify an llen") + + if llen is not None and not isinstance(llen, int): + raise TypeError("llen must be an integer") + + if llen == 0: + raise ValueError("llen must be non-zero") + + if label is None: + label = b"" + + if context is None: + context = b"" + + utils._check_bytes("label", label) + utils._check_bytes("context", context) + self._prf = prf + self._mode = mode + self._length = length + self._rlen = rlen + self._llen = llen + self._location = location + self._break_location = break_location + self._label = label + self._context = context + self._used = False + self._fixed_data = fixed + + @staticmethod + def _valid_byte_length(value: int) -> bool: + if not isinstance(value, int): + raise TypeError("value must be of type int") + + value_bin = utils.int_to_bytes(1, value) + if not 1 <= len(value_bin) <= 4: + return False + return True + + def derive(self, key_material: bytes, prf_output_size: int) -> bytes: + if self._used: + raise AlreadyFinalized + + utils._check_byteslike("key_material", key_material) + self._used = True + + # inverse floor division (equivalent to ceiling) + rounds = -(-self._length // prf_output_size) + + output = [b""] + + # For counter mode, the number of iterations shall not be + # larger than 2^r-1, where r <= 32 is the binary length of the counter + # This ensures that the counter values used as an input to the + # PRF will not repeat during a particular call to the KDF function. + r_bin = utils.int_to_bytes(1, self._rlen) + if rounds > pow(2, len(r_bin) * 8) - 1: + raise ValueError("There are too many iterations.") + + fixed = self._generate_fixed_input() + + if self._location == CounterLocation.BeforeFixed: + data_before_ctr = b"" + data_after_ctr = fixed + elif self._location == CounterLocation.AfterFixed: + data_before_ctr = fixed + data_after_ctr = b"" + else: + if isinstance( + self._break_location, int + ) and self._break_location > len(fixed): + raise ValueError("break_location offset > len(fixed)") + data_before_ctr = fixed[: self._break_location] + data_after_ctr = fixed[self._break_location :] + + for i in range(1, rounds + 1): + h = self._prf(key_material) + + counter = utils.int_to_bytes(i, self._rlen) + input_data = data_before_ctr + counter + data_after_ctr + + h.update(input_data) + + output.append(h.finalize()) + + return b"".join(output)[: self._length] + + def _generate_fixed_input(self) -> bytes: + if self._fixed_data and isinstance(self._fixed_data, bytes): + return self._fixed_data + + l_val = utils.int_to_bytes(self._length * 8, self._llen) + + return b"".join([self._label, b"\x00", self._context, l_val]) + + +class KBKDFHMAC(KeyDerivationFunction): + def __init__( + self, + algorithm: hashes.HashAlgorithm, + mode: Mode, + length: int, + rlen: int, + llen: int | None, + location: CounterLocation, + label: bytes | None, + context: bytes | None, + fixed: bytes | None, + backend: typing.Any = None, + *, + break_location: int | None = None, + ): + if not isinstance(algorithm, hashes.HashAlgorithm): + raise UnsupportedAlgorithm( + "Algorithm supplied is not a supported hash algorithm.", + _Reasons.UNSUPPORTED_HASH, + ) + + from cryptography.hazmat.backends.openssl.backend import ( + backend as ossl, + ) + + if not ossl.hmac_supported(algorithm): + raise UnsupportedAlgorithm( + "Algorithm supplied is not a supported hmac algorithm.", + _Reasons.UNSUPPORTED_HASH, + ) + + self._algorithm = algorithm + + self._deriver = _KBKDFDeriver( + self._prf, + mode, + length, + rlen, + llen, + location, + break_location, + label, + context, + fixed, + ) + + def _prf(self, key_material: bytes) -> hmac.HMAC: + return hmac.HMAC(key_material, self._algorithm) + + def derive(self, key_material: bytes) -> bytes: + return self._deriver.derive(key_material, self._algorithm.digest_size) + + def verify(self, key_material: bytes, expected_key: bytes) -> None: + if not constant_time.bytes_eq(self.derive(key_material), expected_key): + raise InvalidKey + + +class KBKDFCMAC(KeyDerivationFunction): + def __init__( + self, + algorithm, + mode: Mode, + length: int, + rlen: int, + llen: int | None, + location: CounterLocation, + label: bytes | None, + context: bytes | None, + fixed: bytes | None, + backend: typing.Any = None, + *, + break_location: int | None = None, + ): + if not issubclass( + algorithm, ciphers.BlockCipherAlgorithm + ) or not issubclass(algorithm, ciphers.CipherAlgorithm): + raise UnsupportedAlgorithm( + "Algorithm supplied is not a supported cipher algorithm.", + _Reasons.UNSUPPORTED_CIPHER, + ) + + self._algorithm = algorithm + self._cipher: ciphers.BlockCipherAlgorithm | None = None + + self._deriver = _KBKDFDeriver( + self._prf, + mode, + length, + rlen, + llen, + location, + break_location, + label, + context, + fixed, + ) + + def _prf(self, _: bytes) -> cmac.CMAC: + assert self._cipher is not None + + return cmac.CMAC(self._cipher) + + def derive(self, key_material: bytes) -> bytes: + self._cipher = self._algorithm(key_material) + + assert self._cipher is not None + + from cryptography.hazmat.backends.openssl.backend import ( + backend as ossl, + ) + + if not ossl.cmac_algorithm_supported(self._cipher): + raise UnsupportedAlgorithm( + "Algorithm supplied is not a supported cipher algorithm.", + _Reasons.UNSUPPORTED_CIPHER, + ) + + return self._deriver.derive(key_material, self._cipher.block_size // 8) + + def verify(self, key_material: bytes, expected_key: bytes) -> None: + if not constant_time.bytes_eq(self.derive(key_material), expected_key): + raise InvalidKey diff --git a/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/kdf/pbkdf2.py b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/kdf/pbkdf2.py new file mode 100644 index 0000000..82689eb --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/kdf/pbkdf2.py @@ -0,0 +1,62 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +from __future__ import annotations + +import typing + +from cryptography import utils +from cryptography.exceptions import ( + AlreadyFinalized, + InvalidKey, + UnsupportedAlgorithm, + _Reasons, +) +from cryptography.hazmat.bindings._rust import openssl as rust_openssl +from cryptography.hazmat.primitives import constant_time, hashes +from cryptography.hazmat.primitives.kdf import KeyDerivationFunction + + +class PBKDF2HMAC(KeyDerivationFunction): + def __init__( + self, + algorithm: hashes.HashAlgorithm, + length: int, + salt: bytes, + iterations: int, + backend: typing.Any = None, + ): + from cryptography.hazmat.backends.openssl.backend import ( + backend as ossl, + ) + + if not ossl.pbkdf2_hmac_supported(algorithm): + raise UnsupportedAlgorithm( + f"{algorithm.name} is not supported for PBKDF2.", + _Reasons.UNSUPPORTED_HASH, + ) + self._used = False + self._algorithm = algorithm + self._length = length + utils._check_bytes("salt", salt) + self._salt = salt + self._iterations = iterations + + def derive(self, key_material: bytes) -> bytes: + if self._used: + raise AlreadyFinalized("PBKDF2 instances can only be used once.") + self._used = True + + return rust_openssl.kdf.derive_pbkdf2_hmac( + key_material, + self._algorithm, + self._salt, + self._iterations, + self._length, + ) + + def verify(self, key_material: bytes, expected_key: bytes) -> None: + derived_key = self.derive(key_material) + if not constant_time.bytes_eq(derived_key, expected_key): + raise InvalidKey("Keys do not match.") diff --git a/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/kdf/scrypt.py b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/kdf/scrypt.py new file mode 100644 index 0000000..05a4f67 --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/kdf/scrypt.py @@ -0,0 +1,80 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +from __future__ import annotations + +import sys +import typing + +from cryptography import utils +from cryptography.exceptions import ( + AlreadyFinalized, + InvalidKey, + UnsupportedAlgorithm, +) +from cryptography.hazmat.bindings._rust import openssl as rust_openssl +from cryptography.hazmat.primitives import constant_time +from cryptography.hazmat.primitives.kdf import KeyDerivationFunction + +# This is used by the scrypt tests to skip tests that require more memory +# than the MEM_LIMIT +_MEM_LIMIT = sys.maxsize // 2 + + +class Scrypt(KeyDerivationFunction): + def __init__( + self, + salt: bytes, + length: int, + n: int, + r: int, + p: int, + backend: typing.Any = None, + ): + from cryptography.hazmat.backends.openssl.backend import ( + backend as ossl, + ) + + if not ossl.scrypt_supported(): + raise UnsupportedAlgorithm( + "This version of OpenSSL does not support scrypt" + ) + self._length = length + utils._check_bytes("salt", salt) + if n < 2 or (n & (n - 1)) != 0: + raise ValueError("n must be greater than 1 and be a power of 2.") + + if r < 1: + raise ValueError("r must be greater than or equal to 1.") + + if p < 1: + raise ValueError("p must be greater than or equal to 1.") + + self._used = False + self._salt = salt + self._n = n + self._r = r + self._p = p + + def derive(self, key_material: bytes) -> bytes: + if self._used: + raise AlreadyFinalized("Scrypt instances can only be used once.") + self._used = True + + utils._check_byteslike("key_material", key_material) + + return rust_openssl.kdf.derive_scrypt( + key_material, + self._salt, + self._n, + self._r, + self._p, + _MEM_LIMIT, + self._length, + ) + + def verify(self, key_material: bytes, expected_key: bytes) -> None: + derived_key = self.derive(key_material) + if not constant_time.bytes_eq(derived_key, expected_key): + raise InvalidKey("Keys do not match.") diff --git a/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/kdf/x963kdf.py b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/kdf/x963kdf.py new file mode 100644 index 0000000..6e38366 --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/kdf/x963kdf.py @@ -0,0 +1,61 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +from __future__ import annotations + +import typing + +from cryptography import utils +from cryptography.exceptions import AlreadyFinalized, InvalidKey +from cryptography.hazmat.primitives import constant_time, hashes +from cryptography.hazmat.primitives.kdf import KeyDerivationFunction + + +def _int_to_u32be(n: int) -> bytes: + return n.to_bytes(length=4, byteorder="big") + + +class X963KDF(KeyDerivationFunction): + def __init__( + self, + algorithm: hashes.HashAlgorithm, + length: int, + sharedinfo: bytes | None, + backend: typing.Any = None, + ): + max_len = algorithm.digest_size * (2**32 - 1) + if length > max_len: + raise ValueError(f"Cannot derive keys larger than {max_len} bits.") + if sharedinfo is not None: + utils._check_bytes("sharedinfo", sharedinfo) + + self._algorithm = algorithm + self._length = length + self._sharedinfo = sharedinfo + self._used = False + + def derive(self, key_material: bytes) -> bytes: + if self._used: + raise AlreadyFinalized + self._used = True + utils._check_byteslike("key_material", key_material) + output = [b""] + outlen = 0 + counter = 1 + + while self._length > outlen: + h = hashes.Hash(self._algorithm) + h.update(key_material) + h.update(_int_to_u32be(counter)) + if self._sharedinfo is not None: + h.update(self._sharedinfo) + output.append(h.finalize()) + outlen += len(output[-1]) + counter += 1 + + return b"".join(output)[: self._length] + + def verify(self, key_material: bytes, expected_key: bytes) -> None: + if not constant_time.bytes_eq(self.derive(key_material), expected_key): + raise InvalidKey diff --git a/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/keywrap.py b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/keywrap.py new file mode 100644 index 0000000..b93d87d --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/keywrap.py @@ -0,0 +1,177 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +from __future__ import annotations + +import typing + +from cryptography.hazmat.primitives.ciphers import Cipher +from cryptography.hazmat.primitives.ciphers.algorithms import AES +from cryptography.hazmat.primitives.ciphers.modes import ECB +from cryptography.hazmat.primitives.constant_time import bytes_eq + + +def _wrap_core( + wrapping_key: bytes, + a: bytes, + r: list[bytes], +) -> bytes: + # RFC 3394 Key Wrap - 2.2.1 (index method) + encryptor = Cipher(AES(wrapping_key), ECB()).encryptor() + n = len(r) + for j in range(6): + for i in range(n): + # every encryption operation is a discrete 16 byte chunk (because + # AES has a 128-bit block size) and since we're using ECB it is + # safe to reuse the encryptor for the entire operation + b = encryptor.update(a + r[i]) + a = ( + int.from_bytes(b[:8], byteorder="big") ^ ((n * j) + i + 1) + ).to_bytes(length=8, byteorder="big") + r[i] = b[-8:] + + assert encryptor.finalize() == b"" + + return a + b"".join(r) + + +def aes_key_wrap( + wrapping_key: bytes, + key_to_wrap: bytes, + backend: typing.Any = None, +) -> bytes: + if len(wrapping_key) not in [16, 24, 32]: + raise ValueError("The wrapping key must be a valid AES key length") + + if len(key_to_wrap) < 16: + raise ValueError("The key to wrap must be at least 16 bytes") + + if len(key_to_wrap) % 8 != 0: + raise ValueError("The key to wrap must be a multiple of 8 bytes") + + a = b"\xa6\xa6\xa6\xa6\xa6\xa6\xa6\xa6" + r = [key_to_wrap[i : i + 8] for i in range(0, len(key_to_wrap), 8)] + return _wrap_core(wrapping_key, a, r) + + +def _unwrap_core( + wrapping_key: bytes, + a: bytes, + r: list[bytes], +) -> tuple[bytes, list[bytes]]: + # Implement RFC 3394 Key Unwrap - 2.2.2 (index method) + decryptor = Cipher(AES(wrapping_key), ECB()).decryptor() + n = len(r) + for j in reversed(range(6)): + for i in reversed(range(n)): + atr = ( + int.from_bytes(a, byteorder="big") ^ ((n * j) + i + 1) + ).to_bytes(length=8, byteorder="big") + r[i] + # every decryption operation is a discrete 16 byte chunk so + # it is safe to reuse the decryptor for the entire operation + b = decryptor.update(atr) + a = b[:8] + r[i] = b[-8:] + + assert decryptor.finalize() == b"" + return a, r + + +def aes_key_wrap_with_padding( + wrapping_key: bytes, + key_to_wrap: bytes, + backend: typing.Any = None, +) -> bytes: + if len(wrapping_key) not in [16, 24, 32]: + raise ValueError("The wrapping key must be a valid AES key length") + + aiv = b"\xa6\x59\x59\xa6" + len(key_to_wrap).to_bytes( + length=4, byteorder="big" + ) + # pad the key to wrap if necessary + pad = (8 - (len(key_to_wrap) % 8)) % 8 + key_to_wrap = key_to_wrap + b"\x00" * pad + if len(key_to_wrap) == 8: + # RFC 5649 - 4.1 - exactly 8 octets after padding + encryptor = Cipher(AES(wrapping_key), ECB()).encryptor() + b = encryptor.update(aiv + key_to_wrap) + assert encryptor.finalize() == b"" + return b + else: + r = [key_to_wrap[i : i + 8] for i in range(0, len(key_to_wrap), 8)] + return _wrap_core(wrapping_key, aiv, r) + + +def aes_key_unwrap_with_padding( + wrapping_key: bytes, + wrapped_key: bytes, + backend: typing.Any = None, +) -> bytes: + if len(wrapped_key) < 16: + raise InvalidUnwrap("Must be at least 16 bytes") + + if len(wrapping_key) not in [16, 24, 32]: + raise ValueError("The wrapping key must be a valid AES key length") + + if len(wrapped_key) == 16: + # RFC 5649 - 4.2 - exactly two 64-bit blocks + decryptor = Cipher(AES(wrapping_key), ECB()).decryptor() + out = decryptor.update(wrapped_key) + assert decryptor.finalize() == b"" + a = out[:8] + data = out[8:] + n = 1 + else: + r = [wrapped_key[i : i + 8] for i in range(0, len(wrapped_key), 8)] + encrypted_aiv = r.pop(0) + n = len(r) + a, r = _unwrap_core(wrapping_key, encrypted_aiv, r) + data = b"".join(r) + + # 1) Check that MSB(32,A) = A65959A6. + # 2) Check that 8*(n-1) < LSB(32,A) <= 8*n. If so, let + # MLI = LSB(32,A). + # 3) Let b = (8*n)-MLI, and then check that the rightmost b octets of + # the output data are zero. + mli = int.from_bytes(a[4:], byteorder="big") + b = (8 * n) - mli + if ( + not bytes_eq(a[:4], b"\xa6\x59\x59\xa6") + or not 8 * (n - 1) < mli <= 8 * n + or (b != 0 and not bytes_eq(data[-b:], b"\x00" * b)) + ): + raise InvalidUnwrap() + + if b == 0: + return data + else: + return data[:-b] + + +def aes_key_unwrap( + wrapping_key: bytes, + wrapped_key: bytes, + backend: typing.Any = None, +) -> bytes: + if len(wrapped_key) < 24: + raise InvalidUnwrap("Must be at least 24 bytes") + + if len(wrapped_key) % 8 != 0: + raise InvalidUnwrap("The wrapped key must be a multiple of 8 bytes") + + if len(wrapping_key) not in [16, 24, 32]: + raise ValueError("The wrapping key must be a valid AES key length") + + aiv = b"\xa6\xa6\xa6\xa6\xa6\xa6\xa6\xa6" + r = [wrapped_key[i : i + 8] for i in range(0, len(wrapped_key), 8)] + a = r.pop(0) + a, r = _unwrap_core(wrapping_key, a, r) + if not bytes_eq(a, aiv): + raise InvalidUnwrap() + + return b"".join(r) + + +class InvalidUnwrap(Exception): + pass diff --git a/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/padding.py b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/padding.py new file mode 100644 index 0000000..d1ca775 --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/padding.py @@ -0,0 +1,204 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +from __future__ import annotations + +import abc +import typing + +from cryptography import utils +from cryptography.exceptions import AlreadyFinalized +from cryptography.hazmat.bindings._rust import ( + PKCS7PaddingContext, + check_ansix923_padding, + check_pkcs7_padding, +) + + +class PaddingContext(metaclass=abc.ABCMeta): + @abc.abstractmethod + def update(self, data: bytes) -> bytes: + """ + Pads the provided bytes and returns any available data as bytes. + """ + + @abc.abstractmethod + def finalize(self) -> bytes: + """ + Finalize the padding, returns bytes. + """ + + +def _byte_padding_check(block_size: int) -> None: + if not (0 <= block_size <= 2040): + raise ValueError("block_size must be in range(0, 2041).") + + if block_size % 8 != 0: + raise ValueError("block_size must be a multiple of 8.") + + +def _byte_padding_update( + buffer_: bytes | None, data: bytes, block_size: int +) -> tuple[bytes, bytes]: + if buffer_ is None: + raise AlreadyFinalized("Context was already finalized.") + + utils._check_byteslike("data", data) + + buffer_ += bytes(data) + + finished_blocks = len(buffer_) // (block_size // 8) + + result = buffer_[: finished_blocks * (block_size // 8)] + buffer_ = buffer_[finished_blocks * (block_size // 8) :] + + return buffer_, result + + +def _byte_padding_pad( + buffer_: bytes | None, + block_size: int, + paddingfn: typing.Callable[[int], bytes], +) -> bytes: + if buffer_ is None: + raise AlreadyFinalized("Context was already finalized.") + + pad_size = block_size // 8 - len(buffer_) + return buffer_ + paddingfn(pad_size) + + +def _byte_unpadding_update( + buffer_: bytes | None, data: bytes, block_size: int +) -> tuple[bytes, bytes]: + if buffer_ is None: + raise AlreadyFinalized("Context was already finalized.") + + utils._check_byteslike("data", data) + + buffer_ += bytes(data) + + finished_blocks = max(len(buffer_) // (block_size // 8) - 1, 0) + + result = buffer_[: finished_blocks * (block_size // 8)] + buffer_ = buffer_[finished_blocks * (block_size // 8) :] + + return buffer_, result + + +def _byte_unpadding_check( + buffer_: bytes | None, + block_size: int, + checkfn: typing.Callable[[bytes], int], +) -> bytes: + if buffer_ is None: + raise AlreadyFinalized("Context was already finalized.") + + if len(buffer_) != block_size // 8: + raise ValueError("Invalid padding bytes.") + + valid = checkfn(buffer_) + + if not valid: + raise ValueError("Invalid padding bytes.") + + pad_size = buffer_[-1] + return buffer_[:-pad_size] + + +class PKCS7: + def __init__(self, block_size: int): + _byte_padding_check(block_size) + self.block_size = block_size + + def padder(self) -> PaddingContext: + return PKCS7PaddingContext(self.block_size) + + def unpadder(self) -> PaddingContext: + return _PKCS7UnpaddingContext(self.block_size) + + +class _PKCS7UnpaddingContext(PaddingContext): + _buffer: bytes | None + + def __init__(self, block_size: int): + self.block_size = block_size + # TODO: more copies than necessary, we should use zero-buffer (#193) + self._buffer = b"" + + def update(self, data: bytes) -> bytes: + self._buffer, result = _byte_unpadding_update( + self._buffer, data, self.block_size + ) + return result + + def finalize(self) -> bytes: + result = _byte_unpadding_check( + self._buffer, self.block_size, check_pkcs7_padding + ) + self._buffer = None + return result + + +PaddingContext.register(PKCS7PaddingContext) + + +class ANSIX923: + def __init__(self, block_size: int): + _byte_padding_check(block_size) + self.block_size = block_size + + def padder(self) -> PaddingContext: + return _ANSIX923PaddingContext(self.block_size) + + def unpadder(self) -> PaddingContext: + return _ANSIX923UnpaddingContext(self.block_size) + + +class _ANSIX923PaddingContext(PaddingContext): + _buffer: bytes | None + + def __init__(self, block_size: int): + self.block_size = block_size + # TODO: more copies than necessary, we should use zero-buffer (#193) + self._buffer = b"" + + def update(self, data: bytes) -> bytes: + self._buffer, result = _byte_padding_update( + self._buffer, data, self.block_size + ) + return result + + def _padding(self, size: int) -> bytes: + return bytes([0]) * (size - 1) + bytes([size]) + + def finalize(self) -> bytes: + result = _byte_padding_pad( + self._buffer, self.block_size, self._padding + ) + self._buffer = None + return result + + +class _ANSIX923UnpaddingContext(PaddingContext): + _buffer: bytes | None + + def __init__(self, block_size: int): + self.block_size = block_size + # TODO: more copies than necessary, we should use zero-buffer (#193) + self._buffer = b"" + + def update(self, data: bytes) -> bytes: + self._buffer, result = _byte_unpadding_update( + self._buffer, data, self.block_size + ) + return result + + def finalize(self) -> bytes: + result = _byte_unpadding_check( + self._buffer, + self.block_size, + check_ansix923_padding, + ) + self._buffer = None + return result diff --git a/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/poly1305.py b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/poly1305.py new file mode 100644 index 0000000..7f5a77a --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/poly1305.py @@ -0,0 +1,11 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +from __future__ import annotations + +from cryptography.hazmat.bindings._rust import openssl as rust_openssl + +__all__ = ["Poly1305"] + +Poly1305 = rust_openssl.poly1305.Poly1305 diff --git a/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/serialization/__init__.py b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/serialization/__init__.py new file mode 100644 index 0000000..07b2264 --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/serialization/__init__.py @@ -0,0 +1,63 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +from __future__ import annotations + +from cryptography.hazmat.primitives._serialization import ( + BestAvailableEncryption, + Encoding, + KeySerializationEncryption, + NoEncryption, + ParameterFormat, + PrivateFormat, + PublicFormat, + _KeySerializationEncryption, +) +from cryptography.hazmat.primitives.serialization.base import ( + load_der_parameters, + load_der_private_key, + load_der_public_key, + load_pem_parameters, + load_pem_private_key, + load_pem_public_key, +) +from cryptography.hazmat.primitives.serialization.ssh import ( + SSHCertificate, + SSHCertificateBuilder, + SSHCertificateType, + SSHCertPrivateKeyTypes, + SSHCertPublicKeyTypes, + SSHPrivateKeyTypes, + SSHPublicKeyTypes, + load_ssh_private_key, + load_ssh_public_identity, + load_ssh_public_key, +) + +__all__ = [ + "BestAvailableEncryption", + "Encoding", + "KeySerializationEncryption", + "NoEncryption", + "ParameterFormat", + "PrivateFormat", + "PublicFormat", + "SSHCertPrivateKeyTypes", + "SSHCertPublicKeyTypes", + "SSHCertificate", + "SSHCertificateBuilder", + "SSHCertificateType", + "SSHPrivateKeyTypes", + "SSHPublicKeyTypes", + "_KeySerializationEncryption", + "load_der_parameters", + "load_der_private_key", + "load_der_public_key", + "load_pem_parameters", + "load_pem_private_key", + "load_pem_public_key", + "load_ssh_private_key", + "load_ssh_public_identity", + "load_ssh_public_key", +] diff --git a/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/serialization/__pycache__/__init__.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/serialization/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..2224780 Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/serialization/__pycache__/__init__.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/serialization/__pycache__/base.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/serialization/__pycache__/base.cpython-312.pyc new file mode 100644 index 0000000..908998b Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/serialization/__pycache__/base.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/serialization/__pycache__/pkcs12.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/serialization/__pycache__/pkcs12.cpython-312.pyc new file mode 100644 index 0000000..f41c8eb Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/serialization/__pycache__/pkcs12.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/serialization/__pycache__/pkcs7.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/serialization/__pycache__/pkcs7.cpython-312.pyc new file mode 100644 index 0000000..66fc45b Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/serialization/__pycache__/pkcs7.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/serialization/__pycache__/ssh.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/serialization/__pycache__/ssh.cpython-312.pyc new file mode 100644 index 0000000..027141b Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/serialization/__pycache__/ssh.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/serialization/base.py b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/serialization/base.py new file mode 100644 index 0000000..e7c998b --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/serialization/base.py @@ -0,0 +1,14 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +from cryptography.hazmat.bindings._rust import openssl as rust_openssl + +load_pem_private_key = rust_openssl.keys.load_pem_private_key +load_der_private_key = rust_openssl.keys.load_der_private_key + +load_pem_public_key = rust_openssl.keys.load_pem_public_key +load_der_public_key = rust_openssl.keys.load_der_public_key + +load_pem_parameters = rust_openssl.dh.from_pem_parameters +load_der_parameters = rust_openssl.dh.from_der_parameters diff --git a/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/serialization/pkcs12.py b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/serialization/pkcs12.py new file mode 100644 index 0000000..549e1f9 --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/serialization/pkcs12.py @@ -0,0 +1,156 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +from __future__ import annotations + +import typing + +from cryptography import x509 +from cryptography.hazmat.bindings._rust import pkcs12 as rust_pkcs12 +from cryptography.hazmat.primitives import serialization +from cryptography.hazmat.primitives._serialization import PBES as PBES +from cryptography.hazmat.primitives.asymmetric import ( + dsa, + ec, + ed448, + ed25519, + rsa, +) +from cryptography.hazmat.primitives.asymmetric.types import PrivateKeyTypes + +__all__ = [ + "PBES", + "PKCS12Certificate", + "PKCS12KeyAndCertificates", + "PKCS12PrivateKeyTypes", + "load_key_and_certificates", + "load_pkcs12", + "serialize_key_and_certificates", +] + +PKCS12PrivateKeyTypes = typing.Union[ + rsa.RSAPrivateKey, + dsa.DSAPrivateKey, + ec.EllipticCurvePrivateKey, + ed25519.Ed25519PrivateKey, + ed448.Ed448PrivateKey, +] + + +PKCS12Certificate = rust_pkcs12.PKCS12Certificate + + +class PKCS12KeyAndCertificates: + def __init__( + self, + key: PrivateKeyTypes | None, + cert: PKCS12Certificate | None, + additional_certs: list[PKCS12Certificate], + ): + if key is not None and not isinstance( + key, + ( + rsa.RSAPrivateKey, + dsa.DSAPrivateKey, + ec.EllipticCurvePrivateKey, + ed25519.Ed25519PrivateKey, + ed448.Ed448PrivateKey, + ), + ): + raise TypeError( + "Key must be RSA, DSA, EllipticCurve, ED25519, or ED448" + " private key, or None." + ) + if cert is not None and not isinstance(cert, PKCS12Certificate): + raise TypeError("cert must be a PKCS12Certificate object or None") + if not all( + isinstance(add_cert, PKCS12Certificate) + for add_cert in additional_certs + ): + raise TypeError( + "all values in additional_certs must be PKCS12Certificate" + " objects" + ) + self._key = key + self._cert = cert + self._additional_certs = additional_certs + + @property + def key(self) -> PrivateKeyTypes | None: + return self._key + + @property + def cert(self) -> PKCS12Certificate | None: + return self._cert + + @property + def additional_certs(self) -> list[PKCS12Certificate]: + return self._additional_certs + + def __eq__(self, other: object) -> bool: + if not isinstance(other, PKCS12KeyAndCertificates): + return NotImplemented + + return ( + self.key == other.key + and self.cert == other.cert + and self.additional_certs == other.additional_certs + ) + + def __hash__(self) -> int: + return hash((self.key, self.cert, tuple(self.additional_certs))) + + def __repr__(self) -> str: + fmt = ( + "" + ) + return fmt.format(self.key, self.cert, self.additional_certs) + + +load_key_and_certificates = rust_pkcs12.load_key_and_certificates +load_pkcs12 = rust_pkcs12.load_pkcs12 + + +_PKCS12CATypes = typing.Union[ + x509.Certificate, + PKCS12Certificate, +] + + +def serialize_key_and_certificates( + name: bytes | None, + key: PKCS12PrivateKeyTypes | None, + cert: x509.Certificate | None, + cas: typing.Iterable[_PKCS12CATypes] | None, + encryption_algorithm: serialization.KeySerializationEncryption, +) -> bytes: + if key is not None and not isinstance( + key, + ( + rsa.RSAPrivateKey, + dsa.DSAPrivateKey, + ec.EllipticCurvePrivateKey, + ed25519.Ed25519PrivateKey, + ed448.Ed448PrivateKey, + ), + ): + raise TypeError( + "Key must be RSA, DSA, EllipticCurve, ED25519, or ED448" + " private key, or None." + ) + + if not isinstance( + encryption_algorithm, serialization.KeySerializationEncryption + ): + raise TypeError( + "Key encryption algorithm must be a " + "KeySerializationEncryption instance" + ) + + if key is None and cert is None and not cas: + raise ValueError("You must supply at least one of key, cert, or cas") + + return rust_pkcs12.serialize_key_and_certificates( + name, key, cert, cas, encryption_algorithm + ) diff --git a/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/serialization/pkcs7.py b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/serialization/pkcs7.py new file mode 100644 index 0000000..97ea9db --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/serialization/pkcs7.py @@ -0,0 +1,336 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +from __future__ import annotations + +import email.base64mime +import email.generator +import email.message +import email.policy +import io +import typing + +from cryptography import utils, x509 +from cryptography.exceptions import UnsupportedAlgorithm, _Reasons +from cryptography.hazmat.bindings._rust import pkcs7 as rust_pkcs7 +from cryptography.hazmat.primitives import hashes, serialization +from cryptography.hazmat.primitives.asymmetric import ec, padding, rsa +from cryptography.utils import _check_byteslike + +load_pem_pkcs7_certificates = rust_pkcs7.load_pem_pkcs7_certificates + +load_der_pkcs7_certificates = rust_pkcs7.load_der_pkcs7_certificates + +serialize_certificates = rust_pkcs7.serialize_certificates + +PKCS7HashTypes = typing.Union[ + hashes.SHA224, + hashes.SHA256, + hashes.SHA384, + hashes.SHA512, +] + +PKCS7PrivateKeyTypes = typing.Union[ + rsa.RSAPrivateKey, ec.EllipticCurvePrivateKey +] + + +class PKCS7Options(utils.Enum): + Text = "Add text/plain MIME type" + Binary = "Don't translate input data into canonical MIME format" + DetachedSignature = "Don't embed data in the PKCS7 structure" + NoCapabilities = "Don't embed SMIME capabilities" + NoAttributes = "Don't embed authenticatedAttributes" + NoCerts = "Don't embed signer certificate" + + +class PKCS7SignatureBuilder: + def __init__( + self, + data: bytes | None = None, + signers: list[ + tuple[ + x509.Certificate, + PKCS7PrivateKeyTypes, + PKCS7HashTypes, + padding.PSS | padding.PKCS1v15 | None, + ] + ] = [], + additional_certs: list[x509.Certificate] = [], + ): + self._data = data + self._signers = signers + self._additional_certs = additional_certs + + def set_data(self, data: bytes) -> PKCS7SignatureBuilder: + _check_byteslike("data", data) + if self._data is not None: + raise ValueError("data may only be set once") + + return PKCS7SignatureBuilder(data, self._signers) + + def add_signer( + self, + certificate: x509.Certificate, + private_key: PKCS7PrivateKeyTypes, + hash_algorithm: PKCS7HashTypes, + *, + rsa_padding: padding.PSS | padding.PKCS1v15 | None = None, + ) -> PKCS7SignatureBuilder: + if not isinstance( + hash_algorithm, + ( + hashes.SHA224, + hashes.SHA256, + hashes.SHA384, + hashes.SHA512, + ), + ): + raise TypeError( + "hash_algorithm must be one of hashes.SHA224, " + "SHA256, SHA384, or SHA512" + ) + if not isinstance(certificate, x509.Certificate): + raise TypeError("certificate must be a x509.Certificate") + + if not isinstance( + private_key, (rsa.RSAPrivateKey, ec.EllipticCurvePrivateKey) + ): + raise TypeError("Only RSA & EC keys are supported at this time.") + + if rsa_padding is not None: + if not isinstance(rsa_padding, (padding.PSS, padding.PKCS1v15)): + raise TypeError("Padding must be PSS or PKCS1v15") + if not isinstance(private_key, rsa.RSAPrivateKey): + raise TypeError("Padding is only supported for RSA keys") + + return PKCS7SignatureBuilder( + self._data, + [ + *self._signers, + (certificate, private_key, hash_algorithm, rsa_padding), + ], + ) + + def add_certificate( + self, certificate: x509.Certificate + ) -> PKCS7SignatureBuilder: + if not isinstance(certificate, x509.Certificate): + raise TypeError("certificate must be a x509.Certificate") + + return PKCS7SignatureBuilder( + self._data, self._signers, [*self._additional_certs, certificate] + ) + + def sign( + self, + encoding: serialization.Encoding, + options: typing.Iterable[PKCS7Options], + backend: typing.Any = None, + ) -> bytes: + if len(self._signers) == 0: + raise ValueError("Must have at least one signer") + if self._data is None: + raise ValueError("You must add data to sign") + options = list(options) + if not all(isinstance(x, PKCS7Options) for x in options): + raise ValueError("options must be from the PKCS7Options enum") + if encoding not in ( + serialization.Encoding.PEM, + serialization.Encoding.DER, + serialization.Encoding.SMIME, + ): + raise ValueError( + "Must be PEM, DER, or SMIME from the Encoding enum" + ) + + # Text is a meaningless option unless it is accompanied by + # DetachedSignature + if ( + PKCS7Options.Text in options + and PKCS7Options.DetachedSignature not in options + ): + raise ValueError( + "When passing the Text option you must also pass " + "DetachedSignature" + ) + + if PKCS7Options.Text in options and encoding in ( + serialization.Encoding.DER, + serialization.Encoding.PEM, + ): + raise ValueError( + "The Text option is only available for SMIME serialization" + ) + + # No attributes implies no capabilities so we'll error if you try to + # pass both. + if ( + PKCS7Options.NoAttributes in options + and PKCS7Options.NoCapabilities in options + ): + raise ValueError( + "NoAttributes is a superset of NoCapabilities. Do not pass " + "both values." + ) + + return rust_pkcs7.sign_and_serialize(self, encoding, options) + + +class PKCS7EnvelopeBuilder: + def __init__( + self, + *, + _data: bytes | None = None, + _recipients: list[x509.Certificate] | None = None, + ): + from cryptography.hazmat.backends.openssl.backend import ( + backend as ossl, + ) + + if not ossl.rsa_encryption_supported(padding=padding.PKCS1v15()): + raise UnsupportedAlgorithm( + "RSA with PKCS1 v1.5 padding is not supported by this version" + " of OpenSSL.", + _Reasons.UNSUPPORTED_PADDING, + ) + self._data = _data + self._recipients = _recipients if _recipients is not None else [] + + def set_data(self, data: bytes) -> PKCS7EnvelopeBuilder: + _check_byteslike("data", data) + if self._data is not None: + raise ValueError("data may only be set once") + + return PKCS7EnvelopeBuilder(_data=data, _recipients=self._recipients) + + def add_recipient( + self, + certificate: x509.Certificate, + ) -> PKCS7EnvelopeBuilder: + if not isinstance(certificate, x509.Certificate): + raise TypeError("certificate must be a x509.Certificate") + + if not isinstance(certificate.public_key(), rsa.RSAPublicKey): + raise TypeError("Only RSA keys are supported at this time.") + + return PKCS7EnvelopeBuilder( + _data=self._data, + _recipients=[ + *self._recipients, + certificate, + ], + ) + + def encrypt( + self, + encoding: serialization.Encoding, + options: typing.Iterable[PKCS7Options], + ) -> bytes: + if len(self._recipients) == 0: + raise ValueError("Must have at least one recipient") + if self._data is None: + raise ValueError("You must add data to encrypt") + options = list(options) + if not all(isinstance(x, PKCS7Options) for x in options): + raise ValueError("options must be from the PKCS7Options enum") + if encoding not in ( + serialization.Encoding.PEM, + serialization.Encoding.DER, + serialization.Encoding.SMIME, + ): + raise ValueError( + "Must be PEM, DER, or SMIME from the Encoding enum" + ) + + # Only allow options that make sense for encryption + if any( + opt not in [PKCS7Options.Text, PKCS7Options.Binary] + for opt in options + ): + raise ValueError( + "Only the following options are supported for encryption: " + "Text, Binary" + ) + elif PKCS7Options.Text in options and PKCS7Options.Binary in options: + # OpenSSL accepts both options at the same time, but ignores Text. + # We fail defensively to avoid unexpected outputs. + raise ValueError( + "Cannot use Binary and Text options at the same time" + ) + + return rust_pkcs7.encrypt_and_serialize(self, encoding, options) + + +def _smime_signed_encode( + data: bytes, signature: bytes, micalg: str, text_mode: bool +) -> bytes: + # This function works pretty hard to replicate what OpenSSL does + # precisely. For good and for ill. + + m = email.message.Message() + m.add_header("MIME-Version", "1.0") + m.add_header( + "Content-Type", + "multipart/signed", + protocol="application/x-pkcs7-signature", + micalg=micalg, + ) + + m.preamble = "This is an S/MIME signed message\n" + + msg_part = OpenSSLMimePart() + msg_part.set_payload(data) + if text_mode: + msg_part.add_header("Content-Type", "text/plain") + m.attach(msg_part) + + sig_part = email.message.MIMEPart() + sig_part.add_header( + "Content-Type", "application/x-pkcs7-signature", name="smime.p7s" + ) + sig_part.add_header("Content-Transfer-Encoding", "base64") + sig_part.add_header( + "Content-Disposition", "attachment", filename="smime.p7s" + ) + sig_part.set_payload( + email.base64mime.body_encode(signature, maxlinelen=65) + ) + del sig_part["MIME-Version"] + m.attach(sig_part) + + fp = io.BytesIO() + g = email.generator.BytesGenerator( + fp, + maxheaderlen=0, + mangle_from_=False, + policy=m.policy.clone(linesep="\r\n"), + ) + g.flatten(m) + return fp.getvalue() + + +def _smime_enveloped_encode(data: bytes) -> bytes: + m = email.message.Message() + m.add_header("MIME-Version", "1.0") + m.add_header("Content-Disposition", "attachment", filename="smime.p7m") + m.add_header( + "Content-Type", + "application/pkcs7-mime", + smime_type="enveloped-data", + name="smime.p7m", + ) + m.add_header("Content-Transfer-Encoding", "base64") + + m.set_payload(email.base64mime.body_encode(data, maxlinelen=65)) + + return m.as_bytes(policy=m.policy.clone(linesep="\n", max_line_length=0)) + + +class OpenSSLMimePart(email.message.MIMEPart): + # A MIMEPart subclass that replicates OpenSSL's behavior of not including + # a newline if there are no headers. + def _write_headers(self, generator) -> None: + if list(self.raw_items()): + generator._write_headers(self) diff --git a/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/serialization/ssh.py b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/serialization/ssh.py new file mode 100644 index 0000000..c01afb0 --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/serialization/ssh.py @@ -0,0 +1,1569 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +from __future__ import annotations + +import binascii +import enum +import os +import re +import typing +import warnings +from base64 import encodebytes as _base64_encode +from dataclasses import dataclass + +from cryptography import utils +from cryptography.exceptions import UnsupportedAlgorithm +from cryptography.hazmat.primitives import hashes +from cryptography.hazmat.primitives.asymmetric import ( + dsa, + ec, + ed25519, + padding, + rsa, +) +from cryptography.hazmat.primitives.asymmetric import utils as asym_utils +from cryptography.hazmat.primitives.ciphers import ( + AEADDecryptionContext, + Cipher, + algorithms, + modes, +) +from cryptography.hazmat.primitives.serialization import ( + Encoding, + KeySerializationEncryption, + NoEncryption, + PrivateFormat, + PublicFormat, + _KeySerializationEncryption, +) + +try: + from bcrypt import kdf as _bcrypt_kdf + + _bcrypt_supported = True +except ImportError: + _bcrypt_supported = False + + def _bcrypt_kdf( + password: bytes, + salt: bytes, + desired_key_bytes: int, + rounds: int, + ignore_few_rounds: bool = False, + ) -> bytes: + raise UnsupportedAlgorithm("Need bcrypt module") + + +_SSH_ED25519 = b"ssh-ed25519" +_SSH_RSA = b"ssh-rsa" +_SSH_DSA = b"ssh-dss" +_ECDSA_NISTP256 = b"ecdsa-sha2-nistp256" +_ECDSA_NISTP384 = b"ecdsa-sha2-nistp384" +_ECDSA_NISTP521 = b"ecdsa-sha2-nistp521" +_CERT_SUFFIX = b"-cert-v01@openssh.com" + +# U2F application string suffixed pubkey +_SK_SSH_ED25519 = b"sk-ssh-ed25519@openssh.com" +_SK_SSH_ECDSA_NISTP256 = b"sk-ecdsa-sha2-nistp256@openssh.com" + +# These are not key types, only algorithms, so they cannot appear +# as a public key type +_SSH_RSA_SHA256 = b"rsa-sha2-256" +_SSH_RSA_SHA512 = b"rsa-sha2-512" + +_SSH_PUBKEY_RC = re.compile(rb"\A(\S+)[ \t]+(\S+)") +_SK_MAGIC = b"openssh-key-v1\0" +_SK_START = b"-----BEGIN OPENSSH PRIVATE KEY-----" +_SK_END = b"-----END OPENSSH PRIVATE KEY-----" +_BCRYPT = b"bcrypt" +_NONE = b"none" +_DEFAULT_CIPHER = b"aes256-ctr" +_DEFAULT_ROUNDS = 16 + +# re is only way to work on bytes-like data +_PEM_RC = re.compile(_SK_START + b"(.*?)" + _SK_END, re.DOTALL) + +# padding for max blocksize +_PADDING = memoryview(bytearray(range(1, 1 + 16))) + + +@dataclass +class _SSHCipher: + alg: type[algorithms.AES] + key_len: int + mode: type[modes.CTR] | type[modes.CBC] | type[modes.GCM] + block_len: int + iv_len: int + tag_len: int | None + is_aead: bool + + +# ciphers that are actually used in key wrapping +_SSH_CIPHERS: dict[bytes, _SSHCipher] = { + b"aes256-ctr": _SSHCipher( + alg=algorithms.AES, + key_len=32, + mode=modes.CTR, + block_len=16, + iv_len=16, + tag_len=None, + is_aead=False, + ), + b"aes256-cbc": _SSHCipher( + alg=algorithms.AES, + key_len=32, + mode=modes.CBC, + block_len=16, + iv_len=16, + tag_len=None, + is_aead=False, + ), + b"aes256-gcm@openssh.com": _SSHCipher( + alg=algorithms.AES, + key_len=32, + mode=modes.GCM, + block_len=16, + iv_len=12, + tag_len=16, + is_aead=True, + ), +} + +# map local curve name to key type +_ECDSA_KEY_TYPE = { + "secp256r1": _ECDSA_NISTP256, + "secp384r1": _ECDSA_NISTP384, + "secp521r1": _ECDSA_NISTP521, +} + + +def _get_ssh_key_type(key: SSHPrivateKeyTypes | SSHPublicKeyTypes) -> bytes: + if isinstance(key, ec.EllipticCurvePrivateKey): + key_type = _ecdsa_key_type(key.public_key()) + elif isinstance(key, ec.EllipticCurvePublicKey): + key_type = _ecdsa_key_type(key) + elif isinstance(key, (rsa.RSAPrivateKey, rsa.RSAPublicKey)): + key_type = _SSH_RSA + elif isinstance(key, (dsa.DSAPrivateKey, dsa.DSAPublicKey)): + key_type = _SSH_DSA + elif isinstance( + key, (ed25519.Ed25519PrivateKey, ed25519.Ed25519PublicKey) + ): + key_type = _SSH_ED25519 + else: + raise ValueError("Unsupported key type") + + return key_type + + +def _ecdsa_key_type(public_key: ec.EllipticCurvePublicKey) -> bytes: + """Return SSH key_type and curve_name for private key.""" + curve = public_key.curve + if curve.name not in _ECDSA_KEY_TYPE: + raise ValueError( + f"Unsupported curve for ssh private key: {curve.name!r}" + ) + return _ECDSA_KEY_TYPE[curve.name] + + +def _ssh_pem_encode( + data: bytes, + prefix: bytes = _SK_START + b"\n", + suffix: bytes = _SK_END + b"\n", +) -> bytes: + return b"".join([prefix, _base64_encode(data), suffix]) + + +def _check_block_size(data: bytes, block_len: int) -> None: + """Require data to be full blocks""" + if not data or len(data) % block_len != 0: + raise ValueError("Corrupt data: missing padding") + + +def _check_empty(data: bytes) -> None: + """All data should have been parsed.""" + if data: + raise ValueError("Corrupt data: unparsed data") + + +def _init_cipher( + ciphername: bytes, + password: bytes | None, + salt: bytes, + rounds: int, +) -> Cipher[modes.CBC | modes.CTR | modes.GCM]: + """Generate key + iv and return cipher.""" + if not password: + raise ValueError("Key is password-protected.") + + ciph = _SSH_CIPHERS[ciphername] + seed = _bcrypt_kdf( + password, salt, ciph.key_len + ciph.iv_len, rounds, True + ) + return Cipher( + ciph.alg(seed[: ciph.key_len]), + ciph.mode(seed[ciph.key_len :]), + ) + + +def _get_u32(data: memoryview) -> tuple[int, memoryview]: + """Uint32""" + if len(data) < 4: + raise ValueError("Invalid data") + return int.from_bytes(data[:4], byteorder="big"), data[4:] + + +def _get_u64(data: memoryview) -> tuple[int, memoryview]: + """Uint64""" + if len(data) < 8: + raise ValueError("Invalid data") + return int.from_bytes(data[:8], byteorder="big"), data[8:] + + +def _get_sshstr(data: memoryview) -> tuple[memoryview, memoryview]: + """Bytes with u32 length prefix""" + n, data = _get_u32(data) + if n > len(data): + raise ValueError("Invalid data") + return data[:n], data[n:] + + +def _get_mpint(data: memoryview) -> tuple[int, memoryview]: + """Big integer.""" + val, data = _get_sshstr(data) + if val and val[0] > 0x7F: + raise ValueError("Invalid data") + return int.from_bytes(val, "big"), data + + +def _to_mpint(val: int) -> bytes: + """Storage format for signed bigint.""" + if val < 0: + raise ValueError("negative mpint not allowed") + if not val: + return b"" + nbytes = (val.bit_length() + 8) // 8 + return utils.int_to_bytes(val, nbytes) + + +class _FragList: + """Build recursive structure without data copy.""" + + flist: list[bytes] + + def __init__(self, init: list[bytes] | None = None) -> None: + self.flist = [] + if init: + self.flist.extend(init) + + def put_raw(self, val: bytes) -> None: + """Add plain bytes""" + self.flist.append(val) + + def put_u32(self, val: int) -> None: + """Big-endian uint32""" + self.flist.append(val.to_bytes(length=4, byteorder="big")) + + def put_u64(self, val: int) -> None: + """Big-endian uint64""" + self.flist.append(val.to_bytes(length=8, byteorder="big")) + + def put_sshstr(self, val: bytes | _FragList) -> None: + """Bytes prefixed with u32 length""" + if isinstance(val, (bytes, memoryview, bytearray)): + self.put_u32(len(val)) + self.flist.append(val) + else: + self.put_u32(val.size()) + self.flist.extend(val.flist) + + def put_mpint(self, val: int) -> None: + """Big-endian bigint prefixed with u32 length""" + self.put_sshstr(_to_mpint(val)) + + def size(self) -> int: + """Current number of bytes""" + return sum(map(len, self.flist)) + + def render(self, dstbuf: memoryview, pos: int = 0) -> int: + """Write into bytearray""" + for frag in self.flist: + flen = len(frag) + start, pos = pos, pos + flen + dstbuf[start:pos] = frag + return pos + + def tobytes(self) -> bytes: + """Return as bytes""" + buf = memoryview(bytearray(self.size())) + self.render(buf) + return buf.tobytes() + + +class _SSHFormatRSA: + """Format for RSA keys. + + Public: + mpint e, n + Private: + mpint n, e, d, iqmp, p, q + """ + + def get_public( + self, data: memoryview + ) -> tuple[tuple[int, int], memoryview]: + """RSA public fields""" + e, data = _get_mpint(data) + n, data = _get_mpint(data) + return (e, n), data + + def load_public( + self, data: memoryview + ) -> tuple[rsa.RSAPublicKey, memoryview]: + """Make RSA public key from data.""" + (e, n), data = self.get_public(data) + public_numbers = rsa.RSAPublicNumbers(e, n) + public_key = public_numbers.public_key() + return public_key, data + + def load_private( + self, data: memoryview, pubfields + ) -> tuple[rsa.RSAPrivateKey, memoryview]: + """Make RSA private key from data.""" + n, data = _get_mpint(data) + e, data = _get_mpint(data) + d, data = _get_mpint(data) + iqmp, data = _get_mpint(data) + p, data = _get_mpint(data) + q, data = _get_mpint(data) + + if (e, n) != pubfields: + raise ValueError("Corrupt data: rsa field mismatch") + dmp1 = rsa.rsa_crt_dmp1(d, p) + dmq1 = rsa.rsa_crt_dmq1(d, q) + public_numbers = rsa.RSAPublicNumbers(e, n) + private_numbers = rsa.RSAPrivateNumbers( + p, q, d, dmp1, dmq1, iqmp, public_numbers + ) + private_key = private_numbers.private_key() + return private_key, data + + def encode_public( + self, public_key: rsa.RSAPublicKey, f_pub: _FragList + ) -> None: + """Write RSA public key""" + pubn = public_key.public_numbers() + f_pub.put_mpint(pubn.e) + f_pub.put_mpint(pubn.n) + + def encode_private( + self, private_key: rsa.RSAPrivateKey, f_priv: _FragList + ) -> None: + """Write RSA private key""" + private_numbers = private_key.private_numbers() + public_numbers = private_numbers.public_numbers + + f_priv.put_mpint(public_numbers.n) + f_priv.put_mpint(public_numbers.e) + + f_priv.put_mpint(private_numbers.d) + f_priv.put_mpint(private_numbers.iqmp) + f_priv.put_mpint(private_numbers.p) + f_priv.put_mpint(private_numbers.q) + + +class _SSHFormatDSA: + """Format for DSA keys. + + Public: + mpint p, q, g, y + Private: + mpint p, q, g, y, x + """ + + def get_public(self, data: memoryview) -> tuple[tuple, memoryview]: + """DSA public fields""" + p, data = _get_mpint(data) + q, data = _get_mpint(data) + g, data = _get_mpint(data) + y, data = _get_mpint(data) + return (p, q, g, y), data + + def load_public( + self, data: memoryview + ) -> tuple[dsa.DSAPublicKey, memoryview]: + """Make DSA public key from data.""" + (p, q, g, y), data = self.get_public(data) + parameter_numbers = dsa.DSAParameterNumbers(p, q, g) + public_numbers = dsa.DSAPublicNumbers(y, parameter_numbers) + self._validate(public_numbers) + public_key = public_numbers.public_key() + return public_key, data + + def load_private( + self, data: memoryview, pubfields + ) -> tuple[dsa.DSAPrivateKey, memoryview]: + """Make DSA private key from data.""" + (p, q, g, y), data = self.get_public(data) + x, data = _get_mpint(data) + + if (p, q, g, y) != pubfields: + raise ValueError("Corrupt data: dsa field mismatch") + parameter_numbers = dsa.DSAParameterNumbers(p, q, g) + public_numbers = dsa.DSAPublicNumbers(y, parameter_numbers) + self._validate(public_numbers) + private_numbers = dsa.DSAPrivateNumbers(x, public_numbers) + private_key = private_numbers.private_key() + return private_key, data + + def encode_public( + self, public_key: dsa.DSAPublicKey, f_pub: _FragList + ) -> None: + """Write DSA public key""" + public_numbers = public_key.public_numbers() + parameter_numbers = public_numbers.parameter_numbers + self._validate(public_numbers) + + f_pub.put_mpint(parameter_numbers.p) + f_pub.put_mpint(parameter_numbers.q) + f_pub.put_mpint(parameter_numbers.g) + f_pub.put_mpint(public_numbers.y) + + def encode_private( + self, private_key: dsa.DSAPrivateKey, f_priv: _FragList + ) -> None: + """Write DSA private key""" + self.encode_public(private_key.public_key(), f_priv) + f_priv.put_mpint(private_key.private_numbers().x) + + def _validate(self, public_numbers: dsa.DSAPublicNumbers) -> None: + parameter_numbers = public_numbers.parameter_numbers + if parameter_numbers.p.bit_length() != 1024: + raise ValueError("SSH supports only 1024 bit DSA keys") + + +class _SSHFormatECDSA: + """Format for ECDSA keys. + + Public: + str curve + bytes point + Private: + str curve + bytes point + mpint secret + """ + + def __init__(self, ssh_curve_name: bytes, curve: ec.EllipticCurve): + self.ssh_curve_name = ssh_curve_name + self.curve = curve + + def get_public( + self, data: memoryview + ) -> tuple[tuple[memoryview, memoryview], memoryview]: + """ECDSA public fields""" + curve, data = _get_sshstr(data) + point, data = _get_sshstr(data) + if curve != self.ssh_curve_name: + raise ValueError("Curve name mismatch") + if point[0] != 4: + raise NotImplementedError("Need uncompressed point") + return (curve, point), data + + def load_public( + self, data: memoryview + ) -> tuple[ec.EllipticCurvePublicKey, memoryview]: + """Make ECDSA public key from data.""" + (_, point), data = self.get_public(data) + public_key = ec.EllipticCurvePublicKey.from_encoded_point( + self.curve, point.tobytes() + ) + return public_key, data + + def load_private( + self, data: memoryview, pubfields + ) -> tuple[ec.EllipticCurvePrivateKey, memoryview]: + """Make ECDSA private key from data.""" + (curve_name, point), data = self.get_public(data) + secret, data = _get_mpint(data) + + if (curve_name, point) != pubfields: + raise ValueError("Corrupt data: ecdsa field mismatch") + private_key = ec.derive_private_key(secret, self.curve) + return private_key, data + + def encode_public( + self, public_key: ec.EllipticCurvePublicKey, f_pub: _FragList + ) -> None: + """Write ECDSA public key""" + point = public_key.public_bytes( + Encoding.X962, PublicFormat.UncompressedPoint + ) + f_pub.put_sshstr(self.ssh_curve_name) + f_pub.put_sshstr(point) + + def encode_private( + self, private_key: ec.EllipticCurvePrivateKey, f_priv: _FragList + ) -> None: + """Write ECDSA private key""" + public_key = private_key.public_key() + private_numbers = private_key.private_numbers() + + self.encode_public(public_key, f_priv) + f_priv.put_mpint(private_numbers.private_value) + + +class _SSHFormatEd25519: + """Format for Ed25519 keys. + + Public: + bytes point + Private: + bytes point + bytes secret_and_point + """ + + def get_public( + self, data: memoryview + ) -> tuple[tuple[memoryview], memoryview]: + """Ed25519 public fields""" + point, data = _get_sshstr(data) + return (point,), data + + def load_public( + self, data: memoryview + ) -> tuple[ed25519.Ed25519PublicKey, memoryview]: + """Make Ed25519 public key from data.""" + (point,), data = self.get_public(data) + public_key = ed25519.Ed25519PublicKey.from_public_bytes( + point.tobytes() + ) + return public_key, data + + def load_private( + self, data: memoryview, pubfields + ) -> tuple[ed25519.Ed25519PrivateKey, memoryview]: + """Make Ed25519 private key from data.""" + (point,), data = self.get_public(data) + keypair, data = _get_sshstr(data) + + secret = keypair[:32] + point2 = keypair[32:] + if point != point2 or (point,) != pubfields: + raise ValueError("Corrupt data: ed25519 field mismatch") + private_key = ed25519.Ed25519PrivateKey.from_private_bytes(secret) + return private_key, data + + def encode_public( + self, public_key: ed25519.Ed25519PublicKey, f_pub: _FragList + ) -> None: + """Write Ed25519 public key""" + raw_public_key = public_key.public_bytes( + Encoding.Raw, PublicFormat.Raw + ) + f_pub.put_sshstr(raw_public_key) + + def encode_private( + self, private_key: ed25519.Ed25519PrivateKey, f_priv: _FragList + ) -> None: + """Write Ed25519 private key""" + public_key = private_key.public_key() + raw_private_key = private_key.private_bytes( + Encoding.Raw, PrivateFormat.Raw, NoEncryption() + ) + raw_public_key = public_key.public_bytes( + Encoding.Raw, PublicFormat.Raw + ) + f_keypair = _FragList([raw_private_key, raw_public_key]) + + self.encode_public(public_key, f_priv) + f_priv.put_sshstr(f_keypair) + + +def load_application(data) -> tuple[memoryview, memoryview]: + """ + U2F application strings + """ + application, data = _get_sshstr(data) + if not application.tobytes().startswith(b"ssh:"): + raise ValueError( + "U2F application string does not start with b'ssh:' " + f"({application})" + ) + return application, data + + +class _SSHFormatSKEd25519: + """ + The format of a sk-ssh-ed25519@openssh.com public key is: + + string "sk-ssh-ed25519@openssh.com" + string public key + string application (user-specified, but typically "ssh:") + """ + + def load_public( + self, data: memoryview + ) -> tuple[ed25519.Ed25519PublicKey, memoryview]: + """Make Ed25519 public key from data.""" + public_key, data = _lookup_kformat(_SSH_ED25519).load_public(data) + _, data = load_application(data) + return public_key, data + + +class _SSHFormatSKECDSA: + """ + The format of a sk-ecdsa-sha2-nistp256@openssh.com public key is: + + string "sk-ecdsa-sha2-nistp256@openssh.com" + string curve name + ec_point Q + string application (user-specified, but typically "ssh:") + """ + + def load_public( + self, data: memoryview + ) -> tuple[ec.EllipticCurvePublicKey, memoryview]: + """Make ECDSA public key from data.""" + public_key, data = _lookup_kformat(_ECDSA_NISTP256).load_public(data) + _, data = load_application(data) + return public_key, data + + +_KEY_FORMATS = { + _SSH_RSA: _SSHFormatRSA(), + _SSH_DSA: _SSHFormatDSA(), + _SSH_ED25519: _SSHFormatEd25519(), + _ECDSA_NISTP256: _SSHFormatECDSA(b"nistp256", ec.SECP256R1()), + _ECDSA_NISTP384: _SSHFormatECDSA(b"nistp384", ec.SECP384R1()), + _ECDSA_NISTP521: _SSHFormatECDSA(b"nistp521", ec.SECP521R1()), + _SK_SSH_ED25519: _SSHFormatSKEd25519(), + _SK_SSH_ECDSA_NISTP256: _SSHFormatSKECDSA(), +} + + +def _lookup_kformat(key_type: bytes): + """Return valid format or throw error""" + if not isinstance(key_type, bytes): + key_type = memoryview(key_type).tobytes() + if key_type in _KEY_FORMATS: + return _KEY_FORMATS[key_type] + raise UnsupportedAlgorithm(f"Unsupported key type: {key_type!r}") + + +SSHPrivateKeyTypes = typing.Union[ + ec.EllipticCurvePrivateKey, + rsa.RSAPrivateKey, + dsa.DSAPrivateKey, + ed25519.Ed25519PrivateKey, +] + + +def load_ssh_private_key( + data: bytes, + password: bytes | None, + backend: typing.Any = None, +) -> SSHPrivateKeyTypes: + """Load private key from OpenSSH custom encoding.""" + utils._check_byteslike("data", data) + if password is not None: + utils._check_bytes("password", password) + + m = _PEM_RC.search(data) + if not m: + raise ValueError("Not OpenSSH private key format") + p1 = m.start(1) + p2 = m.end(1) + data = binascii.a2b_base64(memoryview(data)[p1:p2]) + if not data.startswith(_SK_MAGIC): + raise ValueError("Not OpenSSH private key format") + data = memoryview(data)[len(_SK_MAGIC) :] + + # parse header + ciphername, data = _get_sshstr(data) + kdfname, data = _get_sshstr(data) + kdfoptions, data = _get_sshstr(data) + nkeys, data = _get_u32(data) + if nkeys != 1: + raise ValueError("Only one key supported") + + # load public key data + pubdata, data = _get_sshstr(data) + pub_key_type, pubdata = _get_sshstr(pubdata) + kformat = _lookup_kformat(pub_key_type) + pubfields, pubdata = kformat.get_public(pubdata) + _check_empty(pubdata) + + if (ciphername, kdfname) != (_NONE, _NONE): + ciphername_bytes = ciphername.tobytes() + if ciphername_bytes not in _SSH_CIPHERS: + raise UnsupportedAlgorithm( + f"Unsupported cipher: {ciphername_bytes!r}" + ) + if kdfname != _BCRYPT: + raise UnsupportedAlgorithm(f"Unsupported KDF: {kdfname!r}") + blklen = _SSH_CIPHERS[ciphername_bytes].block_len + tag_len = _SSH_CIPHERS[ciphername_bytes].tag_len + # load secret data + edata, data = _get_sshstr(data) + # see https://bugzilla.mindrot.org/show_bug.cgi?id=3553 for + # information about how OpenSSH handles AEAD tags + if _SSH_CIPHERS[ciphername_bytes].is_aead: + tag = bytes(data) + if len(tag) != tag_len: + raise ValueError("Corrupt data: invalid tag length for cipher") + else: + _check_empty(data) + _check_block_size(edata, blklen) + salt, kbuf = _get_sshstr(kdfoptions) + rounds, kbuf = _get_u32(kbuf) + _check_empty(kbuf) + ciph = _init_cipher(ciphername_bytes, password, salt.tobytes(), rounds) + dec = ciph.decryptor() + edata = memoryview(dec.update(edata)) + if _SSH_CIPHERS[ciphername_bytes].is_aead: + assert isinstance(dec, AEADDecryptionContext) + _check_empty(dec.finalize_with_tag(tag)) + else: + # _check_block_size requires data to be a full block so there + # should be no output from finalize + _check_empty(dec.finalize()) + else: + # load secret data + edata, data = _get_sshstr(data) + _check_empty(data) + blklen = 8 + _check_block_size(edata, blklen) + ck1, edata = _get_u32(edata) + ck2, edata = _get_u32(edata) + if ck1 != ck2: + raise ValueError("Corrupt data: broken checksum") + + # load per-key struct + key_type, edata = _get_sshstr(edata) + if key_type != pub_key_type: + raise ValueError("Corrupt data: key type mismatch") + private_key, edata = kformat.load_private(edata, pubfields) + # We don't use the comment + _, edata = _get_sshstr(edata) + + # yes, SSH does padding check *after* all other parsing is done. + # need to follow as it writes zero-byte padding too. + if edata != _PADDING[: len(edata)]: + raise ValueError("Corrupt data: invalid padding") + + if isinstance(private_key, dsa.DSAPrivateKey): + warnings.warn( + "SSH DSA keys are deprecated and will be removed in a future " + "release.", + utils.DeprecatedIn40, + stacklevel=2, + ) + + return private_key + + +def _serialize_ssh_private_key( + private_key: SSHPrivateKeyTypes, + password: bytes, + encryption_algorithm: KeySerializationEncryption, +) -> bytes: + """Serialize private key with OpenSSH custom encoding.""" + utils._check_bytes("password", password) + if isinstance(private_key, dsa.DSAPrivateKey): + warnings.warn( + "SSH DSA key support is deprecated and will be " + "removed in a future release", + utils.DeprecatedIn40, + stacklevel=4, + ) + + key_type = _get_ssh_key_type(private_key) + kformat = _lookup_kformat(key_type) + + # setup parameters + f_kdfoptions = _FragList() + if password: + ciphername = _DEFAULT_CIPHER + blklen = _SSH_CIPHERS[ciphername].block_len + kdfname = _BCRYPT + rounds = _DEFAULT_ROUNDS + if ( + isinstance(encryption_algorithm, _KeySerializationEncryption) + and encryption_algorithm._kdf_rounds is not None + ): + rounds = encryption_algorithm._kdf_rounds + salt = os.urandom(16) + f_kdfoptions.put_sshstr(salt) + f_kdfoptions.put_u32(rounds) + ciph = _init_cipher(ciphername, password, salt, rounds) + else: + ciphername = kdfname = _NONE + blklen = 8 + ciph = None + nkeys = 1 + checkval = os.urandom(4) + comment = b"" + + # encode public and private parts together + f_public_key = _FragList() + f_public_key.put_sshstr(key_type) + kformat.encode_public(private_key.public_key(), f_public_key) + + f_secrets = _FragList([checkval, checkval]) + f_secrets.put_sshstr(key_type) + kformat.encode_private(private_key, f_secrets) + f_secrets.put_sshstr(comment) + f_secrets.put_raw(_PADDING[: blklen - (f_secrets.size() % blklen)]) + + # top-level structure + f_main = _FragList() + f_main.put_raw(_SK_MAGIC) + f_main.put_sshstr(ciphername) + f_main.put_sshstr(kdfname) + f_main.put_sshstr(f_kdfoptions) + f_main.put_u32(nkeys) + f_main.put_sshstr(f_public_key) + f_main.put_sshstr(f_secrets) + + # copy result info bytearray + slen = f_secrets.size() + mlen = f_main.size() + buf = memoryview(bytearray(mlen + blklen)) + f_main.render(buf) + ofs = mlen - slen + + # encrypt in-place + if ciph is not None: + ciph.encryptor().update_into(buf[ofs:mlen], buf[ofs:]) + + return _ssh_pem_encode(buf[:mlen]) + + +SSHPublicKeyTypes = typing.Union[ + ec.EllipticCurvePublicKey, + rsa.RSAPublicKey, + dsa.DSAPublicKey, + ed25519.Ed25519PublicKey, +] + +SSHCertPublicKeyTypes = typing.Union[ + ec.EllipticCurvePublicKey, + rsa.RSAPublicKey, + ed25519.Ed25519PublicKey, +] + + +class SSHCertificateType(enum.Enum): + USER = 1 + HOST = 2 + + +class SSHCertificate: + def __init__( + self, + _nonce: memoryview, + _public_key: SSHPublicKeyTypes, + _serial: int, + _cctype: int, + _key_id: memoryview, + _valid_principals: list[bytes], + _valid_after: int, + _valid_before: int, + _critical_options: dict[bytes, bytes], + _extensions: dict[bytes, bytes], + _sig_type: memoryview, + _sig_key: memoryview, + _inner_sig_type: memoryview, + _signature: memoryview, + _tbs_cert_body: memoryview, + _cert_key_type: bytes, + _cert_body: memoryview, + ): + self._nonce = _nonce + self._public_key = _public_key + self._serial = _serial + try: + self._type = SSHCertificateType(_cctype) + except ValueError: + raise ValueError("Invalid certificate type") + self._key_id = _key_id + self._valid_principals = _valid_principals + self._valid_after = _valid_after + self._valid_before = _valid_before + self._critical_options = _critical_options + self._extensions = _extensions + self._sig_type = _sig_type + self._sig_key = _sig_key + self._inner_sig_type = _inner_sig_type + self._signature = _signature + self._cert_key_type = _cert_key_type + self._cert_body = _cert_body + self._tbs_cert_body = _tbs_cert_body + + @property + def nonce(self) -> bytes: + return bytes(self._nonce) + + def public_key(self) -> SSHCertPublicKeyTypes: + # make mypy happy until we remove DSA support entirely and + # the underlying union won't have a disallowed type + return typing.cast(SSHCertPublicKeyTypes, self._public_key) + + @property + def serial(self) -> int: + return self._serial + + @property + def type(self) -> SSHCertificateType: + return self._type + + @property + def key_id(self) -> bytes: + return bytes(self._key_id) + + @property + def valid_principals(self) -> list[bytes]: + return self._valid_principals + + @property + def valid_before(self) -> int: + return self._valid_before + + @property + def valid_after(self) -> int: + return self._valid_after + + @property + def critical_options(self) -> dict[bytes, bytes]: + return self._critical_options + + @property + def extensions(self) -> dict[bytes, bytes]: + return self._extensions + + def signature_key(self) -> SSHCertPublicKeyTypes: + sigformat = _lookup_kformat(self._sig_type) + signature_key, sigkey_rest = sigformat.load_public(self._sig_key) + _check_empty(sigkey_rest) + return signature_key + + def public_bytes(self) -> bytes: + return ( + bytes(self._cert_key_type) + + b" " + + binascii.b2a_base64(bytes(self._cert_body), newline=False) + ) + + def verify_cert_signature(self) -> None: + signature_key = self.signature_key() + if isinstance(signature_key, ed25519.Ed25519PublicKey): + signature_key.verify( + bytes(self._signature), bytes(self._tbs_cert_body) + ) + elif isinstance(signature_key, ec.EllipticCurvePublicKey): + # The signature is encoded as a pair of big-endian integers + r, data = _get_mpint(self._signature) + s, data = _get_mpint(data) + _check_empty(data) + computed_sig = asym_utils.encode_dss_signature(r, s) + hash_alg = _get_ec_hash_alg(signature_key.curve) + signature_key.verify( + computed_sig, bytes(self._tbs_cert_body), ec.ECDSA(hash_alg) + ) + else: + assert isinstance(signature_key, rsa.RSAPublicKey) + if self._inner_sig_type == _SSH_RSA: + hash_alg = hashes.SHA1() + elif self._inner_sig_type == _SSH_RSA_SHA256: + hash_alg = hashes.SHA256() + else: + assert self._inner_sig_type == _SSH_RSA_SHA512 + hash_alg = hashes.SHA512() + signature_key.verify( + bytes(self._signature), + bytes(self._tbs_cert_body), + padding.PKCS1v15(), + hash_alg, + ) + + +def _get_ec_hash_alg(curve: ec.EllipticCurve) -> hashes.HashAlgorithm: + if isinstance(curve, ec.SECP256R1): + return hashes.SHA256() + elif isinstance(curve, ec.SECP384R1): + return hashes.SHA384() + else: + assert isinstance(curve, ec.SECP521R1) + return hashes.SHA512() + + +def _load_ssh_public_identity( + data: bytes, + _legacy_dsa_allowed=False, +) -> SSHCertificate | SSHPublicKeyTypes: + utils._check_byteslike("data", data) + + m = _SSH_PUBKEY_RC.match(data) + if not m: + raise ValueError("Invalid line format") + key_type = orig_key_type = m.group(1) + key_body = m.group(2) + with_cert = False + if key_type.endswith(_CERT_SUFFIX): + with_cert = True + key_type = key_type[: -len(_CERT_SUFFIX)] + if key_type == _SSH_DSA and not _legacy_dsa_allowed: + raise UnsupportedAlgorithm( + "DSA keys aren't supported in SSH certificates" + ) + kformat = _lookup_kformat(key_type) + + try: + rest = memoryview(binascii.a2b_base64(key_body)) + except (TypeError, binascii.Error): + raise ValueError("Invalid format") + + if with_cert: + cert_body = rest + inner_key_type, rest = _get_sshstr(rest) + if inner_key_type != orig_key_type: + raise ValueError("Invalid key format") + if with_cert: + nonce, rest = _get_sshstr(rest) + public_key, rest = kformat.load_public(rest) + if with_cert: + serial, rest = _get_u64(rest) + cctype, rest = _get_u32(rest) + key_id, rest = _get_sshstr(rest) + principals, rest = _get_sshstr(rest) + valid_principals = [] + while principals: + principal, principals = _get_sshstr(principals) + valid_principals.append(bytes(principal)) + valid_after, rest = _get_u64(rest) + valid_before, rest = _get_u64(rest) + crit_options, rest = _get_sshstr(rest) + critical_options = _parse_exts_opts(crit_options) + exts, rest = _get_sshstr(rest) + extensions = _parse_exts_opts(exts) + # Get the reserved field, which is unused. + _, rest = _get_sshstr(rest) + sig_key_raw, rest = _get_sshstr(rest) + sig_type, sig_key = _get_sshstr(sig_key_raw) + if sig_type == _SSH_DSA and not _legacy_dsa_allowed: + raise UnsupportedAlgorithm( + "DSA signatures aren't supported in SSH certificates" + ) + # Get the entire cert body and subtract the signature + tbs_cert_body = cert_body[: -len(rest)] + signature_raw, rest = _get_sshstr(rest) + _check_empty(rest) + inner_sig_type, sig_rest = _get_sshstr(signature_raw) + # RSA certs can have multiple algorithm types + if ( + sig_type == _SSH_RSA + and inner_sig_type + not in [_SSH_RSA_SHA256, _SSH_RSA_SHA512, _SSH_RSA] + ) or (sig_type != _SSH_RSA and inner_sig_type != sig_type): + raise ValueError("Signature key type does not match") + signature, sig_rest = _get_sshstr(sig_rest) + _check_empty(sig_rest) + return SSHCertificate( + nonce, + public_key, + serial, + cctype, + key_id, + valid_principals, + valid_after, + valid_before, + critical_options, + extensions, + sig_type, + sig_key, + inner_sig_type, + signature, + tbs_cert_body, + orig_key_type, + cert_body, + ) + else: + _check_empty(rest) + return public_key + + +def load_ssh_public_identity( + data: bytes, +) -> SSHCertificate | SSHPublicKeyTypes: + return _load_ssh_public_identity(data) + + +def _parse_exts_opts(exts_opts: memoryview) -> dict[bytes, bytes]: + result: dict[bytes, bytes] = {} + last_name = None + while exts_opts: + name, exts_opts = _get_sshstr(exts_opts) + bname: bytes = bytes(name) + if bname in result: + raise ValueError("Duplicate name") + if last_name is not None and bname < last_name: + raise ValueError("Fields not lexically sorted") + value, exts_opts = _get_sshstr(exts_opts) + if len(value) > 0: + value, extra = _get_sshstr(value) + if len(extra) > 0: + raise ValueError("Unexpected extra data after value") + result[bname] = bytes(value) + last_name = bname + return result + + +def load_ssh_public_key( + data: bytes, backend: typing.Any = None +) -> SSHPublicKeyTypes: + cert_or_key = _load_ssh_public_identity(data, _legacy_dsa_allowed=True) + public_key: SSHPublicKeyTypes + if isinstance(cert_or_key, SSHCertificate): + public_key = cert_or_key.public_key() + else: + public_key = cert_or_key + + if isinstance(public_key, dsa.DSAPublicKey): + warnings.warn( + "SSH DSA keys are deprecated and will be removed in a future " + "release.", + utils.DeprecatedIn40, + stacklevel=2, + ) + return public_key + + +def serialize_ssh_public_key(public_key: SSHPublicKeyTypes) -> bytes: + """One-line public key format for OpenSSH""" + if isinstance(public_key, dsa.DSAPublicKey): + warnings.warn( + "SSH DSA key support is deprecated and will be " + "removed in a future release", + utils.DeprecatedIn40, + stacklevel=4, + ) + key_type = _get_ssh_key_type(public_key) + kformat = _lookup_kformat(key_type) + + f_pub = _FragList() + f_pub.put_sshstr(key_type) + kformat.encode_public(public_key, f_pub) + + pub = binascii.b2a_base64(f_pub.tobytes()).strip() + return b"".join([key_type, b" ", pub]) + + +SSHCertPrivateKeyTypes = typing.Union[ + ec.EllipticCurvePrivateKey, + rsa.RSAPrivateKey, + ed25519.Ed25519PrivateKey, +] + + +# This is an undocumented limit enforced in the openssh codebase for sshd and +# ssh-keygen, but it is undefined in the ssh certificates spec. +_SSHKEY_CERT_MAX_PRINCIPALS = 256 + + +class SSHCertificateBuilder: + def __init__( + self, + _public_key: SSHCertPublicKeyTypes | None = None, + _serial: int | None = None, + _type: SSHCertificateType | None = None, + _key_id: bytes | None = None, + _valid_principals: list[bytes] = [], + _valid_for_all_principals: bool = False, + _valid_before: int | None = None, + _valid_after: int | None = None, + _critical_options: list[tuple[bytes, bytes]] = [], + _extensions: list[tuple[bytes, bytes]] = [], + ): + self._public_key = _public_key + self._serial = _serial + self._type = _type + self._key_id = _key_id + self._valid_principals = _valid_principals + self._valid_for_all_principals = _valid_for_all_principals + self._valid_before = _valid_before + self._valid_after = _valid_after + self._critical_options = _critical_options + self._extensions = _extensions + + def public_key( + self, public_key: SSHCertPublicKeyTypes + ) -> SSHCertificateBuilder: + if not isinstance( + public_key, + ( + ec.EllipticCurvePublicKey, + rsa.RSAPublicKey, + ed25519.Ed25519PublicKey, + ), + ): + raise TypeError("Unsupported key type") + if self._public_key is not None: + raise ValueError("public_key already set") + + return SSHCertificateBuilder( + _public_key=public_key, + _serial=self._serial, + _type=self._type, + _key_id=self._key_id, + _valid_principals=self._valid_principals, + _valid_for_all_principals=self._valid_for_all_principals, + _valid_before=self._valid_before, + _valid_after=self._valid_after, + _critical_options=self._critical_options, + _extensions=self._extensions, + ) + + def serial(self, serial: int) -> SSHCertificateBuilder: + if not isinstance(serial, int): + raise TypeError("serial must be an integer") + if not 0 <= serial < 2**64: + raise ValueError("serial must be between 0 and 2**64") + if self._serial is not None: + raise ValueError("serial already set") + + return SSHCertificateBuilder( + _public_key=self._public_key, + _serial=serial, + _type=self._type, + _key_id=self._key_id, + _valid_principals=self._valid_principals, + _valid_for_all_principals=self._valid_for_all_principals, + _valid_before=self._valid_before, + _valid_after=self._valid_after, + _critical_options=self._critical_options, + _extensions=self._extensions, + ) + + def type(self, type: SSHCertificateType) -> SSHCertificateBuilder: + if not isinstance(type, SSHCertificateType): + raise TypeError("type must be an SSHCertificateType") + if self._type is not None: + raise ValueError("type already set") + + return SSHCertificateBuilder( + _public_key=self._public_key, + _serial=self._serial, + _type=type, + _key_id=self._key_id, + _valid_principals=self._valid_principals, + _valid_for_all_principals=self._valid_for_all_principals, + _valid_before=self._valid_before, + _valid_after=self._valid_after, + _critical_options=self._critical_options, + _extensions=self._extensions, + ) + + def key_id(self, key_id: bytes) -> SSHCertificateBuilder: + if not isinstance(key_id, bytes): + raise TypeError("key_id must be bytes") + if self._key_id is not None: + raise ValueError("key_id already set") + + return SSHCertificateBuilder( + _public_key=self._public_key, + _serial=self._serial, + _type=self._type, + _key_id=key_id, + _valid_principals=self._valid_principals, + _valid_for_all_principals=self._valid_for_all_principals, + _valid_before=self._valid_before, + _valid_after=self._valid_after, + _critical_options=self._critical_options, + _extensions=self._extensions, + ) + + def valid_principals( + self, valid_principals: list[bytes] + ) -> SSHCertificateBuilder: + if self._valid_for_all_principals: + raise ValueError( + "Principals can't be set because the cert is valid " + "for all principals" + ) + if ( + not all(isinstance(x, bytes) for x in valid_principals) + or not valid_principals + ): + raise TypeError( + "principals must be a list of bytes and can't be empty" + ) + if self._valid_principals: + raise ValueError("valid_principals already set") + + if len(valid_principals) > _SSHKEY_CERT_MAX_PRINCIPALS: + raise ValueError( + "Reached or exceeded the maximum number of valid_principals" + ) + + return SSHCertificateBuilder( + _public_key=self._public_key, + _serial=self._serial, + _type=self._type, + _key_id=self._key_id, + _valid_principals=valid_principals, + _valid_for_all_principals=self._valid_for_all_principals, + _valid_before=self._valid_before, + _valid_after=self._valid_after, + _critical_options=self._critical_options, + _extensions=self._extensions, + ) + + def valid_for_all_principals(self): + if self._valid_principals: + raise ValueError( + "valid_principals already set, can't set " + "valid_for_all_principals" + ) + if self._valid_for_all_principals: + raise ValueError("valid_for_all_principals already set") + + return SSHCertificateBuilder( + _public_key=self._public_key, + _serial=self._serial, + _type=self._type, + _key_id=self._key_id, + _valid_principals=self._valid_principals, + _valid_for_all_principals=True, + _valid_before=self._valid_before, + _valid_after=self._valid_after, + _critical_options=self._critical_options, + _extensions=self._extensions, + ) + + def valid_before(self, valid_before: int | float) -> SSHCertificateBuilder: + if not isinstance(valid_before, (int, float)): + raise TypeError("valid_before must be an int or float") + valid_before = int(valid_before) + if valid_before < 0 or valid_before >= 2**64: + raise ValueError("valid_before must [0, 2**64)") + if self._valid_before is not None: + raise ValueError("valid_before already set") + + return SSHCertificateBuilder( + _public_key=self._public_key, + _serial=self._serial, + _type=self._type, + _key_id=self._key_id, + _valid_principals=self._valid_principals, + _valid_for_all_principals=self._valid_for_all_principals, + _valid_before=valid_before, + _valid_after=self._valid_after, + _critical_options=self._critical_options, + _extensions=self._extensions, + ) + + def valid_after(self, valid_after: int | float) -> SSHCertificateBuilder: + if not isinstance(valid_after, (int, float)): + raise TypeError("valid_after must be an int or float") + valid_after = int(valid_after) + if valid_after < 0 or valid_after >= 2**64: + raise ValueError("valid_after must [0, 2**64)") + if self._valid_after is not None: + raise ValueError("valid_after already set") + + return SSHCertificateBuilder( + _public_key=self._public_key, + _serial=self._serial, + _type=self._type, + _key_id=self._key_id, + _valid_principals=self._valid_principals, + _valid_for_all_principals=self._valid_for_all_principals, + _valid_before=self._valid_before, + _valid_after=valid_after, + _critical_options=self._critical_options, + _extensions=self._extensions, + ) + + def add_critical_option( + self, name: bytes, value: bytes + ) -> SSHCertificateBuilder: + if not isinstance(name, bytes) or not isinstance(value, bytes): + raise TypeError("name and value must be bytes") + # This is O(n**2) + if name in [name for name, _ in self._critical_options]: + raise ValueError("Duplicate critical option name") + + return SSHCertificateBuilder( + _public_key=self._public_key, + _serial=self._serial, + _type=self._type, + _key_id=self._key_id, + _valid_principals=self._valid_principals, + _valid_for_all_principals=self._valid_for_all_principals, + _valid_before=self._valid_before, + _valid_after=self._valid_after, + _critical_options=[*self._critical_options, (name, value)], + _extensions=self._extensions, + ) + + def add_extension( + self, name: bytes, value: bytes + ) -> SSHCertificateBuilder: + if not isinstance(name, bytes) or not isinstance(value, bytes): + raise TypeError("name and value must be bytes") + # This is O(n**2) + if name in [name for name, _ in self._extensions]: + raise ValueError("Duplicate extension name") + + return SSHCertificateBuilder( + _public_key=self._public_key, + _serial=self._serial, + _type=self._type, + _key_id=self._key_id, + _valid_principals=self._valid_principals, + _valid_for_all_principals=self._valid_for_all_principals, + _valid_before=self._valid_before, + _valid_after=self._valid_after, + _critical_options=self._critical_options, + _extensions=[*self._extensions, (name, value)], + ) + + def sign(self, private_key: SSHCertPrivateKeyTypes) -> SSHCertificate: + if not isinstance( + private_key, + ( + ec.EllipticCurvePrivateKey, + rsa.RSAPrivateKey, + ed25519.Ed25519PrivateKey, + ), + ): + raise TypeError("Unsupported private key type") + + if self._public_key is None: + raise ValueError("public_key must be set") + + # Not required + serial = 0 if self._serial is None else self._serial + + if self._type is None: + raise ValueError("type must be set") + + # Not required + key_id = b"" if self._key_id is None else self._key_id + + # A zero length list is valid, but means the certificate + # is valid for any principal of the specified type. We require + # the user to explicitly set valid_for_all_principals to get + # that behavior. + if not self._valid_principals and not self._valid_for_all_principals: + raise ValueError( + "valid_principals must be set if valid_for_all_principals " + "is False" + ) + + if self._valid_before is None: + raise ValueError("valid_before must be set") + + if self._valid_after is None: + raise ValueError("valid_after must be set") + + if self._valid_after > self._valid_before: + raise ValueError("valid_after must be earlier than valid_before") + + # lexically sort our byte strings + self._critical_options.sort(key=lambda x: x[0]) + self._extensions.sort(key=lambda x: x[0]) + + key_type = _get_ssh_key_type(self._public_key) + cert_prefix = key_type + _CERT_SUFFIX + + # Marshal the bytes to be signed + nonce = os.urandom(32) + kformat = _lookup_kformat(key_type) + f = _FragList() + f.put_sshstr(cert_prefix) + f.put_sshstr(nonce) + kformat.encode_public(self._public_key, f) + f.put_u64(serial) + f.put_u32(self._type.value) + f.put_sshstr(key_id) + fprincipals = _FragList() + for p in self._valid_principals: + fprincipals.put_sshstr(p) + f.put_sshstr(fprincipals.tobytes()) + f.put_u64(self._valid_after) + f.put_u64(self._valid_before) + fcrit = _FragList() + for name, value in self._critical_options: + fcrit.put_sshstr(name) + if len(value) > 0: + foptval = _FragList() + foptval.put_sshstr(value) + fcrit.put_sshstr(foptval.tobytes()) + else: + fcrit.put_sshstr(value) + f.put_sshstr(fcrit.tobytes()) + fext = _FragList() + for name, value in self._extensions: + fext.put_sshstr(name) + if len(value) > 0: + fextval = _FragList() + fextval.put_sshstr(value) + fext.put_sshstr(fextval.tobytes()) + else: + fext.put_sshstr(value) + f.put_sshstr(fext.tobytes()) + f.put_sshstr(b"") # RESERVED FIELD + # encode CA public key + ca_type = _get_ssh_key_type(private_key) + caformat = _lookup_kformat(ca_type) + caf = _FragList() + caf.put_sshstr(ca_type) + caformat.encode_public(private_key.public_key(), caf) + f.put_sshstr(caf.tobytes()) + # Sigs according to the rules defined for the CA's public key + # (RFC4253 section 6.6 for ssh-rsa, RFC5656 for ECDSA, + # and RFC8032 for Ed25519). + if isinstance(private_key, ed25519.Ed25519PrivateKey): + signature = private_key.sign(f.tobytes()) + fsig = _FragList() + fsig.put_sshstr(ca_type) + fsig.put_sshstr(signature) + f.put_sshstr(fsig.tobytes()) + elif isinstance(private_key, ec.EllipticCurvePrivateKey): + hash_alg = _get_ec_hash_alg(private_key.curve) + signature = private_key.sign(f.tobytes(), ec.ECDSA(hash_alg)) + r, s = asym_utils.decode_dss_signature(signature) + fsig = _FragList() + fsig.put_sshstr(ca_type) + fsigblob = _FragList() + fsigblob.put_mpint(r) + fsigblob.put_mpint(s) + fsig.put_sshstr(fsigblob.tobytes()) + f.put_sshstr(fsig.tobytes()) + + else: + assert isinstance(private_key, rsa.RSAPrivateKey) + # Just like Golang, we're going to use SHA512 for RSA + # https://cs.opensource.google/go/x/crypto/+/refs/tags/ + # v0.4.0:ssh/certs.go;l=445 + # RFC 8332 defines SHA256 and 512 as options + fsig = _FragList() + fsig.put_sshstr(_SSH_RSA_SHA512) + signature = private_key.sign( + f.tobytes(), padding.PKCS1v15(), hashes.SHA512() + ) + fsig.put_sshstr(signature) + f.put_sshstr(fsig.tobytes()) + + cert_data = binascii.b2a_base64(f.tobytes()).strip() + # load_ssh_public_identity returns a union, but this is + # guaranteed to be an SSHCertificate, so we cast to make + # mypy happy. + return typing.cast( + SSHCertificate, + load_ssh_public_identity(b"".join([cert_prefix, b" ", cert_data])), + ) diff --git a/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/twofactor/__init__.py b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/twofactor/__init__.py new file mode 100644 index 0000000..c1af423 --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/twofactor/__init__.py @@ -0,0 +1,9 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +from __future__ import annotations + + +class InvalidToken(Exception): + pass diff --git a/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/twofactor/__pycache__/__init__.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/twofactor/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..76a68b3 Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/twofactor/__pycache__/__init__.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/twofactor/__pycache__/hotp.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/twofactor/__pycache__/hotp.cpython-312.pyc new file mode 100644 index 0000000..cfcb4ec Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/twofactor/__pycache__/hotp.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/twofactor/__pycache__/totp.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/twofactor/__pycache__/totp.cpython-312.pyc new file mode 100644 index 0000000..0d17972 Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/twofactor/__pycache__/totp.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/twofactor/hotp.py b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/twofactor/hotp.py new file mode 100644 index 0000000..af5ab6e --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/twofactor/hotp.py @@ -0,0 +1,92 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +from __future__ import annotations + +import base64 +import typing +from urllib.parse import quote, urlencode + +from cryptography.hazmat.primitives import constant_time, hmac +from cryptography.hazmat.primitives.hashes import SHA1, SHA256, SHA512 +from cryptography.hazmat.primitives.twofactor import InvalidToken + +HOTPHashTypes = typing.Union[SHA1, SHA256, SHA512] + + +def _generate_uri( + hotp: HOTP, + type_name: str, + account_name: str, + issuer: str | None, + extra_parameters: list[tuple[str, int]], +) -> str: + parameters = [ + ("digits", hotp._length), + ("secret", base64.b32encode(hotp._key)), + ("algorithm", hotp._algorithm.name.upper()), + ] + + if issuer is not None: + parameters.append(("issuer", issuer)) + + parameters.extend(extra_parameters) + + label = ( + f"{quote(issuer)}:{quote(account_name)}" + if issuer + else quote(account_name) + ) + return f"otpauth://{type_name}/{label}?{urlencode(parameters)}" + + +class HOTP: + def __init__( + self, + key: bytes, + length: int, + algorithm: HOTPHashTypes, + backend: typing.Any = None, + enforce_key_length: bool = True, + ) -> None: + if len(key) < 16 and enforce_key_length is True: + raise ValueError("Key length has to be at least 128 bits.") + + if not isinstance(length, int): + raise TypeError("Length parameter must be an integer type.") + + if length < 6 or length > 8: + raise ValueError("Length of HOTP has to be between 6 and 8.") + + if not isinstance(algorithm, (SHA1, SHA256, SHA512)): + raise TypeError("Algorithm must be SHA1, SHA256 or SHA512.") + + self._key = key + self._length = length + self._algorithm = algorithm + + def generate(self, counter: int) -> bytes: + truncated_value = self._dynamic_truncate(counter) + hotp = truncated_value % (10**self._length) + return "{0:0{1}}".format(hotp, self._length).encode() + + def verify(self, hotp: bytes, counter: int) -> None: + if not constant_time.bytes_eq(self.generate(counter), hotp): + raise InvalidToken("Supplied HOTP value does not match.") + + def _dynamic_truncate(self, counter: int) -> int: + ctx = hmac.HMAC(self._key, self._algorithm) + ctx.update(counter.to_bytes(length=8, byteorder="big")) + hmac_value = ctx.finalize() + + offset = hmac_value[len(hmac_value) - 1] & 0b1111 + p = hmac_value[offset : offset + 4] + return int.from_bytes(p, byteorder="big") & 0x7FFFFFFF + + def get_provisioning_uri( + self, account_name: str, counter: int, issuer: str | None + ) -> str: + return _generate_uri( + self, "hotp", account_name, issuer, [("counter", int(counter))] + ) diff --git a/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/twofactor/totp.py b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/twofactor/totp.py new file mode 100644 index 0000000..68a5077 --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/twofactor/totp.py @@ -0,0 +1,50 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +from __future__ import annotations + +import typing + +from cryptography.hazmat.primitives import constant_time +from cryptography.hazmat.primitives.twofactor import InvalidToken +from cryptography.hazmat.primitives.twofactor.hotp import ( + HOTP, + HOTPHashTypes, + _generate_uri, +) + + +class TOTP: + def __init__( + self, + key: bytes, + length: int, + algorithm: HOTPHashTypes, + time_step: int, + backend: typing.Any = None, + enforce_key_length: bool = True, + ): + self._time_step = time_step + self._hotp = HOTP( + key, length, algorithm, enforce_key_length=enforce_key_length + ) + + def generate(self, time: int | float) -> bytes: + counter = int(time / self._time_step) + return self._hotp.generate(counter) + + def verify(self, totp: bytes, time: int) -> None: + if not constant_time.bytes_eq(self.generate(time), totp): + raise InvalidToken("Supplied TOTP value does not match.") + + def get_provisioning_uri( + self, account_name: str, issuer: str | None + ) -> str: + return _generate_uri( + self._hotp, + "totp", + account_name, + issuer, + [("period", int(self._time_step))], + ) diff --git a/code/.venv/lib/python3.12/site-packages/cryptography/py.typed b/code/.venv/lib/python3.12/site-packages/cryptography/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/code/.venv/lib/python3.12/site-packages/cryptography/utils.py b/code/.venv/lib/python3.12/site-packages/cryptography/utils.py new file mode 100644 index 0000000..706d0ae --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/cryptography/utils.py @@ -0,0 +1,127 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +from __future__ import annotations + +import enum +import sys +import types +import typing +import warnings + + +# We use a UserWarning subclass, instead of DeprecationWarning, because CPython +# decided deprecation warnings should be invisible by default. +class CryptographyDeprecationWarning(UserWarning): + pass + + +# Several APIs were deprecated with no specific end-of-life date because of the +# ubiquity of their use. They should not be removed until we agree on when that +# cycle ends. +DeprecatedIn36 = CryptographyDeprecationWarning +DeprecatedIn37 = CryptographyDeprecationWarning +DeprecatedIn40 = CryptographyDeprecationWarning +DeprecatedIn41 = CryptographyDeprecationWarning +DeprecatedIn42 = CryptographyDeprecationWarning +DeprecatedIn43 = CryptographyDeprecationWarning + + +def _check_bytes(name: str, value: bytes) -> None: + if not isinstance(value, bytes): + raise TypeError(f"{name} must be bytes") + + +def _check_byteslike(name: str, value: bytes) -> None: + try: + memoryview(value) + except TypeError: + raise TypeError(f"{name} must be bytes-like") + + +def int_to_bytes(integer: int, length: int | None = None) -> bytes: + if length == 0: + raise ValueError("length argument can't be 0") + return integer.to_bytes( + length or (integer.bit_length() + 7) // 8 or 1, "big" + ) + + +class InterfaceNotImplemented(Exception): + pass + + +class _DeprecatedValue: + def __init__(self, value: object, message: str, warning_class): + self.value = value + self.message = message + self.warning_class = warning_class + + +class _ModuleWithDeprecations(types.ModuleType): + def __init__(self, module: types.ModuleType): + super().__init__(module.__name__) + self.__dict__["_module"] = module + + def __getattr__(self, attr: str) -> object: + obj = getattr(self._module, attr) + if isinstance(obj, _DeprecatedValue): + warnings.warn(obj.message, obj.warning_class, stacklevel=2) + obj = obj.value + return obj + + def __setattr__(self, attr: str, value: object) -> None: + setattr(self._module, attr, value) + + def __delattr__(self, attr: str) -> None: + obj = getattr(self._module, attr) + if isinstance(obj, _DeprecatedValue): + warnings.warn(obj.message, obj.warning_class, stacklevel=2) + + delattr(self._module, attr) + + def __dir__(self) -> typing.Sequence[str]: + return ["_module", *dir(self._module)] + + +def deprecated( + value: object, + module_name: str, + message: str, + warning_class: type[Warning], + name: str | None = None, +) -> _DeprecatedValue: + module = sys.modules[module_name] + if not isinstance(module, _ModuleWithDeprecations): + sys.modules[module_name] = module = _ModuleWithDeprecations(module) + dv = _DeprecatedValue(value, message, warning_class) + # Maintain backwards compatibility with `name is None` for pyOpenSSL. + if name is not None: + setattr(module, name, dv) + return dv + + +def cached_property(func: typing.Callable) -> property: + cached_name = f"_cached_{func}" + sentinel = object() + + def inner(instance: object): + cache = getattr(instance, cached_name, sentinel) + if cache is not sentinel: + return cache + result = func(instance) + setattr(instance, cached_name, result) + return result + + return property(inner) + + +# Python 3.10 changed representation of enums. We use well-defined object +# representation and string representation from Python 3.9. +class Enum(enum.Enum): + def __repr__(self) -> str: + return f"<{self.__class__.__name__}.{self._name_}: {self._value_!r}>" + + def __str__(self) -> str: + return f"{self.__class__.__name__}.{self._name_}" diff --git a/code/.venv/lib/python3.12/site-packages/cryptography/x509/__init__.py b/code/.venv/lib/python3.12/site-packages/cryptography/x509/__init__.py new file mode 100644 index 0000000..26c6444 --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/cryptography/x509/__init__.py @@ -0,0 +1,259 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +from __future__ import annotations + +from cryptography.x509 import certificate_transparency, verification +from cryptography.x509.base import ( + Attribute, + AttributeNotFound, + Attributes, + Certificate, + CertificateBuilder, + CertificateRevocationList, + CertificateRevocationListBuilder, + CertificateSigningRequest, + CertificateSigningRequestBuilder, + InvalidVersion, + RevokedCertificate, + RevokedCertificateBuilder, + Version, + load_der_x509_certificate, + load_der_x509_crl, + load_der_x509_csr, + load_pem_x509_certificate, + load_pem_x509_certificates, + load_pem_x509_crl, + load_pem_x509_csr, + random_serial_number, +) +from cryptography.x509.extensions import ( + AccessDescription, + AuthorityInformationAccess, + AuthorityKeyIdentifier, + BasicConstraints, + CertificateIssuer, + CertificatePolicies, + CRLDistributionPoints, + CRLNumber, + CRLReason, + DeltaCRLIndicator, + DistributionPoint, + DuplicateExtension, + ExtendedKeyUsage, + Extension, + ExtensionNotFound, + Extensions, + ExtensionType, + FreshestCRL, + GeneralNames, + InhibitAnyPolicy, + InvalidityDate, + IssuerAlternativeName, + IssuingDistributionPoint, + KeyUsage, + MSCertificateTemplate, + NameConstraints, + NoticeReference, + OCSPAcceptableResponses, + OCSPNoCheck, + OCSPNonce, + PolicyConstraints, + PolicyInformation, + PrecertificateSignedCertificateTimestamps, + PrecertPoison, + ReasonFlags, + SignedCertificateTimestamps, + SubjectAlternativeName, + SubjectInformationAccess, + SubjectKeyIdentifier, + TLSFeature, + TLSFeatureType, + UnrecognizedExtension, + UserNotice, +) +from cryptography.x509.general_name import ( + DirectoryName, + DNSName, + GeneralName, + IPAddress, + OtherName, + RegisteredID, + RFC822Name, + UniformResourceIdentifier, + UnsupportedGeneralNameType, +) +from cryptography.x509.name import ( + Name, + NameAttribute, + RelativeDistinguishedName, +) +from cryptography.x509.oid import ( + AuthorityInformationAccessOID, + CertificatePoliciesOID, + CRLEntryExtensionOID, + ExtendedKeyUsageOID, + ExtensionOID, + NameOID, + ObjectIdentifier, + PublicKeyAlgorithmOID, + SignatureAlgorithmOID, +) + +OID_AUTHORITY_INFORMATION_ACCESS = ExtensionOID.AUTHORITY_INFORMATION_ACCESS +OID_AUTHORITY_KEY_IDENTIFIER = ExtensionOID.AUTHORITY_KEY_IDENTIFIER +OID_BASIC_CONSTRAINTS = ExtensionOID.BASIC_CONSTRAINTS +OID_CERTIFICATE_POLICIES = ExtensionOID.CERTIFICATE_POLICIES +OID_CRL_DISTRIBUTION_POINTS = ExtensionOID.CRL_DISTRIBUTION_POINTS +OID_EXTENDED_KEY_USAGE = ExtensionOID.EXTENDED_KEY_USAGE +OID_FRESHEST_CRL = ExtensionOID.FRESHEST_CRL +OID_INHIBIT_ANY_POLICY = ExtensionOID.INHIBIT_ANY_POLICY +OID_ISSUER_ALTERNATIVE_NAME = ExtensionOID.ISSUER_ALTERNATIVE_NAME +OID_KEY_USAGE = ExtensionOID.KEY_USAGE +OID_NAME_CONSTRAINTS = ExtensionOID.NAME_CONSTRAINTS +OID_OCSP_NO_CHECK = ExtensionOID.OCSP_NO_CHECK +OID_POLICY_CONSTRAINTS = ExtensionOID.POLICY_CONSTRAINTS +OID_POLICY_MAPPINGS = ExtensionOID.POLICY_MAPPINGS +OID_SUBJECT_ALTERNATIVE_NAME = ExtensionOID.SUBJECT_ALTERNATIVE_NAME +OID_SUBJECT_DIRECTORY_ATTRIBUTES = ExtensionOID.SUBJECT_DIRECTORY_ATTRIBUTES +OID_SUBJECT_INFORMATION_ACCESS = ExtensionOID.SUBJECT_INFORMATION_ACCESS +OID_SUBJECT_KEY_IDENTIFIER = ExtensionOID.SUBJECT_KEY_IDENTIFIER + +OID_DSA_WITH_SHA1 = SignatureAlgorithmOID.DSA_WITH_SHA1 +OID_DSA_WITH_SHA224 = SignatureAlgorithmOID.DSA_WITH_SHA224 +OID_DSA_WITH_SHA256 = SignatureAlgorithmOID.DSA_WITH_SHA256 +OID_ECDSA_WITH_SHA1 = SignatureAlgorithmOID.ECDSA_WITH_SHA1 +OID_ECDSA_WITH_SHA224 = SignatureAlgorithmOID.ECDSA_WITH_SHA224 +OID_ECDSA_WITH_SHA256 = SignatureAlgorithmOID.ECDSA_WITH_SHA256 +OID_ECDSA_WITH_SHA384 = SignatureAlgorithmOID.ECDSA_WITH_SHA384 +OID_ECDSA_WITH_SHA512 = SignatureAlgorithmOID.ECDSA_WITH_SHA512 +OID_RSA_WITH_MD5 = SignatureAlgorithmOID.RSA_WITH_MD5 +OID_RSA_WITH_SHA1 = SignatureAlgorithmOID.RSA_WITH_SHA1 +OID_RSA_WITH_SHA224 = SignatureAlgorithmOID.RSA_WITH_SHA224 +OID_RSA_WITH_SHA256 = SignatureAlgorithmOID.RSA_WITH_SHA256 +OID_RSA_WITH_SHA384 = SignatureAlgorithmOID.RSA_WITH_SHA384 +OID_RSA_WITH_SHA512 = SignatureAlgorithmOID.RSA_WITH_SHA512 +OID_RSASSA_PSS = SignatureAlgorithmOID.RSASSA_PSS + +OID_COMMON_NAME = NameOID.COMMON_NAME +OID_COUNTRY_NAME = NameOID.COUNTRY_NAME +OID_DOMAIN_COMPONENT = NameOID.DOMAIN_COMPONENT +OID_DN_QUALIFIER = NameOID.DN_QUALIFIER +OID_EMAIL_ADDRESS = NameOID.EMAIL_ADDRESS +OID_GENERATION_QUALIFIER = NameOID.GENERATION_QUALIFIER +OID_GIVEN_NAME = NameOID.GIVEN_NAME +OID_LOCALITY_NAME = NameOID.LOCALITY_NAME +OID_ORGANIZATIONAL_UNIT_NAME = NameOID.ORGANIZATIONAL_UNIT_NAME +OID_ORGANIZATION_NAME = NameOID.ORGANIZATION_NAME +OID_PSEUDONYM = NameOID.PSEUDONYM +OID_SERIAL_NUMBER = NameOID.SERIAL_NUMBER +OID_STATE_OR_PROVINCE_NAME = NameOID.STATE_OR_PROVINCE_NAME +OID_SURNAME = NameOID.SURNAME +OID_TITLE = NameOID.TITLE + +OID_CLIENT_AUTH = ExtendedKeyUsageOID.CLIENT_AUTH +OID_CODE_SIGNING = ExtendedKeyUsageOID.CODE_SIGNING +OID_EMAIL_PROTECTION = ExtendedKeyUsageOID.EMAIL_PROTECTION +OID_OCSP_SIGNING = ExtendedKeyUsageOID.OCSP_SIGNING +OID_SERVER_AUTH = ExtendedKeyUsageOID.SERVER_AUTH +OID_TIME_STAMPING = ExtendedKeyUsageOID.TIME_STAMPING + +OID_ANY_POLICY = CertificatePoliciesOID.ANY_POLICY +OID_CPS_QUALIFIER = CertificatePoliciesOID.CPS_QUALIFIER +OID_CPS_USER_NOTICE = CertificatePoliciesOID.CPS_USER_NOTICE + +OID_CERTIFICATE_ISSUER = CRLEntryExtensionOID.CERTIFICATE_ISSUER +OID_CRL_REASON = CRLEntryExtensionOID.CRL_REASON +OID_INVALIDITY_DATE = CRLEntryExtensionOID.INVALIDITY_DATE + +OID_CA_ISSUERS = AuthorityInformationAccessOID.CA_ISSUERS +OID_OCSP = AuthorityInformationAccessOID.OCSP + +__all__ = [ + "OID_CA_ISSUERS", + "OID_OCSP", + "AccessDescription", + "Attribute", + "AttributeNotFound", + "Attributes", + "AuthorityInformationAccess", + "AuthorityKeyIdentifier", + "BasicConstraints", + "CRLDistributionPoints", + "CRLNumber", + "CRLReason", + "Certificate", + "CertificateBuilder", + "CertificateIssuer", + "CertificatePolicies", + "CertificateRevocationList", + "CertificateRevocationListBuilder", + "CertificateSigningRequest", + "CertificateSigningRequestBuilder", + "DNSName", + "DeltaCRLIndicator", + "DirectoryName", + "DistributionPoint", + "DuplicateExtension", + "ExtendedKeyUsage", + "Extension", + "ExtensionNotFound", + "ExtensionType", + "Extensions", + "FreshestCRL", + "GeneralName", + "GeneralNames", + "IPAddress", + "InhibitAnyPolicy", + "InvalidVersion", + "InvalidityDate", + "IssuerAlternativeName", + "IssuingDistributionPoint", + "KeyUsage", + "MSCertificateTemplate", + "Name", + "NameAttribute", + "NameConstraints", + "NameOID", + "NoticeReference", + "OCSPAcceptableResponses", + "OCSPNoCheck", + "OCSPNonce", + "ObjectIdentifier", + "OtherName", + "PolicyConstraints", + "PolicyInformation", + "PrecertPoison", + "PrecertificateSignedCertificateTimestamps", + "PublicKeyAlgorithmOID", + "RFC822Name", + "ReasonFlags", + "RegisteredID", + "RelativeDistinguishedName", + "RevokedCertificate", + "RevokedCertificateBuilder", + "SignatureAlgorithmOID", + "SignedCertificateTimestamps", + "SubjectAlternativeName", + "SubjectInformationAccess", + "SubjectKeyIdentifier", + "TLSFeature", + "TLSFeatureType", + "UniformResourceIdentifier", + "UnrecognizedExtension", + "UnsupportedGeneralNameType", + "UserNotice", + "Version", + "certificate_transparency", + "load_der_x509_certificate", + "load_der_x509_crl", + "load_der_x509_csr", + "load_pem_x509_certificate", + "load_pem_x509_certificates", + "load_pem_x509_crl", + "load_pem_x509_csr", + "random_serial_number", + "verification", + "verification", +] diff --git a/code/.venv/lib/python3.12/site-packages/cryptography/x509/__pycache__/__init__.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/cryptography/x509/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..7f16432 Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/cryptography/x509/__pycache__/__init__.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/cryptography/x509/__pycache__/base.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/cryptography/x509/__pycache__/base.cpython-312.pyc new file mode 100644 index 0000000..74c4b80 Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/cryptography/x509/__pycache__/base.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/cryptography/x509/__pycache__/certificate_transparency.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/cryptography/x509/__pycache__/certificate_transparency.cpython-312.pyc new file mode 100644 index 0000000..c367a4a Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/cryptography/x509/__pycache__/certificate_transparency.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/cryptography/x509/__pycache__/extensions.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/cryptography/x509/__pycache__/extensions.cpython-312.pyc new file mode 100644 index 0000000..e717609 Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/cryptography/x509/__pycache__/extensions.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/cryptography/x509/__pycache__/general_name.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/cryptography/x509/__pycache__/general_name.cpython-312.pyc new file mode 100644 index 0000000..90be154 Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/cryptography/x509/__pycache__/general_name.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/cryptography/x509/__pycache__/name.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/cryptography/x509/__pycache__/name.cpython-312.pyc new file mode 100644 index 0000000..a233021 Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/cryptography/x509/__pycache__/name.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/cryptography/x509/__pycache__/ocsp.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/cryptography/x509/__pycache__/ocsp.cpython-312.pyc new file mode 100644 index 0000000..31f9df0 Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/cryptography/x509/__pycache__/ocsp.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/cryptography/x509/__pycache__/oid.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/cryptography/x509/__pycache__/oid.cpython-312.pyc new file mode 100644 index 0000000..c6c7e5e Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/cryptography/x509/__pycache__/oid.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/cryptography/x509/__pycache__/verification.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/cryptography/x509/__pycache__/verification.cpython-312.pyc new file mode 100644 index 0000000..a45a319 Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/cryptography/x509/__pycache__/verification.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/cryptography/x509/base.py b/code/.venv/lib/python3.12/site-packages/cryptography/x509/base.py new file mode 100644 index 0000000..6ed41e6 --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/cryptography/x509/base.py @@ -0,0 +1,1226 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +from __future__ import annotations + +import abc +import datetime +import os +import typing +import warnings + +from cryptography import utils +from cryptography.hazmat.bindings._rust import x509 as rust_x509 +from cryptography.hazmat.primitives import hashes, serialization +from cryptography.hazmat.primitives.asymmetric import ( + dsa, + ec, + ed448, + ed25519, + padding, + rsa, + x448, + x25519, +) +from cryptography.hazmat.primitives.asymmetric.types import ( + CertificateIssuerPrivateKeyTypes, + CertificateIssuerPublicKeyTypes, + CertificatePublicKeyTypes, +) +from cryptography.x509.extensions import ( + Extension, + Extensions, + ExtensionType, + _make_sequence_methods, +) +from cryptography.x509.name import Name, _ASN1Type +from cryptography.x509.oid import ObjectIdentifier + +_EARLIEST_UTC_TIME = datetime.datetime(1950, 1, 1) + +# This must be kept in sync with sign.rs's list of allowable types in +# identify_hash_type +_AllowedHashTypes = typing.Union[ + hashes.SHA224, + hashes.SHA256, + hashes.SHA384, + hashes.SHA512, + hashes.SHA3_224, + hashes.SHA3_256, + hashes.SHA3_384, + hashes.SHA3_512, +] + + +class AttributeNotFound(Exception): + def __init__(self, msg: str, oid: ObjectIdentifier) -> None: + super().__init__(msg) + self.oid = oid + + +def _reject_duplicate_extension( + extension: Extension[ExtensionType], + extensions: list[Extension[ExtensionType]], +) -> None: + # This is quadratic in the number of extensions + for e in extensions: + if e.oid == extension.oid: + raise ValueError("This extension has already been set.") + + +def _reject_duplicate_attribute( + oid: ObjectIdentifier, + attributes: list[tuple[ObjectIdentifier, bytes, int | None]], +) -> None: + # This is quadratic in the number of attributes + for attr_oid, _, _ in attributes: + if attr_oid == oid: + raise ValueError("This attribute has already been set.") + + +def _convert_to_naive_utc_time(time: datetime.datetime) -> datetime.datetime: + """Normalizes a datetime to a naive datetime in UTC. + + time -- datetime to normalize. Assumed to be in UTC if not timezone + aware. + """ + if time.tzinfo is not None: + offset = time.utcoffset() + offset = offset if offset else datetime.timedelta() + return time.replace(tzinfo=None) - offset + else: + return time + + +class Attribute: + def __init__( + self, + oid: ObjectIdentifier, + value: bytes, + _type: int = _ASN1Type.UTF8String.value, + ) -> None: + self._oid = oid + self._value = value + self._type = _type + + @property + def oid(self) -> ObjectIdentifier: + return self._oid + + @property + def value(self) -> bytes: + return self._value + + def __repr__(self) -> str: + return f"" + + def __eq__(self, other: object) -> bool: + if not isinstance(other, Attribute): + return NotImplemented + + return ( + self.oid == other.oid + and self.value == other.value + and self._type == other._type + ) + + def __hash__(self) -> int: + return hash((self.oid, self.value, self._type)) + + +class Attributes: + def __init__( + self, + attributes: typing.Iterable[Attribute], + ) -> None: + self._attributes = list(attributes) + + __len__, __iter__, __getitem__ = _make_sequence_methods("_attributes") + + def __repr__(self) -> str: + return f"" + + def get_attribute_for_oid(self, oid: ObjectIdentifier) -> Attribute: + for attr in self: + if attr.oid == oid: + return attr + + raise AttributeNotFound(f"No {oid} attribute was found", oid) + + +class Version(utils.Enum): + v1 = 0 + v3 = 2 + + +class InvalidVersion(Exception): + def __init__(self, msg: str, parsed_version: int) -> None: + super().__init__(msg) + self.parsed_version = parsed_version + + +class Certificate(metaclass=abc.ABCMeta): + @abc.abstractmethod + def fingerprint(self, algorithm: hashes.HashAlgorithm) -> bytes: + """ + Returns bytes using digest passed. + """ + + @property + @abc.abstractmethod + def serial_number(self) -> int: + """ + Returns certificate serial number + """ + + @property + @abc.abstractmethod + def version(self) -> Version: + """ + Returns the certificate version + """ + + @abc.abstractmethod + def public_key(self) -> CertificatePublicKeyTypes: + """ + Returns the public key + """ + + @property + @abc.abstractmethod + def public_key_algorithm_oid(self) -> ObjectIdentifier: + """ + Returns the ObjectIdentifier of the public key. + """ + + @property + @abc.abstractmethod + def not_valid_before(self) -> datetime.datetime: + """ + Not before time (represented as UTC datetime) + """ + + @property + @abc.abstractmethod + def not_valid_before_utc(self) -> datetime.datetime: + """ + Not before time (represented as a non-naive UTC datetime) + """ + + @property + @abc.abstractmethod + def not_valid_after(self) -> datetime.datetime: + """ + Not after time (represented as UTC datetime) + """ + + @property + @abc.abstractmethod + def not_valid_after_utc(self) -> datetime.datetime: + """ + Not after time (represented as a non-naive UTC datetime) + """ + + @property + @abc.abstractmethod + def issuer(self) -> Name: + """ + Returns the issuer name object. + """ + + @property + @abc.abstractmethod + def subject(self) -> Name: + """ + Returns the subject name object. + """ + + @property + @abc.abstractmethod + def signature_hash_algorithm( + self, + ) -> hashes.HashAlgorithm | None: + """ + Returns a HashAlgorithm corresponding to the type of the digest signed + in the certificate. + """ + + @property + @abc.abstractmethod + def signature_algorithm_oid(self) -> ObjectIdentifier: + """ + Returns the ObjectIdentifier of the signature algorithm. + """ + + @property + @abc.abstractmethod + def signature_algorithm_parameters( + self, + ) -> None | padding.PSS | padding.PKCS1v15 | ec.ECDSA: + """ + Returns the signature algorithm parameters. + """ + + @property + @abc.abstractmethod + def extensions(self) -> Extensions: + """ + Returns an Extensions object. + """ + + @property + @abc.abstractmethod + def signature(self) -> bytes: + """ + Returns the signature bytes. + """ + + @property + @abc.abstractmethod + def tbs_certificate_bytes(self) -> bytes: + """ + Returns the tbsCertificate payload bytes as defined in RFC 5280. + """ + + @property + @abc.abstractmethod + def tbs_precertificate_bytes(self) -> bytes: + """ + Returns the tbsCertificate payload bytes with the SCT list extension + stripped. + """ + + @abc.abstractmethod + def __eq__(self, other: object) -> bool: + """ + Checks equality. + """ + + @abc.abstractmethod + def __hash__(self) -> int: + """ + Computes a hash. + """ + + @abc.abstractmethod + def public_bytes(self, encoding: serialization.Encoding) -> bytes: + """ + Serializes the certificate to PEM or DER format. + """ + + @abc.abstractmethod + def verify_directly_issued_by(self, issuer: Certificate) -> None: + """ + This method verifies that certificate issuer name matches the + issuer subject name and that the certificate is signed by the + issuer's private key. No other validation is performed. + """ + + +# Runtime isinstance checks need this since the rust class is not a subclass. +Certificate.register(rust_x509.Certificate) + + +class RevokedCertificate(metaclass=abc.ABCMeta): + @property + @abc.abstractmethod + def serial_number(self) -> int: + """ + Returns the serial number of the revoked certificate. + """ + + @property + @abc.abstractmethod + def revocation_date(self) -> datetime.datetime: + """ + Returns the date of when this certificate was revoked. + """ + + @property + @abc.abstractmethod + def revocation_date_utc(self) -> datetime.datetime: + """ + Returns the date of when this certificate was revoked as a non-naive + UTC datetime. + """ + + @property + @abc.abstractmethod + def extensions(self) -> Extensions: + """ + Returns an Extensions object containing a list of Revoked extensions. + """ + + +# Runtime isinstance checks need this since the rust class is not a subclass. +RevokedCertificate.register(rust_x509.RevokedCertificate) + + +class _RawRevokedCertificate(RevokedCertificate): + def __init__( + self, + serial_number: int, + revocation_date: datetime.datetime, + extensions: Extensions, + ): + self._serial_number = serial_number + self._revocation_date = revocation_date + self._extensions = extensions + + @property + def serial_number(self) -> int: + return self._serial_number + + @property + def revocation_date(self) -> datetime.datetime: + warnings.warn( + "Properties that return a naïve datetime object have been " + "deprecated. Please switch to revocation_date_utc.", + utils.DeprecatedIn42, + stacklevel=2, + ) + return self._revocation_date + + @property + def revocation_date_utc(self) -> datetime.datetime: + return self._revocation_date.replace(tzinfo=datetime.timezone.utc) + + @property + def extensions(self) -> Extensions: + return self._extensions + + +class CertificateRevocationList(metaclass=abc.ABCMeta): + @abc.abstractmethod + def public_bytes(self, encoding: serialization.Encoding) -> bytes: + """ + Serializes the CRL to PEM or DER format. + """ + + @abc.abstractmethod + def fingerprint(self, algorithm: hashes.HashAlgorithm) -> bytes: + """ + Returns bytes using digest passed. + """ + + @abc.abstractmethod + def get_revoked_certificate_by_serial_number( + self, serial_number: int + ) -> RevokedCertificate | None: + """ + Returns an instance of RevokedCertificate or None if the serial_number + is not in the CRL. + """ + + @property + @abc.abstractmethod + def signature_hash_algorithm( + self, + ) -> hashes.HashAlgorithm | None: + """ + Returns a HashAlgorithm corresponding to the type of the digest signed + in the certificate. + """ + + @property + @abc.abstractmethod + def signature_algorithm_oid(self) -> ObjectIdentifier: + """ + Returns the ObjectIdentifier of the signature algorithm. + """ + + @property + @abc.abstractmethod + def signature_algorithm_parameters( + self, + ) -> None | padding.PSS | padding.PKCS1v15 | ec.ECDSA: + """ + Returns the signature algorithm parameters. + """ + + @property + @abc.abstractmethod + def issuer(self) -> Name: + """ + Returns the X509Name with the issuer of this CRL. + """ + + @property + @abc.abstractmethod + def next_update(self) -> datetime.datetime | None: + """ + Returns the date of next update for this CRL. + """ + + @property + @abc.abstractmethod + def next_update_utc(self) -> datetime.datetime | None: + """ + Returns the date of next update for this CRL as a non-naive UTC + datetime. + """ + + @property + @abc.abstractmethod + def last_update(self) -> datetime.datetime: + """ + Returns the date of last update for this CRL. + """ + + @property + @abc.abstractmethod + def last_update_utc(self) -> datetime.datetime: + """ + Returns the date of last update for this CRL as a non-naive UTC + datetime. + """ + + @property + @abc.abstractmethod + def extensions(self) -> Extensions: + """ + Returns an Extensions object containing a list of CRL extensions. + """ + + @property + @abc.abstractmethod + def signature(self) -> bytes: + """ + Returns the signature bytes. + """ + + @property + @abc.abstractmethod + def tbs_certlist_bytes(self) -> bytes: + """ + Returns the tbsCertList payload bytes as defined in RFC 5280. + """ + + @abc.abstractmethod + def __eq__(self, other: object) -> bool: + """ + Checks equality. + """ + + @abc.abstractmethod + def __len__(self) -> int: + """ + Number of revoked certificates in the CRL. + """ + + @typing.overload + def __getitem__(self, idx: int) -> RevokedCertificate: ... + + @typing.overload + def __getitem__(self, idx: slice) -> list[RevokedCertificate]: ... + + @abc.abstractmethod + def __getitem__( + self, idx: int | slice + ) -> RevokedCertificate | list[RevokedCertificate]: + """ + Returns a revoked certificate (or slice of revoked certificates). + """ + + @abc.abstractmethod + def __iter__(self) -> typing.Iterator[RevokedCertificate]: + """ + Iterator over the revoked certificates + """ + + @abc.abstractmethod + def is_signature_valid( + self, public_key: CertificateIssuerPublicKeyTypes + ) -> bool: + """ + Verifies signature of revocation list against given public key. + """ + + +CertificateRevocationList.register(rust_x509.CertificateRevocationList) + + +class CertificateSigningRequest(metaclass=abc.ABCMeta): + @abc.abstractmethod + def __eq__(self, other: object) -> bool: + """ + Checks equality. + """ + + @abc.abstractmethod + def __hash__(self) -> int: + """ + Computes a hash. + """ + + @abc.abstractmethod + def public_key(self) -> CertificatePublicKeyTypes: + """ + Returns the public key + """ + + @property + @abc.abstractmethod + def subject(self) -> Name: + """ + Returns the subject name object. + """ + + @property + @abc.abstractmethod + def signature_hash_algorithm( + self, + ) -> hashes.HashAlgorithm | None: + """ + Returns a HashAlgorithm corresponding to the type of the digest signed + in the certificate. + """ + + @property + @abc.abstractmethod + def signature_algorithm_oid(self) -> ObjectIdentifier: + """ + Returns the ObjectIdentifier of the signature algorithm. + """ + + @property + @abc.abstractmethod + def signature_algorithm_parameters( + self, + ) -> None | padding.PSS | padding.PKCS1v15 | ec.ECDSA: + """ + Returns the signature algorithm parameters. + """ + + @property + @abc.abstractmethod + def extensions(self) -> Extensions: + """ + Returns the extensions in the signing request. + """ + + @property + @abc.abstractmethod + def attributes(self) -> Attributes: + """ + Returns an Attributes object. + """ + + @abc.abstractmethod + def public_bytes(self, encoding: serialization.Encoding) -> bytes: + """ + Encodes the request to PEM or DER format. + """ + + @property + @abc.abstractmethod + def signature(self) -> bytes: + """ + Returns the signature bytes. + """ + + @property + @abc.abstractmethod + def tbs_certrequest_bytes(self) -> bytes: + """ + Returns the PKCS#10 CertificationRequestInfo bytes as defined in RFC + 2986. + """ + + @property + @abc.abstractmethod + def is_signature_valid(self) -> bool: + """ + Verifies signature of signing request. + """ + + @abc.abstractmethod + def get_attribute_for_oid(self, oid: ObjectIdentifier) -> bytes: + """ + Get the attribute value for a given OID. + """ + + +# Runtime isinstance checks need this since the rust class is not a subclass. +CertificateSigningRequest.register(rust_x509.CertificateSigningRequest) + + +load_pem_x509_certificate = rust_x509.load_pem_x509_certificate +load_der_x509_certificate = rust_x509.load_der_x509_certificate + +load_pem_x509_certificates = rust_x509.load_pem_x509_certificates + +load_pem_x509_csr = rust_x509.load_pem_x509_csr +load_der_x509_csr = rust_x509.load_der_x509_csr + +load_pem_x509_crl = rust_x509.load_pem_x509_crl +load_der_x509_crl = rust_x509.load_der_x509_crl + + +class CertificateSigningRequestBuilder: + def __init__( + self, + subject_name: Name | None = None, + extensions: list[Extension[ExtensionType]] = [], + attributes: list[tuple[ObjectIdentifier, bytes, int | None]] = [], + ): + """ + Creates an empty X.509 certificate request (v1). + """ + self._subject_name = subject_name + self._extensions = extensions + self._attributes = attributes + + def subject_name(self, name: Name) -> CertificateSigningRequestBuilder: + """ + Sets the certificate requestor's distinguished name. + """ + if not isinstance(name, Name): + raise TypeError("Expecting x509.Name object.") + if self._subject_name is not None: + raise ValueError("The subject name may only be set once.") + return CertificateSigningRequestBuilder( + name, self._extensions, self._attributes + ) + + def add_extension( + self, extval: ExtensionType, critical: bool + ) -> CertificateSigningRequestBuilder: + """ + Adds an X.509 extension to the certificate request. + """ + if not isinstance(extval, ExtensionType): + raise TypeError("extension must be an ExtensionType") + + extension = Extension(extval.oid, critical, extval) + _reject_duplicate_extension(extension, self._extensions) + + return CertificateSigningRequestBuilder( + self._subject_name, + [*self._extensions, extension], + self._attributes, + ) + + def add_attribute( + self, + oid: ObjectIdentifier, + value: bytes, + *, + _tag: _ASN1Type | None = None, + ) -> CertificateSigningRequestBuilder: + """ + Adds an X.509 attribute with an OID and associated value. + """ + if not isinstance(oid, ObjectIdentifier): + raise TypeError("oid must be an ObjectIdentifier") + + if not isinstance(value, bytes): + raise TypeError("value must be bytes") + + if _tag is not None and not isinstance(_tag, _ASN1Type): + raise TypeError("tag must be _ASN1Type") + + _reject_duplicate_attribute(oid, self._attributes) + + if _tag is not None: + tag = _tag.value + else: + tag = None + + return CertificateSigningRequestBuilder( + self._subject_name, + self._extensions, + [*self._attributes, (oid, value, tag)], + ) + + def sign( + self, + private_key: CertificateIssuerPrivateKeyTypes, + algorithm: _AllowedHashTypes | None, + backend: typing.Any = None, + *, + rsa_padding: padding.PSS | padding.PKCS1v15 | None = None, + ) -> CertificateSigningRequest: + """ + Signs the request using the requestor's private key. + """ + if self._subject_name is None: + raise ValueError("A CertificateSigningRequest must have a subject") + + if rsa_padding is not None: + if not isinstance(rsa_padding, (padding.PSS, padding.PKCS1v15)): + raise TypeError("Padding must be PSS or PKCS1v15") + if not isinstance(private_key, rsa.RSAPrivateKey): + raise TypeError("Padding is only supported for RSA keys") + + return rust_x509.create_x509_csr( + self, private_key, algorithm, rsa_padding + ) + + +class CertificateBuilder: + _extensions: list[Extension[ExtensionType]] + + def __init__( + self, + issuer_name: Name | None = None, + subject_name: Name | None = None, + public_key: CertificatePublicKeyTypes | None = None, + serial_number: int | None = None, + not_valid_before: datetime.datetime | None = None, + not_valid_after: datetime.datetime | None = None, + extensions: list[Extension[ExtensionType]] = [], + ) -> None: + self._version = Version.v3 + self._issuer_name = issuer_name + self._subject_name = subject_name + self._public_key = public_key + self._serial_number = serial_number + self._not_valid_before = not_valid_before + self._not_valid_after = not_valid_after + self._extensions = extensions + + def issuer_name(self, name: Name) -> CertificateBuilder: + """ + Sets the CA's distinguished name. + """ + if not isinstance(name, Name): + raise TypeError("Expecting x509.Name object.") + if self._issuer_name is not None: + raise ValueError("The issuer name may only be set once.") + return CertificateBuilder( + name, + self._subject_name, + self._public_key, + self._serial_number, + self._not_valid_before, + self._not_valid_after, + self._extensions, + ) + + def subject_name(self, name: Name) -> CertificateBuilder: + """ + Sets the requestor's distinguished name. + """ + if not isinstance(name, Name): + raise TypeError("Expecting x509.Name object.") + if self._subject_name is not None: + raise ValueError("The subject name may only be set once.") + return CertificateBuilder( + self._issuer_name, + name, + self._public_key, + self._serial_number, + self._not_valid_before, + self._not_valid_after, + self._extensions, + ) + + def public_key( + self, + key: CertificatePublicKeyTypes, + ) -> CertificateBuilder: + """ + Sets the requestor's public key (as found in the signing request). + """ + if not isinstance( + key, + ( + dsa.DSAPublicKey, + rsa.RSAPublicKey, + ec.EllipticCurvePublicKey, + ed25519.Ed25519PublicKey, + ed448.Ed448PublicKey, + x25519.X25519PublicKey, + x448.X448PublicKey, + ), + ): + raise TypeError( + "Expecting one of DSAPublicKey, RSAPublicKey," + " EllipticCurvePublicKey, Ed25519PublicKey," + " Ed448PublicKey, X25519PublicKey, or " + "X448PublicKey." + ) + if self._public_key is not None: + raise ValueError("The public key may only be set once.") + return CertificateBuilder( + self._issuer_name, + self._subject_name, + key, + self._serial_number, + self._not_valid_before, + self._not_valid_after, + self._extensions, + ) + + def serial_number(self, number: int) -> CertificateBuilder: + """ + Sets the certificate serial number. + """ + if not isinstance(number, int): + raise TypeError("Serial number must be of integral type.") + if self._serial_number is not None: + raise ValueError("The serial number may only be set once.") + if number <= 0: + raise ValueError("The serial number should be positive.") + + # ASN.1 integers are always signed, so most significant bit must be + # zero. + if number.bit_length() >= 160: # As defined in RFC 5280 + raise ValueError( + "The serial number should not be more than 159 bits." + ) + return CertificateBuilder( + self._issuer_name, + self._subject_name, + self._public_key, + number, + self._not_valid_before, + self._not_valid_after, + self._extensions, + ) + + def not_valid_before(self, time: datetime.datetime) -> CertificateBuilder: + """ + Sets the certificate activation time. + """ + if not isinstance(time, datetime.datetime): + raise TypeError("Expecting datetime object.") + if self._not_valid_before is not None: + raise ValueError("The not valid before may only be set once.") + time = _convert_to_naive_utc_time(time) + if time < _EARLIEST_UTC_TIME: + raise ValueError( + "The not valid before date must be on or after" + " 1950 January 1)." + ) + if self._not_valid_after is not None and time > self._not_valid_after: + raise ValueError( + "The not valid before date must be before the not valid after " + "date." + ) + return CertificateBuilder( + self._issuer_name, + self._subject_name, + self._public_key, + self._serial_number, + time, + self._not_valid_after, + self._extensions, + ) + + def not_valid_after(self, time: datetime.datetime) -> CertificateBuilder: + """ + Sets the certificate expiration time. + """ + if not isinstance(time, datetime.datetime): + raise TypeError("Expecting datetime object.") + if self._not_valid_after is not None: + raise ValueError("The not valid after may only be set once.") + time = _convert_to_naive_utc_time(time) + if time < _EARLIEST_UTC_TIME: + raise ValueError( + "The not valid after date must be on or after" + " 1950 January 1." + ) + if ( + self._not_valid_before is not None + and time < self._not_valid_before + ): + raise ValueError( + "The not valid after date must be after the not valid before " + "date." + ) + return CertificateBuilder( + self._issuer_name, + self._subject_name, + self._public_key, + self._serial_number, + self._not_valid_before, + time, + self._extensions, + ) + + def add_extension( + self, extval: ExtensionType, critical: bool + ) -> CertificateBuilder: + """ + Adds an X.509 extension to the certificate. + """ + if not isinstance(extval, ExtensionType): + raise TypeError("extension must be an ExtensionType") + + extension = Extension(extval.oid, critical, extval) + _reject_duplicate_extension(extension, self._extensions) + + return CertificateBuilder( + self._issuer_name, + self._subject_name, + self._public_key, + self._serial_number, + self._not_valid_before, + self._not_valid_after, + [*self._extensions, extension], + ) + + def sign( + self, + private_key: CertificateIssuerPrivateKeyTypes, + algorithm: _AllowedHashTypes | None, + backend: typing.Any = None, + *, + rsa_padding: padding.PSS | padding.PKCS1v15 | None = None, + ) -> Certificate: + """ + Signs the certificate using the CA's private key. + """ + if self._subject_name is None: + raise ValueError("A certificate must have a subject name") + + if self._issuer_name is None: + raise ValueError("A certificate must have an issuer name") + + if self._serial_number is None: + raise ValueError("A certificate must have a serial number") + + if self._not_valid_before is None: + raise ValueError("A certificate must have a not valid before time") + + if self._not_valid_after is None: + raise ValueError("A certificate must have a not valid after time") + + if self._public_key is None: + raise ValueError("A certificate must have a public key") + + if rsa_padding is not None: + if not isinstance(rsa_padding, (padding.PSS, padding.PKCS1v15)): + raise TypeError("Padding must be PSS or PKCS1v15") + if not isinstance(private_key, rsa.RSAPrivateKey): + raise TypeError("Padding is only supported for RSA keys") + + return rust_x509.create_x509_certificate( + self, private_key, algorithm, rsa_padding + ) + + +class CertificateRevocationListBuilder: + _extensions: list[Extension[ExtensionType]] + _revoked_certificates: list[RevokedCertificate] + + def __init__( + self, + issuer_name: Name | None = None, + last_update: datetime.datetime | None = None, + next_update: datetime.datetime | None = None, + extensions: list[Extension[ExtensionType]] = [], + revoked_certificates: list[RevokedCertificate] = [], + ): + self._issuer_name = issuer_name + self._last_update = last_update + self._next_update = next_update + self._extensions = extensions + self._revoked_certificates = revoked_certificates + + def issuer_name( + self, issuer_name: Name + ) -> CertificateRevocationListBuilder: + if not isinstance(issuer_name, Name): + raise TypeError("Expecting x509.Name object.") + if self._issuer_name is not None: + raise ValueError("The issuer name may only be set once.") + return CertificateRevocationListBuilder( + issuer_name, + self._last_update, + self._next_update, + self._extensions, + self._revoked_certificates, + ) + + def last_update( + self, last_update: datetime.datetime + ) -> CertificateRevocationListBuilder: + if not isinstance(last_update, datetime.datetime): + raise TypeError("Expecting datetime object.") + if self._last_update is not None: + raise ValueError("Last update may only be set once.") + last_update = _convert_to_naive_utc_time(last_update) + if last_update < _EARLIEST_UTC_TIME: + raise ValueError( + "The last update date must be on or after 1950 January 1." + ) + if self._next_update is not None and last_update > self._next_update: + raise ValueError( + "The last update date must be before the next update date." + ) + return CertificateRevocationListBuilder( + self._issuer_name, + last_update, + self._next_update, + self._extensions, + self._revoked_certificates, + ) + + def next_update( + self, next_update: datetime.datetime + ) -> CertificateRevocationListBuilder: + if not isinstance(next_update, datetime.datetime): + raise TypeError("Expecting datetime object.") + if self._next_update is not None: + raise ValueError("Last update may only be set once.") + next_update = _convert_to_naive_utc_time(next_update) + if next_update < _EARLIEST_UTC_TIME: + raise ValueError( + "The last update date must be on or after 1950 January 1." + ) + if self._last_update is not None and next_update < self._last_update: + raise ValueError( + "The next update date must be after the last update date." + ) + return CertificateRevocationListBuilder( + self._issuer_name, + self._last_update, + next_update, + self._extensions, + self._revoked_certificates, + ) + + def add_extension( + self, extval: ExtensionType, critical: bool + ) -> CertificateRevocationListBuilder: + """ + Adds an X.509 extension to the certificate revocation list. + """ + if not isinstance(extval, ExtensionType): + raise TypeError("extension must be an ExtensionType") + + extension = Extension(extval.oid, critical, extval) + _reject_duplicate_extension(extension, self._extensions) + return CertificateRevocationListBuilder( + self._issuer_name, + self._last_update, + self._next_update, + [*self._extensions, extension], + self._revoked_certificates, + ) + + def add_revoked_certificate( + self, revoked_certificate: RevokedCertificate + ) -> CertificateRevocationListBuilder: + """ + Adds a revoked certificate to the CRL. + """ + if not isinstance(revoked_certificate, RevokedCertificate): + raise TypeError("Must be an instance of RevokedCertificate") + + return CertificateRevocationListBuilder( + self._issuer_name, + self._last_update, + self._next_update, + self._extensions, + [*self._revoked_certificates, revoked_certificate], + ) + + def sign( + self, + private_key: CertificateIssuerPrivateKeyTypes, + algorithm: _AllowedHashTypes | None, + backend: typing.Any = None, + *, + rsa_padding: padding.PSS | padding.PKCS1v15 | None = None, + ) -> CertificateRevocationList: + if self._issuer_name is None: + raise ValueError("A CRL must have an issuer name") + + if self._last_update is None: + raise ValueError("A CRL must have a last update time") + + if self._next_update is None: + raise ValueError("A CRL must have a next update time") + + if rsa_padding is not None: + if not isinstance(rsa_padding, (padding.PSS, padding.PKCS1v15)): + raise TypeError("Padding must be PSS or PKCS1v15") + if not isinstance(private_key, rsa.RSAPrivateKey): + raise TypeError("Padding is only supported for RSA keys") + + return rust_x509.create_x509_crl( + self, private_key, algorithm, rsa_padding + ) + + +class RevokedCertificateBuilder: + def __init__( + self, + serial_number: int | None = None, + revocation_date: datetime.datetime | None = None, + extensions: list[Extension[ExtensionType]] = [], + ): + self._serial_number = serial_number + self._revocation_date = revocation_date + self._extensions = extensions + + def serial_number(self, number: int) -> RevokedCertificateBuilder: + if not isinstance(number, int): + raise TypeError("Serial number must be of integral type.") + if self._serial_number is not None: + raise ValueError("The serial number may only be set once.") + if number <= 0: + raise ValueError("The serial number should be positive") + + # ASN.1 integers are always signed, so most significant bit must be + # zero. + if number.bit_length() >= 160: # As defined in RFC 5280 + raise ValueError( + "The serial number should not be more than 159 bits." + ) + return RevokedCertificateBuilder( + number, self._revocation_date, self._extensions + ) + + def revocation_date( + self, time: datetime.datetime + ) -> RevokedCertificateBuilder: + if not isinstance(time, datetime.datetime): + raise TypeError("Expecting datetime object.") + if self._revocation_date is not None: + raise ValueError("The revocation date may only be set once.") + time = _convert_to_naive_utc_time(time) + if time < _EARLIEST_UTC_TIME: + raise ValueError( + "The revocation date must be on or after 1950 January 1." + ) + return RevokedCertificateBuilder( + self._serial_number, time, self._extensions + ) + + def add_extension( + self, extval: ExtensionType, critical: bool + ) -> RevokedCertificateBuilder: + if not isinstance(extval, ExtensionType): + raise TypeError("extension must be an ExtensionType") + + extension = Extension(extval.oid, critical, extval) + _reject_duplicate_extension(extension, self._extensions) + return RevokedCertificateBuilder( + self._serial_number, + self._revocation_date, + [*self._extensions, extension], + ) + + def build(self, backend: typing.Any = None) -> RevokedCertificate: + if self._serial_number is None: + raise ValueError("A revoked certificate must have a serial number") + if self._revocation_date is None: + raise ValueError( + "A revoked certificate must have a revocation date" + ) + return _RawRevokedCertificate( + self._serial_number, + self._revocation_date, + Extensions(self._extensions), + ) + + +def random_serial_number() -> int: + return int.from_bytes(os.urandom(20), "big") >> 1 diff --git a/code/.venv/lib/python3.12/site-packages/cryptography/x509/certificate_transparency.py b/code/.venv/lib/python3.12/site-packages/cryptography/x509/certificate_transparency.py new file mode 100644 index 0000000..73647ee --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/cryptography/x509/certificate_transparency.py @@ -0,0 +1,97 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +from __future__ import annotations + +import abc +import datetime + +from cryptography import utils +from cryptography.hazmat.bindings._rust import x509 as rust_x509 +from cryptography.hazmat.primitives.hashes import HashAlgorithm + + +class LogEntryType(utils.Enum): + X509_CERTIFICATE = 0 + PRE_CERTIFICATE = 1 + + +class Version(utils.Enum): + v1 = 0 + + +class SignatureAlgorithm(utils.Enum): + """ + Signature algorithms that are valid for SCTs. + + These are exactly the same as SignatureAlgorithm in RFC 5246 (TLS 1.2). + + See: + """ + + ANONYMOUS = 0 + RSA = 1 + DSA = 2 + ECDSA = 3 + + +class SignedCertificateTimestamp(metaclass=abc.ABCMeta): + @property + @abc.abstractmethod + def version(self) -> Version: + """ + Returns the SCT version. + """ + + @property + @abc.abstractmethod + def log_id(self) -> bytes: + """ + Returns an identifier indicating which log this SCT is for. + """ + + @property + @abc.abstractmethod + def timestamp(self) -> datetime.datetime: + """ + Returns the timestamp for this SCT. + """ + + @property + @abc.abstractmethod + def entry_type(self) -> LogEntryType: + """ + Returns whether this is an SCT for a certificate or pre-certificate. + """ + + @property + @abc.abstractmethod + def signature_hash_algorithm(self) -> HashAlgorithm: + """ + Returns the hash algorithm used for the SCT's signature. + """ + + @property + @abc.abstractmethod + def signature_algorithm(self) -> SignatureAlgorithm: + """ + Returns the signing algorithm used for the SCT's signature. + """ + + @property + @abc.abstractmethod + def signature(self) -> bytes: + """ + Returns the signature for this SCT. + """ + + @property + @abc.abstractmethod + def extension_bytes(self) -> bytes: + """ + Returns the raw bytes of any extensions for this SCT. + """ + + +SignedCertificateTimestamp.register(rust_x509.Sct) diff --git a/code/.venv/lib/python3.12/site-packages/cryptography/x509/extensions.py b/code/.venv/lib/python3.12/site-packages/cryptography/x509/extensions.py new file mode 100644 index 0000000..5e7486a --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/cryptography/x509/extensions.py @@ -0,0 +1,2196 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +from __future__ import annotations + +import abc +import datetime +import hashlib +import ipaddress +import typing + +from cryptography import utils +from cryptography.hazmat.bindings._rust import asn1 +from cryptography.hazmat.bindings._rust import x509 as rust_x509 +from cryptography.hazmat.primitives import constant_time, serialization +from cryptography.hazmat.primitives.asymmetric.ec import EllipticCurvePublicKey +from cryptography.hazmat.primitives.asymmetric.rsa import RSAPublicKey +from cryptography.hazmat.primitives.asymmetric.types import ( + CertificateIssuerPublicKeyTypes, + CertificatePublicKeyTypes, +) +from cryptography.x509.certificate_transparency import ( + SignedCertificateTimestamp, +) +from cryptography.x509.general_name import ( + DirectoryName, + DNSName, + GeneralName, + IPAddress, + OtherName, + RegisteredID, + RFC822Name, + UniformResourceIdentifier, + _IPAddressTypes, +) +from cryptography.x509.name import Name, RelativeDistinguishedName +from cryptography.x509.oid import ( + CRLEntryExtensionOID, + ExtensionOID, + ObjectIdentifier, + OCSPExtensionOID, +) + +ExtensionTypeVar = typing.TypeVar( + "ExtensionTypeVar", bound="ExtensionType", covariant=True +) + + +def _key_identifier_from_public_key( + public_key: CertificatePublicKeyTypes, +) -> bytes: + if isinstance(public_key, RSAPublicKey): + data = public_key.public_bytes( + serialization.Encoding.DER, + serialization.PublicFormat.PKCS1, + ) + elif isinstance(public_key, EllipticCurvePublicKey): + data = public_key.public_bytes( + serialization.Encoding.X962, + serialization.PublicFormat.UncompressedPoint, + ) + else: + # This is a very slow way to do this. + serialized = public_key.public_bytes( + serialization.Encoding.DER, + serialization.PublicFormat.SubjectPublicKeyInfo, + ) + data = asn1.parse_spki_for_data(serialized) + + return hashlib.sha1(data).digest() + + +def _make_sequence_methods(field_name: str): + def len_method(self) -> int: + return len(getattr(self, field_name)) + + def iter_method(self): + return iter(getattr(self, field_name)) + + def getitem_method(self, idx): + return getattr(self, field_name)[idx] + + return len_method, iter_method, getitem_method + + +class DuplicateExtension(Exception): + def __init__(self, msg: str, oid: ObjectIdentifier) -> None: + super().__init__(msg) + self.oid = oid + + +class ExtensionNotFound(Exception): + def __init__(self, msg: str, oid: ObjectIdentifier) -> None: + super().__init__(msg) + self.oid = oid + + +class ExtensionType(metaclass=abc.ABCMeta): + oid: typing.ClassVar[ObjectIdentifier] + + def public_bytes(self) -> bytes: + """ + Serializes the extension type to DER. + """ + raise NotImplementedError( + f"public_bytes is not implemented for extension type {self!r}" + ) + + +class Extensions: + def __init__( + self, extensions: typing.Iterable[Extension[ExtensionType]] + ) -> None: + self._extensions = list(extensions) + + def get_extension_for_oid( + self, oid: ObjectIdentifier + ) -> Extension[ExtensionType]: + for ext in self: + if ext.oid == oid: + return ext + + raise ExtensionNotFound(f"No {oid} extension was found", oid) + + def get_extension_for_class( + self, extclass: type[ExtensionTypeVar] + ) -> Extension[ExtensionTypeVar]: + if extclass is UnrecognizedExtension: + raise TypeError( + "UnrecognizedExtension can't be used with " + "get_extension_for_class because more than one instance of the" + " class may be present." + ) + + for ext in self: + if isinstance(ext.value, extclass): + return ext + + raise ExtensionNotFound( + f"No {extclass} extension was found", extclass.oid + ) + + __len__, __iter__, __getitem__ = _make_sequence_methods("_extensions") + + def __repr__(self) -> str: + return f"" + + +class CRLNumber(ExtensionType): + oid = ExtensionOID.CRL_NUMBER + + def __init__(self, crl_number: int) -> None: + if not isinstance(crl_number, int): + raise TypeError("crl_number must be an integer") + + self._crl_number = crl_number + + def __eq__(self, other: object) -> bool: + if not isinstance(other, CRLNumber): + return NotImplemented + + return self.crl_number == other.crl_number + + def __hash__(self) -> int: + return hash(self.crl_number) + + def __repr__(self) -> str: + return f"" + + @property + def crl_number(self) -> int: + return self._crl_number + + def public_bytes(self) -> bytes: + return rust_x509.encode_extension_value(self) + + +class AuthorityKeyIdentifier(ExtensionType): + oid = ExtensionOID.AUTHORITY_KEY_IDENTIFIER + + def __init__( + self, + key_identifier: bytes | None, + authority_cert_issuer: typing.Iterable[GeneralName] | None, + authority_cert_serial_number: int | None, + ) -> None: + if (authority_cert_issuer is None) != ( + authority_cert_serial_number is None + ): + raise ValueError( + "authority_cert_issuer and authority_cert_serial_number " + "must both be present or both None" + ) + + if authority_cert_issuer is not None: + authority_cert_issuer = list(authority_cert_issuer) + if not all( + isinstance(x, GeneralName) for x in authority_cert_issuer + ): + raise TypeError( + "authority_cert_issuer must be a list of GeneralName " + "objects" + ) + + if authority_cert_serial_number is not None and not isinstance( + authority_cert_serial_number, int + ): + raise TypeError("authority_cert_serial_number must be an integer") + + self._key_identifier = key_identifier + self._authority_cert_issuer = authority_cert_issuer + self._authority_cert_serial_number = authority_cert_serial_number + + # This takes a subset of CertificatePublicKeyTypes because an issuer + # cannot have an X25519/X448 key. This introduces some unfortunate + # asymmetry that requires typing users to explicitly + # narrow their type, but we should make this accurate and not just + # convenient. + @classmethod + def from_issuer_public_key( + cls, public_key: CertificateIssuerPublicKeyTypes + ) -> AuthorityKeyIdentifier: + digest = _key_identifier_from_public_key(public_key) + return cls( + key_identifier=digest, + authority_cert_issuer=None, + authority_cert_serial_number=None, + ) + + @classmethod + def from_issuer_subject_key_identifier( + cls, ski: SubjectKeyIdentifier + ) -> AuthorityKeyIdentifier: + return cls( + key_identifier=ski.digest, + authority_cert_issuer=None, + authority_cert_serial_number=None, + ) + + def __repr__(self) -> str: + return ( + f"" + ) + + def __eq__(self, other: object) -> bool: + if not isinstance(other, AuthorityKeyIdentifier): + return NotImplemented + + return ( + self.key_identifier == other.key_identifier + and self.authority_cert_issuer == other.authority_cert_issuer + and self.authority_cert_serial_number + == other.authority_cert_serial_number + ) + + def __hash__(self) -> int: + if self.authority_cert_issuer is None: + aci = None + else: + aci = tuple(self.authority_cert_issuer) + return hash( + (self.key_identifier, aci, self.authority_cert_serial_number) + ) + + @property + def key_identifier(self) -> bytes | None: + return self._key_identifier + + @property + def authority_cert_issuer( + self, + ) -> list[GeneralName] | None: + return self._authority_cert_issuer + + @property + def authority_cert_serial_number(self) -> int | None: + return self._authority_cert_serial_number + + def public_bytes(self) -> bytes: + return rust_x509.encode_extension_value(self) + + +class SubjectKeyIdentifier(ExtensionType): + oid = ExtensionOID.SUBJECT_KEY_IDENTIFIER + + def __init__(self, digest: bytes) -> None: + self._digest = digest + + @classmethod + def from_public_key( + cls, public_key: CertificatePublicKeyTypes + ) -> SubjectKeyIdentifier: + return cls(_key_identifier_from_public_key(public_key)) + + @property + def digest(self) -> bytes: + return self._digest + + @property + def key_identifier(self) -> bytes: + return self._digest + + def __repr__(self) -> str: + return f"" + + def __eq__(self, other: object) -> bool: + if not isinstance(other, SubjectKeyIdentifier): + return NotImplemented + + return constant_time.bytes_eq(self.digest, other.digest) + + def __hash__(self) -> int: + return hash(self.digest) + + def public_bytes(self) -> bytes: + return rust_x509.encode_extension_value(self) + + +class AuthorityInformationAccess(ExtensionType): + oid = ExtensionOID.AUTHORITY_INFORMATION_ACCESS + + def __init__( + self, descriptions: typing.Iterable[AccessDescription] + ) -> None: + descriptions = list(descriptions) + if not all(isinstance(x, AccessDescription) for x in descriptions): + raise TypeError( + "Every item in the descriptions list must be an " + "AccessDescription" + ) + + self._descriptions = descriptions + + __len__, __iter__, __getitem__ = _make_sequence_methods("_descriptions") + + def __repr__(self) -> str: + return f"" + + def __eq__(self, other: object) -> bool: + if not isinstance(other, AuthorityInformationAccess): + return NotImplemented + + return self._descriptions == other._descriptions + + def __hash__(self) -> int: + return hash(tuple(self._descriptions)) + + def public_bytes(self) -> bytes: + return rust_x509.encode_extension_value(self) + + +class SubjectInformationAccess(ExtensionType): + oid = ExtensionOID.SUBJECT_INFORMATION_ACCESS + + def __init__( + self, descriptions: typing.Iterable[AccessDescription] + ) -> None: + descriptions = list(descriptions) + if not all(isinstance(x, AccessDescription) for x in descriptions): + raise TypeError( + "Every item in the descriptions list must be an " + "AccessDescription" + ) + + self._descriptions = descriptions + + __len__, __iter__, __getitem__ = _make_sequence_methods("_descriptions") + + def __repr__(self) -> str: + return f"" + + def __eq__(self, other: object) -> bool: + if not isinstance(other, SubjectInformationAccess): + return NotImplemented + + return self._descriptions == other._descriptions + + def __hash__(self) -> int: + return hash(tuple(self._descriptions)) + + def public_bytes(self) -> bytes: + return rust_x509.encode_extension_value(self) + + +class AccessDescription: + def __init__( + self, access_method: ObjectIdentifier, access_location: GeneralName + ) -> None: + if not isinstance(access_method, ObjectIdentifier): + raise TypeError("access_method must be an ObjectIdentifier") + + if not isinstance(access_location, GeneralName): + raise TypeError("access_location must be a GeneralName") + + self._access_method = access_method + self._access_location = access_location + + def __repr__(self) -> str: + return ( + f"" + ) + + def __eq__(self, other: object) -> bool: + if not isinstance(other, AccessDescription): + return NotImplemented + + return ( + self.access_method == other.access_method + and self.access_location == other.access_location + ) + + def __hash__(self) -> int: + return hash((self.access_method, self.access_location)) + + @property + def access_method(self) -> ObjectIdentifier: + return self._access_method + + @property + def access_location(self) -> GeneralName: + return self._access_location + + +class BasicConstraints(ExtensionType): + oid = ExtensionOID.BASIC_CONSTRAINTS + + def __init__(self, ca: bool, path_length: int | None) -> None: + if not isinstance(ca, bool): + raise TypeError("ca must be a boolean value") + + if path_length is not None and not ca: + raise ValueError("path_length must be None when ca is False") + + if path_length is not None and ( + not isinstance(path_length, int) or path_length < 0 + ): + raise TypeError( + "path_length must be a non-negative integer or None" + ) + + self._ca = ca + self._path_length = path_length + + @property + def ca(self) -> bool: + return self._ca + + @property + def path_length(self) -> int | None: + return self._path_length + + def __repr__(self) -> str: + return ( + f"" + ) + + def __eq__(self, other: object) -> bool: + if not isinstance(other, BasicConstraints): + return NotImplemented + + return self.ca == other.ca and self.path_length == other.path_length + + def __hash__(self) -> int: + return hash((self.ca, self.path_length)) + + def public_bytes(self) -> bytes: + return rust_x509.encode_extension_value(self) + + +class DeltaCRLIndicator(ExtensionType): + oid = ExtensionOID.DELTA_CRL_INDICATOR + + def __init__(self, crl_number: int) -> None: + if not isinstance(crl_number, int): + raise TypeError("crl_number must be an integer") + + self._crl_number = crl_number + + @property + def crl_number(self) -> int: + return self._crl_number + + def __eq__(self, other: object) -> bool: + if not isinstance(other, DeltaCRLIndicator): + return NotImplemented + + return self.crl_number == other.crl_number + + def __hash__(self) -> int: + return hash(self.crl_number) + + def __repr__(self) -> str: + return f"" + + def public_bytes(self) -> bytes: + return rust_x509.encode_extension_value(self) + + +class CRLDistributionPoints(ExtensionType): + oid = ExtensionOID.CRL_DISTRIBUTION_POINTS + + def __init__( + self, distribution_points: typing.Iterable[DistributionPoint] + ) -> None: + distribution_points = list(distribution_points) + if not all( + isinstance(x, DistributionPoint) for x in distribution_points + ): + raise TypeError( + "distribution_points must be a list of DistributionPoint " + "objects" + ) + + self._distribution_points = distribution_points + + __len__, __iter__, __getitem__ = _make_sequence_methods( + "_distribution_points" + ) + + def __repr__(self) -> str: + return f"" + + def __eq__(self, other: object) -> bool: + if not isinstance(other, CRLDistributionPoints): + return NotImplemented + + return self._distribution_points == other._distribution_points + + def __hash__(self) -> int: + return hash(tuple(self._distribution_points)) + + def public_bytes(self) -> bytes: + return rust_x509.encode_extension_value(self) + + +class FreshestCRL(ExtensionType): + oid = ExtensionOID.FRESHEST_CRL + + def __init__( + self, distribution_points: typing.Iterable[DistributionPoint] + ) -> None: + distribution_points = list(distribution_points) + if not all( + isinstance(x, DistributionPoint) for x in distribution_points + ): + raise TypeError( + "distribution_points must be a list of DistributionPoint " + "objects" + ) + + self._distribution_points = distribution_points + + __len__, __iter__, __getitem__ = _make_sequence_methods( + "_distribution_points" + ) + + def __repr__(self) -> str: + return f"" + + def __eq__(self, other: object) -> bool: + if not isinstance(other, FreshestCRL): + return NotImplemented + + return self._distribution_points == other._distribution_points + + def __hash__(self) -> int: + return hash(tuple(self._distribution_points)) + + def public_bytes(self) -> bytes: + return rust_x509.encode_extension_value(self) + + +class DistributionPoint: + def __init__( + self, + full_name: typing.Iterable[GeneralName] | None, + relative_name: RelativeDistinguishedName | None, + reasons: frozenset[ReasonFlags] | None, + crl_issuer: typing.Iterable[GeneralName] | None, + ) -> None: + if full_name and relative_name: + raise ValueError( + "You cannot provide both full_name and relative_name, at " + "least one must be None." + ) + if not full_name and not relative_name and not crl_issuer: + raise ValueError( + "Either full_name, relative_name or crl_issuer must be " + "provided." + ) + + if full_name is not None: + full_name = list(full_name) + if not all(isinstance(x, GeneralName) for x in full_name): + raise TypeError( + "full_name must be a list of GeneralName objects" + ) + + if relative_name: + if not isinstance(relative_name, RelativeDistinguishedName): + raise TypeError( + "relative_name must be a RelativeDistinguishedName" + ) + + if crl_issuer is not None: + crl_issuer = list(crl_issuer) + if not all(isinstance(x, GeneralName) for x in crl_issuer): + raise TypeError( + "crl_issuer must be None or a list of general names" + ) + + if reasons and ( + not isinstance(reasons, frozenset) + or not all(isinstance(x, ReasonFlags) for x in reasons) + ): + raise TypeError("reasons must be None or frozenset of ReasonFlags") + + if reasons and ( + ReasonFlags.unspecified in reasons + or ReasonFlags.remove_from_crl in reasons + ): + raise ValueError( + "unspecified and remove_from_crl are not valid reasons in a " + "DistributionPoint" + ) + + self._full_name = full_name + self._relative_name = relative_name + self._reasons = reasons + self._crl_issuer = crl_issuer + + def __repr__(self) -> str: + return ( + "".format(self) + ) + + def __eq__(self, other: object) -> bool: + if not isinstance(other, DistributionPoint): + return NotImplemented + + return ( + self.full_name == other.full_name + and self.relative_name == other.relative_name + and self.reasons == other.reasons + and self.crl_issuer == other.crl_issuer + ) + + def __hash__(self) -> int: + if self.full_name is not None: + fn: tuple[GeneralName, ...] | None = tuple(self.full_name) + else: + fn = None + + if self.crl_issuer is not None: + crl_issuer: tuple[GeneralName, ...] | None = tuple(self.crl_issuer) + else: + crl_issuer = None + + return hash((fn, self.relative_name, self.reasons, crl_issuer)) + + @property + def full_name(self) -> list[GeneralName] | None: + return self._full_name + + @property + def relative_name(self) -> RelativeDistinguishedName | None: + return self._relative_name + + @property + def reasons(self) -> frozenset[ReasonFlags] | None: + return self._reasons + + @property + def crl_issuer(self) -> list[GeneralName] | None: + return self._crl_issuer + + +class ReasonFlags(utils.Enum): + unspecified = "unspecified" + key_compromise = "keyCompromise" + ca_compromise = "cACompromise" + affiliation_changed = "affiliationChanged" + superseded = "superseded" + cessation_of_operation = "cessationOfOperation" + certificate_hold = "certificateHold" + privilege_withdrawn = "privilegeWithdrawn" + aa_compromise = "aACompromise" + remove_from_crl = "removeFromCRL" + + +# These are distribution point bit string mappings. Not to be confused with +# CRLReason reason flags bit string mappings. +# ReasonFlags ::= BIT STRING { +# unused (0), +# keyCompromise (1), +# cACompromise (2), +# affiliationChanged (3), +# superseded (4), +# cessationOfOperation (5), +# certificateHold (6), +# privilegeWithdrawn (7), +# aACompromise (8) } +_REASON_BIT_MAPPING = { + 1: ReasonFlags.key_compromise, + 2: ReasonFlags.ca_compromise, + 3: ReasonFlags.affiliation_changed, + 4: ReasonFlags.superseded, + 5: ReasonFlags.cessation_of_operation, + 6: ReasonFlags.certificate_hold, + 7: ReasonFlags.privilege_withdrawn, + 8: ReasonFlags.aa_compromise, +} + +_CRLREASONFLAGS = { + ReasonFlags.key_compromise: 1, + ReasonFlags.ca_compromise: 2, + ReasonFlags.affiliation_changed: 3, + ReasonFlags.superseded: 4, + ReasonFlags.cessation_of_operation: 5, + ReasonFlags.certificate_hold: 6, + ReasonFlags.privilege_withdrawn: 7, + ReasonFlags.aa_compromise: 8, +} + +# CRLReason ::= ENUMERATED { +# unspecified (0), +# keyCompromise (1), +# cACompromise (2), +# affiliationChanged (3), +# superseded (4), +# cessationOfOperation (5), +# certificateHold (6), +# -- value 7 is not used +# removeFromCRL (8), +# privilegeWithdrawn (9), +# aACompromise (10) } +_CRL_ENTRY_REASON_ENUM_TO_CODE = { + ReasonFlags.unspecified: 0, + ReasonFlags.key_compromise: 1, + ReasonFlags.ca_compromise: 2, + ReasonFlags.affiliation_changed: 3, + ReasonFlags.superseded: 4, + ReasonFlags.cessation_of_operation: 5, + ReasonFlags.certificate_hold: 6, + ReasonFlags.remove_from_crl: 8, + ReasonFlags.privilege_withdrawn: 9, + ReasonFlags.aa_compromise: 10, +} + + +class PolicyConstraints(ExtensionType): + oid = ExtensionOID.POLICY_CONSTRAINTS + + def __init__( + self, + require_explicit_policy: int | None, + inhibit_policy_mapping: int | None, + ) -> None: + if require_explicit_policy is not None and not isinstance( + require_explicit_policy, int + ): + raise TypeError( + "require_explicit_policy must be a non-negative integer or " + "None" + ) + + if inhibit_policy_mapping is not None and not isinstance( + inhibit_policy_mapping, int + ): + raise TypeError( + "inhibit_policy_mapping must be a non-negative integer or None" + ) + + if inhibit_policy_mapping is None and require_explicit_policy is None: + raise ValueError( + "At least one of require_explicit_policy and " + "inhibit_policy_mapping must not be None" + ) + + self._require_explicit_policy = require_explicit_policy + self._inhibit_policy_mapping = inhibit_policy_mapping + + def __repr__(self) -> str: + return ( + "".format(self) + ) + + def __eq__(self, other: object) -> bool: + if not isinstance(other, PolicyConstraints): + return NotImplemented + + return ( + self.require_explicit_policy == other.require_explicit_policy + and self.inhibit_policy_mapping == other.inhibit_policy_mapping + ) + + def __hash__(self) -> int: + return hash( + (self.require_explicit_policy, self.inhibit_policy_mapping) + ) + + @property + def require_explicit_policy(self) -> int | None: + return self._require_explicit_policy + + @property + def inhibit_policy_mapping(self) -> int | None: + return self._inhibit_policy_mapping + + def public_bytes(self) -> bytes: + return rust_x509.encode_extension_value(self) + + +class CertificatePolicies(ExtensionType): + oid = ExtensionOID.CERTIFICATE_POLICIES + + def __init__(self, policies: typing.Iterable[PolicyInformation]) -> None: + policies = list(policies) + if not all(isinstance(x, PolicyInformation) for x in policies): + raise TypeError( + "Every item in the policies list must be a " + "PolicyInformation" + ) + + self._policies = policies + + __len__, __iter__, __getitem__ = _make_sequence_methods("_policies") + + def __repr__(self) -> str: + return f"" + + def __eq__(self, other: object) -> bool: + if not isinstance(other, CertificatePolicies): + return NotImplemented + + return self._policies == other._policies + + def __hash__(self) -> int: + return hash(tuple(self._policies)) + + def public_bytes(self) -> bytes: + return rust_x509.encode_extension_value(self) + + +class PolicyInformation: + def __init__( + self, + policy_identifier: ObjectIdentifier, + policy_qualifiers: typing.Iterable[str | UserNotice] | None, + ) -> None: + if not isinstance(policy_identifier, ObjectIdentifier): + raise TypeError("policy_identifier must be an ObjectIdentifier") + + self._policy_identifier = policy_identifier + + if policy_qualifiers is not None: + policy_qualifiers = list(policy_qualifiers) + if not all( + isinstance(x, (str, UserNotice)) for x in policy_qualifiers + ): + raise TypeError( + "policy_qualifiers must be a list of strings and/or " + "UserNotice objects or None" + ) + + self._policy_qualifiers = policy_qualifiers + + def __repr__(self) -> str: + return ( + f"" + ) + + def __eq__(self, other: object) -> bool: + if not isinstance(other, PolicyInformation): + return NotImplemented + + return ( + self.policy_identifier == other.policy_identifier + and self.policy_qualifiers == other.policy_qualifiers + ) + + def __hash__(self) -> int: + if self.policy_qualifiers is not None: + pq: tuple[str | UserNotice, ...] | None = tuple( + self.policy_qualifiers + ) + else: + pq = None + + return hash((self.policy_identifier, pq)) + + @property + def policy_identifier(self) -> ObjectIdentifier: + return self._policy_identifier + + @property + def policy_qualifiers( + self, + ) -> list[str | UserNotice] | None: + return self._policy_qualifiers + + +class UserNotice: + def __init__( + self, + notice_reference: NoticeReference | None, + explicit_text: str | None, + ) -> None: + if notice_reference and not isinstance( + notice_reference, NoticeReference + ): + raise TypeError( + "notice_reference must be None or a NoticeReference" + ) + + self._notice_reference = notice_reference + self._explicit_text = explicit_text + + def __repr__(self) -> str: + return ( + f"" + ) + + def __eq__(self, other: object) -> bool: + if not isinstance(other, UserNotice): + return NotImplemented + + return ( + self.notice_reference == other.notice_reference + and self.explicit_text == other.explicit_text + ) + + def __hash__(self) -> int: + return hash((self.notice_reference, self.explicit_text)) + + @property + def notice_reference(self) -> NoticeReference | None: + return self._notice_reference + + @property + def explicit_text(self) -> str | None: + return self._explicit_text + + +class NoticeReference: + def __init__( + self, + organization: str | None, + notice_numbers: typing.Iterable[int], + ) -> None: + self._organization = organization + notice_numbers = list(notice_numbers) + if not all(isinstance(x, int) for x in notice_numbers): + raise TypeError("notice_numbers must be a list of integers") + + self._notice_numbers = notice_numbers + + def __repr__(self) -> str: + return ( + f"" + ) + + def __eq__(self, other: object) -> bool: + if not isinstance(other, NoticeReference): + return NotImplemented + + return ( + self.organization == other.organization + and self.notice_numbers == other.notice_numbers + ) + + def __hash__(self) -> int: + return hash((self.organization, tuple(self.notice_numbers))) + + @property + def organization(self) -> str | None: + return self._organization + + @property + def notice_numbers(self) -> list[int]: + return self._notice_numbers + + +class ExtendedKeyUsage(ExtensionType): + oid = ExtensionOID.EXTENDED_KEY_USAGE + + def __init__(self, usages: typing.Iterable[ObjectIdentifier]) -> None: + usages = list(usages) + if not all(isinstance(x, ObjectIdentifier) for x in usages): + raise TypeError( + "Every item in the usages list must be an ObjectIdentifier" + ) + + self._usages = usages + + __len__, __iter__, __getitem__ = _make_sequence_methods("_usages") + + def __repr__(self) -> str: + return f"" + + def __eq__(self, other: object) -> bool: + if not isinstance(other, ExtendedKeyUsage): + return NotImplemented + + return self._usages == other._usages + + def __hash__(self) -> int: + return hash(tuple(self._usages)) + + def public_bytes(self) -> bytes: + return rust_x509.encode_extension_value(self) + + +class OCSPNoCheck(ExtensionType): + oid = ExtensionOID.OCSP_NO_CHECK + + def __eq__(self, other: object) -> bool: + if not isinstance(other, OCSPNoCheck): + return NotImplemented + + return True + + def __hash__(self) -> int: + return hash(OCSPNoCheck) + + def __repr__(self) -> str: + return "" + + def public_bytes(self) -> bytes: + return rust_x509.encode_extension_value(self) + + +class PrecertPoison(ExtensionType): + oid = ExtensionOID.PRECERT_POISON + + def __eq__(self, other: object) -> bool: + if not isinstance(other, PrecertPoison): + return NotImplemented + + return True + + def __hash__(self) -> int: + return hash(PrecertPoison) + + def __repr__(self) -> str: + return "" + + def public_bytes(self) -> bytes: + return rust_x509.encode_extension_value(self) + + +class TLSFeature(ExtensionType): + oid = ExtensionOID.TLS_FEATURE + + def __init__(self, features: typing.Iterable[TLSFeatureType]) -> None: + features = list(features) + if ( + not all(isinstance(x, TLSFeatureType) for x in features) + or len(features) == 0 + ): + raise TypeError( + "features must be a list of elements from the TLSFeatureType " + "enum" + ) + + self._features = features + + __len__, __iter__, __getitem__ = _make_sequence_methods("_features") + + def __repr__(self) -> str: + return f"" + + def __eq__(self, other: object) -> bool: + if not isinstance(other, TLSFeature): + return NotImplemented + + return self._features == other._features + + def __hash__(self) -> int: + return hash(tuple(self._features)) + + def public_bytes(self) -> bytes: + return rust_x509.encode_extension_value(self) + + +class TLSFeatureType(utils.Enum): + # status_request is defined in RFC 6066 and is used for what is commonly + # called OCSP Must-Staple when present in the TLS Feature extension in an + # X.509 certificate. + status_request = 5 + # status_request_v2 is defined in RFC 6961 and allows multiple OCSP + # responses to be provided. It is not currently in use by clients or + # servers. + status_request_v2 = 17 + + +_TLS_FEATURE_TYPE_TO_ENUM = {x.value: x for x in TLSFeatureType} + + +class InhibitAnyPolicy(ExtensionType): + oid = ExtensionOID.INHIBIT_ANY_POLICY + + def __init__(self, skip_certs: int) -> None: + if not isinstance(skip_certs, int): + raise TypeError("skip_certs must be an integer") + + if skip_certs < 0: + raise ValueError("skip_certs must be a non-negative integer") + + self._skip_certs = skip_certs + + def __repr__(self) -> str: + return f"" + + def __eq__(self, other: object) -> bool: + if not isinstance(other, InhibitAnyPolicy): + return NotImplemented + + return self.skip_certs == other.skip_certs + + def __hash__(self) -> int: + return hash(self.skip_certs) + + @property + def skip_certs(self) -> int: + return self._skip_certs + + def public_bytes(self) -> bytes: + return rust_x509.encode_extension_value(self) + + +class KeyUsage(ExtensionType): + oid = ExtensionOID.KEY_USAGE + + def __init__( + self, + digital_signature: bool, + content_commitment: bool, + key_encipherment: bool, + data_encipherment: bool, + key_agreement: bool, + key_cert_sign: bool, + crl_sign: bool, + encipher_only: bool, + decipher_only: bool, + ) -> None: + if not key_agreement and (encipher_only or decipher_only): + raise ValueError( + "encipher_only and decipher_only can only be true when " + "key_agreement is true" + ) + + self._digital_signature = digital_signature + self._content_commitment = content_commitment + self._key_encipherment = key_encipherment + self._data_encipherment = data_encipherment + self._key_agreement = key_agreement + self._key_cert_sign = key_cert_sign + self._crl_sign = crl_sign + self._encipher_only = encipher_only + self._decipher_only = decipher_only + + @property + def digital_signature(self) -> bool: + return self._digital_signature + + @property + def content_commitment(self) -> bool: + return self._content_commitment + + @property + def key_encipherment(self) -> bool: + return self._key_encipherment + + @property + def data_encipherment(self) -> bool: + return self._data_encipherment + + @property + def key_agreement(self) -> bool: + return self._key_agreement + + @property + def key_cert_sign(self) -> bool: + return self._key_cert_sign + + @property + def crl_sign(self) -> bool: + return self._crl_sign + + @property + def encipher_only(self) -> bool: + if not self.key_agreement: + raise ValueError( + "encipher_only is undefined unless key_agreement is true" + ) + else: + return self._encipher_only + + @property + def decipher_only(self) -> bool: + if not self.key_agreement: + raise ValueError( + "decipher_only is undefined unless key_agreement is true" + ) + else: + return self._decipher_only + + def __repr__(self) -> str: + try: + encipher_only = self.encipher_only + decipher_only = self.decipher_only + except ValueError: + # Users found None confusing because even though encipher/decipher + # have no meaning unless key_agreement is true, to construct an + # instance of the class you still need to pass False. + encipher_only = False + decipher_only = False + + return ( + f"" + ) + + def __eq__(self, other: object) -> bool: + if not isinstance(other, KeyUsage): + return NotImplemented + + return ( + self.digital_signature == other.digital_signature + and self.content_commitment == other.content_commitment + and self.key_encipherment == other.key_encipherment + and self.data_encipherment == other.data_encipherment + and self.key_agreement == other.key_agreement + and self.key_cert_sign == other.key_cert_sign + and self.crl_sign == other.crl_sign + and self._encipher_only == other._encipher_only + and self._decipher_only == other._decipher_only + ) + + def __hash__(self) -> int: + return hash( + ( + self.digital_signature, + self.content_commitment, + self.key_encipherment, + self.data_encipherment, + self.key_agreement, + self.key_cert_sign, + self.crl_sign, + self._encipher_only, + self._decipher_only, + ) + ) + + def public_bytes(self) -> bytes: + return rust_x509.encode_extension_value(self) + + +class NameConstraints(ExtensionType): + oid = ExtensionOID.NAME_CONSTRAINTS + + def __init__( + self, + permitted_subtrees: typing.Iterable[GeneralName] | None, + excluded_subtrees: typing.Iterable[GeneralName] | None, + ) -> None: + if permitted_subtrees is not None: + permitted_subtrees = list(permitted_subtrees) + if not permitted_subtrees: + raise ValueError( + "permitted_subtrees must be a non-empty list or None" + ) + if not all(isinstance(x, GeneralName) for x in permitted_subtrees): + raise TypeError( + "permitted_subtrees must be a list of GeneralName objects " + "or None" + ) + + self._validate_tree(permitted_subtrees) + + if excluded_subtrees is not None: + excluded_subtrees = list(excluded_subtrees) + if not excluded_subtrees: + raise ValueError( + "excluded_subtrees must be a non-empty list or None" + ) + if not all(isinstance(x, GeneralName) for x in excluded_subtrees): + raise TypeError( + "excluded_subtrees must be a list of GeneralName objects " + "or None" + ) + + self._validate_tree(excluded_subtrees) + + if permitted_subtrees is None and excluded_subtrees is None: + raise ValueError( + "At least one of permitted_subtrees and excluded_subtrees " + "must not be None" + ) + + self._permitted_subtrees = permitted_subtrees + self._excluded_subtrees = excluded_subtrees + + def __eq__(self, other: object) -> bool: + if not isinstance(other, NameConstraints): + return NotImplemented + + return ( + self.excluded_subtrees == other.excluded_subtrees + and self.permitted_subtrees == other.permitted_subtrees + ) + + def _validate_tree(self, tree: typing.Iterable[GeneralName]) -> None: + self._validate_ip_name(tree) + self._validate_dns_name(tree) + + def _validate_ip_name(self, tree: typing.Iterable[GeneralName]) -> None: + if any( + isinstance(name, IPAddress) + and not isinstance( + name.value, (ipaddress.IPv4Network, ipaddress.IPv6Network) + ) + for name in tree + ): + raise TypeError( + "IPAddress name constraints must be an IPv4Network or" + " IPv6Network object" + ) + + def _validate_dns_name(self, tree: typing.Iterable[GeneralName]) -> None: + if any( + isinstance(name, DNSName) and "*" in name.value for name in tree + ): + raise ValueError( + "DNSName name constraints must not contain the '*' wildcard" + " character" + ) + + def __repr__(self) -> str: + return ( + f"" + ) + + def __hash__(self) -> int: + if self.permitted_subtrees is not None: + ps: tuple[GeneralName, ...] | None = tuple(self.permitted_subtrees) + else: + ps = None + + if self.excluded_subtrees is not None: + es: tuple[GeneralName, ...] | None = tuple(self.excluded_subtrees) + else: + es = None + + return hash((ps, es)) + + @property + def permitted_subtrees( + self, + ) -> list[GeneralName] | None: + return self._permitted_subtrees + + @property + def excluded_subtrees( + self, + ) -> list[GeneralName] | None: + return self._excluded_subtrees + + def public_bytes(self) -> bytes: + return rust_x509.encode_extension_value(self) + + +class Extension(typing.Generic[ExtensionTypeVar]): + def __init__( + self, oid: ObjectIdentifier, critical: bool, value: ExtensionTypeVar + ) -> None: + if not isinstance(oid, ObjectIdentifier): + raise TypeError( + "oid argument must be an ObjectIdentifier instance." + ) + + if not isinstance(critical, bool): + raise TypeError("critical must be a boolean value") + + self._oid = oid + self._critical = critical + self._value = value + + @property + def oid(self) -> ObjectIdentifier: + return self._oid + + @property + def critical(self) -> bool: + return self._critical + + @property + def value(self) -> ExtensionTypeVar: + return self._value + + def __repr__(self) -> str: + return ( + f"" + ) + + def __eq__(self, other: object) -> bool: + if not isinstance(other, Extension): + return NotImplemented + + return ( + self.oid == other.oid + and self.critical == other.critical + and self.value == other.value + ) + + def __hash__(self) -> int: + return hash((self.oid, self.critical, self.value)) + + +class GeneralNames: + def __init__(self, general_names: typing.Iterable[GeneralName]) -> None: + general_names = list(general_names) + if not all(isinstance(x, GeneralName) for x in general_names): + raise TypeError( + "Every item in the general_names list must be an " + "object conforming to the GeneralName interface" + ) + + self._general_names = general_names + + __len__, __iter__, __getitem__ = _make_sequence_methods("_general_names") + + @typing.overload + def get_values_for_type( + self, + type: type[DNSName] + | type[UniformResourceIdentifier] + | type[RFC822Name], + ) -> list[str]: ... + + @typing.overload + def get_values_for_type( + self, + type: type[DirectoryName], + ) -> list[Name]: ... + + @typing.overload + def get_values_for_type( + self, + type: type[RegisteredID], + ) -> list[ObjectIdentifier]: ... + + @typing.overload + def get_values_for_type( + self, type: type[IPAddress] + ) -> list[_IPAddressTypes]: ... + + @typing.overload + def get_values_for_type( + self, type: type[OtherName] + ) -> list[OtherName]: ... + + def get_values_for_type( + self, + type: type[DNSName] + | type[DirectoryName] + | type[IPAddress] + | type[OtherName] + | type[RFC822Name] + | type[RegisteredID] + | type[UniformResourceIdentifier], + ) -> ( + list[_IPAddressTypes] + | list[str] + | list[OtherName] + | list[Name] + | list[ObjectIdentifier] + ): + # Return the value of each GeneralName, except for OtherName instances + # which we return directly because it has two important properties not + # just one value. + objs = (i for i in self if isinstance(i, type)) + if type != OtherName: + return [i.value for i in objs] + return list(objs) + + def __repr__(self) -> str: + return f"" + + def __eq__(self, other: object) -> bool: + if not isinstance(other, GeneralNames): + return NotImplemented + + return self._general_names == other._general_names + + def __hash__(self) -> int: + return hash(tuple(self._general_names)) + + +class SubjectAlternativeName(ExtensionType): + oid = ExtensionOID.SUBJECT_ALTERNATIVE_NAME + + def __init__(self, general_names: typing.Iterable[GeneralName]) -> None: + self._general_names = GeneralNames(general_names) + + __len__, __iter__, __getitem__ = _make_sequence_methods("_general_names") + + @typing.overload + def get_values_for_type( + self, + type: type[DNSName] + | type[UniformResourceIdentifier] + | type[RFC822Name], + ) -> list[str]: ... + + @typing.overload + def get_values_for_type( + self, + type: type[DirectoryName], + ) -> list[Name]: ... + + @typing.overload + def get_values_for_type( + self, + type: type[RegisteredID], + ) -> list[ObjectIdentifier]: ... + + @typing.overload + def get_values_for_type( + self, type: type[IPAddress] + ) -> list[_IPAddressTypes]: ... + + @typing.overload + def get_values_for_type( + self, type: type[OtherName] + ) -> list[OtherName]: ... + + def get_values_for_type( + self, + type: type[DNSName] + | type[DirectoryName] + | type[IPAddress] + | type[OtherName] + | type[RFC822Name] + | type[RegisteredID] + | type[UniformResourceIdentifier], + ) -> ( + list[_IPAddressTypes] + | list[str] + | list[OtherName] + | list[Name] + | list[ObjectIdentifier] + ): + return self._general_names.get_values_for_type(type) + + def __repr__(self) -> str: + return f"" + + def __eq__(self, other: object) -> bool: + if not isinstance(other, SubjectAlternativeName): + return NotImplemented + + return self._general_names == other._general_names + + def __hash__(self) -> int: + return hash(self._general_names) + + def public_bytes(self) -> bytes: + return rust_x509.encode_extension_value(self) + + +class IssuerAlternativeName(ExtensionType): + oid = ExtensionOID.ISSUER_ALTERNATIVE_NAME + + def __init__(self, general_names: typing.Iterable[GeneralName]) -> None: + self._general_names = GeneralNames(general_names) + + __len__, __iter__, __getitem__ = _make_sequence_methods("_general_names") + + @typing.overload + def get_values_for_type( + self, + type: type[DNSName] + | type[UniformResourceIdentifier] + | type[RFC822Name], + ) -> list[str]: ... + + @typing.overload + def get_values_for_type( + self, + type: type[DirectoryName], + ) -> list[Name]: ... + + @typing.overload + def get_values_for_type( + self, + type: type[RegisteredID], + ) -> list[ObjectIdentifier]: ... + + @typing.overload + def get_values_for_type( + self, type: type[IPAddress] + ) -> list[_IPAddressTypes]: ... + + @typing.overload + def get_values_for_type( + self, type: type[OtherName] + ) -> list[OtherName]: ... + + def get_values_for_type( + self, + type: type[DNSName] + | type[DirectoryName] + | type[IPAddress] + | type[OtherName] + | type[RFC822Name] + | type[RegisteredID] + | type[UniformResourceIdentifier], + ) -> ( + list[_IPAddressTypes] + | list[str] + | list[OtherName] + | list[Name] + | list[ObjectIdentifier] + ): + return self._general_names.get_values_for_type(type) + + def __repr__(self) -> str: + return f"" + + def __eq__(self, other: object) -> bool: + if not isinstance(other, IssuerAlternativeName): + return NotImplemented + + return self._general_names == other._general_names + + def __hash__(self) -> int: + return hash(self._general_names) + + def public_bytes(self) -> bytes: + return rust_x509.encode_extension_value(self) + + +class CertificateIssuer(ExtensionType): + oid = CRLEntryExtensionOID.CERTIFICATE_ISSUER + + def __init__(self, general_names: typing.Iterable[GeneralName]) -> None: + self._general_names = GeneralNames(general_names) + + __len__, __iter__, __getitem__ = _make_sequence_methods("_general_names") + + @typing.overload + def get_values_for_type( + self, + type: type[DNSName] + | type[UniformResourceIdentifier] + | type[RFC822Name], + ) -> list[str]: ... + + @typing.overload + def get_values_for_type( + self, + type: type[DirectoryName], + ) -> list[Name]: ... + + @typing.overload + def get_values_for_type( + self, + type: type[RegisteredID], + ) -> list[ObjectIdentifier]: ... + + @typing.overload + def get_values_for_type( + self, type: type[IPAddress] + ) -> list[_IPAddressTypes]: ... + + @typing.overload + def get_values_for_type( + self, type: type[OtherName] + ) -> list[OtherName]: ... + + def get_values_for_type( + self, + type: type[DNSName] + | type[DirectoryName] + | type[IPAddress] + | type[OtherName] + | type[RFC822Name] + | type[RegisteredID] + | type[UniformResourceIdentifier], + ) -> ( + list[_IPAddressTypes] + | list[str] + | list[OtherName] + | list[Name] + | list[ObjectIdentifier] + ): + return self._general_names.get_values_for_type(type) + + def __repr__(self) -> str: + return f"" + + def __eq__(self, other: object) -> bool: + if not isinstance(other, CertificateIssuer): + return NotImplemented + + return self._general_names == other._general_names + + def __hash__(self) -> int: + return hash(self._general_names) + + def public_bytes(self) -> bytes: + return rust_x509.encode_extension_value(self) + + +class CRLReason(ExtensionType): + oid = CRLEntryExtensionOID.CRL_REASON + + def __init__(self, reason: ReasonFlags) -> None: + if not isinstance(reason, ReasonFlags): + raise TypeError("reason must be an element from ReasonFlags") + + self._reason = reason + + def __repr__(self) -> str: + return f"" + + def __eq__(self, other: object) -> bool: + if not isinstance(other, CRLReason): + return NotImplemented + + return self.reason == other.reason + + def __hash__(self) -> int: + return hash(self.reason) + + @property + def reason(self) -> ReasonFlags: + return self._reason + + def public_bytes(self) -> bytes: + return rust_x509.encode_extension_value(self) + + +class InvalidityDate(ExtensionType): + oid = CRLEntryExtensionOID.INVALIDITY_DATE + + def __init__(self, invalidity_date: datetime.datetime) -> None: + if not isinstance(invalidity_date, datetime.datetime): + raise TypeError("invalidity_date must be a datetime.datetime") + + self._invalidity_date = invalidity_date + + def __repr__(self) -> str: + return f"" + + def __eq__(self, other: object) -> bool: + if not isinstance(other, InvalidityDate): + return NotImplemented + + return self.invalidity_date == other.invalidity_date + + def __hash__(self) -> int: + return hash(self.invalidity_date) + + @property + def invalidity_date(self) -> datetime.datetime: + return self._invalidity_date + + @property + def invalidity_date_utc(self) -> datetime.datetime: + if self._invalidity_date.tzinfo is None: + return self._invalidity_date.replace(tzinfo=datetime.timezone.utc) + else: + return self._invalidity_date.astimezone(tz=datetime.timezone.utc) + + def public_bytes(self) -> bytes: + return rust_x509.encode_extension_value(self) + + +class PrecertificateSignedCertificateTimestamps(ExtensionType): + oid = ExtensionOID.PRECERT_SIGNED_CERTIFICATE_TIMESTAMPS + + def __init__( + self, + signed_certificate_timestamps: typing.Iterable[ + SignedCertificateTimestamp + ], + ) -> None: + signed_certificate_timestamps = list(signed_certificate_timestamps) + if not all( + isinstance(sct, SignedCertificateTimestamp) + for sct in signed_certificate_timestamps + ): + raise TypeError( + "Every item in the signed_certificate_timestamps list must be " + "a SignedCertificateTimestamp" + ) + self._signed_certificate_timestamps = signed_certificate_timestamps + + __len__, __iter__, __getitem__ = _make_sequence_methods( + "_signed_certificate_timestamps" + ) + + def __repr__(self) -> str: + return f"" + + def __hash__(self) -> int: + return hash(tuple(self._signed_certificate_timestamps)) + + def __eq__(self, other: object) -> bool: + if not isinstance(other, PrecertificateSignedCertificateTimestamps): + return NotImplemented + + return ( + self._signed_certificate_timestamps + == other._signed_certificate_timestamps + ) + + def public_bytes(self) -> bytes: + return rust_x509.encode_extension_value(self) + + +class SignedCertificateTimestamps(ExtensionType): + oid = ExtensionOID.SIGNED_CERTIFICATE_TIMESTAMPS + + def __init__( + self, + signed_certificate_timestamps: typing.Iterable[ + SignedCertificateTimestamp + ], + ) -> None: + signed_certificate_timestamps = list(signed_certificate_timestamps) + if not all( + isinstance(sct, SignedCertificateTimestamp) + for sct in signed_certificate_timestamps + ): + raise TypeError( + "Every item in the signed_certificate_timestamps list must be " + "a SignedCertificateTimestamp" + ) + self._signed_certificate_timestamps = signed_certificate_timestamps + + __len__, __iter__, __getitem__ = _make_sequence_methods( + "_signed_certificate_timestamps" + ) + + def __repr__(self) -> str: + return f"" + + def __hash__(self) -> int: + return hash(tuple(self._signed_certificate_timestamps)) + + def __eq__(self, other: object) -> bool: + if not isinstance(other, SignedCertificateTimestamps): + return NotImplemented + + return ( + self._signed_certificate_timestamps + == other._signed_certificate_timestamps + ) + + def public_bytes(self) -> bytes: + return rust_x509.encode_extension_value(self) + + +class OCSPNonce(ExtensionType): + oid = OCSPExtensionOID.NONCE + + def __init__(self, nonce: bytes) -> None: + if not isinstance(nonce, bytes): + raise TypeError("nonce must be bytes") + + self._nonce = nonce + + def __eq__(self, other: object) -> bool: + if not isinstance(other, OCSPNonce): + return NotImplemented + + return self.nonce == other.nonce + + def __hash__(self) -> int: + return hash(self.nonce) + + def __repr__(self) -> str: + return f"" + + @property + def nonce(self) -> bytes: + return self._nonce + + def public_bytes(self) -> bytes: + return rust_x509.encode_extension_value(self) + + +class OCSPAcceptableResponses(ExtensionType): + oid = OCSPExtensionOID.ACCEPTABLE_RESPONSES + + def __init__(self, responses: typing.Iterable[ObjectIdentifier]) -> None: + responses = list(responses) + if any(not isinstance(r, ObjectIdentifier) for r in responses): + raise TypeError("All responses must be ObjectIdentifiers") + + self._responses = responses + + def __eq__(self, other: object) -> bool: + if not isinstance(other, OCSPAcceptableResponses): + return NotImplemented + + return self._responses == other._responses + + def __hash__(self) -> int: + return hash(tuple(self._responses)) + + def __repr__(self) -> str: + return f"" + + def __iter__(self) -> typing.Iterator[ObjectIdentifier]: + return iter(self._responses) + + def public_bytes(self) -> bytes: + return rust_x509.encode_extension_value(self) + + +class IssuingDistributionPoint(ExtensionType): + oid = ExtensionOID.ISSUING_DISTRIBUTION_POINT + + def __init__( + self, + full_name: typing.Iterable[GeneralName] | None, + relative_name: RelativeDistinguishedName | None, + only_contains_user_certs: bool, + only_contains_ca_certs: bool, + only_some_reasons: frozenset[ReasonFlags] | None, + indirect_crl: bool, + only_contains_attribute_certs: bool, + ) -> None: + if full_name is not None: + full_name = list(full_name) + + if only_some_reasons and ( + not isinstance(only_some_reasons, frozenset) + or not all(isinstance(x, ReasonFlags) for x in only_some_reasons) + ): + raise TypeError( + "only_some_reasons must be None or frozenset of ReasonFlags" + ) + + if only_some_reasons and ( + ReasonFlags.unspecified in only_some_reasons + or ReasonFlags.remove_from_crl in only_some_reasons + ): + raise ValueError( + "unspecified and remove_from_crl are not valid reasons in an " + "IssuingDistributionPoint" + ) + + if not ( + isinstance(only_contains_user_certs, bool) + and isinstance(only_contains_ca_certs, bool) + and isinstance(indirect_crl, bool) + and isinstance(only_contains_attribute_certs, bool) + ): + raise TypeError( + "only_contains_user_certs, only_contains_ca_certs, " + "indirect_crl and only_contains_attribute_certs " + "must all be boolean." + ) + + crl_constraints = [ + only_contains_user_certs, + only_contains_ca_certs, + indirect_crl, + only_contains_attribute_certs, + ] + + if len([x for x in crl_constraints if x]) > 1: + raise ValueError( + "Only one of the following can be set to True: " + "only_contains_user_certs, only_contains_ca_certs, " + "indirect_crl, only_contains_attribute_certs" + ) + + if not any( + [ + only_contains_user_certs, + only_contains_ca_certs, + indirect_crl, + only_contains_attribute_certs, + full_name, + relative_name, + only_some_reasons, + ] + ): + raise ValueError( + "Cannot create empty extension: " + "if only_contains_user_certs, only_contains_ca_certs, " + "indirect_crl, and only_contains_attribute_certs are all False" + ", then either full_name, relative_name, or only_some_reasons " + "must have a value." + ) + + self._only_contains_user_certs = only_contains_user_certs + self._only_contains_ca_certs = only_contains_ca_certs + self._indirect_crl = indirect_crl + self._only_contains_attribute_certs = only_contains_attribute_certs + self._only_some_reasons = only_some_reasons + self._full_name = full_name + self._relative_name = relative_name + + def __repr__(self) -> str: + return ( + f"" + ) + + def __eq__(self, other: object) -> bool: + if not isinstance(other, IssuingDistributionPoint): + return NotImplemented + + return ( + self.full_name == other.full_name + and self.relative_name == other.relative_name + and self.only_contains_user_certs == other.only_contains_user_certs + and self.only_contains_ca_certs == other.only_contains_ca_certs + and self.only_some_reasons == other.only_some_reasons + and self.indirect_crl == other.indirect_crl + and self.only_contains_attribute_certs + == other.only_contains_attribute_certs + ) + + def __hash__(self) -> int: + return hash( + ( + self.full_name, + self.relative_name, + self.only_contains_user_certs, + self.only_contains_ca_certs, + self.only_some_reasons, + self.indirect_crl, + self.only_contains_attribute_certs, + ) + ) + + @property + def full_name(self) -> list[GeneralName] | None: + return self._full_name + + @property + def relative_name(self) -> RelativeDistinguishedName | None: + return self._relative_name + + @property + def only_contains_user_certs(self) -> bool: + return self._only_contains_user_certs + + @property + def only_contains_ca_certs(self) -> bool: + return self._only_contains_ca_certs + + @property + def only_some_reasons( + self, + ) -> frozenset[ReasonFlags] | None: + return self._only_some_reasons + + @property + def indirect_crl(self) -> bool: + return self._indirect_crl + + @property + def only_contains_attribute_certs(self) -> bool: + return self._only_contains_attribute_certs + + def public_bytes(self) -> bytes: + return rust_x509.encode_extension_value(self) + + +class MSCertificateTemplate(ExtensionType): + oid = ExtensionOID.MS_CERTIFICATE_TEMPLATE + + def __init__( + self, + template_id: ObjectIdentifier, + major_version: int | None, + minor_version: int | None, + ) -> None: + if not isinstance(template_id, ObjectIdentifier): + raise TypeError("oid must be an ObjectIdentifier") + self._template_id = template_id + if ( + major_version is not None and not isinstance(major_version, int) + ) or ( + minor_version is not None and not isinstance(minor_version, int) + ): + raise TypeError( + "major_version and minor_version must be integers or None" + ) + self._major_version = major_version + self._minor_version = minor_version + + @property + def template_id(self) -> ObjectIdentifier: + return self._template_id + + @property + def major_version(self) -> int | None: + return self._major_version + + @property + def minor_version(self) -> int | None: + return self._minor_version + + def __repr__(self) -> str: + return ( + f"" + ) + + def __eq__(self, other: object) -> bool: + if not isinstance(other, MSCertificateTemplate): + return NotImplemented + + return ( + self.template_id == other.template_id + and self.major_version == other.major_version + and self.minor_version == other.minor_version + ) + + def __hash__(self) -> int: + return hash((self.template_id, self.major_version, self.minor_version)) + + def public_bytes(self) -> bytes: + return rust_x509.encode_extension_value(self) + + +class UnrecognizedExtension(ExtensionType): + def __init__(self, oid: ObjectIdentifier, value: bytes) -> None: + if not isinstance(oid, ObjectIdentifier): + raise TypeError("oid must be an ObjectIdentifier") + self._oid = oid + self._value = value + + @property + def oid(self) -> ObjectIdentifier: # type: ignore[override] + return self._oid + + @property + def value(self) -> bytes: + return self._value + + def __repr__(self) -> str: + return ( + f"" + ) + + def __eq__(self, other: object) -> bool: + if not isinstance(other, UnrecognizedExtension): + return NotImplemented + + return self.oid == other.oid and self.value == other.value + + def __hash__(self) -> int: + return hash((self.oid, self.value)) + + def public_bytes(self) -> bytes: + return self.value diff --git a/code/.venv/lib/python3.12/site-packages/cryptography/x509/general_name.py b/code/.venv/lib/python3.12/site-packages/cryptography/x509/general_name.py new file mode 100644 index 0000000..672f287 --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/cryptography/x509/general_name.py @@ -0,0 +1,281 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +from __future__ import annotations + +import abc +import ipaddress +import typing +from email.utils import parseaddr + +from cryptography.x509.name import Name +from cryptography.x509.oid import ObjectIdentifier + +_IPAddressTypes = typing.Union[ + ipaddress.IPv4Address, + ipaddress.IPv6Address, + ipaddress.IPv4Network, + ipaddress.IPv6Network, +] + + +class UnsupportedGeneralNameType(Exception): + pass + + +class GeneralName(metaclass=abc.ABCMeta): + @property + @abc.abstractmethod + def value(self) -> typing.Any: + """ + Return the value of the object + """ + + +class RFC822Name(GeneralName): + def __init__(self, value: str) -> None: + if isinstance(value, str): + try: + value.encode("ascii") + except UnicodeEncodeError: + raise ValueError( + "RFC822Name values should be passed as an A-label string. " + "This means unicode characters should be encoded via " + "a library like idna." + ) + else: + raise TypeError("value must be string") + + name, address = parseaddr(value) + if name or not address: + # parseaddr has found a name (e.g. Name ) or the entire + # value is an empty string. + raise ValueError("Invalid rfc822name value") + + self._value = value + + @property + def value(self) -> str: + return self._value + + @classmethod + def _init_without_validation(cls, value: str) -> RFC822Name: + instance = cls.__new__(cls) + instance._value = value + return instance + + def __repr__(self) -> str: + return f"" + + def __eq__(self, other: object) -> bool: + if not isinstance(other, RFC822Name): + return NotImplemented + + return self.value == other.value + + def __hash__(self) -> int: + return hash(self.value) + + +class DNSName(GeneralName): + def __init__(self, value: str) -> None: + if isinstance(value, str): + try: + value.encode("ascii") + except UnicodeEncodeError: + raise ValueError( + "DNSName values should be passed as an A-label string. " + "This means unicode characters should be encoded via " + "a library like idna." + ) + else: + raise TypeError("value must be string") + + self._value = value + + @property + def value(self) -> str: + return self._value + + @classmethod + def _init_without_validation(cls, value: str) -> DNSName: + instance = cls.__new__(cls) + instance._value = value + return instance + + def __repr__(self) -> str: + return f"" + + def __eq__(self, other: object) -> bool: + if not isinstance(other, DNSName): + return NotImplemented + + return self.value == other.value + + def __hash__(self) -> int: + return hash(self.value) + + +class UniformResourceIdentifier(GeneralName): + def __init__(self, value: str) -> None: + if isinstance(value, str): + try: + value.encode("ascii") + except UnicodeEncodeError: + raise ValueError( + "URI values should be passed as an A-label string. " + "This means unicode characters should be encoded via " + "a library like idna." + ) + else: + raise TypeError("value must be string") + + self._value = value + + @property + def value(self) -> str: + return self._value + + @classmethod + def _init_without_validation(cls, value: str) -> UniformResourceIdentifier: + instance = cls.__new__(cls) + instance._value = value + return instance + + def __repr__(self) -> str: + return f"" + + def __eq__(self, other: object) -> bool: + if not isinstance(other, UniformResourceIdentifier): + return NotImplemented + + return self.value == other.value + + def __hash__(self) -> int: + return hash(self.value) + + +class DirectoryName(GeneralName): + def __init__(self, value: Name) -> None: + if not isinstance(value, Name): + raise TypeError("value must be a Name") + + self._value = value + + @property + def value(self) -> Name: + return self._value + + def __repr__(self) -> str: + return f"" + + def __eq__(self, other: object) -> bool: + if not isinstance(other, DirectoryName): + return NotImplemented + + return self.value == other.value + + def __hash__(self) -> int: + return hash(self.value) + + +class RegisteredID(GeneralName): + def __init__(self, value: ObjectIdentifier) -> None: + if not isinstance(value, ObjectIdentifier): + raise TypeError("value must be an ObjectIdentifier") + + self._value = value + + @property + def value(self) -> ObjectIdentifier: + return self._value + + def __repr__(self) -> str: + return f"" + + def __eq__(self, other: object) -> bool: + if not isinstance(other, RegisteredID): + return NotImplemented + + return self.value == other.value + + def __hash__(self) -> int: + return hash(self.value) + + +class IPAddress(GeneralName): + def __init__(self, value: _IPAddressTypes) -> None: + if not isinstance( + value, + ( + ipaddress.IPv4Address, + ipaddress.IPv6Address, + ipaddress.IPv4Network, + ipaddress.IPv6Network, + ), + ): + raise TypeError( + "value must be an instance of ipaddress.IPv4Address, " + "ipaddress.IPv6Address, ipaddress.IPv4Network, or " + "ipaddress.IPv6Network" + ) + + self._value = value + + @property + def value(self) -> _IPAddressTypes: + return self._value + + def _packed(self) -> bytes: + if isinstance( + self.value, (ipaddress.IPv4Address, ipaddress.IPv6Address) + ): + return self.value.packed + else: + return ( + self.value.network_address.packed + self.value.netmask.packed + ) + + def __repr__(self) -> str: + return f"" + + def __eq__(self, other: object) -> bool: + if not isinstance(other, IPAddress): + return NotImplemented + + return self.value == other.value + + def __hash__(self) -> int: + return hash(self.value) + + +class OtherName(GeneralName): + def __init__(self, type_id: ObjectIdentifier, value: bytes) -> None: + if not isinstance(type_id, ObjectIdentifier): + raise TypeError("type_id must be an ObjectIdentifier") + if not isinstance(value, bytes): + raise TypeError("value must be a binary string") + + self._type_id = type_id + self._value = value + + @property + def type_id(self) -> ObjectIdentifier: + return self._type_id + + @property + def value(self) -> bytes: + return self._value + + def __repr__(self) -> str: + return f"" + + def __eq__(self, other: object) -> bool: + if not isinstance(other, OtherName): + return NotImplemented + + return self.type_id == other.type_id and self.value == other.value + + def __hash__(self) -> int: + return hash((self.type_id, self.value)) diff --git a/code/.venv/lib/python3.12/site-packages/cryptography/x509/name.py b/code/.venv/lib/python3.12/site-packages/cryptography/x509/name.py new file mode 100644 index 0000000..1b6b89d --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/cryptography/x509/name.py @@ -0,0 +1,465 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +from __future__ import annotations + +import binascii +import re +import sys +import typing +import warnings + +from cryptography import utils +from cryptography.hazmat.bindings._rust import x509 as rust_x509 +from cryptography.x509.oid import NameOID, ObjectIdentifier + + +class _ASN1Type(utils.Enum): + BitString = 3 + OctetString = 4 + UTF8String = 12 + NumericString = 18 + PrintableString = 19 + T61String = 20 + IA5String = 22 + UTCTime = 23 + GeneralizedTime = 24 + VisibleString = 26 + UniversalString = 28 + BMPString = 30 + + +_ASN1_TYPE_TO_ENUM = {i.value: i for i in _ASN1Type} +_NAMEOID_DEFAULT_TYPE: dict[ObjectIdentifier, _ASN1Type] = { + NameOID.COUNTRY_NAME: _ASN1Type.PrintableString, + NameOID.JURISDICTION_COUNTRY_NAME: _ASN1Type.PrintableString, + NameOID.SERIAL_NUMBER: _ASN1Type.PrintableString, + NameOID.DN_QUALIFIER: _ASN1Type.PrintableString, + NameOID.EMAIL_ADDRESS: _ASN1Type.IA5String, + NameOID.DOMAIN_COMPONENT: _ASN1Type.IA5String, +} + +# Type alias +_OidNameMap = typing.Mapping[ObjectIdentifier, str] +_NameOidMap = typing.Mapping[str, ObjectIdentifier] + +#: Short attribute names from RFC 4514: +#: https://tools.ietf.org/html/rfc4514#page-7 +_NAMEOID_TO_NAME: _OidNameMap = { + NameOID.COMMON_NAME: "CN", + NameOID.LOCALITY_NAME: "L", + NameOID.STATE_OR_PROVINCE_NAME: "ST", + NameOID.ORGANIZATION_NAME: "O", + NameOID.ORGANIZATIONAL_UNIT_NAME: "OU", + NameOID.COUNTRY_NAME: "C", + NameOID.STREET_ADDRESS: "STREET", + NameOID.DOMAIN_COMPONENT: "DC", + NameOID.USER_ID: "UID", +} +_NAME_TO_NAMEOID = {v: k for k, v in _NAMEOID_TO_NAME.items()} + +_NAMEOID_LENGTH_LIMIT = { + NameOID.COUNTRY_NAME: (2, 2), + NameOID.JURISDICTION_COUNTRY_NAME: (2, 2), + NameOID.COMMON_NAME: (1, 64), +} + + +def _escape_dn_value(val: str | bytes) -> str: + """Escape special characters in RFC4514 Distinguished Name value.""" + + if not val: + return "" + + # RFC 4514 Section 2.4 defines the value as being the # (U+0023) character + # followed by the hexadecimal encoding of the octets. + if isinstance(val, bytes): + return "#" + binascii.hexlify(val).decode("utf8") + + # See https://tools.ietf.org/html/rfc4514#section-2.4 + val = val.replace("\\", "\\\\") + val = val.replace('"', '\\"') + val = val.replace("+", "\\+") + val = val.replace(",", "\\,") + val = val.replace(";", "\\;") + val = val.replace("<", "\\<") + val = val.replace(">", "\\>") + val = val.replace("\0", "\\00") + + if val[0] in ("#", " "): + val = "\\" + val + if val[-1] == " ": + val = val[:-1] + "\\ " + + return val + + +def _unescape_dn_value(val: str) -> str: + if not val: + return "" + + # See https://tools.ietf.org/html/rfc4514#section-3 + + # special = escaped / SPACE / SHARP / EQUALS + # escaped = DQUOTE / PLUS / COMMA / SEMI / LANGLE / RANGLE + def sub(m): + val = m.group(1) + # Regular escape + if len(val) == 1: + return val + # Hex-value scape + return chr(int(val, 16)) + + return _RFC4514NameParser._PAIR_RE.sub(sub, val) + + +class NameAttribute: + def __init__( + self, + oid: ObjectIdentifier, + value: str | bytes, + _type: _ASN1Type | None = None, + *, + _validate: bool = True, + ) -> None: + if not isinstance(oid, ObjectIdentifier): + raise TypeError( + "oid argument must be an ObjectIdentifier instance." + ) + if _type == _ASN1Type.BitString: + if oid != NameOID.X500_UNIQUE_IDENTIFIER: + raise TypeError( + "oid must be X500_UNIQUE_IDENTIFIER for BitString type." + ) + if not isinstance(value, bytes): + raise TypeError("value must be bytes for BitString") + else: + if not isinstance(value, str): + raise TypeError("value argument must be a str") + + length_limits = _NAMEOID_LENGTH_LIMIT.get(oid) + if length_limits is not None: + min_length, max_length = length_limits + assert isinstance(value, str) + c_len = len(value.encode("utf8")) + if c_len < min_length or c_len > max_length: + msg = ( + f"Attribute's length must be >= {min_length} and " + f"<= {max_length}, but it was {c_len}" + ) + if _validate is True: + raise ValueError(msg) + else: + warnings.warn(msg, stacklevel=2) + + # The appropriate ASN1 string type varies by OID and is defined across + # multiple RFCs including 2459, 3280, and 5280. In general UTF8String + # is preferred (2459), but 3280 and 5280 specify several OIDs with + # alternate types. This means when we see the sentinel value we need + # to look up whether the OID has a non-UTF8 type. If it does, set it + # to that. Otherwise, UTF8! + if _type is None: + _type = _NAMEOID_DEFAULT_TYPE.get(oid, _ASN1Type.UTF8String) + + if not isinstance(_type, _ASN1Type): + raise TypeError("_type must be from the _ASN1Type enum") + + self._oid = oid + self._value = value + self._type = _type + + @property + def oid(self) -> ObjectIdentifier: + return self._oid + + @property + def value(self) -> str | bytes: + return self._value + + @property + def rfc4514_attribute_name(self) -> str: + """ + The short attribute name (for example "CN") if available, + otherwise the OID dotted string. + """ + return _NAMEOID_TO_NAME.get(self.oid, self.oid.dotted_string) + + def rfc4514_string( + self, attr_name_overrides: _OidNameMap | None = None + ) -> str: + """ + Format as RFC4514 Distinguished Name string. + + Use short attribute name if available, otherwise fall back to OID + dotted string. + """ + attr_name = ( + attr_name_overrides.get(self.oid) if attr_name_overrides else None + ) + if attr_name is None: + attr_name = self.rfc4514_attribute_name + + return f"{attr_name}={_escape_dn_value(self.value)}" + + def __eq__(self, other: object) -> bool: + if not isinstance(other, NameAttribute): + return NotImplemented + + return self.oid == other.oid and self.value == other.value + + def __hash__(self) -> int: + return hash((self.oid, self.value)) + + def __repr__(self) -> str: + return f"" + + +class RelativeDistinguishedName: + def __init__(self, attributes: typing.Iterable[NameAttribute]): + attributes = list(attributes) + if not attributes: + raise ValueError("a relative distinguished name cannot be empty") + if not all(isinstance(x, NameAttribute) for x in attributes): + raise TypeError("attributes must be an iterable of NameAttribute") + + # Keep list and frozenset to preserve attribute order where it matters + self._attributes = attributes + self._attribute_set = frozenset(attributes) + + if len(self._attribute_set) != len(attributes): + raise ValueError("duplicate attributes are not allowed") + + def get_attributes_for_oid( + self, oid: ObjectIdentifier + ) -> list[NameAttribute]: + return [i for i in self if i.oid == oid] + + def rfc4514_string( + self, attr_name_overrides: _OidNameMap | None = None + ) -> str: + """ + Format as RFC4514 Distinguished Name string. + + Within each RDN, attributes are joined by '+', although that is rarely + used in certificates. + """ + return "+".join( + attr.rfc4514_string(attr_name_overrides) + for attr in self._attributes + ) + + def __eq__(self, other: object) -> bool: + if not isinstance(other, RelativeDistinguishedName): + return NotImplemented + + return self._attribute_set == other._attribute_set + + def __hash__(self) -> int: + return hash(self._attribute_set) + + def __iter__(self) -> typing.Iterator[NameAttribute]: + return iter(self._attributes) + + def __len__(self) -> int: + return len(self._attributes) + + def __repr__(self) -> str: + return f"" + + +class Name: + @typing.overload + def __init__(self, attributes: typing.Iterable[NameAttribute]) -> None: ... + + @typing.overload + def __init__( + self, attributes: typing.Iterable[RelativeDistinguishedName] + ) -> None: ... + + def __init__( + self, + attributes: typing.Iterable[NameAttribute | RelativeDistinguishedName], + ) -> None: + attributes = list(attributes) + if all(isinstance(x, NameAttribute) for x in attributes): + self._attributes = [ + RelativeDistinguishedName([typing.cast(NameAttribute, x)]) + for x in attributes + ] + elif all(isinstance(x, RelativeDistinguishedName) for x in attributes): + self._attributes = typing.cast( + typing.List[RelativeDistinguishedName], attributes + ) + else: + raise TypeError( + "attributes must be a list of NameAttribute" + " or a list RelativeDistinguishedName" + ) + + @classmethod + def from_rfc4514_string( + cls, + data: str, + attr_name_overrides: _NameOidMap | None = None, + ) -> Name: + return _RFC4514NameParser(data, attr_name_overrides or {}).parse() + + def rfc4514_string( + self, attr_name_overrides: _OidNameMap | None = None + ) -> str: + """ + Format as RFC4514 Distinguished Name string. + For example 'CN=foobar.com,O=Foo Corp,C=US' + + An X.509 name is a two-level structure: a list of sets of attributes. + Each list element is separated by ',' and within each list element, set + elements are separated by '+'. The latter is almost never used in + real world certificates. According to RFC4514 section 2.1 the + RDNSequence must be reversed when converting to string representation. + """ + return ",".join( + attr.rfc4514_string(attr_name_overrides) + for attr in reversed(self._attributes) + ) + + def get_attributes_for_oid( + self, oid: ObjectIdentifier + ) -> list[NameAttribute]: + return [i for i in self if i.oid == oid] + + @property + def rdns(self) -> list[RelativeDistinguishedName]: + return self._attributes + + def public_bytes(self, backend: typing.Any = None) -> bytes: + return rust_x509.encode_name_bytes(self) + + def __eq__(self, other: object) -> bool: + if not isinstance(other, Name): + return NotImplemented + + return self._attributes == other._attributes + + def __hash__(self) -> int: + # TODO: this is relatively expensive, if this looks like a bottleneck + # for you, consider optimizing! + return hash(tuple(self._attributes)) + + def __iter__(self) -> typing.Iterator[NameAttribute]: + for rdn in self._attributes: + yield from rdn + + def __len__(self) -> int: + return sum(len(rdn) for rdn in self._attributes) + + def __repr__(self) -> str: + rdns = ",".join(attr.rfc4514_string() for attr in self._attributes) + return f"" + + +class _RFC4514NameParser: + _OID_RE = re.compile(r"(0|([1-9]\d*))(\.(0|([1-9]\d*)))+") + _DESCR_RE = re.compile(r"[a-zA-Z][a-zA-Z\d-]*") + + _PAIR = r"\\([\\ #=\"\+,;<>]|[\da-zA-Z]{2})" + _PAIR_RE = re.compile(_PAIR) + _LUTF1 = r"[\x01-\x1f\x21\x24-\x2A\x2D-\x3A\x3D\x3F-\x5B\x5D-\x7F]" + _SUTF1 = r"[\x01-\x21\x23-\x2A\x2D-\x3A\x3D\x3F-\x5B\x5D-\x7F]" + _TUTF1 = r"[\x01-\x1F\x21\x23-\x2A\x2D-\x3A\x3D\x3F-\x5B\x5D-\x7F]" + _UTFMB = rf"[\x80-{chr(sys.maxunicode)}]" + _LEADCHAR = rf"{_LUTF1}|{_UTFMB}" + _STRINGCHAR = rf"{_SUTF1}|{_UTFMB}" + _TRAILCHAR = rf"{_TUTF1}|{_UTFMB}" + _STRING_RE = re.compile( + rf""" + ( + ({_LEADCHAR}|{_PAIR}) + ( + ({_STRINGCHAR}|{_PAIR})* + ({_TRAILCHAR}|{_PAIR}) + )? + )? + """, + re.VERBOSE, + ) + _HEXSTRING_RE = re.compile(r"#([\da-zA-Z]{2})+") + + def __init__(self, data: str, attr_name_overrides: _NameOidMap) -> None: + self._data = data + self._idx = 0 + + self._attr_name_overrides = attr_name_overrides + + def _has_data(self) -> bool: + return self._idx < len(self._data) + + def _peek(self) -> str | None: + if self._has_data(): + return self._data[self._idx] + return None + + def _read_char(self, ch: str) -> None: + if self._peek() != ch: + raise ValueError + self._idx += 1 + + def _read_re(self, pat) -> str: + match = pat.match(self._data, pos=self._idx) + if match is None: + raise ValueError + val = match.group() + self._idx += len(val) + return val + + def parse(self) -> Name: + """ + Parses the `data` string and converts it to a Name. + + According to RFC4514 section 2.1 the RDNSequence must be + reversed when converting to string representation. So, when + we parse it, we need to reverse again to get the RDNs on the + correct order. + """ + + if not self._has_data(): + return Name([]) + + rdns = [self._parse_rdn()] + + while self._has_data(): + self._read_char(",") + rdns.append(self._parse_rdn()) + + return Name(reversed(rdns)) + + def _parse_rdn(self) -> RelativeDistinguishedName: + nas = [self._parse_na()] + while self._peek() == "+": + self._read_char("+") + nas.append(self._parse_na()) + + return RelativeDistinguishedName(nas) + + def _parse_na(self) -> NameAttribute: + try: + oid_value = self._read_re(self._OID_RE) + except ValueError: + name = self._read_re(self._DESCR_RE) + oid = self._attr_name_overrides.get( + name, _NAME_TO_NAMEOID.get(name) + ) + if oid is None: + raise ValueError + else: + oid = ObjectIdentifier(oid_value) + + self._read_char("=") + if self._peek() == "#": + value = self._read_re(self._HEXSTRING_RE) + value = binascii.unhexlify(value[1:]).decode() + else: + raw_value = self._read_re(self._STRING_RE) + value = _unescape_dn_value(raw_value) + + return NameAttribute(oid, value) diff --git a/code/.venv/lib/python3.12/site-packages/cryptography/x509/ocsp.py b/code/.venv/lib/python3.12/site-packages/cryptography/x509/ocsp.py new file mode 100644 index 0000000..dbb475d --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/cryptography/x509/ocsp.py @@ -0,0 +1,678 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +from __future__ import annotations + +import abc +import datetime +import typing + +from cryptography import utils, x509 +from cryptography.hazmat.bindings._rust import ocsp +from cryptography.hazmat.primitives import hashes, serialization +from cryptography.hazmat.primitives.asymmetric.types import ( + CertificateIssuerPrivateKeyTypes, +) +from cryptography.x509.base import ( + _EARLIEST_UTC_TIME, + _convert_to_naive_utc_time, + _reject_duplicate_extension, +) + + +class OCSPResponderEncoding(utils.Enum): + HASH = "By Hash" + NAME = "By Name" + + +class OCSPResponseStatus(utils.Enum): + SUCCESSFUL = 0 + MALFORMED_REQUEST = 1 + INTERNAL_ERROR = 2 + TRY_LATER = 3 + SIG_REQUIRED = 5 + UNAUTHORIZED = 6 + + +_ALLOWED_HASHES = ( + hashes.SHA1, + hashes.SHA224, + hashes.SHA256, + hashes.SHA384, + hashes.SHA512, +) + + +def _verify_algorithm(algorithm: hashes.HashAlgorithm) -> None: + if not isinstance(algorithm, _ALLOWED_HASHES): + raise ValueError( + "Algorithm must be SHA1, SHA224, SHA256, SHA384, or SHA512" + ) + + +class OCSPCertStatus(utils.Enum): + GOOD = 0 + REVOKED = 1 + UNKNOWN = 2 + + +class _SingleResponse: + def __init__( + self, + cert: x509.Certificate, + issuer: x509.Certificate, + algorithm: hashes.HashAlgorithm, + cert_status: OCSPCertStatus, + this_update: datetime.datetime, + next_update: datetime.datetime | None, + revocation_time: datetime.datetime | None, + revocation_reason: x509.ReasonFlags | None, + ): + if not isinstance(cert, x509.Certificate) or not isinstance( + issuer, x509.Certificate + ): + raise TypeError("cert and issuer must be a Certificate") + + _verify_algorithm(algorithm) + if not isinstance(this_update, datetime.datetime): + raise TypeError("this_update must be a datetime object") + if next_update is not None and not isinstance( + next_update, datetime.datetime + ): + raise TypeError("next_update must be a datetime object or None") + + self._cert = cert + self._issuer = issuer + self._algorithm = algorithm + self._this_update = this_update + self._next_update = next_update + + if not isinstance(cert_status, OCSPCertStatus): + raise TypeError( + "cert_status must be an item from the OCSPCertStatus enum" + ) + if cert_status is not OCSPCertStatus.REVOKED: + if revocation_time is not None: + raise ValueError( + "revocation_time can only be provided if the certificate " + "is revoked" + ) + if revocation_reason is not None: + raise ValueError( + "revocation_reason can only be provided if the certificate" + " is revoked" + ) + else: + if not isinstance(revocation_time, datetime.datetime): + raise TypeError("revocation_time must be a datetime object") + + revocation_time = _convert_to_naive_utc_time(revocation_time) + if revocation_time < _EARLIEST_UTC_TIME: + raise ValueError( + "The revocation_time must be on or after" + " 1950 January 1." + ) + + if revocation_reason is not None and not isinstance( + revocation_reason, x509.ReasonFlags + ): + raise TypeError( + "revocation_reason must be an item from the ReasonFlags " + "enum or None" + ) + + self._cert_status = cert_status + self._revocation_time = revocation_time + self._revocation_reason = revocation_reason + + +class OCSPRequest(metaclass=abc.ABCMeta): + @property + @abc.abstractmethod + def issuer_key_hash(self) -> bytes: + """ + The hash of the issuer public key + """ + + @property + @abc.abstractmethod + def issuer_name_hash(self) -> bytes: + """ + The hash of the issuer name + """ + + @property + @abc.abstractmethod + def hash_algorithm(self) -> hashes.HashAlgorithm: + """ + The hash algorithm used in the issuer name and key hashes + """ + + @property + @abc.abstractmethod + def serial_number(self) -> int: + """ + The serial number of the cert whose status is being checked + """ + + @abc.abstractmethod + def public_bytes(self, encoding: serialization.Encoding) -> bytes: + """ + Serializes the request to DER + """ + + @property + @abc.abstractmethod + def extensions(self) -> x509.Extensions: + """ + The list of request extensions. Not single request extensions. + """ + + +class OCSPSingleResponse(metaclass=abc.ABCMeta): + @property + @abc.abstractmethod + def certificate_status(self) -> OCSPCertStatus: + """ + The status of the certificate (an element from the OCSPCertStatus enum) + """ + + @property + @abc.abstractmethod + def revocation_time(self) -> datetime.datetime | None: + """ + The date of when the certificate was revoked or None if not + revoked. + """ + + @property + @abc.abstractmethod + def revocation_time_utc(self) -> datetime.datetime | None: + """ + The date of when the certificate was revoked or None if not + revoked. Represented as a non-naive UTC datetime. + """ + + @property + @abc.abstractmethod + def revocation_reason(self) -> x509.ReasonFlags | None: + """ + The reason the certificate was revoked or None if not specified or + not revoked. + """ + + @property + @abc.abstractmethod + def this_update(self) -> datetime.datetime: + """ + The most recent time at which the status being indicated is known by + the responder to have been correct + """ + + @property + @abc.abstractmethod + def this_update_utc(self) -> datetime.datetime: + """ + The most recent time at which the status being indicated is known by + the responder to have been correct. Represented as a non-naive UTC + datetime. + """ + + @property + @abc.abstractmethod + def next_update(self) -> datetime.datetime | None: + """ + The time when newer information will be available + """ + + @property + @abc.abstractmethod + def next_update_utc(self) -> datetime.datetime | None: + """ + The time when newer information will be available. Represented as a + non-naive UTC datetime. + """ + + @property + @abc.abstractmethod + def issuer_key_hash(self) -> bytes: + """ + The hash of the issuer public key + """ + + @property + @abc.abstractmethod + def issuer_name_hash(self) -> bytes: + """ + The hash of the issuer name + """ + + @property + @abc.abstractmethod + def hash_algorithm(self) -> hashes.HashAlgorithm: + """ + The hash algorithm used in the issuer name and key hashes + """ + + @property + @abc.abstractmethod + def serial_number(self) -> int: + """ + The serial number of the cert whose status is being checked + """ + + +class OCSPResponse(metaclass=abc.ABCMeta): + @property + @abc.abstractmethod + def responses(self) -> typing.Iterator[OCSPSingleResponse]: + """ + An iterator over the individual SINGLERESP structures in the + response + """ + + @property + @abc.abstractmethod + def response_status(self) -> OCSPResponseStatus: + """ + The status of the response. This is a value from the OCSPResponseStatus + enumeration + """ + + @property + @abc.abstractmethod + def signature_algorithm_oid(self) -> x509.ObjectIdentifier: + """ + The ObjectIdentifier of the signature algorithm + """ + + @property + @abc.abstractmethod + def signature_hash_algorithm( + self, + ) -> hashes.HashAlgorithm | None: + """ + Returns a HashAlgorithm corresponding to the type of the digest signed + """ + + @property + @abc.abstractmethod + def signature(self) -> bytes: + """ + The signature bytes + """ + + @property + @abc.abstractmethod + def tbs_response_bytes(self) -> bytes: + """ + The tbsResponseData bytes + """ + + @property + @abc.abstractmethod + def certificates(self) -> list[x509.Certificate]: + """ + A list of certificates used to help build a chain to verify the OCSP + response. This situation occurs when the OCSP responder uses a delegate + certificate. + """ + + @property + @abc.abstractmethod + def responder_key_hash(self) -> bytes | None: + """ + The responder's key hash or None + """ + + @property + @abc.abstractmethod + def responder_name(self) -> x509.Name | None: + """ + The responder's Name or None + """ + + @property + @abc.abstractmethod + def produced_at(self) -> datetime.datetime: + """ + The time the response was produced + """ + + @property + @abc.abstractmethod + def produced_at_utc(self) -> datetime.datetime: + """ + The time the response was produced. Represented as a non-naive UTC + datetime. + """ + + @property + @abc.abstractmethod + def certificate_status(self) -> OCSPCertStatus: + """ + The status of the certificate (an element from the OCSPCertStatus enum) + """ + + @property + @abc.abstractmethod + def revocation_time(self) -> datetime.datetime | None: + """ + The date of when the certificate was revoked or None if not + revoked. + """ + + @property + @abc.abstractmethod + def revocation_time_utc(self) -> datetime.datetime | None: + """ + The date of when the certificate was revoked or None if not + revoked. Represented as a non-naive UTC datetime. + """ + + @property + @abc.abstractmethod + def revocation_reason(self) -> x509.ReasonFlags | None: + """ + The reason the certificate was revoked or None if not specified or + not revoked. + """ + + @property + @abc.abstractmethod + def this_update(self) -> datetime.datetime: + """ + The most recent time at which the status being indicated is known by + the responder to have been correct + """ + + @property + @abc.abstractmethod + def this_update_utc(self) -> datetime.datetime: + """ + The most recent time at which the status being indicated is known by + the responder to have been correct. Represented as a non-naive UTC + datetime. + """ + + @property + @abc.abstractmethod + def next_update(self) -> datetime.datetime | None: + """ + The time when newer information will be available + """ + + @property + @abc.abstractmethod + def next_update_utc(self) -> datetime.datetime | None: + """ + The time when newer information will be available. Represented as a + non-naive UTC datetime. + """ + + @property + @abc.abstractmethod + def issuer_key_hash(self) -> bytes: + """ + The hash of the issuer public key + """ + + @property + @abc.abstractmethod + def issuer_name_hash(self) -> bytes: + """ + The hash of the issuer name + """ + + @property + @abc.abstractmethod + def hash_algorithm(self) -> hashes.HashAlgorithm: + """ + The hash algorithm used in the issuer name and key hashes + """ + + @property + @abc.abstractmethod + def serial_number(self) -> int: + """ + The serial number of the cert whose status is being checked + """ + + @property + @abc.abstractmethod + def extensions(self) -> x509.Extensions: + """ + The list of response extensions. Not single response extensions. + """ + + @property + @abc.abstractmethod + def single_extensions(self) -> x509.Extensions: + """ + The list of single response extensions. Not response extensions. + """ + + @abc.abstractmethod + def public_bytes(self, encoding: serialization.Encoding) -> bytes: + """ + Serializes the response to DER + """ + + +OCSPRequest.register(ocsp.OCSPRequest) +OCSPResponse.register(ocsp.OCSPResponse) +OCSPSingleResponse.register(ocsp.OCSPSingleResponse) + + +class OCSPRequestBuilder: + def __init__( + self, + request: tuple[ + x509.Certificate, x509.Certificate, hashes.HashAlgorithm + ] + | None = None, + request_hash: tuple[bytes, bytes, int, hashes.HashAlgorithm] + | None = None, + extensions: list[x509.Extension[x509.ExtensionType]] = [], + ) -> None: + self._request = request + self._request_hash = request_hash + self._extensions = extensions + + def add_certificate( + self, + cert: x509.Certificate, + issuer: x509.Certificate, + algorithm: hashes.HashAlgorithm, + ) -> OCSPRequestBuilder: + if self._request is not None or self._request_hash is not None: + raise ValueError("Only one certificate can be added to a request") + + _verify_algorithm(algorithm) + if not isinstance(cert, x509.Certificate) or not isinstance( + issuer, x509.Certificate + ): + raise TypeError("cert and issuer must be a Certificate") + + return OCSPRequestBuilder( + (cert, issuer, algorithm), self._request_hash, self._extensions + ) + + def add_certificate_by_hash( + self, + issuer_name_hash: bytes, + issuer_key_hash: bytes, + serial_number: int, + algorithm: hashes.HashAlgorithm, + ) -> OCSPRequestBuilder: + if self._request is not None or self._request_hash is not None: + raise ValueError("Only one certificate can be added to a request") + + if not isinstance(serial_number, int): + raise TypeError("serial_number must be an integer") + + _verify_algorithm(algorithm) + utils._check_bytes("issuer_name_hash", issuer_name_hash) + utils._check_bytes("issuer_key_hash", issuer_key_hash) + if algorithm.digest_size != len( + issuer_name_hash + ) or algorithm.digest_size != len(issuer_key_hash): + raise ValueError( + "issuer_name_hash and issuer_key_hash must be the same length " + "as the digest size of the algorithm" + ) + + return OCSPRequestBuilder( + self._request, + (issuer_name_hash, issuer_key_hash, serial_number, algorithm), + self._extensions, + ) + + def add_extension( + self, extval: x509.ExtensionType, critical: bool + ) -> OCSPRequestBuilder: + if not isinstance(extval, x509.ExtensionType): + raise TypeError("extension must be an ExtensionType") + + extension = x509.Extension(extval.oid, critical, extval) + _reject_duplicate_extension(extension, self._extensions) + + return OCSPRequestBuilder( + self._request, self._request_hash, [*self._extensions, extension] + ) + + def build(self) -> OCSPRequest: + if self._request is None and self._request_hash is None: + raise ValueError("You must add a certificate before building") + + return ocsp.create_ocsp_request(self) + + +class OCSPResponseBuilder: + def __init__( + self, + response: _SingleResponse | None = None, + responder_id: tuple[x509.Certificate, OCSPResponderEncoding] + | None = None, + certs: list[x509.Certificate] | None = None, + extensions: list[x509.Extension[x509.ExtensionType]] = [], + ): + self._response = response + self._responder_id = responder_id + self._certs = certs + self._extensions = extensions + + def add_response( + self, + cert: x509.Certificate, + issuer: x509.Certificate, + algorithm: hashes.HashAlgorithm, + cert_status: OCSPCertStatus, + this_update: datetime.datetime, + next_update: datetime.datetime | None, + revocation_time: datetime.datetime | None, + revocation_reason: x509.ReasonFlags | None, + ) -> OCSPResponseBuilder: + if self._response is not None: + raise ValueError("Only one response per OCSPResponse.") + + singleresp = _SingleResponse( + cert, + issuer, + algorithm, + cert_status, + this_update, + next_update, + revocation_time, + revocation_reason, + ) + return OCSPResponseBuilder( + singleresp, + self._responder_id, + self._certs, + self._extensions, + ) + + def responder_id( + self, encoding: OCSPResponderEncoding, responder_cert: x509.Certificate + ) -> OCSPResponseBuilder: + if self._responder_id is not None: + raise ValueError("responder_id can only be set once") + if not isinstance(responder_cert, x509.Certificate): + raise TypeError("responder_cert must be a Certificate") + if not isinstance(encoding, OCSPResponderEncoding): + raise TypeError( + "encoding must be an element from OCSPResponderEncoding" + ) + + return OCSPResponseBuilder( + self._response, + (responder_cert, encoding), + self._certs, + self._extensions, + ) + + def certificates( + self, certs: typing.Iterable[x509.Certificate] + ) -> OCSPResponseBuilder: + if self._certs is not None: + raise ValueError("certificates may only be set once") + certs = list(certs) + if len(certs) == 0: + raise ValueError("certs must not be an empty list") + if not all(isinstance(x, x509.Certificate) for x in certs): + raise TypeError("certs must be a list of Certificates") + return OCSPResponseBuilder( + self._response, + self._responder_id, + certs, + self._extensions, + ) + + def add_extension( + self, extval: x509.ExtensionType, critical: bool + ) -> OCSPResponseBuilder: + if not isinstance(extval, x509.ExtensionType): + raise TypeError("extension must be an ExtensionType") + + extension = x509.Extension(extval.oid, critical, extval) + _reject_duplicate_extension(extension, self._extensions) + + return OCSPResponseBuilder( + self._response, + self._responder_id, + self._certs, + [*self._extensions, extension], + ) + + def sign( + self, + private_key: CertificateIssuerPrivateKeyTypes, + algorithm: hashes.HashAlgorithm | None, + ) -> OCSPResponse: + if self._response is None: + raise ValueError("You must add a response before signing") + if self._responder_id is None: + raise ValueError("You must add a responder_id before signing") + + return ocsp.create_ocsp_response( + OCSPResponseStatus.SUCCESSFUL, self, private_key, algorithm + ) + + @classmethod + def build_unsuccessful( + cls, response_status: OCSPResponseStatus + ) -> OCSPResponse: + if not isinstance(response_status, OCSPResponseStatus): + raise TypeError( + "response_status must be an item from OCSPResponseStatus" + ) + if response_status is OCSPResponseStatus.SUCCESSFUL: + raise ValueError("response_status cannot be SUCCESSFUL") + + return ocsp.create_ocsp_response(response_status, None, None, None) + + +load_der_ocsp_request = ocsp.load_der_ocsp_request +load_der_ocsp_response = ocsp.load_der_ocsp_response diff --git a/code/.venv/lib/python3.12/site-packages/cryptography/x509/oid.py b/code/.venv/lib/python3.12/site-packages/cryptography/x509/oid.py new file mode 100644 index 0000000..d4e409e --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/cryptography/x509/oid.py @@ -0,0 +1,35 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +from __future__ import annotations + +from cryptography.hazmat._oid import ( + AttributeOID, + AuthorityInformationAccessOID, + CertificatePoliciesOID, + CRLEntryExtensionOID, + ExtendedKeyUsageOID, + ExtensionOID, + NameOID, + ObjectIdentifier, + OCSPExtensionOID, + PublicKeyAlgorithmOID, + SignatureAlgorithmOID, + SubjectInformationAccessOID, +) + +__all__ = [ + "AttributeOID", + "AuthorityInformationAccessOID", + "CRLEntryExtensionOID", + "CertificatePoliciesOID", + "ExtendedKeyUsageOID", + "ExtensionOID", + "NameOID", + "OCSPExtensionOID", + "ObjectIdentifier", + "PublicKeyAlgorithmOID", + "SignatureAlgorithmOID", + "SubjectInformationAccessOID", +] diff --git a/code/.venv/lib/python3.12/site-packages/cryptography/x509/verification.py b/code/.venv/lib/python3.12/site-packages/cryptography/x509/verification.py new file mode 100644 index 0000000..b836506 --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/cryptography/x509/verification.py @@ -0,0 +1,28 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +from __future__ import annotations + +import typing + +from cryptography.hazmat.bindings._rust import x509 as rust_x509 +from cryptography.x509.general_name import DNSName, IPAddress + +__all__ = [ + "ClientVerifier", + "PolicyBuilder", + "ServerVerifier", + "Store", + "Subject", + "VerificationError", + "VerifiedClient", +] + +Store = rust_x509.Store +Subject = typing.Union[DNSName, IPAddress] +VerifiedClient = rust_x509.VerifiedClient +ClientVerifier = rust_x509.ClientVerifier +ServerVerifier = rust_x509.ServerVerifier +PolicyBuilder = rust_x509.PolicyBuilder +VerificationError = rust_x509.VerificationError diff --git a/code/.venv/lib/python3.12/site-packages/ecdsa-0.19.0.dist-info/INSTALLER b/code/.venv/lib/python3.12/site-packages/ecdsa-0.19.0.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/ecdsa-0.19.0.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/code/.venv/lib/python3.12/site-packages/ecdsa-0.19.0.dist-info/LICENSE b/code/.venv/lib/python3.12/site-packages/ecdsa-0.19.0.dist-info/LICENSE new file mode 100644 index 0000000..474479a --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/ecdsa-0.19.0.dist-info/LICENSE @@ -0,0 +1,24 @@ +"python-ecdsa" Copyright (c) 2010 Brian Warner + +Portions written in 2005 by Peter Pearson and placed in the public domain. + +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation +files (the "Software"), to deal in the Software without +restriction, including without limitation the rights to use, +copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. diff --git a/code/.venv/lib/python3.12/site-packages/ecdsa-0.19.0.dist-info/METADATA b/code/.venv/lib/python3.12/site-packages/ecdsa-0.19.0.dist-info/METADATA new file mode 100644 index 0000000..b9fd800 --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/ecdsa-0.19.0.dist-info/METADATA @@ -0,0 +1,671 @@ +Metadata-Version: 2.1 +Name: ecdsa +Version: 0.19.0 +Summary: ECDSA cryptographic signature library (pure python) +Home-page: http://github.com/tlsfuzzer/python-ecdsa +Author: Brian Warner +Author-email: warner@lothar.com +License: MIT +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 2 +Classifier: Programming Language :: Python :: 2.6 +Classifier: Programming Language :: Python :: 2.7 +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3.5 +Classifier: Programming Language :: Python :: 3.6 +Classifier: Programming Language :: Python :: 3.7 +Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: 3.9 +Classifier: Programming Language :: Python :: 3.10 +Classifier: Programming Language :: Python :: 3.11 +Classifier: Programming Language :: Python :: 3.12 +Requires-Python: >=2.6, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.* +Description-Content-Type: text/markdown +License-File: LICENSE +Requires-Dist: six (>=1.9.0) +Provides-Extra: gmpy +Requires-Dist: gmpy ; extra == 'gmpy' +Provides-Extra: gmpy2 +Requires-Dist: gmpy2 ; extra == 'gmpy2' + +# Pure-Python ECDSA and ECDH + +[![Build Status](https://github.com/tlsfuzzer/python-ecdsa/workflows/GitHub%20CI/badge.svg?branch=master)](https://github.com/tlsfuzzer/python-ecdsa/actions?query=workflow%3A%22GitHub+CI%22+branch%3Amaster) +[![Documentation Status](https://readthedocs.org/projects/ecdsa/badge/?version=latest)](https://ecdsa.readthedocs.io/en/latest/?badge=latest) +[![Coverage Status](https://coveralls.io/repos/github/tlsfuzzer/python-ecdsa/badge.svg?branch=master)](https://coveralls.io/github/tlsfuzzer/python-ecdsa?branch=master) +![condition coverage](https://img.shields.io/endpoint?url=https://gist.githubusercontent.com/tomato42/9b6ca1f3410207fbeca785a178781651/raw/python-ecdsa-condition-coverage.json) +![mutation score](https://img.shields.io/endpoint?url=https://gist.githubusercontent.com/tomato42/9b6ca1f3410207fbeca785a178781651/raw/python-ecdsa-mutation-score.json) +[![CodeQL](https://github.com/tlsfuzzer/python-ecdsa/actions/workflows/codeql.yml/badge.svg)](https://github.com/tlsfuzzer/python-ecdsa/actions/workflows/codeql.yml) +[![Latest Version](https://img.shields.io/pypi/v/ecdsa.svg?style=flat)](https://pypi.python.org/pypi/ecdsa/) +![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg?style=flat) + + +This is an easy-to-use implementation of ECC (Elliptic Curve Cryptography) +with support for ECDSA (Elliptic Curve Digital Signature Algorithm), +EdDSA (Edwards-curve Digital Signature Algorithm) and ECDH +(Elliptic Curve Diffie-Hellman), implemented purely in Python, released under +the MIT license. With this library, you can quickly create key pairs (signing +key and verifying key), sign messages, and verify the signatures. You can +also agree on a shared secret key based on exchanged public keys. +The keys and signatures are very short, making them easy to handle and +incorporate into other protocols. + +**NOTE: This library should not be used in production settings, see [Security](#Security) for more details.** + +## Features + +This library provides key generation, signing, verifying, and shared secret +derivation for five +popular NIST "Suite B" GF(p) (_prime field_) curves, with key lengths of 192, +224, 256, 384, and 521 bits. The "short names" for these curves, as known by +the OpenSSL tool (`openssl ecparam -list_curves`), are: `prime192v1`, +`secp224r1`, `prime256v1`, `secp384r1`, and `secp521r1`. It includes the +256-bit curve `secp256k1` used by Bitcoin. There is also support for the +regular (non-twisted) variants of Brainpool curves from 160 to 512 bits. The +"short names" of those curves are: `brainpoolP160r1`, `brainpoolP192r1`, +`brainpoolP224r1`, `brainpoolP256r1`, `brainpoolP320r1`, `brainpoolP384r1`, +`brainpoolP512r1`. Few of the small curves from SEC standard are also +included (mainly to speed-up testing of the library), those are: +`secp112r1`, `secp112r2`, `secp128r1`, and `secp160r1`. +Key generation, siging and verifying is also supported for Ed25519 and +Ed448 curves. +No other curves are included, but it is not too hard to add support for more +curves over prime fields. + +## Dependencies + +This library uses only Python and the 'six' package. It is compatible with +Python 2.6, 2.7, and 3.5+. It also supports execution on alternative +implementations like pypy and pypy3. + +If `gmpy2` or `gmpy` is installed, they will be used for faster arithmetic. +Either of them can be installed after this library is installed, +`python-ecdsa` will detect their presence on start-up and use them +automatically. +You should prefer `gmpy2` on Python3 for optimal performance. + +To run the OpenSSL compatibility tests, the 'openssl' tool must be in your +`PATH`. This release has been tested successfully against OpenSSL 0.9.8o, +1.0.0a, 1.0.2f, 1.1.1d and 3.0.1 (among others). + + +## Installation + +This library is available on PyPI, it's recommended to install it using `pip`: + +``` +pip install ecdsa +``` + +In case higher performance is wanted and using native code is not a problem, +it's possible to specify installation together with `gmpy2`: + +``` +pip install ecdsa[gmpy2] +``` + +or (slower, legacy option): +``` +pip install ecdsa[gmpy] +``` + +## Speed + +The following table shows how long this library takes to generate key pairs +(`keygen`), to sign data (`sign`), to verify those signatures (`verify`), +to derive a shared secret (`ecdh`), and +to verify the signatures with no key-specific precomputation (`no PC verify`). +All those values are in seconds. +For convenience, the inverses of those values are also provided: +how many keys per second can be generated (`keygen/s`), how many signatures +can be made per second (`sign/s`), how many signatures can be verified +per second (`verify/s`), how many shared secrets can be derived per second +(`ecdh/s`), and how many signatures with no key specific +precomputation can be verified per second (`no PC verify/s`). The size of raw +signature (generally the smallest +the way a signature can be encoded) is also provided in the `siglen` column. +Use `tox -e speed` to generate this table on your own computer. +On an Intel Core i7 4790K @ 4.0GHz I'm getting the following performance: + +``` + siglen keygen keygen/s sign sign/s verify verify/s no PC verify no PC verify/s + NIST192p: 48 0.00032s 3134.06 0.00033s 2985.53 0.00063s 1598.36 0.00129s 774.43 + NIST224p: 56 0.00040s 2469.24 0.00042s 2367.88 0.00081s 1233.41 0.00170s 586.66 + NIST256p: 64 0.00051s 1952.73 0.00054s 1867.80 0.00098s 1021.86 0.00212s 471.27 + NIST384p: 96 0.00107s 935.92 0.00111s 904.23 0.00203s 491.77 0.00446s 224.00 + NIST521p: 132 0.00210s 475.52 0.00215s 464.16 0.00398s 251.28 0.00874s 114.39 + SECP256k1: 64 0.00052s 1921.54 0.00054s 1847.49 0.00105s 948.68 0.00210s 477.01 + BRAINPOOLP160r1: 40 0.00025s 4003.88 0.00026s 3845.12 0.00053s 1893.93 0.00105s 949.92 + BRAINPOOLP192r1: 48 0.00033s 3043.97 0.00034s 2975.98 0.00063s 1581.50 0.00135s 742.29 + BRAINPOOLP224r1: 56 0.00041s 2436.44 0.00043s 2315.51 0.00078s 1278.49 0.00180s 556.16 + BRAINPOOLP256r1: 64 0.00053s 1892.49 0.00054s 1846.24 0.00114s 875.64 0.00229s 437.25 + BRAINPOOLP320r1: 80 0.00073s 1361.26 0.00076s 1309.25 0.00143s 699.29 0.00322s 310.49 + BRAINPOOLP384r1: 96 0.00107s 931.29 0.00111s 901.80 0.00230s 434.19 0.00476s 210.20 + BRAINPOOLP512r1: 128 0.00207s 483.41 0.00212s 471.42 0.00425s 235.43 0.00912s 109.61 + SECP112r1: 28 0.00015s 6672.53 0.00016s 6440.34 0.00031s 3265.41 0.00056s 1774.20 + SECP112r2: 28 0.00015s 6697.11 0.00015s 6479.98 0.00028s 3524.72 0.00058s 1716.16 + SECP128r1: 32 0.00018s 5497.65 0.00019s 5272.89 0.00036s 2747.39 0.00072s 1396.16 + SECP160r1: 42 0.00025s 3949.32 0.00026s 3894.45 0.00046s 2153.85 0.00102s 985.07 + Ed25519: 64 0.00076s 1324.48 0.00042s 2405.01 0.00109s 918.05 0.00344s 290.50 + Ed448: 114 0.00176s 569.53 0.00115s 870.94 0.00282s 355.04 0.01024s 97.69 + + ecdh ecdh/s + NIST192p: 0.00104s 964.89 + NIST224p: 0.00134s 748.63 + NIST256p: 0.00170s 587.08 + NIST384p: 0.00352s 283.90 + NIST521p: 0.00717s 139.51 + SECP256k1: 0.00154s 648.40 + BRAINPOOLP160r1: 0.00082s 1220.70 + BRAINPOOLP192r1: 0.00105s 956.75 + BRAINPOOLP224r1: 0.00136s 734.52 + BRAINPOOLP256r1: 0.00178s 563.32 + BRAINPOOLP320r1: 0.00252s 397.23 + BRAINPOOLP384r1: 0.00376s 266.27 + BRAINPOOLP512r1: 0.00733s 136.35 + SECP112r1: 0.00046s 2180.40 + SECP112r2: 0.00045s 2229.14 + SECP128r1: 0.00054s 1868.15 + SECP160r1: 0.00080s 1243.98 +``` + +To test performance with `gmpy2` loaded, use `tox -e speedgmpy2`. +On the same machine I'm getting the following performance with `gmpy2`: +``` + siglen keygen keygen/s sign sign/s verify verify/s no PC verify no PC verify/s + NIST192p: 48 0.00017s 5933.40 0.00017s 5751.70 0.00032s 3125.28 0.00067s 1502.41 + NIST224p: 56 0.00021s 4782.87 0.00022s 4610.05 0.00040s 2487.04 0.00089s 1126.90 + NIST256p: 64 0.00023s 4263.98 0.00024s 4125.16 0.00045s 2200.88 0.00098s 1016.82 + NIST384p: 96 0.00041s 2449.54 0.00042s 2399.96 0.00083s 1210.57 0.00172s 581.43 + NIST521p: 132 0.00071s 1416.07 0.00072s 1389.81 0.00144s 692.93 0.00312s 320.40 + SECP256k1: 64 0.00024s 4245.05 0.00024s 4122.09 0.00045s 2206.40 0.00094s 1068.32 + BRAINPOOLP160r1: 40 0.00014s 6939.17 0.00015s 6681.55 0.00029s 3452.43 0.00057s 1769.81 + BRAINPOOLP192r1: 48 0.00017s 5920.05 0.00017s 5774.36 0.00034s 2979.00 0.00069s 1453.19 + BRAINPOOLP224r1: 56 0.00021s 4732.12 0.00022s 4622.65 0.00041s 2422.47 0.00087s 1149.87 + BRAINPOOLP256r1: 64 0.00024s 4233.02 0.00024s 4115.20 0.00047s 2143.27 0.00098s 1015.60 + BRAINPOOLP320r1: 80 0.00032s 3162.38 0.00032s 3077.62 0.00063s 1598.83 0.00136s 737.34 + BRAINPOOLP384r1: 96 0.00041s 2436.88 0.00042s 2395.62 0.00083s 1202.68 0.00178s 562.85 + BRAINPOOLP512r1: 128 0.00063s 1587.60 0.00064s 1558.83 0.00125s 799.96 0.00281s 355.83 + SECP112r1: 28 0.00009s 11118.66 0.00009s 10775.48 0.00018s 5456.00 0.00033s 3020.83 + SECP112r2: 28 0.00009s 11322.97 0.00009s 10857.71 0.00017s 5748.77 0.00032s 3094.28 + SECP128r1: 32 0.00010s 10078.39 0.00010s 9665.27 0.00019s 5200.58 0.00036s 2760.88 + SECP160r1: 42 0.00015s 6875.51 0.00015s 6647.35 0.00029s 3422.41 0.00057s 1768.35 + Ed25519: 64 0.00030s 3322.56 0.00018s 5568.63 0.00046s 2165.35 0.00153s 654.02 + Ed448: 114 0.00060s 1680.53 0.00039s 2567.40 0.00096s 1036.67 0.00350s 285.62 + + ecdh ecdh/s + NIST192p: 0.00050s 1985.70 + NIST224p: 0.00066s 1524.16 + NIST256p: 0.00071s 1413.07 + NIST384p: 0.00127s 788.89 + NIST521p: 0.00230s 434.85 + SECP256k1: 0.00071s 1409.95 + BRAINPOOLP160r1: 0.00042s 2374.65 + BRAINPOOLP192r1: 0.00051s 1960.01 + BRAINPOOLP224r1: 0.00066s 1518.37 + BRAINPOOLP256r1: 0.00071s 1399.90 + BRAINPOOLP320r1: 0.00100s 997.21 + BRAINPOOLP384r1: 0.00129s 777.51 + BRAINPOOLP512r1: 0.00210s 475.99 + SECP112r1: 0.00022s 4457.70 + SECP112r2: 0.00024s 4252.33 + SECP128r1: 0.00028s 3589.31 + SECP160r1: 0.00043s 2305.02 +``` + +(there's also `gmpy` version, execute it using `tox -e speedgmpy`) + +For comparison, a highly optimised implementation (including curve-specific +assembly for some curves), like the one in OpenSSL 1.1.1d, provides the +following performance numbers on the same machine. +Run `openssl speed ecdsa` and `openssl speed ecdh` to reproduce it: +``` + sign verify sign/s verify/s + 192 bits ecdsa (nistp192) 0.0002s 0.0002s 4785.6 5380.7 + 224 bits ecdsa (nistp224) 0.0000s 0.0001s 22475.6 9822.0 + 256 bits ecdsa (nistp256) 0.0000s 0.0001s 45069.6 14166.6 + 384 bits ecdsa (nistp384) 0.0008s 0.0006s 1265.6 1648.1 + 521 bits ecdsa (nistp521) 0.0003s 0.0005s 3753.1 1819.5 + 256 bits ecdsa (brainpoolP256r1) 0.0003s 0.0003s 2983.5 3333.2 + 384 bits ecdsa (brainpoolP384r1) 0.0008s 0.0007s 1258.8 1528.1 + 512 bits ecdsa (brainpoolP512r1) 0.0015s 0.0012s 675.1 860.1 + + sign verify sign/s verify/s + 253 bits EdDSA (Ed25519) 0.0000s 0.0001s 28217.9 10897.7 + 456 bits EdDSA (Ed448) 0.0003s 0.0005s 3926.5 2147.7 + + op op/s + 192 bits ecdh (nistp192) 0.0002s 4853.4 + 224 bits ecdh (nistp224) 0.0001s 15252.1 + 256 bits ecdh (nistp256) 0.0001s 18436.3 + 384 bits ecdh (nistp384) 0.0008s 1292.7 + 521 bits ecdh (nistp521) 0.0003s 2884.7 + 256 bits ecdh (brainpoolP256r1) 0.0003s 3066.5 + 384 bits ecdh (brainpoolP384r1) 0.0008s 1298.0 + 512 bits ecdh (brainpoolP512r1) 0.0014s 694.8 +``` + +Keys and signature can be serialized in different ways (see Usage, below). +For a NIST192p key, the three basic representations require strings of the +following lengths (in bytes): + + to_string: signkey= 24, verifykey= 48, signature=48 + compressed: signkey=n/a, verifykey= 25, signature=n/a + DER: signkey=106, verifykey= 80, signature=55 + PEM: signkey=278, verifykey=162, (no support for PEM signatures) + +## History + +In 2006, Peter Pearson announced his pure-python implementation of ECDSA in a +[message to sci.crypt][1], available from his [download site][2]. In 2010, +Brian Warner wrote a wrapper around this code, to make it a bit easier and +safer to use. In 2020, Hubert Kario included an implementation of elliptic +curve cryptography that uses Jacobian coordinates internally, improving +performance about 20-fold. You are looking at the README for this wrapper. + +[1]: http://www.derkeiler.com/Newsgroups/sci.crypt/2006-01/msg00651.html +[2]: http://webpages.charter.net/curryfans/peter/downloads.html + +## Testing + +To run the full test suite, do this: + + tox -e coverage + +On an Intel Core i7 4790K @ 4.0GHz, the tests take about 18 seconds to execute. +The test suite uses +[`hypothesis`](https://github.com/HypothesisWorks/hypothesis) so there is some +inherent variability in the test suite execution time. + +One part of `test_pyecdsa.py` and `test_ecdh.py` checks compatibility with +OpenSSL, by running the "openssl" CLI tool, make sure it's in your `PATH` if +you want to test compatibility with it (if OpenSSL is missing, too old, or +doesn't support all the curves supported in upstream releases you will see +skipped tests in the above `coverage` run). + +## Security + +This library was not designed with security in mind. If you are processing +data that needs to be protected we suggest you use a quality wrapper around +OpenSSL. [pyca/cryptography](https://cryptography.io) is one example of such +a wrapper. The primary use-case of this library is as a portable library for +interoperability testing and as a teaching tool. + +**This library does not protect against side-channel attacks.** + +Do not allow attackers to measure how long it takes you to generate a key pair +or sign a message. Do not allow attackers to run code on the same physical +machine when key pair generation or signing is taking place (this includes +virtual machines). Do not allow attackers to measure how much power your +computer uses while generating the key pair or signing a message. Do not allow +attackers to measure RF interference coming from your computer while generating +a key pair or signing a message. Note: just loading the private key will cause +key pair generation. Other operations or attack vectors may also be +vulnerable to attacks. **For a sophisticated attacker observing just one +operation with a private key will be sufficient to completely +reconstruct the private key**. + +Please also note that any Pure-python cryptographic library will be vulnerable +to the same side-channel attacks. This is because Python does not provide +side-channel secure primitives (with the exception of +[`hmac.compare_digest()`][3]), making side-channel secure programming +impossible. + +This library depends upon a strong source of random numbers. Do not use it on +a system where `os.urandom()` does not provide cryptographically secure +random numbers. + +[3]: https://docs.python.org/3/library/hmac.html#hmac.compare_digest + +## Usage + +You start by creating a `SigningKey`. You can use this to sign data, by passing +in data as a byte string and getting back the signature (also a byte string). +You can also ask a `SigningKey` to give you the corresponding `VerifyingKey`. +The `VerifyingKey` can be used to verify a signature, by passing it both the +data string and the signature byte string: it either returns True or raises +`BadSignatureError`. + +```python +from ecdsa import SigningKey +sk = SigningKey.generate() # uses NIST192p +vk = sk.verifying_key +signature = sk.sign(b"message") +assert vk.verify(signature, b"message") +``` + +Each `SigningKey`/`VerifyingKey` is associated with a specific curve, like +NIST192p (the default one). Longer curves are more secure, but take longer to +use, and result in longer keys and signatures. + +```python +from ecdsa import SigningKey, NIST384p +sk = SigningKey.generate(curve=NIST384p) +vk = sk.verifying_key +signature = sk.sign(b"message") +assert vk.verify(signature, b"message") +``` + +The `SigningKey` can be serialized into several different formats: the shortest +is to call `s=sk.to_string()`, and then re-create it with +`SigningKey.from_string(s, curve)` . This short form does not record the +curve, so you must be sure to pass to `from_string()` the same curve you used +for the original key. The short form of a NIST192p-based signing key is just 24 +bytes long. If a point encoding is invalid or it does not lie on the specified +curve, `from_string()` will raise `MalformedPointError`. + +```python +from ecdsa import SigningKey, NIST384p +sk = SigningKey.generate(curve=NIST384p) +sk_string = sk.to_string() +sk2 = SigningKey.from_string(sk_string, curve=NIST384p) +print(sk_string.hex()) +print(sk2.to_string().hex()) +``` + +Note: while the methods are called `to_string()` the type they return is +actually `bytes`, the "string" part is leftover from Python 2. + +`sk.to_pem()` and `sk.to_der()` will serialize the signing key into the same +formats that OpenSSL uses. The PEM file looks like the familiar ASCII-armored +`"-----BEGIN EC PRIVATE KEY-----"` base64-encoded format, and the DER format +is a shorter binary form of the same data. +`SigningKey.from_pem()/.from_der()` will undo this serialization. These +formats include the curve name, so you do not need to pass in a curve +identifier to the deserializer. In case the file is malformed `from_der()` +and `from_pem()` will raise `UnexpectedDER` or` MalformedPointError`. + +```python +from ecdsa import SigningKey, NIST384p +sk = SigningKey.generate(curve=NIST384p) +sk_pem = sk.to_pem() +sk2 = SigningKey.from_pem(sk_pem) +# sk and sk2 are the same key +``` + +Likewise, the `VerifyingKey` can be serialized in the same way: +`vk.to_string()/VerifyingKey.from_string()`, `to_pem()/from_pem()`, and +`to_der()/from_der()`. The same `curve=` argument is needed for +`VerifyingKey.from_string()`. + +```python +from ecdsa import SigningKey, VerifyingKey, NIST384p +sk = SigningKey.generate(curve=NIST384p) +vk = sk.verifying_key +vk_string = vk.to_string() +vk2 = VerifyingKey.from_string(vk_string, curve=NIST384p) +# vk and vk2 are the same key + +from ecdsa import SigningKey, VerifyingKey, NIST384p +sk = SigningKey.generate(curve=NIST384p) +vk = sk.verifying_key +vk_pem = vk.to_pem() +vk2 = VerifyingKey.from_pem(vk_pem) +# vk and vk2 are the same key +``` + +There are a couple of different ways to compute a signature. Fundamentally, +ECDSA takes a number that represents the data being signed, and returns a +pair of numbers that represent the signature. The `hashfunc=` argument to +`sk.sign()` and `vk.verify()` is used to turn an arbitrary string into a +fixed-length digest, which is then turned into a number that ECDSA can sign, +and both sign and verify must use the same approach. The default value is +`hashlib.sha1`, but if you use NIST256p or a longer curve, you can use +`hashlib.sha256` instead. + +There are also multiple ways to represent a signature. The default +`sk.sign()` and `vk.verify()` methods present it as a short string, for +simplicity and minimal overhead. To use a different scheme, use the +`sk.sign(sigencode=)` and `vk.verify(sigdecode=)` arguments. There are helper +functions in the `ecdsa.util` module that can be useful here. + +It is also possible to create a `SigningKey` from a "seed", which is +deterministic. This can be used in protocols where you want to derive +consistent signing keys from some other secret, for example when you want +three separate keys and only want to store a single master secret. You should +start with a uniformly-distributed unguessable seed with about `curve.baselen` +bytes of entropy, and then use one of the helper functions in `ecdsa.util` to +convert it into an integer in the correct range, and then finally pass it +into `SigningKey.from_secret_exponent()`, like this: + +```python +import os +from ecdsa import NIST384p, SigningKey +from ecdsa.util import randrange_from_seed__trytryagain + +def make_key(seed): + secexp = randrange_from_seed__trytryagain(seed, NIST384p.order) + return SigningKey.from_secret_exponent(secexp, curve=NIST384p) + +seed = os.urandom(NIST384p.baselen) # or other starting point +sk1a = make_key(seed) +sk1b = make_key(seed) +# note: sk1a and sk1b are the same key +assert sk1a.to_string() == sk1b.to_string() +sk2 = make_key(b"2-"+seed) # different key +assert sk1a.to_string() != sk2.to_string() +``` + +In case the application will verify a lot of signatures made with a single +key, it's possible to precompute some of the internal values to make +signature verification significantly faster. The break-even point occurs at +about 100 signatures verified. + +To perform precomputation, you can call the `precompute()` method +on `VerifyingKey` instance: +```python +from ecdsa import SigningKey, NIST384p +sk = SigningKey.generate(curve=NIST384p) +vk = sk.verifying_key +vk.precompute() +signature = sk.sign(b"message") +assert vk.verify(signature, b"message") +``` + +Once `precompute()` was called, all signature verifications with this key will +be faster to execute. + +## OpenSSL Compatibility + +To produce signatures that can be verified by OpenSSL tools, or to verify +signatures that were produced by those tools, use: + +```python +# openssl ecparam -name prime256v1 -genkey -out sk.pem +# openssl ec -in sk.pem -pubout -out vk.pem +# echo "data for signing" > data +# openssl dgst -sha256 -sign sk.pem -out data.sig data +# openssl dgst -sha256 -verify vk.pem -signature data.sig data +# openssl dgst -sha256 -prverify sk.pem -signature data.sig data + +import hashlib +from ecdsa import SigningKey, VerifyingKey +from ecdsa.util import sigencode_der, sigdecode_der + +with open("vk.pem") as f: + vk = VerifyingKey.from_pem(f.read()) + +with open("data", "rb") as f: + data = f.read() + +with open("data.sig", "rb") as f: + signature = f.read() + +assert vk.verify(signature, data, hashlib.sha256, sigdecode=sigdecode_der) + +with open("sk.pem") as f: + sk = SigningKey.from_pem(f.read(), hashlib.sha256) + +new_signature = sk.sign_deterministic(data, sigencode=sigencode_der) + +with open("data.sig2", "wb") as f: + f.write(new_signature) + +# openssl dgst -sha256 -verify vk.pem -signature data.sig2 data +``` + +Note: if compatibility with OpenSSL 1.0.0 or earlier is necessary, the +`sigencode_string` and `sigdecode_string` from `ecdsa.util` can be used for +respectively writing and reading the signatures. + +The keys also can be written in format that openssl can handle: + +```python +from ecdsa import SigningKey, VerifyingKey + +with open("sk.pem") as f: + sk = SigningKey.from_pem(f.read()) +with open("sk.pem", "wb") as f: + f.write(sk.to_pem()) + +with open("vk.pem") as f: + vk = VerifyingKey.from_pem(f.read()) +with open("vk.pem", "wb") as f: + f.write(vk.to_pem()) +``` + +## Entropy + +Creating a signing key with `SigningKey.generate()` requires some form of +entropy (as opposed to +`from_secret_exponent`/`from_string`/`from_der`/`from_pem`, +which are deterministic and do not require an entropy source). The default +source is `os.urandom()`, but you can pass any other function that behaves +like `os.urandom` as the `entropy=` argument to do something different. This +may be useful in unit tests, where you want to achieve repeatable results. The +`ecdsa.util.PRNG` utility is handy here: it takes a seed and produces a strong +pseudo-random stream from it: + +```python +from ecdsa.util import PRNG +from ecdsa import SigningKey +rng1 = PRNG(b"seed") +sk1 = SigningKey.generate(entropy=rng1) +rng2 = PRNG(b"seed") +sk2 = SigningKey.generate(entropy=rng2) +# sk1 and sk2 are the same key +``` + +Likewise, ECDSA signature generation requires a random number, and each +signature must use a different one (using the same number twice will +immediately reveal the private signing key). The `sk.sign()` method takes an +`entropy=` argument which behaves the same as `SigningKey.generate(entropy=)`. + +## Deterministic Signatures + +If you call `SigningKey.sign_deterministic(data)` instead of `.sign(data)`, +the code will generate a deterministic signature instead of a random one. +This uses the algorithm from RFC6979 to safely generate a unique `k` value, +derived from the private key and the message being signed. Each time you sign +the same message with the same key, you will get the same signature (using +the same `k`). + +This may become the default in a future version, as it is not vulnerable to +failures of the entropy source. + +## Examples + +Create a NIST192p key pair and immediately save both to disk: + +```python +from ecdsa import SigningKey +sk = SigningKey.generate() +vk = sk.verifying_key +with open("private.pem", "wb") as f: + f.write(sk.to_pem()) +with open("public.pem", "wb") as f: + f.write(vk.to_pem()) +``` + +Load a signing key from disk, use it to sign a message (using SHA-1), and write +the signature to disk: + +```python +from ecdsa import SigningKey +with open("private.pem") as f: + sk = SigningKey.from_pem(f.read()) +with open("message", "rb") as f: + message = f.read() +sig = sk.sign(message) +with open("signature", "wb") as f: + f.write(sig) +``` + +Load the verifying key, message, and signature from disk, and verify the +signature (assume SHA-1 hash): + +```python +from ecdsa import VerifyingKey, BadSignatureError +vk = VerifyingKey.from_pem(open("public.pem").read()) +with open("message", "rb") as f: + message = f.read() +with open("signature", "rb") as f: + sig = f.read() +try: + vk.verify(sig, message) + print "good signature" +except BadSignatureError: + print "BAD SIGNATURE" +``` + +Create a NIST521p key pair: + +```python +from ecdsa import SigningKey, NIST521p +sk = SigningKey.generate(curve=NIST521p) +vk = sk.verifying_key +``` + +Create three independent signing keys from a master seed: + +```python +from ecdsa import NIST192p, SigningKey +from ecdsa.util import randrange_from_seed__trytryagain + +def make_key_from_seed(seed, curve=NIST192p): + secexp = randrange_from_seed__trytryagain(seed, curve.order) + return SigningKey.from_secret_exponent(secexp, curve) + +sk1 = make_key_from_seed("1:%s" % seed) +sk2 = make_key_from_seed("2:%s" % seed) +sk3 = make_key_from_seed("3:%s" % seed) +``` + +Load a verifying key from disk and print it using hex encoding in +uncompressed and compressed format (defined in X9.62 and SEC1 standards): + +```python +from ecdsa import VerifyingKey + +with open("public.pem") as f: + vk = VerifyingKey.from_pem(f.read()) + +print("uncompressed: {0}".format(vk.to_string("uncompressed").hex())) +print("compressed: {0}".format(vk.to_string("compressed").hex())) +``` + +Load a verifying key from a hex string from compressed format, output +uncompressed: + +```python +from ecdsa import VerifyingKey, NIST256p + +comp_str = '022799c0d0ee09772fdd337d4f28dc155581951d07082fb19a38aa396b67e77759' +vk = VerifyingKey.from_string(bytearray.fromhex(comp_str), curve=NIST256p) +print(vk.to_string("uncompressed").hex()) +``` + +ECDH key exchange with remote party: + +```python +from ecdsa import ECDH, NIST256p + +ecdh = ECDH(curve=NIST256p) +ecdh.generate_private_key() +local_public_key = ecdh.get_public_key() +#send `local_public_key` to remote party and receive `remote_public_key` from remote party +with open("remote_public_key.pem") as e: + remote_public_key = e.read() +ecdh.load_received_public_key_pem(remote_public_key) +secret = ecdh.generate_sharedsecret_bytes() +``` diff --git a/code/.venv/lib/python3.12/site-packages/ecdsa-0.19.0.dist-info/RECORD b/code/.venv/lib/python3.12/site-packages/ecdsa-0.19.0.dist-info/RECORD new file mode 100644 index 0000000..57df00e --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/ecdsa-0.19.0.dist-info/RECORD @@ -0,0 +1,66 @@ +ecdsa-0.19.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +ecdsa-0.19.0.dist-info/LICENSE,sha256=PsqYRXc9LluMydjBGdNF8ApIBuS9Zg1KPWzfnA6di7I,1147 +ecdsa-0.19.0.dist-info/METADATA,sha256=1lr-f8_T7fP7a4DaW2fvXolaKE-C3LnqTjHqdrU1wcE,29679 +ecdsa-0.19.0.dist-info/RECORD,, +ecdsa-0.19.0.dist-info/WHEEL,sha256=bb2Ot9scclHKMOLDEHY6B2sicWOgugjFKaJsT7vwMQo,110 +ecdsa-0.19.0.dist-info/top_level.txt,sha256=7ovPHfAPyTou19f8gOSbHm6B9dGjTibWolcCB7Zjovs,6 +ecdsa/__init__.py,sha256=wRUnU3g01MM-mLNemnov-_8B81DlbSrqoFtOO4bZzQQ,1931 +ecdsa/__pycache__/__init__.cpython-312.pyc,, +ecdsa/__pycache__/_compat.cpython-312.pyc,, +ecdsa/__pycache__/_rwlock.cpython-312.pyc,, +ecdsa/__pycache__/_sha3.cpython-312.pyc,, +ecdsa/__pycache__/_version.cpython-312.pyc,, +ecdsa/__pycache__/curves.cpython-312.pyc,, +ecdsa/__pycache__/der.cpython-312.pyc,, +ecdsa/__pycache__/ecdh.cpython-312.pyc,, +ecdsa/__pycache__/ecdsa.cpython-312.pyc,, +ecdsa/__pycache__/eddsa.cpython-312.pyc,, +ecdsa/__pycache__/ellipticcurve.cpython-312.pyc,, +ecdsa/__pycache__/errors.cpython-312.pyc,, +ecdsa/__pycache__/keys.cpython-312.pyc,, +ecdsa/__pycache__/numbertheory.cpython-312.pyc,, +ecdsa/__pycache__/rfc6979.cpython-312.pyc,, +ecdsa/__pycache__/ssh.cpython-312.pyc,, +ecdsa/__pycache__/test_curves.cpython-312.pyc,, +ecdsa/__pycache__/test_der.cpython-312.pyc,, +ecdsa/__pycache__/test_ecdh.cpython-312.pyc,, +ecdsa/__pycache__/test_ecdsa.cpython-312.pyc,, +ecdsa/__pycache__/test_eddsa.cpython-312.pyc,, +ecdsa/__pycache__/test_ellipticcurve.cpython-312.pyc,, +ecdsa/__pycache__/test_jacobi.cpython-312.pyc,, +ecdsa/__pycache__/test_keys.cpython-312.pyc,, +ecdsa/__pycache__/test_malformed_sigs.cpython-312.pyc,, +ecdsa/__pycache__/test_numbertheory.cpython-312.pyc,, +ecdsa/__pycache__/test_pyecdsa.cpython-312.pyc,, +ecdsa/__pycache__/test_rw_lock.cpython-312.pyc,, +ecdsa/__pycache__/test_sha3.cpython-312.pyc,, +ecdsa/__pycache__/util.cpython-312.pyc,, +ecdsa/_compat.py,sha256=5EP735DlmaSb8Slk4BMnh9Z7MvYpTnGS3S_DBmHETwo,4047 +ecdsa/_rwlock.py,sha256=CAwHp2V65ksI8B1UqY7EccK9LaUToiv6pDLVzm44eag,2849 +ecdsa/_sha3.py,sha256=DJs7QLmdkQMU35llyD8HQeAXNvf5sMcujO6oFdScIqI,4747 +ecdsa/_version.py,sha256=rIOw4wECMIfW9NgIJj9cE4QbZiELgE2VQtUGkBNqiu8,498 +ecdsa/curves.py,sha256=EcPE0WRFkjpPMZfVM0-hRQ63CdW5GKTkDRpK-VOy1Zo,15975 +ecdsa/der.py,sha256=y7cSfxxydbgUV6HPIwsEgHuMF5yo21g81T5uassyhvk,14104 +ecdsa/ecdh.py,sha256=Tiirawt5xegVDrY9eS-ATvvfmTIznUyv5fy2k7VnzTk,11011 +ecdsa/ecdsa.py,sha256=e-tlePLMDdt8O5VACr640guulJns4fHfHvds8FDGZtw,31703 +ecdsa/eddsa.py,sha256=IzsGzoGAefcoYjF7DVjFkX5ZJqiK2LTOmAMe6wyf4UU,7170 +ecdsa/ellipticcurve.py,sha256=2mw8LnuRQ6j0oS9Ck3KMSMXozBq740UnHyHBGxriESM,54118 +ecdsa/errors.py,sha256=b4mhnmIpRnEdHzbectHAA5F7O9MtSaI-fYoc13_vBxQ,130 +ecdsa/keys.py,sha256=FkpubL9MGCfkCJ6aVvV5c0bve4ZuBtU3P1dRRBTth0k,65503 +ecdsa/numbertheory.py,sha256=Ad2-mVFaOytMomBC-7d0cJao4tpkVLh4x7vyqj73G6A,17831 +ecdsa/rfc6979.py,sha256=zwzo33lsZJA9r2dSf7HCliI_yIbw5cJ0Ek9tLdRRO40,2850 +ecdsa/ssh.py,sha256=360JY0dbYeZaqx9k_OhlHzPZYZTw5UB5xtK2XLBqb0M,1916 +ecdsa/test_curves.py,sha256=l5N-m4Yo5IAy4a8aJMsBamaSlLAfSoYjYqCj1HDEVpU,13081 +ecdsa/test_der.py,sha256=SKANQM2JuUTxsOkwjB5GRzz8NbMGx9xSEPBWL4oewWY,14933 +ecdsa/test_ecdh.py,sha256=20TEYyGcnynAPS9nlr_k2ed6S7lJ1Z0bbohNU-pEGco,15380 +ecdsa/test_ecdsa.py,sha256=V5Q4Q7uUFfStVdeFtwhXVpFlFi0Nx5J5uru9T3j_3DQ,25037 +ecdsa/test_eddsa.py,sha256=1jfHF_ZSyillv6DLKJZ0SeYThauTCHsyRNAweS_4MEQ,33720 +ecdsa/test_ellipticcurve.py,sha256=6Su-6PWhL82kMGGuH4NIlayAWdSqQFGLqPu94vkJkW4,7637 +ecdsa/test_jacobi.py,sha256=2n30l0nD24RjtgO60MlIWC07-12jBoSgelxPe6OaMdQ,20662 +ecdsa/test_keys.py,sha256=reQ2KtfOsANa59CqvvRWjZm-SaWfscJZVJaE9RFyL94,39415 +ecdsa/test_malformed_sigs.py,sha256=Dg1Dkvgz1tO-KhZ6f9ZRljykz8FBYtld3hq1W2hLLM8,11289 +ecdsa/test_numbertheory.py,sha256=YTtclt_50n_24U__r2JyV3LEqR2BsBmA6IJghwBDz8c,13265 +ecdsa/test_pyecdsa.py,sha256=uu8MaqeV1AYSdK7FsX4D88uSvTKRr7yeoNqVb8ZYEhM,89939 +ecdsa/test_rw_lock.py,sha256=byv0_FTM90cbuHPCI6__LeQJkHL_zYEeVYIBO8e2LLc,7021 +ecdsa/test_sha3.py,sha256=0PkWi7AnTJ10YNfDVqj2SB7adeTbV6DCvMg4n9ULad8,3042 +ecdsa/util.py,sha256=P3lum42zhB2l_4gU_iwRWvdw9DAbnDze-Oge4is1ghI,17646 diff --git a/code/.venv/lib/python3.12/site-packages/ecdsa-0.19.0.dist-info/WHEEL b/code/.venv/lib/python3.12/site-packages/ecdsa-0.19.0.dist-info/WHEEL new file mode 100644 index 0000000..9d8f872 --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/ecdsa-0.19.0.dist-info/WHEEL @@ -0,0 +1,6 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.38.4) +Root-Is-Purelib: true +Tag: py2-none-any +Tag: py3-none-any + diff --git a/code/.venv/lib/python3.12/site-packages/ecdsa-0.19.0.dist-info/top_level.txt b/code/.venv/lib/python3.12/site-packages/ecdsa-0.19.0.dist-info/top_level.txt new file mode 100644 index 0000000..aa5efdb --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/ecdsa-0.19.0.dist-info/top_level.txt @@ -0,0 +1 @@ +ecdsa diff --git a/code/.venv/lib/python3.12/site-packages/ecdsa/__init__.py b/code/.venv/lib/python3.12/site-packages/ecdsa/__init__.py new file mode 100644 index 0000000..342538e --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/ecdsa/__init__.py @@ -0,0 +1,104 @@ +# while we don't use six in this file, we did bundle it for a long time, so +# keep as part of module in a virtual way (through __all__) +import six +from .keys import ( + SigningKey, + VerifyingKey, + BadSignatureError, + BadDigestError, + MalformedPointError, +) +from .curves import ( + NIST192p, + NIST224p, + NIST256p, + NIST384p, + NIST521p, + SECP256k1, + BRAINPOOLP160r1, + BRAINPOOLP192r1, + BRAINPOOLP224r1, + BRAINPOOLP256r1, + BRAINPOOLP320r1, + BRAINPOOLP384r1, + BRAINPOOLP512r1, + SECP112r1, + SECP112r2, + SECP128r1, + SECP160r1, + Ed25519, + Ed448, + BRAINPOOLP160t1, + BRAINPOOLP192t1, + BRAINPOOLP224t1, + BRAINPOOLP256t1, + BRAINPOOLP320t1, + BRAINPOOLP384t1, + BRAINPOOLP512t1, +) +from .ecdh import ( + ECDH, + NoKeyError, + NoCurveError, + InvalidCurveError, + InvalidSharedSecretError, +) +from .der import UnexpectedDER +from . import _version + +# This code comes from http://github.com/tlsfuzzer/python-ecdsa +__all__ = [ + "curves", + "der", + "ecdsa", + "ellipticcurve", + "keys", + "numbertheory", + "test_pyecdsa", + "util", + "six", +] + +_hush_pyflakes = [ + SigningKey, + VerifyingKey, + BadSignatureError, + BadDigestError, + MalformedPointError, + UnexpectedDER, + InvalidCurveError, + NoKeyError, + InvalidSharedSecretError, + ECDH, + NoCurveError, + NIST192p, + NIST224p, + NIST256p, + NIST384p, + NIST521p, + SECP256k1, + BRAINPOOLP160r1, + BRAINPOOLP192r1, + BRAINPOOLP224r1, + BRAINPOOLP256r1, + BRAINPOOLP320r1, + BRAINPOOLP384r1, + BRAINPOOLP512r1, + SECP112r1, + SECP112r2, + SECP128r1, + SECP160r1, + Ed25519, + Ed448, + six.b(""), + BRAINPOOLP160t1, + BRAINPOOLP192t1, + BRAINPOOLP224t1, + BRAINPOOLP256t1, + BRAINPOOLP320t1, + BRAINPOOLP384t1, + BRAINPOOLP512t1, +] +del _hush_pyflakes + +__version__ = _version.get_versions()["version"] diff --git a/code/.venv/lib/python3.12/site-packages/ecdsa/__pycache__/__init__.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/ecdsa/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..35b9192 Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/ecdsa/__pycache__/__init__.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/ecdsa/__pycache__/_compat.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/ecdsa/__pycache__/_compat.cpython-312.pyc new file mode 100644 index 0000000..21046de Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/ecdsa/__pycache__/_compat.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/ecdsa/__pycache__/_rwlock.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/ecdsa/__pycache__/_rwlock.cpython-312.pyc new file mode 100644 index 0000000..bfb7428 Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/ecdsa/__pycache__/_rwlock.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/ecdsa/__pycache__/_sha3.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/ecdsa/__pycache__/_sha3.cpython-312.pyc new file mode 100644 index 0000000..4c7f5bd Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/ecdsa/__pycache__/_sha3.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/ecdsa/__pycache__/_version.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/ecdsa/__pycache__/_version.cpython-312.pyc new file mode 100644 index 0000000..ae88b03 Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/ecdsa/__pycache__/_version.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/ecdsa/__pycache__/curves.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/ecdsa/__pycache__/curves.cpython-312.pyc new file mode 100644 index 0000000..4f0217d Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/ecdsa/__pycache__/curves.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/ecdsa/__pycache__/der.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/ecdsa/__pycache__/der.cpython-312.pyc new file mode 100644 index 0000000..c1906c3 Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/ecdsa/__pycache__/der.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/ecdsa/__pycache__/ecdh.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/ecdsa/__pycache__/ecdh.cpython-312.pyc new file mode 100644 index 0000000..9c8807f Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/ecdsa/__pycache__/ecdh.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/ecdsa/__pycache__/ecdsa.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/ecdsa/__pycache__/ecdsa.cpython-312.pyc new file mode 100644 index 0000000..fc6f5d3 Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/ecdsa/__pycache__/ecdsa.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/ecdsa/__pycache__/eddsa.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/ecdsa/__pycache__/eddsa.cpython-312.pyc new file mode 100644 index 0000000..8f7537d Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/ecdsa/__pycache__/eddsa.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/ecdsa/__pycache__/ellipticcurve.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/ecdsa/__pycache__/ellipticcurve.cpython-312.pyc new file mode 100644 index 0000000..cb25f94 Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/ecdsa/__pycache__/ellipticcurve.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/ecdsa/__pycache__/errors.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/ecdsa/__pycache__/errors.cpython-312.pyc new file mode 100644 index 0000000..566c629 Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/ecdsa/__pycache__/errors.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/ecdsa/__pycache__/keys.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/ecdsa/__pycache__/keys.cpython-312.pyc new file mode 100644 index 0000000..40cf936 Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/ecdsa/__pycache__/keys.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/ecdsa/__pycache__/numbertheory.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/ecdsa/__pycache__/numbertheory.cpython-312.pyc new file mode 100644 index 0000000..a543925 Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/ecdsa/__pycache__/numbertheory.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/ecdsa/__pycache__/rfc6979.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/ecdsa/__pycache__/rfc6979.cpython-312.pyc new file mode 100644 index 0000000..7da3aa0 Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/ecdsa/__pycache__/rfc6979.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/ecdsa/__pycache__/ssh.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/ecdsa/__pycache__/ssh.cpython-312.pyc new file mode 100644 index 0000000..5e5d3a2 Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/ecdsa/__pycache__/ssh.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/ecdsa/__pycache__/test_curves.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/ecdsa/__pycache__/test_curves.cpython-312.pyc new file mode 100644 index 0000000..02fe6a9 Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/ecdsa/__pycache__/test_curves.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/ecdsa/__pycache__/test_der.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/ecdsa/__pycache__/test_der.cpython-312.pyc new file mode 100644 index 0000000..dc3b89f Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/ecdsa/__pycache__/test_der.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/ecdsa/__pycache__/test_ecdh.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/ecdsa/__pycache__/test_ecdh.cpython-312.pyc new file mode 100644 index 0000000..f9be605 Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/ecdsa/__pycache__/test_ecdh.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/ecdsa/__pycache__/test_ecdsa.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/ecdsa/__pycache__/test_ecdsa.cpython-312.pyc new file mode 100644 index 0000000..03b8bb0 Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/ecdsa/__pycache__/test_ecdsa.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/ecdsa/__pycache__/test_eddsa.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/ecdsa/__pycache__/test_eddsa.cpython-312.pyc new file mode 100644 index 0000000..00c3c91 Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/ecdsa/__pycache__/test_eddsa.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/ecdsa/__pycache__/test_ellipticcurve.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/ecdsa/__pycache__/test_ellipticcurve.cpython-312.pyc new file mode 100644 index 0000000..fd7a365 Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/ecdsa/__pycache__/test_ellipticcurve.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/ecdsa/__pycache__/test_jacobi.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/ecdsa/__pycache__/test_jacobi.cpython-312.pyc new file mode 100644 index 0000000..5d142cd Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/ecdsa/__pycache__/test_jacobi.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/ecdsa/__pycache__/test_keys.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/ecdsa/__pycache__/test_keys.cpython-312.pyc new file mode 100644 index 0000000..1f3a01b Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/ecdsa/__pycache__/test_keys.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/ecdsa/__pycache__/test_malformed_sigs.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/ecdsa/__pycache__/test_malformed_sigs.cpython-312.pyc new file mode 100644 index 0000000..c7b8213 Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/ecdsa/__pycache__/test_malformed_sigs.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/ecdsa/__pycache__/test_numbertheory.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/ecdsa/__pycache__/test_numbertheory.cpython-312.pyc new file mode 100644 index 0000000..e39beac Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/ecdsa/__pycache__/test_numbertheory.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/ecdsa/__pycache__/test_pyecdsa.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/ecdsa/__pycache__/test_pyecdsa.cpython-312.pyc new file mode 100644 index 0000000..2c10f79 Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/ecdsa/__pycache__/test_pyecdsa.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/ecdsa/__pycache__/test_rw_lock.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/ecdsa/__pycache__/test_rw_lock.cpython-312.pyc new file mode 100644 index 0000000..2eb533f Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/ecdsa/__pycache__/test_rw_lock.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/ecdsa/__pycache__/test_sha3.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/ecdsa/__pycache__/test_sha3.cpython-312.pyc new file mode 100644 index 0000000..97040b3 Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/ecdsa/__pycache__/test_sha3.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/ecdsa/__pycache__/util.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/ecdsa/__pycache__/util.cpython-312.pyc new file mode 100644 index 0000000..b6bea27 Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/ecdsa/__pycache__/util.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/ecdsa/_compat.py b/code/.venv/lib/python3.12/site-packages/ecdsa/_compat.py new file mode 100644 index 0000000..4558e33 --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/ecdsa/_compat.py @@ -0,0 +1,138 @@ +""" +Common functions for providing cross-python version compatibility. +""" +import sys +import re +import binascii +from six import integer_types + + +def str_idx_as_int(string, index): + """Take index'th byte from string, return as integer""" + val = string[index] + if isinstance(val, integer_types): + return val + return ord(val) + + +if sys.version_info < (3, 0): # pragma: no branch + import platform + + def normalise_bytes(buffer_object): + """Cast the input into array of bytes.""" + # flake8 runs on py3 where `buffer` indeed doesn't exist... + return buffer(buffer_object) # noqa: F821 + + def hmac_compat(ret): + return ret + + if ( + sys.version_info < (2, 7) + or sys.version_info < (2, 7, 4) + or platform.system() == "Java" + ): # pragma: no branch + + def remove_whitespace(text): + """Removes all whitespace from passed in string""" + return re.sub(r"\s+", "", text) + + def compat26_str(val): + return str(val) + + def bit_length(val): + if val == 0: + return 0 + return len(bin(val)) - 2 + + else: + + def remove_whitespace(text): + """Removes all whitespace from passed in string""" + return re.sub(r"\s+", "", text, flags=re.UNICODE) + + def compat26_str(val): + return val + + def bit_length(val): + """Return number of bits necessary to represent an integer.""" + return val.bit_length() + + def b2a_hex(val): + return binascii.b2a_hex(compat26_str(val)) + + def a2b_hex(val): + try: + return bytearray(binascii.a2b_hex(val)) + except Exception as e: + raise ValueError("base16 error: %s" % e) + + def bytes_to_int(val, byteorder): + """Convert bytes to an int.""" + if not val: + return 0 + if byteorder == "big": + return int(b2a_hex(val), 16) + if byteorder == "little": + return int(b2a_hex(val[::-1]), 16) + raise ValueError("Only 'big' and 'little' endian supported") + + def int_to_bytes(val, length=None, byteorder="big"): + """Return number converted to bytes""" + if length is None: + length = byte_length(val) + if byteorder == "big": + return bytearray( + (val >> i) & 0xFF for i in reversed(range(0, length * 8, 8)) + ) + if byteorder == "little": + return bytearray( + (val >> i) & 0xFF for i in range(0, length * 8, 8) + ) + raise ValueError("Only 'big' or 'little' endian supported") + +else: + + def hmac_compat(data): + return data + + def normalise_bytes(buffer_object): + """Cast the input into array of bytes.""" + return memoryview(buffer_object).cast("B") + + def compat26_str(val): + return val + + def remove_whitespace(text): + """Removes all whitespace from passed in string""" + return re.sub(r"\s+", "", text, flags=re.UNICODE) + + def a2b_hex(val): + try: + return bytearray(binascii.a2b_hex(bytearray(val, "ascii"))) + except Exception as e: + raise ValueError("base16 error: %s" % e) + + # pylint: disable=invalid-name + # pylint is stupid here and doesn't notice it's a function, not + # constant + bytes_to_int = int.from_bytes + # pylint: enable=invalid-name + + def bit_length(val): + """Return number of bits necessary to represent an integer.""" + return val.bit_length() + + def int_to_bytes(val, length=None, byteorder="big"): + """Convert integer to bytes.""" + if length is None: + length = byte_length(val) + # for gmpy we need to convert back to native int + if not isinstance(val, int): + val = int(val) + return bytearray(val.to_bytes(length=length, byteorder=byteorder)) + + +def byte_length(val): + """Return number of bytes necessary to represent an integer.""" + length = bit_length(val) + return (length + 7) // 8 diff --git a/code/.venv/lib/python3.12/site-packages/ecdsa/_rwlock.py b/code/.venv/lib/python3.12/site-packages/ecdsa/_rwlock.py new file mode 100644 index 0000000..010e498 --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/ecdsa/_rwlock.py @@ -0,0 +1,86 @@ +# Copyright Mateusz Kobos, (c) 2011 +# https://code.activestate.com/recipes/577803-reader-writer-lock-with-priority-for-writers/ +# released under the MIT licence + +import threading + + +__author__ = "Mateusz Kobos" + + +class RWLock: + """ + Read-Write locking primitive + + Synchronization object used in a solution of so-called second + readers-writers problem. In this problem, many readers can simultaneously + access a share, and a writer has an exclusive access to this share. + Additionally, the following constraints should be met: + 1) no reader should be kept waiting if the share is currently opened for + reading unless a writer is also waiting for the share, + 2) no writer should be kept waiting for the share longer than absolutely + necessary. + + The implementation is based on [1, secs. 4.2.2, 4.2.6, 4.2.7] + with a modification -- adding an additional lock (C{self.__readers_queue}) + -- in accordance with [2]. + + Sources: + [1] A.B. Downey: "The little book of semaphores", Version 2.1.5, 2008 + [2] P.J. Courtois, F. Heymans, D.L. Parnas: + "Concurrent Control with 'Readers' and 'Writers'", + Communications of the ACM, 1971 (via [3]) + [3] http://en.wikipedia.org/wiki/Readers-writers_problem + """ + + def __init__(self): + """ + A lock giving an even higher priority to the writer in certain + cases (see [2] for a discussion). + """ + self.__read_switch = _LightSwitch() + self.__write_switch = _LightSwitch() + self.__no_readers = threading.Lock() + self.__no_writers = threading.Lock() + self.__readers_queue = threading.Lock() + + def reader_acquire(self): + self.__readers_queue.acquire() + self.__no_readers.acquire() + self.__read_switch.acquire(self.__no_writers) + self.__no_readers.release() + self.__readers_queue.release() + + def reader_release(self): + self.__read_switch.release(self.__no_writers) + + def writer_acquire(self): + self.__write_switch.acquire(self.__no_readers) + self.__no_writers.acquire() + + def writer_release(self): + self.__no_writers.release() + self.__write_switch.release(self.__no_readers) + + +class _LightSwitch: + """An auxiliary "light switch"-like object. The first thread turns on the + "switch", the last one turns it off (see [1, sec. 4.2.2] for details).""" + + def __init__(self): + self.__counter = 0 + self.__mutex = threading.Lock() + + def acquire(self, lock): + self.__mutex.acquire() + self.__counter += 1 + if self.__counter == 1: + lock.acquire() + self.__mutex.release() + + def release(self, lock): + self.__mutex.acquire() + self.__counter -= 1 + if self.__counter == 0: + lock.release() + self.__mutex.release() diff --git a/code/.venv/lib/python3.12/site-packages/ecdsa/_sha3.py b/code/.venv/lib/python3.12/site-packages/ecdsa/_sha3.py new file mode 100644 index 0000000..2db0058 --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/ecdsa/_sha3.py @@ -0,0 +1,181 @@ +""" +Implementation of the SHAKE-256 algorithm for Ed448 +""" + +try: + import hashlib + + hashlib.new("shake256").digest(64) + + def shake_256(msg, outlen): + return hashlib.new("shake256", msg).digest(outlen) + +except (TypeError, ValueError): + + from ._compat import bytes_to_int, int_to_bytes + + # From little endian. + def _from_le(s): + return bytes_to_int(s, byteorder="little") + + # Rotate a word x by b places to the left. + def _rol(x, b): + return ((x << b) | (x >> (64 - b))) & (2**64 - 1) + + # Do the SHA-3 state transform on state s. + def _sha3_transform(s): + ROTATIONS = [ + 0, + 1, + 62, + 28, + 27, + 36, + 44, + 6, + 55, + 20, + 3, + 10, + 43, + 25, + 39, + 41, + 45, + 15, + 21, + 8, + 18, + 2, + 61, + 56, + 14, + ] + PERMUTATION = [ + 1, + 6, + 9, + 22, + 14, + 20, + 2, + 12, + 13, + 19, + 23, + 15, + 4, + 24, + 21, + 8, + 16, + 5, + 3, + 18, + 17, + 11, + 7, + 10, + ] + RC = [ + 0x0000000000000001, + 0x0000000000008082, + 0x800000000000808A, + 0x8000000080008000, + 0x000000000000808B, + 0x0000000080000001, + 0x8000000080008081, + 0x8000000000008009, + 0x000000000000008A, + 0x0000000000000088, + 0x0000000080008009, + 0x000000008000000A, + 0x000000008000808B, + 0x800000000000008B, + 0x8000000000008089, + 0x8000000000008003, + 0x8000000000008002, + 0x8000000000000080, + 0x000000000000800A, + 0x800000008000000A, + 0x8000000080008081, + 0x8000000000008080, + 0x0000000080000001, + 0x8000000080008008, + ] + + for rnd in range(0, 24): + # AddColumnParity (Theta) + c = [0] * 5 + d = [0] * 5 + for i in range(0, 25): + c[i % 5] ^= s[i] + for i in range(0, 5): + d[i] = c[(i + 4) % 5] ^ _rol(c[(i + 1) % 5], 1) + for i in range(0, 25): + s[i] ^= d[i % 5] + # RotateWords (Rho) + for i in range(0, 25): + s[i] = _rol(s[i], ROTATIONS[i]) + # PermuteWords (Pi) + t = s[PERMUTATION[0]] + for i in range(0, len(PERMUTATION) - 1): + s[PERMUTATION[i]] = s[PERMUTATION[i + 1]] + s[PERMUTATION[-1]] = t + # NonlinearMixRows (Chi) + for i in range(0, 25, 5): + t = [ + s[i], + s[i + 1], + s[i + 2], + s[i + 3], + s[i + 4], + s[i], + s[i + 1], + ] + for j in range(0, 5): + s[i + j] = t[j] ^ ((~t[j + 1]) & (t[j + 2])) + # AddRoundConstant (Iota) + s[0] ^= RC[rnd] + + # Reinterpret octet array b to word array and XOR it to state s. + def _reinterpret_to_words_and_xor(s, b): + for j in range(0, len(b) // 8): + s[j] ^= _from_le(b[8 * j : 8 * j + 8]) + + # Reinterpret word array w to octet array and return it. + def _reinterpret_to_octets(w): + mp = bytearray() + for j in range(0, len(w)): + mp += int_to_bytes(w[j], 8, byteorder="little") + return mp + + def _sha3_raw(msg, r_w, o_p, e_b): + """Semi-generic SHA-3 implementation""" + r_b = 8 * r_w + s = [0] * 25 + # Handle whole blocks. + idx = 0 + blocks = len(msg) // r_b + for i in range(0, blocks): + _reinterpret_to_words_and_xor(s, msg[idx : idx + r_b]) + idx += r_b + _sha3_transform(s) + # Handle last block padding. + m = bytearray(msg[idx:]) + m.append(o_p) + while len(m) < r_b: + m.append(0) + m[len(m) - 1] |= 128 + # Handle padded last block. + _reinterpret_to_words_and_xor(s, m) + _sha3_transform(s) + # Output. + out = bytearray() + while len(out) < e_b: + out += _reinterpret_to_octets(s[:r_w]) + _sha3_transform(s) + return out[:e_b] + + def shake_256(msg, outlen): + return _sha3_raw(msg, 17, 31, outlen) diff --git a/code/.venv/lib/python3.12/site-packages/ecdsa/_version.py b/code/.venv/lib/python3.12/site-packages/ecdsa/_version.py new file mode 100644 index 0000000..cf329eb --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/ecdsa/_version.py @@ -0,0 +1,21 @@ + +# This file was generated by 'versioneer.py' (0.21) from +# revision-control system data, or from the parent directory name of an +# unpacked source archive. Distribution tarballs contain a pre-generated copy +# of this file. + +import json + +version_json = ''' +{ + "date": "2024-04-08T20:59:55+0200", + "dirty": false, + "error": null, + "full-revisionid": "be70016f8911f79e891a65dcfcb602e5ba866ed3", + "version": "0.19.0" +} +''' # END VERSION_JSON + + +def get_versions(): + return json.loads(version_json) diff --git a/code/.venv/lib/python3.12/site-packages/ecdsa/curves.py b/code/.venv/lib/python3.12/site-packages/ecdsa/curves.py new file mode 100644 index 0000000..38e3a75 --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/ecdsa/curves.py @@ -0,0 +1,590 @@ +from __future__ import division + +from six import PY2 +from . import der, ecdsa, ellipticcurve, eddsa +from .util import orderlen, number_to_string, string_to_number +from ._compat import normalise_bytes, bit_length + + +# orderlen was defined in this module previously, so keep it in __all__, +# will need to mark it as deprecated later +__all__ = [ + "UnknownCurveError", + "orderlen", + "Curve", + "SECP112r1", + "SECP112r2", + "SECP128r1", + "SECP160r1", + "NIST192p", + "NIST224p", + "NIST256p", + "NIST384p", + "NIST521p", + "curves", + "find_curve", + "curve_by_name", + "SECP256k1", + "BRAINPOOLP160r1", + "BRAINPOOLP160t1", + "BRAINPOOLP192r1", + "BRAINPOOLP192t1", + "BRAINPOOLP224r1", + "BRAINPOOLP224t1", + "BRAINPOOLP256r1", + "BRAINPOOLP256t1", + "BRAINPOOLP320r1", + "BRAINPOOLP320t1", + "BRAINPOOLP384r1", + "BRAINPOOLP384t1", + "BRAINPOOLP512r1", + "BRAINPOOLP512t1", + "PRIME_FIELD_OID", + "CHARACTERISTIC_TWO_FIELD_OID", + "Ed25519", + "Ed448", +] + + +PRIME_FIELD_OID = (1, 2, 840, 10045, 1, 1) +CHARACTERISTIC_TWO_FIELD_OID = (1, 2, 840, 10045, 1, 2) + + +class UnknownCurveError(Exception): + pass + + +class Curve: + def __init__(self, name, curve, generator, oid, openssl_name=None): + self.name = name + self.openssl_name = openssl_name # maybe None + self.curve = curve + self.generator = generator + self.order = generator.order() + if isinstance(curve, ellipticcurve.CurveEdTw): + # EdDSA keys are special in that both private and public + # are the same size (as it's defined only with compressed points) + + # +1 for the sign bit and then round up + self.baselen = (bit_length(curve.p()) + 1 + 7) // 8 + self.verifying_key_length = self.baselen + else: + self.baselen = orderlen(self.order) + self.verifying_key_length = 2 * orderlen(curve.p()) + self.signature_length = 2 * self.baselen + self.oid = oid + if oid: + self.encoded_oid = der.encode_oid(*oid) + + def __eq__(self, other): + if isinstance(other, Curve): + return ( + self.curve == other.curve and self.generator == other.generator + ) + return NotImplemented + + def __ne__(self, other): + return not self == other + + def __repr__(self): + return self.name + + def to_der(self, encoding=None, point_encoding="uncompressed"): + """Serialise the curve parameters to binary string. + + :param str encoding: the format to save the curve parameters in. + Default is ``named_curve``, with fallback being the ``explicit`` + if the OID is not set for the curve. + :param str point_encoding: the point encoding of the generator when + explicit curve encoding is used. Ignored for ``named_curve`` + format. + + :return: DER encoded ECParameters structure + :rtype: bytes + """ + if encoding is None: + if self.oid: + encoding = "named_curve" + else: + encoding = "explicit" + + if encoding not in ("named_curve", "explicit"): + raise ValueError( + "Only 'named_curve' and 'explicit' encodings supported" + ) + + if encoding == "named_curve": + if not self.oid: + raise UnknownCurveError( + "Can't encode curve using named_curve encoding without " + "associated curve OID" + ) + return der.encode_oid(*self.oid) + elif isinstance(self.curve, ellipticcurve.CurveEdTw): + assert encoding == "explicit" + raise UnknownCurveError( + "Twisted Edwards curves don't support explicit encoding" + ) + + # encode the ECParameters sequence + curve_p = self.curve.p() + version = der.encode_integer(1) + field_id = der.encode_sequence( + der.encode_oid(*PRIME_FIELD_OID), der.encode_integer(curve_p) + ) + curve = der.encode_sequence( + der.encode_octet_string( + number_to_string(self.curve.a() % curve_p, curve_p) + ), + der.encode_octet_string( + number_to_string(self.curve.b() % curve_p, curve_p) + ), + ) + base = der.encode_octet_string(self.generator.to_bytes(point_encoding)) + order = der.encode_integer(self.generator.order()) + seq_elements = [version, field_id, curve, base, order] + if self.curve.cofactor(): + cofactor = der.encode_integer(self.curve.cofactor()) + seq_elements.append(cofactor) + + return der.encode_sequence(*seq_elements) + + def to_pem(self, encoding=None, point_encoding="uncompressed"): + """ + Serialise the curve parameters to the :term:`PEM` format. + + :param str encoding: the format to save the curve parameters in. + Default is ``named_curve``, with fallback being the ``explicit`` + if the OID is not set for the curve. + :param str point_encoding: the point encoding of the generator when + explicit curve encoding is used. Ignored for ``named_curve`` + format. + + :return: PEM encoded ECParameters structure + :rtype: str + """ + return der.topem( + self.to_der(encoding, point_encoding), "EC PARAMETERS" + ) + + @staticmethod + def from_der(data, valid_encodings=None): + """Decode the curve parameters from DER file. + + :param data: the binary string to decode the parameters from + :type data: :term:`bytes-like object` + :param valid_encodings: set of names of allowed encodings, by default + all (set by passing ``None``), supported ones are ``named_curve`` + and ``explicit`` + :type valid_encodings: :term:`set-like object` + """ + if not valid_encodings: + valid_encodings = set(("named_curve", "explicit")) + if not all(i in ["named_curve", "explicit"] for i in valid_encodings): + raise ValueError( + "Only named_curve and explicit encodings supported" + ) + data = normalise_bytes(data) + if not der.is_sequence(data): + if "named_curve" not in valid_encodings: + raise der.UnexpectedDER( + "named_curve curve parameters not allowed" + ) + oid, empty = der.remove_object(data) + if empty: + raise der.UnexpectedDER("Unexpected data after OID") + return find_curve(oid) + + if "explicit" not in valid_encodings: + raise der.UnexpectedDER("explicit curve parameters not allowed") + + seq, empty = der.remove_sequence(data) + if empty: + raise der.UnexpectedDER( + "Unexpected data after ECParameters structure" + ) + # decode the ECParameters sequence + version, rest = der.remove_integer(seq) + if version != 1: + raise der.UnexpectedDER("Unknown parameter encoding format") + field_id, rest = der.remove_sequence(rest) + curve, rest = der.remove_sequence(rest) + base_bytes, rest = der.remove_octet_string(rest) + order, rest = der.remove_integer(rest) + cofactor = None + if rest: + # the ASN.1 specification of ECParameters allows for future + # extensions of the sequence, so ignore the remaining bytes + cofactor, _ = der.remove_integer(rest) + + # decode the ECParameters.fieldID sequence + field_type, rest = der.remove_object(field_id) + if field_type == CHARACTERISTIC_TWO_FIELD_OID: + raise UnknownCurveError("Characteristic 2 curves unsupported") + if field_type != PRIME_FIELD_OID: + raise UnknownCurveError( + "Unknown field type: {0}".format(field_type) + ) + prime, empty = der.remove_integer(rest) + if empty: + raise der.UnexpectedDER( + "Unexpected data after ECParameters.fieldID.Prime-p element" + ) + + # decode the ECParameters.curve sequence + curve_a_bytes, rest = der.remove_octet_string(curve) + curve_b_bytes, rest = der.remove_octet_string(rest) + # seed can be defined here, but we don't parse it, so ignore `rest` + + curve_a = string_to_number(curve_a_bytes) + curve_b = string_to_number(curve_b_bytes) + + curve_fp = ellipticcurve.CurveFp(prime, curve_a, curve_b, cofactor) + + # decode the ECParameters.base point + + base = ellipticcurve.PointJacobi.from_bytes( + curve_fp, + base_bytes, + valid_encodings=("uncompressed", "compressed", "hybrid"), + order=order, + generator=True, + ) + tmp_curve = Curve("unknown", curve_fp, base, None) + + # if the curve matches one of the well-known ones, use the well-known + # one in preference, as it will have the OID and name associated + for i in curves: + if tmp_curve == i: + return i + return tmp_curve + + @classmethod + def from_pem(cls, string, valid_encodings=None): + """Decode the curve parameters from PEM file. + + :param str string: the text string to decode the parameters from + :param valid_encodings: set of names of allowed encodings, by default + all (set by passing ``None``), supported ones are ``named_curve`` + and ``explicit`` + :type valid_encodings: :term:`set-like object` + """ + if not PY2 and isinstance(string, str): # pragma: no branch + string = string.encode() + + ec_param_index = string.find(b"-----BEGIN EC PARAMETERS-----") + if ec_param_index == -1: + raise der.UnexpectedDER("EC PARAMETERS PEM header not found") + + return cls.from_der( + der.unpem(string[ec_param_index:]), valid_encodings + ) + + +# the SEC curves +SECP112r1 = Curve( + "SECP112r1", + ecdsa.curve_112r1, + ecdsa.generator_112r1, + (1, 3, 132, 0, 6), + "secp112r1", +) + + +SECP112r2 = Curve( + "SECP112r2", + ecdsa.curve_112r2, + ecdsa.generator_112r2, + (1, 3, 132, 0, 7), + "secp112r2", +) + + +SECP128r1 = Curve( + "SECP128r1", + ecdsa.curve_128r1, + ecdsa.generator_128r1, + (1, 3, 132, 0, 28), + "secp128r1", +) + + +SECP160r1 = Curve( + "SECP160r1", + ecdsa.curve_160r1, + ecdsa.generator_160r1, + (1, 3, 132, 0, 8), + "secp160r1", +) + + +# the NIST curves +NIST192p = Curve( + "NIST192p", + ecdsa.curve_192, + ecdsa.generator_192, + (1, 2, 840, 10045, 3, 1, 1), + "prime192v1", +) + + +NIST224p = Curve( + "NIST224p", + ecdsa.curve_224, + ecdsa.generator_224, + (1, 3, 132, 0, 33), + "secp224r1", +) + + +NIST256p = Curve( + "NIST256p", + ecdsa.curve_256, + ecdsa.generator_256, + (1, 2, 840, 10045, 3, 1, 7), + "prime256v1", +) + + +NIST384p = Curve( + "NIST384p", + ecdsa.curve_384, + ecdsa.generator_384, + (1, 3, 132, 0, 34), + "secp384r1", +) + + +NIST521p = Curve( + "NIST521p", + ecdsa.curve_521, + ecdsa.generator_521, + (1, 3, 132, 0, 35), + "secp521r1", +) + + +SECP256k1 = Curve( + "SECP256k1", + ecdsa.curve_secp256k1, + ecdsa.generator_secp256k1, + (1, 3, 132, 0, 10), + "secp256k1", +) + + +BRAINPOOLP160r1 = Curve( + "BRAINPOOLP160r1", + ecdsa.curve_brainpoolp160r1, + ecdsa.generator_brainpoolp160r1, + (1, 3, 36, 3, 3, 2, 8, 1, 1, 1), + "brainpoolP160r1", +) + + +BRAINPOOLP160t1 = Curve( + "BRAINPOOLP160t1", + ecdsa.curve_brainpoolp160t1, + ecdsa.generator_brainpoolp160t1, + (1, 3, 36, 3, 3, 2, 8, 1, 1, 2), + "brainpoolP160t1", +) + + +BRAINPOOLP192r1 = Curve( + "BRAINPOOLP192r1", + ecdsa.curve_brainpoolp192r1, + ecdsa.generator_brainpoolp192r1, + (1, 3, 36, 3, 3, 2, 8, 1, 1, 3), + "brainpoolP192r1", +) + + +BRAINPOOLP192t1 = Curve( + "BRAINPOOLP192t1", + ecdsa.curve_brainpoolp192t1, + ecdsa.generator_brainpoolp192t1, + (1, 3, 36, 3, 3, 2, 8, 1, 1, 4), + "brainpoolP192t1", +) + + +BRAINPOOLP224r1 = Curve( + "BRAINPOOLP224r1", + ecdsa.curve_brainpoolp224r1, + ecdsa.generator_brainpoolp224r1, + (1, 3, 36, 3, 3, 2, 8, 1, 1, 5), + "brainpoolP224r1", +) + + +BRAINPOOLP224t1 = Curve( + "BRAINPOOLP224t1", + ecdsa.curve_brainpoolp224t1, + ecdsa.generator_brainpoolp224t1, + (1, 3, 36, 3, 3, 2, 8, 1, 1, 6), + "brainpoolP224t1", +) + + +BRAINPOOLP256r1 = Curve( + "BRAINPOOLP256r1", + ecdsa.curve_brainpoolp256r1, + ecdsa.generator_brainpoolp256r1, + (1, 3, 36, 3, 3, 2, 8, 1, 1, 7), + "brainpoolP256r1", +) + + +BRAINPOOLP256t1 = Curve( + "BRAINPOOLP256t1", + ecdsa.curve_brainpoolp256t1, + ecdsa.generator_brainpoolp256t1, + (1, 3, 36, 3, 3, 2, 8, 1, 1, 8), + "brainpoolP256t1", +) + + +BRAINPOOLP320r1 = Curve( + "BRAINPOOLP320r1", + ecdsa.curve_brainpoolp320r1, + ecdsa.generator_brainpoolp320r1, + (1, 3, 36, 3, 3, 2, 8, 1, 1, 9), + "brainpoolP320r1", +) + + +BRAINPOOLP320t1 = Curve( + "BRAINPOOLP320t1", + ecdsa.curve_brainpoolp320t1, + ecdsa.generator_brainpoolp320t1, + (1, 3, 36, 3, 3, 2, 8, 1, 1, 10), + "brainpoolP320t1", +) + + +BRAINPOOLP384r1 = Curve( + "BRAINPOOLP384r1", + ecdsa.curve_brainpoolp384r1, + ecdsa.generator_brainpoolp384r1, + (1, 3, 36, 3, 3, 2, 8, 1, 1, 11), + "brainpoolP384r1", +) + + +BRAINPOOLP384t1 = Curve( + "BRAINPOOLP384t1", + ecdsa.curve_brainpoolp384t1, + ecdsa.generator_brainpoolp384t1, + (1, 3, 36, 3, 3, 2, 8, 1, 1, 12), + "brainpoolP384t1", +) + + +BRAINPOOLP512r1 = Curve( + "BRAINPOOLP512r1", + ecdsa.curve_brainpoolp512r1, + ecdsa.generator_brainpoolp512r1, + (1, 3, 36, 3, 3, 2, 8, 1, 1, 13), + "brainpoolP512r1", +) + + +BRAINPOOLP512t1 = Curve( + "BRAINPOOLP512t1", + ecdsa.curve_brainpoolp512t1, + ecdsa.generator_brainpoolp512t1, + (1, 3, 36, 3, 3, 2, 8, 1, 1, 14), + "brainpoolP512t1", +) + + +Ed25519 = Curve( + "Ed25519", + eddsa.curve_ed25519, + eddsa.generator_ed25519, + (1, 3, 101, 112), +) + + +Ed448 = Curve( + "Ed448", + eddsa.curve_ed448, + eddsa.generator_ed448, + (1, 3, 101, 113), +) + + +# no order in particular, but keep previously added curves first +curves = [ + NIST192p, + NIST224p, + NIST256p, + NIST384p, + NIST521p, + SECP256k1, + BRAINPOOLP160r1, + BRAINPOOLP192r1, + BRAINPOOLP224r1, + BRAINPOOLP256r1, + BRAINPOOLP320r1, + BRAINPOOLP384r1, + BRAINPOOLP512r1, + SECP112r1, + SECP112r2, + SECP128r1, + SECP160r1, + Ed25519, + Ed448, + BRAINPOOLP160t1, + BRAINPOOLP192t1, + BRAINPOOLP224t1, + BRAINPOOLP256t1, + BRAINPOOLP320t1, + BRAINPOOLP384t1, + BRAINPOOLP512t1, +] + + +def find_curve(oid_curve): + """Select a curve based on its OID + + :param tuple[int,...] oid_curve: ASN.1 Object Identifier of the + curve to return, like ``(1, 2, 840, 10045, 3, 1, 7)`` for ``NIST256p``. + + :raises UnknownCurveError: When the oid doesn't match any of the supported + curves + + :rtype: ~ecdsa.curves.Curve + """ + for c in curves: + if c.oid == oid_curve: + return c + raise UnknownCurveError( + "I don't know about the curve with oid %s." + "I only know about these: %s" % (oid_curve, [c.name for c in curves]) + ) + + +def curve_by_name(name): + """Select a curve based on its name. + + Returns a :py:class:`~ecdsa.curves.Curve` object with a ``name`` name. + Note that ``name`` is case-sensitve. + + :param str name: Name of the curve to return, like ``NIST256p`` or + ``prime256v1`` + + :raises UnknownCurveError: When the name doesn't match any of the supported + curves + + :rtype: ~ecdsa.curves.Curve + """ + for c in curves: + if name == c.name or (c.openssl_name and name == c.openssl_name): + return c + raise UnknownCurveError( + "Curve with name {0!r} unknown, only curves supported: {1}".format( + name, [c.name for c in curves] + ) + ) diff --git a/code/.venv/lib/python3.12/site-packages/ecdsa/der.py b/code/.venv/lib/python3.12/site-packages/ecdsa/der.py new file mode 100644 index 0000000..b291485 --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/ecdsa/der.py @@ -0,0 +1,409 @@ +from __future__ import division + +import binascii +import base64 +import warnings +from itertools import chain +from six import int2byte, text_type +from ._compat import compat26_str, str_idx_as_int + + +class UnexpectedDER(Exception): + pass + + +def encode_constructed(tag, value): + return int2byte(0xA0 + tag) + encode_length(len(value)) + value + + +def encode_integer(r): + assert r >= 0 # can't support negative numbers yet + h = ("%x" % r).encode() + if len(h) % 2: + h = b"0" + h + s = binascii.unhexlify(h) + num = str_idx_as_int(s, 0) + if num <= 0x7F: + return b"\x02" + encode_length(len(s)) + s + else: + # DER integers are two's complement, so if the first byte is + # 0x80-0xff then we need an extra 0x00 byte to prevent it from + # looking negative. + return b"\x02" + encode_length(len(s) + 1) + b"\x00" + s + + +# sentry object to check if an argument was specified (used to detect +# deprecated calling convention) +_sentry = object() + + +def encode_bitstring(s, unused=_sentry): + """ + Encode a binary string as a BIT STRING using :term:`DER` encoding. + + Note, because there is no native Python object that can encode an actual + bit string, this function only accepts byte strings as the `s` argument. + The byte string is the actual bit string that will be encoded, padded + on the right (least significant bits, looking from big endian perspective) + to the first full byte. If the bit string has a bit length that is multiple + of 8, then the padding should not be included. For correct DER encoding + the padding bits MUST be set to 0. + + Number of bits of padding need to be provided as the `unused` parameter. + In case they are specified as None, it means the number of unused bits + is already encoded in the string as the first byte. + + The deprecated call convention specifies just the `s` parameters and + encodes the number of unused bits as first parameter (same convention + as with None). + + Empty string must be encoded with `unused` specified as 0. + + Future version of python-ecdsa will make specifying the `unused` argument + mandatory. + + :param s: bytes to encode + :type s: bytes like object + :param unused: number of bits at the end of `s` that are unused, must be + between 0 and 7 (inclusive) + :type unused: int or None + + :raises ValueError: when `unused` is too large or too small + + :return: `s` encoded using DER + :rtype: bytes + """ + encoded_unused = b"" + len_extra = 0 + if unused is _sentry: + warnings.warn( + "Legacy call convention used, unused= needs to be specified", + DeprecationWarning, + ) + elif unused is not None: + if not 0 <= unused <= 7: + raise ValueError("unused must be integer between 0 and 7") + if unused: + if not s: + raise ValueError("unused is non-zero but s is empty") + last = str_idx_as_int(s, -1) + if last & (2**unused - 1): + raise ValueError("unused bits must be zeros in DER") + encoded_unused = int2byte(unused) + len_extra = 1 + return b"\x03" + encode_length(len(s) + len_extra) + encoded_unused + s + + +def encode_octet_string(s): + return b"\x04" + encode_length(len(s)) + s + + +def encode_oid(first, second, *pieces): + assert 0 <= first < 2 and 0 <= second <= 39 or first == 2 and 0 <= second + body = b"".join( + chain( + [encode_number(40 * first + second)], + (encode_number(p) for p in pieces), + ) + ) + return b"\x06" + encode_length(len(body)) + body + + +def encode_sequence(*encoded_pieces): + total_len = sum([len(p) for p in encoded_pieces]) + return b"\x30" + encode_length(total_len) + b"".join(encoded_pieces) + + +def encode_number(n): + b128_digits = [] + while n: + b128_digits.insert(0, (n & 0x7F) | 0x80) + n = n >> 7 + if not b128_digits: + b128_digits.append(0) + b128_digits[-1] &= 0x7F + return b"".join([int2byte(d) for d in b128_digits]) + + +def is_sequence(string): + return string and string[:1] == b"\x30" + + +def remove_constructed(string): + s0 = str_idx_as_int(string, 0) + if (s0 & 0xE0) != 0xA0: + raise UnexpectedDER( + "wanted type 'constructed tag' (0xa0-0xbf), got 0x%02x" % s0 + ) + tag = s0 & 0x1F + length, llen = read_length(string[1:]) + body = string[1 + llen : 1 + llen + length] + rest = string[1 + llen + length :] + return tag, body, rest + + +def remove_sequence(string): + if not string: + raise UnexpectedDER("Empty string does not encode a sequence") + if string[:1] != b"\x30": + n = str_idx_as_int(string, 0) + raise UnexpectedDER("wanted type 'sequence' (0x30), got 0x%02x" % n) + length, lengthlength = read_length(string[1:]) + if length > len(string) - 1 - lengthlength: + raise UnexpectedDER("Length longer than the provided buffer") + endseq = 1 + lengthlength + length + return string[1 + lengthlength : endseq], string[endseq:] + + +def remove_octet_string(string): + if string[:1] != b"\x04": + n = str_idx_as_int(string, 0) + raise UnexpectedDER("wanted type 'octetstring' (0x04), got 0x%02x" % n) + length, llen = read_length(string[1:]) + body = string[1 + llen : 1 + llen + length] + rest = string[1 + llen + length :] + return body, rest + + +def remove_object(string): + if not string: + raise UnexpectedDER( + "Empty string does not encode an object identifier" + ) + if string[:1] != b"\x06": + n = str_idx_as_int(string, 0) + raise UnexpectedDER("wanted type 'object' (0x06), got 0x%02x" % n) + length, lengthlength = read_length(string[1:]) + body = string[1 + lengthlength : 1 + lengthlength + length] + rest = string[1 + lengthlength + length :] + if not body: + raise UnexpectedDER("Empty object identifier") + if len(body) != length: + raise UnexpectedDER( + "Length of object identifier longer than the provided buffer" + ) + numbers = [] + while body: + n, ll = read_number(body) + numbers.append(n) + body = body[ll:] + n0 = numbers.pop(0) + if n0 < 80: + first = n0 // 40 + else: + first = 2 + second = n0 - (40 * first) + numbers.insert(0, first) + numbers.insert(1, second) + return tuple(numbers), rest + + +def remove_integer(string): + if not string: + raise UnexpectedDER( + "Empty string is an invalid encoding of an integer" + ) + if string[:1] != b"\x02": + n = str_idx_as_int(string, 0) + raise UnexpectedDER("wanted type 'integer' (0x02), got 0x%02x" % n) + length, llen = read_length(string[1:]) + if length > len(string) - 1 - llen: + raise UnexpectedDER("Length longer than provided buffer") + if length == 0: + raise UnexpectedDER("0-byte long encoding of integer") + numberbytes = string[1 + llen : 1 + llen + length] + rest = string[1 + llen + length :] + msb = str_idx_as_int(numberbytes, 0) + if not msb < 0x80: + raise UnexpectedDER("Negative integers are not supported") + # check if the encoding is the minimal one (DER requirement) + if length > 1 and not msb: + # leading zero byte is allowed if the integer would have been + # considered a negative number otherwise + smsb = str_idx_as_int(numberbytes, 1) + if smsb < 0x80: + raise UnexpectedDER( + "Invalid encoding of integer, unnecessary " + "zero padding bytes" + ) + return int(binascii.hexlify(numberbytes), 16), rest + + +def read_number(string): + number = 0 + llen = 0 + if str_idx_as_int(string, 0) == 0x80: + raise UnexpectedDER("Non minimal encoding of OID subidentifier") + # base-128 big endian, with most significant bit set in all but the last + # byte + while True: + if llen >= len(string): + raise UnexpectedDER("ran out of length bytes") + number = number << 7 + d = str_idx_as_int(string, llen) + number += d & 0x7F + llen += 1 + if not d & 0x80: + break + return number, llen + + +def encode_length(l): + assert l >= 0 + if l < 0x80: + return int2byte(l) + s = ("%x" % l).encode() + if len(s) % 2: + s = b"0" + s + s = binascii.unhexlify(s) + llen = len(s) + return int2byte(0x80 | llen) + s + + +def read_length(string): + if not string: + raise UnexpectedDER("Empty string can't encode valid length value") + num = str_idx_as_int(string, 0) + if not (num & 0x80): + # short form + return (num & 0x7F), 1 + # else long-form: b0&0x7f is number of additional base256 length bytes, + # big-endian + llen = num & 0x7F + if not llen: + raise UnexpectedDER("Invalid length encoding, length of length is 0") + if llen > len(string) - 1: + raise UnexpectedDER("Length of length longer than provided buffer") + # verify that the encoding is minimal possible (DER requirement) + msb = str_idx_as_int(string, 1) + if not msb or llen == 1 and msb < 0x80: + raise UnexpectedDER("Not minimal encoding of length") + return int(binascii.hexlify(string[1 : 1 + llen]), 16), 1 + llen + + +def remove_bitstring(string, expect_unused=_sentry): + """ + Remove a BIT STRING object from `string` following :term:`DER`. + + The `expect_unused` can be used to specify if the bit string should + have the amount of unused bits decoded or not. If it's an integer, any + read BIT STRING that has number of unused bits different from specified + value will cause UnexpectedDER exception to be raised (this is especially + useful when decoding BIT STRINGS that have DER encoded object in them; + DER encoding is byte oriented, so the unused bits will always equal 0). + + If the `expect_unused` is specified as None, the first element returned + will be a tuple, with the first value being the extracted bit string + while the second value will be the decoded number of unused bits. + + If the `expect_unused` is unspecified, the decoding of byte with + number of unused bits will not be attempted and the bit string will be + returned as-is, the callee will be required to decode it and verify its + correctness. + + Future version of python will require the `expected_unused` parameter + to be specified. + + :param string: string of bytes to extract the BIT STRING from + :type string: bytes like object + :param expect_unused: number of bits that should be unused in the BIT + STRING, or None, to return it to caller + :type expect_unused: int or None + + :raises UnexpectedDER: when the encoding does not follow DER. + + :return: a tuple with first element being the extracted bit string and + the second being the remaining bytes in the string (if any); if the + `expect_unused` is specified as None, the first element of the returned + tuple will be a tuple itself, with first element being the bit string + as bytes and the second element being the number of unused bits at the + end of the byte array as an integer + :rtype: tuple + """ + if not string: + raise UnexpectedDER("Empty string does not encode a bitstring") + if expect_unused is _sentry: + warnings.warn( + "Legacy call convention used, expect_unused= needs to be" + " specified", + DeprecationWarning, + ) + num = str_idx_as_int(string, 0) + if string[:1] != b"\x03": + raise UnexpectedDER("wanted bitstring (0x03), got 0x%02x" % num) + length, llen = read_length(string[1:]) + if not length: + raise UnexpectedDER("Invalid length of bit string, can't be 0") + body = string[1 + llen : 1 + llen + length] + rest = string[1 + llen + length :] + if expect_unused is not _sentry: + unused = str_idx_as_int(body, 0) + if not 0 <= unused <= 7: + raise UnexpectedDER("Invalid encoding of unused bits") + if expect_unused is not None and expect_unused != unused: + raise UnexpectedDER("Unexpected number of unused bits") + body = body[1:] + if unused: + if not body: + raise UnexpectedDER("Invalid encoding of empty bit string") + last = str_idx_as_int(body, -1) + # verify that all the unused bits are set to zero (DER requirement) + if last & (2**unused - 1): + raise UnexpectedDER("Non zero padding bits in bit string") + if expect_unused is None: + body = (body, unused) + return body, rest + + +# SEQUENCE([1, STRING(secexp), cont[0], OBJECT(curvename), cont[1], BINTSTRING) + + +# signatures: (from RFC3279) +# ansi-X9-62 OBJECT IDENTIFIER ::= { +# iso(1) member-body(2) us(840) 10045 } +# +# id-ecSigType OBJECT IDENTIFIER ::= { +# ansi-X9-62 signatures(4) } +# ecdsa-with-SHA1 OBJECT IDENTIFIER ::= { +# id-ecSigType 1 } +# so 1,2,840,10045,4,1 +# so 0x42, .. .. + +# Ecdsa-Sig-Value ::= SEQUENCE { +# r INTEGER, +# s INTEGER } + +# id-public-key-type OBJECT IDENTIFIER ::= { ansi-X9.62 2 } +# +# id-ecPublicKey OBJECT IDENTIFIER ::= { id-publicKeyType 1 } + +# I think the secp224r1 identifier is (t=06,l=05,v=2b81040021) +# secp224r1 OBJECT IDENTIFIER ::= { +# iso(1) identified-organization(3) certicom(132) curve(0) 33 } +# and the secp384r1 is (t=06,l=05,v=2b81040022) +# secp384r1 OBJECT IDENTIFIER ::= { +# iso(1) identified-organization(3) certicom(132) curve(0) 34 } + + +def unpem(pem): + if isinstance(pem, text_type): # pragma: no branch + pem = pem.encode() + + d = b"".join( + [ + l.strip() + for l in pem.split(b"\n") + if l and not l.startswith(b"-----") + ] + ) + return base64.b64decode(d) + + +def topem(der, name): + b64 = base64.b64encode(compat26_str(der)) + lines = [("-----BEGIN %s-----\n" % name).encode()] + lines.extend( + [b64[start : start + 76] + b"\n" for start in range(0, len(b64), 76)] + ) + lines.append(("-----END %s-----\n" % name).encode()) + return b"".join(lines) diff --git a/code/.venv/lib/python3.12/site-packages/ecdsa/ecdh.py b/code/.venv/lib/python3.12/site-packages/ecdsa/ecdh.py new file mode 100644 index 0000000..7f697d9 --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/ecdsa/ecdh.py @@ -0,0 +1,336 @@ +""" +Class for performing Elliptic-curve Diffie-Hellman (ECDH) operations. +""" + +from .util import number_to_string +from .ellipticcurve import INFINITY +from .keys import SigningKey, VerifyingKey + + +__all__ = [ + "ECDH", + "NoKeyError", + "NoCurveError", + "InvalidCurveError", + "InvalidSharedSecretError", +] + + +class NoKeyError(Exception): + """ECDH. Key not found but it is needed for operation.""" + + pass + + +class NoCurveError(Exception): + """ECDH. Curve not set but it is needed for operation.""" + + pass + + +class InvalidCurveError(Exception): + """ + ECDH. Raised in case the public and private keys use different curves. + """ + + pass + + +class InvalidSharedSecretError(Exception): + """ECDH. Raised in case the shared secret we obtained is an INFINITY.""" + + pass + + +class ECDH(object): + """ + Elliptic-curve Diffie-Hellman (ECDH). A key agreement protocol. + + Allows two parties, each having an elliptic-curve public-private key + pair, to establish a shared secret over an insecure channel + """ + + def __init__(self, curve=None, private_key=None, public_key=None): + """ + ECDH init. + + Call can be initialised without parameters, then the first operation + (loading either key) will set the used curve. + All parameters must be ultimately set before shared secret + calculation will be allowed. + + :param curve: curve for operations + :type curve: Curve + :param private_key: `my` private key for ECDH + :type private_key: SigningKey + :param public_key: `their` public key for ECDH + :type public_key: VerifyingKey + """ + self.curve = curve + self.private_key = None + self.public_key = None + if private_key: + self.load_private_key(private_key) + if public_key: + self.load_received_public_key(public_key) + + def _get_shared_secret(self, remote_public_key): + if not self.private_key: + raise NoKeyError( + "Private key needs to be set to create shared secret" + ) + if not self.public_key: + raise NoKeyError( + "Public key needs to be set to create shared secret" + ) + if not ( + self.private_key.curve == self.curve == remote_public_key.curve + ): + raise InvalidCurveError( + "Curves for public key and private key is not equal." + ) + + # shared secret = PUBKEYtheirs * PRIVATEKEYours + result = ( + remote_public_key.pubkey.point + * self.private_key.privkey.secret_multiplier + ) + if result == INFINITY: + raise InvalidSharedSecretError("Invalid shared secret (INFINITY).") + + return result.x() + + def set_curve(self, key_curve): + """ + Set the working curve for ecdh operations. + + :param key_curve: curve from `curves` module + :type key_curve: Curve + """ + self.curve = key_curve + + def generate_private_key(self): + """ + Generate local private key for ecdh operation with curve that was set. + + :raises NoCurveError: Curve must be set before key generation. + + :return: public (verifying) key from this private key. + :rtype: VerifyingKey + """ + if not self.curve: + raise NoCurveError("Curve must be set prior to key generation.") + return self.load_private_key(SigningKey.generate(curve=self.curve)) + + def load_private_key(self, private_key): + """ + Load private key from SigningKey (keys.py) object. + + Needs to have the same curve as was set with set_curve method. + If curve is not set - it sets from this SigningKey + + :param private_key: Initialised SigningKey class + :type private_key: SigningKey + + :raises InvalidCurveError: private_key curve not the same as self.curve + + :return: public (verifying) key from this private key. + :rtype: VerifyingKey + """ + if not self.curve: + self.curve = private_key.curve + if self.curve != private_key.curve: + raise InvalidCurveError("Curve mismatch.") + self.private_key = private_key + return self.private_key.get_verifying_key() + + def load_private_key_bytes(self, private_key): + """ + Load private key from byte string. + + Uses current curve and checks if the provided key matches + the curve of ECDH key agreement. + Key loads via from_string method of SigningKey class + + :param private_key: private key in bytes string format + :type private_key: :term:`bytes-like object` + + :raises NoCurveError: Curve must be set before loading. + + :return: public (verifying) key from this private key. + :rtype: VerifyingKey + """ + if not self.curve: + raise NoCurveError("Curve must be set prior to key load.") + return self.load_private_key( + SigningKey.from_string(private_key, curve=self.curve) + ) + + def load_private_key_der(self, private_key_der): + """ + Load private key from DER byte string. + + Compares the curve of the DER-encoded key with the ECDH set curve, + uses the former if unset. + + Note, the only DER format supported is the RFC5915 + Look at keys.py:SigningKey.from_der() + + :param private_key_der: string with the DER encoding of private ECDSA + key + :type private_key_der: string + + :raises InvalidCurveError: private_key curve not the same as self.curve + + :return: public (verifying) key from this private key. + :rtype: VerifyingKey + """ + return self.load_private_key(SigningKey.from_der(private_key_der)) + + def load_private_key_pem(self, private_key_pem): + """ + Load private key from PEM string. + + Compares the curve of the DER-encoded key with the ECDH set curve, + uses the former if unset. + + Note, the only PEM format supported is the RFC5915 + Look at keys.py:SigningKey.from_pem() + it needs to have `EC PRIVATE KEY` section + + :param private_key_pem: string with PEM-encoded private ECDSA key + :type private_key_pem: string + + :raises InvalidCurveError: private_key curve not the same as self.curve + + :return: public (verifying) key from this private key. + :rtype: VerifyingKey + """ + return self.load_private_key(SigningKey.from_pem(private_key_pem)) + + def get_public_key(self): + """ + Provides a public key that matches the local private key. + + Needs to be sent to the remote party. + + :return: public (verifying) key from local private key. + :rtype: VerifyingKey + """ + return self.private_key.get_verifying_key() + + def load_received_public_key(self, public_key): + """ + Load public key from VerifyingKey (keys.py) object. + + Needs to have the same curve as set as current for ecdh operation. + If curve is not set - it sets it from VerifyingKey. + + :param public_key: Initialised VerifyingKey class + :type public_key: VerifyingKey + + :raises InvalidCurveError: public_key curve not the same as self.curve + """ + if not self.curve: + self.curve = public_key.curve + if self.curve != public_key.curve: + raise InvalidCurveError("Curve mismatch.") + self.public_key = public_key + + def load_received_public_key_bytes( + self, public_key_str, valid_encodings=None + ): + """ + Load public key from byte string. + + Uses current curve and checks if key length corresponds to + the current curve. + Key loads via from_string method of VerifyingKey class + + :param public_key_str: public key in bytes string format + :type public_key_str: :term:`bytes-like object` + :param valid_encodings: list of acceptable point encoding formats, + supported ones are: :term:`uncompressed`, :term:`compressed`, + :term:`hybrid`, and :term:`raw encoding` (specified with ``raw`` + name). All formats by default (specified with ``None``). + :type valid_encodings: :term:`set-like object` + """ + return self.load_received_public_key( + VerifyingKey.from_string( + public_key_str, self.curve, valid_encodings + ) + ) + + def load_received_public_key_der(self, public_key_der): + """ + Load public key from DER byte string. + + Compares the curve of the DER-encoded key with the ECDH set curve, + uses the former if unset. + + Note, the only DER format supported is the RFC5912 + Look at keys.py:VerifyingKey.from_der() + + :param public_key_der: string with the DER encoding of public ECDSA key + :type public_key_der: string + + :raises InvalidCurveError: public_key curve not the same as self.curve + """ + return self.load_received_public_key( + VerifyingKey.from_der(public_key_der) + ) + + def load_received_public_key_pem(self, public_key_pem): + """ + Load public key from PEM string. + + Compares the curve of the PEM-encoded key with the ECDH set curve, + uses the former if unset. + + Note, the only PEM format supported is the RFC5912 + Look at keys.py:VerifyingKey.from_pem() + + :param public_key_pem: string with PEM-encoded public ECDSA key + :type public_key_pem: string + + :raises InvalidCurveError: public_key curve not the same as self.curve + """ + return self.load_received_public_key( + VerifyingKey.from_pem(public_key_pem) + ) + + def generate_sharedsecret_bytes(self): + """ + Generate shared secret from local private key and remote public key. + + The objects needs to have both private key and received public key + before generation is allowed. + + :raises InvalidCurveError: public_key curve not the same as self.curve + :raises NoKeyError: public_key or private_key is not set + + :return: shared secret + :rtype: bytes + """ + return number_to_string( + self.generate_sharedsecret(), self.private_key.curve.curve.p() + ) + + def generate_sharedsecret(self): + """ + Generate shared secret from local private key and remote public key. + + The objects needs to have both private key and received public key + before generation is allowed. + + It's the same for local and remote party, + shared secret(local private key, remote public key) == + shared secret(local public key, remote private key) + + :raises InvalidCurveError: public_key curve not the same as self.curve + :raises NoKeyError: public_key or private_key is not set + + :return: shared secret + :rtype: int + """ + return self._get_shared_secret(self.public_key) diff --git a/code/.venv/lib/python3.12/site-packages/ecdsa/ecdsa.py b/code/.venv/lib/python3.12/site-packages/ecdsa/ecdsa.py new file mode 100644 index 0000000..9284ace --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/ecdsa/ecdsa.py @@ -0,0 +1,1094 @@ +#! /usr/bin/env python + +""" +Low level implementation of Elliptic-Curve Digital Signatures. + +.. note :: + You're most likely looking for the :py:class:`~ecdsa.keys` module. + This is a low-level implementation of the ECDSA that operates on + integers, not byte strings. + +NOTE: This a low level implementation of ECDSA, for normal applications +you should be looking at the keys.py module. + +Classes and methods for elliptic-curve signatures: +private keys, public keys, signatures, +and definitions of prime-modulus curves. + +Example: + +.. code-block:: python + + # (In real-life applications, you would probably want to + # protect against defects in SystemRandom.) + from random import SystemRandom + randrange = SystemRandom().randrange + + # Generate a public/private key pair using the NIST Curve P-192: + + g = generator_192 + n = g.order() + secret = randrange( 1, n ) + pubkey = Public_key( g, g * secret ) + privkey = Private_key( pubkey, secret ) + + # Signing a hash value: + + hash = randrange( 1, n ) + signature = privkey.sign( hash, randrange( 1, n ) ) + + # Verifying a signature for a hash value: + + if pubkey.verifies( hash, signature ): + print_("Demo verification succeeded.") + else: + print_("*** Demo verification failed.") + + # Verification fails if the hash value is modified: + + if pubkey.verifies( hash-1, signature ): + print_("**** Demo verification failed to reject tampered hash.") + else: + print_("Demo verification correctly rejected tampered hash.") + +Revision history: + 2005.12.31 - Initial version. + + 2008.11.25 - Substantial revisions introducing new classes. + + 2009.05.16 - Warn against using random.randrange in real applications. + + 2009.05.17 - Use random.SystemRandom by default. + +Originally written in 2005 by Peter Pearson and placed in the public domain, +modified as part of the python-ecdsa package. +""" + +import warnings +from six import int2byte +from . import ellipticcurve +from . import numbertheory +from .util import bit_length +from ._compat import remove_whitespace + + +class RSZeroError(RuntimeError): + pass + + +class InvalidPointError(RuntimeError): + pass + + +class Signature(object): + """ + ECDSA signature. + + :ivar int r: the ``r`` element of the ECDSA signature + :ivar int s: the ``s`` element of the ECDSA signature + """ + + def __init__(self, r, s): + self.r = r + self.s = s + + def recover_public_keys(self, hash, generator): + """ + Returns two public keys for which the signature is valid + + :param int hash: signed hash + :param AbstractPoint generator: is the generator used in creation + of the signature + :rtype: tuple(Public_key, Public_key) + :return: a pair of public keys that can validate the signature + """ + curve = generator.curve() + n = generator.order() + r = self.r + s = self.s + e = hash + x = r + + # Compute the curve point with x as x-coordinate + alpha = ( + pow(x, 3, curve.p()) + (curve.a() * x) + curve.b() + ) % curve.p() + beta = numbertheory.square_root_mod_prime(alpha, curve.p()) + y = beta if beta % 2 == 0 else curve.p() - beta + + # Compute the public key + R1 = ellipticcurve.PointJacobi(curve, x, y, 1, n) + Q1 = numbertheory.inverse_mod(r, n) * (s * R1 + (-e % n) * generator) + Pk1 = Public_key(generator, Q1) + + # And the second solution + R2 = ellipticcurve.PointJacobi(curve, x, -y, 1, n) + Q2 = numbertheory.inverse_mod(r, n) * (s * R2 + (-e % n) * generator) + Pk2 = Public_key(generator, Q2) + + return [Pk1, Pk2] + + +class Public_key(object): + """Public key for ECDSA.""" + + def __init__(self, generator, point, verify=True): + """Low level ECDSA public key object. + + :param generator: the Point that generates the group (the base point) + :param point: the Point that defines the public key + :param bool verify: if True check if point is valid point on curve + + :raises InvalidPointError: if the point parameters are invalid or + point does not lay on the curve + """ + + self.curve = generator.curve() + self.generator = generator + self.point = point + n = generator.order() + p = self.curve.p() + if not (0 <= point.x() < p) or not (0 <= point.y() < p): + raise InvalidPointError( + "The public point has x or y out of range." + ) + if verify and not self.curve.contains_point(point.x(), point.y()): + raise InvalidPointError("Point does not lay on the curve") + if not n: + raise InvalidPointError("Generator point must have order.") + # for curve parameters with base point with cofactor 1, all points + # that are on the curve are scalar multiples of the base point, so + # verifying that is not necessary. See Section 3.2.2.1 of SEC 1 v2 + if ( + verify + and self.curve.cofactor() != 1 + and not n * point == ellipticcurve.INFINITY + ): + raise InvalidPointError("Generator point order is bad.") + + def __eq__(self, other): + """Return True if the keys are identical, False otherwise. + + Note: for comparison, only placement on the same curve and point + equality is considered, use of the same generator point is not + considered. + """ + if isinstance(other, Public_key): + return self.curve == other.curve and self.point == other.point + return NotImplemented + + def __ne__(self, other): + """Return False if the keys are identical, True otherwise.""" + return not self == other + + def verifies(self, hash, signature): + """Verify that signature is a valid signature of hash. + Return True if the signature is valid. + """ + + # From X9.62 J.3.1. + + G = self.generator + n = G.order() + r = signature.r + s = signature.s + if r < 1 or r > n - 1: + return False + if s < 1 or s > n - 1: + return False + c = numbertheory.inverse_mod(s, n) + u1 = (hash * c) % n + u2 = (r * c) % n + if hasattr(G, "mul_add"): + xy = G.mul_add(u1, self.point, u2) + else: + xy = u1 * G + u2 * self.point + v = xy.x() % n + return v == r + + +class Private_key(object): + """Private key for ECDSA.""" + + def __init__(self, public_key, secret_multiplier): + """public_key is of class Public_key; + secret_multiplier is a large integer. + """ + + self.public_key = public_key + self.secret_multiplier = secret_multiplier + + def __eq__(self, other): + """Return True if the points are identical, False otherwise.""" + if isinstance(other, Private_key): + return ( + self.public_key == other.public_key + and self.secret_multiplier == other.secret_multiplier + ) + return NotImplemented + + def __ne__(self, other): + """Return False if the points are identical, True otherwise.""" + return not self == other + + def sign(self, hash, random_k): + """Return a signature for the provided hash, using the provided + random nonce. It is absolutely vital that random_k be an unpredictable + number in the range [1, self.public_key.point.order()-1]. If + an attacker can guess random_k, he can compute our private key from a + single signature. Also, if an attacker knows a few high-order + bits (or a few low-order bits) of random_k, he can compute our private + key from many signatures. The generation of nonces with adequate + cryptographic strength is very difficult and far beyond the scope + of this comment. + + May raise RuntimeError, in which case retrying with a new + random value k is in order. + """ + + G = self.public_key.generator + n = G.order() + k = random_k % n + # Fix the bit-length of the random nonce, + # so that it doesn't leak via timing. + # This does not change that ks = k mod n + ks = k + n + kt = ks + n + if bit_length(ks) == bit_length(n): + p1 = kt * G + else: + p1 = ks * G + r = p1.x() % n + if r == 0: + raise RSZeroError("amazingly unlucky random number r") + s = ( + numbertheory.inverse_mod(k, n) + * (hash + (self.secret_multiplier * r) % n) + ) % n + if s == 0: + raise RSZeroError("amazingly unlucky random number s") + return Signature(r, s) + + +def int_to_string(x): # pragma: no cover + """Convert integer x into a string of bytes, as per X9.62.""" + # deprecated in 0.19 + warnings.warn( + "Function is unused in library code. If you use this code, " + "change to util.string_to_number.", + DeprecationWarning, + ) + assert x >= 0 + if x == 0: + return b"\0" + result = [] + while x: + ordinal = x & 0xFF + result.append(int2byte(ordinal)) + x >>= 8 + + result.reverse() + return b"".join(result) + + +def string_to_int(s): # pragma: no cover + """Convert a string of bytes into an integer, as per X9.62.""" + # deprecated in 0.19 + warnings.warn( + "Function is unused in library code. If you use this code, " + "change to util.number_to_string.", + DeprecationWarning, + ) + result = 0 + for c in s: + if not isinstance(c, int): + c = ord(c) + result = 256 * result + c + return result + + +def digest_integer(m): # pragma: no cover + """Convert an integer into a string of bytes, compute + its SHA-1 hash, and convert the result to an integer.""" + # deprecated in 0.19 + warnings.warn( + "Function is unused in library code. If you use this code, " + "change to a one-liner with util.number_to_string and " + "util.string_to_number methods.", + DeprecationWarning, + ) + # + # I don't expect this function to be used much. I wrote + # it in order to be able to duplicate the examples + # in ECDSAVS. + # + from hashlib import sha1 + + return string_to_int(sha1(int_to_string(m)).digest()) + + +def point_is_valid(generator, x, y): + """Is (x,y) a valid public key based on the specified generator?""" + + # These are the tests specified in X9.62. + + n = generator.order() + curve = generator.curve() + p = curve.p() + if not (0 <= x < p) or not (0 <= y < p): + return False + if not curve.contains_point(x, y): + return False + if ( + curve.cofactor() != 1 + and not n * ellipticcurve.PointJacobi(curve, x, y, 1) + == ellipticcurve.INFINITY + ): + return False + return True + + +# secp112r1 curve +_p = int(remove_whitespace("DB7C 2ABF62E3 5E668076 BEAD208B"), 16) +# s = 00F50B02 8E4D696E 67687561 51752904 72783FB1 +_a = int(remove_whitespace("DB7C 2ABF62E3 5E668076 BEAD2088"), 16) +_b = int(remove_whitespace("659E F8BA0439 16EEDE89 11702B22"), 16) +_Gx = int(remove_whitespace("09487239 995A5EE7 6B55F9C2 F098"), 16) +_Gy = int(remove_whitespace("A89C E5AF8724 C0A23E0E 0FF77500"), 16) +_r = int(remove_whitespace("DB7C 2ABF62E3 5E7628DF AC6561C5"), 16) +_h = 1 +curve_112r1 = ellipticcurve.CurveFp(_p, _a, _b, _h) +generator_112r1 = ellipticcurve.PointJacobi( + curve_112r1, _Gx, _Gy, 1, _r, generator=True +) + + +# secp112r2 curve +_p = int(remove_whitespace("DB7C 2ABF62E3 5E668076 BEAD208B"), 16) +# s = 022757A1 114D69E 67687561 51755316 C05E0BD4 +_a = int(remove_whitespace("6127 C24C05F3 8A0AAAF6 5C0EF02C"), 16) +_b = int(remove_whitespace("51DE F1815DB5 ED74FCC3 4C85D709"), 16) +_Gx = int(remove_whitespace("4BA30AB5 E892B4E1 649DD092 8643"), 16) +_Gy = int(remove_whitespace("ADCD 46F5882E 3747DEF3 6E956E97"), 16) +_r = int(remove_whitespace("36DF 0AAFD8B8 D7597CA1 0520D04B"), 16) +_h = 4 +curve_112r2 = ellipticcurve.CurveFp(_p, _a, _b, _h) +generator_112r2 = ellipticcurve.PointJacobi( + curve_112r2, _Gx, _Gy, 1, _r, generator=True +) + + +# secp128r1 curve +_p = int(remove_whitespace("FFFFFFFD FFFFFFFF FFFFFFFF FFFFFFFF"), 16) +# S = 000E0D4D 69E6768 75615175 0CC03A44 73D03679 +# a and b are mod p, so a is equal to p-3, or simply -3 +# _a = -3 +_b = int(remove_whitespace("E87579C1 1079F43D D824993C 2CEE5ED3"), 16) +_Gx = int(remove_whitespace("161FF752 8B899B2D 0C28607C A52C5B86"), 16) +_Gy = int(remove_whitespace("CF5AC839 5BAFEB13 C02DA292 DDED7A83"), 16) +_r = int(remove_whitespace("FFFFFFFE 00000000 75A30D1B 9038A115"), 16) +_h = 1 +curve_128r1 = ellipticcurve.CurveFp(_p, -3, _b, _h) +generator_128r1 = ellipticcurve.PointJacobi( + curve_128r1, _Gx, _Gy, 1, _r, generator=True +) + + +# secp160r1 +_p = int(remove_whitespace("FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF 7FFFFFFF"), 16) +# S = 1053CDE4 2C14D696 E6768756 1517533B F3F83345 +# a and b are mod p, so a is equal to p-3, or simply -3 +# _a = -3 +_b = int(remove_whitespace("1C97BEFC 54BD7A8B 65ACF89F 81D4D4AD C565FA45"), 16) +_Gx = int( + remove_whitespace("4A96B568 8EF57328 46646989 68C38BB9 13CBFC82"), + 16, +) +_Gy = int( + remove_whitespace("23A62855 3168947D 59DCC912 04235137 7AC5FB32"), + 16, +) +_r = int( + remove_whitespace("01 00000000 00000000 0001F4C8 F927AED3 CA752257"), + 16, +) +_h = 1 +curve_160r1 = ellipticcurve.CurveFp(_p, -3, _b, _h) +generator_160r1 = ellipticcurve.PointJacobi( + curve_160r1, _Gx, _Gy, 1, _r, generator=True +) + + +# NIST Curve P-192: +_p = 6277101735386680763835789423207666416083908700390324961279 +_r = 6277101735386680763835789423176059013767194773182842284081 +# s = 0x3045ae6fc8422f64ed579528d38120eae12196d5L +# c = 0x3099d2bbbfcb2538542dcd5fb078b6ef5f3d6fe2c745de65L +_b = int( + remove_whitespace( + """ + 64210519 E59C80E7 0FA7E9AB 72243049 FEB8DEEC C146B9B1""" + ), + 16, +) +_Gx = int( + remove_whitespace( + """ + 188DA80E B03090F6 7CBF20EB 43A18800 F4FF0AFD 82FF1012""" + ), + 16, +) +_Gy = int( + remove_whitespace( + """ + 07192B95 FFC8DA78 631011ED 6B24CDD5 73F977A1 1E794811""" + ), + 16, +) + +curve_192 = ellipticcurve.CurveFp(_p, -3, _b, 1) +generator_192 = ellipticcurve.PointJacobi( + curve_192, _Gx, _Gy, 1, _r, generator=True +) + + +# NIST Curve P-224: +_p = int( + remove_whitespace( + """ + 2695994666715063979466701508701963067355791626002630814351 + 0066298881""" + ) +) +_r = int( + remove_whitespace( + """ + 2695994666715063979466701508701962594045780771442439172168 + 2722368061""" + ) +) +# s = 0xbd71344799d5c7fcdc45b59fa3b9ab8f6a948bc5L +# c = 0x5b056c7e11dd68f40469ee7f3c7a7d74f7d121116506d031218291fbL +_b = int( + remove_whitespace( + """ + B4050A85 0C04B3AB F5413256 5044B0B7 D7BFD8BA 270B3943 + 2355FFB4""" + ), + 16, +) +_Gx = int( + remove_whitespace( + """ + B70E0CBD 6BB4BF7F 321390B9 4A03C1D3 56C21122 343280D6 + 115C1D21""" + ), + 16, +) +_Gy = int( + remove_whitespace( + """ + BD376388 B5F723FB 4C22DFE6 CD4375A0 5A074764 44D58199 + 85007E34""" + ), + 16, +) + +curve_224 = ellipticcurve.CurveFp(_p, -3, _b, 1) +generator_224 = ellipticcurve.PointJacobi( + curve_224, _Gx, _Gy, 1, _r, generator=True +) + +# NIST Curve P-256: +_p = int( + remove_whitespace( + """ + 1157920892103562487626974469494075735300861434152903141955 + 33631308867097853951""" + ) +) +_r = int( + remove_whitespace( + """ + 115792089210356248762697446949407573529996955224135760342 + 422259061068512044369""" + ) +) +# s = 0xc49d360886e704936a6678e1139d26b7819f7e90L +# c = 0x7efba1662985be9403cb055c75d4f7e0ce8d84a9c5114abcaf3177680104fa0dL +_b = int( + remove_whitespace( + """ + 5AC635D8 AA3A93E7 B3EBBD55 769886BC 651D06B0 CC53B0F6 + 3BCE3C3E 27D2604B""" + ), + 16, +) +_Gx = int( + remove_whitespace( + """ + 6B17D1F2 E12C4247 F8BCE6E5 63A440F2 77037D81 2DEB33A0 + F4A13945 D898C296""" + ), + 16, +) +_Gy = int( + remove_whitespace( + """ + 4FE342E2 FE1A7F9B 8EE7EB4A 7C0F9E16 2BCE3357 6B315ECE + CBB64068 37BF51F5""" + ), + 16, +) + +curve_256 = ellipticcurve.CurveFp(_p, -3, _b, 1) +generator_256 = ellipticcurve.PointJacobi( + curve_256, _Gx, _Gy, 1, _r, generator=True +) + +# NIST Curve P-384: +_p = int( + remove_whitespace( + """ + 3940200619639447921227904010014361380507973927046544666794 + 8293404245721771496870329047266088258938001861606973112319""" + ) +) +_r = int( + remove_whitespace( + """ + 3940200619639447921227904010014361380507973927046544666794 + 6905279627659399113263569398956308152294913554433653942643""" + ) +) +# s = 0xa335926aa319a27a1d00896a6773a4827acdac73L +# c = int(remove_whitespace( +# """ +# 79d1e655 f868f02f ff48dcde e14151dd b80643c1 406d0ca1 +# 0dfe6fc5 2009540a 495e8042 ea5f744f 6e184667 cc722483""" +# ), 16) +_b = int( + remove_whitespace( + """ + B3312FA7 E23EE7E4 988E056B E3F82D19 181D9C6E FE814112 + 0314088F 5013875A C656398D 8A2ED19D 2A85C8ED D3EC2AEF""" + ), + 16, +) +_Gx = int( + remove_whitespace( + """ + AA87CA22 BE8B0537 8EB1C71E F320AD74 6E1D3B62 8BA79B98 + 59F741E0 82542A38 5502F25D BF55296C 3A545E38 72760AB7""" + ), + 16, +) +_Gy = int( + remove_whitespace( + """ + 3617DE4A 96262C6F 5D9E98BF 9292DC29 F8F41DBD 289A147C + E9DA3113 B5F0B8C0 0A60B1CE 1D7E819D 7A431D7C 90EA0E5F""" + ), + 16, +) + +curve_384 = ellipticcurve.CurveFp(_p, -3, _b, 1) +generator_384 = ellipticcurve.PointJacobi( + curve_384, _Gx, _Gy, 1, _r, generator=True +) + +# NIST Curve P-521: +_p = int( + "686479766013060971498190079908139321726943530014330540939" + "446345918554318339765605212255964066145455497729631139148" + "0858037121987999716643812574028291115057151" +) +_r = int( + "686479766013060971498190079908139321726943530014330540939" + "446345918554318339765539424505774633321719753296399637136" + "3321113864768612440380340372808892707005449" +) +# s = 0xd09e8800291cb85396cc6717393284aaa0da64baL +# c = int(remove_whitespace( +# """ +# 0b4 8bfa5f42 0a349495 39d2bdfc 264eeeeb 077688e4 +# 4fbf0ad8 f6d0edb3 7bd6b533 28100051 8e19f1b9 ffbe0fe9 +# ed8a3c22 00b8f875 e523868c 70c1e5bf 55bad637""" +# ), 16) +_b = int( + remove_whitespace( + """ + 051 953EB961 8E1C9A1F 929A21A0 B68540EE A2DA725B + 99B315F3 B8B48991 8EF109E1 56193951 EC7E937B 1652C0BD + 3BB1BF07 3573DF88 3D2C34F1 EF451FD4 6B503F00""" + ), + 16, +) +_Gx = int( + remove_whitespace( + """ + C6 858E06B7 0404E9CD 9E3ECB66 2395B442 9C648139 + 053FB521 F828AF60 6B4D3DBA A14B5E77 EFE75928 FE1DC127 + A2FFA8DE 3348B3C1 856A429B F97E7E31 C2E5BD66""" + ), + 16, +) +_Gy = int( + remove_whitespace( + """ + 118 39296A78 9A3BC004 5C8A5FB4 2C7D1BD9 98F54449 + 579B4468 17AFBD17 273E662C 97EE7299 5EF42640 C550B901 + 3FAD0761 353C7086 A272C240 88BE9476 9FD16650""" + ), + 16, +) + +curve_521 = ellipticcurve.CurveFp(_p, -3, _b, 1) +generator_521 = ellipticcurve.PointJacobi( + curve_521, _Gx, _Gy, 1, _r, generator=True +) + +# Certicom secp256-k1 +_a = 0x0000000000000000000000000000000000000000000000000000000000000000 +_b = 0x0000000000000000000000000000000000000000000000000000000000000007 +_p = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F +_Gx = 0x79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798 +_Gy = 0x483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8 +_r = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 + +curve_secp256k1 = ellipticcurve.CurveFp(_p, _a, _b, 1) +generator_secp256k1 = ellipticcurve.PointJacobi( + curve_secp256k1, _Gx, _Gy, 1, _r, generator=True +) + +# Brainpool P-160-r1 +_a = 0x340E7BE2A280EB74E2BE61BADA745D97E8F7C300 +_b = 0x1E589A8595423412134FAA2DBDEC95C8D8675E58 +_p = 0xE95E4A5F737059DC60DFC7AD95B3D8139515620F +_Gx = 0xBED5AF16EA3F6A4F62938C4631EB5AF7BDBCDBC3 +_Gy = 0x1667CB477A1A8EC338F94741669C976316DA6321 +_q = 0xE95E4A5F737059DC60DF5991D45029409E60FC09 + +curve_brainpoolp160r1 = ellipticcurve.CurveFp(_p, _a, _b, 1) +generator_brainpoolp160r1 = ellipticcurve.PointJacobi( + curve_brainpoolp160r1, _Gx, _Gy, 1, _q, generator=True +) + +# Brainpool P-160-t1 +_a = 0xE95E4A5F737059DC60DFC7AD95B3D8139515620C +_b = 0x7A556B6DAE535B7B51ED2C4D7DAA7A0B5C55F380 +# _z = 0x24DBFF5DEC9B986BBFE5295A29BFBAE45E0F5D0B +_Gx = 0xB199B13B9B34EFC1397E64BAEB05ACC265FF2378 +_Gy = 0xADD6718B7C7C1961F0991B842443772152C9E0AD +_q = 0xE95E4A5F737059DC60DF5991D45029409E60FC09 +curve_brainpoolp160t1 = ellipticcurve.CurveFp(_p, _a, _b, 1) +generator_brainpoolp160t1 = ellipticcurve.PointJacobi( + curve_brainpoolp160t1, _Gx, _Gy, 1, _q, generator=True +) + +# Brainpool P-192-r1 +_a = 0x6A91174076B1E0E19C39C031FE8685C1CAE040E5C69A28EF +_b = 0x469A28EF7C28CCA3DC721D044F4496BCCA7EF4146FBF25C9 +_p = 0xC302F41D932A36CDA7A3463093D18DB78FCE476DE1A86297 +_Gx = 0xC0A0647EAAB6A48753B033C56CB0F0900A2F5C4853375FD6 +_Gy = 0x14B690866ABD5BB88B5F4828C1490002E6773FA2FA299B8F +_q = 0xC302F41D932A36CDA7A3462F9E9E916B5BE8F1029AC4ACC1 + +curve_brainpoolp192r1 = ellipticcurve.CurveFp(_p, _a, _b, 1) +generator_brainpoolp192r1 = ellipticcurve.PointJacobi( + curve_brainpoolp192r1, _Gx, _Gy, 1, _q, generator=True +) + +# Brainpool P-192-t1 +_a = 0xC302F41D932A36CDA7A3463093D18DB78FCE476DE1A86294 +_b = 0x13D56FFAEC78681E68F9DEB43B35BEC2FB68542E27897B79 +# _z = 0x1B6F5CC8DB4DC7AF19458A9CB80DC2295E5EB9C3732104CB +_Gx = 0x3AE9E58C82F63C30282E1FE7BBF43FA72C446AF6F4618129 +_Gy = 0x097E2C5667C2223A902AB5CA449D0084B7E5B3DE7CCC01C9 +_q = 0xC302F41D932A36CDA7A3462F9E9E916B5BE8F1029AC4ACC1 + +curve_brainpoolp192t1 = ellipticcurve.CurveFp(_p, _a, _b, 1) +generator_brainpoolp192t1 = ellipticcurve.PointJacobi( + curve_brainpoolp192t1, _Gx, _Gy, 1, _q, generator=True +) + +# Brainpool P-224-r1 +_a = 0x68A5E62CA9CE6C1C299803A6C1530B514E182AD8B0042A59CAD29F43 +_b = 0x2580F63CCFE44138870713B1A92369E33E2135D266DBB372386C400B +_p = 0xD7C134AA264366862A18302575D1D787B09F075797DA89F57EC8C0FF +_Gx = 0x0D9029AD2C7E5CF4340823B2A87DC68C9E4CE3174C1E6EFDEE12C07D +_Gy = 0x58AA56F772C0726F24C6B89E4ECDAC24354B9E99CAA3F6D3761402CD +_q = 0xD7C134AA264366862A18302575D0FB98D116BC4B6DDEBCA3A5A7939F + +curve_brainpoolp224r1 = ellipticcurve.CurveFp(_p, _a, _b, 1) +generator_brainpoolp224r1 = ellipticcurve.PointJacobi( + curve_brainpoolp224r1, _Gx, _Gy, 1, _q, generator=True +) + +# Brainpool P-224-t1 +_a = 0xD7C134AA264366862A18302575D1D787B09F075797DA89F57EC8C0FC +_b = 0x4B337D934104CD7BEF271BF60CED1ED20DA14C08B3BB64F18A60888D +# _z = 0x2DF271E14427A346910CF7A2E6CFA7B3F484E5C2CCE1C8B730E28B3F +_Gx = 0x6AB1E344CE25FF3896424E7FFE14762ECB49F8928AC0C76029B4D580 +_Gy = 0x0374E9F5143E568CD23F3F4D7C0D4B1E41C8CC0D1C6ABD5F1A46DB4C +_q = 0xD7C134AA264366862A18302575D0FB98D116BC4B6DDEBCA3A5A7939F + +curve_brainpoolp224t1 = ellipticcurve.CurveFp(_p, _a, _b, 1) +generator_brainpoolp224t1 = ellipticcurve.PointJacobi( + curve_brainpoolp224t1, _Gx, _Gy, 1, _q, generator=True +) + +# Brainpool P-256-r1 +_a = 0x7D5A0975FC2C3057EEF67530417AFFE7FB8055C126DC5C6CE94A4B44F330B5D9 +_b = 0x26DC5C6CE94A4B44F330B5D9BBD77CBF958416295CF7E1CE6BCCDC18FF8C07B6 +_p = 0xA9FB57DBA1EEA9BC3E660A909D838D726E3BF623D52620282013481D1F6E5377 +_Gx = 0x8BD2AEB9CB7E57CB2C4B482FFC81B7AFB9DE27E1E3BD23C23A4453BD9ACE3262 +_Gy = 0x547EF835C3DAC4FD97F8461A14611DC9C27745132DED8E545C1D54C72F046997 +_q = 0xA9FB57DBA1EEA9BC3E660A909D838D718C397AA3B561A6F7901E0E82974856A7 + +curve_brainpoolp256r1 = ellipticcurve.CurveFp(_p, _a, _b, 1) +generator_brainpoolp256r1 = ellipticcurve.PointJacobi( + curve_brainpoolp256r1, _Gx, _Gy, 1, _q, generator=True +) + +# Brainpool P-256-t1 +_a = 0xA9FB57DBA1EEA9BC3E660A909D838D726E3BF623D52620282013481D1F6E5374 +_b = 0x662C61C430D84EA4FE66A7733D0B76B7BF93EBC4AF2F49256AE58101FEE92B04 +# _z = 0x3E2D4BD9597B58639AE7AA669CAB9837CF5CF20A2C852D10F655668DFC150EF0 +_Gx = 0xA3E8EB3CC1CFE7B7732213B23A656149AFA142C47AAFBC2B79A191562E1305F4 +_Gy = 0x2D996C823439C56D7F7B22E14644417E69BCB6DE39D027001DABE8F35B25C9BE +_q = 0xA9FB57DBA1EEA9BC3E660A909D838D718C397AA3B561A6F7901E0E82974856A7 + +curve_brainpoolp256t1 = ellipticcurve.CurveFp(_p, _a, _b, 1) +generator_brainpoolp256t1 = ellipticcurve.PointJacobi( + curve_brainpoolp256t1, _Gx, _Gy, 1, _q, generator=True +) + +# Brainpool P-320-r1 +_a = int( + remove_whitespace( + """ + 3EE30B568FBAB0F883CCEBD46D3F3BB8A2A73513F5EB79DA66190EB085FFA9 + F492F375A97D860EB4""" + ), + 16, +) +_b = int( + remove_whitespace( + """ + 520883949DFDBC42D3AD198640688A6FE13F41349554B49ACC31DCCD884539 + 816F5EB4AC8FB1F1A6""" + ), + 16, +) +_p = int( + remove_whitespace( + """ + D35E472036BC4FB7E13C785ED201E065F98FCFA6F6F40DEF4F92B9EC7893EC + 28FCD412B1F1B32E27""" + ), + 16, +) +_Gx = int( + remove_whitespace( + """ + 43BD7E9AFB53D8B85289BCC48EE5BFE6F20137D10A087EB6E7871E2A10A599 + C710AF8D0D39E20611""" + ), + 16, +) +_Gy = int( + remove_whitespace( + """ + 14FDD05545EC1CC8AB4093247F77275E0743FFED117182EAA9C77877AAAC6A + C7D35245D1692E8EE1""" + ), + 16, +) +_q = int( + remove_whitespace( + """ + D35E472036BC4FB7E13C785ED201E065F98FCFA5B68F12A32D482EC7EE8658 + E98691555B44C59311""" + ), + 16, +) + +curve_brainpoolp320r1 = ellipticcurve.CurveFp(_p, _a, _b, 1) +generator_brainpoolp320r1 = ellipticcurve.PointJacobi( + curve_brainpoolp320r1, _Gx, _Gy, 1, _q, generator=True +) + +# Brainpool P-320-t1 +_a = int( + remove_whitespace( + """ + D35E472036BC4FB7E13C785ED201E065F98FCFA6F6F40DEF4F92B9EC7893EC + 28FCD412B1F1B32E24""" + ), + 16, +) +_b = int( + remove_whitespace( + """ + A7F561E038EB1ED560B3D147DB782013064C19F27ED27C6780AAF77FB8A547 + CEB5B4FEF422340353""" + ), + 16, +) +# _z = int( +# remove_whitespace( +# """ +# 15F75CAF668077F7E85B42EB01F0A81FF56ECD6191D55CB82B7D861458A18F +# EFC3E5AB7496F3C7B1""" +# ), +# 16, +# ) +_Gx = int( + remove_whitespace( + """ + 925BE9FB01AFC6FB4D3E7D4990010F813408AB106C4F09CB7EE07868CC136F + FF3357F624A21BED52""" + ), + 16, +) +_Gy = int( + remove_whitespace( + """ + 63BA3A7A27483EBF6671DBEF7ABB30EBEE084E58A0B077AD42A5A0989D1EE7 + 1B1B9BC0455FB0D2C3""" + ), + 16, +) +_q = int( + remove_whitespace( + """ + D35E472036BC4FB7E13C785ED201E065F98FCFA5B68F12A32D482EC7EE8658 + E98691555B44C59311""" + ), + 16, +) + +curve_brainpoolp320t1 = ellipticcurve.CurveFp(_p, _a, _b, 1) +generator_brainpoolp320t1 = ellipticcurve.PointJacobi( + curve_brainpoolp320t1, _Gx, _Gy, 1, _q, generator=True +) + +# Brainpool P-384-r1 +_a = int( + remove_whitespace( + """ + 7BC382C63D8C150C3C72080ACE05AFA0C2BEA28E4FB22787139165EFBA91F9 + 0F8AA5814A503AD4EB04A8C7DD22CE2826""" + ), + 16, +) +_b = int( + remove_whitespace( + """ + 04A8C7DD22CE28268B39B55416F0447C2FB77DE107DCD2A62E880EA53EEB62 + D57CB4390295DBC9943AB78696FA504C11""" + ), + 16, +) +_p = int( + remove_whitespace( + """ + 8CB91E82A3386D280F5D6F7E50E641DF152F7109ED5456B412B1DA197FB711 + 23ACD3A729901D1A71874700133107EC53""" + ), + 16, +) +_Gx = int( + remove_whitespace( + """ + 1D1C64F068CF45FFA2A63A81B7C13F6B8847A3E77EF14FE3DB7FCAFE0CBD10 + E8E826E03436D646AAEF87B2E247D4AF1E""" + ), + 16, +) +_Gy = int( + remove_whitespace( + """ + 8ABE1D7520F9C2A45CB1EB8E95CFD55262B70B29FEEC5864E19C054FF991292 + 80E4646217791811142820341263C5315""" + ), + 16, +) +_q = int( + remove_whitespace( + """ + 8CB91E82A3386D280F5D6F7E50E641DF152F7109ED5456B31F166E6CAC0425 + A7CF3AB6AF6B7FC3103B883202E9046565""" + ), + 16, +) + +curve_brainpoolp384r1 = ellipticcurve.CurveFp(_p, _a, _b, 1) +generator_brainpoolp384r1 = ellipticcurve.PointJacobi( + curve_brainpoolp384r1, _Gx, _Gy, 1, _q, generator=True +) + +_a = int( + remove_whitespace( + """ + 8CB91E82A3386D280F5D6F7E50E641DF152F7109ED5456B412B1DA197FB711 + 23ACD3A729901D1A71874700133107EC50""" + ), + 16, +) +_b = int( + remove_whitespace( + """ + 7F519EADA7BDA81BD826DBA647910F8C4B9346ED8CCDC64E4B1ABD11756DCE + 1D2074AA263B88805CED70355A33B471EE""" + ), + 16, +) +# _z = int( +# remove_whitespace( +# """ +# 41DFE8DD399331F7166A66076734A89CD0D2BCDB7D068E44E1F378F41ECBAE +# 97D2D63DBC87BCCDDCCC5DA39E8589291C""" +# ), +# 16, +# ) +_Gx = int( + remove_whitespace( + """ + 18DE98B02DB9A306F2AFCD7235F72A819B80AB12EBD653172476FECD462AAB + FFC4FF191B946A5F54D8D0AA2F418808CC""" + ), + 16, +) +_Gy = int( + remove_whitespace( + """ + 25AB056962D30651A114AFD2755AD336747F93475B7A1FCA3B88F2B6A208CC + FE469408584DC2B2912675BF5B9E582928""" + ), + 16, +) +_q = int( + remove_whitespace( + """ + 8CB91E82A3386D280F5D6F7E50E641DF152F7109ED5456B31F166E6CAC0425 + A7CF3AB6AF6B7FC3103B883202E9046565""" + ), + 16, +) + +curve_brainpoolp384t1 = ellipticcurve.CurveFp(_p, _a, _b, 1) +generator_brainpoolp384t1 = ellipticcurve.PointJacobi( + curve_brainpoolp384t1, _Gx, _Gy, 1, _q, generator=True +) + +# Brainpool P-512-r1 +_a = int( + remove_whitespace( + """ + 7830A3318B603B89E2327145AC234CC594CBDD8D3DF91610A83441CAEA9863 + BC2DED5D5AA8253AA10A2EF1C98B9AC8B57F1117A72BF2C7B9E7C1AC4D77FC94CA""" + ), + 16, +) +_b = int( + remove_whitespace( + """ + 3DF91610A83441CAEA9863BC2DED5D5AA8253AA10A2EF1C98B9AC8B57F1117 + A72BF2C7B9E7C1AC4D77FC94CADC083E67984050B75EBAE5DD2809BD638016F723""" + ), + 16, +) +_p = int( + remove_whitespace( + """ + AADD9DB8DBE9C48B3FD4E6AE33C9FC07CB308DB3B3C9D20ED6639CCA703308 + 717D4D9B009BC66842AECDA12AE6A380E62881FF2F2D82C68528AA6056583A48F3""" + ), + 16, +) +_Gx = int( + remove_whitespace( + """ + 81AEE4BDD82ED9645A21322E9C4C6A9385ED9F70B5D916C1B43B62EEF4D009 + 8EFF3B1F78E2D0D48D50D1687B93B97D5F7C6D5047406A5E688B352209BCB9F822""" + ), + 16, +) +_Gy = int( + remove_whitespace( + """ + 7DDE385D566332ECC0EABFA9CF7822FDF209F70024A57B1AA000C55B881F81 + 11B2DCDE494A5F485E5BCA4BD88A2763AED1CA2B2FA8F0540678CD1E0F3AD80892""" + ), + 16, +) +_q = int( + remove_whitespace( + """ + AADD9DB8DBE9C48B3FD4E6AE33C9FC07CB308DB3B3C9D20ED6639CCA703308 + 70553E5C414CA92619418661197FAC10471DB1D381085DDADDB58796829CA90069""" + ), + 16, +) + +curve_brainpoolp512r1 = ellipticcurve.CurveFp(_p, _a, _b, 1) +generator_brainpoolp512r1 = ellipticcurve.PointJacobi( + curve_brainpoolp512r1, _Gx, _Gy, 1, _q, generator=True +) + +# Brainpool P-512-t1 +_a = int( + remove_whitespace( + """ + AADD9DB8DBE9C48B3FD4E6AE33C9FC07CB308DB3B3C9D20ED6639CCA703308 + 717D4D9B009BC66842AECDA12AE6A380E62881FF2F2D82C68528AA6056583A48F0""" + ), + 16, +) +_b = int( + remove_whitespace( + """ + 7CBBBCF9441CFAB76E1890E46884EAE321F70C0BCB4981527897504BEC3E36 + A62BCDFA2304976540F6450085F2DAE145C22553B465763689180EA2571867423E""" + ), + 16, +) +# _z = int( +# remove_whitespace( +# """ +# 12EE58E6764838B69782136F0F2D3BA06E27695716054092E60A80BEDB212B +# 64E585D90BCE13761F85C3F1D2A64E3BE8FEA2220F01EBA5EEB0F35DBD29D922AB""" +# ), +# 16, +# ) +_Gx = int( + remove_whitespace( + """ + 640ECE5C12788717B9C1BA06CBC2A6FEBA85842458C56DDE9DB1758D39C031 + 3D82BA51735CDB3EA499AA77A7D6943A64F7A3F25FE26F06B51BAA2696FA9035DA""" + ), + 16, +) +_Gy = int( + remove_whitespace( + """ + 5B534BD595F5AF0FA2C892376C84ACE1BB4E3019B71634C01131159CAE03CE + E9D9932184BEEF216BD71DF2DADF86A627306ECFF96DBB8BACE198B61E00F8B332""" + ), + 16, +) +_q = int( + remove_whitespace( + """ + AADD9DB8DBE9C48B3FD4E6AE33C9FC07CB308DB3B3C9D20ED6639CCA703308 + 70553E5C414CA92619418661197FAC10471DB1D381085DDADDB58796829CA90069""" + ), + 16, +) + +curve_brainpoolp512t1 = ellipticcurve.CurveFp(_p, _a, _b, 1) +generator_brainpoolp512t1 = ellipticcurve.PointJacobi( + curve_brainpoolp512t1, _Gx, _Gy, 1, _q, generator=True +) diff --git a/code/.venv/lib/python3.12/site-packages/ecdsa/eddsa.py b/code/.venv/lib/python3.12/site-packages/ecdsa/eddsa.py new file mode 100644 index 0000000..9769cfd --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/ecdsa/eddsa.py @@ -0,0 +1,252 @@ +"""Implementation of Edwards Digital Signature Algorithm.""" + +import hashlib +from ._sha3 import shake_256 +from . import ellipticcurve +from ._compat import ( + remove_whitespace, + bit_length, + bytes_to_int, + int_to_bytes, + compat26_str, +) + +# edwards25519, defined in RFC7748 +_p = 2**255 - 19 +_a = -1 +_d = int( + remove_whitespace( + "370957059346694393431380835087545651895421138798432190163887855330" + "85940283555" + ) +) +_h = 8 + +_Gx = int( + remove_whitespace( + "151122213495354007725011514095885315114540126930418572060461132" + "83949847762202" + ) +) +_Gy = int( + remove_whitespace( + "463168356949264781694283940034751631413079938662562256157830336" + "03165251855960" + ) +) +_r = 2**252 + 0x14DEF9DEA2F79CD65812631A5CF5D3ED + + +def _sha512(data): + return hashlib.new("sha512", compat26_str(data)).digest() + + +curve_ed25519 = ellipticcurve.CurveEdTw(_p, _a, _d, _h, _sha512) +generator_ed25519 = ellipticcurve.PointEdwards( + curve_ed25519, _Gx, _Gy, 1, _Gx * _Gy % _p, _r, generator=True +) + + +# edwards448, defined in RFC7748 +_p = 2**448 - 2**224 - 1 +_a = 1 +_d = -39081 % _p +_h = 4 + +_Gx = int( + remove_whitespace( + "224580040295924300187604334099896036246789641632564134246125461" + "686950415467406032909029192869357953282578032075146446173674602635" + "247710" + ) +) +_Gy = int( + remove_whitespace( + "298819210078481492676017930443930673437544040154080242095928241" + "372331506189835876003536878655418784733982303233503462500531545062" + "832660" + ) +) +_r = 2**446 - 0x8335DC163BB124B65129C96FDE933D8D723A70AADC873D6D54A7BB0D + + +def _shake256(data): + return shake_256(data, 114) + + +curve_ed448 = ellipticcurve.CurveEdTw(_p, _a, _d, _h, _shake256) +generator_ed448 = ellipticcurve.PointEdwards( + curve_ed448, _Gx, _Gy, 1, _Gx * _Gy % _p, _r, generator=True +) + + +class PublicKey(object): + """Public key for the Edwards Digital Signature Algorithm.""" + + def __init__(self, generator, public_key, public_point=None): + self.generator = generator + self.curve = generator.curve() + self.__encoded = public_key + # plus one for the sign bit and round up + self.baselen = (bit_length(self.curve.p()) + 1 + 7) // 8 + if len(public_key) != self.baselen: + raise ValueError( + "Incorrect size of the public key, expected: {0} bytes".format( + self.baselen + ) + ) + if public_point: + self.__point = public_point + else: + self.__point = ellipticcurve.PointEdwards.from_bytes( + self.curve, public_key + ) + + def __eq__(self, other): + if isinstance(other, PublicKey): + return ( + self.curve == other.curve and self.__encoded == other.__encoded + ) + return NotImplemented + + def __ne__(self, other): + return not self == other + + @property + def point(self): + return self.__point + + @point.setter + def point(self, other): + if self.__point != other: + raise ValueError("Can't change the coordinates of the point") + self.__point = other + + def public_point(self): + return self.__point + + def public_key(self): + return self.__encoded + + def verify(self, data, signature): + """Verify a Pure EdDSA signature over data.""" + data = compat26_str(data) + if len(signature) != 2 * self.baselen: + raise ValueError( + "Invalid signature length, expected: {0} bytes".format( + 2 * self.baselen + ) + ) + R = ellipticcurve.PointEdwards.from_bytes( + self.curve, signature[: self.baselen] + ) + S = bytes_to_int(signature[self.baselen :], "little") + if S >= self.generator.order(): + raise ValueError("Invalid signature") + + dom = bytearray() + if self.curve == curve_ed448: + dom = bytearray(b"SigEd448" + b"\x00\x00") + + k = bytes_to_int( + self.curve.hash_func(dom + R.to_bytes() + self.__encoded + data), + "little", + ) + + if self.generator * S != self.__point * k + R: + raise ValueError("Invalid signature") + + return True + + +class PrivateKey(object): + """Private key for the Edwards Digital Signature Algorithm.""" + + def __init__(self, generator, private_key): + self.generator = generator + self.curve = generator.curve() + # plus one for the sign bit and round up + self.baselen = (bit_length(self.curve.p()) + 1 + 7) // 8 + if len(private_key) != self.baselen: + raise ValueError( + "Incorrect size of private key, expected: {0} bytes".format( + self.baselen + ) + ) + self.__private_key = bytes(private_key) + self.__h = bytearray(self.curve.hash_func(private_key)) + self.__public_key = None + + a = self.__h[: self.baselen] + a = self._key_prune(a) + scalar = bytes_to_int(a, "little") + self.__s = scalar + + @property + def private_key(self): + return self.__private_key + + def __eq__(self, other): + if isinstance(other, PrivateKey): + return ( + self.curve == other.curve + and self.__private_key == other.__private_key + ) + return NotImplemented + + def __ne__(self, other): + return not self == other + + def _key_prune(self, key): + # make sure the key is not in a small subgroup + h = self.curve.cofactor() + if h == 4: + h_log = 2 + elif h == 8: + h_log = 3 + else: + raise ValueError("Only cofactor 4 and 8 curves supported") + key[0] &= ~((1 << h_log) - 1) + + # ensure the highest bit is set but no higher + l = bit_length(self.curve.p()) + if l % 8 == 0: + key[-1] = 0 + key[-2] |= 0x80 + else: + key[-1] = key[-1] & (1 << (l % 8)) - 1 | 1 << (l % 8) - 1 + return key + + def public_key(self): + """Generate the public key based on the included private key""" + if self.__public_key: + return self.__public_key + + public_point = self.generator * self.__s + + self.__public_key = PublicKey( + self.generator, public_point.to_bytes(), public_point + ) + + return self.__public_key + + def sign(self, data): + """Perform a Pure EdDSA signature over data.""" + data = compat26_str(data) + A = self.public_key().public_key() + + prefix = self.__h[self.baselen :] + + dom = bytearray() + if self.curve == curve_ed448: + dom = bytearray(b"SigEd448" + b"\x00\x00") + + r = bytes_to_int(self.curve.hash_func(dom + prefix + data), "little") + R = (self.generator * r).to_bytes() + + k = bytes_to_int(self.curve.hash_func(dom + R + A + data), "little") + k %= self.generator.order() + + S = (r + k * self.__s) % self.generator.order() + + return R + int_to_bytes(S, self.baselen, "little") diff --git a/code/.venv/lib/python3.12/site-packages/ecdsa/ellipticcurve.py b/code/.venv/lib/python3.12/site-packages/ecdsa/ellipticcurve.py new file mode 100644 index 0000000..18816a6 --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/ecdsa/ellipticcurve.py @@ -0,0 +1,1598 @@ +#! /usr/bin/env python +# -*- coding: utf-8 -*- +# +# Implementation of elliptic curves, for cryptographic applications. +# +# This module doesn't provide any way to choose a random elliptic +# curve, nor to verify that an elliptic curve was chosen randomly, +# because one can simply use NIST's standard curves. +# +# Notes from X9.62-1998 (draft): +# Nomenclature: +# - Q is a public key. +# The "Elliptic Curve Domain Parameters" include: +# - q is the "field size", which in our case equals p. +# - p is a big prime. +# - G is a point of prime order (5.1.1.1). +# - n is the order of G (5.1.1.1). +# Public-key validation (5.2.2): +# - Verify that Q is not the point at infinity. +# - Verify that X_Q and Y_Q are in [0,p-1]. +# - Verify that Q is on the curve. +# - Verify that nQ is the point at infinity. +# Signature generation (5.3): +# - Pick random k from [1,n-1]. +# Signature checking (5.4.2): +# - Verify that r and s are in [1,n-1]. +# +# Revision history: +# 2005.12.31 - Initial version. +# 2008.11.25 - Change CurveFp.is_on to contains_point. +# +# Written in 2005 by Peter Pearson and placed in the public domain. +# Modified extensively as part of python-ecdsa. + +from __future__ import division + +try: + from gmpy2 import mpz + + GMPY = True +except ImportError: # pragma: no branch + try: + from gmpy import mpz + + GMPY = True + except ImportError: + GMPY = False + + +from six import python_2_unicode_compatible +from . import numbertheory +from ._compat import normalise_bytes, int_to_bytes, bit_length, bytes_to_int +from .errors import MalformedPointError +from .util import orderlen, string_to_number, number_to_string + + +@python_2_unicode_compatible +class CurveFp(object): + """ + :term:`Short Weierstrass Elliptic Curve ` over a + prime field. + """ + + if GMPY: # pragma: no branch + + def __init__(self, p, a, b, h=None): + """ + The curve of points satisfying y^2 = x^3 + a*x + b (mod p). + + h is an integer that is the cofactor of the elliptic curve domain + parameters; it is the number of points satisfying the elliptic + curve equation divided by the order of the base point. It is used + for selection of efficient algorithm for public point verification. + """ + self.__p = mpz(p) + self.__a = mpz(a) + self.__b = mpz(b) + # h is not used in calculations and it can be None, so don't use + # gmpy with it + self.__h = h + + else: # pragma: no branch + + def __init__(self, p, a, b, h=None): + """ + The curve of points satisfying y^2 = x^3 + a*x + b (mod p). + + h is an integer that is the cofactor of the elliptic curve domain + parameters; it is the number of points satisfying the elliptic + curve equation divided by the order of the base point. It is used + for selection of efficient algorithm for public point verification. + """ + self.__p = p + self.__a = a + self.__b = b + self.__h = h + + def __eq__(self, other): + """Return True if other is an identical curve, False otherwise. + + Note: the value of the cofactor of the curve is not taken into account + when comparing curves, as it's derived from the base point and + intrinsic curve characteristic (but it's complex to compute), + only the prime and curve parameters are considered. + """ + if isinstance(other, CurveFp): + p = self.__p + return ( + self.__p == other.__p + and self.__a % p == other.__a % p + and self.__b % p == other.__b % p + ) + return NotImplemented + + def __ne__(self, other): + """Return False if other is an identical curve, True otherwise.""" + return not self == other + + def __hash__(self): + return hash((self.__p, self.__a, self.__b)) + + def p(self): + return self.__p + + def a(self): + return self.__a + + def b(self): + return self.__b + + def cofactor(self): + return self.__h + + def contains_point(self, x, y): + """Is the point (x,y) on this curve?""" + return (y * y - ((x * x + self.__a) * x + self.__b)) % self.__p == 0 + + def __str__(self): + if self.__h is not None: + return "CurveFp(p={0}, a={1}, b={2}, h={3})".format( + self.__p, + self.__a, + self.__b, + self.__h, + ) + return "CurveFp(p={0}, a={1}, b={2})".format( + self.__p, + self.__a, + self.__b, + ) + + +class CurveEdTw(object): + """Parameters for a Twisted Edwards Elliptic Curve""" + + if GMPY: # pragma: no branch + + def __init__(self, p, a, d, h=None, hash_func=None): + """ + The curve of points satisfying a*x^2 + y^2 = 1 + d*x^2*y^2 (mod p). + + h is the cofactor of the curve. + hash_func is the hash function associated with the curve + (like SHA-512 for Ed25519) + """ + self.__p = mpz(p) + self.__a = mpz(a) + self.__d = mpz(d) + self.__h = h + self.__hash_func = hash_func + + else: + + def __init__(self, p, a, d, h=None, hash_func=None): + """ + The curve of points satisfying a*x^2 + y^2 = 1 + d*x^2*y^2 (mod p). + + h is the cofactor of the curve. + hash_func is the hash function associated with the curve + (like SHA-512 for Ed25519) + """ + self.__p = p + self.__a = a + self.__d = d + self.__h = h + self.__hash_func = hash_func + + def __eq__(self, other): + """Returns True if other is an identical curve.""" + if isinstance(other, CurveEdTw): + p = self.__p + return ( + self.__p == other.__p + and self.__a % p == other.__a % p + and self.__d % p == other.__d % p + ) + return NotImplemented + + def __ne__(self, other): + """Return False if the other is an identical curve, True otherwise.""" + return not self == other + + def __hash__(self): + return hash((self.__p, self.__a, self.__d)) + + def contains_point(self, x, y): + """Is the point (x, y) on this curve?""" + return ( + self.__a * x * x + y * y - 1 - self.__d * x * x * y * y + ) % self.__p == 0 + + def p(self): + return self.__p + + def a(self): + return self.__a + + def d(self): + return self.__d + + def hash_func(self, data): + return self.__hash_func(data) + + def cofactor(self): + return self.__h + + def __str__(self): + if self.__h is not None: + return "CurveEdTw(p={0}, a={1}, d={2}, h={3})".format( + self.__p, + self.__a, + self.__d, + self.__h, + ) + return "CurveEdTw(p={0}, a={1}, d={2})".format( + self.__p, + self.__a, + self.__d, + ) + + +class AbstractPoint(object): + """Class for common methods of elliptic curve points.""" + + @staticmethod + def _from_raw_encoding(data, raw_encoding_length): + """ + Decode public point from :term:`raw encoding`. + + :term:`raw encoding` is the same as the :term:`uncompressed` encoding, + but without the 0x04 byte at the beginning. + """ + # real assert, from_bytes() should not call us with different length + assert len(data) == raw_encoding_length + xs = data[: raw_encoding_length // 2] + ys = data[raw_encoding_length // 2 :] + # real assert, raw_encoding_length is calculated by multiplying an + # integer by two so it will always be even + assert len(xs) == raw_encoding_length // 2 + assert len(ys) == raw_encoding_length // 2 + coord_x = string_to_number(xs) + coord_y = string_to_number(ys) + + return coord_x, coord_y + + @staticmethod + def _from_compressed(data, curve): + """Decode public point from compressed encoding.""" + if data[:1] not in (b"\x02", b"\x03"): + raise MalformedPointError("Malformed compressed point encoding") + + is_even = data[:1] == b"\x02" + x = string_to_number(data[1:]) + p = curve.p() + alpha = (pow(x, 3, p) + (curve.a() * x) + curve.b()) % p + try: + beta = numbertheory.square_root_mod_prime(alpha, p) + except numbertheory.Error as e: + raise MalformedPointError( + "Encoding does not correspond to a point on curve", e + ) + if is_even == bool(beta & 1): + y = p - beta + else: + y = beta + return x, y + + @classmethod + def _from_hybrid(cls, data, raw_encoding_length, validate_encoding): + """Decode public point from hybrid encoding.""" + # real assert, from_bytes() should not call us with different types + assert data[:1] in (b"\x06", b"\x07") + + # primarily use the uncompressed as it's easiest to handle + x, y = cls._from_raw_encoding(data[1:], raw_encoding_length) + + # but validate if it's self-consistent if we're asked to do that + if validate_encoding and ( + y & 1 + and data[:1] != b"\x07" + or (not y & 1) + and data[:1] != b"\x06" + ): + raise MalformedPointError("Inconsistent hybrid point encoding") + + return x, y + + @classmethod + def _from_edwards(cls, curve, data): + """Decode a point on an Edwards curve.""" + data = bytearray(data) + p = curve.p() + # add 1 for the sign bit and then round up + exp_len = (bit_length(p) + 1 + 7) // 8 + if len(data) != exp_len: + raise MalformedPointError("Point length doesn't match the curve.") + x_0 = (data[-1] & 0x80) >> 7 + + data[-1] &= 0x80 - 1 + + y = bytes_to_int(data, "little") + if GMPY: + y = mpz(y) + + x2 = ( + (y * y - 1) + * numbertheory.inverse_mod(curve.d() * y * y - curve.a(), p) + % p + ) + + try: + x = numbertheory.square_root_mod_prime(x2, p) + except numbertheory.Error as e: + raise MalformedPointError( + "Encoding does not correspond to a point on curve", e + ) + + if x % 2 != x_0: + x = -x % p + + return x, y + + @classmethod + def from_bytes( + cls, curve, data, validate_encoding=True, valid_encodings=None + ): + """ + Initialise the object from byte encoding of a point. + + The method does accept and automatically detect the type of point + encoding used. It supports the :term:`raw encoding`, + :term:`uncompressed`, :term:`compressed`, and :term:`hybrid` encodings. + + Note: generally you will want to call the ``from_bytes()`` method of + either a child class, PointJacobi or Point. + + :param data: single point encoding of the public key + :type data: :term:`bytes-like object` + :param curve: the curve on which the public key is expected to lay + :type curve: ~ecdsa.ellipticcurve.CurveFp + :param validate_encoding: whether to verify that the encoding of the + point is self-consistent, defaults to True, has effect only + on ``hybrid`` encoding + :type validate_encoding: bool + :param valid_encodings: list of acceptable point encoding formats, + supported ones are: :term:`uncompressed`, :term:`compressed`, + :term:`hybrid`, and :term:`raw encoding` (specified with ``raw`` + name). All formats by default (specified with ``None``). + :type valid_encodings: :term:`set-like object` + + :raises `~ecdsa.errors.MalformedPointError`: if the public point does + not lay on the curve or the encoding is invalid + + :return: x and y coordinates of the encoded point + :rtype: tuple(int, int) + """ + if not valid_encodings: + valid_encodings = set( + ["uncompressed", "compressed", "hybrid", "raw"] + ) + if not all( + i in set(("uncompressed", "compressed", "hybrid", "raw")) + for i in valid_encodings + ): + raise ValueError( + "Only uncompressed, compressed, hybrid or raw encoding " + "supported." + ) + data = normalise_bytes(data) + + if isinstance(curve, CurveEdTw): + return cls._from_edwards(curve, data) + + key_len = len(data) + raw_encoding_length = 2 * orderlen(curve.p()) + if key_len == raw_encoding_length and "raw" in valid_encodings: + coord_x, coord_y = cls._from_raw_encoding( + data, raw_encoding_length + ) + elif key_len == raw_encoding_length + 1 and ( + "hybrid" in valid_encodings or "uncompressed" in valid_encodings + ): + if data[:1] in (b"\x06", b"\x07") and "hybrid" in valid_encodings: + coord_x, coord_y = cls._from_hybrid( + data, raw_encoding_length, validate_encoding + ) + elif data[:1] == b"\x04" and "uncompressed" in valid_encodings: + coord_x, coord_y = cls._from_raw_encoding( + data[1:], raw_encoding_length + ) + else: + raise MalformedPointError( + "Invalid X9.62 encoding of the public point" + ) + elif ( + key_len == raw_encoding_length // 2 + 1 + and "compressed" in valid_encodings + ): + coord_x, coord_y = cls._from_compressed(data, curve) + else: + raise MalformedPointError( + "Length of string does not match lengths of " + "any of the enabled ({0}) encodings of the " + "curve.".format(", ".join(valid_encodings)) + ) + return coord_x, coord_y + + def _raw_encode(self): + """Convert the point to the :term:`raw encoding`.""" + prime = self.curve().p() + x_str = number_to_string(self.x(), prime) + y_str = number_to_string(self.y(), prime) + return x_str + y_str + + def _compressed_encode(self): + """Encode the point into the compressed form.""" + prime = self.curve().p() + x_str = number_to_string(self.x(), prime) + if self.y() & 1: + return b"\x03" + x_str + return b"\x02" + x_str + + def _hybrid_encode(self): + """Encode the point into the hybrid form.""" + raw_enc = self._raw_encode() + if self.y() & 1: + return b"\x07" + raw_enc + return b"\x06" + raw_enc + + def _edwards_encode(self): + """Encode the point according to RFC8032 encoding.""" + self.scale() + x, y, p = self.x(), self.y(), self.curve().p() + + # add 1 for the sign bit and then round up + enc_len = (bit_length(p) + 1 + 7) // 8 + y_str = int_to_bytes(y, enc_len, "little") + if x % 2: + y_str[-1] |= 0x80 + return y_str + + def to_bytes(self, encoding="raw"): + """ + Convert the point to a byte string. + + The method by default uses the :term:`raw encoding` (specified + by `encoding="raw"`. It can also output points in :term:`uncompressed`, + :term:`compressed`, and :term:`hybrid` formats. + + For points on Edwards curves `encoding` is ignored and only the + encoding defined in RFC 8032 is supported. + + :return: :term:`raw encoding` of a public on the curve + :rtype: bytes + """ + assert encoding in ("raw", "uncompressed", "compressed", "hybrid") + curve = self.curve() + if isinstance(curve, CurveEdTw): + return self._edwards_encode() + elif encoding == "raw": + return self._raw_encode() + elif encoding == "uncompressed": + return b"\x04" + self._raw_encode() + elif encoding == "hybrid": + return self._hybrid_encode() + else: + return self._compressed_encode() + + @staticmethod + def _naf(mult): + """Calculate non-adjacent form of number.""" + ret = [] + while mult: + if mult % 2: + nd = mult % 4 + if nd >= 2: + nd -= 4 + ret.append(nd) + mult -= nd + else: + ret.append(0) + mult //= 2 + return ret + + +class PointJacobi(AbstractPoint): + """ + Point on a short Weierstrass elliptic curve. Uses Jacobi coordinates. + + In Jacobian coordinates, there are three parameters, X, Y and Z. + They correspond to affine parameters 'x' and 'y' like so: + + x = X / Z² + y = Y / Z³ + """ + + def __init__(self, curve, x, y, z, order=None, generator=False): + """ + Initialise a point that uses Jacobi representation internally. + + :param CurveFp curve: curve on which the point resides + :param int x: the X parameter of Jacobi representation (equal to x when + converting from affine coordinates + :param int y: the Y parameter of Jacobi representation (equal to y when + converting from affine coordinates + :param int z: the Z parameter of Jacobi representation (equal to 1 when + converting from affine coordinates + :param int order: the point order, must be non zero when using + generator=True + :param bool generator: the point provided is a curve generator, as + such, it will be commonly used with scalar multiplication. This will + cause to precompute multiplication table generation for it + """ + super(PointJacobi, self).__init__() + self.__curve = curve + if GMPY: # pragma: no branch + self.__coords = (mpz(x), mpz(y), mpz(z)) + self.__order = order and mpz(order) + else: # pragma: no branch + self.__coords = (x, y, z) + self.__order = order + self.__generator = generator + self.__precompute = [] + + @classmethod + def from_bytes( + cls, + curve, + data, + validate_encoding=True, + valid_encodings=None, + order=None, + generator=False, + ): + """ + Initialise the object from byte encoding of a point. + + The method does accept and automatically detect the type of point + encoding used. It supports the :term:`raw encoding`, + :term:`uncompressed`, :term:`compressed`, and :term:`hybrid` encodings. + + :param data: single point encoding of the public key + :type data: :term:`bytes-like object` + :param curve: the curve on which the public key is expected to lay + :type curve: ~ecdsa.ellipticcurve.CurveFp + :param validate_encoding: whether to verify that the encoding of the + point is self-consistent, defaults to True, has effect only + on ``hybrid`` encoding + :type validate_encoding: bool + :param valid_encodings: list of acceptable point encoding formats, + supported ones are: :term:`uncompressed`, :term:`compressed`, + :term:`hybrid`, and :term:`raw encoding` (specified with ``raw`` + name). All formats by default (specified with ``None``). + :type valid_encodings: :term:`set-like object` + :param int order: the point order, must be non zero when using + generator=True + :param bool generator: the point provided is a curve generator, as + such, it will be commonly used with scalar multiplication. This + will cause to precompute multiplication table generation for it + + :raises `~ecdsa.errors.MalformedPointError`: if the public point does + not lay on the curve or the encoding is invalid + + :return: Point on curve + :rtype: PointJacobi + """ + coord_x, coord_y = super(PointJacobi, cls).from_bytes( + curve, data, validate_encoding, valid_encodings + ) + return PointJacobi(curve, coord_x, coord_y, 1, order, generator) + + def _maybe_precompute(self): + if not self.__generator or self.__precompute: + return + + # since this code will execute just once, and it's fully deterministic, + # depend on atomicity of the last assignment to switch from empty + # self.__precompute to filled one and just ignore the unlikely + # situation when two threads execute it at the same time (as it won't + # lead to inconsistent __precompute) + order = self.__order + assert order + precompute = [] + i = 1 + order *= 2 + coord_x, coord_y, coord_z = self.__coords + doubler = PointJacobi(self.__curve, coord_x, coord_y, coord_z, order) + order *= 2 + precompute.append((doubler.x(), doubler.y())) + + while i < order: + i *= 2 + doubler = doubler.double().scale() + precompute.append((doubler.x(), doubler.y())) + + self.__precompute = precompute + + def __getstate__(self): + # while this code can execute at the same time as _maybe_precompute() + # is updating the __precompute or scale() is updating the __coords, + # there is no requirement for consistency between __coords and + # __precompute + state = self.__dict__.copy() + return state + + def __setstate__(self, state): + self.__dict__.update(state) + + def __eq__(self, other): + """Compare for equality two points with each-other. + + Note: only points that lay on the same curve can be equal. + """ + x1, y1, z1 = self.__coords + if other is INFINITY: + return not y1 or not z1 + if isinstance(other, Point): + x2, y2, z2 = other.x(), other.y(), 1 + elif isinstance(other, PointJacobi): + x2, y2, z2 = other.__coords + else: + return NotImplemented + if self.__curve != other.curve(): + return False + p = self.__curve.p() + + zz1 = z1 * z1 % p + zz2 = z2 * z2 % p + + # compare the fractions by bringing them to the same denominator + # depend on short-circuit to save 4 multiplications in case of + # inequality + return (x1 * zz2 - x2 * zz1) % p == 0 and ( + y1 * zz2 * z2 - y2 * zz1 * z1 + ) % p == 0 + + def __ne__(self, other): + """Compare for inequality two points with each-other.""" + return not self == other + + def order(self): + """Return the order of the point. + + None if it is undefined. + """ + return self.__order + + def curve(self): + """Return curve over which the point is defined.""" + return self.__curve + + def x(self): + """ + Return affine x coordinate. + + This method should be used only when the 'y' coordinate is not needed. + It's computationally more efficient to use `to_affine()` and then + call x() and y() on the returned instance. Or call `scale()` + and then x() and y() on the returned instance. + """ + x, _, z = self.__coords + if z == 1: + return x + p = self.__curve.p() + z = numbertheory.inverse_mod(z, p) + return x * z**2 % p + + def y(self): + """ + Return affine y coordinate. + + This method should be used only when the 'x' coordinate is not needed. + It's computationally more efficient to use `to_affine()` and then + call x() and y() on the returned instance. Or call `scale()` + and then x() and y() on the returned instance. + """ + _, y, z = self.__coords + if z == 1: + return y + p = self.__curve.p() + z = numbertheory.inverse_mod(z, p) + return y * z**3 % p + + def scale(self): + """ + Return point scaled so that z == 1. + + Modifies point in place, returns self. + """ + x, y, z = self.__coords + if z == 1: + return self + + # scaling is deterministic, so even if two threads execute the below + # code at the same time, they will set __coords to the same value + p = self.__curve.p() + z_inv = numbertheory.inverse_mod(z, p) + zz_inv = z_inv * z_inv % p + x = x * zz_inv % p + y = y * zz_inv * z_inv % p + self.__coords = (x, y, 1) + return self + + def to_affine(self): + """Return point in affine form.""" + _, y, z = self.__coords + if not y or not z: + return INFINITY + self.scale() + x, y, z = self.__coords + return Point(self.__curve, x, y, self.__order) + + @staticmethod + def from_affine(point, generator=False): + """Create from an affine point. + + :param bool generator: set to True to make the point to precalculate + multiplication table - useful for public point when verifying many + signatures (around 100 or so) or for generator points of a curve. + """ + return PointJacobi( + point.curve(), point.x(), point.y(), 1, point.order(), generator + ) + + # please note that all the methods that use the equations from + # hyperelliptic + # are formatted in a way to maximise performance. + # Things that make code faster: multiplying instead of taking to the power + # (`xx = x * x; xxxx = xx * xx % p` is faster than `xxxx = x**4 % p` and + # `pow(x, 4, p)`), + # multiple assignments at the same time (`x1, x2 = self.x1, self.x2` is + # faster than `x1 = self.x1; x2 = self.x2`), + # similarly, sometimes the `% p` is skipped if it makes the calculation + # faster and the result of calculation is later reduced modulo `p` + + def _double_with_z_1(self, X1, Y1, p, a): + """Add a point to itself with z == 1.""" + # after: + # http://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian.html#doubling-mdbl-2007-bl + XX, YY = X1 * X1 % p, Y1 * Y1 % p + if not YY: + return 0, 0, 1 + YYYY = YY * YY % p + S = 2 * ((X1 + YY) ** 2 - XX - YYYY) % p + M = 3 * XX + a + T = (M * M - 2 * S) % p + # X3 = T + Y3 = (M * (S - T) - 8 * YYYY) % p + Z3 = 2 * Y1 % p + return T, Y3, Z3 + + def _double(self, X1, Y1, Z1, p, a): + """Add a point to itself, arbitrary z.""" + if Z1 == 1: + return self._double_with_z_1(X1, Y1, p, a) + if not Y1 or not Z1: + return 0, 0, 1 + # after: + # http://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian.html#doubling-dbl-2007-bl + XX, YY = X1 * X1 % p, Y1 * Y1 % p + if not YY: + return 0, 0, 1 + YYYY = YY * YY % p + ZZ = Z1 * Z1 % p + S = 2 * ((X1 + YY) ** 2 - XX - YYYY) % p + M = (3 * XX + a * ZZ * ZZ) % p + T = (M * M - 2 * S) % p + # X3 = T + Y3 = (M * (S - T) - 8 * YYYY) % p + Z3 = ((Y1 + Z1) ** 2 - YY - ZZ) % p + + return T, Y3, Z3 + + def double(self): + """Add a point to itself.""" + X1, Y1, Z1 = self.__coords + + if not Y1: + return INFINITY + + p, a = self.__curve.p(), self.__curve.a() + + X3, Y3, Z3 = self._double(X1, Y1, Z1, p, a) + + if not Y3 or not Z3: + return INFINITY + return PointJacobi(self.__curve, X3, Y3, Z3, self.__order) + + def _add_with_z_1(self, X1, Y1, X2, Y2, p): + """add points when both Z1 and Z2 equal 1""" + # after: + # http://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian.html#addition-mmadd-2007-bl + H = X2 - X1 + HH = H * H + I = 4 * HH % p + J = H * I + r = 2 * (Y2 - Y1) + if not H and not r: + return self._double_with_z_1(X1, Y1, p, self.__curve.a()) + V = X1 * I + X3 = (r**2 - J - 2 * V) % p + Y3 = (r * (V - X3) - 2 * Y1 * J) % p + Z3 = 2 * H % p + return X3, Y3, Z3 + + def _add_with_z_eq(self, X1, Y1, Z1, X2, Y2, p): + """add points when Z1 == Z2""" + # after: + # http://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian.html#addition-zadd-2007-m + A = (X2 - X1) ** 2 % p + B = X1 * A % p + C = X2 * A + D = (Y2 - Y1) ** 2 % p + if not A and not D: + return self._double(X1, Y1, Z1, p, self.__curve.a()) + X3 = (D - B - C) % p + Y3 = ((Y2 - Y1) * (B - X3) - Y1 * (C - B)) % p + Z3 = Z1 * (X2 - X1) % p + return X3, Y3, Z3 + + def _add_with_z2_1(self, X1, Y1, Z1, X2, Y2, p): + """add points when Z2 == 1""" + # after: + # http://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian.html#addition-madd-2007-bl + Z1Z1 = Z1 * Z1 % p + U2, S2 = X2 * Z1Z1 % p, Y2 * Z1 * Z1Z1 % p + H = (U2 - X1) % p + HH = H * H % p + I = 4 * HH % p + J = H * I + r = 2 * (S2 - Y1) % p + if not r and not H: + return self._double_with_z_1(X2, Y2, p, self.__curve.a()) + V = X1 * I + X3 = (r * r - J - 2 * V) % p + Y3 = (r * (V - X3) - 2 * Y1 * J) % p + Z3 = ((Z1 + H) ** 2 - Z1Z1 - HH) % p + return X3, Y3, Z3 + + def _add_with_z_ne(self, X1, Y1, Z1, X2, Y2, Z2, p): + """add points with arbitrary z""" + # after: + # http://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian.html#addition-add-2007-bl + Z1Z1 = Z1 * Z1 % p + Z2Z2 = Z2 * Z2 % p + U1 = X1 * Z2Z2 % p + U2 = X2 * Z1Z1 % p + S1 = Y1 * Z2 * Z2Z2 % p + S2 = Y2 * Z1 * Z1Z1 % p + H = U2 - U1 + I = 4 * H * H % p + J = H * I % p + r = 2 * (S2 - S1) % p + if not H and not r: + return self._double(X1, Y1, Z1, p, self.__curve.a()) + V = U1 * I + X3 = (r * r - J - 2 * V) % p + Y3 = (r * (V - X3) - 2 * S1 * J) % p + Z3 = ((Z1 + Z2) ** 2 - Z1Z1 - Z2Z2) * H % p + + return X3, Y3, Z3 + + def __radd__(self, other): + """Add other to self.""" + return self + other + + def _add(self, X1, Y1, Z1, X2, Y2, Z2, p): + """add two points, select fastest method.""" + if not Y1 or not Z1: + return X2, Y2, Z2 + if not Y2 or not Z2: + return X1, Y1, Z1 + if Z1 == Z2: + if Z1 == 1: + return self._add_with_z_1(X1, Y1, X2, Y2, p) + return self._add_with_z_eq(X1, Y1, Z1, X2, Y2, p) + if Z1 == 1: + return self._add_with_z2_1(X2, Y2, Z2, X1, Y1, p) + if Z2 == 1: + return self._add_with_z2_1(X1, Y1, Z1, X2, Y2, p) + return self._add_with_z_ne(X1, Y1, Z1, X2, Y2, Z2, p) + + def __add__(self, other): + """Add two points on elliptic curve.""" + if self == INFINITY: + return other + if other == INFINITY: + return self + if isinstance(other, Point): + other = PointJacobi.from_affine(other) + if self.__curve != other.__curve: + raise ValueError("The other point is on different curve") + + p = self.__curve.p() + X1, Y1, Z1 = self.__coords + X2, Y2, Z2 = other.__coords + + X3, Y3, Z3 = self._add(X1, Y1, Z1, X2, Y2, Z2, p) + + if not Y3 or not Z3: + return INFINITY + return PointJacobi(self.__curve, X3, Y3, Z3, self.__order) + + def __rmul__(self, other): + """Multiply point by an integer.""" + return self * other + + def _mul_precompute(self, other): + """Multiply point by integer with precomputation table.""" + X3, Y3, Z3, p = 0, 0, 1, self.__curve.p() + _add = self._add + for X2, Y2 in self.__precompute: + if other % 2: + if other % 4 >= 2: + other = (other + 1) // 2 + X3, Y3, Z3 = _add(X3, Y3, Z3, X2, -Y2, 1, p) + else: + other = (other - 1) // 2 + X3, Y3, Z3 = _add(X3, Y3, Z3, X2, Y2, 1, p) + else: + other //= 2 + + if not Y3 or not Z3: + return INFINITY + return PointJacobi(self.__curve, X3, Y3, Z3, self.__order) + + def __mul__(self, other): + """Multiply point by an integer.""" + if not self.__coords[1] or not other: + return INFINITY + if other == 1: + return self + if self.__order: + # order*2 as a protection for Minerva + other = other % (self.__order * 2) + self._maybe_precompute() + if self.__precompute: + return self._mul_precompute(other) + + self = self.scale() + X2, Y2, _ = self.__coords + X3, Y3, Z3 = 0, 0, 1 + p, a = self.__curve.p(), self.__curve.a() + _double = self._double + _add = self._add + # since adding points when at least one of them is scaled + # is quicker, reverse the NAF order + for i in reversed(self._naf(other)): + X3, Y3, Z3 = _double(X3, Y3, Z3, p, a) + if i < 0: + X3, Y3, Z3 = _add(X3, Y3, Z3, X2, -Y2, 1, p) + elif i > 0: + X3, Y3, Z3 = _add(X3, Y3, Z3, X2, Y2, 1, p) + + if not Y3 or not Z3: + return INFINITY + + return PointJacobi(self.__curve, X3, Y3, Z3, self.__order) + + def mul_add(self, self_mul, other, other_mul): + """ + Do two multiplications at the same time, add results. + + calculates self*self_mul + other*other_mul + """ + if other == INFINITY or other_mul == 0: + return self * self_mul + if self_mul == 0: + return other * other_mul + if not isinstance(other, PointJacobi): + other = PointJacobi.from_affine(other) + # when the points have precomputed answers, then multiplying them alone + # is faster (as it uses NAF and no point doublings) + self._maybe_precompute() + other._maybe_precompute() + if self.__precompute and other.__precompute: + return self * self_mul + other * other_mul + + if self.__order: + self_mul = self_mul % self.__order + other_mul = other_mul % self.__order + + # (X3, Y3, Z3) is the accumulator + X3, Y3, Z3 = 0, 0, 1 + p, a = self.__curve.p(), self.__curve.a() + + # as we have 6 unique points to work with, we can't scale all of them, + # but do scale the ones that are used most often + self.scale() + X1, Y1, Z1 = self.__coords + other.scale() + X2, Y2, Z2 = other.__coords + + _double = self._double + _add = self._add + + # with NAF we have 3 options: no add, subtract, add + # so with 2 points, we have 9 combinations: + # 0, -A, +A, -B, -A-B, +A-B, +B, -A+B, +A+B + # so we need 4 combined points: + mAmB_X, mAmB_Y, mAmB_Z = _add(X1, -Y1, Z1, X2, -Y2, Z2, p) + pAmB_X, pAmB_Y, pAmB_Z = _add(X1, Y1, Z1, X2, -Y2, Z2, p) + mApB_X, mApB_Y, mApB_Z = pAmB_X, -pAmB_Y, pAmB_Z + pApB_X, pApB_Y, pApB_Z = mAmB_X, -mAmB_Y, mAmB_Z + # when the self and other sum to infinity, we need to add them + # one by one to get correct result but as that's very unlikely to + # happen in regular operation, we don't need to optimise this case + if not pApB_Y or not pApB_Z: + return self * self_mul + other * other_mul + + # gmp object creation has cumulatively higher overhead than the + # speedup we get from calculating the NAF using gmp so ensure use + # of int() + self_naf = list(reversed(self._naf(int(self_mul)))) + other_naf = list(reversed(self._naf(int(other_mul)))) + # ensure that the lists are the same length (zip() will truncate + # longer one otherwise) + if len(self_naf) < len(other_naf): + self_naf = [0] * (len(other_naf) - len(self_naf)) + self_naf + elif len(self_naf) > len(other_naf): + other_naf = [0] * (len(self_naf) - len(other_naf)) + other_naf + + for A, B in zip(self_naf, other_naf): + X3, Y3, Z3 = _double(X3, Y3, Z3, p, a) + + # conditions ordered from most to least likely + if A == 0: + if B == 0: + pass + elif B < 0: + X3, Y3, Z3 = _add(X3, Y3, Z3, X2, -Y2, Z2, p) + else: + assert B > 0 + X3, Y3, Z3 = _add(X3, Y3, Z3, X2, Y2, Z2, p) + elif A < 0: + if B == 0: + X3, Y3, Z3 = _add(X3, Y3, Z3, X1, -Y1, Z1, p) + elif B < 0: + X3, Y3, Z3 = _add(X3, Y3, Z3, mAmB_X, mAmB_Y, mAmB_Z, p) + else: + assert B > 0 + X3, Y3, Z3 = _add(X3, Y3, Z3, mApB_X, mApB_Y, mApB_Z, p) + else: + assert A > 0 + if B == 0: + X3, Y3, Z3 = _add(X3, Y3, Z3, X1, Y1, Z1, p) + elif B < 0: + X3, Y3, Z3 = _add(X3, Y3, Z3, pAmB_X, pAmB_Y, pAmB_Z, p) + else: + assert B > 0 + X3, Y3, Z3 = _add(X3, Y3, Z3, pApB_X, pApB_Y, pApB_Z, p) + + if not Y3 or not Z3: + return INFINITY + + return PointJacobi(self.__curve, X3, Y3, Z3, self.__order) + + def __neg__(self): + """Return negated point.""" + x, y, z = self.__coords + return PointJacobi(self.__curve, x, -y, z, self.__order) + + +class Point(AbstractPoint): + """A point on a short Weierstrass elliptic curve. Altering x and y is + forbidden, but they can be read by the x() and y() methods.""" + + def __init__(self, curve, x, y, order=None): + """curve, x, y, order; order (optional) is the order of this point.""" + super(Point, self).__init__() + self.__curve = curve + if GMPY: + self.__x = x and mpz(x) + self.__y = y and mpz(y) + self.__order = order and mpz(order) + else: + self.__x = x + self.__y = y + self.__order = order + # self.curve is allowed to be None only for INFINITY: + if self.__curve: + assert self.__curve.contains_point(x, y) + # for curves with cofactor 1, all points that are on the curve are + # scalar multiples of the base point, so performing multiplication is + # not necessary to verify that. See Section 3.2.2.1 of SEC 1 v2 + if curve and curve.cofactor() != 1 and order: + assert self * order == INFINITY + + @classmethod + def from_bytes( + cls, + curve, + data, + validate_encoding=True, + valid_encodings=None, + order=None, + ): + """ + Initialise the object from byte encoding of a point. + + The method does accept and automatically detect the type of point + encoding used. It supports the :term:`raw encoding`, + :term:`uncompressed`, :term:`compressed`, and :term:`hybrid` encodings. + + :param data: single point encoding of the public key + :type data: :term:`bytes-like object` + :param curve: the curve on which the public key is expected to lay + :type curve: ~ecdsa.ellipticcurve.CurveFp + :param validate_encoding: whether to verify that the encoding of the + point is self-consistent, defaults to True, has effect only + on ``hybrid`` encoding + :type validate_encoding: bool + :param valid_encodings: list of acceptable point encoding formats, + supported ones are: :term:`uncompressed`, :term:`compressed`, + :term:`hybrid`, and :term:`raw encoding` (specified with ``raw`` + name). All formats by default (specified with ``None``). + :type valid_encodings: :term:`set-like object` + :param int order: the point order, must be non zero when using + generator=True + + :raises `~ecdsa.errors.MalformedPointError`: if the public point does + not lay on the curve or the encoding is invalid + + :return: Point on curve + :rtype: Point + """ + coord_x, coord_y = super(Point, cls).from_bytes( + curve, data, validate_encoding, valid_encodings + ) + return Point(curve, coord_x, coord_y, order) + + def __eq__(self, other): + """Return True if the points are identical, False otherwise. + + Note: only points that lay on the same curve can be equal. + """ + if isinstance(other, Point): + return ( + self.__curve == other.__curve + and self.__x == other.__x + and self.__y == other.__y + ) + return NotImplemented + + def __ne__(self, other): + """Returns False if points are identical, True otherwise.""" + return not self == other + + def __neg__(self): + return Point(self.__curve, self.__x, self.__curve.p() - self.__y) + + def __add__(self, other): + """Add one point to another point.""" + + # X9.62 B.3: + + if not isinstance(other, Point): + return NotImplemented + if other == INFINITY: + return self + if self == INFINITY: + return other + assert self.__curve == other.__curve + if self.__x == other.__x: + if (self.__y + other.__y) % self.__curve.p() == 0: + return INFINITY + else: + return self.double() + + p = self.__curve.p() + + l = ( + (other.__y - self.__y) + * numbertheory.inverse_mod(other.__x - self.__x, p) + ) % p + + x3 = (l * l - self.__x - other.__x) % p + y3 = (l * (self.__x - x3) - self.__y) % p + + return Point(self.__curve, x3, y3) + + def __mul__(self, other): + """Multiply a point by an integer.""" + + def leftmost_bit(x): + assert x > 0 + result = 1 + while result <= x: + result = 2 * result + return result // 2 + + e = other + if e == 0 or (self.__order and e % self.__order == 0): + return INFINITY + if self == INFINITY: + return INFINITY + if e < 0: + return (-self) * (-e) + + # From X9.62 D.3.2: + + e3 = 3 * e + negative_self = Point(self.__curve, self.__x, -self.__y, self.__order) + i = leftmost_bit(e3) // 2 + result = self + # print_("Multiplying %s by %d (e3 = %d):" % (self, other, e3)) + while i > 1: + result = result.double() + if (e3 & i) != 0 and (e & i) == 0: + result = result + self + if (e3 & i) == 0 and (e & i) != 0: + result = result + negative_self + # print_(". . . i = %d, result = %s" % ( i, result )) + i = i // 2 + + return result + + def __rmul__(self, other): + """Multiply a point by an integer.""" + + return self * other + + def __str__(self): + if self == INFINITY: + return "infinity" + return "(%d,%d)" % (self.__x, self.__y) + + def double(self): + """Return a new point that is twice the old.""" + + if self == INFINITY: + return INFINITY + + # X9.62 B.3: + + p = self.__curve.p() + a = self.__curve.a() + + l = ( + (3 * self.__x * self.__x + a) + * numbertheory.inverse_mod(2 * self.__y, p) + ) % p + + x3 = (l * l - 2 * self.__x) % p + y3 = (l * (self.__x - x3) - self.__y) % p + + return Point(self.__curve, x3, y3) + + def x(self): + return self.__x + + def y(self): + return self.__y + + def curve(self): + return self.__curve + + def order(self): + return self.__order + + +class PointEdwards(AbstractPoint): + """Point on Twisted Edwards curve. + + Internally represents the coordinates on the curve using four parameters, + X, Y, Z, T. They correspond to affine parameters 'x' and 'y' like so: + + x = X / Z + y = Y / Z + x*y = T / Z + """ + + def __init__(self, curve, x, y, z, t, order=None, generator=False): + """ + Initialise a point that uses the extended coordinates internally. + """ + super(PointEdwards, self).__init__() + self.__curve = curve + if GMPY: # pragma: no branch + self.__coords = (mpz(x), mpz(y), mpz(z), mpz(t)) + self.__order = order and mpz(order) + else: # pragma: no branch + self.__coords = (x, y, z, t) + self.__order = order + self.__generator = generator + self.__precompute = [] + + @classmethod + def from_bytes( + cls, + curve, + data, + validate_encoding=None, + valid_encodings=None, + order=None, + generator=False, + ): + """ + Initialise the object from byte encoding of a point. + + `validate_encoding` and `valid_encodings` are provided for + compatibility with Weierstrass curves, they are ignored for Edwards + points. + + :param data: single point encoding of the public key + :type data: :term:`bytes-like object` + :param curve: the curve on which the public key is expected to lay + :type curve: ecdsa.ellipticcurve.CurveEdTw + :param None validate_encoding: Ignored, encoding is always validated + :param None valid_encodings: Ignored, there is just one encoding + supported + :param int order: the point order, must be non zero when using + generator=True + :param bool generator: Flag to mark the point as a curve generator, + this will cause the library to pre-compute some values to + make repeated usages of the point much faster + + :raises `~ecdsa.errors.MalformedPointError`: if the public point does + not lay on the curve or the encoding is invalid + + :return: Initialised point on an Edwards curve + :rtype: PointEdwards + """ + coord_x, coord_y = super(PointEdwards, cls).from_bytes( + curve, data, validate_encoding, valid_encodings + ) + return PointEdwards( + curve, coord_x, coord_y, 1, coord_x * coord_y, order, generator + ) + + def _maybe_precompute(self): + if not self.__generator or self.__precompute: + return self.__precompute + + # since this code will execute just once, and it's fully deterministic, + # depend on atomicity of the last assignment to switch from empty + # self.__precompute to filled one and just ignore the unlikely + # situation when two threads execute it at the same time (as it won't + # lead to inconsistent __precompute) + order = self.__order + assert order + precompute = [] + i = 1 + order *= 2 + coord_x, coord_y, coord_z, coord_t = self.__coords + prime = self.__curve.p() + + doubler = PointEdwards( + self.__curve, coord_x, coord_y, coord_z, coord_t, order + ) + # for "protection" against Minerva we need 1 or 2 more bits depending + # on order bit size, but it's easier to just calculate one + # point more always + order *= 4 + + while i < order: + doubler = doubler.scale() + coord_x, coord_y = doubler.x(), doubler.y() + coord_t = coord_x * coord_y % prime + precompute.append((coord_x, coord_y, coord_t)) + + i *= 2 + doubler = doubler.double() + + self.__precompute = precompute + return self.__precompute + + def x(self): + """Return affine x coordinate.""" + X1, _, Z1, _ = self.__coords + if Z1 == 1: + return X1 + p = self.__curve.p() + z_inv = numbertheory.inverse_mod(Z1, p) + return X1 * z_inv % p + + def y(self): + """Return affine y coordinate.""" + _, Y1, Z1, _ = self.__coords + if Z1 == 1: + return Y1 + p = self.__curve.p() + z_inv = numbertheory.inverse_mod(Z1, p) + return Y1 * z_inv % p + + def curve(self): + """Return the curve of the point.""" + return self.__curve + + def order(self): + return self.__order + + def scale(self): + """ + Return point scaled so that z == 1. + + Modifies point in place, returns self. + """ + X1, Y1, Z1, _ = self.__coords + if Z1 == 1: + return self + + p = self.__curve.p() + z_inv = numbertheory.inverse_mod(Z1, p) + x = X1 * z_inv % p + y = Y1 * z_inv % p + t = x * y % p + self.__coords = (x, y, 1, t) + return self + + def __eq__(self, other): + """Compare for equality two points with each-other. + + Note: only points on the same curve can be equal. + """ + x1, y1, z1, t1 = self.__coords + if other is INFINITY: + return not x1 or not t1 + if isinstance(other, PointEdwards): + x2, y2, z2, t2 = other.__coords + else: + return NotImplemented + if self.__curve != other.curve(): + return False + p = self.__curve.p() + + # cross multiply to eliminate divisions + xn1 = x1 * z2 % p + xn2 = x2 * z1 % p + yn1 = y1 * z2 % p + yn2 = y2 * z1 % p + return xn1 == xn2 and yn1 == yn2 + + def __ne__(self, other): + """Compare for inequality two points with each-other.""" + return not self == other + + def _add(self, X1, Y1, Z1, T1, X2, Y2, Z2, T2, p, a): + """add two points, assume sane parameters.""" + # after add-2008-hwcd-2 + # from https://hyperelliptic.org/EFD/g1p/auto-twisted-extended.html + # NOTE: there are more efficient formulas for Z1 or Z2 == 1 + A = X1 * X2 % p + B = Y1 * Y2 % p + C = Z1 * T2 % p + D = T1 * Z2 % p + E = D + C + F = ((X1 - Y1) * (X2 + Y2) + B - A) % p + G = B + a * A + H = D - C + if not H: + return self._double(X1, Y1, Z1, T1, p, a) + X3 = E * F % p + Y3 = G * H % p + T3 = E * H % p + Z3 = F * G % p + + return X3, Y3, Z3, T3 + + def __add__(self, other): + """Add point to another.""" + if other == INFINITY: + return self + if ( + not isinstance(other, PointEdwards) + or self.__curve != other.__curve + ): + raise ValueError("The other point is on a different curve.") + + p, a = self.__curve.p(), self.__curve.a() + X1, Y1, Z1, T1 = self.__coords + X2, Y2, Z2, T2 = other.__coords + + X3, Y3, Z3, T3 = self._add(X1, Y1, Z1, T1, X2, Y2, Z2, T2, p, a) + + if not X3 or not T3: + return INFINITY + return PointEdwards(self.__curve, X3, Y3, Z3, T3, self.__order) + + def __radd__(self, other): + """Add other to self.""" + return self + other + + def _double(self, X1, Y1, Z1, T1, p, a): + """Double the point, assume sane parameters.""" + # after "dbl-2008-hwcd" + # from https://hyperelliptic.org/EFD/g1p/auto-twisted-extended.html + # NOTE: there are more efficient formulas for Z1 == 1 + A = X1 * X1 % p + B = Y1 * Y1 % p + C = 2 * Z1 * Z1 % p + D = a * A % p + E = ((X1 + Y1) * (X1 + Y1) - A - B) % p + G = D + B + F = G - C + H = D - B + X3 = E * F % p + Y3 = G * H % p + T3 = E * H % p + Z3 = F * G % p + + return X3, Y3, Z3, T3 + + def double(self): + """Return point added to itself.""" + X1, Y1, Z1, T1 = self.__coords + + if not X1 or not T1: + return INFINITY + + p, a = self.__curve.p(), self.__curve.a() + + X3, Y3, Z3, T3 = self._double(X1, Y1, Z1, T1, p, a) + + # both Ed25519 and Ed448 have prime order, so no point added to + # itself will equal zero + if not X3 or not T3: # pragma: no branch + return INFINITY + return PointEdwards(self.__curve, X3, Y3, Z3, T3, self.__order) + + def __rmul__(self, other): + """Multiply point by an integer.""" + return self * other + + def _mul_precompute(self, other): + """Multiply point by integer with precomputation table.""" + X3, Y3, Z3, T3, p, a = 0, 1, 1, 0, self.__curve.p(), self.__curve.a() + _add = self._add + for X2, Y2, T2 in self.__precompute: + rem = other % 4 + if rem == 0 or rem == 2: + other //= 2 + elif rem == 3: + other = (other + 1) // 2 + X3, Y3, Z3, T3 = _add(X3, Y3, Z3, T3, -X2, Y2, 1, -T2, p, a) + else: + assert rem == 1 + other = (other - 1) // 2 + X3, Y3, Z3, T3 = _add(X3, Y3, Z3, T3, X2, Y2, 1, T2, p, a) + + if not X3 or not T3: + return INFINITY + + return PointEdwards(self.__curve, X3, Y3, Z3, T3, self.__order) + + def __mul__(self, other): + """Multiply point by an integer.""" + X2, Y2, Z2, T2 = self.__coords + if not X2 or not T2 or not other: + return INFINITY + if other == 1: + return self + if self.__order: + # order*2 as a "protection" for Minerva + other = other % (self.__order * 2) + if self._maybe_precompute(): + return self._mul_precompute(other) + + X3, Y3, Z3, T3 = 0, 1, 1, 0 # INFINITY in extended coordinates + p, a = self.__curve.p(), self.__curve.a() + _double = self._double + _add = self._add + + for i in reversed(self._naf(other)): + X3, Y3, Z3, T3 = _double(X3, Y3, Z3, T3, p, a) + if i < 0: + X3, Y3, Z3, T3 = _add(X3, Y3, Z3, T3, -X2, Y2, Z2, -T2, p, a) + elif i > 0: + X3, Y3, Z3, T3 = _add(X3, Y3, Z3, T3, X2, Y2, Z2, T2, p, a) + + if not X3 or not T3: + return INFINITY + + return PointEdwards(self.__curve, X3, Y3, Z3, T3, self.__order) + + +# This one point is the Point At Infinity for all purposes: +INFINITY = Point(None, None, None) diff --git a/code/.venv/lib/python3.12/site-packages/ecdsa/errors.py b/code/.venv/lib/python3.12/site-packages/ecdsa/errors.py new file mode 100644 index 0000000..0184c05 --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/ecdsa/errors.py @@ -0,0 +1,4 @@ +class MalformedPointError(AssertionError): + """Raised in case the encoding of private or public key is malformed.""" + + pass diff --git a/code/.venv/lib/python3.12/site-packages/ecdsa/keys.py b/code/.venv/lib/python3.12/site-packages/ecdsa/keys.py new file mode 100644 index 0000000..f74252c --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/ecdsa/keys.py @@ -0,0 +1,1631 @@ +""" +Primary classes for performing signing and verification operations. +""" + +import binascii +from hashlib import sha1 +import os +from six import PY2 +from . import ecdsa, eddsa +from . import der, ssh +from . import rfc6979 +from . import ellipticcurve +from .curves import NIST192p, Curve, Ed25519, Ed448 +from .ecdsa import RSZeroError +from .util import string_to_number, number_to_string, randrange +from .util import sigencode_string, sigdecode_string, bit_length +from .util import ( + oid_ecPublicKey, + encoded_oid_ecPublicKey, + oid_ecDH, + oid_ecMQV, + MalformedSignature, +) +from ._compat import normalise_bytes +from .errors import MalformedPointError +from .ellipticcurve import PointJacobi, CurveEdTw + + +__all__ = [ + "BadSignatureError", + "BadDigestError", + "VerifyingKey", + "SigningKey", + "MalformedPointError", +] + + +class BadSignatureError(Exception): + """ + Raised when verification of signature failed. + + Will be raised irrespective of reason of the failure: + + * the calculated or provided hash does not match the signature + * the signature does not match the curve/public key + * the encoding of the signature is malformed + * the size of the signature does not match the curve of the VerifyingKey + """ + + pass + + +class BadDigestError(Exception): + """Raised in case the selected hash is too large for the curve.""" + + pass + + +def _truncate_and_convert_digest(digest, curve, allow_truncate): + """Truncates and converts digest to an integer.""" + if not allow_truncate: + if len(digest) > curve.baselen: + raise BadDigestError( + "this curve ({0}) is too short " + "for the length of your digest ({1})".format( + curve.name, 8 * len(digest) + ) + ) + else: + digest = digest[: curve.baselen] + number = string_to_number(digest) + if allow_truncate: + max_length = bit_length(curve.order) + # we don't use bit_length(number) as that truncates leading zeros + length = len(digest) * 8 + + # See NIST FIPS 186-4: + # + # When the length of the output of the hash function is greater + # than N (i.e., the bit length of q), then the leftmost N bits of + # the hash function output block shall be used in any calculation + # using the hash function output during the generation or + # verification of a digital signature. + # + # as such, we need to shift-out the low-order bits: + number >>= max(0, length - max_length) + + return number + + +class VerifyingKey(object): + """ + Class for handling keys that can verify signatures (public keys). + + :ivar `~ecdsa.curves.Curve` ~.curve: The Curve over which all the + cryptographic operations will take place + :ivar default_hashfunc: the function that will be used for hashing the + data. Should implement the same API as hashlib.sha1 + :vartype default_hashfunc: callable + :ivar pubkey: the actual public key + :vartype pubkey: ~ecdsa.ecdsa.Public_key + """ + + def __init__(self, _error__please_use_generate=None): + """Unsupported, please use one of the classmethods to initialise.""" + if not _error__please_use_generate: + raise TypeError( + "Please use VerifyingKey.generate() to construct me" + ) + self.curve = None + self.default_hashfunc = None + self.pubkey = None + + def __repr__(self): + pub_key = self.to_string("compressed") + if self.default_hashfunc: + hash_name = self.default_hashfunc().name + else: + hash_name = "None" + return "VerifyingKey.from_string({0!r}, {1!r}, {2})".format( + pub_key, self.curve, hash_name + ) + + def __eq__(self, other): + """Return True if the points are identical, False otherwise.""" + if isinstance(other, VerifyingKey): + return self.curve == other.curve and self.pubkey == other.pubkey + return NotImplemented + + def __ne__(self, other): + """Return False if the points are identical, True otherwise.""" + return not self == other + + @classmethod + def from_public_point( + cls, point, curve=NIST192p, hashfunc=sha1, validate_point=True + ): + """ + Initialise the object from a Point object. + + This is a low-level method, generally you will not want to use it. + + :param point: The point to wrap around, the actual public key + :type point: ~ecdsa.ellipticcurve.AbstractPoint + :param curve: The curve on which the point needs to reside, defaults + to NIST192p + :type curve: ~ecdsa.curves.Curve + :param hashfunc: The default hash function that will be used for + verification, needs to implement the same interface + as :py:class:`hashlib.sha1` + :type hashfunc: callable + :type bool validate_point: whether to check if the point lays on curve + should always be used if the public point is not a result + of our own calculation + + :raises MalformedPointError: if the public point does not lay on the + curve + + :return: Initialised VerifyingKey object + :rtype: VerifyingKey + """ + self = cls(_error__please_use_generate=True) + if isinstance(curve.curve, CurveEdTw): + raise ValueError("Method incompatible with Edwards curves") + if not isinstance(point, ellipticcurve.PointJacobi): + point = ellipticcurve.PointJacobi.from_affine(point) + self.curve = curve + self.default_hashfunc = hashfunc + try: + self.pubkey = ecdsa.Public_key( + curve.generator, point, validate_point + ) + except ecdsa.InvalidPointError: + raise MalformedPointError("Point does not lay on the curve") + self.pubkey.order = curve.order + return self + + def precompute(self, lazy=False): + """ + Precompute multiplication tables for faster signature verification. + + Calling this method will cause the library to precompute the + scalar multiplication tables, used in signature verification. + While it's an expensive operation (comparable to performing + as many signatures as the bit size of the curve, i.e. 256 for NIST256p) + it speeds up verification 2 times. You should call this method + if you expect to verify hundreds of signatures (or more) using the same + VerifyingKey object. + + Note: You should call this method only once, this method generates a + new precomputation table every time it's called. + + :param bool lazy: whether to calculate the precomputation table now + (if set to False) or if it should be delayed to the time of first + use (when set to True) + """ + if isinstance(self.curve.curve, CurveEdTw): + pt = self.pubkey.point + self.pubkey.point = ellipticcurve.PointEdwards( + pt.curve(), + pt.x(), + pt.y(), + 1, + pt.x() * pt.y(), + self.curve.order, + generator=True, + ) + else: + self.pubkey.point = ellipticcurve.PointJacobi.from_affine( + self.pubkey.point, True + ) + # as precomputation in now delayed to the time of first use of the + # point and we were asked specifically to precompute now, make + # sure the precomputation is performed now to preserve the behaviour + if not lazy: + self.pubkey.point * 2 + + @classmethod + def from_string( + cls, + string, + curve=NIST192p, + hashfunc=sha1, + validate_point=True, + valid_encodings=None, + ): + """ + Initialise the object from byte encoding of public key. + + The method does accept and automatically detect the type of point + encoding used. It supports the :term:`raw encoding`, + :term:`uncompressed`, :term:`compressed`, and :term:`hybrid` encodings. + It also works with the native encoding of Ed25519 and Ed448 public + keys (technically those are compressed, but encoded differently than + in other signature systems). + + Note, while the method is named "from_string" it's a misnomer from + Python 2 days when there were no binary strings. In Python 3 the + input needs to be a bytes-like object. + + :param string: single point encoding of the public key + :type string: :term:`bytes-like object` + :param curve: the curve on which the public key is expected to lay + :type curve: ~ecdsa.curves.Curve + :param hashfunc: The default hash function that will be used for + verification, needs to implement the same interface as + hashlib.sha1. Ignored for EdDSA. + :type hashfunc: callable + :param validate_point: whether to verify that the point lays on the + provided curve or not, defaults to True. Ignored for EdDSA. + :type validate_point: bool + :param valid_encodings: list of acceptable point encoding formats, + supported ones are: :term:`uncompressed`, :term:`compressed`, + :term:`hybrid`, and :term:`raw encoding` (specified with ``raw`` + name). All formats by default (specified with ``None``). + Ignored for EdDSA. + :type valid_encodings: :term:`set-like object` + + :raises MalformedPointError: if the public point does not lay on the + curve or the encoding is invalid + + :return: Initialised VerifyingKey object + :rtype: VerifyingKey + """ + if isinstance(curve.curve, CurveEdTw): + self = cls(_error__please_use_generate=True) + self.curve = curve + self.default_hashfunc = None # ignored for EdDSA + try: + self.pubkey = eddsa.PublicKey(curve.generator, string) + except ValueError: + raise MalformedPointError("Malformed point for the curve") + return self + + point = PointJacobi.from_bytes( + curve.curve, + string, + validate_encoding=validate_point, + valid_encodings=valid_encodings, + ) + return cls.from_public_point(point, curve, hashfunc, validate_point) + + @classmethod + def from_pem( + cls, + string, + hashfunc=sha1, + valid_encodings=None, + valid_curve_encodings=None, + ): + """ + Initialise from public key stored in :term:`PEM` format. + + The PEM header of the key should be ``BEGIN PUBLIC KEY``. + + See the :func:`~VerifyingKey.from_der()` method for details of the + format supported. + + Note: only a single PEM object decoding is supported in provided + string. + + :param string: text with PEM-encoded public ECDSA key + :type string: str + :param valid_encodings: list of allowed point encodings. + By default :term:`uncompressed`, :term:`compressed`, and + :term:`hybrid`. To read malformed files, include + :term:`raw encoding` with ``raw`` in the list. + :type valid_encodings: :term:`set-like object` + :param valid_curve_encodings: list of allowed encoding formats + for curve parameters. By default (``None``) all are supported: + ``named_curve`` and ``explicit``. + :type valid_curve_encodings: :term:`set-like object` + + + :return: Initialised VerifyingKey object + :rtype: VerifyingKey + """ + return cls.from_der( + der.unpem(string), + hashfunc=hashfunc, + valid_encodings=valid_encodings, + valid_curve_encodings=valid_curve_encodings, + ) + + @classmethod + def from_der( + cls, + string, + hashfunc=sha1, + valid_encodings=None, + valid_curve_encodings=None, + ): + """ + Initialise the key stored in :term:`DER` format. + + The expected format of the key is the SubjectPublicKeyInfo structure + from RFC5912 (for RSA keys, it's known as the PKCS#1 format):: + + SubjectPublicKeyInfo {PUBLIC-KEY: IOSet} ::= SEQUENCE { + algorithm AlgorithmIdentifier {PUBLIC-KEY, {IOSet}}, + subjectPublicKey BIT STRING + } + + Note: only public EC keys are supported by this method. The + SubjectPublicKeyInfo.algorithm.algorithm field must specify + id-ecPublicKey (see RFC3279). + + Only the named curve encoding is supported, thus the + SubjectPublicKeyInfo.algorithm.parameters field needs to be an + object identifier. A sequence in that field indicates an explicit + parameter curve encoding, this format is not supported. A NULL object + in that field indicates an "implicitlyCA" encoding, where the curve + parameters come from CA certificate, those, again, are not supported. + + :param string: binary string with the DER encoding of public ECDSA key + :type string: bytes-like object + :param valid_encodings: list of allowed point encodings. + By default :term:`uncompressed`, :term:`compressed`, and + :term:`hybrid`. To read malformed files, include + :term:`raw encoding` with ``raw`` in the list. + :type valid_encodings: :term:`set-like object` + :param valid_curve_encodings: list of allowed encoding formats + for curve parameters. By default (``None``) all are supported: + ``named_curve`` and ``explicit``. + :type valid_curve_encodings: :term:`set-like object` + + :return: Initialised VerifyingKey object + :rtype: VerifyingKey + """ + if valid_encodings is None: + valid_encodings = set(["uncompressed", "compressed", "hybrid"]) + string = normalise_bytes(string) + # [[oid_ecPublicKey,oid_curve], point_str_bitstring] + s1, empty = der.remove_sequence(string) + if empty != b"": + raise der.UnexpectedDER( + "trailing junk after DER pubkey: %s" % binascii.hexlify(empty) + ) + s2, point_str_bitstring = der.remove_sequence(s1) + # s2 = oid_ecPublicKey,oid_curve + oid_pk, rest = der.remove_object(s2) + if oid_pk in (Ed25519.oid, Ed448.oid): + if oid_pk == Ed25519.oid: + curve = Ed25519 + else: + assert oid_pk == Ed448.oid + curve = Ed448 + point_str, empty = der.remove_bitstring(point_str_bitstring, 0) + if empty: + raise der.UnexpectedDER("trailing junk after public key") + return cls.from_string(point_str, curve, None) + if not oid_pk == oid_ecPublicKey: + raise der.UnexpectedDER( + "Unexpected object identifier in DER " + "encoding: {0!r}".format(oid_pk) + ) + curve = Curve.from_der(rest, valid_curve_encodings) + point_str, empty = der.remove_bitstring(point_str_bitstring, 0) + if empty != b"": + raise der.UnexpectedDER( + "trailing junk after pubkey pointstring: %s" + % binascii.hexlify(empty) + ) + # raw encoding of point is invalid in DER files + if len(point_str) == curve.verifying_key_length: + raise der.UnexpectedDER("Malformed encoding of public point") + return cls.from_string( + point_str, + curve, + hashfunc=hashfunc, + valid_encodings=valid_encodings, + ) + + @classmethod + def from_public_key_recovery( + cls, + signature, + data, + curve, + hashfunc=sha1, + sigdecode=sigdecode_string, + allow_truncate=True, + ): + """ + Return keys that can be used as verifiers of the provided signature. + + Tries to recover the public key that can be used to verify the + signature, usually returns two keys like that. + + :param signature: the byte string with the encoded signature + :type signature: bytes-like object + :param data: the data to be hashed for signature verification + :type data: bytes-like object + :param curve: the curve over which the signature was performed + :type curve: ~ecdsa.curves.Curve + :param hashfunc: The default hash function that will be used for + verification, needs to implement the same interface as hashlib.sha1 + :type hashfunc: callable + :param sigdecode: Callable to define the way the signature needs to + be decoded to an object, needs to handle `signature` as the + first parameter, the curve order (an int) as the second and return + a tuple with two integers, "r" as the first one and "s" as the + second one. See :func:`ecdsa.util.sigdecode_string` and + :func:`ecdsa.util.sigdecode_der` for examples. + :param bool allow_truncate: if True, the provided hashfunc can generate + values larger than the bit size of the order of the curve, the + extra bits (at the end of the digest) will be truncated. + :type sigdecode: callable + + :return: Initialised VerifyingKey objects + :rtype: list of VerifyingKey + """ + if isinstance(curve.curve, CurveEdTw): + raise ValueError("Method unsupported for Edwards curves") + data = normalise_bytes(data) + digest = hashfunc(data).digest() + return cls.from_public_key_recovery_with_digest( + signature, + digest, + curve, + hashfunc=hashfunc, + sigdecode=sigdecode, + allow_truncate=allow_truncate, + ) + + @classmethod + def from_public_key_recovery_with_digest( + cls, + signature, + digest, + curve, + hashfunc=sha1, + sigdecode=sigdecode_string, + allow_truncate=False, + ): + """ + Return keys that can be used as verifiers of the provided signature. + + Tries to recover the public key that can be used to verify the + signature, usually returns two keys like that. + + :param signature: the byte string with the encoded signature + :type signature: bytes-like object + :param digest: the hash value of the message signed by the signature + :type digest: bytes-like object + :param curve: the curve over which the signature was performed + :type curve: ~ecdsa.curves.Curve + :param hashfunc: The default hash function that will be used for + verification, needs to implement the same interface as hashlib.sha1 + :type hashfunc: callable + :param sigdecode: Callable to define the way the signature needs to + be decoded to an object, needs to handle `signature` as the + first parameter, the curve order (an int) as the second and return + a tuple with two integers, "r" as the first one and "s" as the + second one. See :func:`ecdsa.util.sigdecode_string` and + :func:`ecdsa.util.sigdecode_der` for examples. + :type sigdecode: callable + :param bool allow_truncate: if True, the provided hashfunc can generate + values larger than the bit size of the order of the curve (and + the length of provided `digest`), the extra bits (at the end of the + digest) will be truncated. + + :return: Initialised VerifyingKey object + :rtype: VerifyingKey + """ + if isinstance(curve.curve, CurveEdTw): + raise ValueError("Method unsupported for Edwards curves") + generator = curve.generator + r, s = sigdecode(signature, generator.order()) + sig = ecdsa.Signature(r, s) + + digest = normalise_bytes(digest) + digest_as_number = _truncate_and_convert_digest( + digest, curve, allow_truncate + ) + pks = sig.recover_public_keys(digest_as_number, generator) + + # Transforms the ecdsa.Public_key object into a VerifyingKey + verifying_keys = [ + cls.from_public_point(pk.point, curve, hashfunc) for pk in pks + ] + return verifying_keys + + def to_string(self, encoding="raw"): + """ + Convert the public key to a byte string. + + The method by default uses the :term:`raw encoding` (specified + by `encoding="raw"`. It can also output keys in :term:`uncompressed`, + :term:`compressed` and :term:`hybrid` formats. + + Remember that the curve identification is not part of the encoding + so to decode the point using :func:`~VerifyingKey.from_string`, curve + needs to be specified. + + Note: while the method is called "to_string", it's a misnomer from + Python 2 days when character strings and byte strings shared type. + On Python 3 the returned type will be `bytes`. + + :return: :term:`raw encoding` of the public key (public point) on the + curve + :rtype: bytes + """ + assert encoding in ("raw", "uncompressed", "compressed", "hybrid") + return self.pubkey.point.to_bytes(encoding) + + def to_pem( + self, point_encoding="uncompressed", curve_parameters_encoding=None + ): + """ + Convert the public key to the :term:`PEM` format. + + The PEM header of the key will be ``BEGIN PUBLIC KEY``. + + The format of the key is described in the + :func:`~VerifyingKey.from_der()` method. + This method supports only "named curve" encoding of keys. + + :param str point_encoding: specification of the encoding format + of public keys. "uncompressed" is most portable, "compressed" is + smallest. "hybrid" is uncommon and unsupported by most + implementations, it is as big as "uncompressed". + :param str curve_parameters_encoding: the encoding for curve parameters + to use, by default tries to use ``named_curve`` encoding, + if that is not possible, falls back to ``explicit`` encoding. + + :return: portable encoding of the public key + :rtype: bytes + + .. warning:: The PEM is encoded to US-ASCII, it needs to be + re-encoded if the system is incompatible (e.g. uses UTF-16) + """ + return der.topem( + self.to_der(point_encoding, curve_parameters_encoding), + "PUBLIC KEY", + ) + + def to_der( + self, point_encoding="uncompressed", curve_parameters_encoding=None + ): + """ + Convert the public key to the :term:`DER` format. + + The format of the key is described in the + :func:`~VerifyingKey.from_der()` method. + This method supports only "named curve" encoding of keys. + + :param str point_encoding: specification of the encoding format + of public keys. "uncompressed" is most portable, "compressed" is + smallest. "hybrid" is uncommon and unsupported by most + implementations, it is as big as "uncompressed". + :param str curve_parameters_encoding: the encoding for curve parameters + to use, by default tries to use ``named_curve`` encoding, + if that is not possible, falls back to ``explicit`` encoding. + + :return: DER encoding of the public key + :rtype: bytes + """ + if point_encoding == "raw": + raise ValueError("raw point_encoding not allowed in DER") + point_str = self.to_string(point_encoding) + if isinstance(self.curve.curve, CurveEdTw): + return der.encode_sequence( + der.encode_sequence(der.encode_oid(*self.curve.oid)), + der.encode_bitstring(bytes(point_str), 0), + ) + return der.encode_sequence( + der.encode_sequence( + encoded_oid_ecPublicKey, + self.curve.to_der(curve_parameters_encoding, point_encoding), + ), + # 0 is the number of unused bits in the + # bit string + der.encode_bitstring(point_str, 0), + ) + + def to_ssh(self): + """ + Convert the public key to the SSH format. + + :return: SSH encoding of the public key + :rtype: bytes + """ + return ssh.serialize_public( + self.curve.name, + self.to_string(), + ) + + def verify( + self, + signature, + data, + hashfunc=None, + sigdecode=sigdecode_string, + allow_truncate=True, + ): + """ + Verify a signature made over provided data. + + Will hash `data` to verify the signature. + + By default expects signature in :term:`raw encoding`. Can also be used + to verify signatures in ASN.1 DER encoding by using + :func:`ecdsa.util.sigdecode_der` + as the `sigdecode` parameter. + + :param signature: encoding of the signature + :type signature: sigdecode method dependent + :param data: data signed by the `signature`, will be hashed using + `hashfunc`, if specified, or default hash function + :type data: :term:`bytes-like object` + :param hashfunc: The default hash function that will be used for + verification, needs to implement the same interface as hashlib.sha1 + :type hashfunc: callable + :param sigdecode: Callable to define the way the signature needs to + be decoded to an object, needs to handle `signature` as the + first parameter, the curve order (an int) as the second and return + a tuple with two integers, "r" as the first one and "s" as the + second one. See :func:`ecdsa.util.sigdecode_string` and + :func:`ecdsa.util.sigdecode_der` for examples. + :type sigdecode: callable + :param bool allow_truncate: if True, the provided digest can have + bigger bit-size than the order of the curve, the extra bits (at + the end of the digest) will be truncated. Use it when verifying + SHA-384 output using NIST256p or in similar situations. Defaults to + True. + + :raises BadSignatureError: if the signature is invalid or malformed + + :return: True if the verification was successful + :rtype: bool + """ + # signature doesn't have to be a bytes-like-object so don't normalise + # it, the decoders will do that + data = normalise_bytes(data) + if isinstance(self.curve.curve, CurveEdTw): + signature = normalise_bytes(signature) + try: + return self.pubkey.verify(data, signature) + except (ValueError, MalformedPointError) as e: + raise BadSignatureError("Signature verification failed", e) + + hashfunc = hashfunc or self.default_hashfunc + digest = hashfunc(data).digest() + return self.verify_digest(signature, digest, sigdecode, allow_truncate) + + def verify_digest( + self, + signature, + digest, + sigdecode=sigdecode_string, + allow_truncate=False, + ): + """ + Verify a signature made over provided hash value. + + By default expects signature in :term:`raw encoding`. Can also be used + to verify signatures in ASN.1 DER encoding by using + :func:`ecdsa.util.sigdecode_der` + as the `sigdecode` parameter. + + :param signature: encoding of the signature + :type signature: sigdecode method dependent + :param digest: raw hash value that the signature authenticates. + :type digest: :term:`bytes-like object` + :param sigdecode: Callable to define the way the signature needs to + be decoded to an object, needs to handle `signature` as the + first parameter, the curve order (an int) as the second and return + a tuple with two integers, "r" as the first one and "s" as the + second one. See :func:`ecdsa.util.sigdecode_string` and + :func:`ecdsa.util.sigdecode_der` for examples. + :type sigdecode: callable + :param bool allow_truncate: if True, the provided digest can have + bigger bit-size than the order of the curve, the extra bits (at + the end of the digest) will be truncated. Use it when verifying + SHA-384 output using NIST256p or in similar situations. + + :raises BadSignatureError: if the signature is invalid or malformed + :raises BadDigestError: if the provided digest is too big for the curve + associated with this VerifyingKey and allow_truncate was not set + + :return: True if the verification was successful + :rtype: bool + """ + # signature doesn't have to be a bytes-like-object so don't normalise + # it, the decoders will do that + digest = normalise_bytes(digest) + number = _truncate_and_convert_digest( + digest, + self.curve, + allow_truncate, + ) + + try: + r, s = sigdecode(signature, self.pubkey.order) + except (der.UnexpectedDER, MalformedSignature) as e: + raise BadSignatureError("Malformed formatting of signature", e) + sig = ecdsa.Signature(r, s) + if self.pubkey.verifies(number, sig): + return True + raise BadSignatureError("Signature verification failed") + + +class SigningKey(object): + """ + Class for handling keys that can create signatures (private keys). + + :ivar `~ecdsa.curves.Curve` curve: The Curve over which all the + cryptographic operations will take place + :ivar default_hashfunc: the function that will be used for hashing the + data. Should implement the same API as :py:class:`hashlib.sha1` + :ivar int baselen: the length of a :term:`raw encoding` of private key + :ivar `~ecdsa.keys.VerifyingKey` verifying_key: the public key + associated with this private key + :ivar `~ecdsa.ecdsa.Private_key` privkey: the actual private key + """ + + def __init__(self, _error__please_use_generate=None): + """Unsupported, please use one of the classmethods to initialise.""" + if not _error__please_use_generate: + raise TypeError("Please use SigningKey.generate() to construct me") + self.curve = None + self.default_hashfunc = None + self.baselen = None + self.verifying_key = None + self.privkey = None + + def __eq__(self, other): + """Return True if the points are identical, False otherwise.""" + if isinstance(other, SigningKey): + return ( + self.curve == other.curve + and self.verifying_key == other.verifying_key + and self.privkey == other.privkey + ) + return NotImplemented + + def __ne__(self, other): + """Return False if the points are identical, True otherwise.""" + return not self == other + + @classmethod + def _twisted_edwards_keygen(cls, curve, entropy): + """Generate a private key on a Twisted Edwards curve.""" + if not entropy: + entropy = os.urandom + random = entropy(curve.baselen) + private_key = eddsa.PrivateKey(curve.generator, random) + public_key = private_key.public_key() + + verifying_key = VerifyingKey.from_string( + public_key.public_key(), curve + ) + + self = cls(_error__please_use_generate=True) + self.curve = curve + self.default_hashfunc = None + self.baselen = curve.baselen + self.privkey = private_key + self.verifying_key = verifying_key + return self + + @classmethod + def _weierstrass_keygen(cls, curve, entropy, hashfunc): + """Generate a private key on a Weierstrass curve.""" + secexp = randrange(curve.order, entropy) + return cls.from_secret_exponent(secexp, curve, hashfunc) + + @classmethod + def generate(cls, curve=NIST192p, entropy=None, hashfunc=sha1): + """ + Generate a random private key. + + :param curve: The curve on which the point needs to reside, defaults + to NIST192p + :type curve: ~ecdsa.curves.Curve + :param entropy: Source of randomness for generating the private keys, + should provide cryptographically secure random numbers if the keys + need to be secure. Uses os.urandom() by default. + :type entropy: callable + :param hashfunc: The default hash function that will be used for + signing, needs to implement the same interface + as hashlib.sha1 + :type hashfunc: callable + + :return: Initialised SigningKey object + :rtype: SigningKey + """ + if isinstance(curve.curve, CurveEdTw): + return cls._twisted_edwards_keygen(curve, entropy) + return cls._weierstrass_keygen(curve, entropy, hashfunc) + + @classmethod + def from_secret_exponent(cls, secexp, curve=NIST192p, hashfunc=sha1): + """ + Create a private key from a random integer. + + Note: it's a low level method, it's recommended to use the + :func:`~SigningKey.generate` method to create private keys. + + :param int secexp: secret multiplier (the actual private key in ECDSA). + Needs to be an integer between 1 and the curve order. + :param curve: The curve on which the point needs to reside + :type curve: ~ecdsa.curves.Curve + :param hashfunc: The default hash function that will be used for + signing, needs to implement the same interface + as hashlib.sha1 + :type hashfunc: callable + + :raises MalformedPointError: when the provided secexp is too large + or too small for the curve selected + :raises RuntimeError: if the generation of public key from private + key failed + + :return: Initialised SigningKey object + :rtype: SigningKey + """ + if isinstance(curve.curve, CurveEdTw): + raise ValueError( + "Edwards keys don't support setting the secret scalar " + "(exponent) directly" + ) + self = cls(_error__please_use_generate=True) + self.curve = curve + self.default_hashfunc = hashfunc + self.baselen = curve.baselen + n = curve.order + if not 1 <= secexp < n: + raise MalformedPointError( + "Invalid value for secexp, expected integer " + "between 1 and {0}".format(n) + ) + pubkey_point = curve.generator * secexp + if hasattr(pubkey_point, "scale"): + pubkey_point = pubkey_point.scale() + self.verifying_key = VerifyingKey.from_public_point( + pubkey_point, curve, hashfunc, False + ) + pubkey = self.verifying_key.pubkey + self.privkey = ecdsa.Private_key(pubkey, secexp) + self.privkey.order = n + return self + + @classmethod + def from_string(cls, string, curve=NIST192p, hashfunc=sha1): + """ + Decode the private key from :term:`raw encoding`. + + Note: the name of this method is a misnomer coming from days of + Python 2, when binary strings and character strings shared a type. + In Python 3, the expected type is `bytes`. + + :param string: the raw encoding of the private key + :type string: :term:`bytes-like object` + :param curve: The curve on which the point needs to reside + :type curve: ~ecdsa.curves.Curve + :param hashfunc: The default hash function that will be used for + signing, needs to implement the same interface + as hashlib.sha1 + :type hashfunc: callable + + :raises MalformedPointError: if the length of encoding doesn't match + the provided curve or the encoded values is too large + :raises RuntimeError: if the generation of public key from private + key failed + + :return: Initialised SigningKey object + :rtype: SigningKey + """ + string = normalise_bytes(string) + + if len(string) != curve.baselen: + raise MalformedPointError( + "Invalid length of private key, received {0}, " + "expected {1}".format(len(string), curve.baselen) + ) + if isinstance(curve.curve, CurveEdTw): + self = cls(_error__please_use_generate=True) + self.curve = curve + self.default_hashfunc = None # Ignored for EdDSA + self.baselen = curve.baselen + self.privkey = eddsa.PrivateKey(curve.generator, string) + self.verifying_key = VerifyingKey.from_string( + self.privkey.public_key().public_key(), curve + ) + return self + secexp = string_to_number(string) + return cls.from_secret_exponent(secexp, curve, hashfunc) + + @classmethod + def from_pem(cls, string, hashfunc=sha1, valid_curve_encodings=None): + """ + Initialise from key stored in :term:`PEM` format. + + The PEM formats supported are the un-encrypted RFC5915 + (the ssleay format) supported by OpenSSL, and the more common + un-encrypted RFC5958 (the PKCS #8 format). + + The legacy format files have the header with the string + ``BEGIN EC PRIVATE KEY``. + PKCS#8 files have the header ``BEGIN PRIVATE KEY``. + Encrypted files (ones that include the string + ``Proc-Type: 4,ENCRYPTED`` + right after the PEM header) are not supported. + + See :func:`~SigningKey.from_der` for ASN.1 syntax of the objects in + this files. + + :param string: text with PEM-encoded private ECDSA key + :type string: str + :param valid_curve_encodings: list of allowed encoding formats + for curve parameters. By default (``None``) all are supported: + ``named_curve`` and ``explicit``. + :type valid_curve_encodings: :term:`set-like object` + + + :raises MalformedPointError: if the length of encoding doesn't match + the provided curve or the encoded values is too large + :raises RuntimeError: if the generation of public key from private + key failed + :raises UnexpectedDER: if the encoding of the PEM file is incorrect + + :return: Initialised SigningKey object + :rtype: SigningKey + """ + if not PY2 and isinstance(string, str): # pragma: no branch + string = string.encode() + + # The privkey pem may have multiple sections, commonly it also has + # "EC PARAMETERS", we need just "EC PRIVATE KEY". PKCS#8 should not + # have the "EC PARAMETERS" section; it's just "PRIVATE KEY". + private_key_index = string.find(b"-----BEGIN EC PRIVATE KEY-----") + if private_key_index == -1: + private_key_index = string.index(b"-----BEGIN PRIVATE KEY-----") + + return cls.from_der( + der.unpem(string[private_key_index:]), + hashfunc, + valid_curve_encodings, + ) + + @classmethod + def from_der(cls, string, hashfunc=sha1, valid_curve_encodings=None): + """ + Initialise from key stored in :term:`DER` format. + + The DER formats supported are the un-encrypted RFC5915 + (the ssleay format) supported by OpenSSL, and the more common + un-encrypted RFC5958 (the PKCS #8 format). + + Both formats contain an ASN.1 object following the syntax specified + in RFC5915:: + + ECPrivateKey ::= SEQUENCE { + version INTEGER { ecPrivkeyVer1(1) }} (ecPrivkeyVer1), + privateKey OCTET STRING, + parameters [0] ECParameters {{ NamedCurve }} OPTIONAL, + publicKey [1] BIT STRING OPTIONAL + } + + `publicKey` field is ignored completely (errors, if any, in it will + be undetected). + + Two formats are supported for the `parameters` field: the named + curve and the explicit encoding of curve parameters. + In the legacy ssleay format, this implementation requires the optional + `parameters` field to get the curve name. In PKCS #8 format, the curve + is part of the PrivateKeyAlgorithmIdentifier. + + The PKCS #8 format includes an ECPrivateKey object as the `privateKey` + field within a larger structure:: + + OneAsymmetricKey ::= SEQUENCE { + version Version, + privateKeyAlgorithm PrivateKeyAlgorithmIdentifier, + privateKey PrivateKey, + attributes [0] Attributes OPTIONAL, + ..., + [[2: publicKey [1] PublicKey OPTIONAL ]], + ... + } + + The `attributes` and `publicKey` fields are completely ignored; errors + in them will not be detected. + + :param string: binary string with DER-encoded private ECDSA key + :type string: :term:`bytes-like object` + :param valid_curve_encodings: list of allowed encoding formats + for curve parameters. By default (``None``) all are supported: + ``named_curve`` and ``explicit``. + Ignored for EdDSA. + :type valid_curve_encodings: :term:`set-like object` + + :raises MalformedPointError: if the length of encoding doesn't match + the provided curve or the encoded values is too large + :raises RuntimeError: if the generation of public key from private + key failed + :raises UnexpectedDER: if the encoding of the DER file is incorrect + + :return: Initialised SigningKey object + :rtype: SigningKey + """ + s = normalise_bytes(string) + curve = None + + s, empty = der.remove_sequence(s) + if empty != b"": + raise der.UnexpectedDER( + "trailing junk after DER privkey: %s" % binascii.hexlify(empty) + ) + + version, s = der.remove_integer(s) + + # At this point, PKCS #8 has a sequence containing the algorithm + # identifier and the curve identifier. The ssleay format instead has + # an octet string containing the key data, so this is how we can + # distinguish the two formats. + if der.is_sequence(s): + if version not in (0, 1): + raise der.UnexpectedDER( + "expected version '0' or '1' at start of privkey, got %d" + % version + ) + + sequence, s = der.remove_sequence(s) + algorithm_oid, algorithm_identifier = der.remove_object(sequence) + + if algorithm_oid in (Ed25519.oid, Ed448.oid): + if algorithm_identifier: + raise der.UnexpectedDER( + "Non NULL parameters for a EdDSA key" + ) + key_str_der, s = der.remove_octet_string(s) + + # As RFC5958 describe, there are may be optional Attributes + # and Publickey. Don't raise error if something after + # Privatekey + + # TODO parse attributes or validate publickey + # if s: + # raise der.UnexpectedDER( + # "trailing junk inside the privateKey" + # ) + key_str, s = der.remove_octet_string(key_str_der) + if s: + raise der.UnexpectedDER( + "trailing junk after the encoded private key" + ) + + if algorithm_oid == Ed25519.oid: + curve = Ed25519 + else: + assert algorithm_oid == Ed448.oid + curve = Ed448 + + return cls.from_string(key_str, curve, None) + + if algorithm_oid not in (oid_ecPublicKey, oid_ecDH, oid_ecMQV): + raise der.UnexpectedDER( + "unexpected algorithm identifier '%s'" % (algorithm_oid,) + ) + + curve = Curve.from_der(algorithm_identifier, valid_curve_encodings) + + # Up next is an octet string containing an ECPrivateKey. Ignore + # the optional "attributes" and "publicKey" fields that come after. + s, _ = der.remove_octet_string(s) + + # Unpack the ECPrivateKey to get to the key data octet string, + # and rejoin the ssleay parsing path. + s, empty = der.remove_sequence(s) + if empty != b"": + raise der.UnexpectedDER( + "trailing junk after DER privkey: %s" + % binascii.hexlify(empty) + ) + + version, s = der.remove_integer(s) + + # The version of the ECPrivateKey must be 1. + if version != 1: + raise der.UnexpectedDER( + "expected version '1' at start of DER privkey, got %d" + % version + ) + + privkey_str, s = der.remove_octet_string(s) + + if not curve: + tag, curve_oid_str, s = der.remove_constructed(s) + if tag != 0: + raise der.UnexpectedDER( + "expected tag 0 in DER privkey, got %d" % tag + ) + curve = Curve.from_der(curve_oid_str, valid_curve_encodings) + + # we don't actually care about the following fields + # + # tag, pubkey_bitstring, s = der.remove_constructed(s) + # if tag != 1: + # raise der.UnexpectedDER("expected tag 1 in DER privkey, got %d" + # % tag) + # pubkey_str = der.remove_bitstring(pubkey_bitstring, 0) + # if empty != "": + # raise der.UnexpectedDER("trailing junk after DER privkey " + # "pubkeystr: %s" + # % binascii.hexlify(empty)) + + # our from_string method likes fixed-length privkey strings + if len(privkey_str) < curve.baselen: + privkey_str = ( + b"\x00" * (curve.baselen - len(privkey_str)) + privkey_str + ) + return cls.from_string(privkey_str, curve, hashfunc) + + def to_string(self): + """ + Convert the private key to :term:`raw encoding`. + + Note: while the method is named "to_string", its name comes from + Python 2 days, when binary and character strings used the same type. + The type used in Python 3 is `bytes`. + + :return: raw encoding of private key + :rtype: bytes + """ + if isinstance(self.curve.curve, CurveEdTw): + return bytes(self.privkey.private_key) + secexp = self.privkey.secret_multiplier + s = number_to_string(secexp, self.privkey.order) + return s + + def to_pem( + self, + point_encoding="uncompressed", + format="ssleay", + curve_parameters_encoding=None, + ): + """ + Convert the private key to the :term:`PEM` format. + + See :func:`~SigningKey.from_pem` method for format description. + + Only the named curve format is supported. + The public key will be included in generated string. + + The PEM header will specify ``BEGIN EC PRIVATE KEY`` or + ``BEGIN PRIVATE KEY``, depending on the desired format. + + :param str point_encoding: format to use for encoding public point + :param str format: either ``ssleay`` (default) or ``pkcs8`` + :param str curve_parameters_encoding: format of encoded curve + parameters, default depends on the curve, if the curve has + an associated OID, ``named_curve`` format will be used, + if no OID is associated with the curve, the fallback of + ``explicit`` parameters will be used. + + :return: PEM encoded private key + :rtype: bytes + + .. warning:: The PEM is encoded to US-ASCII, it needs to be + re-encoded if the system is incompatible (e.g. uses UTF-16) + """ + # TODO: "BEGIN ECPARAMETERS" + assert format in ("ssleay", "pkcs8") + header = "EC PRIVATE KEY" if format == "ssleay" else "PRIVATE KEY" + return der.topem( + self.to_der(point_encoding, format, curve_parameters_encoding), + header, + ) + + def _encode_eddsa(self): + """Create a PKCS#8 encoding of EdDSA keys.""" + ec_private_key = der.encode_octet_string(self.to_string()) + return der.encode_sequence( + der.encode_integer(0), + der.encode_sequence(der.encode_oid(*self.curve.oid)), + der.encode_octet_string(ec_private_key), + ) + + def to_der( + self, + point_encoding="uncompressed", + format="ssleay", + curve_parameters_encoding=None, + ): + """ + Convert the private key to the :term:`DER` format. + + See :func:`~SigningKey.from_der` method for format specification. + + Only the named curve format is supported. + The public key will be included in the generated string. + + :param str point_encoding: format to use for encoding public point + Ignored for EdDSA + :param str format: either ``ssleay`` (default) or ``pkcs8``. + EdDSA keys require ``pkcs8``. + :param str curve_parameters_encoding: format of encoded curve + parameters, default depends on the curve, if the curve has + an associated OID, ``named_curve`` format will be used, + if no OID is associated with the curve, the fallback of + ``explicit`` parameters will be used. + Ignored for EdDSA. + + :return: DER encoded private key + :rtype: bytes + """ + # SEQ([int(1), octetstring(privkey),cont[0], oid(secp224r1), + # cont[1],bitstring]) + if point_encoding == "raw": + raise ValueError("raw encoding not allowed in DER") + assert format in ("ssleay", "pkcs8") + if isinstance(self.curve.curve, CurveEdTw): + if format != "pkcs8": + raise ValueError("Only PKCS#8 format supported for EdDSA keys") + return self._encode_eddsa() + encoded_vk = self.get_verifying_key().to_string(point_encoding) + priv_key_elems = [ + der.encode_integer(1), + der.encode_octet_string(self.to_string()), + ] + if format == "ssleay": + priv_key_elems.append( + der.encode_constructed( + 0, self.curve.to_der(curve_parameters_encoding) + ) + ) + # the 0 in encode_bitstring specifies the number of unused bits + # in the `encoded_vk` string + priv_key_elems.append( + der.encode_constructed(1, der.encode_bitstring(encoded_vk, 0)) + ) + ec_private_key = der.encode_sequence(*priv_key_elems) + + if format == "ssleay": + return ec_private_key + else: + return der.encode_sequence( + # version = 1 means the public key is not present in the + # top-level structure. + der.encode_integer(1), + der.encode_sequence( + der.encode_oid(*oid_ecPublicKey), + self.curve.to_der(curve_parameters_encoding), + ), + der.encode_octet_string(ec_private_key), + ) + + def to_ssh(self): + """ + Convert the private key to the SSH format. + + :return: SSH encoded private key + :rtype: bytes + """ + return ssh.serialize_private( + self.curve.name, + self.verifying_key.to_string(), + self.to_string(), + ) + + def get_verifying_key(self): + """ + Return the VerifyingKey associated with this private key. + + Equivalent to reading the `verifying_key` field of an instance. + + :return: a public key that can be used to verify the signatures made + with this SigningKey + :rtype: VerifyingKey + """ + return self.verifying_key + + def sign_deterministic( + self, + data, + hashfunc=None, + sigencode=sigencode_string, + extra_entropy=b"", + ): + """ + Create signature over data. + + For Weierstrass curves it uses the deterministic RFC6979 algorithm. + For Edwards curves it uses the standard EdDSA algorithm. + + For ECDSA the data will be hashed using the `hashfunc` function before + signing. + For EdDSA the data will be hashed with the hash associated with the + curve (SHA-512 for Ed25519 and SHAKE-256 for Ed448). + + This is the recommended method for performing signatures when hashing + of data is necessary. + + :param data: data to be hashed and computed signature over + :type data: :term:`bytes-like object` + :param hashfunc: hash function to use for computing the signature, + if unspecified, the default hash function selected during + object initialisation will be used (see + `VerifyingKey.default_hashfunc`). The object needs to implement + the same interface as hashlib.sha1. + Ignored with EdDSA. + :type hashfunc: callable + :param sigencode: function used to encode the signature. + The function needs to accept three parameters: the two integers + that are the signature and the order of the curve over which the + signature was computed. It needs to return an encoded signature. + See `ecdsa.util.sigencode_string` and `ecdsa.util.sigencode_der` + as examples of such functions. + Ignored with EdDSA. + :type sigencode: callable + :param extra_entropy: additional data that will be fed into the random + number generator used in the RFC6979 process. Entirely optional. + Ignored with EdDSA. + :type extra_entropy: :term:`bytes-like object` + + :return: encoded signature over `data` + :rtype: bytes or sigencode function dependent type + """ + hashfunc = hashfunc or self.default_hashfunc + data = normalise_bytes(data) + + if isinstance(self.curve.curve, CurveEdTw): + return self.privkey.sign(data) + + extra_entropy = normalise_bytes(extra_entropy) + digest = hashfunc(data).digest() + + return self.sign_digest_deterministic( + digest, + hashfunc=hashfunc, + sigencode=sigencode, + extra_entropy=extra_entropy, + allow_truncate=True, + ) + + def sign_digest_deterministic( + self, + digest, + hashfunc=None, + sigencode=sigencode_string, + extra_entropy=b"", + allow_truncate=False, + ): + """ + Create signature for digest using the deterministic RFC6979 algorithm. + + `digest` should be the output of cryptographically secure hash function + like SHA256 or SHA-3-256. + + This is the recommended method for performing signatures when no + hashing of data is necessary. + + :param digest: hash of data that will be signed + :type digest: :term:`bytes-like object` + :param hashfunc: hash function to use for computing the random "k" + value from RFC6979 process, + if unspecified, the default hash function selected during + object initialisation will be used (see + :attr:`.VerifyingKey.default_hashfunc`). The object needs to + implement + the same interface as :func:`~hashlib.sha1` from :py:mod:`hashlib`. + :type hashfunc: callable + :param sigencode: function used to encode the signature. + The function needs to accept three parameters: the two integers + that are the signature and the order of the curve over which the + signature was computed. It needs to return an encoded signature. + See :func:`~ecdsa.util.sigencode_string` and + :func:`~ecdsa.util.sigencode_der` + as examples of such functions. + :type sigencode: callable + :param extra_entropy: additional data that will be fed into the random + number generator used in the RFC6979 process. Entirely optional. + :type extra_entropy: :term:`bytes-like object` + :param bool allow_truncate: if True, the provided digest can have + bigger bit-size than the order of the curve, the extra bits (at + the end of the digest) will be truncated. Use it when signing + SHA-384 output using NIST256p or in similar situations. + + :return: encoded signature for the `digest` hash + :rtype: bytes or sigencode function dependent type + """ + if isinstance(self.curve.curve, CurveEdTw): + raise ValueError("Method unsupported for Edwards curves") + secexp = self.privkey.secret_multiplier + hashfunc = hashfunc or self.default_hashfunc + digest = normalise_bytes(digest) + extra_entropy = normalise_bytes(extra_entropy) + + def simple_r_s(r, s, order): + return r, s, order + + retry_gen = 0 + while True: + k = rfc6979.generate_k( + self.curve.generator.order(), + secexp, + hashfunc, + digest, + retry_gen=retry_gen, + extra_entropy=extra_entropy, + ) + try: + r, s, order = self.sign_digest( + digest, + sigencode=simple_r_s, + k=k, + allow_truncate=allow_truncate, + ) + break + except RSZeroError: + retry_gen += 1 + + return sigencode(r, s, order) + + def sign( + self, + data, + entropy=None, + hashfunc=None, + sigencode=sigencode_string, + k=None, + allow_truncate=True, + ): + """ + Create signature over data. + + Uses the probabilistic ECDSA algorithm for Weierstrass curves + (NIST256p, etc.) and the deterministic EdDSA algorithm for the + Edwards curves (Ed25519, Ed448). + + This method uses the standard ECDSA algorithm that requires a + cryptographically secure random number generator. + + It's recommended to use the :func:`~SigningKey.sign_deterministic` + method instead of this one. + + :param data: data that will be hashed for signing + :type data: :term:`bytes-like object` + :param callable entropy: randomness source, :func:`os.urandom` by + default. Ignored with EdDSA. + :param hashfunc: hash function to use for hashing the provided + ``data``. + If unspecified the default hash function selected during + object initialisation will be used (see + :attr:`.VerifyingKey.default_hashfunc`). + Should behave like :func:`~hashlib.sha1` from :py:mod:`hashlib`. + The output length of the + hash (in bytes) must not be longer than the length of the curve + order (rounded up to the nearest byte), so using SHA256 with + NIST256p is ok, but SHA256 with NIST192p is not. (In the 2**-96ish + unlikely event of a hash output larger than the curve order, the + hash will effectively be wrapped mod n). + If you want to explicitly allow use of large hashes with small + curves set the ``allow_truncate`` to ``True``. + Use ``hashfunc=hashlib.sha1`` to match openssl's + ``-ecdsa-with-SHA1`` mode, + or ``hashfunc=hashlib.sha256`` for openssl-1.0.0's + ``-ecdsa-with-SHA256``. + Ignored for EdDSA + :type hashfunc: callable + :param sigencode: function used to encode the signature. + The function needs to accept three parameters: the two integers + that are the signature and the order of the curve over which the + signature was computed. It needs to return an encoded signature. + See :func:`~ecdsa.util.sigencode_string` and + :func:`~ecdsa.util.sigencode_der` + as examples of such functions. + Ignored for EdDSA + :type sigencode: callable + :param int k: a pre-selected nonce for calculating the signature. + In typical use cases, it should be set to None (the default) to + allow its generation from an entropy source. + Ignored for EdDSA. + :param bool allow_truncate: if ``True``, the provided digest can have + bigger bit-size than the order of the curve, the extra bits (at + the end of the digest) will be truncated. Use it when signing + SHA-384 output using NIST256p or in similar situations. True by + default. + Ignored for EdDSA. + + :raises RSZeroError: in the unlikely event when *r* parameter or + *s* parameter of the created signature is equal 0, as that would + leak the key. Caller should try a better entropy source, retry with + different ``k``, or use the + :func:`~SigningKey.sign_deterministic` in such case. + + :return: encoded signature of the hash of `data` + :rtype: bytes or sigencode function dependent type + """ + hashfunc = hashfunc or self.default_hashfunc + data = normalise_bytes(data) + if isinstance(self.curve.curve, CurveEdTw): + return self.sign_deterministic(data) + h = hashfunc(data).digest() + return self.sign_digest(h, entropy, sigencode, k, allow_truncate) + + def sign_digest( + self, + digest, + entropy=None, + sigencode=sigencode_string, + k=None, + allow_truncate=False, + ): + """ + Create signature over digest using the probabilistic ECDSA algorithm. + + This method uses the standard ECDSA algorithm that requires a + cryptographically secure random number generator. + + This method does not hash the input. + + It's recommended to use the + :func:`~SigningKey.sign_digest_deterministic` method + instead of this one. + + :param digest: hash value that will be signed + :type digest: :term:`bytes-like object` + :param callable entropy: randomness source, os.urandom by default + :param sigencode: function used to encode the signature. + The function needs to accept three parameters: the two integers + that are the signature and the order of the curve over which the + signature was computed. It needs to return an encoded signature. + See `ecdsa.util.sigencode_string` and `ecdsa.util.sigencode_der` + as examples of such functions. + :type sigencode: callable + :param int k: a pre-selected nonce for calculating the signature. + In typical use cases, it should be set to None (the default) to + allow its generation from an entropy source. + :param bool allow_truncate: if True, the provided digest can have + bigger bit-size than the order of the curve, the extra bits (at + the end of the digest) will be truncated. Use it when signing + SHA-384 output using NIST256p or in similar situations. + + :raises RSZeroError: in the unlikely event when "r" parameter or + "s" parameter of the created signature is equal 0, as that would + leak the key. Caller should try a better entropy source, retry with + different 'k', or use the + :func:`~SigningKey.sign_digest_deterministic` in such case. + + :return: encoded signature for the `digest` hash + :rtype: bytes or sigencode function dependent type + """ + if isinstance(self.curve.curve, CurveEdTw): + raise ValueError("Method unsupported for Edwards curves") + digest = normalise_bytes(digest) + number = _truncate_and_convert_digest( + digest, + self.curve, + allow_truncate, + ) + r, s = self.sign_number(number, entropy, k) + return sigencode(r, s, self.privkey.order) + + def sign_number(self, number, entropy=None, k=None): + """ + Sign an integer directly. + + Note, this is a low level method, usually you will want to use + :func:`~SigningKey.sign_deterministic` or + :func:`~SigningKey.sign_digest_deterministic`. + + :param int number: number to sign using the probabilistic ECDSA + algorithm. + :param callable entropy: entropy source, os.urandom by default + :param int k: pre-selected nonce for signature operation. If unset + it will be selected at random using the entropy source. + + :raises RSZeroError: in the unlikely event when "r" parameter or + "s" parameter of the created signature is equal 0, as that would + leak the key. Caller should try a better entropy source, retry with + different 'k', or use the + :func:`~SigningKey.sign_digest_deterministic` in such case. + + :return: the "r" and "s" parameters of the signature + :rtype: tuple of ints + """ + if isinstance(self.curve.curve, CurveEdTw): + raise ValueError("Method unsupported for Edwards curves") + order = self.privkey.order + + if k is not None: + _k = k + else: + _k = randrange(order, entropy) + + assert 1 <= _k < order + sig = self.privkey.sign(number, _k) + return sig.r, sig.s diff --git a/code/.venv/lib/python3.12/site-packages/ecdsa/numbertheory.py b/code/.venv/lib/python3.12/site-packages/ecdsa/numbertheory.py new file mode 100644 index 0000000..fe974f8 --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/ecdsa/numbertheory.py @@ -0,0 +1,835 @@ +#! /usr/bin/env python +# +# Provide some simple capabilities from number theory. +# +# Version of 2008.11.14. +# +# Written in 2005 and 2006 by Peter Pearson and placed in the public domain. +# Revision history: +# 2008.11.14: Use pow(base, exponent, modulus) for modular_exp. +# Make gcd and lcm accept arbitrarily many arguments. + +from __future__ import division + +import sys +from six import integer_types, PY2 +from six.moves import reduce + +try: + xrange +except NameError: + xrange = range +try: + from gmpy2 import powmod, mpz + + GMPY2 = True + GMPY = False +except ImportError: # pragma: no branch + GMPY2 = False + try: + from gmpy import mpz + + GMPY = True + except ImportError: + GMPY = False + + +if GMPY2 or GMPY: # pragma: no branch + integer_types = tuple(integer_types + (type(mpz(1)),)) + + +import math +import warnings +import random +from .util import bit_length + + +class Error(Exception): + """Base class for exceptions in this module.""" + + pass + + +class JacobiError(Error): + pass + + +class SquareRootError(Error): + pass + + +class NegativeExponentError(Error): + pass + + +def modular_exp(base, exponent, modulus): # pragma: no cover + """Raise base to exponent, reducing by modulus""" + # deprecated in 0.14 + warnings.warn( + "Function is unused in library code. If you use this code, " + "change to pow() builtin.", + DeprecationWarning, + ) + if exponent < 0: + raise NegativeExponentError( + "Negative exponents (%d) not allowed" % exponent + ) + return pow(base, exponent, modulus) + + +def polynomial_reduce_mod(poly, polymod, p): + """Reduce poly by polymod, integer arithmetic modulo p. + + Polynomials are represented as lists of coefficients + of increasing powers of x.""" + + # This module has been tested only by extensive use + # in calculating modular square roots. + + # Just to make this easy, require a monic polynomial: + assert polymod[-1] == 1 + + assert len(polymod) > 1 + + while len(poly) >= len(polymod): + if poly[-1] != 0: + for i in xrange(2, len(polymod) + 1): + poly[-i] = (poly[-i] - poly[-1] * polymod[-i]) % p + poly = poly[0:-1] + + return poly + + +def polynomial_multiply_mod(m1, m2, polymod, p): + """Polynomial multiplication modulo a polynomial over ints mod p. + + Polynomials are represented as lists of coefficients + of increasing powers of x.""" + + # This is just a seat-of-the-pants implementation. + + # This module has been tested only by extensive use + # in calculating modular square roots. + + # Initialize the product to zero: + + prod = (len(m1) + len(m2) - 1) * [0] + + # Add together all the cross-terms: + + for i in xrange(len(m1)): + for j in xrange(len(m2)): + prod[i + j] = (prod[i + j] + m1[i] * m2[j]) % p + + return polynomial_reduce_mod(prod, polymod, p) + + +def polynomial_exp_mod(base, exponent, polymod, p): + """Polynomial exponentiation modulo a polynomial over ints mod p. + + Polynomials are represented as lists of coefficients + of increasing powers of x.""" + + # Based on the Handbook of Applied Cryptography, algorithm 2.227. + + # This module has been tested only by extensive use + # in calculating modular square roots. + + assert exponent < p + + if exponent == 0: + return [1] + + G = base + k = exponent + if k % 2 == 1: + s = G + else: + s = [1] + + while k > 1: + k = k // 2 + G = polynomial_multiply_mod(G, G, polymod, p) + if k % 2 == 1: + s = polynomial_multiply_mod(G, s, polymod, p) + + return s + + +def jacobi(a, n): + """Jacobi symbol""" + + # Based on the Handbook of Applied Cryptography (HAC), algorithm 2.149. + + # This function has been tested by comparison with a small + # table printed in HAC, and by extensive use in calculating + # modular square roots. + + if not n >= 3: + raise JacobiError("n must be larger than 2") + if not n % 2 == 1: + raise JacobiError("n must be odd") + a = a % n + if a == 0: + return 0 + if a == 1: + return 1 + a1, e = a, 0 + while a1 % 2 == 0: + a1, e = a1 // 2, e + 1 + if e % 2 == 0 or n % 8 == 1 or n % 8 == 7: + s = 1 + else: + s = -1 + if a1 == 1: + return s + if n % 4 == 3 and a1 % 4 == 3: + s = -s + return s * jacobi(n % a1, a1) + + +def square_root_mod_prime(a, p): + """Modular square root of a, mod p, p prime.""" + + # Based on the Handbook of Applied Cryptography, algorithms 3.34 to 3.39. + + # This module has been tested for all values in [0,p-1] for + # every prime p from 3 to 1229. + + assert 0 <= a < p + assert 1 < p + + if a == 0: + return 0 + if p == 2: + return a + + jac = jacobi(a, p) + if jac == -1: + raise SquareRootError("%d has no square root modulo %d" % (a, p)) + + if p % 4 == 3: + return pow(a, (p + 1) // 4, p) + + if p % 8 == 5: + d = pow(a, (p - 1) // 4, p) + if d == 1: + return pow(a, (p + 3) // 8, p) + assert d == p - 1 + return (2 * a * pow(4 * a, (p - 5) // 8, p)) % p + + if PY2: + # xrange on python2 can take integers representable as C long only + range_top = min(0x7FFFFFFF, p) + else: + range_top = p + for b in xrange(2, range_top): # pragma: no branch + if jacobi(b * b - 4 * a, p) == -1: + f = (a, -b, 1) + ff = polynomial_exp_mod((0, 1), (p + 1) // 2, f, p) + if ff[1]: + raise SquareRootError("p is not prime") + return ff[0] + # just an assertion + raise RuntimeError("No b found.") # pragma: no cover + + +# because all the inverse_mod code is arch/environment specific, and coveralls +# expects it to execute equal number of times, we need to waive it by +# adding the "no branch" pragma to all branches +if GMPY2: # pragma: no branch + + def inverse_mod(a, m): + """Inverse of a mod m.""" + if a == 0: # pragma: no branch + return 0 + return powmod(a, -1, m) + +elif GMPY: # pragma: no branch + + def inverse_mod(a, m): + """Inverse of a mod m.""" + # while libgmp does support inverses modulo, it is accessible + # only using the native `pow()` function, and `pow()` in gmpy sanity + # checks the parameters before passing them on to underlying + # implementation + if a == 0: # pragma: no branch + return 0 + a = mpz(a) + m = mpz(m) + + lm, hm = mpz(1), mpz(0) + low, high = a % m, m + while low > 1: # pragma: no branch + r = high // low + lm, low, hm, high = hm - lm * r, high - low * r, lm, low + + return lm % m + +elif sys.version_info >= (3, 8): # pragma: no branch + + def inverse_mod(a, m): + """Inverse of a mod m.""" + if a == 0: # pragma: no branch + return 0 + return pow(a, -1, m) + +else: # pragma: no branch + + def inverse_mod(a, m): + """Inverse of a mod m.""" + + if a == 0: # pragma: no branch + return 0 + + lm, hm = 1, 0 + low, high = a % m, m + while low > 1: # pragma: no branch + r = high // low + lm, low, hm, high = hm - lm * r, high - low * r, lm, low + + return lm % m + + +try: + gcd2 = math.gcd +except AttributeError: + + def gcd2(a, b): + """Greatest common divisor using Euclid's algorithm.""" + while a: + a, b = b % a, a + return b + + +def gcd(*a): + """Greatest common divisor. + + Usage: gcd([ 2, 4, 6 ]) + or: gcd(2, 4, 6) + """ + + if len(a) > 1: + return reduce(gcd2, a) + if hasattr(a[0], "__iter__"): + return reduce(gcd2, a[0]) + return a[0] + + +def lcm2(a, b): + """Least common multiple of two integers.""" + + return (a * b) // gcd(a, b) + + +def lcm(*a): + """Least common multiple. + + Usage: lcm([ 3, 4, 5 ]) + or: lcm(3, 4, 5) + """ + + if len(a) > 1: + return reduce(lcm2, a) + if hasattr(a[0], "__iter__"): + return reduce(lcm2, a[0]) + return a[0] + + +def factorization(n): + """Decompose n into a list of (prime,exponent) pairs.""" + + assert isinstance(n, integer_types) + + if n < 2: + return [] + + result = [] + + # Test the small primes: + + for d in smallprimes: + if d > n: + break + q, r = divmod(n, d) + if r == 0: + count = 1 + while d <= n: # pragma: no branch + n = q + q, r = divmod(n, d) + if r != 0: + break + count = count + 1 + result.append((d, count)) + + # If n is still greater than the last of our small primes, + # it may require further work: + + if n > smallprimes[-1]: + if is_prime(n): # If what's left is prime, it's easy: + result.append((n, 1)) + else: # Ugh. Search stupidly for a divisor: + d = smallprimes[-1] + while 1: + d = d + 2 # Try the next divisor. + q, r = divmod(n, d) + if q < d: # n < d*d means we're done, n = 1 or prime. + break + if r == 0: # d divides n. How many times? + count = 1 + n = q + # As long as d might still divide n, + while d <= n: # pragma: no branch + q, r = divmod(n, d) # see if it does. + if r != 0: + break + n = q # It does. Reduce n, increase count. + count = count + 1 + result.append((d, count)) + if n > 1: + result.append((n, 1)) + + return result + + +def phi(n): # pragma: no cover + """Return the Euler totient function of n.""" + # deprecated in 0.14 + warnings.warn( + "Function is unused by library code. If you use this code, " + "please open an issue in " + "https://github.com/tlsfuzzer/python-ecdsa", + DeprecationWarning, + ) + + assert isinstance(n, integer_types) + + if n < 3: + return 1 + + result = 1 + ff = factorization(n) + for f in ff: + e = f[1] + if e > 1: + result = result * f[0] ** (e - 1) * (f[0] - 1) + else: + result = result * (f[0] - 1) + return result + + +def carmichael(n): # pragma: no cover + """Return Carmichael function of n. + + Carmichael(n) is the smallest integer x such that + m**x = 1 mod n for all m relatively prime to n. + """ + # deprecated in 0.14 + warnings.warn( + "Function is unused by library code. If you use this code, " + "please open an issue in " + "https://github.com/tlsfuzzer/python-ecdsa", + DeprecationWarning, + ) + + return carmichael_of_factorized(factorization(n)) + + +def carmichael_of_factorized(f_list): # pragma: no cover + """Return the Carmichael function of a number that is + represented as a list of (prime,exponent) pairs. + """ + # deprecated in 0.14 + warnings.warn( + "Function is unused by library code. If you use this code, " + "please open an issue in " + "https://github.com/tlsfuzzer/python-ecdsa", + DeprecationWarning, + ) + + if len(f_list) < 1: + return 1 + + result = carmichael_of_ppower(f_list[0]) + for i in xrange(1, len(f_list)): + result = lcm(result, carmichael_of_ppower(f_list[i])) + + return result + + +def carmichael_of_ppower(pp): # pragma: no cover + """Carmichael function of the given power of the given prime.""" + # deprecated in 0.14 + warnings.warn( + "Function is unused by library code. If you use this code, " + "please open an issue in " + "https://github.com/tlsfuzzer/python-ecdsa", + DeprecationWarning, + ) + + p, a = pp + if p == 2 and a > 2: + return 2 ** (a - 2) + else: + return (p - 1) * p ** (a - 1) + + +def order_mod(x, m): # pragma: no cover + """Return the order of x in the multiplicative group mod m.""" + # deprecated in 0.14 + warnings.warn( + "Function is unused by library code. If you use this code, " + "please open an issue in " + "https://github.com/tlsfuzzer/python-ecdsa", + DeprecationWarning, + ) + + # Warning: this implementation is not very clever, and will + # take a long time if m is very large. + + if m <= 1: + return 0 + + assert gcd(x, m) == 1 + + z = x + result = 1 + while z != 1: + z = (z * x) % m + result = result + 1 + return result + + +def largest_factor_relatively_prime(a, b): # pragma: no cover + """Return the largest factor of a relatively prime to b.""" + # deprecated in 0.14 + warnings.warn( + "Function is unused by library code. If you use this code, " + "please open an issue in " + "https://github.com/tlsfuzzer/python-ecdsa", + DeprecationWarning, + ) + + while 1: + d = gcd(a, b) + if d <= 1: + break + b = d + while 1: + q, r = divmod(a, d) + if r > 0: + break + a = q + return a + + +def kinda_order_mod(x, m): # pragma: no cover + """Return the order of x in the multiplicative group mod m', + where m' is the largest factor of m relatively prime to x. + """ + # deprecated in 0.14 + warnings.warn( + "Function is unused by library code. If you use this code, " + "please open an issue in " + "https://github.com/tlsfuzzer/python-ecdsa", + DeprecationWarning, + ) + + return order_mod(x, largest_factor_relatively_prime(m, x)) + + +def is_prime(n): + """Return True if x is prime, False otherwise. + + We use the Miller-Rabin test, as given in Menezes et al. p. 138. + This test is not exact: there are composite values n for which + it returns True. + + In testing the odd numbers from 10000001 to 19999999, + about 66 composites got past the first test, + 5 got past the second test, and none got past the third. + Since factors of 2, 3, 5, 7, and 11 were detected during + preliminary screening, the number of numbers tested by + Miller-Rabin was (19999999 - 10000001)*(2/3)*(4/5)*(6/7) + = 4.57 million. + """ + + # (This is used to study the risk of false positives:) + global miller_rabin_test_count + + miller_rabin_test_count = 0 + + if n <= smallprimes[-1]: + if n in smallprimes: + return True + else: + return False + # 2310 = 2 * 3 * 5 * 7 * 11 + if gcd(n, 2310) != 1: + return False + + # Choose a number of iterations sufficient to reduce the + # probability of accepting a composite below 2**-80 + # (from Menezes et al. Table 4.4): + + t = 40 + n_bits = 1 + bit_length(n) + assert 11 <= n_bits <= 16384 + for k, tt in ( + (100, 27), + (150, 18), + (200, 15), + (250, 12), + (300, 9), + (350, 8), + (400, 7), + (450, 6), + (550, 5), + (650, 4), + (850, 3), + (1300, 2), + ): + if n_bits < k: + break + t = tt + + # Run the test t times: + + s = 0 + r = n - 1 + while (r % 2) == 0: + s = s + 1 + r = r // 2 + for i in xrange(t): + a = random.choice(smallprimes) + y = pow(a, r, n) + if y != 1 and y != n - 1: + j = 1 + while j <= s - 1 and y != n - 1: + y = pow(y, 2, n) + if y == 1: + miller_rabin_test_count = i + 1 + return False + j = j + 1 + if y != n - 1: + miller_rabin_test_count = i + 1 + return False + return True + + +def next_prime(starting_value): + """Return the smallest prime larger than the starting value.""" + + if starting_value < 2: + return 2 + result = (starting_value + 1) | 1 + while not is_prime(result): + result = result + 2 + return result + + +smallprimes = [ + 2, + 3, + 5, + 7, + 11, + 13, + 17, + 19, + 23, + 29, + 31, + 37, + 41, + 43, + 47, + 53, + 59, + 61, + 67, + 71, + 73, + 79, + 83, + 89, + 97, + 101, + 103, + 107, + 109, + 113, + 127, + 131, + 137, + 139, + 149, + 151, + 157, + 163, + 167, + 173, + 179, + 181, + 191, + 193, + 197, + 199, + 211, + 223, + 227, + 229, + 233, + 239, + 241, + 251, + 257, + 263, + 269, + 271, + 277, + 281, + 283, + 293, + 307, + 311, + 313, + 317, + 331, + 337, + 347, + 349, + 353, + 359, + 367, + 373, + 379, + 383, + 389, + 397, + 401, + 409, + 419, + 421, + 431, + 433, + 439, + 443, + 449, + 457, + 461, + 463, + 467, + 479, + 487, + 491, + 499, + 503, + 509, + 521, + 523, + 541, + 547, + 557, + 563, + 569, + 571, + 577, + 587, + 593, + 599, + 601, + 607, + 613, + 617, + 619, + 631, + 641, + 643, + 647, + 653, + 659, + 661, + 673, + 677, + 683, + 691, + 701, + 709, + 719, + 727, + 733, + 739, + 743, + 751, + 757, + 761, + 769, + 773, + 787, + 797, + 809, + 811, + 821, + 823, + 827, + 829, + 839, + 853, + 857, + 859, + 863, + 877, + 881, + 883, + 887, + 907, + 911, + 919, + 929, + 937, + 941, + 947, + 953, + 967, + 971, + 977, + 983, + 991, + 997, + 1009, + 1013, + 1019, + 1021, + 1031, + 1033, + 1039, + 1049, + 1051, + 1061, + 1063, + 1069, + 1087, + 1091, + 1093, + 1097, + 1103, + 1109, + 1117, + 1123, + 1129, + 1151, + 1153, + 1163, + 1171, + 1181, + 1187, + 1193, + 1201, + 1213, + 1217, + 1223, + 1229, +] + +miller_rabin_test_count = 0 diff --git a/code/.venv/lib/python3.12/site-packages/ecdsa/rfc6979.py b/code/.venv/lib/python3.12/site-packages/ecdsa/rfc6979.py new file mode 100644 index 0000000..0728b5a --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/ecdsa/rfc6979.py @@ -0,0 +1,113 @@ +""" +RFC 6979: + Deterministic Usage of the Digital Signature Algorithm (DSA) and + Elliptic Curve Digital Signature Algorithm (ECDSA) + + http://tools.ietf.org/html/rfc6979 + +Many thanks to Coda Hale for his implementation in Go language: + https://github.com/codahale/rfc6979 +""" + +import hmac +from binascii import hexlify +from .util import number_to_string, number_to_string_crop, bit_length +from ._compat import hmac_compat + + +# bit_length was defined in this module previously so keep it for backwards +# compatibility, will need to deprecate and remove it later +__all__ = ["bit_length", "bits2int", "bits2octets", "generate_k"] + + +def bits2int(data, qlen): + x = int(hexlify(data), 16) + l = len(data) * 8 + + if l > qlen: + return x >> (l - qlen) + return x + + +def bits2octets(data, order): + z1 = bits2int(data, bit_length(order)) + z2 = z1 - order + + if z2 < 0: + z2 = z1 + + return number_to_string_crop(z2, order) + + +# https://tools.ietf.org/html/rfc6979#section-3.2 +def generate_k(order, secexp, hash_func, data, retry_gen=0, extra_entropy=b""): + """ + Generate the ``k`` value - the nonce for DSA. + + :param int order: order of the DSA generator used in the signature + :param int secexp: secure exponent (private key) in numeric form + :param hash_func: reference to the same hash function used for generating + hash, like :py:class:`hashlib.sha1` + :param bytes data: hash in binary form of the signing data + :param int retry_gen: how many good 'k' values to skip before returning + :param bytes extra_entropy: additional added data in binary form as per + section-3.6 of rfc6979 + :rtype: int + """ + + qlen = bit_length(order) + holen = hash_func().digest_size + rolen = (qlen + 7) // 8 + bx = ( + hmac_compat(number_to_string(secexp, order)), + hmac_compat(bits2octets(data, order)), + hmac_compat(extra_entropy), + ) + + # Step B + v = b"\x01" * holen + + # Step C + k = b"\x00" * holen + + # Step D + + k = hmac.new(k, digestmod=hash_func) + k.update(v + b"\x00") + for i in bx: + k.update(i) + k = k.digest() + + # Step E + v = hmac.new(k, v, hash_func).digest() + + # Step F + k = hmac.new(k, digestmod=hash_func) + k.update(v + b"\x01") + for i in bx: + k.update(i) + k = k.digest() + + # Step G + v = hmac.new(k, v, hash_func).digest() + + # Step H + while True: + # Step H1 + t = b"" + + # Step H2 + while len(t) < rolen: + v = hmac.new(k, v, hash_func).digest() + t += v + + # Step H3 + secret = bits2int(t, qlen) + + if 1 <= secret < order: + if retry_gen <= 0: + return secret + retry_gen -= 1 + + k = hmac.new(k, v + b"\x00", hash_func).digest() + v = hmac.new(k, v, hash_func).digest() diff --git a/code/.venv/lib/python3.12/site-packages/ecdsa/ssh.py b/code/.venv/lib/python3.12/site-packages/ecdsa/ssh.py new file mode 100644 index 0000000..64e9403 --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/ecdsa/ssh.py @@ -0,0 +1,83 @@ +import binascii +from . import der +from ._compat import compat26_str, int_to_bytes + +_SSH_ED25519 = b"ssh-ed25519" +_SK_MAGIC = b"openssh-key-v1\0" +_NONE = b"none" + + +def _get_key_type(name): + if name == "Ed25519": + return _SSH_ED25519 + else: + raise ValueError("Unsupported key type") + + +class _Serializer: + def __init__(self): + self.bytes = b"" + + def put_raw(self, val): + self.bytes += val + + def put_u32(self, val): + self.bytes += int_to_bytes(val, length=4, byteorder="big") + + def put_str(self, val): + self.put_u32(len(val)) + self.bytes += val + + def put_pad(self, blklen=8): + padlen = blklen - (len(self.bytes) % blklen) + self.put_raw(bytearray(range(1, 1 + padlen))) + + def encode(self): + return binascii.b2a_base64(compat26_str(self.bytes)) + + def tobytes(self): + return self.bytes + + def topem(self): + return der.topem(self.bytes, "OPENSSH PRIVATE KEY") + + +def serialize_public(name, pub): + serial = _Serializer() + ktype = _get_key_type(name) + serial.put_str(ktype) + serial.put_str(pub) + return b" ".join([ktype, serial.encode()]) + + +def serialize_private(name, pub, priv): + # encode public part + spub = _Serializer() + ktype = _get_key_type(name) + spub.put_str(ktype) + spub.put_str(pub) + + # encode private part + spriv = _Serializer() + checksum = 0 + spriv.put_u32(checksum) + spriv.put_u32(checksum) + spriv.put_raw(spub.tobytes()) + spriv.put_str(priv + pub) + comment = b"" + spriv.put_str(comment) + spriv.put_pad() + + # top-level structure + main = _Serializer() + main.put_raw(_SK_MAGIC) + ciphername = kdfname = _NONE + main.put_str(ciphername) + main.put_str(kdfname) + nokdf = 0 + main.put_u32(nokdf) + nkeys = 1 + main.put_u32(nkeys) + main.put_str(spub.tobytes()) + main.put_str(spriv.tobytes()) + return main.topem() diff --git a/code/.venv/lib/python3.12/site-packages/ecdsa/test_curves.py b/code/.venv/lib/python3.12/site-packages/ecdsa/test_curves.py new file mode 100644 index 0000000..93b6c9b --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/ecdsa/test_curves.py @@ -0,0 +1,361 @@ +try: + import unittest2 as unittest +except ImportError: + import unittest + +import base64 +import pytest +from .curves import ( + Curve, + NIST256p, + curves, + UnknownCurveError, + PRIME_FIELD_OID, + curve_by_name, +) +from .ellipticcurve import CurveFp, PointJacobi, CurveEdTw +from . import der +from .util import number_to_string + + +class TestParameterEncoding(unittest.TestCase): + @classmethod + def setUpClass(cls): + # minimal, but with cofactor (excludes seed when compared to + # OpenSSL output) + cls.base64_params = ( + "MIHgAgEBMCwGByqGSM49AQECIQD/////AAAAAQAAAAAAAAAAAAAAAP/////////" + "//////zBEBCD/////AAAAAQAAAAAAAAAAAAAAAP///////////////AQgWsY12K" + "o6k+ez671VdpiGvGUdBrDMU7D2O848PifSYEsEQQRrF9Hy4SxCR/i85uVjpEDyd" + "wN9gS3rM6D0oTlF2JjClk/jQuL+Gn+bjufrSnwPnhYrzjNXazFezsu2QGg3v1H1" + "AiEA/////wAAAAD//////////7zm+q2nF56E87nKwvxjJVECAQE=" + ) + + def test_from_pem(self): + pem_params = ( + "-----BEGIN EC PARAMETERS-----\n" + "MIHgAgEBMCwGByqGSM49AQECIQD/////AAAAAQAAAAAAAAAAAAAAAP/////////\n" + "//////zBEBCD/////AAAAAQAAAAAAAAAAAAAAAP///////////////AQgWsY12K\n" + "o6k+ez671VdpiGvGUdBrDMU7D2O848PifSYEsEQQRrF9Hy4SxCR/i85uVjpEDyd\n" + "wN9gS3rM6D0oTlF2JjClk/jQuL+Gn+bjufrSnwPnhYrzjNXazFezsu2QGg3v1H1\n" + "AiEA/////wAAAAD//////////7zm+q2nF56E87nKwvxjJVECAQE=\n" + "-----END EC PARAMETERS-----\n" + ) + curve = Curve.from_pem(pem_params) + + self.assertIs(curve, NIST256p) + + def test_from_pem_with_explicit_when_explicit_disabled(self): + pem_params = ( + "-----BEGIN EC PARAMETERS-----\n" + "MIHgAgEBMCwGByqGSM49AQECIQD/////AAAAAQAAAAAAAAAAAAAAAP/////////\n" + "//////zBEBCD/////AAAAAQAAAAAAAAAAAAAAAP///////////////AQgWsY12K\n" + "o6k+ez671VdpiGvGUdBrDMU7D2O848PifSYEsEQQRrF9Hy4SxCR/i85uVjpEDyd\n" + "wN9gS3rM6D0oTlF2JjClk/jQuL+Gn+bjufrSnwPnhYrzjNXazFezsu2QGg3v1H1\n" + "AiEA/////wAAAAD//////////7zm+q2nF56E87nKwvxjJVECAQE=\n" + "-----END EC PARAMETERS-----\n" + ) + with self.assertRaises(der.UnexpectedDER) as e: + Curve.from_pem(pem_params, ["named_curve"]) + + self.assertIn("explicit curve parameters not", str(e.exception)) + + def test_from_pem_with_named_curve_with_named_curve_disabled(self): + pem_params = ( + "-----BEGIN EC PARAMETERS-----\n" + "BggqhkjOPQMBBw==\n" + "-----END EC PARAMETERS-----\n" + ) + with self.assertRaises(der.UnexpectedDER) as e: + Curve.from_pem(pem_params, ["explicit"]) + + self.assertIn("named_curve curve parameters not", str(e.exception)) + + def test_from_pem_with_wrong_header(self): + pem_params = ( + "-----BEGIN PARAMETERS-----\n" + "MIHgAgEBMCwGByqGSM49AQECIQD/////AAAAAQAAAAAAAAAAAAAAAP/////////\n" + "//////zBEBCD/////AAAAAQAAAAAAAAAAAAAAAP///////////////AQgWsY12K\n" + "o6k+ez671VdpiGvGUdBrDMU7D2O848PifSYEsEQQRrF9Hy4SxCR/i85uVjpEDyd\n" + "wN9gS3rM6D0oTlF2JjClk/jQuL+Gn+bjufrSnwPnhYrzjNXazFezsu2QGg3v1H1\n" + "AiEA/////wAAAAD//////////7zm+q2nF56E87nKwvxjJVECAQE=\n" + "-----END PARAMETERS-----\n" + ) + with self.assertRaises(der.UnexpectedDER) as e: + Curve.from_pem(pem_params) + + self.assertIn("PARAMETERS PEM header", str(e.exception)) + + def test_to_pem(self): + pem_params = ( + b"-----BEGIN EC PARAMETERS-----\n" + b"BggqhkjOPQMBBw==\n" + b"-----END EC PARAMETERS-----\n" + ) + encoding = NIST256p.to_pem() + + self.assertEqual(pem_params, encoding) + + def test_compare_with_different_object(self): + self.assertNotEqual(NIST256p, 256) + + def test_named_curve_params_der(self): + encoded = NIST256p.to_der() + + # just the encoding of the NIST256p OID (prime256v1) + self.assertEqual(b"\x06\x08\x2a\x86\x48\xce\x3d\x03\x01\x07", encoded) + + def test_verify_that_default_is_named_curve_der(self): + encoded_default = NIST256p.to_der() + encoded_named = NIST256p.to_der("named_curve") + + self.assertEqual(encoded_default, encoded_named) + + def test_encoding_to_explicit_params(self): + encoded = NIST256p.to_der("explicit") + + self.assertEqual(encoded, bytes(base64.b64decode(self.base64_params))) + + def test_encoding_to_unsupported_type(self): + with self.assertRaises(ValueError) as e: + NIST256p.to_der("unsupported") + + self.assertIn("Only 'named_curve'", str(e.exception)) + + def test_encoding_to_explicit_compressed_params(self): + encoded = NIST256p.to_der("explicit", "compressed") + + compressed_base_point = ( + "MIHAAgEBMCwGByqGSM49AQECIQD/////AAAAAQAAAAAAAAAAAAAAAP//////////" + "/////zBEBCD/////AAAAAQAAAAAAAAAAAAAAAP///////////////AQgWsY12Ko6" + "k+ez671VdpiGvGUdBrDMU7D2O848PifSYEsEIQNrF9Hy4SxCR/i85uVjpEDydwN9" + "gS3rM6D0oTlF2JjClgIhAP////8AAAAA//////////+85vqtpxeehPO5ysL8YyVR" + "AgEB" + ) + + self.assertEqual( + encoded, bytes(base64.b64decode(compressed_base_point)) + ) + + def test_decoding_explicit_from_openssl(self): + # generated with openssl 1.1.1k using + # openssl ecparam -name P-256 -param_enc explicit -out /tmp/file.pem + p256_explicit = ( + "MIH3AgEBMCwGByqGSM49AQECIQD/////AAAAAQAAAAAAAAAAAAAAAP//////////" + "/////zBbBCD/////AAAAAQAAAAAAAAAAAAAAAP///////////////AQgWsY12Ko6" + "k+ez671VdpiGvGUdBrDMU7D2O848PifSYEsDFQDEnTYIhucEk2pmeOETnSa3gZ9+" + "kARBBGsX0fLhLEJH+Lzm5WOkQPJ3A32BLeszoPShOUXYmMKWT+NC4v4af5uO5+tK" + "fA+eFivOM1drMV7Oy7ZAaDe/UfUCIQD/////AAAAAP//////////vOb6racXnoTz" + "ucrC/GMlUQIBAQ==" + ) + + decoded = Curve.from_der(bytes(base64.b64decode(p256_explicit))) + + self.assertEqual(NIST256p, decoded) + + def test_decoding_well_known_from_explicit_params(self): + curve = Curve.from_der(bytes(base64.b64decode(self.base64_params))) + + self.assertIs(curve, NIST256p) + + def test_decoding_with_incorrect_valid_encodings(self): + with self.assertRaises(ValueError) as e: + Curve.from_der(b"", ["explicitCA"]) + + self.assertIn("Only named_curve", str(e.exception)) + + def test_compare_curves_with_different_generators(self): + curve_fp = CurveFp(23, 1, 7) + base_a = PointJacobi(curve_fp, 13, 3, 1, 9, generator=True) + base_b = PointJacobi(curve_fp, 1, 20, 1, 9, generator=True) + + curve_a = Curve("unknown", curve_fp, base_a, None) + curve_b = Curve("unknown", curve_fp, base_b, None) + + self.assertNotEqual(curve_a, curve_b) + + def test_default_encode_for_custom_curve(self): + curve_fp = CurveFp(23, 1, 7) + base_point = PointJacobi(curve_fp, 13, 3, 1, 9, generator=True) + + curve = Curve("unknown", curve_fp, base_point, None) + + encoded = curve.to_der() + + decoded = Curve.from_der(encoded) + + self.assertEqual(curve, decoded) + + expected = "MCECAQEwDAYHKoZIzj0BAQIBFzAGBAEBBAEHBAMEDQMCAQk=" + + self.assertEqual(encoded, bytes(base64.b64decode(expected))) + + def test_named_curve_encode_for_custom_curve(self): + curve_fp = CurveFp(23, 1, 7) + base_point = PointJacobi(curve_fp, 13, 3, 1, 9, generator=True) + + curve = Curve("unknown", curve_fp, base_point, None) + + with self.assertRaises(UnknownCurveError) as e: + curve.to_der("named_curve") + + self.assertIn("Can't encode curve", str(e.exception)) + + def test_try_decoding_binary_explicit(self): + sect113r1_explicit = ( + "MIGRAgEBMBwGByqGSM49AQIwEQIBcQYJKoZIzj0BAgMCAgEJMDkEDwAwiCUMpufH" + "/mSc6Fgg9wQPAOi+5NPiJgdEGIvg6ccjAxUAEOcjqxTWluZ2h1YVF1b+v4/LSakE" + "HwQAnXNhbzX0qxQH1zViwQ8ApSgwJ3lY7oTRMV7TGIYCDwEAAAAAAAAA2czsijnl" + "bwIBAg==" + ) + + with self.assertRaises(UnknownCurveError) as e: + Curve.from_der(base64.b64decode(sect113r1_explicit)) + + self.assertIn("Characteristic 2 curves unsupported", str(e.exception)) + + def test_decode_malformed_named_curve(self): + bad_der = der.encode_oid(*NIST256p.oid) + der.encode_integer(1) + + with self.assertRaises(der.UnexpectedDER) as e: + Curve.from_der(bad_der) + + self.assertIn("Unexpected data after OID", str(e.exception)) + + def test_decode_malformed_explicit_garbage_after_ECParam(self): + bad_der = bytes( + base64.b64decode(self.base64_params) + ) + der.encode_integer(1) + + with self.assertRaises(der.UnexpectedDER) as e: + Curve.from_der(bad_der) + + self.assertIn("Unexpected data after ECParameters", str(e.exception)) + + def test_decode_malformed_unknown_version_number(self): + bad_der = der.encode_sequence(der.encode_integer(2)) + + with self.assertRaises(der.UnexpectedDER) as e: + Curve.from_der(bad_der) + + self.assertIn("Unknown parameter encoding format", str(e.exception)) + + def test_decode_malformed_unknown_field_type(self): + curve_p = NIST256p.curve.p() + bad_der = der.encode_sequence( + der.encode_integer(1), + der.encode_sequence( + der.encode_oid(1, 2, 3), der.encode_integer(curve_p) + ), + der.encode_sequence( + der.encode_octet_string( + number_to_string(NIST256p.curve.a() % curve_p, curve_p) + ), + der.encode_octet_string( + number_to_string(NIST256p.curve.b(), curve_p) + ), + ), + der.encode_octet_string( + NIST256p.generator.to_bytes("uncompressed") + ), + der.encode_integer(NIST256p.generator.order()), + ) + + with self.assertRaises(UnknownCurveError) as e: + Curve.from_der(bad_der) + + self.assertIn("Unknown field type: (1, 2, 3)", str(e.exception)) + + def test_decode_malformed_garbage_after_prime(self): + curve_p = NIST256p.curve.p() + bad_der = der.encode_sequence( + der.encode_integer(1), + der.encode_sequence( + der.encode_oid(*PRIME_FIELD_OID), + der.encode_integer(curve_p), + der.encode_integer(1), + ), + der.encode_sequence( + der.encode_octet_string( + number_to_string(NIST256p.curve.a() % curve_p, curve_p) + ), + der.encode_octet_string( + number_to_string(NIST256p.curve.b(), curve_p) + ), + ), + der.encode_octet_string( + NIST256p.generator.to_bytes("uncompressed") + ), + der.encode_integer(NIST256p.generator.order()), + ) + + with self.assertRaises(der.UnexpectedDER) as e: + Curve.from_der(bad_der) + + self.assertIn("Prime-p element", str(e.exception)) + + +class TestCurveSearching(unittest.TestCase): + def test_correct_name(self): + c = curve_by_name("NIST256p") + self.assertIs(c, NIST256p) + + def test_openssl_name(self): + c = curve_by_name("prime256v1") + self.assertIs(c, NIST256p) + + def test_unknown_curve(self): + with self.assertRaises(UnknownCurveError) as e: + curve_by_name("foo bar") + + self.assertIn( + "name 'foo bar' unknown, only curves supported: " + "['NIST192p', 'NIST224p'", + str(e.exception), + ) + + def test_with_None_as_parameter(self): + with self.assertRaises(UnknownCurveError) as e: + curve_by_name(None) + + self.assertIn( + "name None unknown, only curves supported: " + "['NIST192p', 'NIST224p'", + str(e.exception), + ) + + +@pytest.mark.parametrize("curve", curves, ids=[i.name for i in curves]) +def test_curve_params_encode_decode_named(curve): + ret = Curve.from_der(curve.to_der("named_curve")) + + assert curve == ret + + +@pytest.mark.parametrize("curve", curves, ids=[i.name for i in curves]) +def test_curve_params_encode_decode_explicit(curve): + if isinstance(curve.curve, CurveEdTw): + with pytest.raises(UnknownCurveError): + curve.to_der("explicit") + else: + ret = Curve.from_der(curve.to_der("explicit")) + + assert curve == ret + + +@pytest.mark.parametrize("curve", curves, ids=[i.name for i in curves]) +def test_curve_params_encode_decode_default(curve): + ret = Curve.from_der(curve.to_der()) + + assert curve == ret + + +@pytest.mark.parametrize("curve", curves, ids=[i.name for i in curves]) +def test_curve_params_encode_decode_explicit_compressed(curve): + if isinstance(curve.curve, CurveEdTw): + with pytest.raises(UnknownCurveError): + curve.to_der("explicit", "compressed") + else: + ret = Curve.from_der(curve.to_der("explicit", "compressed")) + + assert curve == ret diff --git a/code/.venv/lib/python3.12/site-packages/ecdsa/test_der.py b/code/.venv/lib/python3.12/site-packages/ecdsa/test_der.py new file mode 100644 index 0000000..0c2dc4d --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/ecdsa/test_der.py @@ -0,0 +1,478 @@ +# compatibility with Python 2.6, for that we need unittest2 package, +# which is not available on 3.3 or 3.4 +import warnings +from binascii import hexlify + +try: + import unittest2 as unittest +except ImportError: + import unittest +import sys +import hypothesis.strategies as st +from hypothesis import given, settings +import pytest +from ._compat import str_idx_as_int +from .curves import NIST256p, NIST224p +from .der import ( + remove_integer, + UnexpectedDER, + read_length, + encode_bitstring, + remove_bitstring, + remove_object, + encode_oid, + remove_constructed, + remove_octet_string, + remove_sequence, +) + + +class TestRemoveInteger(unittest.TestCase): + # DER requires the integers to be 0-padded only if they would be + # interpreted as negative, check if those errors are detected + def test_non_minimal_encoding(self): + with self.assertRaises(UnexpectedDER): + remove_integer(b"\x02\x02\x00\x01") + + def test_negative_with_high_bit_set(self): + with self.assertRaises(UnexpectedDER): + remove_integer(b"\x02\x01\x80") + + def test_minimal_with_high_bit_set(self): + val, rem = remove_integer(b"\x02\x02\x00\x80") + + self.assertEqual(val, 0x80) + self.assertEqual(rem, b"") + + def test_two_zero_bytes_with_high_bit_set(self): + with self.assertRaises(UnexpectedDER): + remove_integer(b"\x02\x03\x00\x00\xff") + + def test_zero_length_integer(self): + with self.assertRaises(UnexpectedDER): + remove_integer(b"\x02\x00") + + def test_empty_string(self): + with self.assertRaises(UnexpectedDER): + remove_integer(b"") + + def test_encoding_of_zero(self): + val, rem = remove_integer(b"\x02\x01\x00") + + self.assertEqual(val, 0) + self.assertEqual(rem, b"") + + def test_encoding_of_127(self): + val, rem = remove_integer(b"\x02\x01\x7f") + + self.assertEqual(val, 127) + self.assertEqual(rem, b"") + + def test_encoding_of_128(self): + val, rem = remove_integer(b"\x02\x02\x00\x80") + + self.assertEqual(val, 128) + self.assertEqual(rem, b"") + + def test_wrong_tag(self): + with self.assertRaises(UnexpectedDER) as e: + remove_integer(b"\x01\x02\x00\x80") + + self.assertIn("wanted type 'integer'", str(e.exception)) + + def test_wrong_length(self): + with self.assertRaises(UnexpectedDER) as e: + remove_integer(b"\x02\x03\x00\x80") + + self.assertIn("Length longer", str(e.exception)) + + +class TestReadLength(unittest.TestCase): + # DER requires the lengths between 0 and 127 to be encoded using the short + # form and lengths above that encoded with minimal number of bytes + # necessary + def test_zero_length(self): + self.assertEqual((0, 1), read_length(b"\x00")) + + def test_two_byte_zero_length(self): + with self.assertRaises(UnexpectedDER): + read_length(b"\x81\x00") + + def test_two_byte_small_length(self): + with self.assertRaises(UnexpectedDER): + read_length(b"\x81\x7f") + + def test_long_form_with_zero_length(self): + with self.assertRaises(UnexpectedDER): + read_length(b"\x80") + + def test_smallest_two_byte_length(self): + self.assertEqual((128, 2), read_length(b"\x81\x80")) + + def test_zero_padded_length(self): + with self.assertRaises(UnexpectedDER): + read_length(b"\x82\x00\x80") + + def test_two_three_byte_length(self): + self.assertEqual((256, 3), read_length(b"\x82\x01\x00")) + + def test_empty_string(self): + with self.assertRaises(UnexpectedDER): + read_length(b"") + + def test_length_overflow(self): + with self.assertRaises(UnexpectedDER): + read_length(b"\x83\x01\x00") + + +class TestEncodeBitstring(unittest.TestCase): + # DER requires BIT STRINGS to include a number of padding bits in the + # encoded byte string, that padding must be between 0 and 7 + + def test_old_call_convention(self): + """This is the old way to use the function.""" + warnings.simplefilter("always") + with pytest.warns(DeprecationWarning) as warns: + der = encode_bitstring(b"\x00\xff") + + self.assertEqual(len(warns), 1) + self.assertIn( + "unused= needs to be specified", warns[0].message.args[0] + ) + + self.assertEqual(der, b"\x03\x02\x00\xff") + + def test_new_call_convention(self): + """This is how it should be called now.""" + # make sure no warnings are raised + with warnings.catch_warnings(): + warnings.simplefilter("error") + der = encode_bitstring(b"\xff", 0) + + self.assertEqual(der, b"\x03\x02\x00\xff") + + def test_implicit_unused_bits(self): + """ + Writing bit string with already included the number of unused bits. + """ + # make sure no warnings are raised + with warnings.catch_warnings(): + warnings.simplefilter("error") + der = encode_bitstring(b"\x00\xff", None) + + self.assertEqual(der, b"\x03\x02\x00\xff") + + def test_explicit_unused_bits(self): + der = encode_bitstring(b"\xff\xf0", 4) + + self.assertEqual(der, b"\x03\x03\x04\xff\xf0") + + def test_empty_string(self): + self.assertEqual(encode_bitstring(b"", 0), b"\x03\x01\x00") + + def test_invalid_unused_count(self): + with self.assertRaises(ValueError): + encode_bitstring(b"\xff\x00", 8) + + def test_invalid_unused_with_empty_string(self): + with self.assertRaises(ValueError): + encode_bitstring(b"", 1) + + def test_non_zero_padding_bits(self): + with self.assertRaises(ValueError): + encode_bitstring(b"\xff", 2) + + +class TestRemoveBitstring(unittest.TestCase): + def test_old_call_convention(self): + """This is the old way to call the function.""" + warnings.simplefilter("always") + with pytest.warns(DeprecationWarning) as warns: + bits, rest = remove_bitstring(b"\x03\x02\x00\xff") + + self.assertEqual(len(warns), 1) + self.assertIn( + "expect_unused= needs to be specified", warns[0].message.args[0] + ) + + self.assertEqual(bits, b"\x00\xff") + self.assertEqual(rest, b"") + + def test_new_call_convention(self): + # make sure no warnings are raised + with warnings.catch_warnings(): + warnings.simplefilter("error") + bits, rest = remove_bitstring(b"\x03\x02\x00\xff", 0) + + self.assertEqual(bits, b"\xff") + self.assertEqual(rest, b"") + + def test_implicit_unexpected_unused(self): + # make sure no warnings are raised + with warnings.catch_warnings(): + warnings.simplefilter("error") + bits, rest = remove_bitstring(b"\x03\x02\x00\xff", None) + + self.assertEqual(bits, (b"\xff", 0)) + self.assertEqual(rest, b"") + + def test_with_padding(self): + ret, rest = remove_bitstring(b"\x03\x02\x04\xf0", None) + + self.assertEqual(ret, (b"\xf0", 4)) + self.assertEqual(rest, b"") + + def test_not_a_bitstring(self): + with self.assertRaises(UnexpectedDER): + remove_bitstring(b"\x02\x02\x00\xff", None) + + def test_empty_encoding(self): + with self.assertRaises(UnexpectedDER): + remove_bitstring(b"\x03\x00", None) + + def test_empty_string(self): + with self.assertRaises(UnexpectedDER): + remove_bitstring(b"", None) + + def test_no_length(self): + with self.assertRaises(UnexpectedDER): + remove_bitstring(b"\x03", None) + + def test_unexpected_number_of_unused_bits(self): + with self.assertRaises(UnexpectedDER): + remove_bitstring(b"\x03\x02\x00\xff", 1) + + def test_invalid_encoding_of_unused_bits(self): + with self.assertRaises(UnexpectedDER): + remove_bitstring(b"\x03\x03\x08\xff\x00", None) + + def test_invalid_encoding_of_empty_string(self): + with self.assertRaises(UnexpectedDER): + remove_bitstring(b"\x03\x01\x01", None) + + def test_invalid_padding_bits(self): + with self.assertRaises(UnexpectedDER): + remove_bitstring(b"\x03\x02\x01\xff", None) + + +class TestStrIdxAsInt(unittest.TestCase): + def test_str(self): + self.assertEqual(115, str_idx_as_int("str", 0)) + + def test_bytes(self): + self.assertEqual(115, str_idx_as_int(b"str", 0)) + + def test_bytearray(self): + self.assertEqual(115, str_idx_as_int(bytearray(b"str"), 0)) + + +class TestEncodeOid(unittest.TestCase): + def test_pub_key_oid(self): + oid_ecPublicKey = encode_oid(1, 2, 840, 10045, 2, 1) + self.assertEqual(hexlify(oid_ecPublicKey), b"06072a8648ce3d0201") + + def test_nist224p_oid(self): + self.assertEqual(hexlify(NIST224p.encoded_oid), b"06052b81040021") + + def test_nist256p_oid(self): + self.assertEqual( + hexlify(NIST256p.encoded_oid), b"06082a8648ce3d030107" + ) + + def test_large_second_subid(self): + # from X.690, section 8.19.5 + oid = encode_oid(2, 999, 3) + self.assertEqual(oid, b"\x06\x03\x88\x37\x03") + + def test_with_two_subids(self): + oid = encode_oid(2, 999) + self.assertEqual(oid, b"\x06\x02\x88\x37") + + def test_zero_zero(self): + oid = encode_oid(0, 0) + self.assertEqual(oid, b"\x06\x01\x00") + + def test_with_wrong_types(self): + with self.assertRaises((TypeError, AssertionError)): + encode_oid(0, None) + + def test_with_small_first_large_second(self): + with self.assertRaises(AssertionError): + encode_oid(1, 40) + + def test_small_first_max_second(self): + oid = encode_oid(1, 39) + self.assertEqual(oid, b"\x06\x01\x4f") + + def test_with_invalid_first(self): + with self.assertRaises(AssertionError): + encode_oid(3, 39) + + +class TestRemoveObject(unittest.TestCase): + @classmethod + def setUpClass(cls): + cls.oid_ecPublicKey = encode_oid(1, 2, 840, 10045, 2, 1) + + def test_pub_key_oid(self): + oid, rest = remove_object(self.oid_ecPublicKey) + self.assertEqual(rest, b"") + self.assertEqual(oid, (1, 2, 840, 10045, 2, 1)) + + def test_with_extra_bytes(self): + oid, rest = remove_object(self.oid_ecPublicKey + b"more") + self.assertEqual(rest, b"more") + self.assertEqual(oid, (1, 2, 840, 10045, 2, 1)) + + def test_with_large_second_subid(self): + # from X.690, section 8.19.5 + oid, rest = remove_object(b"\x06\x03\x88\x37\x03") + self.assertEqual(rest, b"") + self.assertEqual(oid, (2, 999, 3)) + + def test_with_padded_first_subid(self): + with self.assertRaises(UnexpectedDER): + remove_object(b"\x06\x02\x80\x00") + + def test_with_padded_second_subid(self): + with self.assertRaises(UnexpectedDER): + remove_object(b"\x06\x04\x88\x37\x80\x01") + + def test_with_missing_last_byte_of_multi_byte(self): + with self.assertRaises(UnexpectedDER): + remove_object(b"\x06\x03\x88\x37\x83") + + def test_with_two_subids(self): + oid, rest = remove_object(b"\x06\x02\x88\x37") + self.assertEqual(rest, b"") + self.assertEqual(oid, (2, 999)) + + def test_zero_zero(self): + oid, rest = remove_object(b"\x06\x01\x00") + self.assertEqual(rest, b"") + self.assertEqual(oid, (0, 0)) + + def test_empty_string(self): + with self.assertRaises(UnexpectedDER): + remove_object(b"") + + def test_missing_length(self): + with self.assertRaises(UnexpectedDER): + remove_object(b"\x06") + + def test_empty_oid(self): + with self.assertRaises(UnexpectedDER): + remove_object(b"\x06\x00") + + def test_empty_oid_overflow(self): + with self.assertRaises(UnexpectedDER): + remove_object(b"\x06\x01") + + def test_with_wrong_type(self): + with self.assertRaises(UnexpectedDER): + remove_object(b"\x04\x02\x88\x37") + + def test_with_too_long_length(self): + with self.assertRaises(UnexpectedDER): + remove_object(b"\x06\x03\x88\x37") + + +class TestRemoveConstructed(unittest.TestCase): + def test_simple(self): + data = b"\xa1\x02\xff\xaa" + + tag, body, rest = remove_constructed(data) + + self.assertEqual(tag, 0x01) + self.assertEqual(body, b"\xff\xaa") + self.assertEqual(rest, b"") + + def test_with_malformed_tag(self): + data = b"\x01\x02\xff\xaa" + + with self.assertRaises(UnexpectedDER) as e: + remove_constructed(data) + + self.assertIn("constructed tag", str(e.exception)) + + +class TestRemoveOctetString(unittest.TestCase): + def test_simple(self): + data = b"\x04\x03\xaa\xbb\xcc" + body, rest = remove_octet_string(data) + self.assertEqual(body, b"\xaa\xbb\xcc") + self.assertEqual(rest, b"") + + def test_with_malformed_tag(self): + data = b"\x03\x03\xaa\xbb\xcc" + with self.assertRaises(UnexpectedDER) as e: + remove_octet_string(data) + + self.assertIn("octetstring", str(e.exception)) + + +class TestRemoveSequence(unittest.TestCase): + def test_simple(self): + data = b"\x30\x02\xff\xaa" + body, rest = remove_sequence(data) + self.assertEqual(body, b"\xff\xaa") + self.assertEqual(rest, b"") + + def test_with_empty_string(self): + with self.assertRaises(UnexpectedDER) as e: + remove_sequence(b"") + + self.assertIn("Empty string", str(e.exception)) + + def test_with_wrong_tag(self): + data = b"\x20\x02\xff\xaa" + + with self.assertRaises(UnexpectedDER) as e: + remove_sequence(data) + + self.assertIn("wanted type 'sequence'", str(e.exception)) + + def test_with_wrong_length(self): + data = b"\x30\x03\xff\xaa" + + with self.assertRaises(UnexpectedDER) as e: + remove_sequence(data) + + self.assertIn("Length longer", str(e.exception)) + + +@st.composite +def st_oid(draw, max_value=2**512, max_size=50): + """ + Hypothesis strategy that returns valid OBJECT IDENTIFIERs as tuples + + :param max_value: maximum value of any single sub-identifier + :param max_size: maximum length of the generated OID + """ + first = draw(st.integers(min_value=0, max_value=2)) + if first < 2: + second = draw(st.integers(min_value=0, max_value=39)) + else: + second = draw(st.integers(min_value=0, max_value=max_value)) + rest = draw( + st.lists( + st.integers(min_value=0, max_value=max_value), max_size=max_size + ) + ) + return (first, second) + tuple(rest) + + +HYP_SETTINGS = {} + + +if "--fast" in sys.argv: # pragma: no cover + HYP_SETTINGS["max_examples"] = 2 + + +@settings(**HYP_SETTINGS) +@given(st_oid()) +def test_oids(ids): + encoded_oid = encode_oid(*ids) + decoded_oid, rest = remove_object(encoded_oid) + assert rest == b"" + assert decoded_oid == ids diff --git a/code/.venv/lib/python3.12/site-packages/ecdsa/test_ecdh.py b/code/.venv/lib/python3.12/site-packages/ecdsa/test_ecdh.py new file mode 100644 index 0000000..cb22580 --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/ecdsa/test_ecdh.py @@ -0,0 +1,449 @@ +import os +import sys +import shutil +import subprocess +import pytest +from binascii import unhexlify + +try: + import unittest2 as unittest +except ImportError: + import unittest + +from .curves import ( + NIST192p, + NIST224p, + NIST256p, + NIST384p, + NIST521p, + BRAINPOOLP160r1, + SECP112r2, + SECP128r1, +) +from .curves import curves +from .ecdh import ( + ECDH, + InvalidCurveError, + InvalidSharedSecretError, + NoKeyError, + NoCurveError, +) +from .keys import SigningKey, VerifyingKey +from .ellipticcurve import CurveEdTw + + +if "--fast" in sys.argv: # pragma: no cover + curves = [SECP112r2, SECP128r1] + + +@pytest.mark.parametrize( + "vcurve", + curves, + ids=[curve.name for curve in curves], +) +def test_ecdh_each(vcurve): + if isinstance(vcurve.curve, CurveEdTw): + pytest.skip("ECDH is not supported for Edwards curves") + ecdh1 = ECDH(curve=vcurve) + ecdh2 = ECDH(curve=vcurve) + + ecdh2.generate_private_key() + ecdh1.load_received_public_key(ecdh2.get_public_key()) + ecdh2.load_received_public_key(ecdh1.generate_private_key()) + + secret1 = ecdh1.generate_sharedsecret_bytes() + secret2 = ecdh2.generate_sharedsecret_bytes() + assert secret1 == secret2 + + +def test_ecdh_both_keys_present(): + key1 = SigningKey.generate(BRAINPOOLP160r1) + key2 = SigningKey.generate(BRAINPOOLP160r1) + + ecdh1 = ECDH(BRAINPOOLP160r1, key1, key2.verifying_key) + ecdh2 = ECDH(private_key=key2, public_key=key1.verifying_key) + + secret1 = ecdh1.generate_sharedsecret_bytes() + secret2 = ecdh2.generate_sharedsecret_bytes() + + assert secret1 == secret2 + + +def test_ecdh_no_public_key(): + ecdh1 = ECDH(curve=NIST192p) + + with pytest.raises(NoKeyError): + ecdh1.generate_sharedsecret_bytes() + + ecdh1.generate_private_key() + + with pytest.raises(NoKeyError): + ecdh1.generate_sharedsecret_bytes() + + +class TestECDH(unittest.TestCase): + def test_load_key_from_wrong_curve(self): + ecdh1 = ECDH() + ecdh1.set_curve(NIST192p) + + key1 = SigningKey.generate(BRAINPOOLP160r1) + + with self.assertRaises(InvalidCurveError) as e: + ecdh1.load_private_key(key1) + + self.assertIn("Curve mismatch", str(e.exception)) + + def test_generate_without_curve(self): + ecdh1 = ECDH() + + with self.assertRaises(NoCurveError) as e: + ecdh1.generate_private_key() + + self.assertIn("Curve must be set", str(e.exception)) + + def test_load_bytes_without_curve_set(self): + ecdh1 = ECDH() + + with self.assertRaises(NoCurveError) as e: + ecdh1.load_private_key_bytes(b"\x01" * 32) + + self.assertIn("Curve must be set", str(e.exception)) + + def test_set_curve_from_received_public_key(self): + ecdh1 = ECDH() + + key1 = SigningKey.generate(BRAINPOOLP160r1) + + ecdh1.load_received_public_key(key1.verifying_key) + + self.assertEqual(ecdh1.curve, BRAINPOOLP160r1) + + +def test_ecdh_wrong_public_key_curve(): + ecdh1 = ECDH(curve=NIST192p) + ecdh1.generate_private_key() + ecdh2 = ECDH(curve=NIST256p) + ecdh2.generate_private_key() + + with pytest.raises(InvalidCurveError): + ecdh1.load_received_public_key(ecdh2.get_public_key()) + + with pytest.raises(InvalidCurveError): + ecdh2.load_received_public_key(ecdh1.get_public_key()) + + ecdh1.public_key = ecdh2.get_public_key() + ecdh2.public_key = ecdh1.get_public_key() + + with pytest.raises(InvalidCurveError): + ecdh1.generate_sharedsecret_bytes() + + with pytest.raises(InvalidCurveError): + ecdh2.generate_sharedsecret_bytes() + + +def test_ecdh_invalid_shared_secret_curve(): + ecdh1 = ECDH(curve=NIST256p) + ecdh1.generate_private_key() + + ecdh1.load_received_public_key( + SigningKey.generate(NIST256p).get_verifying_key() + ) + + ecdh1.private_key.privkey.secret_multiplier = ecdh1.private_key.curve.order + + with pytest.raises(InvalidSharedSecretError): + ecdh1.generate_sharedsecret_bytes() + + +# https://github.com/scogliani/ecc-test-vectors/blob/master/ecdh_kat/secp192r1.txt +# https://github.com/scogliani/ecc-test-vectors/blob/master/ecdh_kat/secp256r1.txt +# https://github.com/coruus/nist-testvectors/blob/master/csrc.nist.gov/groups/STM/cavp/documents/components/ecccdhtestvectors/KAS_ECC_CDH_PrimitiveTest.txt +@pytest.mark.parametrize( + "curve,privatekey,pubkey,secret", + [ + pytest.param( + NIST192p, + "f17d3fea367b74d340851ca4270dcb24c271f445bed9d527", + "42ea6dd9969dd2a61fea1aac7f8e98edcc896c6e55857cc0" + "dfbe5d7c61fac88b11811bde328e8a0d12bf01a9d204b523", + "803d8ab2e5b6e6fca715737c3a82f7ce3c783124f6d51cd0", + id="NIST192p-1", + ), + pytest.param( + NIST192p, + "56e853349d96fe4c442448dacb7cf92bb7a95dcf574a9bd5", + "deb5712fa027ac8d2f22c455ccb73a91e17b6512b5e030e7" + "7e2690a02cc9b28708431a29fb54b87b1f0c14e011ac2125", + "c208847568b98835d7312cef1f97f7aa298283152313c29d", + id="NIST192p-2", + ), + pytest.param( + NIST192p, + "c6ef61fe12e80bf56f2d3f7d0bb757394519906d55500949", + "4edaa8efc5a0f40f843663ec5815e7762dddc008e663c20f" + "0a9f8dc67a3e60ef6d64b522185d03df1fc0adfd42478279", + "87229107047a3b611920d6e3b2c0c89bea4f49412260b8dd", + id="NIST192p-3", + ), + pytest.param( + NIST192p, + "e6747b9c23ba7044f38ff7e62c35e4038920f5a0163d3cda", + "8887c276edeed3e9e866b46d58d895c73fbd80b63e382e88" + "04c5097ba6645e16206cfb70f7052655947dd44a17f1f9d5", + "eec0bed8fc55e1feddc82158fd6dc0d48a4d796aaf47d46c", + id="NIST192p-4", + ), + pytest.param( + NIST192p, + "beabedd0154a1afcfc85d52181c10f5eb47adc51f655047d", + "0d045f30254adc1fcefa8a5b1f31bf4e739dd327cd18d594" + "542c314e41427c08278a08ce8d7305f3b5b849c72d8aff73", + "716e743b1b37a2cd8479f0a3d5a74c10ba2599be18d7e2f4", + id="NIST192p-5", + ), + pytest.param( + NIST192p, + "cf70354226667321d6e2baf40999e2fd74c7a0f793fa8699", + "fb35ca20d2e96665c51b98e8f6eb3d79113508d8bccd4516" + "368eec0d5bfb847721df6aaff0e5d48c444f74bf9cd8a5a7", + "f67053b934459985a315cb017bf0302891798d45d0e19508", + id="NIST192p-6", + ), + pytest.param( + NIST224p, + "8346a60fc6f293ca5a0d2af68ba71d1dd389e5e40837942df3e43cbd", + "af33cd0629bc7e996320a3f40368f74de8704fa37b8fab69abaae280" + "882092ccbba7930f419a8a4f9bb16978bbc3838729992559a6f2e2d7", + "7d96f9a3bd3c05cf5cc37feb8b9d5209d5c2597464dec3e9983743e8", + id="NIST224p", + ), + pytest.param( + NIST256p, + "7d7dc5f71eb29ddaf80d6214632eeae03d9058af1fb6d22ed80badb62bc1a534", + "700c48f77f56584c5cc632ca65640db91b6bacce3a4df6b42ce7cc838833d287" + "db71e509e3fd9b060ddb20ba5c51dcc5948d46fbf640dfe0441782cab85fa4ac", + "46fc62106420ff012e54a434fbdd2d25ccc5852060561e68040dd7778997bd7b", + id="NIST256p-1", + ), + pytest.param( + NIST256p, + "38f65d6dce47676044d58ce5139582d568f64bb16098d179dbab07741dd5caf5", + "809f04289c64348c01515eb03d5ce7ac1a8cb9498f5caa50197e58d43a86a7ae" + "b29d84e811197f25eba8f5194092cb6ff440e26d4421011372461f579271cda3", + "057d636096cb80b67a8c038c890e887d1adfa4195e9b3ce241c8a778c59cda67", + id="NIST256p-2", + ), + pytest.param( + NIST256p, + "1accfaf1b97712b85a6f54b148985a1bdc4c9bec0bd258cad4b3d603f49f32c8", + "a2339c12d4a03c33546de533268b4ad667debf458b464d77443636440ee7fec3" + "ef48a3ab26e20220bcda2c1851076839dae88eae962869a497bf73cb66faf536", + "2d457b78b4614132477618a5b077965ec90730a8c81a1c75d6d4ec68005d67ec", + id="NIST256p-3", + ), + pytest.param( + NIST256p, + "207c43a79bfee03db6f4b944f53d2fb76cc49ef1c9c4d34d51b6c65c4db6932d", + "df3989b9fa55495719b3cf46dccd28b5153f7808191dd518eff0c3cff2b705ed" + "422294ff46003429d739a33206c8752552c8ba54a270defc06e221e0feaf6ac4", + "96441259534b80f6aee3d287a6bb17b5094dd4277d9e294f8fe73e48bf2a0024", + id="NIST256p-4", + ), + pytest.param( + NIST256p, + "59137e38152350b195c9718d39673d519838055ad908dd4757152fd8255c09bf", + "41192d2813e79561e6a1d6f53c8bc1a433a199c835e141b05a74a97b0faeb922" + "1af98cc45e98a7e041b01cf35f462b7562281351c8ebf3ffa02e33a0722a1328", + "19d44c8d63e8e8dd12c22a87b8cd4ece27acdde04dbf47f7f27537a6999a8e62", + id="NIST256p-5", + ), + pytest.param( + NIST256p, + "f5f8e0174610a661277979b58ce5c90fee6c9b3bb346a90a7196255e40b132ef", + "33e82092a0f1fb38f5649d5867fba28b503172b7035574bf8e5b7100a3052792" + "f2cf6b601e0a05945e335550bf648d782f46186c772c0f20d3cd0d6b8ca14b2f", + "664e45d5bba4ac931cd65d52017e4be9b19a515f669bea4703542a2c525cd3d3", + id="NIST256p-6", + ), + pytest.param( + NIST384p, + "3cc3122a68f0d95027ad38c067916ba0eb8c38894d22e1b1" + "5618b6818a661774ad463b205da88cf699ab4d43c9cf98a1", + "a7c76b970c3b5fe8b05d2838ae04ab47697b9eaf52e76459" + "2efda27fe7513272734466b400091adbf2d68c58e0c50066" + "ac68f19f2e1cb879aed43a9969b91a0839c4c38a49749b66" + "1efedf243451915ed0905a32b060992b468c64766fc8437a", + "5f9d29dc5e31a163060356213669c8ce132e22f57c9a04f4" + "0ba7fcead493b457e5621e766c40a2e3d4d6a04b25e533f1", + id="NIST384p", + ), + pytest.param( + NIST521p, + "017eecc07ab4b329068fba65e56a1f8890aa935e57134ae0ffcce802735151f4ea" + "c6564f6ee9974c5e6887a1fefee5743ae2241bfeb95d5ce31ddcb6f9edb4d6fc47", + "00685a48e86c79f0f0875f7bc18d25eb5fc8c0b07e5da4f4370f3a949034085433" + "4b1e1b87fa395464c60626124a4e70d0f785601d37c09870ebf176666877a2046d" + "01ba52c56fc8776d9e8f5db4f0cc27636d0b741bbe05400697942e80b739884a83" + "bde99e0f6716939e632bc8986fa18dccd443a348b6c3e522497955a4f3c302f676", + "005fc70477c3e63bc3954bd0df3ea0d1f41ee21746ed95fc5e1fdf90930d5e1366" + "72d72cc770742d1711c3c3a4c334a0ad9759436a4d3c5bf6e74b9578fac148c831", + id="NIST521p", + ), + ], +) +def test_ecdh_NIST(curve, privatekey, pubkey, secret): + ecdh = ECDH(curve=curve) + ecdh.load_private_key_bytes(unhexlify(privatekey)) + ecdh.load_received_public_key_bytes(unhexlify(pubkey)) + + sharedsecret = ecdh.generate_sharedsecret_bytes() + + assert sharedsecret == unhexlify(secret) + + +pem_local_private_key = ( + "-----BEGIN EC PRIVATE KEY-----\n" + "MF8CAQEEGF7IQgvW75JSqULpiQQ8op9WH6Uldw6xxaAKBggqhkjOPQMBAaE0AzIA\n" + "BLiBd9CE7xf15FY5QIAoNg+fWbSk1yZOYtoGUdzkejWkxbRc9RWTQjqLVXucIJnz\n" + "bA==\n" + "-----END EC PRIVATE KEY-----\n" +) +der_local_private_key = ( + "305f02010104185ec8420bd6ef9252a942e989043ca29f561fa525770eb1c5a00a06082a864" + "8ce3d030101a13403320004b88177d084ef17f5e45639408028360f9f59b4a4d7264e62da06" + "51dce47a35a4c5b45cf51593423a8b557b9c2099f36c" +) +pem_remote_public_key = ( + "-----BEGIN PUBLIC KEY-----\n" + "MEkwEwYHKoZIzj0CAQYIKoZIzj0DAQEDMgAEuIF30ITvF/XkVjlAgCg2D59ZtKTX\n" + "Jk5i2gZR3OR6NaTFtFz1FZNCOotVe5wgmfNs\n" + "-----END PUBLIC KEY-----\n" +) +der_remote_public_key = ( + "3049301306072a8648ce3d020106082a8648ce3d03010103320004b88177d084ef17f5e4563" + "9408028360f9f59b4a4d7264e62da0651dce47a35a4c5b45cf51593423a8b557b9c2099f36c" +) +gshared_secret = "8f457e34982478d1c34b9cd2d0c15911b72dd60d869e2cea" + + +def test_ecdh_pem(): + ecdh = ECDH() + ecdh.load_private_key_pem(pem_local_private_key) + ecdh.load_received_public_key_pem(pem_remote_public_key) + + sharedsecret = ecdh.generate_sharedsecret_bytes() + + assert sharedsecret == unhexlify(gshared_secret) + + +def test_ecdh_der(): + ecdh = ECDH() + ecdh.load_private_key_der(unhexlify(der_local_private_key)) + ecdh.load_received_public_key_der(unhexlify(der_remote_public_key)) + + sharedsecret = ecdh.generate_sharedsecret_bytes() + + assert sharedsecret == unhexlify(gshared_secret) + + +# Exception classes used by run_openssl. +class RunOpenSslError(Exception): + pass + + +def run_openssl(cmd): + OPENSSL = "openssl" + p = subprocess.Popen( + [OPENSSL] + cmd.split(), + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + ) + stdout, ignored = p.communicate() + if p.returncode != 0: + raise RunOpenSslError( + "cmd '%s %s' failed: rc=%s, stdout/err was %s" + % (OPENSSL, cmd, p.returncode, stdout) + ) + return stdout.decode() + + +OPENSSL_SUPPORTED_CURVES = set( + c.split(":")[0].strip() + for c in run_openssl("ecparam -list_curves").split("\n") +) + + +@pytest.mark.slow +@pytest.mark.parametrize( + "vcurve", + curves, + ids=[curve.name for curve in curves], +) +def test_ecdh_with_openssl(vcurve): + if isinstance(vcurve.curve, CurveEdTw): + pytest.skip("Edwards curves are not supported for ECDH") + + assert vcurve.openssl_name + + if vcurve.openssl_name not in OPENSSL_SUPPORTED_CURVES: + pytest.skip("system openssl does not support " + vcurve.openssl_name) + + try: + hlp = run_openssl("pkeyutl -help") + if hlp.find("-derive") == 0: # pragma: no cover + pytest.skip("system openssl does not support `pkeyutl -derive`") + except RunOpenSslError: # pragma: no cover + pytest.skip("system openssl could not be executed") + + if os.path.isdir("t"): # pragma: no branch + shutil.rmtree("t") + os.mkdir("t") + run_openssl( + "ecparam -name %s -genkey -out t/privkey1.pem" % vcurve.openssl_name + ) + run_openssl( + "ecparam -name %s -genkey -out t/privkey2.pem" % vcurve.openssl_name + ) + run_openssl("ec -in t/privkey1.pem -pubout -out t/pubkey1.pem") + + ecdh1 = ECDH(curve=vcurve) + ecdh2 = ECDH(curve=vcurve) + with open("t/privkey1.pem") as e: + key = e.read() + ecdh1.load_private_key_pem(key) + with open("t/privkey2.pem") as e: + key = e.read() + ecdh2.load_private_key_pem(key) + + with open("t/pubkey1.pem") as e: + key = e.read() + vk1 = VerifyingKey.from_pem(key) + assert vk1.to_string() == ecdh1.get_public_key().to_string() + vk2 = ecdh2.get_public_key() + with open("t/pubkey2.pem", "wb") as e: + e.write(vk2.to_pem()) + + ecdh1.load_received_public_key(vk2) + ecdh2.load_received_public_key(vk1) + secret1 = ecdh1.generate_sharedsecret_bytes() + secret2 = ecdh2.generate_sharedsecret_bytes() + + assert secret1 == secret2 + + run_openssl( + "pkeyutl -derive -inkey t/privkey1.pem -peerkey t/pubkey2.pem -out t/secret1" + ) + run_openssl( + "pkeyutl -derive -inkey t/privkey2.pem -peerkey t/pubkey1.pem -out t/secret2" + ) + + with open("t/secret1", "rb") as e: + ssl_secret1 = e.read() + with open("t/secret1", "rb") as e: + ssl_secret2 = e.read() + + assert len(ssl_secret1) == vk1.curve.verifying_key_length // 2 + assert len(secret1) == vk1.curve.verifying_key_length // 2 + + assert ssl_secret1 == ssl_secret2 + assert secret1 == ssl_secret1 diff --git a/code/.venv/lib/python3.12/site-packages/ecdsa/test_ecdsa.py b/code/.venv/lib/python3.12/site-packages/ecdsa/test_ecdsa.py new file mode 100644 index 0000000..c1e2582 --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/ecdsa/test_ecdsa.py @@ -0,0 +1,694 @@ +from __future__ import print_function +import sys +import hypothesis.strategies as st +from hypothesis import given, settings, note, example + +try: + import unittest2 as unittest +except ImportError: + import unittest +import pytest +from .ecdsa import ( + Private_key, + Public_key, + Signature, + generator_192, + digest_integer, + ellipticcurve, + point_is_valid, + generator_224, + generator_256, + generator_384, + generator_521, + generator_secp256k1, + curve_192, + InvalidPointError, + curve_112r2, + generator_112r2, + int_to_string, +) +from .ellipticcurve import Point + + +HYP_SETTINGS = {} +# old hypothesis doesn't have the "deadline" setting +if sys.version_info > (2, 7): # pragma: no branch + # SEC521p is slow, allow long execution for it + HYP_SETTINGS["deadline"] = 5000 + + +class TestP192FromX9_62(unittest.TestCase): + """Check test vectors from X9.62""" + + @classmethod + def setUpClass(cls): + cls.d = 651056770906015076056810763456358567190100156695615665659 + cls.Q = cls.d * generator_192 + cls.k = 6140507067065001063065065565667405560006161556565665656654 + cls.R = cls.k * generator_192 + + cls.msg = 968236873715988614170569073515315707566766479517 + cls.pubk = Public_key(generator_192, generator_192 * cls.d) + cls.privk = Private_key(cls.pubk, cls.d) + cls.sig = cls.privk.sign(cls.msg, cls.k) + + def test_point_multiplication(self): + assert self.Q.x() == 0x62B12D60690CDCF330BABAB6E69763B471F994DD702D16A5 + + def test_point_multiplication_2(self): + assert self.R.x() == 0x885052380FF147B734C330C43D39B2C4A89F29B0F749FEAD + assert self.R.y() == 0x9CF9FA1CBEFEFB917747A3BB29C072B9289C2547884FD835 + + def test_mult_and_addition(self): + u1 = 2563697409189434185194736134579731015366492496392189760599 + u2 = 6266643813348617967186477710235785849136406323338782220568 + temp = u1 * generator_192 + u2 * self.Q + assert temp.x() == 0x885052380FF147B734C330C43D39B2C4A89F29B0F749FEAD + assert temp.y() == 0x9CF9FA1CBEFEFB917747A3BB29C072B9289C2547884FD835 + + def test_signature(self): + r, s = self.sig.r, self.sig.s + assert r == 3342403536405981729393488334694600415596881826869351677613 + assert s == 5735822328888155254683894997897571951568553642892029982342 + + def test_verification(self): + assert self.pubk.verifies(self.msg, self.sig) + + def test_rejection(self): + assert not self.pubk.verifies(self.msg - 1, self.sig) + + def test_verification_with_regular_point(self): + pubk = Public_key( + Point( + generator_192.curve(), + generator_192.x(), + generator_192.y(), + generator_192.order(), + ), + self.pubk.point, + ) + + assert pubk.verifies(self.msg, self.sig) + + +class TestPublicKey(unittest.TestCase): + def test_equality_public_keys(self): + gen = generator_192 + x = 0xC58D61F88D905293BCD4CD0080BCB1B7F811F2FFA41979F6 + y = 0x8804DC7A7C4C7F8B5D437F5156F3312CA7D6DE8A0E11867F + point = ellipticcurve.Point(gen.curve(), x, y) + pub_key1 = Public_key(gen, point) + pub_key2 = Public_key(gen, point) + self.assertEqual(pub_key1, pub_key2) + + def test_inequality_public_key(self): + gen = generator_192 + x1 = 0xC58D61F88D905293BCD4CD0080BCB1B7F811F2FFA41979F6 + y1 = 0x8804DC7A7C4C7F8B5D437F5156F3312CA7D6DE8A0E11867F + point1 = ellipticcurve.Point(gen.curve(), x1, y1) + + x2 = 0x6A223D00BD22C52833409A163E057E5B5DA1DEF2A197DD15 + y2 = 0x7B482604199367F1F303F9EF627F922F97023E90EAE08ABF + point2 = ellipticcurve.Point(gen.curve(), x2, y2) + + pub_key1 = Public_key(gen, point1) + pub_key2 = Public_key(gen, point2) + self.assertNotEqual(pub_key1, pub_key2) + + def test_inequality_different_curves(self): + gen = generator_192 + x1 = 0xC58D61F88D905293BCD4CD0080BCB1B7F811F2FFA41979F6 + y1 = 0x8804DC7A7C4C7F8B5D437F5156F3312CA7D6DE8A0E11867F + point1 = ellipticcurve.Point(gen.curve(), x1, y1) + + x2 = 0x722BA0FB6B8FC8898A4C6AB49E66 + y2 = 0x2B7344BB57A7ABC8CA0F1A398C7D + point2 = ellipticcurve.Point(generator_112r2.curve(), x2, y2) + + pub_key1 = Public_key(gen, point1) + pub_key2 = Public_key(generator_112r2, point2) + self.assertNotEqual(pub_key1, pub_key2) + + def test_inequality_public_key_not_implemented(self): + gen = generator_192 + x = 0xC58D61F88D905293BCD4CD0080BCB1B7F811F2FFA41979F6 + y = 0x8804DC7A7C4C7F8B5D437F5156F3312CA7D6DE8A0E11867F + point = ellipticcurve.Point(gen.curve(), x, y) + pub_key = Public_key(gen, point) + self.assertNotEqual(pub_key, None) + + def test_public_key_with_generator_without_order(self): + gen = ellipticcurve.PointJacobi( + generator_192.curve(), generator_192.x(), generator_192.y(), 1 + ) + + x = 0xC58D61F88D905293BCD4CD0080BCB1B7F811F2FFA41979F6 + y = 0x8804DC7A7C4C7F8B5D437F5156F3312CA7D6DE8A0E11867F + point = ellipticcurve.Point(gen.curve(), x, y) + + with self.assertRaises(InvalidPointError) as e: + Public_key(gen, point) + + self.assertIn("Generator point must have order", str(e.exception)) + + def test_public_point_on_curve_not_scalar_multiple_of_base_point(self): + x = 2 + y = 0xBE6AA4938EF7CFE6FE29595B6B00 + # we need a curve with cofactor != 1 + point = ellipticcurve.PointJacobi(curve_112r2, x, y, 1) + + self.assertTrue(curve_112r2.contains_point(x, y)) + + with self.assertRaises(InvalidPointError) as e: + Public_key(generator_112r2, point) + + self.assertIn("Generator point order", str(e.exception)) + + def test_point_is_valid_with_not_scalar_multiple_of_base_point(self): + x = 2 + y = 0xBE6AA4938EF7CFE6FE29595B6B00 + + self.assertFalse(point_is_valid(generator_112r2, x, y)) + + # the tests to verify the extensiveness of tests in ecdsa.ecdsa + # if PointJacobi gets modified to calculate the x and y mod p the tests + # below will need to use a fake/mock object + def test_invalid_point_x_negative(self): + pt = ellipticcurve.PointJacobi(curve_192, -1, 0, 1) + + with self.assertRaises(InvalidPointError) as e: + Public_key(generator_192, pt) + + self.assertIn("The public point has x or y", str(e.exception)) + + def test_invalid_point_x_equal_p(self): + pt = ellipticcurve.PointJacobi(curve_192, curve_192.p(), 0, 1) + + with self.assertRaises(InvalidPointError) as e: + Public_key(generator_192, pt) + + self.assertIn("The public point has x or y", str(e.exception)) + + def test_invalid_point_y_negative(self): + pt = ellipticcurve.PointJacobi(curve_192, 0, -1, 1) + + with self.assertRaises(InvalidPointError) as e: + Public_key(generator_192, pt) + + self.assertIn("The public point has x or y", str(e.exception)) + + def test_invalid_point_y_equal_p(self): + pt = ellipticcurve.PointJacobi(curve_192, 0, curve_192.p(), 1) + + with self.assertRaises(InvalidPointError) as e: + Public_key(generator_192, pt) + + self.assertIn("The public point has x or y", str(e.exception)) + + +class TestPublicKeyVerifies(unittest.TestCase): + # test all the different ways that a signature can be publicly invalid + @classmethod + def setUpClass(cls): + gen = generator_192 + x = 0xC58D61F88D905293BCD4CD0080BCB1B7F811F2FFA41979F6 + y = 0x8804DC7A7C4C7F8B5D437F5156F3312CA7D6DE8A0E11867F + point = ellipticcurve.Point(gen.curve(), x, y) + + cls.pub_key = Public_key(gen, point) + + def test_sig_with_r_zero(self): + sig = Signature(0, 1) + + self.assertFalse(self.pub_key.verifies(1, sig)) + + def test_sig_with_r_order(self): + sig = Signature(generator_192.order(), 1) + + self.assertFalse(self.pub_key.verifies(1, sig)) + + def test_sig_with_s_zero(self): + sig = Signature(1, 0) + + self.assertFalse(self.pub_key.verifies(1, sig)) + + def test_sig_with_s_order(self): + sig = Signature(1, generator_192.order()) + + self.assertFalse(self.pub_key.verifies(1, sig)) + + +class TestPrivateKey(unittest.TestCase): + @classmethod + def setUpClass(cls): + gen = generator_192 + x = 0xC58D61F88D905293BCD4CD0080BCB1B7F811F2FFA41979F6 + y = 0x8804DC7A7C4C7F8B5D437F5156F3312CA7D6DE8A0E11867F + point = ellipticcurve.Point(gen.curve(), x, y) + cls.pub_key = Public_key(gen, point) + + def test_equality_private_keys(self): + pr_key1 = Private_key(self.pub_key, 100) + pr_key2 = Private_key(self.pub_key, 100) + self.assertEqual(pr_key1, pr_key2) + + def test_inequality_private_keys(self): + pr_key1 = Private_key(self.pub_key, 100) + pr_key2 = Private_key(self.pub_key, 200) + self.assertNotEqual(pr_key1, pr_key2) + + def test_inequality_private_keys_not_implemented(self): + pr_key = Private_key(self.pub_key, 100) + self.assertNotEqual(pr_key, None) + + +# Testing point validity, as per ECDSAVS.pdf B.2.2: +P192_POINTS = [ + ( + generator_192, + 0xCD6D0F029A023E9AACA429615B8F577ABEE685D8257CC83A, + 0x00019C410987680E9FB6C0B6ECC01D9A2647C8BAE27721BACDFC, + False, + ), + ( + generator_192, + 0x00017F2FCE203639E9EAF9FB50B81FC32776B30E3B02AF16C73B, + 0x95DA95C5E72DD48E229D4748D4EEE658A9A54111B23B2ADB, + False, + ), + ( + generator_192, + 0x4F77F8BC7FCCBADD5760F4938746D5F253EE2168C1CF2792, + 0x000147156FF824D131629739817EDB197717C41AAB5C2A70F0F6, + False, + ), + ( + generator_192, + 0xC58D61F88D905293BCD4CD0080BCB1B7F811F2FFA41979F6, + 0x8804DC7A7C4C7F8B5D437F5156F3312CA7D6DE8A0E11867F, + True, + ), + ( + generator_192, + 0xCDF56C1AA3D8AFC53C521ADF3FFB96734A6A630A4A5B5A70, + 0x97C1C44A5FB229007B5EC5D25F7413D170068FFD023CAA4E, + True, + ), + ( + generator_192, + 0x89009C0DC361C81E99280C8E91DF578DF88CDF4B0CDEDCED, + 0x27BE44A529B7513E727251F128B34262A0FD4D8EC82377B9, + True, + ), + ( + generator_192, + 0x6A223D00BD22C52833409A163E057E5B5DA1DEF2A197DD15, + 0x7B482604199367F1F303F9EF627F922F97023E90EAE08ABF, + True, + ), + ( + generator_192, + 0x6DCCBDE75C0948C98DAB32EA0BC59FE125CF0FB1A3798EDA, + 0x0001171A3E0FA60CF3096F4E116B556198DE430E1FBD330C8835, + False, + ), + ( + generator_192, + 0xD266B39E1F491FC4ACBBBC7D098430931CFA66D55015AF12, + 0x193782EB909E391A3148B7764E6B234AA94E48D30A16DBB2, + False, + ), + ( + generator_192, + 0x9D6DDBCD439BAA0C6B80A654091680E462A7D1D3F1FFEB43, + 0x6AD8EFC4D133CCF167C44EB4691C80ABFFB9F82B932B8CAA, + False, + ), + ( + generator_192, + 0x146479D944E6BDA87E5B35818AA666A4C998A71F4E95EDBC, + 0xA86D6FE62BC8FBD88139693F842635F687F132255858E7F6, + False, + ), + ( + generator_192, + 0xE594D4A598046F3598243F50FD2C7BD7D380EDB055802253, + 0x509014C0C4D6B536E3CA750EC09066AF39B4C8616A53A923, + False, + ), +] + + +@pytest.mark.parametrize("generator,x,y,expected", P192_POINTS) +def test_point_validity(generator, x, y, expected): + """ + `generator` defines the curve; is `(x, y)` a point on + this curve? `expected` is True if the right answer is Yes. + """ + assert point_is_valid(generator, x, y) == expected + + +# Trying signature-verification tests from ECDSAVS.pdf B.2.4: +CURVE_192_KATS = [ + ( + generator_192, + int( + "0x84ce72aa8699df436059f052ac51b6398d2511e49631bcb7e71f89c499b9ee" + "425dfbc13a5f6d408471b054f2655617cbbaf7937b7c80cd8865cf02c8487d30" + "d2b0fbd8b2c4e102e16d828374bbc47b93852f212d5043c3ea720f086178ff79" + "8cc4f63f787b9c2e419efa033e7644ea7936f54462dc21a6c4580725f7f0e7d1" + "58", + 16, + ), + 0xD9DBFB332AA8E5FF091E8CE535857C37C73F6250FFB2E7AC, + 0x282102E364FEDED3AD15DDF968F88D8321AA268DD483EBC4, + 0x64DCA58A20787C488D11D6DD96313F1B766F2D8EFE122916, + 0x1ECBA28141E84AB4ECAD92F56720E2CC83EB3D22DEC72479, + True, + ), + ( + generator_192, + int( + "0x94bb5bacd5f8ea765810024db87f4224ad71362a3c28284b2b9f39fab86db1" + "2e8beb94aae899768229be8fdb6c4f12f28912bb604703a79ccff769c1607f5a" + "91450f30ba0460d359d9126cbd6296be6d9c4bb96c0ee74cbb44197c207f6db3" + "26ab6f5a659113a9034e54be7b041ced9dcf6458d7fb9cbfb2744d999f7dfd63" + "f4", + 16, + ), + 0x3E53EF8D3112AF3285C0E74842090712CD324832D4277AE7, + 0xCC75F8952D30AEC2CBB719FC6AA9934590B5D0FF5A83ADB7, + 0x8285261607283BA18F335026130BAB31840DCFD9C3E555AF, + 0x356D89E1B04541AFC9704A45E9C535CE4A50929E33D7E06C, + True, + ), + ( + generator_192, + int( + "0xf6227a8eeb34afed1621dcc89a91d72ea212cb2f476839d9b4243c66877911" + "b37b4ad6f4448792a7bbba76c63bdd63414b6facab7dc71c3396a73bd7ee14cd" + "d41a659c61c99b779cecf07bc51ab391aa3252386242b9853ea7da67fd768d30" + "3f1b9b513d401565b6f1eb722dfdb96b519fe4f9bd5de67ae131e64b40e78c42" + "dd", + 16, + ), + 0x16335DBE95F8E8254A4E04575D736BEFB258B8657F773CB7, + 0x421B13379C59BC9DCE38A1099CA79BBD06D647C7F6242336, + 0x4141BD5D64EA36C5B0BD21EF28C02DA216ED9D04522B1E91, + 0x159A6AA852BCC579E821B7BB0994C0861FB08280C38DAA09, + False, + ), + ( + generator_192, + int( + "0x16b5f93afd0d02246f662761ed8e0dd9504681ed02a253006eb36736b56309" + "7ba39f81c8e1bce7a16c1339e345efabbc6baa3efb0612948ae51103382a8ee8" + "bc448e3ef71e9f6f7a9676694831d7f5dd0db5446f179bcb737d4a526367a447" + "bfe2c857521c7f40b6d7d7e01a180d92431fb0bbd29c04a0c420a57b3ed26ccd" + "8a", + 16, + ), + 0xFD14CDF1607F5EFB7B1793037B15BDF4BAA6F7C16341AB0B, + 0x83FA0795CC6C4795B9016DAC928FD6BAC32F3229A96312C4, + 0x8DFDB832951E0167C5D762A473C0416C5C15BC1195667DC1, + 0x1720288A2DC13FA1EC78F763F8FE2FF7354A7E6FDDE44520, + False, + ), + ( + generator_192, + int( + "0x08a2024b61b79d260e3bb43ef15659aec89e5b560199bc82cf7c65c77d3919" + "2e03b9a895d766655105edd9188242b91fbde4167f7862d4ddd61e5d4ab55196" + "683d4f13ceb90d87aea6e07eb50a874e33086c4a7cb0273a8e1c4408f4b846bc" + "eae1ebaac1b2b2ea851a9b09de322efe34cebe601653efd6ddc876ce8c2f2072" + "fb", + 16, + ), + 0x674F941DC1A1F8B763C9334D726172D527B90CA324DB8828, + 0x65ADFA32E8B236CB33A3E84CF59BFB9417AE7E8EDE57A7FF, + 0x9508B9FDD7DAF0D8126F9E2BC5A35E4C6D800B5B804D7796, + 0x36F2BF6B21B987C77B53BB801B3435A577E3D493744BFAB0, + False, + ), + ( + generator_192, + int( + "0x1843aba74b0789d4ac6b0b8923848023a644a7b70afa23b1191829bbe4397c" + "e15b629bf21a8838298653ed0c19222b95fa4f7390d1b4c844d96e645537e0aa" + "e98afb5c0ac3bd0e4c37f8daaff25556c64e98c319c52687c904c4de7240a1cc" + "55cd9756b7edaef184e6e23b385726e9ffcba8001b8f574987c1a3fedaaa83ca" + "6d", + 16, + ), + 0x10ECCA1AAD7220B56A62008B35170BFD5E35885C4014A19F, + 0x04EB61984C6C12ADE3BC47F3C629ECE7AA0A033B9948D686, + 0x82BFA4E82C0DFE9274169B86694E76CE993FD83B5C60F325, + 0xA97685676C59A65DBDE002FE9D613431FB183E8006D05633, + False, + ), + ( + generator_192, + int( + "0x5a478f4084ddd1a7fea038aa9732a822106385797d02311aeef4d0264f824f" + "698df7a48cfb6b578cf3da416bc0799425bb491be5b5ecc37995b85b03420a98" + "f2c4dc5c31a69a379e9e322fbe706bbcaf0f77175e05cbb4fa162e0da82010a2" + "78461e3e974d137bc746d1880d6eb02aa95216014b37480d84b87f717bb13f76" + "e1", + 16, + ), + 0x6636653CB5B894CA65C448277B29DA3AD101C4C2300F7C04, + 0xFDF1CBB3FC3FD6A4F890B59E554544175FA77DBDBEB656C1, + 0xEAC2DDECDDFB79931A9C3D49C08DE0645C783A24CB365E1C, + 0x3549FEE3CFA7E5F93BC47D92D8BA100E881A2A93C22F8D50, + False, + ), + ( + generator_192, + int( + "0xc598774259a058fa65212ac57eaa4f52240e629ef4c310722088292d1d4af6" + "c39b49ce06ba77e4247b20637174d0bd67c9723feb57b5ead232b47ea452d5d7" + "a089f17c00b8b6767e434a5e16c231ba0efa718a340bf41d67ea2d295812ff1b" + "9277daacb8bc27b50ea5e6443bcf95ef4e9f5468fe78485236313d53d1c68f6b" + "a2", + 16, + ), + 0xA82BD718D01D354001148CD5F69B9EBF38FF6F21898F8AAA, + 0xE67CEEDE07FC2EBFAFD62462A51E4B6C6B3D5B537B7CAF3E, + 0x4D292486C620C3DE20856E57D3BB72FCDE4A73AD26376955, + 0xA85289591A6081D5728825520E62FF1C64F94235C04C7F95, + False, + ), + ( + generator_192, + int( + "0xca98ed9db081a07b7557f24ced6c7b9891269a95d2026747add9e9eb80638a" + "961cf9c71a1b9f2c29744180bd4c3d3db60f2243c5c0b7cc8a8d40a3f9a7fc91" + "0250f2187136ee6413ffc67f1a25e1c4c204fa9635312252ac0e0481d89b6d53" + "808f0c496ba87631803f6c572c1f61fa049737fdacce4adff757afed4f05beb6" + "58", + 16, + ), + 0x7D3B016B57758B160C4FCA73D48DF07AE3B6B30225126C2F, + 0x4AF3790D9775742BDE46F8DA876711BE1B65244B2B39E7EC, + 0x95F778F5F656511A5AB49A5D69DDD0929563C29CBC3A9E62, + 0x75C87FC358C251B4C83D2DD979FAAD496B539F9F2EE7A289, + False, + ), + ( + generator_192, + int( + "0x31dd9a54c8338bea06b87eca813d555ad1850fac9742ef0bbe40dad400e102" + "88acc9c11ea7dac79eb16378ebea9490e09536099f1b993e2653cd50240014c9" + "0a9c987f64545abc6a536b9bd2435eb5e911fdfde2f13be96ea36ad38df4ae9e" + "a387b29cced599af777338af2794820c9cce43b51d2112380a35802ab7e396c9" + "7a", + 16, + ), + 0x9362F28C4EF96453D8A2F849F21E881CD7566887DA8BEB4A, + 0xE64D26D8D74C48A024AE85D982EE74CD16046F4EE5333905, + 0xF3923476A296C88287E8DE914B0B324AD5A963319A4FE73B, + 0xF0BAEED7624ED00D15244D8BA2AEDE085517DBDEC8AC65F5, + True, + ), + ( + generator_192, + int( + "0xb2b94e4432267c92f9fdb9dc6040c95ffa477652761290d3c7de312283f645" + "0d89cc4aabe748554dfb6056b2d8e99c7aeaad9cdddebdee9dbc099839562d90" + "64e68e7bb5f3a6bba0749ca9a538181fc785553a4000785d73cc207922f63e8c" + "e1112768cb1de7b673aed83a1e4a74592f1268d8e2a4e9e63d414b5d442bd045" + "6d", + 16, + ), + 0xCC6FC032A846AAAC25533EB033522824F94E670FA997ECEF, + 0xE25463EF77A029ECCDA8B294FD63DD694E38D223D30862F1, + 0x066B1D07F3A40E679B620EDA7F550842A35C18B80C5EBE06, + 0xA0B0FB201E8F2DF65E2C4508EF303BDC90D934016F16B2DC, + False, + ), + ( + generator_192, + int( + "0x4366fcadf10d30d086911de30143da6f579527036937007b337f7282460eae" + "5678b15cccda853193ea5fc4bc0a6b9d7a31128f27e1214988592827520b214e" + "ed5052f7775b750b0c6b15f145453ba3fee24a085d65287e10509eb5d5f602c4" + "40341376b95c24e5c4727d4b859bfe1483d20538acdd92c7997fa9c614f0f839" + "d7", + 16, + ), + 0x955C908FE900A996F7E2089BEE2F6376830F76A19135E753, + 0xBA0C42A91D3847DE4A592A46DC3FDAF45A7CC709B90DE520, + 0x1F58AD77FC04C782815A1405B0925E72095D906CBF52A668, + 0xF2E93758B3AF75EDF784F05A6761C9B9A6043C66B845B599, + False, + ), + ( + generator_192, + int( + "0x543f8af57d750e33aa8565e0cae92bfa7a1ff78833093421c2942cadf99866" + "70a5ff3244c02a8225e790fbf30ea84c74720abf99cfd10d02d34377c3d3b412" + "69bea763384f372bb786b5846f58932defa68023136cd571863b304886e95e52" + "e7877f445b9364b3f06f3c28da12707673fecb4b8071de06b6e0a3c87da160ce" + "f3", + 16, + ), + 0x31F7FA05576D78A949B24812D4383107A9A45BB5FCCDD835, + 0x8DC0EB65994A90F02B5E19BD18B32D61150746C09107E76B, + 0xBE26D59E4E883DDE7C286614A767B31E49AD88789D3A78FF, + 0x8762CA831C1CE42DF77893C9B03119428E7A9B819B619068, + False, + ), + ( + generator_192, + int( + "0xd2e8454143ce281e609a9d748014dcebb9d0bc53adb02443a6aac2ffe6cb009f" + "387c346ecb051791404f79e902ee333ad65e5c8cb38dc0d1d39a8dc90add502357" + "2720e5b94b190d43dd0d7873397504c0c7aef2727e628eb6a74411f2e400c65670" + "716cb4a815dc91cbbfeb7cfe8c929e93184c938af2c078584da045e8f8d1", + 16, + ), + 0x66AA8EDBBDB5CF8E28CEB51B5BDA891CAE2DF84819FE25C0, + 0x0C6BC2F69030A7CE58D4A00E3B3349844784A13B8936F8DA, + 0xA4661E69B1734F4A71B788410A464B71E7FFE42334484F23, + 0x738421CF5E049159D69C57A915143E226CAC8355E149AFE9, + False, + ), + ( + generator_192, + int( + "0x6660717144040f3e2f95a4e25b08a7079c702a8b29babad5a19a87654bc5c5af" + "a261512a11b998a4fb36b5d8fe8bd942792ff0324b108120de86d63f65855e5461" + "184fc96a0a8ffd2ce6d5dfb0230cbbdd98f8543e361b3205f5da3d500fdc8bac6d" + "b377d75ebef3cb8f4d1ff738071ad0938917889250b41dd1d98896ca06fb", + 16, + ), + 0xBCFACF45139B6F5F690A4C35A5FFFA498794136A2353FC77, + 0x6F4A6C906316A6AFC6D98FE1F0399D056F128FE0270B0F22, + 0x9DB679A3DAFE48F7CCAD122933ACFE9DA0970B71C94C21C1, + 0x984C2DB99827576C0A41A5DA41E07D8CC768BC82F18C9DA9, + False, + ), +] + + +@pytest.mark.parametrize("gen,msg,qx,qy,r,s,expected", CURVE_192_KATS) +def test_signature_validity(gen, msg, qx, qy, r, s, expected): + """ + `msg` = message, `qx` and `qy` represent the base point on + elliptic curve of `gen`, `r` and `s` are the signature, and + `expected` is True iff the signature is expected to be valid.""" + pubk = Public_key(gen, ellipticcurve.Point(gen.curve(), qx, qy)) + with pytest.warns(DeprecationWarning) as warns: + msg_dgst = digest_integer(msg) + assert len(warns) == 3 + assert "unused" in warns[0].message.args[0] + assert "unused" in warns[1].message.args[0] + assert "unused" in warns[2].message.args[0] + assert expected == pubk.verifies(msg_dgst, Signature(r, s)) + + +@pytest.mark.parametrize( + "gen,msg,qx,qy,r,s,expected", [x for x in CURVE_192_KATS if x[6]] +) +def test_pk_recovery(gen, msg, r, s, qx, qy, expected): + del expected + sign = Signature(r, s) + with pytest.warns(DeprecationWarning) as warns: + msg_dgst = digest_integer(msg) + assert len(warns) == 3 + assert "unused" in warns[0].message.args[0] + assert "unused" in warns[1].message.args[0] + assert "unused" in warns[2].message.args[0] + pks = sign.recover_public_keys(msg_dgst, gen) + + assert pks + + # Test if the signature is valid for all found public keys + for pk in pks: + q = pk.point + test_signature_validity(gen, msg, q.x(), q.y(), r, s, True) + + # Test if the original public key is in the set of found keys + original_q = ellipticcurve.Point(gen.curve(), qx, qy) + points = [pk.point for pk in pks] + assert original_q in points + + +@st.composite +def st_random_gen_key_msg_nonce(draw): + """Hypothesis strategy for test_sig_verify().""" + name_gen = { + "generator_192": generator_192, + "generator_224": generator_224, + "generator_256": generator_256, + "generator_secp256k1": generator_secp256k1, + "generator_384": generator_384, + "generator_521": generator_521, + } + name = draw(st.sampled_from(sorted(name_gen.keys()))) + note("Generator used: {0}".format(name)) + generator = name_gen[name] + order = int(generator.order()) - 1 + + key = draw(st.integers(min_value=1, max_value=order)) + msg = draw(st.integers(min_value=1, max_value=order)) + nonce = draw( + st.integers(min_value=1, max_value=order) + | st.integers(min_value=order >> 1, max_value=order) + ) + return generator, key, msg, nonce + + +SIG_VER_SETTINGS = dict(HYP_SETTINGS) +if "--fast" in sys.argv: # pragma: no cover + SIG_VER_SETTINGS["max_examples"] = 1 +else: + SIG_VER_SETTINGS["max_examples"] = 10 + + +@settings(**SIG_VER_SETTINGS) +@example((generator_224, 4, 1, 1)) +@given(st_random_gen_key_msg_nonce()) +def test_sig_verify(args): + """ + Check if signing and verification works for arbitrary messages and + that signatures for other messages are rejected. + """ + generator, sec_mult, msg, nonce = args + + pubkey = Public_key(generator, generator * sec_mult) + privkey = Private_key(pubkey, sec_mult) + + signature = privkey.sign(msg, nonce) + + assert pubkey.verifies(msg, signature) + + assert not pubkey.verifies(msg - 1, signature) + + +def test_int_to_string_with_zero(): + with pytest.warns(DeprecationWarning) as warns: + assert int_to_string(0) == b"\x00" + + assert len(warns) == 1 + assert "unused" in warns[0].message.args[0] diff --git a/code/.venv/lib/python3.12/site-packages/ecdsa/test_eddsa.py b/code/.venv/lib/python3.12/site-packages/ecdsa/test_eddsa.py new file mode 100644 index 0000000..6821b3b --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/ecdsa/test_eddsa.py @@ -0,0 +1,1124 @@ +import sys +import pickle +import hashlib +import pytest + +try: + import unittest2 as unittest +except ImportError: + import unittest +from hypothesis import given, settings, example +import hypothesis.strategies as st +from .ellipticcurve import PointEdwards, INFINITY, CurveEdTw +from .eddsa import ( + generator_ed25519, + curve_ed25519, + generator_ed448, + curve_ed448, + PrivateKey, + PublicKey, +) +from .ecdsa import generator_256, curve_256 +from .errors import MalformedPointError +from ._compat import a2b_hex, compat26_str + + +class TestA2B_Hex(unittest.TestCase): + def test_invalid_input(self): + with self.assertRaises(ValueError): + a2b_hex("abcdefghi") + + +def test_ed25519_curve_compare(): + assert curve_ed25519 != curve_256 + + +def test_ed25519_and_ed448_compare(): + assert curve_ed448 != curve_ed25519 + + +def test_ed25519_and_custom_curve_compare(): + a = CurveEdTw(curve_ed25519.p(), -curve_ed25519.a(), 1) + + assert curve_ed25519 != a + + +def test_ed25519_and_almost_exact_curve_compare(): + a = CurveEdTw(curve_ed25519.p(), curve_ed25519.a(), 1) + + assert curve_ed25519 != a + + +def test_ed25519_and_same_curve_params(): + a = CurveEdTw(curve_ed25519.p(), curve_ed25519.a(), curve_ed25519.d()) + + assert curve_ed25519 == a + assert not (curve_ed25519 != a) + + +def test_ed25519_contains_point(): + g = generator_ed25519 + assert curve_ed25519.contains_point(g.x(), g.y()) + + +def test_ed25519_contains_point_bad(): + assert not curve_ed25519.contains_point(1, 1) + + +def test_ed25519_double(): + a = generator_ed25519 + + z = a.double() + + assert isinstance(z, PointEdwards) + + x2 = int( + "24727413235106541002554574571675588834622768167397638456726423" + "682521233608206" + ) + y2 = int( + "15549675580280190176352668710449542251549572066445060580507079" + "593062643049417" + ) + + b = PointEdwards(curve_ed25519, x2, y2, 1, x2 * y2) + + assert z == b + assert a != b + + +def test_ed25519_add_as_double(): + a = generator_ed25519 + + z = a + a + + assert isinstance(z, PointEdwards) + + b = generator_ed25519.double() + + assert z == b + + +def test_ed25519_double_infinity(): + a = PointEdwards(curve_ed25519, 0, 1, 1, 0) + + z = a.double() + + assert z is INFINITY + + +def test_ed25519_double_badly_encoded_infinity(): + # invalid point, mostly to make instrumental happy + a = PointEdwards(curve_ed25519, 1, 1, 1, 0) + + z = a.double() + + assert z is INFINITY + + +def test_ed25519_eq_with_different_z(): + x = generator_ed25519.x() + y = generator_ed25519.y() + p = curve_ed25519.p() + + a = PointEdwards(curve_ed25519, x * 2 % p, y * 2 % p, 2, x * y * 2 % p) + b = PointEdwards(curve_ed25519, x * 3 % p, y * 3 % p, 3, x * y * 3 % p) + + assert a == b + + assert not (a != b) + + +def test_ed25519_eq_against_infinity(): + assert generator_ed25519 != INFINITY + + +def test_ed25519_eq_encoded_infinity_against_infinity(): + a = PointEdwards(curve_ed25519, 0, 1, 1, 0) + assert a == INFINITY + + +def test_ed25519_eq_bad_encode_of_infinity_against_infinity(): + # technically incorrect encoding of the point at infinity, but we check + # both X and T, so verify that just T==0 works + a = PointEdwards(curve_ed25519, 1, 1, 1, 0) + assert a == INFINITY + + +def test_ed25519_eq_against_non_Edwards_point(): + assert generator_ed25519 != generator_256 + + +def test_ed25519_eq_against_negated_point(): + g = generator_ed25519 + neg = PointEdwards(curve_ed25519, -g.x(), g.y(), 1, -g.x() * g.y()) + assert g != neg + + +def test_ed25519_eq_x_different_y(): + # not points on the curve, but __eq__ doesn't care + a = PointEdwards(curve_ed25519, 1, 1, 1, 1) + b = PointEdwards(curve_ed25519, 1, 2, 1, 2) + + assert a != b + + +def test_ed25519_mul_by_order(): + g = PointEdwards( + curve_ed25519, + generator_ed25519.x(), + generator_ed25519.y(), + 1, + generator_ed25519.x() * generator_ed25519.y(), + ) + + assert g * generator_ed25519.order() == INFINITY + + +def test_radd(): + + a = PointEdwards(curve_ed25519, 1, 1, 1, 1) + + p = INFINITY + a + + assert p == a + + +def test_ed25519_test_normalisation_and_scaling(): + x = generator_ed25519.x() + y = generator_ed25519.y() + p = curve_ed25519.p() + + a = PointEdwards(curve_ed25519, x * 11 % p, y * 11 % p, 11, x * y * 11 % p) + + assert a.x() == x + assert a.y() == y + + a.scale() + + assert a.x() == x + assert a.y() == y + + a.scale() # second execution should be a noop + + assert a.x() == x + assert a.y() == y + + +def test_ed25519_add_three_times(): + a = generator_ed25519 + + z = a + a + a + + x3 = int( + "468967334644549386571235445953867877890461982801326656862413" + "21779790909858396" + ) + y3 = int( + "832484377853344397649037712036920113830141722629755531674120" + "2210403726505172" + ) + + b = PointEdwards(curve_ed25519, x3, y3, 1, x3 * y3) + + assert z == b + + +def test_ed25519_add_to_infinity(): + # generator * (order-1) + x1 = int( + "427838232691226969392843410947554224151809796397784248136826" + "78720006717057747" + ) + y1 = int( + "463168356949264781694283940034751631413079938662562256157830" + "33603165251855960" + ) + inf_m_1 = PointEdwards(curve_ed25519, x1, y1, 1, x1 * y1) + + inf = inf_m_1 + generator_ed25519 + + assert inf is INFINITY + + +def test_ed25519_add_and_mul_equivalence(): + g = generator_ed25519 + + assert g + g == g * 2 + assert g + g + g == g * 3 + + +def test_ed25519_add_literal_infinity(): + g = generator_ed25519 + z = g + INFINITY + + assert z == g + + +def test_ed25519_add_infinity(): + inf = PointEdwards(curve_ed25519, 0, 1, 1, 0) + g = generator_ed25519 + z = g + inf + + assert z == g + + z = inf + g + + assert z == g + + +class TestEd25519(unittest.TestCase): + def test_add_wrong_curves(self): + with self.assertRaises(ValueError) as e: + generator_ed25519 + generator_ed448 + + self.assertIn("different curve", str(e.exception)) + + def test_add_wrong_point_type(self): + with self.assertRaises(ValueError) as e: + generator_ed25519 + generator_256 + + self.assertIn("different curve", str(e.exception)) + + +def test_generate_with_point(): + x1 = int( + "427838232691226969392843410947554224151809796397784248136826" + "78720006717057747" + ) + y1 = int( + "463168356949264781694283940034751631413079938662562256157830" + "33603165251855960" + ) + p = PointEdwards(curve_ed25519, x1, y1, 1, x1 * y1) + + pk = PublicKey(generator_ed25519, b"0" * 32, public_point=p) + + assert pk.public_point() == p + + +def test_ed25519_mul_to_order_min_1(): + x1 = int( + "427838232691226969392843410947554224151809796397784248136826" + "78720006717057747" + ) + y1 = int( + "463168356949264781694283940034751631413079938662562256157830" + "33603165251855960" + ) + inf_m_1 = PointEdwards(curve_ed25519, x1, y1, 1, x1 * y1) + + assert generator_ed25519 * (generator_ed25519.order() - 1) == inf_m_1 + + +def test_ed25519_mul_to_infinity(): + assert generator_ed25519 * generator_ed25519.order() == INFINITY + + +def test_ed25519_mul_to_infinity_plus_1(): + g = generator_ed25519 + assert g * (g.order() + 1) == g + + +def test_ed25519_mul_and_add(): + g = generator_ed25519 + a = g * 128 + b = g * 64 + g * 64 + + assert a == b + + +def test_ed25519_mul_and_add_2(): + g = generator_ed25519 + + a = g * 123 + b = g * 120 + g * 3 + + assert a == b + + +def test_ed25519_mul_infinity(): + inf = PointEdwards(curve_ed25519, 0, 1, 1, 0) + + z = inf * 11 + + assert z == INFINITY + + +def test_ed25519_mul_by_zero(): + z = generator_ed25519 * 0 + + assert z == INFINITY + + +def test_ed25519_mul_by_one(): + z = generator_ed25519 * 1 + + assert z == generator_ed25519 + + +def test_ed25519_mul_custom_point(): + # verify that multiplication without order set works + + g = generator_ed25519 + + a = PointEdwards(curve_ed25519, g.x(), g.y(), 1, g.x() * g.y()) + + z = a * 11 + + assert z == g * 11 + + +def test_ed25519_pickle(): + g = generator_ed25519 + assert pickle.loads(pickle.dumps(g)) == g + + +def test_ed448_eq_against_different_curve(): + assert generator_ed25519 != generator_ed448 + + +def test_ed448_double(): + g = generator_ed448 + z = g.double() + + assert isinstance(z, PointEdwards) + + x2 = int( + "4845591495304045936995492052586696895690942404582120401876" + "6013278705691214670908136440114445572635086627683154494739" + "7859048262938744149" + ) + y2 = int( + "4940887598674337276743026725267350893505445523037277237461" + "2648447308771911703729389009346215770388834286503647778745" + "3078312060500281069" + ) + + b = PointEdwards(curve_ed448, x2, y2, 1, x2 * y2) + + assert z == b + assert g != b + + +def test_ed448_add_as_double(): + g = generator_ed448 + z = g + g + + b = g.double() + + assert z == b + + +def test_ed448_mul_as_double(): + g = generator_ed448 + z = g * 2 + b = g.double() + + assert z == b + + +def test_ed448_add_to_infinity(): + # generator * (order - 1) + x1 = int( + "5022586839996825903617194737881084981068517190547539260353" + "6473749366191269932473977736719082931859264751085238669719" + "1187378895383117729" + ) + y1 = int( + "2988192100784814926760179304439306734375440401540802420959" + "2824137233150618983587600353687865541878473398230323350346" + "2500531545062832660" + ) + inf_m_1 = PointEdwards(curve_ed448, x1, y1, 1, x1 * y1) + + inf = inf_m_1 + generator_ed448 + + assert inf is INFINITY + + +def test_ed448_mul_to_infinity(): + g = generator_ed448 + inf = g * g.order() + + assert inf is INFINITY + + +def test_ed448_mul_to_infinity_plus_1(): + g = generator_ed448 + + z = g * (g.order() + 1) + + assert z == g + + +def test_ed448_add_and_mul_equivalence(): + g = generator_ed448 + + assert g + g == g * 2 + assert g + g + g == g * 3 + + +def test_ed25519_encode(): + g = generator_ed25519 + g_bytes = g.to_bytes() + assert len(g_bytes) == 32 + exp_bytes = ( + b"\x58\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66" + b"\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66" + ) + assert g_bytes == exp_bytes + + +def test_ed25519_decode(): + exp_bytes = ( + b"\x58\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66" + b"\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66" + ) + a = PointEdwards.from_bytes(curve_ed25519, exp_bytes) + + assert a == generator_ed25519 + + +class TestEdwardsMalformed(unittest.TestCase): + def test_invalid_point(self): + exp_bytes = ( + b"\x78\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66" + b"\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66" + ) + with self.assertRaises(MalformedPointError): + PointEdwards.from_bytes(curve_ed25519, exp_bytes) + + def test_invalid_length(self): + exp_bytes = ( + b"\x58\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66" + b"\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66" + b"\x66" + ) + with self.assertRaises(MalformedPointError) as e: + PointEdwards.from_bytes(curve_ed25519, exp_bytes) + + self.assertIn("length", str(e.exception)) + + def test_ed448_invalid(self): + exp_bytes = b"\xff" * 57 + with self.assertRaises(MalformedPointError): + PointEdwards.from_bytes(curve_ed448, exp_bytes) + + +def test_ed448_encode(): + g = generator_ed448 + g_bytes = g.to_bytes() + assert len(g_bytes) == 57 + exp_bytes = ( + b"\x14\xfa\x30\xf2\x5b\x79\x08\x98\xad\xc8\xd7\x4e\x2c\x13\xbd" + b"\xfd\xc4\x39\x7c\xe6\x1c\xff\xd3\x3a\xd7\xc2\xa0\x05\x1e\x9c" + b"\x78\x87\x40\x98\xa3\x6c\x73\x73\xea\x4b\x62\xc7\xc9\x56\x37" + b"\x20\x76\x88\x24\xbc\xb6\x6e\x71\x46\x3f\x69\x00" + ) + assert g_bytes == exp_bytes + + +def test_ed448_decode(): + exp_bytes = ( + b"\x14\xfa\x30\xf2\x5b\x79\x08\x98\xad\xc8\xd7\x4e\x2c\x13\xbd" + b"\xfd\xc4\x39\x7c\xe6\x1c\xff\xd3\x3a\xd7\xc2\xa0\x05\x1e\x9c" + b"\x78\x87\x40\x98\xa3\x6c\x73\x73\xea\x4b\x62\xc7\xc9\x56\x37" + b"\x20\x76\x88\x24\xbc\xb6\x6e\x71\x46\x3f\x69\x00" + ) + + a = PointEdwards.from_bytes(curve_ed448, exp_bytes) + + assert a == generator_ed448 + + +class TestEdDSAEquality(unittest.TestCase): + def test_equal_public_points(self): + key1 = PublicKey(generator_ed25519, b"\x01" * 32) + key2 = PublicKey(generator_ed25519, b"\x01" * 32) + + self.assertEqual(key1, key2) + # verify that `__ne__` works as expected + self.assertFalse(key1 != key2) + + def test_unequal_public_points(self): + key1 = PublicKey(generator_ed25519, b"\x01" * 32) + key2 = PublicKey(generator_ed25519, b"\x03" * 32) + + self.assertNotEqual(key1, key2) + + def test_unequal_to_string(self): + key1 = PublicKey(generator_ed25519, b"\x01" * 32) + key2 = b"\x01" * 32 + + self.assertNotEqual(key1, key2) + + def test_unequal_publickey_curves(self): + key1 = PublicKey(generator_ed25519, b"\x01" * 32) + key2 = PublicKey(generator_ed448, b"\x03" * 56 + b"\x00") + + self.assertNotEqual(key1, key2) + # verify that `__ne__` works as expected + self.assertTrue(key1 != key2) + + def test_equal_private_keys(self): + key1 = PrivateKey(generator_ed25519, b"\x01" * 32) + key2 = PrivateKey(generator_ed25519, b"\x01" * 32) + + self.assertEqual(key1, key2) + # verify that `__ne__` works as expected + self.assertFalse(key1 != key2) + + def test_unequal_private_keys(self): + key1 = PrivateKey(generator_ed25519, b"\x01" * 32) + key2 = PrivateKey(generator_ed25519, b"\x02" * 32) + + self.assertNotEqual(key1, key2) + # verify that `__ne__` works as expected + self.assertTrue(key1 != key2) + + def test_unequal_privatekey_to_string(self): + key1 = PrivateKey(generator_ed25519, b"\x01" * 32) + key2 = b"\x01" * 32 + + self.assertNotEqual(key1, key2) + + def test_unequal_privatekey_curves(self): + key1 = PrivateKey(generator_ed25519, b"\x01" * 32) + key2 = PrivateKey(generator_ed448, b"\x01" * 57) + + self.assertNotEqual(key1, key2) + + +class TestInvalidEdDSAInputs(unittest.TestCase): + def test_wrong_length_of_private_key(self): + with self.assertRaises(ValueError): + PrivateKey(generator_ed25519, b"\x01" * 31) + + def test_wrong_length_of_public_key(self): + with self.assertRaises(ValueError): + PublicKey(generator_ed25519, b"\x01" * 33) + + def test_wrong_cofactor_curve(self): + ed_c = curve_ed25519 + + def _hash(data): + return hashlib.new("sha512", compat26_str(data)).digest() + + curve = CurveEdTw(ed_c.p(), ed_c.a(), ed_c.d(), 1, _hash) + g = generator_ed25519 + fake_gen = PointEdwards(curve, g.x(), g.y(), 1, g.x() * g.y()) + + with self.assertRaises(ValueError) as e: + PrivateKey(fake_gen, g.to_bytes()) + + self.assertIn("cofactor", str(e.exception)) + + def test_invalid_signature_length(self): + key = PublicKey(generator_ed25519, b"\x01" * 32) + + with self.assertRaises(ValueError) as e: + key.verify(b"", b"\x01" * 65) + + self.assertIn("length", str(e.exception)) + + def test_changing_public_key(self): + key = PublicKey(generator_ed25519, b"\x01" * 32) + + g = key.point + + new_g = PointEdwards(curve_ed25519, g.x(), g.y(), 1, g.x() * g.y()) + + key.point = new_g + + self.assertEqual(g, key.point) + + def test_changing_public_key_to_different_point(self): + key = PublicKey(generator_ed25519, b"\x01" * 32) + + with self.assertRaises(ValueError) as e: + key.point = generator_ed25519 + + self.assertIn("coordinates", str(e.exception)) + + def test_invalid_s_value(self): + key = PublicKey( + generator_ed25519, + b"\xd7\x5a\x98\x01\x82\xb1\x0a\xb7\xd5\x4b\xfe\xd3\xc9\x64\x07\x3a" + b"\x0e\xe1\x72\xf3\xda\xa6\x23\x25\xaf\x02\x1a\x68\xf7\x07\x51\x1a", + ) + sig_valid = bytearray( + b"\xe5\x56\x43\x00\xc3\x60\xac\x72\x90\x86\xe2\xcc\x80\x6e\x82\x8a" + b"\x84\x87\x7f\x1e\xb8\xe5\xd9\x74\xd8\x73\xe0\x65\x22\x49\x01\x55" + b"\x5f\xb8\x82\x15\x90\xa3\x3b\xac\xc6\x1e\x39\x70\x1c\xf9\xb4\x6b" + b"\xd2\x5b\xf5\xf0\x59\x5b\xbe\x24\x65\x51\x41\x43\x8e\x7a\x10\x0b" + ) + + self.assertTrue(key.verify(b"", sig_valid)) + + sig_invalid = bytearray(sig_valid) + sig_invalid[-1] = 0xFF + + with self.assertRaises(ValueError): + key.verify(b"", sig_invalid) + + def test_invalid_r_value(self): + key = PublicKey( + generator_ed25519, + b"\xd7\x5a\x98\x01\x82\xb1\x0a\xb7\xd5\x4b\xfe\xd3\xc9\x64\x07\x3a" + b"\x0e\xe1\x72\xf3\xda\xa6\x23\x25\xaf\x02\x1a\x68\xf7\x07\x51\x1a", + ) + sig_valid = bytearray( + b"\xe5\x56\x43\x00\xc3\x60\xac\x72\x90\x86\xe2\xcc\x80\x6e\x82\x8a" + b"\x84\x87\x7f\x1e\xb8\xe5\xd9\x74\xd8\x73\xe0\x65\x22\x49\x01\x55" + b"\x5f\xb8\x82\x15\x90\xa3\x3b\xac\xc6\x1e\x39\x70\x1c\xf9\xb4\x6b" + b"\xd2\x5b\xf5\xf0\x59\x5b\xbe\x24\x65\x51\x41\x43\x8e\x7a\x10\x0b" + ) + + self.assertTrue(key.verify(b"", sig_valid)) + + sig_invalid = bytearray(sig_valid) + sig_invalid[0] = 0xE0 + + with self.assertRaises(ValueError): + key.verify(b"", sig_invalid) + + +HYP_SETTINGS = dict() +if "--fast" in sys.argv: # pragma: no cover + HYP_SETTINGS["max_examples"] = 2 +else: + HYP_SETTINGS["max_examples"] = 10 + + +@settings(**HYP_SETTINGS) +@example(1) +@example(5) # smallest multiple that requires changing sign of x +@given(st.integers(min_value=1, max_value=int(generator_ed25519.order() - 1))) +def test_ed25519_encode_decode(multiple): + a = generator_ed25519 * multiple + + b = PointEdwards.from_bytes(curve_ed25519, a.to_bytes()) + + assert a == b + + +@settings(**HYP_SETTINGS) +@example(1) +@example(2) # smallest multiple that requires changing the sign of x +@given(st.integers(min_value=1, max_value=int(generator_ed448.order() - 1))) +def test_ed448_encode_decode(multiple): + a = generator_ed448 * multiple + + b = PointEdwards.from_bytes(curve_ed448, a.to_bytes()) + + assert a == b + + +@settings(**HYP_SETTINGS) +@example(1) +@example(2) +@given(st.integers(min_value=1, max_value=int(generator_ed25519.order()) - 1)) +def test_ed25519_mul_precompute_vs_naf(multiple): + """Compare multiplication with and without precomputation.""" + g = generator_ed25519 + new_g = PointEdwards(curve_ed25519, g.x(), g.y(), 1, g.x() * g.y()) + + assert g * multiple == multiple * new_g + + +# Test vectors from RFC 8032 +TEST_VECTORS = [ + # TEST 1 + ( + generator_ed25519, + "9d61b19deffd5a60ba844af492ec2cc4" "4449c5697b326919703bac031cae7f60", + "d75a980182b10ab7d54bfed3c964073a" "0ee172f3daa62325af021a68f707511a", + "", + "e5564300c360ac729086e2cc806e828a" + "84877f1eb8e5d974d873e06522490155" + "5fb8821590a33bacc61e39701cf9b46b" + "d25bf5f0595bbe24655141438e7a100b", + ), + # TEST 2 + ( + generator_ed25519, + "4ccd089b28ff96da9db6c346ec114e0f" "5b8a319f35aba624da8cf6ed4fb8a6fb", + "3d4017c3e843895a92b70aa74d1b7ebc" "9c982ccf2ec4968cc0cd55f12af4660c", + "72", + "92a009a9f0d4cab8720e820b5f642540" + "a2b27b5416503f8fb3762223ebdb69da" + "085ac1e43e15996e458f3613d0f11d8c" + "387b2eaeb4302aeeb00d291612bb0c00", + ), + # TEST 3 + ( + generator_ed25519, + "c5aa8df43f9f837bedb7442f31dcb7b1" "66d38535076f094b85ce3a2e0b4458f7", + "fc51cd8e6218a1a38da47ed00230f058" "0816ed13ba3303ac5deb911548908025", + "af82", + "6291d657deec24024827e69c3abe01a3" + "0ce548a284743a445e3680d7db5ac3ac" + "18ff9b538d16f290ae67f760984dc659" + "4a7c15e9716ed28dc027beceea1ec40a", + ), + # TEST 1024 + ( + generator_ed25519, + "f5e5767cf153319517630f226876b86c" "8160cc583bc013744c6bf255f5cc0ee5", + "278117fc144c72340f67d0f2316e8386" "ceffbf2b2428c9c51fef7c597f1d426e", + "08b8b2b733424243760fe426a4b54908" + "632110a66c2f6591eabd3345e3e4eb98" + "fa6e264bf09efe12ee50f8f54e9f77b1" + "e355f6c50544e23fb1433ddf73be84d8" + "79de7c0046dc4996d9e773f4bc9efe57" + "38829adb26c81b37c93a1b270b20329d" + "658675fc6ea534e0810a4432826bf58c" + "941efb65d57a338bbd2e26640f89ffbc" + "1a858efcb8550ee3a5e1998bd177e93a" + "7363c344fe6b199ee5d02e82d522c4fe" + "ba15452f80288a821a579116ec6dad2b" + "3b310da903401aa62100ab5d1a36553e" + "06203b33890cc9b832f79ef80560ccb9" + "a39ce767967ed628c6ad573cb116dbef" + "efd75499da96bd68a8a97b928a8bbc10" + "3b6621fcde2beca1231d206be6cd9ec7" + "aff6f6c94fcd7204ed3455c68c83f4a4" + "1da4af2b74ef5c53f1d8ac70bdcb7ed1" + "85ce81bd84359d44254d95629e9855a9" + "4a7c1958d1f8ada5d0532ed8a5aa3fb2" + "d17ba70eb6248e594e1a2297acbbb39d" + "502f1a8c6eb6f1ce22b3de1a1f40cc24" + "554119a831a9aad6079cad88425de6bd" + "e1a9187ebb6092cf67bf2b13fd65f270" + "88d78b7e883c8759d2c4f5c65adb7553" + "878ad575f9fad878e80a0c9ba63bcbcc" + "2732e69485bbc9c90bfbd62481d9089b" + "eccf80cfe2df16a2cf65bd92dd597b07" + "07e0917af48bbb75fed413d238f5555a" + "7a569d80c3414a8d0859dc65a46128ba" + "b27af87a71314f318c782b23ebfe808b" + "82b0ce26401d2e22f04d83d1255dc51a" + "ddd3b75a2b1ae0784504df543af8969b" + "e3ea7082ff7fc9888c144da2af58429e" + "c96031dbcad3dad9af0dcbaaaf268cb8" + "fcffead94f3c7ca495e056a9b47acdb7" + "51fb73e666c6c655ade8297297d07ad1" + "ba5e43f1bca32301651339e22904cc8c" + "42f58c30c04aafdb038dda0847dd988d" + "cda6f3bfd15c4b4c4525004aa06eeff8" + "ca61783aacec57fb3d1f92b0fe2fd1a8" + "5f6724517b65e614ad6808d6f6ee34df" + "f7310fdc82aebfd904b01e1dc54b2927" + "094b2db68d6f903b68401adebf5a7e08" + "d78ff4ef5d63653a65040cf9bfd4aca7" + "984a74d37145986780fc0b16ac451649" + "de6188a7dbdf191f64b5fc5e2ab47b57" + "f7f7276cd419c17a3ca8e1b939ae49e4" + "88acba6b965610b5480109c8b17b80e1" + "b7b750dfc7598d5d5011fd2dcc5600a3" + "2ef5b52a1ecc820e308aa342721aac09" + "43bf6686b64b2579376504ccc493d97e" + "6aed3fb0f9cd71a43dd497f01f17c0e2" + "cb3797aa2a2f256656168e6c496afc5f" + "b93246f6b1116398a346f1a641f3b041" + "e989f7914f90cc2c7fff357876e506b5" + "0d334ba77c225bc307ba537152f3f161" + "0e4eafe595f6d9d90d11faa933a15ef1" + "369546868a7f3a45a96768d40fd9d034" + "12c091c6315cf4fde7cb68606937380d" + "b2eaaa707b4c4185c32eddcdd306705e" + "4dc1ffc872eeee475a64dfac86aba41c" + "0618983f8741c5ef68d3a101e8a3b8ca" + "c60c905c15fc910840b94c00a0b9d0", + "0aab4c900501b3e24d7cdf4663326a3a" + "87df5e4843b2cbdb67cbf6e460fec350" + "aa5371b1508f9f4528ecea23c436d94b" + "5e8fcd4f681e30a6ac00a9704a188a03", + ), + # TEST SHA(abc) + ( + generator_ed25519, + "833fe62409237b9d62ec77587520911e" "9a759cec1d19755b7da901b96dca3d42", + "ec172b93ad5e563bf4932c70e1245034" "c35467ef2efd4d64ebf819683467e2bf", + "ddaf35a193617abacc417349ae204131" + "12e6fa4e89a97ea20a9eeee64b55d39a" + "2192992a274fc1a836ba3c23a3feebbd" + "454d4423643ce80e2a9ac94fa54ca49f", + "dc2a4459e7369633a52b1bf277839a00" + "201009a3efbf3ecb69bea2186c26b589" + "09351fc9ac90b3ecfdfbc7c66431e030" + "3dca179c138ac17ad9bef1177331a704", + ), + # Blank + ( + generator_ed448, + "6c82a562cb808d10d632be89c8513ebf" + "6c929f34ddfa8c9f63c9960ef6e348a3" + "528c8a3fcc2f044e39a3fc5b94492f8f" + "032e7549a20098f95b", + "5fd7449b59b461fd2ce787ec616ad46a" + "1da1342485a70e1f8a0ea75d80e96778" + "edf124769b46c7061bd6783df1e50f6c" + "d1fa1abeafe8256180", + "", + "533a37f6bbe457251f023c0d88f976ae" + "2dfb504a843e34d2074fd823d41a591f" + "2b233f034f628281f2fd7a22ddd47d78" + "28c59bd0a21bfd3980ff0d2028d4b18a" + "9df63e006c5d1c2d345b925d8dc00b41" + "04852db99ac5c7cdda8530a113a0f4db" + "b61149f05a7363268c71d95808ff2e65" + "2600", + ), + # 1 octet + ( + generator_ed448, + "c4eab05d357007c632f3dbb48489924d" + "552b08fe0c353a0d4a1f00acda2c463a" + "fbea67c5e8d2877c5e3bc397a659949e" + "f8021e954e0a12274e", + "43ba28f430cdff456ae531545f7ecd0a" + "c834a55d9358c0372bfa0c6c6798c086" + "6aea01eb00742802b8438ea4cb82169c" + "235160627b4c3a9480", + "03", + "26b8f91727bd62897af15e41eb43c377" + "efb9c610d48f2335cb0bd0087810f435" + "2541b143c4b981b7e18f62de8ccdf633" + "fc1bf037ab7cd779805e0dbcc0aae1cb" + "cee1afb2e027df36bc04dcecbf154336" + "c19f0af7e0a6472905e799f1953d2a0f" + "f3348ab21aa4adafd1d234441cf807c0" + "3a00", + ), + # 11 octets + ( + generator_ed448, + "cd23d24f714274e744343237b93290f5" + "11f6425f98e64459ff203e8985083ffd" + "f60500553abc0e05cd02184bdb89c4cc" + "d67e187951267eb328", + "dcea9e78f35a1bf3499a831b10b86c90" + "aac01cd84b67a0109b55a36e9328b1e3" + "65fce161d71ce7131a543ea4cb5f7e9f" + "1d8b00696447001400", + "0c3e544074ec63b0265e0c", + "1f0a8888ce25e8d458a21130879b840a" + "9089d999aaba039eaf3e3afa090a09d3" + "89dba82c4ff2ae8ac5cdfb7c55e94d5d" + "961a29fe0109941e00b8dbdeea6d3b05" + "1068df7254c0cdc129cbe62db2dc957d" + "bb47b51fd3f213fb8698f064774250a5" + "028961c9bf8ffd973fe5d5c206492b14" + "0e00", + ), + # 12 octets + ( + generator_ed448, + "258cdd4ada32ed9c9ff54e63756ae582" + "fb8fab2ac721f2c8e676a72768513d93" + "9f63dddb55609133f29adf86ec9929dc" + "cb52c1c5fd2ff7e21b", + "3ba16da0c6f2cc1f30187740756f5e79" + "8d6bc5fc015d7c63cc9510ee3fd44adc" + "24d8e968b6e46e6f94d19b945361726b" + "d75e149ef09817f580", + "64a65f3cdedcdd66811e2915", + "7eeeab7c4e50fb799b418ee5e3197ff6" + "bf15d43a14c34389b59dd1a7b1b85b4a" + "e90438aca634bea45e3a2695f1270f07" + "fdcdf7c62b8efeaf00b45c2c96ba457e" + "b1a8bf075a3db28e5c24f6b923ed4ad7" + "47c3c9e03c7079efb87cb110d3a99861" + "e72003cbae6d6b8b827e4e6c143064ff" + "3c00", + ), + # 13 octets + ( + generator_ed448, + "7ef4e84544236752fbb56b8f31a23a10" + "e42814f5f55ca037cdcc11c64c9a3b29" + "49c1bb60700314611732a6c2fea98eeb" + "c0266a11a93970100e", + "b3da079b0aa493a5772029f0467baebe" + "e5a8112d9d3a22532361da294f7bb381" + "5c5dc59e176b4d9f381ca0938e13c6c0" + "7b174be65dfa578e80", + "64a65f3cdedcdd66811e2915e7", + "6a12066f55331b6c22acd5d5bfc5d712" + "28fbda80ae8dec26bdd306743c5027cb" + "4890810c162c027468675ecf645a8317" + "6c0d7323a2ccde2d80efe5a1268e8aca" + "1d6fbc194d3f77c44986eb4ab4177919" + "ad8bec33eb47bbb5fc6e28196fd1caf5" + "6b4e7e0ba5519234d047155ac727a105" + "3100", + ), + # 64 octets + ( + generator_ed448, + "d65df341ad13e008567688baedda8e9d" + "cdc17dc024974ea5b4227b6530e339bf" + "f21f99e68ca6968f3cca6dfe0fb9f4fa" + "b4fa135d5542ea3f01", + "df9705f58edbab802c7f8363cfe5560a" + "b1c6132c20a9f1dd163483a26f8ac53a" + "39d6808bf4a1dfbd261b099bb03b3fb5" + "0906cb28bd8a081f00", + "bd0f6a3747cd561bdddf4640a332461a" + "4a30a12a434cd0bf40d766d9c6d458e5" + "512204a30c17d1f50b5079631f64eb31" + "12182da3005835461113718d1a5ef944", + "554bc2480860b49eab8532d2a533b7d5" + "78ef473eeb58c98bb2d0e1ce488a98b1" + "8dfde9b9b90775e67f47d4a1c3482058" + "efc9f40d2ca033a0801b63d45b3b722e" + "f552bad3b4ccb667da350192b61c508c" + "f7b6b5adadc2c8d9a446ef003fb05cba" + "5f30e88e36ec2703b349ca229c267083" + "3900", + ), + # 256 octets + ( + generator_ed448, + "2ec5fe3c17045abdb136a5e6a913e32a" + "b75ae68b53d2fc149b77e504132d3756" + "9b7e766ba74a19bd6162343a21c8590a" + "a9cebca9014c636df5", + "79756f014dcfe2079f5dd9e718be4171" + "e2ef2486a08f25186f6bff43a9936b9b" + "fe12402b08ae65798a3d81e22e9ec80e" + "7690862ef3d4ed3a00", + "15777532b0bdd0d1389f636c5f6b9ba7" + "34c90af572877e2d272dd078aa1e567c" + "fa80e12928bb542330e8409f31745041" + "07ecd5efac61ae7504dabe2a602ede89" + "e5cca6257a7c77e27a702b3ae39fc769" + "fc54f2395ae6a1178cab4738e543072f" + "c1c177fe71e92e25bf03e4ecb72f47b6" + "4d0465aaea4c7fad372536c8ba516a60" + "39c3c2a39f0e4d832be432dfa9a706a6" + "e5c7e19f397964ca4258002f7c0541b5" + "90316dbc5622b6b2a6fe7a4abffd9610" + "5eca76ea7b98816af0748c10df048ce0" + "12d901015a51f189f3888145c03650aa" + "23ce894c3bd889e030d565071c59f409" + "a9981b51878fd6fc110624dcbcde0bf7" + "a69ccce38fabdf86f3bef6044819de11", + "c650ddbb0601c19ca11439e1640dd931" + "f43c518ea5bea70d3dcde5f4191fe53f" + "00cf966546b72bcc7d58be2b9badef28" + "743954e3a44a23f880e8d4f1cfce2d7a" + "61452d26da05896f0a50da66a239a8a1" + "88b6d825b3305ad77b73fbac0836ecc6" + "0987fd08527c1a8e80d5823e65cafe2a" + "3d00", + ), + # 1023 octets + ( + generator_ed448, + "872d093780f5d3730df7c212664b37b8" + "a0f24f56810daa8382cd4fa3f77634ec" + "44dc54f1c2ed9bea86fafb7632d8be19" + "9ea165f5ad55dd9ce8", + "a81b2e8a70a5ac94ffdbcc9badfc3feb" + "0801f258578bb114ad44ece1ec0e799d" + "a08effb81c5d685c0c56f64eecaef8cd" + "f11cc38737838cf400", + "6ddf802e1aae4986935f7f981ba3f035" + "1d6273c0a0c22c9c0e8339168e675412" + "a3debfaf435ed651558007db4384b650" + "fcc07e3b586a27a4f7a00ac8a6fec2cd" + "86ae4bf1570c41e6a40c931db27b2faa" + "15a8cedd52cff7362c4e6e23daec0fbc" + "3a79b6806e316efcc7b68119bf46bc76" + "a26067a53f296dafdbdc11c77f7777e9" + "72660cf4b6a9b369a6665f02e0cc9b6e" + "dfad136b4fabe723d2813db3136cfde9" + "b6d044322fee2947952e031b73ab5c60" + "3349b307bdc27bc6cb8b8bbd7bd32321" + "9b8033a581b59eadebb09b3c4f3d2277" + "d4f0343624acc817804728b25ab79717" + "2b4c5c21a22f9c7839d64300232eb66e" + "53f31c723fa37fe387c7d3e50bdf9813" + "a30e5bb12cf4cd930c40cfb4e1fc6225" + "92a49588794494d56d24ea4b40c89fc0" + "596cc9ebb961c8cb10adde976a5d602b" + "1c3f85b9b9a001ed3c6a4d3b1437f520" + "96cd1956d042a597d561a596ecd3d173" + "5a8d570ea0ec27225a2c4aaff26306d1" + "526c1af3ca6d9cf5a2c98f47e1c46db9" + "a33234cfd4d81f2c98538a09ebe76998" + "d0d8fd25997c7d255c6d66ece6fa56f1" + "1144950f027795e653008f4bd7ca2dee" + "85d8e90f3dc315130ce2a00375a318c7" + "c3d97be2c8ce5b6db41a6254ff264fa6" + "155baee3b0773c0f497c573f19bb4f42" + "40281f0b1f4f7be857a4e59d416c06b4" + "c50fa09e1810ddc6b1467baeac5a3668" + "d11b6ecaa901440016f389f80acc4db9" + "77025e7f5924388c7e340a732e554440" + "e76570f8dd71b7d640b3450d1fd5f041" + "0a18f9a3494f707c717b79b4bf75c984" + "00b096b21653b5d217cf3565c9597456" + "f70703497a078763829bc01bb1cbc8fa" + "04eadc9a6e3f6699587a9e75c94e5bab" + "0036e0b2e711392cff0047d0d6b05bd2" + "a588bc109718954259f1d86678a579a3" + "120f19cfb2963f177aeb70f2d4844826" + "262e51b80271272068ef5b3856fa8535" + "aa2a88b2d41f2a0e2fda7624c2850272" + "ac4a2f561f8f2f7a318bfd5caf969614" + "9e4ac824ad3460538fdc25421beec2cc" + "6818162d06bbed0c40a387192349db67" + "a118bada6cd5ab0140ee273204f628aa" + "d1c135f770279a651e24d8c14d75a605" + "9d76b96a6fd857def5e0b354b27ab937" + "a5815d16b5fae407ff18222c6d1ed263" + "be68c95f32d908bd895cd76207ae7264" + "87567f9a67dad79abec316f683b17f2d" + "02bf07e0ac8b5bc6162cf94697b3c27c" + "d1fea49b27f23ba2901871962506520c" + "392da8b6ad0d99f7013fbc06c2c17a56" + "9500c8a7696481c1cd33e9b14e40b82e" + "79a5f5db82571ba97bae3ad3e0479515" + "bb0e2b0f3bfcd1fd33034efc6245eddd" + "7ee2086ddae2600d8ca73e214e8c2b0b" + "db2b047c6a464a562ed77b73d2d841c4" + "b34973551257713b753632efba348169" + "abc90a68f42611a40126d7cb21b58695" + "568186f7e569d2ff0f9e745d0487dd2e" + "b997cafc5abf9dd102e62ff66cba87", + "e301345a41a39a4d72fff8df69c98075" + "a0cc082b802fc9b2b6bc503f926b65bd" + "df7f4c8f1cb49f6396afc8a70abe6d8a" + "ef0db478d4c6b2970076c6a0484fe76d" + "76b3a97625d79f1ce240e7c576750d29" + "5528286f719b413de9ada3e8eb78ed57" + "3603ce30d8bb761785dc30dbc320869e" + "1a00", + ), +] + + +@pytest.mark.parametrize( + "generator,private_key,public_key,message,signature", + TEST_VECTORS, +) +def test_vectors(generator, private_key, public_key, message, signature): + private_key = a2b_hex(private_key) + public_key = a2b_hex(public_key) + message = a2b_hex(message) + signature = a2b_hex(signature) + + sig_key = PrivateKey(generator, private_key) + ver_key = PublicKey(generator, public_key) + + assert sig_key.public_key().public_key() == ver_key.public_key() + + gen_sig = sig_key.sign(message) + + assert gen_sig == signature + + assert ver_key.verify(message, signature) diff --git a/code/.venv/lib/python3.12/site-packages/ecdsa/test_ellipticcurve.py b/code/.venv/lib/python3.12/site-packages/ecdsa/test_ellipticcurve.py new file mode 100644 index 0000000..9bf0951 --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/ecdsa/test_ellipticcurve.py @@ -0,0 +1,256 @@ +import pytest + +try: + import unittest2 as unittest +except ImportError: + import unittest +from hypothesis import given, settings +import hypothesis.strategies as st + +try: + from hypothesis import HealthCheck + + HC_PRESENT = True +except ImportError: # pragma: no cover + HC_PRESENT = False +from .numbertheory import inverse_mod +from .ellipticcurve import CurveFp, INFINITY, Point, CurveEdTw + + +HYP_SETTINGS = {} +if HC_PRESENT: # pragma: no branch + HYP_SETTINGS["suppress_health_check"] = [HealthCheck.too_slow] + HYP_SETTINGS["deadline"] = 5000 + + +# NIST Curve P-192: +p = 6277101735386680763835789423207666416083908700390324961279 +r = 6277101735386680763835789423176059013767194773182842284081 +# s = 0x3045ae6fc8422f64ed579528d38120eae12196d5 +# c = 0x3099d2bbbfcb2538542dcd5fb078b6ef5f3d6fe2c745de65 +b = 0x64210519E59C80E70FA7E9AB72243049FEB8DEECC146B9B1 +Gx = 0x188DA80EB03090F67CBF20EB43A18800F4FF0AFD82FF1012 +Gy = 0x07192B95FFC8DA78631011ED6B24CDD573F977A11E794811 + +c192 = CurveFp(p, -3, b) +p192 = Point(c192, Gx, Gy, r) + +c_23 = CurveFp(23, 1, 1) +g_23 = Point(c_23, 13, 7, 7) + + +HYP_SLOW_SETTINGS = dict(HYP_SETTINGS) +HYP_SLOW_SETTINGS["max_examples"] = 2 + + +@settings(**HYP_SLOW_SETTINGS) +@given(st.integers(min_value=1, max_value=r - 1)) +def test_p192_mult_tests(multiple): + inv_m = inverse_mod(multiple, r) + + p1 = p192 * multiple + assert p1 * inv_m == p192 + + +def add_n_times(point, n): + ret = INFINITY + i = 0 + while i <= n: + yield ret + ret = ret + point + i += 1 + + +# From X9.62 I.1 (p. 96): +@pytest.mark.parametrize( + "p, m, check", + [(g_23, n, exp) for n, exp in enumerate(add_n_times(g_23, 8))], + ids=["g_23 test with mult {0}".format(i) for i in range(9)], +) +def test_add_and_mult_equivalence(p, m, check): + assert p * m == check + + +class TestCurve(unittest.TestCase): + @classmethod + def setUpClass(cls): + cls.c_23 = CurveFp(23, 1, 1) + + def test_equality_curves(self): + self.assertEqual(self.c_23, CurveFp(23, 1, 1)) + + def test_inequality_curves(self): + c192 = CurveFp(p, -3, b) + self.assertNotEqual(self.c_23, c192) + + def test_usability_in_a_hashed_collection_curves(self): + {self.c_23: None} + + def test_hashability_curves(self): + hash(self.c_23) + + def test_conflation_curves(self): + ne1, ne2, ne3 = CurveFp(24, 1, 1), CurveFp(23, 2, 1), CurveFp(23, 1, 2) + eq1, eq2, eq3 = CurveFp(23, 1, 1), CurveFp(23, 1, 1), self.c_23 + self.assertEqual(len(set((c_23, eq1, eq2, eq3))), 1) + self.assertEqual(len(set((c_23, ne1, ne2, ne3))), 4) + self.assertDictEqual({c_23: None}, {eq1: None}) + self.assertIn(eq2, {eq3: None}) + + def test___str__(self): + self.assertEqual(str(self.c_23), "CurveFp(p=23, a=1, b=1)") + + def test___str___with_cofactor(self): + c = CurveFp(23, 1, 1, 4) + self.assertEqual(str(c), "CurveFp(p=23, a=1, b=1, h=4)") + + +class TestCurveEdTw(unittest.TestCase): + @classmethod + def setUpClass(cls): + cls.c_23 = CurveEdTw(23, 1, 1) + + def test___str__(self): + self.assertEqual(str(self.c_23), "CurveEdTw(p=23, a=1, d=1)") + + def test___str___with_cofactor(self): + c = CurveEdTw(23, 1, 1, 4) + self.assertEqual(str(c), "CurveEdTw(p=23, a=1, d=1, h=4)") + + def test_usability_in_a_hashed_collection_curves(self): + {self.c_23: None} + + def test_hashability_curves(self): + hash(self.c_23) + + +class TestPoint(unittest.TestCase): + @classmethod + def setUpClass(cls): + cls.c_23 = CurveFp(23, 1, 1) + cls.g_23 = Point(cls.c_23, 13, 7, 7) + + p = 6277101735386680763835789423207666416083908700390324961279 + r = 6277101735386680763835789423176059013767194773182842284081 + # s = 0x3045ae6fc8422f64ed579528d38120eae12196d5 + # c = 0x3099d2bbbfcb2538542dcd5fb078b6ef5f3d6fe2c745de65 + b = 0x64210519E59C80E70FA7E9AB72243049FEB8DEECC146B9B1 + Gx = 0x188DA80EB03090F67CBF20EB43A18800F4FF0AFD82FF1012 + Gy = 0x07192B95FFC8DA78631011ED6B24CDD573F977A11E794811 + + cls.c192 = CurveFp(p, -3, b) + cls.p192 = Point(cls.c192, Gx, Gy, r) + + def test_p192(self): + # Checking against some sample computations presented + # in X9.62: + d = 651056770906015076056810763456358567190100156695615665659 + Q = d * self.p192 + self.assertEqual( + Q.x(), 0x62B12D60690CDCF330BABAB6E69763B471F994DD702D16A5 + ) + + k = 6140507067065001063065065565667405560006161556565665656654 + R = k * self.p192 + self.assertEqual( + R.x(), 0x885052380FF147B734C330C43D39B2C4A89F29B0F749FEAD + ) + self.assertEqual( + R.y(), 0x9CF9FA1CBEFEFB917747A3BB29C072B9289C2547884FD835 + ) + + u1 = 2563697409189434185194736134579731015366492496392189760599 + u2 = 6266643813348617967186477710235785849136406323338782220568 + temp = u1 * self.p192 + u2 * Q + self.assertEqual( + temp.x(), 0x885052380FF147B734C330C43D39B2C4A89F29B0F749FEAD + ) + self.assertEqual( + temp.y(), 0x9CF9FA1CBEFEFB917747A3BB29C072B9289C2547884FD835 + ) + + def test_double_infinity(self): + p1 = INFINITY + p3 = p1.double() + self.assertEqual(p1, p3) + self.assertEqual(p3.x(), p1.x()) + self.assertEqual(p3.y(), p3.y()) + + def test_double(self): + x1, y1, x3, y3 = (3, 10, 7, 12) + + p1 = Point(self.c_23, x1, y1) + p3 = p1.double() + self.assertEqual(p3.x(), x3) + self.assertEqual(p3.y(), y3) + + def test_multiply(self): + x1, y1, m, x3, y3 = (3, 10, 2, 7, 12) + p1 = Point(self.c_23, x1, y1) + p3 = p1 * m + self.assertEqual(p3.x(), x3) + self.assertEqual(p3.y(), y3) + + # Trivial tests from X9.62 B.3: + def test_add(self): + """We expect that on curve c, (x1,y1) + (x2, y2 ) = (x3, y3).""" + + x1, y1, x2, y2, x3, y3 = (3, 10, 9, 7, 17, 20) + p1 = Point(self.c_23, x1, y1) + p2 = Point(self.c_23, x2, y2) + p3 = p1 + p2 + self.assertEqual(p3.x(), x3) + self.assertEqual(p3.y(), y3) + + def test_add_as_double(self): + """We expect that on curve c, (x1,y1) + (x2, y2 ) = (x3, y3).""" + + x1, y1, x2, y2, x3, y3 = (3, 10, 3, 10, 7, 12) + p1 = Point(self.c_23, x1, y1) + p2 = Point(self.c_23, x2, y2) + p3 = p1 + p2 + self.assertEqual(p3.x(), x3) + self.assertEqual(p3.y(), y3) + + def test_equality_points(self): + self.assertEqual(self.g_23, Point(self.c_23, 13, 7, 7)) + + def test_inequality_points(self): + c = CurveFp(100, -3, 100) + p = Point(c, 100, 100, 100) + self.assertNotEqual(self.g_23, p) + + def test_inequality_points_diff_types(self): + c = CurveFp(100, -3, 100) + self.assertNotEqual(self.g_23, c) + + def test_to_bytes_from_bytes(self): + p = Point(self.c_23, 3, 10) + + self.assertEqual(p, Point.from_bytes(self.c_23, p.to_bytes())) + + def test_add_to_neg_self(self): + p = Point(self.c_23, 3, 10) + + self.assertEqual(INFINITY, p + (-p)) + + def test_add_to_infinity(self): + p = Point(self.c_23, 3, 10) + + self.assertIs(p, p + INFINITY) + + def test_mul_infinity_by_scalar(self): + self.assertIs(INFINITY, INFINITY * 10) + + def test_mul_by_negative(self): + p = Point(self.c_23, 3, 10) + + self.assertEqual(p * -5, (-p) * 5) + + def test_str_infinity(self): + self.assertEqual(str(INFINITY), "infinity") + + def test_str_point(self): + p = Point(self.c_23, 3, 10) + + self.assertEqual(str(p), "(3,10)") diff --git a/code/.venv/lib/python3.12/site-packages/ecdsa/test_jacobi.py b/code/.venv/lib/python3.12/site-packages/ecdsa/test_jacobi.py new file mode 100644 index 0000000..9a46afe --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/ecdsa/test_jacobi.py @@ -0,0 +1,753 @@ +import pickle +import sys + +try: + import unittest2 as unittest +except ImportError: + import unittest + +import os +import signal +import pytest +import threading +import platform +import hypothesis.strategies as st +from hypothesis import given, assume, settings, example + +from .ellipticcurve import CurveFp, PointJacobi, INFINITY +from .ecdsa import ( + generator_256, + curve_256, + generator_224, + generator_brainpoolp160r1, + curve_brainpoolp160r1, + generator_112r2, + curve_112r2, +) +from .numbertheory import inverse_mod +from .util import randrange + + +NO_OLD_SETTINGS = {} +if sys.version_info > (2, 7): # pragma: no branch + NO_OLD_SETTINGS["deadline"] = 5000 + + +SLOW_SETTINGS = {} +if "--fast" in sys.argv: # pragma: no cover + SLOW_SETTINGS["max_examples"] = 2 +else: + SLOW_SETTINGS["max_examples"] = 10 + + +class TestJacobi(unittest.TestCase): + def test___init__(self): + curve = object() + x = 2 + y = 3 + z = 1 + order = 4 + pj = PointJacobi(curve, x, y, z, order) + + self.assertEqual(pj.order(), order) + self.assertIs(pj.curve(), curve) + self.assertEqual(pj.x(), x) + self.assertEqual(pj.y(), y) + + def test_add_with_different_curves(self): + p_a = PointJacobi.from_affine(generator_256) + p_b = PointJacobi.from_affine(generator_224) + + with self.assertRaises(ValueError): # pragma: no branch + p_a + p_b + + def test_compare_different_curves(self): + self.assertNotEqual(generator_256, generator_224) + + def test_equality_with_non_point(self): + pj = PointJacobi.from_affine(generator_256) + + self.assertNotEqual(pj, "value") + + def test_conversion(self): + pj = PointJacobi.from_affine(generator_256) + pw = pj.to_affine() + + self.assertEqual(generator_256, pw) + + def test_single_double(self): + pj = PointJacobi.from_affine(generator_256) + pw = generator_256.double() + + pj = pj.double() + + self.assertEqual(pj.x(), pw.x()) + self.assertEqual(pj.y(), pw.y()) + + def test_double_with_zero_point(self): + pj = PointJacobi(curve_256, 0, 0, 1) + + pj = pj.double() + + self.assertIs(pj, INFINITY) + + def test_double_with_zero_equivalent_point(self): + pj = PointJacobi(curve_256, 0, curve_256.p(), 1) + + pj = pj.double() + + self.assertIs(pj, INFINITY) + + def test_double_with_zero_equivalent_point_non_1_z(self): + pj = PointJacobi(curve_256, 0, curve_256.p(), 2) + + pj = pj.double() + + self.assertIs(pj, INFINITY) + + def test_compare_with_affine_point(self): + pj = PointJacobi.from_affine(generator_256) + pa = pj.to_affine() + + self.assertEqual(pj, pa) + self.assertEqual(pa, pj) + + def test_to_affine_with_zero_point(self): + pj = PointJacobi(curve_256, 0, 0, 1) + + pa = pj.to_affine() + + self.assertIs(pa, INFINITY) + + def test_add_with_affine_point(self): + pj = PointJacobi.from_affine(generator_256) + pa = pj.to_affine() + + s = pj + pa + + self.assertEqual(s, pj.double()) + + def test_radd_with_affine_point(self): + pj = PointJacobi.from_affine(generator_256) + pa = pj.to_affine() + + s = pa + pj + + self.assertEqual(s, pj.double()) + + def test_add_with_infinity(self): + pj = PointJacobi.from_affine(generator_256) + + s = pj + INFINITY + + self.assertEqual(s, pj) + + def test_add_zero_point_to_affine(self): + pa = PointJacobi.from_affine(generator_256).to_affine() + pj = PointJacobi(curve_256, 0, 0, 1) + + s = pj + pa + + self.assertIs(s, pa) + + def test_multiply_by_zero(self): + pj = PointJacobi.from_affine(generator_256) + + pj = pj * 0 + + self.assertIs(pj, INFINITY) + + def test_zero_point_multiply_by_one(self): + pj = PointJacobi(curve_256, 0, 0, 1) + + pj = pj * 1 + + self.assertIs(pj, INFINITY) + + def test_multiply_by_one(self): + pj = PointJacobi.from_affine(generator_256) + pw = generator_256 * 1 + + pj = pj * 1 + + self.assertEqual(pj.x(), pw.x()) + self.assertEqual(pj.y(), pw.y()) + + def test_multiply_by_two(self): + pj = PointJacobi.from_affine(generator_256) + pw = generator_256 * 2 + + pj = pj * 2 + + self.assertEqual(pj.x(), pw.x()) + self.assertEqual(pj.y(), pw.y()) + + def test_rmul_by_two(self): + pj = PointJacobi.from_affine(generator_256) + pw = generator_256 * 2 + + pj = 2 * pj + + self.assertEqual(pj, pw) + + def test_compare_non_zero_with_infinity(self): + pj = PointJacobi.from_affine(generator_256) + + self.assertNotEqual(pj, INFINITY) + + def test_compare_zero_point_with_infinity(self): + pj = PointJacobi(curve_256, 0, 0, 1) + + self.assertEqual(pj, INFINITY) + + def test_compare_double_with_multiply(self): + pj = PointJacobi.from_affine(generator_256) + dbl = pj.double() + mlpl = pj * 2 + + self.assertEqual(dbl, mlpl) + + @settings(**SLOW_SETTINGS) + @given( + st.integers( + min_value=0, max_value=int(generator_brainpoolp160r1.order() - 1) + ) + ) + def test_multiplications(self, mul): + pj = PointJacobi.from_affine(generator_brainpoolp160r1) + pw = pj.to_affine() * mul + + pj = pj * mul + + self.assertEqual((pj.x(), pj.y()), (pw.x(), pw.y())) + self.assertEqual(pj, pw) + + @settings(**SLOW_SETTINGS) + @given( + st.integers( + min_value=0, max_value=int(generator_brainpoolp160r1.order() - 1) + ) + ) + @example(0) + @example(int(generator_brainpoolp160r1.order())) + def test_precompute(self, mul): + precomp = generator_brainpoolp160r1 + self.assertTrue(precomp._PointJacobi__precompute) + pj = PointJacobi.from_affine(generator_brainpoolp160r1) + + a = precomp * mul + b = pj * mul + + self.assertEqual(a, b) + + @settings(**SLOW_SETTINGS) + @given( + st.integers( + min_value=1, max_value=int(generator_brainpoolp160r1.order() - 1) + ), + st.integers( + min_value=1, max_value=int(generator_brainpoolp160r1.order() - 1) + ), + ) + @example(3, 3) + def test_add_scaled_points(self, a_mul, b_mul): + j_g = PointJacobi.from_affine(generator_brainpoolp160r1) + a = PointJacobi.from_affine(j_g * a_mul) + b = PointJacobi.from_affine(j_g * b_mul) + + c = a + b + + self.assertEqual(c, j_g * (a_mul + b_mul)) + + @settings(**SLOW_SETTINGS) + @given( + st.integers( + min_value=1, max_value=int(generator_brainpoolp160r1.order() - 1) + ), + st.integers( + min_value=1, max_value=int(generator_brainpoolp160r1.order() - 1) + ), + st.integers(min_value=1, max_value=int(curve_brainpoolp160r1.p() - 1)), + ) + def test_add_one_scaled_point(self, a_mul, b_mul, new_z): + j_g = PointJacobi.from_affine(generator_brainpoolp160r1) + a = PointJacobi.from_affine(j_g * a_mul) + b = PointJacobi.from_affine(j_g * b_mul) + + p = curve_brainpoolp160r1.p() + + assume(inverse_mod(new_z, p)) + + new_zz = new_z * new_z % p + + b = PointJacobi( + curve_brainpoolp160r1, + b.x() * new_zz % p, + b.y() * new_zz * new_z % p, + new_z, + ) + + c = a + b + + self.assertEqual(c, j_g * (a_mul + b_mul)) + + @pytest.mark.slow + @settings(**SLOW_SETTINGS) + @given( + st.integers( + min_value=1, max_value=int(generator_brainpoolp160r1.order() - 1) + ), + st.integers( + min_value=1, max_value=int(generator_brainpoolp160r1.order() - 1) + ), + st.integers(min_value=1, max_value=int(curve_brainpoolp160r1.p() - 1)), + ) + @example(1, 1, 1) + @example(3, 3, 3) + @example(2, int(generator_brainpoolp160r1.order() - 2), 1) + @example(2, int(generator_brainpoolp160r1.order() - 2), 3) + def test_add_same_scale_points(self, a_mul, b_mul, new_z): + j_g = PointJacobi.from_affine(generator_brainpoolp160r1) + a = PointJacobi.from_affine(j_g * a_mul) + b = PointJacobi.from_affine(j_g * b_mul) + + p = curve_brainpoolp160r1.p() + + assume(inverse_mod(new_z, p)) + + new_zz = new_z * new_z % p + + a = PointJacobi( + curve_brainpoolp160r1, + a.x() * new_zz % p, + a.y() * new_zz * new_z % p, + new_z, + ) + b = PointJacobi( + curve_brainpoolp160r1, + b.x() * new_zz % p, + b.y() * new_zz * new_z % p, + new_z, + ) + + c = a + b + + self.assertEqual(c, j_g * (a_mul + b_mul)) + + def test_add_same_scale_points_static(self): + j_g = generator_brainpoolp160r1 + p = curve_brainpoolp160r1.p() + a = j_g * 11 + a.scale() + z1 = 13 + x = PointJacobi( + curve_brainpoolp160r1, + a.x() * z1**2 % p, + a.y() * z1**3 % p, + z1, + ) + y = PointJacobi( + curve_brainpoolp160r1, + a.x() * z1**2 % p, + a.y() * z1**3 % p, + z1, + ) + + c = a + a + + self.assertEqual(c, x + y) + + @pytest.mark.slow + @settings(**SLOW_SETTINGS) + @given( + st.integers( + min_value=1, max_value=int(generator_brainpoolp160r1.order() - 1) + ), + st.integers( + min_value=1, max_value=int(generator_brainpoolp160r1.order() - 1) + ), + st.lists( + st.integers( + min_value=1, max_value=int(curve_brainpoolp160r1.p() - 1) + ), + min_size=2, + max_size=2, + unique=True, + ), + ) + @example(2, 2, [2, 1]) + @example(2, 2, [2, 3]) + @example(2, int(generator_brainpoolp160r1.order() - 2), [2, 3]) + @example(2, int(generator_brainpoolp160r1.order() - 2), [2, 1]) + def test_add_different_scale_points(self, a_mul, b_mul, new_z): + j_g = PointJacobi.from_affine(generator_brainpoolp160r1) + a = PointJacobi.from_affine(j_g * a_mul) + b = PointJacobi.from_affine(j_g * b_mul) + + p = curve_brainpoolp160r1.p() + + assume(inverse_mod(new_z[0], p)) + assume(inverse_mod(new_z[1], p)) + + new_zz0 = new_z[0] * new_z[0] % p + new_zz1 = new_z[1] * new_z[1] % p + + a = PointJacobi( + curve_brainpoolp160r1, + a.x() * new_zz0 % p, + a.y() * new_zz0 * new_z[0] % p, + new_z[0], + ) + b = PointJacobi( + curve_brainpoolp160r1, + b.x() * new_zz1 % p, + b.y() * new_zz1 * new_z[1] % p, + new_z[1], + ) + + c = a + b + + self.assertEqual(c, j_g * (a_mul + b_mul)) + + def test_add_different_scale_points_static(self): + j_g = generator_brainpoolp160r1 + p = curve_brainpoolp160r1.p() + a = j_g * 11 + a.scale() + z1 = 13 + x = PointJacobi( + curve_brainpoolp160r1, + a.x() * z1**2 % p, + a.y() * z1**3 % p, + z1, + ) + z2 = 29 + y = PointJacobi( + curve_brainpoolp160r1, + a.x() * z2**2 % p, + a.y() * z2**3 % p, + z2, + ) + + c = a + a + + self.assertEqual(c, x + y) + + def test_add_different_points_same_scale_static(self): + j_g = generator_brainpoolp160r1 + p = curve_brainpoolp160r1.p() + a = j_g * 11 + a.scale() + b = j_g * 12 + z = 13 + x = PointJacobi( + curve_brainpoolp160r1, + a.x() * z**2 % p, + a.y() * z**3 % p, + z, + ) + y = PointJacobi( + curve_brainpoolp160r1, + b.x() * z**2 % p, + b.y() * z**3 % p, + z, + ) + + c = a + b + + self.assertEqual(c, x + y) + + def test_add_same_point_different_scale_second_z_1_static(self): + j_g = generator_112r2 + p = curve_112r2.p() + z = 11 + a = j_g * z + a.scale() + + x = PointJacobi( + curve_112r2, + a.x() * z**2 % p, + a.y() * z**3 % p, + z, + ) + y = PointJacobi( + curve_112r2, + a.x(), + a.y(), + 1, + ) + + c = a + a + + self.assertEqual(c, x + y) + + def test_add_to_infinity_static(self): + j_g = generator_112r2 + + z = 11 + a = j_g * z + a.scale() + + b = -a + + x = PointJacobi( + curve_112r2, + a.x(), + a.y(), + 1, + ) + y = PointJacobi( + curve_112r2, + b.x(), + b.y(), + 1, + ) + + self.assertEqual(INFINITY, x + y) + + def test_add_point_3_times(self): + j_g = PointJacobi.from_affine(generator_256) + + self.assertEqual(j_g * 3, j_g + j_g + j_g) + + def test_mul_without_order(self): + j_g = PointJacobi(curve_256, generator_256.x(), generator_256.y(), 1) + + self.assertEqual(j_g * generator_256.order(), INFINITY) + + def test_mul_add_inf(self): + j_g = PointJacobi.from_affine(generator_256) + + self.assertEqual(j_g, j_g.mul_add(1, INFINITY, 1)) + + def test_mul_add_same(self): + j_g = PointJacobi.from_affine(generator_256) + + self.assertEqual(j_g * 2, j_g.mul_add(1, j_g, 1)) + + def test_mul_add_precompute(self): + j_g = PointJacobi.from_affine(generator_brainpoolp160r1, True) + b = PointJacobi.from_affine(j_g * 255, True) + + self.assertEqual(j_g * 256, j_g + b) + self.assertEqual(j_g * (5 + 255 * 7), j_g * 5 + b * 7) + self.assertEqual(j_g * (5 + 255 * 7), j_g.mul_add(5, b, 7)) + + def test_mul_add_precompute_large(self): + j_g = PointJacobi.from_affine(generator_brainpoolp160r1, True) + b = PointJacobi.from_affine(j_g * 255, True) + + self.assertEqual(j_g * 256, j_g + b) + self.assertEqual( + j_g * (0xFF00 + 255 * 0xF0F0), j_g * 0xFF00 + b * 0xF0F0 + ) + self.assertEqual( + j_g * (0xFF00 + 255 * 0xF0F0), j_g.mul_add(0xFF00, b, 0xF0F0) + ) + + def test_mul_add_to_mul(self): + j_g = PointJacobi.from_affine(generator_256) + + a = j_g * 3 + b = j_g.mul_add(2, j_g, 1) + + self.assertEqual(a, b) + + def test_mul_add_differnt(self): + j_g = PointJacobi.from_affine(generator_256) + + w_a = j_g * 2 + + self.assertEqual(j_g.mul_add(1, w_a, 1), j_g * 3) + + def test_mul_add_slightly_different(self): + j_g = PointJacobi.from_affine(generator_256) + + w_a = j_g * 2 + w_b = j_g * 3 + + self.assertEqual(w_a.mul_add(1, w_b, 3), w_a * 1 + w_b * 3) + + def test_mul_add(self): + j_g = PointJacobi.from_affine(generator_256) + + w_a = generator_256 * 255 + w_b = generator_256 * (0xA8 * 0xF0) + j_b = j_g * 0xA8 + + ret = j_g.mul_add(255, j_b, 0xF0) + + self.assertEqual(ret.to_affine(), w_a + w_b) + + def test_mul_add_large(self): + j_g = PointJacobi.from_affine(generator_256) + b = PointJacobi.from_affine(j_g * 255) + + self.assertEqual(j_g * 256, j_g + b) + self.assertEqual( + j_g * (0xFF00 + 255 * 0xF0F0), j_g * 0xFF00 + b * 0xF0F0 + ) + self.assertEqual( + j_g * (0xFF00 + 255 * 0xF0F0), j_g.mul_add(0xFF00, b, 0xF0F0) + ) + + def test_mul_add_with_infinity_as_result(self): + j_g = PointJacobi.from_affine(generator_256) + + order = generator_256.order() + + b = PointJacobi.from_affine(generator_256 * 256) + + self.assertEqual(j_g.mul_add(order % 256, b, order // 256), INFINITY) + + def test_mul_add_without_order(self): + j_g = PointJacobi(curve_256, generator_256.x(), generator_256.y(), 1) + + order = generator_256.order() + + w_b = generator_256 * 34 + w_b.scale() + + b = PointJacobi(curve_256, w_b.x(), w_b.y(), 1) + + self.assertEqual(j_g.mul_add(order % 34, b, order // 34), INFINITY) + + def test_mul_add_with_doubled_negation_of_itself(self): + j_g = PointJacobi.from_affine(generator_256 * 17) + + dbl_neg = 2 * (-j_g) + + self.assertEqual(j_g.mul_add(4, dbl_neg, 2), INFINITY) + + def test_equality(self): + pj1 = PointJacobi(curve=CurveFp(23, 1, 1, 1), x=2, y=3, z=1, order=1) + pj2 = PointJacobi(curve=CurveFp(23, 1, 1, 1), x=2, y=3, z=1, order=1) + self.assertEqual(pj1, pj2) + + def test_equality_with_invalid_object(self): + j_g = PointJacobi.from_affine(generator_256) + + self.assertNotEqual(j_g, 12) + + def test_equality_with_wrong_curves(self): + p_a = PointJacobi.from_affine(generator_256) + p_b = PointJacobi.from_affine(generator_224) + + self.assertNotEqual(p_a, p_b) + + def test_add_with_point_at_infinity(self): + pj1 = PointJacobi(curve=CurveFp(23, 1, 1, 1), x=2, y=3, z=1, order=1) + x, y, z = pj1._add(2, 3, 1, 5, 5, 0, 23) + + self.assertEqual((x, y, z), (2, 3, 1)) + + def test_pickle(self): + pj = PointJacobi(curve=CurveFp(23, 1, 1, 1), x=2, y=3, z=1, order=1) + self.assertEqual(pickle.loads(pickle.dumps(pj)), pj) + + @pytest.mark.slow + @settings(**NO_OLD_SETTINGS) + @pytest.mark.skipif( + platform.python_implementation() == "PyPy", + reason="threading on PyPy breaks coverage", + ) + @given(st.integers(min_value=1, max_value=10)) + def test_multithreading(self, thread_num): # pragma: no cover + # ensure that generator's precomputation table is filled + generator_112r2 * 2 + + # create a fresh point that doesn't have a filled precomputation table + gen = generator_112r2 + gen = PointJacobi(gen.curve(), gen.x(), gen.y(), 1, gen.order(), True) + + self.assertEqual(gen._PointJacobi__precompute, []) + + def runner(generator): + order = generator.order() + for _ in range(10): + generator * randrange(order) + + threads = [] + for _ in range(thread_num): + threads.append(threading.Thread(target=runner, args=(gen,))) + + for t in threads: + t.start() + + runner(gen) + + for t in threads: + t.join() + + self.assertEqual( + gen._PointJacobi__precompute, + generator_112r2._PointJacobi__precompute, + ) + + @pytest.mark.slow + @pytest.mark.skipif( + platform.system() == "Windows" + or platform.python_implementation() == "PyPy", + reason="there are no signals on Windows, and threading breaks coverage" + " on PyPy", + ) + def test_multithreading_with_interrupts(self): # pragma: no cover + thread_num = 10 + # ensure that generator's precomputation table is filled + generator_112r2 * 2 + + # create a fresh point that doesn't have a filled precomputation table + gen = generator_112r2 + gen = PointJacobi(gen.curve(), gen.x(), gen.y(), 1, gen.order(), True) + + self.assertEqual(gen._PointJacobi__precompute, []) + + def runner(generator): + order = generator.order() + for _ in range(50): + generator * randrange(order) + + def interrupter(barrier_start, barrier_end, lock_exit): + # wait until MainThread can handle KeyboardInterrupt + barrier_start.release() + barrier_end.acquire() + os.kill(os.getpid(), signal.SIGINT) + lock_exit.release() + + threads = [] + for _ in range(thread_num): + threads.append(threading.Thread(target=runner, args=(gen,))) + + barrier_start = threading.Lock() + barrier_start.acquire() + barrier_end = threading.Lock() + barrier_end.acquire() + lock_exit = threading.Lock() + lock_exit.acquire() + + threads.append( + threading.Thread( + target=interrupter, + args=(barrier_start, barrier_end, lock_exit), + ) + ) + + for t in threads: + t.start() + + with self.assertRaises(KeyboardInterrupt): + # signal to interrupter that we can now handle the signal + barrier_start.acquire() + barrier_end.release() + runner(gen) + # use the lock to ensure we never go past the scope of + # assertRaises before the os.kill is called + lock_exit.acquire() + + for t in threads: + t.join() + + self.assertEqual( + gen._PointJacobi__precompute, + generator_112r2._PointJacobi__precompute, + ) diff --git a/code/.venv/lib/python3.12/site-packages/ecdsa/test_keys.py b/code/.venv/lib/python3.12/site-packages/ecdsa/test_keys.py new file mode 100644 index 0000000..348475e --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/ecdsa/test_keys.py @@ -0,0 +1,1138 @@ +try: + import unittest2 as unittest +except ImportError: + import unittest + +try: + buffer +except NameError: + buffer = memoryview + +import os +import array +import pytest +import hashlib + +from .keys import ( + VerifyingKey, + SigningKey, + MalformedPointError, + BadSignatureError, +) +from .der import ( + unpem, + UnexpectedDER, + encode_sequence, + encode_oid, + encode_bitstring, + encode_integer, + encode_octet_string, +) +from .util import ( + sigencode_string, + sigencode_der, + sigencode_strings, + sigdecode_string, + sigdecode_der, + sigdecode_strings, +) +from .curves import NIST256p, Curve, BRAINPOOLP160r1, Ed25519, Ed448 +from .ellipticcurve import Point, PointJacobi, CurveFp, INFINITY +from .ecdsa import generator_brainpoolp160r1 + + +class TestVerifyingKeyFromString(unittest.TestCase): + """ + Verify that ecdsa.keys.VerifyingKey.from_string() can be used with + bytes-like objects + """ + + @classmethod + def setUpClass(cls): + cls.key_bytes = ( + b"\x04L\xa2\x95\xdb\xc7Z\xd7\x1f\x93\nz\xcf\x97\xcf" + b"\xd7\xc2\xd9o\xfe8}X!\xae\xd4\xfah\xfa^\rpI\xba\xd1" + b"Y\xfb\x92xa\xebo+\x9cG\xfav\xca" + ) + cls.vk = VerifyingKey.from_string(cls.key_bytes) + + def test_bytes(self): + self.assertIsNotNone(self.vk) + self.assertIsInstance(self.vk, VerifyingKey) + self.assertEqual( + self.vk.pubkey.point.x(), + 105419898848891948935835657980914000059957975659675736097, + ) + self.assertEqual( + self.vk.pubkey.point.y(), + 4286866841217412202667522375431381222214611213481632495306, + ) + + def test_bytes_memoryview(self): + vk = VerifyingKey.from_string(buffer(self.key_bytes)) + + self.assertEqual(self.vk.to_string(), vk.to_string()) + + def test_bytearray(self): + vk = VerifyingKey.from_string(bytearray(self.key_bytes)) + + self.assertEqual(self.vk.to_string(), vk.to_string()) + + def test_bytesarray_memoryview(self): + vk = VerifyingKey.from_string(buffer(bytearray(self.key_bytes))) + + self.assertEqual(self.vk.to_string(), vk.to_string()) + + def test_array_array_of_bytes(self): + arr = array.array("B", self.key_bytes) + vk = VerifyingKey.from_string(arr) + + self.assertEqual(self.vk.to_string(), vk.to_string()) + + def test_array_array_of_bytes_memoryview(self): + arr = array.array("B", self.key_bytes) + vk = VerifyingKey.from_string(buffer(arr)) + + self.assertEqual(self.vk.to_string(), vk.to_string()) + + def test_array_array_of_ints(self): + arr = array.array("I", self.key_bytes) + vk = VerifyingKey.from_string(arr) + + self.assertEqual(self.vk.to_string(), vk.to_string()) + + def test_array_array_of_ints_memoryview(self): + arr = array.array("I", self.key_bytes) + vk = VerifyingKey.from_string(buffer(arr)) + + self.assertEqual(self.vk.to_string(), vk.to_string()) + + def test_bytes_uncompressed(self): + vk = VerifyingKey.from_string(b"\x04" + self.key_bytes) + + self.assertEqual(self.vk.to_string(), vk.to_string()) + + def test_bytearray_uncompressed(self): + vk = VerifyingKey.from_string(bytearray(b"\x04" + self.key_bytes)) + + self.assertEqual(self.vk.to_string(), vk.to_string()) + + def test_bytes_compressed(self): + vk = VerifyingKey.from_string(b"\x02" + self.key_bytes[:24]) + + self.assertEqual(self.vk.to_string(), vk.to_string()) + + def test_bytearray_compressed(self): + vk = VerifyingKey.from_string(bytearray(b"\x02" + self.key_bytes[:24])) + + self.assertEqual(self.vk.to_string(), vk.to_string()) + + def test_ed25519_VerifyingKey_from_string_imported(self): + with self.assertRaises(MalformedPointError): + VerifyingKey.from_string(b"AAA", Ed25519) + + +class TestVerifyingKeyFromDer(unittest.TestCase): + """ + Verify that ecdsa.keys.VerifyingKey.from_der() can be used with + bytes-like objects. + """ + + @classmethod + def setUpClass(cls): + prv_key_str = ( + "-----BEGIN EC PRIVATE KEY-----\n" + "MF8CAQEEGF7IQgvW75JSqULpiQQ8op9WH6Uldw6xxaAKBggqhkjOPQMBAaE0AzIA\n" + "BLiBd9CE7xf15FY5QIAoNg+fWbSk1yZOYtoGUdzkejWkxbRc9RWTQjqLVXucIJnz\n" + "bA==\n" + "-----END EC PRIVATE KEY-----\n" + ) + key_str = ( + "-----BEGIN PUBLIC KEY-----\n" + "MEkwEwYHKoZIzj0CAQYIKoZIzj0DAQEDMgAEuIF30ITvF/XkVjlAgCg2D59ZtKTX\n" + "Jk5i2gZR3OR6NaTFtFz1FZNCOotVe5wgmfNs\n" + "-----END PUBLIC KEY-----\n" + ) + cls.key_pem = key_str + + cls.key_bytes = unpem(key_str) + assert isinstance(cls.key_bytes, bytes) + cls.vk = VerifyingKey.from_pem(key_str) + cls.sk = SigningKey.from_pem(prv_key_str) + + key_str = ( + "-----BEGIN PUBLIC KEY-----\n" + "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE4H3iRbG4TSrsSRb/gusPQB/4YcN8\n" + "Poqzgjau4kfxBPyZimeRfuY/9g/wMmPuhGl4BUve51DsnKJFRr8psk0ieA==\n" + "-----END PUBLIC KEY-----\n" + ) + cls.vk2 = VerifyingKey.from_pem(key_str) + + cls.sk2 = SigningKey.generate(vk.curve) + + def test_load_key_with_explicit_parameters(self): + pub_key_str = ( + "-----BEGIN PUBLIC KEY-----\n" + "MIIBSzCCAQMGByqGSM49AgEwgfcCAQEwLAYHKoZIzj0BAQIhAP////8AAAABAAAA\n" + "AAAAAAAAAAAA////////////////MFsEIP////8AAAABAAAAAAAAAAAAAAAA////\n" + "///////////8BCBaxjXYqjqT57PrvVV2mIa8ZR0GsMxTsPY7zjw+J9JgSwMVAMSd\n" + "NgiG5wSTamZ44ROdJreBn36QBEEEaxfR8uEsQkf4vOblY6RA8ncDfYEt6zOg9KE5\n" + "RdiYwpZP40Li/hp/m47n60p8D54WK84zV2sxXs7LtkBoN79R9QIhAP////8AAAAA\n" + "//////////+85vqtpxeehPO5ysL8YyVRAgEBA0IABIr1UkgYs5jmbFc7it1/YI2X\n" + "T//IlaEjMNZft1owjqpBYH2ErJHk4U5Pp4WvWq1xmHwIZlsH7Ig4KmefCfR6SmU=\n" + "-----END PUBLIC KEY-----" + ) + pk = VerifyingKey.from_pem(pub_key_str) + + pk_exp = VerifyingKey.from_string( + b"\x04\x8a\xf5\x52\x48\x18\xb3\x98\xe6\x6c\x57\x3b\x8a\xdd\x7f" + b"\x60\x8d\x97\x4f\xff\xc8\x95\xa1\x23\x30\xd6\x5f\xb7\x5a\x30" + b"\x8e\xaa\x41\x60\x7d\x84\xac\x91\xe4\xe1\x4e\x4f\xa7\x85\xaf" + b"\x5a\xad\x71\x98\x7c\x08\x66\x5b\x07\xec\x88\x38\x2a\x67\x9f" + b"\x09\xf4\x7a\x4a\x65", + curve=NIST256p, + ) + self.assertEqual(pk, pk_exp) + + def test_load_key_with_explicit_with_explicit_disabled(self): + pub_key_str = ( + "-----BEGIN PUBLIC KEY-----\n" + "MIIBSzCCAQMGByqGSM49AgEwgfcCAQEwLAYHKoZIzj0BAQIhAP////8AAAABAAAA\n" + "AAAAAAAAAAAA////////////////MFsEIP////8AAAABAAAAAAAAAAAAAAAA////\n" + "///////////8BCBaxjXYqjqT57PrvVV2mIa8ZR0GsMxTsPY7zjw+J9JgSwMVAMSd\n" + "NgiG5wSTamZ44ROdJreBn36QBEEEaxfR8uEsQkf4vOblY6RA8ncDfYEt6zOg9KE5\n" + "RdiYwpZP40Li/hp/m47n60p8D54WK84zV2sxXs7LtkBoN79R9QIhAP////8AAAAA\n" + "//////////+85vqtpxeehPO5ysL8YyVRAgEBA0IABIr1UkgYs5jmbFc7it1/YI2X\n" + "T//IlaEjMNZft1owjqpBYH2ErJHk4U5Pp4WvWq1xmHwIZlsH7Ig4KmefCfR6SmU=\n" + "-----END PUBLIC KEY-----" + ) + with self.assertRaises(UnexpectedDER): + VerifyingKey.from_pem( + pub_key_str, valid_curve_encodings=["named_curve"] + ) + + def test_load_key_with_disabled_format(self): + with self.assertRaises(MalformedPointError) as e: + VerifyingKey.from_der(self.key_bytes, valid_encodings=["raw"]) + + self.assertIn("enabled (raw) encodings", str(e.exception)) + + def test_custom_hashfunc(self): + vk = VerifyingKey.from_der(self.key_bytes, hashlib.sha256) + + self.assertIs(vk.default_hashfunc, hashlib.sha256) + + def test_from_pem_with_custom_hashfunc(self): + vk = VerifyingKey.from_pem(self.key_pem, hashlib.sha256) + + self.assertIs(vk.default_hashfunc, hashlib.sha256) + + def test_bytes(self): + vk = VerifyingKey.from_der(self.key_bytes) + + self.assertEqual(self.vk.to_string(), vk.to_string()) + + def test_bytes_memoryview(self): + vk = VerifyingKey.from_der(buffer(self.key_bytes)) + + self.assertEqual(self.vk.to_string(), vk.to_string()) + + def test_bytearray(self): + vk = VerifyingKey.from_der(bytearray(self.key_bytes)) + + self.assertEqual(self.vk.to_string(), vk.to_string()) + + def test_bytesarray_memoryview(self): + vk = VerifyingKey.from_der(buffer(bytearray(self.key_bytes))) + + self.assertEqual(self.vk.to_string(), vk.to_string()) + + def test_array_array_of_bytes(self): + arr = array.array("B", self.key_bytes) + vk = VerifyingKey.from_der(arr) + + self.assertEqual(self.vk.to_string(), vk.to_string()) + + def test_array_array_of_bytes_memoryview(self): + arr = array.array("B", self.key_bytes) + vk = VerifyingKey.from_der(buffer(arr)) + + self.assertEqual(self.vk.to_string(), vk.to_string()) + + def test_equality_on_verifying_keys(self): + self.assertTrue(self.vk == self.sk.get_verifying_key()) + + def test_inequality_on_verifying_keys(self): + self.assertFalse(self.vk == self.vk2) + + def test_inequality_on_verifying_keys_not_implemented(self): + self.assertFalse(self.vk == None) + + def test_VerifyingKey_inequality_on_same_curve(self): + self.assertNotEqual(self.vk, self.sk2.verifying_key) + + def test_SigningKey_inequality_on_same_curve(self): + self.assertNotEqual(self.sk, self.sk2) + + def test_inequality_on_wrong_types(self): + self.assertFalse(self.vk == self.sk) + + def test_from_public_point_old(self): + pj = self.vk.pubkey.point + point = Point(pj.curve(), pj.x(), pj.y()) + + vk = VerifyingKey.from_public_point(point, self.vk.curve) + + self.assertTrue(vk == self.vk) + + def test_ed25519_VerifyingKey_repr__(self): + sk = SigningKey.from_string(Ed25519.generator.to_bytes(), Ed25519) + string = repr(sk.verifying_key) + + self.assertEqual( + "VerifyingKey.from_string(" + "bytearray(b'K\\x0c\\xfbZH\\x8e\\x8c\\x8c\\x07\\xee\\xda\\xfb" + "\\xe1\\x97\\xcd\\x90\\x18\\x02\\x15h]\\xfe\\xbe\\xcbB\\xba\\xe6r" + "\\x10\\xae\\xf1P'), Ed25519, None)", + string, + ) + + def test_edwards_from_public_point(self): + point = Ed25519.generator + with self.assertRaises(ValueError) as e: + VerifyingKey.from_public_point(point, Ed25519) + + self.assertIn("incompatible with Edwards", str(e.exception)) + + def test_edwards_precompute_no_side_effect(self): + sk = SigningKey.from_string(Ed25519.generator.to_bytes(), Ed25519) + vk = sk.verifying_key + vk2 = VerifyingKey.from_string(vk.to_string(), Ed25519) + vk.precompute() + + self.assertEqual(vk, vk2) + + def test_parse_malfomed_eddsa_der_pubkey(self): + der_str = encode_sequence( + encode_sequence(encode_oid(*Ed25519.oid)), + encode_bitstring(bytes(Ed25519.generator.to_bytes()), 0), + encode_bitstring(b"\x00", 0), + ) + + with self.assertRaises(UnexpectedDER) as e: + VerifyingKey.from_der(der_str) + + self.assertIn("trailing junk after public key", str(e.exception)) + + def test_edwards_from_public_key_recovery(self): + with self.assertRaises(ValueError) as e: + VerifyingKey.from_public_key_recovery(b"", b"", Ed25519) + + self.assertIn("unsupported for Edwards", str(e.exception)) + + def test_edwards_from_public_key_recovery_with_digest(self): + with self.assertRaises(ValueError) as e: + VerifyingKey.from_public_key_recovery_with_digest( + b"", b"", Ed25519 + ) + + self.assertIn("unsupported for Edwards", str(e.exception)) + + def test_load_ed25519_from_pem(self): + vk_pem = ( + "-----BEGIN PUBLIC KEY-----\n" + "MCowBQYDK2VwAyEAIwBQ0NZkIiiO41WJfm5BV42u3kQm7lYnvIXmCy8qy2U=\n" + "-----END PUBLIC KEY-----\n" + ) + + vk = VerifyingKey.from_pem(vk_pem) + + self.assertIsInstance(vk.curve, Curve) + self.assertIs(vk.curve, Ed25519) + + vk_str = ( + b"\x23\x00\x50\xd0\xd6\x64\x22\x28\x8e\xe3\x55\x89\x7e\x6e\x41\x57" + b"\x8d\xae\xde\x44\x26\xee\x56\x27\xbc\x85\xe6\x0b\x2f\x2a\xcb\x65" + ) + + vk_2 = VerifyingKey.from_string(vk_str, Ed25519) + + self.assertEqual(vk, vk_2) + + def test_export_ed255_to_pem(self): + vk_str = ( + b"\x23\x00\x50\xd0\xd6\x64\x22\x28\x8e\xe3\x55\x89\x7e\x6e\x41\x57" + b"\x8d\xae\xde\x44\x26\xee\x56\x27\xbc\x85\xe6\x0b\x2f\x2a\xcb\x65" + ) + + vk = VerifyingKey.from_string(vk_str, Ed25519) + + vk_pem = ( + b"-----BEGIN PUBLIC KEY-----\n" + b"MCowBQYDK2VwAyEAIwBQ0NZkIiiO41WJfm5BV42u3kQm7lYnvIXmCy8qy2U=\n" + b"-----END PUBLIC KEY-----\n" + ) + + self.assertEqual(vk_pem, vk.to_pem()) + + def test_export_ed255_to_ssh(self): + vk_str = ( + b"\x23\x00\x50\xd0\xd6\x64\x22\x28\x8e\xe3\x55\x89\x7e\x6e\x41\x57" + b"\x8d\xae\xde\x44\x26\xee\x56\x27\xbc\x85\xe6\x0b\x2f\x2a\xcb\x65" + ) + + vk = VerifyingKey.from_string(vk_str, Ed25519) + + vk_ssh = b"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAICMAUNDWZCIojuNViX5uQVeNrt5EJu5WJ7yF5gsvKstl\n" + + self.assertEqual(vk_ssh, vk.to_ssh()) + + def test_ed25519_export_import(self): + sk = SigningKey.generate(Ed25519) + vk = sk.verifying_key + + vk2 = VerifyingKey.from_pem(vk.to_pem()) + + self.assertEqual(vk, vk2) + + def test_ed25519_sig_verify(self): + vk_pem = ( + "-----BEGIN PUBLIC KEY-----\n" + "MCowBQYDK2VwAyEAIwBQ0NZkIiiO41WJfm5BV42u3kQm7lYnvIXmCy8qy2U=\n" + "-----END PUBLIC KEY-----\n" + ) + + vk = VerifyingKey.from_pem(vk_pem) + + data = b"data\n" + + # signature created by OpenSSL 3.0.0 beta1 + sig = ( + b"\x64\x47\xab\x6a\x33\xcd\x79\x45\xad\x98\x11\x6c\xb9\xf2\x20\xeb" + b"\x90\xd6\x50\xe3\xc7\x8f\x9f\x60\x10\xec\x75\xe0\x2f\x27\xd3\x96" + b"\xda\xe8\x58\x7f\xe0\xfe\x46\x5c\x81\xef\x50\xec\x29\x9f\xae\xd5" + b"\xad\x46\x3c\x91\x68\x83\x4d\xea\x8d\xa8\x19\x04\x04\x79\x03\x0b" + ) + + self.assertTrue(vk.verify(sig, data)) + + def test_ed25519_sig_verify_malformed(self): + vk_pem = ( + "-----BEGIN PUBLIC KEY-----\n" + "MCowBQYDK2VwAyEAIwBQ0NZkIiiO41WJfm5BV42u3kQm7lYnvIXmCy8qy2U=\n" + "-----END PUBLIC KEY-----\n" + ) + + vk = VerifyingKey.from_pem(vk_pem) + + data = b"data\n" + + # modified signature from test_ed25519_sig_verify + sig = ( + b"\xAA\x47\xab\x6a\x33\xcd\x79\x45\xad\x98\x11\x6c\xb9\xf2\x20\xeb" + b"\x90\xd6\x50\xe3\xc7\x8f\x9f\x60\x10\xec\x75\xe0\x2f\x27\xd3\x96" + b"\xda\xe8\x58\x7f\xe0\xfe\x46\x5c\x81\xef\x50\xec\x29\x9f\xae\xd5" + b"\xad\x46\x3c\x91\x68\x83\x4d\xea\x8d\xa8\x19\x04\x04\x79\x03\x0b" + ) + + with self.assertRaises(BadSignatureError): + vk.verify(sig, data) + + def test_ed448_from_pem(self): + pem_str = ( + "-----BEGIN PUBLIC KEY-----\n" + "MEMwBQYDK2VxAzoAeQtetSu7CMEzE+XWB10Bg47LCA0giNikOxHzdp+tZ/eK/En0\n" + "dTdYD2ll94g58MhSnBiBQB9A1MMA\n" + "-----END PUBLIC KEY-----\n" + ) + + vk = VerifyingKey.from_pem(pem_str) + + self.assertIsInstance(vk.curve, Curve) + self.assertIs(vk.curve, Ed448) + + vk_str = ( + b"\x79\x0b\x5e\xb5\x2b\xbb\x08\xc1\x33\x13\xe5\xd6\x07\x5d\x01\x83" + b"\x8e\xcb\x08\x0d\x20\x88\xd8\xa4\x3b\x11\xf3\x76\x9f\xad\x67\xf7" + b"\x8a\xfc\x49\xf4\x75\x37\x58\x0f\x69\x65\xf7\x88\x39\xf0\xc8\x52" + b"\x9c\x18\x81\x40\x1f\x40\xd4\xc3\x00" + ) + + vk2 = VerifyingKey.from_string(vk_str, Ed448) + + self.assertEqual(vk, vk2) + + def test_ed448_to_pem(self): + vk_str = ( + b"\x79\x0b\x5e\xb5\x2b\xbb\x08\xc1\x33\x13\xe5\xd6\x07\x5d\x01\x83" + b"\x8e\xcb\x08\x0d\x20\x88\xd8\xa4\x3b\x11\xf3\x76\x9f\xad\x67\xf7" + b"\x8a\xfc\x49\xf4\x75\x37\x58\x0f\x69\x65\xf7\x88\x39\xf0\xc8\x52" + b"\x9c\x18\x81\x40\x1f\x40\xd4\xc3\x00" + ) + vk = VerifyingKey.from_string(vk_str, Ed448) + + vk_pem = ( + b"-----BEGIN PUBLIC KEY-----\n" + b"MEMwBQYDK2VxAzoAeQtetSu7CMEzE+XWB10Bg47LCA0giNikOxHzdp+tZ/eK/En0dTdYD2ll94g5\n" + b"8MhSnBiBQB9A1MMA\n" + b"-----END PUBLIC KEY-----\n" + ) + + self.assertEqual(vk_pem, vk.to_pem()) + + def test_ed448_export_import(self): + sk = SigningKey.generate(Ed448) + vk = sk.verifying_key + + vk2 = VerifyingKey.from_pem(vk.to_pem()) + + self.assertEqual(vk, vk2) + + def test_ed448_sig_verify(self): + pem_str = ( + "-----BEGIN PUBLIC KEY-----\n" + "MEMwBQYDK2VxAzoAeQtetSu7CMEzE+XWB10Bg47LCA0giNikOxHzdp+tZ/eK/En0\n" + "dTdYD2ll94g58MhSnBiBQB9A1MMA\n" + "-----END PUBLIC KEY-----\n" + ) + + vk = VerifyingKey.from_pem(pem_str) + + data = b"data\n" + + # signature created by OpenSSL 3.0.0 beta1 + sig = ( + b"\x68\xed\x2c\x70\x35\x22\xca\x1c\x35\x03\xf3\xaa\x51\x33\x3d\x00" + b"\xc0\xae\xb0\x54\xc5\xdc\x7f\x6f\x30\x57\xb4\x1d\xcb\xe9\xec\xfa" + b"\xc8\x45\x3e\x51\xc1\xcb\x60\x02\x6a\xd0\x43\x11\x0b\x5f\x9b\xfa" + b"\x32\x88\xb2\x38\x6b\xed\xac\x09\x00\x78\xb1\x7b\x5d\x7e\xf8\x16" + b"\x31\xdd\x1b\x3f\x98\xa0\xce\x19\xe7\xd8\x1c\x9f\x30\xac\x2f\xd4" + b"\x1e\x55\xbf\x21\x98\xf6\x4c\x8c\xbe\x81\xa5\x2d\x80\x4c\x62\x53" + b"\x91\xd5\xee\x03\x30\xc6\x17\x66\x4b\x9e\x0c\x8d\x40\xd0\xad\xae" + b"\x0a\x00" + ) + + self.assertTrue(vk.verify(sig, data)) + + +class TestSigningKey(unittest.TestCase): + """ + Verify that ecdsa.keys.SigningKey.from_der() can be used with + bytes-like objects. + """ + + @classmethod + def setUpClass(cls): + prv_key_str = ( + "-----BEGIN EC PRIVATE KEY-----\n" + "MF8CAQEEGF7IQgvW75JSqULpiQQ8op9WH6Uldw6xxaAKBggqhkjOPQMBAaE0AzIA\n" + "BLiBd9CE7xf15FY5QIAoNg+fWbSk1yZOYtoGUdzkejWkxbRc9RWTQjqLVXucIJnz\n" + "bA==\n" + "-----END EC PRIVATE KEY-----\n" + ) + cls.sk1 = SigningKey.from_pem(prv_key_str) + + prv_key_str = ( + "-----BEGIN PRIVATE KEY-----\n" + "MG8CAQAwEwYHKoZIzj0CAQYIKoZIzj0DAQEEVTBTAgEBBBheyEIL1u+SUqlC6YkE\n" + "PKKfVh+lJXcOscWhNAMyAAS4gXfQhO8X9eRWOUCAKDYPn1m0pNcmTmLaBlHc5Ho1\n" + "pMW0XPUVk0I6i1V7nCCZ82w=\n" + "-----END PRIVATE KEY-----\n" + ) + cls.sk1_pkcs8 = SigningKey.from_pem(prv_key_str) + + prv_key_str = ( + "-----BEGIN EC PRIVATE KEY-----\n" + "MHcCAQEEIKlL2EAm5NPPZuXwxRf4nXMk0A80y6UUbiQ17be/qFhRoAoGCCqGSM49\n" + "AwEHoUQDQgAE4H3iRbG4TSrsSRb/gusPQB/4YcN8Poqzgjau4kfxBPyZimeRfuY/\n" + "9g/wMmPuhGl4BUve51DsnKJFRr8psk0ieA==\n" + "-----END EC PRIVATE KEY-----\n" + ) + cls.sk2 = SigningKey.from_pem(prv_key_str) + + def test_to_der_pkcs8(self): + self.assertEqual( + self.sk1.to_der(format="pkcs8"), + b"0o\x02\x01\x010\x13\x06\x07*\x86H\xce=\x02\x01\x06\x08*\x86H" + b"\xce=\x03\x01\x01\x04U0S\x02\x01\x01\x04\x18^\xc8B\x0b\xd6\xef" + b"\x92R\xa9B\xe9\x89\x04<\xa2\x9fV\x1f\xa5%w\x0e\xb1\xc5\xa14\x03" + b"2\x00\x04\xb8\x81w\xd0\x84\xef\x17\xf5\xe4V9@\x80(6\x0f\x9fY" + b"\xb4\xa4\xd7&Nb\xda\x06Q\xdc\xe4z5\xa4\xc5\xb4\\\xf5\x15\x93B:" + b"\x8bU{\x9c \x99\xf3l", + ) + + def test_decoding_explicit_curve_parameters(self): + prv_key_str = ( + "-----BEGIN PRIVATE KEY-----\n" + "MIIBeQIBADCCAQMGByqGSM49AgEwgfcCAQEwLAYHKoZIzj0BAQIhAP////8AAAAB\n" + "AAAAAAAAAAAAAAAA////////////////MFsEIP////8AAAABAAAAAAAAAAAAAAAA\n" + "///////////////8BCBaxjXYqjqT57PrvVV2mIa8ZR0GsMxTsPY7zjw+J9JgSwMV\n" + "AMSdNgiG5wSTamZ44ROdJreBn36QBEEEaxfR8uEsQkf4vOblY6RA8ncDfYEt6zOg\n" + "9KE5RdiYwpZP40Li/hp/m47n60p8D54WK84zV2sxXs7LtkBoN79R9QIhAP////8A\n" + "AAAA//////////+85vqtpxeehPO5ysL8YyVRAgEBBG0wawIBAQQgIXtREfUmR16r\n" + "ZbmvDGD2lAEFPZa2DLPyz0czSja58yChRANCAASK9VJIGLOY5mxXO4rdf2CNl0//\n" + "yJWhIzDWX7daMI6qQWB9hKyR5OFOT6eFr1qtcZh8CGZbB+yIOCpnnwn0ekpl\n" + "-----END PRIVATE KEY-----\n" + ) + + sk = SigningKey.from_pem(prv_key_str) + + sk2 = SigningKey.from_string( + b"\x21\x7b\x51\x11\xf5\x26\x47\x5e\xab\x65\xb9\xaf\x0c\x60\xf6" + b"\x94\x01\x05\x3d\x96\xb6\x0c\xb3\xf2\xcf\x47\x33\x4a\x36\xb9" + b"\xf3\x20", + curve=NIST256p, + ) + + self.assertEqual(sk, sk2) + + def test_decoding_explicit_curve_parameters_with_explicit_disabled(self): + prv_key_str = ( + "-----BEGIN PRIVATE KEY-----\n" + "MIIBeQIBADCCAQMGByqGSM49AgEwgfcCAQEwLAYHKoZIzj0BAQIhAP////8AAAAB\n" + "AAAAAAAAAAAAAAAA////////////////MFsEIP////8AAAABAAAAAAAAAAAAAAAA\n" + "///////////////8BCBaxjXYqjqT57PrvVV2mIa8ZR0GsMxTsPY7zjw+J9JgSwMV\n" + "AMSdNgiG5wSTamZ44ROdJreBn36QBEEEaxfR8uEsQkf4vOblY6RA8ncDfYEt6zOg\n" + "9KE5RdiYwpZP40Li/hp/m47n60p8D54WK84zV2sxXs7LtkBoN79R9QIhAP////8A\n" + "AAAA//////////+85vqtpxeehPO5ysL8YyVRAgEBBG0wawIBAQQgIXtREfUmR16r\n" + "ZbmvDGD2lAEFPZa2DLPyz0czSja58yChRANCAASK9VJIGLOY5mxXO4rdf2CNl0//\n" + "yJWhIzDWX7daMI6qQWB9hKyR5OFOT6eFr1qtcZh8CGZbB+yIOCpnnwn0ekpl\n" + "-----END PRIVATE KEY-----\n" + ) + + with self.assertRaises(UnexpectedDER): + SigningKey.from_pem( + prv_key_str, valid_curve_encodings=["named_curve"] + ) + + def test_equality_on_signing_keys(self): + sk = SigningKey.from_secret_exponent( + self.sk1.privkey.secret_multiplier, self.sk1.curve + ) + self.assertEqual(self.sk1, sk) + self.assertEqual(self.sk1_pkcs8, sk) + + def test_verify_with_empty_message(self): + sig = self.sk1.sign(b"") + + self.assertTrue(sig) + + vk = self.sk1.verifying_key + + self.assertTrue(vk.verify(sig, b"")) + + def test_verify_with_precompute(self): + sig = self.sk1.sign(b"message") + + vk = self.sk1.verifying_key + + vk.precompute() + + self.assertTrue(vk.verify(sig, b"message")) + + def test_compare_verifying_key_with_precompute(self): + vk1 = self.sk1.verifying_key + vk1.precompute() + + vk2 = self.sk1_pkcs8.verifying_key + + self.assertEqual(vk1, vk2) + + def test_verify_with_lazy_precompute(self): + sig = self.sk2.sign(b"other message") + + vk = self.sk2.verifying_key + + vk.precompute(lazy=True) + + self.assertTrue(vk.verify(sig, b"other message")) + + def test_inequality_on_signing_keys(self): + self.assertNotEqual(self.sk1, self.sk2) + + def test_inequality_on_signing_keys_not_implemented(self): + self.assertNotEqual(self.sk1, None) + + def test_ed25519_from_pem(self): + pem_str = ( + "-----BEGIN PRIVATE KEY-----\n" + "MC4CAQAwBQYDK2VwBCIEIDS6x9FO1PG8T4xIPg8Zd0z8uL6sVGZFEZrX17gHC/XU\n" + "-----END PRIVATE KEY-----\n" + ) + + sk = SigningKey.from_pem(pem_str) + + sk_str = SigningKey.from_string( + b"\x34\xBA\xC7\xD1\x4E\xD4\xF1\xBC\x4F\x8C\x48\x3E\x0F\x19\x77\x4C" + b"\xFC\xB8\xBE\xAC\x54\x66\x45\x11\x9A\xD7\xD7\xB8\x07\x0B\xF5\xD4", + Ed25519, + ) + + self.assertEqual(sk, sk_str) + + def test_ed25519_from_der_bad_alg_id_params(self): + der_str = encode_sequence( + encode_integer(1), + encode_sequence(encode_oid(*Ed25519.oid), encode_integer(1)), + encode_octet_string(encode_octet_string(b"A" * 32)), + ) + + with self.assertRaises(UnexpectedDER) as e: + SigningKey.from_der(der_str) + + self.assertIn("Non NULL parameters", str(e.exception)) + + def test_ed25519_from_der_junk_after_priv_key(self): + der_str = encode_sequence( + encode_integer(1), + encode_sequence( + encode_oid(*Ed25519.oid), + ), + encode_octet_string(encode_octet_string(b"A" * 32) + b"B"), + ) + + with self.assertRaises(UnexpectedDER) as e: + SigningKey.from_der(der_str) + + self.assertIn( + "trailing junk after the encoded private key", str(e.exception) + ) + + def test_ed25519_sign(self): + sk_str = SigningKey.from_string( + b"\x34\xBA\xC7\xD1\x4E\xD4\xF1\xBC\x4F\x8C\x48\x3E\x0F\x19\x77\x4C" + b"\xFC\xB8\xBE\xAC\x54\x66\x45\x11\x9A\xD7\xD7\xB8\x07\x0B\xF5\xD4", + Ed25519, + ) + + msg = b"message" + + sig = sk_str.sign(msg, sigencode=sigencode_der) + + self.assertEqual( + sig, + b"\xe1,v\xc9>%\xda\xd2~>\xc3&\na\xf4@|\x9e`X\x11\x13@<\x987\xd4" + b"\r\xb1\xf5\xb3\x15\x7f%i{\xdf}\xdd\xb1\xf3\x02\x7f\x80\x02\xc2" + b'|\xe5\xd6\x06\xc4\n\xa3\xb0\xf6}\xc0\xed)"+E\xaf\x00', + ) + + def test_ed25519_sign_digest_deterministic(self): + sk_str = SigningKey.from_string( + b"\x34\xBA\xC7\xD1\x4E\xD4\xF1\xBC\x4F\x8C\x48\x3E\x0F\x19\x77\x4C" + b"\xFC\xB8\xBE\xAC\x54\x66\x45\x11\x9A\xD7\xD7\xB8\x07\x0B\xF5\xD4", + Ed25519, + ) + with self.assertRaises(ValueError) as e: + sk_str.sign_digest_deterministic(b"a" * 20) + + self.assertIn("Method unsupported for Edwards", str(e.exception)) + + def test_ed25519_sign_digest(self): + sk_str = SigningKey.from_string( + b"\x34\xBA\xC7\xD1\x4E\xD4\xF1\xBC\x4F\x8C\x48\x3E\x0F\x19\x77\x4C" + b"\xFC\xB8\xBE\xAC\x54\x66\x45\x11\x9A\xD7\xD7\xB8\x07\x0B\xF5\xD4", + Ed25519, + ) + with self.assertRaises(ValueError) as e: + sk_str.sign_digest(b"a" * 20) + + self.assertIn("Method unsupported for Edwards", str(e.exception)) + + def test_ed25519_sign_number(self): + sk_str = SigningKey.from_string( + b"\x34\xBA\xC7\xD1\x4E\xD4\xF1\xBC\x4F\x8C\x48\x3E\x0F\x19\x77\x4C" + b"\xFC\xB8\xBE\xAC\x54\x66\x45\x11\x9A\xD7\xD7\xB8\x07\x0B\xF5\xD4", + Ed25519, + ) + with self.assertRaises(ValueError) as e: + sk_str.sign_number(20) + + self.assertIn("Method unsupported for Edwards", str(e.exception)) + + def test_ed25519_to_der_ssleay(self): + pem_str = ( + "-----BEGIN PRIVATE KEY-----\n" + "MC4CAQAwBQYDK2VwBCIEIDS6x9FO1PG8T4xIPg8Zd0z8uL6sVGZFEZrX17gHC/XU\n" + "-----END PRIVATE KEY-----\n" + ) + + sk = SigningKey.from_pem(pem_str) + + with self.assertRaises(ValueError) as e: + sk.to_der(format="ssleay") + + self.assertIn("Only PKCS#8 format", str(e.exception)) + + def test_ed25519_to_pem(self): + sk = SigningKey.from_string( + b"\x34\xBA\xC7\xD1\x4E\xD4\xF1\xBC\x4F\x8C\x48\x3E\x0F\x19\x77\x4C" + b"\xFC\xB8\xBE\xAC\x54\x66\x45\x11\x9A\xD7\xD7\xB8\x07\x0B\xF5\xD4", + Ed25519, + ) + + pem_str = ( + b"-----BEGIN PRIVATE KEY-----\n" + b"MC4CAQAwBQYDK2VwBCIEIDS6x9FO1PG8T4xIPg8Zd0z8uL6sVGZFEZrX17gHC/XU\n" + b"-----END PRIVATE KEY-----\n" + ) + + self.assertEqual(sk.to_pem(format="pkcs8"), pem_str) + + def test_ed25519_to_ssh(self): + sk = SigningKey.from_string( + b"\x34\xBA\xC7\xD1\x4E\xD4\xF1\xBC\x4F\x8C\x48\x3E\x0F\x19\x77\x4C" + b"\xFC\xB8\xBE\xAC\x54\x66\x45\x11\x9A\xD7\xD7\xB8\x07\x0B\xF5\xD4", + Ed25519, + ) + + ssh_str = ( + b"-----BEGIN OPENSSH PRIVATE KEY-----\n" + b"b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZWQyNTUx\n" + b"OQAAACAjAFDQ1mQiKI7jVYl+bkFXja7eRCbuVie8heYLLyrLZQAAAIgAAAAAAAAAAAAAAAtzc2gt\n" + b"ZWQyNTUxOQAAACAjAFDQ1mQiKI7jVYl+bkFXja7eRCbuVie8heYLLyrLZQAAAEA0usfRTtTxvE+M\n" + b"SD4PGXdM/Li+rFRmRRGa19e4Bwv11CMAUNDWZCIojuNViX5uQVeNrt5EJu5WJ7yF5gsvKstlAAAA\n" + b"AAECAwQF\n" + b"-----END OPENSSH PRIVATE KEY-----\n" + ) + + self.assertEqual(sk.to_ssh(), ssh_str) + + def test_ed25519_to_and_from_pem(self): + sk = SigningKey.generate(Ed25519) + + decoded = SigningKey.from_pem(sk.to_pem(format="pkcs8")) + + self.assertEqual(sk, decoded) + + def test_ed25519_custom_entropy(self): + sk = SigningKey.generate(Ed25519, entropy=os.urandom) + + self.assertIsNotNone(sk) + + def test_ed25519_from_secret_exponent(self): + with self.assertRaises(ValueError) as e: + SigningKey.from_secret_exponent(1234567890, curve=Ed25519) + + self.assertIn("don't support setting the secret", str(e.exception)) + + def test_ed448_from_pem(self): + pem_str = ( + "-----BEGIN PRIVATE KEY-----\n" + "MEcCAQAwBQYDK2VxBDsEOTyFuXqFLXgJlV8uDqcOw9nG4IqzLiZ/i5NfBDoHPzmP\n" + "OP0JMYaLGlTzwovmvCDJ2zLaezu9NLz9aQ==\n" + "-----END PRIVATE KEY-----\n" + ) + sk = SigningKey.from_pem(pem_str) + + sk_str = SigningKey.from_string( + b"\x3C\x85\xB9\x7A\x85\x2D\x78\x09\x95\x5F\x2E\x0E\xA7\x0E\xC3\xD9" + b"\xC6\xE0\x8A\xB3\x2E\x26\x7F\x8B\x93\x5F\x04\x3A\x07\x3F\x39\x8F" + b"\x38\xFD\x09\x31\x86\x8B\x1A\x54\xF3\xC2\x8B\xE6\xBC\x20\xC9\xDB" + b"\x32\xDA\x7B\x3B\xBD\x34\xBC\xFD\x69", + Ed448, + ) + + self.assertEqual(sk, sk_str) + + def test_ed448_to_pem(self): + sk = SigningKey.from_string( + b"\x3C\x85\xB9\x7A\x85\x2D\x78\x09\x95\x5F\x2E\x0E\xA7\x0E\xC3\xD9" + b"\xC6\xE0\x8A\xB3\x2E\x26\x7F\x8B\x93\x5F\x04\x3A\x07\x3F\x39\x8F" + b"\x38\xFD\x09\x31\x86\x8B\x1A\x54\xF3\xC2\x8B\xE6\xBC\x20\xC9\xDB" + b"\x32\xDA\x7B\x3B\xBD\x34\xBC\xFD\x69", + Ed448, + ) + pem_str = ( + b"-----BEGIN PRIVATE KEY-----\n" + b"MEcCAQAwBQYDK2VxBDsEOTyFuXqFLXgJlV8uDqcOw9nG4IqzLiZ/i5NfBDoHPzmPOP0JMYaLGlTz\n" + b"wovmvCDJ2zLaezu9NLz9aQ==\n" + b"-----END PRIVATE KEY-----\n" + ) + + self.assertEqual(sk.to_pem(format="pkcs8"), pem_str) + + def test_ed448_encode_decode(self): + sk = SigningKey.generate(Ed448) + + decoded = SigningKey.from_pem(sk.to_pem(format="pkcs8")) + + self.assertEqual(decoded, sk) + + +class TestTrivialCurve(unittest.TestCase): + @classmethod + def setUpClass(cls): + # To test what happens with r or s in signing happens to be zero we + # need to find a scalar that creates one of the points on a curve that + # has x coordinate equal to zero. + # Even for secp112r2 curve that's non trivial so use this toy + # curve, for which we can iterate over all points quickly + curve = CurveFp(163, 84, 58) + gen = PointJacobi(curve, 2, 87, 1, 167, generator=True) + + cls.toy_curve = Curve("toy_p8", curve, gen, (1, 2, 0)) + + cls.sk = SigningKey.from_secret_exponent( + 140, + cls.toy_curve, + hashfunc=hashlib.sha1, + ) + + def test_generator_sanity(self): + gen = self.toy_curve.generator + + self.assertEqual(gen * gen.order(), INFINITY) + + def test_public_key_sanity(self): + self.assertEqual(self.sk.verifying_key.to_string(), b"\x98\x1e") + + def test_deterministic_sign(self): + sig = self.sk.sign_deterministic(b"message") + + self.assertEqual(sig, b"-.") + + self.assertTrue(self.sk.verifying_key.verify(sig, b"message")) + + def test_deterministic_sign_random_message(self): + msg = os.urandom(32) + sig = self.sk.sign_deterministic(msg) + self.assertEqual(len(sig), 2) + self.assertTrue(self.sk.verifying_key.verify(sig, msg)) + + def test_deterministic_sign_that_rises_R_zero_error(self): + # the raised RSZeroError is caught and handled internally by + # sign_deterministic methods + msg = b"\x00\x4f" + sig = self.sk.sign_deterministic(msg) + self.assertEqual(sig, b"\x36\x9e") + self.assertTrue(self.sk.verifying_key.verify(sig, msg)) + + def test_deterministic_sign_that_rises_S_zero_error(self): + msg = b"\x01\x6d" + sig = self.sk.sign_deterministic(msg) + self.assertEqual(sig, b"\x49\x6c") + self.assertTrue(self.sk.verifying_key.verify(sig, msg)) + + +# test VerifyingKey.verify() +prv_key_str = ( + "-----BEGIN EC PRIVATE KEY-----\n" + "MF8CAQEEGF7IQgvW75JSqULpiQQ8op9WH6Uldw6xxaAKBggqhkjOPQMBAaE0AzIA\n" + "BLiBd9CE7xf15FY5QIAoNg+fWbSk1yZOYtoGUdzkejWkxbRc9RWTQjqLVXucIJnz\n" + "bA==\n" + "-----END EC PRIVATE KEY-----\n" +) +key_bytes = unpem(prv_key_str) +assert isinstance(key_bytes, bytes) +sk = SigningKey.from_der(key_bytes) +vk = sk.verifying_key + +data = ( + b"some string for signing" + b"contents don't really matter" + b"but do include also some crazy values: " + b"\x00\x01\t\r\n\x00\x00\x00\xff\xf0" +) +assert len(data) % 4 == 0 +sha1 = hashlib.sha1() +sha1.update(data) +data_hash = sha1.digest() +assert isinstance(data_hash, bytes) +sig_raw = sk.sign(data, sigencode=sigencode_string) +assert isinstance(sig_raw, bytes) +sig_der = sk.sign(data, sigencode=sigencode_der) +assert isinstance(sig_der, bytes) +sig_strings = sk.sign(data, sigencode=sigencode_strings) +assert isinstance(sig_strings[0], bytes) + +verifiers = [] +for modifier, fun in [ + ("bytes", lambda x: x), + ("bytes memoryview", buffer), + ("bytearray", bytearray), + ("bytearray memoryview", lambda x: buffer(bytearray(x))), + ("array.array of bytes", lambda x: array.array("B", x)), + ("array.array of bytes memoryview", lambda x: buffer(array.array("B", x))), + ("array.array of ints", lambda x: array.array("I", x)), + ("array.array of ints memoryview", lambda x: buffer(array.array("I", x))), +]: + if "ints" in modifier: + conv = lambda x: x + else: + conv = fun + for sig_format, signature, decoder, mod_apply in [ + ("raw", sig_raw, sigdecode_string, lambda x: conv(x)), + ("der", sig_der, sigdecode_der, lambda x: conv(x)), + ( + "strings", + sig_strings, + sigdecode_strings, + lambda x: tuple(conv(i) for i in x), + ), + ]: + for method_name, vrf_mthd, vrf_data in [ + ("verify", vk.verify, data), + ("verify_digest", vk.verify_digest, data_hash), + ]: + verifiers.append( + pytest.param( + signature, + decoder, + mod_apply, + fun, + vrf_mthd, + vrf_data, + id="{2}-{0}-{1}".format(modifier, sig_format, method_name), + ) + ) + + +@pytest.mark.parametrize( + "signature,decoder,mod_apply,fun,vrf_mthd,vrf_data", verifiers +) +def test_VerifyingKey_verify( + signature, decoder, mod_apply, fun, vrf_mthd, vrf_data +): + sig = mod_apply(signature) + + assert vrf_mthd(sig, fun(vrf_data), sigdecode=decoder) + + +# test SigningKey.from_string() +prv_key_bytes = ( + b"^\xc8B\x0b\xd6\xef\x92R\xa9B\xe9\x89\x04<\xa2" + b"\x9fV\x1f\xa5%w\x0e\xb1\xc5" +) +assert len(prv_key_bytes) == 24 +converters = [] +for modifier, convert in [ + ("bytes", lambda x: x), + ("bytes memoryview", buffer), + ("bytearray", bytearray), + ("bytearray memoryview", lambda x: buffer(bytearray(x))), + ("array.array of bytes", lambda x: array.array("B", x)), + ("array.array of bytes memoryview", lambda x: buffer(array.array("B", x))), + ("array.array of ints", lambda x: array.array("I", x)), + ("array.array of ints memoryview", lambda x: buffer(array.array("I", x))), +]: + converters.append(pytest.param(convert, id=modifier)) + + +@pytest.mark.parametrize("convert", converters) +def test_SigningKey_from_string(convert): + key = convert(prv_key_bytes) + sk = SigningKey.from_string(key) + + assert sk.to_string() == prv_key_bytes + + +# test SigningKey.from_der() +prv_key_str = ( + "-----BEGIN EC PRIVATE KEY-----\n" + "MF8CAQEEGF7IQgvW75JSqULpiQQ8op9WH6Uldw6xxaAKBggqhkjOPQMBAaE0AzIA\n" + "BLiBd9CE7xf15FY5QIAoNg+fWbSk1yZOYtoGUdzkejWkxbRc9RWTQjqLVXucIJnz\n" + "bA==\n" + "-----END EC PRIVATE KEY-----\n" +) +key_bytes = unpem(prv_key_str) +assert isinstance(key_bytes, bytes) + +# last two converters are for array.array of ints, those require input +# that's multiple of 4, which no curve we support produces +@pytest.mark.parametrize("convert", converters[:-2]) +def test_SigningKey_from_der(convert): + key = convert(key_bytes) + sk = SigningKey.from_der(key) + + assert sk.to_string() == prv_key_bytes + + +# test SigningKey.sign_deterministic() +extra_entropy = b"\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11" + + +@pytest.mark.parametrize("convert", converters) +def test_SigningKey_sign_deterministic(convert): + sig = sk.sign_deterministic( + convert(data), extra_entropy=convert(extra_entropy) + ) + + vk.verify(sig, data) + + +# test SigningKey.sign_digest_deterministic() +@pytest.mark.parametrize("convert", converters) +def test_SigningKey_sign_digest_deterministic(convert): + sig = sk.sign_digest_deterministic( + convert(data_hash), extra_entropy=convert(extra_entropy) + ) + + vk.verify(sig, data) + + +@pytest.mark.parametrize("convert", converters) +def test_SigningKey_sign(convert): + sig = sk.sign(convert(data)) + + vk.verify(sig, data) + + +@pytest.mark.parametrize("convert", converters) +def test_SigningKey_sign_digest(convert): + sig = sk.sign_digest(convert(data_hash)) + + vk.verify(sig, data) + + +def test_SigningKey_with_unlikely_value(): + sk = SigningKey.from_secret_exponent(NIST256p.order - 1, curve=NIST256p) + vk = sk.verifying_key + sig = sk.sign(b"hello") + assert vk.verify(sig, b"hello") + + +def test_SigningKey_with_custom_curve_old_point(): + generator = generator_brainpoolp160r1 + generator = Point( + generator.curve(), + generator.x(), + generator.y(), + generator.order(), + ) + + curve = Curve( + "BRAINPOOLP160r1", + generator.curve(), + generator, + (1, 3, 36, 3, 3, 2, 8, 1, 1, 1), + ) + + sk = SigningKey.from_secret_exponent(12, curve) + + sk2 = SigningKey.from_secret_exponent(12, BRAINPOOLP160r1) + + assert sk.privkey == sk2.privkey + + +def test_VerifyingKey_inequality_with_different_curves(): + sk1 = SigningKey.from_secret_exponent(2, BRAINPOOLP160r1) + sk2 = SigningKey.from_secret_exponent(2, NIST256p) + + assert not (sk1.verifying_key == sk2.verifying_key) + + +def test_VerifyingKey_inequality_with_different_secret_points(): + sk1 = SigningKey.from_secret_exponent(2, BRAINPOOLP160r1) + sk2 = SigningKey.from_secret_exponent(3, BRAINPOOLP160r1) + + assert not (sk1.verifying_key == sk2.verifying_key) + + +def test_SigningKey_from_pem_pkcs8v2_EdDSA(): + pem = """-----BEGIN PRIVATE KEY----- + MFMCAQEwBQYDK2VwBCIEICc2F2ag1n1QP0jY+g9qWx5sDkx0s/HdNi3cSRHw+zsI + oSMDIQA+HQ2xCif8a/LMWR2m5HaCm5I2pKe/cc8OiRANMHxjKQ== + -----END PRIVATE KEY-----""" + + sk = SigningKey.from_pem(pem) + assert sk.curve == Ed25519 diff --git a/code/.venv/lib/python3.12/site-packages/ecdsa/test_malformed_sigs.py b/code/.venv/lib/python3.12/site-packages/ecdsa/test_malformed_sigs.py new file mode 100644 index 0000000..e5a87c2 --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/ecdsa/test_malformed_sigs.py @@ -0,0 +1,378 @@ +from __future__ import with_statement, division + +import hashlib + +try: + from hashlib import algorithms_available +except ImportError: # pragma: no cover + algorithms_available = [ + "md5", + "sha1", + "sha224", + "sha256", + "sha384", + "sha512", + ] +# skip algorithms broken by change to OpenSSL 3.0 and early versions +# of hashlib that list algorithms that require the legacy provider to work +# https://bugs.python.org/issue38820 +algorithms_available = [ + i + for i in algorithms_available + if i not in ("mdc2", "md2", "md4", "whirlpool", "ripemd160") +] +from functools import partial +import pytest +import sys +import hypothesis.strategies as st +from hypothesis import note, assume, given, settings, example + +from .keys import SigningKey +from .keys import BadSignatureError +from .util import sigencode_der, sigencode_string +from .util import sigdecode_der, sigdecode_string +from .curves import curves, SECP112r2, SECP128r1 +from .der import ( + encode_integer, + encode_bitstring, + encode_octet_string, + encode_oid, + encode_sequence, + encode_constructed, +) +from .ellipticcurve import CurveEdTw + + +example_data = b"some data to sign" +"""Since the data is hashed for processing, really any string will do.""" + + +hash_and_size = [ + (name, hashlib.new(name).digest_size) for name in algorithms_available +] +"""Pairs of hash names and their output sizes. +Needed for pairing with curves as we don't support hashes +bigger than order sizes of curves.""" + + +if "--fast" in sys.argv: # pragma: no cover + curves = [SECP112r2, SECP128r1] + + +keys_and_sigs = [] +"""Name of the curve+hash combination, VerifyingKey and DER signature.""" + + +# for hypothesis strategy shrinking we want smallest curves and hashes first +for curve in sorted(curves, key=lambda x: x.baselen): + for hash_alg in [ + name + for name, size in sorted(hash_and_size, key=lambda x: x[1]) + if 0 < size <= curve.baselen + ]: + sk = SigningKey.generate( + curve, hashfunc=partial(hashlib.new, hash_alg) + ) + + keys_and_sigs.append( + ( + "{0} {1}".format(curve, hash_alg), + sk.verifying_key, + sk.sign(example_data, sigencode=sigencode_der), + ) + ) + + +# first make sure that the signatures can be verified +@pytest.mark.parametrize( + "verifying_key,signature", + [pytest.param(vk, sig, id=name) for name, vk, sig in keys_and_sigs], +) +def test_signatures(verifying_key, signature): + assert verifying_key.verify( + signature, example_data, sigdecode=sigdecode_der + ) + + +@st.composite +def st_fuzzed_sig(draw, keys_and_sigs): # pragma: no cover + """ + Hypothesis strategy that generates pairs of VerifyingKey and malformed + signatures created by fuzzing of a valid signature. + """ + name, verifying_key, old_sig = draw(st.sampled_from(keys_and_sigs)) + note("Configuration: {0}".format(name)) + + sig = bytearray(old_sig) + + # decide which bytes should be removed + to_remove = draw( + st.lists(st.integers(min_value=0, max_value=len(sig) - 1), unique=True) + ) + to_remove.sort() + for i in reversed(to_remove): + del sig[i] + note("Remove bytes: {0}".format(to_remove)) + + # decide which bytes of the original signature should be changed + xors = None + if sig: # pragma: no branch + xors = draw( + st.dictionaries( + st.integers(min_value=0, max_value=len(sig) - 1), + st.integers(min_value=1, max_value=255), + ) + ) + for i, val in xors.items(): + sig[i] ^= val + note("xors: {0}".format(xors)) + + # decide where new data should be inserted + insert_pos = draw(st.integers(min_value=0, max_value=len(sig))) + # NIST521p signature is about 140 bytes long, test slightly longer + insert_data = draw(st.binary(max_size=256)) + + sig = sig[:insert_pos] + insert_data + sig[insert_pos:] + note( + "Inserted at position {0} bytes: {1!r}".format(insert_pos, insert_data) + ) + + sig = bytes(sig) + # make sure that there was performed at least one mutation on the data + assume(to_remove or xors or insert_data) + # and that the mutations didn't cancel each-other out + assume(sig != old_sig) + + return verifying_key, sig + + +params = {} +# not supported in hypothesis 2.0.0 +if sys.version_info >= (2, 7): # pragma: no branch + from hypothesis import HealthCheck + + # deadline=5s because NIST521p are slow to verify + params["deadline"] = 5000 + params["suppress_health_check"] = [ + HealthCheck.data_too_large, + HealthCheck.filter_too_much, + HealthCheck.too_slow, + ] +if "--fast" in sys.argv: # pragma: no cover + params["max_examples"] = 20 + +slow_params = dict(params) +if "--fast" in sys.argv: # pragma: no cover + slow_params["max_examples"] = 1 +else: + slow_params["max_examples"] = 10 + + +@settings(**slow_params) +@given(st_fuzzed_sig(keys_and_sigs)) +def test_fuzzed_der_signatures(args): + verifying_key, sig = args + + with pytest.raises(BadSignatureError): + verifying_key.verify(sig, example_data, sigdecode=sigdecode_der) + + +@st.composite +def st_random_der_ecdsa_sig_value(draw): # pragma: no cover + """ + Hypothesis strategy for selecting random values and encoding them + to ECDSA-Sig-Value object:: + + ECDSA-Sig-Value ::= SEQUENCE { + r INTEGER, + s INTEGER + } + """ + name, verifying_key, _ = draw(st.sampled_from(keys_and_sigs)) + note("Configuration: {0}".format(name)) + order = int(verifying_key.curve.order) + + # the encode_integer doesn't support negative numbers, would be nice + # to generate them too, but we have coverage for remove_integer() + # verifying that it doesn't accept them, so meh. + # Test all numbers around the ones that can show up (around order) + # way smaller and slightly bigger + r = draw( + st.integers(min_value=0, max_value=order << 4) + | st.integers(min_value=order >> 2, max_value=order + 1) + ) + s = draw( + st.integers(min_value=0, max_value=order << 4) + | st.integers(min_value=order >> 2, max_value=order + 1) + ) + + sig = encode_sequence(encode_integer(r), encode_integer(s)) + + return verifying_key, sig + + +@settings(**slow_params) +@given(st_random_der_ecdsa_sig_value()) +def test_random_der_ecdsa_sig_value(params): + """ + Check if random values encoded in ECDSA-Sig-Value structure are rejected + as signature. + """ + verifying_key, sig = params + + with pytest.raises(BadSignatureError): + verifying_key.verify(sig, example_data, sigdecode=sigdecode_der) + + +def st_der_integer(*args, **kwargs): # pragma: no cover + """ + Hypothesis strategy that returns a random positive integer as DER + INTEGER. + Parameters are passed to hypothesis.strategy.integer. + """ + if "min_value" not in kwargs: # pragma: no branch + kwargs["min_value"] = 0 + return st.builds(encode_integer, st.integers(*args, **kwargs)) + + +@st.composite +def st_der_bit_string(draw, *args, **kwargs): # pragma: no cover + """ + Hypothesis strategy that returns a random DER BIT STRING. + Parameters are passed to hypothesis.strategy.binary. + """ + data = draw(st.binary(*args, **kwargs)) + if data: + unused = draw(st.integers(min_value=0, max_value=7)) + data = bytearray(data) + data[-1] &= -(2**unused) + data = bytes(data) + else: + unused = 0 + return encode_bitstring(data, unused) + + +def st_der_octet_string(*args, **kwargs): # pragma: no cover + """ + Hypothesis strategy that returns a random DER OCTET STRING object. + Parameters are passed to hypothesis.strategy.binary + """ + return st.builds(encode_octet_string, st.binary(*args, **kwargs)) + + +def st_der_null(): # pragma: no cover + """ + Hypothesis strategy that returns DER NULL object. + """ + return st.just(b"\x05\x00") + + +@st.composite +def st_der_oid(draw): # pragma: no cover + """ + Hypothesis strategy that returns DER OBJECT IDENTIFIER objects. + """ + first = draw(st.integers(min_value=0, max_value=2)) + if first < 2: + second = draw(st.integers(min_value=0, max_value=39)) + else: + second = draw(st.integers(min_value=0, max_value=2**512)) + rest = draw( + st.lists(st.integers(min_value=0, max_value=2**512), max_size=50) + ) + return encode_oid(first, second, *rest) + + +def st_der(): # pragma: no cover + """ + Hypothesis strategy that returns random DER structures. + + A valid DER structure is any primitive object, an octet encoding + of a valid DER structure, sequence of valid DER objects or a constructed + encoding of any of the above. + """ + return st.recursive( # pragma: no branch + st.just(b"") + | st_der_integer(max_value=2**4096) + | st_der_bit_string(max_size=1024**2) + | st_der_octet_string(max_size=1024**2) + | st_der_null() + | st_der_oid(), + lambda children: st.builds(encode_octet_string, st.one_of(children)) + | st.builds(lambda x: encode_bitstring(x, 0), st.one_of(children)) + | st.builds( + lambda x: encode_sequence(*x), st.lists(children, max_size=200) + ) + | st.builds( + encode_constructed, + st.integers(min_value=0, max_value=0x3F), + st.one_of(children), + ), + max_leaves=40, + ) + + +@settings(**slow_params) +@given(st.sampled_from(keys_and_sigs), st_der()) +def test_random_der_as_signature(params, der): + """Check if random DER structures are rejected as signature""" + name, verifying_key, _ = params + + with pytest.raises(BadSignatureError): + verifying_key.verify(der, example_data, sigdecode=sigdecode_der) + + +@settings(**slow_params) +@given(st.sampled_from(keys_and_sigs), st.binary(max_size=1024**2)) +@example( + keys_and_sigs[0], encode_sequence(encode_integer(0), encode_integer(0)) +) +@example( + keys_and_sigs[0], + encode_sequence(encode_integer(1), encode_integer(1)) + b"\x00", +) +@example(keys_and_sigs[0], encode_sequence(*[encode_integer(1)] * 3)) +def test_random_bytes_as_signature(params, der): + """Check if random bytes are rejected as signature""" + name, verifying_key, _ = params + + with pytest.raises(BadSignatureError): + verifying_key.verify(der, example_data, sigdecode=sigdecode_der) + + +keys_and_string_sigs = [ + ( + name, + verifying_key, + sigencode_string( + *sigdecode_der(sig, verifying_key.curve.order), + order=verifying_key.curve.order + ), + ) + for name, verifying_key, sig in keys_and_sigs + if not isinstance(verifying_key.curve.curve, CurveEdTw) +] +""" +Name of the curve+hash combination, VerifyingKey and signature as a +byte string. +""" + + +keys_and_string_sigs += [ + ( + name, + verifying_key, + sig, + ) + for name, verifying_key, sig in keys_and_sigs + if isinstance(verifying_key.curve.curve, CurveEdTw) +] + + +@settings(**slow_params) +@given(st_fuzzed_sig(keys_and_string_sigs)) +def test_fuzzed_string_signatures(params): + verifying_key, sig = params + + with pytest.raises(BadSignatureError): + verifying_key.verify(sig, example_data, sigdecode=sigdecode_string) diff --git a/code/.venv/lib/python3.12/site-packages/ecdsa/test_numbertheory.py b/code/.venv/lib/python3.12/site-packages/ecdsa/test_numbertheory.py new file mode 100644 index 0000000..966eca2 --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/ecdsa/test_numbertheory.py @@ -0,0 +1,483 @@ +import operator +from functools import reduce +import sys + +try: + import unittest2 as unittest +except ImportError: + import unittest +import hypothesis.strategies as st +import pytest +from hypothesis import given, settings, example + +try: + from hypothesis import HealthCheck + + HC_PRESENT = True +except ImportError: # pragma: no cover + HC_PRESENT = False +from .numbertheory import ( + SquareRootError, + JacobiError, + factorization, + gcd, + lcm, + jacobi, + inverse_mod, + is_prime, + next_prime, + smallprimes, + square_root_mod_prime, +) + +try: + from gmpy2 import mpz +except ImportError: + try: + from gmpy import mpz + except ImportError: + + def mpz(x): + return x + + +BIGPRIMES = ( + 999671, + 999683, + 999721, + 999727, + 999749, + 999763, + 999769, + 999773, + 999809, + 999853, + 999863, + 999883, + 999907, + 999917, + 999931, + 999953, + 999959, + 999961, + 999979, + 999983, +) + + +@pytest.mark.parametrize( + "prime, next_p", [(p, q) for p, q in zip(BIGPRIMES[:-1], BIGPRIMES[1:])] +) +def test_next_prime(prime, next_p): + assert next_prime(prime) == next_p + + +@pytest.mark.parametrize("val", [-1, 0, 1]) +def test_next_prime_with_nums_less_2(val): + assert next_prime(val) == 2 + + +@pytest.mark.slow +@pytest.mark.parametrize("prime", smallprimes) +def test_square_root_mod_prime_for_small_primes(prime): + squares = set() + for num in range(0, 1 + prime // 2): + sq = num * num % prime + squares.add(sq) + root = square_root_mod_prime(sq, prime) + # tested for real with TestNumbertheory.test_square_root_mod_prime + assert root * root % prime == sq + + for nonsquare in range(0, prime): + if nonsquare in squares: + continue + with pytest.raises(SquareRootError): + square_root_mod_prime(nonsquare, prime) + + +def test_square_root_mod_prime_for_2(): + a = square_root_mod_prime(1, 2) + assert a == 1 + + +def test_square_root_mod_prime_for_small_prime(): + root = square_root_mod_prime(98**2 % 101, 101) + assert root * root % 101 == 9 + + +def test_square_root_mod_prime_for_p_congruent_5(): + p = 13 + assert p % 8 == 5 + + root = square_root_mod_prime(3, p) + assert root * root % p == 3 + + +def test_square_root_mod_prime_for_p_congruent_5_large_d(): + p = 29 + assert p % 8 == 5 + + root = square_root_mod_prime(4, p) + assert root * root % p == 4 + + +class TestSquareRootModPrime(unittest.TestCase): + def test_power_of_2_p(self): + with self.assertRaises(JacobiError): + square_root_mod_prime(12, 32) + + def test_no_square(self): + with self.assertRaises(SquareRootError) as e: + square_root_mod_prime(12, 31) + + self.assertIn("no square root", str(e.exception)) + + def test_non_prime(self): + with self.assertRaises(SquareRootError) as e: + square_root_mod_prime(12, 33) + + self.assertIn("p is not prime", str(e.exception)) + + def test_non_prime_with_negative(self): + with self.assertRaises(SquareRootError) as e: + square_root_mod_prime(697 - 1, 697) + + self.assertIn("p is not prime", str(e.exception)) + + +@st.composite +def st_two_nums_rel_prime(draw): + # 521-bit is the biggest curve we operate on, use 1024 for a bit + # of breathing space + mod = draw(st.integers(min_value=2, max_value=2**1024)) + num = draw( + st.integers(min_value=1, max_value=mod - 1).filter( + lambda x: gcd(x, mod) == 1 + ) + ) + return num, mod + + +@st.composite +def st_primes(draw, *args, **kwargs): + if "min_value" not in kwargs: # pragma: no branch + kwargs["min_value"] = 1 + prime = draw( + st.sampled_from(smallprimes) + | st.integers(*args, **kwargs).filter(is_prime) + ) + return prime + + +@st.composite +def st_num_square_prime(draw): + prime = draw(st_primes(max_value=2**1024)) + num = draw(st.integers(min_value=0, max_value=1 + prime // 2)) + sq = num * num % prime + return sq, prime + + +@st.composite +def st_comp_with_com_fac(draw): + """ + Strategy that returns lists of numbers, all having a common factor. + """ + primes = draw( + st.lists(st_primes(max_value=2**512), min_size=1, max_size=10) + ) + # select random prime(s) that will make the common factor of composites + com_fac_primes = draw( + st.lists(st.sampled_from(primes), min_size=1, max_size=20) + ) + com_fac = reduce(operator.mul, com_fac_primes, 1) + + # select at most 20 lists (returned numbers), + # each having at most 30 primes (factors) including none (then the number + # will be 1) + comp_primes = draw( # pragma: no branch + st.integers(min_value=1, max_value=20).flatmap( + lambda n: st.lists( + st.lists(st.sampled_from(primes), max_size=30), + min_size=1, + max_size=n, + ) + ) + ) + + return [reduce(operator.mul, nums, 1) * com_fac for nums in comp_primes] + + +@st.composite +def st_comp_no_com_fac(draw): + """ + Strategy that returns lists of numbers that don't have a common factor. + """ + primes = draw( + st.lists( + st_primes(max_value=2**512), min_size=2, max_size=10, unique=True + ) + ) + # first select the primes that will create the uncommon factor + # between returned numbers + uncom_fac_primes = draw( + st.lists( + st.sampled_from(primes), + min_size=1, + max_size=len(primes) - 1, + unique=True, + ) + ) + uncom_fac = reduce(operator.mul, uncom_fac_primes, 1) + + # then build composites from leftover primes + leftover_primes = [i for i in primes if i not in uncom_fac_primes] + + assert leftover_primes + assert uncom_fac_primes + + # select at most 20 lists, each having at most 30 primes + # selected from the leftover_primes list + number_primes = draw( # pragma: no branch + st.integers(min_value=1, max_value=20).flatmap( + lambda n: st.lists( + st.lists(st.sampled_from(leftover_primes), max_size=30), + min_size=1, + max_size=n, + ) + ) + ) + + numbers = [reduce(operator.mul, nums, 1) for nums in number_primes] + + insert_at = draw(st.integers(min_value=0, max_value=len(numbers))) + numbers.insert(insert_at, uncom_fac) + return numbers + + +HYP_SETTINGS = {} +if HC_PRESENT: # pragma: no branch + HYP_SETTINGS["suppress_health_check"] = [ + HealthCheck.filter_too_much, + HealthCheck.too_slow, + ] + # the factorization() sometimes takes a long time to finish + HYP_SETTINGS["deadline"] = 5000 + +if "--fast" in sys.argv: # pragma: no cover + HYP_SETTINGS["max_examples"] = 20 + + +HYP_SLOW_SETTINGS = dict(HYP_SETTINGS) +if "--fast" in sys.argv: # pragma: no cover + HYP_SLOW_SETTINGS["max_examples"] = 1 +else: + HYP_SLOW_SETTINGS["max_examples"] = 20 + + +class TestIsPrime(unittest.TestCase): + def test_very_small_prime(self): + assert is_prime(23) + + def test_very_small_composite(self): + assert not is_prime(22) + + def test_small_prime(self): + assert is_prime(123456791) + + def test_special_composite(self): + assert not is_prime(10261) + + def test_medium_prime_1(self): + # nextPrime[2^256] + assert is_prime(2**256 + 0x129) + + def test_medium_prime_2(self): + # nextPrime(2^256+0x129) + assert is_prime(2**256 + 0x12D) + + def test_medium_trivial_composite(self): + assert not is_prime(2**256 + 0x130) + + def test_medium_non_trivial_composite(self): + assert not is_prime(2**256 + 0x12F) + + def test_large_prime(self): + # nextPrime[2^2048] + assert is_prime(mpz(2) ** 2048 + 0x3D5) + + def test_pseudoprime_base_19(self): + assert not is_prime(1543267864443420616877677640751301) + + def test_pseudoprime_base_300(self): + # F. Arnault "Constructing Carmichael Numbers Which Are Strong + # Pseudoprimes to Several Bases". Journal of Symbolic + # Computation. 20 (2): 151-161. doi:10.1006/jsco.1995.1042. + # Section 4.4 Large Example (a pseudoprime to all bases up to + # 300) + p = int( + "29 674 495 668 685 510 550 154 174 642 905 332 730 " + "771 991 799 853 043 350 995 075 531 276 838 753 171 " + "770 199 594 238 596 428 121 188 033 664 754 218 345 " + "562 493 168 782 883".replace(" ", "") + ) + + assert is_prime(p) + for _ in range(10): + if not is_prime(p * (313 * (p - 1) + 1) * (353 * (p - 1) + 1)): + break + else: + assert False, "composite not detected" + + +class TestNumbertheory(unittest.TestCase): + def test_gcd(self): + assert gcd(3 * 5 * 7, 3 * 5 * 11, 3 * 5 * 13) == 3 * 5 + assert gcd([3 * 5 * 7, 3 * 5 * 11, 3 * 5 * 13]) == 3 * 5 + assert gcd(3) == 3 + + @unittest.skipUnless( + HC_PRESENT, + "Hypothesis 2.0.0 can't be made tolerant of hard to " + "meet requirements (like `is_prime()`), the test " + "case times-out on it", + ) + @settings(**HYP_SLOW_SETTINGS) + @example([877 * 1151, 877 * 1009]) + @given(st_comp_with_com_fac()) + def test_gcd_with_com_factor(self, numbers): + n = gcd(numbers) + assert 1 in numbers or n != 1 + for i in numbers: + assert i % n == 0 + + @unittest.skipUnless( + HC_PRESENT, + "Hypothesis 2.0.0 can't be made tolerant of hard to " + "meet requirements (like `is_prime()`), the test " + "case times-out on it", + ) + @settings(**HYP_SLOW_SETTINGS) + @example([1151, 1069, 1009]) + @given(st_comp_no_com_fac()) + def test_gcd_with_uncom_factor(self, numbers): + n = gcd(numbers) + assert n == 1 + + @settings(**HYP_SLOW_SETTINGS) + @given( + st.lists( + st.integers(min_value=1, max_value=2**8192), + min_size=1, + max_size=20, + ) + ) + def test_gcd_with_random_numbers(self, numbers): + n = gcd(numbers) + for i in numbers: + # check that at least it's a divider + assert i % n == 0 + + def test_lcm(self): + assert lcm(3, 5 * 3, 7 * 3) == 3 * 5 * 7 + assert lcm([3, 5 * 3, 7 * 3]) == 3 * 5 * 7 + assert lcm(3) == 3 + + @settings(**HYP_SLOW_SETTINGS) + @given( + st.lists( + st.integers(min_value=1, max_value=2**8192), + min_size=1, + max_size=20, + ) + ) + def test_lcm_with_random_numbers(self, numbers): + n = lcm(numbers) + for i in numbers: + assert n % i == 0 + + @unittest.skipUnless( + HC_PRESENT, + "Hypothesis 2.0.0 can't be made tolerant of hard to " + "meet requirements (like `is_prime()`), the test " + "case times-out on it", + ) + @settings(**HYP_SLOW_SETTINGS) + @given(st_num_square_prime()) + def test_square_root_mod_prime(self, vals): + square, prime = vals + + calc = square_root_mod_prime(square, prime) + assert calc * calc % prime == square + + @pytest.mark.slow + @settings(**HYP_SLOW_SETTINGS) + @given(st.integers(min_value=1, max_value=10**12)) + @example(265399 * 1526929) + @example(373297**2 * 553991) + def test_factorization(self, num): + factors = factorization(num) + mult = 1 + for i in factors: + mult *= i[0] ** i[1] + assert mult == num + + def test_factorisation_smallprimes(self): + exp = 101 * 103 + assert 101 in smallprimes + assert 103 in smallprimes + factors = factorization(exp) + mult = 1 + for i in factors: + mult *= i[0] ** i[1] + assert mult == exp + + def test_factorisation_not_smallprimes(self): + exp = 1231 * 1237 + assert 1231 not in smallprimes + assert 1237 not in smallprimes + factors = factorization(exp) + mult = 1 + for i in factors: + mult *= i[0] ** i[1] + assert mult == exp + + def test_jacobi_with_zero(self): + assert jacobi(0, 3) == 0 + + def test_jacobi_with_one(self): + assert jacobi(1, 3) == 1 + + @settings(**HYP_SLOW_SETTINGS) + @given(st.integers(min_value=3, max_value=1000).filter(lambda x: x % 2)) + def test_jacobi(self, mod): + mod = mpz(mod) + if is_prime(mod): + squares = set() + for root in range(1, mod): + root = mpz(root) + assert jacobi(root * root, mod) == 1 + squares.add(root * root % mod) + for i in range(1, mod): + if i not in squares: + i = mpz(i) + assert jacobi(i, mod) == -1 + else: + factors = factorization(mod) + for a in range(1, mod): + c = 1 + for i in factors: + c *= jacobi(a, i[0]) ** i[1] + assert c == jacobi(a, mod) + + @settings(**HYP_SLOW_SETTINGS) + @given(st_two_nums_rel_prime()) + def test_inverse_mod(self, nums): + num, mod = nums + + inv = inverse_mod(num, mod) + + assert 0 < inv < mod + assert num * inv % mod == 1 + + def test_inverse_mod_with_zero(self): + assert 0 == inverse_mod(0, 11) diff --git a/code/.venv/lib/python3.12/site-packages/ecdsa/test_pyecdsa.py b/code/.venv/lib/python3.12/site-packages/ecdsa/test_pyecdsa.py new file mode 100644 index 0000000..20201ba --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/ecdsa/test_pyecdsa.py @@ -0,0 +1,2522 @@ +from __future__ import with_statement, division + +try: + import unittest2 as unittest +except ImportError: + import unittest +import os +import shutil +import subprocess +import pytest +import sys +from binascii import hexlify, unhexlify +import hashlib +from functools import partial + +from hypothesis import given, settings +import hypothesis.strategies as st + +from six import b, print_, binary_type +from .keys import SigningKey, VerifyingKey +from .keys import BadSignatureError, MalformedPointError, BadDigestError +from . import util +from .util import ( + sigencode_der, + sigencode_strings, + sigencode_strings_canonize, + sigencode_string_canonize, + sigencode_der_canonize, +) +from .util import sigdecode_der, sigdecode_strings, sigdecode_string +from .util import number_to_string, encoded_oid_ecPublicKey, MalformedSignature +from .curves import Curve, UnknownCurveError +from .curves import ( + SECP112r1, + SECP112r2, + SECP128r1, + SECP160r1, + NIST192p, + NIST224p, + NIST256p, + NIST384p, + NIST521p, + SECP256k1, + BRAINPOOLP160r1, + BRAINPOOLP192r1, + BRAINPOOLP224r1, + BRAINPOOLP256r1, + BRAINPOOLP320r1, + BRAINPOOLP384r1, + BRAINPOOLP512r1, + BRAINPOOLP160t1, + BRAINPOOLP192t1, + BRAINPOOLP224t1, + BRAINPOOLP256t1, + BRAINPOOLP320t1, + BRAINPOOLP384t1, + BRAINPOOLP512t1, + Ed25519, + Ed448, + curves, +) +from .ecdsa import ( + curve_brainpoolp224r1, + curve_brainpoolp256r1, + curve_brainpoolp384r1, + curve_brainpoolp512r1, +) +from .ellipticcurve import Point +from . import der +from . import rfc6979 +from . import ecdsa + + +class SubprocessError(Exception): + pass + + +HYP_SETTINGS = {} + + +if "--fast" in sys.argv: # pragma: no cover + HYP_SETTINGS["max_examples"] = 2 + + +def run_openssl(cmd): + OPENSSL = "openssl" + p = subprocess.Popen( + [OPENSSL] + cmd.split(), + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + ) + stdout, ignored = p.communicate() + if p.returncode != 0: + raise SubprocessError( + "cmd '%s %s' failed: rc=%s, stdout/err was %s" + % (OPENSSL, cmd, p.returncode, stdout) + ) + return stdout.decode() + + +class ECDSA(unittest.TestCase): + def test_basic(self): + priv = SigningKey.generate() + pub = priv.get_verifying_key() + + data = b"blahblah" + sig = priv.sign(data) + + self.assertTrue(pub.verify(sig, data)) + self.assertRaises(BadSignatureError, pub.verify, sig, data + b"bad") + + pub2 = VerifyingKey.from_string(pub.to_string()) + self.assertTrue(pub2.verify(sig, data)) + + def test_deterministic(self): + data = b"blahblah" + secexp = int("9d0219792467d7d37b4d43298a7d0c05", 16) + + priv = SigningKey.from_secret_exponent( + secexp, SECP256k1, hashlib.sha256 + ) + pub = priv.get_verifying_key() + + k = rfc6979.generate_k( + SECP256k1.generator.order(), + secexp, + hashlib.sha256, + hashlib.sha256(data).digest(), + ) + + sig1 = priv.sign(data, k=k) + self.assertTrue(pub.verify(sig1, data)) + + sig2 = priv.sign(data, k=k) + self.assertTrue(pub.verify(sig2, data)) + + sig3 = priv.sign_deterministic(data, hashlib.sha256) + self.assertTrue(pub.verify(sig3, data)) + + self.assertEqual(sig1, sig2) + self.assertEqual(sig1, sig3) + + def test_bad_usage(self): + # sk=SigningKey() is wrong + self.assertRaises(TypeError, SigningKey) + self.assertRaises(TypeError, VerifyingKey) + + def test_lengths_default(self): + default = NIST192p + priv = SigningKey.generate() + pub = priv.get_verifying_key() + self.assertEqual(len(pub.to_string()), default.verifying_key_length) + sig = priv.sign(b"data") + self.assertEqual(len(sig), default.signature_length) + + def test_serialize(self): + seed = b"secret" + curve = NIST192p + secexp1 = util.randrange_from_seed__trytryagain(seed, curve.order) + secexp2 = util.randrange_from_seed__trytryagain(seed, curve.order) + self.assertEqual(secexp1, secexp2) + priv1 = SigningKey.from_secret_exponent(secexp1, curve) + priv2 = SigningKey.from_secret_exponent(secexp2, curve) + self.assertEqual( + hexlify(priv1.to_string()), hexlify(priv2.to_string()) + ) + self.assertEqual(priv1.to_pem(), priv2.to_pem()) + pub1 = priv1.get_verifying_key() + pub2 = priv2.get_verifying_key() + data = b"data" + sig1 = priv1.sign(data) + sig2 = priv2.sign(data) + self.assertTrue(pub1.verify(sig1, data)) + self.assertTrue(pub2.verify(sig1, data)) + self.assertTrue(pub1.verify(sig2, data)) + self.assertTrue(pub2.verify(sig2, data)) + self.assertEqual(hexlify(pub1.to_string()), hexlify(pub2.to_string())) + + def test_nonrandom(self): + s = b"all the entropy in the entire world, compressed into one line" + + def not_much_entropy(numbytes): + return s[:numbytes] + + # we control the entropy source, these two keys should be identical: + priv1 = SigningKey.generate(entropy=not_much_entropy) + priv2 = SigningKey.generate(entropy=not_much_entropy) + self.assertEqual( + hexlify(priv1.get_verifying_key().to_string()), + hexlify(priv2.get_verifying_key().to_string()), + ) + # likewise, signatures should be identical. Obviously you'd never + # want to do this with keys you care about, because the secrecy of + # the private key depends upon using different random numbers for + # each signature + sig1 = priv1.sign(b"data", entropy=not_much_entropy) + sig2 = priv2.sign(b"data", entropy=not_much_entropy) + self.assertEqual(hexlify(sig1), hexlify(sig2)) + + def assertTruePrivkeysEqual(self, priv1, priv2): + self.assertEqual( + priv1.privkey.secret_multiplier, priv2.privkey.secret_multiplier + ) + self.assertEqual( + priv1.privkey.public_key.generator, + priv2.privkey.public_key.generator, + ) + + def test_privkey_creation(self): + s = b"all the entropy in the entire world, compressed into one line" + + def not_much_entropy(numbytes): + return s[:numbytes] + + priv1 = SigningKey.generate() + self.assertEqual(priv1.baselen, NIST192p.baselen) + + priv1 = SigningKey.generate(curve=NIST224p) + self.assertEqual(priv1.baselen, NIST224p.baselen) + + priv1 = SigningKey.generate(entropy=not_much_entropy) + self.assertEqual(priv1.baselen, NIST192p.baselen) + priv2 = SigningKey.generate(entropy=not_much_entropy) + self.assertEqual(priv2.baselen, NIST192p.baselen) + self.assertTruePrivkeysEqual(priv1, priv2) + + priv1 = SigningKey.from_secret_exponent(secexp=3) + self.assertEqual(priv1.baselen, NIST192p.baselen) + priv2 = SigningKey.from_secret_exponent(secexp=3) + self.assertTruePrivkeysEqual(priv1, priv2) + + priv1 = SigningKey.from_secret_exponent(secexp=4, curve=NIST224p) + self.assertEqual(priv1.baselen, NIST224p.baselen) + + def test_privkey_strings(self): + priv1 = SigningKey.generate() + s1 = priv1.to_string() + self.assertEqual(type(s1), binary_type) + self.assertEqual(len(s1), NIST192p.baselen) + priv2 = SigningKey.from_string(s1) + self.assertTruePrivkeysEqual(priv1, priv2) + + s1 = priv1.to_pem() + self.assertEqual(type(s1), binary_type) + self.assertTrue(s1.startswith(b"-----BEGIN EC PRIVATE KEY-----")) + self.assertTrue(s1.strip().endswith(b"-----END EC PRIVATE KEY-----")) + priv2 = SigningKey.from_pem(s1) + self.assertTruePrivkeysEqual(priv1, priv2) + + s1 = priv1.to_der() + self.assertEqual(type(s1), binary_type) + priv2 = SigningKey.from_der(s1) + self.assertTruePrivkeysEqual(priv1, priv2) + + priv1 = SigningKey.generate(curve=NIST256p) + s1 = priv1.to_pem() + self.assertEqual(type(s1), binary_type) + self.assertTrue(s1.startswith(b"-----BEGIN EC PRIVATE KEY-----")) + self.assertTrue(s1.strip().endswith(b"-----END EC PRIVATE KEY-----")) + priv2 = SigningKey.from_pem(s1) + self.assertTruePrivkeysEqual(priv1, priv2) + + s1 = priv1.to_der() + self.assertEqual(type(s1), binary_type) + priv2 = SigningKey.from_der(s1) + self.assertTruePrivkeysEqual(priv1, priv2) + + def test_privkey_strings_brainpool(self): + priv1 = SigningKey.generate(curve=BRAINPOOLP512r1) + s1 = priv1.to_pem() + self.assertEqual(type(s1), binary_type) + self.assertTrue(s1.startswith(b"-----BEGIN EC PRIVATE KEY-----")) + self.assertTrue(s1.strip().endswith(b"-----END EC PRIVATE KEY-----")) + priv2 = SigningKey.from_pem(s1) + self.assertTruePrivkeysEqual(priv1, priv2) + + s1 = priv1.to_der() + self.assertEqual(type(s1), binary_type) + priv2 = SigningKey.from_der(s1) + self.assertTruePrivkeysEqual(priv1, priv2) + + def assertTruePubkeysEqual(self, pub1, pub2): + self.assertEqual(pub1.pubkey.point, pub2.pubkey.point) + self.assertEqual(pub1.pubkey.generator, pub2.pubkey.generator) + self.assertEqual(pub1.curve, pub2.curve) + + def test_pubkey_strings(self): + priv1 = SigningKey.generate() + pub1 = priv1.get_verifying_key() + s1 = pub1.to_string() + self.assertEqual(type(s1), binary_type) + self.assertEqual(len(s1), NIST192p.verifying_key_length) + pub2 = VerifyingKey.from_string(s1) + self.assertTruePubkeysEqual(pub1, pub2) + + priv1 = SigningKey.generate(curve=NIST256p) + pub1 = priv1.get_verifying_key() + s1 = pub1.to_string() + self.assertEqual(type(s1), binary_type) + self.assertEqual(len(s1), NIST256p.verifying_key_length) + pub2 = VerifyingKey.from_string(s1, curve=NIST256p) + self.assertTruePubkeysEqual(pub1, pub2) + + pub1_der = pub1.to_der() + self.assertEqual(type(pub1_der), binary_type) + pub2 = VerifyingKey.from_der(pub1_der) + self.assertTruePubkeysEqual(pub1, pub2) + + self.assertRaises( + der.UnexpectedDER, VerifyingKey.from_der, pub1_der + b"junk" + ) + badpub = VerifyingKey.from_der(pub1_der) + + class FakeGenerator: + def order(self): + return 123456789 + + class FakeCurveFp: + def p(self): + return int( + "6525534529039240705020950546962731340" + "4541085228058844382513856749047873406763" + ) + + badcurve = Curve( + "unknown", FakeCurveFp(), FakeGenerator(), (1, 2, 3, 4, 5, 6), None + ) + badpub.curve = badcurve + badder = badpub.to_der() + self.assertRaises(UnknownCurveError, VerifyingKey.from_der, badder) + + pem = pub1.to_pem() + self.assertEqual(type(pem), binary_type) + self.assertTrue(pem.startswith(b"-----BEGIN PUBLIC KEY-----"), pem) + self.assertTrue(pem.strip().endswith(b"-----END PUBLIC KEY-----"), pem) + pub2 = VerifyingKey.from_pem(pem) + self.assertTruePubkeysEqual(pub1, pub2) + + def test_pubkey_strings_brainpool(self): + priv1 = SigningKey.generate(curve=BRAINPOOLP512r1) + pub1 = priv1.get_verifying_key() + s1 = pub1.to_string() + self.assertEqual(type(s1), binary_type) + self.assertEqual(len(s1), BRAINPOOLP512r1.verifying_key_length) + pub2 = VerifyingKey.from_string(s1, curve=BRAINPOOLP512r1) + self.assertTruePubkeysEqual(pub1, pub2) + + pub1_der = pub1.to_der() + self.assertEqual(type(pub1_der), binary_type) + pub2 = VerifyingKey.from_der(pub1_der) + self.assertTruePubkeysEqual(pub1, pub2) + + def test_vk_to_der_with_invalid_point_encoding(self): + sk = SigningKey.generate() + vk = sk.verifying_key + + with self.assertRaises(ValueError): + vk.to_der("raw") + + def test_sk_to_der_with_invalid_point_encoding(self): + sk = SigningKey.generate() + + with self.assertRaises(ValueError): + sk.to_der("raw") + + def test_vk_from_der_garbage_after_curve_oid(self): + type_oid_der = encoded_oid_ecPublicKey + curve_oid_der = der.encode_oid(*(1, 2, 840, 10045, 3, 1, 1)) + b( + "garbage" + ) + enc_type_der = der.encode_sequence(type_oid_der, curve_oid_der) + point_der = der.encode_bitstring(b"\x00\xff", None) + to_decode = der.encode_sequence(enc_type_der, point_der) + + with self.assertRaises(der.UnexpectedDER): + VerifyingKey.from_der(to_decode) + + def test_vk_from_der_invalid_key_type(self): + type_oid_der = der.encode_oid(*(1, 2, 3)) + curve_oid_der = der.encode_oid(*(1, 2, 840, 10045, 3, 1, 1)) + enc_type_der = der.encode_sequence(type_oid_der, curve_oid_der) + point_der = der.encode_bitstring(b"\x00\xff", None) + to_decode = der.encode_sequence(enc_type_der, point_der) + + with self.assertRaises(der.UnexpectedDER): + VerifyingKey.from_der(to_decode) + + def test_vk_from_der_garbage_after_point_string(self): + type_oid_der = encoded_oid_ecPublicKey + curve_oid_der = der.encode_oid(*(1, 2, 840, 10045, 3, 1, 1)) + enc_type_der = der.encode_sequence(type_oid_der, curve_oid_der) + point_der = der.encode_bitstring(b"\x00\xff", None) + b"garbage" + to_decode = der.encode_sequence(enc_type_der, point_der) + + with self.assertRaises(der.UnexpectedDER): + VerifyingKey.from_der(to_decode) + + def test_vk_from_der_invalid_bitstring(self): + type_oid_der = encoded_oid_ecPublicKey + curve_oid_der = der.encode_oid(*(1, 2, 840, 10045, 3, 1, 1)) + enc_type_der = der.encode_sequence(type_oid_der, curve_oid_der) + point_der = der.encode_bitstring(b"\x08\xff", None) + to_decode = der.encode_sequence(enc_type_der, point_der) + + with self.assertRaises(der.UnexpectedDER): + VerifyingKey.from_der(to_decode) + + def test_vk_from_der_with_invalid_length_of_encoding(self): + type_oid_der = encoded_oid_ecPublicKey + curve_oid_der = der.encode_oid(*(1, 2, 840, 10045, 3, 1, 1)) + enc_type_der = der.encode_sequence(type_oid_der, curve_oid_der) + point_der = der.encode_bitstring(b"\xff" * 64, 0) + to_decode = der.encode_sequence(enc_type_der, point_der) + + with self.assertRaises(MalformedPointError): + VerifyingKey.from_der(to_decode) + + def test_vk_from_der_with_raw_encoding(self): + type_oid_der = encoded_oid_ecPublicKey + curve_oid_der = der.encode_oid(*(1, 2, 840, 10045, 3, 1, 1)) + enc_type_der = der.encode_sequence(type_oid_der, curve_oid_der) + point_der = der.encode_bitstring(b"\xff" * 48, 0) + to_decode = der.encode_sequence(enc_type_der, point_der) + + with self.assertRaises(der.UnexpectedDER): + VerifyingKey.from_der(to_decode) + + def test_signature_strings(self): + priv1 = SigningKey.generate() + pub1 = priv1.get_verifying_key() + data = b"data" + + sig = priv1.sign(data) + self.assertEqual(type(sig), binary_type) + self.assertEqual(len(sig), NIST192p.signature_length) + self.assertTrue(pub1.verify(sig, data)) + + sig = priv1.sign(data, sigencode=sigencode_strings) + self.assertEqual(type(sig), tuple) + self.assertEqual(len(sig), 2) + self.assertEqual(type(sig[0]), binary_type) + self.assertEqual(type(sig[1]), binary_type) + self.assertEqual(len(sig[0]), NIST192p.baselen) + self.assertEqual(len(sig[1]), NIST192p.baselen) + self.assertTrue(pub1.verify(sig, data, sigdecode=sigdecode_strings)) + + sig_der = priv1.sign(data, sigencode=sigencode_der) + self.assertEqual(type(sig_der), binary_type) + self.assertTrue(pub1.verify(sig_der, data, sigdecode=sigdecode_der)) + + def test_sigencode_string_canonize_no_change(self): + r = 12 + s = 400 + order = SECP112r1.order + + new_r, new_s = sigdecode_string( + sigencode_string_canonize(r, s, order), order + ) + + self.assertEqual(r, new_r) + self.assertEqual(s, new_s) + + def test_sigencode_string_canonize(self): + r = 12 + order = SECP112r1.order + s = order - 10 + + new_r, new_s = sigdecode_string( + sigencode_string_canonize(r, s, order), order + ) + + self.assertEqual(r, new_r) + self.assertEqual(order - s, new_s) + + def test_sigencode_strings_canonize_no_change(self): + r = 12 + s = 400 + order = SECP112r1.order + + new_r, new_s = sigdecode_strings( + sigencode_strings_canonize(r, s, order), order + ) + + self.assertEqual(r, new_r) + self.assertEqual(s, new_s) + + def test_sigencode_strings_canonize(self): + r = 12 + order = SECP112r1.order + s = order - 10 + + new_r, new_s = sigdecode_strings( + sigencode_strings_canonize(r, s, order), order + ) + + self.assertEqual(r, new_r) + self.assertEqual(order - s, new_s) + + def test_sigencode_der_canonize_no_change(self): + r = 13 + s = 200 + order = SECP112r1.order + + new_r, new_s = sigdecode_der( + sigencode_der_canonize(r, s, order), order + ) + + self.assertEqual(r, new_r) + self.assertEqual(s, new_s) + + def test_sigencode_der_canonize(self): + r = 13 + order = SECP112r1.order + s = order - 14 + + new_r, new_s = sigdecode_der( + sigencode_der_canonize(r, s, order), order + ) + + self.assertEqual(r, new_r) + self.assertEqual(order - s, new_s) + + def test_sig_decode_strings_with_invalid_count(self): + with self.assertRaises(MalformedSignature): + sigdecode_strings([b"one", b"two", b"three"], 0xFF) + + def test_sig_decode_strings_with_wrong_r_len(self): + with self.assertRaises(MalformedSignature): + sigdecode_strings([b"one", b"two"], 0xFF) + + def test_sig_decode_strings_with_wrong_s_len(self): + with self.assertRaises(MalformedSignature): + sigdecode_strings([b"\xa0", b"\xb0\xff"], 0xFF) + + def test_verify_with_too_long_input(self): + sk = SigningKey.generate() + vk = sk.verifying_key + + with self.assertRaises(BadDigestError): + vk.verify_digest(None, b"\x00" * 128) + + def test_sk_from_secret_exponent_with_wrong_sec_exponent(self): + with self.assertRaises(MalformedPointError): + SigningKey.from_secret_exponent(0) + + def test_sk_from_string_with_wrong_len_string(self): + with self.assertRaises(MalformedPointError): + SigningKey.from_string(b"\x01") + + def test_sk_from_der_with_junk_after_sequence(self): + ver_der = der.encode_integer(1) + to_decode = der.encode_sequence(ver_der) + b"garbage" + + with self.assertRaises(der.UnexpectedDER): + SigningKey.from_der(to_decode) + + def test_sk_from_der_with_wrong_version(self): + ver_der = der.encode_integer(0) + to_decode = der.encode_sequence(ver_der) + + with self.assertRaises(der.UnexpectedDER): + SigningKey.from_der(to_decode) + + def test_sk_from_der_invalid_const_tag(self): + ver_der = der.encode_integer(1) + privkey_der = der.encode_octet_string(b"\x00\xff") + curve_oid_der = der.encode_oid(*(1, 2, 3)) + const_der = der.encode_constructed(1, curve_oid_der) + to_decode = der.encode_sequence( + ver_der, privkey_der, const_der, curve_oid_der + ) + + with self.assertRaises(der.UnexpectedDER): + SigningKey.from_der(to_decode) + + def test_sk_from_der_garbage_after_privkey_oid(self): + ver_der = der.encode_integer(1) + privkey_der = der.encode_octet_string(b"\x00\xff") + curve_oid_der = der.encode_oid(*(1, 2, 3)) + b"garbage" + const_der = der.encode_constructed(0, curve_oid_der) + to_decode = der.encode_sequence( + ver_der, privkey_der, const_der, curve_oid_der + ) + + with self.assertRaises(der.UnexpectedDER): + SigningKey.from_der(to_decode) + + def test_sk_from_der_with_short_privkey(self): + ver_der = der.encode_integer(1) + privkey_der = der.encode_octet_string(b"\x00\xff") + curve_oid_der = der.encode_oid(*(1, 2, 840, 10045, 3, 1, 1)) + const_der = der.encode_constructed(0, curve_oid_der) + to_decode = der.encode_sequence( + ver_der, privkey_der, const_der, curve_oid_der + ) + + sk = SigningKey.from_der(to_decode) + self.assertEqual(sk.privkey.secret_multiplier, 255) + + def test_sk_from_p8_der_with_wrong_version(self): + ver_der = der.encode_integer(2) + algorithm_der = der.encode_sequence( + der.encode_oid(1, 2, 840, 10045, 2, 1), + der.encode_oid(1, 2, 840, 10045, 3, 1, 1), + ) + privkey_der = der.encode_octet_string( + der.encode_sequence( + der.encode_integer(1), der.encode_octet_string(b"\x00\xff") + ) + ) + to_decode = der.encode_sequence(ver_der, algorithm_der, privkey_der) + + with self.assertRaises(der.UnexpectedDER): + SigningKey.from_der(to_decode) + + def test_sk_from_p8_der_with_wrong_algorithm(self): + ver_der = der.encode_integer(1) + algorithm_der = der.encode_sequence( + der.encode_oid(1, 2, 3), der.encode_oid(1, 2, 840, 10045, 3, 1, 1) + ) + privkey_der = der.encode_octet_string( + der.encode_sequence( + der.encode_integer(1), der.encode_octet_string(b"\x00\xff") + ) + ) + to_decode = der.encode_sequence(ver_der, algorithm_der, privkey_der) + + with self.assertRaises(der.UnexpectedDER): + SigningKey.from_der(to_decode) + + def test_sk_from_p8_der_with_trailing_junk_after_algorithm(self): + ver_der = der.encode_integer(1) + algorithm_der = der.encode_sequence( + der.encode_oid(1, 2, 840, 10045, 2, 1), + der.encode_oid(1, 2, 840, 10045, 3, 1, 1), + der.encode_octet_string(b"junk"), + ) + privkey_der = der.encode_octet_string( + der.encode_sequence( + der.encode_integer(1), der.encode_octet_string(b"\x00\xff") + ) + ) + to_decode = der.encode_sequence(ver_der, algorithm_der, privkey_der) + + with self.assertRaises(der.UnexpectedDER): + SigningKey.from_der(to_decode) + + def test_sk_from_p8_der_with_trailing_junk_after_key(self): + ver_der = der.encode_integer(1) + algorithm_der = der.encode_sequence( + der.encode_oid(1, 2, 840, 10045, 2, 1), + der.encode_oid(1, 2, 840, 10045, 3, 1, 1), + ) + privkey_der = der.encode_octet_string( + der.encode_sequence( + der.encode_integer(1), der.encode_octet_string(b"\x00\xff") + ) + + der.encode_integer(999) + ) + to_decode = der.encode_sequence( + ver_der, + algorithm_der, + privkey_der, + der.encode_octet_string(b"junk"), + ) + + with self.assertRaises(der.UnexpectedDER): + SigningKey.from_der(to_decode) + + def test_sign_with_too_long_hash(self): + sk = SigningKey.from_secret_exponent(12) + + with self.assertRaises(BadDigestError): + sk.sign_digest(b"\xff" * 64) + + def test_hashfunc(self): + sk = SigningKey.generate(curve=NIST256p, hashfunc=hashlib.sha256) + data = b"security level is 128 bits" + sig = sk.sign(data) + vk = VerifyingKey.from_string( + sk.get_verifying_key().to_string(), + curve=NIST256p, + hashfunc=hashlib.sha256, + ) + self.assertTrue(vk.verify(sig, data)) + + sk2 = SigningKey.generate(curve=NIST256p) + sig2 = sk2.sign(data, hashfunc=hashlib.sha256) + vk2 = VerifyingKey.from_string( + sk2.get_verifying_key().to_string(), + curve=NIST256p, + hashfunc=hashlib.sha256, + ) + self.assertTrue(vk2.verify(sig2, data)) + + vk3 = VerifyingKey.from_string( + sk.get_verifying_key().to_string(), curve=NIST256p + ) + self.assertTrue(vk3.verify(sig, data, hashfunc=hashlib.sha256)) + + def test_public_key_recovery(self): + # Create keys + curve = BRAINPOOLP160r1 + + sk = SigningKey.generate(curve=curve) + vk = sk.get_verifying_key() + + # Sign a message + data = b"blahblah" + signature = sk.sign(data) + + # Recover verifying keys + recovered_vks = VerifyingKey.from_public_key_recovery( + signature, data, curve + ) + + # Test if each pk is valid + for recovered_vk in recovered_vks: + # Test if recovered vk is valid for the data + self.assertTrue(recovered_vk.verify(signature, data)) + + # Test if properties are equal + self.assertEqual(vk.curve, recovered_vk.curve) + self.assertEqual( + vk.default_hashfunc, recovered_vk.default_hashfunc + ) + + # Test if original vk is the list of recovered keys + self.assertIn( + vk.pubkey.point, + [recovered_vk.pubkey.point for recovered_vk in recovered_vks], + ) + + def test_public_key_recovery_with_custom_hash(self): + # Create keys + curve = BRAINPOOLP160r1 + + sk = SigningKey.generate(curve=curve, hashfunc=hashlib.sha256) + vk = sk.get_verifying_key() + + # Sign a message + data = b"blahblah" + signature = sk.sign(data) + + # Recover verifying keys + recovered_vks = VerifyingKey.from_public_key_recovery( + signature, + data, + curve, + hashfunc=hashlib.sha256, + allow_truncate=True, + ) + + # Test if each pk is valid + for recovered_vk in recovered_vks: + # Test if recovered vk is valid for the data + self.assertTrue(recovered_vk.verify(signature, data)) + + # Test if properties are equal + self.assertEqual(vk.curve, recovered_vk.curve) + self.assertEqual(hashlib.sha256, recovered_vk.default_hashfunc) + + # Test if original vk is the list of recovered keys + self.assertIn( + vk.pubkey.point, + [recovered_vk.pubkey.point for recovered_vk in recovered_vks], + ) + + def test_encoding(self): + sk = SigningKey.from_secret_exponent(123456789) + vk = sk.verifying_key + + exp = b( + "\x0c\xe0\x1d\xe0d\x1c\x8eS\x8a\xc0\x9eK\xa8x !\xd5\xc2\xc3" + "\xfd\xc8\xa0c\xff\xfb\x02\xb9\xc4\x84)\x1a\x0f\x8b\x87\xa4" + "z\x8a#\xb5\x97\xecO\xb6\xa0HQ\x89*" + ) + self.assertEqual(vk.to_string(), exp) + self.assertEqual(vk.to_string("raw"), exp) + self.assertEqual(vk.to_string("uncompressed"), b"\x04" + exp) + self.assertEqual(vk.to_string("compressed"), b"\x02" + exp[:24]) + self.assertEqual(vk.to_string("hybrid"), b"\x06" + exp) + + def test_decoding(self): + sk = SigningKey.from_secret_exponent(123456789) + vk = sk.verifying_key + + enc = b( + "\x0c\xe0\x1d\xe0d\x1c\x8eS\x8a\xc0\x9eK\xa8x !\xd5\xc2\xc3" + "\xfd\xc8\xa0c\xff\xfb\x02\xb9\xc4\x84)\x1a\x0f\x8b\x87\xa4" + "z\x8a#\xb5\x97\xecO\xb6\xa0HQ\x89*" + ) + + from_raw = VerifyingKey.from_string(enc) + self.assertEqual(from_raw.pubkey.point, vk.pubkey.point) + + from_uncompressed = VerifyingKey.from_string(b"\x04" + enc) + self.assertEqual(from_uncompressed.pubkey.point, vk.pubkey.point) + + from_compressed = VerifyingKey.from_string(b"\x02" + enc[:24]) + self.assertEqual(from_compressed.pubkey.point, vk.pubkey.point) + + from_uncompressed = VerifyingKey.from_string(b"\x06" + enc) + self.assertEqual(from_uncompressed.pubkey.point, vk.pubkey.point) + + def test_uncompressed_decoding_as_only_alowed(self): + enc = b( + "\x04" + "\x0c\xe0\x1d\xe0d\x1c\x8eS\x8a\xc0\x9eK\xa8x !\xd5\xc2\xc3" + "\xfd\xc8\xa0c\xff\xfb\x02\xb9\xc4\x84)\x1a\x0f\x8b\x87\xa4" + "z\x8a#\xb5\x97\xecO\xb6\xa0HQ\x89*" + ) + vk = VerifyingKey.from_string(enc, valid_encodings=("uncompressed",)) + sk = SigningKey.from_secret_exponent(123456789) + + self.assertEqual(vk, sk.verifying_key) + + def test_raw_decoding_with_blocked_format(self): + enc = b( + "\x0c\xe0\x1d\xe0d\x1c\x8eS\x8a\xc0\x9eK\xa8x !\xd5\xc2\xc3" + "\xfd\xc8\xa0c\xff\xfb\x02\xb9\xc4\x84)\x1a\x0f\x8b\x87\xa4" + "z\x8a#\xb5\x97\xecO\xb6\xa0HQ\x89*" + ) + with self.assertRaises(MalformedPointError) as exp: + VerifyingKey.from_string(enc, valid_encodings=("hybrid",)) + + self.assertIn("hybrid", str(exp.exception)) + + def test_decoding_with_unknown_format(self): + with self.assertRaises(ValueError) as e: + VerifyingKey.from_string(b"", valid_encodings=("raw", "foobar")) + + self.assertIn("Only uncompressed, compressed", str(e.exception)) + + def test_uncompressed_decoding_with_blocked_format(self): + enc = b( + "\x04" + "\x0c\xe0\x1d\xe0d\x1c\x8eS\x8a\xc0\x9eK\xa8x !\xd5\xc2\xc3" + "\xfd\xc8\xa0c\xff\xfb\x02\xb9\xc4\x84)\x1a\x0f\x8b\x87\xa4" + "z\x8a#\xb5\x97\xecO\xb6\xa0HQ\x89*" + ) + with self.assertRaises(MalformedPointError) as exp: + VerifyingKey.from_string(enc, valid_encodings=("hybrid",)) + + self.assertIn("Invalid X9.62 encoding", str(exp.exception)) + + def test_hybrid_decoding_with_blocked_format(self): + enc = b( + "\x06" + "\x0c\xe0\x1d\xe0d\x1c\x8eS\x8a\xc0\x9eK\xa8x !\xd5\xc2\xc3" + "\xfd\xc8\xa0c\xff\xfb\x02\xb9\xc4\x84)\x1a\x0f\x8b\x87\xa4" + "z\x8a#\xb5\x97\xecO\xb6\xa0HQ\x89*" + ) + with self.assertRaises(MalformedPointError) as exp: + VerifyingKey.from_string(enc, valid_encodings=("uncompressed",)) + + self.assertIn("Invalid X9.62 encoding", str(exp.exception)) + + def test_compressed_decoding_with_blocked_format(self): + enc = b( + "\x02" + "\x0c\xe0\x1d\xe0d\x1c\x8eS\x8a\xc0\x9eK\xa8x !\xd5\xc2\xc3" + "\xfd\xc8\xa0c\xff\xfb\x02\xb9\xc4\x84)\x1a\x0f\x8b\x87\xa4" + "z\x8a#\xb5\x97\xecO\xb6\xa0HQ\x89*" + )[:25] + with self.assertRaises(MalformedPointError) as exp: + VerifyingKey.from_string(enc, valid_encodings=("hybrid", "raw")) + + self.assertIn("(hybrid, raw)", str(exp.exception)) + + def test_decoding_with_malformed_uncompressed(self): + enc = b( + "\x0c\xe0\x1d\xe0d\x1c\x8eS\x8a\xc0\x9eK\xa8x !\xd5\xc2\xc3" + "\xfd\xc8\xa0c\xff\xfb\x02\xb9\xc4\x84)\x1a\x0f\x8b\x87\xa4" + "z\x8a#\xb5\x97\xecO\xb6\xa0HQ\x89*" + ) + + with self.assertRaises(MalformedPointError): + VerifyingKey.from_string(b"\x02" + enc) + + def test_decoding_with_malformed_compressed(self): + enc = b( + "\x0c\xe0\x1d\xe0d\x1c\x8eS\x8a\xc0\x9eK\xa8x !\xd5\xc2\xc3" + "\xfd\xc8\xa0c\xff\xfb\x02\xb9\xc4\x84)\x1a\x0f\x8b\x87\xa4" + "z\x8a#\xb5\x97\xecO\xb6\xa0HQ\x89*" + ) + + with self.assertRaises(MalformedPointError): + VerifyingKey.from_string(b"\x01" + enc[:24]) + + def test_decoding_with_inconsistent_hybrid(self): + enc = b( + "\x0c\xe0\x1d\xe0d\x1c\x8eS\x8a\xc0\x9eK\xa8x !\xd5\xc2\xc3" + "\xfd\xc8\xa0c\xff\xfb\x02\xb9\xc4\x84)\x1a\x0f\x8b\x87\xa4" + "z\x8a#\xb5\x97\xecO\xb6\xa0HQ\x89*" + ) + + with self.assertRaises(MalformedPointError): + VerifyingKey.from_string(b"\x07" + enc) + + def test_decoding_with_point_not_on_curve(self): + enc = b( + "\x0c\xe0\x1d\xe0d\x1c\x8eS\x8a\xc0\x9eK\xa8x !\xd5\xc2\xc3" + "\xfd\xc8\xa0c\xff\xfb\x02\xb9\xc4\x84)\x1a\x0f\x8b\x87\xa4" + "z\x8a#\xb5\x97\xecO\xb6\xa0HQ\x89*" + ) + + with self.assertRaises(MalformedPointError): + VerifyingKey.from_string(enc[:47] + b"\x00") + + def test_decoding_with_point_at_infinity(self): + # decoding it is unsupported, as it's not necessary to encode it + with self.assertRaises(MalformedPointError): + VerifyingKey.from_string(b"\x00") + + def test_not_lying_on_curve(self): + enc = number_to_string(NIST192p.curve.p(), NIST192p.curve.p() + 1) + + with self.assertRaises(MalformedPointError): + VerifyingKey.from_string(b"\x02" + enc) + + def test_from_string_with_invalid_curve_too_short_ver_key_len(self): + # both verifying_key_length and baselen are calculated internally + # by the Curve constructor, but since we depend on them verify + # that inconsistent values are detected + curve = Curve("test", ecdsa.curve_192, ecdsa.generator_192, (1, 2)) + curve.verifying_key_length = 16 + curve.baselen = 32 + + with self.assertRaises(MalformedPointError): + VerifyingKey.from_string(b"\x00" * 16, curve) + + def test_from_string_with_invalid_curve_too_long_ver_key_len(self): + # both verifying_key_length and baselen are calculated internally + # by the Curve constructor, but since we depend on them verify + # that inconsistent values are detected + curve = Curve("test", ecdsa.curve_192, ecdsa.generator_192, (1, 2)) + curve.verifying_key_length = 16 + curve.baselen = 16 + + with self.assertRaises(MalformedPointError): + VerifyingKey.from_string(b"\x00" * 16, curve) + + +@pytest.mark.parametrize( + "val,even", [(i, j) for i in range(256) for j in [True, False]] +) +def test_VerifyingKey_decode_with_small_values(val, even): + enc = number_to_string(val, NIST192p.order) + + if even: + enc = b"\x02" + enc + else: + enc = b"\x03" + enc + + # small values can both be actual valid public keys and not, verify that + # only expected exceptions are raised if they are not + try: + vk = VerifyingKey.from_string(enc) + assert isinstance(vk, VerifyingKey) + except MalformedPointError: + assert True + + +params = [] +for curve in curves: + for enc in ["raw", "uncompressed", "compressed", "hybrid"]: + params.append( + pytest.param(curve, enc, id="{0}-{1}".format(curve.name, enc)) + ) + + +@pytest.mark.parametrize("curve,encoding", params) +def test_VerifyingKey_encode_decode(curve, encoding): + sk = SigningKey.generate(curve=curve) + vk = sk.verifying_key + + encoded = vk.to_string(encoding) + + from_enc = VerifyingKey.from_string(encoded, curve=curve) + + assert vk.pubkey.point == from_enc.pubkey.point + + +if "--fast" in sys.argv: # pragma: no cover + params = [NIST192p, BRAINPOOLP160r1] +else: + params = curves + + +@pytest.mark.parametrize("curve", params) +def test_lengths(curve): + priv = SigningKey.generate(curve=curve) + pub1 = priv.get_verifying_key() + pub2 = VerifyingKey.from_string(pub1.to_string(), curve) + assert pub1.to_string() == pub2.to_string() + assert len(pub1.to_string()) == curve.verifying_key_length + sig = priv.sign(b"data") + assert len(sig) == curve.signature_length + + +@pytest.mark.slow +class OpenSSL(unittest.TestCase): + # test interoperability with OpenSSL tools. Note that openssl's ECDSA + # sign/verify arguments changed between 0.9.8 and 1.0.0: the early + # versions require "-ecdsa-with-SHA1", the later versions want just + # "-SHA1" (or to leave out that argument entirely, which means the + # signature will use some default digest algorithm, probably determined + # by the key, probably always SHA1). + # + # openssl ecparam -name secp224r1 -genkey -out privkey.pem + # openssl ec -in privkey.pem -text -noout # get the priv/pub keys + # openssl dgst -ecdsa-with-SHA1 -sign privkey.pem -out data.sig data.txt + # openssl asn1parse -in data.sig -inform DER + # data.sig is 64 bytes, probably 56b plus ASN1 overhead + # openssl dgst -ecdsa-with-SHA1 -prverify privkey.pem -signature data.sig data.txt ; echo $? + # openssl ec -in privkey.pem -pubout -out pubkey.pem + # openssl ec -in privkey.pem -pubout -outform DER -out pubkey.der + + OPENSSL_SUPPORTED_CURVES = set( + c.split(":")[0].strip() + for c in run_openssl("ecparam -list_curves").split("\n") + ) + + def get_openssl_messagedigest_arg(self, hash_name): + v = run_openssl("version") + # e.g. "OpenSSL 1.0.0 29 Mar 2010", or "OpenSSL 1.0.0a 1 Jun 2010", + # or "OpenSSL 0.9.8o 01 Jun 2010" + vs = v.split()[1].split(".") + if vs >= ["1", "0", "0"]: # pragma: no cover + return "-{0}".format(hash_name) + else: # pragma: no cover + return "-ecdsa-with-{0}".format(hash_name) + + # sk: 1:OpenSSL->python 2:python->OpenSSL + # vk: 3:OpenSSL->python 4:python->OpenSSL + # sig: 5:OpenSSL->python 6:python->OpenSSL + + @pytest.mark.slow + @pytest.mark.skipif( + "secp112r1" not in OPENSSL_SUPPORTED_CURVES, + reason="system openssl does not support secp112r1", + ) + def test_from_openssl_secp112r1(self): + return self.do_test_from_openssl(SECP112r1) + + @pytest.mark.slow + @pytest.mark.skipif( + "secp112r2" not in OPENSSL_SUPPORTED_CURVES, + reason="system openssl does not support secp112r2", + ) + def test_from_openssl_secp112r2(self): + return self.do_test_from_openssl(SECP112r2) + + @pytest.mark.slow + @pytest.mark.skipif( + "secp128r1" not in OPENSSL_SUPPORTED_CURVES, + reason="system openssl does not support secp128r1", + ) + def test_from_openssl_secp128r1(self): + return self.do_test_from_openssl(SECP128r1) + + @pytest.mark.slow + @pytest.mark.skipif( + "secp160r1" not in OPENSSL_SUPPORTED_CURVES, + reason="system openssl does not support secp160r1", + ) + def test_from_openssl_secp160r1(self): + return self.do_test_from_openssl(SECP160r1) + + @pytest.mark.slow + @pytest.mark.skipif( + "prime192v1" not in OPENSSL_SUPPORTED_CURVES, + reason="system openssl does not support prime192v1", + ) + def test_from_openssl_nist192p(self): + return self.do_test_from_openssl(NIST192p) + + @pytest.mark.slow + @pytest.mark.skipif( + "prime192v1" not in OPENSSL_SUPPORTED_CURVES, + reason="system openssl does not support prime192v1", + ) + def test_from_openssl_nist192p_sha256(self): + return self.do_test_from_openssl(NIST192p, "SHA256") + + @pytest.mark.slow + @pytest.mark.skipif( + "secp224r1" not in OPENSSL_SUPPORTED_CURVES, + reason="system openssl does not support secp224r1", + ) + def test_from_openssl_nist224p(self): + return self.do_test_from_openssl(NIST224p) + + @pytest.mark.slow + @pytest.mark.skipif( + "prime256v1" not in OPENSSL_SUPPORTED_CURVES, + reason="system openssl does not support prime256v1", + ) + def test_from_openssl_nist256p(self): + return self.do_test_from_openssl(NIST256p) + + @pytest.mark.slow + @pytest.mark.skipif( + "prime256v1" not in OPENSSL_SUPPORTED_CURVES, + reason="system openssl does not support prime256v1", + ) + def test_from_openssl_nist256p_sha384(self): + return self.do_test_from_openssl(NIST256p, "SHA384") + + @pytest.mark.slow + @pytest.mark.skipif( + "prime256v1" not in OPENSSL_SUPPORTED_CURVES, + reason="system openssl does not support prime256v1", + ) + def test_from_openssl_nist256p_sha512(self): + return self.do_test_from_openssl(NIST256p, "SHA512") + + @pytest.mark.slow + @pytest.mark.skipif( + "secp384r1" not in OPENSSL_SUPPORTED_CURVES, + reason="system openssl does not support secp384r1", + ) + def test_from_openssl_nist384p(self): + return self.do_test_from_openssl(NIST384p) + + @pytest.mark.slow + @pytest.mark.skipif( + "secp521r1" not in OPENSSL_SUPPORTED_CURVES, + reason="system openssl does not support secp521r1", + ) + def test_from_openssl_nist521p(self): + return self.do_test_from_openssl(NIST521p) + + @pytest.mark.slow + @pytest.mark.skipif( + "secp256k1" not in OPENSSL_SUPPORTED_CURVES, + reason="system openssl does not support secp256k1", + ) + def test_from_openssl_secp256k1(self): + return self.do_test_from_openssl(SECP256k1) + + @pytest.mark.slow + @pytest.mark.skipif( + "brainpoolP160r1" not in OPENSSL_SUPPORTED_CURVES, + reason="system openssl does not support brainpoolP160r1", + ) + def test_from_openssl_brainpoolp160r1(self): + return self.do_test_from_openssl(BRAINPOOLP160r1) + + @pytest.mark.slow + @pytest.mark.skipif( + "brainpoolP192r1" not in OPENSSL_SUPPORTED_CURVES, + reason="system openssl does not support brainpoolP192r1", + ) + def test_from_openssl_brainpoolp192r1(self): + return self.do_test_from_openssl(BRAINPOOLP192r1) + + @pytest.mark.slow + @pytest.mark.skipif( + "brainpoolP224r1" not in OPENSSL_SUPPORTED_CURVES, + reason="system openssl does not support brainpoolP224r1", + ) + def test_from_openssl_brainpoolp224r1(self): + return self.do_test_from_openssl(BRAINPOOLP224r1) + + @pytest.mark.slow + @pytest.mark.skipif( + "brainpoolP256r1" not in OPENSSL_SUPPORTED_CURVES, + reason="system openssl does not support brainpoolP256r1", + ) + def test_from_openssl_brainpoolp256r1(self): + return self.do_test_from_openssl(BRAINPOOLP256r1) + + @pytest.mark.slow + @pytest.mark.skipif( + "brainpoolP320r1" not in OPENSSL_SUPPORTED_CURVES, + reason="system openssl does not support brainpoolP320r1", + ) + def test_from_openssl_brainpoolp320r1(self): + return self.do_test_from_openssl(BRAINPOOLP320r1) + + @pytest.mark.slow + @pytest.mark.skipif( + "brainpoolP384r1" not in OPENSSL_SUPPORTED_CURVES, + reason="system openssl does not support brainpoolP384r1", + ) + def test_from_openssl_brainpoolp384r1(self): + return self.do_test_from_openssl(BRAINPOOLP384r1) + + @pytest.mark.slow + @pytest.mark.skipif( + "brainpoolP512r1" not in OPENSSL_SUPPORTED_CURVES, + reason="system openssl does not support brainpoolP512r1", + ) + def test_from_openssl_brainpoolp512r1(self): + return self.do_test_from_openssl(BRAINPOOLP512r1) + + @pytest.mark.slow + @pytest.mark.skipif( + "brainpoolP160t1" not in OPENSSL_SUPPORTED_CURVES, + reason="system openssl does not support brainpoolP160t1", + ) + def test_from_openssl_brainpoolp160t1(self): + return self.do_test_from_openssl(BRAINPOOLP160t1) + + @pytest.mark.slow + @pytest.mark.skipif( + "brainpoolP192t1" not in OPENSSL_SUPPORTED_CURVES, + reason="system openssl does not support brainpoolP192t1", + ) + def test_from_openssl_brainpoolp192t1(self): + return self.do_test_from_openssl(BRAINPOOLP192t1) + + @pytest.mark.slow + @pytest.mark.skipif( + "brainpoolP224t1" not in OPENSSL_SUPPORTED_CURVES, + reason="system openssl does not support brainpoolP224t1", + ) + def test_from_openssl_brainpoolp224t1(self): + return self.do_test_from_openssl(BRAINPOOLP224t1) + + @pytest.mark.slow + @pytest.mark.skipif( + "brainpoolP256t1" not in OPENSSL_SUPPORTED_CURVES, + reason="system openssl does not support brainpoolP256t1", + ) + def test_from_openssl_brainpoolp256t1(self): + return self.do_test_from_openssl(BRAINPOOLP256t1) + + @pytest.mark.slow + @pytest.mark.skipif( + "brainpoolP320t1" not in OPENSSL_SUPPORTED_CURVES, + reason="system openssl does not support brainpoolP320t1", + ) + def test_from_openssl_brainpoolp320t1(self): + return self.do_test_from_openssl(BRAINPOOLP320t1) + + @pytest.mark.slow + @pytest.mark.skipif( + "brainpoolP384t1" not in OPENSSL_SUPPORTED_CURVES, + reason="system openssl does not support brainpoolP384t1", + ) + def test_from_openssl_brainpoolp384t1(self): + return self.do_test_from_openssl(BRAINPOOLP384t1) + + @pytest.mark.slow + @pytest.mark.skipif( + "brainpoolP512t1" not in OPENSSL_SUPPORTED_CURVES, + reason="system openssl does not support brainpoolP512t1", + ) + def test_from_openssl_brainpoolp512t1(self): + return self.do_test_from_openssl(BRAINPOOLP512t1) + + def do_test_from_openssl(self, curve, hash_name="SHA1"): + curvename = curve.openssl_name + assert curvename + # OpenSSL: create sk, vk, sign. + # Python: read vk(3), checksig(5), read sk(1), sign, check + mdarg = self.get_openssl_messagedigest_arg(hash_name) + if os.path.isdir("t"): # pragma: no cover + shutil.rmtree("t") + os.mkdir("t") + run_openssl("ecparam -name %s -genkey -out t/privkey.pem" % curvename) + run_openssl("ec -in t/privkey.pem -pubout -out t/pubkey.pem") + data = b"data" + with open("t/data.txt", "wb") as e: + e.write(data) + run_openssl( + "dgst %s -sign t/privkey.pem -out t/data.sig t/data.txt" % mdarg + ) + run_openssl( + "dgst %s -verify t/pubkey.pem -signature t/data.sig t/data.txt" + % mdarg + ) + with open("t/pubkey.pem", "rb") as e: + pubkey_pem = e.read() + vk = VerifyingKey.from_pem(pubkey_pem) # 3 + with open("t/data.sig", "rb") as e: + sig_der = e.read() + self.assertTrue( + vk.verify( + sig_der, + data, # 5 + hashfunc=partial(hashlib.new, hash_name), + sigdecode=sigdecode_der, + ) + ) + + with open("t/privkey.pem") as e: + fp = e.read() + sk = SigningKey.from_pem(fp) # 1 + sig = sk.sign(data, hashfunc=partial(hashlib.new, hash_name)) + self.assertTrue( + vk.verify(sig, data, hashfunc=partial(hashlib.new, hash_name)) + ) + + run_openssl( + "pkcs8 -topk8 -nocrypt " + "-in t/privkey.pem -outform pem -out t/privkey-p8.pem" + ) + with open("t/privkey-p8.pem", "rb") as e: + privkey_p8_pem = e.read() + sk_from_p8 = SigningKey.from_pem(privkey_p8_pem) + self.assertEqual(sk, sk_from_p8) + + @pytest.mark.slow + @pytest.mark.skipif( + "secp112r1" not in OPENSSL_SUPPORTED_CURVES, + reason="system openssl does not support secp112r1", + ) + def test_to_openssl_secp112r1(self): + self.do_test_to_openssl(SECP112r1) + + @pytest.mark.slow + @pytest.mark.skipif( + "secp112r2" not in OPENSSL_SUPPORTED_CURVES, + reason="system openssl does not support secp112r2", + ) + def test_to_openssl_secp112r2(self): + self.do_test_to_openssl(SECP112r2) + + @pytest.mark.slow + @pytest.mark.skipif( + "secp128r1" not in OPENSSL_SUPPORTED_CURVES, + reason="system openssl does not support secp128r1", + ) + def test_to_openssl_secp128r1(self): + self.do_test_to_openssl(SECP128r1) + + @pytest.mark.slow + @pytest.mark.skipif( + "secp160r1" not in OPENSSL_SUPPORTED_CURVES, + reason="system openssl does not support secp160r1", + ) + def test_to_openssl_secp160r1(self): + self.do_test_to_openssl(SECP160r1) + + @pytest.mark.slow + @pytest.mark.skipif( + "prime192v1" not in OPENSSL_SUPPORTED_CURVES, + reason="system openssl does not support prime192v1", + ) + def test_to_openssl_nist192p(self): + self.do_test_to_openssl(NIST192p) + + @pytest.mark.slow + @pytest.mark.skipif( + "prime192v1" not in OPENSSL_SUPPORTED_CURVES, + reason="system openssl does not support prime192v1", + ) + def test_to_openssl_nist192p_sha256(self): + self.do_test_to_openssl(NIST192p, "SHA256") + + @pytest.mark.slow + @pytest.mark.skipif( + "secp224r1" not in OPENSSL_SUPPORTED_CURVES, + reason="system openssl does not support secp224r1", + ) + def test_to_openssl_nist224p(self): + self.do_test_to_openssl(NIST224p) + + @pytest.mark.slow + @pytest.mark.skipif( + "prime256v1" not in OPENSSL_SUPPORTED_CURVES, + reason="system openssl does not support prime256v1", + ) + def test_to_openssl_nist256p(self): + self.do_test_to_openssl(NIST256p) + + @pytest.mark.slow + @pytest.mark.skipif( + "prime256v1" not in OPENSSL_SUPPORTED_CURVES, + reason="system openssl does not support prime256v1", + ) + def test_to_openssl_nist256p_sha384(self): + self.do_test_to_openssl(NIST256p, "SHA384") + + @pytest.mark.slow + @pytest.mark.skipif( + "prime256v1" not in OPENSSL_SUPPORTED_CURVES, + reason="system openssl does not support prime256v1", + ) + def test_to_openssl_nist256p_sha512(self): + self.do_test_to_openssl(NIST256p, "SHA512") + + @pytest.mark.slow + @pytest.mark.skipif( + "secp384r1" not in OPENSSL_SUPPORTED_CURVES, + reason="system openssl does not support secp384r1", + ) + def test_to_openssl_nist384p(self): + self.do_test_to_openssl(NIST384p) + + @pytest.mark.slow + @pytest.mark.skipif( + "secp521r1" not in OPENSSL_SUPPORTED_CURVES, + reason="system openssl does not support secp521r1", + ) + def test_to_openssl_nist521p(self): + self.do_test_to_openssl(NIST521p) + + @pytest.mark.slow + @pytest.mark.skipif( + "secp256k1" not in OPENSSL_SUPPORTED_CURVES, + reason="system openssl does not support secp256k1", + ) + def test_to_openssl_secp256k1(self): + self.do_test_to_openssl(SECP256k1) + + @pytest.mark.slow + @pytest.mark.skipif( + "brainpoolP160r1" not in OPENSSL_SUPPORTED_CURVES, + reason="system openssl does not support brainpoolP160r1", + ) + def test_to_openssl_brainpoolp160r1(self): + self.do_test_to_openssl(BRAINPOOLP160r1) + + @pytest.mark.slow + @pytest.mark.skipif( + "brainpoolP192r1" not in OPENSSL_SUPPORTED_CURVES, + reason="system openssl does not support brainpoolP192r1", + ) + def test_to_openssl_brainpoolp192r1(self): + self.do_test_to_openssl(BRAINPOOLP192r1) + + @pytest.mark.slow + @pytest.mark.skipif( + "brainpoolP224r1" not in OPENSSL_SUPPORTED_CURVES, + reason="system openssl does not support brainpoolP224r1", + ) + def test_to_openssl_brainpoolp224r1(self): + self.do_test_to_openssl(BRAINPOOLP224r1) + + @pytest.mark.slow + @pytest.mark.skipif( + "brainpoolP256r1" not in OPENSSL_SUPPORTED_CURVES, + reason="system openssl does not support brainpoolP256r1", + ) + def test_to_openssl_brainpoolp256r1(self): + self.do_test_to_openssl(BRAINPOOLP256r1) + + @pytest.mark.slow + @pytest.mark.skipif( + "brainpoolP320r1" not in OPENSSL_SUPPORTED_CURVES, + reason="system openssl does not support brainpoolP320r1", + ) + def test_to_openssl_brainpoolp320r1(self): + self.do_test_to_openssl(BRAINPOOLP320r1) + + @pytest.mark.slow + @pytest.mark.skipif( + "brainpoolP384r1" not in OPENSSL_SUPPORTED_CURVES, + reason="system openssl does not support brainpoolP384r1", + ) + def test_to_openssl_brainpoolp384r1(self): + self.do_test_to_openssl(BRAINPOOLP384r1) + + @pytest.mark.slow + @pytest.mark.skipif( + "brainpoolP512r1" not in OPENSSL_SUPPORTED_CURVES, + reason="system openssl does not support brainpoolP512r1", + ) + def test_to_openssl_brainpoolp512r1(self): + self.do_test_to_openssl(BRAINPOOLP512r1) + + @pytest.mark.slow + @pytest.mark.skipif( + "brainpoolP160t1" not in OPENSSL_SUPPORTED_CURVES, + reason="system openssl does not support brainpoolP160t1", + ) + def test_to_openssl_brainpoolp160t1(self): + self.do_test_to_openssl(BRAINPOOLP160t1) + + @pytest.mark.slow + @pytest.mark.skipif( + "brainpoolP192t1" not in OPENSSL_SUPPORTED_CURVES, + reason="system openssl does not support brainpoolP192t1", + ) + def test_to_openssl_brainpoolp192t1(self): + self.do_test_to_openssl(BRAINPOOLP192t1) + + @pytest.mark.slow + @pytest.mark.skipif( + "brainpoolP224t1" not in OPENSSL_SUPPORTED_CURVES, + reason="system openssl does not support brainpoolP224t1", + ) + def test_to_openssl_brainpoolp224t1(self): + self.do_test_to_openssl(BRAINPOOLP224t1) + + @pytest.mark.slow + @pytest.mark.skipif( + "brainpoolP256t1" not in OPENSSL_SUPPORTED_CURVES, + reason="system openssl does not support brainpoolP256t1", + ) + def test_to_openssl_brainpoolp256t1(self): + self.do_test_to_openssl(BRAINPOOLP256t1) + + @pytest.mark.slow + @pytest.mark.skipif( + "brainpoolP320t1" not in OPENSSL_SUPPORTED_CURVES, + reason="system openssl does not support brainpoolP320t1", + ) + def test_to_openssl_brainpoolp320t1(self): + self.do_test_to_openssl(BRAINPOOLP320t1) + + @pytest.mark.slow + @pytest.mark.skipif( + "brainpoolP384t1" not in OPENSSL_SUPPORTED_CURVES, + reason="system openssl does not support brainpoolP384t1", + ) + def test_to_openssl_brainpoolp384t1(self): + self.do_test_to_openssl(BRAINPOOLP384t1) + + @pytest.mark.slow + @pytest.mark.skipif( + "brainpoolP512t1" not in OPENSSL_SUPPORTED_CURVES, + reason="system openssl does not support brainpoolP512t1", + ) + def test_to_openssl_brainpoolp512t1(self): + self.do_test_to_openssl(BRAINPOOLP512t1) + + def do_test_to_openssl(self, curve, hash_name="SHA1"): + curvename = curve.openssl_name + assert curvename + # Python: create sk, vk, sign. + # OpenSSL: read vk(4), checksig(6), read sk(2), sign, check + mdarg = self.get_openssl_messagedigest_arg(hash_name) + if os.path.isdir("t"): # pragma: no cover + shutil.rmtree("t") + os.mkdir("t") + sk = SigningKey.generate(curve=curve) + vk = sk.get_verifying_key() + data = b"data" + with open("t/pubkey.der", "wb") as e: + e.write(vk.to_der()) # 4 + with open("t/pubkey.pem", "wb") as e: + e.write(vk.to_pem()) # 4 + sig_der = sk.sign( + data, + hashfunc=partial(hashlib.new, hash_name), + sigencode=sigencode_der, + ) + + with open("t/data.sig", "wb") as e: + e.write(sig_der) # 6 + with open("t/data.txt", "wb") as e: + e.write(data) + with open("t/baddata.txt", "wb") as e: + e.write(data + b"corrupt") + + self.assertRaises( + SubprocessError, + run_openssl, + "dgst %s -verify t/pubkey.der -keyform DER -signature t/data.sig t/baddata.txt" + % mdarg, + ) + run_openssl( + "dgst %s -verify t/pubkey.der -keyform DER -signature t/data.sig t/data.txt" + % mdarg + ) + + with open("t/privkey.pem", "wb") as e: + e.write(sk.to_pem()) # 2 + run_openssl( + "dgst %s -sign t/privkey.pem -out t/data.sig2 t/data.txt" % mdarg + ) + run_openssl( + "dgst %s -verify t/pubkey.pem -signature t/data.sig2 t/data.txt" + % mdarg + ) + + with open("t/privkey-explicit.pem", "wb") as e: + e.write(sk.to_pem(curve_parameters_encoding="explicit")) + run_openssl( + "dgst %s -sign t/privkey-explicit.pem -out t/data.sig2 t/data.txt" + % mdarg + ) + run_openssl( + "dgst %s -verify t/pubkey.pem -signature t/data.sig2 t/data.txt" + % mdarg + ) + + with open("t/privkey-p8.pem", "wb") as e: + e.write(sk.to_pem(format="pkcs8")) + run_openssl( + "dgst %s -sign t/privkey-p8.pem -out t/data.sig3 t/data.txt" + % mdarg + ) + run_openssl( + "dgst %s -verify t/pubkey.pem -signature t/data.sig3 t/data.txt" + % mdarg + ) + + with open("t/privkey-p8-explicit.pem", "wb") as e: + e.write( + sk.to_pem(format="pkcs8", curve_parameters_encoding="explicit") + ) + run_openssl( + "dgst %s -sign t/privkey-p8-explicit.pem -out t/data.sig3 t/data.txt" + % mdarg + ) + run_openssl( + "dgst %s -verify t/pubkey.pem -signature t/data.sig3 t/data.txt" + % mdarg + ) + + OPENSSL_SUPPORTED_TYPES = set() + try: + if "-rawin" in run_openssl("pkeyutl -help"): + OPENSSL_SUPPORTED_TYPES = set( # pragma: no branch + c.lower() + for c in ("ED25519", "ED448") + if c in run_openssl("list -public-key-methods") + ) + except SubprocessError: # pragma: no cover + pass + + def do_eddsa_test_to_openssl(self, curve): + if os.path.isdir("t"): + shutil.rmtree("t") + os.mkdir("t") + + sk = SigningKey.generate(curve=curve) + vk = sk.get_verifying_key() + + data = b"data" + with open("t/pubkey.der", "wb") as e: + e.write(vk.to_der()) + with open("t/pubkey.pem", "wb") as e: + e.write(vk.to_pem()) + + sig = sk.sign(data) + + with open("t/data.sig", "wb") as e: + e.write(sig) + with open("t/data.txt", "wb") as e: + e.write(data) + with open("t/baddata.txt", "wb") as e: + e.write(data + b"corrupt") + + with self.assertRaises(SubprocessError): + run_openssl( + "pkeyutl -verify -pubin -inkey t/pubkey.pem -rawin " + "-in t/baddata.txt -sigfile t/data.sig" + ) + run_openssl( + "pkeyutl -verify -pubin -inkey t/pubkey.pem -rawin " + "-in t/data.txt -sigfile t/data.sig" + ) + + shutil.rmtree("t") + + # in practice at least OpenSSL 3.0.0 is needed to make EdDSA signatures + # earlier versions support EdDSA only in X.509 certificates + @pytest.mark.slow + @pytest.mark.skipif( + "ed25519" not in OPENSSL_SUPPORTED_TYPES, + reason="system openssl does not support signing with Ed25519", + ) + def test_to_openssl_ed25519(self): + return self.do_eddsa_test_to_openssl(Ed25519) + + @pytest.mark.slow + @pytest.mark.skipif( + "ed448" not in OPENSSL_SUPPORTED_TYPES, + reason="system openssl does not support signing with Ed448", + ) + def test_to_openssl_ed448(self): + return self.do_eddsa_test_to_openssl(Ed448) + + def do_eddsa_test_from_openssl(self, curve): + curvename = curve.name + + if os.path.isdir("t"): + shutil.rmtree("t") + os.mkdir("t") + + data = b"data" + + run_openssl( + "genpkey -algorithm {0} -outform PEM -out t/privkey.pem".format( + curvename + ) + ) + run_openssl( + "pkey -outform PEM -pubout -in t/privkey.pem -out t/pubkey.pem" + ) + + with open("t/data.txt", "wb") as e: + e.write(data) + run_openssl( + "pkeyutl -sign -inkey t/privkey.pem " + "-rawin -in t/data.txt -out t/data.sig" + ) + + with open("t/data.sig", "rb") as e: + sig = e.read() + with open("t/pubkey.pem", "rb") as e: + vk = VerifyingKey.from_pem(e.read()) + + self.assertIs(vk.curve, curve) + + vk.verify(sig, data) + + shutil.rmtree("t") + + @pytest.mark.slow + @pytest.mark.skipif( + "ed25519" not in OPENSSL_SUPPORTED_TYPES, + reason="system openssl does not support signing with Ed25519", + ) + def test_from_openssl_ed25519(self): + return self.do_eddsa_test_from_openssl(Ed25519) + + @pytest.mark.slow + @pytest.mark.skipif( + "ed448" not in OPENSSL_SUPPORTED_TYPES, + reason="system openssl does not support signing with Ed448", + ) + def test_from_openssl_ed448(self): + return self.do_eddsa_test_from_openssl(Ed448) + + +class TooSmallCurve(unittest.TestCase): + OPENSSL_SUPPORTED_CURVES = set( + c.split(":")[0].strip() + for c in run_openssl("ecparam -list_curves").split("\n") + ) + + @pytest.mark.skipif( + "prime192v1" not in OPENSSL_SUPPORTED_CURVES, + reason="system openssl does not support prime192v1", + ) + def test_sign_too_small_curve_dont_allow_truncate_raises(self): + sk = SigningKey.generate(curve=NIST192p) + data = b"data" + with self.assertRaises(BadDigestError): + sk.sign( + data, + hashfunc=partial(hashlib.new, "SHA256"), + sigencode=sigencode_der, + allow_truncate=False, + ) + + @pytest.mark.skipif( + "prime192v1" not in OPENSSL_SUPPORTED_CURVES, + reason="system openssl does not support prime192v1", + ) + def test_verify_too_small_curve_dont_allow_truncate_raises(self): + sk = SigningKey.generate(curve=NIST192p) + vk = sk.get_verifying_key() + data = b"data" + sig_der = sk.sign( + data, + hashfunc=partial(hashlib.new, "SHA256"), + sigencode=sigencode_der, + allow_truncate=True, + ) + with self.assertRaises(BadDigestError): + vk.verify( + sig_der, + data, + hashfunc=partial(hashlib.new, "SHA256"), + sigdecode=sigdecode_der, + allow_truncate=False, + ) + + +class DER(unittest.TestCase): + def test_integer(self): + self.assertEqual(der.encode_integer(0), b"\x02\x01\x00") + self.assertEqual(der.encode_integer(1), b"\x02\x01\x01") + self.assertEqual(der.encode_integer(127), b"\x02\x01\x7f") + self.assertEqual(der.encode_integer(128), b"\x02\x02\x00\x80") + self.assertEqual(der.encode_integer(256), b"\x02\x02\x01\x00") + # self.assertEqual(der.encode_integer(-1), b"\x02\x01\xff") + + def s(n): + return der.remove_integer(der.encode_integer(n) + b"junk") + + self.assertEqual(s(0), (0, b"junk")) + self.assertEqual(s(1), (1, b"junk")) + self.assertEqual(s(127), (127, b"junk")) + self.assertEqual(s(128), (128, b"junk")) + self.assertEqual(s(256), (256, b"junk")) + self.assertEqual( + s(1234567890123456789012345678901234567890), + (1234567890123456789012345678901234567890, b"junk"), + ) + + def test_number(self): + self.assertEqual(der.encode_number(0), b"\x00") + self.assertEqual(der.encode_number(127), b"\x7f") + self.assertEqual(der.encode_number(128), b"\x81\x00") + self.assertEqual(der.encode_number(3 * 128 + 7), b"\x83\x07") + # self.assertEqual(der.read_number("\x81\x9b" + "more"), (155, 2)) + # self.assertEqual(der.encode_number(155), b"\x81\x9b") + for n in (0, 1, 2, 127, 128, 3 * 128 + 7, 840, 10045): # , 155): + x = der.encode_number(n) + b"more" + n1, llen = der.read_number(x) + self.assertEqual(n1, n) + self.assertEqual(x[llen:], b"more") + + def test_length(self): + self.assertEqual(der.encode_length(0), b"\x00") + self.assertEqual(der.encode_length(127), b"\x7f") + self.assertEqual(der.encode_length(128), b"\x81\x80") + self.assertEqual(der.encode_length(255), b"\x81\xff") + self.assertEqual(der.encode_length(256), b"\x82\x01\x00") + self.assertEqual(der.encode_length(3 * 256 + 7), b"\x82\x03\x07") + self.assertEqual(der.read_length(b"\x81\x9b" + b"more"), (155, 2)) + self.assertEqual(der.encode_length(155), b"\x81\x9b") + for n in (0, 1, 2, 127, 128, 255, 256, 3 * 256 + 7, 155): + x = der.encode_length(n) + b"more" + n1, llen = der.read_length(x) + self.assertEqual(n1, n) + self.assertEqual(x[llen:], b"more") + + def test_sequence(self): + x = der.encode_sequence(b"ABC", b"DEF") + b"GHI" + self.assertEqual(x, b"\x30\x06ABCDEFGHI") + x1, rest = der.remove_sequence(x) + self.assertEqual(x1, b"ABCDEF") + self.assertEqual(rest, b"GHI") + + def test_constructed(self): + x = der.encode_constructed(0, NIST224p.encoded_oid) + self.assertEqual(hexlify(x), b"a007" + b"06052b81040021") + x = der.encode_constructed(1, unhexlify(b"0102030a0b0c")) + self.assertEqual(hexlify(x), b"a106" + b"0102030a0b0c") + + +class Util(unittest.TestCase): + @pytest.mark.slow + def test_trytryagain(self): + tta = util.randrange_from_seed__trytryagain + for i in range(1000): + seed = "seed-%d" % i + for order in ( + 2**8 - 2, + 2**8 - 1, + 2**8, + 2**8 + 1, + 2**8 + 2, + 2**16 - 1, + 2**16 + 1, + ): + n = tta(seed, order) + self.assertTrue(1 <= n < order, (1, n, order)) + # this trytryagain *does* provide long-term stability + self.assertEqual( + ("%x" % (tta("seed", NIST224p.order))).encode(), + b"6fa59d73bf0446ae8743cf748fc5ac11d5585a90356417e97155c3bc", + ) + + def test_trytryagain_single(self): + tta = util.randrange_from_seed__trytryagain + order = 2**8 - 2 + seed = b"text" + n = tta(seed, order) + # known issue: https://github.com/warner/python-ecdsa/issues/221 + if sys.version_info < (3, 0): # pragma: no branch + self.assertEqual(n, 228) + else: # pragma: no branch + self.assertEqual(n, 18) + + @settings(**HYP_SETTINGS) + @given(st.integers(min_value=0, max_value=10**200)) + def test_randrange(self, i): + # util.randrange does not provide long-term stability: we might + # change the algorithm in the future. + entropy = util.PRNG("seed-%d" % i) + for order in ( + 2**8 - 2, + 2**8 - 1, + 2**8, + 2**16 - 1, + 2**16 + 1, + ): + # that oddball 2**16+1 takes half our runtime + n = util.randrange(order, entropy=entropy) + self.assertTrue(1 <= n < order, (1, n, order)) + + def OFF_test_prove_uniformity(self): # pragma: no cover + order = 2**8 - 2 + counts = dict([(i, 0) for i in range(1, order)]) + assert 0 not in counts + assert order not in counts + for i in range(1000000): + seed = "seed-%d" % i + n = util.randrange_from_seed__trytryagain(seed, order) + counts[n] += 1 + # this technique should use the full range + self.assertTrue(counts[order - 1]) + for i in range(1, order): + print_("%3d: %s" % (i, "*" * (counts[i] // 100))) + + +class RFC6979(unittest.TestCase): + # https://tools.ietf.org/html/rfc6979#appendix-A.1 + def _do(self, generator, secexp, hsh, hash_func, expected): + actual = rfc6979.generate_k(generator.order(), secexp, hash_func, hsh) + self.assertEqual(expected, actual) + + def test_SECP256k1(self): + """RFC doesn't contain test vectors for SECP256k1 used in bitcoin. + This vector has been computed by Golang reference implementation instead.""" + self._do( + generator=SECP256k1.generator, + secexp=int("9d0219792467d7d37b4d43298a7d0c05", 16), + hsh=hashlib.sha256(b"sample").digest(), + hash_func=hashlib.sha256, + expected=int( + "8fa1f95d514760e498f28957b824ee6ec39ed64826ff4fecc2b5739ec45b91cd", + 16, + ), + ) + + def test_SECP256k1_2(self): + self._do( + generator=SECP256k1.generator, + secexp=int( + "cca9fbcc1b41e5a95d369eaa6ddcff73b61a4efaa279cfc6567e8daa39cbaf50", + 16, + ), + hsh=hashlib.sha256(b"sample").digest(), + hash_func=hashlib.sha256, + expected=int( + "2df40ca70e639d89528a6b670d9d48d9165fdc0febc0974056bdce192b8e16a3", + 16, + ), + ) + + def test_SECP256k1_3(self): + self._do( + generator=SECP256k1.generator, + secexp=0x1, + hsh=hashlib.sha256(b"Satoshi Nakamoto").digest(), + hash_func=hashlib.sha256, + expected=0x8F8A276C19F4149656B280621E358CCE24F5F52542772691EE69063B74F15D15, + ) + + def test_SECP256k1_4(self): + self._do( + generator=SECP256k1.generator, + secexp=0x1, + hsh=hashlib.sha256( + b"All those moments will be lost in time, like tears in rain. Time to die..." + ).digest(), + hash_func=hashlib.sha256, + expected=0x38AA22D72376B4DBC472E06C3BA403EE0A394DA63FC58D88686C611ABA98D6B3, + ) + + def test_SECP256k1_5(self): + self._do( + generator=SECP256k1.generator, + secexp=0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364140, + hsh=hashlib.sha256(b"Satoshi Nakamoto").digest(), + hash_func=hashlib.sha256, + expected=0x33A19B60E25FB6F4435AF53A3D42D493644827367E6453928554F43E49AA6F90, + ) + + def test_SECP256k1_6(self): + self._do( + generator=SECP256k1.generator, + secexp=0xF8B8AF8CE3C7CCA5E300D33939540C10D45CE001B8F252BFBC57BA0342904181, + hsh=hashlib.sha256(b"Alan Turing").digest(), + hash_func=hashlib.sha256, + expected=0x525A82B70E67874398067543FD84C83D30C175FDC45FDEEE082FE13B1D7CFDF1, + ) + + def test_1(self): + # Basic example of the RFC, it also tests 'try-try-again' from Step H of rfc6979 + self._do( + generator=Point( + None, + 0, + 0, + int("4000000000000000000020108A2E0CC0D99F8A5EF", 16), + ), + secexp=int("09A4D6792295A7F730FC3F2B49CBC0F62E862272F", 16), + hsh=unhexlify( + b( + "AF2BDBE1AA9B6EC1E2ADE1D694F41FC71A831D0268E9891562113D8A62ADD1BF" + ) + ), + hash_func=hashlib.sha256, + expected=int("23AF4074C90A02B3FE61D286D5C87F425E6BDD81B", 16), + ) + + def test_2(self): + self._do( + generator=NIST192p.generator, + secexp=int("6FAB034934E4C0FC9AE67F5B5659A9D7D1FEFD187EE09FD4", 16), + hsh=hashlib.sha1(b"sample").digest(), + hash_func=hashlib.sha1, + expected=int( + "37D7CA00D2C7B0E5E412AC03BD44BA837FDD5B28CD3B0021", 16 + ), + ) + + def test_3(self): + self._do( + generator=NIST192p.generator, + secexp=int("6FAB034934E4C0FC9AE67F5B5659A9D7D1FEFD187EE09FD4", 16), + hsh=hashlib.sha256(b"sample").digest(), + hash_func=hashlib.sha256, + expected=int( + "32B1B6D7D42A05CB449065727A84804FB1A3E34D8F261496", 16 + ), + ) + + def test_4(self): + self._do( + generator=NIST192p.generator, + secexp=int("6FAB034934E4C0FC9AE67F5B5659A9D7D1FEFD187EE09FD4", 16), + hsh=hashlib.sha512(b"sample").digest(), + hash_func=hashlib.sha512, + expected=int( + "A2AC7AB055E4F20692D49209544C203A7D1F2C0BFBC75DB1", 16 + ), + ) + + def test_5(self): + self._do( + generator=NIST192p.generator, + secexp=int("6FAB034934E4C0FC9AE67F5B5659A9D7D1FEFD187EE09FD4", 16), + hsh=hashlib.sha1(b"test").digest(), + hash_func=hashlib.sha1, + expected=int( + "D9CF9C3D3297D3260773A1DA7418DB5537AB8DD93DE7FA25", 16 + ), + ) + + def test_6(self): + self._do( + generator=NIST192p.generator, + secexp=int("6FAB034934E4C0FC9AE67F5B5659A9D7D1FEFD187EE09FD4", 16), + hsh=hashlib.sha256(b"test").digest(), + hash_func=hashlib.sha256, + expected=int( + "5C4CE89CF56D9E7C77C8585339B006B97B5F0680B4306C6C", 16 + ), + ) + + def test_7(self): + self._do( + generator=NIST192p.generator, + secexp=int("6FAB034934E4C0FC9AE67F5B5659A9D7D1FEFD187EE09FD4", 16), + hsh=hashlib.sha512(b"test").digest(), + hash_func=hashlib.sha512, + expected=int( + "0758753A5254759C7CFBAD2E2D9B0792EEE44136C9480527", 16 + ), + ) + + def test_8(self): + self._do( + generator=NIST521p.generator, + secexp=int( + "0FAD06DAA62BA3B25D2FB40133DA757205DE67F5BB0018FEE8C86E1B68C7E75CAA896EB32F1F47C70855836A6D16FCC1466F6D8FBEC67DB89EC0C08B0E996B83538", + 16, + ), + hsh=hashlib.sha1(b"sample").digest(), + hash_func=hashlib.sha1, + expected=int( + "089C071B419E1C2820962321787258469511958E80582E95D8378E0C2CCDB3CB42BEDE42F50E3FA3C71F5A76724281D31D9C89F0F91FC1BE4918DB1C03A5838D0F9", + 16, + ), + ) + + def test_9(self): + self._do( + generator=NIST521p.generator, + secexp=int( + "0FAD06DAA62BA3B25D2FB40133DA757205DE67F5BB0018FEE8C86E1B68C7E75CAA896EB32F1F47C70855836A6D16FCC1466F6D8FBEC67DB89EC0C08B0E996B83538", + 16, + ), + hsh=hashlib.sha256(b"sample").digest(), + hash_func=hashlib.sha256, + expected=int( + "0EDF38AFCAAECAB4383358B34D67C9F2216C8382AAEA44A3DAD5FDC9C32575761793FEF24EB0FC276DFC4F6E3EC476752F043CF01415387470BCBD8678ED2C7E1A0", + 16, + ), + ) + + def test_10(self): + self._do( + generator=NIST521p.generator, + secexp=int( + "0FAD06DAA62BA3B25D2FB40133DA757205DE67F5BB0018FEE8C86E1B68C7E75CAA896EB32F1F47C70855836A6D16FCC1466F6D8FBEC67DB89EC0C08B0E996B83538", + 16, + ), + hsh=hashlib.sha512(b"test").digest(), + hash_func=hashlib.sha512, + expected=int( + "16200813020EC986863BEDFC1B121F605C1215645018AEA1A7B215A564DE9EB1B38A67AA1128B80CE391C4FB71187654AAA3431027BFC7F395766CA988C964DC56D", + 16, + ), + ) + + +class ECDH(unittest.TestCase): + def _do(self, curve, generator, dA, x_qA, y_qA, dB, x_qB, y_qB, x_Z, y_Z): + qA = dA * generator + qB = dB * generator + Z = dA * qB + self.assertEqual(Point(curve, x_qA, y_qA), qA) + self.assertEqual(Point(curve, x_qB, y_qB), qB) + self.assertTrue( + (dA * qB) + == (dA * dB * generator) + == (dB * dA * generator) + == (dB * qA) + ) + self.assertEqual(Point(curve, x_Z, y_Z), Z) + + +class RFC6932(ECDH): + # https://tools.ietf.org/html/rfc6932#appendix-A.1 + + def test_brainpoolP224r1(self): + self._do( + curve=curve_brainpoolp224r1, + generator=BRAINPOOLP224r1.generator, + dA=int( + "7C4B7A2C8A4BAD1FBB7D79CC0955DB7C6A4660CA64CC4778159B495E", 16 + ), + x_qA=int( + "B104A67A6F6E85E14EC1825E1539E8ECDBBF584922367DD88C6BDCF2", 16 + ), + y_qA=int( + "46D782E7FDB5F60CD8404301AC5949C58EDB26BC68BA07695B750A94", 16 + ), + dB=int( + "63976D4AAE6CD0F6DD18DEFEF55D96569D0507C03E74D6486FFA28FB", 16 + ), + x_qB=int( + "2A97089A9296147B71B21A4B574E1278245B536F14D8C2B9D07A874E", 16 + ), + y_qB=int( + "9B900D7C77A709A797276B8CA1BA61BB95B546FC29F862E44D59D25B", 16 + ), + x_Z=int( + "312DFD98783F9FB77B9704945A73BEB6DCCBE3B65D0F967DCAB574EB", 16 + ), + y_Z=int( + "6F800811D64114B1C48C621AB3357CF93F496E4238696A2A012B3C98", 16 + ), + ) + + def test_brainpoolP256r1(self): + self._do( + curve=curve_brainpoolp256r1, + generator=BRAINPOOLP256r1.generator, + dA=int( + "041EB8B1E2BC681BCE8E39963B2E9FC415B05283313DD1A8BCC055F11AE" + "49699", + 16, + ), + x_qA=int( + "78028496B5ECAAB3C8B6C12E45DB1E02C9E4D26B4113BC4F015F60C5C" + "CC0D206", + 16, + ), + y_qA=int( + "A2AE1762A3831C1D20F03F8D1E3C0C39AFE6F09B4D44BBE80CD100987" + "B05F92B", + 16, + ), + dB=int( + "06F5240EACDB9837BC96D48274C8AA834B6C87BA9CC3EEDD81F99A16B8D" + "804D3", + 16, + ), + x_qB=int( + "8E07E219BA588916C5B06AA30A2F464C2F2ACFC1610A3BE2FB240B635" + "341F0DB", + 16, + ), + y_qB=int( + "148EA1D7D1E7E54B9555B6C9AC90629C18B63BEE5D7AA6949EBBF47B2" + "4FDE40D", + 16, + ), + x_Z=int( + "05E940915549E9F6A4A75693716E37466ABA79B4BF2919877A16DD2CC2" + "E23708", + 16, + ), + y_Z=int( + "6BC23B6702BC5A019438CEEA107DAAD8B94232FFBBC350F3B137628FE6" + "FD134C", + 16, + ), + ) + + @pytest.mark.slow + def test_brainpoolP384r1(self): + self._do( + curve=curve_brainpoolp384r1, + generator=BRAINPOOLP384r1.generator, + dA=int( + "014EC0755B78594BA47FB0A56F6173045B4331E74BA1A6F47322E70D79D" + "828D97E095884CA72B73FDABD5910DF0FA76A", + 16, + ), + x_qA=int( + "45CB26E4384DAF6FB776885307B9A38B7AD1B5C692E0C32F012533277" + "8F3B8D3F50CA358099B30DEB5EE69A95C058B4E", + 16, + ), + y_qA=int( + "8173A1C54AFFA7E781D0E1E1D12C0DC2B74F4DF58E4A4E3AF7026C5D3" + "2DC530A2CD89C859BB4B4B768497F49AB8CC859", + 16, + ), + dB=int( + "6B461CB79BD0EA519A87D6828815D8CE7CD9B3CAA0B5A8262CBCD550A01" + "5C90095B976F3529957506E1224A861711D54", + 16, + ), + x_qB=int( + "01BF92A92EE4BE8DED1A911125C209B03F99E3161CFCC986DC7711383" + "FC30AF9CE28CA3386D59E2C8D72CE1E7B4666E8", + 16, + ), + y_qB=int( + "3289C4A3A4FEE035E39BDB885D509D224A142FF9FBCC5CFE5CCBB3026" + "8EE47487ED8044858D31D848F7A95C635A347AC", + 16, + ), + x_Z=int( + "04CC4FF3DCCCB07AF24E0ACC529955B36D7C807772B92FCBE48F3AFE9A" + "2F370A1F98D3FA73FD0C0747C632E12F1423EC", + 16, + ), + y_Z=int( + "7F465F90BD69AFB8F828A214EB9716D66ABC59F17AF7C75EE7F1DE22AB" + "5D05085F5A01A9382D05BF72D96698FE3FF64E", + 16, + ), + ) + + @pytest.mark.slow + def test_brainpoolP512r1(self): + self._do( + curve=curve_brainpoolp512r1, + generator=BRAINPOOLP512r1.generator, + dA=int( + "636B6BE0482A6C1C41AA7AE7B245E983392DB94CECEA2660A379CFE1595" + "59E357581825391175FC195D28BAC0CF03A7841A383B95C262B98378287" + "4CCE6FE333", + 16, + ), + x_qA=int( + "0562E68B9AF7CBFD5565C6B16883B777FF11C199161ECC427A39D17EC" + "2166499389571D6A994977C56AD8252658BA8A1B72AE42F4FB7532151" + "AFC3EF0971CCDA", + 16, + ), + y_qA=int( + "A7CA2D8191E21776A89860AFBC1F582FAA308D551C1DC6133AF9F9C3C" + "AD59998D70079548140B90B1F311AFB378AA81F51B275B2BE6B7DEE97" + "8EFC7343EA642E", + 16, + ), + dB=int( + "0AF4E7F6D52EDD52907BB8DBAB3992A0BB696EC10DF11892FF205B66D38" + "1ECE72314E6A6EA079CEA06961DBA5AE6422EF2E9EE803A1F236FB96A17" + "99B86E5C8B", + 16, + ), + x_qB=int( + "5A7954E32663DFF11AE24712D87419F26B708AC2B92877D6BFEE2BFC4" + "3714D89BBDB6D24D807BBD3AEB7F0C325F862E8BADE4F74636B97EAAC" + "E739E11720D323", + 16, + ), + y_qB=int( + "96D14621A9283A1BED84DE8DD64836B2C0758B11441179DC0C54C0D49" + "A47C03807D171DD544B72CAAEF7B7CE01C7753E2CAD1A861ECA55A719" + "54EE1BA35E04BE", + 16, + ), + x_Z=int( + "1EE8321A4BBF93B9CF8921AB209850EC9B7066D1984EF08C2BB7232362" + "08AC8F1A483E79461A00E0D5F6921CE9D360502F85C812BEDEE23AC5B2" + "10E5811B191E", + 16, + ), + y_Z=int( + "2632095B7B936174B41FD2FAF369B1D18DCADEED7E410A7E251F083109" + "7C50D02CFED02607B6A2D5ADB4C0006008562208631875B58B54ECDA5A" + "4F9FE9EAABA6", + 16, + ), + ) + + +class RFC7027(ECDH): + # https://tools.ietf.org/html/rfc7027#appendix-A + + def test_brainpoolP256r1(self): + self._do( + curve=curve_brainpoolp256r1, + generator=BRAINPOOLP256r1.generator, + dA=int( + "81DB1EE100150FF2EA338D708271BE38300CB54241D79950F77B0630398" + "04F1D", + 16, + ), + x_qA=int( + "44106E913F92BC02A1705D9953A8414DB95E1AAA49E81D9E85F929A8E" + "3100BE5", + 16, + ), + y_qA=int( + "8AB4846F11CACCB73CE49CBDD120F5A900A69FD32C272223F789EF10E" + "B089BDC", + 16, + ), + dB=int( + "55E40BC41E37E3E2AD25C3C6654511FFA8474A91A0032087593852D3E7D" + "76BD3", + 16, + ), + x_qB=int( + "8D2D688C6CF93E1160AD04CC4429117DC2C41825E1E9FCA0ADDD34E6F" + "1B39F7B", + 16, + ), + y_qB=int( + "990C57520812BE512641E47034832106BC7D3E8DD0E4C7F1136D70065" + "47CEC6A", + 16, + ), + x_Z=int( + "89AFC39D41D3B327814B80940B042590F96556EC91E6AE7939BCE31F3A" + "18BF2B", + 16, + ), + y_Z=int( + "49C27868F4ECA2179BFD7D59B1E3BF34C1DBDE61AE12931648F43E5963" + "2504DE", + 16, + ), + ) + + @pytest.mark.slow + def test_brainpoolP384r1(self): + self._do( + curve=curve_brainpoolp384r1, + generator=BRAINPOOLP384r1.generator, + dA=int( + "1E20F5E048A5886F1F157C74E91BDE2B98C8B52D58E5003D57053FC4B0B" + "D65D6F15EB5D1EE1610DF870795143627D042", + 16, + ), + x_qA=int( + "68B665DD91C195800650CDD363C625F4E742E8134667B767B1B476793" + "588F885AB698C852D4A6E77A252D6380FCAF068", + 16, + ), + y_qA=int( + "55BC91A39C9EC01DEE36017B7D673A931236D2F1F5C83942D049E3FA2" + "0607493E0D038FF2FD30C2AB67D15C85F7FAA59", + 16, + ), + dB=int( + "032640BC6003C59260F7250C3DB58CE647F98E1260ACCE4ACDA3DD869F7" + "4E01F8BA5E0324309DB6A9831497ABAC96670", + 16, + ), + x_qB=int( + "4D44326F269A597A5B58BBA565DA5556ED7FD9A8A9EB76C25F46DB69D" + "19DC8CE6AD18E404B15738B2086DF37E71D1EB4", + 16, + ), + y_qB=int( + "62D692136DE56CBE93BF5FA3188EF58BC8A3A0EC6C1E151A21038A42E" + "9185329B5B275903D192F8D4E1F32FE9CC78C48", + 16, + ), + x_Z=int( + "0BD9D3A7EA0B3D519D09D8E48D0785FB744A6B355E6304BC51C229FBBC" + "E239BBADF6403715C35D4FB2A5444F575D4F42", + 16, + ), + y_Z=int( + "0DF213417EBE4D8E40A5F76F66C56470C489A3478D146DECF6DF0D94BA" + "E9E598157290F8756066975F1DB34B2324B7BD", + 16, + ), + ) + + @pytest.mark.slow + def test_brainpoolP512r1(self): + self._do( + curve=curve_brainpoolp512r1, + generator=BRAINPOOLP512r1.generator, + dA=int( + "16302FF0DBBB5A8D733DAB7141C1B45ACBC8715939677F6A56850A38BD8" + "7BD59B09E80279609FF333EB9D4C061231FB26F92EEB04982A5F1D1764C" + "AD57665422", + 16, + ), + x_qA=int( + "0A420517E406AAC0ACDCE90FCD71487718D3B953EFD7FBEC5F7F27E28" + "C6149999397E91E029E06457DB2D3E640668B392C2A7E737A7F0BF044" + "36D11640FD09FD", + 16, + ), + y_qA=int( + "72E6882E8DB28AAD36237CD25D580DB23783961C8DC52DFA2EC138AD4" + "72A0FCEF3887CF62B623B2A87DE5C588301EA3E5FC269B373B60724F5" + "E82A6AD147FDE7", + 16, + ), + dB=int( + "230E18E1BCC88A362FA54E4EA3902009292F7F8033624FD471B5D8ACE49" + "D12CFABBC19963DAB8E2F1EBA00BFFB29E4D72D13F2224562F405CB8050" + "3666B25429", + 16, + ), + x_qB=int( + "9D45F66DE5D67E2E6DB6E93A59CE0BB48106097FF78A081DE781CDB31" + "FCE8CCBAAEA8DD4320C4119F1E9CD437A2EAB3731FA9668AB268D871D" + "EDA55A5473199F", + 16, + ), + y_qB=int( + "2FDC313095BCDD5FB3A91636F07A959C8E86B5636A1E930E8396049CB" + "481961D365CC11453A06C719835475B12CB52FC3C383BCE35E27EF194" + "512B71876285FA", + 16, + ), + x_Z=int( + "A7927098655F1F9976FA50A9D566865DC530331846381C87256BAF3226" + "244B76D36403C024D7BBF0AA0803EAFF405D3D24F11A9B5C0BEF679FE1" + "454B21C4CD1F", + 16, + ), + y_Z=int( + "7DB71C3DEF63212841C463E881BDCF055523BD368240E6C3143BD8DEF8" + "B3B3223B95E0F53082FF5E412F4222537A43DF1C6D25729DDB51620A83" + "2BE6A26680A2", + 16, + ), + ) + + +# https://tools.ietf.org/html/rfc4754#page-5 +@pytest.mark.parametrize( + "w, gwx, gwy, k, msg, md, r, s, curve", + [ + pytest.param( + "DC51D3866A15BACDE33D96F992FCA99DA7E6EF0934E7097559C27F1614C88A7F", + "2442A5CC0ECD015FA3CA31DC8E2BBC70BF42D60CBCA20085E0822CB04235E970", + "6FC98BD7E50211A4A27102FA3549DF79EBCB4BF246B80945CDDFE7D509BBFD7D", + "9E56F509196784D963D1C0A401510EE7ADA3DCC5DEE04B154BF61AF1D5A6DECE", + b"abc", + hashlib.sha256, + "CB28E0999B9C7715FD0A80D8E47A77079716CBBF917DD72E97566EA1C066957C", + "86FA3BB4E26CAD5BF90B7F81899256CE7594BB1EA0C89212748BFF3B3D5B0315", + NIST256p, + id="ECDSA-256", + ), + pytest.param( + "0BEB646634BA87735D77AE4809A0EBEA865535DE4C1E1DCB692E84708E81A5AF" + "62E528C38B2A81B35309668D73524D9F", + "96281BF8DD5E0525CA049C048D345D3082968D10FEDF5C5ACA0C64E6465A97EA" + "5CE10C9DFEC21797415710721F437922", + "447688BA94708EB6E2E4D59F6AB6D7EDFF9301D249FE49C33096655F5D502FAD" + "3D383B91C5E7EDAA2B714CC99D5743CA", + "B4B74E44D71A13D568003D7489908D564C7761E229C58CBFA18950096EB7463B" + "854D7FA992F934D927376285E63414FA", + b"abc", + hashlib.sha384, + "FB017B914E29149432D8BAC29A514640B46F53DDAB2C69948084E2930F1C8F7E" + "08E07C9C63F2D21A07DCB56A6AF56EB3", + "B263A1305E057F984D38726A1B46874109F417BCA112674C528262A40A629AF1" + "CBB9F516CE0FA7D2FF630863A00E8B9F", + NIST384p, + id="ECDSA-384", + ), + pytest.param( + "0065FDA3409451DCAB0A0EAD45495112A3D813C17BFD34BDF8C1209D7DF58491" + "20597779060A7FF9D704ADF78B570FFAD6F062E95C7E0C5D5481C5B153B48B37" + "5FA1", + "0151518F1AF0F563517EDD5485190DF95A4BF57B5CBA4CF2A9A3F6474725A35F" + "7AFE0A6DDEB8BEDBCD6A197E592D40188901CECD650699C9B5E456AEA5ADD190" + "52A8", + "006F3B142EA1BFFF7E2837AD44C9E4FF6D2D34C73184BBAD90026DD5E6E85317" + "D9DF45CAD7803C6C20035B2F3FF63AFF4E1BA64D1C077577DA3F4286C58F0AEA" + "E643", + "00C1C2B305419F5A41344D7E4359933D734096F556197A9B244342B8B62F46F9" + "373778F9DE6B6497B1EF825FF24F42F9B4A4BD7382CFC3378A540B1B7F0C1B95" + "6C2F", + b"abc", + hashlib.sha512, + "0154FD3836AF92D0DCA57DD5341D3053988534FDE8318FC6AAAAB68E2E6F4339" + "B19F2F281A7E0B22C269D93CF8794A9278880ED7DBB8D9362CAEACEE54432055" + "2251", + "017705A7030290D1CEB605A9A1BB03FF9CDD521E87A696EC926C8C10C8362DF4" + "975367101F67D1CF9BCCBF2F3D239534FA509E70AAC851AE01AAC68D62F86647" + "2660", + NIST521p, + id="ECDSA-521", + ), + ], +) +def test_RFC4754_vectors(w, gwx, gwy, k, msg, md, r, s, curve): + sk = SigningKey.from_string(unhexlify(w), curve) + vk = VerifyingKey.from_string(unhexlify(gwx + gwy), curve) + assert sk.verifying_key == vk + sig = sk.sign(msg, hashfunc=md, sigencode=sigencode_strings, k=int(k, 16)) + + assert sig == (unhexlify(r), unhexlify(s)) + + assert vk.verify(sig, msg, md, sigdecode_strings) diff --git a/code/.venv/lib/python3.12/site-packages/ecdsa/test_rw_lock.py b/code/.venv/lib/python3.12/site-packages/ecdsa/test_rw_lock.py new file mode 100644 index 0000000..0a84b9c --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/ecdsa/test_rw_lock.py @@ -0,0 +1,180 @@ +# Copyright Mateusz Kobos, (c) 2011 +# https://code.activestate.com/recipes/577803-reader-writer-lock-with-priority-for-writers/ +# released under the MIT licence + +try: + import unittest2 as unittest +except ImportError: + import unittest +import threading +import time +import copy +from ._rwlock import RWLock + + +class Writer(threading.Thread): + def __init__( + self, buffer_, rw_lock, init_sleep_time, sleep_time, to_write + ): + """ + @param buffer_: common buffer_ shared by the readers and writers + @type buffer_: list + @type rw_lock: L{RWLock} + @param init_sleep_time: sleep time before doing any action + @type init_sleep_time: C{float} + @param sleep_time: sleep time while in critical section + @type sleep_time: C{float} + @param to_write: data that will be appended to the buffer + """ + threading.Thread.__init__(self) + self.__buffer = buffer_ + self.__rw_lock = rw_lock + self.__init_sleep_time = init_sleep_time + self.__sleep_time = sleep_time + self.__to_write = to_write + self.entry_time = None + """Time of entry to the critical section""" + self.exit_time = None + """Time of exit from the critical section""" + + def run(self): + time.sleep(self.__init_sleep_time) + self.__rw_lock.writer_acquire() + self.entry_time = time.time() + time.sleep(self.__sleep_time) + self.__buffer.append(self.__to_write) + self.exit_time = time.time() + self.__rw_lock.writer_release() + + +class Reader(threading.Thread): + def __init__(self, buffer_, rw_lock, init_sleep_time, sleep_time): + """ + @param buffer_: common buffer shared by the readers and writers + @type buffer_: list + @type rw_lock: L{RWLock} + @param init_sleep_time: sleep time before doing any action + @type init_sleep_time: C{float} + @param sleep_time: sleep time while in critical section + @type sleep_time: C{float} + """ + threading.Thread.__init__(self) + self.__buffer = buffer_ + self.__rw_lock = rw_lock + self.__init_sleep_time = init_sleep_time + self.__sleep_time = sleep_time + self.buffer_read = None + """a copy of a the buffer read while in critical section""" + self.entry_time = None + """Time of entry to the critical section""" + self.exit_time = None + """Time of exit from the critical section""" + + def run(self): + time.sleep(self.__init_sleep_time) + self.__rw_lock.reader_acquire() + self.entry_time = time.time() + time.sleep(self.__sleep_time) + self.buffer_read = copy.deepcopy(self.__buffer) + self.exit_time = time.time() + self.__rw_lock.reader_release() + + +class RWLockTestCase(unittest.TestCase): + def test_readers_nonexclusive_access(self): + (buffer_, rw_lock, threads) = self.__init_variables() + + threads.append(Reader(buffer_, rw_lock, 0, 0)) + threads.append(Writer(buffer_, rw_lock, 0.2, 0.4, 1)) + threads.append(Reader(buffer_, rw_lock, 0.3, 0.3)) + threads.append(Reader(buffer_, rw_lock, 0.5, 0)) + + self.__start_and_join_threads(threads) + + ## The third reader should enter after the second one but it should + ## exit before the second one exits + ## (i.e. the readers should be in the critical section + ## at the same time) + + self.assertEqual([], threads[0].buffer_read) + self.assertEqual([1], threads[2].buffer_read) + self.assertEqual([1], threads[3].buffer_read) + self.assertTrue(threads[1].exit_time <= threads[2].entry_time) + self.assertTrue(threads[2].entry_time <= threads[3].entry_time) + self.assertTrue(threads[3].exit_time < threads[2].exit_time) + + def test_writers_exclusive_access(self): + (buffer_, rw_lock, threads) = self.__init_variables() + + threads.append(Writer(buffer_, rw_lock, 0, 0.4, 1)) + threads.append(Writer(buffer_, rw_lock, 0.1, 0, 2)) + threads.append(Reader(buffer_, rw_lock, 0.2, 0)) + + self.__start_and_join_threads(threads) + + ## The second writer should wait for the first one to exit + + self.assertEqual([1, 2], threads[2].buffer_read) + self.assertTrue(threads[0].exit_time <= threads[1].entry_time) + self.assertTrue(threads[1].exit_time <= threads[2].exit_time) + + def test_writer_priority(self): + (buffer_, rw_lock, threads) = self.__init_variables() + + threads.append(Writer(buffer_, rw_lock, 0, 0, 1)) + threads.append(Reader(buffer_, rw_lock, 0.1, 0.4)) + threads.append(Writer(buffer_, rw_lock, 0.2, 0, 2)) + threads.append(Reader(buffer_, rw_lock, 0.3, 0)) + threads.append(Reader(buffer_, rw_lock, 0.3, 0)) + + self.__start_and_join_threads(threads) + + ## The second writer should go before the second and the third reader + + self.assertEqual([1], threads[1].buffer_read) + self.assertEqual([1, 2], threads[3].buffer_read) + self.assertEqual([1, 2], threads[4].buffer_read) + self.assertTrue(threads[0].exit_time < threads[1].entry_time) + self.assertTrue(threads[1].exit_time <= threads[2].entry_time) + self.assertTrue(threads[2].exit_time <= threads[3].entry_time) + self.assertTrue(threads[2].exit_time <= threads[4].entry_time) + + def test_many_writers_priority(self): + (buffer_, rw_lock, threads) = self.__init_variables() + + threads.append(Writer(buffer_, rw_lock, 0, 0, 1)) + threads.append(Reader(buffer_, rw_lock, 0.1, 0.6)) + threads.append(Writer(buffer_, rw_lock, 0.2, 0.1, 2)) + threads.append(Reader(buffer_, rw_lock, 0.3, 0)) + threads.append(Reader(buffer_, rw_lock, 0.4, 0)) + threads.append(Writer(buffer_, rw_lock, 0.5, 0.1, 3)) + + self.__start_and_join_threads(threads) + + ## The two last writers should go first -- after the first reader and + ## before the second and the third reader + + self.assertEqual([1], threads[1].buffer_read) + self.assertEqual([1, 2, 3], threads[3].buffer_read) + self.assertEqual([1, 2, 3], threads[4].buffer_read) + self.assertTrue(threads[0].exit_time < threads[1].entry_time) + self.assertTrue(threads[1].exit_time <= threads[2].entry_time) + self.assertTrue(threads[1].exit_time <= threads[5].entry_time) + self.assertTrue(threads[2].exit_time <= threads[3].entry_time) + self.assertTrue(threads[2].exit_time <= threads[4].entry_time) + self.assertTrue(threads[5].exit_time <= threads[3].entry_time) + self.assertTrue(threads[5].exit_time <= threads[4].entry_time) + + @staticmethod + def __init_variables(): + buffer_ = [] + rw_lock = RWLock() + threads = [] + return (buffer_, rw_lock, threads) + + @staticmethod + def __start_and_join_threads(threads): + for t in threads: + t.start() + for t in threads: + t.join() diff --git a/code/.venv/lib/python3.12/site-packages/ecdsa/test_sha3.py b/code/.venv/lib/python3.12/site-packages/ecdsa/test_sha3.py new file mode 100644 index 0000000..d30381d --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/ecdsa/test_sha3.py @@ -0,0 +1,111 @@ +try: + import unittest2 as unittest +except ImportError: + import unittest +import pytest + +try: + from gmpy2 import mpz + + GMPY = True +except ImportError: # pragma: no cover + try: + from gmpy import mpz + + GMPY = True + except ImportError: + GMPY = False + +from ._sha3 import shake_256 +from ._compat import bytes_to_int, int_to_bytes + +B2I_VECTORS = [ + (b"\x00\x01", "big", 1), + (b"\x00\x01", "little", 0x0100), + (b"", "big", 0), + (b"\x00", "little", 0), +] + + +@pytest.mark.parametrize("bytes_in,endian,int_out", B2I_VECTORS) +def test_bytes_to_int(bytes_in, endian, int_out): + out = bytes_to_int(bytes_in, endian) + assert out == int_out + + +class TestBytesToInt(unittest.TestCase): + def test_bytes_to_int_wrong_endian(self): + with self.assertRaises(ValueError): + bytes_to_int(b"\x00", "middle") + + def test_int_to_bytes_wrong_endian(self): + with self.assertRaises(ValueError): + int_to_bytes(0, byteorder="middle") + + +@pytest.mark.skipif(GMPY == False, reason="requires gmpy or gmpy2") +def test_int_to_bytes_with_gmpy(): + assert int_to_bytes(mpz(1)) == b"\x01" + + +I2B_VECTORS = [ + (0, None, "big", b""), + (0, 1, "big", b"\x00"), + (1, None, "big", b"\x01"), + (0x0100, None, "little", b"\x00\x01"), + (0x0100, 4, "little", b"\x00\x01\x00\x00"), + (1, 4, "big", b"\x00\x00\x00\x01"), +] + + +@pytest.mark.parametrize("int_in,length,endian,bytes_out", I2B_VECTORS) +def test_int_to_bytes(int_in, length, endian, bytes_out): + out = int_to_bytes(int_in, length, endian) + assert out == bytes_out + + +SHAKE_256_VECTORS = [ + ( + b"Message.", + 32, + b"\x78\xa1\x37\xbb\x33\xae\xe2\x72\xb1\x02\x4f\x39\x43\xe5\xcf\x0c" + b"\x4e\x9c\x72\x76\x2e\x34\x4c\xf8\xf9\xc3\x25\x9d\x4f\x91\x2c\x3a", + ), + ( + b"", + 32, + b"\x46\xb9\xdd\x2b\x0b\xa8\x8d\x13\x23\x3b\x3f\xeb\x74\x3e\xeb\x24" + b"\x3f\xcd\x52\xea\x62\xb8\x1b\x82\xb5\x0c\x27\x64\x6e\xd5\x76\x2f", + ), + ( + b"message", + 32, + b"\x86\x16\xe1\xe4\xcf\xd8\xb5\xf7\xd9\x2d\x43\xd8\x6e\x1b\x14\x51" + b"\xa2\xa6\x5a\xf8\x64\xfc\xb1\x26\xc2\x66\x0a\xb3\x46\x51\xb1\x75", + ), + ( + b"message", + 16, + b"\x86\x16\xe1\xe4\xcf\xd8\xb5\xf7\xd9\x2d\x43\xd8\x6e\x1b\x14\x51", + ), + ( + b"message", + 64, + b"\x86\x16\xe1\xe4\xcf\xd8\xb5\xf7\xd9\x2d\x43\xd8\x6e\x1b\x14\x51" + b"\xa2\xa6\x5a\xf8\x64\xfc\xb1\x26\xc2\x66\x0a\xb3\x46\x51\xb1\x75" + b"\x30\xd6\xba\x2a\x46\x65\xf1\x9d\xf0\x62\x25\xb1\x26\xd1\x3e\xed" + b"\x91\xd5\x0d\xe7\xb9\xcb\x65\xf3\x3a\x46\xae\xd3\x6c\x7d\xc5\xe8", + ), + ( + b"A" * 1024, + 32, + b"\xa5\xef\x7e\x30\x8b\xe8\x33\x64\xe5\x9c\xf3\xb5\xf3\xba\x20\xa3" + b"\x5a\xe7\x30\xfd\xbc\x33\x11\xbf\x83\x89\x50\x82\xb4\x41\xe9\xb3", + ), +] + + +@pytest.mark.parametrize("msg,olen,ohash", SHAKE_256_VECTORS) +def test_shake_256(msg, olen, ohash): + out = shake_256(msg, olen) + assert out == bytearray(ohash) diff --git a/code/.venv/lib/python3.12/site-packages/ecdsa/util.py b/code/.venv/lib/python3.12/site-packages/ecdsa/util.py new file mode 100644 index 0000000..639bc0c --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/ecdsa/util.py @@ -0,0 +1,519 @@ +""" +This module includes some utility functions. + +The methods most typically used are the sigencode and sigdecode functions +to be used with :func:`~ecdsa.keys.SigningKey.sign` and +:func:`~ecdsa.keys.VerifyingKey.verify` +respectively. See the :func:`sigencode_strings`, :func:`sigdecode_string`, +:func:`sigencode_der`, :func:`sigencode_strings_canonize`, +:func:`sigencode_string_canonize`, :func:`sigencode_der_canonize`, +:func:`sigdecode_strings`, :func:`sigdecode_string`, and +:func:`sigdecode_der` functions. +""" + +from __future__ import division + +import os +import math +import binascii +import sys +from hashlib import sha256 +from six import PY2, int2byte, next +from . import der +from ._compat import normalise_bytes + + +# RFC5480: +# The "unrestricted" algorithm identifier is: +# id-ecPublicKey OBJECT IDENTIFIER ::= { +# iso(1) member-body(2) us(840) ansi-X9-62(10045) keyType(2) 1 } + +oid_ecPublicKey = (1, 2, 840, 10045, 2, 1) +encoded_oid_ecPublicKey = der.encode_oid(*oid_ecPublicKey) + +# RFC5480: +# The ECDH algorithm uses the following object identifier: +# id-ecDH OBJECT IDENTIFIER ::= { +# iso(1) identified-organization(3) certicom(132) schemes(1) +# ecdh(12) } + +oid_ecDH = (1, 3, 132, 1, 12) + +# RFC5480: +# The ECMQV algorithm uses the following object identifier: +# id-ecMQV OBJECT IDENTIFIER ::= { +# iso(1) identified-organization(3) certicom(132) schemes(1) +# ecmqv(13) } + +oid_ecMQV = (1, 3, 132, 1, 13) + +if sys.version_info >= (3,): # pragma: no branch + + def entropy_to_bits(ent_256): + """Convert a bytestring to string of 0's and 1's""" + return bin(int.from_bytes(ent_256, "big"))[2:].zfill(len(ent_256) * 8) + +else: + + def entropy_to_bits(ent_256): + """Convert a bytestring to string of 0's and 1's""" + return "".join(bin(ord(x))[2:].zfill(8) for x in ent_256) + + +if sys.version_info < (2, 7): # pragma: no branch + # Can't add a method to a built-in type so we are stuck with this + def bit_length(x): + return len(bin(x)) - 2 + +else: + + def bit_length(x): + return x.bit_length() or 1 + + +def orderlen(order): + return (1 + len("%x" % order)) // 2 # bytes + + +def randrange(order, entropy=None): + """Return a random integer k such that 1 <= k < order, uniformly + distributed across that range. Worst case should be a mean of 2 loops at + (2**k)+2. + + Note that this function is not declared to be forwards-compatible: we may + change the behavior in future releases. The entropy= argument (which + should get a callable that behaves like os.urandom) can be used to + achieve stability within a given release (for repeatable unit tests), but + should not be used as a long-term-compatible key generation algorithm. + """ + assert order > 1 + if entropy is None: + entropy = os.urandom + upper_2 = bit_length(order - 2) + upper_256 = upper_2 // 8 + 1 + while True: # I don't think this needs a counter with bit-wise randrange + ent_256 = entropy(upper_256) + ent_2 = entropy_to_bits(ent_256) + rand_num = int(ent_2[:upper_2], base=2) + 1 + if 0 < rand_num < order: + return rand_num + + +class PRNG: + # this returns a callable which, when invoked with an integer N, will + # return N pseudorandom bytes. Note: this is a short-term PRNG, meant + # primarily for the needs of randrange_from_seed__trytryagain(), which + # only needs to run it a few times per seed. It does not provide + # protection against state compromise (forward security). + def __init__(self, seed): + self.generator = self.block_generator(seed) + + def __call__(self, numbytes): + a = [next(self.generator) for i in range(numbytes)] + + if PY2: # pragma: no branch + return "".join(a) + else: + return bytes(a) + + def block_generator(self, seed): + counter = 0 + while True: + for byte in sha256( + ("prng-%d-%s" % (counter, seed)).encode() + ).digest(): + yield byte + counter += 1 + + +def randrange_from_seed__overshoot_modulo(seed, order): + # hash the data, then turn the digest into a number in [1,order). + # + # We use David-Sarah Hopwood's suggestion: turn it into a number that's + # sufficiently larger than the group order, then modulo it down to fit. + # This should give adequate (but not perfect) uniformity, and simple + # code. There are other choices: try-try-again is the main one. + base = PRNG(seed)(2 * orderlen(order)) + number = (int(binascii.hexlify(base), 16) % (order - 1)) + 1 + assert 1 <= number < order, (1, number, order) + return number + + +def lsb_of_ones(numbits): + return (1 << numbits) - 1 + + +def bits_and_bytes(order): + bits = int(math.log(order - 1, 2) + 1) + bytes = bits // 8 + extrabits = bits % 8 + return bits, bytes, extrabits + + +# the following randrange_from_seed__METHOD() functions take an +# arbitrarily-sized secret seed and turn it into a number that obeys the same +# range limits as randrange() above. They are meant for deriving consistent +# signing keys from a secret rather than generating them randomly, for +# example a protocol in which three signing keys are derived from a master +# secret. You should use a uniformly-distributed unguessable seed with about +# curve.baselen bytes of entropy. To use one, do this: +# seed = os.urandom(curve.baselen) # or other starting point +# secexp = ecdsa.util.randrange_from_seed__trytryagain(sed, curve.order) +# sk = SigningKey.from_secret_exponent(secexp, curve) + + +def randrange_from_seed__truncate_bytes(seed, order, hashmod=sha256): + # hash the seed, then turn the digest into a number in [1,order), but + # don't worry about trying to uniformly fill the range. This will lose, + # on average, four bits of entropy. + bits, _bytes, extrabits = bits_and_bytes(order) + if extrabits: + _bytes += 1 + base = hashmod(seed).digest()[:_bytes] + base = "\x00" * (_bytes - len(base)) + base + number = 1 + int(binascii.hexlify(base), 16) + assert 1 <= number < order + return number + + +def randrange_from_seed__truncate_bits(seed, order, hashmod=sha256): + # like string_to_randrange_truncate_bytes, but only lose an average of + # half a bit + bits = int(math.log(order - 1, 2) + 1) + maxbytes = (bits + 7) // 8 + base = hashmod(seed).digest()[:maxbytes] + base = "\x00" * (maxbytes - len(base)) + base + topbits = 8 * maxbytes - bits + if topbits: + base = int2byte(ord(base[0]) & lsb_of_ones(topbits)) + base[1:] + number = 1 + int(binascii.hexlify(base), 16) + assert 1 <= number < order + return number + + +def randrange_from_seed__trytryagain(seed, order): + # figure out exactly how many bits we need (rounded up to the nearest + # bit), so we can reduce the chance of looping to less than 0.5 . This is + # specified to feed from a byte-oriented PRNG, and discards the + # high-order bits of the first byte as necessary to get the right number + # of bits. The average number of loops will range from 1.0 (when + # order=2**k-1) to 2.0 (when order=2**k+1). + assert order > 1 + bits, bytes, extrabits = bits_and_bytes(order) + generate = PRNG(seed) + while True: + extrabyte = b"" + if extrabits: + extrabyte = int2byte(ord(generate(1)) & lsb_of_ones(extrabits)) + guess = string_to_number(extrabyte + generate(bytes)) + 1 + if 1 <= guess < order: + return guess + + +def number_to_string(num, order): + l = orderlen(order) + fmt_str = "%0" + str(2 * l) + "x" + string = binascii.unhexlify((fmt_str % num).encode()) + assert len(string) == l, (len(string), l) + return string + + +def number_to_string_crop(num, order): + l = orderlen(order) + fmt_str = "%0" + str(2 * l) + "x" + string = binascii.unhexlify((fmt_str % num).encode()) + return string[:l] + + +def string_to_number(string): + return int(binascii.hexlify(string), 16) + + +def string_to_number_fixedlen(string, order): + l = orderlen(order) + assert len(string) == l, (len(string), l) + return int(binascii.hexlify(string), 16) + + +def sigencode_strings(r, s, order): + """ + Encode the signature to a pair of strings in a tuple + + Encodes signature into raw encoding (:term:`raw encoding`) with the + ``r`` and ``s`` parts of the signature encoded separately. + + It's expected that this function will be used as a ``sigencode=`` parameter + in :func:`ecdsa.keys.SigningKey.sign` method. + + :param int r: first parameter of the signature + :param int s: second parameter of the signature + :param int order: the order of the curve over which the signature was + computed + + :return: raw encoding of ECDSA signature + :rtype: tuple(bytes, bytes) + """ + r_str = number_to_string(r, order) + s_str = number_to_string(s, order) + return (r_str, s_str) + + +def sigencode_string(r, s, order): + """ + Encode the signature to raw format (:term:`raw encoding`) + + It's expected that this function will be used as a ``sigencode=`` parameter + in :func:`ecdsa.keys.SigningKey.sign` method. + + :param int r: first parameter of the signature + :param int s: second parameter of the signature + :param int order: the order of the curve over which the signature was + computed + + :return: raw encoding of ECDSA signature + :rtype: bytes + """ + # for any given curve, the size of the signature numbers is + # fixed, so just use simple concatenation + r_str, s_str = sigencode_strings(r, s, order) + return r_str + s_str + + +def sigencode_der(r, s, order): + """ + Encode the signature into the ECDSA-Sig-Value structure using :term:`DER`. + + Encodes the signature to the following :term:`ASN.1` structure:: + + Ecdsa-Sig-Value ::= SEQUENCE { + r INTEGER, + s INTEGER + } + + It's expected that this function will be used as a ``sigencode=`` parameter + in :func:`ecdsa.keys.SigningKey.sign` method. + + :param int r: first parameter of the signature + :param int s: second parameter of the signature + :param int order: the order of the curve over which the signature was + computed + + :return: DER encoding of ECDSA signature + :rtype: bytes + """ + return der.encode_sequence(der.encode_integer(r), der.encode_integer(s)) + + +def sigencode_strings_canonize(r, s, order): + """ + Encode the signature to a pair of strings in a tuple + + Encodes signature into raw encoding (:term:`raw encoding`) with the + ``r`` and ``s`` parts of the signature encoded separately. + + Makes sure that the signature is encoded in the canonical format, where + the ``s`` parameter is always smaller than ``order / 2``. + Most commonly used in bitcoin. + + It's expected that this function will be used as a ``sigencode=`` parameter + in :func:`ecdsa.keys.SigningKey.sign` method. + + :param int r: first parameter of the signature + :param int s: second parameter of the signature + :param int order: the order of the curve over which the signature was + computed + + :return: raw encoding of ECDSA signature + :rtype: tuple(bytes, bytes) + """ + if s > order / 2: + s = order - s + return sigencode_strings(r, s, order) + + +def sigencode_string_canonize(r, s, order): + """ + Encode the signature to raw format (:term:`raw encoding`) + + Makes sure that the signature is encoded in the canonical format, where + the ``s`` parameter is always smaller than ``order / 2``. + Most commonly used in bitcoin. + + It's expected that this function will be used as a ``sigencode=`` parameter + in :func:`ecdsa.keys.SigningKey.sign` method. + + :param int r: first parameter of the signature + :param int s: second parameter of the signature + :param int order: the order of the curve over which the signature was + computed + + :return: raw encoding of ECDSA signature + :rtype: bytes + """ + if s > order / 2: + s = order - s + return sigencode_string(r, s, order) + + +def sigencode_der_canonize(r, s, order): + """ + Encode the signature into the ECDSA-Sig-Value structure using :term:`DER`. + + Makes sure that the signature is encoded in the canonical format, where + the ``s`` parameter is always smaller than ``order / 2``. + Most commonly used in bitcoin. + + Encodes the signature to the following :term:`ASN.1` structure:: + + Ecdsa-Sig-Value ::= SEQUENCE { + r INTEGER, + s INTEGER + } + + It's expected that this function will be used as a ``sigencode=`` parameter + in :func:`ecdsa.keys.SigningKey.sign` method. + + :param int r: first parameter of the signature + :param int s: second parameter of the signature + :param int order: the order of the curve over which the signature was + computed + + :return: DER encoding of ECDSA signature + :rtype: bytes + """ + if s > order / 2: + s = order - s + return sigencode_der(r, s, order) + + +class MalformedSignature(Exception): + """ + Raised by decoding functions when the signature is malformed. + + Malformed in this context means that the relevant strings or integers + do not match what a signature over provided curve would create. Either + because the byte strings have incorrect lengths or because the encoded + values are too large. + """ + + pass + + +def sigdecode_string(signature, order): + """ + Decoder for :term:`raw encoding` of ECDSA signatures. + + raw encoding is a simple concatenation of the two integers that comprise + the signature, with each encoded using the same amount of bytes depending + on curve size/order. + + It's expected that this function will be used as the ``sigdecode=`` + parameter to the :func:`ecdsa.keys.VerifyingKey.verify` method. + + :param signature: encoded signature + :type signature: bytes like object + :param order: order of the curve over which the signature was computed + :type order: int + + :raises MalformedSignature: when the encoding of the signature is invalid + + :return: tuple with decoded ``r`` and ``s`` values of signature + :rtype: tuple of ints + """ + signature = normalise_bytes(signature) + l = orderlen(order) + if not len(signature) == 2 * l: + raise MalformedSignature( + "Invalid length of signature, expected {0} bytes long, " + "provided string is {1} bytes long".format(2 * l, len(signature)) + ) + r = string_to_number_fixedlen(signature[:l], order) + s = string_to_number_fixedlen(signature[l:], order) + return r, s + + +def sigdecode_strings(rs_strings, order): + """ + Decode the signature from two strings. + + First string needs to be a big endian encoding of ``r``, second needs to + be a big endian encoding of the ``s`` parameter of an ECDSA signature. + + It's expected that this function will be used as the ``sigdecode=`` + parameter to the :func:`ecdsa.keys.VerifyingKey.verify` method. + + :param list rs_strings: list of two bytes-like objects, each encoding one + parameter of signature + :param int order: order of the curve over which the signature was computed + + :raises MalformedSignature: when the encoding of the signature is invalid + + :return: tuple with decoded ``r`` and ``s`` values of signature + :rtype: tuple of ints + """ + if not len(rs_strings) == 2: + raise MalformedSignature( + "Invalid number of strings provided: {0}, expected 2".format( + len(rs_strings) + ) + ) + (r_str, s_str) = rs_strings + r_str = normalise_bytes(r_str) + s_str = normalise_bytes(s_str) + l = orderlen(order) + if not len(r_str) == l: + raise MalformedSignature( + "Invalid length of first string ('r' parameter), " + "expected {0} bytes long, provided string is {1} " + "bytes long".format(l, len(r_str)) + ) + if not len(s_str) == l: + raise MalformedSignature( + "Invalid length of second string ('s' parameter), " + "expected {0} bytes long, provided string is {1} " + "bytes long".format(l, len(s_str)) + ) + r = string_to_number_fixedlen(r_str, order) + s = string_to_number_fixedlen(s_str, order) + return r, s + + +def sigdecode_der(sig_der, order): + """ + Decoder for DER format of ECDSA signatures. + + DER format of signature is one that uses the :term:`ASN.1` :term:`DER` + rules to encode it as a sequence of two integers:: + + Ecdsa-Sig-Value ::= SEQUENCE { + r INTEGER, + s INTEGER + } + + It's expected that this function will be used as as the ``sigdecode=`` + parameter to the :func:`ecdsa.keys.VerifyingKey.verify` method. + + :param sig_der: encoded signature + :type sig_der: bytes like object + :param order: order of the curve over which the signature was computed + :type order: int + + :raises UnexpectedDER: when the encoding of signature is invalid + + :return: tuple with decoded ``r`` and ``s`` values of signature + :rtype: tuple of ints + """ + sig_der = normalise_bytes(sig_der) + # return der.encode_sequence(der.encode_integer(r), der.encode_integer(s)) + rs_strings, empty = der.remove_sequence(sig_der) + if empty != b"": + raise der.UnexpectedDER( + "trailing junk after DER sig: %s" % binascii.hexlify(empty) + ) + r, rest = der.remove_integer(rs_strings) + s, empty = der.remove_integer(rest) + if empty != b"": + raise der.UnexpectedDER( + "trailing junk after DER numbers: %s" % binascii.hexlify(empty) + ) + return r, s diff --git a/code/.venv/lib/python3.12/site-packages/espefuse/__init__.py b/code/.venv/lib/python3.12/site-packages/espefuse/__init__.py new file mode 100644 index 0000000..777d53f --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/espefuse/__init__.py @@ -0,0 +1,302 @@ +# SPDX-FileCopyrightText: 2016-2022 Espressif Systems (Shanghai) CO LTD +# +# SPDX-License-Identifier: GPL-2.0-or-later + +import argparse +import os +import sys +from collections import namedtuple +from io import StringIO + +import espefuse.efuse.esp32 as esp32_efuse +import espefuse.efuse.esp32c2 as esp32c2_efuse +import espefuse.efuse.esp32c3 as esp32c3_efuse +import espefuse.efuse.esp32c6 as esp32c6_efuse +import espefuse.efuse.esp32h2 as esp32h2_efuse +import espefuse.efuse.esp32h2beta1 as esp32h2beta1_efuse +import espefuse.efuse.esp32p4 as esp32p4_efuse +import espefuse.efuse.esp32s2 as esp32s2_efuse +import espefuse.efuse.esp32s3 as esp32s3_efuse +import espefuse.efuse.esp32s3beta2 as esp32s3beta2_efuse + +import esptool + +DefChip = namedtuple("DefChip", ["chip_name", "efuse_lib", "chip_class"]) + +SUPPORTED_BURN_COMMANDS = [ + "read_protect_efuse", + "write_protect_efuse", + "burn_efuse", + "burn_block_data", + "burn_bit", + "burn_key", + "burn_key_digest", + "burn_custom_mac", + "set_flash_voltage", + "execute_scripts", +] + +SUPPORTED_COMMANDS = [ + "summary", + "dump", + "get_custom_mac", + "adc_info", + "check_error", +] + SUPPORTED_BURN_COMMANDS + +SUPPORTED_CHIPS = { + "esp32": DefChip("ESP32", esp32_efuse, esptool.targets.ESP32ROM), + "esp32c2": DefChip("ESP32-C2", esp32c2_efuse, esptool.targets.ESP32C2ROM), + "esp32c3": DefChip("ESP32-C3", esp32c3_efuse, esptool.targets.ESP32C3ROM), + "esp32c6": DefChip("ESP32-C6", esp32c6_efuse, esptool.targets.ESP32C6ROM), + "esp32h2": DefChip("ESP32-H2", esp32h2_efuse, esptool.targets.ESP32H2ROM), + "esp32p4": DefChip("ESP32-P4", esp32p4_efuse, esptool.targets.ESP32P4ROM), + "esp32h2beta1": DefChip( + "ESP32-H2(beta1)", esp32h2beta1_efuse, esptool.targets.ESP32H2BETA1ROM + ), + "esp32s2": DefChip("ESP32-S2", esp32s2_efuse, esptool.targets.ESP32S2ROM), + "esp32s3": DefChip("ESP32-S3", esp32s3_efuse, esptool.targets.ESP32S3ROM), + "esp32s3beta2": DefChip( + "ESP32-S3(beta2)", esp32s3beta2_efuse, esptool.targets.ESP32S3BETA2ROM + ), +} + + +def get_esp( + port, + baud, + connect_mode, + chip="auto", + skip_connect=False, + virt=False, + debug=False, + virt_efuse_file=None, +): + if chip not in ["auto"] + list(SUPPORTED_CHIPS.keys()): + raise esptool.FatalError("get_esp: Unsupported chip (%s)" % chip) + if virt: + efuse = SUPPORTED_CHIPS.get(chip, SUPPORTED_CHIPS["esp32"]).efuse_lib + esp = efuse.EmulateEfuseController(virt_efuse_file, debug) + else: + if chip == "auto" and not skip_connect: + esp = esptool.cmds.detect_chip(port, baud, connect_mode) + else: + esp = SUPPORTED_CHIPS.get(chip, SUPPORTED_CHIPS["esp32"]).chip_class( + port if not skip_connect else StringIO(), baud + ) + if not skip_connect: + esp.connect(connect_mode) + return esp + + +def get_efuses(esp, skip_connect=False, debug_mode=False, do_not_confirm=False): + for name in SUPPORTED_CHIPS: + if SUPPORTED_CHIPS[name].chip_name == esp.CHIP_NAME: + efuse = SUPPORTED_CHIPS[name].efuse_lib + return ( + efuse.EspEfuses(esp, skip_connect, debug_mode, do_not_confirm), + efuse.operations, + ) + else: + raise esptool.FatalError("get_efuses: Unsupported chip (%s)" % esp.CHIP_NAME) + + +def split_on_groups(all_args): + """ + This function splits the all_args list into groups, + where each item is a cmd with all its args. + + Example: + all_args: + ['burn_key_digest', 'secure_images/ecdsa256_secure_boot_signing_key_v2.pem', + 'burn_key', 'BLOCK_KEY0', 'images/efuse/128bit_key', + 'XTS_AES_128_KEY_DERIVED_FROM_128_EFUSE_BITS'] + + used_cmds: ['burn_key_digest', 'burn_key'] + groups: + [['burn_key_digest', 'secure_images/ecdsa256_secure_boot_signing_key_v2.pem'], + ['burn_key', 'BLOCK_KEY0', 'images/efuse/128bit_key', + 'XTS_AES_128_KEY_DERIVED_FROM_128_EFUSE_BITS']] + """ + + groups = [] + cmd = [] + used_cmds = [] + for item in all_args: + if item in SUPPORTED_COMMANDS: + used_cmds.append(item) + if cmd != []: + groups.append(cmd) + cmd = [] + cmd.append(item) + if cmd: + groups.append(cmd) + return groups, used_cmds + + +def main(custom_commandline=None, esp=None): + """ + Main function for espefuse + + custom_commandline - Optional override for default arguments parsing + (that uses sys.argv), can be a list of custom arguments as strings. + Arguments and their values need to be added as individual items to the list + e.g. "--port /dev/ttyUSB1" thus becomes ['--port', '/dev/ttyUSB1']. + + esp - Optional override of the connected device previously + returned by esptool.get_default_connected_device() + """ + + external_esp = esp is not None + + init_parser = argparse.ArgumentParser( + description="espefuse.py v%s - [ESP32xx] efuse get/set tool" + % esptool.__version__, + prog="espefuse", + add_help=False, + ) + + init_parser.add_argument( + "--chip", + "-c", + help="Target chip type", + choices=["auto"] + list(SUPPORTED_CHIPS.keys()), + default=os.environ.get("ESPTOOL_CHIP", "auto"), + ) + + init_parser.add_argument( + "--baud", + "-b", + help="Serial port baud rate used when flashing/reading", + type=esptool.arg_auto_int, + default=os.environ.get("ESPTOOL_BAUD", esptool.loader.ESPLoader.ESP_ROM_BAUD), + ) + + init_parser.add_argument( + "--port", + "-p", + help="Serial port device", + default=os.environ.get("ESPTOOL_PORT", esptool.loader.ESPLoader.DEFAULT_PORT), + ) + + init_parser.add_argument( + "--before", + help="What to do before connecting to the chip", + choices=["default_reset", "usb_reset", "no_reset", "no_reset_no_sync"], + default="default_reset", + ) + + init_parser.add_argument( + "--debug", + "-d", + help="Show debugging information (loglevel=DEBUG)", + action="store_true", + ) + init_parser.add_argument( + "--virt", + help="For host tests, the tool will work in the virtual mode " + "(without connecting to a chip).", + action="store_true", + ) + init_parser.add_argument( + "--path-efuse-file", + help="For host tests, saves efuse memory to file.", + type=str, + default=None, + ) + init_parser.add_argument( + "--do-not-confirm", + help="Do not pause for confirmation before permanently writing efuses. " + "Use with caution.", + action="store_true", + ) + + common_args, remaining_args = init_parser.parse_known_args(custom_commandline) + debug_mode = common_args.debug or ("dump" in remaining_args) + just_print_help = [ + True for arg in remaining_args if arg in ["--help", "-h"] + ] or remaining_args == [] + + print("espefuse.py v{}".format(esptool.__version__)) + + if not external_esp: + try: + esp = get_esp( + common_args.port, + common_args.baud, + common_args.before, + common_args.chip, + just_print_help, + common_args.virt, + common_args.debug, + common_args.path_efuse_file, + ) + except esptool.FatalError as e: + raise esptool.FatalError( + f"{e}\nPlease make sure that you have specified " + "the right port with the --port argument" + ) + # TODO: Require the --port argument in the next major release, ESPTOOL-490 + + efuses, efuse_operations = get_efuses( + esp, just_print_help, debug_mode, common_args.do_not_confirm + ) + + parser = argparse.ArgumentParser(parents=[init_parser]) + subparsers = parser.add_subparsers( + dest="operation", help="Run espefuse.py {command} -h for additional help" + ) + + efuse_operations.add_commands(subparsers, efuses) + + grouped_remaining_args, used_cmds = split_on_groups(remaining_args) + if len(grouped_remaining_args) == 0: + parser.print_help() + parser.exit(1) + there_are_multiple_burn_commands_in_args = ( + sum(cmd in SUPPORTED_BURN_COMMANDS for cmd in used_cmds) > 1 + ) + if there_are_multiple_burn_commands_in_args: + efuses.batch_mode_cnt += 1 + + try: + for rem_args in grouped_remaining_args: + args, unused_args = parser.parse_known_args(rem_args, namespace=common_args) + if args.operation is None: + parser.print_help() + parser.exit(1) + assert ( + len(unused_args) == 0 + ), 'Not all commands were recognized "{}"'.format(unused_args) + + operation_func = vars(efuse_operations)[args.operation] + # each 'operation' is a module-level function of the same name + print('\n=== Run "{}" command ==='.format(args.operation)) + + if hasattr(args, "show_sensitive_info"): + if args.show_sensitive_info or args.debug: + args.show_sensitive_info = True + else: + print("Sensitive data will be hidden (see --show-sensitive-info)") + + operation_func(esp, efuses, args) + + if there_are_multiple_burn_commands_in_args: + efuses.batch_mode_cnt -= 1 + if not efuses.burn_all(check_batch_mode=True): + raise esptool.FatalError("BURN was not done") + finally: + if not external_esp and not common_args.virt and esp._port: + esp._port.close() + + +def _main(): + try: + main() + except esptool.FatalError as e: + print("\nA fatal error occurred: %s" % e) + sys.exit(2) + + +if __name__ == "__main__": + _main() diff --git a/code/.venv/lib/python3.12/site-packages/espefuse/__main__.py b/code/.venv/lib/python3.12/site-packages/espefuse/__main__.py new file mode 100644 index 0000000..596cf65 --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/espefuse/__main__.py @@ -0,0 +1,8 @@ +# SPDX-FileCopyrightText: 2016-2022 Espressif Systems (Shanghai) CO LTD +# +# SPDX-License-Identifier: GPL-2.0-or-later + +import espefuse + +if __name__ == "__main__": + espefuse._main() diff --git a/code/.venv/lib/python3.12/site-packages/espefuse/__pycache__/__init__.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/espefuse/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..5307e84 Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/espefuse/__pycache__/__init__.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/espefuse/__pycache__/__main__.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/espefuse/__pycache__/__main__.cpython-312.pyc new file mode 100644 index 0000000..c31f0c8 Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/espefuse/__pycache__/__main__.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/espefuse/efuse/__init__.py b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/code/.venv/lib/python3.12/site-packages/espefuse/efuse/__pycache__/__init__.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..b8a8956 Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/__pycache__/__init__.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/espefuse/efuse/__pycache__/base_fields.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/__pycache__/base_fields.cpython-312.pyc new file mode 100644 index 0000000..45b1b67 Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/__pycache__/base_fields.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/espefuse/efuse/__pycache__/base_operations.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/__pycache__/base_operations.cpython-312.pyc new file mode 100644 index 0000000..8980bb3 Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/__pycache__/base_operations.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/espefuse/efuse/__pycache__/emulate_efuse_controller_base.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/__pycache__/emulate_efuse_controller_base.cpython-312.pyc new file mode 100644 index 0000000..2ea0150 Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/__pycache__/emulate_efuse_controller_base.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/espefuse/efuse/__pycache__/mem_definition_base.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/__pycache__/mem_definition_base.cpython-312.pyc new file mode 100644 index 0000000..8e9241c Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/__pycache__/mem_definition_base.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/espefuse/efuse/__pycache__/util.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/__pycache__/util.cpython-312.pyc new file mode 100644 index 0000000..3383a72 Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/__pycache__/util.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/espefuse/efuse/base_fields.py b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/base_fields.py new file mode 100644 index 0000000..ab12f03 --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/base_fields.py @@ -0,0 +1,754 @@ +# This file describes the common eFuses structures for chips +# +# SPDX-FileCopyrightText: 2020-2022 Espressif Systems (Shanghai) CO LTD +# +# SPDX-License-Identifier: GPL-2.0-or-later + +import binascii +import sys + +from bitstring import BitArray, BitStream, CreationError + +import esptool + +from . import util + + +class CheckArgValue(object): + def __init__(self, efuses, name): + self.efuses = efuses + self.name = name + + def __call__(self, new_value_str): + def check_arg_value(efuse, new_value): + if efuse.efuse_type.startswith("bool"): + new_value = 1 if new_value is None else int(new_value, 0) + if new_value != 1: + raise esptool.FatalError( + "New value is not accepted for efuse '{}' " + "(will always burn 0->1), given value={}".format( + efuse.name, new_value + ) + ) + elif efuse.efuse_type.startswith(("int", "uint")): + if efuse.efuse_class == "bitcount": + if new_value is None: + # find the first unset bit and set it + old_value = efuse.get_raw() + new_value = old_value + bit = 1 + while new_value == old_value: + new_value = bit | old_value + bit <<= 1 + else: + new_value = int(new_value, 0) + else: + if new_value is None: + raise esptool.FatalError( + "New value required for efuse '{}' (given None)".format( + efuse.name + ) + ) + new_value = int(new_value, 0) + if new_value == 0: + raise esptool.FatalError( + "New value should not be 0 for '{}' " + "(given value= {})".format(efuse.name, new_value) + ) + elif efuse.efuse_type.startswith("bytes"): + if new_value is None: + raise esptool.FatalError( + "New value required for efuse '{}' " + "(given None)".format(efuse.name) + ) + if len(new_value) * 8 != efuse.bitarray.len: + raise esptool.FatalError( + "The length of efuse '{}' ({} bits) " + "(given len of the new value= {} bits)".format( + efuse.name, efuse.bitarray.len, len(new_value) * 8 + ) + ) + else: + raise esptool.FatalError( + "The '{}' type for the '{}' efuse is not supported yet.".format( + efuse.efuse_type, efuse.name + ) + ) + return new_value + + efuse = self.efuses[self.name] + new_value = efuse.check_format(new_value_str) + return check_arg_value(efuse, new_value) + + +class EfuseProtectBase(object): + # This class is used by EfuseBlockBase and EfuseFieldBase + + def get_read_disable_mask(self, blk_part=None): + """Returns mask of read protection bits + blk_part: + - None: Calculate mask for all read protection bits. + - a number: Calculate mask only for specific item in read protection list. + """ + mask = 0 + if isinstance(self.read_disable_bit, list): + if blk_part is None: + for i in self.read_disable_bit: + mask |= 1 << i + else: + mask |= 1 << self.read_disable_bit[blk_part] + else: + mask = 1 << self.read_disable_bit + return mask + + def get_count_read_disable_bits(self): + """Returns the number of read protection bits used by the field""" + # On the C2 chip, BLOCK_KEY0 has two read protection bits [0, 1]. + return bin(self.get_read_disable_mask()).count("1") + + def is_readable(self, blk_part=None): + """Return true if the efuse is readable by software""" + num_bit = self.read_disable_bit + if num_bit is None: + return True # read cannot be disabled + return (self.parent["RD_DIS"].get() & self.get_read_disable_mask(blk_part)) == 0 + + def disable_read(self): + num_bit = self.read_disable_bit + if num_bit is None: + raise esptool.FatalError("This efuse cannot be read-disabled") + if not self.parent["RD_DIS"].is_writeable(): + raise esptool.FatalError( + "This efuse cannot be read-disabled due the to RD_DIS field is " + "already write-disabled" + ) + self.parent["RD_DIS"].save(self.get_read_disable_mask()) + + def is_writeable(self): + num_bit = self.write_disable_bit + if num_bit is None: + return True # write cannot be disabled + return (self.parent["WR_DIS"].get() & (1 << num_bit)) == 0 + + def disable_write(self): + num_bit = self.write_disable_bit + if not self.parent["WR_DIS"].is_writeable(): + raise esptool.FatalError( + "This efuse cannot be write-disabled due to the WR_DIS field is " + "already write-disabled" + ) + self.parent["WR_DIS"].save(1 << num_bit) + + def check_wr_rd_protect(self): + if not self.is_readable(): + error_msg = "\t{} is read-protected.".format(self.name) + "The written value can not be read, the efuse/block looks as all 0.\n" + error_msg += "\tBurn in this case may damage an already written value." + self.parent.print_error_msg(error_msg) + if not self.is_writeable(): + error_msg = "\t{} is write-protected. Burn is not possible.".format( + self.name + ) + self.parent.print_error_msg(error_msg) + + +class EfuseBlockBase(EfuseProtectBase): + def __init__(self, parent, param, skip_read=False): + self.parent = parent + self.name = param.name + self.alias = param.alias + self.id = param.id + self.rd_addr = param.rd_addr + self.wr_addr = param.wr_addr + self.write_disable_bit = param.write_disable_bit + self.read_disable_bit = param.read_disable_bit + self.len = param.len + self.key_purpose_name = param.key_purpose + bit_block_len = self.get_block_len() * 8 + self.bitarray = BitStream(bit_block_len) + self.bitarray.set(0) + self.wr_bitarray = BitStream(bit_block_len) + self.wr_bitarray.set(0) + self.fail = False + self.num_errors = 0 + if self.id == 0: + self.err_bitarray = BitStream(bit_block_len) + self.err_bitarray.set(0) + else: + self.err_bitarray = None + + if not skip_read: + self.read() + + def get_block_len(self): + coding_scheme = self.get_coding_scheme() + if coding_scheme == self.parent.REGS.CODING_SCHEME_NONE: + return self.len * 4 + elif coding_scheme == self.parent.REGS.CODING_SCHEME_34: + return (self.len * 3 // 4) * 4 + elif coding_scheme == self.parent.REGS.CODING_SCHEME_RS: + return self.len * 4 + else: + raise esptool.FatalError( + "Coding scheme (%d) not supported" % (coding_scheme) + ) + + def get_coding_scheme(self): + if self.id == 0: + return self.parent.REGS.CODING_SCHEME_NONE + else: + return self.parent.coding_scheme + + def get_raw(self, from_read=True): + if from_read: + return self.bitarray.bytes + else: + return self.wr_bitarray.bytes + + def get(self, from_read=True): + self.get_bitstring(from_read=from_read) + + def get_bitstring(self, from_read=True): + if from_read: + return self.bitarray + else: + return self.wr_bitarray + + def convert_to_bitstring(self, new_data): + if isinstance(new_data, BitArray): + return new_data + else: + return BitArray(bytes=new_data, length=len(new_data) * 8) + + def get_words(self): + def get_offsets(self): + return [x + self.rd_addr for x in range(0, self.get_block_len(), 4)] + + return [self.parent.read_reg(offs) for offs in get_offsets(self)] + + def read(self): + words = self.get_words() + data = BitArray() + for word in reversed(words): + data.append("uint:32=%d" % word) + self.bitarray.overwrite(data, pos=0) + self.print_block(self.bitarray, "read_regs") + + def print_block(self, bit_string, comment, debug=False): + if self.parent.debug or debug: + bit_string.pos = 0 + print( + "%-15s (%-16s) [%-2d] %s:" + % (self.name, " ".join(self.alias)[:16], self.id, comment), + " ".join( + [ + "%08x" % word + for word in bit_string.readlist( + "%d*uint:32" % (bit_string.len / 32) + )[::-1] + ] + ), + ) + + def check_wr_data(self): + wr_data = self.wr_bitarray + if wr_data.all(False): + # nothing to burn + if self.parent.debug: + print("[{:02}] {:20} nothing to burn".format(self.id, self.name)) + return False + if len(wr_data.bytes) != len(self.bitarray.bytes): + raise esptool.FatalError( + "Data does not fit: the block%d size is %d bytes, data is %d bytes" + % (self.id, len(self.bitarray.bytes), len(wr_data.bytes)) + ) + self.check_wr_rd_protect() + + if self.get_bitstring().all(False): + print( + "[{:02}] {:20} is empty, will burn the new value".format( + self.id, self.name + ) + ) + else: + # the written block in chip is not empty + if self.get_bitstring() == wr_data: + print( + "[{:02}] {:20} is already written the same value, " + "continue with EMPTY_BLOCK".format(self.id, self.name) + ) + wr_data.set(0) + else: + print("[{:02}] {:20} is not empty".format(self.id, self.name)) + print("\t(written ):", self.get_bitstring()) + print("\t(to write):", wr_data) + mask = self.get_bitstring() & wr_data + if mask == wr_data: + print( + "\tAll wr_data bits are set in the written block, " + "continue with EMPTY_BLOCK." + ) + wr_data.set(0) + else: + coding_scheme = self.get_coding_scheme() + if coding_scheme == self.parent.REGS.CODING_SCHEME_NONE: + print("\t(coding scheme = NONE)") + elif coding_scheme == self.parent.REGS.CODING_SCHEME_RS: + print("\t(coding scheme = RS)") + error_msg = ( + "\tBurn into %s is forbidden " + "(RS coding scheme does not allow this)." % (self.name) + ) + self.parent.print_error_msg(error_msg) + elif coding_scheme == self.parent.REGS.CODING_SCHEME_34: + print("\t(coding scheme = 3/4)") + data_can_not_be_burn = False + for i in range(0, self.get_bitstring().len, 6 * 8): + rd_chunk = self.get_bitstring()[i : i + 6 * 8 :] + wr_chunk = wr_data[i : i + 6 * 8 :] + if rd_chunk.any(True): + if wr_chunk.any(True): + print( + "\twritten chunk [%d] and wr_chunk " + "are not empty. " % (i // (6 * 8)), + end="", + ) + if rd_chunk == wr_chunk: + print( + "wr_chunk == rd_chunk. " + "Countinue with empty chunk." + ) + wr_data[i : i + 6 * 8 :].set(0) + else: + print("wr_chunk != rd_chunk. Can not burn.") + print("\twritten ", rd_chunk) + print("\tto write", wr_chunk) + data_can_not_be_burn = True + if data_can_not_be_burn: + error_msg = ( + "\tBurn into %s is forbidden " + "(3/4 coding scheme does not allow this)." % (self.name) + ) + self.parent.print_error_msg(error_msg) + else: + raise esptool.FatalError( + "The coding scheme ({}) is not supported".format( + coding_scheme + ) + ) + + def save(self, new_data): + # new_data will be checked by check_wr_data() during burn_all() + # new_data (bytes) = [0][1][2] ... [N] (original data) + # in string format = [0] [1] [2] ... [N] (util.hexify(data, " ")) + # in hex format = 0x[N]....[2][1][0] (from bitstring print(data)) + # in reg format = [3][2][1][0] ... [N][][][] (as it will be in the device) + # in bitstring = [N] ... [2][1][0] (to get a correct bitstring + # need to reverse new_data) + # *[x] - means a byte. + data = BitStream(bytes=new_data[::-1], length=len(new_data) * 8) + if self.parent.debug: + print( + "\twritten : {} ->\n\tto write: {}".format(self.get_bitstring(), data) + ) + self.wr_bitarray.overwrite(self.wr_bitarray | data, pos=0) + + def burn_words(self, words): + for burns in range(3): + self.parent.efuse_controller_setup() + if self.parent.debug: + print("Write data to BLOCK%d" % (self.id)) + write_reg_addr = self.wr_addr + for word in words: + # for ep32s2: using EFUSE_PGM_DATA[0..7]_REG for writing data + # 32 bytes to EFUSE_PGM_DATA[0..7]_REG + # 12 bytes to EFUSE_CHECK_VALUE[0..2]_REG. These regs are next after + # EFUSE_PGM_DATA_REG + # for esp32: + # each block has the special regs EFUSE_BLK[0..3]_WDATA[0..7]_REG + # for writing data + if self.parent.debug: + print("Addr 0x%08x, data=0x%08x" % (write_reg_addr, word)) + self.parent.write_reg(write_reg_addr, word) + write_reg_addr += 4 + + self.parent.write_efuses(self.id) + for _ in range(5): + self.parent.efuse_read() + self.parent.get_coding_scheme_warnings(silent=True) + if self.fail or self.num_errors: + print( + "Error in BLOCK%d, re-burn it again (#%d), to fix it. " + "fail_bit=%d, num_errors=%d" + % (self.id, burns, self.fail, self.num_errors) + ) + break + if not self.fail and self.num_errors == 0: + break + + def burn(self): + if self.wr_bitarray.all(False): + # nothing to burn + return + before_burn_bitarray = self.bitarray[:] + assert before_burn_bitarray is not self.bitarray + self.print_block(self.wr_bitarray, "to_write") + words = self.apply_coding_scheme() + self.burn_words(words) + self.read() + if not self.is_readable(): + print( + "{} ({}) is read-protected. " + "Read back the burn value is not possible.".format( + self.name, self.alias + ) + ) + if self.bitarray.all(False): + print("Read all '0'") + else: + # Should never happen + raise esptool.FatalError( + "The {} is read-protected but not all '0' ({})".format( + self.name, self.bitarray.hex + ) + ) + else: + if self.wr_bitarray == self.bitarray: + print("BURN BLOCK%-2d - OK (write block == read block)" % self.id) + elif ( + self.wr_bitarray & self.bitarray == self.wr_bitarray + and self.bitarray & before_burn_bitarray == before_burn_bitarray + ): + print("BURN BLOCK%-2d - OK (all write block bits are set)" % self.id) + else: + # Happens only when an efuse is written and read-protected + # in one command + self.print_block(self.wr_bitarray, "Expected") + self.print_block(self.bitarray, "Real ") + # Read-protected BLK0 values are reported back as zeros, + # raise error only for other blocks + if self.id != 0: + raise esptool.FatalError( + "Burn {} ({}) was not successful".format(self.name, self.alias) + ) + self.wr_bitarray.set(0) + + +class EspEfusesBase(object): + """ + Wrapper object to manage the efuse fields in a connected ESP bootloader + """ + + _esp = None + blocks = [] + efuses = [] + coding_scheme = None + force_write_always = None + batch_mode_cnt = 0 + + def __iter__(self): + return self.efuses.__iter__() + + def get_crystal_freq(self): + return self._esp.get_crystal_freq() + + def read_efuse(self, n): + """Read the nth word of the ESP3x EFUSE region.""" + return self._esp.read_efuse(n) + + def read_reg(self, addr): + return self._esp.read_reg(addr) + + def write_reg(self, addr, value, mask=0xFFFFFFFF, delay_us=0, delay_after_us=0): + return self._esp.write_reg(addr, value, mask, delay_us, delay_after_us) + + def update_reg(self, addr, mask, new_val): + return self._esp.update_reg(addr, mask, new_val) + + def efuse_controller_setup(self): + pass + + def reconnect_chip(self, esp): + print("Re-connecting...") + baudrate = esp._port.baudrate + port = esp._port.port + esp._port.close() + return esptool.cmds.detect_chip(port, baudrate) + + def get_index_block_by_name(self, name): + for block in self.blocks: + if block.name == name or name in block.alias: + return block.id + return None + + def read_blocks(self): + for block in self.blocks: + block.read() + + def update_efuses(self): + for efuse in self.efuses: + efuse.update(self.blocks[efuse.block].bitarray) + + def burn_all(self, check_batch_mode=False): + if check_batch_mode: + if self.batch_mode_cnt != 0: + print( + "\nBatch mode is enabled, " + "the burn will be done at the end of the command." + ) + return False + print("\nCheck all blocks for burn...") + print("idx, BLOCK_NAME, Conclusion") + have_wr_data_for_burn = False + for block in self.blocks: + block.check_wr_data() + if not have_wr_data_for_burn and block.get_bitstring(from_read=False).any( + True + ): + have_wr_data_for_burn = True + if not have_wr_data_for_burn: + print("Nothing to burn, see messages above.") + return + EspEfusesBase.confirm("", self.do_not_confirm) + + # Burn from BLKn -> BLK0. Because BLK0 can set rd or/and wr protection bits. + for block in reversed(self.blocks): + old_fail = block.fail + old_num_errors = block.num_errors + block.burn() + if (block.fail and old_fail != block.fail) or ( + block.num_errors and block.num_errors > old_num_errors + ): + raise esptool.FatalError("Error(s) were detected in eFuses") + print("Reading updated efuses...") + self.read_coding_scheme() + self.read_blocks() + self.update_efuses() + return True + + @staticmethod + def confirm(action, do_not_confirm): + print( + "%s%s\nThis is an irreversible operation!" + % (action, "" if action.endswith("\n") else ". ") + ) + if not do_not_confirm: + print("Type 'BURN' (all capitals) to continue.") + # required for Pythons which disable line buffering, ie mingw in mintty + sys.stdout.flush() + yes = input() + if yes != "BURN": + print("Aborting.") + sys.exit(0) + + def print_error_msg(self, error_msg): + if self.force_write_always is not None: + if not self.force_write_always: + error_msg += "(use '--force-write-always' option to ignore it)" + if self.force_write_always: + print(error_msg, "Skipped because '--force-write-always' option.") + else: + raise esptool.FatalError(error_msg) + + def get_block_errors(self, block_num): + """Returns (error count, failure boolean flag)""" + return self.blocks[block_num].num_errors, self.blocks[block_num].fail + + +class EfuseFieldBase(EfuseProtectBase): + def __init__(self, parent, param): + self.category = param.category + self.parent = parent + self.block = param.block + self.word = param.word + self.pos = param.pos + self.write_disable_bit = param.write_disable_bit + self.read_disable_bit = param.read_disable_bit + self.name = param.name + self.efuse_class = param.class_type + self.efuse_type = param.type + self.description = param.description + self.dict_value = param.dictionary + self.bit_len = param.bit_len + self.alt_names = param.alt_names + self.fail = False + self.num_errors = 0 + self.bitarray = BitStream(self.bit_len) + self.bitarray.set(0) + self.update(self.parent.blocks[self.block].bitarray) + + def is_field_calculated(self): + return self.word is None or self.pos is None + + def check_format(self, new_value_str): + if new_value_str is None: + return new_value_str + else: + if self.efuse_type.startswith("bytes"): + if new_value_str.startswith("0x"): + # cmd line: 0x0102030405060708 .... 112233ff (hex) + # regs: 112233ff ... 05060708 01020304 + # BLK: ff 33 22 11 ... 08 07 06 05 04 03 02 01 + return binascii.unhexlify(new_value_str[2:])[::-1] + else: + # cmd line: 0102030405060708 .... 112233ff (string) + # regs: 04030201 08070605 ... ff332211 + # BLK: 01 02 03 04 05 06 07 08 ... 11 22 33 ff + return binascii.unhexlify(new_value_str) + else: + return new_value_str + + def convert_to_bitstring(self, new_value): + if isinstance(new_value, BitArray): + return new_value + else: + if self.efuse_type.startswith("bytes"): + # new_value (bytes) = [0][1][2] ... [N] + # (original data) + # in string format = [0] [1] [2] ... [N] + # (util.hexify(data, " ")) + # in hex format = 0x[N]....[2][1][0] + # (from bitstring print(data)) + # in reg format = [3][2][1][0] ... [N][][][] + # (as it will be in the device) + # in bitstring = [N] ... [2][1][0] + # (to get a correct bitstring need to reverse new_value) + # *[x] - means a byte. + return BitArray(bytes=new_value[::-1], length=len(new_value) * 8) + else: + try: + return BitArray(self.efuse_type + "={}".format(new_value)) + except CreationError as err: + print( + "New value '{}' is not suitable for {} ({})".format( + new_value, self.name, self.efuse_type + ) + ) + raise esptool.FatalError(err) + + def check_new_value(self, bitarray_new_value): + bitarray_old_value = self.get_bitstring() | self.get_bitstring(from_read=False) + if bitarray_new_value.len != bitarray_old_value.len: + raise esptool.FatalError( + "For {} efuse, the length of the new value is wrong, " + "expected {} bits, was {} bits.".format( + self.name, bitarray_old_value.len, bitarray_new_value.len + ) + ) + if bitarray_new_value == bitarray_old_value: + error_msg = "\tThe same value for {} ".format(self.name) + error_msg += "is already burned. Do not change the efuse." + print(error_msg) + bitarray_new_value.set(0) + elif bitarray_new_value == self.get_bitstring(from_read=False): + error_msg = "\tThe same value for {} ".format(self.name) + error_msg += "is already prepared for the burn operation." + print(error_msg) + bitarray_new_value.set(0) + else: + if self.name not in ["WR_DIS", "RD_DIS"]: + # WR_DIS, RD_DIS fields can have already set bits. + # Do not neeed to check below condition for them. + if bitarray_new_value | bitarray_old_value != bitarray_new_value: + error_msg = "\tNew value contains some bits that cannot be cleared " + error_msg += "(value will be {})".format( + bitarray_old_value | bitarray_new_value + ) + self.parent.print_error_msg(error_msg) + self.check_wr_rd_protect() + + def save_to_block(self, bitarray_field): + block = self.parent.blocks[self.block] + wr_bitarray_temp = block.wr_bitarray.copy() + position = wr_bitarray_temp.length - ( + self.word * 32 + self.pos + bitarray_field.len + ) + wr_bitarray_temp.overwrite(bitarray_field, pos=position) + block.wr_bitarray |= wr_bitarray_temp + + def save(self, new_value): + bitarray_field = self.convert_to_bitstring(new_value) + self.check_new_value(bitarray_field) + self.save_to_block(bitarray_field) + + def update(self, bit_array_block): + if self.is_field_calculated(): + self.bitarray.overwrite( + self.convert_to_bitstring(self.check_format(self.get())), pos=0 + ) + return + field_len = self.bitarray.len + bit_array_block.pos = bit_array_block.length - ( + self.word * 32 + self.pos + field_len + ) + self.bitarray.overwrite(bit_array_block.read(field_len), pos=0) + err_bitarray = self.parent.blocks[self.block].err_bitarray + if err_bitarray is not None: + err_bitarray.pos = err_bitarray.length - ( + self.word * 32 + self.pos + field_len + ) + self.fail = not err_bitarray.read(field_len).all(False) + else: + self.fail = self.parent.blocks[self.block].fail + self.num_errors = self.parent.blocks[self.block].num_errors + + def get_raw(self, from_read=True): + """Return the raw (unformatted) numeric value of the efuse bits + + Returns a simple integer or (for some subclasses) a bitstring. + type: int or bool -> int + type: bytes -> bytearray + """ + return self.get_bitstring(from_read).read(self.efuse_type) + + def get(self, from_read=True): + """Get a formatted version of the efuse value, suitable for display + type: int or bool -> int + type: bytes -> string "01 02 03 04 05 06 07 08 ... ". + Byte order [0] ... [N]. dump regs: 0x04030201 0x08070605 ... + """ + if self.efuse_type.startswith("bytes"): + return util.hexify(self.get_bitstring(from_read).bytes[::-1], " ") + else: + return self.get_raw(from_read) + + def get_meaning(self, from_read=True): + """Get the meaning of efuse from dict if possible, suitable for display""" + if self.dict_value: + try: + return self.dict_value[self.get_raw(from_read)] + except KeyError: + pass + return self.get(from_read) + + def get_bitstring(self, from_read=True): + if from_read: + self.bitarray.pos = 0 + return self.bitarray + else: + field_len = self.bitarray.len + block = self.parent.blocks[self.block] + block.wr_bitarray.pos = block.wr_bitarray.length - ( + self.word * 32 + self.pos + field_len + ) + return block.wr_bitarray.read(self.bitarray.len) + + def burn(self, new_value): + # Burn a efuse. Added for compatibility reason. + self.save(new_value) + self.parent.burn_all() + + def get_info(self): + output = f"{self.name} (BLOCK{self.block})" + if self.block == 0: + if self.fail: + output += "[error]" + else: + errs, fail = self.parent.get_block_errors(self.block) + if errs != 0 or fail: + output += "[error]" + if self.efuse_class == "keyblock": + name = self.parent.blocks[self.block].key_purpose_name + if name is not None: + output += f"\n Purpose: {self.parent[name].get()}\n " + return output diff --git a/code/.venv/lib/python3.12/site-packages/espefuse/efuse/base_operations.py b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/base_operations.py new file mode 100644 index 0000000..5e3475e --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/base_operations.py @@ -0,0 +1,731 @@ +# This file includes the common operations with eFuses for chips +# +# SPDX-FileCopyrightText: 2020-2022 Espressif Systems (Shanghai) CO LTD +# +# SPDX-License-Identifier: GPL-2.0-or-later + +import argparse +import json +import sys + +from bitstring import BitStream + +import esptool + +from . import base_fields +from . import util + + +def add_common_commands(subparsers, efuses): + class ActionEfuseValuePair(argparse.Action): + def __init__(self, option_strings, dest, nargs=None, **kwargs): + self._nargs = nargs + self._choices = kwargs.get("efuse_choices") + self.efuses = kwargs.get("efuses") + del kwargs["efuse_choices"] + del kwargs["efuses"] + super(ActionEfuseValuePair, self).__init__( + option_strings, dest, nargs=nargs, **kwargs + ) + + def __call__(self, parser, namespace, values, option_string=None): + def check_efuse_name(efuse_name, efuse_list): + if efuse_name not in self._choices: + raise esptool.FatalError( + "Invalid the efuse name '{}'. " + "Available the efuse names: {}".format( + efuse_name, self._choices + ) + ) + + efuse_value_pairs = {} + if len(values) > 1: + if len(values) % 2: + raise esptool.FatalError( + "The list does not have a valid pair (name value) {}".format( + values + ) + ) + for i in range(0, len(values), 2): + efuse_name, new_value = values[i : i + 2 :] + check_efuse_name(efuse_name, self._choices) + check_arg = base_fields.CheckArgValue(self.efuses, efuse_name) + efuse_value_pairs[efuse_name] = check_arg(new_value) + else: + # For the case of compatibility, when only the efuse_name is given + # Fields with 'bitcount' and 'bool' types can be without new_value arg + efuse_name = values[0] + check_efuse_name(efuse_name, self._choices) + check_arg = base_fields.CheckArgValue(self.efuses, efuse_name) + efuse_value_pairs[efuse_name] = check_arg(None) + setattr(namespace, self.dest, efuse_value_pairs) + + burn = subparsers.add_parser( + "burn_efuse", help="Burn the efuse with the specified name" + ) + burn.add_argument( + "name_value_pairs", + help="Name of efuse register and New value pairs to burn", + action=ActionEfuseValuePair, + nargs="+", + metavar="[EFUSE_NAME VALUE] [{} VALUE".format( + " VALUE] [".join([e.name for e in efuses.efuses]) + ), + efuse_choices=[e.name for e in efuses.efuses] + + [name for e in efuses.efuses for name in e.alt_names if name != ""], + efuses=efuses, + ) + + read_protect_efuse = subparsers.add_parser( + "read_protect_efuse", + help="Disable readback for the efuse with the specified name", + ) + read_protect_efuse.add_argument( + "efuse_name", + help="Name of efuse register to burn", + nargs="+", + choices=[e.name for e in efuses.efuses if e.read_disable_bit is not None] + + [ + name + for e in efuses.efuses + if e.read_disable_bit is not None + for name in e.alt_names + if name != "" + ], + ) + + write_protect_efuse = subparsers.add_parser( + "write_protect_efuse", + help="Disable writing to the efuse with the specified name", + ) + write_protect_efuse.add_argument( + "efuse_name", + help="Name of efuse register to burn", + nargs="+", + choices=[e.name for e in efuses.efuses if e.write_disable_bit is not None] + + [ + name + for e in efuses.efuses + if e.write_disable_bit is not None + for name in e.alt_names + if name != "" + ], + ) + + burn_block_data = subparsers.add_parser( + "burn_block_data", + help="Burn non-key data to EFUSE blocks. " + "(Don't use this command to burn key data for Flash Encryption or " + "ESP32 Secure Boot V1, as the byte order of keys is swapped (use burn_key)).", + ) + add_force_write_always(burn_block_data) + burn_block_data.add_argument( + "--offset", "-o", help="Byte offset in the efuse block", type=int, default=0 + ) + burn_block_data.add_argument( + "block", + help="Efuse block to burn.", + action="append", + choices=efuses.BURN_BLOCK_DATA_NAMES, + ) + burn_block_data.add_argument( + "datafile", + help="File containing data to burn into the efuse block", + action="append", + type=argparse.FileType("rb"), + ) + for _ in range(0, len(efuses.BURN_BLOCK_DATA_NAMES)): + burn_block_data.add_argument( + "block", + help="Efuse block to burn.", + metavar="BLOCK", + nargs="?", + action="append", + choices=efuses.BURN_BLOCK_DATA_NAMES, + ) + burn_block_data.add_argument( + "datafile", + nargs="?", + help="File containing data to burn into the efuse block", + metavar="DATAFILE", + action="append", + type=argparse.FileType("rb"), + ) + + set_bit_cmd = subparsers.add_parser("burn_bit", help="Burn bit in the efuse block.") + add_force_write_always(set_bit_cmd) + set_bit_cmd.add_argument( + "block", help="Efuse block to burn.", choices=efuses.BURN_BLOCK_DATA_NAMES + ) + set_bit_cmd.add_argument( + "bit_number", + help="Bit number in the efuse block [0..BLK_LEN-1]", + nargs="+", + type=int, + ) + + subparsers.add_parser( + "adc_info", + help="Display information about ADC calibration data stored in efuse.", + ) + + dump_cmd = subparsers.add_parser("dump", help="Dump raw hex values of all efuses") + dump_cmd.add_argument( + "--file_name", + help="Saves dump for each block into separate file. Provide the common " + "path name /path/blk.bin, it will create: blk0.bin, blk1.bin ... blkN.bin. " + "Use burn_block_data to write it back to another chip.", + ) + + summary_cmd = subparsers.add_parser( + "summary", help="Print human-readable summary of efuse values" + ) + summary_cmd.add_argument( + "--format", + help="Select the summary format", + choices=["summary", "json"], + default="summary", + ) + summary_cmd.add_argument( + "--file", + help="File to save the efuse summary", + type=argparse.FileType("w"), + default=sys.stdout, + ) + + execute_scripts = subparsers.add_parser( + "execute_scripts", help="Executes scripts to burn at one time." + ) + execute_scripts.add_argument( + "scripts", + help="The special format of python scripts.", + nargs="+", + type=argparse.FileType("r"), + ) + execute_scripts.add_argument( + "--index", + help="integer index. " + "It allows to retrieve unique data per chip from configfiles " + "and then burn them (ex. CUSTOM_MAC, UNIQUE_ID).", + type=int, + ) + execute_scripts.add_argument( + "--configfiles", + help="List of configfiles with data", + nargs="?", + action="append", + type=argparse.FileType("r"), + ) + + check_error_cmd = subparsers.add_parser("check_error", help="Checks eFuse errors") + check_error_cmd.add_argument( + "--recovery", + help="Recovery of BLOCKs after encoding errors", + action="store_true", + ) + + +def add_force_write_always(p): + p.add_argument( + "--force-write-always", + help="Write the efuse even if it looks like it's already been written, " + "or is write protected. Note that this option can't disable write protection, " + "or clear any bit which has already been set.", + action="store_true", + ) + + +def add_show_sensitive_info_option(p): + p.add_argument( + "--show-sensitive-info", + help="Show data to be burned (may expose sensitive data). " + "Enabled if --debug is used.", + action="store_true", + default=False, + ) + + +def summary(esp, efuses, args): + """Print a human-readable summary of efuse contents""" + ROW_FORMAT = "%-50s %-50s%s = %s %s %s" + human_output = args.format == "summary" + json_efuse = {} + if args.file != sys.stdout: + print("Saving efuse values to " + args.file.name) + if human_output: + print( + ROW_FORMAT.replace("-50", "-12") + % ( + "EFUSE_NAME (Block)", + "Description", + "", + "[Meaningful Value]", + "[Readable/Writeable]", + "(Hex Value)", + ), + file=args.file, + ) + print("-" * 88, file=args.file) + for category in sorted(set(e.category for e in efuses), key=lambda c: c.title()): + if human_output: + print("%s fuses:" % category.title(), file=args.file) + for e in (e for e in efuses if e.category == category): + if e.efuse_type.startswith("bytes"): + raw = "" + else: + raw = "({})".format(e.get_bitstring()) + (readable, writeable) = (e.is_readable(), e.is_writeable()) + if readable and writeable: + perms = "R/W" + elif readable: + perms = "R/-" + elif writeable: + perms = "-/W" + else: + perms = "-/-" + base_value = e.get_meaning() + value = str(base_value) + if not readable: + count_read_disable_bits = e.get_count_read_disable_bits() + if count_read_disable_bits == 2: + # On the C2 chip, BLOCK_KEY0 has two read protection bits [0, 1] + # related to the lower and higher part of the block. + v = [value[: (len(value) // 2)], value[(len(value) // 2) :]] + for i in range(count_read_disable_bits): + if not e.is_readable(blk_part=i): + v[i] = v[i].replace("0", "?") + value = "".join(v) + else: + value = value.replace("0", "?") + if human_output: + print( + ROW_FORMAT + % ( + e.get_info(), + e.description[:50], + "\n " if len(value) > 20 else "", + value, + perms, + raw, + ), + file=args.file, + ) + desc_len = len(e.description[50:]) + if desc_len: + desc_len += 50 + for i in range(50, desc_len, 50): + print( + "%-50s %-50s" % ("", e.description[i : (50 + i)]), + file=args.file, + ) + if args.format == "json": + json_efuse[e.name] = { + "name": e.name, + "value": base_value if readable else value, + "readable": readable, + "writeable": writeable, + "description": e.description, + "category": e.category, + "block": e.block, + "word": e.word, + "pos": e.pos, + "efuse_type": e.efuse_type, + "bit_len": e.bit_len, + } + if human_output: + print("", file=args.file) + if human_output: + print(efuses.summary(), file=args.file) + warnings = efuses.get_coding_scheme_warnings() + if warnings: + print( + "WARNING: Coding scheme has encoding bit error warnings", file=args.file + ) + if args.file != sys.stdout: + args.file.close() + print("Done") + if args.format == "json": + json.dump(json_efuse, args.file, sort_keys=True, indent=4) + print("") + + +def dump(esp, efuses, args): + """Dump raw efuse data registers""" + # Using --debug option allows to print dump. + # Nothing to do here. The log will be printed + # during EspEfuses.__init__() in self.read_blocks() + if args.file_name: + # save dump to the file + for block in efuses.blocks: + file_dump_name = args.file_name + place_for_index = file_dump_name.find(".bin") + file_dump_name = ( + file_dump_name[:place_for_index] + + str(block.id) + + file_dump_name[place_for_index:] + ) + print(file_dump_name) + with open(file_dump_name, "wb") as f: + block.get_bitstring().byteswap() + block.get_bitstring().tofile(f) + + +def burn_efuse(esp, efuses, args): + def print_attention(blocked_efuses_after_burn): + if len(blocked_efuses_after_burn): + print( + " ATTENTION! This BLOCK uses NOT the NONE coding scheme " + "and after 'BURN', these efuses can not be burned in the feature:" + ) + for i in range(0, len(blocked_efuses_after_burn), 5): + print( + " ", + "".join("{}".format(blocked_efuses_after_burn[i : i + 5 :])), + ) + + efuse_name_list = [name for name in args.name_value_pairs.keys()] + burn_efuses_list = [efuses[name] for name in efuse_name_list] + old_value_list = [efuses[name].get_raw() for name in efuse_name_list] + new_value_list = [value for value in args.name_value_pairs.values()] + util.check_duplicate_name_in_list(efuse_name_list) + + attention = "" + print("The efuses to burn:") + for block in efuses.blocks: + burn_list_a_block = [e for e in burn_efuses_list if e.block == block.id] + if len(burn_list_a_block): + print(" from BLOCK%d" % (block.id)) + for field in burn_list_a_block: + print(" - %s" % (field.name)) + if ( + efuses.blocks[field.block].get_coding_scheme() + != efuses.REGS.CODING_SCHEME_NONE + ): + using_the_same_block_names = [ + e.name for e in efuses if e.block == field.block + ] + wr_names = [e.name for e in burn_list_a_block] + blocked_efuses_after_burn = [ + name + for name in using_the_same_block_names + if name not in wr_names + ] + attention = " (see 'ATTENTION!' above)" + if attention: + print_attention(blocked_efuses_after_burn) + + print("\nBurning efuses{}:".format(attention)) + for efuse, new_value in zip(burn_efuses_list, new_value_list): + print( + "\n - '{}' ({}) {} -> {}".format( + efuse.name, + efuse.description, + efuse.get_bitstring(), + efuse.convert_to_bitstring(new_value), + ) + ) + efuse.save(new_value) + + print() + if "ENABLE_SECURITY_DOWNLOAD" in efuse_name_list: + print( + "ENABLE_SECURITY_DOWNLOAD -> 1: eFuses will not be read back " + "for confirmation because this mode disables " + "any SRAM and register operations." + ) + print(" espefuse will not work.") + print(" esptool can read/write only flash.") + + if "DIS_DOWNLOAD_MODE" in efuse_name_list: + print( + "DIS_DOWNLOAD_MODE -> 1: eFuses will not be read back for " + "confirmation because this mode disables any communication with the chip." + ) + print( + " espefuse/esptool will not work because " + "they will not be able to connect to the chip." + ) + + if ( + esp.CHIP_NAME == "ESP32" + and esp.get_chip_revision() >= 300 + and "UART_DOWNLOAD_DIS" in efuse_name_list + ): + print( + "UART_DOWNLOAD_DIS -> 1: eFuses will be read for confirmation, " + "but after that connection to the chip will become impossible." + ) + print(" espefuse/esptool will not work.") + + if not efuses.burn_all(check_batch_mode=True): + return + + print("Checking efuses...") + raise_error = False + for efuse, old_value, new_value in zip( + burn_efuses_list, old_value_list, new_value_list + ): + if not efuse.is_readable(): + print( + "Efuse %s is read-protected. Read back the burn value is not possible." + % efuse.name + ) + else: + new_value = efuse.convert_to_bitstring(new_value) + burned_value = efuse.get_bitstring() + if burned_value != new_value: + print( + burned_value, + "->", + new_value, + "Efuse %s failed to burn. Protected?" % efuse.name, + ) + raise_error = True + if raise_error: + raise esptool.FatalError("The burn was not successful.") + else: + print("Successful") + + +def read_protect_efuse(esp, efuses, args): + util.check_duplicate_name_in_list(args.efuse_name) + + for efuse_name in args.efuse_name: + efuse = efuses[efuse_name] + if not efuse.is_readable(): + print("Efuse %s is already read protected" % efuse.name) + else: + if esp.CHIP_NAME == "ESP32": + if ( + efuse_name == "BLOCK2" + and not efuses["ABS_DONE_0"].get() + and esp.get_chip_revision() >= 300 + ): + if efuses["ABS_DONE_1"].get(): + raise esptool.FatalError( + "Secure Boot V2 is on (ABS_DONE_1 = True), " + "BLOCK2 must be readable, stop this operation!" + ) + else: + print( + "If Secure Boot V2 is used, BLOCK2 must be readable, " + "please stop this operation!" + ) + elif esp.CHIP_NAME == "ESP32-C2": + error = ( + not efuses["XTS_KEY_LENGTH_256"].get() + and efuse_name == "BLOCK_KEY0" + ) + error |= efuses["SECURE_BOOT_EN"].get() and efuse_name in [ + "BLOCK_KEY0", + "BLOCK_KEY0_HI_128", + ] + if error: + raise esptool.FatalError( + "%s must be readable, stop this operation!" % efuse_name + ) + else: + for block in efuses.Blocks.BLOCKS: + block = efuses.Blocks.get(block) + if block.name == efuse_name and block.key_purpose is not None: + if not efuses[block.key_purpose].need_rd_protect( + efuses[block.key_purpose].get() + ): + raise esptool.FatalError( + "%s must be readable, stop this operation!" % efuse_name + ) + break + # make full list of which efuses will be disabled + # (ie share a read disable bit) + all_disabling = [ + e for e in efuses if e.read_disable_bit == efuse.read_disable_bit + ] + names = ", ".join(e.name for e in all_disabling) + print( + "Permanently read-disabling efuse%s %s" + % ("s" if len(all_disabling) > 1 else "", names) + ) + efuse.disable_read() + + if not efuses.burn_all(check_batch_mode=True): + return + + print("Checking efuses...") + raise_error = False + for efuse_name in args.efuse_name: + efuse = efuses[efuse_name] + if efuse.is_readable(): + print("Efuse %s is not read-protected." % efuse.name) + raise_error = True + if raise_error: + raise esptool.FatalError("The burn was not successful.") + else: + print("Successful") + + +def write_protect_efuse(esp, efuses, args): + util.check_duplicate_name_in_list(args.efuse_name) + for efuse_name in args.efuse_name: + efuse = efuses[efuse_name] + if not efuse.is_writeable(): + print("Efuse %s is already write protected" % efuse.name) + else: + # make full list of which efuses will be disabled + # (ie share a write disable bit) + all_disabling = [ + e for e in efuses if e.write_disable_bit == efuse.write_disable_bit + ] + names = ", ".join(e.name for e in all_disabling) + print( + "Permanently write-disabling efuse%s %s" + % ("s" if len(all_disabling) > 1 else "", names) + ) + efuse.disable_write() + + if not efuses.burn_all(check_batch_mode=True): + return + + print("Checking efuses...") + raise_error = False + for efuse_name in args.efuse_name: + efuse = efuses[efuse_name] + if efuse.is_writeable(): + print("Efuse %s is not write-protected." % efuse.name) + raise_error = True + if raise_error: + raise esptool.FatalError("The burn was not successful.") + else: + print("Successful") + + +def burn_block_data(esp, efuses, args): + block_name_list = args.block[ + 0 : len([name for name in args.block if name is not None]) : + ] + datafile_list = args.datafile[ + 0 : len([name for name in args.datafile if name is not None]) : + ] + efuses.force_write_always = args.force_write_always + + util.check_duplicate_name_in_list(block_name_list) + if args.offset and len(block_name_list) > 1: + raise esptool.FatalError( + "The 'offset' option is not applicable when a few blocks are passed. " + "With 'offset', should only one block be used." + ) + else: + offset = args.offset + if offset: + num_block = efuses.get_index_block_by_name(block_name_list[0]) + block = efuses.blocks[num_block] + num_bytes = block.get_block_len() + if offset >= num_bytes: + raise esptool.FatalError( + "Invalid offset: the block%d only holds %d bytes." + % (block.id, num_bytes) + ) + if len(block_name_list) != len(datafile_list): + raise esptool.FatalError( + "The number of block_name (%d) and datafile (%d) should be the same." + % (len(block_name_list), len(datafile_list)) + ) + + for block_name, datafile in zip(block_name_list, datafile_list): + num_block = efuses.get_index_block_by_name(block_name) + block = efuses.blocks[num_block] + data = datafile.read() + num_bytes = block.get_block_len() + if offset != 0: + data = (b"\x00" * offset) + data + data = data + (b"\x00" * (num_bytes - len(data))) + if len(data) != num_bytes: + raise esptool.FatalError( + "Data does not fit: the block%d size is %d bytes, " + "data file is %d bytes, offset %d" + % (block.id, num_bytes, len(data), offset) + ) + print( + "[{:02}] {:20} size={:02} bytes, offset={:02} - > [{}].".format( + block.id, block.name, len(data), offset, util.hexify(data, " ") + ) + ) + block.save(data) + + if not efuses.burn_all(check_batch_mode=True): + return + print("Successful") + + +def burn_bit(esp, efuses, args): + efuses.force_write_always = args.force_write_always + num_block = efuses.get_index_block_by_name(args.block) + block = efuses.blocks[num_block] + data_block = BitStream(block.get_block_len() * 8) + data_block.set(0) + try: + data_block.set(True, args.bit_number) + except IndexError: + raise esptool.FatalError( + "%s has bit_number in [0..%d]" % (args.block, data_block.len - 1) + ) + data_block.reverse() + print( + "bit_number: " + "[%-03d]........................................................[0]" + % (data_block.len - 1) + ) + print("BLOCK%-2d :" % block.id, data_block) + block.print_block(data_block, "regs_to_write", debug=True) + block.save(data_block.bytes[::-1]) + + if not efuses.burn_all(check_batch_mode=True): + return + print("Successful") + + +def get_error_summary(efuses): + error_in_blocks = efuses.get_coding_scheme_warnings() + if not error_in_blocks: + return False + writable = True + for blk in efuses.blocks: + if blk.fail or blk.num_errors: + if blk.id == 0: + for field in efuses: + if field.block == blk.id and (field.fail or field.num_errors): + wr = "writable" if field.is_writeable() else "not writable" + writable &= wr == "writable" + name = field.name + val = field.get() + print(f"BLOCK{field.block:<2}: {name:<40} = {val:<8} ({wr})") + else: + wr = "writable" if blk.is_writeable() else "not writable" + writable &= wr == "writable" + name = f"{blk.name} [ERRORS:{blk.num_errors} FAIL:{int(blk.fail)}]" + val = str(blk.get_bitstring()) + print(f"BLOCK{blk.id:<2}: {name:<40} = {val:<8} ({wr})") + if not writable and error_in_blocks: + print("Not all errors can be fixed because some fields are write-protected!") + return True + + +def check_error(esp, efuses, args): + error_in_blocks = get_error_summary(efuses) + if args.recovery and error_in_blocks: + confirmed = False + for block in reversed(efuses.blocks): + if block.fail or block.num_errors > 0: + if not block.get_bitstring().all(False): + block.save(block.get_bitstring().bytes[::-1]) + if not confirmed: + confirmed = True + efuses.confirm( + "Recovery of block coding errors", args.do_not_confirm + ) + block.burn() + if confirmed: + efuses.update_efuses() + error_in_blocks = get_error_summary(efuses) + if error_in_blocks: + raise esptool.FatalError("Error(s) were detected in eFuses") + print("No errors detected") diff --git a/code/.venv/lib/python3.12/site-packages/espefuse/efuse/emulate_efuse_controller_base.py b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/emulate_efuse_controller_base.py new file mode 100644 index 0000000..232bfaa --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/emulate_efuse_controller_base.py @@ -0,0 +1,225 @@ +# This file describes eFuses controller for ESP32 chip +# +# SPDX-FileCopyrightText: 2020-2022 Espressif Systems (Shanghai) CO LTD +# +# SPDX-License-Identifier: GPL-2.0-or-later + +import re + +from bitstring import BitStream + + +class EmulateEfuseControllerBase(object): + """The class for virtual efuse operations. Using for HOST_TEST.""" + + CHIP_NAME = "" + mem = None + debug = False + Blocks = None + Fields = None + REGS = None + + def __init__(self, efuse_file=None, debug=False): + self.debug = debug + self.efuse_file = efuse_file + if self.efuse_file: + try: + self.mem = BitStream( + bytes=open(self.efuse_file, "rb").read(), + length=self.REGS.EFUSE_MEM_SIZE * 8, + ) + except (ValueError, FileNotFoundError): + # the file is empty or does not fit the length. + self.mem = BitStream(length=self.REGS.EFUSE_MEM_SIZE * 8) + self.mem.set(0) + self.mem.tofile(open(self.efuse_file, "a+b")) + else: + # efuse_file is not provided + # it means we do not want to keep the result of efuse operations + self.mem = BitStream(self.REGS.EFUSE_MEM_SIZE * 8) + self.mem.set(0) + + """ esptool method start >> """ + + def get_chip_description(self): + major_rev = self.get_major_chip_version() + minor_rev = self.get_minor_chip_version() + return f"{self.CHIP_NAME} (revision v{major_rev}.{minor_rev})" + + def get_chip_revision(self): + return self.get_major_chip_version() * 100 + self.get_minor_chip_version() + + def read_efuse(self, n, block=0): + """Read the nth word of the ESP3x EFUSE region.""" + blk = self.Blocks.get(self.Blocks.BLOCKS[block]) + return self.read_reg(blk.rd_addr + (4 * n)) + + def read_reg(self, addr): + self.mem.pos = self.mem.length - ((addr - self.REGS.DR_REG_EFUSE_BASE) * 8 + 32) + return self.mem.read("uint:32") + + def write_reg(self, addr, value, mask=0xFFFFFFFF, delay_us=0, delay_after_us=0): + self.mem.pos = self.mem.length - ((addr - self.REGS.DR_REG_EFUSE_BASE) * 8 + 32) + self.mem.overwrite("uint:32={}".format(value & mask)) + self.handle_writing_event(addr, value) + + def update_reg(self, addr, mask, new_val): + position = self.mem.length - ((addr - self.REGS.DR_REG_EFUSE_BASE) * 8 + 32) + self.mem.pos = position + cur_val = self.mem.read("uint:32") + self.mem.pos = position + self.mem.overwrite("uint:32={}".format(cur_val | (new_val & mask))) + + def write_efuse(self, n, value, block=0): + """Write the nth word of the ESP3x EFUSE region.""" + blk = self.Blocks.get(self.Blocks.BLOCKS[block]) + self.write_reg(blk.wr_addr + (4 * n), value) + + """ << esptool method end """ + + def handle_writing_event(self, addr, value): + self.save_to_file() + + def save_to_file(self): + if self.efuse_file: + with open(self.efuse_file, "wb") as f: + self.mem.tofile(f) + + def handle_coding_scheme(self, blk, data): + return data + + def copy_blocks_wr_regs_to_rd_regs(self, updated_block=None): + for b in reversed(self.Blocks.BLOCKS): + blk = self.Blocks.get(b) + if updated_block is not None: + if blk.id != updated_block: + continue + data = self.read_block(blk.id, wr_regs=True) + if self.debug: + print(blk.name, data.hex) + plain_data = self.handle_coding_scheme(blk, data) + plain_data = self.check_wr_protection_area(blk.id, plain_data) + self.update_block(blk, plain_data) + + def clean_blocks_wr_regs(self): + for b in self.Blocks.BLOCKS: + blk = self.Blocks.get(b) + for offset in range(0, blk.len * 4, 4): + wr_addr = blk.wr_addr + offset + self.write_reg(wr_addr, 0) + + def read_field(self, name, bitstring=True): + for field in self.Fields.EFUSES: + if field.name == name: + self.read_block(field.block) + block = self.read_block(field.block) + if field.type.startswith("bool"): + field_len = 1 + else: + field_len = int(re.search(r"\d+", field.type).group()) + if field.type.startswith("bytes"): + field_len *= 8 + block.pos = block.length - (field.word * 32 + field.pos + field_len) + if bitstring: + return block.read(field_len) + else: + return block.read(field.type) + return None + + def get_bitlen_of_block(self, blk, wr=False): + return 32 * blk.len + + def read_block(self, idx, wr_regs=False): + block = None + for b in self.Blocks.BLOCKS: + blk = self.Blocks.get(b) + if blk.id == idx: + blk_len_bits = self.get_bitlen_of_block(blk, wr=wr_regs) + addr = blk.wr_addr if wr_regs else blk.rd_addr + self.mem.pos = self.mem.length - ( + (addr - self.REGS.DR_REG_EFUSE_BASE) * 8 + blk_len_bits + ) + block = self.mem.read(blk_len_bits) + break + return block + + def update_block(self, blk, wr_data): + wr_data = self.read_block(blk.id) | wr_data + self.overwrite_mem_from_block(blk, wr_data) + + def overwrite_mem_from_block(self, blk, wr_data): + self.mem.pos = self.mem.length - ( + (blk.rd_addr - self.REGS.DR_REG_EFUSE_BASE) * 8 + wr_data.len + ) + self.mem.overwrite(wr_data) + + def check_wr_protection_area(self, num_blk, wr_data): + # checks fields which have the write protection bit. + # if the write protection bit is set, we need to protect that area from changes. + write_disable_bit = self.read_field("WR_DIS", bitstring=False) + mask_wr_data = BitStream(len(wr_data)) + mask_wr_data.set(0) + blk = self.Blocks.get(self.Blocks.BLOCKS[num_blk]) + if blk.write_disable_bit is not None and write_disable_bit & ( + 1 << blk.write_disable_bit + ): + mask_wr_data.set(1) + else: + for field in self.Fields.EFUSES: + if blk.id == field.block and field.block == num_blk: + if field.write_disable_bit is not None and write_disable_bit & ( + 1 << field.write_disable_bit + ): + data = self.read_field(field.name) + data.set(1) + mask_wr_data.pos = mask_wr_data.length - ( + field.word * 32 + field.pos + data.len + ) + mask_wr_data.overwrite(data) + mask_wr_data.invert() + return wr_data & mask_wr_data + + def check_rd_protection_area(self): + # checks fields which have the read protection bits. + # if the read protection bit is set then we need to reset this field to 0. + read_disable_bit = self.read_field("RD_DIS", bitstring=False) + for b in self.Blocks.BLOCKS: + blk = self.Blocks.get(b) + block = self.read_block(blk.id) + if blk.read_disable_bit is not None and read_disable_bit & ( + 1 << blk.read_disable_bit + ): + block.set(0) + else: + for field in self.Fields.EFUSES: + if ( + blk.id == field.block + and field.read_disable_bit is not None + and read_disable_bit & (1 << field.read_disable_bit) + ): + raw_data = self.read_field(field.name) + raw_data.set(0) + block.pos = block.length - ( + field.word * 32 + field.pos + raw_data.length + ) + block.overwrite(BitStream(raw_data.length)) + self.overwrite_mem_from_block(blk, block) + + def clean_mem(self): + self.mem.set(0) + if self.efuse_file: + with open(self.efuse_file, "wb") as f: + self.mem.tofile(f) + + +class FatalError(RuntimeError): + """ + Wrapper class for runtime errors that aren't caused by internal bugs + """ + + def __init__(self, message): + RuntimeError.__init__(self, message) + + @staticmethod + def WithResult(message, result): + return FatalError(result) diff --git a/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32/__init__.py b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32/__init__.py new file mode 100644 index 0000000..a3b55a8 --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32/__init__.py @@ -0,0 +1,3 @@ +from . import operations +from .emulate_efuse_controller import EmulateEfuseController +from .fields import EspEfuses diff --git a/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32/__pycache__/__init__.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..c5bee1f Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32/__pycache__/__init__.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32/__pycache__/emulate_efuse_controller.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32/__pycache__/emulate_efuse_controller.cpython-312.pyc new file mode 100644 index 0000000..20e1415 Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32/__pycache__/emulate_efuse_controller.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32/__pycache__/fields.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32/__pycache__/fields.cpython-312.pyc new file mode 100644 index 0000000..0756935 Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32/__pycache__/fields.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32/__pycache__/mem_definition.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32/__pycache__/mem_definition.cpython-312.pyc new file mode 100644 index 0000000..48de294 Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32/__pycache__/mem_definition.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32/__pycache__/operations.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32/__pycache__/operations.cpython-312.pyc new file mode 100644 index 0000000..2d538b9 Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32/__pycache__/operations.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32/emulate_efuse_controller.py b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32/emulate_efuse_controller.py new file mode 100644 index 0000000..da7cd44 --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32/emulate_efuse_controller.py @@ -0,0 +1,141 @@ +# This file describes eFuses controller for ESP32 chip +# +# SPDX-FileCopyrightText: 2020-2022 Espressif Systems (Shanghai) CO LTD +# +# SPDX-License-Identifier: GPL-2.0-or-later + +import time + +from .mem_definition import EfuseDefineBlocks, EfuseDefineFields, EfuseDefineRegisters +from ..emulate_efuse_controller_base import EmulateEfuseControllerBase, FatalError + + +class EmulateEfuseController(EmulateEfuseControllerBase): + """The class for virtual efuse operations. Using for HOST_TEST.""" + + CHIP_NAME = "ESP32" + mem = None + debug = False + + def __init__(self, efuse_file=None, debug=False): + self.Blocks = EfuseDefineBlocks + self.Fields = EfuseDefineFields() + self.REGS = EfuseDefineRegisters + super(EmulateEfuseController, self).__init__(efuse_file, debug) + + """ esptool method start >> """ + + def get_major_chip_version(self): + return 3 + + def get_minor_chip_version(self): + return 0 + + def get_crystal_freq(self): + return 40 # MHz (common for all chips) + + def read_reg(self, addr): + if addr == self.REGS.APB_CTL_DATE_ADDR: + return self.REGS.APB_CTL_DATE_V << self.REGS.APB_CTL_DATE_S + else: + val = 0 + if addr == self.REGS.EFUSE_BLK0_RDATA3_REG: + val = self.REGS.EFUSE_RD_CHIP_VER_REV1 + if addr == self.REGS.EFUSE_BLK0_RDATA5_REG: + val = self.REGS.EFUSE_RD_CHIP_VER_REV2 + return val | super(EmulateEfuseController, self).read_reg(addr) + + """ << esptool method end """ + + def send_burn_cmd(self): + def wait_idle(): + deadline = time.time() + self.REGS.EFUSE_BURN_TIMEOUT + while time.time() < deadline: + if self.read_reg(self.REGS.EFUSE_REG_CMD) == 0: + return + raise FatalError( + "Timed out waiting for Efuse controller command to complete" + ) + + self.write_reg(self.REGS.EFUSE_REG_CMD, self.REGS.EFUSE_CMD_WRITE) + wait_idle() + self.write_reg(self.REGS.EFUSE_REG_CONF, self.REGS.EFUSE_CONF_READ) + self.write_reg(self.REGS.EFUSE_REG_CMD, self.REGS.EFUSE_CMD_READ) + wait_idle() + + def handle_writing_event(self, addr, value): + if addr == self.REGS.EFUSE_REG_CMD: + if value == self.REGS.EFUSE_CMD_WRITE: + self.write_reg(addr, 0) + elif value == self.REGS.EFUSE_CMD_READ: + self.copy_blocks_wr_regs_to_rd_regs() + self.clean_blocks_wr_regs() + self.check_rd_protection_area() + self.write_reg(addr, 0) + self.save_to_file() + + def read_raw_coding_scheme(self): + return ( + self.read_efuse(self.REGS.EFUSE_CODING_SCHEME_WORD) + & self.REGS.EFUSE_CODING_SCHEME_MASK + ) + + def write_raw_coding_scheme(self, value): + self.write_efuse( + self.REGS.EFUSE_CODING_SCHEME_WORD, + value & self.REGS.EFUSE_CODING_SCHEME_MASK, + ) + self.send_burn_cmd() + if value != self.read_raw_coding_scheme(): + raise FatalError( + "Error during a burning process to set the new coding scheme" + ) + print("Set coding scheme = %d" % self.read_raw_coding_scheme()) + + def get_bitlen_of_block(self, blk, wr=False): + if blk.id == 0: + return 32 * blk.len + else: + coding_scheme = self.read_raw_coding_scheme() + if coding_scheme == self.REGS.CODING_SCHEME_NONE: + return 32 * blk.len + elif coding_scheme == self.REGS.CODING_SCHEME_34: + if wr: + return 32 * 8 + else: + return 32 * blk.len * 3 // 4 + else: + raise FatalError( + "The {} coding scheme is not supported".format(coding_scheme) + ) + + def handle_coding_scheme(self, blk, data): + # it verifies the coding scheme part of data and returns just data + if blk.id != 0 and self.read_raw_coding_scheme() == self.REGS.CODING_SCHEME_34: + # CODING_SCHEME 3/4 applied only for BLK1..3 + # Takes 24 byte sequence to be represented in 3/4 encoding, + # returns 8 words suitable for writing "encoded" to an efuse block + data.pos = 0 + for _ in range(0, 4): + xor_res = 0 + mul_res = 0 + chunk_data = data.readlist("8*uint:8") + chunk_data = chunk_data[::-1] + for i in range(0, 6): + byte_data = chunk_data[i] + xor_res ^= byte_data + mul_res += (i + 1) * bin(byte_data).count("1") + if xor_res != chunk_data[6] or mul_res != chunk_data[7]: + print( + "xor_res ", + xor_res, + chunk_data[6], + "mul_res", + mul_res, + chunk_data[7], + ) + raise FatalError("Error in coding scheme data") + # cut the coded data + for i in range(0, 4): + del data[i * 6 * 8 : (i * 6 * 8) + 16] + return data diff --git a/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32/fields.py b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32/fields.py new file mode 100644 index 0000000..7ca5fc3 --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32/fields.py @@ -0,0 +1,466 @@ +# This file describes eFuses for ESP32 chip +# +# SPDX-FileCopyrightText: 2020-2022 Espressif Systems (Shanghai) CO LTD +# +# SPDX-License-Identifier: GPL-2.0-or-later + +import binascii +import struct +import time + +import esptool + +from .mem_definition import EfuseDefineBlocks, EfuseDefineFields, EfuseDefineRegisters +from .. import base_fields +from .. import util + + +class EfuseBlock(base_fields.EfuseBlockBase): + def len_of_burn_unit(self): + # The writing register window is the same as len of a block. + return self.len + + def __init__(self, parent, param, skip_read=False): + if skip_read: + parent.coding_scheme = parent.REGS.CODING_SCHEME_NONE + else: + if parent.coding_scheme is None: + parent.read_coding_scheme() + super(EfuseBlock, self).__init__(parent, param, skip_read=skip_read) + + def apply_coding_scheme(self): + data = self.get_raw(from_read=False)[::-1] + if self.get_coding_scheme() == self.parent.REGS.CODING_SCHEME_34: + # CODING_SCHEME 3/4 applied only for BLK1..3 + # Takes 24 byte sequence to be represented in 3/4 encoding, + # returns 8 words suitable for writing "encoded" to an efuse block + if len(data) != 24: + raise esptool.FatalError("Should take 24 bytes for 3/4 encoding.") + data = data[:24] + outbits = b"" + while len(data) > 0: # process in chunks of 6 bytes + bits = data[0:6] + data = data[6:] + xor_res = 0 + mul_res = 0 + index = 1 + for b in struct.unpack("B" * 6, bits): + xor_res ^= b + mul_res += index * util.popcnt(b) + index += 1 + outbits += bits + outbits += struct.pack("BB", xor_res, mul_res) + words = struct.unpack("<" + "I" * (len(outbits) // 4), outbits) + # returns 8 words + else: + # CODING_SCHEME NONE applied for BLK0 and BLK1..3 + # BLK0 len = 7 words, BLK1..3 len = 8 words. + words = struct.unpack("<" + ("I" * (len(data) // 4)), data) + # returns 7 words for BLK0 or 8 words for BLK1..3 + return words + + +class EspEfuses(base_fields.EspEfusesBase): + """ + Wrapper object to manage the efuse fields in a connected ESP bootloader + """ + + debug = False + do_not_confirm = False + + def __init__(self, esp, skip_connect=False, debug=False, do_not_confirm=False): + self.Blocks = EfuseDefineBlocks() + self.Fields = EfuseDefineFields() + self.REGS = EfuseDefineRegisters + self.BURN_BLOCK_DATA_NAMES = self.Blocks.get_burn_block_data_names() + self.BLOCKS_FOR_KEYS = self.Blocks.get_blocks_for_keys() + self._esp = esp + self.debug = debug + self.do_not_confirm = do_not_confirm + if esp.CHIP_NAME != "ESP32": + raise esptool.FatalError( + "Expected the 'esp' param for ESP32 chip but got for '%s'." + % (esp.CHIP_NAME) + ) + self.blocks = [ + EfuseBlock(self, self.Blocks.get(block), skip_read=skip_connect) + for block in self.Blocks.BLOCKS + ] + if not skip_connect: + self.get_coding_scheme_warnings() + self.efuses = [EfuseField.convert(self, efuse) for efuse in self.Fields.EFUSES] + if skip_connect: + self.efuses += [ + EfuseField.convert(self, efuse) for efuse in self.Fields.KEYBLOCKS_256 + ] + self.efuses += [ + EfuseField.convert(self, efuse) for efuse in self.Fields.CUSTOM_MAC + ] + self.efuses += [ + EfuseField.convert(self, efuse) for efuse in self.Fields.ADC_CALIBRATION + ] + else: + if self.coding_scheme == self.REGS.CODING_SCHEME_NONE: + self.efuses += [ + EfuseField.convert(self, efuse) + for efuse in self.Fields.KEYBLOCKS_256 + ] + elif self.coding_scheme == self.REGS.CODING_SCHEME_34: + self.efuses += [ + EfuseField.convert(self, efuse) + for efuse in self.Fields.KEYBLOCKS_192 + ] + else: + raise esptool.FatalError( + "The coding scheme (%d) - is not supported" % self.coding_scheme + ) + if self["MAC_VERSION"].get() == 1: + self.efuses += [ + EfuseField.convert(self, efuse) for efuse in self.Fields.CUSTOM_MAC + ] + if self["BLK3_PART_RESERVE"].get(): + self.efuses += [ + EfuseField.convert(self, efuse) + for efuse in self.Fields.ADC_CALIBRATION + ] + self.efuses += [ + EfuseField.convert(self, efuse) for efuse in self.Fields.CALC + ] + + def __getitem__(self, efuse_name): + """Return the efuse field with the given name""" + for e in self.efuses: + if efuse_name == e.name or any(x == efuse_name for x in e.alt_names): + return e + new_fields = False + for efuse in self.Fields.CUSTOM_MAC: + if efuse.name == efuse_name or any( + x == efuse_name for x in efuse.alt_names + ): + self.efuses += [ + EfuseField.convert(self, efuse) for efuse in self.Fields.CUSTOM_MAC + ] + new_fields = True + for efuse in self.Fields.ADC_CALIBRATION: + if efuse.name == efuse_name or any( + x == efuse_name for x in efuse.alt_names + ): + self.efuses += [ + EfuseField.convert(self, efuse) + for efuse in self.Fields.ADC_CALIBRATION + ] + new_fields = True + if new_fields: + for e in self.efuses: + if efuse_name == e.name or any(x == efuse_name for x in e.alt_names): + return e + raise KeyError + + def read_coding_scheme(self): + self.coding_scheme = ( + self.read_efuse(self.REGS.EFUSE_CODING_SCHEME_WORD) + & self.REGS.EFUSE_CODING_SCHEME_MASK + ) + + def print_status_regs(self): + print("") + print( + "{:27} 0x{:08x}".format( + "EFUSE_REG_DEC_STATUS", self.read_reg(self.REGS.EFUSE_REG_DEC_STATUS) + ) + ) + + def write_efuses(self, block): + """Write the values in the efuse write registers to + the efuse hardware, then refresh the efuse read registers. + """ + + # Configure clock + apb_freq = self.get_crystal_freq() + clk_sel0, clk_sel1, dac_clk_div = self.REGS.EFUSE_CLK_SETTINGS[apb_freq] + + self.update_reg( + self.REGS.EFUSE_DAC_CONF_REG, self.REGS.EFUSE_DAC_CLK_DIV_MASK, dac_clk_div + ) + self.update_reg( + self.REGS.EFUSE_CLK_REG, self.REGS.EFUSE_CLK_SEL0_MASK, clk_sel0 + ) + self.update_reg( + self.REGS.EFUSE_CLK_REG, self.REGS.EFUSE_CLK_SEL1_MASK, clk_sel1 + ) + + self.write_reg(self.REGS.EFUSE_REG_CONF, self.REGS.EFUSE_CONF_WRITE) + self.write_reg(self.REGS.EFUSE_REG_CMD, self.REGS.EFUSE_CMD_WRITE) + + self.efuse_read() + return self.get_coding_scheme_warnings(silent=True) + + def wait_efuse_idle(self): + deadline = time.time() + self.REGS.EFUSE_BURN_TIMEOUT + while time.time() < deadline: + if self.read_reg(self.REGS.EFUSE_REG_CMD) == 0: + return + raise esptool.FatalError( + "Timed out waiting for Efuse controller command to complete" + ) + + def efuse_read(self): + self.wait_efuse_idle() + self.write_reg(self.REGS.EFUSE_REG_CONF, self.REGS.EFUSE_CONF_READ) + self.write_reg(self.REGS.EFUSE_REG_CMD, self.REGS.EFUSE_CMD_READ) + self.wait_efuse_idle() + + def get_coding_scheme_warnings(self, silent=False): + """Check if the coding scheme has detected any errors. + Meaningless for default coding scheme (0) + """ + err = ( + self.read_reg(self.REGS.EFUSE_REG_DEC_STATUS) + & self.REGS.EFUSE_REG_DEC_STATUS_MASK + ) + for block in self.blocks: + if block.id != 0: + block.num_errors = 0 + block.fail = err != 0 + if not silent and block.fail: + print( + "Error(s) in BLOCK%d [ERRORS:%d FAIL:%d]" + % (block.id, block.num_errors, block.fail) + ) + if (self.debug or err) and not silent: + self.print_status_regs() + return err != 0 + + def summary(self): + if self["XPD_SDIO_FORCE"].get() == 0: + output = "Flash voltage (VDD_SDIO) determined by GPIO12 on reset (High for 1.8V, Low/NC for 3.3V)" + elif self["XPD_SDIO_REG"].get() == 0: + output = "Flash voltage (VDD_SDIO) internal regulator disabled by efuse." + elif self["XPD_SDIO_TIEH"].get() == 0: + output = "Flash voltage (VDD_SDIO) set to 1.8V by efuse." + else: + output = "Flash voltage (VDD_SDIO) set to 3.3V by efuse." + return output + + +class EfuseField(base_fields.EfuseFieldBase): + @staticmethod + def convert(parent, efuse): + return { + "mac": EfuseMacField, + "spipin": EfuseSpiPinField, + "vref": EfuseVRefField, + "adc_tp": EfuseAdcPointCalibration, + "wafer": EfuseWafer, + "pkg": EfusePkg, + }.get(efuse.class_type, EfuseField)(parent, efuse) + + +class EfuseMacField(EfuseField): + """ + Supports: MAC and CUSTOM_MAC fields. + (if MAC_VERSION == 1 then the CUSTOM_MAC is used) + """ + + def check_format(self, new_value_str): + if new_value_str is None: + raise esptool.FatalError( + "Required MAC Address in AA:CD:EF:01:02:03 format!" + ) + if new_value_str.count(":") != 5: + raise esptool.FatalError( + "MAC Address needs to be a 6-byte hexadecimal format " + "separated by colons (:)!" + ) + hexad = new_value_str.replace(":", "") + if len(hexad) != 12: + raise esptool.FatalError( + "MAC Address needs to be a 6-byte hexadecimal number " + "(12 hexadecimal characters)!" + ) + # order of bytearray = b'\xaa\xcd\xef\x01\x02\x03', + bindata = binascii.unhexlify(hexad) + # unicast address check according to + # https://tools.ietf.org/html/rfc7042#section-2.1 + if esptool.util.byte(bindata, 0) & 0x01: + raise esptool.FatalError("Custom MAC must be a unicast MAC!") + return bindata + + @staticmethod + def get_and_check(raw_mac, stored_crc): + computed_crc = EfuseMacField.calc_crc(raw_mac) + if computed_crc == stored_crc: + valid_msg = "(CRC 0x%02x OK)" % stored_crc + else: + valid_msg = "(CRC 0x%02x invalid - calculated 0x%02x)" % ( + stored_crc, + computed_crc, + ) + return "%s %s" % (util.hexify(raw_mac, ":"), valid_msg) + + @staticmethod + def calc_crc(raw_mac): + """ + This algorithm is the equivalent of esp_crc8() in ESP32 ROM code + + This is CRC-8 w/ inverted polynomial value 0x8C & initial value 0x00. + """ + result = 0x00 + for b in struct.unpack("B" * 6, raw_mac): + result ^= b + for _ in range(8): + lsb = result & 1 + result >>= 1 + if lsb != 0: + result ^= 0x8C + return result + + def get(self, from_read=True): + if self.name == "CUSTOM_MAC": + mac = self.get_raw(from_read)[::-1] + stored_crc = self.parent["CUSTOM_MAC_CRC"].get(from_read) + else: + mac = self.get_raw(from_read) + stored_crc = self.parent["MAC_CRC"].get(from_read) + return EfuseMacField.get_and_check(mac, stored_crc) + + def save(self, new_value): + def print_field(e, new_value): + print( + " - '{}' ({}) {} -> {}".format( + e.name, e.description, e.get_bitstring(), new_value + ) + ) + + if self.name == "CUSTOM_MAC": + # Writing the BLK3: + # - MAC_VERSION = 1 + # - CUSTOM_MAC = AB:CD:EF:01:02:03 + # - CUSTOM_MAC_CRC = crc8(CUSTOM_MAC) + mac_version = self.parent["MAC_VERSION"] + if mac_version.get() == 0: + mac_version_value = 1 + print_field(mac_version, hex(mac_version_value)) + mac_version.save(mac_version_value) + else: + if mac_version.get() != 1: + if not self.parent.force_write_always: + raise esptool.FatalError( + "MAC_VERSION = {}, should be 0 or 1.".format( + mac_version.get() + ) + ) + + bitarray_mac = self.convert_to_bitstring(new_value) + print_field(self, bitarray_mac) + super(EfuseMacField, self).save(new_value) + + crc_val = self.calc_crc(new_value) + crc_field = self.parent["CUSTOM_MAC_CRC"] + print_field(crc_field, hex(crc_val)) + crc_field.save(crc_val) + else: + # Writing the BLK0 default MAC is not possible, + # as it's written in the factory. + raise esptool.FatalError("Writing Factory MAC address is not supported") + + +class EfuseWafer(EfuseField): + def get(self, from_read=True): + rev_bit0 = self.parent["CHIP_VER_REV1"].get(from_read) + assert self.parent["CHIP_VER_REV1"].bit_len == 1 + rev_bit1 = self.parent["CHIP_VER_REV2"].get(from_read) + assert self.parent["CHIP_VER_REV2"].bit_len == 1 + apb_ctl_date = self.parent.read_reg(self.parent.REGS.APB_CTL_DATE_ADDR) + rev_bit2 = ( + apb_ctl_date >> self.parent.REGS.APB_CTL_DATE_S + ) & self.parent.REGS.APB_CTL_DATE_V + combine_value = (rev_bit2 << 2) | (rev_bit1 << 1) | rev_bit0 + + revision = { + 0: 0, + 1: 1, + 3: 2, + 7: 3, + }.get(combine_value, 0) + return revision + + def save(self, new_value): + raise esptool.FatalError("Burning %s is not supported" % self.name) + + +class EfusePkg(EfuseField): + def get(self, from_read=True): + lo_bits = self.parent["CHIP_PACKAGE"].get(from_read) + hi_bits = self.parent["CHIP_PACKAGE_4BIT"].get(from_read) + return (hi_bits << 3) + lo_bits + + def save(self, new_value): + raise esptool.FatalError("Burning %s is not supported" % self.name) + + +class EfuseSpiPinField(EfuseField): + def get(self, from_read=True): + val = self.get_raw(from_read) + if val >= 30: + val += 2 # values 30,31 map to 32, 33 + return val + + def check_format(self, new_value_str): + if new_value_str is None: + return new_value_str + + new_value_int = int(new_value_str, 0) + + if new_value_int in [30, 31]: + raise esptool.FatalError( + "IO pins 30 & 31 cannot be set for SPI flash. 0-29, 32 & 33 only." + ) + elif new_value_int > 33: + raise esptool.FatalError( + "IO pin %d cannot be set for SPI flash. 0-29, 32 & 33 only." + % new_value_int + ) + elif new_value_int in [32, 33]: + return str(new_value_int - 2) + else: + return new_value_str + + +class EfuseVRefField(EfuseField): + VREF_OFFSET = 1100 # ideal efuse value in mV + VREF_STEP_SIZE = 7 # 1 count in efuse == 7mV + VREF_SIGN_BIT = 0x10 + VREF_MAG_BITS = 0x0F + + def get(self, from_read=True): + val = self.get_raw(from_read) + # sign-magnitude format + if val & self.VREF_SIGN_BIT: + val = -(val & self.VREF_MAG_BITS) + else: + val = val & self.VREF_MAG_BITS + val *= self.VREF_STEP_SIZE + return self.VREF_OFFSET + val + + def save(self, new_value): + raise esptool.FatalError("Writing to VRef is not supported.") + + +class EfuseAdcPointCalibration(EfuseField): + TP_OFFSET = { # See TP_xxxx_OFFSET in esp_adc_cal.c in ESP-IDF + "ADC1_TP_LOW": 278, + "ADC2_TP_LOW": 421, + "ADC1_TP_HIGH": 3265, + "ADC2_TP_HIGH": 3406, + } + SIGN_BIT = (0x40, 0x100) # LOW, HIGH (2s complement format) + STEP_SIZE = 4 + + def get(self, from_read=True): + idx = 0 if self.name.endswith("LOW") else 1 + sign_bit = self.SIGN_BIT[idx] + offset = self.TP_OFFSET[self.name] + raw = self.get_raw() + delta = (raw & (sign_bit - 1)) - (raw & sign_bit) + return offset + (delta * self.STEP_SIZE) diff --git a/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32/mem_definition.py b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32/mem_definition.py new file mode 100644 index 0000000..7d29a01 --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32/mem_definition.py @@ -0,0 +1,179 @@ +# This file describes eFuses fields and registers for ESP32 chip +# +# SPDX-FileCopyrightText: 2020-2022 Espressif Systems (Shanghai) CO LTD +# +# SPDX-License-Identifier: GPL-2.0-or-later + +import copy +import os + +import yaml + +from ..mem_definition_base import ( + EfuseBlocksBase, + EfuseFieldsBase, + EfuseRegistersBase, + Field, +) + + +class EfuseDefineRegisters(EfuseRegistersBase): + EFUSE_MEM_SIZE = 0x011C + 4 + + # EFUSE registers & command/conf values + DR_REG_EFUSE_BASE = 0x3FF5A000 + EFUSE_REG_CONF = DR_REG_EFUSE_BASE + 0x0FC + EFUSE_CONF_WRITE = 0x5A5A + EFUSE_CONF_READ = 0x5AA5 + EFUSE_REG_CMD = DR_REG_EFUSE_BASE + 0x104 + EFUSE_CMD_OP_MASK = 0x3 + EFUSE_CMD_WRITE = 0x2 + EFUSE_CMD_READ = 0x1 + + # 3/4 Coding scheme warnings registers + EFUSE_REG_DEC_STATUS = DR_REG_EFUSE_BASE + 0x11C + EFUSE_REG_DEC_STATUS_MASK = 0xFFF + + # Coding Scheme + EFUSE_CODING_SCHEME_WORD = 6 + EFUSE_CODING_SCHEME_MASK = 0x3 + + # Efuse clock control + EFUSE_DAC_CONF_REG = DR_REG_EFUSE_BASE + 0x118 + EFUSE_CLK_REG = DR_REG_EFUSE_BASE + 0x0F8 + EFUSE_DAC_CLK_DIV_MASK = 0xFF + EFUSE_CLK_SEL0_MASK = 0x00FF + EFUSE_CLK_SEL1_MASK = 0xFF00 + + EFUSE_CLK_SETTINGS = { + # APB freq: clk_sel0, clk_sel1, dac_clk_div + # Taken from TRM chapter "eFuse Controller": Timing Configuration + # 80 is here for completeness only as esptool never sets an 80MHz APB clock + 26: (250, 255, 52), + 40: (160, 255, 80), + 80: (80, 128, 100), + } + + DR_REG_SYSCON_BASE = 0x3FF66000 + APB_CTL_DATE_ADDR = DR_REG_SYSCON_BASE + 0x7C + APB_CTL_DATE_V = 0x1 + APB_CTL_DATE_S = 31 + + EFUSE_BLK0_RDATA3_REG = DR_REG_EFUSE_BASE + 0x00C + EFUSE_RD_CHIP_VER_REV1 = 1 << 15 + + EFUSE_BLK0_RDATA5_REG = DR_REG_EFUSE_BASE + 0x014 + EFUSE_RD_CHIP_VER_REV2 = 1 << 20 + + +class EfuseDefineBlocks(EfuseBlocksBase): + __base_regs = EfuseDefineRegisters.DR_REG_EFUSE_BASE + # List of efuse blocks + # fmt: off + BLOCKS = [ + # Name, Alias, Index, Read address, Write address, Write protect bit, Read protect bit, Len, key_purpose + ("BLOCK0", [], 0, __base_regs + 0x000, __base_regs + 0x01C, None, None, 7, None), + ("BLOCK1", ["flash_encryption"], 1, __base_regs + 0x038, __base_regs + 0x098, 7, 0, 8, None), + ("BLOCK2", ["secure_boot_v1", "secure_boot_v2"], 2, __base_regs + 0x058, __base_regs + 0x0B8, 8, 1, 8, None), + ("BLOCK3", [], 3, __base_regs + 0x078, __base_regs + 0x0D8, 9, 2, 8, None), + ] + # fmt: on + + def get_burn_block_data_names(self): + list_of_names = [] + for block in self.BLOCKS: + blk = self.get(block) + if blk.name: + list_of_names.append(blk.name) + return list_of_names + + +class EfuseDefineFields(EfuseFieldsBase): + def __init__(self) -> None: + self.EFUSES = [] + # if MAC_VERSION is set "1", these efuse fields are in BLOCK3: + self.CUSTOM_MAC = [] + # The len of fields depends on coding scheme: for CODING_SCHEME_NONE + self.KEYBLOCKS_256 = [] + # The len of fields depends on coding scheme: for CODING_SCHEME_34 + self.KEYBLOCKS_192 = [] + # if BLK3_PART_RESERVE is set, these efuse fields are in BLOCK3: + self.ADC_CALIBRATION = [] + + self.CALC = [] + + dir_name = os.path.dirname(os.path.abspath(__file__)) + dir_name, file_name = os.path.split(dir_name) + file_name = file_name + ".yaml" + dir_name, _ = os.path.split(dir_name) + efuse_file = os.path.join(dir_name, "efuse_defs", file_name) + with open(f"{efuse_file}", "r") as r_file: + e_desc = yaml.safe_load(r_file) + super().__init__(e_desc) + + for i, efuse in enumerate(self.ALL_EFUSES): + if efuse.name == "BLOCK1" or efuse.name == "BLOCK2": + self.KEYBLOCKS_256.append(efuse) + BLOCK = copy.deepcopy(efuse) + BLOCK.type = "bytes:24" + BLOCK.bit_len = 24 * 8 + self.KEYBLOCKS_192.append(BLOCK) + self.ALL_EFUSES[i] = None + + elif efuse.name == "MAC_VERSION": + # A field from BLOCK3, It is used as a template + BLOCK3 = copy.deepcopy(efuse) + BLOCK3.name = "BLOCK3" + BLOCK3.block = 3 + BLOCK3.word = 0 + BLOCK3.pos = 0 + BLOCK3.bit_len = 32 * 8 + BLOCK3.type = "bytes:32" + BLOCK3.category = "security" + BLOCK3.class_type = "keyblock" + BLOCK3.description = "Variable Block 3" + self.KEYBLOCKS_256.append(BLOCK3) + + BLOCK3 = copy.deepcopy(BLOCK3) + BLOCK3.type = "bytes:24" + BLOCK3.bit_len = 24 * 8 + self.KEYBLOCKS_192.append(BLOCK3) + + elif efuse.category == "calibration" and efuse.block == 3: + self.ADC_CALIBRATION.append(efuse) + self.ALL_EFUSES[i] = None + + elif efuse.name in ["CUSTOM_MAC_CRC", "CUSTOM_MAC"]: + self.CUSTOM_MAC.append(efuse) + self.ALL_EFUSES[i] = None + + elif efuse.category == "spi pad": + efuse.class_type = "spipin" + + f = Field() + f.name = "WAFER_VERSION_MAJOR" + f.block = 0 + f.bit_len = 3 + f.type = f"uint:{f.bit_len}" + f.category = "identity" + f.class_type = "wafer" + f.description = "calc WAFER VERSION MAJOR from CHIP_VER_REV1 and CHIP_VER_REV2 and apb_ctl_date (read only)" + self.CALC.append(f) + + f = Field() + f.name = "PKG_VERSION" + f.block = 0 + f.bit_len = 4 + f.type = f"uint:{f.bit_len}" + f.category = "identity" + f.class_type = "pkg" + f.description = ( + "calc Chip package = CHIP_PACKAGE_4BIT << 3 + CHIP_PACKAGE (read only)" + ) + self.CALC.append(f) + + for efuse in self.ALL_EFUSES: + if efuse is not None: + self.EFUSES.append(efuse) + + self.ALL_EFUSES = [] diff --git a/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32/operations.py b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32/operations.py new file mode 100644 index 0000000..e0f419a --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32/operations.py @@ -0,0 +1,365 @@ +# This file includes the operations with eFuses for ESP32 chip +# +# SPDX-FileCopyrightText: 2020-2022 Espressif Systems (Shanghai) CO LTD +# +# SPDX-License-Identifier: GPL-2.0-or-later + +import argparse +import os # noqa: F401. It is used in IDF scripts +import traceback + +import espsecure + +import esptool + +from . import fields +from .. import util +from ..base_operations import ( + add_common_commands, + add_force_write_always, + add_show_sensitive_info_option, + burn_bit, + burn_block_data, + burn_efuse, + check_error, + dump, + read_protect_efuse, + summary, + write_protect_efuse, +) + + +def add_commands(subparsers, efuses): + add_common_commands(subparsers, efuses) + p = subparsers.add_parser( + "burn_key", + help="Burn a 256-bit key to EFUSE: %s" % ", ".join(efuses.BLOCKS_FOR_KEYS), + ) + p.add_argument( + "--no-protect-key", + help="Disable default read- and write-protecting of the key. " + "If this option is not set, once the key is flashed " + "it cannot be read back or changed.", + action="store_true", + ) + add_force_write_always(p) + add_show_sensitive_info_option(p) + p.add_argument( + "block", + help='Key block to burn. "flash_encryption" (block1), ' + '"secure_boot_v1" (block2), "secure_boot_v2" (block2)', + action="append", + choices=efuses.BLOCKS_FOR_KEYS, + ) + p.add_argument( + "keyfile", + help="File containing 256 bits of binary key data", + action="append", + type=argparse.FileType("rb"), + ) + for _ in efuses.BLOCKS_FOR_KEYS: + p.add_argument( + "block", + help='Key block to burn. "flash_encryption" (block1), ' + '"secure_boot_v1" (block2), "secure_boot_v2" (block2)', + metavar="BLOCK", + nargs="?", + action="append", + choices=efuses.BLOCKS_FOR_KEYS, + ) + p.add_argument( + "keyfile", + help="File containing 256 bits of binary key data", + metavar="KEYFILE", + nargs="?", + action="append", + type=argparse.FileType("rb"), + ) + + burn_key_digest = subparsers.add_parser( + "burn_key_digest", + help="Parse a RSA public key and burn the digest " + "to eFuse for use with Secure Boot V2", + ) + burn_key_digest.add_argument( + "keyfile", help="Key file to digest (PEM format)", type=argparse.FileType("rb") + ) + burn_key_digest.add_argument( + "--no-protect-key", + help="Disable default write-protecting of the key digest. " + "If this option is not set, once the key is flashed it cannot be changed.", + action="store_true", + ) + add_force_write_always(burn_key_digest) + add_show_sensitive_info_option(burn_key_digest) + + p = subparsers.add_parser( + "set_flash_voltage", + help="Permanently set the internal flash voltage regulator " + "to either 1.8V, 3.3V or OFF. This means GPIO12 can be high or low at reset " + "without changing the flash voltage.", + ) + p.add_argument("voltage", help="Voltage selection", choices=["1.8V", "3.3V", "OFF"]) + + p = subparsers.add_parser( + "burn_custom_mac", help="Burn a 48-bit Custom MAC Address to EFUSE BLOCK3." + ) + p.add_argument( + "mac", + help="Custom MAC Address to burn given in hexadecimal format " + "with bytes separated by colons " + "(e.g. AA:CD:EF:01:02:03).", + type=fields.base_fields.CheckArgValue(efuses, "CUSTOM_MAC"), + ) + add_force_write_always(p) + + p = subparsers.add_parser("get_custom_mac", help="Prints the Custom MAC Address.") + + +def burn_custom_mac(esp, efuses, args): + # Writing to BLK3: + # - MAC_VERSION = 1 + # - CUSTOM_MAC = AA:CD:EF:01:02:03 + # - CUSTOM_MAC_CRC = crc8(CUSTOM_MAC) + efuses["CUSTOM_MAC"].save(args.mac) + if not efuses.burn_all(check_batch_mode=True): + return + get_custom_mac(esp, efuses, args) + print("Successful") + + +def get_custom_mac(esp, efuses, args): + version = efuses["MAC_VERSION"].get() + if version > 0: + print( + "Custom MAC Address version {}: {}".format( + version, efuses["CUSTOM_MAC"].get() + ) + ) + else: + print("Custom MAC Address is not set in the device.") + + +def set_flash_voltage(esp, efuses, args): + sdio_force = efuses["XPD_SDIO_FORCE"] + sdio_tieh = efuses["XPD_SDIO_TIEH"] + sdio_reg = efuses["XPD_SDIO_REG"] + + # check efuses aren't burned in a way which makes this impossible + if args.voltage == "OFF" and sdio_reg.get() != 0: + raise esptool.FatalError( + "Can't set flash regulator to OFF as XPD_SDIO_REG efuse is already burned" + ) + + if args.voltage == "1.8V" and sdio_tieh.get() != 0: + raise esptool.FatalError( + "Can't set regulator to 1.8V is XPD_SDIO_TIEH efuse is already burned" + ) + + if args.voltage == "OFF": + msg = "Disable internal flash voltage regulator (VDD_SDIO). " + "SPI flash will need to be powered from an external source.\n" + "The following efuse is burned: XPD_SDIO_FORCE.\n" + "It is possible to later re-enable the internal regulator (%s) " % ( + "to 3.3V" if sdio_tieh.get() != 0 else "to 1.8V or 3.3V" + ) + "by burning an additional efuse" + elif args.voltage == "1.8V": + msg = "Set internal flash voltage regulator (VDD_SDIO) to 1.8V.\n" + "The following efuses are burned: XPD_SDIO_FORCE, XPD_SDIO_REG.\n" + "It is possible to later increase the voltage to 3.3V (permanently) " + "by burning additional efuse XPD_SDIO_TIEH" + elif args.voltage == "3.3V": + msg = "Enable internal flash voltage regulator (VDD_SDIO) to 3.3V.\n" + "The following efuses are burned: XPD_SDIO_FORCE, XPD_SDIO_REG, XPD_SDIO_TIEH." + print(msg) + sdio_force.save(1) # Disable GPIO12 + if args.voltage != "OFF": + sdio_reg.save(1) # Enable internal regulator + if args.voltage == "3.3V": + sdio_tieh.save(1) + print("VDD_SDIO setting complete.") + if not efuses.burn_all(check_batch_mode=True): + return + print("Successful") + + +def adc_info(esp, efuses, args): + adc_vref = efuses["ADC_VREF"] + blk3_reserve = efuses["BLK3_PART_RESERVE"] + + vref_raw = adc_vref.get_raw() + if vref_raw == 0: + print("ADC VRef calibration: None (1100mV nominal)") + else: + print("ADC VRef calibration: %dmV" % adc_vref.get()) + + if blk3_reserve.get(): + print("ADC readings stored in efuse BLOCK3:") + print(" ADC1 Low reading (150mV): %d" % efuses["ADC1_TP_LOW"].get()) + print(" ADC1 High reading (850mV): %d" % efuses["ADC1_TP_HIGH"].get()) + print(" ADC2 Low reading (150mV): %d" % efuses["ADC2_TP_LOW"].get()) + print(" ADC2 High reading (850mV): %d" % efuses["ADC2_TP_HIGH"].get()) + + +def burn_key(esp, efuses, args): + datafile_list = args.keyfile[ + 0 : len([keyfile for keyfile in args.keyfile if keyfile is not None]) : + ] + block_name_list = args.block[ + 0 : len([block for block in args.block if block is not None]) : + ] + efuses.force_write_always = args.force_write_always + no_protect_key = args.no_protect_key + + util.check_duplicate_name_in_list(block_name_list) + if len(block_name_list) != len(datafile_list): + raise esptool.FatalError( + "The number of blocks (%d) and datafile (%d) should be the same." + % (len(block_name_list), len(datafile_list)) + ) + + print("Burn keys to blocks:") + for block_name, datafile in zip(block_name_list, datafile_list): + efuse = None + for block in efuses.blocks: + if block_name == block.name or block_name in block.alias: + efuse = efuses[block.name] + if efuse is None: + raise esptool.FatalError("Unknown block name - %s" % (block_name)) + num_bytes = efuse.bit_len // 8 + data = datafile.read() + revers_msg = None + if block_name in ("flash_encryption", "secure_boot_v1"): + revers_msg = "\tReversing the byte order" + data = data[::-1] + print(" - %s" % (efuse.name), end=" ") + print( + "-> [{}]".format( + util.hexify(data, " ") + if args.show_sensitive_info + else " ".join(["??"] * len(data)) + ) + ) + if revers_msg: + print(revers_msg) + if len(data) != num_bytes: + raise esptool.FatalError( + "Incorrect key file size %d. " + "Key file must be %d bytes (%d bits) of raw binary key data." + % (len(data), num_bytes, num_bytes * 8) + ) + + efuse.save(data) + + if block_name in ("flash_encryption", "secure_boot_v1"): + if not no_protect_key: + print("\tDisabling read to key block") + efuse.disable_read() + + if not no_protect_key: + print("\tDisabling write to key block") + efuse.disable_write() + print("") + + if args.no_protect_key: + print("Key is left unprotected as per --no-protect-key argument.") + + msg = "Burn keys in efuse blocks.\n" + if no_protect_key: + msg += ( + "The key block will left readable and writeable (due to --no-protect-key)" + ) + else: + msg += "The key block will be read and write protected " + "(no further changes or readback)" + print(msg, "\n") + if not efuses.burn_all(check_batch_mode=True): + return + print("Successful") + + +def burn_key_digest(esp, efuses, args): + if efuses.coding_scheme == efuses.REGS.CODING_SCHEME_34: + raise esptool.FatalError("burn_key_digest only works with 'None' coding scheme") + + chip_revision = esp.get_chip_revision() + if chip_revision < 300: + raise esptool.FatalError( + "Incorrect chip revision for Secure boot v2. " + "Detected: v%d.%d. Expected: >= v3.0" + % (chip_revision / 100, chip_revision % 100) + ) + + digest = espsecure._digest_sbv2_public_key(args.keyfile) + efuse = efuses["BLOCK2"] + num_bytes = efuse.bit_len // 8 + if len(digest) != num_bytes: + raise esptool.FatalError( + "Incorrect digest size %d. " + "Digest must be %d bytes (%d bits) of raw binary key data." + % (len(digest), num_bytes, num_bytes * 8) + ) + print(" - %s" % (efuse.name), end=" ") + print( + "-> [{}]".format( + util.hexify(digest, " ") + if args.show_sensitive_info + else " ".join(["??"] * len(digest)) + ) + ) + + efuse.save(digest) + if not args.no_protect_key: + print("Disabling write to efuse %s..." % (efuse.name)) + efuse.disable_write() + + if not efuses.burn_all(check_batch_mode=True): + return + print("Successful") + + +def espefuse(esp, efuses, args, command): + parser = argparse.ArgumentParser() + subparsers = parser.add_subparsers(dest="operation") + add_commands(subparsers, efuses) + try: + cmd_line_args = parser.parse_args(command.split()) + except SystemExit: + traceback.print_stack() + raise esptool.FatalError('"{}" - incorrect command'.format(command)) + if cmd_line_args.operation == "execute_scripts": + configfiles = cmd_line_args.configfiles + index = cmd_line_args.index + # copy arguments from args to cmd_line_args + vars(cmd_line_args).update(vars(args)) + if cmd_line_args.operation == "execute_scripts": + cmd_line_args.configfiles = configfiles + cmd_line_args.index = index + if cmd_line_args.operation is None: + parser.print_help() + parser.exit(1) + operation_func = globals()[cmd_line_args.operation] + # each 'operation' is a module-level function of the same name + operation_func(esp, efuses, cmd_line_args) + + +def execute_scripts(esp, efuses, args): + efuses.batch_mode_cnt += 1 + del args.operation + scripts = args.scripts + del args.scripts + + for file in scripts: + with open(file.name, "r") as file: + exec(compile(file.read(), file.name, "exec")) + + if args.debug: + for block in efuses.blocks: + data = block.get_bitstring(from_read=False) + block.print_block(data, "regs_for_burn", args.debug) + + efuses.batch_mode_cnt -= 1 + if not efuses.burn_all(check_batch_mode=True): + return + print("Successful") diff --git a/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32c2/__init__.py b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32c2/__init__.py new file mode 100644 index 0000000..a3b55a8 --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32c2/__init__.py @@ -0,0 +1,3 @@ +from . import operations +from .emulate_efuse_controller import EmulateEfuseController +from .fields import EspEfuses diff --git a/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32c2/__pycache__/__init__.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32c2/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..d28f158 Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32c2/__pycache__/__init__.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32c2/__pycache__/emulate_efuse_controller.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32c2/__pycache__/emulate_efuse_controller.cpython-312.pyc new file mode 100644 index 0000000..74e506e Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32c2/__pycache__/emulate_efuse_controller.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32c2/__pycache__/fields.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32c2/__pycache__/fields.cpython-312.pyc new file mode 100644 index 0000000..4b8f7ef Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32c2/__pycache__/fields.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32c2/__pycache__/mem_definition.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32c2/__pycache__/mem_definition.cpython-312.pyc new file mode 100644 index 0000000..0a537ef Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32c2/__pycache__/mem_definition.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32c2/__pycache__/operations.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32c2/__pycache__/operations.cpython-312.pyc new file mode 100644 index 0000000..23bfee9 Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32c2/__pycache__/operations.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32c2/emulate_efuse_controller.py b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32c2/emulate_efuse_controller.py new file mode 100644 index 0000000..26796df --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32c2/emulate_efuse_controller.py @@ -0,0 +1,139 @@ +# This file describes eFuses controller for ESP32-C2 chip +# +# SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from bitstring import BitStream + +import reedsolo + +from .mem_definition import EfuseDefineBlocks, EfuseDefineFields, EfuseDefineRegisters +from ..emulate_efuse_controller_base import EmulateEfuseControllerBase, FatalError + + +class EmulateEfuseController(EmulateEfuseControllerBase): + """The class for virtual efuse operation. Using for HOST_TEST.""" + + CHIP_NAME = "ESP32-C2" + mem = None + debug = False + + def __init__(self, efuse_file=None, debug=False): + self.Blocks = EfuseDefineBlocks + self.Fields = EfuseDefineFields() + self.REGS = EfuseDefineRegisters + super(EmulateEfuseController, self).__init__(efuse_file, debug) + self.write_reg(self.REGS.EFUSE_CMD_REG, 0) + + """ esptool method start >>""" + + def get_major_chip_version(self): + return 1 + + def get_minor_chip_version(self): + return 0 + + def get_crystal_freq(self): + return 40 # MHz + + def get_security_info(self): + return { + "flags": 0, + "flash_crypt_cnt": 0, + "key_purposes": 0, + "chip_id": 0, + "api_version": 0, + } + + """ << esptool method end """ + + def handle_writing_event(self, addr, value): + if addr == self.REGS.EFUSE_CMD_REG: + if value & self.REGS.EFUSE_PGM_CMD: + self.copy_blocks_wr_regs_to_rd_regs(updated_block=(value >> 2) & 0xF) + self.clean_blocks_wr_regs() + self.check_rd_protection_area() + self.write_reg(addr, 0) + self.write_reg(self.REGS.EFUSE_CMD_REG, 0) + elif value == self.REGS.EFUSE_READ_CMD: + self.write_reg(addr, 0) + self.write_reg(self.REGS.EFUSE_CMD_REG, 0) + self.save_to_file() + + def get_bitlen_of_block(self, blk, wr=False): + if blk.id == 0: + if wr: + return 32 * 8 + else: + return 32 * blk.len + else: + if wr: + rs_coding = 32 * 3 + return 32 * 8 + rs_coding + else: + return 32 * blk.len + + def handle_coding_scheme(self, blk, data): + if blk.id != 0: + # CODING_SCHEME RS applied only for all blocks except BLK0. + coded_bytes = 12 + data.pos = coded_bytes * 8 + plain_data = data.readlist("32*uint:8")[::-1] + # takes 32 bytes + # apply RS encoding + rs = reedsolo.RSCodec(coded_bytes) + # 32 byte of data + 12 bytes RS + calc_encoded_data = list(rs.encode([x for x in plain_data])) + data.pos = 0 + if calc_encoded_data != data.readlist("44*uint:8")[::-1]: + raise FatalError("Error in coding scheme data") + data = data[coded_bytes * 8 :] + if blk.len < 8: + data = data[(8 - blk.len) * 32 :] + return data + + def check_rd_protection_area(self): + # checks fields which have the read protection bits. + # if the read protection bit is set then we need to reset this field to 0. + + def get_read_disable_mask(blk): + mask = 0 + if isinstance(blk.read_disable_bit, list): + for i in blk.read_disable_bit: + mask |= 1 << i + else: + mask = 1 << blk.read_disable_bit + return mask + + read_disable_bit = self.read_field("RD_DIS", bitstring=False) + for b in self.Blocks.BLOCKS: + blk = self.Blocks.get(b) + block = self.read_block(blk.id) + if ( + blk.read_disable_bit is not None + and read_disable_bit & get_read_disable_mask(blk) + ): + if isinstance(blk.read_disable_bit, list): + if read_disable_bit & (1 << blk.read_disable_bit[0]): + block.set( + 0, [i for i in range(blk.len * 32 // 2, blk.len * 32)] + ) + if read_disable_bit & (1 << blk.read_disable_bit[1]): + block.set(0, [i for i in range(0, blk.len * 32 // 2)]) + else: + block.set(0) + else: + for field in self.Fields.EFUSES: + if ( + blk.id == field.block + and field.read_disable_bit is not None + and read_disable_bit & get_read_disable_mask(field) + ): + raw_data = self.read_field(field.name) + raw_data.set(0) + block.pos = block.length - ( + field.word * 32 + field.pos + raw_data.length + ) + block.overwrite(BitStream(raw_data.length)) + self.overwrite_mem_from_block(blk, block) diff --git a/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32c2/fields.py b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32c2/fields.py new file mode 100644 index 0000000..cba599e --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32c2/fields.py @@ -0,0 +1,382 @@ +# This file describes eFuses for ESP32-C2 chip +# +# SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD +# +# SPDX-License-Identifier: GPL-2.0-or-later + +import binascii +import struct +import sys +import time + +from bitstring import BitArray + +import esptool + +import reedsolo + +from .mem_definition import EfuseDefineBlocks, EfuseDefineFields, EfuseDefineRegisters +from .. import base_fields +from .. import util + + +class EfuseBlock(base_fields.EfuseBlockBase): + def len_of_burn_unit(self): + # The writing register window is 8 registers for any blocks. + # len in bytes + return 8 * 4 + + def __init__(self, parent, param, skip_read=False): + parent.read_coding_scheme() + super(EfuseBlock, self).__init__(parent, param, skip_read=skip_read) + + def apply_coding_scheme(self): + data = self.get_raw(from_read=False)[::-1] + if len(data) < self.len_of_burn_unit(): + add_empty_bytes = self.len_of_burn_unit() - len(data) + data = data + (b"\x00" * add_empty_bytes) + if self.get_coding_scheme() == self.parent.REGS.CODING_SCHEME_RS: + # takes 32 bytes + # apply RS encoding + rs = reedsolo.RSCodec(12) + # 32 byte of data + 12 bytes RS + encoded_data = rs.encode([x for x in data]) + words = struct.unpack("<" + "I" * 11, encoded_data) + # returns 11 words (8 words of data + 3 words of RS coding) + else: + # takes 32 bytes + words = struct.unpack("<" + ("I" * (len(data) // 4)), data) + # returns 8 words + return words + + +class EspEfuses(base_fields.EspEfusesBase): + """ + Wrapper object to manage the efuse fields in a connected ESP bootloader + """ + + debug = False + do_not_confirm = False + + def __init__(self, esp, skip_connect=False, debug=False, do_not_confirm=False): + self.Blocks = EfuseDefineBlocks() + self.Fields = EfuseDefineFields() + self.REGS = EfuseDefineRegisters + self.BURN_BLOCK_DATA_NAMES = self.Blocks.get_burn_block_data_names() + self.BLOCKS_FOR_KEYS = self.Blocks.get_blocks_for_keys() + self._esp = esp + self.debug = debug + self.do_not_confirm = do_not_confirm + if esp.CHIP_NAME != "ESP32-C2": + raise esptool.FatalError( + "Expected the 'esp' param for ESP32-C2 chip but got for '%s'." + % (esp.CHIP_NAME) + ) + if not skip_connect: + flags = self._esp.get_security_info()["flags"] + GET_SECURITY_INFO_FLAG_SECURE_DOWNLOAD_ENABLE = 1 << 2 + if flags & GET_SECURITY_INFO_FLAG_SECURE_DOWNLOAD_ENABLE: + raise esptool.FatalError( + "Secure Download Mode is enabled. The tool can not read eFuses." + ) + self.blocks = [ + EfuseBlock(self, self.Blocks.get(block), skip_read=skip_connect) + for block in self.Blocks.BLOCKS + ] + if not skip_connect: + self.get_coding_scheme_warnings() + self.efuses = [EfuseField.convert(self, efuse) for efuse in self.Fields.EFUSES] + self.efuses += [ + EfuseField.convert(self, efuse) for efuse in self.Fields.KEYBLOCKS + ] + if skip_connect: + self.efuses += [ + EfuseField.convert(self, efuse) + for efuse in self.Fields.BLOCK2_CALIBRATION_EFUSES + ] + else: + if self["BLK_VERSION_MINOR"].get() == 1: + self.efuses += [ + EfuseField.convert(self, efuse) + for efuse in self.Fields.BLOCK2_CALIBRATION_EFUSES + ] + + def __getitem__(self, efuse_name): + """Return the efuse field with the given name""" + for e in self.efuses: + if efuse_name == e.name or any(x == efuse_name for x in e.alt_names): + return e + new_fields = False + for efuse in self.Fields.BLOCK2_CALIBRATION_EFUSES: + if efuse.name == efuse_name or any( + x == efuse_name for x in efuse.alt_names + ): + self.efuses += [ + EfuseField.convert(self, efuse) + for efuse in self.Fields.BLOCK2_CALIBRATION_EFUSES + ] + new_fields = True + if new_fields: + for e in self.efuses: + if efuse_name == e.name or any(x == efuse_name for x in e.alt_names): + return e + raise KeyError + + def read_coding_scheme(self): + self.coding_scheme = self.REGS.CODING_SCHEME_RS + + def print_status_regs(self): + print("") + self.blocks[0].print_block(self.blocks[0].err_bitarray, "err__regs", debug=True) + print( + "{:27} 0x{:08x}".format( + "EFUSE_RD_RS_ERR_REG", self.read_reg(self.REGS.EFUSE_RD_RS_ERR_REG) + ) + ) + + def efuse_controller_setup(self): + self.set_efuse_timing() + self.clear_pgm_registers() + self.wait_efuse_idle() + + def write_efuses(self, block): + self.efuse_program(block) + return self.get_coding_scheme_warnings(silent=True) + + def clear_pgm_registers(self): + self.wait_efuse_idle() + for r in range( + self.REGS.EFUSE_PGM_DATA0_REG, self.REGS.EFUSE_PGM_DATA0_REG + 32, 4 + ): + self.write_reg(r, 0) + + def wait_efuse_idle(self): + deadline = time.time() + self.REGS.EFUSE_BURN_TIMEOUT + while time.time() < deadline: + cmds = self.REGS.EFUSE_PGM_CMD | self.REGS.EFUSE_READ_CMD + if self.read_reg(self.REGS.EFUSE_CMD_REG) & cmds == 0: + if self.read_reg(self.REGS.EFUSE_CMD_REG) & cmds == 0: + # Due to a hardware error, we have to read READ_CMD again + # to make sure the efuse clock is normal. + # For PGM_CMD it is not necessary. + return + raise esptool.FatalError( + "Timed out waiting for Efuse controller command to complete" + ) + + def efuse_program(self, block): + self.wait_efuse_idle() + self.write_reg(self.REGS.EFUSE_CONF_REG, self.REGS.EFUSE_WRITE_OP_CODE) + self.write_reg(self.REGS.EFUSE_CMD_REG, self.REGS.EFUSE_PGM_CMD | (block << 2)) + self.wait_efuse_idle() + self.clear_pgm_registers() + self.efuse_read() + + def efuse_read(self): + self.wait_efuse_idle() + self.write_reg(self.REGS.EFUSE_CONF_REG, self.REGS.EFUSE_READ_OP_CODE) + # need to add a delay after triggering EFUSE_READ_CMD, as ROM loader checks some + # efuse registers after each command is completed + # if ENABLE_SECURITY_DOWNLOAD or DIS_DOWNLOAD_MODE is enabled by the current cmd, then we need to try to reconnect to the chip. + try: + self.write_reg( + self.REGS.EFUSE_CMD_REG, self.REGS.EFUSE_READ_CMD, delay_after_us=1000 + ) + self.wait_efuse_idle() + except esptool.FatalError: + secure_download_mode_before = self._esp.secure_download_mode + + try: + self._esp = self.reconnect_chip(self._esp) + except esptool.FatalError: + print("Can not re-connect to the chip") + if not self["DIS_DOWNLOAD_MODE"].get() and self[ + "DIS_DOWNLOAD_MODE" + ].get(from_read=False): + print( + "This is the correct behavior as we are actually burning " + "DIS_DOWNLOAD_MODE which disables the connection to the chip" + ) + print("DIS_DOWNLOAD_MODE is enabled") + print("Successful") + sys.exit(0) # finish without errors + raise + + print("Established a connection with the chip") + if self._esp.secure_download_mode and not secure_download_mode_before: + print("Secure download mode is enabled") + if not self["ENABLE_SECURITY_DOWNLOAD"].get() and self[ + "ENABLE_SECURITY_DOWNLOAD" + ].get(from_read=False): + print( + "espefuse tool can not continue to work in Secure download mode" + ) + print("ENABLE_SECURITY_DOWNLOAD is enabled") + print("Successful") + sys.exit(0) # finish without errors + raise + + def set_efuse_timing(self): + """Set timing registers for burning efuses""" + # Configure clock + xtal_freq = self.get_crystal_freq() + if xtal_freq not in [26, 40]: + raise esptool.FatalError( + "The eFuse supports only xtal=26M and 40M (xtal was %d)" % xtal_freq + ) + + self.update_reg(self.REGS.EFUSE_DAC_CONF_REG, self.REGS.EFUSE_DAC_NUM_M, 0xFF) + self.update_reg( + self.REGS.EFUSE_DAC_CONF_REG, self.REGS.EFUSE_DAC_CLK_DIV_M, 0x28 + ) + self.update_reg( + self.REGS.EFUSE_WR_TIM_CONF1_REG, self.REGS.EFUSE_PWR_ON_NUM_M, 0x3000 + ) + self.update_reg( + self.REGS.EFUSE_WR_TIM_CONF2_REG, self.REGS.EFUSE_PWR_OFF_NUM_M, 0x190 + ) + + tpgm_inactive_val = 200 if xtal_freq == 40 else 130 + self.update_reg( + self.REGS.EFUSE_WR_TIM_CONF0_REG, + self.REGS.EFUSE_TPGM_INACTIVE_M, + tpgm_inactive_val, + ) + + def get_coding_scheme_warnings(self, silent=False): + """Check if the coding scheme has detected any errors.""" + old_addr_reg = 0 + reg_value = 0 + ret_fail = False + for block in self.blocks: + if block.id == 0: + words = [ + self.read_reg(self.REGS.EFUSE_RD_REPEAT_ERR_REG + offs * 4) + for offs in range(1) + ] + block.err_bitarray.pos = 0 + for word in reversed(words): + block.err_bitarray.overwrite(BitArray("uint:32=%d" % word)) + block.num_errors = block.err_bitarray.count(True) + block.fail = block.num_errors != 0 + else: + addr_reg, err_num_mask, err_num_offs, fail_bit = self.REGS.BLOCK_ERRORS[ + block.id + ] + if err_num_mask is None or err_num_offs is None or fail_bit is None: + continue + if addr_reg != old_addr_reg: + old_addr_reg = addr_reg + reg_value = self.read_reg(addr_reg) + block.fail = reg_value & (1 << fail_bit) != 0 + block.num_errors = (reg_value >> err_num_offs) & err_num_mask + ret_fail |= block.fail + if not silent and (block.fail or block.num_errors): + print( + "Error(s) in BLOCK%d [ERRORS:%d FAIL:%d]" + % (block.id, block.num_errors, block.fail) + ) + if (self.debug or ret_fail) and not silent: + self.print_status_regs() + return ret_fail + + def summary(self): + # TODO add support set_flash_voltage - "Flash voltage (VDD_SPI)" + return "" + + +class EfuseField(base_fields.EfuseFieldBase): + @staticmethod + def convert(parent, efuse): + return { + "mac": EfuseMacField, + "keypurpose": EfuseKeyPurposeField, + "t_sensor": EfuseTempSensor, + "adc_tp": EfuseAdcPointCalibration, + }.get(efuse.class_type, EfuseField)(parent, efuse) + + +class EfuseTempSensor(EfuseField): + def get(self, from_read=True): + value = self.get_bitstring(from_read) + sig = -1 if value[0] else 1 + return sig * value[1:].uint * 0.1 + + +class EfuseAdcPointCalibration(EfuseField): + def get(self, from_read=True): + STEP_SIZE = 4 + value = self.get_bitstring(from_read) + sig = -1 if value[0] else 1 + return sig * value[1:].uint * STEP_SIZE + + +class EfuseMacField(EfuseField): + def check_format(self, new_value_str): + if new_value_str is None: + raise esptool.FatalError( + "Required MAC Address in AA:CD:EF:01:02:03 format!" + ) + if new_value_str.count(":") != 5: + raise esptool.FatalError( + "MAC Address needs to be a 6-byte hexadecimal format " + "separated by colons (:)!" + ) + hexad = new_value_str.replace(":", "") + if len(hexad) != 12: + raise esptool.FatalError( + "MAC Address needs to be a 6-byte hexadecimal number " + "(12 hexadecimal characters)!" + ) + # order of bytearray = b'\xaa\xcd\xef\x01\x02\x03', + bindata = binascii.unhexlify(hexad) + # unicast address check according to + # https://tools.ietf.org/html/rfc7042#section-2.1 + if esptool.util.byte(bindata, 0) & 0x01: + raise esptool.FatalError("Custom MAC must be a unicast MAC!") + return bindata + + def check(self): + errs, fail = self.parent.get_block_errors(self.block) + if errs != 0 or fail: + output = "Block%d has ERRORS:%d FAIL:%d" % (self.block, errs, fail) + else: + output = "OK" + return "(" + output + ")" + + def get(self, from_read=True): + if self.name == "CUSTOM_MAC": + mac = self.get_raw(from_read)[::-1] + else: + mac = self.get_raw(from_read) + return "%s %s" % (util.hexify(mac, ":"), self.check()) + + def save(self, new_value): + def print_field(e, new_value): + print( + " - '{}' ({}) {} -> {}".format( + e.name, e.description, e.get_bitstring(), new_value + ) + ) + + if self.name == "CUSTOM_MAC": + bitarray_mac = self.convert_to_bitstring(new_value) + print_field(self, bitarray_mac) + super(EfuseMacField, self).save(new_value) + else: + raise esptool.FatalError("Writing Factory MAC address is not supported") + + +class EfuseKeyPurposeField(EfuseField): + KEY_PURPOSES = [ + # fmt: off + ("USER", 0, None), # User purposes (software-only use) + ("XTS_AES_128_KEY", 1, None), # (whole 256bits) flash/PSRAM encryption + ("XTS_AES_128_KEY_DERIVED_FROM_128_EFUSE_BITS", 2, None), # (lo 128bits) flash/PSRAM encryption + ("SECURE_BOOT_DIGEST", 3, "DIGEST"), + # (hi 128bits) Secure Boot key digest + # fmt: on + ] + + KEY_PURPOSES_NAME = [name[0] for name in KEY_PURPOSES] + DIGEST_KEY_PURPOSES = [name[0] for name in KEY_PURPOSES if name[2] == "DIGEST"] diff --git a/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32c2/mem_definition.py b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32c2/mem_definition.py new file mode 100644 index 0000000..7adb011 --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32c2/mem_definition.py @@ -0,0 +1,147 @@ +# This file describes eFuses fields and registers for ESP32-C2 chip +# +# SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD +# +# SPDX-License-Identifier: GPL-2.0-or-later + +import copy +import os + +import yaml + +from ..mem_definition_base import ( + EfuseBlocksBase, + EfuseFieldsBase, + EfuseRegistersBase, +) + + +class EfuseDefineRegisters(EfuseRegistersBase): + EFUSE_MEM_SIZE = 0x01FC + 4 + + # EFUSE registers & command/conf values + DR_REG_EFUSE_BASE = 0x60008800 + EFUSE_PGM_DATA0_REG = DR_REG_EFUSE_BASE + EFUSE_PGM_CHECK_VALUE0_REG = DR_REG_EFUSE_BASE + 0x020 + EFUSE_CLK_REG = DR_REG_EFUSE_BASE + 0x88 + EFUSE_CONF_REG = DR_REG_EFUSE_BASE + 0x8C + EFUSE_STATUS_REG = DR_REG_EFUSE_BASE + 0x90 + EFUSE_CMD_REG = DR_REG_EFUSE_BASE + 0x94 + EFUSE_RD_REPEAT_ERR_REG = DR_REG_EFUSE_BASE + 0x80 + EFUSE_RD_RS_ERR_REG = DR_REG_EFUSE_BASE + 0x84 + EFUSE_WRITE_OP_CODE = 0x5A5A + EFUSE_READ_OP_CODE = 0x5AA5 + EFUSE_PGM_CMD_MASK = 0x3 + EFUSE_PGM_CMD = 0x2 + EFUSE_READ_CMD = 0x1 + + BLOCK_ERRORS = [ + # error_reg, err_num_mask, err_num_offs, fail_bit + (EFUSE_RD_REPEAT_ERR_REG, None, None, None), # BLOCK0 + (EFUSE_RD_RS_ERR_REG, 0x7, 0, 3), # BLOCK1 + (EFUSE_RD_RS_ERR_REG, 0x7, 4, 7), # BLOCK2 + (EFUSE_RD_RS_ERR_REG, 0x7, 8, 11), # BLOCK3 + ] + + EFUSE_WR_TIM_CONF2_REG = DR_REG_EFUSE_BASE + 0x118 + EFUSE_PWR_OFF_NUM_S = 0 + EFUSE_PWR_OFF_NUM_M = 0xFFFF << EFUSE_PWR_OFF_NUM_S + + EFUSE_WR_TIM_CONF0_REG = DR_REG_EFUSE_BASE + 0x110 + EFUSE_TPGM_INACTIVE_S = 8 + EFUSE_TPGM_INACTIVE_M = 0xFF << EFUSE_TPGM_INACTIVE_S + + EFUSE_WR_TIM_CONF1_REG = DR_REG_EFUSE_BASE + 0x114 + EFUSE_PWR_ON_NUM_S = 8 + EFUSE_PWR_ON_NUM_M = 0x0000FFFF << EFUSE_PWR_ON_NUM_S + + EFUSE_DAC_CONF_REG = DR_REG_EFUSE_BASE + 0x108 + EFUSE_DAC_CLK_DIV_S = 0 + EFUSE_DAC_CLK_DIV_M = 0xFF << EFUSE_DAC_CLK_DIV_S + + # EFUSE_DAC_CONF_REG + EFUSE_DAC_NUM_S = 9 + EFUSE_DAC_NUM_M = 0xFF << EFUSE_DAC_NUM_S + + +class EfuseDefineBlocks(EfuseBlocksBase): + __base_rd_regs = EfuseDefineRegisters.DR_REG_EFUSE_BASE + __base_wr_regs = EfuseDefineRegisters.EFUSE_PGM_DATA0_REG + # List of efuse blocks + # fmt: off + BLOCKS = [ + # Name, Alias, Index, Read address, Write address, Write protect bit, Read protect bit, Len, key_purpose + ("BLOCK0", ["BLOCK0"], 0, __base_rd_regs + 0x02C, __base_wr_regs, None, None, 2, None), + ("BLOCK1", ["BLOCK1"], 1, __base_rd_regs + 0x034, __base_wr_regs, 5, None, 3, None), + ("BLOCK2", ["BLOCK2"], 2, __base_rd_regs + 0x040, __base_wr_regs, 6, None, 8, None), + ("BLOCK_KEY0", ["BLOCK3"], 3, __base_rd_regs + 0x060, __base_wr_regs, 7, [0, 1], 8, None), + ] + # fmt: on + + def get_burn_block_data_names(self): + list_of_names = [] + for block in self.BLOCKS: + blk = self.get(block) + if blk.name: + list_of_names.append(blk.name) + if blk.alias: + for alias in blk.alias: + list_of_names.append(alias) + return list_of_names + + def get_blocks_for_keys(self): + return ["BLOCK_KEY0"] + + +class EfuseDefineFields(EfuseFieldsBase): + def __init__(self) -> None: + # List of efuse fields from TRM the chapter eFuse Controller. + self.EFUSES = [] + + self.KEYBLOCKS = [] + + # if BLK_VERSION_MINOR is 1, these efuse fields are in BLOCK2 + self.BLOCK2_CALIBRATION_EFUSES = [] + + dir_name = os.path.dirname(os.path.abspath(__file__)) + dir_name, file_name = os.path.split(dir_name) + file_name = file_name + ".yaml" + dir_name, _ = os.path.split(dir_name) + efuse_file = os.path.join(dir_name, "efuse_defs", file_name) + with open(f"{efuse_file}", "r") as r_file: + e_desc = yaml.safe_load(r_file) + super().__init__(e_desc) + + for i, efuse in enumerate(self.ALL_EFUSES): + if efuse.name in ["BLOCK_KEY0"]: + self.KEYBLOCKS.append(efuse) + BLOCK_KEY0_LOW_128 = copy.deepcopy(efuse) + BLOCK_KEY0_LOW_128.name = "BLOCK_KEY0_LOW_128" + BLOCK_KEY0_LOW_128.type = "bytes:16" + BLOCK_KEY0_LOW_128.bit_len = 16 * 8 + BLOCK_KEY0_LOW_128.description = ( + "BLOCK_KEY0 - lower 128-bits. 128-bit key of Flash Encryption" + ) + BLOCK_KEY0_LOW_128.read_disable_bit = efuse.read_disable_bit[0] + self.KEYBLOCKS.append(BLOCK_KEY0_LOW_128) + BLOCK_KEY0_HI_128 = copy.deepcopy(efuse) + BLOCK_KEY0_HI_128.name = "BLOCK_KEY0_HI_128" + BLOCK_KEY0_HI_128.word = 4 + BLOCK_KEY0_HI_128.type = "bytes:16" + BLOCK_KEY0_HI_128.bit_len = 16 * 8 + BLOCK_KEY0_HI_128.description = ( + "BLOCK_KEY0 - higher 128-bits. 128-bits key of Secure Boot" + ) + BLOCK_KEY0_HI_128.read_disable_bit = efuse.read_disable_bit[1] + self.KEYBLOCKS.append(BLOCK_KEY0_HI_128) + self.ALL_EFUSES[i] = None + + elif efuse.category == "calibration": + self.BLOCK2_CALIBRATION_EFUSES.append(efuse) + self.ALL_EFUSES[i] = None + + for efuse in self.ALL_EFUSES: + if efuse is not None: + self.EFUSES.append(efuse) + + self.ALL_EFUSES = [] diff --git a/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32c2/operations.py b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32c2/operations.py new file mode 100644 index 0000000..fb7324e --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32c2/operations.py @@ -0,0 +1,356 @@ +# This file includes the operations with eFuses for ESP32-C2 chip +# +# SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD +# +# SPDX-License-Identifier: GPL-2.0-or-later + +import argparse +import os # noqa: F401. It is used in IDF scripts +import traceback + +import espsecure + +import esptool + +from . import fields +from .. import util +from ..base_operations import ( + add_common_commands, + add_force_write_always, + add_show_sensitive_info_option, + burn_bit, + burn_block_data, + burn_efuse, + check_error, + dump, + read_protect_efuse, + summary, + write_protect_efuse, +) + + +def protect_options(p): + p.add_argument( + "--no-write-protect", + help="Disable write-protecting of the key. The key remains writable. " + "(The keys use the RS coding scheme that does not support " + "post-write data changes. Forced write can damage RS encoding bits.) " + "The write-protecting of keypurposes does not depend on the option, " + "it will be set anyway.", + action="store_true", + ) + p.add_argument( + "--no-read-protect", + help="Disable read-protecting of the key. The key remains readable software.", + action="store_true", + ) + + +def add_commands(subparsers, efuses): + add_common_commands(subparsers, efuses) + burn_key = subparsers.add_parser( + "burn_key", help="Burn the key block with the specified name" + ) + protect_options(burn_key) + add_force_write_always(burn_key) + add_show_sensitive_info_option(burn_key) + burn_key.add_argument( + "block", + help="Key block to burn", + action="append", + choices=efuses.BLOCKS_FOR_KEYS, + ) + burn_key.add_argument( + "keyfile", + help="File containing 128/256 bits of binary key data", + action="append", + type=argparse.FileType("rb"), + ) + burn_key.add_argument( + "keypurpose", + help="Purpose to set.", + action="append", + choices=fields.EfuseKeyPurposeField.KEY_PURPOSES_NAME, + ) + for _ in range(1): + burn_key.add_argument( + "block", + help="Key block to burn", + nargs="?", + action="append", + metavar="BLOCK", + choices=efuses.BLOCKS_FOR_KEYS, + ) + burn_key.add_argument( + "keyfile", + help="File containing 128/256 bits of binary key data", + nargs="?", + action="append", + metavar="KEYFILE", + type=argparse.FileType("rb"), + ) + burn_key.add_argument( + "keypurpose", + help="Purpose to set.", + nargs="?", + action="append", + metavar="KEYPURPOSE", + choices=fields.EfuseKeyPurposeField.KEY_PURPOSES_NAME, + ) + + burn_key_digest = subparsers.add_parser( + "burn_key_digest", + help="Parse an ECDSA public key and burn the digest " + "to higher 128-bits of BLOCK_KEY0", + ) + protect_options(burn_key_digest) + add_force_write_always(burn_key_digest) + add_show_sensitive_info_option(burn_key_digest) + burn_key_digest.add_argument( + "keyfile", help="Key file to digest (PEM format)", type=argparse.FileType("rb") + ) + + p = subparsers.add_parser( + "set_flash_voltage", + help="Permanently set the internal flash voltage regulator " + "to either 1.8V, 3.3V or OFF. This means GPIO45 can be high or low " + "at reset without changing the flash voltage.", + ) + p.add_argument("voltage", help="Voltage selection", choices=["1.8V", "3.3V", "OFF"]) + + p = subparsers.add_parser( + "burn_custom_mac", help="Burn a 48-bit Custom MAC Address to EFUSE BLOCK1." + ) + p.add_argument( + "mac", + help="Custom MAC Address to burn given in hexadecimal format " + "with bytes separated by colons (e.g. AA:CD:EF:01:02:03).", + type=fields.base_fields.CheckArgValue(efuses, "CUSTOM_MAC"), + ) + add_force_write_always(p) + + p = subparsers.add_parser("get_custom_mac", help="Prints the Custom MAC Address.") + + +def burn_custom_mac(esp, efuses, args): + efuses["CUSTOM_MAC"].save(args.mac) + efuses["CUSTOM_MAC_USED"].save(1) + if not efuses.burn_all(check_batch_mode=True): + return + get_custom_mac(esp, efuses, args) + print("Successful") + + +def get_custom_mac(esp, efuses, args): + print("Custom MAC Address: {}".format(efuses["CUSTOM_MAC"].get())) + + +def set_flash_voltage(esp, efuses, args): + raise esptool.FatalError("set_flash_voltage is not supported!") + + +def adc_info(esp, efuses, args): + print("") + # fmt: off + if efuses["BLK_VERSION_MINOR"].get() == 1: + print(" RF_REF_I_BIAS_CONFIG: {}".format(efuses["RF_REF_I_BIAS_CONFIG"].get())) + + print(" LDO_VOL_BIAS_CONFIG_LOW: {}".format(efuses["LDO_VOL_BIAS_CONFIG_LOW"].get())) + print(" LDO_VOL_BIAS_CONFIG_HIGH: {}".format(efuses["LDO_VOL_BIAS_CONFIG_HIGH"].get())) + + print(" PVT_LOW: {}".format(efuses["PVT_LOW"].get())) + print(" PVT_HIGH: {}".format(efuses["PVT_HIGH"].get())) + + print(" ADC_CALIBRATION_0: {}".format(efuses["ADC_CALIBRATION_0"].get())) + print(" ADC_CALIBRATION_1: {}".format(efuses["ADC_CALIBRATION_1"].get())) + print(" ADC_CALIBRATION_2: {}".format(efuses["ADC_CALIBRATION_2"].get())) + + else: + print("BLK_VERSION_MINOR = {}".format(efuses["BLK_VERSION_MINOR"].get_meaning())) + # fmt: on + + +def burn_key(esp, efuses, args, digest=None): + if digest is None: + datafile_list = args.keyfile[ + 0 : len([name for name in args.keyfile if name is not None]) : + ] + else: + datafile_list = digest[0 : len([name for name in digest if name is not None]) :] + efuses.force_write_always = args.force_write_always + block_name_list = args.block[ + 0 : len([name for name in args.block if name is not None]) : + ] + keypurpose_list = args.keypurpose[ + 0 : len([name for name in args.keypurpose if name is not None]) : + ] + + util.check_duplicate_name_in_list(keypurpose_list) + if len(block_name_list) != len(datafile_list) or len(block_name_list) != len( + keypurpose_list + ): + raise esptool.FatalError( + "The number of blocks (%d), datafile (%d) and " + "keypurpose (%d) should be the same." + % (len(block_name_list), len(datafile_list), len(keypurpose_list)) + ) + + assert 1 <= len(block_name_list) <= 2, "Unexpected case" + + if len(block_name_list) == 2: + incompatible = True if "XTS_AES_128_KEY" in keypurpose_list else False + permitted_purposes = [ + "XTS_AES_128_KEY_DERIVED_FROM_128_EFUSE_BITS", + "SECURE_BOOT_DIGEST", + ] + incompatible |= ( + keypurpose_list[0] in permitted_purposes + and keypurpose_list[1] not in permitted_purposes + ) + if incompatible: + raise esptool.FatalError( + "These keypurposes are incompatible %s" % (keypurpose_list) + ) + + print("Burn keys to blocks:") + for datafile, keypurpose in zip(datafile_list, keypurpose_list): + data = datafile if isinstance(datafile, bytes) else datafile.read() + + if keypurpose == "XTS_AES_128_KEY_DERIVED_FROM_128_EFUSE_BITS": + efuse = efuses["BLOCK_KEY0_LOW_128"] + elif keypurpose == "SECURE_BOOT_DIGEST": + efuse = efuses["BLOCK_KEY0_HI_128"] + if len(data) == 32: + print( + "\tProgramming only left-most 128-bits from SHA256 hash of " + "public key to highest 128-bits of BLOCK KEY0" + ) + data = data[:16] + elif len(data) != efuse.bit_len // 8: + raise esptool.FatalError( + "Wrong length of this file for SECURE_BOOT_DIGEST. " + "Got %d (expected %d or %d)" % (len(data), 32, efuse.bit_len // 8) + ) + assert len(data) == 16, "Only 16 bytes expected" + else: + efuse = efuses["BLOCK_KEY0"] + + num_bytes = efuse.bit_len // 8 + + print(" - %s" % (efuse.name), end=" ") + revers_msg = None + if keypurpose.startswith("XTS_AES_"): + revers_msg = "\tReversing byte order for AES-XTS hardware peripheral" + data = data[::-1] + print( + "-> [{}]".format( + util.hexify(data, " ") + if args.show_sensitive_info + else " ".join(["??"] * len(data)) + ) + ) + if revers_msg: + print(revers_msg) + if len(data) != num_bytes: + raise esptool.FatalError( + "Incorrect key file size %d. " + "Key file must be %d bytes (%d bits) of raw binary key data." + % (len(data), num_bytes, num_bytes * 8) + ) + + if keypurpose.startswith("XTS_AES_"): + read_protect = False if args.no_read_protect else True + else: + read_protect = False + write_protect = not args.no_write_protect + + # using efuse instead of a block gives the advantage + # of checking it as the whole field. + efuse.save(data) + + if keypurpose == "XTS_AES_128_KEY": + if efuses["XTS_KEY_LENGTH_256"].get(): + print("\t'XTS_KEY_LENGTH_256' is already '1'") + else: + print("\tXTS_KEY_LENGTH_256 -> 1") + efuses["XTS_KEY_LENGTH_256"].save(1) + + if read_protect: + print("\tDisabling read to key block") + efuse.disable_read() + + if write_protect: + print("\tDisabling write to key block") + efuse.disable_write() + print("") + + if not write_protect: + print("Keys will remain writeable (due to --no-write-protect)") + if args.no_read_protect: + print("Keys will remain readable (due to --no-read-protect)") + + if not efuses.burn_all(check_batch_mode=True): + return + print("Successful") + + +def burn_key_digest(esp, efuses, args): + datafile = args.keyfile + args.keypurpose = ["SECURE_BOOT_DIGEST"] + args.block = ["BLOCK_KEY0"] + digest = espsecure._digest_sbv2_public_key(datafile) + digest = digest[:16] + num_bytes = efuses["BLOCK_KEY0_HI_128"].bit_len // 8 + if len(digest) != num_bytes: + raise esptool.FatalError( + "Incorrect digest size %d. " + "Digest must be %d bytes (%d bits) of raw binary key data." + % (len(digest), num_bytes, num_bytes * 8) + ) + burn_key(esp, efuses, args, digest=[digest]) + + +def espefuse(esp, efuses, args, command): + parser = argparse.ArgumentParser() + subparsers = parser.add_subparsers(dest="operation") + add_commands(subparsers, efuses) + try: + cmd_line_args = parser.parse_args(command.split()) + except SystemExit: + traceback.print_stack() + raise esptool.FatalError('"{}" - incorrect command'.format(command)) + if cmd_line_args.operation == "execute_scripts": + configfiles = cmd_line_args.configfiles + index = cmd_line_args.index + # copy arguments from args to cmd_line_args + vars(cmd_line_args).update(vars(args)) + if cmd_line_args.operation == "execute_scripts": + cmd_line_args.configfiles = configfiles + cmd_line_args.index = index + if cmd_line_args.operation is None: + parser.print_help() + parser.exit(1) + operation_func = globals()[cmd_line_args.operation] + # each 'operation' is a module-level function of the same name + operation_func(esp, efuses, cmd_line_args) + + +def execute_scripts(esp, efuses, args): + efuses.batch_mode_cnt += 1 + del args.operation + scripts = args.scripts + del args.scripts + + for file in scripts: + with open(file.name, "r") as file: + exec(compile(file.read(), file.name, "exec")) + + if args.debug: + for block in efuses.blocks: + data = block.get_bitstring(from_read=False) + block.print_block(data, "regs_for_burn", args.debug) + + efuses.batch_mode_cnt -= 1 + if not efuses.burn_all(check_batch_mode=True): + return + print("Successful") diff --git a/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32c3/__init__.py b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32c3/__init__.py new file mode 100644 index 0000000..a3b55a8 --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32c3/__init__.py @@ -0,0 +1,3 @@ +from . import operations +from .emulate_efuse_controller import EmulateEfuseController +from .fields import EspEfuses diff --git a/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32c3/__pycache__/__init__.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32c3/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..900e22f Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32c3/__pycache__/__init__.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32c3/__pycache__/emulate_efuse_controller.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32c3/__pycache__/emulate_efuse_controller.cpython-312.pyc new file mode 100644 index 0000000..cc40a26 Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32c3/__pycache__/emulate_efuse_controller.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32c3/__pycache__/fields.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32c3/__pycache__/fields.cpython-312.pyc new file mode 100644 index 0000000..9e31441 Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32c3/__pycache__/fields.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32c3/__pycache__/mem_definition.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32c3/__pycache__/mem_definition.cpython-312.pyc new file mode 100644 index 0000000..8c7a124 Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32c3/__pycache__/mem_definition.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32c3/__pycache__/operations.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32c3/__pycache__/operations.cpython-312.pyc new file mode 100644 index 0000000..f540d9f Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32c3/__pycache__/operations.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32c3/emulate_efuse_controller.py b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32c3/emulate_efuse_controller.py new file mode 100644 index 0000000..2d812a1 --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32c3/emulate_efuse_controller.py @@ -0,0 +1,92 @@ +# This file describes eFuses controller for ESP32-C3 chip +# +# SPDX-FileCopyrightText: 2020-2022 Espressif Systems (Shanghai) CO LTD +# +# SPDX-License-Identifier: GPL-2.0-or-later + +import reedsolo + +from .mem_definition import EfuseDefineBlocks, EfuseDefineFields, EfuseDefineRegisters +from ..emulate_efuse_controller_base import EmulateEfuseControllerBase, FatalError + + +class EmulateEfuseController(EmulateEfuseControllerBase): + """The class for virtual efuse operation. Using for HOST_TEST.""" + + CHIP_NAME = "ESP32-C3" + mem = None + debug = False + + def __init__(self, efuse_file=None, debug=False): + self.Blocks = EfuseDefineBlocks + self.Fields = EfuseDefineFields() + self.REGS = EfuseDefineRegisters + super(EmulateEfuseController, self).__init__(efuse_file, debug) + self.write_reg(self.REGS.EFUSE_CMD_REG, 0) + + """ esptool method start >>""" + + def get_major_chip_version(self): + return 0 + + def get_minor_chip_version(self): + return 4 + + def get_crystal_freq(self): + return 40 # MHz (common for all chips) + + def get_security_info(self): + return { + "flags": 0, + "flash_crypt_cnt": 0, + "key_purposes": 0, + "chip_id": 0, + "api_version": 0, + } + + """ << esptool method end """ + + def handle_writing_event(self, addr, value): + if addr == self.REGS.EFUSE_CMD_REG: + if value & self.REGS.EFUSE_PGM_CMD: + self.copy_blocks_wr_regs_to_rd_regs(updated_block=(value >> 2) & 0xF) + self.clean_blocks_wr_regs() + self.check_rd_protection_area() + self.write_reg(addr, 0) + self.write_reg(self.REGS.EFUSE_CMD_REG, 0) + elif value == self.REGS.EFUSE_READ_CMD: + self.write_reg(addr, 0) + self.write_reg(self.REGS.EFUSE_CMD_REG, 0) + self.save_to_file() + + def get_bitlen_of_block(self, blk, wr=False): + if blk.id == 0: + if wr: + return 32 * 8 + else: + return 32 * blk.len + else: + if wr: + rs_coding = 32 * 3 + return 32 * 8 + rs_coding + else: + return 32 * blk.len + + def handle_coding_scheme(self, blk, data): + if blk.id != 0: + # CODING_SCHEME RS applied only for all blocks except BLK0. + coded_bytes = 12 + data.pos = coded_bytes * 8 + plain_data = data.readlist("32*uint:8")[::-1] + # takes 32 bytes + # apply RS encoding + rs = reedsolo.RSCodec(coded_bytes) + # 32 byte of data + 12 bytes RS + calc_encoded_data = list(rs.encode([x for x in plain_data])) + data.pos = 0 + if calc_encoded_data != data.readlist("44*uint:8")[::-1]: + raise FatalError("Error in coding scheme data") + data = data[coded_bytes * 8 :] + if blk.len < 8: + data = data[(8 - blk.len) * 32 :] + return data diff --git a/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32c3/fields.py b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32c3/fields.py new file mode 100644 index 0000000..05914e4 --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32c3/fields.py @@ -0,0 +1,447 @@ +# This file describes eFuses for ESP32-C3 chip +# +# SPDX-FileCopyrightText: 2020-2022 Espressif Systems (Shanghai) CO LTD +# +# SPDX-License-Identifier: GPL-2.0-or-later + +import binascii +import struct +import sys +import time + +from bitstring import BitArray + +import esptool + +import reedsolo + +from .mem_definition import EfuseDefineBlocks, EfuseDefineFields, EfuseDefineRegisters +from .. import base_fields +from .. import util + + +class EfuseBlock(base_fields.EfuseBlockBase): + def len_of_burn_unit(self): + # The writing register window is 8 registers for any blocks. + # len in bytes + return 8 * 4 + + def __init__(self, parent, param, skip_read=False): + parent.read_coding_scheme() + super(EfuseBlock, self).__init__(parent, param, skip_read=skip_read) + + def apply_coding_scheme(self): + data = self.get_raw(from_read=False)[::-1] + if len(data) < self.len_of_burn_unit(): + add_empty_bytes = self.len_of_burn_unit() - len(data) + data = data + (b"\x00" * add_empty_bytes) + if self.get_coding_scheme() == self.parent.REGS.CODING_SCHEME_RS: + # takes 32 bytes + # apply RS encoding + rs = reedsolo.RSCodec(12) + # 32 byte of data + 12 bytes RS + encoded_data = rs.encode([x for x in data]) + words = struct.unpack("<" + "I" * 11, encoded_data) + # returns 11 words (8 words of data + 3 words of RS coding) + else: + # takes 32 bytes + words = struct.unpack("<" + ("I" * (len(data) // 4)), data) + # returns 8 words + return words + + +class EspEfuses(base_fields.EspEfusesBase): + """ + Wrapper object to manage the efuse fields in a connected ESP bootloader + """ + + debug = False + do_not_confirm = False + + def __init__(self, esp, skip_connect=False, debug=False, do_not_confirm=False): + self.Blocks = EfuseDefineBlocks() + self.Fields = EfuseDefineFields() + self.REGS = EfuseDefineRegisters + self.BURN_BLOCK_DATA_NAMES = self.Blocks.get_burn_block_data_names() + self.BLOCKS_FOR_KEYS = self.Blocks.get_blocks_for_keys() + self._esp = esp + self.debug = debug + self.do_not_confirm = do_not_confirm + if esp.CHIP_NAME != "ESP32-C3": + raise esptool.FatalError( + "Expected the 'esp' param for ESP32-C3 chip but got for '%s'." + % (esp.CHIP_NAME) + ) + if not skip_connect: + flags = self._esp.get_security_info()["flags"] + GET_SECURITY_INFO_FLAG_SECURE_DOWNLOAD_ENABLE = 1 << 2 + if flags & GET_SECURITY_INFO_FLAG_SECURE_DOWNLOAD_ENABLE: + raise esptool.FatalError( + "Secure Download Mode is enabled. The tool can not read eFuses." + ) + self.blocks = [ + EfuseBlock(self, self.Blocks.get(block), skip_read=skip_connect) + for block in self.Blocks.BLOCKS + ] + if not skip_connect: + self.get_coding_scheme_warnings() + self.efuses = [EfuseField.convert(self, efuse) for efuse in self.Fields.EFUSES] + self.efuses += [ + EfuseField.convert(self, efuse) for efuse in self.Fields.KEYBLOCKS + ] + if skip_connect: + self.efuses += [ + EfuseField.convert(self, efuse) + for efuse in self.Fields.BLOCK2_CALIBRATION_EFUSES + ] + else: + if self["BLK_VERSION_MAJOR"].get() == 1: + self.efuses += [ + EfuseField.convert(self, efuse) + for efuse in self.Fields.BLOCK2_CALIBRATION_EFUSES + ] + self.efuses += [ + EfuseField.convert(self, efuse) for efuse in self.Fields.CALC + ] + + def __getitem__(self, efuse_name): + """Return the efuse field with the given name""" + for e in self.efuses: + if efuse_name == e.name or any(x == efuse_name for x in e.alt_names): + return e + new_fields = False + for efuse in self.Fields.BLOCK2_CALIBRATION_EFUSES: + if efuse.name == efuse_name or any( + x == efuse_name for x in efuse.alt_names + ): + self.efuses += [ + EfuseField.convert(self, efuse) + for efuse in self.Fields.BLOCK2_CALIBRATION_EFUSES + ] + new_fields = True + if new_fields: + for e in self.efuses: + if efuse_name == e.name or any(x == efuse_name for x in e.alt_names): + return e + raise KeyError + + def read_coding_scheme(self): + self.coding_scheme = self.REGS.CODING_SCHEME_RS + + def print_status_regs(self): + print("") + self.blocks[0].print_block(self.blocks[0].err_bitarray, "err__regs", debug=True) + print( + "{:27} 0x{:08x}".format( + "EFUSE_RD_RS_ERR0_REG", self.read_reg(self.REGS.EFUSE_RD_RS_ERR0_REG) + ) + ) + print( + "{:27} 0x{:08x}".format( + "EFUSE_RD_RS_ERR1_REG", self.read_reg(self.REGS.EFUSE_RD_RS_ERR1_REG) + ) + ) + + def efuse_controller_setup(self): + self.set_efuse_timing() + self.clear_pgm_registers() + self.wait_efuse_idle() + + def write_efuses(self, block): + self.efuse_program(block) + return self.get_coding_scheme_warnings(silent=True) + + def clear_pgm_registers(self): + self.wait_efuse_idle() + for r in range( + self.REGS.EFUSE_PGM_DATA0_REG, self.REGS.EFUSE_PGM_DATA0_REG + 32, 4 + ): + self.write_reg(r, 0) + + def wait_efuse_idle(self): + deadline = time.time() + self.REGS.EFUSE_BURN_TIMEOUT + while time.time() < deadline: + cmds = self.REGS.EFUSE_PGM_CMD | self.REGS.EFUSE_READ_CMD + if self.read_reg(self.REGS.EFUSE_CMD_REG) & cmds == 0: + if self.read_reg(self.REGS.EFUSE_CMD_REG) & cmds == 0: + # Due to a hardware error, we have to read READ_CMD again + # to make sure the efuse clock is normal. + # For PGM_CMD it is not necessary. + return + raise esptool.FatalError( + "Timed out waiting for Efuse controller command to complete" + ) + + def efuse_program(self, block): + self.wait_efuse_idle() + self.write_reg(self.REGS.EFUSE_CONF_REG, self.REGS.EFUSE_WRITE_OP_CODE) + self.write_reg(self.REGS.EFUSE_CMD_REG, self.REGS.EFUSE_PGM_CMD | (block << 2)) + self.wait_efuse_idle() + self.clear_pgm_registers() + self.efuse_read() + + def efuse_read(self): + self.wait_efuse_idle() + self.write_reg(self.REGS.EFUSE_CONF_REG, self.REGS.EFUSE_READ_OP_CODE) + # need to add a delay after triggering EFUSE_READ_CMD, as ROM loader checks some + # efuse registers after each command is completed + # if ENABLE_SECURITY_DOWNLOAD or DIS_DOWNLOAD_MODE is enabled by the current cmd, then we need to try to reconnect to the chip. + try: + self.write_reg( + self.REGS.EFUSE_CMD_REG, self.REGS.EFUSE_READ_CMD, delay_after_us=1000 + ) + self.wait_efuse_idle() + except esptool.FatalError: + secure_download_mode_before = self._esp.secure_download_mode + + try: + self._esp = self.reconnect_chip(self._esp) + except esptool.FatalError: + print("Can not re-connect to the chip") + if not self["DIS_DOWNLOAD_MODE"].get() and self[ + "DIS_DOWNLOAD_MODE" + ].get(from_read=False): + print( + "This is the correct behavior as we are actually burning " + "DIS_DOWNLOAD_MODE which disables the connection to the chip" + ) + print("DIS_DOWNLOAD_MODE is enabled") + print("Successful") + sys.exit(0) # finish without errors + raise + + print("Established a connection with the chip") + if self._esp.secure_download_mode and not secure_download_mode_before: + print("Secure download mode is enabled") + if not self["ENABLE_SECURITY_DOWNLOAD"].get() and self[ + "ENABLE_SECURITY_DOWNLOAD" + ].get(from_read=False): + print( + "espefuse tool can not continue to work in Secure download mode" + ) + print("ENABLE_SECURITY_DOWNLOAD is enabled") + print("Successful") + sys.exit(0) # finish without errors + raise + + def set_efuse_timing(self): + """Set timing registers for burning efuses""" + # Configure clock + apb_freq = self.get_crystal_freq() + if apb_freq != 40: + raise esptool.FatalError( + "The eFuse supports only xtal=40M (xtal was %d)" % apb_freq + ) + + self.update_reg(self.REGS.EFUSE_DAC_CONF_REG, self.REGS.EFUSE_DAC_NUM_M, 0xFF) + self.update_reg( + self.REGS.EFUSE_DAC_CONF_REG, self.REGS.EFUSE_DAC_CLK_DIV_M, 0x28 + ) + self.update_reg( + self.REGS.EFUSE_WR_TIM_CONF1_REG, self.REGS.EFUSE_PWR_ON_NUM_M, 0x3000 + ) + self.update_reg( + self.REGS.EFUSE_WR_TIM_CONF2_REG, self.REGS.EFUSE_PWR_OFF_NUM_M, 0x190 + ) + + def get_coding_scheme_warnings(self, silent=False): + """Check if the coding scheme has detected any errors.""" + ret_fail = False + for block in self.blocks: + if block.id == 0: + words = [ + self.read_reg(self.REGS.EFUSE_RD_REPEAT_ERR0_REG + offs * 4) + for offs in range(5) + ] + block.err_bitarray.pos = 0 + for word in reversed(words): + block.err_bitarray.overwrite(BitArray("uint:32=%d" % word)) + block.num_errors = block.err_bitarray.count(True) + block.fail = block.num_errors != 0 + else: + addr_reg_f, fail_bit = self.REGS.BLOCK_FAIL_BIT[block.id] + if fail_bit is None: + block.fail = False + else: + block.fail = self.read_reg(addr_reg_f) & (1 << fail_bit) != 0 + + addr_reg_n, num_mask, num_offs = self.REGS.BLOCK_NUM_ERRORS[block.id] + if num_mask is None or num_offs is None: + block.num_errors = 0 + else: + block.num_errors = ( + self.read_reg(addr_reg_n) >> num_offs + ) & num_mask + + ret_fail |= block.fail + if not silent and (block.fail or block.num_errors): + print( + "Error(s) in BLOCK%d [ERRORS:%d FAIL:%d]" + % (block.id, block.num_errors, block.fail) + ) + if (self.debug or ret_fail) and not silent: + self.print_status_regs() + return ret_fail + + def summary(self): + # TODO add support set_flash_voltage - "Flash voltage (VDD_SPI)" + return "" + + +class EfuseField(base_fields.EfuseFieldBase): + @staticmethod + def convert(parent, efuse): + return { + "mac": EfuseMacField, + "keypurpose": EfuseKeyPurposeField, + "t_sensor": EfuseTempSensor, + "adc_tp": EfuseAdcPointCalibration, + "wafer": EfuseWafer, + }.get(efuse.class_type, EfuseField)(parent, efuse) + + +class EfuseWafer(EfuseField): + def get(self, from_read=True): + hi_bits = self.parent["WAFER_VERSION_MINOR_HI"].get(from_read) + assert self.parent["WAFER_VERSION_MINOR_HI"].bit_len == 1 + lo_bits = self.parent["WAFER_VERSION_MINOR_LO"].get(from_read) + assert self.parent["WAFER_VERSION_MINOR_LO"].bit_len == 3 + return (hi_bits << 3) + lo_bits + + def save(self, new_value): + raise esptool.FatalError("Burning %s is not supported" % self.name) + + +class EfuseTempSensor(EfuseField): + def get(self, from_read=True): + value = self.get_bitstring(from_read) + sig = -1 if value[0] else 1 + return sig * value[1:].uint * 0.1 + + +class EfuseAdcPointCalibration(EfuseField): + def get(self, from_read=True): + STEP_SIZE = 4 + value = self.get_bitstring(from_read) + sig = -1 if value[0] else 1 + return sig * value[1:].uint * STEP_SIZE + + +class EfuseMacField(EfuseField): + def check_format(self, new_value_str): + if new_value_str is None: + raise esptool.FatalError( + "Required MAC Address in AA:CD:EF:01:02:03 format!" + ) + if new_value_str.count(":") != 5: + raise esptool.FatalError( + "MAC Address needs to be a 6-byte hexadecimal format " + "separated by colons (:)!" + ) + hexad = new_value_str.replace(":", "") + if len(hexad) != 12: + raise esptool.FatalError( + "MAC Address needs to be a 6-byte hexadecimal number " + "(12 hexadecimal characters)!" + ) + # order of bytearray = b'\xaa\xcd\xef\x01\x02\x03', + bindata = binascii.unhexlify(hexad) + # unicast address check according to + # https://tools.ietf.org/html/rfc7042#section-2.1 + if esptool.util.byte(bindata, 0) & 0x01: + raise esptool.FatalError("Custom MAC must be a unicast MAC!") + return bindata + + def check(self): + errs, fail = self.parent.get_block_errors(self.block) + if errs != 0 or fail: + output = "Block%d has ERRORS:%d FAIL:%d" % (self.block, errs, fail) + else: + output = "OK" + return "(" + output + ")" + + def get(self, from_read=True): + if self.name == "CUSTOM_MAC": + mac = self.get_raw(from_read)[::-1] + else: + mac = self.get_raw(from_read) + return "%s %s" % (util.hexify(mac, ":"), self.check()) + + def save(self, new_value): + def print_field(e, new_value): + print( + " - '{}' ({}) {} -> {}".format( + e.name, e.description, e.get_bitstring(), new_value + ) + ) + + if self.name == "CUSTOM_MAC": + bitarray_mac = self.convert_to_bitstring(new_value) + print_field(self, bitarray_mac) + super(EfuseMacField, self).save(new_value) + else: + # Writing the BLOCK1 (MAC_SPI_8M_0) default MAC is not possible, + # as it's written in the factory. + raise esptool.FatalError("Writing Factory MAC address is not supported") + + +# fmt: off +class EfuseKeyPurposeField(EfuseField): + KEY_PURPOSES = [ + ("USER", 0, None, None, "no_need_rd_protect"), # User purposes (software-only use) + ("RESERVED", 1, None, None, "no_need_rd_protect"), # Reserved + ("XTS_AES_128_KEY", 4, None, "Reverse", "need_rd_protect"), # XTS_AES_128_KEY (flash/PSRAM encryption) + ("HMAC_DOWN_ALL", 5, None, None, "need_rd_protect"), # HMAC Downstream mode + ("HMAC_DOWN_JTAG", 6, None, None, "need_rd_protect"), # JTAG soft enable key (uses HMAC Downstream mode) + ("HMAC_DOWN_DIGITAL_SIGNATURE", 7, None, None, "need_rd_protect"), # Digital Signature peripheral key (uses HMAC Downstream mode) + ("HMAC_UP", 8, None, None, "need_rd_protect"), # HMAC Upstream mode + ("SECURE_BOOT_DIGEST0", 9, "DIGEST", None, "no_need_rd_protect"), # SECURE_BOOT_DIGEST0 (Secure Boot key digest) + ("SECURE_BOOT_DIGEST1", 10, "DIGEST", None, "no_need_rd_protect"), # SECURE_BOOT_DIGEST1 (Secure Boot key digest) + ("SECURE_BOOT_DIGEST2", 11, "DIGEST", None, "no_need_rd_protect"), # SECURE_BOOT_DIGEST2 (Secure Boot key digest) + ] +# fmt: on + KEY_PURPOSES_NAME = [name[0] for name in KEY_PURPOSES] + DIGEST_KEY_PURPOSES = [name[0] for name in KEY_PURPOSES if name[2] == "DIGEST"] + + def check_format(self, new_value_str): + # str convert to int: "XTS_AES_128_KEY" - > str(4) + # if int: 4 -> str(4) + raw_val = new_value_str + for purpose_name in self.KEY_PURPOSES: + if purpose_name[0] == new_value_str: + raw_val = str(purpose_name[1]) + break + if raw_val.isdigit(): + if int(raw_val) not in [p[1] for p in self.KEY_PURPOSES if p[1] > 0]: + raise esptool.FatalError("'%s' can not be set (value out of range)" % raw_val) + else: + raise esptool.FatalError("'%s' unknown name" % raw_val) + return raw_val + + def need_reverse(self, new_key_purpose): + for key in self.KEY_PURPOSES: + if key[0] == new_key_purpose: + return key[3] == "Reverse" + + def need_rd_protect(self, new_key_purpose): + for key in self.KEY_PURPOSES: + if key[0] == new_key_purpose: + return key[4] == "need_rd_protect" + + def get(self, from_read=True): + for p in self.KEY_PURPOSES: + if p[1] == self.get_raw(from_read): + return p[0] + return "FORBIDDEN_STATE" + + def get_name(self, raw_val): + for key in self.KEY_PURPOSES: + if key[1] == raw_val: + return key[0] + + def save(self, new_value): + raw_val = int(self.check_format(str(new_value))) + str_new_value = self.get_name(raw_val) + if self.name == "KEY_PURPOSE_5" and str_new_value.startswith("XTS_AES"): + raise esptool.FatalError(f"{self.name} can not have {str_new_value} key due to a hardware bug (please see TRM for more details)") + return super(EfuseKeyPurposeField, self).save(raw_val) diff --git a/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32c3/mem_definition.py b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32c3/mem_definition.py new file mode 100644 index 0000000..610d64e --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32c3/mem_definition.py @@ -0,0 +1,189 @@ +# This file describes eFuses fields and registers for ESP32-C3 chip +# +# SPDX-FileCopyrightText: 2020-2022 Espressif Systems (Shanghai) CO LTD +# +# SPDX-License-Identifier: GPL-2.0-or-later + +import os + +import yaml + +from ..mem_definition_base import ( + EfuseBlocksBase, + EfuseFieldsBase, + EfuseRegistersBase, + Field, +) + + +class EfuseDefineRegisters(EfuseRegistersBase): + EFUSE_MEM_SIZE = 0x01FC + 4 + + # EFUSE registers & command/conf values + DR_REG_EFUSE_BASE = 0x60008800 + EFUSE_PGM_DATA0_REG = DR_REG_EFUSE_BASE + EFUSE_CHECK_VALUE0_REG = DR_REG_EFUSE_BASE + 0x020 + EFUSE_CLK_REG = DR_REG_EFUSE_BASE + 0x1C8 + EFUSE_CONF_REG = DR_REG_EFUSE_BASE + 0x1CC + EFUSE_STATUS_REG = DR_REG_EFUSE_BASE + 0x1D0 + EFUSE_CMD_REG = DR_REG_EFUSE_BASE + 0x1D4 + EFUSE_RD_RS_ERR0_REG = DR_REG_EFUSE_BASE + 0x1C0 + EFUSE_RD_RS_ERR1_REG = DR_REG_EFUSE_BASE + 0x1C4 + EFUSE_RD_REPEAT_ERR0_REG = DR_REG_EFUSE_BASE + 0x17C + EFUSE_RD_REPEAT_ERR1_REG = DR_REG_EFUSE_BASE + 0x180 + EFUSE_RD_REPEAT_ERR2_REG = DR_REG_EFUSE_BASE + 0x184 + EFUSE_RD_REPEAT_ERR3_REG = DR_REG_EFUSE_BASE + 0x188 + EFUSE_RD_REPEAT_ERR4_REG = DR_REG_EFUSE_BASE + 0x18C + EFUSE_DAC_CONF_REG = DR_REG_EFUSE_BASE + 0x1E8 + EFUSE_RD_TIM_CONF_REG = DR_REG_EFUSE_BASE + 0x1EC + EFUSE_WR_TIM_CONF1_REG = DR_REG_EFUSE_BASE + 0x1F0 + EFUSE_WR_TIM_CONF2_REG = DR_REG_EFUSE_BASE + 0x1F4 + EFUSE_DATE_REG = DR_REG_EFUSE_BASE + 0x1FC + EFUSE_WRITE_OP_CODE = 0x5A5A + EFUSE_READ_OP_CODE = 0x5AA5 + EFUSE_PGM_CMD_MASK = 0x3 + EFUSE_PGM_CMD = 0x2 + EFUSE_READ_CMD = 0x1 + + # this chip has a design error so fail_bit is shifted by one block but err_num is in the correct place + BLOCK_FAIL_BIT = [ + # error_reg, fail_bit + (EFUSE_RD_REPEAT_ERR0_REG, None), # BLOCK0 + (EFUSE_RD_RS_ERR0_REG, 7), # MAC_SPI_8M_0 + (EFUSE_RD_RS_ERR0_REG, 11), # BLOCK_SYS_DATA + (EFUSE_RD_RS_ERR0_REG, 15), # BLOCK_USR_DATA + (EFUSE_RD_RS_ERR0_REG, 19), # BLOCK_KEY0 + (EFUSE_RD_RS_ERR0_REG, 23), # BLOCK_KEY1 + (EFUSE_RD_RS_ERR0_REG, 27), # BLOCK_KEY2 + (EFUSE_RD_RS_ERR0_REG, 31), # BLOCK_KEY3 + (EFUSE_RD_RS_ERR1_REG, 3), # BLOCK_KEY4 + (EFUSE_RD_RS_ERR1_REG, 7), # BLOCK_KEY5 + (EFUSE_RD_RS_ERR1_REG, None), # BLOCK_SYS_DATA2 + ] + + BLOCK_NUM_ERRORS = [ + # error_reg, err_num_mask, err_num_offs + (EFUSE_RD_REPEAT_ERR0_REG, None, None), # BLOCK0 + (EFUSE_RD_RS_ERR0_REG, 0x7, 0), # MAC_SPI_8M_0 + (EFUSE_RD_RS_ERR0_REG, 0x7, 4), # BLOCK_SYS_DATA + (EFUSE_RD_RS_ERR0_REG, 0x7, 8), # BLOCK_USR_DATA + (EFUSE_RD_RS_ERR0_REG, 0x7, 12), # BLOCK_KEY0 + (EFUSE_RD_RS_ERR0_REG, 0x7, 16), # BLOCK_KEY1 + (EFUSE_RD_RS_ERR0_REG, 0x7, 20), # BLOCK_KEY2 + (EFUSE_RD_RS_ERR0_REG, 0x7, 24), # BLOCK_KEY3 + (EFUSE_RD_RS_ERR0_REG, 0x7, 28), # BLOCK_KEY4 + (EFUSE_RD_RS_ERR1_REG, 0x7, 0), # BLOCK_KEY5 + (EFUSE_RD_RS_ERR1_REG, 0x7, 4), # BLOCK_SYS_DATA2 + ] + + # EFUSE_WR_TIM_CONF2_REG + EFUSE_PWR_OFF_NUM_S = 0 + EFUSE_PWR_OFF_NUM_M = 0xFFFF << EFUSE_PWR_OFF_NUM_S + + # EFUSE_WR_TIM_CONF1_REG + EFUSE_PWR_ON_NUM_S = 8 + EFUSE_PWR_ON_NUM_M = 0x0000FFFF << EFUSE_PWR_ON_NUM_S + + # EFUSE_DAC_CONF_REG + EFUSE_DAC_CLK_DIV_S = 0 + EFUSE_DAC_CLK_DIV_M = 0xFF << EFUSE_DAC_CLK_DIV_S + + # EFUSE_DAC_CONF_REG + EFUSE_DAC_NUM_S = 9 + EFUSE_DAC_NUM_M = 0xFF << EFUSE_DAC_NUM_S + + +class EfuseDefineBlocks(EfuseBlocksBase): + __base_rd_regs = EfuseDefineRegisters.DR_REG_EFUSE_BASE + __base_wr_regs = EfuseDefineRegisters.EFUSE_PGM_DATA0_REG + # List of efuse blocks + # fmt: off + BLOCKS = [ + # Name, Alias, Index, Read address, Write address, Write protect bit, Read protect bit, Len, key_purpose + ("BLOCK0", [], 0, __base_rd_regs + 0x02C, __base_wr_regs, None, None, 6, None), + ("MAC_SPI_8M_0", ["BLOCK1"], 1, __base_rd_regs + 0x044, __base_wr_regs, 20, None, 6, None), + ("BLOCK_SYS_DATA", ["BLOCK2"], 2, __base_rd_regs + 0x05C, __base_wr_regs, 21, None, 8, None), + ("BLOCK_USR_DATA", ["BLOCK3"], 3, __base_rd_regs + 0x07C, __base_wr_regs, 22, None, 8, None), + ("BLOCK_KEY0", ["BLOCK4"], 4, __base_rd_regs + 0x09C, __base_wr_regs, 23, 0, 8, "KEY_PURPOSE_0"), + ("BLOCK_KEY1", ["BLOCK5"], 5, __base_rd_regs + 0x0BC, __base_wr_regs, 24, 1, 8, "KEY_PURPOSE_1"), + ("BLOCK_KEY2", ["BLOCK6"], 6, __base_rd_regs + 0x0DC, __base_wr_regs, 25, 2, 8, "KEY_PURPOSE_2"), + ("BLOCK_KEY3", ["BLOCK7"], 7, __base_rd_regs + 0x0FC, __base_wr_regs, 26, 3, 8, "KEY_PURPOSE_3"), + ("BLOCK_KEY4", ["BLOCK8"], 8, __base_rd_regs + 0x11C, __base_wr_regs, 27, 4, 8, "KEY_PURPOSE_4"), + ("BLOCK_KEY5", ["BLOCK9"], 9, __base_rd_regs + 0x13C, __base_wr_regs, 28, 5, 8, "KEY_PURPOSE_5"), + ("BLOCK_SYS_DATA2", ["BLOCK10"], 10, __base_rd_regs + 0x15C, __base_wr_regs, 29, 6, 8, None), + ] + # fmt: on + + def get_burn_block_data_names(self): + list_of_names = [] + for block in self.BLOCKS: + blk = self.get(block) + if blk.name: + list_of_names.append(blk.name) + if blk.alias: + for alias in blk.alias: + list_of_names.append(alias) + return list_of_names + + +class EfuseDefineFields(EfuseFieldsBase): + def __init__(self) -> None: + # List of efuse fields from TRM the chapter eFuse Controller. + self.EFUSES = [] + + self.KEYBLOCKS = [] + + # if BLK_VERSION_MAJOR is 1, these efuse fields are in BLOCK2 + self.BLOCK2_CALIBRATION_EFUSES = [] + + self.CALC = [] + + dir_name = os.path.dirname(os.path.abspath(__file__)) + dir_name, file_name = os.path.split(dir_name) + file_name = file_name + ".yaml" + dir_name, _ = os.path.split(dir_name) + efuse_file = os.path.join(dir_name, "efuse_defs", file_name) + with open(f"{efuse_file}", "r") as r_file: + e_desc = yaml.safe_load(r_file) + super().__init__(e_desc) + + for i, efuse in enumerate(self.ALL_EFUSES): + if efuse.name in [ + "BLOCK_USR_DATA", + "BLOCK_KEY0", + "BLOCK_KEY1", + "BLOCK_KEY2", + "BLOCK_KEY3", + "BLOCK_KEY4", + "BLOCK_KEY5", + "BLOCK_SYS_DATA2", + ]: + if efuse.name == "BLOCK_USR_DATA": + efuse.bit_len = 256 + efuse.type = "bytes:32" + self.KEYBLOCKS.append(efuse) + self.ALL_EFUSES[i] = None + + elif efuse.category == "calibration": + self.BLOCK2_CALIBRATION_EFUSES.append(efuse) + self.ALL_EFUSES[i] = None + + # It is not functional, a bug in the hardware + elif efuse.name == "JTAG_SEL_ENABLE": + self.ALL_EFUSES[i] = None + + f = Field() + f.name = "WAFER_VERSION_MINOR" + f.block = 0 + f.bit_len = 4 + f.type = f"uint:{f.bit_len}" + f.category = "identity" + f.class_type = "wafer" + f.description = "calc WAFER VERSION MINOR = WAFER_VERSION_MINOR_HI << 3 + WAFER_VERSION_MINOR_LO (read only)" + self.CALC.append(f) + + for efuse in self.ALL_EFUSES: + if efuse is not None: + self.EFUSES.append(efuse) + + self.ALL_EFUSES = [] diff --git a/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32c3/operations.py b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32c3/operations.py new file mode 100644 index 0000000..59f4469 --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32c3/operations.py @@ -0,0 +1,422 @@ +# This file includes the operations with eFuses for ESP32-C3 chip +# +# SPDX-FileCopyrightText: 2020-2022 Espressif Systems (Shanghai) CO LTD +# +# SPDX-License-Identifier: GPL-2.0-or-later + +import argparse +import os # noqa: F401. It is used in IDF scripts +import traceback + +import espsecure + +import esptool + +from . import fields +from .. import util +from ..base_operations import ( + add_common_commands, + add_force_write_always, + add_show_sensitive_info_option, + burn_bit, + burn_block_data, + burn_efuse, + check_error, + dump, + read_protect_efuse, + summary, + write_protect_efuse, +) + + +def protect_options(p): + p.add_argument( + "--no-write-protect", + help="Disable write-protecting of the key. The key remains writable. " + "(The keys use the RS coding scheme that does not support " + "post-write data changes. Forced write can damage RS encoding bits.) " + "The write-protecting of keypurposes does not depend on the option, " + "it will be set anyway.", + action="store_true", + ) + p.add_argument( + "--no-read-protect", + help="Disable read-protecting of the key. The key remains readable software." + "The key with keypurpose[USER, RESERVED and *_DIGEST] " + "will remain readable anyway. For the rest keypurposes the read-protection " + "will be defined the option (Read-protect by default).", + action="store_true", + ) + + +def add_commands(subparsers, efuses): + add_common_commands(subparsers, efuses) + burn_key = subparsers.add_parser( + "burn_key", help="Burn the key block with the specified name" + ) + protect_options(burn_key) + add_force_write_always(burn_key) + add_show_sensitive_info_option(burn_key) + burn_key.add_argument( + "block", + help="Key block to burn", + action="append", + choices=efuses.BLOCKS_FOR_KEYS, + ) + burn_key.add_argument( + "keyfile", + help="File containing 256 bits of binary key data", + action="append", + type=argparse.FileType("rb"), + ) + burn_key.add_argument( + "keypurpose", + help="Purpose to set.", + action="append", + choices=fields.EfuseKeyPurposeField.KEY_PURPOSES_NAME, + ) + for _ in efuses.BLOCKS_FOR_KEYS: + burn_key.add_argument( + "block", + help="Key block to burn", + nargs="?", + action="append", + metavar="BLOCK", + choices=efuses.BLOCKS_FOR_KEYS, + ) + burn_key.add_argument( + "keyfile", + help="File containing 256 bits of binary key data", + nargs="?", + action="append", + metavar="KEYFILE", + type=argparse.FileType("rb"), + ) + burn_key.add_argument( + "keypurpose", + help="Purpose to set.", + nargs="?", + action="append", + metavar="KEYPURPOSE", + choices=fields.EfuseKeyPurposeField.KEY_PURPOSES_NAME, + ) + + burn_key_digest = subparsers.add_parser( + "burn_key_digest", + help="Parse a RSA public key and burn the digest to key efuse block", + ) + protect_options(burn_key_digest) + add_force_write_always(burn_key_digest) + add_show_sensitive_info_option(burn_key_digest) + burn_key_digest.add_argument( + "block", + help="Key block to burn", + action="append", + choices=efuses.BLOCKS_FOR_KEYS, + ) + burn_key_digest.add_argument( + "keyfile", + help="Key file to digest (PEM format)", + action="append", + type=argparse.FileType("rb"), + ) + burn_key_digest.add_argument( + "keypurpose", + help="Purpose to set.", + action="append", + choices=fields.EfuseKeyPurposeField.DIGEST_KEY_PURPOSES, + ) + for _ in efuses.BLOCKS_FOR_KEYS: + burn_key_digest.add_argument( + "block", + help="Key block to burn", + nargs="?", + action="append", + metavar="BLOCK", + choices=efuses.BLOCKS_FOR_KEYS, + ) + burn_key_digest.add_argument( + "keyfile", + help="Key file to digest (PEM format)", + nargs="?", + action="append", + metavar="KEYFILE", + type=argparse.FileType("rb"), + ) + burn_key_digest.add_argument( + "keypurpose", + help="Purpose to set.", + nargs="?", + action="append", + metavar="KEYPURPOSE", + choices=fields.EfuseKeyPurposeField.DIGEST_KEY_PURPOSES, + ) + + p = subparsers.add_parser( + "set_flash_voltage", + help="Permanently set the internal flash voltage regulator " + "to either 1.8V, 3.3V or OFF. " + "This means GPIO45 can be high or low at reset without " + "changing the flash voltage.", + ) + p.add_argument("voltage", help="Voltage selection", choices=["1.8V", "3.3V", "OFF"]) + + p = subparsers.add_parser( + "burn_custom_mac", help="Burn a 48-bit Custom MAC Address to EFUSE BLOCK3." + ) + p.add_argument( + "mac", + help="Custom MAC Address to burn given in hexadecimal format with bytes " + "separated by colons (e.g. AA:CD:EF:01:02:03).", + type=fields.base_fields.CheckArgValue(efuses, "CUSTOM_MAC"), + ) + add_force_write_always(p) + + p = subparsers.add_parser("get_custom_mac", help="Prints the Custom MAC Address.") + + +def burn_custom_mac(esp, efuses, args): + efuses["CUSTOM_MAC"].save(args.mac) + if not efuses.burn_all(check_batch_mode=True): + return + get_custom_mac(esp, efuses, args) + print("Successful") + + +def get_custom_mac(esp, efuses, args): + print("Custom MAC Address: {}".format(efuses["CUSTOM_MAC"].get())) + + +def set_flash_voltage(esp, efuses, args): + raise esptool.FatalError("set_flash_voltage is not supported!") + + +def adc_info(esp, efuses, args): + print("") + # fmt: off + if efuses["BLK_VERSION_MAJOR"].get() == 1: + print("Temperature Sensor Calibration = {}C".format(efuses["TEMP_SENSOR_CAL"].get())) + + print("") + print("ADC1 readings stored in efuse BLOCK2:") + print(" MODE0 D1 reading (250mV): {}".format(efuses["ADC1_MODE0_D1"].get())) + print(" MODE0 D2 reading (600mV): {}".format(efuses["ADC1_MODE0_D2"].get())) + + print(" MODE1 D1 reading (250mV): {}".format(efuses["ADC1_MODE1_D1"].get())) + print(" MODE1 D2 reading (800mV): {}".format(efuses["ADC1_MODE1_D2"].get())) + + print(" MODE2 D1 reading (250mV): {}".format(efuses["ADC1_MODE2_D1"].get())) + print(" MODE2 D2 reading (1000mV): {}".format(efuses["ADC1_MODE2_D2"].get())) + + print(" MODE3 D1 reading (250mV): {}".format(efuses["ADC1_MODE3_D1"].get())) + print(" MODE3 D2 reading (2000mV): {}".format(efuses["ADC1_MODE3_D2"].get())) + + print("") + print("ADC2 readings stored in efuse BLOCK2:") + print(" MODE0 D1 reading (250mV): {}".format(efuses["ADC2_MODE0_D1"].get())) + print(" MODE0 D2 reading (600mV): {}".format(efuses["ADC2_MODE0_D2"].get())) + + print(" MODE1 D1 reading (250mV): {}".format(efuses["ADC2_MODE1_D1"].get())) + print(" MODE1 D2 reading (800mV): {}".format(efuses["ADC2_MODE1_D2"].get())) + + print(" MODE2 D1 reading (250mV): {}".format(efuses["ADC2_MODE2_D1"].get())) + print(" MODE2 D2 reading (1000mV): {}".format(efuses["ADC2_MODE2_D2"].get())) + + print(" MODE3 D1 reading (250mV): {}".format(efuses["ADC2_MODE3_D1"].get())) + print(" MODE3 D2 reading (2000mV): {}".format(efuses["ADC2_MODE3_D2"].get())) + else: + print("BLK_VERSION_MAJOR = {}".format(efuses["BLK_VERSION_MAJOR"].get_meaning())) + # fmt: on + + +def burn_key(esp, efuses, args, digest=None): + if digest is None: + datafile_list = args.keyfile[ + 0 : len([name for name in args.keyfile if name is not None]) : + ] + else: + datafile_list = digest[0 : len([name for name in digest if name is not None]) :] + efuses.force_write_always = args.force_write_always + block_name_list = args.block[ + 0 : len([name for name in args.block if name is not None]) : + ] + keypurpose_list = args.keypurpose[ + 0 : len([name for name in args.keypurpose if name is not None]) : + ] + + util.check_duplicate_name_in_list(block_name_list) + if len(block_name_list) != len(datafile_list) or len(block_name_list) != len( + keypurpose_list + ): + raise esptool.FatalError( + "The number of blocks (%d), datafile (%d) and keypurpose (%d) " + "should be the same." + % (len(block_name_list), len(datafile_list), len(keypurpose_list)) + ) + + print("Burn keys to blocks:") + for block_name, datafile, keypurpose in zip( + block_name_list, datafile_list, keypurpose_list + ): + efuse = None + for block in efuses.blocks: + if block_name == block.name or block_name in block.alias: + efuse = efuses[block.name] + if efuse is None: + raise esptool.FatalError("Unknown block name - %s" % (block_name)) + num_bytes = efuse.bit_len // 8 + + block_num = efuses.get_index_block_by_name(block_name) + block = efuses.blocks[block_num] + + if digest is None: + data = datafile.read() + else: + data = datafile + + print(" - %s" % (efuse.name), end=" ") + revers_msg = None + if efuses[block.key_purpose_name].need_reverse(keypurpose): + revers_msg = "\tReversing byte order for AES-XTS hardware peripheral" + data = data[::-1] + print( + "-> [{}]".format( + util.hexify(data, " ") + if args.show_sensitive_info + else " ".join(["??"] * len(data)) + ) + ) + if revers_msg: + print(revers_msg) + if len(data) != num_bytes: + raise esptool.FatalError( + "Incorrect key file size %d. Key file must be %d bytes (%d bits) " + "of raw binary key data." % (len(data), num_bytes, num_bytes * 8) + ) + + if efuses[block.key_purpose_name].need_rd_protect(keypurpose): + read_protect = False if args.no_read_protect else True + else: + read_protect = False + write_protect = not args.no_write_protect + + # using efuse instead of a block gives the advantage of checking it as the whole field. + efuse.save(data) + + disable_wr_protect_key_purpose = False + if efuses[block.key_purpose_name].get() != keypurpose: + if efuses[block.key_purpose_name].is_writeable(): + print( + "\t'%s': '%s' -> '%s'." + % ( + block.key_purpose_name, + efuses[block.key_purpose_name].get(), + keypurpose, + ) + ) + efuses[block.key_purpose_name].save(keypurpose) + disable_wr_protect_key_purpose = True + else: + raise esptool.FatalError( + "It is not possible to change '%s' to '%s' " + "because write protection bit is set." + % (block.key_purpose_name, keypurpose) + ) + else: + print("\t'%s' is already '%s'." % (block.key_purpose_name, keypurpose)) + if efuses[block.key_purpose_name].is_writeable(): + disable_wr_protect_key_purpose = True + + if disable_wr_protect_key_purpose: + print("\tDisabling write to '%s'." % block.key_purpose_name) + efuses[block.key_purpose_name].disable_write() + + if read_protect: + print("\tDisabling read to key block") + efuse.disable_read() + + if write_protect: + print("\tDisabling write to key block") + efuse.disable_write() + print("") + + if not write_protect: + print("Keys will remain writeable (due to --no-write-protect)") + if args.no_read_protect: + print("Keys will remain readable (due to --no-read-protect)") + + if not efuses.burn_all(check_batch_mode=True): + return + print("Successful") + + +def burn_key_digest(esp, efuses, args): + digest_list = [] + datafile_list = args.keyfile[ + 0 : len([name for name in args.keyfile if name is not None]) : + ] + block_list = args.block[ + 0 : len([block for block in args.block if block is not None]) : + ] + for block_name, datafile in zip(block_list, datafile_list): + efuse = None + for block in efuses.blocks: + if block_name == block.name or block_name in block.alias: + efuse = efuses[block.name] + if efuse is None: + raise esptool.FatalError("Unknown block name - %s" % (block_name)) + num_bytes = efuse.bit_len // 8 + digest = espsecure._digest_sbv2_public_key(datafile) + if len(digest) != num_bytes: + raise esptool.FatalError( + "Incorrect digest size %d. Digest must be %d bytes (%d bits) " + "of raw binary key data." % (len(digest), num_bytes, num_bytes * 8) + ) + digest_list.append(digest) + burn_key(esp, efuses, args, digest=digest_list) + + +def espefuse(esp, efuses, args, command): + parser = argparse.ArgumentParser() + subparsers = parser.add_subparsers(dest="operation") + add_commands(subparsers, efuses) + try: + cmd_line_args = parser.parse_args(command.split()) + except SystemExit: + traceback.print_stack() + raise esptool.FatalError('"{}" - incorrect command'.format(command)) + if cmd_line_args.operation == "execute_scripts": + configfiles = cmd_line_args.configfiles + index = cmd_line_args.index + # copy arguments from args to cmd_line_args + vars(cmd_line_args).update(vars(args)) + if cmd_line_args.operation == "execute_scripts": + cmd_line_args.configfiles = configfiles + cmd_line_args.index = index + if cmd_line_args.operation is None: + parser.print_help() + parser.exit(1) + operation_func = globals()[cmd_line_args.operation] + # each 'operation' is a module-level function of the same name + operation_func(esp, efuses, cmd_line_args) + + +def execute_scripts(esp, efuses, args): + efuses.batch_mode_cnt += 1 + del args.operation + scripts = args.scripts + del args.scripts + + for file in scripts: + with open(file.name, "r") as file: + exec(compile(file.read(), file.name, "exec")) + + if args.debug: + for block in efuses.blocks: + data = block.get_bitstring(from_read=False) + block.print_block(data, "regs_for_burn", args.debug) + + efuses.batch_mode_cnt -= 1 + if not efuses.burn_all(check_batch_mode=True): + return + print("Successful") diff --git a/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32c6/__init__.py b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32c6/__init__.py new file mode 100644 index 0000000..a3b55a8 --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32c6/__init__.py @@ -0,0 +1,3 @@ +from . import operations +from .emulate_efuse_controller import EmulateEfuseController +from .fields import EspEfuses diff --git a/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32c6/__pycache__/__init__.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32c6/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..01f54c7 Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32c6/__pycache__/__init__.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32c6/__pycache__/emulate_efuse_controller.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32c6/__pycache__/emulate_efuse_controller.cpython-312.pyc new file mode 100644 index 0000000..552cab2 Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32c6/__pycache__/emulate_efuse_controller.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32c6/__pycache__/fields.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32c6/__pycache__/fields.cpython-312.pyc new file mode 100644 index 0000000..f4a678a Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32c6/__pycache__/fields.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32c6/__pycache__/mem_definition.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32c6/__pycache__/mem_definition.cpython-312.pyc new file mode 100644 index 0000000..cdb39b9 Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32c6/__pycache__/mem_definition.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32c6/__pycache__/operations.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32c6/__pycache__/operations.cpython-312.pyc new file mode 100644 index 0000000..4977924 Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32c6/__pycache__/operations.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32c6/emulate_efuse_controller.py b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32c6/emulate_efuse_controller.py new file mode 100644 index 0000000..a9e7d4d --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32c6/emulate_efuse_controller.py @@ -0,0 +1,92 @@ +# This file describes eFuses controller for ESP32-C6 chip +# +# SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD +# +# SPDX-License-Identifier: GPL-2.0-or-later + +import reedsolo + +from .mem_definition import EfuseDefineBlocks, EfuseDefineFields, EfuseDefineRegisters +from ..emulate_efuse_controller_base import EmulateEfuseControllerBase, FatalError + + +class EmulateEfuseController(EmulateEfuseControllerBase): + """The class for virtual efuse operation. Using for HOST_TEST.""" + + CHIP_NAME = "ESP32-C6" + mem = None + debug = False + + def __init__(self, efuse_file=None, debug=False): + self.Blocks = EfuseDefineBlocks + self.Fields = EfuseDefineFields() + self.REGS = EfuseDefineRegisters + super(EmulateEfuseController, self).__init__(efuse_file, debug) + self.write_reg(self.REGS.EFUSE_CMD_REG, 0) + + """ esptool method start >>""" + + def get_major_chip_version(self): + return 0 + + def get_minor_chip_version(self): + return 0 + + def get_crystal_freq(self): + return 40 # MHz (common for all chips) + + def get_security_info(self): + return { + "flags": 0, + "flash_crypt_cnt": 0, + "key_purposes": 0, + "chip_id": 0, + "api_version": 0, + } + + """ << esptool method end """ + + def handle_writing_event(self, addr, value): + if addr == self.REGS.EFUSE_CMD_REG: + if value & self.REGS.EFUSE_PGM_CMD: + self.copy_blocks_wr_regs_to_rd_regs(updated_block=(value >> 2) & 0xF) + self.clean_blocks_wr_regs() + self.check_rd_protection_area() + self.write_reg(addr, 0) + self.write_reg(self.REGS.EFUSE_CMD_REG, 0) + elif value == self.REGS.EFUSE_READ_CMD: + self.write_reg(addr, 0) + self.write_reg(self.REGS.EFUSE_CMD_REG, 0) + self.save_to_file() + + def get_bitlen_of_block(self, blk, wr=False): + if blk.id == 0: + if wr: + return 32 * 8 + else: + return 32 * blk.len + else: + if wr: + rs_coding = 32 * 3 + return 32 * 8 + rs_coding + else: + return 32 * blk.len + + def handle_coding_scheme(self, blk, data): + if blk.id != 0: + # CODING_SCHEME RS applied only for all blocks except BLK0. + coded_bytes = 12 + data.pos = coded_bytes * 8 + plain_data = data.readlist("32*uint:8")[::-1] + # takes 32 bytes + # apply RS encoding + rs = reedsolo.RSCodec(coded_bytes) + # 32 byte of data + 12 bytes RS + calc_encoded_data = list(rs.encode([x for x in plain_data])) + data.pos = 0 + if calc_encoded_data != data.readlist("44*uint:8")[::-1]: + raise FatalError("Error in coding scheme data") + data = data[coded_bytes * 8 :] + if blk.len < 8: + data = data[(8 - blk.len) * 32 :] + return data diff --git a/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32c6/fields.py b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32c6/fields.py new file mode 100644 index 0000000..5e0a449 --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32c6/fields.py @@ -0,0 +1,456 @@ +# This file describes eFuses for ESP32-C6 chip +# +# SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD +# +# SPDX-License-Identifier: GPL-2.0-or-later + +import binascii +import struct +import sys +import time + +from bitstring import BitArray + +import esptool + +import reedsolo + +from .mem_definition import EfuseDefineBlocks, EfuseDefineFields, EfuseDefineRegisters +from .. import base_fields +from .. import util + + +class EfuseBlock(base_fields.EfuseBlockBase): + def len_of_burn_unit(self): + # The writing register window is 8 registers for any blocks. + # len in bytes + return 8 * 4 + + def __init__(self, parent, param, skip_read=False): + parent.read_coding_scheme() + super(EfuseBlock, self).__init__(parent, param, skip_read=skip_read) + + def apply_coding_scheme(self): + data = self.get_raw(from_read=False)[::-1] + if len(data) < self.len_of_burn_unit(): + add_empty_bytes = self.len_of_burn_unit() - len(data) + data = data + (b"\x00" * add_empty_bytes) + if self.get_coding_scheme() == self.parent.REGS.CODING_SCHEME_RS: + # takes 32 bytes + # apply RS encoding + rs = reedsolo.RSCodec(12) + # 32 byte of data + 12 bytes RS + encoded_data = rs.encode([x for x in data]) + words = struct.unpack("<" + "I" * 11, encoded_data) + # returns 11 words (8 words of data + 3 words of RS coding) + else: + # takes 32 bytes + words = struct.unpack("<" + ("I" * (len(data) // 4)), data) + # returns 8 words + return words + + +class EspEfuses(base_fields.EspEfusesBase): + """ + Wrapper object to manage the efuse fields in a connected ESP bootloader + """ + + debug = False + do_not_confirm = False + + def __init__(self, esp, skip_connect=False, debug=False, do_not_confirm=False): + self.Blocks = EfuseDefineBlocks() + self.Fields = EfuseDefineFields() + self.REGS = EfuseDefineRegisters + self.BURN_BLOCK_DATA_NAMES = self.Blocks.get_burn_block_data_names() + self.BLOCKS_FOR_KEYS = self.Blocks.get_blocks_for_keys() + self._esp = esp + self.debug = debug + self.do_not_confirm = do_not_confirm + if esp.CHIP_NAME != "ESP32-C6": + raise esptool.FatalError( + "Expected the 'esp' param for ESP32-C6 chip but got for '%s'." + % (esp.CHIP_NAME) + ) + if not skip_connect: + flags = self._esp.get_security_info()["flags"] + GET_SECURITY_INFO_FLAG_SECURE_DOWNLOAD_ENABLE = 1 << 2 + if flags & GET_SECURITY_INFO_FLAG_SECURE_DOWNLOAD_ENABLE: + raise esptool.FatalError( + "Secure Download Mode is enabled. The tool can not read eFuses." + ) + self.blocks = [ + EfuseBlock(self, self.Blocks.get(block), skip_read=skip_connect) + for block in self.Blocks.BLOCKS + ] + if not skip_connect: + self.get_coding_scheme_warnings() + self.efuses = [EfuseField.convert(self, efuse) for efuse in self.Fields.EFUSES] + self.efuses += [ + EfuseField.convert(self, efuse) for efuse in self.Fields.KEYBLOCKS + ] + if skip_connect: + self.efuses += [ + EfuseField.convert(self, efuse) + for efuse in self.Fields.BLOCK2_CALIBRATION_EFUSES + ] + else: + if self["BLK_VERSION_MINOR"].get() == 1: + self.efuses += [ + EfuseField.convert(self, efuse) + for efuse in self.Fields.BLOCK2_CALIBRATION_EFUSES + ] + self.efuses += [ + EfuseField.convert(self, efuse) for efuse in self.Fields.CALC + ] + + def __getitem__(self, efuse_name): + """Return the efuse field with the given name""" + for e in self.efuses: + if efuse_name == e.name or any(x == efuse_name for x in e.alt_names): + return e + new_fields = False + for efuse in self.Fields.BLOCK2_CALIBRATION_EFUSES: + if efuse.name == efuse_name or any( + x == efuse_name for x in efuse.alt_names + ): + self.efuses += [ + EfuseField.convert(self, efuse) + for efuse in self.Fields.BLOCK2_CALIBRATION_EFUSES + ] + new_fields = True + if new_fields: + for e in self.efuses: + if efuse_name == e.name or any(x == efuse_name for x in e.alt_names): + return e + raise KeyError + + def read_coding_scheme(self): + self.coding_scheme = self.REGS.CODING_SCHEME_RS + + def print_status_regs(self): + print("") + self.blocks[0].print_block(self.blocks[0].err_bitarray, "err__regs", debug=True) + print( + "{:27} 0x{:08x}".format( + "EFUSE_RD_RS_ERR0_REG", self.read_reg(self.REGS.EFUSE_RD_RS_ERR0_REG) + ) + ) + print( + "{:27} 0x{:08x}".format( + "EFUSE_RD_RS_ERR1_REG", self.read_reg(self.REGS.EFUSE_RD_RS_ERR1_REG) + ) + ) + + def efuse_controller_setup(self): + self.set_efuse_timing() + self.clear_pgm_registers() + self.wait_efuse_idle() + + def write_efuses(self, block): + self.efuse_program(block) + return self.get_coding_scheme_warnings(silent=True) + + def clear_pgm_registers(self): + self.wait_efuse_idle() + for r in range( + self.REGS.EFUSE_PGM_DATA0_REG, self.REGS.EFUSE_PGM_DATA0_REG + 32, 4 + ): + self.write_reg(r, 0) + + def wait_efuse_idle(self): + deadline = time.time() + self.REGS.EFUSE_BURN_TIMEOUT + while time.time() < deadline: + cmds = self.REGS.EFUSE_PGM_CMD | self.REGS.EFUSE_READ_CMD + if self.read_reg(self.REGS.EFUSE_CMD_REG) & cmds == 0: + if self.read_reg(self.REGS.EFUSE_CMD_REG) & cmds == 0: + # Due to a hardware error, we have to read READ_CMD again + # to make sure the efuse clock is normal. + # For PGM_CMD it is not necessary. + return + raise esptool.FatalError( + "Timed out waiting for Efuse controller command to complete" + ) + + def efuse_program(self, block): + self.wait_efuse_idle() + self.write_reg(self.REGS.EFUSE_CONF_REG, self.REGS.EFUSE_WRITE_OP_CODE) + self.write_reg(self.REGS.EFUSE_CMD_REG, self.REGS.EFUSE_PGM_CMD | (block << 2)) + self.wait_efuse_idle() + self.clear_pgm_registers() + self.efuse_read() + + def efuse_read(self): + self.wait_efuse_idle() + self.write_reg(self.REGS.EFUSE_CONF_REG, self.REGS.EFUSE_READ_OP_CODE) + # need to add a delay after triggering EFUSE_READ_CMD, as ROM loader checks some + # efuse registers after each command is completed + # if ENABLE_SECURITY_DOWNLOAD or DIS_DOWNLOAD_MODE is enabled by the current cmd, then we need to try to reconnect to the chip. + try: + self.write_reg( + self.REGS.EFUSE_CMD_REG, self.REGS.EFUSE_READ_CMD, delay_after_us=1000 + ) + self.wait_efuse_idle() + except esptool.FatalError: + secure_download_mode_before = self._esp.secure_download_mode + + try: + self._esp = self.reconnect_chip(self._esp) + except esptool.FatalError: + print("Can not re-connect to the chip") + if not self["DIS_DOWNLOAD_MODE"].get() and self[ + "DIS_DOWNLOAD_MODE" + ].get(from_read=False): + print( + "This is the correct behavior as we are actually burning " + "DIS_DOWNLOAD_MODE which disables the connection to the chip" + ) + print("DIS_DOWNLOAD_MODE is enabled") + print("Successful") + sys.exit(0) # finish without errors + raise + + print("Established a connection with the chip") + if self._esp.secure_download_mode and not secure_download_mode_before: + print("Secure download mode is enabled") + if not self["ENABLE_SECURITY_DOWNLOAD"].get() and self[ + "ENABLE_SECURITY_DOWNLOAD" + ].get(from_read=False): + print( + "espefuse tool can not continue to work in Secure download mode" + ) + print("ENABLE_SECURITY_DOWNLOAD is enabled") + print("Successful") + sys.exit(0) # finish without errors + raise + + def set_efuse_timing(self): + """Set timing registers for burning efuses""" + # Configure clock + apb_freq = self.get_crystal_freq() + if apb_freq != 40: + raise esptool.FatalError( + "The eFuse supports only xtal=40M (xtal was %d)" % apb_freq + ) + + self.update_reg(self.REGS.EFUSE_DAC_CONF_REG, self.REGS.EFUSE_DAC_NUM_M, 0xFF) + self.update_reg( + self.REGS.EFUSE_DAC_CONF_REG, self.REGS.EFUSE_DAC_CLK_DIV_M, 0x28 + ) + self.update_reg( + self.REGS.EFUSE_WR_TIM_CONF1_REG, self.REGS.EFUSE_PWR_ON_NUM_M, 0x3000 + ) + self.update_reg( + self.REGS.EFUSE_WR_TIM_CONF2_REG, self.REGS.EFUSE_PWR_OFF_NUM_M, 0x190 + ) + + def get_coding_scheme_warnings(self, silent=False): + """Check if the coding scheme has detected any errors.""" + old_addr_reg = 0 + reg_value = 0 + ret_fail = False + for block in self.blocks: + if block.id == 0: + words = [ + self.read_reg(self.REGS.EFUSE_RD_REPEAT_ERR0_REG + offs * 4) + for offs in range(5) + ] + block.err_bitarray.pos = 0 + for word in reversed(words): + block.err_bitarray.overwrite(BitArray("uint:32=%d" % word)) + block.num_errors = block.err_bitarray.count(True) + block.fail = block.num_errors != 0 + else: + addr_reg, err_num_mask, err_num_offs, fail_bit = self.REGS.BLOCK_ERRORS[ + block.id + ] + if err_num_mask is None or err_num_offs is None or fail_bit is None: + continue + if addr_reg != old_addr_reg: + old_addr_reg = addr_reg + reg_value = self.read_reg(addr_reg) + block.fail = reg_value & (1 << fail_bit) != 0 + block.num_errors = (reg_value >> err_num_offs) & err_num_mask + ret_fail |= block.fail + if not silent and (block.fail or block.num_errors): + print( + "Error(s) in BLOCK%d [ERRORS:%d FAIL:%d]" + % (block.id, block.num_errors, block.fail) + ) + if (self.debug or ret_fail) and not silent: + self.print_status_regs() + return ret_fail + + def summary(self): + # TODO add support set_flash_voltage - "Flash voltage (VDD_SPI)" + return "" + + +class EfuseField(base_fields.EfuseFieldBase): + @staticmethod + def convert(parent, efuse): + return { + "mac": EfuseMacField, + "keypurpose": EfuseKeyPurposeField, + "t_sensor": EfuseTempSensor, + "adc_tp": EfuseAdcPointCalibration, + "wafer": EfuseWafer, + }.get(efuse.class_type, EfuseField)(parent, efuse) + + +class EfuseWafer(EfuseField): + def get(self, from_read=True): + hi_bits = self.parent["WAFER_VERSION_MINOR_HI"].get(from_read) + assert self.parent["WAFER_VERSION_MINOR_HI"].bit_len == 1 + lo_bits = self.parent["WAFER_VERSION_MINOR_LO"].get(from_read) + assert self.parent["WAFER_VERSION_MINOR_LO"].bit_len == 3 + return (hi_bits << 3) + lo_bits + + def save(self, new_value): + raise esptool.FatalError("Burning %s is not supported" % self.name) + + +class EfuseTempSensor(EfuseField): + def get(self, from_read=True): + value = self.get_bitstring(from_read) + sig = -1 if value[0] else 1 + return sig * value[1:].uint * 0.1 + + +class EfuseAdcPointCalibration(EfuseField): + def get(self, from_read=True): + STEP_SIZE = 4 + value = self.get_bitstring(from_read) + sig = -1 if value[0] else 1 + return sig * value[1:].uint * STEP_SIZE + + +class EfuseMacField(EfuseField): + def check_format(self, new_value_str): + if new_value_str is None: + raise esptool.FatalError( + "Required MAC Address in AA:CD:EF:01:02:03 format!" + ) + num_bytes = 8 if self.name == "MAC_EUI64" else 6 + if new_value_str.count(":") != num_bytes - 1: + raise esptool.FatalError( + f"MAC Address needs to be a {num_bytes}-byte hexadecimal format " + "separated by colons (:)!" + ) + hexad = new_value_str.replace(":", "").split(" ", 1)[0] + hexad = hexad.split(" ", 1)[0] if self.is_field_calculated() else hexad + if len(hexad) != num_bytes * 2: + raise esptool.FatalError( + f"MAC Address needs to be a {num_bytes}-byte hexadecimal number " + f"({num_bytes * 2} hexadecimal characters)!" + ) + # order of bytearray = b'\xaa\xcd\xef\x01\x02\x03', + bindata = binascii.unhexlify(hexad) + + if not self.is_field_calculated(): + # unicast address check according to + # https://tools.ietf.org/html/rfc7042#section-2.1 + if esptool.util.byte(bindata, 0) & 0x01: + raise esptool.FatalError("Custom MAC must be a unicast MAC!") + return bindata + + def check(self): + errs, fail = self.parent.get_block_errors(self.block) + if errs != 0 or fail: + output = "Block%d has ERRORS:%d FAIL:%d" % (self.block, errs, fail) + else: + output = "OK" + return "(" + output + ")" + + def get(self, from_read=True): + if self.name == "CUSTOM_MAC": + mac = self.get_raw(from_read)[::-1] + elif self.name == "MAC": + mac = self.get_raw(from_read) + elif self.name == "MAC_EUI64": + mac = self.parent["MAC"].get_bitstring(from_read).copy() + mac_ext = self.parent["MAC_EXT"].get_bitstring(from_read) + mac.insert(mac_ext, 24) + mac = mac.bytes + else: + mac = self.get_raw(from_read) + return "%s %s" % (util.hexify(mac, ":"), self.check()) + + def save(self, new_value): + def print_field(e, new_value): + print( + " - '{}' ({}) {} -> {}".format( + e.name, e.description, e.get_bitstring(), new_value + ) + ) + + if self.name == "CUSTOM_MAC": + bitarray_mac = self.convert_to_bitstring(new_value) + print_field(self, bitarray_mac) + super(EfuseMacField, self).save(new_value) + else: + # Writing the BLOCK1 (MAC_SPI_8M_0) default MAC is not possible, + # as it's written in the factory. + raise esptool.FatalError(f"Burning {self.name} is not supported") + + +# fmt: off +class EfuseKeyPurposeField(EfuseField): + KEY_PURPOSES = [ + ("USER", 0, None, None, "no_need_rd_protect"), # User purposes (software-only use) + ("RESERVED", 1, None, None, "no_need_rd_protect"), # Reserved + ("XTS_AES_128_KEY", 4, None, "Reverse", "need_rd_protect"), # XTS_AES_128_KEY (flash/PSRAM encryption) + ("HMAC_DOWN_ALL", 5, None, None, "need_rd_protect"), # HMAC Downstream mode + ("HMAC_DOWN_JTAG", 6, None, None, "need_rd_protect"), # JTAG soft enable key (uses HMAC Downstream mode) + ("HMAC_DOWN_DIGITAL_SIGNATURE", 7, None, None, "need_rd_protect"), # Digital Signature peripheral key (uses HMAC Downstream mode) + ("HMAC_UP", 8, None, None, "need_rd_protect"), # HMAC Upstream mode + ("SECURE_BOOT_DIGEST0", 9, "DIGEST", None, "no_need_rd_protect"), # SECURE_BOOT_DIGEST0 (Secure Boot key digest) + ("SECURE_BOOT_DIGEST1", 10, "DIGEST", None, "no_need_rd_protect"), # SECURE_BOOT_DIGEST1 (Secure Boot key digest) + ("SECURE_BOOT_DIGEST2", 11, "DIGEST", None, "no_need_rd_protect"), # SECURE_BOOT_DIGEST2 (Secure Boot key digest) + ] +# fmt: on + KEY_PURPOSES_NAME = [name[0] for name in KEY_PURPOSES] + DIGEST_KEY_PURPOSES = [name[0] for name in KEY_PURPOSES if name[2] == "DIGEST"] + + def check_format(self, new_value_str): + # str convert to int: "XTS_AES_128_KEY" - > str(4) + # if int: 4 -> str(4) + raw_val = new_value_str + for purpose_name in self.KEY_PURPOSES: + if purpose_name[0] == new_value_str: + raw_val = str(purpose_name[1]) + break + if raw_val.isdigit(): + if int(raw_val) not in [p[1] for p in self.KEY_PURPOSES if p[1] > 0]: + raise esptool.FatalError("'%s' can not be set (value out of range)" % raw_val) + else: + raise esptool.FatalError("'%s' unknown name" % raw_val) + return raw_val + + def need_reverse(self, new_key_purpose): + for key in self.KEY_PURPOSES: + if key[0] == new_key_purpose: + return key[3] == "Reverse" + + def need_rd_protect(self, new_key_purpose): + for key in self.KEY_PURPOSES: + if key[0] == new_key_purpose: + return key[4] == "need_rd_protect" + + def get(self, from_read=True): + for p in self.KEY_PURPOSES: + if p[1] == self.get_raw(from_read): + return p[0] + return "FORBIDDEN_STATE" + + def get_name(self, raw_val): + for key in self.KEY_PURPOSES: + if key[1] == raw_val: + return key[0] + + def save(self, new_value): + raw_val = int(self.check_format(str(new_value))) + str_new_value = self.get_name(raw_val) + if self.name == "KEY_PURPOSE_5" and str_new_value.startswith("XTS_AES"): + raise esptool.FatalError(f"{self.name} can not have {str_new_value} key due to a hardware bug (please see TRM for more details)") + return super(EfuseKeyPurposeField, self).save(raw_val) diff --git a/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32c6/mem_definition.py b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32c6/mem_definition.py new file mode 100644 index 0000000..25e4caf --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32c6/mem_definition.py @@ -0,0 +1,169 @@ +# This file describes eFuses fields and registers for ESP32-C6 chip +# +# SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD +# +# SPDX-License-Identifier: GPL-2.0-or-later + +import os + +import yaml + +from ..mem_definition_base import ( + EfuseBlocksBase, + EfuseFieldsBase, + EfuseRegistersBase, + Field, +) + + +class EfuseDefineRegisters(EfuseRegistersBase): + EFUSE_MEM_SIZE = 0x01FC + 4 + + # EFUSE registers & command/conf values + DR_REG_EFUSE_BASE = 0x600B0800 + EFUSE_PGM_DATA0_REG = DR_REG_EFUSE_BASE + EFUSE_CHECK_VALUE0_REG = DR_REG_EFUSE_BASE + 0x020 + EFUSE_CLK_REG = DR_REG_EFUSE_BASE + 0x1C8 + EFUSE_CONF_REG = DR_REG_EFUSE_BASE + 0x1CC + EFUSE_STATUS_REG = DR_REG_EFUSE_BASE + 0x1D0 + EFUSE_CMD_REG = DR_REG_EFUSE_BASE + 0x1D4 + EFUSE_RD_RS_ERR0_REG = DR_REG_EFUSE_BASE + 0x1C0 + EFUSE_RD_RS_ERR1_REG = DR_REG_EFUSE_BASE + 0x1C4 + EFUSE_RD_REPEAT_ERR0_REG = DR_REG_EFUSE_BASE + 0x17C + EFUSE_RD_REPEAT_ERR1_REG = DR_REG_EFUSE_BASE + 0x180 + EFUSE_RD_REPEAT_ERR2_REG = DR_REG_EFUSE_BASE + 0x184 + EFUSE_RD_REPEAT_ERR3_REG = DR_REG_EFUSE_BASE + 0x188 + EFUSE_RD_REPEAT_ERR4_REG = DR_REG_EFUSE_BASE + 0x18C + EFUSE_DAC_CONF_REG = DR_REG_EFUSE_BASE + 0x1E8 + EFUSE_RD_TIM_CONF_REG = DR_REG_EFUSE_BASE + 0x1EC + EFUSE_WR_TIM_CONF1_REG = DR_REG_EFUSE_BASE + 0x1F0 + EFUSE_WR_TIM_CONF2_REG = DR_REG_EFUSE_BASE + 0x1F4 + EFUSE_DATE_REG = DR_REG_EFUSE_BASE + 0x1FC + EFUSE_WRITE_OP_CODE = 0x5A5A + EFUSE_READ_OP_CODE = 0x5AA5 + EFUSE_PGM_CMD_MASK = 0x3 + EFUSE_PGM_CMD = 0x2 + EFUSE_READ_CMD = 0x1 + + BLOCK_ERRORS = [ + # error_reg, err_num_mask, err_num_offs, fail_bit + (EFUSE_RD_REPEAT_ERR0_REG, None, None, None), # BLOCK0 + (EFUSE_RD_RS_ERR0_REG, 0x7, 0, 3), # MAC_SPI_8M_0 + (EFUSE_RD_RS_ERR0_REG, 0x7, 4, 7), # BLOCK_SYS_DATA + (EFUSE_RD_RS_ERR0_REG, 0x7, 8, 11), # BLOCK_USR_DATA + (EFUSE_RD_RS_ERR0_REG, 0x7, 12, 15), # BLOCK_KEY0 + (EFUSE_RD_RS_ERR0_REG, 0x7, 16, 19), # BLOCK_KEY1 + (EFUSE_RD_RS_ERR0_REG, 0x7, 20, 23), # BLOCK_KEY2 + (EFUSE_RD_RS_ERR0_REG, 0x7, 24, 27), # BLOCK_KEY3 + (EFUSE_RD_RS_ERR0_REG, 0x7, 28, 31), # BLOCK_KEY4 + (EFUSE_RD_RS_ERR1_REG, 0x7, 0, 3), # BLOCK_KEY5 + (EFUSE_RD_RS_ERR1_REG, 0x7, 4, 7), # BLOCK_SYS_DATA2 + ] + + # EFUSE_WR_TIM_CONF2_REG + EFUSE_PWR_OFF_NUM_S = 0 + EFUSE_PWR_OFF_NUM_M = 0xFFFF << EFUSE_PWR_OFF_NUM_S + + # EFUSE_WR_TIM_CONF1_REG + EFUSE_PWR_ON_NUM_S = 8 + EFUSE_PWR_ON_NUM_M = 0x0000FFFF << EFUSE_PWR_ON_NUM_S + + # EFUSE_DAC_CONF_REG + EFUSE_DAC_CLK_DIV_S = 0 + EFUSE_DAC_CLK_DIV_M = 0xFF << EFUSE_DAC_CLK_DIV_S + + # EFUSE_DAC_CONF_REG + EFUSE_DAC_NUM_S = 9 + EFUSE_DAC_NUM_M = 0xFF << EFUSE_DAC_NUM_S + + +class EfuseDefineBlocks(EfuseBlocksBase): + __base_rd_regs = EfuseDefineRegisters.DR_REG_EFUSE_BASE + __base_wr_regs = EfuseDefineRegisters.EFUSE_PGM_DATA0_REG + # List of efuse blocks + # fmt: off + BLOCKS = [ + # Name, Alias, Index, Read address, Write address, Write protect bit, Read protect bit, Len, key_purpose + ("BLOCK0", [], 0, __base_rd_regs + 0x02C, __base_wr_regs, None, None, 6, None), + ("MAC_SPI_8M_0", ["BLOCK1"], 1, __base_rd_regs + 0x044, __base_wr_regs, 20, None, 6, None), + ("BLOCK_SYS_DATA", ["BLOCK2"], 2, __base_rd_regs + 0x05C, __base_wr_regs, 21, None, 8, None), + ("BLOCK_USR_DATA", ["BLOCK3"], 3, __base_rd_regs + 0x07C, __base_wr_regs, 22, None, 8, None), + ("BLOCK_KEY0", ["BLOCK4"], 4, __base_rd_regs + 0x09C, __base_wr_regs, 23, 0, 8, "KEY_PURPOSE_0"), + ("BLOCK_KEY1", ["BLOCK5"], 5, __base_rd_regs + 0x0BC, __base_wr_regs, 24, 1, 8, "KEY_PURPOSE_1"), + ("BLOCK_KEY2", ["BLOCK6"], 6, __base_rd_regs + 0x0DC, __base_wr_regs, 25, 2, 8, "KEY_PURPOSE_2"), + ("BLOCK_KEY3", ["BLOCK7"], 7, __base_rd_regs + 0x0FC, __base_wr_regs, 26, 3, 8, "KEY_PURPOSE_3"), + ("BLOCK_KEY4", ["BLOCK8"], 8, __base_rd_regs + 0x11C, __base_wr_regs, 27, 4, 8, "KEY_PURPOSE_4"), + ("BLOCK_KEY5", ["BLOCK9"], 9, __base_rd_regs + 0x13C, __base_wr_regs, 28, 5, 8, "KEY_PURPOSE_5"), + ("BLOCK_SYS_DATA2", ["BLOCK10"], 10, __base_rd_regs + 0x15C, __base_wr_regs, 29, 6, 8, None), + ] + # fmt: on + + def get_burn_block_data_names(self): + list_of_names = [] + for block in self.BLOCKS: + blk = self.get(block) + if blk.name: + list_of_names.append(blk.name) + if blk.alias: + for alias in blk.alias: + list_of_names.append(alias) + return list_of_names + + +class EfuseDefineFields(EfuseFieldsBase): + def __init__(self) -> None: + # List of efuse fields from TRM the chapter eFuse Controller. + self.EFUSES = [] + + self.KEYBLOCKS = [] + + # if BLK_VERSION_MINOR is 1, these efuse fields are in BLOCK2 + self.BLOCK2_CALIBRATION_EFUSES = [] + + self.CALC = [] + + dir_name = os.path.dirname(os.path.abspath(__file__)) + dir_name, file_name = os.path.split(dir_name) + file_name = file_name + ".yaml" + dir_name, _ = os.path.split(dir_name) + efuse_file = os.path.join(dir_name, "efuse_defs", file_name) + with open(f"{efuse_file}", "r") as r_file: + e_desc = yaml.safe_load(r_file) + super().__init__(e_desc) + + for i, efuse in enumerate(self.ALL_EFUSES): + if efuse.name in [ + "BLOCK_USR_DATA", + "BLOCK_KEY0", + "BLOCK_KEY1", + "BLOCK_KEY2", + "BLOCK_KEY3", + "BLOCK_KEY4", + "BLOCK_KEY5", + "BLOCK_SYS_DATA2", + ]: + if efuse.name == "BLOCK_USR_DATA": + efuse.bit_len = 256 + efuse.type = "bytes:32" + self.KEYBLOCKS.append(efuse) + self.ALL_EFUSES[i] = None + + elif efuse.category == "calibration": + self.BLOCK2_CALIBRATION_EFUSES.append(efuse) + self.ALL_EFUSES[i] = None + + f = Field() + f.name = "MAC_EUI64" + f.block = 1 + f.bit_len = 64 + f.type = f"bytes:{f.bit_len // 8}" + f.category = "MAC" + f.class_type = "mac" + f.description = "calc MAC_EUI64 = MAC[0]:MAC[1]:MAC[2]:MAC_EXT[0]:MAC_EXT[1]:MAC[3]:MAC[4]:MAC[5]" + self.CALC.append(f) + + for efuse in self.ALL_EFUSES: + if efuse is not None: + self.EFUSES.append(efuse) + + self.ALL_EFUSES = [] diff --git a/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32c6/operations.py b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32c6/operations.py new file mode 100644 index 0000000..f8e450f --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32c6/operations.py @@ -0,0 +1,413 @@ +# This file includes the operations with eFuses for ESP32-C6 chip +# +# SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD +# +# SPDX-License-Identifier: GPL-2.0-or-later + +import argparse +import os # noqa: F401. It is used in IDF scripts +import traceback + +import espsecure + +import esptool + +from . import fields +from .. import util +from ..base_operations import ( + add_common_commands, + add_force_write_always, + add_show_sensitive_info_option, + burn_bit, + burn_block_data, + burn_efuse, + check_error, + dump, + read_protect_efuse, + summary, + write_protect_efuse, +) + + +def protect_options(p): + p.add_argument( + "--no-write-protect", + help="Disable write-protecting of the key. The key remains writable. " + "(The keys use the RS coding scheme that does not support " + "post-write data changes. Forced write can damage RS encoding bits.) " + "The write-protecting of keypurposes does not depend on the option, " + "it will be set anyway.", + action="store_true", + ) + p.add_argument( + "--no-read-protect", + help="Disable read-protecting of the key. The key remains readable software." + "The key with keypurpose[USER, RESERVED and *_DIGEST] " + "will remain readable anyway. For the rest keypurposes the read-protection " + "will be defined the option (Read-protect by default).", + action="store_true", + ) + + +def add_commands(subparsers, efuses): + add_common_commands(subparsers, efuses) + burn_key = subparsers.add_parser( + "burn_key", help="Burn the key block with the specified name" + ) + protect_options(burn_key) + add_force_write_always(burn_key) + add_show_sensitive_info_option(burn_key) + burn_key.add_argument( + "block", + help="Key block to burn", + action="append", + choices=efuses.BLOCKS_FOR_KEYS, + ) + burn_key.add_argument( + "keyfile", + help="File containing 256 bits of binary key data", + action="append", + type=argparse.FileType("rb"), + ) + burn_key.add_argument( + "keypurpose", + help="Purpose to set.", + action="append", + choices=fields.EfuseKeyPurposeField.KEY_PURPOSES_NAME, + ) + for _ in efuses.BLOCKS_FOR_KEYS: + burn_key.add_argument( + "block", + help="Key block to burn", + nargs="?", + action="append", + metavar="BLOCK", + choices=efuses.BLOCKS_FOR_KEYS, + ) + burn_key.add_argument( + "keyfile", + help="File containing 256 bits of binary key data", + nargs="?", + action="append", + metavar="KEYFILE", + type=argparse.FileType("rb"), + ) + burn_key.add_argument( + "keypurpose", + help="Purpose to set.", + nargs="?", + action="append", + metavar="KEYPURPOSE", + choices=fields.EfuseKeyPurposeField.KEY_PURPOSES_NAME, + ) + + burn_key_digest = subparsers.add_parser( + "burn_key_digest", + help="Parse a RSA public key and burn the digest to key efuse block", + ) + protect_options(burn_key_digest) + add_force_write_always(burn_key_digest) + add_show_sensitive_info_option(burn_key_digest) + burn_key_digest.add_argument( + "block", + help="Key block to burn", + action="append", + choices=efuses.BLOCKS_FOR_KEYS, + ) + burn_key_digest.add_argument( + "keyfile", + help="Key file to digest (PEM format)", + action="append", + type=argparse.FileType("rb"), + ) + burn_key_digest.add_argument( + "keypurpose", + help="Purpose to set.", + action="append", + choices=fields.EfuseKeyPurposeField.DIGEST_KEY_PURPOSES, + ) + for _ in efuses.BLOCKS_FOR_KEYS: + burn_key_digest.add_argument( + "block", + help="Key block to burn", + nargs="?", + action="append", + metavar="BLOCK", + choices=efuses.BLOCKS_FOR_KEYS, + ) + burn_key_digest.add_argument( + "keyfile", + help="Key file to digest (PEM format)", + nargs="?", + action="append", + metavar="KEYFILE", + type=argparse.FileType("rb"), + ) + burn_key_digest.add_argument( + "keypurpose", + help="Purpose to set.", + nargs="?", + action="append", + metavar="KEYPURPOSE", + choices=fields.EfuseKeyPurposeField.DIGEST_KEY_PURPOSES, + ) + + p = subparsers.add_parser( + "set_flash_voltage", + help="Permanently set the internal flash voltage regulator " + "to either 1.8V, 3.3V or OFF. " + "This means GPIO45 can be high or low at reset without " + "changing the flash voltage.", + ) + p.add_argument("voltage", help="Voltage selection", choices=["1.8V", "3.3V", "OFF"]) + + p = subparsers.add_parser( + "burn_custom_mac", help="Burn a 48-bit Custom MAC Address to EFUSE BLOCK3." + ) + p.add_argument( + "mac", + help="Custom MAC Address to burn given in hexadecimal format with bytes " + "separated by colons (e.g. AA:CD:EF:01:02:03).", + type=fields.base_fields.CheckArgValue(efuses, "CUSTOM_MAC"), + ) + add_force_write_always(p) + + p = subparsers.add_parser("get_custom_mac", help="Prints the Custom MAC Address.") + + +def burn_custom_mac(esp, efuses, args): + efuses["CUSTOM_MAC"].save(args.mac) + if not efuses.burn_all(check_batch_mode=True): + return + get_custom_mac(esp, efuses, args) + print("Successful") + + +def get_custom_mac(esp, efuses, args): + print("Custom MAC Address: {}".format(efuses["CUSTOM_MAC"].get())) + + +def set_flash_voltage(esp, efuses, args): + raise esptool.FatalError("set_flash_voltage is not supported!") + + +def adc_info(esp, efuses, args): + print("") + # fmt: off + if efuses["BLK_VERSION_MINOR"].get() == 1: + print("Temperature Sensor Calibration = {}C".format(efuses["TEMP_CALIB"].get())) + + print("") + print("ADC1 Calibration data stored in efuse BLOCK2:") + print(f"OCODE: {efuses['OCODE'].get()}") + print(f"INIT_CODE_ATTEN0: {efuses['ADC1_INIT_CODE_ATTEN0'].get()}") + print(f"INIT_CODE_ATTEN1: {efuses['ADC1_INIT_CODE_ATTEN1'].get()}") + print(f"INIT_CODE_ATTEN2: {efuses['ADC1_INIT_CODE_ATTEN2'].get()}") + print(f"INIT_CODE_ATTEN3: {efuses['ADC1_INIT_CODE_ATTEN3'].get()}") + print(f"CAL_VOL_ATTEN0: {efuses['ADC1_CAL_VOL_ATTEN0'].get()}") + print(f"CAL_VOL_ATTEN1: {efuses['ADC1_CAL_VOL_ATTEN1'].get()}") + print(f"CAL_VOL_ATTEN2: {efuses['ADC1_CAL_VOL_ATTEN2'].get()}") + print(f"CAL_VOL_ATTEN3: {efuses['ADC1_CAL_VOL_ATTEN3'].get()}") + print(f"INIT_CODE_ATTEN0_CH0: {efuses['ADC1_INIT_CODE_ATTEN0_CH0'].get()}") + print(f"INIT_CODE_ATTEN0_CH1: {efuses['ADC1_INIT_CODE_ATTEN0_CH1'].get()}") + print(f"INIT_CODE_ATTEN0_CH2: {efuses['ADC1_INIT_CODE_ATTEN0_CH2'].get()}") + print(f"INIT_CODE_ATTEN0_CH3: {efuses['ADC1_INIT_CODE_ATTEN0_CH3'].get()}") + print(f"INIT_CODE_ATTEN0_CH4: {efuses['ADC1_INIT_CODE_ATTEN0_CH4'].get()}") + print(f"INIT_CODE_ATTEN0_CH5: {efuses['ADC1_INIT_CODE_ATTEN0_CH5'].get()}") + print(f"INIT_CODE_ATTEN0_CH6: {efuses['ADC1_INIT_CODE_ATTEN0_CH6'].get()}") + else: + print("BLK_VERSION_MINOR = {}".format(efuses["BLK_VERSION_MINOR"].get_meaning())) + # fmt: on + + +def burn_key(esp, efuses, args, digest=None): + if digest is None: + datafile_list = args.keyfile[ + 0 : len([name for name in args.keyfile if name is not None]) : + ] + else: + datafile_list = digest[0 : len([name for name in digest if name is not None]) :] + efuses.force_write_always = args.force_write_always + block_name_list = args.block[ + 0 : len([name for name in args.block if name is not None]) : + ] + keypurpose_list = args.keypurpose[ + 0 : len([name for name in args.keypurpose if name is not None]) : + ] + + util.check_duplicate_name_in_list(block_name_list) + if len(block_name_list) != len(datafile_list) or len(block_name_list) != len( + keypurpose_list + ): + raise esptool.FatalError( + "The number of blocks (%d), datafile (%d) and keypurpose (%d) " + "should be the same." + % (len(block_name_list), len(datafile_list), len(keypurpose_list)) + ) + + print("Burn keys to blocks:") + for block_name, datafile, keypurpose in zip( + block_name_list, datafile_list, keypurpose_list + ): + efuse = None + for block in efuses.blocks: + if block_name == block.name or block_name in block.alias: + efuse = efuses[block.name] + if efuse is None: + raise esptool.FatalError("Unknown block name - %s" % (block_name)) + num_bytes = efuse.bit_len // 8 + + block_num = efuses.get_index_block_by_name(block_name) + block = efuses.blocks[block_num] + + if digest is None: + data = datafile.read() + else: + data = datafile + + print(" - %s" % (efuse.name), end=" ") + revers_msg = None + if efuses[block.key_purpose_name].need_reverse(keypurpose): + revers_msg = "\tReversing byte order for AES-XTS hardware peripheral" + data = data[::-1] + print( + "-> [{}]".format( + util.hexify(data, " ") + if args.show_sensitive_info + else " ".join(["??"] * len(data)) + ) + ) + if revers_msg: + print(revers_msg) + if len(data) != num_bytes: + raise esptool.FatalError( + "Incorrect key file size %d. Key file must be %d bytes (%d bits) " + "of raw binary key data." % (len(data), num_bytes, num_bytes * 8) + ) + + if efuses[block.key_purpose_name].need_rd_protect(keypurpose): + read_protect = False if args.no_read_protect else True + else: + read_protect = False + write_protect = not args.no_write_protect + + # using efuse instead of a block gives the advantage of checking it as the whole field. + efuse.save(data) + + disable_wr_protect_key_purpose = False + if efuses[block.key_purpose_name].get() != keypurpose: + if efuses[block.key_purpose_name].is_writeable(): + print( + "\t'%s': '%s' -> '%s'." + % ( + block.key_purpose_name, + efuses[block.key_purpose_name].get(), + keypurpose, + ) + ) + efuses[block.key_purpose_name].save(keypurpose) + disable_wr_protect_key_purpose = True + else: + raise esptool.FatalError( + "It is not possible to change '%s' to '%s' " + "because write protection bit is set." + % (block.key_purpose_name, keypurpose) + ) + else: + print("\t'%s' is already '%s'." % (block.key_purpose_name, keypurpose)) + if efuses[block.key_purpose_name].is_writeable(): + disable_wr_protect_key_purpose = True + + if disable_wr_protect_key_purpose: + print("\tDisabling write to '%s'." % block.key_purpose_name) + efuses[block.key_purpose_name].disable_write() + + if read_protect: + print("\tDisabling read to key block") + efuse.disable_read() + + if write_protect: + print("\tDisabling write to key block") + efuse.disable_write() + print("") + + if not write_protect: + print("Keys will remain writeable (due to --no-write-protect)") + if args.no_read_protect: + print("Keys will remain readable (due to --no-read-protect)") + + if not efuses.burn_all(check_batch_mode=True): + return + print("Successful") + + +def burn_key_digest(esp, efuses, args): + digest_list = [] + datafile_list = args.keyfile[ + 0 : len([name for name in args.keyfile if name is not None]) : + ] + block_list = args.block[ + 0 : len([block for block in args.block if block is not None]) : + ] + for block_name, datafile in zip(block_list, datafile_list): + efuse = None + for block in efuses.blocks: + if block_name == block.name or block_name in block.alias: + efuse = efuses[block.name] + if efuse is None: + raise esptool.FatalError("Unknown block name - %s" % (block_name)) + num_bytes = efuse.bit_len // 8 + digest = espsecure._digest_sbv2_public_key(datafile) + if len(digest) != num_bytes: + raise esptool.FatalError( + "Incorrect digest size %d. Digest must be %d bytes (%d bits) " + "of raw binary key data." % (len(digest), num_bytes, num_bytes * 8) + ) + digest_list.append(digest) + burn_key(esp, efuses, args, digest=digest_list) + + +def espefuse(esp, efuses, args, command): + parser = argparse.ArgumentParser() + subparsers = parser.add_subparsers(dest="operation") + add_commands(subparsers, efuses) + try: + cmd_line_args = parser.parse_args(command.split()) + except SystemExit: + traceback.print_stack() + raise esptool.FatalError('"{}" - incorrect command'.format(command)) + if cmd_line_args.operation == "execute_scripts": + configfiles = cmd_line_args.configfiles + index = cmd_line_args.index + # copy arguments from args to cmd_line_args + vars(cmd_line_args).update(vars(args)) + if cmd_line_args.operation == "execute_scripts": + cmd_line_args.configfiles = configfiles + cmd_line_args.index = index + if cmd_line_args.operation is None: + parser.print_help() + parser.exit(1) + operation_func = globals()[cmd_line_args.operation] + # each 'operation' is a module-level function of the same name + operation_func(esp, efuses, cmd_line_args) + + +def execute_scripts(esp, efuses, args): + efuses.batch_mode_cnt += 1 + del args.operation + scripts = args.scripts + del args.scripts + + for file in scripts: + with open(file.name, "r") as file: + exec(compile(file.read(), file.name, "exec")) + + if args.debug: + for block in efuses.blocks: + data = block.get_bitstring(from_read=False) + block.print_block(data, "regs_for_burn", args.debug) + + efuses.batch_mode_cnt -= 1 + if not efuses.burn_all(check_batch_mode=True): + return + print("Successful") diff --git a/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32h2/__init__.py b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32h2/__init__.py new file mode 100644 index 0000000..a3b55a8 --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32h2/__init__.py @@ -0,0 +1,3 @@ +from . import operations +from .emulate_efuse_controller import EmulateEfuseController +from .fields import EspEfuses diff --git a/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32h2/__pycache__/__init__.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32h2/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..15bf7a5 Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32h2/__pycache__/__init__.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32h2/__pycache__/emulate_efuse_controller.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32h2/__pycache__/emulate_efuse_controller.cpython-312.pyc new file mode 100644 index 0000000..597d58c Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32h2/__pycache__/emulate_efuse_controller.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32h2/__pycache__/fields.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32h2/__pycache__/fields.cpython-312.pyc new file mode 100644 index 0000000..832a7a4 Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32h2/__pycache__/fields.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32h2/__pycache__/mem_definition.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32h2/__pycache__/mem_definition.cpython-312.pyc new file mode 100644 index 0000000..5e94b3f Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32h2/__pycache__/mem_definition.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32h2/__pycache__/operations.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32h2/__pycache__/operations.cpython-312.pyc new file mode 100644 index 0000000..409adc3 Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32h2/__pycache__/operations.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32h2/emulate_efuse_controller.py b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32h2/emulate_efuse_controller.py new file mode 100644 index 0000000..c8d3cd9 --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32h2/emulate_efuse_controller.py @@ -0,0 +1,92 @@ +# This file describes eFuses controller for ESP32-H2 chip +# +# SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD +# +# SPDX-License-Identifier: GPL-2.0-or-later + +import reedsolo + +from .mem_definition import EfuseDefineBlocks, EfuseDefineFields, EfuseDefineRegisters +from ..emulate_efuse_controller_base import EmulateEfuseControllerBase, FatalError + + +class EmulateEfuseController(EmulateEfuseControllerBase): + """The class for virtual efuse operation. Using for HOST_TEST.""" + + CHIP_NAME = "ESP32-H2" + mem = None + debug = False + + def __init__(self, efuse_file=None, debug=False): + self.Blocks = EfuseDefineBlocks + self.Fields = EfuseDefineFields() + self.REGS = EfuseDefineRegisters + super(EmulateEfuseController, self).__init__(efuse_file, debug) + self.write_reg(self.REGS.EFUSE_CMD_REG, 0) + + """ esptool method start >>""" + + def get_major_chip_version(self): + return 0 + + def get_minor_chip_version(self): + return 0 + + def get_crystal_freq(self): + return 32 # MHz + + def get_security_info(self): + return { + "flags": 0, + "flash_crypt_cnt": 0, + "key_purposes": 0, + "chip_id": 0, + "api_version": 0, + } + + """ << esptool method end """ + + def handle_writing_event(self, addr, value): + if addr == self.REGS.EFUSE_CMD_REG: + if value & self.REGS.EFUSE_PGM_CMD: + self.copy_blocks_wr_regs_to_rd_regs(updated_block=(value >> 2) & 0xF) + self.clean_blocks_wr_regs() + self.check_rd_protection_area() + self.write_reg(addr, 0) + self.write_reg(self.REGS.EFUSE_CMD_REG, 0) + elif value == self.REGS.EFUSE_READ_CMD: + self.write_reg(addr, 0) + self.write_reg(self.REGS.EFUSE_CMD_REG, 0) + self.save_to_file() + + def get_bitlen_of_block(self, blk, wr=False): + if blk.id == 0: + if wr: + return 32 * 8 + else: + return 32 * blk.len + else: + if wr: + rs_coding = 32 * 3 + return 32 * 8 + rs_coding + else: + return 32 * blk.len + + def handle_coding_scheme(self, blk, data): + if blk.id != 0: + # CODING_SCHEME RS applied only for all blocks except BLK0. + coded_bytes = 12 + data.pos = coded_bytes * 8 + plain_data = data.readlist("32*uint:8")[::-1] + # takes 32 bytes + # apply RS encoding + rs = reedsolo.RSCodec(coded_bytes) + # 32 byte of data + 12 bytes RS + calc_encoded_data = list(rs.encode([x for x in plain_data])) + data.pos = 0 + if calc_encoded_data != data.readlist("44*uint:8")[::-1]: + raise FatalError("Error in coding scheme data") + data = data[coded_bytes * 8 :] + if blk.len < 8: + data = data[(8 - blk.len) * 32 :] + return data diff --git a/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32h2/fields.py b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32h2/fields.py new file mode 100644 index 0000000..4618260 --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32h2/fields.py @@ -0,0 +1,458 @@ +# This file describes eFuses for ESP32-H2 chip +# +# SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD +# +# SPDX-License-Identifier: GPL-2.0-or-later + +import binascii +import struct +import sys +import time + +from bitstring import BitArray + +import esptool + +import reedsolo + +from .mem_definition import EfuseDefineBlocks, EfuseDefineFields, EfuseDefineRegisters +from .. import base_fields +from .. import util + + +class EfuseBlock(base_fields.EfuseBlockBase): + def len_of_burn_unit(self): + # The writing register window is 8 registers for any blocks. + # len in bytes + return 8 * 4 + + def __init__(self, parent, param, skip_read=False): + parent.read_coding_scheme() + super(EfuseBlock, self).__init__(parent, param, skip_read=skip_read) + + def apply_coding_scheme(self): + data = self.get_raw(from_read=False)[::-1] + if len(data) < self.len_of_burn_unit(): + add_empty_bytes = self.len_of_burn_unit() - len(data) + data = data + (b"\x00" * add_empty_bytes) + if self.get_coding_scheme() == self.parent.REGS.CODING_SCHEME_RS: + # takes 32 bytes + # apply RS encoding + rs = reedsolo.RSCodec(12) + # 32 byte of data + 12 bytes RS + encoded_data = rs.encode([x for x in data]) + words = struct.unpack("<" + "I" * 11, encoded_data) + # returns 11 words (8 words of data + 3 words of RS coding) + else: + # takes 32 bytes + words = struct.unpack("<" + ("I" * (len(data) // 4)), data) + # returns 8 words + return words + + +class EspEfuses(base_fields.EspEfusesBase): + """ + Wrapper object to manage the efuse fields in a connected ESP bootloader + """ + + debug = False + do_not_confirm = False + + def __init__(self, esp, skip_connect=False, debug=False, do_not_confirm=False): + self.Blocks = EfuseDefineBlocks() + self.Fields = EfuseDefineFields() + self.REGS = EfuseDefineRegisters + self.BURN_BLOCK_DATA_NAMES = self.Blocks.get_burn_block_data_names() + self.BLOCKS_FOR_KEYS = self.Blocks.get_blocks_for_keys() + self._esp = esp + self.debug = debug + self.do_not_confirm = do_not_confirm + if esp.CHIP_NAME != "ESP32-H2": + raise esptool.FatalError( + "Expected the 'esp' param for ESP32-H2 chip but got for '%s'." + % (esp.CHIP_NAME) + ) + if not skip_connect: + flags = self._esp.get_security_info()["flags"] + GET_SECURITY_INFO_FLAG_SECURE_DOWNLOAD_ENABLE = 1 << 2 + if flags & GET_SECURITY_INFO_FLAG_SECURE_DOWNLOAD_ENABLE: + raise esptool.FatalError( + "Secure Download Mode is enabled. The tool can not read eFuses." + ) + self.blocks = [ + EfuseBlock(self, self.Blocks.get(block), skip_read=skip_connect) + for block in self.Blocks.BLOCKS + ] + if not skip_connect: + self.get_coding_scheme_warnings() + self.efuses = [EfuseField.convert(self, efuse) for efuse in self.Fields.EFUSES] + self.efuses += [ + EfuseField.convert(self, efuse) for efuse in self.Fields.KEYBLOCKS + ] + if skip_connect: + self.efuses += [ + EfuseField.convert(self, efuse) + for efuse in self.Fields.BLOCK2_CALIBRATION_EFUSES + ] + else: + if self["BLK_VERSION_MINOR"].get() == 2: + self.efuses += [ + EfuseField.convert(self, efuse) + for efuse in self.Fields.BLOCK2_CALIBRATION_EFUSES + ] + self.efuses += [ + EfuseField.convert(self, efuse) for efuse in self.Fields.CALC + ] + + def __getitem__(self, efuse_name): + """Return the efuse field with the given name""" + for e in self.efuses: + if efuse_name == e.name or any(x == efuse_name for x in e.alt_names): + return e + new_fields = False + for efuse in self.Fields.BLOCK2_CALIBRATION_EFUSES: + if efuse.name == efuse_name or any( + x == efuse_name for x in efuse.alt_names + ): + self.efuses += [ + EfuseField.convert(self, efuse) + for efuse in self.Fields.BLOCK2_CALIBRATION_EFUSES + ] + new_fields = True + if new_fields: + for e in self.efuses: + if efuse_name == e.name or any(x == efuse_name for x in e.alt_names): + return e + raise KeyError + + def read_coding_scheme(self): + self.coding_scheme = self.REGS.CODING_SCHEME_RS + + def print_status_regs(self): + print("") + self.blocks[0].print_block(self.blocks[0].err_bitarray, "err__regs", debug=True) + print( + "{:27} 0x{:08x}".format( + "EFUSE_RD_RS_ERR0_REG", self.read_reg(self.REGS.EFUSE_RD_RS_ERR0_REG) + ) + ) + print( + "{:27} 0x{:08x}".format( + "EFUSE_RD_RS_ERR1_REG", self.read_reg(self.REGS.EFUSE_RD_RS_ERR1_REG) + ) + ) + + def efuse_controller_setup(self): + self.set_efuse_timing() + self.clear_pgm_registers() + self.wait_efuse_idle() + + def write_efuses(self, block): + self.efuse_program(block) + return self.get_coding_scheme_warnings(silent=True) + + def clear_pgm_registers(self): + self.wait_efuse_idle() + for r in range( + self.REGS.EFUSE_PGM_DATA0_REG, self.REGS.EFUSE_PGM_DATA0_REG + 32, 4 + ): + self.write_reg(r, 0) + + def wait_efuse_idle(self): + deadline = time.time() + self.REGS.EFUSE_BURN_TIMEOUT + while time.time() < deadline: + cmds = self.REGS.EFUSE_PGM_CMD | self.REGS.EFUSE_READ_CMD + if self.read_reg(self.REGS.EFUSE_CMD_REG) & cmds == 0: + if self.read_reg(self.REGS.EFUSE_CMD_REG) & cmds == 0: + # Due to a hardware error, we have to read READ_CMD again + # to make sure the efuse clock is normal. + # For PGM_CMD it is not necessary. + return + raise esptool.FatalError( + "Timed out waiting for Efuse controller command to complete" + ) + + def efuse_program(self, block): + self.wait_efuse_idle() + self.write_reg(self.REGS.EFUSE_CONF_REG, self.REGS.EFUSE_WRITE_OP_CODE) + self.write_reg(self.REGS.EFUSE_CMD_REG, self.REGS.EFUSE_PGM_CMD | (block << 2)) + self.wait_efuse_idle() + self.clear_pgm_registers() + self.efuse_read() + + def efuse_read(self): + self.wait_efuse_idle() + self.write_reg(self.REGS.EFUSE_CONF_REG, self.REGS.EFUSE_READ_OP_CODE) + # need to add a delay after triggering EFUSE_READ_CMD, as ROM loader checks some + # efuse registers after each command is completed + # if ENABLE_SECURITY_DOWNLOAD or DIS_DOWNLOAD_MODE is enabled by the current cmd, then we need to try to reconnect to the chip. + try: + self.write_reg( + self.REGS.EFUSE_CMD_REG, self.REGS.EFUSE_READ_CMD, delay_after_us=1000 + ) + self.wait_efuse_idle() + except esptool.FatalError: + secure_download_mode_before = self._esp.secure_download_mode + + try: + self._esp = self.reconnect_chip(self._esp) + except esptool.FatalError: + print("Can not re-connect to the chip") + if not self["DIS_DOWNLOAD_MODE"].get() and self[ + "DIS_DOWNLOAD_MODE" + ].get(from_read=False): + print( + "This is the correct behavior as we are actually burning " + "DIS_DOWNLOAD_MODE which disables the connection to the chip" + ) + print("DIS_DOWNLOAD_MODE is enabled") + print("Successful") + sys.exit(0) # finish without errors + raise + + print("Established a connection with the chip") + if self._esp.secure_download_mode and not secure_download_mode_before: + print("Secure download mode is enabled") + if not self["ENABLE_SECURITY_DOWNLOAD"].get() and self[ + "ENABLE_SECURITY_DOWNLOAD" + ].get(from_read=False): + print( + "espefuse tool can not continue to work in Secure download mode" + ) + print("ENABLE_SECURITY_DOWNLOAD is enabled") + print("Successful") + sys.exit(0) # finish without errors + raise + + def set_efuse_timing(self): + """Set timing registers for burning efuses""" + # Configure clock + apb_freq = self.get_crystal_freq() + # Based on `CONFIG_SOC_XTAL_SUPPORT_32M=y` for this target from ESP-IDF configuration + if apb_freq != 32: + raise esptool.FatalError( + "The eFuse supports only xtal=32M (xtal was %d)" % apb_freq + ) + + self.update_reg(self.REGS.EFUSE_DAC_CONF_REG, self.REGS.EFUSE_DAC_NUM_M, 0xFF) + self.update_reg( + self.REGS.EFUSE_DAC_CONF_REG, self.REGS.EFUSE_DAC_CLK_DIV_M, 0x28 + ) + self.update_reg( + self.REGS.EFUSE_WR_TIM_CONF1_REG, self.REGS.EFUSE_PWR_ON_NUM_M, 0x3000 + ) + self.update_reg( + self.REGS.EFUSE_WR_TIM_CONF2_REG, self.REGS.EFUSE_PWR_OFF_NUM_M, 0x190 + ) + + def get_coding_scheme_warnings(self, silent=False): + """Check if the coding scheme has detected any errors.""" + old_addr_reg = 0 + reg_value = 0 + ret_fail = False + for block in self.blocks: + if block.id == 0: + words = [ + self.read_reg(self.REGS.EFUSE_RD_REPEAT_ERR0_REG + offs * 4) + for offs in range(5) + ] + block.err_bitarray.pos = 0 + for word in reversed(words): + block.err_bitarray.overwrite(BitArray("uint:32=%d" % word)) + block.num_errors = block.err_bitarray.count(True) + block.fail = block.num_errors != 0 + else: + addr_reg, err_num_mask, err_num_offs, fail_bit = self.REGS.BLOCK_ERRORS[ + block.id + ] + if err_num_mask is None or err_num_offs is None or fail_bit is None: + continue + if addr_reg != old_addr_reg: + old_addr_reg = addr_reg + reg_value = self.read_reg(addr_reg) + block.fail = reg_value & (1 << fail_bit) != 0 + block.num_errors = (reg_value >> err_num_offs) & err_num_mask + ret_fail |= block.fail + if not silent and (block.fail or block.num_errors): + print( + "Error(s) in BLOCK%d [ERRORS:%d FAIL:%d]" + % (block.id, block.num_errors, block.fail) + ) + if (self.debug or ret_fail) and not silent: + self.print_status_regs() + return ret_fail + + def summary(self): + # TODO add support set_flash_voltage - "Flash voltage (VDD_SPI)" + return "" + + +class EfuseField(base_fields.EfuseFieldBase): + @staticmethod + def convert(parent, efuse): + return { + "mac": EfuseMacField, + "keypurpose": EfuseKeyPurposeField, + "t_sensor": EfuseTempSensor, + "adc_tp": EfuseAdcPointCalibration, + "wafer": EfuseWafer, + }.get(efuse.class_type, EfuseField)(parent, efuse) + + +class EfuseWafer(EfuseField): + def get(self, from_read=True): + hi_bits = self.parent["WAFER_VERSION_MINOR_HI"].get(from_read) + assert self.parent["WAFER_VERSION_MINOR_HI"].bit_len == 1 + lo_bits = self.parent["WAFER_VERSION_MINOR_LO"].get(from_read) + assert self.parent["WAFER_VERSION_MINOR_LO"].bit_len == 3 + return (hi_bits << 3) + lo_bits + + def save(self, new_value): + raise esptool.FatalError("Burning %s is not supported" % self.name) + + +class EfuseTempSensor(EfuseField): + def get(self, from_read=True): + value = self.get_bitstring(from_read) + sig = -1 if value[0] else 1 + return sig * value[1:].uint * 0.1 + + +class EfuseAdcPointCalibration(EfuseField): + def get(self, from_read=True): + STEP_SIZE = 4 + value = self.get_bitstring(from_read) + sig = -1 if value[0] else 1 + return sig * value[1:].uint * STEP_SIZE + + +class EfuseMacField(EfuseField): + def check_format(self, new_value_str): + if new_value_str is None: + raise esptool.FatalError( + "Required MAC Address in AA:CD:EF:01:02:03 format!" + ) + num_bytes = 8 if self.name == "MAC_EUI64" else 6 + if new_value_str.count(":") != num_bytes - 1: + raise esptool.FatalError( + f"MAC Address needs to be a {num_bytes}-byte hexadecimal format " + "separated by colons (:)!" + ) + hexad = new_value_str.replace(":", "") + hexad = hexad.split(" ", 1)[0] if self.is_field_calculated() else hexad + if len(hexad) != num_bytes * 2: + raise esptool.FatalError( + f"MAC Address needs to be a {num_bytes}-byte hexadecimal number " + f"({num_bytes * 2} hexadecimal characters)!" + ) + # order of bytearray = b'\xaa\xcd\xef\x01\x02\x03', + bindata = binascii.unhexlify(hexad) + + if not self.is_field_calculated(): + # unicast address check according to + # https://tools.ietf.org/html/rfc7042#section-2.1 + if esptool.util.byte(bindata, 0) & 0x01: + raise esptool.FatalError("Custom MAC must be a unicast MAC!") + return bindata + + def check(self): + errs, fail = self.parent.get_block_errors(self.block) + if errs != 0 or fail: + output = "Block%d has ERRORS:%d FAIL:%d" % (self.block, errs, fail) + else: + output = "OK" + return "(" + output + ")" + + def get(self, from_read=True): + if self.name == "CUSTOM_MAC": + mac = self.get_raw(from_read)[::-1] + elif self.name == "MAC": + mac = self.get_raw(from_read) + elif self.name == "MAC_EUI64": + mac = self.parent["MAC"].get_bitstring(from_read).copy() + mac_ext = self.parent["MAC_EXT"].get_bitstring(from_read) + mac.insert(mac_ext, 24) + mac = mac.bytes + else: + mac = self.get_raw(from_read) + return "%s %s" % (util.hexify(mac, ":"), self.check()) + + def save(self, new_value): + def print_field(e, new_value): + print( + " - '{}' ({}) {} -> {}".format( + e.name, e.description, e.get_bitstring(), new_value + ) + ) + + if self.name == "CUSTOM_MAC": + bitarray_mac = self.convert_to_bitstring(new_value) + print_field(self, bitarray_mac) + super(EfuseMacField, self).save(new_value) + else: + # Writing the BLOCK1 (MAC_SPI_8M_0) default MAC is not possible, + # as it's written in the factory. + raise esptool.FatalError(f"Burning {self.name} is not supported") + + +# fmt: off +class EfuseKeyPurposeField(EfuseField): + KEY_PURPOSES = [ + ("USER", 0, None, None, "no_need_rd_protect"), # User purposes (software-only use) + ("ECDSA_KEY", 1, None, "Reverse", "need_rd_protect"), # ECDSA key + ("RESERVED", 2, None, None, "no_need_rd_protect"), # Reserved + ("XTS_AES_128_KEY", 4, None, "Reverse", "need_rd_protect"), # XTS_AES_128_KEY (flash/PSRAM encryption) + ("HMAC_DOWN_ALL", 5, None, None, "need_rd_protect"), # HMAC Downstream mode + ("HMAC_DOWN_JTAG", 6, None, None, "need_rd_protect"), # JTAG soft enable key (uses HMAC Downstream mode) + ("HMAC_DOWN_DIGITAL_SIGNATURE", 7, None, None, "need_rd_protect"), # Digital Signature peripheral key (uses HMAC Downstream mode) + ("HMAC_UP", 8, None, None, "need_rd_protect"), # HMAC Upstream mode + ("SECURE_BOOT_DIGEST0", 9, "DIGEST", None, "no_need_rd_protect"), # SECURE_BOOT_DIGEST0 (Secure Boot key digest) + ("SECURE_BOOT_DIGEST1", 10, "DIGEST", None, "no_need_rd_protect"), # SECURE_BOOT_DIGEST1 (Secure Boot key digest) + ("SECURE_BOOT_DIGEST2", 11, "DIGEST", None, "no_need_rd_protect"), # SECURE_BOOT_DIGEST2 (Secure Boot key digest) + ] +# fmt: on + KEY_PURPOSES_NAME = [name[0] for name in KEY_PURPOSES] + DIGEST_KEY_PURPOSES = [name[0] for name in KEY_PURPOSES if name[2] == "DIGEST"] + + def check_format(self, new_value_str): + # str convert to int: "XTS_AES_128_KEY" - > str(4) + # if int: 4 -> str(4) + raw_val = new_value_str + for purpose_name in self.KEY_PURPOSES: + if purpose_name[0] == new_value_str: + raw_val = str(purpose_name[1]) + break + if raw_val.isdigit(): + if int(raw_val) not in [p[1] for p in self.KEY_PURPOSES if p[1] > 0]: + raise esptool.FatalError("'%s' can not be set (value out of range)" % raw_val) + else: + raise esptool.FatalError("'%s' unknown name" % raw_val) + return raw_val + + def need_reverse(self, new_key_purpose): + for key in self.KEY_PURPOSES: + if key[0] == new_key_purpose: + return key[3] == "Reverse" + + def need_rd_protect(self, new_key_purpose): + for key in self.KEY_PURPOSES: + if key[0] == new_key_purpose: + return key[4] == "need_rd_protect" + + def get(self, from_read=True): + for p in self.KEY_PURPOSES: + if p[1] == self.get_raw(from_read): + return p[0] + return "FORBIDDEN_STATE" + + def get_name(self, raw_val): + for key in self.KEY_PURPOSES: + if key[1] == raw_val: + return key[0] + + def save(self, new_value): + raw_val = int(self.check_format(str(new_value))) + str_new_value = self.get_name(raw_val) + if self.name == "KEY_PURPOSE_5" and str_new_value in ["XTS_AES_128_KEY", "ECDSA_KEY"]: + raise esptool.FatalError(f"{self.name} can not have {str_new_value} key due to a hardware bug (please see TRM for more details)") + return super(EfuseKeyPurposeField, self).save(raw_val) diff --git a/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32h2/mem_definition.py b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32h2/mem_definition.py new file mode 100644 index 0000000..d08a71f --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32h2/mem_definition.py @@ -0,0 +1,169 @@ +# This file describes eFuses fields and registers for ESP32-H2 chip +# +# SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD +# +# SPDX-License-Identifier: GPL-2.0-or-later + +import os + +import yaml + +from ..mem_definition_base import ( + EfuseBlocksBase, + EfuseFieldsBase, + EfuseRegistersBase, + Field, +) + + +class EfuseDefineRegisters(EfuseRegistersBase): + EFUSE_MEM_SIZE = 0x01FC + 4 + + # EFUSE registers & command/conf values + DR_REG_EFUSE_BASE = 0x600B0800 + EFUSE_PGM_DATA0_REG = DR_REG_EFUSE_BASE + EFUSE_CHECK_VALUE0_REG = DR_REG_EFUSE_BASE + 0x020 + EFUSE_CLK_REG = DR_REG_EFUSE_BASE + 0x1C8 + EFUSE_CONF_REG = DR_REG_EFUSE_BASE + 0x1CC + EFUSE_STATUS_REG = DR_REG_EFUSE_BASE + 0x1D0 + EFUSE_CMD_REG = DR_REG_EFUSE_BASE + 0x1D4 + EFUSE_RD_RS_ERR0_REG = DR_REG_EFUSE_BASE + 0x1C0 + EFUSE_RD_RS_ERR1_REG = DR_REG_EFUSE_BASE + 0x1C4 + EFUSE_RD_REPEAT_ERR0_REG = DR_REG_EFUSE_BASE + 0x17C + EFUSE_RD_REPEAT_ERR1_REG = DR_REG_EFUSE_BASE + 0x180 + EFUSE_RD_REPEAT_ERR2_REG = DR_REG_EFUSE_BASE + 0x184 + EFUSE_RD_REPEAT_ERR3_REG = DR_REG_EFUSE_BASE + 0x188 + EFUSE_RD_REPEAT_ERR4_REG = DR_REG_EFUSE_BASE + 0x18C + EFUSE_DAC_CONF_REG = DR_REG_EFUSE_BASE + 0x1E8 + EFUSE_RD_TIM_CONF_REG = DR_REG_EFUSE_BASE + 0x1EC + EFUSE_WR_TIM_CONF1_REG = DR_REG_EFUSE_BASE + 0x1F0 + EFUSE_WR_TIM_CONF2_REG = DR_REG_EFUSE_BASE + 0x1F4 + EFUSE_DATE_REG = DR_REG_EFUSE_BASE + 0x1FC + EFUSE_WRITE_OP_CODE = 0x5A5A + EFUSE_READ_OP_CODE = 0x5AA5 + EFUSE_PGM_CMD_MASK = 0x3 + EFUSE_PGM_CMD = 0x2 + EFUSE_READ_CMD = 0x1 + + BLOCK_ERRORS = [ + # error_reg, err_num_mask, err_num_offs, fail_bit + (EFUSE_RD_REPEAT_ERR0_REG, None, None, None), # BLOCK0 + (EFUSE_RD_RS_ERR0_REG, 0x7, 0, 3), # MAC_SPI_8M_0 + (EFUSE_RD_RS_ERR0_REG, 0x7, 4, 7), # BLOCK_SYS_DATA + (EFUSE_RD_RS_ERR0_REG, 0x7, 8, 11), # BLOCK_USR_DATA + (EFUSE_RD_RS_ERR0_REG, 0x7, 12, 15), # BLOCK_KEY0 + (EFUSE_RD_RS_ERR0_REG, 0x7, 16, 19), # BLOCK_KEY1 + (EFUSE_RD_RS_ERR0_REG, 0x7, 20, 23), # BLOCK_KEY2 + (EFUSE_RD_RS_ERR0_REG, 0x7, 24, 27), # BLOCK_KEY3 + (EFUSE_RD_RS_ERR0_REG, 0x7, 28, 31), # BLOCK_KEY4 + (EFUSE_RD_RS_ERR1_REG, 0x7, 0, 3), # BLOCK_KEY5 + (EFUSE_RD_RS_ERR1_REG, 0x7, 4, 7), # BLOCK_SYS_DATA2 + ] + + # EFUSE_WR_TIM_CONF2_REG + EFUSE_PWR_OFF_NUM_S = 0 + EFUSE_PWR_OFF_NUM_M = 0xFFFF << EFUSE_PWR_OFF_NUM_S + + # EFUSE_WR_TIM_CONF1_REG + EFUSE_PWR_ON_NUM_S = 8 + EFUSE_PWR_ON_NUM_M = 0x0000FFFF << EFUSE_PWR_ON_NUM_S + + # EFUSE_DAC_CONF_REG + EFUSE_DAC_CLK_DIV_S = 0 + EFUSE_DAC_CLK_DIV_M = 0xFF << EFUSE_DAC_CLK_DIV_S + + # EFUSE_DAC_CONF_REG + EFUSE_DAC_NUM_S = 9 + EFUSE_DAC_NUM_M = 0xFF << EFUSE_DAC_NUM_S + + +class EfuseDefineBlocks(EfuseBlocksBase): + __base_rd_regs = EfuseDefineRegisters.DR_REG_EFUSE_BASE + __base_wr_regs = EfuseDefineRegisters.EFUSE_PGM_DATA0_REG + # List of efuse blocks + # fmt: off + BLOCKS = [ + # Name, Alias, Index, Read address, Write address, Write protect bit, Read protect bit, Len, key_purpose + ("BLOCK0", [], 0, __base_rd_regs + 0x02C, __base_wr_regs, None, None, 6, None), + ("MAC_SPI_8M_0", ["BLOCK1"], 1, __base_rd_regs + 0x044, __base_wr_regs, 20, None, 6, None), + ("BLOCK_SYS_DATA", ["BLOCK2"], 2, __base_rd_regs + 0x05C, __base_wr_regs, 21, None, 8, None), + ("BLOCK_USR_DATA", ["BLOCK3"], 3, __base_rd_regs + 0x07C, __base_wr_regs, 22, None, 8, None), + ("BLOCK_KEY0", ["BLOCK4"], 4, __base_rd_regs + 0x09C, __base_wr_regs, 23, 0, 8, "KEY_PURPOSE_0"), + ("BLOCK_KEY1", ["BLOCK5"], 5, __base_rd_regs + 0x0BC, __base_wr_regs, 24, 1, 8, "KEY_PURPOSE_1"), + ("BLOCK_KEY2", ["BLOCK6"], 6, __base_rd_regs + 0x0DC, __base_wr_regs, 25, 2, 8, "KEY_PURPOSE_2"), + ("BLOCK_KEY3", ["BLOCK7"], 7, __base_rd_regs + 0x0FC, __base_wr_regs, 26, 3, 8, "KEY_PURPOSE_3"), + ("BLOCK_KEY4", ["BLOCK8"], 8, __base_rd_regs + 0x11C, __base_wr_regs, 27, 4, 8, "KEY_PURPOSE_4"), + ("BLOCK_KEY5", ["BLOCK9"], 9, __base_rd_regs + 0x13C, __base_wr_regs, 28, 5, 8, "KEY_PURPOSE_5"), + ("BLOCK_SYS_DATA2", ["BLOCK10"], 10, __base_rd_regs + 0x15C, __base_wr_regs, 29, 6, 8, None), + ] + # fmt: on + + def get_burn_block_data_names(self): + list_of_names = [] + for block in self.BLOCKS: + blk = self.get(block) + if blk.name: + list_of_names.append(blk.name) + if blk.alias: + for alias in blk.alias: + list_of_names.append(alias) + return list_of_names + + +class EfuseDefineFields(EfuseFieldsBase): + def __init__(self) -> None: + # List of efuse fields from TRM the chapter eFuse Controller. + self.EFUSES = [] + + self.KEYBLOCKS = [] + + # if BLK_VERSION_MINOR is 2, these efuse fields are in BLOCK2 + self.BLOCK2_CALIBRATION_EFUSES = [] + + self.CALC = [] + + dir_name = os.path.dirname(os.path.abspath(__file__)) + dir_name, file_name = os.path.split(dir_name) + file_name = file_name + ".yaml" + dir_name, _ = os.path.split(dir_name) + efuse_file = os.path.join(dir_name, "efuse_defs", file_name) + with open(f"{efuse_file}", "r") as r_file: + e_desc = yaml.safe_load(r_file) + super().__init__(e_desc) + + for i, efuse in enumerate(self.ALL_EFUSES): + if efuse.name in [ + "BLOCK_USR_DATA", + "BLOCK_KEY0", + "BLOCK_KEY1", + "BLOCK_KEY2", + "BLOCK_KEY3", + "BLOCK_KEY4", + "BLOCK_KEY5", + "BLOCK_SYS_DATA2", + ]: + if efuse.name == "BLOCK_USR_DATA": + efuse.bit_len = 256 + efuse.type = "bytes:32" + self.KEYBLOCKS.append(efuse) + self.ALL_EFUSES[i] = None + + elif efuse.category == "calibration": + self.BLOCK2_CALIBRATION_EFUSES.append(efuse) + self.ALL_EFUSES[i] = None + + f = Field() + f.name = "MAC_EUI64" + f.block = 1 + f.bit_len = 64 + f.type = f"bytes:{f.bit_len // 8}" + f.category = "MAC" + f.class_type = "mac" + f.description = "calc MAC_EUI64 = MAC[0]:MAC[1]:MAC[2]:MAC_EXT[0]:MAC_EXT[1]:MAC[3]:MAC[4]:MAC[5]" + self.CALC.append(f) + + for efuse in self.ALL_EFUSES: + if efuse is not None: + self.EFUSES.append(efuse) + + self.ALL_EFUSES = [] diff --git a/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32h2/operations.py b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32h2/operations.py new file mode 100644 index 0000000..6debd09 --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32h2/operations.py @@ -0,0 +1,427 @@ +# This file includes the operations with eFuses for ESP32-H2 chip +# +# SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD +# +# SPDX-License-Identifier: GPL-2.0-or-later + +import argparse +import os # noqa: F401. It is used in IDF scripts +import traceback + +import espsecure + +import esptool + +from . import fields +from .. import util +from ..base_operations import ( + add_common_commands, + add_force_write_always, + add_show_sensitive_info_option, + burn_bit, + burn_block_data, + burn_efuse, + check_error, + dump, + read_protect_efuse, + summary, + write_protect_efuse, +) + + +def protect_options(p): + p.add_argument( + "--no-write-protect", + help="Disable write-protecting of the key. The key remains writable. " + "(The keys use the RS coding scheme that does not support " + "post-write data changes. Forced write can damage RS encoding bits.) " + "The write-protecting of keypurposes does not depend on the option, " + "it will be set anyway.", + action="store_true", + ) + p.add_argument( + "--no-read-protect", + help="Disable read-protecting of the key. The key remains readable software." + "The key with keypurpose[USER, RESERVED and *_DIGEST] " + "will remain readable anyway. For the rest keypurposes the read-protection " + "will be defined the option (Read-protect by default).", + action="store_true", + ) + + +def add_commands(subparsers, efuses): + add_common_commands(subparsers, efuses) + burn_key = subparsers.add_parser( + "burn_key", help="Burn the key block with the specified name" + ) + protect_options(burn_key) + add_force_write_always(burn_key) + add_show_sensitive_info_option(burn_key) + burn_key.add_argument( + "block", + help="Key block to burn", + action="append", + choices=efuses.BLOCKS_FOR_KEYS, + ) + burn_key.add_argument( + "keyfile", + help="File containing 256 bits of binary key data. For the ECDSA_KEY purpose use PEM file.", + action="append", + type=argparse.FileType("rb"), + ) + burn_key.add_argument( + "keypurpose", + help="Purpose to set.", + action="append", + choices=fields.EfuseKeyPurposeField.KEY_PURPOSES_NAME, + ) + for _ in efuses.BLOCKS_FOR_KEYS: + burn_key.add_argument( + "block", + help="Key block to burn", + nargs="?", + action="append", + metavar="BLOCK", + choices=efuses.BLOCKS_FOR_KEYS, + ) + burn_key.add_argument( + "keyfile", + help="File containing 256 bits of binary key data. For the ECDSA_KEY purpose use PEM file.", + nargs="?", + action="append", + metavar="KEYFILE", + type=argparse.FileType("rb"), + ) + burn_key.add_argument( + "keypurpose", + help="Purpose to set.", + nargs="?", + action="append", + metavar="KEYPURPOSE", + choices=fields.EfuseKeyPurposeField.KEY_PURPOSES_NAME, + ) + + burn_key_digest = subparsers.add_parser( + "burn_key_digest", + help="Parse a RSA public key and burn the digest to key efuse block", + ) + protect_options(burn_key_digest) + add_force_write_always(burn_key_digest) + add_show_sensitive_info_option(burn_key_digest) + burn_key_digest.add_argument( + "block", + help="Key block to burn", + action="append", + choices=efuses.BLOCKS_FOR_KEYS, + ) + burn_key_digest.add_argument( + "keyfile", + help="Key file to digest (PEM format)", + action="append", + type=argparse.FileType("rb"), + ) + burn_key_digest.add_argument( + "keypurpose", + help="Purpose to set.", + action="append", + choices=fields.EfuseKeyPurposeField.DIGEST_KEY_PURPOSES, + ) + for _ in efuses.BLOCKS_FOR_KEYS: + burn_key_digest.add_argument( + "block", + help="Key block to burn", + nargs="?", + action="append", + metavar="BLOCK", + choices=efuses.BLOCKS_FOR_KEYS, + ) + burn_key_digest.add_argument( + "keyfile", + help="Key file to digest (PEM format)", + nargs="?", + action="append", + metavar="KEYFILE", + type=argparse.FileType("rb"), + ) + burn_key_digest.add_argument( + "keypurpose", + help="Purpose to set.", + nargs="?", + action="append", + metavar="KEYPURPOSE", + choices=fields.EfuseKeyPurposeField.DIGEST_KEY_PURPOSES, + ) + + p = subparsers.add_parser( + "set_flash_voltage", + help="Permanently set the internal flash voltage regulator " + "to either 1.8V, 3.3V or OFF. This means GPIO45 can be high or low " + "at reset without changing the flash voltage.", + ) + p.add_argument("voltage", help="Voltage selection", choices=["1.8V", "3.3V", "OFF"]) + + p = subparsers.add_parser( + "burn_custom_mac", help="Burn a 48-bit Custom MAC Address to EFUSE BLOCK3." + ) + p.add_argument( + "mac", + help="Custom MAC Address to burn given in hexadecimal format with bytes " + "separated by colons (e.g. AA:CD:EF:01:02:03).", + type=fields.base_fields.CheckArgValue(efuses, "CUSTOM_MAC"), + ) + add_force_write_always(p) + + p = subparsers.add_parser("get_custom_mac", help="Prints the Custom MAC Address.") + + +def burn_custom_mac(esp, efuses, args): + efuses["CUSTOM_MAC"].save(args.mac) + if not efuses.burn_all(check_batch_mode=True): + return + get_custom_mac(esp, efuses, args) + print("Successful") + + +def get_custom_mac(esp, efuses, args): + print("Custom MAC Address: {}".format(efuses["CUSTOM_MAC"].get())) + + +def set_flash_voltage(esp, efuses, args): + raise esptool.FatalError("set_flash_voltage is not supported!") + + +def adc_info(esp, efuses, args): + print("") + # fmt: off + if efuses["BLK_VERSION_MINOR"].get() == 2: + print("Temperature Sensor Calibration = {}C".format(efuses["TEMP_CALIB"].get())) + + print("") + print("ADC1 readings stored in efuse BLOCK2:") + print(" AVE_INITCODE_ATTEN0: {}".format(efuses["ADC1_AVE_INITCODE_ATTEN0"].get())) + print(" AVE_INITCODE_ATTEN1: {}".format(efuses["ADC1_AVE_INITCODE_ATTEN1"].get())) + print(" AVE_INITCODE_ATTEN2: {}".format(efuses["ADC1_AVE_INITCODE_ATTEN2"].get())) + print(" AVE_INITCODE_ATTEN3: {}".format(efuses["ADC1_AVE_INITCODE_ATTEN3"].get())) + + print(" HI_DOUT_ATTEN0: {}".format(efuses["ADC1_HI_DOUT_ATTEN0"].get())) + print(" HI_DOUT_ATTEN1: {}".format(efuses["ADC1_HI_DOUT_ATTEN1"].get())) + print(" HI_DOUT_ATTEN2: {}".format(efuses["ADC1_HI_DOUT_ATTEN2"].get())) + print(" HI_DOUT_ATTEN3: {}".format(efuses["ADC1_HI_DOUT_ATTEN3"].get())) + + print(" CH0_ATTEN0_INITCODE_DIFF: {}".format(efuses["ADC1_CH0_ATTEN0_INITCODE_DIFF"].get())) + print(" CH1_ATTEN0_INITCODE_DIFF: {}".format(efuses["ADC1_CH1_ATTEN0_INITCODE_DIFF"].get())) + print(" CH2_ATTEN0_INITCODE_DIFF: {}".format(efuses["ADC1_CH2_ATTEN0_INITCODE_DIFF"].get())) + print(" CH3_ATTEN0_INITCODE_DIFF: {}".format(efuses["ADC1_CH3_ATTEN0_INITCODE_DIFF"].get())) + print(" CH4_ATTEN0_INITCODE_DIFF: {}".format(efuses["ADC1_CH4_ATTEN0_INITCODE_DIFF"].get())) + else: + print("BLK_VERSION_MINOR = {}".format(efuses["BLK_VERSION_MINOR"].get())) + # fmt: on + + +def burn_key(esp, efuses, args, digest=None): + if digest is None: + datafile_list = args.keyfile[ + 0 : len([name for name in args.keyfile if name is not None]) : + ] + else: + datafile_list = digest[0 : len([name for name in digest if name is not None]) :] + efuses.force_write_always = args.force_write_always + block_name_list = args.block[ + 0 : len([name for name in args.block if name is not None]) : + ] + keypurpose_list = args.keypurpose[ + 0 : len([name for name in args.keypurpose if name is not None]) : + ] + + util.check_duplicate_name_in_list(block_name_list) + if len(block_name_list) != len(datafile_list) or len(block_name_list) != len( + keypurpose_list + ): + raise esptool.FatalError( + "The number of blocks (%d), datafile (%d) and keypurpose (%d) " + "should be the same." + % (len(block_name_list), len(datafile_list), len(keypurpose_list)) + ) + + print("Burn keys to blocks:") + for block_name, datafile, keypurpose in zip( + block_name_list, datafile_list, keypurpose_list + ): + efuse = None + for block in efuses.blocks: + if block_name == block.name or block_name in block.alias: + efuse = efuses[block.name] + if efuse is None: + raise esptool.FatalError("Unknown block name - %s" % (block_name)) + num_bytes = efuse.bit_len // 8 + + block_num = efuses.get_index_block_by_name(block_name) + block = efuses.blocks[block_num] + + if digest is None: + if keypurpose == "ECDSA_KEY": + sk = espsecure.load_ecdsa_signing_key(datafile) + data = sk.to_string() + if len(data) == 24: + # the private key is 24 bytes long for NIST192p, add 8 bytes of padding + data = b"\x00" * 8 + data + else: + data = datafile.read() + else: + data = datafile + + print(" - %s" % (efuse.name), end=" ") + revers_msg = None + if efuses[block.key_purpose_name].need_reverse(keypurpose): + revers_msg = f"\tReversing byte order for {keypurpose} hardware peripheral" + data = data[::-1] + print( + "-> [{}]".format( + util.hexify(data, " ") + if args.show_sensitive_info + else " ".join(["??"] * len(data)) + ) + ) + if revers_msg: + print(revers_msg) + if len(data) != num_bytes: + raise esptool.FatalError( + "Incorrect key file size %d. Key file must be %d bytes (%d bits) " + "of raw binary key data." % (len(data), num_bytes, num_bytes * 8) + ) + + if efuses[block.key_purpose_name].need_rd_protect(keypurpose): + read_protect = False if args.no_read_protect else True + else: + read_protect = False + write_protect = not args.no_write_protect + + # using efuse instead of a block gives the advantage of checking it as the whole field. + efuse.save(data) + + disable_wr_protect_key_purpose = False + if efuses[block.key_purpose_name].get() != keypurpose: + if efuses[block.key_purpose_name].is_writeable(): + print( + "\t'%s': '%s' -> '%s'." + % ( + block.key_purpose_name, + efuses[block.key_purpose_name].get(), + keypurpose, + ) + ) + efuses[block.key_purpose_name].save(keypurpose) + disable_wr_protect_key_purpose = True + else: + raise esptool.FatalError( + "It is not possible to change '%s' to '%s' " + "because write protection bit is set." + % (block.key_purpose_name, keypurpose) + ) + else: + print("\t'%s' is already '%s'." % (block.key_purpose_name, keypurpose)) + if efuses[block.key_purpose_name].is_writeable(): + disable_wr_protect_key_purpose = True + + if keypurpose == "ECDSA_KEY": + if efuses["ECDSA_FORCE_USE_HARDWARE_K"].get() == 0: + # For ECDSA key purpose block permanently enable + # the hardware TRNG supplied k mode (most secure mode) + print("\tECDSA_FORCE_USE_HARDWARE_K: 0 -> 1") + efuses["ECDSA_FORCE_USE_HARDWARE_K"].save(1) + else: + print("\tECDSA_FORCE_USE_HARDWARE_K is already '1'") + + if disable_wr_protect_key_purpose: + print("\tDisabling write to '%s'." % block.key_purpose_name) + efuses[block.key_purpose_name].disable_write() + + if read_protect: + print("\tDisabling read to key block") + efuse.disable_read() + + if write_protect: + print("\tDisabling write to key block") + efuse.disable_write() + print("") + + if not write_protect: + print("Keys will remain writeable (due to --no-write-protect)") + if args.no_read_protect: + print("Keys will remain readable (due to --no-read-protect)") + + if not efuses.burn_all(check_batch_mode=True): + return + print("Successful") + + +def burn_key_digest(esp, efuses, args): + digest_list = [] + datafile_list = args.keyfile[ + 0 : len([name for name in args.keyfile if name is not None]) : + ] + block_list = args.block[ + 0 : len([block for block in args.block if block is not None]) : + ] + for block_name, datafile in zip(block_list, datafile_list): + efuse = None + for block in efuses.blocks: + if block_name == block.name or block_name in block.alias: + efuse = efuses[block.name] + if efuse is None: + raise esptool.FatalError("Unknown block name - %s" % (block_name)) + num_bytes = efuse.bit_len // 8 + digest = espsecure._digest_sbv2_public_key(datafile) + if len(digest) != num_bytes: + raise esptool.FatalError( + "Incorrect digest size %d. Digest must be %d bytes (%d bits) " + "of raw binary key data." % (len(digest), num_bytes, num_bytes * 8) + ) + digest_list.append(digest) + burn_key(esp, efuses, args, digest=digest_list) + + +def espefuse(esp, efuses, args, command): + parser = argparse.ArgumentParser() + subparsers = parser.add_subparsers(dest="operation") + add_commands(subparsers, efuses) + try: + cmd_line_args = parser.parse_args(command.split()) + except SystemExit: + traceback.print_stack() + raise esptool.FatalError('"{}" - incorrect command'.format(command)) + if cmd_line_args.operation == "execute_scripts": + configfiles = cmd_line_args.configfiles + index = cmd_line_args.index + # copy arguments from args to cmd_line_args + vars(cmd_line_args).update(vars(args)) + if cmd_line_args.operation == "execute_scripts": + cmd_line_args.configfiles = configfiles + cmd_line_args.index = index + if cmd_line_args.operation is None: + parser.print_help() + parser.exit(1) + operation_func = globals()[cmd_line_args.operation] + # each 'operation' is a module-level function of the same name + operation_func(esp, efuses, cmd_line_args) + + +def execute_scripts(esp, efuses, args): + efuses.batch_mode_cnt += 1 + del args.operation + scripts = args.scripts + del args.scripts + + for file in scripts: + with open(file.name, "r") as file: + exec(compile(file.read(), file.name, "exec")) + + if args.debug: + for block in efuses.blocks: + data = block.get_bitstring(from_read=False) + block.print_block(data, "regs_for_burn", args.debug) + + efuses.batch_mode_cnt -= 1 + if not efuses.burn_all(check_batch_mode=True): + return + print("Successful") diff --git a/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32h2beta1/__init__.py b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32h2beta1/__init__.py new file mode 100644 index 0000000..a3b55a8 --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32h2beta1/__init__.py @@ -0,0 +1,3 @@ +from . import operations +from .emulate_efuse_controller import EmulateEfuseController +from .fields import EspEfuses diff --git a/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32h2beta1/__pycache__/__init__.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32h2beta1/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..3d1f365 Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32h2beta1/__pycache__/__init__.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32h2beta1/__pycache__/emulate_efuse_controller.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32h2beta1/__pycache__/emulate_efuse_controller.cpython-312.pyc new file mode 100644 index 0000000..933c032 Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32h2beta1/__pycache__/emulate_efuse_controller.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32h2beta1/__pycache__/fields.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32h2beta1/__pycache__/fields.cpython-312.pyc new file mode 100644 index 0000000..0aaaf09 Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32h2beta1/__pycache__/fields.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32h2beta1/__pycache__/mem_definition.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32h2beta1/__pycache__/mem_definition.cpython-312.pyc new file mode 100644 index 0000000..6668211 Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32h2beta1/__pycache__/mem_definition.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32h2beta1/__pycache__/operations.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32h2beta1/__pycache__/operations.cpython-312.pyc new file mode 100644 index 0000000..f1f34dd Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32h2beta1/__pycache__/operations.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32h2beta1/emulate_efuse_controller.py b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32h2beta1/emulate_efuse_controller.py new file mode 100644 index 0000000..b81e8e0 --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32h2beta1/emulate_efuse_controller.py @@ -0,0 +1,92 @@ +# This file describes eFuses controller for ESP32-H2 chip +# +# SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD +# +# SPDX-License-Identifier: GPL-2.0-or-later + +import reedsolo + +from .mem_definition import EfuseDefineBlocks, EfuseDefineFields, EfuseDefineRegisters +from ..emulate_efuse_controller_base import EmulateEfuseControllerBase, FatalError + + +class EmulateEfuseController(EmulateEfuseControllerBase): + """The class for virtual efuse operation. Using for HOST_TEST.""" + + CHIP_NAME = "ESP32-H2(beta1)" + mem = None + debug = False + + def __init__(self, efuse_file=None, debug=False): + self.Blocks = EfuseDefineBlocks + self.Fields = EfuseDefineFields() + self.REGS = EfuseDefineRegisters + super(EmulateEfuseController, self).__init__(efuse_file, debug) + self.write_reg(self.REGS.EFUSE_CMD_REG, 0) + + """ esptool method start >>""" + + def get_major_chip_version(self): + return 0 + + def get_minor_chip_version(self): + return 0 + + def get_crystal_freq(self): + return 32 # MHz (common for all chips) + + def get_security_info(self): + return { + "flags": 0, + "flash_crypt_cnt": 0, + "key_purposes": 0, + "chip_id": 0, + "api_version": 0, + } + + """ << esptool method end """ + + def handle_writing_event(self, addr, value): + if addr == self.REGS.EFUSE_CMD_REG: + if value & self.REGS.EFUSE_PGM_CMD: + self.copy_blocks_wr_regs_to_rd_regs(updated_block=(value >> 2) & 0xF) + self.clean_blocks_wr_regs() + self.check_rd_protection_area() + self.write_reg(addr, 0) + self.write_reg(self.REGS.EFUSE_CMD_REG, 0) + elif value == self.REGS.EFUSE_READ_CMD: + self.write_reg(addr, 0) + self.write_reg(self.REGS.EFUSE_CMD_REG, 0) + self.save_to_file() + + def get_bitlen_of_block(self, blk, wr=False): + if blk.id == 0: + if wr: + return 32 * 8 + else: + return 32 * blk.len + else: + if wr: + rs_coding = 32 * 3 + return 32 * 8 + rs_coding + else: + return 32 * blk.len + + def handle_coding_scheme(self, blk, data): + if blk.id != 0: + # CODING_SCHEME RS applied only for all blocks except BLK0. + coded_bytes = 12 + data.pos = coded_bytes * 8 + plain_data = data.readlist("32*uint:8")[::-1] + # takes 32 bytes + # apply RS encoding + rs = reedsolo.RSCodec(coded_bytes) + # 32 byte of data + 12 bytes RS + calc_encoded_data = list(rs.encode([x for x in plain_data])) + data.pos = 0 + if calc_encoded_data != data.readlist("44*uint:8")[::-1]: + raise FatalError("Error in coding scheme data") + data = data[coded_bytes * 8 :] + if blk.len < 8: + data = data[(8 - blk.len) * 32 :] + return data diff --git a/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32h2beta1/fields.py b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32h2beta1/fields.py new file mode 100644 index 0000000..16bc78f --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32h2beta1/fields.py @@ -0,0 +1,458 @@ +# This file describes eFuses for ESP32-H2 chip +# +# SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD +# +# SPDX-License-Identifier: GPL-2.0-or-later + +import binascii +import struct +import sys +import time + +from bitstring import BitArray + +import esptool + +import reedsolo + +from .mem_definition import EfuseDefineBlocks, EfuseDefineFields, EfuseDefineRegisters +from .. import base_fields +from .. import util + + +class EfuseBlock(base_fields.EfuseBlockBase): + def len_of_burn_unit(self): + # The writing register window is 8 registers for any blocks. + # len in bytes + return 8 * 4 + + def __init__(self, parent, param, skip_read=False): + parent.read_coding_scheme() + super(EfuseBlock, self).__init__(parent, param, skip_read=skip_read) + + def apply_coding_scheme(self): + data = self.get_raw(from_read=False)[::-1] + if len(data) < self.len_of_burn_unit(): + add_empty_bytes = self.len_of_burn_unit() - len(data) + data = data + (b"\x00" * add_empty_bytes) + if self.get_coding_scheme() == self.parent.REGS.CODING_SCHEME_RS: + # takes 32 bytes + # apply RS encoding + rs = reedsolo.RSCodec(12) + # 32 byte of data + 12 bytes RS + encoded_data = rs.encode([x for x in data]) + words = struct.unpack("<" + "I" * 11, encoded_data) + # returns 11 words (8 words of data + 3 words of RS coding) + else: + # takes 32 bytes + words = struct.unpack("<" + ("I" * (len(data) // 4)), data) + # returns 8 words + return words + + +class EspEfuses(base_fields.EspEfusesBase): + """ + Wrapper object to manage the efuse fields in a connected ESP bootloader + """ + + debug = False + do_not_confirm = False + + def __init__(self, esp, skip_connect=False, debug=False, do_not_confirm=False): + self.Blocks = EfuseDefineBlocks() + self.Fields = EfuseDefineFields() + self.REGS = EfuseDefineRegisters + self.BURN_BLOCK_DATA_NAMES = self.Blocks.get_burn_block_data_names() + self.BLOCKS_FOR_KEYS = self.Blocks.get_blocks_for_keys() + self._esp = esp + self.debug = debug + self.do_not_confirm = do_not_confirm + if esp.CHIP_NAME != "ESP32-H2(beta1)": + raise esptool.FatalError( + "Expected the 'esp' param for ESP32-H2(beta1) chip but got for '%s'." + % (esp.CHIP_NAME) + ) + if not skip_connect: + flags = self._esp.get_security_info()["flags"] + GET_SECURITY_INFO_FLAG_SECURE_DOWNLOAD_ENABLE = 1 << 2 + if flags & GET_SECURITY_INFO_FLAG_SECURE_DOWNLOAD_ENABLE: + raise esptool.FatalError( + "Secure Download Mode is enabled. The tool can not read eFuses." + ) + self.blocks = [ + EfuseBlock(self, self.Blocks.get(block), skip_read=skip_connect) + for block in self.Blocks.BLOCKS + ] + if not skip_connect: + self.get_coding_scheme_warnings() + self.efuses = [EfuseField.convert(self, efuse) for efuse in self.Fields.EFUSES] + self.efuses += [ + EfuseField.convert(self, efuse) for efuse in self.Fields.KEYBLOCKS + ] + if skip_connect: + self.efuses += [ + EfuseField.convert(self, efuse) + for efuse in self.Fields.BLOCK2_CALIBRATION_EFUSES + ] + else: + if self["BLK_VERSION_MAJOR"].get() == 1: + self.efuses += [ + EfuseField.convert(self, efuse) + for efuse in self.Fields.BLOCK2_CALIBRATION_EFUSES + ] + self.efuses += [ + EfuseField.convert(self, efuse) for efuse in self.Fields.CALC + ] + + def __getitem__(self, efuse_name): + """Return the efuse field with the given name""" + for e in self.efuses: + if efuse_name == e.name or any(x == efuse_name for x in e.alt_names): + return e + new_fields = False + for efuse in self.Fields.BLOCK2_CALIBRATION_EFUSES: + if efuse.name == efuse_name or any( + x == efuse_name for x in efuse.alt_names + ): + self.efuses += [ + EfuseField.convert(self, efuse) + for efuse in self.Fields.BLOCK2_CALIBRATION_EFUSES + ] + new_fields = True + if new_fields: + for e in self.efuses: + if efuse_name == e.name or any(x == efuse_name for x in e.alt_names): + return e + raise KeyError + + def read_coding_scheme(self): + self.coding_scheme = self.REGS.CODING_SCHEME_RS + + def print_status_regs(self): + print("") + self.blocks[0].print_block(self.blocks[0].err_bitarray, "err__regs", debug=True) + print( + "{:27} 0x{:08x}".format( + "EFUSE_RD_RS_ERR0_REG", self.read_reg(self.REGS.EFUSE_RD_RS_ERR0_REG) + ) + ) + print( + "{:27} 0x{:08x}".format( + "EFUSE_RD_RS_ERR1_REG", self.read_reg(self.REGS.EFUSE_RD_RS_ERR1_REG) + ) + ) + + def efuse_controller_setup(self): + self.set_efuse_timing() + self.clear_pgm_registers() + self.wait_efuse_idle() + + def write_efuses(self, block): + self.efuse_program(block) + return self.get_coding_scheme_warnings(silent=True) + + def clear_pgm_registers(self): + self.wait_efuse_idle() + for r in range( + self.REGS.EFUSE_PGM_DATA0_REG, self.REGS.EFUSE_PGM_DATA0_REG + 32, 4 + ): + self.write_reg(r, 0) + + def wait_efuse_idle(self): + deadline = time.time() + self.REGS.EFUSE_BURN_TIMEOUT + while time.time() < deadline: + cmds = self.REGS.EFUSE_PGM_CMD | self.REGS.EFUSE_READ_CMD + if self.read_reg(self.REGS.EFUSE_CMD_REG) & cmds == 0: + if self.read_reg(self.REGS.EFUSE_CMD_REG) & cmds == 0: + # Due to a hardware error, we have to read READ_CMD again + # to make sure the efuse clock is normal. + # For PGM_CMD it is not necessary. + return + raise esptool.FatalError( + "Timed out waiting for Efuse controller command to complete" + ) + + def efuse_program(self, block): + self.wait_efuse_idle() + self.write_reg(self.REGS.EFUSE_CONF_REG, self.REGS.EFUSE_WRITE_OP_CODE) + self.write_reg(self.REGS.EFUSE_CMD_REG, self.REGS.EFUSE_PGM_CMD | (block << 2)) + self.wait_efuse_idle() + self.clear_pgm_registers() + self.efuse_read() + + def efuse_read(self): + self.wait_efuse_idle() + self.write_reg(self.REGS.EFUSE_CONF_REG, self.REGS.EFUSE_READ_OP_CODE) + # need to add a delay after triggering EFUSE_READ_CMD, as ROM loader checks some + # efuse registers after each command is completed + # if ENABLE_SECURITY_DOWNLOAD or DIS_DOWNLOAD_MODE is enabled by the current cmd, then we need to try to reconnect to the chip. + try: + self.write_reg( + self.REGS.EFUSE_CMD_REG, self.REGS.EFUSE_READ_CMD, delay_after_us=1000 + ) + self.wait_efuse_idle() + except esptool.FatalError: + secure_download_mode_before = self._esp.secure_download_mode + + try: + self._esp = self.reconnect_chip(self._esp) + except esptool.FatalError: + print("Can not re-connect to the chip") + if not self["DIS_DOWNLOAD_MODE"].get() and self[ + "DIS_DOWNLOAD_MODE" + ].get(from_read=False): + print( + "This is the correct behavior as we are actually burning " + "DIS_DOWNLOAD_MODE which disables the connection to the chip" + ) + print("DIS_DOWNLOAD_MODE is enabled") + print("Successful") + sys.exit(0) # finish without errors + raise + + print("Established a connection with the chip") + if self._esp.secure_download_mode and not secure_download_mode_before: + print("Secure download mode is enabled") + if not self["ENABLE_SECURITY_DOWNLOAD"].get() and self[ + "ENABLE_SECURITY_DOWNLOAD" + ].get(from_read=False): + print( + "espefuse tool can not continue to work in Secure download mode" + ) + print("ENABLE_SECURITY_DOWNLOAD is enabled") + print("Successful") + sys.exit(0) # finish without errors + raise + + def set_efuse_timing(self): + """Set timing registers for burning efuses""" + # Configure clock + apb_freq = self.get_crystal_freq() + if apb_freq != 32: + raise esptool.FatalError( + "The eFuse supports only xtal=32M (xtal was %d)" % apb_freq + ) + + self.update_reg(self.REGS.EFUSE_DAC_CONF_REG, self.REGS.EFUSE_DAC_NUM_M, 0xFF) + self.update_reg( + self.REGS.EFUSE_DAC_CONF_REG, self.REGS.EFUSE_DAC_CLK_DIV_M, 0x28 + ) + self.update_reg( + self.REGS.EFUSE_WR_TIM_CONF1_REG, self.REGS.EFUSE_PWR_ON_NUM_M, 0x3000 + ) + self.update_reg( + self.REGS.EFUSE_WR_TIM_CONF2_REG, self.REGS.EFUSE_PWR_OFF_NUM_M, 0x190 + ) + + def get_coding_scheme_warnings(self, silent=False): + """Check if the coding scheme has detected any errors.""" + old_addr_reg = 0 + reg_value = 0 + ret_fail = False + for block in self.blocks: + if block.id == 0: + words = [ + self.read_reg(self.REGS.EFUSE_RD_REPEAT_ERR0_REG + offs * 4) + for offs in range(5) + ] + block.err_bitarray.pos = 0 + for word in reversed(words): + block.err_bitarray.overwrite(BitArray("uint:32=%d" % word)) + block.num_errors = block.err_bitarray.count(True) + block.fail = block.num_errors != 0 + else: + addr_reg, err_num_mask, err_num_offs, fail_bit = self.REGS.BLOCK_ERRORS[ + block.id + ] + if err_num_mask is None or err_num_offs is None or fail_bit is None: + continue + if addr_reg != old_addr_reg: + old_addr_reg = addr_reg + reg_value = self.read_reg(addr_reg) + block.fail = reg_value & (1 << fail_bit) != 0 + block.num_errors = (reg_value >> err_num_offs) & err_num_mask + ret_fail |= block.fail + if not silent and (block.fail or block.num_errors): + print( + "Error(s) in BLOCK%d [ERRORS:%d FAIL:%d]" + % (block.id, block.num_errors, block.fail) + ) + if (self.debug or ret_fail) and not silent: + self.print_status_regs() + return ret_fail + + def summary(self): + # TODO add support set_flash_voltage - "Flash voltage (VDD_SPI)" + return "" + + +class EfuseField(base_fields.EfuseFieldBase): + @staticmethod + def convert(parent, efuse): + return { + "mac": EfuseMacField, + "keypurpose": EfuseKeyPurposeField, + "t_sensor": EfuseTempSensor, + "adc_tp": EfuseAdcPointCalibration, + "wafer": EfuseWafer, + }.get(efuse.class_type, EfuseField)(parent, efuse) + + +class EfuseWafer(EfuseField): + def get(self, from_read=True): + hi_bits = self.parent["WAFER_VERSION_MINOR_HI"].get(from_read) + assert self.parent["WAFER_VERSION_MINOR_HI"].bit_len == 1 + lo_bits = self.parent["WAFER_VERSION_MINOR_LO"].get(from_read) + assert self.parent["WAFER_VERSION_MINOR_LO"].bit_len == 3 + return (hi_bits << 3) + lo_bits + + def save(self, new_value): + raise esptool.FatalError("Burning %s is not supported" % self.name) + + +class EfuseTempSensor(EfuseField): + def get(self, from_read=True): + value = self.get_bitstring(from_read) + sig = -1 if value[0] else 1 + return sig * value[1:].uint * 0.1 + + +class EfuseAdcPointCalibration(EfuseField): + def get(self, from_read=True): + STEP_SIZE = 4 + value = self.get_bitstring(from_read) + sig = -1 if value[0] else 1 + return sig * value[1:].uint * STEP_SIZE + + +class EfuseMacField(EfuseField): + def check_format(self, new_value_str): + if new_value_str is None: + raise esptool.FatalError( + "Required MAC Address in AA:CD:EF:01:02:03 format!" + ) + num_bytes = 8 if self.name == "MAC_EUI64" else 6 + if new_value_str.count(":") != num_bytes - 1: + raise esptool.FatalError( + f"MAC Address needs to be a {num_bytes}-byte hexadecimal format " + "separated by colons (:)!" + ) + hexad = new_value_str.replace(":", "").split(" ", 1)[0] + hexad = hexad.split(" ", 1)[0] if self.is_field_calculated() else hexad + if len(hexad) != num_bytes * 2: + raise esptool.FatalError( + f"MAC Address needs to be a {num_bytes}-byte hexadecimal number " + f"({num_bytes * 2} hexadecimal characters)!" + ) + # order of bytearray = b'\xaa\xcd\xef\x01\x02\x03', + bindata = binascii.unhexlify(hexad) + + if not self.is_field_calculated(): + # unicast address check according to + # https://tools.ietf.org/html/rfc7042#section-2.1 + if esptool.util.byte(bindata, 0) & 0x01: + raise esptool.FatalError("Custom MAC must be a unicast MAC!") + return bindata + + def check(self): + errs, fail = self.parent.get_block_errors(self.block) + if errs != 0 or fail: + output = "Block%d has ERRORS:%d FAIL:%d" % (self.block, errs, fail) + else: + output = "OK" + return "(" + output + ")" + + def get(self, from_read=True): + if self.name == "CUSTOM_MAC": + mac = self.get_raw(from_read)[::-1] + elif self.name == "MAC": + mac = self.get_raw(from_read) + elif self.name == "MAC_EUI64": + mac = self.parent["MAC"].get_bitstring(from_read).copy() + mac_ext = self.parent["MAC_EXT"].get_bitstring(from_read) + mac.insert(mac_ext, 24) + mac = mac.bytes + else: + mac = self.get_raw(from_read) + return "%s %s" % (util.hexify(mac, ":"), self.check()) + + def save(self, new_value): + def print_field(e, new_value): + print( + " - '{}' ({}) {} -> {}".format( + e.name, e.description, e.get_bitstring(), new_value + ) + ) + + if self.name == "CUSTOM_MAC": + bitarray_mac = self.convert_to_bitstring(new_value) + print_field(self, bitarray_mac) + super(EfuseMacField, self).save(new_value) + else: + # Writing the BLOCK1 (MAC_SPI_8M_0) default MAC is not possible, + # as it's written in the factory. + raise esptool.FatalError(f"Burning {self.name} is not supported") + raise esptool.FatalError("Writing Factory MAC address is not supported") + + +# fmt: off +class EfuseKeyPurposeField(EfuseField): + KEY_PURPOSES = [ + ("USER", 0, None, None, "no_need_rd_protect"), # User purposes (software-only use) + ("RESERVED", 1, None, None, "no_need_rd_protect"), # Reserved + ("XTS_AES_128_KEY", 4, None, "Reverse", "need_rd_protect"), # XTS_AES_128_KEY (flash/PSRAM encryption) + ("HMAC_DOWN_ALL", 5, None, None, "need_rd_protect"), # HMAC Downstream mode + ("HMAC_DOWN_JTAG", 6, None, None, "need_rd_protect"), # JTAG soft enable key (uses HMAC Downstream mode) + ("HMAC_DOWN_DIGITAL_SIGNATURE", 7, None, None, "need_rd_protect"), # Digital Signature peripheral key (uses HMAC Downstream mode) + ("HMAC_UP", 8, None, None, "need_rd_protect"), # HMAC Upstream mode + ("SECURE_BOOT_DIGEST0", 9, "DIGEST", None, "no_need_rd_protect"), # SECURE_BOOT_DIGEST0 (Secure Boot key digest) + ("SECURE_BOOT_DIGEST1", 10, "DIGEST", None, "no_need_rd_protect"), # SECURE_BOOT_DIGEST1 (Secure Boot key digest) + ("SECURE_BOOT_DIGEST2", 11, "DIGEST", None, "no_need_rd_protect"), # SECURE_BOOT_DIGEST2 (Secure Boot key digest) + ] +# fmt: on + + KEY_PURPOSES_NAME = [name[0] for name in KEY_PURPOSES] + DIGEST_KEY_PURPOSES = [name[0] for name in KEY_PURPOSES if name[2] == "DIGEST"] + + def check_format(self, new_value_str): + # str convert to int: "XTS_AES_128_KEY" - > str(4) + # if int: 4 -> str(4) + raw_val = new_value_str + for purpose_name in self.KEY_PURPOSES: + if purpose_name[0] == new_value_str: + raw_val = str(purpose_name[1]) + break + if raw_val.isdigit(): + if int(raw_val) not in [p[1] for p in self.KEY_PURPOSES if p[1] > 0]: + raise esptool.FatalError("'%s' can not be set (value out of range)" % raw_val) + else: + raise esptool.FatalError("'%s' unknown name" % raw_val) + return raw_val + + def need_reverse(self, new_key_purpose): + for key in self.KEY_PURPOSES: + if key[0] == new_key_purpose: + return key[3] == "Reverse" + + def need_rd_protect(self, new_key_purpose): + for key in self.KEY_PURPOSES: + if key[0] == new_key_purpose: + return key[4] == "need_rd_protect" + + def get(self, from_read=True): + for p in self.KEY_PURPOSES: + if p[1] == self.get_raw(from_read): + return p[0] + return "FORBIDDEN_STATE" + + def get_name(self, raw_val): + for key in self.KEY_PURPOSES: + if key[1] == raw_val: + return key[0] + + def save(self, new_value): + raw_val = int(self.check_format(str(new_value))) + str_new_value = self.get_name(raw_val) + if self.name == "KEY_PURPOSE_5" and str_new_value.startswith("XTS_AES"): + raise esptool.FatalError(f"{self.name} can not have {str_new_value} key due to a hardware bug (please see TRM for more details)") + return super(EfuseKeyPurposeField, self).save(raw_val) diff --git a/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32h2beta1/mem_definition.py b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32h2beta1/mem_definition.py new file mode 100644 index 0000000..ee2f006 --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32h2beta1/mem_definition.py @@ -0,0 +1,155 @@ +# This file describes eFuses fields and registers for ESP32-H2 chip +# +# SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD +# +# SPDX-License-Identifier: GPL-2.0-or-later + +import os + +import yaml + +from ..mem_definition_base import EfuseBlocksBase, EfuseFieldsBase, EfuseRegistersBase + + +class EfuseDefineRegisters(EfuseRegistersBase): + EFUSE_MEM_SIZE = 0x01FC + 4 + + # EFUSE registers & command/conf values + DR_REG_EFUSE_BASE = 0x6001A000 + EFUSE_PGM_DATA0_REG = DR_REG_EFUSE_BASE + EFUSE_CHECK_VALUE0_REG = DR_REG_EFUSE_BASE + 0x020 + EFUSE_CLK_REG = DR_REG_EFUSE_BASE + 0x1C8 + EFUSE_CONF_REG = DR_REG_EFUSE_BASE + 0x1CC + EFUSE_STATUS_REG = DR_REG_EFUSE_BASE + 0x1D0 + EFUSE_CMD_REG = DR_REG_EFUSE_BASE + 0x1D4 + EFUSE_RD_RS_ERR0_REG = DR_REG_EFUSE_BASE + 0x1C0 + EFUSE_RD_RS_ERR1_REG = DR_REG_EFUSE_BASE + 0x1C4 + EFUSE_RD_REPEAT_ERR0_REG = DR_REG_EFUSE_BASE + 0x17C + EFUSE_RD_REPEAT_ERR1_REG = DR_REG_EFUSE_BASE + 0x180 + EFUSE_RD_REPEAT_ERR2_REG = DR_REG_EFUSE_BASE + 0x184 + EFUSE_RD_REPEAT_ERR3_REG = DR_REG_EFUSE_BASE + 0x188 + EFUSE_RD_REPEAT_ERR4_REG = DR_REG_EFUSE_BASE + 0x18C + EFUSE_DAC_CONF_REG = DR_REG_EFUSE_BASE + 0x1E8 + EFUSE_RD_TIM_CONF_REG = DR_REG_EFUSE_BASE + 0x1EC + EFUSE_WR_TIM_CONF1_REG = DR_REG_EFUSE_BASE + 0x1F0 + EFUSE_WR_TIM_CONF2_REG = DR_REG_EFUSE_BASE + 0x1F4 + EFUSE_DATE_REG = DR_REG_EFUSE_BASE + 0x1FC + EFUSE_WRITE_OP_CODE = 0x5A5A + EFUSE_READ_OP_CODE = 0x5AA5 + EFUSE_PGM_CMD_MASK = 0x3 + EFUSE_PGM_CMD = 0x2 + EFUSE_READ_CMD = 0x1 + + BLOCK_ERRORS = [ + # error_reg, err_num_mask, err_num_offs, fail_bit + (EFUSE_RD_REPEAT_ERR0_REG, None, None, None), # BLOCK0 + (EFUSE_RD_RS_ERR0_REG, 0x7, 0, 3), # MAC_SPI_8M_0 + (EFUSE_RD_RS_ERR0_REG, 0x7, 4, 7), # BLOCK_SYS_DATA + (EFUSE_RD_RS_ERR0_REG, 0x7, 8, 11), # BLOCK_USR_DATA + (EFUSE_RD_RS_ERR0_REG, 0x7, 12, 15), # BLOCK_KEY0 + (EFUSE_RD_RS_ERR0_REG, 0x7, 16, 19), # BLOCK_KEY1 + (EFUSE_RD_RS_ERR0_REG, 0x7, 20, 23), # BLOCK_KEY2 + (EFUSE_RD_RS_ERR0_REG, 0x7, 24, 27), # BLOCK_KEY3 + (EFUSE_RD_RS_ERR0_REG, 0x7, 28, 31), # BLOCK_KEY4 + (EFUSE_RD_RS_ERR1_REG, 0x7, 0, 3), # BLOCK_KEY5 + (EFUSE_RD_RS_ERR1_REG, 0x7, 4, 7), # BLOCK_SYS_DATA2 + ] + + # EFUSE_WR_TIM_CONF2_REG + EFUSE_PWR_OFF_NUM_S = 0 + EFUSE_PWR_OFF_NUM_M = 0xFFFF << EFUSE_PWR_OFF_NUM_S + + # EFUSE_WR_TIM_CONF1_REG + EFUSE_PWR_ON_NUM_S = 8 + EFUSE_PWR_ON_NUM_M = 0x0000FFFF << EFUSE_PWR_ON_NUM_S + + # EFUSE_DAC_CONF_REG + EFUSE_DAC_CLK_DIV_S = 0 + EFUSE_DAC_CLK_DIV_M = 0xFF << EFUSE_DAC_CLK_DIV_S + + # EFUSE_DAC_CONF_REG + EFUSE_DAC_NUM_S = 9 + EFUSE_DAC_NUM_M = 0xFF << EFUSE_DAC_NUM_S + + +class EfuseDefineBlocks(EfuseBlocksBase): + __base_rd_regs = EfuseDefineRegisters.DR_REG_EFUSE_BASE + __base_wr_regs = EfuseDefineRegisters.EFUSE_PGM_DATA0_REG + # List of efuse blocks + # fmt: off + BLOCKS = [ + # Name, Alias, Index, Read address, Write address, Write protect bit, Read protect bit, Len, key_purpose + ("BLOCK0", [], 0, __base_rd_regs + 0x02C, __base_wr_regs, None, None, 6, None), + ("MAC_SPI_8M_0", ["BLOCK1"], 1, __base_rd_regs + 0x044, __base_wr_regs, 20, None, 6, None), + ("BLOCK_SYS_DATA", ["BLOCK2"], 2, __base_rd_regs + 0x05C, __base_wr_regs, 21, None, 8, None), + ("BLOCK_USR_DATA", ["BLOCK3"], 3, __base_rd_regs + 0x07C, __base_wr_regs, 22, None, 8, None), + ("BLOCK_KEY0", ["BLOCK4"], 4, __base_rd_regs + 0x09C, __base_wr_regs, 23, 0, 8, "KEY_PURPOSE_0"), + ("BLOCK_KEY1", ["BLOCK5"], 5, __base_rd_regs + 0x0BC, __base_wr_regs, 24, 1, 8, "KEY_PURPOSE_1"), + ("BLOCK_KEY2", ["BLOCK6"], 6, __base_rd_regs + 0x0DC, __base_wr_regs, 25, 2, 8, "KEY_PURPOSE_2"), + ("BLOCK_KEY3", ["BLOCK7"], 7, __base_rd_regs + 0x0FC, __base_wr_regs, 26, 3, 8, "KEY_PURPOSE_3"), + ("BLOCK_KEY4", ["BLOCK8"], 8, __base_rd_regs + 0x11C, __base_wr_regs, 27, 4, 8, "KEY_PURPOSE_4"), + ("BLOCK_KEY5", ["BLOCK9"], 9, __base_rd_regs + 0x13C, __base_wr_regs, 28, 5, 8, "KEY_PURPOSE_5"), + ("BLOCK_SYS_DATA2", ["BLOCK10"], 10, __base_rd_regs + 0x15C, __base_wr_regs, 29, 6, 8, None), + ] + # fmt: on + + def get_burn_block_data_names(self): + list_of_names = [] + for block in self.BLOCKS: + blk = self.get(block) + if blk.name: + list_of_names.append(blk.name) + if blk.alias: + for alias in blk.alias: + list_of_names.append(alias) + return list_of_names + + +class EfuseDefineFields(EfuseFieldsBase): + def __init__(self) -> None: + # List of efuse fields from TRM the chapter eFuse Controller. + self.EFUSES = [] + + self.KEYBLOCKS = [] + + # if BLK_VERSION_MAJOR is 1, these efuse fields are in BLOCK2 + self.BLOCK2_CALIBRATION_EFUSES = [] + + self.CALC = [] + + dir_name = os.path.dirname(os.path.abspath(__file__)) + dir_name, file_name = os.path.split(dir_name) + file_name = file_name + ".yaml" + dir_name, _ = os.path.split(dir_name) + efuse_file = os.path.join(dir_name, "efuse_defs", file_name) + efuse_file = efuse_file.replace("esp32h2beta1", "esp32h2") + with open(f"{efuse_file}", "r") as r_file: + e_desc = yaml.safe_load(r_file) + super().__init__(e_desc) + + for i, efuse in enumerate(self.ALL_EFUSES): + if efuse.name in [ + "BLOCK_USR_DATA", + "BLOCK_KEY0", + "BLOCK_KEY1", + "BLOCK_KEY2", + "BLOCK_KEY3", + "BLOCK_KEY4", + "BLOCK_KEY5", + "BLOCK_SYS_DATA2", + ]: + if efuse.name == "BLOCK_USR_DATA": + efuse.bit_len = 256 + efuse.type = "bytes:32" + self.KEYBLOCKS.append(efuse) + self.ALL_EFUSES[i] = None + + elif efuse.category == "calibration": + self.BLOCK2_CALIBRATION_EFUSES.append(efuse) + self.ALL_EFUSES[i] = None + + for efuse in self.ALL_EFUSES: + if efuse is not None: + self.EFUSES.append(efuse) + + self.ALL_EFUSES = [] diff --git a/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32h2beta1/operations.py b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32h2beta1/operations.py new file mode 100644 index 0000000..9f60254 --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32h2beta1/operations.py @@ -0,0 +1,420 @@ +# This file includes the operations with eFuses for ESP32-H2 chip +# +# SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD +# +# SPDX-License-Identifier: GPL-2.0-or-later + +import argparse +import os # noqa: F401. It is used in IDF scripts +import traceback + +import espsecure + +import esptool + +from . import fields +from .. import util +from ..base_operations import ( + add_common_commands, + add_force_write_always, + add_show_sensitive_info_option, + burn_bit, + burn_block_data, + burn_efuse, + check_error, + dump, + read_protect_efuse, + summary, + write_protect_efuse, +) + + +def protect_options(p): + p.add_argument( + "--no-write-protect", + help="Disable write-protecting of the key. The key remains writable. " + "(The keys use the RS coding scheme that does not support post-write " + "data changes. Forced write can damage RS encoding bits.) " + "The write-protecting of keypurposes does not depend on the option, " + "it will be set anyway.", + action="store_true", + ) + p.add_argument( + "--no-read-protect", + help="Disable read-protecting of the key. The key remains readable software." + "The key with keypurpose[USER, RESERVED and *_DIGEST] will remain " + "readable anyway. For the rest keypurposes the read-protection will be " + "defined the option (Read-protect by default).", + action="store_true", + ) + + +def add_commands(subparsers, efuses): + add_common_commands(subparsers, efuses) + burn_key = subparsers.add_parser( + "burn_key", help="Burn the key block with the specified name" + ) + protect_options(burn_key) + add_force_write_always(burn_key) + add_show_sensitive_info_option(burn_key) + burn_key.add_argument( + "block", + help="Key block to burn", + action="append", + choices=efuses.BLOCKS_FOR_KEYS, + ) + burn_key.add_argument( + "keyfile", + help="File containing 256 bits of binary key data", + action="append", + type=argparse.FileType("rb"), + ) + burn_key.add_argument( + "keypurpose", + help="Purpose to set.", + action="append", + choices=fields.EfuseKeyPurposeField.KEY_PURPOSES_NAME, + ) + for _ in efuses.BLOCKS_FOR_KEYS: + burn_key.add_argument( + "block", + help="Key block to burn", + nargs="?", + action="append", + metavar="BLOCK", + choices=efuses.BLOCKS_FOR_KEYS, + ) + burn_key.add_argument( + "keyfile", + help="File containing 256 bits of binary key data", + nargs="?", + action="append", + metavar="KEYFILE", + type=argparse.FileType("rb"), + ) + burn_key.add_argument( + "keypurpose", + help="Purpose to set.", + nargs="?", + action="append", + metavar="KEYPURPOSE", + choices=fields.EfuseKeyPurposeField.KEY_PURPOSES_NAME, + ) + + burn_key_digest = subparsers.add_parser( + "burn_key_digest", + help="Parse a RSA public key and burn the digest to key efuse block", + ) + protect_options(burn_key_digest) + add_force_write_always(burn_key_digest) + add_show_sensitive_info_option(burn_key_digest) + burn_key_digest.add_argument( + "block", + help="Key block to burn", + action="append", + choices=efuses.BLOCKS_FOR_KEYS, + ) + burn_key_digest.add_argument( + "keyfile", + help="Key file to digest (PEM format)", + action="append", + type=argparse.FileType("rb"), + ) + burn_key_digest.add_argument( + "keypurpose", + help="Purpose to set.", + action="append", + choices=fields.EfuseKeyPurposeField.DIGEST_KEY_PURPOSES, + ) + for _ in efuses.BLOCKS_FOR_KEYS: + burn_key_digest.add_argument( + "block", + help="Key block to burn", + nargs="?", + action="append", + metavar="BLOCK", + choices=efuses.BLOCKS_FOR_KEYS, + ) + burn_key_digest.add_argument( + "keyfile", + help="Key file to digest (PEM format)", + nargs="?", + action="append", + metavar="KEYFILE", + type=argparse.FileType("rb"), + ) + burn_key_digest.add_argument( + "keypurpose", + help="Purpose to set.", + nargs="?", + action="append", + metavar="KEYPURPOSE", + choices=fields.EfuseKeyPurposeField.DIGEST_KEY_PURPOSES, + ) + + p = subparsers.add_parser( + "set_flash_voltage", + help="Permanently set the internal flash voltage regulator " + "to either 1.8V, 3.3V or OFF. This means GPIO45 can be high or low " + "at reset without changing the flash voltage.", + ) + p.add_argument("voltage", help="Voltage selection", choices=["1.8V", "3.3V", "OFF"]) + + p = subparsers.add_parser( + "burn_custom_mac", help="Burn a 48-bit Custom MAC Address to EFUSE BLOCK3." + ) + p.add_argument( + "mac", + help="Custom MAC Address to burn given in hexadecimal format with bytes " + "separated by colons (e.g. AA:CD:EF:01:02:03).", + type=fields.base_fields.CheckArgValue(efuses, "CUSTOM_MAC"), + ) + add_force_write_always(p) + + p = subparsers.add_parser("get_custom_mac", help="Prints the Custom MAC Address.") + + +def burn_custom_mac(esp, efuses, args): + efuses["CUSTOM_MAC"].save(args.mac) + if not efuses.burn_all(check_batch_mode=True): + return + get_custom_mac(esp, efuses, args) + print("Successful") + + +def get_custom_mac(esp, efuses, args): + print("Custom MAC Address: {}".format(efuses["CUSTOM_MAC"].get())) + + +def set_flash_voltage(esp, efuses, args): + raise esptool.FatalError("set_flash_voltage is not supported!") + + +def adc_info(esp, efuses, args): + print("") + # fmt: off + if efuses["BLK_VERSION_MAJOR"].get() == 1: + print("Temperature Sensor Calibration = {}C".format(efuses["TEMP_SENSOR_CAL"].get())) + + print("") + print("ADC1 readings stored in efuse BLOCK2:") + print(" MODE0 D1 reading (250mV): {}".format(efuses["ADC1_MODE0_D1"].get())) + print(" MODE0 D2 reading (600mV): {}".format(efuses["ADC1_MODE0_D2"].get())) + + print(" MODE1 D1 reading (250mV): {}".format(efuses["ADC1_MODE1_D1"].get())) + print(" MODE1 D2 reading (800mV): {}".format(efuses["ADC1_MODE1_D2"].get())) + + print(" MODE2 D1 reading (250mV): {}".format(efuses["ADC1_MODE2_D1"].get())) + print(" MODE2 D2 reading (1000mV): {}".format(efuses["ADC1_MODE2_D2"].get())) + + print(" MODE3 D1 reading (250mV): {}".format(efuses["ADC1_MODE3_D1"].get())) + print(" MODE3 D2 reading (2000mV): {}".format(efuses["ADC1_MODE3_D2"].get())) + + print("") + print("ADC2 readings stored in efuse BLOCK2:") + print(" MODE0 D1 reading (250mV): {}".format(efuses["ADC2_MODE0_D1"].get())) + print(" MODE0 D2 reading (600mV): {}".format(efuses["ADC2_MODE0_D2"].get())) + + print(" MODE1 D1 reading (250mV): {}".format(efuses["ADC2_MODE1_D1"].get())) + print(" MODE1 D2 reading (800mV): {}".format(efuses["ADC2_MODE1_D2"].get())) + + print(" MODE2 D1 reading (250mV): {}".format(efuses["ADC2_MODE2_D1"].get())) + print(" MODE2 D2 reading (1000mV): {}".format(efuses["ADC2_MODE2_D2"].get())) + + print(" MODE3 D1 reading (250mV): {}".format(efuses["ADC2_MODE3_D1"].get())) + print(" MODE3 D2 reading (2000mV): {}".format(efuses["ADC2_MODE3_D2"].get())) + else: + print("BLK_VERSION_MAJOR = {}".format(efuses["BLK_VERSION_MAJOR"].get_meaning())) + # fmt: on + + +def burn_key(esp, efuses, args, digest=None): + if digest is None: + datafile_list = args.keyfile[ + 0 : len([name for name in args.keyfile if name is not None]) : + ] + else: + datafile_list = digest[0 : len([name for name in digest if name is not None]) :] + efuses.force_write_always = args.force_write_always + block_name_list = args.block[ + 0 : len([name for name in args.block if name is not None]) : + ] + keypurpose_list = args.keypurpose[ + 0 : len([name for name in args.keypurpose if name is not None]) : + ] + + util.check_duplicate_name_in_list(block_name_list) + if len(block_name_list) != len(datafile_list) or len(block_name_list) != len( + keypurpose_list + ): + raise esptool.FatalError( + "The number of blocks (%d), datafile (%d) and keypurpose (%d) " + "should be the same." + % (len(block_name_list), len(datafile_list), len(keypurpose_list)) + ) + + print("Burn keys to blocks:") + for block_name, datafile, keypurpose in zip( + block_name_list, datafile_list, keypurpose_list + ): + efuse = None + for block in efuses.blocks: + if block_name == block.name or block_name in block.alias: + efuse = efuses[block.name] + if efuse is None: + raise esptool.FatalError("Unknown block name - %s" % (block_name)) + num_bytes = efuse.bit_len // 8 + + block_num = efuses.get_index_block_by_name(block_name) + block = efuses.blocks[block_num] + + if digest is None: + data = datafile.read() + else: + data = datafile + + print(" - %s" % (efuse.name), end=" ") + revers_msg = None + if efuses[block.key_purpose_name].need_reverse(keypurpose): + revers_msg = "\tReversing byte order for AES-XTS hardware peripheral" + data = data[::-1] + print( + "-> [{}]".format( + util.hexify(data, " ") + if args.show_sensitive_info + else " ".join(["??"] * len(data)) + ) + ) + if revers_msg: + print(revers_msg) + if len(data) != num_bytes: + raise esptool.FatalError( + "Incorrect key file size %d. Key file must be %d bytes (%d bits) " + "of raw binary key data." % (len(data), num_bytes, num_bytes * 8) + ) + + if efuses[block.key_purpose_name].need_rd_protect(keypurpose): + read_protect = False if args.no_read_protect else True + else: + read_protect = False + write_protect = not args.no_write_protect + + # using efuse instead of a block gives the advantage of checking it as the whole field. + efuse.save(data) + + disable_wr_protect_key_purpose = False + if efuses[block.key_purpose_name].get() != keypurpose: + if efuses[block.key_purpose_name].is_writeable(): + print( + "\t'%s': '%s' -> '%s'." + % ( + block.key_purpose_name, + efuses[block.key_purpose_name].get(), + keypurpose, + ) + ) + efuses[block.key_purpose_name].save(keypurpose) + disable_wr_protect_key_purpose = True + else: + raise esptool.FatalError( + "It is not possible to change '%s' to '%s' because write " + "protection bit is set." % (block.key_purpose_name, keypurpose) + ) + else: + print("\t'%s' is already '%s'." % (block.key_purpose_name, keypurpose)) + if efuses[block.key_purpose_name].is_writeable(): + disable_wr_protect_key_purpose = True + + if disable_wr_protect_key_purpose: + print("\tDisabling write to '%s'." % block.key_purpose_name) + efuses[block.key_purpose_name].disable_write() + + if read_protect: + print("\tDisabling read to key block") + efuse.disable_read() + + if write_protect: + print("\tDisabling write to key block") + efuse.disable_write() + print("") + + if not write_protect: + print("Keys will remain writeable (due to --no-write-protect)") + if args.no_read_protect: + print("Keys will remain readable (due to --no-read-protect)") + + if not efuses.burn_all(check_batch_mode=True): + return + print("Successful") + + +def burn_key_digest(esp, efuses, args): + digest_list = [] + datafile_list = args.keyfile[ + 0 : len([name for name in args.keyfile if name is not None]) : + ] + block_list = args.block[ + 0 : len([block for block in args.block if block is not None]) : + ] + for block_name, datafile in zip(block_list, datafile_list): + efuse = None + for block in efuses.blocks: + if block_name == block.name or block_name in block.alias: + efuse = efuses[block.name] + if efuse is None: + raise esptool.FatalError("Unknown block name - %s" % (block_name)) + num_bytes = efuse.bit_len // 8 + digest = espsecure._digest_sbv2_public_key(datafile) + if len(digest) != num_bytes: + raise esptool.FatalError( + "Incorrect digest size %d. Digest must be %d bytes (%d bits) of raw " + "binary key data." % (len(digest), num_bytes, num_bytes * 8) + ) + digest_list.append(digest) + burn_key(esp, efuses, args, digest=digest_list) + + +def espefuse(esp, efuses, args, command): + parser = argparse.ArgumentParser() + subparsers = parser.add_subparsers(dest="operation") + add_commands(subparsers, efuses) + try: + cmd_line_args = parser.parse_args(command.split()) + except SystemExit: + traceback.print_stack() + raise esptool.FatalError('"{}" - incorrect command'.format(command)) + if cmd_line_args.operation == "execute_scripts": + configfiles = cmd_line_args.configfiles + index = cmd_line_args.index + # copy arguments from args to cmd_line_args + vars(cmd_line_args).update(vars(args)) + if cmd_line_args.operation == "execute_scripts": + cmd_line_args.configfiles = configfiles + cmd_line_args.index = index + if cmd_line_args.operation is None: + parser.print_help() + parser.exit(1) + operation_func = globals()[cmd_line_args.operation] + # each 'operation' is a module-level function of the same name + operation_func(esp, efuses, cmd_line_args) + + +def execute_scripts(esp, efuses, args): + efuses.batch_mode_cnt += 1 + del args.operation + scripts = args.scripts + del args.scripts + + for file in scripts: + with open(file.name, "r") as file: + exec(compile(file.read(), file.name, "exec")) + + if args.debug: + for block in efuses.blocks: + data = block.get_bitstring(from_read=False) + block.print_block(data, "regs_for_burn", args.debug) + + efuses.batch_mode_cnt -= 1 + if not efuses.burn_all(check_batch_mode=True): + return + print("Successful") diff --git a/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32p4/__init__.py b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32p4/__init__.py new file mode 100644 index 0000000..a3b55a8 --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32p4/__init__.py @@ -0,0 +1,3 @@ +from . import operations +from .emulate_efuse_controller import EmulateEfuseController +from .fields import EspEfuses diff --git a/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32p4/__pycache__/__init__.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32p4/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..38de20b Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32p4/__pycache__/__init__.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32p4/__pycache__/emulate_efuse_controller.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32p4/__pycache__/emulate_efuse_controller.cpython-312.pyc new file mode 100644 index 0000000..7ef18ef Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32p4/__pycache__/emulate_efuse_controller.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32p4/__pycache__/fields.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32p4/__pycache__/fields.cpython-312.pyc new file mode 100644 index 0000000..e4d75ea Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32p4/__pycache__/fields.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32p4/__pycache__/mem_definition.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32p4/__pycache__/mem_definition.cpython-312.pyc new file mode 100644 index 0000000..ef868f6 Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32p4/__pycache__/mem_definition.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32p4/__pycache__/operations.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32p4/__pycache__/operations.cpython-312.pyc new file mode 100644 index 0000000..fa02826 Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32p4/__pycache__/operations.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32p4/emulate_efuse_controller.py b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32p4/emulate_efuse_controller.py new file mode 100644 index 0000000..aa670af --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32p4/emulate_efuse_controller.py @@ -0,0 +1,92 @@ +# This file describes eFuses controller for ESP32-P4 chip +# +# SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD +# +# SPDX-License-Identifier: GPL-2.0-or-later + +import reedsolo + +from .mem_definition import EfuseDefineBlocks, EfuseDefineFields, EfuseDefineRegisters +from ..emulate_efuse_controller_base import EmulateEfuseControllerBase, FatalError + + +class EmulateEfuseController(EmulateEfuseControllerBase): + """The class for virtual efuse operation. Using for HOST_TEST.""" + + CHIP_NAME = "ESP32-P4" + mem = None + debug = False + + def __init__(self, efuse_file=None, debug=False): + self.Blocks = EfuseDefineBlocks + self.Fields = EfuseDefineFields() + self.REGS = EfuseDefineRegisters + super(EmulateEfuseController, self).__init__(efuse_file, debug) + self.write_reg(self.REGS.EFUSE_CMD_REG, 0) + + """ esptool method start >>""" + + def get_major_chip_version(self): + return 0 + + def get_minor_chip_version(self): + return 0 + + def get_crystal_freq(self): + return 40 # MHz (common for all chips) + + def get_security_info(self): + return { + "flags": 0, + "flash_crypt_cnt": 0, + "key_purposes": 0, + "chip_id": 0, + "api_version": 0, + } + + """ << esptool method end """ + + def handle_writing_event(self, addr, value): + if addr == self.REGS.EFUSE_CMD_REG: + if value & self.REGS.EFUSE_PGM_CMD: + self.copy_blocks_wr_regs_to_rd_regs(updated_block=(value >> 2) & 0xF) + self.clean_blocks_wr_regs() + self.check_rd_protection_area() + self.write_reg(addr, 0) + self.write_reg(self.REGS.EFUSE_CMD_REG, 0) + elif value == self.REGS.EFUSE_READ_CMD: + self.write_reg(addr, 0) + self.write_reg(self.REGS.EFUSE_CMD_REG, 0) + self.save_to_file() + + def get_bitlen_of_block(self, blk, wr=False): + if blk.id == 0: + if wr: + return 32 * 8 + else: + return 32 * blk.len + else: + if wr: + rs_coding = 32 * 3 + return 32 * 8 + rs_coding + else: + return 32 * blk.len + + def handle_coding_scheme(self, blk, data): + if blk.id != 0: + # CODING_SCHEME RS applied only for all blocks except BLK0. + coded_bytes = 12 + data.pos = coded_bytes * 8 + plain_data = data.readlist("32*uint:8")[::-1] + # takes 32 bytes + # apply RS encoding + rs = reedsolo.RSCodec(coded_bytes) + # 32 byte of data + 12 bytes RS + calc_encoded_data = list(rs.encode([x for x in plain_data])) + data.pos = 0 + if calc_encoded_data != data.readlist("44*uint:8")[::-1]: + raise FatalError("Error in coding scheme data") + data = data[coded_bytes * 8 :] + if blk.len < 8: + data = data[(8 - blk.len) * 32 :] + return data diff --git a/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32p4/fields.py b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32p4/fields.py new file mode 100644 index 0000000..415a6b3 --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32p4/fields.py @@ -0,0 +1,435 @@ +# This file describes eFuses for ESP32-P4 chip +# +# SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD +# +# SPDX-License-Identifier: GPL-2.0-or-later + +import binascii +import struct +import sys +import time + +from bitstring import BitArray + +import esptool + +import reedsolo + +from .mem_definition import EfuseDefineBlocks, EfuseDefineFields, EfuseDefineRegisters +from .. import base_fields +from .. import util + + +class EfuseBlock(base_fields.EfuseBlockBase): + def len_of_burn_unit(self): + # The writing register window is 8 registers for any blocks. + # len in bytes + return 8 * 4 + + def __init__(self, parent, param, skip_read=False): + parent.read_coding_scheme() + super(EfuseBlock, self).__init__(parent, param, skip_read=skip_read) + + def apply_coding_scheme(self): + data = self.get_raw(from_read=False)[::-1] + if len(data) < self.len_of_burn_unit(): + add_empty_bytes = self.len_of_burn_unit() - len(data) + data = data + (b"\x00" * add_empty_bytes) + if self.get_coding_scheme() == self.parent.REGS.CODING_SCHEME_RS: + # takes 32 bytes + # apply RS encoding + rs = reedsolo.RSCodec(12) + # 32 byte of data + 12 bytes RS + encoded_data = rs.encode([x for x in data]) + words = struct.unpack("<" + "I" * 11, encoded_data) + # returns 11 words (8 words of data + 3 words of RS coding) + else: + # takes 32 bytes + words = struct.unpack("<" + ("I" * (len(data) // 4)), data) + # returns 8 words + return words + + +class EspEfuses(base_fields.EspEfusesBase): + """ + Wrapper object to manage the efuse fields in a connected ESP bootloader + """ + + debug = False + do_not_confirm = False + + def __init__(self, esp, skip_connect=False, debug=False, do_not_confirm=False): + self.Blocks = EfuseDefineBlocks() + self.Fields = EfuseDefineFields() + self.REGS = EfuseDefineRegisters + self.BURN_BLOCK_DATA_NAMES = self.Blocks.get_burn_block_data_names() + self.BLOCKS_FOR_KEYS = self.Blocks.get_blocks_for_keys() + self._esp = esp + self.debug = debug + self.do_not_confirm = do_not_confirm + if esp.CHIP_NAME != "ESP32-P4": + raise esptool.FatalError( + "Expected the 'esp' param for ESP32-P4 chip but got for '%s'." + % (esp.CHIP_NAME) + ) + if not skip_connect: + flags = self._esp.get_security_info()["flags"] + GET_SECURITY_INFO_FLAG_SECURE_DOWNLOAD_ENABLE = 1 << 2 + if flags & GET_SECURITY_INFO_FLAG_SECURE_DOWNLOAD_ENABLE: + raise esptool.FatalError( + "Secure Download Mode is enabled. The tool can not read eFuses." + ) + self.blocks = [ + EfuseBlock(self, self.Blocks.get(block), skip_read=skip_connect) + for block in self.Blocks.BLOCKS + ] + if not skip_connect: + self.get_coding_scheme_warnings() + self.efuses = [EfuseField.convert(self, efuse) for efuse in self.Fields.EFUSES] + self.efuses += [ + EfuseField.convert(self, efuse) for efuse in self.Fields.KEYBLOCKS + ] + if skip_connect: + self.efuses += [ + EfuseField.convert(self, efuse) + for efuse in self.Fields.BLOCK2_CALIBRATION_EFUSES + ] + else: + # TODO add processing of self.Fields.BLOCK2_CALIBRATION_EFUSES + # if self["BLK_VERSION_MINOR"].get() == 1: + # self.efuses += [ + # EfuseField.convert(self, efuse) + # for efuse in self.Fields.BLOCK2_CALIBRATION_EFUSES + # ] + self.efuses += [ + EfuseField.convert(self, efuse) for efuse in self.Fields.CALC + ] + + def __getitem__(self, efuse_name): + """Return the efuse field with the given name""" + for e in self.efuses: + if efuse_name == e.name or any(x == efuse_name for x in e.alt_names): + return e + new_fields = False + for efuse in self.Fields.BLOCK2_CALIBRATION_EFUSES: + if efuse.name == efuse_name or any( + x == efuse_name for x in efuse.alt_names + ): + self.efuses += [ + EfuseField.convert(self, efuse) + for efuse in self.Fields.BLOCK2_CALIBRATION_EFUSES + ] + new_fields = True + if new_fields: + for e in self.efuses: + if efuse_name == e.name or any(x == efuse_name for x in e.alt_names): + return e + raise KeyError + + def read_coding_scheme(self): + self.coding_scheme = self.REGS.CODING_SCHEME_RS + + def print_status_regs(self): + print("") + self.blocks[0].print_block(self.blocks[0].err_bitarray, "err__regs", debug=True) + print( + "{:27} 0x{:08x}".format( + "EFUSE_RD_RS_ERR0_REG", self.read_reg(self.REGS.EFUSE_RD_RS_ERR0_REG) + ) + ) + print( + "{:27} 0x{:08x}".format( + "EFUSE_RD_RS_ERR1_REG", self.read_reg(self.REGS.EFUSE_RD_RS_ERR1_REG) + ) + ) + + def efuse_controller_setup(self): + self.set_efuse_timing() + self.clear_pgm_registers() + self.wait_efuse_idle() + + def write_efuses(self, block): + self.efuse_program(block) + return self.get_coding_scheme_warnings(silent=True) + + def clear_pgm_registers(self): + self.wait_efuse_idle() + for r in range( + self.REGS.EFUSE_PGM_DATA0_REG, self.REGS.EFUSE_PGM_DATA0_REG + 32, 4 + ): + self.write_reg(r, 0) + + def wait_efuse_idle(self): + deadline = time.time() + self.REGS.EFUSE_BURN_TIMEOUT + while time.time() < deadline: + cmds = self.REGS.EFUSE_PGM_CMD | self.REGS.EFUSE_READ_CMD + if self.read_reg(self.REGS.EFUSE_CMD_REG) & cmds == 0: + if self.read_reg(self.REGS.EFUSE_CMD_REG) & cmds == 0: + # Due to a hardware error, we have to read READ_CMD again + # to make sure the efuse clock is normal. + # For PGM_CMD it is not necessary. + return + raise esptool.FatalError( + "Timed out waiting for Efuse controller command to complete" + ) + + def efuse_program(self, block): + self.wait_efuse_idle() + self.write_reg(self.REGS.EFUSE_CONF_REG, self.REGS.EFUSE_WRITE_OP_CODE) + self.write_reg(self.REGS.EFUSE_CMD_REG, self.REGS.EFUSE_PGM_CMD | (block << 2)) + self.wait_efuse_idle() + self.clear_pgm_registers() + self.efuse_read() + + def efuse_read(self): + self.wait_efuse_idle() + self.write_reg(self.REGS.EFUSE_CONF_REG, self.REGS.EFUSE_READ_OP_CODE) + # need to add a delay after triggering EFUSE_READ_CMD, as ROM loader checks some + # efuse registers after each command is completed + # if ENABLE_SECURITY_DOWNLOAD or DIS_DOWNLOAD_MODE is enabled by the current cmd, then we need to try to reconnect to the chip. + try: + self.write_reg( + self.REGS.EFUSE_CMD_REG, self.REGS.EFUSE_READ_CMD, delay_after_us=1000 + ) + self.wait_efuse_idle() + except esptool.FatalError: + secure_download_mode_before = self._esp.secure_download_mode + + try: + self._esp = self.reconnect_chip(self._esp) + except esptool.FatalError: + print("Can not re-connect to the chip") + if not self["DIS_DOWNLOAD_MODE"].get() and self[ + "DIS_DOWNLOAD_MODE" + ].get(from_read=False): + print( + "This is the correct behavior as we are actually burning " + "DIS_DOWNLOAD_MODE which disables the connection to the chip" + ) + print("DIS_DOWNLOAD_MODE is enabled") + print("Successful") + sys.exit(0) # finish without errors + raise + + print("Established a connection with the chip") + if self._esp.secure_download_mode and not secure_download_mode_before: + print("Secure download mode is enabled") + if not self["ENABLE_SECURITY_DOWNLOAD"].get() and self[ + "ENABLE_SECURITY_DOWNLOAD" + ].get(from_read=False): + print( + "espefuse tool can not continue to work in Secure download mode" + ) + print("ENABLE_SECURITY_DOWNLOAD is enabled") + print("Successful") + sys.exit(0) # finish without errors + raise + + def set_efuse_timing(self): + """Set timing registers for burning efuses""" + # Configure clock + apb_freq = self.get_crystal_freq() + if apb_freq != 40: + raise esptool.FatalError( + "The eFuse supports only xtal=40M (xtal was %d)" % apb_freq + ) + # keep default timing settings + + def get_coding_scheme_warnings(self, silent=False): + """Check if the coding scheme has detected any errors.""" + old_addr_reg = 0 + reg_value = 0 + ret_fail = False + for block in self.blocks: + if block.id == 0: + words = [ + self.read_reg(self.REGS.EFUSE_RD_REPEAT_ERR0_REG + offs * 4) + for offs in range(5) + ] + block.err_bitarray.pos = 0 + for word in reversed(words): + block.err_bitarray.overwrite(BitArray("uint:32=%d" % word)) + block.num_errors = block.err_bitarray.count(True) + block.fail = block.num_errors != 0 + else: + addr_reg, err_num_mask, err_num_offs, fail_bit = self.REGS.BLOCK_ERRORS[ + block.id + ] + if err_num_mask is None or err_num_offs is None or fail_bit is None: + continue + if addr_reg != old_addr_reg: + old_addr_reg = addr_reg + reg_value = self.read_reg(addr_reg) + block.fail = reg_value & (1 << fail_bit) != 0 + block.num_errors = (reg_value >> err_num_offs) & err_num_mask + ret_fail |= block.fail + if not silent and (block.fail or block.num_errors): + print( + "Error(s) in BLOCK%d [ERRORS:%d FAIL:%d]" + % (block.id, block.num_errors, block.fail) + ) + if (self.debug or ret_fail) and not silent: + self.print_status_regs() + return ret_fail + + def summary(self): + # TODO add support set_flash_voltage - "Flash voltage (VDD_SPI)" + return "" + + +class EfuseField(base_fields.EfuseFieldBase): + @staticmethod + def convert(parent, efuse): + return { + "mac": EfuseMacField, + "keypurpose": EfuseKeyPurposeField, + "t_sensor": EfuseTempSensor, + "adc_tp": EfuseAdcPointCalibration, + }.get(efuse.class_type, EfuseField)(parent, efuse) + + +class EfuseTempSensor(EfuseField): + def get(self, from_read=True): + value = self.get_bitstring(from_read) + sig = -1 if value[0] else 1 + return sig * value[1:].uint * 0.1 + + +class EfuseAdcPointCalibration(EfuseField): + def get(self, from_read=True): + STEP_SIZE = 4 + value = self.get_bitstring(from_read) + sig = -1 if value[0] else 1 + return sig * value[1:].uint * STEP_SIZE + + +class EfuseMacField(EfuseField): + def check_format(self, new_value_str): + if new_value_str is None: + raise esptool.FatalError( + "Required MAC Address in AA:CD:EF:01:02:03 format!" + ) + num_bytes = 8 if self.name == "MAC_EUI64" else 6 + if new_value_str.count(":") != num_bytes - 1: + raise esptool.FatalError( + f"MAC Address needs to be a {num_bytes}-byte hexadecimal format " + "separated by colons (:)!" + ) + hexad = new_value_str.replace(":", "").split(" ", 1)[0] + hexad = hexad.split(" ", 1)[0] if self.is_field_calculated() else hexad + if len(hexad) != num_bytes * 2: + raise esptool.FatalError( + f"MAC Address needs to be a {num_bytes}-byte hexadecimal number " + f"({num_bytes * 2} hexadecimal characters)!" + ) + # order of bytearray = b'\xaa\xcd\xef\x01\x02\x03', + bindata = binascii.unhexlify(hexad) + + if not self.is_field_calculated(): + # unicast address check according to + # https://tools.ietf.org/html/rfc7042#section-2.1 + if esptool.util.byte(bindata, 0) & 0x01: + raise esptool.FatalError("Custom MAC must be a unicast MAC!") + return bindata + + def check(self): + errs, fail = self.parent.get_block_errors(self.block) + if errs != 0 or fail: + output = "Block%d has ERRORS:%d FAIL:%d" % (self.block, errs, fail) + else: + output = "OK" + return "(" + output + ")" + + def get(self, from_read=True): + if self.name == "CUSTOM_MAC": + mac = self.get_raw(from_read)[::-1] + elif self.name == "MAC": + mac = self.get_raw(from_read) + elif self.name == "MAC_EUI64": + mac = self.parent["MAC"].get_bitstring(from_read).copy() + mac_ext = self.parent["MAC_EXT"].get_bitstring(from_read) + mac.insert(mac_ext, 24) + mac = mac.bytes + else: + mac = self.get_raw(from_read) + return "%s %s" % (util.hexify(mac, ":"), self.check()) + + def save(self, new_value): + def print_field(e, new_value): + print( + " - '{}' ({}) {} -> {}".format( + e.name, e.description, e.get_bitstring(), new_value + ) + ) + + if self.name == "CUSTOM_MAC": + bitarray_mac = self.convert_to_bitstring(new_value) + print_field(self, bitarray_mac) + super(EfuseMacField, self).save(new_value) + else: + # Writing the BLOCK1 (MAC_SPI_8M_0) default MAC is not possible, + # as it's written in the factory. + raise esptool.FatalError(f"Burning {self.name} is not supported") + + +# fmt: off +class EfuseKeyPurposeField(EfuseField): + KEY_PURPOSES = [ + ("USER", 0, None, None, "no_need_rd_protect"), # User purposes (software-only use) + ("ECDSA_KEY", 1, None, "Reverse", "need_rd_protect"), # ECDSA key + ("XTS_AES_256_KEY_1", 2, None, "Reverse", "need_rd_protect"), # XTS_AES_256_KEY_1 (flash/PSRAM encryption) + ("XTS_AES_256_KEY_2", 3, None, "Reverse", "need_rd_protect"), # XTS_AES_256_KEY_2 (flash/PSRAM encryption) + ("XTS_AES_128_KEY", 4, None, "Reverse", "need_rd_protect"), # XTS_AES_128_KEY (flash/PSRAM encryption) + ("HMAC_DOWN_ALL", 5, None, None, "need_rd_protect"), # HMAC Downstream mode + ("HMAC_DOWN_JTAG", 6, None, None, "need_rd_protect"), # JTAG soft enable key (uses HMAC Downstream mode) + ("HMAC_DOWN_DIGITAL_SIGNATURE", 7, None, None, "need_rd_protect"), # Digital Signature peripheral key (uses HMAC Downstream mode) + ("HMAC_UP", 8, None, None, "need_rd_protect"), # HMAC Upstream mode + ("SECURE_BOOT_DIGEST0", 9, "DIGEST", None, "no_need_rd_protect"), # SECURE_BOOT_DIGEST0 (Secure Boot key digest) + ("SECURE_BOOT_DIGEST1", 10, "DIGEST", None, "no_need_rd_protect"), # SECURE_BOOT_DIGEST1 (Secure Boot key digest) + ("SECURE_BOOT_DIGEST2", 11, "DIGEST", None, "no_need_rd_protect"), # SECURE_BOOT_DIGEST2 (Secure Boot key digest) + ("KM_INIT_KEY", 12, None, None, "need_rd_protect"), # init key that is used for the generation of AES/ECDSA key + ("XTS_AES_256_KEY", -1, "VIRTUAL", None, "no_need_rd_protect"), # Virtual purpose splits to XTS_AES_256_KEY_1 and XTS_AES_256_KEY_2 + ] +# fmt: on + KEY_PURPOSES_NAME = [name[0] for name in KEY_PURPOSES] + DIGEST_KEY_PURPOSES = [name[0] for name in KEY_PURPOSES if name[2] == "DIGEST"] + + def check_format(self, new_value_str): + # str convert to int: "XTS_AES_128_KEY" - > str(4) + # if int: 4 -> str(4) + raw_val = new_value_str + for purpose_name in self.KEY_PURPOSES: + if purpose_name[0] == new_value_str: + raw_val = str(purpose_name[1]) + break + if raw_val.isdigit(): + if int(raw_val) not in [p[1] for p in self.KEY_PURPOSES if p[1] > 0]: + raise esptool.FatalError("'%s' can not be set (value out of range)" % raw_val) + else: + raise esptool.FatalError("'%s' unknown name" % raw_val) + return raw_val + + def need_reverse(self, new_key_purpose): + for key in self.KEY_PURPOSES: + if key[0] == new_key_purpose: + return key[3] == "Reverse" + + def need_rd_protect(self, new_key_purpose): + for key in self.KEY_PURPOSES: + if key[0] == new_key_purpose: + return key[4] == "need_rd_protect" + + def get(self, from_read=True): + for p in self.KEY_PURPOSES: + if p[1] == self.get_raw(from_read): + return p[0] + return "FORBIDDEN_STATE" + + def get_name(self, raw_val): + for key in self.KEY_PURPOSES: + if key[1] == raw_val: + return key[0] + + def save(self, new_value): + raw_val = int(self.check_format(str(new_value))) + return super(EfuseKeyPurposeField, self).save(raw_val) diff --git a/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32p4/mem_definition.py b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32p4/mem_definition.py new file mode 100644 index 0000000..1d56f8d --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32p4/mem_definition.py @@ -0,0 +1,169 @@ +# This file describes eFuses fields and registers for ESP32-P4 chip +# +# SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD +# +# SPDX-License-Identifier: GPL-2.0-or-later + +import os + +import yaml + +from ..mem_definition_base import ( + EfuseBlocksBase, + EfuseFieldsBase, + EfuseRegistersBase, + Field, +) + + +class EfuseDefineRegisters(EfuseRegistersBase): + EFUSE_MEM_SIZE = 0x01FC + 4 + + # EFUSE registers & command/conf values + DR_REG_EFUSE_BASE = 0x5012D000 + EFUSE_PGM_DATA0_REG = DR_REG_EFUSE_BASE + EFUSE_CHECK_VALUE0_REG = DR_REG_EFUSE_BASE + 0x020 + EFUSE_CLK_REG = DR_REG_EFUSE_BASE + 0x1C8 + EFUSE_CONF_REG = DR_REG_EFUSE_BASE + 0x1CC + EFUSE_STATUS_REG = DR_REG_EFUSE_BASE + 0x1D0 + EFUSE_CMD_REG = DR_REG_EFUSE_BASE + 0x1D4 + EFUSE_RD_RS_ERR0_REG = DR_REG_EFUSE_BASE + 0x1C0 + EFUSE_RD_RS_ERR1_REG = DR_REG_EFUSE_BASE + 0x1C4 + EFUSE_RD_REPEAT_ERR0_REG = DR_REG_EFUSE_BASE + 0x17C + EFUSE_RD_REPEAT_ERR1_REG = DR_REG_EFUSE_BASE + 0x180 + EFUSE_RD_REPEAT_ERR2_REG = DR_REG_EFUSE_BASE + 0x184 + EFUSE_RD_REPEAT_ERR3_REG = DR_REG_EFUSE_BASE + 0x188 + EFUSE_RD_REPEAT_ERR4_REG = DR_REG_EFUSE_BASE + 0x18C + EFUSE_DAC_CONF_REG = DR_REG_EFUSE_BASE + 0x1E8 + EFUSE_RD_TIM_CONF_REG = DR_REG_EFUSE_BASE + 0x1EC + EFUSE_WR_TIM_CONF1_REG = DR_REG_EFUSE_BASE + 0x1F0 + EFUSE_WR_TIM_CONF2_REG = DR_REG_EFUSE_BASE + 0x1F4 + EFUSE_DATE_REG = DR_REG_EFUSE_BASE + 0x1FC + EFUSE_WRITE_OP_CODE = 0x5A5A + EFUSE_READ_OP_CODE = 0x5AA5 + EFUSE_PGM_CMD_MASK = 0x3 + EFUSE_PGM_CMD = 0x2 + EFUSE_READ_CMD = 0x1 + + BLOCK_ERRORS = [ + # error_reg, err_num_mask, err_num_offs, fail_bit + (EFUSE_RD_REPEAT_ERR0_REG, None, None, None), # BLOCK0 + (EFUSE_RD_RS_ERR0_REG, 0x7, 0, 3), # MAC_SPI_8M_0 + (EFUSE_RD_RS_ERR0_REG, 0x7, 4, 7), # BLOCK_SYS_DATA + (EFUSE_RD_RS_ERR0_REG, 0x7, 8, 11), # BLOCK_USR_DATA + (EFUSE_RD_RS_ERR0_REG, 0x7, 12, 15), # BLOCK_KEY0 + (EFUSE_RD_RS_ERR0_REG, 0x7, 16, 19), # BLOCK_KEY1 + (EFUSE_RD_RS_ERR0_REG, 0x7, 20, 23), # BLOCK_KEY2 + (EFUSE_RD_RS_ERR0_REG, 0x7, 24, 27), # BLOCK_KEY3 + (EFUSE_RD_RS_ERR0_REG, 0x7, 28, 31), # BLOCK_KEY4 + (EFUSE_RD_RS_ERR1_REG, 0x7, 0, 3), # BLOCK_KEY5 + (EFUSE_RD_RS_ERR1_REG, 0x7, 4, 7), # BLOCK_SYS_DATA2 + ] + + # EFUSE_WR_TIM_CONF2_REG + EFUSE_PWR_OFF_NUM_S = 0 + EFUSE_PWR_OFF_NUM_M = 0xFFFF << EFUSE_PWR_OFF_NUM_S + + # EFUSE_WR_TIM_CONF1_REG + EFUSE_PWR_ON_NUM_S = 8 + EFUSE_PWR_ON_NUM_M = 0x0000FFFF << EFUSE_PWR_ON_NUM_S + + # EFUSE_DAC_CONF_REG + EFUSE_DAC_CLK_DIV_S = 0 + EFUSE_DAC_CLK_DIV_M = 0xFF << EFUSE_DAC_CLK_DIV_S + + # EFUSE_DAC_CONF_REG + EFUSE_DAC_NUM_S = 9 + EFUSE_DAC_NUM_M = 0xFF << EFUSE_DAC_NUM_S + + +class EfuseDefineBlocks(EfuseBlocksBase): + __base_rd_regs = EfuseDefineRegisters.DR_REG_EFUSE_BASE + __base_wr_regs = EfuseDefineRegisters.EFUSE_PGM_DATA0_REG + # List of efuse blocks + # fmt: off + BLOCKS = [ + # Name, Alias, Index, Read address, Write address, Write protect bit, Read protect bit, Len, key_purpose + ("BLOCK0", [], 0, __base_rd_regs + 0x02C, __base_wr_regs, None, None, 6, None), + ("MAC_SPI_8M_0", ["BLOCK1"], 1, __base_rd_regs + 0x044, __base_wr_regs, 20, None, 6, None), + ("BLOCK_SYS_DATA", ["BLOCK2"], 2, __base_rd_regs + 0x05C, __base_wr_regs, 21, None, 8, None), + ("BLOCK_USR_DATA", ["BLOCK3"], 3, __base_rd_regs + 0x07C, __base_wr_regs, 22, None, 8, None), + ("BLOCK_KEY0", ["BLOCK4"], 4, __base_rd_regs + 0x09C, __base_wr_regs, 23, 0, 8, "KEY_PURPOSE_0"), + ("BLOCK_KEY1", ["BLOCK5"], 5, __base_rd_regs + 0x0BC, __base_wr_regs, 24, 1, 8, "KEY_PURPOSE_1"), + ("BLOCK_KEY2", ["BLOCK6"], 6, __base_rd_regs + 0x0DC, __base_wr_regs, 25, 2, 8, "KEY_PURPOSE_2"), + ("BLOCK_KEY3", ["BLOCK7"], 7, __base_rd_regs + 0x0FC, __base_wr_regs, 26, 3, 8, "KEY_PURPOSE_3"), + ("BLOCK_KEY4", ["BLOCK8"], 8, __base_rd_regs + 0x11C, __base_wr_regs, 27, 4, 8, "KEY_PURPOSE_4"), + ("BLOCK_KEY5", ["BLOCK9"], 9, __base_rd_regs + 0x13C, __base_wr_regs, 28, 5, 8, "KEY_PURPOSE_5"), + ("BLOCK_SYS_DATA2", ["BLOCK10"], 10, __base_rd_regs + 0x15C, __base_wr_regs, 29, 6, 8, None), + ] + # fmt: on + + def get_burn_block_data_names(self): + list_of_names = [] + for block in self.BLOCKS: + blk = self.get(block) + if blk.name: + list_of_names.append(blk.name) + if blk.alias: + for alias in blk.alias: + list_of_names.append(alias) + return list_of_names + + +class EfuseDefineFields(EfuseFieldsBase): + def __init__(self) -> None: + # List of efuse fields from TRM the chapter eFuse Controller. + self.EFUSES = [] + + self.KEYBLOCKS = [] + + # if BLK_VERSION_MINOR is 1, these efuse fields are in BLOCK2 + self.BLOCK2_CALIBRATION_EFUSES = [] + + self.CALC = [] + + dir_name = os.path.dirname(os.path.abspath(__file__)) + dir_name, file_name = os.path.split(dir_name) + file_name = file_name + ".yaml" + dir_name, _ = os.path.split(dir_name) + efuse_file = os.path.join(dir_name, "efuse_defs", file_name) + with open(f"{efuse_file}", "r") as r_file: + e_desc = yaml.safe_load(r_file) + super().__init__(e_desc) + + for i, efuse in enumerate(self.ALL_EFUSES): + if efuse.name in [ + "BLOCK_USR_DATA", + "BLOCK_KEY0", + "BLOCK_KEY1", + "BLOCK_KEY2", + "BLOCK_KEY3", + "BLOCK_KEY4", + "BLOCK_KEY5", + "BLOCK_SYS_DATA2", + ]: + if efuse.name == "BLOCK_USR_DATA": + efuse.bit_len = 256 + efuse.type = "bytes:32" + self.KEYBLOCKS.append(efuse) + self.ALL_EFUSES[i] = None + + elif efuse.category == "calibration": + self.BLOCK2_CALIBRATION_EFUSES.append(efuse) + self.ALL_EFUSES[i] = None + + f = Field() + f.name = "MAC_EUI64" + f.block = 1 + f.bit_len = 64 + f.type = f"bytes:{f.bit_len // 8}" + f.category = "MAC" + f.class_type = "mac" + f.description = "calc MAC_EUI64 = MAC[0]:MAC[1]:MAC[2]:MAC_EXT[0]:MAC_EXT[1]:MAC[3]:MAC[4]:MAC[5]" + self.CALC.append(f) + + for efuse in self.ALL_EFUSES: + if efuse is not None: + self.EFUSES.append(efuse) + + self.ALL_EFUSES = [] diff --git a/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32p4/operations.py b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32p4/operations.py new file mode 100644 index 0000000..b24a735 --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32p4/operations.py @@ -0,0 +1,458 @@ +# This file includes the operations with eFuses for ESP32-P4 chip +# +# SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD +# +# SPDX-License-Identifier: GPL-2.0-or-later + +import argparse +import io +import os # noqa: F401. It is used in IDF scripts +import traceback + +import espsecure + +import esptool + +from . import fields +from .. import util +from ..base_operations import ( + add_common_commands, + add_force_write_always, + add_show_sensitive_info_option, + burn_bit, + burn_block_data, + burn_efuse, + check_error, + dump, + read_protect_efuse, + summary, + write_protect_efuse, +) + + +def protect_options(p): + p.add_argument( + "--no-write-protect", + help="Disable write-protecting of the key. The key remains writable. " + "(The keys use the RS coding scheme that does not support " + "post-write data changes. Forced write can damage RS encoding bits.) " + "The write-protecting of keypurposes does not depend on the option, " + "it will be set anyway.", + action="store_true", + ) + p.add_argument( + "--no-read-protect", + help="Disable read-protecting of the key. The key remains readable software." + "The key with keypurpose[USER, RESERVED and *_DIGEST] " + "will remain readable anyway. For the rest keypurposes the read-protection " + "will be defined the option (Read-protect by default).", + action="store_true", + ) + + +def add_commands(subparsers, efuses): + add_common_commands(subparsers, efuses) + burn_key = subparsers.add_parser( + "burn_key", help="Burn the key block with the specified name" + ) + protect_options(burn_key) + add_force_write_always(burn_key) + add_show_sensitive_info_option(burn_key) + burn_key.add_argument( + "block", + help="Key block to burn", + action="append", + choices=efuses.BLOCKS_FOR_KEYS, + ) + burn_key.add_argument( + "keyfile", + help="File containing 256 bits of binary key data. For the ECDSA_KEY purpose use PEM file.", + action="append", + type=argparse.FileType("rb"), + ) + burn_key.add_argument( + "keypurpose", + help="Purpose to set.", + action="append", + choices=fields.EfuseKeyPurposeField.KEY_PURPOSES_NAME, + ) + for _ in efuses.BLOCKS_FOR_KEYS: + burn_key.add_argument( + "block", + help="Key block to burn", + nargs="?", + action="append", + metavar="BLOCK", + choices=efuses.BLOCKS_FOR_KEYS, + ) + burn_key.add_argument( + "keyfile", + help="File containing 256 bits of binary key data. For the ECDSA_KEY purpose use PEM file.", + nargs="?", + action="append", + metavar="KEYFILE", + type=argparse.FileType("rb"), + ) + burn_key.add_argument( + "keypurpose", + help="Purpose to set.", + nargs="?", + action="append", + metavar="KEYPURPOSE", + choices=fields.EfuseKeyPurposeField.KEY_PURPOSES_NAME, + ) + + burn_key_digest = subparsers.add_parser( + "burn_key_digest", + help="Parse a RSA public key and burn the digest to key efuse block", + ) + protect_options(burn_key_digest) + add_force_write_always(burn_key_digest) + add_show_sensitive_info_option(burn_key_digest) + burn_key_digest.add_argument( + "block", + help="Key block to burn", + action="append", + choices=efuses.BLOCKS_FOR_KEYS, + ) + burn_key_digest.add_argument( + "keyfile", + help="Key file to digest (PEM format)", + action="append", + type=argparse.FileType("rb"), + ) + burn_key_digest.add_argument( + "keypurpose", + help="Purpose to set.", + action="append", + choices=fields.EfuseKeyPurposeField.DIGEST_KEY_PURPOSES, + ) + for _ in efuses.BLOCKS_FOR_KEYS: + burn_key_digest.add_argument( + "block", + help="Key block to burn", + nargs="?", + action="append", + metavar="BLOCK", + choices=efuses.BLOCKS_FOR_KEYS, + ) + burn_key_digest.add_argument( + "keyfile", + help="Key file to digest (PEM format)", + nargs="?", + action="append", + metavar="KEYFILE", + type=argparse.FileType("rb"), + ) + burn_key_digest.add_argument( + "keypurpose", + help="Purpose to set.", + nargs="?", + action="append", + metavar="KEYPURPOSE", + choices=fields.EfuseKeyPurposeField.DIGEST_KEY_PURPOSES, + ) + + p = subparsers.add_parser( + "set_flash_voltage", + help="Permanently set the internal flash voltage regulator " + "to either 1.8V, 3.3V or OFF. " + "This means GPIO45 can be high or low at reset without " + "changing the flash voltage.", + ) + p.add_argument("voltage", help="Voltage selection", choices=["1.8V", "3.3V", "OFF"]) + + p = subparsers.add_parser( + "burn_custom_mac", help="Burn a 48-bit Custom MAC Address to EFUSE BLOCK3." + ) + p.add_argument( + "mac", + help="Custom MAC Address to burn given in hexadecimal format with bytes " + "separated by colons (e.g. AA:CD:EF:01:02:03).", + type=fields.base_fields.CheckArgValue(efuses, "CUSTOM_MAC"), + ) + add_force_write_always(p) + + p = subparsers.add_parser("get_custom_mac", help="Prints the Custom MAC Address.") + + +def burn_custom_mac(esp, efuses, args): + print("Not supported yet") + + +def get_custom_mac(esp, efuses, args): + print("Not supported yet") + + +def set_flash_voltage(esp, efuses, args): + raise esptool.FatalError("set_flash_voltage is not supported!") + + +def adc_info(esp, efuses, args): + print("not supported yet") + + +def key_block_is_unused(block, key_purpose_block): + if not block.is_readable() or not block.is_writeable(): + return False + + if key_purpose_block.get() != "USER" or not key_purpose_block.is_writeable(): + return False + + if not block.get_bitstring().all(False): + return False + + return True + + +def get_next_key_block(efuses, current_key_block, block_name_list): + key_blocks = [b for b in efuses.blocks if b.key_purpose_name] + start = key_blocks.index(current_key_block) + + # Sort key blocks so that we pick the next free block (and loop around if necessary) + key_blocks = key_blocks[start:] + key_blocks[0:start] + + # Exclude any other blocks that will be be burned + key_blocks = [b for b in key_blocks if b.name not in block_name_list] + + for block in key_blocks: + key_purpose_block = efuses[block.key_purpose_name] + if key_block_is_unused(block, key_purpose_block): + return block + + return None + + +def split_512_bit_key(efuses, block_name_list, datafile_list, keypurpose_list): + i = keypurpose_list.index("XTS_AES_256_KEY") + block_name = block_name_list[i] + + block_num = efuses.get_index_block_by_name(block_name) + block = efuses.blocks[block_num] + + data = datafile_list[i].read() + if len(data) != 64: + raise esptool.FatalError( + "Incorrect key file size %d, XTS_AES_256_KEY should be 64 bytes" % len(data) + ) + + key_block_2 = get_next_key_block(efuses, block, block_name_list) + if not key_block_2: + raise esptool.FatalError("XTS_AES_256_KEY requires two free keyblocks") + + keypurpose_list.append("XTS_AES_256_KEY_1") + datafile_list.append(io.BytesIO(data[:32])) + block_name_list.append(block_name) + + keypurpose_list.append("XTS_AES_256_KEY_2") + datafile_list.append(io.BytesIO(data[32:])) + block_name_list.append(key_block_2.name) + + keypurpose_list.pop(i) + datafile_list.pop(i) + block_name_list.pop(i) + + +def burn_key(esp, efuses, args, digest=None): + if digest is None: + datafile_list = args.keyfile[ + 0 : len([name for name in args.keyfile if name is not None]) : + ] + else: + datafile_list = digest[0 : len([name for name in digest if name is not None]) :] + efuses.force_write_always = args.force_write_always + block_name_list = args.block[ + 0 : len([name for name in args.block if name is not None]) : + ] + keypurpose_list = args.keypurpose[ + 0 : len([name for name in args.keypurpose if name is not None]) : + ] + + if "XTS_AES_256_KEY" in keypurpose_list: + # XTS_AES_256_KEY is not an actual HW key purpose, needs to be split into + # XTS_AES_256_KEY_1 and XTS_AES_256_KEY_2 + split_512_bit_key(efuses, block_name_list, datafile_list, keypurpose_list) + + util.check_duplicate_name_in_list(block_name_list) + if len(block_name_list) != len(datafile_list) or len(block_name_list) != len( + keypurpose_list + ): + raise esptool.FatalError( + "The number of blocks (%d), datafile (%d) and keypurpose (%d) " + "should be the same." + % (len(block_name_list), len(datafile_list), len(keypurpose_list)) + ) + + print("Burn keys to blocks:") + for block_name, datafile, keypurpose in zip( + block_name_list, datafile_list, keypurpose_list + ): + efuse = None + for block in efuses.blocks: + if block_name == block.name or block_name in block.alias: + efuse = efuses[block.name] + if efuse is None: + raise esptool.FatalError("Unknown block name - %s" % (block_name)) + num_bytes = efuse.bit_len // 8 + + block_num = efuses.get_index_block_by_name(block_name) + block = efuses.blocks[block_num] + + if digest is None: + if keypurpose == "ECDSA_KEY": + sk = espsecure.load_ecdsa_signing_key(datafile) + data = sk.to_string() + if len(data) == 24: + # the private key is 24 bytes long for NIST192p, add 8 bytes of padding + data = b"\x00" * 8 + data + else: + data = datafile.read() + else: + data = datafile + + print(" - %s" % (efuse.name), end=" ") + revers_msg = None + if efuses[block.key_purpose_name].need_reverse(keypurpose): + revers_msg = f"\tReversing byte order for {keypurpose} hardware peripheral" + data = data[::-1] + print( + "-> [{}]".format( + util.hexify(data, " ") + if args.show_sensitive_info + else " ".join(["??"] * len(data)) + ) + ) + if revers_msg: + print(revers_msg) + if len(data) != num_bytes: + raise esptool.FatalError( + "Incorrect key file size %d. Key file must be %d bytes (%d bits) " + "of raw binary key data." % (len(data), num_bytes, num_bytes * 8) + ) + + if efuses[block.key_purpose_name].need_rd_protect(keypurpose): + read_protect = False if args.no_read_protect else True + else: + read_protect = False + write_protect = not args.no_write_protect + + # using efuse instead of a block gives the advantage of checking it as the whole field. + efuse.save(data) + + disable_wr_protect_key_purpose = False + if efuses[block.key_purpose_name].get() != keypurpose: + if efuses[block.key_purpose_name].is_writeable(): + print( + "\t'%s': '%s' -> '%s'." + % ( + block.key_purpose_name, + efuses[block.key_purpose_name].get(), + keypurpose, + ) + ) + efuses[block.key_purpose_name].save(keypurpose) + disable_wr_protect_key_purpose = True + else: + raise esptool.FatalError( + "It is not possible to change '%s' to '%s' " + "because write protection bit is set." + % (block.key_purpose_name, keypurpose) + ) + else: + print("\t'%s' is already '%s'." % (block.key_purpose_name, keypurpose)) + if efuses[block.key_purpose_name].is_writeable(): + disable_wr_protect_key_purpose = True + + if disable_wr_protect_key_purpose: + print("\tDisabling write to '%s'." % block.key_purpose_name) + efuses[block.key_purpose_name].disable_write() + + if read_protect: + print("\tDisabling read to key block") + efuse.disable_read() + + if write_protect: + print("\tDisabling write to key block") + efuse.disable_write() + print("") + + if not write_protect: + print("Keys will remain writeable (due to --no-write-protect)") + if args.no_read_protect: + print("Keys will remain readable (due to --no-read-protect)") + + if not efuses.burn_all(check_batch_mode=True): + return + print("Successful") + + +def burn_key_digest(esp, efuses, args): + digest_list = [] + datafile_list = args.keyfile[ + 0 : len([name for name in args.keyfile if name is not None]) : + ] + block_list = args.block[ + 0 : len([block for block in args.block if block is not None]) : + ] + for block_name, datafile in zip(block_list, datafile_list): + efuse = None + for block in efuses.blocks: + if block_name == block.name or block_name in block.alias: + efuse = efuses[block.name] + if efuse is None: + raise esptool.FatalError("Unknown block name - %s" % (block_name)) + num_bytes = efuse.bit_len // 8 + digest = espsecure._digest_sbv2_public_key(datafile) + if len(digest) != num_bytes: + raise esptool.FatalError( + "Incorrect digest size %d. Digest must be %d bytes (%d bits) " + "of raw binary key data." % (len(digest), num_bytes, num_bytes * 8) + ) + digest_list.append(digest) + burn_key(esp, efuses, args, digest=digest_list) + + +def espefuse(esp, efuses, args, command): + parser = argparse.ArgumentParser() + subparsers = parser.add_subparsers(dest="operation") + add_commands(subparsers, efuses) + try: + cmd_line_args = parser.parse_args(command.split()) + except SystemExit: + traceback.print_stack() + raise esptool.FatalError('"{}" - incorrect command'.format(command)) + if cmd_line_args.operation == "execute_scripts": + configfiles = cmd_line_args.configfiles + index = cmd_line_args.index + # copy arguments from args to cmd_line_args + vars(cmd_line_args).update(vars(args)) + if cmd_line_args.operation == "execute_scripts": + cmd_line_args.configfiles = configfiles + cmd_line_args.index = index + if cmd_line_args.operation is None: + parser.print_help() + parser.exit(1) + operation_func = globals()[cmd_line_args.operation] + # each 'operation' is a module-level function of the same name + operation_func(esp, efuses, cmd_line_args) + + +def execute_scripts(esp, efuses, args): + efuses.batch_mode_cnt += 1 + del args.operation + scripts = args.scripts + del args.scripts + + for file in scripts: + with open(file.name, "r") as file: + exec(compile(file.read(), file.name, "exec")) + + if args.debug: + for block in efuses.blocks: + data = block.get_bitstring(from_read=False) + block.print_block(data, "regs_for_burn", args.debug) + + efuses.batch_mode_cnt -= 1 + if not efuses.burn_all(check_batch_mode=True): + return + print("Successful") diff --git a/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32s2/__init__.py b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32s2/__init__.py new file mode 100644 index 0000000..a3b55a8 --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32s2/__init__.py @@ -0,0 +1,3 @@ +from . import operations +from .emulate_efuse_controller import EmulateEfuseController +from .fields import EspEfuses diff --git a/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32s2/__pycache__/__init__.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32s2/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..ab31bf1 Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32s2/__pycache__/__init__.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32s2/__pycache__/emulate_efuse_controller.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32s2/__pycache__/emulate_efuse_controller.cpython-312.pyc new file mode 100644 index 0000000..144c90d Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32s2/__pycache__/emulate_efuse_controller.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32s2/__pycache__/fields.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32s2/__pycache__/fields.cpython-312.pyc new file mode 100644 index 0000000..4c6193c Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32s2/__pycache__/fields.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32s2/__pycache__/mem_definition.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32s2/__pycache__/mem_definition.cpython-312.pyc new file mode 100644 index 0000000..8c6dd3e Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32s2/__pycache__/mem_definition.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32s2/__pycache__/operations.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32s2/__pycache__/operations.cpython-312.pyc new file mode 100644 index 0000000..8248c51 Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32s2/__pycache__/operations.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32s2/emulate_efuse_controller.py b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32s2/emulate_efuse_controller.py new file mode 100644 index 0000000..497c91a --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32s2/emulate_efuse_controller.py @@ -0,0 +1,92 @@ +# This file describes eFuses controller for ESP32-S2 chip +# +# SPDX-FileCopyrightText: 2020-2022 Espressif Systems (Shanghai) CO LTD +# +# SPDX-License-Identifier: GPL-2.0-or-later + +import reedsolo + +from .mem_definition import EfuseDefineBlocks, EfuseDefineFields, EfuseDefineRegisters +from ..emulate_efuse_controller_base import EmulateEfuseControllerBase, FatalError + + +class EmulateEfuseController(EmulateEfuseControllerBase): + """The class for virtual efuse operation. Using for HOST_TEST.""" + + CHIP_NAME = "ESP32-S2" + mem = None + debug = False + + def __init__(self, efuse_file=None, debug=False): + self.Blocks = EfuseDefineBlocks + self.Fields = EfuseDefineFields() + self.REGS = EfuseDefineRegisters + super(EmulateEfuseController, self).__init__(efuse_file, debug) + self.write_reg(self.REGS.EFUSE_CMD_REG, 0) + + """ esptool method start >>""" + + def get_major_chip_version(self): + return 1 + + def get_minor_chip_version(self): + return 0 + + def get_crystal_freq(self): + return 40 # MHz (common for all chips) + + def get_security_info(self): + return { + "flags": 0, + "flash_crypt_cnt": 0, + "key_purposes": 0, + "chip_id": None, + "api_version": None, + } + + """ << esptool method end """ + + def handle_writing_event(self, addr, value): + if addr == self.REGS.EFUSE_CMD_REG: + if value & self.REGS.EFUSE_PGM_CMD: + self.copy_blocks_wr_regs_to_rd_regs(updated_block=(value >> 2) & 0xF) + self.clean_blocks_wr_regs() + self.check_rd_protection_area() + self.write_reg(addr, 0) + self.write_reg(self.REGS.EFUSE_CMD_REG, 0) + elif value == self.REGS.EFUSE_READ_CMD: + self.write_reg(addr, 0) + self.write_reg(self.REGS.EFUSE_CMD_REG, 0) + self.save_to_file() + + def get_bitlen_of_block(self, blk, wr=False): + if blk.id == 0: + if wr: + return 32 * 8 + else: + return 32 * blk.len + else: + if wr: + rs_coding = 32 * 3 + return 32 * 8 + rs_coding + else: + return 32 * blk.len + + def handle_coding_scheme(self, blk, data): + if blk.id != 0: + # CODING_SCHEME RS applied only for all blocks except BLK0. + coded_bytes = 12 + data.pos = coded_bytes * 8 + plain_data = data.readlist("32*uint:8")[::-1] + # takes 32 bytes + # apply RS encoding + rs = reedsolo.RSCodec(coded_bytes) + # 32 byte of data + 12 bytes RS + calc_encoded_data = list(rs.encode([x for x in plain_data])) + data.pos = 0 + if calc_encoded_data != data.readlist("44*uint:8")[::-1]: + raise FatalError("Error in coding scheme data") + data = data[coded_bytes * 8 :] + if blk.len < 8: + data = data[(8 - blk.len) * 32 :] + return data diff --git a/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32s2/fields.py b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32s2/fields.py new file mode 100644 index 0000000..15d9275 --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32s2/fields.py @@ -0,0 +1,491 @@ +# This file describes eFuses for ESP32S2 chip +# +# SPDX-FileCopyrightText: 2020-2022 Espressif Systems (Shanghai) CO LTD +# +# SPDX-License-Identifier: GPL-2.0-or-later + +import binascii +import struct +import sys +import time + +from bitstring import BitArray + +import esptool + +import reedsolo + +from .mem_definition import EfuseDefineBlocks, EfuseDefineFields, EfuseDefineRegisters +from .. import base_fields +from .. import util + + +class EfuseBlock(base_fields.EfuseBlockBase): + def len_of_burn_unit(self): + # The writing register window is 8 registers for any blocks. + # len in bytes + return 8 * 4 + + def __init__(self, parent, param, skip_read=False): + parent.read_coding_scheme() + super(EfuseBlock, self).__init__(parent, param, skip_read=skip_read) + + def apply_coding_scheme(self): + data = self.get_raw(from_read=False)[::-1] + if len(data) < self.len_of_burn_unit(): + add_empty_bytes = self.len_of_burn_unit() - len(data) + data = data + (b"\x00" * add_empty_bytes) + if self.get_coding_scheme() == self.parent.REGS.CODING_SCHEME_RS: + # takes 32 bytes + # apply RS encoding + rs = reedsolo.RSCodec(12) + # 32 byte of data + 12 bytes RS + encoded_data = rs.encode([x for x in data]) + words = struct.unpack("<" + "I" * 11, encoded_data) + # returns 11 words (8 words of data + 3 words of RS coding) + else: + # takes 32 bytes + words = struct.unpack("<" + ("I" * (len(data) // 4)), data) + # returns 8 words + return words + + +class EspEfuses(base_fields.EspEfusesBase): + """ + Wrapper object to manage the efuse fields in a connected ESP bootloader + """ + + debug = False + do_not_confirm = False + + def __init__(self, esp, skip_connect=False, debug=False, do_not_confirm=False): + self.Blocks = EfuseDefineBlocks() + self.Fields = EfuseDefineFields() + self.REGS = EfuseDefineRegisters + self.BURN_BLOCK_DATA_NAMES = self.Blocks.get_burn_block_data_names() + self.BLOCKS_FOR_KEYS = self.Blocks.get_blocks_for_keys() + self._esp = esp + self.debug = debug + self.do_not_confirm = do_not_confirm + if esp.CHIP_NAME != "ESP32-S2": + raise esptool.FatalError( + "Expected the 'esp' param for ESP32-S2 chip but got for '%s'." + % (esp.CHIP_NAME) + ) + if not skip_connect: + flags = self._esp.get_security_info()["flags"] + GET_SECURITY_INFO_FLAG_SECURE_DOWNLOAD_ENABLE = 1 << 2 + if flags & GET_SECURITY_INFO_FLAG_SECURE_DOWNLOAD_ENABLE: + raise esptool.FatalError( + "Secure Download Mode is enabled. The tool can not read eFuses." + ) + self.blocks = [ + EfuseBlock(self, self.Blocks.get(block), skip_read=skip_connect) + for block in self.Blocks.BLOCKS + ] + if not skip_connect: + self.get_coding_scheme_warnings() + self.efuses = [EfuseField.convert(self, efuse) for efuse in self.Fields.EFUSES] + self.efuses += [ + EfuseField.convert(self, efuse) for efuse in self.Fields.KEYBLOCKS + ] + if skip_connect: + self.efuses += [ + EfuseField.convert(self, efuse) + for efuse in self.Fields.BLOCK2_CALIBRATION_EFUSES + ] + else: + if self["BLK_VERSION_MINOR"].get() == 1: + self.efuses += [ + EfuseField.convert(self, efuse) + for efuse in self.Fields.BLOCK2_CALIBRATION_EFUSES + ] + self.efuses += [ + EfuseField.convert(self, efuse) for efuse in self.Fields.CALC + ] + + def __getitem__(self, efuse_name): + """Return the efuse field with the given name""" + for e in self.efuses: + if efuse_name == e.name or any(x == efuse_name for x in e.alt_names): + return e + new_fields = False + for efuse in self.Fields.BLOCK2_CALIBRATION_EFUSES: + if efuse.name == efuse_name or any( + x == efuse_name for x in efuse.alt_names + ): + self.efuses += [ + EfuseField.convert(self, efuse) + for efuse in self.Fields.BLOCK2_CALIBRATION_EFUSES + ] + new_fields = True + if new_fields: + for e in self.efuses: + if efuse_name == e.name or any(x == efuse_name for x in e.alt_names): + return e + raise KeyError + + def read_coding_scheme(self): + self.coding_scheme = self.REGS.CODING_SCHEME_RS + + def print_status_regs(self): + print("") + self.blocks[0].print_block(self.blocks[0].err_bitarray, "err__regs", debug=True) + print( + "{:27} 0x{:08x}".format( + "EFUSE_RD_RS_ERR0_REG", self.read_reg(self.REGS.EFUSE_RD_RS_ERR0_REG) + ) + ) + print( + "{:27} 0x{:08x}".format( + "EFUSE_RD_RS_ERR1_REG", self.read_reg(self.REGS.EFUSE_RD_RS_ERR1_REG) + ) + ) + + def efuse_controller_setup(self): + self.set_efuse_timing() + self.clear_pgm_registers() + self.wait_efuse_idle() + + def write_efuses(self, block): + self.efuse_program(block) + return self.get_coding_scheme_warnings(silent=True) + + def clear_pgm_registers(self): + self.wait_efuse_idle() + for r in range( + self.REGS.EFUSE_PGM_DATA0_REG, self.REGS.EFUSE_PGM_DATA0_REG + 32, 4 + ): + self.write_reg(r, 0) + + def wait_efuse_idle(self): + deadline = time.time() + self.REGS.EFUSE_BURN_TIMEOUT + while time.time() < deadline: + cmds = self.REGS.EFUSE_PGM_CMD | self.REGS.EFUSE_READ_CMD + if self.read_reg(self.REGS.EFUSE_CMD_REG) & cmds == 0: + if self.read_reg(self.REGS.EFUSE_CMD_REG) & cmds == 0: + # Due to a hardware error, we have to read READ_CMD again + # to make sure the efuse clock is normal. + # For PGM_CMD it is not necessary. + return + raise esptool.FatalError( + "Timed out waiting for Efuse controller command to complete" + ) + + def efuse_program(self, block): + self.wait_efuse_idle() + self.write_reg(self.REGS.EFUSE_CONF_REG, self.REGS.EFUSE_WRITE_OP_CODE) + self.write_reg(self.REGS.EFUSE_CMD_REG, self.REGS.EFUSE_PGM_CMD | (block << 2)) + self.wait_efuse_idle() + self.clear_pgm_registers() + self.efuse_read() + + def efuse_read(self): + self.wait_efuse_idle() + self.write_reg(self.REGS.EFUSE_CONF_REG, self.REGS.EFUSE_READ_OP_CODE) + # need to add a delay after triggering EFUSE_READ_CMD, as ROM loader checks some + # efuse registers after each command is completed + # if ENABLE_SECURITY_DOWNLOAD or DIS_DOWNLOAD_MODE is enabled by the current cmd, then we need to try to reconnect to the chip. + try: + self.write_reg( + self.REGS.EFUSE_CMD_REG, self.REGS.EFUSE_READ_CMD, delay_after_us=1000 + ) + self.wait_efuse_idle() + except esptool.FatalError: + secure_download_mode_before = self._esp.secure_download_mode + + try: + self._esp = self.reconnect_chip(self._esp) + except esptool.FatalError: + print("Can not re-connect to the chip") + if not self["DIS_DOWNLOAD_MODE"].get() and self[ + "DIS_DOWNLOAD_MODE" + ].get(from_read=False): + print( + "This is the correct behavior as we are actually burning " + "DIS_DOWNLOAD_MODE which disables the connection to the chip" + ) + print("DIS_DOWNLOAD_MODE is enabled") + print("Successful") + sys.exit(0) # finish without errors + raise + + print("Established a connection with the chip") + if self._esp.secure_download_mode and not secure_download_mode_before: + print("Secure download mode is enabled") + if not self["ENABLE_SECURITY_DOWNLOAD"].get() and self[ + "ENABLE_SECURITY_DOWNLOAD" + ].get(from_read=False): + print( + "espefuse tool can not continue to work in Secure download mode" + ) + print("ENABLE_SECURITY_DOWNLOAD is enabled") + print("Successful") + sys.exit(0) # finish without errors + raise + + def set_efuse_timing(self): + """Set timing registers for burning efuses""" + # Configure clock + apb_freq = self.get_crystal_freq() + ( + EFUSE_TSUP_A, + EFUSE_TPGM, + EFUSE_THP_A, + EFUSE_TPGM_INACTIVE, + ) = self.REGS.EFUSE_PROGRAMMING_TIMING_PARAMETERS[apb_freq] + self.update_reg( + self.REGS.EFUSE_WR_TIM_CONF1_REG, self.REGS.EFUSE_TSUP_A_M, EFUSE_TSUP_A + ) + self.update_reg( + self.REGS.EFUSE_WR_TIM_CONF0_REG, self.REGS.EFUSE_TPGM_M, EFUSE_TPGM + ) + self.update_reg( + self.REGS.EFUSE_WR_TIM_CONF0_REG, self.REGS.EFUSE_THP_A_M, EFUSE_THP_A + ) + self.update_reg( + self.REGS.EFUSE_WR_TIM_CONF0_REG, + self.REGS.EFUSE_TPGM_INACTIVE_M, + EFUSE_TPGM_INACTIVE, + ) + + ( + EFUSE_DAC_CLK_DIV, + EFUSE_PWR_ON_NUM, + EFUSE_PWR_OFF_NUM, + ) = self.REGS.VDDQ_TIMING_PARAMETERS[apb_freq] + self.update_reg( + self.REGS.EFUSE_DAC_CONF_REG, + self.REGS.EFUSE_DAC_CLK_DIV_M, + EFUSE_DAC_CLK_DIV, + ) + self.update_reg( + self.REGS.EFUSE_WR_TIM_CONF1_REG, + self.REGS.EFUSE_PWR_ON_NUM_M, + EFUSE_PWR_ON_NUM, + ) + self.update_reg( + self.REGS.EFUSE_WR_TIM_CONF2_REG, + self.REGS.EFUSE_PWR_OFF_NUM_M, + EFUSE_PWR_OFF_NUM, + ) + + EFUSE_TSUR_A, EFUSE_TRD, EFUSE_THR_A = self.REGS.EFUSE_READING_PARAMETERS[ + apb_freq + ] + # self.update_reg( + # self.REGS.EFUSE_RD_TIM_CONF_REG, self.REGS.EFUSE_TSUR_A_M, EFUSE_TSUR_A + # ) + self.update_reg( + self.REGS.EFUSE_RD_TIM_CONF_REG, self.REGS.EFUSE_TRD_M, EFUSE_TRD + ) + self.update_reg( + self.REGS.EFUSE_RD_TIM_CONF_REG, self.REGS.EFUSE_THR_A_M, EFUSE_THR_A + ) + + def get_coding_scheme_warnings(self, silent=False): + """Check if the coding scheme has detected any errors.""" + old_addr_reg = 0 + reg_value = 0 + ret_fail = False + for block in self.blocks: + if block.id == 0: + words = [ + self.read_reg(self.REGS.EFUSE_RD_REPEAT_ERR0_REG + offs * 4) + for offs in range(5) + ] + block.err_bitarray.pos = 0 + for word in reversed(words): + block.err_bitarray.overwrite(BitArray("uint:32=%d" % word)) + block.num_errors = block.err_bitarray.count(True) + block.fail = block.num_errors != 0 + else: + addr_reg, err_num_mask, err_num_offs, fail_bit = self.REGS.BLOCK_ERRORS[ + block.id + ] + if err_num_mask is None or err_num_offs is None or fail_bit is None: + continue + if addr_reg != old_addr_reg: + old_addr_reg = addr_reg + reg_value = self.read_reg(addr_reg) + block.fail = reg_value & (1 << fail_bit) != 0 + block.num_errors = (reg_value >> err_num_offs) & err_num_mask + ret_fail |= block.fail + if not silent and (block.fail or block.num_errors): + print( + "Error(s) in BLOCK%d [ERRORS:%d FAIL:%d]" + % (block.id, block.num_errors, block.fail) + ) + if (self.debug or ret_fail) and not silent: + self.print_status_regs() + return ret_fail + + def summary(self): + if self["VDD_SPI_FORCE"].get() == 0: + output = "Flash voltage (VDD_SPI) determined by GPIO45 on reset " + output += "(GPIO45=High: VDD_SPI pin is powered from internal 1.8V LDO\n" + output += "GPIO45=Low or NC: VDD_SPI pin is powered directly from " + output += "VDD3P3_RTC_IO via resistor Rspi. " + output += "Typically this voltage is 3.3 V)." + elif self["VDD_SPI_XPD"].get() == 0: + output = "Flash voltage (VDD_SPI) internal regulator disabled by efuse." + elif self["VDD_SPI_TIEH"].get() == 0: + output = "Flash voltage (VDD_SPI) set to 1.8V by efuse." + else: + output = "Flash voltage (VDD_SPI) set to 3.3V by efuse." + return output + + +class EfuseField(base_fields.EfuseFieldBase): + @staticmethod + def convert(parent, efuse): + return { + "mac": EfuseMacField, + "keypurpose": EfuseKeyPurposeField, + "t_sensor": EfuseTempSensor, + "adc_tp": EfuseAdcPointCalibration, + "wafer": EfuseWafer, + }.get(efuse.class_type, EfuseField)(parent, efuse) + + +class EfuseWafer(EfuseField): + def get(self, from_read=True): + hi_bits = self.parent["WAFER_VERSION_MINOR_HI"].get(from_read) + assert self.parent["WAFER_VERSION_MINOR_HI"].bit_len == 1 + lo_bits = self.parent["WAFER_VERSION_MINOR_LO"].get(from_read) + assert self.parent["WAFER_VERSION_MINOR_LO"].bit_len == 3 + return (hi_bits << 3) + lo_bits + + def save(self, new_value): + raise esptool.FatalError("Burning %s is not supported" % self.name) + + +class EfuseTempSensor(EfuseField): + def get(self, from_read=True): + value = self.get_bitstring(from_read) + sig = -1 if value[0] else 1 + return sig * value[1:].uint * 0.1 + + +class EfuseAdcPointCalibration(EfuseField): + def get(self, from_read=True): + STEP_SIZE = 4 + value = self.get_bitstring(from_read) + sig = -1 if value[0] else 1 + return sig * value[1:].uint * STEP_SIZE + + +class EfuseMacField(EfuseField): + def check_format(self, new_value_str): + if new_value_str is None: + raise esptool.FatalError( + "Required MAC Address in AA:CD:EF:01:02:03 format!" + ) + if new_value_str.count(":") != 5: + raise esptool.FatalError( + "MAC Address needs to be a 6-byte hexadecimal format " + "separated by colons (:)!" + ) + hexad = new_value_str.replace(":", "") + if len(hexad) != 12: + raise esptool.FatalError( + "MAC Address needs to be a 6-byte hexadecimal number " + "(12 hexadecimal characters)!" + ) + # order of bytearray = b'\xaa\xcd\xef\x01\x02\x03', + bindata = binascii.unhexlify(hexad) + # unicast address check according to + # https://tools.ietf.org/html/rfc7042#section-2.1 + if esptool.util.byte(bindata, 0) & 0x01: + raise esptool.FatalError("Custom MAC must be a unicast MAC!") + return bindata + + def check(self): + errs, fail = self.parent.get_block_errors(self.block) + if errs != 0 or fail: + output = "Block%d has ERRORS:%d FAIL:%d" % (self.block, errs, fail) + else: + output = "OK" + return "(" + output + ")" + + def get(self, from_read=True): + if self.name == "CUSTOM_MAC": + mac = self.get_raw(from_read)[::-1] + else: + mac = self.get_raw(from_read) + return "%s %s" % (util.hexify(mac, ":"), self.check()) + + def save(self, new_value): + def print_field(e, new_value): + print( + " - '{}' ({}) {} -> {}".format( + e.name, e.description, e.get_bitstring(), new_value + ) + ) + + if self.name == "CUSTOM_MAC": + bitarray_mac = self.convert_to_bitstring(new_value) + print_field(self, bitarray_mac) + super(EfuseMacField, self).save(new_value) + else: + # Writing the BLOCK1 (MAC_SPI_8M_0) default MAC is not possible, + # as it's written in the factory. + raise esptool.FatalError("Writing Factory MAC address is not supported") + + +# fmt: off +class EfuseKeyPurposeField(EfuseField): + KEY_PURPOSES = [ + ("USER", 0, None, None, "no_need_rd_protect"), # User purposes (software-only use) + ("RESERVED", 1, None, None, "no_need_rd_protect"), # Reserved + ("XTS_AES_256_KEY_1", 2, None, "Reverse", "need_rd_protect"), # XTS_AES_256_KEY_1 (flash/PSRAM encryption) + ("XTS_AES_256_KEY_2", 3, None, "Reverse", "need_rd_protect"), # XTS_AES_256_KEY_2 (flash/PSRAM encryption) + ("XTS_AES_128_KEY", 4, None, "Reverse", "need_rd_protect"), # XTS_AES_128_KEY (flash/PSRAM encryption) + ("HMAC_DOWN_ALL", 5, None, None, "need_rd_protect"), # HMAC Downstream mode + ("HMAC_DOWN_JTAG", 6, None, None, "need_rd_protect"), # JTAG soft enable key (uses HMAC Downstream mode) + ("HMAC_DOWN_DIGITAL_SIGNATURE", 7, None, None, "need_rd_protect"), # Digital Signature peripheral key (uses HMAC Downstream mode) + ("HMAC_UP", 8, None, None, "need_rd_protect"), # HMAC Upstream mode + ("SECURE_BOOT_DIGEST0", 9, "DIGEST", None, "no_need_rd_protect"), # SECURE_BOOT_DIGEST0 (Secure Boot key digest) + ("SECURE_BOOT_DIGEST1", 10, "DIGEST", None, "no_need_rd_protect"), # SECURE_BOOT_DIGEST1 (Secure Boot key digest) + ("SECURE_BOOT_DIGEST2", 11, "DIGEST", None, "no_need_rd_protect"), # SECURE_BOOT_DIGEST2 (Secure Boot key digest) + ("XTS_AES_256_KEY", -1, "VIRTUAL", None, "no_need_rd_protect"), # Virtual purpose splits to XTS_AES_256_KEY_1 and XTS_AES_256_KEY_2 + ] +# fmt: on + + KEY_PURPOSES_NAME = [name[0] for name in KEY_PURPOSES] + DIGEST_KEY_PURPOSES = [name[0] for name in KEY_PURPOSES if name[2] == "DIGEST"] + + def check_format(self, new_value_str): + # str convert to int: "XTS_AES_128_KEY" - > str(4) + # if int: 4 -> str(4) + raw_val = new_value_str + for purpose_name in self.KEY_PURPOSES: + if purpose_name[0] == new_value_str: + raw_val = str(purpose_name[1]) + break + if raw_val.isdigit(): + if int(raw_val) not in [p[1] for p in self.KEY_PURPOSES if p[1] > 0]: + raise esptool.FatalError("'%s' can not be set (value out of range)" % raw_val) + else: + raise esptool.FatalError("'%s' unknown name" % raw_val) + return raw_val + + def need_reverse(self, new_key_purpose): + for key in self.KEY_PURPOSES: + if key[0] == new_key_purpose: + return key[3] == "Reverse" + + def need_rd_protect(self, new_key_purpose): + for key in self.KEY_PURPOSES: + if key[0] == new_key_purpose: + return key[4] == "need_rd_protect" + + def get(self, from_read=True): + for p in self.KEY_PURPOSES: + if p[1] == self.get_raw(from_read): + return p[0] + return "FORBIDDEN_STATE" + + def save(self, new_value): + raw_val = int(self.check_format(str(new_value))) + return super(EfuseKeyPurposeField, self).save(raw_val) diff --git a/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32s2/mem_definition.py b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32s2/mem_definition.py new file mode 100644 index 0000000..b83ea91 --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32s2/mem_definition.py @@ -0,0 +1,208 @@ +# This file describes eFuses fields and registers for ESP32 chip +# +# SPDX-FileCopyrightText: 2020-2022 Espressif Systems (Shanghai) CO LTD +# +# SPDX-License-Identifier: GPL-2.0-or-later + +import os + +import yaml + +from ..mem_definition_base import ( + EfuseBlocksBase, + EfuseFieldsBase, + EfuseRegistersBase, + Field, +) + + +class EfuseDefineRegisters(EfuseRegistersBase): + EFUSE_MEM_SIZE = 0x01FC + 4 + + # EFUSE registers & command/conf values + DR_REG_EFUSE_BASE = 0x3F41A000 + EFUSE_PGM_DATA0_REG = DR_REG_EFUSE_BASE + EFUSE_CHECK_VALUE0_REG = DR_REG_EFUSE_BASE + 0x020 + EFUSE_CLK_REG = DR_REG_EFUSE_BASE + 0x1C8 + EFUSE_CONF_REG = DR_REG_EFUSE_BASE + 0x1CC + EFUSE_STATUS_REG = DR_REG_EFUSE_BASE + 0x1D0 + EFUSE_CMD_REG = DR_REG_EFUSE_BASE + 0x1D4 + EFUSE_RD_RS_ERR0_REG = DR_REG_EFUSE_BASE + 0x194 + EFUSE_RD_RS_ERR1_REG = DR_REG_EFUSE_BASE + 0x198 + EFUSE_RD_REPEAT_ERR0_REG = DR_REG_EFUSE_BASE + 0x17C + EFUSE_RD_REPEAT_ERR1_REG = DR_REG_EFUSE_BASE + 0x180 + EFUSE_RD_REPEAT_ERR2_REG = DR_REG_EFUSE_BASE + 0x184 + EFUSE_RD_REPEAT_ERR3_REG = DR_REG_EFUSE_BASE + 0x188 + EFUSE_RD_REPEAT_ERR4_REG = DR_REG_EFUSE_BASE + 0x18C + EFUSE_DAC_CONF_REG = DR_REG_EFUSE_BASE + 0x1E8 + EFUSE_RD_TIM_CONF_REG = DR_REG_EFUSE_BASE + 0x1EC + EFUSE_WR_TIM_CONF1_REG = DR_REG_EFUSE_BASE + 0x1F4 + EFUSE_WR_TIM_CONF2_REG = DR_REG_EFUSE_BASE + 0x1F8 + EFUSE_DATE_REG = DR_REG_EFUSE_BASE + 0x1FC + EFUSE_WRITE_OP_CODE = 0x5A5A + EFUSE_READ_OP_CODE = 0x5AA5 + EFUSE_PGM_CMD_MASK = 0x3 + EFUSE_PGM_CMD = 0x2 + EFUSE_READ_CMD = 0x1 + + BLOCK_ERRORS = [ + # error_reg, err_num_mask, err_num_offs, fail_bit + (EFUSE_RD_REPEAT_ERR0_REG, None, None, None), # BLOCK0 + (EFUSE_RD_RS_ERR0_REG, 0x7, 0, 3), # MAC_SPI_8M_0 + (EFUSE_RD_RS_ERR0_REG, 0x7, 4, 7), # BLOCK_SYS_DATA + (EFUSE_RD_RS_ERR0_REG, 0x7, 8, 11), # BLOCK_USR_DATA + (EFUSE_RD_RS_ERR0_REG, 0x7, 12, 15), # BLOCK_KEY0 + (EFUSE_RD_RS_ERR0_REG, 0x7, 16, 19), # BLOCK_KEY1 + (EFUSE_RD_RS_ERR0_REG, 0x7, 20, 23), # BLOCK_KEY2 + (EFUSE_RD_RS_ERR0_REG, 0x7, 24, 27), # BLOCK_KEY3 + (EFUSE_RD_RS_ERR0_REG, 0x7, 28, 31), # BLOCK_KEY4 + (EFUSE_RD_RS_ERR1_REG, 0x7, 0, 3), # BLOCK_KEY5 + (EFUSE_RD_RS_ERR1_REG, 0x7, 4, 7), # BLOCK_SYS_DATA2 + ] + + EFUSE_DAC_CONF_REG = DR_REG_EFUSE_BASE + 0x1E8 + EFUSE_DAC_CLK_DIV_S = 0 + EFUSE_DAC_CLK_DIV_M = 0xFF << EFUSE_DAC_CLK_DIV_S + + EFUSE_RD_TIM_CONF_REG = DR_REG_EFUSE_BASE + 0x1EC + EFUSE_TSUR_A_S = 16 + EFUSE_TSUR_A_M = 0xFF << EFUSE_TSUR_A_S + EFUSE_TRD_S = 8 + EFUSE_TRD_M = 0xFF << EFUSE_TRD_S + EFUSE_THR_A_S = 0 + EFUSE_THR_A_M = 0xFF << EFUSE_THR_A_S + + EFUSE_WR_TIM_CONF0_REG = DR_REG_EFUSE_BASE + 0x1F0 + EFUSE_TPGM_S = 16 + EFUSE_TPGM_M = 0xFFFF << EFUSE_TPGM_S + EFUSE_TPGM_INACTIVE_S = 8 + EFUSE_TPGM_INACTIVE_M = 0xFF << EFUSE_TPGM_INACTIVE_S + EFUSE_THP_A_S = 0 + EFUSE_THP_A_M = 0xFF << EFUSE_THP_A_S + + # EFUSE_WR_TIM_CONF1_REG + EFUSE_PWR_ON_NUM_S = 8 + EFUSE_PWR_ON_NUM_M = 0xFFFF << EFUSE_PWR_ON_NUM_S + EFUSE_TSUP_A_S = 0 + EFUSE_TSUP_A_M = 0xFF << EFUSE_TSUP_A_S + + # EFUSE_WR_TIM_CONF2_REG + EFUSE_PWR_OFF_NUM_S = 0 + EFUSE_PWR_OFF_NUM_M = 0xFFFF << EFUSE_PWR_OFF_NUM_S + + # Configure clock + EFUSE_PROGRAMMING_TIMING_PARAMETERS = { + # APB Frequency: ( EFUSE_TSUP_A, EFUSE_TPGM, EFUSE_THP_A, EFUSE_TPGM_INACTIVE ) + # Taken from TRM chapter "eFuse Controller": eFuse-Programming Timing + 80: (0x2, 0x320, 0x2, 0x4), + 40: (0x1, 0x190, 0x1, 0x2), + 20: (0x1, 0xC8, 0x1, 0x1), + } + + VDDQ_TIMING_PARAMETERS = { + # APB Frequency: ( EFUSE_DAC_CLK_DIV, EFUSE_PWR_ON_NUM, EFUSE_PWR_OFF_NUM ) + # Taken from TRM chapter "eFuse Controller": eFuse VDDQ Timing Setting + 80: (0xA0, 0xA200, 0x100), + 40: (0x50, 0x5100, 0x80), + 20: (0x28, 0x2880, 0x40), + } + + EFUSE_READING_PARAMETERS = { + # APB Frequency: ( EFUSE_TSUR_A, EFUSE_TRD, EFUSE_THR_A ) + # Taken from TRM chapter "eFuse Controller": eFuse-Read Timing + 80: (0x2, 0x4, 0x2), + 40: (0x1, 0x2, 0x1), + 20: (0x1, 0x1, 0x1), + } + + +class EfuseDefineBlocks(EfuseBlocksBase): + __base_rd_regs = EfuseDefineRegisters.DR_REG_EFUSE_BASE + __base_wr_regs = EfuseDefineRegisters.EFUSE_PGM_DATA0_REG + # List of efuse blocks + # fmt: off + BLOCKS = [ + # Name, Alias, Index, Read address, Write address, Write protect bit, Read protect bit, Len, key_purpose + ("BLOCK0", [], 0, __base_rd_regs + 0x02C, __base_wr_regs, None, None, 6, None), + ("MAC_SPI_8M_0", ["BLOCK1"], 1, __base_rd_regs + 0x044, __base_wr_regs, 20, None, 6, None), + ("BLOCK_SYS_DATA", ["BLOCK2"], 2, __base_rd_regs + 0x05C, __base_wr_regs, 21, None, 8, None), + ("BLOCK_USR_DATA", ["BLOCK3"], 3, __base_rd_regs + 0x07C, __base_wr_regs, 22, None, 8, None), + ("BLOCK_KEY0", ["BLOCK4"], 4, __base_rd_regs + 0x09C, __base_wr_regs, 23, 0, 8, "KEY_PURPOSE_0"), + ("BLOCK_KEY1", ["BLOCK5"], 5, __base_rd_regs + 0x0BC, __base_wr_regs, 24, 1, 8, "KEY_PURPOSE_1"), + ("BLOCK_KEY2", ["BLOCK6"], 6, __base_rd_regs + 0x0DC, __base_wr_regs, 25, 2, 8, "KEY_PURPOSE_2"), + ("BLOCK_KEY3", ["BLOCK7"], 7, __base_rd_regs + 0x0FC, __base_wr_regs, 26, 3, 8, "KEY_PURPOSE_3"), + ("BLOCK_KEY4", ["BLOCK8"], 8, __base_rd_regs + 0x11C, __base_wr_regs, 27, 4, 8, "KEY_PURPOSE_4"), + ("BLOCK_KEY5", ["BLOCK9"], 9, __base_rd_regs + 0x13C, __base_wr_regs, 28, 5, 8, "KEY_PURPOSE_5"), + ("BLOCK_SYS_DATA2", ["BLOCK10"], 10, __base_rd_regs + 0x15C, __base_wr_regs, 29, 6, 8, None), + ] + # fmt: on + + def get_burn_block_data_names(self): + list_of_names = [] + for block in self.BLOCKS: + blk = self.get(block) + if blk.name: + list_of_names.append(blk.name) + if blk.alias: + for alias in blk.alias: + list_of_names.append(alias) + return list_of_names + + +class EfuseDefineFields(EfuseFieldsBase): + def __init__(self) -> None: + # List of efuse fields from TRM the chapter eFuse Controller. + self.EFUSES = [] + + self.KEYBLOCKS = [] + + # if BLK_VERSION_MINOR is 1, these efuse fields are in BLOCK2 + self.BLOCK2_CALIBRATION_EFUSES = [] + + self.CALC = [] + + dir_name = os.path.dirname(os.path.abspath(__file__)) + dir_name, file_name = os.path.split(dir_name) + file_name = file_name + ".yaml" + dir_name, _ = os.path.split(dir_name) + efuse_file = os.path.join(dir_name, "efuse_defs", file_name) + with open(f"{efuse_file}", "r") as r_file: + e_desc = yaml.safe_load(r_file) + super().__init__(e_desc) + + for i, efuse in enumerate(self.ALL_EFUSES): + if efuse.name in [ + "BLOCK_USR_DATA", + "BLOCK_KEY0", + "BLOCK_KEY1", + "BLOCK_KEY2", + "BLOCK_KEY3", + "BLOCK_KEY4", + "BLOCK_KEY5", + "BLOCK_SYS_DATA2", + ]: + if efuse.name == "BLOCK_USR_DATA": + efuse.bit_len = 256 + efuse.type = "bytes:32" + self.KEYBLOCKS.append(efuse) + self.ALL_EFUSES[i] = None + + elif efuse.category == "calibration": + self.BLOCK2_CALIBRATION_EFUSES.append(efuse) + self.ALL_EFUSES[i] = None + + f = Field() + f.name = "WAFER_VERSION_MINOR" + f.block = 0 + f.bit_len = 4 + f.type = f"uint:{f.bit_len}" + f.category = "identity" + f.class_type = "wafer" + f.description = "calc WAFER VERSION MINOR = WAFER_VERSION_MINOR_HI << 3 + WAFER_VERSION_MINOR_LO (read only)" + self.CALC.append(f) + + for efuse in self.ALL_EFUSES: + if efuse is not None: + self.EFUSES.append(efuse) + + self.ALL_EFUSES = [] diff --git a/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32s2/operations.py b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32s2/operations.py new file mode 100644 index 0000000..5ccfac3 --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32s2/operations.py @@ -0,0 +1,532 @@ +# This file includes the operations with eFuses for ESP32S2 chip +# +# SPDX-FileCopyrightText: 2020-2022 Espressif Systems (Shanghai) CO LTD +# +# SPDX-License-Identifier: GPL-2.0-or-later + +import argparse +import io +import os # noqa: F401. It is used in IDF scripts +import traceback + +import espsecure + +import esptool + +from . import fields +from .. import util +from ..base_operations import ( + add_common_commands, + add_force_write_always, + add_show_sensitive_info_option, + burn_bit, + burn_block_data, + burn_efuse, + check_error, + dump, + read_protect_efuse, + summary, + write_protect_efuse, +) + + +def protect_options(p): + p.add_argument( + "--no-write-protect", + help="Disable write-protecting of the key. The key remains writable. " + "(The keys use the RS coding scheme that does not support post-write " + "data changes. Forced write can damage RS encoding bits.) " + "The write-protecting of keypurposes does not depend on the option, " + "it will be set anyway.", + action="store_true", + ) + p.add_argument( + "--no-read-protect", + help="Disable read-protecting of the key. The key remains readable software." + "The key with keypurpose[USER, RESERVED and *_DIGEST] " + "will remain readable anyway. For the rest keypurposes the read-protection " + "will be defined the option (Read-protect by default).", + action="store_true", + ) + + +def add_commands(subparsers, efuses): + add_common_commands(subparsers, efuses) + burn_key = subparsers.add_parser( + "burn_key", help="Burn the key block with the specified name" + ) + protect_options(burn_key) + add_force_write_always(burn_key) + add_show_sensitive_info_option(burn_key) + burn_key.add_argument( + "block", + help="Key block to burn", + action="append", + choices=efuses.BLOCKS_FOR_KEYS, + ) + burn_key.add_argument( + "keyfile", + help="File containing 256 bits of binary key data", + action="append", + type=argparse.FileType("rb"), + ) + burn_key.add_argument( + "keypurpose", + help="Purpose to set.", + action="append", + choices=fields.EfuseKeyPurposeField.KEY_PURPOSES_NAME, + ) + for _ in efuses.BLOCKS_FOR_KEYS: + burn_key.add_argument( + "block", + help="Key block to burn", + nargs="?", + action="append", + metavar="BLOCK", + choices=efuses.BLOCKS_FOR_KEYS, + ) + burn_key.add_argument( + "keyfile", + help="File containing 256 bits of binary key data", + nargs="?", + action="append", + metavar="KEYFILE", + type=argparse.FileType("rb"), + ) + burn_key.add_argument( + "keypurpose", + help="Purpose to set.", + nargs="?", + action="append", + metavar="KEYPURPOSE", + choices=fields.EfuseKeyPurposeField.KEY_PURPOSES_NAME, + ) + + burn_key_digest = subparsers.add_parser( + "burn_key_digest", + help="Parse a RSA public key and burn the digest to key efuse block", + ) + protect_options(burn_key_digest) + add_force_write_always(burn_key_digest) + add_show_sensitive_info_option(burn_key_digest) + burn_key_digest.add_argument( + "block", + help="Key block to burn", + action="append", + choices=efuses.BLOCKS_FOR_KEYS, + ) + burn_key_digest.add_argument( + "keyfile", + help="Key file to digest (PEM format)", + action="append", + type=argparse.FileType("rb"), + ) + burn_key_digest.add_argument( + "keypurpose", + help="Purpose to set.", + action="append", + choices=fields.EfuseKeyPurposeField.DIGEST_KEY_PURPOSES, + ) + for _ in efuses.BLOCKS_FOR_KEYS: + burn_key_digest.add_argument( + "block", + help="Key block to burn", + nargs="?", + action="append", + metavar="BLOCK", + choices=efuses.BLOCKS_FOR_KEYS, + ) + burn_key_digest.add_argument( + "keyfile", + help="Key file to digest (PEM format)", + nargs="?", + action="append", + metavar="KEYFILE", + type=argparse.FileType("rb"), + ) + burn_key_digest.add_argument( + "keypurpose", + help="Purpose to set.", + nargs="?", + action="append", + metavar="KEYPURPOSE", + choices=fields.EfuseKeyPurposeField.DIGEST_KEY_PURPOSES, + ) + + p = subparsers.add_parser( + "set_flash_voltage", + help="Permanently set the internal flash voltage regulator " + "to either 1.8V, 3.3V or OFF. " + "This means GPIO45 can be high or low at reset without " + "changing the flash voltage.", + ) + p.add_argument("voltage", help="Voltage selection", choices=["1.8V", "3.3V", "OFF"]) + + p = subparsers.add_parser( + "burn_custom_mac", help="Burn a 48-bit Custom MAC Address to EFUSE BLOCK3." + ) + p.add_argument( + "mac", + help="Custom MAC Address to burn given in hexadecimal format with bytes " + "separated by colons (e.g. AA:CD:EF:01:02:03).", + type=fields.base_fields.CheckArgValue(efuses, "CUSTOM_MAC"), + ) + add_force_write_always(p) + + p = subparsers.add_parser("get_custom_mac", help="Prints the Custom MAC Address.") + + +def burn_custom_mac(esp, efuses, args): + efuses["CUSTOM_MAC"].save(args.mac) + if not efuses.burn_all(check_batch_mode=True): + return + get_custom_mac(esp, efuses, args) + print("Successful") + + +def get_custom_mac(esp, efuses, args): + print("Custom MAC Address: {}".format(efuses["CUSTOM_MAC"].get())) + + +def set_flash_voltage(esp, efuses, args): + sdio_force = efuses["VDD_SPI_FORCE"] + sdio_tieh = efuses["VDD_SPI_TIEH"] + sdio_reg = efuses["VDD_SPI_XPD"] + + # check efuses aren't burned in a way which makes this impossible + if args.voltage == "OFF" and sdio_reg.get() != 0: + raise esptool.FatalError( + "Can't set flash regulator to OFF as VDD_SPI_XPD efuse is already burned" + ) + + if args.voltage == "1.8V" and sdio_tieh.get() != 0: + raise esptool.FatalError( + "Can't set regulator to 1.8V is VDD_SPI_TIEH efuse is already burned" + ) + + if args.voltage == "OFF": + msg = "Disable internal flash voltage regulator (VDD_SPI). SPI flash will " + "need to be powered from an external source.\n" + "The following efuse is burned: VDD_SPI_FORCE.\n" + "It is possible to later re-enable the internal regulator (%s) " % ( + "to 3.3V" if sdio_tieh.get() != 0 else "to 1.8V or 3.3V" + ) + "by burning an additional efuse" + elif args.voltage == "1.8V": + msg = "Set internal flash voltage regulator (VDD_SPI) to 1.8V.\n" + "The following efuses are burned: VDD_SPI_FORCE, VDD_SPI_XPD.\n" + "It is possible to later increase the voltage to 3.3V (permanently) " + "by burning additional efuse VDD_SPI_TIEH" + elif args.voltage == "3.3V": + msg = "Enable internal flash voltage regulator (VDD_SPI) to 3.3V.\n" + "The following efuses are burned: VDD_SPI_FORCE, VDD_SPI_XPD, VDD_SPI_TIEH." + print(msg) + + sdio_force.save(1) # Disable GPIO45 + if args.voltage != "OFF": + sdio_reg.save(1) # Enable internal regulator + if args.voltage == "3.3V": + sdio_tieh.save(1) + print("VDD_SPI setting complete.") + + if not efuses.burn_all(check_batch_mode=True): + return + print("Successful") + + +def adc_info(esp, efuses, args): + print("") + # fmt: off + if efuses["BLK_VERSION_MINOR"].get() == 1: + print("Temperature Sensor Calibration = {}C".format(efuses["TEMP_SENSOR_CAL"].get())) + + print("") + print("ADC1 readings stored in efuse BLOCK2:") + print(" MODE0 D1 reading (250mV): {}".format(efuses["ADC1_MODE0_D1"].get())) + print(" MODE0 D2 reading (600mV): {}".format(efuses["ADC1_MODE0_D2"].get())) + + print(" MODE1 D1 reading (250mV): {}".format(efuses["ADC1_MODE1_D1"].get())) + print(" MODE1 D2 reading (800mV): {}".format(efuses["ADC1_MODE1_D2"].get())) + + print(" MODE2 D1 reading (250mV): {}".format(efuses["ADC1_MODE2_D1"].get())) + print(" MODE2 D2 reading (1000mV): {}".format(efuses["ADC1_MODE2_D2"].get())) + + print(" MODE3 D1 reading (250mV): {}".format(efuses["ADC1_MODE3_D1"].get())) + print(" MODE3 D2 reading (2000mV): {}".format(efuses["ADC1_MODE3_D2"].get())) + + print("") + print("ADC2 readings stored in efuse BLOCK2:") + print(" MODE0 D1 reading (250mV): {}".format(efuses["ADC2_MODE0_D1"].get())) + print(" MODE0 D2 reading (600mV): {}".format(efuses["ADC2_MODE0_D2"].get())) + + print(" MODE1 D1 reading (250mV): {}".format(efuses["ADC2_MODE1_D1"].get())) + print(" MODE1 D2 reading (800mV): {}".format(efuses["ADC2_MODE1_D2"].get())) + + print(" MODE2 D1 reading (250mV): {}".format(efuses["ADC2_MODE2_D1"].get())) + print(" MODE2 D2 reading (1000mV): {}".format(efuses["ADC2_MODE2_D2"].get())) + + print(" MODE3 D1 reading (250mV): {}".format(efuses["ADC2_MODE3_D1"].get())) + print(" MODE3 D2 reading (2000mV): {}".format(efuses["ADC2_MODE3_D2"].get())) + else: + print("BLK_VERSION_MINOR = {}".format(efuses["BLK_VERSION_MINOR"].get_meaning())) + # fmt: on + + +def key_block_is_unused(block, key_purpose_block): + if not block.is_readable() or not block.is_writeable(): + return False + + if key_purpose_block.get() != "USER" or not key_purpose_block.is_writeable(): + return False + + if not block.get_bitstring().all(False): + return False + + return True + + +def get_next_key_block(efuses, current_key_block, block_name_list): + key_blocks = [b for b in efuses.blocks if b.key_purpose_name] + start = key_blocks.index(current_key_block) + + # Sort key blocks so that we pick the next free block (and loop around if necessary) + key_blocks = key_blocks[start:] + key_blocks[0:start] + + # Exclude any other blocks that will be be burned + key_blocks = [b for b in key_blocks if b.name not in block_name_list] + + for block in key_blocks: + key_purpose_block = efuses[block.key_purpose_name] + if key_block_is_unused(block, key_purpose_block): + return block + + return None + + +def split_512_bit_key(efuses, block_name_list, datafile_list, keypurpose_list): + i = keypurpose_list.index("XTS_AES_256_KEY") + block_name = block_name_list[i] + + block_num = efuses.get_index_block_by_name(block_name) + block = efuses.blocks[block_num] + + data = datafile_list[i].read() + if len(data) != 64: + raise esptool.FatalError( + "Incorrect key file size %d, XTS_AES_256_KEY should be 64 bytes" % len(data) + ) + + key_block_2 = get_next_key_block(efuses, block, block_name_list) + if not key_block_2: + raise esptool.FatalError("XTS_AES_256_KEY requires two free keyblocks") + + keypurpose_list.append("XTS_AES_256_KEY_1") + datafile_list.append(io.BytesIO(data[:32])) + block_name_list.append(block_name) + + keypurpose_list.append("XTS_AES_256_KEY_2") + datafile_list.append(io.BytesIO(data[32:])) + block_name_list.append(key_block_2.name) + + keypurpose_list.pop(i) + datafile_list.pop(i) + block_name_list.pop(i) + + +def burn_key(esp, efuses, args, digest=None): + if digest is None: + datafile_list = args.keyfile[ + 0 : len([name for name in args.keyfile if name is not None]) : + ] + else: + datafile_list = digest[0 : len([name for name in digest if name is not None]) :] + efuses.force_write_always = args.force_write_always + block_name_list = args.block[ + 0 : len([name for name in args.block if name is not None]) : + ] + keypurpose_list = args.keypurpose[ + 0 : len([name for name in args.keypurpose if name is not None]) : + ] + + if "XTS_AES_256_KEY" in keypurpose_list: + # XTS_AES_256_KEY is not an actual HW key purpose, needs to be split into + # XTS_AES_256_KEY_1 and XTS_AES_256_KEY_2 + split_512_bit_key(efuses, block_name_list, datafile_list, keypurpose_list) + + util.check_duplicate_name_in_list(block_name_list) + if len(block_name_list) != len(datafile_list) or len(block_name_list) != len( + keypurpose_list + ): + raise esptool.FatalError( + "The number of blocks (%d), datafile (%d) and keypurpose (%d) " + "should be the same." + % (len(block_name_list), len(datafile_list), len(keypurpose_list)) + ) + + print("Burn keys to blocks:") + for block_name, datafile, keypurpose in zip( + block_name_list, datafile_list, keypurpose_list + ): + efuse = None + for block in efuses.blocks: + if block_name == block.name or block_name in block.alias: + efuse = efuses[block.name] + if efuse is None: + raise esptool.FatalError("Unknown block name - %s" % (block_name)) + num_bytes = efuse.bit_len // 8 + + block_num = efuses.get_index_block_by_name(block_name) + block = efuses.blocks[block_num] + + if digest is None: + data = datafile.read() + else: + data = datafile + + print(" - %s" % (efuse.name), end=" ") + revers_msg = None + if efuses[block.key_purpose_name].need_reverse(keypurpose): + revers_msg = "\tReversing byte order for AES-XTS hardware peripheral" + data = data[::-1] + print( + "-> [{}]".format( + util.hexify(data, " ") + if args.show_sensitive_info + else " ".join(["??"] * len(data)) + ) + ) + if revers_msg: + print(revers_msg) + if len(data) != num_bytes: + raise esptool.FatalError( + "Incorrect key file size %d. Key file must be %d bytes (%d bits) " + "of raw binary key data." % (len(data), num_bytes, num_bytes * 8) + ) + + if efuses[block.key_purpose_name].need_rd_protect(keypurpose): + read_protect = False if args.no_read_protect else True + else: + read_protect = False + write_protect = not args.no_write_protect + + # using efuse instead of a block gives the advantage of + # checking it as the whole field. + efuse.save(data) + + disable_wr_protect_key_purpose = False + if efuses[block.key_purpose_name].get() != keypurpose: + if efuses[block.key_purpose_name].is_writeable(): + print( + "\t'%s': '%s' -> '%s'." + % ( + block.key_purpose_name, + efuses[block.key_purpose_name].get(), + keypurpose, + ) + ) + efuses[block.key_purpose_name].save(keypurpose) + disable_wr_protect_key_purpose = True + else: + raise esptool.FatalError( + "It is not possible to change '%s' to '%s' because " + "write protection bit is set." + % (block.key_purpose_name, keypurpose) + ) + else: + print("\t'%s' is already '%s'." % (block.key_purpose_name, keypurpose)) + if efuses[block.key_purpose_name].is_writeable(): + disable_wr_protect_key_purpose = True + + if disable_wr_protect_key_purpose: + print("\tDisabling write to '%s'." % block.key_purpose_name) + efuses[block.key_purpose_name].disable_write() + + if read_protect: + print("\tDisabling read to key block") + efuse.disable_read() + + if write_protect: + print("\tDisabling write to key block") + efuse.disable_write() + print("") + + if not write_protect: + print("Keys will remain writeable (due to --no-write-protect)") + if args.no_read_protect: + print("Keys will remain readable (due to --no-read-protect)") + + if not efuses.burn_all(check_batch_mode=True): + return + print("Successful") + + +def burn_key_digest(esp, efuses, args): + digest_list = [] + datafile_list = args.keyfile[ + 0 : len([name for name in args.keyfile if name is not None]) : + ] + block_list = args.block[ + 0 : len([block for block in args.block if block is not None]) : + ] + for block_name, datafile in zip(block_list, datafile_list): + efuse = None + for block in efuses.blocks: + if block_name == block.name or block_name in block.alias: + efuse = efuses[block.name] + if efuse is None: + raise esptool.FatalError("Unknown block name - %s" % (block_name)) + num_bytes = efuse.bit_len // 8 + digest = espsecure._digest_sbv2_public_key(datafile) + if len(digest) != num_bytes: + raise esptool.FatalError( + "Incorrect digest size %d. Digest must be %d bytes (%d bits) of raw " + "binary key data." % (len(digest), num_bytes, num_bytes * 8) + ) + digest_list.append(digest) + burn_key(esp, efuses, args, digest=digest_list) + + +def espefuse(esp, efuses, args, command): + parser = argparse.ArgumentParser() + subparsers = parser.add_subparsers(dest="operation") + add_commands(subparsers, efuses) + try: + cmd_line_args = parser.parse_args(command.split()) + except SystemExit: + traceback.print_stack() + raise esptool.FatalError('"{}" - incorrect command'.format(command)) + if cmd_line_args.operation == "execute_scripts": + configfiles = cmd_line_args.configfiles + index = cmd_line_args.index + # copy arguments from args to cmd_line_args + vars(cmd_line_args).update(vars(args)) + if cmd_line_args.operation == "execute_scripts": + cmd_line_args.configfiles = configfiles + cmd_line_args.index = index + if cmd_line_args.operation is None: + parser.print_help() + parser.exit(1) + operation_func = globals()[cmd_line_args.operation] + # each 'operation' is a module-level function of the same name + operation_func(esp, efuses, cmd_line_args) + + +def execute_scripts(esp, efuses, args): + efuses.batch_mode_cnt += 1 + del args.operation + scripts = args.scripts + del args.scripts + + for file in scripts: + with open(file.name, "r") as file: + exec(compile(file.read(), file.name, "exec")) + + if args.debug: + for block in efuses.blocks: + data = block.get_bitstring(from_read=False) + block.print_block(data, "regs_for_burn", args.debug) + + efuses.batch_mode_cnt -= 1 + if not efuses.burn_all(check_batch_mode=True): + return + print("Successful") diff --git a/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32s3/__init__.py b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32s3/__init__.py new file mode 100644 index 0000000..a3b55a8 --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32s3/__init__.py @@ -0,0 +1,3 @@ +from . import operations +from .emulate_efuse_controller import EmulateEfuseController +from .fields import EspEfuses diff --git a/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32s3/__pycache__/__init__.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32s3/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..6eb2c52 Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32s3/__pycache__/__init__.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32s3/__pycache__/emulate_efuse_controller.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32s3/__pycache__/emulate_efuse_controller.cpython-312.pyc new file mode 100644 index 0000000..a197eca Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32s3/__pycache__/emulate_efuse_controller.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32s3/__pycache__/fields.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32s3/__pycache__/fields.cpython-312.pyc new file mode 100644 index 0000000..1bb6f4e Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32s3/__pycache__/fields.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32s3/__pycache__/mem_definition.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32s3/__pycache__/mem_definition.cpython-312.pyc new file mode 100644 index 0000000..4eb05fd Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32s3/__pycache__/mem_definition.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32s3/__pycache__/operations.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32s3/__pycache__/operations.cpython-312.pyc new file mode 100644 index 0000000..b5bad2a Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32s3/__pycache__/operations.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32s3/emulate_efuse_controller.py b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32s3/emulate_efuse_controller.py new file mode 100644 index 0000000..7e767b9 --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32s3/emulate_efuse_controller.py @@ -0,0 +1,92 @@ +# This file describes eFuses controller for ESP32-S3 chip +# +# SPDX-FileCopyrightText: 2020-2022 Espressif Systems (Shanghai) CO LTD +# +# SPDX-License-Identifier: GPL-2.0-or-later + +import reedsolo + +from .mem_definition import EfuseDefineBlocks, EfuseDefineFields, EfuseDefineRegisters +from ..emulate_efuse_controller_base import EmulateEfuseControllerBase, FatalError + + +class EmulateEfuseController(EmulateEfuseControllerBase): + """The class for virtual efuse operation. Using for HOST_TEST.""" + + CHIP_NAME = "ESP32-S3" + mem = None + debug = False + + def __init__(self, efuse_file=None, debug=False): + self.Blocks = EfuseDefineBlocks + self.Fields = EfuseDefineFields() + self.REGS = EfuseDefineRegisters + super(EmulateEfuseController, self).__init__(efuse_file, debug) + self.write_reg(self.REGS.EFUSE_CMD_REG, 0) + + """ esptool method start >>""" + + def get_major_chip_version(self): + return 0 + + def get_minor_chip_version(self): + return 2 + + def get_crystal_freq(self): + return 40 # MHz (common for all chips) + + def get_security_info(self): + return { + "flags": 0, + "flash_crypt_cnt": 0, + "key_purposes": 0, + "chip_id": 0, + "api_version": 0, + } + + """ << esptool method end """ + + def handle_writing_event(self, addr, value): + if addr == self.REGS.EFUSE_CMD_REG: + if value & self.REGS.EFUSE_PGM_CMD: + self.copy_blocks_wr_regs_to_rd_regs(updated_block=(value >> 2) & 0xF) + self.clean_blocks_wr_regs() + self.check_rd_protection_area() + self.write_reg(addr, 0) + self.write_reg(self.REGS.EFUSE_CMD_REG, 0) + elif value == self.REGS.EFUSE_READ_CMD: + self.write_reg(addr, 0) + self.write_reg(self.REGS.EFUSE_CMD_REG, 0) + self.save_to_file() + + def get_bitlen_of_block(self, blk, wr=False): + if blk.id == 0: + if wr: + return 32 * 8 + else: + return 32 * blk.len + else: + if wr: + rs_coding = 32 * 3 + return 32 * 8 + rs_coding + else: + return 32 * blk.len + + def handle_coding_scheme(self, blk, data): + if blk.id != 0: + # CODING_SCHEME RS applied only for all blocks except BLK0. + coded_bytes = 12 + data.pos = coded_bytes * 8 + plain_data = data.readlist("32*uint:8")[::-1] + # takes 32 bytes + # apply RS encoding + rs = reedsolo.RSCodec(coded_bytes) + # 32 byte of data + 12 bytes RS + calc_encoded_data = list(rs.encode([x for x in plain_data])) + data.pos = 0 + if calc_encoded_data != data.readlist("44*uint:8")[::-1]: + raise FatalError("Error in coding scheme data") + data = data[coded_bytes * 8 :] + if blk.len < 8: + data = data[(8 - blk.len) * 32 :] + return data diff --git a/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32s3/fields.py b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32s3/fields.py new file mode 100644 index 0000000..7cd8cd9 --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32s3/fields.py @@ -0,0 +1,460 @@ +# This file describes eFuses for ESP32-S3 chip +# +# SPDX-FileCopyrightText: 2020-2022 Espressif Systems (Shanghai) CO LTD +# +# SPDX-License-Identifier: GPL-2.0-or-later + +import binascii +import struct +import sys +import time + +from bitstring import BitArray + +import esptool + +import reedsolo + +from .mem_definition import EfuseDefineBlocks, EfuseDefineFields, EfuseDefineRegisters +from .. import base_fields +from .. import util + + +class EfuseBlock(base_fields.EfuseBlockBase): + def len_of_burn_unit(self): + # The writing register window is 8 registers for any blocks. + # len in bytes + return 8 * 4 + + def __init__(self, parent, param, skip_read=False): + parent.read_coding_scheme() + super(EfuseBlock, self).__init__(parent, param, skip_read=skip_read) + + def apply_coding_scheme(self): + data = self.get_raw(from_read=False)[::-1] + if len(data) < self.len_of_burn_unit(): + add_empty_bytes = self.len_of_burn_unit() - len(data) + data = data + (b"\x00" * add_empty_bytes) + if self.get_coding_scheme() == self.parent.REGS.CODING_SCHEME_RS: + # takes 32 bytes + # apply RS encoding + rs = reedsolo.RSCodec(12) + # 32 byte of data + 12 bytes RS + encoded_data = rs.encode([x for x in data]) + words = struct.unpack("<" + "I" * 11, encoded_data) + # returns 11 words (8 words of data + 3 words of RS coding) + else: + # takes 32 bytes + words = struct.unpack("<" + ("I" * (len(data) // 4)), data) + # returns 8 words + return words + + +class EspEfuses(base_fields.EspEfusesBase): + """ + Wrapper object to manage the efuse fields in a connected ESP bootloader + """ + + debug = False + do_not_confirm = False + + def __init__(self, esp, skip_connect=False, debug=False, do_not_confirm=False): + self.Blocks = EfuseDefineBlocks() + self.Fields = EfuseDefineFields() + self.REGS = EfuseDefineRegisters + self.BURN_BLOCK_DATA_NAMES = self.Blocks.get_burn_block_data_names() + self.BLOCKS_FOR_KEYS = self.Blocks.get_blocks_for_keys() + self._esp = esp + self.debug = debug + self.do_not_confirm = do_not_confirm + if esp.CHIP_NAME != "ESP32-S3": + raise esptool.FatalError( + "Expected the 'esp' param for ESP32-S3 chip but got for '%s'." + % (esp.CHIP_NAME) + ) + if not skip_connect: + flags = self._esp.get_security_info()["flags"] + GET_SECURITY_INFO_FLAG_SECURE_DOWNLOAD_ENABLE = 1 << 2 + if flags & GET_SECURITY_INFO_FLAG_SECURE_DOWNLOAD_ENABLE: + raise esptool.FatalError( + "Secure Download Mode is enabled. The tool can not read eFuses." + ) + self.blocks = [ + EfuseBlock(self, self.Blocks.get(block), skip_read=skip_connect) + for block in self.Blocks.BLOCKS + ] + if not skip_connect: + self.get_coding_scheme_warnings() + self.efuses = [EfuseField.convert(self, efuse) for efuse in self.Fields.EFUSES] + self.efuses += [ + EfuseField.convert(self, efuse) for efuse in self.Fields.KEYBLOCKS + ] + if skip_connect: + self.efuses += [ + EfuseField.convert(self, efuse) + for efuse in self.Fields.BLOCK2_CALIBRATION_EFUSES + ] + else: + if self["BLK_VERSION_MAJOR"].get() == 1: + self.efuses += [ + EfuseField.convert(self, efuse) + for efuse in self.Fields.BLOCK2_CALIBRATION_EFUSES + ] + self.efuses += [ + EfuseField.convert(self, efuse) for efuse in self.Fields.CALC + ] + + def __getitem__(self, efuse_name): + """Return the efuse field with the given name""" + for e in self.efuses: + if efuse_name == e.name or any(x == efuse_name for x in e.alt_names): + return e + new_fields = False + for efuse in self.Fields.BLOCK2_CALIBRATION_EFUSES: + if efuse.name == efuse_name or any( + x == efuse_name for x in efuse.alt_names + ): + self.efuses += [ + EfuseField.convert(self, efuse) + for efuse in self.Fields.BLOCK2_CALIBRATION_EFUSES + ] + new_fields = True + if new_fields: + for e in self.efuses: + if efuse_name == e.name or any(x == efuse_name for x in e.alt_names): + return e + raise KeyError + + def read_coding_scheme(self): + self.coding_scheme = self.REGS.CODING_SCHEME_RS + + def print_status_regs(self): + print("") + self.blocks[0].print_block(self.blocks[0].err_bitarray, "err__regs", debug=True) + print( + "{:27} 0x{:08x}".format( + "EFUSE_RD_RS_ERR0_REG", self.read_reg(self.REGS.EFUSE_RD_RS_ERR0_REG) + ) + ) + print( + "{:27} 0x{:08x}".format( + "EFUSE_RD_RS_ERR1_REG", self.read_reg(self.REGS.EFUSE_RD_RS_ERR1_REG) + ) + ) + + def efuse_controller_setup(self): + self.set_efuse_timing() + self.clear_pgm_registers() + self.wait_efuse_idle() + + def write_efuses(self, block): + self.efuse_program(block) + return self.get_coding_scheme_warnings(silent=True) + + def clear_pgm_registers(self): + self.wait_efuse_idle() + for r in range( + self.REGS.EFUSE_PGM_DATA0_REG, self.REGS.EFUSE_PGM_DATA0_REG + 32, 4 + ): + self.write_reg(r, 0) + + def wait_efuse_idle(self): + deadline = time.time() + self.REGS.EFUSE_BURN_TIMEOUT + while time.time() < deadline: + cmds = self.REGS.EFUSE_PGM_CMD | self.REGS.EFUSE_READ_CMD + if self.read_reg(self.REGS.EFUSE_CMD_REG) & cmds == 0: + if self.read_reg(self.REGS.EFUSE_CMD_REG) & cmds == 0: + # Due to a hardware error, we have to read READ_CMD again + # to make sure the efuse clock is normal. + # For PGM_CMD it is not necessary. + return + raise esptool.FatalError( + "Timed out waiting for Efuse controller command to complete" + ) + + def efuse_program(self, block): + self.wait_efuse_idle() + self.write_reg(self.REGS.EFUSE_CONF_REG, self.REGS.EFUSE_WRITE_OP_CODE) + self.write_reg(self.REGS.EFUSE_CMD_REG, self.REGS.EFUSE_PGM_CMD | (block << 2)) + self.wait_efuse_idle() + self.clear_pgm_registers() + self.efuse_read() + + def efuse_read(self): + self.wait_efuse_idle() + self.write_reg(self.REGS.EFUSE_CONF_REG, self.REGS.EFUSE_READ_OP_CODE) + # need to add a delay after triggering EFUSE_READ_CMD, as ROM loader checks some + # efuse registers after each command is completed + # if ENABLE_SECURITY_DOWNLOAD or DIS_DOWNLOAD_MODE is enabled by the current cmd, then we need to try to reconnect to the chip. + try: + self.write_reg( + self.REGS.EFUSE_CMD_REG, self.REGS.EFUSE_READ_CMD, delay_after_us=1000 + ) + self.wait_efuse_idle() + except esptool.FatalError: + secure_download_mode_before = self._esp.secure_download_mode + + try: + self._esp = self.reconnect_chip(self._esp) + except esptool.FatalError: + print("Can not re-connect to the chip") + if not self["DIS_DOWNLOAD_MODE"].get() and self[ + "DIS_DOWNLOAD_MODE" + ].get(from_read=False): + print( + "This is the correct behavior as we are actually burning " + "DIS_DOWNLOAD_MODE which disables the connection to the chip" + ) + print("DIS_DOWNLOAD_MODE is enabled") + print("Successful") + sys.exit(0) # finish without errors + raise + + print("Established a connection with the chip") + if self._esp.secure_download_mode and not secure_download_mode_before: + print("Secure download mode is enabled") + if not self["ENABLE_SECURITY_DOWNLOAD"].get() and self[ + "ENABLE_SECURITY_DOWNLOAD" + ].get(from_read=False): + print( + "espefuse tool can not continue to work in Secure download mode" + ) + print("ENABLE_SECURITY_DOWNLOAD is enabled") + print("Successful") + sys.exit(0) # finish without errors + raise + + def set_efuse_timing(self): + """Set timing registers for burning efuses""" + # Configure clock + apb_freq = self.get_crystal_freq() + if apb_freq != 40: + raise esptool.FatalError( + "The eFuse supports only xtal=40M (xtal was %d)" % apb_freq + ) + + self.update_reg(self.REGS.EFUSE_DAC_CONF_REG, self.REGS.EFUSE_DAC_NUM_M, 0xFF) + self.update_reg( + self.REGS.EFUSE_DAC_CONF_REG, self.REGS.EFUSE_DAC_CLK_DIV_M, 0x28 + ) + self.update_reg( + self.REGS.EFUSE_WR_TIM_CONF1_REG, self.REGS.EFUSE_PWR_ON_NUM_M, 0x3000 + ) + self.update_reg( + self.REGS.EFUSE_WR_TIM_CONF2_REG, self.REGS.EFUSE_PWR_OFF_NUM_M, 0x190 + ) + + def get_coding_scheme_warnings(self, silent=False): + """Check if the coding scheme has detected any errors.""" + old_addr_reg = 0 + reg_value = 0 + ret_fail = False + for block in self.blocks: + if block.id == 0: + words = [ + self.read_reg(self.REGS.EFUSE_RD_REPEAT_ERR0_REG + offs * 4) + for offs in range(5) + ] + block.err_bitarray.pos = 0 + for word in reversed(words): + block.err_bitarray.overwrite(BitArray("uint:32=%d" % word)) + block.num_errors = block.err_bitarray.count(True) + block.fail = block.num_errors != 0 + else: + addr_reg, err_num_mask, err_num_offs, fail_bit = self.REGS.BLOCK_ERRORS[ + block.id + ] + if err_num_mask is None or err_num_offs is None or fail_bit is None: + continue + if addr_reg != old_addr_reg: + old_addr_reg = addr_reg + reg_value = self.read_reg(addr_reg) + block.fail = reg_value & (1 << fail_bit) != 0 + block.num_errors = (reg_value >> err_num_offs) & err_num_mask + ret_fail |= block.fail + if not silent and (block.fail or block.num_errors): + print( + "Error(s) in BLOCK%d [ERRORS:%d FAIL:%d]" + % (block.id, block.num_errors, block.fail) + ) + if (self.debug or ret_fail) and not silent: + self.print_status_regs() + return ret_fail + + def summary(self): + if self["VDD_SPI_FORCE"].get() == 0: + output = "Flash voltage (VDD_SPI) determined by GPIO45 on reset " + output += "(GPIO45=High: VDD_SPI pin is powered from internal 1.8V LDO\n" + output += "GPIO45=Low or NC: VDD_SPI pin is powered directly from " + output += "VDD3P3_RTC_IO via resistor Rspi. " + output += "Typically this voltage is 3.3 V)." + elif self["VDD_SPI_XPD"].get() == 0: + output = "Flash voltage (VDD_SPI) internal regulator disabled by efuse." + elif self["VDD_SPI_TIEH"].get() == 0: + output = "Flash voltage (VDD_SPI) set to 1.8V by efuse." + else: + output = "Flash voltage (VDD_SPI) set to 3.3V by efuse." + return output + + +class EfuseField(base_fields.EfuseFieldBase): + @staticmethod + def convert(parent, efuse): + return { + "mac": EfuseMacField, + "keypurpose": EfuseKeyPurposeField, + "t_sensor": EfuseTempSensor, + "adc_tp": EfuseAdcPointCalibration, + "wafer": EfuseWafer, + }.get(efuse.class_type, EfuseField)(parent, efuse) + + +class EfuseWafer(EfuseField): + def get(self, from_read=True): + hi_bits = self.parent["WAFER_VERSION_MINOR_HI"].get(from_read) + assert self.parent["WAFER_VERSION_MINOR_HI"].bit_len == 1 + lo_bits = self.parent["WAFER_VERSION_MINOR_LO"].get(from_read) + assert self.parent["WAFER_VERSION_MINOR_LO"].bit_len == 3 + return (hi_bits << 3) + lo_bits + + def save(self, new_value): + raise esptool.FatalError("Burning %s is not supported" % self.name) + + +class EfuseTempSensor(EfuseField): + def get(self, from_read=True): + value = self.get_bitstring(from_read) + sig = -1 if value[0] else 1 + return sig * value[1:].uint * 0.1 + + +class EfuseAdcPointCalibration(EfuseField): + def get(self, from_read=True): + STEP_SIZE = 4 + value = self.get_bitstring(from_read) + sig = -1 if value[0] else 1 + return sig * value[1:].uint * STEP_SIZE + + +class EfuseMacField(EfuseField): + def check_format(self, new_value_str): + if new_value_str is None: + raise esptool.FatalError( + "Required MAC Address in AA:CD:EF:01:02:03 format!" + ) + if new_value_str.count(":") != 5: + raise esptool.FatalError( + "MAC Address needs to be a 6-byte hexadecimal format " + "separated by colons (:)!" + ) + hexad = new_value_str.replace(":", "") + if len(hexad) != 12: + raise esptool.FatalError( + "MAC Address needs to be a 6-byte hexadecimal number " + "(12 hexadecimal characters)!" + ) + # order of bytearray = b'\xaa\xcd\xef\x01\x02\x03', + bindata = binascii.unhexlify(hexad) + # unicast address check according to + # https://tools.ietf.org/html/rfc7042#section-2.1 + if esptool.util.byte(bindata, 0) & 0x01: + raise esptool.FatalError("Custom MAC must be a unicast MAC!") + return bindata + + def check(self): + errs, fail = self.parent.get_block_errors(self.block) + if errs != 0 or fail: + output = "Block%d has ERRORS:%d FAIL:%d" % (self.block, errs, fail) + else: + output = "OK" + return "(" + output + ")" + + def get(self, from_read=True): + if self.name == "CUSTOM_MAC": + mac = self.get_raw(from_read)[::-1] + else: + mac = self.get_raw(from_read) + return "%s %s" % (util.hexify(mac, ":"), self.check()) + + def save(self, new_value): + def print_field(e, new_value): + print( + " - '{}' ({}) {} -> {}".format( + e.name, e.description, e.get_bitstring(), new_value + ) + ) + + if self.name == "CUSTOM_MAC": + bitarray_mac = self.convert_to_bitstring(new_value) + print_field(self, bitarray_mac) + super(EfuseMacField, self).save(new_value) + else: + # Writing the BLOCK1 (MAC_SPI_8M_0) default MAC is not sensible, + # as it's written in the factory. + raise esptool.FatalError("Writing Factory MAC address is not supported") + + +# fmt: off +class EfuseKeyPurposeField(EfuseField): + KEY_PURPOSES = [ + ("USER", 0, None, None, "no_need_rd_protect"), # User purposes (software-only use) + ("RESERVED", 1, None, None, "no_need_rd_protect"), # Reserved + ("XTS_AES_256_KEY_1", 2, None, "Reverse", "need_rd_protect"), # XTS_AES_256_KEY_1 (flash/PSRAM encryption) + ("XTS_AES_256_KEY_2", 3, None, "Reverse", "need_rd_protect"), # XTS_AES_256_KEY_2 (flash/PSRAM encryption) + ("XTS_AES_128_KEY", 4, None, "Reverse", "need_rd_protect"), # XTS_AES_128_KEY (flash/PSRAM encryption) + ("HMAC_DOWN_ALL", 5, None, None, "need_rd_protect"), # HMAC Downstream mode + ("HMAC_DOWN_JTAG", 6, None, None, "need_rd_protect"), # JTAG soft enable key (uses HMAC Downstream mode) + ("HMAC_DOWN_DIGITAL_SIGNATURE", 7, None, None, "need_rd_protect"), # Digital Signature peripheral key (uses HMAC Downstream mode) + ("HMAC_UP", 8, None, None, "need_rd_protect"), # HMAC Upstream mode + ("SECURE_BOOT_DIGEST0", 9, "DIGEST", None, "no_need_rd_protect"), # SECURE_BOOT_DIGEST0 (Secure Boot key digest) + ("SECURE_BOOT_DIGEST1", 10, "DIGEST", None, "no_need_rd_protect"), # SECURE_BOOT_DIGEST1 (Secure Boot key digest) + ("SECURE_BOOT_DIGEST2", 11, "DIGEST", None, "no_need_rd_protect"), # SECURE_BOOT_DIGEST2 (Secure Boot key digest) + ("XTS_AES_256_KEY", -1, "VIRTUAL", None, "no_need_rd_protect"), # Virtual purpose splits to XTS_AES_256_KEY_1 and XTS_AES_256_KEY_2 + ] +# fmt: on + + KEY_PURPOSES_NAME = [name[0] for name in KEY_PURPOSES] + DIGEST_KEY_PURPOSES = [name[0] for name in KEY_PURPOSES if name[2] == "DIGEST"] + + def check_format(self, new_value_str): + # str convert to int: "XTS_AES_128_KEY" - > str(4) + # if int: 4 -> str(4) + raw_val = new_value_str + for purpose_name in self.KEY_PURPOSES: + if purpose_name[0] == new_value_str: + raw_val = str(purpose_name[1]) + break + if raw_val.isdigit(): + if int(raw_val) not in [p[1] for p in self.KEY_PURPOSES if p[1] > 0]: + raise esptool.FatalError("'%s' can not be set (value out of range)" % raw_val) + else: + raise esptool.FatalError("'%s' unknown name" % raw_val) + return raw_val + + def need_reverse(self, new_key_purpose): + for key in self.KEY_PURPOSES: + if key[0] == new_key_purpose: + return key[3] == "Reverse" + + def need_rd_protect(self, new_key_purpose): + for key in self.KEY_PURPOSES: + if key[0] == new_key_purpose: + return key[4] == "need_rd_protect" + + def get(self, from_read=True): + for p in self.KEY_PURPOSES: + if p[1] == self.get_raw(from_read): + return p[0] + return "FORBIDDEN_STATE" + + def get_name(self, raw_val): + for key in self.KEY_PURPOSES: + if key[1] == raw_val: + return key[0] + + def save(self, new_value): + raw_val = int(self.check_format(str(new_value))) + str_new_value = self.get_name(raw_val) + if self.name == "KEY_PURPOSE_5" and str_new_value.startswith("XTS_AES"): + raise esptool.FatalError(f"{self.name} can not have {str_new_value} key due to a hardware bug (please see TRM for more details)") + return super(EfuseKeyPurposeField, self).save(raw_val) diff --git a/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32s3/mem_definition.py b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32s3/mem_definition.py new file mode 100644 index 0000000..54c8871 --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32s3/mem_definition.py @@ -0,0 +1,170 @@ +# This file describes eFuses fields and registers for ESP32-S3 chip +# +# SPDX-FileCopyrightText: 2020-2022 Espressif Systems (Shanghai) CO LTD +# +# SPDX-License-Identifier: GPL-2.0-or-later + +import os + +import yaml + +from ..mem_definition_base import ( + EfuseBlocksBase, + EfuseFieldsBase, + EfuseRegistersBase, + Field, +) + + +class EfuseDefineRegisters(EfuseRegistersBase): + EFUSE_ADDR_MASK = 0x00000FFF + EFUSE_MEM_SIZE = 0x01FC + 4 + + # EFUSE registers & command/conf values + DR_REG_EFUSE_BASE = 0x60007000 + EFUSE_PGM_DATA0_REG = DR_REG_EFUSE_BASE + EFUSE_CHECK_VALUE0_REG = DR_REG_EFUSE_BASE + 0x020 + EFUSE_CLK_REG = DR_REG_EFUSE_BASE + 0x1C8 + EFUSE_CONF_REG = DR_REG_EFUSE_BASE + 0x1CC + EFUSE_STATUS_REG = DR_REG_EFUSE_BASE + 0x1D0 + EFUSE_CMD_REG = DR_REG_EFUSE_BASE + 0x1D4 + EFUSE_RD_RS_ERR0_REG = DR_REG_EFUSE_BASE + 0x1C0 + EFUSE_RD_RS_ERR1_REG = DR_REG_EFUSE_BASE + 0x1C4 + EFUSE_RD_REPEAT_ERR0_REG = DR_REG_EFUSE_BASE + 0x17C + EFUSE_RD_REPEAT_ERR1_REG = DR_REG_EFUSE_BASE + 0x180 + EFUSE_RD_REPEAT_ERR2_REG = DR_REG_EFUSE_BASE + 0x184 + EFUSE_RD_REPEAT_ERR3_REG = DR_REG_EFUSE_BASE + 0x188 + EFUSE_RD_REPEAT_ERR4_REG = DR_REG_EFUSE_BASE + 0x18C + EFUSE_DAC_CONF_REG = DR_REG_EFUSE_BASE + 0x1E8 + EFUSE_RD_TIM_CONF_REG = DR_REG_EFUSE_BASE + 0x1EC + EFUSE_WR_TIM_CONF1_REG = DR_REG_EFUSE_BASE + 0x1F4 + EFUSE_WR_TIM_CONF2_REG = DR_REG_EFUSE_BASE + 0x1F8 + EFUSE_DATE_REG = DR_REG_EFUSE_BASE + 0x1FC + EFUSE_WRITE_OP_CODE = 0x5A5A + EFUSE_READ_OP_CODE = 0x5AA5 + EFUSE_PGM_CMD_MASK = 0x3 + EFUSE_PGM_CMD = 0x2 + EFUSE_READ_CMD = 0x1 + + BLOCK_ERRORS = [ + # error_reg, err_num_mask, err_num_offs, fail_bit + (EFUSE_RD_REPEAT_ERR0_REG, None, None, None), # BLOCK0 + (EFUSE_RD_RS_ERR0_REG, 0x7, 0, 3), # MAC_SPI_8M_0 + (EFUSE_RD_RS_ERR0_REG, 0x7, 4, 7), # BLOCK_SYS_DATA + (EFUSE_RD_RS_ERR0_REG, 0x7, 8, 11), # BLOCK_USR_DATA + (EFUSE_RD_RS_ERR0_REG, 0x7, 12, 15), # BLOCK_KEY0 + (EFUSE_RD_RS_ERR0_REG, 0x7, 16, 19), # BLOCK_KEY1 + (EFUSE_RD_RS_ERR0_REG, 0x7, 20, 23), # BLOCK_KEY2 + (EFUSE_RD_RS_ERR0_REG, 0x7, 24, 27), # BLOCK_KEY3 + (EFUSE_RD_RS_ERR0_REG, 0x7, 28, 31), # BLOCK_KEY4 + (EFUSE_RD_RS_ERR1_REG, 0x7, 0, 3), # BLOCK_KEY5 + (EFUSE_RD_RS_ERR1_REG, 0x7, 4, 7), # BLOCK_SYS_DATA2 + ] + + # EFUSE_WR_TIM_CONF2_REG + EFUSE_PWR_OFF_NUM_S = 0 + EFUSE_PWR_OFF_NUM_M = 0xFFFF << EFUSE_PWR_OFF_NUM_S + + # EFUSE_WR_TIM_CONF1_REG + EFUSE_PWR_ON_NUM_S = 8 + EFUSE_PWR_ON_NUM_M = 0x0000FFFF << EFUSE_PWR_ON_NUM_S + + # EFUSE_DAC_CONF_REG + EFUSE_DAC_CLK_DIV_S = 0 + EFUSE_DAC_CLK_DIV_M = 0xFF << EFUSE_DAC_CLK_DIV_S + + # EFUSE_DAC_CONF_REG + EFUSE_DAC_NUM_S = 9 + EFUSE_DAC_NUM_M = 0xFF << EFUSE_DAC_NUM_S + + +class EfuseDefineBlocks(EfuseBlocksBase): + __base_rd_regs = EfuseDefineRegisters.DR_REG_EFUSE_BASE + __base_wr_regs = EfuseDefineRegisters.EFUSE_PGM_DATA0_REG + # List of efuse blocks + # fmt: off + BLOCKS = [ + # Name, Alias, Index, Read address, Write address, Write protect bit, Read protect bit, Len, key_purpose + ("BLOCK0", [], 0, __base_rd_regs + 0x02C, __base_wr_regs, None, None, 6, None), + ("MAC_SPI_8M_0", ["BLOCK1"], 1, __base_rd_regs + 0x044, __base_wr_regs, 20, None, 6, None), + ("BLOCK_SYS_DATA", ["BLOCK2"], 2, __base_rd_regs + 0x05C, __base_wr_regs, 21, None, 8, None), + ("BLOCK_USR_DATA", ["BLOCK3"], 3, __base_rd_regs + 0x07C, __base_wr_regs, 22, None, 8, None), + ("BLOCK_KEY0", ["BLOCK4"], 4, __base_rd_regs + 0x09C, __base_wr_regs, 23, 0, 8, "KEY_PURPOSE_0"), + ("BLOCK_KEY1", ["BLOCK5"], 5, __base_rd_regs + 0x0BC, __base_wr_regs, 24, 1, 8, "KEY_PURPOSE_1"), + ("BLOCK_KEY2", ["BLOCK6"], 6, __base_rd_regs + 0x0DC, __base_wr_regs, 25, 2, 8, "KEY_PURPOSE_2"), + ("BLOCK_KEY3", ["BLOCK7"], 7, __base_rd_regs + 0x0FC, __base_wr_regs, 26, 3, 8, "KEY_PURPOSE_3"), + ("BLOCK_KEY4", ["BLOCK8"], 8, __base_rd_regs + 0x11C, __base_wr_regs, 27, 4, 8, "KEY_PURPOSE_4"), + ("BLOCK_KEY5", ["BLOCK9"], 9, __base_rd_regs + 0x13C, __base_wr_regs, 28, 5, 8, "KEY_PURPOSE_5"), + ("BLOCK_SYS_DATA2", ["BLOCK10"], 10, __base_rd_regs + 0x15C, __base_wr_regs, 29, 6, 8, None), + ] + # fmt: on + + def get_burn_block_data_names(self): + list_of_names = [] + for block in self.BLOCKS: + blk = self.get(block) + if blk.name: + list_of_names.append(blk.name) + if blk.alias: + for alias in blk.alias: + list_of_names.append(alias) + return list_of_names + + +class EfuseDefineFields(EfuseFieldsBase): + def __init__(self) -> None: + # List of efuse fields from TRM the chapter eFuse Controller. + self.EFUSES = [] + + self.KEYBLOCKS = [] + + # if BLK_VERSION_MAJOR is 1, these efuse fields are in BLOCK2 + self.BLOCK2_CALIBRATION_EFUSES = [] + + self.CALC = [] + + dir_name = os.path.dirname(os.path.abspath(__file__)) + dir_name, file_name = os.path.split(dir_name) + file_name = file_name + ".yaml" + dir_name, _ = os.path.split(dir_name) + efuse_file = os.path.join(dir_name, "efuse_defs", file_name) + with open(f"{efuse_file}", "r") as r_file: + e_desc = yaml.safe_load(r_file) + super().__init__(e_desc) + + for i, efuse in enumerate(self.ALL_EFUSES): + if efuse.name in [ + "BLOCK_USR_DATA", + "BLOCK_KEY0", + "BLOCK_KEY1", + "BLOCK_KEY2", + "BLOCK_KEY3", + "BLOCK_KEY4", + "BLOCK_KEY5", + "BLOCK_SYS_DATA2", + ]: + if efuse.name == "BLOCK_USR_DATA": + efuse.bit_len = 256 + efuse.type = "bytes:32" + self.KEYBLOCKS.append(efuse) + self.ALL_EFUSES[i] = None + + elif efuse.category == "calibration": + self.BLOCK2_CALIBRATION_EFUSES.append(efuse) + self.ALL_EFUSES[i] = None + + f = Field() + f.name = "WAFER_VERSION_MINOR" + f.block = 0 + f.bit_len = 4 + f.type = f"uint:{f.bit_len}" + f.category = "identity" + f.class_type = "wafer" + f.description = "calc WAFER VERSION MINOR = WAFER_VERSION_MINOR_HI << 3 + WAFER_VERSION_MINOR_LO (read only)" + self.CALC.append(f) + + for efuse in self.ALL_EFUSES: + if efuse is not None: + self.EFUSES.append(efuse) + + self.ALL_EFUSES = [] diff --git a/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32s3/operations.py b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32s3/operations.py new file mode 100644 index 0000000..9331374 --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32s3/operations.py @@ -0,0 +1,531 @@ +# This file includes the operations with eFuses for ESP32-S3 chip +# +# SPDX-FileCopyrightText: 2020-2022 Espressif Systems (Shanghai) CO LTD +# +# SPDX-License-Identifier: GPL-2.0-or-later + +import argparse +import io +import os # noqa: F401. It is used in IDF scripts +import traceback + +import espsecure + +import esptool + +from . import fields +from .. import util +from ..base_operations import ( + add_common_commands, + add_force_write_always, + add_show_sensitive_info_option, + burn_bit, + burn_block_data, + burn_efuse, + check_error, + dump, + read_protect_efuse, + summary, + write_protect_efuse, +) + + +def protect_options(p): + p.add_argument( + "--no-write-protect", + help="Disable write-protecting of the key. The key remains writable. " + "(The keys use the RS coding scheme that does not support post-write " + "data changes. Forced write can damage RS encoding bits.) " + "The write-protecting of keypurposes does not depend on the option, " + "it will be set anyway.", + action="store_true", + ) + p.add_argument( + "--no-read-protect", + help="Disable read-protecting of the key. The key remains readable software." + "The key with keypurpose[USER, RESERVED and *_DIGEST] " + "will remain readable anyway. " + "For the rest keypurposes the read-protection will be defined the option " + "(Read-protect by default).", + action="store_true", + ) + + +def add_commands(subparsers, efuses): + add_common_commands(subparsers, efuses) + burn_key = subparsers.add_parser( + "burn_key", help="Burn the key block with the specified name" + ) + protect_options(burn_key) + add_force_write_always(burn_key) + add_show_sensitive_info_option(burn_key) + burn_key.add_argument( + "block", + help="Key block to burn", + action="append", + choices=efuses.BLOCKS_FOR_KEYS, + ) + burn_key.add_argument( + "keyfile", + help="File containing 256 bits of binary key data", + action="append", + type=argparse.FileType("rb"), + ) + burn_key.add_argument( + "keypurpose", + help="Purpose to set.", + action="append", + choices=fields.EfuseKeyPurposeField.KEY_PURPOSES_NAME, + ) + for _ in efuses.BLOCKS_FOR_KEYS: + burn_key.add_argument( + "block", + help="Key block to burn", + nargs="?", + action="append", + metavar="BLOCK", + choices=efuses.BLOCKS_FOR_KEYS, + ) + burn_key.add_argument( + "keyfile", + help="File containing 256 bits of binary key data", + nargs="?", + action="append", + metavar="KEYFILE", + type=argparse.FileType("rb"), + ) + burn_key.add_argument( + "keypurpose", + help="Purpose to set.", + nargs="?", + action="append", + metavar="KEYPURPOSE", + choices=fields.EfuseKeyPurposeField.KEY_PURPOSES_NAME, + ) + + burn_key_digest = subparsers.add_parser( + "burn_key_digest", + help="Parse a RSA public key and burn the digest to key efuse block", + ) + protect_options(burn_key_digest) + add_force_write_always(burn_key_digest) + add_show_sensitive_info_option(burn_key_digest) + burn_key_digest.add_argument( + "block", + help="Key block to burn", + action="append", + choices=efuses.BLOCKS_FOR_KEYS, + ) + burn_key_digest.add_argument( + "keyfile", + help="Key file to digest (PEM format)", + action="append", + type=argparse.FileType("rb"), + ) + burn_key_digest.add_argument( + "keypurpose", + help="Purpose to set.", + action="append", + choices=fields.EfuseKeyPurposeField.DIGEST_KEY_PURPOSES, + ) + for _ in efuses.BLOCKS_FOR_KEYS: + burn_key_digest.add_argument( + "block", + help="Key block to burn", + nargs="?", + action="append", + metavar="BLOCK", + choices=efuses.BLOCKS_FOR_KEYS, + ) + burn_key_digest.add_argument( + "keyfile", + help="Key file to digest (PEM format)", + nargs="?", + action="append", + metavar="KEYFILE", + type=argparse.FileType("rb"), + ) + burn_key_digest.add_argument( + "keypurpose", + help="Purpose to set.", + nargs="?", + action="append", + metavar="KEYPURPOSE", + choices=fields.EfuseKeyPurposeField.DIGEST_KEY_PURPOSES, + ) + + p = subparsers.add_parser( + "set_flash_voltage", + help="Permanently set the internal flash voltage regulator " + "to either 1.8V, 3.3V or OFF. This means GPIO45 can be high or low at reset " + "without changing the flash voltage.", + ) + p.add_argument("voltage", help="Voltage selection", choices=["1.8V", "3.3V", "OFF"]) + + p = subparsers.add_parser( + "burn_custom_mac", help="Burn a 48-bit Custom MAC Address to EFUSE BLOCK3." + ) + p.add_argument( + "mac", + help="Custom MAC Address to burn given in hexadecimal format with " + "bytes separated by colons (e.g. AA:CD:EF:01:02:03).", + type=fields.base_fields.CheckArgValue(efuses, "CUSTOM_MAC"), + ) + add_force_write_always(p) + + p = subparsers.add_parser("get_custom_mac", help="Prints the Custom MAC Address.") + + +def burn_custom_mac(esp, efuses, args): + efuses["CUSTOM_MAC"].save(args.mac) + if not efuses.burn_all(check_batch_mode=True): + return + get_custom_mac(esp, efuses, args) + print("Successful") + + +def get_custom_mac(esp, efuses, args): + print("Custom MAC Address: {}".format(efuses["CUSTOM_MAC"].get())) + + +def set_flash_voltage(esp, efuses, args): + sdio_force = efuses["VDD_SPI_FORCE"] + sdio_tieh = efuses["VDD_SPI_TIEH"] + sdio_reg = efuses["VDD_SPI_XPD"] + + # check efuses aren't burned in a way which makes this impossible + if args.voltage == "OFF" and sdio_reg.get() != 0: + raise esptool.FatalError( + "Can't set flash regulator to OFF as VDD_SPI_XPD efuse is already burned" + ) + + if args.voltage == "1.8V" and sdio_tieh.get() != 0: + raise esptool.FatalError( + "Can't set regulator to 1.8V is VDD_SPI_TIEH efuse is already burned" + ) + + if args.voltage == "OFF": + msg = "Disable internal flash voltage regulator (VDD_SPI). " + "SPI flash will need to be powered from an external source.\n" + "The following efuse is burned: VDD_SPI_FORCE.\n" + "It is possible to later re-enable the internal regulator (%s) " % ( + "to 3.3V" if sdio_tieh.get() != 0 else "to 1.8V or 3.3V" + ) + "by burning an additional efuse" + elif args.voltage == "1.8V": + msg = "Set internal flash voltage regulator (VDD_SPI) to 1.8V.\n" + "The following efuses are burned: VDD_SPI_FORCE, VDD_SPI_XPD.\n" + "It is possible to later increase the voltage to 3.3V (permanently) " + "by burning additional efuse VDD_SPI_TIEH" + elif args.voltage == "3.3V": + msg = "Enable internal flash voltage regulator (VDD_SPI) to 3.3V.\n" + "The following efuses are burned: VDD_SPI_FORCE, VDD_SPI_XPD, VDD_SPI_TIEH." + print(msg) + + sdio_force.save(1) # Disable GPIO45 + if args.voltage != "OFF": + sdio_reg.save(1) # Enable internal regulator + if args.voltage == "3.3V": + sdio_tieh.save(1) + print("VDD_SPI setting complete.") + if not efuses.burn_all(check_batch_mode=True): + return + print("Successful") + + +def adc_info(esp, efuses, args): + print("") + # fmt: off + if efuses["BLK_VERSION_MAJOR"].get() == 1: + print("Temperature Sensor Calibration = {}C".format(efuses["TEMP_SENSOR_CAL"].get())) + + print("") + print("ADC1 readings stored in efuse BLOCK2:") + print(" MODE0 D1 reading (250mV): {}".format(efuses["ADC1_MODE0_D1"].get())) + print(" MODE0 D2 reading (600mV): {}".format(efuses["ADC1_MODE0_D2"].get())) + + print(" MODE1 D1 reading (250mV): {}".format(efuses["ADC1_MODE1_D1"].get())) + print(" MODE1 D2 reading (800mV): {}".format(efuses["ADC1_MODE1_D2"].get())) + + print(" MODE2 D1 reading (250mV): {}".format(efuses["ADC1_MODE2_D1"].get())) + print(" MODE2 D2 reading (1000mV): {}".format(efuses["ADC1_MODE2_D2"].get())) + + print(" MODE3 D1 reading (250mV): {}".format(efuses["ADC1_MODE3_D1"].get())) + print(" MODE3 D2 reading (2000mV): {}".format(efuses["ADC1_MODE3_D2"].get())) + + print("") + print("ADC2 readings stored in efuse BLOCK2:") + print(" MODE0 D1 reading (250mV): {}".format(efuses["ADC2_MODE0_D1"].get())) + print(" MODE0 D2 reading (600mV): {}".format(efuses["ADC2_MODE0_D2"].get())) + + print(" MODE1 D1 reading (250mV): {}".format(efuses["ADC2_MODE1_D1"].get())) + print(" MODE1 D2 reading (800mV): {}".format(efuses["ADC2_MODE1_D2"].get())) + + print(" MODE2 D1 reading (250mV): {}".format(efuses["ADC2_MODE2_D1"].get())) + print(" MODE2 D2 reading (1000mV): {}".format(efuses["ADC2_MODE2_D2"].get())) + + print(" MODE3 D1 reading (250mV): {}".format(efuses["ADC2_MODE3_D1"].get())) + print(" MODE3 D2 reading (2000mV): {}".format(efuses["ADC2_MODE3_D2"].get())) + else: + print("BLK_VERSION_MAJOR = {}".format(efuses["BLK_VERSION_MAJOR"].get_meaning())) + # fmt: on + + +def key_block_is_unused(block, key_purpose_block): + if not block.is_readable() or not block.is_writeable(): + return False + + if key_purpose_block.get() != "USER" or not key_purpose_block.is_writeable(): + return False + + if not block.get_bitstring().all(False): + return False + + return True + + +def get_next_key_block(efuses, current_key_block, block_name_list): + key_blocks = [b for b in efuses.blocks if b.key_purpose_name] + start = key_blocks.index(current_key_block) + + # Sort key blocks so that we pick the next free block (and loop around if necessary) + key_blocks = key_blocks[start:] + key_blocks[0:start] + + # Exclude any other blocks that will be be burned + key_blocks = [b for b in key_blocks if b.name not in block_name_list] + + for block in key_blocks: + key_purpose_block = efuses[block.key_purpose_name] + if key_block_is_unused(block, key_purpose_block): + return block + + return None + + +def split_512_bit_key(efuses, block_name_list, datafile_list, keypurpose_list): + i = keypurpose_list.index("XTS_AES_256_KEY") + block_name = block_name_list[i] + + block_num = efuses.get_index_block_by_name(block_name) + block = efuses.blocks[block_num] + + data = datafile_list[i].read() + if len(data) != 64: + raise esptool.FatalError( + "Incorrect key file size %d, XTS_AES_256_KEY should be 64 bytes" % len(data) + ) + + key_block_2 = get_next_key_block(efuses, block, block_name_list) + if not key_block_2: + raise esptool.FatalError("XTS_AES_256_KEY requires two free keyblocks") + + keypurpose_list.append("XTS_AES_256_KEY_1") + datafile_list.append(io.BytesIO(data[:32])) + block_name_list.append(block_name) + + keypurpose_list.append("XTS_AES_256_KEY_2") + datafile_list.append(io.BytesIO(data[32:])) + block_name_list.append(key_block_2.name) + + keypurpose_list.pop(i) + datafile_list.pop(i) + block_name_list.pop(i) + + +def burn_key(esp, efuses, args, digest=None): + if digest is None: + datafile_list = args.keyfile[ + 0 : len([name for name in args.keyfile if name is not None]) : + ] + else: + datafile_list = digest[0 : len([name for name in digest if name is not None]) :] + efuses.force_write_always = args.force_write_always + block_name_list = args.block[ + 0 : len([name for name in args.block if name is not None]) : + ] + keypurpose_list = args.keypurpose[ + 0 : len([name for name in args.keypurpose if name is not None]) : + ] + + if "XTS_AES_256_KEY" in keypurpose_list: + # XTS_AES_256_KEY is not an actual HW key purpose, needs to be split into + # XTS_AES_256_KEY_1 and XTS_AES_256_KEY_2 + split_512_bit_key(efuses, block_name_list, datafile_list, keypurpose_list) + + util.check_duplicate_name_in_list(block_name_list) + if len(block_name_list) != len(datafile_list) or len(block_name_list) != len( + keypurpose_list + ): + raise esptool.FatalError( + "The number of blocks (%d), datafile (%d) and keypurpose (%d) " + "should be the same." + % (len(block_name_list), len(datafile_list), len(keypurpose_list)) + ) + + print("Burn keys to blocks:") + for block_name, datafile, keypurpose in zip( + block_name_list, datafile_list, keypurpose_list + ): + efuse = None + for block in efuses.blocks: + if block_name == block.name or block_name in block.alias: + efuse = efuses[block.name] + if efuse is None: + raise esptool.FatalError("Unknown block name - %s" % (block_name)) + num_bytes = efuse.bit_len // 8 + + block_num = efuses.get_index_block_by_name(block_name) + block = efuses.blocks[block_num] + + if digest is None: + data = datafile.read() + else: + data = datafile + + print(" - %s" % (efuse.name), end=" ") + revers_msg = None + if efuses[block.key_purpose_name].need_reverse(keypurpose): + revers_msg = "\tReversing byte order for AES-XTS hardware peripheral" + data = data[::-1] + print( + "-> [{}]".format( + util.hexify(data, " ") + if args.show_sensitive_info + else " ".join(["??"] * len(data)) + ) + ) + if revers_msg: + print(revers_msg) + if len(data) != num_bytes: + raise esptool.FatalError( + "Incorrect key file size %d. Key file must be %d bytes (%d bits) " + "of raw binary key data." % (len(data), num_bytes, num_bytes * 8) + ) + + if efuses[block.key_purpose_name].need_rd_protect(keypurpose): + read_protect = False if args.no_read_protect else True + else: + read_protect = False + write_protect = not args.no_write_protect + + # using efuse instead of a block gives the advantage of + # checking it as the whole field. + efuse.save(data) + + disable_wr_protect_key_purpose = False + if efuses[block.key_purpose_name].get() != keypurpose: + if efuses[block.key_purpose_name].is_writeable(): + print( + "\t'%s': '%s' -> '%s'." + % ( + block.key_purpose_name, + efuses[block.key_purpose_name].get(), + keypurpose, + ) + ) + efuses[block.key_purpose_name].save(keypurpose) + disable_wr_protect_key_purpose = True + else: + raise esptool.FatalError( + "It is not possible to change '%s' to '%s' because " + "write protection bit is set." + % (block.key_purpose_name, keypurpose) + ) + else: + print("\t'%s' is already '%s'." % (block.key_purpose_name, keypurpose)) + if efuses[block.key_purpose_name].is_writeable(): + disable_wr_protect_key_purpose = True + + if disable_wr_protect_key_purpose: + print("\tDisabling write to '%s'." % block.key_purpose_name) + efuses[block.key_purpose_name].disable_write() + + if read_protect: + print("\tDisabling read to key block") + efuse.disable_read() + + if write_protect: + print("\tDisabling write to key block") + efuse.disable_write() + print("") + + if not write_protect: + print("Keys will remain writeable (due to --no-write-protect)") + if args.no_read_protect: + print("Keys will remain readable (due to --no-read-protect)") + + if not efuses.burn_all(check_batch_mode=True): + return + print("Successful") + + +def burn_key_digest(esp, efuses, args): + digest_list = [] + datafile_list = args.keyfile[ + 0 : len([name for name in args.keyfile if name is not None]) : + ] + block_list = args.block[ + 0 : len([block for block in args.block if block is not None]) : + ] + for block_name, datafile in zip(block_list, datafile_list): + efuse = None + for block in efuses.blocks: + if block_name == block.name or block_name in block.alias: + efuse = efuses[block.name] + if efuse is None: + raise esptool.FatalError("Unknown block name - %s" % (block_name)) + num_bytes = efuse.bit_len // 8 + digest = espsecure._digest_sbv2_public_key(datafile) + if len(digest) != num_bytes: + raise esptool.FatalError( + "Incorrect digest size %d. Digest must be %d bytes (%d bits) " + "of raw binary key data." % (len(digest), num_bytes, num_bytes * 8) + ) + digest_list.append(digest) + burn_key(esp, efuses, args, digest=digest_list) + + +def espefuse(esp, efuses, args, command): + parser = argparse.ArgumentParser() + subparsers = parser.add_subparsers(dest="operation") + add_commands(subparsers, efuses) + try: + cmd_line_args = parser.parse_args(command.split()) + except SystemExit: + traceback.print_stack() + raise esptool.FatalError('"{}" - incorrect command'.format(command)) + if cmd_line_args.operation == "execute_scripts": + configfiles = cmd_line_args.configfiles + index = cmd_line_args.index + # copy arguments from args to cmd_line_args + vars(cmd_line_args).update(vars(args)) + if cmd_line_args.operation == "execute_scripts": + cmd_line_args.configfiles = configfiles + cmd_line_args.index = index + if cmd_line_args.operation is None: + parser.print_help() + parser.exit(1) + operation_func = globals()[cmd_line_args.operation] + # each 'operation' is a module-level function of the same name + operation_func(esp, efuses, cmd_line_args) + + +def execute_scripts(esp, efuses, args): + efuses.batch_mode_cnt += 1 + del args.operation + scripts = args.scripts + del args.scripts + + for file in scripts: + with open(file.name, "r") as file: + exec(compile(file.read(), file.name, "exec")) + + if args.debug: + for block in efuses.blocks: + data = block.get_bitstring(from_read=False) + block.print_block(data, "regs_for_burn", args.debug) + + efuses.batch_mode_cnt -= 1 + if not efuses.burn_all(check_batch_mode=True): + return + print("Successful") diff --git a/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32s3beta2/__init__.py b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32s3beta2/__init__.py new file mode 100644 index 0000000..a3b55a8 --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32s3beta2/__init__.py @@ -0,0 +1,3 @@ +from . import operations +from .emulate_efuse_controller import EmulateEfuseController +from .fields import EspEfuses diff --git a/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32s3beta2/__pycache__/__init__.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32s3beta2/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..2f2e771 Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32s3beta2/__pycache__/__init__.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32s3beta2/__pycache__/emulate_efuse_controller.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32s3beta2/__pycache__/emulate_efuse_controller.cpython-312.pyc new file mode 100644 index 0000000..2c5ed55 Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32s3beta2/__pycache__/emulate_efuse_controller.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32s3beta2/__pycache__/fields.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32s3beta2/__pycache__/fields.cpython-312.pyc new file mode 100644 index 0000000..f0bba9e Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32s3beta2/__pycache__/fields.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32s3beta2/__pycache__/mem_definition.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32s3beta2/__pycache__/mem_definition.cpython-312.pyc new file mode 100644 index 0000000..d6260d5 Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32s3beta2/__pycache__/mem_definition.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32s3beta2/__pycache__/operations.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32s3beta2/__pycache__/operations.cpython-312.pyc new file mode 100644 index 0000000..fda59d7 Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32s3beta2/__pycache__/operations.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32s3beta2/emulate_efuse_controller.py b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32s3beta2/emulate_efuse_controller.py new file mode 100644 index 0000000..0d81c08 --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32s3beta2/emulate_efuse_controller.py @@ -0,0 +1,92 @@ +# This file describes eFuses controller for ESP32-S3(beta2) chip +# +# SPDX-FileCopyrightText: 2020-2022 Espressif Systems (Shanghai) CO LTD +# +# SPDX-License-Identifier: GPL-2.0-or-later + +import reedsolo + +from .mem_definition import EfuseDefineBlocks, EfuseDefineFields, EfuseDefineRegisters +from ..emulate_efuse_controller_base import EmulateEfuseControllerBase, FatalError + + +class EmulateEfuseController(EmulateEfuseControllerBase): + """The class for virtual efuse operation. Using for HOST_TEST.""" + + CHIP_NAME = "ESP32-S3(beta2)" + mem = None + debug = False + + def __init__(self, efuse_file=None, debug=False): + self.Blocks = EfuseDefineBlocks + self.Fields = EfuseDefineFields() + self.REGS = EfuseDefineRegisters + super(EmulateEfuseController, self).__init__(efuse_file, debug) + self.write_reg(self.REGS.EFUSE_CMD_REG, 0) + + """ esptool method start >>""" + + def get_major_chip_version(self): + return 0 + + def get_minor_chip_version(self): + return 2 + + def get_crystal_freq(self): + return 40 # MHz (common for all chips) + + def get_security_info(self): + return { + "flags": 0, + "flash_crypt_cnt": 0, + "key_purposes": 0, + "chip_id": 0, + "api_version": 0, + } + + """ << esptool method end """ + + def handle_writing_event(self, addr, value): + if addr == self.REGS.EFUSE_CMD_REG: + if value & self.REGS.EFUSE_PGM_CMD: + self.copy_blocks_wr_regs_to_rd_regs(updated_block=(value >> 2) & 0xF) + self.clean_blocks_wr_regs() + self.check_rd_protection_area() + self.write_reg(addr, 0) + self.write_reg(self.REGS.EFUSE_CMD_REG, 0) + elif value == self.REGS.EFUSE_READ_CMD: + self.write_reg(addr, 0) + self.write_reg(self.REGS.EFUSE_CMD_REG, 0) + self.save_to_file() + + def get_bitlen_of_block(self, blk, wr=False): + if blk.id == 0: + if wr: + return 32 * 8 + else: + return 32 * blk.len + else: + if wr: + rs_coding = 32 * 3 + return 32 * 8 + rs_coding + else: + return 32 * blk.len + + def handle_coding_scheme(self, blk, data): + if blk.id != 0: + # CODING_SCHEME RS applied only for all blocks except BLK0. + coded_bytes = 12 + data.pos = coded_bytes * 8 + plain_data = data.readlist("32*uint:8")[::-1] + # takes 32 bytes + # apply RS encoding + rs = reedsolo.RSCodec(coded_bytes) + # 32 byte of data + 12 bytes RS + calc_encoded_data = list(rs.encode([x for x in plain_data])) + data.pos = 0 + if calc_encoded_data != data.readlist("44*uint:8")[::-1]: + raise FatalError("Error in coding scheme data") + data = data[coded_bytes * 8 :] + if blk.len < 8: + data = data[(8 - blk.len) * 32 :] + return data diff --git a/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32s3beta2/fields.py b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32s3beta2/fields.py new file mode 100644 index 0000000..0622350 --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32s3beta2/fields.py @@ -0,0 +1,460 @@ +# This file describes eFuses for ESP32-S3 chip +# +# SPDX-FileCopyrightText: 2020-2022 Espressif Systems (Shanghai) CO LTD +# +# SPDX-License-Identifier: GPL-2.0-or-later + +import binascii +import struct +import sys +import time + +from bitstring import BitArray + +import esptool + +import reedsolo + +from .mem_definition import EfuseDefineBlocks, EfuseDefineFields, EfuseDefineRegisters +from .. import base_fields +from .. import util + + +class EfuseBlock(base_fields.EfuseBlockBase): + def len_of_burn_unit(self): + # The writing register window is 8 registers for any blocks. + # len in bytes + return 8 * 4 + + def __init__(self, parent, param, skip_read=False): + parent.read_coding_scheme() + super(EfuseBlock, self).__init__(parent, param, skip_read=skip_read) + + def apply_coding_scheme(self): + data = self.get_raw(from_read=False)[::-1] + if len(data) < self.len_of_burn_unit(): + add_empty_bytes = self.len_of_burn_unit() - len(data) + data = data + (b"\x00" * add_empty_bytes) + if self.get_coding_scheme() == self.parent.REGS.CODING_SCHEME_RS: + # takes 32 bytes + # apply RS encoding + rs = reedsolo.RSCodec(12) + # 32 byte of data + 12 bytes RS + encoded_data = rs.encode([x for x in data]) + words = struct.unpack("<" + "I" * 11, encoded_data) + # returns 11 words (8 words of data + 3 words of RS coding) + else: + # takes 32 bytes + words = struct.unpack("<" + ("I" * (len(data) // 4)), data) + # returns 8 words + return words + + +class EspEfuses(base_fields.EspEfusesBase): + """ + Wrapper object to manage the efuse fields in a connected ESP bootloader + """ + + debug = False + do_not_confirm = False + + def __init__(self, esp, skip_connect=False, debug=False, do_not_confirm=False): + self.Blocks = EfuseDefineBlocks() + self.Fields = EfuseDefineFields() + self.REGS = EfuseDefineRegisters + self.BURN_BLOCK_DATA_NAMES = self.Blocks.get_burn_block_data_names() + self.BLOCKS_FOR_KEYS = self.Blocks.get_blocks_for_keys() + self._esp = esp + self.debug = debug + self.do_not_confirm = do_not_confirm + if esp.CHIP_NAME != "ESP32-S3(beta2)": + raise esptool.FatalError( + "Expected the 'esp' param for ESP32-S3(beta2) chip but got for '%s'." + % (esp.CHIP_NAME) + ) + if not skip_connect: + flags = self._esp.get_security_info()["flags"] + GET_SECURITY_INFO_FLAG_SECURE_DOWNLOAD_ENABLE = 1 << 2 + if flags & GET_SECURITY_INFO_FLAG_SECURE_DOWNLOAD_ENABLE: + raise esptool.FatalError( + "Secure Download Mode is enabled. The tool can not read eFuses." + ) + self.blocks = [ + EfuseBlock(self, self.Blocks.get(block), skip_read=skip_connect) + for block in self.Blocks.BLOCKS + ] + if not skip_connect: + self.get_coding_scheme_warnings() + self.efuses = [EfuseField.convert(self, efuse) for efuse in self.Fields.EFUSES] + self.efuses += [ + EfuseField.convert(self, efuse) for efuse in self.Fields.KEYBLOCKS + ] + if skip_connect: + self.efuses += [ + EfuseField.convert(self, efuse) + for efuse in self.Fields.BLOCK2_CALIBRATION_EFUSES + ] + else: + if self["BLK_VERSION_MAJOR"].get() == 1: + self.efuses += [ + EfuseField.convert(self, efuse) + for efuse in self.Fields.BLOCK2_CALIBRATION_EFUSES + ] + self.efuses += [ + EfuseField.convert(self, efuse) for efuse in self.Fields.CALC + ] + + def __getitem__(self, efuse_name): + """Return the efuse field with the given name""" + for e in self.efuses: + if efuse_name == e.name or any(x == efuse_name for x in e.alt_names): + return e + new_fields = False + for efuse in self.Fields.BLOCK2_CALIBRATION_EFUSES: + if efuse.name == efuse_name or any( + x == efuse_name for x in efuse.alt_names + ): + self.efuses += [ + EfuseField.convert(self, efuse) + for efuse in self.Fields.BLOCK2_CALIBRATION_EFUSES + ] + new_fields = True + if new_fields: + for e in self.efuses: + if efuse_name == e.name or any(x == efuse_name for x in e.alt_names): + return e + raise KeyError + + def read_coding_scheme(self): + self.coding_scheme = self.REGS.CODING_SCHEME_RS + + def print_status_regs(self): + print("") + self.blocks[0].print_block(self.blocks[0].err_bitarray, "err__regs", debug=True) + print( + "{:27} 0x{:08x}".format( + "EFUSE_RD_RS_ERR0_REG", self.read_reg(self.REGS.EFUSE_RD_RS_ERR0_REG) + ) + ) + print( + "{:27} 0x{:08x}".format( + "EFUSE_RD_RS_ERR1_REG", self.read_reg(self.REGS.EFUSE_RD_RS_ERR1_REG) + ) + ) + + def efuse_controller_setup(self): + self.set_efuse_timing() + self.clear_pgm_registers() + self.wait_efuse_idle() + + def write_efuses(self, block): + self.efuse_program(block) + return self.get_coding_scheme_warnings(silent=True) + + def clear_pgm_registers(self): + self.wait_efuse_idle() + for r in range( + self.REGS.EFUSE_PGM_DATA0_REG, self.REGS.EFUSE_PGM_DATA0_REG + 32, 4 + ): + self.write_reg(r, 0) + + def wait_efuse_idle(self): + deadline = time.time() + self.REGS.EFUSE_BURN_TIMEOUT + while time.time() < deadline: + cmds = self.REGS.EFUSE_PGM_CMD | self.REGS.EFUSE_READ_CMD + if self.read_reg(self.REGS.EFUSE_CMD_REG) & cmds == 0: + if self.read_reg(self.REGS.EFUSE_CMD_REG) & cmds == 0: + # Due to a hardware error, we have to read READ_CMD again + # to make sure the efuse clock is normal. + # For PGM_CMD it is not necessary. + return + raise esptool.FatalError( + "Timed out waiting for Efuse controller command to complete" + ) + + def efuse_program(self, block): + self.wait_efuse_idle() + self.write_reg(self.REGS.EFUSE_CONF_REG, self.REGS.EFUSE_WRITE_OP_CODE) + self.write_reg(self.REGS.EFUSE_CMD_REG, self.REGS.EFUSE_PGM_CMD | (block << 2)) + self.wait_efuse_idle() + self.clear_pgm_registers() + self.efuse_read() + + def efuse_read(self): + self.wait_efuse_idle() + self.write_reg(self.REGS.EFUSE_CONF_REG, self.REGS.EFUSE_READ_OP_CODE) + # need to add a delay after triggering EFUSE_READ_CMD, as ROM loader checks some + # efuse registers after each command is completed + # if ENABLE_SECURITY_DOWNLOAD or DIS_DOWNLOAD_MODE is enabled by the current cmd, then we need to try to reconnect to the chip. + try: + self.write_reg( + self.REGS.EFUSE_CMD_REG, self.REGS.EFUSE_READ_CMD, delay_after_us=1000 + ) + self.wait_efuse_idle() + except esptool.FatalError: + secure_download_mode_before = self._esp.secure_download_mode + + try: + self._esp = self.reconnect_chip(self._esp) + except esptool.FatalError: + print("Can not re-connect to the chip") + if not self["DIS_DOWNLOAD_MODE"].get() and self[ + "DIS_DOWNLOAD_MODE" + ].get(from_read=False): + print( + "This is the correct behavior as we are actually burning " + "DIS_DOWNLOAD_MODE which disables the connection to the chip" + ) + print("DIS_DOWNLOAD_MODE is enabled") + print("Successful") + sys.exit(0) # finish without errors + raise + + print("Established a connection with the chip") + if self._esp.secure_download_mode and not secure_download_mode_before: + print("Secure download mode is enabled") + if not self["ENABLE_SECURITY_DOWNLOAD"].get() and self[ + "ENABLE_SECURITY_DOWNLOAD" + ].get(from_read=False): + print( + "espefuse tool can not continue to work in Secure download mode" + ) + print("ENABLE_SECURITY_DOWNLOAD is enabled") + print("Successful") + sys.exit(0) # finish without errors + raise + + def set_efuse_timing(self): + """Set timing registers for burning efuses""" + # Configure clock + apb_freq = self.get_crystal_freq() + if apb_freq != 40: + raise esptool.FatalError( + "The eFuse supports only xtal=40M (xtal was %d)" % apb_freq + ) + + self.update_reg(self.REGS.EFUSE_DAC_CONF_REG, self.REGS.EFUSE_DAC_NUM_M, 0xFF) + self.update_reg( + self.REGS.EFUSE_DAC_CONF_REG, self.REGS.EFUSE_DAC_CLK_DIV_M, 0x28 + ) + self.update_reg( + self.REGS.EFUSE_WR_TIM_CONF1_REG, self.REGS.EFUSE_PWR_ON_NUM_M, 0x3000 + ) + self.update_reg( + self.REGS.EFUSE_WR_TIM_CONF2_REG, self.REGS.EFUSE_PWR_OFF_NUM_M, 0x190 + ) + + def get_coding_scheme_warnings(self, silent=False): + """Check if the coding scheme has detected any errors.""" + old_addr_reg = 0 + reg_value = 0 + ret_fail = False + for block in self.blocks: + if block.id == 0: + words = [ + self.read_reg(self.REGS.EFUSE_RD_REPEAT_ERR0_REG + offs * 4) + for offs in range(5) + ] + block.err_bitarray.pos = 0 + for word in reversed(words): + block.err_bitarray.overwrite(BitArray("uint:32=%d" % word)) + block.num_errors = block.err_bitarray.count(True) + block.fail = block.num_errors != 0 + else: + addr_reg, err_num_mask, err_num_offs, fail_bit = self.REGS.BLOCK_ERRORS[ + block.id + ] + if err_num_mask is None or err_num_offs is None or fail_bit is None: + continue + if addr_reg != old_addr_reg: + old_addr_reg = addr_reg + reg_value = self.read_reg(addr_reg) + block.fail = reg_value & (1 << fail_bit) != 0 + block.num_errors = (reg_value >> err_num_offs) & err_num_mask + ret_fail |= block.fail + if not silent and (block.fail or block.num_errors): + print( + "Error(s) in BLOCK%d [ERRORS:%d FAIL:%d]" + % (block.id, block.num_errors, block.fail) + ) + if (self.debug or ret_fail) and not silent: + self.print_status_regs() + return ret_fail + + def summary(self): + if self["VDD_SPI_FORCE"].get() == 0: + output = "Flash voltage (VDD_SPI) determined by GPIO45 on reset " + output += "(GPIO45=High: VDD_SPI pin is powered from internal 1.8V LDO\n" + output += "GPIO45=Low or NC: VDD_SPI pin is powered directly from " + output += "VDD3P3_RTC_IO via resistor Rspi. " + output += "Typically this voltage is 3.3 V)." + elif self["VDD_SPI_XPD"].get() == 0: + output = "Flash voltage (VDD_SPI) internal regulator disabled by efuse." + elif self["VDD_SPI_TIEH"].get() == 0: + output = "Flash voltage (VDD_SPI) set to 1.8V by efuse." + else: + output = "Flash voltage (VDD_SPI) set to 3.3V by efuse." + return output + + +class EfuseField(base_fields.EfuseFieldBase): + @staticmethod + def convert(parent, efuse): + return { + "mac": EfuseMacField, + "keypurpose": EfuseKeyPurposeField, + "t_sensor": EfuseTempSensor, + "adc_tp": EfuseAdcPointCalibration, + "wafer": EfuseWafer, + }.get(efuse.class_type, EfuseField)(parent, efuse) + + +class EfuseWafer(EfuseField): + def get(self, from_read=True): + hi_bits = self.parent["WAFER_VERSION_MINOR_HI"].get(from_read) + assert self.parent["WAFER_VERSION_MINOR_HI"].bit_len == 1 + lo_bits = self.parent["WAFER_VERSION_MINOR_LO"].get(from_read) + assert self.parent["WAFER_VERSION_MINOR_LO"].bit_len == 3 + return (hi_bits << 3) + lo_bits + + def save(self, new_value): + raise esptool.FatalError("Burning %s is not supported" % self.name) + + +class EfuseTempSensor(EfuseField): + def get(self, from_read=True): + value = self.get_bitstring(from_read) + sig = -1 if value[0] else 1 + return sig * value[1:].uint * 0.1 + + +class EfuseAdcPointCalibration(EfuseField): + def get(self, from_read=True): + STEP_SIZE = 4 + value = self.get_bitstring(from_read) + sig = -1 if value[0] else 1 + return sig * value[1:].uint * STEP_SIZE + + +class EfuseMacField(EfuseField): + def check_format(self, new_value_str): + if new_value_str is None: + raise esptool.FatalError( + "Required MAC Address in AA:CD:EF:01:02:03 format!" + ) + if new_value_str.count(":") != 5: + raise esptool.FatalError( + "MAC Address needs to be a 6-byte hexadecimal format " + "separated by colons (:)!" + ) + hexad = new_value_str.replace(":", "") + if len(hexad) != 12: + raise esptool.FatalError( + "MAC Address needs to be a 6-byte hexadecimal number " + "(12 hexadecimal characters)!" + ) + # order of bytearray = b'\xaa\xcd\xef\x01\x02\x03', + bindata = binascii.unhexlify(hexad) + # unicast address check according to + # https://tools.ietf.org/html/rfc7042#section-2.1 + if esptool.util.byte(bindata, 0) & 0x01: + raise esptool.FatalError("Custom MAC must be a unicast MAC!") + return bindata + + def check(self): + errs, fail = self.parent.get_block_errors(self.block) + if errs != 0 or fail: + output = "Block%d has ERRORS:%d FAIL:%d" % (self.block, errs, fail) + else: + output = "OK" + return "(" + output + ")" + + def get(self, from_read=True): + if self.name == "CUSTOM_MAC": + mac = self.get_raw(from_read)[::-1] + else: + mac = self.get_raw(from_read) + return "%s %s" % (util.hexify(mac, ":"), self.check()) + + def save(self, new_value): + def print_field(e, new_value): + print( + " - '{}' ({}) {} -> {}".format( + e.name, e.description, e.get_bitstring(), new_value + ) + ) + + if self.name == "CUSTOM_MAC": + bitarray_mac = self.convert_to_bitstring(new_value) + print_field(self, bitarray_mac) + super(EfuseMacField, self).save(new_value) + else: + # Writing the BLOCK1 (MAC_SPI_8M_0) default MAC is not possible, + # as it's written in the factory. + raise esptool.FatalError("Writing Factory MAC address is not supported") + + +# fmt: off +class EfuseKeyPurposeField(EfuseField): + KEY_PURPOSES = [ + ("USER", 0, None, None, "no_need_rd_protect"), # User purposes (software-only use) + ("RESERVED", 1, None, None, "no_need_rd_protect"), # Reserved + ("XTS_AES_256_KEY_1", 2, None, "Reverse", "need_rd_protect"), # XTS_AES_256_KEY_1 (flash/PSRAM encryption) + ("XTS_AES_256_KEY_2", 3, None, "Reverse", "need_rd_protect"), # XTS_AES_256_KEY_2 (flash/PSRAM encryption) + ("XTS_AES_128_KEY", 4, None, "Reverse", "need_rd_protect"), # XTS_AES_128_KEY (flash/PSRAM encryption) + ("HMAC_DOWN_ALL", 5, None, None, "need_rd_protect"), # HMAC Downstream mode + ("HMAC_DOWN_JTAG", 6, None, None, "need_rd_protect"), # JTAG soft enable key (uses HMAC Downstream mode) + ("HMAC_DOWN_DIGITAL_SIGNATURE", 7, None, None, "need_rd_protect"), # Digital Signature peripheral key (uses HMAC Downstream mode) + ("HMAC_UP", 8, None, None, "need_rd_protect"), # HMAC Upstream mode + ("SECURE_BOOT_DIGEST0", 9, "DIGEST", None, "no_need_rd_protect"), # SECURE_BOOT_DIGEST0 (Secure Boot key digest) + ("SECURE_BOOT_DIGEST1", 10, "DIGEST", None, "no_need_rd_protect"), # SECURE_BOOT_DIGEST1 (Secure Boot key digest) + ("SECURE_BOOT_DIGEST2", 11, "DIGEST", None, "no_need_rd_protect"), # SECURE_BOOT_DIGEST2 (Secure Boot key digest) + ("XTS_AES_256_KEY", -1, "VIRTUAL", None, "no_need_rd_protect"), # Virtual purpose splits to XTS_AES_256_KEY_1 and XTS_AES_256_KEY_2 + ] +# fmt: on + + KEY_PURPOSES_NAME = [name[0] for name in KEY_PURPOSES] + DIGEST_KEY_PURPOSES = [name[0] for name in KEY_PURPOSES if name[2] == "DIGEST"] + + def check_format(self, new_value_str): + # str convert to int: "XTS_AES_128_KEY" - > str(4) + # if int: 4 -> str(4) + raw_val = new_value_str + for purpose_name in self.KEY_PURPOSES: + if purpose_name[0] == new_value_str: + raw_val = str(purpose_name[1]) + break + if raw_val.isdigit(): + if int(raw_val) not in [p[1] for p in self.KEY_PURPOSES if p[1] > 0]: + raise esptool.FatalError("'%s' can not be set (value out of range)" % raw_val) + else: + raise esptool.FatalError("'%s' unknown name" % raw_val) + return raw_val + + def need_reverse(self, new_key_purpose): + for key in self.KEY_PURPOSES: + if key[0] == new_key_purpose: + return key[3] == "Reverse" + + def need_rd_protect(self, new_key_purpose): + for key in self.KEY_PURPOSES: + if key[0] == new_key_purpose: + return key[4] == "need_rd_protect" + + def get(self, from_read=True): + for p in self.KEY_PURPOSES: + if p[1] == self.get_raw(from_read): + return p[0] + return "FORBIDDEN_STATE" + + def get_name(self, raw_val): + for key in self.KEY_PURPOSES: + if key[1] == raw_val: + return key[0] + + def save(self, new_value): + raw_val = int(self.check_format(str(new_value))) + str_new_value = self.get_name(raw_val) + if self.name == "KEY_PURPOSE_5" and str_new_value.startswith("XTS_AES"): + raise esptool.FatalError(f"{self.name} can not have {str_new_value} key due to a hardware bug (please see TRM for more details)") + return super(EfuseKeyPurposeField, self).save(raw_val) diff --git a/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32s3beta2/mem_definition.py b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32s3beta2/mem_definition.py new file mode 100644 index 0000000..eabf767 --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32s3beta2/mem_definition.py @@ -0,0 +1,171 @@ +# This file describes eFuses fields and registers for ESP32-S3(beta2) chip +# +# SPDX-FileCopyrightText: 2020-2022 Espressif Systems (Shanghai) CO LTD +# +# SPDX-License-Identifier: GPL-2.0-or-later + +import os + +import yaml + +from ..mem_definition_base import ( + EfuseBlocksBase, + EfuseFieldsBase, + EfuseRegistersBase, + Field, +) + + +class EfuseDefineRegisters(EfuseRegistersBase): + EFUSE_ADDR_MASK = 0x00000FFF + EFUSE_MEM_SIZE = 0x01FC + 4 + + # EFUSE registers & command/conf values + DR_REG_EFUSE_BASE = 0x6001A000 + EFUSE_PGM_DATA0_REG = DR_REG_EFUSE_BASE + EFUSE_CHECK_VALUE0_REG = DR_REG_EFUSE_BASE + 0x020 + EFUSE_CLK_REG = DR_REG_EFUSE_BASE + 0x1C8 + EFUSE_CONF_REG = DR_REG_EFUSE_BASE + 0x1CC + EFUSE_STATUS_REG = DR_REG_EFUSE_BASE + 0x1D0 + EFUSE_CMD_REG = DR_REG_EFUSE_BASE + 0x1D4 + EFUSE_RD_RS_ERR0_REG = DR_REG_EFUSE_BASE + 0x1C0 + EFUSE_RD_RS_ERR1_REG = DR_REG_EFUSE_BASE + 0x1C4 + EFUSE_RD_REPEAT_ERR0_REG = DR_REG_EFUSE_BASE + 0x17C + EFUSE_RD_REPEAT_ERR1_REG = DR_REG_EFUSE_BASE + 0x180 + EFUSE_RD_REPEAT_ERR2_REG = DR_REG_EFUSE_BASE + 0x184 + EFUSE_RD_REPEAT_ERR3_REG = DR_REG_EFUSE_BASE + 0x188 + EFUSE_RD_REPEAT_ERR4_REG = DR_REG_EFUSE_BASE + 0x18C + EFUSE_DAC_CONF_REG = DR_REG_EFUSE_BASE + 0x1E8 + EFUSE_RD_TIM_CONF_REG = DR_REG_EFUSE_BASE + 0x1EC + EFUSE_WR_TIM_CONF1_REG = DR_REG_EFUSE_BASE + 0x1F4 + EFUSE_WR_TIM_CONF2_REG = DR_REG_EFUSE_BASE + 0x1F8 + EFUSE_DATE_REG = DR_REG_EFUSE_BASE + 0x1FC + EFUSE_WRITE_OP_CODE = 0x5A5A + EFUSE_READ_OP_CODE = 0x5AA5 + EFUSE_PGM_CMD_MASK = 0x3 + EFUSE_PGM_CMD = 0x2 + EFUSE_READ_CMD = 0x1 + + BLOCK_ERRORS = [ + # error_reg, err_num_mask, err_num_offs, fail_bit + (EFUSE_RD_REPEAT_ERR0_REG, None, None, None), # BLOCK0 + (EFUSE_RD_RS_ERR0_REG, 0x7, 0, 3), # MAC_SPI_8M_0 + (EFUSE_RD_RS_ERR0_REG, 0x7, 4, 7), # BLOCK_SYS_DATA + (EFUSE_RD_RS_ERR0_REG, 0x7, 8, 11), # BLOCK_USR_DATA + (EFUSE_RD_RS_ERR0_REG, 0x7, 12, 15), # BLOCK_KEY0 + (EFUSE_RD_RS_ERR0_REG, 0x7, 16, 19), # BLOCK_KEY1 + (EFUSE_RD_RS_ERR0_REG, 0x7, 20, 23), # BLOCK_KEY2 + (EFUSE_RD_RS_ERR0_REG, 0x7, 24, 27), # BLOCK_KEY3 + (EFUSE_RD_RS_ERR0_REG, 0x7, 28, 31), # BLOCK_KEY4 + (EFUSE_RD_RS_ERR1_REG, 0x7, 0, 3), # BLOCK_KEY5 + (EFUSE_RD_RS_ERR1_REG, 0x7, 4, 7), # BLOCK_SYS_DATA2 + ] + + # EFUSE_WR_TIM_CONF2_REG + EFUSE_PWR_OFF_NUM_S = 0 + EFUSE_PWR_OFF_NUM_M = 0xFFFF << EFUSE_PWR_OFF_NUM_S + + # EFUSE_WR_TIM_CONF1_REG + EFUSE_PWR_ON_NUM_S = 8 + EFUSE_PWR_ON_NUM_M = 0x0000FFFF << EFUSE_PWR_ON_NUM_S + + # EFUSE_DAC_CONF_REG + EFUSE_DAC_CLK_DIV_S = 0 + EFUSE_DAC_CLK_DIV_M = 0xFF << EFUSE_DAC_CLK_DIV_S + + # EFUSE_DAC_CONF_REG + EFUSE_DAC_NUM_S = 9 + EFUSE_DAC_NUM_M = 0xFF << EFUSE_DAC_NUM_S + + +class EfuseDefineBlocks(EfuseBlocksBase): + __base_rd_regs = EfuseDefineRegisters.DR_REG_EFUSE_BASE + __base_wr_regs = EfuseDefineRegisters.EFUSE_PGM_DATA0_REG + # List of efuse blocks + # fmt: off + BLOCKS = [ + # Name, Alias, Index, Read address, Write address, Write protect bit, Read protect bit, Len, key_purpose + ("BLOCK0", [], 0, __base_rd_regs + 0x02C, __base_wr_regs, None, None, 6, None), + ("MAC_SPI_8M_0", ["BLOCK1"], 1, __base_rd_regs + 0x044, __base_wr_regs, 20, None, 6, None), + ("BLOCK_SYS_DATA", ["BLOCK2"], 2, __base_rd_regs + 0x05C, __base_wr_regs, 21, None, 8, None), + ("BLOCK_USR_DATA", ["BLOCK3"], 3, __base_rd_regs + 0x07C, __base_wr_regs, 22, None, 8, None), + ("BLOCK_KEY0", ["BLOCK4"], 4, __base_rd_regs + 0x09C, __base_wr_regs, 23, 0, 8, "KEY_PURPOSE_0"), + ("BLOCK_KEY1", ["BLOCK5"], 5, __base_rd_regs + 0x0BC, __base_wr_regs, 24, 1, 8, "KEY_PURPOSE_1"), + ("BLOCK_KEY2", ["BLOCK6"], 6, __base_rd_regs + 0x0DC, __base_wr_regs, 25, 2, 8, "KEY_PURPOSE_2"), + ("BLOCK_KEY3", ["BLOCK7"], 7, __base_rd_regs + 0x0FC, __base_wr_regs, 26, 3, 8, "KEY_PURPOSE_3"), + ("BLOCK_KEY4", ["BLOCK8"], 8, __base_rd_regs + 0x11C, __base_wr_regs, 27, 4, 8, "KEY_PURPOSE_4"), + ("BLOCK_KEY5", ["BLOCK9"], 9, __base_rd_regs + 0x13C, __base_wr_regs, 28, 5, 8, "KEY_PURPOSE_5"), + ("BLOCK_SYS_DATA2", ["BLOCK10"], 10, __base_rd_regs + 0x15C, __base_wr_regs, 29, 6, 8, None), + ] + # fmt: on + + def get_burn_block_data_names(self): + list_of_names = [] + for block in self.BLOCKS: + blk = self.get(block) + if blk.name: + list_of_names.append(blk.name) + if blk.alias: + for alias in blk.alias: + list_of_names.append(alias) + return list_of_names + + +class EfuseDefineFields(EfuseFieldsBase): + def __init__(self) -> None: + # List of efuse fields from TRM the chapter eFuse Controller. + self.EFUSES = [] + + self.KEYBLOCKS = [] + + # if BLK_VERSION_MAJOR is 1, these efuse fields are in BLOCK2 + self.BLOCK2_CALIBRATION_EFUSES = [] + + self.CALC = [] + + dir_name = os.path.dirname(os.path.abspath(__file__)) + dir_name, file_name = os.path.split(dir_name) + file_name = file_name + ".yaml" + dir_name, _ = os.path.split(dir_name) + efuse_file = os.path.join(dir_name, "efuse_defs", file_name) + efuse_file = efuse_file.replace("esp32s3beta2", "esp32s3") + with open(f"{efuse_file}", "r") as r_file: + e_desc = yaml.safe_load(r_file) + super().__init__(e_desc) + + for i, efuse in enumerate(self.ALL_EFUSES): + if efuse.name in [ + "BLOCK_USR_DATA", + "BLOCK_KEY0", + "BLOCK_KEY1", + "BLOCK_KEY2", + "BLOCK_KEY3", + "BLOCK_KEY4", + "BLOCK_KEY5", + "BLOCK_SYS_DATA2", + ]: + if efuse.name == "BLOCK_USR_DATA": + efuse.bit_len = 256 + efuse.type = "bytes:32" + self.KEYBLOCKS.append(efuse) + self.ALL_EFUSES[i] = None + + elif efuse.category == "calibration": + self.BLOCK2_CALIBRATION_EFUSES.append(efuse) + self.ALL_EFUSES[i] = None + + f = Field() + f.name = "WAFER_VERSION_MINOR" + f.block = 0 + f.bit_len = 4 + f.type = f"uint:{f.bit_len}" + f.category = "identity" + f.class_type = "wafer" + f.description = "calc WAFER VERSION MINOR = WAFER_VERSION_MINOR_HI << 3 + WAFER_VERSION_MINOR_LO (read only)" + self.CALC.append(f) + + for efuse in self.ALL_EFUSES: + if efuse is not None: + self.EFUSES.append(efuse) + + self.ALL_EFUSES = [] diff --git a/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32s3beta2/operations.py b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32s3beta2/operations.py new file mode 100644 index 0000000..e17d773 --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/esp32s3beta2/operations.py @@ -0,0 +1,531 @@ +# This file includes the operations with eFuses for ESP32-S3(beta2) chip +# +# SPDX-FileCopyrightText: 2020-2022 Espressif Systems (Shanghai) CO LTD +# +# SPDX-License-Identifier: GPL-2.0-or-later + +import argparse +import io +import os # noqa: F401. It is used in IDF scripts +import traceback + +import espsecure + +import esptool + +from . import fields +from .. import util +from ..base_operations import ( + add_common_commands, + add_force_write_always, + add_show_sensitive_info_option, + burn_bit, + burn_block_data, + burn_efuse, + check_error, + dump, + read_protect_efuse, + summary, + write_protect_efuse, +) + + +def protect_options(p): + p.add_argument( + "--no-write-protect", + help="Disable write-protecting of the key. The key remains writable. " + "(The keys use the RS coding scheme that does not support post-write " + "data changes. Forced write can damage RS encoding bits.) " + "The write-protecting of keypurposes does not depend on the option, " + "it will be set anyway.", + action="store_true", + ) + p.add_argument( + "--no-read-protect", + help="Disable read-protecting of the key. The key remains readable software." + "The key with keypurpose[USER, RESERVED and *_DIGEST] " + "will remain readable anyway. " + "For the rest keypurposes the read-protection will be defined the option " + "(Read-protect by default).", + action="store_true", + ) + + +def add_commands(subparsers, efuses): + add_common_commands(subparsers, efuses) + burn_key = subparsers.add_parser( + "burn_key", help="Burn the key block with the specified name" + ) + protect_options(burn_key) + add_force_write_always(burn_key) + add_show_sensitive_info_option(burn_key) + burn_key.add_argument( + "block", + help="Key block to burn", + action="append", + choices=efuses.BLOCKS_FOR_KEYS, + ) + burn_key.add_argument( + "keyfile", + help="File containing 256 bits of binary key data", + action="append", + type=argparse.FileType("rb"), + ) + burn_key.add_argument( + "keypurpose", + help="Purpose to set.", + action="append", + choices=fields.EfuseKeyPurposeField.KEY_PURPOSES_NAME, + ) + for _ in efuses.BLOCKS_FOR_KEYS: + burn_key.add_argument( + "block", + help="Key block to burn", + nargs="?", + action="append", + metavar="BLOCK", + choices=efuses.BLOCKS_FOR_KEYS, + ) + burn_key.add_argument( + "keyfile", + help="File containing 256 bits of binary key data", + nargs="?", + action="append", + metavar="KEYFILE", + type=argparse.FileType("rb"), + ) + burn_key.add_argument( + "keypurpose", + help="Purpose to set.", + nargs="?", + action="append", + metavar="KEYPURPOSE", + choices=fields.EfuseKeyPurposeField.KEY_PURPOSES_NAME, + ) + + burn_key_digest = subparsers.add_parser( + "burn_key_digest", + help="Parse a RSA public key and burn the digest to key efuse block", + ) + protect_options(burn_key_digest) + add_force_write_always(burn_key_digest) + add_show_sensitive_info_option(burn_key_digest) + burn_key_digest.add_argument( + "block", + help="Key block to burn", + action="append", + choices=efuses.BLOCKS_FOR_KEYS, + ) + burn_key_digest.add_argument( + "keyfile", + help="Key file to digest (PEM format)", + action="append", + type=argparse.FileType("rb"), + ) + burn_key_digest.add_argument( + "keypurpose", + help="Purpose to set.", + action="append", + choices=fields.EfuseKeyPurposeField.DIGEST_KEY_PURPOSES, + ) + for _ in efuses.BLOCKS_FOR_KEYS: + burn_key_digest.add_argument( + "block", + help="Key block to burn", + nargs="?", + action="append", + metavar="BLOCK", + choices=efuses.BLOCKS_FOR_KEYS, + ) + burn_key_digest.add_argument( + "keyfile", + help="Key file to digest (PEM format)", + nargs="?", + action="append", + metavar="KEYFILE", + type=argparse.FileType("rb"), + ) + burn_key_digest.add_argument( + "keypurpose", + help="Purpose to set.", + nargs="?", + action="append", + metavar="KEYPURPOSE", + choices=fields.EfuseKeyPurposeField.DIGEST_KEY_PURPOSES, + ) + + p = subparsers.add_parser( + "set_flash_voltage", + help="Permanently set the internal flash voltage regulator " + "to either 1.8V, 3.3V or OFF. This means GPIO45 can be high or low at reset " + "without changing the flash voltage.", + ) + p.add_argument("voltage", help="Voltage selection", choices=["1.8V", "3.3V", "OFF"]) + + p = subparsers.add_parser( + "burn_custom_mac", help="Burn a 48-bit Custom MAC Address to EFUSE BLOCK3." + ) + p.add_argument( + "mac", + help="Custom MAC Address to burn given in hexadecimal format with bytes " + "separated by colons (e.g. AA:CD:EF:01:02:03).", + type=fields.base_fields.CheckArgValue(efuses, "CUSTOM_MAC"), + ) + add_force_write_always(p) + + p = subparsers.add_parser("get_custom_mac", help="Prints the Custom MAC Address.") + + +def burn_custom_mac(esp, efuses, args): + efuses["CUSTOM_MAC"].save(args.mac) + if not efuses.burn_all(check_batch_mode=True): + return + get_custom_mac(esp, efuses, args) + print("Successful") + + +def get_custom_mac(esp, efuses, args): + print("Custom MAC Address: {}".format(efuses["CUSTOM_MAC"].get())) + + +def set_flash_voltage(esp, efuses, args): + sdio_force = efuses["VDD_SPI_FORCE"] + sdio_tieh = efuses["VDD_SPI_TIEH"] + sdio_reg = efuses["VDD_SPI_XPD"] + + # check efuses aren't burned in a way which makes this impossible + if args.voltage == "OFF" and sdio_reg.get() != 0: + raise esptool.FatalError( + "Can't set flash regulator to OFF as VDD_SPI_XPD efuse is already burned" + ) + + if args.voltage == "1.8V" and sdio_tieh.get() != 0: + raise esptool.FatalError( + "Can't set regulator to 1.8V is VDD_SPI_TIEH efuse is already burned" + ) + + if args.voltage == "OFF": + msg = "Disable internal flash voltage regulator (VDD_SPI). " + "SPI flash will need to be powered from an external source.\n" + "The following efuse is burned: VDD_SPI_FORCE.\n" + "It is possible to later re-enable the internal regulator (%s) " % ( + "to 3.3V" if sdio_tieh.get() != 0 else "to 1.8V or 3.3V" + ) + "by burning an additional efuse" + elif args.voltage == "1.8V": + msg = "Set internal flash voltage regulator (VDD_SPI) to 1.8V.\n" + "The following efuses are burned: VDD_SPI_FORCE, VDD_SPI_XPD.\n" + "It is possible to later increase the voltage to 3.3V (permanently) " + "by burning additional efuse VDD_SPI_TIEH" + elif args.voltage == "3.3V": + msg = "Enable internal flash voltage regulator (VDD_SPI) to 3.3V.\n" + "The following efuses are burned: VDD_SPI_FORCE, VDD_SPI_XPD, VDD_SPI_TIEH." + print(msg) + + sdio_force.save(1) # Disable GPIO45 + if args.voltage != "OFF": + sdio_reg.save(1) # Enable internal regulator + if args.voltage == "3.3V": + sdio_tieh.save(1) + print("VDD_SPI setting complete.") + if not efuses.burn_all(check_batch_mode=True): + return + print("Successful") + + +def adc_info(esp, efuses, args): + print("") + # fmt: off + if efuses["BLK_VERSION_MAJOR"].get() == 1: + print("Temperature Sensor Calibration = {}C".format(efuses["TEMP_SENSOR_CAL"].get())) + + print("") + print("ADC1 readings stored in efuse BLOCK2:") + print(" MODE0 D1 reading (250mV): {}".format(efuses["ADC1_MODE0_D1"].get())) + print(" MODE0 D2 reading (600mV): {}".format(efuses["ADC1_MODE0_D2"].get())) + + print(" MODE1 D1 reading (250mV): {}".format(efuses["ADC1_MODE1_D1"].get())) + print(" MODE1 D2 reading (800mV): {}".format(efuses["ADC1_MODE1_D2"].get())) + + print(" MODE2 D1 reading (250mV): {}".format(efuses["ADC1_MODE2_D1"].get())) + print(" MODE2 D2 reading (1000mV): {}".format(efuses["ADC1_MODE2_D2"].get())) + + print(" MODE3 D1 reading (250mV): {}".format(efuses["ADC1_MODE3_D1"].get())) + print(" MODE3 D2 reading (2000mV): {}".format(efuses["ADC1_MODE3_D2"].get())) + + print("") + print("ADC2 readings stored in efuse BLOCK2:") + print(" MODE0 D1 reading (250mV): {}".format(efuses["ADC2_MODE0_D1"].get())) + print(" MODE0 D2 reading (600mV): {}".format(efuses["ADC2_MODE0_D2"].get())) + + print(" MODE1 D1 reading (250mV): {}".format(efuses["ADC2_MODE1_D1"].get())) + print(" MODE1 D2 reading (800mV): {}".format(efuses["ADC2_MODE1_D2"].get())) + + print(" MODE2 D1 reading (250mV): {}".format(efuses["ADC2_MODE2_D1"].get())) + print(" MODE2 D2 reading (1000mV): {}".format(efuses["ADC2_MODE2_D2"].get())) + + print(" MODE3 D1 reading (250mV): {}".format(efuses["ADC2_MODE3_D1"].get())) + print(" MODE3 D2 reading (2000mV): {}".format(efuses["ADC2_MODE3_D2"].get())) + else: + print("BLK_VERSION_MAJOR = {}".format(efuses["BLK_VERSION_MAJOR"].get_meaning())) + # fmt: on + + +def key_block_is_unused(block, key_purpose_block): + if not block.is_readable() or not block.is_writeable(): + return False + + if key_purpose_block.get() != "USER" or not key_purpose_block.is_writeable(): + return False + + if not block.get_bitstring().all(False): + return False + + return True + + +def get_next_key_block(efuses, current_key_block, block_name_list): + key_blocks = [b for b in efuses.blocks if b.key_purpose_name] + start = key_blocks.index(current_key_block) + + # Sort key blocks so that we pick the next free block (and loop around if necessary) + key_blocks = key_blocks[start:] + key_blocks[0:start] + + # Exclude any other blocks that will be be burned + key_blocks = [b for b in key_blocks if b.name not in block_name_list] + + for block in key_blocks: + key_purpose_block = efuses[block.key_purpose_name] + if key_block_is_unused(block, key_purpose_block): + return block + + return None + + +def split_512_bit_key(efuses, block_name_list, datafile_list, keypurpose_list): + i = keypurpose_list.index("XTS_AES_256_KEY") + block_name = block_name_list[i] + + block_num = efuses.get_index_block_by_name(block_name) + block = efuses.blocks[block_num] + + data = datafile_list[i].read() + if len(data) != 64: + raise esptool.FatalError( + "Incorrect key file size %d, XTS_AES_256_KEY should be 64 bytes" % len(data) + ) + + key_block_2 = get_next_key_block(efuses, block, block_name_list) + if not key_block_2: + raise esptool.FatalError("XTS_AES_256_KEY requires two free keyblocks") + + keypurpose_list.append("XTS_AES_256_KEY_1") + datafile_list.append(io.BytesIO(data[:32])) + block_name_list.append(block_name) + + keypurpose_list.append("XTS_AES_256_KEY_2") + datafile_list.append(io.BytesIO(data[32:])) + block_name_list.append(key_block_2.name) + + keypurpose_list.pop(i) + datafile_list.pop(i) + block_name_list.pop(i) + + +def burn_key(esp, efuses, args, digest=None): + if digest is None: + datafile_list = args.keyfile[ + 0 : len([name for name in args.keyfile if name is not None]) : + ] + else: + datafile_list = digest[0 : len([name for name in digest if name is not None]) :] + efuses.force_write_always = args.force_write_always + block_name_list = args.block[ + 0 : len([name for name in args.block if name is not None]) : + ] + keypurpose_list = args.keypurpose[ + 0 : len([name for name in args.keypurpose if name is not None]) : + ] + + if "XTS_AES_256_KEY" in keypurpose_list: + # XTS_AES_256_KEY is not an actual HW key purpose, needs to be split into + # XTS_AES_256_KEY_1 and XTS_AES_256_KEY_2 + split_512_bit_key(efuses, block_name_list, datafile_list, keypurpose_list) + + util.check_duplicate_name_in_list(block_name_list) + if len(block_name_list) != len(datafile_list) or len(block_name_list) != len( + keypurpose_list + ): + raise esptool.FatalError( + "The number of blocks (%d), datafile (%d) and keypurpose (%d) " + "should be the same." + % (len(block_name_list), len(datafile_list), len(keypurpose_list)) + ) + + print("Burn keys to blocks:") + for block_name, datafile, keypurpose in zip( + block_name_list, datafile_list, keypurpose_list + ): + efuse = None + for block in efuses.blocks: + if block_name == block.name or block_name in block.alias: + efuse = efuses[block.name] + if efuse is None: + raise esptool.FatalError("Unknown block name - %s" % (block_name)) + num_bytes = efuse.bit_len // 8 + + block_num = efuses.get_index_block_by_name(block_name) + block = efuses.blocks[block_num] + + if digest is None: + data = datafile.read() + else: + data = datafile + + print(" - %s" % (efuse.name), end=" ") + revers_msg = None + if efuses[block.key_purpose_name].need_reverse(keypurpose): + revers_msg = "\tReversing byte order for AES-XTS hardware peripheral" + data = data[::-1] + print( + "-> [{}]".format( + util.hexify(data, " ") + if args.show_sensitive_info + else " ".join(["??"] * len(data)) + ) + ) + if revers_msg: + print(revers_msg) + if len(data) != num_bytes: + raise esptool.FatalError( + "Incorrect key file size %d. Key file must be %d bytes (%d bits) " + "of raw binary key data." % (len(data), num_bytes, num_bytes * 8) + ) + + if efuses[block.key_purpose_name].need_rd_protect(keypurpose): + read_protect = False if args.no_read_protect else True + else: + read_protect = False + write_protect = not args.no_write_protect + + # using efuse instead of a block gives the advantage of + # checking it as the whole field. + efuse.save(data) + + disable_wr_protect_key_purpose = False + if efuses[block.key_purpose_name].get() != keypurpose: + if efuses[block.key_purpose_name].is_writeable(): + print( + "\t'%s': '%s' -> '%s'." + % ( + block.key_purpose_name, + efuses[block.key_purpose_name].get(), + keypurpose, + ) + ) + efuses[block.key_purpose_name].save(keypurpose) + disable_wr_protect_key_purpose = True + else: + raise esptool.FatalError( + "It is not possible to change '%s' to '%s' because " + "write protection bit is set." + % (block.key_purpose_name, keypurpose) + ) + else: + print("\t'%s' is already '%s'." % (block.key_purpose_name, keypurpose)) + if efuses[block.key_purpose_name].is_writeable(): + disable_wr_protect_key_purpose = True + + if disable_wr_protect_key_purpose: + print("\tDisabling write to '%s'." % block.key_purpose_name) + efuses[block.key_purpose_name].disable_write() + + if read_protect: + print("\tDisabling read to key block") + efuse.disable_read() + + if write_protect: + print("\tDisabling write to key block") + efuse.disable_write() + print("") + + if not write_protect: + print("Keys will remain writeable (due to --no-write-protect)") + if args.no_read_protect: + print("Keys will remain readable (due to --no-read-protect)") + + if not efuses.burn_all(check_batch_mode=True): + return + print("Successful") + + +def burn_key_digest(esp, efuses, args): + digest_list = [] + datafile_list = args.keyfile[ + 0 : len([name for name in args.keyfile if name is not None]) : + ] + block_list = args.block[ + 0 : len([block for block in args.block if block is not None]) : + ] + for block_name, datafile in zip(block_list, datafile_list): + efuse = None + for block in efuses.blocks: + if block_name == block.name or block_name in block.alias: + efuse = efuses[block.name] + if efuse is None: + raise esptool.FatalError("Unknown block name - %s" % (block_name)) + num_bytes = efuse.bit_len // 8 + digest = espsecure._digest_sbv2_public_key(datafile) + if len(digest) != num_bytes: + raise esptool.FatalError( + "Incorrect digest size %d. Digest must be %d bytes (%d bits) " + "of raw binary key data." % (len(digest), num_bytes, num_bytes * 8) + ) + digest_list.append(digest) + burn_key(esp, efuses, args, digest=digest_list) + + +def espefuse(esp, efuses, args, command): + parser = argparse.ArgumentParser() + subparsers = parser.add_subparsers(dest="operation") + add_commands(subparsers, efuses) + try: + cmd_line_args = parser.parse_args(command.split()) + except SystemExit: + traceback.print_stack() + raise esptool.FatalError('"{}" - incorrect command'.format(command)) + if cmd_line_args.operation == "execute_scripts": + configfiles = cmd_line_args.configfiles + index = cmd_line_args.index + # copy arguments from args to cmd_line_args + vars(cmd_line_args).update(vars(args)) + if cmd_line_args.operation == "execute_scripts": + cmd_line_args.configfiles = configfiles + cmd_line_args.index = index + if cmd_line_args.operation is None: + parser.print_help() + parser.exit(1) + operation_func = globals()[cmd_line_args.operation] + # each 'operation' is a module-level function of the same name + operation_func(esp, efuses, cmd_line_args) + + +def execute_scripts(esp, efuses, args): + efuses.batch_mode_cnt += 1 + del args.operation + scripts = args.scripts + del args.scripts + + for file in scripts: + with open(file.name, "r") as file: + exec(compile(file.read(), file.name, "exec")) + + if args.debug: + for block in efuses.blocks: + data = block.get_bitstring(from_read=False) + block.print_block(data, "regs_for_burn", args.debug) + + efuses.batch_mode_cnt -= 1 + if not efuses.burn_all(check_batch_mode=True): + return + print("Successful") diff --git a/code/.venv/lib/python3.12/site-packages/espefuse/efuse/mem_definition_base.py b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/mem_definition_base.py new file mode 100644 index 0000000..21ae698 --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/mem_definition_base.py @@ -0,0 +1,171 @@ +# This file describes eFuses fields and registers for ESP32 chip +# +# SPDX-FileCopyrightText: 2020-2022 Espressif Systems (Shanghai) CO LTD +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from collections import namedtuple + + +class EfuseRegistersBase(object): + # Coding Scheme values + CODING_SCHEME_NONE = 0 + CODING_SCHEME_34 = 1 + CODING_SCHEME_REPEAT = 2 + CODING_SCHEME_NONE_RECOVERY = 3 + CODING_SCHEME_RS = 4 + + EFUSE_BURN_TIMEOUT = 0.250 # seconds + + +class EfuseBlocksBase(object): + BLOCKS = None + NamedtupleBlock = namedtuple( + "Block", + "name alias id rd_addr wr_addr write_disable_bit " + "read_disable_bit len key_purpose", + ) + + @staticmethod + def get(tuple_block): + return EfuseBlocksBase.NamedtupleBlock._make(tuple_block) + + def get_blocks_for_keys(self): + list_of_names = [] + for block in self.BLOCKS: + blk = self.get(block) + if blk.id > 0: + if blk.name: + list_of_names.append(blk.name) + if blk.alias: + for alias in blk.alias: + list_of_names.append(alias) + return list_of_names + + +class Field: + name = "" + block = 0 + word = None + pos = None + bit_len = 0 + alt_names = [] + type = "" + write_disable_bit = None + read_disable_bit = None + category = "config" + class_type = "" + description = "" + dictionary = None + + +class EfuseFieldsBase(object): + def __init__(self, e_desc) -> None: + self.ALL_EFUSES = [] + + def set_category_and_class_type(efuse, name): + def includes(name, names): + return any([word in name for word in names]) + + if name.startswith("SPI_PAD_CONFIG"): + efuse.category = "spi pad" + + elif "USB" in name: + efuse.category = "usb" + + elif "WDT" in name: + efuse.category = "wdt" + + elif "JTAG" in name: + efuse.category = "jtag" + + elif includes(name, ["FLASH", "FORCE_SEND_RESUME"]): + efuse.category = "flash" + + elif includes(name, ["VDD_SPI_", "XPD"]): + efuse.category = "vdd" + + elif "MAC" in name: + efuse.category = "MAC" + if name in ["MAC", "CUSTOM_MAC", "MAC_EXT"]: + efuse.class_type = "mac" + + elif includes( + name, + [ + "BLOCK_KEY0", + "BLOCK_KEY1", + "BLOCK_KEY2", + "BLOCK_KEY3", + "BLOCK_KEY4", + "BLOCK_KEY5", + "BLOCK1", + "BLOCK2", + ], + ): + efuse.category = "security" + efuse.class_type = "keyblock" + + elif includes( + name, + [ + "KEY", + "SECURE", + "DOWNLOAD", + "SPI_BOOT_CRYPT_CNT", + "KEY_PURPOSE", + "SECURE_VERSION", + "DPA", + "ECDSA", + "FLASH_CRYPT_CNT", + "ENCRYPT", + "DECRYPT", + "ABS_DONE", + ], + ): + efuse.category = "security" + if name.startswith("KEY_PURPOSE"): + efuse.class_type = "keypurpose" + elif includes( + name, ["FLASH_CRYPT_CNT", "SPI_BOOT_CRYPT_CNT", "SECURE_VERSION"] + ): + efuse.class_type = "bitcount" + + elif includes(name, ["VERSION", "WAFER", "_ID", "PKG", "PACKAGE", "REV"]): + efuse.category = "identity" + if name == "OPTIONAL_UNIQUE_ID": + efuse.class_type = "keyblock" + + elif includes(name, ["ADC", "LDO", "DBIAS", "_HVT", "CALIB", "OCODE"]): + efuse.category = "calibration" + if name == "ADC_VREF": + efuse.class_type = "vref" + return + if includes(name, ["ADC", "LDO", "DBIAS", "_HVT"]): + efuse.class_type = "adc_tp" + elif name == "TEMP_CALIB": + efuse.class_type = "t_sensor" + + for e_name in e_desc["EFUSES"]: + data_dict = e_desc["EFUSES"][e_name] + if data_dict["show"] == "y": + d = Field() + d.name = e_name + d.block = data_dict["blk"] + d.word = data_dict["word"] + d.pos = data_dict["pos"] + d.bit_len = data_dict["len"] + d.type = data_dict["type"] + d.write_disable_bit = data_dict["wr_dis"] + d.read_disable_bit = ( + [int(x) for x in data_dict["rd_dis"].split(" ")] + if isinstance(data_dict["rd_dis"], str) + else data_dict["rd_dis"] + ) + d.description = data_dict["desc"] + d.alt_names = data_dict["alt"].split(" ") if data_dict["alt"] else [] + d.dictionary = ( + eval(data_dict["dict"]) if data_dict["dict"] != "" else None + ) + set_category_and_class_type(d, e_name) + self.ALL_EFUSES.append(d) diff --git a/code/.venv/lib/python3.12/site-packages/espefuse/efuse/util.py b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/util.py new file mode 100644 index 0000000..004e03b --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/espefuse/efuse/util.py @@ -0,0 +1,47 @@ +# This file consists of the common useful functions for eFuse +# +# SPDX-FileCopyrightText: 2020-2022 Espressif Systems (Shanghai) CO LTD +# +# SPDX-License-Identifier: GPL-2.0-or-later + +import esptool + + +def hexify(bitstring, separator=""): + as_bytes = tuple(b for b in bitstring) + return separator.join(("%02x" % b) for b in as_bytes) + + +def popcnt(b): + """Return number of "1" bits set in 'b'""" + return len([x for x in bin(b) if x == "1"]) + + +def check_duplicate_name_in_list(name_list): + duples_name = [name for i, name in enumerate(name_list) if name in name_list[:i]] + if duples_name != []: + raise esptool.FatalError( + "Found repeated {} in the name list".format(duples_name) + ) + + +class SdkConfig(object): + def __init__(self, path_to_file): + self.sdkconfig = dict() + if path_to_file is None: + return + with open(path_to_file, "r") as file: + for line in file.readlines(): + if line.startswith("#"): + continue + config = line.strip().split("=", 1) + if len(config) == 2: + self.sdkconfig[config[0]] = ( + True if config[1] == "y" else config[1].strip('"') + ) + + def __getitem__(self, config_name): + try: + return self.sdkconfig[config_name] + except KeyError: + return False diff --git a/code/.venv/lib/python3.12/site-packages/espefuse/efuse_defs/esp32.yaml b/code/.venv/lib/python3.12/site-packages/espefuse/efuse_defs/esp32.yaml new file mode 100644 index 0000000..cfae990 --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/espefuse/efuse_defs/esp32.yaml @@ -0,0 +1,64 @@ +VER_NO: 369d2d860d34e777c0f7d545a7dfc3c4 +EFUSES: + WR_DIS : {show: y, blk : 0, word: 0, pos : 0, len : 16, start : 0, type : 'uint:16', wr_dis : 1, rd_dis: null, alt : '', dict : '', desc: Efuse write disable mask, rloc: 'EFUSE_BLK0_RDATA0_REG[15:0]', bloc: 'B0,B1'} + RD_DIS : {show: y, blk : 0, word: 0, pos: 16, len : 4, start : 16, type : 'uint:4', wr_dis : 0, rd_dis: null, alt : '', dict : '', desc: Disable reading from BlOCK1-3, rloc: 'EFUSE_BLK0_RDATA0_REG[19:16]', bloc: 'B2[3:0]'} + FLASH_CRYPT_CNT : {show: y, blk : 0, word: 0, pos: 20, len : 7, start : 20, type : 'uint:7', wr_dis : 2, rd_dis: null, alt : '', dict : '', desc: Flash encryption is enabled if this field has an odd number of bits set, rloc: 'EFUSE_BLK0_RDATA0_REG[26:20]', bloc: 'B2[7:4],B3[2:0]'} + UART_DOWNLOAD_DIS : {show: y, blk : 0, word: 0, pos: 27, len : 1, start : 27, type : bool, wr_dis : 2, rd_dis: null, alt : '', dict : '', desc: Disable UART download mode. Valid for ESP32 V3 and newer; only, rloc: 'EFUSE_BLK0_RDATA0_REG[27]', bloc: 'B3[3]'} + RESERVED_0_28 : {show: n, blk : 0, word: 0, pos: 28, len : 4, start : 28, type : 'uint:4', wr_dis: null, rd_dis: null, alt : '', dict : '', desc: reserved, rloc: 'EFUSE_BLK0_RDATA0_REG[31:28]', bloc: 'B3[7:4]'} + MAC : {show: y, blk : 0, word: 1, pos : 0, len : 48, start : 32, type : 'bytes:6', wr_dis : 3, rd_dis: null, alt : MAC_FACTORY, dict : '', desc: MAC address, rloc: EFUSE_BLK0_RDATA1_REG, bloc: 'B4,B5,B6,B7,B8,B9'} + MAC_CRC : {show: y, blk : 0, word: 2, pos: 16, len : 8, start : 80, type : 'uint:8', wr_dis : 3, rd_dis: null, alt : MAC_FACTORY_CRC, dict : '', desc: CRC8 for MAC address, rloc: 'EFUSE_BLK0_RDATA2_REG[23:16]', bloc: B10} + RESERVE_0_88 : {show: n, blk : 0, word: 2, pos: 24, len : 8, start : 88, type : 'uint:8', wr_dis: null, rd_dis: null, alt : '', dict : '', desc: Reserved; it was created by set_missed_fields_in_regs func, rloc: 'EFUSE_BLK0_RDATA2_REG[31:24]', bloc: B11} + DISABLE_APP_CPU : {show: y, blk : 0, word: 3, pos : 0, len : 1, start : 96, type : bool, wr_dis : 3, rd_dis: null, alt : CHIP_VER_DIS_APP_CPU, dict : '', desc: Disables APP CPU, rloc: 'EFUSE_BLK0_RDATA3_REG[0]', bloc: 'B12[0]'} + DISABLE_BT : {show: y, blk : 0, word: 3, pos : 1, len : 1, start : 97, type : bool, wr_dis : 3, rd_dis: null, alt : CHIP_VER_DIS_BT, dict : '', desc: Disables Bluetooth, rloc: 'EFUSE_BLK0_RDATA3_REG[1]', bloc: 'B12[1]'} + CHIP_PACKAGE_4BIT : {show: y, blk : 0, word: 3, pos : 2, len : 1, start : 98, type : bool, wr_dis: null, rd_dis: null, alt : CHIP_VER_PKG_4BIT, dict : '', desc: 'Chip package identifier #4bit', rloc: 'EFUSE_BLK0_RDATA3_REG[2]', bloc: 'B12[2]'} + DIS_CACHE : {show: y, blk : 0, word: 3, pos : 3, len : 1, start : 99, type : bool, wr_dis : 3, rd_dis: null, alt : CHIP_VER_DIS_CACHE, dict : '', desc: Disables cache, rloc: 'EFUSE_BLK0_RDATA3_REG[3]', bloc: 'B12[3]'} + SPI_PAD_CONFIG_HD : {show: y, blk : 0, word: 3, pos : 4, len : 5, start: 100, type : 'uint:5', wr_dis: null, rd_dis: null, alt : '', dict : '', desc: read for SPI_pad_config_hd, rloc: 'EFUSE_BLK0_RDATA3_REG[8:4]', bloc: 'B12[7:4],B13[0]'} + CHIP_PACKAGE : {show: y, blk : 0, word: 3, pos : 9, len : 3, start: 105, type : 'uint:3', wr_dis: null, rd_dis: null, alt : CHIP_VER_PKG, dict : '', desc: Chip package identifier, rloc: 'EFUSE_BLK0_RDATA3_REG[11:9]', bloc: 'B13[3:1]'} + CHIP_CPU_FREQ_LOW : {show: y, blk : 0, word: 3, pos: 12, len : 1, start: 108, type : bool, wr_dis: null, rd_dis: null, alt : '', dict : '', desc: If set alongside EFUSE_RD_CHIP_CPU_FREQ_RATED; the ESP32's max CPU frequency is rated for 160MHz. 240MHz otherwise, rloc: 'EFUSE_BLK0_RDATA3_REG[12]', bloc: 'B13[4]'} + CHIP_CPU_FREQ_RATED : {show: y, blk : 0, word: 3, pos: 13, len : 1, start: 109, type : bool, wr_dis: null, rd_dis: null, alt : '', dict : '', desc: If set; the ESP32's maximum CPU frequency has been rated, rloc: 'EFUSE_BLK0_RDATA3_REG[13]', bloc: 'B13[5]'} + BLK3_PART_RESERVE : {show: y, blk : 0, word: 3, pos: 14, len : 1, start: 110, type : bool, wr_dis : 10, rd_dis : 3, alt : '', dict : '', desc: BLOCK3 partially served for ADC calibration data, rloc: 'EFUSE_BLK0_RDATA3_REG[14]', bloc: 'B13[6]'} + CHIP_VER_REV1 : {show: y, blk : 0, word: 3, pos: 15, len : 1, start: 111, type : bool, wr_dis: null, rd_dis: null, alt : '', dict : '', desc: bit is set to 1 for rev1 silicon, rloc: 'EFUSE_BLK0_RDATA3_REG[15]', bloc: 'B13[7]'} + RESERVE_0_112 : {show: n, blk : 0, word: 3, pos: 16, len : 16, start: 112, type : 'uint:16', wr_dis: null, rd_dis: null, alt : '', dict : '', desc: Reserved; it was created by set_missed_fields_in_regs func, rloc: 'EFUSE_BLK0_RDATA3_REG[31:16]', bloc: 'B14,B15'} + CLK8M_FREQ : {show: y, blk : 0, word: 4, pos : 0, len : 8, start: 128, type : 'uint:8', wr_dis : 4, rd_dis: null, alt : CK8M_FREQ, dict : '', desc: 8MHz clock freq override, rloc: 'EFUSE_BLK0_RDATA4_REG[7:0]', bloc: B16} + ADC_VREF : {show: y, blk : 0, word: 4, pos : 8, len : 5, start: 136, type : 'uint:5', wr_dis : 4, rd_dis: null, alt : '', dict : '', desc: True ADC reference voltage, rloc: 'EFUSE_BLK0_RDATA4_REG[12:8]', bloc: 'B17[4:0]'} + RESERVE_0_141 : {show: n, blk : 0, word: 4, pos: 13, len : 1, start: 141, type : bool, wr_dis: null, rd_dis: null, alt : '', dict : '', desc: Reserved; it was created by set_missed_fields_in_regs func, rloc: 'EFUSE_BLK0_RDATA4_REG[13]', bloc: 'B17[5]'} + XPD_SDIO_REG : {show: y, blk : 0, word: 4, pos: 14, len : 1, start: 142, type : bool, wr_dis : 5, rd_dis: null, alt : '', dict : '', desc: read for XPD_SDIO_REG, rloc: 'EFUSE_BLK0_RDATA4_REG[14]', bloc: 'B17[6]'} + XPD_SDIO_TIEH : {show: y, blk : 0, word: 4, pos: 15, len : 1, start: 143, type : bool, wr_dis : 5, rd_dis: null, alt : SDIO_TIEH, dict: '{1: "3.3V", 0: "1.8V"}', desc: If XPD_SDIO_FORCE & XPD_SDIO_REG, rloc: 'EFUSE_BLK0_RDATA4_REG[15]', bloc: 'B17[7]'} + XPD_SDIO_FORCE : {show: y, blk : 0, word: 4, pos: 16, len : 1, start: 144, type : bool, wr_dis : 5, rd_dis: null, alt : SDIO_FORCE, dict : '', desc: Ignore MTDI pin (GPIO12) for VDD_SDIO on reset, rloc: 'EFUSE_BLK0_RDATA4_REG[16]', bloc: 'B18[0]'} + RESERVE_0_145 : {show: n, blk : 0, word: 4, pos: 17, len : 15, start: 145, type : 'uint:15', wr_dis: null, rd_dis: null, alt : '', dict : '', desc: Reserved; it was created by set_missed_fields_in_regs func, rloc: 'EFUSE_BLK0_RDATA4_REG[31:17]', bloc: 'B18[7:1],B19'} + SPI_PAD_CONFIG_CLK : {show: y, blk : 0, word: 5, pos : 0, len : 5, start: 160, type : 'uint:5', wr_dis : 6, rd_dis: null, alt : '', dict : '', desc: Override SD_CLK pad (GPIO6/SPICLK), rloc: 'EFUSE_BLK0_RDATA5_REG[4:0]', bloc: 'B20[4:0]'} + SPI_PAD_CONFIG_Q : {show: y, blk : 0, word: 5, pos : 5, len : 5, start: 165, type : 'uint:5', wr_dis : 6, rd_dis: null, alt : '', dict : '', desc: Override SD_DATA_0 pad (GPIO7/SPIQ), rloc: 'EFUSE_BLK0_RDATA5_REG[9:5]', bloc: 'B20[7:5],B21[1:0]'} + SPI_PAD_CONFIG_D : {show: y, blk : 0, word: 5, pos: 10, len : 5, start: 170, type : 'uint:5', wr_dis : 6, rd_dis: null, alt : '', dict : '', desc: Override SD_DATA_1 pad (GPIO8/SPID), rloc: 'EFUSE_BLK0_RDATA5_REG[14:10]', bloc: 'B21[6:2]'} + SPI_PAD_CONFIG_CS0 : {show: y, blk : 0, word: 5, pos: 15, len : 5, start: 175, type : 'uint:5', wr_dis : 6, rd_dis: null, alt : '', dict : '', desc: Override SD_CMD pad (GPIO11/SPICS0), rloc: 'EFUSE_BLK0_RDATA5_REG[19:15]', bloc: 'B21[7],B22[3:0]'} + CHIP_VER_REV2 : {show: y, blk : 0, word: 5, pos: 20, len : 1, start: 180, type : bool, wr_dis: null, rd_dis: null, alt : '', dict : '', desc: '', rloc: 'EFUSE_BLK0_RDATA5_REG[20]', bloc: 'B22[4]'} + RESERVE_0_181 : {show: n, blk : 0, word: 5, pos: 21, len : 1, start: 181, type : bool, wr_dis: null, rd_dis: null, alt : '', dict : '', desc: Reserved; it was created by set_missed_fields_in_regs func, rloc: 'EFUSE_BLK0_RDATA5_REG[21]', bloc: 'B22[5]'} + VOL_LEVEL_HP_INV : {show: y, blk : 0, word: 5, pos: 22, len : 2, start: 182, type : 'uint:2', wr_dis : 3, rd_dis: null, alt : '', dict : '', desc: 'This field stores the voltage level for CPU to run at 240 MHz; or for flash/PSRAM to run at 80 MHz.0x0: level 7; 0x1: level 6; 0x2: level 5; 0x3: level 4. (RO)', rloc: 'EFUSE_BLK0_RDATA5_REG[23:22]', bloc: 'B22[7:6]'} + WAFER_VERSION_MINOR : {show: y, blk : 0, word: 5, pos: 24, len : 2, start: 184, type : 'uint:2', wr_dis: null, rd_dis: null, alt : '', dict : '', desc: '', rloc: 'EFUSE_BLK0_RDATA5_REG[25:24]', bloc: 'B23[1:0]'} + RESERVE_0_186 : {show: n, blk : 0, word: 5, pos: 26, len : 2, start: 186, type : 'uint:2', wr_dis: null, rd_dis: null, alt : '', dict : '', desc: Reserved; it was created by set_missed_fields_in_regs func, rloc: 'EFUSE_BLK0_RDATA5_REG[27:26]', bloc: 'B23[3:2]'} + FLASH_CRYPT_CONFIG : {show: y, blk : 0, word: 5, pos: 28, len : 4, start: 188, type : 'uint:4', wr_dis : 10, rd_dis : 3, alt : ENCRYPT_CONFIG, dict : '', desc: Flash encryption config (key tweak bits), rloc: 'EFUSE_BLK0_RDATA5_REG[31:28]', bloc: 'B23[7:4]'} + CODING_SCHEME : {show: y, blk : 0, word: 6, pos : 0, len : 2, start: 192, type : 'uint:2', wr_dis : 10, rd_dis : 3, alt : '', dict: '{0: "NONE (BLK1-3 len=256 bits)", 1: "3/4 (BLK1-3 len=192 bits)", 2: "REPEAT (BLK1-3 len=128 bits) not supported", 3: "NONE (BLK1-3 len=256 bits)"}', desc: Efuse variable block length scheme, rloc: 'EFUSE_BLK0_RDATA6_REG[1:0]', bloc: 'B24[1:0]'} + CONSOLE_DEBUG_DISABLE: {show: y, blk : 0, word: 6, pos : 2, len : 1, start: 194, type : bool, wr_dis : 15, rd_dis: null, alt : '', dict : '', desc: Disable ROM BASIC interpreter fallback, rloc: 'EFUSE_BLK0_RDATA6_REG[2]', bloc: 'B24[2]'} + DISABLE_SDIO_HOST : {show: y, blk : 0, word: 6, pos : 3, len : 1, start: 195, type : bool, wr_dis: null, rd_dis: null, alt : '', dict : '', desc: '', rloc: 'EFUSE_BLK0_RDATA6_REG[3]', bloc: 'B24[3]'} + ABS_DONE_0 : {show: y, blk : 0, word: 6, pos : 4, len : 1, start: 196, type : bool, wr_dis : 12, rd_dis: null, alt : '', dict : '', desc: Secure boot V1 is enabled for bootloader image, rloc: 'EFUSE_BLK0_RDATA6_REG[4]', bloc: 'B24[4]'} + ABS_DONE_1 : {show: y, blk : 0, word: 6, pos : 5, len : 1, start: 197, type : bool, wr_dis : 13, rd_dis: null, alt : '', dict : '', desc: Secure boot V2 is enabled for bootloader image, rloc: 'EFUSE_BLK0_RDATA6_REG[5]', bloc: 'B24[5]'} + JTAG_DISABLE : {show: y, blk : 0, word: 6, pos : 6, len : 1, start: 198, type : bool, wr_dis : 14, rd_dis: null, alt : DISABLE_JTAG, dict : '', desc: Disable JTAG, rloc: 'EFUSE_BLK0_RDATA6_REG[6]', bloc: 'B24[6]'} + DISABLE_DL_ENCRYPT : {show: y, blk : 0, word: 6, pos : 7, len : 1, start: 199, type : bool, wr_dis : 15, rd_dis: null, alt : '', dict : '', desc: Disable flash encryption in UART bootloader, rloc: 'EFUSE_BLK0_RDATA6_REG[7]', bloc: 'B24[7]'} + DISABLE_DL_DECRYPT : {show: y, blk : 0, word: 6, pos : 8, len : 1, start: 200, type : bool, wr_dis : 15, rd_dis: null, alt : '', dict : '', desc: Disable flash decryption in UART bootloader, rloc: 'EFUSE_BLK0_RDATA6_REG[8]', bloc: 'B25[0]'} + DISABLE_DL_CACHE : {show: y, blk : 0, word: 6, pos : 9, len : 1, start: 201, type : bool, wr_dis : 15, rd_dis: null, alt : '', dict : '', desc: Disable flash cache in UART bootloader, rloc: 'EFUSE_BLK0_RDATA6_REG[9]', bloc: 'B25[1]'} + KEY_STATUS : {show: y, blk : 0, word: 6, pos: 10, len : 1, start: 202, type : bool, wr_dis : 10, rd_dis : 3, alt : '', dict : '', desc: Usage of efuse block 3 (reserved), rloc: 'EFUSE_BLK0_RDATA6_REG[10]', bloc: 'B25[2]'} + RESERVE_0_203 : {show: n, blk : 0, word: 6, pos: 11, len : 21, start: 203, type : 'uint:21', wr_dis: null, rd_dis: null, alt : '', dict : '', desc: Reserved; it was created by set_missed_fields_in_regs func, rloc: 'EFUSE_BLK0_RDATA6_REG[31:11]', bloc: 'B25[7:3],B26,B27'} + BLOCK1 : {show: y, blk : 1, word: 0, pos : 0, len: 256, start : 0, type: 'bytes:32', wr_dis : 7, rd_dis : 0, alt : ENCRYPT_FLASH_KEY, dict : '', desc: Flash encryption key, rloc: EFUSE_BLK1_RDATA0_REG, bloc: 'B0,B1,B2,B3,B4,B5,B6,B7,B8,B9,B10,B11,B12,B13,B14,B15,B16,B17,B18,B19,B20,B21,B22,B23,B24,B25,B26,B27,B28,B29,B30,B31'} + BLOCK2 : {show: y, blk : 2, word: 0, pos : 0, len: 256, start : 0, type: 'bytes:32', wr_dis : 8, rd_dis : 1, alt : SECURE_BOOT_KEY, dict : '', desc: Security boot key, rloc: EFUSE_BLK2_RDATA0_REG, bloc: 'B0,B1,B2,B3,B4,B5,B6,B7,B8,B9,B10,B11,B12,B13,B14,B15,B16,B17,B18,B19,B20,B21,B22,B23,B24,B25,B26,B27,B28,B29,B30,B31'} + CUSTOM_MAC_CRC : {show: y, blk : 3, word: 0, pos : 0, len : 8, start : 0, type : 'uint:8', wr_dis : 9, rd_dis : 2, alt : MAC_CUSTOM_CRC, dict : '', desc: CRC8 for custom MAC address, rloc: 'EFUSE_BLK3_RDATA0_REG[7:0]', bloc: B0} + CUSTOM_MAC : {show: y, blk : 3, word: 0, pos : 8, len : 48, start : 8, type : 'bytes:6', wr_dis : 9, rd_dis : 2, alt : MAC_CUSTOM, dict : '', desc: Custom MAC address, rloc: 'EFUSE_BLK3_RDATA0_REG[31:8]', bloc: 'B1,B2,B3,B4,B5,B6'} + RESERVED_3_56 : {show: n, blk : 3, word: 1, pos: 24, len : 8, start : 56, type : 'uint:8', wr_dis : 9, rd_dis : 2, alt : '', dict : '', desc: reserved, rloc: 'EFUSE_BLK3_RDATA1_REG[31:24]', bloc: B7} + BLK3_RESERVED_2 : {show: n, blk : 3, word: 2, pos : 0, len : 32, start : 64, type : 'uint:32', wr_dis : 9, rd_dis : 2, alt : '', dict : '', desc: read for BLOCK3, rloc: EFUSE_BLK3_RDATA2_REG, bloc: 'B8,B9,B10,B11'} + ADC1_TP_LOW : {show: y, blk : 3, word: 3, pos : 0, len : 7, start : 96, type : 'uint:7', wr_dis : 9, rd_dis : 2, alt : '', dict : '', desc: ADC1 Two Point calibration low point. Only valid if EFUSE_RD_BLK3_PART_RESERVE, rloc: 'EFUSE_BLK3_RDATA3_REG[6:0]', bloc: 'B12[6:0]'} + ADC1_TP_HIGH : {show: y, blk : 3, word: 3, pos : 7, len : 9, start: 103, type : 'uint:9', wr_dis : 9, rd_dis : 2, alt : '', dict : '', desc: ADC1 Two Point calibration high point. Only valid if EFUSE_RD_BLK3_PART_RESERVE, rloc: 'EFUSE_BLK3_RDATA3_REG[15:7]', bloc: 'B12[7],B13'} + ADC2_TP_LOW : {show: y, blk : 3, word: 3, pos: 16, len : 7, start: 112, type : 'uint:7', wr_dis : 9, rd_dis : 2, alt : '', dict : '', desc: ADC2 Two Point calibration low point. Only valid if EFUSE_RD_BLK3_PART_RESERVE, rloc: 'EFUSE_BLK3_RDATA3_REG[22:16]', bloc: 'B14[6:0]'} + ADC2_TP_HIGH : {show: y, blk : 3, word: 3, pos: 23, len : 9, start: 119, type : 'uint:9', wr_dis : 9, rd_dis : 2, alt : '', dict : '', desc: ADC2 Two Point calibration high point. Only valid if EFUSE_RD_BLK3_PART_RESERVE, rloc: 'EFUSE_BLK3_RDATA3_REG[31:23]', bloc: 'B14[7],B15'} + SECURE_VERSION : {show: y, blk : 3, word: 4, pos : 0, len : 32, start: 128, type : 'uint:32', wr_dis : 9, rd_dis : 2, alt : '', dict : '', desc: Secure version for anti-rollback, rloc: EFUSE_BLK3_RDATA4_REG, bloc: 'B16,B17,B18,B19'} + RESERVED_3_160 : {show: n, blk : 3, word: 5, pos : 0, len : 24, start: 160, type : 'uint:24', wr_dis : 9, rd_dis : 2, alt : '', dict : '', desc: reserved, rloc: 'EFUSE_BLK3_RDATA5_REG[23:0]', bloc: 'B20,B21,B22'} + MAC_VERSION : {show: y, blk : 3, word: 5, pos: 24, len : 8, start: 184, type : 'uint:8', wr_dis : 9, rd_dis : 2, alt : MAC_CUSTOM_VER, dict: '{1: "Custom MAC in BLOCK3"}', desc: Version of the MAC field, rloc: 'EFUSE_BLK3_RDATA5_REG[31:24]', bloc: B23} + BLK3_RESERVED_6 : {show: n, blk : 3, word: 6, pos : 0, len : 32, start: 192, type : 'uint:32', wr_dis : 9, rd_dis : 2, alt : '', dict : '', desc: read for BLOCK3, rloc: EFUSE_BLK3_RDATA6_REG, bloc: 'B24,B25,B26,B27'} + BLK3_RESERVED_7 : {show: n, blk : 3, word: 7, pos : 0, len : 32, start: 224, type : 'uint:32', wr_dis : 9, rd_dis : 2, alt : '', dict : '', desc: read for BLOCK3, rloc: EFUSE_BLK3_RDATA7_REG, bloc: 'B28,B29,B30,B31'} diff --git a/code/.venv/lib/python3.12/site-packages/espefuse/efuse_defs/esp32c2.yaml b/code/.venv/lib/python3.12/site-packages/espefuse/efuse_defs/esp32c2.yaml new file mode 100644 index 0000000..e89e354 --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/espefuse/efuse_defs/esp32c2.yaml @@ -0,0 +1,53 @@ +VER_NO: 897499b0349a608b895d467abbcf006b +EFUSES: + WR_DIS : {show: y, blk : 0, word: 0, pos : 0, len : 8, start : 0, type : 'uint:8', wr_dis: null, rd_dis: null, alt : '', dict : '', desc: Disable programming of individual eFuses, rloc: 'EFUSE_RD_WR_DIS_REG[7:0]', bloc: B0} + RESERVED_0_8 : {show: n, blk : 0, word: 0, pos : 8, len : 24, start : 8, type : 'uint:24', wr_dis: null, rd_dis: null, alt : '', dict : '', desc: '', rloc: 'EFUSE_RD_WR_DIS_REG[31:8]', bloc: 'B1,B2,B3'} + RD_DIS : {show: y, blk : 0, word: 1, pos : 0, len : 2, start : 32, type : 'uint:2', wr_dis : 0, rd_dis: null, alt : '', dict : '', desc: Disable reading from BlOCK3, rloc: 'EFUSE_RD_REPEAT_DATA0_REG[1:0]', bloc: 'B1[1:0]'} + WDT_DELAY_SEL : {show: y, blk : 0, word: 1, pos : 2, len : 2, start : 34, type : 'uint:2', wr_dis : 1, rd_dis: null, alt : '', dict: '{0: "40000", 1: "80000", 2: "160000", 3: "320000"}', desc: RTC watchdog timeout threshold; in unit of slow clock cycle, rloc: 'EFUSE_RD_REPEAT_DATA0_REG[3:2]', bloc: 'B1[3:2]'} + DIS_PAD_JTAG : {show: y, blk : 0, word: 1, pos : 4, len : 1, start : 36, type : bool, wr_dis : 1, rd_dis: null, alt : '', dict : '', desc: Set this bit to disable pad jtag, rloc: 'EFUSE_RD_REPEAT_DATA0_REG[4]', bloc: 'B1[4]'} + DIS_DOWNLOAD_ICACHE : {show: y, blk : 0, word: 1, pos : 5, len : 1, start : 37, type : bool, wr_dis : 1, rd_dis: null, alt : '', dict : '', desc: The bit be set to disable icache in download mode, rloc: 'EFUSE_RD_REPEAT_DATA0_REG[5]', bloc: 'B1[5]'} + DIS_DOWNLOAD_MANUAL_ENCRYPT: {show: y, blk : 0, word: 1, pos : 6, len : 1, start : 38, type : bool, wr_dis : 2, rd_dis: null, alt : '', dict : '', desc: The bit be set to disable manual encryption, rloc: 'EFUSE_RD_REPEAT_DATA0_REG[6]', bloc: 'B1[6]'} + SPI_BOOT_CRYPT_CNT : {show: y, blk : 0, word: 1, pos : 7, len : 3, start : 39, type : 'uint:3', wr_dis : 2, rd_dis: null, alt : '', dict: '{0: "Disable", 1: "Enable", 3: "Disable", 7: "Enable"}', desc: Enables flash encryption when 1 or 3 bits are set and disables otherwise, rloc: 'EFUSE_RD_REPEAT_DATA0_REG[9:7]', bloc: 'B1[7],B2[1:0]'} + XTS_KEY_LENGTH_256 : {show: y, blk : 0, word: 1, pos: 10, len : 1, start : 42, type : bool, wr_dis : 2, rd_dis: null, alt : '', dict: '{0: "128 bits key", 1: "256 bits key"}', desc: Flash encryption key length, rloc: 'EFUSE_RD_REPEAT_DATA0_REG[10]', bloc: 'B2[2]'} + UART_PRINT_CONTROL : {show: y, blk : 0, word: 1, pos: 11, len : 2, start : 43, type : 'uint:2', wr_dis : 3, rd_dis: null, alt : '', dict: '{0: "Enable", 1: "Enable when GPIO8 is low at reset", 2: "Enable when GPIO8 is high at reset", 3: "Disable"}', desc: Set the default UARTboot message output mode, rloc: 'EFUSE_RD_REPEAT_DATA0_REG[12:11]', bloc: 'B2[4:3]'} + FORCE_SEND_RESUME : {show: y, blk : 0, word: 1, pos: 13, len : 1, start : 45, type : bool, wr_dis : 3, rd_dis: null, alt : '', dict : '', desc: Set this bit to force ROM code to send a resume command during SPI boot, rloc: 'EFUSE_RD_REPEAT_DATA0_REG[13]', bloc: 'B2[5]'} + DIS_DOWNLOAD_MODE : {show: y, blk : 0, word: 1, pos: 14, len : 1, start : 46, type : bool, wr_dis : 3, rd_dis: null, alt : '', dict : '', desc: 'Set this bit to disable download mode (boot_mode[3:0] = 0; 1; 2; 4; 5; 6; 7)', rloc: 'EFUSE_RD_REPEAT_DATA0_REG[14]', bloc: 'B2[6]'} + DIS_DIRECT_BOOT : {show: y, blk : 0, word: 1, pos: 15, len : 1, start : 47, type : bool, wr_dis : 3, rd_dis: null, alt : '', dict : '', desc: This bit set means disable direct_boot mode, rloc: 'EFUSE_RD_REPEAT_DATA0_REG[15]', bloc: 'B2[7]'} + ENABLE_SECURITY_DOWNLOAD : {show: y, blk : 0, word: 1, pos: 16, len : 1, start : 48, type : bool, wr_dis : 3, rd_dis: null, alt : '', dict : '', desc: Set this bit to enable secure UART download mode, rloc: 'EFUSE_RD_REPEAT_DATA0_REG[16]', bloc: 'B3[0]'} + FLASH_TPUW : {show: y, blk : 0, word: 1, pos: 17, len : 4, start : 49, type : 'uint:4', wr_dis : 3, rd_dis: null, alt : '', dict : '', desc: Configures flash waiting time after power-up; in unit of ms. If the value is less than 15; the waiting time is the configurable value. Otherwise; the waiting time is twice the configurable value, rloc: 'EFUSE_RD_REPEAT_DATA0_REG[20:17]', bloc: 'B3[4:1]'} + SECURE_BOOT_EN : {show: y, blk : 0, word: 1, pos: 21, len : 1, start : 53, type : bool, wr_dis : 2, rd_dis: null, alt : '', dict : '', desc: The bit be set to enable secure boot, rloc: 'EFUSE_RD_REPEAT_DATA0_REG[21]', bloc: 'B3[5]'} + SECURE_VERSION : {show: y, blk : 0, word: 1, pos: 22, len : 4, start : 54, type : 'uint:4', wr_dis : 4, rd_dis: null, alt : '', dict : '', desc: Secure version for anti-rollback, rloc: 'EFUSE_RD_REPEAT_DATA0_REG[25:22]', bloc: 'B3[7:6],B4[1:0]'} + CUSTOM_MAC_USED : {show: y, blk : 0, word: 1, pos: 26, len : 1, start : 58, type : bool, wr_dis : 4, rd_dis: null, alt : ENABLE_CUSTOM_MAC, dict : '', desc: True if MAC_CUSTOM is burned, rloc: 'EFUSE_RD_REPEAT_DATA0_REG[26]', bloc: 'B4[2]'} + DISABLE_WAFER_VERSION_MAJOR: {show: y, blk : 0, word: 1, pos: 27, len : 1, start : 59, type : bool, wr_dis : 4, rd_dis: null, alt : '', dict : '', desc: Disables check of wafer version major, rloc: 'EFUSE_RD_REPEAT_DATA0_REG[27]', bloc: 'B4[3]'} + DISABLE_BLK_VERSION_MAJOR : {show: y, blk : 0, word: 1, pos: 28, len : 1, start : 60, type : bool, wr_dis : 4, rd_dis: null, alt : '', dict : '', desc: Disables check of blk version major, rloc: 'EFUSE_RD_REPEAT_DATA0_REG[28]', bloc: 'B4[4]'} + RESERVED_0_61 : {show: n, blk : 0, word: 1, pos: 29, len : 3, start : 61, type : 'uint:3', wr_dis: null, rd_dis: null, alt : '', dict : '', desc: reserved, rloc: 'EFUSE_RD_REPEAT_DATA0_REG[31:29]', bloc: 'B4[7:5]'} + CUSTOM_MAC : {show: y, blk : 1, word: 0, pos : 0, len : 48, start : 0, type : 'bytes:6', wr_dis : 5, rd_dis: null, alt: MAC_CUSTOM USER_DATA_MAC_CUSTOM, dict : '', desc: Custom MAC address, rloc: EFUSE_RD_BLK1_DATA0_REG, bloc: 'B0,B1,B2,B3,B4,B5'} + RESERVED_1_48 : {show: n, blk : 1, word: 1, pos: 16, len : 16, start : 48, type : 'uint:16', wr_dis : 5, rd_dis: null, alt : '', dict : '', desc: reserved, rloc: 'EFUSE_RD_BLK1_DATA1_REG[31:16]', bloc: 'B6,B7'} + SYSTEM_DATA2 : {show: n, blk : 1, word: 2, pos : 0, len : 24, start : 64, type : 'uint:24', wr_dis : 5, rd_dis: null, alt : '', dict : '', desc: 'Stores the bits [64:87] of system data', rloc: 'EFUSE_RD_BLK1_DATA2_REG[23:0]', bloc: 'B8,B9,B10'} + MAC : {show: y, blk : 2, word: 0, pos : 0, len : 48, start : 0, type : 'bytes:6', wr_dis : 6, rd_dis: null, alt : MAC_FACTORY, dict : '', desc: MAC address, rloc: EFUSE_RD_BLK2_DATA0_REG, bloc: 'B0,B1,B2,B3,B4,B5'} + WAFER_VERSION_MINOR : {show: y, blk : 2, word: 1, pos: 16, len : 4, start : 48, type : 'uint:4', wr_dis : 6, rd_dis: null, alt : '', dict : '', desc: WAFER_VERSION_MINOR, rloc: 'EFUSE_RD_BLK2_DATA1_REG[19:16]', bloc: 'B6[3:0]'} + WAFER_VERSION_MAJOR : {show: y, blk : 2, word: 1, pos: 20, len : 2, start : 52, type : 'uint:2', wr_dis : 6, rd_dis: null, alt : '', dict : '', desc: WAFER_VERSION_MAJOR, rloc: 'EFUSE_RD_BLK2_DATA1_REG[21:20]', bloc: 'B6[5:4]'} + PKG_VERSION : {show: y, blk : 2, word: 1, pos: 22, len : 3, start : 54, type : 'uint:3', wr_dis : 6, rd_dis: null, alt : '', dict : '', desc: EFUSE_PKG_VERSION, rloc: 'EFUSE_RD_BLK2_DATA1_REG[24:22]', bloc: 'B6[7:6],B7[0]'} + BLK_VERSION_MINOR : {show: y, blk : 2, word: 1, pos: 25, len : 3, start : 57, type : 'uint:3', wr_dis : 6, rd_dis: null, alt : '', dict: '{0: "No calib", 1: "With calib"}', desc: Minor version of BLOCK2, rloc: 'EFUSE_RD_BLK2_DATA1_REG[27:25]', bloc: 'B7[3:1]'} + BLK_VERSION_MAJOR : {show: y, blk : 2, word: 1, pos: 28, len : 2, start : 60, type : 'uint:2', wr_dis : 6, rd_dis: null, alt : '', dict : '', desc: Major version of BLOCK2, rloc: 'EFUSE_RD_BLK2_DATA1_REG[29:28]', bloc: 'B7[5:4]'} + OCODE : {show: y, blk : 2, word: 1, pos: 30, len : 7, start : 62, type : 'uint:7', wr_dis : 6, rd_dis: null, alt : '', dict : '', desc: OCode, rloc: 'EFUSE_RD_BLK2_DATA1_REG[31:30]', bloc: 'B7[7:6],B8[4:0]'} + TEMP_CALIB : {show: y, blk : 2, word: 2, pos : 5, len : 9, start : 69, type : 'uint:9', wr_dis : 6, rd_dis: null, alt : '', dict : '', desc: Temperature calibration data, rloc: 'EFUSE_RD_BLK2_DATA2_REG[13:5]', bloc: 'B8[7:5],B9[5:0]'} + ADC1_INIT_CODE_ATTEN0 : {show: y, blk : 2, word: 2, pos: 14, len : 8, start : 78, type : 'uint:8', wr_dis : 6, rd_dis: null, alt : '', dict : '', desc: ADC1 init code at atten0, rloc: 'EFUSE_RD_BLK2_DATA2_REG[21:14]', bloc: 'B9[7:6],B10[5:0]'} + ADC1_INIT_CODE_ATTEN3 : {show: y, blk : 2, word: 2, pos: 22, len : 5, start : 86, type : 'uint:5', wr_dis : 6, rd_dis: null, alt : '', dict : '', desc: ADC1 init code at atten3, rloc: 'EFUSE_RD_BLK2_DATA2_REG[26:22]', bloc: 'B10[7:6],B11[2:0]'} + ADC1_CAL_VOL_ATTEN0 : {show: y, blk : 2, word: 2, pos: 27, len : 8, start : 91, type : 'uint:8', wr_dis : 6, rd_dis: null, alt : '', dict : '', desc: ADC1 calibration voltage at atten0, rloc: 'EFUSE_RD_BLK2_DATA2_REG[31:27]', bloc: 'B11[7:3],B12[2:0]'} + ADC1_CAL_VOL_ATTEN3 : {show: y, blk : 2, word: 3, pos : 3, len : 6, start : 99, type : 'uint:6', wr_dis : 6, rd_dis: null, alt : '', dict : '', desc: ADC1 calibration voltage at atten3, rloc: 'EFUSE_RD_BLK2_DATA3_REG[8:3]', bloc: 'B12[7:3],B13[0]'} + DIG_DBIAS_HVT : {show: y, blk : 2, word: 3, pos : 9, len : 5, start: 105, type : 'uint:5', wr_dis : 6, rd_dis: null, alt : '', dict : '', desc: BLOCK2 digital dbias when hvt, rloc: 'EFUSE_RD_BLK2_DATA3_REG[13:9]', bloc: 'B13[5:1]'} + DIG_LDO_SLP_DBIAS2 : {show: y, blk : 2, word: 3, pos: 14, len : 7, start: 110, type : 'uint:7', wr_dis : 6, rd_dis: null, alt : '', dict : '', desc: BLOCK2 DIG_LDO_DBG0_DBIAS2, rloc: 'EFUSE_RD_BLK2_DATA3_REG[20:14]', bloc: 'B13[7:6],B14[4:0]'} + DIG_LDO_SLP_DBIAS26 : {show: y, blk : 2, word: 3, pos: 21, len : 8, start: 117, type : 'uint:8', wr_dis : 6, rd_dis: null, alt : '', dict : '', desc: BLOCK2 DIG_LDO_DBG0_DBIAS26, rloc: 'EFUSE_RD_BLK2_DATA3_REG[28:21]', bloc: 'B14[7:5],B15[4:0]'} + DIG_LDO_ACT_DBIAS26 : {show: y, blk : 2, word: 3, pos: 29, len : 6, start: 125, type : 'uint:6', wr_dis : 6, rd_dis: null, alt : '', dict : '', desc: BLOCK2 DIG_LDO_ACT_DBIAS26, rloc: 'EFUSE_RD_BLK2_DATA3_REG[31:29]', bloc: 'B15[7:5],B16[2:0]'} + DIG_LDO_ACT_STEPD10 : {show: y, blk : 2, word: 4, pos : 3, len : 4, start: 131, type : 'uint:4', wr_dis : 6, rd_dis: null, alt : '', dict : '', desc: BLOCK2 DIG_LDO_ACT_STEPD10, rloc: 'EFUSE_RD_BLK2_DATA4_REG[6:3]', bloc: 'B16[6:3]'} + RTC_LDO_SLP_DBIAS13 : {show: y, blk : 2, word: 4, pos : 7, len : 7, start: 135, type : 'uint:7', wr_dis : 6, rd_dis: null, alt : '', dict : '', desc: BLOCK2 DIG_LDO_SLP_DBIAS13, rloc: 'EFUSE_RD_BLK2_DATA4_REG[13:7]', bloc: 'B16[7],B17[5:0]'} + RTC_LDO_SLP_DBIAS29 : {show: y, blk : 2, word: 4, pos: 14, len : 9, start: 142, type : 'uint:9', wr_dis : 6, rd_dis: null, alt : '', dict : '', desc: BLOCK2 DIG_LDO_SLP_DBIAS29, rloc: 'EFUSE_RD_BLK2_DATA4_REG[22:14]', bloc: 'B17[7:6],B18[6:0]'} + RTC_LDO_SLP_DBIAS31 : {show: y, blk : 2, word: 4, pos: 23, len : 6, start: 151, type : 'uint:6', wr_dis : 6, rd_dis: null, alt : '', dict : '', desc: BLOCK2 DIG_LDO_SLP_DBIAS31, rloc: 'EFUSE_RD_BLK2_DATA4_REG[28:23]', bloc: 'B18[7],B19[4:0]'} + RTC_LDO_ACT_DBIAS31 : {show: y, blk : 2, word: 4, pos: 29, len : 6, start: 157, type : 'uint:6', wr_dis : 6, rd_dis: null, alt : '', dict : '', desc: BLOCK2 DIG_LDO_ACT_DBIAS31, rloc: 'EFUSE_RD_BLK2_DATA4_REG[31:29]', bloc: 'B19[7:5],B20[2:0]'} + RTC_LDO_ACT_DBIAS13 : {show: y, blk : 2, word: 5, pos : 3, len : 8, start: 163, type : 'uint:8', wr_dis : 6, rd_dis: null, alt : '', dict : '', desc: BLOCK2 DIG_LDO_ACT_DBIAS13, rloc: 'EFUSE_RD_BLK2_DATA5_REG[10:3]', bloc: 'B20[7:3],B21[2:0]'} + RESERVED_2_171 : {show: n, blk : 2, word: 5, pos: 11, len : 21, start: 171, type : 'uint:21', wr_dis : 6, rd_dis: null, alt : '', dict : '', desc: reserved, rloc: 'EFUSE_RD_BLK2_DATA5_REG[31:11]', bloc: 'B21[7:3],B22,B23'} + ADC_CALIBRATION_3 : {show: y, blk : 2, word: 6, pos : 0, len : 11, start: 192, type : 'uint:11', wr_dis : 6, rd_dis: null, alt : '', dict : '', desc: 'Store the bit [86:96] of ADC calibration data', rloc: 'EFUSE_RD_BLK2_DATA6_REG[10:0]', bloc: 'B24,B25[2:0]'} + BLK2_RESERVED_DATA_0 : {show: n, blk : 2, word: 6, pos: 11, len : 21, start: 203, type : 'uint:21', wr_dis : 6, rd_dis: null, alt : '', dict : '', desc: 'Store the bit [0:20] of block2 reserved data', rloc: 'EFUSE_RD_BLK2_DATA6_REG[31:11]', bloc: 'B25[7:3],B26,B27'} + BLK2_RESERVED_DATA_1 : {show: n, blk : 2, word: 7, pos : 0, len : 32, start: 224, type : 'uint:32', wr_dis : 6, rd_dis: null, alt : '', dict : '', desc: 'Store the bit [21:52] of block2 reserved data', rloc: EFUSE_RD_BLK2_DATA7_REG, bloc: 'B28,B29,B30,B31'} + BLOCK_KEY0 : {show: y, blk : 3, word: 0, pos : 0, len: 256, start : 0, type: 'bytes:32', wr_dis : 7, rd_dis : 0 1, alt : KEY0, dict : '', desc: BLOCK_KEY0 - 256-bits. 256-bit key of Flash Encryption, rloc: EFUSE_RD_BLK3_DATA0_REG, bloc: 'B0,B1,B2,B3,B4,B5,B6,B7,B8,B9,B10,B11,B12,B13,B14,B15,B16,B17,B18,B19,B20,B21,B22,B23,B24,B25,B26,B27,B28,B29,B30,B31'} diff --git a/code/.venv/lib/python3.12/site-packages/espefuse/efuse_defs/esp32c3.yaml b/code/.venv/lib/python3.12/site-packages/espefuse/efuse_defs/esp32c3.yaml new file mode 100644 index 0000000..035f30c --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/espefuse/efuse_defs/esp32c3.yaml @@ -0,0 +1,112 @@ +VER_NO: 4622cf9245401eca0eb1df8122449a6d +EFUSES: + WR_DIS : {show: y, blk : 0, word: 0, pos : 0, len : 32, start : 0, type : 'uint:32', wr_dis: null, rd_dis: null, alt : '', dict : '', desc: Disable programming of individual eFuses, rloc: EFUSE_RD_WR_DIS_REG, bloc: 'B0,B1,B2,B3'} + RD_DIS : {show: y, blk : 0, word: 1, pos : 0, len : 7, start : 32, type : 'uint:7', wr_dis : 0, rd_dis: null, alt : '', dict : '', desc: Disable reading from BlOCK4-10, rloc: 'EFUSE_RD_REPEAT_DATA0_REG[6:0]', bloc: 'B4[6:0]'} + DIS_RTC_RAM_BOOT : {show: n, blk : 0, word: 1, pos : 7, len : 1, start : 39, type : bool, wr_dis : 1, rd_dis: null, alt : '', dict : '', desc: Set this bit to disable boot from RTC RAM, rloc: 'EFUSE_RD_REPEAT_DATA0_REG[7]', bloc: 'B4[7]'} + DIS_ICACHE : {show: y, blk : 0, word: 1, pos : 8, len : 1, start : 40, type : bool, wr_dis : 2, rd_dis: null, alt : '', dict : '', desc: Set this bit to disable Icache, rloc: 'EFUSE_RD_REPEAT_DATA0_REG[8]', bloc: 'B5[0]'} + DIS_USB_JTAG : {show: y, blk : 0, word: 1, pos : 9, len : 1, start : 41, type : bool, wr_dis : 2, rd_dis: null, alt : '', dict : '', desc: Set this bit to disable function of usb switch to jtag in module of usb device, rloc: 'EFUSE_RD_REPEAT_DATA0_REG[9]', bloc: 'B5[1]'} + DIS_DOWNLOAD_ICACHE : {show: y, blk : 0, word: 1, pos: 10, len : 1, start : 42, type : bool, wr_dis : 2, rd_dis: null, alt : '', dict : '', desc: 'Set this bit to disable Icache in download mode (boot_mode[3:0] is 0; 1; 2; 3; 6; 7)', rloc: 'EFUSE_RD_REPEAT_DATA0_REG[10]', bloc: 'B5[2]'} + DIS_USB_SERIAL_JTAG : {show: y, blk : 0, word: 1, pos: 11, len : 1, start : 43, type : bool, wr_dis : 2, rd_dis: null, alt : DIS_USB_DEVICE, dict: '{0: "Enable", 1: "Disable"}', desc: USB-Serial-JTAG, rloc: 'EFUSE_RD_REPEAT_DATA0_REG[11]', bloc: 'B5[3]'} + DIS_FORCE_DOWNLOAD : {show: y, blk : 0, word: 1, pos: 12, len : 1, start : 44, type : bool, wr_dis : 2, rd_dis: null, alt : '', dict : '', desc: Set this bit to disable the function that forces chip into download mode, rloc: 'EFUSE_RD_REPEAT_DATA0_REG[12]', bloc: 'B5[4]'} + RPT4_RESERVED6 : {show: n, blk : 0, word: 1, pos: 13, len : 1, start : 45, type : bool, wr_dis: null, rd_dis: null, alt : '', dict : '', desc: Reserved (used for four backups method), rloc: 'EFUSE_RD_REPEAT_DATA0_REG[13]', bloc: 'B5[5]'} + DIS_TWAI : {show: y, blk : 0, word: 1, pos: 14, len : 1, start : 46, type : bool, wr_dis : 2, rd_dis: null, alt : DIS_CAN, dict : '', desc: Set this bit to disable CAN function, rloc: 'EFUSE_RD_REPEAT_DATA0_REG[14]', bloc: 'B5[6]'} + JTAG_SEL_ENABLE : {show: y, blk : 0, word: 1, pos: 15, len : 1, start : 47, type : bool, wr_dis : 2, rd_dis: null, alt : '', dict : '', desc: Set this bit to enable selection between usb_to_jtag and pad_to_jtag through strapping gpio10 when both reg_dis_usb_jtag and reg_dis_pad_jtag are equal to 0, rloc: 'EFUSE_RD_REPEAT_DATA0_REG[15]', bloc: 'B5[7]'} + SOFT_DIS_JTAG : {show: y, blk : 0, word: 1, pos: 16, len : 3, start : 48, type : 'uint:3', wr_dis : 31, rd_dis: null, alt : '', dict : '', desc: Set these bits to disable JTAG in the soft way (odd number 1 means disable ). JTAG can be enabled in HMAC module, rloc: 'EFUSE_RD_REPEAT_DATA0_REG[18:16]', bloc: 'B6[2:0]'} + DIS_PAD_JTAG : {show: y, blk : 0, word: 1, pos: 19, len : 1, start : 51, type : bool, wr_dis : 2, rd_dis: null, alt : '', dict : '', desc: Set this bit to disable JTAG in the hard way. JTAG is disabled permanently, rloc: 'EFUSE_RD_REPEAT_DATA0_REG[19]', bloc: 'B6[3]'} + DIS_DOWNLOAD_MANUAL_ENCRYPT : {show: y, blk : 0, word: 1, pos: 20, len : 1, start : 52, type : bool, wr_dis : 2, rd_dis: null, alt : '', dict : '', desc: Set this bit to disable flash encryption when in download boot modes, rloc: 'EFUSE_RD_REPEAT_DATA0_REG[20]', bloc: 'B6[4]'} + USB_DREFH : {show: n, blk : 0, word: 1, pos: 21, len : 2, start : 53, type : 'uint:2', wr_dis : 30, rd_dis: null, alt : '', dict : '', desc: Controls single-end input threshold vrefh; 1.76 V to 2 V with step of 80 mV; stored in eFuse, rloc: 'EFUSE_RD_REPEAT_DATA0_REG[22:21]', bloc: 'B6[6:5]'} + USB_DREFL : {show: n, blk : 0, word: 1, pos: 23, len : 2, start : 55, type : 'uint:2', wr_dis : 30, rd_dis: null, alt : '', dict : '', desc: Controls single-end input threshold vrefl; 0.8 V to 1.04 V with step of 80 mV; stored in eFuse, rloc: 'EFUSE_RD_REPEAT_DATA0_REG[24:23]', bloc: 'B6[7],B7[0]'} + USB_EXCHG_PINS : {show: y, blk : 0, word: 1, pos: 25, len : 1, start : 57, type : bool, wr_dis : 30, rd_dis: null, alt : '', dict : '', desc: Set this bit to exchange USB D+ and D- pins, rloc: 'EFUSE_RD_REPEAT_DATA0_REG[25]', bloc: 'B7[1]'} + VDD_SPI_AS_GPIO : {show: y, blk : 0, word: 1, pos: 26, len : 1, start : 58, type : bool, wr_dis : 30, rd_dis: null, alt : '', dict : '', desc: Set this bit to vdd spi pin function as gpio, rloc: 'EFUSE_RD_REPEAT_DATA0_REG[26]', bloc: 'B7[2]'} + BTLC_GPIO_ENABLE : {show: n, blk : 0, word: 1, pos: 27, len : 2, start : 59, type : 'uint:2', wr_dis : 30, rd_dis: null, alt : '', dict : '', desc: Enable btlc gpio, rloc: 'EFUSE_RD_REPEAT_DATA0_REG[28:27]', bloc: 'B7[4:3]'} + POWERGLITCH_EN : {show: n, blk : 0, word: 1, pos: 29, len : 1, start : 61, type : bool, wr_dis : 3, rd_dis: null, alt : '', dict : '', desc: Set this bit to enable power glitch function, rloc: 'EFUSE_RD_REPEAT_DATA0_REG[29]', bloc: 'B7[5]'} + POWER_GLITCH_DSENSE : {show: n, blk : 0, word: 1, pos: 30, len : 2, start : 62, type : 'uint:2', wr_dis : 3, rd_dis: null, alt : '', dict : '', desc: Sample delay configuration of power glitch, rloc: 'EFUSE_RD_REPEAT_DATA0_REG[31:30]', bloc: 'B7[7:6]'} + RPT4_RESERVED2 : {show: n, blk : 0, word: 2, pos : 0, len : 16, start : 64, type : 'uint:16', wr_dis: null, rd_dis: null, alt : '', dict : '', desc: Reserved (used for four backups method), rloc: 'EFUSE_RD_REPEAT_DATA1_REG[15:0]', bloc: 'B8,B9'} + WDT_DELAY_SEL : {show: y, blk : 0, word: 2, pos: 16, len : 2, start : 80, type : 'uint:2', wr_dis : 3, rd_dis: null, alt : '', dict: '{0: "40000", 1: "80000", 2: "160000", 3: "320000"}', desc: RTC watchdog timeout threshold; in unit of slow clock cycle, rloc: 'EFUSE_RD_REPEAT_DATA1_REG[17:16]', bloc: 'B10[1:0]'} + SPI_BOOT_CRYPT_CNT : {show: y, blk : 0, word: 2, pos: 18, len : 3, start : 82, type : 'uint:3', wr_dis : 4, rd_dis: null, alt : '', dict: '{0: "Disable", 1: "Enable", 3: "Disable", 7: "Enable"}', desc: Enables flash encryption when 1 or 3 bits are set and disables otherwise, rloc: 'EFUSE_RD_REPEAT_DATA1_REG[20:18]', bloc: 'B10[4:2]'} + SECURE_BOOT_KEY_REVOKE0 : {show: y, blk : 0, word: 2, pos: 21, len : 1, start : 85, type : bool, wr_dis : 5, rd_dis: null, alt : '', dict : '', desc: Revoke 1st secure boot key, rloc: 'EFUSE_RD_REPEAT_DATA1_REG[21]', bloc: 'B10[5]'} + SECURE_BOOT_KEY_REVOKE1 : {show: y, blk : 0, word: 2, pos: 22, len : 1, start : 86, type : bool, wr_dis : 6, rd_dis: null, alt : '', dict : '', desc: Revoke 2nd secure boot key, rloc: 'EFUSE_RD_REPEAT_DATA1_REG[22]', bloc: 'B10[6]'} + SECURE_BOOT_KEY_REVOKE2 : {show: y, blk : 0, word: 2, pos: 23, len : 1, start : 87, type : bool, wr_dis : 7, rd_dis: null, alt : '', dict : '', desc: Revoke 3rd secure boot key, rloc: 'EFUSE_RD_REPEAT_DATA1_REG[23]', bloc: 'B10[7]'} + KEY_PURPOSE_0 : {show: y, blk : 0, word: 2, pos: 24, len : 4, start : 88, type : 'uint:4', wr_dis : 8, rd_dis: null, alt : KEY0_PURPOSE, dict : '', desc: Purpose of Key0, rloc: 'EFUSE_RD_REPEAT_DATA1_REG[27:24]', bloc: 'B11[3:0]'} + KEY_PURPOSE_1 : {show: y, blk : 0, word: 2, pos: 28, len : 4, start : 92, type : 'uint:4', wr_dis : 9, rd_dis: null, alt : KEY1_PURPOSE, dict : '', desc: Purpose of Key1, rloc: 'EFUSE_RD_REPEAT_DATA1_REG[31:28]', bloc: 'B11[7:4]'} + KEY_PURPOSE_2 : {show: y, blk : 0, word: 3, pos : 0, len : 4, start : 96, type : 'uint:4', wr_dis : 10, rd_dis: null, alt : KEY2_PURPOSE, dict : '', desc: Purpose of Key2, rloc: 'EFUSE_RD_REPEAT_DATA2_REG[3:0]', bloc: 'B12[3:0]'} + KEY_PURPOSE_3 : {show: y, blk : 0, word: 3, pos : 4, len : 4, start: 100, type : 'uint:4', wr_dis : 11, rd_dis: null, alt : KEY3_PURPOSE, dict : '', desc: Purpose of Key3, rloc: 'EFUSE_RD_REPEAT_DATA2_REG[7:4]', bloc: 'B12[7:4]'} + KEY_PURPOSE_4 : {show: y, blk : 0, word: 3, pos : 8, len : 4, start: 104, type : 'uint:4', wr_dis : 12, rd_dis: null, alt : KEY4_PURPOSE, dict : '', desc: Purpose of Key4, rloc: 'EFUSE_RD_REPEAT_DATA2_REG[11:8]', bloc: 'B13[3:0]'} + KEY_PURPOSE_5 : {show: y, blk : 0, word: 3, pos: 12, len : 4, start: 108, type : 'uint:4', wr_dis : 13, rd_dis: null, alt : KEY5_PURPOSE, dict : '', desc: Purpose of Key5, rloc: 'EFUSE_RD_REPEAT_DATA2_REG[15:12]', bloc: 'B13[7:4]'} + RPT4_RESERVED3 : {show: n, blk : 0, word: 3, pos: 16, len : 4, start: 112, type : 'uint:4', wr_dis: null, rd_dis: null, alt : '', dict : '', desc: Reserved (used for four backups method), rloc: 'EFUSE_RD_REPEAT_DATA2_REG[19:16]', bloc: 'B14[3:0]'} + SECURE_BOOT_EN : {show: y, blk : 0, word: 3, pos: 20, len : 1, start: 116, type : bool, wr_dis : 15, rd_dis: null, alt : '', dict : '', desc: Set this bit to enable secure boot, rloc: 'EFUSE_RD_REPEAT_DATA2_REG[20]', bloc: 'B14[4]'} + SECURE_BOOT_AGGRESSIVE_REVOKE : {show: y, blk : 0, word: 3, pos: 21, len : 1, start: 117, type : bool, wr_dis : 16, rd_dis: null, alt : '', dict : '', desc: Set this bit to enable revoking aggressive secure boot, rloc: 'EFUSE_RD_REPEAT_DATA2_REG[21]', bloc: 'B14[5]'} + RPT4_RESERVED0 : {show: n, blk : 0, word: 3, pos: 22, len : 6, start: 118, type : 'uint:6', wr_dis: null, rd_dis: null, alt : '', dict : '', desc: Reserved (used for four backups method), rloc: 'EFUSE_RD_REPEAT_DATA2_REG[27:22]', bloc: 'B14[7:6],B15[3:0]'} + FLASH_TPUW : {show: y, blk : 0, word: 3, pos: 28, len : 4, start: 124, type : 'uint:4', wr_dis : 18, rd_dis: null, alt : '', dict : '', desc: Configures flash waiting time after power-up; in unit of ms. If the value is less than 15; the waiting time is the configurable value; Otherwise; the waiting time is twice the configurable value, rloc: 'EFUSE_RD_REPEAT_DATA2_REG[31:28]', bloc: 'B15[7:4]'} + DIS_DOWNLOAD_MODE : {show: y, blk : 0, word: 4, pos : 0, len : 1, start: 128, type : bool, wr_dis : 18, rd_dis: null, alt : '', dict : '', desc: 'Set this bit to disable download mode (boot_mode[3:0] = 0; 1; 2; 3; 6; 7)', rloc: 'EFUSE_RD_REPEAT_DATA3_REG[0]', bloc: 'B16[0]'} + DIS_DIRECT_BOOT : {show: y, blk : 0, word: 4, pos : 1, len : 1, start: 129, type : bool, wr_dis : 18, rd_dis: null, alt : DIS_LEGACY_SPI_BOOT, dict : '', desc: Disable direct boot mode, rloc: 'EFUSE_RD_REPEAT_DATA3_REG[1]', bloc: 'B16[1]'} + DIS_USB_SERIAL_JTAG_ROM_PRINT : {show: y, blk : 0, word: 4, pos : 2, len : 1, start: 130, type : bool, wr_dis : 18, rd_dis: null, alt : UART_PRINT_CHANNEL, dict: '{0: "Enable", 1: "Disable"}', desc: USB printing, rloc: 'EFUSE_RD_REPEAT_DATA3_REG[2]', bloc: 'B16[2]'} + FLASH_ECC_MODE : {show: n, blk : 0, word: 4, pos : 3, len : 1, start: 131, type : bool, wr_dis : 18, rd_dis: null, alt : '', dict: '{0: "ROM would Enable Flash ECC 16to18 byte mode", 1: "ROM would use 16to17 byte mode"}', desc: ECC mode in ROM, rloc: 'EFUSE_RD_REPEAT_DATA3_REG[3]', bloc: 'B16[3]'} + DIS_USB_SERIAL_JTAG_DOWNLOAD_MODE: {show: y, blk : 0, word: 4, pos : 4, len : 1, start: 132, type : bool, wr_dis : 18, rd_dis: null, alt : DIS_USB_DOWNLOAD_MODE, dict : '', desc: Disable UART download mode through USB-Serial-JTAG, rloc: 'EFUSE_RD_REPEAT_DATA3_REG[4]', bloc: 'B16[4]'} + ENABLE_SECURITY_DOWNLOAD : {show: y, blk : 0, word: 4, pos : 5, len : 1, start: 133, type : bool, wr_dis : 18, rd_dis: null, alt : '', dict : '', desc: Set this bit to enable secure UART download mode, rloc: 'EFUSE_RD_REPEAT_DATA3_REG[5]', bloc: 'B16[5]'} + UART_PRINT_CONTROL : {show: y, blk : 0, word: 4, pos : 6, len : 2, start: 134, type : 'uint:2', wr_dis : 18, rd_dis: null, alt : '', dict: '{0: "Enable", 1: "Enable when GPIO8 is low at reset", 2: "Enable when GPIO8 is high at reset", 3: "Disable"}', desc: Set the default UARTboot message output mode, rloc: 'EFUSE_RD_REPEAT_DATA3_REG[7:6]', bloc: 'B16[7:6]'} + PIN_POWER_SELECTION : {show: n, blk : 0, word: 4, pos : 8, len : 1, start: 136, type : bool, wr_dis : 18, rd_dis: null, alt : '', dict: '{0: "VDD3P3_CPU", 1: "VDD_SPI"}', desc: GPIO33-GPIO37 power supply selection in ROM code, rloc: 'EFUSE_RD_REPEAT_DATA3_REG[8]', bloc: 'B17[0]'} + FLASH_TYPE : {show: n, blk : 0, word: 4, pos : 9, len : 1, start: 137, type : bool, wr_dis : 18, rd_dis: null, alt : '', dict: '{0: "4 data lines", 1: "8 data lines"}', desc: Maximum lines of SPI flash, rloc: 'EFUSE_RD_REPEAT_DATA3_REG[9]', bloc: 'B17[1]'} + FLASH_PAGE_SIZE : {show: n, blk : 0, word: 4, pos: 10, len : 2, start: 138, type : 'uint:2', wr_dis : 18, rd_dis: null, alt : '', dict : '', desc: Set Flash page size, rloc: 'EFUSE_RD_REPEAT_DATA3_REG[11:10]', bloc: 'B17[3:2]'} + FLASH_ECC_EN : {show: n, blk : 0, word: 4, pos: 12, len : 1, start: 140, type : bool, wr_dis : 18, rd_dis: null, alt : '', dict : '', desc: Set 1 to enable ECC for flash boot, rloc: 'EFUSE_RD_REPEAT_DATA3_REG[12]', bloc: 'B17[4]'} + FORCE_SEND_RESUME : {show: y, blk : 0, word: 4, pos: 13, len : 1, start: 141, type : bool, wr_dis : 18, rd_dis: null, alt : '', dict : '', desc: Set this bit to force ROM code to send a resume command during SPI boot, rloc: 'EFUSE_RD_REPEAT_DATA3_REG[13]', bloc: 'B17[5]'} + SECURE_VERSION : {show: y, blk : 0, word: 4, pos: 14, len : 16, start: 142, type : 'uint:16', wr_dis : 18, rd_dis: null, alt : '', dict : '', desc: Secure version (used by ESP-IDF anti-rollback feature), rloc: 'EFUSE_RD_REPEAT_DATA3_REG[29:14]', bloc: 'B17[7:6],B18,B19[5:0]'} + RESERVED_0_158 : {show: n, blk : 0, word: 4, pos: 30, len : 1, start: 158, type : bool, wr_dis: null, rd_dis: null, alt : '', dict : '', desc: reserved, rloc: 'EFUSE_RD_REPEAT_DATA3_REG[30]', bloc: 'B19[6]'} + ERR_RST_ENABLE : {show: y, blk : 0, word: 4, pos: 31, len : 1, start: 159, type : bool, wr_dis : 19, rd_dis: null, alt : '', dict: '{0: "without check", 1: "with check"}', desc: Use BLOCK0 to check error record registers, rloc: 'EFUSE_RD_REPEAT_DATA3_REG[31]', bloc: 'B19[7]'} + DISABLE_WAFER_VERSION_MAJOR : {show: y, blk : 0, word: 5, pos : 0, len : 1, start: 160, type : bool, wr_dis : 19, rd_dis: null, alt : '', dict : '', desc: Disables check of wafer version major, rloc: 'EFUSE_RD_REPEAT_DATA4_REG[0]', bloc: 'B20[0]'} + DISABLE_BLK_VERSION_MAJOR : {show: y, blk : 0, word: 5, pos : 1, len : 1, start: 161, type : bool, wr_dis : 19, rd_dis: null, alt : '', dict : '', desc: Disables check of blk version major, rloc: 'EFUSE_RD_REPEAT_DATA4_REG[1]', bloc: 'B20[1]'} + RESERVED_0_162 : {show: n, blk : 0, word: 5, pos : 2, len : 22, start: 162, type : 'uint:22', wr_dis: null, rd_dis: null, alt : '', dict : '', desc: reserved, rloc: 'EFUSE_RD_REPEAT_DATA4_REG[23:2]', bloc: 'B20[7:2],B21,B22'} + MAC : {show: y, blk : 1, word: 0, pos : 0, len : 48, start : 0, type : 'bytes:6', wr_dis : 20, rd_dis: null, alt : MAC_FACTORY, dict : '', desc: MAC address, rloc: EFUSE_RD_MAC_SPI_SYS_0_REG, bloc: 'B0,B1,B2,B3,B4,B5'} + SPI_PAD_CONFIG_CLK : {show: y, blk : 1, word: 1, pos: 16, len : 6, start : 48, type : 'uint:6', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: SPI PAD CLK, rloc: 'EFUSE_RD_MAC_SPI_SYS_1_REG[21:16]', bloc: 'B6[5:0]'} + SPI_PAD_CONFIG_Q : {show: y, blk : 1, word: 1, pos: 22, len : 6, start : 54, type : 'uint:6', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: SPI PAD Q(D1), rloc: 'EFUSE_RD_MAC_SPI_SYS_1_REG[27:22]', bloc: 'B6[7:6],B7[3:0]'} + SPI_PAD_CONFIG_D : {show: y, blk : 1, word: 1, pos: 28, len : 6, start : 60, type : 'uint:6', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: SPI PAD D(D0), rloc: 'EFUSE_RD_MAC_SPI_SYS_1_REG[31:28]', bloc: 'B7[7:4],B8[1:0]'} + SPI_PAD_CONFIG_CS : {show: y, blk : 1, word: 2, pos : 2, len : 6, start : 66, type : 'uint:6', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: SPI PAD CS, rloc: 'EFUSE_RD_MAC_SPI_SYS_2_REG[7:2]', bloc: 'B8[7:2]'} + SPI_PAD_CONFIG_HD : {show: y, blk : 1, word: 2, pos : 8, len : 6, start : 72, type : 'uint:6', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: SPI PAD HD(D3), rloc: 'EFUSE_RD_MAC_SPI_SYS_2_REG[13:8]', bloc: 'B9[5:0]'} + SPI_PAD_CONFIG_WP : {show: y, blk : 1, word: 2, pos: 14, len : 6, start : 78, type : 'uint:6', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: SPI PAD WP(D2), rloc: 'EFUSE_RD_MAC_SPI_SYS_2_REG[19:14]', bloc: 'B9[7:6],B10[3:0]'} + SPI_PAD_CONFIG_DQS : {show: y, blk : 1, word: 2, pos: 20, len : 6, start : 84, type : 'uint:6', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: SPI PAD DQS, rloc: 'EFUSE_RD_MAC_SPI_SYS_2_REG[25:20]', bloc: 'B10[7:4],B11[1:0]'} + SPI_PAD_CONFIG_D4 : {show: y, blk : 1, word: 2, pos: 26, len : 6, start : 90, type : 'uint:6', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: SPI PAD D4, rloc: 'EFUSE_RD_MAC_SPI_SYS_2_REG[31:26]', bloc: 'B11[7:2]'} + SPI_PAD_CONFIG_D5 : {show: y, blk : 1, word: 3, pos : 0, len : 6, start : 96, type : 'uint:6', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: SPI PAD D5, rloc: 'EFUSE_RD_MAC_SPI_SYS_3_REG[5:0]', bloc: 'B12[5:0]'} + SPI_PAD_CONFIG_D6 : {show: y, blk : 1, word: 3, pos : 6, len : 6, start: 102, type : 'uint:6', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: SPI PAD D6, rloc: 'EFUSE_RD_MAC_SPI_SYS_3_REG[11:6]', bloc: 'B12[7:6],B13[3:0]'} + SPI_PAD_CONFIG_D7 : {show: y, blk : 1, word: 3, pos: 12, len : 6, start: 108, type : 'uint:6', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: SPI PAD D7, rloc: 'EFUSE_RD_MAC_SPI_SYS_3_REG[17:12]', bloc: 'B13[7:4],B14[1:0]'} + WAFER_VERSION_MINOR_LO : {show: y, blk : 1, word: 3, pos: 18, len : 3, start: 114, type : 'uint:3', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: WAFER_VERSION_MINOR least significant bits, rloc: 'EFUSE_RD_MAC_SPI_SYS_3_REG[20:18]', bloc: 'B14[4:2]'} + PKG_VERSION : {show: y, blk : 1, word: 3, pos: 21, len : 3, start: 117, type : 'uint:3', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: Package version, rloc: 'EFUSE_RD_MAC_SPI_SYS_3_REG[23:21]', bloc: 'B14[7:5]'} + BLK_VERSION_MINOR : {show: y, blk : 1, word: 3, pos: 24, len : 3, start: 120, type : 'uint:3', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: BLK_VERSION_MINOR, rloc: 'EFUSE_RD_MAC_SPI_SYS_3_REG[26:24]', bloc: 'B15[2:0]'} + FLASH_CAP : {show: y, blk : 1, word: 3, pos: 27, len : 3, start: 123, type : 'uint:3', wr_dis : 20, rd_dis: null, alt : '', dict: '{0: "None", 1: "4M", 2: "2M", 3: "1M", 4: "8M"}', desc: Flash capacity, rloc: 'EFUSE_RD_MAC_SPI_SYS_3_REG[29:27]', bloc: 'B15[5:3]'} + FLASH_TEMP : {show: y, blk : 1, word: 3, pos: 30, len : 2, start: 126, type : 'uint:2', wr_dis : 20, rd_dis: null, alt : '', dict: '{0: "None", 1: "105C", 2: "85C"}', desc: Flash temperature, rloc: 'EFUSE_RD_MAC_SPI_SYS_3_REG[31:30]', bloc: 'B15[7:6]'} + FLASH_VENDOR : {show: y, blk : 1, word: 4, pos : 0, len : 3, start: 128, type : 'uint:3', wr_dis : 20, rd_dis: null, alt : '', dict: '{0: "None", 1: "XMC", 2: "GD", 3: "FM", 4: "TT", 5: "ZBIT"}', desc: Flash vendor, rloc: 'EFUSE_RD_MAC_SPI_SYS_4_REG[2:0]', bloc: 'B16[2:0]'} + RESERVED_1_131 : {show: n, blk : 1, word: 4, pos : 3, len : 4, start: 131, type : 'uint:4', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: reserved, rloc: 'EFUSE_RD_MAC_SPI_SYS_4_REG[6:3]', bloc: 'B16[6:3]'} + K_RTC_LDO : {show: y, blk : 1, word: 4, pos : 7, len : 7, start: 135, type : 'uint:7', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: BLOCK1 K_RTC_LDO, rloc: 'EFUSE_RD_MAC_SPI_SYS_4_REG[13:7]', bloc: 'B16[7],B17[5:0]'} + K_DIG_LDO : {show: y, blk : 1, word: 4, pos: 14, len : 7, start: 142, type : 'uint:7', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: BLOCK1 K_DIG_LDO, rloc: 'EFUSE_RD_MAC_SPI_SYS_4_REG[20:14]', bloc: 'B17[7:6],B18[4:0]'} + V_RTC_DBIAS20 : {show: y, blk : 1, word: 4, pos: 21, len : 8, start: 149, type : 'uint:8', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: BLOCK1 voltage of rtc dbias20, rloc: 'EFUSE_RD_MAC_SPI_SYS_4_REG[28:21]', bloc: 'B18[7:5],B19[4:0]'} + V_DIG_DBIAS20 : {show: y, blk : 1, word: 4, pos: 29, len : 8, start: 157, type : 'uint:8', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: BLOCK1 voltage of digital dbias20, rloc: 'EFUSE_RD_MAC_SPI_SYS_4_REG[31:29]', bloc: 'B19[7:5],B20[4:0]'} + DIG_DBIAS_HVT : {show: y, blk : 1, word: 5, pos : 5, len : 5, start: 165, type : 'uint:5', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: BLOCK1 digital dbias when hvt, rloc: 'EFUSE_RD_MAC_SPI_SYS_5_REG[9:5]', bloc: 'B20[7:5],B21[1:0]'} + THRES_HVT : {show: y, blk : 1, word: 5, pos: 10, len : 10, start: 170, type : 'uint:10', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: BLOCK1 pvt threshold when hvt, rloc: 'EFUSE_RD_MAC_SPI_SYS_5_REG[19:10]', bloc: 'B21[7:2],B22[3:0]'} + RESERVED_1_180 : {show: n, blk : 1, word: 5, pos: 20, len : 3, start: 180, type : 'uint:3', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: reserved, rloc: 'EFUSE_RD_MAC_SPI_SYS_5_REG[22:20]', bloc: 'B22[6:4]'} + WAFER_VERSION_MINOR_HI : {show: y, blk : 1, word: 5, pos: 23, len : 1, start: 183, type : bool, wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: WAFER_VERSION_MINOR most significant bit, rloc: 'EFUSE_RD_MAC_SPI_SYS_5_REG[23]', bloc: 'B22[7]'} + WAFER_VERSION_MAJOR : {show: y, blk : 1, word: 5, pos: 24, len : 2, start: 184, type : 'uint:2', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: WAFER_VERSION_MAJOR, rloc: 'EFUSE_RD_MAC_SPI_SYS_5_REG[25:24]', bloc: 'B23[1:0]'} + RESERVED_1_186 : {show: n, blk : 1, word: 5, pos: 26, len : 6, start: 186, type : 'uint:6', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: reserved, rloc: 'EFUSE_RD_MAC_SPI_SYS_5_REG[31:26]', bloc: 'B23[7:2]'} + OPTIONAL_UNIQUE_ID : {show: y, blk : 2, word: 0, pos : 0, len: 128, start : 0, type: 'bytes:16', wr_dis : 21, rd_dis: null, alt : '', dict : '', desc: Optional unique 128-bit ID, rloc: EFUSE_RD_SYS_PART1_DATA0_REG, bloc: 'B0,B1,B2,B3,B4,B5,B6,B7,B8,B9,B10,B11,B12,B13,B14,B15'} + BLK_VERSION_MAJOR : {show: y, blk : 2, word: 4, pos : 0, len : 2, start: 128, type : 'uint:2', wr_dis : 21, rd_dis: null, alt : '', dict: '{0: "No calibration", 1: "With calibration"}', desc: BLK_VERSION_MAJOR of BLOCK2, rloc: 'EFUSE_RD_SYS_PART1_DATA4_REG[1:0]', bloc: 'B16[1:0]'} + RESERVED_2_130 : {show: n, blk : 2, word: 4, pos : 2, len : 1, start: 130, type : bool, wr_dis : 21, rd_dis: null, alt : '', dict : '', desc: reserved, rloc: 'EFUSE_RD_SYS_PART1_DATA4_REG[2]', bloc: 'B16[2]'} + TEMP_CALIB : {show: y, blk : 2, word: 4, pos : 3, len : 9, start: 131, type : 'uint:9', wr_dis : 21, rd_dis: null, alt : '', dict : '', desc: Temperature calibration data, rloc: 'EFUSE_RD_SYS_PART1_DATA4_REG[11:3]', bloc: 'B16[7:3],B17[3:0]'} + OCODE : {show: y, blk : 2, word: 4, pos: 12, len : 8, start: 140, type : 'uint:8', wr_dis : 21, rd_dis: null, alt : '', dict : '', desc: ADC OCode, rloc: 'EFUSE_RD_SYS_PART1_DATA4_REG[19:12]', bloc: 'B17[7:4],B18[3:0]'} + ADC1_INIT_CODE_ATTEN0 : {show: y, blk : 2, word: 4, pos: 20, len : 10, start: 148, type : 'uint:10', wr_dis : 21, rd_dis: null, alt : '', dict : '', desc: ADC1 init code at atten0, rloc: 'EFUSE_RD_SYS_PART1_DATA4_REG[29:20]', bloc: 'B18[7:4],B19[5:0]'} + ADC1_INIT_CODE_ATTEN1 : {show: y, blk : 2, word: 4, pos: 30, len : 10, start: 158, type : 'uint:10', wr_dis : 21, rd_dis: null, alt : '', dict : '', desc: ADC1 init code at atten1, rloc: 'EFUSE_RD_SYS_PART1_DATA4_REG[31:30]', bloc: 'B19[7:6],B20'} + ADC1_INIT_CODE_ATTEN2 : {show: y, blk : 2, word: 5, pos : 8, len : 10, start: 168, type : 'uint:10', wr_dis : 21, rd_dis: null, alt : '', dict : '', desc: ADC1 init code at atten2, rloc: 'EFUSE_RD_SYS_PART1_DATA5_REG[17:8]', bloc: 'B21,B22[1:0]'} + ADC1_INIT_CODE_ATTEN3 : {show: y, blk : 2, word: 5, pos: 18, len : 10, start: 178, type : 'uint:10', wr_dis : 21, rd_dis: null, alt : '', dict : '', desc: ADC1 init code at atten3, rloc: 'EFUSE_RD_SYS_PART1_DATA5_REG[27:18]', bloc: 'B22[7:2],B23[3:0]'} + ADC1_CAL_VOL_ATTEN0 : {show: y, blk : 2, word: 5, pos: 28, len : 10, start: 188, type : 'uint:10', wr_dis : 21, rd_dis: null, alt : '', dict : '', desc: ADC1 calibration voltage at atten0, rloc: 'EFUSE_RD_SYS_PART1_DATA5_REG[31:28]', bloc: 'B23[7:4],B24[5:0]'} + ADC1_CAL_VOL_ATTEN1 : {show: y, blk : 2, word: 6, pos : 6, len : 10, start: 198, type : 'uint:10', wr_dis : 21, rd_dis: null, alt : '', dict : '', desc: ADC1 calibration voltage at atten1, rloc: 'EFUSE_RD_SYS_PART1_DATA6_REG[15:6]', bloc: 'B24[7:6],B25'} + ADC1_CAL_VOL_ATTEN2 : {show: y, blk : 2, word: 6, pos: 16, len : 10, start: 208, type : 'uint:10', wr_dis : 21, rd_dis: null, alt : '', dict : '', desc: ADC1 calibration voltage at atten2, rloc: 'EFUSE_RD_SYS_PART1_DATA6_REG[25:16]', bloc: 'B26,B27[1:0]'} + ADC1_CAL_VOL_ATTEN3 : {show: y, blk : 2, word: 6, pos: 26, len : 10, start: 218, type : 'uint:10', wr_dis : 21, rd_dis: null, alt : '', dict : '', desc: ADC1 calibration voltage at atten3, rloc: 'EFUSE_RD_SYS_PART1_DATA6_REG[31:26]', bloc: 'B27[7:2],B28[3:0]'} + RESERVED_2_228 : {show: n, blk : 2, word: 7, pos : 4, len : 28, start: 228, type : 'uint:28', wr_dis : 21, rd_dis: null, alt : '', dict : '', desc: reserved, rloc: 'EFUSE_RD_SYS_PART1_DATA7_REG[31:4]', bloc: 'B28[7:4],B29,B30,B31'} + BLOCK_USR_DATA : {show: y, blk : 3, word: 0, pos : 0, len: 192, start : 0, type: 'bytes:24', wr_dis : 22, rd_dis: null, alt : USER_DATA, dict : '', desc: User data, rloc: EFUSE_RD_USR_DATA0_REG, bloc: 'B0,B1,B2,B3,B4,B5,B6,B7,B8,B9,B10,B11,B12,B13,B14,B15,B16,B17,B18,B19,B20,B21,B22,B23'} + RESERVED_3_192 : {show: n, blk : 3, word: 6, pos : 0, len : 8, start: 192, type : 'uint:8', wr_dis : 22, rd_dis: null, alt : '', dict : '', desc: reserved, rloc: 'EFUSE_RD_USR_DATA6_REG[7:0]', bloc: B24} + CUSTOM_MAC : {show: y, blk : 3, word: 6, pos : 8, len : 48, start: 200, type : 'bytes:6', wr_dis : 22, rd_dis: null, alt: MAC_CUSTOM USER_DATA_MAC_CUSTOM, dict : '', desc: Custom MAC address, rloc: 'EFUSE_RD_USR_DATA6_REG[31:8]', bloc: 'B25,B26,B27,B28,B29,B30'} + RESERVED_3_248 : {show: n, blk : 3, word: 7, pos: 24, len : 8, start: 248, type : 'uint:8', wr_dis : 22, rd_dis: null, alt : '', dict : '', desc: reserved, rloc: 'EFUSE_RD_USR_DATA7_REG[31:24]', bloc: B31} + BLOCK_KEY0 : {show: y, blk : 4, word: 0, pos : 0, len: 256, start : 0, type: 'bytes:32', wr_dis : 23, rd_dis : 0, alt : KEY0, dict : '', desc: Key0 or user data, rloc: EFUSE_RD_KEY0_DATA0_REG, bloc: 'B0,B1,B2,B3,B4,B5,B6,B7,B8,B9,B10,B11,B12,B13,B14,B15,B16,B17,B18,B19,B20,B21,B22,B23,B24,B25,B26,B27,B28,B29,B30,B31'} + BLOCK_KEY1 : {show: y, blk : 5, word: 0, pos : 0, len: 256, start : 0, type: 'bytes:32', wr_dis : 24, rd_dis : 1, alt : KEY1, dict : '', desc: Key1 or user data, rloc: EFUSE_RD_KEY1_DATA0_REG, bloc: 'B0,B1,B2,B3,B4,B5,B6,B7,B8,B9,B10,B11,B12,B13,B14,B15,B16,B17,B18,B19,B20,B21,B22,B23,B24,B25,B26,B27,B28,B29,B30,B31'} + BLOCK_KEY2 : {show: y, blk : 6, word: 0, pos : 0, len: 256, start : 0, type: 'bytes:32', wr_dis : 25, rd_dis : 2, alt : KEY2, dict : '', desc: Key2 or user data, rloc: EFUSE_RD_KEY2_DATA0_REG, bloc: 'B0,B1,B2,B3,B4,B5,B6,B7,B8,B9,B10,B11,B12,B13,B14,B15,B16,B17,B18,B19,B20,B21,B22,B23,B24,B25,B26,B27,B28,B29,B30,B31'} + BLOCK_KEY3 : {show: y, blk : 7, word: 0, pos : 0, len: 256, start : 0, type: 'bytes:32', wr_dis : 26, rd_dis : 3, alt : KEY3, dict : '', desc: Key3 or user data, rloc: EFUSE_RD_KEY3_DATA0_REG, bloc: 'B0,B1,B2,B3,B4,B5,B6,B7,B8,B9,B10,B11,B12,B13,B14,B15,B16,B17,B18,B19,B20,B21,B22,B23,B24,B25,B26,B27,B28,B29,B30,B31'} + BLOCK_KEY4 : {show: y, blk : 8, word: 0, pos : 0, len: 256, start : 0, type: 'bytes:32', wr_dis : 27, rd_dis : 4, alt : KEY4, dict : '', desc: Key4 or user data, rloc: EFUSE_RD_KEY4_DATA0_REG, bloc: 'B0,B1,B2,B3,B4,B5,B6,B7,B8,B9,B10,B11,B12,B13,B14,B15,B16,B17,B18,B19,B20,B21,B22,B23,B24,B25,B26,B27,B28,B29,B30,B31'} + BLOCK_KEY5 : {show: y, blk : 9, word: 0, pos : 0, len: 256, start : 0, type: 'bytes:32', wr_dis : 28, rd_dis : 5, alt : KEY5, dict : '', desc: Key5 or user data, rloc: EFUSE_RD_KEY5_DATA0_REG, bloc: 'B0,B1,B2,B3,B4,B5,B6,B7,B8,B9,B10,B11,B12,B13,B14,B15,B16,B17,B18,B19,B20,B21,B22,B23,B24,B25,B26,B27,B28,B29,B30,B31'} + BLOCK_SYS_DATA2 : {show: y, blk: 10, word: 0, pos : 0, len: 256, start : 0, type: 'bytes:32', wr_dis : 29, rd_dis : 6, alt : SYS_DATA_PART2, dict : '', desc: System data part 2 (reserved), rloc: EFUSE_RD_SYS_PART2_DATA0_REG, bloc: 'B0,B1,B2,B3,B4,B5,B6,B7,B8,B9,B10,B11,B12,B13,B14,B15,B16,B17,B18,B19,B20,B21,B22,B23,B24,B25,B26,B27,B28,B29,B30,B31'} diff --git a/code/.venv/lib/python3.12/site-packages/espefuse/efuse_defs/esp32c6.yaml b/code/.venv/lib/python3.12/site-packages/espefuse/efuse_defs/esp32c6.yaml new file mode 100644 index 0000000..aee9430 --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/espefuse/efuse_defs/esp32c6.yaml @@ -0,0 +1,106 @@ +VER_NO: 709e8ea096e8a03a10006d40d5451a49 +EFUSES: + WR_DIS : {show: y, blk : 0, word: 0, pos : 0, len : 32, start : 0, type : 'uint:32', wr_dis: null, rd_dis: null, alt : '', dict : '', desc: Disable programming of individual eFuses, rloc: EFUSE_RD_WR_DIS_REG, bloc: 'B0,B1,B2,B3'} + RD_DIS : {show: y, blk : 0, word: 1, pos : 0, len : 7, start : 32, type : 'uint:7', wr_dis : 0, rd_dis: null, alt : '', dict : '', desc: Disable reading from BlOCK4-10, rloc: 'EFUSE_RD_REPEAT_DATA0_REG[6:0]', bloc: 'B4[6:0]'} + SWAP_UART_SDIO_EN : {show: y, blk : 0, word: 1, pos : 7, len : 1, start : 39, type : bool, wr_dis : 2, rd_dis: null, alt : '', dict : '', desc: 'Represents whether pad of uart and sdio is swapped or not. 1: swapped. 0: not swapped', rloc: 'EFUSE_RD_REPEAT_DATA0_REG[7]', bloc: 'B4[7]'} + DIS_ICACHE : {show: y, blk : 0, word: 1, pos : 8, len : 1, start : 40, type : bool, wr_dis : 2, rd_dis: null, alt : '', dict : '', desc: 'Represents whether icache is disabled or enabled. 1: disabled. 0: enabled', rloc: 'EFUSE_RD_REPEAT_DATA0_REG[8]', bloc: 'B5[0]'} + DIS_USB_JTAG : {show: y, blk : 0, word: 1, pos : 9, len : 1, start : 41, type : bool, wr_dis : 2, rd_dis: null, alt : '', dict : '', desc: 'Represents whether the function of usb switch to jtag is disabled or enabled. 1: disabled. 0: enabled', rloc: 'EFUSE_RD_REPEAT_DATA0_REG[9]', bloc: 'B5[1]'} + DIS_DOWNLOAD_ICACHE : {show: y, blk : 0, word: 1, pos: 10, len : 1, start : 42, type : bool, wr_dis : 2, rd_dis: null, alt : '', dict : '', desc: 'Represents whether icache is disabled or enabled in Download mode. 1: disabled. 0: enabled', rloc: 'EFUSE_RD_REPEAT_DATA0_REG[10]', bloc: 'B5[2]'} + DIS_USB_SERIAL_JTAG : {show: y, blk : 0, word: 1, pos: 11, len : 1, start : 43, type : bool, wr_dis : 2, rd_dis: null, alt : '', dict : '', desc: 'Represents whether USB-Serial-JTAG is disabled or enabled. 1: disabled. 0: enabled', rloc: 'EFUSE_RD_REPEAT_DATA0_REG[11]', bloc: 'B5[3]'} + DIS_FORCE_DOWNLOAD : {show: y, blk : 0, word: 1, pos: 12, len : 1, start : 44, type : bool, wr_dis : 2, rd_dis: null, alt : '', dict : '', desc: 'Represents whether the function that forces chip into download mode is disabled or enabled. 1: disabled. 0: enabled', rloc: 'EFUSE_RD_REPEAT_DATA0_REG[12]', bloc: 'B5[4]'} + SPI_DOWNLOAD_MSPI_DIS : {show: y, blk : 0, word: 1, pos: 13, len : 1, start : 45, type : bool, wr_dis : 17, rd_dis: null, alt : '', dict : '', desc: 'Represents whether SPI0 controller during boot_mode_download is disabled or enabled. 1: disabled. 0: enabled', rloc: 'EFUSE_RD_REPEAT_DATA0_REG[13]', bloc: 'B5[5]'} + DIS_TWAI : {show: y, blk : 0, word: 1, pos: 14, len : 1, start : 46, type : bool, wr_dis : 2, rd_dis: null, alt : DIS_CAN, dict : '', desc: 'Represents whether TWAI function is disabled or enabled. 1: disabled. 0: enabled', rloc: 'EFUSE_RD_REPEAT_DATA0_REG[14]', bloc: 'B5[6]'} + JTAG_SEL_ENABLE : {show: y, blk : 0, word: 1, pos: 15, len : 1, start : 47, type : bool, wr_dis : 2, rd_dis: null, alt : '', dict : '', desc: 'Represents whether the selection between usb_to_jtag and pad_to_jtag through strapping gpio15 when both EFUSE_DIS_PAD_JTAG and EFUSE_DIS_USB_JTAG are equal to 0 is enabled or disabled. 1: enabled. 0: disabled', rloc: 'EFUSE_RD_REPEAT_DATA0_REG[15]', bloc: 'B5[7]'} + SOFT_DIS_JTAG : {show: y, blk : 0, word: 1, pos: 16, len : 3, start : 48, type : 'uint:3', wr_dis : 31, rd_dis: null, alt : '', dict : '', desc: 'Represents whether JTAG is disabled in soft way. Odd number: disabled. Even number: enabled', rloc: 'EFUSE_RD_REPEAT_DATA0_REG[18:16]', bloc: 'B6[2:0]'} + DIS_PAD_JTAG : {show: y, blk : 0, word: 1, pos: 19, len : 1, start : 51, type : bool, wr_dis : 2, rd_dis: null, alt : '', dict : '', desc: 'Represents whether JTAG is disabled in the hard way(permanently). 1: disabled. 0: enabled', rloc: 'EFUSE_RD_REPEAT_DATA0_REG[19]', bloc: 'B6[3]'} + DIS_DOWNLOAD_MANUAL_ENCRYPT : {show: y, blk : 0, word: 1, pos: 20, len : 1, start : 52, type : bool, wr_dis : 2, rd_dis: null, alt : '', dict : '', desc: 'Represents whether flash encrypt function is disabled or enabled(except in SPI boot mode). 1: disabled. 0: enabled', rloc: 'EFUSE_RD_REPEAT_DATA0_REG[20]', bloc: 'B6[4]'} + USB_DREFH : {show: n, blk : 0, word: 1, pos: 21, len : 2, start : 53, type : 'uint:2', wr_dis : 30, rd_dis: null, alt : '', dict : '', desc: Represents the single-end input threhold vrefh; 1.76 V to 2 V with step of 80 mV, rloc: 'EFUSE_RD_REPEAT_DATA0_REG[22:21]', bloc: 'B6[6:5]'} + USB_DREFL : {show: n, blk : 0, word: 1, pos: 23, len : 2, start : 55, type : 'uint:2', wr_dis : 30, rd_dis: null, alt : '', dict : '', desc: Represents the single-end input threhold vrefl; 1.76 V to 2 V with step of 80 mV, rloc: 'EFUSE_RD_REPEAT_DATA0_REG[24:23]', bloc: 'B6[7],B7[0]'} + USB_EXCHG_PINS : {show: y, blk : 0, word: 1, pos: 25, len : 1, start : 57, type : bool, wr_dis : 30, rd_dis: null, alt : '', dict : '', desc: 'Represents whether the D+ and D- pins is exchanged. 1: exchanged. 0: not exchanged', rloc: 'EFUSE_RD_REPEAT_DATA0_REG[25]', bloc: 'B7[1]'} + VDD_SPI_AS_GPIO : {show: y, blk : 0, word: 1, pos: 26, len : 1, start : 58, type : bool, wr_dis : 30, rd_dis: null, alt : '', dict : '', desc: 'Represents whether vdd spi pin is functioned as gpio. 1: functioned. 0: not functioned', rloc: 'EFUSE_RD_REPEAT_DATA0_REG[26]', bloc: 'B7[2]'} + RPT4_RESERVED0_2 : {show: n, blk : 0, word: 1, pos: 27, len : 2, start : 59, type : 'uint:2', wr_dis: null, rd_dis: null, alt : '', dict : '', desc: Reserved, rloc: 'EFUSE_RD_REPEAT_DATA0_REG[28:27]', bloc: 'B7[4:3]'} + RPT4_RESERVED0_1 : {show: n, blk : 0, word: 1, pos: 29, len : 1, start : 61, type : bool, wr_dis: null, rd_dis: null, alt : '', dict : '', desc: Reserved, rloc: 'EFUSE_RD_REPEAT_DATA0_REG[29]', bloc: 'B7[5]'} + RPT4_RESERVED0_0 : {show: n, blk : 0, word: 1, pos: 30, len : 2, start : 62, type : 'uint:2', wr_dis: null, rd_dis: null, alt : '', dict : '', desc: Reserved, rloc: 'EFUSE_RD_REPEAT_DATA0_REG[31:30]', bloc: 'B7[7:6]'} + RPT4_RESERVED1_0 : {show: n, blk : 0, word: 2, pos : 0, len : 16, start : 64, type : 'uint:16', wr_dis: null, rd_dis: null, alt : '', dict : '', desc: Reserved, rloc: 'EFUSE_RD_REPEAT_DATA1_REG[15:0]', bloc: 'B8,B9'} + WDT_DELAY_SEL : {show: y, blk : 0, word: 2, pos: 16, len : 2, start : 80, type : 'uint:2', wr_dis : 3, rd_dis: null, alt : '', dict : '', desc: 'Represents whether RTC watchdog timeout threshold is selected at startup. 1: selected. 0: not selected', rloc: 'EFUSE_RD_REPEAT_DATA1_REG[17:16]', bloc: 'B10[1:0]'} + SPI_BOOT_CRYPT_CNT : {show: y, blk : 0, word: 2, pos: 18, len : 3, start : 82, type : 'uint:3', wr_dis : 4, rd_dis: null, alt : '', dict: '{0: "Disable", 1: "Enable", 3: "Disable", 7: "Enable"}', desc: Enables flash encryption when 1 or 3 bits are set and disables otherwise, rloc: 'EFUSE_RD_REPEAT_DATA1_REG[20:18]', bloc: 'B10[4:2]'} + SECURE_BOOT_KEY_REVOKE0 : {show: y, blk : 0, word: 2, pos: 21, len : 1, start : 85, type : bool, wr_dis : 5, rd_dis: null, alt : '', dict : '', desc: Revoke 1st secure boot key, rloc: 'EFUSE_RD_REPEAT_DATA1_REG[21]', bloc: 'B10[5]'} + SECURE_BOOT_KEY_REVOKE1 : {show: y, blk : 0, word: 2, pos: 22, len : 1, start : 86, type : bool, wr_dis : 6, rd_dis: null, alt : '', dict : '', desc: Revoke 2nd secure boot key, rloc: 'EFUSE_RD_REPEAT_DATA1_REG[22]', bloc: 'B10[6]'} + SECURE_BOOT_KEY_REVOKE2 : {show: y, blk : 0, word: 2, pos: 23, len : 1, start : 87, type : bool, wr_dis : 7, rd_dis: null, alt : '', dict : '', desc: Revoke 3rd secure boot key, rloc: 'EFUSE_RD_REPEAT_DATA1_REG[23]', bloc: 'B10[7]'} + KEY_PURPOSE_0 : {show: y, blk : 0, word: 2, pos: 24, len : 4, start : 88, type : 'uint:4', wr_dis : 8, rd_dis: null, alt : KEY0_PURPOSE, dict : '', desc: Represents the purpose of Key0, rloc: 'EFUSE_RD_REPEAT_DATA1_REG[27:24]', bloc: 'B11[3:0]'} + KEY_PURPOSE_1 : {show: y, blk : 0, word: 2, pos: 28, len : 4, start : 92, type : 'uint:4', wr_dis : 9, rd_dis: null, alt : KEY1_PURPOSE, dict : '', desc: Represents the purpose of Key1, rloc: 'EFUSE_RD_REPEAT_DATA1_REG[31:28]', bloc: 'B11[7:4]'} + KEY_PURPOSE_2 : {show: y, blk : 0, word: 3, pos : 0, len : 4, start : 96, type : 'uint:4', wr_dis : 10, rd_dis: null, alt : KEY2_PURPOSE, dict : '', desc: Represents the purpose of Key2, rloc: 'EFUSE_RD_REPEAT_DATA2_REG[3:0]', bloc: 'B12[3:0]'} + KEY_PURPOSE_3 : {show: y, blk : 0, word: 3, pos : 4, len : 4, start: 100, type : 'uint:4', wr_dis : 11, rd_dis: null, alt : KEY3_PURPOSE, dict : '', desc: Represents the purpose of Key3, rloc: 'EFUSE_RD_REPEAT_DATA2_REG[7:4]', bloc: 'B12[7:4]'} + KEY_PURPOSE_4 : {show: y, blk : 0, word: 3, pos : 8, len : 4, start: 104, type : 'uint:4', wr_dis : 12, rd_dis: null, alt : KEY4_PURPOSE, dict : '', desc: Represents the purpose of Key4, rloc: 'EFUSE_RD_REPEAT_DATA2_REG[11:8]', bloc: 'B13[3:0]'} + KEY_PURPOSE_5 : {show: y, blk : 0, word: 3, pos: 12, len : 4, start: 108, type : 'uint:4', wr_dis : 13, rd_dis: null, alt : KEY5_PURPOSE, dict : '', desc: Represents the purpose of Key5, rloc: 'EFUSE_RD_REPEAT_DATA2_REG[15:12]', bloc: 'B13[7:4]'} + SEC_DPA_LEVEL : {show: y, blk : 0, word: 3, pos: 16, len : 2, start: 112, type : 'uint:2', wr_dis : 14, rd_dis: null, alt : DPA_SEC_LEVEL, dict : '', desc: Represents the spa secure level by configuring the clock random divide mode, rloc: 'EFUSE_RD_REPEAT_DATA2_REG[17:16]', bloc: 'B14[1:0]'} + CRYPT_DPA_ENABLE : {show: y, blk : 0, word: 3, pos: 18, len : 1, start: 114, type : bool, wr_dis : 1, rd_dis: null, alt : '', dict : '', desc: 'Represents whether anti-dpa attack is enabled. 1:enabled. 0: disabled', rloc: 'EFUSE_RD_REPEAT_DATA2_REG[18]', bloc: 'B14[2]'} + RPT4_RESERVED2_1 : {show: n, blk : 0, word: 3, pos: 19, len : 1, start: 115, type : bool, wr_dis: null, rd_dis: null, alt : '', dict : '', desc: Reserved, rloc: 'EFUSE_RD_REPEAT_DATA2_REG[19]', bloc: 'B14[3]'} + SECURE_BOOT_EN : {show: y, blk : 0, word: 3, pos: 20, len : 1, start: 116, type : bool, wr_dis : 15, rd_dis: null, alt : '', dict : '', desc: 'Represents whether secure boot is enabled or disabled. 1: enabled. 0: disabled', rloc: 'EFUSE_RD_REPEAT_DATA2_REG[20]', bloc: 'B14[4]'} + SECURE_BOOT_AGGRESSIVE_REVOKE : {show: y, blk : 0, word: 3, pos: 21, len : 1, start: 117, type : bool, wr_dis : 16, rd_dis: null, alt : '', dict : '', desc: 'Represents whether revoking aggressive secure boot is enabled or disabled. 1: enabled. 0: disabled', rloc: 'EFUSE_RD_REPEAT_DATA2_REG[21]', bloc: 'B14[5]'} + RPT4_RESERVED2_0 : {show: n, blk : 0, word: 3, pos: 22, len : 6, start: 118, type : 'uint:6', wr_dis: null, rd_dis: null, alt : '', dict : '', desc: Reserved, rloc: 'EFUSE_RD_REPEAT_DATA2_REG[27:22]', bloc: 'B14[7:6],B15[3:0]'} + FLASH_TPUW : {show: y, blk : 0, word: 3, pos: 28, len : 4, start: 124, type : 'uint:4', wr_dis : 18, rd_dis: null, alt : '', dict : '', desc: Represents the flash waiting time after power-up; in unit of ms. When the value less than 15; the waiting time is the programmed value. Otherwise; the waiting time is 2 times the programmed value, rloc: 'EFUSE_RD_REPEAT_DATA2_REG[31:28]', bloc: 'B15[7:4]'} + DIS_DOWNLOAD_MODE : {show: y, blk : 0, word: 4, pos : 0, len : 1, start: 128, type : bool, wr_dis : 18, rd_dis: null, alt : '', dict : '', desc: 'Represents whether Download mode is disabled or enabled. 1: disabled. 0: enabled', rloc: 'EFUSE_RD_REPEAT_DATA3_REG[0]', bloc: 'B16[0]'} + DIS_DIRECT_BOOT : {show: y, blk : 0, word: 4, pos : 1, len : 1, start: 129, type : bool, wr_dis : 18, rd_dis: null, alt : '', dict : '', desc: 'Represents whether direct boot mode is disabled or enabled. 1: disabled. 0: enabled', rloc: 'EFUSE_RD_REPEAT_DATA3_REG[1]', bloc: 'B16[1]'} + DIS_USB_SERIAL_JTAG_ROM_PRINT : {show: y, blk : 0, word: 4, pos : 2, len : 1, start: 130, type : bool, wr_dis : 18, rd_dis: null, alt : DIS_USB_PRINT, dict : '', desc: 'Represents whether print from USB-Serial-JTAG is disabled or enabled. 1: disabled. 0: enabled', rloc: 'EFUSE_RD_REPEAT_DATA3_REG[2]', bloc: 'B16[2]'} + RPT4_RESERVED3_5 : {show: n, blk : 0, word: 4, pos : 3, len : 1, start: 131, type : bool, wr_dis: null, rd_dis: null, alt : '', dict : '', desc: Reserved, rloc: 'EFUSE_RD_REPEAT_DATA3_REG[3]', bloc: 'B16[3]'} + DIS_USB_SERIAL_JTAG_DOWNLOAD_MODE: {show: y, blk : 0, word: 4, pos : 4, len : 1, start: 132, type : bool, wr_dis : 18, rd_dis: null, alt : '', dict : '', desc: 'Represents whether the USB-Serial-JTAG download function is disabled or enabled. 1: disabled. 0: enabled', rloc: 'EFUSE_RD_REPEAT_DATA3_REG[4]', bloc: 'B16[4]'} + ENABLE_SECURITY_DOWNLOAD : {show: y, blk : 0, word: 4, pos : 5, len : 1, start: 133, type : bool, wr_dis : 18, rd_dis: null, alt : '', dict : '', desc: 'Represents whether security download is enabled or disabled. 1: enabled. 0: disabled', rloc: 'EFUSE_RD_REPEAT_DATA3_REG[5]', bloc: 'B16[5]'} + UART_PRINT_CONTROL : {show: y, blk : 0, word: 4, pos : 6, len : 2, start: 134, type : 'uint:2', wr_dis : 18, rd_dis: null, alt : '', dict: '{0: "Enable", 1: "Enable when GPIO8 is low at reset", 2: "Enable when GPIO8 is high at reset", 3: "Disable"}', desc: Set the default UARTboot message output mode, rloc: 'EFUSE_RD_REPEAT_DATA3_REG[7:6]', bloc: 'B16[7:6]'} + RPT4_RESERVED3_4 : {show: n, blk : 0, word: 4, pos : 8, len : 1, start: 136, type : bool, wr_dis: null, rd_dis: null, alt : '', dict : '', desc: Reserved, rloc: 'EFUSE_RD_REPEAT_DATA3_REG[8]', bloc: 'B17[0]'} + RPT4_RESERVED3_3 : {show: n, blk : 0, word: 4, pos : 9, len : 1, start: 137, type : bool, wr_dis: null, rd_dis: null, alt : '', dict : '', desc: Reserved, rloc: 'EFUSE_RD_REPEAT_DATA3_REG[9]', bloc: 'B17[1]'} + RPT4_RESERVED3_2 : {show: n, blk : 0, word: 4, pos: 10, len : 2, start: 138, type : 'uint:2', wr_dis: null, rd_dis: null, alt : '', dict : '', desc: Reserved, rloc: 'EFUSE_RD_REPEAT_DATA3_REG[11:10]', bloc: 'B17[3:2]'} + RPT4_RESERVED3_1 : {show: n, blk : 0, word: 4, pos: 12, len : 1, start: 140, type : bool, wr_dis: null, rd_dis: null, alt : '', dict : '', desc: Reserved, rloc: 'EFUSE_RD_REPEAT_DATA3_REG[12]', bloc: 'B17[4]'} + FORCE_SEND_RESUME : {show: y, blk : 0, word: 4, pos: 13, len : 1, start: 141, type : bool, wr_dis : 18, rd_dis: null, alt : '', dict : '', desc: 'Represents whether ROM code is forced to send a resume command during SPI boot. 1: forced. 0:not forced', rloc: 'EFUSE_RD_REPEAT_DATA3_REG[13]', bloc: 'B17[5]'} + SECURE_VERSION : {show: y, blk : 0, word: 4, pos: 14, len : 16, start: 142, type : 'uint:16', wr_dis : 18, rd_dis: null, alt : '', dict : '', desc: Represents the version used by ESP-IDF anti-rollback feature, rloc: 'EFUSE_RD_REPEAT_DATA3_REG[29:14]', bloc: 'B17[7:6],B18,B19[5:0]'} + SECURE_BOOT_DISABLE_FAST_WAKE : {show: y, blk : 0, word: 4, pos: 30, len : 1, start: 158, type : bool, wr_dis : 19, rd_dis: null, alt : '', dict : '', desc: 'Represents whether FAST VERIFY ON WAKE is disabled or enabled when Secure Boot is enabled. 1: disabled. 0: enabled', rloc: 'EFUSE_RD_REPEAT_DATA3_REG[30]', bloc: 'B19[6]'} + RPT4_RESERVED3_0 : {show: n, blk : 0, word: 4, pos: 31, len : 1, start: 159, type : bool, wr_dis: null, rd_dis: null, alt : '', dict : '', desc: Reserved, rloc: 'EFUSE_RD_REPEAT_DATA3_REG[31]', bloc: 'B19[7]'} + DISABLE_WAFER_VERSION_MAJOR : {show: y, blk : 0, word: 5, pos : 0, len : 1, start: 160, type : bool, wr_dis : 19, rd_dis: null, alt : '', dict : '', desc: Disables check of wafer version major, rloc: 'EFUSE_RD_REPEAT_DATA4_REG[0]', bloc: 'B20[0]'} + DISABLE_BLK_VERSION_MAJOR : {show: y, blk : 0, word: 5, pos : 1, len : 1, start: 161, type : bool, wr_dis : 19, rd_dis: null, alt : '', dict : '', desc: Disables check of blk version major, rloc: 'EFUSE_RD_REPEAT_DATA4_REG[1]', bloc: 'B20[1]'} + RESERVED_0_162 : {show: n, blk : 0, word: 5, pos : 2, len : 22, start: 162, type : 'uint:22', wr_dis: null, rd_dis: null, alt : '', dict : '', desc: reserved, rloc: 'EFUSE_RD_REPEAT_DATA4_REG[23:2]', bloc: 'B20[7:2],B21,B22'} + RPT4_RESERVED4_0 : {show: n, blk : 0, word: 5, pos: 24, len : 8, start: 184, type : 'uint:8', wr_dis: null, rd_dis: null, alt : '', dict : '', desc: Reserved, rloc: 'EFUSE_RD_REPEAT_DATA4_REG[31:24]', bloc: B23} + MAC : {show: y, blk : 1, word: 0, pos : 0, len : 48, start : 0, type : 'bytes:6', wr_dis : 20, rd_dis: null, alt : MAC_FACTORY, dict : '', desc: MAC address, rloc: EFUSE_RD_MAC_SPI_SYS_0_REG, bloc: 'B0,B1,B2,B3,B4,B5'} + MAC_EXT : {show: y, blk : 1, word: 1, pos: 16, len : 16, start : 48, type : 'bytes:2', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: Stores the extended bits of MAC address, rloc: 'EFUSE_RD_MAC_SPI_SYS_1_REG[31:16]', bloc: 'B6,B7'} + MAC_SPI_RESERVED : {show: n, blk : 1, word: 2, pos : 0, len : 14, start : 64, type : 'uint:14', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: Reserved, rloc: 'EFUSE_RD_MAC_SPI_SYS_2_REG[13:0]', bloc: 'B8,B9[5:0]'} + SPI_PAD_CONF_1 : {show: n, blk : 1, word: 2, pos: 14, len : 18, start : 78, type : 'uint:18', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: Stores the first part of SPI_PAD_CONF, rloc: 'EFUSE_RD_MAC_SPI_SYS_2_REG[31:14]', bloc: 'B9[7:6],B10,B11'} + SPI_PAD_CONF_2 : {show: n, blk : 1, word: 3, pos : 0, len : 18, start : 96, type : 'uint:18', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: Stores the second part of SPI_PAD_CONF, rloc: 'EFUSE_RD_MAC_SPI_SYS_3_REG[17:0]', bloc: 'B12,B13,B14[1:0]'} + WAFER_VERSION_MINOR : {show: y, blk : 1, word: 3, pos: 18, len : 4, start: 114, type : 'uint:4', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: '', rloc: 'EFUSE_RD_MAC_SPI_SYS_3_REG[21:18]', bloc: 'B14[5:2]'} + WAFER_VERSION_MAJOR : {show: y, blk : 1, word: 3, pos: 22, len : 2, start: 118, type : 'uint:2', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: '', rloc: 'EFUSE_RD_MAC_SPI_SYS_3_REG[23:22]', bloc: 'B14[7:6]'} + PKG_VERSION : {show: y, blk : 1, word: 3, pos: 24, len : 3, start: 120, type : 'uint:3', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: Package version, rloc: 'EFUSE_RD_MAC_SPI_SYS_3_REG[26:24]', bloc: 'B15[2:0]'} + BLK_VERSION_MINOR : {show: y, blk : 1, word: 3, pos: 27, len : 3, start: 123, type : 'uint:3', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: BLK_VERSION_MINOR of BLOCK2, rloc: 'EFUSE_RD_MAC_SPI_SYS_3_REG[29:27]', bloc: 'B15[5:3]'} + BLK_VERSION_MAJOR : {show: y, blk : 1, word: 3, pos: 30, len : 2, start: 126, type : 'uint:2', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: BLK_VERSION_MAJOR of BLOCK2, rloc: 'EFUSE_RD_MAC_SPI_SYS_3_REG[31:30]', bloc: 'B15[7:6]'} + FLASH_CAP : {show: y, blk : 1, word: 4, pos : 0, len : 3, start: 128, type : 'uint:3', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: '', rloc: 'EFUSE_RD_MAC_SPI_SYS_4_REG[2:0]', bloc: 'B16[2:0]'} + FLASH_TEMP : {show: y, blk : 1, word: 4, pos : 3, len : 2, start: 131, type : 'uint:2', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: '', rloc: 'EFUSE_RD_MAC_SPI_SYS_4_REG[4:3]', bloc: 'B16[4:3]'} + FLASH_VENDOR : {show: y, blk : 1, word: 4, pos : 5, len : 3, start: 133, type : 'uint:3', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: '', rloc: 'EFUSE_RD_MAC_SPI_SYS_4_REG[7:5]', bloc: 'B16[7:5]'} + RESERVED_1_136 : {show: n, blk : 1, word: 4, pos : 8, len : 24, start: 136, type : 'uint:24', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: reserved, rloc: 'EFUSE_RD_MAC_SPI_SYS_4_REG[31:8]', bloc: 'B17,B18,B19'} + SYS_DATA_PART0_2 : {show: n, blk : 1, word: 5, pos : 0, len : 32, start: 160, type : 'uint:32', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: Stores the second 32 bits of the zeroth part of system data, rloc: EFUSE_RD_MAC_SPI_SYS_5_REG, bloc: 'B20,B21,B22,B23'} + OPTIONAL_UNIQUE_ID : {show: y, blk : 2, word: 0, pos : 0, len: 128, start : 0, type: 'bytes:16', wr_dis : 21, rd_dis: null, alt : '', dict : '', desc: Optional unique 128-bit ID, rloc: EFUSE_RD_SYS_PART1_DATA0_REG, bloc: 'B0,B1,B2,B3,B4,B5,B6,B7,B8,B9,B10,B11,B12,B13,B14,B15'} + TEMP_CALIB : {show: y, blk : 2, word: 4, pos : 0, len : 9, start: 128, type : 'uint:9', wr_dis : 21, rd_dis: null, alt : '', dict : '', desc: Temperature calibration data, rloc: 'EFUSE_RD_SYS_PART1_DATA4_REG[8:0]', bloc: 'B16,B17[0]'} + OCODE : {show: y, blk : 2, word: 4, pos : 9, len : 8, start: 137, type : 'uint:8', wr_dis : 21, rd_dis: null, alt : '', dict : '', desc: ADC OCode, rloc: 'EFUSE_RD_SYS_PART1_DATA4_REG[16:9]', bloc: 'B17[7:1],B18[0]'} + ADC1_INIT_CODE_ATTEN0 : {show: y, blk : 2, word: 4, pos: 17, len : 10, start: 145, type : 'uint:10', wr_dis : 21, rd_dis: null, alt : '', dict : '', desc: ADC1 init code at atten0, rloc: 'EFUSE_RD_SYS_PART1_DATA4_REG[26:17]', bloc: 'B18[7:1],B19[2:0]'} + ADC1_INIT_CODE_ATTEN1 : {show: y, blk : 2, word: 4, pos: 27, len : 10, start: 155, type : 'uint:10', wr_dis : 21, rd_dis: null, alt : '', dict : '', desc: ADC1 init code at atten1, rloc: 'EFUSE_RD_SYS_PART1_DATA4_REG[31:27]', bloc: 'B19[7:3],B20[4:0]'} + ADC1_INIT_CODE_ATTEN2 : {show: y, blk : 2, word: 5, pos : 5, len : 10, start: 165, type : 'uint:10', wr_dis : 21, rd_dis: null, alt : '', dict : '', desc: ADC1 init code at atten2, rloc: 'EFUSE_RD_SYS_PART1_DATA5_REG[14:5]', bloc: 'B20[7:5],B21[6:0]'} + ADC1_INIT_CODE_ATTEN3 : {show: y, blk : 2, word: 5, pos: 15, len : 10, start: 175, type : 'uint:10', wr_dis : 21, rd_dis: null, alt : '', dict : '', desc: ADC1 init code at atten3, rloc: 'EFUSE_RD_SYS_PART1_DATA5_REG[24:15]', bloc: 'B21[7],B22,B23[0]'} + ADC1_CAL_VOL_ATTEN0 : {show: y, blk : 2, word: 5, pos: 25, len : 10, start: 185, type : 'uint:10', wr_dis : 21, rd_dis: null, alt : '', dict : '', desc: ADC1 calibration voltage at atten0, rloc: 'EFUSE_RD_SYS_PART1_DATA5_REG[31:25]', bloc: 'B23[7:1],B24[2:0]'} + ADC1_CAL_VOL_ATTEN1 : {show: y, blk : 2, word: 6, pos : 3, len : 10, start: 195, type : 'uint:10', wr_dis : 21, rd_dis: null, alt : '', dict : '', desc: ADC1 calibration voltage at atten1, rloc: 'EFUSE_RD_SYS_PART1_DATA6_REG[12:3]', bloc: 'B24[7:3],B25[4:0]'} + ADC1_CAL_VOL_ATTEN2 : {show: y, blk : 2, word: 6, pos: 13, len : 10, start: 205, type : 'uint:10', wr_dis : 21, rd_dis: null, alt : '', dict : '', desc: ADC1 calibration voltage at atten2, rloc: 'EFUSE_RD_SYS_PART1_DATA6_REG[22:13]', bloc: 'B25[7:5],B26[6:0]'} + ADC1_CAL_VOL_ATTEN3 : {show: y, blk : 2, word: 6, pos: 23, len : 10, start: 215, type : 'uint:10', wr_dis : 21, rd_dis: null, alt : '', dict : '', desc: ADC1 calibration voltage at atten3, rloc: 'EFUSE_RD_SYS_PART1_DATA6_REG[31:23]', bloc: 'B26[7],B27,B28[0]'} + ADC1_INIT_CODE_ATTEN0_CH0 : {show: y, blk : 2, word: 7, pos : 1, len : 4, start: 225, type : 'uint:4', wr_dis : 21, rd_dis: null, alt : '', dict : '', desc: ADC1 init code at atten0 ch0, rloc: 'EFUSE_RD_SYS_PART1_DATA7_REG[4:1]', bloc: 'B28[4:1]'} + ADC1_INIT_CODE_ATTEN0_CH1 : {show: y, blk : 2, word: 7, pos : 5, len : 4, start: 229, type : 'uint:4', wr_dis : 21, rd_dis: null, alt : '', dict : '', desc: ADC1 init code at atten0 ch1, rloc: 'EFUSE_RD_SYS_PART1_DATA7_REG[8:5]', bloc: 'B28[7:5],B29[0]'} + ADC1_INIT_CODE_ATTEN0_CH2 : {show: y, blk : 2, word: 7, pos : 9, len : 4, start: 233, type : 'uint:4', wr_dis : 21, rd_dis: null, alt : '', dict : '', desc: ADC1 init code at atten0 ch2, rloc: 'EFUSE_RD_SYS_PART1_DATA7_REG[12:9]', bloc: 'B29[4:1]'} + ADC1_INIT_CODE_ATTEN0_CH3 : {show: y, blk : 2, word: 7, pos: 13, len : 4, start: 237, type : 'uint:4', wr_dis : 21, rd_dis: null, alt : '', dict : '', desc: ADC1 init code at atten0 ch3, rloc: 'EFUSE_RD_SYS_PART1_DATA7_REG[16:13]', bloc: 'B29[7:5],B30[0]'} + ADC1_INIT_CODE_ATTEN0_CH4 : {show: y, blk : 2, word: 7, pos: 17, len : 4, start: 241, type : 'uint:4', wr_dis : 21, rd_dis: null, alt : '', dict : '', desc: ADC1 init code at atten0 ch4, rloc: 'EFUSE_RD_SYS_PART1_DATA7_REG[20:17]', bloc: 'B30[4:1]'} + ADC1_INIT_CODE_ATTEN0_CH5 : {show: y, blk : 2, word: 7, pos: 21, len : 4, start: 245, type : 'uint:4', wr_dis : 21, rd_dis: null, alt : '', dict : '', desc: ADC1 init code at atten0 ch5, rloc: 'EFUSE_RD_SYS_PART1_DATA7_REG[24:21]', bloc: 'B30[7:5],B31[0]'} + ADC1_INIT_CODE_ATTEN0_CH6 : {show: y, blk : 2, word: 7, pos: 25, len : 4, start: 249, type : 'uint:4', wr_dis : 21, rd_dis: null, alt : '', dict : '', desc: ADC1 init code at atten0 ch6, rloc: 'EFUSE_RD_SYS_PART1_DATA7_REG[28:25]', bloc: 'B31[4:1]'} + RESERVED_2_253 : {show: n, blk : 2, word: 7, pos: 29, len : 3, start: 253, type : 'uint:3', wr_dis : 21, rd_dis: null, alt : '', dict : '', desc: reserved, rloc: 'EFUSE_RD_SYS_PART1_DATA7_REG[31:29]', bloc: 'B31[7:5]'} + BLOCK_USR_DATA : {show: y, blk : 3, word: 0, pos : 0, len: 192, start : 0, type: 'bytes:24', wr_dis : 22, rd_dis: null, alt : USER_DATA, dict : '', desc: User data, rloc: EFUSE_RD_USR_DATA0_REG, bloc: 'B0,B1,B2,B3,B4,B5,B6,B7,B8,B9,B10,B11,B12,B13,B14,B15,B16,B17,B18,B19,B20,B21,B22,B23'} + RESERVED_3_192 : {show: n, blk : 3, word: 6, pos : 0, len : 8, start: 192, type : 'uint:8', wr_dis : 22, rd_dis: null, alt : '', dict : '', desc: reserved, rloc: 'EFUSE_RD_USR_DATA6_REG[7:0]', bloc: B24} + CUSTOM_MAC : {show: y, blk : 3, word: 6, pos : 8, len : 48, start: 200, type : 'bytes:6', wr_dis : 22, rd_dis: null, alt: MAC_CUSTOM USER_DATA_MAC_CUSTOM, dict : '', desc: Custom MAC, rloc: 'EFUSE_RD_USR_DATA6_REG[31:8]', bloc: 'B25,B26,B27,B28,B29,B30'} + RESERVED_3_248 : {show: n, blk : 3, word: 7, pos: 24, len : 8, start: 248, type : 'uint:8', wr_dis : 22, rd_dis: null, alt : '', dict : '', desc: reserved, rloc: 'EFUSE_RD_USR_DATA7_REG[31:24]', bloc: B31} + BLOCK_KEY0 : {show: y, blk : 4, word: 0, pos : 0, len: 256, start : 0, type: 'bytes:32', wr_dis : 23, rd_dis : 0, alt : KEY0, dict : '', desc: Key0 or user data, rloc: EFUSE_RD_KEY0_DATA0_REG, bloc: 'B0,B1,B2,B3,B4,B5,B6,B7,B8,B9,B10,B11,B12,B13,B14,B15,B16,B17,B18,B19,B20,B21,B22,B23,B24,B25,B26,B27,B28,B29,B30,B31'} + BLOCK_KEY1 : {show: y, blk : 5, word: 0, pos : 0, len: 256, start : 0, type: 'bytes:32', wr_dis : 24, rd_dis : 1, alt : KEY1, dict : '', desc: Key1 or user data, rloc: EFUSE_RD_KEY1_DATA0_REG, bloc: 'B0,B1,B2,B3,B4,B5,B6,B7,B8,B9,B10,B11,B12,B13,B14,B15,B16,B17,B18,B19,B20,B21,B22,B23,B24,B25,B26,B27,B28,B29,B30,B31'} + BLOCK_KEY2 : {show: y, blk : 6, word: 0, pos : 0, len: 256, start : 0, type: 'bytes:32', wr_dis : 25, rd_dis : 2, alt : KEY2, dict : '', desc: Key2 or user data, rloc: EFUSE_RD_KEY2_DATA0_REG, bloc: 'B0,B1,B2,B3,B4,B5,B6,B7,B8,B9,B10,B11,B12,B13,B14,B15,B16,B17,B18,B19,B20,B21,B22,B23,B24,B25,B26,B27,B28,B29,B30,B31'} + BLOCK_KEY3 : {show: y, blk : 7, word: 0, pos : 0, len: 256, start : 0, type: 'bytes:32', wr_dis : 26, rd_dis : 3, alt : KEY3, dict : '', desc: Key3 or user data, rloc: EFUSE_RD_KEY3_DATA0_REG, bloc: 'B0,B1,B2,B3,B4,B5,B6,B7,B8,B9,B10,B11,B12,B13,B14,B15,B16,B17,B18,B19,B20,B21,B22,B23,B24,B25,B26,B27,B28,B29,B30,B31'} + BLOCK_KEY4 : {show: y, blk : 8, word: 0, pos : 0, len: 256, start : 0, type: 'bytes:32', wr_dis : 27, rd_dis : 4, alt : KEY4, dict : '', desc: Key4 or user data, rloc: EFUSE_RD_KEY4_DATA0_REG, bloc: 'B0,B1,B2,B3,B4,B5,B6,B7,B8,B9,B10,B11,B12,B13,B14,B15,B16,B17,B18,B19,B20,B21,B22,B23,B24,B25,B26,B27,B28,B29,B30,B31'} + BLOCK_KEY5 : {show: y, blk : 9, word: 0, pos : 0, len: 256, start : 0, type: 'bytes:32', wr_dis : 28, rd_dis : 5, alt : KEY5, dict : '', desc: Key5 or user data, rloc: EFUSE_RD_KEY5_DATA0_REG, bloc: 'B0,B1,B2,B3,B4,B5,B6,B7,B8,B9,B10,B11,B12,B13,B14,B15,B16,B17,B18,B19,B20,B21,B22,B23,B24,B25,B26,B27,B28,B29,B30,B31'} + BLOCK_SYS_DATA2 : {show: y, blk: 10, word: 0, pos : 0, len: 256, start : 0, type: 'bytes:32', wr_dis : 29, rd_dis : 6, alt : SYS_DATA_PART2, dict : '', desc: System data part 2 (reserved), rloc: EFUSE_RD_SYS_PART2_DATA0_REG, bloc: 'B0,B1,B2,B3,B4,B5,B6,B7,B8,B9,B10,B11,B12,B13,B14,B15,B16,B17,B18,B19,B20,B21,B22,B23,B24,B25,B26,B27,B28,B29,B30,B31'} diff --git a/code/.venv/lib/python3.12/site-packages/espefuse/efuse_defs/esp32h2.yaml b/code/.venv/lib/python3.12/site-packages/espefuse/efuse_defs/esp32h2.yaml new file mode 100644 index 0000000..a86a018 --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/espefuse/efuse_defs/esp32h2.yaml @@ -0,0 +1,103 @@ +VER_NO: b69ddcfb39a412df490e3facbbfb46b2 +EFUSES: + WR_DIS : {show: y, blk : 0, word: 0, pos : 0, len : 32, start : 0, type : 'uint:32', wr_dis: null, rd_dis: null, alt : '', dict : '', desc: Disable programming of individual eFuses, rloc: EFUSE_RD_WR_DIS_REG, bloc: 'B0,B1,B2,B3'} + RD_DIS : {show: y, blk : 0, word: 1, pos : 0, len : 7, start : 32, type : 'uint:7', wr_dis : 0, rd_dis: null, alt : '', dict : '', desc: Disable reading from BlOCK4-10, rloc: 'EFUSE_RD_REPEAT_DATA0_REG[6:0]', bloc: 'B4[6:0]'} + RPT4_RESERVED0_4 : {show: n, blk : 0, word: 1, pos : 7, len : 1, start : 39, type : bool, wr_dis: null, rd_dis: null, alt : '', dict : '', desc: Reserved, rloc: 'EFUSE_RD_REPEAT_DATA0_REG[7]', bloc: 'B4[7]'} + DIS_ICACHE : {show: y, blk : 0, word: 1, pos : 8, len : 1, start : 40, type : bool, wr_dis : 2, rd_dis: null, alt : '', dict : '', desc: 'Represents whether icache is disabled or enabled. 1: disabled. 0: enabled', rloc: 'EFUSE_RD_REPEAT_DATA0_REG[8]', bloc: 'B5[0]'} + DIS_USB_JTAG : {show: y, blk : 0, word: 1, pos : 9, len : 1, start : 41, type : bool, wr_dis : 2, rd_dis: null, alt : '', dict : '', desc: 'Represents whether the function of usb switch to jtag is disabled or enabled. 1: disabled. 0: enabled', rloc: 'EFUSE_RD_REPEAT_DATA0_REG[9]', bloc: 'B5[1]'} + POWERGLITCH_EN : {show: y, blk : 0, word: 1, pos: 10, len : 1, start : 42, type : bool, wr_dis : 2, rd_dis: null, alt : '', dict : '', desc: 'Represents whether power glitch function is enabled. 1: enabled. 0: disabled', rloc: 'EFUSE_RD_REPEAT_DATA0_REG[10]', bloc: 'B5[2]'} + DIS_USB_SERIAL_JTAG : {show: n, blk : 0, word: 1, pos: 11, len : 1, start : 43, type : bool, wr_dis : 2, rd_dis: null, alt : '', dict : '', desc: 'Represents whether USB-Serial-JTAG is disabled or enabled. 1: disabled. 0: enabled', rloc: 'EFUSE_RD_REPEAT_DATA0_REG[11]', bloc: 'B5[3]'} + DIS_FORCE_DOWNLOAD : {show: y, blk : 0, word: 1, pos: 12, len : 1, start : 44, type : bool, wr_dis : 2, rd_dis: null, alt : '', dict : '', desc: 'Represents whether the function that forces chip into download mode is disabled or enabled. 1: disabled. 0: enabled', rloc: 'EFUSE_RD_REPEAT_DATA0_REG[12]', bloc: 'B5[4]'} + SPI_DOWNLOAD_MSPI_DIS : {show: y, blk : 0, word: 1, pos: 13, len : 1, start : 45, type : bool, wr_dis : 2, rd_dis: null, alt : '', dict : '', desc: 'Represents whether SPI0 controller during boot_mode_download is disabled or enabled. 1: disabled. 0: enabled', rloc: 'EFUSE_RD_REPEAT_DATA0_REG[13]', bloc: 'B5[5]'} + DIS_TWAI : {show: y, blk : 0, word: 1, pos: 14, len : 1, start : 46, type : bool, wr_dis : 2, rd_dis: null, alt : DIS_CAN, dict : '', desc: 'Represents whether TWAI function is disabled or enabled. 1: disabled. 0: enabled', rloc: 'EFUSE_RD_REPEAT_DATA0_REG[14]', bloc: 'B5[6]'} + JTAG_SEL_ENABLE : {show: y, blk : 0, word: 1, pos: 15, len : 1, start : 47, type : bool, wr_dis : 2, rd_dis: null, alt : '', dict : '', desc: Set this bit to enable selection between usb_to_jtag and pad_to_jtag through strapping gpio25 when both EFUSE_DIS_PAD_JTAG and EFUSE_DIS_USB_JTAG are equal to 0, rloc: 'EFUSE_RD_REPEAT_DATA0_REG[15]', bloc: 'B5[7]'} + SOFT_DIS_JTAG : {show: y, blk : 0, word: 1, pos: 16, len : 3, start : 48, type : 'uint:3', wr_dis : 31, rd_dis: null, alt : '', dict : '', desc: 'Represents whether JTAG is disabled in soft way. Odd number: disabled. Even number: enabled', rloc: 'EFUSE_RD_REPEAT_DATA0_REG[18:16]', bloc: 'B6[2:0]'} + DIS_PAD_JTAG : {show: y, blk : 0, word: 1, pos: 19, len : 1, start : 51, type : bool, wr_dis : 2, rd_dis: null, alt : '', dict : '', desc: 'Represents whether JTAG is disabled in the hard way(permanently). 1: disabled. 0: enabled', rloc: 'EFUSE_RD_REPEAT_DATA0_REG[19]', bloc: 'B6[3]'} + DIS_DOWNLOAD_MANUAL_ENCRYPT : {show: y, blk : 0, word: 1, pos: 20, len : 1, start : 52, type : bool, wr_dis : 2, rd_dis: null, alt : '', dict : '', desc: 'Represents whether flash encrypt function is disabled or enabled(except in SPI boot mode). 1: disabled. 0: enabled', rloc: 'EFUSE_RD_REPEAT_DATA0_REG[20]', bloc: 'B6[4]'} + USB_DREFH : {show: n, blk : 0, word: 1, pos: 21, len : 2, start : 53, type : 'uint:2', wr_dis : 30, rd_dis: null, alt : '', dict : '', desc: Represents the single-end input threhold vrefh; 1.76 V to 2 V with step of 80 mV, rloc: 'EFUSE_RD_REPEAT_DATA0_REG[22:21]', bloc: 'B6[6:5]'} + USB_DREFL : {show: n, blk : 0, word: 1, pos: 23, len : 2, start : 55, type : 'uint:2', wr_dis : 30, rd_dis: null, alt : '', dict : '', desc: Represents the single-end input threhold vrefl; 1.76 V to 2 V with step of 80 mV, rloc: 'EFUSE_RD_REPEAT_DATA0_REG[24:23]', bloc: 'B6[7],B7[0]'} + USB_EXCHG_PINS : {show: y, blk : 0, word: 1, pos: 25, len : 1, start : 57, type : bool, wr_dis : 30, rd_dis: null, alt : '', dict : '', desc: 'Represents whether the D+ and D- pins is exchanged. 1: exchanged. 0: not exchanged', rloc: 'EFUSE_RD_REPEAT_DATA0_REG[25]', bloc: 'B7[1]'} + VDD_SPI_AS_GPIO : {show: y, blk : 0, word: 1, pos: 26, len : 1, start : 58, type : bool, wr_dis : 30, rd_dis: null, alt : '', dict : '', desc: 'Represents whether vdd spi pin is functioned as gpio. 1: functioned. 0: not functioned', rloc: 'EFUSE_RD_REPEAT_DATA0_REG[26]', bloc: 'B7[2]'} + RPT4_RESERVED0_2 : {show: n, blk : 0, word: 1, pos: 27, len : 2, start : 59, type : 'uint:2', wr_dis: null, rd_dis: null, alt : '', dict : '', desc: Reserved, rloc: 'EFUSE_RD_REPEAT_DATA0_REG[28:27]', bloc: 'B7[4:3]'} + RPT4_RESERVED0_1 : {show: n, blk : 0, word: 1, pos: 29, len : 1, start : 61, type : bool, wr_dis: null, rd_dis: null, alt : '', dict : '', desc: Reserved, rloc: 'EFUSE_RD_REPEAT_DATA0_REG[29]', bloc: 'B7[5]'} + RPT4_RESERVED0_0 : {show: n, blk : 0, word: 1, pos: 30, len : 2, start : 62, type : 'uint:2', wr_dis: null, rd_dis: null, alt : '', dict : '', desc: Reserved, rloc: 'EFUSE_RD_REPEAT_DATA0_REG[31:30]', bloc: 'B7[7:6]'} + RPT4_RESERVED1_1 : {show: n, blk : 0, word: 2, pos : 0, len : 16, start : 64, type : 'uint:16', wr_dis: null, rd_dis: null, alt : '', dict : '', desc: Reserved, rloc: 'EFUSE_RD_REPEAT_DATA1_REG[15:0]', bloc: 'B8,B9'} + WDT_DELAY_SEL : {show: y, blk : 0, word: 2, pos: 16, len : 2, start : 80, type : 'uint:2', wr_dis : 3, rd_dis: null, alt : '', dict : '', desc: 'Represents whether RTC watchdog timeout threshold is selected at startup. 1: selected. 0: not selected', rloc: 'EFUSE_RD_REPEAT_DATA1_REG[17:16]', bloc: 'B10[1:0]'} + SPI_BOOT_CRYPT_CNT : {show: y, blk : 0, word: 2, pos: 18, len : 3, start : 82, type : 'uint:3', wr_dis : 4, rd_dis: null, alt : '', dict: '{0: "Disable", 1: "Enable", 3: "Disable", 7: "Enable"}', desc: Enables flash encryption when 1 or 3 bits are set and disables otherwise, rloc: 'EFUSE_RD_REPEAT_DATA1_REG[20:18]', bloc: 'B10[4:2]'} + SECURE_BOOT_KEY_REVOKE0 : {show: y, blk : 0, word: 2, pos: 21, len : 1, start : 85, type : bool, wr_dis : 5, rd_dis: null, alt : '', dict : '', desc: Revoke 1st secure boot key, rloc: 'EFUSE_RD_REPEAT_DATA1_REG[21]', bloc: 'B10[5]'} + SECURE_BOOT_KEY_REVOKE1 : {show: y, blk : 0, word: 2, pos: 22, len : 1, start : 86, type : bool, wr_dis : 6, rd_dis: null, alt : '', dict : '', desc: Revoke 2nd secure boot key, rloc: 'EFUSE_RD_REPEAT_DATA1_REG[22]', bloc: 'B10[6]'} + SECURE_BOOT_KEY_REVOKE2 : {show: y, blk : 0, word: 2, pos: 23, len : 1, start : 87, type : bool, wr_dis : 7, rd_dis: null, alt : '', dict : '', desc: Revoke 3rd secure boot key, rloc: 'EFUSE_RD_REPEAT_DATA1_REG[23]', bloc: 'B10[7]'} + KEY_PURPOSE_0 : {show: y, blk : 0, word: 2, pos: 24, len : 4, start : 88, type : 'uint:4', wr_dis : 8, rd_dis: null, alt : KEY0_PURPOSE, dict : '', desc: Represents the purpose of Key0, rloc: 'EFUSE_RD_REPEAT_DATA1_REG[27:24]', bloc: 'B11[3:0]'} + KEY_PURPOSE_1 : {show: y, blk : 0, word: 2, pos: 28, len : 4, start : 92, type : 'uint:4', wr_dis : 9, rd_dis: null, alt : KEY1_PURPOSE, dict : '', desc: Represents the purpose of Key1, rloc: 'EFUSE_RD_REPEAT_DATA1_REG[31:28]', bloc: 'B11[7:4]'} + KEY_PURPOSE_2 : {show: y, blk : 0, word: 3, pos : 0, len : 4, start : 96, type : 'uint:4', wr_dis : 10, rd_dis: null, alt : KEY2_PURPOSE, dict : '', desc: Represents the purpose of Key2, rloc: 'EFUSE_RD_REPEAT_DATA2_REG[3:0]', bloc: 'B12[3:0]'} + KEY_PURPOSE_3 : {show: y, blk : 0, word: 3, pos : 4, len : 4, start: 100, type : 'uint:4', wr_dis : 11, rd_dis: null, alt : KEY3_PURPOSE, dict : '', desc: Represents the purpose of Key3, rloc: 'EFUSE_RD_REPEAT_DATA2_REG[7:4]', bloc: 'B12[7:4]'} + KEY_PURPOSE_4 : {show: y, blk : 0, word: 3, pos : 8, len : 4, start: 104, type : 'uint:4', wr_dis : 12, rd_dis: null, alt : KEY4_PURPOSE, dict : '', desc: Represents the purpose of Key4, rloc: 'EFUSE_RD_REPEAT_DATA2_REG[11:8]', bloc: 'B13[3:0]'} + KEY_PURPOSE_5 : {show: y, blk : 0, word: 3, pos: 12, len : 4, start: 108, type : 'uint:4', wr_dis : 13, rd_dis: null, alt : KEY5_PURPOSE, dict : '', desc: Represents the purpose of Key5, rloc: 'EFUSE_RD_REPEAT_DATA2_REG[15:12]', bloc: 'B13[7:4]'} + SEC_DPA_LEVEL : {show: y, blk : 0, word: 3, pos: 16, len : 2, start: 112, type : 'uint:2', wr_dis : 14, rd_dis: null, alt : '', dict : '', desc: Represents the spa secure level by configuring the clock random divide mode, rloc: 'EFUSE_RD_REPEAT_DATA2_REG[17:16]', bloc: 'B14[1:0]'} + ECDSA_FORCE_USE_HARDWARE_K : {show: y, blk : 0, word: 3, pos: 18, len : 1, start: 114, type : bool, wr_dis : 17, rd_dis: null, alt : '', dict : '', desc: 'Represents whether hardware random number k is forced used in ESDCA. 1: force used. 0: not force used', rloc: 'EFUSE_RD_REPEAT_DATA2_REG[18]', bloc: 'B14[2]'} + CRYPT_DPA_ENABLE : {show: y, blk : 0, word: 3, pos: 19, len : 1, start: 115, type : bool, wr_dis : 14, rd_dis: null, alt : '', dict : '', desc: 'Represents whether anti-dpa attack is enabled. 1:enabled. 0: disabled', rloc: 'EFUSE_RD_REPEAT_DATA2_REG[19]', bloc: 'B14[3]'} + SECURE_BOOT_EN : {show: y, blk : 0, word: 3, pos: 20, len : 1, start: 116, type : bool, wr_dis : 15, rd_dis: null, alt : '', dict : '', desc: 'Represents whether secure boot is enabled or disabled. 1: enabled. 0: disabled', rloc: 'EFUSE_RD_REPEAT_DATA2_REG[20]', bloc: 'B14[4]'} + SECURE_BOOT_AGGRESSIVE_REVOKE : {show: y, blk : 0, word: 3, pos: 21, len : 1, start: 117, type : bool, wr_dis : 16, rd_dis: null, alt : '', dict : '', desc: 'Represents whether revoking aggressive secure boot is enabled or disabled. 1: enabled. 0: disabled', rloc: 'EFUSE_RD_REPEAT_DATA2_REG[21]', bloc: 'B14[5]'} + RPT4_RESERVED2_0 : {show: n, blk : 0, word: 3, pos: 22, len : 6, start: 118, type : 'uint:6', wr_dis: null, rd_dis: null, alt : '', dict : '', desc: Reserved, rloc: 'EFUSE_RD_REPEAT_DATA2_REG[27:22]', bloc: 'B14[7:6],B15[3:0]'} + FLASH_TPUW : {show: y, blk : 0, word: 3, pos: 28, len : 4, start: 124, type : 'uint:4', wr_dis : 18, rd_dis: null, alt : '', dict : '', desc: Represents the flash waiting time after power-up; in unit of ms. When the value less than 15; the waiting time is the programmed value. Otherwise; the waiting time is 2 times the programmed value, rloc: 'EFUSE_RD_REPEAT_DATA2_REG[31:28]', bloc: 'B15[7:4]'} + DIS_DOWNLOAD_MODE : {show: y, blk : 0, word: 4, pos : 0, len : 1, start: 128, type : bool, wr_dis : 18, rd_dis: null, alt : '', dict : '', desc: 'Represents whether Download mode is disabled or enabled. 1: disabled. 0: enabled', rloc: 'EFUSE_RD_REPEAT_DATA3_REG[0]', bloc: 'B16[0]'} + DIS_DIRECT_BOOT : {show: y, blk : 0, word: 4, pos : 1, len : 1, start: 129, type : bool, wr_dis : 18, rd_dis: null, alt : '', dict : '', desc: 'Represents whether direct boot mode is disabled or enabled. 1: disabled. 0: enabled', rloc: 'EFUSE_RD_REPEAT_DATA3_REG[1]', bloc: 'B16[1]'} + DIS_USB_SERIAL_JTAG_ROM_PRINT : {show: y, blk : 0, word: 4, pos : 2, len : 1, start: 130, type : bool, wr_dis : 18, rd_dis: null, alt : DIS_USB_PRINT, dict : '', desc: Set this bit to disable USB-Serial-JTAG print during rom boot, rloc: 'EFUSE_RD_REPEAT_DATA3_REG[2]', bloc: 'B16[2]'} + RPT4_RESERVED3_5 : {show: n, blk : 0, word: 4, pos : 3, len : 1, start: 131, type : bool, wr_dis: null, rd_dis: null, alt : '', dict : '', desc: Reserved, rloc: 'EFUSE_RD_REPEAT_DATA3_REG[3]', bloc: 'B16[3]'} + DIS_USB_SERIAL_JTAG_DOWNLOAD_MODE: {show: y, blk : 0, word: 4, pos : 4, len : 1, start: 132, type : bool, wr_dis : 18, rd_dis: null, alt : '', dict : '', desc: 'Represents whether the USB-Serial-JTAG download function is disabled or enabled. 1: disabled. 0: enabled', rloc: 'EFUSE_RD_REPEAT_DATA3_REG[4]', bloc: 'B16[4]'} + ENABLE_SECURITY_DOWNLOAD : {show: y, blk : 0, word: 4, pos : 5, len : 1, start: 133, type : bool, wr_dis : 18, rd_dis: null, alt : '', dict : '', desc: 'Represents whether security download is enabled or disabled. 1: enabled. 0: disabled', rloc: 'EFUSE_RD_REPEAT_DATA3_REG[5]', bloc: 'B16[5]'} + UART_PRINT_CONTROL : {show: y, blk : 0, word: 4, pos : 6, len : 2, start: 134, type : 'uint:2', wr_dis : 18, rd_dis: null, alt : '', dict: '{0: "Enable", 1: "Enable when GPIO8 is low at reset", 2: "Enable when GPIO8 is high at reset", 3: "Disable"}', desc: Set the default UARTboot message output mode, rloc: 'EFUSE_RD_REPEAT_DATA3_REG[7:6]', bloc: 'B16[7:6]'} + FORCE_SEND_RESUME : {show: y, blk : 0, word: 4, pos : 8, len : 1, start: 136, type : bool, wr_dis : 18, rd_dis: null, alt : '', dict : '', desc: 'Represents whether ROM code is forced to send a resume command during SPI boot. 1: forced. 0:not forced', rloc: 'EFUSE_RD_REPEAT_DATA3_REG[8]', bloc: 'B17[0]'} + SECURE_VERSION : {show: y, blk : 0, word: 4, pos : 9, len : 16, start: 137, type : 'uint:16', wr_dis : 18, rd_dis: null, alt : '', dict : '', desc: Represents the version used by ESP-IDF anti-rollback feature, rloc: 'EFUSE_RD_REPEAT_DATA3_REG[24:9]', bloc: 'B17[7:1],B18,B19[0]'} + SECURE_BOOT_DISABLE_FAST_WAKE : {show: y, blk : 0, word: 4, pos: 25, len : 1, start: 153, type : bool, wr_dis : 18, rd_dis: null, alt : '', dict : '', desc: 'Represents whether FAST VERIFY ON WAKE is disabled or enabled when Secure Boot is enabled. 1: disabled. 0: enabled', rloc: 'EFUSE_RD_REPEAT_DATA3_REG[25]', bloc: 'B19[1]'} + HYS_EN_PAD0 : {show: y, blk : 0, word: 4, pos: 26, len : 6, start: 154, type : 'uint:6', wr_dis : 19, rd_dis: null, alt : '', dict : '', desc: Set bits to enable hysteresis function of PAD0~5, rloc: 'EFUSE_RD_REPEAT_DATA3_REG[31:26]', bloc: 'B19[7:2]'} + HYS_EN_PAD1 : {show: y, blk : 0, word: 5, pos : 0, len : 22, start: 160, type : 'uint:22', wr_dis : 19, rd_dis: null, alt : '', dict : '', desc: Set bits to enable hysteresis function of PAD6~27, rloc: 'EFUSE_RD_REPEAT_DATA4_REG[21:0]', bloc: 'B20,B21,B22[5:0]'} + RPT4_RESERVED4_1 : {show: n, blk : 0, word: 5, pos: 22, len : 2, start: 182, type : 'uint:2', wr_dis: null, rd_dis: null, alt : '', dict : '', desc: Reserved, rloc: 'EFUSE_RD_REPEAT_DATA4_REG[23:22]', bloc: 'B22[7:6]'} + RPT4_RESERVED4_0 : {show: n, blk : 0, word: 5, pos: 24, len : 8, start: 184, type : 'uint:8', wr_dis: null, rd_dis: null, alt : '', dict : '', desc: Reserved, rloc: 'EFUSE_RD_REPEAT_DATA4_REG[31:24]', bloc: B23} + MAC : {show: y, blk : 1, word: 0, pos : 0, len : 48, start : 0, type : 'bytes:6', wr_dis : 20, rd_dis: null, alt : MAC_FACTORY, dict : '', desc: MAC address, rloc: EFUSE_RD_MAC_SYS_0_REG, bloc: 'B0,B1,B2,B3,B4,B5'} + MAC_EXT : {show: y, blk : 1, word: 1, pos: 16, len : 16, start : 48, type : 'bytes:2', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: Stores the extended bits of MAC address, rloc: 'EFUSE_RD_MAC_SYS_1_REG[31:16]', bloc: 'B6,B7'} + RXIQ_VERSION : {show: y, blk : 1, word: 2, pos : 0, len : 3, start : 64, type : 'uint:3', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: RF Calibration data. RXIQ version, rloc: 'EFUSE_RD_MAC_SYS_2_REG[2:0]', bloc: 'B8[2:0]'} + RXIQ_0 : {show: y, blk : 1, word: 2, pos : 3, len : 7, start : 67, type : 'uint:7', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: RF Calibration data. RXIQ data 0, rloc: 'EFUSE_RD_MAC_SYS_2_REG[9:3]', bloc: 'B8[7:3],B9[1:0]'} + RXIQ_1 : {show: y, blk : 1, word: 2, pos: 10, len : 7, start : 74, type : 'uint:7', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: RF Calibration data. RXIQ data 1, rloc: 'EFUSE_RD_MAC_SYS_2_REG[16:10]', bloc: 'B9[7:2],B10[0]'} + RESERVED_1_81 : {show: n, blk : 1, word: 2, pos: 17, len : 15, start : 81, type : 'uint:15', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: reserved, rloc: 'EFUSE_RD_MAC_SYS_2_REG[31:17]', bloc: 'B10[7:1],B11'} + MAC_RESERVED_2 : {show: n, blk : 1, word: 3, pos : 0, len : 18, start : 96, type : 'uint:18', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: Reserved, rloc: 'EFUSE_RD_MAC_SYS_3_REG[17:0]', bloc: 'B12,B13,B14[1:0]'} + WAFER_VERSION_MINOR : {show: y, blk : 1, word: 3, pos: 18, len : 3, start: 114, type : 'uint:3', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: '', rloc: 'EFUSE_RD_MAC_SYS_3_REG[20:18]', bloc: 'B14[4:2]'} + WAFER_VERSION_MAJOR : {show: y, blk : 1, word: 3, pos: 21, len : 2, start: 117, type : 'uint:2', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: '', rloc: 'EFUSE_RD_MAC_SYS_3_REG[22:21]', bloc: 'B14[6:5]'} + DISABLE_WAFER_VERSION_MAJOR : {show: y, blk : 1, word: 3, pos: 23, len : 1, start: 119, type : bool, wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: Disables check of wafer version major, rloc: 'EFUSE_RD_MAC_SYS_3_REG[23]', bloc: 'B14[7]'} + FLASH_CAP : {show: y, blk : 1, word: 3, pos: 24, len : 3, start: 120, type : 'uint:3', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: '', rloc: 'EFUSE_RD_MAC_SYS_3_REG[26:24]', bloc: 'B15[2:0]'} + FLASH_TEMP : {show: y, blk : 1, word: 3, pos: 27, len : 2, start: 123, type : 'uint:2', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: '', rloc: 'EFUSE_RD_MAC_SYS_3_REG[28:27]', bloc: 'B15[4:3]'} + FLASH_VENDOR : {show: y, blk : 1, word: 3, pos: 29, len : 3, start: 125, type : 'uint:3', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: '', rloc: 'EFUSE_RD_MAC_SYS_3_REG[31:29]', bloc: 'B15[7:5]'} + PKG_VERSION : {show: y, blk : 1, word: 4, pos : 0, len : 3, start: 128, type : 'uint:3', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: Package version, rloc: 'EFUSE_RD_MAC_SYS_4_REG[2:0]', bloc: 'B16[2:0]'} + RESERVED_1_131 : {show: n, blk : 1, word: 4, pos : 3, len : 29, start: 131, type : 'uint:29', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: reserved, rloc: 'EFUSE_RD_MAC_SYS_4_REG[31:3]', bloc: 'B16[7:3],B17,B18,B19'} + SYS_DATA_PART0_2 : {show: n, blk : 1, word: 5, pos : 0, len : 32, start: 160, type : 'uint:32', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: Stores the second 32 bits of the zeroth part of system data, rloc: EFUSE_RD_MAC_SYS_5_REG, bloc: 'B20,B21,B22,B23'} + OPTIONAL_UNIQUE_ID : {show: y, blk : 2, word: 0, pos : 0, len: 128, start : 0, type: 'bytes:16', wr_dis : 21, rd_dis: null, alt : '', dict : '', desc: Optional unique 128-bit ID, rloc: EFUSE_RD_SYS_PART1_DATA0_REG, bloc: 'B0,B1,B2,B3,B4,B5,B6,B7,B8,B9,B10,B11,B12,B13,B14,B15'} + RESERVED_2_128 : {show: n, blk : 2, word: 4, pos : 0, len : 2, start: 128, type : 'uint:2', wr_dis : 21, rd_dis: null, alt : '', dict : '', desc: reserved, rloc: 'EFUSE_RD_SYS_PART1_DATA4_REG[1:0]', bloc: 'B16[1:0]'} + BLK_VERSION_MINOR : {show: y, blk : 2, word: 4, pos : 2, len : 3, start: 130, type : 'uint:3', wr_dis : 21, rd_dis: null, alt : '', dict : '', desc: 'BLK_VERSION_MINOR of BLOCK2. 1: RF Calibration data in BLOCK1', rloc: 'EFUSE_RD_SYS_PART1_DATA4_REG[4:2]', bloc: 'B16[4:2]'} + BLK_VERSION_MAJOR : {show: y, blk : 2, word: 4, pos : 5, len : 2, start: 133, type : 'uint:2', wr_dis : 21, rd_dis: null, alt : '', dict : '', desc: BLK_VERSION_MAJOR of BLOCK2, rloc: 'EFUSE_RD_SYS_PART1_DATA4_REG[6:5]', bloc: 'B16[6:5]'} + DISABLE_BLK_VERSION_MAJOR : {show: y, blk : 2, word: 4, pos : 7, len : 1, start: 135, type : bool, wr_dis : 21, rd_dis: null, alt : '', dict : '', desc: Disables check of blk version major, rloc: 'EFUSE_RD_SYS_PART1_DATA4_REG[7]', bloc: 'B16[7]'} + TEMP_CALIB : {show: y, blk : 2, word: 4, pos : 8, len : 9, start: 136, type : 'uint:9', wr_dis : 21, rd_dis: null, alt : '', dict : '', desc: Temperature calibration data, rloc: 'EFUSE_RD_SYS_PART1_DATA4_REG[16:8]', bloc: 'B17,B18[0]'} + ADC1_AVE_INITCODE_ATTEN0 : {show: y, blk : 2, word: 4, pos: 17, len : 10, start: 145, type : 'uint:10', wr_dis : 21, rd_dis: null, alt : '', dict : '', desc: ADC1 calibration data, rloc: 'EFUSE_RD_SYS_PART1_DATA4_REG[26:17]', bloc: 'B18[7:1],B19[2:0]'} + ADC1_AVE_INITCODE_ATTEN1 : {show: y, blk : 2, word: 4, pos: 27, len : 10, start: 155, type : 'uint:10', wr_dis : 21, rd_dis: null, alt : '', dict : '', desc: ADC1 calibration data, rloc: 'EFUSE_RD_SYS_PART1_DATA4_REG[31:27]', bloc: 'B19[7:3],B20[4:0]'} + ADC1_AVE_INITCODE_ATTEN2 : {show: y, blk : 2, word: 5, pos : 5, len : 10, start: 165, type : 'uint:10', wr_dis : 21, rd_dis: null, alt : '', dict : '', desc: ADC1 calibration data, rloc: 'EFUSE_RD_SYS_PART1_DATA5_REG[14:5]', bloc: 'B20[7:5],B21[6:0]'} + ADC1_AVE_INITCODE_ATTEN3 : {show: y, blk : 2, word: 5, pos: 15, len : 10, start: 175, type : 'uint:10', wr_dis : 21, rd_dis: null, alt : '', dict : '', desc: ADC1 calibration data, rloc: 'EFUSE_RD_SYS_PART1_DATA5_REG[24:15]', bloc: 'B21[7],B22,B23[0]'} + ADC1_HI_DOUT_ATTEN0 : {show: y, blk : 2, word: 5, pos: 25, len : 10, start: 185, type : 'uint:10', wr_dis : 21, rd_dis: null, alt : '', dict : '', desc: ADC1 calibration data, rloc: 'EFUSE_RD_SYS_PART1_DATA5_REG[31:25]', bloc: 'B23[7:1],B24[2:0]'} + ADC1_HI_DOUT_ATTEN1 : {show: y, blk : 2, word: 6, pos : 3, len : 10, start: 195, type : 'uint:10', wr_dis : 21, rd_dis: null, alt : '', dict : '', desc: ADC1 calibration data, rloc: 'EFUSE_RD_SYS_PART1_DATA6_REG[12:3]', bloc: 'B24[7:3],B25[4:0]'} + ADC1_HI_DOUT_ATTEN2 : {show: y, blk : 2, word: 6, pos: 13, len : 10, start: 205, type : 'uint:10', wr_dis : 21, rd_dis: null, alt : '', dict : '', desc: ADC1 calibration data, rloc: 'EFUSE_RD_SYS_PART1_DATA6_REG[22:13]', bloc: 'B25[7:5],B26[6:0]'} + ADC1_HI_DOUT_ATTEN3 : {show: y, blk : 2, word: 6, pos: 23, len : 10, start: 215, type : 'uint:10', wr_dis : 21, rd_dis: null, alt : '', dict : '', desc: ADC1 calibration data, rloc: 'EFUSE_RD_SYS_PART1_DATA6_REG[31:23]', bloc: 'B26[7],B27,B28[0]'} + ADC1_CH0_ATTEN0_INITCODE_DIFF : {show: y, blk : 2, word: 7, pos : 1, len : 4, start: 225, type : 'uint:4', wr_dis : 21, rd_dis: null, alt : '', dict : '', desc: ADC1 calibration data, rloc: 'EFUSE_RD_SYS_PART1_DATA7_REG[4:1]', bloc: 'B28[4:1]'} + ADC1_CH1_ATTEN0_INITCODE_DIFF : {show: y, blk : 2, word: 7, pos : 5, len : 4, start: 229, type : 'uint:4', wr_dis : 21, rd_dis: null, alt : '', dict : '', desc: ADC1 calibration data, rloc: 'EFUSE_RD_SYS_PART1_DATA7_REG[8:5]', bloc: 'B28[7:5],B29[0]'} + ADC1_CH2_ATTEN0_INITCODE_DIFF : {show: y, blk : 2, word: 7, pos : 9, len : 4, start: 233, type : 'uint:4', wr_dis : 21, rd_dis: null, alt : '', dict : '', desc: ADC1 calibration data, rloc: 'EFUSE_RD_SYS_PART1_DATA7_REG[12:9]', bloc: 'B29[4:1]'} + ADC1_CH3_ATTEN0_INITCODE_DIFF : {show: y, blk : 2, word: 7, pos: 13, len : 4, start: 237, type : 'uint:4', wr_dis : 21, rd_dis: null, alt : '', dict : '', desc: ADC1 calibration data, rloc: 'EFUSE_RD_SYS_PART1_DATA7_REG[16:13]', bloc: 'B29[7:5],B30[0]'} + ADC1_CH4_ATTEN0_INITCODE_DIFF : {show: y, blk : 2, word: 7, pos: 17, len : 4, start: 241, type : 'uint:4', wr_dis : 21, rd_dis: null, alt : '', dict : '', desc: ADC1 calibration data, rloc: 'EFUSE_RD_SYS_PART1_DATA7_REG[20:17]', bloc: 'B30[4:1]'} + RESERVED_2_245 : {show: n, blk : 2, word: 7, pos: 21, len : 11, start: 245, type : 'uint:11', wr_dis : 21, rd_dis: null, alt : '', dict : '', desc: reserved, rloc: 'EFUSE_RD_SYS_PART1_DATA7_REG[31:21]', bloc: 'B30[7:5],B31'} + BLOCK_USR_DATA : {show: y, blk : 3, word: 0, pos : 0, len: 192, start : 0, type: 'bytes:24', wr_dis : 22, rd_dis: null, alt : USER_DATA, dict : '', desc: User data, rloc: EFUSE_RD_USR_DATA0_REG, bloc: 'B0,B1,B2,B3,B4,B5,B6,B7,B8,B9,B10,B11,B12,B13,B14,B15,B16,B17,B18,B19,B20,B21,B22,B23'} + RESERVED_3_192 : {show: n, blk : 3, word: 6, pos : 0, len : 8, start: 192, type : 'uint:8', wr_dis : 22, rd_dis: null, alt : '', dict : '', desc: reserved, rloc: 'EFUSE_RD_USR_DATA6_REG[7:0]', bloc: B24} + CUSTOM_MAC : {show: y, blk : 3, word: 6, pos : 8, len : 48, start: 200, type : 'bytes:6', wr_dis : 22, rd_dis: null, alt: MAC_CUSTOM USER_DATA_MAC_CUSTOM, dict : '', desc: Custom MAC, rloc: 'EFUSE_RD_USR_DATA6_REG[31:8]', bloc: 'B25,B26,B27,B28,B29,B30'} + RESERVED_3_248 : {show: n, blk : 3, word: 7, pos: 24, len : 8, start: 248, type : 'uint:8', wr_dis : 22, rd_dis: null, alt : '', dict : '', desc: reserved, rloc: 'EFUSE_RD_USR_DATA7_REG[31:24]', bloc: B31} + BLOCK_KEY0 : {show: y, blk : 4, word: 0, pos : 0, len: 256, start : 0, type: 'bytes:32', wr_dis : 23, rd_dis : 0, alt : KEY0, dict : '', desc: Key0 or user data, rloc: EFUSE_RD_KEY0_DATA0_REG, bloc: 'B0,B1,B2,B3,B4,B5,B6,B7,B8,B9,B10,B11,B12,B13,B14,B15,B16,B17,B18,B19,B20,B21,B22,B23,B24,B25,B26,B27,B28,B29,B30,B31'} + BLOCK_KEY1 : {show: y, blk : 5, word: 0, pos : 0, len: 256, start : 0, type: 'bytes:32', wr_dis : 24, rd_dis : 1, alt : KEY1, dict : '', desc: Key1 or user data, rloc: EFUSE_RD_KEY1_DATA0_REG, bloc: 'B0,B1,B2,B3,B4,B5,B6,B7,B8,B9,B10,B11,B12,B13,B14,B15,B16,B17,B18,B19,B20,B21,B22,B23,B24,B25,B26,B27,B28,B29,B30,B31'} + BLOCK_KEY2 : {show: y, blk : 6, word: 0, pos : 0, len: 256, start : 0, type: 'bytes:32', wr_dis : 25, rd_dis : 2, alt : KEY2, dict : '', desc: Key2 or user data, rloc: EFUSE_RD_KEY2_DATA0_REG, bloc: 'B0,B1,B2,B3,B4,B5,B6,B7,B8,B9,B10,B11,B12,B13,B14,B15,B16,B17,B18,B19,B20,B21,B22,B23,B24,B25,B26,B27,B28,B29,B30,B31'} + BLOCK_KEY3 : {show: y, blk : 7, word: 0, pos : 0, len: 256, start : 0, type: 'bytes:32', wr_dis : 26, rd_dis : 3, alt : KEY3, dict : '', desc: Key3 or user data, rloc: EFUSE_RD_KEY3_DATA0_REG, bloc: 'B0,B1,B2,B3,B4,B5,B6,B7,B8,B9,B10,B11,B12,B13,B14,B15,B16,B17,B18,B19,B20,B21,B22,B23,B24,B25,B26,B27,B28,B29,B30,B31'} + BLOCK_KEY4 : {show: y, blk : 8, word: 0, pos : 0, len: 256, start : 0, type: 'bytes:32', wr_dis : 27, rd_dis : 4, alt : KEY4, dict : '', desc: Key4 or user data, rloc: EFUSE_RD_KEY4_DATA0_REG, bloc: 'B0,B1,B2,B3,B4,B5,B6,B7,B8,B9,B10,B11,B12,B13,B14,B15,B16,B17,B18,B19,B20,B21,B22,B23,B24,B25,B26,B27,B28,B29,B30,B31'} + BLOCK_KEY5 : {show: y, blk : 9, word: 0, pos : 0, len: 256, start : 0, type: 'bytes:32', wr_dis : 28, rd_dis : 5, alt : KEY5, dict : '', desc: Key5 or user data, rloc: EFUSE_RD_KEY5_DATA0_REG, bloc: 'B0,B1,B2,B3,B4,B5,B6,B7,B8,B9,B10,B11,B12,B13,B14,B15,B16,B17,B18,B19,B20,B21,B22,B23,B24,B25,B26,B27,B28,B29,B30,B31'} + BLOCK_SYS_DATA2 : {show: y, blk: 10, word: 0, pos : 0, len: 256, start : 0, type: 'bytes:32', wr_dis : 29, rd_dis : 6, alt : SYS_DATA_PART2, dict : '', desc: System data part 2 (reserved), rloc: EFUSE_RD_SYS_PART2_DATA0_REG, bloc: 'B0,B1,B2,B3,B4,B5,B6,B7,B8,B9,B10,B11,B12,B13,B14,B15,B16,B17,B18,B19,B20,B21,B22,B23,B24,B25,B26,B27,B28,B29,B30,B31'} diff --git a/code/.venv/lib/python3.12/site-packages/espefuse/efuse_defs/esp32p4.yaml b/code/.venv/lib/python3.12/site-packages/espefuse/efuse_defs/esp32p4.yaml new file mode 100644 index 0000000..fe10b2e --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/espefuse/efuse_defs/esp32p4.yaml @@ -0,0 +1,91 @@ +VER_NO: 95ae7b662df04208c40c69564ea06a28 +EFUSES: + WR_DIS : {show: y, blk : 0, word: 0, pos : 0, len : 32, start : 0, type : 'uint:32', wr_dis: null, rd_dis: null, alt : '', dict : '', desc: Disable programming of individual eFuses, rloc: EFUSE_RD_WR_DIS_REG, bloc: 'B0,B1,B2,B3'} + RD_DIS : {show: y, blk : 0, word: 1, pos : 0, len : 7, start : 32, type : 'uint:7', wr_dis : 0, rd_dis: null, alt : '', dict : '', desc: Disable reading from BlOCK4-10, rloc: 'EFUSE_RD_REPEAT_DATA0_REG[6:0]', bloc: 'B4[6:0]'} + USB_DEVICE_EXCHG_PINS : {show: y, blk : 0, word: 1, pos : 7, len : 1, start : 39, type : bool, wr_dis: null, rd_dis: null, alt : '', dict : '', desc: Enable usb device exchange pins of D+ and D-, rloc: 'EFUSE_RD_REPEAT_DATA0_REG[7]', bloc: 'B4[7]'} + USB_OTG11_EXCHG_PINS : {show: y, blk : 0, word: 1, pos : 8, len : 1, start : 40, type : bool, wr_dis: null, rd_dis: null, alt : '', dict : '', desc: Enable usb otg11 exchange pins of D+ and D-, rloc: 'EFUSE_RD_REPEAT_DATA0_REG[8]', bloc: 'B5[0]'} + DIS_USB_JTAG : {show: y, blk : 0, word: 1, pos : 9, len : 1, start : 41, type : bool, wr_dis: null, rd_dis: null, alt : '', dict : '', desc: 'Represents whether the function of usb switch to jtag is disabled or enabled. 1: disabled. 0: enabled', rloc: 'EFUSE_RD_REPEAT_DATA0_REG[9]', bloc: 'B5[1]'} + POWERGLITCH_EN : {show: y, blk : 0, word: 1, pos: 10, len : 1, start : 42, type : bool, wr_dis: null, rd_dis: null, alt : '', dict : '', desc: 'Represents whether power glitch function is enabled. 1: enabled. 0: disabled', rloc: 'EFUSE_RD_REPEAT_DATA0_REG[10]', bloc: 'B5[2]'} + DIS_USB_SERIAL_JTAG : {show: n, blk : 0, word: 1, pos: 11, len : 1, start : 43, type : bool, wr_dis: null, rd_dis: null, alt : '', dict : '', desc: 'Represents whether USB-Serial-JTAG is disabled or enabled. 1: disabled. 0: enabled', rloc: 'EFUSE_RD_REPEAT_DATA0_REG[11]', bloc: 'B5[3]'} + DIS_FORCE_DOWNLOAD : {show: y, blk : 0, word: 1, pos: 12, len : 1, start : 44, type : bool, wr_dis: null, rd_dis: null, alt : '', dict : '', desc: 'Represents whether the function that forces chip into download mode is disabled or enabled. 1: disabled. 0: enabled', rloc: 'EFUSE_RD_REPEAT_DATA0_REG[12]', bloc: 'B5[4]'} + SPI_DOWNLOAD_MSPI_DIS : {show: y, blk : 0, word: 1, pos: 13, len : 1, start : 45, type : bool, wr_dis: null, rd_dis: null, alt : '', dict : '', desc: Set this bit to disable accessing MSPI flash/MSPI ram by SYS AXI matrix during boot_mode_download, rloc: 'EFUSE_RD_REPEAT_DATA0_REG[13]', bloc: 'B5[5]'} + DIS_TWAI : {show: y, blk : 0, word: 1, pos: 14, len : 1, start : 46, type : bool, wr_dis: null, rd_dis: null, alt : '', dict : '', desc: 'Represents whether TWAI function is disabled or enabled. 1: disabled. 0: enabled', rloc: 'EFUSE_RD_REPEAT_DATA0_REG[14]', bloc: 'B5[6]'} + JTAG_SEL_ENABLE : {show: y, blk : 0, word: 1, pos: 15, len : 1, start : 47, type : bool, wr_dis: null, rd_dis: null, alt : '', dict : '', desc: 'Represents whether the selection between usb_to_jtag and pad_to_jtag through strapping gpio15 when both EFUSE_DIS_PAD_JTAG and EFUSE_DIS_USB_JTAG are equal to 0 is enabled or disabled. 1: enabled. 0: disabled', rloc: 'EFUSE_RD_REPEAT_DATA0_REG[15]', bloc: 'B5[7]'} + SOFT_DIS_JTAG : {show: y, blk : 0, word: 1, pos: 16, len : 3, start : 48, type : 'uint:3', wr_dis: null, rd_dis: null, alt : '', dict : '', desc: 'Represents whether JTAG is disabled in soft way. Odd number: disabled. Even number: enabled', rloc: 'EFUSE_RD_REPEAT_DATA0_REG[18:16]', bloc: 'B6[2:0]'} + DIS_PAD_JTAG : {show: y, blk : 0, word: 1, pos: 19, len : 1, start : 51, type : bool, wr_dis: null, rd_dis: null, alt : '', dict : '', desc: 'Represents whether JTAG is disabled in the hard way(permanently). 1: disabled. 0: enabled', rloc: 'EFUSE_RD_REPEAT_DATA0_REG[19]', bloc: 'B6[3]'} + DIS_DOWNLOAD_MANUAL_ENCRYPT : {show: y, blk : 0, word: 1, pos: 20, len : 1, start : 52, type : bool, wr_dis: null, rd_dis: null, alt : '', dict : '', desc: 'Represents whether flash encrypt function is disabled or enabled(except in SPI boot mode). 1: disabled. 0: enabled', rloc: 'EFUSE_RD_REPEAT_DATA0_REG[20]', bloc: 'B6[4]'} + USB_DEVICE_DREFH : {show: n, blk : 0, word: 1, pos: 21, len : 2, start : 53, type : 'uint:2', wr_dis: null, rd_dis: null, alt : '', dict : '', desc: USB intphy of usb device signle-end input high threshold; 1.76V to 2V. Step by 80mV, rloc: 'EFUSE_RD_REPEAT_DATA0_REG[22:21]', bloc: 'B6[6:5]'} + USB_OTG11_DREFH : {show: n, blk : 0, word: 1, pos: 23, len : 2, start : 55, type : 'uint:2', wr_dis: null, rd_dis: null, alt : '', dict : '', desc: USB intphy of usb otg11 signle-end input high threshold; 1.76V to 2V. Step by 80mV, rloc: 'EFUSE_RD_REPEAT_DATA0_REG[24:23]', bloc: 'B6[7],B7[0]'} + USB_PHY_SEL : {show: y, blk : 0, word: 1, pos: 25, len : 1, start : 57, type : bool, wr_dis: null, rd_dis: null, alt : '', dict : '', desc: TBD, rloc: 'EFUSE_RD_REPEAT_DATA0_REG[25]', bloc: 'B7[1]'} + KM_HUK_GEN_STATE_LOW : {show: y, blk : 0, word: 1, pos: 26, len : 6, start : 58, type : 'uint:6', wr_dis: null, rd_dis: null, alt : '', dict : '', desc: Set this bit to control validation of HUK generate mode. Odd of 1 is invalid; even of 1 is valid, rloc: 'EFUSE_RD_REPEAT_DATA0_REG[31:26]', bloc: 'B7[7:2]'} + KM_HUK_GEN_STATE_HIGH : {show: y, blk : 0, word: 2, pos : 0, len : 3, start : 64, type : 'uint:3', wr_dis: null, rd_dis: null, alt : '', dict : '', desc: Set this bit to control validation of HUK generate mode. Odd of 1 is invalid; even of 1 is valid, rloc: 'EFUSE_RD_REPEAT_DATA1_REG[2:0]', bloc: 'B8[2:0]'} + KM_RND_SWITCH_CYCLE : {show: y, blk : 0, word: 2, pos : 3, len : 2, start : 67, type : 'uint:2', wr_dis: null, rd_dis: null, alt : '', dict : '', desc: 'Set bits to control key manager random number switch cycle. 0: control by register. 1: 8 km clk cycles. 2: 16 km cycles. 3: 32 km cycles', rloc: 'EFUSE_RD_REPEAT_DATA1_REG[4:3]', bloc: 'B8[4:3]'} + KM_DEPLOY_ONLY_ONCE : {show: y, blk : 0, word: 2, pos : 5, len : 4, start : 69, type : 'uint:4', wr_dis: null, rd_dis: null, alt : '', dict : '', desc: 'Set each bit to control whether corresponding key can only be deployed once. 1 is true; 0 is false. Bit0: ecdsa. Bit1: xts. Bit2: hmac. Bit3: ds', rloc: 'EFUSE_RD_REPEAT_DATA1_REG[8:5]', bloc: 'B8[7:5],B9[0]'} + FORCE_USE_KEY_MANAGER_KEY : {show: y, blk : 0, word: 2, pos : 9, len : 4, start : 73, type : 'uint:4', wr_dis: null, rd_dis: null, alt : '', dict : '', desc: 'Set each bit to control whether corresponding key must come from key manager.. 1 is true; 0 is false. Bit0: ecdsa. Bit1: xts. Bit2: hmac. Bit3: ds', rloc: 'EFUSE_RD_REPEAT_DATA1_REG[12:9]', bloc: 'B9[4:1]'} + FORCE_DISABLE_SW_INIT_KEY : {show: y, blk : 0, word: 2, pos: 13, len : 1, start : 77, type : bool, wr_dis: null, rd_dis: null, alt : '', dict : '', desc: Set this bit to disable software written init key; and force use efuse_init_key, rloc: 'EFUSE_RD_REPEAT_DATA1_REG[13]', bloc: 'B9[5]'} + XTS_KEY_LENGTH_256 : {show: y, blk : 0, word: 2, pos: 14, len : 1, start : 78, type : bool, wr_dis: null, rd_dis: null, alt : '', dict : '', desc: Set this bit to configure flash encryption use xts-128 key; else use xts-256 key, rloc: 'EFUSE_RD_REPEAT_DATA1_REG[14]', bloc: 'B9[6]'} + RESERVE_0_79 : {show: n, blk : 0, word: 2, pos: 15, len : 1, start : 79, type : bool, wr_dis: null, rd_dis: null, alt : '', dict : '', desc: Reserved; it was created by set_missed_fields_in_regs func, rloc: 'EFUSE_RD_REPEAT_DATA1_REG[15]', bloc: 'B9[7]'} + WDT_DELAY_SEL : {show: y, blk : 0, word: 2, pos: 16, len : 2, start : 80, type : 'uint:2', wr_dis: null, rd_dis: null, alt : '', dict : '', desc: 'Represents whether RTC watchdog timeout threshold is selected at startup. 1: selected. 0: not selected', rloc: 'EFUSE_RD_REPEAT_DATA1_REG[17:16]', bloc: 'B10[1:0]'} + SPI_BOOT_CRYPT_CNT : {show: y, blk : 0, word: 2, pos: 18, len : 3, start : 82, type : 'uint:3', wr_dis : 4, rd_dis: null, alt : '', dict: '{0: "Disable", 1: "Enable", 3: "Disable", 7: "Enable"}', desc: Enables flash encryption when 1 or 3 bits are set and disables otherwise, rloc: 'EFUSE_RD_REPEAT_DATA1_REG[20:18]', bloc: 'B10[4:2]'} + SECURE_BOOT_KEY_REVOKE0 : {show: y, blk : 0, word: 2, pos: 21, len : 1, start : 85, type : bool, wr_dis : 5, rd_dis: null, alt : '', dict : '', desc: Revoke 1st secure boot key, rloc: 'EFUSE_RD_REPEAT_DATA1_REG[21]', bloc: 'B10[5]'} + SECURE_BOOT_KEY_REVOKE1 : {show: y, blk : 0, word: 2, pos: 22, len : 1, start : 86, type : bool, wr_dis : 6, rd_dis: null, alt : '', dict : '', desc: Revoke 2nd secure boot key, rloc: 'EFUSE_RD_REPEAT_DATA1_REG[22]', bloc: 'B10[6]'} + SECURE_BOOT_KEY_REVOKE2 : {show: y, blk : 0, word: 2, pos: 23, len : 1, start : 87, type : bool, wr_dis : 7, rd_dis: null, alt : '', dict : '', desc: Revoke 3rd secure boot key, rloc: 'EFUSE_RD_REPEAT_DATA1_REG[23]', bloc: 'B10[7]'} + KEY_PURPOSE_0 : {show: y, blk : 0, word: 2, pos: 24, len : 4, start : 88, type : 'uint:4', wr_dis : 8, rd_dis: null, alt : KEY0_PURPOSE, dict : '', desc: Represents the purpose of Key0, rloc: 'EFUSE_RD_REPEAT_DATA1_REG[27:24]', bloc: 'B11[3:0]'} + KEY_PURPOSE_1 : {show: y, blk : 0, word: 2, pos: 28, len : 4, start : 92, type : 'uint:4', wr_dis : 9, rd_dis: null, alt : KEY1_PURPOSE, dict : '', desc: Represents the purpose of Key1, rloc: 'EFUSE_RD_REPEAT_DATA1_REG[31:28]', bloc: 'B11[7:4]'} + KEY_PURPOSE_2 : {show: y, blk : 0, word: 3, pos : 0, len : 4, start : 96, type : 'uint:4', wr_dis : 10, rd_dis: null, alt : KEY2_PURPOSE, dict : '', desc: Represents the purpose of Key2, rloc: 'EFUSE_RD_REPEAT_DATA2_REG[3:0]', bloc: 'B12[3:0]'} + KEY_PURPOSE_3 : {show: y, blk : 0, word: 3, pos : 4, len : 4, start: 100, type : 'uint:4', wr_dis : 11, rd_dis: null, alt : KEY3_PURPOSE, dict : '', desc: Represents the purpose of Key3, rloc: 'EFUSE_RD_REPEAT_DATA2_REG[7:4]', bloc: 'B12[7:4]'} + KEY_PURPOSE_4 : {show: y, blk : 0, word: 3, pos : 8, len : 4, start: 104, type : 'uint:4', wr_dis : 12, rd_dis: null, alt : KEY4_PURPOSE, dict : '', desc: Represents the purpose of Key4, rloc: 'EFUSE_RD_REPEAT_DATA2_REG[11:8]', bloc: 'B13[3:0]'} + KEY_PURPOSE_5 : {show: y, blk : 0, word: 3, pos: 12, len : 4, start: 108, type : 'uint:4', wr_dis : 13, rd_dis: null, alt : KEY5_PURPOSE, dict : '', desc: Represents the purpose of Key5, rloc: 'EFUSE_RD_REPEAT_DATA2_REG[15:12]', bloc: 'B13[7:4]'} + SEC_DPA_LEVEL : {show: y, blk : 0, word: 3, pos: 16, len : 2, start: 112, type : 'uint:2', wr_dis: null, rd_dis: null, alt : '', dict : '', desc: Represents the spa secure level by configuring the clock random divide mode, rloc: 'EFUSE_RD_REPEAT_DATA2_REG[17:16]', bloc: 'B14[1:0]'} + ECDSA_ENABLE_SOFT_K : {show: y, blk : 0, word: 3, pos: 18, len : 1, start: 114, type : bool, wr_dis: null, rd_dis: null, alt : '', dict : '', desc: 'Represents whether hardware random number k is forced used in ESDCA. 1: force used. 0: not force used', rloc: 'EFUSE_RD_REPEAT_DATA2_REG[18]', bloc: 'B14[2]'} + CRYPT_DPA_ENABLE : {show: y, blk : 0, word: 3, pos: 19, len : 1, start: 115, type : bool, wr_dis: null, rd_dis: null, alt : '', dict : '', desc: 'Represents whether anti-dpa attack is enabled. 1:enabled. 0: disabled', rloc: 'EFUSE_RD_REPEAT_DATA2_REG[19]', bloc: 'B14[3]'} + SECURE_BOOT_EN : {show: y, blk : 0, word: 3, pos: 20, len : 1, start: 116, type : bool, wr_dis: null, rd_dis: null, alt : '', dict : '', desc: 'Represents whether secure boot is enabled or disabled. 1: enabled. 0: disabled', rloc: 'EFUSE_RD_REPEAT_DATA2_REG[20]', bloc: 'B14[4]'} + SECURE_BOOT_AGGRESSIVE_REVOKE : {show: y, blk : 0, word: 3, pos: 21, len : 1, start: 117, type : bool, wr_dis: null, rd_dis: null, alt : '', dict : '', desc: 'Represents whether revoking aggressive secure boot is enabled or disabled. 1: enabled. 0: disabled', rloc: 'EFUSE_RD_REPEAT_DATA2_REG[21]', bloc: 'B14[5]'} + RESERVE_0_118 : {show: n, blk : 0, word: 3, pos: 22, len : 1, start: 118, type : bool, wr_dis: null, rd_dis: null, alt : '', dict : '', desc: Reserved; it was created by set_missed_fields_in_regs func, rloc: 'EFUSE_RD_REPEAT_DATA2_REG[22]', bloc: 'B14[6]'} + FLASH_TYPE : {show: y, blk : 0, word: 3, pos: 23, len : 1, start: 119, type : bool, wr_dis: null, rd_dis: null, alt : '', dict : '', desc: 'The type of interfaced flash. 0: four data lines; 1: eight data lines', rloc: 'EFUSE_RD_REPEAT_DATA2_REG[23]', bloc: 'B14[7]'} + FLASH_PAGE_SIZE : {show: y, blk : 0, word: 3, pos: 24, len : 2, start: 120, type : 'uint:2', wr_dis: null, rd_dis: null, alt : '', dict : '', desc: Set flash page size, rloc: 'EFUSE_RD_REPEAT_DATA2_REG[25:24]', bloc: 'B15[1:0]'} + FLASH_ECC_EN : {show: y, blk : 0, word: 3, pos: 26, len : 1, start: 122, type : bool, wr_dis: null, rd_dis: null, alt : '', dict : '', desc: Set this bit to enable ecc for flash boot, rloc: 'EFUSE_RD_REPEAT_DATA2_REG[26]', bloc: 'B15[2]'} + DIS_USB_OTG_DOWNLOAD_MODE : {show: y, blk : 0, word: 3, pos: 27, len : 1, start: 123, type : bool, wr_dis: null, rd_dis: null, alt : '', dict : '', desc: Set this bit to disable download via USB-OTG, rloc: 'EFUSE_RD_REPEAT_DATA2_REG[27]', bloc: 'B15[3]'} + FLASH_TPUW : {show: y, blk : 0, word: 3, pos: 28, len : 4, start: 124, type : 'uint:4', wr_dis: null, rd_dis: null, alt : '', dict : '', desc: Represents the flash waiting time after power-up; in unit of ms. When the value less than 15; the waiting time is the programmed value. Otherwise; the waiting time is 2 times the programmed value, rloc: 'EFUSE_RD_REPEAT_DATA2_REG[31:28]', bloc: 'B15[7:4]'} + DIS_DOWNLOAD_MODE : {show: y, blk : 0, word: 4, pos : 0, len : 1, start: 128, type : bool, wr_dis: null, rd_dis: null, alt : '', dict : '', desc: 'Represents whether Download mode is disabled or enabled. 1: disabled. 0: enabled', rloc: 'EFUSE_RD_REPEAT_DATA3_REG[0]', bloc: 'B16[0]'} + DIS_DIRECT_BOOT : {show: y, blk : 0, word: 4, pos : 1, len : 1, start: 129, type : bool, wr_dis: null, rd_dis: null, alt : '', dict : '', desc: 'Represents whether direct boot mode is disabled or enabled. 1: disabled. 0: enabled', rloc: 'EFUSE_RD_REPEAT_DATA3_REG[1]', bloc: 'B16[1]'} + DIS_USB_SERIAL_JTAG_ROM_PRINT : {show: y, blk : 0, word: 4, pos : 2, len : 1, start: 130, type : bool, wr_dis: null, rd_dis: null, alt : '', dict : '', desc: 'Represents whether print from USB-Serial-JTAG is disabled or enabled. 1: disabled. 0: enabled', rloc: 'EFUSE_RD_REPEAT_DATA3_REG[2]', bloc: 'B16[2]'} + LOCK_KM_KEY : {show: y, blk : 0, word: 4, pos : 3, len : 1, start: 131, type : bool, wr_dis: null, rd_dis: null, alt : '', dict : '', desc: TBD, rloc: 'EFUSE_RD_REPEAT_DATA3_REG[3]', bloc: 'B16[3]'} + DIS_USB_SERIAL_JTAG_DOWNLOAD_MODE: {show: y, blk : 0, word: 4, pos : 4, len : 1, start: 132, type : bool, wr_dis: null, rd_dis: null, alt : '', dict : '', desc: 'Represents whether the USB-Serial-JTAG download function is disabled or enabled. 1: disabled. 0: enabled', rloc: 'EFUSE_RD_REPEAT_DATA3_REG[4]', bloc: 'B16[4]'} + ENABLE_SECURITY_DOWNLOAD : {show: y, blk : 0, word: 4, pos : 5, len : 1, start: 133, type : bool, wr_dis: null, rd_dis: null, alt : '', dict : '', desc: 'Represents whether security download is enabled or disabled. 1: enabled. 0: disabled', rloc: 'EFUSE_RD_REPEAT_DATA3_REG[5]', bloc: 'B16[5]'} + UART_PRINT_CONTROL : {show: y, blk : 0, word: 4, pos : 6, len : 2, start: 134, type : 'uint:2', wr_dis: null, rd_dis: null, alt : '', dict : '', desc: 'Represents the type of UART printing. 00: force enable printing. 01: enable printing when GPIO8 is reset at low level. 10: enable printing when GPIO8 is reset at high level. 11: force disable printing', rloc: 'EFUSE_RD_REPEAT_DATA3_REG[7:6]', bloc: 'B16[7:6]'} + FORCE_SEND_RESUME : {show: y, blk : 0, word: 4, pos : 8, len : 1, start: 136, type : bool, wr_dis: null, rd_dis: null, alt : '', dict : '', desc: 'Represents whether ROM code is forced to send a resume command during SPI boot. 1: forced. 0:not forced', rloc: 'EFUSE_RD_REPEAT_DATA3_REG[8]', bloc: 'B17[0]'} + SECURE_VERSION : {show: y, blk : 0, word: 4, pos : 9, len : 16, start: 137, type : 'uint:16', wr_dis: null, rd_dis: null, alt : '', dict : '', desc: Represents the version used by ESP-IDF anti-rollback feature, rloc: 'EFUSE_RD_REPEAT_DATA3_REG[24:9]', bloc: 'B17[7:1],B18,B19[0]'} + SECURE_BOOT_DISABLE_FAST_WAKE : {show: y, blk : 0, word: 4, pos: 25, len : 1, start: 153, type : bool, wr_dis: null, rd_dis: null, alt : '', dict : '', desc: 'Represents whether FAST VERIFY ON WAKE is disabled or enabled when Secure Boot is enabled. 1: disabled. 0: enabled', rloc: 'EFUSE_RD_REPEAT_DATA3_REG[25]', bloc: 'B19[1]'} + HYS_EN_PAD : {show: y, blk : 0, word: 4, pos: 26, len : 1, start: 154, type : bool, wr_dis: null, rd_dis: null, alt : '', dict : '', desc: 'Represents whether the hysteresis function of corresponding PAD is enabled. 1: enabled. 0:disabled', rloc: 'EFUSE_RD_REPEAT_DATA3_REG[26]', bloc: 'B19[2]'} + DCDC_VSET : {show: y, blk : 0, word: 4, pos: 27, len : 5, start: 155, type : 'uint:5', wr_dis: null, rd_dis: null, alt : '', dict : '', desc: Set the dcdc voltage default, rloc: 'EFUSE_RD_REPEAT_DATA3_REG[31:27]', bloc: 'B19[7:3]'} + PXA0_TIEH_SEL_0 : {show: y, blk : 0, word: 5, pos : 0, len : 2, start: 160, type : 'uint:2', wr_dis: null, rd_dis: null, alt : 0PXA_TIEH_SEL_0, dict : '', desc: TBD, rloc: 'EFUSE_RD_REPEAT_DATA4_REG[1:0]', bloc: 'B20[1:0]'} + PXA0_TIEH_SEL_1 : {show: y, blk : 0, word: 5, pos : 2, len : 2, start: 162, type : 'uint:2', wr_dis: null, rd_dis: null, alt : 0PXA_TIEH_SEL_1, dict : '', desc: TBD, rloc: 'EFUSE_RD_REPEAT_DATA4_REG[3:2]', bloc: 'B20[3:2]'} + PXA0_TIEH_SEL_2 : {show: y, blk : 0, word: 5, pos : 4, len : 2, start: 164, type : 'uint:2', wr_dis: null, rd_dis: null, alt : 0PXA_TIEH_SEL_2, dict : '', desc: TBD, rloc: 'EFUSE_RD_REPEAT_DATA4_REG[5:4]', bloc: 'B20[5:4]'} + PXA0_TIEH_SEL_3 : {show: y, blk : 0, word: 5, pos : 6, len : 2, start: 166, type : 'uint:2', wr_dis: null, rd_dis: null, alt : 0PXA_TIEH_SEL_3, dict : '', desc: TBD, rloc: 'EFUSE_RD_REPEAT_DATA4_REG[7:6]', bloc: 'B20[7:6]'} + KM_DISABLE_DEPLOY_MODE : {show: y, blk : 0, word: 5, pos : 8, len : 4, start: 168, type : 'uint:4', wr_dis: null, rd_dis: null, alt : '', dict : '', desc: TBD, rloc: 'EFUSE_RD_REPEAT_DATA4_REG[11:8]', bloc: 'B21[3:0]'} + USB_DEVICE_DREFL : {show: n, blk : 0, word: 5, pos: 12, len : 2, start: 172, type : 'uint:2', wr_dis: null, rd_dis: null, alt : '', dict : '', desc: Represents the usb device single-end input low threhold; 0.8 V to 1.04 V with step of 80 mV, rloc: 'EFUSE_RD_REPEAT_DATA4_REG[13:12]', bloc: 'B21[5:4]'} + USB_OTG11_DREFL : {show: n, blk : 0, word: 5, pos: 14, len : 2, start: 174, type : 'uint:2', wr_dis: null, rd_dis: null, alt : '', dict : '', desc: Represents the usb otg11 single-end input low threhold; 0.8 V to 1.04 V with step of 80 mV, rloc: 'EFUSE_RD_REPEAT_DATA4_REG[15:14]', bloc: 'B21[7:6]'} + RESERVE_0_176 : {show: n, blk : 0, word: 5, pos: 16, len : 2, start: 176, type : 'uint:2', wr_dis: null, rd_dis: null, alt : '', dict : '', desc: Reserved; it was created by set_missed_fields_in_regs func, rloc: 'EFUSE_RD_REPEAT_DATA4_REG[17:16]', bloc: 'B22[1:0]'} + HP_PWR_SRC_SEL : {show: y, blk : 0, word: 5, pos: 18, len : 1, start: 178, type : bool, wr_dis: null, rd_dis: null, alt : '', dict : '', desc: 'HP system power source select. 0:LDO. 1: DCDC', rloc: 'EFUSE_RD_REPEAT_DATA4_REG[18]', bloc: 'B22[2]'} + DCDC_VSET_EN : {show: y, blk : 0, word: 5, pos: 19, len : 1, start: 179, type : bool, wr_dis: null, rd_dis: null, alt : '', dict : '', desc: Select dcdc vset use efuse_dcdc_vset, rloc: 'EFUSE_RD_REPEAT_DATA4_REG[19]', bloc: 'B22[3]'} + DIS_WDT : {show: y, blk : 0, word: 5, pos: 20, len : 1, start: 180, type : bool, wr_dis: null, rd_dis: null, alt : '', dict : '', desc: Set this bit to disable watch dog, rloc: 'EFUSE_RD_REPEAT_DATA4_REG[20]', bloc: 'B22[4]'} + DIS_SWD : {show: y, blk : 0, word: 5, pos: 21, len : 1, start: 181, type : bool, wr_dis: null, rd_dis: null, alt : '', dict : '', desc: Set this bit to disable super-watchdog, rloc: 'EFUSE_RD_REPEAT_DATA4_REG[21]', bloc: 'B22[5]'} + RESERVE_0_182 : {show: n, blk : 0, word: 5, pos: 22, len : 10, start: 182, type : 'uint:10', wr_dis: null, rd_dis: null, alt : '', dict : '', desc: Reserved; it was created by set_missed_fields_in_regs func, rloc: 'EFUSE_RD_REPEAT_DATA4_REG[31:22]', bloc: 'B22[7:6],B23'} + MAC : {show: y, blk : 1, word: 0, pos : 0, len : 48, start : 0, type : 'bytes:6', wr_dis : 20, rd_dis: null, alt : MAC_FACTORY, dict : '', desc: MAC address, rloc: EFUSE_RD_MAC_SYS_0_REG, bloc: 'B0,B1,B2,B3,B4,B5'} + MAC_EXT : {show: y, blk : 1, word: 1, pos: 16, len : 16, start : 48, type : 'bytes:2', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: Stores the extended bits of MAC address, rloc: 'EFUSE_RD_MAC_SYS_1_REG[31:16]', bloc: 'B6,B7'} + MAC_RESERVED_1 : {show: n, blk : 1, word: 2, pos : 0, len : 14, start : 64, type : 'uint:14', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: Reserved, rloc: 'EFUSE_RD_MAC_SYS_2_REG[13:0]', bloc: 'B8,B9[5:0]'} + MAC_RESERVED_0 : {show: n, blk : 1, word: 2, pos: 14, len : 18, start : 78, type : 'uint:18', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: Reserved, rloc: 'EFUSE_RD_MAC_SYS_2_REG[31:14]', bloc: 'B9[7:6],B10,B11'} + MAC_RESERVED_2 : {show: n, blk : 1, word: 3, pos : 0, len : 18, start : 96, type : 'uint:18', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: Reserved, rloc: 'EFUSE_RD_MAC_SYS_3_REG[17:0]', bloc: 'B12,B13,B14[1:0]'} + SYS_DATA_PART0_0 : {show: n, blk : 1, word: 3, pos: 18, len : 14, start: 114, type : 'uint:14', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: Stores the first 14 bits of the zeroth part of system data, rloc: 'EFUSE_RD_MAC_SYS_3_REG[31:18]', bloc: 'B14[7:2],B15'} + SYS_DATA_PART0_1 : {show: n, blk : 1, word: 4, pos : 0, len : 32, start: 128, type : 'uint:32', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: Stores the first 32 bits of the zeroth part of system data, rloc: EFUSE_RD_MAC_SYS_4_REG, bloc: 'B16,B17,B18,B19'} + SYS_DATA_PART0_2 : {show: n, blk : 1, word: 5, pos : 0, len : 32, start: 160, type : 'uint:32', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: Stores the second 32 bits of the zeroth part of system data, rloc: EFUSE_RD_MAC_SYS_5_REG, bloc: 'B20,B21,B22,B23'} + BLOCK_SYS_DATA1 : {show: y, blk : 2, word: 0, pos : 0, len: 256, start : 0, type: 'bytes:32', wr_dis : 21, rd_dis: null, alt : SYS_DATA_PART1, dict : '', desc: System data part 1, rloc: EFUSE_RD_SYS_PART1_DATA0_REG, bloc: 'B0,B1,B2,B3,B4,B5,B6,B7,B8,B9,B10,B11,B12,B13,B14,B15,B16,B17,B18,B19,B20,B21,B22,B23,B24,B25,B26,B27,B28,B29,B30,B31'} + BLOCK_USR_DATA : {show: y, blk : 3, word: 0, pos : 0, len: 256, start : 0, type: 'bytes:32', wr_dis : 22, rd_dis: null, alt : USER_DATA, dict : '', desc: User data, rloc: EFUSE_RD_USR_DATA0_REG, bloc: 'B0,B1,B2,B3,B4,B5,B6,B7,B8,B9,B10,B11,B12,B13,B14,B15,B16,B17,B18,B19,B20,B21,B22,B23,B24,B25,B26,B27,B28,B29,B30,B31'} + BLOCK_KEY0 : {show: y, blk : 4, word: 0, pos : 0, len: 256, start : 0, type: 'bytes:32', wr_dis : 23, rd_dis : 0, alt : KEY0, dict : '', desc: Key0 or user data, rloc: EFUSE_RD_KEY0_DATA0_REG, bloc: 'B0,B1,B2,B3,B4,B5,B6,B7,B8,B9,B10,B11,B12,B13,B14,B15,B16,B17,B18,B19,B20,B21,B22,B23,B24,B25,B26,B27,B28,B29,B30,B31'} + BLOCK_KEY1 : {show: y, blk : 5, word: 0, pos : 0, len: 256, start : 0, type: 'bytes:32', wr_dis : 24, rd_dis : 1, alt : KEY1, dict : '', desc: Key1 or user data, rloc: EFUSE_RD_KEY1_DATA0_REG, bloc: 'B0,B1,B2,B3,B4,B5,B6,B7,B8,B9,B10,B11,B12,B13,B14,B15,B16,B17,B18,B19,B20,B21,B22,B23,B24,B25,B26,B27,B28,B29,B30,B31'} + BLOCK_KEY2 : {show: y, blk : 6, word: 0, pos : 0, len: 256, start : 0, type: 'bytes:32', wr_dis : 25, rd_dis : 2, alt : KEY2, dict : '', desc: Key2 or user data, rloc: EFUSE_RD_KEY2_DATA0_REG, bloc: 'B0,B1,B2,B3,B4,B5,B6,B7,B8,B9,B10,B11,B12,B13,B14,B15,B16,B17,B18,B19,B20,B21,B22,B23,B24,B25,B26,B27,B28,B29,B30,B31'} + BLOCK_KEY3 : {show: y, blk : 7, word: 0, pos : 0, len: 256, start : 0, type: 'bytes:32', wr_dis : 26, rd_dis : 3, alt : KEY3, dict : '', desc: Key3 or user data, rloc: EFUSE_RD_KEY3_DATA0_REG, bloc: 'B0,B1,B2,B3,B4,B5,B6,B7,B8,B9,B10,B11,B12,B13,B14,B15,B16,B17,B18,B19,B20,B21,B22,B23,B24,B25,B26,B27,B28,B29,B30,B31'} + BLOCK_KEY4 : {show: y, blk : 8, word: 0, pos : 0, len: 256, start : 0, type: 'bytes:32', wr_dis : 27, rd_dis : 4, alt : KEY4, dict : '', desc: Key4 or user data, rloc: EFUSE_RD_KEY4_DATA0_REG, bloc: 'B0,B1,B2,B3,B4,B5,B6,B7,B8,B9,B10,B11,B12,B13,B14,B15,B16,B17,B18,B19,B20,B21,B22,B23,B24,B25,B26,B27,B28,B29,B30,B31'} + BLOCK_KEY5 : {show: y, blk : 9, word: 0, pos : 0, len: 256, start : 0, type: 'bytes:32', wr_dis : 28, rd_dis : 5, alt : KEY5, dict : '', desc: Key5 or user data, rloc: EFUSE_RD_KEY5_DATA0_REG, bloc: 'B0,B1,B2,B3,B4,B5,B6,B7,B8,B9,B10,B11,B12,B13,B14,B15,B16,B17,B18,B19,B20,B21,B22,B23,B24,B25,B26,B27,B28,B29,B30,B31'} + BLOCK_SYS_DATA2 : {show: y, blk: 10, word: 0, pos : 0, len: 256, start : 0, type: 'bytes:32', wr_dis : 29, rd_dis : 6, alt : SYS_DATA_PART2, dict : '', desc: System data part 2 (reserved), rloc: EFUSE_RD_SYS_PART2_DATA0_REG, bloc: 'B0,B1,B2,B3,B4,B5,B6,B7,B8,B9,B10,B11,B12,B13,B14,B15,B16,B17,B18,B19,B20,B21,B22,B23,B24,B25,B26,B27,B28,B29,B30,B31'} diff --git a/code/.venv/lib/python3.12/site-packages/espefuse/efuse_defs/esp32s2.yaml b/code/.venv/lib/python3.12/site-packages/espefuse/efuse_defs/esp32s2.yaml new file mode 100644 index 0000000..45b5900 --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/espefuse/efuse_defs/esp32s2.yaml @@ -0,0 +1,119 @@ +VER_NO: 888a61f6f500d9c7ee0aa32016b0bee7 +EFUSES: + WR_DIS : {show: y, blk : 0, word: 0, pos : 0, len : 32, start : 0, type : 'uint:32', wr_dis: null, rd_dis: null, alt : '', dict : '', desc: Disable programming of individual eFuses, rloc: EFUSE_RD_WR_DIS_REG, bloc: 'B0,B1,B2,B3'} + RD_DIS : {show: y, blk : 0, word: 1, pos : 0, len : 7, start : 32, type : 'uint:7', wr_dis : 0, rd_dis: null, alt : '', dict : '', desc: Disable reading from BlOCK4-10, rloc: 'EFUSE_RD_REPEAT_DATA0_REG[6:0]', bloc: 'B4[6:0]'} + DIS_RTC_RAM_BOOT : {show: n, blk : 0, word: 1, pos : 7, len : 1, start : 39, type : bool, wr_dis : 1, rd_dis: null, alt : '', dict : '', desc: Reserved, rloc: 'EFUSE_RD_REPEAT_DATA0_REG[7]', bloc: 'B4[7]'} + DIS_ICACHE : {show: y, blk : 0, word: 1, pos : 8, len : 1, start : 40, type : bool, wr_dis : 2, rd_dis: null, alt : '', dict : '', desc: Set this bit to disable Icache, rloc: 'EFUSE_RD_REPEAT_DATA0_REG[8]', bloc: 'B5[0]'} + DIS_DCACHE : {show: y, blk : 0, word: 1, pos : 9, len : 1, start : 41, type : bool, wr_dis : 2, rd_dis: null, alt : '', dict : '', desc: Set this bit to disable Dcache, rloc: 'EFUSE_RD_REPEAT_DATA0_REG[9]', bloc: 'B5[1]'} + DIS_DOWNLOAD_ICACHE : {show: y, blk : 0, word: 1, pos: 10, len : 1, start : 42, type : bool, wr_dis : 2, rd_dis: null, alt : '', dict : '', desc: Disables Icache when SoC is in Download mode, rloc: 'EFUSE_RD_REPEAT_DATA0_REG[10]', bloc: 'B5[2]'} + DIS_DOWNLOAD_DCACHE : {show: y, blk : 0, word: 1, pos: 11, len : 1, start : 43, type : bool, wr_dis : 2, rd_dis: null, alt : '', dict : '', desc: Disables Dcache when SoC is in Download mode, rloc: 'EFUSE_RD_REPEAT_DATA0_REG[11]', bloc: 'B5[3]'} + DIS_FORCE_DOWNLOAD : {show: y, blk : 0, word: 1, pos: 12, len : 1, start : 44, type : bool, wr_dis : 2, rd_dis: null, alt : '', dict : '', desc: Set this bit to disable the function that forces chip into download mode, rloc: 'EFUSE_RD_REPEAT_DATA0_REG[12]', bloc: 'B5[4]'} + DIS_USB : {show: y, blk : 0, word: 1, pos: 13, len : 1, start : 45, type : bool, wr_dis : 2, rd_dis: null, alt : '', dict : '', desc: Set this bit to disable USB OTG function, rloc: 'EFUSE_RD_REPEAT_DATA0_REG[13]', bloc: 'B5[5]'} + DIS_TWAI : {show: y, blk : 0, word: 1, pos: 14, len : 1, start : 46, type : bool, wr_dis : 2, rd_dis: null, alt : DIS_CAN, dict : '', desc: Set this bit to disable the TWAI Controller function, rloc: 'EFUSE_RD_REPEAT_DATA0_REG[14]', bloc: 'B5[6]'} + DIS_BOOT_REMAP : {show: y, blk : 0, word: 1, pos: 15, len : 1, start : 47, type : bool, wr_dis : 2, rd_dis: null, alt : '', dict : '', desc: Disables capability to Remap RAM to ROM address space, rloc: 'EFUSE_RD_REPEAT_DATA0_REG[15]', bloc: 'B5[7]'} + RPT4_RESERVED5 : {show: n, blk : 0, word: 1, pos: 16, len : 1, start : 48, type : bool, wr_dis: null, rd_dis: null, alt : '', dict : '', desc: Reserved (used for four backups method), rloc: 'EFUSE_RD_REPEAT_DATA0_REG[16]', bloc: 'B6[0]'} + SOFT_DIS_JTAG : {show: y, blk : 0, word: 1, pos: 17, len : 1, start : 49, type : bool, wr_dis : 2, rd_dis: null, alt : '', dict : '', desc: Software disables JTAG. When software disabled; JTAG can be activated temporarily by HMAC peripheral, rloc: 'EFUSE_RD_REPEAT_DATA0_REG[17]', bloc: 'B6[1]'} + HARD_DIS_JTAG : {show: y, blk : 0, word: 1, pos: 18, len : 1, start : 50, type : bool, wr_dis : 2, rd_dis: null, alt : '', dict : '', desc: Hardware disables JTAG permanently, rloc: 'EFUSE_RD_REPEAT_DATA0_REG[18]', bloc: 'B6[2]'} + DIS_DOWNLOAD_MANUAL_ENCRYPT : {show: y, blk : 0, word: 1, pos: 19, len : 1, start : 51, type : bool, wr_dis : 2, rd_dis: null, alt : '', dict : '', desc: Disables flash encryption when in download boot modes, rloc: 'EFUSE_RD_REPEAT_DATA0_REG[19]', bloc: 'B6[3]'} + USB_DREFH : {show: n, blk : 0, word: 1, pos: 20, len : 2, start : 52, type : 'uint:2', wr_dis : 30, rd_dis: null, alt : '', dict : '', desc: Controls single-end input threshold vrefh; 1.76 V to 2 V with step of 80 mV; stored in eFuse, rloc: 'EFUSE_RD_REPEAT_DATA0_REG[21:20]', bloc: 'B6[5:4]'} + USB_DREFL : {show: n, blk : 0, word: 1, pos: 22, len : 2, start : 54, type : 'uint:2', wr_dis : 30, rd_dis: null, alt : '', dict : '', desc: Controls single-end input threshold vrefl; 0.8 V to 1.04 V with step of 80 mV; stored in eFuse, rloc: 'EFUSE_RD_REPEAT_DATA0_REG[23:22]', bloc: 'B6[7:6]'} + USB_EXCHG_PINS : {show: y, blk : 0, word: 1, pos: 24, len : 1, start : 56, type : bool, wr_dis : 30, rd_dis: null, alt : '', dict : '', desc: Set this bit to exchange USB D+ and D- pins, rloc: 'EFUSE_RD_REPEAT_DATA0_REG[24]', bloc: 'B7[0]'} + USB_EXT_PHY_ENABLE : {show: y, blk : 0, word: 1, pos: 25, len : 1, start : 57, type : bool, wr_dis : 30, rd_dis: null, alt : EXT_PHY_ENABLE, dict : '', desc: Set this bit to enable external USB PHY, rloc: 'EFUSE_RD_REPEAT_DATA0_REG[25]', bloc: 'B7[1]'} + USB_FORCE_NOPERSIST : {show: y, blk : 0, word: 1, pos: 26, len : 1, start : 58, type : bool, wr_dis : 30, rd_dis: null, alt : '', dict : '', desc: If set; forces USB BVALID to 1, rloc: 'EFUSE_RD_REPEAT_DATA0_REG[26]', bloc: 'B7[2]'} + BLOCK0_VERSION : {show: y, blk : 0, word: 1, pos: 27, len : 2, start : 59, type : 'uint:2', wr_dis : 30, rd_dis: null, alt : '', dict : '', desc: BLOCK0 efuse version, rloc: 'EFUSE_RD_REPEAT_DATA0_REG[28:27]', bloc: 'B7[4:3]'} + VDD_SPI_MODECURLIM : {show: n, blk : 0, word: 1, pos: 29, len : 1, start : 61, type : bool, wr_dis : 3, rd_dis: null, alt : '', dict : '', desc: SPI regulator switches current limit mode, rloc: 'EFUSE_RD_REPEAT_DATA0_REG[29]', bloc: 'B7[5]'} + VDD_SPI_DREFH : {show: n, blk : 0, word: 1, pos: 30, len : 2, start : 62, type : 'uint:2', wr_dis : 3, rd_dis: null, alt : '', dict : '', desc: SPI regulator high voltage reference, rloc: 'EFUSE_RD_REPEAT_DATA0_REG[31:30]', bloc: 'B7[7:6]'} + VDD_SPI_DREFM : {show: n, blk : 0, word: 2, pos : 0, len : 2, start : 64, type : 'uint:2', wr_dis : 3, rd_dis: null, alt : '', dict : '', desc: SPI regulator medium voltage reference, rloc: 'EFUSE_RD_REPEAT_DATA1_REG[1:0]', bloc: 'B8[1:0]'} + VDD_SPI_DREFL : {show: n, blk : 0, word: 2, pos : 2, len : 2, start : 66, type : 'uint:2', wr_dis : 3, rd_dis: null, alt : '', dict : '', desc: SPI regulator low voltage reference, rloc: 'EFUSE_RD_REPEAT_DATA1_REG[3:2]', bloc: 'B8[3:2]'} + VDD_SPI_XPD : {show: y, blk : 0, word: 2, pos : 4, len : 1, start : 68, type : bool, wr_dis : 3, rd_dis: null, alt : '', dict : '', desc: If VDD_SPI_FORCE is 1; this value determines if the VDD_SPI regulator is powered on, rloc: 'EFUSE_RD_REPEAT_DATA1_REG[4]', bloc: 'B8[4]'} + VDD_SPI_TIEH : {show: y, blk : 0, word: 2, pos : 5, len : 1, start : 69, type : bool, wr_dis : 3, rd_dis: null, alt : '', dict: '{0: "VDD_SPI connects to 1.8 V LDO", 1: "VDD_SPI connects to VDD3P3_RTC_IO"}', desc: If VDD_SPI_FORCE is 1; determines VDD_SPI voltage, rloc: 'EFUSE_RD_REPEAT_DATA1_REG[5]', bloc: 'B8[5]'} + VDD_SPI_FORCE : {show: y, blk : 0, word: 2, pos : 6, len : 1, start : 70, type : bool, wr_dis : 3, rd_dis: null, alt : '', dict : '', desc: Set this bit to use XPD_VDD_PSI_REG and VDD_SPI_TIEH to configure VDD_SPI LDO, rloc: 'EFUSE_RD_REPEAT_DATA1_REG[6]', bloc: 'B8[6]'} + VDD_SPI_EN_INIT : {show: n, blk : 0, word: 2, pos : 7, len : 1, start : 71, type : bool, wr_dis : 2, rd_dis: null, alt : '', dict : '', desc: 'Set SPI regulator to 0 to configure init[1:0]=0', rloc: 'EFUSE_RD_REPEAT_DATA1_REG[7]', bloc: 'B8[7]'} + VDD_SPI_ENCURLIM : {show: n, blk : 0, word: 2, pos : 8, len : 1, start : 72, type : bool, wr_dis : 2, rd_dis: null, alt : '', dict : '', desc: Set SPI regulator to 1 to enable output current limit, rloc: 'EFUSE_RD_REPEAT_DATA1_REG[8]', bloc: 'B9[0]'} + VDD_SPI_DCURLIM : {show: n, blk : 0, word: 2, pos : 9, len : 3, start : 73, type : 'uint:3', wr_dis : 2, rd_dis: null, alt : '', dict : '', desc: Tunes the current limit threshold of SPI regulator when tieh=0; about 800 mA/(8+d), rloc: 'EFUSE_RD_REPEAT_DATA1_REG[11:9]', bloc: 'B9[3:1]'} + VDD_SPI_INIT : {show: n, blk : 0, word: 2, pos: 12, len : 2, start : 76, type : 'uint:2', wr_dis : 2, rd_dis: null, alt : '', dict: '{0: "no resistance", 1: "6K", 2: "4K", 3: "2K"}', desc: Adds resistor from LDO output to ground, rloc: 'EFUSE_RD_REPEAT_DATA1_REG[13:12]', bloc: 'B9[5:4]'} + VDD_SPI_DCAP : {show: n, blk : 0, word: 2, pos: 14, len : 2, start : 78, type : 'uint:2', wr_dis : 2, rd_dis: null, alt : '', dict : '', desc: Prevents SPI regulator from overshoot, rloc: 'EFUSE_RD_REPEAT_DATA1_REG[15:14]', bloc: 'B9[7:6]'} + WDT_DELAY_SEL : {show: y, blk : 0, word: 2, pos: 16, len : 2, start : 80, type : 'uint:2', wr_dis : 3, rd_dis: null, alt : '', dict: '{0: "40000", 1: "80000", 2: "160000", 3: "320000"}', desc: RTC watchdog timeout threshold; in unit of slow clock cycle, rloc: 'EFUSE_RD_REPEAT_DATA1_REG[17:16]', bloc: 'B10[1:0]'} + SPI_BOOT_CRYPT_CNT : {show: y, blk : 0, word: 2, pos: 18, len : 3, start : 82, type : 'uint:3', wr_dis : 4, rd_dis: null, alt : '', dict: '{0: "Disable", 1: "Enable", 3: "Disable", 7: "Enable"}', desc: Enables flash encryption when 1 or 3 bits are set and disabled otherwise, rloc: 'EFUSE_RD_REPEAT_DATA1_REG[20:18]', bloc: 'B10[4:2]'} + SECURE_BOOT_KEY_REVOKE0 : {show: y, blk : 0, word: 2, pos: 21, len : 1, start : 85, type : bool, wr_dis : 5, rd_dis: null, alt : '', dict : '', desc: Revoke 1st secure boot key, rloc: 'EFUSE_RD_REPEAT_DATA1_REG[21]', bloc: 'B10[5]'} + SECURE_BOOT_KEY_REVOKE1 : {show: y, blk : 0, word: 2, pos: 22, len : 1, start : 86, type : bool, wr_dis : 6, rd_dis: null, alt : '', dict : '', desc: Revoke 2nd secure boot key, rloc: 'EFUSE_RD_REPEAT_DATA1_REG[22]', bloc: 'B10[6]'} + SECURE_BOOT_KEY_REVOKE2 : {show: y, blk : 0, word: 2, pos: 23, len : 1, start : 87, type : bool, wr_dis : 7, rd_dis: null, alt : '', dict : '', desc: Revoke 3rd secure boot key, rloc: 'EFUSE_RD_REPEAT_DATA1_REG[23]', bloc: 'B10[7]'} + KEY_PURPOSE_0 : {show: y, blk : 0, word: 2, pos: 24, len : 4, start : 88, type : 'uint:4', wr_dis : 8, rd_dis: null, alt : KEY0_PURPOSE, dict : '', desc: Purpose of KEY0, rloc: 'EFUSE_RD_REPEAT_DATA1_REG[27:24]', bloc: 'B11[3:0]'} + KEY_PURPOSE_1 : {show: y, blk : 0, word: 2, pos: 28, len : 4, start : 92, type : 'uint:4', wr_dis : 9, rd_dis: null, alt : KEY1_PURPOSE, dict : '', desc: Purpose of KEY1, rloc: 'EFUSE_RD_REPEAT_DATA1_REG[31:28]', bloc: 'B11[7:4]'} + KEY_PURPOSE_2 : {show: y, blk : 0, word: 3, pos : 0, len : 4, start : 96, type : 'uint:4', wr_dis : 10, rd_dis: null, alt : KEY2_PURPOSE, dict : '', desc: Purpose of KEY2, rloc: 'EFUSE_RD_REPEAT_DATA2_REG[3:0]', bloc: 'B12[3:0]'} + KEY_PURPOSE_3 : {show: y, blk : 0, word: 3, pos : 4, len : 4, start: 100, type : 'uint:4', wr_dis : 11, rd_dis: null, alt : KEY3_PURPOSE, dict : '', desc: Purpose of KEY3, rloc: 'EFUSE_RD_REPEAT_DATA2_REG[7:4]', bloc: 'B12[7:4]'} + KEY_PURPOSE_4 : {show: y, blk : 0, word: 3, pos : 8, len : 4, start: 104, type : 'uint:4', wr_dis : 12, rd_dis: null, alt : KEY4_PURPOSE, dict : '', desc: Purpose of KEY4, rloc: 'EFUSE_RD_REPEAT_DATA2_REG[11:8]', bloc: 'B13[3:0]'} + KEY_PURPOSE_5 : {show: y, blk : 0, word: 3, pos: 12, len : 4, start: 108, type : 'uint:4', wr_dis : 13, rd_dis: null, alt : KEY5_PURPOSE, dict : '', desc: Purpose of KEY5, rloc: 'EFUSE_RD_REPEAT_DATA2_REG[15:12]', bloc: 'B13[7:4]'} + KEY_PURPOSE_6 : {show: n, blk : 0, word: 3, pos: 16, len : 4, start: 112, type : 'uint:4', wr_dis : 14, rd_dis: null, alt : '', dict : '', desc: Purpose of KEY6, rloc: 'EFUSE_RD_REPEAT_DATA2_REG[19:16]', bloc: 'B14[3:0]'} + SECURE_BOOT_EN : {show: y, blk : 0, word: 3, pos: 20, len : 1, start: 116, type : bool, wr_dis : 15, rd_dis: null, alt : '', dict : '', desc: Set this bit to enable secure boot, rloc: 'EFUSE_RD_REPEAT_DATA2_REG[20]', bloc: 'B14[4]'} + SECURE_BOOT_AGGRESSIVE_REVOKE: {show: y, blk : 0, word: 3, pos: 21, len : 1, start: 117, type : bool, wr_dis : 16, rd_dis: null, alt : '', dict : '', desc: Set this bit to enable aggressive secure boot key revocation mode, rloc: 'EFUSE_RD_REPEAT_DATA2_REG[21]', bloc: 'B14[5]'} + RPT4_RESERVED1 : {show: n, blk : 0, word: 3, pos: 22, len : 6, start: 118, type : 'uint:6', wr_dis: null, rd_dis: null, alt : '', dict : '', desc: Reserved (used for four backups method), rloc: 'EFUSE_RD_REPEAT_DATA2_REG[27:22]', bloc: 'B14[7:6],B15[3:0]'} + FLASH_TPUW : {show: y, blk : 0, word: 3, pos: 28, len : 4, start: 124, type : 'uint:4', wr_dis : 18, rd_dis: null, alt : '', dict : '', desc: Configures flash startup delay after SoC power-up; in unit of (ms/2). When the value is 15; delay is 7.5 ms, rloc: 'EFUSE_RD_REPEAT_DATA2_REG[31:28]', bloc: 'B15[7:4]'} + DIS_DOWNLOAD_MODE : {show: y, blk : 0, word: 4, pos : 0, len : 1, start: 128, type : bool, wr_dis : 18, rd_dis: null, alt : '', dict : '', desc: Set this bit to disable all download boot modes, rloc: 'EFUSE_RD_REPEAT_DATA3_REG[0]', bloc: 'B16[0]'} + DIS_LEGACY_SPI_BOOT : {show: y, blk : 0, word: 4, pos : 1, len : 1, start: 129, type : bool, wr_dis : 18, rd_dis: null, alt : '', dict : '', desc: Set this bit to disable Legacy SPI boot mode, rloc: 'EFUSE_RD_REPEAT_DATA3_REG[1]', bloc: 'B16[1]'} + UART_PRINT_CHANNEL : {show: y, blk : 0, word: 4, pos : 2, len : 1, start: 130, type : bool, wr_dis : 18, rd_dis: null, alt : '', dict: '{0: "UART0", 1: "UART1"}', desc: Selects the default UART for printing boot messages, rloc: 'EFUSE_RD_REPEAT_DATA3_REG[2]', bloc: 'B16[2]'} + RPT4_RESERVED3 : {show: n, blk : 0, word: 4, pos : 3, len : 1, start: 131, type : bool, wr_dis: null, rd_dis: null, alt : '', dict : '', desc: Reserved (used for four backups method), rloc: 'EFUSE_RD_REPEAT_DATA3_REG[3]', bloc: 'B16[3]'} + DIS_USB_DOWNLOAD_MODE : {show: y, blk : 0, word: 4, pos : 4, len : 1, start: 132, type : bool, wr_dis : 18, rd_dis: null, alt : '', dict : '', desc: Set this bit to disable use of USB OTG in UART download boot mode, rloc: 'EFUSE_RD_REPEAT_DATA3_REG[4]', bloc: 'B16[4]'} + ENABLE_SECURITY_DOWNLOAD : {show: y, blk : 0, word: 4, pos : 5, len : 1, start: 133, type : bool, wr_dis : 18, rd_dis: null, alt : '', dict : '', desc: Set this bit to enable secure UART download mode (read/write flash only), rloc: 'EFUSE_RD_REPEAT_DATA3_REG[5]', bloc: 'B16[5]'} + UART_PRINT_CONTROL : {show: y, blk : 0, word: 4, pos : 6, len : 2, start: 134, type : 'uint:2', wr_dis : 18, rd_dis: null, alt : '', dict: '{0: "Enable", 1: "Enable when GPIO46 is low at reset", 2: "Enable when GPIO46 is high at reset", 3: "Disable"}', desc: Set the default UART boot message output mode, rloc: 'EFUSE_RD_REPEAT_DATA3_REG[7:6]', bloc: 'B16[7:6]'} + PIN_POWER_SELECTION : {show: y, blk : 0, word: 4, pos : 8, len : 1, start: 136, type : bool, wr_dis : 18, rd_dis: null, alt : '', dict: '{0: "VDD3P3_CPU", 1: "VDD_SPI"}', desc: Set default power supply for GPIO33-GPIO37; set when SPI flash is initialized, rloc: 'EFUSE_RD_REPEAT_DATA3_REG[8]', bloc: 'B17[0]'} + FLASH_TYPE : {show: y, blk : 0, word: 4, pos : 9, len : 1, start: 137, type : bool, wr_dis : 18, rd_dis: null, alt : '', dict: '{0: "4 data lines", 1: "8 data lines"}', desc: SPI flash type, rloc: 'EFUSE_RD_REPEAT_DATA3_REG[9]', bloc: 'B17[1]'} + FORCE_SEND_RESUME : {show: y, blk : 0, word: 4, pos: 10, len : 1, start: 138, type : bool, wr_dis : 18, rd_dis: null, alt : '', dict : '', desc: If set; forces ROM code to send an SPI flash resume command during SPI boot, rloc: 'EFUSE_RD_REPEAT_DATA3_REG[10]', bloc: 'B17[2]'} + SECURE_VERSION : {show: y, blk : 0, word: 4, pos: 11, len : 16, start: 139, type : 'uint:16', wr_dis : 18, rd_dis: null, alt : '', dict : '', desc: Secure version (used by ESP-IDF anti-rollback feature), rloc: 'EFUSE_RD_REPEAT_DATA3_REG[26:11]', bloc: 'B17[7:3],B18,B19[2:0]'} + RPT4_RESERVED2 : {show: n, blk : 0, word: 4, pos: 27, len : 5, start: 155, type : 'uint:5', wr_dis: null, rd_dis: null, alt : '', dict : '', desc: Reserved (used for four backups method), rloc: 'EFUSE_RD_REPEAT_DATA3_REG[31:27]', bloc: 'B19[7:3]'} + DISABLE_WAFER_VERSION_MAJOR : {show: y, blk : 0, word: 5, pos : 0, len : 1, start: 160, type : bool, wr_dis: null, rd_dis: null, alt : '', dict : '', desc: Disables check of wafer version major, rloc: 'EFUSE_RD_REPEAT_DATA4_REG[0]', bloc: 'B20[0]'} + DISABLE_BLK_VERSION_MAJOR : {show: y, blk : 0, word: 5, pos : 1, len : 1, start: 161, type : bool, wr_dis: null, rd_dis: null, alt : '', dict : '', desc: Disables check of blk version major, rloc: 'EFUSE_RD_REPEAT_DATA4_REG[1]', bloc: 'B20[1]'} + RESERVED_0_162 : {show: n, blk : 0, word: 5, pos : 2, len : 22, start: 162, type : 'uint:22', wr_dis: null, rd_dis: null, alt : '', dict : '', desc: reserved, rloc: 'EFUSE_RD_REPEAT_DATA4_REG[23:2]', bloc: 'B20[7:2],B21,B22'} + MAC : {show: y, blk : 1, word: 0, pos : 0, len : 48, start : 0, type : 'bytes:6', wr_dis : 20, rd_dis: null, alt : MAC_FACTORY, dict : '', desc: MAC address, rloc: EFUSE_RD_MAC_SPI_SYS_0_REG, bloc: 'B0,B1,B2,B3,B4,B5'} + SPI_PAD_CONFIG_CLK : {show: y, blk : 1, word: 1, pos: 16, len : 6, start : 48, type : 'uint:6', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: SPI_PAD_configure CLK, rloc: 'EFUSE_RD_MAC_SPI_SYS_1_REG[21:16]', bloc: 'B6[5:0]'} + SPI_PAD_CONFIG_Q : {show: y, blk : 1, word: 1, pos: 22, len : 6, start : 54, type : 'uint:6', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: SPI_PAD_configure Q(D1), rloc: 'EFUSE_RD_MAC_SPI_SYS_1_REG[27:22]', bloc: 'B6[7:6],B7[3:0]'} + SPI_PAD_CONFIG_D : {show: y, blk : 1, word: 1, pos: 28, len : 6, start : 60, type : 'uint:6', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: SPI_PAD_configure D(D0), rloc: 'EFUSE_RD_MAC_SPI_SYS_1_REG[31:28]', bloc: 'B7[7:4],B8[1:0]'} + SPI_PAD_CONFIG_CS : {show: y, blk : 1, word: 2, pos : 2, len : 6, start : 66, type : 'uint:6', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: SPI_PAD_configure CS, rloc: 'EFUSE_RD_MAC_SPI_SYS_2_REG[7:2]', bloc: 'B8[7:2]'} + SPI_PAD_CONFIG_HD : {show: y, blk : 1, word: 2, pos : 8, len : 6, start : 72, type : 'uint:6', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: SPI_PAD_configure HD(D3), rloc: 'EFUSE_RD_MAC_SPI_SYS_2_REG[13:8]', bloc: 'B9[5:0]'} + SPI_PAD_CONFIG_WP : {show: y, blk : 1, word: 2, pos: 14, len : 6, start : 78, type : 'uint:6', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: SPI_PAD_configure WP(D2), rloc: 'EFUSE_RD_MAC_SPI_SYS_2_REG[19:14]', bloc: 'B9[7:6],B10[3:0]'} + SPI_PAD_CONFIG_DQS : {show: y, blk : 1, word: 2, pos: 20, len : 6, start : 84, type : 'uint:6', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: SPI_PAD_configure DQS, rloc: 'EFUSE_RD_MAC_SPI_SYS_2_REG[25:20]', bloc: 'B10[7:4],B11[1:0]'} + SPI_PAD_CONFIG_D4 : {show: y, blk : 1, word: 2, pos: 26, len : 6, start : 90, type : 'uint:6', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: SPI_PAD_configure D4, rloc: 'EFUSE_RD_MAC_SPI_SYS_2_REG[31:26]', bloc: 'B11[7:2]'} + SPI_PAD_CONFIG_D5 : {show: y, blk : 1, word: 3, pos : 0, len : 6, start : 96, type : 'uint:6', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: SPI_PAD_configure D5, rloc: 'EFUSE_RD_MAC_SPI_SYS_3_REG[5:0]', bloc: 'B12[5:0]'} + SPI_PAD_CONFIG_D6 : {show: y, blk : 1, word: 3, pos : 6, len : 6, start: 102, type : 'uint:6', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: SPI_PAD_configure D6, rloc: 'EFUSE_RD_MAC_SPI_SYS_3_REG[11:6]', bloc: 'B12[7:6],B13[3:0]'} + SPI_PAD_CONFIG_D7 : {show: y, blk : 1, word: 3, pos: 12, len : 6, start: 108, type : 'uint:6', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: SPI_PAD_configure D7, rloc: 'EFUSE_RD_MAC_SPI_SYS_3_REG[17:12]', bloc: 'B13[7:4],B14[1:0]'} + WAFER_VERSION_MAJOR : {show: y, blk : 1, word: 3, pos: 18, len : 2, start: 114, type : 'uint:2', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: WAFER_VERSION_MAJOR, rloc: 'EFUSE_RD_MAC_SPI_SYS_3_REG[19:18]', bloc: 'B14[3:2]'} + WAFER_VERSION_MINOR_HI : {show: y, blk : 1, word: 3, pos: 20, len : 1, start: 116, type : bool, wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: WAFER_VERSION_MINOR most significant bit, rloc: 'EFUSE_RD_MAC_SPI_SYS_3_REG[20]', bloc: 'B14[4]'} + FLASH_VERSION : {show: y, blk : 1, word: 3, pos: 21, len : 4, start: 117, type : 'uint:4', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: Flash version, rloc: 'EFUSE_RD_MAC_SPI_SYS_3_REG[24:21]', bloc: 'B14[7:5],B15[0]'} + BLK_VERSION_MAJOR : {show: y, blk : 1, word: 3, pos: 25, len : 2, start: 121, type : 'uint:2', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: BLK_VERSION_MAJOR, rloc: 'EFUSE_RD_MAC_SPI_SYS_3_REG[26:25]', bloc: 'B15[2:1]'} + RESERVED_1_123 : {show: n, blk : 1, word: 3, pos: 27, len : 1, start: 123, type : bool, wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: reserved, rloc: 'EFUSE_RD_MAC_SPI_SYS_3_REG[27]', bloc: 'B15[3]'} + PSRAM_VERSION : {show: y, blk : 1, word: 3, pos: 28, len : 4, start: 124, type : 'uint:4', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: PSRAM version, rloc: 'EFUSE_RD_MAC_SPI_SYS_3_REG[31:28]', bloc: 'B15[7:4]'} + PKG_VERSION : {show: y, blk : 1, word: 4, pos : 0, len : 4, start: 128, type : 'uint:4', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: Package version, rloc: 'EFUSE_RD_MAC_SPI_SYS_4_REG[3:0]', bloc: 'B16[3:0]'} + WAFER_VERSION_MINOR_LO : {show: y, blk : 1, word: 4, pos : 4, len : 3, start: 132, type : 'uint:3', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: WAFER_VERSION_MINOR least significant bits, rloc: 'EFUSE_RD_MAC_SPI_SYS_4_REG[6:4]', bloc: 'B16[6:4]'} + RESERVED_1_135 : {show: n, blk : 1, word: 4, pos : 7, len : 25, start: 135, type : 'uint:25', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: reserved, rloc: 'EFUSE_RD_MAC_SPI_SYS_4_REG[31:7]', bloc: 'B16[7],B17,B18,B19'} + SYS_DATA_PART0_2 : {show: n, blk : 1, word: 5, pos : 0, len : 32, start: 160, type : 'uint:32', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: Stores the second part of the zeroth part of system data, rloc: EFUSE_RD_MAC_SPI_SYS_5_REG, bloc: 'B20,B21,B22,B23'} + OPTIONAL_UNIQUE_ID : {show: y, blk : 2, word: 0, pos : 0, len: 128, start : 0, type: 'bytes:16', wr_dis : 21, rd_dis: null, alt : '', dict : '', desc: Optional unique 128-bit ID, rloc: EFUSE_RD_SYS_PART1_DATA0_REG, bloc: 'B0,B1,B2,B3,B4,B5,B6,B7,B8,B9,B10,B11,B12,B13,B14,B15'} + ADC_CALIB : {show: y, blk : 2, word: 4, pos : 0, len : 4, start: 128, type : 'uint:4', wr_dis : 21, rd_dis: null, alt : '', dict : '', desc: 4 bit of ADC calibration, rloc: 'EFUSE_RD_SYS_PART1_DATA4_REG[3:0]', bloc: 'B16[3:0]'} + BLK_VERSION_MINOR : {show: y, blk : 2, word: 4, pos : 4, len : 3, start: 132, type : 'uint:3', wr_dis : 21, rd_dis: null, alt : '', dict: '{0: "No calib", 1: "ADC calib V1", 2: "ADC calib V2"}', desc: BLK_VERSION_MINOR of BLOCK2, rloc: 'EFUSE_RD_SYS_PART1_DATA4_REG[6:4]', bloc: 'B16[6:4]'} + TEMP_CALIB : {show: y, blk : 2, word: 4, pos : 7, len : 9, start: 135, type : 'uint:9', wr_dis : 21, rd_dis: null, alt : '', dict : '', desc: Temperature calibration data, rloc: 'EFUSE_RD_SYS_PART1_DATA4_REG[15:7]', bloc: 'B16[7],B17'} + RTCCALIB_V1IDX_A10H : {show: y, blk : 2, word: 4, pos: 16, len : 8, start: 144, type : 'uint:8', wr_dis : 21, rd_dis: null, alt : '', dict : '', desc: '', rloc: 'EFUSE_RD_SYS_PART1_DATA4_REG[23:16]', bloc: B18} + RTCCALIB_V1IDX_A11H : {show: y, blk : 2, word: 4, pos: 24, len : 8, start: 152, type : 'uint:8', wr_dis : 21, rd_dis: null, alt : '', dict : '', desc: '', rloc: 'EFUSE_RD_SYS_PART1_DATA4_REG[31:24]', bloc: B19} + RTCCALIB_V1IDX_A12H : {show: y, blk : 2, word: 5, pos : 0, len : 8, start: 160, type : 'uint:8', wr_dis : 21, rd_dis: null, alt : '', dict : '', desc: '', rloc: 'EFUSE_RD_SYS_PART1_DATA5_REG[7:0]', bloc: B20} + RTCCALIB_V1IDX_A13H : {show: y, blk : 2, word: 5, pos : 8, len : 8, start: 168, type : 'uint:8', wr_dis : 21, rd_dis: null, alt : '', dict : '', desc: '', rloc: 'EFUSE_RD_SYS_PART1_DATA5_REG[15:8]', bloc: B21} + RTCCALIB_V1IDX_A20H : {show: y, blk : 2, word: 5, pos: 16, len : 8, start: 176, type : 'uint:8', wr_dis : 21, rd_dis: null, alt : '', dict : '', desc: '', rloc: 'EFUSE_RD_SYS_PART1_DATA5_REG[23:16]', bloc: B22} + RTCCALIB_V1IDX_A21H : {show: y, blk : 2, word: 5, pos: 24, len : 8, start: 184, type : 'uint:8', wr_dis : 21, rd_dis: null, alt : '', dict : '', desc: '', rloc: 'EFUSE_RD_SYS_PART1_DATA5_REG[31:24]', bloc: B23} + RTCCALIB_V1IDX_A22H : {show: y, blk : 2, word: 6, pos : 0, len : 8, start: 192, type : 'uint:8', wr_dis : 21, rd_dis: null, alt : '', dict : '', desc: '', rloc: 'EFUSE_RD_SYS_PART1_DATA6_REG[7:0]', bloc: B24} + RTCCALIB_V1IDX_A23H : {show: y, blk : 2, word: 6, pos : 8, len : 8, start: 200, type : 'uint:8', wr_dis : 21, rd_dis: null, alt : '', dict : '', desc: '', rloc: 'EFUSE_RD_SYS_PART1_DATA6_REG[15:8]', bloc: B25} + RTCCALIB_V1IDX_A10L : {show: y, blk : 2, word: 6, pos: 16, len : 6, start: 208, type : 'uint:6', wr_dis : 21, rd_dis: null, alt : '', dict : '', desc: '', rloc: 'EFUSE_RD_SYS_PART1_DATA6_REG[21:16]', bloc: 'B26[5:0]'} + RTCCALIB_V1IDX_A11L : {show: y, blk : 2, word: 6, pos: 22, len : 6, start: 214, type : 'uint:6', wr_dis : 21, rd_dis: null, alt : '', dict : '', desc: '', rloc: 'EFUSE_RD_SYS_PART1_DATA6_REG[27:22]', bloc: 'B26[7:6],B27[3:0]'} + RTCCALIB_V1IDX_A12L : {show: y, blk : 2, word: 6, pos: 28, len : 6, start: 220, type : 'uint:6', wr_dis : 21, rd_dis: null, alt : '', dict : '', desc: '', rloc: 'EFUSE_RD_SYS_PART1_DATA6_REG[31:28]', bloc: 'B27[7:4],B28[1:0]'} + RTCCALIB_V1IDX_A13L : {show: y, blk : 2, word: 7, pos : 2, len : 6, start: 226, type : 'uint:6', wr_dis : 21, rd_dis: null, alt : '', dict : '', desc: '', rloc: 'EFUSE_RD_SYS_PART1_DATA7_REG[7:2]', bloc: 'B28[7:2]'} + RTCCALIB_V1IDX_A20L : {show: y, blk : 2, word: 7, pos : 8, len : 6, start: 232, type : 'uint:6', wr_dis : 21, rd_dis: null, alt : '', dict : '', desc: '', rloc: 'EFUSE_RD_SYS_PART1_DATA7_REG[13:8]', bloc: 'B29[5:0]'} + RTCCALIB_V1IDX_A21L : {show: y, blk : 2, word: 7, pos: 14, len : 6, start: 238, type : 'uint:6', wr_dis : 21, rd_dis: null, alt : '', dict : '', desc: '', rloc: 'EFUSE_RD_SYS_PART1_DATA7_REG[19:14]', bloc: 'B29[7:6],B30[3:0]'} + RTCCALIB_V1IDX_A22L : {show: y, blk : 2, word: 7, pos: 20, len : 6, start: 244, type : 'uint:6', wr_dis : 21, rd_dis: null, alt : '', dict : '', desc: '', rloc: 'EFUSE_RD_SYS_PART1_DATA7_REG[25:20]', bloc: 'B30[7:4],B31[1:0]'} + RTCCALIB_V1IDX_A23L : {show: y, blk : 2, word: 7, pos: 26, len : 6, start: 250, type : 'uint:6', wr_dis : 21, rd_dis: null, alt : '', dict : '', desc: '', rloc: 'EFUSE_RD_SYS_PART1_DATA7_REG[31:26]', bloc: 'B31[7:2]'} + BLOCK_USR_DATA : {show: y, blk : 3, word: 0, pos : 0, len: 192, start : 0, type: 'bytes:24', wr_dis : 22, rd_dis: null, alt : USER_DATA, dict : '', desc: User data, rloc: EFUSE_RD_USR_DATA0_REG, bloc: 'B0,B1,B2,B3,B4,B5,B6,B7,B8,B9,B10,B11,B12,B13,B14,B15,B16,B17,B18,B19,B20,B21,B22,B23'} + RESERVED_3_192 : {show: n, blk : 3, word: 6, pos : 0, len : 8, start: 192, type : 'uint:8', wr_dis : 22, rd_dis: null, alt : '', dict : '', desc: reserved, rloc: 'EFUSE_RD_USR_DATA6_REG[7:0]', bloc: B24} + CUSTOM_MAC : {show: y, blk : 3, word: 6, pos : 8, len : 48, start: 200, type : 'bytes:6', wr_dis : 22, rd_dis: null, alt: MAC_CUSTOM USER_DATA_MAC_CUSTOM, dict : '', desc: Custom MAC, rloc: 'EFUSE_RD_USR_DATA6_REG[31:8]', bloc: 'B25,B26,B27,B28,B29,B30'} + RESERVED_3_248 : {show: n, blk : 3, word: 7, pos: 24, len : 8, start: 248, type : 'uint:8', wr_dis : 22, rd_dis: null, alt : '', dict : '', desc: reserved, rloc: 'EFUSE_RD_USR_DATA7_REG[31:24]', bloc: B31} + BLOCK_KEY0 : {show: y, blk : 4, word: 0, pos : 0, len: 256, start : 0, type: 'bytes:32', wr_dis : 23, rd_dis : 0, alt : KEY0, dict : '', desc: Key0 or user data, rloc: EFUSE_RD_KEY0_DATA0_REG, bloc: 'B0,B1,B2,B3,B4,B5,B6,B7,B8,B9,B10,B11,B12,B13,B14,B15,B16,B17,B18,B19,B20,B21,B22,B23,B24,B25,B26,B27,B28,B29,B30,B31'} + BLOCK_KEY1 : {show: y, blk : 5, word: 0, pos : 0, len: 256, start : 0, type: 'bytes:32', wr_dis : 24, rd_dis : 1, alt : KEY1, dict : '', desc: Key1 or user data, rloc: EFUSE_RD_KEY1_DATA0_REG, bloc: 'B0,B1,B2,B3,B4,B5,B6,B7,B8,B9,B10,B11,B12,B13,B14,B15,B16,B17,B18,B19,B20,B21,B22,B23,B24,B25,B26,B27,B28,B29,B30,B31'} + BLOCK_KEY2 : {show: y, blk : 6, word: 0, pos : 0, len: 256, start : 0, type: 'bytes:32', wr_dis : 25, rd_dis : 2, alt : KEY2, dict : '', desc: Key2 or user data, rloc: EFUSE_RD_KEY2_DATA0_REG, bloc: 'B0,B1,B2,B3,B4,B5,B6,B7,B8,B9,B10,B11,B12,B13,B14,B15,B16,B17,B18,B19,B20,B21,B22,B23,B24,B25,B26,B27,B28,B29,B30,B31'} + BLOCK_KEY3 : {show: y, blk : 7, word: 0, pos : 0, len: 256, start : 0, type: 'bytes:32', wr_dis : 26, rd_dis : 3, alt : KEY3, dict : '', desc: Key3 or user data, rloc: EFUSE_RD_KEY3_DATA0_REG, bloc: 'B0,B1,B2,B3,B4,B5,B6,B7,B8,B9,B10,B11,B12,B13,B14,B15,B16,B17,B18,B19,B20,B21,B22,B23,B24,B25,B26,B27,B28,B29,B30,B31'} + BLOCK_KEY4 : {show: y, blk : 8, word: 0, pos : 0, len: 256, start : 0, type: 'bytes:32', wr_dis : 27, rd_dis : 4, alt : KEY4, dict : '', desc: Key4 or user data, rloc: EFUSE_RD_KEY4_DATA0_REG, bloc: 'B0,B1,B2,B3,B4,B5,B6,B7,B8,B9,B10,B11,B12,B13,B14,B15,B16,B17,B18,B19,B20,B21,B22,B23,B24,B25,B26,B27,B28,B29,B30,B31'} + BLOCK_KEY5 : {show: y, blk : 9, word: 0, pos : 0, len: 256, start : 0, type: 'bytes:32', wr_dis : 28, rd_dis : 5, alt : KEY5, dict : '', desc: Key5 or user data, rloc: EFUSE_RD_KEY5_DATA0_REG, bloc: 'B0,B1,B2,B3,B4,B5,B6,B7,B8,B9,B10,B11,B12,B13,B14,B15,B16,B17,B18,B19,B20,B21,B22,B23,B24,B25,B26,B27,B28,B29,B30,B31'} + BLOCK_SYS_DATA2 : {show: y, blk: 10, word: 0, pos : 0, len: 256, start : 0, type: 'bytes:32', wr_dis : 29, rd_dis : 6, alt : SYS_DATA_PART2, dict : '', desc: System data part 2 (reserved), rloc: EFUSE_RD_SYS_PART2_DATA0_REG, bloc: 'B0,B1,B2,B3,B4,B5,B6,B7,B8,B9,B10,B11,B12,B13,B14,B15,B16,B17,B18,B19,B20,B21,B22,B23,B24,B25,B26,B27,B28,B29,B30,B31'} diff --git a/code/.venv/lib/python3.12/site-packages/espefuse/efuse_defs/esp32s3.yaml b/code/.venv/lib/python3.12/site-packages/espefuse/efuse_defs/esp32s3.yaml new file mode 100644 index 0000000..4d4c695 --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/espefuse/efuse_defs/esp32s3.yaml @@ -0,0 +1,134 @@ +VER_NO: f75f74727101326a187188a23f4a6c70 +EFUSES: + WR_DIS : {show: y, blk : 0, word: 0, pos : 0, len : 32, start : 0, type : 'uint:32', wr_dis: null, rd_dis: null, alt : '', dict : '', desc: Disable programming of individual eFuses, rloc: EFUSE_RD_WR_DIS_REG, bloc: 'B0,B1,B2,B3'} + RD_DIS : {show: y, blk : 0, word: 1, pos : 0, len : 7, start : 32, type : 'uint:7', wr_dis : 0, rd_dis: null, alt : '', dict : '', desc: Disable reading from BlOCK4-10, rloc: 'EFUSE_RD_REPEAT_DATA0_REG[6:0]', bloc: 'B4[6:0]'} + DIS_RTC_RAM_BOOT : {show: n, blk : 0, word: 1, pos : 7, len : 1, start : 39, type : bool, wr_dis : 1, rd_dis: null, alt : '', dict : '', desc: Set this bit to disable boot from RTC RAM, rloc: 'EFUSE_RD_REPEAT_DATA0_REG[7]', bloc: 'B4[7]'} + DIS_ICACHE : {show: y, blk : 0, word: 1, pos : 8, len : 1, start : 40, type : bool, wr_dis : 2, rd_dis: null, alt : '', dict : '', desc: Set this bit to disable Icache, rloc: 'EFUSE_RD_REPEAT_DATA0_REG[8]', bloc: 'B5[0]'} + DIS_DCACHE : {show: y, blk : 0, word: 1, pos : 9, len : 1, start : 41, type : bool, wr_dis : 2, rd_dis: null, alt : '', dict : '', desc: Set this bit to disable Dcache, rloc: 'EFUSE_RD_REPEAT_DATA0_REG[9]', bloc: 'B5[1]'} + DIS_DOWNLOAD_ICACHE : {show: y, blk : 0, word: 1, pos: 10, len : 1, start : 42, type : bool, wr_dis : 2, rd_dis: null, alt : '', dict : '', desc: 'Set this bit to disable Icache in download mode (boot_mode[3:0] is 0; 1; 2; 3; 6; 7)', rloc: 'EFUSE_RD_REPEAT_DATA0_REG[10]', bloc: 'B5[2]'} + DIS_DOWNLOAD_DCACHE : {show: y, blk : 0, word: 1, pos: 11, len : 1, start : 43, type : bool, wr_dis : 2, rd_dis: null, alt : '', dict : '', desc: 'Set this bit to disable Dcache in download mode ( boot_mode[3:0] is 0; 1; 2; 3; 6; 7)', rloc: 'EFUSE_RD_REPEAT_DATA0_REG[11]', bloc: 'B5[3]'} + DIS_FORCE_DOWNLOAD : {show: y, blk : 0, word: 1, pos: 12, len : 1, start : 44, type : bool, wr_dis : 2, rd_dis: null, alt : '', dict : '', desc: Set this bit to disable the function that forces chip into download mode, rloc: 'EFUSE_RD_REPEAT_DATA0_REG[12]', bloc: 'B5[4]'} + DIS_USB_OTG : {show: y, blk : 0, word: 1, pos: 13, len : 1, start : 45, type : bool, wr_dis : 2, rd_dis: null, alt : DIS_USB, dict : '', desc: Set this bit to disable USB function, rloc: 'EFUSE_RD_REPEAT_DATA0_REG[13]', bloc: 'B5[5]'} + DIS_TWAI : {show: y, blk : 0, word: 1, pos: 14, len : 1, start : 46, type : bool, wr_dis : 2, rd_dis: null, alt : DIS_CAN, dict : '', desc: Set this bit to disable CAN function, rloc: 'EFUSE_RD_REPEAT_DATA0_REG[14]', bloc: 'B5[6]'} + DIS_APP_CPU : {show: y, blk : 0, word: 1, pos: 15, len : 1, start : 47, type : bool, wr_dis : 2, rd_dis: null, alt : '', dict : '', desc: Disable app cpu, rloc: 'EFUSE_RD_REPEAT_DATA0_REG[15]', bloc: 'B5[7]'} + SOFT_DIS_JTAG : {show: y, blk : 0, word: 1, pos: 16, len : 3, start : 48, type : 'uint:3', wr_dis : 31, rd_dis: null, alt : '', dict : '', desc: Set these bits to disable JTAG in the soft way (odd number 1 means disable ). JTAG can be enabled in HMAC module, rloc: 'EFUSE_RD_REPEAT_DATA0_REG[18:16]', bloc: 'B6[2:0]'} + DIS_PAD_JTAG : {show: y, blk : 0, word: 1, pos: 19, len : 1, start : 51, type : bool, wr_dis : 2, rd_dis: null, alt : HARD_DIS_JTAG, dict : '', desc: Set this bit to disable JTAG in the hard way. JTAG is disabled permanently, rloc: 'EFUSE_RD_REPEAT_DATA0_REG[19]', bloc: 'B6[3]'} + DIS_DOWNLOAD_MANUAL_ENCRYPT : {show: y, blk : 0, word: 1, pos: 20, len : 1, start : 52, type : bool, wr_dis : 2, rd_dis: null, alt : '', dict : '', desc: Set this bit to disable flash encryption when in download boot modes, rloc: 'EFUSE_RD_REPEAT_DATA0_REG[20]', bloc: 'B6[4]'} + USB_DREFH : {show: n, blk : 0, word: 1, pos: 21, len : 2, start : 53, type : 'uint:2', wr_dis : 30, rd_dis: null, alt : '', dict : '', desc: Controls single-end input threshold vrefh; 1.76 V to 2 V with step of 80 mV; stored in eFuse, rloc: 'EFUSE_RD_REPEAT_DATA0_REG[22:21]', bloc: 'B6[6:5]'} + USB_DREFL : {show: n, blk : 0, word: 1, pos: 23, len : 2, start : 55, type : 'uint:2', wr_dis : 30, rd_dis: null, alt : '', dict : '', desc: Controls single-end input threshold vrefl; 0.8 V to 1.04 V with step of 80 mV; stored in eFuse, rloc: 'EFUSE_RD_REPEAT_DATA0_REG[24:23]', bloc: 'B6[7],B7[0]'} + USB_EXCHG_PINS : {show: y, blk : 0, word: 1, pos: 25, len : 1, start : 57, type : bool, wr_dis : 30, rd_dis: null, alt : '', dict : '', desc: Set this bit to exchange USB D+ and D- pins, rloc: 'EFUSE_RD_REPEAT_DATA0_REG[25]', bloc: 'B7[1]'} + USB_EXT_PHY_ENABLE : {show: y, blk : 0, word: 1, pos: 26, len : 1, start : 58, type : bool, wr_dis : 30, rd_dis: null, alt : EXT_PHY_ENABLE, dict : '', desc: Set this bit to enable external PHY, rloc: 'EFUSE_RD_REPEAT_DATA0_REG[26]', bloc: 'B7[2]'} + BTLC_GPIO_ENABLE : {show: n, blk : 0, word: 1, pos: 27, len : 2, start : 59, type : 'uint:2', wr_dis : 30, rd_dis: null, alt : '', dict : '', desc: Bluetooth GPIO signal output security level control, rloc: 'EFUSE_RD_REPEAT_DATA0_REG[28:27]', bloc: 'B7[4:3]'} + VDD_SPI_MODECURLIM : {show: n, blk : 0, word: 1, pos: 29, len : 1, start : 61, type : bool, wr_dis : 3, rd_dis: null, alt : '', dict : '', desc: SPI regulator switches current limit mode, rloc: 'EFUSE_RD_REPEAT_DATA0_REG[29]', bloc: 'B7[5]'} + VDD_SPI_DREFH : {show: n, blk : 0, word: 1, pos: 30, len : 2, start : 62, type : 'uint:2', wr_dis : 3, rd_dis: null, alt : '', dict : '', desc: SPI regulator high voltage reference, rloc: 'EFUSE_RD_REPEAT_DATA0_REG[31:30]', bloc: 'B7[7:6]'} + VDD_SPI_DREFM : {show: n, blk : 0, word: 2, pos : 0, len : 2, start : 64, type : 'uint:2', wr_dis : 3, rd_dis: null, alt : '', dict : '', desc: SPI regulator medium voltage reference, rloc: 'EFUSE_RD_REPEAT_DATA1_REG[1:0]', bloc: 'B8[1:0]'} + VDD_SPI_DREFL : {show: n, blk : 0, word: 2, pos : 2, len : 2, start : 66, type : 'uint:2', wr_dis : 3, rd_dis: null, alt : '', dict : '', desc: SPI regulator low voltage reference, rloc: 'EFUSE_RD_REPEAT_DATA1_REG[3:2]', bloc: 'B8[3:2]'} + VDD_SPI_XPD : {show: y, blk : 0, word: 2, pos : 4, len : 1, start : 68, type : bool, wr_dis : 3, rd_dis: null, alt : '', dict : '', desc: SPI regulator power up signal, rloc: 'EFUSE_RD_REPEAT_DATA1_REG[4]', bloc: 'B8[4]'} + VDD_SPI_TIEH : {show: y, blk : 0, word: 2, pos : 5, len : 1, start : 69, type : bool, wr_dis : 3, rd_dis: null, alt : '', dict: '{0: "VDD_SPI connects to 1.8 V LDO", 1: "VDD_SPI connects to VDD3P3_RTC_IO"}', desc: If VDD_SPI_FORCE is 1; determines VDD_SPI voltage, rloc: 'EFUSE_RD_REPEAT_DATA1_REG[5]', bloc: 'B8[5]'} + VDD_SPI_FORCE : {show: y, blk : 0, word: 2, pos : 6, len : 1, start : 70, type : bool, wr_dis : 3, rd_dis: null, alt : '', dict : '', desc: Set this bit and force to use the configuration of eFuse to configure VDD_SPI, rloc: 'EFUSE_RD_REPEAT_DATA1_REG[6]', bloc: 'B8[6]'} + VDD_SPI_EN_INIT : {show: n, blk : 0, word: 2, pos : 7, len : 1, start : 71, type : bool, wr_dis : 3, rd_dis: null, alt : '', dict : '', desc: 'Set SPI regulator to 0 to configure init[1:0]=0', rloc: 'EFUSE_RD_REPEAT_DATA1_REG[7]', bloc: 'B8[7]'} + VDD_SPI_ENCURLIM : {show: n, blk : 0, word: 2, pos : 8, len : 1, start : 72, type : bool, wr_dis : 3, rd_dis: null, alt : '', dict : '', desc: Set SPI regulator to 1 to enable output current limit, rloc: 'EFUSE_RD_REPEAT_DATA1_REG[8]', bloc: 'B9[0]'} + VDD_SPI_DCURLIM : {show: n, blk : 0, word: 2, pos : 9, len : 3, start : 73, type : 'uint:3', wr_dis : 3, rd_dis: null, alt : '', dict : '', desc: Tunes the current limit threshold of SPI regulator when tieh=0; about 800 mA/(8+d), rloc: 'EFUSE_RD_REPEAT_DATA1_REG[11:9]', bloc: 'B9[3:1]'} + VDD_SPI_INIT : {show: n, blk : 0, word: 2, pos: 12, len : 2, start : 76, type : 'uint:2', wr_dis : 3, rd_dis: null, alt : '', dict: '{0: "no resistance", 1: "6K", 2: "4K", 3: "2K"}', desc: Adds resistor from LDO output to ground, rloc: 'EFUSE_RD_REPEAT_DATA1_REG[13:12]', bloc: 'B9[5:4]'} + VDD_SPI_DCAP : {show: n, blk : 0, word: 2, pos: 14, len : 2, start : 78, type : 'uint:2', wr_dis : 3, rd_dis: null, alt : '', dict : '', desc: Prevents SPI regulator from overshoot, rloc: 'EFUSE_RD_REPEAT_DATA1_REG[15:14]', bloc: 'B9[7:6]'} + WDT_DELAY_SEL : {show: y, blk : 0, word: 2, pos: 16, len : 2, start : 80, type : 'uint:2', wr_dis : 3, rd_dis: null, alt : '', dict: '{0: "40000", 1: "80000", 2: "160000", 3: "320000"}', desc: RTC watchdog timeout threshold; in unit of slow clock cycle, rloc: 'EFUSE_RD_REPEAT_DATA1_REG[17:16]', bloc: 'B10[1:0]'} + SPI_BOOT_CRYPT_CNT : {show: y, blk : 0, word: 2, pos: 18, len : 3, start : 82, type : 'uint:3', wr_dis : 4, rd_dis: null, alt : '', dict: '{0: "Disable", 1: "Enable", 3: "Disable", 7: "Enable"}', desc: Enables flash encryption when 1 or 3 bits are set and disabled otherwise, rloc: 'EFUSE_RD_REPEAT_DATA1_REG[20:18]', bloc: 'B10[4:2]'} + SECURE_BOOT_KEY_REVOKE0 : {show: y, blk : 0, word: 2, pos: 21, len : 1, start : 85, type : bool, wr_dis : 5, rd_dis: null, alt : '', dict : '', desc: Revoke 1st secure boot key, rloc: 'EFUSE_RD_REPEAT_DATA1_REG[21]', bloc: 'B10[5]'} + SECURE_BOOT_KEY_REVOKE1 : {show: y, blk : 0, word: 2, pos: 22, len : 1, start : 86, type : bool, wr_dis : 6, rd_dis: null, alt : '', dict : '', desc: Revoke 2nd secure boot key, rloc: 'EFUSE_RD_REPEAT_DATA1_REG[22]', bloc: 'B10[6]'} + SECURE_BOOT_KEY_REVOKE2 : {show: y, blk : 0, word: 2, pos: 23, len : 1, start : 87, type : bool, wr_dis : 7, rd_dis: null, alt : '', dict : '', desc: Revoke 3rd secure boot key, rloc: 'EFUSE_RD_REPEAT_DATA1_REG[23]', bloc: 'B10[7]'} + KEY_PURPOSE_0 : {show: y, blk : 0, word: 2, pos: 24, len : 4, start : 88, type : 'uint:4', wr_dis : 8, rd_dis: null, alt : KEY0_PURPOSE, dict : '', desc: Purpose of Key0, rloc: 'EFUSE_RD_REPEAT_DATA1_REG[27:24]', bloc: 'B11[3:0]'} + KEY_PURPOSE_1 : {show: y, blk : 0, word: 2, pos: 28, len : 4, start : 92, type : 'uint:4', wr_dis : 9, rd_dis: null, alt : KEY1_PURPOSE, dict : '', desc: Purpose of Key1, rloc: 'EFUSE_RD_REPEAT_DATA1_REG[31:28]', bloc: 'B11[7:4]'} + KEY_PURPOSE_2 : {show: y, blk : 0, word: 3, pos : 0, len : 4, start : 96, type : 'uint:4', wr_dis : 10, rd_dis: null, alt : KEY2_PURPOSE, dict : '', desc: Purpose of Key2, rloc: 'EFUSE_RD_REPEAT_DATA2_REG[3:0]', bloc: 'B12[3:0]'} + KEY_PURPOSE_3 : {show: y, blk : 0, word: 3, pos : 4, len : 4, start: 100, type : 'uint:4', wr_dis : 11, rd_dis: null, alt : KEY3_PURPOSE, dict : '', desc: Purpose of Key3, rloc: 'EFUSE_RD_REPEAT_DATA2_REG[7:4]', bloc: 'B12[7:4]'} + KEY_PURPOSE_4 : {show: y, blk : 0, word: 3, pos : 8, len : 4, start: 104, type : 'uint:4', wr_dis : 12, rd_dis: null, alt : KEY4_PURPOSE, dict : '', desc: Purpose of Key4, rloc: 'EFUSE_RD_REPEAT_DATA2_REG[11:8]', bloc: 'B13[3:0]'} + KEY_PURPOSE_5 : {show: y, blk : 0, word: 3, pos: 12, len : 4, start: 108, type : 'uint:4', wr_dis : 13, rd_dis: null, alt : KEY5_PURPOSE, dict : '', desc: Purpose of Key5, rloc: 'EFUSE_RD_REPEAT_DATA2_REG[15:12]', bloc: 'B13[7:4]'} + RPT4_RESERVED0 : {show: n, blk : 0, word: 3, pos: 16, len : 4, start: 112, type : 'uint:4', wr_dis: null, rd_dis: null, alt : '', dict : '', desc: Reserved (used for four backups method), rloc: 'EFUSE_RD_REPEAT_DATA2_REG[19:16]', bloc: 'B14[3:0]'} + SECURE_BOOT_EN : {show: y, blk : 0, word: 3, pos: 20, len : 1, start: 116, type : bool, wr_dis : 15, rd_dis: null, alt : '', dict : '', desc: Set this bit to enable secure boot, rloc: 'EFUSE_RD_REPEAT_DATA2_REG[20]', bloc: 'B14[4]'} + SECURE_BOOT_AGGRESSIVE_REVOKE : {show: y, blk : 0, word: 3, pos: 21, len : 1, start: 117, type : bool, wr_dis : 16, rd_dis: null, alt : '', dict : '', desc: Set this bit to enable revoking aggressive secure boot, rloc: 'EFUSE_RD_REPEAT_DATA2_REG[21]', bloc: 'B14[5]'} + DIS_USB_JTAG : {show: y, blk : 0, word: 3, pos: 22, len : 1, start: 118, type : bool, wr_dis : 2, rd_dis: null, alt : '', dict : '', desc: Set this bit to disable function of usb switch to jtag in module of usb device, rloc: 'EFUSE_RD_REPEAT_DATA2_REG[22]', bloc: 'B14[6]'} + DIS_USB_SERIAL_JTAG : {show: y, blk : 0, word: 3, pos: 23, len : 1, start: 119, type : bool, wr_dis : 2, rd_dis: null, alt : DIS_USB_DEVICE, dict : '', desc: Set this bit to disable usb device, rloc: 'EFUSE_RD_REPEAT_DATA2_REG[23]', bloc: 'B14[7]'} + STRAP_JTAG_SEL : {show: y, blk : 0, word: 3, pos: 24, len : 1, start: 120, type : bool, wr_dis : 2, rd_dis: null, alt : '', dict : '', desc: Set this bit to enable selection between usb_to_jtag and pad_to_jtag through strapping gpio10 when both reg_dis_usb_jtag and reg_dis_pad_jtag are equal to 0, rloc: 'EFUSE_RD_REPEAT_DATA2_REG[24]', bloc: 'B15[0]'} + USB_PHY_SEL : {show: y, blk : 0, word: 3, pos: 25, len : 1, start: 121, type : bool, wr_dis : 2, rd_dis: null, alt : '', dict: '{0: "internal PHY is assigned to USB Device while external PHY is assigned to USB OTG", 1: "internal PHY is assigned to USB OTG while external PHY is assigned to USB Device"}', desc: This bit is used to switch internal PHY and external PHY for USB OTG and USB Device, rloc: 'EFUSE_RD_REPEAT_DATA2_REG[25]', bloc: 'B15[1]'} + POWER_GLITCH_DSENSE : {show: n, blk : 0, word: 3, pos: 26, len : 2, start: 122, type : 'uint:2', wr_dis : 17, rd_dis: null, alt : '', dict : '', desc: Sample delay configuration of power glitch, rloc: 'EFUSE_RD_REPEAT_DATA2_REG[27:26]', bloc: 'B15[3:2]'} + FLASH_TPUW : {show: y, blk : 0, word: 3, pos: 28, len : 4, start: 124, type : 'uint:4', wr_dis : 18, rd_dis: null, alt : '', dict : '', desc: Configures flash waiting time after power-up; in unit of ms. If the value is less than 15; the waiting time is the configurable value. Otherwise; the waiting time is twice the configurable value, rloc: 'EFUSE_RD_REPEAT_DATA2_REG[31:28]', bloc: 'B15[7:4]'} + DIS_DOWNLOAD_MODE : {show: y, blk : 0, word: 4, pos : 0, len : 1, start: 128, type : bool, wr_dis : 18, rd_dis: null, alt : '', dict : '', desc: 'Set this bit to disable download mode (boot_mode[3:0] = 0; 1; 2; 3; 6; 7)', rloc: 'EFUSE_RD_REPEAT_DATA3_REG[0]', bloc: 'B16[0]'} + DIS_DIRECT_BOOT : {show: y, blk : 0, word: 4, pos : 1, len : 1, start: 129, type : bool, wr_dis : 18, rd_dis: null, alt : DIS_LEGACY_SPI_BOOT, dict : '', desc: Disable direct boot mode, rloc: 'EFUSE_RD_REPEAT_DATA3_REG[1]', bloc: 'B16[1]'} + DIS_USB_SERIAL_JTAG_ROM_PRINT : {show: y, blk : 0, word: 4, pos : 2, len : 1, start: 130, type : bool, wr_dis : 18, rd_dis: null, alt : UART_PRINT_CHANNEL, dict: '{0: "Enable", 1: "Disable"}', desc: USB printing, rloc: 'EFUSE_RD_REPEAT_DATA3_REG[2]', bloc: 'B16[2]'} + FLASH_ECC_MODE : {show: y, blk : 0, word: 4, pos : 3, len : 1, start: 131, type : bool, wr_dis : 18, rd_dis: null, alt : '', dict: '{0: "16to18 byte", 1: "16to17 byte"}', desc: Flash ECC mode in ROM, rloc: 'EFUSE_RD_REPEAT_DATA3_REG[3]', bloc: 'B16[3]'} + DIS_USB_SERIAL_JTAG_DOWNLOAD_MODE: {show: y, blk : 0, word: 4, pos : 4, len : 1, start: 132, type : bool, wr_dis : 18, rd_dis: null, alt : DIS_USB_DOWNLOAD_MODE, dict : '', desc: Set this bit to disable UART download mode through USB, rloc: 'EFUSE_RD_REPEAT_DATA3_REG[4]', bloc: 'B16[4]'} + ENABLE_SECURITY_DOWNLOAD : {show: y, blk : 0, word: 4, pos : 5, len : 1, start: 133, type : bool, wr_dis : 18, rd_dis: null, alt : '', dict : '', desc: Set this bit to enable secure UART download mode, rloc: 'EFUSE_RD_REPEAT_DATA3_REG[5]', bloc: 'B16[5]'} + UART_PRINT_CONTROL : {show: y, blk : 0, word: 4, pos : 6, len : 2, start: 134, type : 'uint:2', wr_dis : 18, rd_dis: null, alt : '', dict: '{0: "Enable", 1: "Enable when GPIO46 is low at reset", 2: "Enable when GPIO46 is high at reset", 3: "Disable"}', desc: Set the default UART boot message output mode, rloc: 'EFUSE_RD_REPEAT_DATA3_REG[7:6]', bloc: 'B16[7:6]'} + PIN_POWER_SELECTION : {show: y, blk : 0, word: 4, pos : 8, len : 1, start: 136, type : bool, wr_dis : 18, rd_dis: null, alt : '', dict: '{0: "VDD3P3_CPU", 1: "VDD_SPI"}', desc: Set default power supply for GPIO33-GPIO37; set when SPI flash is initialized, rloc: 'EFUSE_RD_REPEAT_DATA3_REG[8]', bloc: 'B17[0]'} + FLASH_TYPE : {show: y, blk : 0, word: 4, pos : 9, len : 1, start: 137, type : bool, wr_dis : 18, rd_dis: null, alt : '', dict: '{0: "4 data lines", 1: "8 data lines"}', desc: SPI flash type, rloc: 'EFUSE_RD_REPEAT_DATA3_REG[9]', bloc: 'B17[1]'} + FLASH_PAGE_SIZE : {show: y, blk : 0, word: 4, pos: 10, len : 2, start: 138, type : 'uint:2', wr_dis : 18, rd_dis: null, alt : '', dict : '', desc: Set Flash page size, rloc: 'EFUSE_RD_REPEAT_DATA3_REG[11:10]', bloc: 'B17[3:2]'} + FLASH_ECC_EN : {show: y, blk : 0, word: 4, pos: 12, len : 1, start: 140, type : bool, wr_dis : 18, rd_dis: null, alt : '', dict : '', desc: Set 1 to enable ECC for flash boot, rloc: 'EFUSE_RD_REPEAT_DATA3_REG[12]', bloc: 'B17[4]'} + FORCE_SEND_RESUME : {show: y, blk : 0, word: 4, pos: 13, len : 1, start: 141, type : bool, wr_dis : 18, rd_dis: null, alt : '', dict : '', desc: Set this bit to force ROM code to send a resume command during SPI boot, rloc: 'EFUSE_RD_REPEAT_DATA3_REG[13]', bloc: 'B17[5]'} + SECURE_VERSION : {show: y, blk : 0, word: 4, pos: 14, len : 16, start: 142, type : 'uint:16', wr_dis : 18, rd_dis: null, alt : '', dict : '', desc: Secure version (used by ESP-IDF anti-rollback feature), rloc: 'EFUSE_RD_REPEAT_DATA3_REG[29:14]', bloc: 'B17[7:6],B18,B19[5:0]'} + POWERGLITCH_EN : {show: n, blk : 0, word: 4, pos: 30, len : 1, start: 158, type : bool, wr_dis : 17, rd_dis: null, alt : '', dict : '', desc: Set this bit to enable power glitch function, rloc: 'EFUSE_RD_REPEAT_DATA3_REG[30]', bloc: 'B19[6]'} + DIS_USB_OTG_DOWNLOAD_MODE : {show: y, blk : 0, word: 4, pos: 31, len : 1, start: 159, type : bool, wr_dis : 19, rd_dis: null, alt : '', dict : '', desc: Set this bit to disable download through USB-OTG, rloc: 'EFUSE_RD_REPEAT_DATA3_REG[31]', bloc: 'B19[7]'} + DISABLE_WAFER_VERSION_MAJOR : {show: y, blk : 0, word: 5, pos : 0, len : 1, start: 160, type : bool, wr_dis : 19, rd_dis: null, alt : '', dict : '', desc: Disables check of wafer version major, rloc: 'EFUSE_RD_REPEAT_DATA4_REG[0]', bloc: 'B20[0]'} + DISABLE_BLK_VERSION_MAJOR : {show: y, blk : 0, word: 5, pos : 1, len : 1, start: 161, type : bool, wr_dis : 19, rd_dis: null, alt : '', dict : '', desc: Disables check of blk version major, rloc: 'EFUSE_RD_REPEAT_DATA4_REG[1]', bloc: 'B20[1]'} + RESERVED_0_162 : {show: n, blk : 0, word: 5, pos : 2, len : 22, start: 162, type : 'uint:22', wr_dis: null, rd_dis: null, alt : '', dict : '', desc: reserved, rloc: 'EFUSE_RD_REPEAT_DATA4_REG[23:2]', bloc: 'B20[7:2],B21,B22'} + MAC : {show: y, blk : 1, word: 0, pos : 0, len : 48, start : 0, type : 'bytes:6', wr_dis : 20, rd_dis: null, alt : MAC_FACTORY, dict : '', desc: MAC address, rloc: EFUSE_RD_MAC_SPI_SYS_0_REG, bloc: 'B0,B1,B2,B3,B4,B5'} + SPI_PAD_CONFIG_CLK : {show: y, blk : 1, word: 1, pos: 16, len : 6, start : 48, type : 'uint:6', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: SPI_PAD_configure CLK, rloc: 'EFUSE_RD_MAC_SPI_SYS_1_REG[21:16]', bloc: 'B6[5:0]'} + SPI_PAD_CONFIG_Q : {show: y, blk : 1, word: 1, pos: 22, len : 6, start : 54, type : 'uint:6', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: SPI_PAD_configure Q(D1), rloc: 'EFUSE_RD_MAC_SPI_SYS_1_REG[27:22]', bloc: 'B6[7:6],B7[3:0]'} + SPI_PAD_CONFIG_D : {show: y, blk : 1, word: 1, pos: 28, len : 6, start : 60, type : 'uint:6', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: SPI_PAD_configure D(D0), rloc: 'EFUSE_RD_MAC_SPI_SYS_1_REG[31:28]', bloc: 'B7[7:4],B8[1:0]'} + SPI_PAD_CONFIG_CS : {show: y, blk : 1, word: 2, pos : 2, len : 6, start : 66, type : 'uint:6', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: SPI_PAD_configure CS, rloc: 'EFUSE_RD_MAC_SPI_SYS_2_REG[7:2]', bloc: 'B8[7:2]'} + SPI_PAD_CONFIG_HD : {show: y, blk : 1, word: 2, pos : 8, len : 6, start : 72, type : 'uint:6', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: SPI_PAD_configure HD(D3), rloc: 'EFUSE_RD_MAC_SPI_SYS_2_REG[13:8]', bloc: 'B9[5:0]'} + SPI_PAD_CONFIG_WP : {show: y, blk : 1, word: 2, pos: 14, len : 6, start : 78, type : 'uint:6', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: SPI_PAD_configure WP(D2), rloc: 'EFUSE_RD_MAC_SPI_SYS_2_REG[19:14]', bloc: 'B9[7:6],B10[3:0]'} + SPI_PAD_CONFIG_DQS : {show: y, blk : 1, word: 2, pos: 20, len : 6, start : 84, type : 'uint:6', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: SPI_PAD_configure DQS, rloc: 'EFUSE_RD_MAC_SPI_SYS_2_REG[25:20]', bloc: 'B10[7:4],B11[1:0]'} + SPI_PAD_CONFIG_D4 : {show: y, blk : 1, word: 2, pos: 26, len : 6, start : 90, type : 'uint:6', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: SPI_PAD_configure D4, rloc: 'EFUSE_RD_MAC_SPI_SYS_2_REG[31:26]', bloc: 'B11[7:2]'} + SPI_PAD_CONFIG_D5 : {show: y, blk : 1, word: 3, pos : 0, len : 6, start : 96, type : 'uint:6', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: SPI_PAD_configure D5, rloc: 'EFUSE_RD_MAC_SPI_SYS_3_REG[5:0]', bloc: 'B12[5:0]'} + SPI_PAD_CONFIG_D6 : {show: y, blk : 1, word: 3, pos : 6, len : 6, start: 102, type : 'uint:6', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: SPI_PAD_configure D6, rloc: 'EFUSE_RD_MAC_SPI_SYS_3_REG[11:6]', bloc: 'B12[7:6],B13[3:0]'} + SPI_PAD_CONFIG_D7 : {show: y, blk : 1, word: 3, pos: 12, len : 6, start: 108, type : 'uint:6', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: SPI_PAD_configure D7, rloc: 'EFUSE_RD_MAC_SPI_SYS_3_REG[17:12]', bloc: 'B13[7:4],B14[1:0]'} + WAFER_VERSION_MINOR_LO : {show: y, blk : 1, word: 3, pos: 18, len : 3, start: 114, type : 'uint:3', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: WAFER_VERSION_MINOR least significant bits, rloc: 'EFUSE_RD_MAC_SPI_SYS_3_REG[20:18]', bloc: 'B14[4:2]'} + PKG_VERSION : {show: y, blk : 1, word: 3, pos: 21, len : 3, start: 117, type : 'uint:3', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: Package version, rloc: 'EFUSE_RD_MAC_SPI_SYS_3_REG[23:21]', bloc: 'B14[7:5]'} + BLK_VERSION_MINOR : {show: y, blk : 1, word: 3, pos: 24, len : 3, start: 120, type : 'uint:3', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: BLK_VERSION_MINOR, rloc: 'EFUSE_RD_MAC_SPI_SYS_3_REG[26:24]', bloc: 'B15[2:0]'} + FLASH_CAP : {show: y, blk : 1, word: 3, pos: 27, len : 3, start: 123, type : 'uint:3', wr_dis : 20, rd_dis: null, alt : '', dict: '{0: "None", 1: "8M", 2: "4M"}', desc: Flash capacity, rloc: 'EFUSE_RD_MAC_SPI_SYS_3_REG[29:27]', bloc: 'B15[5:3]'} + FLASH_TEMP : {show: y, blk : 1, word: 3, pos: 30, len : 2, start: 126, type : 'uint:2', wr_dis : 20, rd_dis: null, alt : '', dict: '{0: "None", 1: "105C", 2: "85C"}', desc: Flash temperature, rloc: 'EFUSE_RD_MAC_SPI_SYS_3_REG[31:30]', bloc: 'B15[7:6]'} + FLASH_VENDOR : {show: y, blk : 1, word: 4, pos : 0, len : 3, start: 128, type : 'uint:3', wr_dis : 20, rd_dis: null, alt : '', dict: '{0: "None", 1: "XMC", 2: "GD", 3: "FM", 4: "TT", 5: "BY"}', desc: Flash vendor, rloc: 'EFUSE_RD_MAC_SPI_SYS_4_REG[2:0]', bloc: 'B16[2:0]'} + PSRAM_CAP : {show: y, blk : 1, word: 4, pos : 3, len : 2, start: 131, type : 'uint:2', wr_dis : 20, rd_dis: null, alt : '', dict: '{0: "None", 1: "8M", 2: "2M"}', desc: PSRAM capacity, rloc: 'EFUSE_RD_MAC_SPI_SYS_4_REG[4:3]', bloc: 'B16[4:3]'} + PSRAM_TEMP : {show: y, blk : 1, word: 4, pos : 5, len : 2, start: 133, type : 'uint:2', wr_dis : 20, rd_dis: null, alt : '', dict: '{0: "None", 1: "105C", 2: "85C"}', desc: PSRAM temperature, rloc: 'EFUSE_RD_MAC_SPI_SYS_4_REG[6:5]', bloc: 'B16[6:5]'} + PSRAM_VENDOR : {show: y, blk : 1, word: 4, pos : 7, len : 2, start: 135, type : 'uint:2', wr_dis : 20, rd_dis: null, alt : '', dict: '{0: "None", 1: "AP_3v3", 2: "AP_1v8"}', desc: PSRAM vendor, rloc: 'EFUSE_RD_MAC_SPI_SYS_4_REG[8:7]', bloc: 'B16[7],B17[0]'} + RESERVED_1_137 : {show: n, blk : 1, word: 4, pos : 9, len : 4, start: 137, type : 'uint:4', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: reserved, rloc: 'EFUSE_RD_MAC_SPI_SYS_4_REG[12:9]', bloc: 'B17[4:1]'} + K_RTC_LDO : {show: y, blk : 1, word: 4, pos: 13, len : 7, start: 141, type : 'uint:7', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: BLOCK1 K_RTC_LDO, rloc: 'EFUSE_RD_MAC_SPI_SYS_4_REG[19:13]', bloc: 'B17[7:5],B18[3:0]'} + K_DIG_LDO : {show: y, blk : 1, word: 4, pos: 20, len : 7, start: 148, type : 'uint:7', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: BLOCK1 K_DIG_LDO, rloc: 'EFUSE_RD_MAC_SPI_SYS_4_REG[26:20]', bloc: 'B18[7:4],B19[2:0]'} + V_RTC_DBIAS20 : {show: y, blk : 1, word: 4, pos: 27, len : 8, start: 155, type : 'uint:8', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: BLOCK1 voltage of rtc dbias20, rloc: 'EFUSE_RD_MAC_SPI_SYS_4_REG[31:27]', bloc: 'B19[7:3],B20[2:0]'} + V_DIG_DBIAS20 : {show: y, blk : 1, word: 5, pos : 3, len : 8, start: 163, type : 'uint:8', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: BLOCK1 voltage of digital dbias20, rloc: 'EFUSE_RD_MAC_SPI_SYS_5_REG[10:3]', bloc: 'B20[7:3],B21[2:0]'} + DIG_DBIAS_HVT : {show: y, blk : 1, word: 5, pos: 11, len : 5, start: 171, type : 'uint:5', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: BLOCK1 digital dbias when hvt, rloc: 'EFUSE_RD_MAC_SPI_SYS_5_REG[15:11]', bloc: 'B21[7:3]'} + RESERVED_1_176 : {show: n, blk : 1, word: 5, pos: 16, len : 7, start: 176, type : 'uint:7', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: reserved, rloc: 'EFUSE_RD_MAC_SPI_SYS_5_REG[22:16]', bloc: 'B22[6:0]'} + WAFER_VERSION_MINOR_HI : {show: y, blk : 1, word: 5, pos: 23, len : 1, start: 183, type : bool, wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: WAFER_VERSION_MINOR most significant bit, rloc: 'EFUSE_RD_MAC_SPI_SYS_5_REG[23]', bloc: 'B22[7]'} + WAFER_VERSION_MAJOR : {show: y, blk : 1, word: 5, pos: 24, len : 2, start: 184, type : 'uint:2', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: WAFER_VERSION_MAJOR, rloc: 'EFUSE_RD_MAC_SPI_SYS_5_REG[25:24]', bloc: 'B23[1:0]'} + ADC2_CAL_VOL_ATTEN3 : {show: y, blk : 1, word: 5, pos: 26, len : 6, start: 186, type : 'uint:6', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: ADC2 calibration voltage at atten3, rloc: 'EFUSE_RD_MAC_SPI_SYS_5_REG[31:26]', bloc: 'B23[7:2]'} + OPTIONAL_UNIQUE_ID : {show: y, blk : 2, word: 0, pos : 0, len: 128, start : 0, type: 'bytes:16', wr_dis : 21, rd_dis: null, alt : '', dict : '', desc: Optional unique 128-bit ID, rloc: EFUSE_RD_SYS_PART1_DATA0_REG, bloc: 'B0,B1,B2,B3,B4,B5,B6,B7,B8,B9,B10,B11,B12,B13,B14,B15'} + BLK_VERSION_MAJOR : {show: y, blk : 2, word: 4, pos : 0, len : 2, start: 128, type : 'uint:2', wr_dis : 21, rd_dis: null, alt : '', dict: '{0: "No calib", 1: "ADC calib V1"}', desc: BLK_VERSION_MAJOR of BLOCK2, rloc: 'EFUSE_RD_SYS_PART1_DATA4_REG[1:0]', bloc: 'B16[1:0]'} + RESERVED_2_130 : {show: n, blk : 2, word: 4, pos : 2, len : 2, start: 130, type : 'uint:2', wr_dis : 21, rd_dis: null, alt : '', dict : '', desc: reserved, rloc: 'EFUSE_RD_SYS_PART1_DATA4_REG[3:2]', bloc: 'B16[3:2]'} + TEMP_CALIB : {show: y, blk : 2, word: 4, pos : 4, len : 9, start: 132, type : 'uint:9', wr_dis : 21, rd_dis: null, alt : '', dict : '', desc: Temperature calibration data, rloc: 'EFUSE_RD_SYS_PART1_DATA4_REG[12:4]', bloc: 'B16[7:4],B17[4:0]'} + OCODE : {show: y, blk : 2, word: 4, pos: 13, len : 8, start: 141, type : 'uint:8', wr_dis : 21, rd_dis: null, alt : '', dict : '', desc: ADC OCode, rloc: 'EFUSE_RD_SYS_PART1_DATA4_REG[20:13]', bloc: 'B17[7:5],B18[4:0]'} + ADC1_INIT_CODE_ATTEN0 : {show: y, blk : 2, word: 4, pos: 21, len : 8, start: 149, type : 'uint:8', wr_dis : 21, rd_dis: null, alt : '', dict : '', desc: ADC1 init code at atten0, rloc: 'EFUSE_RD_SYS_PART1_DATA4_REG[28:21]', bloc: 'B18[7:5],B19[4:0]'} + ADC1_INIT_CODE_ATTEN1 : {show: y, blk : 2, word: 4, pos: 29, len : 6, start: 157, type : 'uint:6', wr_dis : 21, rd_dis: null, alt : '', dict : '', desc: ADC1 init code at atten1, rloc: 'EFUSE_RD_SYS_PART1_DATA4_REG[31:29]', bloc: 'B19[7:5],B20[2:0]'} + ADC1_INIT_CODE_ATTEN2 : {show: y, blk : 2, word: 5, pos : 3, len : 6, start: 163, type : 'uint:6', wr_dis : 21, rd_dis: null, alt : '', dict : '', desc: ADC1 init code at atten2, rloc: 'EFUSE_RD_SYS_PART1_DATA5_REG[8:3]', bloc: 'B20[7:3],B21[0]'} + ADC1_INIT_CODE_ATTEN3 : {show: y, blk : 2, word: 5, pos : 9, len : 6, start: 169, type : 'uint:6', wr_dis : 21, rd_dis: null, alt : '', dict : '', desc: ADC1 init code at atten3, rloc: 'EFUSE_RD_SYS_PART1_DATA5_REG[14:9]', bloc: 'B21[6:1]'} + ADC2_INIT_CODE_ATTEN0 : {show: y, blk : 2, word: 5, pos: 15, len : 8, start: 175, type : 'uint:8', wr_dis : 21, rd_dis: null, alt : '', dict : '', desc: ADC2 init code at atten0, rloc: 'EFUSE_RD_SYS_PART1_DATA5_REG[22:15]', bloc: 'B21[7],B22[6:0]'} + ADC2_INIT_CODE_ATTEN1 : {show: y, blk : 2, word: 5, pos: 23, len : 6, start: 183, type : 'uint:6', wr_dis : 21, rd_dis: null, alt : '', dict : '', desc: ADC2 init code at atten1, rloc: 'EFUSE_RD_SYS_PART1_DATA5_REG[28:23]', bloc: 'B22[7],B23[4:0]'} + ADC2_INIT_CODE_ATTEN2 : {show: y, blk : 2, word: 5, pos: 29, len : 6, start: 189, type : 'uint:6', wr_dis : 21, rd_dis: null, alt : '', dict : '', desc: ADC2 init code at atten2, rloc: 'EFUSE_RD_SYS_PART1_DATA5_REG[31:29]', bloc: 'B23[7:5],B24[2:0]'} + ADC2_INIT_CODE_ATTEN3 : {show: y, blk : 2, word: 6, pos : 3, len : 6, start: 195, type : 'uint:6', wr_dis : 21, rd_dis: null, alt : '', dict : '', desc: ADC2 init code at atten3, rloc: 'EFUSE_RD_SYS_PART1_DATA6_REG[8:3]', bloc: 'B24[7:3],B25[0]'} + ADC1_CAL_VOL_ATTEN0 : {show: y, blk : 2, word: 6, pos : 9, len : 8, start: 201, type : 'uint:8', wr_dis : 21, rd_dis: null, alt : '', dict : '', desc: ADC1 calibration voltage at atten0, rloc: 'EFUSE_RD_SYS_PART1_DATA6_REG[16:9]', bloc: 'B25[7:1],B26[0]'} + ADC1_CAL_VOL_ATTEN1 : {show: y, blk : 2, word: 6, pos: 17, len : 8, start: 209, type : 'uint:8', wr_dis : 21, rd_dis: null, alt : '', dict : '', desc: ADC1 calibration voltage at atten1, rloc: 'EFUSE_RD_SYS_PART1_DATA6_REG[24:17]', bloc: 'B26[7:1],B27[0]'} + ADC1_CAL_VOL_ATTEN2 : {show: y, blk : 2, word: 6, pos: 25, len : 8, start: 217, type : 'uint:8', wr_dis : 21, rd_dis: null, alt : '', dict : '', desc: ADC1 calibration voltage at atten2, rloc: 'EFUSE_RD_SYS_PART1_DATA6_REG[31:25]', bloc: 'B27[7:1],B28[0]'} + ADC1_CAL_VOL_ATTEN3 : {show: y, blk : 2, word: 7, pos : 1, len : 8, start: 225, type : 'uint:8', wr_dis : 21, rd_dis: null, alt : '', dict : '', desc: ADC1 calibration voltage at atten3, rloc: 'EFUSE_RD_SYS_PART1_DATA7_REG[8:1]', bloc: 'B28[7:1],B29[0]'} + ADC2_CAL_VOL_ATTEN0 : {show: y, blk : 2, word: 7, pos : 9, len : 8, start: 233, type : 'uint:8', wr_dis : 21, rd_dis: null, alt : '', dict : '', desc: ADC2 calibration voltage at atten0, rloc: 'EFUSE_RD_SYS_PART1_DATA7_REG[16:9]', bloc: 'B29[7:1],B30[0]'} + ADC2_CAL_VOL_ATTEN1 : {show: y, blk : 2, word: 7, pos: 17, len : 7, start: 241, type : 'uint:7', wr_dis : 21, rd_dis: null, alt : '', dict : '', desc: ADC2 calibration voltage at atten1, rloc: 'EFUSE_RD_SYS_PART1_DATA7_REG[23:17]', bloc: 'B30[7:1]'} + ADC2_CAL_VOL_ATTEN2 : {show: y, blk : 2, word: 7, pos: 24, len : 7, start: 248, type : 'uint:7', wr_dis : 21, rd_dis: null, alt : '', dict : '', desc: ADC2 calibration voltage at atten2, rloc: 'EFUSE_RD_SYS_PART1_DATA7_REG[30:24]', bloc: 'B31[6:0]'} + RESERVED_2_255 : {show: n, blk : 2, word: 7, pos: 31, len : 1, start: 255, type : bool, wr_dis : 21, rd_dis: null, alt : '', dict : '', desc: reserved, rloc: 'EFUSE_RD_SYS_PART1_DATA7_REG[31]', bloc: 'B31[7]'} + BLOCK_USR_DATA : {show: y, blk : 3, word: 0, pos : 0, len: 192, start : 0, type: 'bytes:24', wr_dis : 22, rd_dis: null, alt : USER_DATA, dict : '', desc: User data, rloc: EFUSE_RD_USR_DATA0_REG, bloc: 'B0,B1,B2,B3,B4,B5,B6,B7,B8,B9,B10,B11,B12,B13,B14,B15,B16,B17,B18,B19,B20,B21,B22,B23'} + RESERVED_3_192 : {show: n, blk : 3, word: 6, pos : 0, len : 8, start: 192, type : 'uint:8', wr_dis : 22, rd_dis: null, alt : '', dict : '', desc: reserved, rloc: 'EFUSE_RD_USR_DATA6_REG[7:0]', bloc: B24} + CUSTOM_MAC : {show: y, blk : 3, word: 6, pos : 8, len : 48, start: 200, type : 'bytes:6', wr_dis : 22, rd_dis: null, alt: MAC_CUSTOM USER_DATA_MAC_CUSTOM, dict : '', desc: Custom MAC, rloc: 'EFUSE_RD_USR_DATA6_REG[31:8]', bloc: 'B25,B26,B27,B28,B29,B30'} + RESERVED_3_248 : {show: n, blk : 3, word: 7, pos: 24, len : 8, start: 248, type : 'uint:8', wr_dis : 22, rd_dis: null, alt : '', dict : '', desc: reserved, rloc: 'EFUSE_RD_USR_DATA7_REG[31:24]', bloc: B31} + BLOCK_KEY0 : {show: y, blk : 4, word: 0, pos : 0, len: 256, start : 0, type: 'bytes:32', wr_dis : 23, rd_dis : 0, alt : KEY0, dict : '', desc: Key0 or user data, rloc: EFUSE_RD_KEY0_DATA0_REG, bloc: 'B0,B1,B2,B3,B4,B5,B6,B7,B8,B9,B10,B11,B12,B13,B14,B15,B16,B17,B18,B19,B20,B21,B22,B23,B24,B25,B26,B27,B28,B29,B30,B31'} + BLOCK_KEY1 : {show: y, blk : 5, word: 0, pos : 0, len: 256, start : 0, type: 'bytes:32', wr_dis : 24, rd_dis : 1, alt : KEY1, dict : '', desc: Key1 or user data, rloc: EFUSE_RD_KEY1_DATA0_REG, bloc: 'B0,B1,B2,B3,B4,B5,B6,B7,B8,B9,B10,B11,B12,B13,B14,B15,B16,B17,B18,B19,B20,B21,B22,B23,B24,B25,B26,B27,B28,B29,B30,B31'} + BLOCK_KEY2 : {show: y, blk : 6, word: 0, pos : 0, len: 256, start : 0, type: 'bytes:32', wr_dis : 25, rd_dis : 2, alt : KEY2, dict : '', desc: Key2 or user data, rloc: EFUSE_RD_KEY2_DATA0_REG, bloc: 'B0,B1,B2,B3,B4,B5,B6,B7,B8,B9,B10,B11,B12,B13,B14,B15,B16,B17,B18,B19,B20,B21,B22,B23,B24,B25,B26,B27,B28,B29,B30,B31'} + BLOCK_KEY3 : {show: y, blk : 7, word: 0, pos : 0, len: 256, start : 0, type: 'bytes:32', wr_dis : 26, rd_dis : 3, alt : KEY3, dict : '', desc: Key3 or user data, rloc: EFUSE_RD_KEY3_DATA0_REG, bloc: 'B0,B1,B2,B3,B4,B5,B6,B7,B8,B9,B10,B11,B12,B13,B14,B15,B16,B17,B18,B19,B20,B21,B22,B23,B24,B25,B26,B27,B28,B29,B30,B31'} + BLOCK_KEY4 : {show: y, blk : 8, word: 0, pos : 0, len: 256, start : 0, type: 'bytes:32', wr_dis : 27, rd_dis : 4, alt : KEY4, dict : '', desc: Key4 or user data, rloc: EFUSE_RD_KEY4_DATA0_REG, bloc: 'B0,B1,B2,B3,B4,B5,B6,B7,B8,B9,B10,B11,B12,B13,B14,B15,B16,B17,B18,B19,B20,B21,B22,B23,B24,B25,B26,B27,B28,B29,B30,B31'} + BLOCK_KEY5 : {show: y, blk : 9, word: 0, pos : 0, len: 256, start : 0, type: 'bytes:32', wr_dis : 28, rd_dis : 5, alt : KEY5, dict : '', desc: Key5 or user data, rloc: EFUSE_RD_KEY5_DATA0_REG, bloc: 'B0,B1,B2,B3,B4,B5,B6,B7,B8,B9,B10,B11,B12,B13,B14,B15,B16,B17,B18,B19,B20,B21,B22,B23,B24,B25,B26,B27,B28,B29,B30,B31'} + BLOCK_SYS_DATA2 : {show: y, blk: 10, word: 0, pos : 0, len: 256, start : 0, type: 'bytes:32', wr_dis : 29, rd_dis : 6, alt : SYS_DATA_PART2, dict : '', desc: System data part 2 (reserved), rloc: EFUSE_RD_SYS_PART2_DATA0_REG, bloc: 'B0,B1,B2,B3,B4,B5,B6,B7,B8,B9,B10,B11,B12,B13,B14,B15,B16,B17,B18,B19,B20,B21,B22,B23,B24,B25,B26,B27,B28,B29,B30,B31'} diff --git a/code/.venv/lib/python3.12/site-packages/espsecure/__init__.py b/code/.venv/lib/python3.12/site-packages/espsecure/__init__.py new file mode 100644 index 0000000..3e5ef20 --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/espsecure/__init__.py @@ -0,0 +1,1828 @@ +# SPDX-FileCopyrightText: 2016-2023 Espressif Systems (Shanghai) CO LTD +# +# SPDX-License-Identifier: GPL-2.0-or-later + +import argparse +import hashlib +import operator +import os +import struct +import sys +import tempfile +import zlib +from collections import namedtuple + +from cryptography import exceptions +from cryptography.hazmat.backends import default_backend +from cryptography.hazmat.primitives import hashes +from cryptography.hazmat.primitives import serialization +from cryptography.hazmat.primitives.asymmetric import ec, padding, rsa, utils +from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes +from cryptography.utils import int_to_bytes + +import ecdsa + +import esptool + +SIG_BLOCK_MAGIC = 0xE7 + +# Scheme used in Secure Boot V2 +SIG_BLOCK_VERSION_RSA = 0x02 +SIG_BLOCK_VERSION_ECDSA = 0x03 + +# Curve IDs used in Secure Boot V2 ECDSA signature blocks +CURVE_ID_P192 = 1 +CURVE_ID_P256 = 2 + +SECTOR_SIZE = 4096 +SIG_BLOCK_SIZE = ( + 1216 # Refer to secure boot v2 signature block format for more details. +) + + +def get_chunks(source, chunk_len): + """Returns an iterator over 'chunk_len' chunks of 'source'""" + return (source[i : i + chunk_len] for i in range(0, len(source), chunk_len)) + + +def endian_swap_words(source): + """Endian-swap each word in 'source' bitstring""" + assert len(source) % 4 == 0 + words = "I" * (len(source) // 4) + return struct.pack("<" + words, *struct.unpack(">" + words, source)) + + +def swap_word_order(source): + """Swap the order of the words in 'source' bitstring""" + assert len(source) % 4 == 0 + words = "I" * (len(source) // 4) + return struct.pack(words, *reversed(struct.unpack(words, source))) + + +def _load_hardware_key(keyfile): + """Load a 128/256/512-bit key, similar to stored in efuse, from a file + + 128-bit keys will be extended to 256-bit using the SHA256 of the key + 192-bit keys will be extended to 256-bit using the same algorithm used + by hardware if 3/4 Coding Scheme is set. + """ + key = keyfile.read() + if len(key) not in [16, 24, 32, 64]: + raise esptool.FatalError( + "Key file contains wrong length (%d bytes), 16, 24, 32 or 64 expected." + % len(key) + ) + if len(key) == 16: + key = _sha256_digest(key) + print("Using 128-bit key (extended)") + elif len(key) == 24: + key = key + key[8:16] + assert len(key) == 32 + print("Using 192-bit key (extended)") + elif len(key) == 32: + print("Using 256-bit key") + else: + print("Using 512-bit key") + return key + + +def digest_secure_bootloader(args): + """Calculate the digest of a bootloader image, in the same way the hardware + secure boot engine would do so. Can be used with a pre-loaded key to update a + secure bootloader.""" + _check_output_is_not_input(args.keyfile, args.output) + _check_output_is_not_input(args.image, args.output) + _check_output_is_not_input(args.iv, args.output) + if args.iv is not None: + print("WARNING: --iv argument is for TESTING PURPOSES ONLY") + iv = args.iv.read(128) + else: + iv = os.urandom(128) + plaintext_image = args.image.read() + args.image.seek(0) + + # secure boot engine reads in 128 byte blocks (ie SHA512 block + # size), but also doesn't look for any appended SHA-256 digest + fw_image = esptool.bin_image.ESP32FirmwareImage(args.image) + if fw_image.append_digest: + if len(plaintext_image) % 128 <= 32: + # ROM bootloader will read to the end of the 128 byte block, but not + # to the end of the SHA-256 digest at the end + new_len = len(plaintext_image) - (len(plaintext_image) % 128) + plaintext_image = plaintext_image[:new_len] + + # if image isn't 128 byte multiple then pad with 0xFF (ie unwritten flash) + # as this is what the secure boot engine will see + if len(plaintext_image) % 128 != 0: + plaintext_image += b"\xFF" * (128 - (len(plaintext_image) % 128)) + + plaintext = iv + plaintext_image + + # Secure Boot digest algorithm in hardware uses AES256 ECB to + # produce a ciphertext, then feeds output through SHA-512 to + # produce the digest. Each block in/out of ECB is reordered + # (due to hardware quirks not for security.) + + key = _load_hardware_key(args.keyfile) + backend = default_backend() + cipher = Cipher(algorithms.AES(key), modes.ECB(), backend=backend) + encryptor = cipher.encryptor() + digest = hashlib.sha512() + + for block in get_chunks(plaintext, 16): + block = block[::-1] # reverse each input block + + cipher_block = encryptor.update(block) + # reverse and then byte swap each word in the output block + cipher_block = cipher_block[::-1] + for block in get_chunks(cipher_block, 4): + # Python hashlib can build each SHA block internally + digest.update(block[::-1]) + + if args.output is None: + args.output = os.path.splitext(args.image.name)[0] + "-digest-0x0000.bin" + with open(args.output, "wb") as f: + f.write(iv) + digest = digest.digest() + for word in get_chunks(digest, 4): + f.write(word[::-1]) # swap word order in the result + f.write(b"\xFF" * (0x1000 - f.tell())) # pad to 0x1000 + f.write(plaintext_image) + print("digest+image written to %s" % args.output) + + +def _generate_ecdsa_signing_key(curve_id, keyfile): + sk = ecdsa.SigningKey.generate(curve=curve_id) + with open(keyfile, "wb") as f: + f.write(sk.to_pem()) + + +def generate_signing_key(args): + if os.path.exists(args.keyfile): + raise esptool.FatalError("ERROR: Key file %s already exists" % args.keyfile) + if args.version == "1": + if hasattr(args, "scheme"): + if args.scheme != "ecdsa256" and args.scheme is not None: + raise esptool.FatalError("ERROR: V1 only supports ECDSA256") + """ + Generate an ECDSA signing key for signing secure boot images (post-bootloader) + """ + _generate_ecdsa_signing_key(ecdsa.NIST256p, args.keyfile) + print("ECDSA NIST256p private key in PEM format written to %s" % args.keyfile) + elif args.version == "2": + if args.scheme == "rsa3072" or args.scheme is None: + """Generate a RSA 3072 signing key for signing secure boot images""" + private_key = rsa.generate_private_key( + public_exponent=65537, key_size=3072, backend=default_backend() + ).private_bytes( + encoding=serialization.Encoding.PEM, + format=serialization.PrivateFormat.TraditionalOpenSSL, + encryption_algorithm=serialization.NoEncryption(), + ) + with open(args.keyfile, "wb") as f: + f.write(private_key) + print("RSA 3072 private key in PEM format written to %s" % args.keyfile) + elif args.scheme == "ecdsa192": + """Generate a ECDSA 192 signing key for signing secure boot images""" + _generate_ecdsa_signing_key(ecdsa.NIST192p, args.keyfile) + print( + "ECDSA NIST192p private key in PEM format written to %s" % args.keyfile + ) + elif args.scheme == "ecdsa256": + """Generate a ECDSA 256 signing key for signing secure boot images""" + _generate_ecdsa_signing_key(ecdsa.NIST256p, args.keyfile) + print( + "ECDSA NIST256p private key in PEM format written to %s" % args.keyfile + ) + else: + raise esptool.FatalError( + "ERROR: Unsupported signing scheme (%s)" % args.scheme + ) + + +def load_ecdsa_signing_key(keyfile): + """Load ECDSA signing key""" + try: + sk = ecdsa.SigningKey.from_pem(keyfile.read()) + except ValueError: + raise esptool.FatalError( + "Incorrect ECDSA private key specified. " + "Please check algorithm and/or format." + ) + if sk.curve not in [ecdsa.NIST192p, ecdsa.NIST256p]: + raise esptool.FatalError("Supports NIST192p and NIST256p keys only") + return sk + + +def _load_ecdsa_signing_key(keyfile): + """Load ECDSA signing key for Secure Boot V1 only""" + sk = load_ecdsa_signing_key(keyfile) + if sk.curve != ecdsa.NIST256p: + raise esptool.FatalError( + "Signing key uses incorrect curve. ESP32 Secure Boot only supports " + "NIST256p (openssl calls this curve 'prime256v1')" + ) + return sk + + +def _load_ecdsa_verifying_key(keyfile): + """Load ECDSA verifying key for Secure Boot V1 only""" + try: + vk = ecdsa.VerifyingKey.from_pem(keyfile.read()) + except ValueError: + raise esptool.FatalError( + "Incorrect ECDSA public key specified. " + "Please check algorithm and/or format." + ) + if vk.curve != ecdsa.NIST256p: + raise esptool.FatalError( + "Signing key uses incorrect curve. ESP32 Secure Boot only supports " + "NIST256p (openssl calls this curve 'prime256v1')" + ) + return vk + + +def _load_sbv2_signing_key(keydata): + """ + Load Secure Boot V2 signing key + + can be rsa.RSAPrivateKey or ec.EllipticCurvePrivateKey + """ + sk = serialization.load_pem_private_key( + keydata, password=None, backend=default_backend() + ) + if isinstance(sk, rsa.RSAPrivateKey): + if sk.key_size != 3072: + raise esptool.FatalError( + "Key file has length %d bits. Secure boot v2 only supports RSA-3072." + % sk.key_size + ) + return sk + if isinstance(sk, ec.EllipticCurvePrivateKey): + if not ( + isinstance(sk.curve, ec.SECP192R1) or isinstance(sk.curve, ec.SECP256R1) + ): + raise esptool.FatalError( + "Key file uses incorrect curve. Secure Boot V2 + ECDSA only supports " + "NIST192p, NIST256p (aka prime192v1, prime256v1)" + ) + return sk + + raise esptool.FatalError("Unsupported signing key for Secure Boot V2") + + +def _load_sbv2_pub_key(keydata): + """ + Load Secure Boot V2 public key, can be rsa.RSAPublicKey or ec.EllipticCurvePublicKey + """ + vk = serialization.load_pem_public_key(keydata, backend=default_backend()) + if isinstance(vk, rsa.RSAPublicKey): + if vk.key_size != 3072: + raise esptool.FatalError( + "Key file has length %d bits. Secure boot v2 only supports RSA-3072." + % vk.key_size + ) + return vk + if isinstance(vk, ec.EllipticCurvePublicKey): + if not ( + isinstance(vk.curve, ec.SECP192R1) or isinstance(vk.curve, ec.SECP256R1) + ): + raise esptool.FatalError( + "Key file uses incorrect curve. Secure Boot V2 + ECDSA only supports " + "NIST192p, NIST256p (aka prime192v1, prime256v1)" + ) + return vk + + raise esptool.FatalError("Unsupported public key for Secure Boot V2") + + +def _get_sbv2_pub_key(keyfile): + key_data = keyfile.read() + if ( + b"-BEGIN RSA PRIVATE KEY" in key_data + or b"-BEGIN EC PRIVATE KEY" in key_data + or b"-BEGIN PRIVATE KEY" in key_data + ): + return _load_sbv2_signing_key(key_data).public_key() + elif b"-BEGIN PUBLIC KEY" in key_data: + vk = _load_sbv2_pub_key(key_data) + else: + raise esptool.FatalError( + "Verification key does not appear to be an RSA Private or " + "Public key in PEM format. Unsupported" + ) + return vk + + +def _get_sbv2_rsa_primitives(public_key): + primitives = namedtuple("primitives", ["n", "e", "m", "rinv"]) + numbers = public_key.public_numbers() + primitives.n = numbers.n # + primitives.e = numbers.e # two public key components + + # Note: this cheats and calls a private 'rsa' method to get the modular + # inverse calculation. + primitives.m = -rsa._modinv(primitives.n, 1 << 32) + + rr = 1 << (public_key.key_size * 2) + primitives.rinv = rr % primitives.n + return primitives + + +def _microecc_format(a, b, curve_len): + """ + Given two numbers (curve coordinates or (r,s) signature), write them out as a + little-endian byte sequence suitable for micro-ecc + "native little endian" mode + """ + byte_len = int(curve_len / 8) + ab = int_to_bytes(a, byte_len)[::-1] + int_to_bytes(b, byte_len)[::-1] + assert len(ab) == 48 or len(ab) == 64 + return ab + + +def sign_data(args): + if args.keyfile: + _check_output_is_not_input(args.keyfile, args.output) + _check_output_is_not_input(args.datafile, args.output) + if args.version == "1": + return sign_secure_boot_v1(args) + elif args.version == "2": + return sign_secure_boot_v2(args) + + +def sign_secure_boot_v1(args): + """ + Sign a data file with a ECDSA private key, append binary signature to file contents + """ + binary_content = args.datafile.read() + + if args.hsm: + raise esptool.FatalError( + "Secure Boot V1 does not support signing using an " + "external Hardware Security Module (HSM)" + ) + + if args.signature: + print("Pre-calculated signatures found") + if len(args.pub_key) > 1: + raise esptool.FatalError("Secure Boot V1 only supports one signing key") + signature = args.signature[0].read() + # get verifying/public key + vk = _load_ecdsa_verifying_key(args.pub_key[0]) + else: + if len(args.keyfile) > 1: + raise esptool.FatalError("Secure Boot V1 only supports one signing key") + sk = _load_ecdsa_signing_key(args.keyfile[0]) + + # calculate signature of binary data + signature = sk.sign_deterministic(binary_content, hashlib.sha256) + # get verifying/public key + vk = sk.get_verifying_key() + + # back-verify signature + vk.verify(signature, binary_content, hashlib.sha256) # throws exception on failure + if args.output is None or os.path.abspath(args.output) == os.path.abspath( + args.datafile.name + ): # append signature to input file + args.datafile.close() + outfile = open(args.datafile.name, "ab") + else: # write file & signature to new file + outfile = open(args.output, "wb") + outfile.write(binary_content) + outfile.write( + struct.pack("I", 0) + ) # Version indicator, allow for different curves/formats later + outfile.write(signature) + outfile.close() + print("Signed %d bytes of data from %s" % (len(binary_content), args.datafile.name)) + + +def sign_secure_boot_v2(args): + """ + Sign a firmware app image with an RSA private key using RSA-PSS, + or ECDSA private key using P192 or P256. + + Write output file with a Secure Boot V2 header appended. + """ + SIG_BLOCK_MAX_COUNT = 3 + contents = args.datafile.read() + sig_block_num = 0 + signature_sector = b"" + + signature = args.signature + pub_key = args.pub_key + + if len(contents) % SECTOR_SIZE != 0: + if args.signature: + raise esptool.FatalError( + "Secure Boot V2 requires the signature block to start " + "from a 4KB aligned sector " + "but the datafile supplied is not sector aligned." + ) + else: + pad_by = SECTOR_SIZE - (len(contents) % SECTOR_SIZE) + print( + f"Padding data contents by {pad_by} bytes " + "so signature sector aligns at sector boundary" + ) + contents += b"\xff" * pad_by + + elif args.append_signatures: + while sig_block_num < SIG_BLOCK_MAX_COUNT: + sig_block = validate_signature_block(contents, sig_block_num) + if sig_block is None: + break + signature_sector += ( + sig_block # Signature sector is populated with already valid blocks + ) + sig_block_num += 1 + + if len(signature_sector) % SIG_BLOCK_SIZE != 0: + raise esptool.FatalError("Incorrect signature sector size") + + if sig_block_num == 0: + print( + "No valid signature blocks found. " + "Discarding --append-signature and proceeding to sign the image afresh." + ) + else: + print( + f"{sig_block_num} valid signature block(s) already present " + "in the signature sector." + ) + if sig_block_num == SIG_BLOCK_MAX_COUNT: + raise esptool.FatalError( + f"Upto {SIG_BLOCK_MAX_COUNT} signature blocks are supported. " + "(For ESP32-ECO3 only 1 signature block is supported)" + ) + + # Signature stripped off the content + # (the legitimate blocks are included in signature_sector) + contents = contents[: len(contents) - SECTOR_SIZE] + + if args.hsm: + if args.hsm_config is None: + raise esptool.FatalError( + "Config file is required to generate signature using an external HSM." + ) + import espsecure.esp_hsm_sign as hsm + + try: + config = hsm.read_hsm_config(args.hsm_config) + except Exception as e: + raise esptool.FatalError(f"Incorrect HSM config file format ({e})") + if pub_key is None: + pub_key = extract_pubkey_from_hsm(config) + signature = generate_signature_using_hsm(config, contents) + + if signature: + print("Pre-calculated signatures found") + key_count = len(pub_key) + if len(signature) != key_count: + raise esptool.FatalError( + f"Number of public keys ({key_count}) not equal to " + f"the number of signatures {len(signature)}." + ) + else: + key_count = len(args.keyfile) + + empty_signature_blocks = SIG_BLOCK_MAX_COUNT - sig_block_num + if key_count > empty_signature_blocks: + raise esptool.FatalError( + f"Number of keys({key_count}) more than the empty signature blocks." + f"({empty_signature_blocks})" + ) + + print(f"{key_count} signing key(s) found.") + # Calculate digest of data file + digest = hashlib.sha256() + digest.update(contents) + digest = digest.digest() + + # Generate signature block using pre-calculated signatures + if signature: + signature_block = generate_signature_block_using_pre_calculated_signature( + signature, pub_key, digest + ) + # Generate signature block by signing using private keys + else: + signature_block = generate_signature_block_using_private_key( + args.keyfile, digest + ) + + if signature_block is None or len(signature_block) == 0: + raise esptool.FatalError("Signature Block generation failed") + + signature_sector += signature_block + + if ( + len(signature_sector) < 0 + and len(signature_sector) > SIG_BLOCK_SIZE * 3 + and len(signature_sector) % SIG_BLOCK_SIZE != 0 + ): + raise esptool.FatalError("Incorrect signature sector generation") + + total_sig_blocks = len(signature_sector) // SIG_BLOCK_SIZE + + # Pad signature_sector to sector + signature_sector = signature_sector + ( + b"\xff" * (SECTOR_SIZE - len(signature_sector)) + ) + if len(signature_sector) != SECTOR_SIZE: + raise esptool.FatalError("Incorrect signature sector size") + + # Write to output file, or append to existing file + if args.output is None: + args.datafile.close() + args.output = args.datafile.name + with open(args.output, "wb") as f: + f.write(contents + signature_sector) + print( + f"Signed {len(contents)} bytes of data from {args.datafile.name}. " + f"Signature sector now has {total_sig_blocks} signature blocks." + ) + + +def generate_signature_using_hsm(config, contents): + import espsecure.esp_hsm_sign as hsm + + session = hsm.establish_session(config) + # get the private key + private_key = hsm.get_privkey_info(session, config) + # Sign payload + signature = hsm.sign_payload(private_key, contents) + hsm.close_connection(session) + temp_signature_file = tempfile.TemporaryFile() + temp_signature_file.write(signature) + temp_signature_file.seek(0) + return [temp_signature_file] + + +def generate_signature_block_using_pre_calculated_signature(signature, pub_key, digest): + signature_blocks = b"" + for sig, pk in zip(signature, pub_key): + try: + public_key = _get_sbv2_pub_key(pk) + signature = sig.read() + if isinstance(public_key, rsa.RSAPublicKey): + # RSA signature + rsa_primitives = _get_sbv2_rsa_primitives(public_key) + # Verify the signature + public_key.verify( + signature, + digest, + padding.PSS(mgf=padding.MGF1(hashes.SHA256()), salt_length=32), + utils.Prehashed(hashes.SHA256()), + ) + + signature_block = generate_rsa_signature_block( + digest, rsa_primitives, signature + ) + else: + # ECDSA signature + numbers = public_key.public_numbers() + if isinstance(numbers.curve, ec.SECP192R1): + curve_len = 192 + curve_id = CURVE_ID_P192 + elif isinstance(numbers.curve, ec.SECP256R1): + curve_len = 256 + curve_id = CURVE_ID_P256 + else: + raise esptool.FatalError("Invalid ECDSA curve instance.") + + # Verify the signature + public_key.verify( + signature, digest, ec.ECDSA(utils.Prehashed(hashes.SHA256())) + ) + + pubkey_point = _microecc_format(numbers.x, numbers.y, curve_len) + r, s = utils.decode_dss_signature(signature) + signature_rs = _microecc_format(r, s, curve_len) + signature_block = generate_ecdsa_signature_block( + digest, curve_id, pubkey_point, signature_rs + ) + except exceptions.InvalidSignature: + raise esptool.FatalError( + "Signature verification failed: Invalid Signature\n" + "The pre-calculated signature has not been signed " + "using the given public key" + ) + signature_block += struct.pack("> 5 + key ^= ((mul1 * addr) | ((mul2 * addr) & mul2_mask)) & tweak_range + return int.to_bytes(key, length=32, byteorder="big", signed=False) + + +def generate_flash_encryption_key(args): + print("Writing %d random bits to key file %s" % (args.keylen, args.key_file.name)) + args.key_file.write(os.urandom(args.keylen // 8)) + + +def _flash_encryption_operation_esp32( + output_file, input_file, flash_address, keyfile, flash_crypt_conf, do_decrypt +): + key = _load_hardware_key(keyfile) + + if flash_address % 16 != 0: + raise esptool.FatalError( + "Starting flash address 0x%x must be a multiple of 16" % flash_address + ) + + if flash_crypt_conf == 0: + print("WARNING: Setting FLASH_CRYPT_CONF to zero is not recommended") + + tweak_range = _flash_encryption_tweak_range_bits(flash_crypt_conf) + key = int.from_bytes(key, byteorder="big", signed=False) + + backend = default_backend() + + cipher = None + block_offs = flash_address + while True: + block = input_file.read(16) + if len(block) == 0: + break + elif len(block) < 16: + if do_decrypt: + raise esptool.FatalError("Data length is not a multiple of 16 bytes") + pad = 16 - len(block) + block = block + os.urandom(pad) + print( + "Note: Padding with %d bytes of random data " + "(encrypted data must be multiple of 16 bytes long)" % pad + ) + + if block_offs % 32 == 0 or cipher is None: + # each bit of the flash encryption key is XORed with tweak bits + # derived from the offset of 32 byte block of flash + block_key = _flash_encryption_tweak_key(key, block_offs, tweak_range) + + if cipher is None: # first pass + cipher = Cipher(algorithms.AES(block_key), modes.ECB(), backend=backend) + + # note AES is used inverted for flash encryption, so + # "decrypting" flash uses AES encrypt algorithm and vice + # versa. (This does not weaken AES.) + actor = cipher.encryptor() if do_decrypt else cipher.decryptor() + else: + # performance hack: changing the key using pyca-cryptography API + # requires recreating'actor'. + # With openssl backend, this re-initializes the openssl cipher context. + # To save some time, manually call EVP_CipherInit_ex() in the openssl + # backend to update the key. + # If it fails, fall back to recreating the entire context via public API + try: + backend = actor._ctx._backend + res = backend._lib.EVP_CipherInit_ex( + actor._ctx._ctx, + backend._ffi.NULL, + backend._ffi.NULL, + backend._ffi.from_buffer(block_key), + backend._ffi.NULL, + actor._ctx._operation, + ) + backend.openssl_assert(res != 0) + except AttributeError: + # backend is not an openssl backend, or implementation has changed: + # fall back to the slow safe version + cipher.algorithm.key = block_key + actor = cipher.encryptor() if do_decrypt else cipher.decryptor() + + block = block[::-1] # reverse input block byte order + block = actor.update(block) + + output_file.write(block[::-1]) # reverse output block byte order + block_offs += 16 + + +def _flash_encryption_operation_aes_xts( + output_file, input_file, flash_address, keyfile, do_decrypt +): + """ + Apply the AES-XTS algorithm with the hardware addressing scheme used by Espressif + + key = AES-XTS key (32 or 64 bytes) + flash_address = address in flash to encrypt at. Must be multiple of 16 bytes. + indata = Data to encrypt/decrypt. Must be multiple of 16 bytes. + encrypt = True to Encrypt indata, False to decrypt indata. + + Returns a bitstring of the ciphertext or plaintext result. + """ + + backend = default_backend() + key = _load_hardware_key(keyfile) + indata = input_file.read() + + if flash_address % 16 != 0: + raise esptool.FatalError( + "Starting flash address 0x%x must be a multiple of 16" % flash_address + ) + + if len(indata) % 16 != 0: + raise esptool.FatalError( + "Input data length (%d) must be a multiple of 16" % len(indata) + ) + + if len(indata) == 0: + raise esptool.FatalError("Input data must be longer than 0") + + # left pad for a 1024-bit aligned address + pad_left = flash_address % 0x80 + indata = (b"\x00" * pad_left) + indata + + # right pad for full 1024-bit blocks + pad_right = len(indata) % 0x80 + if pad_right > 0: + pad_right = 0x80 - pad_right + indata = indata + (b"\x00" * pad_right) + + inblocks = _split_blocks(indata, 0x80) # split into 1024 bit blocks + + output = [] + for inblock in inblocks: # for each block + tweak = struct.pack(" 0: + if not self.file_obj: + self.file_obj = open(self.path, "wb") + self.file_obj.write(payload) + + def close(self): + if self.file_obj: + self.file_obj.close() + self.file_obj = None + + @property + def name(self): + return self.path + + +def main(custom_commandline=None): + """ + Main function for espsecure + + custom_commandline - Optional override for default arguments parsing + (that uses sys.argv), can be a list of custom arguments as strings. + Arguments and their values need to be added as individual items to the list + e.g. "--port /dev/ttyUSB1" thus becomes ['--port', '/dev/ttyUSB1']. + """ + parser = argparse.ArgumentParser( + description="espsecure.py v%s - ESP32 Secure Boot & Flash Encryption tool" + % esptool.__version__, + prog="espsecure", + ) + + subparsers = parser.add_subparsers( + dest="operation", help="Run espsecure.py {command} -h for additional help" + ) + + p = subparsers.add_parser( + "digest_secure_bootloader", + help="Take a bootloader binary image and a secure boot key, " + "and output a combined digest+binary suitable for flashing along " + "with the precalculated secure boot key.", + ) + p.add_argument( + "--keyfile", + "-k", + help="256 bit key for secure boot digest.", + type=argparse.FileType("rb"), + required=True, + ) + p.add_argument("--output", "-o", help="Output file for signed digest image.") + p.add_argument( + "--iv", + help="128 byte IV file. Supply a file for testing purposes only, " + "if not supplied an IV will be randomly generated.", + type=argparse.FileType("rb"), + ) + p.add_argument( + "image", + help="Bootloader image file to calculate digest from", + type=argparse.FileType("rb"), + ) + + p = subparsers.add_parser( + "generate_signing_key", + help="Generate a private key for signing secure boot images " + "as per the secure boot version. " + "Key file is generated in PEM format, " + "Secure Boot V1 - ECDSA NIST256p private key. " + "Secure Boot V2 - RSA 3072, ECDSA NIST256p, ECDSA NIST192p private key.", + ) + p.add_argument( + "--version", + "-v", + help="Version of the secure boot signing scheme to use.", + choices=["1", "2"], + default="1", + ) + p.add_argument( + "--scheme", + "-s", + help="Scheme of secure boot signing.", + choices=["rsa3072", "ecdsa192", "ecdsa256"], + required=False, + ) + p.add_argument( + "keyfile", help="Filename for private key file (embedded public key)" + ) + + p = subparsers.add_parser( + "sign_data", + help="Sign a data file for use with secure boot. " + "Signing algorithm is deterministic ECDSA w/ SHA-512 (V1) " + "or either RSA-PSS or ECDSA w/ SHA-256 (V2).", + ) + p.add_argument( + "--version", + "-v", + help="Version of the secure boot signing scheme to use.", + choices=["1", "2"], + required=True, + ) + p.add_argument( + "--keyfile", + "-k", + help="Private key file for signing. Key is in PEM format.", + type=argparse.FileType("rb"), + nargs="+", + ) + p.add_argument( + "--append_signatures", + "-a", + help="Append signature block(s) to already signed image. " + "Valid only for ESP32-S2.", + action="store_true", + ) + p.add_argument( + "--hsm", + help="Use an external Hardware Security Module " + "to generate signature using PKCS#11 interface.", + action="store_true", + ) + p.add_argument( + "--hsm-config", + help="Config file for the external Hardware Security Module " + "to be used to generate signature.", + default=None, + ) + p.add_argument( + "--pub-key", + help="Public key files corresponding to the private key used to generate " + "the pre-calculated signatures. Keys should be in PEM format.", + type=argparse.FileType("rb"), + nargs="+", + ) + p.add_argument( + "--signature", + help="Pre-calculated signatures. " + "Signatures generated using external private keys e.g. keys stored in HSM.", + type=argparse.FileType("rb"), + nargs="+", + default=None, + ) + p.add_argument( + "--output", + "-o", + help="Output file for signed digest image. Default is to sign the input file.", + ) + p.add_argument( + "datafile", + help="File to sign. For version 1, this can be any file. " + "For version 2, this must be a valid app image.", + type=argparse.FileType("rb"), + ) + + p = subparsers.add_parser( + "verify_signature", + help='Verify a data file previously signed by "sign_data", ' + "using the public key.", + ) + p.add_argument( + "--version", + "-v", + help="Version of the secure boot scheme to use.", + choices=["1", "2"], + required=True, + ) + p.add_argument( + "--hsm", + help="Use an external Hardware Security Module " + "to verify signature using PKCS#11 interface.", + action="store_true", + ) + p.add_argument( + "--hsm-config", + help="Config file for the external Hardware Security Module " + "to be used to verify signature.", + default=None, + ) + p.add_argument( + "--keyfile", + "-k", + help="Public key file for verification. " + "Can be private or public key in PEM format.", + type=argparse.FileType("rb"), + ) + p.add_argument( + "datafile", + help="Signed data file to verify signature.", + type=argparse.FileType("rb"), + ) + + p = subparsers.add_parser( + "extract_public_key", + help="Extract the public verification key for signatures, " + "save it as a raw binary file.", + ) + p.add_argument( + "--version", + "-v", + help="Version of the secure boot signing scheme to use.", + choices=["1", "2"], + default="1", + ) + p.add_argument( + "--keyfile", + "-k", + help="Private key file (PEM format) to extract the " + "public verification key from.", + type=argparse.FileType("rb"), + required=True, + ) + p.add_argument( + "public_keyfile", help="File to save new public key into", type=OutFileType() + ) + + # Kept for compatibility purpose. We can deprecate this in a future release + p = subparsers.add_parser( + "digest_rsa_public_key", + help="Generate an SHA-256 digest of the RSA public key. " + "This digest is burned into the eFuse and asserts the legitimacy " + "of the public key for Secure boot v2.", + ) + p.add_argument( + "--keyfile", + "-k", + help="Public key file for verification. " + "Can be private or public key in PEM format.", + type=argparse.FileType("rb"), + required=True, + ) + p.add_argument("--output", "-o", help="Output file for the digest.", required=True) + + p = subparsers.add_parser( + "digest_sbv2_public_key", + help="Generate an SHA-256 digest of the public key. " + "This digest is burned into the eFuse and asserts the legitimacy " + "of the public key for Secure boot v2.", + ) + p.add_argument( + "--keyfile", + "-k", + help="Public key file for verification. " + "Can be private or public key in PEM format.", + type=argparse.FileType("rb"), + required=True, + ) + p.add_argument("--output", "-o", help="Output file for the digest.", required=True) + + p = subparsers.add_parser( + "signature_info_v2", + help="Reads the signature block and provides the signature block information.", + ) + p.add_argument( + "datafile", + help="Secure boot v2 signed data file.", + type=argparse.FileType("rb"), + ) + + p = subparsers.add_parser( + "digest_private_key", + help="Generate an SHA-256 digest of the private signing key. " + "This can be used as a reproducible secure bootloader (only secure boot v1) " + "or flash encryption key.", + ) + p.add_argument( + "--keyfile", + "-k", + help="Private key file (PEM format) to generate a digest from.", + type=argparse.FileType("rb"), + required=True, + ) + p.add_argument( + "--keylen", + "-l", + help="Length of private key digest file to generate (in bits). " + "3/4 Coding Scheme requires 192 bit key.", + choices=[192, 256], + default=256, + type=int, + ) + p.add_argument( + "digest_file", help="File to write 32 byte digest into", type=OutFileType() + ) + + p = subparsers.add_parser( + "generate_flash_encryption_key", + help="Generate a development-use flash encryption key with random data.", + ) + p.add_argument( + "--keylen", + "-l", + help="Length of private key digest file to generate (in bits). " + "3/4 Coding Scheme requires 192 bit key.", + choices=[128, 192, 256, 512], + default=256, + type=int, + ) + p.add_argument( + "key_file", + help="File to write 16, 24, 32 or 64 byte key into", + type=OutFileType(), + ) + + p = subparsers.add_parser( + "decrypt_flash_data", + help="Decrypt some data read from encrypted flash (using known key)", + ) + p.add_argument( + "encrypted_file", + help="File with encrypted flash contents", + type=argparse.FileType("rb"), + ) + p.add_argument( + "--aes_xts", + "-x", + help="Decrypt data using AES-XTS as used on " + "ESP32-S2, ESP32-C2, ESP32-C3, ESP32-C6 and ESP32-P4", + action="store_true", + ) + p.add_argument( + "--keyfile", + "-k", + help="File with flash encryption key", + type=argparse.FileType("rb"), + required=True, + ) + p.add_argument( + "--output", + "-o", + help="Output file for plaintext data.", + type=OutFileType(), + required=True, + ) + p.add_argument( + "--address", + "-a", + help="Address offset in flash that file was read from.", + required=True, + type=esptool.arg_auto_int, + ) + p.add_argument( + "--flash_crypt_conf", + help="Override FLASH_CRYPT_CONF efuse value (default is 0XF).", + required=False, + default=0xF, + type=esptool.arg_auto_int, + ) + + p = subparsers.add_parser( + "encrypt_flash_data", + help="Encrypt some data suitable for encrypted flash (using known key)", + ) + p.add_argument( + "--aes_xts", + "-x", + help="Encrypt data using AES-XTS as used on " + "ESP32-S2, ESP32-C2, ESP32-C3, ESP32-C6 and ESP32-P4", + action="store_true", + ) + p.add_argument( + "--keyfile", + "-k", + help="File with flash encryption key", + type=argparse.FileType("rb"), + required=True, + ) + p.add_argument( + "--output", + "-o", + help="Output file for encrypted data.", + type=OutFileType(), + required=True, + ) + p.add_argument( + "--address", + "-a", + help="Address offset in flash where file will be flashed.", + required=True, + type=esptool.arg_auto_int, + ) + p.add_argument( + "--flash_crypt_conf", + help="Override FLASH_CRYPT_CONF efuse value (default is 0XF).", + required=False, + default=0xF, + type=esptool.arg_auto_int, + ) + p.add_argument( + "plaintext_file", + help="File with plaintext content for encrypting", + type=argparse.FileType("rb"), + ) + + args = parser.parse_args(custom_commandline) + print("espsecure.py v%s" % esptool.__version__) + if args.operation is None: + parser.print_help() + parser.exit(1) + + try: + # each 'operation' is a module-level function of the same name + operation_func = globals()[args.operation] + operation_func(args) + finally: + for arg_name in vars(args): + obj = getattr(args, arg_name) + if isinstance(obj, OutFileType): + obj.close() + + +def _main(): + try: + main() + except esptool.FatalError as e: + print("\nA fatal error occurred: %s" % e) + sys.exit(2) + except ValueError as e: + try: + if [arg for arg in e.args if "Could not deserialize key data." in arg]: + print( + "Note: This error originates from the cryptography module. " + "It is likely not a problem with espsecure, " + "please make sure you are using a compatible OpenSSL backend." + ) + finally: + raise + + +if __name__ == "__main__": + _main() diff --git a/code/.venv/lib/python3.12/site-packages/espsecure/__main__.py b/code/.venv/lib/python3.12/site-packages/espsecure/__main__.py new file mode 100644 index 0000000..00f62ca --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/espsecure/__main__.py @@ -0,0 +1,8 @@ +# SPDX-FileCopyrightText: 2016-2022 Espressif Systems (Shanghai) CO LTD +# +# SPDX-License-Identifier: GPL-2.0-or-later + +import espsecure + +if __name__ == "__main__": + espsecure._main() diff --git a/code/.venv/lib/python3.12/site-packages/espsecure/__pycache__/__init__.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/espsecure/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..0223b95 Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/espsecure/__pycache__/__init__.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/espsecure/__pycache__/__main__.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/espsecure/__pycache__/__main__.cpython-312.pyc new file mode 100644 index 0000000..7fab2e6 Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/espsecure/__pycache__/__main__.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/espsecure/esp_hsm_sign/__init__.py b/code/.venv/lib/python3.12/site-packages/espsecure/esp_hsm_sign/__init__.py new file mode 100644 index 0000000..d255116 --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/espsecure/esp_hsm_sign/__init__.py @@ -0,0 +1,180 @@ +# SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD +# +# SPDX-License-Identifier: GPL-2.0-or-later + +import binascii +import configparser +import os +import sys +from getpass import getpass + +try: + import pkcs11 + from .exceptions import handle_exceptions +except ImportError: + raise ImportError( + "python-pkcs11 package is not installed. " + "Please install it using the required packages with command: " + "pip install 'esptool[hsm]'" + ) + +import cryptography.hazmat.primitives.asymmetric.ec as EC +import cryptography.hazmat.primitives.asymmetric.rsa as RSA + +import ecdsa + + +def read_hsm_config(configfile): + config = configparser.ConfigParser() + config.read(configfile) + + section = "hsm_config" + if not config.has_section(section): + raise configparser.NoSectionError(section) + + section_options = ["pkcs11_lib", "slot", "label"] + for option in section_options: + if not config.has_option(section, option): + raise configparser.NoOptionError(option, section) + + # If the config file does not contain the "credentials" option, + # prompt the user for the HSM PIN + if not config.has_option(section, "credentials"): + hsm_pin = getpass("Please enter the PIN of your HSM:\n") + config.set(section, "credentials", hsm_pin) + + return config[section] + + +def establish_session(config): + print("Trying to establish a session with the HSM.") + try: + if os.path.exists(config["pkcs11_lib"]): + lib = pkcs11.lib(config["pkcs11_lib"]) + else: + print(f'LIB file does not exist at {config["pkcs11_lib"]}') + sys.exit(1) + for slot in lib.get_slots(token_present=True): + if slot.slot_id == int(config["slot"]): + break + + token = slot.get_token() + session = token.open(rw=True, user_pin=config["credentials"]) + print(f'Session creation successful with HSM slot {int(config["slot"])}.') + return session + + except pkcs11.exceptions.PKCS11Error as e: + handle_exceptions(e) + print("Session establishment failed") + sys.exit(1) + + +def get_privkey_info(session, config): + try: + private_key = session.get_key( + object_class=pkcs11.constants.ObjectClass.PRIVATE_KEY, label=config["label"] + ) + print(f'Got private key metadata with label {config["label"]}.') + return private_key + + except pkcs11.exceptions.PKCS11Error as e: + handle_exceptions(e) + print("Failed to get the private key") + sys.exit(1) + + +def get_pubkey(session, config): + print("Trying to extract public key from the HSM.") + try: + if "label_pubkey" in config: + public_key_label = config["label_pubkey"] + else: + print( + "Config option 'label_pubkey' not found, " + "using config option 'label' for public key." + ) + public_key_label = config["label"] + + public_key = session.get_key( + object_class=pkcs11.constants.ObjectClass.PUBLIC_KEY, + label=public_key_label, + ) + if public_key.key_type == pkcs11.mechanisms.KeyType.RSA: + exponent = public_key[pkcs11.Attribute.PUBLIC_EXPONENT] + modulus = public_key[pkcs11.Attribute.MODULUS] + e = int.from_bytes(exponent, byteorder="big") + n = int.from_bytes(modulus, byteorder="big") + public_key = RSA.RSAPublicNumbers(e, n).public_key() + + elif public_key.key_type == pkcs11.mechanisms.KeyType.EC: + ecpoints, _ = ecdsa.der.remove_octet_string( + public_key[pkcs11.Attribute.EC_POINT] + ) + public_key = EC.EllipticCurvePublicKey.from_encoded_point( + EC.SECP256R1(), ecpoints + ) + + else: + print("Incorrect public key algorithm") + sys.exit(1) + + print(f"Got public key with label {public_key_label}.") + return public_key + + except pkcs11.exceptions.PKCS11Error as e: + handle_exceptions(e) + print("Failed to extract the public key") + sys.exit(1) + + +def sign_payload(private_key, payload): + try: + print("Signing payload using the HSM.") + key_type = private_key.key_type + mechanism, mechanism_params = get_mechanism(key_type) + signature = private_key.sign( + data=payload, mechanism=mechanism, mechanism_param=mechanism_params + ) + + if len(signature) != 0: + print("Signature generation successful.") + + if key_type == pkcs11.mechanisms.KeyType.EC: + r = int(binascii.hexlify(signature[:32]), 16) + s = int(binascii.hexlify(signature[32:]), 16) + + # der encoding in case of ecdsa signatures + signature = ecdsa.der.encode_sequence( + ecdsa.der.encode_integer(r), ecdsa.der.encode_integer(s) + ) + + return signature + + except pkcs11.exceptions.PKCS11Error as e: + handle_exceptions(e, mechanism) + print("Payload Signing Failed") + sys.exit(1) + + +def get_mechanism(key_type): + if key_type == pkcs11.mechanisms.KeyType.RSA: + return pkcs11.mechanisms.Mechanism.SHA256_RSA_PKCS_PSS, ( + pkcs11.mechanisms.Mechanism.SHA256, + pkcs11.MGF.SHA256, + 32, + ) + elif key_type == pkcs11.mechanisms.KeyType.EC: + return pkcs11.mechanisms.Mechanism.ECDSA_SHA256, None + else: + print("Invalid signing key mechanism") + sys.exit(1) + + +def close_connection(session): + try: + session.close() + print("Connection closed successfully") + except pkcs11.exceptions.PKCS11Error as e: + handle_exceptions(e) + print("Failed to close the HSM session") + sys.exit(1) diff --git a/code/.venv/lib/python3.12/site-packages/espsecure/esp_hsm_sign/__pycache__/__init__.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/espsecure/esp_hsm_sign/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..2dabcd9 Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/espsecure/esp_hsm_sign/__pycache__/__init__.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/espsecure/esp_hsm_sign/__pycache__/exceptions.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/espsecure/esp_hsm_sign/__pycache__/exceptions.cpython-312.pyc new file mode 100644 index 0000000..53b40ca Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/espsecure/esp_hsm_sign/__pycache__/exceptions.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/espsecure/esp_hsm_sign/exceptions.py b/code/.venv/lib/python3.12/site-packages/espsecure/esp_hsm_sign/exceptions.py new file mode 100644 index 0000000..47ba8e3 --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/espsecure/esp_hsm_sign/exceptions.py @@ -0,0 +1,50 @@ +# SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from pkcs11.exceptions import ( + AlreadyInitialized, + AnotherUserAlreadyLoggedIn, + ArgumentsBad, + DeviceRemoved, + DomainParamsInvalid, + FunctionFailed, + MechanismInvalid, + NoSuchKey, + NoSuchToken, + OperationNotInitialized, + SessionClosed, +) + + +def handle_exceptions(e, info=""): + exception_type = e.__class__ + if exception_type == MechanismInvalid: + print("The External HSM does not support the given mechanism", info) + elif exception_type == FunctionFailed: + print( + "Please ensure proper configuration, privileges and environment variables" + ) + elif exception_type == AlreadyInitialized: + print("pkcs11 is already initialized with another library") + elif exception_type == AnotherUserAlreadyLoggedIn: + print("Another User has been already logged in") + elif exception_type == ArgumentsBad: + print("Please check the arguments supplied to the function") + elif exception_type == DomainParamsInvalid: + print("Invalid or unsupported domain parameters were supplied to the function") + elif exception_type == DeviceRemoved: + print( + "The token has been removed from its slot during " + "the execution of the function" + ) + elif exception_type == NoSuchToken: + print("No such token found") + elif exception_type == NoSuchKey: + print("No such key found") + elif exception_type == OperationNotInitialized: + print("Operation not Initialized") + elif exception_type == SessionClosed: + print("Session already closed") + else: + print(e.__class__, info) diff --git a/code/.venv/lib/python3.12/site-packages/esptool-4.7.0.dist-info/INSTALLER b/code/.venv/lib/python3.12/site-packages/esptool-4.7.0.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/esptool-4.7.0.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/code/.venv/lib/python3.12/site-packages/esptool-4.7.0.dist-info/LICENSE b/code/.venv/lib/python3.12/site-packages/esptool-4.7.0.dist-info/LICENSE new file mode 100644 index 0000000..d159169 --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/esptool-4.7.0.dist-info/LICENSE @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. diff --git a/code/.venv/lib/python3.12/site-packages/esptool-4.7.0.dist-info/METADATA b/code/.venv/lib/python3.12/site-packages/esptool-4.7.0.dist-info/METADATA new file mode 100644 index 0000000..8e9db26 --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/esptool-4.7.0.dist-info/METADATA @@ -0,0 +1,65 @@ +Metadata-Version: 2.1 +Name: esptool +Version: 4.7.0 +Summary: A serial utility to communicate & flash code to Espressif chips. +Home-page: https://github.com/espressif/esptool/ +Author: Fredrik Ahlberg (themadinventor) & Angus Gratton (projectgus) & Espressif Systems +Author-email: +License: GPLv2+ +Project-URL: Documentation, https://docs.espressif.com/projects/esptool/ +Project-URL: Source, https://github.com/espressif/esptool/ +Project-URL: Tracker, https://github.com/espressif/esptool/issues/ +Classifier: Development Status :: 5 - Production/Stable +Classifier: Intended Audience :: Developers +Classifier: Natural Language :: English +Classifier: Operating System :: POSIX +Classifier: Operating System :: Microsoft :: Windows +Classifier: Operating System :: MacOS :: MacOS X +Classifier: Topic :: Software Development :: Embedded Systems +Classifier: Environment :: Console +Classifier: License :: OSI Approved :: GNU General Public License v2 or later (GPLv2+) +Classifier: Programming Language :: Python :: 3.7 +Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: 3.9 +Classifier: Programming Language :: Python :: 3.10 +Classifier: Programming Language :: Python :: 3.11 +Classifier: Programming Language :: Python :: 3.12 +Requires-Python: >=3.7 +License-File: LICENSE +Requires-Dist: bitstring >=3.1.6 +Requires-Dist: cryptography >=2.1.4 +Requires-Dist: ecdsa >=0.16.0 +Requires-Dist: pyserial >=3.0 +Requires-Dist: reedsolo <1.8,>=1.5.3 +Requires-Dist: PyYAML >=5.1 +Requires-Dist: intelhex +Provides-Extra: dev +Requires-Dist: flake8 >=3.2.0 ; extra == 'dev' +Requires-Dist: flake8-import-order ; extra == 'dev' +Requires-Dist: flake8-gl-codeclimate ; extra == 'dev' +Requires-Dist: pyelftools ; extra == 'dev' +Requires-Dist: coverage ~=6.0 ; extra == 'dev' +Requires-Dist: black ; extra == 'dev' +Requires-Dist: pre-commit ; extra == 'dev' +Requires-Dist: pytest ; extra == 'dev' +Requires-Dist: pytest-rerunfailures ; extra == 'dev' +Requires-Dist: requests ; extra == 'dev' +Requires-Dist: commitizen ; extra == 'dev' +Provides-Extra: hsm +Requires-Dist: python-pkcs11 ; extra == 'hsm' + + +========== +esptool.py +========== +A Python-based, open-source, platform-independent utility to communicate with the ROM bootloader in Espressif chips. + +The esptool.py project is `hosted on github `_. + +Documentation +------------- +Visit online `esptool documentation `_ or run ``esptool.py -h``. + +Contributing +------------ +Please see the `contributions guide `_. diff --git a/code/.venv/lib/python3.12/site-packages/esptool-4.7.0.dist-info/RECORD b/code/.venv/lib/python3.12/site-packages/esptool-4.7.0.dist-info/RECORD new file mode 100644 index 0000000..a77debb --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/esptool-4.7.0.dist-info/RECORD @@ -0,0 +1,206 @@ +../../../bin/__pycache__/esp_rfc2217_server.cpython-312.pyc,, +../../../bin/__pycache__/espefuse.cpython-312.pyc,, +../../../bin/__pycache__/espsecure.cpython-312.pyc,, +../../../bin/__pycache__/esptool.cpython-312.pyc,, +../../../bin/esp_rfc2217_server.py,sha256=SDopMdr7qX0utY7DddqAcOdg6WD1Lw0vTOgZkn-CgN8,10528 +../../../bin/espefuse.py,sha256=0T2S--pWQgsnR1hCSqODm9PRf5freN3yJI3qAbojs_Q,1212 +../../../bin/espsecure.py,sha256=Jy4cCRTbb8XKp9ORuZXDgg3SkvljOTzt2oD5DmrNr4w,1217 +../../../bin/esptool.py,sha256=cMBJhsHGFTp8DLFY-ZVSzcslc_BOJZZbOhHVlZ6sBow,1207 +espefuse/__init__.py,sha256=kMxrmuXu3WYHBg9F3erug26z5oLuIokriGI14MwDPXA,9844 +espefuse/__main__.py,sha256=t0UBMkH6umJmTMwqx--Wd578Pth_cjMdSVs5s7kFw2w,184 +espefuse/__pycache__/__init__.cpython-312.pyc,, +espefuse/__pycache__/__main__.cpython-312.pyc,, +espefuse/efuse/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +espefuse/efuse/__pycache__/__init__.cpython-312.pyc,, +espefuse/efuse/__pycache__/base_fields.cpython-312.pyc,, +espefuse/efuse/__pycache__/base_operations.cpython-312.pyc,, +espefuse/efuse/__pycache__/emulate_efuse_controller_base.cpython-312.pyc,, +espefuse/efuse/__pycache__/mem_definition_base.cpython-312.pyc,, +espefuse/efuse/__pycache__/util.cpython-312.pyc,, +espefuse/efuse/base_fields.py,sha256=CtDSzuWPkrjqcwm4HOS32JKNvCUz_XwR8yfu0GUJ1oc,30777 +espefuse/efuse/base_operations.py,sha256=jxH-bYaPhI0LP2qstfmn0Iv0dQEzbUkUAdi434rtko8,27177 +espefuse/efuse/emulate_efuse_controller_base.py,sha256=3uw2_yfgdlQEKdbrPehSsml9N0knvd1h0OuLqp4aey4,8630 +espefuse/efuse/esp32/__init__.py,sha256=lKWQYRS5r0GFNZ8lkXNvHRQ1F9_07OaR8dS1_tCdDzc,116 +espefuse/efuse/esp32/__pycache__/__init__.cpython-312.pyc,, +espefuse/efuse/esp32/__pycache__/emulate_efuse_controller.cpython-312.pyc,, +espefuse/efuse/esp32/__pycache__/fields.cpython-312.pyc,, +espefuse/efuse/esp32/__pycache__/mem_definition.cpython-312.pyc,, +espefuse/efuse/esp32/__pycache__/operations.cpython-312.pyc,, +espefuse/efuse/esp32/emulate_efuse_controller.py,sha256=gkoxMR0WoLz19qdvs_VxPNTuX2yziOF4-MU5Htqvw7I,5279 +espefuse/efuse/esp32/fields.py,sha256=NPrpaHhTj8QVsTohEakWeZzRICtDbtyHrx4RW2c2WzY,17058 +espefuse/efuse/esp32/mem_definition.py,sha256=kMurkrFHk2V645PWXQA3GSRbdJlDpnKZQX5ZOV9DIdw,6193 +espefuse/efuse/esp32/operations.py,sha256=7KdBFb-0W6hGxlMe2XdAqkg8i1LcERhPComV3hQHGIg,12519 +espefuse/efuse/esp32c2/__init__.py,sha256=lKWQYRS5r0GFNZ8lkXNvHRQ1F9_07OaR8dS1_tCdDzc,116 +espefuse/efuse/esp32c2/__pycache__/__init__.cpython-312.pyc,, +espefuse/efuse/esp32c2/__pycache__/emulate_efuse_controller.cpython-312.pyc,, +espefuse/efuse/esp32c2/__pycache__/fields.cpython-312.pyc,, +espefuse/efuse/esp32c2/__pycache__/mem_definition.cpython-312.pyc,, +espefuse/efuse/esp32c2/__pycache__/operations.cpython-312.pyc,, +espefuse/efuse/esp32c2/emulate_efuse_controller.py,sha256=E4OTu8c2fNnRxMqFNoLXDd7DCAYJb-zS9C78l6zkF1I,5047 +espefuse/efuse/esp32c2/fields.py,sha256=AnewZnPmCYQkKYAkfa5R7aYZmDnCbY6oKXdERPs47VM,14802 +espefuse/efuse/esp32c2/mem_definition.py,sha256=9XeR6KZ2Ju6YGsvCP3eKd8_UO7l5sDeckwgPO6oe_Tc,5616 +espefuse/efuse/esp32c2/operations.py,sha256=q6tdFVuu8lPiGEFCO4edeknmpuJWcS-LsBiU50KlIg0,12287 +espefuse/efuse/esp32c3/__init__.py,sha256=lKWQYRS5r0GFNZ8lkXNvHRQ1F9_07OaR8dS1_tCdDzc,116 +espefuse/efuse/esp32c3/__pycache__/__init__.cpython-312.pyc,, +espefuse/efuse/esp32c3/__pycache__/emulate_efuse_controller.cpython-312.pyc,, +espefuse/efuse/esp32c3/__pycache__/fields.cpython-312.pyc,, +espefuse/efuse/esp32c3/__pycache__/mem_definition.cpython-312.pyc,, +espefuse/efuse/esp32c3/__pycache__/operations.cpython-312.pyc,, +espefuse/efuse/esp32c3/emulate_efuse_controller.py,sha256=CiEpApXPfYnReIny0CqMH9p6C7u5UjjSl4ZFJUuE6LA,3063 +espefuse/efuse/esp32c3/fields.py,sha256=zWLf7TP4dsuLbP6DLKULzvXTKhn0HDTBwHvgBUMD0qM,18098 +espefuse/efuse/esp32c3/mem_definition.py,sha256=tAmJ312LaVR8zPGNcusgOCD0LKHH2Q0jeoUmgZNC78s,7552 +espefuse/efuse/esp32c3/operations.py,sha256=U1XorXOVhIifPZjNz6LtRg97V5xD4wiS5PUReTkq5tM,14960 +espefuse/efuse/esp32c6/__init__.py,sha256=lKWQYRS5r0GFNZ8lkXNvHRQ1F9_07OaR8dS1_tCdDzc,116 +espefuse/efuse/esp32c6/__pycache__/__init__.cpython-312.pyc,, +espefuse/efuse/esp32c6/__pycache__/emulate_efuse_controller.cpython-312.pyc,, +espefuse/efuse/esp32c6/__pycache__/fields.cpython-312.pyc,, +espefuse/efuse/esp32c6/__pycache__/mem_definition.cpython-312.pyc,, +espefuse/efuse/esp32c6/__pycache__/operations.cpython-312.pyc,, +espefuse/efuse/esp32c6/emulate_efuse_controller.py,sha256=bVRTKoqIaYM8XCtqF2WEldqInmzNv2ydxDAIy9lMQNo,3058 +espefuse/efuse/esp32c6/fields.py,sha256=ndNSRQGHP6hll07sKkAEswpG9T8RXcr75SuT8FS2I1o,18660 +espefuse/efuse/esp32c6/mem_definition.py,sha256=khwo2YpOWi3SxanVGokfR4Ecnv7-MVvTvOiiRhT5kPI,6679 +espefuse/efuse/esp32c6/operations.py,sha256=octrb-D704FJu8ushbQ7cu0TMhp7Ornar4cHhkuTjIQ,14667 +espefuse/efuse/esp32h2/__init__.py,sha256=lKWQYRS5r0GFNZ8lkXNvHRQ1F9_07OaR8dS1_tCdDzc,116 +espefuse/efuse/esp32h2/__pycache__/__init__.cpython-312.pyc,, +espefuse/efuse/esp32h2/__pycache__/emulate_efuse_controller.cpython-312.pyc,, +espefuse/efuse/esp32h2/__pycache__/fields.cpython-312.pyc,, +espefuse/efuse/esp32h2/__pycache__/mem_definition.cpython-312.pyc,, +espefuse/efuse/esp32h2/__pycache__/operations.cpython-312.pyc,, +espefuse/efuse/esp32h2/emulate_efuse_controller.py,sha256=drb2LVpe2qzlQp6_mv_YXkzUJTEhMf7Qift8nqM0D8o,3035 +espefuse/efuse/esp32h2/fields.py,sha256=V_V1dhAFhiPB6pAMtZC2_Q6O5B8TPR_7eP103fiAfzM,18856 +espefuse/efuse/esp32h2/mem_definition.py,sha256=1uJmnMeZSI81MxrXSyMgI257ucI6-FQgdtN8beGQoqA,6679 +espefuse/efuse/esp32h2/operations.py,sha256=9YlTjQ_j0XZfXCJ92HiaVOWJ3A0kGSToxdYGxmG9mww,15535 +espefuse/efuse/esp32h2beta1/__init__.py,sha256=lKWQYRS5r0GFNZ8lkXNvHRQ1F9_07OaR8dS1_tCdDzc,116 +espefuse/efuse/esp32h2beta1/__pycache__/__init__.cpython-312.pyc,, +espefuse/efuse/esp32h2beta1/__pycache__/emulate_efuse_controller.cpython-312.pyc,, +espefuse/efuse/esp32h2beta1/__pycache__/fields.cpython-312.pyc,, +espefuse/efuse/esp32h2beta1/__pycache__/mem_definition.cpython-312.pyc,, +espefuse/efuse/esp32h2beta1/__pycache__/operations.cpython-312.pyc,, +espefuse/efuse/esp32h2beta1/emulate_efuse_controller.py,sha256=csh1aeaIWjmo_IgXFkOvHm711ZuESf9_P2usetmEWSw,3070 +espefuse/efuse/esp32h2beta1/fields.py,sha256=KJDxba8Psboh6HazTrVeU7k5I1Q5lrrQQ18kYYbRY7w,18765 +espefuse/efuse/esp32h2beta1/mem_definition.py,sha256=LdU3WWe6pYgVSAENcXNNU4cq5wBdhkUcHcYckgi7ZgY,6396 +espefuse/efuse/esp32h2beta1/operations.py,sha256=6HgNdkH3xXsDZwXSHrEwgQv-3PQIjjzkJiboURR-eFo,14929 +espefuse/efuse/esp32p4/__init__.py,sha256=lKWQYRS5r0GFNZ8lkXNvHRQ1F9_07OaR8dS1_tCdDzc,116 +espefuse/efuse/esp32p4/__pycache__/__init__.cpython-312.pyc,, +espefuse/efuse/esp32p4/__pycache__/emulate_efuse_controller.cpython-312.pyc,, +espefuse/efuse/esp32p4/__pycache__/fields.cpython-312.pyc,, +espefuse/efuse/esp32p4/__pycache__/mem_definition.cpython-312.pyc,, +espefuse/efuse/esp32p4/__pycache__/operations.cpython-312.pyc,, +espefuse/efuse/esp32p4/emulate_efuse_controller.py,sha256=3p-Mxi4ZiK4jZqr8j6KpjCT0iovcPNV4EyBHQ5mHXDo,3058 +espefuse/efuse/esp32p4/fields.py,sha256=PmIRopzcFC5kyBk3sLvslxJSIBw76svVCMhA7UyN9hQ,18146 +espefuse/efuse/esp32p4/mem_definition.py,sha256=m0oob1WQFsjZ6qNVKKitze3ANCuPY7RbubWFG1aypd0,6679 +espefuse/efuse/esp32p4/operations.py,sha256=stR-QhzvH9QlYZbgxokgBVQC9bqyIRxYp_EK7VxEkSg,15591 +espefuse/efuse/esp32s2/__init__.py,sha256=lKWQYRS5r0GFNZ8lkXNvHRQ1F9_07OaR8dS1_tCdDzc,116 +espefuse/efuse/esp32s2/__pycache__/__init__.cpython-312.pyc,, +espefuse/efuse/esp32s2/__pycache__/emulate_efuse_controller.cpython-312.pyc,, +espefuse/efuse/esp32s2/__pycache__/fields.cpython-312.pyc,, +espefuse/efuse/esp32s2/__pycache__/mem_definition.cpython-312.pyc,, +espefuse/efuse/esp32s2/__pycache__/operations.cpython-312.pyc,, +espefuse/efuse/esp32s2/emulate_efuse_controller.py,sha256=OZu6YPkmH9IkiIo078mMgN3RMLDJBmAUoop9UQ_5Jlo,3069 +espefuse/efuse/esp32s2/fields.py,sha256=uT0vL5hQsJNLtCNgaU4_yzbZoBWCLg-dVVjuXZUbuX8,19971 +espefuse/efuse/esp32s2/mem_definition.py,sha256=PXaX8N6YjaU5k6hM5bRr6m6Iv69I6OCHLL5moCLmEog,8141 +espefuse/efuse/esp32s2/operations.py,sha256=mcnuh5e9rHSLP4k1uVMywqsQ0r3FyGJZ57MJU_-WRrw,18946 +espefuse/efuse/esp32s3/__init__.py,sha256=lKWQYRS5r0GFNZ8lkXNvHRQ1F9_07OaR8dS1_tCdDzc,116 +espefuse/efuse/esp32s3/__pycache__/__init__.cpython-312.pyc,, +espefuse/efuse/esp32s3/__pycache__/emulate_efuse_controller.cpython-312.pyc,, +espefuse/efuse/esp32s3/__pycache__/fields.cpython-312.pyc,, +espefuse/efuse/esp32s3/__pycache__/mem_definition.cpython-312.pyc,, +espefuse/efuse/esp32s3/__pycache__/operations.cpython-312.pyc,, +espefuse/efuse/esp32s3/emulate_efuse_controller.py,sha256=TH80erqiJScH43qS35kJLsMT_PTaIdkjd6eJL8Xxxs8,3063 +espefuse/efuse/esp32s3/fields.py,sha256=8qSsyLVFHVncVEdDIw37SvIaCTV7HJVnT_lCJmJgXPM,19184 +espefuse/efuse/esp32s3/mem_definition.py,sha256=QPMwcwlc8UW53aJfycDMumBaVMFiHF5m6WdWV_wn1so,6737 +espefuse/efuse/esp32s3/operations.py,sha256=vopgOUdCI6gA0xh6pnMrD95AMCGWeicRJgkjij54S5w,18946 +espefuse/efuse/esp32s3beta2/__init__.py,sha256=lKWQYRS5r0GFNZ8lkXNvHRQ1F9_07OaR8dS1_tCdDzc,116 +espefuse/efuse/esp32s3beta2/__pycache__/__init__.cpython-312.pyc,, +espefuse/efuse/esp32s3beta2/__pycache__/emulate_efuse_controller.cpython-312.pyc,, +espefuse/efuse/esp32s3beta2/__pycache__/fields.cpython-312.pyc,, +espefuse/efuse/esp32s3beta2/__pycache__/mem_definition.cpython-312.pyc,, +espefuse/efuse/esp32s3beta2/__pycache__/operations.cpython-312.pyc,, +espefuse/efuse/esp32s3beta2/emulate_efuse_controller.py,sha256=4GbLTBY10_MLzwljMIHqJXqxrdv-1A7F7cFebUBxMEo,3077 +espefuse/efuse/esp32s3beta2/fields.py,sha256=TY366CCFm7brvCbeRg604_nqmS_K9dDlsKNvzW1vhLM,19198 +espefuse/efuse/esp32s3beta2/mem_definition.py,sha256=rXS-lg_4uT4FM9zD3N3sfZsyFy2iSZQxvfS0IBQsTJk,6811 +espefuse/efuse/esp32s3beta2/operations.py,sha256=tIcIZ7cUBmnUTH-UIAEqELoa9aTGQnym95sMiFv6dZY,18953 +espefuse/efuse/mem_definition_base.py,sha256=P6gVPT8Fco4101V89JqAGHHS4vJ8fpJr2U4rBD_YrBU,5427 +espefuse/efuse/util.py,sha256=qYWtoKoht-wQ_i6d1-aU7VMpvvZHXekVb-JHzcJr5l4,1416 +espefuse/efuse_defs/esp32.yaml,sha256=8x4sAAH0XaixIC6LKZSLux_uquX-txVqKN_BZRjUnJU,18349 +espefuse/efuse_defs/esp32c2.yaml,sha256=4meipftjmn4IQdhotcsUXWKpXcv8bAHsz55zcMbmNGI,15595 +espefuse/efuse_defs/esp32c3.yaml,sha256=nVtuEA7FXKZsrEfghx2-OkS3rltIMY9cLwpFJxuq6LA,34635 +espefuse/efuse_defs/esp32c6.yaml,sha256=xaZCbw4X7Bkru1dEgUf0VA6z4PBeNZuzqdChlMIicEI,33534 +espefuse/efuse_defs/esp32h2.yaml,sha256=R0rlSdAAEdILWYtMdISdOLuH7houuj2ECncMQ6KhiLc,32571 +espefuse/efuse_defs/esp32p4.yaml,sha256=_aouN_du6dk3KRRHFPEgzKJ5Lw028X1BOj4p_uEQR9s,30226 +espefuse/efuse_defs/esp32s2.yaml,sha256=0IKh1ft-Cskif8zoNPjPGOg1EbEDqzYcMgOU27IEmXg,35820 +espefuse/efuse_defs/esp32s3.yaml,sha256=0rxq7vgINUjUI1FxVeUvc_D_EVJlgnp4or8Zc9doLOc,41844 +espsecure/__init__.py,sha256=LCFjeOztxpbuy3mNg1uPMyXJQicYsOqUHqhNDA26i3c,63283 +espsecure/__main__.py,sha256=3e7eVuC3vB42lTOvVGn4wQTb7N9fDH3bsBACd1qQnMQ,186 +espsecure/__pycache__/__init__.cpython-312.pyc,, +espsecure/__pycache__/__main__.cpython-312.pyc,, +espsecure/esp_hsm_sign/__init__.py,sha256=aZRRndolCuzJzDMGWHV3dwp5PHsZtAq3VyNFa47_Wk4,5796 +espsecure/esp_hsm_sign/__pycache__/__init__.cpython-312.pyc,, +espsecure/esp_hsm_sign/__pycache__/exceptions.cpython-312.pyc,, +espsecure/esp_hsm_sign/exceptions.py,sha256=mIG9fLpzBOJ-9MJxCT63G7Iy0G9i1mD-8btUuYxEdsU,1750 +esptool-4.7.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +esptool-4.7.0.dist-info/LICENSE,sha256=gXf5dRMhNSbfLPYYTY_5hsZ1r7UU1OaKQEAQUhuIBkM,18092 +esptool-4.7.0.dist-info/METADATA,sha256=NSvOSbuivyQ6TVYyscfGDQoeGLkGxqcco-eRA-HpRLE,2629 +esptool-4.7.0.dist-info/RECORD,, +esptool-4.7.0.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +esptool-4.7.0.dist-info/WHEEL,sha256=HiCZjzuy6Dw0hdX5R3LCFPDmFS4BWl8H-8W39XfmgX4,91 +esptool-4.7.0.dist-info/top_level.txt,sha256=e2gykZuzaB-2vJ5_XCe55aAhEDxjeeS6MvOWLu8z6JM,27 +esptool/__init__.py,sha256=7bnLNNEVHxnYqp77MjrAw-FoXo_JurGzCYFe7yP8fIg,39339 +esptool/__main__.py,sha256=Pn4uepOz1eAyehm4mvZoevYz3Rhtev6EkWtUGK6ioGs,246 +esptool/__pycache__/__init__.cpython-312.pyc,, +esptool/__pycache__/__main__.cpython-312.pyc,, +esptool/__pycache__/bin_image.cpython-312.pyc,, +esptool/__pycache__/cmds.cpython-312.pyc,, +esptool/__pycache__/config.cpython-312.pyc,, +esptool/__pycache__/loader.cpython-312.pyc,, +esptool/__pycache__/reset.cpython-312.pyc,, +esptool/__pycache__/uf2_writer.cpython-312.pyc,, +esptool/__pycache__/util.cpython-312.pyc,, +esptool/bin_image.py,sha256=XqJJvBcxYGUPW70yzX6TV5wF35teU1xU8g12d27ghQA,50005 +esptool/cmds.py,sha256=NTC3oBVHGJ32BAeHckaZqApBHL2oglgGIf7CFmrqMmI,51216 +esptool/config.py,sha256=9ETU-_Y8LTYpCfoRvBMH1J87jFklog5iYpB2RXlUAiY,3106 +esptool/loader.py,sha256=hSiyq7D-6Gi3z7ikhTSG9wKoVxbNYDvMlMXT6VI-ZcM,62705 +esptool/reset.py,sha256=ktgEnEHIORfuuFjY9OFUEaV3J1ajIub-m8CSjhUlZeo,5743 +esptool/targets/__init__.py,sha256=IUeB4WpiBxzdmV1U_fgRXwaUr2oqL9GtQ-KHSWkLyec,925 +esptool/targets/__pycache__/__init__.cpython-312.pyc,, +esptool/targets/__pycache__/esp32.cpython-312.pyc,, +esptool/targets/__pycache__/esp32c2.cpython-312.pyc,, +esptool/targets/__pycache__/esp32c3.cpython-312.pyc,, +esptool/targets/__pycache__/esp32c6.cpython-312.pyc,, +esptool/targets/__pycache__/esp32c6beta.cpython-312.pyc,, +esptool/targets/__pycache__/esp32h2.cpython-312.pyc,, +esptool/targets/__pycache__/esp32h2beta1.cpython-312.pyc,, +esptool/targets/__pycache__/esp32h2beta2.cpython-312.pyc,, +esptool/targets/__pycache__/esp32p4.cpython-312.pyc,, +esptool/targets/__pycache__/esp32s2.cpython-312.pyc,, +esptool/targets/__pycache__/esp32s3.cpython-312.pyc,, +esptool/targets/__pycache__/esp32s3beta2.cpython-312.pyc,, +esptool/targets/__pycache__/esp8266.cpython-312.pyc,, +esptool/targets/esp32.py,sha256=AmxZm-wP4EDObEDw-zls2JD9AG5RD-pt6mPZzqA4xPU,13941 +esptool/targets/esp32c2.py,sha256=_2LVt06AmwCKJ_4LSVKyZX4XAYci1ld88SnkWz1b_fs,6328 +esptool/targets/esp32c3.py,sha256=uO-DrlL5f6kGZ4kq9DP--xithrRNcCmu7fQ_IiTd2c0,9327 +esptool/targets/esp32c6.py,sha256=GWqzWFFF_Ip0RpZuMkDfSJLNh8cyA83kQSAJoVlodGA,7430 +esptool/targets/esp32c6beta.py,sha256=ZuB7FKJVrO8zaR3Ttt1hoEGpod5YWcWDVXz-oCffVnw,786 +esptool/targets/esp32h2.py,sha256=uE4z62rphVFe0jrSqba9VjgnD_BzZh6G1Zra7PV3lYI,3093 +esptool/targets/esp32h2beta1.py,sha256=XIu9YaWVjjl4uPsGrKXKYWHQiCbRlNFwjYJq2Ymx7Js,5228 +esptool/targets/esp32h2beta2.py,sha256=HL9ijfm9y_K3NryqYSgudliA3Gkb2oeKfVRgBxmxDoU,1429 +esptool/targets/esp32p4.py,sha256=IhvK62lf-RbZBmOWdlbXN6KgYFmI7LWUmZM77tZKfww,6369 +esptool/targets/esp32s2.py,sha256=O07_K-418SUnheUKStQg1pFDsXFo6CL6EgH0-y_CehE,11312 +esptool/targets/esp32s3.py,sha256=I5ABGmyIUv4j8U0WlMeAqdJOgLwsK-hKd_-j_V9gngo,13974 +esptool/targets/esp32s3beta2.py,sha256=vNccq30hZ6NuCY8tTLKiJg3GtRhKMHp-nDqlDuA6UNs,1148 +esptool/targets/esp8266.py,sha256=6-IiqBP3rmt5zLGvq8_KLsNbWGcN9vkYjpYiVaZSK-k,6141 +esptool/targets/stub_flasher/stub_flasher_32.json,sha256=imcrZH1ukDTLIj8sdW__vCNK9ijPCWebdAJ7xHxP9eQ,5068 +esptool/targets/stub_flasher/stub_flasher_32c2.json,sha256=VPH7ug00vLA7FXEIv04yi4GtmL2e1HxIc00FtsrHACU,4872 +esptool/targets/stub_flasher/stub_flasher_32c3.json,sha256=8cmUt01eqmov_rKL9v6rECHcuq5aT6eu5zXqogRGUTY,5452 +esptool/targets/stub_flasher/stub_flasher_32c6.json,sha256=Egk3DMjaq_Bqt55jSJC_UOee0YqjFS61wDhIfkytPfI,5352 +esptool/targets/stub_flasher/stub_flasher_32c6beta.json,sha256=YFPaTjEIRDGbN6eaOQOne8L8P5bHMiWbDwJUIoJAX-I,4904 +esptool/targets/stub_flasher/stub_flasher_32h2.json,sha256=EMEWeo9Vdhz2TuEybaPqbKd61cX9aeAEgy4qJPyOG4Y,5352 +esptool/targets/stub_flasher/stub_flasher_32h2beta1.json,sha256=eD4ajLS8f_Wxpi1HIkweEa6tAdPLNCoB9MfhN1PpRBE,4904 +esptool/targets/stub_flasher/stub_flasher_32h2beta2.json,sha256=pO9jo0gF6jiU9ZVdYUx6aSTSyZLm7PZ5oaQr5B5D5lM,4904 +esptool/targets/stub_flasher/stub_flasher_32p4.json,sha256=PafcYPq6BFABnPlmVRGee6v4ZdirAUesE-atO3CBUFk,4904 +esptool/targets/stub_flasher/stub_flasher_32s2.json,sha256=8d4jL9EpjqhUhb03jh0QgQJnwt0qmhne2lmfU3fNI0I,6292 +esptool/targets/stub_flasher/stub_flasher_32s3.json,sha256=j8oVO1nlL-JJV7I4eTjpGGZZ0TEdy38qHqTHcAG_mgE,7476 +esptool/targets/stub_flasher/stub_flasher_32s3beta2.json,sha256=5WQwYx-1TJWfNQS-LKvtMiEOpHZfr95V8qnTew1xwQM,5196 +esptool/targets/stub_flasher/stub_flasher_8266.json,sha256=OztDZh_GYH3RY-m8CLjvuwSsj1cLjh0-LHhspKLzfzE,12172 +esptool/uf2_writer.py,sha256=wNGQYsrcODvohLvLwPXGKh_CGIdw93gPoJnWclzAcuM,3023 +esptool/util.py,sha256=8JPRZxPTDfCY9NmYz4s-Dop9SqS80CmEk3hjgicps6o,5731 diff --git a/code/.venv/lib/python3.12/site-packages/esptool-4.7.0.dist-info/REQUESTED b/code/.venv/lib/python3.12/site-packages/esptool-4.7.0.dist-info/REQUESTED new file mode 100644 index 0000000..e69de29 diff --git a/code/.venv/lib/python3.12/site-packages/esptool-4.7.0.dist-info/WHEEL b/code/.venv/lib/python3.12/site-packages/esptool-4.7.0.dist-info/WHEEL new file mode 100644 index 0000000..71360e0 --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/esptool-4.7.0.dist-info/WHEEL @@ -0,0 +1,5 @@ +Wheel-Version: 1.0 +Generator: setuptools (72.2.0) +Root-Is-Purelib: true +Tag: py3-none-any + diff --git a/code/.venv/lib/python3.12/site-packages/esptool-4.7.0.dist-info/top_level.txt b/code/.venv/lib/python3.12/site-packages/esptool-4.7.0.dist-info/top_level.txt new file mode 100644 index 0000000..34928a1 --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/esptool-4.7.0.dist-info/top_level.txt @@ -0,0 +1,3 @@ +espefuse +espsecure +esptool diff --git a/code/.venv/lib/python3.12/site-packages/esptool/__init__.py b/code/.venv/lib/python3.12/site-packages/esptool/__init__.py new file mode 100644 index 0000000..ffe6db7 --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/esptool/__init__.py @@ -0,0 +1,1162 @@ +# SPDX-FileCopyrightText: 2014-2022 Fredrik Ahlberg, Angus Gratton, +# Espressif Systems (Shanghai) CO LTD, other contributors as noted. +# +# SPDX-License-Identifier: GPL-2.0-or-later + +__all__ = [ + "chip_id", + "detect_chip", + "dump_mem", + "elf2image", + "erase_flash", + "erase_region", + "flash_id", + "get_security_info", + "image_info", + "load_ram", + "make_image", + "merge_bin", + "read_flash", + "read_flash_status", + "read_mac", + "read_mem", + "run", + "verify_flash", + "version", + "write_flash", + "write_flash_status", + "write_mem", +] + +__version__ = "4.7.0" + +import argparse +import inspect +import os +import shlex +import sys +import time +import traceback + +from esptool.bin_image import intel_hex_to_bin +from esptool.cmds import ( + DETECTED_FLASH_SIZES, + chip_id, + detect_chip, + detect_flash_size, + dump_mem, + elf2image, + erase_flash, + erase_region, + flash_id, + get_security_info, + image_info, + load_ram, + make_image, + merge_bin, + read_flash, + read_flash_status, + read_mac, + read_mem, + run, + verify_flash, + version, + write_flash, + write_flash_status, + write_mem, +) +from esptool.config import load_config_file +from esptool.loader import DEFAULT_CONNECT_ATTEMPTS, ESPLoader, list_ports +from esptool.targets import CHIP_DEFS, CHIP_LIST, ESP32ROM +from esptool.util import ( + FatalError, + NotImplementedInROMError, + flash_size_bytes, + strip_chip_name, +) + +import serial + + +def main(argv=None, esp=None): + """ + Main function for esptool + + argv - Optional override for default arguments parsing (that uses sys.argv), + can be a list of custom arguments as strings. Arguments and their values + need to be added as individual items to the list + e.g. "-b 115200" thus becomes ['-b', '115200']. + + esp - Optional override of the connected device previously + returned by get_default_connected_device() + """ + + external_esp = esp is not None + + parser = argparse.ArgumentParser( + description="esptool.py v%s - Espressif chips ROM Bootloader Utility" + % __version__, + prog="esptool", + ) + + parser.add_argument( + "--chip", + "-c", + help="Target chip type", + type=strip_chip_name, + choices=["auto"] + CHIP_LIST, + default=os.environ.get("ESPTOOL_CHIP", "auto"), + ) + + parser.add_argument( + "--port", + "-p", + help="Serial port device", + default=os.environ.get("ESPTOOL_PORT", None), + ) + + parser.add_argument( + "--baud", + "-b", + help="Serial port baud rate used when flashing/reading", + type=arg_auto_int, + default=os.environ.get("ESPTOOL_BAUD", ESPLoader.ESP_ROM_BAUD), + ) + + parser.add_argument( + "--before", + help="What to do before connecting to the chip", + choices=["default_reset", "usb_reset", "no_reset", "no_reset_no_sync"], + default=os.environ.get("ESPTOOL_BEFORE", "default_reset"), + ) + + parser.add_argument( + "--after", + "-a", + help="What to do after esptool.py is finished", + choices=["hard_reset", "soft_reset", "no_reset", "no_reset_stub"], + default=os.environ.get("ESPTOOL_AFTER", "hard_reset"), + ) + + parser.add_argument( + "--no-stub", + help="Disable launching the flasher stub, only talk to ROM bootloader. " + "Some features will not be available.", + action="store_true", + ) + + parser.add_argument( + "--trace", + "-t", + help="Enable trace-level output of esptool.py interactions.", + action="store_true", + ) + + parser.add_argument( + "--override-vddsdio", + help="Override ESP32 VDDSDIO internal voltage regulator (use with care)", + choices=ESP32ROM.OVERRIDE_VDDSDIO_CHOICES, + nargs="?", + ) + + parser.add_argument( + "--connect-attempts", + help=( + "Number of attempts to connect, negative or 0 for infinite. " + "Default: %d." % DEFAULT_CONNECT_ATTEMPTS + ), + type=int, + default=os.environ.get("ESPTOOL_CONNECT_ATTEMPTS", DEFAULT_CONNECT_ATTEMPTS), + ) + + subparsers = parser.add_subparsers( + dest="operation", help="Run esptool.py {command} -h for additional help" + ) + + def add_spi_connection_arg(parent): + parent.add_argument( + "--spi-connection", + "-sc", + help="Override default SPI Flash connection. " + "Value can be SPI, HSPI or a comma-separated list of 5 I/O numbers " + "to use for SPI flash (CLK,Q,D,HD,CS). Not supported with ESP8266.", + action=SpiConnectionAction, + ) + + parser_load_ram = subparsers.add_parser( + "load_ram", help="Download an image to RAM and execute" + ) + parser_load_ram.add_argument( + "filename", help="Firmware image", action=AutoHex2BinAction + ) + + parser_dump_mem = subparsers.add_parser( + "dump_mem", help="Dump arbitrary memory to disk" + ) + parser_dump_mem.add_argument("address", help="Base address", type=arg_auto_int) + parser_dump_mem.add_argument( + "size", help="Size of region to dump", type=arg_auto_int + ) + parser_dump_mem.add_argument("filename", help="Name of binary dump") + + parser_read_mem = subparsers.add_parser( + "read_mem", help="Read arbitrary memory location" + ) + parser_read_mem.add_argument("address", help="Address to read", type=arg_auto_int) + + parser_write_mem = subparsers.add_parser( + "write_mem", help="Read-modify-write to arbitrary memory location" + ) + parser_write_mem.add_argument("address", help="Address to write", type=arg_auto_int) + parser_write_mem.add_argument("value", help="Value", type=arg_auto_int) + parser_write_mem.add_argument( + "mask", + help="Mask of bits to write", + type=arg_auto_int, + nargs="?", + default="0xFFFFFFFF", + ) + + def add_spi_flash_subparsers(parent, allow_keep, auto_detect): + """Add common parser arguments for SPI flash properties""" + extra_keep_args = ["keep"] if allow_keep else [] + + if auto_detect and allow_keep: + extra_fs_message = ", detect, or keep" + flash_sizes = ["detect", "keep"] + elif auto_detect: + extra_fs_message = ", or detect" + flash_sizes = ["detect"] + elif allow_keep: + extra_fs_message = ", or keep" + flash_sizes = ["keep"] + else: + extra_fs_message = "" + flash_sizes = [] + + parent.add_argument( + "--flash_freq", + "-ff", + help="SPI Flash frequency", + choices=extra_keep_args + + [ + "80m", + "60m", + "48m", + "40m", + "30m", + "26m", + "24m", + "20m", + "16m", + "15m", + "12m", + ], + default=os.environ.get("ESPTOOL_FF", "keep" if allow_keep else None), + ) + parent.add_argument( + "--flash_mode", + "-fm", + help="SPI Flash mode", + choices=extra_keep_args + ["qio", "qout", "dio", "dout"], + default=os.environ.get("ESPTOOL_FM", "keep" if allow_keep else "qio"), + ) + parent.add_argument( + "--flash_size", + "-fs", + help="SPI Flash size in MegaBytes " + "(1MB, 2MB, 4MB, 8MB, 16MB, 32MB, 64MB, 128MB) " + "plus ESP8266-only (256KB, 512KB, 2MB-c1, 4MB-c1)" + extra_fs_message, + choices=flash_sizes + + [ + "256KB", + "512KB", + "1MB", + "2MB", + "2MB-c1", + "4MB", + "4MB-c1", + "8MB", + "16MB", + "32MB", + "64MB", + "128MB", + ], + default=os.environ.get("ESPTOOL_FS", "keep" if allow_keep else "1MB"), + ) + add_spi_connection_arg(parent) + + parser_write_flash = subparsers.add_parser( + "write_flash", help="Write a binary blob to flash" + ) + + parser_write_flash.add_argument( + "addr_filename", + metavar="
", + help="Address followed by binary filename, separated by space", + action=AddrFilenamePairAction, + ) + parser_write_flash.add_argument( + "--erase-all", + "-e", + help="Erase all regions of flash (not just write areas) before programming", + action="store_true", + ) + + add_spi_flash_subparsers(parser_write_flash, allow_keep=True, auto_detect=True) + parser_write_flash.add_argument( + "--no-progress", "-p", help="Suppress progress output", action="store_true" + ) + parser_write_flash.add_argument( + "--verify", + help="Verify just-written data on flash " + "(mostly superfluous, data is read back during flashing)", + action="store_true", + ) + parser_write_flash.add_argument( + "--encrypt", + help="Apply flash encryption when writing data " + "(required correct efuse settings)", + action="store_true", + ) + # In order to not break backward compatibility, + # our list of encrypted files to flash is a new parameter + parser_write_flash.add_argument( + "--encrypt-files", + metavar="
", + help="Files to be encrypted on the flash. " + "Address followed by binary filename, separated by space.", + action=AddrFilenamePairAction, + ) + parser_write_flash.add_argument( + "--ignore-flash-encryption-efuse-setting", + help="Ignore flash encryption efuse settings ", + action="store_true", + ) + parser_write_flash.add_argument( + "--force", + help="Force write, skip security and compatibility checks. Use with caution!", + action="store_true", + ) + + compress_args = parser_write_flash.add_mutually_exclusive_group(required=False) + compress_args.add_argument( + "--compress", + "-z", + help="Compress data in transfer (default unless --no-stub is specified)", + action="store_true", + default=None, + ) + compress_args.add_argument( + "--no-compress", + "-u", + help="Disable data compression during transfer " + "(default if --no-stub is specified)", + action="store_true", + ) + + subparsers.add_parser("run", help="Run application code in flash") + + parser_image_info = subparsers.add_parser( + "image_info", help="Dump headers from a binary file (bootloader or application)" + ) + parser_image_info.add_argument( + "filename", help="Image file to parse", action=AutoHex2BinAction + ) + parser_image_info.add_argument( + "--version", + "-v", + help="Output format version (1 - legacy, 2 - extended)", + choices=["1", "2"], + default="1", + ) + + parser_make_image = subparsers.add_parser( + "make_image", help="Create an application image from binary files" + ) + parser_make_image.add_argument("output", help="Output image file") + parser_make_image.add_argument( + "--segfile", "-f", action="append", help="Segment input file" + ) + parser_make_image.add_argument( + "--segaddr", + "-a", + action="append", + help="Segment base address", + type=arg_auto_int, + ) + parser_make_image.add_argument( + "--entrypoint", + "-e", + help="Address of entry point", + type=arg_auto_int, + default=0, + ) + + parser_elf2image = subparsers.add_parser( + "elf2image", help="Create an application image from ELF file" + ) + parser_elf2image.add_argument("input", help="Input ELF file") + parser_elf2image.add_argument( + "--output", + "-o", + help="Output filename prefix (for version 1 image), " + "or filename (for version 2 single image)", + type=str, + ) + parser_elf2image.add_argument( + "--version", + "-e", + help="Output image version", + choices=["1", "2", "3"], + default="1", + ) + parser_elf2image.add_argument( + # it kept for compatibility + # Minimum chip revision (deprecated, consider using --min-rev-full) + "--min-rev", + "-r", + help=argparse.SUPPRESS, + type=int, + choices=range(256), + metavar="{0, ... 255}", + default=0, + ) + parser_elf2image.add_argument( + "--min-rev-full", + help="Minimal chip revision (in format: major * 100 + minor)", + type=int, + choices=range(65536), + metavar="{0, ... 65535}", + default=0, + ) + parser_elf2image.add_argument( + "--max-rev-full", + help="Maximal chip revision (in format: major * 100 + minor)", + type=int, + choices=range(65536), + metavar="{0, ... 65535}", + default=65535, + ) + parser_elf2image.add_argument( + "--secure-pad", + action="store_true", + help="Pad image so once signed it will end on a 64KB boundary. " + "For Secure Boot v1 images only.", + ) + parser_elf2image.add_argument( + "--secure-pad-v2", + action="store_true", + help="Pad image to 64KB, so once signed its signature sector will" + "start at the next 64K block. For Secure Boot v2 images only.", + ) + parser_elf2image.add_argument( + "--elf-sha256-offset", + help="If set, insert SHA256 hash (32 bytes) of the input ELF file " + "at specified offset in the binary.", + type=arg_auto_int, + default=None, + ) + parser_elf2image.add_argument( + "--dont-append-digest", + dest="append_digest", + help="Don't append a SHA256 digest of the entire image after the checksum. " + "This argument is not supported and ignored for ESP8266.", + action="store_false", + default=True, + ) + parser_elf2image.add_argument( + "--use_segments", + help="If set, ELF segments will be used instead of ELF sections " + "to genereate the image.", + action="store_true", + ) + parser_elf2image.add_argument( + "--flash-mmu-page-size", + help="Change flash MMU page size.", + choices=["64KB", "32KB", "16KB", "8KB"], + ) + parser_elf2image.add_argument( + "--pad-to-size", + help="The block size with which the final binary image after padding " + "must be aligned to. Value 0xFF is used for padding, similar to erase_flash", + default=None, + ) + parser_elf2image.add_argument( + "--ram-only-header", + help="Order segments of the output so IRAM and DRAM are placed at the " + "beginning and force the main header segment number to RAM segments " + "quantity. This will make the other segments invisible to the ROM " + "loader. Use this argument with care because the ROM loader will load " + "only the RAM segments although the other segments being present in " + "the output.", + action="store_true", + default=None, + ) + + add_spi_flash_subparsers(parser_elf2image, allow_keep=False, auto_detect=False) + + subparsers.add_parser("read_mac", help="Read MAC address from OTP ROM") + + subparsers.add_parser("chip_id", help="Read Chip ID from OTP ROM") + + parser_flash_id = subparsers.add_parser( + "flash_id", help="Read SPI flash manufacturer and device ID" + ) + add_spi_connection_arg(parser_flash_id) + + parser_read_status = subparsers.add_parser( + "read_flash_status", help="Read SPI flash status register" + ) + + add_spi_connection_arg(parser_read_status) + parser_read_status.add_argument( + "--bytes", + help="Number of bytes to read (1-3)", + type=int, + choices=[1, 2, 3], + default=2, + ) + + parser_write_status = subparsers.add_parser( + "write_flash_status", help="Write SPI flash status register" + ) + + add_spi_connection_arg(parser_write_status) + parser_write_status.add_argument( + "--non-volatile", + help="Write non-volatile bits (use with caution)", + action="store_true", + ) + parser_write_status.add_argument( + "--bytes", + help="Number of status bytes to write (1-3)", + type=int, + choices=[1, 2, 3], + default=2, + ) + parser_write_status.add_argument("value", help="New value", type=arg_auto_int) + + parser_read_flash = subparsers.add_parser( + "read_flash", help="Read SPI flash content" + ) + add_spi_connection_arg(parser_read_flash) + parser_read_flash.add_argument("address", help="Start address", type=arg_auto_int) + parser_read_flash.add_argument( + "size", + help="Size of region to dump. Use `ALL` to read to the end of flash.", + type=arg_auto_size, + ) + parser_read_flash.add_argument("filename", help="Name of binary dump") + parser_read_flash.add_argument( + "--no-progress", "-p", help="Suppress progress output", action="store_true" + ) + + parser_verify_flash = subparsers.add_parser( + "verify_flash", help="Verify a binary blob against flash" + ) + parser_verify_flash.add_argument( + "addr_filename", + help="Address and binary file to verify there, separated by space", + action=AddrFilenamePairAction, + ) + parser_verify_flash.add_argument( + "--diff", "-d", help="Show differences", choices=["no", "yes"], default="no" + ) + add_spi_flash_subparsers(parser_verify_flash, allow_keep=True, auto_detect=True) + + parser_erase_flash = subparsers.add_parser( + "erase_flash", help="Perform Chip Erase on SPI flash" + ) + parser_erase_flash.add_argument( + "--force", + help="Erase flash even if security features are enabled. Use with caution!", + action="store_true", + ) + add_spi_connection_arg(parser_erase_flash) + + parser_erase_region = subparsers.add_parser( + "erase_region", help="Erase a region of the flash" + ) + parser_erase_region.add_argument( + "--force", + help="Erase region even if security features are enabled. Use with caution!", + action="store_true", + ) + add_spi_connection_arg(parser_erase_region) + parser_erase_region.add_argument( + "address", help="Start address (must be multiple of 4096)", type=arg_auto_int + ) + parser_erase_region.add_argument( + "size", + help="Size of region to erase (must be multiple of 4096). " + "Use `ALL` to erase to the end of flash.", + type=arg_auto_size, + ) + + parser_merge_bin = subparsers.add_parser( + "merge_bin", + help="Merge multiple raw binary files into a single file for later flashing", + ) + + parser_merge_bin.add_argument( + "--output", "-o", help="Output filename", type=str, required=True + ) + parser_merge_bin.add_argument( + "--format", + "-f", + help="Format of the output file", + choices=["raw", "uf2", "hex"], + default="raw", + ) + uf2_group = parser_merge_bin.add_argument_group("UF2 format") + uf2_group.add_argument( + "--chunk-size", + help="Specify the used data part of the 512 byte UF2 block. " + "A common value is 256. By default the largest possible value will be used.", + default=None, + type=arg_auto_chunk_size, + ) + uf2_group.add_argument( + "--md5-disable", + help="Disable MD5 checksum in UF2 output", + action="store_true", + ) + add_spi_flash_subparsers(parser_merge_bin, allow_keep=True, auto_detect=False) + + raw_group = parser_merge_bin.add_argument_group("RAW format") + raw_group.add_argument( + "--target-offset", + "-t", + help="Target offset where the output file will be flashed", + type=arg_auto_int, + default=0, + ) + raw_group.add_argument( + "--fill-flash-size", + help="If set, the final binary file will be padded with FF " + "bytes up to this flash size.", + choices=[ + "256KB", + "512KB", + "1MB", + "2MB", + "4MB", + "8MB", + "16MB", + "32MB", + "64MB", + "128MB", + ], + ) + parser_merge_bin.add_argument( + "addr_filename", + metavar="
", + help="Address followed by binary filename, separated by space", + action=AddrFilenamePairAction, + ) + + subparsers.add_parser("get_security_info", help="Get some security-related data") + + subparsers.add_parser("version", help="Print esptool version") + + # internal sanity check - every operation matches a module function of the same name + for operation in subparsers.choices.keys(): + assert operation in globals(), "%s should be a module function" % operation + + argv = expand_file_arguments(argv or sys.argv[1:]) + + args = parser.parse_args(argv) + print("esptool.py v%s" % __version__) + load_config_file(verbose=True) + + # operation function can take 1 arg (args), 2 args (esp, arg) + # or be a member function of the ESPLoader class. + + if args.operation is None: + parser.print_help() + sys.exit(1) + + # Forbid the usage of both --encrypt, which means encrypt all the given files, + # and --encrypt-files, which represents the list of files to encrypt. + # The reason is that allowing both at the same time increases the chances of + # having contradictory lists (e.g. one file not available in one of list). + if ( + args.operation == "write_flash" + and args.encrypt + and args.encrypt_files is not None + ): + raise FatalError( + "Options --encrypt and --encrypt-files " + "must not be specified at the same time." + ) + + operation_func = globals()[args.operation] + operation_args = inspect.getfullargspec(operation_func).args + + if ( + operation_args[0] == "esp" + ): # operation function takes an ESPLoader connection object + if args.before != "no_reset_no_sync": + initial_baud = min( + ESPLoader.ESP_ROM_BAUD, args.baud + ) # don't sync faster than the default baud rate + else: + initial_baud = args.baud + + if args.port is None: + ser_list = get_port_list() + print("Found %d serial ports" % len(ser_list)) + else: + ser_list = [args.port] + esp = esp or get_default_connected_device( + ser_list, + port=args.port, + connect_attempts=args.connect_attempts, + initial_baud=initial_baud, + chip=args.chip, + trace=args.trace, + before=args.before, + ) + + if esp is None: + raise FatalError( + "Could not connect to an Espressif device " + "on any of the %d available serial ports." % len(ser_list) + ) + + if esp.secure_download_mode: + print("Chip is %s in Secure Download Mode" % esp.CHIP_NAME) + else: + print("Chip is %s" % (esp.get_chip_description())) + print("Features: %s" % ", ".join(esp.get_chip_features())) + print("Crystal is %dMHz" % esp.get_crystal_freq()) + read_mac(esp, args) + + if not args.no_stub: + if esp.secure_download_mode: + print( + "WARNING: Stub loader is not supported in Secure Download Mode, " + "setting --no-stub" + ) + args.no_stub = True + elif not esp.IS_STUB and esp.stub_is_disabled: + print( + "WARNING: Stub loader has been disabled for compatibility, " + "setting --no-stub" + ) + args.no_stub = True + else: + try: + esp = esp.run_stub() + except Exception: + # The CH9102 bridge (PID: 0x55D4) can have issues on MacOS + if sys.platform == "darwin" and esp._get_pid() == 0x55D4: + print( + "\nNote: If issues persist, " + "try installing the WCH USB-to-Serial MacOS driver." + ) + raise + + if args.override_vddsdio: + esp.override_vddsdio(args.override_vddsdio) + + if args.baud > initial_baud: + try: + esp.change_baud(args.baud) + except NotImplementedInROMError: + print( + "WARNING: ROM doesn't support changing baud rate. " + "Keeping initial baud rate %d" % initial_baud + ) + + # Override the common SPI flash parameter stuff if configured to do so + if hasattr(args, "spi_connection") and args.spi_connection is not None: + spi_config = args.spi_connection + if args.spi_connection == "SPI": + value = 0 + elif args.spi_connection == "HSPI": + value = 1 + else: + esp.check_spi_connection(args.spi_connection) + # Encode the pin numbers as a 32-bit integer with packed 6-bit values, + # the same way the ESP ROM takes them + clk, q, d, hd, cs = args.spi_connection + spi_config = f"CLK:{clk}, Q:{q}, D:{d}, HD:{hd}, CS:{cs}" + value = (hd << 24) | (cs << 18) | (d << 12) | (q << 6) | clk + print(f"Configuring SPI flash mode ({spi_config})...") + esp.flash_spi_attach(value) + elif args.no_stub: + print("Enabling default SPI flash mode...") + # ROM loader doesn't enable flash unless we explicitly do it + esp.flash_spi_attach(0) + + # XMC chip startup sequence + XMC_VENDOR_ID = 0x20 + + def is_xmc_chip_strict(): + id = esp.flash_id() + rdid = ((id & 0xFF) << 16) | ((id >> 16) & 0xFF) | (id & 0xFF00) + + vendor_id = (rdid >> 16) & 0xFF + mfid = (rdid >> 8) & 0xFF + cpid = rdid & 0xFF + + if vendor_id != XMC_VENDOR_ID: + return False + + matched = False + if mfid == 0x40: + if cpid >= 0x13 and cpid <= 0x20: + matched = True + elif mfid == 0x41: + if cpid >= 0x17 and cpid <= 0x20: + matched = True + elif mfid == 0x50: + if cpid >= 0x15 and cpid <= 0x16: + matched = True + return matched + + def flash_xmc_startup(): + # If the RDID value is a valid XMC one, may skip the flow + fast_check = True + if fast_check and is_xmc_chip_strict(): + return # Successful XMC flash chip boot-up detected by RDID, skipping. + + sfdp_mfid_addr = 0x10 + mf_id = esp.read_spiflash_sfdp(sfdp_mfid_addr, 8) + if mf_id != XMC_VENDOR_ID: # Non-XMC chip detected by SFDP Read, skipping. + return + + print( + "WARNING: XMC flash chip boot-up failure detected! " + "Running XMC25QHxxC startup flow" + ) + esp.run_spiflash_command(0xB9) # Enter DPD + esp.run_spiflash_command(0x79) # Enter UDPD + esp.run_spiflash_command(0xFF) # Exit UDPD + time.sleep(0.002) # Delay tXUDPD + esp.run_spiflash_command(0xAB) # Release Power-Down + time.sleep(0.00002) + # Check for success + if not is_xmc_chip_strict(): + print("WARNING: XMC flash boot-up fix failed.") + print("XMC flash chip boot-up fix successful!") + + # Check flash chip connection + if not esp.secure_download_mode: + try: + flash_id = esp.flash_id() + if flash_id in (0xFFFFFF, 0x000000): + print( + "WARNING: Failed to communicate with the flash chip, " + "read/write operations will fail. " + "Try checking the chip connections or removing " + "any other hardware connected to IOs." + ) + if ( + hasattr(args, "spi_connection") + and args.spi_connection is not None + ): + print( + "Some GPIO pins might be used by other peripherals, " + "try using another --spi-connection combination." + ) + + except FatalError as e: + raise FatalError(f"Unable to verify flash chip connection ({e}).") + + # Check if XMC SPI flash chip booted-up successfully, fix if not + if not esp.secure_download_mode: + try: + flash_xmc_startup() + except FatalError as e: + esp.trace(f"Unable to perform XMC flash chip startup sequence ({e}).") + + if hasattr(args, "flash_size"): + print("Configuring flash size...") + if args.flash_size == "detect": + flash_size = detect_flash_size(esp, args) + elif args.flash_size == "keep": + flash_size = detect_flash_size(esp, args=None) + else: + flash_size = args.flash_size + + if flash_size is not None: # Secure download mode + esp.flash_set_parameters(flash_size_bytes(flash_size)) + # Check if stub supports chosen flash size + if ( + esp.IS_STUB + and esp.CHIP_NAME != "ESP32-S3" + and flash_size_bytes(flash_size) > 16 * 1024 * 1024 + ): + print( + "WARNING: Flasher stub doesn't fully support flash size larger " + "than 16MB, in case of failure use --no-stub." + ) + + if getattr(args, "size", "") == "all": + if esp.secure_download_mode: + raise FatalError( + "Detecting flash size is not supported in secure download mode. " + "Set an exact size value." + ) + # detect flash size + flash_id = esp.flash_id() + size_id = flash_id >> 16 + size_str = DETECTED_FLASH_SIZES.get(size_id) + if size_str is None: + raise FatalError( + "Detecting flash size failed. Set an exact size value." + ) + print(f"Detected flash size: {size_str}") + args.size = flash_size_bytes(size_str) + + if esp.IS_STUB and hasattr(args, "address") and hasattr(args, "size"): + if esp.CHIP_NAME != "ESP32-S3" and args.address + args.size > 0x1000000: + print( + "WARNING: Flasher stub doesn't fully support flash size larger " + "than 16MB, in case of failure use --no-stub." + ) + + try: + operation_func(esp, args) + finally: + try: # Clean up AddrFilenamePairAction files + for address, argfile in args.addr_filename: + argfile.close() + except AttributeError: + pass + + # Handle post-operation behaviour (reset or other) + if operation_func == load_ram: + # the ESP is now running the loaded image, so let it run + print("Exiting immediately.") + elif args.after == "hard_reset": + esp.hard_reset() + elif args.after == "soft_reset": + print("Soft resetting...") + # flash_finish will trigger a soft reset + esp.soft_reset(False) + elif args.after == "no_reset_stub": + print("Staying in flasher stub.") + else: # args.after == 'no_reset' + print("Staying in bootloader.") + if esp.IS_STUB: + esp.soft_reset(True) # exit stub back to ROM loader + + if not external_esp: + esp._port.close() + + else: + operation_func(args) + + +def arg_auto_int(x): + return int(x, 0) + + +def arg_auto_size(x): + x = x.lower() + return x if x == "all" else arg_auto_int(x) + + +def arg_auto_chunk_size(string: str) -> int: + num = int(string, 0) + if num & 3 != 0: + raise argparse.ArgumentTypeError("Chunk size should be a 4-byte aligned number") + return num + + +def get_port_list(): + if list_ports is None: + raise FatalError( + "Listing all serial ports is currently not available. " + "Please try to specify the port when running esptool.py or update " + "the pyserial package to the latest version" + ) + return sorted(ports.device for ports in list_ports.comports()) + + +def expand_file_arguments(argv): + """ + Any argument starting with "@" gets replaced with all values read from a text file. + Text file arguments can be split by newline or by space. + Values are added "as-is", as if they were specified in this order + on the command line. + """ + new_args = [] + expanded = False + for arg in argv: + if arg.startswith("@"): + expanded = True + with open(arg[1:], "r") as f: + for line in f.readlines(): + new_args += shlex.split(line) + else: + new_args.append(arg) + if expanded: + print(f"esptool.py {' '.join(new_args)}") + return new_args + return argv + + +def get_default_connected_device( + serial_list, + port, + connect_attempts, + initial_baud, + chip="auto", + trace=False, + before="default_reset", +): + _esp = None + for each_port in reversed(serial_list): + print("Serial port %s" % each_port) + try: + if chip == "auto": + _esp = detect_chip( + each_port, initial_baud, before, trace, connect_attempts + ) + else: + chip_class = CHIP_DEFS[chip] + _esp = chip_class(each_port, initial_baud, trace) + _esp.connect(before, connect_attempts) + break + except (FatalError, OSError) as err: + if port is not None: + raise + print("%s failed to connect: %s" % (each_port, err)) + if _esp and _esp._port: + _esp._port.close() + _esp = None + return _esp + + +class SpiConnectionAction(argparse.Action): + """ + Custom action to parse 'spi connection' override. + Values are SPI, HSPI, or a sequence of 5 pin numbers separated by commas. + """ + + def __call__(self, parser, namespace, value, option_string=None): + if value.upper() in ["SPI", "HSPI"]: + values = value.upper() + elif "," in value: + values = value.split(",") + if len(values) != 5: + raise argparse.ArgumentError( + self, + f"{value} is not a valid list of comma-separate pin numbers. " + "Must be 5 numbers - CLK,Q,D,HD,CS.", + ) + try: + values = tuple(int(v, 0) for v in values) + except ValueError: + raise argparse.ArgumentError( + self, + f"{values} is not a valid argument. " + "All pins must be numeric values", + ) + else: + raise argparse.ArgumentError( + self, + f"{value} is not a valid spi-connection value. " + "Values are SPI, HSPI, or a sequence of 5 pin numbers - CLK,Q,D,HD,CS.", + ) + setattr(namespace, self.dest, values) + + +class AutoHex2BinAction(argparse.Action): + """Custom parser class for auto conversion of input files from hex to bin""" + + def __call__(self, parser, namespace, value, option_string=None): + try: + with open(value, "rb") as f: + # if hex file was detected replace hex file with converted temp bin + # otherwise keep the original file + value = intel_hex_to_bin(f).name + except IOError as e: + raise argparse.ArgumentError(self, e) + setattr(namespace, self.dest, value) + + +class AddrFilenamePairAction(argparse.Action): + """Custom parser class for the address/filename pairs passed as arguments""" + + def __init__(self, option_strings, dest, nargs="+", **kwargs): + super(AddrFilenamePairAction, self).__init__( + option_strings, dest, nargs, **kwargs + ) + + def __call__(self, parser, namespace, values, option_string=None): + # validate pair arguments + pairs = [] + for i in range(0, len(values), 2): + try: + address = int(values[i], 0) + except ValueError: + raise argparse.ArgumentError( + self, 'Address "%s" must be a number' % values[i] + ) + try: + argfile = open(values[i + 1], "rb") + except IOError as e: + raise argparse.ArgumentError(self, e) + except IndexError: + raise argparse.ArgumentError( + self, + "Must be pairs of an address " + "and the binary filename to write there", + ) + # check for intel hex files and convert them to bin + argfile = intel_hex_to_bin(argfile, address) + pairs.append((address, argfile)) + + # Sort the addresses and check for overlapping + end = 0 + for address, argfile in sorted(pairs, key=lambda x: x[0]): + argfile.seek(0, 2) # seek to end + size = argfile.tell() + argfile.seek(0) + sector_start = address & ~(ESPLoader.FLASH_SECTOR_SIZE - 1) + sector_end = ( + (address + size + ESPLoader.FLASH_SECTOR_SIZE - 1) + & ~(ESPLoader.FLASH_SECTOR_SIZE - 1) + ) - 1 + if sector_start < end: + message = "Detected overlap at address: 0x%x for file: %s" % ( + address, + argfile.name, + ) + raise argparse.ArgumentError(self, message) + end = sector_end + setattr(namespace, self.dest, pairs) + + +def _main(): + try: + main() + except FatalError as e: + print(f"\nA fatal error occurred: {e}") + sys.exit(2) + except serial.serialutil.SerialException as e: + print(f"\nA serial exception error occurred: {e}") + print( + "Note: This error originates from pySerial. " + "It is likely not a problem with esptool, " + "but with the hardware connection or drivers." + ) + print( + "For troubleshooting steps visit: " + "https://docs.espressif.com/projects/esptool/en/latest/troubleshooting.html" + ) + sys.exit(1) + except StopIteration: + print(traceback.format_exc()) + print("A fatal error occurred: The chip stopped responding.") + sys.exit(2) + + +if __name__ == "__main__": + _main() diff --git a/code/.venv/lib/python3.12/site-packages/esptool/__main__.py b/code/.venv/lib/python3.12/site-packages/esptool/__main__.py new file mode 100644 index 0000000..11e3bce --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/esptool/__main__.py @@ -0,0 +1,9 @@ +# SPDX-FileCopyrightText: 2014-2022 Fredrik Ahlberg, Angus Gratton, +# Espressif Systems (Shanghai) CO LTD, other contributors as noted. +# +# SPDX-License-Identifier: GPL-2.0-or-later + +import esptool + +if __name__ == "__main__": + esptool._main() diff --git a/code/.venv/lib/python3.12/site-packages/esptool/__pycache__/__init__.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/esptool/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..6546686 Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/esptool/__pycache__/__init__.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/esptool/__pycache__/__main__.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/esptool/__pycache__/__main__.cpython-312.pyc new file mode 100644 index 0000000..59ba41e Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/esptool/__pycache__/__main__.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/esptool/__pycache__/bin_image.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/esptool/__pycache__/bin_image.cpython-312.pyc new file mode 100644 index 0000000..f6c1a10 Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/esptool/__pycache__/bin_image.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/esptool/__pycache__/cmds.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/esptool/__pycache__/cmds.cpython-312.pyc new file mode 100644 index 0000000..b894273 Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/esptool/__pycache__/cmds.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/esptool/__pycache__/config.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/esptool/__pycache__/config.cpython-312.pyc new file mode 100644 index 0000000..b6d805c Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/esptool/__pycache__/config.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/esptool/__pycache__/loader.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/esptool/__pycache__/loader.cpython-312.pyc new file mode 100644 index 0000000..6b329c9 Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/esptool/__pycache__/loader.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/esptool/__pycache__/reset.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/esptool/__pycache__/reset.cpython-312.pyc new file mode 100644 index 0000000..1b290ef Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/esptool/__pycache__/reset.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/esptool/__pycache__/uf2_writer.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/esptool/__pycache__/uf2_writer.cpython-312.pyc new file mode 100644 index 0000000..ed22752 Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/esptool/__pycache__/uf2_writer.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/esptool/__pycache__/util.cpython-312.pyc b/code/.venv/lib/python3.12/site-packages/esptool/__pycache__/util.cpython-312.pyc new file mode 100644 index 0000000..3675825 Binary files /dev/null and b/code/.venv/lib/python3.12/site-packages/esptool/__pycache__/util.cpython-312.pyc differ diff --git a/code/.venv/lib/python3.12/site-packages/esptool/bin_image.py b/code/.venv/lib/python3.12/site-packages/esptool/bin_image.py new file mode 100644 index 0000000..55ca635 --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/esptool/bin_image.py @@ -0,0 +1,1317 @@ +# SPDX-FileCopyrightText: 2014-2022 Fredrik Ahlberg, Angus Gratton, +# Espressif Systems (Shanghai) CO LTD, other contributors as noted. +# +# SPDX-License-Identifier: GPL-2.0-or-later + +import binascii +import copy +import hashlib +import io +import os +import re +import struct +import tempfile +from typing import BinaryIO, Optional + +from intelhex import IntelHex + +from .loader import ESPLoader +from .targets import ( + ESP32C2ROM, + ESP32C3ROM, + ESP32C6BETAROM, + ESP32C6ROM, + ESP32H2BETA1ROM, + ESP32H2BETA2ROM, + ESP32H2ROM, + ESP32P4ROM, + ESP32ROM, + ESP32S2ROM, + ESP32S3BETA2ROM, + ESP32S3ROM, + ESP8266ROM, +) +from .util import FatalError, byte, pad_to + + +def align_file_position(f, size): + """Align the position in the file to the next block of specified size""" + align = (size - 1) - (f.tell() % size) + f.seek(align, 1) + + +def intel_hex_to_bin(file: BinaryIO, start_addr: Optional[int] = None) -> BinaryIO: + """Convert IntelHex file to temp binary file with padding from start_addr + If hex file was detected return temp bin file object; input file otherwise""" + INTEL_HEX_MAGIC = b":" + magic = file.read(1) + file.seek(0) + if magic == INTEL_HEX_MAGIC: + ih = IntelHex() + ih.loadhex(file.name) + file.close() + bin = tempfile.NamedTemporaryFile(suffix=".bin", delete=False) + ih.tobinfile(bin, start=start_addr) + return bin + else: + return file + + +def LoadFirmwareImage(chip, image_file): + """ + Load a firmware image. Can be for any supported SoC. + + ESP8266 images will be examined to determine if they are original ROM firmware + images (ESP8266ROMFirmwareImage) or "v2" OTA bootloader images. + + Returns a BaseFirmwareImage subclass, either ESP8266ROMFirmwareImage (v1) + or ESP8266V2FirmwareImage (v2). + """ + + def select_image_class(f, chip): + chip = re.sub(r"[-()]", "", chip.lower()) + if chip != "esp8266": + return { + "esp32": ESP32FirmwareImage, + "esp32s2": ESP32S2FirmwareImage, + "esp32s3beta2": ESP32S3BETA2FirmwareImage, + "esp32s3": ESP32S3FirmwareImage, + "esp32c3": ESP32C3FirmwareImage, + "esp32c6beta": ESP32C6BETAFirmwareImage, + "esp32h2beta1": ESP32H2BETA1FirmwareImage, + "esp32h2beta2": ESP32H2BETA2FirmwareImage, + "esp32c2": ESP32C2FirmwareImage, + "esp32c6": ESP32C6FirmwareImage, + "esp32h2": ESP32H2FirmwareImage, + "esp32p4": ESP32P4FirmwareImage, + }[chip](f) + else: # Otherwise, ESP8266 so look at magic to determine the image type + magic = ord(f.read(1)) + f.seek(0) + if magic == ESPLoader.ESP_IMAGE_MAGIC: + return ESP8266ROMFirmwareImage(f) + elif magic == ESP8266V2FirmwareImage.IMAGE_V2_MAGIC: + return ESP8266V2FirmwareImage(f) + else: + raise FatalError("Invalid image magic number: %d" % magic) + + if isinstance(image_file, str): + with open(image_file, "rb") as f: + return select_image_class(f, chip) + return select_image_class(image_file, chip) + + +class ImageSegment(object): + """Wrapper class for a segment in an ESP image + (very similar to a section in an ELFImage also)""" + + def __init__(self, addr, data, file_offs=None): + self.addr = addr + self.data = data + self.file_offs = file_offs + self.include_in_checksum = True + if self.addr != 0: + self.pad_to_alignment( + 4 + ) # pad all "real" ImageSegments 4 byte aligned length + + def copy_with_new_addr(self, new_addr): + """Return a new ImageSegment with same data, but mapped at + a new address.""" + return ImageSegment(new_addr, self.data, 0) + + def split_image(self, split_len): + """Return a new ImageSegment which splits "split_len" bytes + from the beginning of the data. Remaining bytes are kept in + this segment object (and the start address is adjusted to match.)""" + result = copy.copy(self) + result.data = self.data[:split_len] + self.data = self.data[split_len:] + self.addr += split_len + self.file_offs = None + result.file_offs = None + return result + + def __repr__(self): + r = "len 0x%05x load 0x%08x" % (len(self.data), self.addr) + if self.file_offs is not None: + r += " file_offs 0x%08x" % (self.file_offs) + return r + + def get_memory_type(self, image): + """ + Return a list describing the memory type(s) that is covered by this + segment's start address. + """ + return [ + map_range[2] + for map_range in image.ROM_LOADER.MEMORY_MAP + if map_range[0] <= self.addr < map_range[1] + ] + + def pad_to_alignment(self, alignment): + self.data = pad_to(self.data, alignment, b"\x00") + + +class ELFSection(ImageSegment): + """Wrapper class for a section in an ELF image, has a section + name as well as the common properties of an ImageSegment.""" + + def __init__(self, name, addr, data): + super(ELFSection, self).__init__(addr, data) + self.name = name.decode("utf-8") + + def __repr__(self): + return "%s %s" % (self.name, super(ELFSection, self).__repr__()) + + +class BaseFirmwareImage(object): + SEG_HEADER_LEN = 8 + SHA256_DIGEST_LEN = 32 + + """ Base class with common firmware image functions """ + + def __init__(self): + self.segments = [] + self.entrypoint = 0 + self.elf_sha256 = None + self.elf_sha256_offset = 0 + self.pad_to_size = 0 + + def load_common_header(self, load_file, expected_magic): + ( + magic, + segments, + self.flash_mode, + self.flash_size_freq, + self.entrypoint, + ) = struct.unpack(" 16: + raise FatalError( + "Invalid segment count %d (max 16). " + "Usually this indicates a linker script problem." % len(self.segments) + ) + + def load_segment(self, f, is_irom_segment=False): + """Load the next segment from the image file""" + file_offs = f.tell() + (offset, size) = struct.unpack(" 0x40200000 or offset < 0x3FFE0000 or size > 65536: + print("WARNING: Suspicious segment 0x%x, length %d" % (offset, size)) + + def maybe_patch_segment_data(self, f, segment_data): + """ + If SHA256 digest of the ELF file needs to be inserted into this segment, do so. + Returns segment data. + """ + segment_len = len(segment_data) + file_pos = f.tell() # file_pos is position in the .bin file + if ( + self.elf_sha256_offset >= file_pos + and self.elf_sha256_offset < file_pos + segment_len + ): + # SHA256 digest needs to be patched into this binary segment, + # calculate offset of the digest inside the binary segment. + patch_offset = self.elf_sha256_offset - file_pos + # Sanity checks + if ( + patch_offset < self.SEG_HEADER_LEN + or patch_offset + self.SHA256_DIGEST_LEN > segment_len + ): + raise FatalError( + "Cannot place SHA256 digest on segment boundary" + "(elf_sha256_offset=%d, file_pos=%d, segment_size=%d)" + % (self.elf_sha256_offset, file_pos, segment_len) + ) + # offset relative to the data part + patch_offset -= self.SEG_HEADER_LEN + if ( + segment_data[patch_offset : patch_offset + self.SHA256_DIGEST_LEN] + != b"\x00" * self.SHA256_DIGEST_LEN + ): + raise FatalError( + "Contents of segment at SHA256 digest offset 0x%x are not all zero." + " Refusing to overwrite." % self.elf_sha256_offset + ) + assert len(self.elf_sha256) == self.SHA256_DIGEST_LEN + segment_data = ( + segment_data[0:patch_offset] + + self.elf_sha256 + + segment_data[patch_offset + self.SHA256_DIGEST_LEN :] + ) + return segment_data + + def save_segment(self, f, segment, checksum=None): + """ + Save the next segment to the image file, + return next checksum value if provided + """ + segment_data = self.maybe_patch_segment_data(f, segment.data) + f.write(struct.pack(" 0: + if len(irom_segments) != 1: + raise FatalError( + "Found %d segments that could be irom0. Bad ELF file?" + % len(irom_segments) + ) + return irom_segments[0] + return None + + def get_non_irom_segments(self): + irom_segment = self.get_irom_segment() + return [s for s in self.segments if s != irom_segment] + + def merge_adjacent_segments(self): + if not self.segments: + return # nothing to merge + + segments = [] + # The easiest way to merge the sections is the browse them backward. + for i in range(len(self.segments) - 1, 0, -1): + # elem is the previous section, the one `next_elem` may need to be + # merged in + elem = self.segments[i - 1] + next_elem = self.segments[i] + if all( + ( + elem.get_memory_type(self) == next_elem.get_memory_type(self), + elem.include_in_checksum == next_elem.include_in_checksum, + next_elem.addr == elem.addr + len(elem.data), + ) + ): + # Merge any segment that ends where the next one starts, + # without spanning memory types + # + # (don't 'pad' any gaps here as they may be excluded from the image + # due to 'noinit' or other reasons.) + elem.data += next_elem.data + else: + # The section next_elem cannot be merged into the previous one, + # which means it needs to be part of the final segments. + # As we are browsing the list backward, the elements need to be + # inserted at the beginning of the final list. + segments.insert(0, next_elem) + + # The first segment will always be here as it cannot be merged into any + # "previous" section. + segments.insert(0, self.segments[0]) + + # note: we could sort segments here as well, but the ordering of segments is + # sometimes important for other reasons (like embedded ELF SHA-256), + # so we assume that the linker script will have produced any adjacent sections + # in linear order in the ELF, anyhow. + self.segments = segments + + def set_mmu_page_size(self, size): + """ + If supported, this should be overridden by the chip-specific class. + Gets called in elf2image. + """ + print( + "WARNING: Changing MMU page size is not supported on {}! " + "Defaulting to 64KB.".format(self.ROM_LOADER.CHIP_NAME) + ) + + +class ESP8266ROMFirmwareImage(BaseFirmwareImage): + """'Version 1' firmware image, segments loaded directly by the ROM bootloader.""" + + ROM_LOADER = ESP8266ROM + + def __init__(self, load_file=None): + super(ESP8266ROMFirmwareImage, self).__init__() + self.flash_mode = 0 + self.flash_size_freq = 0 + self.version = 1 + + if load_file is not None: + segments = self.load_common_header(load_file, ESPLoader.ESP_IMAGE_MAGIC) + + for _ in range(segments): + self.load_segment(load_file) + self.checksum = self.read_checksum(load_file) + + self.verify() + + def default_output_name(self, input_file): + """Derive a default output name from the ELF name.""" + return input_file + "-" + + def save(self, basename): + """Save a set of V1 images for flashing. Parameter is a base filename.""" + # IROM data goes in its own plain binary file + irom_segment = self.get_irom_segment() + if irom_segment is not None: + with open( + "%s0x%05x.bin" + % (basename, irom_segment.addr - ESP8266ROM.IROM_MAP_START), + "wb", + ) as f: + f.write(irom_segment.data) + + # everything but IROM goes at 0x00000 in an image file + normal_segments = self.get_non_irom_segments() + with open("%s0x00000.bin" % basename, "wb") as f: + self.write_common_header(f, normal_segments) + checksum = ESPLoader.ESP_CHECKSUM_MAGIC + for segment in normal_segments: + checksum = self.save_segment(f, segment, checksum) + self.append_checksum(f, checksum) + + +ESP8266ROM.BOOTLOADER_IMAGE = ESP8266ROMFirmwareImage + + +class ESP8266V2FirmwareImage(BaseFirmwareImage): + """'Version 2' firmware image, segments loaded by software bootloader stub + (ie Espressif bootloader or rboot) + """ + + ROM_LOADER = ESP8266ROM + # First byte of the "v2" application image + IMAGE_V2_MAGIC = 0xEA + + # First 'segment' value in a "v2" application image, + # appears to be a constant version value? + IMAGE_V2_SEGMENT = 4 + + def __init__(self, load_file=None): + super(ESP8266V2FirmwareImage, self).__init__() + self.version = 2 + if load_file is not None: + segments = self.load_common_header(load_file, self.IMAGE_V2_MAGIC) + if segments != self.IMAGE_V2_SEGMENT: + # segment count is not really segment count here, + # but we expect to see '4' + print( + 'Warning: V2 header has unexpected "segment" count %d (usually 4)' + % segments + ) + + # irom segment comes before the second header + # + # the file is saved in the image with a zero load address + # in the header, so we need to calculate a load address + irom_segment = self.load_segment(load_file, True) + # for actual mapped addr, add ESP8266ROM.IROM_MAP_START + flashing_addr + 8 + irom_segment.addr = 0 + irom_segment.include_in_checksum = False + + first_flash_mode = self.flash_mode + first_flash_size_freq = self.flash_size_freq + first_entrypoint = self.entrypoint + # load the second header + + segments = self.load_common_header(load_file, ESPLoader.ESP_IMAGE_MAGIC) + + if first_flash_mode != self.flash_mode: + print( + "WARNING: Flash mode value in first header (0x%02x) disagrees " + "with second (0x%02x). Using second value." + % (first_flash_mode, self.flash_mode) + ) + if first_flash_size_freq != self.flash_size_freq: + print( + "WARNING: Flash size/freq value in first header (0x%02x) disagrees " + "with second (0x%02x). Using second value." + % (first_flash_size_freq, self.flash_size_freq) + ) + if first_entrypoint != self.entrypoint: + print( + "WARNING: Entrypoint address in first header (0x%08x) disagrees " + "with second header (0x%08x). Using second value." + % (first_entrypoint, self.entrypoint) + ) + + # load all the usual segments + for _ in range(segments): + self.load_segment(load_file) + self.checksum = self.read_checksum(load_file) + + self.verify() + + def default_output_name(self, input_file): + """Derive a default output name from the ELF name.""" + irom_segment = self.get_irom_segment() + if irom_segment is not None: + irom_offs = irom_segment.addr - ESP8266ROM.IROM_MAP_START + else: + irom_offs = 0 + return "%s-0x%05x.bin" % ( + os.path.splitext(input_file)[0], + irom_offs & ~(ESPLoader.FLASH_SECTOR_SIZE - 1), + ) + + def save(self, filename): + with open(filename, "wb") as f: + # Save first header for irom0 segment + f.write( + struct.pack( + b" 0: + last_addr = flash_segments[0].addr + for segment in flash_segments[1:]: + if segment.addr // self.IROM_ALIGN == last_addr // self.IROM_ALIGN: + raise FatalError( + "Segment loaded at 0x%08x lands in same 64KB flash mapping " + "as segment loaded at 0x%08x. Can't generate binary. " + "Suggest changing linker script or ELF to merge sections." + % (segment.addr, last_addr) + ) + last_addr = segment.addr + + def get_alignment_data_needed(segment): + # Actual alignment (in data bytes) required for a segment header: + # positioned so that after we write the next 8 byte header, + # file_offs % IROM_ALIGN == segment.addr % IROM_ALIGN + # + # (this is because the segment's vaddr may not be IROM_ALIGNed, + # more likely is aligned IROM_ALIGN+0x18 + # to account for the binary file header + align_past = (segment.addr % self.IROM_ALIGN) - self.SEG_HEADER_LEN + pad_len = (self.IROM_ALIGN - (f.tell() % self.IROM_ALIGN)) + align_past + if pad_len == 0 or pad_len == self.IROM_ALIGN: + return 0 # already aligned + + # subtract SEG_HEADER_LEN a second time, + # as the padding block has a header as well + pad_len -= self.SEG_HEADER_LEN + if pad_len < 0: + pad_len += self.IROM_ALIGN + return pad_len + + if self.ram_only_header: + # write RAM segments first in order to get only RAM segments quantity + # and checksum (ROM bootloader will only care for RAM segments and its + # correct checksums) + for segment in ram_segments: + checksum = self.save_segment(f, segment, checksum) + total_segments += 1 + self.append_checksum(f, checksum) + + # reversing to match the same section order from linker script + flash_segments.reverse() + for segment in flash_segments: + pad_len = get_alignment_data_needed(segment) + while pad_len > 0: + pad_segment = ImageSegment(0, b"\x00" * pad_len, f.tell()) + self.save_segment(f, pad_segment) + total_segments += 1 + pad_len = get_alignment_data_needed(segment) + # write the flash segment + assert ( + f.tell() + 8 + ) % self.IROM_ALIGN == segment.addr % self.IROM_ALIGN + # save the flash segment but not saving its checksum neither + # saving the number of flash segments, since ROM bootloader + # should "not see" them + self.save_flash_segment(f, segment) + total_segments += 1 + else: # not self.ram_only_header + # try to fit each flash segment on a 64kB aligned boundary + # by padding with parts of the non-flash segments... + while len(flash_segments) > 0: + segment = flash_segments[0] + pad_len = get_alignment_data_needed(segment) + if pad_len > 0: # need to pad + if len(ram_segments) > 0 and pad_len > self.SEG_HEADER_LEN: + pad_segment = ram_segments[0].split_image(pad_len) + if len(ram_segments[0].data) == 0: + ram_segments.pop(0) + else: + pad_segment = ImageSegment(0, b"\x00" * pad_len, f.tell()) + checksum = self.save_segment(f, pad_segment, checksum) + total_segments += 1 + else: + # write the flash segment + assert ( + f.tell() + 8 + ) % self.IROM_ALIGN == segment.addr % self.IROM_ALIGN + checksum = self.save_flash_segment(f, segment, checksum) + flash_segments.pop(0) + total_segments += 1 + + # flash segments all written, so write any remaining RAM segments + for segment in ram_segments: + checksum = self.save_segment(f, segment, checksum) + total_segments += 1 + + if self.secure_pad: + # pad the image so that after signing it will end on a a 64KB boundary. + # This ensures all mapped flash content will be verified. + if not self.append_digest: + raise FatalError( + "secure_pad only applies if a SHA-256 digest " + "is also appended to the image" + ) + align_past = (f.tell() + self.SEG_HEADER_LEN) % self.IROM_ALIGN + # 16 byte aligned checksum + # (force the alignment to simplify calculations) + checksum_space = 16 + if self.secure_pad == "1": + # after checksum: SHA-256 digest + + # (to be added by signing process) version, + # signature + 12 trailing bytes due to alignment + space_after_checksum = 32 + 4 + 64 + 12 + elif self.secure_pad == "2": # Secure Boot V2 + # after checksum: SHA-256 digest + + # signature sector, + # but we place signature sector after the 64KB boundary + space_after_checksum = 32 + pad_len = ( + self.IROM_ALIGN - align_past - checksum_space - space_after_checksum + ) % self.IROM_ALIGN + pad_segment = ImageSegment(0, b"\x00" * pad_len, f.tell()) + + checksum = self.save_segment(f, pad_segment, checksum) + total_segments += 1 + + if not self.ram_only_header: + # done writing segments + self.append_checksum(f, checksum) + image_length = f.tell() + + if self.secure_pad: + assert ((image_length + space_after_checksum) % self.IROM_ALIGN) == 0 + + # kinda hacky: go back to the initial header and write the new segment count + # that includes padding segments. This header is not checksummed + f.seek(1) + if self.ram_only_header: + # Update the header with the RAM segments quantity as it should be + # visible by the ROM bootloader + f.write(bytes([len(ram_segments)])) + else: + f.write(bytes([total_segments])) + + if self.append_digest: + # calculate the SHA256 of the whole file and append it + f.seek(0) + digest = hashlib.sha256() + digest.update(f.read(image_length)) + f.write(digest.digest()) + + if self.pad_to_size: + image_length = f.tell() + if image_length % self.pad_to_size != 0: + pad_by = self.pad_to_size - (image_length % self.pad_to_size) + f.write(b"\xff" * pad_by) + + with open(filename, "wb") as real_file: + real_file.write(f.getvalue()) + + def load_extended_header(self, load_file): + def split_byte(n): + return (n & 0x0F, (n >> 4) & 0x0F) + + fields = list( + struct.unpack(self.EXTENDED_HEADER_STRUCT_FMT, load_file.read(16)) + ) + + self.wp_pin = fields[0] + + # SPI pin drive stengths are two per byte + self.clk_drv, self.q_drv = split_byte(fields[1]) + self.d_drv, self.cs_drv = split_byte(fields[2]) + self.hd_drv, self.wp_drv = split_byte(fields[3]) + + self.chip_id = fields[4] + if self.chip_id != self.ROM_LOADER.IMAGE_CHIP_ID: + print( + ( + "Unexpected chip id in image. Expected %d but value was %d. " + "Is this image for a different chip model?" + ) + % (self.ROM_LOADER.IMAGE_CHIP_ID, self.chip_id) + ) + + self.min_rev = fields[5] + self.min_rev_full = fields[6] + self.max_rev_full = fields[7] + + append_digest = fields[-1] # last byte is append_digest + if append_digest in [0, 1]: + self.append_digest = append_digest == 1 + else: + raise RuntimeError( + "Invalid value for append_digest field (0x%02x). Should be 0 or 1.", + append_digest, + ) + + def save_extended_header(self, save_file): + def join_byte(ln, hn): + return (ln & 0x0F) + ((hn & 0x0F) << 4) + + append_digest = 1 if self.append_digest else 0 + + fields = [ + self.wp_pin, + join_byte(self.clk_drv, self.q_drv), + join_byte(self.d_drv, self.cs_drv), + join_byte(self.hd_drv, self.wp_drv), + self.ROM_LOADER.IMAGE_CHIP_ID, + self.min_rev, + self.min_rev_full, + self.max_rev_full, + ] + fields += [0] * 4 # padding + fields += [append_digest] + + packed = struct.pack(self.EXTENDED_HEADER_STRUCT_FMT, *fields) + save_file.write(packed) + + +class ESP8266V3FirmwareImage(ESP32FirmwareImage): + """ESP8266 V3 firmware image is very similar to ESP32 image""" + + EXTENDED_HEADER_STRUCT_FMT = "B" * 16 + + def is_flash_addr(self, addr): + return addr > ESP8266ROM.IROM_MAP_START + + def save(self, filename): + total_segments = 0 + with io.BytesIO() as f: # write file to memory first + self.write_common_header(f, self.segments) + + checksum = ESPLoader.ESP_CHECKSUM_MAGIC + + # split segments into flash-mapped vs ram-loaded, + # and take copies so we can mutate them + flash_segments = [ + copy.deepcopy(s) + for s in sorted(self.segments, key=lambda s: s.addr) + if self.is_flash_addr(s.addr) and len(s.data) + ] + ram_segments = [ + copy.deepcopy(s) + for s in sorted(self.segments, key=lambda s: s.addr) + if not self.is_flash_addr(s.addr) and len(s.data) + ] + + # check for multiple ELF sections that are mapped in the same + # flash mapping region. This is usually a sign of a broken linker script, + # but if you have a legitimate use case then let us know + if len(flash_segments) > 0: + last_addr = flash_segments[0].addr + for segment in flash_segments[1:]: + if segment.addr // self.IROM_ALIGN == last_addr // self.IROM_ALIGN: + raise FatalError( + "Segment loaded at 0x%08x lands in same 64KB flash mapping " + "as segment loaded at 0x%08x. Can't generate binary. " + "Suggest changing linker script or ELF to merge sections." + % (segment.addr, last_addr) + ) + last_addr = segment.addr + + # try to fit each flash segment on a 64kB aligned boundary + # by padding with parts of the non-flash segments... + while len(flash_segments) > 0: + segment = flash_segments[0] + # remove 8 bytes empty data for insert segment header + if segment.name == ".flash.rodata": + segment.data = segment.data[8:] + # write the flash segment + checksum = self.save_segment(f, segment, checksum) + flash_segments.pop(0) + total_segments += 1 + + # flash segments all written, so write any remaining RAM segments + for segment in ram_segments: + checksum = self.save_segment(f, segment, checksum) + total_segments += 1 + + # done writing segments + self.append_checksum(f, checksum) + image_length = f.tell() + + # kinda hacky: go back to the initial header and write the new segment count + # that includes padding segments. This header is not checksummed + f.seek(1) + f.write(bytes([total_segments])) + + if self.append_digest: + # calculate the SHA256 of the whole file and append it + f.seek(0) + digest = hashlib.sha256() + digest.update(f.read(image_length)) + f.write(digest.digest()) + + with open(filename, "wb") as real_file: + real_file.write(f.getvalue()) + + def load_extended_header(self, load_file): + def split_byte(n): + return (n & 0x0F, (n >> 4) & 0x0F) + + fields = list( + struct.unpack(self.EXTENDED_HEADER_STRUCT_FMT, load_file.read(16)) + ) + + self.wp_pin = fields[0] + + # SPI pin drive stengths are two per byte + self.clk_drv, self.q_drv = split_byte(fields[1]) + self.d_drv, self.cs_drv = split_byte(fields[2]) + self.hd_drv, self.wp_drv = split_byte(fields[3]) + + if fields[15] in [0, 1]: + self.append_digest = fields[15] == 1 + else: + raise RuntimeError( + "Invalid value for append_digest field (0x%02x). Should be 0 or 1.", + fields[15], + ) + + # remaining fields in the middle should all be zero + if any(f for f in fields[4:15] if f != 0): + print( + "Warning: some reserved header fields have non-zero values. " + "This image may be from a newer esptool.py?" + ) + + +ESP32ROM.BOOTLOADER_IMAGE = ESP32FirmwareImage + + +class ESP32S2FirmwareImage(ESP32FirmwareImage): + """ESP32S2 Firmware Image almost exactly the same as ESP32FirmwareImage""" + + ROM_LOADER = ESP32S2ROM + + +ESP32S2ROM.BOOTLOADER_IMAGE = ESP32S2FirmwareImage + + +class ESP32S3BETA2FirmwareImage(ESP32FirmwareImage): + """ESP32S3 Firmware Image almost exactly the same as ESP32FirmwareImage""" + + ROM_LOADER = ESP32S3BETA2ROM + + +ESP32S3BETA2ROM.BOOTLOADER_IMAGE = ESP32S3BETA2FirmwareImage + + +class ESP32S3FirmwareImage(ESP32FirmwareImage): + """ESP32S3 Firmware Image almost exactly the same as ESP32FirmwareImage""" + + ROM_LOADER = ESP32S3ROM + + +ESP32S3ROM.BOOTLOADER_IMAGE = ESP32S3FirmwareImage + + +class ESP32C3FirmwareImage(ESP32FirmwareImage): + """ESP32C3 Firmware Image almost exactly the same as ESP32FirmwareImage""" + + ROM_LOADER = ESP32C3ROM + + +ESP32C3ROM.BOOTLOADER_IMAGE = ESP32C3FirmwareImage + + +class ESP32C6BETAFirmwareImage(ESP32FirmwareImage): + """ESP32C6 Firmware Image almost exactly the same as ESP32FirmwareImage""" + + ROM_LOADER = ESP32C6BETAROM + + +ESP32C6BETAROM.BOOTLOADER_IMAGE = ESP32C6BETAFirmwareImage + + +class ESP32H2BETA1FirmwareImage(ESP32FirmwareImage): + """ESP32H2 Firmware Image almost exactly the same as ESP32FirmwareImage""" + + ROM_LOADER = ESP32H2BETA1ROM + + +ESP32H2BETA1ROM.BOOTLOADER_IMAGE = ESP32H2BETA1FirmwareImage + + +class ESP32H2BETA2FirmwareImage(ESP32FirmwareImage): + """ESP32H2 Firmware Image almost exactly the same as ESP32FirmwareImage""" + + ROM_LOADER = ESP32H2BETA2ROM + + +ESP32H2BETA2ROM.BOOTLOADER_IMAGE = ESP32H2BETA2FirmwareImage + + +class ESP32C2FirmwareImage(ESP32FirmwareImage): + """ESP32C2 Firmware Image almost exactly the same as ESP32FirmwareImage""" + + ROM_LOADER = ESP32C2ROM + + def set_mmu_page_size(self, size): + if size not in [16384, 32768, 65536]: + raise FatalError( + "{} bytes is not a valid ESP32-C2 page size, " + "select from 64KB, 32KB, 16KB.".format(size) + ) + self.IROM_ALIGN = size + + +ESP32C2ROM.BOOTLOADER_IMAGE = ESP32C2FirmwareImage + + +class ESP32C6FirmwareImage(ESP32FirmwareImage): + """ESP32C6 Firmware Image almost exactly the same as ESP32FirmwareImage""" + + ROM_LOADER = ESP32C6ROM + + def set_mmu_page_size(self, size): + if size not in [8192, 16384, 32768, 65536]: + raise FatalError( + "{} bytes is not a valid ESP32-C6 page size, " + "select from 64KB, 32KB, 16KB, 8KB.".format(size) + ) + self.IROM_ALIGN = size + + +ESP32C6ROM.BOOTLOADER_IMAGE = ESP32C6FirmwareImage + + +class ESP32P4FirmwareImage(ESP32FirmwareImage): + """ESP32P4 Firmware Image almost exactly the same as ESP32FirmwareImage""" + + ROM_LOADER = ESP32P4ROM + + +ESP32P4ROM.BOOTLOADER_IMAGE = ESP32P4FirmwareImage + + +class ESP32H2FirmwareImage(ESP32C6FirmwareImage): + """ESP32H2 Firmware Image almost exactly the same as ESP32FirmwareImage""" + + ROM_LOADER = ESP32H2ROM + + +ESP32H2ROM.BOOTLOADER_IMAGE = ESP32H2FirmwareImage + + +class ELFFile(object): + SEC_TYPE_PROGBITS = 0x01 + SEC_TYPE_STRTAB = 0x03 + SEC_TYPE_NOBITS = 0x08 # e.g. .bss section + SEC_TYPE_INITARRAY = 0x0E + SEC_TYPE_FINIARRAY = 0x0F + + PROG_SEC_TYPES = (SEC_TYPE_PROGBITS, SEC_TYPE_INITARRAY, SEC_TYPE_FINIARRAY) + + LEN_SEC_HEADER = 0x28 + + SEG_TYPE_LOAD = 0x01 + LEN_SEG_HEADER = 0x20 + + def __init__(self, name): + # Load sections from the ELF file + self.name = name + with open(self.name, "rb") as f: + self._read_elf_file(f) + + def get_section(self, section_name): + for s in self.sections: + if s.name == section_name: + return s + raise ValueError("No section %s in ELF file" % section_name) + + def _read_elf_file(self, f): + # read the ELF file header + LEN_FILE_HEADER = 0x34 + try: + ( + ident, + _type, + machine, + _version, + self.entrypoint, + _phoff, + shoff, + _flags, + _ehsize, + _phentsize, + _phnum, + shentsize, + shnum, + shstrndx, + ) = struct.unpack("<16sHHLLLLLHHHHHH", f.read(LEN_FILE_HEADER)) + except struct.error as e: + raise FatalError( + "Failed to read a valid ELF header from %s: %s" % (self.name, e) + ) + + if byte(ident, 0) != 0x7F or ident[1:4] != b"ELF": + raise FatalError("%s has invalid ELF magic header" % self.name) + if machine not in [0x5E, 0xF3]: + raise FatalError( + "%s does not appear to be an Xtensa or an RISCV ELF file. " + "e_machine=%04x" % (self.name, machine) + ) + if shentsize != self.LEN_SEC_HEADER: + raise FatalError( + "%s has unexpected section header entry size 0x%x (not 0x%x)" + % (self.name, shentsize, self.LEN_SEC_HEADER) + ) + if shnum == 0: + raise FatalError("%s has 0 section headers" % (self.name)) + self._read_sections(f, shoff, shnum, shstrndx) + self._read_segments(f, _phoff, _phnum, shstrndx) + + def _read_sections(self, f, section_header_offs, section_header_count, shstrndx): + f.seek(section_header_offs) + len_bytes = section_header_count * self.LEN_SEC_HEADER + section_header = f.read(len_bytes) + if len(section_header) == 0: + raise FatalError( + "No section header found at offset %04x in ELF file." + % section_header_offs + ) + if len(section_header) != (len_bytes): + raise FatalError( + "Only read 0x%x bytes from section header (expected 0x%x.) " + "Truncated ELF file?" % (len(section_header), len_bytes) + ) + + # walk through the section header and extract all sections + section_header_offsets = range(0, len(section_header), self.LEN_SEC_HEADER) + + def read_section_header(offs): + name_offs, sec_type, _flags, lma, sec_offs, size = struct.unpack_from( + " 0 + ] + self.sections = prog_sections + self.nobits_sections = [ + ELFSection(lookup_string(n_offs), lma, b"") + for (n_offs, _type, lma, size, offs) in nobits_secitons + if lma != 0 and size > 0 + ] + + def _read_segments(self, f, segment_header_offs, segment_header_count, shstrndx): + f.seek(segment_header_offs) + len_bytes = segment_header_count * self.LEN_SEG_HEADER + segment_header = f.read(len_bytes) + if len(segment_header) == 0: + raise FatalError( + "No segment header found at offset %04x in ELF file." + % segment_header_offs + ) + if len(segment_header) != (len_bytes): + raise FatalError( + "Only read 0x%x bytes from segment header (expected 0x%x.) " + "Truncated ELF file?" % (len(segment_header), len_bytes) + ) + + # walk through the segment header and extract all segments + segment_header_offsets = range(0, len(segment_header), self.LEN_SEG_HEADER) + + def read_segment_header(offs): + ( + seg_type, + seg_offs, + _vaddr, + lma, + size, + _memsize, + _flags, + _align, + ) = struct.unpack_from(" 0 + ] + self.segments = prog_segments + + def sha256(self): + # return SHA256 hash of the input ELF file + sha256 = hashlib.sha256() + with open(self.name, "rb") as f: + sha256.update(f.read()) + return sha256.digest() diff --git a/code/.venv/lib/python3.12/site-packages/esptool/cmds.py b/code/.venv/lib/python3.12/site-packages/esptool/cmds.py new file mode 100644 index 0000000..87abb88 --- /dev/null +++ b/code/.venv/lib/python3.12/site-packages/esptool/cmds.py @@ -0,0 +1,1357 @@ +# SPDX-FileCopyrightText: 2014-2022 Fredrik Ahlberg, Angus Gratton, +# Espressif Systems (Shanghai) CO LTD, other contributors as noted. +# +# SPDX-License-Identifier: GPL-2.0-or-later + +import hashlib +import io +import os +import struct +import sys +import time +import zlib + +from intelhex import IntelHex + +from .bin_image import ELFFile, ImageSegment, LoadFirmwareImage +from .bin_image import ( + ESP8266ROMFirmwareImage, + ESP8266V2FirmwareImage, + ESP8266V3FirmwareImage, +) +from .loader import ( + DEFAULT_CONNECT_ATTEMPTS, + DEFAULT_TIMEOUT, + ERASE_WRITE_TIMEOUT_PER_MB, + ESPLoader, + timeout_per_mb, +) +from .targets import CHIP_DEFS, CHIP_LIST, ROM_LIST +from .uf2_writer import UF2Writer +from .util import ( + FatalError, + NotImplementedInROMError, + NotSupportedError, + UnsupportedCommandError, +) +from .util import ( + div_roundup, + flash_size_bytes, + get_file_size, + hexify, + pad_to, + print_overwrite, +) + +DETECTED_FLASH_SIZES = { + 0x12: "256KB", + 0x13: "512KB", + 0x14: "1MB", + 0x15: "2MB", + 0x16: "4MB", + 0x17: "8MB", + 0x18: "16MB", + 0x19: "32MB", + 0x1A: "64MB", + 0x1B: "128MB", + 0x1C: "256MB", + 0x20: "64MB", + 0x21: "128MB", + 0x22: "256MB", + 0x32: "256KB", + 0x33: "512KB", + 0x34: "1MB", + 0x35: "2MB", + 0x36: "4MB", + 0x37: "8MB", + 0x38: "16MB", + 0x39: "32MB", + 0x3A: "64MB", +} + +FLASH_MODES = {"qio": 0, "qout": 1, "dio": 2, "dout": 3} + + +def detect_chip( + port=ESPLoader.DEFAULT_PORT, + baud=ESPLoader.ESP_ROM_BAUD, + connect_mode="default_reset", + trace_enabled=False, + connect_attempts=DEFAULT_CONNECT_ATTEMPTS, +): + """Use serial access to detect the chip type. + + First, get_security_info command is sent to detect the ID of the chip + (supported only by ESP32-C3 and later, works even in the Secure Download Mode). + If this fails, we reconnect and fall-back to reading the magic number. + It's mapped at a specific ROM address and has a different value on each chip model. + This way we use one memory read and compare it to the magic number for each chip. + + This routine automatically performs ESPLoader.connect() (passing + connect_mode parameter) as part of querying the chip. + """ + inst = None + detect_port = ESPLoader(port, baud, trace_enabled=trace_enabled) + if detect_port.serial_port.startswith("rfc2217:"): + detect_port.USES_RFC2217 = True + detect_port.connect(connect_mode, connect_attempts, detecting=True) + try: + print("Detecting chip type...", end="") + chip_id = detect_port.get_chip_id() + for cls in [ + n for n in ROM_LIST if n.CHIP_NAME not in ("ESP8266", "ESP32", "ESP32-S2") + ]: + # cmd not supported on ESP8266 and ESP32 + ESP32-S2 doesn't return chip_id + if chip_id == cls.IMAGE_CHIP_ID: + inst = cls(detect_port._port, baud, trace_enabled=trace_enabled) + try: + inst.read_reg( + ESPLoader.CHIP_DETECT_MAGIC_REG_ADDR + ) # Dummy read to check Secure Download mode + except UnsupportedCommandError: + inst.secure_download_mode = True + inst._post_connect() + break + else: + err_msg = f"Unexpected chip ID value {chip_id}." + except (UnsupportedCommandError, struct.error, FatalError) as e: + # UnsupportedCommmanddError: ESP8266/ESP32 ROM + # struct.error: ESP32-S2 + # FatalError: ESP8266/ESP32 STUB + print(" Unsupported detection protocol, switching and trying again...") + try: + # ESP32/ESP8266 are reset after an unsupported command, need to reconnect + # (not needed on ESP32-S2) + if not isinstance(e, struct.error): + detect_port.connect( + connect_mode, connect_attempts, detecting=True, warnings=False + ) + print("Detecting chip type...", end="") + sys.stdout.flush() + chip_magic_value = detect_port.read_reg( + ESPLoader.CHIP_DETECT_MAGIC_REG_ADDR + ) + + for cls in ROM_LIST: + if chip_magic_value in cls.CHIP_DETECT_MAGIC_VALUE: + inst = cls(detect_port._port, baud, trace_enabled=trace_enabled) + inst._post_connect() + inst.check_chip_id() + break + else: + err_msg = f"Unexpected chip magic value {chip_magic_value:#010x}." + except UnsupportedCommandError: + raise FatalError( + "Unsupported Command Error received. " + "Probably this means Secure Download Mode is enabled, " + "autodetection will not work. Need to manually specify the chip." + ) + finally: + if inst is not None: + print(" %s" % inst.CHIP_NAME, end="") + if detect_port.sync_stub_detected: + inst = inst.STUB_CLASS(inst) + inst.sync_stub_detected = True + print("") # end line + return inst + raise FatalError( + f"{err_msg} Failed to autodetect chip type." + "\nProbably it is unsupported by this version of esptool." + ) + + +# "Operation" commands, executable at command line. One function each +# +# Each function takes either two args (, ) or a single +# argument. + + +def load_ram(esp, args): + image = LoadFirmwareImage(esp.CHIP_NAME, args.filename) + + print("RAM boot...") + for seg in image.segments: + size = len(seg.data) + print("Downloading %d bytes at %08x..." % (size, seg.addr), end=" ") + sys.stdout.flush() + esp.mem_begin( + size, div_roundup(size, esp.ESP_RAM_BLOCK), esp.ESP_RAM_BLOCK, seg.addr + ) + + seq = 0 + while len(seg.data) > 0: + esp.mem_block(seg.data[0 : esp.ESP_RAM_BLOCK], seq) + seg.data = seg.data[esp.ESP_RAM_BLOCK :] + seq += 1 + print("done!") + + print("All segments done, executing at %08x" % image.entrypoint) + esp.mem_finish(image.entrypoint) + + +def read_mem(esp, args): + print("0x%08x = 0x%08x" % (args.address, esp.read_reg(args.address))) + + +def write_mem(esp, args): + esp.write_reg(args.address, args.value, args.mask, 0) + print("Wrote %08x, mask %08x to %08x" % (args.value, args.mask, args.address)) + + +def dump_mem(esp, args): + with open(args.filename, "wb") as f: + for i in range(args.size // 4): + d = esp.read_reg(args.address + (i * 4)) + f.write(struct.pack(b"> 16 + flash_size = DETECTED_FLASH_SIZES.get(size_id) + if args is not None and args.flash_size == "detect": + if flash_size is None: + flash_size = "4MB" + print( + "Warning: Could not auto-detect Flash size " + f"(FlashID={flash_id:#x}, SizeID={size_id:#x}), defaulting to 4MB" + ) + else: + print("Auto-detected Flash size:", flash_size) + args.flash_size = flash_size + return flash_size + + +def _update_image_flash_params(esp, address, args, image): + """ + Modify the flash mode & size bytes if this looks like an executable bootloader image + """ + if len(image) < 8: + return image # not long enough to be a bootloader image + + # unpack the (potential) image header + magic, _, flash_mode, flash_size_freq = struct.unpack("BBBB", image[:4]) + if address != esp.BOOTLOADER_FLASH_OFFSET: + return image # not flashing bootloader offset, so don't modify this + + if (args.flash_mode, args.flash_freq, args.flash_size) == ("keep",) * 3: + return image # all settings are 'keep', not modifying anything + + # easy check if this is an image: does it start with a magic byte? + if magic != esp.ESP_IMAGE_MAGIC: + print( + "Warning: Image file at 0x%x doesn't look like an image file, " + "so not changing any flash settings." % address + ) + return image + + # make sure this really is an image, and not just data that + # starts with esp.ESP_IMAGE_MAGIC (mostly a problem for encrypted + # images that happen to start with a magic byte + try: + test_image = esp.BOOTLOADER_IMAGE(io.BytesIO(image)) + test_image.verify() + except Exception: + print( + "Warning: Image file at 0x%x is not a valid %s image, " + "so not changing any flash settings." % (address, esp.CHIP_NAME) + ) + return image + + # After the 8-byte header comes the extended header for chips others than ESP8266. + # The 15th byte of the extended header indicates if the image is protected by + # a SHA256 checksum. In that case we should not modify the header because + # the checksum check would fail. + sha_implies_keep = args.chip != "esp8266" and image[8 + 15] == 1 + + def print_keep_warning(arg_to_keep, arg_used): + print( + "Warning: Image file at {addr} is protected with a hash checksum, " + "so not changing the flash {arg} setting. " + "Use the --flash_{arg}=keep option instead of --flash_{arg}={arg_orig} " + "in order to remove this warning, or use the --dont-append-digest option " + "for the elf2image command in order to generate an image file " + "without a hash checksum".format( + addr=hex(address), arg=arg_to_keep, arg_orig=arg_used + ) + ) + + if args.flash_mode != "keep": + new_flash_mode = FLASH_MODES[args.flash_mode] + if flash_mode != new_flash_mode and sha_implies_keep: + print_keep_warning("mode", args.flash_mode) + else: + flash_mode = new_flash_mode + + flash_freq = flash_size_freq & 0x0F + if args.flash_freq != "keep": + new_flash_freq = esp.parse_flash_freq_arg(args.flash_freq) + if flash_freq != new_flash_freq and sha_implies_keep: + print_keep_warning("frequency", args.flash_freq) + else: + flash_freq = new_flash_freq + + flash_size = flash_size_freq & 0xF0 + if args.flash_size != "keep": + new_flash_size = esp.parse_flash_size_arg(args.flash_size) + if flash_size != new_flash_size and sha_implies_keep: + print_keep_warning("size", args.flash_size) + else: + flash_size = new_flash_size + + flash_params = struct.pack(b"BB", flash_mode, flash_size + flash_freq) + if flash_params != image[2:4]: + print("Flash params set to 0x%04x" % struct.unpack(">H", flash_params)) + image = image[0:2] + flash_params + image[4:] + return image + + +def write_flash(esp, args): + # set args.compress based on default behaviour: + # -> if either --compress or --no-compress is set, honour that + # -> otherwise, set --compress unless --no-stub is set + if args.compress is None and not args.no_compress: + args.compress = not args.no_stub + + if not args.force and esp.CHIP_NAME != "ESP8266" and not esp.secure_download_mode: + # Check if secure boot is active + if esp.get_secure_boot_enabled(): + for address, _ in args.addr_filename: + if address < 0x8000: + raise FatalError( + "Secure Boot detected, writing to flash regions < 0x8000 " + "is disabled to protect the bootloader. " + "Use --force to override, " + "please use with caution, otherwise it may brick your device!" + ) + # Check if chip_id and min_rev in image are valid for the target in use + for _, argfile in args.addr_filename: + try: + image = LoadFirmwareImage(esp.CHIP_NAME, argfile) + except (FatalError, struct.error, RuntimeError): + continue + finally: + argfile.seek(0) # LoadFirmwareImage changes the file handle position + if image.chip_id != esp.IMAGE_CHIP_ID: + raise FatalError( + f"{argfile.name} is not an {esp.CHIP_NAME} image. " + "Use --force to flash anyway." + ) + + # this logic below decides which min_rev to use, min_rev or min/max_rev_full + if image.max_rev_full == 0: # image does not have max/min_rev_full fields + use_rev_full_fields = False + elif image.max_rev_full == 65535: # image has default value of max_rev_full + use_rev_full_fields = True + if ( + image.min_rev_full == 0 and image.min_rev != 0 + ): # min_rev_full is not set, min_rev is used + use_rev_full_fields = False + else: # max_rev_full set to a version + use_rev_full_fields = True + + if use_rev_full_fields: + rev = esp.get_chip_revision() + if rev < image.min_rev_full or rev > image.max_rev_full: + error_str = f"{argfile.name} requires chip revision in range " + error_str += ( + f"[v{image.min_rev_full // 100}.{image.min_rev_full % 100} - " + ) + if image.max_rev_full == 65535: + error_str += "max rev not set] " + else: + error_str += ( + f"v{image.max_rev_full // 100}.{image.max_rev_full % 100}] " + ) + error_str += f"(this chip is revision v{rev // 100}.{rev % 100})" + raise FatalError(f"{error_str}. Use --force to flash anyway.") + else: + # In IDF, image.min_rev is set based on Kconfig option. + # For C3 chip, image.min_rev is the Minor revision + # while for the rest chips it is the Major revision. + if esp.CHIP_NAME == "ESP32-C3": + rev = esp.get_minor_chip_version() + else: + rev = esp.get_major_chip_version() + if rev < image.min_rev: + raise FatalError( + f"{argfile.name} requires chip revision " + f"{image.min_rev} or higher (this chip is revision {rev}). " + "Use --force to flash anyway." + ) + + # In case we have encrypted files to write, + # we first do few sanity checks before actual flash + if args.encrypt or args.encrypt_files is not None: + do_write = True + + if not esp.secure_download_mode: + if esp.get_encrypted_download_disabled(): + raise FatalError( + "This chip has encrypt functionality " + "in UART download mode disabled. " + "This is the Flash Encryption configuration for Production mode " + "instead of Development mode." + ) + + crypt_cfg_efuse = esp.get_flash_crypt_config() + + if crypt_cfg_efuse is not None and crypt_cfg_efuse != 0xF: + print("Unexpected FLASH_CRYPT_CONFIG value: 0x%x" % (crypt_cfg_efuse)) + do_write = False + + enc_key_valid = esp.is_flash_encryption_key_valid() + + if not enc_key_valid: + print("Flash encryption key is not programmed") + do_write = False + + # Determine which files list contain the ones to encrypt + files_to_encrypt = args.addr_filename if args.encrypt else args.encrypt_files + + for address, argfile in files_to_encrypt: + if address % esp.FLASH_ENCRYPTED_WRITE_ALIGN: + print( + "File %s address 0x%x is not %d byte aligned, can't flash encrypted" + % (argfile.name, address, esp.FLASH_ENCRYPTED_WRITE_ALIGN) + ) + do_write = False + + if not do_write and not args.ignore_flash_encryption_efuse_setting: + raise FatalError( + "Can't perform encrypted flash write, " + "consult Flash Encryption documentation for more information" + ) + else: + if not args.force and esp.CHIP_NAME != "ESP8266": + # ESP32 does not support `get_security_info()` and `secure_download_mode` + if ( + esp.CHIP_NAME != "ESP32" + and esp.secure_download_mode + and bin(esp.get_security_info()["flash_crypt_cnt"]).count("1") & 1 != 0 + ): + raise FatalError( + "WARNING: Detected flash encryption and " + "secure download mode enabled.\n" + "Flashing plaintext binary may brick your device! " + "Use --force to override the warning." + ) + + if ( + not esp.secure_download_mode + and esp.get_encrypted_download_disabled() + and esp.get_flash_encryption_enabled() + ): + raise FatalError( + "WARNING: Detected flash encryption enabled and " + "download manual encrypt disabled.\n" + "Flashing plaintext binary may brick your device! " + "Use --force to override the warning." + ) + + # verify file sizes fit in flash + flash_end = flash_size_bytes( + detect_flash_size(esp) if args.flash_size == "keep" else args.flash_size + ) + if flash_end is not None: # Not in secure download mode + for address, argfile in args.addr_filename: + argfile.seek(0, os.SEEK_END) + if address + argfile.tell() > flash_end: + raise FatalError( + "File %s (length %d) at offset %d " + "will not fit in %d bytes of flash. " + "Use --flash_size argument, or change flashing address." + % (argfile.name, argfile.tell(), address, flash_end) + ) + argfile.seek(0) + + if args.erase_all: + erase_flash(esp, args) + else: + for address, argfile in args.addr_filename: + argfile.seek(0, os.SEEK_END) + write_end = address + argfile.tell() + argfile.seek(0) + bytes_over = address % esp.FLASH_SECTOR_SIZE + if bytes_over != 0: + print( + "WARNING: Flash address {:#010x} is not aligned " + "to a {:#x} byte flash sector. " + "{:#x} bytes before this address will be erased.".format( + address, esp.FLASH_SECTOR_SIZE, bytes_over + ) + ) + # Print the address range of to-be-erased flash memory region + print( + "Flash will be erased from {:#010x} to {:#010x}...".format( + address - bytes_over, + div_roundup(write_end, esp.FLASH_SECTOR_SIZE) + * esp.FLASH_SECTOR_SIZE + - 1, + ) + ) + + """ Create a list describing all the files we have to flash. + Each entry holds an "encrypt" flag marking whether the file needs encryption or not. + This list needs to be sorted. + + First, append to each entry of our addr_filename list the flag args.encrypt + E.g., if addr_filename is [(0x1000, "partition.bin"), (0x8000, "bootloader")], + all_files will be [ + (0x1000, "partition.bin", args.encrypt), + (0x8000, "bootloader", args.encrypt) + ], + where, of course, args.encrypt is either True or False + """ + all_files = [ + (offs, filename, args.encrypt) for (offs, filename) in args.addr_filename + ] + + """ + Now do the same with encrypt_files list, if defined. + In this case, the flag is True + """ + if args.encrypt_files is not None: + encrypted_files_flag = [ + (offs, filename, True) for (offs, filename) in args.encrypt_files + ] + + # Concatenate both lists and sort them. + # As both list are already sorted, we could simply do a merge instead, + # but for the sake of simplicity and because the lists are very small, + # let's use sorted. + all_files = sorted(all_files + encrypted_files_flag, key=lambda x: x[0]) + + for address, argfile, encrypted in all_files: + compress = args.compress + + # Check whether we can compress the current file before flashing + if compress and encrypted: + print("\nWARNING: - compress and encrypt options are mutually exclusive ") + print("Will flash %s uncompressed" % argfile.name) + compress = False + + if args.no_stub: + print("Erasing flash...") + image = pad_to( + argfile.read(), esp.FLASH_ENCRYPTED_WRITE_ALIGN if encrypted else 4 + ) + if len(image) == 0: + print("WARNING: File %s is empty" % argfile.name) + continue + image = _update_image_flash_params(esp, address, args, image) + calcmd5 = hashlib.md5(image).hexdigest() + uncsize = len(image) + if compress: + uncimage = image + image = zlib.compress(uncimage, 9) + # Decompress the compressed binary a block at a time, + # to dynamically calculate the timeout based on the real write size + decompress = zlib.decompressobj() + blocks = esp.flash_defl_begin(uncsize, len(image), address) + else: + blocks = esp.flash_begin(uncsize, address, begin_rom_encrypted=encrypted) + argfile.seek(0) # in case we need it again + seq = 0 + bytes_sent = 0 # bytes sent on wire + bytes_written = 0 # bytes written to flash + t = time.time() + + timeout = DEFAULT_TIMEOUT + + while len(image) > 0: + print_overwrite( + "Writing at 0x%08x... (%d %%)" + % (address + bytes_written, 100 * (seq + 1) // blocks) + ) + sys.stdout.flush() + block = image[0 : esp.FLASH_WRITE_SIZE] + if compress: + # feeding each compressed block into the decompressor lets us + # see block-by-block how much will be written + block_uncompressed = len(decompress.decompress(block)) + bytes_written += block_uncompressed + block_timeout = max( + DEFAULT_TIMEOUT, + timeout_per_mb(ERASE_WRITE_TIMEOUT_PER_MB, block_uncompressed), + ) + if not esp.IS_STUB: + timeout = ( + block_timeout # ROM code writes block to flash before ACKing + ) + esp.flash_defl_block(block, seq, timeout=timeout) + if esp.IS_STUB: + # Stub ACKs when block is received, + # then writes to flash while receiving the block after it + timeout = block_timeout + else: + # Pad the last block + block = block + b"\xff" * (esp.FLASH_WRITE_SIZE - len(block)) + if encrypted: + esp.flash_encrypt_block(block, seq) + else: + esp.flash_block(block, seq) + bytes_written += len(block) + bytes_sent += len(block) + image = image[esp.FLASH_WRITE_SIZE :] + seq += 1 + + if esp.IS_STUB: + # Stub only writes each block to flash after 'ack'ing the receive, + # so do a final dummy operation which will not be 'ack'ed + # until the last block has actually been written out to flash + esp.read_reg(ESPLoader.CHIP_DETECT_MAGIC_REG_ADDR, timeout=timeout) + + t = time.time() - t + speed_msg = "" + if compress: + if t > 0.0: + speed_msg = " (effective %.1f kbit/s)" % (uncsize / t * 8 / 1000) + print_overwrite( + "Wrote %d bytes (%d compressed) at 0x%08x in %.1f seconds%s..." + % (uncsize, bytes_sent, address, t, speed_msg), + last_line=True, + ) + else: + if t > 0.0: + speed_msg = " (%.1f kbit/s)" % (bytes_written / t * 8 / 1000) + print_overwrite( + "Wrote %d bytes at 0x%08x in %.1f seconds%s..." + % (bytes_written, address, t, speed_msg), + last_line=True, + ) + + if not encrypted and not esp.secure_download_mode: + try: + res = esp.flash_md5sum(address, uncsize) + if res != calcmd5: + print("File md5: %s" % calcmd5) + print("Flash md5: %s" % res) + print( + "MD5 of 0xFF is %s" + % (hashlib.md5(b"\xFF" * uncsize).hexdigest()) + ) + raise FatalError("MD5 of file does not match data in flash!") + else: + print("Hash of data verified.") + except NotImplementedInROMError: + pass + + print("\nLeaving...") + + if esp.IS_STUB: + # skip sending flash_finish to ROM loader here, + # as it causes the loader to exit and run user code + esp.flash_begin(0, 0) + + # Get the "encrypted" flag for the last file flashed + # Note: all_files list contains triplets like: + # (address: Integer, filename: String, encrypted: Boolean) + last_file_encrypted = all_files[-1][2] + + # Check whether the last file flashed was compressed or not + if args.compress and not last_file_encrypted: + esp.flash_defl_finish(False) + else: + esp.flash_finish(False) + + if args.verify: + print("Verifying just-written flash...") + print( + "(This option is deprecated, " + "flash contents are now always read back after flashing.)" + ) + # If some encrypted files have been flashed, + # print a warning saying that we won't check them + if args.encrypt or args.encrypt_files is not None: + print("WARNING: - cannot verify encrypted files, they will be ignored") + # Call verify_flash function only if there is at least + # one non-encrypted file flashed + if not args.encrypt: + verify_flash(esp, args) + + +def image_info(args): + def v2(): + def get_key_from_value(dict, val): + """Get key from value in dictionary""" + for key, value in dict.items(): + if value == val: + return key + return None + + print() + title = "{} image header".format(args.chip.upper()) + print(title) + print("=" * len(title)) + print("Image version: {}".format(image.version)) + print( + "Entry point: {:#8x}".format(image.entrypoint) + if image.entrypoint != 0 + else "Entry point not set" + ) + + print("Segments: {}".format(len(image.segments))) + + # Flash size + flash_s_bits = image.flash_size_freq & 0xF0 # high four bits + flash_s = get_key_from_value(image.ROM_LOADER.FLASH_SIZES, flash_s_bits) + print( + "Flash size: {}".format(flash_s) + if flash_s is not None + else "WARNING: Invalid flash size ({:#02x})".format(flash_s_bits) + ) + + # Flash frequency + flash_fr_bits = image.flash_size_freq & 0x0F # low four bits + flash_fr = get_key_from_value(image.ROM_LOADER.FLASH_FREQUENCY, flash_fr_bits) + print( + "Flash freq: {}".format(flash_fr) + if flash_fr is not None + else "WARNING: Invalid flash frequency ({:#02x})".format(flash_fr_bits) + ) + + # Flash mode + flash_mode = get_key_from_value(FLASH_MODES, image.flash_mode) + print( + "Flash mode: {}".format(flash_mode.upper()) + if flash_mode is not None + else "WARNING: Invalid flash mode ({})".format(image.flash_mode) + ) + + # Extended header (ESP32 and later only) + if args.chip != "esp8266": + print() + title = "{} extended image header".format(args.chip.upper()) + print(title) + print("=" * len(title)) + print( + f"WP pin: {image.wp_pin:#02x}", + *["(disabled)"] if image.wp_pin == image.WP_PIN_DISABLED else [], + ) + print( + "Flash pins drive settings: " + "clk_drv: {:#02x}, q_drv: {:#02x}, d_drv: {:#02x}, " + "cs0_drv: {:#02x}, hd_drv: {:#02x}, wp_drv: {:#02x}".format( + image.clk_drv, + image.q_drv, + image.d_drv, + image.cs_drv, + image.hd_drv, + image.wp_drv, + ) + ) + try: + chip = next( + chip + for chip in CHIP_DEFS.values() + if getattr(chip, "IMAGE_CHIP_ID", None) == image.chip_id + ) + print(f"Chip ID: {image.chip_id} ({chip.CHIP_NAME})") + except StopIteration: + print(f"Chip ID: {image.chip_id} (Unknown ID)") + print( + "Minimal chip revision: " + f"v{image.min_rev_full // 100}.{image.min_rev_full % 100}, " + f"(legacy min_rev = {image.min_rev})" + ) + print( + "Maximal chip revision: " + f"v{image.max_rev_full // 100}.{image.max_rev_full % 100}" + ) + print() + + # Segments overview + title = "Segments information" + print(title) + print("=" * len(title)) + headers_str = "{:>7} {:>7} {:>10} {:>10} {:10}" + print( + headers_str.format( + "Segment", "Length", "Load addr", "File offs", "Memory types" + ) + ) + print( + "{} {} {} {} {}".format("-" * 7, "-" * 7, "-" * 10, "-" * 10, "-" * 12) + ) + format_str = "{:7} {:#07x} {:#010x} {:#010x} {}" + app_desc = None + bootloader_desc = None + for idx, seg in enumerate(image.segments, start=1): + segs = seg.get_memory_type(image) + seg_name = ", ".join(segs) + if "DROM" in segs: # The DROM segment starts with the esp_app_desc_t struct + app_desc = seg.data[:256] + elif "DRAM" in segs: + # The DRAM segment starts with the esp_bootloader_desc_t struct + if len(seg.data) >= 80: + bootloader_desc = seg.data[:80] + print( + format_str.format(idx, len(seg.data), seg.addr, seg.file_offs, seg_name) + ) + print() + + # Footer + title = f"{args.chip.upper()} image footer" + print(title) + print("=" * len(title)) + calc_checksum = image.calculate_checksum() + print( + "Checksum: {:#02x} ({})".format( + image.checksum, + "valid" + if image.checksum == calc_checksum + else "invalid - calculated {:02x}".format(calc_checksum), + ) + ) + try: + digest_msg = "Not appended" + if image.append_digest: + is_valid = image.stored_digest == image.calc_digest + digest_msg = "{} ({})".format( + hexify(image.calc_digest, uppercase=False), + "valid" if is_valid else "invalid", + ) + print("Validation hash: {}".format(digest_msg)) + except AttributeError: + pass # ESP8266 image has no append_digest field + + if app_desc: + APP_DESC_STRUCT_FMT = "