LCOV - code coverage report
Current view: top level - core - awssignaturev2.cpp (source / functions) Hit Total Coverage
Test: libqtaws 0.1.0 Lines: 45 45 100.0 %
Date: 2015-06-16 07:50:35 Functions: 7 7 100.0 %

          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 "awssignaturev2.h"
      21             : #include "awssignaturev2_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 <QDebug>
      30             : #include <QNetworkRequest>
      31             : #include <QUrl>
      32             : 
      33             : QTAWS_BEGIN_NAMESPACE
      34             : 
      35             : /**
      36             :  * @class  AwsSignatureV2
      37             :  *
      38             :  * @brief  Implements AWS Signature Version 2.
      39             :  *
      40             :  * @see    http://docs.aws.amazon.com/general/latest/gr/signature-version-2.html
      41             :  */
      42             : 
      43             : /**
      44             :  * @brief  Constructs a new AwsSignatureV2 object.
      45             :  *
      46             :  * Use instances of this object to provide Version 2 signatures for AWS services.
      47             :  *
      48             :  * @param  hashAlgorithm  Hash algorithm for signatures.  Must be either QCryptographicHash::Sha1
      49             :  *                        or QCryptographicHash::Sha256 (default, recommended).
      50             :  */
      51          18 : AwsSignatureV2::AwsSignatureV2(const QCryptographicHash::Algorithm hashAlgorithm)
      52          18 :         : AwsAbstractSignature(new AwsSignatureV2Private(this))
      53             : {
      54          18 :     Q_ASSERT((hashAlgorithm == QCryptographicHash::Sha1) || (hashAlgorithm == QCryptographicHash::Sha256));
      55          18 :     Q_D(AwsSignatureV2);
      56          18 :     d->hashAlgorithm = hashAlgorithm;
      57          18 : }
      58             : 
      59           1 : void AwsSignatureV2::sign(const AwsAbstractCredentials &credentials, const QNetworkAccessManager::Operation operation,
      60             :                           QNetworkRequest &request, const QByteArray &data) const
      61             : {
      62             :     Q_UNUSED(data) // Not included in V2 signatures.
      63           1 :     Q_D(const AwsSignatureV2);
      64             : 
      65             :     // Set the AccessKeyId, SignatureMethod, SignatureVersion and Timestamp query items, if not already.
      66           1 :     d->adornRequest(request, credentials);
      67             : 
      68             :     // Calculate the signature.
      69           1 :     const QByteArray stringToSign = d->canonicalRequest(operation, request.url());
      70             :     const QString signature = QString::fromUtf8(QUrl::toPercentEncoding(QString::fromUtf8(
      71           1 :         QMessageAuthenticationCode::hash(stringToSign, credentials.secretKey().toUtf8(),
      72           3 :                                          d->hashAlgorithm).toBase64())));
      73             : 
      74             :     // Append the signature to the request.
      75           2 :     QUrl url = request.url();
      76           1 :     url.setQuery(url.query() + QLatin1String("&Signature=") + signature);
      77           2 :     request.setUrl(url);
      78           1 : }
      79             : 
      80           1 : int AwsSignatureV2::version() const
      81             : {
      82           1 :     return 2;
      83             : }
      84             : 
      85             : /**
      86             :  * @internal
      87             :  *
      88             :  * @class  AwsSignatureV2Private
      89             :  *
      90             :  * @brief  Private implementation for AwsSignatureV2.
      91             :  *
      92             :  * @warning  This is an internal private implementation class, and as such external should
      93             :  *           code should **not** depend directly on anything contained within this class.
      94             :  *
      95             :  * @see    http://docs.aws.amazon.com/general/latest/gr/signature-version-4.html
      96             :  */
      97             : 
      98             : /**
      99             :  * @internal
     100             :  *
     101             :  * @brief  Constructs a new AwsSignatureV2Private object.
     102             :  *
     103             :  * @param  q  Pointer to this object's public AwsSignatureV2 instance.
     104             :  */
     105          18 : AwsSignatureV2Private::AwsSignatureV2Private(AwsSignatureV2 * const q) : AwsAbstractSignaturePrivate(q)
     106             : {
     107             : 
     108          18 : }
     109             : 
     110             : /**
     111             :  * @internal
     112             :  *
     113             :  * @brief  Add AWS Signature Version 2 adornments to an AWS request.
     114             :  *
     115             :  * In addition to service-specific request parameters, Amazon requires that version
     116             :  * 2 signatures contain a number of common query parameters.  This functions adds
     117             :  * those query parameters to \a request if they're not already present.
     118             :  *
     119             :  * The query parameters added by this function, as required by Amazon, are:
     120             :  *   * `AWSAccessKeyId` - set to \a credentials.accessKeyId().
     121             :  *   * `SignatureMethod` - set to `HMAC-SHA1` or `HMAC-SHA256`.
     122             :  *   * `SignatureVersion` - set to `2`.
     123             :  *   * `Timestamp` - set to a current UTC timestamp in an ISO 8601 format, like
     124             :  *                 `2013-10-30T12:34:56Z`.
     125             :  *
     126             :  * @param  request      Request to adorn.
     127             :  * @param  credentials  Credentials to use when adorning \a request.
     128             :  *
     129             :  * @see    signatureMethod
     130             :  * @see    http://docs.aws.amazon.com/general/latest/gr/signature-version-2.html
     131             :  */
     132           3 : void AwsSignatureV2Private::adornRequest(QNetworkRequest &request,
     133             :                                          const AwsAbstractCredentials &credentials) const
     134             : {
     135             :     // Set / add the necessary query items.
     136           3 :     QUrl url = request.url();
     137           6 :     QUrlQuery query(url);
     138           3 :     setQueryItem(query, QLatin1String("AWSAccessKeyId"), credentials.accessKeyId());
     139           3 :     setQueryItem(query, QLatin1String("SignatureVersion"), QLatin1String("2"));
     140           3 :     setQueryItem(query, QLatin1String("SignatureMethod"), QString::fromUtf8(signatureMethod(hashAlgorithm)));
     141             :     setQueryItem(query, QLatin1String("Timestamp"),
     142             :                     QString::fromUtf8(QUrl::toPercentEncoding(
     143             :                         QDateTime::currentDateTimeUtc().toString(QLatin1String("yyyy-MM-ddThh:mm:ssZ"))
     144             :                     )),
     145           3 :                     false); // Don't warn if its already set to something else.
     146             : 
     147             :     // If we've touched the query items (likely), then update the request.
     148           3 :     if (query != QUrlQuery(url)) {
     149           2 :         qDebug() << Q_FUNC_INFO << url;
     150           2 :         url.setQuery(query);
     151           2 :         qDebug() << Q_FUNC_INFO << url;
     152           2 :         request.setUrl(url);
     153           3 :     }
     154           3 : }
     155             : 
     156             : /**
     157             :  * @internal
     158             :  *
     159             :  * @brief  Create an AWS V2 Signature canonical request.
     160             :  *
     161             :  * This function creates a canonical representation of an AWS request as defined by
     162             :  * Amazon's V2 signature specification.
     163             :  *
     164             :  * For example, for the following HTTP `GET` request:
     165             :  *
     166             :  *     https://elasticmapreduce.amazonaws.com?Action=DescribeJobFlows&Version=2009-03-31&AWSAccessKeyId=AKIAIOSFODNN7EXAMPLE&SignatureVersion=2SignatureMethod=HmacSHA256Timestamp=2011-10-03T15%3A19%3A30
     167             :  *
     168             :  * this function will return the following canonical form:
     169             :  *
     170             :  *     GET
     171             :  *     elasticmapreduce.amazonaws.com
     172             :  *     /
     173             :  *     AWSAccessKeyId=AKIAIOSFODNN7EXAMPLE&Action=DescribeJobFlows&SignatureMethod=HmacSHA256&SignatureVersion=2&Timestamp=2011-10-03T15%3A19%3A30&Version=2009-03-31
     174             :  *
     175             :  * @note  All URL components are encoded to UTF-8, as required by Amazon.
     176             :  *
     177             :  * @param  operation  The HTTP method being requested.
     178             :  * @param  url        The URL being request.
     179             :  *
     180             :  * @return An AWS V2 Signature canonical request.
     181             :  *
     182             :  * @see http://docs.aws.amazon.com/general/latest/gr/signature-version-2.html
     183             :  */
     184           2 : QByteArray AwsSignatureV2Private::canonicalRequest(const QNetworkAccessManager::Operation operation,
     185             :                                                    const QUrl &url) const
     186             : {
     187           4 :     return httpMethod(operation).toUtf8() + '\n' +
     188           8 :            url.host().toUtf8() + '\n' +
     189           4 :            canonicalPath(url).toUtf8() + '\n' +
     190           6 :            canonicalQuery(QUrlQuery(url));
     191             : }
     192             : 
     193             : /**
     194             :  * @brief  Create an AWS V2 Signature method designation.
     195             :  *
     196             :  * This function returns a signature method designation, as defined by Amazon, for
     197             :  * use with V2 signatures.
     198             :  *
     199             :  * For example, if the algorith is `QCryptographicHash::Sha256`, this function will
     200             :  * return `HmacSHA256`.
     201             :  *
     202             :  * @note   Amazon only supports two algorithms for V2 signatures - SHA1 and SHA256.
     203             :  *
     204             :  * @param  algorithm  The hash algorithm to get the canonical designation for.
     205             :  *
     206             :  * @return An AWS V2 Signature method designation.
     207             :  *
     208             :  * @see    http://docs.aws.amazon.com/general/latest/gr/signature-version-2.html
     209             :  */
     210          16 : QByteArray AwsSignatureV2Private::signatureMethod(const QCryptographicHash::Algorithm algorithm) const
     211             : {
     212          16 :     switch (algorithm) {
     213           3 :         case QCryptographicHash::Sha1:     return "HmacSHA1";
     214           4 :         case QCryptographicHash::Sha256:   return "HmacSHA256";
     215             :         default:
     216           9 :             Q_ASSERT_X(false, Q_FUNC_INFO, "invalid algorithm");
     217           9 :             return "invalid-algorithm";
     218             :     }
     219             : }
     220             : 
     221             : QTAWS_END_NAMESPACE

Generated by: LCOV version 1.11