LCOV - code coverage report
Current view: top level - core - awssignaturev0.cpp (source / functions) Hit Total Coverage
Test: libqtaws 0.1.0 Lines: 43 43 100.0 %
Date: 2015-06-16 07:50:35 Functions: 9 9 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 "awssignaturev0.h"
      21             : #include "awssignaturev0_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  AwsSignatureV0
      38             :  *
      39             :  * @brief  Implements AWS Signature Version 0 \ref deprecated "(deprecated by Amazon)"
      40             :  *
      41             :  * @deprecated  Amazon has officially deprecated signature Version 0 in favor of later,
      42             :  *              more secure signatures, such as AwsSignatureV2 and AwsSignatureV4.
      43             :  *
      44             :  * As version 0 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             :  */
      51             : 
      52             : /**
      53             :  * @brief  Constructs a new AwsSignatureV0 object.
      54             :  *
      55             :  * Use instances of this object to provide Version 0 signatures for AWS services.
      56             :  */
      57           8 : AwsSignatureV0::AwsSignatureV0() : AwsAbstractSignature(new AwsSignatureV0Private(this))
      58             : {
      59             : 
      60           8 : }
      61             : 
      62             : /**
      63             :  * @internal
      64             :  *
      65             :  * @brief  Constructs a new AwsSignatureV0 object with a specific private implementation.
      66             :  *
      67             :  * This internal constructor allows derived classes to provide their own derived
      68             :  * private implementation.  Specifically, AwsSignatureV1 uses this constructor to
      69             :  * extend this class.
      70             :  *
      71             :  * @param  d  Internal private implementation to use.
      72             :  *
      73             :  * @see    AwsSignatureV1
      74             :  */
      75           8 : AwsSignatureV0::AwsSignatureV0(AwsSignatureV0Private * const d) : AwsAbstractSignature(d)
      76             : {
      77             : 
      78           8 : }
      79             : 
      80           6 : void AwsSignatureV0::sign(const AwsAbstractCredentials &credentials, const QNetworkAccessManager::Operation operation,
      81             :                           QNetworkRequest &request, const QByteArray &data) const
      82             : {
      83             :     Q_UNUSED(operation) // Not included in V0 signatures.
      84             :     Q_UNUSED(data)      // Not included in V0 signatures.
      85           6 :     Q_D(const AwsSignatureV0);
      86             : 
      87             :     // Refuse to sign non-HTTPS requests, unless built with QTAWS_ALLOW_INSECURE_SIGNATURES defined.
      88             : #ifndef QTAWS_ALLOW_INSECURE_SIGNATURES
      89           6 :     if (request.url().scheme() != QString::fromLatin1("https")) {
      90           3 :         qWarning("AwsSignatureV%d::sign Refusing to sign insecure (non-HTTPS) request", version());
      91           3 :         Q_ASSERT_X(false, Q_FUNC_INFO, "insecure V1 signatures not enabled");
      92           9 :         return;
      93             :     }
      94             : #endif
      95             : 
      96             :     // Set the AWSAccessKeyId, SignatureVersion and Timestamp query items, if not already.
      97           3 :     d->adornRequest(request, credentials);
      98             : 
      99             :     // Calculate the signature.
     100           3 :     const QByteArray stringToSign = d->canonicalQuery(QUrlQuery(request.url().query()));
     101             :     const QString signature = QString::fromUtf8(QUrl::toPercentEncoding(QString::fromUtf8(
     102           3 :         QMessageAuthenticationCode::hash(stringToSign, credentials.secretKey().toUtf8(),
     103           9 :                                          QCryptographicHash::Sha1).toBase64())));
     104             : 
     105             :     // Append the signature to the request.
     106           6 :     QUrl url = request.url();
     107           3 :     url.setQuery(url.query() + QLatin1String("&Signature=") + signature);
     108           6 :     request.setUrl(url);
     109             : }
     110             : 
     111           6 : int AwsSignatureV0::version() const
     112             : {
     113           6 :     return 0;
     114             : }
     115             : 
     116             : /**
     117             :  * @internal
     118             :  *
     119             :  * @class  AwsSignatureV0Private
     120             :  *
     121             :  * @brief  Private implementation for AwsSignatureV0.
     122             :  *
     123             :  * @warning  This is an internal private implementation class, and as such external should
     124             :  *           code should **not** depend directly on anything contained within this class.
     125             :  *
     126             :  * @see    http://docs.aws.amazon.com/general/latest/gr/signature-version-4.html
     127             :  */
     128             : 
     129             : /**
     130             :  * @internal
     131             :  *
     132             :  * @brief  Constructs a new AwsSignatureV0Private object.
     133             :  *
     134             :  * @param  q  Pointer to this object's public AwsSignatureV0 instance.
     135             :  */
     136          16 : AwsSignatureV0Private::AwsSignatureV0Private(AwsSignatureV0 * const q) : AwsAbstractSignaturePrivate(q)
     137             : {
     138             : 
     139          16 : }
     140             : 
     141             : /**
     142             :  * @brief AwsSignatureV0Private destructor.
     143             :  */
     144          24 : AwsSignatureV0Private::~AwsSignatureV0Private()
     145             : {
     146             : 
     147          24 : }
     148             : 
     149             : /**
     150             :  * @internal
     151             :  *
     152             :  * @brief  Add AWS Signature Version 0 adornments to an AWS request.
     153             :  *
     154             :  * In addition to service-specific request parameters, Amazon requires that version
     155             :  * 1 signatures contain a number of common query parameters.  This functions adds
     156             :  * those query parameters to \a request if they're not already present.
     157             :  *
     158             :  * The query parameters added by this function, as required by Amazon, are:
     159             :  *   * `AWSAccessKeyId` - set to \a credentials.accessKeyId().
     160             :  *   * `SignatureVersion` - set to `0`.
     161             :  *   * `Timestamp` - set to a current UTC timestamp in an ISO 8601 format, like
     162             :  *                 `2013-10-30T12:34:56Z`, unless an `Expires` value is present,
     163             :  *                 in which case no `Timestamp` parameter is added.
     164             :  *
     165             :  * @note   The `SignatureVersion` header is optional for version 0 signatures, but
     166             :  *         this function always includes it for clarity.
     167             :  *
     168             :  * @param  request         Request to adorn.
     169             :  * @param  credentials     Credentials to use when adorning \a request.
     170             :  *
     171             :  * @see    http://s3.amazonaws.com/awsdocs/SQS/20070501/sqs-dg-20070501.pdf
     172             :  */
     173           5 : void AwsSignatureV0Private::adornRequest(QNetworkRequest &request,
     174             :                                          const AwsAbstractCredentials &credentials) const
     175             : {
     176           5 :     Q_Q(const AwsSignatureV0);
     177             : 
     178             :     // Set / add the necessary query items.
     179           5 :     QUrl url = request.url();
     180          10 :     QUrlQuery query(url);
     181           5 :     setQueryItem(query, QLatin1String("AWSAccessKeyId"), credentials.accessKeyId());
     182           5 :     setQueryItem(query, QLatin1String("SignatureVersion"), QString::fromLatin1("%1").arg(q->version()));
     183             : 
     184             :     // Amazon: "Query requests must include either Timestamp or Expires, but not both."
     185             :     // See http://s3.amazonaws.com/awsdocs/SQS/20070501/sqs-dg-20070501.pdf
     186           5 :     if (!query.hasQueryItem(QLatin1String("Expires"))) {
     187             :         setQueryItem(query, QLatin1String("Timestamp"),
     188             :                         QString::fromUtf8(QUrl::toPercentEncoding(
     189             :                             QDateTime::currentDateTimeUtc().toString(QLatin1String("yyyy-MM-ddThh:mm:ssZ"))
     190             :                         )),
     191           3 :                         false); // Don't warn if its already set to something else.
     192             :     }
     193             : 
     194             :     // If we've touched the query items (likely), then update the request.
     195           5 :     if (query != QUrlQuery(url.query())) {
     196           3 :         qDebug() << Q_FUNC_INFO << url;
     197           3 :         url.setQuery(query);
     198           3 :         qDebug() << Q_FUNC_INFO << url;
     199           3 :         request.setUrl(url);
     200           5 :     }
     201           5 : }
     202             : 
     203             : /**
     204             :  * @internal
     205             :  *
     206             :  * @brief  Create an AWS Signature version 0 canonical query.
     207             :  *
     208             :  * This function returns a string containing the concatenation of `Action` and
     209             :  * `timestamp` (or `Expires`) query parameters.
     210             :  *
     211             :  * For example, for the following SQS query string:
     212             :  *
     213             :  *     ?Action=CreateQueue&QueueName=queue2&AWSAccessKeyId=AKIAIOSFODNN7EXAMPLE&SignatureVersion=1&Expires=2007-01-12T12:00:00Z&Version=2006-04-01
     214             :  *
     215             :  * this function will return the following canonical form:
     216             :  *
     217             :  *     CreateQueue2007-01-12T12:00:00Z
     218             :  *
     219             :  * @param  query  Query to encode the HTTP query string from.
     220             :  *
     221             :  * @pre    \a query must already contain an `Action` and either a `Timestamp` or
     222             :  *         `Expires` query paramter.  See adornRequest().
     223             :  *
     224             :  * @return An AWS Signature canonical query string.
     225             :  *
     226             :  * @see    adornRequest()
     227             :  * @see    http://s3.amazonaws.com/awsdocs/SQS/20070501/sqs-dg-20070501.pdf
     228             :  */
     229           4 : QByteArray AwsSignatureV0Private::canonicalQuery(const QUrlQuery &query) const
     230             : {
     231           4 :     Q_ASSERT_X(query.hasQueryItem(QLatin1String("Action")), Q_FUNC_INFO, "Action query parameter required");
     232             :     Q_ASSERT_X(query.hasQueryItem(QLatin1String("Timestamp")) || query.hasQueryItem(QLatin1String("Expires")),
     233           4 :                Q_FUNC_INFO, "Timestamp or Expires query parameter required");
     234             : 
     235          16 :     return (query.queryItemValue(QLatin1String("Action")) + query.queryItemValue(QLatin1String(
     236          16 :                 (query.hasQueryItem(QLatin1String("Timestamp"))) ? "Timestamp" : "Expires"))).toUtf8();
     237             : }
     238             : 
     239             : QTAWS_END_NAMESPACE

Generated by: LCOV version 1.11