1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30 """Serializer abstraction module
31
32 This module introduces a simple abstraction over the serialization
33 backend (currently json).
34
35 """
36
37
38
39
40
41 import re
42
43
44
45
46
47
48 import simplejson
49
50 from ganeti import errors
51 from ganeti import utils
52
53
54 _RE_EOLSP = re.compile("[ \t]+$", re.MULTILINE)
55
56
58 """Serialize a given object.
59
60 @param data: the data to serialize
61 @return: the string representation of data
62
63 """
64 encoded = simplejson.dumps(data)
65
66 txt = _RE_EOLSP.sub("", encoded)
67 if not txt.endswith("\n"):
68 txt += "\n"
69
70 return txt
71
72
74 """Unserialize data from a string.
75
76 @param txt: the json-encoded form
77
78 @return: the original data
79
80 """
81 return simplejson.loads(txt)
82
83
85 """Serialize a given object and authenticate it.
86
87 @param data: the data to serialize
88 @param key: shared hmac key
89 @param key_selector: name/id that identifies the key (in case there are
90 multiple keys in use, e.g. in a multi-cluster environment)
91 @return: the string representation of data signed by the hmac key
92
93 """
94 txt = DumpJson(data)
95 if salt is None:
96 salt = ""
97 signed_dict = {
98 "msg": txt,
99 "salt": salt,
100 }
101
102 if key_selector:
103 signed_dict["key_selector"] = key_selector
104 else:
105 key_selector = ""
106
107 signed_dict["hmac"] = utils.Sha1Hmac(key, txt, salt=salt + key_selector)
108
109 return DumpJson(signed_dict)
110
111
113 """Verify that a given message was signed with the given key, and load it.
114
115 @param txt: json-encoded hmac-signed message
116 @param key: the shared hmac key or a callable taking one argument (the key
117 selector), which returns the hmac key belonging to the key selector.
118 Typical usage is to pass a reference to the get method of a dict.
119 @rtype: tuple of original data, string
120 @return: original data, salt
121 @raises errors.SignatureError: if the message signature doesn't verify
122
123 """
124 signed_dict = LoadJson(txt)
125 if not isinstance(signed_dict, dict):
126 raise errors.SignatureError("Invalid external message")
127 try:
128 msg = signed_dict["msg"]
129 salt = signed_dict["salt"]
130 hmac_sign = signed_dict["hmac"]
131 except KeyError:
132 raise errors.SignatureError("Invalid external message")
133
134 if callable(key):
135
136 key_selector = signed_dict.get("key_selector", None)
137 hmac_key = key(key_selector)
138 if not hmac_key:
139 raise errors.SignatureError("No key with key selector '%s' found" %
140 key_selector)
141 else:
142 key_selector = ""
143 hmac_key = key
144
145 if not utils.VerifySha1Hmac(hmac_key, msg, hmac_sign,
146 salt=salt + key_selector):
147 raise errors.SignatureError("Invalid Signature")
148
149 return LoadJson(msg), salt
150
151
153 """Parses and verifies JSON data.
154
155 @type raw: string
156 @param raw: Input data in JSON format
157 @type verify_fn: callable
158 @param verify_fn: Verification function, usually from L{ht}
159 @return: De-serialized data
160
161 """
162 try:
163 data = LoadJson(raw)
164 except Exception, err:
165 raise errors.ParseError("Can't parse input data: %s" % err)
166
167 if not verify_fn(data):
168 raise errors.ParseError("Data does not match expected format: %s" %
169 verify_fn)
170
171 return data
172
173
174 Dump = DumpJson
175 Load = LoadJson
176 DumpSigned = DumpSignedJson
177 LoadSigned = LoadSignedJson
178