summaryrefslogtreecommitdiff
path: root/tools/ca
diff options
context:
space:
mode:
Diffstat (limited to 'tools/ca')
-rw-r--r--tools/ca/ca/__main__.py48
1 files changed, 45 insertions, 3 deletions
diff --git a/tools/ca/ca/__main__.py b/tools/ca/ca/__main__.py
index e3e4bbe6..118b3763 100644
--- a/tools/ca/ca/__main__.py
+++ b/tools/ca/ca/__main__.py
@@ -30,6 +30,7 @@ from tempfile import TemporaryFile
30import subprocess 30import subprocess
31import json 31import json
32from leapseconddata import LeapSecondData 32from leapseconddata import LeapSecondData
33from collections.abc import Iterable
33 34
34 35
35class KeyType(Enum): 36class KeyType(Enum):
@@ -76,6 +77,28 @@ class KeyType(Enum):
76 except KeyError: 77 except KeyError:
77 raise ValueError() 78 raise ValueError()
78 79
80class SupportedKeyUsage(Enum):
81 SERVER_AUTH = 'server'
82 CLIENT_AUTH = 'client'
83
84 @property
85 def oid(self):
86 match self:
87 case SupportedKeyUsage.SERVER_AUTH:
88 return ExtendedKeyUsageOID.SERVER_AUTH
89 case SupportedKeyUsage.CLIENT_AUTH:
90 return ExtendedKeyUsageOID.CLIENT_AUTH
91
92 def __str__(self):
93 return self.value
94
95 @classmethod
96 def from_string(cls, s):
97 try:
98 return cls(s)
99 except KeyError:
100 raise ValueError()
101
79class ValidFQDN(FQDN): 102class ValidFQDN(FQDN):
80 def __init__(self, *args, **kwds): 103 def __init__(self, *args, **kwds):
81 super().__init__(*args, **kwds) 104 super().__init__(*args, **kwds)
@@ -133,6 +156,19 @@ class BooleanAction(argparse.Action):
133 def __call__(self, parser, namespace, values, option_string=None): 156 def __call__(self, parser, namespace, values, option_string=None):
134 setattr(namespace, self.dest, False if option_string.startswith('--no') else True) 157 setattr(namespace, self.dest, False if option_string.startswith('--no') else True)
135 158
159class ExtendAction(argparse.Action):
160 def __init__(self, *args, **kwargs):
161 super().__init__(*args, **kwargs)
162 self.reset_dest = False
163 def __call__(self, parser, namespace, values, option_string=None):
164 if not self.reset_dest:
165 setattr(namespace, self.dest, [])
166 self.reset_dest = True
167 if isinstance(values, Iterable):
168 getattr(namespace, self.dest).extend(values)
169 else:
170 getattr(namespace, self.dest).append(values)
171
136 172
137def load_key(keyfile, prompt='CA private key password: '): 173def load_key(keyfile, prompt='CA private key password: '):
138 key = None 174 key = None
@@ -294,7 +330,10 @@ def initca(ca_cert, ca_key, key_type, subject, clock_skew, validity, sops):
294 logger.debug('Adjusting permissions for ‘%s’...', ca_cert) 330 logger.debug('Adjusting permissions for ‘%s’...', ca_cert)
295 os.chmod(ca_cert, 0o0444) 331 os.chmod(ca_cert, 0o0444)
296 332
297def signcsr(ca_cert, ca_key, clock_skew, validity, subject, alternative_name, ignore_alternative_names, csr, output): 333def signcsr(ca_cert, ca_key, clock_skew, validity, subject, alternative_name, key_usage, ignore_alternative_names, csr, output):
334 if not key_usage:
335 raise InvalidParamsError('No extended key usages specified')
336
298 csr_bytes = None 337 csr_bytes = None
299 try: 338 try:
300 csr_bytes = csr.read() 339 csr_bytes = csr.read()
@@ -348,7 +387,7 @@ def signcsr(ca_cert, ca_key, clock_skew, validity, subject, alternative_name, ig
348 x509.BasicConstraints(ca=False, path_length=None), 387 x509.BasicConstraints(ca=False, path_length=None),
349 True 388 True
350 ).add_extension( 389 ).add_extension(
351 x509.ExtendedKeyUsage([ExtendedKeyUsageOID.CLIENT_AUTH]), 390 x509.ExtendedKeyUsage(list(map(lambda ku: ku.oid, key_usage))),
352 False 391 False
353 ) 392 )
354 393
@@ -374,7 +413,7 @@ def signcsr(ca_cert, ca_key, clock_skew, validity, subject, alternative_name, ig
374 logger.debug('Adjusting permissions for ‘%s’...', output) 413 logger.debug('Adjusting permissions for ‘%s’...', output)
375 os.chmod(output, 0o0444) 414 os.chmod(output, 0o0444)
376 415
377def new_client(ca_cert, ca_key, key_type, clock_skew, validity, subject, alternative_name, sops, output): 416def new_client(ca_cert, ca_key, key_type, clock_skew, validity, subject, alternative_name, key_usage, sops, output):
378 key_file = output.with_suffix('.key') 417 key_file = output.with_suffix('.key')
379 cert_file = output.with_suffix('.crt') 418 cert_file = output.with_suffix('.crt')
380 419
@@ -417,6 +456,7 @@ def new_client(ca_cert, ca_key, key_type, clock_skew, validity, subject, alterna
417 validity=validity, 456 validity=validity,
418 subject=None, 457 subject=None,
419 alternative_name=[], 458 alternative_name=[],
459 key_usage=key_usage,
420 ignore_alternative_names=False, 460 ignore_alternative_names=False,
421 output=cert_file, 461 output=cert_file,
422 csr=csr.sign( 462 csr=csr.sign(
@@ -524,6 +564,7 @@ def main():
524 subparser.add_argument('--validity', metavar='DURATION', type=duration, default=timedelta(days=ceil(365.2425*10))) 564 subparser.add_argument('--validity', metavar='DURATION', type=duration, default=timedelta(days=ceil(365.2425*10)))
525 subparser.add_argument('--subject', metavar='CN', type=str, required=False) 565 subparser.add_argument('--subject', metavar='CN', type=str, required=False)
526 subparser.add_argument('--ignore-alternative-names', '--no-ignore-alternative-names', action=BooleanAction, default=True) 566 subparser.add_argument('--ignore-alternative-names', '--no-ignore-alternative-names', action=BooleanAction, default=True)
567 subparser.add_argument('--key-usage', metavar='KEY_USAGE', type=SupportedKeyUsage, action=ExtendAction, default=[SupportedKeyUsage.CLIENT_AUTH])
527 subparser.add_argument('--alternative-name', metavar='CN', type=str, action='append') 568 subparser.add_argument('--alternative-name', metavar='CN', type=str, action='append')
528 subparser.add_argument('--output', type=Path, required=True) 569 subparser.add_argument('--output', type=Path, required=True)
529 subparser.add_argument('csr', metavar='FILE', type=argparse.FileType(mode='rb')) 570 subparser.add_argument('csr', metavar='FILE', type=argparse.FileType(mode='rb'))
@@ -537,6 +578,7 @@ def main():
537 subparser.add_argument('--validity', metavar='DURATION', type=duration, default=timedelta(days=ceil(365.2425*10))) 578 subparser.add_argument('--validity', metavar='DURATION', type=duration, default=timedelta(days=ceil(365.2425*10)))
538 subparser.add_argument('--sops', '--no-sops', action=BooleanAction, default=True) 579 subparser.add_argument('--sops', '--no-sops', action=BooleanAction, default=True)
539 subparser.add_argument('--subject', metavar='CN', type=str, required=True) 580 subparser.add_argument('--subject', metavar='CN', type=str, required=True)
581 subparser.add_argument('--key-usage', metavar='KEY_USAGE', type=SupportedKeyUsage, action=ExtendAction, default=[SupportedKeyUsage.CLIENT_AUTH])
540 subparser.add_argument('--alternative-name', metavar='CN', type=str, action='append') 582 subparser.add_argument('--alternative-name', metavar='CN', type=str, action='append')
541 subparser.add_argument('--output', type=Path, required=True) 583 subparser.add_argument('--output', type=Path, required=True)
542 subparser.set_defaults(cmd=new_client) 584 subparser.set_defaults(cmd=new_client)