Statistics
| Branch: | Tag: | Revision:

root / lib / serializer.py @ f4a2f532

History | View | Annotate | Download (4.5 kB)

1
#
2
#
3

    
4
# Copyright (C) 2007, 2008 Google Inc.
5
#
6
# This program is free software; you can redistribute it and/or modify
7
# it under the terms of the GNU General Public License as published by
8
# the Free Software Foundation; either version 2 of the License, or
9
# (at your option) any later version.
10
#
11
# This program is distributed in the hope that it will be useful, but
12
# WITHOUT ANY WARRANTY; without even the implied warranty of
13
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14
# General Public License for more details.
15
#
16
# You should have received a copy of the GNU General Public License
17
# along with this program; if not, write to the Free Software
18
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19
# 02110-1301, USA.
20

    
21
"""Serializer abstraction module
22

23
This module introduces a simple abstraction over the serialization
24
backend (currently json).
25

26
"""
27

    
28
import simplejson
29
import re
30
import hmac
31
import hashlib
32

    
33
from ganeti import errors
34

    
35

    
36
# Check whether the simplejson module supports indentation
37
_JSON_INDENT = 2
38
try:
39
  simplejson.dumps(1, indent=_JSON_INDENT)
40
except TypeError:
41
  _JSON_INDENT = None
42

    
43
_RE_EOLSP = re.compile('[ \t]+$', re.MULTILINE)
44

    
45

    
46
def DumpJson(data, indent=True):
47
  """Serialize a given object.
48

49
  @param data: the data to serialize
50
  @param indent: whether to indent output (depends on simplejson version)
51

52
  @return: the string representation of data
53

54
  """
55
  if not indent or _JSON_INDENT is None:
56
    txt = simplejson.dumps(data)
57
  else:
58
    txt = simplejson.dumps(data, indent=_JSON_INDENT)
59

    
60
  txt = _RE_EOLSP.sub("", txt)
61
  if not txt.endswith('\n'):
62
    txt += '\n'
63
  return txt
64

    
65

    
66
def LoadJson(txt):
67
  """Unserialize data from a string.
68

69
  @param txt: the json-encoded form
70

71
  @return: the original data
72

73
  """
74
  return simplejson.loads(txt)
75

    
76

    
77
def DumpSignedJson(data, key, salt=None):
78
  """Serialize a given object and authenticate it.
79

80
  @param data: the data to serialize
81
  @param key: shared hmac key
82
  @return: the string representation of data signed by the hmac key
83

84
  """
85
  txt = DumpJson(data, indent=False)
86
  if salt is None:
87
    salt = ''
88
  signed_dict = {
89
    'msg': txt,
90
    'salt': salt,
91
    'hmac': hmac.new(key, salt + txt, hashlib.sha256).hexdigest(),
92
  }
93
  return DumpJson(signed_dict)
94

    
95

    
96
def LoadSignedJson(txt, key, salt_verifier=None):
97
  """Verify that a given message was signed with the given key, and load it.
98

99
  @param txt: json-encoded hmac-signed message
100
  @param key: shared hmac key
101
  @param salt_verifier: function taking a salt as input and returning boolean
102
  @rtype: tuple of original data, string
103
  @return: (original data, salt)
104
  @raises errors.SignatureError: if the message signature doesn't verify
105

106
  """
107
  signed_dict = LoadJson(txt)
108
  if not isinstance(signed_dict, dict):
109
    raise errors.SignatureError('Invalid external message')
110
  try:
111
    msg = signed_dict['msg']
112
    salt = signed_dict['salt']
113
    hmac_sign = signed_dict['hmac']
114
  except KeyError:
115
    raise errors.SignatureError('Invalid external message')
116

    
117
  if salt and not salt_verifier:
118
    raise errors.SignatureError('Salted message is not verified')
119
  elif salt_verifier is not None:
120
    if not salt_verifier(salt):
121
      raise errors.SignatureError('Invalid salt')
122

    
123
  if hmac.new(key, salt + msg, hashlib.sha256).hexdigest() != hmac_sign:
124
    raise errors.SignatureError('Invalid Signature')
125
  return LoadJson(msg)
126

    
127

    
128
def SaltEqualTo(expected):
129
  """Helper salt verifier function that checks for equality.
130

131
  @type expected: string
132
  @param expected: expected salt
133
  @rtype: function
134
  @return: salt verifier that returns True if the target salt is "x"
135

136
  """
137
  return lambda salt: salt == expected
138

    
139

    
140
def SaltIn(expected):
141
  """Helper salt verifier function that checks for equality.
142

143
  @type expected: collection
144
  @param expected: collection of possible valid salts
145
  @rtype: function
146
  @return: salt verifier that returns True if the salt is in the collection
147

148
  """
149
  return lambda salt: salt in expected
150

    
151

    
152
def SaltInRange(min, max):
153
  """Helper salt verifier function that checks for equality.
154

155
  @type min: integer
156
  @param min: minimum salt value
157
  @type max: integer
158
  @param max: maximum salt value
159
  @rtype: function
160
  @return: salt verifier that returns True if the salt is in the min,max range
161

162
  """
163
  def _CheckSaltInRange(salt):
164
    try:
165
      i_salt = int(salt)
166
    except (TypeError, ValueError), err:
167
      return False
168

    
169
    return i_salt > min and i_salt < max
170

    
171
  return _CheckSaltInRange
172

    
173

    
174
Dump = DumpJson
175
Load = LoadJson
176
DumpSigned = DumpSignedJson
177
LoadSigned = LoadSignedJson