blob: 37437f18ba8948b1e2c86e9afbc0572e39ac928c [file] [log] [blame]
Mao Huang700663d2015-08-12 09:58:59 +08001# Copyright 2015 The Chromium OS Authors. All rights reserved.
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
5"""The DRM Keys Provisioning Server (DKPS) implementation."""
6
7# TODO(littlecvr): Implement "without filter mode", which lets OEM encrypts DRM
8# keys directly with ODM's public key, and the key server
9# merely stores them without knowing anything about them.
10
11# TODO(littlecvr): Allow using pre-generated server GPG key when initializing.
12
13import argparse
14import imp
15import json
Mao Huang041483e2015-09-14 23:28:18 +080016import logging
Mao Huang700663d2015-08-12 09:58:59 +080017import os
18import shutil
19import SimpleXMLRPCServer
20import sqlite3
Mao Huang041483e2015-09-14 23:28:18 +080021import sys
Mao Huang700663d2015-08-12 09:58:59 +080022import textwrap
23
24import gnupg
25
26
27SCRIPT_DIR = os.path.dirname(os.path.realpath(__file__))
Mao Huang041483e2015-09-14 23:28:18 +080028LOG_FILE_PATH = os.path.join(SCRIPT_DIR, 'dkps.log')
Mao Huang700663d2015-08-12 09:58:59 +080029FILTERS_DIR = os.path.join(SCRIPT_DIR, 'filters')
30CREATE_DATABASE_SQL_FILE_PATH = os.path.join(
31 SCRIPT_DIR, 'sql', 'create_database.sql')
32
33
34class ProjectNotFoundException(ValueError):
35 """Raised when no project was found in the database."""
36 pass
37
38
39class InvalidUploaderException(ValueError):
40 """Raised when the signature of the uploader can't be verified."""
41 pass
42
43
44class InvalidRequesterException(ValueError):
45 """Raised when the signature of the requester can't be verified."""
46 pass
47
48
49def GetSQLite3Connection(database_file_path):
50 """Returns a tuple of SQLite3's (connection, cursor) to database_file_path.
51
52 If the connection has been created before, it is returned directly. If it's
53 not, this function creates the connection, ensures that the foreign key
54 constraint is enabled, and returns.
55
56 Args:
57 database_file_path: path to the SQLite3 database file.
58 """
59 database_file_path = os.path.realpath(database_file_path)
60
61 # Return if the connection to database_file_path has been created before.
62 try:
63 connection = GetSQLite3Connection.connection_dict[database_file_path]
64 return (connection, connection.cursor())
65 except KeyError:
66 pass
67 except AttributeError:
68 GetSQLite3Connection.connection_dict = {}
69
70 # Create connection.
71 connection = sqlite3.connect(database_file_path)
72 connection.row_factory = sqlite3.Row
73 cursor = connection.cursor()
74
75 # Enable foreign key constraint since SQLite3 disables it by default.
76 cursor.execute('PRAGMA foreign_keys = ON')
77 # Check if foreign key constraint is enabled.
78 cursor.execute('PRAGMA foreign_keys')
79 if cursor.fetchone()[0] != 1:
80 raise RuntimeError('Failed to enable SQLite3 foreign key constraint')
81
82 GetSQLite3Connection.connection_dict[database_file_path] = connection
83
84 return (connection, cursor)
85
86
87class DRMKeysProvisioningServer(object):
88 """The DRM Keys Provisioning Server (DKPS) class."""
89
90 def __init__(self, database_file_path, gnupg_homedir):
91 """DKPS constructor.
92
93 Args:
94 database_file_path: path to the SQLite3 database file.
95 gnupg_homedir: path to the GnuPG home directory.
96 """
97 self.database_file_path = database_file_path
98 self.gnupg_homedir = gnupg_homedir
99
100 if not os.path.isdir(self.gnupg_homedir):
101 self.gpg = None
102 else:
103 self.gpg = gnupg.GPG(gnupghome=self.gnupg_homedir)
104
105 if not os.path.isfile(self.database_file_path):
106 self.db_connection, self.db_cursor = (None, None)
107 else:
108 self.db_connection, self.db_cursor = GetSQLite3Connection(
109 self.database_file_path)
110
111 def Initialize(self, gpg_gen_key_args_dict=None):
112 """Creates the SQLite3 database and GnuPG home, and generates a GPG key for
113 the server to use.
114
115 Args:
116 gpg_gen_key_args_dict: will be passed directly as the keyword arguments to
117 python-gnupg's gen_key() function. Can be used to customize the key
118 generator process, such as key_type, key_length, etc. See
119 python-gnupg's doc for what can be customized. Note that name_real,
120 name_email, and name_comment can not be customized.
121
122 Raises:
123 RuntimeError is the database and GnuPG home have already been initialized.
124 """
125 # Fixed info for server GPG key.
126 SERVER_KEY_OWNER_NAME = 'DKPS Server'
127 SERVER_KEY_OWNER_EMAIL = 'chromeos-factory-dkps@google.com'
128 SERVER_KEY_OWNER_COMMENT = 'DRM Keys Provisioning Server'
129
130 # Create GPG instance and database connection.
131 self.gpg = gnupg.GPG(gnupghome=self.gnupg_homedir)
132 self.db_connection, self.db_cursor = GetSQLite3Connection(
133 self.database_file_path)
134
135 # If server key exists, the system has already been initialized.
136 search_string = '%s (%s) <%s>' % ( # this is how GPG shows the key UID
137 SERVER_KEY_OWNER_NAME, SERVER_KEY_OWNER_COMMENT, SERVER_KEY_OWNER_EMAIL)
138 for key in self.gpg.list_keys():
139 if search_string in key['uids']:
140 raise RuntimeError('Already initialized')
141
142 # Generate a GPG key for this server.
143 if gpg_gen_key_args_dict is None:
144 gpg_gen_key_args_dict = {}
145 key_input_data = self.gpg.gen_key_input(
146 name_real=SERVER_KEY_OWNER_NAME, name_email=SERVER_KEY_OWNER_EMAIL,
147 name_comment=SERVER_KEY_OWNER_COMMENT, **gpg_gen_key_args_dict)
148 server_key = self.gpg.gen_key(key_input_data)
149
150 # Create and set up the schema of the database.
151 with open(CREATE_DATABASE_SQL_FILE_PATH) as f:
152 create_database_sql = f.read()
153 with self.db_connection:
154 self.db_cursor.executescript(create_database_sql)
155
156 # Record the server key fingerprint.
157 with self.db_connection:
158 self.db_cursor.execute(
159 'INSERT INTO settings (key, value) VALUES (?, ?)',
160 ('server_key_fingerprint', server_key.fingerprint))
161
162 def Destroy(self):
163 """Destroys the database and GnuPG home directory.
164
165 This is the opposite of Initialize(). It essentially removes the SQLite3
166 database file and GnuPG home directory.
167 """
168 # Remove database.
169 if self.db_connection:
170 self.db_connection.close()
171 if os.path.exists(self.database_file_path):
172 os.remove(self.database_file_path)
173
174 # Remove GnuPG home.
175 if self.gpg:
176 self.gpg = None
177 if os.path.exists(self.gnupg_homedir):
178 shutil.rmtree(self.gnupg_homedir)
179
180 def AddProject(self, name, uploader_key_file_path, requester_key_file_path,
181 filter_module_file_name=None):
182 """Adds a project.
183
184 Args:
185 name: name of the project, must be unique.
186 uploader_key_file_path: path to the OEM's public key file.
187 requester_key_file_path: path to the ODM's public key file.
188 filter_module_file_name: file name of the filter python module.
189
190 Raises:
191 ValueError if either the uploader's or requester's key are imported (which
192 means they are used by another project).
193 """
194 # Try to load the filter module.
195 if filter_module_file_name is not None:
196 self._LoadFilterModule(filter_module_file_name)
197
198 # Try to import uploader and requester keys and add project info into the
199 # database, if failed at any step, delete imported keys.
200 uploader_key_fingerprint, requester_key_fingerprint = (None, None)
201 uploader_key_already_exists, requester_key_already_exists = (False, False)
202 try:
203 uploader_key_fingerprint, uploader_key_already_exists = (
204 self._ImportGPGKey(uploader_key_file_path))
205 if uploader_key_already_exists:
206 raise ValueError('Uploader key already exists')
207 requester_key_fingerprint, requester_key_already_exists = (
208 self._ImportGPGKey(requester_key_file_path))
209 if requester_key_already_exists:
210 raise ValueError('Requester key already exists')
211 with self.db_connection:
212 self.db_cursor.execute(
213 'INSERT INTO projects (name, uploader_key_fingerprint, '
214 'requester_key_fingerprint, filter_module_file_name) VALUES '
215 '(?, ?, ?, ?)',
216 (name, uploader_key_fingerprint, requester_key_fingerprint,
217 filter_module_file_name))
218 except BaseException:
219 if not uploader_key_already_exists and uploader_key_fingerprint:
220 self.gpg.delete_keys(uploader_key_fingerprint)
221 if not requester_key_already_exists and requester_key_fingerprint:
222 self.gpg.delete_keys(requester_key_fingerprint)
223 raise
224
225 def UpdateProject(self, name, uploader_key_file_path=None,
226 requester_key_file_path=None, filter_module_file_name=None):
227 """Updates a project.
228
229 Args:
230 name: name of the project, must be unique.
231 uploader_key_file_path: path to the OEM's public key file.
232 requester_key_file_path: path to the ODM's public key file.
233 filter_module_file_name: file name of the filter python module.
234
235 Raises:
236 RuntimeError if SQLite3 can't update the project row (for any reason).
237 """
238 # Try to load the filter module.
239 if filter_module_file_name is not None:
240 self._LoadFilterModule(filter_module_file_name)
241
242 project = self._FetchProjectByName(name)
243
244 # Try to import uploader and requester keys and add project info into the
245 # database, if failed at any step, delete any newly imported keys.
246 uploader_key_fingerprint, requester_key_fingerprint = (None, None)
247 old_uploader_key_fingerprint = project['uploader_key_fingerprint']
248 old_requester_key_fingerprint = project['requester_key_fingerprint']
249 same_uploader_key, same_requester_key = (True, True)
250 try:
251 sql_set_clause_list = ['filter_module_file_name = ?']
252 sql_parameters = [filter_module_file_name]
253
254 if uploader_key_file_path:
255 uploader_key_fingerprint, same_uploader_key = self._ImportGPGKey(
256 uploader_key_file_path)
257 sql_set_clause_list.append('uploader_key_fingerprint = ?')
258 sql_parameters.append(uploader_key_fingerprint)
259
260 if requester_key_file_path:
261 requester_key_fingerprint, same_requester_key = self._ImportGPGKey(
262 uploader_key_file_path)
263 sql_set_clause_list.append('requester_key_fingerprint = ?')
264 sql_parameters.append(requester_key_fingerprint)
265
266 sql_set_clause = ','.join(sql_set_clause_list)
267 sql_parameters.append(name)
268 with self.db_connection:
269 self.db_cursor.execute(
270 'UPDATE projects SET %s WHERE name = ?' % sql_set_clause,
271 tuple(sql_parameters))
272 if self.db_cursor.rowcount != 1:
273 raise RuntimeError('Failed to update project %s' % name)
274 except BaseException:
275 if not same_uploader_key and uploader_key_fingerprint:
276 self.gpg.delete_keys(uploader_key_fingerprint)
277 if not same_requester_key and requester_key_fingerprint:
278 self.gpg.delete_keys(requester_key_fingerprint)
279 raise
280
281 if not same_uploader_key:
282 self.gpg.delete_keys(old_uploader_key_fingerprint)
283 if not same_requester_key:
284 self.gpg.delete_keys(old_requester_key_fingerprint)
285
286 def RemoveProject(self, name):
287 """Removes a project.
288
289 Args:
290 name: the name of the project specified when added.
291 """
292 project = self._FetchProjectByName(name)
293
294 self.gpg.delete_keys(project['uploader_key_fingerprint'])
295 self.gpg.delete_keys(project['requester_key_fingerprint'])
296
297 with self.db_connection:
298 self.db_cursor.execute(
299 'DELETE FROM drm_keys WHERE project_name = ?', (name,))
300 self.db_cursor.execute('DELETE FROM projects WHERE name = ?', (name,))
301
302 def ListProjects(self):
303 """Lists all projects."""
304 self.db_cursor.execute('SELECT * FROM projects ORDER BY name ASC')
305 return self.db_cursor.fetchall()
306
307 def Upload(self, encrypted_serialized_drm_keys):
308 """Uploads a list of DRM keys to the server. This is an atomic operation. It
309 will either succeed and save all the keys, or fail and save no keys.
310
311 Args:
312 encrypted_serialized_drm_keys: the serialized DRM keys signed by the
313 uploader and encrypted by the server's public key.
314
315 Raises:
316 InvalidUploaderException if the signature of the uploader can not be
317 verified.
318 """
319 decrypted_obj = self.gpg.decrypt(encrypted_serialized_drm_keys)
320 project = self._FetchProjectByUploaderKeyFingerprint(
321 decrypted_obj.fingerprint)
322 serialized_drm_keys = decrypted_obj.data
323
324 # Pass to the filter function.
325 filter_module = self._LoadFilterModule(project['filter_module_file_name'])
326 filtered_drm_key_list = filter_module.Filter(serialized_drm_keys)
327
328 # Fetch server key for signing.
329 server_key_fingerprint = self._FetchServerKeyFingerprint()
330
331 # Sign and encrypt each key by server's private key and requester's public
332 # key, respectively.
333 encrypted_serialized_drm_key_list = []
334 requester_key_fingerprint = project['requester_key_fingerprint']
335 for drm_key in filtered_drm_key_list:
336 encrypted_obj = self.gpg.encrypt(
337 json.dumps(drm_key), requester_key_fingerprint,
338 always_trust=True, sign=server_key_fingerprint)
339 encrypted_serialized_drm_key_list.append(encrypted_obj.data)
340
341 # Insert into the database.
342 with self.db_connection:
343 self.db_cursor.executemany(
344 'INSERT INTO drm_keys (project_name, encrypted_drm_key) '
345 'VALUES (?, ?)',
346 zip([project['name']] * len(encrypted_serialized_drm_key_list),
347 encrypted_serialized_drm_key_list))
348
349 def AvailableKeyCount(self, requester_signature):
350 """Queries the number of remaining keys.
351
352 Args:
353 requester_signature: a message signed by the requester. Since the server
354 doesn't need any additional info from the requester, the requester can
355 simply sign a random string and send it here.
356
357 Returns:
358 The number of remaining keys that can be requested.
359
360 Raises:
361 InvalidRequesterException if the signature of the requester can not be
362 verified.
363 """
364 verified = self.gpg.verify(requester_signature)
365 if not verified:
366 raise InvalidRequesterException(
367 'Invalid requester, check your signing key')
368
369 project = self._FetchProjectByRequesterKeyFingerprint(verified.fingerprint)
370
371 self.db_cursor.execute(
372 'SELECT COUNT(*) AS available_key_count FROM drm_keys '
373 'WHERE project_name = ? AND device_serial_number IS NULL',
374 (project['name'],))
375 return self.db_cursor.fetchone()['available_key_count']
376
377 def Request(self, encrypted_device_serial_number):
378 """Requests a DRM key by device serial number.
379
380 Args:
381 encrypted_device_serial_number: the device serial number signed by the
382 requester and encrypted by the server's public key.
383
384 Raises:
385 InvalidRequesterException if the signature of the requester can not be
386 verified. RuntimeError if no available keys left in the database.
387 """
388 decrypted_obj = self.gpg.decrypt(encrypted_device_serial_number)
389 project = self._FetchProjectByRequesterKeyFingerprint(
390 decrypted_obj.fingerprint)
391 device_serial_number = decrypted_obj.data
392
393 def FetchDRMKeyByDeviceSerialNumber(project_name, device_serial_number):
394 self.db_cursor.execute(
395 'SELECT * FROM drm_keys WHERE project_name = ? AND '
396 'device_serial_number = ?',
397 (project_name, device_serial_number))
398 return self.db_cursor.fetchone()
399
400 row = FetchDRMKeyByDeviceSerialNumber(project['name'], device_serial_number)
401 if row: # the SN has already paired
402 return row['encrypted_drm_key']
403
404 # Find an unpaired key.
405 with self.db_connection:
406 # SQLite3 does not support using LIMIT clause in UPDATE statement by
407 # default, unless SQLITE_ENABLE_UPDATE_DELETE_LIMIT flag is defined during
408 # compilation. Since this script may be deployed on partner's computer,
409 # we'd better assume they don't have this flag on.
410 self.db_cursor.execute(
411 'UPDATE drm_keys SET device_serial_number = ? '
412 'WHERE id = (SELECT id FROM drm_keys WHERE project_name = ? AND '
413 ' device_serial_number IS NULL LIMIT 1)',
414 (device_serial_number, project['name']))
415 if self.db_cursor.rowcount != 1: # insufficient keys
416 raise RuntimeError(
417 'Insufficient DRM keys, ask for the OEM to upload more')
418
419 row = FetchDRMKeyByDeviceSerialNumber(project['name'], device_serial_number)
420 if row:
421 return row['encrypted_drm_key']
422 else:
423 raise RuntimeError('Failed to find paired DRM key')
424
425 def ListenForever(self, ip, port):
426 """Starts the XML RPC server waiting for commands.
427
428 Args:
429 ip: IP to bind.
430 port: port to bind.
431 """
432 server = SimpleXMLRPCServer.SimpleXMLRPCServer((ip, port), allow_none=True)
433
434 server.register_introspection_functions()
435 server.register_function(self.AvailableKeyCount)
436 server.register_function(self.Upload)
437 server.register_function(self.Request)
438
Mao Huang041483e2015-09-14 23:28:18 +0800439 # Redirect stdout and stderr to log file.
440 log_file = open(LOG_FILE_PATH, 'a')
441 sys.stdout = log_file
442 sys.stderr = log_file
443
Mao Huang700663d2015-08-12 09:58:59 +0800444 server.serve_forever()
445
446 def _ImportGPGKey(self, key_file_path):
447 """Imports a GPG key from a file.
448
449 Args:
450 key_file_path: path to the GPG key file.
451
452 Returns:
453 A tuple (key_fingerprint, key_already_exists). The 1st element is the
454 imported key's fingerprint, and the 2nd element is True if the key was
455 already in the database before importing, False otherwise.
456 """
457 with open(key_file_path) as f:
458 import_results = self.gpg.import_keys(f.read())
459 key_already_exists = (import_results.imported == 0)
460 key_fingerprint = import_results.fingerprints[0]
461 return (key_fingerprint, key_already_exists)
462
463 def _LoadFilterModule(self, filter_module_file_name):
464 """Loads the filter module.
465
466 Args:
467 filter_module_file_name: file name of the filter module in the "filters"
468 folder.
469
470 Returns:
471 The loaded filter module on success.
472
473 Raises:
474 Exception if failed, see imp.load_source()'s doc for what could be raised.
475 """
476 return imp.load_source(
477 'filter_module', os.path.join(FILTERS_DIR, filter_module_file_name))
478
479 def _FetchServerKeyFingerprint(self):
480 """Returns the server GPG key's fingerprint."""
481 self.db_cursor.execute(
482 "SELECT * FROM settings WHERE key = 'server_key_fingerprint'")
483 row = self.db_cursor.fetchone()
484 if not row:
485 raise ValueError('Server key fingerprint not exists')
486 return row['value']
487
488 def _FetchOneProject(self, name=None, # pylint: disable=W0613
489 uploader_key_fingerprint=None, # pylint: disable=W0613
490 requester_key_fingerprint=None, # pylint: disable=W0613
491 exception_type=None, error_msg=None):
492 """Fetches the project by name, uploader key fingerprint, or requester key
493 fingerprint.
494
495 This function combines the name, uploader_key_fingerprint,
496 requester_key_fingerprint conditions (if not None) with the AND operator,
497 and tries to fetch one project from the database.
498
499 Args:
500 name: name of the project.
501 uploader_key_fingerprint: uploader key fingerprint of the project.
502 requester_key_fingerprint: requester key fingerprint of the project.
503 exception_type: if no project was found and exception_type is not None,
504 raise exception_type with error_msg.
505 error_msg: if no project was found and exception_type is not None, raise
506 exception_type with error_msg.
507
508 Returns:
509 A project that matches the name, uploader_key_fingerprint, and
510 requester_key_fingerprint conditiions.
511
512 Raises:
513 exception_type with error_msg if not project was found.
514 """
515 where_clause_list = []
516 params = []
517 local_vars = locals()
518 for param_name in ['name', 'uploader_key_fingerprint',
519 'requester_key_fingerprint']:
520 if local_vars[param_name] is not None:
521 where_clause_list.append('%s = ?' % param_name)
522 params.append(locals()[param_name])
523 if not where_clause_list:
524 raise ValueError('No conditions given to fetch the project')
525 where_clause = 'WHERE ' + ' AND '.join(where_clause_list)
526
527 self.db_cursor.execute(
528 'SELECT * FROM projects %s' % where_clause, tuple(params))
529 project = self.db_cursor.fetchone()
530
531 if not project and exception_type:
532 raise exception_type(error_msg)
533
534 return project
535
536 def _FetchProjectByName(self, name):
537 return self._FetchOneProject(
538 name=name, exception_type=ProjectNotFoundException,
539 error_msg=('Project %s not found' % name))
540
541 def _FetchProjectByUploaderKeyFingerprint(self, uploader_key_fingerprint):
542 return self._FetchOneProject(
543 uploader_key_fingerprint=uploader_key_fingerprint,
544 exception_type=InvalidUploaderException,
545 error_msg='Invalid uploader, check your signing key')
546
547 def _FetchProjectByRequesterKeyFingerprint(self, requester_key_fingerprint):
548 return self._FetchOneProject(
549 requester_key_fingerprint=requester_key_fingerprint,
550 exception_type=InvalidRequesterException,
551 error_msg='Invalid requester, check your signing key')
552
553
554def _ParseArguments():
555 parser = argparse.ArgumentParser()
556 parser.add_argument(
557 '-d', '--database_file_path', default=os.path.join(SCRIPT_DIR, 'dkps.db'),
558 help='path to the SQLite3 database file, default to "dkps.db" in the '
559 'same directory of this script')
560 parser.add_argument(
561 '-g', '--gnupg_homedir', default=os.path.join(SCRIPT_DIR, 'gnupg'),
562 help='path to the GnuGP home directory, default to "gnupg" in the same '
563 'directory of this script')
564 subparsers = parser.add_subparsers(dest='command')
565
566 parser_add = subparsers.add_parser('add', help='adds a new project')
567 parser_add.add_argument('-n', '--name', required=True,
568 help='name of the new project')
569 parser_add.add_argument('-u', '--uploader_key_file_path', required=True,
570 help="path to the uploader's public key file")
571 parser_add.add_argument('-r', '--requester_key_file_path', required=True,
572 help="path to the requester's public key file")
573 parser_add.add_argument('-f', '--filter_module_file_name', default=None,
574 help='file name of the filter module')
575
576 subparsers.add_parser('destroy', help='destroys the database')
577
578 parser_update = subparsers.add_parser('update',
579 help='updates an existing project')
580 parser_update.add_argument('-n', '--name', required=True,
581 help='name of the project')
582 parser_update.add_argument('-u', '--uploader_key_file_path', default=None,
583 help="path to the uploader's public key file")
584 parser_update.add_argument('-r', '--requester_key_file_path', default=None,
585 help="path to the requester's public key file")
586 parser_update.add_argument('-f', '--filter_module_file_name', default=None,
587 help='file name of the filter module')
588
589 parser_init = subparsers.add_parser('init', help='initializes the database')
590 parser_init.add_argument(
591 '-g', '--gpg_gen_key_args', action='append', nargs=2, default={},
592 help='arguments to use when generating GPG key for server')
593
594 subparsers.add_parser('list', help='lists all projects')
595
596 parser_listen = subparsers.add_parser(
597 'listen', help='starts the server, waiting for upload or request keys')
598 parser_listen.add_argument(
599 '--ip', default='0.0.0.0', help='IP to bind, default to 0.0.0.0')
600 parser_listen.add_argument(
601 '--port', type=int, default=5438, help='port to listen, default to 5438')
602
603 parser_rm = subparsers.add_parser('rm', help='removes an existing project')
604 parser_rm.add_argument('-n', '--name', required=True,
605 help='name of the project to remove')
606
607 return parser.parse_args()
608
609
610def main():
611 args = _ParseArguments()
612
Mao Huang041483e2015-09-14 23:28:18 +0800613 # TODO(littlecvr): Customizable logging level.
614 # TODO(littlecvr): Also print on stdout if it's not running in background.
615 logging.basicConfig(
616 filename=LOG_FILE_PATH,
617 format='%(asctime)s:%(levelname)s:%(funcName)s:%(lineno)d:%(message)s',
618 level=logging.DEBUG)
619 logging.debug('Parsed arguments: %r', args)
620
Mao Huang700663d2015-08-12 09:58:59 +0800621 dkps = DRMKeysProvisioningServer(args.database_file_path, args.gnupg_homedir)
622 if args.command == 'init':
623 # Convert from command line arguments to a dict.
624 gpg_gen_key_args_dict = {}
625 for pair in args.gpg_gen_key_args:
626 gpg_gen_key_args_dict[pair[0]] = pair[1]
627 dkps.Initialize(gpg_gen_key_args_dict)
628 elif args.command == 'destroy':
629 message = (
630 'This action will remove all projects and keys information and is NOT '
631 'recoverable! Are you sure? (y/N)')
632 answer = raw_input(textwrap.fill(message, 80) + ' ')
633 if answer.lower() != 'y' and answer.lower() != 'yes':
634 print 'OK, nothing will be removed.'
635 else:
636 print 'Removing all projects and keys information...',
637 dkps.Destroy()
638 print 'done.'
639 elif args.command == 'listen':
640 dkps.ListenForever(args.ip, args.port)
641 elif args.command == 'list':
642 print dkps.ListProjects()
643 elif args.command == 'add':
644 dkps.AddProject(
645 args.name, args.uploader_key_file_path, args.requester_key_file_path,
646 args.filter_module_file_name)
647 elif args.command == 'update':
648 dkps.UpdateProject(
649 args.name, args.uploader_key_file_path, args.requester_key_file_path,
650 args.filter_module_file_name)
651 elif args.command == 'rm':
652 dkps.RemoveProject(args.name)
653 else:
654 raise ValueError('Unknown command %s' % args.command)
655
656
657if __name__ == '__main__':
658 main()