Statistics
| Branch: | Tag: | Revision:

root / lib / serializer.py @ 1fe93c75

History | View | Annotate | Download (3.1 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

    
32
from ganeti import errors
33

    
34
try:
35
  from hashlib import sha1
36
except ImportError:
37
  import sha as sha1
38

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

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

    
48

    
49
def DumpJson(data, indent=True):
50
  """Serialize a given object.
51

52
  @param data: the data to serialize
53
  @param indent: whether to indent output (depends on simplejson version)
54

55
  @return: the string representation of data
56

57
  """
58
  if not indent or _JSON_INDENT is None:
59
    txt = simplejson.dumps(data)
60
  else:
61
    txt = simplejson.dumps(data, indent=_JSON_INDENT)
62

    
63
  txt = _RE_EOLSP.sub("", txt)
64
  if not txt.endswith('\n'):
65
    txt += '\n'
66
  return txt
67

    
68

    
69
def LoadJson(txt):
70
  """Unserialize data from a string.
71

72
  @param txt: the json-encoded form
73

74
  @return: the original data
75

76
  """
77
  return simplejson.loads(txt)
78

    
79

    
80
def DumpSignedJson(data, key, salt=None):
81
  """Serialize a given object and authenticate it.
82

83
  @param data: the data to serialize
84
  @param key: shared hmac key
85
  @return: the string representation of data signed by the hmac key
86

87
  """
88
  txt = DumpJson(data, indent=False)
89
  if salt is None:
90
    salt = ''
91
  signed_dict = {
92
    'msg': txt,
93
    'salt': salt,
94
    'hmac': hmac.new(key, salt + txt, sha1).hexdigest(),
95
  }
96
  return DumpJson(signed_dict)
97

    
98

    
99
def LoadSignedJson(txt, key):
100
  """Verify that a given message was signed with the given key, and load it.
101

102
  @param txt: json-encoded hmac-signed message
103
  @param key: shared hmac key
104
  @rtype: tuple of original data, string
105
  @return: original data, salt
106
  @raises errors.SignatureError: if the message signature doesn't verify
107

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

    
119
  if hmac.new(key, salt + msg, sha1).hexdigest() != hmac_sign:
120
    raise errors.SignatureError('Invalid Signature')
121

    
122
  return LoadJson(msg), salt
123

    
124

    
125
Dump = DumpJson
126
Load = LoadJson
127
DumpSigned = DumpSignedJson
128
LoadSigned = LoadSignedJson