You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197
  1. import glob
  2. import os
  3. from typing import Optional
  4. import yaml
  5. import platform
  6. import helpers
  7. class Scheme:
  8. def __init__(self):
  9. self.type = None
  10. self.name = None
  11. self.implementations = []
  12. def path(self, base='..'):
  13. return os.path.join(base, 'crypto_' + self.type, self.name)
  14. def namespace_prefix(self):
  15. return 'PQCLEAN_{}_'.format(self.name.upper()).replace('-', '')
  16. @staticmethod
  17. def by_name(scheme_name):
  18. for scheme in Scheme.all_schemes():
  19. if scheme.name == scheme_name:
  20. return scheme
  21. raise KeyError()
  22. @staticmethod
  23. def all_schemes():
  24. schemes = []
  25. schemes.extend(Scheme.all_schemes_of_type('kem'))
  26. schemes.extend(Scheme.all_schemes_of_type('sign'))
  27. return schemes
  28. @staticmethod
  29. def all_implementations():
  30. implementations = []
  31. for scheme in Scheme.all_schemes():
  32. implementations.extend(scheme.implementations)
  33. return implementations
  34. @staticmethod
  35. def all_supported_implementations():
  36. return [impl for impl in Scheme.all_implementations()
  37. if impl.supported_on_current_platform()]
  38. @staticmethod
  39. def all_schemes_of_type(type: str) -> list:
  40. schemes = []
  41. p = os.path.join('..', 'crypto_' + type)
  42. if os.path.isdir(p):
  43. for d in os.listdir(p):
  44. if os.path.isdir(os.path.join(p, d)):
  45. if type == 'kem':
  46. schemes.append(KEM(d))
  47. elif type == 'sign':
  48. schemes.append(Signature(d))
  49. else:
  50. assert('Unknown type')
  51. return schemes
  52. def metadata(self):
  53. metafile = os.path.join(self.path(), 'META.yml')
  54. try:
  55. with open(metafile, encoding='utf-8') as f:
  56. metadata = yaml.safe_load(f.read())
  57. return metadata
  58. except Exception as e:
  59. print("Can't open {}: {}".format(metafile, e))
  60. return None
  61. def __repr__(self):
  62. return "<{}({})>".format(self.type.title(), self.name)
  63. class Implementation:
  64. def __init__(self, scheme, name):
  65. self.scheme = scheme
  66. self.name = name
  67. def metadata(self):
  68. for i in self.scheme.metadata()['implementations']:
  69. if i['name'] == self.name:
  70. return i
  71. def path(self, base='..') -> str:
  72. return os.path.join(self.scheme.path(base=base), self.name)
  73. def libname(self) -> str:
  74. if os.name == 'nt':
  75. return "lib{}_{}.lib".format(self.scheme.name, self.name)
  76. return "lib{}_{}.a".format(self.scheme.name, self.name)
  77. def cfiles(self) -> [str]:
  78. return glob.glob(os.path.join(self.path(), '*.c'))
  79. def hfiles(self) -> [str]:
  80. return glob.glob(os.path.join(self.path(), '*.h'))
  81. def ofiles(self) -> [str]:
  82. return glob.glob(os.path.join(self.path(),
  83. '*.o' if os.name != 'nt' else '*.obj'))
  84. @staticmethod
  85. def by_name(scheme_name, implementation_name):
  86. scheme = Scheme.by_name(scheme_name)
  87. for implementation in scheme.implementations:
  88. if implementation.name == implementation_name:
  89. return implementation
  90. raise KeyError()
  91. @staticmethod
  92. def all_implementations(scheme: Scheme) -> list:
  93. implementations = []
  94. for d in os.listdir(scheme.path()):
  95. if os.path.isdir(os.path.join(scheme.path(), d)):
  96. implementations.append(Implementation(scheme, d))
  97. return implementations
  98. @staticmethod
  99. def all_supported_implementations(scheme: Scheme) -> list:
  100. return [impl for impl in Implementation.all_implementations(scheme)
  101. if impl.supported_on_current_platform()]
  102. def namespace_prefix(self):
  103. return '{}{}_'.format(self.scheme.namespace_prefix(),
  104. self.name.upper()).replace('-', '')
  105. def supported_on_os(self, os: Optional[str] = None) -> bool:
  106. """Check if we support the OS
  107. If no OS is specified, then we run on the current OS
  108. """
  109. if os is None:
  110. os = platform.system()
  111. for platform_ in self.metadata().get('supported_platforms', []):
  112. if 'operating_systems' in platform_:
  113. if os not in platform_['operating_systems']:
  114. return False
  115. return True
  116. def supported_on_current_platform(self) -> bool:
  117. if 'supported_platforms' not in self.metadata():
  118. return True
  119. if platform.machine() == 'ppc':
  120. return False
  121. if not self.supported_on_os():
  122. return False
  123. cpuinfo = helpers.get_cpu_info()
  124. for platform_ in self.metadata()['supported_platforms']:
  125. if platform_['architecture'] == cpuinfo['arch'].lower():
  126. # Detect actually running on emulated i386
  127. if (platform_['architecture'] == 'x86_64' and
  128. platform.architecture()[0] == '32bit'):
  129. continue
  130. if all([flag in cpuinfo['flags']
  131. for flag in platform_['required_flags']]):
  132. return True
  133. return False
  134. def __str__(self):
  135. return "{} implementation of {}".format(self.name, self.scheme.name)
  136. def __repr__(self):
  137. return "<Implementation({}, {})>".format(self.scheme.name, self.name)
  138. class KEM(Scheme):
  139. def __init__(self, name: str):
  140. self.type = 'kem'
  141. self.name = name
  142. self.implementations = Implementation.all_implementations(self)
  143. @staticmethod
  144. def all_kems() -> list:
  145. return Scheme.all_schemes_of_type('kem')
  146. class Signature(Scheme):
  147. def __init__(self, name: str):
  148. self.type = 'sign'
  149. self.name = name
  150. self.implementations = Implementation.all_implementations(self)
  151. @staticmethod
  152. def all_sigs():
  153. return Scheme.all_schemes_of_type('sign')