~ubuntu-branches/ubuntu/quantal/nova/quantal-proposed

« back to all changes in this revision

Viewing changes to nova/auth/signer.py

  • Committer: Package Import Robot
  • Author(s): Chuck Short
  • Date: 2012-08-16 14:04:11 UTC
  • mto: This revision was merged to the branch mainline in revision 84.
  • Revision ID: package-import@ubuntu.com-20120816140411-0mr4n241wmk30t9l
Tags: upstream-2012.2~f3
ImportĀ upstreamĀ versionĀ 2012.2~f3

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# vim: tabstop=4 shiftwidth=4 softtabstop=4
2
 
 
3
 
# Copyright 2010 United States Government as represented by the
4
 
# Administrator of the National Aeronautics and Space Administration.
5
 
# All Rights Reserved.
6
 
#
7
 
#    Licensed under the Apache License, Version 2.0 (the "License"); you may
8
 
#    not use this file except in compliance with the License. You may obtain
9
 
#    a copy of the License at
10
 
#
11
 
#         http://www.apache.org/licenses/LICENSE-2.0
12
 
#
13
 
#    Unless required by applicable law or agreed to in writing, software
14
 
#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
15
 
#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
16
 
#    License for the specific language governing permissions and limitations
17
 
#    under the License.
18
 
#
19
 
# PORTIONS OF THIS FILE ARE FROM:
20
 
# http://code.google.com/p/boto
21
 
# Copyright (c) 2006-2009 Mitch Garnaat http://garnaat.org/
22
 
#
23
 
# Permission is hereby granted, free of charge, to any person obtaining a
24
 
# copy of this software and associated documentation files (the
25
 
# "Software"), to deal in the Software without restriction, including
26
 
# without limitation the rights to use, copy, modify, merge, publish, dis-
27
 
# tribute, sublicense, and/or sell copies of the Software, and to permit
28
 
# persons to whom the Software is furnished to do so, subject to the fol-
29
 
# lowing conditions:
30
 
#
31
 
# The above copyright notice and this permission notice shall be included
32
 
# in all copies or substantial portions of the Software.
33
 
#
34
 
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
35
 
# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABIL-
36
 
# ITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
37
 
# SHALL THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
38
 
# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
39
 
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
40
 
# IN THE SOFTWARE.
41
 
 
42
 
"""
43
 
Utility class for parsing signed AMI manifests.
44
 
"""
45
 
 
46
 
import base64
47
 
import hashlib
48
 
import hmac
49
 
import urllib
50
 
 
51
 
try:
52
 
    # NOTE(vish): for new boto
53
 
    import boto
54
 
    # for boto.utils
55
 
    import boto.provider
56
 
    # NOTE(vish): for old boto
57
 
    import boto.utils
58
 
except ImportError:
59
 
    boto = None
60
 
 
61
 
from nova import exception
62
 
from nova.openstack.common import log as logging
63
 
 
64
 
 
65
 
LOG = logging.getLogger(__name__)
66
 
 
67
 
 
68
 
class Signer(object):
69
 
    """Hacked up code from boto/connection.py"""
70
 
 
71
 
    def __init__(self, secret_key):
72
 
        self.hmac = hmac.new(secret_key, digestmod=hashlib.sha1)
73
 
        if hashlib.sha256:
74
 
            self.hmac_256 = hmac.new(secret_key, digestmod=hashlib.sha256)
75
 
        else:
76
 
            self.hmac_256 = None
77
 
 
78
 
    def s3_authorization(self, headers, verb, path):
79
 
        """Generate S3 authorization string."""
80
 
        if not boto:
81
 
            raise exception.NovaException('boto is not installed')
82
 
        c_string = boto.utils.canonical_string(verb, path, headers)
83
 
        hmac_copy = self.hmac.copy()
84
 
        hmac_copy.update(c_string)
85
 
        b64_hmac = base64.encodestring(hmac_copy.digest()).strip()
86
 
        return b64_hmac
87
 
 
88
 
    def generate(self, params, verb, server_string, path):
89
 
        """Generate auth string according to what SignatureVersion is given.
90
 
 
91
 
        The signature method must be SHA1 or SHA256.
92
 
 
93
 
        """
