/**
 *    Copyright (C) 2014 10gen Inc.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */

#include "mongo/crypto/crypto.h"
#include "mongo/unittest/unittest.h"

namespace mongo {
namespace {
const int digestLen = 20;

// SHA-1 test vectors from http://csrc.nist.gov/groups/ST/toolkit/documents/Examples/SHA_All.pdf
const struct {
    const char* msg;
    unsigned char hash[digestLen];
} sha1Tests[] = {{"abc",
                  {0xa9,
                   0x99,
                   0x3e,
                   0x36,
                   0x47,
                   0x06,
                   0x81,
                   0x6a,
                   0xba,
                   0x3e,
                   0x25,
                   0x71,
                   0x78,
                   0x50,
                   0xc2,
                   0x6c,
                   0x9c,
                   0xd0,
                   0xd8,
                   0x9d}},

                 {"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq",
                  {0x84,
                   0x98,
                   0x3E,
                   0x44,
                   0x1C,
                   0x3B,
                   0xD2,
                   0x6E,
                   0xBA,
                   0xAE,
                   0x4A,
                   0xA1,
                   0xF9,
                   0x51,
                   0x29,
                   0xE5,
                   0xE5,
                   0x46,
                   0x70,
                   0xF1}}};

TEST(CryptoVectors, SHA1) {
    unsigned char sha1Result[digestLen];
    size_t numTests = sizeof(sha1Tests) / sizeof(sha1Tests[0]);
    for (size_t i = 0; i < numTests; i++) {
        ASSERT(crypto::sha1(reinterpret_cast<const unsigned char*>(sha1Tests[i].msg),
                            strlen(sha1Tests[i].msg),
                            sha1Result))
            << "Failed SHA1 iteration " << i;
        ASSERT(0 == memcmp(sha1Tests[i].hash, sha1Result, digestLen)) << "Failed SHA1 iteration "
                                                                      << i;
    }
}

const int maxKeySize = 80;
const int maxDataSize = 54;
// HMAC-SHA-1 test vectors from http://tools.ietf.org/html/rfc2202.html
const struct {
    unsigned char key[maxKeySize];
    int keyLen;
    unsigned char data[maxDataSize];
    int dataLen;
    unsigned char hash[digestLen];
} hmacSha1Tests[] = {
    // RFC test case 1
    {{0x0b,
      0x0b,
      0x0b,
      0x0b,
      0x0b,
      0x0b,
      0x0b,
      0x0b,
      0x0b,
      0x0b,
      0x0b,
      0x0b,
      0x0b,
      0x0b,
      0x0b,
      0x0b,
      0x0b,
      0x0b,
      0x0b,
      0x0b},
     20,
     {0x48, 0x69, 0x20, 0x54, 0x68, 0x65, 0x72, 0x65},
     8,
     {0xb6,
      0x17,
      0x31,
      0x86,
      0x55,
      0x05,
      0x72,
      0x64,
      0xe2,
      0x8b,
      0xc0,
      0xb6,
      0xfb,
      0x37,
      0x8c,
      0x8e,
      0xf1,
      0x46,
      0xbe,
      0x00}},

    // RFC test case 3
    {{0xaa,
      0xaa,
      0xaa,
      0xaa,
      0xaa,
      0xaa,
      0xaa,
      0xaa,
      0xaa,
      0xaa,
      0xaa,
      0xaa,
      0xaa,
      0xaa,
      0xaa,
      0xaa,
      0xaa,
      0xaa,
      0xaa,
      0xaa},
     20,
     {0xdd,
      0xdd,
      0xdd,
      0xdd,
      0xdd,
      0xdd,
      0xdd,
      0xdd,
      0xdd,
      0xdd,
      0xdd,
      0xdd,
      0xdd,
      0xdd,
      0xdd,
      0xdd,
      0xdd,
      0xdd,
      0xdd,
      0xdd,
      0xdd,
      0xdd,
      0xdd,
      0xdd,
      0xdd,
      0xdd,
      0xdd,
      0xdd,
      0xdd,
      0xdd,
      0xdd,
      0xdd,
      0xdd,
      0xdd,
      0xdd,
      0xdd,
      0xdd,
      0xdd,
      0xdd,
      0xdd,
      0xdd,
      0xdd,
      0xdd,
      0xdd,
      0xdd,
      0xdd,
      0xdd,
      0xdd,
      0xdd,
      0xdd},
     50,
     {0x12,
      0x5d,
      0x73,
      0x42,
      0xb9,
      0xac,
      0x11,
      0xcd,
      0x91,
      0xa3,
      0x9a,
      0xf4,
      0x8a,
      0xa1,
      0x7b,
      0x4f,
      0x63,
      0xf1,
      0x75,
      0xd3}},

    // RFC test case 4
    {{0x01,
      0x02,
      0x03,
      0x04,
      0x05,
      0x06,
      0x07,
      0x08,
      0x09,
      0x0a,
      0x0b,
      0x0c,
      0x0d,
      0x0e,
      0x0f,
      0x10,
      0x11,
      0x12,
      0x13,
      0x14,
      0x15,
      0x16,
      0x17,
      0x18,
      0x19},
     25,
     {0xcd,
      0xcd,
      0xcd,
      0xcd,
      0xcd,
      0xcd,
      0xcd,
      0xcd,
      0xcd,
      0xcd,
      0xcd,
      0xcd,
      0xcd,
      0xcd,
      0xcd,
      0xcd,
      0xcd,
      0xcd,
      0xcd,
      0xcd,
      0xcd,
      0xcd,
      0xcd,
      0xcd,
      0xcd,
      0xcd,
      0xcd,
      0xcd,
      0xcd,
      0xcd,
      0xcd,
      0xcd,
      0xcd,
      0xcd,
      0xcd,
      0xcd,
      0xcd,
      0xcd,
      0xcd,
      0xcd,
      0xcd,
      0xcd,
      0xcd,
      0xcd,
      0xcd,
      0xcd,
      0xcd,
      0xcd,
      0xcd,
      0xcd},
     50,
     {0x4c,
      0x90,
      0x07,
      0xf4,
      0x02,
      0x62,
      0x50,
      0xc6,
      0xbc,
      0x84,
      0x14,
      0xf9,
      0xbf,
      0x50,
      0xc8,
      0x6c,
      0x2d,
      0x72,
      0x35,
      0xda}},

    // RFC test case 6
    {{0xaa,
      0xaa,
      0xaa,
      0xaa,
      0xaa,
      0xaa,
      0xaa,
      0xaa,
      0xaa,
      0xaa,
      0xaa,
      0xaa,
      0xaa,
      0xaa,
      0xaa,
      0xaa,
      0xaa,
      0xaa,
      0xaa,
      0xaa,
      0xaa,
      0xaa,
      0xaa,
      0xaa,
      0xaa,
      0xaa,
      0xaa,
      0xaa,
      0xaa,
      0xaa,
      0xaa,
      0xaa,
      0xaa,
      0xaa,
      0xaa,
      0xaa,
      0xaa,
      0xaa,
      0xaa,
      0xaa,
      0xaa,
      0xaa,
      0xaa,
      0xaa,
      0xaa,
      0xaa,
      0xaa,
      0xaa,
      0xaa,
      0xaa,
      0xaa,
      0xaa,
      0xaa,
      0xaa,
      0xaa,
      0xaa,
      0xaa,
      0xaa,
      0xaa,
      0xaa,
      0xaa,
      0xaa,
      0xaa,
      0xaa,
      0xaa,
      0xaa,
      0xaa,
      0xaa,
      0xaa,
      0xaa,
      0xaa,
      0xaa,
      0xaa,
      0xaa,
      0xaa,
      0xaa,
      0xaa,
      0xaa,
      0xaa,
      0xaa},
     80,
     {0x54,
      0x65,
      0x73,
      0x74,
      0x20,
      0x55,
      0x73,
      0x69,
      0x6e,
      0x67,
      0x20,
      0x4c,
      0x61,
      0x72,
      0x67,
      0x65,
      0x72,
      0x20,
      0x54,
      0x68,
      0x61,
      0x6e,
      0x20,
      0x42,
      0x6c,
      0x6f,
      0x63,
      0x6b,
      0x2d,
      0x53,
      0x69,
      0x7a,
      0x65,
      0x20,
      0x4b,
      0x65,
      0x79,
      0x20,
      0x2d,
      0x20,
      0x48,
      0x61,
      0x73,
      0x68,
      0x20,
      0x4b,
      0x65,
      0x79,
      0x20,
      0x46,
      0x69,
      0x72,
      0x73,
      0x74},
     54,
     {0xaa,
      0x4a,
      0xe5,
      0xe1,
      0x52,
      0x72,
      0xd0,
      0x0e,
      0x95,
      0x70,
      0x56,
      0x37,
      0xce,
      0x8a,
      0x3b,
      0x55,
      0xed,
      0x40,
      0x21,
      0x12}}};

TEST(CryptoVectors, HMACSHA1) {
    unsigned char hmacSha1Result[digestLen];
    unsigned int hashLen = digestLen;

    size_t numTests = sizeof(hmacSha1Tests) / sizeof(hmacSha1Tests[0]);
    for (size_t i = 0; i < numTests; i++) {
        ASSERT(crypto::hmacSha1(hmacSha1Tests[i].key,
                                hmacSha1Tests[i].keyLen,
                                hmacSha1Tests[i].data,
                                hmacSha1Tests[i].dataLen,
                                hmacSha1Result,
                                &hashLen))
            << "Failed HMAC-SHA1 iteration " << i;
        ASSERT(0 == memcmp(hmacSha1Tests[i].hash, hmacSha1Result, digestLen))
            << "Failed HMAC-SHA1 iteration " << i;
    }
}
}  // namespace
}  // namespace mongo
