~ubuntu-branches/ubuntu/precise/keystone/precise-security

« back to all changes in this revision

Viewing changes to keystone/logic/signer.py

  • Committer: Bazaar Package Importer
  • Author(s): Chuck Short
  • Date: 2011-08-23 10:18:22 UTC
  • Revision ID: james.westby@ubuntu.com-20110823101822-enve6zceb3lqhuvj
Tags: upstream-1.0~d4~20110823.1078
ImportĀ upstreamĀ versionĀ 1.0~d4~20110823.1078

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 logging
 
50
import urllib
 
51
 
 
52
 
 
53
LOG = logging.getLogger('keystone.signer')
 
54
 
 
55
 
 
56
class Signer(object):
 
57
    """Hacked up code from boto/connection.py"""
 
58
 
 
59
    def __init__(self, secret_key):
 
60
        secret_key = secret_key.encode()
 
61
        self.hmac = hmac.new(secret_key, digestmod=hashlib.sha1)
 
62
        if hashlib.sha256:
 
63
            self.hmac_256 = hmac.new(secret_key, digestmod=hashlib.sha256)
 
64
 
 
65
    def generate(self, credentials):
 
66
        """Generate auth string according to what SignatureVersion is given."""
 
67
        if credentials.params['SignatureVersion'] == '0':
 
68
            return self._calc_signature_0(credentials.params)
 
69
        if credentials.params['SignatureVersion'] == '1':
 
70
            return self._calc_signature_1(credentials.params)
 
71
        if credentials.params['SignatureVersion'] == '2':
 
72
            return self._calc_signature_2(credentials.params,
 
73
                                          credentials.verb,
 
74
                                          credentials.host,
 
75
                                          credentials.path)
 
76
        raise Exception('Unknown Signature Version: %s' %
 
77
                        credentials.params['SignatureVersion'])
 
78
 
 
79
    @staticmethod
 
80
    def _get_utf8_value(value):
 
81
        """Get the UTF8-encoded version of a value."""
 
82
        if not isinstance(value, str) and not isinstance(value, unicode):
 
83
            value = str(value)
 
84
        if isinstance(value, unicode):
 
85
            return value.encode('utf-8')
 
86
        else:
 
87
            return value
 
88
 
 
89
    def _calc_signature_0(self, params):
 
90
        """Generate AWS signature version 0 string."""
 
91
        s = params['Action'] + params['Timestamp']
 
92
        self.hmac.update(s)
 
93
        return base64.b64encode(self.hmac.digest())
 
94
 
 
95
    def _calc_signature_1(self, params):
 
96
        """Generate AWS signature version 1 string."""
 
97
        keys = params.keys()
 
98
        keys.sort(cmp=lambda x, y: cmp(x.lower(), y.lower()))
 
99
        for key in keys:
 
100
            self.hmac.update(key)
 
101
            val = self._get_utf8_value(params[key])
 
102
            self.hmac.update(val)
 
103
        return base64.b64encode(self.hmac.digest())
 
104
 
 
105
    def _calc_signature_2(self, params, verb, server_string, path):
 
106
        """Generate AWS signature version 2 string."""
 
107
        LOG.debug('using _calc_signature_2')
 
108
        string_to_sign = '%s\n%s\n%s\n' % (verb, server_string, path)
 
109
        if self.hmac_256:
 
110
            current_hmac = self.hmac_256
 
111
            params['SignatureMethod'] = 'HmacSHA256'
 
112
        else:
 
113
            current_hmac = self.hmac
 
114
            params['SignatureMethod'] = 'HmacSHA1'
 
115
        keys = params.keys()
 
116
        keys.sort()
 
117
        pairs = []
 
118
        for key in keys:
 
119
            val = self._get_utf8_value(params[key])
 
120
            val = urllib.quote(val, safe='-_~')
 
121
            pairs.append(urllib.quote(key, safe='') + '=' + val)
 
122
        qs = '&'.join(pairs)
 
123
        LOG.debug('query string: %s', qs)
 
124
        string_to_sign += qs
 
125
        LOG.debug('string_to_sign: %s', string_to_sign)
 
126
        current_hmac.update(string_to_sign)
 
127
        b64 = base64.b64encode(current_hmac.digest())
 
128
        LOG.debug('len(b64)=%d', len(b64))
 
129
        LOG.debug('base64 encoded digest: %s', b64)
 
130
        return b64
 
131
 
 
132
 
 
133
if __name__ == '__main__':
 
134
    print Signer('foo').generate({'SignatureMethod': 'HmacSHA256',
 
135
                                  'SignatureVersion': '2'},
 
136
                                 'get', 'server', '/foo')