diff options
Diffstat (limited to 'lib/mbedtls-2.27.0/scripts/mbedtls_dev')
7 files changed, 1313 insertions, 0 deletions
diff --git a/lib/mbedtls-2.27.0/scripts/mbedtls_dev/asymmetric_key_data.py b/lib/mbedtls-2.27.0/scripts/mbedtls_dev/asymmetric_key_data.py new file mode 100644 index 0000000..6fd6223 --- /dev/null +++ b/lib/mbedtls-2.27.0/scripts/mbedtls_dev/asymmetric_key_data.py @@ -0,0 +1,166 @@ +"""Sample key material for asymmetric key types. + +Meant for use in crypto_knowledge.py. +""" + +# Copyright The Mbed TLS Contributors +# SPDX-License-Identifier: Apache-2.0 +# +# 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 +# +# http://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. + +import binascii +import re +from typing import Dict + +STR_TRANS_REMOVE_BLANKS = str.maketrans('', '', ' \t\n\r') + +def unhexlify(text: str) -> bytes: + return binascii.unhexlify(text.translate(STR_TRANS_REMOVE_BLANKS)) + +def construct_asymmetric_key_data(src) -> Dict[str, Dict[int, bytes]]: + """Split key pairs into separate table entries and convert hex to bytes. + + Input format: src[abbreviated_type][size] = (private_key_hex, public_key_hex) + Output format: dst['PSA_KEY_TYPE_xxx'][size] = key_bytes + """ + dst = {} #type: Dict[str, Dict[int, bytes]] + for typ in src: + private = 'PSA_KEY_TYPE_' + re.sub(r'(\(|\Z)', r'_KEY_PAIR\1', typ, 1) + public = 'PSA_KEY_TYPE_' + re.sub(r'(\(|\Z)', r'_PUBLIC_KEY\1', typ, 1) + dst[private] = {} + dst[public] = {} + for size in src[typ]: + dst[private][size] = unhexlify(src[typ][size][0]) + dst[public][size] = unhexlify(src[typ][size][1]) + return dst + +## These are valid keys that don't try to exercise any edge cases. They're +## either test vectors from some specification, or randomly generated. All +## pairs consist of a private key and its public key. +#pylint: disable=line-too-long +ASYMMETRIC_KEY_DATA = construct_asymmetric_key_data({ + 'ECC(PSA_ECC_FAMILY_SECP_K1)': { + 192: ("297ac1722ccac7589ecb240dc719842538ca974beb79f228", + "0426b7bb38da649ac2138fc050c6548b32553dab68afebc36105d325b75538c12323cb0764789ecb992671beb2b6bef2f5"), + 224: ("0024122bf020fa113f6c0ac978dfbd41f749257a9468febdbe0dc9f7e8", + "042cc7335f4b76042bed44ef45959a62aa215f7a5ff0c8111b8c44ed654ee71c1918326ad485b2d599fe2a6eab096ee26d977334d2bac6d61d"), + 256: ("7fa06fa02d0e911b9a47fdc17d2d962ca01e2f31d60c6212d0ed7e3bba23a7b9", + "045c39154579efd667adc73a81015a797d2c8682cdfbd3c3553c4a185d481cdc50e42a0e1cbc3ca29a32a645e927f54beaed14c9dbbf8279d725f5495ca924b24d"), + }, + 'ECC(PSA_ECC_FAMILY_SECP_R1)': { + 225: ("872f203b3ad35b7f2ecc803c3a0e1e0b1ed61cc1afe71b189cd4c995", + "046f00eadaa949fee3e9e1c7fa1247eecec86a0dce46418b9bd3117b981d4bd0ae7a990de912f9d060d6cb531a42d22e394ac29e81804bf160"), + 256: ("49c9a8c18c4b885638c431cf1df1c994131609b580d4fd43a0cab17db2f13eee", + "047772656f814b399279d5e1f1781fac6f099a3c5ca1b0e35351834b08b65e0b572590cdaf8f769361bcf34acfc11e5e074e8426bdde04be6e653945449617de45"), + 384: ("3f5d8d9be280b5696cc5cc9f94cf8af7e6b61dd6592b2ab2b3a4c607450417ec327dcdcaed7c10053d719a0574f0a76a", + "04d9c662b50ba29ca47990450e043aeaf4f0c69b15676d112f622a71c93059af999691c5680d2b44d111579db12f4a413a2ed5c45fcfb67b5b63e00b91ebe59d09a6b1ac2c0c4282aa12317ed5914f999bc488bb132e8342cc36f2ca5e3379c747"), + 521: ("01b1b6ad07bb79e7320da59860ea28e055284f6058f279de666e06d435d2af7bda28d99fa47b7dd0963e16b0073078ee8b8a38d966a582f46d19ff95df3ad9685aae", + "04001de142d54f69eb038ee4b7af9d3ca07736fd9cf719eb354d69879ee7f3c136fb0fbf9f08f86be5fa128ec1a051d3e6c643e85ada8ffacf3663c260bd2c844b6f5600cee8e48a9e65d09cadd89f235dee05f3b8a646be715f1f67d5b434e0ff23a1fc07ef7740193e40eeff6f3bcdfd765aa9155033524fe4f205f5444e292c4c2f6ac1"), + }, + 'ECC(PSA_ECC_FAMILY_SECP_R2)': { + 160: ("00bf539a1cdda0d7f71a50a3f98aec0a2e8e4ced1e", + "049570d541398665adb5cfa16f5af73b3196926bbd4b876bdb80f8eab20d0f540c22f4de9c140f6d7b"), + }, + 'ECC(PSA_ECC_FAMILY_SECT_K1)': { + 163: ("03ebc8fcded2d6ab72ec0f75bdb4fd080481273e71", + "0406f88f90b4b65950f06ce433afdb097e320f433dc2062b8a65db8fafd3c110f46bc45663fbf021ee7eb9"), + 233: ("41f08485ce587b06061c087e76e247c359de2ba9927ee013b2f1ed9ca8", + "0401e9d7189189f773bd8f71be2c10774ba18842434dfa9312595ea545104400f45a9d5675647513ba75b079fe66a29daac2ec86a6a5d4e75c5f290c1f"), + 239: ("1a8069ce2c2c8bdd7087f2a6ab49588797e6294e979495602ab9650b9c61", + "04068d76b9f4508762c2379db9ee8b87ad8d86d9535132ffba3b5680440cfa28eb133d4232faf1c9aba96af11aefe634a551440800d5f8185105d3072d"), + 283: ("006d627885dd48b9ec6facb5b3865377d755b75a5d51440e45211c1f600e15eff8a881a0", + "0405f48374debceaadb46ba385fd92048fcc5b9af1a1c90408bf94a68b9378df1cbfdfb6fb026a96bea06d8f181bf10c020adbcc88b6ecff96bdc564a9649c247cede601c4be63afc3"), + 409: ("3ff5e74d932fa77db139b7c948c81e4069c72c24845574064beea8976b70267f1c6f9a503e3892ea1dcbb71fcea423faa370a8", + "04012c587f69f68b308ba6dcb238797f4e22290ca939ae806604e2b5ab4d9caef5a74a98fd87c4f88d292dd39d92e556e16c6ecc3c019a105826eef507cd9a04119f54d5d850b3720b3792d5d03410e9105610f7e4b420166ed45604a7a1f229d80975ba6be2060e8b"), + 571: ("005008c97b4a161c0db1bac6452c72846d57337aa92d8ecb4a66eb01d2f29555ffb61a5317225dcc8ca6917d91789e227efc0bfe9eeda7ee21998cd11c3c9885056b0e55b4f75d51", + "04050172a7fd7adf98e4e2ed2742faa5cd12731a15fb0dbbdf75b1c3cc771a4369af6f2fa00e802735650881735759ea9c79961ded18e0daa0ac59afb1d513b5bbda9962e435f454fc020b4afe1445c2302ada07d295ec2580f8849b2dfa7f956b09b4cbe4c88d3b1c217049f75d3900d36df0fa12689256b58dd2ef784ebbeb0564600cf47a841485f8cf897a68accd5a"), + }, + 'ECC(PSA_ECC_FAMILY_SECT_R1)': { + 163: ("009b05dc82d46d64a04a22e6e5ca70ca1231e68c50", + "0400465eeb9e7258b11e33c02266bfe834b20bcb118700772796ee4704ec67651bd447e3011959a79a04cb"), + 233: ("00e5e42834e3c78758088b905deea975f28dc20ef6173e481f96e88afe7f", + "0400cd68c8af4430c92ec7a7048becfdf00a6bae8d1b4c37286f2d336f2a0e017eca3748f4ad6d435c85867aa014eea1bd6d9d005bbd8319cab629001d"), + 283: ("004cecad915f6f3c9bbbd92d1eb101eda23f16c7dad60a57c87c7e1fd2b29b22f6d666ad", + "04052f9ff887254c2d1440ba9e30f13e2185ba53c373b2c410dae21cf8c167f796c08134f601cbc4c570bffbc2433082cf4d9eb5ba173ecb8caec15d66a02673f60807b2daa729b765"), + 409: ("00c22422d265721a3ae2b3b2baeb77bee50416e19877af97b5fc1c700a0a88916ecb9050135883accb5e64edc77a3703f4f67a64", + "0401aa25466b1d291846db365957b25431591e50d9c109fe2106e93bb369775896925b15a7bfec397406ab4fe6f6b1a13bf8fdcb9300fa5500a813228676b0a6c572ed96b0f4aec7e87832e7e20f17ca98ecdfd36f59c82bddb8665f1f357a73900e827885ec9e1f22"), + 571: ("026ac1cdf92a13a1b8d282da9725847908745138f5c6706b52d164e3675fcfbf86fc3e6ab2de732193267db029dd35a0599a94a118f480231cfc6ccca2ebfc1d8f54176e0f5656a1", + "040708f3403ee9948114855c17572152a08f8054d486defef5f29cbffcfb7cfd9280746a1ac5f751a6ad902ec1e0525120e9be56f03437af196fbe60ee7856e3542ab2cf87880632d80290e39b1a2bd03c6bbf6225511c567bd2ff41d2325dc58346f2b60b1feee4dc8b2af2296c2dc52b153e0556b5d24152b07f690c3fa24e4d1d19efbdeb1037833a733654d2366c74"), + }, + 'ECC(PSA_ECC_FAMILY_SECT_R2)': { + 163: ("0210b482a458b4822d0cb21daa96819a67c8062d34", + "0403692601144c32a6cfa369ae20ae5d43c1c764678c037bafe80c6fd2e42b7ced96171d9c5367fd3dca6f"), + }, + 'ECC(PSA_ECC_FAMILY_BRAINPOOL_P_R1)': { + 160: ("69502c4fdaf48d4fa617bdd24498b0406d0eeaac", + "04d4b9186816358e2f9c59cf70748cb70641b22fbab65473db4b4e22a361ed7e3de7e8a8ddc4130c5c"), + 192: ("1688a2c5fbf4a3c851d76a98c3ec88f445a97996283db59f", + "043fdd168c179ff5363dd71dcd58de9617caad791ae0c37328be9ca0bfc79cebabf6a95d1c52df5b5f3c8b1a2441cf6c88"), + 224: ("a69835dafeb5da5ab89c59860dddebcfd80b529a99f59b880882923c", + "045fbea378fc8583b3837e3f21a457c31eaf20a54e18eb11d104b3adc47f9d1c97eb9ea4ac21740d70d88514b98bf0bc31addac1d19c4ab3cc"), + 256: ("2161d6f2db76526fa62c16f356a80f01f32f776784b36aa99799a8b7662080ff", + "04768c8cae4abca6306db0ed81b0c4a6215c378066ec6d616c146e13f1c7df809b96ab6911c27d8a02339f0926840e55236d3d1efbe2669d090e4c4c660fada91d"), + 320: ("61b8daa7a6e5aa9fccf1ef504220b2e5a5b8c6dc7475d16d3172d7db0b2778414e4f6e8fa2032ead", + "049caed8fb4742956cc2ad12a9a1c995e21759ef26a07bc2054136d3d2f28bb331a70e26c4c687275ab1f434be7871e115d2350c0c5f61d4d06d2bcdb67f5cb63fdb794e5947c87dc6849a58694e37e6cd"), + 384: ("3dd92e750d90d7d39fc1885cd8ad12ea9441f22b9334b4d965202adb1448ce24c5808a85dd9afc229af0a3124f755bcb", + "04719f9d093a627e0d350385c661cebf00c61923566fe9006a3107af1d871bc6bb68985fd722ea32be316f8e783b7cd1957785f66cfc0cb195dd5c99a8e7abaa848553a584dfd2b48e76d445fe00dd8be59096d877d4696d23b4bc8db14724e66a"), + 512: ("372c9778f69f726cbca3f4a268f16b4d617d10280d79a6a029cd51879fe1012934dfe5395455337df6906dc7d6d2eea4dbb2065c0228f73b3ed716480e7d71d2", + "0438b7ec92b61c5c6c7fbc28a4ec759d48fcd4e2e374defd5c4968a54dbef7510e517886fbfc38ea39aa529359d70a7156c35d3cbac7ce776bdb251dd64bce71234424ee7049eed072f0dbc4d79996e175d557e263763ae97095c081e73e7db2e38adc3d4c9a0487b1ede876dc1fca61c902e9a1d8722b8612928f18a24845591a"), + }, + 'ECC(PSA_ECC_FAMILY_MONTGOMERY)': { + 255: ("70076d0a7318a57d3c16c17251b26645df4c2f87ebc0992ab177fba51db92c6a", + "8520f0098930a754748b7ddcb43ef75a0dbf3a0d26381af4eba4a98eaa9b4e6a"), + 448: ("e4e49f52686f9ee3b638528f721f1596196ffd0a1cddb64c3f216f06541805cfeb1a286dc78018095cdfec050e8007b5f4908962ba20d6c1", + "c0d3a5a2b416a573dc9909f92f134ac01323ab8f8e36804e578588ba2d09fe7c3e737f771ca112825b548a0ffded6d6a2fd09a3e77dec30e"), + }, + 'ECC(PSA_ECC_FAMILY_TWISTED_EDWARDS)': { + 255: ("9d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60", + "d75a980182b10ab7d54bfed3c964073a0ee172f3daa62325af021a68f707511a"), + 448: ("6c82a562cb808d10d632be89c8513ebf6c929f34ddfa8c9f63c9960ef6e348a3528c8a3fcc2f044e39a3fc5b94492f8f032e7549a20098f95b", + "5fd7449b59b461fd2ce787ec616ad46a1da1342485a70e1f8a0ea75d80e96778edf124769b46c7061bd6783df1e50f6cd1fa1abeafe8256180"), + }, + 'RSA': { + 1024: (""" +3082025e + 020100 + 02818100af057d396ee84fb75fdbb5c2b13c7fe5a654aa8aa2470b541ee1feb0b12d25c79711531249e1129628042dbbb6c120d1443524ef4c0e6e1d8956eeb2077af12349ddeee54483bc06c2c61948cd02b202e796aebd94d3a7cbf859c2c1819c324cb82b9cd34ede263a2abffe4733f077869e8660f7d6834da53d690ef7985f6bc3 + 0203010001 + 02818100874bf0ffc2f2a71d14671ddd0171c954d7fdbf50281e4f6d99ea0e1ebcf82faa58e7b595ffb293d1abe17f110b37c48cc0f36c37e84d876621d327f64bbe08457d3ec4098ba2fa0a319fba411c2841ed7be83196a8cdf9daa5d00694bc335fc4c32217fe0488bce9cb7202e59468b1ead119000477db2ca797fac19eda3f58c1 + 024100e2ab760841bb9d30a81d222de1eb7381d82214407f1b975cbbfe4e1a9467fd98adbd78f607836ca5be1928b9d160d97fd45c12d6b52e2c9871a174c66b488113 + 024100c5ab27602159ae7d6f20c3c2ee851e46dc112e689e28d5fcbbf990a99ef8a90b8bb44fd36467e7fc1789ceb663abda338652c3c73f111774902e840565927091 + 024100b6cdbd354f7df579a63b48b3643e353b84898777b48b15f94e0bfc0567a6ae5911d57ad6409cf7647bf96264e9bd87eb95e263b7110b9a1f9f94acced0fafa4d + 024071195eec37e8d257decfc672b07ae639f10cbb9b0c739d0c809968d644a94e3fd6ed9287077a14583f379058f76a8aecd43c62dc8c0f41766650d725275ac4a1 + 024100bb32d133edc2e048d463388b7be9cb4be29f4b6250be603e70e3647501c97ddde20a4e71be95fd5e71784e25aca4baf25be5738aae59bbfe1c997781447a2b24 +""", """ + 308189 + 02818100af057d396ee84fb75fdbb5c2b13c7fe5a654aa8aa2470b541ee1feb0b12d25c79711531249e1129628042dbbb6c120d1443524ef4c0e6e1d8956eeb2077af12349ddeee54483bc06c2c61948cd02b202e796aebd94d3a7cbf859c2c1819c324cb82b9cd34ede263a2abffe4733f077869e8660f7d6834da53d690ef7985f6bc3 + 0203010001 +"""), + 1536: (""" +3082037b + 020100 + 0281c100c870feb6ca6b1d2bd9f2dd99e20f1fe2d7e5192de662229dbe162bd1ba66336a7182903ca0b72796cd441c83d24bcdc3e9a2f5e4399c8a043f1c3ddf04754a66d4cfe7b3671a37dd31a9b4c13bfe06ee90f9d94ddaa06de67a52ac863e68f756736ceb014405a6160579640f831dddccc34ad0b05070e3f9954a58d1815813e1b83bcadba814789c87f1ef2ba5d738b793ec456a67360eea1b5faf1c7cc7bf24f3b2a9d0f8958b1096e0f0c335f8888d0c63a51c3c0337214fa3f5efdf6dcc35 + 0203010001 + 0281c06d2d670047973a87752a9d5bc14f3dae00acb01f593aa0e24cf4a49f932931de4bbfb332e2d38083da80bc0b6d538edba479f7f77d0deffb4a28e6e67ff6273585bb4cd862535c946605ab0809d65f0e38f76e4ec2c3d9b8cd6e14bcf667943892cd4b34cc6420a439abbf3d7d35ef73976dd6f9cbde35a51fa5213f0107f83e3425835d16d3c9146fc9e36ce75a09bb66cdff21dd5a776899f1cb07e282cca27be46510e9c799f0d8db275a6be085d9f3f803218ee3384265bfb1a3640e8ca1 + 026100e6848c31d466fffefc547e3a3b0d3785de6f78b0dd12610843512e495611a0675509b1650b27415009838dd8e68eec6e7530553b637d602424643b33e8bc5b762e1799bc79d56b13251d36d4f201da2182416ce13574e88278ff04467ad602d9 + 026100de994fdf181f02be2bf9e5f5e4e517a94993b827d1eaf609033e3a6a6f2396ae7c44e9eb594cf1044cb3ad32ea258f0c82963b27bb650ed200cde82cb993374be34be5b1c7ead5446a2b82a4486e8c1810a0b01551609fb0841d474bada802bd + 026076ddae751b73a959d0bfb8ff49e7fcd378e9be30652ecefe35c82cb8003bc29cc60ae3809909baf20c95db9516fe680865417111d8b193dbcf30281f1249de57c858bf1ba32f5bb1599800e8398a9ef25c7a642c95261da6f9c17670e97265b1 + 0260732482b837d5f2a9443e23c1aa0106d83e82f6c3424673b5fdc3769c0f992d1c5c93991c7038e882fcda04414df4d7a5f4f698ead87851ce37344b60b72d7b70f9c60cae8566e7a257f8e1bef0e89df6e4c2f9d24d21d9f8889e4c7eccf91751 + 026009050d94493da8f00a4ddbe9c800afe3d44b43f78a48941a79b2814a1f0b81a18a8b2347642a03b27998f5a18de9abc9ae0e54ab8294feac66dc87e854cce6f7278ac2710cb5878b592ffeb1f4f0a1853e4e8d1d0561b6efcc831a296cf7eeaf +""", """ +3081c9 + 0281c100c870feb6ca6b1d2bd9f2dd99e20f1fe2d7e5192de662229dbe162bd1ba66336a7182903ca0b72796cd441c83d24bcdc3e9a2f5e4399c8a043f1c3ddf04754a66d4cfe7b3671a37dd31a9b4c13bfe06ee90f9d94ddaa06de67a52ac863e68f756736ceb014405a6160579640f831dddccc34ad0b05070e3f9954a58d1815813e1b83bcadba814789c87f1ef2ba5d738b793ec456a67360eea1b5faf1c7cc7bf24f3b2a9d0f8958b1096e0f0c335f8888d0c63a51c3c0337214fa3f5efdf6dcc35 + 0203010001 +"""), + }, +}) diff --git a/lib/mbedtls-2.27.0/scripts/mbedtls_dev/c_build_helper.py b/lib/mbedtls-2.27.0/scripts/mbedtls_dev/c_build_helper.py new file mode 100644 index 0000000..5c587a1 --- /dev/null +++ b/lib/mbedtls-2.27.0/scripts/mbedtls_dev/c_build_helper.py @@ -0,0 +1,138 @@ +"""Generate and run C code. +""" + +# Copyright The Mbed TLS Contributors +# SPDX-License-Identifier: Apache-2.0 +# +# 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 +# +# http://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. + +import os +import platform +import subprocess +import sys +import tempfile + +def remove_file_if_exists(filename): + """Remove the specified file, ignoring errors.""" + if not filename: + return + try: + os.remove(filename) + except OSError: + pass + +def create_c_file(file_label): + """Create a temporary C file. + + * ``file_label``: a string that will be included in the file name. + + Return ```(c_file, c_name, exe_name)``` where ``c_file`` is a Python + stream open for writing to the file, ``c_name`` is the name of the file + and ``exe_name`` is the name of the executable that will be produced + by compiling the file. + """ + c_fd, c_name = tempfile.mkstemp(prefix='tmp-{}-'.format(file_label), + suffix='.c') + exe_suffix = '.exe' if platform.system() == 'Windows' else '' + exe_name = c_name[:-2] + exe_suffix + remove_file_if_exists(exe_name) + c_file = os.fdopen(c_fd, 'w', encoding='ascii') + return c_file, c_name, exe_name + +def generate_c_printf_expressions(c_file, cast_to, printf_format, expressions): + """Generate C instructions to print the value of ``expressions``. + + Write the code with ``c_file``'s ``write`` method. + + Each expression is cast to the type ``cast_to`` and printed with the + printf format ``printf_format``. + """ + for expr in expressions: + c_file.write(' printf("{}\\n", ({}) {});\n' + .format(printf_format, cast_to, expr)) + +def generate_c_file(c_file, + caller, header, + main_generator): + """Generate a temporary C source file. + + * ``c_file`` is an open stream on the C source file. + * ``caller``: an informational string written in a comment at the top + of the file. + * ``header``: extra code to insert before any function in the generated + C file. + * ``main_generator``: a function called with ``c_file`` as its sole argument + to generate the body of the ``main()`` function. + """ + c_file.write('/* Generated by {} */' + .format(caller)) + c_file.write(''' +#include <stdio.h> +''') + c_file.write(header) + c_file.write(''' +int main(void) +{ +''') + main_generator(c_file) + c_file.write(''' return 0; +} +''') + +def get_c_expression_values( + cast_to, printf_format, + expressions, + caller=__name__, file_label='', + header='', include_path=None, + keep_c=False, +): # pylint: disable=too-many-arguments + """Generate and run a program to print out numerical values for expressions. + + * ``cast_to``: a C type. + * ``printf_format``: a printf format suitable for the type ``cast_to``. + * ``header``: extra code to insert before any function in the generated + C file. + * ``expressions``: a list of C language expressions that have the type + ``cast_to``. + * ``include_path``: a list of directories containing header files. + * ``keep_c``: if true, keep the temporary C file (presumably for debugging + purposes). + + Return the list of values of the ``expressions``. + """ + if include_path is None: + include_path = [] + c_name = None + exe_name = None + try: + c_file, c_name, exe_name = create_c_file(file_label) + generate_c_file( + c_file, caller, header, + lambda c_file: generate_c_printf_expressions(c_file, + cast_to, printf_format, + expressions) + ) + c_file.close() + cc = os.getenv('CC', 'cc') + subprocess.check_call([cc] + + ['-I' + dir for dir in include_path] + + ['-o', exe_name, c_name]) + if keep_c: + sys.stderr.write('List of {} tests kept at {}\n' + .format(caller, c_name)) + else: + os.remove(c_name) + output = subprocess.check_output([exe_name]) + return output.decode('ascii').strip().split('\n') + finally: + remove_file_if_exists(exe_name) diff --git a/lib/mbedtls-2.27.0/scripts/mbedtls_dev/crypto_knowledge.py b/lib/mbedtls-2.27.0/scripts/mbedtls_dev/crypto_knowledge.py new file mode 100644 index 0000000..7214adb --- /dev/null +++ b/lib/mbedtls-2.27.0/scripts/mbedtls_dev/crypto_knowledge.py @@ -0,0 +1,153 @@ +"""Knowledge about cryptographic mechanisms implemented in Mbed TLS. + +This module is entirely based on the PSA API. +""" + +# Copyright The Mbed TLS Contributors +# SPDX-License-Identifier: Apache-2.0 +# +# 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 +# +# http://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. + +import re +from typing import Dict, Iterable, Optional, Pattern, Tuple + +from mbedtls_dev.asymmetric_key_data import ASYMMETRIC_KEY_DATA + +class KeyType: + """Knowledge about a PSA key type.""" + + def __init__(self, name: str, params: Optional[Iterable[str]] = None): + """Analyze a key type. + + The key type must be specified in PSA syntax. In its simplest form, + `name` is a string 'PSA_KEY_TYPE_xxx' which is the name of a PSA key + type macro. For key types that take arguments, the arguments can + be passed either through the optional argument `params` or by + passing an expression of the form 'PSA_KEY_TYPE_xxx(param1, ...)' + in `name` as a string. + """ + + self.name = name.strip() + """The key type macro name (``PSA_KEY_TYPE_xxx``). + + For key types constructed from a macro with arguments, this is the + name of the macro, and the arguments are in `self.params`. + """ + if params is None: + if '(' in self.name: + m = re.match(r'(\w+)\s*\((.*)\)\Z', self.name) + assert m is not None + self.name = m.group(1) + params = m.group(2).split(',') + self.params = (None if params is None else + [param.strip() for param in params]) + """The parameters of the key type, if there are any. + + None if the key type is a macro without arguments. + """ + assert re.match(r'PSA_KEY_TYPE_\w+\Z', self.name) + + self.expression = self.name + """A C expression whose value is the key type encoding.""" + if self.params is not None: + self.expression += '(' + ', '.join(self.params) + ')' + + self.private_type = re.sub(r'_PUBLIC_KEY\Z', r'_KEY_PAIR', self.name) + """The key type macro name for the corresponding key pair type. + + For everything other than a public key type, this is the same as + `self.name`. + """ + + ECC_KEY_SIZES = { + 'PSA_ECC_FAMILY_SECP_K1': (192, 224, 256), + 'PSA_ECC_FAMILY_SECP_R1': (225, 256, 384, 521), + 'PSA_ECC_FAMILY_SECP_R2': (160,), + 'PSA_ECC_FAMILY_SECT_K1': (163, 233, 239, 283, 409, 571), + 'PSA_ECC_FAMILY_SECT_R1': (163, 233, 283, 409, 571), + 'PSA_ECC_FAMILY_SECT_R2': (163,), + 'PSA_ECC_FAMILY_BRAINPOOL_P_R1': (160, 192, 224, 256, 320, 384, 512), + 'PSA_ECC_FAMILY_MONTGOMERY': (255, 448), + 'PSA_ECC_FAMILY_TWISTED_EDWARDS': (255, 448), + } + KEY_TYPE_SIZES = { + 'PSA_KEY_TYPE_AES': (128, 192, 256), # exhaustive + 'PSA_KEY_TYPE_ARC4': (8, 128, 2048), # extremes + sensible + 'PSA_KEY_TYPE_ARIA': (128, 192, 256), # exhaustive + 'PSA_KEY_TYPE_CAMELLIA': (128, 192, 256), # exhaustive + 'PSA_KEY_TYPE_CHACHA20': (256,), # exhaustive + 'PSA_KEY_TYPE_DERIVE': (120, 128), # sample + 'PSA_KEY_TYPE_DES': (64, 128, 192), # exhaustive + 'PSA_KEY_TYPE_HMAC': (128, 160, 224, 256, 384, 512), # standard size for each supported hash + 'PSA_KEY_TYPE_RAW_DATA': (8, 40, 128), # sample + 'PSA_KEY_TYPE_RSA_KEY_PAIR': (1024, 1536), # small sample + } + def sizes_to_test(self) -> Tuple[int, ...]: + """Return a tuple of key sizes to test. + + For key types that only allow a single size, or only a small set of + sizes, these are all the possible sizes. For key types that allow a + wide range of sizes, these are a representative sample of sizes, + excluding large sizes for which a typical resource-constrained platform + may run out of memory. + """ + if self.private_type == 'PSA_KEY_TYPE_ECC_KEY_PAIR': + assert self.params is not None + return self.ECC_KEY_SIZES[self.params[0]] + return self.KEY_TYPE_SIZES[self.private_type] + + # "48657265006973206b6579a064617461" + DATA_BLOCK = b'Here\000is key\240data' + def key_material(self, bits: int) -> bytes: + """Return a byte string containing suitable key material with the given bit length. + + Use the PSA export representation. The resulting byte string is one that + can be obtained with the following code: + ``` + psa_set_key_type(&attributes, `self.expression`); + psa_set_key_bits(&attributes, `bits`); + psa_set_key_usage_flags(&attributes, PSA_KEY_USAGE_EXPORT); + psa_generate_key(&attributes, &id); + psa_export_key(id, `material`, ...); + ``` + """ + if self.expression in ASYMMETRIC_KEY_DATA: + if bits not in ASYMMETRIC_KEY_DATA[self.expression]: + raise ValueError('No key data for {}-bit {}' + .format(bits, self.expression)) + return ASYMMETRIC_KEY_DATA[self.expression][bits] + if bits % 8 != 0: + raise ValueError('Non-integer number of bytes: {} bits for {}' + .format(bits, self.expression)) + length = bits // 8 + if self.name == 'PSA_KEY_TYPE_DES': + # "644573206b457901644573206b457902644573206b457904" + des3 = b'dEs kEy\001dEs kEy\002dEs kEy\004' + return des3[:length] + return b''.join([self.DATA_BLOCK] * (length // len(self.DATA_BLOCK)) + + [self.DATA_BLOCK[:length % len(self.DATA_BLOCK)]]) + + KEY_TYPE_FOR_SIGNATURE = { + 'PSA_KEY_USAGE_SIGN_HASH': re.compile('.*KEY_PAIR'), + 'PSA_KEY_USAGE_VERIFY_HASH': re.compile('.*KEY.*') + } #type: Dict[str, Pattern] + """Use a regexp to determine key types for which signature is possible + when using the actual usage flag. + """ + def is_valid_for_signature(self, usage: str) -> bool: + """Determine if the key type is compatible with the specified + signitute type. + + """ + # This is just temporaly solution for the implicit usage flags. + return re.match(self.KEY_TYPE_FOR_SIGNATURE[usage], self.name) is not None diff --git a/lib/mbedtls-2.27.0/scripts/mbedtls_dev/macro_collector.py b/lib/mbedtls-2.27.0/scripts/mbedtls_dev/macro_collector.py new file mode 100644 index 0000000..f8d6155 --- /dev/null +++ b/lib/mbedtls-2.27.0/scripts/mbedtls_dev/macro_collector.py @@ -0,0 +1,512 @@ +"""Collect macro definitions from header files. +""" + +# Copyright The Mbed TLS Contributors +# SPDX-License-Identifier: Apache-2.0 +# +# 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 +# +# http://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. + +import itertools +import re +from typing import Dict, Iterable, Iterator, List, Optional, Pattern, Set, Tuple, Union + + +class ReadFileLineException(Exception): + def __init__(self, filename: str, line_number: Union[int, str]) -> None: + message = 'in {} at {}'.format(filename, line_number) + super(ReadFileLineException, self).__init__(message) + self.filename = filename + self.line_number = line_number + + +class read_file_lines: + # Dear Pylint, conventionally, a context manager class name is lowercase. + # pylint: disable=invalid-name,too-few-public-methods + """Context manager to read a text file line by line. + + ``` + with read_file_lines(filename) as lines: + for line in lines: + process(line) + ``` + is equivalent to + ``` + with open(filename, 'r') as input_file: + for line in input_file: + process(line) + ``` + except that if process(line) raises an exception, then the read_file_lines + snippet annotates the exception with the file name and line number. + """ + def __init__(self, filename: str, binary: bool = False) -> None: + self.filename = filename + self.line_number = 'entry' #type: Union[int, str] + self.generator = None #type: Optional[Iterable[Tuple[int, str]]] + self.binary = binary + def __enter__(self) -> 'read_file_lines': + self.generator = enumerate(open(self.filename, + 'rb' if self.binary else 'r')) + return self + def __iter__(self) -> Iterator[str]: + assert self.generator is not None + for line_number, content in self.generator: + self.line_number = line_number + yield content + self.line_number = 'exit' + def __exit__(self, exc_type, exc_value, exc_traceback) -> None: + if exc_type is not None: + raise ReadFileLineException(self.filename, self.line_number) \ + from exc_value + + +class PSAMacroEnumerator: + """Information about constructors of various PSA Crypto types. + + This includes macro names as well as information about their arguments + when applicable. + + This class only provides ways to enumerate expressions that evaluate to + values of the covered types. Derived classes are expected to populate + the set of known constructors of each kind, as well as populate + `self.arguments_for` for arguments that are not of a kind that is + enumerated here. + """ + #pylint: disable=too-many-instance-attributes + + def __init__(self) -> None: + """Set up an empty set of known constructor macros. + """ + self.statuses = set() #type: Set[str] + self.lifetimes = set() #type: Set[str] + self.locations = set() #type: Set[str] + self.persistence_levels = set() #type: Set[str] + self.algorithms = set() #type: Set[str] + self.ecc_curves = set() #type: Set[str] + self.dh_groups = set() #type: Set[str] + self.key_types = set() #type: Set[str] + self.key_usage_flags = set() #type: Set[str] + self.hash_algorithms = set() #type: Set[str] + self.mac_algorithms = set() #type: Set[str] + self.ka_algorithms = set() #type: Set[str] + self.kdf_algorithms = set() #type: Set[str] + self.aead_algorithms = set() #type: Set[str] + self.sign_algorithms = set() #type: Set[str] + # macro name -> list of argument names + self.argspecs = {} #type: Dict[str, List[str]] + # argument name -> list of values + self.arguments_for = { + 'mac_length': [], + 'min_mac_length': [], + 'tag_length': [], + 'min_tag_length': [], + } #type: Dict[str, List[str]] + # Whether to include intermediate macros in enumerations. Intermediate + # macros serve as category headers and are not valid values of their + # type. See `is_internal_name`. + # Always false in this class, may be set to true in derived classes. + self.include_intermediate = False + + def is_internal_name(self, name: str) -> bool: + """Whether this is an internal macro. Internal macros will be skipped.""" + if not self.include_intermediate: + if name.endswith('_BASE') or name.endswith('_NONE'): + return True + if '_CATEGORY_' in name: + return True + return name.endswith('_FLAG') or name.endswith('_MASK') + + def gather_arguments(self) -> None: + """Populate the list of values for macro arguments. + + Call this after parsing all the inputs. + """ + self.arguments_for['hash_alg'] = sorted(self.hash_algorithms) + self.arguments_for['mac_alg'] = sorted(self.mac_algorithms) + self.arguments_for['ka_alg'] = sorted(self.ka_algorithms) + self.arguments_for['kdf_alg'] = sorted(self.kdf_algorithms) + self.arguments_for['aead_alg'] = sorted(self.aead_algorithms) + self.arguments_for['sign_alg'] = sorted(self.sign_algorithms) + self.arguments_for['curve'] = sorted(self.ecc_curves) + self.arguments_for['group'] = sorted(self.dh_groups) + self.arguments_for['persistence'] = sorted(self.persistence_levels) + self.arguments_for['location'] = sorted(self.locations) + self.arguments_for['lifetime'] = sorted(self.lifetimes) + + @staticmethod + def _format_arguments(name: str, arguments: Iterable[str]) -> str: + """Format a macro call with arguments. + + The resulting format is consistent with + `InputsForTest.normalize_argument`. + """ + return name + '(' + ', '.join(arguments) + ')' + + _argument_split_re = re.compile(r' *, *') + @classmethod + def _argument_split(cls, arguments: str) -> List[str]: + return re.split(cls._argument_split_re, arguments) + + def distribute_arguments(self, name: str) -> Iterator[str]: + """Generate macro calls with each tested argument set. + + If name is a macro without arguments, just yield "name". + If name is a macro with arguments, yield a series of + "name(arg1,...,argN)" where each argument takes each possible + value at least once. + """ + try: + if name not in self.argspecs: + yield name + return + argspec = self.argspecs[name] + if argspec == []: + yield name + '()' + return + argument_lists = [self.arguments_for[arg] for arg in argspec] + arguments = [values[0] for values in argument_lists] + yield self._format_arguments(name, arguments) + # Dear Pylint, enumerate won't work here since we're modifying + # the array. + # pylint: disable=consider-using-enumerate + for i in range(len(arguments)): + for value in argument_lists[i][1:]: + arguments[i] = value + yield self._format_arguments(name, arguments) + arguments[i] = argument_lists[0][0] + except BaseException as e: + raise Exception('distribute_arguments({})'.format(name)) from e + + def distribute_arguments_without_duplicates( + self, seen: Set[str], name: str + ) -> Iterator[str]: + """Same as `distribute_arguments`, but don't repeat seen results.""" + for result in self.distribute_arguments(name): + if result not in seen: + seen.add(result) + yield result + + def generate_expressions(self, names: Iterable[str]) -> Iterator[str]: + """Generate expressions covering values constructed from the given names. + + `names` can be any iterable collection of macro names. + + For example: + * ``generate_expressions(['PSA_ALG_CMAC', 'PSA_ALG_HMAC'])`` + generates ``'PSA_ALG_CMAC'`` as well as ``'PSA_ALG_HMAC(h)'`` for + every known hash algorithm ``h``. + * ``macros.generate_expressions(macros.key_types)`` generates all + key types. + """ + seen = set() #type: Set[str] + return itertools.chain(*( + self.distribute_arguments_without_duplicates(seen, name) + for name in names + )) + + +class PSAMacroCollector(PSAMacroEnumerator): + """Collect PSA crypto macro definitions from C header files. + """ + + def __init__(self, include_intermediate: bool = False) -> None: + """Set up an object to collect PSA macro definitions. + + Call the read_file method of the constructed object on each header file. + + * include_intermediate: if true, include intermediate macros such as + PSA_XXX_BASE that do not designate semantic values. + """ + super().__init__() + self.include_intermediate = include_intermediate + self.key_types_from_curve = {} #type: Dict[str, str] + self.key_types_from_group = {} #type: Dict[str, str] + self.algorithms_from_hash = {} #type: Dict[str, str] + + def record_algorithm_subtype(self, name: str, expansion: str) -> None: + """Record the subtype of an algorithm constructor. + + Given a ``PSA_ALG_xxx`` macro name and its expansion, if the algorithm + is of a subtype that is tracked in its own set, add it to the relevant + set. + """ + # This code is very ad hoc and fragile. It should be replaced by + # something more robust. + if re.match(r'MAC(?:_|\Z)', name): + self.mac_algorithms.add(name) + elif re.match(r'KDF(?:_|\Z)', name): + self.kdf_algorithms.add(name) + elif re.search(r'0x020000[0-9A-Fa-f]{2}', expansion): + self.hash_algorithms.add(name) + elif re.search(r'0x03[0-9A-Fa-f]{6}', expansion): + self.mac_algorithms.add(name) + elif re.search(r'0x05[0-9A-Fa-f]{6}', expansion): + self.aead_algorithms.add(name) + elif re.search(r'0x09[0-9A-Fa-f]{2}0000', expansion): + self.ka_algorithms.add(name) + elif re.search(r'0x08[0-9A-Fa-f]{6}', expansion): + self.kdf_algorithms.add(name) + + # "#define" followed by a macro name with either no parameters + # or a single parameter and a non-empty expansion. + # Grab the macro name in group 1, the parameter name if any in group 2 + # and the expansion in group 3. + _define_directive_re = re.compile(r'\s*#\s*define\s+(\w+)' + + r'(?:\s+|\((\w+)\)\s*)' + + r'(.+)') + _deprecated_definition_re = re.compile(r'\s*MBEDTLS_DEPRECATED') + + def read_line(self, line): + """Parse a C header line and record the PSA identifier it defines if any. + This function analyzes lines that start with "#define PSA_" + (up to non-significant whitespace) and skips all non-matching lines. + """ + # pylint: disable=too-many-branches + m = re.match(self._define_directive_re, line) + if not m: + return + name, parameter, expansion = m.groups() + expansion = re.sub(r'/\*.*?\*/|//.*', r' ', expansion) + if parameter: + self.argspecs[name] = [parameter] + if re.match(self._deprecated_definition_re, expansion): + # Skip deprecated values, which are assumed to be + # backward compatibility aliases that share + # numerical values with non-deprecated values. + return + if self.is_internal_name(name): + # Macro only to build actual values + return + elif (name.startswith('PSA_ERROR_') or name == 'PSA_SUCCESS') \ + and not parameter: + self.statuses.add(name) + elif name.startswith('PSA_KEY_TYPE_') and not parameter: + self.key_types.add(name) + elif name.startswith('PSA_KEY_TYPE_') and parameter == 'curve': + self.key_types_from_curve[name] = name[:13] + 'IS_' + name[13:] + elif name.startswith('PSA_KEY_TYPE_') and parameter == 'group': + self.key_types_from_group[name] = name[:13] + 'IS_' + name[13:] + elif name.startswith('PSA_ECC_FAMILY_') and not parameter: + self.ecc_curves.add(name) + elif name.startswith('PSA_DH_FAMILY_') and not parameter: + self.dh_groups.add(name) + elif name.startswith('PSA_ALG_') and not parameter: + if name in ['PSA_ALG_ECDSA_BASE', + 'PSA_ALG_RSA_PKCS1V15_SIGN_BASE']: + # Ad hoc skipping of duplicate names for some numerical values + return + self.algorithms.add(name) + self.record_algorithm_subtype(name, expansion) + elif name.startswith('PSA_ALG_') and parameter == 'hash_alg': + if name in ['PSA_ALG_DSA', 'PSA_ALG_ECDSA']: + # A naming irregularity + tester = name[:8] + 'IS_RANDOMIZED_' + name[8:] + else: + tester = name[:8] + 'IS_' + name[8:] + self.algorithms_from_hash[name] = tester + elif name.startswith('PSA_KEY_USAGE_') and not parameter: + self.key_usage_flags.add(name) + else: + # Other macro without parameter + return + + _nonascii_re = re.compile(rb'[^\x00-\x7f]+') + _continued_line_re = re.compile(rb'\\\r?\n\Z') + def read_file(self, header_file): + for line in header_file: + m = re.search(self._continued_line_re, line) + while m: + cont = next(header_file) + line = line[:m.start(0)] + cont + m = re.search(self._continued_line_re, line) + line = re.sub(self._nonascii_re, rb'', line).decode('ascii') + self.read_line(line) + + +class InputsForTest(PSAMacroEnumerator): + # pylint: disable=too-many-instance-attributes + """Accumulate information about macros to test. +enumerate + This includes macro names as well as information about their arguments + when applicable. + """ + + def __init__(self) -> None: + super().__init__() + self.all_declared = set() #type: Set[str] + # Identifier prefixes + self.table_by_prefix = { + 'ERROR': self.statuses, + 'ALG': self.algorithms, + 'ECC_CURVE': self.ecc_curves, + 'DH_GROUP': self.dh_groups, + 'KEY_LIFETIME': self.lifetimes, + 'KEY_LOCATION': self.locations, + 'KEY_PERSISTENCE': self.persistence_levels, + 'KEY_TYPE': self.key_types, + 'KEY_USAGE': self.key_usage_flags, + } #type: Dict[str, Set[str]] + # Test functions + self.table_by_test_function = { + # Any function ending in _algorithm also gets added to + # self.algorithms. + 'key_type': [self.key_types], + 'block_cipher_key_type': [self.key_types], + 'stream_cipher_key_type': [self.key_types], + 'ecc_key_family': [self.ecc_curves], + 'ecc_key_types': [self.ecc_curves], + 'dh_key_family': [self.dh_groups], + 'dh_key_types': [self.dh_groups], + 'hash_algorithm': [self.hash_algorithms], + 'mac_algorithm': [self.mac_algorithms], + 'cipher_algorithm': [], + 'hmac_algorithm': [self.mac_algorithms, self.sign_algorithms], + 'aead_algorithm': [self.aead_algorithms], + 'key_derivation_algorithm': [self.kdf_algorithms], + 'key_agreement_algorithm': [self.ka_algorithms], + 'asymmetric_signature_algorithm': [self.sign_algorithms], + 'asymmetric_signature_wildcard': [self.algorithms], + 'asymmetric_encryption_algorithm': [], + 'other_algorithm': [], + 'lifetime': [self.lifetimes], + } #type: Dict[str, List[Set[str]]] + self.arguments_for['mac_length'] += ['1', '63'] + self.arguments_for['min_mac_length'] += ['1', '63'] + self.arguments_for['tag_length'] += ['1', '63'] + self.arguments_for['min_tag_length'] += ['1', '63'] + + def add_numerical_values(self) -> None: + """Add numerical values that are not supported to the known identifiers.""" + # Sets of names per type + self.algorithms.add('0xffffffff') + self.ecc_curves.add('0xff') + self.dh_groups.add('0xff') + self.key_types.add('0xffff') + self.key_usage_flags.add('0x80000000') + + # Hard-coded values for unknown algorithms + # + # These have to have values that are correct for their respective + # PSA_ALG_IS_xxx macros, but are also not currently assigned and are + # not likely to be assigned in the near future. + self.hash_algorithms.add('0x020000fe') # 0x020000ff is PSA_ALG_ANY_HASH + self.mac_algorithms.add('0x03007fff') + self.ka_algorithms.add('0x09fc0000') + self.kdf_algorithms.add('0x080000ff') + # For AEAD algorithms, the only variability is over the tag length, + # and this only applies to known algorithms, so don't test an + # unknown algorithm. + + def get_names(self, type_word: str) -> Set[str]: + """Return the set of known names of values of the given type.""" + return { + 'status': self.statuses, + 'algorithm': self.algorithms, + 'ecc_curve': self.ecc_curves, + 'dh_group': self.dh_groups, + 'key_type': self.key_types, + 'key_usage': self.key_usage_flags, + }[type_word] + + # Regex for interesting header lines. + # Groups: 1=macro name, 2=type, 3=argument list (optional). + _header_line_re = \ + re.compile(r'#define +' + + r'(PSA_((?:(?:DH|ECC|KEY)_)?[A-Z]+)_\w+)' + + r'(?:\(([^\n()]*)\))?') + # Regex of macro names to exclude. + _excluded_name_re = re.compile(r'_(?:GET|IS|OF)_|_(?:BASE|FLAG|MASK)\Z') + # Additional excluded macros. + _excluded_names = set([ + # Macros that provide an alternative way to build the same + # algorithm as another macro. + 'PSA_ALG_AEAD_WITH_DEFAULT_LENGTH_TAG', + 'PSA_ALG_FULL_LENGTH_MAC', + # Auxiliary macro whose name doesn't fit the usual patterns for + # auxiliary macros. + 'PSA_ALG_AEAD_WITH_DEFAULT_LENGTH_TAG_CASE', + ]) + def parse_header_line(self, line: str) -> None: + """Parse a C header line, looking for "#define PSA_xxx".""" + m = re.match(self._header_line_re, line) + if not m: + return + name = m.group(1) + self.all_declared.add(name) + if re.search(self._excluded_name_re, name) or \ + name in self._excluded_names or \ + self.is_internal_name(name): + return + dest = self.table_by_prefix.get(m.group(2)) + if dest is None: + return + dest.add(name) + if m.group(3): + self.argspecs[name] = self._argument_split(m.group(3)) + + _nonascii_re = re.compile(rb'[^\x00-\x7f]+') #type: Pattern + def parse_header(self, filename: str) -> None: + """Parse a C header file, looking for "#define PSA_xxx".""" + with read_file_lines(filename, binary=True) as lines: + for line in lines: + line = re.sub(self._nonascii_re, rb'', line).decode('ascii') + self.parse_header_line(line) + + _macro_identifier_re = re.compile(r'[A-Z]\w+') + def generate_undeclared_names(self, expr: str) -> Iterable[str]: + for name in re.findall(self._macro_identifier_re, expr): + if name not in self.all_declared: + yield name + + def accept_test_case_line(self, function: str, argument: str) -> bool: + #pylint: disable=unused-argument + undeclared = list(self.generate_undeclared_names(argument)) + if undeclared: + raise Exception('Undeclared names in test case', undeclared) + return True + + @staticmethod + def normalize_argument(argument: str) -> str: + """Normalize whitespace in the given C expression. + + The result uses the same whitespace as + ` PSAMacroEnumerator.distribute_arguments`. + """ + return re.sub(r',', r', ', re.sub(r' +', r'', argument)) + + def add_test_case_line(self, function: str, argument: str) -> None: + """Parse a test case data line, looking for algorithm metadata tests.""" + sets = [] + if function.endswith('_algorithm'): + sets.append(self.algorithms) + if function == 'key_agreement_algorithm' and \ + argument.startswith('PSA_ALG_KEY_AGREEMENT('): + # We only want *raw* key agreement algorithms as such, so + # exclude ones that are already chained with a KDF. + # Keep the expression as one to test as an algorithm. + function = 'other_algorithm' + sets += self.table_by_test_function[function] + if self.accept_test_case_line(function, argument): + for s in sets: + s.add(self.normalize_argument(argument)) + + # Regex matching a *.data line containing a test function call and + # its arguments. The actual definition is partly positional, but this + # regex is good enough in practice. + _test_case_line_re = re.compile(r'(?!depends_on:)(\w+):([^\n :][^:\n]*)') + def parse_test_cases(self, filename: str) -> None: + """Parse a test case file (*.data), looking for algorithm metadata tests.""" + with read_file_lines(filename) as lines: + for line in lines: + m = re.match(self._test_case_line_re, line) + if m: + self.add_test_case_line(m.group(1), m.group(2)) diff --git a/lib/mbedtls-2.27.0/scripts/mbedtls_dev/psa_storage.py b/lib/mbedtls-2.27.0/scripts/mbedtls_dev/psa_storage.py new file mode 100644 index 0000000..45f0380 --- /dev/null +++ b/lib/mbedtls-2.27.0/scripts/mbedtls_dev/psa_storage.py @@ -0,0 +1,203 @@ +"""Knowledge about the PSA key store as implemented in Mbed TLS. +""" + +# Copyright The Mbed TLS Contributors +# SPDX-License-Identifier: Apache-2.0 +# +# 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 +# +# http://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. + +import re +import struct +from typing import Dict, List, Optional, Set, Union +import unittest + +from mbedtls_dev import c_build_helper + + +class Expr: + """Representation of a C expression with a known or knowable numerical value.""" + + def __init__(self, content: Union[int, str]): + if isinstance(content, int): + digits = 8 if content > 0xffff else 4 + self.string = '{0:#0{1}x}'.format(content, digits + 2) + self.value_if_known = content #type: Optional[int] + else: + self.string = content + self.unknown_values.add(self.normalize(content)) + self.value_if_known = None + + value_cache = {} #type: Dict[str, int] + """Cache of known values of expressions.""" + + unknown_values = set() #type: Set[str] + """Expressions whose values are not present in `value_cache` yet.""" + + def update_cache(self) -> None: + """Update `value_cache` for expressions registered in `unknown_values`.""" + expressions = sorted(self.unknown_values) + values = c_build_helper.get_c_expression_values( + 'unsigned long', '%lu', + expressions, + header=""" + #include <psa/crypto.h> + """, + include_path=['include']) #type: List[str] + for e, v in zip(expressions, values): + self.value_cache[e] = int(v, 0) + self.unknown_values.clear() + + @staticmethod + def normalize(string: str) -> str: + """Put the given C expression in a canonical form. + + This function is only intended to give correct results for the + relatively simple kind of C expression typically used with this + module. + """ + return re.sub(r'\s+', r'', string) + + def value(self) -> int: + """Return the numerical value of the expression.""" + if self.value_if_known is None: + if re.match(r'([0-9]+|0x[0-9a-f]+)\Z', self.string, re.I): + return int(self.string, 0) + normalized = self.normalize(self.string) + if normalized not in self.value_cache: + self.update_cache() + self.value_if_known = self.value_cache[normalized] + return self.value_if_known + +Exprable = Union[str, int, Expr] +"""Something that can be converted to a C expression with a known numerical value.""" + +def as_expr(thing: Exprable) -> Expr: + """Return an `Expr` object for `thing`. + + If `thing` is already an `Expr` object, return it. Otherwise build a new + `Expr` object from `thing`. `thing` can be an integer or a string that + contains a C expression. + """ + if isinstance(thing, Expr): + return thing + else: + return Expr(thing) + + +class Key: + """Representation of a PSA crypto key object and its storage encoding. + """ + + LATEST_VERSION = 0 + """The latest version of the storage format.""" + + def __init__(self, *, + version: Optional[int] = None, + id: Optional[int] = None, #pylint: disable=redefined-builtin + lifetime: Exprable = 'PSA_KEY_LIFETIME_PERSISTENT', + type: Exprable, #pylint: disable=redefined-builtin + bits: int, + usage: Exprable, alg: Exprable, alg2: Exprable, + material: bytes #pylint: disable=used-before-assignment + ) -> None: + self.version = self.LATEST_VERSION if version is None else version + self.id = id #pylint: disable=invalid-name #type: Optional[int] + self.lifetime = as_expr(lifetime) #type: Expr + self.type = as_expr(type) #type: Expr + self.bits = bits #type: int + self.usage = as_expr(usage) #type: Expr + self.alg = as_expr(alg) #type: Expr + self.alg2 = as_expr(alg2) #type: Expr + self.material = material #type: bytes + + MAGIC = b'PSA\000KEY\000' + + @staticmethod + def pack( + fmt: str, + *args: Union[int, Expr] + ) -> bytes: #pylint: disable=used-before-assignment + """Pack the given arguments into a byte string according to the given format. + + This function is similar to `struct.pack`, but with the following differences: + * All integer values are encoded with standard sizes and in + little-endian representation. `fmt` must not include an endianness + prefix. + * Arguments can be `Expr` objects instead of integers. + * Only integer-valued elements are supported. + """ + return struct.pack('<' + fmt, # little-endian, standard sizes + *[arg.value() if isinstance(arg, Expr) else arg + for arg in args]) + + def bytes(self) -> bytes: + """Return the representation of the key in storage as a byte array. + + This is the content of the PSA storage file. When PSA storage is + implemented over stdio files, this does not include any wrapping made + by the PSA-storage-over-stdio-file implementation. + """ + header = self.MAGIC + self.pack('L', self.version) + if self.version == 0: + attributes = self.pack('LHHLLL', + self.lifetime, self.type, self.bits, + self.usage, self.alg, self.alg2) + material = self.pack('L', len(self.material)) + self.material + else: + raise NotImplementedError + return header + attributes + material + + def hex(self) -> str: + """Return the representation of the key as a hexadecimal string. + + This is the hexadecimal representation of `self.bytes`. + """ + return self.bytes().hex() + + def location_value(self) -> int: + """The numerical value of the location encoded in the key's lifetime.""" + return self.lifetime.value() >> 8 + + +class TestKey(unittest.TestCase): + # pylint: disable=line-too-long + """A few smoke tests for the functionality of the `Key` class.""" + + def test_numerical(self): + key = Key(version=0, + id=1, lifetime=0x00000001, + type=0x2400, bits=128, + usage=0x00000300, alg=0x05500200, alg2=0x04c01000, + material=b'@ABCDEFGHIJKLMNO') + expected_hex = '505341004b45590000000000010000000024800000030000000250050010c00410000000404142434445464748494a4b4c4d4e4f' + self.assertEqual(key.bytes(), bytes.fromhex(expected_hex)) + self.assertEqual(key.hex(), expected_hex) + + def test_names(self): + length = 0xfff8 // 8 # PSA_MAX_KEY_BITS in bytes + key = Key(version=0, + id=1, lifetime='PSA_KEY_LIFETIME_PERSISTENT', + type='PSA_KEY_TYPE_RAW_DATA', bits=length*8, + usage=0, alg=0, alg2=0, + material=b'\x00' * length) + expected_hex = '505341004b45590000000000010000000110f8ff000000000000000000000000ff1f0000' + '00' * length + self.assertEqual(key.bytes(), bytes.fromhex(expected_hex)) + self.assertEqual(key.hex(), expected_hex) + + def test_defaults(self): + key = Key(type=0x1001, bits=8, + usage=0, alg=0, alg2=0, + material=b'\x2a') + expected_hex = '505341004b455900000000000100000001100800000000000000000000000000010000002a' + self.assertEqual(key.bytes(), bytes.fromhex(expected_hex)) + self.assertEqual(key.hex(), expected_hex) diff --git a/lib/mbedtls-2.27.0/scripts/mbedtls_dev/test_case.py b/lib/mbedtls-2.27.0/scripts/mbedtls_dev/test_case.py new file mode 100644 index 0000000..d01e143 --- /dev/null +++ b/lib/mbedtls-2.27.0/scripts/mbedtls_dev/test_case.py @@ -0,0 +1,102 @@ +"""Library for generating Mbed TLS test data. +""" + +# Copyright The Mbed TLS Contributors +# SPDX-License-Identifier: Apache-2.0 +# +# 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 +# +# http://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. + +import binascii +import os +import sys +from typing import Iterable, List, Optional + +from mbedtls_dev import typing_util + +def hex_string(data: bytes) -> str: + return '"' + binascii.hexlify(data).decode('ascii') + '"' + + +class MissingDescription(Exception): + pass + +class MissingFunction(Exception): + pass + +class TestCase: + """An Mbed TLS test case.""" + + def __init__(self, description: Optional[str] = None): + self.comments = [] #type: List[str] + self.description = description #type: Optional[str] + self.dependencies = [] #type: List[str] + self.function = None #type: Optional[str] + self.arguments = [] #type: List[str] + + def add_comment(self, *lines: str) -> None: + self.comments += lines + + def set_description(self, description: str) -> None: + self.description = description + + def set_dependencies(self, dependencies: List[str]) -> None: + self.dependencies = dependencies + + def set_function(self, function: str) -> None: + self.function = function + + def set_arguments(self, arguments: List[str]) -> None: + self.arguments = arguments + + def check_completeness(self) -> None: + if self.description is None: + raise MissingDescription + if self.function is None: + raise MissingFunction + + def write(self, out: typing_util.Writable) -> None: + """Write the .data file paragraph for this test case. + + The output starts and ends with a single newline character. If the + surrounding code writes lines (consisting of non-newline characters + and a final newline), you will end up with a blank line before, but + not after the test case. + """ + self.check_completeness() + assert self.description is not None # guide mypy + assert self.function is not None # guide mypy + out.write('\n') + for line in self.comments: + out.write('# ' + line + '\n') + out.write(self.description + '\n') + if self.dependencies: + out.write('depends_on:' + ':'.join(self.dependencies) + '\n') + out.write(self.function + ':' + ':'.join(self.arguments) + '\n') + + + +def write_data_file(filename: str, + test_cases: Iterable[TestCase], + caller: Optional[str] = None) -> None: + """Write the test cases to the specified file. + + If the file already exists, it is overwritten. + """ + if caller is None: + caller = os.path.basename(sys.argv[0]) + with open(filename, 'w') as out: + out.write('# Automatically generated by {}. Do not edit!\n' + .format(caller)) + for tc in test_cases: + tc.write(out) + out.write('\n# End of automatically generated file.\n') diff --git a/lib/mbedtls-2.27.0/scripts/mbedtls_dev/typing_util.py b/lib/mbedtls-2.27.0/scripts/mbedtls_dev/typing_util.py new file mode 100644 index 0000000..4c34449 --- /dev/null +++ b/lib/mbedtls-2.27.0/scripts/mbedtls_dev/typing_util.py @@ -0,0 +1,39 @@ +"""Auxiliary definitions used in type annotations. +""" + +# Copyright The Mbed TLS Contributors +# SPDX-License-Identifier: Apache-2.0 +# +# 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 +# +# http://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. + +from typing import Any + +# The typing_extensions module is necessary for type annotations that are +# checked with mypy. It is only used for type annotations or to define +# things that are themselves only used for type annotations. It is not +# available on a default Python installation. Therefore, try loading +# what we need from it for the sake of mypy (which depends on, or comes +# with, typing_extensions), and if not define substitutes that lack the +# static type information but are good enough at runtime. +try: + from typing_extensions import Protocol #pylint: disable=import-error +except ImportError: + class Protocol: #type: ignore + #pylint: disable=too-few-public-methods + pass + +class Writable(Protocol): + """Abstract class for typing hints.""" + # pylint: disable=no-self-use,too-few-public-methods,unused-argument + def write(self, text: str) -> Any: + ... |