"""smartcard.util package __author__ = "https://www.gemalto.com/" Copyright 2001-2012 gemalto Author: Jean-Daniel Aussel, mailto:jean-daniel.aussel@gemalto.com This file is part of pyscard. pyscard is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. pyscard 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with pyscard; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA """ from __future__ import annotations import warnings PACK = 1 HEX = 2 UPPERCASE = 4 COMMA = 8 def padd(bytelist: list[int], length: int, padding: str = "FF"): """Padds a byte list with a constant byte value (default is x0FF) @param bytelist: the byte list to padd @param length: the total length of the resulting byte list; no padding if length is smaller than the byte list length @param padding: padding value (default is 0xff) @return: the padded bytelist >>> padd([59, 101, 0, 0, 156, 17, 1, 1, 3], 16) [59, 101, 0, 0, 156, 17, 1, 1, 3, 255, 255, 255, 255, 255, 255, 255] >>> padd([59, 101, 0, 0, 156, 17, 1, 1, 3], 12, '80') [59, 101, 0, 0, 156, 17, 1, 1, 3, 128, 128, 128] >>> padd([59, 101, 0, 0, 156, 17, 1, 1, 3], 8) [59, 101, 0, 0, 156, 17, 1, 1, 3] """ return bytelist + [int(padding, 16)] * (length - len(bytelist)) def toASCIIBytes(stringtoconvert: str) -> list[int]: """Convert a string to a list of UTF-8 encoded bytes. @param stringtoconvert: the string to convert into a byte list @return: a byte list of the ASCII codes of the string characters L{toASCIIBytes()} is the reverse of L{toASCIIString()} >>> toASCIIBytes("Number 101") [78, 117, 109, 98, 101, 114, 32, 49, 48, 49] """ return list(stringtoconvert.encode("utf-8")) def toASCIIString(bytelist: list[int]) -> str: """Convert a list of integers in the range ``[32, 127]`` to a string. Integer values outside the range ``[32, 127]`` are replaced with a period. @param bytelist: list of ASCII bytes to convert into a string @return: a string from the ASCII code list L{toASCIIString()} is the reverse of L{toASCIIBytes()} >>> toASCIIString([0x4E,0x75,0x6D,0x62,0x65,0x72,0x20,0x31,0x30,0x31]) 'Number 101' >>> toASCIIString([0x01, 0x20, 0x80, 0x7E, 0xF0]) ". .~." """ return "".join(chr(c) if 32 <= c <= 127 else "." for c in bytelist) def toBytes(bytestring: str) -> list[int]: """Convert a string of hexadecimal characters to a list of integers. @param bytestring: a byte string >>> toBytes("3B 65 00 00 9C 11 01 01 03") [59, 101, 0, 0, 156, 17, 1, 1, 3] >>> toBytes("3B6500009C11010103") [59, 101, 0, 0, 156, 17, 1, 1, 3] >>> toBytes("3B6500 009C1101 0103") [59, 101, 0, 0, 156, 17, 1, 1, 3] """ try: return list(bytes.fromhex(bytestring)) except ValueError: raise TypeError("not a string representing a list of bytes") """GSM3.38 character conversion table.""" __dic_GSM_3_38__ = { "@": 0x00, # @ At symbol "£": 0x01, # £ Britain pound symbol "$": 0x02, # $ Dollar symbol "¥": 0x03, # ¥ Yen symbol "è": 0x04, # è e accent grave "é": 0x05, # é e accent aigu "ù": 0x06, # ù u accent grave "ì": 0x07, # ì i accent grave "ò": 0x08, # ò o accent grave "Ç": 0x09, # Ç C majuscule cedille "\n": 0x0A, # LF Line Feed "Ø": 0x0B, # Ø O majuscule barré "ø": 0x0C, # ø o minuscule barré "\r": 0x0D, # CR Carriage Return "Å": 0x0E, # Å Angstroem majuscule "å": 0x0F, # å Angstroem minuscule "Δ": 0x10, # Δ Greek letter delta "_": 0x11, # underscore "Φ": 0x12, # Φ Greek letter phi "Γ": 0x13, # Γ Greek letter gamma "Λ": 0x14, # Λ Greek letter lambda "Ω": 0x15, # Ω Greek letter omega "Π": 0x16, # Π Greek letter pi "Ψ": 0x17, # Ψ Greek letter psi "Σ": 0x18, # Σ Greek letter sigma "Θ": 0x19, # Θ Greek letter theta "Ξ": 0x1A, # Ξ Greek letter xi # 0x1B maps to extension table "Æ": 0x1C, # Æ majuscule ae "æ": 0x1D, # æ minuscule ae "ß": 0x1E, # ß s dur allemand "É": 0x1F, # É majuscule é " ": 0x20, "!": 0x21, '"': 0x22, # guillemet "#": 0x23, "¤": 0x24, # ¤ carré # # 0x25 ... 0x3F # % ... ? # "¡": 0x40, # ¡ point d'exclamation renversé # # 0x41 ... 0x5A # A ... Z # "Ä": 0x5B, # Ä majuscule A trema "Ö": 0x5C, # Ö majuscule O trema "Ñ": 0x5D, # Ñ majuscule N tilde espagnol "Ü": 0x5E, # Ü majuscule U trema "§": 0x5F, # § signe paragraphe "¿": 0x60, # ¿ point interrogation renversé # # 0x61 ... 0x7A # a ... z # "ä": 0x7B, # ä minuscule a trema "ö": 0x7C, # ö minuscule o trema "ñ": 0x7D, # ñ minuscule n tilde espagnol "ü": 0x7E, # ü minuscule u trema "à": 0x7F, # à a accent grave } def toGSM3_38Bytes(stringtoconvert: str | bytes) -> list[int]: """Returns a list of bytes from a string using GSM 3.38 conversion table. @param stringtoconvert: string to convert @return: a list of bytes >>> toGSM3_38Bytes("@ùPascal") [0, 6, 80, 97, 115, 99, 97, 108] """ if isinstance(stringtoconvert, bytes): stringtoconvert = stringtoconvert.decode("iso8859-1") result = [] for char in stringtoconvert: if ("%" <= char <= "?") or ("A" <= char <= "Z") or ("a" <= char <= "z"): result.append(ord(char)) else: result.append(__dic_GSM_3_38__[char]) return result def toHexString(data: list[int] | None = None, format: int = 0) -> str: """Convert a list of integers to a formatted string of hexadecimal. Integers larger than 255 will be truncated to two-byte hexadecimal pairs. @param data: a list of bytes to stringify, e.g. [59, 22, 148, 32, 2, 1, 0, 0, 13] @param format: a logical OR of - COMMA: add a comma between bytes - HEX: add the 0x chars before bytes - UPPERCASE: use 0X before bytes (need HEX) - PACK: remove blanks >>> vals = [0x3B, 0x65, 0x00, 0x00, 0x9C, 0x11, 0x01, 0x01, 0x03] >>> toHexString(vals) '3B 65 00 00 9C 11 01 01 03' >>> toHexString(vals, COMMA) '3B, 65, 00, 00, 9C, 11, 01, 01, 03' >>> toHexString(vals, HEX) '0x3B 0x65 0x00 0x00 0x9C 0x11 0x01 0x01 0x03' >>> toHexString(vals, HEX | COMMA) '0x3B, 0x65, 0x00, 0x00, 0x9C, 0x11, 0x01, 0x01, 0x03' >>> toHexString(vals, PACK) '3B6500009C11010103' >>> toHexString(vals, HEX | UPPERCASE) '0X3B 0X65 0X00 0X00 0X9C 0X11 0X01 0X01 0X03' >>> toHexString(vals, HEX | UPPERCASE | COMMA) '0X3B, 0X65, 0X00, 0X00, 0X9C, 0X11, 0X01, 0X01, 0X03' """ if not (data is None or isinstance(data, list)): raise TypeError("not a list of bytes") if not data: return "" pformat = "%-0.2X" separator = "" if COMMA & format: separator = "," if not PACK & format: separator += " " if HEX & format: if UPPERCASE & format: pformat = "0X" + pformat else: pformat = "0x" + pformat return separator.join(pformat % (a & 0xFF) for a in data).rstrip() def HexListToBinString(hexlist: list[int]) -> str: """Deprecated. Use `bytes(hexlist).decode("utf-8")` or similar. >>> HexListToBinString([78, 117, 109, 98, 101, 114, 32, 49, 48, 49]) 'Number 101' """ warnings.warn( 'Use `bytes(hexlist).decode("utf-8")` or similar.', DeprecationWarning, ) return bytes(hexlist).decode("utf-8") def BinStringToHexList(binstring: str) -> list[int]: """Deprecated. Use `list(binstring.encode("utf-8"))` or similar. >>> BinStringToHexList("Number 101") [78, 117, 109, 98, 101, 114, 32, 49, 48, 49] """ warnings.warn( 'Use `list(binstring.encode("utf-8"))` or similar.', DeprecationWarning, ) return list(binstring.encode("utf-8")) hl2bs = HexListToBinString bs2hl = BinStringToHexList