1#!/usr/local/bin/python3.8 2# -*- coding: utf-8 -*- 3''' 4ntpkeygen - generate cryptographic keys for NTP clients and servers 5 6All file names are like "ntpkey_<type>_<hostname>.<filestamp>", where 7<type> is the file type, <hostname> the generating host name and 8<filestamp> the generation time in NTP seconds. The NTP programs 9expect generic names such as "ntpkey_<type>_whimsy.udel.edu" with the 10association maintained by soft links. Following is a list of file 11types. 12 13ntpkey_AES_<hostname>.<filestamp> 14AES (128-bit) keys used to compute CMAC mode authentcation 15using shared key cryptography 16 17The file can be edited by hand to support MD5 and SHA-1 for 18old digest mode authentication. 19''' 20 21from __future__ import print_function 22 23import os 24import sys 25import socket 26import time 27import getopt 28import stat 29 30try: 31 import secrets 32 def gen_key(bytes, asciified=True): 33 if asciified: 34 result = '' 35 for index in range(bytes): 36 # Start ASCII characters with 0x24 so as not to include comment-beginning # 37 result += chr(0x24 + secrets.randbelow(0x5a)) 38 return result 39 else: 40 return secrets.token_hex(bytes) 41except ImportError: 42 import random 43 def gen_key(bytes, asciified=True): 44 result = '' 45 if asciified: 46 for index in range(bytes): 47 # Start ASCII characters with 0x24 so as not to include comment-beginning # 48 result += chr(random.randint(0x24, 0x7e)) 49 else: 50 for index in range(bytes): 51 result += "%02x" % random.randint(0x0, 0xff) 52 return result 53 54# 55# Cryptodefines 56# 57NUMKEYS = 10 # number of keys generated of each type 58KEYSIZE = 16 # maximum key size 59 60 61def gen_keys(ident, groupname): 62 "Generate semi-random AES keys for versions of ntpd with CMAC support." 63 with fheader("AES", ident, groupname) as wp: 64 for i in range(1, NUMKEYS+1): 65 key = gen_key(KEYSIZE, True) 66 wp.write("%2d AES %s\n" % (i, key)) 67 for i in range(1, NUMKEYS+1): 68 key = gen_key(KEYSIZE, False) 69 wp.write("%2d AES %s\n" % (i + NUMKEYS, key)) 70 71 72# 73# Generate file header and link 74# 75def fheader(fileid, # file name id 76 ulink, # linkname 77 owner # owner name 78 ): 79 "Generate file header and link" 80 try: 81 filename = "ntpkey_%s_%s.%u" % (fileid, owner, int(time.time())) 82 orig_umask = os.umask(stat.S_IWGRP | stat.S_IRWXO) 83 wp = open(filename, "w") 84 os.umask(orig_umask) 85 86 linkname = "ntp.keys" 87 if os.path.exists(linkname): 88 os.remove(linkname) # The symlink() line below matters 89 os.symlink(filename, linkname) 90 91 sys.stderr.write("Generating new %s file and link\n" % ulink) 92 sys.stderr.write("%s->%s\n" % (linkname, filename)) 93 wp.write("# %s\n# %s\n" % (filename, time.ctime())) 94 return wp 95 except IOError: 96 sys.stderr.write("Key file creation or link failed.\n") 97 raise SystemExit(1) 98 99 100if __name__ == '__main__': 101 try: 102 (options, arguments) = getopt.getopt(sys.argv[1:], "hV", 103 ["help", "version"]) 104 except getopt.GetoptError as e: 105 print(e) 106 raise SystemExit(1) 107 108 for (switch, val) in options: 109 if switch in ("-h", "--help"): 110 print("usage: ntpkeygen") 111 raise SystemExit(0) 112 elif switch in ("-V", "--version"): 113 print("ntpkeygen ntpsec-@NTPSEC_VERSION_EXTENDED@") 114 raise SystemExit(0) 115 116 # The seed is ignored by random.SystemRandom, 117 # even though the docs do not say so. 118 gen_keys("AES", socket.gethostname()) 119 raise SystemExit(0) 120 121# end 122