4141import pkg_resources
4242import pyqrcode
4343
44+ from nacl .signing import SigningKey
45+ from nacl .encoding import HexEncoder
46+
47+ import txaio
48+ from autobahn .util import utcnow
4449from autobahn .wamp .cryptosign import _read_signify_ed25519_pubkey , _qrcode_from_signify_ed25519_pubkey
4550
4651from twisted .python .runtime import platform
4752
4853import crossbar
4954
5055
51- def _read_release_pubkey ():
56+ log = txaio .make_logger ()
57+
58+
59+ def _read_release_key ():
5260 release_pubkey_file = 'crossbar-{}.pub' .format ('-' .join (crossbar .__version__ .split ('.' )[0 :2 ]))
5361 release_pubkey_path = os .path .join (pkg_resources .resource_filename ('crossbar' , 'keys' ), release_pubkey_file )
5462
@@ -68,7 +76,7 @@ def _read_release_pubkey():
6876 return release_pubkey
6977
7078
71- def _parse_keyfile (key_path , private = True ):
79+ def _parse_key_file (key_path , private = True ):
7280 """
7381 Internal helper. This parses a node.pub or node.priv file and
7482 returns a dict mapping tags -> values.
@@ -99,41 +107,40 @@ def _parse_keyfile(key_path, private=True):
99107 return tags
100108
101109
102- def _read_node_pubkey (cbdir , privkey_path = u'key.priv' , pubkey_path = u'key.pub' ):
103-
104- node_pubkey_path = os .path .join (cbdir , pubkey_path )
105-
106- if not os .path .exists (node_pubkey_path ):
107- raise Exception ('no node public key found at {}' .format (node_pubkey_path ))
110+ def _read_node_key (cbdir , privkey_path = u'key.priv' , pubkey_path = u'key.pub' , private = False ):
111+ if private :
112+ node_key_path = os .path .join (cbdir , privkey_path )
113+ else :
114+ node_key_path = os .path .join (cbdir , pubkey_path )
108115
109- node_pubkey_tags = _parse_keyfile (node_pubkey_path )
116+ if not os .path .exists (node_key_path ):
117+ raise Exception ('no node key file found at {}' .format (node_key_path ))
110118
111- node_pubkey_hex = node_pubkey_tags [ u'public-key-ed25519' ]
119+ node_key_tags = _parse_key_file ( node_key_path )
112120
113- qr = pyqrcode .create (node_pubkey_hex , error = 'L' , mode = 'binary' )
121+ if private :
122+ node_key_hex = node_key_tags [u'private-key-ed25519' ]
123+ else :
124+ node_key_hex = node_key_tags [u'public-key-ed25519' ]
114125
126+ qr = pyqrcode .create (node_key_hex , error = 'L' , mode = 'binary' )
115127 mode = 'text'
116-
117128 if mode == 'text' :
118- node_pubkey_qr = qr .terminal ()
119-
129+ node_key_qr = qr .terminal ()
120130 elif mode == 'svg' :
121131 import io
122132 data_buffer = io .BytesIO ()
123-
124133 qr .svg (data_buffer , omithw = True )
125-
126- node_pubkey_qr = data_buffer .getvalue ()
127-
134+ node_key_qr = data_buffer .getvalue ()
128135 else :
129136 raise Exception ('logic error' )
130137
131- node_pubkey = {
132- u'hex' : node_pubkey_hex ,
133- u'qrcode' : node_pubkey_qr
138+ node_key = {
139+ u'hex' : node_key_hex ,
140+ u'qrcode' : node_key_qr
134141 }
135142
136- return node_pubkey
143+ return node_key
137144
138145
139146def _machine_id ():
@@ -187,3 +194,100 @@ def _write_node_key(filepath, tags, msg):
187194 if value is None :
188195 value = 'unknown'
189196 f .write (u'{}: {}\n ' .format (tag , value ))
197+
198+
199+ def _maybe_generate_key (cbdir , privfile = u'key.priv' , pubfile = u'key.pub' ):
200+
201+ privkey_path = os .path .join (cbdir , privfile )
202+ pubkey_path = os .path .join (cbdir , pubfile )
203+
204+ if os .path .exists (privkey_path ):
205+
206+ # node private key seems to exist already .. check!
207+
208+ priv_tags = _parse_key_file (privkey_path , private = True )
209+ for tag in [u'creator' , u'created-at' , u'machine-id' , u'public-key-ed25519' , u'private-key-ed25519' ]:
210+ if tag not in priv_tags :
211+ raise Exception ("Corrupt node private key file {} - {} tag not found" .format (privkey_path , tag ))
212+
213+ privkey_hex = priv_tags [u'private-key-ed25519' ]
214+ privkey = SigningKey (privkey_hex , encoder = HexEncoder )
215+ pubkey = privkey .verify_key
216+ pubkey_hex = pubkey .encode (encoder = HexEncoder ).decode ('ascii' )
217+
218+ if priv_tags [u'public-key-ed25519' ] != pubkey_hex :
219+ raise Exception (
220+ ("Inconsistent node private key file {} - public-key-ed25519 doesn't"
221+ " correspond to private-key-ed25519" ).format (pubkey_path )
222+ )
223+
224+ if os .path .exists (pubkey_path ):
225+ pub_tags = _parse_key_file (pubkey_path , private = False )
226+ for tag in [u'creator' , u'created-at' , u'machine-id' , u'public-key-ed25519' ]:
227+ if tag not in pub_tags :
228+ raise Exception ("Corrupt node public key file {} - {} tag not found" .format (pubkey_path , tag ))
229+
230+ if pub_tags [u'public-key-ed25519' ] != pubkey_hex :
231+ raise Exception (
232+ ("Inconsistent node public key file {} - public-key-ed25519 doesn't"
233+ " correspond to private-key-ed25519" ).format (pubkey_path )
234+ )
235+ else :
236+ log .info (
237+ "Node public key file {pub_path} not found - re-creating from node private key file {priv_path}" ,
238+ pub_path = pubkey_path ,
239+ priv_path = privkey_path ,
240+ )
241+ pub_tags = OrderedDict ([
242+ (u'creator' , priv_tags [u'creator' ]),
243+ (u'created-at' , priv_tags [u'created-at' ]),
244+ (u'machine-id' , priv_tags [u'machine-id' ]),
245+ (u'public-key-ed25519' , pubkey_hex ),
246+ ])
247+ msg = u'Crossbar.io node public key\n \n '
248+ _write_node_key (pubkey_path , pub_tags , msg )
249+
250+ log .info ("Node key files exist and are valid" )
251+ log .debug ("Node public key: {hex}" , hex = pubkey_hex )
252+
253+ else :
254+ # node private key does not yet exist: generate one
255+
256+ privkey = SigningKey .generate ()
257+ privkey_hex = privkey .encode (encoder = HexEncoder ).decode ('ascii' )
258+ pubkey = privkey .verify_key
259+ pubkey_hex = pubkey .encode (encoder = HexEncoder ).decode ('ascii' )
260+
261+ # first, write the public file
262+ tags = OrderedDict ([
263+ (u'creator' , _creator ()),
264+ (u'created-at' , utcnow ()),
265+ (u'machine-id' , _machine_id ()),
266+ (u'public-key-ed25519' , pubkey_hex ),
267+ ])
268+ msg = u'Crossbar.io node public key\n \n '
269+ _write_node_key (pubkey_path , tags , msg )
270+
271+ # now, add the private key and write the private file
272+ tags [u'private-key-ed25519' ] = privkey_hex
273+ msg = u'Crossbar.io node private key - KEEP THIS SAFE!\n \n '
274+ _write_node_key (privkey_path , tags , msg )
275+
276+ log .info ("New node key pair generated!" )
277+
278+ # fix file permissions on node public/private key files
279+ # note: we use decimals instead of octals as octal literals have changed between Py2/3
280+ #
281+ if os .stat (pubkey_path ).st_mode & 511 != 420 : # 420 (decimal) == 0644 (octal)
282+ os .chmod (pubkey_path , 420 )
283+ log .info ("File permissions on node public key fixed" )
284+
285+ if os .stat (privkey_path ).st_mode & 511 != 384 : # 384 (decimal) == 0600 (octal)
286+ os .chmod (privkey_path , 384 )
287+ log .info ("File permissions on node private key fixed" )
288+
289+ log .info (
290+ 'Node key loaded from "{priv_path}"' ,
291+ priv_path = privkey_path ,
292+ )
293+ return privkey
0 commit comments