Line data Source code
1 : /*
2 : Copyright 2013-2015 Paul Colby
3 :
4 : This file is part of libqtaws.
5 :
6 : Libqtaws is free software: you can redistribute it and/or modify
7 : it under the terms of the GNU Lesser General Public License as published by
8 : the Free Software Foundation, either version 3 of the License, or
9 : (at your option) any later version.
10 :
11 : Libqtaws is distributed in the hope that it will be useful,
12 : but WITHOUT ANY WARRANTY; without even the implied warranty of
13 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 : GNU Lesser General Public License for more details.
15 :
16 : You should have received a copy of the GNU Lesser General Public License
17 : along with libqtaws. If not, see <http://www.gnu.org/licenses/>.
18 : */
19 :
20 : #include "awssignaturev1.h"
21 : #include "awssignaturev1_p.h"
22 :
23 : #if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) && QT_VERSION < QT_VERSION_CHECK(5, 1, 0)
24 : #include "qmessageauthenticationcode.h"
25 : #else
26 : #include <QMessageAuthenticationCode>
27 : #endif
28 :
29 : #include <QCryptographicHash>
30 : #include <QDebug>
31 : #include <QNetworkRequest>
32 : #include <QUrl>
33 :
34 : QTAWS_BEGIN_NAMESPACE
35 :
36 : /**
37 : * @class AwsSignatureV1
38 : *
39 : * @brief Implements AWS Signature Version 1 \ref deprecated "(deprecated by Amazon)"
40 : *
41 : * @deprecated Amazon has officially deprecated signature Version 1 in favor of later,
42 : * more secure signatures, such as AwsSignatureV2 and AwsSignatureV4.
43 : *
44 : * As version 1 signatures are rightly regarded as *insecure*, this class will refuse to sign
45 : * requests that use insecure transports such as HTTP instead of HTTPS. However, insecure
46 : * signatures can be enabled (why would you want to?) by defining `QTAWS_ALLOW_INSECURE_SIGNATURES`
47 : * when compiling this library.
48 : *
49 : * @see http://s3.amazonaws.com/awsdocs/SQS/20070501/sqs-dg-20070501.pdf
50 : * @see http://lmgtfy.com/?q=aws+signature+version+1+is+insecure
51 : */
52 :
53 : /**
54 : * @brief Constructs a new AwsSignatureV1 object.
55 : *
56 : * Use instances of this object to provide Version 1 signatures for AWS services.
57 : */
58 8 : AwsSignatureV1::AwsSignatureV1() : AwsSignatureV0(new AwsSignatureV1Private(this))
59 : {
60 :
61 8 : }
62 :
63 4 : int AwsSignatureV1::version() const
64 : {
65 4 : return 1;
66 : }
67 :
68 : /**
69 : * @internal
70 : *
71 : * @class AwsSignatureV1Private
72 : *
73 : * @brief Private implementation for AwsSignatureV1.
74 : *
75 : * @warning This is an internal private implementation class, and as such external should
76 : * code should **not** depend directly on anything contained within this class.
77 : *
78 : * @see http://docs.aws.amazon.com/general/latest/gr/signature-version-4.html
79 : */
80 :
81 : /**
82 : * @internal
83 : *
84 : * @brief Constructs a new AwsSignatureV1Private object.
85 : *
86 : * @param q Pointer to this object's public AwsSignatureV1 instance.
87 : */
88 8 : AwsSignatureV1Private::AwsSignatureV1Private(AwsSignatureV1 * const q) : AwsSignatureV0Private(q)
89 : {
90 :
91 8 : }
92 :
93 : /**
94 : * @internal
95 : *
96 : * @brief Create an AWS Signature version 1 canonical query.
97 : *
98 : * This function returns a string containing all non-empty query parameters in
99 : * sorted order (case-insensitive), with no separators at all.
100 : *
101 : * For example, for the following SQS query string:
102 : *
103 : * ?Action=CreateQueue&QueueName=queue2&AWSAccessKeyId=AKIAIOSFODNN7EXAMPLE&SignatureVersion=1&Expires=2007-01-12T12:00:00Z&Version=2006-04-01
104 : *
105 : * this function will return the following canonical form:
106 : *
107 : * ActionCreateQueueAWSAccessKeyIdAKIAIOSFODNN7EXAMPLEExpires2007-01-12T12:00:00ZQueueNamequeue2SignatureVersion1Version2006-04-01
108 : *
109 : * This function is very similar to AwsAbstractSignature::canonicalQuery(), except
110 : * that:
111 : * 1. this function sorts case-insensitively, whereas AwsAbstractSignature::canonicalQuery()
112 : * use a byte sort (ie is case sensitive); and
113 : * 2. this function excludes parameters with empty values, where
114 : * AwsAbstractSignature::canonicalQuery() includes all query parameters, regardless
115 : * of content; and
116 : * 3. this function does not use any separators in the generated string, whereas
117 : * AwsAbstractSignature::canonicalQuery() uses `&` and `=` separators just as
118 : * you would expect to see them in a typical query string; and
119 : * 4. this function does not perform any URL encoding of the query parameters,
120 : * whereas AwsAbstractSignature::canonicalQuery() URL encodes both parameter
121 : * keys and values.
122 : *
123 : * The AwsAbstractSignature::canonicalQuery() function is used by the later signature
124 : * algorithms, such as AwsSignatureV2 and AwsSignatureV4, as required by Amazon. Instead
125 : * this function is specific to version 1 signatures.
126 : *
127 : * @param query Query to encode the HTTP query string from.
128 : *
129 : * @return An AWS Signature canonical query string.
130 : *
131 : * @see http://docs.aws.amazon.com/AmazonDevPay/latest/DevPayDeveloperGuide/LSAPI_Auth_REST.html#CalculatingHMACSignature
132 : */
133 5 : QByteArray AwsSignatureV1Private::canonicalQuery(const QUrlQuery &query) const
134 : {
135 5 : QList<QStringPair> list = query.queryItems(QUrl::FullyDecoded);
136 5 : qSort(list.begin(), list.end(), AwsSignatureV1Private::caseInsensitiveLessThan);
137 10 : QString result;
138 35 : foreach (const QStringPair &pair, list) {
139 30 : if (!pair.second.isEmpty()) {
140 30 : result += pair.first + pair.second;
141 : }
142 5 : }
143 10 : return result.toUtf8();
144 : }
145 :
146 : /**
147 : * @internal
148 : *
149 : * @brief Is a key-value pair less than another key-value pair?
150 : *
151 : * This static function is used by the canonicalQuery function to sort query string
152 : * parameters in case-insensitive order, via Qt's qSort function.
153 : *
154 : * @param pair1 The first key-value (query string parameter) pair.
155 : * @param pair2 The second key-value (query string parameter) pair.
156 : *
157 : * @returns `true` if \a pair1 is less than \a pair2.
158 : */
159 96 : bool AwsSignatureV1Private::caseInsensitiveLessThan(const QStringPair &pair1, const QStringPair &pair2)
160 : {
161 96 : if (pair1.first.toLower() < pair2.first.toLower())
162 27 : return true;
163 69 : if (pair1.first.toLower() > pair2.first.toLower())
164 63 : return false;
165 6 : return (pair1.second.toLower() < pair2.second.toLower());
166 : }
167 :
168 : QTAWS_END_NAMESPACE
|