##// END OF EJS Templates
add notebook signing to nbformat
MinRK -
Show More
@@ -0,0 +1,141 b''
1 """Functions for signing notebooks"""
2 #-----------------------------------------------------------------------------
3 # Copyright (C) 2014, The IPython Development Team
4 #
5 # Distributed under the terms of the BSD License. The full license is in
6 # the file COPYING, distributed as part of this software.
7 #-----------------------------------------------------------------------------
8
9 #-----------------------------------------------------------------------------
10 # Imports
11 #-----------------------------------------------------------------------------
12
13 from contextlib import contextmanager
14 import hashlib
15 from hmac import HMAC
16
17 from IPython.utils.py3compat import string_types, unicode_type, cast_bytes
18
19 #-----------------------------------------------------------------------------
20 # Code
21 #-----------------------------------------------------------------------------
22
23
24 def yield_everything(obj):
25 """Yield every item in a container as bytes
26
27 Allows any JSONable object to be passed to an HMAC digester
28 without having to serialize the whole thing.
29 """
30 if isinstance(obj, dict):
31 for key in sorted(obj):
32 value = obj[key]
33 yield cast_bytes(key)
34 for b in yield_everything(value):
35 yield b
36 elif isinstance(obj, (list, tuple)):
37 for element in obj:
38 for b in yield_everything(element):
39 yield b
40 elif isinstance(obj, unicode_type):
41 yield obj.encode('utf8')
42 else:
43 yield unicode_type(obj).encode('utf8')
44
45
46 @contextmanager
47 def signature_removed(nb):
48 """Context manager for operating on a notebook with its signature removed
49
50 Used for excluding the previous signature when computing a notebook's signature.
51 """
52 save_signature = nb['metadata'].pop('signature', None)
53 try:
54 yield
55 finally:
56 if save_signature is not None:
57 nb['metadata']['signature'] = save_signature
58
59
60 def notebook_signature(nb, secret, scheme):
61 """Compute a notebook's signature
62
63 by hashing the entire contents of the notebook via HMAC digest.
64 scheme is the hashing scheme, which must be an attribute of the hashlib module,
65 as listed in hashlib.algorithms.
66 """
67 hmac = HMAC(secret, digestmod=getattr(hashlib, scheme))
68 # don't include the previous hash in the content to hash
69 with signature_removed(nb):
70 # sign the whole thing
71 for b in yield_everything(nb):
72 hmac.update(b)
73
74 return hmac.hexdigest()
75
76
77 def check_notebook_signature(nb, secret):
78 """Check a notebook's stored signature
79
80 If a signature is stored in the notebook's metadata,
81 a new signature is computed using the same hashing scheme,
82 and compared.
83
84 If no signature can be found, or the scheme of the existing signature is unavailable,
85 it will return False.
86 """
87 stored_signature = nb['metadata'].get('signature', None)
88 if not stored_signature \
89 or not isinstance(stored_signature, string_types) \
90 or ':' not in stored_signature:
91 return False
92 scheme, sig = stored_signature.split(':', 1)
93 try:
94 my_signature = notebook_signature(nb, secret, scheme)
95 except AttributeError:
96 return False
97 return my_signature == sig
98
99
100 def trust_notebook(nb, secret, scheme):
101 """Re-sign a notebook, indicating that its output is trusted
102
103 stores 'scheme:hmac-hexdigest' in notebook.metadata.signature
104
105 e.g. 'sha256:deadbeef123...'
106 """
107 signature = notebook_signature(nb, secret, scheme)
108 nb['metadata']['signature'] = "%s:%s" % (scheme, signature)
109
110
111 def mark_trusted_cells(nb, secret):
112 """Mark cells as trusted if the notebook's signature can be verified
113
114 Sets ``cell.trusted = True | False`` on all code cells,
115 depending on whether the stored signature can be verified.
116 """
117 if not nb['worksheets']:
118 # nothing to mark if there are no cells
119 return True
120 trusted = check_notebook_signature(nb, secret)
121 for cell in nb['worksheets'][0]['cells']:
122 if cell['cell_type'] == 'code':
123 cell['trusted'] = trusted
124 return trusted
125
126
127 def check_trusted_cells(nb):
128 """Return whether all code cells are trusted
129
130 If there are no code cells, return True.
131 """
132 if not nb['worksheets']:
133 return True
134 for cell in nb['worksheets'][0]['cells']:
135 if cell['cell_type'] != 'code':
136 continue
137 if not cell.get('trusted', False):
138 return False
139 return True
140
141 No newline at end of file
General Comments 0
You need to be logged in to leave comments. Login now