/* Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You 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 "SHA1MessageDigestSpi.h" #ifdef HAVE_STRING_H #include #endif #include #include using namespace decaf; using namespace decaf::lang; using namespace decaf::security; using namespace decaf::internal; using namespace decaf::internal::security; using namespace decaf::internal::security::provider; using namespace decaf::internal::security::provider::crypto; //////////////////////////////////////////////////////////////////////////////// namespace { #define DECAF_SHA1_DIGESTSIZE 20 /* SHA f()-functions */ #define f1(x,y,z) ((x & y) | (~x & z)) #define f2(x,y,z) (x ^ y ^ z) #define f3(x,y,z) ((x & y) | (x & z) | (y & z)) #define f4(x,y,z) (x ^ y ^ z) // SHA constants #define CONST1 0x5a827999 #define CONST2 0x6ed9eba1 #define CONST3 0x8f1bbcdc #define CONST4 0xca62c1d6 // 32-bit rotate #define ROT32(x,n) ((x << n) | (x >> (32 - n))) #define FUNC(n,i) \ temp = ROT32(A,5) + f##n(B,C,D) + E + W[i] + CONST##n; \ E = D; D = C; C = ROT32(B,30); B = A; A = temp #define SHA_BLOCKSIZE 64 } //////////////////////////////////////////////////////////////////////////////// namespace decaf { namespace internal { namespace security { namespace provider { namespace crypto { class SHA1MessageDigestSpiImpl { public: // message digest unsigned int state[5]; // 64-bit bit counts unsigned int count[2]; // SHA data buffer unsigned int data[16]; // unprocessed amount in data form last update int local; private: union endianTest { long Long; char Char[sizeof(long)]; }; static bool isLittleEndian() { static union endianTest u; u.Long = 1; return (u.Char[0] == 1); } static void maybeReverseBytes(unsigned int* buffer, int count) { int i; unsigned char ct[4], *cp; if (isLittleEndian()) { // do the swap only if it is little endian count = (int)(count / 4); cp = (unsigned char*) buffer; for (i = 0; i < count; ++i) { ct[0] = cp[0]; ct[1] = cp[1]; ct[2] = cp[2]; ct[3] = cp[3]; cp[0] = ct[3]; cp[1] = ct[2]; cp[2] = ct[1]; cp[3] = ct[0]; cp += sizeof(int); } } } static void SHA1Transform(unsigned int state[5], const unsigned int buffer[16]) { int i; unsigned int temp, A, B, C, D, E, W[80]; for (i = 0; i < 16; ++i) { W[i] = buffer[i]; } for (i = 16; i < 80; ++i) { W[i] = W[i-3] ^ W[i-8] ^ W[i-14] ^ W[i-16]; } A = state[0]; B = state[1]; C = state[2]; D = state[3]; E = state[4]; FUNC(1, 0); FUNC(1, 1); FUNC(1, 2); FUNC(1, 3); FUNC(1, 4); FUNC(1, 5); FUNC(1, 6); FUNC(1, 7); FUNC(1, 8); FUNC(1, 9); FUNC(1,10); FUNC(1,11); FUNC(1,12); FUNC(1,13); FUNC(1,14); FUNC(1,15); FUNC(1,16); FUNC(1,17); FUNC(1,18); FUNC(1,19); FUNC(2,20); FUNC(2,21); FUNC(2,22); FUNC(2,23); FUNC(2,24); FUNC(2,25); FUNC(2,26); FUNC(2,27); FUNC(2,28); FUNC(2,29); FUNC(2,30); FUNC(2,31); FUNC(2,32); FUNC(2,33); FUNC(2,34); FUNC(2,35); FUNC(2,36); FUNC(2,37); FUNC(2,38); FUNC(2,39); FUNC(3,40); FUNC(3,41); FUNC(3,42); FUNC(3,43); FUNC(3,44); FUNC(3,45); FUNC(3,46); FUNC(3,47); FUNC(3,48); FUNC(3,49); FUNC(3,50); FUNC(3,51); FUNC(3,52); FUNC(3,53); FUNC(3,54); FUNC(3,55); FUNC(3,56); FUNC(3,57); FUNC(3,58); FUNC(3,59); FUNC(4,60); FUNC(4,61); FUNC(4,62); FUNC(4,63); FUNC(4,64); FUNC(4,65); FUNC(4,66); FUNC(4,67); FUNC(4,68); FUNC(4,69); FUNC(4,70); FUNC(4,71); FUNC(4,72); FUNC(4,73); FUNC(4,74); FUNC(4,75); FUNC(4,76); FUNC(4,77); FUNC(4,78); FUNC(4,79); state[0] += A; state[1] += B; state[2] += C; state[3] += D; state[4] += E; } public: SHA1MessageDigestSpiImpl() : state(), count(), data(), local() { reset(); } void reset() { state[0] = 0x67452301; state[1] = 0xefcdab89; state[2] = 0x98badcfe; state[3] = 0x10325476; state[4] = 0xc3d2e1f0; count[0] = 0; count[0] = 0; local = 0; } void update(const unsigned char* input, int length) { int i; if ((count[0] + ((unsigned int) length << 3)) < count[0]) { ++count[1]; } count[0] += (unsigned int) length << 3; count[1] += (unsigned int) length >> 29; if (local) { i = SHA_BLOCKSIZE - local; if (i > length) { i = length; } memcpy(((unsigned char*) data) + local, input, i); length -= i; input += i; local += i; if (local == SHA_BLOCKSIZE) { maybeReverseBytes(data, SHA_BLOCKSIZE); SHA1Transform(state, data); } else { return; } } while (length >= SHA_BLOCKSIZE) { memcpy(data, input, SHA_BLOCKSIZE); input += SHA_BLOCKSIZE; length -= SHA_BLOCKSIZE; maybeReverseBytes(data, SHA_BLOCKSIZE); SHA1Transform(state, data); } memcpy(data, input, length); local = length; } void finalize(unsigned char digest[DECAF_SHA1_DIGESTSIZE]) { int index, i, j; unsigned int lo_bit_count, hi_bit_count, k; lo_bit_count = count[0]; hi_bit_count = count[1]; index = (int) ((lo_bit_count >> 3) & 0x3f); ((unsigned char*) data)[index++] = 0x80; if (index > SHA_BLOCKSIZE - 8) { memset(((unsigned char*) data) + index, 0, SHA_BLOCKSIZE - index); maybeReverseBytes(data, SHA_BLOCKSIZE); SHA1Transform(state, data); memset((unsigned char*) data, 0, SHA_BLOCKSIZE - 8); } else { memset(((unsigned char*) data) + index, 0, SHA_BLOCKSIZE - 8 - index); } maybeReverseBytes(data, SHA_BLOCKSIZE); data[14] = hi_bit_count; data[15] = lo_bit_count; SHA1Transform(state, data); for (i = 0, j = 0; j < DECAF_SHA1_DIGESTSIZE; i++) { k = state[i]; digest[j++] = (unsigned char) ((k >> 24) & 0xff); digest[j++] = (unsigned char) ((k >> 16) & 0xff); digest[j++] = (unsigned char) ((k >> 8) & 0xff); digest[j++] = (unsigned char) (k & 0xff); } } }; }}}}} //////////////////////////////////////////////////////////////////////////////// SHA1MessageDigestSpi::SHA1MessageDigestSpi() : MessageDigestSpi(), impl(new SHA1MessageDigestSpiImpl) { } //////////////////////////////////////////////////////////////////////////////// SHA1MessageDigestSpi::~SHA1MessageDigestSpi() { delete this->impl; } //////////////////////////////////////////////////////////////////////////////// MessageDigestSpi* SHA1MessageDigestSpi::clone() { SHA1MessageDigestSpi* clone = new SHA1MessageDigestSpi; System::arraycopy(clone->impl->state, 0, this->impl->state, 0, 5); System::arraycopy(clone->impl->count, 0, this->impl->count, 0, 2); System::arraycopy(clone->impl->data, 0, this->impl->data, 0, 16); clone->impl->local = this->impl->local; return clone; } //////////////////////////////////////////////////////////////////////////////// int SHA1MessageDigestSpi::engineGetDigestLength() { return DECAF_SHA1_DIGESTSIZE; } //////////////////////////////////////////////////////////////////////////////// void SHA1MessageDigestSpi::engineUpdate(unsigned char input) { this->impl->update(&input, 1); } //////////////////////////////////////////////////////////////////////////////// void SHA1MessageDigestSpi::engineUpdate(const unsigned char* input, int size, int offset, int length) { if (input == NULL && size > 0) { throw DigestException(__FILE__, __LINE__, "Null buffer parameter."); } if (size <= 0) { return; } if (offset < 0 || length < 0) { engineReset(); throw DigestException(__FILE__, __LINE__, "Incorrect offset or length value."); } if (offset + length > size) { engineReset(); throw DigestException(__FILE__, __LINE__, "Incorrect offset or length value."); } this->impl->update(&input[offset], length); } //////////////////////////////////////////////////////////////////////////////// void SHA1MessageDigestSpi::engineUpdate(const std::vector& input) { if (input.empty()) { return; } this->impl->update(&input[0], (int)input.size()); } //////////////////////////////////////////////////////////////////////////////// void SHA1MessageDigestSpi::engineUpdate(decaf::nio::ByteBuffer& input) { if (!input.hasRemaining()) { return; } unsigned char* temp; if (input.hasArray()) { temp = input.array(); int offset = input.arrayOffset(); int position = input.position(); int limit = input.limit(); engineUpdate(temp, limit - position, offset + position, limit - position); input.position(limit); } else { int length = input.limit() - input.position(); temp = new unsigned char[length]; input.get(temp, length, 0, length); engineUpdate(temp, length, 0, length); delete temp; } } //////////////////////////////////////////////////////////////////////////////// void SHA1MessageDigestSpi::engineReset() { this->impl->reset(); } //////////////////////////////////////////////////////////////////////////////// std::vector SHA1MessageDigestSpi::engineDigest() { std::vector buffer; buffer.resize(DECAF_SHA1_DIGESTSIZE); this->impl->finalize(&buffer[0]); return buffer; } //////////////////////////////////////////////////////////////////////////////// int SHA1MessageDigestSpi::engineDigest(unsigned char* buffer, int size, int offset, int length) { if (buffer == NULL) { engineReset(); throw DigestException(__FILE__, __LINE__, "Null buffer parameter."); } if (size < engineGetDigestLength()) { engineReset(); throw DigestException(__FILE__, __LINE__, "The value of size parameter is less than the actual digest length."); } if (length < engineGetDigestLength()) { engineReset(); throw DigestException(__FILE__, __LINE__, "The value of length parameter is less than the actual digest length."); } if (offset < 0) { engineReset(); throw DigestException(__FILE__, __LINE__, "Invalid negative offset."); } if (offset + length > size) { engineReset(); throw DigestException(__FILE__, __LINE__, "Incorrect offset or length value."); } std::vector temp = engineDigest(); if (length < (int)temp.size()) { throw DigestException(__FILE__, __LINE__, "The value of length parameter is less than the actual digest length.."); } System::arraycopy(&temp[0], 0, buffer, offset, temp.size()); return (int)temp.size(); }