~0x44/nova/extdoc

« back to all changes in this revision

Viewing changes to nova/auth/signer.py

  • Committer: Jesse Andrews
  • Date: 2010-05-28 06:05:26 UTC
  • Revision ID: git-v1:bf6e6e718cdc7488e2da87b21e258ccc065fe499
initial commit

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Copyright [2010] [Anso Labs, LLC]
 
2
 
3
#    Licensed under the Apache License, Version 2.0 (the "License");
 
4
#    you may not use this file except in compliance with the License.
 
5
#    You may obtain a copy of the License at
 
6
 
7
#        http://www.apache.org/licenses/LICENSE-2.0
 
8
 
9
#    Unless required by applicable law or agreed to in writing, software
 
10
#    distributed under the License is distributed on an "AS IS" BASIS,
 
11
#    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 
12
#    See the License for the specific language governing permissions and
 
13
#    limitations under the License.
 
14
 
 
15
# PORTIONS OF THIS FILE ARE FROM:
 
16
# http://code.google.com/p/boto
 
17
# Copyright (c) 2006-2009 Mitch Garnaat http://garnaat.org/
 
18
#
 
19
# Permission is hereby granted, free of charge, to any person obtaining a
 
20
# copy of this software and associated documentation files (the
 
21
# "Software"), to deal in the Software without restriction, including
 
22
# without limitation the rights to use, copy, modify, merge, publish, dis-
 
23
# tribute, sublicense, and/or sell copies of the Software, and to permit
 
24
# persons to whom the Software is furnished to do so, subject to the fol-
 
25
# lowing conditions:
 
26
#
 
27
# The above copyright notice and this permission notice shall be included
 
28
# in all copies or substantial portions of the Software.
 
29
#
 
30
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
 
31
# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABIL-
 
32
# ITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
 
33
# SHALL THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 
 
34
# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 
35
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 
36
# IN THE SOFTWARE.
 
37
 
 
38
"""
 
39
Utility class for parsing signed AMI manifests.
 
40
"""
 
41
 
 
42
import logging
 
43
import hashlib
 
44
import hmac
 
45
import urllib
 
46
import base64
 
47
from nova.exception import Error
 
48
 
 
49
_log = logging.getLogger('signer')
 
50
logging.getLogger('signer').setLevel(logging.WARN)
 
51
 
 
52
class Signer(object):
 
53
    """ hacked up code from boto/connection.py """
 
54
    
 
55
    def __init__(self, secret_key):
 
56
        self.hmac = hmac.new(secret_key, digestmod=hashlib.sha1)
 
57
        if hashlib.sha256:
 
58
            self.hmac_256 = hmac.new(secret_key, digestmod=hashlib.sha256)
 
59
 
 
60
    def generate(self, params, verb, server_string, path):
 
61
        if params['SignatureVersion'] == '0':
 
62
            t = self._calc_signature_0(params)
 
63
        elif params['SignatureVersion'] == '1':
 
64
            t = self._calc_signature_1(params)
 
65
        elif params['SignatureVersion'] == '2':
 
66
            t = self._calc_signature_2(params, verb, server_string, path)
 
67
        else:
 
68
            raise Error('Unknown Signature Version: %s' % self.SignatureVersion)
 
69
        return t
 
70
        
 
71
    def _get_utf8_value(self, value):
 
72
        if not isinstance(value, str) and not isinstance(value, unicode):
 
73
            value = str(value)
 
74
        if isinstance(value, unicode):
 
75
            return value.encode('utf-8')
 
76
        else:
 
77
            return value
 
78
 
 
79
    def _calc_signature_0(self, params):
 
80
        s = params['Action'] + params['Timestamp']
 
81
        self.hmac.update(s)
 
82
        keys = params.keys()
 
83
        keys.sort(cmp = lambda x, y: cmp(x.lower(), y.lower()))
 
84
        pairs = []
 
85
        for key in keys:
 
86
            val = self._get_utf8_value(params[key])
 
87
            pairs.append(key + '=' + urllib.quote(val))
 
88
        return base64.b64encode(self.hmac.digest())
 
89
 
 
90
    def _calc_signature_1(self, params):
 
91
        keys = params.keys()
 
92
        keys.sort(cmp = lambda x, y: cmp(x.lower(), y.lower()))
 
93
        pairs = []
 
94
        for key in keys:
 
95
            self.hmac.update(key)
 
96
            val = self._get_utf8_value(params[key])
 
97
            self.hmac.update(val)
 
98
            pairs.append(key + '=' + urllib.quote(val))
 
99
        return base64.b64encode(self.hmac.digest())
 
100
 
 
101
    def _calc_signature_2(self, params, verb, server_string, path):
 
102
        _log.debug('using _calc_signature_2')
 
103
        string_to_sign = '%s\n%s\n%s\n' % (verb, server_string, path)
 
104
        if self.hmac_256:
 
105
            hmac = self.hmac_256
 
106
            params['SignatureMethod'] = 'HmacSHA256'
 
107
        else:
 
108
            hmac = self.hmac
 
109
            params['SignatureMethod'] = 'HmacSHA1'
 
110
        keys = params.keys()
 
111
        keys.sort()
 
112
        pairs = []
 
113
        for key in keys:
 
114
            val = self._get_utf8_value(params[key])
 
115
            pairs.append(urllib.quote(key, safe='') + '=' + urllib.quote(val, safe='-_~'))
 
116
        qs = '&'.join(pairs)
 
117
        _log.debug('query string: %s' % qs)
 
118
        string_to_sign += qs
 
119
        _log.debug('string_to_sign: %s' % string_to_sign)
 
120
        hmac.update(string_to_sign)
 
121
        b64 = base64.b64encode(hmac.digest())
 
122
        _log.debug('len(b64)=%d' % len(b64))
 
123
        _log.debug('base64 encoded digest: %s' % b64)
 
124
        return b64
 
125
 
 
126
if __name__ == '__main__':
 
127
    print Signer('foo').generate({"SignatureMethod": 'HmacSHA256', 'SignatureVersion': '2'}, "get", "server", "/foo")