94
 
        if params['SignatureVersion'] == '0':
95
 
            return self._calc_signature_0(params)
96
 
        if params['SignatureVersion'] == '1':
97
 
            return self._calc_signature_1(params)
98
 
        if params['SignatureVersion'] == '2':
99
 
            return self._calc_signature_2(params, verb, server_string, path)
100
 
        raise exception.NovaException('Unknown Signature Version: %s' %
101
 
                    params['SignatureVersion'])
102
 
 
103
 
    @staticmethod
104
 
    def _get_utf8_value(value):
105
 
        """Get the UTF8-encoded version of a value."""
106
 
        if not isinstance(value, str) and not isinstance(value, unicode):
107
 
            value = str(value)
108
 
        if isinstance(value, unicode):
109
 
            return value.encode('utf-8')
110
 
        else:
111
 
            return value
112
 
 
113
 
    def _calc_signature_0(self, params):
114
 
        """Generate AWS signature version 0 string."""
115
 
        s = params['Action'] + params['Timestamp']
116
 
        self.hmac.update(s)
117
 
        keys = params.keys()
118
 
        keys.sort(cmp=lambda x, y: cmp(x.lower(), y.lower()))
119
 
        pairs = []
120
 
        for key in keys:
121
 
            val = self._get_utf8_value(params[key])
122
 
            pairs.append(key + '=' + urllib.quote(val))
123
 
        return base64.b64encode(self.hmac.digest())
124
 
 
125
 
    def _calc_signature_1(self, params):
126
 
        """Generate AWS signature version 1 string."""
127
 
        keys = params.keys()
128
 
        keys.sort(cmp=lambda x, y: cmp(x.lower(), y.lower()))
129
 
        pairs = []
130
 
        for key in keys:
131
 
            self.hmac.update(key)
132
 
            val = self._get_utf8_value(params[key])
133
 
            self.hmac.update(val)
134
 
            pairs.append(key + '=' + urllib.quote(val))
135
 
        return base64.b64encode(self.hmac.digest())
136
 
 
137
 
    def _calc_signature_2(self, params, verb, server_string, path):
138
 
        """Generate AWS signature version 2 string."""
139
 
        LOG.debug('using _calc_signature_2')
140
 
        string_to_sign = '%s\n%s\n%s\n' % (verb, server_string, path)
141
 
 
142
 
        if 'SignatureMethod' not in params:
143
 
            raise exception.NovaException('No SignatureMethod specified')
144
 
 
145
 
        if params['SignatureMethod'] == 'HmacSHA256':
146
 
            if not self.hmac_256:
147
 
                msg = _('SHA256 not supported on this server')
148
 
                raise exception.NovaException(msg)
149
 
            current_hmac = self.hmac_256
150
 
        elif params['SignatureMethod'] == 'HmacSHA1':
151
 
            current_hmac = self.hmac
152
 
        else:
153
 
            raise exception.NovaException('SignatureMethod %s not supported'
154
 
                                  % params['SignatureMethod'])
155
 
 
156
 
        keys = params.keys()
157
 
        keys.sort()
158
 
        pairs = []
159
 
        for key in keys:
160
 
            val = self._get_utf8_value(params[key])
161
 
            val = urllib.quote(val, safe='-_~')
162
 
            pairs.append(urllib.quote(key, safe='') + '=' + val)
163
 
        qs = '&'.join(pairs)
164
 
        LOG.debug('query string: %s', qs)
165
 
        string_to_sign += qs
166
 
        LOG.debug('string_to_sign: %s', string_to_sign)
167
 
        current_hmac.update(string_to_sign)
168
 
        b64 = base64.b64encode(current_hmac.digest())
169
 
        LOG.debug('len(b64)=%d', len(b64))
170
 
        LOG.debug('base64 encoded digest: %s', b64)
171
 
        return b64
172
 
 
173
 
 
174
 
if __name__ == '__main__':
175
 
    print Signer('foo').generate({'SignatureVersion': '2',
176
 
                                  'SignatureMethod': 'HmacSHA256'},
177
 
                                  'get', 'server', '/foo')