Revision 72ca1dcb

b/lib/serializer.py
101 101
  return simplejson.loads(txt)
102 102

  
103 103

  
104
def DumpSignedJson(data, key, salt=None):
104
def DumpSignedJson(data, key, salt=None, key_selector=None):
105 105
  """Serialize a given object and authenticate it.
106 106

  
107 107
  @param data: the data to serialize
108 108
  @param key: shared hmac key
109
  @param key_selector: name/id that identifies the key (in case there are
110
    multiple keys in use, e.g. in a multi-cluster environment)
109 111
  @return: the string representation of data signed by the hmac key
110 112

  
111 113
  """
......
115 117
  signed_dict = {
116 118
    'msg': txt,
117 119
    'salt': salt,
118
    'hmac': hmac.new(key, salt + txt, sha1).hexdigest(),
119 120
  }
121
  if key_selector:
122
    signed_dict["key_selector"] = key_selector
123
    message = salt + key_selector + txt
124
  else:
125
    message = salt + txt
126
  signed_dict["hmac"] = hmac.new(key, message,
127
                                 sha1).hexdigest()
128

  
120 129
  return DumpJson(signed_dict, indent=False)
121 130

  
122 131

  
......
124 133
  """Verify that a given message was signed with the given key, and load it.
125 134

  
126 135
  @param txt: json-encoded hmac-signed message
127
  @param key: shared hmac key
136
  @param key: the shared hmac key or a callable taking one argument (the key
137
    selector), which returns the hmac key belonging to the key selector.
138
    Typical usage is to pass a reference to the get method of a dict.
128 139
  @rtype: tuple of original data, string
129 140
  @return: original data, salt
130 141
  @raises errors.SignatureError: if the message signature doesn't verify
......
140 151
  except KeyError:
141 152
    raise errors.SignatureError('Invalid external message')
142 153

  
143
  if hmac.new(key, salt + msg, sha1).hexdigest() != hmac_sign:
154
  if callable(key):
155
    key_selector = signed_dict.get("key_selector", None)
156
    hmac_key = key(key_selector)
157
    if not hmac_key:
158
      raise errors.SignatureError("No key with key selector '%s' found" %
159
                                  key_selector)
160
  else:
161
    key_selector = ""
162
    hmac_key = key
163

  
164
  if hmac.new(hmac_key, salt + key_selector + msg,
165
              sha1).hexdigest() != hmac_sign:
144 166
    raise errors.SignatureError('Invalid Signature')
145 167

  
146 168
  return LoadJson(msg), salt
b/test/ganeti.serializer_unittest.py
71 71
      self.assertEqualValues(LoadSigned(DumpSigned(data, "mykey"), "mykey"),
72 72
                             (data, ''))
73 73
      self.assertEqualValues(LoadSigned(DumpSigned(data, "myprivatekey",
74
                                                   "mysalt"),
74
                                                   salt="mysalt"),
75 75
                                        "myprivatekey"),
76 76
                             (data, "mysalt"))
77 77

  
78
      keydict = {"mykey_id": "myprivatekey"}
79
      self.assertEqualValues(LoadSigned(DumpSigned(data,
80
                                                   "myprivatekey",
81
                                                   salt="mysalt",
82
                                                   key_selector="mykey_id"),
83
                                        keydict.get),
84
                             (data, "mysalt"))
85

  
78 86
    self.assertRaises(errors.SignatureError, serializer.LoadSigned,
79 87
                      serializer.DumpSigned("test", "myprivatekey"),
80 88
                      "myotherkey")

Also available in: Unified diff