# ----- General ----- #
UINT32_MAX = 0xFFFF_FFFF

# ----- PIN and encryption related ----- #

# App ID where PIN log is stored.
PIN_APP_ID = 0x00

# Storage key of the combined salt, EDEK, ESEK and PIN verification code entry.
EDEK_ESEK_PVC_KEY = (PIN_APP_ID << 8) | 0x02

# Storage key of the PIN set flag.
PIN_NOT_SET_KEY = (PIN_APP_ID << 8) | 0x03

# Norcow storage key of the storage version.
VERSION_KEY = (PIN_APP_ID << 8) | 0x04

# Norcow storage key of the storage authentication tag.
SAT_KEY = (PIN_APP_ID << 8) | 0x05

# Norcow storage key of the wipe code data.
WIPE_CODE_DATA_KEY = (PIN_APP_ID << 8) | 0x06

# Norcow storage key of the storage upgrade flag.
STORAGE_UPGRADED_KEY = (PIN_APP_ID << 8) | 0x07

# Norcow storage key of the unauthenticated storage version.
UNAUTH_VERSION_KEY = (PIN_APP_ID << 8) | 0x08

# The PIN value corresponding to an empty PIN.
PIN_EMPTY = ""

# Maximum number of failed unlock attempts.
PIN_MAX_TRIES = 16

# The total number of iterations to use in PBKDF2.
PIN_ITER_COUNT = 20000

# The length of the data encryption key in bytes.
DEK_SIZE = 32

# The length of the storage authentication key in bytes.
SAK_SIZE = 16

# The length of the storage authentication tag in bytes.
SAT_SIZE = 16

# The length of the random salt in bytes.
PIN_SALT_SIZE = 4
PIN_HARDWARE_SALT_SIZE = 32

# The length of the PIN verification code in bytes.
PVC_SIZE = 8

# The length of KEK in bytes.
KEK_SIZE = 32

# The length of KEIV in bytes.
KEIV_SIZE = 12

# The byte length of the salt used in checking the wipe code.
WIPE_CODE_SALT_SIZE = 8

# The byte length of the tag used in checking the wipe code.
WIPE_CODE_TAG_SIZE = 8

# The value corresponding to an unconfigured wipe code.
# NOTE: This is intentionally different from PIN_EMPTY so that we don't need
# special handling when both the PIN and wipe code are not set.
WIPE_CODE_EMPTY = "\0\0\0\0"

# Size of counter. 4B integer and 8B tail.
COUNTER_TAIL = 12
COUNTER_TAIL_SIZE = 8
COUNTER_MAX_TAIL = 64

# ----- PIN logs ----- #

# Storage key of the PIN entry log and PIN success log.
PIN_LOG_KEY = (PIN_APP_ID << 8) | 0x01

# Length of items in the PIN entry log
PIN_LOG_GUARD_KEY_SIZE = 4

# Values used for the guard key integrity check.
GUARD_KEY_MODULUS = 6311
GUARD_KEY_REMAINDER = 15
GUARD_KEY_RANDOM_MAX = (0xFFFFFFFF // GUARD_KEY_MODULUS) + 1

# Length of both success log and entry log
PIN_LOG_SIZE = 64

# Used for in guard bits operations.
LOW_MASK = 0x55555555

# Log initialized to all FFs.
ALL_FF_LOG = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF

# ----- Bytes -----

# If the top bit of APP is set, then the value is not encrypted.
FLAG_PUBLIC = 0x80

# If the top two bits of APP are set, then the value is not encrypted and it
# can be written even when the storage is locked.
FLAGS_WRITE = 0xC0

# Length of word in bytes.
WORD_SIZE = 4

# Boolean values are stored as a simple 0/1 int.
TRUE_BYTE = b"\x01"
FALSE_BYTE = b"\x00"
TRUE_WORD = b"\xA5\x69\x5A\xC3"
FALSE_WORD = b"\x5A\x96\xA5\x3C"

# ----- Crypto ----- #

# The length of the Poly1305 MAC in bytes.
POLY1305_MAC_SIZE = 16

# The length of the ChaCha20 IV (aka nonce) in bytes as per RFC 7539.
CHACHA_IV_SIZE = 12

# ----- Norcow ----- #

NORCOW_SECTOR_COUNT = 2
NORCOW_SECTOR_SIZE = 64 * 1024

# Magic flag at the beggining of an active sector.
NORCOW_MAGIC = b"NRC2"

# Norcow version, set in the storage header, but also as an encrypted item.
NORCOW_VERSION = b"\x03\x00\x00\x00"

# Norcow magic combined with the version, which is stored as its negation.
NORCOW_MAGIC_AND_VERSION = NORCOW_MAGIC + bytes(
    [
        ~NORCOW_VERSION[0] & 0xFF,
        ~NORCOW_VERSION[1] & 0xFF,
        ~NORCOW_VERSION[2] & 0xFF,
        ~NORCOW_VERSION[3] & 0xFF,
    ]
)

# Signalizes free storage.
NORCOW_KEY_FREE = 0xFFFF


# |-----------|-------------------|
# | Private   | APP = 0           |
# | Protected | 1 <= APP <= 127   |
# | Public    | 128 <= APP <= 255 |


def is_app_public(app: int):
    if app & FLAG_PUBLIC:
        return True
    return False


def is_app_protected(app: int):
    if is_app_public(app):
        return False
    if is_app_private(app):
        return False
    return True


def is_app_private(app: int):
    return app == PIN_APP_ID


def is_app_lock_writable(app: int):
    if app & FLAGS_WRITE == FLAGS_WRITE:
        return True
    return False
