Commit 6e204620 authored by Eduardo Lima Mitev's avatar Eduardo Lima Mitev Committed by Julian Andres Klode

gchecksum: Adds SHA512 support

https://bugzilla.gnome.org/show_bug.cgi?id=602715
parent 5c94a3e8
......@@ -54,7 +54,7 @@
* Support for checksums has been added in GLib 2.16
**/
#define IS_VALID_TYPE(type) ((type) >= G_CHECKSUM_MD5 && (type) <= G_CHECKSUM_SHA256)
#define IS_VALID_TYPE(type) ((type) >= G_CHECKSUM_MD5 && (type) <= G_CHECKSUM_SHA512)
/* The fact that these are lower case characters is part of the ABI */
static const gchar hex_digits[] = "0123456789abcdef";
......@@ -102,6 +102,21 @@ typedef struct
guchar digest[SHA256_DIGEST_LEN];
} Sha256sum;
#define SHA512_BLOCK_LEN 128 /* 1024 bits message block */
#define SHA512_DIGEST_LEN 64
typedef struct
{
guint64 H[8];
guint8 block[SHA512_BLOCK_LEN];
guint8 block_len;
guint64 data_len[2];
guchar digest[SHA512_DIGEST_LEN];
} Sha512sum;
struct _GChecksum
{
GChecksumType type;
......@@ -112,6 +127,7 @@ struct _GChecksum
Md5sum md5;
Sha1sum sha1;
Sha256sum sha256;
Sha512sum sha512;
} sum;
};
......@@ -1069,6 +1085,284 @@ sha256_sum_digest (Sha256sum *sha256,
digest[i] = sha256->digest[i];
}
/*
* SHA-512 Checksum
*
* Implemented following FIPS-180-2 standard at
* http://csrc.nist.gov/publications/fips/fips180-2/fips180-2.pdf.
* References in the form [§x.y.z] map to sections in that document.
*
* Author: Eduardo Lima Mitev <elima@igalia.com>
*/
/* SHA-384 and SHA-512 functions [§4.1.3] */
#define Ch(x,y,z) ((x & y) ^ (~x & z))
#define Maj(x,y,z) ((x & y) ^ (x & z) ^ (y & z))
#define SHR(n,x) (x >> n)
#define ROTR(n,x) (SHR (n, x) | (x << (64 - n)))
#define SIGMA0(x) (ROTR (28, x) ^ ROTR (34, x) ^ ROTR (39, x))
#define SIGMA1(x) (ROTR (14, x) ^ ROTR (18, x) ^ ROTR (41, x))
#define sigma0(x) (ROTR ( 1, x) ^ ROTR ( 8, x) ^ SHR ( 7, x))
#define sigma1(x) (ROTR (19, x) ^ ROTR (61, x) ^ SHR ( 6, x))
#define PUT_UINT64(n,b,i) G_STMT_START{ \
(b)[(i) ] = (guint8) (n >> 56); \
(b)[(i) + 1] = (guint8) (n >> 48); \
(b)[(i) + 2] = (guint8) (n >> 40); \
(b)[(i) + 3] = (guint8) (n >> 32); \
(b)[(i) + 4] = (guint8) (n >> 24); \
(b)[(i) + 5] = (guint8) (n >> 16); \
(b)[(i) + 6] = (guint8) (n >> 8); \
(b)[(i) + 7] = (guint8) (n ); } G_STMT_END
static void
sha512_sum_init (Sha512sum *sha512)
{
/* Initial Hash Value [§5.3.4] */
sha512->H[0] = 0x6a09e667f3bcc908;
sha512->H[1] = 0xbb67ae8584caa73b;
sha512->H[2] = 0x3c6ef372fe94f82b;
sha512->H[3] = 0xa54ff53a5f1d36f1;
sha512->H[4] = 0x510e527fade682d1;
sha512->H[5] = 0x9b05688c2b3e6c1f;
sha512->H[6] = 0x1f83d9abfb41bd6b;
sha512->H[7] = 0x5be0cd19137e2179;
sha512->block_len = 0;
sha512->data_len[0] = 0;
sha512->data_len[1] = 0;
}
/* SHA-384 and SHA-512 constants [§4.2.3] */
static const guint64 SHA512_K[80] = {
0x428a2f98d728ae22, 0x7137449123ef65cd,
0xb5c0fbcfec4d3b2f, 0xe9b5dba58189dbbc,
0x3956c25bf348b538, 0x59f111f1b605d019,
0x923f82a4af194f9b, 0xab1c5ed5da6d8118,
0xd807aa98a3030242, 0x12835b0145706fbe,
0x243185be4ee4b28c, 0x550c7dc3d5ffb4e2,
0x72be5d74f27b896f, 0x80deb1fe3b1696b1,
0x9bdc06a725c71235, 0xc19bf174cf692694,
0xe49b69c19ef14ad2, 0xefbe4786384f25e3,
0x0fc19dc68b8cd5b5, 0x240ca1cc77ac9c65,
0x2de92c6f592b0275, 0x4a7484aa6ea6e483,
0x5cb0a9dcbd41fbd4, 0x76f988da831153b5,
0x983e5152ee66dfab, 0xa831c66d2db43210,
0xb00327c898fb213f, 0xbf597fc7beef0ee4,
0xc6e00bf33da88fc2, 0xd5a79147930aa725,
0x06ca6351e003826f, 0x142929670a0e6e70,
0x27b70a8546d22ffc, 0x2e1b21385c26c926,
0x4d2c6dfc5ac42aed, 0x53380d139d95b3df,
0x650a73548baf63de, 0x766a0abb3c77b2a8,
0x81c2c92e47edaee6, 0x92722c851482353b,
0xa2bfe8a14cf10364, 0xa81a664bbc423001,
0xc24b8b70d0f89791, 0xc76c51a30654be30,
0xd192e819d6ef5218, 0xd69906245565a910,
0xf40e35855771202a, 0x106aa07032bbd1b8,
0x19a4c116b8d2d0c8, 0x1e376c085141ab53,
0x2748774cdf8eeb99, 0x34b0bcb5e19b48a8,
0x391c0cb3c5c95a63, 0x4ed8aa4ae3418acb,
0x5b9cca4f7763e373, 0x682e6ff3d6b2b8a3,
0x748f82ee5defb2fc, 0x78a5636f43172f60,
0x84c87814a1f0ab72, 0x8cc702081a6439ec,
0x90befffa23631e28, 0xa4506cebde82bde9,
0xbef9a3f7b2c67915, 0xc67178f2e372532b,
0xca273eceea26619c, 0xd186b8c721c0c207,
0xeada7dd6cde0eb1e, 0xf57d4f7fee6ed178,
0x06f067aa72176fba, 0x0a637dc5a2c898a6,
0x113f9804bef90dae, 0x1b710b35131c471b,
0x28db77f523047d84, 0x32caab7b40c72493,
0x3c9ebe0a15c9bebc, 0x431d67c49c100d4c,
0x4cc5d4becb3e42b6, 0x597f299cfc657e2a,
0x5fcb6fab3ad6faec, 0x6c44198c4a475817
};
static void
sha512_transform (guint64 H[8],
guint8 const data[SHA512_BLOCK_LEN])
{
gint i;
gint t;
guint64 a, b, c, d, e, f, g, h;
guint64 M[16];
guint64 W[80];
/* SHA-512 hash computation [§6.3.2] */
/* prepare the message schedule */
for (i = 0; i < 16; i++)
{
gint p = i * 8;
M[i] =
((guint64) data[p + 0] << 56) |
((guint64) data[p + 1] << 48) |
((guint64) data[p + 2] << 40) |
((guint64) data[p + 3] << 32) |
((guint64) data[p + 4] << 24) |
((guint64) data[p + 5] << 16) |
((guint64) data[p + 6] << 8) |
((guint64) data[p + 7] );
}
for (t = 0; t < 80; t++)
if (t < 16)
W[t] = M[t];
else
W[t] = sigma1 (W[t - 2]) + W[t - 7] + sigma0 (W[t - 15]) + W[t - 16];
/* initialize the eight working variables */
a = H[0];
b = H[1];
c = H[2];
d = H[3];
e = H[4];
f = H[5];
g = H[6];
h = H[7];
for (t = 0; t < 80; t++)
{
guint64 T1, T2;
T1 = h + SIGMA1 (e) + Ch (e, f, g) + SHA512_K[t] + W[t];
T2 = SIGMA0 (a) + Maj (a, b, c);
h = g;
g = f;
f = e;
e = d + T1;
d = c;
c = b;
b = a;
a = T1 + T2;
}
/* Compute the intermediate hash value H */
H[0] += a;
H[1] += b;
H[2] += c;
H[3] += d;
H[4] += e;
H[5] += f;
H[6] += g;
H[7] += h;
}
static void
sha512_sum_update (Sha512sum *sha512,
const guchar *buffer,
gsize length)
{
gsize block_left, offset = 0;
if (length == 0)
return;
sha512->data_len[0] += length * 8;
if (sha512->data_len[0] < length)
sha512->data_len[1]++;
/* try to fill current block */
block_left = SHA512_BLOCK_LEN - sha512->block_len;
if (block_left > 0)
{
gsize fill_len;
fill_len = MIN (block_left, length);
memcpy (sha512->block + sha512->block_len, buffer, fill_len);
sha512->block_len += fill_len;
length -= fill_len;
offset += fill_len;
if (sha512->block_len == SHA512_BLOCK_LEN)
{
sha512_transform (sha512->H, sha512->block);
sha512->block_len = 0;
}
}
/* process complete blocks */
while (length >= SHA512_BLOCK_LEN)
{
memcpy (sha512->block, buffer + offset, SHA512_BLOCK_LEN);
sha512_transform (sha512->H, sha512->block);
length -= SHA512_BLOCK_LEN;
offset += SHA512_BLOCK_LEN;
}
/* keep remaining data for next block */
if (length > 0)
{
memcpy (sha512->block, buffer + offset, length);
sha512->block_len = length;
}
}
static void
sha512_sum_close (Sha512sum *sha512)
{
guint l;
gint zeros;
guint8 pad[SHA512_BLOCK_LEN * 2] = { 0, };
guint pad_len = 0;
gint i;
/* apply padding [§5.1.2] */
l = sha512->block_len * 8;
zeros = 896 - (l + 1);
if (zeros < 0)
zeros += 128 * 8;
pad[0] = 0x80; /* 1000 0000 */
zeros -= 7;
pad_len++;
memset (pad + pad_len, 0x00, zeros / 8);
pad_len += zeros / 8;
zeros = zeros % 8;
/* put message bit length at the end of padding */
PUT_UINT64 (sha512->data_len[1], pad, pad_len);
pad_len += 8;
PUT_UINT64 (sha512->data_len[0], pad, pad_len);
pad_len += 8;
/* update checksum with the padded block */
sha512_sum_update (sha512, pad, pad_len);
/* copy resulting 64-bit words into digest */
for (i = 0; i < 8; i++)
PUT_UINT64 (sha512->H[i], sha512->digest, i * 8);
}
static gchar *
sha512_sum_to_string (Sha512sum *sha512)
{
return digest_to_string (sha512->digest, SHA512_DIGEST_LEN);
}
static void
sha512_sum_digest (Sha512sum *sha512,
guint8 *digest)
{
memcpy (digest, sha512->digest, SHA512_DIGEST_LEN);
}
#undef Ch
#undef Maj
#undef SHR
#undef ROTR
#undef SIGMA0
#undef SIGMA1
#undef sigma0
#undef sigma1
#undef PUT_UINT64
/*
* Public API
......@@ -1101,6 +1395,9 @@ g_checksum_type_get_length (GChecksumType checksum_type)
case G_CHECKSUM_SHA256:
len = SHA256_DIGEST_LEN;
break;
case G_CHECKSUM_SHA512:
len = SHA512_DIGEST_LEN;
break;
default:
len = -1;
break;
......@@ -1175,6 +1472,9 @@ g_checksum_reset (GChecksum *checksum)
case G_CHECKSUM_SHA256:
sha256_sum_init (&(checksum->sum.sha256));
break;
case G_CHECKSUM_SHA512:
sha512_sum_init (&(checksum->sum.sha512));
break;
default:
g_assert_not_reached ();
break;
......@@ -1270,6 +1570,9 @@ g_checksum_update (GChecksum *checksum,
case G_CHECKSUM_SHA256:
sha256_sum_update (&(checksum->sum.sha256), data, length);
break;
case G_CHECKSUM_SHA512:
sha512_sum_update (&(checksum->sum.sha512), data, length);
break;
default:
g_assert_not_reached ();
break;
......@@ -1317,6 +1620,10 @@ g_checksum_get_string (GChecksum *checksum)
sha256_sum_close (&(checksum->sum.sha256));
str = sha256_sum_to_string (&(checksum->sum.sha256));
break;
case G_CHECKSUM_SHA512:
sha512_sum_close (&(checksum->sum.sha512));
str = sha512_sum_to_string (&(checksum->sum.sha512));
break;
default:
g_assert_not_reached ();
break;
......@@ -1384,6 +1691,14 @@ g_checksum_get_digest (GChecksum *checksum,
}
sha256_sum_digest (&(checksum->sum.sha256), buffer);
break;
case G_CHECKSUM_SHA512:
if (checksum_open)
{
sha512_sum_close (&(checksum->sum.sha512));
str = sha512_sum_to_string (&(checksum->sum.sha512));
}
sha512_sum_digest (&(checksum->sum.sha512), buffer);
break;
default:
g_assert_not_reached ();
break;
......
......@@ -35,6 +35,7 @@ G_BEGIN_DECLS
* @G_CHECKSUM_MD5: Use the MD5 hashing algorithm
* @G_CHECKSUM_SHA1: Use the SHA-1 hashing algorithm
* @G_CHECKSUM_SHA256: Use the SHA-256 hashing algorithm
* @G_CHECKSUM_SHA512: Use the SHA-512 hashing algorithm
*
* The hashing algorithm to be used by #GChecksum when performing the
* digest of some data.
......@@ -47,7 +48,8 @@ G_BEGIN_DECLS
typedef enum {
G_CHECKSUM_MD5,
G_CHECKSUM_SHA1,
G_CHECKSUM_SHA256
G_CHECKSUM_SHA256,
G_CHECKSUM_SHA512
} GChecksumType;
/**
......
This diff is collapsed.
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment