blob: c92b7476aa3e2862475df3c071bf49ca5357845b [file] [log] [blame]
/* 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 <string.h>
#endif
#include <decaf/lang/System.h>
#include <decaf/security/DigestException.h>
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<unsigned char>& 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<unsigned char> SHA1MessageDigestSpi::engineDigest() {
std::vector<unsigned char> 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<unsigned char> 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();
}