2 # -*- coding: utf-8 -*-
4 # Copyright the NTPsec project contributors
6 # SPDX-License-Identifier: BSD-2-Clause
9 ntpkeygen - generate cryptographic keys for NTP clients and servers
11 All file names are like "ntpkey_<type>_<hostname>.<filestamp>", where
12 <type> is the file type, <hostname> the generating host name and
13 <filestamp> the generation time in NTP seconds. The NTP programs
14 expect generic names such as "ntpkey_<type>_whimsy.udel.edu" with the
15 association maintained by soft links. Following is a list of file
18 ntpkey_AES_<hostname>.<filestamp>
19 AES (128-bit) keys used to compute CMAC mode authentcation
20 using shared key cryptography
22 The file can be edited by hand to support MD5 and SHA-1 for
23 old digest mode authentication.
26 from __future__
import print_function
37 def gen_key(bytes
, asciified
=True):
40 for index
in range(bytes
):
41 # Start ASCII characters with 0x24 so as not to include comment-beginning #
42 result
+= chr(0x24 + secrets
.randbelow(0x5a))
45 return secrets
.token_hex(bytes
)
48 def gen_key(bytes
, asciified
=True):
51 for index
in range(bytes
):
52 # Start ASCII characters with 0x24 so as not to include comment-beginning #
53 result
+= chr(random
.randint(0x24, 0x7e))
55 for index
in range(bytes
):
56 result
+= "%02x" % random
.randint(0x0, 0xff)
62 NUMKEYS
= 10 # number of keys generated of each type
63 KEYSIZE
= 16 # maximum key size
66 def gen_keys(ident
, groupname
):
67 "Generate semi-random AES keys for versions of ntpd with CMAC support."
68 with
fheader("AES", ident
, groupname
) as wp
:
69 for i
in range(1, NUMKEYS
+1):
70 key
= gen_key(KEYSIZE
, True)
71 wp
.write("%2d AES %s\n" % (i
, key
))
72 for i
in range(1, NUMKEYS
+1):
73 key
= gen_key(KEYSIZE
, False)
74 wp
.write("%2d AES %s\n" % (i
+ NUMKEYS
, key
))
78 # Generate file header and link
80 def fheader(fileid
, # file name id
84 "Generate file header and link"
86 filename
= "ntpkey_%s_%s.%u" % (fileid
, owner
, int(time
.time()))
87 orig_umask
= os
.umask(stat
.S_IWGRP | stat
.S_IRWXO
)
88 wp
= open(filename
, "w")
92 if os
.path
.exists(linkname
):
93 os
.remove(linkname
) # The symlink() line below matters
94 os
.symlink(filename
, linkname
)
96 sys
.stderr
.write("Generating new %s file and link\n" % ulink
)
97 sys
.stderr
.write("%s->%s\n" % (linkname
, filename
))
98 wp
.write("# %s\n# %s\n" % (filename
, time
.ctime()))
101 sys
.stderr
.write("Key file creation or link failed.\n")
105 if __name__
== '__main__':
107 (options
, arguments
) = getopt
.getopt(sys
.argv
[1:], "hV",
109 except getopt
.GetoptError
as e
:
113 for (switch
, val
) in options
:
114 if switch
in ("-h", "--help"):
115 print("usage: ntpkeygen")
117 elif switch
in ("-V", "--version"):
118 print("ntpkeygen ntpsec-@NTPSEC_VERSION_EXTENDED@")
121 # The seed is ignored by random.SystemRandom,
122 # even though the docs do not say so.
123 gen_keys("AES", socket
.gethostname())